Repository: thug1src/thug Branch: master Commit: d8eb7147663d Files: 1308 Total size: 18.4 MB Directory structure: gitextract_37te5saf/ └── Code/ ├── Core/ │ ├── Debug/ │ │ ├── Assert.cpp │ │ ├── Checks.h │ │ ├── Debug.cpp │ │ ├── Mem_stat.h │ │ ├── Messages.h │ │ ├── Module.h │ │ ├── NGPS/ │ │ │ └── P_debug.cpp │ │ ├── Project.h │ │ ├── Signatrs.h │ │ ├── Wn32/ │ │ │ └── P_debug.cpp │ │ ├── XBox/ │ │ │ └── p_debug.cpp │ │ ├── log.cpp │ │ └── ngc/ │ │ └── P_debug.cpp │ ├── Debug.h │ ├── Defines.h │ ├── DynamicTable.cpp │ ├── DynamicTable.h │ ├── HashTable.h │ ├── List/ │ │ ├── Head.h │ │ ├── Node.h │ │ ├── Search.h │ │ └── list.cpp │ ├── List.h │ ├── LookupTable.cpp │ ├── LookupTable.h │ ├── Math/ │ │ ├── Xbox/ │ │ │ └── sse.h │ │ ├── geometry.cpp │ │ ├── geometry.h │ │ ├── math.cpp │ │ ├── math.h │ │ ├── matrix.cpp │ │ ├── matrix.h │ │ ├── matrix.inl │ │ ├── quat.h │ │ ├── quat.inl │ │ ├── rect.h │ │ ├── rot90.cpp │ │ ├── rot90.h │ │ ├── slerp.cpp │ │ ├── slerp.h │ │ ├── vector.cpp │ │ ├── vector.h │ │ └── vector.inl │ ├── String/ │ │ ├── CString.cpp │ │ ├── CString.h │ │ ├── stringutils.cpp │ │ └── stringutils.h │ ├── StringHashTable.h │ ├── Support/ │ │ ├── Lock.h │ │ ├── Ref.h │ │ ├── class.cpp │ │ ├── class.h │ │ └── support.h │ ├── Task/ │ │ ├── Hook.h │ │ ├── List.h │ │ ├── Stack.h │ │ ├── Task.cpp │ │ ├── Task.h │ │ ├── Tlist.cpp │ │ └── Tstack.cpp │ ├── Task.h │ ├── Thread/ │ │ ├── ngc/ │ │ │ ├── t_thread.cpp │ │ │ └── t_thread.h │ │ ├── ngps/ │ │ │ ├── t_thread.cpp │ │ │ └── t_thread.h │ │ └── wn32/ │ │ ├── t_thread.cpp │ │ └── t_thread.h │ ├── TimestampedFlag.h │ ├── compress.cpp │ ├── compress.h │ ├── crc.cpp │ ├── crc.h │ ├── flags.h │ ├── glue.h │ ├── log.h │ ├── macros.h │ ├── math.h │ ├── singleton.h │ ├── support.h │ └── thread.h ├── GameFlow.txt ├── Gel/ │ ├── AssMan/ │ │ ├── AssMan.cpp │ │ ├── AssMan.h │ │ ├── NodeArrayAsset.cpp │ │ ├── NodeArrayAsset.h │ │ ├── animasset.cpp │ │ ├── animasset.h │ │ ├── asset.cpp │ │ ├── asset.h │ │ ├── assettypes.h │ │ ├── cutsceneasset.cpp │ │ ├── cutsceneasset.h │ │ ├── refasset.cpp │ │ ├── refasset.h │ │ ├── skeletonasset.cpp │ │ ├── skeletonasset.h │ │ ├── skinasset.cpp │ │ └── skinasset.h │ ├── Collision/ │ │ ├── BatchTriColl.cpp │ │ ├── BatchTriColl.h │ │ ├── CollCache.cpp │ │ ├── CollCache.h │ │ ├── CollEnums.h │ │ ├── CollTriData.cpp │ │ ├── CollTriData.h │ │ ├── Collision.cpp │ │ ├── Collision.h │ │ ├── MovCollMan.cpp │ │ └── MovCollMan.h │ ├── Components/ │ │ ├── BouncyComponent.h │ │ ├── CameraComponent.cpp │ │ ├── CameraComponent.h │ │ ├── CameraLookAroundComponent.cpp │ │ ├── CameraLookAroundComponent.h │ │ ├── CameraUtil.cpp │ │ ├── CameraUtil.h │ │ ├── CollideAndDieComponent.cpp │ │ ├── CollideAndDieComponent.h │ │ ├── FloatingLabelComponent.cpp │ │ ├── FloatingLabelComponent.h │ │ ├── GunslingerCameraLookAroundComponent.cpp │ │ ├── GunslingerWalkCameraComponent.cpp │ │ ├── GunslingerWalkComponent.cpp │ │ ├── HorseCameraComponent.cpp │ │ ├── HorseCameraComponent.h │ │ ├── HorseComponent.cpp │ │ ├── HorseComponent.h │ │ ├── InputComponent.cpp │ │ ├── InputComponent.h │ │ ├── ModelLightUpdateComponent.cpp │ │ ├── ModelLightUpdateComponent.h │ │ ├── MovableContactComponent.cpp │ │ ├── MovableContactComponent.h │ │ ├── NearComponent.cpp │ │ ├── NearComponent.h │ │ ├── NodeArrayComponent.cpp │ │ ├── NodeArrayComponent.h │ │ ├── ObjectHookManagerComponent.cpp │ │ ├── ObjectHookManagerComponent.h │ │ ├── ParticleComponent.cpp │ │ ├── ParticleComponent.h │ │ ├── PedLogicComponent.cpp │ │ ├── PedLogicComponent.h │ │ ├── ProximTriggerComponent.cpp │ │ ├── ProximTriggerComponent.h │ │ ├── RailManagerComponent.cpp │ │ ├── RailManagerComponent.h │ │ ├── RibbonComponent.cpp │ │ ├── RibbonComponent.h │ │ ├── RiderComponent.cpp │ │ ├── RiderComponent.h │ │ ├── SetDisplayMatrixComponent.cpp │ │ ├── SetDisplayMatrixComponent.h │ │ ├── SkaterCameraComponent.cpp │ │ ├── SkaterCameraComponent.h │ │ ├── SkitchComponent.cpp │ │ ├── SkitchComponent.h │ │ ├── StaticVehicleComponent.cpp │ │ ├── StaticVehicleComponent.h │ │ ├── StatsManagerComponent.cpp │ │ ├── StatsManagerComponent.h │ │ ├── StreamComponent.cpp │ │ ├── StreamComponent.h │ │ ├── SuspendComponent.cpp │ │ ├── SuspendComponent.h │ │ ├── TriggerComponent.cpp │ │ ├── TriggerComponent.h │ │ ├── VehicleSoundComponent.cpp │ │ ├── VehicleSoundComponent.h │ │ ├── VelocityComponent.cpp │ │ ├── VelocityComponent.h │ │ ├── VibrationComponent.cpp │ │ ├── VibrationComponent.h │ │ ├── WalkCameraComponent.cpp │ │ ├── WalkCameraComponent.h │ │ ├── WalkComponent.cpp │ │ ├── WalkComponent.h │ │ ├── WalkHangUtil.cpp │ │ ├── WalkLadderUtil.cpp │ │ ├── animationcomponent.cpp │ │ ├── animationcomponent.h │ │ ├── avoidcomponent.cpp │ │ ├── avoidcomponent.h │ │ ├── bouncycomponent.cpp │ │ ├── carphysicscomponent.cpp │ │ ├── carphysicscomponent.h │ │ ├── collisioncomponent.cpp │ │ ├── collisioncomponent.h │ │ ├── emptycomponent.cpp │ │ ├── emptycomponent.h │ │ ├── lockobjcomponent.cpp │ │ ├── lockobjcomponent.h │ │ ├── modelcomponent.cpp │ │ ├── modelcomponent.h │ │ ├── motioncomponent.cpp │ │ ├── motioncomponent.h │ │ ├── rigidbodycomponent.cpp │ │ ├── rigidbodycomponent.h │ │ ├── shadowcomponent.cpp │ │ ├── shadowcomponent.h │ │ ├── skeletoncomponent.cpp │ │ ├── skeletoncomponent.h │ │ ├── soundcomponent.cpp │ │ ├── soundcomponent.h │ │ ├── specialitemcomponent.cpp │ │ ├── specialitemcomponent.h │ │ ├── trickcomponent.cpp │ │ ├── trickcomponent.h │ │ ├── vehiclecameracomponent.cpp │ │ ├── vehiclecameracomponent.h │ │ ├── vehiclecomponent.cpp │ │ ├── vehiclecomponent.h │ │ ├── weaponcomponent.cpp │ │ └── weaponcomponent.h │ ├── Environment/ │ │ ├── terrain.cpp │ │ └── terrain.h │ ├── Event.h │ ├── Input/ │ │ ├── InpMan.cpp │ │ └── inpserv.cpp │ ├── MainLoop/ │ │ └── Mainloop.cpp │ ├── Module/ │ │ ├── modman.cpp │ │ └── module.cpp │ ├── Movies/ │ │ ├── Movies.cpp │ │ ├── Movies.h │ │ ├── Ngps/ │ │ │ ├── audiodec.cpp │ │ │ ├── audiodec.h │ │ │ ├── defs.h │ │ │ ├── disp.cpp │ │ │ ├── disp.h │ │ │ ├── p_movies.cpp │ │ │ ├── p_movies.h │ │ │ ├── read.cpp │ │ │ ├── readbuf.cpp │ │ │ ├── readbuf.h │ │ │ ├── strfile.cpp │ │ │ ├── strfile.h │ │ │ ├── vibuf.cpp │ │ │ ├── vibuf.h │ │ │ ├── videodec.cpp │ │ │ ├── videodec.h │ │ │ ├── vobuf.cpp │ │ │ └── vobuf.h │ │ ├── Xbox/ │ │ │ ├── p_movies.cpp │ │ │ └── p_movies.h │ │ └── ngc/ │ │ ├── p_movies.cpp │ │ └── p_movies.h │ ├── Music/ │ │ ├── Ngps/ │ │ │ ├── Bgm/ │ │ │ │ ├── PathDefs │ │ │ │ ├── bgm_com.c │ │ │ │ ├── bgm_entr.c │ │ │ │ ├── bgm_i.h │ │ │ │ ├── bgm_play.c │ │ │ │ ├── bgm_r2s.s │ │ │ │ ├── bgm_r2sm.c │ │ │ │ └── makefile │ │ │ ├── Pcm/ │ │ │ │ ├── Makefile │ │ │ │ ├── PathDefs │ │ │ │ ├── pcm.h │ │ │ │ ├── pcm_com.c │ │ │ │ ├── pcm_ent.c │ │ │ │ ├── pcm_sound.c │ │ │ │ └── pcmiop.h │ │ │ ├── p_music.cpp │ │ │ └── p_music.h │ │ ├── Xbox/ │ │ │ ├── p_adpcmfilestream.cpp │ │ │ ├── p_adpcmfilestream.h │ │ │ ├── p_music.cpp │ │ │ ├── p_music.h │ │ │ ├── p_soundtrack.cpp │ │ │ ├── p_soundtrack.h │ │ │ ├── p_wmafilestream.cpp │ │ │ └── p_wmafilestream.h │ │ ├── music.cpp │ │ ├── music.h │ │ └── ngc/ │ │ ├── bgm/ │ │ │ ├── bgm_com.c │ │ │ ├── bgm_entr.c │ │ │ ├── bgm_i.h │ │ │ ├── bgm_play.c │ │ │ ├── bgm_r2s.s │ │ │ └── bgm_r2sm.c │ │ ├── divx/ │ │ │ ├── AUDSimpleAudio.cpp │ │ │ ├── AUDSimpleAudio.h │ │ │ ├── AUDSimplePlayer.cpp │ │ │ └── AUDSimplePlayer.h │ │ ├── p_music.cpp │ │ ├── p_music.h │ │ └── pcm/ │ │ ├── pcm.h │ │ ├── pcm_com.c │ │ ├── pcm_ent.c │ │ └── pcmiop.h │ ├── Net/ │ │ ├── App/ │ │ │ └── netapp.cpp │ │ ├── Client/ │ │ │ ├── netclnt.cpp │ │ │ └── netclnt.h │ │ ├── Dispatch/ │ │ │ └── netdsptch.cpp │ │ ├── Handler/ │ │ │ └── nethndlr.cpp │ │ ├── Server/ │ │ │ ├── netserv.cpp │ │ │ └── netserv.h │ │ ├── net.cpp │ │ ├── net.h │ │ └── netconn.cpp │ ├── ObjPtr.h │ ├── Object/ │ │ ├── Event.cpp │ │ ├── ObjPtr.cpp │ │ ├── RefCounted.cpp │ │ ├── basecomponent.cpp │ │ ├── basecomponent.h │ │ ├── compositeobject.cpp │ │ ├── compositeobject.h │ │ ├── compositeobjectmanager.cpp │ │ ├── compositeobjectmanager.h │ │ ├── object.cpp │ │ ├── objman.cpp │ │ ├── objsearch.cpp │ │ └── objtrack.cpp │ ├── Prefs/ │ │ ├── Prefs.cpp │ │ └── Prefs.h │ ├── RefCounted.h │ ├── Scripting/ │ │ ├── array.cpp │ │ ├── array.h │ │ ├── checksum.cpp │ │ ├── checksum.h │ │ ├── component.cpp │ │ ├── component.h │ │ ├── debugger.cpp │ │ ├── debugger.h │ │ ├── eval.cpp │ │ ├── eval.h │ │ ├── file.cpp │ │ ├── file.h │ │ ├── init.cpp │ │ ├── init.h │ │ ├── parse.cpp │ │ ├── parse.h │ │ ├── script.cpp │ │ ├── script.h │ │ ├── scriptcache.cpp │ │ ├── scriptcache.h │ │ ├── scriptdefs.h │ │ ├── skiptoken.cpp │ │ ├── string.cpp │ │ ├── string.h │ │ ├── struct.cpp │ │ ├── struct.h │ │ ├── symboltable.cpp │ │ ├── symboltable.h │ │ ├── symboltype.cpp │ │ ├── symboltype.h │ │ ├── tokens.cpp │ │ ├── tokens.h │ │ ├── utils.cpp │ │ ├── utils.h │ │ ├── vecpair.cpp │ │ ├── vecpair.h │ │ └── win32functions.cpp │ ├── SoundFX/ │ │ ├── NGPS/ │ │ │ ├── p_sfx.cpp │ │ │ └── p_sfx.h │ │ ├── Xbox/ │ │ │ ├── p_sfx.cpp │ │ │ ├── p_sfx.h │ │ │ └── skate5fx.h │ │ ├── ngc/ │ │ │ ├── p_sfx.cpp │ │ │ └── p_sfx.h │ │ ├── soundfx.cpp │ │ └── soundfx.h │ ├── inpman.h │ ├── mainloop.h │ ├── modman.h │ ├── module.h │ ├── object.h │ ├── objman.h │ ├── objsearch.h │ ├── objserv.h │ └── objtrack.h ├── Gfx/ │ ├── 2D/ │ │ ├── BlurEffect.cpp │ │ ├── BlurEffect.h │ │ ├── Element3d.cpp │ │ ├── Element3d.h │ │ ├── Menu2.cpp │ │ ├── Menu2.h │ │ ├── ScreenElemMan.cpp │ │ ├── ScreenElemMan.h │ │ ├── ScreenElement2.cpp │ │ ├── ScreenElement2.h │ │ ├── ScrollingMenu.cpp │ │ ├── ScrollingMenu.h │ │ ├── SpriteElement.cpp │ │ ├── SpriteElement.h │ │ ├── TextElement.cpp │ │ ├── TextElement.h │ │ ├── Window.cpp │ │ └── Window.h │ ├── AnimController.cpp │ ├── AnimController.h │ ├── BonedAnim.cpp │ ├── BonedAnim.h │ ├── BonedAnimTypes.h │ ├── CasUtils.cpp │ ├── CasUtils.h │ ├── CustomAnimKey.cpp │ ├── CustomAnimKey.h │ ├── FaceMassage.cpp │ ├── FaceMassage.h │ ├── FaceTexture.cpp │ ├── FaceTexture.h │ ├── Image/ │ │ └── ImageBasic.h │ ├── ModelAppearance.cpp │ ├── ModelAppearance.h │ ├── ModelBuilder.cpp │ ├── ModelBuilder.h │ ├── NGC/ │ │ ├── NX/ │ │ │ ├── anim.cpp │ │ │ ├── anim.h │ │ │ ├── chars.cpp │ │ │ ├── chars.h │ │ │ ├── geomnode.cpp │ │ │ ├── geomnode.h │ │ │ ├── grass.cpp │ │ │ ├── grass.h │ │ │ ├── import.cpp │ │ │ ├── import.h │ │ │ ├── instance.cpp │ │ │ ├── instance.h │ │ │ ├── light.cpp │ │ │ ├── light.h │ │ │ ├── line.cpp │ │ │ ├── line.h │ │ │ ├── material.cpp │ │ │ ├── material.h │ │ │ ├── mesh.cpp │ │ │ ├── mesh.h │ │ │ ├── nx_init.cpp │ │ │ ├── nx_init.h │ │ │ ├── occlude.cpp │ │ │ ├── occlude.h │ │ │ ├── particles.cpp │ │ │ ├── particles.h │ │ │ ├── render.cpp │ │ │ ├── render.h │ │ │ ├── scene.cpp │ │ │ ├── scene.h │ │ │ ├── sprite.cpp │ │ │ ├── sprite.h │ │ │ ├── texture.cpp │ │ │ ├── texture.h │ │ │ └── types.h │ │ ├── blur.cpp │ │ ├── p_NxGeom.cpp │ │ ├── p_NxGeom.h │ │ ├── p_NxImposter.cpp │ │ ├── p_NxImposter.h │ │ ├── p_NxLightMan.cpp │ │ ├── p_NxMesh.cpp │ │ ├── p_NxMesh.h │ │ ├── p_NxSprite.cpp │ │ ├── p_NxSprite.h │ │ ├── p_NxTextured3dPoly.cpp │ │ ├── p_NxTextured3dPoly.h │ │ ├── p_NxViewport.cpp │ │ ├── p_NxViewport.h │ │ ├── p_NxWin2D.cpp │ │ ├── p_gfxman.cpp │ │ ├── p_loadscreen.cpp │ │ ├── p_memview.cpp │ │ ├── p_memview.h │ │ ├── p_nx.cpp │ │ ├── p_nxanimcache.cpp │ │ ├── p_nxanimcache.h │ │ ├── p_nxfont.cpp │ │ ├── p_nxfont.h │ │ ├── p_nxfontman.cpp │ │ ├── p_nxlight.cpp │ │ ├── p_nxlight.h │ │ ├── p_nxloadscreen.cpp │ │ ├── p_nxmiscfx.cpp │ │ ├── p_nxmodel.cpp │ │ ├── p_nxmodel.h │ │ ├── p_nxnewparticle.cpp │ │ ├── p_nxnewparticle.h │ │ ├── p_nxnewparticlemgr.cpp │ │ ├── p_nxnewparticlemgr.h │ │ ├── p_nxparticle.cpp │ │ ├── p_nxparticle.h │ │ ├── p_nxparticleflat.cpp │ │ ├── p_nxparticleflat.h │ │ ├── p_nxparticleglow.cpp │ │ ├── p_nxparticleglow.h │ │ ├── p_nxparticleglowribbontrail.cpp │ │ ├── p_nxparticleglowribbontrail.h │ │ ├── p_nxparticleline.cpp │ │ ├── p_nxparticleline.h │ │ ├── p_nxparticleribbon.cpp │ │ ├── p_nxparticleribbon.h │ │ ├── p_nxparticleribbontrail.cpp │ │ ├── p_nxparticleribbontrail.h │ │ ├── p_nxparticleshaded.cpp │ │ ├── p_nxparticleshaded.h │ │ ├── p_nxparticlesmooth.cpp │ │ ├── p_nxparticlesmooth.h │ │ ├── p_nxparticlesmoothribbon.cpp │ │ ├── p_nxparticlesmoothribbon.h │ │ ├── p_nxparticlesmoothstar.cpp │ │ ├── p_nxparticlesmoothstar.h │ │ ├── p_nxparticlestar.cpp │ │ ├── p_nxparticlestar.h │ │ ├── p_nxscene.cpp │ │ ├── p_nxscene.h │ │ ├── p_nxsector.cpp │ │ ├── p_nxsector.h │ │ ├── p_nxtexman.cpp │ │ ├── p_nxtexture.cpp │ │ ├── p_nxtexture.h │ │ ├── p_nxviewman.cpp │ │ ├── p_nxweather.cpp │ │ ├── p_nxweather.h │ │ └── p_nxwin2D.h │ ├── NGPS/ │ │ ├── NX/ │ │ │ ├── Makefile │ │ │ ├── asmdma.dsm │ │ │ ├── asmdma.h │ │ │ ├── chars.cpp │ │ │ ├── chars.h │ │ │ ├── dma.cpp │ │ │ ├── dma.h │ │ │ ├── dmacalls.cpp │ │ │ ├── dmacalls.h │ │ │ ├── fx.cpp │ │ │ ├── fx.h │ │ │ ├── geomnode.cpp │ │ │ ├── geomnode.h │ │ │ ├── gif.cpp │ │ │ ├── gif.h │ │ │ ├── group.cpp │ │ │ ├── group.h │ │ │ ├── gs.cpp │ │ │ ├── gs.h │ │ │ ├── immediate.cpp │ │ │ ├── immediate.h │ │ │ ├── instance.cpp │ │ │ ├── instance.h │ │ │ ├── interrupts.cpp │ │ │ ├── interrupts.h │ │ │ ├── light.cpp │ │ │ ├── light.h │ │ │ ├── line.cpp │ │ │ ├── line.h │ │ │ ├── loadscreen.cpp │ │ │ ├── loadscreen.h │ │ │ ├── maintest.cpp │ │ │ ├── material.cpp │ │ │ ├── material.h │ │ │ ├── mesh.cpp │ │ │ ├── mesh.h │ │ │ ├── mikemath.cpp │ │ │ ├── mikemath.h │ │ │ ├── nx_init.cpp │ │ │ ├── nx_init.h │ │ │ ├── occlude.cpp │ │ │ ├── occlude.h │ │ │ ├── pcrtc.cpp │ │ │ ├── pcrtc.h │ │ │ ├── render.cpp │ │ │ ├── render.h │ │ │ ├── resource.cpp │ │ │ ├── resource.h │ │ │ ├── scene.cpp │ │ │ ├── scene.h │ │ │ ├── sprite.cpp │ │ │ ├── sprite.h │ │ │ ├── switches.h │ │ │ ├── texture.cpp │ │ │ ├── texture.h │ │ │ ├── texturemem.cpp │ │ │ ├── texturemem.h │ │ │ ├── types.h │ │ │ ├── vif.cpp │ │ │ ├── vif.h │ │ │ ├── vu0code.dsm │ │ │ ├── vu0code.h │ │ │ ├── vu1/ │ │ │ │ ├── defs.vsm │ │ │ │ ├── jumptab.vsm │ │ │ │ ├── main.vsm │ │ │ │ ├── newvu1code.dsm │ │ │ │ ├── newvu1code.h │ │ │ │ └── particle.vsm │ │ │ ├── vu1.cpp │ │ │ ├── vu1.h │ │ │ ├── vu1code.dsm │ │ │ ├── vu1code.h │ │ │ ├── vu1context.cpp │ │ │ └── vu1context.h │ │ ├── PaletteGen.cpp │ │ ├── PaletteGen.h │ │ ├── p_NxAnimCache.cpp │ │ ├── p_NxAnimCache.h │ │ ├── p_NxFont.cpp │ │ ├── p_NxFont.h │ │ ├── p_NxFontMan.cpp │ │ ├── p_NxGeom.cpp │ │ ├── p_NxGeom.h │ │ ├── p_NxImposter.cpp │ │ ├── p_NxImposter.h │ │ ├── p_NxLight.cpp │ │ ├── p_NxLight.h │ │ ├── p_NxLightMan.cpp │ │ ├── p_NxLightMan.h │ │ ├── p_NxLoadScreen.cpp │ │ ├── p_NxMesh.cpp │ │ ├── p_NxMesh.h │ │ ├── p_NxModel.cpp │ │ ├── p_NxModel.h │ │ ├── p_NxScene.cpp │ │ ├── p_NxScene.h │ │ ├── p_NxSector.cpp │ │ ├── p_NxSector.h │ │ ├── p_NxSprite.cpp │ │ ├── p_NxSprite.h │ │ ├── p_NxTexMan.cpp │ │ ├── p_NxTexture.cpp │ │ ├── p_NxTexture.h │ │ ├── p_NxTextured3dPoly.cpp │ │ ├── p_NxTextured3dPoly.h │ │ ├── p_NxViewMan.cpp │ │ ├── p_NxViewport.cpp │ │ ├── p_NxViewport.h │ │ ├── p_NxWin2D.cpp │ │ ├── p_NxWin2D.h │ │ ├── p_gfxman.cpp │ │ ├── p_memview.cpp │ │ ├── p_memview.h │ │ ├── p_nx.cpp │ │ ├── p_nxmiscfx.cpp │ │ ├── p_nxnewparticle.cpp │ │ ├── p_nxnewparticle.h │ │ ├── p_nxnewparticlemgr.cpp │ │ ├── p_nxnewparticlemgr.h │ │ ├── p_nxparticle.cpp │ │ ├── p_nxparticle.h │ │ ├── p_nxparticleflat.cpp │ │ ├── p_nxparticleflat.h │ │ ├── p_nxparticleglow.cpp │ │ ├── p_nxparticleglow.h │ │ ├── p_nxparticleglowribbontrail.cpp │ │ ├── p_nxparticleglowribbontrail.h │ │ ├── p_nxparticleline.cpp │ │ ├── p_nxparticleline.h │ │ ├── p_nxparticlenewflat.cpp │ │ ├── p_nxparticlenewflat.h │ │ ├── p_nxparticleribbon.cpp │ │ ├── p_nxparticleribbon.h │ │ ├── p_nxparticleribbontrail.cpp │ │ ├── p_nxparticleribbontrail.h │ │ ├── p_nxparticleshaded.cpp │ │ ├── p_nxparticleshaded.h │ │ ├── p_nxparticlesmooth.cpp │ │ ├── p_nxparticlesmooth.h │ │ ├── p_nxparticlesmoothribbon.cpp │ │ ├── p_nxparticlesmoothribbon.h │ │ ├── p_nxparticlesmoothstar.cpp │ │ ├── p_nxparticlesmoothstar.h │ │ ├── p_nxparticlestar.cpp │ │ ├── p_nxparticlestar.h │ │ ├── p_nxweather.cpp │ │ └── p_nxweather.h │ ├── NxAnimCache.cpp │ ├── NxAnimCache.h │ ├── NxFont.cpp │ ├── NxFont.h │ ├── NxFontMan.cpp │ ├── NxFontMan.h │ ├── NxGeom.cpp │ ├── NxGeom.h │ ├── NxHierarchy.h │ ├── NxImposter.cpp │ ├── NxImposter.h │ ├── NxLight.cpp │ ├── NxLight.h │ ├── NxLightMan.cpp │ ├── NxLightMan.h │ ├── NxLoadScreen.cpp │ ├── NxLoadScreen.h │ ├── NxMesh.cpp │ ├── NxMesh.h │ ├── NxMiscFX.cpp │ ├── NxMiscFX.h │ ├── NxModel.cpp │ ├── NxModel.h │ ├── NxNewParticle.cpp │ ├── NxNewParticle.h │ ├── NxNewParticleMgr.cpp │ ├── NxNewParticleMgr.h │ ├── NxQuickAnim.cpp │ ├── NxQuickAnim.h │ ├── NxScene.cpp │ ├── NxScene.h │ ├── NxSector.cpp │ ├── NxSector.h │ ├── NxSkinComponent.cpp │ ├── NxSkinComponent.h │ ├── NxSprite.cpp │ ├── NxSprite.h │ ├── NxTexMan.cpp │ ├── NxTexMan.h │ ├── NxTexture.cpp │ ├── NxTexture.h │ ├── NxTextured3dPoly.cpp │ ├── NxTextured3dPoly.h │ ├── NxViewMan.cpp │ ├── NxViewMan.h │ ├── NxViewport.cpp │ ├── NxViewport.h │ ├── NxWin2D.cpp │ ├── NxWin2D.h │ ├── Pose.h │ ├── Skeleton.cpp │ ├── Skeleton.h │ ├── XBox/ │ │ ├── NX/ │ │ │ ├── BillboardScreenAlignedVS.vsh │ │ │ ├── ParticleFlatVS.vsh │ │ │ ├── ParticleNewFlatPointSpriteVS.vsh │ │ │ ├── ParticleNewFlatVS.vsh │ │ │ ├── PixelShader0.psh │ │ │ ├── PixelShader0IVA.psh │ │ │ ├── PixelShader1.psh │ │ │ ├── PixelShader2.psh │ │ │ ├── PixelShader3.psh │ │ │ ├── PixelShader4.psh │ │ │ ├── PixelShader5.psh │ │ │ ├── PixelShaderBrighten.psh │ │ │ ├── PixelShaderBrightenIVA.psh │ │ │ ├── PixelShaderBumpWater.psh │ │ │ ├── PixelShaderFocusBlur.psh │ │ │ ├── PixelShaderFocusIntegrate.psh │ │ │ ├── PixelShaderFocusLookupIntegrate.psh │ │ │ ├── PixelShaderNULL.psh │ │ │ ├── PixelShaderPointSprite.psh │ │ │ ├── PixelShader_ShadowBuffer.psh │ │ │ ├── ShadowBufferStaticGeomPS.psh │ │ │ ├── ShadowBufferStaticGeomVS.vsh │ │ │ ├── WeightedMeshVS_1Weight.vsh │ │ │ ├── WeightedMeshVS_2Weight.vsh │ │ │ ├── WeightedMeshVS_3Weight.vsh │ │ │ ├── WeightedMeshVS_VXC_1Weight.vsh │ │ │ ├── WeightedMeshVS_VXC_1Weight_SBPassThru.vsh │ │ │ ├── WeightedMeshVS_VXC_1Weight_UVTransform.vsh │ │ │ ├── WeightedMeshVS_VXC_2Weight.vsh │ │ │ ├── WeightedMeshVS_VXC_2Weight_SBPassThru.vsh │ │ │ ├── WeightedMeshVS_VXC_2Weight_UVTransform.vsh │ │ │ ├── WeightedMeshVS_VXC_3Weight.vsh │ │ │ ├── WeightedMeshVS_VXC_3Weight_SBPassThru.vsh │ │ │ ├── WeightedMeshVS_VXC_3Weight_UVTransform.vsh │ │ │ ├── WeightedMeshVS_VXC_4Weight.vsh │ │ │ ├── WeightedMeshVS_VXC_4Weight_SBPassThru.vsh │ │ │ ├── WeightedMeshVS_VXC_Specular_1Weight.vsh │ │ │ ├── WeightedMeshVS_VXC_Specular_2Weight.vsh │ │ │ ├── WeightedMeshVS_VXC_Specular_3Weight.vsh │ │ │ ├── WeightedMeshVS_VXC_Specular_4Weight.vsh │ │ │ ├── WeightedMeshVertexShader0.vsh │ │ │ ├── WeightedMeshVertexShader1.vsh │ │ │ ├── WeightedMeshVertexShader_SBWrite.vsh │ │ │ ├── anim.cpp │ │ │ ├── anim.h │ │ │ ├── anim_vertdefs.h │ │ │ ├── billboard.cpp │ │ │ ├── billboard.h │ │ │ ├── chars.cpp │ │ │ ├── chars.h │ │ │ ├── gamma.cpp │ │ │ ├── gamma.h │ │ │ ├── grass.cpp │ │ │ ├── grass.h │ │ │ ├── instance.cpp │ │ │ ├── instance.h │ │ │ ├── material.cpp │ │ │ ├── material.h │ │ │ ├── mesh.cpp │ │ │ ├── mesh.h │ │ │ ├── mipmap.inl │ │ │ ├── nx_init.cpp │ │ │ ├── nx_init.h │ │ │ ├── occlude.cpp │ │ │ ├── occlude.h │ │ │ ├── particles.cpp │ │ │ ├── particles.h │ │ │ ├── render.cpp │ │ │ ├── render.h │ │ │ ├── scene.cpp │ │ │ ├── scene.h │ │ │ ├── screenfx.cpp │ │ │ ├── screenfx.h │ │ │ ├── sprite.cpp │ │ │ ├── sprite.h │ │ │ ├── swizzleformat.h │ │ │ ├── texture.cpp │ │ │ ├── texture.h │ │ │ ├── verlet.cpp │ │ │ ├── verlet.h │ │ │ └── xbmemfnt.h │ │ ├── p_NxGeom.cpp │ │ ├── p_NxGeom.h │ │ ├── p_NxImposter.cpp │ │ ├── p_NxImposter.h │ │ ├── p_NxLight.cpp │ │ ├── p_NxLight.h │ │ ├── p_NxLightMan.cpp │ │ ├── p_NxLoadScreen.cpp │ │ ├── p_NxMesh.cpp │ │ ├── p_NxMesh.h │ │ ├── p_NxModel.cpp │ │ ├── p_NxModel.h │ │ ├── p_NxParticleRibbonTrail.cpp │ │ ├── p_NxParticleRibbonTrail.h │ │ ├── p_NxParticleShaded.cpp │ │ ├── p_NxParticleShaded.h │ │ ├── p_NxParticleStar.cpp │ │ ├── p_NxParticleStar.h │ │ ├── p_NxSprite.cpp │ │ ├── p_NxSprite.h │ │ ├── p_NxTextured3dPoly.cpp │ │ ├── p_NxTextured3dPoly.h │ │ ├── p_NxViewMan.cpp │ │ ├── p_NxViewport.cpp │ │ ├── p_NxViewport.h │ │ ├── p_NxWin2D.cpp │ │ ├── p_NxWin2D.h │ │ ├── p_gfxman.cpp │ │ ├── p_loadscreen.cpp │ │ ├── p_memview.cpp │ │ ├── p_memview.h │ │ ├── p_nx.cpp │ │ ├── p_nxfont.cpp │ │ ├── p_nxfont.h │ │ ├── p_nxfontman.cpp │ │ ├── p_nxmiscfx.cpp │ │ ├── p_nxnewparticle.cpp │ │ ├── p_nxnewparticle.h │ │ ├── p_nxnewparticlemgr.cpp │ │ ├── p_nxnewparticlemgr.h │ │ ├── p_nxparticle.cpp │ │ ├── p_nxparticle.h │ │ ├── p_nxparticleflat.cpp │ │ ├── p_nxparticleflat.h │ │ ├── p_nxparticleglow.cpp │ │ ├── p_nxparticleglow.h │ │ ├── p_nxparticleglowribbontrail.cpp │ │ ├── p_nxparticleglowribbontrail.h │ │ ├── p_nxparticleline.cpp │ │ ├── p_nxparticleline.h │ │ ├── p_nxparticleribbon.cpp │ │ ├── p_nxparticleribbon.h │ │ ├── p_nxparticlesmooth.cpp │ │ ├── p_nxparticlesmooth.h │ │ ├── p_nxparticlesmoothribbon.cpp │ │ ├── p_nxparticlesmoothribbon.h │ │ ├── p_nxparticlesmoothstar.cpp │ │ ├── p_nxparticlesmoothstar.h │ │ ├── p_nxscene.cpp │ │ ├── p_nxscene.h │ │ ├── p_nxsector.cpp │ │ ├── p_nxsector.h │ │ ├── p_nxtexman.cpp │ │ ├── p_nxtexture.cpp │ │ ├── p_nxtexture.h │ │ ├── p_nxweather.cpp │ │ └── p_nxweather.h │ ├── baseanimcontroller.cpp │ ├── baseanimcontroller.h │ ├── bbox.cpp │ ├── bbox.h │ ├── blendchannel.cpp │ ├── blendchannel.h │ ├── camera.cpp │ ├── camera.h │ ├── debuggfx.cpp │ ├── debuggfx.h │ ├── gfxman.cpp │ ├── gfxman.h │ ├── gfxutils.cpp │ ├── gfxutils.h │ ├── nx.cpp │ ├── nx.h │ ├── nxflags.h │ ├── nxparticle.cpp │ ├── nxparticle.h │ ├── nxparticlemgr.cpp │ ├── nxparticlemgr.h │ ├── nxweather.cpp │ ├── nxweather.h │ ├── shadow.cpp │ ├── shadow.h │ ├── stdafx.h │ ├── subanimcontroller.cpp │ ├── subanimcontroller.h │ ├── vecfont.cpp │ └── vecfont.h ├── Sk/ │ ├── Components/ │ │ ├── EditorCameraComponent.cpp │ │ ├── EditorCameraComponent.h │ │ ├── GoalEditorComponent.cpp │ │ ├── GoalEditorComponent.h │ │ ├── ProjectileCollisionComponent.cpp │ │ ├── ProjectileCollisionComponent.h │ │ ├── RailEditorComponent.cpp │ │ ├── RailEditorComponent.h │ │ ├── SkaterAdjustPhysicsComponent.cpp │ │ ├── SkaterAdjustPhysicsComponent.h │ │ ├── SkaterBalanceTrickComponent.cpp │ │ ├── SkaterBalanceTrickComponent.h │ │ ├── SkaterCleanupStateComponent.cpp │ │ ├── SkaterCleanupStateComponent.h │ │ ├── SkaterCorePhysicsComponent.cpp │ │ ├── SkaterCorePhysicsComponent.h │ │ ├── SkaterEndRunComponent.cpp │ │ ├── SkaterEndRunComponent.h │ │ ├── SkaterFinalizePhysicsComponent.cpp │ │ ├── SkaterFinalizePhysicsComponent.h │ │ ├── SkaterFlipAndRotateComponent.cpp │ │ ├── SkaterFlipAndRotateComponent.h │ │ ├── SkaterFloatingNameComponent.cpp │ │ ├── SkaterFloatingNameComponent.h │ │ ├── SkaterGapComponent.cpp │ │ ├── SkaterGapComponent.h │ │ ├── SkaterLocalNetLogicComponent.cpp │ │ ├── SkaterLocalNetLogicComponent.h │ │ ├── SkaterLoopingSoundComponent.cpp │ │ ├── SkaterLoopingSoundComponent.h │ │ ├── SkaterMatrixQueriesComponent.cpp │ │ ├── SkaterMatrixQueriesComponent.h │ │ ├── SkaterNonLocalNetLogicComponent.cpp │ │ ├── SkaterNonLocalNetLogicComponent.h │ │ ├── SkaterPhysicsControlComponent.cpp │ │ ├── SkaterPhysicsControlComponent.h │ │ ├── SkaterProximityComponent.cpp │ │ ├── SkaterProximityComponent.h │ │ ├── SkaterRotateComponent.cpp │ │ ├── SkaterRotateComponent.h │ │ ├── SkaterRunTimerComponent.cpp │ │ ├── SkaterRunTimerComponent.h │ │ ├── SkaterScoreComponent.cpp │ │ ├── SkaterScoreComponent.h │ │ ├── SkaterSoundComponent.cpp │ │ ├── SkaterSoundComponent.h │ │ ├── SkaterStancePanelComponent.cpp │ │ ├── SkaterStancePanelComponent.h │ │ ├── SkaterStateComponent.cpp │ │ ├── SkaterStateComponent.h │ │ ├── SkaterStateHistoryComponent.cpp │ │ └── SkaterStateHistoryComponent.h │ ├── Engine/ │ │ ├── RectFeeler.cpp │ │ ├── RectFeeler.h │ │ ├── SuperSector.cpp │ │ ├── SuperSector.h │ │ ├── contact.cpp │ │ ├── contact.h │ │ ├── feeler.cpp │ │ ├── feeler.h │ │ ├── sounds.cpp │ │ └── sounds.h │ ├── GameNet/ │ │ ├── ExportMsg.h │ │ ├── GameHandler.cpp │ │ ├── GameMsg.h │ │ ├── GameNet.cpp │ │ ├── GameNet.h │ │ ├── Lobby.cpp │ │ ├── Lobby.h │ │ ├── Ngps/ │ │ │ ├── dnas.cpp │ │ │ ├── p_buddy.cpp │ │ │ ├── p_buddy.h │ │ │ ├── p_content.cpp │ │ │ ├── p_content.h │ │ │ ├── p_ezcommon.h │ │ │ ├── p_ezconfig.h │ │ │ ├── p_ezmain.h │ │ │ ├── p_libezcnf.cpp │ │ │ ├── p_libezcnf.h │ │ │ ├── p_netcnfif.h │ │ │ ├── p_netconfig.cpp │ │ │ ├── p_stats.cpp │ │ │ ├── p_stats.h │ │ │ └── snglue.cpp │ │ ├── Player.cpp │ │ ├── ServerList.cpp │ │ ├── XBox/ │ │ │ ├── p_auth.cpp │ │ │ ├── p_auth.h │ │ │ ├── p_buddy.cpp │ │ │ ├── p_buddy.h │ │ │ ├── p_match.cpp │ │ │ ├── p_match.h │ │ │ ├── p_voice.cpp │ │ │ └── p_voice.h │ │ └── scriptdebugger.h │ ├── Main.cpp │ ├── Modules/ │ │ ├── FrontEnd/ │ │ │ ├── FrontEnd.cpp │ │ │ └── FrontEnd.h │ │ ├── Skate/ │ │ │ ├── BettingGuy.cpp │ │ │ ├── BettingGuy.h │ │ │ ├── CATGoal.cpp │ │ │ ├── CATGoal.h │ │ │ ├── CompetitionGoal.cpp │ │ │ ├── CompetitionGoal.h │ │ │ ├── CreateATrick.cpp │ │ │ ├── CreateATrick.h │ │ │ ├── FilmGoal.cpp │ │ │ ├── FilmGoal.h │ │ │ ├── FindGapsGoal.cpp │ │ │ ├── FindGapsGoal.h │ │ │ ├── GameFlow.cpp │ │ │ ├── GameFlow.h │ │ │ ├── GameMode.cpp │ │ │ ├── GameMode.h │ │ │ ├── Goal.cpp │ │ │ ├── Goal.h │ │ │ ├── GoalManager.cpp │ │ │ ├── GoalManager.h │ │ │ ├── GoalPed.cpp │ │ │ ├── GoalPed.h │ │ │ ├── HorseGoal.cpp │ │ │ ├── HorseGoal.h │ │ │ ├── Minigame.cpp │ │ │ ├── Minigame.h │ │ │ ├── NetGoal.cpp │ │ │ ├── NetGoal.h │ │ │ ├── RaceGoal.cpp │ │ │ ├── RaceGoal.h │ │ │ ├── SkatetrisGoal.cpp │ │ │ ├── SkatetrisGoal.h │ │ │ ├── VictoryCond.cpp │ │ │ ├── VictoryCond.h │ │ │ ├── competition.cpp │ │ │ ├── competition.h │ │ │ ├── horse.cpp │ │ │ ├── horse.h │ │ │ ├── score.cpp │ │ │ ├── score.h │ │ │ ├── skate.cpp │ │ │ ├── skate.h │ │ │ └── skatenet.cpp │ │ └── Viewer/ │ │ ├── Viewer.cpp │ │ └── Viewer.h │ ├── Ngps/ │ │ └── crt0.s │ ├── Objects/ │ │ ├── FollowOb.h │ │ ├── GameObj.cpp │ │ ├── GameObj.h │ │ ├── MovingObject.cpp │ │ ├── MovingObject.h │ │ ├── PathMan.cpp │ │ ├── PathMan.h │ │ ├── PathOb.cpp │ │ ├── PathOb.h │ │ ├── PlayerProfileManager.cpp │ │ ├── PlayerProfileManager.h │ │ ├── SkaterButton.cpp │ │ ├── SkaterButton.h │ │ ├── SkaterPad.h │ │ ├── SkaterProfile.cpp │ │ ├── SkaterProfile.h │ │ ├── SkaterTricks.cpp │ │ ├── SkaterTricks.h │ │ ├── TrickObject.cpp │ │ ├── TrickObject.h │ │ ├── ViewerObj.cpp │ │ ├── ViewerObj.h │ │ ├── car.cpp │ │ ├── car.h │ │ ├── crown.cpp │ │ ├── crown.h │ │ ├── cutscenedetails.cpp │ │ ├── cutscenedetails.h │ │ ├── emitter.cpp │ │ ├── emitter.h │ │ ├── followob.cpp │ │ ├── gap.cpp │ │ ├── gap.h │ │ ├── manual.cpp │ │ ├── manual.h │ │ ├── moviecam.cpp │ │ ├── moviecam.h │ │ ├── moviedetails.cpp │ │ ├── moviedetails.h │ │ ├── navigation.cpp │ │ ├── navigation.h │ │ ├── objecthook.cpp │ │ ├── objecthook.h │ │ ├── ped.cpp │ │ ├── ped.h │ │ ├── proxim.cpp │ │ ├── proxim.h │ │ ├── rail.cpp │ │ ├── rail.h │ │ ├── records.cpp │ │ ├── records.h │ │ ├── restart.cpp │ │ ├── restart.h │ │ ├── skater.cpp │ │ ├── skater.h │ │ ├── skatercam.cpp │ │ ├── skatercam.h │ │ ├── skatercareer.cpp │ │ ├── skatercareer.h │ │ ├── skaterflags.h │ │ └── skaterpad.cpp │ ├── ParkEditor/ │ │ ├── EdRail.cpp │ │ ├── EdRail.h │ │ └── LoadPath.txt │ ├── ParkEditor2/ │ │ ├── EdMap.cpp │ │ ├── EdMap.h │ │ ├── GapManager.cpp │ │ ├── GapManager.h │ │ ├── ParkEd.cpp │ │ ├── ParkEd.h │ │ ├── ParkGen.cpp │ │ ├── ParkGen.h │ │ ├── clipboard.cpp │ │ └── clipboard.h │ ├── Scripting/ │ │ ├── cfuncs.cpp │ │ ├── cfuncs.h │ │ ├── ftables.cpp │ │ ├── ftables.h │ │ ├── gs_file.cpp │ │ ├── gs_file.h │ │ ├── gs_init.cpp │ │ ├── gs_init.h │ │ ├── mcfuncs.cpp │ │ ├── mcfuncs.h │ │ ├── nodearray.cpp │ │ ├── nodearray.h │ │ ├── skfuncs.cpp │ │ └── skfuncs.h │ ├── heap_sizes.h │ ├── language.h │ ├── ngc/ │ │ ├── crt0.s │ │ └── defs.s │ ├── product_codes.h │ └── template.h ├── Sys/ │ ├── Config/ │ │ ├── NGC/ │ │ │ └── p_config.cpp │ │ ├── NGPS/ │ │ │ └── p_config.cpp │ │ ├── Win32/ │ │ │ └── p_config.cpp │ │ ├── XBox/ │ │ │ └── p_config.cpp │ │ ├── config.cpp │ │ └── config.h │ ├── File/ │ │ ├── AsyncFilesys.cpp │ │ ├── AsyncFilesys.h │ │ ├── AsyncTypes.h │ │ ├── FileLibrary.cpp │ │ ├── FileLibrary.h │ │ ├── PRE.cpp │ │ ├── PRE.h │ │ ├── XBox/ │ │ │ ├── hed.cpp │ │ │ ├── hed.h │ │ │ ├── p_AsyncFilesys.cpp │ │ │ ├── p_AsyncFilesys.h │ │ │ ├── p_filesys.cpp │ │ │ ├── p_pre.cpp │ │ │ ├── p_streamer.cpp │ │ │ └── p_streamer.h │ │ ├── filesys.h │ │ ├── memfile.h │ │ ├── ngc/ │ │ │ ├── hed.cpp │ │ │ ├── hed.h │ │ │ ├── p_AsyncFilesys.cpp │ │ │ ├── p_asyncFilesys.h │ │ │ ├── p_filesys.cpp │ │ │ └── p_pre.cpp │ │ ├── ngps/ │ │ │ ├── FileIO/ │ │ │ │ ├── FIleIO_IOP.h │ │ │ │ ├── FileIO.h │ │ │ │ ├── Makefile │ │ │ │ ├── PathDefs │ │ │ │ ├── command.c │ │ │ │ └── start.c │ │ │ ├── hed.cpp │ │ │ ├── hed.h │ │ │ ├── p_AsyncFilesys.cpp │ │ │ ├── p_AsyncFilesys.h │ │ │ └── p_filesys.cpp │ │ ├── pip.cpp │ │ └── pip.h │ ├── McMan.h │ ├── Mem/ │ │ ├── CompactPool.cpp │ │ ├── CompactPool.h │ │ ├── PoolManager.cpp │ │ ├── PoolManager.h │ │ ├── Poolable.cpp │ │ ├── Poolable.h │ │ ├── alloc.cpp │ │ ├── alloc.h │ │ ├── handle.h │ │ ├── heap.cpp │ │ ├── heap.h │ │ ├── memdbg.h │ │ ├── memman.cpp │ │ ├── memman.h │ │ ├── memptr.h │ │ ├── memtest.cpp │ │ ├── memtest.h │ │ ├── pile.cpp │ │ ├── pile.h │ │ ├── pool.cpp │ │ ├── pool.h │ │ ├── region.cpp │ │ └── region.h │ ├── MemCard/ │ │ ├── NGPS/ │ │ │ └── p_McMan.cpp │ │ ├── XBox/ │ │ │ └── p_McMan.cpp │ │ └── ngc/ │ │ └── p_McMan.cpp │ ├── Profiler.cpp │ ├── Profiler.h │ ├── Replay/ │ │ ├── NGC/ │ │ │ └── p_replay.cpp │ │ ├── NGPS/ │ │ │ └── p_replay.cpp │ │ ├── Win32/ │ │ │ └── p_replay.cpp │ │ ├── XBox/ │ │ │ └── p_replay.cpp │ │ ├── replay.cpp │ │ └── replay.h │ ├── SIO/ │ │ ├── NGPS/ │ │ │ └── p_keyboard.cpp │ │ ├── XBox/ │ │ │ ├── p_keyboard.cpp │ │ │ ├── p_siodev.cpp │ │ │ └── p_sioman.cpp │ │ ├── keyboard.h │ │ ├── ngc/ │ │ │ ├── p_keyboard.cpp │ │ │ ├── siodev.cpp │ │ │ └── sioman.cpp │ │ ├── siodev.cpp │ │ └── sioman.cpp │ ├── Win32/ │ │ └── p_timer.cpp │ ├── XBox/ │ │ └── p_timer.cpp │ ├── demo.cpp │ ├── demo.h │ ├── ngc/ │ │ ├── p_anim.cpp │ │ ├── p_anim.h │ │ ├── p_anim_core.s │ │ ├── p_aram.cpp │ │ ├── p_aram.h │ │ ├── p_assert.h │ │ ├── p_atomic.cpp │ │ ├── p_atomic.h │ │ ├── p_bbox.cpp │ │ ├── p_bbox.h │ │ ├── p_buffer.cpp │ │ ├── p_buffer.h │ │ ├── p_camera.cpp │ │ ├── p_camera.h │ │ ├── p_clump.cpp │ │ ├── p_clump.h │ │ ├── p_collision.cpp │ │ ├── p_collision.h │ │ ├── p_debugfont.cpp │ │ ├── p_debugfont.h │ │ ├── p_display.cpp │ │ ├── p_display.h │ │ ├── p_dl.cpp │ │ ├── p_dl.h │ │ ├── p_dlman.cpp │ │ ├── p_dlman.h │ │ ├── p_dma.cpp │ │ ├── p_dma.h │ │ ├── p_dvd.cpp │ │ ├── p_dvd.h │ │ ├── p_file.cpp │ │ ├── p_file.h │ │ ├── p_font.cpp │ │ ├── p_frame.cpp │ │ ├── p_frame.h │ │ ├── p_gx.cpp │ │ ├── p_gx.h │ │ ├── p_hashid.cpp │ │ ├── p_hashid.h │ │ ├── p_hw.h │ │ ├── p_hwinit.cpp │ │ ├── p_hwpad.cpp │ │ ├── p_hwpad.h │ │ ├── p_light.cpp │ │ ├── p_light.h │ │ ├── p_material.cpp │ │ ├── p_material.h │ │ ├── p_matman.cpp │ │ ├── p_matman.h │ │ ├── p_matrix.cpp │ │ ├── p_matrix.h │ │ ├── p_model.cpp │ │ ├── p_model.h │ │ ├── p_particle.cpp │ │ ├── p_particle.h │ │ ├── p_ppcwgpipe.h │ │ ├── p_prim.cpp │ │ ├── p_prim.h │ │ ├── p_profile.cpp │ │ ├── p_profile.h │ │ ├── p_quat.cpp │ │ ├── p_quat.h │ │ ├── p_reftypes.h │ │ ├── p_render.cpp │ │ ├── p_render.h │ │ ├── p_scene.cpp │ │ ├── p_scene.h │ │ ├── p_screenshot.cpp │ │ ├── p_screenshot.h │ │ ├── p_slerp.cpp │ │ ├── p_slerp.h │ │ ├── p_tex.cpp │ │ ├── p_tex.h │ │ ├── p_texman.cpp │ │ ├── p_texman.h │ │ ├── p_timer.cpp │ │ ├── p_triangle.cpp │ │ ├── p_triangle.h │ │ ├── p_vector.cpp │ │ └── p_vector.h │ ├── ngps/ │ │ └── p_timer.cpp │ ├── siodev.h │ ├── sioman.h │ ├── sys.cpp │ ├── sys.h │ ├── timer.cpp │ └── timer.h ├── standard.cpp ├── template.cpp └── template.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: Code/Core/Debug/Assert.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (DBG) ** ** ** ** File name: assert.cpp ** ** ** ** Created by: 05/27/99 - mjb ** ** ** ** Description: Assert support code ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include #include #ifndef __PLAT_WN32__ #include #endif // __PLAT_WN32__ #ifdef __PLAT_NGPS__ int DumpUnwindStack( int iMaxDepth, int *pDest ); #include #endif #ifdef __PLAT_NGC__ #include #define _output OSReport #else #define _output printf #endif #ifdef __NOPT_ASSERT__ /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Dbg { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ static const int vASSERT_BUFFER_SIZE = 512; /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ static AssertTrap* assert_trap_handler = NULL; static bool screen_assert_active = false; // made public for Dbg_ Macros char* msg_null_pointer = "Null Pointer"; char* msg_misaligned_pointer = "Pointer not aligned"; char* msg_pointer_to_free_mem = "Pointer to free mem"; char* msg_unknown_reason = "No reason supplied"; char* msg_type_mismatch = "Type Mismatch: \"%s\" is of type \"%s\" - not a valid \"%s\""; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ void set_trap( AssertTrap* trap ) { assert_trap_handler = trap; } /******************************************************************/ /* */ /* */ /******************************************************************/ void screen_assert( bool on ) { screen_assert_active = on; } /******************************************************************/ /* */ /* */ /******************************************************************/ #ifdef __NOPT_DEBUG__ void Assert( char* file, uint line, Signature& sig, char* reason ) #else void Assert( char* file, uint line, char* reason ) #endif { static char assert_buffer1[vASSERT_BUFFER_SIZE]; #ifdef __NOPT_DEBUG static char assert_buffer2[vASSERT_BUFFER_SIZE]; #endif static char assert_buffer3[vASSERT_BUFFER_SIZE]; static char* tmp1 = assert_buffer1; #ifdef __NOPT_DEBUG static char* tmp2 = assert_buffer2; #endif static char* tmp3 = assert_buffer3; #if !( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ )) static int again = 0; if (again) { _output ("MEM CONTEXT: %s\n",Mem::Manager::sHandle().GetContextName()); _output( "LOOPED ASSERTION: %s(%d)\n%s\n\n", file, line, reason ); _output ("Real Assertion: %s\n%s\n",tmp1,tmp3); while (1); // and hang... } again = 1; _output ("\n--------------------------------------------------\nPLEASE COPY FROM A FEW LINES ABOVE HERE\n\nCURRENT MEM CONTEXT: %s\n\n" ,Mem::Manager::sHandle().GetContextName()); Mem::Manager& mem_man = Mem::Manager::sHandle(); // Mem::Heap* heap = mem_man.TopDownHeap(); // Mem::Region* region = heap->ParentRegion(); // _output ("TopDown Fragmentation %7dK, in %5d Blocks\n",heap->mFreeMem.m_count / 1024, heap->mFreeBlocks.m_count); // _output (" used %7dK, in %5d Blocks\n",heap->mUsedMem.m_count / 1024, heap->mUsedBlocks.m_count); // heap = mem_man.BottomUpHeap(); // _output ("BottomUp Fragmentation %7dK, in %5d Blocks\n",heap->mFreeMem.m_count / 1024, heap->mFreeBlocks.m_count); // _output (" used %7dK, in %5d Blocks\n",heap->mUsedMem.m_count / 1024, heap->mUsedBlocks.m_count); // _output ("Shared Region %dK free out of %d K available\n", region->MemAvailable() / 1024, region->TotalSize() / 1024 ); _output("Name Used Frag Free Min Blocks\n"); _output("--------------- ----- ----- ---- ------ ------\n"); Mem::Heap* heap; for (heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap)) { Mem::Region* region = heap->ParentRegion(); _output( "%12s: %5dK %4dK %4dK %4dK %5d \n", heap->GetName(), heap->mUsedMem.m_count / 1024, heap->mFreeMem.m_count / 1024, region->MemAvailable() / 1024, region->MinMemAvailable() / 1024, heap->mUsedBlocks.m_count ); } _output( "FILE: %s(%d)\nASSERTION: %s\n\n", file, line, reason ); _output( tmp1, "FILE: %s(%d) ", file, line ); _output( tmp3, "ASSERTION: %s", reason ); // attempt to dump the call stack // requires that you have the correct .map file, along with the executable #ifndef __PLAT_WN32__ MemView_FindLeaks(); #endif _output( "\nCALL STACK ..........................\n\n"); #ifdef __PLAT_NGPS__ DumpUnwindStack( 40, NULL ); #endif #endif sprintf( tmp1, "FILE: %s(%d) ", file, line ); sprintf( tmp3, "ASSERTION: %s", reason ); #ifndef __PLAT_NGC__ #ifndef __PLAT_WN32__ // Mick: Attempt to save a screenshot // Dbg_Printf("Attempting to save screenshot 'screens\\Assert???.bmp'\n"); if (!Config::CD()) { Gfx::Manager * gfx_manager = Gfx::Manager::Instance(); gfx_manager->ScreenShot( "Assert" ); } #endif if ( screen_assert_active ) { screen_assert_active = false; #ifndef __PLAT_WN32__ Gfx::Manager* gfx_man = Gfx::Manager::Instance(); gfx_man->AssertText ( 0, tmp1 ); #ifdef __NOPT_DEBUG sprintf( tmp2, "%s", &sig.GetName() ); gfx_man->AssertText ( 1, tmp2 ); #endif gfx_man->AssertText ( 2, tmp3 ); gfx_man->AssertFlush(); #endif // __PLAT_WN32__ } #endif // __PLAT_NGC__ #ifdef __NOPT_DEBUG sprintf( tmp1, "ASSERTION: %s(%d) %s\n%s\n\n", file, line, &sig.GetName(), reason ); #else sprintf( tmp1, "FILE: %s(%d)\nASSERTION: %s\n\n", file, line, reason ); #endif #ifdef __PLAT_NGPS__ #if 0 // does not seem to output anything... sceDevConsInit(); // Initialise screen console output int console = sceDevConsOpen( (2048 - 640/2 + 20) << 4, // top left GS X primitive coord (2048 - 448/2 + 20) << 4, // top left GS Y primitive coord 80, // Number of chars (X) 5); // Number of chars (Y) sceDevConsAttribute(console, 7); // Output in white (default colours can be // redefined by sceDevConsSetColor) sceDevConsClear(console); sceDevConsPrintf(console, tmp1); sceDevConsPrintf(console, "blah \n"); sceDevConsPrintf(console, "blah2 \n"); sceDevConsPrintf(console, "blah3 \n"); sceDevConsDraw(console); #endif #endif if ( assert_trap_handler != NULL ) { _output ("MEM CONTEXT: %s\n",Mem::Manager::sHandle().GetContextName()); Dbg_Printf( "%s\n", tmp1 ); assert_trap_handler( tmp1 ); } else { Dbg_Printf( "%s\n", tmp1 ); Dbg_Printf( "!!NO TRAP HANDLER SET!!\n" ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ #if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ )) void assert_vcc( char* file, uint line, char* reason ) { static char assert_buffer[vASSERT_BUFFER_SIZE]; static char* tmp = assert_buffer; sprintf( tmp, "ASSERTION: %s(%d)\n%s\n\n", file, line, reason ); if ( assert_trap_handler != NULL ) { Dbg_Printf( "%s\n", tmp ); assert_trap_handler( tmp ); } else { Dbg_Printf( "%s\n", tmp ); Dbg_Printf( "!!NO TRAP HANDLER SET!!\n" ); } } #endif // #ifdef __PLAT_XBOX__ /***************************************************************************** ** Public Functions ** *****************************************************************************/ } // namespace Dbg #endif //__NOPT_DEBUG__ ================================================ FILE: Code/Core/Debug/Checks.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (Dbg_) ** ** ** ** Created: 05/27/99 mjb ** ** ** ** File name: core/debug/checks.h ** ** ** *****************************************************************************/ #ifndef __CORE_DEBUG_CHECKS_H #define __CORE_DEBUG_CHECKS_H #ifndef __CORE_DEBUG_LOG_H #include #endif /***************************************************************************** ** Includes ** *****************************************************************************/ #include "signatrs.h" #ifdef __NOPT_ASSERT__ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Dbg { /***************************************************************************** ** Type Defines ** *****************************************************************************/ typedef void ( AssertTrap ) ( char* message ); /***************************************************************************** ** Private Declarations ** *****************************************************************************/ extern char* msg_unknown_reason; // message strings extern char* msg_null_pointer; extern char* msg_misaligned_pointer; extern char* msg_pointer_to_free_mem; extern char* msg_type_mismatch; extern AssertTrap default_trap; /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ #ifdef __NOPT_DEBUG__ void Assert ( char* file, uint line, Signature& sig, char* reason = msg_unknown_reason ); #else void Assert ( char* file, uint line, char* reason = msg_unknown_reason ); #endif void pad_printf ( const char* text, ... ); void set_trap( AssertTrap* trap = default_trap ); void screen_assert( bool on = false ); /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Dbg /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Macros ** *****************************************************************************/ #define Dbg_SetTrap(A) { Dbg::set_trap(A); } #define Dbg_SetScreenAssert(A) { Dbg::screen_assert(A); } /******************************************************************/ /* Assertions */ /* */ /******************************************************************/ #ifdef __NOPT_DEBUG__ #define Dbg_Assert(_c) \ \ if ( !(_c)) \ { \ Dbg::Assert( __FILE__, __LINE__, Dbg_signature ); \ } /******************************************************************/ /* */ /* */ /******************************************************************/ #define Dbg_MsgAssert( _c, _params ) \ \ if( !( _c )) \ { \ Dbg::pad_printf _params; \ Dbg::Assert( __FILE__, __LINE__, Dbg_signature, \ Dbg::sprintf_pad ); \ } #else // __NOPT_DEBUG__ // Mick: If we have assertions, but are not in DEBUG mode // then we don not have the Dbg_signature thing // so we just pass NULL to assert() #define Dbg_Assert(_c) \ \ if ( !(_c)) \ { \ Dbg::Assert ( __FILE__, __LINE__, NULL ); \ } /******************************************************************/ /* */ /* */ /******************************************************************/ #ifdef __PLAT_WN32__ #ifdef _CONSOLE #define Dbg_MsgAssert( _c, _params ) \ \ if( !( _c )) \ { \ printf("\nGame code assert!\n"); \ printf("File %s, line %d\n",__FILE__,__LINE__); \ printf _params; \ printf("\nPress CTRL-C to quit ... "); \ while(1); \ } #else #define Dbg_MsgAssert( _c, _params ) #endif #else #define Dbg_MsgAssert( _c, _params ) \ \ if( !( _c )) \ { \ Dbg::pad_printf _params; \ Dbg::Assert( __FILE__, __LINE__, Dbg::sprintf_pad ); \ } #endif #endif // __NOPT_DEBUG__ #ifdef __PLAT_NGPS__ #define Dbg_MsgLog( _params ) \ { \ Dbg::pad_printf _params; \ Log::AddEntry(__FILE__,__LINE__,__PRETTY_FUNCTION__,Dbg::sprintf_pad); \ } #define Dbg_Log( ) \ { \ Log::AddEntry(__FILE__,__LINE__,__PRETTY_FUNCTION__); \ } #else #define Dbg_MsgLog( _params ) \ { \ Dbg::pad_printf _params; \ Log::AddEntry(__FILE__,__LINE__,__FUNCTION__,Dbg::sprintf_pad); \ } #define Dbg_Log( ) \ { \ Log::AddEntry(__FILE__,__LINE__,__FUNCTION__); \ } #endif /******************************************************************/ /* */ /* */ /******************************************************************/ #ifdef __NOPT_MEM_DEBUG__ #define Dbg_AssertPtr(_p) \ { \ if ((_p) == NULL ) \ { \ Dbg::Assert ( __FILE__, __LINE__, \ Dbg_signature, Dbg::msg_null_pointer ); \ } \ else if ( *((uint64*)(nAlignDown(_p))) == Mem::vFREE_BLOCK ) \ { \ Dbg::Assert ( __FILE__, __LINE__, \ Dbg_signature, Dbg::msg_pointer_to_free_mem ); \ } \ } #else // __NOPT_MEM_DEBUG__ #ifdef __NOPT_DEBUG__ #define Dbg_AssertPtr(_p) \ { \ if ((_p) == NULL ) \ { \ Dbg::Assert ( __FILE__, __LINE__, \ Dbg_signature, Dbg::msg_null_pointer ); \ } \ } #else #define Dbg_AssertPtr(_p) \ { \ if ((_p) == NULL ) \ { \ Dbg::Assert ( __FILE__, __LINE__, \ Dbg::msg_null_pointer ); \ } \ } #endif //__NOPT_DEBUG__ #endif // __NOPT_MEM_DEBUG__ /******************************************************************/ /* Type Checking */ /* */ /******************************************************************/ #ifdef __NOPT_FULL_DEBUG__ #define Dbg_AssertType(_c,_t) \ { \ Dbg_AssertPtr(_c); \ Dbg_MsgAssert( nAligned(_c), Dbg::msg_misaligned_pointer ); \ Dbg_MsgAssert(((_c)->classStamp != Spt::Class::vDELETED_CLASS ),"Deleted Class" ); \ Dbg_MsgAssert(((_c)->classStamp == Spt::Class::vREGISTERED_CLASS ), "Corrupt Class" ); \ Dbg_MsgAssert(((_c)->vClassNode()->IsDerivedOrSame(*_t::sClassNode())), \ Dbg::msg_type_mismatch,#_c,(_c)->vClassNode()->GetName(),#_t); \ } #define Dbg_AssertThis \ { \ Dbg_AssertPtr(this); \ Dbg_MsgAssert( nAligned(this), Dbg::msg_misaligned_pointer ); \ Dbg_MsgAssert((classStamp != Spt::Class::vDELETED_CLASS ),"Deleted Class" ); \ Dbg_MsgAssert((classStamp == Spt::Class::vREGISTERED_CLASS ), "Corrupt Class" ); \ Dbg_MsgAssert((vClassNode()->IsDerivedOrSame(*sClassNode())), \ Dbg::msg_type_mismatch,"this", vClassNode()->GetName(),sClassNode()->GetName()); \ } #else // __NOPT_FULL_DEBUG__ #define Dbg_AssertType(_c,_t) Dbg_AssertPtr(_c) #define Dbg_AssertThis Dbg_AssertPtr(this) #endif // __NOPT_FULL_DEBUG__ /***************************************************************************** ** Stubs ** *****************************************************************************/ #else #define Dbg_MsgAssert( _c, _params ) #define Dbg_SetTrap(_f) #ifdef __NOPT_ASSERT__ #define Dbg_SetScreenAssert(A) { Dbg::screen_assert(A); } #else #define Dbg_SetScreenAssert(_b) #endif #define Dbg_Assert(_c) #define Dbg_AssertPtr(_p) #define Dbg_AssertType(_c,_t) #define Dbg_AssertThis #define Dbg_MsgLog( _params ) #define Dbg_Log( ) #endif // __NOPT_ASSERT__ /******************************************************************/ /* */ /* */ /******************************************************************/ #endif // __CORE_DEBUG_CHECKS_H ================================================ FILE: Code/Core/Debug/Debug.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (DBG) ** ** ** ** File name: debug.cpp ** ** ** ** Created by: 05/27/99 - mjb ** ** ** ** Description: Debug message support. ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #define __ERROR_STRINGS__ #include #include #include #include #include #include #include #ifdef __PLAT_NGC__ #include #endif __PLAT_NGC__ #ifdef __NOPT_ASSERT__ /***************************************************************************** ** DBG Information ** *****************************************************************************/ Dbg_DefineProject( Core, "Core Library") namespace Dbg { /***************************************************************************** ** Externals ** *****************************************************************************/ extern void set_up ( void ); extern void close_down ( void ); extern OutputCode default_print; extern AssertTrap default_trap; /***************************************************************************** ** Defines ** *****************************************************************************/ static const int vBUFFER_SIZE = 512; /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ static char s_sprintf_buffer[vBUFFER_SIZE]; static char s_printf_buffer[vBUFFER_SIZE]; static Flags< Level > s_level_mask = mNONE; #ifdef __NOPT_DEBUG__ static char* s_typename[] = { "ERROR!!", "WARNING", "Notify ", "Message" }; #endif static OutputCode* output; // public for Dbg_ Macros char* sprintf_pad = s_sprintf_buffer; char* printf_pad = s_printf_buffer; #ifdef __NOPT_DEBUG__ Signature* current_sig; #endif /***************************************************************************** ** Public Data ** *****************************************************************************/ #ifdef __NOPT_DEBUG__ Project* RegisteredProjects = NULL; Module* RegisteredModules = NULL; #endif /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ void print( char* text ); /***************************************************************************** ** Private Functions ** *****************************************************************************/ static void s_prefixed_output( Level level, char* text, va_list args ) { Dbg_AssertPtr( text ); Dbg_Assert( level >= vERROR ); Dbg_Assert( level <= vMESSAGE ); if ( s_level_mask.Test( level )) { return; } #ifdef __NOPT_DEBUG__ printf( "[%s] %s - ", s_typename[level], ¤t_sig->GetName()); #endif vsprintf( printf_pad, text, args); Dbg::print( printf_pad ); Dbg::print( "\n" ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void pad_printf( const char* text, ... ) { Dbg_AssertPtr( text ); va_list args; va_start( args, text ); vsprintf( sprintf_pad, text, args); va_end( args ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void print( char* text ) { if ( output ) { output( text ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void message ( char *text, ... ) { Dbg_AssertPtr( text ); if ( s_level_mask.TestMask ( mMESSAGE )) { return; } va_list args; va_start( args, text ); s_prefixed_output( vMESSAGE, text, args ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void notify( char *text, ... ) { Dbg_AssertPtr( text ); if ( s_level_mask.TestMask ( mNOTIFY )) { return; } va_list args; va_start( args, text ); s_prefixed_output( vNOTIFY, text, args ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void warning( char *text, ... ) { Dbg_AssertPtr( text ); if ( s_level_mask.TestMask ( mWARNING )) { return; } va_list args; va_start( args, text ); s_prefixed_output( vWARNING, text, args ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void error( char *text, ... ) { Dbg_AssertPtr( text ); if ( s_level_mask.TestMask ( mERROR )) { return; } va_list args; va_start( args, text ); s_prefixed_output( vERROR, text, args ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void set_output( OutputCode* handler ) { Dbg_AssertPtr( handler ); output = handler; } /******************************************************************/ /* */ /* */ /******************************************************************/ void level_mask( Flags< Mask > mask ) { s_level_mask.SetMask( mask ); } /***************************************************************************** ** Public Functions ** *****************************************************************************/ void SetUp( void ) { set_trap( default_trap ); set_output( Dbg::default_print ); #ifdef __NOPT_DEBUG__ set_up(); // platform specific setup #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ void CloseDown( void ) { #ifdef __NOPT_DEBUG__ close_down(); // platform specific closedown #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Dbg #endif // __NOPT_DEBUG__ #ifdef __PLAT_NGPS__ extern "C" { int snputs(const char* pszStr); } #endif // Need to do this, cos otherwise the printf inside OurPrintf will get converted to // an OurPrintf, & it will infinitely recurse. #undef printf int OurPrintf ( const char* fmt, ... ) { // Don't want printf's in some builds. # if defined( __PLAT_XBOX__ ) && !defined( __NOPT_ASSERT__ ) return 0; # endif int ret=-1; char p_buffer[1024]; char *p_buf = p_buffer; #if 1 p_buf[0]=0; #else sprintf (p_buf,"%6d: ",(int)Tmr::GetRenderFrame()); p_buf+=strlen(p_buf); #endif va_list args; va_start( args, fmt ); vsprintf(p_buf,fmt,args); va_end( args ); switch (Config::GetHardware()) { #ifdef __PLAT_NGPS__ case Config::HARDWARE_PS2_PROVIEW: ret=snputs(p_buffer); break; case Config::HARDWARE_PS2_DEVSYSTEM: case Config::HARDWARE_PS2: // If we are capturing to a file, then redirect there if (dumping_printfs == 1) { dump_printf(p_buffer); } printf("%s",p_buffer); break; #endif #ifdef __PLAT_NGC__ case Config::HARDWARE_NGC: OSReport("%s",p_buffer); break; #endif #ifdef __PLAT_XBOX__ case Config::HARDWARE_XBOX: OutputDebugString(p_buffer); break; #endif default: break; } return ret; } uint32 quick_check_checksum(uint32 _i, const char *_s, const char *f, int line); uint32 check_checksum(uint32 _i, const char *_s, const char *f, int line) { #ifdef __PLAT_NGPS__ uint32* ra; asm ( "daddu %0, $31, $0" : "=r" (ra) ); Dbg_MsgAssert(_i == Crc::GenerateCRCFromString(_s),("%s:%d: Checksum 0x%x does not match %s, should be 0x%x",f,line,_i,_s,Crc::GenerateCRCFromString(_s))); // After the assert has been run once, there is no need to run it again, so // patch up the calling code to call quick_check_checksum instead // we just need to inspect the code just before ra, // make sure it contains the instructions for calling "check_checksum" // and replace it with the equivalent for "quick_check_checksum" // This will still all get compiled out for the release build // but this method lets us validata all usages of CRCD that get called. // Note, due to I-Cache, this code might still be executed several times // as the routine will continue to be called until the cache entry for the // call is overwritten uint32 JAL_check_checksum = 0x0c000000 + ((uint32)check_checksum >> 2); uint32 JAL_quick_check_checksum = 0x0c000000 + ((uint32)quick_check_checksum >> 2); if (ra[-2] == JAL_check_checksum) { ra[-2] = JAL_quick_check_checksum; } return quick_check_checksum(_i,_s,f,line); #else return _i; #endif } uint32 quick_check_checksum(uint32 _i, const char *_s, const char *f, int line) { return _i; } ================================================ FILE: Code/Core/Debug/Mem_stat.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Host (HOST_) ** ** ** ** File name: core/host.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_DEBUG_MEM_STAT_H #define __CORE_DEBUG_MEM_STAT_H #ifdef __NOPT_DEBUG__ /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Type Defines ** *****************************************************************************/ class Dbg_MEMORY_STATS { public : Dbg_MEMORY_STATS ( uint total = 0, uint spike = 0, uint system = 0, uint fixed = 0 ); private : uint total; // total memory currently allocated on behalf of this module/project uint spike; // peak total memory allocated on behalf of this module/project uint system; // current module/project private memory that is adjustable by the user uint fixed; // current module/project private memory that is a fixed overhead }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Macros ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ inline Dbg_MEMORY_STATS::Dbg_MEMORY_STATS ( uint total, uint spike, uint system, uint fixed ) : total ( total ), spike ( spike ), system ( system ), fixed ( fixed ) { } #endif // __NOPT_DEBUG__ /******************************************************************/ /* */ /* */ /******************************************************************/ #endif // __CORE_DEBUG_SIGNATRS_H ================================================ FILE: Code/Core/Debug/Messages.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (Dbg_) ** ** ** ** File name: core/debug/messages.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_DEBUG_MESSAGES_H #define __CORE_DEBUG_MESSAGES_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include #ifdef __NOPT_ASSERT__ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Dbg { enum Level { vERROR, vWARNING, vNOTIFY, vMESSAGE, vPRINTF }; enum Mask { mERROR = (1< mask ); void message( char* text, ...); void notify ( char* text, ...); void warning( char* text, ...); void error ( char* text, ...); /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ } // namespace Dbg /***************************************************************************** ** Macros ** *****************************************************************************/ #if (defined ( __PLAT_XBOX__ ) || defined( __PLAT_WN32__ )) inline void Dbg_SetOutput( const char* A ... ) {}; #define Dbg_LevelMask(A) { Dbg::level_mask(A); } inline void Dbg_Printf( const char* A ... ) {}; inline void Dbg_Message( const char* A ... ) {}; inline void Dbg_Notify( const char* A ... ) {}; inline void Dbg_Warning( const char* A ... ) {}; inline void Dbg_Error( const char* A ... ) {}; #else #define Dbg_SetOutput(A...) { Dbg::set_output(##A); } #define Dbg_LevelMask(A) { Dbg::level_mask(A); } #ifdef __NOPT_DEBUG__ #define Dbg_Printf(A...) { printf(##A); } #define Dbg_Message(A...) { Dbg::current_sig = &Dbg_signature; Dbg::message(##A); } #define Dbg_Notify(A...) { Dbg::current_sig = &Dbg_signature; Dbg::notify(##A); } #define Dbg_Warning(A...) { Dbg::current_sig = &Dbg_signature; Dbg::warning(##A); } #define Dbg_Error(A...) { Dbg::current_sig = &Dbg_signature; Dbg::error(##A); } #else #define Dbg_Printf(A...) { printf(##A); } #define Dbg_Message(A...) { Dbg::message(##A); } #define Dbg_Notify(A...) { Dbg::notify(##A); } #define Dbg_Warning(A...) { Dbg::warning(##A); } #define Dbg_Error(A...) { Dbg::error(##A); } #endif #endif // __PLAT_XBOX__ /***************************************************************************** ** Stubs ** *****************************************************************************/ #else #if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ )) inline void Dbg_SetOutput( const char* A ... ) {}; #define Dbg_LevelMask(A) inline void Dbg_Printf( const char* A ... ) {}; inline void Dbg_Message( const char* A ... ) {}; inline void Dbg_Notify( const char* A ... ) {}; inline void Dbg_Warning( const char* A ... ) {}; inline void Dbg_Error( const char* A ... ) {}; #else #define Dbg_SetOutput(A...) #define Dbg_LevelMask(A) #define Dbg_Printf(A...) #define Dbg_Message(A...) #define Dbg_Notify(A...) #define Dbg_Warning(A...) #define Dbg_Error(A...) #endif #endif // __NOPT_MESSAGES__ /******************************************************************/ /* */ /* */ /******************************************************************/ // A special printf function that only works for Ryan // (since I love them so much) #if defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ ) inline void Ryan(const char* A ...) {}; inline void Ken(const char* A ...) {}; inline void Matt(const char* A ...) {}; #else #ifdef __USER_RYAN__ #define Ryan(A...) printf(##A) #else #define Ryan(A...) #endif #if defined(__USER_KEN__) && defined(__NOPT_DEBUG__) #define Ken(A...) printf(##A) #else #define Ken(A...) #endif #if defined(__USER_MATT__) && defined(__NOPT_DEBUG__) #define Matt(A...) printf(##A) #else #define Matt(A...) #endif #endif #endif // __CORE_DEBUG_MESSAGES_H ================================================ FILE: Code/Core/Debug/Module.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Confidential Information ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (Dbg_) ** ** ** ** File name: core/debug/module.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_DEBUG_MODULE_H #define __CORE_DEBUG_MODULE_H #ifdef __NOPT_DEBUG__ /***************************************************************************** ** Includes ** *****************************************************************************/ #include "mem_stat.h" /***************************************************************************** ** Defines ** *****************************************************************************/ // Unfortunately, Visual C++ does not support the __PRETTY_FUNCTION__ predefined // name. The most information we can get at is __FILE__. #if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ )) #define __PRETTY_FUNCTION__ __FILE__ #endif /***************************************************************************** ** Type Defines ** *****************************************************************************/ namespace Dbg { class Project; class Module { public : Module ( Project& proj, const char& prefix, const char& description ); void SetNext ( const Module* next ); void SetSibling ( const Module* sibling ); const Module* GetNext ( void ) const; const Module* GetSibling ( void ) const; private : const Module* m_next; // pointer to next registered module const Module* m_sibling; // pointer to next module in same project const char& m_prefix; // module prefix const char& m_description; // module description Project& m_project; // project that this modules belongs to Dbg_MEMORY_STATS m_stats; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ extern Module* RegisteredModules; } // namespace Dbg /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Macros ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ namespace Dbg { inline Module::Module( Project& proj, const char& pref, const char& desc ) : m_prefix ( pref ), m_description ( desc ), m_project ( proj ) { m_next = Dbg::RegisteredModules; // add module to main registration list Dbg::RegisteredModules = this; m_sibling = m_project.GetChildren(); // add module to its project parent m_project.SetChildren( this ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Module::SetNext( const Module* next ) { m_next = next; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Module::SetSibling( const Module* sibling ) { m_sibling = sibling; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline const Module* Module::GetNext( void ) const { return m_next; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline const Module* Module::GetSibling( void ) const { return m_sibling; } } // namespace Dbg /***************************************************************************** ** Stubs ** *****************************************************************************/ #else #endif //__NOPT_DEBUG__ /******************************************************************/ /* */ /* */ /******************************************************************/ #endif // __CORE_DEBUG_MODULE_H ================================================ FILE: Code/Core/Debug/NGPS/P_debug.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (DBG) ** ** ** ** File name: p_debug.cpp ** ** ** ** Created by: 05/27/99 - mjb ** ** ** ** Description: Platform specific debug code ** ** ** *****************************************************************************/ #include #include #include #include #ifdef __NOPT_DEBUG__ /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Externals ** *****************************************************************************/ extern char _dbg_start[]; extern char _dbg_end[]; /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Dbg { /***************************************************************************** ** Defines ** *****************************************************************************/ #ifdef __NOPT_FULL_DEBUG__ enum { vMAX_CLASS_NODES = 28000, vMAX_INSTANCE_NODES = 100000 }; static const int vFREE_CLASS_BLOCK_ID = 0x02030405; static const int vFREE_INST_BLOCK_ID = 0x27354351; /***************************************************************************** ** Private Types ** *****************************************************************************/ struct FreeNode { FreeNode* next; int id; } ; union ClassNodeBlock { char pad[sizeof(Spt::ClassNode)]; FreeNode free_node; }; union InstanceNodeBlock { char pad[sizeof(Spt::ClassNode::InstanceNode)]; FreeNode free_node; }; /***************************************************************************** ** Private Data ** *****************************************************************************/ static ClassNodeBlock* spClassNodeFreeList = NULL; static InstanceNodeBlock* spInstanceNodeFreeList = NULL; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ void set_up( void ) { spClassNodeFreeList = (ClassNodeBlock*)_dbg_start; ClassNodeBlock* p_class_node = spClassNodeFreeList; for( int i = 0; i < ( vMAX_CLASS_NODES - 1 ); i++ ) { p_class_node->free_node.id = vFREE_CLASS_BLOCK_ID; p_class_node->free_node.next = (FreeNode*)(++p_class_node); } p_class_node->free_node.id = vFREE_CLASS_BLOCK_ID; (p_class_node++)->free_node.next = (FreeNode*)spClassNodeFreeList; spInstanceNodeFreeList = (InstanceNodeBlock*)(++p_class_node); InstanceNodeBlock* p_inst_node = spInstanceNodeFreeList; for( int i = 0; i < ( vMAX_INSTANCE_NODES - 1 ) ; i++ ) { p_inst_node->free_node.id = vFREE_INST_BLOCK_ID; p_inst_node->free_node.next = (FreeNode*)(++p_inst_node); } p_inst_node->free_node.id = vFREE_INST_BLOCK_ID; (p_inst_node++)->free_node.next = (FreeNode*)spInstanceNodeFreeList; Dbg_MsgAssert( (int)p_inst_node <= (int)_dbg_end, "Dbg Mem Block not big enough (%d bytes too small)", (int)p_inst_node - (int)_dbg_end ); if ( (int)p_inst_node < ((int)_dbg_end)) { Dbg_Warning ( "%dbytes unused in Dbg mem block",((int)_dbg_end - (int)p_inst_node)); } Dbg_Notify ( "Dbg Mem Block Allocated successfully" ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void close_down( void ) { int count = 0; ClassNodeBlock* p_class_node = spClassNodeFreeList; InstanceNodeBlock* p_inst_node = spInstanceNodeFreeList; do { Dbg_MsgAssert ( p_class_node->free_node.id == vFREE_CLASS_BLOCK_ID, "Block not free" ); p_class_node = (ClassNodeBlock*)p_class_node->free_node.next; count++; } while ( p_class_node != spClassNodeFreeList ); Dbg_MsgAssert ( count == vMAX_CLASS_NODES, "%d Class Nodes still registered", vMAX_CLASS_NODES - count ); count = 0; do { Dbg_MsgAssert ( p_inst_node->free_node.id == vFREE_INST_BLOCK_ID, "Block not free" ); p_inst_node = (InstanceNodeBlock*)p_inst_node->free_node.next; count++; } while ( p_inst_node != spInstanceNodeFreeList ); Dbg_MsgAssert ( count == vMAX_INSTANCE_NODES, "%d Instance Nodes still registered", vMAX_INSTANCE_NODES - count ); Dbg_Notify ( "Dbg Mem Block Released successfully" ); } /***************************************************************************** ** Public Functions ** *****************************************************************************/ void* NewClassNode( size_t size ) { void* p_ret = (void*)spClassNodeFreeList; Dbg_MsgAssert ( (int)spClassNodeFreeList != (int)(spClassNodeFreeList->free_node.next), "ClassNode pool full" ); Dbg_MsgAssert ( spClassNodeFreeList->free_node.id == vFREE_CLASS_BLOCK_ID, "Not a Free Class Node" ); spClassNodeFreeList = (ClassNodeBlock*)spClassNodeFreeList->free_node.next; // printf("NewClassNode %p next %p\n",p_ret, spClassNodeFreeList); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void DeleteClassNode( void* pMem ) { Dbg_MsgAssert ( (((int)pMem >= (int)_dbg_start ) && (((int)pMem < (int)_dbg_end ))), "Memory not in Debug block (%p)",pMem ); ClassNodeBlock* p_freeblock = (ClassNodeBlock*)pMem; p_freeblock->free_node.next = (FreeNode*)spClassNodeFreeList; spClassNodeFreeList = (ClassNodeBlock*)p_freeblock; spClassNodeFreeList->free_node.id = vFREE_CLASS_BLOCK_ID; // printf("DeleteClassNode %p next %p\n",spClassNodeFreeList, p_freeblock->free_node.next); } /******************************************************************/ /* */ /* */ /******************************************************************/ void* NewInstanceNode( size_t size ) { void* p_ret = (void*)spInstanceNodeFreeList; Dbg_MsgAssert ( (int)spInstanceNodeFreeList != (int)(spInstanceNodeFreeList->free_node.next), "InstanceNode pool full" ); Dbg_MsgAssert ( spInstanceNodeFreeList->free_node.id == vFREE_INST_BLOCK_ID, "Not a Free Instance Node" ); spInstanceNodeFreeList = (InstanceNodeBlock*)spInstanceNodeFreeList->free_node.next; // printf("NewInstanceNode %p next %p\n",p_ret, spInstanceNodeFreeList); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void DeleteInstanceNode( void* pMem ) { Dbg_MsgAssert ( (((int)pMem >= (int)_dbg_start ) && (((int)pMem < (int)_dbg_end ))), "Memory not in Debug block (%p)",pMem ); InstanceNodeBlock* p_freeblock = (InstanceNodeBlock*)pMem; p_freeblock->free_node.next = (FreeNode*)spInstanceNodeFreeList; spInstanceNodeFreeList = (InstanceNodeBlock*)p_freeblock; spInstanceNodeFreeList->free_node.id = vFREE_INST_BLOCK_ID; // printf("DeleteInstanceNode %p next %p\n",spInstanceNodeFreeList, p_freeblock->free_node.next); } /******************************************************************/ /* */ /* */ /******************************************************************/ #else // __NOPT_FULL_DEBUG__ void set_up( void ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void close_down( void ) { } #endif // __NOPT_FULL_DEBUG__ /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Dbg #endif // __NOPT_DEBUG__ #ifdef __NOPT_ASSERT__ namespace Dbg { /******************************************************************/ /* */ /* */ /******************************************************************/ void default_print( char *text ) { std::printf( text ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void default_trap( char* mess ) { uint* ptr = reinterpret_cast< uint* >( 0x00000001 ); *ptr = 0; } } // namespace Dbg #endif ================================================ FILE: Code/Core/Debug/Project.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Confidential Information ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (Dbg_) ** ** ** ** File name: core/debug/project.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_DEBUG_PROJECT_H #define __CORE_DEBUG_PROJECT_H #ifdef __NOPT_DEBUG__ /***************************************************************************** ** Includes ** *****************************************************************************/ #include "mem_stat.h" /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Type Defines ** *****************************************************************************/ namespace Dbg { class Project { public : Project ( const char& name, const char& description ); void SetChildren ( Dbg::Module* children ); Dbg::Module* GetChildren ( void ) const; private : const char& name; const char& description; Project* next; // pointer to next registered project Dbg::Module* children; // pointer to children modules Dbg_MEMORY_STATS stats; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ extern Dbg::Project* RegisteredProjects; } // namespace Dbg /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Macros ** *****************************************************************************/ #define Dbg_DefineProject(proj,des) \ \ Dbg::Project& Dbg_project_##proj ( void ) \ { \ static Dbg::Project project ( *#proj, *des ); \ \ return project; \ } \ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ namespace Dbg { inline Project::Project ( const char& name, const char& description ) : name ( name ), description ( description ) { next = RegisteredProjects; RegisteredProjects = this; children = NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Project::SetChildren ( Dbg::Module* children_in ) { children = children_in; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Dbg::Module* Project::GetChildren ( void ) const { return children; } } // namespace Dbg /***************************************************************************** ** Stubs ** *****************************************************************************/ #else #define Dbg_DefineProject(proj,des) #endif //__NOPT_DEBUG__ /******************************************************************/ /* */ /* */ /******************************************************************/ #endif // __CORE_DEBUG_PROJECT_H ================================================ FILE: Code/Core/Debug/Signatrs.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (Dbg_) ** ** ** ** File name: core/debug/signatrs.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_DEBUG_SIGNATRS_H #define __CORE_DEBUG_SIGNATRS_H #ifdef __NOPT_DEBUG__ /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Dbg { /***************************************************************************** ** Type Defines ** *****************************************************************************/ class Module; class Signature { public : Signature( char* name, const Module& module ); Signature( const char* cl, char* name, const Module& module ); const char& GetName( void ) const ; private : char* m_name; // function's name const Module& m_module; // function's module }; /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Macros ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ inline Signature::Signature( char* name, const Module& module ) : m_name ( name ), m_module ( module ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ inline const char& Signature::GetName( void ) const { return *m_name; } } // namespace Dbg /***************************************************************************** ** Private Declarations ** *****************************************************************************/ #ifdef __PLAT_NGC__ extern Dbg::Module& Dbg_module; extern Dbg::Signature Dbg_signature; #endif #ifndef __NOPT_STRICT_SIGNATURES__ extern Dbg::Module& Dbg_module; extern Dbg::Signature Dbg_signature; #endif #endif // __NOPT_DEBUG__ /******************************************************************/ /* */ /* */ /******************************************************************/ #endif // __CORE_DEBUG_SIGNATRS_H ================================================ FILE: Code/Core/Debug/Wn32/P_debug.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (DBG) ** ** ** ** File name: p_debug.cpp ** ** ** ** Created by: 05/27/99 - mjb ** ** ** ** Description: Platform specific debug code ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Dbg { /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ #ifdef __NOPT_DEBUG__ void set_up ( void ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void close_down ( void ) { } #endif // __NOPT_DEBUG__ /******************************************************************/ /* */ /* */ /******************************************************************/ void default_print ( char *text ) { #ifdef __PLAT_WN32__ OutputDebugString ( text ); #else printf ( text ); #endif // __CC_VISUALC__ } /******************************************************************/ /* */ /* */ /******************************************************************/ void default_trap ( char* message ) { #ifdef __CC_VISUALC__ #ifdef __ALLOW_CONTINUE__ switch ( MessageBox ( GetActiveWindow(), message, "Assertion Failure - Trigger Debugger ?", MB_DEFBUTTON1 | MB_YESNO | MB_ICONEXCLAMATION )) { case IDYES: __asm int 3; // trigger the debugger break; } #else // __ALLOW_CONTINUE__ MessageBox ( GetActiveWindow(), message, "Assertion Failure", MB_DEFBUTTON1 | MB_ICONEXCLAMATION ); __asm int 3; #endif // __ALLOW_CONTINUE__ #else // __CC_VISUALC__ exit(-10); #endif // __CC_VISUALC__ } /***************************************************************************** ** Public Functions ** *****************************************************************************/ } ================================================ FILE: Code/Core/Debug/XBox/p_debug.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (DBG) ** ** ** ** File name: p_debug.cpp ** ** ** ** Created by: 09/25/00 - dc ** ** ** ** Description: Platform specific debug code ** ** ** *****************************************************************************/ #include #include #include #include #ifdef __NOPT_DEBUG__ /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Externals ** *****************************************************************************/ extern char _dbg_start[]; extern char _dbg_end[]; /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Dbg { Dbg_Module // module this code belongs to /***************************************************************************** ** Defines ** *****************************************************************************/ #ifdef __NOPT_FULL_DEBUG__ enum { vMAX_CLASS_NODES = 28000, vMAX_INSTANCE_NODES = 100000 }; static const int vFREE_CLASS_BLOCK_ID = 0x02030405; static const int vFREE_INST_BLOCK_ID = 0x27354351; /***************************************************************************** ** Private Types ** *****************************************************************************/ struct FreeNode { FreeNode* next; int id; } ; union ClassNodeBlock { char pad[sizeof(Spt::ClassNode)]; FreeNode free_node; }; union InstanceNodeBlock { char pad[sizeof(Spt::ClassNode::InstanceNode)]; FreeNode free_node; }; /***************************************************************************** ** Private Data ** *****************************************************************************/ static ClassNodeBlock* spClassNodeFreeList = NULL; static InstanceNodeBlock* spInstanceNodeFreeList = NULL; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ void set_up( void ) { Dbg_Function; spClassNodeFreeList = (ClassNodeBlock*)_dbg_start; ClassNodeBlock* p_class_node = spClassNodeFreeList; for( int i = 0; i < ( vMAX_CLASS_NODES - 1 ); i++ ) { p_class_node->free_node.id = vFREE_CLASS_BLOCK_ID; p_class_node->free_node.next = (FreeNode*)(++p_class_node); } p_class_node->free_node.id = vFREE_CLASS_BLOCK_ID; (p_class_node++)->free_node.next = (FreeNode*)spClassNodeFreeList; spInstanceNodeFreeList = (InstanceNodeBlock*)(++p_class_node); InstanceNodeBlock* p_inst_node = spInstanceNodeFreeList; for( int i = 0; i < ( vMAX_INSTANCE_NODES - 1 ) ; i++ ) { p_inst_node->free_node.id = vFREE_INST_BLOCK_ID; p_inst_node->free_node.next = (FreeNode*)(++p_inst_node); } p_inst_node->free_node.id = vFREE_INST_BLOCK_ID; (p_inst_node++)->free_node.next = (FreeNode*)spInstanceNodeFreeList; Dbg_MsgAssert( (int)p_inst_node <= (int)_dbg_end, "Dbg Mem Block not big enough (%d bytes too small)", (int)p_inst_node - (int)_dbg_end ); if ( (int)p_inst_node < ((int)_dbg_end)) { Dbg_Warning ( "%dbytes unused in Dbg mem block",((int)_dbg_end - (int)p_inst_node)); } Dbg_Notify ( "Dbg Mem Block Allocated successfully" ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void close_down( void ) { Dbg_Function; int count = 0; ClassNodeBlock* p_class_node = spClassNodeFreeList; InstanceNodeBlock* p_inst_node = spInstanceNodeFreeList; do { Dbg_MsgAssert ( p_class_node->free_node.id == vFREE_CLASS_BLOCK_ID, "Block not free" ); p_class_node = (ClassNodeBlock*)p_class_node->free_node.next; count++; } while ( p_class_node != spClassNodeFreeList ); Dbg_MsgAssert ( count == vMAX_CLASS_NODES, "%d Class Nodes still registered", vMAX_CLASS_NODES - count ); count = 0; do { Dbg_MsgAssert ( p_inst_node->free_node.id == vFREE_INST_BLOCK_ID, "Block not free" ); p_inst_node = (InstanceNodeBlock*)p_inst_node->free_node.next; count++; } while ( p_inst_node != spInstanceNodeFreeList ); Dbg_MsgAssert ( count == vMAX_INSTANCE_NODES, "%d Instance Nodes still registered", vMAX_INSTANCE_NODES - count ); Dbg_Notify ( "Dbg Mem Block Released successfully" ); } /***************************************************************************** ** Public Functions ** *****************************************************************************/ void* NewClassNode( size_t size ) { Dbg_Function; void* p_ret = (void*)spClassNodeFreeList; Dbg_MsgAssert ( (int)spClassNodeFreeList != (int)(spClassNodeFreeList->free_node.next), "ClassNode pool full" ); Dbg_MsgAssert ( spClassNodeFreeList->free_node.id == vFREE_CLASS_BLOCK_ID, "Not a Free Class Node" ); spClassNodeFreeList = (ClassNodeBlock*)spClassNodeFreeList->free_node.next; // printf("NewClassNode %p next %p\n",p_ret, spClassNodeFreeList); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void DeleteClassNode( void* pMem ) { Dbg_Function; Dbg_MsgAssert ( (((int)pMem >= (int)_dbg_start ) && (((int)pMem < (int)_dbg_end ))), "Memory not in Debug block (%p)",pMem ); ClassNodeBlock* p_freeblock = (ClassNodeBlock*)pMem; p_freeblock->free_node.next = (FreeNode*)spClassNodeFreeList; spClassNodeFreeList = (ClassNodeBlock*)p_freeblock; spClassNodeFreeList->free_node.id = vFREE_CLASS_BLOCK_ID; // printf("DeleteClassNode %p next %p\n",spClassNodeFreeList, p_freeblock->free_node.next); } /******************************************************************/ /* */ /* */ /******************************************************************/ void* NewInstanceNode( size_t size ) { Dbg_Function; void* p_ret = (void*)spInstanceNodeFreeList; Dbg_MsgAssert ( (int)spInstanceNodeFreeList != (int)(spInstanceNodeFreeList->free_node.next), "InstanceNode pool full" ); Dbg_MsgAssert ( spInstanceNodeFreeList->free_node.id == vFREE_INST_BLOCK_ID, "Not a Free Instance Node" ); spInstanceNodeFreeList = (InstanceNodeBlock*)spInstanceNodeFreeList->free_node.next; // printf("NewInstanceNode %p next %p\n",p_ret, spInstanceNodeFreeList); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void DeleteInstanceNode( void* pMem ) { Dbg_Function; Dbg_MsgAssert ( (((int)pMem >= (int)_dbg_start ) && (((int)pMem < (int)_dbg_end ))), "Memory not in Debug block (%p)",pMem ); InstanceNodeBlock* p_freeblock = (InstanceNodeBlock*)pMem; p_freeblock->free_node.next = (FreeNode*)spInstanceNodeFreeList; spInstanceNodeFreeList = (InstanceNodeBlock*)p_freeblock; spInstanceNodeFreeList->free_node.id = vFREE_INST_BLOCK_ID; // printf("DeleteInstanceNode %p next %p\n",spInstanceNodeFreeList, p_freeblock->free_node.next); } /******************************************************************/ /* */ /* */ /******************************************************************/ #else // __NOPT_FULL_DEBUG__ void set_up( void ) { Dbg_Function; } /******************************************************************/ /* */ /* */ /******************************************************************/ void close_down( void ) { Dbg_Function; } #endif // __NOPT_FULL_DEBUG__ /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Dbg #endif // __NOPT_DEBUG__ #ifdef __NOPT_ASSERT__ namespace Dbg { /******************************************************************/ /* */ /* */ /******************************************************************/ void default_print( char *text ) { OutputDebugString( text ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void default_trap( char* message ) { OutputDebugString( message ); uint* ptr = reinterpret_cast< uint* >( 0x00000001 ); *ptr = NULL; } } // namespace Dbg #endif ================================================ FILE: Code/Core/Debug/log.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (DBG) ** ** ** ** File name: log.cpp ** ** ** ** Created by: 09/25/02 - ksh ** ** ** ** Description: Logging code ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include #include #ifndef __PLAT_WN32__ #include #endif // __PLAT_WN32__ #ifdef __PLAT_NGC__ #include #define _output OSReport #else #define _output printf #endif #ifdef __NOPT_ASSERT__ #ifdef __PLAT_NGPS__ /***************************************************************************** ** DBG Information ** *****************************************************************************/ // 128 bytes each (sizeof(SLogEntry)) and they come off the debug heap. #define MAX_LOG_ENTRIES 20000 extern char _log_info_start[]; extern char _log_info_end[]; namespace Log { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ // Chosen just to make sizeof(SLogEntry) a nice round 128 #define MAX_MESSAGE_LENGTH 107 struct SLogEntry { Tmr::CPUCycles mCPUTime; const char *mpSourceFileName; const char *mpFunctionName; int mLineNumber; char mpMessage[MAX_MESSAGE_LENGTH+1]; }; //char p_foo[sizeof(SLogEntry)/0]; struct SLogBufferInfo { // Pointer to the start of the big log buffer, which will be in the debug heap somewhere. SLogEntry *mpBuffer; // The total number of entries in the buffer. int mTotalEntries; // The number of entries written to so far. This starts at 0 when the game starts, // and increases till it hits mTotalEntries, then stays at that value. int mNumEntries; // Points to just after the last entry that got added to the buffer. // So it may not point to a valid SLogEntry, for example when the buffer is filling // up, or when pTop points to the end of pBuffer. SLogEntry *mpTop; }; /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ static bool sInitialised=false; static SLogBufferInfo *spLogInfo=NULL; static SLogEntry *spNotALeak=NULL; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ void Init() { if (Config::GotExtraMemory()) { Dbg_MsgAssert(!sInitialised,("Tried to call Log::Init twice")); Dbg_MsgAssert((uint32)(_log_info_end-_log_info_start) >= sizeof(SLogBufferInfo),("log_info section too small, got=%d, required=%d",(uint32)(_log_info_end-_log_info_start),sizeof(SLogBufferInfo))); spLogInfo=(SLogBufferInfo*)_log_info_start; Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap()); spLogInfo->mNumEntries=0; spLogInfo->mTotalEntries=MAX_LOG_ENTRIES; spLogInfo->mpBuffer=(SLogEntry*)Mem::Malloc(MAX_LOG_ENTRIES*sizeof(SLogEntry)); spNotALeak=spLogInfo->mpBuffer; spLogInfo->mpTop=spLogInfo->mpBuffer; uint32 *p_long=(uint32*)spLogInfo->mpBuffer; Dbg_MsgAssert((sizeof(SLogEntry)&3)==0,("sizeof(SLogEntry) not a multiple of 4")); for (uint32 i=0; imNumEntries < MAX_LOG_ENTRIES) { p_new=spLogInfo->mpTop++; ++spLogInfo->mNumEntries; } else { if (spLogInfo->mpTop >= spLogInfo->mpBuffer+MAX_LOG_ENTRIES) { spLogInfo->mpTop=spLogInfo->mpBuffer; } p_new=spLogInfo->mpTop++; } p_new->mCPUTime=Tmr::GetTimeInCPUCycles(); p_new->mLineNumber=lineNumber; p_new->mpFunctionName=p_functionName; p_new->mpSourceFileName=p_fileName; if (p_message) { Dbg_MsgAssert(strlen(p_message)<=MAX_MESSAGE_LENGTH,("Log message '%s' too long",p_message)); strcpy(p_new->mpMessage,p_message); } else { p_new->mpMessage[0]=0; } } } } // namespace Log #else namespace Log { void Init() { } void AddEntry(char *p_fileName, int lineNumber, char *p_functionName, char *p_message) { } } // namespace Log #endif // #ifdef __PLAT_NGPS__ #endif //__NOPT_ASSERT__ ================================================ FILE: Code/Core/Debug/ngc/P_debug.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (DBG) ** ** ** ** File name: p_debug.cpp ** ** ** ** Created by: 05/27/99 - mjb ** ** ** ** Description: Platform specific debug code ** ** ** *****************************************************************************/ #include #include #include #include #include #ifdef __NOPT_DEBUG__ /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Externals ** *****************************************************************************/ extern char _dbg_start[]; extern char _dbg_end[]; /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Dbg { /***************************************************************************** ** Defines ** *****************************************************************************/ #ifdef __NOPT_FULL_DEBUG__ enum { vMAX_CLASS_NODES = 28000, vMAX_INSTANCE_NODES = 100000 }; static const int vFREE_CLASS_BLOCK_ID = 0x02030405; static const int vFREE_INST_BLOCK_ID = 0x27354351; /***************************************************************************** ** Private Types ** *****************************************************************************/ struct FreeNode { FreeNode* next; int id; } ; union ClassNodeBlock { char pad[sizeof(Spt::ClassNode)]; FreeNode free_node; }; union InstanceNodeBlock { char pad[sizeof(Spt::ClassNode::InstanceNode)]; FreeNode free_node; }; /***************************************************************************** ** Private Data ** *****************************************************************************/ static ClassNodeBlock* spClassNodeFreeList = NULL; static InstanceNodeBlock* spInstanceNodeFreeList = NULL; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ void set_up( void ) { spClassNodeFreeList = (ClassNodeBlock*)_dbg_start; ClassNodeBlock* p_class_node = spClassNodeFreeList; for( int i = 0; i < ( vMAX_CLASS_NODES - 1 ); i++ ) { p_class_node->free_node.id = vFREE_CLASS_BLOCK_ID; p_class_node->free_node.next = (FreeNode*)(++p_class_node); } p_class_node->free_node.id = vFREE_CLASS_BLOCK_ID; (p_class_node++)->free_node.next = (FreeNode*)spClassNodeFreeList; spInstanceNodeFreeList = (InstanceNodeBlock*)(++p_class_node); InstanceNodeBlock* p_inst_node = spInstanceNodeFreeList; for( int i = 0; i < ( vMAX_INSTANCE_NODES - 1 ) ; i++ ) { p_inst_node->free_node.id = vFREE_INST_BLOCK_ID; p_inst_node->free_node.next = (FreeNode*)(++p_inst_node); } p_inst_node->free_node.id = vFREE_INST_BLOCK_ID; (p_inst_node++)->free_node.next = (FreeNode*)spInstanceNodeFreeList; Dbg_MsgAssert( (int)p_inst_node <= (int)_dbg_end, "Dbg Mem Block not big enough (%d bytes too small)", (int)p_inst_node - (int)_dbg_end ); if ( (int)p_inst_node < ((int)_dbg_end)) { Dbg_Warning ( "%dbytes unused in Dbg mem block",((int)_dbg_end - (int)p_inst_node)); } Dbg_Notify ( "Dbg Mem Block Allocated successfully" ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void close_down( void ) { int count = 0; ClassNodeBlock* p_class_node = spClassNodeFreeList; InstanceNodeBlock* p_inst_node = spInstanceNodeFreeList; do { Dbg_MsgAssert ( p_class_node->free_node.id == vFREE_CLASS_BLOCK_ID, "Block not free" ); p_class_node = (ClassNodeBlock*)p_class_node->free_node.next; count++; } while ( p_class_node != spClassNodeFreeList ); Dbg_MsgAssert ( count == vMAX_CLASS_NODES, "%d Class Nodes still registered", vMAX_CLASS_NODES - count ); count = 0; do { Dbg_MsgAssert ( p_inst_node->free_node.id == vFREE_INST_BLOCK_ID, "Block not free" ); p_inst_node = (InstanceNodeBlock*)p_inst_node->free_node.next; count++; } while ( p_inst_node != spInstanceNodeFreeList ); Dbg_MsgAssert ( count == vMAX_INSTANCE_NODES, "%d Instance Nodes still registered", vMAX_INSTANCE_NODES - count ); Dbg_Notify ( "Dbg Mem Block Released successfully" ); } /***************************************************************************** ** Public Functions ** *****************************************************************************/ void* NewClassNode( size_t size ) { void* p_ret = (void*)spClassNodeFreeList; Dbg_MsgAssert ( (int)spClassNodeFreeList != (int)(spClassNodeFreeList->free_node.next), "ClassNode pool full" ); Dbg_MsgAssert ( spClassNodeFreeList->free_node.id == vFREE_CLASS_BLOCK_ID, "Not a Free Class Node" ); spClassNodeFreeList = (ClassNodeBlock*)spClassNodeFreeList->free_node.next; // printf("NewClassNode %p next %p\n",p_ret, spClassNodeFreeList); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void DeleteClassNode( void* pMem ) { Dbg_MsgAssert ( (((int)pMem >= (int)_dbg_start ) && (((int)pMem < (int)_dbg_end ))), "Memory not in Debug block (%p)",pMem ); ClassNodeBlock* p_freeblock = (ClassNodeBlock*)pMem; p_freeblock->free_node.next = (FreeNode*)spClassNodeFreeList; spClassNodeFreeList = (ClassNodeBlock*)p_freeblock; spClassNodeFreeList->free_node.id = vFREE_CLASS_BLOCK_ID; // printf("DeleteClassNode %p next %p\n",spClassNodeFreeList, p_freeblock->free_node.next); } /******************************************************************/ /* */ /* */ /******************************************************************/ void* NewInstanceNode( size_t size ) { void* p_ret = (void*)spInstanceNodeFreeList; Dbg_MsgAssert ( (int)spInstanceNodeFreeList != (int)(spInstanceNodeFreeList->free_node.next), "InstanceNode pool full" ); Dbg_MsgAssert ( spInstanceNodeFreeList->free_node.id == vFREE_INST_BLOCK_ID, "Not a Free Instance Node" ); spInstanceNodeFreeList = (InstanceNodeBlock*)spInstanceNodeFreeList->free_node.next; // printf("NewInstanceNode %p next %p\n",p_ret, spInstanceNodeFreeList); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void DeleteInstanceNode( void* pMem ) { Dbg_MsgAssert ( (((int)pMem >= (int)_dbg_start ) && (((int)pMem < (int)_dbg_end ))), "Memory not in Debug block (%p)",pMem ); InstanceNodeBlock* p_freeblock = (InstanceNodeBlock*)pMem; p_freeblock->free_node.next = (FreeNode*)spInstanceNodeFreeList; spInstanceNodeFreeList = (InstanceNodeBlock*)p_freeblock; spInstanceNodeFreeList->free_node.id = vFREE_INST_BLOCK_ID; // printf("DeleteInstanceNode %p next %p\n",spInstanceNodeFreeList, p_freeblock->free_node.next); } /******************************************************************/ /* */ /* */ /******************************************************************/ #else // __NOPT_FULL_DEBUG__ void set_up( void ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ void close_down( void ) { } #endif // __NOPT_FULL_DEBUG__ /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Dbg #endif // __NOPT_DEBUG__ #ifdef __NOPT_ASSERT__ namespace Dbg { /******************************************************************/ /* */ /* */ /******************************************************************/ void default_print( char *text ) { std::printf( text ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void default_trap( char* message ) { // uint* ptr = reinterpret_cast< uint* >( 0x00000001 ); // // *ptr = NULL; snPause(); } } // namespace Dbg #endif ================================================ FILE: Code/Core/Debug.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Debug (DBG) ** ** ** ** Created: 05/27/99 mjb ** ** ** ** File name: core/debug.h ** ** ** *****************************************************************************/ #ifndef __CORE_DEBUG_H #define __CORE_DEBUG_H #ifdef __NOPT_DEBUG__ // for now - always turn on messages and assertion #define __NOPT_MESSAGES__ #define __NOPT_ASSERT__ #else // Mick - added assertions always on, regardless of debug mode. #define __NOPT_ASSERT__ // Gary - ... unless you're working on the tools, // in which case the asserts prevent the code from compiling... #ifdef __PLAT_WN32__ #ifndef _CONSOLE // Ken: Asserts now compile for console apps. (see levelassetlister project) #undef __NOPT_ASSERT__ #endif #endif #endif // __NOPT_DEBUG__ #ifdef __NOPT_CDROM__OLD #undef __NOPT_ASSERT__ #endif // Temporary switch off assertions flag #ifdef __NOPT_NOASSERTIONS__ #undef __NOPT_ASSERT__ #endif // no assertions on final build ("final=") #ifdef __NOPT_FINAL__ #undef __NOPT_ASSERT__ #endif #ifdef __NOPT_ASSERT__ #define __DEBUG_CODE__ #endif /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include "debug/messages.h" #include "debug/checks.h" /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Dbg { #ifdef __NOPT_DEBUG__ /***************************************************************************** ** Type Defines ** *****************************************************************************/ /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ void SetUp( void ); void CloseDown( void ); #ifdef __NOPT_FULL_DEBUG__ void* NewClassNode( size_t size ); void DeleteClassNode( void* pMem ); void* NewInstanceNode( size_t size ); void DeleteInstanceNode( void* pMem ); #endif // __NOPT_FULL_DEBUG__ /***************************************************************************** ** Macros ** *****************************************************************************/ #define Dbg_Code(X) X /***************************************************************************** ** Stubs ** *****************************************************************************/ #else // __NOPT_DEBUG__ #ifndef __NOPT_ASSERT__ inline void SetUp( void ) {}; inline void CloseDown( void ) {}; #else void SetUp( void ); void CloseDown( void ); #endif // __NOPT_ASSERT #define Dbg_Code(X) #endif // __NOPT_DEBUG__ } // namespace Dbg #endif // __CORE_DEBUG_H ================================================ FILE: Code/Core/Defines.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Standard Header ** ** ** ** File name: core/defines.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** ** All code depends on the definitions in this files ** ** It should be included in every file ** ** ** *****************************************************************************/ #ifndef __CORE_DEFINES_H #define __CORE_DEFINES_H /***************************************************************************** ** Includes ** *****************************************************************************/ #ifdef __PLAT_WN32__ //#include //#include #ifdef __USE_OLD_STREAMS__ #include #else #include using namespace std; #endif #include #include #pragma warning( disable : 4800 ) #pragma warning( disable : 4355 ) // 'this' : used in base member initializer list #pragma warning( disable : 4291 ) // no matching operator delete found #else #ifdef __PLAT_NGPS__ #include #include //#include #include #include #else #ifdef __PLAT_NGC__ #include #include #include #include #else #ifdef __PLAT_XBOX__ #include //#include #include #include #pragma warning( disable : 4800 ) #pragma warning( disable : 4291 ) // no matching operator delete found #pragma warning( disable : 4355 ) // 'this' : used in base member initializer list #pragma warning( disable : 4995 ) // name was marked as #pragma deprecated #endif #endif #endif #endif #ifndef __PLAT_WN32__ //#ifdef __NOPT_ASSERT__ #ifdef __PLAT_NGC__ #ifdef __NOPT_FINAL__ #define printf(A...) #else int OurPrintf(const char *fmt, ...); #define printf OurPrintf #endif #else int OurPrintf(const char *fmt, ...); #define printf OurPrintf #endif //#else // inline void NullPrintf(const char *fmt, ...){} // #define printf NullPrintf //#endif #endif /***************************************************************************** ** Defines ** *****************************************************************************/ #define vINT8_MAX 0x7F #define vINT8_MIN 0x81 #define vINT16_MAX 0x7FFF #define vINT16_MIN 0x8001 #define vINT32_MAX 0x7FFFFFFF #define vINT32_MIN 0x80000001 #define vINT64_MAX 0x7FFFFFFFFFFFFFFF #define vINT64_MIN 0x8000000000000001 #define vUINT8_MAX 0xFF #define vUINT16_MAX 0xFFFF #define vUINT32_MAX 0xFFFFFFFF #define vUINT64_MAX 0xFFFFFFFFFFFFFFFF #ifndef TRUE #define FALSE 0 #define TRUE (!FALSE) #endif #ifndef NULL #define NULL 0 #endif /***************************************************************************** ** Type Defines ** *****************************************************************************/ typedef char int8; typedef short int16; typedef unsigned int uint; typedef unsigned char uint8; typedef unsigned short uint16; typedef signed int sint; typedef signed char sint8; typedef signed short sint16; #define vINT_MAX vINT32_MAX #define vINT_MIN vINT32_MIN #define vUINT_MAX vUINT32_MAX #ifdef __PLAT_WN32__ typedef long int32; typedef unsigned long uint32; typedef signed long sint32; typedef __int64 int64; typedef unsigned __int64 uint64; typedef signed __int64 sint64; #endif #ifdef __PLAT_NGPS__ typedef int int32; typedef unsigned int uint32; typedef signed int sint32; typedef long int64; typedef unsigned long uint64; typedef signed long sint64; typedef long128 int128; typedef u_long128 uint128; typedef long128 sint128; #endif #ifdef __PLAT_XBOX__ typedef long int32; typedef unsigned long uint32; typedef signed long sint32; typedef __int64 int64; typedef unsigned __int64 uint64; typedef signed __int64 sint64; #endif #ifdef __PLAT_NGC__ typedef int int32; typedef unsigned int uint32; typedef signed int sint32; typedef long long int64; typedef unsigned long long uint64; typedef signed long long sint64; // Paul: No GameCube 128-bit types. //typedef long128 int128; //typedef u_long128 uint128; //typedef long128 sint128; typedef long long int128; typedef unsigned long long uint128; typedef signed long long sint128; #endif #if defined(__PLAT_NGPS__) || defined(__PLAT_XBOX__) || defined(__PLAT_NGC__) class ostream { public: ostream& operator<< ( char* str ) { printf ( str ); return *this; } ostream& operator<< ( const char* str ) { printf ( str ); return *this; } ostream& operator<< ( sint i ) { printf ( "%d", i ); return *this; } ostream& operator<< ( uint i ) { printf ( "%u", i ); return *this; } ostream& operator<< ( float f ) { printf ( "%f", f ); return *this; } ostream& operator<< ( void* p ) { printf ( "%p", p ); return *this; } ostream& operator<< ( const void* p ) { printf ( "%p", p ); return *this; } }; #endif #define vINT_BITS 32 #define vPTR_BITS 32 /******************************************************************/ /* */ /* */ /******************************************************************/ // Alignment macros #ifdef __PLAT_NGPS__ #define nAlign(bits) __attribute__((aligned((bits>>3)))) #else #define nAlign(bits) #endif #define nPad64(X) uint64 _pad##X; #define nPad32(X) uint32 _pad##X; #define nPad16(X) uint16 _pad##X; #define nPad8(X) uint8 _pad##X; /******************************************************************/ /* */ /* */ /******************************************************************/ // version stamping #define __nTIME__ __TIME__ #define __nDATE__ __DATE__ /******************************************************************/ /* */ /* */ /******************************************************************/ #define nBit(b) ( 1 << (b) ) typedef sint nID; typedef sint8 nID8; typedef sint16 nID16; typedef sint32 nID32; typedef sint64 nID64; #define nMakeID(a,b,c,d) ( (nID) ( ( (nID) (a) ) << 24 | ( (nID) (b) ) << 16 | \ ( (nID) (c) ) << 8 | ( (nID) (d) ) ) ) // nMakeStructID() differs from nMakeID in that struct IDs are always // readable from a memory dump, where as IDs would be reversed on little // endian machines #if __CPU_BIG_ENDIAN__ #define nMakeStructID(a,b,c,d) ( (nID) ( ((nID)(a))<<24 | ((nID)(b))<<16 | \ ((nID)(c))<<8 | ((nID)(d)) )) #else #define nMakeStructID(a,b,c,d) ( (ID) ( ((nID)(d))<<24 | ((nID)(c))<<16 | \ ((nID)(b))<<8 | ((nID)(a)) )) #endif /***************************************************************************** ** Macros ** *****************************************************************************/ #define nReverse32(L) ( (( (L)>>24 ) & 0xff) | (((L)>>8) &0xff00) | (((L)<<8)&0xff0000) | (((L)<<24)&0xff000000)) #define nReverse16(L) ( (( (L)>>8 ) & 0xff) | (((L)<<8)&0xff00) ) #if __CPU_BIG_ENDIAN__ #define nBgEn32(L) (L) #define nBgEn16(L) (L) #define nLtEn32(L) nReverse32(L) #define nLtEn16(L) nReverse16(L) #else #define nBgEn32(L) nReverse32(L) #define nBgEn16(L) nReverse16(L) #define nLtEn32(L) (L) #define nLtEn16(L) (L) #endif #ifdef __PLAT_XBOX__ #define __PRETTY_FUNCTION__ "?" #define isnanf _isnan #define isinff _isnan #endif /******************************************************************/ /* */ /* */ /******************************************************************/ #define __CPU_WORD_BALIGN__ 4 // Memory word byte alignment #define PTR_ALIGNMASK ( vUINT_MAX << __CPU_WORD_BALIGN__) // The alignment macros align elements for fastest access #define nAligned(P) ( !( (uint) (P) & (~PTR_ALIGNMASK) ) ) #define nAlignDown(P) (void*)( (uint) (P) & PTR_ALIGNMASK ) #define nAlignUp(P) (void*)( ( (uint) (P) + ( 1 << __CPU_WORD_BALIGN__ ) - 1 ) & PTR_ALIGNMASK ) #define nAlignedBy(P,A) ( !( (uint) (P) & ( ~(vUINT_MAX << (A) ) ) ) ) #define nAlignDownBy(P,A) (void*)( (uint) (P) & (vUINT_MAX << (A) ) ) #define nAlignUpBy(P,A) (void*)( ( (uint) (P) + ( 1 << (A) ) - 1 ) & ( vUINT_MAX <<( A ) ) ) #define nStorage(X) nAlignUp ( (X) + 1 ) /****************************************************************************/ #define nAddPointer(P,X) (void*) ( (uint) (P) + (uint) (X) ) #define nSubPointer(P,X) (void*) ( (uint) (P) - (uint) (X) ) /****************************************************************************/ // Converts a string into a checksum. This macro can be used for readability. // Later, for speed, some application can scan all .cpp and .h files, and // replace all instances of CRCX(...) with corresponding CRCD(...) instances // // Example: CRCX("object_id") #define CRCX(_s) Script::GenerateCRC(_s) // This macro exists simply for readability. Whenever you see a CRCD(...) instance, // you will know what string the checksum maps to. CRCD(...) instances in the code // can be generated from CRCX(...), and can be reverse mapped if desired. // // Example: CRCD(0xdcd2a9d4, "object_id") #include /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #ifdef __PLAT_NGPS__ #include #include "libsn.h" #elif defined( __PLAT_NGC__ ) #include //#include "libsn.h" #elif defined( __PLAT_XBOX__ ) #include #endif // Mick: This check slows the game down quite a bit #if 1 && defined( __NOPT_ASSERT__ ) extern uint32 check_checksum(uint32 _i, const char *_s, const char *f, int line); #define CRCD(_i, _s) check_checksum(_i, _s, __FILE__, __LINE__) #else #define CRCD(_i, _s) _i #endif // CRC-C, for use only in switch statements, where you want to use the same syntax as CRCD #define CRCC(_i, _s) _i //#ifdef __PLAT_NGC__ //class TCPPInit //{ //public: // static bool IsHeapInitialized ; //} ; // //// these definitions override the new and delete operators. // #ifdef __PLAT_NGC__ static inline void* operator new ( size_t blocksize ) ; static inline void* operator new[] ( size_t blocksize ) ; static inline void operator delete ( void* block ) ; static inline void operator delete[] ( void* block ) ; #endif // __PLAT_NGC__ // //#else #ifdef __PLAT_WN32__ inline void* operator new( size_t size, bool assert_on_fail ) { return new char[size]; } #else #ifndef __PLAT_NGC__ /******************************************************************/ /* Global new/delete operators */ /* */ /******************************************************************/ inline void* operator new( size_t size ) { return Mem::Manager::sHandle().New( size, true ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void* operator new[] ( size_t size ) { return Mem::Manager::sHandle().New( size, true ); } #endif // __PLAT_NGC__ /******************************************************************/ /* */ /* */ /******************************************************************/ inline void* operator new( size_t size, bool assert_on_fail ) { return Mem::Manager::sHandle().New( size, assert_on_fail ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void* operator new[] ( size_t size, bool assert_on_fail ) { return Mem::Manager::sHandle().New( size, assert_on_fail ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void* operator new( size_t size, Mem::Allocator* pAlloc, bool assert_on_fail = true ) { return Mem::Manager::sHandle().New( size, assert_on_fail, pAlloc ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void* operator new[]( size_t size, Mem::Allocator* pAlloc, bool assert_on_fail = true ) { return Mem::Manager::sHandle().New( size, assert_on_fail, pAlloc ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void* operator new( size_t size, void* pLocation ) { return pLocation; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void* operator new[]( size_t size, void* pLocation ) { return pLocation; } #ifndef __PLAT_NGC__ /******************************************************************/ /* */ /* */ /******************************************************************/ inline void operator delete( void* pAddr ) { Mem::Manager::sHandle().Delete( pAddr ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void operator delete[]( void* pAddr ) { Mem::Manager::sHandle().Delete( pAddr ); } #endif // __PLAT_NGC__ //#ifdef __PLAT_NGC__ /******************************************************************/ /* only used when exception is thrown in constructor */ /* */ /****************************************************************** inline void operator delete( void* pAddr, Mem::Allocator* pAlloc ) { Mem::Manager * mem_man = Mem::Manager::Instance(); Mem::Manager::sHandle().Delete( pAddr ); } /******************************************************************/ /* */ /* */ /****************************************************************** inline void operator delete[]( void* pAddr, Mem::Allocator* pAlloc ) { Mem::Manager * mem_man = Mem::Manager::Instance(); Mem::Manager::sHandle().Delete( pAddr ); } /******************************************************************/ /* */ /* */ /****************************************************************** inline void operator delete( void*, void* pLocation ) { return; } /******************************************************************/ /* */ /* */ /******************************************************************/ //#endif // __PLAT_NGC__ #endif // __PLAT_WN32__ #endif // __CORE_DEFINES_H ================================================ FILE: Code/Core/DynamicTable.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core library ** ** ** ** Module: List ** ** ** ** File name: ** ** ** ** Created by: ** ** ** ** Description: ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include // /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Lst { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ /****************************************************************************** * * Function: * * Description: Default constructor * * Parameters: * *****************************************************************************/ } // namespace Lst ================================================ FILE: Code/Core/DynamicTable.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core library ** ** ** ** Module: DynamicTable ** ** ** ** File name: Core\DynamicTable.h ** ** ** ** Created by: 12/14/2000 - rjm ** ** ** ** Description: A handy DynamicTable class ** ** ** *****************************************************************************/ #ifndef __CORE_LIST_DYNAMICTABLE_H #define __CORE_LIST_DYNAMICTABLE_H /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Lst { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ nTemplateBaseClass(_V, DynamicTable) { Dbg_TemplateBaseClass(_V, DynamicTable); public: DynamicTable(int size, int grow_increment = 8); ~DynamicTable(); // it goes on the end of the table void Add(_V *pItem); int GetSize(); void Reset(); _V &Last(); void Replace(int index, _V *pNewItem); void Remove(int index); /* bool IsEmpty(int index); */ _V &operator[](int index); private: _V ** mpp_table; int m_size; int m_entriesFilled; int m_growIncrement; }; template class DynamicTableDestroyer { public: DynamicTableDestroyer(DynamicTable<_V> *pTable); void DeleteTableContents(); private: DynamicTable<_V> * mp_table; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ template DynamicTable<_V>::DynamicTable(int size, int grow_increment) { m_size = size; m_growIncrement = grow_increment; mpp_table = (_V**) Mem::Malloc(sizeof(_V *) * m_size); //mpp_table = new _V*[m_size]; for (int i = 0; i < m_size; i++) mpp_table[i] = NULL; m_entriesFilled = 0; } template DynamicTable<_V>::~DynamicTable() { Mem::Free(mpp_table); //delete [] mpp_table; } template void DynamicTable<_V>::Add(_V* pItem) { Dbg_MsgAssert(pItem,( "can't add NULL item to table")); // do we need to create a new table? if (m_size <= m_entriesFilled) { // create new table int new_size = m_size + m_growIncrement; _V **ppTemp = (_V**) Mem::Malloc(sizeof(_V *) * new_size); //_V **ppTemp = new _V*[new_size]; // copy contents of old one int i; for (i = 0; i < m_size; i++) ppTemp[i] = mpp_table[i]; for (i = m_size; i < new_size; i++) ppTemp[i] = NULL; Mem::Free(mpp_table); //delete [] mpp_table; mpp_table = ppTemp; m_size = new_size; } mpp_table[m_entriesFilled++] = pItem; } template int DynamicTable<_V>::GetSize() { return m_entriesFilled; } template void DynamicTable<_V>::Reset() { m_entriesFilled = 0; } template void DynamicTable<_V>::Replace(int index, _V *pNewItem) { Dbg_MsgAssert(index >= 0 && index < m_entriesFilled,( "index out of bounds in DynamicTable")); Dbg_AssertPtr(pNewItem); mpp_table[index] = pNewItem; } template void DynamicTable<_V>::Remove(int index) { Dbg_MsgAssert(index >= 0 && index < m_entriesFilled,( "index out of bounds in DynamicTable")); delete mpp_table[index]; for ( int i = index; i < m_entriesFilled-1; i++ ) { mpp_table[i] = mpp_table[i+1]; } mpp_table[m_entriesFilled-1] = NULL; m_entriesFilled--; } /* template bool DynamicTable<_V>::IsEmpty(int index) { Dbg_MsgAssert(index >= 0 && index < m_entriesFilled,( "index out of bounds in DynamicTable")); if (mpp_table[index]) return false; return true; } */ template _V &DynamicTable<_V>::operator[](int index) { Dbg_MsgAssert(index >= 0 && index < m_entriesFilled,( "index %d out of bounds in DynamicTable", index)); Dbg_MsgAssert(mpp_table[index],( "table entry is NULL")); Dbg_AssertPtr(mpp_table[index]); return *mpp_table[index]; } template _V &DynamicTable<_V>::Last() { Dbg_MsgAssert(m_entriesFilled > 0,( "empty list")); return *mpp_table[m_entriesFilled-1]; } template DynamicTableDestroyer<_V>::DynamicTableDestroyer(DynamicTable<_V> *pTable) { mp_table = pTable; } template void DynamicTableDestroyer<_V>::DeleteTableContents() { int size = mp_table->GetSize(); for (int i = 0; i < size; i++) { //if (!mp_table->IsEmpty()) delete &(*mp_table)[i]; } mp_table->Reset(); } } // namespace Lst #endif // __CORE_LIST_DYNAMICTABLE_H ================================================ FILE: Code/Core/HashTable.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core library ** ** ** ** Module: HashTable ** ** ** ** File name: Core\HashTable.h ** ** ** ** Created by: 9/22/2000 - rjm ** ** ** ** Description: A handy Hashtable class ** ** ** *****************************************************************************/ #ifndef __CORE_LIST_HASHTABLE_H #define __CORE_LIST_HASHTABLE_H #ifndef __CORE_DEFINES_H #include #endif #ifndef __PLAT_WN32__ #include #endif /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Lst { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ template< class _V > class HashTable; template< class _V > class HashItem { friend class HashTable<_V>; private: HashItem(); void Init(); uint32 m_key; _V * mp_value; HashItem<_V> * mp_next; }; template class HashTable { typedef void (*HashCallback)(_V*, void*); public: HashTable(uint32 numBits); ~HashTable(); // if any item exists with the same key, replace it bool PutItem(const uint32 &key, _V *item); // delete the item, and remove it from the table void FlushItem(const uint32 &key); // print all instances of an item (debugging) void PrintItem(const uint32 &key); // gets a pointer to requested item, returns NULL if item not in table _V * GetItem(const uint32 &key, bool assert_if_clash = true); _V * GetNextItemWithSameKey(const uint32 &key, _V *p_item); void FlushAllItems(void); void HandleCallback(HashCallback, void* pData); int GetSize(){return m_size;} void IterateStart(); _V * IterateNext(uint32 *pRetKey = NULL); #ifdef __NOPT_ASSERT__ void PrintContents(); #endif void AllowDuplicateKeys(bool allowed) { m_allowDuplicateKeys = allowed; } protected: uint32 m_numBits; // resolution of hash table HashItem<_V> * mp_hash_table; int m_size; // number of entries in the table int m_iterator_index; HashItem<_V> * mp_iterator_item; bool m_allowDuplicateKeys; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ template //inline HashTable<_V>::HashTable(uint32 numBits) { //Ryan("Creating HashTable"); m_numBits = numBits; mp_hash_table = new HashItem<_V>[1< //inline HashTable<_V>::~HashTable() { //Ryan("Destroying HashTable"); Dbg_AssertPtr( mp_hash_table ); if (!mp_hash_table) return; FlushAllItems(); // Remove the table. // Mem::Free(mp_hash_table); delete[] mp_hash_table; mp_hash_table=NULL; } template //inline bool HashTable<_V>::PutItem(const uint32 &key, _V *item) { Dbg_AssertPtr(item); //Ryan("putting item in Hash table\n"); Dbg_AssertPtr( mp_hash_table ); // sometimes, we want to allow checksum conflicts // (for instance, ConvertAssets' file database) if ( !m_allowDuplicateKeys ) { Dbg_MsgAssert(!GetItem(key), ("item 0x%x already in hash table", key)); } // can't add an item of 0,NULL, as that is used to indicate an empty head slot Dbg_MsgAssert(key || item, ("Both key and item are 0 (NULL) in hash table")); // can have a value of NULL either, as the the test below uses pEntry->mp_value == NULL // to indicate an an empty head slot // We could just change it to ( pEntry->mp_value || pEntry->m_key ) if NULL values are desirable Dbg_MsgAssert( item, ("NULL item added to hash table")); HashItem<_V> *pEntry=&mp_hash_table[key&((1<mp_value ) { // The main table entry is already occupied, so create a new HashEntry and // link it in between the first and the rest. #ifndef __PLAT_WN32__ HashItem<_V> *pNew = new (Mem::PoolManager::SCreateItem(Mem::PoolManager::vHASH_ITEM_POOL)) HashItem<_V>(); #else HashItem<_V> *pNew = new HashItem<_V>; #endif pNew->m_key = key; pNew->mp_value = item; pNew->mp_next = pEntry->mp_next; pEntry->mp_next = pNew; } else { // Main table entry is not occupied, so wack it in there. pEntry->m_key = key; pEntry->mp_value = item; // leave pEntry->mp_next untouched } m_size++; return true; } template //inline _V *HashTable<_V>::GetNextItemWithSameKey(const uint32 &key, _V *p_item) { Dbg_AssertPtr( mp_hash_table ); // Jump to the linked list of all entries with similar checksums. HashItem<_V> *pEntry=&mp_hash_table[key&((1<m_key == key && pEntry->mp_value==p_item) { break; } pEntry=pEntry->mp_next; } if (!pEntry) { // p_item was not found. return NULL; } // Found p_item, so search the rest of the list for the next element with the same key. pEntry=pEntry->mp_next; while (pEntry) { if (pEntry->m_key == key && pEntry->mp_value) { return pEntry->mp_value; } pEntry=pEntry->mp_next; } return NULL; } template //inline _V *HashTable<_V>::GetItem(const uint32 &key, bool assert_if_clash) { Dbg_AssertPtr( mp_hash_table ); // Jump to the linked list of all entries with similar checksums. HashItem<_V> *pEntry=&mp_hash_table[key&((1<m_key == key && pEntry->mp_value) { return (_V *) pEntry->mp_value; } pEntry=pEntry->mp_next; } return NULL; } template //inline void HashTable<_V>::PrintItem(const uint32 &key) { Dbg_AssertPtr( mp_hash_table ); // Jump to the linked list of all entries with similar checksums. HashItem<_V> *pEntry=&mp_hash_table[key&((1<m_key == key && pEntry->mp_value) { printf ("%d: Entry for 0x%x at %p\n", n ,key,(_V *) pEntry->mp_value); n++; } pEntry=pEntry->mp_next; } } template //inline void HashTable<_V>::FlushItem(const uint32 &key) { Dbg_AssertPtr( mp_hash_table ); if (!mp_hash_table) return; // Jump to the linked list of all entries with similar checksums. HashItem<_V> *pEntry=&mp_hash_table[key&((1< *pLast = NULL; // Scan through the small list until the matching entry is found. while (pEntry) { HashItem<_V> *p_next_entry = pEntry->mp_next; if (pEntry->m_key==key && pEntry->mp_value) // to allow keys of value 0, we have to skip head nodes that are 0,NULL { if (pLast) { // this is not a main table entry; this is a linked entry pLast->mp_next = pEntry->mp_next; #ifndef __PLAT_WN32__ Mem::PoolManager::SFreeItem(Mem::PoolManager::vHASH_ITEM_POOL, pEntry); #else delete pEntry; #endif } else { // this is a main table entry, it still might be linked to something // clear the entry to 0,NULL (see comment above about keys of value 0) pEntry->m_key = 0; pEntry->mp_value = NULL; } m_size--; return; } pLast = pEntry; pEntry = p_next_entry; } return; } template //inline void HashTable<_V>::FlushAllItems() { Dbg_AssertPtr( mp_hash_table ); if (!mp_hash_table) return; // Run through the table and delete any of the extra // HashItem<_V>s. HashItem<_V> *pMainEntry = mp_hash_table; uint32 hashTableSize = (1< *pLinkedEntry = pMainEntry->mp_next; while (pLinkedEntry) { HashItem<_V> *pNext = pLinkedEntry->mp_next; #ifndef __PLAT_WN32__ Mem::PoolManager::SFreeItem(Mem::PoolManager::vHASH_ITEM_POOL, pLinkedEntry); #else delete pLinkedEntry; #endif pLinkedEntry = pNext; } pMainEntry->Init(); ++pMainEntry; } m_size = 0; } template //inline void HashTable<_V>::HandleCallback(HashCallback hashCallback, void* pData) { HashItem<_V> *pMainEntry = mp_hash_table; uint32 hashTableSize=(1< *pLinkedEntry = pMainEntry->mp_next; while (pLinkedEntry) { HashItem<_V> *pNext = pLinkedEntry->mp_next; // Mem::Free(pLinkedEntry); if (pLinkedEntry->mp_value) (hashCallback)((_V *) pLinkedEntry->mp_value, pData); pLinkedEntry = pNext; } if (pMainEntry->mp_value) (hashCallback)((_V *) pMainEntry->mp_value, pData); ++pMainEntry; } } template //inline void HashTable<_V>::IterateStart() { m_iterator_index = -1; mp_iterator_item = NULL; } template //inline _V * HashTable<_V>::IterateNext(uint32 *pRetKey) { uint32 hashTableSize=(1<mp_next; else if (m_iterator_index >= 0) // we've exhausted all the lists return NULL; if (!mp_iterator_item) { // no entry in list, move on to next list do { m_iterator_index++; if (m_iterator_index >= (int) hashTableSize) return NULL; mp_iterator_item = mp_hash_table + m_iterator_index; } // main entry has to contain something, or be part of a list while (!mp_iterator_item->mp_value && !mp_iterator_item->mp_next); if (!mp_iterator_item->mp_value) // this must be an empty main entry, skip ahead mp_iterator_item = mp_iterator_item->mp_next; } // Ken: Added this because it was hanging here once when loading the junkyard // off CD. It was trying to dereference mp_iterator_item. Added the printf // so we can at least see when it happens without having to go into the debugger. if (!mp_iterator_item) { #ifdef __NOPT_ASSERT__ printf("Error!! NULL mp_iterator_item in IterateNext()\n"); #endif return NULL; } if (pRetKey) *pRetKey = mp_iterator_item->m_key; return mp_iterator_item->mp_value; } #ifdef __NOPT_ASSERT__ template //inline void HashTable<_V>::PrintContents() { printf("Items in Hash Table:\n"); uint32 hashTableSize=(1< *pEntry = mp_hash_table + i; while(pEntry) { if (pEntry->mp_value) printf(" 0x%x [%d]\n", pEntry->m_key, i); pEntry = pEntry->mp_next; } } } #endif template //inline HashItem<_V>::HashItem<_V>() { Init(); } template //inline void HashItem<_V>::Init() { m_key = 0; mp_value = NULL; mp_next = NULL; } } // namespace Lst #endif // __CORE_LIST_HASHTABLE_H ================================================ FILE: Code/Core/List/Head.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: List (LST_) ** ** ** ** File name: core/list/head.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_LIST_HEAD_H #define __CORE_LIST_HEAD_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Lst { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /*********************************************************************** * * Class: Head * * Description: Linked-list head node. * ***********************************************************************/ nTemplateSubClass( _T, Head, Node< _T > ) { public: Head( void ); virtual ~Head( void ); void Merge( Head< _T >* dest ); // Source list will be empty after merge uint CountItems( void ); Node< _T >* GetItem( uint number ); // Zero-based ( 0 will return first node ) void AddNode( Node< _T >* node ); // Using priority void AddNodeFromTail( Node< _T >* node ); // Using priority, search backwards from tail (i.e same-priorties are appended rather than pre-pended) bool AddUniqueSequence( Node< _T >* node ); // Only add if priority is unique // and priority decreases void AddToTail( Node< _T >* node ); void AddToHead( Node< _T >* node ); void RemoveAllNodes( void ); void DestroyAllNodes( void ); // ONLY USE FOR INHERITED LISTS bool IsEmpty( void ); Node< _T >* FirstItem(); // get first node, or NULL if none }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ template < class _T > inline Head< _T >::Head( void ) : Node< _T > ( reinterpret_cast < _T* >( vHEAD_NODE ) ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Head< _T >::~Head( void ) { Dbg_MsgAssert( IsEmpty(),( "List is not empty" )); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Head< _T >::AddNode( Node< _T >* node ) { Dbg_AssertType( node, Node< _T > ); Dbg_MsgAssert( this->is_head (),( "Object is not a list" )); Dbg_MsgAssert( !node->InList (),( "Object is already in a list" )); Node< _T >* node_ptr = this; Priority new_pri = node->GetPri(); while (( node_ptr = node_ptr->GetNext() )) { if ( node_ptr->GetPri() <= new_pri ) { node_ptr->Insert( node ); return; } } Insert( node ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Head< _T >::AddNodeFromTail( Node< _T >* node ) { Dbg_AssertType( node, Node< _T > ); Dbg_MsgAssert( this->is_head (),( "Object is not a list" )); Dbg_MsgAssert( !node->InList (),( "Object is already in a list" )); Node< _T >* node_ptr = this; Priority new_pri = node->GetPri(); while (( node_ptr = node_ptr->GetPrev() )) { if ( node_ptr->GetPri() >= new_pri ) { node_ptr->Append( node ); return; } } Append( node ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline bool Head< _T >::AddUniqueSequence( Node< _T >* node ) { Dbg_AssertType( node, Node< _T > ); Dbg_MsgAssert( this->is_head (),( "Object is not a list" )); Dbg_MsgAssert( !node->InList (),( "Object is already in a list" )); Node< _T >* node_ptr = this; Priority new_pri = node->GetPri(); while (( node_ptr = node_ptr->GetNext() )) { if ( node_ptr->GetPri() == new_pri ) { return false; } else if ( node_ptr->GetPri() > new_pri ) { node_ptr->Insert( node ); return true; } } Insert( node ); return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Head< _T >::Merge( Head< _T >* dest ) { Dbg_AssertType( dest, Head< _T > ); Dbg_MsgAssert( this->is_head (),( "Object is not a list" )); Dbg_MsgAssert( dest->is_head (),( "Object is not a list" )); Node< _T >* first = next; Node< _T >* last = prev; Node< _T >* node = dest->GetPrev(); if ( this == first ) // source list is empty { return; } node->SetNext( first ); first->SetPrev( node ); last->SetNext( dest ); dest->SetPrev( last ); node_init(); // make the source list empty } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Node< _T >* Head< _T >::GetItem( uint number ) { Dbg_MsgAssert( this->is_head (),( "Object is not a list" )); Node< _T >* node = GetNext(); while ( node ) { if ( number-- == 0 ) { return node; } node = node->GetNext(); } Dbg_Warning( "Item requested (%d) out of range (%d)", number, CountItems() ); return NULL; } /******************************************************************/ /* */ /* */ /******************************************************************/ // Return the firs node in the list that this is the head of template < class _T > inline Node< _T >* Head< _T >::FirstItem( ) { Dbg_MsgAssert( this->is_head (),( "Object is not a list" )); return GetNext(); } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline uint Head< _T >::CountItems( void ) { Dbg_MsgAssert( this->is_head (),( "Object is not a list" )); uint count = 0; Node< _T >* node = GetNext(); while ( node ) { count++; node = node->GetNext(); } return count; } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline void Head< _T >::RemoveAllNodes( void ) { Dbg_MsgAssert( this->is_head (),( "Object is not a list" )); Node< _T >* next_nd; Node< _T >* node = GetNext(); while ( node ) { next_nd = node->GetNext(); node->Remove(); node = next_nd; } } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline void Head< _T >::DestroyAllNodes( void ) { Dbg_MsgAssert( this->is_head (),( "Object is not a list" )); Node< _T >* next_nd; Node< _T >* node = GetNext(); while ( node ) { next_nd = node->GetNext(); // node->Remove(); delete node; node = next_nd; } } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline void Head< _T >::AddToTail( Node< _T >* node ) { Dbg_AssertType( node, Node< _T > ); Dbg_MsgAssert( this->is_head (),( "Object is not a list" )); Dbg_MsgAssert( !node->InList (),( "Node is already in a list" )); Insert ( node ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline void Head< _T >::AddToHead ( Node< _T >* node ) { Dbg_AssertType( node, Node< _T > ); Dbg_MsgAssert( this->is_head(),(( "Object is not a list" ))); Dbg_MsgAssert( !node->InList(),(( "Node is already in a list" ))); Append( node ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline bool Head< _T >::IsEmpty( void ) { Dbg_MsgAssert ( this->is_head(),( "Object is not a list" )); return ( !InList() ); } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Lst #endif // __CORE_LIST_HEAD_H ================================================ FILE: Code/Core/List/Node.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: List (LST_) ** ** ** ** File name: core/list/node.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_LIST_Node_H #define __CORE_LIST_Node_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Lst { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /*********************************************************************** * * Class: Lst::Node * * Description: Linked-list node. * ***********************************************************************/ nTemplateBaseClass( _T, Node ) { public: // GJ: Note we have to cast vINT_MIN to an sint // ( ((sint)vINT_MIN) / 2 ) ends up positive. enum { vNORMAL_PRIORITY = 0, vHEAD_NODE = vUINT32_MAX }; enum { vSYSTEM_TASK_PRIORITY_PROCESS_MODULES = -1000, vSYSTEM_TASK_PRIORITY_FLUSH_DEAD_OBJECTS = 1000, }; enum { vLOGIC_TASK_PRIORITY_REPLAY_END_FRAME = -3000, vLOGIC_TASK_PRIORITY_PROCESS_NETWORK_METRICS = -2500, vLOGIC_TASK_PRIORITY_SERVER_SEND_NETWORK_DATA = -2000, vLOGIC_TASK_PRIORITY_CLIENT_SEND_NETWORK_DATA = -2000, vLOGIC_TASK_PRIORITY_TIMEOUT_CONNECTIONS = -2000, vLOGIC_TASK_PRIORITY_CLIENT_ADD_NEW_PLAYERS = -2000, vLOGIC_TASK_PRIORITY_SERVER_ADD_NEW_PLAYERS = -2000, vLOGIC_TASK_PRIORITY_FRONTEND = -2000, vLOGIC_TASK_PRIORITY_SCRIPT_DEBUGGER = -1500, vLOGIC_TASK_PRIORITY_LOCKED_OBJECT_MANAGER_LOGIC = -1100, vLOGIC_TASK_PRIORITY_PARTICLE_MANAGER_LOGIC = -1000, vLOGIC_TASK_PRIORITY_HANDLE_KEYBOARD = -1000, vLOGIC_TASK_PRIORITY_OBJECT_UPDATE = -1000, vLOGIC_TASK_PRIORITY_SCORE_UPDATE = -1000, vLOGIC_TASK_PRIORITY_COMPOSITE_MANAGER = -900, vLOGIC_TASK_PRIORITY_PROCESS_NETWORK_DATA = 0, vLOGIC_TASK_PRIORITY_SKATER_SERVER = 1000, vLOGIC_TASK_PRIORITY_TRANSFER_NETWORK_DATA = 1000, vLOGIC_TASK_PRIORITY_RECEIVE_NETWORK_DATA = 1000, vLOGIC_TASK_PRIORITY_PROCESS_HANDLERS = 2000, vLOGIC_TASK_PRIORITY_REPLAY_START_FRAME = 3000, }; enum { vDISPLAY_TASK_PRIORITY_PARK_EDITOR_DISPLAY = -1000, vDISPLAY_TASK_PRIORITY_CPARKEDITOR_DISPLAY = -1000, vDISPLAY_TASK_PRIORITY_IMAGE_VIEWER_DISPLAY = -1000, vDISPLAY_TASK_PRIORITY_PANEL_DISPLAY = -500, }; enum { vHANDLER_PRIORITY_OBSERVER_INPUT_LOGIC = -1000, vHANDLER_PRIORITY_FRONTEND_INPUT_LOGIC0 = -1000, vHANDLER_PRIORITY_FRONTEND_INPUT_LOGIC1 = -1000, vHANDLER_PRIORITY_IMAGE_VIEWER_INPUT_LOGIC = 1000, vHANDLER_PRIORITY_VIEWER_SHIFT_INPUT_LOGIC = 1000, }; typedef sint Priority; Node ( _T* d, Priority p = vNORMAL_PRIORITY ); virtual ~Node ( void ); void Insert ( Node< _T >* node ); void Append ( Node< _T >* node ); void Remove ( void ); void SetPri ( const Priority priority ); void SetNext ( Node< _T >* node ); void SetPrev ( Node< _T >* node ); Priority GetPri ( void ) const; Node< _T >* GetNext ( void ) const; Node< _T >* GetPrev ( void ) const; _T* GetData ( void ) const; Node< _T >* LoopNext ( void ) const; Node< _T >* LoopPrev ( void ) const; bool InList ( void ) const; protected: bool is_head ( void ) const; void node_init ( void ); private: _T* data; Priority pri; Node< _T >* next; Node< _T >* prev; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Inline Functions ** *****************************************************************************/ template < class _T > inline void Node< _T >::node_init( void ) { next = this; prev = this; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline bool Node< _T >::is_head( void ) const { return ( data == reinterpret_cast( vHEAD_NODE )); } /***************************************************************************** ** Public Inline Functions ** *****************************************************************************/ template < class _T > inline Node< _T >::Node( _T* d, Priority p ) : data( d ), pri( p ) { node_init(); } /******************************************************************/ /* */ /* */ /******************************************************************/ // Must define inline functions before they are used template < class _T > inline void Node< _T >::Remove( void ) { prev->next = next; next->prev = prev; node_init(); // so we know that the node is not in a list } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Node< _T >::~Node( void ) { Remove(); } /******************************************************************/ /* */ /* */ /******************************************************************/ // Mick: Moved this here from the end of the file // as it's intended to b inline // yet it is used by other functions below template < class _T > inline bool Node< _T >::InList( void ) const { return ( prev != this ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Node< _T >::Insert ( Node< _T >* node ) { Dbg_AssertType( node, Node< _T > ); Dbg_MsgAssert( !node->InList(),("node is already in a list" )); node->prev = prev; node->next = this; prev->next = node; prev = node; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Node< _T >::Append( Node< _T >* node ) { Dbg_AssertType( node, Node< _T > ); Dbg_MsgAssert( !node->InList(),( "node is already in a list" )); node->prev = this; node->next = next; next->prev = node; next = node; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Node< _T >::SetPri( const Priority priority ) { pri = priority; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Node< _T >::SetNext( Node< _T >* node ) { Dbg_AssertType( node, Node< _T > ); next = node; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Node< _T >::SetPrev( Node< _T >* node ) { Dbg_AssertType( node, Node< _T > ); prev = node; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline typename Node< _T >::Priority Node< _T >::GetPri( void ) const { return pri; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Node< _T >* Node< _T >::GetNext( void ) const { if ( next->is_head() ) { return NULL; } return next; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Node< _T >* Node< _T >::GetPrev( void ) const { if ( prev->is_head() ) { return NULL; } return prev; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline _T* Node< _T >::GetData( void ) const { return data; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Node< _T >* Node< _T >::LoopNext( void ) const { Node< _T >* next_node = next; if ( next_node->is_head() ) { next_node = next_node->next; // skip head node if ( next_node->is_head() ) { return NULL; // list is empty } } return next_node; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Node< _T >* Node< _T >::LoopPrev( void ) const { Node< _T >* prev_node = prev; if ( prev_node->is_head() ) { prev_node = prev_node->prev; // skip head node if ( prev_node->is_head() ) { return NULL; // list is empty } } return prev_node; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Lst #endif // __CORE_LIST_Node_H ================================================ FILE: Code/Core/List/Search.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: List (LST_) ** ** ** ** File name: core/list/search.h ** ** ** ** Created: 04/02/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_LIST_SEARCH_H #define __CORE_LIST_SEARCH_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Lst { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ nTemplateBaseClass ( _T, Search ) { public: Search ( void ); virtual ~Search ( void ); _T* FirstItem ( Head<_T>& head ); _T* LastItem ( Head<_T>& head ); _T* NextItem ( void ); _T* PrevItem ( void ); private: Node< _T >* node; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ template inline Search<_T>::Search ( void ) : node ( NULL ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline Search<_T>::~Search ( void ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline _T* Search<_T>::FirstItem ( Head<_T>& head ) { Dbg_AssertType ( &head, Head<_T> ); node = &head; return ( NextItem () ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline _T* Search<_T>::LastItem ( Head<_T>& head ) { Dbg_AssertType ( &head, Head<_T> ); node = &head; return ( PrevItem () ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline _T* Search<_T>::NextItem ( void ) { Dbg_AssertType ( node, Node< _T > ); node = node->GetNext(); if ( !node ) { return NULL; } Dbg_AssertType ( node, Node<_T> ); return ( node->GetData() ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template inline _T* Search<_T>::PrevItem ( void ) { Dbg_AssertType ( node, Node< _T > ); node = node->GetPrev(); if ( !node ) { return NULL; } Dbg_AssertType ( node, Node<_T> ); return ( node->GetData() ); } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Lst #endif // __CORE_LIST_SEARCH_H ================================================ FILE: Code/Core/List/list.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: List (Lst) ** ** ** ** File name: list.cpp ** ** ** ** Created by: 05/27/99 - mjb ** ** ** ** Description: List management code ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ ================================================ FILE: Code/Core/List.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: List (Lst) ** ** ** ** File name: core/list.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_LIST_H #define __CORE_LIST_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ #endif // __CORE_LIST_H ================================================ FILE: Code/Core/LookupTable.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core library ** ** ** ** Module: List ** ** ** ** File name: Core\List\LookupTable.cpp ** ** ** ** Created by: 9/22/2000 - rjm ** ** ** ** Description: A Lookuptable ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include // /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Lst { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ /****************************************************************************** * * Function: * * Description: Default constructor * * Parameters: * *****************************************************************************/ } // namespace Lst ================================================ FILE: Code/Core/LookupTable.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core library ** ** ** ** Module: LookupTable ** ** ** ** File name: Core\LookupTable.h ** ** ** ** Created by: 9/22/2000 - rjm ** ** ** ** Description: A handy Lookuptable class ** ** ** *****************************************************************************/ #ifndef __CORE_LIST_LOOKUPTABLE_H #define __CORE_LIST_LOOKUPTABLE_H #ifndef __CORE_CRC_H #include #endif /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Lst { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /**************************************************************************** * * Class: LookupItem * * Description: Used to represent an item in a Lookup table. * * Usage: * * * ****************************************************************************/ // forward declaration template< class _V > class LookupTable; //nTemplateBaseClass2(_K, _V, LookupItem) template< class _V > class LookupItem { // Dbg_TemplateBaseClass2(_K, _V, LookupItem); friend class LookupTable<_V>; private: LookupItem(); int m_key; _V * mp_value; LookupItem<_V> * mp_next; }; nTemplateBaseClass(_V, LookupTable) { Dbg_TemplateBaseClass(_V, LookupTable); public: LookupTable(int size=0); // Mick, size is not used, so give it default until we get rid of it ~LookupTable(); // if any item exists with the same key, replace it bool PutItem(const int &key, _V *item); // gets a pointer to requested item, returns NULL if item not in table _V *GetItem(const int &key); // gets a pointer to requested item, returns NULL if item not in table _V *GetItemByIndex(const int &index, int *pKey = NULL); int getSize() {return m_size;} // removes item from table, calls its destructor if requested void FlushItem(const int &key); void flushAllItems(); private: int m_size; LookupItem<_V> * mp_list; // first item in list LookupItem<_V> * mp_last; // last item in list LookupItem<_V> * mp_current; // Pointer to current item in this table, NULL if invalid int m_currentIndex; // the index of this item. Only valid if mp_current is not NULL }; template class StringLookupTable : public LookupTable<_V> { //Dbg_TemplateBaseClass2(_K, _V, LookupItem); public: StringLookupTable(int size); bool PutItem(const char *stringKey, _V *item); _V *GetItem(const char *stringKey); void FlushItem(const char *stringKey); }; template class LookupTableDestroyer { public: LookupTableDestroyer(LookupTable<_V> *pTable); void DeleteTableContents(); private: LookupTable<_V> * mp_table; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ template //inline LookupItem<_V>::LookupItem() { mp_value = NULL; mp_next = NULL; } template //inline LookupTable<_V>::LookupTable(int size=0) { m_size = 0; mp_list = NULL; mp_current = NULL; // initialized invalid, so we don't try to use it } template //inline LookupTable<_V>::~LookupTable() { flushAllItems(); } template //inline bool LookupTable<_V>::PutItem(const int &key, _V *item) { Dbg_AssertPtr(item); //Ryan("putting item in lookup table\n"); # ifdef __NOPT_DEBUG__ if (GetItem(key)) return false; # endif LookupItem<_V> *pItem = new LookupItem<_V>; pItem->mp_value = item; pItem->m_key = key; if (!mp_list) mp_list = pItem; else mp_last->mp_next = pItem; mp_last = pItem; m_size++; mp_current = NULL; // no longer valid return true; } template //inline _V *LookupTable<_V>::GetItem(const int &key) { LookupItem<_V> *pItem = mp_list; mp_current = NULL; // set invalid now, so if not found, then it will be correctly invalid m_currentIndex = 0; // index should be 0, for the first item while(pItem) { if (pItem->m_key == key) { mp_current = pItem; // we have a valid current, and hence index return pItem->mp_value; } m_currentIndex++; // update index, in case we find the item pItem = pItem->mp_next; } //printf("returning NULL from GetItem()\n"); // warning should be given by calling function, if necessary //Dbg_Warning("Item not found in lookup table"); return NULL; } template //inline _V *LookupTable<_V>::GetItemByIndex(const int &index, int *pKey) { // size must be at least 1 Dbg_MsgAssert(index >= 0 && index < m_size,( "bad index %d", index)); // if we have a valid mp_current, then check if we can use that (for speed) if (mp_current) { // is it the same as last time? if (m_currentIndex == index) { // It's this one, so just leave it alone } else if (m_currentIndex < index) { // need to step forward until we find the correct one // this is the most likely scenario if // we are using GetItemByIndex in a for loop // really we need an interator access class // but this should be similar, in terms of code speed while (m_currentIndex < index) { m_currentIndex++; mp_current = mp_current->mp_next; } } else { // otherwise, we need to start again, so invalidate mp_current mp_current = NULL; } } // if we don't have a valid mp_current at this point, then we need // to start searching from the start again if (!mp_current) { LookupItem<_V> *pItem = mp_list; for (int i = 0; i < index; i++) { pItem = pItem->mp_next; } m_currentIndex = index; mp_current = pItem; } // if they want us to returnt he key, then poke it into the supplied destination if ( pKey ) *pKey = mp_current->m_key; // the current value is correct return mp_current->mp_value; } template //inline void LookupTable<_V>::FlushItem(const int &key) { mp_current = NULL; // No longer valid LookupItem<_V> *pItem = mp_list; LookupItem<_V> *prev = NULL; while(pItem) { if (pItem->m_key == key) { if (prev) prev->mp_next = pItem->mp_next; else mp_list = pItem->mp_next; if (mp_last == pItem) mp_last = prev; delete pItem; m_size--; return; } prev = pItem; pItem = pItem->mp_next; } } template //inline void LookupTable<_V>::flushAllItems() { LookupItem<_V> *pItem = mp_list; while(pItem) { LookupItem<_V> *pNext = pItem->mp_next; delete pItem; pItem = pNext; } mp_list = NULL; mp_current = NULL; m_size = 0; } /* int m_size; LookupItem<_K, _V> *m_array; _K m_key; _V *mp_value; */ template //inline StringLookupTable<_V>::StringLookupTable(int size) : LookupTable<_V>(size) { } template //inline bool StringLookupTable<_V>::PutItem(const char *stringKey, _V *item) { int key = Crc::GenerateCRCFromString(stringKey); return LookupTable<_V>::PutItem(key, item); } template //inline _V *StringLookupTable<_V>::GetItem(const char *stringKey) { int key = Crc::GenerateCRCFromString(stringKey); return LookupTable<_V>::GetItem(key); } template //inline void StringLookupTable<_V>::FlushItem(const char *stringKey) { int key = Crc::GenerateCRCFromString(stringKey); LookupTable<_V>::FlushItem(key); } template //inline LookupTableDestroyer<_V>::LookupTableDestroyer(LookupTable<_V> *pTable) { mp_table = pTable; } template inline void LookupTableDestroyer<_V>::DeleteTableContents() { int num_items = mp_table->getSize(); for (int i = 0; i < num_items; i++) { int key; // since we are flushing items out of the table as we go, // we should always grab the first one _V *pItem = mp_table->GetItemByIndex(0, &key); mp_table->FlushItem(key); if (pItem) delete pItem; } } } // namespace Lst #endif // __CORE_LIST_LOOKUPTABLE_H ================================================ FILE: Code/Core/Math/Xbox/sse.h ================================================ //----------------------------------------------------------------------------- // File: SSE.h // // Desc: P3 SSE conversions and estimates // // Hist: 1.7.03 - Created // // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #ifndef P3_SSE #define P3_SSE //----------------------------------------------------------------------------- // Name: Ftoi_ASM // Desc: SSE float to int conversion. Note that no control word needs to be // set to round down //----------------------------------------------------------------------------- __forceinline int Ftoi_ASM( const float f ) { __asm cvttss2si eax, f // return int(f) } //----------------------------------------------------------------------------- // Name: ReciprocalEstimate_ASM // Desc: SSE reciprocal estimate, accurate to 12 significant bits of // the mantissa //----------------------------------------------------------------------------- __forceinline float ReciprocalEstimate_ASM( const float f ) { float rec; __asm rcpss xmm0, f // xmm0 = rcpss(f) __asm movss rec , xmm0 // return xmm0 return rec; } //----------------------------------------------------------------------------- // Name: ReciprocalSqrtEstimate_ASM // Desc: SSE reciprocal square root estimate, accurate to 12 significant // bits of the mantissa //----------------------------------------------------------------------------- __forceinline float ReciprocalSqrtEstimate_ASM( const float f ) { float recsqrt; __asm rsqrtss xmm0, f // xmm0 = rsqrtss(f) __asm movss recsqrt, xmm0 // return xmm0 return recsqrt; } //----------------------------------------------------------------------------- // Name: SqrtEstimae_ASM // Desc: SSE square root estimate, accurate to 12 significant bits of // the mantissa. Note that a check for zero must be made since // sqrt(0) == 0 but 1/sqrt(0) = inf //----------------------------------------------------------------------------- __forceinline float SqrtEstimate_ASM( const float f ) { float recsqrt; __asm movss xmm0,f // xmm0 = f __asm rsqrtss xmm1, xmm0 // xmm1 = rsqrtss(f) __asm mulss xmm1, xmm0 // xmm1 = rsqrtss(f) * f = sqrt(f) __asm xorps xmm2, xmm2 // xmm2 = 0 __asm cmpneqss xmm2, xmm0 // xmm2 = (f != 0 ? 1s : 0s) __asm andps xmm1, xmm2 // xmm1 = xmm1 & xmm2 __asm movss recsqrt, xmm1 // return xmm1 return recsqrt; } //----------------------------------------------------------------------------- // Name: ReciprocalEstimateNR_ASM // Desc: SSE Newton-Raphson reciprocal estimate, accurate to 23 significant // bits of the mantissa // One Newtown-Raphson Iteration: // f(i+1) = 2 * rcpss(f) - f * rcpss(f) * rcpss(f) //----------------------------------------------------------------------------- __forceinline float ReciprocalEstimateNR_ASM( const float f ) { float rec; __asm rcpss xmm0, f // xmm0 = rcpss(f) __asm movss xmm1, f // xmm1 = f __asm mulss xmm1, xmm0 // xmm1 = f * rcpss(f) __asm mulss xmm1, xmm0 // xmm2 = f * rcpss(f) * rcpss(f) __asm addss xmm0, xmm0 // xmm0 = 2 * rcpss(f) __asm subss xmm0, xmm1 // xmm0 = 2 * rcpss(f) // - f * rcpss(f) * rcpss(f) __asm movss rec, xmm0 // return xmm0 return rec; } //----------------------------------------------------------------------------- // Newton-Rapson square root iteration constants //----------------------------------------------------------------------------- const float g_SqrtNRConst[2] = {0.5f, 3.0f}; //----------------------------------------------------------------------------- // Name: ReciprocalSqrtEstimateNR_ASM // Desc: SSE Newton-Raphson reciprocal square root estimate, accurate to 23 // significant bits of the mantissa // One Newtown-Raphson Iteration: // f(i+1) = 0.5 * rsqrtss(f) * (3.0 - (f * rsqrtss(f) * rsqrtss(f)) // NOTE: rsqrtss(f) * rsqrtss(f) != rcpss(f) (presision is not maintained) //----------------------------------------------------------------------------- __forceinline float ReciprocalSqrtEstimateNR_ASM( const float f ) { float recsqrt; __asm rsqrtss xmm0, f // xmm0 = rsqrtss(f) __asm movss xmm1, f // xmm1 = f __asm mulss xmm1, xmm0 // xmm1 = f * rsqrtss(f) __asm movss xmm2, g_SqrtNRConst+4 // xmm2 = 3.0f __asm mulss xmm1, xmm0 // xmm1 = f * rsqrtss(f) * rsqrtss(f) __asm mulss xmm0, g_SqrtNRConst // xmm0 = 0.5f * rsqrtss(f) __asm subss xmm2, xmm1 // xmm2 = 3.0f - // f * rsqrtss(f) * rsqrtss(f) __asm mulss xmm0, xmm2 // xmm0 = 0.5 * rsqrtss(f) // * (3.0 - (f * rsqrtss(f) * rsqrtss(f)) __asm movss recsqrt, xmm0 // return xmm0 return recsqrt; } //----------------------------------------------------------------------------- // Name: SqrtEstimateNR_ASM // Desc: SSE Newton-Raphson square root estimate, accurate to 23 significant // bits of the mantissa // NOTE: x/sqrt(x) = sqrt(x) // One Newtown-Raphson Iteration (for 1/sqrt(x)) : // f(i+1) = 0.5 * rsqrtss(f) * (3.0 - (f * rsqrtss(f) * rsqrtss(f)) // NOTE: rsqrtss(f) * rsqrtss(f) != rcpss(f) (presision is not maintained) //----------------------------------------------------------------------------- __forceinline float SqrtEstimateNR_ASM( const float f ) { float recsqrt; __asm rsqrtss xmm0, f // xmm0 = rsqrtss(f) __asm movss xmm1, f // xmm1 = f __asm mulss xmm1, xmm0 // xmm1 = f * rsqrtss(f) __asm movss xmm2, g_SqrtNRConst+4 // xmm2 = 3.0f __asm mulss xmm1, xmm0 // xmm1 = f * rsqrtss(f) * rsqrtss(f) __asm mulss xmm0, g_SqrtNRConst // xmm0 = 0.5f * rsqrtss(f) __asm subss xmm2, xmm1 // xmm2 = 3.0f - // f * rsqrtss(f) * rsqrtss(f) __asm mulss xmm0, xmm2 // xmm0 = 0.5 * rsqrtss(f) // * (3.0 - (f * rsqrtss(f) * rsqrtss(f)) __asm xorps xmm1, xmm1 // xmm1 = 0 __asm mulss xmm0, f // xmm0 = sqrt(f) __asm cmpneqss xmm1, f // xmm1 = (f != 0 ? 1s : 0s) __asm andps xmm0, xmm1 // xmm0 = xmm1 & xmm2 __asm movss recsqrt, xmm0 // return xmm0 return recsqrt; } #endif // P3_SSE ================================================ FILE: Code/Core/Math/geometry.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Geometry (MTH) ** ** ** ** File name: Geometry.cpp ** ** ** ** Created by: 11/14/00 - Mick ** ** ** ** Description: Math ** // This module handles the representation and manipulation of line segements // and planes // // a line segment (Mth::Line) is defined by two points, m_start and m_end // // a plane is defined by a point and a normal ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Mth { /***************************************************************************** ** DBG Information ** *****************************************************************************/ // /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ Line::Line() { } Line::Line ( const Vector &start, const Vector &end ) { m_start = start; m_end = end; } Plane::Plane() { } Plane::Plane (const Vector &point, const Vector &normal) { m_point = point; m_normal = normal; } Rectangle::Rectangle() { } Rectangle::Rectangle ( const Vector& corner, const Vector& first_edge, const Vector& second_edge ) { m_corner = corner; m_first_edge = first_edge; m_second_edge = second_edge; } /* Calculate the line segment PaPb that is the shortest route between two lines P1P2 and P3P4. Calculate also the values of mua and mub where Pa = P1 + mua (P2 - P1) Pb = P3 + mub (P4 - P3) Return FALSE if no solution exists. original algorithm from http://www.swin.edu.au/astronomy/pbourke/geometry/lineline3d/ note that I (Mick) modified it to clamp the points to within the line segments if you remove the "Clamp" calls below, then the lines will be assumed to be of infinite length */ #define EPS 0.00001f bool LineLineIntersect( Line & l1, Line & l2, Vector *pa, Vector *pb, float *mua, float *mub, bool clamp ) { Vector &p1 = l1.m_start; Vector &p2 = l1.m_end; Vector &p3 = l2.m_start; Vector &p4 = l2.m_end; Vector p13,p43,p21; float d1343,d4321,d1321,d4343,d2121; float numer,denom; p13 = p1 - p3; p43 = p4 - p3; if (Abs(p43[X]) < EPS && Abs(p43[Y]) < EPS && Abs(p43[Z]) < EPS) return(false); p21 = p2 - p1; if (Abs(p21[X]) < EPS && Abs(p21[Y]) < EPS && Abs(p21[Z]) < EPS) return(false); d1343 = DotProduct(p13,p43); d4321 = DotProduct(p43,p21); d1321 = DotProduct(p13,p21); d4343 = DotProduct(p43,p43); d2121 = DotProduct(p21,p21); denom = d2121 * d4343 - d4321 * d4321; if (Abs(denom) < EPS) return(false); numer = d1343 * d4321 - d1321 * d4343; *mua = numer / denom; if( clamp ) { *mua = Clamp(*mua,0.0f,1.0f); } *mub = (d1343 + d4321 * (*mua)) / d4343; if( clamp ) { *mub = Clamp(*mub,0.0f,1.0f); } *pa = p1 + (*mua * p21); *pb = p3 + (*mub * p43); return(true); } /******************************************************************/ /* */ /* */ /******************************************************************/ CBBox::CBBox() { Reset(); } CBBox::CBBox(const Vector &point) { Set(point, point); } CBBox::CBBox(const Vector &min, const Vector &max) { Set(min, max); } void CBBox::AddPoint(const Vector &point) { // Adjust min/max points if (point[X] < m_min[X]) m_min[X] = point[X]; if (point[Y] < m_min[Y]) m_min[Y] = point[Y]; if (point[Z] < m_min[Z]) m_min[Z] = point[Z]; if (point[X] > m_max[X]) m_max[X] = point[X]; if (point[Y] > m_max[Y]) m_max[Y] = point[Y]; if (point[Z] > m_max[Z]) m_max[Z] = point[Z]; } // Returns number of axes within and flags for which ones int CBBox::WithinAxes(const Vector &point, uint32 &axis_flags) const { int number_of_axes = 0; axis_flags = 0; if ((point[X] >= m_min[X]) && (point[X] <= m_max[X])) { number_of_axes++; axis_flags |= 1 << X; } if ((point[Y] >= m_min[Y]) && (point[Y] <= m_max[Y])) { number_of_axes++; axis_flags |= 1 << Y; } if ((point[Z] >= m_min[Z]) && (point[Z] <= m_max[Z])) { number_of_axes++; axis_flags |= 1 << Z; } return number_of_axes; } void CBBox::DebugRender(uint32 rgba, int frames) const { // Min YZ square Gfx::AddDebugLine(Mth::Vector(m_min[X],m_min[Y],m_min[Z]),Mth::Vector(m_min[X],m_min[Y],m_max[Z]),rgba,rgba,frames); Gfx::AddDebugLine(Mth::Vector(m_min[X],m_min[Y],m_max[Z]),Mth::Vector(m_min[X],m_max[Y],m_max[Z]),rgba,rgba,frames); Gfx::AddDebugLine(Mth::Vector(m_min[X],m_max[Y],m_max[Z]),Mth::Vector(m_min[X],m_max[Y],m_min[Z]),rgba,rgba,frames); Gfx::AddDebugLine(Mth::Vector(m_min[X],m_max[Y],m_min[Z]),Mth::Vector(m_min[X],m_min[Y],m_min[Z]),rgba,rgba,frames); // Max YZ square Gfx::AddDebugLine(Mth::Vector(m_max[X],m_min[Y],m_min[Z]),Mth::Vector(m_max[X],m_min[Y],m_max[Z]),rgba,rgba,frames); Gfx::AddDebugLine(Mth::Vector(m_max[X],m_min[Y],m_max[Z]),Mth::Vector(m_max[X],m_max[Y],m_max[Z]),rgba,rgba,frames); Gfx::AddDebugLine(Mth::Vector(m_max[X],m_max[Y],m_max[Z]),Mth::Vector(m_max[X],m_max[Y],m_min[Z]),rgba,rgba,frames); Gfx::AddDebugLine(Mth::Vector(m_max[X],m_max[Y],m_min[Z]),Mth::Vector(m_max[X],m_min[Y],m_min[Z]),rgba,rgba,frames); // lines joining corners Gfx::AddDebugLine(Mth::Vector(m_min[X],m_min[Y],m_min[Z]),Mth::Vector(m_max[X],m_min[Y],m_min[Z]),rgba,rgba,frames); Gfx::AddDebugLine(Mth::Vector(m_min[X],m_min[Y],m_max[Z]),Mth::Vector(m_max[X],m_min[Y],m_max[Z]),rgba,rgba,frames); Gfx::AddDebugLine(Mth::Vector(m_min[X],m_max[Y],m_max[Z]),Mth::Vector(m_max[X],m_max[Y],m_max[Z]),rgba,rgba,frames); Gfx::AddDebugLine(Mth::Vector(m_min[X],m_max[Y],m_min[Z]),Mth::Vector(m_max[X],m_max[Y],m_min[Z]),rgba,rgba,frames); } void CBBox::DebugRender(const Mth::Matrix & transform, uint32 rgba, int frames) const { uint32 rgba_min = 0xFF000080; uint32 rgba_max = 0x0000FF80; Mth::Vector min_yz_1(m_min[X], m_min[Y], m_min[Z]); Mth::Vector min_yz_2(m_min[X], m_min[Y], m_max[Z]); Mth::Vector min_yz_3(m_min[X], m_max[Y], m_max[Z]); Mth::Vector min_yz_4(m_min[X], m_max[Y], m_min[Z]); Mth::Vector max_yz_1(m_max[X], m_min[Y], m_min[Z]); Mth::Vector max_yz_2(m_max[X], m_min[Y], m_max[Z]); Mth::Vector max_yz_3(m_max[X], m_max[Y], m_max[Z]); Mth::Vector max_yz_4(m_max[X], m_max[Y], m_min[Z]); // Transform min_yz_1 = transform.Transform(min_yz_1); min_yz_2 = transform.Transform(min_yz_2); min_yz_3 = transform.Transform(min_yz_3); min_yz_4 = transform.Transform(min_yz_4); max_yz_1 = transform.Transform(max_yz_1); max_yz_2 = transform.Transform(max_yz_2); max_yz_3 = transform.Transform(max_yz_3); max_yz_4 = transform.Transform(max_yz_4); // Min YZ square Gfx::AddDebugLine(min_yz_1, min_yz_2,rgba_min,rgba_min,frames); Gfx::AddDebugLine(min_yz_2, min_yz_3,rgba_min,rgba_min,frames); Gfx::AddDebugLine(min_yz_3, min_yz_4,rgba_min,rgba_min,frames); Gfx::AddDebugLine(min_yz_4, min_yz_1,rgba_min,rgba_min,frames); // Max YZ square Gfx::AddDebugLine(max_yz_1, max_yz_2,rgba_max,rgba_max,frames); Gfx::AddDebugLine(max_yz_2, max_yz_3,rgba_max,rgba_max,frames); Gfx::AddDebugLine(max_yz_3, max_yz_4,rgba_max,rgba_max,frames); Gfx::AddDebugLine(max_yz_4, max_yz_1,rgba_max,rgba_max,frames); // lines joining corners Gfx::AddDebugLine(min_yz_1, max_yz_1,rgba,rgba,frames); Gfx::AddDebugLine(min_yz_2, max_yz_2,rgba,rgba,frames); Gfx::AddDebugLine(min_yz_3, max_yz_3,rgba,rgba,frames); Gfx::AddDebugLine(min_yz_4, max_yz_4,rgba,rgba,frames); } bool CBBox::LineIntersect( const Mth::Line &line, Mth::Vector &point, Mth::Vector &normal ) const { bool start_point_within = Within(line.m_start); bool end_point_within = Within(line.m_end); // Doesn't intersect side if both points within if (start_point_within && end_point_within) { return false; } // Trivial rejection. if ((line.m_start[Y] > m_max[Y]) && (line.m_end[Y] > m_max[Y])) return false; if ((line.m_start[Y] < m_min[Y]) && (line.m_end[Y] < m_min[Y])) return false; if ((line.m_start[X] > m_max[X]) && (line.m_end[X] > m_max[X])) return false; if ((line.m_start[X] < m_min[X]) && (line.m_end[X] < m_min[X])) return false; if ((line.m_start[Z] > m_max[Z]) && (line.m_end[Z] > m_max[Z])) return false; if ((line.m_start[Z] < m_min[Z]) && (line.m_end[Z] < m_min[Z])) return false; float dx = line.m_end[X] - line.m_start[X]; float dy = line.m_end[Y] - line.m_start[Y]; float dz = line.m_end[Z] - line.m_start[Z]; // avoid divide by zeros. if ( !dx ) { dx = ( 0.000001f ); } if ( !dy ) { dy = ( 0.000001f ); } if ( !dz ) { dz = ( 0.000001f ); } // // Garrett: I back-face cull 3 of the sides to reduce the calculations and keep // the collisions down to one side. // Check the max-x face. if (line.m_start[X] > m_max[X] && line.m_end[X] < m_max[X] && (dx < 0.0f)) { // It crosses the plane of the face, so calculate the y & z coords // of the intersection and see if they are in the face, float d=m_max[X] - line.m_start[X]; float y=d*dy/dx + line.m_start[Y]; float z=d*dz/dx + line.m_start[Z]; if (y < m_max[Y] && y > m_min[Y] && z < m_max[Z] && z > m_min[Z]) { // It does collide! point = Mth::Vector(m_max[X], y, z, 1.0f); normal = Mth::Vector(1.0f, 0.0f, 0.0f, 0.0f); return true; } } // Check the min-x face. if (line.m_start[X] < m_min[X] && line.m_end[X] > m_min[X] && (dx > 0.0f)) { // It crosses the plane of the face, so calculate the y & z coords // of the intersection and see if they are in the face, float d=m_min[X] - line.m_start[X]; float y=d*dy/dx + line.m_start[Y]; float z=d*dz/dx + line.m_start[Z]; if (y < m_max[Y] && y > m_min[Y] && z < m_max[Z] && z > m_min[Z]) { // It does collide! point = Mth::Vector(m_min[X], y, z, 1.0f); normal = Mth::Vector(-1.0f, 0.0f, 0.0f, 0.0f); return true; } } // Check the max-y face. if (line.m_start[Y] > m_max[Y] && line.m_end[Y] < m_max[Y] && (dy < 0.0f)) { // It crosses the plane of the face, so calculate the x & z coords // of the intersection and see if they are in the face, float d=m_max[Y] - line.m_start[Y]; float x=d*dx/dy + line.m_start[X]; float z=d*dz/dy + line.m_start[Z]; if (x < m_max[X] && x > m_min[X] && z < m_max[Z] && z > m_min[Z]) { // It does collide! point = Mth::Vector(x, m_max[Y], z, 1.0f); normal = Mth::Vector(0.0f, 1.0f, 0.0f, 0.0f); return true; } } // Check the min-y face. if (line.m_start[Y] < m_min[Y] && line.m_end[Y] > m_min[Y] && (dy > 0.0f)) { // It crosses the plane of the face, so calculate the x & z coords // of the intersection and see if they are in the face, float d=m_min[Y] - line.m_start[Y]; float x=d*dx/dy + line.m_start[X]; float z=d*dz/dy + line.m_start[Z]; if (x < m_max[X] && x > m_min[X] && z < m_max[Z] && z > m_min[Z]) { // It does collide! point = Mth::Vector(x, m_min[Y], z, 1.0f); normal = Mth::Vector(0.0f, -1.0f, 0.0f, 0.0f); return true; } } // Check the max-z face. if (line.m_start[Z] > m_max[Z] && line.m_end[Z] < m_max[Z] && (dz < 0.0f)) { // It crosses the plane of the face, so calculate the x & y coords // of the intersection and see if they are in the face, float d=m_max[Z] - line.m_start[Z]; float x=d*dx/dz + line.m_start[X]; float y=d*dy/dz + line.m_start[Y]; if (x < m_max[X] && x > m_min[X] && y < m_max[Y] && y > m_min[Y]) { // It does collide! point = Mth::Vector(x, y, m_max[Z], 1.0f); normal = Mth::Vector(0.0f, 0.0f, 1.0f, 0.0f); return true; } } // Check the min-z face. if (line.m_start[Z] < m_min[Z] && line.m_end[Z] > m_min[Z] && (dz > 0.0f)) { // It crosses the plane of the face, so calculate the x & y coords // of the intersection and see if they are in the face, float d=m_min[Z] - line.m_start[Z]; float x=d*dx/dz + line.m_start[X]; float y=d*dy/dz + line.m_start[Y]; if (x < m_max[X] && x > m_min[X] && y < m_max[Y] && y > m_min[Y]) { // It does collide! point = Mth::Vector(x, y, m_min[Z], 1.0f); normal = Mth::Vector(0.0f, 0.0f, -1.0f, 0.0f); return true; } } return false; } void CBBox::GetClosestIntersectPoint(const Mth::Vector &pos, Mth::Vector &point) const { uint32 axes_within_flags; int axes_within = WithinAxes(pos, axes_within_flags); if (axes_within == 3) { float closest_dist = -1.0f; int closest_axis = -1; float closest_axis_coord = 0.0f; // Value of the minimum axis coordinate // Find the closest rectangle and move the point to that rectangle to find the intersection // point. for (int axis = X; axis <= Z; axis++) { float min_to_pos_dist = pos[axis] - m_min[axis]; float max_to_pos_dist = m_max[axis] - pos[axis]; Dbg_Assert(min_to_pos_dist >= 0.0f); Dbg_Assert(max_to_pos_dist >= 0.0f); float min_dist; float min_coord; // Figure out the closest distance if (min_to_pos_dist < max_to_pos_dist) { min_dist = min_to_pos_dist; min_coord = m_min[axis]; } else { min_dist = max_to_pos_dist; min_coord = m_max[axis]; } // See if this is the first axis or if this one is closer if ((axis == X) || (min_dist < closest_dist)) { closest_axis = axis; closest_dist = min_dist; closest_axis_coord = min_coord; } } Dbg_Assert((closest_axis >= X) && (closest_axis <= Z)); Dbg_Assert(closest_dist >= 0.0f); // Calc the intersection point point = pos; // Start with position point[closest_axis] = closest_axis_coord; #if 0 static int print_now; if ((print_now++ % 60) == 0) { Dbg_Message("Closest axis %d, (min - max) pos (%f - %f) %f, closest dist %f, closest coord %f", closest_axis, m_min[closest_axis], m_max[closest_axis], pos[closest_axis], closest_dist, closest_axis_coord); } #endif } else { point[W] = 1.0f; // Homogeneous // Go through each axis. If the pos coordinate is between the min and max, use that // coordinate. Otherwise, use the min or max coordinate that is closest. for (int axis = X; axis <= Z; axis++) { if (axes_within_flags & (1 << axis)) { // Within, use pos coord point[axis] = pos[axis]; } else { // Figure out which end we need if (pos[axis] <= m_min[axis]) { point[axis] = m_min[axis]; } else { point[axis] = m_max[axis]; } } } } } } ================================================ FILE: Code/Core/Math/geometry.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: core/math/geometry.h ** ** ** ** Created: 11/29/99 - mjb ** ** ** ** Description: Vector Math Class ** ** ** *****************************************************************************/ #ifndef __CORE_MATH_GEOMETRY_H #define __CORE_MATH_GEOMETRY_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Mth { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /**************************************************************************** * * Class: Vector * * Description: Vector math class * ****************************************************************************/ class Plane; class Line { friend class Plane; public: Line(); Line ( const Vector &start, const Vector &end ); // Line& operator+= ( const Vector& v ); inline void MirrorAboutStart(); inline void FlipDirection(); bool operator== ( const Line& l ) const; bool operator!= ( const Line& l ) const; Line& operator+= ( const Vector& v ); Line& operator-= ( const Vector& v ); float Length() const {return (m_end-m_start).Length();} Vector m_start, m_end; private: }; class Plane { friend class Line; public: Plane(); Plane (const Vector &point, const Vector &normal); Vector m_point, m_normal; private: }; class Rectangle { public: Rectangle ( ); Rectangle ( const Vector& corner, const Vector& first_edge, const Vector& second_edge ); Vector m_corner, m_first_edge, m_second_edge; private: }; /**************************************************************************** * * Class: CBBox * * Description: Axis-aligned Bounding Box * ****************************************************************************/ class CBBox { public: // Constructors CBBox(); CBBox(const Vector &point); CBBox(const Vector &min, const Vector &max); // inline void Set(const Vector &min, const Vector &max); inline void Reset(); // inline const Vector &GetMin() const; inline const Vector &GetMax() const; inline void SetMin(const Vector &min); inline void SetMax(const Vector &max); // void AddPoint(const Vector &point); inline bool Intersect(const CBBox &bbox) const; inline bool CouldIntersect(const Vector &v0, const Vector &v1, const Vector &v2) const; // Intersect w/ triangle inline bool Within(const Vector &point) const; inline bool Within(const CBBox &bbox) const; int WithinAxes(const Vector &point, uint32 &axis_flags) const; // Returns number of axes within and flags for which ones // Checks to see if the line intersects any of the sides bool LineIntersect( const Mth::Line &line, Mth::Vector &point, Mth::Vector &normal ) const; // Finds the closest point on the bounding box to a position void GetClosestIntersectPoint(const Mth::Vector &pos, Mth::Vector &point) const; void DebugRender(uint32 rgba, int frames = 0) const; void DebugRender(const Mth::Matrix & transform, uint32 rgba, int frames = 0) const; private: // Vector m_min, m_max; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ bool LineLineIntersect( Line & l1, Line & l2, Vector *pa, Vector *pb, float *mua, float *mub, bool clamp = true ); /***************************************************************************** ** Inline Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Line::operator== ( const Line& l ) const { return ( m_start == l.m_start) && ( m_end == l.m_end ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Line::operator!= ( const Line& l ) const { return !( *this == l ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Line& Line::operator+= ( const Vector& v ) { m_start += v; m_end += v; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Line& Line::operator-= ( const Vector& v ) { m_start -= v; m_end -= v; return *this; } // A->B becomes B<-A, where A is unchanged inline void Line::MirrorAboutStart() { m_end = m_start - 2.0f * ( m_end - m_start ); } // A->B becores B->A (A and B swap positions) inline void Line::FlipDirection() { Mth::Vector temp = m_end; m_end = m_start; m_start = temp; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void CBBox::Reset() { // m_min = Vector( (float)HUGE_VAL, (float)HUGE_VAL, (float)HUGE_VAL); // m_max = Vector((float)-HUGE_VAL, (float)-HUGE_VAL, (float)-HUGE_VAL); m_min = Vector( 10000000.0f, 10000000.0f, 10000000.0f); m_max = Vector(-10000000.0f, -10000000.0f, -10000000.0f); } inline void CBBox::Set(const Vector &min, const Vector &max) { m_min = min; m_max = max; } inline const Vector &CBBox::GetMin() const { return m_min; } inline const Vector &CBBox::GetMax() const { return m_max; } inline void CBBox::SetMin(const Vector &min) { m_min = min; } inline void CBBox::SetMax(const Vector &max) { m_max = max; } // for intersection test, we do X and Z first, // as we war aer more likely to be intersecting in the Y // as everyhting is about the same height // so this eliminates thigns quicker inline bool CBBox::Intersect(const CBBox &bbox) const { if ((m_min[X] > bbox.m_max[X]) || (bbox.m_min[X] > m_max[X]) || (m_min[Z] > bbox.m_max[Z]) || (bbox.m_min[Z] > m_max[Z]) || (m_min[Y] > bbox.m_max[Y]) || (bbox.m_min[Y] > m_max[Y])) { return false; } return true; } inline bool CBBox::Within(const Vector &point) const { return ((point[X] >= m_min[X]) && (point[Y] >= m_min[Y]) && (point[Z] >= m_min[Z]) && (point[X] <= m_max[X]) && (point[Y] <= m_max[Y]) && (point[Z] <= m_max[Z])); } inline bool CBBox::Within(const CBBox &bbox) const { if ((bbox.m_min[X] >= m_min[X]) && (bbox.m_max[X] <= m_max[X]) && (bbox.m_min[Y] >= m_min[Y]) && (bbox.m_max[Y] <= m_max[Y]) && (bbox.m_min[Z] >= m_min[Z]) && (bbox.m_max[Z] <= m_max[Z])) { return true; } return false; } // Not So Quick & Dirty Intersection w/ Triangle inline bool CBBox::CouldIntersect(const Vector &v0, const Vector &v1, const Vector &v2) const { if ( ( v0[X] > m_max[X] ) && ( v1[X] > m_max[X] ) && ( v2[X] > m_max[X] ) ) return false; if ( ( v0[X] < m_min[X] ) && ( v1[X] < m_min[X] ) && ( v2[X] < m_min[X] ) ) return false; if ( ( v0[Y] > m_max[Y] ) && ( v1[Y] > m_max[Y] ) && ( v2[Y] > m_max[Y] ) ) return false; if ( ( v0[Y] < m_min[Y] ) && ( v1[Y] < m_min[Y] ) && ( v2[Y] < m_min[Y] ) ) return false; if ( ( v0[Z] > m_max[Z] ) && ( v1[Z] > m_max[Z] ) && ( v2[Z] > m_max[Z] ) ) return false; if ( ( v0[Z] < m_min[Z] ) && ( v1[Z] < m_min[Z] ) && ( v2[Z] < m_min[Z] ) ) return false; return true; } } // namespace Mth #endif // __CORE_MATH_GEOMETRY_H ================================================ FILE: Code/Core/Math/math.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: math.cpp ** ** ** ** Created by: 11/24/99 - mjb ** ** ** ** Description: Math Library code ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #ifdef __PLAT_WN32__ #include #endif #ifdef __PLAT_XBOX__ #include #endif /***************************************************************************** ** DBG Information ** *****************************************************************************/ #include #if DEBUGGING_REPLAY_RND #include #include extern int *gReplayTestRndLine; extern uint64 *gReplayTestRndFunc; static int gRndIndex = 0; static int gTestMode = 0; #define MAX_RND_TEST_INDEX 3666 #endif namespace Mth { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ static int RandSeed; static int RandA; static int RandB; static int RandSeed2; static int RandC; static int RandD; void InitialRand(int a) { RandSeed = a; RandA = 314159265; RandB = 178453311; RandSeed2 = a; RandC = 314159265; RandD = 178453311; } #if DEBUGGING_REPLAY_RND static bool gFuckedUp = false; bool RndFuckedUp( void ) { if ( gTestMode != 2 ) return ( false ); return ( gFuckedUp ); } int Rnd_DbgVersion(int n, int line, char *file) { if ( gRndIndex < MAX_RND_TEST_INDEX ) { if ( gTestMode == 1 ) { // record: gReplayTestRndLine[ gRndIndex ] = line; strncpy( ( char * )( &gReplayTestRndFunc[ gRndIndex ] ), file, 8 ); gRndIndex++; } else if ( gTestMode == 2 ) { // compare: if ( line != gReplayTestRndLine[ gRndIndex ] ) { char temp[ 9 ]; strncpy( temp, ( char * )( &gReplayTestRndFunc[ gRndIndex ] ), 8 ); temp[ 8 ] = '\0'; Dbg_Message( "********Rnd Fuckup!********* num %d time %d\ncurrent line %d file %s conflicts with prev line %d file %s", Tmr::GetTime( ), gRndIndex, line, file, gReplayTestRndLine[ gRndIndex ], temp ); gFuckedUp = true; } else { gFuckedUp = false; } gRndIndex++; } } RandSeed=RandSeed*RandA+RandB; RandA = (RandA ^ RandSeed) + (RandSeed>>4); RandB += (RandSeed>>3) - 0x10101010L; return (int)((RandSeed&0xffff) * n)>>16; } void SetRndTestMode( int mode ) { gTestMode = mode; gRndIndex = 0; } #else int Rnd(int n) { RandSeed=RandSeed*RandA+RandB; RandA = (RandA ^ RandSeed) + (RandSeed>>4); RandB += (RandSeed>>3) - 0x10101010L; return (int)((RandSeed&0xffff) * n)>>16; } #endif int Rnd2(int n) { RandSeed2=RandSeed2*RandC+RandD; RandC = (RandC ^ RandSeed2) + (RandSeed2>>4); RandD += (RandSeed2>>3) - 0x10101010L; return (int)((RandSeed2&0xffff) * n)>>16; } // Return a random number in the range +/- n float PlusOrMinus(float n) { float range = (float)(Rnd(10000)); range -= 5000.0f; return n * range / 5000.0f; } #if 0 // 8192 makes Kensinf and sinf match to all 6 decimal places, (when x is in the range -2pi to 2pi) // apart from very occassionally where there might be a .000001 difference. // The table size must be a power of two. #define SINF_NUM_DIVISIONS 8192 static float pSinLookup[SINF_NUM_DIVISIONS+5]; // +5 for good measure, since sometimes we look one beyond the end. void InitSinLookupTable() { // The lookup table covers a whole period (0 to 2pi) of the sin function. // This saves a few if's in the sin function. for (int i=0; i fabsf( delta ) ) { return ( current - fabsf( delta ) ); } return ( target ); } if ( ( target - current ) > fabsf( delta ) ) { return ( current + fabsf( delta ) ); } return ( target ); } int RunFilter( int target, int current, int delta ) { if ( target < current ) { if ( current - target > abs( delta ) ) return ( target ); return ( current - abs( delta ) ); } if ( target - current > abs( delta ) ) return ( target ); return ( current + abs( delta ) ); } // returns atan(y/x) with appropiate sign determinations // although it does not work, as we cant; find atan ..... /* float Atan2 (float y, float x) { if (x == 0.0f) { if (y < 0.0f) return(-PI/2.0f); else return( PI/2.0f); } else { if (x < 0.0f) { if (y < 0.0f) return(atan(y/x)-PI); else return(atan(y/x)+PI); } else { return(atan(y/x)); } } } */ } // namespace Mth ================================================ FILE: Code/Core/Math/math.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: core/math/math.h ** ** ** ** Created: 11/23/99 - mjb ** ** ** ** Description: Math Library ** ** ** *****************************************************************************/ #ifndef __CORE_MATH_MATH_H #define __CORE_MATH_MATH_H /***************************************************************************** ** Includes ** *****************************************************************************/ #ifdef __PLAT_XBOX__ #include // Required for fabsf(). #include #endif #ifdef __PLAT_WN32__ #include // Required for fabsf(). #endif /***************************************************************************** ** Defines ** *****************************************************************************/ #ifdef __USER_MATT__ #define DEBUGGING_REPLAY_RND 0 #else #define DEBUGGING_REPLAY_RND 0 #endif #if DEBUGGING_REPLAY_RND #define Rnd( x ) Rnd_DbgVersion( ( x ), __LINE__, __FILE__ ) #else #define Rnd( x ) Rnd( x ) #endif namespace Mth { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /***************************************************************************** ** Type Definitions ** *****************************************************************************/ /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ void InitialRand(int a); #if DEBUGGING_REPLAY_RND int Rnd_DbgVersion(int n, int line, char *file); void SetRndTestMode( int mode ); bool RndFuckedUp( void ); #else int Rnd(int n); #endif // use for non-deterministic things // especially for CD-timing reliant stuff that would throw off our random number dealy whopper. int Rnd2(int n); float PlusOrMinus(float n); void InitSinLookupTable(); float Kensinf(float x); float Kencosf(float x); float Kenacosf(float x); float Atan2 (float y, float x); // replaces atan2, much faster /***************************************************************************** ** Inline Functions ** *****************************************************************************/ inline int Abs ( int x ) { return ( x>=0?x:-x); } inline float Abs ( float x ) { return float ( fabsf ( x )); } // round down to nearest whole number inline float Whole ( float x ) { return (float) ( (int) ( x )); } // round to nearest whole number inline float Round ( float x ) { return (float) ( (int) ( x + 0.499999f)); } ////////////////////////////////////////////////////////////////////////////////////////// // Calculate the "Parabolic Lerp" value for a set of parameters // (Note: made-up name, by Mick) // // A lerp (Linear intERPalotion) value is always in the range 0.0 to 1.0 // and is used to move one value (A) towards another (B) // usually the lerp value is fixed. // // A new value C is calculated as C = A + lerp * (B - A) // // a "ParaLerp" is a lerp value that varies based on the distance apart of the two values // // The purpose of this function is to be able to smoothly change a value // that really only needs lerping when the values are within a small range // The result of this equation is a parabolic curve that quickly tends // towards lerp = 1.0, as x increases // The valeu "Rate" is the value of x for which lerp half way between Min and 1.0 // Min is the value of lerp when x is 0 inline float ParaLerp(float x, float Rate = 1.0f, float Minimum = 0.0f) { float lerp = Minimum + (1 - 1 / (1 + x / Rate) ) * (1 - Minimum); return lerp; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float ClampMin ( float v, float min ) { if ( v < min ) { return min; } else { return v; } } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float ClampMax ( float v, float max ) { if ( v > max ) { return max; } else { return v; } } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Clamp ( float v, float min, float max ) { return ClampMin ( ClampMax ( v, max ), min ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Lerp ( float a, float b, float value ) { return a + ( b - a ) * value; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float LinearMap ( float a, float b, float value, float value_min, float value_max ) { return Lerp(a, b, (value - value_min) / (value_max - value_min)); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float SmoothStep ( float value ) { // interpolates from zero to one with a zero derivative at the end-points return -2.0f * value * value * ( value - (3.0f / 2.0f) ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float SmoothInterp ( float a, float b, float value ) { return Lerp(a, b, SmoothStep(value)); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float SmoothMap ( float a, float b, float value, float value_min, float value_max ) { return Lerp(a, b, SmoothStep((value - value_min) / (value_max - value_min))); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Max ( float a, float b ) { return ( a > b ) ? a : b; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Min ( float a, float b ) { return ( a < b ) ? a : b; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Sgn ( float x ) { if ( x < 0.f ) { return -1.0f; } else { return 1.0f; } } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Sqr ( float x ) { return ( x * x ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Swap ( float& a, float& b ) { float t = a; a = b; b = t; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Equal ( const float a, const float b, const float perc ) { return ( Abs ( a - b ) <= (( Abs ( a ) + Abs ( b )) * perc )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline sint Max ( sint a, sint b ) { return ( a > b ) ? a : b; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline sint Min ( sint a, sint b ) { return ( a < b ) ? a : b; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Max3 ( float a, float b, float c ) { return Max(a, Max(b,c)); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Min3 ( float a, float b, float c ) { return Min(a, Min(b,c)); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Determinant ( float a, float b, float c, float d ) { return ( a * d ) - ( b * c ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Determinant3 ( float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3 ) { return a1 * (b2 * c3 - b3 * c2) - b1 * (a2 * c3 - a3 * c2) + c1 * (a2 * b3 - a3 * b2); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline int WeightedRnd ( float min_val, float max_val ) { // This random function uses the West distribution // to return values that favor the midpoint. // For example, WeightedRnd( 10, 20 ) returns: // Bucket[10] = 2.12 percent // Bucket[11] = 5.72 percent // Bucket[12] = 9.44 percent // Bucket[13] = 13.87 percent // Bucket[14] = 18.16 percent // Bucket[15] = 15.70 percent // Bucket[16] = 14.52 percent // Bucket[17] = 10.16 percent // Bucket[18] = 6.05 percent // Bucket[19] = 2.26 percent // Bucket[20] = 2.00 percent float range = ( max_val - min_val ) / 2; float midpoint = min_val + range; float random_val = range - (float)sqrtf((float)Rnd( (int)(range * range) ) ); // negate it half of the time if ( Rnd(2) ) { random_val = -random_val; } return (int)( random_val + midpoint ); } /******************************************************************/ /* */ /* */ /******************************************************************/ // Trig const float EPSILON = 0.000001f; const float PI = 3.141592654f; /******************************************************************/ /* */ /* */ /******************************************************************/ inline float DegToRad ( float degrees ) { return degrees * ( PI / 180.0f); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float RadToDeg ( float radians ) { return radians * ( 180.0f / PI ); } int RunFilter( int target, int current, int delta ); float FRunFilter( float target, float current, float delta ); /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Mth #endif // __CORE_MATH_MATH_H ================================================ FILE: Code/Core/Math/matrix.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: matrix.cpp ** ** ** ** Created by: 10/03/2000 - mjb ** ** ** ** Description: Matrix Math Library code ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Mth { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ Matrix::Matrix( float p, float h, float r) { // Algorithm from http://www.flipcode.com/documents/matrfaq.html#Q37 float A,B,C,D,E,F,AD,BD; A = cosf( p ); B = sinf( p ); C = cosf( h ); D = sinf( h ); E = cosf( r ); F = sinf( r ); AD = A * D; BD = B * D; row[0][0] = C * E; row[0][1] = -C * F; row[0][2] = -D; row[1][0] = -BD * E + A * F; row[1][1] = BD * F + A * E; row[1][2] = -B * C; row[2][0] = AD * E + B * F; row[2][1] = -AD * F + B * E; row[2][2] = A * C; row[0][3] = row[1][3] = row[2][3] = row[3][0] = row[3][1] = row[3][2] = 0.0f; row[3][3] = 1.0f; } #ifdef __PLAT_NGPS__ void xsceVu0MulMatrix(Mth::Matrix* m0, Mth::Matrix* m1, const Mth::Matrix* m2) { asm __volatile__(" lqc2 vf4,0x0(%2) lqc2 vf5,0x10(%2) lqc2 vf6,0x20(%2) lqc2 vf7,0x30(%2) li $7,4 _loopMulMatrix: lqc2 vf8,0x0(%1) vmulax.xyzw ACC, vf4,vf8 vmadday.xyzw ACC, vf5,vf8 vmaddaz.xyzw ACC, vf6,vf8 vmaddw.xyzw vf9,vf7,vf8 sqc2 vf9,0x0(%0) addi $7,-1 addi %1,0x10 addi %0,0x10 bne $0,$7,_loopMulMatrix ": : "r" (m0), "r" (m2), "r" (m1) : "$7"); } #endif /******************************************************************/ /* */ /* */ /******************************************************************/ // Ken: Faster version of atan2f, using the same algorithm used by VU1 // (Coeefficients got from the VU manual, from documentation of EATANxy function, p130 // SPEEDOPT: This could probably be made even faster using a less accurate approx with few coeffs. float katan(float y, float x) { if (fabs(y)>fabs(x)) { // The approximation only works for y<=x, bummer! return atan2f(y,x); } register bool x_negative=false; register bool y_negative=false; if (x<0.0f) { x=-x; x_negative=true; } if (y<=-0.0f) { y=-y; y_negative=true; } // Use the non-ratio version, because the ratio version goes // all innaccurate for some values ... don't know why ... y=y/x; register float t1=0.999999344348907f; register float t2=-0.333298563957214f; register float t3=0.199465364217758f; register float t4=-0.139085337519646f; register float t5=0.096420042216778f; register float t6=-0.055909886956215f; register float t7=0.021861229091883f; register float t8=-0.004054057877511f; register float t=(y-1.0f)/(y+1.0f); register float tt=t*t; register float s=t8*t; s=s*tt+t7*t; s=s*tt+t6*t; s=s*tt+t5*t; s=s*tt+t4*t; s=s*tt+t3*t; s=s*tt+t2*t; s=s*tt+t1*t; if (x_negative) { if (y_negative) { return s+0.785398185253143f-3.141592653589793f; } else { return 3.141592653589793f-0.785398185253143f-s; } } else { if (y_negative) { return -0.785398185253143f-s; } else { return s+0.785398185253143f; } } } void Matrix::GetEulers( Vector& euler ) const { // Algorithm from http://www.flipcode.com/documents/matrfaq.html#Q37 float C, D; float tr_x, tr_y; #ifdef __PLAT_NGC__ float ang = row[0][2]; if ( ang > 1.0f ) ang = 1.0f; if ( ang < -1.0f ) ang = -1.0f; euler[Y] = D = -asinf( ang ); /* Calculate Y-axis angle */ #else float ang = row[0][2]; if ( ang > 1.0f ) ang = 1.0f; if ( ang < -1.0f ) ang = -1.0f; euler[Y] = D = -asinf( ang ); /* Calculate Y-axis angle */ // euler[Y] = D = -asinf( row[0][2]); /* Calculate Y-axis angle */ #endif // __PLAT_NGC__ C = cosf( euler[Y] ); if( fabsf( C ) > 0.005f ) /* Gimball lock? */ { tr_x = row[2][2] / C; /* No, so get X-axis angle */ tr_y = -row[1][2] / C; euler[X] = katan( tr_y, tr_x ); tr_x = row[0][0] / C; /* Get Z-axis angle */ tr_y = -row[0][1] / C; euler[Z] = katan( tr_y, tr_x ); } else /* Gimball lock has occurred */ { euler[X] = 0; /* Set X-axis angle to zero */ tr_x = row[1][1]; /* And calculate Z-axis angle */ tr_y = row[1][0]; euler[Z] = katan( tr_y, tr_x ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void Matrix::PrintContents() const { printf( "-----------------\n" ); printf( "%f %f %f %f\n", row[RIGHT][X], row[RIGHT][Y], row[RIGHT][Z], row[RIGHT][W] ); printf( "%f %f %f %f\n", row[UP][X], row[UP][Y], row[UP][Z], row[UP][W] ); printf( "%f %f %f %f\n", row[AT][X], row[AT][Y], row[AT][Z], row[AT][W] ); printf( "%f %f %f %f\n", row[POS][X], row[POS][Y], row[POS][Z], row[POS][W] ); printf( "-----------------\n" ); } /******************************************************************/ /* */ /* */ /******************************************************************/ /* // Original source of the optimized fromToRoation function #include #define EPSILON 0.000001 #define CROSS(dest, v1, v2){ \ dest[0] = v1[1] * v2[2] - v1[2] * v2[1]; \ dest[1] = v1[2] * v2[0] - v1[0] * v2[2]; \ dest[2] = v1[0] * v2[1] - v1[1] * v2[0];} #define DOT(v1, v2) (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) #define SUB(dest, v1, v2){ \ dest[0] = v1[0] - v2[0]; \ dest[1] = v1[1] - v2[1]; \ dest[2] = v1[2] - v2[2];} // // * A function for creating a rotation matrix that rotates a vector called // * "from" into another vector called "to". // * Input : from[3], to[3] which both must be *normalized* non-zero vectors // * Output: mtx[3][3] -- a 3x3 matrix in colum-major form // * Authors: Tomas Mller, John Hughes 1999 // void fromToRotation(float from[3], float to[3], float mtx[3][3]) { float v[3]; float e, h, f; CROSS(v, from, to); e = DOT(from, to); f = (e < 0)? -e:e; if (f > 1.0 - EPSILON) // "from" and "to"-vector almost parallel { float u[3], v[3]; // temporary storage vectors float x[3]; // vector most nearly orthogonal to "from" float c1, c2, c3; // coefficients for later use int i, j; x[0] = (from[0] > 0.0)? from[0] : -from[0]; x[1] = (from[1] > 0.0)? from[1] : -from[1]; x[2] = (from[2] > 0.0)? from[2] : -from[2]; if (x[0] < x[1]) { if (x[0] < x[2]) { x[0] = 1.0; x[1] = x[2] = 0.0; } else { x[2] = 1.0; x[0] = x[1] = 0.0; } } else { if (x[1] < x[2]) { x[1] = 1.0; x[0] = x[2] = 0.0; } else { x[2] = 1.0; x[0] = x[1] = 0.0; } } u[0] = x[0] - from[0]; u[1] = x[1] - from[1]; u[2] = x[2] - from[2]; v[0] = x[0] - to[0]; v[1] = x[1] - to[1]; v[2] = x[2] - to[2]; c1 = 2.0 / DOT(u, u); c2 = 2.0 / DOT(v, v); c3 = c1 * c2 * DOT(u, v); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { mtx[i][j] = - c1 * u[i] * u[j] - c2 * v[i] * v[j] + c3 * v[i] * u[j]; } mtx[i][i] += 1.0; } } else // the most common case, unless "from"="to", or "from"=-"to" { #if 0 // unoptimized version - a good compiler will optimize this. h = (1.0 - e)/DOT(v, v); mtx[0][0] = e + h * v[0] * v[0]; mtx[0][1] = h * v[0] * v[1] - v[2]; mtx[0][2] = h * v[0] * v[2] + v[1]; mtx[1][0] = h * v[0] * v[1] + v[2]; mtx[1][1] = e + h * v[1] * v[1]; mtx[1][2] = h * v[1] * v[2] - v[0]; mtx[2][0] = h * v[0] * v[2] - v[1]; mtx[2][1] = h * v[1] * v[2] + v[0]; mtx[2][2] = e + h * v[2] * v[2]; #else // ...otherwise use this hand optimized version (9 mults less) float hvx, hvz, hvxy, hvxz, hvyz; h = (1.0 - e)/DOT(v, v); hvx = h * v[0]; hvz = h * v[2]; hvxy = hvx * v[1]; hvxz = hvx * v[2]; hvyz = hvz * v[1]; mtx[0][0] = e + hvx * v[0]; mtx[0][1] = hvxy - v[2]; mtx[0][2] = hvxz + v[1]; mtx[1][0] = hvxy + v[2]; mtx[1][1] = e + h * v[1] * v[1]; mtx[1][2] = hvyz - v[0]; mtx[2][0] = hvxz - v[1]; mtx[2][1] = hvyz + v[0]; mtx[2][2] = e + hvz * v[2]; #endif } } */ #define FROMTO_EPSILON 0.000001f //////////////////////////////////////////////////////////////////////////////// //Matrix& CreateFromToMatrix( Matrix &mtx, Vector from, Vector to ) // // * A function for creating a rotation matrix that rotates a vector called // * "from" into another vector called "to". // * Input : from[3], to[3] which both must be *normalized* non-zero vectors // * Output: mtx[3][3] -- a 3x3 matrix in colum-major form // * Authors: Tomas Mller, John Hughes 1999 // // // Micknote .. on testing this, it seems like it produces the inverse of what we want // possibly just need to swap the from/to vectors, but for now I'm just inverting it // at the end of the function, before it returns the matrix // // Sample usage: // Mth::Matrix rotate; // Mth::CreateFromToMatrix(rotate,m_matrix[Y],m_normal); // m_matrix *= rotate; // // will rotate m_matrix, so the Y component is coincident with m_normal // (this is used to align the skater to a surface, given the surface normal) // Matrix& CreateFromToMatrix( Matrix &mtx, Vector from, Vector to ) { Vector cross; float e, h, f; mtx.Ident(); // clean up W rows and cols cross = CrossProduct(from,to); // CROSS(v, from, to); e = DotProduct(from,to); // e = DOT(from, to); f = (e < 0.0f)? -e:e; if (f > 1.0f - FROMTO_EPSILON) // "from" and "to"-vector almost parallel { Vector u, v; // temporary storage vectors Vector x; // vector most nearly orthogonal to "from" float c1, c2, c3; // coefficients for later use int i, j; x[0] = (from[0] >= 0.0f)? from[0] : -from[0]; x[1] = (from[1] >= 0.0f)? from[1] : -from[1]; x[2] = (from[2] >= 0.0f)? from[2] : -from[2]; if (x[0] < x[1]) { if (x[0] < x[2]) { x[0] = 1.0f; x[1] = x[2] = 0.0f; } else { x[2] = 1.0f; x[0] = x[1] = 0.0f; } } else { if (x[1] < x[2]) { x[1] = 1.0f; x[0] = x[2] = 0.0f; } else { x[2] = 1.0f; x[0] = x[1] = 0.0f; } } u[0] = x[0] - from[0]; u[1] = x[1] - from[1]; u[2] = x[2] - from[2]; v[0] = x[0] - to[0]; v[1] = x[1] - to[1]; v[2] = x[2] - to[2]; c1 = 2.0f / DotProduct(u, u); c2 = 2.0f / DotProduct(v, v); c3 = c1 * c2 * DotProduct(u, v); for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { mtx[i][j] = - c1 * u[i] * u[j] - c2 * v[i] * v[j] + c3 * v[i] * u[j]; } mtx[i][i] += 1.0f; } } else // the most common case, unless "from"="to", or "from"=-"to" { #if 0 // unoptimized version - a good compiler will optimize this. h = (1.0f - e)/DOT(v, v); mtx[0][0] = e + h * v[0] * v[0]; mtx[0][1] = h * v[0] * v[1] - v[2]; mtx[0][2] = h * v[0] * v[2] + v[1]; mtx[1][0] = h * v[0] * v[1] + v[2]; mtx[1][1] = e + h * v[1] * v[1]; mtx[1][2] = h * v[1] * v[2] - v[0]; mtx[2][0] = h * v[0] * v[2] - v[1]; mtx[2][1] = h * v[1] * v[2] + v[0]; mtx[2][2] = e + h * v[2] * v[2]; #else // ...otherwise use this hand optimized version (9 mults less) float hvx, hvz, hvxy, hvxz, hvyz; h = (1.0f - e)/DotProduct(cross, cross); hvx = h * cross[0]; hvz = h * cross[2]; hvxy = hvx * cross[1]; hvxz = hvx * cross[2]; hvyz = hvz * cross[1]; mtx[0][0] = e + hvx * cross[0]; mtx[0][1] = hvxy - cross[2]; mtx[0][2] = hvxz + cross[1]; mtx[1][0] = hvxy + cross[2]; mtx[1][1] = e + h * cross[1] * cross[1]; mtx[1][2] = hvyz - cross[0]; mtx[2][0] = hvxz - cross[1]; mtx[2][1] = hvyz + cross[0]; mtx[2][2] = e + hvz * cross[2]; #endif } mtx.Invert(); // Micknote: not sure why we need to invert it, but let it be for now return mtx; } Matrix& CreateRotateMatrix ( Matrix& mat, const Vector& axis, const float angle ) { Vector unitAxis = axis; unitAxis.Normalize(); float oneMinusCosine = 1.0f - cosf( angle ); Vector leading; leading[X] = 1.0f - ( unitAxis[X] * unitAxis[X] ); leading[Y] = 1.0f - ( unitAxis[Y] * unitAxis[Y] ); leading[Z] = 1.0f - ( unitAxis[Z] * unitAxis[Z] ); leading *= oneMinusCosine; Vector crossed; crossed[X] = ( unitAxis[Y] * unitAxis[Z] ); crossed[Y] = ( unitAxis[Z] * unitAxis[X] ); crossed[Z] = ( unitAxis[X] * unitAxis[Y] ); crossed *= oneMinusCosine; unitAxis *= sinf( angle ); mat[RIGHT][X] = 1.0f - leading[X]; mat[RIGHT][Y] = crossed[Z] + unitAxis[Z]; mat[RIGHT][Z] = crossed[Y] - unitAxis[Y]; mat[RIGHT][W] = 0.0f; mat[UP][X] = crossed[Z] - unitAxis[Z]; mat[UP][Y] = 1.0f - leading[Y]; mat[UP][Z] = crossed[X] + unitAxis[X]; mat[UP][W] = 0.0f; mat[AT][X] = crossed[Y] + unitAxis[Y]; mat[AT][Y] = crossed[X] - unitAxis[X]; mat[AT][Z] = 1.0f - leading[Z]; mat[AT][W] = 0.0f; mat[POS][X] = 0.0f; mat[POS][Y] = 0.0f; mat[POS][Z] = 0.0f; mat[POS][W] = 1.0f; return mat; } /******************************************************************/ /* */ /* */ /******************************************************************/ Matrix& CreateRotateXMatrix( Matrix& rotX, float angle ) { float s = sinf( angle ); float c = cosf( angle ); rotX[RIGHT][X] = 1.0f; rotX[RIGHT][Y] = 0.0f; rotX[RIGHT][Z] = 0.0f; rotX[RIGHT][W] = 0.0f; rotX[UP][X] = 0.0f; rotX[UP][Y] = c; rotX[UP][Z] = s; rotX[UP][W] = 0.0f; rotX[AT][X] = 0.0f; rotX[AT][Y] = -s; rotX[AT][Z] = c; rotX[AT][W] = 0.0f; rotX[POS][X] = 0.0f; rotX[POS][Y] = 0.0f; rotX[POS][Z] = 0.0f; rotX[POS][W] = 1.0f; return rotX; } /******************************************************************/ /* */ /* */ /******************************************************************/ Matrix& CreateRotateYMatrix( Matrix& rotY, float angle ) { float s = sinf( angle ); float c = cosf( angle ); rotY[RIGHT][X] = c; rotY[RIGHT][Y] = 0.0f; rotY[RIGHT][Z] = -s; rotY[RIGHT][W] = 0.0f; rotY[UP][X] = 0.0f; rotY[UP][Y] = 1.0f; rotY[UP][Z] = 0.0f; rotY[UP][W] = 0.0f; rotY[AT][X] = s; rotY[AT][Y] = 0.0f; rotY[AT][Z] = c; rotY[AT][W] = 0.0f; rotY[POS][X] = 0.0f; rotY[POS][Y] = 0.0f; rotY[POS][Z] = 0.0f; rotY[POS][W] = 1.0f; return rotY; } /******************************************************************/ /* */ /* */ /******************************************************************/ Matrix& CreateRotateZMatrix( Matrix& rotZ, float angle ) { float s = sinf( angle ); float c = cosf( angle ); rotZ[RIGHT][X] = c; rotZ[RIGHT][Y] = s; rotZ[RIGHT][Z] = 0.0f; rotZ[RIGHT][W] = 0.0f; rotZ[UP][X] = -s; rotZ[UP][Y] = c; rotZ[UP][Z] = 0; rotZ[UP][W] = 0.0f; rotZ[AT][X] = 0.0f; rotZ[AT][Y] = 0.0f; rotZ[AT][Z] = 1.0f; rotZ[AT][W] = 0.0f; rotZ[POS][X] = 0.0f; rotZ[POS][Y] = 0.0f; rotZ[POS][Z] = 0.0f; rotZ[POS][W] = 1.0f; return rotZ; } /******************************************************************/ /* */ /* */ /******************************************************************/ Matrix& CreateRotateMatrix( Matrix &rot, int axis, const float angle ) { int a = (axis + 1) % (Z + 1); int b = (axis + 2) % (Z + 1); float s = sinf( angle ); float c = cosf( angle ); rot.Ident(); rot[a][a] = c; rot[a][b] = s; rot[b][a] = -s; rot[b][b] = c; return rot; } /******************************************************************/ /* */ /* */ /******************************************************************/ Matrix& CreateMatrixLookAt( Matrix& mat, const Vector& pos, const Vector& lookat, const Vector& up ) { // Create Z axis Mth::Vector z_axis(lookat - pos); z_axis.Normalize(); //mat[Z] = z_axis; // Create X from Z and up (up assumed to be normalized) Mth::Vector x_axis(Mth::CrossProduct(z_axis, up)); //mat[X] = x_axis; // Create Y from Z and X Mth::Vector y_axis(Mth::CrossProduct(x_axis, z_axis)); //mat[Y] = y_axis; // Orientation needs transposing (since we want the inverse orientation) //mat.Transpose(); mat[X][X] = x_axis[X]; mat[Y][X] = x_axis[Y]; mat[Z][X] = x_axis[Z]; mat[X][Y] = y_axis[X]; mat[Y][Y] = y_axis[Y]; mat[Z][Y] = y_axis[Z]; mat[X][Z] = z_axis[X]; mat[Y][Z] = z_axis[Y]; mat[Z][Z] = z_axis[Z]; // These may not be zero, but should be mat[X][W] = 0.0f; mat[Y][W] = 0.0f; mat[Z][W] = 0.0f; // Create inverse translation mat[POS][X] = -Mth::DotProduct(x_axis, pos); mat[POS][Y] = -Mth::DotProduct(y_axis, pos); mat[POS][Z] = -Mth::DotProduct(z_axis, pos); mat[POS][W] = 1.0f; //Dbg_Message("LookAt matrix:"); //Dbg_Message("[ %f, %f, %f, %f ]", mat[X][X], mat[X][Y], mat[X][Z], mat[X][W]); //Dbg_Message("[ %f, %f, %f, %f ]", mat[Y][X], mat[Y][Y], mat[Y][Z], mat[Y][W]); //Dbg_Message("[ %f, %f, %f, %f ]", mat[Z][X], mat[Z][Y], mat[Z][Z], mat[Z][W]); //Dbg_Message("[ %f, %f, %f, %f ]", mat[W][X], mat[W][Y], mat[W][Z], mat[W][W]); return mat; } /******************************************************************/ /* */ /* */ /******************************************************************/ Matrix& CreateMatrixOrtho( Matrix& mat, float width, float height, float f_near, float f_far ) { mat[X] = Mth::Vector(2.0f / width, 0.0f, 0.0f, 0.0f); mat[Y] = Mth::Vector(0.0f, 2.0f / height, 0.0f, 0.0f); mat[Z] = Mth::Vector(0.0f, 0.0f, 2.0f / (f_far - f_near), 0.0f); mat[W] = Mth::Vector(0.0f, 0.0f, -(f_far + f_near) / (f_far - f_near), 1.0f); //Dbg_Message("Orthographic matrix:"); //Dbg_Message("[ %f, %f, %f, %f ]", mat[X][X], mat[X][Y], mat[X][Z], mat[X][W]); //Dbg_Message("[ %f, %f, %f, %f ]", mat[Y][X], mat[Y][Y], mat[Y][Z], mat[Y][W]); //Dbg_Message("[ %f, %f, %f, %f ]", mat[Z][X], mat[Z][Y], mat[Z][Z], mat[Z][W]); //Dbg_Message("[ %f, %f, %f, %f ]", mat[W][X], mat[W][Y], mat[W][Z], mat[W][W]); return mat; } // Orthonormalize a matrix keeping one row r0 unchanged // (Mick accepts responsibility for this). Matrix& Matrix::OrthoNormalizeAbout(int r0) { int r1, r2; r1 = r0+1; if (r1 == 3) { r1 = 0; } r2 = r1+1; if (r2 == 3) { r2 = 0; } // Now regarding Rows r0,r1,r2 // r0 = r1 x r2 (implied) // r1 = r2 x r0 (calculate this) // r2 = r0 x r1 (and this) // // We need to recalculate rows r1 and r2 using the above cross produces // however if r0 is close to r2, then the calculation of r1 will be off // so it's better to calulate r2 and then r1 // the first pair to do will be whichever has the smaller dot product if (Abs(DotProduct(*(Vector*)(row[r2]),*(Vector*)(row[r0]))) < Abs(DotProduct(*(Vector*)(row[r0]),*(Vector*)(row[r1])))) { *(Vector*)(row[r1]) = Mth::CrossProduct(*(Vector*)(row[r2]),*(Vector*)(row[r0])); (*(Vector*)(row[r1])).Normalize(); *(Vector*)(row[r2]) = Mth::CrossProduct(*(Vector*)(row[r0]),*(Vector*)(row[r1])); (*(Vector*)(row[r2])).Normalize(); } else { *(Vector*)(row[r2]) = Mth::CrossProduct(*(Vector*)(row[r0]),*(Vector*)(row[r1])); (*(Vector*)(row[r2])).Normalize(); *(Vector*)(row[r1]) = Mth::CrossProduct(*(Vector*)(row[r2]),*(Vector*)(row[r0])); (*(Vector*)(row[r1])).Normalize(); } return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ void Matrix::GetRotationAxisAndAngle( Vector* pAxis, float* pRadians ) { // Given a transform matrix (end = start * xform) // this will return its rotation axis and angle Dbg_Assert( pAxis ); Dbg_Assert( pRadians ); float nTwoSinTheta, nTwoCosTheta; Mth::Vector vTwoSinThetaAxis; nTwoCosTheta = row[Mth::RIGHT][X] + row[Mth::UP][Y] + row[Mth::AT][Z] - 1.0f; vTwoSinThetaAxis[X] = row[Mth::UP][Z] - row[Mth::AT][Y]; vTwoSinThetaAxis[Y] = row[Mth::AT][X] - row[Mth::RIGHT][Z]; vTwoSinThetaAxis[Z] = row[Mth::RIGHT][Y] - row[Mth::UP][X]; vTwoSinThetaAxis[W] = 1.0f; // Gary: There used to be a magic patch added by Dave // (basically negating the axis) which made it work with // the RW-based Slerp object. This doesn't seem to be // necessary any more, but I'm going to leave it // in the code just in case our Slerp stops working... // vTwoSinThetaAxis[X] = row[Mth::AT][Y] - row[Mth::UP][Z]; // vTwoSinThetaAxis[Y] = row[Mth::RIGHT][Z] - row[Mth::AT][X]; // vTwoSinThetaAxis[Z] = row[Mth::UP][X] - row[Mth::RIGHT][Y]; // vTwoSinThetaAxis[W] = 1.0f; nTwoSinTheta = vTwoSinThetaAxis.Length(); if (nTwoSinTheta > 0.0f) { float recipLength = (1.0f / (nTwoSinTheta)); *pAxis = vTwoSinThetaAxis; pAxis->Scale( recipLength ); } else { pAxis->Set( 0.0f, 0.0f, 0.0f ); } (*pRadians) = (float)atan2(nTwoSinTheta, nTwoCosTheta); if ((nTwoSinTheta <= 0.01f) && (nTwoCosTheta <= 0.0f)) { /* * sin theta is 0; cos theta is -1; theta is 180 degrees * vTwoSinThetaAxis was degenerate * axis will have to be found another way. */ //Vector vTwoSinThetaAxis; /* * Matrix is: * [ [ 2 a_x^2 - 1, 2 a_x a_y, 2 a_x a_z, 0 ] * [ 2 a_x a_y, 2 a_y^2 - 1, 2 a_y a_z, 0 ] * [ 2 a_x a_z, 2 a_y a_z, 2 a_z^2 - 1, 0 ] * [ 0, 0, 0, 1 ] ] * Build axis scaled by 4 * component of maximum absolute value */ if (row[Mth::RIGHT][X] > row[Mth::UP][Y]) { if (row[Mth::RIGHT][X] > row[Mth::AT][Z]) { vTwoSinThetaAxis[X] = 1.0f + row[Mth::RIGHT][X]; vTwoSinThetaAxis[X] = vTwoSinThetaAxis[X] + vTwoSinThetaAxis[X]; vTwoSinThetaAxis[Y] = row[Mth::RIGHT][Y] + row[Mth::UP][X]; vTwoSinThetaAxis[Z] = row[Mth::RIGHT][Z] + row[Mth::AT][X]; } else { vTwoSinThetaAxis[Z] = 1.0f + row[Mth::AT][Z]; vTwoSinThetaAxis[Z] = vTwoSinThetaAxis[Z] + vTwoSinThetaAxis[Z]; vTwoSinThetaAxis[X] = row[Mth::AT][X] + row[Mth::RIGHT][Z]; vTwoSinThetaAxis[Y] = row[Mth::AT][Y] + row[Mth::UP][Z]; } } else { if (row[Mth::UP][Y] > row[Mth::AT][Z]) { vTwoSinThetaAxis[Y] = 1.0f + row[Mth::UP][Y]; vTwoSinThetaAxis[Y] = vTwoSinThetaAxis[Y] + vTwoSinThetaAxis[Y]; vTwoSinThetaAxis[Z] = row[Mth::UP][Z] + row[Mth::AT][Y]; vTwoSinThetaAxis[X] = row[Mth::UP][X] + row[Mth::RIGHT][Y]; } else { vTwoSinThetaAxis[Z] = 1.0f + row[Mth::AT][Z]; vTwoSinThetaAxis[Z] = vTwoSinThetaAxis[Z] + vTwoSinThetaAxis[Z]; vTwoSinThetaAxis[X] = row[Mth::AT][X] + row[Mth::RIGHT][Z]; vTwoSinThetaAxis[Y] = row[Mth::AT][Y] + row[Mth::UP][Z]; } } /* * and normalize the axis */ *pAxis = vTwoSinThetaAxis; pAxis->Normalize(); } } /******************************************************************/ /* */ /* */ /******************************************************************/ void Matrix::RotateLocal( const Vector &rot ) { if ( rot[ X ] ) { RotateXLocal( rot[ X ] ); } if ( rot[ Y ] ) { RotateYLocal( rot[ Y ] ); } if ( rot[ Z ] ) { RotateZLocal( rot[ Z ] ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool Matrix::PatchOrthogonality ( ) { // Given a matrix m which is assumed to be orthonormal, check to see if it is, and if not then fix it up // returns true if any patching was done float lx,ly,lz; lx = (*(Vector*)(row[X])).Length(); ly = (*(Vector*)(row[Y])).Length(); lz = (*(Vector*)(row[Z])).Length(); const float near1 = 0.99; if (lx < near1) { if (ly < near1) { if (lz < near1) { // everything collapsing to a point!!! // just reset to the identity matrix Ident(); } else { // x and y have collapsed, but Z is okay Ident(); } } else { if (lz < near1) { // x and z have collapsed, y is okay (most likely situation) Ident(); } else { // just x has collapsed, y and z are okay *(Vector*)(row[X]) = Mth::CrossProduct(*(Vector*)(row[Y]),*(Vector*)(row[Z])); (*(Vector*)(row[X])).Normalize(); } } } else { if (ly < near1) { if (lz < near1) { // y and z collapsed, x is okay Ident(); } else { // y has collapsed, x and Z are okay *(Vector*)(row[Y]) = Mth::CrossProduct(*(Vector*)(row[Z]),*(Vector*)(row[X])); (*(Vector*)(row[Y])).Normalize(); } } else { if (lz < near1) { // just z has collapsed, x and y is okay *(Vector*)(row[Z]) = Mth::CrossProduct(*(Vector*)(row[X]),*(Vector*)(row[Y])); (*(Vector*)(row[Z])).Normalize(); } else { // nothing has collapsed return false; } } } return true; } } // namespace Mth ================================================ FILE: Code/Core/Math/matrix.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: core/math/Matrix.h ** ** ** ** Created: 11/29/99 - mjb ** ** ** ** Description: 4x4 Matrix Math Class ** ** ** *****************************************************************************/ #ifndef __CORE_MATH_MATRIX_H #define __CORE_MATH_MATRIX_H /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Mth { enum { RIGHT, UP, AT, POS }; /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /**************************************************************************** * * Class: Matrix * * Description: 4x4 Matrix math class * ****************************************************************************/ class Matrix { public: Matrix ( void ); Matrix ( const Matrix& src ); Matrix ( const Vector& axis, const float angle ); Matrix ( int axis, const float angle ); Matrix ( float p, float h, float r ); // Euler to Matrix Matrix ( const Mth::Quat& orientaion ); Matrix& operator= ( const Matrix& src ); Matrix& operator+= ( const Matrix& n ); Matrix& operator*= ( const Matrix& n ); Matrix& operator*= ( const float f ); bool operator== ( const Matrix& n ) const; bool operator!= ( const Matrix& n ) const; float Determinant ( void ) const; bool IsIdent ( void ) const; Matrix& Ident ( void ); // set to 4x4 identity Matrix& Identity ( void ); // same, but better name Matrix& Zero ( void ); // set to all 0's Matrix& Adjoint ( const Matrix& src ); Matrix& Invert ( const Matrix& src ); // this = Invert ( src ) Matrix& Invert ( void ); // this = Invert ( this ) Matrix& InvertUniform ( void ); Matrix& Transpose ( const Matrix& src ); Matrix& Transpose ( void ); Vector Transform ( const Vector& src ) const; Vector TransformAsPos ( const Vector& src ) const; Vector Rotate ( const Vector& src ) const; Matrix& OrthoNormalize ( const Matrix& src ); // this = OrthoNornalize ( src ) Matrix& OrthoNormalize ( void ); // this = OrthoNormalize ( this ) void RotateLocal ( const Vector& src ); Matrix& Translate ( const Vector& trans ); Matrix& TranslateLocal ( const Vector& trans ); Matrix& Rotate ( const Vector& axis, const float angle ); Matrix& RotateLocal ( const Vector& axis, const float angle ); Matrix& RotateLocal ( int axis, float angle ); Matrix& RotateX ( const float angle ); Matrix& RotateXLocal ( const float angle ); Matrix& RotateY ( const float angle ); Matrix& RotateYLocal ( const float angle ); Matrix& RotateZ ( const float angle ); Matrix& RotateZLocal ( const float angle ); Matrix& Scale ( const Vector& scale ); Matrix& ScaleLocal ( const Vector& scale ); Vector& GetRight ( void ) { return *(Vector*)(&row[RIGHT]); } Vector& GetUp ( void ) { return *(Vector*)(&row[UP]); } Vector& GetAt ( void ) { return *(Vector*)(&row[AT]); } Vector& GetPos ( void ) { return *(Vector*)(&row[POS]); } void SetPos ( const Vector& trans ); Matrix& SetColumn ( sint i, const Vector& v ); Vector GetColumn ( sint i ) const; const Vector& operator[] ( sint i ) const; Vector& operator[] ( sint i ); Matrix& OrthoNormalizeAbout(int r0); void PrintContents() const; void GetEulers( Vector& euler ) const; void GetRotationAxisAndAngle( Vector* pAxis, float* pRadians ); bool PatchOrthogonality ( ); Matrix& SetFromAngles ( const Vector& angles ); private: enum { NUM_ELEMENTS = 4 }; // Vector row[NUM_ELEMENTS]; // Old style, an array of vectors float row[NUM_ELEMENTS][4]; // New style, an array of float, for constructor optimization } nAlign(128); /***************************************************************************** ** Private Inline Functions ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ #ifdef __PLAT_NGPS__ void xsceVu0MulMatrix(Mth::Matrix* m0, Mth::Matrix* m1, const Mth::Matrix* m2); #endif /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ void Swap ( Matrix& a, Matrix& b ); void Slerp ( Matrix& result, const Matrix& s1, const Matrix& s2, float t ); Matrix operator* ( const Matrix& m1, const Matrix& m2 ); ostream& operator<< ( ostream& os, const Matrix& m ); Matrix& CreateRotateMatrix ( Matrix& mat, const Vector& axis, const float angle ); Matrix& CreateRotateMatrix ( Matrix& mat, int axis, const float angle ); Matrix& CreateRotateXMatrix ( Matrix& mat, const float angle ); Matrix& CreateRotateYMatrix ( Matrix& mat, const float angle ); Matrix& CreateRotateZMatrix ( Matrix& mat, const float angle ); Matrix& CreateFromToMatrix( Matrix &mtx, Vector from, Vector to ); Matrix& CreateMatrixLookAt( Matrix& mat, const Vector& pos, const Vector& lookat, const Vector& up ); Matrix& CreateMatrixOrtho( Matrix& mat, float width, float height, float f_near, float f_far ); /***************************************************************************** ** Inline Functions ** *****************************************************************************/ } // namespace Mth #endif // __CORE_MATH_MATRIX_H ================================================ FILE: Code/Core/Math/matrix.inl ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (Mth) ** ** ** ** File name: core/math/Matrix.inl ** ** ** ** Created: 11/23/99 - mjb ** ** ** ** Description: 4x4 Matrix Math Class ** ** ** *****************************************************************************/ #ifndef __CORE_MATH_MATRIX_INL #define __CORE_MATH_MATRIX_INL namespace Mth { /***************************************************************************** ** Private Inline Functions ** *****************************************************************************/ /***************************************************************************** ** Public Inline Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Ident ( void ) { row[X][X] = 1.0f; row[X][Y] = 0.0f; row[X][Z] = 0.0f; row[X][W] = 0.0f; row[Y][X] = 0.0f; row[Y][Y] = 1.0f; row[Y][Z] = 0.0f; row[Y][W] = 0.0f; row[Z][X] = 0.0f; row[Z][Y] = 0.0f; row[Z][Z] = 1.0f; row[Z][W] = 0.0f; row[W][X] = 0.0f; row[W][Y] = 0.0f; row[W][Z] = 0.0f; row[W][W] = 1.0f; return *this; } inline Matrix::Matrix( void ) { #ifdef DEBUG_UNINIT_VECTORS // For debugging uninitialized vectors, we set them to a magic value // and then the vector code should assert when they are accessed // via the [] operator, which casts a row to a vector *((uint32*)(&row[X][X])) = 0x7F800010; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[X][Y])) = 0x7F800011; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[X][Z])) = 0x7F800012; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[X][W])) = 0x7F800013; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[Y][X])) = 0x7F800014; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[Y][Y])) = 0x7F800015; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[Y][Z])) = 0x7F800016; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[Y][W])) = 0x7F800017; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[Z][X])) = 0x7F800018; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[Z][Y])) = 0x7F800019; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[Z][Z])) = 0x7F80001a; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[Z][W])) = 0x7F80001b; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[W][X])) = 0x7F80001c; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[W][Y])) = 0x7F80001d; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[W][Z])) = 0x7F80001e; // Positive NANS (Not A Number - Signaling) *((uint32*)(&row[W][W])) = 0x7F80001f; // Positive NANS (Not A Number - Signaling) #else #ifdef __INITIALIZE_VECTORS__ Ident(); #endif #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix::Matrix( const Vector& axis, const float angle ) { CreateRotateMatrix( *this, axis, angle ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix::Matrix( int axis, const float angle ) { CreateRotateMatrix( *this, axis, angle ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix::Matrix( const Mth::Quat &orientation ) { orientation.GetMatrix(*this); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline const Vector& Matrix::operator[] ( sint i ) const { // Dbg_MsgAssert (( i >= X && i < NUM_ELEMENTS ),( "index out of range" )); return *(Vector*)(row[i]); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Matrix::operator[] ( sint i ) { // Dbg_MsgAssert (( i >= X && i < NUM_ELEMENTS ),( "index out of range" )); return *(Mth::Vector*)(&row[i]); } /* #ifdef __USE_VU0__ inline void asm_copy(void* m0, void* m1) { asm __volatile__(" lq $6,0x0(%1) lq $7,0x10(%1) lq $8,0x20(%1) lq $9,0x30(%1) sq $6,0x0(%0) sq $7,0x10(%0) sq $8,0x20(%0) sq $9,0x30(%0) ": : "r" (m0) , "r" (m1):"$6","$7","$8","$9"); } #endif */ /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::operator= ( const Matrix& src ) { #ifdef __USE_VU0__ // Mick: This actually is faster than the asm_copy method as // the compiler can optimize it better *((uint128 *)&row[0])=*((uint128 *)&src[0]); *((uint128 *)&row[1])=*((uint128 *)&src[1]); *((uint128 *)&row[2])=*((uint128 *)&src[2]); *((uint128 *)&row[3])=*((uint128 *)&src[3]); // asm_copy(this,(void*)&src[0]); #else *(Vector*)row[X] = *(Vector*)src.row[X]; *(Vector*)row[Y] = *(Vector*)src.row[Y]; *(Vector*)row[Z] = *(Vector*)src.row[Z]; *(Vector*)row[W] = *(Vector*)src.row[W]; #endif return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix::Matrix( const Matrix& src ) { *this = src; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Swap ( Matrix& a, Matrix& b ) { Matrix t = a; a = b; b = t; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::operator*= ( const Matrix& n ) { # ifdef __PLAT_XBOX__ D3DXMatrixMultiply((D3DXMATRIX*)this, (D3DXMATRIX*)this, (D3DXMATRIX*)&n ); # else #ifdef __USE_VU0__ sceVu0MulMatrix((sceVu0FVECTOR*)this,(sceVu0FVECTOR*)&n,(sceVu0FVECTOR*)this); # else Vector vec; vec[X] = row[X][X] * n[X][X] + row[X][Y] * n[Y][X] + row[X][Z] * n[Z][X] + row[X][W] * n[W][X]; vec[Y] = row[X][X] * n[X][Y] + row[X][Y] * n[Y][Y] + row[X][Z] * n[Z][Y] + row[X][W] * n[W][Y]; vec[Z] = row[X][X] * n[X][Z] + row[X][Y] * n[Y][Z] + row[X][Z] * n[Z][Z] + row[X][W] * n[W][Z]; vec[W] = row[X][X] * n[X][W] + row[X][Y] * n[Y][W] + row[X][Z] * n[Z][W] + row[X][W] * n[W][W]; *(Vector*)row[X] = vec; vec[X] = row[Y][X] * n[X][X] + row[Y][Y] * n[Y][X] + row[Y][Z] * n[Z][X] + row[Y][W] * n[W][X]; vec[Y] = row[Y][X] * n[X][Y] + row[Y][Y] * n[Y][Y] + row[Y][Z] * n[Z][Y] + row[Y][W] * n[W][Y]; vec[Z] = row[Y][X] * n[X][Z] + row[Y][Y] * n[Y][Z] + row[Y][Z] * n[Z][Z] + row[Y][W] * n[W][Z]; vec[W] = row[Y][X] * n[X][W] + row[Y][Y] * n[Y][W] + row[Y][Z] * n[Z][W] + row[Y][W] * n[W][W]; *(Vector*)row[Y] = vec; vec[X] = row[Z][X] * n[X][X] + row[Z][Y] * n[Y][X] + row[Z][Z] * n[Z][X] + row[Z][W] * n[W][X]; vec[Y] = row[Z][X] * n[X][Y] + row[Z][Y] * n[Y][Y] + row[Z][Z] * n[Z][Y] + row[Z][W] * n[W][Y]; vec[Z] = row[Z][X] * n[X][Z] + row[Z][Y] * n[Y][Z] + row[Z][Z] * n[Z][Z] + row[Z][W] * n[W][Z]; vec[W] = row[Z][X] * n[X][W] + row[Z][Y] * n[Y][W] + row[Z][Z] * n[Z][W] + row[Z][W] * n[W][W]; *(Vector*)row[Z] = vec; vec[X] = row[W][X] * n[X][X] + row[W][Y] * n[Y][X] + row[W][Z] * n[Z][X] + row[W][W] * n[W][X]; vec[Y] = row[W][X] * n[X][Y] + row[W][Y] * n[Y][Y] + row[W][Z] * n[Z][Y] + row[W][W] * n[W][Y]; vec[Z] = row[W][X] * n[X][Z] + row[W][Y] * n[Y][Z] + row[W][Z] * n[Z][Z] + row[W][W] * n[W][Z]; vec[W] = row[W][X] * n[X][W] + row[W][Y] * n[Y][W] + row[W][Z] * n[Z][W] + row[W][W] * n[W][W]; *(Vector*)row[W] = vec; # endif # endif return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix operator* ( const Matrix& m1, const Matrix& m2 ) { Matrix result = m1; result *= m2; return result; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::operator*= ( const float s ) { if ( s != 1.0f ) { *(Vector*)(row[X]) *= s; *(Vector*)(row[Y]) *= s; *(Vector*)(row[Z]) *= s; *(Vector*)(row[W]) *= s; } return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::operator+= ( const Matrix& n ) { row[X][X] += n[X][X]; row[X][Y] += n[X][Y]; row[X][Z] += n[X][Z]; row[X][W] += n[X][W]; row[Y][X] += n[Y][X]; row[Y][Y] += n[Y][Y]; row[Y][Z] += n[Y][Z]; row[Y][W] += n[Y][W]; row[Z][X] += n[Z][X]; row[Z][Y] += n[Z][Y]; row[Z][Z] += n[Z][Z]; row[Z][W] += n[Z][W]; row[W][X] += n[W][X]; row[W][Y] += n[W][Y]; row[W][Z] += n[W][Z]; row[W][W] += n[W][W]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Matrix::operator== ( const Matrix& n ) const { for ( sint i = 0; i < NUM_ELEMENTS; i++ ) { for ( sint j = 0; j < NUM_ELEMENTS; j++ ) { if ( row[i][j] != n[i][j] ) { return false; } } } return true; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Matrix::operator!= ( const Matrix& n ) const { return !( *this == n ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Matrix::Determinant ( void ) const { return row[X][X] * Determinant3 ( row[Y][Y], row[Z][Y], row[W][Y], row[Y][Z], row[Z][Z], row[W][Z], row[Y][W], row[Z][W], row[W][W] ) - row[X][Y] * Determinant3 ( row[Y][X], row[Z][X], row[W][X], row[Y][Z], row[Z][Z], row[W][Z], row[Y][W], row[Z][W], row[W][W] ) + row[X][Z] * Determinant3 ( row[Y][X], row[Z][X], row[W][X], row[Y][Y], row[Z][Y], row[W][Y], row[Y][W], row[Z][W], row[W][W] ) - row[X][W] * Determinant3 ( row[Y][X], row[Z][X], row[W][X], row[Y][Y], row[Z][Y], row[W][Y], row[Y][Z], row[Z][Z], row[W][Z] ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Matrix::IsIdent ( void ) const { return ( row[X][X] == 1.0f && row[X][Y] == 0.0f && row[X][Z] == 0.0f && row[X][W] == 0.0f && row[Y][X] == 0.0f && row[Y][Y] == 1.0f && row[Y][Z] == 0.0f && row[Y][W] == 0.0f && row[Z][X] == 0.0f && row[Z][Y] == 0.0f && row[Z][Z] == 1.0f && row[Z][W] == 0.0f && row[W][X] == 0.0f && row[W][Y] == 0.0f && row[W][Z] == 0.0f && row[W][W] == 1.0f ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Identity ( void ) { row[X][X] = 1.0f; row[X][Y] = 0.0f; row[X][Z] = 0.0f; row[X][W] = 0.0f; row[Y][X] = 0.0f; row[Y][Y] = 1.0f; row[Y][Z] = 0.0f; row[Y][W] = 0.0f; row[Z][X] = 0.0f; row[Z][Y] = 0.0f; row[Z][Z] = 1.0f; row[Z][W] = 0.0f; row[W][X] = 0.0f; row[W][Y] = 0.0f; row[W][Z] = 0.0f; row[W][W] = 1.0f; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Zero ( void ) { row[X][X] = 0.0f; row[X][Y] = 0.0f; row[X][Z] = 0.0f; row[X][W] = 0.0f; row[Y][X] = 0.0f; row[Y][Y] = 0.0f; row[Y][Z] = 0.0f; row[Y][W] = 0.0f; row[Z][X] = 0.0f; row[Z][Y] = 0.0f; row[Z][Z] = 0.0f; row[Z][W] = 0.0f; row[W][X] = 0.0f; row[W][Y] = 0.0f; row[W][Z] = 0.0f; row[W][W] = 0.0f; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Adjoint ( const Matrix& src ) { row[X][X] = Determinant3 ( src[Y][Y], src[Z][Y], src[W][Y], src[Y][Z], src[Z][Z], src[W][Z], src[Y][W], src[Z][W], src[W][W] ); row[Y][X] = -Determinant3 ( src[Y][X], src[Z][X], src[W][X], src[Y][Z], src[Z][Z], src[W][Z], src[Y][W], src[Z][W], src[W][W] ); row[Z][X] = Determinant3 ( src[Y][X], src[Z][X], src[W][X], src[Y][Y], src[Z][Y], src[W][Y], src[Y][W], src[Z][W], src[W][W] ); row[W][X] = -Determinant3 ( src[Y][X], src[Z][X], src[W][X], src[Y][Y], src[Z][Y], src[W][Y], src[Y][Z], src[Z][Z], src[W][Z] ); row[X][Y] = -Determinant3 ( src[X][Y], src[Z][Y], src[W][Y], src[X][Z], src[Z][Z], src[W][Z], src[X][W], src[Z][W], src[W][W] ); row[Y][Y] = Determinant3 ( src[X][X], src[Z][X], src[W][X], src[X][Z], src[Z][Z], src[W][Z], src[X][W], src[Z][W], src[W][W] ); row[Z][Y] = -Determinant3 ( src[X][X], src[Z][X], src[W][X], src[X][Y], src[Z][Y], src[W][Y], src[X][W], src[Z][W], src[W][W] ); row[W][Y] = Determinant3 ( src[X][X], src[Z][X], src[W][X], src[X][Y], src[Z][Y], src[W][Y], src[X][Z], src[Z][Z], src[W][Z] ); row[X][Z] = Determinant3 ( src[X][Y], src[Y][Y], src[W][Y], src[X][Z], src[Y][Z], src[W][Z], src[X][W], src[Y][W], src[W][W] ); row[Y][Z] = -Determinant3 ( src[X][X], src[Y][X], src[W][X], src[X][Z], src[Y][Z], src[W][Z], src[X][W], src[Y][W], src[W][W] ); row[Z][Z] = Determinant3 ( src[X][X], src[Y][X], src[W][X], src[X][Y], src[Y][Y], src[W][Y], src[X][W], src[Y][W], src[W][W] ); row[W][Z] = -Determinant3 ( src[X][X], src[Y][X], src[W][X], src[X][Y], src[Y][Y], src[W][Y], src[X][Z], src[Y][Z], src[W][Z] ); row[X][W] = -Determinant3 ( src[X][Y], src[Y][Y], src[Z][Y], src[X][Z], src[Y][Z], src[Z][Z], src[X][W], src[Y][W], src[Z][W] ); row[Y][W] = Determinant3 ( src[X][X], src[Y][X], src[Z][X], src[X][Z], src[Y][Z], src[Z][Z], src[X][W], src[Y][W], src[Z][W] ); row[Z][W] = -Determinant3 ( src[X][X], src[Y][X], src[Z][X], src[X][Y], src[Y][Y], src[Z][Y], src[X][W], src[Y][W], src[Z][W] ); row[W][W] = Determinant3 ( src[X][X], src[Y][X], src[Z][X], src[X][Y], src[Y][Y], src[Z][Y], src[X][Z], src[Y][Z], src[Z][Z] ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Invert ( const Matrix& src ) { Adjoint (src); float det = src.Determinant(); if ( det != 1.0f ) { *this *= ( 1.0f / det ); } return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Invert ( void ) { Matrix d; d.Invert( *this ); *this = d; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ #ifdef __USE_VU0__ // K: Copied from sceVu0InversMatrix, just tweaked to take one param & be inline. // This is about 18% faster than in C. (1000000 calls take .121 s instead of .148) inline void Vu0Invert(sceVu0FMATRIX m0) { asm __volatile__(" lq $8,0x0000(%0) lq $9,0x0010(%0) lq $10,0x0020(%0) lqc2 vf4,0x0030(%0) vmove.xyzw vf5,vf4 vsub.xyz vf4,vf4,vf4 #vf4.xyz=0; vmove.xyzw vf9,vf4 qmfc2 $11,vf4 #]u pextlw $12,$9,$8 pextuw $13,$9,$8 pextlw $14,$11,$10 pextuw $15,$11,$10 pcpyld $8,$14,$12 pcpyud $9,$12,$14 pcpyld $10,$15,$13 qmtc2 $8,vf6 qmtc2 $9,vf7 qmtc2 $10,vf8 # vmulax.xyz ACC, vf6,vf5 vmadday.xyz ACC, vf7,vf5 vmaddz.xyz vf4,vf8,vf5 vsub.xyz vf4,vf9,vf4 sq $8,0x0000(%0) sq $9,0x0010(%0) sq $10,0x0020(%0) sqc2 vf4,0x0030(%0) ": : "r" (m0) :"$8","$9","$10","$11","$12","$13","$14","$15"); } #endif inline Matrix& Matrix::InvertUniform () { // Only works for orthonormal Matrix #if 0 // (Mike) this version is buggy! Transpose ( src ); row[POS][X] = -DotProduct ( src[POS], src[RIGHT] ); row[POS][Y] = -DotProduct ( src[POS], src[UP] ); row[POS][Z] = -DotProduct ( src[POS], src[AT] ); return *this; #else // (Mike) try this one instead... // (Dan) actually, don't use the vu0; it returns before the correct values are safely stored in memory; if you access the results too // quickly, you get the wrong answers; this is because the compiler may reorder instructions #if 0 // (Ken) or try this one, uses vu0 Vu0Invert((sceVu0FMATRIX)this); return *this; #else // need to copy the old row, // or else it will change on us as we're doing // our dot products... Mth::Vector oldPos = *(Vector*)row[POS]; row[POS][X] = -DotProduct ( oldPos, *(Vector*)row[RIGHT] ); row[POS][Y] = -DotProduct ( oldPos, *(Vector*)row[UP] ); row[POS][Z] = -DotProduct ( oldPos, *(Vector*)row[AT] ); Swap ( row[X][Y], row[Y][X] ); Swap ( row[X][Z], row[Z][X] ); Swap ( row[Y][Z], row[Z][Y] ); return *this; #endif #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Transpose ( void ) { Swap ( row[X][Y], row[Y][X] ); Swap ( row[X][Z], row[Z][X] ); Swap ( row[X][W], row[W][X] ); Swap ( row[Y][Z], row[Z][Y] ); Swap ( row[Y][W], row[W][Y] ); Swap ( row[Z][W], row[W][Z] ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Transpose ( const Matrix& src ) { if ( &src == this ) { return Transpose(); } row[X][X] = src[X][X]; row[X][Y] = src[Y][X]; row[X][Z] = src[Z][X]; row[X][W] = src[W][X]; row[Y][X] = src[X][Y]; row[Y][Y] = src[Y][Y]; row[Y][Z] = src[Z][Y]; row[Y][W] = src[W][Y]; row[Z][X] = src[X][Z]; row[Z][Y] = src[Y][Z]; row[Z][Z] = src[Z][Z]; row[Z][W] = src[W][Z]; row[W][X] = src[X][W]; row[W][Y] = src[Y][W]; row[W][Z] = src[Z][W]; row[W][W] = src[W][W]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Matrix::Transform ( const Vector& src ) const { Vector v; #ifdef __USE_VU0__ sceVu0ApplyMatrix((float*)&v,(sceVu0FVECTOR*)this,(float*)&src); #else v[X] = src[X] * row[RIGHT][X] + src[Y] * row[UP][X] + src[Z] * row[AT][X] + src[W] * row[POS][X]; v[Y] = src[X] * row[RIGHT][Y] + src[Y] * row[UP][Y] + src[Z] * row[AT][Y] + src[W] * row[POS][Y]; v[Z] = src[X] * row[RIGHT][Z] + src[Y] * row[UP][Z] + src[Z] * row[AT][Z] + src[W] * row[POS][Z]; v[W] = src[X] * row[RIGHT][W] + src[Y] * row[UP][W] + src[Z] * row[AT][W] + src[W] * row[POS][W]; #endif return v; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Matrix::TransformAsPos ( const Vector& src ) const { Vector v; v[X] = src[X] * row[RIGHT][X] + src[Y] * row[UP][X] + src[Z] * row[AT][X] + row[POS][X]; v[Y] = src[X] * row[RIGHT][Y] + src[Y] * row[UP][Y] + src[Z] * row[AT][Y] + row[POS][Y]; v[Z] = src[X] * row[RIGHT][Z] + src[Y] * row[UP][Z] + src[Z] * row[AT][Z] + row[POS][Z]; v[W] = src[X] * row[RIGHT][W] + src[Y] * row[UP][W] + src[Z] * row[AT][W] + row[POS][W]; return v; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Matrix::Rotate ( const Vector& src ) const { Vector v; v[X] = src[X] * row[RIGHT][X] + src[Y] * row[UP][X] + src[Z] * row[AT][X]; v[Y] = src[X] * row[RIGHT][Y] + src[Y] * row[UP][Y] + src[Z] * row[AT][Y]; v[Z] = src[X] * row[RIGHT][Z] + src[Y] * row[UP][Z] + src[Z] * row[AT][Z]; v[W] = src[W]; return v; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Translate( const Vector& trans ) { *(Vector*)(row[POS]) += trans; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ // Ken: Added cos I needed it. inline void Matrix::SetPos( const Vector& trans ) { *(Vector*)(row[POS]) = trans; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::TranslateLocal( const Vector& trans ) { Vector& vec = *(Vector*)(row[POS]); vec[X] += (( trans[X] * row[RIGHT][X] ) + ( trans[Y] * row[UP][X] ) + ( trans[Z] * row[AT][X] )); vec[Y] += (( trans[X] * row[RIGHT][Y] ) + ( trans[Y] * row[UP][Y] ) + ( trans[Z] * row[AT][Y] )); vec[Z] += (( trans[X] * row[RIGHT][Z] ) + ( trans[Y] * row[UP][Z] ) + ( trans[Z] * row[AT][Z] )); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Rotate( const Vector& axis, const float angle ) { Matrix rot_mat( axis, angle ); *this *= rot_mat; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::RotateLocal( const Vector& axis, const float angle ) { Matrix rot_mat( axis, angle ); *this = rot_mat * *this; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::RotateLocal( int axis, const float angle ) { Matrix rot_mat( axis, angle ); *this = rot_mat * *this; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::RotateX( const float angle ) { Matrix rotX; *this *= CreateRotateXMatrix( rotX, angle ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::RotateXLocal( const float angle ) { Matrix rotX; *this = CreateRotateXMatrix( rotX, angle ) * *this; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::RotateY( const float angle ) { Matrix rotY; *this *= CreateRotateYMatrix( rotY, angle ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::RotateYLocal( const float angle ) { #ifdef __USE_VU0__ // Use the end of SCache for a quick and easy speed increase. // (Mick, changed to use the end, so start can be used for permanent stuff) Matrix *p_rot_y=(Matrix*)(0x70004000-4*4*4); *this = CreateRotateYMatrix( *p_rot_y, angle ) * *this; #else Matrix rotY; *this = CreateRotateYMatrix( rotY, angle ) * *this; #endif return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::RotateZ( const float angle ) { Matrix rotZ; *this *= CreateRotateZMatrix( rotZ, angle ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::RotateZLocal( const float angle ) { Matrix rotZ; *this = CreateRotateZMatrix( rotZ, angle ) * *this; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::Scale( const Vector& scale ) { *(Vector*)(row[RIGHT]) *= scale; *(Vector*)(row[UP]) *= scale; *(Vector*)(row[AT]) *= scale; *(Vector*)(row[POS]) *= scale; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::ScaleLocal( const Vector& scale ) { *(Vector*)(row[RIGHT]) *= scale[X]; *(Vector*)(row[UP]) *= scale[Y]; *(Vector*)(row[AT]) *= scale[Z]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::SetColumn ( sint i, const Vector& v ) { // Dbg_MsgAssert (( i >= X && i < NUM_ELEMENTS ),( "index out of range" )); row[X][i] = v[X]; row[Y][i] = v[Y]; row[Z][i] = v[Z]; row[W][i] = v[W]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Matrix::GetColumn ( sint i ) const { // Dbg_MsgAssert (( i >= X && i < NUM_ELEMENTS ),( "index out of range" )); return Vector ( row[X][i], row[Y][i], row[Z][i], row[W][i] ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline ostream& operator<< ( ostream& os, const Matrix& m ) { return os << "{ " << m[X] << ",\n " << m[Y] << ",\n " << m[Z] << ",\n " << m[W] << " }"; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Matrix& Matrix::SetFromAngles ( const Vector& angles ) { Identity(); if (angles[X] != 0.0f || angles[Y] != 0.0f || angles[Z] != 0.0f) { RotateX(angles[X]); RotateY(angles[Y]); RotateZ(angles[Z]); } return *this; } } // namespace Mth #endif // __CORE_MATH_MATRIX_INL ================================================ FILE: Code/Core/Math/quat.h ================================================ /***************************************************************************** /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: core/math/Quat.h ** ** ** ** Created: 11/23/99 - mjb ** ** ** ** Description: Quaternion Math Class ** ** ** *****************************************************************************/ #ifndef __CORE_MATH_QUAT_H #define __CORE_MATH_QUAT_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Mth { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ class Matrix; // forward declaration /**************************************************************************** * * Class: Quat * * Description: Quaternion math class * ****************************************************************************/ class Quat //: public Spt::Class { public: friend bool quat_equal ( const Quat& q1, const Quat& q2, const float tol ); Quat ( float cx = 0.0f, float cy = 0.0f, float cz = 0.0f, float cw = 1.0f ); Quat ( const Vector& axis, float angle ); Quat ( const Matrix& mat ); Quat& Invert ( void ); Quat& Invert ( const Quat& src ); float Modulus ( void ); float ModulusSqr ( void ) const; Quat& Normalize ( float len = 1.0f ); Vector Rotate ( const Vector& vec ) const; void SetScalar ( const float w ); void SetVector ( const float x, const float y, const float z ); void SetVector ( const Vector& v ); const float& GetScalar ( void ) const; const Vector& GetVector ( void ) const { return quat; } void GetMatrix ( Matrix& mat ) const; Quat& operator= ( const Quat& q ); Quat& operator+= ( const Quat& q ); Quat& operator-= ( const Quat& q ); Quat& operator*= ( const Quat& q ); Quat& operator*= ( float s ); Quat& operator/= ( float s ); const float& operator[] ( sint i ) const; float& operator[] ( sint i ); private: Vector quat; // X,Y,Z : Imaginary (Vector) component // W : Real (Scalar) component }; /***************************************************************************** ** Private Inline Functions ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ Quat operator+ ( const Quat& q1, const Quat& q2 ); Quat operator- ( const Quat& q1, const Quat& q2 ); Quat operator* ( const Quat& q1, const Quat& q2 ); Quat operator* ( const Quat& q, const float s ); Quat operator* ( const float s, const Quat& q ); Quat operator- ( const Quat& q ); // negate all elements -> equilavent rotation float DotProduct ( const Quat& v1, const Quat& v2 ); Quat Slerp ( const Quat& q1, const Quat& q2, const float t ); Quat FastSlerp( Quat& qIn1, Quat& qIn2, const float t ); bool Equal ( const Quat& q1, const Quat& q2, const float tol = 0.0001f ); ostream& operator<< ( ostream& os, const Quat& v ); Quat EulerToQuat( const Vector& v ); // converts a quat+vector xform into a matrix xform void QuatVecToMatrix( Mth::Quat* pQ, Mth::Vector* pT, Mth::Matrix* pMatrix ); void SCacheQuatVecToMatrix( Mth::Quat* pQ, Mth::Vector* pT, Mth::Matrix* pMatrix ); /***************************************************************************** ** Inline Functions ** *****************************************************************************/ } // namespace Mth #endif // __CORE_MATH_QUAT_H ================================================ FILE: Code/Core/Math/quat.inl ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (Mth) ** ** ** ** File name: core/math/Quat.inl ** ** ** ** Created: 11/23/99 - mjb ** ** ** ** Description: Quaternion Math Class ** ** ** *****************************************************************************/ #ifndef __CORE_MATH_QUAT_INL #define __CORE_MATH_QUAT_INL namespace Mth { /******************************************************************/ /* */ /* */ /******************************************************************/ inline const float& Quat::operator[] ( sint i ) const { return quat[i]; } /***************************************************************************** ** Private Inline Functions ** *****************************************************************************/ inline bool quat_equal( const Quat& q1, const Quat& q2, const float tol ) { return ( Equal( q1.quat, q2.quat, tol )); } /***************************************************************************** ** Public Inline Functions ** *****************************************************************************/ inline Quat::Quat( float cx, float cy, float cz, float cw ) : quat( cx, cy, cz, cw ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat::Quat( const Vector& axis, float angle ) { float mod = axis.Length(); float ang = angle / 2.0f; mod = ( mod > 0.0f ) ? ( 1.0f / mod ) : 0.0f; mod *= sinf( ang ); SetVector( mod * axis[X], mod * axis[Y], mod * axis[Z] ); SetScalar( cosf ( ang )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float& Quat::operator[] ( sint i ) { return quat[i]; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat::Quat( const Matrix& m ) { float t = m[X][X] + m[Y][Y] + m[Z][Z]; float trace; if ( t > 0.0f ) { trace = sqrtf( t + 1.0f ); SetScalar( trace * 0.5f ); trace = 0.5f / trace; SetVector(( m[Z][Y] - m[Y][Z] ) * trace, ( m[X][Z] - m[Z][X] ) * trace, ( m[Y][X] - m[X][Y] ) * trace ); } else { // find greatest element in Matrix diagonal sint i = X; if ( m[Y][Y] > m[X][X] ) i = Y; if ( m[Z][Z] > m[i][i] ) i = Z; sint j = ( i + 1 ) % W; sint k = ( j + 1 ) % W; trace = sqrtf(( m[i][i] - (m[j][j] + m[k][k] )) + 1.0f ); quat[i] = ( trace * 0.5f ); trace = 0.5f / trace; quat[j] = ( m[j][i] + m[i][j] ) * trace; quat[k] = ( m[k][i] + m[i][k] ) * trace; quat[W] = ( m[k][j] - m[j][k] ) * trace; } } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat& Quat::Invert( void ) // this = Invert ( this ) { quat.Negate(); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat& Quat::Invert( const Quat& src ) // this = Invert ( src ) { quat.Negate( src.quat ); quat[W] = src.quat[W]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Quat::Modulus( void ) { return sqrtf( ModulusSqr () ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Quat::ModulusSqr ( void ) const { return (( quat[X] * quat[X] ) + ( quat[Y] * quat[Y] ) + ( quat[Z] * quat[Z] ) + ( quat[W] * quat[W] )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat& Quat::Normalize( float len ) { float mod = Modulus(); if ( mod > 0.0f ) { mod = len / mod; quat *= mod; } return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Quat::Rotate( const Vector& vec ) const { Quat inv; // Quat pt( vec[X], vec[Y], vec[Z], vec[W] ); Quat pt( vec[X], vec[Y], vec[Z], 1.0f ); // Mick: Setting W to sensible value, otherwise can cause overflow Quat res = *this; inv.Invert( *this ); res *= pt; res *= inv; return Vector( res[X], res[Y], res[Z], res[W] ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Quat::SetScalar( const float w ) { quat[W] = w; }; /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Quat::SetVector( const float x, const float y, const float z ) { quat[X] = x; quat[Y] = y; quat[Z] = z; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Quat::SetVector( const Vector& v ) { quat[X] = v[X]; quat[Y] = v[Y]; quat[Z] = v[Z]; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat& Quat::operator= ( const Quat& q ) { quat = q.quat; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat& Quat::operator+= ( const Quat& q ) { quat += q.quat; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat& Quat::operator-= ( const Quat& q ) { quat -= q.quat; return *this; } inline const float& Quat::GetScalar ( void ) const { return quat[W]; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat& Quat::operator*= ( const Quat& q ) { float s1 = GetScalar(); float s2 = q.GetScalar(); Vector v1 = GetVector(); Vector v2 = q.GetVector(); SetVector (( v2 * s1 ) + ( v1 * s2 ) + CrossProduct ( v1, v2 )); SetScalar (( s1 * s2 ) - DotProduct ( v1, v2 )); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat& Quat::operator*= ( float s ) { quat *= s; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat& Quat::operator/= ( float s ) { quat /= s; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Quat::GetMatrix ( Matrix& mat ) const { float xs, ys, zs, wx, wy, wz, xx, xy, xz, yy, yz, zz, ss; #if 0 // our original version. Seems essentially broken, as LengthSqr ignores W ss = 2.0f / quat.LengthSqr(); #else // version suggested by Andre at Left Field // uses proper Modulus, and clamps 2.0/0.0f to zero ss = ModulusSqr(); ss = ( ss>0.0f ? 2.0f/ss : 0.0f); #endif xs = quat[X] * ss; ys = quat[Y] * ss; zs = quat[Z] * ss; wx = quat[W] * xs; wy = quat[W] * ys; wz = quat[W] * zs; xx = quat[X] * xs; xy = quat[X] * ys; xz = quat[X] * zs; yy = quat[Y] * ys; yz = quat[Y] * zs; zz = quat[Z] * zs; mat[X][X] = 1.0f - (yy + zz); mat[Y][X] = xy + wz; mat[Z][X] = xz - wy; mat[X][Y] = xy - wz; mat[Y][Y] = 1.0f - (xx + zz); mat[Z][Y] = yz + wx; mat[X][Z] = xz + wy; mat[Y][Z] = yz - wx; mat[Z][Z] = 1.0f - (xx + yy ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat operator+ ( const Quat& q1, const Quat& q2 ) { Quat sum = q1; sum += q2; return sum; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat operator- ( const Quat& q1, const Quat& q2 ) { Quat diff = q1; diff -= q2; return diff; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat operator* ( const Quat& q1, const Quat& q2 ) { Quat prod = q1; prod *= q2; return prod; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat operator* ( const Quat& q, const float s ) { Quat prod = q; prod *= s; return prod; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat operator* ( const float s, const Quat& q ) { return q * s; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat operator- ( const Quat& q ) { return Quat ( -q[X], -q[Y], -q[Z], -q[W] ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float DotProduct ( const Quat& v1, const Quat& v2 ) { return ( v1[X] * v2[X] ) + ( v1[Y] * v2[Y] ) + ( v1[Z] * v2[Z] ) + ( v1[W] * v2[W] ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Quat Slerp ( const Quat& q1, const Quat& q3, float t ) { float sclp, sclq; // GJ: Need to possibly negate the second quaternion, // to avoid spinning in the wrong direction. Quat q2 = q3; if ( DotProduct( q1, q3 ) < 0.0f ) { q2 = -q2; } float coso = q1.GetScalar() * q2.GetScalar() + DotProduct ( q1.GetVector(), q2.GetVector()); Quat q; if (( 1.0f + coso + 1.0f ) > EPSILON ) { if (( 1.0f - coso ) > EPSILON ) // slerp { float omega = acosf ( coso ); float sino = sinf ( omega ); sclp = sinf (( 1.0f - t ) * omega ) / sino; sclq = sinf ( t * omega ) / sino; } else // lerp ( angle tends to 0 ) { sclp = 1.0f - t; sclq = t; } q = ( sclp * q1 ) + ( sclq * q2 ); } else // angle tends to 2*PI { q.SetVector ( -q1[Y], q1[X], -q1[W] ); q.SetScalar ( q1[Z] ); sclp = sinf (( 1.0f - t ) * PI / 2.0f ); sclq = sinf ( t * PI / 2.0f ); q.SetVector (( sclp * q1.GetVector()) + ( sclq * q.GetVector())); } return q; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Equal ( const Quat& q1, const Quat& q2, const float tol ) { if (( quat_equal ( q1, q2, tol )) || ( quat_equal ( -q1, q2, tol ))) { return true; } return false; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline ostream& operator<< ( ostream& os, const Quat& q ) { return os << "(( " << q[X] << ", " << q[Y] << ", " << q[Z] << ") , " << q[W] << " )"; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void QuatVecToMatrix( Mth::Quat* pQ, Mth::Vector* pT, Mth::Matrix* pMatrix ) { Dbg_Assert( pQ ); Dbg_Assert( pT ); Dbg_Assert( pMatrix ); pQ->Invert(); Mth::Vector square; Mth::Vector cross; Mth::Vector wimag; square[X] = (*pQ)[X] * (*pQ)[X]; square[Y] = (*pQ)[Y] * (*pQ)[Y]; square[Z] = (*pQ)[Z] * (*pQ)[Z]; cross[X] = (*pQ)[Y] * (*pQ)[Z]; cross[Y] = (*pQ)[Z] * (*pQ)[X]; cross[Z] = (*pQ)[X] * (*pQ)[Y]; wimag[X] = (*pQ)[W] * (*pQ)[X]; wimag[Y] = (*pQ)[W] * (*pQ)[Y]; wimag[Z] = (*pQ)[W] * (*pQ)[Z]; (*pMatrix)[Mth::RIGHT][X] = 1 - 2 * (square[Y] + square[Z]); (*pMatrix)[Mth::RIGHT][Y] = 2 * (cross[Z] + wimag[Z]); (*pMatrix)[Mth::RIGHT][Z] = 2 * (cross[Y] - wimag[Y]); (*pMatrix)[Mth::RIGHT][W] = 0.0f; (*pMatrix)[Mth::UP][X] = 2 * (cross[Z] - wimag[Z]); (*pMatrix)[Mth::UP][Y] = 1 - 2 * (square[X] + square[Z]); (*pMatrix)[Mth::UP][Z] = 2 * (cross[X] + wimag[X]); (*pMatrix)[Mth::UP][W] = 0.0f; (*pMatrix)[Mth::AT][X] = 2 * (cross[Y] + wimag[Y]); (*pMatrix)[Mth::AT][Y] = 2 * (cross[X] - wimag[X]); (*pMatrix)[Mth::AT][Z] = 1 - 2 * (square[X] + square[Y]); (*pMatrix)[Mth::AT][W] = 0.0f; (*pMatrix)[Mth::POS] = *pT; (*pMatrix)[Mth::POS][W] = 1.0f; } inline void SCacheQuatVecToMatrix( Mth::Quat* pQ, Mth::Vector* pT, Mth::Matrix* pMatrix ) { Dbg_Assert( pQ ); Dbg_Assert( pT ); Dbg_Assert( pMatrix ); pQ->Invert(); Mth::Vector *p_square = ((Mth::Vector*)0x7000000)+0; Mth::Vector *p_cross = ((Mth::Vector*)0x7000000)+1; Mth::Vector *p_wimag = ((Mth::Vector*)0x7000000)+2; (*p_square)[X] = (*pQ)[X] * (*pQ)[X]; (*p_square)[Y] = (*pQ)[Y] * (*pQ)[Y]; (*p_square)[Z] = (*pQ)[Z] * (*pQ)[Z]; (*p_cross)[X] = (*pQ)[Y] * (*pQ)[Z]; (*p_cross)[Y] = (*pQ)[Z] * (*pQ)[X]; (*p_cross)[Z] = (*pQ)[X] * (*pQ)[Y]; (*p_wimag)[X] = (*pQ)[W] * (*pQ)[X]; (*p_wimag)[Y] = (*pQ)[W] * (*pQ)[Y]; (*p_wimag)[Z] = (*pQ)[W] * (*pQ)[Z]; (*pMatrix)[Mth::RIGHT][X] = 1 - 2 * ((*p_square)[Y] + (*p_square)[Z]); (*pMatrix)[Mth::RIGHT][Y] = 2 * ((*p_cross)[Z] + (*p_wimag)[Z]); (*pMatrix)[Mth::RIGHT][Z] = 2 * ((*p_cross)[Y] - (*p_wimag)[Y]); (*pMatrix)[Mth::RIGHT][W] = 0.0f; (*pMatrix)[Mth::UP][X] = 2 * ((*p_cross)[Z] - (*p_wimag)[Z]); (*pMatrix)[Mth::UP][Y] = 1 - 2 * ((*p_square)[X] + (*p_square)[Z]); (*pMatrix)[Mth::UP][Z] = 2 * ((*p_cross)[X] + (*p_wimag)[X]); (*pMatrix)[Mth::UP][W] = 0.0f; (*pMatrix)[Mth::AT][X] = 2 * ((*p_cross)[Y] + (*p_wimag)[Y]); (*pMatrix)[Mth::AT][Y] = 2 * ((*p_cross)[X] - (*p_wimag)[X]); (*pMatrix)[Mth::AT][Z] = 1 - 2 * ((*p_square)[X] + (*p_square)[Y]); (*pMatrix)[Mth::AT][W] = 0.0f; (*pMatrix)[Mth::POS] = *pT; (*pMatrix)[Mth::POS][W] = 1.0f; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Slerp ( Matrix& result, const Matrix& s1, const Matrix& s2, float t ) { Slerp ( Quat(s1), Quat(s2), t).GetMatrix ( result ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float counter_warp(float t, float cos_alpha) { const float ATTENUATION = 0.82279687f; const float WORST_CASE_SLOPE = 0.58549219f; float factor = 1.0f - ATTENUATION * cos_alpha; factor *= factor; float k = WORST_CASE_SLOPE * factor; return t*(k*t*(2.0f*t - 3.0f) + 1.0f + k); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Mth::Quat FastSlerp( Mth::Quat& qIn1, Mth::Quat& qIn2, const float t ) { if ( Mth::DotProduct( qIn1, qIn2 ) < 0.0f ) { qIn2 = -qIn2; } # if 0 float cos_a = v1[X]*v2[X] + v1[Y]*v2[Y] + v1[Z]*v2[Z] + v1[W]*v2[W]; if (t <= 0.5f) { t = counter_warp(t, cos_a); } else { t = 1.0f - counter_warp(1.0f - t, cos_a); } # endif float xxx_x = qIn1[X] + ( qIn2[X] - qIn1[X] ) * t; float xxx_y = qIn1[Y] + ( qIn2[Y] - qIn1[Y] ) * t; float xxx_z = qIn1[Z] + ( qIn2[Z] - qIn1[Z] ) * t; float xxx_w = qIn1[W] + ( qIn2[W] - qIn1[W] ) * t; float len = 1.0f / sqrtf( xxx_x * xxx_x + xxx_y * xxx_y + xxx_z * xxx_z + xxx_w * xxx_w ); qIn2.SetVector( xxx_x * len, xxx_y * len, xxx_z * len ); qIn2.SetScalar( xxx_w * len ); return qIn2; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Mth::Quat EulerToQuat( const Mth::Vector& euler ) { // This code is a modified version of Left Field's // MakeQuatFromEuler() function. Mth::Quat q; float roll = euler[X]; float pitch = euler[Y]; float yaw = euler[Z]; float cyaw, cpitch, croll, syaw, spitch, sroll; float cyawcpitch, syawspitch, cyawspitch, syawcpitch; cyaw = cosf(0.5f * yaw); cpitch = cosf(0.5f * pitch); croll = cosf(0.5f * roll); syaw = sinf(0.5f * yaw); spitch = sinf(0.5f * pitch); sroll = sinf(0.5f * roll); cyawcpitch = cyaw * cpitch; syawspitch = syaw * spitch; cyawspitch = cyaw * spitch; syawcpitch = syaw * cpitch; q[W] = cyawcpitch * croll + syawspitch * sroll; q[X] = cyawcpitch * sroll - syawspitch * croll; q[Y] = cyawspitch * croll + syawcpitch * sroll; q[Z] = syawcpitch * croll - cyawspitch * sroll; return q; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Mth #endif // __CORE_MATH_QUAT_INL ================================================ FILE: Code/Core/Math/rect.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: core/math/Rect.h ** ** ** ** Created: 01/31/00 - mjb ** ** ** ** Description: Math Library Rectangle class ** ** ** *****************************************************************************/ #ifndef __CORE_MATH_RECT_H #define __CORE_MATH_RECT_H /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Mth { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ nTemplateBaseClass ( _T, _Rect ) { public: _Rect ( const _T x, const _T y, const _T w, const _T h ); ~_Rect ( ); void SetOriginX ( const _T ) ; void SetOriginY ( const _T ) ; void SetWidth ( const _T ) ; void SetHeight ( const _T ) ; const _T GetOriginX ( void ) const; const _T GetOriginY ( void ) const; const _T GetWidth ( void ) const; const _T GetHeight ( void ) const; private: _T x, y, w, h; }; /***************************************************************************** ** Type Definitions ** *****************************************************************************/ typedef _Rect < float > Rect; typedef _Rect < sint > IRect; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ template < class _T > inline _Rect< _T >::_Rect ( const _T _x, const _T _y, const _T _w, const _T _h ) : x (_x), y (_y), w(_w), h(_h) { } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline _Rect< _T >::~_Rect ( void ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void _Rect< _T >::SetOriginX ( const _T xval ) { x = xval; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void _Rect< _T >::SetOriginY ( const _T yval ) { y = yval; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void _Rect< _T >::SetWidth ( const _T width ) { w = width; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void _Rect< _T >::SetHeight ( const _T height ) { h = height; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline const _T _Rect< _T >::GetOriginX ( void ) const { return x; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline const _T _Rect< _T >::GetOriginY ( void ) const { return y; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline const _T _Rect< _T >::GetWidth ( void ) const { return w; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline const _T _Rect< _T >::GetHeight ( void ) const { return h; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline ostream& operator<< ( ostream& str, const _Rect< _T >& r ) { str << "(( " << r.GetOriginX() << ", " << r.GetOriginY() << " ),( " << r.GetWidth() << ", " << r.GetHeight() << " ))"; return str; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Mth #endif // __CORE_MATH_RECT_H ================================================ FILE: Code/Core/Math/rot90.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: core/math/rot90.cpp ** ** ** ** Created: 07/17/02 - grj ** ** ** ** Description: Rotation 90 Math Class ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include namespace Mth { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** DBG Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ void RotateY90( ERot90 angle, int32& x, int32& y, int32& z ) { int32 temp; switch (angle) { case ROT_0: break; case ROT_90: temp = x; x = z; z = -temp; break; case ROT_180: x = -x; z = -z; break; case ROT_270: temp = x; x = -z; z = temp; break; default: Dbg_MsgAssert(0, ("RotateY90() out of range: %d", angle)); break; } } } // namespace Mth ================================================ FILE: Code/Core/Math/rot90.h ================================================ /////////////////////////////////////////////////////////////////////////////////////// // rot90.h // #ifndef __CORE_ROT90_H #define __CORE_ROT90_H namespace Mth { // Rotation values for 90 degree increment Rotation (uses no multiplication) enum ERot90 { ROT_0 = 0, ROT_90, ROT_180, ROT_270, NUM_ROTS }; // Integer rotate void RotateY90( ERot90 angle, int32& x, int32& y, int32& z ); } #endif ================================================ FILE: Code/Core/Math/slerp.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: core/math/slerp.cpp ** ** ** ** Created: 12/20/01 - gj ** ** ** ** Description: Slerp Interpolator Math Class ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include namespace Mth { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ const float USE_LERP_INSTEAD_DEGREES = 2.0f; const float USE_LERP_INSTEAD_RADIANS = USE_LERP_INSTEAD_DEGREES * PI / 180.0f; /***************************************************************************** ** DBG Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ SlerpInterpolator::SlerpInterpolator() { } /******************************************************************/ /* */ /* */ /******************************************************************/ SlerpInterpolator::SlerpInterpolator( const Matrix * start, const Matrix * end ) { setMatrices( start, end ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void SlerpInterpolator::setMatrices( const Matrix * start, const Matrix * end ) { Matrix inv; m_start = *start; m_end = *end; // Calculate the inverse transformation. inv = m_start; inv.Invert(); inv = inv * m_end; // Get the axis and angle. inv.GetRotationAxisAndAngle( &m_axis, &m_radians ); #if 0 // Debugging stuff static int print_x_times = 50; if ( print_x_times > 0 ) { print_x_times--; printf("Start mat\n"); m_start.PrintContents(); printf("Inv mat\n"); inv.PrintContents(); printf("End mat\n"); m_end.PrintContents(); printf("Start * Inv mat\n"); Mth::Matrix siMat = m_start; siMat = siMat * inv; siMat.PrintContents(); { printf("Rotated mat w=0\n"); Mth::Matrix tempMatrix = m_start; m_axis[W] = 0.0f; tempMatrix.Rotate( m_axis, m_radians ); tempMatrix.PrintContents(); } { printf("Rotated mat w=1\n"); Mth::Matrix tempMatrix2 = m_start; m_axis[W] = 1.0f; tempMatrix2.Rotate( m_axis, m_radians ); tempMatrix2.PrintContents(); } { printf("Rotated local mat w=0\n"); Mth::Matrix tempMatrix = m_start; m_axis[W] = 0.0f; tempMatrix.RotateLocal( m_axis, m_radians ); tempMatrix.PrintContents(); } { printf("Rotated local mat w=1\n"); Mth::Matrix tempMatrix2 = m_start; m_axis[W] = 1.0f; tempMatrix2.RotateLocal( m_axis, m_radians ); tempMatrix2.PrintContents(); } printf( "Slerp axis=(%f %f %f %f) angle=%f\n", m_axis[X], m_axis[Y], m_axis[Z], m_axis[W], m_radians ); } else { Dbg_Assert( 0 ); } #endif // If angle is too small, use lerp. m_useLerp = m_radians < USE_LERP_INSTEAD_RADIANS; } /******************************************************************/ /* */ /* */ /******************************************************************/ void SlerpInterpolator::getMatrix( Matrix * result, float delta ) { /* Cap and floor the delta */ /* If we are at one end the solution is easy */ if (delta <= 0.0f) { // delta = 0.0f; *result = m_start; return; } else if (delta >= 1.0f) { // delta = 1.0f; *result = m_end; return; } #if 0 // GJ: always lerp, used while slerp was being debugged // m_useLerp = true; #endif /* Do the lerp if we are, else... */ if ( m_useLerp ) { /* Get the lerp matrix */ Matrix lerp; Vector lpos; Vector spos; Vector epos; Vector rpos; lerp.Ident(); spos = m_start[Mth::POS]; epos = m_end[Mth::POS]; lerp[Mth::RIGHT] = m_end[Mth::RIGHT] - m_start[Mth::RIGHT]; lerp[Mth::UP] = m_end[Mth::UP] - m_start[Mth::UP]; lerp[Mth::AT] = m_end[Mth::AT] - m_start[Mth::AT]; lpos = epos - spos; /* Do lerp */ lerp[Mth::RIGHT].Scale( delta ); lerp[Mth::UP].Scale( delta ); lerp[Mth::AT].Scale( delta ); lpos.Scale( delta ); (*result)[Mth::RIGHT] = m_start[Mth::RIGHT] + lerp[Mth::RIGHT]; (*result)[Mth::UP] = m_start[Mth::UP] + lerp[Mth::UP]; (*result)[Mth::AT] = m_start[Mth::AT] + lerp[Mth::AT]; rpos = spos + lpos; (*result)[Mth::RIGHT].Normalize(); (*result)[Mth::UP].Normalize(); (*result)[Mth::AT].Normalize(); (*result)[Mth::POS] = rpos; } else { Vector rpos; Vector spos; Vector epos; Vector lpos; spos = m_start[Mth::POS]; epos = m_end[Mth::POS]; /* Remove the translation for now */ *result = m_start; (*result)[Mth::POS] = Mth::Vector( 0.0f, 0.0f, 0.0f ); /* Rotate the new matrix */ // m_axis[W] = 0.0f; result->Rotate( m_axis, m_radians * delta ); /* Do linear interpolation on position */ lpos = epos - spos; lpos.Scale( delta ); rpos = spos + lpos; (*result)[Mth::POS] = rpos; } } /******************************************************************/ /* */ /* */ /******************************************************************/ void SlerpInterpolator::invertDirection ( ) { m_axis = -m_axis; m_radians = (2.0f * Mth::PI) - m_radians; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Obj ================================================ FILE: Code/Core/Math/slerp.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: core/math/slerp.h ** ** ** ** Created: 12/20/01 - gj ** ** ** ** Description: Slerp Interpolator Math Class ** ** ** *****************************************************************************/ #ifndef __CORE_MATH_SLERP_H #define __CORE_MATH_SLERP_H /***************************************************************************** ** Includes ** *****************************************************************************/ #ifndef __CORE_DEFINES_H #include #endif #include #include /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Mth { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /**************************************************************************** * * Class: Slerp Interpolator * * Description: Slerp Interpolator math class * ****************************************************************************/ class SlerpInterpolator { public: SlerpInterpolator(); SlerpInterpolator( const Matrix* pStart, const Matrix* pEnd ); public: void setMatrices( const Matrix* pStart, const Matrix* pEnd ); void getMatrix( Matrix* result, float delta ); void invertDirection ( ); float getRadians ( ) { return m_radians; } const Mth::Vector& getAxis ( ) { return m_axis; } protected: Matrix m_start; Matrix m_end; Vector m_axis; float m_radians; bool m_useLerp; // do linear-interpolation, rather than slerping }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ } // namespace Mth #endif // __CORE_MATH_SLERP_H ================================================ FILE: Code/Core/Math/vector.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Vector (MTH) ** ** ** ** File name: vector.cpp ** ** ** ** Created by: 11/24/99 - Mick ** ** ** ** Description: Math Library code ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include //#include #include #include #include #include #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Mth { /***************************************************************************** ** DBG Information ** *****************************************************************************/ // /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ Vector& Vector::RotateY90( ERot90 angle ) { float temp; switch (angle) { case ROT_0: break; case ROT_90: temp = col[X]; col[X] = col[Z]; col[Z] = -temp; break; case ROT_180: col[X] = -col[X]; col[Z] = -col[Z]; break; case ROT_270: temp = col[X]; col[X] = -col[Z]; col[Z] = temp; break; default: Dbg_MsgAssert(0, ("Vector::RotateY90() out of range: %d", angle)); break; } return *this; } /***************************************************************************** // Project the vector onto the plane define by a normal *****************************************************************************/ Vector& Vector::ProjectToPlane(const Vector& normal) { float perp_component = Mth::DotProduct(*this,normal); *this -= normal * perp_component; return *this; } Vector& Vector::RotateToPlane(const Vector& normal) { // get the length of the vector float length = Length(); // Project the vector onto the plane ProjectToPlane(normal); // now we've projected it ontot he plane // we need to handle the case where it dissapears into a point // which would indicate that we were perpendicular to the plane // so need to get an arbitary vector in the plane float projected_length = Length(); if (projected_length == 0.0f) // is this a valid comparision? { // Rotate vector through -90 degrees about Y then +90 about X col[X] = -normal[Z]; col[Y] = normal[X]; col[Z] = -normal[Y]; } // get unit vector in this direction Normalize(); // multiply by original speed to rotate velocity onto plane *this *= length; col[W] = 0.0f; // clean up W, otherwise multilications will accumelate over time... return *this; } Vector& Vector::RotateToNormal(const Vector& normal) { *this = normal*Length(); return *this; } Vector& Vector::ProjectToNormal(const Vector& normal) { float dot = Mth::DotProduct(*this,normal); *this = normal * dot; return *this; } /* Finds the largest contributor to the length of the vector... If you pass in whichAxis, it will fill it in with which axis is the max... */ float Vector::GetMaxAxis( int *whichAxis ) { int which_axis; if ( !whichAxis ) { whichAxis = &which_axis; } *whichAxis = X; if ( Abs( this->col[ Y ] ) > Abs( this->col[ X ] ) ) { if ( Abs( this->col[ Z ] ) > Abs( this->col[ Y ] ) ) *whichAxis = Z; else *whichAxis = Y; } else if ( Abs( this->col[ Z ] ) > Abs( this->col[ X ] ) ) { *whichAxis = Z; } return ( this->col[ *whichAxis ] ); } void Vector::DegreesToRadians( void ) { this->col[ X ] = DEGREES_TO_RADIANS( this->col[ X ] ); this->col[ Y ] = DEGREES_TO_RADIANS( this->col[ Y ] ); this->col[ Z ] = DEGREES_TO_RADIANS( this->col[ Z ] ); } void Vector::RadiansToDegrees( void ) { this->col[ X ] = RADIANS_TO_DEGREES( this->col[ X ] ); this->col[ Y ] = RADIANS_TO_DEGREES( this->col[ Y ] ); this->col[ Z ] = RADIANS_TO_DEGREES( this->col[ Z ] ); } void Vector::FeetToInches( void ) { this->col[ X ] = FEET_TO_INCHES( this->col[ X ] ); this->col[ Y ] = FEET_TO_INCHES( this->col[ Y ] ); this->col[ Z ] = FEET_TO_INCHES( this->col[ Z ] ); } // convert to 3d studio max coords ( Z+ up, Y- forward, X to the right... ) void Vector::ConvertToMaxCoords( void ) { this->col[ X ] = -this->col[ X ]; float temp = this->col[ Y ]; this->col[ Y ] = -this->col[ Z ]; this->col[ Z ] = temp; } // convert to 3d studio max coords ( Z+ up, Y- forward, X to the right... ) void Vector::ConvertFromMaxCoords( void ) { this->col[ X ] = -this->col[ X ]; float temp = this->col[ Y ]; this->col[ Y ] = this->col[ Z ]; this->col[ Z ] = -temp; } float GetAngle( const Matrix ¤tOrientation, const Vector &desiredHeading, int headingAxis, int rotAxis ) { Dbg_MsgAssert( headingAxis == X || headingAxis == Y || headingAxis == Z,( "Improper heading axis." )); Dbg_MsgAssert( rotAxis == X || rotAxis == Y || rotAxis == Z,( "Improper rot axis." )); Dbg_MsgAssert( rotAxis != headingAxis,( "Can't use heading axis as rot axis." )); Mth::Vector tempHeading = desiredHeading; tempHeading.ProjectToPlane( currentOrientation[ rotAxis ] ); if ( !tempHeading.Length( ) ) { // If our rot axis is along Y, for example, the // target heading is right above us... Can't pick // a rotation! return ( 0.0f ); } tempHeading.Normalize( ); // while we have these two vectors, find the angle between them... float angCos = DotProduct( currentOrientation[ headingAxis ], tempHeading ); // Mick: contrain Dot product to range -1.0f to +1.0f, since FP innacuracies might // make it go outside this range // (previously this was done only on NGC. Not sure why it has started failing now // but it seems logical that it would fail occasionally, so this check is necessary) float ang = angCos; if ( ang > 1.0f ) ang = 1.0f; if ( ang < -1.0f ) ang = -1.0f; float retVal = -acosf( ang ); Mth::Vector temp = Mth::CrossProduct( currentOrientation[ headingAxis ], tempHeading ); // check to see if the cross product is in the same quatrant as our rot axis... // if so, gots to rotate the other way and shit like that... int whichAxis; float tempMax = temp.GetMaxAxis( &whichAxis ); if ( ( tempMax > 0 ) == ( currentOrientation[ rotAxis ][ whichAxis ] > 0 ) ) { return ( -retVal ); } return ( retVal ); } float GetAngle(Vector &v1, Vector &v2) { Vector a=v1; Vector b=v2; a.Normalize(); b.Normalize(); float dot = Mth::DotProduct(a,b); if ( dot > 1.0f ) dot = 1.0f; if ( dot < -1.0f ) dot = -1.0f; return Mth::RadToDeg(acosf(dot)); } // Returns true if the angle between a and b is greater than the passed Angle. bool AngleBetweenGreaterThan(const Vector& a, const Vector& b, float Angle) { float dot=Mth::DotProduct(a,b); float len=a.Length(); if (len<0.5f) { return false; } dot/=len; len=b.Length(); if (len<0.5f) { return false; } dot/=len; float TestAngleCosine=cosf(Mth::DegToRad(Angle)); if (dot>=0.0f) { return dot=0.0f) { return true; } else { return dot /***************************************************************************** ** Defines ** *****************************************************************************/ enum { X, Y, Z, W }; namespace Mth { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ class Quat; // forward refs class Matrix; /**************************************************************************** * * Class: Vector * * Description: Vector math class * ****************************************************************************/ class Vector { public: // Made special enum so we can have a constructor that does not initialize enum ENoInitType { NO_INIT }; Vector ( ); Vector ( ENoInitType ); /// A constructor that doesn't init the members. Vector ( float cx, float cy, float cz ); Vector ( float cx, float cy, float cz, float cw ); // Vector ( float cx = 0.0f, float cy = 0.0f, float cz = 0.0f, float cw = 1.0f ); Vector ( const Vector& v ); Vector& operator= ( const Vector& v ); bool operator== ( const Vector& v ) const; bool operator!= ( const Vector& v ) const; Vector& operator+= ( const Vector& v ); Vector& operator-= ( const Vector& v ); Vector& operator*= ( const float s ); Vector& operator/= ( float s ); Vector& operator*= ( const Vector& v ); Vector& operator/= ( const Vector& v ); Vector& operator*= ( const Matrix& mat ); const float& operator[] ( sint i ) const; float& operator[] ( sint i ); Vector& Set ( float cx = 0.0f, float cy = 0.0f, float cz = 0.0f, float cw = 1.0f ); float Length ( void ) const; float LengthSqr ( void ) const; Vector& Negate ( void ); // this = -this Vector& Negate ( const Vector& src ); // this = -src Vector& Normalize ( ); // faster version normlaizes to 1 Vector& Normalize ( float len); Vector& ShearX ( float shy, float shz ); Vector& ShearY ( float shx, float shz ); Vector& ShearZ ( float shx, float shy ); Vector& RotateX ( float s, float c ); Vector& RotateY ( float s, float c ); Vector& RotateZ ( float s, float c ); Vector& RotateX ( float angle ); Vector& RotateY ( float angle ); Vector& RotateZ ( float angle ); Vector& Rotate ( const Quat& quat ); Vector& Rotate ( const Matrix& matrix ); Vector& Rotate ( const Vector& axis, const float angle ); // 90 degree increment Rotation (uses no multiplication) Vector& RotateY90 ( ERot90 angle ); Vector& Scale ( float scale ); Vector& Scale ( const Vector& scale ); Vector& RotateToPlane(const Vector& normal); Vector& RotateToNormal(const Vector& normal); Vector& ProjectToPlane(const Vector& normal); Vector& ProjectToNormal(const Vector& normal); Vector& ZeroIfShorterThan ( float length ); Vector& ClampMin ( float min ); Vector& ClampMax ( float max ); Vector& Clamp ( float min = 0.0f, float max = 1.0f ); Vector& ClampMin ( Vector& min ); Vector& ClampMax ( Vector& max ); Vector& Clamp ( Vector& min, Vector& max ); float GetX ( void ) const; float GetY ( void ) const; float GetZ ( void ) const; float GetW ( void ) const; float GetMaxAxis ( int *whichAxis = NULL ); void DegreesToRadians( void ); void RadiansToDegrees( void ); void FeetToInches( void ); // convert to 3d studio max coords ( Z+ up, Y- forward, X to the right... ) void ConvertToMaxCoords( void ); void ConvertFromMaxCoords( void ); // debug info void PrintContents() const; private: enum { NUM_ELEMENTS = 4 }; float col[NUM_ELEMENTS]; } nAlign(128); /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ Vector operator+ ( const Vector& v1, const Vector& v2 ); Vector operator- ( const Vector& v1, const Vector& v2 ); Vector operator* ( const Vector& v, const float s ); Vector operator* ( const float s, const Vector& v ); Vector operator* ( const Vector& v, const Matrix& m ); Vector operator/ ( const Vector& v, const float s ); Vector operator- ( const Vector& v ); float DotProduct ( const Vector& v1, const Vector& v2 ); Vector CrossProduct ( const Vector& v1, const Vector& v2 ); Vector Clamp ( const Vector& v, float min = 0.0f, float max = 1.0f ); Vector Clamp ( const Vector& v, Vector& min, Vector& max ); float Distance ( const Vector& v1, const Vector& v2 ); float DistanceSqr ( const Vector& v1, const Vector& v2 ); float GetAngle ( const Matrix ¤tOrientation, const Vector &desiredHeading, int headingAxis = Z, int rotAxis = Y ); float GetAngle(Vector &v1, Vector &v2); bool AngleBetweenGreaterThan(const Vector& a, const Vector& b, float Angle); float GetAngleAbout(Vector &v1, Vector &v2, Vector &Axis); bool Equal ( const Vector& v1, const Vector& v2, const float tol = 0.0001f ); Vector Lerp ( const Vector& v1, const Vector& v2, const float val ); Vector Min ( const Vector& v1, const Vector& v2 ); Vector Max ( const Vector& v1, const Vector& v2 ); Vector Min ( const Vector& v, float c ); Vector Max ( const Vector& v, float c ); void Swap ( Vector& a, Vector& b ); ostream& operator<< ( ostream& os, const Vector& v ); /***************************************************************************** ** Inline Functions ** *****************************************************************************/ } // namespace Mth #endif // __CORE_MATH_VECTOR_H ================================================ FILE: Code/Core/Math/vector.inl ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (Mth) ** ** ** ** File name: core/math/Vector.h ** ** ** ** Created: 11/29/99 - mjb ** ** ** ** Description: Vector Math Class ** ** ** *****************************************************************************/ #ifndef __CORE_MATH_VECTOR_INL #define __CORE_MATH_VECTOR_INL #ifdef __PLAT_NGPS__ #include #define __USE_VU0__ #ifdef DEBUG_UNINIT_VECTORS #undef __USE_VU0__ #endif #ifdef DEBUG_OVERFLOW_VECTORS #undef __USE_VU0__ #endif #endif /***************************************************************************** ** Private Inline Functions ** *****************************************************************************/ namespace Mth { /***************************************************************************** ** Public Inline Functions ** *****************************************************************************/ // Unitialized vectors always need overwriting // for now we debug this by setting them to a NaN value // which we can check for later (and some platforms will check automatically) #ifdef __INITIALIZE_VECTORS__ inline Vector::Vector ( ) { col[X] = 0.0f; col[Y] = 0.0f; col[Z] = 0.0f; col[W] = 1.0f; } #else #ifdef DEBUG_UNINIT_VECTORS inline Vector::Vector ( ) { *((uint32*)(&col[X])) = 0x7F800001; // Positive NANS (Not A Number - Signaling) *((uint32*)(&col[Y])) = 0x7F800002; // Positive NANS (Not A Number - Signaling) *((uint32*)(&col[Z])) = 0x7F800003; // Positive NANS (Not A Number - Signaling) *((uint32*)(&col[W])) = 0x7F800004; // Positive NANS (Not A Number - Signaling) } #else inline Vector::Vector ( ) { } #endif #endif inline Vector::Vector ( ENoInitType ) { // DO NOTHING } inline Vector::Vector ( float cx, float cy, float cz) { col[X] = cx; col[Y] = cy; col[Z] = cz; col[W] = 1.0f; } inline Vector::Vector ( float cx, float cy, float cz, float cw ) { col[X] = cx; col[Y] = cy; col[Z] = cz; col[W] = cw; } /******************************************************************/ /* */ /* */ /******************************************************************/ #ifndef DEBUG_UNINIT_VECTORS #ifndef DEBUG_OVERFLOW_VECTORS inline const float& Vector::operator[] ( sint i ) const { return col[i]; } #endif #endif /******************************************************************/ /* */ /* */ /******************************************************************/ inline float& Vector::operator[] ( sint i ) { // Dbg_MsgAssert (( i >= X && i < NUM_ELEMENTS ),( "index out of range" )); return col[i]; } /******************************************************************/ /* */ /* */ /******************************************************************/ #ifndef DEBUG_UNINIT_VECTORS inline Vector& Vector::operator= ( const Vector& v ) { #ifdef __USE_VU0__ // Not really VU0, but it's hardware specific *(uint128*)(this) = *(uint128*)(&v); #else col[X] = v[X]; col[Y] = v[Y]; col[Z] = v[Z]; col[W] = v[W]; #endif return *this; } #endif /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector::Vector ( const Vector& v ) { *this = v; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Vector::operator== ( const Vector& v ) const { return (( col[X] == v[X] ) && ( col[Y] == v[Y] ) && ( col[Z] == v[Z] ) && ( col[W] == v[W] )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Vector::operator!= ( const Vector& v ) const { return !( *this == v ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::operator+= ( const Vector& v ) { col[X] += v[X]; col[Y] += v[Y]; col[Z] += v[Z]; col[W] += v[W]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::operator-= ( const Vector& v ) { col[X] -= v[X]; col[Y] -= v[Y]; col[Z] -= v[Z]; col[W] -= v[W]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::operator*= ( const float s ) { col[X] *= s; col[Y] *= s; col[Z] *= s; col[W] *= s; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::operator/= ( float s ) { Dbg_MsgAssert( s != 0,( "Divide by zero" )); s = 1.0f / s; col[X] *= s; col[Y] *= s; col[Z] *= s; col[W] *= s; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ #ifdef __USE_VU0__ inline void xsceVu0MulVector(sceVu0FVECTOR v0, sceVu0FVECTOR v1, sceVu0FVECTOR v2) { asm __volatile__(" .set noreorder lqc2 vf4,0x0(%1) lqc2 vf5,0x0(%2) vmul.xyzw vf6,vf4,vf5 sqc2 vf6,0x0(%0) .set reorder ": : "r" (v0) , "r" (v1), "r" (v2)); } #endif inline Vector& Vector::operator*= ( const Vector& v ) { #ifndef __USE_VU0__ col[X] *= v[X]; col[Y] *= v[Y]; col[Z] *= v[Z]; col[W] *= v[W]; # else xsceVu0MulVector(col,(float*)v.col,col); # endif return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::operator/= ( const Vector& v ) { Dbg_MsgAssert( v[X] != 0,( "Divide by zero X" )); Dbg_MsgAssert( v[Y] != 0,( "Divide by zero Y" )); Dbg_MsgAssert( v[Z] != 0,( "Divide by zero Z" )); Dbg_MsgAssert( v[W] != 0,( "Divide by zero W" )); col[X] /= v[X]; col[Y] /= v[Y]; col[Z] /= v[Z]; col[W] /= v[W]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::operator*= ( const Matrix& mat ) { #ifdef __USE_VU0__ sceVu0ApplyMatrix((sceVu0FVECTOR)this,(sceVu0FMATRIX)&mat,(sceVu0FVECTOR)this); #else Vector v=*this; col[X] = v[X] * mat[X][X] + v[Y] * mat[Y][X] + v[Z] * mat[Z][X] + v[W] * mat[W][X]; col[Y] = v[X] * mat[X][Y] + v[Y] * mat[Y][Y] + v[Z] * mat[Z][Y] + v[W] * mat[W][Y]; col[Z] = v[X] * mat[X][Z] + v[Y] * mat[Y][Z] + v[Z] * mat[Z][Z] + v[W] * mat[W][Z]; col[W] = v[X] * mat[X][W] + v[Y] * mat[Y][W] + v[Z] * mat[Z][W] + v[W] * mat[W][W]; #endif return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Set ( float cx, float cy, float cz, float cw ) { col[X] = cx; col[Y] = cy; col[Z] = cz; col[W] = cw; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Vector::LengthSqr ( void ) const { return (( col[X] * col[X] ) + ( col[Y] * col[Y] ) + ( col[Z] * col[Z] )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Vector::Length ( void ) const { return sqrtf ( LengthSqr ()); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Negate ( void ) { col[X] = -col[X]; col[Y] = -col[Y]; col[Z] = -col[Z]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Negate ( const Vector& src ) { col[X] = -src[X]; col[Y] = -src[Y]; col[Z] = -src[Z]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ #ifdef __USE_VU0__ // currenntly this version seems to have problems with instrcutionre-ordering, // so don't use it unless you figure it out. // Note: it was originally a non-inline library function, so those problems would not occur inline void xsceVu0Normalize(sceVu0FVECTOR v0, sceVu0FVECTOR v1) { asm __volatile__(" .set noreorder lqc2 vf4,0x0(%1) vmul.xyz vf5,vf4,vf4 vaddy.x vf5,vf5,vf5 vaddz.x vf5,vf5,vf5 vsqrt Q,vf5x vwaitq vaddq.x vf5x,vf0x,Q vdiv Q,vf0w,vf5x vsub.xyzw vf6,vf0,vf0 #vf6.xyzw=0; vwaitq vmulq.xyz vf6,vf4,Q sqc2 vf6,0x0(%0) .set reorder ": : "r" (v0) , "r" (v1)); } #endif inline Vector& Vector::Normalize( ) { //# if 0 #ifdef __USE_VU0__ // currenntly inline (xsce) version seems to have problems with instrcutionre-ordering, // so don't use it unless you figure it out. sceVu0Normalize(col,col); // NOTE::: using the non-inline version (not xsce....) # else float l = Length(); if ( l > 0.0f ) { l = 1.0f / l; col[X] *= l; col[Y] *= l; col[Z] *= l; } else { // Dbg_MsgAssert(0,("Normalizing vector of zero length")); } # endif return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Normalize ( float len ) { float l = Length(); if ( l > 0.0f ) { l = len / l; col[X] *= l; col[Y] *= l; col[Z] *= l; } else { // Dbg_MsgAssert(0,("Normalizing vector of zero length")); } return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::ShearX ( float shy, float shz ) { col[X] += (( col[Y] * shy ) + ( col[Z] * shz )); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::ShearY ( float shx, float shz ) { col[Y] += (( col[X] * shx ) + ( col[Z] * shz )); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::ShearZ ( float shx, float shy ) { col[Z] += (( col[X] * shx ) + ( col[Y] * shy )); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::RotateX ( float s, float c ) { float y = ( c * col[Y] ) - ( s * col[Z] ); float z = ( s * col[Y] ) + ( c * col[Z] ); col[Y] = y; col[Z] = z; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::RotateY ( float s, float c ) { float x = ( c * col[X] ) + ( s * col[Z] ); float z = ( -s * col[X] ) + ( c * col[Z] ); col[X] = x; col[Z] = z; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::RotateZ ( float s, float c ) { float x = ( c * col[X] ) - ( s * col[Y] ); float y = ( s * col[X] ) + ( c * col[Y] ); col[X] = x; col[Y] = y; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::RotateX ( float angle ) { return RotateX ( sinf ( angle ), cosf ( angle )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::RotateY ( float angle ) { return RotateY ( sinf ( angle ), cosf ( angle )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::RotateZ ( float angle ) { return RotateZ ( sinf ( angle ), cosf ( angle )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Rotate ( const Quat& quat ) { *this = quat.Rotate ( *this ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Rotate ( const Matrix& mat ) { *this = mat.Rotate ( *this ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Rotate ( const Vector& axis, const float angle ) { #if 0 Matrix mat( axis, angle ); *this = mat.Rotate( *this ); #else Quat quat( axis, angle ); *this = quat.Rotate( *this ); #endif return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Scale ( float scale ) { col[X] *= scale; col[Y] *= scale; col[Z] *= scale; col[W] *= scale; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Scale ( const Vector& scale ) { col[X] *= scale[X]; col[Y] *= scale[Y]; col[Z] *= scale[Z]; col[W] *= scale[W]; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::ClampMin ( float min ) { col[X] = Mth::ClampMin ( col[X], min ); col[Y] = Mth::ClampMin ( col[Y], min ); col[Z] = Mth::ClampMin ( col[Z], min ); col[W] = Mth::ClampMin ( col[W], min ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::ClampMax ( float max ) { col[X] = Mth::ClampMax ( col[X], max ); col[Y] = Mth::ClampMax ( col[Y], max ); col[Z] = Mth::ClampMax ( col[Z], max ); col[W] = Mth::ClampMax ( col[W], max ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Clamp ( float min, float max ) { col[X] = Mth::ClampMin ( Mth::ClampMax ( col[X], max ), min ); col[Y] = Mth::ClampMin ( Mth::ClampMax ( col[Y], max ), min ); col[Z] = Mth::ClampMin ( Mth::ClampMax ( col[Z], max ), min ); col[W] = Mth::ClampMin ( Mth::ClampMax ( col[W], max ), min ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::ClampMin ( Vector& min ) { col[X] = Mth::ClampMin ( col[X], min[X] ); col[Y] = Mth::ClampMin ( col[Y], min[Y] ); col[Z] = Mth::ClampMin ( col[Z], min[Z] ); col[W] = Mth::ClampMin ( col[W], min[W] ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::ClampMax ( Vector& max ) { col[X] = Mth::ClampMax ( col[X], max[X] ); col[Y] = Mth::ClampMax ( col[Y], max[Y] ); col[Z] = Mth::ClampMax ( col[Z], max[Z] ); col[W] = Mth::ClampMax ( col[W], max[W] ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::Clamp ( Vector& min, Vector& max ) { col[X] = Mth::ClampMin ( Mth::ClampMax ( col[X], max[X] ), min[X] ); col[Y] = Mth::ClampMin ( Mth::ClampMax ( col[Y], max[Y] ), min[Y] ); col[Z] = Mth::ClampMin ( Mth::ClampMax ( col[Z], max[Z] ), min[Z] ); col[W] = Mth::ClampMin ( Mth::ClampMax ( col[W], max[W] ), min[W] ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Vector::GetX( void ) const { return col[X]; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Vector::GetY( void ) const { return col[Y]; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Vector::GetZ( void ) const { return col[Z]; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Vector::GetW( void ) const { return col[W]; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector operator+ ( const Vector& v1, const Vector& v2 ) { return Vector ( v1[X] + v2[X], v1[Y] + v2[Y], v1[Z] + v2[Z], v1[W] + v2[W] ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector operator- ( const Vector& v1, const Vector& v2 ) { return Vector ( v1[X] - v2[X], v1[Y] - v2[Y], v1[Z] - v2[Z], v1[W] - v2[W] ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector operator* ( const Vector& v, const float s ) { return Vector ( v[X] * s, v[Y] * s, v[Z] * s, v[W] * s ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector operator* ( const float s, const Vector& v ) { return v * s; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector operator* ( const Vector& v, const Matrix& m ) { #ifdef __USE_VU0__ // Mick: Mth::Vector result; sceVu0ApplyMatrix((sceVu0FVECTOR)&result,(sceVu0FMATRIX)&m,(sceVu0FVECTOR)&v); return result; #else // Mick: This is REALLY SLOW, as it calls Mth::Matrix operator[] return Vector ( v[X] * m[X][X] + v[Y] * m[Y][X] + v[Z] * m[Z][X] + v[W] * m[W][X], v[X] * m[X][Y] + v[Y] * m[Y][Y] + v[Z] * m[Z][Y] + v[W] * m[W][Y], v[X] * m[X][Z] + v[Y] * m[Y][Z] + v[Z] * m[Z][Z] + v[W] * m[W][Z], v[X] * m[X][W] + v[Y] * m[Y][W] + v[Z] * m[Z][W] + v[W] * m[W][W]); #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector operator/ ( const Vector& v, const float s ) { Dbg_MsgAssert (( s != 0.0f ),( "Divide by Zero" )); float r = 1.0f / s; return v * r; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector operator- ( const Vector& v ) { return Vector ( -v[X], -v[Y], -v[Z], -v[W] ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float DotProduct ( const Vector& v1, const Vector& v2 ) { return ( v1[X] * v2[X] ) + ( v1[Y] * v2[Y] ) + ( v1[Z] * v2[Z] ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector CrossProduct ( const Vector& v1, const Vector& v2 ) { return Vector (( v1[Y] * v2[Z] ) - ( v1[Z] * v2[Y] ), ( v1[Z] * v2[X] ) - ( v1[X] * v2[Z] ), ( v1[X] * v2[Y] ) - ( v1[Y] * v2[X] ), 0.0f ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Clamp ( const Vector& v, float min, float max ) { Vector f = v; f.Clamp ( min, max ); return f; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Clamp ( const Vector& v, Vector& min, Vector& max ) { Vector f = v; f.Clamp ( min, max ); return f; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float Distance ( const Vector& v1, const Vector& v2 ) { Vector d = v1 - v2; return sqrtf ( DotProduct ( d, d )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline float DistanceSqr ( const Vector& v1, const Vector& v2 ) { Vector d = v1 - v2; return ( DotProduct ( d, d )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Equal ( const Vector& v1, const Vector& v2, const float tol ) { return ( Equal ( v1[X], v2[X], tol ) && Equal ( v1[Y], v2[Y], tol ) && Equal ( v1[Z], v2[Z], tol )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Lerp ( const Vector& v1, const Vector& v2, const float val ) { return Vector ( v1 + ( v2 - v1 ) * val ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector LinearMap ( const Vector& v1, const Vector& v2, float value, float value_min, float value_max ) { return Vector ( v1 + ( v2 - v1 ) * ( (value - value_min) / (value_max - value_min) ) ); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Min ( const Vector& v1, const Vector& v2 ) { return Vector ( Min ( v1[X], v2[X] ), Min ( v1[Y], v2[Y] ), Min ( v1[Z], v2[Z] )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Max ( const Vector& v1, const Vector& v2 ) { return Vector ( Max ( v1[X], v2[X] ), Max ( v1[Y], v2[Y] ), Max ( v1[Z], v2[Z] )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Min ( const Vector& v, float c ) { return Min ( v, Vector ( c, c, c, 1.0f )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector Max ( const Vector& v, float c ) { return Max ( v, Vector ( c, c, c, 0.0f )); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Swap ( Vector& a, Vector& b ) { Vector t = a; a = b; b = t; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Vector& Vector::ZeroIfShorterThan ( float length ) { if (LengthSqr() < length *length) { Set(0.0f,0.0f,0.0f,0.0f); } return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline ostream& operator<< ( ostream& os, const Vector& v ) { return os << "( " << v[X] << ", " << v[Y] << ", " << v[Z] << ", " << v[W] << " )"; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Mth #endif // __CORE_MATH_VECTOR_INL ================================================ FILE: Code/Core/String/CString.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core library ** ** ** ** Module: String ** ** ** ** File name: Core\String\CString.cpp ** ** ** ** Created by: 9/20/2000 - rjm ** ** ** ** Description: A handy, safe class to represent a string ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Str { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ const int String::s_max_size = 256; /***************************************************************************** ** Public Data ** *****************************************************************************/ int String::sDumbCount = 0; /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ /****************************************************************************** * * Function: * * Description: Default constructor * * Parameters: * *****************************************************************************/ String::String( void ) : mp_string( NULL ) { // m_dumbNum = sDumbCount; sDumbCount++; } /****************************************************************************** * * Description: First copy constructor * (called by: String x = "blah";) * *****************************************************************************/ String::String( const char* string ) : mp_string ( NULL ) { // m_dumbNum = sDumbCount; sDumbCount++; // call operator= function *this = string; //Ryan("copy successful\n"); } /****************************************************************************** * * Description: Second copy constructor * *****************************************************************************/ String::String( const String& string ) : mp_string ( NULL ) { // // call operator= function *this = string; m_dumbNum = sDumbCount; sDumbCount++; } /****************************************************************************** * * Description: destructor * *****************************************************************************/ String::~String( void ) { // //Ryan("Am deleting string"); if (mp_string) { delete [] mp_string; mp_string = NULL; } //Ryan("\n"); sDumbCount--; } /******************************************************************/ /* */ /* */ /******************************************************************/ String & String::operator= (const char * string) { // int last_char = 0; if ( string ) { last_char = strlen(string) - 1; // Fix the case where "string" is the empty string if( last_char < 0 ) { last_char = 0; } } copy(string, 0, last_char); //Ryan("Copying %s to String\n", mp_string); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ String & String::operator= (const String & string) { // Dbg_MsgAssert(this != &string,( "can't assign a String to itself")); // call other operator= function *this = string.mp_string; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ bool String::operator== (const char * string) { // //Ryan("comparing String to string: %s to %s\n", mp_string, string); if (mp_string && string) { return (strcmp(mp_string, string) == 0); } return (!mp_string && !string); } /******************************************************************/ /* */ /* */ /******************************************************************/ bool String::operator== (const String & string) { // //Ryan("comparing String to String: %s to %s\n", mp_string, string.mp_string); return (*this == string.mp_string); } /******************************************************************/ /* */ /* */ /******************************************************************/ bool String::operator!() { return (mp_string == NULL); } /****************************************************************************** * * Description: Returns pointer to internal char * array. Maybe should be const. * *****************************************************************************/ const char * String::getString() const { // return mp_string; } /******************************************************************/ /* */ /* */ /******************************************************************/ void String::copy(const char *pChar, int first_char, int last_char) { if ( pChar && last_char >= first_char) { // GJ: The following handles 1-byte long strings incorrectly: // if ( first_char == last_char ) if ( pChar[first_char] == 0 ) { if (mp_string) { delete [] mp_string; } mp_string = new char[1]; mp_string[0] = 0; m_length = 2; // MICK-GJ: Set length to 2, so it matches what we have below } else { int length = last_char - first_char + 2; if (length < 4) length = 4; Dbg_MsgAssert (length <= s_max_size,( "string too long for String object")); // if current string exists and is smaller than new string, delete it if ( mp_string && ( length > m_length )) { delete [] mp_string; mp_string = NULL; } // if current string doesn't exist, or has been deleted, make a new one if ( !mp_string ) { m_length = length; mp_string = new char[m_length]; } // perform string copy int i = 0; int j; for (j = first_char; j <= last_char; j++) { mp_string[i] = pChar[j]; i++; } mp_string[i] = '\0'; } } else { if ( mp_string ) { delete [] mp_string; mp_string = NULL; } } //Ryan("copied string %s\n", pChar); } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace String ================================================ FILE: Code/Core/String/CString.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core library ** ** ** ** Module: String ** ** ** ** File name: Core\String\CString.h ** ** ** ** Created by: 9/20/2000 - rjm ** ** ** ** Description: A handy, safe class to represent a string ** ** ** *****************************************************************************/ #ifndef __CORE_STRING_STRING_H #define __CORE_STRING_STRING_H /***************************************************************************** ** Includes ** *****************************************************************************/ #ifndef __CORE_SUPPORT_CLASS_H #include #endif /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Str { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /**************************************************************************** * * Class: String * * Description: A handy, safe class for representing a string. * * Usage: String x = "try our new diet of pinecones"; * String y = x; * printf("%s\n", y.getString()); * ****************************************************************************/ class String : public Spt::Class // fix bug delete[] !!! { public: String(); String(const char *string); String(const String &string); ~String(); String& operator= (const char * string); String& operator= (const String & string); bool operator== (const char * string); bool operator== (const String & string); bool operator!(); const char* getString() const; void copy(const char *pChar, int first_char, int last_char); int m_dumbNum; static int sDumbCount; private: char* mp_string; int m_length; static const int s_max_size; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace String #endif // __CORE_STRING_STRING_H ================================================ FILE: Code/Core/String/stringutils.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: ** ** ** ** Module: String ** ** ** ** File name: stringutils.cpp ** ** ** ** Created by: 05/31/01 - Mick ** ** ** ** Description: useful string (char *) utilities ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Str { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ #define Caseless(letter) (((letter)>='A' && (letter)<='Z')?((letter)+'a'-'A'):letter) // Returns location of Needle in the Haystack const char * StrStr (const char *pHay, const char *pNeedle) { register char First = *pNeedle++; // get first letter in needle if (!First) return 0; // if end of needle, then return NULL First = Caseless(First); while (1) { while (1) // scan Haystack one letter at a time { char FirstHay = *pHay++; // get first letter of haystack FirstHay = Caseless(FirstHay); if (FirstHay == First) break; // first letter matches, so drop into other code if (!FirstHay) return 0; // if end of haystack, then return NULL } const char *pH = pHay; // second letter of hay const char *pN = pNeedle; // second letter of needle while (1) { char n = *pN++; // get a needle letter if (!n) return pHay-1; // end of needle, so return pointer to start of string in haystack char h = *pH++; // get a hay letter if (Caseless(n) != Caseless(h)) break; // difference, so stop looping (could be end of haystack, which is valid) } } } void LowerCase(char *p) { while (*p) { char letter = *p; if (letter>='A' && letter<='Z') letter+='a'-'A'; *p++ = letter; } } void UpperCase(char *p) { while (*p) { char letter = *p; if (letter>='a' && letter<='z') letter-='a'-'A'; *p++ = letter; } } int WhiteSpace(char *p) { if (*p == 0x20 || *p== 0x09) return 1; else return 0; } char *PrintThousands(int n, char c) { static char buffer[32]; // enough for any int char normal[32]; // printf in here without commas char *p = buffer; if (n<0) { *p++ = '-'; n = -n; } sprintf (normal, "%d", n); int digits = strlen(normal); char *p_digit = normal; // Since other countries deliminate their successive kilos differently switch (Config::GetLanguage()) { case Config::LANGUAGE_FRENCH: c=' '; break; case Config::LANGUAGE_SPANISH: c='.'; break; case Config::LANGUAGE_GERMAN: c='.'; break; case Config::LANGUAGE_ITALIAN: c='.'; break; default: c=','; break; } while (digits) { *p++ = *p_digit++; digits--; if ( digits && ( digits % 3) == 0) { *p++ = c; } } *p++ = 0; // printf ("%s\n",buffer); return buffer; } // helper function for SText::Draw(), SFont::QueryString() uint DehexifyDigit(const char *pLetter) { uint digit = 0; if (*pLetter >= '0' && *pLetter <= '9') digit = *pLetter - '0'; else if (*pLetter >= 'a' && *pLetter <= 'v') digit = *pLetter - 'a' + 10; else if (*pLetter >= 'A' && *pLetter <= 'V') digit = *pLetter - 'A' + 10; else { Dbg_MsgAssert(0, ("Non Hex digit")); } return digit; } // Returns // -1 if ab // // // if int Compare(const char *p_a, const char *p_b) { for (;;) { char a = *p_a++; char b = *p_b++; a = Caseless(a); b = Caseless(b); if (a < b) { return -1; } if (a > b) { return 1; } if (!a && !b) { // strings are identical return 0; } if (!a) { // a is shorter, so a < b return -1; } if (!b) { // b is shorter, so b < a return 1; } } } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Str ================================================ FILE: Code/Core/String/stringutils.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: ** ** ** ** Module: String ** ** ** ** File name: stringutils.h ** ** ** ** Created by: 05/31/01 - Mick ** ** ** ** Description: useful string (char *) utilities ** ** ** *****************************************************************************/ #ifndef __CORE_STRING_STRINGUTILS_H #define __CORE_STRING_STRINGUTILS_H /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Str { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ const char* StrStr(const char* pHay, const char* pNeedle); void LowerCase(char* p); void UpperCase(char* p); int WhiteSpace(char* p); char * PrintThousands(int n, char c = ','); uint DehexifyDigit(const char *pLetter); int Compare(const char *p_a, const char *p_b); /***************************************************************************** ** Inline Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Str #endif // __DIR_SUBDIR_FILE_H ================================================ FILE: Code/Core/StringHashTable.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core library ** ** ** ** Module: HashTable ** ** ** ** File name: Core\HashTable.h ** ** ** ** Created by: 9/22/2000 - rjm ** ** ** ** Description: A handy Hashtable class ** ** ** *****************************************************************************/ #ifndef __CORE_LIST_STRINGHASHTABLE_H #define __CORE_LIST_STRINGHASHTABLE_H #ifndef __PLAT_WN32__ #include #endif #include "HashTable.h" namespace Crc { uint32 GenerateCRCFromString( const char *pName ); } namespace Script { // So as not to require inclusion of checksum.h #ifdef __NOPT_ASSERT__ const char *FindChecksumName(uint32 checksum); #endif } /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Lst { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ template class StringHashTable : public HashTable<_V> { public: StringHashTable(int numBits); bool PutItem(const char *stringKey, _V *item); _V * GetItem(const char *stringKey, bool assert_if_clash = true); void FlushItem(const char *stringKey); }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ template //inline StringHashTable<_V>::StringHashTable(int numBits) : HashTable<_V>(numBits) { } template //inline bool StringHashTable<_V>::PutItem(const char *stringKey, _V *item) { uint32 key = Crc::GenerateCRCFromString(stringKey); return HashTable<_V>::PutItem(key, item); } template //inline _V *StringHashTable<_V>::GetItem(const char *stringKey, bool assert_if_clash) { uint32 key = Crc::GenerateCRCFromString(stringKey); return HashTable<_V>::GetItem(key, assert_if_clash); } template //inline void StringHashTable<_V>::FlushItem(const char *stringKey) { uint32 key = Crc::GenerateCRCFromString(stringKey); HashTable<_V>::FlushItem(key); } } // namespace Lst #endif // __CORE_LIST_STRINGHASHTABLE_H ================================================ FILE: Code/Core/Support/Lock.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Support (SPT) ** ** ** ** File name: core/support/lock.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_LOCK_H #define __CORE_LOCK_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Spt { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ class Access : public Spt::Class { public : Access(); void Forbid( void ); void Permit( void ); void Enable( void ); bool HaveAccess( void ); private : sint m_count; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ inline Access::Access() : m_count( 0 ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Access::Forbid( void ) { m_count++; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Access::Permit( void ) { m_count--; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Access::Enable( void ) { m_count = 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Access::HaveAccess ( void ) { return( m_count == 0 ); } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Spt #endif // __CORE_LOCK_H ================================================ FILE: Code/Core/Support/Ref.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Support (SPT) ** ** ** ** File name: core/support/lock.h ** ** ** ** Created: 03/16/00 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_REF_H #define __CORE_REF_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Spt { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ class Ref : public Spt::Class { public : Ref(); Ref( const Ref& rhs ); void Acquire( void ); void Release( void ); bool InUse( void ); private : sint m_count; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ inline Ref::Ref () : m_count( 0 ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Ref::Ref( const Ref& rhs ) : m_count( 0 ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Ref::Acquire ( void ) { m_count++; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void Ref::Release ( void ) { Dbg_MsgAssert(m_count>0,("Tried to call Release when m_count was already zero")); m_count--; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool Ref::InUse ( void ) { return ( m_count != 0 ); } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Spt #endif // __CORE_REF_H ================================================ FILE: Code/Core/Support/class.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Support (SPT) ** ** ** ** File name: class.cpp ** ** ** ** Created by: 06/10/99 - mjb ** ** ** ** Description: Base class from which all other classes are derived. ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ #define NUM_ADDRESS 0 // Change this to 8 to have a handy list of addresses to check. // If set to 0, no extra code/data will be generated. // Remember to change to 8, compile, then get the addresses in question // as the act of adding this code/data will change your addresses. #if NUM_ADDRESS > 0 uint32 gAddress[NUM_ADDRESS] = { 0x81701061, 0x81700ee1, 0x81700d61, 0x81700be1, 0x81700a61, 0x817008e1, 0x81700761, 0x817005e1 }; static void check_address( void * p, int size ) { for ( int lp = 0; lp < 8; lp++ ) { if ( gAddress[lp] == ((uint32)p) ) { Dbg_Message( "We found the address we're looing for: 0x%08x (%d)\n", (uint32)p, size ); } } } #else #define check_address(a,b) #endif #ifdef __PLAT_NGC__ /******************************************************************/ /* */ /* */ /******************************************************************/ static inline void* operator new( size_t size ) { // Dbg_SetPlacementNew( false ); void * rv = Mem::Manager::sHandle().New( size, true ); check_address( rv, size ); return rv; } /******************************************************************/ /* */ /* */ /******************************************************************/ static inline void* operator new[] ( size_t size ) { // Dbg_SetPlacementNew( false ); void * rv = Mem::Manager::sHandle().New( size, true ); check_address( rv, size ); return rv; } /******************************************************************/ /* */ /* */ /******************************************************************/ static inline void operator delete( void* pAddr ) { Mem::Manager::sHandle().Delete( pAddr ); } /******************************************************************/ /* */ /* */ /******************************************************************/ static inline void operator delete[]( void* pAddr ) { Mem::Manager::sHandle().Delete( pAddr ); } #endif // __PLAT_NGC__ namespace Spt { /******************************************************************/ /* new operators - same as global + zero-ed memory */ /* */ /******************************************************************/ #if (defined ( ZERO_CLASS_MEM ) && !defined ( __PLAT_WN32__ )) void* Class::operator new( size_t size ) { void* p_ret = Mem::Manager::sHandle().New( size ); if ( p_ret ) { memset ( p_ret, 0, size ); } check_address( p_ret, size ); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void* Class::operator new[] ( size_t size ) { void* p_ret = Mem::Manager::sHandle().New( size ); if ( p_ret ) { memset ( p_ret, 0, size ); } check_address( p_ret, size ); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void* Class::operator new( size_t size, bool assert_on_fail ) { void* p_ret = Mem::Manager::sHandle().New( size, assert_on_fail ); if ( p_ret ) { memset ( p_ret, 0, size ); } check_address( p_ret, size ); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void* Class::operator new[] ( size_t size, bool assert_on_fail ) { void* p_ret = Mem::Manager::sHandle().New( size, assert_on_fail ); if ( p_ret ) { memset ( p_ret, 0, size ); } check_address( p_ret, size ); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void* Class::operator new( size_t size, Mem::Allocator* pAlloc, bool assert_on_fail ) { void* p_ret = Mem::Manager::sHandle().New( size, assert_on_fail, pAlloc ); if ( p_ret ) { memset ( p_ret, 0, size ); } check_address( p_ret, size ); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void* Class::operator new[]( size_t size, Mem::Allocator* pAlloc, bool assert_on_fail ) { void* p_ret = Mem::Manager::sHandle().New( size, assert_on_fail, pAlloc ); if ( p_ret ) { memset ( p_ret, 0, size ); } check_address( p_ret, size ); return p_ret; } /******************************************************************/ /* */ /* */ /******************************************************************/ void* Class::operator new( size_t size, void* pLocation ) { if ( pLocation ) { memset ( pLocation, 0, size ); } return pLocation; } /******************************************************************/ /* */ /* */ /******************************************************************/ void* Class::operator new[]( size_t size, void* pLocation ) { if ( pLocation ) { memset ( pLocation, 0, size ); } return pLocation; } /******************************************************************/ /* */ /* */ /******************************************************************/ #endif } // namespace Spt ================================================ FILE: Code/Core/Support/class.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Support (SPT) ** ** ** ** Created: 06/10/99 mjb ** ** ** ** File name: core/support/class.h ** ** ** ** Description: Base class from which classes are derived if they want ** ** their memory zeroed when instantiated (via new) ** ** ** *****************************************************************************/ #ifndef __CORE_SUPPORT_CLASS_H #define __CORE_SUPPORT_CLASS_H #ifndef __CORE_DEFINES_H #include #endif /***************************************************************************** ** Defines ** *****************************************************************************/ #define ZERO_CLASS_MEM TRUE namespace Mem { class Allocator; } namespace Spt { class Class { public: #if (defined ( ZERO_CLASS_MEM ) && !defined ( __PLAT_WN32__ )) void* operator new( size_t size ); void* operator new[] ( size_t size ); void* operator new( size_t size, bool assert_on_fail ); void* operator new[] ( size_t size, bool assert_on_fail ); void* operator new( size_t size, Mem::Allocator* pAlloc, bool assert_on_fail = true ); void* operator new[]( size_t size, Mem::Allocator* pAlloc, bool assert_on_fail = true ); void* operator new( size_t size, void* pLocation ); void* operator new[]( size_t size, void* pLocation ); #endif // ZERO_CLASS_MEM }; } // namespace Spt #endif // __CORE_SUPPORT_CLASS_H ================================================ FILE: Code/Core/Support/support.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Support (SPT) ** ** ** ** Created: 06/10/99 mjb ** ** ** ** File name: core/support/support.h ** ** ** ** Description: template class for tracking class hierarchy ** ** ** *****************************************************************************/ #ifndef __CORE_SUPPORT_SUPPORT_H #define __CORE_SUPPORT_SUPPORT_H /***************************************************************************** ** Includes ** *****************************************************************************/ #ifdef __PLAT_XBOX__ #include #else #include #endif #include /***************************************************************************** ** Defines ** *****************************************************************************/ //#define class _c : public Spt::Class class _c : public Spt::Class //#define class _c : public _b class _c : public _b #define nTemplateBaseClass(_t,_c) \ template< class _t > \ class _c : public Spt::Class \ #define nTemplateBaseClass2(_t1,_t2,_c) \ template< class _t1, class _t2 > \ class _c : public Spt::Class \ #define nTemplateBaseClass3(_t1,_t2,_t3,_c) \ template< class _t1, class _t2, class _t3 > \ class _c : public Spt::Class \ #define nTemplateSubClass(_t,_c,_b) \ template< class _t > \ class _c : public _b \ #define nTemplateSubClass2(_t1,_t2,_c,_b) \ template< class _t1, class _t2 > \ class _c : public _b \ #define nTemplateSubClass3(_t1,_t2,_t3,_c,_b) \ template< class _t1, class _t2, class _t3 > \ class _c : public _b \ #define Dbg_TemplateBaseClass(_t1,_c) #define Dbg_TemplateSubClass(_t1,_c,_b) #define Dbg_TemplateBaseClass(_t1,_c) #define Dbg_TemplateSubClass(_t1,_c,_b) #define Dbg_TemplateBaseClass2(_t1,_t2,_c) #define Dbg_TemplateSubClass2(_t1,_t2,_c,_b) #endif // __CORE_SUPPORT_SUPPORT_H ================================================ FILE: Code/Core/Task/Hook.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Task (TSK_) ** ** ** ** File name: core/task/hook.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_TASK_HOOK_H #define __CORE_TASK_HOOK_H /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Tsk { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /*********************************************************************** * * Class: BaseHook * * Description: abstract base class for code hook * defines minimal interface * ***********************************************************************/ class BaseHook : public Spt::Class { public : virtual void Call( void ) const = 0; protected : BaseHook( void ) {} }; /*********************************************************************** * * Class: Hook< _T > * * Description: derived template class for hook with typed data field * ***********************************************************************/ nTemplateSubClass( _T, Hook, BaseHook ) { public : typedef void ( Code )( const Hook< _T >& ); Hook( Code* code, _T& data ); void Call( void ) const; _T& GetData( void ) const; private : Code* const code; _T& data; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ template < class _T > inline Hook< _T >::Hook( Code* _code, _T& _data ) : code( _code ), data( _data ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Hook< _T >::Call( void ) const { Dbg_AssertPtr( code ); code( *this ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline _T& Hook< _T >::GetData( void ) const { Dbg_AssertType( &data, _T ); return data; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Tsk #endif // __CORE_TASK_HOOK_H ================================================ FILE: Code/Core/Task/List.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Task (TSK_) ** ** ** ** File name: core/task/list.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_TASK_LIST_H #define __CORE_TASK_LIST_H /***************************************************************************** ** Includes ** *****************************************************************************/ #ifndef __CORE_DEFINES_H #include #endif #include #include "core/support/lock.h" /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Tsk { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ class BaseTask; // forward declaration class List : public Spt::Access { public : List ( void ); ~List ( void ); void RemoveAllTasks ( void ); void Process ( bool time, uint mask ); void Dump ( void ); void AddTask ( BaseTask& task ); bool IsEmpty ( void ); void SignalListChange( void ); private : Lst::Head list; uint stamp; bool list_changed; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ inline bool List::IsEmpty ( void ) { return list.IsEmpty(); } } // namespace Tsk #endif // __CORE_TASK_LIST_H ================================================ FILE: Code/Core/Task/Stack.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Task (TSK_) ** ** ** ** File name: core/task/stack.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_TASK_STACK_H #define __CORE_TASK_STACK_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Tsk { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ class Stack : public Spt::Access { public : Stack ( void ); ~Stack ( void ); void Push ( void ); void Pop ( void ); void Process ( bool time = false); void Dump ( void ); void AddTask ( BaseTask& task ); void AddPushTask ( BaseTask& task ); void RemoveAllPushTasks ( void ); void RemoveAllTasks ( void ); void SetMask(uint mask); private : uint m_mask_off; // mask to supress execution of tasks void remove_dead_entries( void ); class StackElement; // forward declaration Lst::Head< StackElement > m_stack; Lst::Head< StackElement > m_dead; // dead element list StackElement* m_run; // current run task list StackElement* m_add; // current add task list // these get reset when Process() is called bool m_pushedList; bool m_poppedList; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ inline void Stack::SetMask(uint mask) { m_mask_off = mask; } } // namespace Tsk #endif // __CORE_TASK_STACK_H ================================================ FILE: Code/Core/Task/Task.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Task Manager (TSK) ** ** ** ** File name: task.cpp ** ** ** ** Created by: 05/27/99 - mjb ** ** ** ** Description: Task Support ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Tsk { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ BaseTask::BaseTask( Node::Priority priority ) : tlist(NULL), stamp(0) { m_mask = 0; // default to "No type of thing", so it will always run node = new Node( this, priority ); Dbg_AssertType( node, Node ); } /******************************************************************/ /* */ /* */ /******************************************************************/ BaseTask::~BaseTask () { if ( tlist ) { Remove(); } Dbg_AssertType( node, Node ); delete node; } /******************************************************************/ /* */ /* */ /******************************************************************/ void BaseTask::Remove( void ) { if ( tlist ) { tlist->Forbid(); tlist->SignalListChange(); node->Remove(); tlist->Permit(); tlist = NULL; } else { Dbg_Warning( "Task is not in a List" ); } } /******************************************************************/ /* */ /* */ /******************************************************************/ bool BaseTask::InList( void ) const { return node->InList(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void BaseTask::SetPriority( Node::Priority pri ) const { node->SetPri( pri ); } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Tsk ================================================ FILE: Code/Core/Task/Task.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Task (TSK_) ** ** ** ** File name: core/task/task.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_TASK_TASK_H #define __CORE_TASK_TASK_H /***************************************************************************** ** Includes ** *****************************************************************************/ #ifndef __CORE_DEFINES_H #include #endif #include #include /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Tsk { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ class BaseTask : public Spt::Access { friend class List; public: typedef Lst::Node< BaseTask > Node; bool InList( void ) const; void Remove( void ); virtual void vCall( void ) const = 0; virtual void * GetCode(void) const = 0; void SetPriority( Node::Priority pri ) const; virtual ~BaseTask( void ); void SetMask(uint mask); uint GetMask(); protected: BaseTask( Node::Priority pri ); Node* node; // link node private: List* tlist; // task list to which we belong uint stamp; // process stamp uint m_mask; // mask indicating what types of thing this is }; /******************************************************************/ /* */ /* */ /******************************************************************/ nTemplateSubClass( _T, Task, BaseTask ) { public: typedef void (Code)( const Task< _T >& ); Task( Code* const code, _T& data, Node::Priority pri = Lst::Node< BaseTask >::vNORMAL_PRIORITY ); virtual void vCall( void ) const; _T& GetData( void ) const; virtual void * GetCode(void) const; private : Code* const code; // tasks entry point _T& data; // task defined data }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ template < class _T > inline Task< _T >::Task ( Code* const _code, _T& _data, Node::Priority pri ) : BaseTask( pri ), code( _code ), data( _data ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Task< _T >::vCall( void ) const { Dbg_AssertPtr( code ); code( *this ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline _T& Task< _T >::GetData( void ) const { Dbg_AssertType( &data, _T ); return data; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void * Task< _T >::GetCode( void ) const { return (void*)code; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void BaseTask::SetMask( uint mask ) { m_mask = mask; } inline uint BaseTask::GetMask( void ) { return m_mask; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Tsk #endif // __CORE_TASK_TASK_H ================================================ FILE: Code/Core/Task/Tlist.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Task Manager (TSK) ** ** ** ** File name: tlist.cpp ** ** ** ** Created by: 05/27/99 - mjb ** ** ** ** Description: Task List Support ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include // Including for debugging #include // Including for debugging /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Tsk { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ List* TSK_BkGndTasks = NULL; /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ List::List( void ) : stamp( 0 ), list_changed( false ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ List::~List ( void ) { Dbg_MsgAssert( list.IsEmpty(),( "Task List Not Empty" )); } /******************************************************************/ /* */ /* */ /******************************************************************/ void List::Process( bool time, uint mask ) { if ( !HaveAccess ()) { return; } stamp++; do { Lst::Search iterator; BaseTask* next = iterator.FirstItem( list ); list_changed = false; while ( next ) { BaseTask* task = next; Dbg_AssertType( task, BaseTask ); Dbg_AssertPtr( task ); Dbg_AssertPtr( task->node ); Dbg_MsgAssert( task->node->InList(),( "Task was removed from list by previous task" )); next = iterator.NextItem(); // get next task before we execute the current // one as the task might remove itself Dbg_MsgAssert(!list_changed,( "list changed, you fucker")); if ( task->HaveAccess() ) { if ( task->stamp != stamp ) { task->stamp = stamp; #ifdef __USE_PROFILER__ Tmr::MicroSeconds start_time=0; #ifdef __PLAT_NGPS__ Tmr::MicroSeconds end_time=0; #endif if (time) { start_time = Tmr::GetTimeInUSeconds(); } #endif // only call the task if it is not masked off if (! (task->GetMask() & mask)) { task->vCall(); } #ifdef __USE_PROFILER__ #ifdef __PLAT_NGPS__ if (time) { end_time = Tmr::GetTimeInUSeconds(); int length = (int)(end_time - start_time); int size; printf ("%6d %s\n",length,MemView_GetFunctionName((int)task->GetCode(), &size)); } #endif #endif } } if ( list_changed ) { break; } } } while ( list_changed ); } /******************************************************************/ // Mick Debugging: Dump out the contents of the task list // /******************************************************************/ void List::Dump( void ) { Lst::Search iterator; BaseTask* next = iterator.FirstItem( list ); list_changed = false; while ( next ) { BaseTask* task = next; Dbg_AssertType( task, BaseTask ); Dbg_AssertPtr( task ); Dbg_AssertPtr( task->node ); next = iterator.NextItem(); // get next task before we execute the current // one as the task might remove itself #ifdef __PLAT_NGPS__ if ( task->HaveAccess() ) { // char *MemView_GetFunctionName(int pc, int *p_size); // task->vCall(); int size; printf ("Task %p %s\n",task->GetCode(), MemView_GetFunctionName((int)task->GetCode(), &size)); } #elif defined( __PLAT_NGC__ ) if ( task->HaveAccess() ) { // char *MemView_GetFunctionName(int pc, int *p_size); // task->vCall(); Dbg_Printf ("Task %p\n",task->GetCode() ); // MemView_GetFunctionName((int)task->GetCode(), &size)); } #endif } } /******************************************************************/ /* */ /* */ /******************************************************************/ void List::AddTask( BaseTask& task ) { Dbg_AssertType( &task, BaseTask ); Dbg_MsgAssert( !task.node->InList(),( "Task is already in a list" )); Forbid (); // stop any processing while list is modified list.AddNode( task.node ); task.tlist = this; task.stamp = stamp - 1; list_changed = true; Permit(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void List::RemoveAllTasks( void ) { Lst::Search iterator; BaseTask* next; Forbid(); while (( next = iterator.FirstItem( list ) )) { next->Remove(); list_changed = true; } Permit(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void List::SignalListChange( void ) { list_changed = true; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Tsk ================================================ FILE: Code/Core/Task/Tstack.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Task Manager (TSK) ** ** ** ** File name: tstack.cpp ** ** ** ** Created by: 05/27/99 - mjb ** ** ** ** Description: Task Stack Support ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Tsk { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ class Stack::StackElement : public List { friend class Stack; public : StackElement( void ); ~StackElement( void ); private: Lst::Node< StackElement >* m_node; }; /***************************************************************************** ** Private Data ** *****************************************************************************/ /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ void Stack::remove_dead_entries( void ) { Lst::Search< StackElement > iterator; StackElement* dead_entry; Dbg_MsgAssert( HaveAccess(),( "List currently being processed" )); while (( dead_entry = iterator.FirstItem( m_dead ))) { delete dead_entry; } } /***************************************************************************** ** Public Functions ** *****************************************************************************/ Stack::StackElement::StackElement( void ) { m_node = new Lst::Node< StackElement >( this ); Dbg_AssertType( m_node, Lst::Node< StackElement > ); } /******************************************************************/ /* */ /* */ /******************************************************************/ Stack::StackElement::~StackElement ( void ) { Dbg_AssertType( m_node, Lst::Node< StackElement > ); delete m_node; } /******************************************************************/ /* */ /* */ /******************************************************************/ Stack::Stack ( void ) { m_pushedList = false; m_poppedList = false; Push(); // create initial list m_add = m_run; m_pushedList = false; m_poppedList = false; } /******************************************************************/ /* */ /* */ /******************************************************************/ Stack::~Stack ( void ) { Dbg_MsgAssert( HaveAccess(),( "List currently being processed" )); Dbg_Code ( if ( m_stack.CountItems() != 1 ) { Dbg_Warning( "Task Stack contains %d Task Lists", m_stack.CountItems()); } ) while ( m_stack.CountItems() ) { Pop (); } remove_dead_entries(); Dbg_MsgAssert (( m_run == NULL ),( "Task Stack not empty" )); } /******************************************************************/ /* */ /* */ /******************************************************************/ void Stack::Push( void ) { /* On entry: m_add -> top list on stack (the one being processed) m_run -> probably the same On exit: m_run -> new top list m_add -> old top list (the one being processed) */ Dbg_MsgAssert(!m_pushedList,( "push already called this frame")); m_run = new StackElement; Dbg_AssertType( m_run, StackElement ); m_stack.AddToHead( m_run->m_node ); // deferred push m_pushedList = true; } /******************************************************************/ /* */ /* */ /******************************************************************/ void Stack::Pop( void ) { /* On entry: m_add -> top list on stack (the one being processed) m_run -> top list on stack On exit: m_run and m_add -> the new top list (originally second to top) m_dead -> list to be destroyed (still being processed) */ Dbg_AssertType( m_run, StackElement ); // this assert is problematic because the task system does not call Process() on the input // task list stacks for inactive devices, though Push() and Pop() are called //Dbg_MsgAssert(!m_poppedList,( "pop already called this frame")); // remove item Lst::Search< StackElement > iterator; iterator.FirstItem( m_stack ); // must use this test, m_pushedList is not reliable if (m_add == m_run) m_add = iterator.NextItem(); else { // otherwise, current list is retained, m_add stays the same m_pushedList = false; } // m_run may have been just added this frame, but that shouldn't matter Dbg_MsgAssert( m_run->IsEmpty(),( "Tasks List not Empty" )); if( HaveAccess() ) { delete m_run; } else { m_run->m_node->Remove(); m_dead.AddToHead( m_run->m_node ); // deferred kill } m_run = m_add; m_poppedList = true; } /******************************************************************/ /* */ /* */ /******************************************************************/ void Stack::Process( bool time ) { Forbid(); Dbg_AssertType( m_run, StackElement ); m_add = m_run; // push now in effect m_run->Process( time, m_mask_off ); Permit(); remove_dead_entries(); m_pushedList = false; m_poppedList = false; } void Stack::Dump( void ) { Forbid(); Dbg_AssertType( m_run, StackElement ); m_add = m_run; // push now in effect m_run->Dump(); Permit(); remove_dead_entries(); m_pushedList = false; m_poppedList = false; } /******************************************************************/ /* */ /* */ /******************************************************************/ void Stack::AddTask( BaseTask& task ) { Dbg_AssertType( &task, BaseTask ); Dbg_AssertType( m_add, StackElement ); m_add->AddTask( task ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void Stack::AddPushTask( BaseTask& task ) { Dbg_AssertType( &task, BaseTask ); Dbg_AssertType( m_run, StackElement ); m_run->AddTask( task ); } /******************************************************************/ /* */ /* */ /******************************************************************/ void Stack::RemoveAllTasks( void ) { // we may really be removing all tasks in list about to be pushed if (m_pushedList) { RemoveAllPushTasks(); Dbg_MsgAssert( m_run->IsEmpty(),( "should now be empty" )); return; } Dbg_AssertType( m_add, StackElement ); m_add->RemoveAllTasks(); } /******************************************************************/ /* */ /* */ /******************************************************************/ void Stack::RemoveAllPushTasks( void ) { Dbg_AssertType( m_run, StackElement ); m_run->RemoveAllTasks(); } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Tsk ================================================ FILE: Code/Core/Task.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Task (TSK) ** ** ** ** File name: core/task.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_TASK_H #define __CORE_TASK_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ #endif // __CORE_TASK_H ================================================ FILE: Code/Core/Thread/ngc/t_thread.cpp ================================================ ///****************************************************************** //* //* DESCRIPTION: thread/ngps/t_thread.cpp //* //* AUTHOR: JAB //* //* HISTORY: //* //* DATE:9/1/2000 //* //*******************************************************************/ // ///** include files **/ // //#include //#include //#include ////#include // //#include "t_thread.h" // ////#ifdef __NOPT_STD_MEM__ //extern "C" int _std_stack[1]; //static int* _stack = _std_stack; ////#else ////extern "C" int _dbg_stack[1]; ////static int* _stack = _dbg_stack; ////#endif // //extern "C" int _stack_size[1]; //extern "C" int _start(void); // //Thread::PerThreadStruct _rootThreadStruct = //{ // "ROOT", // 0, // 0, // _start, // m_pEntry // _stack, // m_pStackBase // (int)_stack_size, // m_iStackSize // 0, // 0 //}; // //int Thread::dummy = -1; // // //namespace Thread //{ // Dbg_Module // //// PJR - Commented out. //// HTHREAD CreateThread( PerThreadStruct* pPTS ) //// { //// ThreadParam sParam; //// sParam.entry = pPTS->m_pEntry; //// sParam.stack = pPTS->m_pStackBase; //// sParam.stackSize = pPTS->m_iStackSize; //// //ps2memset( pPTS->m_pStackBase, 0xca, pPTS->m_iStackSize); //// sParam.initPriority = pPTS->m_iInitialPriority; //// sParam.gpReg = pPTS; ////#ifdef __THREAD_DEBUGGING_PRINTS__ //// Dbg_Message( "T::CrThread: %s, entry 0x%x, stack 0x%x, size 0x%x\n",pPTS->m_cID, (unsigned int) pPTS->m_pEntry, (unsigned int) pPTS->m_pStackBase, (unsigned int) pPTS->m_iStackSize ); ////#endif //// pPTS->m_osId = ::CreateThread( &sParam ); //// return (HTHREAD) pPTS; //// } //} ================================================ FILE: Code/Core/Thread/ngc/t_thread.h ================================================ /******************************************************************* * * DESCRIPTION: thread/ngc/t_thread.h * * AUTHOR: JAB * * HISTORY: * * DATE:9/1/2000 * *******************************************************************/ /** include files **/ #ifndef __CORE_THREAD_NGC_T_THREAD_H__ #define __CORE_THREAD_NGC_T_THREAD_H__ #include //#include namespace Thread { typedef int THREADID; // Id for use and assigned by application typedef int OSTHREADID; // Id assigned by the OS. typedef unsigned int HTHREAD; // A value that is easy to determine from the OS class PerThreadStruct { public: char m_cID[16]; // a text name so its easy to THREADID m_utid; // an id that the use can assign OSTHREADID m_osId; // an id that the OS assigns void* m_pEntry; void* m_pStackBase; int m_iStackSize; int m_iInitialPriority; void* m_pRpcData; // ptr to rpc specific data. }; // PJR - Not sure... // register PerThreadStruct* g_pCurrThread asm ("gp"); // Gotta fucking love that, eh? // inline THREADID GetCurrentThreadId() { return (g_pCurrThread) ? g_pCurrThread->m_utid : 0; } // inline HTHREAD GetCurrentHandle() { return (HTHREAD) g_pCurrThread; } // This is fastest, so use it if you want to get data fast; //inline void StartThread( HTHREAD hthr, void* arg = NULL ) { ::StartThread( ((PerThreadStruct*)hthr)->m_osId, arg ); } // So you must set up the stack, and the perthreadstruct. HTHREAD CreateThread( PerThreadStruct* pPTS ); /* Commentary on data for submodules (eg m_pRpcData): Yes there are less data dependent ways of doing this. For example, by having an array of pointers and the subsystem requests an index that it can use. But the extra work (small tho it is) seems a bit pointless when the penalty for hardcoding it is having to recompile lots of things if it changes. Not a big deal. */ extern int dummy; }; struct PTS{ char m_cID[16]; // a text name so its easy to Thread::THREADID m_utid; // an id that the use can assign Thread::OSTHREADID m_osId; // an id that the OS assigns void* m_pEntry; void* m_pStackBase; int m_iStackSize; int m_iInitialPriority; void* m_pRpcData; // ptr to rpc specific data. }; extern "C" Thread::PerThreadStruct _rootThreadStruct; #endif // __CORE_THREAD_NGC_T_THREAD_H__ ================================================ FILE: Code/Core/Thread/ngps/t_thread.cpp ================================================ /******************************************************************* * * DESCRIPTION: thread/ngps/t_thread.cpp * * AUTHOR: JAB * * HISTORY: * * DATE:9/1/2000 * *******************************************************************/ /** include files **/ #include #include #include #include "t_thread.h" extern "C" int _std_stack[8]; static int* _stack = _std_stack; extern "C" int _stack_size[8]; extern "C" int _start(void); Thread::PerThreadStruct _rootThreadStruct = { "ROOT", 0, 0, _start, // m_pEntry _stack, // m_pStackBase (int)_stack_size, // m_iStackSize 0, 0 }; int Thread::dummy = -1; typedef void (*VOID_CB)(void*); // pointer to function that takes a void pointer namespace Thread { HTHREAD CreateThread( PerThreadStruct* pPTS ) { ThreadParam sParam; /*sParam.entry = pPTS->m_pEntry;*/ sParam.entry = (VOID_CB) (pPTS->m_pEntry); sParam.stack = pPTS->m_pStackBase; sParam.stackSize = pPTS->m_iStackSize; //ps2memset( pPTS->m_pStackBase, 0xca, pPTS->m_iStackSize); sParam.initPriority = pPTS->m_iInitialPriority; sParam.gpReg = &_gp; #ifdef __THREAD_DEBUGGING_PRINTS__ Dbg_Message( "T::CrThread: %s, entry 0x%x, stack 0x%x, size 0x%x\n",pPTS->m_cID, (unsigned int) pPTS->m_pEntry, (unsigned int) pPTS->m_pStackBase, (unsigned int) pPTS->m_iStackSize ); #endif pPTS->m_osId = ::CreateThread( &sParam ); return (HTHREAD) pPTS; } } ================================================ FILE: Code/Core/Thread/ngps/t_thread.h ================================================ /******************************************************************* * * DESCRIPTION: thread/ngps/t_thread.h * * AUTHOR: JAB * * HISTORY: * * DATE:9/1/2000 * *******************************************************************/ /** include files **/ #ifndef __CORE_THREAD_NGPS_T_THREAD_H__ #define __CORE_THREAD_NGPS_T_THREAD_H__ #include #include namespace Thread { typedef int THREADID; // Id for use and assigned by application typedef int OSTHREADID; // Id assigned by the OS. typedef unsigned int HTHREAD; // A value that is easy to determine from the OS class PerThreadStruct { public: char m_cID[16]; // a text name so its easy to THREADID m_utid; // an id that the use can assign OSTHREADID m_osId; // an id that the OS assigns void* m_pEntry; void* m_pStackBase; int m_iStackSize; int m_iInitialPriority; void* m_pRpcData; // ptr to rpc specific data. }; //inline void StartThread( HTHREAD hthr, void* arg = NULL ) { ::StartThread( ((PerThreadStruct*)hthr)->m_osId, arg ); } // So you must set up the stack, and the perthreadstruct. HTHREAD CreateThread( PerThreadStruct* pPTS ); /* Commentary on data for submodules (eg m_pRpcData): Yes there are less data dependent ways of doing this. For example, by having an array of pointers and the subsystem requests an index that it can use. But the extra work (small tho it is) seems a bit pointless when the penalty for hardcoding it is having to recompile lots of things if it changes. Not a big deal. */ extern int dummy; }; struct PTS{ char m_cID[16]; // a text name so its easy to Thread::THREADID m_utid; // an id that the use can assign Thread::OSTHREADID m_osId; // an id that the OS assigns void* m_pEntry; void* m_pStackBase; int m_iStackSize; int m_iInitialPriority; void* m_pRpcData; // ptr to rpc specific data. }; extern "C" Thread::PerThreadStruct _rootThreadStruct; #endif // __CORE_THREAD_NGPS_T_THREAD_H__ ================================================ FILE: Code/Core/Thread/wn32/t_thread.cpp ================================================ /******************************************************************* * * DESCRIPTION: t_target.h * * AUTHOR: JAB * * HISTORY: * * DATE:9/5/2000 * This is a dummy file, because its in the NGPS version it has to be here too. *******************************************************************/ /** include files **/ /** local definitions **/ /* default settings */ /** external functions **/ /** external data **/ /** internal functions **/ /** public data **/ /** private data **/ /** public functions **/ /** private functions **/ ================================================ FILE: Code/Core/Thread/wn32/t_thread.h ================================================ /******************************************************************* * * DESCRIPTION: t_target.h * * AUTHOR: JAB * * HISTORY: * * DATE:9/5/2000 * This is a dummy file, because its in the NGPS version it has to be here too. *******************************************************************/ /** include files **/ /** local definitions **/ /* default settings */ /** external functions **/ /** external data **/ /** internal functions **/ /** public data **/ /** private data **/ /** public functions **/ /** private functions **/ ================================================ FILE: Code/Core/TimestampedFlag.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Standard Header ** ** ** ** File name: core/TimestampedFlag.h ** ** ** ** Created: 3/3/3 - Dan ** ** ** *****************************************************************************/ #ifndef __CORE_TIMESTAMPEDFLAG_H #define __CORE_TIMESTAMPEDFLAG_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /************************************************************************ * * * Class: TimestampedFlag * * * * Description: Flag which remembers the time of its last * * modification * * Extracted from skater.h * * * * * ***********************************************************************/ class CTimestampedFlag : public Spt::Class { public: void Set ( bool state ); void SetTrue ( ); void SetFalse ( ); void Toggle ( ); bool Get ( ); Tmr::Time GetTime ( ); Tmr::Time GetElapsedTime ( ); operator bool ( ) { return Get(); } private: bool m_state; // flag state Tmr::Time m_time; // time of last state change }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ /******************************************************************/ /* */ /* */ /******************************************************************/ inline void CTimestampedFlag::Set ( bool state ) { if (m_state == state) { return; } m_state = state; m_time = Tmr::GetTime(); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void CTimestampedFlag::SetTrue ( ) { Set(true); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void CTimestampedFlag::SetFalse ( ) { Set(false); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline void CTimestampedFlag::Toggle ( ) { Set(!m_state); } /******************************************************************/ /* */ /* */ /******************************************************************/ inline bool CTimestampedFlag::Get ( ) { return m_state; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Tmr::Time CTimestampedFlag::GetTime ( ) { return m_time; } /******************************************************************/ /* */ /* */ /******************************************************************/ inline Tmr::Time CTimestampedFlag::GetElapsedTime ( ) { return Tmr::GetTime() - GetTime(); } #endif // __CORE_TIMESTAMPEDFLAG_H ================================================ FILE: Code/Core/compress.cpp ================================================ #include #define N 4096 /* size of ring buffer */ #define F 18 /* upper limit for match_length */ #define THRESHOLD 2 /* encode string into position and length if match_length is greater than this */ #define NIL N /* index for root of binary search trees */ unsigned long int textsize = 0, /* text size counter */ codesize = 0, /* code size counter */ printcount = 0; /* counter for reporting progress every 1K bytes */ //unsigned char text_buf[N + F - 1]; //ring buffer of size N, with extra F-1 bytes to facilitate string comparison int match_position, match_length; // of longest match. These are set by the InsertNode() procedure. //int lson[N + 1], rson[N + 257], dad[N + 1]; // left & right children & parents -- These constitute binary search trees. unsigned char *text_buf; int *lson; int *rson; int *dad; #define readc() *pIn++ #define writec(x) *pOut++ = x void InitTree(void) /* initialize trees */ { int i; Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); text_buf = new unsigned char[N + F - 1]; lson = new int[N+1]; rson = new int[N+257]; dad = new int[N+1]; Mem::Manager::sHandle().PopContext(); //Mem::Manager::sHandle().TopDownHeap()); /* For i = 0 to N - 1, rson[i] and lson[i] will be the right and left children of node i. These nodes need not be initialized. Also, dad[i] is the parent of node i. These are initialized to NIL (= N), which stands for 'not used.' For i = 0 to 255, rson[N + i + 1] is the root of the tree for strings that begin with character i. These are initialized to NIL. Note there are 256 trees. */ for ( i = N + 1; i <= N + 256; i++ ) rson[i] = NIL; for ( i = 0; i < N; i++ ) dad[i] = NIL; } void DeInitTree(void) /* free up the memory */ { delete [] text_buf; delete [] lson; delete [] rson; delete [] dad; } void InsertNode(int r) /* Inserts string of length F, text_buf[r..r+F-1], into one of the trees (text_buf[r]'th tree) and returns the longest-match position and length via the global variables match_position and match_length. If match_length = F, then removes the old node in favor of the new one, because the old one will be deleted sooner. Note r plays double role, as tree node and position in buffer. */ { int i, p, cmp; unsigned char *key; cmp = 1; key = &text_buf[r]; p = N + 1 + key[0]; rson[r] = lson[r] = NIL; match_length = 0; for ( ; ; ) { if ( cmp >= 0 ) { if ( rson[p] != NIL ) { p = rson[p]; } else { rson[p] = r; dad[r] = p; return; } } else { if ( lson[p] != NIL ) { p = lson[p]; } else { lson[p] = r; dad[r] = p; return; } } for ( i = 1; i < F; i++ ) { if ( (cmp = key[i] - text_buf[p + i]) != 0 ) break; } if ( i > match_length ) { match_position = p; if ( (match_length = i) >= F ) { break; } } } dad[r] = dad[p]; lson[r] = lson[p]; rson[r] = rson[p]; dad[lson[p]] = r; dad[rson[p]] = r; if ( rson[dad[p]] == p ) { rson[dad[p]] = r; } else { lson[dad[p]] = r; } dad[p] = NIL; /* remove p */ } void DeleteNode(int p) /* deletes node p from tree */ { int q; if ( dad[p] == NIL ) return; /* not in tree */ if ( rson[p] == NIL ) q = lson[p]; else if ( lson[p] == NIL ) q = rson[p]; else { q = lson[p]; if ( rson[q] != NIL ) { do { q = rson[q]; } while ( rson[q] != NIL ); rson[dad[q]] = lson[q]; dad[lson[q]] = dad[q]; lson[q] = lson[p]; dad[lson[p]] = q; } rson[q] = rson[p]; dad[rson[p]] = q; } dad[q] = dad[p]; if ( rson[dad[p]] == p ) rson[dad[p]] = q; else lson[dad[p]] = q; dad[p] = NIL; } int Encode(char *pIn, char *pOut, int bytes_to_read, bool print_progress) { int i, c, len, r, s, last_match_length, code_buf_ptr; unsigned char code_buf[17], mask; textsize = 0; /* text size counter */ codesize = 0; /* code size counter */ printcount = 0; /* counter for reporting progress every 1K bytes */ InitTree(); /* initialize trees */ code_buf[0] = 0; /* code_buf[1..16] saves eight units of code, and code_buf[0] works as eight flags, "1" representing that the unit is an unencoded letter (1 byte), "0" a position-and-length pair (2 bytes). Thus, eight units require at most 16 bytes of code. */ code_buf_ptr = mask = 1; s = 0; r = N - F; for ( i = s; i < r; i++ ) text_buf[i] = ' '; /* Clear the buffer with any character that will appear often. */ for ( len = 0; len < F && bytes_to_read; len++ ) { c = readc(); bytes_to_read--; text_buf[r + len] = c; /* Read F bytes into the last F bytes of the buffer */ } if ( (textsize = len) == 0 ) { DeInitTree(); return 0; /* text of size zero */ } for ( i = 1; i <= F; i++ ) InsertNode(r - i); /* Insert the F strings, each of which begins with one or more 'space' characters. Note the order in which these strings are inserted. This way, degenerate trees will be less likely to occur. */ InsertNode(r); /* Finally, insert the whole string just read. The global variables match_length and match_position are set. */ do { if ( match_length > len ) match_length = len; /* match_length may be spuriously long near the end of text. */ if ( match_length <= THRESHOLD ) { match_length = 1; /* Not long enough match. Send one byte. */ code_buf[0] |= mask; /* 'send one byte' flag */ code_buf[code_buf_ptr++] = text_buf[r]; /* Send uncoded. */ } else { code_buf[code_buf_ptr++] = (unsigned char) match_position; code_buf[code_buf_ptr++] = (unsigned char) (((match_position >> 4) & 0xf0) | (match_length - (THRESHOLD + 1))); /* Send position and length pair. Note match_length > THRESHOLD. */ } if ( (mask <<= 1) == 0 ) { /* Shift mask left one bit. */ for ( i = 0; i < code_buf_ptr; i++ ) /* Send at most 8 units of */ writec(code_buf[i]); /* code together */ codesize += code_buf_ptr; code_buf[0] = 0; code_buf_ptr = mask = 1; } last_match_length = match_length; for ( i = 0; i < last_match_length && bytes_to_read; i++ ) { c = readc(); bytes_to_read--; DeleteNode(s); /* Delete old strings and */ text_buf[s] = c; /* read new bytes */ if ( s < F - 1 ) text_buf[s + N] = c; /* If the position is near the end of buffer, extend the buffer to make string comparison easier. */ s = (s + 1) & (N - 1); r = (r + 1) & (N - 1); /* Since this is a ring buffer, increment the position modulo N. */ InsertNode(r); /* Register the string in text_buf[r..r+F-1] */ } if ( (textsize += i) > printcount ) { // printf("%12ld\r", textsize); printcount += 1024; /* Reports progress each time the textsize exceeds multiples of 1024. */ } while ( i++ < last_match_length ) { /* After the end of text, */ DeleteNode(s); /* no need to read, but */ s = (s + 1) & (N - 1); r = (r + 1) & (N - 1); if ( --len ) InsertNode(r); /* buffer may not be empty. */ } } while ( len > 0 ); /* until length of string to be processed is zero */ if ( code_buf_ptr > 1 ) { /* Send remaining code. */ for ( i = 0; i < code_buf_ptr; i++ ) writec(code_buf[i]); codesize += code_buf_ptr; } if ( print_progress ) { printf(" In : %ld bytes\n", textsize); /* Encoding is done. */ printf(" Out: %ld bytes\n", codesize); printf(" Out/In: %.3f\n", (double)codesize / textsize); } DeInitTree(); return codesize; } /////////////////////////////////////////////////////////////////////////////////////// // // Decompression code, cut & pasted from pre.cpp // /////////////////////////////////////////////////////////////////////////////////////// #define RINGBUFFERSIZE 4096 /* N size of ring buffer */ #define MATCHLIMIT 18 /* F upper limit for match_length */ #define THRESHOLD 2 /* encode string into position and length */ //#define WriteOut(x) {Dbg_MsgAssert(pOut>= 1) & 256) == 0 ) { ReadInto(c); flags = c | 0xff00; /* uses higher byte cleverly */ } /* to count eight */ if ( flags & 1 ) { ReadInto(c); // putc(c, outfile); WriteOut(c); #if USE_BUFFER sTextBuf[r++] = c; r &= (RINGBUFFERSIZE - 1); #else r++; // r &= (RINGBUFFERSIZE - 1); // don't need to wrap r until it is used #endif } else { ReadInto(i); ReadInto2(j); // note, don't need to check len on this one.... i |= ((j & 0xf0) << 4); // i is 12 bit offset #if !USE_BUFFER j = (j & 0x0f) + THRESHOLD+1; // j is 4 bit length (above the threshold) unsigned char *pStream; r &= (RINGBUFFERSIZE - 1); // wrap r around before it is used pStream = pOut - r; // get base of block if ( i>=r ) // if offset > r, then pStream -= RINGBUFFERSIZE; // it's the previous block pStream += i; // add in the offset to the base r+=j; // add size to r while ( j-- ) // copy j bytes WriteOut(*pStream++); #else j = (j & 0x0f) + THRESHOLD; // j is 4 bit length (above the threshold) for ( k = 0; k <= j; k++ ) // just copy the bytes { c = sTextBuf[(i+k) & (RINGBUFFERSIZE - 1)]; WriteOut(c); sTextBuf[r++] = c; r &= (RINGBUFFERSIZE - 1); } #endif } } // int Time = (int) Tmr::ElapsedTime(basetime); // if (Time > 5) // { // printf("decomp time is %d ms, for %d bytes, %d bytes/second\n", Time,len, len * 1000 /Time ); // } return pOut; } ================================================ FILE: Code/Core/compress.h ================================================ #ifndef __CORE_COMPRESS_H #define __CORE_COMPRESS_H #ifndef __CORE_DEFINES_H #include #endif int Encode(char *pIn, char *pOut, int bytes_to_read, bool print_progress); unsigned char *DecodeLZSS(unsigned char *pIn, unsigned char *pOut, int Len); #endif ================================================ FILE: Code/Core/crc.cpp ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment. ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: PS2 ** ** ** ** Module: Core ** ** ** ** File name: Core/crc.cpp ** ** ** ** Created by: 03/05/01 - spg ** ** ** ** Description: Crc functionality ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace Crc { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Private Types ** *****************************************************************************/ /***************************************************************************** ** Private Data ** *****************************************************************************/ static uint32 CRCTable[256] = // CRC polynomial 0xedb88320 { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /***************************************************************************** ** Public Functions ** *****************************************************************************/ //////////////////////////////////////////////////////////////////////////////// // Generates a checksum from raw data, given pointer to that data, and the size // // input: // const char *stream = pointer to data // int size = number of bytes to CRC // // output: // returns uint32 = checksum of the data // if NULL pointer passed in, or zero size is given, then will return 0 // uint32 GenerateCRCCaseSensitive( const char *stream, int size ) { // A checksum of zero is used to mean no name. if(( !stream ) || ( size <= 0 )) { return 0; } // Initializing the CRC to all one bits avoids failure of detection // should entire data stream get cyclically bit-shifted by one position. // The calculation of the probability of this happening is left as // an exercise for the reader. uint32 rc = 0xffffffff; for (int i=0; i>8) & 0x00ffffff); } return rc; } /******************************************************************/ /* */ /* */ /******************************************************************/ // For use when calculating a total checksum of a bunch of chunks of data. // Currently used when saving out the replay buffer to mem card. uint32 UpdateCRC( const char *p_stream, int size, uint32 rc ) { Dbg_MsgAssert(p_stream,("NULL p_stream")); for (int i=0; i>8) & 0x00ffffff); } return rc; } /******************************************************************/ /* */ /* */ /******************************************************************/ // Generates a checksum from a name, case insensitive. uint32 GenerateCRC( const char *stream, int size ) { // A checksum of zero is used to mean no name. if(( !stream ) || ( size <= 0 )) { return 0; } // Initializing the CRC to all one bits avoids failure of detection // should entire data stream get cyclically bit-shifted by one position. // The calculation of the probability of this happening is left as // an exercise for the reader. uint32 rc = 0xffffffff; for (int i=0; i='A' && ch<='Z') { ch='a'+ch-'A'; } // Convert forward slashes to backslashes, otherwise two filenames which are // effectively the same but with different slashes will give different checksums. if (ch=='/') { ch='\\'; } rc = CRCTable[(rc^ch) & 0xff] ^ ((rc>>8) & 0x00ffffff); } return rc; } /******************************************************************/ /* */ /* */ /******************************************************************/ uint32 GenerateCRCFromString( const char *pName ) { // A checksum of zero is used to mean no name. if (!pName) { return 0; } // Initializing the CRC to all one bits avoids failure of detection // should entire data stream get cyclically bit-shifted by one position. // The calculation of the probability of this happening is left as // an exercise for the reader. uint32 rc = 0xffffffff; const char *pCh=pName; while (true) { char ch=*pCh++; if (!ch) { break; } // Convert to lower case. if (ch>='A' && ch<='Z') { ch='a'+ch-'A'; } // Convert forward slashes to backslashes, otherwise two filenames which are // effectively the same but with different slashes will give different checksums. if (ch=='/') { ch='\\'; } rc = CRCTable[(rc^ch) & 0xff] ^ ((rc>>8) & 0x00ffffff); } return rc; } // Same as above, but it's calculating the CRC of two strings added together // but the first string is already CRCed // essentially it's like jumping into GenerateGRCFromString halfway through uint32 ExtendCRCWithString( uint32 rc, const char *pName ) { if (!pName) { return rc; } const char *pCh=pName; while (true) { char ch=*pCh++; if (!ch) { break; } // Convert to lower case. if (ch>='A' && ch<='Z') { ch='a'+ch-'A'; } // Convert forward slashes to backslashes, otherwise two filenames which are // effectively the same but with different slashes will give different checksums. if (ch=='/') { ch='\\'; } rc = CRCTable[(rc^ch) & 0xff] ^ ((rc>>8) & 0x00ffffff); } return rc; } /******************************************************************/ /* */ /* */ /******************************************************************/ uint32 GetCRCTableEntry( int entry ) { Dbg_Assert(( entry < 256 ) && ( entry >= 0 )); return CRCTable[entry]; } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace Crc ================================================ FILE: Code/Core/crc.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Crc (CRC) ** ** ** ** Created: 03/05/01 spg ** ** ** ** File name: core/crc.h ** ** ** ** Description: Crc Functionality ** ** ** *****************************************************************************/ #ifndef __CORE_CRC_H #define __CORE_CRC_H /***************************************************************************** ** Includes ** *****************************************************************************/ #ifndef __CORE_DEFINES_H #include #endif /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Crc { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ uint32 GenerateCRC( const char *stream, int size ); uint32 GenerateCRCCaseSensitive( const char *stream, int size ); uint32 UpdateCRC( const char *p_stream, int size, uint32 rc=0xffffffff ); uint32 GenerateCRCFromString( const char *pName ); uint32 ExtendCRCWithString( uint32 rc, const char *pName ); uint32 GetCRCTableEntry( int entry ); /***************************************************************************** ** Inline Functions ** *****************************************************************************/ } // namespace Crc #endif // __CORE_CRC_H ================================================ FILE: Code/Core/flags.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Standard Header ** ** ** ** File name: core/flags.h ** ** ** ** Created: 10/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_FLAGS_H #define __CORE_FLAGS_H /***************************************************************************** ** Includes ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ /***************************************************************************** ** Class Definitions ** *****************************************************************************/ /************************************************************************ * * * Class: flags * * * * Description: Template class for flags. * * * * * ***********************************************************************/ template < class _T > class Flags { public: Flags ( void ); Flags ( const uint val ); void ClearAll ( void ); void Clear ( _T flag_index ); void ClearMask ( const uint& f ); void Set ( _T flag_index, bool on = true ); void SetAll ( void ); void SetMask ( const uint& mask ); void SetVal ( const uint& f ); bool TestAny ( void ) const; bool Test ( _T flag_index ) const; bool TestMask ( const uint& mask ) const; bool TestMaskAny ( const uint& mask ) const; bool TestMaskAll ( const uint& mask ) const; void Toggle ( _T flag_index ); operator uint (void) const; Flags& operator= ( const Flags& src ); Flags& operator= ( const uint& val ); private: uint flag; }; /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ template < class _T > inline Flags< _T >::Flags ( void ) : flag ( 0 ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Flags< _T >::Flags ( const uint val ) : flag ( val ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Flags< _T >::ClearAll ( void ) { flag = 0; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Flags< _T >::Clear ( _T flag_index ) { flag &= ~( 1 << static_cast< uint >( flag_index )); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Flags< _T >::ClearMask ( const uint& f ) { flag &= ~f; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Flags< _T >::Set ( _T flag_index, bool on ) { if ( on ) { flag |= ( 1 << static_cast< uint >( flag_index )); } else { flag &= ~( 1 << static_cast< uint >( flag_index )); } } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Flags< _T >::SetAll ( void ) { flag = vUINT_MAX; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Flags< _T >::SetMask ( const uint& mask ) { flag |= mask; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Flags< _T >::SetVal ( const uint& f ) { flag = f; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline bool Flags< _T >::TestAny ( void ) const { return ( flag != 0 ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline bool Flags< _T >::Test ( _T flag_index ) const { return (( flag & ( 1 << static_cast< uint >( flag_index ))) != 0 ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline bool Flags< _T >::TestMask ( const uint& mask ) const { return TestMaskAny(mask); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline bool Flags< _T >::TestMaskAny ( const uint& mask ) const { return (( flag & mask ) != 0 ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline bool Flags< _T >::TestMaskAll ( const uint& mask ) const { return (( flag & mask ) == mask ); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline void Flags< _T >::Toggle ( _T flag_index ) { flag ^= ( 1 << static_cast< uint >( flag_index )); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Flags< _T >::operator uint (void) const { return flag; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Flags< _T >& Flags< _T >::operator= ( const Flags< _T >& src ) { flag = static_cast< uint >( src.flag ); return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline Flags< _T >& Flags< _T >::operator= ( const uint& val ) { flag = val; return *this; } /******************************************************************/ /* */ /* */ /******************************************************************/ /* template < class _T > inline ostream& operator<< ( ostream& os, const Flags< _T >& src ) { return os << static_cast< uint >( src ); } */ /******************************************************************/ /* */ /* */ /******************************************************************/ #endif // __CORE_FLAGS_H ================================================ FILE: Code/Core/glue.h ================================================ #ifndef __GLUE_H__ #define __GLUE_H__ #endif // __GLUE_H__ ================================================ FILE: Code/Core/log.h ================================================ #ifndef __CORE_DEBUG_LOG_H #define __CORE_DEBUG_LOG_H namespace Log { void Init(); void AddEntry(char *p_fileName, int lineNumber, char *p_functionName, char *p_message=NULL); } #endif // #ifndef __CORE_DEBUG_LOG_H ================================================ FILE: Code/Core/macros.h ================================================ /* Useful little macros... Add any macros you want into this: */ #ifndef __USEFUL_LITTLE_MACROS_H__ #define __USEFUL_LITTLE_MACROS_H__ #ifndef __CORE_DEFINES_H #include #endif #include #define PERCENT_MULT ( ( 1.0f ) / 100.0f ) #define PERCENT( x, percent ) ( ( ( ( ( float )( x ) ) * ( ( float )( percent ) ) ) * PERCENT_MULT ) ) #define IPU (1.0f) // Inches per unit #define INCHES(x) ((float)(x)) #define INCHES_PER_SECOND(x) ((float)(x)) #define INCHES_PER_SECOND_SQUARED(x) ((float)(x)) #define FEET_TO_INCHES( x ) ( x * 12.0f ) #define FEET(x) ((float)(x*12.0f)) #define FEET_PER_MILE 5280.0f #define MPH_TO_INCHES_PER_SECOND( x ) ( ( ( x ) * FEET_PER_MILE * 12.0f ) / ( 60.0f * 60.0f ) ) #define MPH_TO_IPS( x ) MPH_TO_INCHES_PER_SECOND( x ) #define INCHES_PER_SECOND_TO_MPH( x ) ( ( x ) / 12.0f / FEET_PER_MILE * 60.0f * 60.0f ) #define IPS_TO_MPH( x ) INCHES_PER_SECOND_TO_MPH( x ) #define RADIANS_PER_SECOND_TO_RPM( x ) (9.5496f * ( x )) #define RPM_TO_RADIANS_PER_SECOND( x ) (0.10472f * ( x )) #define DEGREES_TO_RADIANS( x ) ( ( x ) * ( 2.0f * Mth::PI / 360.0f ) ) #define RADIANS_TO_DEGREES( x ) ( ( x ) / ( DEGREES_TO_RADIANS( 1.0f ) ) ) #define THE_NUMBER_OF_THE_BEAST 666 // Takes screen coordinates in 'default' screen space - 640x448 - and converts them based on PAL and system defines. // Convert from logical to SCREEN coordinates #ifdef __PLAT_XBOX__ // Unfortunately, this has to be a platform-specific macro, since it references Xbox specific global structures to determine // the width and height of the back buffer. #include #define SCREEN_CONV_X( x ) ((( x ) * NxXbox::EngineGlobals.screen_conv_x_multiplier ) + NxXbox::EngineGlobals.screen_conv_x_offset ) #define SCREEN_CONV_Y( y ) ((( y ) * NxXbox::EngineGlobals.screen_conv_y_multiplier ) + NxXbox::EngineGlobals.screen_conv_y_offset ) #else #define SCREEN_CONV_X( x ) (Config::GetHardware()==Config::HARDWARE_XBOX ? (int)((( x ) * ( 640.0f / 704.0f )) + 32 ) : Config::PAL() ? (( x ) * 512 ) / 640 : (x) ) #define SCREEN_CONV_Y( y ) (Config::GetHardware()==Config::HARDWARE_XBOX ? (( y ) + 16 ) : Config::PAL() ? (( y ) * 512 ) / 448 : (y) ) #endif // __PLAT_XBOX__ // Convert from screen to LOGICAL coordinates #define LOGICAL_CONV_X( x ) (Config::GetHardware()==Config::HARDWARE_XBOX ? (x) : Config::PAL() ? (( x ) * 640 ) / 512 : (x) ) #define LOGICAL_CONV_Y( y ) (Config::GetHardware()==Config::HARDWARE_XBOX ? (y) : Config::PAL() ? (( y ) * 448 ) / 512 : (y) ) #endif // __USEFUL_LITTLE_MACROS_H__ ================================================ FILE: Code/Core/math.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Math (MTH) ** ** ** ** File name: core/math.h ** ** ** ** Created: 11/23/99 - mjb ** ** ** ** Description: Math Library ** ** ** *****************************************************************************/ #ifndef __CORE_MATH_H #define __CORE_MATH_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include // The following overlaoded functions will make sure that if you accidently call a double version of // a trig function with a float argument, then it will simply call the float version // (double precision is very very slow on the PS2) #ifndef __PLAT_XBOX__ inline float acos( float x ) {return acosf( x );} inline float asin( float x ) {return asinf( x );} inline float atan( float x ) {return atanf( x );} inline float atan2( float x, float y){return atan2f( x , y );} inline float cos( float x ) {return cosf( x );} inline float sin( float x ) {return sinf( x );} inline float tan( float x ) {return tanf( x );} inline float fabs( float x ) {return fabsf( x );} #endif // ifndef __PLAT_XBOX__ #ifndef __CORE_DEFINES_H #include #endif #include #include #include #include #include // need quat.h before matrix, but after vector (for the GetScalar function) #include #include #include #include /******************************************************************/ /* */ /* */ /******************************************************************/ #endif // __CORE_MATH_H ================================================ FILE: Code/Core/singleton.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 2000 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Template Singleton class ** ** ** ** File name: core/singleton.h ** ** ** ** Created: 10/17/00 - mjb ** ** ** ** ** *****************************************************************************/ #ifndef __CORE_SINGLETON_H #define __CORE_SINGLETON_H /* Singletons (Documentation by Mick) A singleton is a single instance of a class which is dynamically allocated just once. To make a class such as CStats a singleton class, you do this: class CStats { // Normal Class definition goes here... ... // Constructor and destructor MUST BE PRIVATE, for it to be a singleton private: CStats( void ); ~CStats( void ); // Macro to include the singleton code DeclareSingletonClass( CStats ); } This macro just places some static functions in the class, and a static pointer to the single instance of class itself (initially NULL) You can then initialize the singleton by the line Spt::SingletonPtr< CStats > p_stats( true ); This is somewhere at a high level in the code (currently they are all in main()) The original design of our singletons had in mind that there would be reference counting of the references tot he singleton, and when it was no longer referenced, it would delete itself. So, in many places in the code, the singletons are reference by creating a "Spt::SingletonPtr", which is a smart pointer to the instance of the singleton. For example: Spt::SingletonPtr< CStats > p_stats; p_stats->Update(); In the ultimate version of this idea, the game would be comprised of many modules, systems and subsystems. Each would be a singleton. Each could be dependent on other singletons, but more that one module could depend on a particular singleton, so you coudl not rely on a module to de-initialize the singletons it needed as they might be needed elsewhere. So, each module would include a Spt::SingletonPtr for each of the modules that it needed, so when a module was created it would increase the reference on the singletons and create any that did not exists. Then when it shut down, the pointers would automatically go out of scope, and any singleton that was not referenced would automatically be deleted, so there would never be more systems active than there needed to be. However, it turned out that this was generally overkill. Most systems were initialized at the start, and those that were not, we wanted to have control over the initialization of them. The use of smart pointers here also obfuscated the code, and introduced a significant execution overhead that was totally unnecessary. So, the prefered method to access a singleton now is: CStats::Instance()->Update(); This simply references the instance directly (as it's inline), and so does no addition processing. It's easier to type as well. */ /***************************************************************************** ** Includes ** *****************************************************************************/ #ifndef __CORE_DEFINES_H #include #endif #include #ifndef __SYS_MEM_MEMPTR_H #include #endif /***************************************************************************** ** Defines ** *****************************************************************************/ namespace Spt { /***************************************************************************** ** Class Definitions ** *****************************************************************************/ template< class _T > class SingletonPtr : public Spt::Class { public: SingletonPtr( bool create = false ); virtual ~SingletonPtr( void ); #if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ )) SingletonPtr( const SingletonPtr< _T > & rhs ); SingletonPtr< _T >& operator= ( const SingletonPtr< _T >& rhs ); #else template < class _NewT > // template copy contructor SingletonPtr( const SingletonPtr< _NewT >& rhs ); // needed to support inheritance correctly template < class _NewT > SingletonPtr< _T >& operator= ( const SingletonPtr< _NewT >& rhs ); // template assignment operator #endif _T* operator-> () const; _T& operator* () const; private: Mem::Ptr< _T > mp_instance; }; /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline SingletonPtr< _T >::SingletonPtr( bool create ) : mp_instance ( _T::sSgltnInstance( create ) ) { #ifdef __NOPT_FULL_DEBUG__ SetName( const_cast< char* >( sClassNode()->GetName() )); #endif } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline SingletonPtr< _T >::~SingletonPtr() { mp_instance->sSgltnDelete(); } #if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ )) /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline SingletonPtr< _T >::SingletonPtr ( const SingletonPtr< _T >& rhs ) : mp_instance ( _T::sSgltnInstance() ) { } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline SingletonPtr< _T >& SingletonPtr< _T >::operator= ( const SingletonPtr< _T >& rhs ) { return *this; } #else /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > template < class _NewT > inline SingletonPtr< _T >::SingletonPtr< _T >( const SingletonPtr< _NewT >& rhs ) : mp_instance ( _NewT::sSgltnInstance() ) { Dbg_MsgAssert( false,( "Microsoft VC++ sucks - don't do this (yet)" )); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > template < class _NewT > inline SingletonPtr< _T >& SingletonPtr< _T >::operator= ( const SingletonPtr< _NewT >& rhs ) { Dbg_MsgAssert( false,( "Microsoft VC++ sucks - don't do this (yet)" )); return *this; } #endif /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline _T* SingletonPtr< _T >::operator-> () const { return mp_instance.Addr(); } /******************************************************************/ /* */ /* */ /******************************************************************/ template < class _T > inline _T& SingletonPtr< _T >::operator* () const { return *mp_instance; } /******************************************************************/ /* */ /* */ /******************************************************************/ #define DeclareSingletonClass(_T) \ private: \ static _T* sSgltnInstance( bool create = false ); \ static void sSgltnDelete( void ); \ \ static _T* sp_sgltn_instance; \ static uint s_sgltn_count; \ public: \ inline static void Create(void) {sp_sgltn_instance = new _T;} \ inline static _T* Instance(void) {return sp_sgltn_instance;} \ inline bool Initilized(void) {return (sp_sgltn_instance!=NULL);} \ \ friend class Spt::SingletonPtr< _T >; \ #define DefinePlacementSingletonClass(_T, _P, _N ) \ \ _T* _T::sp_sgltn_instance = NULL; \ uint _T::s_sgltn_count = 0; \ \ _T* _T::sSgltnInstance( bool create ) \ { \ \ \ if ( !sp_sgltn_instance && create ) \ { \ sp_sgltn_instance = new (_P) _T; \ } \ \ Dbg_AssertType( sp_sgltn_instance, _T ); \ \ ++s_sgltn_count; \ return sp_sgltn_instance; \ } \ \ void _T::sSgltnDelete( void ) \ { \ \ \ Dbg_MsgAssert(( s_sgltn_count > 0 ),( "Reference count imbalance" )); \ \ if ( s_sgltn_count > 1 ) \ { \ s_sgltn_count--; \ return; \ } \ \ delete sp_sgltn_instance; \ sp_sgltn_instance = NULL; \ s_sgltn_count = 0; \ } \ #define DefineSingletonClass(_T,_N) \ DefinePlacementSingletonClass(_T, (Mem::Allocator*)NULL, _N ) /***************************************************************************** ** Private Declarations ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Public Declarations ** *****************************************************************************/ /***************************************************************************** ** Public Prototypes ** *****************************************************************************/ /***************************************************************************** ** Inline Functions ** *****************************************************************************/ /***************************************************************************** ** Macros ** *****************************************************************************/ } // namespace Spt #endif // __CORE_SINGLETON_H ================================================ FILE: Code/Core/support.h ================================================ /***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: Core Library ** ** ** ** Module: Support (SPT) ** ** ** ** File name: core/support.h ** ** ** ** Created: 05/27/99 - mjb ** ** ** *****************************************************************************/ #ifndef __CORE_SUPPORT_H #define __CORE_SUPPORT_H /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include "sk/language.h" /***************************************************************************** ** Defines ** *****************************************************************************/ // #define PAL for PAL mode build. #define PALx #endif // __CORE_TASK_H ================================================ FILE: Code/Core/thread.h ================================================ /******************************************************************* * * DESCRIPTION: thread.h * * AUTHOR: JAB * * HISTORY: * * DATE:9/1/2000 * *******************************************************************/ /** include files **/ #ifndef __CORE_DEFINES_H #include #endif #ifdef __PLAT_WN32__ #else # ifdef __PLAT_XBOX__ # else # ifdef __PLAT_NGPS__ # include "thread/ngps/t_thread.h" # elif defined( __PLAT_NGC__ ) # include "thread/ngc/t_thread.h" # else # error Unsupported Platform # endif # endif #endif ================================================ FILE: Code/GameFlow.txt ================================================ Flow of control ===================================== "autolaunch level=Load_Sch game=career" script autolaunch script request_level level= ScriptRequestLevel -set Skate::m_requested_level script cleanup_before_loading_level ScriptCleanup -skate_mod->Cleanup( ) -skate_mod->GetGoalManager()->RemoveAllGoals() -particle_manager->DestroyAllSystems(); -ParticleFX::BloodSplatDestroy(); -SkateScript::ClearNodeNameHashTable(); -ParticleFX::DestroyTerrainEffectProperties(); -gfx_man->DisableFog(); -Script::KillStoppedScripts(); -sk3_sfx_manager->Reset( ); -sfx_manager->CleanUp( ); -ass_manager->UnloadAllTables( ); -Script::CSymbolTableEntry *p_sym=Script::LookUpSymbol("NodeArray"); -if (p_sym) SkateScript::UnloadQB(p_sym->mSourceFileNameChecksum); -SkateScript::UnloadQB(s_level_specific_qb); -ScriptUnloadAllLevelGeometry(NULL,NULL); -skate_mod->UnloadSkaters(); -Mem::Manager::sHandle().DeleteSkaterHeaps(); script load_requested_level level=load_sch script LoadLevel script PreLevelLoad -does nothing ScriptLaunchLevel level=load_sch -Skate::OpenLevel() -Script::RunScript(level_script); script Load_Sch script load_level level_sch -see below.... PostLevelLoad -does nothing gameflow StandardGameFlow skate_mod->mp_gameFlow->Reset( ScriptChecksum ) -set m_requestedScript Later... CGameFlow::Update() GameFlow_Startup -reset panel, sound stuff -maybe enter free skate in network game -choose game mode -Put skaters in world -Set screen mode -Run intro script -repeat: GameFlow_StartRun GameFlow_PlayRun GameFlow_WaitEnd -and other shit ===================================== ----------------------- "load_level level_sch" Level_Sch = { loading_screen = "loadscrn_generic" pre = "sch.pre" scnpre = "schscn.pre" Level = "sch" Sky = "sf2_Sky" qb = "levels\sch\sch.qb" colpre = "schcol.pre" level_qb = "levels\sch\sch_scripts.qb" startup_script = sch_Startup default_stats = 5 } Level_Sk4Ed = { loading_screen = "loadscrn_generic" pre = "sk4ed.pre" scnpre = "sk4edscn.pre" Level = "sk4ed" Sky = "can_Sky" qb = "levels\sk4ed\sk4ed.qb" colpre = "sk4edcol.pre" level_qb = "levels\sk4ed\sk4ed_scripts.qb" startup_script = Sk4Ed_Startup default_stats = 8 level_number = LevelNum_Lon park_editor } ----------------------- script load_level level_sch DisplayLoadingScreen *** Park editor yes *** script LoadLevelPreFile ScriptLoadPreFile *** Park editor, need special flag *** ScriptLoadScene scene= (for sky, then world) Nx::CTexDictManager::sLoadTextureDictionary(p_scene_name) Nx::CEngine::sLoadScene(p_scene_name, p_tex_dict); ScriptUnloadPreFile *** Park editor maybe *** script LoadLevelPreFile ScriptLoadCollision scene= p_scene->LoadCollision(p_scene_name) ScriptUnloadPreFile *** Park editor yes *** script LoadLevelPreFile
	ScriptLoadQB 
		SkateScript::LoadQB
			Script::LoadQB
				-load it into memory
				ParseQB()
				-restart all scripts referring to symbols
			-do symbol table processing
				-do following just for NodeArray symbol
				- can reference nodes by name or prefix
				CreateNodeNameHashTab
				GeneratePrefixInfo();
	*** don't call ScriptParseNodeArray for park editor, I don't think ***
	*** duplicate some functionality ***
	ScriptLoadNodeArray 
		SkateScript::LoadQB (see above)
		ScriptParseNodeArray
			-does a lot of stuff...
	LoadTerrain
		-doesn't seem to exist
	CC_Startup
		-loads some test camera stuff
	run  (sch_startup)       
	ScriptUnloadPreFile




================================================
FILE: Code/Gel/AssMan/AssMan.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/AssMan
//* FILENAME:       AssMan.cpp
//* OWNER:          Matt Duncan
//* CREATION DATE:  11/17/2000
//****************************************************************************

// start autoduck documentation
// @DOC Assman
// @module Assman | None
// @subindex Scripting Database
// @index script | Assman

/*

	Assets are generally binary files that are processed in some way when loaded
	(although they might simply be loaded).
	
	They are always referenced by the 32-bit checksum of the name of the file

	Design and art guidelines already require that all filenames in the project 
	are unique names.

	The asset manager should handle generic assets...  (it shouldn't need
	to know anything about texture dictionaries or clumps or anims...)
*/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 							
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Ass
{

DefineSingletonClass( CAssMan, "Shared Asset Manager" );

struct SAssetLookup
{
   	char*		p_extension;
	EAssetType	type;
};

static SAssetLookup s_asset_lookup[] = 
{
	{"BIN",		ASSET_BINARY},
	{"CLD",		ASSET_COLLISION},
	{"SCN",		ASSET_SCENE},
	{"MDL",		ASSET_SKIN},
	{"SKA",		ASSET_ANIM},
	{"FAM",		ASSET_ANIM}, 	// facial anims
    {"SKE",		ASSET_SKELETON},
	{"SKIN",	ASSET_SKIN},
	{"TEX",		ASSET_TEXTURES},
	{"CUT",		ASSET_CUTSCENE},
	{"QB",		ASSET_NODEARRAY},
	
// Insert new types above this line	
	{NULL,		ASSET_UNKNOWN}		   				// terminator;
};
	
/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAssMan::CAssMan()
{
	mp_asset_table = new Lst::HashTable( 10 );

	// for debugging
	mp_assetlist_head = new CAsset;
	mp_assetlist_head->SetText("Asset List Head (Possible error here?)");	
	
	mp_assetlist_head->mp_next = mp_assetlist_head;
	mp_assetlist_head->mp_prev = mp_assetlist_head;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAssMan::~CAssMan()
{	
	UnloadAllTables( true );

	delete mp_asset_table;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAssetType CAssMan::FindAssetType( const char *p_assetName )
{
	// given a file name, then find the type of asset that this represents
	
	// the asset type is determined by the file extension
	// so first, find the last . in the assetName
	const char *p_ext = p_assetName + strlen(p_assetName);
	while ( p_ext > p_assetName && *p_ext != '.' )
	{
		p_ext--;
	}
	
	if ( *p_ext != '.' )
	{
		return ASSET_UNKNOWN;
	}
	
	p_ext++;
	// p_ext now points to the extension for the file name

	SAssetLookup *p_lookup = &s_asset_lookup[0];
	while ( p_lookup->p_extension != NULL )
	{
		// note, ignoring case
		if ( strcmpi( p_lookup->p_extension, p_ext ) == 0 )	
		{
			return p_lookup->type;
		}
		p_lookup++;
	}
	
	return ASSET_UNKNOWN;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void* CAssMan::LoadAssetFromStream(uint32 asset_name, uint32 asset_type, uint32* p_data, int data_size, bool permanent, uint32 group)
{
	// Note:  There's no reason to support async loads when
	// loading an asset from a data stream...

	// load an asset, and return a pointer to the asset data

	// given an asset name checksum, find the type of the asset, and then load it

	Dbg_MsgAssert(!AssetAllocated(asset_name),("Asset %08x already loaded",asset_name));

	CAsset* p_asset = NULL;	
		
	// based on the asset type, create an asset of the correct type	
	switch (asset_type)
	{
		case ASSET_ANIM:
			p_asset = new CAnimAsset;
			break;

		case ASSET_SKIN:
			p_asset = new CSkinAsset;
			break;

		default:
			// right now, the only file type that's supported
			// for data streams is the "cutscene model"
			Dbg_MsgAssert(0,("Asset %08x is of unsupported type %d",asset_name,asset_type));   		
	}

	p_asset->m_permanent = permanent;
	p_asset->m_group = group;
	p_asset->m_dead = false;	  			// it is not dead	

	// fake an asset name for debugging
	char pDebugAssetString[256];
	sprintf( pDebugAssetString, "%08x from data stream", asset_name );
	p_asset->SetText(pDebugAssetString);			
	
	// finally, call the asset's Load member function to load it
	int error = p_asset->Load(p_data, data_size);	
	if ( error != 0 )
	{
		if ( Script::GetInteger( CRCD(0x25dc7904,"AssertOnMissingAssets") ) )
		{
			Dbg_MsgAssert(0,("Loading asset %08x returned error %d",asset_name,error));
		}
		else
		{
			delete p_asset;
			p_asset = NULL;
		}
		return NULL;
	}
	else
	{
		// only add it to the table once we're sure there's no error
		uint32 checksum = asset_name;
		Dbg_MsgAssert(!mp_asset_table->GetItem( checksum ),("Asset %08x already in table",asset_name));	
		mp_asset_table->PutItem( checksum,  p_asset);
		p_asset->SetID(checksum);

		// link in at the end, so things are added in order
		p_asset->mp_next = mp_assetlist_head;
		p_asset->mp_prev = mp_assetlist_head->mp_prev;
		mp_assetlist_head->mp_prev->mp_next = p_asset;
		mp_assetlist_head->mp_prev = p_asset;

		Dbg_MsgAssert(mp_asset_table->GetItem( checksum ) == p_asset,("Asset does not match entry in table"));
		Dbg_Assert( p_asset );
		return p_asset->GetData();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void* CAssMan::LoadAsset(const char *p_assetName, bool async_load, bool use_pip, bool permanent, uint32 group, void* pExtraData, Script::CStruct *pParams)
{
	// load an asset, and return a pointer to the asset data

	// given an asset filename, find the type of the asset, and then load it

	Dbg_MsgAssert(!AssetAllocated(p_assetName),("Asset %s already loaded",p_assetName));

	CAsset	* p_asset = NULL;	
	
// Find the asset type	
	EAssetType	asset_type = FindAssetType(p_assetName); 
	Dbg_MsgAssert(asset_type != ASSET_UNKNOWN,("Asset %s is of unknown type",p_assetName));
	
// based on that, create an asset of the correct type	
	switch (asset_type)
	{
		case ASSET_ANIM:
			p_asset = new CAnimAsset;
			break;

        case ASSET_SKELETON:
            p_asset = new CSkeletonAsset;
            break;

		case ASSET_SKIN:
			p_asset = new CSkinAsset;
			break;

		case ASSET_CUTSCENE:
			p_asset = new CCutsceneAsset;
			break;
			
		case ASSET_NODEARRAY:
			p_asset = new CNodeArrayAsset;
			break;
			
		case ASSET_SCENE:
		case ASSET_TEXTURES:
		case ASSET_COLLISION:
		case ASSET_BINARY:
		default:
			Dbg_MsgAssert(0,("Asset %s is of unsupported type %d",p_assetName,asset_type));   		
	}

	p_asset->m_permanent = permanent;
	p_asset->m_group = group;
	p_asset->m_dead = false;	  			// it is not dead	

	p_asset->SetText(p_assetName);			// record asset name for debugging
	
	// Garrett: Make sure that we only set async on the things that can handle them
	if (async_load)
	{
		Dbg_MsgAssert(asset_type == ASSET_ANIM, ("Can't load this asset type asynchronously: %d", asset_type));
	}

	// finally, call the asset's Load member function to load it
	int	error = p_asset->Load(p_assetName, async_load, use_pip, pExtraData, pParams);	
	if ( error != 0 )
	{
		if ( Script::GetInteger( CRCD(0x25dc7904,"AssertOnMissingAssets") ) )
		{
			Dbg_MsgAssert(0,("Loading asset %s returned error %d",p_assetName,error));
		}
		else
		{
			delete p_asset;
			p_asset = NULL;
		}
		return NULL;
	}
	else
	{
		// only add it to the table once we're sure there's no error
		uint32 checksum = Script::GenerateCRC( p_assetName );
		Dbg_MsgAssert(!mp_asset_table->GetItem( checksum ),("Asset %s already in table",p_assetName));	
		mp_asset_table->PutItem( checksum,  p_asset);
		p_asset->SetID(checksum);

		// link in at the end, so things are added in order
		p_asset->mp_next = mp_assetlist_head;
		p_asset->mp_prev = mp_assetlist_head->mp_prev;
		mp_assetlist_head->mp_prev->mp_next = p_asset;
		mp_assetlist_head->mp_prev = p_asset;

		Dbg_MsgAssert(mp_asset_table->GetItem( Script::GenerateCRC( p_assetName )) == p_asset,("Asset does not match entry in table"));
		Dbg_Assert( p_asset );
		return p_asset->GetData();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void* CAssMan::GetFirstInGroup( uint32 group )
{
	// return the first asset in the asset manager that is in this group
	// will return NULL if there are none of teh specified group
	
	return GetNthInGroup(group,0);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void* CAssMan::GetNthInGroup( uint32 group, int n )
{
	// return the nth asset in the asset manager that is in this group
	// will return NULL if there are none of the specified group
	
	CAsset* p_asset = mp_assetlist_head->mp_next;
	while (p_asset != mp_assetlist_head)
	{
//		printf ("%d: Checkingfor %x, group = %x\n",n,group,p_asset->m_group); 
		CAsset *p_next = p_asset->mp_next;	
		if (p_asset->m_group == group)
		{
//			printf ("%d: GROUP MATCH %x, group = %x\n",n,group,p_asset->m_group); 
			if (n==0)
			{
//				printf ("%d: COUNT MATCH %x, group = %x\n",n,group,p_asset->m_group); 
				return	p_asset->GetData();				
			}
			n--;
		}			
		p_asset = p_next;		
	}
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CAssMan::GetGroup(uint32 checksum)
{
	CAsset* p_actual = mp_asset_table->GetItem(checksum);
	Dbg_MsgAssert(p_actual, ("GetIndexInGroup with asset not found\n")); 
	return p_actual->GetGroup();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CAssMan::GetIndexInGroup(uint32 checksum)
{
	// find this entry, and see which number it is in the group
	// i.e, returns the intex into the group
	
	CAsset* p_actual = mp_asset_table->GetItem(checksum);
	Dbg_MsgAssert(p_actual, ("GetIndexInGroup with asset not found\n")); 
	
	int n = 0;										  	// index starts at 0
	CAsset* p_asset = mp_assetlist_head->mp_next;
	while (p_asset != mp_assetlist_head)
	{
		CAsset *p_next = p_asset->mp_next;	
		if (p_asset == p_actual)
		{
			return n;						  			// if we've found it, then return the asset
		}
		if (p_asset->m_group == p_actual->m_group)	 	// if in same group
		{
			n++;										// increment index
		}
		p_asset = p_next;		
	}
	Dbg_MsgAssert(0, ("ERROR IN GetIndexInGroup\n")); 
	return 0;
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CAssMan::CountGroup(uint32 group)
{
	int n = 0;										  	// index starts at 0
	CAsset* p_asset = mp_assetlist_head->mp_next;
	while (p_asset != mp_assetlist_head)
	{
		CAsset *p_next = p_asset->mp_next;	
		if (p_asset->m_group == group)	 	// if in same group
		{
			n++;										// increment index
		}
		p_asset = p_next;		
	}
	return n;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CAssMan::CountSameGroup(uint32 checksum)
{
	// a utility function to count the assets in the same group as another asset
	
	return CountGroup(GetGroup(checksum));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
					   
void CAssMan::DestroyReferences( CAsset* pAsset )
{
	// Destroys an asset's references
	
	Dbg_Assert( pAsset );
		
	while (pAsset->mp_ref_asset)
	{
		// destroying a reference is similar to an asset, except we
		// do not need to call unload

//		printf ("Deleting Reference %p of type %d, <%s>, <%s>\n",p_asset->mp_ref_asset,p_asset->mp_ref_asset->GetType(),p_asset->mp_ref_asset->Name(), p_asset->mp_ref_asset->GetText());
		uint32 id = pAsset->mp_ref_asset->m_id;
		pAsset->mp_ref_asset->Unlink();
		delete pAsset->mp_ref_asset; 
		mp_asset_table->FlushItem(id);		
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAssMan::UnloadAsset( CAsset* pAsset )
{
	// Unload an asset and delete it
	// note the current pairing of create/load,  and unload/delete
	// in the future, we might need to seperate them out into two distinct stages 
	
	Dbg_Assert( pAsset );

	uint32 id = pAsset->m_id;

	Dbg_MsgAssert(pAsset->LoadFinished(), ("UnloadAsset(): Asset not finished loading"));

	pAsset->Unlink();
	pAsset->Unload();
	delete pAsset;				
	mp_asset_table->FlushItem(id);		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAssMan::ReloadAsset( uint32 assetID, const char* pFileName, bool assertOnFail )
{
	Ass::CAsset* pAsset;

	pAsset = this->GetAssetNode( assetID, assertOnFail );

	if ( pAsset )
	{
		Dbg_MsgAssert(pAsset->LoadFinished(), ("ReloadAsset(): Asset not finished loading"));

		return pAsset->Reload( pFileName );
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAsset*	CAssMan::GetAssetNode( uint32 assetID, bool assertOnFail )
{
	CAsset *p_asset = mp_asset_table->GetItem(assetID);
		
	if ( assertOnFail )
	{
		Dbg_MsgAssert(p_asset, ("Asset Not found <%s>", Script::FindChecksumName(assetID)));
		//Dbg_MsgAssert(p_asset->LoadFinished(), ("Asset not finished loading (%s)", Script::FindChecksumName(assetID)));
	}

	// Don't return pointer if it isn't done loading
	if ( p_asset && !p_asset->LoadFinished() )
	{
		//p_asset = NULL;

		// Wait if not done loading
		while (!p_asset->LoadFinished())
		{
			Dbg_Message("Waiting for async asset load to finish...");
			File::CAsyncFileLoader::sWaitForIOEvent(false);
		}
	}
	
	return p_asset;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void* CAssMan::GetAsset(uint32 assetID, bool assertOnFail )	
{
	// Return a pointer to the asset data, or NULL if not found
	
	CAsset *p_asset = mp_asset_table->GetItem(assetID);

	if (p_asset /*&& p_asset->LoadFinished()*/)
	{
		// Wait if not done loading
		while (!p_asset->LoadFinished())
		{
			Dbg_Message("Waiting for async asset load to finish...");
			File::CAsyncFileLoader::sWaitForIOEvent(false);
		}

		void *p_asset_data = p_asset->GetData();
		Dbg_MsgAssert(p_asset_data,("Asset has no data"));
		return	p_asset_data;
	}
	else
	{
		if ( assertOnFail )
		{
			Dbg_MsgAssert(p_asset, ("Asset 0x%x Not found (%s)",assetID,Script::FindChecksumName(assetID)));
			//Dbg_MsgAssert(p_asset->LoadFinished(), ("Asset 0x%x not finished loading (%s)",assetID,Script::FindChecksumName(assetID)));
		}
		return NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void* CAssMan::GetAsset(const char *p_assetName, bool assertOnFail)
{
	//returns a pointer to the asset data, does not load it if not found
	
	if ( assertOnFail )
	{
		Dbg_MsgAssert(AssetAllocated(p_assetName),("asset %s is not loaded in GetAsset",p_assetName));
	}
	void *p_asset_data = GetAsset(Script::GenerateCRC( p_assetName ), assertOnFail );
	
	if ( assertOnFail )
	{
		Dbg_MsgAssert(p_asset_data,("Asset has NULL data %s",p_assetName));
	}
	return p_asset_data;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void* CAssMan::LoadOrGetAsset(const char *p_assetName, bool async_load, bool use_pip, bool permanent, uint32 group, void* pExtraData, Script::CStruct * pParams)
{
	//returns a pointer to the asset data, and loads the asset if not found
	
	void *p_asset_data;
	CAsset *p_asset = mp_asset_table->GetItem(Script::GenerateCRC( p_assetName ));
	if (p_asset)
	{
		p_asset_data = p_asset->GetData();
		Dbg_MsgAssert(p_asset_data,("Asset has NULL data %s",p_assetName));
		//Dbg_MsgAssert(p_asset->LoadFinished(), ("Asset not finished loading (%s)", p_assetName));

		// Wait if not done loading
		while (!p_asset->LoadFinished())
		{
			Dbg_Message("Waiting for async asset load to finish...");
			File::CAsyncFileLoader::sWaitForIOEvent(false);
		}
	}
	else
	{
		p_asset_data = LoadAsset(p_assetName, async_load, use_pip, permanent, 0, pExtraData,pParams);	
	}
	
	if ( Script::GetInteger( CRCD(0x25dc7904,"AssertOnMissingAssets") ) )
	{
		// do some extra checks here if we're asserting on missing assets
		Dbg_MsgAssert(p_asset_data,("Could not load asset %s",p_assetName));
		Dbg_MsgAssert(AssetAllocated(p_assetName),("asset %s is not loaded after LoadOrGetAsset",p_assetName));
	}

	return p_asset_data;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAssMan::AssetAllocated(uint32 assetID)
{
	//  Return true if the asset has at least started loading
	
	return (NULL != mp_asset_table->GetItem(assetID));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAssMan::AssetAllocated(const char *p_assetName)
{
	//  Return true if the asset has at least started loading
	
	return (AssetAllocated(Script::GenerateCRC( p_assetName )));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAssMan::AssetLoaded(uint32 assetID)
{
	//  Return true if the asset is done loading
	CAsset *p_asset = mp_asset_table->GetItem(assetID);

	if (p_asset && p_asset->LoadFinished())
	{
		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAssMan::AssetLoaded(const char *p_assetName)
{
	//  Return true if the asset is done loading
	
	return (AssetLoaded(Script::GenerateCRC( p_assetName )));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAssMan::AddRef(uint32 asset_id, uint32 ref_id, uint32 group)
{
	// Add a reference to an asset, so we can access it via a new id

	Dbg_MsgAssert(AssetAllocated(asset_id),("Adding ref to unloaded asset\n"));
	CAsset *p_asset = new CRefAsset(mp_asset_table->GetItem(asset_id));
	Dbg_MsgAssert(!mp_asset_table->GetItem( ref_id ),("ref already in table/n"));	
	mp_asset_table->PutItem( ref_id,  p_asset);
	p_asset->SetID(ref_id);
	p_asset->SetGroup(group);

	// link it to the end of the list
	p_asset->mp_next = mp_assetlist_head;
	p_asset->mp_prev = mp_assetlist_head->mp_prev;
	mp_assetlist_head->mp_prev->mp_next = p_asset;
	mp_assetlist_head->mp_prev = p_asset;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAssMan::AddRef(const char *p_assetName, uint32 ref_id, uint32 group)
{
	Dbg_MsgAssert(!mp_asset_table->GetItem( ref_id ),("ref %lx already in table\nwhen adding to %s\n",ref_id,p_assetName));	
	Dbg_MsgAssert(AssetAllocated(p_assetName),("Adding ref to unloaded asset %s\n",p_assetName));
	uint32	checksum = Script::GenerateCRC( p_assetName );
	AddRef(checksum,ref_id, group);	
}
												 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAssMan::UnloadAllTables( bool destroy_permanent )
{
//	printf ( "Unloading all tables" );
	
	CAsset* p_asset;
	 
	p_asset = mp_assetlist_head->mp_next;
	while (p_asset != mp_assetlist_head)
	{
		CAsset *p_next = p_asset->mp_next;	
		if ( destroy_permanent || !p_asset->m_permanent )
		{
			// First destroy any references to this asset
			DestroyReferences( p_asset );
			
			// Need to update p_next, in case it was change by destroying above code
			p_next = p_asset->mp_next;	
	
			UnloadAsset( p_asset );
		}
		p_asset = p_next;		
	}

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAssMan::SetDefaultPermanent(bool permanent)
{
	// set the default permanent state of loaded assets
	// (generally this will be false)
	
	m_default_permanent = permanent;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAssMan::GetDefaultPermanent() const
{
	return m_default_permanent;
}

// The following functions are kind of kludge for referencing anims
// Ideally, I'd like to remove these kludges and simplify the
// asset manager

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAssMan::LoadAnim(const char* pFileName, uint32 animName, uint32 referenceChecksum, bool async_load, bool use_pip)
{
	// If the asset was previously loaded, then we are just creating a reference to it
	// so the m_permanent flag should be set based on m_default_permanent, and NOT INHERITED
			
	if (AssetAllocated(pFileName))
	{
		// if descChecksum was supplied, then adding a reference
		if (animName && !AssetAllocated(animName + referenceChecksum))
		{
			// adding a reference
			AddRef(pFileName, animName + referenceChecksum, referenceChecksum);	 
			// the m_permanent will have been inherited from the parent asset
			// Now get the reference asset, and set the permanent flag to m_defualt_permanet
			CAsset *p_asset = mp_asset_table->GetItem(animName + referenceChecksum);
			Dbg_MsgAssert(p_asset,("(Mick) Error re-getting asset ref for %s\n",pFileName));
			//p_asset->SetPermanent(m_default_permanent);  // <<<< this would also set the perm flag on the parent

			p_asset->m_permanent = m_default_permanent;		// just in the reference!!!
			return true;
		}
		else
		{
			//Dbg_MsgAssert( 0, ( "This file was already loaded %s", pFileName ) );
			return false;
		}
	}
	else
	{
		if (!LoadOrGetAsset(pFileName, async_load, use_pip, m_default_permanent, 0))
		{
			Dbg_MsgAssert(0,("Failed to load anim %s",pFileName));
			return false;
		}

		// Now add the reference to it, if one was requested
		// this gets combined with the "reference checksum"
		if (animName && !AssetAllocated(animName + referenceChecksum))
		{
			AddRef(pFileName, animName + referenceChecksum, referenceChecksum);	 
		}

		return true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAssMan::SetReferenceChecksum(uint32 reference_checksum)	   
{
	// kind of a kludge for anims

	m_reference_checksum = reference_checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CAssMan::GetReferenceChecksum() const
{
	return m_reference_checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Ass

#if 0
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAssMan::DumpAssets()
{
	CAsset * p_asset = mp_assetlist_head->mp_next;
	int i=0;
	while (p_asset != mp_assetlist_head)
	{
		CAsset *p_next = p_asset->mp_next;	
		printf ("Asset %3d: perm=%d %s\n",i,p_asset->m_permanent, p_asset->GetText());
		i++;
		p_asset = p_next;		
	}	
}
#endif


================================================
FILE: Code/Gel/AssMan/AssMan.h
================================================
//****************************************************************************
//* MODULE:         Gel/AssMan
//* FILENAME:       AssMan.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/17/2000
//****************************************************************************

#ifndef __GEL_ASSMAN_H
#define __GEL_ASSMAN_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Lst
{
	template class HashTable;
}

namespace Script
{
	class CStruct;
}

namespace Ass
{
	class CAsset;

/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/
   
class CAssMan : public Spt::Class 
{	
	DeclareSingletonClass( CAssMan );

public:
    
	CAssMan( void );
    ~CAssMan( void );

	// New generic functions
	EAssetType	FindAssetType(const char *p_assetName);

	void*		LoadAssetFromStream(uint32 asset_name, uint32 asset_type, uint32* p_data, int data_size, bool permanent, uint32 group);
	void*		LoadAsset(const char *p_assetName, bool async_load, bool use_pip = false, bool permanent = false, uint32 group = 0, void* pExtraData = NULL, Script::CStruct * pParams = NULL);
	bool		ReloadAsset( uint32 assetID, const char* pFileName, bool assertOnFail );
	void*		GetAsset(const char *p_assetName, bool assertOnFail = true);	
	void*		GetAsset(uint32	assetID, bool assertOnFail = true);	
	void*		LoadOrGetAsset(const char *p_assetName, bool async_load, bool use_pip, bool permanent = false, uint32 group = 0, void* pExtraData = NULL, Script::CStruct *pParams = NULL);	
	
	bool		AssetAllocated(uint32	assetID);
	bool		AssetAllocated(const char *p_assetName);

	bool		AssetLoaded(uint32	assetID);
	bool		AssetLoaded(const char *p_assetName);
	
	void		AddRef(uint32 asset_id, uint32 ref_id, uint32 group = 0);
	void		AddRef(const char *p_assetName, uint32 ref_id, uint32 group = 0);
	
	void		SetReferenceChecksum(uint32 reference_checksum);	   
	uint32		GetReferenceChecksum() const;
	
	void		SetDefaultPermanent(bool permanent);
	bool		GetDefaultPermanent() const;

	void* 		GetFirstInGroup(uint32 group);
	void* 		GetNthInGroup(uint32 group, int n);

	int 		GetIndexInGroup(uint32 checksum);
	int 		GetGroup(uint32 checksum);
	int 		CountGroup(uint32 group);
	int 		CountSameGroup(uint32 checksum);

	void		UnloadAllTables( bool destroy_permanent = false );
	void		DestroyReferences( CAsset* pAsset );
	void		UnloadAsset( CAsset* pAsset );
	CAsset*		GetAssetNode( uint32 assetID, bool assertOnFail );
	
	// ideally, the LoadAnim function should be made more generic
	bool		LoadAnim(const char* pFileName, uint32 animName, uint32 referenceChecksum, bool async_load, bool use_pip);
	
private:

	bool							m_default_permanent;
	uint32 							m_reference_checksum;	// used for anims, bit of a patch
	Lst::HashTable*	mp_asset_table;
	CAsset*							mp_assetlist_head;		   
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Ass

#endif	// __GEL_ASSMAN_H



================================================
FILE: Code/Gel/AssMan/NodeArrayAsset.cpp
================================================
///////////////////////////////////////////////////////////////////////////
// NodeArrayAsset.cpp
//
// Asset depended code for loading, unloading and reloading a node array
//
// Dave
//


#include	
#include	
#include	
#include	
#include	
#include	

namespace Ass
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CNodeArrayAsset::Load( const char* p_file, bool async_load, bool use_pip, void* pExtraData , Script::CStruct *pStruct)
{
	SkateScript::LoadQB( p_file, Script::ASSERT_IF_DUPLICATE_SYMBOLS);
	m_qb_checksum = Script::GenerateCRC( p_file );

	char nodearrayname[128];

	// Skip back to the backslash.
	const char *p = &p_file[strlen( p_file ) - 1];
	while( *p != '\\' && *p != '/') p--;
	p++;

	strcpy( nodearrayname, p );												// nodearrayname is now "name.qb"
	sprintf( &nodearrayname[strlen( nodearrayname ) - 3], "_nodearray" );	// nodearrayname is now "name_nodearray"
	void *p_data = Script::GetArray( nodearrayname );

	// Set the array as the data returned when queried,
	SetData( p_data );

	Dbg_MsgAssert( p_data, ( "Could not find nodearray %s in %s", nodearrayname, p_file ));

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CNodeArrayAsset::Unload()                     
{
	// Unload the asset.
	SkateScript::UnloadQB( m_qb_checksum );
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CNodeArrayAsset::Reload( const char* p_file )
{
	Dbg_Message( "Reloading %s", p_file );
	
	Unload();

	return( Load( p_file, false, 0, NULL, NULL ) == 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CNodeArrayAsset::LoadFinished()
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* CNodeArrayAsset::Name()            
{
	// Printable name, for debugging.
	return "Node Array";	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAssetType CNodeArrayAsset::GetType()         
{
	// type is hard wired into asset class 
	return ASSET_NODEARRAY;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}


================================================
FILE: Code/Gel/AssMan/NodeArrayAsset.h
================================================
////////////////////////////////////////////////////////////////////////////
// animasset.h - interface between asset manager, and the actual animation
// provides a common interface to the 
#ifndef	__GEL_NODEARRAYASSET_H__
#define	__GEL_NODEARRAYASSET_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#include	

namespace Ass
{

class 	CNodeArrayAsset : public CAsset
{

public:
	virtual int 				Load( const char *p_file, bool async_load, bool use_pip, void* pExtraData, Script::CStruct *pStruct );	// create or load the asset
	virtual int 				Unload();										// Unload the asset
	virtual int 				Reload( const char *p_file);
	virtual bool				LoadFinished();									// Check to make sure asset is actually there
	virtual const char *  		Name();											// printable name, for debugging
	virtual EAssetType 			GetType();										// type is hard wired into asset class
private:


	uint32						m_qb_checksum;
};


} // end namespace Ass

#endif			// #ifndef	__GEL_ASSET_H__



================================================
FILE: Code/Gel/AssMan/animasset.cpp
================================================
///////////////////////////////////////////////////////////////////////////
// AnimAsset.cpp
//
// Asset depended code for loading, unloading and reloading an animation
//
// Mick
//


#include	

#include	

#include	
#include	

#include	


namespace Ass
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CAnimAsset::Load(const char* p_file, bool async_load, bool use_pip, void* pExtraData, Script::CStruct *pStruct)     // create or load the asset
{
	int errorCode = -1;

	Mem::PushMemProfile((char*)p_file);
	
	Gfx::CBonedAnimFrameData* pSeq = new Gfx::CBonedAnimFrameData;

	char fullName[256];
	
	// add extension to create name of platform-specific SKA file
	sprintf( fullName, "%s.%s", p_file, Nx::CEngine::sGetPlatformExtension() );
	
	// Load the data
	if ( !pSeq->Load(fullName, true, async_load, use_pip) )
	{
		if ( Script::GetInteger( CRCD(0x25dc7904,"AssertOnMissingAssets") ) )
		{
			Dbg_MsgAssert( 0,( "Anim %s doesn't exist.", fullName ) );
		}
		delete pSeq;
		pSeq = NULL;
		goto failure;
	}

	// if we get this far, then it's successful
	errorCode = 0;

failure:
	// Add it to the list:
	SetData( (void*)pSeq );
	Mem::PopMemProfile(/*(char*)p_file*/);
	return errorCode;
}

int CAnimAsset::Load(uint32* p_data, int data_size)     // create or load the asset
{
	int errorCode = -1;

	char pDebugAssetString[256];
	sprintf( pDebugAssetString, "anim from data stream" );
	
	Mem::PushMemProfile((char*)pDebugAssetString);

	Gfx::CBonedAnimFrameData* pSeq = new Gfx::CBonedAnimFrameData;

	// Load the data
	if ( !pSeq->Load(p_data, data_size, true ) )
	{
		if ( Script::GetInteger( CRCD(0x25dc7904,"AssertOnMissingAssets") ) )
		{
			Dbg_Assert( 0 );
		}
		delete pSeq;
		pSeq = NULL;
		goto failure;
	}

	// if we get this far, then it's successful
	errorCode = 0;

failure:
	// Add it to the list:
	SetData( (void*)pSeq );
	Mem::PopMemProfile(/*(char*)p_file*/);
	return errorCode;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CAnimAsset::Unload()                     
{
	// Unload the asset
	
	if (GetData())
	{
        delete (Gfx::CBonedAnimFrameData*) GetData();
		
        SetData(NULL);
	}
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CAnimAsset::Reload(const char* p_file)
{
	Dbg_Message( "Reloading %s", p_file );
	
	Unload();

	return ( Load(p_file, false, 0, NULL, NULL ) == 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAnimAsset::LoadFinished()
{
	Gfx::CBonedAnimFrameData * p_anim = (Gfx::CBonedAnimFrameData*) GetData();
	Dbg_MsgAssert(p_anim, ("LoadFinished(): Data pointer NULL (load probably was never started)"));

	return p_anim->LoadFinished();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* CAnimAsset::Name()            
{
	// printable name, for debugging
	return "Animation";	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAssetType CAnimAsset::GetType()         
{
	// type is hard wired into asset class 
	return ASSET_ANIM;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}


================================================
FILE: Code/Gel/AssMan/animasset.h
================================================
////////////////////////////////////////////////////////////////////////////
// animasset.h - interface between asset manager, and the actual animation
// provides a common interface to the 
#ifndef	__GEL_ANIMASSET_H__
#define	__GEL_ANIMASSET_H__



#ifndef __CORE_DEFINES_H
#include 
#endif

#include	

namespace Ass
{



class 	CAnimAsset : public CAsset
{

public:
        virtual int 				Load(const char *p_file, bool async_load, bool use_pip, void* pExtraData, Script::CStruct *pStruct);	// create or load the asset
		virtual int 				Load(uint32* p_data, int data_size);	// create or load the asset
        virtual int 				Unload();                     // Unload the asset
        virtual int 				Reload(const char *p_file);
		virtual bool				LoadFinished();    // Check to make sure asset is actually there
     	virtual const char *  		Name();            // printable name, for debugging
		virtual EAssetType 			GetType();         // type is hard wired into asset class 
private:
		

};


} // end namespace Ass

#endif			// #ifndef	__GEL_ASSET_H__



================================================
FILE: Code/Gel/AssMan/asset.cpp
================================================
///////////////////////////////////////////////////////////////////////////
// Asset.cpp
//
// Mick
//
// Base class of all assets
// provides basic interface to them


#include	
#include 	


namespace Ass
{

// when destroying an asset, we also unload it
// this should only be done via the asset manager

CAsset::CAsset()
{
}

CAsset::~CAsset()
{
}

// Unlink for the doubly linked list
void CAsset::Unlink()
{
	mp_next->mp_prev = mp_prev;
	mp_prev->mp_next = mp_next;
	mp_prev = this;
	mp_next = this;
}

int CAsset::Load(const char *p_file, bool async_load, bool use_pip, void* pExtraData , Script::CStruct *pParams)
{
	Dbg_MsgAssert(0,("CAsset::Load() should not be called"));
	return 0;
}  	

int CAsset::Load(uint32* p_data, int data_size)
{
	Dbg_MsgAssert(0,("CAsset::Load() from data buffer should not be called"));
	return 0;
}

int CAsset::Reload(const char *p_file)   
{
	Dbg_MsgAssert(0,("CAsset::Reload() should not be called"));
	return 0;
}

int 	CAsset::Unload()                  
{
	Dbg_MsgAssert(0,("CAsset::Unload() should not be called"));
	return 0;
}

bool	CAsset::LoadFinished()
{
	Dbg_MsgAssert(0,("CAsset::LoadFinished() should not be called"));
	return false;
}



const char *  CAsset::Name()            // printable name, for debugging
{
	return	"Unnamed Asset";	
}

void * 	CAsset::GetData()             // return a pointer to the asset's data
{
	return mp_data;					
}

void  	CAsset::SetData(void *p_data)             
{
	mp_data = p_data;				   
}


//----------------------------------------------------

void   CAsset::SetID(uint32 id)         
{
	m_id = id;
}

uint32 CAsset::GetID()
{
	return m_id;
}

void   CAsset::SetGroup(uint32 group)     
{
	m_group = group;
}

uint32 CAsset::GetGroup()
{
	return m_group;
}


EAssetType CAsset::GetType()         // type is hard wired into asset class 
{
	return ASSET_UNKNOWN; 		// for now return 0, not sure if this should return the EAssetType
}

}


================================================
FILE: Code/Gel/AssMan/asset.h
================================================
///////////////////////////////////////////////////////////
// asset.h - base class for managed assets

#ifndef	__GEL_ASSET_H__
#define	__GEL_ASSET_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
#include 

namespace Script
{
	class CStruct;
}


namespace Ass
{
class 	CAsset : public Spt::Class
{
	friend class CAssMan;
	friend class CRefAsset;

protected:																	 
		CAsset();	  				// constructor is private, so only CAssMan can create them
		virtual ~CAsset(); 			//

		virtual    	int 		Load(const char *p_file, bool async_load, bool use_pip, void* pExtraData, Script::CStruct *pParams);	// create or load the asset
		virtual		int			Load(uint32* p_data, int data_size);
		virtual    	int 		Unload();                  	// Unload the asset
		virtual    	int 		Reload(const char *p_file);
		virtual		bool		LoadFinished();				// Check to make sure asset is actually there


					void 		Unlink();                  	// Unlink the asset

// Some acessor functions for memory usage metrics					
		virtual   	const char* Name();          	// printable name, for debugging

// acessors for seting the low level id stuff 
// maybe this should just be derived from CObject?    
		void      			   	SetID(uint32 id);           	// Unique asset ID
		uint32    			   	GetID();
		virtual void      		SetGroup(uint32 group);     	// Unique group ID
		virtual uint32    		GetGroup();
		
		virtual EAssetType	  	GetType();         				// type is hard wired into asset class 
		virtual void     		SetData(void *p_data);          // return a pointer to the asset.
		virtual void *    		GetData();             			// return a pointer to the asset.
		virtual	void			SetPermanent(bool perm);

		void 					SetText(const char *p_text);
		const char *			GetText();

private:
		uint32				   	m_id;
		uint32				   	m_group;
		
		char				   	m_permanent;  					// should it stay in memory after a cleanup
		char					m_dead;							// asset is dead, remove it 		
		
		void *					mp_data;   						// pointer to the asset data

//		Str::String				m_text;

protected:
		CRefAsset	*			mp_ref_asset;					// pointer to first CRefAsset that points to this 

// The assets are also stored in a simple doubly linked list with a head node
// this is to simplify the 
		CAsset  *				mp_prev;
		CAsset  *				mp_next;
	
};



//////////////////////////////////////////////////////////////////////////
// Inline member functions																	 


inline		void 					CAsset::SetText(const char *p_text)
{
//	m_text = p_text;
}

inline 		const char *			CAsset::GetText()
{
//	return m_text.getString();
	return "names removed";
}
																	 
inline	void	CAsset::SetPermanent(bool perm) {m_permanent = perm;}


} // end namespace Ass

#endif			// #ifndef	__GEL_ASSET_H__



================================================
FILE: Code/Gel/AssMan/assettypes.h
================================================
// asset types for the asset manager

#ifndef __GEL_ASSETTYPES_H
#define __GEL_ASSETTYPES_H

namespace Ass
{

enum	EAssetType {
	ASSET_UNKNOWN,							// unknown, needs to be determined by inspection
	ASSET_BINARY,							// binary file
	ASSET_SCENE,							// world_geometry
	ASSET_TEXTURES,							// Texture file
	ASSET_COLLISION,						// collision file
	ASSET_ANIM,								// animation
    ASSET_SKELETON,                         // skeleton
	ASSET_SKIN,								// skin
	ASSET_CUTSCENE,							// cutscene
	ASSET_NODEARRAY,						// model node array
};

} // namespace Ass

#endif


================================================
FILE: Code/Gel/AssMan/cutsceneasset.cpp
================================================
//****************************************************************************
//* MODULE:         Ass
//* FILENAME:       cutsceneasset.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  01/13/2003
//****************************************************************************

#include 

#include 
#include 
#include 

namespace Ass
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CCutsceneAsset::Load( const char* p_file, bool async_load, bool use_pip, void* pExtraData, Script::CStruct *pStruct )     // create or load the asset
{
	Mem::PushMemProfile( (char*)p_file );
	
	Obj::CCutsceneData* p_cutsceneData = new Obj::CCutsceneData;

	// get the platform-specific CUT file name
	char platFileName[256];
	sprintf( platFileName, "%s.%s", p_file, Nx::CEngine::sGetPlatformExtension() );
	
	// load the data
	if ( !p_cutsceneData->Load( platFileName, true, async_load ) )
	{
		Dbg_MsgAssert( 0, ( "Cutscene %s doesn't exist.", platFileName ) );
		return -1;
	}
	
	// add it to the list:
	SetData( (void*)p_cutsceneData );

	Mem::PopMemProfile(/*(char*)p_file*/);

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CCutsceneAsset::Unload()                     
{
	// Unload the asset
	Obj::CCutsceneData* pData = (Obj::CCutsceneData*)GetData();
	if ( pData )
	{
        delete pData;
        SetData(NULL);
	}
	
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CCutsceneAsset::Reload(const char* p_file)
{
	Dbg_Message( "Reloading %s...", p_file );
	
	Unload();

	return ( Load( p_file, false, 0, NULL, NULL ) == 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneAsset::LoadFinished()
{
	Obj::CCutsceneData* p_cutsceneData = (Obj::CCutsceneData*)GetData();
	Dbg_MsgAssert( p_cutsceneData, ( "LoadFinished(): Data pointer NULL (load probably was never started)" ) );
	return p_cutsceneData->LoadFinished();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* CCutsceneAsset::Name()            
{
	// printable name, for debugging
	return "Cutscene";	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAssetType CCutsceneAsset::GetType()         
{
	// type is hard wired into asset class 
	return ASSET_CUTSCENE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}


================================================
FILE: Code/Gel/AssMan/cutsceneasset.h
================================================
//****************************************************************************
//* MODULE:         Ass
//* FILENAME:       cutsceneasset.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  01/13/2003
//****************************************************************************

// interface between asset manager, and the actual cutscene

#ifndef	__GEL_CUTSCENEASSET_H__
#define	__GEL_CUTSCENEASSET_H__

#include 
#include 

#include 

namespace Ass
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
class CCutsceneAsset : public CAsset
{

public:
        virtual int 				Load(const char *p_file, bool async_load, bool use_pip, void* pExtraData, Script::CStruct *pStruct);	// create or load the asset
        virtual int 				Unload();                     				// unload the asset
        virtual int 				Reload(const char *p_file);
		virtual bool				LoadFinished();    							// check to make sure asset is actually there
     	virtual const char *  		Name();            							// printable name, for debugging
		virtual EAssetType 			GetType();         							// type is hard wired into asset class 
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}

#endif



================================================
FILE: Code/Gel/AssMan/refasset.cpp
================================================
///////////////////////////////////////////////////////////////////////////
// RefAsset.cpp
//
// Mick
//
// Type of asset that just refers to another asset
// so you can have multiple indicies to reference an asset


#include	
#include	


namespace Ass
{

CRefAsset::CRefAsset(CAsset *p_asset)
{

	mp_asset = p_asset;
	mp_sibling = NULL;
	if (p_asset->mp_ref_asset == NULL)
	{
		// this is the first reference, so just stick it in mp_ref_asset
		p_asset->mp_ref_asset = this;		
	}
	else
	{
		// it's a new asset, so insert it as the head
		// of siblings that refer to this
		CRefAsset *p_other_ref = p_asset->mp_ref_asset;
		p_asset->mp_ref_asset = this;
		mp_sibling = p_other_ref;
	}
	mp_data = NULL;							// Ref Assets explicitly have no data
	m_permanent = p_asset->m_permanent;		// inherit the permanence flag
}



CRefAsset::~CRefAsset()
{

// unhook the references
// from the assman table, the asset it refers to, and other assets
	
	if (mp_asset)
	{
		CRefAsset * p_prev = mp_asset->mp_ref_asset;
		if (p_prev == this)
		{
			mp_asset->mp_ref_asset = mp_sibling;
		}
		else
		{
			while (p_prev && p_prev->mp_sibling  != this)
			{
				p_prev = p_prev->mp_sibling;
			}
			Dbg_MsgAssert(p_prev,("Reference not listed in its parent asset")); 
			p_prev->mp_sibling = mp_sibling;
		}	
		mp_asset = NULL;	
		mp_sibling = NULL;
	}					
	

	Dbg_MsgAssert(mp_asset == NULL,("Destroying ref with non-null asset"));	
	Dbg_MsgAssert(mp_sibling == NULL,("Destroying ref with non-null sibling"));	
	Dbg_MsgAssert(mp_data == NULL,("Ref Asset has data!"));

}


////////////////////////////////////////////////////////////////////////////////////
// all other functions simply call the appropiate function on the referenced asset
// (might want to make these inline later)

int 			CRefAsset::Load(const char *p_file, bool async_load, bool use_pip, void* pExtraData)  	// create or load the asset
{
	Dbg_MsgAssert(!async_load, ("Async load not supported on CRefAsset"));

//	return mp_asset->Load(p_file);
	return 0;
}


int 			CRefAsset::Unload()                  	// Unload the asset
{
//	return mp_asset->Unload();
	return 0;
}


int 			CRefAsset::Reload(const char *p_file)
{
//	printf("Reloading dereferenced asset with %s\n", p_file);

	return mp_asset->Reload(p_file);
}

bool			CRefAsset::LoadFinished()
{
	Dbg_MsgAssert(GetData(), ("LoadFinished(): Data pointer NULL (load probably was never started)"));

	return mp_asset->LoadFinished();
}



const char* 	CRefAsset::Name()          			// printable name, for debugging
{
	return mp_asset->Name();
}


void      		CRefAsset::SetGroup(uint32 group)     // Unique group ID
{
//	mp_asset->SetGroup(group);
	m_group = group;
}


uint32    		CRefAsset::GetGroup()
{
//	return mp_asset->GetGroup();
	return m_group;
}


EAssetType	  	CRefAsset::GetType()         			// type is hard wired into asset class 
{
	return mp_asset->GetType();
}


void     		CRefAsset::SetData(void *p_data)      // return a pointer to the asset.
{
	mp_asset->SetData(p_data);
}


void *    		CRefAsset::GetData()             		// return a pointer to the asset.
{
	return mp_asset->GetData();
}


void			CRefAsset::SetPermanent(bool perm)
{
	m_permanent = perm;
	mp_asset->SetPermanent(perm);
}


}


================================================
FILE: Code/Gel/AssMan/refasset.h
================================================
///////////////////////////////////////////////////////////
// asset.h - base class for managed assets

#ifndef	__GEL_REFASSET_H__
#define	__GEL_REFASSET_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 

namespace Ass
{



class 	CRefAsset : public CAsset
{
		friend class CAssMan;
protected:
						CRefAsset(CAsset *p_asset);
						~CRefAsset();
		
		int 			Load(const char *p_file, bool async_load, bool use_pip, void* pExtraData);	// create or load the asset
		int 			Unload();                  	// Unload the asset
		int 			Reload(const char *p_file);
		bool			LoadFinished();				// Check to make sure asset is actually there
		int  			RamUsage();
		int  			VramUsage();
		int  			SramUsage();
		const char* 	Name();          			// printable name, for debugging
		void      		SetGroup(uint32 group);     // Unique group ID
		uint32    		GetGroup();
		EAssetType	  	GetType();         			// type is hard wired into asset class 
		void     		SetData(void *p_data);      // return a pointer to the asset.
		void *    		GetData();             		// return a pointer to the asset.
		void			SetPermanent(bool perm);

private:
		CAsset	*				mp_asset;			// pointer to the actual asset 
		CRefAsset	*			mp_sibling;			// pointer to other CRefAsset that references mp_asset 	
};

//////////////////////////////////////////////////////////////////////////
// Inline member functions																	 
																	 


} // end namespace Ass

#endif			// #ifndef	__GEL_REFASSET_H__



================================================
FILE: Code/Gel/AssMan/skeletonasset.cpp
================================================
//****************************************************************************
//* MODULE:         Ass
//* FILENAME:       skeletonasset.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  ??/??/????
//****************************************************************************

#include 

#include 

#include 
#include 

namespace Ass
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CSkeletonAsset::Load( const char *p_file, bool async_load, bool use_pip, void* pExtraData , Script::CStruct *pStruct)     // create or load the asset
{																			   
	Dbg_MsgAssert( !async_load, ( "Async load not supported on CSkeletonAsset" ) );

	// Load the data, add it to the list:
	Gfx::CSkeletonData* pSkeletonData = new Gfx::CSkeletonData;
	
	char fullName[256];
	
	// add extension to create name of platform-specific SKE file
	sprintf( fullName, "%s.%s", p_file, Nx::CEngine::sGetPlatformExtension() );
	
	if ( !pSkeletonData->Load( fullName, true ) )
	{
		Dbg_MsgAssert( 0,( "File %s doesn't exist.", fullName ));
		return -1;
	}
	SetData( (void*)pSkeletonData );

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CSkeletonAsset::Load( uint32* p_data, int data_size )
{
	char pDebugAssetString[256];
	sprintf( pDebugAssetString, "skeleton from data stream" );
	
	Mem::PushMemProfile((char*)pDebugAssetString);

	// Load the data, add it to the list:
	Gfx::CSkeletonData* pSkeletonData = new Gfx::CSkeletonData;
	
	if ( !pSkeletonData->Load( p_data, data_size, true ) )
	{
		Dbg_MsgAssert( 0,( "Couldn't create skeleton from data stream." ));
		return -1;
	}

	SetData((void*)pSkeletonData);

	Mem::PopMemProfile(/*"skeleton from data stream"*/);

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CSkeletonAsset::Unload()                     // Unload the asset
{
	Gfx::CSkeletonData* pData = (Gfx::CSkeletonData*)GetData();
	if ( pData )
	{
        delete pData;
        SetData(NULL);
	}
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CSkeletonAsset::Reload( const char *p_file )
{
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CSkeletonAsset::LoadFinished()
{
	Dbg_MsgAssert( GetData(), ( "LoadFinished(): Data pointer NULL (load probably was never started)" ) );

	// Since we don't support async, this is always true
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
const char *  CSkeletonAsset::Name()            // printable name, for debugging
{
	return "Skeleton";	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
EAssetType CSkeletonAsset::GetType()         // type is hard wired into asset class 
{
	return ASSET_SKELETON; 					// for now return 0, not sure if this should return the EAssetType
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/AssMan/skeletonasset.h
================================================
//****************************************************************************
//* MODULE:         Ass
//* FILENAME:       skeletonasset.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  ??/??/????
//****************************************************************************

// interface between asset manager, and the actual skeleton data
// provides a common interface to the skeleton data

#ifndef	__GEL_SKELETONASSET_H__
#define	__GEL_SKELETONASSET_H__

#include 
#include 

#include 

namespace Ass
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
class CSkeletonAsset : public CAsset
{

public:
        virtual int 				Load(const char *p_file, bool async_load, bool use_pip, void* pExtraData, Script::CStruct *pStruct);	// create or load the asset
		virtual int 				Load(uint32* p_data, int data_size);		// create or load the asset from data stream
        virtual int 				Unload();                     				// unload the asset
        virtual int 				Reload(const char *p_file);
		virtual bool				LoadFinished();    							// check to make sure asset is actually there
     	virtual const char *  		Name();            							// printable name, for debugging
		virtual EAssetType 			GetType();         							// type is hard wired into asset class 
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}

#endif

================================================
FILE: Code/Gel/AssMan/skinasset.cpp
================================================
///////////////////////////////////////////////////////////////////////////
// SkinAsset.cpp
//
// Asset depended code for loading, unloading and reloading a skin
//
// Mick
//


#include	

#include	
						   
#include	

#include	
#include	

namespace Ass
{

int CSkinAsset::Load(const char *p_file, bool async_load, bool use_pip, void* pExtraData, Script::CStruct *pStruct)     // create or load the asset
{
	Dbg_MsgAssert(!async_load, ("Async load not supported on CSkinAsset"));

	Mem::PushMemProfile((char*)p_file);

	SSkinAssetLoadContext theContext;
	theContext.forceTexDictLookup = false;
	theContext.doShadowVolume = false;
	theContext.texDictOffset = 0;

	// GJ:  kludge to prevent tex dict offset clashes
	// in bail board asset...  i will remove this
	// after i figure out why the tex dict offsets
	// are clashing in the first place...
//	if ( strstr(p_file,"board_default") )
//	{
//		texDictOffset = -1;
//	}

	if ( pExtraData )
	{
		theContext = *(SSkinAssetLoadContext*)pExtraData;
	}
	
	Nx::CMesh* pMesh = Nx::CEngine::sLoadMesh( p_file, theContext.texDictOffset, theContext.forceTexDictLookup, theContext.doShadowVolume );
	if ( !pMesh )
	{
		Dbg_MsgAssert( 0,( "mesh %s doesn't exist.", p_file ));
		return -1;
	}

	SetData((void*)pMesh);

	// make sure the filename is in lower-case
	char msg[128];
	strcpy( msg, p_file );
	Str::LowerCase( msg );

	// only MDL files should load their collision data
	if ( strstr( msg, ".mdl" ) )
	{
		// Might be overrided by having a "nocollision=1" as a parameter
		int no_collision = 0;
		if (pStruct)
		{
			pStruct->GetInteger(CRCD(0xbf29bc0,"nocollision"),&no_collision);
		}
		if (!no_collision)
		{
			pMesh->LoadCollision(p_file);
		}
	}
	
	// Model node arrays are now loaded via the NodeArrayComponent.
//	pMesh->LoadModelNodeArray(p_file);

	Mem::PopMemProfile(/*"Skin"*/);

	return 0;
}

int CSkinAsset::Load(uint32* p_data, int data_size)     // create or load the asset
{
	char pDebugAssetString[256];
	sprintf( pDebugAssetString, "skin from data stream" );
	
	Mem::PushMemProfile((char*)pDebugAssetString);

	// GJ:  for now, we need to access 3 pointers for the MDL, TEX, and CAS
	// data...  eventually, i'd like to store each of the 3 files
	// separately in the asset manager, in which case we wouldn't
	// have to do this clumsy hack of sending 3 pointers wrapped up
	// in a struct...
	SCutsceneModelDataInfo* p_cutscene_data = (SCutsceneModelDataInfo*)p_data;

	Nx::CMesh* pMesh = Nx::CEngine::sLoadMesh( p_cutscene_data->modelChecksum,
											   p_cutscene_data->pModelData,
											   p_cutscene_data->modelDataSize,
											   p_cutscene_data->pCASData,
											   p_cutscene_data->texDictChecksum,
											   p_cutscene_data->pTextureData,
											   p_cutscene_data->textureDataSize,
											   p_cutscene_data->texDictOffset,
											   p_cutscene_data->isSkin,
											   p_cutscene_data->doShadowVolume );
	if ( !pMesh )
	{
		Dbg_MsgAssert( 0,( "Couldn't create mesh from data stream." ));
		return -1;
	}

	SetData((void*)pMesh);

	// for now, we're going to assume that data loaded in through
	// a data stream will not have collision or a node array,
	// since we're only using this for cutscene assets right 
	// now...  this can be changed very easily...

	Mem::PopMemProfile(/*"skin from data stream"*/);

	return 0;
}

int CSkinAsset::Unload()                     // Unload the asset
{
	if (GetData())
	{
		Nx::CEngine::sUnloadMesh( (Nx::CMesh*) GetData() );

        SetData(NULL);
	}
	
	return 0;
}

int CSkinAsset::Reload(const char *p_file)
{
	return 0;
}

bool CSkinAsset::LoadFinished()
{
	Dbg_MsgAssert(GetData(), ("LoadFinished(): Data pointer NULL (load probably was never started)"));

	// Since we don't support async, this is always true
	return true;
}

const char *  CSkinAsset::Name()            // printable name, for debugging
{
	return "Skin";	
}

EAssetType CSkinAsset::GetType()         // type is hard wired into asset class 
{
	return ASSET_SKIN; 					// for now return 0, not sure if this should return the EAssetType
}

}


================================================
FILE: Code/Gel/AssMan/skinasset.h
================================================
////////////////////////////////////////////////////////////////////////////
// skinasset.h - interface between asset manager, and the actual skin
// provides a common interface to the asset manager
#ifndef	__GEL_SKINASSET_H__
#define	__GEL_SKINASSET_H__



#ifndef __CORE_DEFINES_H
#include 
#endif

#include	

namespace Ass
{

struct SCutsceneModelDataInfo
{
	uint32		modelChecksum;
	uint32*		pModelData;
	int			modelDataSize;
	uint32*		pTextureData;
	int			textureDataSize;
	uint8*		pCASData;
	uint32		texDictChecksum;
	int			texDictOffset;
	bool		isSkin;
	bool		doShadowVolume;
};

struct SSkinAssetLoadContext
{
	int			forceTexDictLookup;
	bool		doShadowVolume;
	int			texDictOffset;
};

class 	CSkinAsset : public CAsset
{

public:
        virtual int 				Load(const char *p_file, bool async_load, bool use_pip, void* pExtraData, Script::CStruct *pStruct);	// create or load the asset
		virtual int 				Load(uint32* p_data, int data_size);	// create or load the asset
        virtual int 				Unload();                     // Unload the asset
        virtual int 				Reload(const char *p_file);
		virtual bool				LoadFinished();    // Check to make sure asset is actually there
     	virtual const char *  		Name();            // printable name, for debugging
		virtual EAssetType 			GetType();         // type is hard wired into asset class 
private:
		

};


} // end namespace Ass

#endif			// #ifndef	__GEL_SKINASSET_H__



================================================
FILE: Code/Gel/Collision/BatchTriColl.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx								 						**
**																			**
**	File name:		gel\collision\BatchTriColl.cpp 							**
**																			**
**	Created by:		04/12/02	-	grj										**
**																			**
**	Description:															**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

#ifdef	__PLAT_NGPS__
#include 
#include 
#include 
#include 
#endif //__PLAT_NGPS__

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Nx
{

#ifdef BATCH_TRI_COLLISION

CollData *		CBatchTriColl::sp_coll_data;

volatile bool	CBatchTriCollMan::s_processing = false;
volatile bool	CBatchTriCollMan::s_result_processing = false;
volatile int	CBatchTriCollMan::s_nested = 0;
volatile bool	CBatchTriCollMan::s_found_collision = false;
CBatchTriColl 	CBatchTriCollMan::s_tri_collision_array[MAX_BATCH_COLLISIONS][2];
int				CBatchTriCollMan::s_current_array = 0;
int				CBatchTriCollMan::s_array_size = 0;
int				CBatchTriCollMan::s_next_idx_to_batch = 0;
int				CBatchTriCollMan::s_num_collisions = 0;
#ifdef __PLAT_NGPS__
int 			CBatchTriCollMan::s_collision_handler_id = -1;
bool 			CBatchTriCollMan::s_use_vu0_micro = false;
#endif

//----------------------------------------------------------------------------
//

#ifdef	__PLAT_NGPS__
bool vu0RayTriangleCollision(const Mth::Vector *rayStart, const Mth::Vector *rayDir,
							 Mth::Vector *v0, Mth::Vector *v1, Mth::Vector *v2, float *t)
{
	register bool result;

	asm __volatile__(
	"
	.set noreorder
	lqc2    vf06,0x0(%4)		# v0
	lqc2    vf08,0x0(%6)		# v2
	lqc2    vf07,0x0(%5)		# v1
	lqc2    vf04,0x0(%2)		# rayStart
	lqc2    vf05,0x0(%3)		# rayDir

	vcallms	RayTriangleCollision	# call microsubroutine
	vnop							# interlocking instruction, waits for vu0 completion

	cfc2	%0,$vi02			# get boolean result from vu0
	#li		%0,1				# store 1 for return value

	beq		%0, $0, vu0RayTriDone	# skip copy of t if not needed
	nop

	qmfc2	$8, $vf17			# move t to $8
	sw		$8, 0(%1)			# and write out

vu0RayTriDone:

	.set reorder
	": "=r" (result), "+r" (t) : "r" (rayStart), "r" (rayDir), "r" (v0), "r" (v1) , "r" (v2) : "$8", "$9" );

	return result;
}
#endif //	__PLAT_NGPS__

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CBatchTriCollMan::sInit(CollData *p_coll_data)
{
	// Check for nesting; abort if true
	if (s_processing)
	{
		s_nested++;
		return false;
	}

	Dbg_Assert(s_processing == false);

	s_array_size = 0;
	s_next_idx_to_batch = 0;
	s_found_collision = false;

	CBatchTriColl::sp_coll_data = p_coll_data;

#ifdef	__PLAT_NGPS__
    // install interrupt handler for render completion
	if (s_collision_handler_id == -1)
	{
		s_collision_handler_id = AddIntcHandler(INTC_VU0, s_collision_done, 0);
		EnableIntc(INTC_VU0);
		sceDevVu0PutTBit(1);		// Use T-bit interrupt
	}
#endif //	__PLAT_NGPS__

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CBatchTriCollMan::sFinish()
{
	if (s_nested)
	{
		s_nested--;
		Dbg_Assert(s_nested >= 0);
		return;
	}

	// For now, don't wait for collision to finish, since we
	// are trying to avoid a stall here.  But this will probably
	// cause deadlock situations once it is on the VU0.
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CBatchTriCollMan::sAddTriCollision(const Mth::Line &test_line, const Mth::Vector &test_line_dir,
										   CCollObj *p_collision_obj, FaceIndex face_index)
{
	Dbg_Assert(s_nested == 0);

	CBatchTriColl *p_tri_collision;
	p_tri_collision = &(s_tri_collision_array[s_array_size++][s_current_array]);

	p_tri_collision->m_test_line = test_line;
	p_tri_collision->m_test_line_dir = test_line_dir;
	p_tri_collision->mp_collision_obj = p_collision_obj;
	p_tri_collision->m_face_index = face_index;

	//Dbg_Assert(s_array_size <= MAX_BATCH_COLLISIONS);
	if (s_array_size == MAX_BATCH_COLLISIONS)
	{
//		sWaitTriCollisions();		// make sure were done working
		sStartNewTriCollisions();

		//s_switch_buffers();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CBatchTriCollMan::sRemoveTriCollision(CBatchTriColl *p_tri_collision)
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CBatchTriCollMan::sStartNewTriCollisions()
{
	Dbg_Assert(s_nested == 0);

	sWaitTriCollisions();		// make sure were done working
	Dbg_Assert(!s_processing);

	// Check if we have anything to do
	if (s_next_idx_to_batch >= s_array_size)
	{
		return;
	}

	s_processing = true;

#ifdef	__PLAT_NGPS__
	if(s_use_vu0_micro)
	{
		uint128 *p_vu0_mem = (uint128*)0x11004000;		// VU0 data memory

		// Write out sizes first
		uint32 *p_vu0_word_mem = (uint32 *) (p_vu0_mem++);
		*(p_vu0_word_mem++) = s_next_idx_to_batch;
		*(p_vu0_word_mem++) = s_array_size;

		for (int i = s_next_idx_to_batch; i < s_array_size; i++)
		{
			const CBatchTriColl &tri_coll = s_tri_collision_array[i][s_current_array];

			Mth::Vector *v0, *v1, *v2;
			if (tri_coll.mp_collision_obj->mp_coll_tri_data->m_use_face_small)
			{
				CCollObjTriData::SFaceSmall *face = &(tri_coll.mp_collision_obj->mp_coll_tri_data->mp_face_small[tri_coll.m_face_index]);

				v0 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
				v1 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
				v2 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
			} else {
				CCollObjTriData::SFace *face = &(tri_coll.mp_collision_obj->mp_coll_tri_data->mp_faces[tri_coll.m_face_index]);

				v0 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
				v1 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
				v2 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
			}

			*(p_vu0_mem++) = *((uint128*) &(tri_coll.m_test_line.m_start));
			*(p_vu0_mem++) = *((uint128*) &(tri_coll.m_test_line_dir));
			*(p_vu0_mem++) = *((uint128*) v0);
			*(p_vu0_mem++) = *((uint128*) v1);
			*(p_vu0_mem++) = *((uint128*) v2);
		}

		//FlushCache(WRITEBACK_DCACHE);

		//sceDevVu0PutTBit(1);		// Use T-bit interrupt

		// Switch the buffers before we make the call (in case it finishes immediately)
		s_switch_buffers();

		asm __volatile__(
		"
		.set noreorder
		sync.l
		vcallms	BatchRayTriangleCollision	# call microsubroutine

		.set reorder
		");
	}
	else
#endif //	__PLAT_NGPS__
	{
		// Just do it manually now
		for (int i = s_next_idx_to_batch; i < s_array_size; i++)
		{
			const CBatchTriColl &tri_coll = s_tri_collision_array[i][s_current_array];

			Mth::Vector *v0, *v1, *v2;
			if (tri_coll.mp_collision_obj->mp_coll_tri_data->m_use_face_small)
			{
				CCollObjTriData::SFaceSmall *face = &(tri_coll.mp_collision_obj->mp_coll_tri_data->mp_face_small[tri_coll.m_face_index]);

				v0 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
				v1 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
				v2 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
			} else {
				CCollObjTriData::SFace *face = &(tri_coll.mp_collision_obj->mp_coll_tri_data->mp_faces[tri_coll.m_face_index]);

				v0 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
				v1 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
				v2 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
			}

			float distance;
#ifdef	__PLAT_NGPS__
			if (vu0RayTriangleCollision(&tri_coll.m_test_line.m_start, &tri_coll.m_test_line_dir, v0, v1, v2, &distance))
#else
			if (CCollObj::sRayTriangleCollision(&tri_coll.m_test_line.m_start, &tri_coll.m_test_line_dir,
												v0, v1, v2, &distance))
#endif
			{
				SCollSurface collisionSurface;

				/* We've got one */
				collisionSurface.point = (*v0);
				collisionSurface.index = tri_coll.m_face_index;

				// Find normal
				Mth::Vector vTmp1(*v1 - *v0);
				Mth::Vector vTmp2(*v2 - *v0);
				collisionSurface.normal = Mth::CrossProduct(vTmp1, vTmp2);
				collisionSurface.normal.Normalize();

				if (CCollObj::s_found_collision(&tri_coll.m_test_line, tri_coll.mp_collision_obj, &collisionSurface, distance, CBatchTriColl::sp_coll_data))
				{
					s_found_collision = true;
				}
			}
		}
		s_processing = false;		// Just for manual version

		s_switch_buffers();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		CBatchTriCollMan::s_collision_done(int arg)
{
#ifdef	__PLAT_NGPS__
	// We should check to see if this is the T-bit first (instead of the debug D bit)
	uint32 stat;
    asm( "cfc2 %0, $vi29" :"=r"( stat ) : );
    if ( !(stat & 0x4) )
	{
		ExitHandler();
		return 0;
    }

	// Save floats
    //unsigned int floatBuffer[32];
    //saveFloatRegs(floatBuffer);

	asm __volatile__(
	"
	.set noreorder
	cfc2	%0,$vi01					# get number of collisions result from vu0

	.set reorder
	": "=r" (s_num_collisions) );

	s_processing = false;		// Tell that we are done

	if (s_num_collisions > 0)
	{
		s_result_processing = true;
	}

    // restore floats
    //restoreFloatRegs(floatBuffer);

	ExitHandler();
#endif //	__PLAT_NGPS__

	return 0;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CBatchTriCollMan::s_process_results()
{
#ifdef	__PLAT_NGPS__
	SCollOutput *p_output = (SCollOutput*)0x11004010;		// VU0 data memory
	int previous_array = s_current_array ^ 1;
	for (int i = 0; i < s_num_collisions; i++)
	{
		const CBatchTriColl &tri_coll = s_tri_collision_array[p_output->index][previous_array];

		Mth::Vector *v0, *v1, *v2;
		if (tri_coll.mp_collision_obj->mp_coll_tri_data->m_use_face_small)
		{
			CCollObjTriData::SFaceSmall *face = &(tri_coll.mp_collision_obj->mp_coll_tri_data->mp_face_small[tri_coll.m_face_index]);

			v0 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
			v1 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
			v2 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
		} else {
			CCollObjTriData::SFace *face = &(tri_coll.mp_collision_obj->mp_coll_tri_data->mp_faces[tri_coll.m_face_index]);

			v0 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
			v1 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
			v2 = &tri_coll.mp_collision_obj->mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
		}

		SCollSurface collisionSurface;

		/* We've got one */
		collisionSurface.point = (*v0);
		collisionSurface.index = tri_coll.m_face_index;

		// Find normal
		Mth::Vector vTmp1(*v1 - *v0);
		Mth::Vector vTmp2(*v2 - *v0);
		collisionSurface.normal = Mth::CrossProduct(vTmp1, vTmp2);
		collisionSurface.normal.Normalize();

		if (CCollObj::s_found_collision(&tri_coll.m_test_line, tri_coll.mp_collision_obj, &collisionSurface, p_output->distance, CBatchTriColl::sp_coll_data))
		{
			s_found_collision = true;
		}

		p_output++;
	}

	s_result_processing = false;
#else
	Dbg_Assert(s_result_processing);
#endif //	__PLAT_NGPS__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CBatchTriCollMan::sWaitTriCollisions()
{
	// Wait for collision to finish
	while (s_processing)
		;

	if (s_result_processing)
	{
		s_process_results();
	}

	return s_found_collision;
}

#ifdef	__PLAT_NGPS__
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CBatchTriCollMan::sUseVU0Micro()
{
	// Make sure we're not in the middle of something
	Dbg_Assert(!s_processing);
	Dbg_Assert(!s_result_processing);

	return s_use_vu0_micro = NxPs2::CSystemResources::sRequestResource(NxPs2::CSystemResources::vVU0_MEMORY);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CBatchTriCollMan::sDisableVU0Micro()
{
	// Make sure we're not in the middle of something
	Dbg_Assert(!s_processing);
	Dbg_Assert(!s_result_processing);

	if (s_use_vu0_micro)
	{
		NxPs2::CSystemResources::sFreeResource(NxPs2::CSystemResources::vVU0_MEMORY);
		s_use_vu0_micro = false;
	}
}
#endif	//	__PLAT_NGPS__

#endif	// BATCH_TRI_COLLISION

} // namespace Nx



================================================
FILE: Code/Gel/Collision/BatchTriColl.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx														**
**																			**
**	File name:		BatchTriColl.h											**
**																			**
**	Created: 		04/12/2002	-	grj										**
**																			**
*****************************************************************************/

#ifndef	__GEL_BATCHTRICOLL_H
#define	__GEL_BATCHTRICOLL_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#ifdef __PLAT_NGPS__
//#define BATCH_TRI_COLLISION
#endif

namespace Nx
{

#ifdef BATCH_TRI_COLLISION

class CCollObj;
class CollData;

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CBatchTriColl
{
public:
	Mth::Line					m_test_line;
	Mth::Vector					m_test_line_dir;
	CCollObj *					mp_collision_obj;
	float						m_distance;
	FaceIndex					m_face_index;

	static CollData *			sp_coll_data;			// We should only have one at a time
};

// Results from collision
struct SCollOutput
{
	float distance;
	int pad1;
	int pad2;
	int index;
};

class CBatchTriCollMan
{
public:
	static bool					sInit(CollData *p_coll_data);
	static void					sFinish();

	static void					sAddTriCollision(const Mth::Line &test_line, const Mth::Vector &test_line_dir,
												 CCollObj *p_collision_obj, FaceIndex face_index);
	static bool					sRemoveTriCollision(CBatchTriColl *p_tri_collsion);

	static void					sStartNewTriCollisions();
	static volatile bool		sIsTriCollisionDone();
	static volatile bool		sIsNested();
	static bool					sWaitTriCollisions();

#ifdef __PLAT_NGPS__
	static bool					sUseVU0Micro();
	static void					sDisableVU0Micro();
#endif

protected:
	enum
	{
		MAX_BATCH_COLLISIONS = 40,
	};

	// Collision callback
	static int					s_collision_done(int);
	static void					s_process_results();

	static void					s_switch_buffers();

	static volatile bool		s_processing;
	static volatile bool		s_result_processing;
	static volatile int			s_nested;				// Indicates nesting level, so normal collision must be used
	static volatile bool		s_found_collision;

	static CBatchTriColl		s_tri_collision_array[MAX_BATCH_COLLISIONS][2];		// Double buffered
	static int					s_current_array;
	static int					s_array_size;
	static int					s_next_idx_to_batch;

	static int					s_num_collisions;
	static SCollOutput			s_collision_results[MAX_BATCH_COLLISIONS];

private:
#ifdef __PLAT_NGPS__
	static int 					s_collision_handler_id;// for interrupt callback
	static bool					s_use_vu0_micro;
#endif
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline volatile bool			CBatchTriCollMan::sIsTriCollisionDone()
{
	return !s_processing;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline volatile bool			CBatchTriCollMan::sIsNested()
{
	return s_nested > 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void						CBatchTriCollMan::s_switch_buffers()
{
	s_array_size = 0;
	s_next_idx_to_batch = 0;
	s_current_array = s_current_array ^ 1;
}

#endif // BATCH_TRI_COLLISION

} // namespace Nx

#endif	//	__GEL_BATCHTRICOLL_H


================================================
FILE: Code/Gel/Collision/CollCache.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx								 						**
**																			**
**	File name:		gel\collision\collcache.cpp   							**
**																			**
**	Created by:		08/16/02	-	grj										**
**																			**
**	Description:															**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


#define PRINT_TIMES 0

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CCollCache::CCollCache()
{
	Clear();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CCollCache::~CCollCache()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollCache::Clear()
{
	m_bbox.Reset();

	m_array_size = 0;
	m_num_static_coll = 0;
	m_num_movable_coll = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollCache::Update(const Mth::CBBox &bbox)
{
#if PRINT_TIMES
	static uint64 s_total_time = 0, s_num_collisions = 0;
	uint64 start_time = Tmr::GetTimeInUSeconds();
#endif

	// Clear old cache first
	Clear();

	// Make line
	Mth::Line is(bbox.GetMin(), bbox.GetMax());

	// initialize starting "max" distance to be max collision distance
	SSec::Manager *ss_man;
	ss_man = Nx::CEngine::sGetNearestSuperSectorManager(is);
//	Dbg_Assert(ss_man);
   	if (!ss_man)
	{
		return;
	}
 
	// Copy bounding box
	m_bbox = bbox;



	CCollStatic** p_coll_obj_list = ss_man->GetIntersectingCollSectors( is );
	
	// Bloat the collision line only for the purposes of deciding against which world 
	// sectors to test the actual collision line
	Mth::CBBox line_bbox;
	Mth::Vector extend;

	extend = bbox.GetMax();
	extend[X] += CCollObj::sLINE_BOX_EXTENT;
	extend[Y] += CCollObj::sLINE_BOX_EXTENT;
	extend[Z] += CCollObj::sLINE_BOX_EXTENT;
	line_bbox.AddPoint(extend);

	extend = bbox.GetMin();
	extend[X] -= CCollObj::sLINE_BOX_EXTENT;
	extend[Y] -= CCollObj::sLINE_BOX_EXTENT;
	extend[Z] -= CCollObj::sLINE_BOX_EXTENT;
	line_bbox.AddPoint(extend);

	// Look for static collision
    CCollStatic* p_coll_obj;
	while((p_coll_obj = *p_coll_obj_list))
	{
		// TODO: Come up with a cleaner BBox check
		//Dbg_Assert(p_coll_obj->GetGeometry());
		//if(line_bbox.Intersect(p_coll_obj->GetGeometry()->GetBBox()))
		if (p_coll_obj->WithinBBox(line_bbox))
		{
			add_static_collision(p_coll_obj);
		}
		p_coll_obj_list++;
	}

	// Now look for movable collision
	Lst::Node< CCollObj > *p_movable_node = CMovableCollMan::sGetCollisionList()->GetNext();
	while(p_movable_node)
	{
		CCollObj *p_coll_obj = p_movable_node->GetData();
		if (p_coll_obj && !(p_coll_obj->m_Flags & (mSD_NON_COLLIDABLE | mSD_KILLED )))
		{
			if (p_coll_obj->WithinBBox(line_bbox))
			{
				add_movable_collision(p_coll_obj);
			}
		}
		p_movable_node = p_movable_node->GetNext();
	}

#if PRINT_TIMES
	uint64 end_time = Tmr::GetTimeInUSeconds();
	s_total_time += end_time - start_time;

	if (++s_num_collisions >= 1000)
	{
		Dbg_Message("Cache Update time %d us", s_total_time);
		s_total_time = s_num_collisions = 0;
	}
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollCache::add_static_collision(CCollStatic *p_collision)
{
	Dbg_Assert(p_collision);
	Dbg_MsgAssert(m_num_movable_coll == 0, ("Can't add static collision to cache after movable collision"));
	Dbg_MsgAssert(m_array_size < MAX_COLLISION_OBJECTS, ("Collision cache full."));

	m_collision_array[m_array_size].mp_bbox = &(p_collision->GetGeometry()->GetBBox());
	Dbg_MsgAssert(m_collision_array[m_array_size].mp_bbox, ("No bounding box found for the static collision"));

	m_collision_array[m_array_size++].mp_coll_obj = p_collision;
	
	m_num_static_coll++;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollCache::add_movable_collision(CCollObj *p_collision)
{
	Dbg_Assert(p_collision);
	Dbg_MsgAssert(m_array_size < MAX_COLLISION_OBJECTS, ("Collision cache full."));

	m_collision_array[m_array_size].mp_bbox = p_collision->get_bbox();
	//Dbg_MsgAssert(m_collision_array[m_array_size].mp_bbox, ("No bounding box found for the movable collision"));

	m_collision_array[m_array_size++].mp_coll_obj = p_collision;

	m_num_movable_coll++;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollCache::delete_static_collision(CCollStatic *p_collision)
{
	for (int i = 0; i < m_num_static_coll; i++)
	{
		if (m_collision_array[i].mp_coll_obj == p_collision)
		{
			// Must copy the whole block down
			for (int j = i + 1; j < m_array_size; j++)
			{
				m_collision_array[j - 1] = m_collision_array[j];
			}
			m_num_static_coll--;
			m_array_size--;
			break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollCache::delete_movable_collision(CCollObj *p_collision)
{
	for (int i = m_num_static_coll; i < m_array_size; i++)
	{
		if (m_collision_array[i].mp_coll_obj == p_collision)
		{
			m_collision_array[i] = m_collision_array[--m_array_size];
			m_num_movable_coll--;
			break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollCache::delete_collision(CCollObj *p_collision)
{
	delete_static_collision(static_cast(p_collision));
	delete_movable_collision(p_collision);
}

////////////////////////////////////

CCollCache *	CCollCacheManager::sp_coll_cache_array[MAX_COLLISION_CACHES];
int				CCollCacheManager::s_num_coll_caches = 0;

bool			CCollCacheManager::s_assert_on_cache_miss;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollCache *	CCollCacheManager::sCreateCollCache()
{
	Dbg_MsgAssert(s_num_coll_caches < MAX_COLLISION_CACHES, ("Too many collision caches"));
	sp_coll_cache_array[s_num_coll_caches] = new CCollCache;

	return sp_coll_cache_array[s_num_coll_caches++];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CCollCacheManager::sDestroyCollCache(CCollCache *p_cache)
{
	bool found = false;

	// Take entry out of array
	for (int i = 0; i < s_num_coll_caches; i++)
	{
		if (sp_coll_cache_array[i] == p_cache)
		{
			// Move last in array to here
			sp_coll_cache_array[i] = sp_coll_cache_array[--s_num_coll_caches];

			found = true;
			break;
		}
	}

	// And do the actual deletion
	delete p_cache;

	Dbg_Assert(found);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CCollCacheManager::sDeleteMovableCollision(CCollObj *p_collision)
{
	// Check each cache
	for (int i = 0; i < s_num_coll_caches; i++)
	{
		sp_coll_cache_array[i]->delete_movable_collision(p_collision);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CCollCacheManager::sDeleteCollision(CCollObj *p_collision)
{
	// Check each cache
	for (int i = 0; i < s_num_coll_caches; i++)
	{
		sp_coll_cache_array[i]->delete_collision(p_collision);
	}
}

} // namespace Nx



================================================
FILE: Code/Gel/Collision/CollCache.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx														**
**																			**
**	File name:		CollCache.h												**
**																			**
**	Created: 		08/16/2002	-	grj										**
**																			**
*****************************************************************************/

#ifndef	__GEL_COLLCACHE_H
#define	__GEL_COLLCACHE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Nx
{

class CCollObj;
class CCollStatic;
class CCollObjTriData;
class CCollCacheManager;

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/
struct SCollCacheNode
{
	CCollObj			*mp_coll_obj;		// Collision object
	const Mth::CBBox	*mp_bbox;			// Current bounding box
};

////////////////////////////////////

class CCollCache
{
public:
							CCollCache();
							~CCollCache();

	void					Clear();

	// WARNING: The Update() function takes just as long as a normal collision call.  Make sure you
	// plan on using the cache multiple times.
	void					Update(const Mth::CBBox &bbox);

	// Checks to see if this cache can be used
	bool					Contains(const Mth::CBBox &test_bbox) const;
	bool					Contains(const Mth::Line &test_line) const;

	const SCollCacheNode *	GetCollisionArray() const;
	int						GetNumCollisions() const;
	const SCollCacheNode *	GetStaticCollisionArray() const;
	int						GetNumStaticCollisions() const;
	const SCollCacheNode *	GetMovableCollisionArray() const;
	int						GetNumMovableCollisions() const;

protected:
	enum
	{
		MAX_COLLISION_OBJECTS = 500,
	};

	void					add_static_collision(CCollStatic *p_collision);
	void					add_movable_collision(CCollObj *p_collision);
	void					delete_static_collision(CCollStatic *p_collision);
	void					delete_movable_collision(CCollObj *p_collision);
	void					delete_collision(CCollObj *p_collision);			// In case we don't know what type (less efficient)

	Mth::CBBox				m_bbox;				// bounding box where cache is valid
	SCollCacheNode			m_collision_array[MAX_COLLISION_OBJECTS];
	int						m_array_size;
	int						m_num_static_coll;
	int						m_num_movable_coll;

	// Friends
	friend CCollCacheManager;
};

////////////////////////////////////

class CCollCacheManager
{
public:

	// Allocation functions
	static CCollCache *		sCreateCollCache();
	static void				sDestroyCollCache(CCollCache *p_cache);

	// Collision deletion functions
	static void				sDeleteStaticCollision(CCollStatic *p_collision);
	static void				sDeleteMovableCollision(CCollObj *p_collision);
	static void				sDeleteCollision(CCollObj *p_collision);			// In case we don't know what type (less efficient)
	
	static void				sSetAssertOnCacheMiss ( bool state ) { s_assert_on_cache_miss = state; }
	static bool				sGetAssertOnCacheMiss (   ) { return s_assert_on_cache_miss; }

protected:
	enum
	{
		MAX_COLLISION_CACHES = 8,
	};
	
	static CCollCache *		sp_coll_cache_array[MAX_COLLISION_CACHES];
	static int				s_num_coll_caches;
	
	static bool				s_assert_on_cache_miss;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool					CCollCache::Contains(const Mth::CBBox &test_bbox) const
{
	return m_bbox.Within(test_bbox);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool					CCollCache::Contains(const Mth::Line &test_line) const
{
	return (m_bbox.Within(test_line.m_start) && m_bbox.Within(test_line.m_end));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const SCollCacheNode *CCollCache::GetCollisionArray() const
{
	return m_collision_array;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline int					CCollCache::GetNumCollisions() const
{
	return m_array_size;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const SCollCacheNode *CCollCache::GetStaticCollisionArray() const
{
	return m_collision_array;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline int					CCollCache::GetNumStaticCollisions() const
{
	return m_num_static_coll;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const SCollCacheNode *CCollCache::GetMovableCollisionArray() const
{
	return &(m_collision_array[m_num_static_coll]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline int					CCollCache::GetNumMovableCollisions() const
{
	return m_num_movable_coll;
}

} // namespace Nx

#endif	//	__GEL_COLLCACHE_H


================================================
FILE: Code/Gel/Collision/CollEnums.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx														**
**																			**
**	File name:		CollEnums.h												**
**																			**
**	Created: 		02/27/2002	-	grj										**
**																			**
*****************************************************************************/

#ifndef	__GEL_COLLENUMS_H
#define	__GEL_COLLENUMS_H

namespace Nx
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

////////////////////////////////////////////////////////////////
// Types of collision
enum CollType
{
	vCOLL_TYPE_NONE = 0,
	vCOLL_TYPE_BBOX,
	vCOLL_TYPE_SPHERE,
	vCOLL_TYPE_CYLINDRICAL,
	vCOLL_TYPE_GEOM,
};

// Typedefs
typedef uint16 FaceIndex;
typedef uint8  FaceByteIndex;

} // namespace Nx

#endif  //	__GEL_COLLENUMS_H


================================================
FILE: Code/Gel/Collision/CollTriData.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx								 						**
**																			**
**	File name:		gel\collision\collide.cpp      							**
**																			**
**	Created by:		02/20/02	-	grj										**
**																			**
**	Description:															**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

#include 
#include 	// for face flag stuff
#include 

#include 


// For debugging rendering
#include 
#include 

#ifdef	__PLAT_NGPS__	
#include 
#include 
#include 
#include 
namespace	NxPs2
{
	bool TestSphereAgainstOccluders( Mth::Vector *p_center, float radius, uint32 meshes = 1 );
	bool	IsInFrame(Mth::Vector ¢er, float radius);
}

#endif

#ifdef	__PLAT_NGC__
#include 
#include 
#endif		// __PLAT_NGC__


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Nx
{


FaceIndex	CCollObjTriData::s_face_index_buffer[MAX_FACE_INDICIES];
FaceIndex	CCollObjTriData::s_seq_face_index_buffer[MAX_FACE_INDICIES] = { 0xFFFF }; // Set to uninitialized
uint		CCollObjTriData::s_num_face_indicies;

const uint	CCollObjTriData::s_max_face_per_leaf = 20;		// maximum number faces per leaf
const uint	CCollObjTriData::s_max_tree_levels = 7;			// maximum number of levels in a tree

#define USE_BSP_CLONE

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollObjTriData::CCollObjTriData()
{
#ifdef __PLAT_NGC__
	mp_cloned_vert_pos = NULL;
#endif		// __PLAT_NGC__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollObjTriData::~CCollObjTriData()
{
	// Clones are the only types that should make it here
	// Remove vertices
#ifdef __PLAT_NGC__
	if ( mp_cloned_vert_pos )
	{
		delete [] mp_cloned_vert_pos;
	}
#else
#ifdef FIXED_POINT_VERTICES
	if (mp_float_vert)
	{
		delete [] mp_float_vert;
	}
#else
	if (mp_vert_pos)
	{
		delete[] mp_vert_pos;
	}
#endif // FIXED_POINT_VERTICES
#endif		// __PLAT_NGC__
	if (mp_intensity)
	{
		delete[] mp_intensity;
	}

	// Remove faces
	if (mp_faces)
	{
		if (m_use_face_small)
		{
			delete[] mp_face_small;
		} else{
			delete[] mp_faces;
		}
	}

	DeleteBSPTree();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollObjTriData::InitCollObjTriData(CScene * p_scene, void *p_base_vert_addr, void *p_base_intensity_addr,
											void *p_base_face_addr, void *p_base_node_addr, void *p_base_face_idx_addr)
{
	// Set base addr
#ifdef __PLAT_NGC__
	NxNgc::sScene *p_engine_scene = ( static_cast( p_scene ))->GetEngineScene();
	mp_raw_vert_pos = (NsVector*)p_engine_scene->mp_pos_pool;
	mp_intensity = (unsigned char *) ((int) p_base_vert_addr + (int)mp_intensity); 
	mp_cloned_vert_pos = NULL;
#else
#ifdef FIXED_POINT_VERTICES
	// Get indexes
	int start_vert_pos_offset = (int) mp_float_vert;
	int start_intensity_index = (int) mp_intensity;

	// Find first element
	mp_float_vert = (SFloatVert *) ((int) p_base_vert_addr + start_vert_pos_offset);
	mp_intensity = (uint8 *) p_base_intensity_addr;
	mp_intensity += start_intensity_index;
#else
	// Get indexes
	int start_vert_pos_offset = (int) mp_vert_pos;

	// Find first element
	mp_vert_pos = (Mth::Vector *) ((int) p_base_vert_addr + start_vert_pos_offset);

	mp_intensity = NULL;
#endif // FIXED_POINT_VERTICES
#endif		// __PLAT_NGC__

	mp_faces = (SFace *)((int) mp_faces + (int) p_base_face_addr);

	//Dbg_Message ( "Object has %d verts sizeof %d", m_num_verts, sizeof(SReadVertex));
	//Dbg_Message ( "Object has %d faces sizeof %d", m_num_faces, sizeof(SReadFace) );
#ifndef FIXED_POINT_VERTICES
	Dbg_Assert(!m_use_fixed_verts)
#endif

	// Init BSP tree
	mp_bsp_tree = (CCollBSPNode *)((int) mp_bsp_tree + (int) p_base_node_addr);
	s_init_tree(mp_bsp_tree, p_base_node_addr, p_base_face_idx_addr);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollBSPNode::CCollBSPNode()
{
	//m_node.m_split_axis = 0;
	m_node.m_split_point = 0; 		// This is so we know it is a node
	m_node.m_children.Init();
	//mp_greater_branch = NULL;
}

CCollBSPNode::~CCollBSPNode()
{
#ifdef USE_BSP_CLONE
	// Since the cloned BSP tree is allocated as one big buffer, these calls shouldn't
	// be necessary.
	return;

#else
	Dbg_MsgAssert(IsNode(), ("Called ~CCollBSPNode() on CCollBSPLeaf()"));

	Dbg_MsgAssert(0, ("Only cloned collision should get to the BSP destructor"));
#endif // USE_BSP_CLONE

	if (GetLessBranch())
	{
		delete GetLessBranch();
	}

	if (GetGreaterBranch())
	{
		delete GetGreaterBranch();
	}

	if (GetFaceIndexArray())
	{
		delete GetFaceIndexArray();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CCollBSPNode::SetSplitAxis(int axis)
{
	Dbg_Assert(IsNode());
	
	m_node.m_split_point = m_node.m_split_point & ~((1 << NUM_AXIS_BITS) - 1);		// Clear out old axis
	m_node.m_split_point = m_node.m_split_point | (axis & ((1 << NUM_AXIS_BITS) - 1));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Garrett: This is an unconventional way of cloning the data.  We are assuming that all the
// tree nodes are in one contiguous block of memory.  I normally wouldn't write code like
// this, but we need to avoid lots of small allocations.
CCollBSPNode * CCollBSPNode::clone(bool instance)
{
	Dbg_MsgAssert(!instance, ("CCollBSPNode::clone() with instances not implemented yet"));
	//Dbg_MsgAssert(IsNode(), ("Called CCollBSPNode::clone() on CCollBSPLeaf()"));

	// So that we don't make lots of little allocations, allocate the whole BSP array at once
	int num_nodes = count_bsp_nodes();
	int num_face_indices = count_bsp_face_indices();
	if (num_nodes > 0)
	{
		Dbg_MsgAssert(num_face_indices, ("Didn't find any face indices in the BSP tree"));
		FaceIndex *		p_orig_face_index_array = find_bsp_face_index_array_start();

		CCollBSPNode *	p_new_bsp_array = new CCollBSPNode[num_nodes];
		FaceIndex *		p_new_face_index_array = new FaceIndex[num_face_indices];

		// Just memcpy it first, since we know the source tree is in an array
		memcpy(p_new_bsp_array, this, num_nodes * sizeof(CCollBSPNode));
		memcpy(p_new_face_index_array, p_orig_face_index_array, num_face_indices * sizeof(FaceIndex));

#ifdef	__NOPT_ASSERT__
		// Look through new bsp array that still has the old face index pointers in it.
		for (int node_idx = 0; node_idx < num_nodes; node_idx++)
		{
			if (p_new_bsp_array[node_idx].IsLeaf())
			{
				Dbg_MsgAssert(p_orig_face_index_array <= p_new_bsp_array[node_idx].GetFaceIndexArray(), ("find_bsp_face_index_array_start() failed"));
			}
		}
#endif //	__NOPT_ASSERT__

		// Now adjust the pointers by finding the differences
		int node_address_diff = (int) p_new_bsp_array - (int) this;
		int face_address_diff = (int) p_new_face_index_array - (int) p_orig_face_index_array;
		for (int i = 0; i < num_nodes; i++)
		{
			if (p_new_bsp_array[i].IsNode())
			{
				// Adjust branch pointers
				p_new_bsp_array[i].m_node.m_children.SetBasePointer((CCollBSPNode *)((int) p_new_bsp_array[i].m_node.m_children.GetBasePointer() + node_address_diff));
			}
			else
			{
				// Adjust face index pointer
				p_new_bsp_array[i].m_leaf.mp_face_idx_array = (FaceIndex *) ((int) p_new_bsp_array[i].m_leaf.mp_face_idx_array + face_address_diff);
			}
		}

		return p_new_bsp_array;
	}
	else
	{
		return NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int				CCollBSPNode::count_bsp_nodes()
{
	if (IsLeaf())
	{
		return 1;
	}
	else
	{
		return 1 + GetLessBranch()->count_bsp_nodes() + GetGreaterBranch()->count_bsp_nodes();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int				CCollBSPNode::count_bsp_face_indices()
{
	if (IsLeaf())
	{
		return m_leaf.m_num_faces;
	}
	else
	{
		return GetLessBranch()->count_bsp_face_indices() + GetGreaterBranch()->count_bsp_face_indices();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

FaceIndex *		CCollBSPNode::find_bsp_face_index_array_start()
{
	if (IsLeaf())
	{
		return m_leaf.mp_face_idx_array;
	}
	else
	{
		FaceIndex *p_index1 = GetLessBranch()->find_bsp_face_index_array_start();
		FaceIndex *p_index2 = GetGreaterBranch()->find_bsp_face_index_array_start();

		if (p_index1 < p_index2)
			return p_index1;
		else
			return p_index2;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CCollBSPNode::CCollBSPChildren::SetLeftGreater(bool greater)
{
	 if (greater)
	 {
		 m_left_child_and_flags |= mLEFT_IS_GREATER;
	 }
	 else
	 {
		 m_left_child_and_flags &= ~mLEFT_IS_GREATER;
	 }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CCollBSPNode::translate(const Mth::Vector & delta_trans)
{
	// Translate node
	if (IsNode())
	{
		int axis = GetSplitAxis();

		SetFSplitPoint(GetFSplitPoint() + delta_trans[axis]);

		// Traverse the tree
		GetLessBranch()->translate(delta_trans);
		GetGreaterBranch()->translate(delta_trans);
	}

	// Leaf does nothing
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CCollBSPNode::rotate_y(const Mth::Vector & world_origin, Mth::ERot90 rot_y, bool root_node)
{
	// Take it to the origin
	if (root_node)
	{
		translate(-world_origin);
	}

	// Rotate node
	if (IsNode())
	{
		int axis = GetSplitAxis();

		if (axis != Y)		// Y won't change
		{
			int other_axis = (axis == X) ? Z : X;		// So we know what axis we potentially flip to

			switch (rot_y)
			{
			case Mth::ROT_0:
				break;

			case Mth::ROT_90:
				if (axis == X)
				{
					SetSplitPoint(-GetSplitPoint());
					m_node.m_children.SetLeftGreater(!m_node.m_children.IsLeftGreater());
				}
				SetSplitAxis(other_axis);
				break;

			case Mth::ROT_180:
				SetSplitPoint(-GetSplitPoint());
				m_node.m_children.SetLeftGreater(!m_node.m_children.IsLeftGreater());
				break;

			case Mth::ROT_270:
				if (axis == Z)
				{
					SetSplitPoint(-GetSplitPoint());
					m_node.m_children.SetLeftGreater(!m_node.m_children.IsLeftGreater());
				}
				SetSplitAxis(other_axis);
				break;

			default:
				Dbg_MsgAssert(0, ("CCollBSPNode::rotate_y() out of range: %d", rot_y));
				break;
			}
		}

		// Traverse the tree
		GetLessBranch()->rotate_y(world_origin, rot_y, false);
		GetGreaterBranch()->rotate_y(world_origin, rot_y, false);
	}
	// Leaf does nothing

	// Put it back
	if (root_node)
	{
		translate(world_origin);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CCollBSPNode::scale(const Mth::Vector & world_origin, const Mth::Vector & scale, bool root_node)
{
	// Take it to the origin
	if (root_node)
	{
		translate(-world_origin);
	}

	// Scale node
	if (IsNode())
	{
		int axis = GetSplitAxis();

		SetFSplitPoint(GetFSplitPoint() * scale[axis]);

		// Traverse the tree
		GetLessBranch()->scale(world_origin, scale, false);
		GetGreaterBranch()->scale(world_origin, scale, false);
	}
	// Leaf does nothing

	// Put it back
	if (root_node)
	{
		translate(world_origin);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollObjTriData::s_init_tree(CCollBSPNode *p_tree, void *p_base_node_addr, void *p_base_face_idx_addr)
{
	if (p_tree->IsLeaf())
	{
		// Set face index array pointer
		int face_idx = (int) p_tree->m_leaf.mp_face_idx_array;
		p_tree->m_leaf.mp_face_idx_array = (FaceIndex *) p_base_face_idx_addr;
		p_tree->m_leaf.mp_face_idx_array += face_idx;

		//Dbg_Assert(((int) p_leaf->mp_less_branch) == -1);
		//Dbg_Assert(((int) p_leaf->mp_greater_branch) == -1);

		//p_leaf->mp_less_branch = NULL;
		//p_leaf->mp_greater_branch = NULL;
	} else {
		Dbg_MsgAssert(p_tree->GetSplitAxis() < 3, ("BSP split axis is %d", p_tree->GetSplitAxis()));
		// Set branch pointers
		p_tree->m_node.m_children.SetBasePointer((CCollBSPNode *)((int) p_tree->m_node.m_children.GetBasePointer() + (int) p_base_node_addr));

		// And init branches
		s_init_tree(p_tree->GetLessBranch(), p_base_node_addr, p_base_face_idx_addr);
		s_init_tree(p_tree->GetGreaterBranch(), p_base_node_addr, p_base_face_idx_addr);
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollObjTriData::InitBSPTree()
{
	// Initialize sequence face index list, if not done yet
	if (s_seq_face_index_buffer[0] != 0)
	{
		for (int i = 0; i < MAX_FACE_INDICIES; i++)
		{
			s_seq_face_index_buffer[i] = i;
		}
	}

	Dbg_MsgAssert(m_num_faces < MAX_FACE_INDICIES, ("Too many polys in a collision sector: %d", m_num_faces));

	//Dbg_MsgAssert(0, ("Node size %d; Leaf size %d", sizeof(CCollBSPNode), sizeof(CCollBSPLeaf)));

	// Create the tree
   	// Don't use this since it is pip-ed
	//mp_bsp_tree = create_bsp_tree(m_bbox, s_seq_face_index_buffer, m_num_faces);

	return mp_bsp_tree != NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollObjTriData::DeleteBSPTree()
{
#ifndef USE_BSP_CLONE
   	// Don't use this since it is pip-ed
	return false;

	Dbg_MsgAssert(0, ("This should only be called on clones"));
#endif

	if (mp_bsp_tree)
	{
#ifndef USE_BSP_CLONE
		// Recursively delete
		delete mp_bsp_tree;
#else
		// Delete as array
		FaceIndex *p_face_index_array = mp_bsp_tree->find_bsp_face_index_array_start();
		if (p_face_index_array)
		{
			delete [] p_face_index_array;
		}
		delete [] mp_bsp_tree;
#endif
		mp_bsp_tree = NULL;

		return true;
	} else {
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollObjTriData::calc_split_faces(uint axis, float axis_distance, FaceIndex *p_face_indexes,
										  uint num_faces, uint & less_faces, uint & greater_faces,
										  FaceIndex *p_less_face_indexes, FaceIndex *p_greater_face_indexes)
{
	less_faces = greater_faces = 0;

	for (uint i = 0; i < num_faces; i++)
	{
		bool less = false, greater = false;

		// Check the face
		for (uint j = 0; j < 3; j++)
		{
			uint vidx = GetFaceVertIndex(i, j);
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
			if (GetRawVertexPos(vidx)[axis] < axis_distance)
			{
				less = true;
			} else if (GetRawVertexPos(vidx)[axis] >= axis_distance)
			{
				greater = true;
			}
#else
			if (mp_vert_pos[vidx][axis] < axis_distance)
			{
				less = true;
			} else if (mp_vert_pos[vidx][axis] >= axis_distance)
			{
				greater = true;
			}
#endif
		}

		Dbg_Assert(less || greater);

		// Increment counts and possibly put in new array
		if (less)
		{
			if (p_less_face_indexes)
			{
				p_less_face_indexes[less_faces] = p_face_indexes[i];
			}
			less_faces++;
		}
		if (greater)
		{
			if (p_greater_face_indexes)
			{
				p_greater_face_indexes[greater_faces] = p_face_indexes[i];
			}
			greater_faces++;
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#if 0
CCollBSPLeaf * CCollObjTriData::create_bsp_leaf(FaceIndex *p_face_indexes, uint num_faces)
{
	CCollBSPLeaf *p_bsp_leaf = new CCollBSPLeaf;
	p_bsp_leaf->m_split_axis = -1;
	p_bsp_leaf->mp_less_branch = NULL;
	p_bsp_leaf->mp_greater_branch = NULL;

	// Make new array in BottomUp memory
	p_bsp_leaf->m_num_faces = num_faces;
	p_bsp_leaf->mp_face_idx_array = new FaceIndex[num_faces];
	for (uint i = 0; i < num_faces; i++)
	{
		p_bsp_leaf->mp_face_idx_array[i] = p_face_indexes[i];
	}

	return p_bsp_leaf;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollBSPNode * CCollObjTriData::create_bsp_tree(const Mth::CBBox & bbox, FaceIndex *p_face_indexes, uint num_faces, uint level)
{
	if ((num_faces <= s_max_face_per_leaf) || (level == s_max_tree_levels)) // Check if this should be a leaf
	{
		return create_bsp_leaf(p_face_indexes, num_faces);
	} else {																// Create Node

		// Find initial splits on the three axis
		Mth::Vector mid_width((bbox.GetMax() - bbox.GetMin()) * 0.5f);
		Mth::Vector mid_split(mid_width + bbox.GetMin());

		// Find the weighting of the three potential splits
		uint less_faces[3], greater_faces[3];
		calc_split_faces(X, mid_split[X], p_face_indexes, num_faces, less_faces[X], greater_faces[X]);
		calc_split_faces(Y, mid_split[Y], p_face_indexes, num_faces, less_faces[Y], greater_faces[Y]);
		calc_split_faces(Z, mid_split[Z], p_face_indexes, num_faces, less_faces[Z], greater_faces[Z]);

		// Figure out best split
		int best_axis = -1;
		float best_diff = -1;
		const int duplicate_threshold = (num_faces * 7) / 10;	// tunable
		for (uint axis = X; axis <= Z; axis++)
		{
			float new_diff = (float)fabs((float)(less_faces[axis] - greater_faces[axis]));
			int duplicates = less_faces[axis] + greater_faces[axis] - num_faces;

			if (duplicates >= duplicate_threshold)
				continue;

			new_diff += duplicates;				// tunable
			new_diff /= mid_width[axis];		// tunable

			if ((best_axis < 0) || (new_diff < best_diff))
			{
				best_axis = axis;
				best_diff = new_diff;
			}
		}

		if (best_axis < 0)			// Couldn't make a good split, give up
		{
			return create_bsp_leaf(p_face_indexes, num_faces);
		}

		// We need to allocate temp arrays
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

		// Allocate new temp arrays for the face indexes
		FaceIndex *p_less_face_indexes = new FaceIndex[less_faces[best_axis]];
		FaceIndex *p_greater_face_indexes = new FaceIndex[greater_faces[best_axis]];

		Mem::Manager::sHandle().PopContext();

		// Now fill in the array
		calc_split_faces(best_axis, mid_split[best_axis], p_face_indexes, num_faces, 
						 less_faces[best_axis], greater_faces[best_axis],
						 p_less_face_indexes, p_greater_face_indexes);

		// And the new bboxes
		Mth::CBBox less_bbox(bbox), greater_bbox(bbox);
		Mth::Vector less_max = less_bbox.GetMax();
		Mth::Vector greater_min = greater_bbox.GetMin();
		less_max[best_axis] = mid_split[best_axis];
		greater_min[best_axis] = mid_split[best_axis];
		less_bbox.SetMax(less_max);
		greater_bbox.SetMin(greater_min);
	
		// And now calculate the branches
		CCollBSPNode *p_bsp_tree = new CCollBSPNode;
		p_bsp_tree->m_split_axis = best_axis;
		p_bsp_tree->m_split_point = mid_split[best_axis];
		p_bsp_tree->mp_less_branch = create_bsp_tree(less_bbox, p_less_face_indexes, less_faces[best_axis], level + 1);
		p_bsp_tree->mp_greater_branch = create_bsp_tree(greater_bbox, p_greater_face_indexes, greater_faces[best_axis], level + 1);

		// Free temp arrays
		delete p_less_face_indexes;
		delete p_greater_face_indexes;

		return p_bsp_tree;
	}
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CCollObjTriData::find_faces(CCollBSPNode *p_bsp_node, const Mth::CBBox & bbox)
{
	int axis = p_bsp_node->GetSplitAxis();

	if (axis == 3)	// Leaf
	{
		//CCollBSPLeaf *p_bsp_leaf = static_cast(p_bsp_node);		// We are certain that it is this type
		uint num_faces = p_bsp_node->m_leaf.m_num_faces;
		FaceIndex *p_dest_buffer = &(s_face_index_buffer[s_num_face_indicies]);
		FaceIndex *p_src_buffer = p_bsp_node->m_leaf.mp_face_idx_array;

		for (uint i = 0; i < num_faces; i++)
		{
			//s_face_index_buffer[s_num_face_indicies++] = p_bsp_leaf->mp_face_idx_array[i];
			*(p_dest_buffer++) = *(p_src_buffer++);
		}

		s_num_face_indicies += num_faces;
		Dbg_Assert(s_num_face_indicies < MAX_FACE_INDICIES);
	} else {							// Node
		float f_split_point = p_bsp_node->GetFSplitPoint();

		if (bbox.GetMin()[axis] < f_split_point)
		{
			find_faces(p_bsp_node->GetLessBranch(), bbox);
		}

		if (bbox.GetMax()[axis] >= f_split_point)
		{
			find_faces(p_bsp_node->GetGreaterBranch(), bbox);
		}
	}
}

// Calculate the normal of a face
// this is rather expensive if doing a lot of processing, so don't call
// this function more than you need to.
const Mth::Vector CCollObjTriData::GetFaceNormal(int face_idx) const
{
		Mth::Vector v0 = GetRawVertexPos(GetFaceVertIndex(face_idx,0));
		Mth::Vector v1 = GetRawVertexPos(GetFaceVertIndex(face_idx,1));
		Mth::Vector v2 = GetRawVertexPos(GetFaceVertIndex(face_idx,2));

		// Find normal
		Mth::Vector vTmp1(v1 - v0);
		Mth::Vector vTmp2(v2 - v0);
		Mth::Vector normal = Mth::CrossProduct(vTmp1, vTmp2);
		normal.Normalize();
		return normal;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

FaceIndex *			CCollObjTriData::FindIntersectingFaces(const Mth::CBBox & line_bbox, uint & num_faces)
{
	// Make sure we have a tree
	if (!mp_bsp_tree)
	{
		num_faces = m_num_faces;
		Dbg_Assert(num_faces < MAX_FACE_INDICIES);
		return s_seq_face_index_buffer;
	}

	// Search tree
	s_num_face_indicies = 0;
	find_faces(mp_bsp_tree, line_bbox);

	num_faces = s_num_face_indicies;

	#if 0	
	// see if there are any duplicates....
	int dups = 0;
	if (num_faces)
	{
		for (uint i = 0;imp_vert_pos	= (NsVector*)new char[CCollObjTriData::GetVertElemSize()*m_num_verts];
	m_new_coll->mp_raw_vert_pos	= mp_raw_vert_pos;
	intensity_array_size = m_num_faces * 3;
#else
	if (m_use_fixed_verts)
	{
		m_new_coll->mp_fixed_vert = new SFixedVert[m_num_verts];
	}
	else
	{
#ifdef FIXED_POINT_VERTICES
		m_new_coll->mp_float_vert = new SFloatVert[m_num_verts];
#else
		m_new_coll->mp_vert_pos	= new Mth::Vector[m_num_verts];
#endif
	}
	intensity_array_size = m_num_verts;
#endif		// __PLAT_NGC__
	if (m_use_face_small)
	{
		m_new_coll->mp_face_small = new SFaceSmall[m_num_faces];
	} else {
		m_new_coll->mp_faces = new SFace[m_num_faces];
	}

#ifdef FIXED_POINT_VERTICES
	if (m_new_coll->mp_float_vert && m_new_coll->mp_faces)
#else
	if (m_new_coll->mp_raw_vert_pos && m_new_coll->mp_faces)
#endif
	{
#ifdef __PLAT_NGC__
		memcpy(m_new_coll->mp_faces,		mp_faces		, m_num_faces * sizeof(SFace));
#else
		if (m_use_fixed_verts)
		{
			memcpy(m_new_coll->mp_fixed_vert,	mp_fixed_vert	, m_num_verts * CCollObjTriData::GetVertSmallElemSize());
		}
		else
		{
#ifdef FIXED_POINT_VERTICES
			memcpy(m_new_coll->mp_float_vert,	mp_float_vert	, m_num_verts * CCollObjTriData::GetVertElemSize());
#else
			memcpy(m_new_coll->mp_vert_pos,		mp_vert_pos		, m_num_verts * CCollObjTriData::GetVertElemSize());
#endif
		}
		if (m_use_face_small)
		{
			memcpy(m_new_coll->mp_face_small,	mp_face_small	, m_num_faces * sizeof(SFaceSmall));
		} else {
			memcpy(m_new_coll->mp_faces,		mp_faces		, m_num_faces * sizeof(SFace));
		}
#endif		// __PLAT_NGC__
	} else {
		Dbg_Error("Can't allocate new collision data");
		return NULL;
	}

	// Copy intensity array if there is one
	if (mp_intensity)
	{
		m_new_coll->mp_intensity = new uint8[intensity_array_size];
		memcpy(m_new_coll->mp_intensity, mp_intensity , intensity_array_size * sizeof(uint8));
	}

	// Set BSP tree to NULL for now until we come up with a method to copy and translate
#ifdef USE_BSP_CLONE
	m_new_coll->mp_bsp_tree = mp_bsp_tree->clone(instance);
#else
	m_new_coll->mp_bsp_tree = NULL;
#endif // USE_BSP_CLONE

	return m_new_coll;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollObjTriData::Translate(const Mth::Vector & delta_pos)
{
	Mth::Vector min_point, max_point;
	int i;


	// Translate BSP tree
	if (mp_bsp_tree)
	{
		//printf ("WARNING:  moving colision geometry that contains a BSP tree.  *** CLEARING ***\n");
		//mp_bsp_tree = NULL;
		mp_bsp_tree->translate(delta_pos);
	}

	#ifdef __PLAT_NGC__
	// Get Verts
	Mth::Vector *p_float_verts = NULL;
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	p_float_verts = new Mth::Vector[m_num_verts];
	Mem::Manager::sHandle().PopContext();
	get_float_array_from_data(p_float_verts);
	
	// Translate
	for ( i = 0; i < m_num_verts; i++ )
	{
		p_float_verts[i] += delta_pos;
	}

	// Put Verts
	put_float_array_into_data(p_float_verts);
	delete [] p_float_verts;


//	if ( !mp_offset ) mp_offset = new Mth::Vector;
//	mp_offset[0][X] += delta_pos[X];
//	mp_offset[0][Y] += delta_pos[Y];
//	mp_offset[0][Z] += delta_pos[Z];
	#else
	// Pos (don't touch W since the color is there)
	if (!m_use_fixed_verts)
	{
		for (i = 0; i < m_num_verts; i++)
		{
	#ifdef FIXED_POINT_VERTICES
			mp_float_vert[i].m_pos[X] += delta_pos[X];
			mp_float_vert[i].m_pos[Y] += delta_pos[Y];
			mp_float_vert[i].m_pos[Z] += delta_pos[Z];
	#else
			mp_vert_pos[i][X] += delta_pos[X];
			mp_vert_pos[i][Y] += delta_pos[Y];
			mp_vert_pos[i][Z] += delta_pos[Z];
	#endif // FIXED_POINT_VERTICES
		}
	}
	#endif		// __PLAT_NGC__

	// BBox
	min_point = m_bbox.GetMin() + delta_pos;
	max_point = m_bbox.GetMax() + delta_pos;
	m_bbox.Set(min_point, max_point);

	//Dbg_Message("Min BBox (%f, %f, %f)", m_bbox.GetMin()[X], m_bbox.GetMin()[Y], m_bbox.GetMin()[Z]);
	//Dbg_Message("Max BBox (%f, %f, %f)", m_bbox.GetMax()[X], m_bbox.GetMax()[Y], m_bbox.GetMax()[Z]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollObjTriData::RotateY(const Mth::Vector & world_origin, Mth::ERot90 rot_y)
{
	Mth::Vector min_point, max_point;
	int i;

	// Put object at origin
	Translate(-world_origin);

	// Rotate BSP tree
	if (mp_bsp_tree)
	{
		mp_bsp_tree->rotate_y(world_origin, rot_y, false);		// Don't allow it to do another translation
	}

	// Convert to floating point, if fixed point data
	Mth::Vector *p_float_verts = NULL;
	#ifndef __PLAT_NGC__
	if (m_use_fixed_verts)
	#endif		// __PLAT_NGC__
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
		p_float_verts = new Mth::Vector[m_num_verts];
		Mem::Manager::sHandle().PopContext();

		get_float_array_from_data(p_float_verts);
	}

	// Rotate
	for (i = 0; i < m_num_verts; i++)
	{

		if (p_float_verts)
		{
			p_float_verts[i].RotateY90(rot_y);
		}
		#ifndef __PLAT_NGC__
		else
		{
		#ifdef FIXED_POINT_VERTICES
			Mth::Vector pos(Mth::Vector::NO_INIT);
			GetRawVertexPos(i, pos);
			pos.RotateY90(rot_y);
			mp_float_vert[i].m_pos[X] = pos[X];
			mp_float_vert[i].m_pos[Y] = pos[Y];
			mp_float_vert[i].m_pos[Z] = pos[Z];
		#else
			mp_vert_pos[i].RotateY90(rot_y);
		#endif
		}
		#endif		// __PLAT_NGC__
	}

	// BBox rotate
	min_point = m_bbox.GetMin();
	max_point = m_bbox.GetMax();
	min_point.RotateY90(rot_y);
	max_point.RotateY90(rot_y);
	m_bbox.Reset();
	m_bbox.AddPoint(min_point);
	m_bbox.AddPoint(max_point);

	// Convert back to fixed point, if necessary
	if (p_float_verts)
	{
		put_float_array_into_data(p_float_verts);
		delete [] p_float_verts;
	}

	// Put object back
	Translate(world_origin);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollObjTriData::Scale(const Mth::Vector & world_origin, const Mth::Vector & scale)
{
	// Put object at origin
	Translate(-world_origin);

	// Scale BSP tree
	if (mp_bsp_tree)
	{
		mp_bsp_tree->scale(world_origin, scale, false);		// Don't allow it to do another translation
	}

	// Convert to floating point, if fixed point data
	Mth::Vector *p_float_verts = NULL;
	#ifndef __PLAT_NGC__
	if (m_use_fixed_verts)
	#endif		// __PLAT_NGC__
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
		p_float_verts = new Mth::Vector[m_num_verts];
		Mem::Manager::sHandle().PopContext();

		get_float_array_from_data(p_float_verts);
	}

	// Pos (don't touch W since the color is there)
	for (int i = 0; i < m_num_verts; i++)
	{
		if (p_float_verts)
		{
			p_float_verts[i][X] *= scale[X];
			p_float_verts[i][Y] *= scale[Y];
			p_float_verts[i][Z] *= scale[Z];
		}
		#ifndef __PLAT_NGC__
		else
		{
		#ifdef FIXED_POINT_VERTICES
			mp_float_vert[i].m_pos[X] *= scale[X];
			mp_float_vert[i].m_pos[Y] *= scale[Y];
			mp_float_vert[i].m_pos[Z] *= scale[Z];
		#else
			mp_vert_pos[i][X] *= scale[X];
			mp_vert_pos[i][Y] *= scale[Y];
			mp_vert_pos[i][Z] *= scale[Z];
		#endif
		}
		#endif		// __PLAT_NGC__
	}

	// BBox
	Mth::Vector min_point(m_bbox.GetMin()), max_point(m_bbox.GetMax());

	min_point[X] *= scale[X];
	min_point[Y] *= scale[Y];
	min_point[Z] *= scale[Z];

	max_point[X] *= scale[X];
	max_point[Y] *= scale[Y];
	max_point[Z] *= scale[Z];

	m_bbox.Set(min_point, max_point);

	// Convert back to fixed point, if necessary
	if (p_float_verts)
	{
		put_float_array_into_data(p_float_verts);
		delete [] p_float_verts;
	}

	// Put object back
	Translate(world_origin);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollObjTriData::SetRawVertexPos(int vert_idx, const Mth::Vector & pos)
{
	Dbg_MsgAssert(mp_bsp_tree == NULL, ("Cannot change a vertex within a BSP Tree"));

	set_vertex_pos(vert_idx, pos);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollObjTriData::set_vertex_pos(int vert_idx, const Mth::Vector & pos)
{
#ifndef __PLAT_NGC__	// Since they are shared with the render data
	if (m_use_fixed_verts)
	{
		Mth::Vector rel_pos(pos - m_bbox.GetMin());

		Dbg_MsgAssert((rel_pos[X] >= 0.0f) && (rel_pos[Y] >= 0.0f) && (rel_pos[Z] >= 0.0f), ("Adding a vert that is outside of bounding box"));

		mp_fixed_vert[vert_idx].m_pos[X] = (uint16) (rel_pos[X] * COLLISION_SUB_INCH_PRECISION);
		mp_fixed_vert[vert_idx].m_pos[Y] = (uint16) (rel_pos[Y] * COLLISION_SUB_INCH_PRECISION);
		mp_fixed_vert[vert_idx].m_pos[Z] = (uint16) (rel_pos[Z] * COLLISION_SUB_INCH_PRECISION);
	}
	else
	{
#ifdef FIXED_POINT_VERTICES
		mp_float_vert[vert_idx].m_pos[X] = pos[X];
		mp_float_vert[vert_idx].m_pos[Y] = pos[Y];
		mp_float_vert[vert_idx].m_pos[Z] = pos[Z];
#else
		mp_vert_pos[vert_idx][X] = pos[X];
		mp_vert_pos[vert_idx][Y] = pos[Y];
		mp_vert_pos[vert_idx][Z] = pos[Z];
#endif
	}
#endif		// __PLAT_NGC__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollObjTriData::GetRawVertices(Mth::Vector *p_vert_array) const
{
	get_float_array_from_data(p_vert_array);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollObjTriData::SetRawVertices(const Mth::Vector *p_vert_array)
{
	// Must set the bounding box first in case data is in fixed point
	m_bbox.Reset();
	for (int i = 0; i < m_num_verts; i++)
	{
		m_bbox.AddPoint(p_vert_array[i]);
	}

	put_float_array_into_data(p_vert_array);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#define MAX_USED 80000

void	CCollObjTriData::get_float_array_from_data(Mth::Vector *p_float_array) const
{
#ifdef __PLAT_NGC__
	if ( mp_cloned_vert_pos )
	{
		// Already cloned, just copy.
		for ( int i = 0; i < m_num_verts; i++ )
		{
			p_float_array[i][X] = mp_cloned_vert_pos[i].x;
			p_float_array[i][Y] = mp_cloned_vert_pos[i].y;
			p_float_array[i][Z] = mp_cloned_vert_pos[i].z;
			p_float_array[i][W] = 0.0f;
		}
	}
	else
	{
		// We need to pull the verts out of the main render vert list.
		int lp;
		int lp2;
		int index;
		unsigned char used[(MAX_USED/8)];

		// Build a bit list of used verts.
		for ( lp = 0; lp < (MAX_USED/8); lp++ ) used[lp] = 0;

		for ( lp = 0; lp < m_num_faces; lp++ )
		{
			index = mp_faces[lp].m_vertex_index[0];
			used[(index>>3)] |= (1<<(index&7));
			index = mp_faces[lp].m_vertex_index[1];
			used[(index>>3)] |= (1<<(index&7));
			index = mp_faces[lp].m_vertex_index[2];
			used[(index>>3)] |= (1<<(index&7));
		}

		// Copy verts out in order.
		int num_copied = 0;
		for ( lp = 0; lp < (MAX_USED/8); lp++ )
		{
			if ( used[lp] )
			{
				for ( lp2 = 0; lp2 < 8; lp2++ )
				{
					if ( used[lp] & (1<>3)] |= (1<<(index&7));
			index = mp_faces[lp].m_vertex_index[1];
			used[(index>>3)] |= (1<<(index&7));
			index = mp_faces[lp].m_vertex_index[2];
			used[(index>>3)] |= (1<<(index&7));
		}

		// Remap faces.
		int num_copied = 0;
		for ( lp = 0; lp < (MAX_USED/8); lp++ )
		{
			if ( used[lp] )
			{
				for ( lp2 = 0; lp2 < 8; lp2++ )
				{
					if ( used[lp] & (1<m_flags & ( 1 << 31 ))
		{
			continue;
		}
		
		Mth::Vector	v[4];
		v[0] = GetRawVertexPos(GetFaceVertIndex(fidx0, 0));
		v[1] = GetRawVertexPos(GetFaceVertIndex(fidx0, 1));
		v[2] = GetRawVertexPos(GetFaceVertIndex(fidx0, 2));
//		printf( "Occlusion Triangle %d\n(%f,%f,%f)\n(%f,%f,%f)\n(%f,%f,%f)\n",fidx0,v[0][X],v[0][Y],v[0][Z],v[1][X],v[1][Y],v[1][Z],v[2][X],v[2][Y],v[2][Z] );

		for( int fidx1 = fidx0 + 1; fidx1 < m_num_faces; ++fidx1 )
		{
			SFaceInfo *p_face1 = get_face_info(fidx1);

			// Check we haven't already used this face.
			if( p_face1->m_flags & ( 1 << 31 ))
			{
				continue;
			}

			v[0] = GetRawVertexPos(GetFaceVertIndex(fidx1, 0));
			v[1] = GetRawVertexPos(GetFaceVertIndex(fidx1, 1));
			v[2] = GetRawVertexPos(GetFaceVertIndex(fidx1, 2));
//			printf( "Occlusion Triangle %d\n(%f,%f,%f)\n(%f,%f,%f)\n(%f,%f,%f)\n",fidx1,v[0][X],v[0][Y],v[0][Z],v[1][X],v[1][Y],v[1][Z],v[2][X],v[2][Y],v[2][Z] );

			int indices_matched0[3]	= { -1, -1, -1 };
			int indices_matched1[3]	= { -1, -1, -1 };
			int num_indices_matched	= 0;

			for( int i = 0; i < 3; ++i )
			{
				if( GetFaceVertIndex(fidx0, i) == GetFaceVertIndex(fidx1, 0) )
				{
					indices_matched0[i] = GetFaceVertIndex(fidx0, i);
					indices_matched1[0] = GetFaceVertIndex(fidx1, 0);
					++num_indices_matched;
				}
				else if( GetFaceVertIndex(fidx0, i) == GetFaceVertIndex(fidx1, 1) )
				{
					indices_matched0[i] = GetFaceVertIndex(fidx0, i);
					indices_matched1[1] = GetFaceVertIndex(fidx1, 1);
					++num_indices_matched;
				}
				else if( GetFaceVertIndex(fidx0, i) == GetFaceVertIndex(fidx1, 2) )
				{
					indices_matched0[i] = GetFaceVertIndex(fidx0, i);
					indices_matched1[2] = GetFaceVertIndex(fidx1, 2);
					++num_indices_matched;
				}
			}

			// Three indices matched is just plain wrong.
			Dbg_Assert( num_indices_matched < 3 );

			// Two indices matched is just what we want.
			if( num_indices_matched == 2 )
			{
				// Make sure that the 2 tris lie in the same plane, which is no longer necessarily the case since they could be
				// tris which adjoin at a right angle.
				v[0]				= GetRawVertexPos(GetFaceVertIndex( fidx0, 0 ));
				v[1]				= GetRawVertexPos(GetFaceVertIndex( fidx0, 1 ));
				v[2]				= GetRawVertexPos(GetFaceVertIndex( fidx0, 2 ));
				Mth::Vector norm0	= Mth::CrossProduct( v[1] - v[0], v[2] - v[0] );
				v[0]				= GetRawVertexPos(GetFaceVertIndex( fidx1, 0 ));
				v[1]				= GetRawVertexPos(GetFaceVertIndex( fidx1, 1 ));
				v[2]				= GetRawVertexPos(GetFaceVertIndex( fidx1, 2 ));
				Mth::Vector norm1	= Mth::CrossProduct( v[1] - v[0], v[2] - v[0] );
				norm0.Normalize();
				norm1.Normalize();
				
				if(( fabs( norm1[X]-norm0[X]) <= 0.05f ) && ( fabs( norm1[Y]-norm0[Y]) <= 0.05f ) && ( fabs( norm1[Z]-norm0[Z]) <= 0.05f ))
				{
								
					// These two tris are coplanar.
				
					// Get the index from tri1 that isn't shared with tri0.
					int unshared_tri1_index = -1;
					for( int i = 0; i < 3; ++i )
					{
						if( indices_matched1[i] == -1 )
						{
							unshared_tri1_index = GetFaceVertIndex(fidx1, i);
							break;
						}
					}
					Dbg_Assert( unshared_tri1_index >= 0 );
				
					// Fill in the missing 2 verts, one from each tri. What is important here is which side of triangle0 that
					// that triangle1 shares, since this will determine the overall order of the quad abcd.
					if(( indices_matched0[0] >= 0 ) && ( indices_matched0[1] >= 0 ))
					{
						// They share side 0->1. Thus the quad will be t0(0), t1(unshared), t0(1), t0(2).
						v[0] = GetRawVertexPos(GetFaceVertIndex(fidx0, 0));
						v[1] = GetRawVertexPos(unshared_tri1_index);
						v[2] = GetRawVertexPos(GetFaceVertIndex(fidx0, 1));
						v[3] = GetRawVertexPos(GetFaceVertIndex(fidx0, 2));
					}
					else if(( indices_matched0[1] >= 0 ) && ( indices_matched0[2] >= 0 ))
					{
						// They share side 1->2. Thus the quad will be t0(0), t0(1), t1(unshared), t0(2).
						v[0] = GetRawVertexPos(GetFaceVertIndex(fidx0, 0));
						v[1] = GetRawVertexPos(GetFaceVertIndex(fidx0, 1));
						v[2] = GetRawVertexPos(unshared_tri1_index);
						v[3] = GetRawVertexPos(GetFaceVertIndex(fidx0, 2));
					}
					else if(( indices_matched0[0] >= 0 ) && ( indices_matched0[2] >= 0 ))
					{
						// They share side 2->0. Thus the quad will be t0(0), t0(1), t0(2), t1(unshared).
						v[0] = GetRawVertexPos(GetFaceVertIndex(fidx0, 0));
						v[1] = GetRawVertexPos(GetFaceVertIndex(fidx0, 1));
						v[2] = GetRawVertexPos(GetFaceVertIndex(fidx0, 2));
						v[3] = GetRawVertexPos(unshared_tri1_index);
					}
					else
					{
						Dbg_Assert( 0 );
					}

//					printf( "Occlusion Quad\n(%f,%f,%f)\n(%f,%f,%f)\n(%f,%f,%f)\n(%f,%f,%f)\n",v[0][X],v[0][Y],v[0][Z],v[1][X],v[1][Y],v[1][Z],v[2][X],v[2][Y],v[2][Z],v[3][X],v[3][Y],v[3][Z] );

					// Register this quad with the engine.
					Nx::CEngine::sAddOcclusionPoly( 4, v, m_checksum );
				
					// Flag the two tris as having been used.
					Dbg_Assert(( p_face0->m_flags & ( 1 << 31 )) == 0 );
					Dbg_Assert(( p_face1->m_flags & ( 1 << 31 )) == 0 );
					p_face0->m_flags |= ( 1 << 31 );
					p_face1->m_flags |= ( 1 << 31 );
				
					// Break out of this inner loop now since we have found a pair.
					break;
				}
			}
		}
	}

	// Scan through and clear 'used' flag on faces.
	for( int fidx0 = 0; fidx0 < m_num_faces; ++fidx0 )
	{
		SFaceInfo *p_face0 = get_face_info(fidx0);
		p_face0->m_flags &= ~( 1 << 31 );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef	__DEBUG_CODE__

void	CCollObjTriData::DebugRender(uint32 ignore_1, uint32 ignore_0)
{
#ifdef	__NOPT_ASSERT__
	static Mth::Matrix ident_matrix(0.0f, 0.0f, 0.0f);	// static so it doesn't waste time in the constuctor

	// simple culling based on the world bbox
	Mth::Vector mid = (GetBBox().GetMax() + GetBBox().GetMin())/2.0f;
	float	radius = (mid - GetBBox().GetMin()).Length();

	if (Nx::CEngine::GetWireframeMode() == 6)	 // 6 = occlusion	  
	{
		// Rendering as 2d, to demontrate occlusion
		// we try to get the visibility flag from the engine

#if	0		//def	__PLAT_NGPS__	
	bool	drawn = true;

	Nx::CPs2Sector* p_sector = (Nx::CPs2Sector*)Nx::CEngine::sGetSector(m_checksum);
	if (p_sector)
	{
	
		NxPs2::CGeomNode *p_node = (p_sector->GetEngineObject());
		if (p_node)
		{
			drawn = p_node->WasRendered();		
		}
	}


//
	if (drawn)
#else
		if ( Nx::CEngine::sIsVisible(mid,radius))
#endif	
		{
			Nx::CSector* p_sector = Nx::CEngine::sGetSector(m_checksum); // SLOWWWWWWWWWWWWW
			if (p_sector && !p_sector->GetVisibility())
			{
				DebugRender2D(ignore_1, ignore_0,0x000080);   			// red = hidden
			}
			else
			{
				DebugRender2D(ignore_1, ignore_0,0x808080);   			// grey = visible
			}
		}
		else
		{
			#ifdef	__PLAT_NGPS__
			// Mick:  Should be abel to use the actual Occluded bits here....
			if (NxPs2::IsInFrame( mid, radius ) && NxPs2::TestSphereAgainstOccluders(&mid,radius,0))
				DebugRender2D(ignore_1, ignore_0,0x40c040);	   // green = occluded 
			else
			#endif
			{
				DebugRender2DBBox(ignore_1, ignore_0,0x003030);		// dk yellow box and 
				DebugRender2DOct(ignore_1, ignore_0,0x003000);		// dark green ocotogon off camera
			}
		}
	}
	else
	{
//	    if ( Nx::CEngine::sIsVisible(mid,radius))
#		ifdef __PLAT_NGPS__	
   		if (NxPs2::IsInFrame( mid, radius ))
			DebugRender(ident_matrix, ignore_1, ignore_0, false);
#		endif
	}
#endif	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollObjTriData::DebugRender(const Mth::Matrix & transform, uint32 ignore_1, uint32 ignore_0, bool do_transform)
{
#ifdef	__PLAT_NGPS__	

	uint32	rgb = 0x000000;
	int n = m_num_faces;	
	int r=0;
	int g=0;
	int b=0;
	
	NxPs2::DMAOverflowOK = 2;


	switch (Nx::CEngine::GetWireframeMode())
	{
		case 1:	// Polygon density, red = high, white=higher 
		{
			#define	peak 500	
			if (n <= peak )
			{
				r = (255 * (n)) / peak;  	// r ramps up (black to red)	
			}
			else if (n <= peak * 2)
			{
				r = 255;
				b = (255 * (n - peak) / (peak));	// b&g ramps up (to white)
				g = b;
				
			}
			else
			{
				r = g = b = 255;
			}
			break;
		}
		case 2:	// Low Polygon density, red = high, white=higher 
		{
			#define	peak 500	
			if (n <= 2 )
			{
				r = g = b = 255;			// White = two or less
			}
			else if (n <= 4)
			{
				g = 255;				   // Green = 4 or less
				
			}
			else if (n <= 8)
			{
				b = 255;				   // blue = 8 or less
			}
			else if (n <= 16)			   
			{
				r = b = 255;			   // Magenta = 16 or less
			}
			break;
		}
		case 3:	   	// Each object a different color, based on checksum
		{
			r = (m_checksum >>16) & 0xff;
			g = (m_checksum >>8) & 0xff;
			b = (m_checksum ) & 0xff;
		}
		default:
			break;	
	}


	// fix up anything I did stupid
	if (r<0) r=0;
	if (r>255) r=255;
	if (g<0) g=0;
	if (g>255) g=255;
	if (b<0) b=0;
	if (b>255) b=255;

	rgb = (b<<16)|(g<<8)|r;	
	
	uint32 current = 12345678;


		#if 0
		// scan through all verts, and see if any are close, but not close enough....
		// obviously this will be somewhat slow
		NxPs2::BeginLines3D(0x80000000 + (0x00ff00ff));		// Magenta = unwelded verts

		for (int i=0;i0.0000001f && len < 1.0f)
				{
				
					Mth::Vector *v0,*v1;
					v0 = &mp_vert_pos[i];
					v1 = &mp_vert_pos[j];
					NxPs2::DrawLine3D((*v0)[X],(*v0)[Y],(*v0)[Z],(*v0)[X],(*v0)[Y]+100.0f,(*v0)[Z]);								
					NxPs2::DrawLine3D((*v1)[X],(*v1)[Y],(*v1)[Z],(*v1)[X],(*v1)[Y]+100.0f,(*v1)[Z]);								
				}
			}
		}
		NxPs2::EndLines3D();
		return;

		
		#endif


	Mth::Vector v0, v1, v2;
	

	for (int fidx = 0; fidx < m_num_faces; fidx++)
	{
		SFaceInfo *face = get_face_info(fidx);

		if (!(face->m_flags & ignore_1) && !(~face->m_flags & ignore_0))
		{
		
		if (Nx::CEngine::GetWireframeMode() == 0)   // face flags
		{
			rgb = 0xffffff;						// white = no flags
			if (face->m_flags & mFD_VERT)
			{
				rgb = 0x4040ff;			   		// red = vert
			}				
			if (face->m_flags & mFD_TRIGGER)
			{
				rgb = 0xff4040;				    // blue = trigger
			}				
			if (face->m_flags & mFD_WALL_RIDABLE)
			{
				rgb = 0x00ffff;				   // yellow = wallride
			}				
		}			   
				   
			// check for context changes
			if (current != rgb)
			{
				if (current == 12345678)
				{
					NxPs2::BeginLines3D(0x80000000 + (0x00ffffff & rgb));
				}
				else
				{
		    		NxPs2::ChangeLineColor(0x80000000 + (0x00ffffff & rgb));
				}
				current = rgb;
			}

#ifdef FIXED_POINT_VERTICES
			if (do_transform)
			{
				v0 = transform.TransformAsPos(GetRawVertexPos(GetFaceVertIndex(fidx, 0)));
				v1 = transform.TransformAsPos(GetRawVertexPos(GetFaceVertIndex(fidx, 1)));
				v2 = transform.TransformAsPos(GetRawVertexPos(GetFaceVertIndex(fidx, 2)));
			}
			else
			{
				v0 = GetRawVertexPos(GetFaceVertIndex(fidx, 0));
				v1 = GetRawVertexPos(GetFaceVertIndex(fidx, 1));
				v2 = GetRawVertexPos(GetFaceVertIndex(fidx, 2));
			}
#else
			if (do_transform)
			{
				v0 = transform.TransformAsPos(mp_vert_pos[GetFaceVertIndex(fidx, 0)]);
				v1 = transform.TransformAsPos(mp_vert_pos[GetFaceVertIndex(fidx, 1)]);
				v2 = transform.TransformAsPos(mp_vert_pos[GetFaceVertIndex(fidx, 2)]);
			}
			else
			{
				v0 = mp_vert_pos[GetFaceVertIndex(fidx, 0)];
				v1 = mp_vert_pos[GetFaceVertIndex(fidx, 1)];
				v2 = mp_vert_pos[GetFaceVertIndex(fidx, 2)];
			}
#endif // FIXED_POINT_VERTICES

			NxPs2::DrawLine3D(v0[X], v0[Y], v0[Z], v1[X], v1[Y], v1[Z]);
			NxPs2::DrawLine3D(v0[X], v0[Y], v0[Z], v2[X], v2[Y], v2[Z]);
			NxPs2::DrawLine3D(v2[X], v2[Y], v2[Z], v1[X], v1[Y], v1[Z]);
		}
	}
	// only if we actually drew some
	if ( current != 12345678)
	{
		NxPs2::EndLines3D();
	}
#else
	const uint32 rgba = 0x0000FF80;

	for (int fidx = 0; fidx < m_num_faces; fidx++)
	{
		Mth::Vector v0, v1, v2;

		if (do_transform)
		{
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
			v0 = transform.TransformAsPos(GetRawVertexPos(GetFaceVertIndex(fidx, 0)));
			v1 = transform.TransformAsPos(GetRawVertexPos(GetFaceVertIndex(fidx, 1)));
			v2 = transform.TransformAsPos(GetRawVertexPos(GetFaceVertIndex(fidx, 2)));
#else
			v0 = transform.TransformAsPos(mp_vert_pos[GetFaceVertIndex(fidx, 0)]);
			v1 = transform.TransformAsPos(mp_vert_pos[GetFaceVertIndex(fidx, 1)]);
			v2 = transform.TransformAsPos(mp_vert_pos[GetFaceVertIndex(fidx, 2)]);
#endif // FIXED_POINT_VERTICES
		}
		else
		{
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
			v0 = GetRawVertexPos(GetFaceVertIndex(fidx, 0));
			v1 = GetRawVertexPos(GetFaceVertIndex(fidx, 1));
			v2 = GetRawVertexPos(GetFaceVertIndex(fidx, 2));
#else
			v0 = mp_vert_pos[GetFaceVertIndex(fidx, 0)];
			v1 = mp_vert_pos[GetFaceVertIndex(fidx, 1)];
			v2 = mp_vert_pos[GetFaceVertIndex(fidx, 2)];
#endif // FIXED_POINT_VERTICES
		}		
		// Draw Triangle
		Gfx::AddDebugLine(v0, v1, rgba, rgba, 1);
		Gfx::AddDebugLine(v1, v2, rgba, rgba, 1);
		Gfx::AddDebugLine(v2, v0, rgba, rgba, 1);
	}
#endif //	__PLAT_NGPS__	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static Mth::Vector x;
static Mth::Vector y;

inline void rot(Mth::Vector &v)
{
	float vx = v[X];
	float vy = v[Z];
	v[X] = vx * x[X] + vy * x[Z];
	v[Z] = vx * y[X] + vy * y[Z];
}




static float	debug_2d_scale =  0.02f;
static Mth::Vector s_overhead_cam_pos;

void	_debug_change_2d_scale(float x)
{
	debug_2d_scale *= x;
	if (debug_2d_scale > 0.5f) debug_2d_scale = 0.5f;
	if (debug_2d_scale < 0.002f) debug_2d_scale = 0.002f;
	
}

void	OverheadLine(Mth::Vector v0, Mth::Vector v1)
{
#ifdef	__PLAT_NGPS__	

	Mth::Vector offset;
	offset.Set(320.0f,0.0f,224.0f);

	v0 -= s_overhead_cam_pos;
	v1 -= s_overhead_cam_pos;

	v0 *= debug_2d_scale;
	v1 *= debug_2d_scale;

	rot(v0);
	rot(v1);

	v0 += offset;
	v1 += offset;
	
	if (v0[X] < 0 && v1[X] < 0) return;
	if (v0[Z] < 0 && v1[Z] < 0) return;
	if (v0[X] > 639 && v1[X] >639) return;
	if (v0[Z] > 447 && v1[Z] > 447) return;


	// Some simple clipping

	NxPs2::DrawLine2D(v0[X], v0[Z], 0.0f, v1[X],  v1[Z],0.0f);

#endif
}


void	CCollObjTriData::DebugRender2D(uint32 ignore_1, uint32 ignore_0, uint32 visible)
{
#ifdef	__PLAT_NGPS__	


	Gfx::Camera *cur_camera = Nx::CViewportManager::sGetCamera( 0 );
	s_overhead_cam_pos = cur_camera->GetPos();

	// get orientation from the camere
	y = cur_camera->GetMatrix()[Z];
	y[Y] = 0.0f;
	y.Normalize();
	x[X] =   y[Z];
	x[Y] = 0.0f;
	x[Z] = - y[X];

	uint32	rgb = visible;		 		// blue = visible
	Mth::Vector v0, v1, v2;
	NxPs2::BeginLines2D(0x80000000 + (0x00ffffff & rgb));
	for (int fidx = 0; fidx < m_num_faces; fidx++)
	{
		SFaceInfo *face = get_face_info(fidx);
		if (!(face->m_flags & ignore_1) && !(~face->m_flags & ignore_0))
		{
			GetRawVertexPos(GetFaceVertIndex(fidx, 0), v0);
			GetRawVertexPos(GetFaceVertIndex(fidx, 1), v1);
			GetRawVertexPos(GetFaceVertIndex(fidx, 2), v2);
			OverheadLine(v0,v1);
			OverheadLine(v1,v2);
			OverheadLine(v2,v0);
		}
	}
	NxPs2::EndLines2D();
#endif
}

void	CCollObjTriData::DebugRender2DBBox(uint32 ignore_1, uint32 ignore_0, uint32 visible)
{
#ifdef	__PLAT_NGPS__	


	Gfx::Camera *cur_camera = Nx::CViewportManager::sGetCamera( 0 );

	// get orientation from the camere
	y = cur_camera->GetMatrix()[Z];
	y[Y] = 0.0f;
	y.Normalize();
	x[X] =   y[Z];
	x[Y] = 0.0f;
	x[Z] = - y[X];

	uint32	rgb = visible;		 		// blue = visible
	
	Mth::Vector v0, v1, v2,v3;
	
	NxPs2::BeginLines2D(0x80000000 + (0x00ffffff & rgb));
	v0 = GetBBox().GetMax();
	v2 = GetBBox().GetMin();
	v1[X] = v2[X];
	v1[Z] = v0[Z];
	v3[X] = v0[X];
	v3[Z] = v2[Z];
	
	OverheadLine(v0,v1);
	OverheadLine(v1,v2);
	OverheadLine(v2,v3);
	OverheadLine(v3,v0);
	NxPs2::EndLines2D();
#endif
}

// draw as an octagon, to simultate the bounding sphere									   
void	CCollObjTriData::DebugRender2DOct(uint32 ignore_1, uint32 ignore_0, uint32 visible)
{
#ifdef	__PLAT_NGPS__	


	Gfx::Camera *cur_camera = Nx::CViewportManager::sGetCamera( 0 );

	// get orientation from the camere
	y = cur_camera->GetMatrix()[Z];
	y[Y] = 0.0f;
	y.Normalize();
	x[X] =   y[Z];
	x[Y] = 0.0f;
	x[Z] = - y[X];

	uint32	rgb = visible;		 		// blue = visible
	
	Mth::Vector v[8];
	
	Mth::Vector offset;
	
	offset.Set(320.0f,0.0f,224.0f);


	// simple culling based on the world bbox
	Mth::Vector mid = (GetBBox().GetMax() + GetBBox().GetMin())/2.0f;
	float	radius = (mid - GetBBox().GetMin()).Length();
	
	float	half = radius * 0.707106f;		


	v[0].Set(-half,0,-half);
	v[2].Set( half,0,-half);
	v[4].Set( half,0, half);
	v[6].Set(-half,0, half);
	v[1].Set( 0     ,0,-radius);
	v[3].Set( radius,0, 0     );
	v[5].Set( 0     ,0, radius);
	v[7].Set(-radius,0, 0     );

	NxPs2::BeginLines2D(0x80000000 + (0x00ffffff & rgb));
	for (int i=0;i<8;i++)
	{
		v[i] += mid;
	}
	for (int i=0;i<8;i++)
	{
		OverheadLine(v[i], v[(i+1)&7]);
	}
	NxPs2::EndLines2D();
#endif
}

#else
// Stub function for
void	CCollObjTriData::DebugRender(uint32 ignore_1, uint32 ignore_0) {}
void	CCollObjTriData::DebugRender(const Mth::Matrix & transform, uint32 ignore_1, uint32 ignore_0, bool do_transform) {}

#endif

// Checks to see if a coll sector has any potential "Uberfrig" holes or seams
// that would be where there is NO ground beneath a point
// Algorithm:
// for each edge or each triangle
//  find the midpoint of the edge
//  for left and right sides
//   check if there is a hole at either of 0.000001, 0.0001, 0.1, 1.0 inches away (perp to edge)
//	 if hole, then check six feet away
//     if collision six feet away, then we classify this as a hole, and flag it in the level 
//		                                (flag the edge, and the point with a hole)
//
// Note: the checks encompass the whole world, not just this sector
#ifdef	__DEBUG_CODE__

bool CheckEdgeAt(Mth::Vector& mid, Mth::Vector& left, float dist, CFeeler& feeler)
{

	float up = 1000.0f;

/*
	// if we are next to a wall, then we need to check from a much greater height
	feeler.SetLine(mid + left * 10.0f + up, mid - left * 10.0f + up);
	if (feeler.GetCollision())
	{
		up = 1000.0f;
	}
	else
	{
		// need to check for wall from both directions, I think
		feeler.FlipDirection();
		if (feeler.GetCollision())
		{
			up = 1000.0f;		
		}
	}
*/
	
	feeler.SetLine(mid + left * dist + Mth::Vector(0.0f,up,0.0f),
				   mid + left * dist + Mth::Vector(0.0f,-1000.0f,0.0f));
//				   mid + left * dist + Mth::Vector(0.0f,-5800.0f,0.0f));
				   
//	feeler.DebugLine(0,255,0);				   
				   
	return feeler.GetCollision(false);	
}									  
									  
void			CheckEdgeForHoles(Mth::Vector v0, Mth::Vector v1)
{
	Mth::Vector	mid = (v0 + v1) / 2.0f;
	Mth::Vector left = v1 - v0;
	left.Normalize();
	if (left[X] == 0.0f && left[Z] == 0.0f)
	{
		// edge is perfectly vertical, so return
		return;
	}

	// make left vector be at right angles to the edge in the XZ plane	
	float x = left[X];
	left[X] = left[Z];
	left[Z] = -x;
	left[Y] = 0;

	CFeeler	feeler;
	
	// first check six feet away, to eliminate around the edge of the level 

	if (!CheckEdgeAt(mid, left, 72.0f,feeler))
	{
		// hole six feet away, so return
//		Gfx::AddDebugLine(mid+left*60.0f, mid+left*60.0f * 10.0f,0xff0000);
		return;
	}


	for (float x = 0.001f; x<0.0015f; x *= 10.0f)   // does 0.001, 0.01, 0.10
	{
		if (!CheckEdgeAt(mid,left,x,feeler))
		{
			// Found a hole!!!
			// we draw bunch of lines of differning length, so we can see it when we zoom in
			
			Gfx::AddDebugLine(v0, v1,0xff00ff);  // magenta = bad edge

//			feeler.DebugLine(0,0,255);			// blue = actual collision line			
			Gfx::AddDebugLine(mid+left*x, mid+left*x + Mth::Vector(0,40,0),0xffff);  
			Gfx::AddDebugLine(mid+left*x, mid+left*x + Mth::Vector(0,200,0),0xffff); 
			Gfx::AddDebugLine(mid+left*x, mid+left*x + Mth::Vector(0,1000,0),0xffff);
		}
	}

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollObjTriData::CheckForHoles()
{

	printf ("\nChecking for unvelded verts in object with %d verts\n",m_num_verts);

	for (int i=0;i0.0000001f && len < 0.1f)
			{
			
#ifndef __PLAT_NGC__
				Gfx::AddDebugLine(GetRawVertexPos(i), GetRawVertexPos(i) + Mth::Vector(0,400,0),0xff00ff);  
				Gfx::AddDebugLine(GetRawVertexPos(i), GetRawVertexPos(i) + Mth::Vector(0,40,0),0xff00ff);  
#endif		// __PLAT_NGC__
				printf ("Found one, at dist %f,m check the magenta lines\n",len);
			}
		}
	}


	printf ("Then Checking for holes in object with %d faces\n",m_num_faces);
	for (int fidx = 0; fidx < m_num_faces; fidx++)
	{
		
		SFaceInfo *face = get_face_info(fidx);
		if (!(face->m_flags & mFD_NON_COLLIDABLE))
		{
			CheckEdgeForHoles(GetRawVertexPos(GetFaceVertIndex(fidx, 0)),GetRawVertexPos(GetFaceVertIndex(fidx, 1)));
			CheckEdgeForHoles(GetRawVertexPos(GetFaceVertIndex(fidx, 1)),GetRawVertexPos(GetFaceVertIndex(fidx, 2)));
			CheckEdgeForHoles(GetRawVertexPos(GetFaceVertIndex(fidx, 2)),GetRawVertexPos(GetFaceVertIndex(fidx, 0)));
		}
	}

}
#endif

} // namespace Nx



================================================
FILE: Code/Gel/Collision/CollTriData.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx														**
**																			**
**	File name:		CollTriData.h											**
**																			**
**	Created: 		02/27/2002	-	grj										**
**																			**
*****************************************************************************/

#ifndef	__GEL_COLLTRIDATA_H
#define	__GEL_COLLTRIDATA_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#include 

#include 

#ifdef __PLAT_NGC__
#include 
#include 
#endif

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// Precision for fixed point split point value
#define COLLISION_SUB_INCH_PRECISION 16.0f
#define COLLISION_RECIPROCAL_SUB_INCH_PRECISION 0.0625f

namespace Nx
{

class CCollStatic;
class CCollMovable;
class CCollStaticTri;
class CCollMovTri;
class CCollObjTriData;
struct CollData;
class CBatchTriCollMan;

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/


////////////////////////////////////////////////////////////////
// Axis-Aligned BSP tree node
//
class CCollBSPNode
{
public:
						CCollBSPNode();

	// These functions allow us to have "virtual" functionality with an instance.  We can safely
	// cast a pointer to CCollBSPLeaf if isLeaf() returns true.
	uint8				GetSplitAxis() const { return m_leaf.m_split_axis & 0x3; }
	void				SetSplitAxis(int axis);
	bool				IsNode() const { return (GetSplitAxis() != 3); }
	bool				IsLeaf() const { return (GetSplitAxis() == 3); }

	CCollBSPNode *		GetLessBranch() { Dbg_Assert(IsNode()); return m_node.m_children.GetLessBranch(); }
	CCollBSPNode *		GetGreaterBranch() { Dbg_Assert(IsNode()); return m_node.m_children.GetGreaterBranch(); }

	FaceIndex *			GetFaceIndexArray() { Dbg_Assert(IsLeaf()); return m_leaf.mp_face_idx_array; }

	int					GetSplitPoint() const { return m_node.m_split_point >> NUM_AXIS_BITS; }
	void				SetSplitPoint(int ipoint) { m_node.m_split_point = (ipoint << NUM_AXIS_BITS) | GetSplitAxis(); }
	float				GetFSplitPoint() const { return ((float) GetSplitPoint()) * COLLISION_RECIPROCAL_SUB_INCH_PRECISION; }
	void				SetFSplitPoint(float point) { SetSplitPoint(point * COLLISION_SUB_INCH_PRECISION); }

private:
	////////////////////////////////////////////////////////////////
	// Basically, a class for a pointer that uses the lowest two bits for flags.
	// It also assumes that the pointer points to two consecutive nodes: the left
	// node and the right node.
	//
	class CCollBSPChildren
	{
	public:

		void			Init() { m_left_child_and_flags = 0; }

		// Actual branches
		CCollBSPNode *	GetLeftBranch() const { return GetBasePointer(); }
		CCollBSPNode *	GetRightBranch() const { return GetBasePointer() + 1; }

		// Figures out which branch is which
		CCollBSPNode *	GetLessBranch() const { return IsLeftGreater() ? GetRightBranch() : GetLeftBranch(); }
		CCollBSPNode *	GetGreaterBranch() const { return IsLeftGreater() ? GetLeftBranch() : GetRightBranch(); }

		// These two are needed for the cloning function
		CCollBSPNode *	GetBasePointer() const { return (CCollBSPNode *) (m_left_child_and_flags & ~0x3); }
		void			SetBasePointer(CCollBSPNode *p_base) { m_left_child_and_flags = ((uint32) p_base) | (m_left_child_and_flags & 0x3); }

		bool			IsLeftGreater() const { return m_left_child_and_flags & mLEFT_IS_GREATER; }
		void			SetLeftGreater(bool greater);

	private:

		// Constants
		enum
		{
			mLEFT_IS_GREATER = 0x01,			// Indicates that the left branch is the greater branch
		};

		uint32			m_left_child_and_flags; // points to left branch, right branch one node over
	};		

protected:

   	// Constants
	enum
	{
		NUM_AXIS_BITS = 2,			// Number of bits used in fixed split_point for the axis identification
	};

						~CCollBSPNode();

	// m_split axis must always be in line with the low byte of m_split_point
	struct SNode
	{
		int32				m_split_point;		// the point on the axis (low 2 bits is the axis itself)
		CCollBSPChildren	m_children;			// 32-bit value points to left branch, right branch one node over
	};

	struct SLeaf
	{
#ifdef __PLAT_NGC__		// Big endian on NGC
		uint16				m_num_faces;		// number in faces in face array
		uint8				m_pad1;
		uint8				m_split_axis;		// the axis it is split on (0 = X, 1 = Y, 2 = Z, 3 = Leaf)
#else
		uint8				m_split_axis;		// the axis it is split on (0 = X, 1 = Y, 2 = Z, 3 = Leaf)
		uint8				m_pad1;
		uint16				m_num_faces;		// number in faces in face array
#endif __PLAT_NGC__
		FaceIndex *			mp_face_idx_array;	// leaf
	};

	// Clone functions
	CCollBSPNode *		clone(bool instance = false);
	int					count_bsp_nodes();
	int					count_bsp_face_indices();
	FaceIndex *			find_bsp_face_index_array_start();

	// Modify functions
	void				translate(const Mth::Vector & delta_trans);		// delta since we don't store the original pos
	void				rotate_y(const Mth::Vector & world_origin, Mth::ERot90 rot_y, bool root_node = true);
	void				scale(const Mth::Vector & world_origin, const Mth::Vector & scale, bool root_node = true);

	// The split axis data MUST be in the same place in both SNode and SLeaf
	union
	{
		SNode			m_node;
		SLeaf			m_leaf;
	};

	// Friends
	friend CCollObjTriData;
};

#ifndef __PLAT_NGC__
#define FIXED_POINT_VERTICES
#endif		// __PLAT_NGC__

////////////////////////////////////////////////////////////////
// Collision data for an object
//
class CCollObjTriData
{
public:
	////////////////////////////////////////////////////////////
	// The following structure represent the disk format of
	// the collision data with the correct padding.  If the
	// disk format changes, these structures MUST change, too.
	//
   	struct SReadHeader
	{
		int 	m_version;
		int 	m_num_objects;
		int 	m_total_num_verts;
#ifdef __PLAT_NGC__
		int 	m_total_num_faces;
#else
		int 	m_total_num_faces_large;
		int 	m_total_num_faces_small;
		int		m_total_num_verts_large;
		int		m_total_num_verts_small;
		int		m_pad3;
#endif
	};

	//
						CCollObjTriData();
						~CCollObjTriData();

	bool				InitCollObjTriData(CScene * p_scene, void *p_base_vert_addr, void *p_base_intensity_addr, // Init collision sector after read from file
										   void *p_base_face_addr, void *p_base_node_addr, void *p_base_face_idx_addr);
	bool				InitBSPTree();					// Generate the BSP Tree
	bool				DeleteBSPTree();				// Delete the BSP Tree

	uint32				GetChecksum() const;
	void				SetChecksum(uint32 checksum);	// For cloning
	uint16				GetSectorFlags() const;
	void				SetSectorFlags(uint16 flags);
	void				ClearSectorFlags(uint16 flags);
	const Mth::CBBox &	GetBBox() const;
	int					GetNumVerts() const;
	int					GetNumFaces() const;

	// Vertex functions
#ifdef __PLAT_NGC__
	const unsigned char GetVertexIntensity(int face_idx, int vert_idx) const;
	void				SetVertexIntensity(int face_idx, int vert_idx, unsigned char intensity);

	void				SetVertexPointer( NsVector * p_vertex ) { mp_raw_vert_pos = p_vertex; }
	NsVector *			GetVertexPointer() { return mp_raw_vert_pos; }
#else
	const unsigned char GetVertexIntensity(int vert_idx) const;
	void				SetVertexIntensity(int vert_idx, unsigned char intensity);
#endif		//__PLAT_NGC__ 

	// Face functions
	uint16				GetFaceTerrainType(int face_idx) const;
	uint32				GetFaceFlags(int face_idx) const;
	uint16				GetFaceVertIndex(int face_idx, int vert_num) const;
	const Mth::Vector 	GetFaceNormal(int face_idx) const;
	Mth::Vector			GetRawVertexPos(int vert_idx) const;		// Must copy data for fixed point
	void				GetRawVertexPos(int vert_idx, Mth::Vector & pos) const;
	void				SetRawVertexPos(int vert_idx, const Mth::Vector & pos);
	void				GetRawVertices(Mth::Vector *p_vert_array) const;
	void				SetRawVertices(const Mth::Vector *p_vert_array);

	// Collision functions
	FaceIndex *			FindIntersectingFaces(const Mth::CBBox & line_bbox, uint & num_faces);

	// Clone and move functions
	CCollObjTriData *	Clone(bool instance = false, bool skip_no_verts = false);
	void				Translate(const Mth::Vector & delta_trans);		// delta since we don't store the original pos
	void				RotateY(const Mth::Vector & world_origin, Mth::ERot90 rot_y);
	void				Scale(const Mth::Vector & world_origin, const Mth::Vector & scale);

	void				ProcessOcclusion();

	// Debug functions
	void				DebugRender(uint32 ignore_1, uint32 ignore_0);
	void				DebugRender2D(uint32 ignore_1, uint32 ignore_0, uint32 visible);
	void				DebugRender2DBBox(uint32 ignore_1, uint32 ignore_0, uint32 visible);
	void				DebugRender2DOct(uint32 ignore_1, uint32 ignore_0, uint32 visible);
	void				DebugRender(const Mth::Matrix & transform, uint32 ignore_1, uint32 ignore_0, bool do_transfrom = true);
	void				CheckForHoles();

	// Element size functions for init
	static uint			GetVertElemSize();
	static uint			GetVertSmallElemSize();
	static uint			GetFaceElemSize();
	static uint			GetFaceSmallElemSize();

protected:
	//////////////////////////////////////////////////
	// All of the following member variables and structures
	// match the output of SceneConv.exe EXACTLY.
	// Do not change these without changing SceneConv.

	//////////////////////////////////////////////////
	// Internal structures for the faces (note its size isn't a qword multiple)
	struct SFaceInfo
	{
		uint16			m_flags;				// collision attributes
		uint16			m_terrain_type;			// terrain type
	};

	// Everything in SFace and SFaceSmall before the vert indexes MUST be the same.
	struct SFace
	{
		SFaceInfo		m_info;					// face info
		FaceIndex		m_vertex_index[3];		// indexes into the corresponding vertex array
	};

	struct SFaceSmall
	{
		SFaceInfo		m_info;					// face info
		FaceByteIndex	m_vertex_index[3];		// indexes into the corresponding vertex array
#ifndef __PLAT_NGC__
		uint8			m_pad;
#endif		// __PLAT_NGC__
	};

	// Use this structure to shove the rgba value into the W value of a Vector
	struct SOverlay
	{
		uint32			m_dont_touch_X;			// equivalent to Mth::Vector X
		uint32			m_dont_touch_Y;			// equivalent to Mth::Vector Y
		uint32			m_dont_touch_Z;			// equivalent to Mth::Vector Z
		uint8			m_type;					// equivalent to Mth::Vector W
		uint8			m_intensity;
		uint8			m_pad[2];
	};

	// This structure is used for 16-bit fixed point vertices
	struct SFloatVert
	{
		float			m_pos[3];
//		uint8			m_type;
//		uint8			m_intensity;
//		uint8			m_pad[2];
	};

	// This structure is used for 16-bit fixed point vertices
	struct SFixedVert
	{
		uint16			m_pos[3];			// Relative to min point of bounding box
//		uint8			m_pad;
//		uint8			m_intensity;
	};

	//////////////////////////////////////////////////
	// Members
	uint32				m_checksum;			// checksum of sector
	uint16				m_Flags;			// Sector-level flags
	uint16				m_num_verts;
	uint16				m_num_faces;
	uint8				m_use_face_small;	// Set to 1 if using SFaceSmall below
	uint8				m_use_fixed_verts;

	union {
		SFace *			mp_faces;			// array of faces
		SFaceSmall *	mp_face_small;		// array of small faces
	};

	Mth::CBBox			m_bbox;				// bounding box of sector

#ifdef __PLAT_NGC__
	NsVector *			mp_raw_vert_pos;
#else
	union {
#ifndef FIXED_POINT_VERTICES
		Mth::Vector *	mp_vert_pos;			// array of 32-bit vertices
		SOverlay *		mp_vert_rgba_overlay;	// overlay array of vertex colors for 32-bit vertices
#endif
		SFloatVert *	mp_float_vert;			// array of 32-bit vertices
		SFixedVert *	mp_fixed_vert;			// array of 16-bit vertices
	};
#endif

	CCollBSPNode *		mp_bsp_tree;		// head of BSP tree
	uint8 *				mp_intensity;		// Intensity list
#ifdef __PLAT_NGC__
	NsVector *			mp_cloned_vert_pos;
#else
	uint32				m_pad1;				// padding
#endif		// __PLAT_NGC__

	SFaceInfo *			get_face_info(int face_idx) const;

private:
	// Constants
	enum
	{
		MAX_FACE_INDICIES = 6000 //2048
	};

	// Gets and puts float data (converting to and from fixed point if necessary)
	void				get_float_array_from_data(Mth::Vector *p_float_array) const;
	void				put_float_array_into_data(const Mth::Vector *p_float_array);

	void				set_vertex_pos(int vert_idx, const Mth::Vector & pos);

	CCollBSPNode *		create_bsp_tree(const Mth::CBBox & bbox, FaceIndex *p_face_indexes, uint num_faces, uint level = 1);
	CCollBSPNode *		create_bsp_leaf(FaceIndex *p_face_indexes, uint num_faces);
	bool				calc_split_faces(uint axis, float axis_distance, FaceIndex *p_face_indexes,
										 uint num_faces, uint & less_faces, uint & greater_faces, 
										 FaceIndex *p_less_face_indexes = NULL, FaceIndex *p_greater_face_indexes = NULL);

	void				find_faces(CCollBSPNode *p_bsp_node, const Mth::CBBox & bbox);		// recursively search for faces

	static bool			s_init_tree(CCollBSPNode *p_tree, void *p_base_node_addr, void *p_base_face_idx_addr);

	static const uint	s_max_face_per_leaf;// maximum number faces per leaf
	static const uint	s_max_tree_levels;	// maximum number of levels in a tree

	static FaceIndex	s_face_index_buffer[MAX_FACE_INDICIES];
	static FaceIndex	s_seq_face_index_buffer[MAX_FACE_INDICIES];
	static uint			s_num_face_indicies;

	// Should be readable by all the CCollObj classes
	friend CCollStatic;
	friend CCollMovable;
	friend CCollStaticTri;
	friend CCollMovTri;
	friend CBatchTriCollMan;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CCollObjTriData::SetChecksum(uint32 checksum)
{
	m_checksum = checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint32				CCollObjTriData::GetChecksum() const
{
	return m_checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline int					CCollObjTriData::GetNumVerts() const
{
	return m_num_verts;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline int					CCollObjTriData::GetNumFaces() const
{
	return m_num_faces;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::CBBox &	CCollObjTriData::GetBBox() const
{
	return m_bbox;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint					CCollObjTriData::GetVertElemSize()
{
#ifdef __PLAT_NGC__
	return 1 * 3;
#else
#ifdef FIXED_POINT_VERTICES
	return sizeof(SFloatVert);
#else
	return sizeof(Mth::Vector);
#endif // FIXED_POINT_VERTICES
#endif		// __PLAT_NGC__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint					CCollObjTriData::GetVertSmallElemSize()
{
	return sizeof(SFixedVert);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint					CCollObjTriData::GetFaceElemSize()
{
	return sizeof(SFace);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint					CCollObjTriData::GetFaceSmallElemSize()
{
	return sizeof(SFaceSmall);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint16				CCollObjTriData::GetFaceTerrainType(int face_idx) const
{
	return (m_use_face_small) ? mp_face_small[face_idx].m_info.m_terrain_type : mp_faces[face_idx].m_info.m_terrain_type;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint32				CCollObjTriData::GetFaceFlags(int face_idx) const
{
	return (m_use_face_small) ? mp_face_small[face_idx].m_info.m_flags : mp_faces[face_idx].m_info.m_flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint16				CCollObjTriData::GetFaceVertIndex(int face_idx, int vert_num) const
{
	return (m_use_face_small) ? mp_face_small[face_idx].m_vertex_index[vert_num] : mp_faces[face_idx].m_vertex_index[vert_num];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Mth::Vector		 	CCollObjTriData::GetRawVertexPos(int vert_idx) const
{
#if !defined(FIXED_POINT_VERTICES)

#ifdef __PLAT_NGC__
	Mth::Vector v;

	if ( mp_cloned_vert_pos )
	{
		v[X] = mp_cloned_vert_pos[vert_idx].x;
		v[Y] = mp_cloned_vert_pos[vert_idx].y;
		v[Z] = mp_cloned_vert_pos[vert_idx].z;
		v[W] = 0.0f;
	}
	else
	{
		v[X] = mp_raw_vert_pos[vert_idx].x;
		v[Y] = mp_raw_vert_pos[vert_idx].y;
		v[Z] = mp_raw_vert_pos[vert_idx].z;
		v[W] = 0.0f;
	}

	return v;
#else
	return mp_vert_pos[vert_idx];
#endif		// __PLAT_NGC__

#else

	if (m_use_fixed_verts)
	{
		Mth::Vector pos(m_bbox.GetMin());

		pos[X] += ((float) mp_fixed_vert[vert_idx].m_pos[X]) * COLLISION_RECIPROCAL_SUB_INCH_PRECISION;
		pos[Y] += ((float) mp_fixed_vert[vert_idx].m_pos[Y]) * COLLISION_RECIPROCAL_SUB_INCH_PRECISION;
		pos[Z] += ((float) mp_fixed_vert[vert_idx].m_pos[Z]) * COLLISION_RECIPROCAL_SUB_INCH_PRECISION;
		pos[W]	= 0.0f;

		return pos;
	}
	else
	{
		Mth::Vector pos(Mth::Vector::NO_INIT);
		pos[X]			= mp_float_vert[vert_idx].m_pos[X];
		pos[Y]			= mp_float_vert[vert_idx].m_pos[Y];
		pos[Z]			= mp_float_vert[vert_idx].m_pos[Z];
		pos[W]			= 0.0f;
		return pos;
	}
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CCollObjTriData::GetRawVertexPos(int vert_idx, Mth::Vector & pos) const
{
#ifdef __PLAT_NGC__

	if ( mp_cloned_vert_pos )
	{
		pos[X] = mp_cloned_vert_pos[vert_idx].x;
		pos[Y] = mp_cloned_vert_pos[vert_idx].y;
		pos[Z] = mp_cloned_vert_pos[vert_idx].z;
		pos[W] = 0.0f;
	}
	else
	{
		pos[X] = mp_raw_vert_pos[vert_idx].x;
		pos[Y] = mp_raw_vert_pos[vert_idx].y;
		pos[Z] = mp_raw_vert_pos[vert_idx].z;
		pos[W] = 0.0f;
	}
#else
	if (m_use_fixed_verts)
	{
		pos = m_bbox.GetMin();
		pos[X] += ((float) mp_fixed_vert[vert_idx].m_pos[X]) * COLLISION_RECIPROCAL_SUB_INCH_PRECISION;
		pos[Y] += ((float) mp_fixed_vert[vert_idx].m_pos[Y]) * COLLISION_RECIPROCAL_SUB_INCH_PRECISION;
		pos[Z] += ((float) mp_fixed_vert[vert_idx].m_pos[Z]) * COLLISION_RECIPROCAL_SUB_INCH_PRECISION;
		pos[W]	= 0.0f;
	}
	else
	{
#ifdef FIXED_POINT_VERTICES
		pos[X]	= mp_float_vert[vert_idx].m_pos[X];
		pos[Y]	= mp_float_vert[vert_idx].m_pos[Y];
		pos[Z]	= mp_float_vert[vert_idx].m_pos[Z];
#else
		pos		= mp_vert_pos[vert_idx];
#endif
		pos[W]	= 0.0f;
	}
#endif		// __PLAT_NGC__
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef __PLAT_NGC__
inline const unsigned char CCollObjTriData::GetVertexIntensity(int face_idx, int vert_idx) const
{
	return mp_intensity[(face_idx * 3) + vert_idx];
}
#else
inline const unsigned char CCollObjTriData::GetVertexIntensity(int vert_idx) const
{
#ifdef FIXED_POINT_VERTICES
	return mp_intensity[vert_idx];
	//if (m_use_fixed_verts)
	//{
	//	return mp_fixed_vert[vert_idx].m_intensity;
	//}
	//else
	//{
	//	return mp_float_vert[vert_idx].m_intensity;
	//}
#else
	{
		return mp_vert_rgba_overlay[vert_idx].m_intensity;
	}
#endif // FIXED_POINT_VERTICES
}
#endif		// __PLAT_NGC__

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef __PLAT_NGC__
inline void					CCollObjTriData::SetVertexIntensity(int face_idx, int vert_idx, unsigned char intensity)
{
	mp_intensity[(face_idx * 3) + vert_idx] = intensity; 
}
#else
inline void					CCollObjTriData::SetVertexIntensity(int vert_idx, unsigned char intensity)
{
#ifdef FIXED_POINT_VERTICES
	mp_intensity[vert_idx] = intensity;
	//if (m_use_fixed_verts)
	//{
	//	mp_fixed_vert[vert_idx].m_intensity = intensity;
	//}
	//else
	//{
	//	mp_float_vert[vert_idx].m_intensity = intensity;
	//}
#else
	{
		mp_vert_rgba_overlay[vert_idx].m_intensity = intensity;
	}
#endif // FIXED_POINT_VERTICES
}
#endif		// __PLAT_NGC__

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline CCollObjTriData::SFaceInfo *	CCollObjTriData::get_face_info(int face_idx) const
{
	return (m_use_face_small) ? &(mp_face_small[face_idx].m_info) : &(mp_faces[face_idx].m_info);
}

} // namespace Nx

#endif  //	__GEL_COLLTRIDATA_H


================================================
FILE: Code/Gel/Collision/Collision.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx								 						**
**																			**
**	File name:		gel\collision\collide.cpp      							**
**																			**
**	Created by:		02/20/02	-	grj										**
**																			**
**	Description:															**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 	// for face flag stuff
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#ifdef	__PLAT_NGPS__
#define USE_VU0_MICROMODE
#endif

#define PRINT_CACHE_HITS 0

/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

Mth::Matrix		CCollStatic::sOrient;
Mth::Vector		CCollStatic::sWorldPos;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

static bool s_check_for_far = false;			// if true then we return the farthest result

bool CCollObj::s_found_collision(const Mth::Line *p_is, CCollObj *p_coll_object, SCollSurface *p_collSurface,
								 float distance, CollData *p_collData)
{
	Dbg_Assert( p_collData );
	
	bool good = false;
	bool updated = false;

	if (s_check_for_far)
	{
		if( distance >= p_collData->dist )
		{
			good = true;
		}
	}
	else
	{
		//Dbg_Message("Checking new dist %f against old dist %f", distance, p_collData->dist);
		if( distance <= p_collData->dist )
		{
			good = true;
		}
	}
	CollData 	old;
	
	
	if (!good)
	{
		old = *p_collData;				 
	}
			 

	// Get the user plugin offset														
	int index = p_collSurface->index;		// the index of the surface in the object
	
	// get the face flags from within this
	// note that this uses the "index" field, which is
	// an index of a face within the sector
	uint32  flags = p_coll_object->GetFaceFlags(index);
	
	// Test flags against the ignore_1 (ignore if a bit is set in flags)
	// and against ignore_0 (ignore if the bit is clear in flags)
					
	//Dbg_Message("*Flags %x ignore_0 %x ignore_1 %x", flags, p_collData->ignore_0, p_collData->ignore_1);
	if (!(flags & p_collData->ignore_1) && !(~flags & p_collData->ignore_0))
	{
		// Setup the collData members		
		
		p_collData->coll_found = true;     		// found at least one (might be overrridden, but found at least one)
		p_collData->surface = *p_collSurface;	// Store the collision surface
		p_collData->dist = distance;			// set distance (will end up being the smallest dist)
		p_collData->flags = flags; 				// Store face flags
		p_collData->terrain = (ETerrainType) p_coll_object->GetFaceTerrainType(index);
		p_collData->p_coll_object = p_coll_object;
		p_collData->trigger = (flags& mFD_TRIGGER);	// set flag if it's a trigger

		p_collData->node_name =  p_coll_object->GetChecksum();
		// Determine the point of intersection on the triangle
		// which will be in the direction of the line's vector at "distance" length
		Mth::Vector coll_point, line_vec;
		line_vec = p_is->m_end - p_is->m_start;

		coll_point = line_vec.Scale(distance);
		coll_point = p_is->m_start + coll_point;
		coll_point[W] = 1.0f;					// Hack to force into homogenious space.  Even though the math is correct here,
												// many times the line data fed to it isn't.
		p_collData->surface.point = coll_point;	// After looking at their implementation, I don't
												// trust "point" so I recalculate it

		// check for additional per-face callback
		
		if (p_collData->callback)
		{
			// call the callback function, and pass it this col data 
			p_collData->callback(p_collData);
		}
	
		updated = true;
	}
		
	if (!good)
	{
		*p_collData = old;				 
	}

	return (good && updated);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCollObj::sRayTriangleCollision(const Mth::Vector *rayStart, const Mth::Vector *rayDir,
									 Mth::Vector *v0, Mth::Vector *v1, Mth::Vector *v2, float *t)
{
#if 0
// #ifdef	__PLAT_NGPS__
// Turned off assem versions to test correction to false positive issue as discussed in C code below.  Dan - 1/21/3
#ifdef	USE_VU0_MICROMODE
	register bool result;

	asm __volatile__(
	"
	.set noreorder
	lqc2    vf06,%4				# v0
	lqc2    vf08,%6				# v2
	lqc2    vf07,%5				# v1
	lqc2    vf04,%2				# rayStart
	lqc2    vf05,%3				# rayDir

	vcallms	RayTriangleCollision	# call microsubroutine
	vnop							# interlocking instruction, waits for vu0 completion

	cfc2	%0,$vi02			# get boolean result from vu0

	beq		%0, $0, vu0RayTriDone	# skip copy of t if not needed
	nop

	qmfc2	$12, $vf17			# move t to $8
	sw		$12, %1				# and write out

vu0RayTriDone:

	.set reorder
	": "=&r" (result), "=m" (*t) : "m" (*rayStart), "m" (*rayDir), "m" (*v0), "m" (*v1) , "m" (*v2) : "$12" );

	return result;
#else
    //*t = 2.0f;
	bool result;

	asm __volatile__(
	"
	.set noreorder
	lqc2    vf06,0x0(%4)		# v0
	lqc2    vf08,0x0(%6)		# v2
	lqc2    vf07,0x0(%5)		# v1
	lqc2    vf04,0x0(%2)		# rayStart
	lqc2    vf05,0x0(%3)		# rayDir

	vsub.xyz vf10, vf08, vf06    # edge2 = v2 - v0
	vsub.xyz vf09, vf07, vf06    # edge1 = v1 - v0 
	vsub.xyz vf13, vf04, vf06    # tvec = rayStart - v0
   	li.s     $f4,0.00001		 # EPSILON
	vaddw.x  vf02, vf00, vf00w   # Load 1.0f to VF02x

	vopmula.xyz  ACC,vf05,vf10   # Cross product of ray normal and edge2 (pvec)
	vopmsub.xyz  vf12,vf10,vf05  # Second part of cross product
   	mfc1         $8,$f4
	li		     %0,0			 # store 0 for return value
	#li      $9,0x80	 		 # Set the mask X sign flag
	#li      $10,0x10			 # Set the mask W sign flag

	vmul.xyz  vf11,vf09,vf12     # det = edge1 * pvec [start]
	vmulax.x  ACC,vf09,vf12x
	vmul.xyz  vf15,vf13,vf12     # u = tvec * pvec [start]
	qmtc2	$8, $vf03
	vmadday.x ACC,vf02,vf11y
	vmaddz.x  vf11,vf02,vf11z    # det = edge1 * pvec [ready]
	vmulax.x  ACC,vf02,vf15x
	vmadday.x ACC,vf02,vf15y
	vmaddz.x  vf15,vf02,vf15z    # u = tvec * pvec [ready]
	vdiv 	  Q,vf00w,vf11x      # Q = 1.0f / det

	vopmula.xyz ACC,vf13,vf09    # qvec = crossProd(tvec, edge1);
	vopmsub.xyz vf14,vf09,vf13
	vsub.x      vf01,vf11,vf03   # If det < EPSILON
		vnop
		vnop
		vnop
		vnop
		vnop
	cfc2    $8,$vi17			 # transfer if det result (MAC register)
	vmulq.x vf15,vf15,Q          # u = (tvec * pvec) / det
	vnop
	vnop
	vnop
		vnop
		andi	$8, $8, 0x80		 # check result
	cfc2    $9,$vi17			 # transfer u result (MAC register)
	bne		$8, $0, vu0RayTriDone
	vaddx.w vf03, vf00, vf03x	 # put 1 + EPSILON in VF03w
	andi	$9, $9, 0x80		 # check result (u < 0) fail
	bne		$9, $0, vu0RayTriDone
	#vaddq.x vf20,vf00,Q          

    vsubw.x vf00,vf15,vf03w		 # if u > 1 + EPSILON
		vnop
		vnop
		vnop
		vnop
		vnop
	cfc2    $8,$vi17			 # transfer if u > 1 + EPSILON result (MAC register)
    vmul.xyz vf16,vf05,vf14      # v = rayDir * qvec [start] 
    vmulax.x ACC,vf05,vf14x
	andi	$8, $8, 0x80		 # check result (u > 1) fail
	beq		$8, $0, vu0RayTriDone
	vnop

    vmadday.x ACC,vf02,vf16
    vmaddz.x vf16,vf02,vf16z     # v = rayDir * qvec [ready]
	vnop
    vmul.xyz vf17,vf10,vf14      # t = edge2 * qvec [start]
    vmulax.x ACC,vf10,vf14x
    vmulq.x   vf16,vf16,Q        # v = (rayDir * qvec) / det;
	vnop
		vnop
		vnop
		vnop
		vnop
	cfc2    $8,$vi17			 # transfer if v < 0 result (MAC register)

	vmadday.x ACC,vf02,vf17
	vmaddz.x  vf17,vf02,vf17z    # t = edge2 * qvec [ready]
	vadd.x    vf01,vf15,vf16
	andi	  $8, $8, 0x80		 # check result (v < 0) fail
	bne		  $8, $0, vu0RayTriDone
    vmulq.x    vf17,vf17,Q       # t = (edge2 * qvec) / det

    vsubw.x vf31,vf01,vf03w      # if (u+v) > 1 + EPSILON [start]
	vnop
	vnop
	vnop
		vnop
		vnop
	cfc2    $8,$vi17			 # transfer if u+v > 1 + EPSILON result (MAC register)
    vsub.x vf00,vf17,vf00
	vnop
		vnop
		vnop
		vnop
	andi	$8, $8, 0x80		 # check result (u+v > 1 + EPSILON) fail
	cfc2    $9,$vi17			 # transfer if t < 0 result (MAC register)
	vsubx.w vf00,vf00,vf17       # if t > 1 [start]
	vnop
		vnop
		#vnop
		#vnop
		#vnop
	beq		$8, $0, vu0RayTriDone
	andi	$9, $9, 0x80		 # check result (t < 0) fail
	bne		$9, $0, vu0RayTriDone
	cfc2    $8,$vi17			 # transfer if t > 1 result (MAC register)
	andi	$8, $8, 0x10		 # check result (t > 1) fail
	bne		$8, $0, vu0RayTriDone
	qmfc2	$9, $vf17			 # move t to $9
	sw		$9, 0(%1)			 # and write out
	li		%0,1				 # store 1 for return value


vu0RayTriDone:
	.set reorder
	": "=r" (result), "+r" (t) : "r" (rayStart), "r" (rayDir), "r" (v0), "r" (v1) , "r" (v2) : "$8", "$9");
	//return *t <= 1.0f;
	return result;
#endif //	USE_VU0_MICROMODE
#else
    const float EPSILON = 0.00001f;

    /* find vectors for two edges sharing vert0 */
    Mth::Vector edge1 = *v1 - *v0;
    Mth::Vector edge2 = *v2 - *v0;
    
    /* begin calculating determinant - also used to calculate U parameter */
    Mth::Vector pvec = Mth::CrossProduct(*rayDir, edge2);

    /* if determinant is near zero, ray lies in plane of triangle */
    const float det = Mth::DotProduct(edge1, pvec);

#if 1 // Dan - 1/21/3
	// Moved division below several false returns and reduced mult count.  This probably removed the need for adding EPSILON to 1.0f for the u and v test,
	// but I don't know this for sure, so left it in.
	// Also, added EPSILON_2.  EPSILON is of appropriate scale for comparing floats of order one to zero, but is too small for comparing floats
	// on the order of the cube of a typical vertex edge.  In certain special cases the dot product in the calculation of det will require the
	// cancelation of very large floats, resulting in a zero which is larger than EPSILON.  This was causing false positives.
	// It is possible that this new larger EPSILON_2 will cause false negatives.  I suppose we shall see.
	// These changes HAVE NOT BEEN MADE to the assembler code versions.  They continue to suffer from the over-small EPSILON issue.

    const float EPSILON_2 = 0.03f;
    
	// We are back-facing, so also throw away negative
    if (det < EPSILON_2)
	{
        return false;
    }

	const float adjusted_det = (1.0f + EPSILON) * det;

    /* calculate distance from vert0 to ray origin */
    const Mth::Vector tvec = *rayStart - *v0;
    
    /* calculate U parameter and test bounds */
    float u = Mth::DotProduct(tvec, pvec);

    if (u < 0.0f || u > adjusted_det) {
        return false;
    }
    
    /* prepare to test V parameter */
    const Mth::Vector qvec = Mth::CrossProduct(tvec, edge1);
    
    /* calculate V parameter and test bounds */
    float v = Mth::DotProduct(*rayDir, qvec);

    if (v < 0.0f || u + v > adjusted_det) {
        return false;
    }

	const float inv_det = 1.0f / det;

    /* calculate t, ray intersects triangle */
    *t = Mth::DotProduct(edge2, qvec) * inv_det;

	// And update u & v, although we don't seem to use it now.
	//u *= inv_det;
	//v *= inv_det;

#else
	// We are back-facing, so also throw away negative
    if (det < EPSILON)
	{
        return false;
    }

	const float inv_det = 1.0f / det;

    /* calculate distance from vert0 to ray origin */
    const Mth::Vector tvec = *rayStart - *v0;
    
    /* calculate U parameter and test bounds */
    float u = Mth::DotProduct(tvec, pvec) * inv_det;

    if (u < 0.0f || u > (1.0f + EPSILON)) {
        return false;
    }
    
    /* prepare to test V parameter */
    const Mth::Vector qvec = Mth::CrossProduct(tvec, edge1);
    
    /* calculate V parameter and test bounds */
    float v = Mth::DotProduct(*rayDir, qvec) * inv_det;

    if (v < 0.0f || u + v > (1.0f + EPSILON)) {
        return false;
    }
    
    /* calculate t, ray intersects triangle */
    *t = Mth::DotProduct(edge2, qvec) * inv_det;

	// And update u & v, although we don't seem to use it now.
	//u *= inv_det;
	//v *= inv_det;
#endif

    return (*t <= 1.0f) && (*t >= 0.0f);
#endif

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCollObj::s2DLineLineCollision ( float p_start_X, float p_start_Y, float p_delta_X, float p_delta_Y, float q_start_X, float q_start_Y, float q_delta_X, float q_delta_Y, float *s )
{
	// determines collision between two lines (p and q) in 2D; reports the interval along p at which the collision occurs
	
	// Antonio's method; Real-Time Rendering, p 337
	
    const float EPSILON = 0.00000000001f;
	
	float a_X = q_delta_X;
	float a_Y = q_delta_Y;
	
	float b_X = p_delta_X;
	float b_Y = p_delta_Y;
	
	float c_X = p_start_X - q_start_X;
	float c_Y = p_start_Y - q_start_Y;
	
	float d = c_X * -a_Y + c_Y * a_X;
	
	float e = c_X * -b_Y + c_Y * b_X;
	
	float f = a_X * -b_Y + a_Y * b_X;
	
	if (f > 0.0f)
	{
		if (d < 0.0f || d > f) return false;
	}
	else
	{
		if (d > 0.0f || d < f) return false;
	}
	
	if (f > 0.0f)
	{
		if (e < 0.0f || e > f) return false;
	}
	else
	{
		if (e > 0.0f || e < f) return false;
	}
	
	if (Mth::Abs(f) < EPSILON)
	{
		*s = 0.0f;
		return true;
	}
	
	*s = d / f;
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCollObj::sRectangleTriangleCollision(const Mth::Rectangle *rect, const Mth::Vector *v0, const Mth::Vector *v1, const Mth::Vector *v2, Mth::Vector p_collision_endpoints[2], ETriangleEdgeType p_collision_triangle_edges[2] )
{
	// ERIT method; Real-Time Rendering, p 317
	
    const float EPSILON = 0.00001f;
	
	// calculate the plane equation of the rectangle
	Mth::Vector rectangle_n = Mth::CrossProduct(rect->m_first_edge, rect->m_second_edge);
	float rectangle_d = -Mth::DotProduct(rectangle_n, rect->m_corner);
	
	// calculate the distance (times rectangle_n squared) of the triangle vertices from the rectangle's plane
	float d0 = Mth::DotProduct(rectangle_n, *v0) + rectangle_d; 
	float d1 = Mth::DotProduct(rectangle_n, *v1) + rectangle_d; 
	float d2 = Mth::DotProduct(rectangle_n, *v2) + rectangle_d;
	 
	// if all vertices are on the same side of the plane, we reject
	if (d0 > 0.0f && d1 > 0.0f && d2 > 0.0f) return false;
	if (d0 < 0.0f && d1 < 0.0f && d2 < 0.0f) return false;
	
	// snap verticies near the plane to the plane
	if (Mth::Abs(d0) < EPSILON)
	{
		d0 = 0.0f;
	}
	if (Mth::Abs(d1) < EPSILON)
	{
		d1 = 0.0f;
	}
	if (Mth::Abs(d2) < EPSILON)
	{
		d2 = 0.0f;
	}
	
	// find the two points (p and q) on the plane of the rectangle which are the endpoints of the triangle's interception with the rectangle's plane
	Mth::Vector p, q;
	ETriangleEdgeType p_edge, q_edge;
	if ((d0 <= 0.0f && d1 >= 0.0f) || (d0 >= 0.0f && d1 <= 0.0f))
	{
		p_edge = TRIANGLE_EDGE_V0_V1;
		if (Mth::Abs(d0 - d1) > EPSILON)
		{
			p = *v0 + (*v1 - *v0) * d0 / (d0 - d1);
		}
		else
		{
			p = *v0;
		}
		
		if ((d1 <= 0.0f && d2 >= 0.0f) || (d1 >= 0.0f && d2 <= 0.0f))
		{
			q_edge = TRIANGLE_EDGE_V1_V2;
			if (Mth::Abs(d1 - d2) > EPSILON)
			{
				q = *v1 + (*v2 - *v1) * d1 / (d1 - d2);
			}
			else
			{
				q = *v1;
			}
		}
		else
		{
			q_edge = TRIANGLE_EDGE_V2_V0;
			if (Mth::Abs(d2 - d0) > EPSILON)
			{
				q = *v2 + (*v0 - *v2) * d2 / (d2 - d0);
			}
			else
			{
				q = *v2;
			}
		}
	}
	else
	{
		p_edge = TRIANGLE_EDGE_V1_V2;
		if (Mth::Abs(d1 - d2) > EPSILON)
		{
			p = *v1 + (*v2 - *v1) * d1 / (d1 - d2);
		}
		else
		{
			p = *v1;
		}
		
		q_edge = TRIANGLE_EDGE_V2_V0;
		if (Mth::Abs(d0 - d2) > EPSILON)
		{
			q = *v0 + (*v2 - *v0) * d0 / (d0 - d2);
		}
		else
		{
			q = *v0;
		}
	}
	
	// project the rectangle and the intersection endpoints into the coordinate plane which maximizes the area of the rectangle's projection
	
	float r_corner_S, r_corner_T;
	float r_first_edge_S, r_first_edge_T;
	float r_second_edge_S, r_second_edge_T;
	float p_S, p_T;
	float q_S, q_T;
	
	if (Mth::Abs(rectangle_n[X]) < Mth::Abs(rectangle_n[Z]) && Mth::Abs(rectangle_n[Y]) < Mth::Abs(rectangle_n[Z]))
	{
		// project onto XY plane
		r_corner_S = rect->m_corner[X];
		r_corner_T = rect->m_corner[Y];
		r_first_edge_S = rect->m_first_edge[X];
		r_first_edge_T = rect->m_first_edge[Y];
		r_second_edge_S = rect->m_second_edge[X];
		r_second_edge_T = rect->m_second_edge[Y];
		p_S = p[X];
		p_T = p[Y];
		q_S = q[X];
		q_T = q[Y];
	}
	else if (Mth::Abs(rectangle_n[X]) < Mth::Abs(rectangle_n[Y]) && Mth::Abs(rectangle_n[Z]) < Mth::Abs(rectangle_n[Y]))
	{
		// project onto XZ plane
		r_corner_S = rect->m_corner[X];
		r_corner_T = rect->m_corner[Z];
		r_first_edge_S = rect->m_first_edge[X];
		r_first_edge_T = rect->m_first_edge[Z];
		r_second_edge_S = rect->m_second_edge[X];
		r_second_edge_T = rect->m_second_edge[Z];
		p_S = p[X];
		p_T = p[Z];
		q_S = q[X];
		q_T = q[Z];
	}
	else
	{
		// project onto YZ plane
		r_corner_S = rect->m_corner[Y];
		r_corner_T = rect->m_corner[Z];
		r_first_edge_S = rect->m_first_edge[Y];
		r_first_edge_T = rect->m_first_edge[Z];
		r_second_edge_S = rect->m_second_edge[Y];
		r_second_edge_T = rect->m_second_edge[Z];
		p_S = p[Y];
		p_T = p[Z];
		q_S = q[Y];
		q_T = q[Z];
	}
	
	// test p and q against the four edges of the rectangle; if both lie outside any edge, we reject
	
	// these variables define a rectangle's edge in 2D; as + bt + c = 0
	float a, b, c;
	
	float pd, qd;
	float inside_sign;
	
	// not really convinced that this bitmap method of faster 
	// char within = 0;
	// enum
	// {
		// P_TRIANGLE_EDGE_0_MASK = 1 << 0,
		// Q_TRIANGLE_EDGE_0_MASK = 1 << 1,
		// P_TRIANGLE_EDGE_1_MASK = 1 << 2,
		// Q_TRIANGLE_EDGE_1_MASK = 1 << 3,
		// P_TRIANGLE_EDGE_2_MASK = 1 << 4,
		// Q_TRIANGLE_EDGE_2_MASK = 1 << 5,
		// P_TRIANGLE_EDGE_3_MASK = 1 << 6,
		// Q_TRIANGLE_EDGE_3_MASK = 1 << 7,
		// ALL_P_MASKS = P_TRIANGLE_EDGE_0_MASK | P_TRIANGLE_EDGE_1_MASK | P_TRIANGLE_EDGE_2_MASK | P_TRIANGLE_EDGE_3_MASK,
		// ALL_Q_MASKS = Q_TRIANGLE_EDGE_0_MASK | Q_TRIANGLE_EDGE_1_MASK | Q_TRIANGLE_EDGE_2_MASK | Q_TRIANGLE_EDGE_3_MASK
	// };
	bool p_within[4] = { false, false, false, false };
	bool q_within[4] = { false, false, false, false };
	
	// edge 0: corner to corner + first_edge
	// second_edge defines inside direction
	a = r_first_edge_T;
	b = -r_first_edge_S;
	c = r_corner_T * r_first_edge_S - r_corner_S * r_first_edge_T;
	pd = a * p_S + b * p_T + c;
	qd = a * q_S + b * q_T + c;
	inside_sign = a * r_second_edge_S + b * r_second_edge_T;
	if (inside_sign > 0.0f)
	{
		// within = (P_TRIANGLE_EDGE_0_MASK * (pd >= 0.0f)) | (Q_TRIANGLE_EDGE_0_MASK * (qd >= 0.0f));
		p_within[0] = pd >= 0.0f;
		q_within[0] = qd >= 0.0f;
	}
	else
	{
		// within = (P_TRIANGLE_EDGE_0_MASK * (pd <= 0.0f)) | (Q_TRIANGLE_EDGE_0_MASK * (qd <= 0.0f));
		p_within[0] = pd <= 0.0f;
		q_within[0] = qd <= 0.0f;
	}
	// if (!(within & (P_TRIANGLE_EDGE_0_MASK | Q_TRIANGLE_EDGE_0_MASK))) return false;
	if (!p_within[0] && !q_within[0]) return false;
		
	// edge 1: corner + second_edge to corner + first_edge + second_edge
	// second_edge defines outside direction
	c = (r_corner_T + r_second_edge_T) * r_first_edge_S - (r_corner_S + r_second_edge_S) * r_first_edge_T;
	pd = a * p_S + b * p_T + c;
	qd = a * q_S + b * q_T + c;
	if (inside_sign < 0.0f)
	{
		// within |= (P_TRIANGLE_EDGE_1_MASK * (pd >= 0.0f)) | (Q_TRIANGLE_EDGE_1_MASK * (qd >= 0.0f));
		p_within[1] = pd >= 0.0f;
		q_within[1] = qd >= 0.0f;
	}
	else
	{
		// within |= (P_TRIANGLE_EDGE_1_MASK * (pd <= 0.0f)) | (Q_TRIANGLE_EDGE_1_MASK * (qd <= 0.0f));
		p_within[1] = pd <= 0.0f;
		q_within[1] = qd <= 0.0f;
	}
	// if (!(within & (P_TRIANGLE_EDGE_1_MASK | Q_TRIANGLE_EDGE_1_MASK))) return false;
	if (!p_within[1] && !q_within[1]) return false;
	
	// edge 2: corner to corner + second_edge
	// first_edge defines inside direction
	a = r_second_edge_T;
	b = -r_second_edge_S;
	c = r_corner_T * r_second_edge_S - r_corner_S * r_second_edge_T;
	pd = a * p_S + b * p_T + c;
	qd = a * q_S + b * q_T + c;
	inside_sign = a * r_first_edge_S + b * r_first_edge_T;
	if (inside_sign > 0.0f)
	{
		// within |= (P_TRIANGLE_EDGE_2_MASK * (pd >= 0.0f)) | (Q_TRIANGLE_EDGE_2_MASK * (qd >= 0.0f));
		p_within[2] = pd >= 0.0f;
		q_within[2] = qd >= 0.0f;
	}
	else
	{
		// within |= (P_TRIANGLE_EDGE_2_MASK * (pd <= 0.0f)) | (Q_TRIANGLE_EDGE_2_MASK * (qd <= 0.0f));
		p_within[2] = pd <= 0.0f;
		q_within[2] = qd <= 0.0f;
	}
	// if (!(within & (P_TRIANGLE_EDGE_2_MASK | Q_TRIANGLE_EDGE_2_MASK))) return false;
	if (!p_within[2] && !q_within[2]) return false;
	
	// edge 3: corner + first_edge to corner + first_edge + second_edge
	// first_edge defines outside direction
	c = (r_corner_T + r_first_edge_T) * r_second_edge_S - (r_corner_S + r_first_edge_S) * r_second_edge_T;
	pd = a * p_S + b * p_T + c;
	qd = a * q_S + b * q_T + c;
	if (inside_sign < 0.0f)
	{
		// within |= (P_TRIANGLE_EDGE_3_MASK * (pd >= 0.0f)) | (Q_TRIANGLE_EDGE_3_MASK * (qd >= 0.0f));
		p_within[3] = pd >= 0.0f;
		q_within[3] = qd >= 0.0f;
	}
	else
	{
		// within |= (P_TRIANGLE_EDGE_3_MASK * (pd <= 0.0f)) | (Q_TRIANGLE_EDGE_3_MASK * (qd <= 0.0f));
		p_within[3] = pd <= 0.0f;
		q_within[3] = qd <= 0.0f;
	}
	// if (!(within & (P_TRIANGLE_EDGE_3_MASK | Q_TRIANGLE_EDGE_3_MASK))) return false;
	if (!p_within[3] && !q_within[3]) return false;
	
	int next_collision_endpoint_idx;
	
	// if p or q is within all edges, it is the endpoint of the collision
	// if ((within & ALL_P_MASKS) == ALL_P_MASKS)
	if (p_within[0] && p_within[1] && p_within[2] && p_within[3])
	{
        p_collision_endpoints[0] = p;
		p_collision_triangle_edges[0] = p_edge;
		// if ((within & ALL_Q_MASKS) == ALL_Q_MASKS)
		if (q_within[0] && q_within[1] && q_within[2] && q_within[3])
		{
			p_collision_endpoints[1] = q;
			p_collision_triangle_edges[1] = q_edge;
			return true;
		}
		next_collision_endpoint_idx = 1;
	}
	// else if ((within & ALL_Q_MASKS) == ALL_Q_MASKS)
	else if (q_within[0] && q_within[1] && q_within[2] && q_within[3])
	{
		p_collision_endpoints[0] = q;
		p_collision_triangle_edges[0] = q_edge;
		next_collision_endpoint_idx = 1;
	}
	else
	{
		next_collision_endpoint_idx = 0;
	}
	
	// we've found any endpoints of the collision which are within the rectangle; further collision endpoints will lie on the edges of the rectangle;
	 
	// we check each edge of the rectangle for intersection with the triangle's plane-intersection line segment
	
	float s;
	
	// edge 0: corner to corner + first_edge
	if (s2DLineLineCollision(r_corner_S, r_corner_T, r_first_edge_S, r_first_edge_T, p_S, p_T, q_S - p_S, q_T - p_T, &s))
	{
		p_collision_endpoints[next_collision_endpoint_idx] = rect->m_first_edge;
		p_collision_endpoints[next_collision_endpoint_idx] *= s;
		p_collision_endpoints[next_collision_endpoint_idx] += rect->m_corner;
		p_collision_triangle_edges[next_collision_endpoint_idx] = NO_TRIANGLE_EDGE;
		if (next_collision_endpoint_idx == 1) return true;
		next_collision_endpoint_idx = 1;
	}
	
	// edge 1: corner + second_edge to corner + first_edge + second_edge
	if (s2DLineLineCollision(r_corner_S + r_second_edge_S, r_corner_T + r_second_edge_T, r_first_edge_S, r_first_edge_T, p_S, p_T, q_S - p_S, q_T - p_T, &s))
	{
		p_collision_endpoints[next_collision_endpoint_idx] = rect->m_first_edge;
		p_collision_endpoints[next_collision_endpoint_idx] *= s;
		p_collision_endpoints[next_collision_endpoint_idx] += rect->m_corner;
		p_collision_endpoints[next_collision_endpoint_idx] += rect->m_second_edge;
		p_collision_triangle_edges[next_collision_endpoint_idx] = NO_TRIANGLE_EDGE;
		if (next_collision_endpoint_idx == 1) return true;
		next_collision_endpoint_idx = 1;
	}
	
	// edge 2: corner to corner + second_edge
	if (s2DLineLineCollision(r_corner_S, r_corner_T, r_second_edge_S, r_second_edge_T, p_S, p_T, q_S - p_S, q_T - p_T, &s))
	{
		p_collision_endpoints[next_collision_endpoint_idx] = rect->m_second_edge;
		p_collision_endpoints[next_collision_endpoint_idx] *= s;
		p_collision_endpoints[next_collision_endpoint_idx] += rect->m_corner;
		p_collision_triangle_edges[next_collision_endpoint_idx] = NO_TRIANGLE_EDGE;
		if (next_collision_endpoint_idx == 1) return true;
		next_collision_endpoint_idx = 1;
	}
	
	// edge 3: corner + first_edge to corner + first_edge + second_edge
	if (s2DLineLineCollision(r_corner_S + r_first_edge_S, r_corner_T + r_first_edge_T, r_second_edge_S, r_second_edge_T, p_S, p_T, q_S - p_S, q_T - p_T, &s))
	{
		if (next_collision_endpoint_idx == 1)
		{
			p_collision_endpoints[next_collision_endpoint_idx] = rect->m_second_edge;
			p_collision_endpoints[next_collision_endpoint_idx] *= s;
			p_collision_endpoints[next_collision_endpoint_idx] += rect->m_corner;
			p_collision_endpoints[next_collision_endpoint_idx] += rect->m_first_edge;
			p_collision_triangle_edges[next_collision_endpoint_idx] = NO_TRIANGLE_EDGE;
			return true;
		}
	}
	
	// no collision could be found
	return false;
}

////////////////////////////////////////////////////////////////////
// CCollObj member functions
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollObj::CCollObj() : m_Flags(0), mp_coll_tri_data(NULL)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollObj::~CCollObj()
{
	// this should only be done on clones
	if (mp_coll_tri_data && (m_Flags & mSD_CLONE))
	{
		delete mp_coll_tri_data;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CCollObj::SetChecksum(uint32 checksum)
{
	m_checksum = checksum;
	if (mp_coll_tri_data)
	{
		mp_coll_tri_data->SetChecksum(checksum);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollObj::SetMovingObject(Obj::CCompositeObject *p_movable_object)
{
	Dbg_MsgAssert(0, ("Can't call SetMovingObject() on a non-moving collision type"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CCompositeObject * CCollObj::GetMovingObject() const
{
	return NULL;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

#define PRINT_RECT_COLLISION_DEBUG
const float CCollObj::sLINE_BOX_EXTENT = 0.5f;	// Extent of box around collision line

bool					CCollObj::sFindRectangleStaticCollision ( Mth::Rectangle& rect, S2DCollData* p_2D_coll_data, CCollCache *p_cache )
{
#if PRINT_CACHE_HITS
	static uint s_rect_cache_hits = 0, s_num_rect_collisions = 0;
#endif

	Dbg_Assert(p_2D_coll_data);
	
	Mth::CBBox rect_bbox(rect.m_corner);
	rect_bbox.AddPoint(rect.m_corner + rect.m_first_edge);
	rect_bbox.AddPoint(rect.m_corner + rect.m_second_edge);
	rect_bbox.AddPoint(rect.m_corner + rect.m_first_edge + rect.m_second_edge);
	
	bool use_cache = p_cache;
	if (use_cache)
	{
		use_cache = p_cache->Contains(rect_bbox);
#if PRINT_CACHE_HITS
		if (use_cache)
		{
			s_rect_cache_hits++;
		}
		if (++s_num_rect_collisions >= 500)
		{
			Dbg_Message("Static Rect Cache Hits %d/500", s_rect_cache_hits);
			s_rect_cache_hits = s_num_rect_collisions = 0;
		}
#endif
	}
	
	CCollStatic** p_coll_obj_list = NULL;
	if (!use_cache)
	{
		SSec::Manager* ss_man = Nx::CEngine::sGetNearestSuperSectorManager(rect.m_corner);
		if (!ss_man) return false;
		
		// get a list of sectors within the super sectors cut by the rectangle
		p_coll_obj_list = ss_man->GetIntersectingCollSectors(rect_bbox);
	}
	
	// bloat the rectangle bounding box for comparison to sector bounding boxes
	Mth::Vector extension(sLINE_BOX_EXTENT, sLINE_BOX_EXTENT, sLINE_BOX_EXTENT);
	rect_bbox.Set(rect_bbox.GetMin() - extension, rect_bbox.GetMax() + extension);
	
	p_2D_coll_data->num_surfaces = 0;
	
	if (use_cache)
	{
		int num_collisions = p_cache->GetNumStaticCollisions();
		const SCollCacheNode* p_coll_node = p_cache->GetStaticCollisionArray();
		
		// loop over sectors in the cache
		for (int i = num_collisions; i--; )
		{
			// check to see if the sector's bounding box intersects with the rectangle's bounding box
			if (rect_bbox.Intersect(*(p_coll_node->mp_bbox)))
			{
				// check the sector's trangle's for collision with the rectangle
				p_coll_node->mp_coll_obj->CollisionWithRectangle(rect, rect_bbox, p_2D_coll_data);
			}

			p_coll_node++;
		} // END loop over sectors in the cache
	}
	else
	{
		// loop over potentially collided with sectors
		CCollStatic* p_coll_obj;
		while ((p_coll_obj = *p_coll_obj_list))
		{
			Dbg_Assert(p_coll_obj->GetGeometry());
			
			// check to see if the sector's bounding box intersects with the rectangle's bounding box
			if (p_coll_obj->WithinBBox(rect_bbox))
			{
				// check the sector's trangle's for collision with the rectangle
				p_coll_obj->CollisionWithRectangle(rect, rect_bbox, p_2D_coll_data);
			}
			
			p_coll_obj_list++;
		} // END loop over potentially collided with sectors
	}
	
	return p_2D_coll_data->num_surfaces > 0;
}

bool GPrintCollisionData = false;

bool					CCollObj::sFindNearestStaticCollision( Mth::Line &is, CollData *p_data, void *p_callback, CCollCache *p_cache)
{
#if PRINT_CACHE_HITS
	static uint s_cache_hits = 0, s_num_collisions = 0;
#endif

    Dbg_Assert(p_data);
    Dbg_Assert(p_data->coll_found == false);

	p_data->callback = (void (*)( CollData*)) p_callback;

	// Make initial line bounding box
	Mth::CBBox line_bbox(is.m_start);
	line_bbox.AddPoint(is.m_end);

	if (GPrintCollisionData)
	{
		Dbg_Message("Inited line BBox (%f, %f, %f), (%f, %f, %f)",
					line_bbox.GetMin()[X], line_bbox.GetMin()[Y], line_bbox.GetMin()[Z],
					line_bbox.GetMax()[X], line_bbox.GetMax()[Y], line_bbox.GetMax()[Z]);
	}

	// Check to see if we can use the cache
	bool use_cache = p_cache != NULL;
	if (use_cache)
	{
		use_cache = p_cache->Contains(line_bbox);
		
		if (CCollCacheManager::sGetAssertOnCacheMiss())
		{
			Dbg_MsgAssert(use_cache, ("Collision cache miss"));
		}
#if PRINT_CACHE_HITS
		if (use_cache) s_cache_hits++;
		if (++s_num_collisions >= 5000)
		{
			Dbg_Message("Static Cache Hits %d/5000", s_cache_hits);
			s_cache_hits = s_num_collisions = 0;
		}
#endif
	}

	// initialize starting "max" distance to be max collision distance
	CCollStatic** p_coll_obj_list = NULL;
	if (!use_cache)
	{
		SSec::Manager *ss_man;

		ss_man = Nx::CEngine::sGetNearestSuperSectorManager(is);
		if (!ss_man)
			return NULL;

		p_coll_obj_list = ss_man->GetIntersectingCollSectors( is );
	}
	
	// For a line, the dist is returned as a fraction of the line length
	// so we set the dist field to just over 1.0, any found collision
	// will be less than this
	if (s_check_for_far)
	{
		p_data->dist = -0.01f; 		// but if checking in the other direction, we need to to reverse the check
	}
	else
	{
		p_data->dist = 1.01f;
	}

	// Bloat the collision line only for the purposes of deciding against which world 
	// sectors to test the actual collision line
	Mth::Vector extend;

	extend = line_bbox.GetMax();
	extend[X] += sLINE_BOX_EXTENT;
	extend[Y] += sLINE_BOX_EXTENT;
	extend[Z] += sLINE_BOX_EXTENT;
	line_bbox.AddPoint(extend);

	extend = line_bbox.GetMin();
	extend[X] -= sLINE_BOX_EXTENT;
	extend[Y] -= sLINE_BOX_EXTENT;
	extend[Z] -= sLINE_BOX_EXTENT;
	line_bbox.AddPoint(extend);

	// Calculate ray
	Mth::Vector line_dir(is.m_end - is.m_start);

	if (GPrintCollisionData)
	{
		Dbg_Message("Starting collision check with line BBox (%f, %f, %f), (%f, %f, %f)",
					line_bbox.GetMin()[X], line_bbox.GetMin()[Y], line_bbox.GetMin()[Z],
					line_bbox.GetMax()[X], line_bbox.GetMax()[Y], line_bbox.GetMax()[Z]);
		Dbg_Message("Line start (%f, %f, %f)", is.m_start[X], is.m_start[Y], is.m_start[Z]);
		Dbg_Message("Line end (%f, %f, %f)", is.m_end[X], is.m_end[Y], is.m_end[Z]);
	}

	bool new_coll_found = false;

	if (use_cache)
	{
		int num_collisions = p_cache->GetNumStaticCollisions();
		const SCollCacheNode *p_coll_node = p_cache->GetStaticCollisionArray();
		for (int i = 0; i < num_collisions; i++)
		{
			if(line_bbox.Intersect(*(p_coll_node->mp_bbox)))
			{
				/* It's a world sector, call the callback */
				if (p_coll_node->mp_coll_obj->CollisionWithLine( is, line_dir, p_data, &line_bbox ))
				{
					new_coll_found = true;
				}
			}
			p_coll_node++;
		}
	} else {
		CCollStatic* p_coll_obj;

	#ifdef BATCH_TRI_COLLISION
		// Init batch manager
		bool do_batch;
		do_batch = CBatchTriCollMan::sInit(p_data);
	#endif

		/* Start at the top */
		while((p_coll_obj = *p_coll_obj_list))
		{
			// TODO: Come up with a cleaner BBox check
			Dbg_Assert(p_coll_obj->GetGeometry());
			//if(line_bbox.Intersect(p_coll_obj->GetGeometry()->GetBBox()))
			if (p_coll_obj->WithinBBox(line_bbox))
			{
				// this line will display the bounding box of the potential collidable object
				// useful to make sure we are only colliding with the minimum necessary objects
				// p_coll_obj->mp_coll_tri_data->GetBBox().DebugRender(0xfffff,1);

				/* It's a world sector, call the callback */
				if (p_coll_obj->CollisionWithLine( is, line_dir, p_data, &line_bbox ))
				{
					new_coll_found = true;
				}
			}
			p_coll_obj_list++;
		}

	#ifdef BATCH_TRI_COLLISION
		// Wait for tri collision to finish first
		if (do_batch)
		{
			new_coll_found = CBatchTriCollMan::sWaitTriCollisions();
		}
		CBatchTriCollMan::sFinish();				// This should really be in the CFeeler code
	#endif
	}

    /* All done */
	if (p_data->coll_found)
	{
		if ((p_data->dist > 1.0f) || (p_data->dist < 0.0f))
		{
			Dbg_Message("Static Collision distance too big: %f", p_data->dist);
		}
	}

	// return true if we found something			
	return( new_coll_found );
}


bool					CCollObj::sFindFarStaticCollision( Mth::Line &is, CollData *p_data, void *p_callback, CCollCache *p_cache)
{
	s_check_for_far = true;
	bool result = sFindNearestStaticCollision(is,p_data,p_callback,p_cache);
	s_check_for_far = false;
	return result;		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Note, p_data is assumed to be already initialized
// either it is set to default values, or we have
// come here after using it to check for static collision
// and the results of that are in there
// - see below, we don't set "dist" if we've previously found a collision
// (stops you snapping through walls onto cars)
				  
Obj::CCompositeObject *	CCollObj::sFindNearestMovableCollision( Mth::Line &is, CollData *p_data, void *p_callback, CCollCache *p_cache)
{
#if PRINT_CACHE_HITS
	static uint s_cache_hits = 0, s_num_collisions = 0;
#endif

    Dbg_Assert(p_data);

	Obj::CCompositeObject *p_collision_obj = NULL;

	p_data->callback = (void (*)( CollData*)) p_callback;

	
	// initialize starting "max" distance to be max collision distance
	// actually this	
	if (!p_data->coll_found)
	{
		// For a line, the dist is returned as a fraction of the line length
		// so we set the dist field to just over 1.0, any found collision
		// will be less than this
		if (s_check_for_far)
		{
			p_data->dist = -0.01f; 		// but if checking in the other direction, we need to to reverse the check
		}
		else
		{
			p_data->dist = 1.01f;
		}
	}

	// Make initial line bounding box
	Mth::CBBox line_bbox(is.m_start);
	line_bbox.AddPoint(is.m_end);

	// Check to see if we can use the cache
	bool use_cache = p_cache != NULL;
	if (use_cache)
	{
		use_cache = p_cache->Contains(line_bbox);
#if PRINT_CACHE_HITS
		if (use_cache) s_cache_hits++;
		if (++s_num_collisions >= 5000)
		{
			Dbg_Message("Movable Cache Hits %d/5000", s_cache_hits);
			s_cache_hits = s_num_collisions = 0;
		}
#endif
	}

	if (0 && GPrintCollisionData)
	{
		Dbg_Message("Inited line BBox (%f, %f, %f), (%f, %f, %f)",
					line_bbox.GetMin()[X], line_bbox.GetMin()[Y], line_bbox.GetMin()[Z],
					line_bbox.GetMax()[X], line_bbox.GetMax()[Y], line_bbox.GetMax()[Z]);
	}

	// Bloat the collision line only for the purposes of deciding against which world 
	// sectors to test the actual collision line
	Mth::Vector extend;

	extend = line_bbox.GetMax();
	extend[X] += sLINE_BOX_EXTENT;
	extend[Y] += sLINE_BOX_EXTENT;
	extend[Z] += sLINE_BOX_EXTENT;
	line_bbox.AddPoint(extend);

	extend = line_bbox.GetMin();
	extend[X] -= sLINE_BOX_EXTENT;
	extend[Y] -= sLINE_BOX_EXTENT;
	extend[Z] -= sLINE_BOX_EXTENT;
	line_bbox.AddPoint(extend);

	// Calculate ray
	Mth::Vector line_dir(is.m_end - is.m_start);

	if (0 && GPrintCollisionData)
	{
		Dbg_Message("Starting collision check with line BBox (%f, %f, %f), (%f, %f, %f)",
					line_bbox.GetMin()[X], line_bbox.GetMin()[Y], line_bbox.GetMin()[Z],
					line_bbox.GetMax()[X], line_bbox.GetMax()[Y], line_bbox.GetMax()[Z]);
		Dbg_Message("Line start (%f, %f, %f)", is.m_start[X], is.m_start[Y], is.m_start[Z]);
		Dbg_Message("Line end (%f, %f, %f)", is.m_end[X], is.m_end[Y], is.m_end[Z]);
	}

//	Dbg_Message("Size of movable list %d", Nx::CEngine::sGetMovableObjects().CountItems());
//	Dbg_Message("Size of movable collision list %d", CMovableCollMan::sGetCollisionList()->CountItems());
//	Dbg_Assert(0);

	if (use_cache)
	{
		int num_collisions = p_cache->GetNumMovableCollisions();
		const SCollCacheNode *p_coll_node = p_cache->GetMovableCollisionArray();
		for (int i = 0; i < num_collisions; i++)
		{
			if(p_coll_node->mp_bbox && line_bbox.Intersect(*(p_coll_node->mp_bbox)))
			{
				// Set the callback_object, so any callback will know
				// what object we were colliding with
				p_data->mp_callback_object = p_coll_node->mp_coll_obj->GetMovingObject();

				/* It's a world sector, call the callback */
				if (p_coll_node->mp_coll_obj->CollisionWithLine( is, line_dir, p_data, &line_bbox ))
				{
					p_collision_obj = p_coll_node->mp_coll_obj->GetMovingObject();
					Dbg_Assert(p_collision_obj);
				}

				// the callback object is only valid for the duration of a possible callback											 
				p_data->mp_callback_object = NULL;
			}
			p_coll_node++;
		}
	} else {
	#if 0 //def BATCH_TRI_COLLISION
		// Init batch manager
		bool do_batch;
		do_batch = CBatchTriCollMan::sInit(p_data);
	#endif


		Lst::Node< CCollObj > *p_movable_node = CMovableCollMan::sGetCollisionList()->GetNext();
		//CCollObj **p_movable_node = CMovableCollMan::sGetCollisionArray();

		while(p_movable_node)
		{
			CCollObj *p_coll_obj = p_movable_node->GetData();
			//CCollObj *p_coll_obj = *p_movable_node;

			if (p_coll_obj && !(p_coll_obj->m_Flags & (mSD_NON_COLLIDABLE | mSD_KILLED )))
			{
		
				/* It's a collision object, call the callback */
				//if (p_coll_obj->CollisionWithLine( is, line_dir, p_data , &line_bbox))
				if (p_coll_obj->WithinBBox(line_bbox))
				{
					// Set the callback_object, so any callback will know
					// what object we were colliding with
					p_data->mp_callback_object = p_coll_obj->GetMovingObject();
				
					if (p_coll_obj->CollisionWithLine( is, line_dir, p_data, &line_bbox ))
					{
						p_collision_obj = p_coll_obj->GetMovingObject();
						Dbg_Assert(p_collision_obj);
					}
					// the callback object is only valid for the duration of a possible callback											 
					p_data->mp_callback_object = NULL;
				}
			
			}

			p_movable_node = p_movable_node->GetNext();
			//p_movable_node++;
		}

	#if 0 //def BATCH_TRI_COLLISION
		// Wait for tri collision to finish first
		if (do_batch)
		{
			new_coll_found = CBatchTriCollMan::sWaitTriCollisions();
		}
		CBatchTriCollMan::sFinish();				// This should really be in the CFeeler code
	#endif
	}

    /* All done */
	if (p_collision_obj)
	{

		if ((p_data->dist > 1.0f) || (p_data->dist < 0.0f))
		{
			Dbg_Message("Movable Collision distance too big: %f", p_data->dist);
		}
	}

	return p_collision_obj;
}

Obj::CCompositeObject *	CCollObj::sFindFarMovableCollision( Mth::Line &is, CollData *p_data, void *p_callback, CCollCache *p_cache)
{
	s_check_for_far = true;
	Obj::CCompositeObject *result = sFindNearestMovableCollision(is,p_data,p_callback,p_cache);
	s_check_for_far = false;
	return result;		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CollType	CCollObj::sConvertTypeChecksum(uint32 checksum)
{
	CollType c_type;

	switch (checksum)
	{
	case 0x6aadf154: // Geometry
		c_type = vCOLL_TYPE_GEOM;
		break;

	case 0xe460abde: // BoundingBox
	case 0x2ba71070: // Spherical
	case 0xdd42aa66: // Cylindrical
		c_type =  vCOLL_TYPE_BBOX;
		break;

	case 0x806fff30: // None
	default:
		c_type = vCOLL_TYPE_NONE;
		break;
	}

	return c_type;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollObj *	CCollObj::sCreateMovableCollision(CollType type, Nx::CCollObjTriData *p_coll_obj_data, int num_coll_objects, Obj::CCompositeObject *p_object)
{
	CCollObj *p_collision;

	switch (type)
	{
	case vCOLL_TYPE_BBOX:
		{
			CCollMovBBox *p_coll_bbox = new Nx::CCollMovBBox;

			// TODO: determine if we should make more than one collision object
			if (num_coll_objects >= 1) {
				Mth::CBBox bbox/*(Mth::Vector(0.0f, 0.0f, 0.0f))*/;	// Add origin for now

				for (int i = 0; i < num_coll_objects; i++)
				{
					bbox.AddPoint(p_coll_obj_data[i].GetBBox().GetMin());
					bbox.AddPoint(p_coll_obj_data[i].GetBBox().GetMax());
				}

				p_coll_bbox->SetBoundingBox(bbox);
			}

			p_collision = p_coll_bbox;
			break;
		}
	case vCOLL_TYPE_GEOM:
		if (num_coll_objects == 1)				// one node
		{
			p_collision = new CCollMovTri(p_coll_obj_data);
		} else if (num_coll_objects > 1) {		// multi nodes
			CCollMulti *p_coll_multi = new Nx::CCollMulti;

			for (int i = 0; i < num_coll_objects; i++)
			{
				CCollObj *p_coll_tri;

				p_coll_tri = new CCollMovTri(&(p_coll_obj_data[i]));
				p_coll_multi->AddCollision(p_coll_tri);
			}

			p_collision = p_coll_multi;
		} else {
			p_collision = NULL;
		}
		break;

	default:
		p_collision = NULL;						// default to no collision
		break;
	}

	// Add to movable collision manager
	if (p_collision)
	{
		p_collision->SetMovingObject(p_object);
		CMovableCollMan::sAddCollision(p_collision, p_object);
	}

	return p_collision;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollMulti::CCollMulti()
{
	mp_collision_list = new Lst::Head< CCollObj >;
	m_movement_changed = true;
	mp_movable_object = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollMulti::~CCollMulti()
{
	if (mp_collision_list)
	{
		Lst::Node< CCollObj > *obj_node, *next;

		for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = next)
		{
			next = obj_node->GetNext();

			delete obj_node->GetData();
			delete obj_node;
		}

		delete mp_collision_list;
	}

	// Take out of caches
	CCollCacheManager::sDeleteCollision(this);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMulti::AddCollision(CCollObj *p_collision)
{
	Lst::Node< CCollObj > *node = new Lst::Node< CCollObj > (p_collision);
	mp_collision_list->AddToTail(node);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMulti::SetWorldPosition(const Mth::Vector & pos)
{
	Lst::Node< CCollObj > *obj_node;

	for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
	{
		obj_node->GetData()->SetWorldPosition(pos);
	}

	m_movement_changed = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMulti::SetOrientation(const Mth::Matrix & orient)
{
	Lst::Node< CCollObj > *obj_node;

	for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
	{
		obj_node->GetData()->SetOrientation(orient);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMulti::SetGeometry(CCollObjTriData *p_geom_data)
{
	Lst::Node< CCollObj > *obj_node;

	for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
	{
		obj_node->GetData()->SetGeometry(p_geom_data);
	}

	m_movement_changed = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMulti::SetMovingObject(Obj::CCompositeObject *p_movable_object)
{
	mp_movable_object = p_movable_object;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CCompositeObject * CCollMulti::GetMovingObject() const
{
	return mp_movable_object;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollObj * 	CCollMulti::Clone(bool instance)
{
	Dbg_MsgAssert(!instance, ("CCollMulti::Clone() with instances not implemented yet"));
	Dbg_Assert(mp_coll_tri_data == NULL);

	CCollMulti *m_new_coll = new CCollMulti(*this);

	m_new_coll->m_Flags |=  mSD_CLONE;

	Lst::Node< CCollObj > *obj_node;

	// Clone List
	m_new_coll->mp_collision_list = new Lst::Head< CCollObj >;
	for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
	{
		Lst::Node< CCollObj > *node = new Lst::Node< CCollObj > (obj_node->GetData()->Clone(instance));
		m_new_coll->mp_collision_list->AddToTail(node);
	}

	return m_new_coll;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CCollMulti::IsTriangleCollision() const
{
	Lst::Node< CCollObj > *obj_node;
	bool ret_val = true;

	for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
	{
		ret_val = ret_val && obj_node->GetData()->IsTriangleCollision();
	}

	return ret_val;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMulti::update_world_bbox()
{
	Lst::Node< CCollObj > *obj_node;

	m_world_bbox_valid = true;
	m_world_bbox.Reset();
	for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
	{
		Mth::CBBox *obj_bbox;
		if ((obj_bbox = obj_node->GetData()->get_bbox()))
		{
			m_world_bbox.AddPoint(obj_bbox->GetMin());
			m_world_bbox.AddPoint(obj_bbox->GetMax());
		} else {
			m_world_bbox_valid = false;
			break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::CBBox *CCollMulti::get_bbox()
{
	if (m_movement_changed)
	{
		update_world_bbox();
		m_movement_changed = false;
	}

	if (m_world_bbox_valid)
	{
		return &m_world_bbox;
	} else {
		return NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CCollMulti::WithinBBox(const Mth::CBBox & testBBox)
{
	if (m_movement_changed)
	{
		update_world_bbox();
		m_movement_changed = false;
	}

	return !m_world_bbox_valid || testBBox.Intersect(m_world_bbox);
	//return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CCollMulti::CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox * p_bbox)
{
	Lst::Node< CCollObj > *obj_node;
	bool ret_val = false;

	for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
	{
		ret_val = obj_node->GetData()->CollisionWithLine(testLine, lineDir, p_data, p_bbox) || ret_val;
	}

	return ret_val;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CCollMulti::CollisionWithRectangle(const Mth::Rectangle& rect, const Mth::CBBox& rect_bbox, S2DCollData* p_data)
{
	Dbg_MsgAssert(false, ("CCollMulti::CollisionWithRecangle is not implemented"));
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMulti::DebugRender(uint32 ignore_1, uint32 ignore_0)
{
	Lst::Node< CCollObj > *obj_node;

	for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
	{
		obj_node->GetData()->DebugRender(ignore_1, ignore_0);
	}
}

// Don't call these!

const Mth::Vector &	CCollMulti::GetWorldPosition() const
{
	Dbg_MsgAssert(0, ("Don't call CCollMulti::GetWorldPosition()"));
	return mp_collision_list->GetNext()->GetData()->GetWorldPosition();
}

const Mth::Matrix &	CCollMulti::GetOrientation() const
{
	Dbg_MsgAssert(0, ("Don't call CCollMulti::GetOrientation()"));
	return mp_collision_list->GetNext()->GetData()->GetOrientation();
}

uint32 		CCollMulti::GetFaceFlags(int face_idx) const
{
	Dbg_MsgAssert(0, ("Don't call CCollMulti::GetFaceFlags()"));
	return 0;
}

uint16 		CCollMulti::GetFaceTerrainType(int face_idx) const
{
	Dbg_MsgAssert(0, ("Don't call CCollMulti::GetFaceTerrainType()"));
	return 0;
}

Mth::Vector	CCollMulti::GetVertexPos(int vert_idx) const
{
	Dbg_MsgAssert(0, ("Don't call CCollMulti::GetVertexPos()"));
	return Mth::Vector(0, 0, 0, 0);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollMovable::CCollMovable() : 
	m_world_pos(0, 0, 0, 1),
	m_orient(0, 0, 0),
	m_orient_transpose(0, 0, 0),
	mp_movable_object(NULL)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollMovable::~CCollMovable()
{
	// Take out of caches
	CCollCacheManager::sDeleteMovableCollision(this);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector	CCollMovable::GetVertexPos(int vert_idx) const
{
	Dbg_Assert(mp_coll_tri_data);

	Mth::Vector pos(mp_coll_tri_data->GetRawVertexPos(vert_idx));

	pos[W] = 0.0f;				// start as vector
	pos.Rotate(m_orient);
	pos += m_world_pos;

	return pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMovable::SetMovingObject(Obj::CCompositeObject *p_movable_object)
{
	mp_movable_object = p_movable_object;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CCompositeObject * CCollMovable::GetMovingObject() const
{
	return mp_movable_object;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMovable::convert_line_to_local(const Mth::Line &world_line, Mth::Line &local_line)
{
	local_line = world_line;

	// First, translate back
	local_line -= m_world_pos;

	// And then do a reverse rotation
	local_line.m_start.Rotate(m_orient_transpose);
	local_line.m_end.Rotate(m_orient_transpose);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollStatic::CCollStatic() //: 
//	m_world_pos(0, 0, 0, 1)//,
//	m_orient(0, 0, 0)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollStatic::~CCollStatic()
{
	// Take out of caches
	//CCollCacheManager::sDeleteStaticCollision(this);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector	CCollStatic::GetVertexPos(int vert_idx) const
{
	Dbg_Assert(mp_coll_tri_data);

	Mth::Vector pos(mp_coll_tri_data->GetRawVertexPos(vert_idx));

	pos[W] = 1.0f;				// make a point

	return pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollMovBBox::CCollMovBBox() :
	m_flags(0),
	m_terrain_type(0)
{
	// Sets a default bounding box, if nothing loaded
	//m_bbox.Set(Mth::Vector(-60, -20, -60, 1), Mth::Vector(60, 36, 60, 1));
	m_bbox.Set(Mth::Vector(-50, -20, -90, 1), Mth::Vector(30, 36, 90, 1));		// size of car
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollMovBBox::CCollMovBBox(CCollObjTriData *p_geom_data) :
	m_flags(0),
	m_terrain_type(0)
{
	SetGeometry(p_geom_data);
	Dbg_Assert(mp_coll_tri_data);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollMovBBox::~CCollMovBBox()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMovBBox::SetGeometry(CCollObjTriData *p_geom_data)
{
	CCollObj::SetGeometry(p_geom_data);

	Dbg_Assert(mp_coll_tri_data);
	SetBoundingBox(mp_coll_tri_data->GetBBox());
}

// Temp functions
uint32 CCollMovBBox::GetFaceFlags(int face_idx) const
{
	return m_flags;
}

uint16 CCollMovBBox::GetFaceTerrainType(int face_idx) const
{
	return m_terrain_type;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollObj * 	CCollMovBBox::Clone(bool instance)
{
	Dbg_MsgAssert(!instance, ("CCollMovBBox::Clone() with instances not implemented yet"));

	CCollMovBBox *m_new_coll = new CCollMovBBox(*this);

	m_new_coll->m_Flags |=  mSD_CLONE;

	m_new_coll->mp_coll_tri_data = NULL;		// We don't need to clone the data since we have the bbox

	return m_new_coll;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollMovBBox::CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox *p_bbox)
{
	Dbg_Assert(p_data);

	// Find line in local space
	Mth::Line local_line;
	convert_line_to_local(testLine, local_line);


	if (0 && GPrintCollisionData)
	{
		Dbg_Message("Testing ray with start (%f, %f, %f)", testLine.m_start[X], testLine.m_start[Y], testLine.m_start[Z]);
		Dbg_Message("and end (%f, %f, %f)", testLine.m_end[X], testLine.m_end[Y], testLine.m_end[Z]);
		Dbg_Message("with the direction (%f, %f, %f)", lineDir[X], lineDir[Y], lineDir[Z]);
	}


	// Mick: temp optimization, generate a bounding box for the line
	// so it can be tested against the collision bounding box
	Mth::CBBox line_bbox(local_line.m_start);
	line_bbox.AddPoint(local_line.m_end);


	// Do collision
	if (m_bbox.Intersect(line_bbox))
	{
		Mth::Vector local_point, local_normal;

		if (m_bbox.LineIntersect(local_line, local_point, local_normal))
		{
			float distance = Mth::Vector(local_point - local_line.m_start).Length() / 			// gotta be a faster way
							 Mth::Vector(local_line.m_end - local_line.m_start).Length();

			SCollSurface collisionSurface;

			if (GPrintCollisionData)
			{
				Dbg_Message("\n*Start point (%f, %f, %f)", testLine.m_start[X], testLine.m_start[Y], testLine.m_start[Z]);
				Dbg_Message("*End point (%f, %f, %f)", testLine.m_end[X], testLine.m_end[Y], testLine.m_end[Z]);
				Dbg_Message("*Start local point (%f, %f, %f)", local_line.m_start[X], local_line.m_start[Y], local_line.m_start[Z]);
				Dbg_Message("*End local point (%f, %f, %f)", local_line.m_end[X], local_line.m_end[Y], local_line.m_end[Z]);
				Dbg_Message("*World pos (%f, %f, %f)", m_world_pos[X], m_world_pos[Y], m_world_pos[Z]);
				Dbg_Message("*Local collision pos (%f, %f, %f)", local_point[X], local_point[Y], local_point[Z]);
				Dbg_Message("*Local Normal (%f, %f, %f)", local_normal[X], local_normal[Y], local_normal[Z]);
				Dbg_Message("*Orientation [%f, %f, %f]", m_orient[X][X], m_orient[X][Y], m_orient[X][Z]);
				Dbg_Message("*            [%f, %f, %f]", m_orient[Y][X], m_orient[Y][Y], m_orient[Y][Z]);
				Dbg_Message("*            [%f, %f, %f]", m_orient[Z][X], m_orient[Z][Y], m_orient[Z][Z]);
			}

			// Convert to world coordinates
			collisionSurface.point = local_point.Rotate(m_orient);
			collisionSurface.point += m_world_pos;
			collisionSurface.normal = local_normal.Rotate(m_orient);

			if (GPrintCollisionData)
			{
				Dbg_Message("*Collision pos (%f, %f, %f)", collisionSurface.point[X], collisionSurface.point[Y], collisionSurface.point[Z]);
				Dbg_Message("*Normal (%f, %f, %f)", collisionSurface.normal[X], collisionSurface.normal[Y], collisionSurface.normal[Z]);
				Dbg_Message("*distance %f", distance);
			}

			return s_found_collision(&testLine, this, &collisionSurface, distance, p_data);
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollMovBBox::CollisionWithRectangle(const Mth::Rectangle& rect, const Mth::CBBox& rect_bbox, S2DCollData* p_data)

{
	Dbg_MsgAssert(false, ("CCollMovBBox::CollisionWithRecangle is not implemented"));
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollMovBBox::DebugRender(uint32 ignore_1, uint32 ignore_0)
{
	// Debug box
	Mth::Matrix total_mat = m_orient;
	total_mat[W] = m_world_pos;
	m_bbox.DebugRender(total_mat, 0x80808080, 1);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollStaticTri::CCollStaticTri()
{
	// This instance can't be used until someone sets the geometry data
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollStaticTri::~CCollStaticTri()
{
	// Don't delete the Tri Data
}

// Temp functions
uint32 CCollStaticTri::GetFaceFlags(int face_idx) const
{
	Dbg_Assert(mp_coll_tri_data);
	return mp_coll_tri_data->GetFaceFlags(face_idx);
}

uint16 CCollStaticTri::GetFaceTerrainType(int face_idx) const
{
	Dbg_Assert(mp_coll_tri_data);
	return mp_coll_tri_data->GetFaceTerrainType(face_idx);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollStaticTri::SetWorldPosition(const Mth::Vector & pos)
{
	Dbg_Assert(mp_coll_tri_data);
	mp_coll_tri_data->Translate(pos - GetWorldPosition());

	CCollStatic::SetWorldPosition(pos);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	CCollStaticTri::SetOrientation(const Mth::Matrix & orient)
{
	CCollStatic::SetOrientation(orient);

	Dbg_MsgAssert(0, ("CCollStaticTri::SetOrientation() not implemented yet"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollObj * 	CCollStaticTri::Clone(bool instance)
{
	Dbg_MsgAssert(!instance, ("CCollStaticTri::Clone() with instances not implemented yet"));

	// Clone only if there is geometry
	CCollObjTriData *p_new_tri_data = mp_coll_tri_data->Clone(instance, true);
	if (!p_new_tri_data)
	{
		return NULL;
	}

	CCollStaticTri *p_new_coll = new CCollStaticTri(*this);

	p_new_coll->m_Flags |=  mSD_CLONE;

	p_new_coll->mp_coll_tri_data = p_new_tri_data;

	return p_new_coll;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollStaticTri::RotateY(const Mth::Vector & world_origin, Mth::ERot90 rot_y)
{
	Dbg_Assert(mp_coll_tri_data);

	mp_coll_tri_data->RotateY(world_origin, rot_y);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollStaticTri::Scale(const Mth::Vector & world_origin, const Mth::Vector & scale)
{
	Dbg_Assert(mp_coll_tri_data);

	mp_coll_tri_data->Scale(world_origin, scale);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollStaticTri::WithinBBox(const Mth::CBBox & testBBox)
{
	Dbg_Assert(mp_coll_tri_data);
	return testBBox.Intersect(mp_coll_tri_data->GetBBox());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollStaticTri::CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox *p_bbox)
{
	Dbg_Assert(p_data);
	Dbg_Assert(mp_coll_tri_data);

	bool best_collision = false;

	// Generate a bounding box for the line
	// so it can be tested against the BSP tree.	  
	// (Should we just pass along the one we had before, even though it won't work with movables?)
//	Mth::CBBox line_bbox(testLine.m_start);
//	line_bbox.AddPoint(testLine.m_end);

	uint num_faces;
	FaceIndex *p_face_indexes;
	p_face_indexes = mp_coll_tri_data->FindIntersectingFaces(*p_bbox, num_faces);
	Dbg_Assert(p_face_indexes);

#ifdef BATCH_TRI_COLLISION
	bool do_batch = !CBatchTriCollMan::sIsNested();
#endif

	for (uint fidx = 0; fidx < num_faces; fidx++, p_face_indexes++)
	{
#ifdef BATCH_TRI_COLLISION
		if (do_batch)
		{
			CBatchTriCollMan::sAddTriCollision(testLine, lineDir, this, *p_face_indexes);
			continue;
		}
#endif // BATCH_TRI_COLLISION

#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
		Mth::Vector v0, v1, v2;
		if (mp_coll_tri_data->m_use_face_small)
		{
			CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);

			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
		} else {
			CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);

			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
		}
#else
		Mth::Vector *v0, *v1, *v2;
		if (mp_coll_tri_data->m_use_face_small)
		{
			CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);

			v0 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
			v1 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
			v2 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
		} else {
			CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);

			v0 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
			v1 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
			v2 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
		}
#endif // FIXED_POINT_VERTICES

#if 0
		uint32 coll_color = (uint32) MAKE_RGB(200, 200, 200);
		if (coll_color != (uint32) MAKE_RGB( 0, 200, 0 ))
		{
			Gfx::AddDebugLine(*v0,*v1,coll_color,1);
			Gfx::AddDebugLine(*v1,*v2,coll_color,1);
			Gfx::AddDebugLine(*v2,*v0,coll_color,1);
		}
#endif

		float distance;
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
		if (sRayTriangleCollision(&testLine.m_start, &lineDir, &v0, &v1, &v2, &distance))
#else
		if (sRayTriangleCollision(&testLine.m_start, &lineDir, v0, v1, v2, &distance))
#endif
		{
			SCollSurface collisionSurface;

			/* We've got one */
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
			collisionSurface.point = v0;
			collisionSurface.index = *p_face_indexes;

			// Find normal
			Mth::Vector vTmp1(v1 - v0);
			Mth::Vector vTmp2(v2 - v0);
#else
			collisionSurface.point = (*v0);
			collisionSurface.index = *p_face_indexes;

			// Find normal
			Mth::Vector vTmp1(*v1 - *v0);
			Mth::Vector vTmp2(*v2 - *v0);
#endif // FIXED_POINT_VERTICES
			collisionSurface.normal = Mth::CrossProduct(vTmp1, vTmp2);
			collisionSurface.normal.Normalize();

			if (s_found_collision(&testLine, this, &collisionSurface, distance, p_data))
			{
				best_collision = true;
			}
		}
	}

#ifdef BATCH_TRI_COLLISION
	if (do_batch)
	{
		CBatchTriCollMan::sStartNewTriCollisions();
	}
#endif // BATCH_TRI_COLLISION

	return best_collision;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollStaticTri::CollisionWithRectangle(const Mth::Rectangle& testRect, const Mth::CBBox& testRectBBox, S2DCollData *p_coll_data)
{
	Dbg_Assert(p_coll_data);
	
	uint num_faces;
	FaceIndex* p_face_indexes = mp_coll_tri_data->FindIntersectingFaces(testRectBBox, num_faces);
	Dbg_Assert(p_face_indexes);
	
	// loop over the relavent faces of the sector
	for (uint fidx = 0; fidx < num_faces; fidx++, p_face_indexes++)
	{
		uint32 face_flags = GetFaceFlags(fidx);
		if ((face_flags & p_coll_data->ignore_1) || (~face_flags & p_coll_data->ignore_0)) continue;

#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
		Mth::Vector v0, v1, v2;
		if (mp_coll_tri_data->m_use_face_small)
		{
			CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);

			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
		} else {
			CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);

			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
		}
#else
		Mth::Vector v0, v1, v2;
		if (mp_coll_tri_data->m_use_face_small)
		{
			CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);

			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
		} else {
			CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);

			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
		}
		
#endif // FIXED_POINT_VERTICES

		Mth::Vector p_coll_point[2];
		ETriangleEdgeType p_triangle_edge[2];

		if (sRectangleTriangleCollision(&testRect, &v0, &v1, &v2, p_coll_point, p_triangle_edge))
		{
			if (p_coll_data->num_surfaces == MAX_NUM_2D_COLLISIONS_REPORTED)
			{
				Dbg_Message("Number of faces colliding with rectangle exceeds static maximum; dropping additional collisions");
				return true;
			}
			
			// add the face to the collided surface list
			S2DCollSurface& surface = p_coll_data->p_surfaces[p_coll_data->num_surfaces];
			surface.normal = Mth::CrossProduct(v1 - v0, v2 - v0).Normalize();

			// ignore triangles with zero area
			if (surface.normal.LengthSqr() <= 0.0f)	continue;
			
			surface.trigger = (face_flags & mFD_TRIGGER);
			surface.node_name = this->GetChecksum();
			
			surface.ends[0].point = p_coll_point[0];
			surface.ends[1].point = p_coll_point[1];

			for (int end_idx = 2; end_idx--; )
			{
				surface.ends[end_idx].tangent_exists = p_triangle_edge[end_idx] != NO_TRIANGLE_EDGE;
				switch (p_triangle_edge[end_idx])
				{
					case TRIANGLE_EDGE_V0_V1:
						surface.ends[end_idx].tangent = (v1 - v0).Normalize();
						break;
					case TRIANGLE_EDGE_V1_V2:
						surface.ends[end_idx].tangent = (v2 - v1).Normalize();
						break;
					case TRIANGLE_EDGE_V2_V0:
						surface.ends[end_idx].tangent = (v0 - v2).Normalize();
						break;
					case NO_TRIANGLE_EDGE:
						break;
				}
			}
			
			p_coll_data->num_surfaces++;
		}
	}
	
	return p_coll_data->num_surfaces > 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollStaticTri::DebugRender(uint32 ignore_1, uint32 ignore_0)
{
	Dbg_Assert(mp_coll_tri_data);
	mp_coll_tri_data->DebugRender(ignore_1, ignore_0);
}

void	CCollStaticTri::ProcessOcclusion()
{
	Dbg_Assert(mp_coll_tri_data);
	mp_coll_tri_data->ProcessOcclusion();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollStaticTri::CheckForHoles()
{
	Dbg_Assert(mp_coll_tri_data);
#ifdef	__DEBUG_CODE__
	mp_coll_tri_data->CheckForHoles();
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollMovTri::CCollMovTri()
{
	// This instance can't be used until someone sets the geometry data
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollMovTri::CCollMovTri(CCollObjTriData *p_coll_data) :
	CCollMovable()
{
	SetGeometry(p_coll_data);
	Dbg_Assert(mp_coll_tri_data);

	//m_movement_changed = false;
	m_movement_changed = true;		// to update m_world_bbox
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollMovTri::~CCollMovTri()
{
	// Don't delete the Tri Data
}

// Temp functions
uint32 CCollMovTri::GetFaceFlags(int face_idx) const
{
	Dbg_Assert(mp_coll_tri_data);
	return mp_coll_tri_data->GetFaceFlags(face_idx);
}

uint16 CCollMovTri::GetFaceTerrainType(int face_idx) const
{
	Dbg_Assert(mp_coll_tri_data);
	return mp_coll_tri_data->GetFaceTerrainType(face_idx);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CCollMovTri::SetGeometry(CCollObjTriData *p_geom_data)
{
	CCollObj::SetGeometry(p_geom_data);
	Dbg_Assert(mp_coll_tri_data);
	
	// Re-calculate radius
	float new_length;
	m_max_radius = 0.0f;
	for (int i = 0; i < mp_coll_tri_data->m_num_verts; i++)
	{
		new_length = mp_coll_tri_data->GetRawVertexPos(i).Length();
		if (new_length > m_max_radius)
		{
			m_max_radius = new_length;
		}
	}

	m_movement_changed = true;		// to update m_world_bbox
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollObj * 	CCollMovTri::Clone(bool instance)
{
	Dbg_MsgAssert(!instance, ("CCollMovTri::Clone() with instances not implemented yet"));

	CCollMovTri *m_new_coll = new CCollMovTri(*this);

	m_new_coll->m_Flags |=  mSD_CLONE;

	m_new_coll->mp_coll_tri_data = mp_coll_tri_data->Clone(instance);

	return m_new_coll;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollMovTri::WithinBBox(const Mth::CBBox & testBBox)
{
	Dbg_Assert(mp_coll_tri_data);

	if (m_movement_changed)
	{
		update_world_bbox();
		m_movement_changed = false;
	}

	return testBBox.Intersect(m_world_bbox);
	//return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollMovTri::CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox *p_bbox)
{
	Dbg_Assert(p_data);
	Dbg_Assert(mp_coll_tri_data);

	bool best_collision = false;

	// Find line in local space
	Mth::Line local_line;
	convert_line_to_local(testLine, local_line);
	Mth::Vector local_line_dir(local_line.m_end - local_line.m_start);

	// Generate a bounding box for the line
	// so it can be tested against the BSP tree
	Mth::CBBox line_bbox(local_line.m_start);
	line_bbox.AddPoint(local_line.m_end);

	uint num_faces;
	FaceIndex *p_face_indexes;
	p_face_indexes = mp_coll_tri_data->FindIntersectingFaces(line_bbox, num_faces);

	for (uint fidx = 0; fidx < num_faces; fidx++, p_face_indexes++)
	{
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
		Mth::Vector v0, v1, v2;
		if (mp_coll_tri_data->m_use_face_small)
		{
			CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);

			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
		} else {
			CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);

			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
			mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
		}
#else
		Mth::Vector *v0, *v1, *v2;
		if (mp_coll_tri_data->m_use_face_small)
		{
			CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);

			v0 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
			v1 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
			v2 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
		} else {
			CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);

			v0 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
			v1 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
			v2 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
		}
#endif // FIXED_POINT_VERTICES

		float distance;
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
		if (sRayTriangleCollision(&local_line.m_start, &local_line_dir, &v0, &v1, &v2, &distance))
#else
		if (sRayTriangleCollision(&local_line.m_start, &local_line_dir, v0, v1, v2, &distance))
#endif
		{
			SCollSurface collisionSurface;

			/* We've got one */
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
			collisionSurface.point = v0;
			collisionSurface.index = *p_face_indexes;

			// Find normal
			Mth::Vector vTmp1(v1 - v0);
			Mth::Vector vTmp2(v2 - v0);
#else
			collisionSurface.point = (*v0);
			collisionSurface.index = *p_face_indexes;

			// Find normal
			Mth::Vector vTmp1(*v1 - *v0);
			Mth::Vector vTmp2(*v2 - *v0);
#endif // FIXED_POINT_VERTICES
			collisionSurface.normal = Mth::CrossProduct(vTmp1, vTmp2);
			collisionSurface.normal.Normalize();

			// Convert to world coordinates
			collisionSurface.point.Rotate(m_orient);
			collisionSurface.point += m_world_pos;
			collisionSurface.normal.Rotate(m_orient);

			if (s_found_collision(&testLine, this, &collisionSurface, distance, p_data))
			{
				best_collision = true;
			}
		}
	}

	return best_collision;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCollMovTri::CollisionWithRectangle(const Mth::Rectangle& rect, const Mth::CBBox& rect_bbox, S2DCollData* p_data)
{
	Dbg_MsgAssert(false, ("CCollMovTri::CollisionWithRecangle is not implemented"));
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollMovTri::DebugRender(uint32 ignore_1, uint32 ignore_0)
{
	// Debug box
	Mth::Matrix total_mat = m_orient;
	total_mat[W] = m_world_pos;
	mp_coll_tri_data->DebugRender(total_mat, ignore_1, ignore_0);
}

} // namespace Nx



================================================
FILE: Code/Gel/Collision/Collision.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx														**
**																			**
**	File name:		Collision.h												**
**																			**
**	Created: 		02/15/2002	-	grj										**
**																			**
*****************************************************************************/

#ifndef	__GEL_COLLISION_H
#define	__GEL_COLLISION_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#include 
#include 

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{
	class CCompositeObject;
}

namespace Nx
{
	
const int MAX_NUM_2D_COLLISIONS_REPORTED = 32;


// Forward declarations
class CCollObj;
class CCollMulti;
class CCollStatic;
struct CollData;
class CBatchTriCollMan;
class CCollCache;

struct SCollSurface
{
    Mth::Vector	normal;			/**< Triangle normal */
    Mth::Vector	point;			/**< First triangle vertex */
    int			index;			/**< Index of surface in object (if applicable) */
    //Mth::Vector * vertices[3];	/**< Pointers to three triangle vertices */
};

// structure used to record the collision of a single triangle with a 2D object; a collision test for a 2D object will generate a set of these
struct S2DCollSurface
{
	struct S2DCollEndpoint
	{
		Mth::Vector point;			// endpoint position
		Mth::Vector tangent;		// normalized tangent vector to the endpoint
		char tangent_exists;		// if a tangent of the endpoint is defined
	};
	
	S2DCollEndpoint ends[2];		// endpoints of the line cut through the triangle by the collision
	
	Mth::Vector	normal;				// normal of the collided triangle
	
	uint32 node_name;
	char trigger;
};

struct CollData
{
	SCollSurface	surface;
	float	dist;   
	uint16	ignore_1;			// bitmask, ignore poly if flags matching tis mask are 1
	uint16	ignore_0;			// ditto, but for flags that are 0
	uint16	flags;
	ETerrainType terrain;
	CCollObj *p_coll_object;	// the sector (object) with which we collided
	uint32	node_name;			// checsum of name of node associated with this object
	uint32	script;				// set to the script checksum of the object (or NULL if none)
	void	(*callback)( CollData* p_col_data);	   // a callback called every frame
	void 	*p_callback_data;		// 	pointer to some data the callback can use

	Obj::CCompositeObject *mp_callback_object;	// set only during callbacks
	
	char 	coll_found;
	char	trigger;			// true if it was a trigger	(if mFD_TRIGGER flag is set on the colliding face)
};

// collision data generated by a collision test for a 2D object
struct S2DCollData
{
	S2DCollSurface	p_surfaces[MAX_NUM_2D_COLLISIONS_REPORTED];	// surfaces collided with
	int 			num_surfaces;								// number of surfaces collided with
	uint16			ignore_1;
	uint16			ignore_0;
};

// enum used internally by the rectangle collision code to keep track of which triangle edge a collision line endpoint is at
enum ETriangleEdgeType
{
	NO_TRIANGLE_EDGE, TRIANGLE_EDGE_V0_V1, TRIANGLE_EDGE_V1_V2, TRIANGLE_EDGE_V2_V0
};

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

////////////////////////////////////////////////////////////////
// The base class for collision
class CCollObj
{
public:
						CCollObj();
	virtual				~CCollObj();

	uint32				GetChecksum() const;
	void				SetChecksum(uint32 checksum);	// For cloning
	uint16				GetObjectFlags() const;
	void				SetObjectFlags(uint16 flags);
	void				ClearObjectFlags(uint16 flags);

	//
	virtual bool		IsTriangleCollision() const { return false; }	// by default, no triangle collision

	//
	virtual uint32		GetFaceFlags(int face_idx) const = 0;
	virtual uint16		GetFaceTerrainType(int face_idx) const = 0;

	//
	virtual void		SetGeometry(CCollObjTriData *p_geom_data);
	CCollObjTriData *	GetGeometry() const;

	//
	virtual void		SetWorldPosition(const Mth::Vector & pos) = 0;
	virtual void		SetOrientation(const Mth::Matrix & orient) = 0;

	// The Get functions are virtual in case Static and Movable differ at a later point
	virtual const Mth::Vector &	GetWorldPosition() const = 0;
	virtual const Mth::Matrix &	GetOrientation() const = 0;
	virtual Mth::Vector GetVertexPos(int vert_idx) const = 0;

	virtual Obj::CCompositeObject *GetMovingObject() const;
	virtual void				SetMovingObject(Obj::CCompositeObject *p_movable_object);

	// Clone
	virtual CCollObj *	Clone(bool instance = false) = 0;

	// The virtual collision functions
	virtual bool		WithinBBox(const Mth::CBBox & testBBox) = 0;		// VERY quick test to see if we're in bbox range.
																			// Derived class may always return TRUE if it isn't
																			// worth checking.
	virtual bool		CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data,  Mth::CBBox *p_bbox) = 0;
	virtual bool		CollisionWithRectangle(const Mth::Rectangle& testRect, const Mth::CBBox& testRectBBox, S2DCollData *p_coll_data) = 0;

	// Wireframe drawing
	virtual void		DebugRender(uint32 ignore_1, uint32 ignore_0) = 0;

	// Conversion functions
	static CollType		sConvertTypeChecksum(uint32 checksum);

	// Collision creation
	static CCollObj *	sCreateStaticCollision(CollType type, Nx::CCollObjTriData *p_coll_obj_data, int num_coll_objects);
	static CCollObj *	sCreateMovableCollision(CollType type, Nx::CCollObjTriData *p_coll_obj_data, int num_coll_objects,
												Obj::CCompositeObject *p_object);

	// Does collision against all the static collision in the SuperSectors
	static bool			sFindNearestStaticCollision(Mth::Line &is, CollData *p_data, void *p_callback = NULL, CCollCache *p_cache = NULL);
	static bool			sFindFarStaticCollision(Mth::Line &is, CollData *p_data, void *p_callback = NULL, CCollCache *p_cache = NULL);

	// Does collision against all the movable collision
	static Obj::CCompositeObject *	sFindNearestMovableCollision(Mth::Line &is, CollData *p_data, void *p_callback = NULL, CCollCache *p_cache = NULL);
	static Obj::CCompositeObject *	sFindFarMovableCollision(Mth::Line &is, CollData *p_data, void *p_callback = NULL, CCollCache *p_cache = NULL);

	// Does rect collision against all the static collision
	static bool			sFindRectangleStaticCollision(Mth::Rectangle& rect, S2DCollData* p_coll_data, CCollCache* p_cache = NULL);
	
	// Triangle collision function
	static bool			sRayTriangleCollision(const Mth::Vector *rayStart, const Mth::Vector *rayDir,
											  Mth::Vector *v0, Mth::Vector *v1, Mth::Vector *v2, float *distance);
	
	static bool			sRectangleTriangleCollision(const Mth::Rectangle *rect,
													const Mth::Vector *v0, const Mth::Vector *v1, const Mth::Vector *v2,
													Mth::Vector p_collision_endpoints[2], ETriangleEdgeType p_collision_triangle_edges[2] );
	
	// 2D line-line collision function
	static bool			s2DLineLineCollision ( float p_start_X, float p_start_Y, float p_delta_X, float p_delta_Y,
											   float q_start_X, float q_start_Y, float q_delta_X, float q_delta_Y, float *s );
	
protected:
	virtual Mth::CBBox *get_bbox();											// Gets the quick bbox

	// Called on every collision found.  Returns true if this was the best collision so far.
	static bool			s_found_collision(const Mth::Line *p_is, CCollObj *p_coll_object, SCollSurface *p_collSurface,
										  float distance, CollData *p_collData);

	uint32				m_checksum;
	uint16				m_Flags;

	// Triangle data, similar to CCollSector's
	CCollObjTriData		*mp_coll_tri_data;

	static const float	sLINE_BOX_EXTENT;				 // Extent of box around collision line

	// Friends
	friend CCollMulti;
	friend CBatchTriCollMan;
	friend CCollCache;
};

////////////////////////////////////////////////////////////////
// Collision List
class CCollMulti : public CCollObj
{
public:
						CCollMulti();
	virtual				~CCollMulti();

	//
	virtual bool		IsTriangleCollision() const;

	//
	virtual void		SetWorldPosition(const Mth::Vector & pos);
	virtual void		SetOrientation(const Mth::Matrix & orient);
	// The following Get functions must be defined, but should never be called
	virtual void		SetGeometry(CCollObjTriData *p_geom_data);
	virtual const Mth::Vector &	GetWorldPosition() const;
	virtual const Mth::Matrix &	GetOrientation() const;
	virtual uint32		GetFaceFlags(int face_idx) const;
	virtual uint16		GetFaceTerrainType(int face_idx) const;
	virtual Mth::Vector GetVertexPos(int vert_idx) const;

	virtual Obj::CCompositeObject *GetMovingObject() const;
	virtual void		SetMovingObject(Obj::CCompositeObject *p_movable_object);

	// Add to collision list
	void				AddCollision(CCollObj *p_collision);

	// Clone
	virtual CCollObj *	Clone(bool instance = false);

	// The virtual collision functions
	virtual bool		WithinBBox(const Mth::CBBox & testBBox);			// VERY quick test to see if we're in bbox range.
																			// Derived class may always return TRUE if it isn't
																			// worth checking.
	virtual bool		CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox *p_bbox);
	virtual bool		CollisionWithRectangle(const Mth::Rectangle& testRect, const Mth::CBBox& testRectBBox, S2DCollData *p_coll_data);

	// Wireframe drawing
	virtual void		DebugRender(uint32 ignore_1, uint32 ignore_0);

protected:
	virtual Mth::CBBox *get_bbox();											// Gets the quick bbox

private:
	void				update_world_bbox();

	// 
	Lst::Head *mp_collision_list;
	Mth::CBBox			m_world_bbox;
	bool				m_movement_changed;				// Set to true every time SetWorldPosition() is called
	bool				m_world_bbox_valid;

	// Pointer to moving object
	Obj::CCompositeObject	*mp_movable_object;
};

////////////////////////////////////////////////////////////////
// Static object collision
class CCollStatic : public CCollObj
{
public:
	virtual				~CCollStatic();

	//
	virtual void		SetWorldPosition(const Mth::Vector & pos);
	virtual void		SetOrientation(const Mth::Matrix & orient);
	virtual const Mth::Vector &	GetWorldPosition() const;
	virtual const Mth::Matrix &	GetOrientation() const;
	virtual Mth::Vector GetVertexPos(int vert_idx) const;

	// SuperSector ID access
	uint8				GetSuperSectorID() const;
	void				SetSuperSectorID(uint8);

	virtual void		RotateY(const Mth::Vector & world_origin, Mth::ERot90 rot_y) = 0;
	virtual void		Scale(const Mth::Vector & world_origin, const Mth::Vector& scale) = 0;


protected:
						CCollStatic();	// Only the derived classes should call this

	// The position and orientation are already factored into the collision
	// data, unlike the Movable counterpart.
//	Mth::Vector			m_world_pos;
//	Mth::Matrix			m_orient;
	static	Mth::Vector	sWorldPos;		// just a dummy for now....
	static	Mth::Matrix	sOrient;		// just a dummy for now....

	//
	uint8				m_op_id;		// SuperSector ID
};

////////////////////////////////////////////////////////////////
// Movable object collision
class CCollMovable : public CCollObj
{
public:
	virtual				~CCollMovable();

	//
	virtual void		SetWorldPosition(const Mth::Vector & pos);
	virtual void		SetOrientation(const Mth::Matrix & orient);
	virtual const Mth::Vector &	GetWorldPosition() const;
	virtual const Mth::Matrix &	GetOrientation() const;
	virtual Mth::Vector GetVertexPos(int vert_idx) const;

	virtual Obj::CCompositeObject *GetMovingObject() const;
	virtual void				SetMovingObject(Obj::CCompositeObject *p_movable_object);

protected:
						CCollMovable();	// Only the derived classes should call this

	void				convert_line_to_local(const Mth::Line &world_line, Mth::Line &local_line);

	// These will be set by the CMovableObject
	Mth::Vector			m_world_pos;
	Mth::Matrix			m_orient;
	Mth::Matrix			m_orient_transpose;

	// Pointer to moving object
	Obj::CCompositeObject	*mp_movable_object;
};

////////////////////////////////////////////////////////////////
// Movable collision using axis-aligned bounding box data
class CCollMovBBox : public CCollMovable
{
public:
						CCollMovBBox();
						CCollMovBBox(CCollObjTriData *p_coll_data);
	virtual				~CCollMovBBox();

	virtual void		SetGeometry(CCollObjTriData *p_geom_data);	// Also sets the bounding box based on it
	void				SetBoundingBox(const Mth::CBBox & bbox);	// Sets bounding box (will go away)

	virtual uint32		GetFaceFlags(int face_idx) const;
	virtual uint16		GetFaceTerrainType(int face_idx) const;

	// Clone
	virtual CCollObj *	Clone(bool instance = false);

	// The virtual collision functions
	virtual bool		WithinBBox(const Mth::CBBox & testBBox); 			// Will always return TRUE because it isn't
																			// worth checking.
	virtual bool		CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox *p_bbox);
	virtual bool		CollisionWithRectangle(const Mth::Rectangle& testRect, const Mth::CBBox& testRectBBox, S2DCollData *p_coll_data);

	// Wireframe drawing
	virtual void		DebugRender(uint32 ignore_1, uint32 ignore_0);

private:
	Mth::CBBox			m_bbox;

	// Face data (bbox is considered one face)
	uint32				m_flags;
	uint16				m_terrain_type;			// terrain type
	Image::RGBA			m_rgba;					// just one color
};

////////////////////////////////////////////////////////////////
// Movable collision using triangles
class CCollStaticTri : public CCollStatic
{
public:
						CCollStaticTri();
						CCollStaticTri(CCollObjTriData *p_coll_data);
	virtual				~CCollStaticTri();

	//
	virtual bool		IsTriangleCollision() const { return true; }

	//virtual void		SetGeometry(CCollObjTriData *p_geom_data);
	virtual void		SetWorldPosition(const Mth::Vector & pos);
	virtual void		SetOrientation(const Mth::Matrix & orient);

	virtual uint32		GetFaceFlags(int face_idx) const;
	virtual uint16		GetFaceTerrainType(int face_idx) const;

	// Clone
	virtual CCollObj *	Clone(bool instance = false);

	virtual void		RotateY(const Mth::Vector & world_origin, Mth::ERot90 rot_y);
	virtual void		Scale(const Mth::Vector & world_origin, const Mth::Vector& scale);

	// The virtual collision functions
	virtual bool		WithinBBox(const Mth::CBBox & testBBox);			// VERY quick test to see if we're in bbox range.
	virtual bool		CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox *p_bbox);
	virtual bool		CollisionWithRectangle(const Mth::Rectangle& testRect, const Mth::CBBox& testRectBBox, S2DCollData *p_coll_data);

	void				ProcessOcclusion();			  
			  
	// Wireframe drawing
	virtual void		DebugRender(uint32 ignore_1, uint32 ignore_0);
	void				CheckForHoles();
	

private:
};

////////////////////////////////////////////////////////////////
// Movable collision using triangles
class CCollMovTri : public CCollMovable
{
public:
						CCollMovTri();
						CCollMovTri(CCollObjTriData *p_coll_data);
	virtual				~CCollMovTri();

	//
	virtual bool		IsTriangleCollision() const { return true; }

	virtual void		SetGeometry(CCollObjTriData *p_geom_data);
	virtual void		SetWorldPosition(const Mth::Vector & pos);
	virtual void		SetOrientation(const Mth::Matrix & orient);

	virtual uint32		GetFaceFlags(int face_idx) const;
	virtual uint16		GetFaceTerrainType(int face_idx) const;

	// Clone
	virtual CCollObj *	Clone(bool instance = false);

	// The virtual collision functions
	virtual bool		WithinBBox(const Mth::CBBox & testBBox);			// VERY quick test to see if we're in bbox range.
	virtual bool		CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox *p_bbox);
	virtual bool		CollisionWithRectangle(const Mth::Rectangle& testRect, const Mth::CBBox& testRectBBox, S2DCollData* p_coll_data);

	// Wireframe drawing
	virtual void		DebugRender(uint32 ignore_1, uint32 ignore_0);

protected:
	virtual Mth::CBBox *get_bbox();											// Gets the quick bbox

private:
	void				update_world_bbox();

	float				m_max_radius;					// maximum radius that encompases geometry around the origin
	bool				m_movement_changed;				// Set to true every time SetWorldPosition() is called
	Mth::CBBox			m_world_bbox;					// approx bbox in world space that is guaranteed to encompass geometry
};


///////////////////////////////////////////////////////////////////////
//
// Inline functions
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint32			CCollObj::GetChecksum() const
{
	return m_checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint16			CCollObj::GetObjectFlags() const
{
	return m_Flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void				CCollObj::SetGeometry(CCollObjTriData *p_geom_data)
{
	mp_coll_tri_data = p_geom_data;
	// This only works for non-instanced collision
	if (p_geom_data) m_checksum = p_geom_data->GetChecksum();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline CCollObjTriData *CCollObj::GetGeometry() const
{
	return mp_coll_tri_data;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void				CCollObj::SetObjectFlags(uint16 flags)
{
	m_Flags |= flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void				CCollObj::ClearObjectFlags(uint16 flags)
{
	m_Flags &= ~flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Mth::CBBox *		CCollObj::get_bbox()
{
	return NULL;		// default is none
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void				CCollStatic::SetWorldPosition(const Mth::Vector & pos)
{
//	m_world_pos = pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector &	CCollStatic::GetWorldPosition() const
{
//	return m_world_pos;
	return sWorldPos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void				CCollStatic::SetOrientation(const Mth::Matrix & orient)
{
//	m_orient[X] = orient[X];	// Just the 3x3
//	m_orient[Y] = orient[Y];
//	m_orient[Z] = orient[Z];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Matrix &	CCollStatic::GetOrientation() const
{
//	return m_orient;
	return sOrient;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint8				CCollStatic::GetSuperSectorID() const
{
	return m_op_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CCollStatic::SetSuperSectorID(uint8 id)
{
	m_op_id = id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void				CCollMovable::SetWorldPosition(const Mth::Vector & pos)
{
	m_world_pos = pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector &	CCollMovable::GetWorldPosition() const
{
	return m_world_pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void				CCollMovable::SetOrientation(const Mth::Matrix & orient)
{
	m_orient[X] = orient[X];	// Just the 3x3
	m_orient[Y] = orient[Y];
	m_orient[Z] = orient[Z];

	m_orient_transpose.Transpose(m_orient);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Matrix &	CCollMovable::GetOrientation() const
{
	return m_orient;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CCollMovBBox::SetBoundingBox(const Mth::CBBox & bbox)
{
	m_bbox = bbox;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool					CCollMovBBox::WithinBBox(const Mth::CBBox & testBBox)
{
	// Will always return TRUE because it isn't worth checking
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CCollMovTri::SetWorldPosition(const Mth::Vector & pos)
{
	CCollMovable::SetWorldPosition(pos);
	m_movement_changed = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CCollMovTri::SetOrientation(const Mth::Matrix & orient)
{
	CCollMovable::SetOrientation(orient);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CCollMovTri::update_world_bbox()
{
	m_world_bbox.SetMin(Mth::Vector(-m_max_radius + m_world_pos[X], -m_max_radius + m_world_pos[Y], -m_max_radius + m_world_pos[Z]));
	m_world_bbox.SetMax(Mth::Vector( m_max_radius + m_world_pos[X],  m_max_radius + m_world_pos[Y],  m_max_radius + m_world_pos[Z]));
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Mth::CBBox *			CCollMovTri::get_bbox()
{
	if (m_movement_changed)
	{
		update_world_bbox();
		m_movement_changed = false;
	}

	return &m_world_bbox;
}


} // namespace Nx

#endif  //	__GEL_COLLISION_H


================================================
FILE: Code/Gel/Collision/MovCollMan.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx								 						**
**																			**
**	File name:		gel\collision\movcollman.cpp   							**
**																			**
**	Created by:		04/08/02	-	grj										**
**																			**
**	Description:															**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Nx
{

Lst::Head CMovableCollMan::s_collision_list;
//CCollObj * CMovableCollMan::s_collision_array[MAX_COLLISION_OBJECTS];
//int CMovableCollMan::s_array_size = 0;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CMovableCollMan::sAddCollision(CCollObj *p_collision, Obj::CCompositeObject *p_object)
{
	Lst::Node *node = new Lst::Node(p_collision);
	s_collision_list.AddToTail(node);
	//s_collision_array[s_array_size++] = p_collision;
	//Dbg_Assert(s_array_size <= MAX_COLLISION_OBJECTS);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CMovableCollMan::sRemoveCollision(CCollObj *p_collision)
{
	Lst::Node *node_coll, *node_next;

	for(node_coll = s_collision_list.GetNext(); node_coll; node_coll = node_next)
	{
		node_next = node_coll->GetNext();

		if (node_coll->GetData() == p_collision)
		{
			delete node_coll;
		}
	}
}


} // namespace Nx



================================================
FILE: Code/Gel/Collision/MovCollMan.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx														**
**																			**
**	File name:		MovCollMan.h											**
**																			**
**	Created: 		04/08/2002	-	grj										**
**																			**
*****************************************************************************/

#ifndef	__GEL_MOVCOLLMAN_H
#define	__GEL_MOVCOLLMAN_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Nx
{

class CCollObj;
class CCollObjTriData;

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CMovableCollMan
{
public:
	//static void				sInit();
	//static void				sCleanup();

	static void				sAddCollision(CCollObj *p_collsion, Obj::CCompositeObject *p_object);
	static void				sRemoveCollision(CCollObj *p_collsion);

	static Lst::Head *sGetCollisionList();
	static CCollObj **			sGetCollisionArray();

protected:
	enum
	{
		MAX_COLLISION_OBJECTS = 100,
	};

	static Lst::Head	s_collision_list;
	static CCollObj *			s_collision_array[MAX_COLLISION_OBJECTS];		// If the speed is needed
	static int					s_array_size;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Lst::Head *CMovableCollMan::sGetCollisionList()
{
	return &s_collision_list;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline CCollObj **			CMovableCollMan::sGetCollisionArray()
{
	return s_collision_array;
}

} // namespace Nx

#endif	//	__GEL_MOVCOLLMAN_H


================================================
FILE: Code/Gel/Components/BouncyComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       BouncyComponent.h
//* OWNER:          Mick West
//* CREATION DATE:  10/22/2002
//****************************************************************************

#ifndef __COMPONENTS_BOUNCYCOMPONENT_H__
#define __COMPONENTS_BOUNCYCOMPONENT_H__

#include 
#include 
#include 
#include 


#define		CRC_BOUNCY CRCD(0x69c257e9,"Bouncy")
#define		GetBouncyComponent() ((Obj::CBouncyComponent*)GetComponent(CRC_BOUNCY))

namespace Mth
{
    class SlerpInterpolator;
};


#define BOUNCYOBJ_FLAG_PLAYER_COLLISION_OFF			( 1 << 0 )


namespace Obj
{


// These are in the Obj:: namespace, whilse we are sperating the bouncy componet from the bouncy object
enum
{
	BOUNCYOBJ_STATE_INIT,
	BOUNCYOBJ_STATE_IDLE,
	BOUNCYOBJ_STATE_BOUNCING,
};

class CBouncyComponent : public CBaseComponent
{
	
public:
// Functions common to components - the interface to the outside world								
								CBouncyComponent();
    virtual         			~CBouncyComponent();
    virtual void    			Update();
    virtual void    			InitFromStructure( Script::CStruct* pParams );
	static CBaseComponent *		s_create();
	CBaseComponent::EMemberFunctionResult	CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript );
	virtual void 				GetDebugInfo( Script::CStruct* p_info );

// private functions - indirectly called from the above functions	
private:
	void						bounce( const Mth::Vector &vel );
	void 						bounce_from_object_vel( const Mth::Vector &vel, const Mth::Vector &pos );
	void						do_bounce( void );
	bool 						rotate_to_quat( void );
	void 						set_up_quats( void );

	void 						land_on_any_face( Mth::Vector &rot );
	void 						land_on_top_or_bottom( Mth::Vector &rot );
	void 						land_traffic_cone( Mth::Vector &rot );

	int							m_bouncyobj_flags;
	Mth::Vector 				m_rotation;
	int							m_rot_per_axis;
	int							m_bounce_rot;
	float						m_min_bounce_vel;
	// should the object come to rest flat (90 degree oriented?)
	int							m_rest_orientation_type;
	// percent of bounce vel maintained ( 1.0 would make it bounce the same height )
	float						m_bounce_mult;
	float						m_up_mag; // this will be the upward velocity (varies depending on initial vel)
	int							m_bounce_count;
	bool						m_quat_initialized;
	float						m_quatrot_time_elapsed;
	float						m_gravity;
	float						m_bounciness;
	float						m_min_initial_vel;
	int							m_random_dir; // on bounce, change direction by this much...
	Mth::SlerpInterpolator *	mp_bounce_slerp;
	uint32						m_bounce_sound;
	uint32						m_hit_sound;
					
	float						m_bounce_collision_radius;
	float						m_skater_collision_radius_squared;
						
	bool						m_destroy_when_done;

    // logic states...
    short 						m_state;
    short 						m_substate;

	// 
	uint32						m_collide_script;
	Script::CStruct*			mp_collide_script_params;	
	uint32						m_bounce_script;
	Script::CStruct*			mp_bounce_script_params;	
	
	// The following is stuff that might be better off in the composite object, or some general physics component?
	// or perhaps a "simplephysics" base compoennt
	
	Mth::Vector					m_pos;
	Mth::Vector					m_old_pos;
	Mth::Vector					m_vel;
	Mth::Matrix					m_matrix;
	float						m_time;


};

}

#endif


================================================
FILE: Code/Gel/Components/CameraComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CameraComponent.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/21/03
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent* CCameraComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CCameraComponent );	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CCameraComponent::CCameraComponent() : CBaseComponent()
{
	SetType( CRC_CAMERA );

	// Enabled by default.
	m_enabled = true;

	// Create and attach a Gfx::Camera.
	mp_camera = new Gfx::Camera();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CCameraComponent::~CCameraComponent()
{
	// Destroy the Gfx::Camera.
	delete mp_camera;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCameraComponent::InitFromStructure( Script::CStruct* pParams )
{
	// cameras must have a very low priority to insure that all objects update before they do (most importantly, the skater)
	CCompositeObjectManager::Instance()->SetObjectPriority(*GetObject(), -1000);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCameraComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCameraComponent::Update()
{
	if( m_enabled )
	{
		// Use the position and orientation of the parent object to position and orient the attached camera.
		Mth::Vector pos = GetObject()->GetPos();
		Mth::Matrix mat = GetObject()->GetMatrix();

		// Set the Display pos of the object to the actual pos, so we can attach a model
		// to the camera
		GetObject()->SetDisplayMatrix(mat);

		mp_camera->SetPos( pos );
		mp_camera->SetMatrix( mat );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent::EMemberFunctionResult CCameraComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
/*
        // @script | DoSomething | does some functionality
		case 0xbb4ad101:		// DoSomething
			DoSomething();
			break;

        // @script | ValueIsTrue | returns a boolean value
		case 0x769260f7:		// ValueIsTrue
		{
			return ValueIsTrue() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		break;
*/

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCameraComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CCameraComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*	Example:
	p_info->AddInteger("m_never_suspend",m_never_suspend);
	p_info->AddFloat("m_suspend_distance",m_suspend_distance);
	*/
	
	// We call the base component's GetDebugInfo, so we can add info from the common base component.
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCameraComponent::Enable( bool enable )
{
	m_enabled = enable;

	// Go through and enable other attached components?
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector &CCameraComponent::GetPosition( void ) const
{
	return GetObject()->GetPos();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCameraComponent::SetPosition( Mth::Vector& pos )
{
 	GetObject()->SetPos( pos );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Mth::Matrix& CCameraComponent::GetMatrix( void )
{
	return GetObject()->GetMatrix();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCameraComponent::StoreOldPosition( void )
{
	if( mp_camera )
	{
		mp_camera->StoreOldPos();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCameraComponent::SetHFOV( float hfov )
{
	if( mp_camera )
	{
		mp_camera->SetHFOV( hfov );
	}
}



}

================================================
FILE: Code/Gel/Components/CameraComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CameraComponent.h
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/21/03
//****************************************************************************

#ifndef __COMPONENTS_CAMERACOMPONENT_H__
#define __COMPONENTS_CAMERACOMPONENT_H__

#include 
#include 

#include 
#include 

// Replace this with the CRCD of the component you are adding.
#define		CRC_CAMERA								CRCD( 0xc4e311fa, "Camera" )

//  Standard accessor macros for getting the component either from within an object, or given an object.
#define		GetCameraComponent()					((Obj::CCameraComponent*)GetComponent( CRC_CAMERA ))
#define		GetCameraComponentFromObject( pObj )	((Obj::CCameraComponent*)(pObj)->GetComponent( CRC_CAMERA ))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CCameraComponent : public CBaseComponent
{
public:
    CCameraComponent();
    virtual ~CCameraComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

	void							SetHFOV( float hfov );

	void							Enable( bool enable );
	Gfx::Camera*					GetCamera( void )		{ return mp_camera; }
	const Mth::Vector &				GetPosition( void ) const ;
	void							SetPosition( Mth::Vector& pos );
	void							StoreOldPosition( void );
	Mth::Matrix&					GetMatrix( void );

private:

	bool							m_enabled;
	Gfx::Camera*					mp_camera;
};

}

#endif


================================================
FILE: Code/Gel/Components/CameraLookAroundComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CameraLookAroundComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  4/14/3
//****************************************************************************

#ifdef TESTING_GUNSLINGER

#include 

#else

#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
						   
namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CCameraLookAroundComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CCameraLookAroundComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCameraLookAroundComponent::CCameraLookAroundComponent() : CBaseComponent()
{
	SetType( CRC_CAMERALOOKAROUND );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCameraLookAroundComponent::~CCameraLookAroundComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::InitFromStructure( Script::CStruct* pParams )
{
	mLookaroundTilt					= 0.0f;
	mLookaroundHeading				= 0.0f;
	mLookaroundZoom					= 1.0f;
	
	mLookaroundLock					= false;
	mLookaroundOverride				= false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::Finalize (   )
{
	mp_input_component = GetInputComponentFromObject(GetObject());
	
	Dbg_Assert(mp_input_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::Update()
{
	CControlPad * p_control_pad;
	
	if (Script::GetInt(CRCD(0x702247a5,"Viewer_controller"), false))
	{
		p_control_pad = &mp_input_component->GetControlPad2();
	}
	else
	{
		p_control_pad = &mp_input_component->GetControlPad();
	}
	

	// Handle pressing of "Select" button
	// 	
	if (!Script::GetInt(CRCD(0xf3e055e1,"select_shift")))
	{
		if (p_control_pad->m_select.GetTriggered())
		{
			printf ("Camera Detected Select Pressed\n");
			p_control_pad->m_select.ClearTrigger();
			// Depending on which skatercam we are, run one of the UserSelectSelect scripts
			if (GetObject()->GetID() == CRCD(0x967c138c,"SkaterCam0"))
			{
				Script::RunScript(CRCD(0x60871393,"UserSelectSelect"));
			}
			else
			{
				Script::RunScript(CRCD(0xa1b1146d,"UserSelectSelect2"));
			}
		}
	}
	
	if (p_control_pad->m_R3.GetTriggered())
	{
		mLookaroundLock = !mLookaroundLock;
		p_control_pad->m_R3.ClearTrigger();
	}
	
	float frame_length = Tmr::FrameLength();
	
	if (!mLookaroundLock && !mLookaroundOverride)
	{
		float target = p_control_pad->m_scaled_rightY;
		target = -1.4f * (target < 0.0f ? (0.7f * target) : (0.5f * target));
		if (Mth::Abs(target - mLookaroundTilt) > 0.001f)
		{
			mLookaroundTilt += (target - mLookaroundTilt) * 3.75f * frame_length;
		}
		else
		{
			mLookaroundTilt = target;
		}
		
		target = 3.0f * p_control_pad->m_scaled_rightX;
		if (Mth::Abs(target - mLookaroundHeading) > 0.001f)
		{
			mLookaroundHeading += (target - mLookaroundHeading) * 3.75f * frame_length;
		}
		else
		{
			mLookaroundHeading = target;
		}
	}
	
	float delta_time = Tmr::FrameLength();

	if (mLookaroundOverride)
	{
		if (mLookaroundDeltaTimer > 0.0f)
		{
			if (mLookaroundDeltaTimer > delta_time)
			{
				mLookaroundHeading		+= mLookaroundHeadingDelta * delta_time;
				mLookaroundTilt			+= mLookaroundTiltDelta * delta_time;
				mLookaroundDeltaTimer	-= delta_time;
			}
			else
			{
				mLookaroundHeading		= mLookaroundHeadingTarget;
				mLookaroundTilt			= mLookaroundTiltTarget;
				mLookaroundDeltaTimer	= 0.0f;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CCameraLookAroundComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | SC_SetSkaterCamOverride | 
        // @parm float | heading |
        // @parm float | tilt | 
        // @parm float | time | 
        // @parm float | zoom | 
		case 0xb8f23899:	// SC_SetSkaterCamOverride
		{
			float heading, tilt, time;
			float zoom = 1.0f;
			pParams->GetFloat( CRCD(0xfd4bc03e,"heading"), &heading, Script::ASSERT );
			pParams->GetFloat( CRCD(0xe3c07609,"tilt"), &tilt, Script::ASSERT );
			pParams->GetFloat( CRCD(0x906b67ba,"time"), &time, Script::ASSERT );
			pParams->GetFloat( CRCD(0x48d4868b,"zoom"), &zoom );

			SetLookaround( heading, tilt, time, zoom );
			
			break;
		}

        // @script | SC_ClearSkaterCamOverride |
		case 0xe3b327c0:	// SC_ClearSkaterCamOverride
			ClearLookaround();
			break;

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CCameraLookAroundComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::SetLookaround( float heading, float tilt, float time, float zoom )
{
	mLookaroundOverride				= true;

	mLookaroundHeadingTarget		= heading;
	mLookaroundTiltTarget			= tilt;

	// Calculate delta - the amount to move in 1 second.
	if (time > 0.0f)
	{
		mLookaroundHeadingDelta		= (heading - mLookaroundHeading) / time;
		mLookaroundTiltDelta		= (tilt - mLookaroundTilt) / time;
	}

	mLookaroundDeltaTimer			= time;
	mLookaroundZoom					= zoom;
}

#if 0 // old control input checking code

char touched[256 * 256 / 8];
bool initialized = false;

void MapInput (   )
{
	if (!initialized)
	{
		for (int n = 255 * 255; n--; )
		{
			touched[n / 8] = 0;
		}
		initialized = true;
	}
	
	CControlPad& pad = mp_input_component->GetControlPad();
	
	int x = static_cast< int >(pad.m_rightX) + 128;
	int y = static_cast< int >(pad.m_rightY) + 128;
	
	// printf("x = %i; y = %i\n", x, y);
	
	int index = (x * 256) + y;
	
	// printf("index / 8 = %i\n", index / 8);
	// printf("MAX = %i\n", 256 * 256 / 8);
	
	touched[index / 8] |= (1 << (index % 8));
}


void DrawMap (   )
{
	Mth::Vector pos = GetObject()->GetPos();
	Mth::Matrix matrix = GetObject()->GetMatrix();
	
	Mth::Vector center = pos - 300.0f * matrix[Z];
	Mth::Vector up = matrix[Y];
	Mth::Vector left = -matrix[X];
	
	for (int n = 255 * 255; n--; )
	{
		if (!(touched[n / 8] & (1 << (n % 8)))) continue;
		
		int x = n / 256;
		int y = n % 256;
		
		Mth::Vector point = center + up * (y - 128) + left * (x - 128);
		
		Gfx::AddDebugLine(point + up + left, point - up - left, MAKE_RGB(255, 255, 255), 0, 1);
		Gfx::AddDebugLine(point + up - left, point - up + left, MAKE_RGB(255, 255, 255), 0, 1);
	}
}

#endif

}

#endif // TESTING_GUNSLINGER


================================================
FILE: Code/Gel/Components/CameraLookAroundComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CameraLookAroundComponent.h
//* OWNER:          Dan
//* CREATION DATE:  4/14/3
//****************************************************************************

#ifndef __COMPONENTS_CAMERALOOKAROUNDCOMPONENT_H__
#define __COMPONENTS_CAMERALOOKAROUNDCOMPONENT_H__

#include 
#include 

#include 
#include 

#define		CRC_CAMERALOOKAROUND CRCD(0x81c7122d, "CameraLookAround")

#define		GetCameraLookAroundComponent() ((Obj::CCameraLookAroundComponent*)GetComponent(CRC_CAMERALOOKAROUND))
#define		GetCameraLookAroundComponentFromObject(pObj) ((Obj::CCameraLookAroundComponent*)(pObj)->GetComponent(CRC_CAMERALOOKAROUND))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CInputComponent;

class CCameraLookAroundComponent : public CBaseComponent
{
	friend class CSkaterCameraComponent;
	friend class CWalkCameraComponent;
	friend class CVehicleCameraComponent;
	
public:
    CCameraLookAroundComponent();
    virtual ~CCameraLookAroundComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							SetLookaroundHeadingExtra( float lookaround_heading_extra );
	void							ClearLookaround( void );
	void							SetLookaround( float heading, float tilt, float time, float zoom );
	
	bool							IsHeadingTargetZero (   );
	bool							IsTiltTargetZero (   );
	bool							IsLookaroundActive ( void );
	
private:
	bool							mLookaroundLock;
	bool							mLookaroundOverride;	// For when the designer is scripting the lookaround values.
	float							mLookaroundZoom;		// Allows designers to adjust the zoom when overrideing the lookaround values.
	float							mLookaroundTilt;
	float							mLookaroundHeading;
	float							mLookaroundHeadingStartingPoint;
	float							mLookaroundTiltTarget;
	float							mLookaroundHeadingTarget;
	float							mLookaroundTiltDelta;
	float							mLookaroundHeadingDelta;
	float							mLookaroundDeltaTimer;

	CInputComponent*				mp_input_component;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CCameraLookAroundComponent::ClearLookaround( void )
{
	mLookaroundOverride				= false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CCameraLookAroundComponent::IsHeadingTargetZero ( void )
{
	return mp_input_component->GetControlPad().m_scaled_rightX == 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CCameraLookAroundComponent::IsTiltTargetZero ( void )
{
	return mp_input_component->GetControlPad().m_scaled_rightY == 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CCameraLookAroundComponent::IsLookaroundActive ( void )
{
	return mLookaroundHeading != 0.0f || !IsHeadingTargetZero()
		|| mLookaroundTilt != 0.0f || !IsTiltTargetZero();
}

}

#endif


================================================
FILE: Code/Gel/Components/CameraUtil.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CameraUtil.cpp
//* OWNER:          Dan
//* CREATION DATE:  4/10/3
//****************************************************************************

/*
 * Utility functions for camera behavior components.
 */

#include 

#include 

namespace Obj
{

/*******************************************************************************************************
logic for getTimeAdjustedSlerp()

If the camera is lagging a distance D behind the target, and the target is moving with velocity V, and the lerp is set to L.
Then when the camera is stable, it will be moving at the same rate as the target (V), and since the distance it will move is
equal to the lerp multiplied by the distance from the target (which will be the old distance plus the new movement of the
target V)

Then:

L*(D+V) = V 
L*D + L*V = V
D = V * (1 - L) / L

Assuming in the above T = 1, then if we have a variable T, the speed of the skater will be T*V, yet we want the distance (Dt)
moved to remain unchanged (D), so for a time adjusted lerp Lt

Dt = T * V * (1-Lt)/Lt

Since D = Dt

V * (1 - L) / L = T * V * (1-Lt)/Lt

V cancels out, and we get

Lt = TL / (1 - L + TL)

Sanity check,  

if L is 0.25, and T is 1, then Lt = 1*0.25 / (1 - 0.25 + 1*0.25)  = 0.25
if L is 0.25, and T is 2, then Lt = 2*0.25 / (1 - 0.25 + 2*0.25)  = 0.5 / 1.25 = 0.40
if L is 0.25, and T is 5, then Lt = 5*0.25 / (1 - 0.25 + 5*0.25)  = 1.25 / 2 = 0.625

Sounds about right.
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float GetTimeAdjustedSlerp( float slerp, float delta )
{
	// Mick's method, tries to guarantee that the distance from the target point
	// will never alter at constant velocity, with changing frame rate
	// Lt = TL / (1 - L + TL)
	float t		= delta * 60.0f;
	float Lt	= t * slerp / ( 1.0f - slerp + t * slerp );
	return Lt;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#define vMIN_WALL_DISTANCE (2.0f)

void ApplyCameraCollisionDetection ( Mth::Vector& camera_pos, Mth::Matrix& camera_matrix, const Mth::Vector& target_pos, const Mth::Vector& focus_pos, bool side_feelers, bool refocus )
{
	// camera collision detection
	
	Mth::Vector target_to_camera_direction = camera_pos - target_pos;
	target_to_camera_direction.Normalize();
	
	CFeeler feeler;
	
	// Ignore faces based on face falgs
	feeler.SetIgnore(IGNORE_FACE_FLAGS_1, IGNORE_FACE_FLAGS_0);
	feeler.SetLine(target_pos, camera_pos + vMIN_WALL_DISTANCE * target_to_camera_direction);
	bool collision = feeler.GetCollision(true);
	if( collision )
	{
//			Gfx::AddDebugLine( at_pos, cam_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 2000 );

		// Limits the camera to getting nearer than 11.9 inches to the focus point.
		float distance = feeler.GetDist();
		float length = feeler.Length();
		if( length * distance < 11.9f + vMIN_WALL_DISTANCE )
		{
			distance = (11.9f + vMIN_WALL_DISTANCE) / length;
		}
		camera_pos = target_pos + (distance * length - vMIN_WALL_DISTANCE) * target_to_camera_direction;
	}
	
	if (!side_feelers) return;

//#	if defined( __PLAT_NGPS__ ) || defined ( __PLAT_XBOX__ )
	// Now do two additional checks 1 foot to either side of the camera.
	Mth::Vector	left( camera_matrix[X][X], 0.0f, camera_matrix[X][Z], 0.0f );
	left.Normalize( 8.0f );
	left += camera_pos;

	collision = feeler.GetCollision(camera_pos, left, true);
	if( collision )
	{
		left		   -= feeler.GetPoint();
		camera_pos		   -= left;
	}
	else
	{
		Mth::Vector	right( -camera_matrix[X][X], 0.0f, -camera_matrix[X][Z], 0.0f );
		right.Normalize( 8.0f );
		right += camera_pos;

		collision = feeler.GetCollision(camera_pos, right, true);
		if( collision )
		{
			right		   -= feeler.GetPoint();
			camera_pos		   -= right;
		}
	}
	
	if (!refocus) return;

	if( collision )
	{
		// Re-orient the camera again.
		camera_matrix[Z].Set( focus_pos.GetX() - camera_pos.GetX(), focus_pos.GetY() - camera_pos.GetY(), focus_pos.GetZ() - camera_pos.GetZ());
		camera_matrix[Z].Normalize();

		// Read back the Y from the current matrix.
//			target[Y][0]	= p_frame_matrix->up.x;
//			target[Y][1]	= p_frame_matrix->up.y;
//			target[Y][2]	= p_frame_matrix->up.z;

		// Generate new orthonormal X and Y axes.
		camera_matrix[X]		= Mth::CrossProduct( camera_matrix[Y], camera_matrix[Z] );
		camera_matrix[X].Normalize();
		camera_matrix[Y]		= Mth::CrossProduct( camera_matrix[Z], camera_matrix[X] );
		camera_matrix[Y].Normalize();

		// Write back into camera matrix.
		camera_matrix[X]		= -camera_matrix[X];
		camera_matrix[Z]		= -camera_matrix[Z];
		
		// Fix the final column
		camera_matrix[X][W] = 0.0f;
		camera_matrix[Y][W] = 0.0f;
		camera_matrix[Z][W] = 0.0f;
		camera_matrix[W][W] = 1.0f;
		
	}
//#endif
}

}


================================================
FILE: Code/Gel/Components/CameraUtil.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CameraUtil.cpp
//* OWNER:          Dan
//* CREATION DATE:  4/10/3
//****************************************************************************

#ifndef __COMPONENTS_CAMERAUTIL_H__
#define __COMPONENTS_CAMERAUTIL_H__

#include 
#include 
#include 

#define SKATERCAMERACOMPONENT_PERFECT_ABOVE			(3.0f)
			
#if 0
// Ignore faces that are NOT flagged as camera collidable
#define IGNORE_FACE_FLAGS_1 	(0)
#define IGNORE_FACE_FLAGS_0 	(mFD_CAMERA_COLLIDABLE)
#endif

#if 0
// ignore faces that ARE flagged as camera collidable
#define IGNORE_FACE_FLAGS_1 	(0)
#define IGNORE_FACE_FLAGS_0 	(mFD_CAMERA_COLLIDABLE)
#endif

#if 1
// ignore faces that are non-collidable or invisible
//#define IGNORE_FACE_FLAGS_1 	(mFD_NON_COLLIDABLE | mFD_INVISIBLE)
#define IGNORE_FACE_FLAGS_1 	(mFD_NON_COLLIDABLE | mFD_CAMERA_COLLIDABLE)
#define IGNORE_FACE_FLAGS_0 	(0)
#endif

#	define CAMERA_SLERP_STOP 0.9999f
	   
namespace Obj
{
	float			GetTimeAdjustedSlerp ( float slerp, float delta );
	void			ApplyCameraCollisionDetection ( Mth::Vector& camera_pos,  Mth::Matrix& camera_matrix, const Mth::Vector& target_pos, const Mth::Vector& forcus_pos, bool side_feelers = true, bool refocus = true );
	
	struct SCameraState
	{
		Mth::Matrix lastActualMatrix;
		Mth::Vector lastTripodPos;
		float lastDot;
		float lastZoom;
	};
}

#endif


================================================
FILE: Code/Gel/Components/CollideAndDieComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CollideAndDieComponent.cpp
//* OWNER:          SPG
//* CREATION DATE:  7/10/03
//****************************************************************************

// The CEmptyComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy emptycomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "Empty" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage

#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#define vFIREBALL_LIFETIME 	Tmr::Seconds( 10 )

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CCollideAndDieComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CCollideAndDieComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CCollideAndDieComponent::CCollideAndDieComponent() : CBaseComponent()
{
    SetType( CRC_COLLIDEANDDIE );
	m_radius = 0.0f;
	m_scale = 0;
	m_birth_time = Tmr::GetTime();
	m_death_script = 0;
	m_dying = false;
	m_first_frame = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCollideAndDieComponent::~CCollideAndDieComponent()
{   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCollideAndDieComponent::SetCollisionRadius( float radius )
{
	m_radius = radius;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CCollideAndDieComponent::InitFromStructure( Script::CStruct* pParams )
{
	pParams->GetFloat(CRCD(0xc48391a5,"radius"), &m_radius, Script::ASSERT);
	pParams->GetFloat(CRCD(0x13b9da7b,"scale"), &m_scale, Script::ASSERT);
	
	pParams->GetChecksum(CRCD(0x6647adc3,"death_script"), &m_death_script, Script::ASSERT);

    m_vel.Set( 0, 0, 1 );
	pParams->GetVector(CRCD(0xc4c809e, "vel"), &m_vel, Script::ASSERT);
	m_vel.Normalize();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CCollideAndDieComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	//InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCollideAndDieComponent::Finalize()
{
	mp_suspend_component =  GetSuspendComponentFromObject( GetObject() );
}
	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCollideAndDieComponent::Hide( bool should_hide )
{
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CCollideAndDieComponent::Update()
{
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();

	if (gamenet_man->InNetGame() || !mp_suspend_component->SkipLogic())
	{
		if(( Tmr::GetTime() - m_birth_time ) > vFIREBALL_LIFETIME )
		{
			GetObject()->MarkAsDead();
		}
		else
		{
			if( m_dying )
			{
				if( Tmr::GetTime() > m_death_time )
				{
					GetObject()->MarkAsDead();
				}
			}
			else
			{
				Mth::Vector vel, start, end;
				CFeeler feeler;
		
				vel = GetObject()->GetVel();
				vel.Normalize();
				if( m_first_frame )
				{
					start = GetObject()->GetPos();
				}
				else
				{
					start = m_last_pos;
				}
				
				start[Y] += FEET( 2 );
				end = GetObject()->GetPos();
				end[Y] += FEET( 2 );
				end += ( vel * m_radius );
				feeler.SetStart( start );
				feeler.SetEnd( end );
		
				if( feeler.GetCollision())
				{
					if( m_dying == false )
					{
						CProjectileCollisionComponent* proj_comp;

						proj_comp = GetProjectileCollisionComponentFromObject( GetObject());
						if( proj_comp )
						{
							proj_comp->MarkAsDying();
						}

						m_dying = true;
						m_death_time = Tmr::GetTime() + Tmr::Seconds( 1 );
						if( m_death_script != 0 )
						{
							Script::CStruct* params;
							Mth::Vector pos;

							pos = feeler.GetPoint();
							params = new Script::CStruct;
							
							params->AddVector( CRCD(0x7f261953,"pos"), pos );
							params->AddVector( CRCD(0xc4c809e,"vel"), m_vel );
							params->AddFloat( CRCD(0x13b9da7b,"scale"), m_scale );

							Script::RunScript( m_death_script, params );

							delete params;
						}
					}
				}
			}
		}

		m_last_pos = GetObject()->GetPos();
		m_first_frame = false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CCollideAndDieComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCollideAndDieComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CCollideAndDieComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums
	

// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/CollideAndDieComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VelocityComponent.h
//* OWNER:          SPG
//* CREATION DATE:  07/10/03
//****************************************************************************

#ifndef __COMPONENTS_COLLIDEANDDIECOMPONENT_H__
#define __COMPONENTS_COLLIDEANDDIECOMPONENT_H__

#include 
#include 

#include 
#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_COLLIDEANDDIE CRCD(0x6259b52b,"CollideAndDie")

//  Standard accessor macros for getting the component either from within an object, or 
//  given an object				 
#define		GetCollideAndDieComponent() ((Obj::CCollideAndDieComponent*)GetComponent(CRC_COLLIDE_AND_DIE))
#define		GetCollideAndDieComponentFromObject(pObj) ((Obj::CCollideAndDieComponent*)(pObj)->GetComponent(CRC_COLLIDE_AND_DIE))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CCollideAndDieComponent : public CBaseComponent
{
public:
    CCollideAndDieComponent();
    virtual ~CCollideAndDieComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void					Hide( bool should_hide );
	virtual void					Finalize();
		
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

	void							SetCollisionRadius( float radius );

private:
	CSuspendComponent*	mp_suspend_component;

	float				m_radius;	// Radius for spherical collision detection
	float				m_scale;
	Tmr::Time			m_birth_time;
	uint32				m_death_script;
	Tmr::Time			m_death_time;
	bool				m_dying;
	bool				m_first_frame;
	Mth::Vector			m_vel;
	Mth::Vector			m_last_pos;
};

}

#endif


================================================
FILE: Code/Gel/Components/FloatingLabelComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       FloatingLabelComponent.h
//* OWNER:			Dan
//* CREATION DATE:  3/13/3
//****************************************************************************

#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent* CFloatingLabelComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CFloatingLabelComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CFloatingLabelComponent::CFloatingLabelComponent() : CBaseComponent()
{
	SetType( CRC_FLOATINGLABEL );
	
	strcpy(m_string, "Unset Label");
	m_color_index = 2;
	m_y_offset = 10.0f * 12.0f;
	m_screen_element_id = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CFloatingLabelComponent::~CFloatingLabelComponent()
{
	Script::CStruct* pParams = pParams = new Script::CStruct;
	pParams->AddChecksum(CRCD(0x40c698af, "id"), m_screen_element_id);
	
	Script::RunScript(CRCD(0x2575b406, "destroy_object_label"), pParams);
	
	delete pParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFloatingLabelComponent::InitFromStructure( Script::CStruct* pParams )
{
	const char* string;
	if (pParams->GetString(CRCD(0x61414d56, "string"), &string))
	{
		strncpy(m_string, string, 64);
	}
	pParams->GetInteger(CRCD(0x99a9b716, "color"), &m_color_index);
	pParams->GetFloat(CRCD(0x14975800, "y_offset"), &m_y_offset);
	pParams->GetChecksum(CRCD(0x727f9552, "screen_element_id"), &m_screen_element_id);
	
	Dbg_MsgAssert(m_screen_element_id, ("FloatingLabelComponent has bad screen_elemend_id"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFloatingLabelComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFloatingLabelComponent::Update()
{
	Script::CStruct* pParams = new Script::CStruct;
	pParams->AddChecksum(CRCD(0x40c698af, "id"), m_screen_element_id);
	
	char text[64];
	sprintf(text, "\\c%d%s", m_color_index, m_string);

	pParams->AddString(CRCD(0xc4745838, "text"), text);
	pParams->AddVector(CRCD(0x4b491900, "pos3D"), GetObject()->m_pos[X], GetObject()->m_pos[Y] + m_y_offset, GetObject()->m_pos[Z]);

	Front::CScreenElement *p_name_elem = Front::CScreenElementManager::Instance()->GetElement(m_screen_element_id);
	if (p_name_elem)
	{
		p_name_elem->SetProperties(pParams);
	}
	else
	{
		Script::RunScript(CRCD(0x6a060cf0, "create_object_label"), pParams);
	}
	
	delete pParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CFloatingLabelComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFloatingLabelComponent::GetDebugInfo ( Script::CStruct *p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CFloatingLabelComponent::GetDebugInfo"));
	
	p_info->AddString(CRCD(0x2bd21fa8, "m_string"), m_string);
	p_info->AddInteger(CRCD(0x6d83427f, "m_color_index"), m_color_index);
	p_info->AddFloat(CRCD(0x1bcdee78, "m_y_offset"), m_y_offset);
	p_info->AddChecksum(CRCD(0x5f6e2f93, "m_screen_element_id"), m_screen_element_id);
	
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}
	
}


================================================
FILE: Code/Gel/Components/FloatingLabelComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       FloatingLabelComponent.h
//* OWNER:			Dan
//* CREATION DATE:  3/13/3
//****************************************************************************

#ifndef __COMPONENTS_FLOATINGLABELCOMPONENT_H__
#define __COMPONENTS_FLOATINGLABELCOMPONENT_H__

#include 
#include 

#include 

#define		CRC_FLOATINGLABEL CRCD(0xeada1621, "FloatingLabel")

#define		GetFloatingLabelComponent() ((Obj::CFloatingLabelComponent*)GetComponent(CRC_FLOATINGLABEL))
#define		GetFloatingLabelComponentFromObject(pObj) ((Obj::CFloatingLabelComponent*)(pObj)->GetComponent(CRC_FLOATINGLABEL))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CFloatingLabelComponent : public CBaseComponent
{
public:
    CFloatingLabelComponent();
    virtual ~CFloatingLabelComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							SetColor ( int color_index ) { m_color_index = color_index; }
	void							SetString ( char* string ) { strncpy(m_string, string, 64); }
	
private:
	char							m_string[64];
	int								m_color_index;
	float							m_y_offset;
	uint32							m_screen_element_id;
};



}

#endif


================================================
FILE: Code/Gel/Components/GunslingerCameraLookAroundComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       GunslingerCameraLookAroundComponent.cpp
//* OWNER:          Dave
//* CREATION DATE:  5/13/03
//****************************************************************************

#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 

#include 
						   
float	spin_modulator			= 1.0f;
float	tilt_modulator			= 1.0f;
float	last_heading_change		= 0.0f;
float	last_tilt_change		= 0.0f;
bool	gun_fired				= false;
bool	allow_strafe_locking	= false;

float	screen_angle			= 72.0f;		// Test value for setting field of view.

float	lookaround_tilt_angular_speed		= 0.0f;
float	lookaround_heading_angular_speed	= 0.0f;



namespace Obj
{
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CCameraLookAroundComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CCameraLookAroundComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCameraLookAroundComponent::CCameraLookAroundComponent() : CBaseComponent()
{
	SetType( CRC_CAMERALOOKAROUND );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCameraLookAroundComponent::~CCameraLookAroundComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::Finalize (   )
{
	mp_input_component = GetInputComponentFromObject(GetObject());
	
	Dbg_Assert(mp_input_component);

	mLookaroundTilt					= 0.0f;
	mLookaroundHeading				= 0.0f;
	mLookaroundZoom					= 1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCameraLookAroundComponent::Update()
{
	CControlPad * p_control_pad;
	
	if (Script::GetInt(CRCD(0x702247a5,"Viewer_controller"), false))
	{
		p_control_pad = &mp_input_component->GetControlPad2();
	}
	else
	{
		p_control_pad = &mp_input_component->GetControlPad();
	}

	if( p_control_pad->m_up.GetTriggered())
	{
		p_control_pad->m_up.ClearTrigger();

		// Unfortunately, pressing up on the left control stick will also cause m_up to be triggered, so check this
		// is really the d-pad press.
		if( p_control_pad->GetScaledLeftAnalogStickMagnitude() == 0.0f )
		{
			// This would be a good place to put the field of view setting code for the sniper rifle.
			if( screen_angle == 72.0f )
			{
				screen_angle = 25.0f;
			}
			else if( screen_angle == 25.0f )
			{
				screen_angle = 7.5f;
			}
			else if( screen_angle == 7.5f )
			{
				screen_angle = 72.0f;
			}
			Nx::CViewportManager::sSetScreenAngle( screen_angle );
		}
	}
	
	float frame_length = Tmr::FrameLength();
	
	// Strafe locking is allowed by default.
	allow_strafe_locking = true;

	// Shouldn't really do this here. Disable strafe locking if not strafing, or if moving forward or backwards.
	float fb = p_control_pad->m_scaled_leftY;
	float lr = p_control_pad->m_scaled_leftX;

	if( Mth::Abs( fb ) > 0.0f )
	{
		allow_strafe_locking = false;
	}

	if( Mth::Abs( lr ) < 0.2f )
	{
		allow_strafe_locking = false;
	}

	if (!mLookaroundLock && !mLookaroundOverride)
	{
		float target = p_control_pad->m_scaled_rightY;

		// Modulate with the variable used to damp cursor movement when aiming at a target.
		target = target * tilt_modulator;

#		if 1
//		if( target != 0.0f )
		{
			allow_strafe_locking = false;

			if( Script::GetInteger( CRCD( 0x9edfc7af, "GunslingerInvertAiming" )) == 0 )
			{
				// Negate value if vertical aiming invert is not enabled.
				target = -target;
			}

			// Get script values.
			float tilt_ka = Script::GetFloat( CRCD( 0xcac0c1d4, "GunslingerLookaroundTiltKa" ), Script::ASSERT );
			float tilt_ea = Script::GetFloat( CRCD( 0x5443ec5a, "GunslingerLookaroundTiltEa" ), Script::ASSERT );

			float tilt_ks = Script::GetFloat( CRCD( 0x3979b09c, "GunslingerLookaroundTiltKs" ), Script::ASSERT );
			float tilt_es = Script::GetFloat( CRCD( 0xa7fa9d12, "GunslingerLookaroundTiltEs" ), Script::ASSERT );

			// Calculate acceleration.
			float a = tilt_ka * powf( Mth::Abs( target ), tilt_ea ) * (( target > 0.0f ) ? 1.0f : ( target < 0.0f ) ? -1.0f : 0.0f );

			// Calculate max speed.
			float s = tilt_ks * powf( Mth::Abs( target ), tilt_es ) * (( target > 0.0f ) ? 1.0f : ( target < 0.0f ) ? -1.0f : 0.0f );

			lookaround_tilt_angular_speed += a;

			if( s == 0.0f )
			{
				lookaround_tilt_angular_speed = 0.0f;
			}
			else if((( s > 0.0f ) && ( lookaround_tilt_angular_speed > s )) || (( s < 0.0f ) && ( lookaround_tilt_angular_speed < s )))
			{
					lookaround_tilt_angular_speed = s;
			}

			float lookaround_tilt_speed = Script::GetFloat( CRCD( 0x67796648, "GunslingerLookaroundTiltSpeed" ), Script::ASSERT );
			mLookaroundTilt += lookaround_tilt_angular_speed * lookaround_tilt_speed * frame_length;

			last_tilt_change = lookaround_tilt_angular_speed * lookaround_tilt_speed * frame_length;

			if( mLookaroundTilt > 1.1f )
				mLookaroundTilt = 1.1f;
			else if( mLookaroundTilt < -0.85f )
				mLookaroundTilt = -0.85f;
		}
#		else
		// This calculation is different for Gunslinger, since we want to be able to look up higher.
//		target = -1.4f * (target < 0.0f ? (0.7f * target) : (0.4f * target));
		target = -1.4f * ( target < 0.0f ? ( 0.7f * target ) : ( 0.7f * target ));

		if( Mth::Abs( target - mLookaroundTilt ) > 0.001f )
		{
			// Ditto for this calc.
//			mLookaroundTilt += (target - mLookaroundTilt) * 3.75f * frame_length;
			mLookaroundTilt += (target - mLookaroundTilt) * 6.0f * frame_length;
		}
		else
		{
			mLookaroundTilt = target;
		}
#		endif
		
#		if 1
		target = p_control_pad->m_scaled_rightX;

		// Modulate with the variable used to damp cursor movement when aiming at a target.
		target = target * spin_modulator;
		
//		if( target != 0.0f )
		{
			allow_strafe_locking = false;

			// Get script values.
			float heading_ka = Script::GetFloat( CRCD( 0x6fd803d9, "GunslingerLookaroundHeadingKa" ), Script::ASSERT );
			float heading_ea = Script::GetFloat( CRCD( 0xf15b2e57, "GunslingerLookaroundHeadingEa" ), Script::ASSERT );

			float heading_ks = Script::GetFloat( CRCD( 0x9c617291, "GunslingerLookaroundHeadingKs" ), Script::ASSERT );
			float heading_es = Script::GetFloat( CRCD( 0x2e25f1f, "GunslingerLookaroundHeadingEs" ), Script::ASSERT );

			// Calculate acceleration.
			float a = heading_ka * powf( Mth::Abs( target ), heading_ea ) * (( target > 0.0f ) ? 1.0f : ( target < 0.0f ) ? -1.0f : 0.0f );

			// Calculate max speed.
			float s = heading_ks * powf( Mth::Abs( target ), heading_es ) * (( target > 0.0f ) ? 1.0f : ( target < 0.0f ) ? -1.0f : 0.0f );

			lookaround_heading_angular_speed += a;

			if( s == 0.0f )
			{
				lookaround_heading_angular_speed = 0.0f;
			}
			else if((( s > 0.0f ) && ( lookaround_heading_angular_speed > s )) || (( s < 0.0f ) && ( lookaround_heading_angular_speed < s )))
			{
					lookaround_heading_angular_speed = s;
			}

			float lookaround_heading_speed = Script::GetFloat( CRCD( 0x8501d824, "GunslingerLookaroundHeadingSpeed" ), Script::ASSERT );

			// Control stick left - reticle should move left.
			mLookaroundHeading -= lookaround_heading_angular_speed * lookaround_heading_speed * frame_length;

			last_heading_change	= lookaround_heading_angular_speed * lookaround_heading_speed * frame_length;

			if( mLookaroundHeading > ( 2 * Mth::PI ))
				mLookaroundHeading -= ( 2 * Mth::PI );
			else if( mLookaroundHeading < 0.0f )
				mLookaroundHeading += 2 * Mth::PI;
		}
#		else
		target = 3.0f * p_control_pad->m_scaled_rightX;
		if (Mth::Abs(target - mLookaroundHeading) > 0.001f)
		{
			mLookaroundHeading += (target - mLookaroundHeading) * 3.75f * frame_length;
		}
		else
		{
			mLookaroundHeading = target;
		}
#		endif
	}
	
	float delta_time = Tmr::FrameLength();

	if (mLookaroundOverride)
	{
		if (mLookaroundDeltaTimer > 0.0f)
		{
			if (mLookaroundDeltaTimer > delta_time)
			{
				mLookaroundHeading		+= mLookaroundHeadingDelta * delta_time;
				mLookaroundTilt			+= mLookaroundTiltDelta * delta_time;
				mLookaroundDeltaTimer	-= delta_time;
			}
			else
			{
				mLookaroundHeading		= mLookaroundHeadingTarget;
				mLookaroundTilt			= mLookaroundTiltTarget;
				mLookaroundDeltaTimer	= 0.0f;
			}
		}
	}

	// Total hack to simulate shooting for now.
	gun_fired = false;
	if( p_control_pad->m_R1.GetTriggered())
	{
		gun_fired = true;
		p_control_pad->m_R1.ClearTrigger();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CCameraLookAroundComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | SC_SetSkaterCamOverride | 
        // @parm float | heading |
        // @parm float | tilt | 
        // @parm float | time | 
        // @parm float | zoom | 
		case 0xb8f23899:	// SC_SetSkaterCamOverride
		{
			float heading, tilt, time;
			float zoom = 1.0f;
			pParams->GetFloat( CRCD(0xfd4bc03e,"heading"), &heading, Script::ASSERT );
			pParams->GetFloat( CRCD(0xe3c07609,"tilt"), &tilt, Script::ASSERT );
			pParams->GetFloat( CRCD(0x906b67ba,"time"), &time, Script::ASSERT );
			pParams->GetFloat( CRCD(0x48d4868b,"zoom"), &zoom );

			SetLookaround( heading, tilt, time, zoom );
			
			break;
		}

        // @script | SC_ClearSkaterCamOverride |
		case 0xe3b327c0:	// SC_ClearSkaterCamOverride
			ClearLookaround();
			break;

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CCameraLookAroundComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCameraLookAroundComponent::SetLookaround( float heading, float tilt, float time, float zoom )
{
	mLookaroundOverride				= true;

	mLookaroundHeadingTarget		= heading;
	mLookaroundTiltTarget			= tilt;

	// Calculate delta - the amount to move in 1 second.
	if (time > 0.0f)
	{
		mLookaroundHeadingDelta		= (heading - mLookaroundHeading) / time;
		mLookaroundTiltDelta		= (tilt - mLookaroundTilt) / time;
	}

	mLookaroundDeltaTimer			= time;
	mLookaroundZoom					= zoom;
}

#if 0 // old control input checking code

char touched[256 * 256 / 8];
bool initialized = false;

void MapInput (   )
{
	if (!initialized)
	{
		for (int n = 255 * 255; n--; )
		{
			touched[n / 8] = 0;
		}
		initialized = true;
	}
	
	CControlPad& pad = mp_input_component->GetControlPad();
	
	int x = static_cast< int >(pad.m_rightX) + 128;
	int y = static_cast< int >(pad.m_rightY) + 128;
	
	// printf("x = %i; y = %i\n", x, y);
	
	int index = (x * 256) + y;
	
	// printf("index / 8 = %i\n", index / 8);
	// printf("MAX = %i\n", 256 * 256 / 8);
	
	touched[index / 8] |= (1 << (index % 8));
}


void DrawMap (   )
{
	Mth::Vector pos = GetObject()->GetPos();
	Mth::Matrix matrix = GetObject()->GetMatrix();
	
	Mth::Vector center = pos - 300.0f * matrix[Z];
	Mth::Vector up = matrix[Y];
	Mth::Vector left = -matrix[X];
	
	for (int n = 255 * 255; n--; )
	{
		if (!(touched[n / 8] & (1 << (n % 8)))) continue;
		
		int x = n / 256;
		int y = n % 256;
		
		Mth::Vector point = center + up * (y - 128) + left * (x - 128);
		
		Gfx::AddDebugLine(point + up + left, point - up - left, MAKE_RGB(255, 255, 255), 0, 1);
		Gfx::AddDebugLine(point + up - left, point - up + left, MAKE_RGB(255, 255, 255), 0, 1);
	}
}

#endif

}


================================================
FILE: Code/Gel/Components/GunslingerWalkCameraComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       GunslingerWalkCameraComponent.cpp
//* OWNER:          Dave
//* CREATION DATE:  5/13/03
//****************************************************************************

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 				// Messy, but for now used to obtain ped positions etc.
#include 
#include 
#include 
#include 
#include 

// These should be members in the cameralookaroundcomponent.
extern float			spin_modulator;
extern float			tilt_modulator;
extern float			last_heading_change;
extern float			last_tilt_change;
extern bool				gun_fired;
extern bool				allow_strafe_locking;

int						best_ped_timer			= 0;
Obj::CCompositeObject*	p_selected_target		= NULL;
float					target_selection_timer	= 0.0f;		// Times how long the selected target has been targeted for.

namespace Obj
{

	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static float s_get_gunslinger_param( uint32 checksum )
{
	Script::CStruct* p_cam_params = Script::GetStructure( CRCD( 0xa13ff9ae,"GunslingerCameraParameters" ));
	Dbg_Assert( p_cam_params );

	float param;
	p_cam_params->GetFloat( checksum, ¶m, Script::ASSERT );
	return param;
}

	
	
	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CWalkCameraComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CWalkCameraComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CWalkCameraComponent::CWalkCameraComponent() : CBaseComponent()
{
	SetType( CRC_WALKCAMERA );

	m_last_actual_matrix.Ident();
	m_last_tripod_pos.Set( 0.0f, 0.0f, 0.0f, 1.0f );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CWalkCameraComponent::~CWalkCameraComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::InitFromStructure( Script::CStruct* pParams )
{
	uint32 target_id = 0 ;
	pParams->GetChecksum("CameraTarget", &target_id, Script::ASSERT);
	
	CCompositeObject* p_target = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(target_id));
	Dbg_MsgAssert(p_target, ("Bad CameraTarget given to WalkCameraComponent"));
		
	set_target(p_target);
	
	m_last_dot = 1.0f;
	m_current_zoom = 1.0f;
	
	m_last_actual_matrix = GetObject()->GetMatrix();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::Finalize()
{
	mp_lookaround_component = GetCameraLookAroundComponentFromObject(GetObject());
	mp_camera_component = GetCameraComponentFromObject(GetObject());
	
	Dbg_Assert(mp_lookaround_component);
	Dbg_Assert(mp_camera_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::Update()
{
	// optimization KLUDGE
//	if (Script::GetInteger(CRCD(0x1aa88b08, "vehicle_mode")))
//	{
//		GetObject()->Pause(true);
//		return;
//	}
	
	if (mp_target->HasTeleported())
	{
		m_instant_count = 3;
	}

	bool instantly;
	if (m_instant_count > 0)
	{
		--m_instant_count;
		instantly = true;
	}
	else
	{
		instantly = false;
	}
	
	mp_camera_component->StoreOldPosition();

	float frame_length = Tmr::FrameLength();
	
	// get input
//	float horiz_control = GetInputComponentFromObject( GetObject())->GetControlPad().m_scaled_rightX;
	float horiz_control = mp_lookaround_component->mLookaroundHeading;

	// Restore camera position from last frame, previous to refocusing, lookaround and collision detection.
	GetObject()->GetMatrix() = m_last_actual_matrix;

	Mth::Vector	target_facing = -GetObject()->GetMatrix()[Z];
	target_facing[Y] = 0.0f;
	target_facing.Normalize();
	
	Mth::Vector subject_facing = mp_target->GetMatrix()[Z];
	subject_facing[Y] = 0.0f;
	subject_facing.Normalize();
	
	// two options; we either use our old facing as the target facing or use the subject's facing as the target facing
	bool use_subject_facing = true;
	
	// if in a flush request
	if (m_flush_request_active)
	{
		// always use the subject's matrix
	}
	// if controlling camera
	else if (horiz_control != 0.0f)
	{
		use_subject_facing = false;
	}
	// if the subject's facing is towards the camera
	else if (Mth::DotProduct(target_facing, subject_facing) < cosf(Mth::DegToRad(s_get_gunslinger_param(CRCD(0xb8da8a73, "lock_angle")))))
	{
		use_subject_facing = false;
	}
	// if the subject is moving very slowly
	else // if (!mp_target_walk_component->UseDPadCamera())
	{
		float full_slerp_speed;
		float min_slerp_speed;
		if (!mp_target_walk_component->UseDPadCamera())
		{
			full_slerp_speed = s_get_gunslinger_param(CRCD(0xbcef6dda, "full_slerp_speed"));
			min_slerp_speed = s_get_gunslinger_param(CRCD(0x824349e5, "min_slerp_speed"));
		}
		else
		{
			full_slerp_speed = s_get_gunslinger_param(CRCD(0x73da1ec0, "dpad_full_slerp_speed"));
			min_slerp_speed = s_get_gunslinger_param(CRCD(0xda5cad3, "dpad_min_slerp_speed"));
		}
		
		float target_vel = mp_target->GetVel().Length();
		if (target_vel < full_slerp_speed)
		{
			use_subject_facing = false;
		}
	}
	
	// Never want to use the subject facing camera.
	use_subject_facing = false;

	if (use_subject_facing)
	{
		target_facing = subject_facing;
	}
	
	// Build target matrix.
	Mth::Matrix target_matrix;
	target_matrix[W].Set( 0.0f, 0.0f, 0.0f, 1.0f );
	target_matrix[Z] = target_facing;
	target_matrix[Y].Set( 0.0f, 1.0f, 0.0f );
	target_matrix[X].Set( target_facing[Z], 0.0f, -target_facing[X] );

	// Dave note - testing default value for now.
	float slerp = 0.06f;

	target_matrix[X] = -target_matrix[X];
	target_matrix[Z] = -target_matrix[Z];

	// use later for camera position
	Mth::Vector up = mp_target->GetMatrix()[Y];
	
	if (!instantly)
	{
		if( Mth::DotProduct(target_matrix[X], GetObject()->GetMatrix()[X]) > CAMERA_SLERP_STOP &&
			Mth::DotProduct(target_matrix[Y], GetObject()->GetMatrix()[Y]) > CAMERA_SLERP_STOP &&
			Mth::DotProduct(target_matrix[Z], GetObject()->GetMatrix()[Z]) > CAMERA_SLERP_STOP )
		{
			// we're already at our target, so don't do anything
			
			// turn off any flush request
			if (m_flush_request_active)
			{
				m_flush_request_active = false;
				mp_target_walk_component->SetForwardControlLock(false);
			}
		}
		else
		{
			// Slerp to the target matrix.
			Mth::SlerpInterpolator slerper( &GetObject()->GetMatrix(), &target_matrix );
			
			// Apply the slerping.
			slerper.getMatrix( &GetObject()->GetMatrix(), GetTimeAdjustedSlerp( slerp, frame_length ));

			// Calculate for the skater camera.
			m_last_dot = Mth::DotProduct(m_last_actual_matrix[Z], GetObject()->GetMatrix()[Z]);
		}
	}
	else
	{
		GetObject()->GetMatrix() = target_matrix;
	}

	// At this point, GetObject()->GetMatrix() is valid to store.
	m_last_actual_matrix = GetObject()->GetMatrix();

	// Now apply the lookaround adjustments to the matrix.
	// Control over target facing.
	if( horiz_control != 0.0f && !m_flush_request_active )
	{
		// The horiz_control value needs to be damped when there is an item of interest within the reticle area.
		GetObject()->GetMatrix().RotateYLocal( horiz_control );
	}

	float tilt = s_get_gunslinger_param( CRCD( 0xe3c07609, "tilt" ));
	GetObject()->GetMatrix().RotateXLocal( tilt + mp_lookaround_component->mLookaroundTilt );

	Mth::Vector	camera_pos = get_tripod_pos( instantly );

	// Test the weapon component to generate the 'sticky' targetting behavior.
	if( mp_target && p_selected_target )
	{
		CWeaponComponent* p_weapon = GetWeaponComponentFromObject( mp_target );

		p_weapon->SetCurrentTarget( p_selected_target );
		p_weapon->SetSightPos( camera_pos );
		p_weapon->SetSightMatrix( GetObject()->GetMatrix());

		float extra_heading_change, extra_tilt_change;
		p_weapon->ProcessStickyTarget( last_heading_change, last_tilt_change, &extra_heading_change, &extra_tilt_change );

		if(( extra_heading_change != 0.0f ) || ( extra_tilt_change != 0.0f ))
		{
			// Reset the matrix to what it was prior to the heading and tilt adjustments.
			GetObject()->SetMatrix( m_last_actual_matrix );

			mp_lookaround_component->mLookaroundHeading += extra_heading_change;
			horiz_control += extra_heading_change;
			GetObject()->GetMatrix().RotateYLocal( horiz_control );

			mp_lookaround_component->mLookaroundTilt += extra_tilt_change;
			GetObject()->GetMatrix().RotateXLocal( tilt + mp_lookaround_component->mLookaroundTilt );
		}
	}

	// Calculate zoom
	float above, behind;
	calculate_zoom( above, behind );
	
	camera_pos += GetObject()->GetMatrix()[Z] * behind + up * above;
	
	Mth::Vector	focus_pos = mp_target_walk_component->GetEffectivePos() + up * above;
	
	// Focus the camera directly on the target object
	target_matrix[Z] = focus_pos - camera_pos;
	target_matrix[Z].Normalize();

	// Read back the Y from the current matrix.
//	target_matrix[Y] = GetObject()->GetMatrix()[Y];
	target_matrix[Y] = Mth::Vector( 0.0f, 1.0f, 0.0f );

	// Generate new orthonormal X and Y axes.
	target_matrix[X] = Mth::CrossProduct(target_matrix[Y], target_matrix[Z]);
	target_matrix[X].Normalize();

	target_matrix[Y] = Mth::CrossProduct(target_matrix[Z], target_matrix[X]);
	target_matrix[Y].Normalize();

	// Write back into camera matrix.
	// Since camera points in -Z, but player in +Z, we must negate the X and Z axes
	GetObject()->GetMatrix()[X]	= -target_matrix[X];
	GetObject()->GetMatrix()[Y] = target_matrix[Y];
	GetObject()->GetMatrix()[Z] = -target_matrix[Z];
	
	// clean up matrix
	GetObject()->GetMatrix()[X][W] = 0.0f;
	GetObject()->GetMatrix()[Y][W] = 0.0f;
	GetObject()->GetMatrix()[Z][W] = 0.0f;
	GetObject()->GetMatrix()[W].Set(0.0f, 0.0f, 0.0f, 1.0f);

	// Now do collision detection.
	ApplyCameraCollisionDetection( camera_pos, GetObject()->GetMatrix(), camera_pos - GetObject()->GetMatrix()[Z] * behind,	focus_pos );
	
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		Gfx::AddDebugStar(focus_pos, 24.0f, MAKE_RGB(255, 200, 0), 1);
	}
	#endif
	
	camera_pos[W] = 1.0f;
	GetObject()->SetPos( camera_pos );

	// Do the target selection.
	if( mp_target )
	{
		Mth::Vector reticle_min	= camera_pos;
		Mth::Vector reticle_max;

		CWeaponComponent* p_weapon = GetWeaponComponentFromObject( mp_target );

		p_weapon->SetSightMatrix( GetObject()->GetMatrix());
		p_selected_target = p_weapon->GetCurrentTarget( reticle_min, &reticle_max );

		if( gun_fired )
		{
			p_weapon->Fire();
		}

		spin_modulator = p_weapon->GetSpinModulator();
		tilt_modulator = p_weapon->GetTiltModulator();

		p_weapon->DrawReticle();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CWalkCameraComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | WalkCamera_FlushRequest | Force the camera to lerp quickly to behind the walker
		case CRCC(0x73febd0f, "WalkCamera_FlushRequest"):
			FlushRequest();
			break;
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CWalkCameraComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::ReadyForActivation ( const SCameraState& state )
{
	m_last_tripod_pos = state.lastTripodPos;
	m_last_actual_matrix = state.lastActualMatrix;
	m_last_dot = state.lastDot;
	m_current_zoom = state.lastZoom;
	m_flush_request_active = false;
	
	mp_lookaround_component->mLookaroundTilt = 0.0f;
	mp_lookaround_component->mLookaroundHeading = 0.0f;
	mp_lookaround_component->mLookaroundZoom = 1.0f;
	mp_lookaround_component->mLookaroundLock = false;
	
	m_override_active = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::GetCameraState ( SCameraState& state )
{
	state.lastActualMatrix = m_last_actual_matrix;
	state.lastTripodPos = m_last_tripod_pos;
	state.lastDot = m_last_dot;
	state.lastZoom = m_current_zoom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::FlushRequest (   )
{
	m_flush_request_active = true;
	mp_target_walk_component->SetForwardControlLock(true);
	
	// flush requests zero skater cam lookaround
	mp_lookaround_component->mLookaroundHeading = 0.0f;
	mp_lookaround_component->mLookaroundTilt = 0.0f;
	mp_lookaround_component->mLookaroundZoom = 1.0f;
	mp_lookaround_component->mLookaroundLock = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::set_target ( CCompositeObject* p_target )
{
	mp_target = p_target;
	Dbg_Assert(mp_target);
	
	mp_target_walk_component = GetWalkComponentFromObject(mp_target);
	Dbg_Assert(mp_target_walk_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CWalkCameraComponent::get_tripod_pos( bool instantly )
{
	if (instantly)
	{
		m_last_tripod_pos = mp_target_walk_component->GetEffectivePos();
	}
	else
	{
		float lerp_xz = GetTimeAdjustedSlerp(s_get_gunslinger_param(CRCD(0xae47899b, "lerp_xz")), Tmr::FrameLength());
		float lerp_y = GetTimeAdjustedSlerp(s_get_gunslinger_param(CRCD(0xe1e4e104, "lerp_y")), Tmr::FrameLength());
		
		const Mth::Vector& target_pos = mp_target_walk_component->GetEffectivePos();

		m_last_tripod_pos.Set(
			Mth::Lerp(m_last_tripod_pos[X], target_pos[X], lerp_xz),
			Mth::Lerp(m_last_tripod_pos[Y], target_pos[Y], lerp_y),
			Mth::Lerp(m_last_tripod_pos[Z], target_pos[Z], lerp_xz)
		);
	}

	return m_last_tripod_pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::calculate_zoom ( float& above, float& behind )
{
	float target_zoom = 1.0f;

	// If lookaround override is set, factor in the lookaround override zoom.
	if (mp_lookaround_component->mLookaroundOverride && mp_lookaround_component->mLookaroundZoom != 1.0f)
	{
		target_zoom *= mp_lookaround_component->mLookaroundZoom;
	}
	
//	m_current_zoom += (( target_zoom - m_current_zoom ) * s_get_gunslinger_param( CRCD( 0x748743a7, "zoom_lerp" )));
	m_current_zoom += (( target_zoom - m_current_zoom ) * s_get_gunslinger_param( CRCD( 0x748743a7, "zoom_lerp" )));
	
//	behind = s_get_gunslinger_param(CRCD(0x52e6c41b, "behind")) * m_current_zoom;
	behind = s_get_gunslinger_param(CRCD(0x52e6c41b, "behind")) * m_current_zoom;

	// Behind is also shortened when the lookaround camera is tilting upwards.
	if( mp_lookaround_component->mLookaroundTilt < 0.0f )
	{
		float max_tilt = -0.9f;
//		behind = behind * (0.4f + (0.6f * ((max_tilt + mp_lookaround_component->mLookaroundTilt) / max_tilt)));
		behind = behind * ( 0.0f + ( 1.0f * (( max_tilt - mp_lookaround_component->mLookaroundTilt ) / max_tilt )));
	}
	else if( mp_lookaround_component->mLookaroundTilt > 0.0f )
	{
		float max_tilt = 1.4f;
		behind = behind * ( 0.0f + ( 1.0f * (( max_tilt - mp_lookaround_component->mLookaroundTilt ) / max_tilt )));
	}

	// Use lip_trick_above when doing a lip trick.
//	float above_val = s_get_gunslinger_param(CRCD(0xb96ae2d, "above"));
	float above_val = s_get_gunslinger_param( CRCD( 0xb96ae2d, "above" ));
	
	// Figure above tending towards the perfect above, if zoom is < 1.0.
	if( m_current_zoom < 1.0f )
	{
		above = SKATERCAMERACOMPONENT_PERFECT_ABOVE + ((above_val - SKATERCAMERACOMPONENT_PERFECT_ABOVE) * m_current_zoom);
	}
	else
	{
		above = above_val;
	}
}

}


================================================
FILE: Code/Gel/Components/GunslingerWalkComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       WalkComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  4/2/3
//****************************************************************************

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 


/*
 * - Take out manual, take out manual, take out manual.
 * - Switch over to no-origin-teleport animations.
 * - Catching on curbs right after jump (use snap up code from skater)
 * - Moving contacts (and sticking to moving rails).
 * - Control Feel
 *    - Perhaps X equals run only during run outs.
 * - Camera
 *    - Flush requests don't quite complete using the timer method.  Perhaps with forward control locking, the goal method may once again be useful.
 *    - Transition out of vert facing camera.  Perhaps flush camera at first landing after an air transition.
 *    - Rewrite camera with "forward locking" affecting target matrix instead of turning off lerping.  Lookaround when walking shouldn't reset to behind
 *      during walking.
 * - Shadow update.
 * - Height update.
 * - Enter/exit walk behavior (always rotates to along slope, run out to walk behavior still looks odd).
 * - Turn off stance panel nollie.
 * - Step up/down (use feeler to look ahead).  Delay WalkOffEdge animation change.
 * - Animations:
 *    - Pull-up-from-hang has no feet!
 *    - Trot walking animation.
 *    - Drop-to-hang animation.
 *    - Hop-to-hang animation.
 *    - Three walk/run-to-stand animations.
 *    - Rotate walk/run animation.
 *    - Two ladder idle animations.
 *    - Onto-ladder-top animation. 
 *    - Off-ladder-bottom animation.
 *    - Ladder-to-hang/hang-to-ladder animations.
 *    - idle skater to stand
 *    - land to walk issues
 *    - special pull-up-to-wire anim
 *    - off-ladder-bottom anim to allow blending at anim start / teleport at anim end
 *    - onto-ladder-top anim to allow blending at anim start / teleport at anim end
 *    - grab-to-hang swing and wall anims suck
 *    - grab-to-hang sideways swing
 *    - Onto-ladder-top animation.  Currently play Off-ladder-top backwards.
 *    - Fall-air before jump-air anim.
 * - Animation comments:
 *    - running land doesn't really work (no weight), especially when you land and then immediately run off an edge and then land again
 *    - full run is odd looking
 * - Walk to stand animation will require at least three versions.
 * - Hang
 *    - Rail-to-rail ledge transition issues.
 *    - Jerky rail corners.
 *    - Non horizontal hang movement animations.
 *    - Camera pop during pull-up (caused by camera collision).
 *    - Jump off hang.
 * - Ladder
 *    - Jump off ladder.
 *    - Jump onto ladder.
 *    - You can grind ladders.
 * - Grab rail in air.
 * - Little talkie boxes don't always work.
 * - Gaps still trigger, but based on skate state.  Maybe run gap component while walking and include walking gap flag.
 * - How can we deal with a low snap-up height, yet allow for steep slope walking?
 * - Grind transition animation in Grind script (check to see if its playing).
 * - Clean 1/3 second delay before X running.
 * - Upper body interpenetrates stuff.
 * - Hang from bottom of ladder.
 * - Pull up collision restrictions are too restrictive (use push feeler distance?).
 * - Make it easier to grab a rail when you walk off an edge.
 * - Ladder to rail across top.  Rail across top to ladder.
 * - Hand-plants, drop-ins, etc.
 * BUGS:
 * - Snap to hangs can cause the origin to go to bad places?  NJ by your roof.
 * - Fall through verts with low frame rate.
 * - Pop in transition from last to first frames of slow cycling walk animations.
 */

namespace Obj
{
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static float s_get_gunslinger_param( uint32 checksum )
{
	Script::CStruct* p_walk_params = Script::GetStructure( CRCD( 0x1c33e162, "GunslingerWalkParameters" ));
	Dbg_Assert(p_walk_params);
	
	float param;
	p_walk_params->GetFloat(checksum, ¶m, Script::ASSERT);
	return param;
}

	
	
	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CWalkComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CWalkComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CWalkComponent::CWalkComponent() : CBaseComponent()
{
	SetType( CRC_WALK );
	
	mp_collision_cache = Nx::CCollCacheManager::sCreateCollCache();
	
	mp_input_component = NULL;
	mp_animation_component = NULL;
	mp_movable_contact_component = NULL;

	m_facing.Set( 0.0f, 0.0f, 0.0f, 0.0f );
	m_control_direction.Set( 0.0f, 0.0f, 0.0f, 0.0f );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CWalkComponent::~CWalkComponent()
{
	Nx::CCollCacheManager::sDestroyCollCache(mp_collision_cache);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::Finalize()
{
	mp_input_component = GetInputComponentFromObject(GetObject());
	mp_animation_component = GetAnimationComponentFromObject(GetObject());
	mp_model_component = GetModelComponentFromObject(GetObject());
	mp_trigger_component = GetTriggerComponentFromObject(GetObject());
	mp_physics_control_component = GetSkaterPhysicsControlComponentFromObject(GetObject());
	mp_movable_contact_component = GetMovableContactComponentFromObject(GetObject());
	
	Dbg_Assert(mp_input_component);
	Dbg_Assert(mp_animation_component);
	Dbg_Assert(mp_model_component);
	Dbg_Assert(mp_trigger_component);
	Dbg_Assert(mp_physics_control_component);
	Dbg_Assert(mp_movable_contact_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::InitFromStructure( Script::CStruct* pParams )
{
	uint32 camera_id;
	if (pParams->GetChecksum(CRCD(0xc4e311fa, "camera"), &camera_id))
	{
		CCompositeObject* p_camera = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(camera_id));
		Dbg_MsgAssert(mp_camera, ("No such camera object"));
		SetAssociatedCamera(p_camera);
	}
}
		 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::Update()
{
	// TEMP: debounce R1 after a transition
	if (m_ignore_grab_button && !mp_input_component->GetControlPad().m_R1.GetPressed())
	{
		m_ignore_grab_button = false;
	}

	// zero the frame event
	m_last_frame_event = m_frame_event;
	m_frame_event = 0;
	
	// get input
    get_controller_input();
	
	// extract initial state for this frame from the object
	
	m_frame_start_pos = m_pos = GetObject()->GetPos();
	
	m_horizontal_vel = GetObject()->GetVel();
	m_horizontal_vel[Y] = 0.0f;
	m_vertical_vel = GetObject()->GetVel()[Y];
	
	// note that m_facing and m_upward will often not be orthogonal, but will always span a plan
	
	// generally straight up, but now after a transition from skating
	m_upward = GetObject()->GetMatrix()[Y];
	
	m_facing = GetObject()->GetMatrix()[Z];
	m_facing[Y] = 0.0f;
	float length = m_facing.Length();
	if (length < 0.001f)
	{
		// upward facing orientation matrix
		m_facing = -GetObject()->GetMatrix()[Y];
		m_facing[Y] = 0.0f;
		m_facing.Normalize();
		
		// since m_upward is now in the same plan as m_facing, push m_upward up a touch
		m_upward[Y] += 0.01f;
		m_upward.Normalize();
	}
	else
	{
		m_facing /= length;
	}
	
	// set the frame length
	m_frame_length = Tmr::FrameLength();
	
	// go to our true Y position
	m_curb_float_height_adjusted = false;
	m_pos[Y] -= m_curb_float_height;
	
	// switch logic based on walking state
	switch (m_state)
	{
		case WALKING_GROUND:
			go_on_ground_state();
			break;

		case WALKING_AIR:
			go_in_air_state();
			break;
																	  
//		case WALKING_HOP:
//			go_hop_state();
//			break;
																	  
		case WALKING_HANG:
			go_hang_state();
			break;
            
		case WALKING_LADDER:
			go_ladder_state();
            break;
			
		case WALKING_ANIMWAIT:
			go_anim_wait_state (   );
			break;
	}
	
	// the there's no curb to adjust due to, lerp down to zero
	if (!m_curb_float_height_adjusted)
	{
		m_curb_float_height = Mth::Lerp(m_curb_float_height, 0.0f, s_get_gunslinger_param(CRCD(0x9b3388fa, "curb_float_lerp_down_rate")) * m_frame_length);
	}
	
	// adjust back to our curb float Y position
	m_pos[Y] += m_curb_float_height;
	
	// scripts may have restarted us / switched us to skating
//	if (should_bail_from_frame()) return;
	
	// keep the object from falling through holes in the geometry
	if (m_state == WALKING_GROUND || m_state == WALKING_AIR)
	{
		uber_frig();
	}
	
	// rotate to upright
	lerp_upright();
	
	// setup the object based on this frame's walking
	copy_state_into_object();
	
	Dbg_Assert(m_frame_event);
	GetObject()->SelfEvent(m_frame_event);
	
	// set the animation speeds
	switch (m_anim_scale_speed)
	{
		case RUNNING:
			if (m_anim_standard_speed > 0.0f)
			{
				mp_animation_component->SetAnimSpeed(m_anim_effective_speed / m_anim_standard_speed, false, false);
			}
			break;
			
		case HANGMOVE:
			mp_animation_component->SetAnimSpeed(m_anim_effective_speed / s_get_gunslinger_param(CRCD(0xd77ee881, "hang_move_speed")), false, false);
			break;
					
		case LADDERMOVE:
			mp_animation_component->SetAnimSpeed(m_anim_effective_speed / s_get_gunslinger_param(CRCD(0xab2db54, "ladder_move_speed")), false, false);
			break;
	
		default:
			break;
	}
	
	// camera controls
	// NOTE: script parameters
	switch (m_frame_event)
	{
		case CRCC(0xf41aba21, "Hop"):
			mp_camera_component->SetOverrides(m_facing, 0.05f);
			break;
		
		case CRCC(0x2d9815c3, "HangMoveLeft"):
		{
			Mth::Vector facing = m_facing;
			facing.RotateY(-0.95f);
			mp_camera_component->SetOverrides(facing, 0.05f);
			break;
		}
			
		case CRCC(0x279b1f0b, "HangMoveRight"):
		{
			Mth::Vector facing = m_facing;
			facing.RotateY(0.95f);
			mp_camera_component->SetOverrides(facing, 0.05f);
			break;
		}
					
		case CRCC(0x4194ecca, "Hang"):
			mp_camera_component->SetOverrides(m_facing, 0.05f);
			break;
		
		case CRCC(0xc84243da, "Ladder"):
		case CRCC(0xaf5abc82, "LadderMoveUp"):
		case CRCC(0xfec9dded, "LadderMoveDown"):
			mp_camera_component->SetOverrides(m_facing, 0.05f);
			break;
					
		case CRCC(0x4fe6069c, "AnimWait"):
			if (m_anim_wait_camera_mode == AWC_CURRENT)
			{
				mp_camera_component->SetOverrides(m_facing, 0.05f);
			}
			else
			{
				mp_camera_component->SetOverrides(m_drift_goal_facing, 0.05f);
			}
			break;
	
		default:
			mp_camera_component->UnsetOverrides();
			break;
	}
	
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		Gfx::AddDebugStar(GetObject()->GetPos(), 36.0f, MAKE_RGB(255, 255, 255), 1);
	}
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CWalkComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | Walk_Ground |
		case CRCC(0x893213e5, "Walk_Ground"):
			return m_state == WALKING_GROUND ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Walk_Air |
		case CRCC(0x5012082e, "Walk_Air"):
			return m_state == WALKING_AIR ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Walk_Hang |
		case CRCC(0x9a3ca853, "Walk_Hang"):
			return m_state == WALKING_HANG ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Walk_Ladder |
		case CRCC(0x19702ca8, "Walk_Ladder"):
			return m_state == WALKING_LADDER ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Walk_GetStateTime | Loads the time in milliseconds since last state change.
		case CRCC(0xce64576c, "Walk_GetStateTime"):
			pScript->GetParams()->AddInteger(CRCD(0x5ab23cc9, "StateTime"), Tmr::ElapsedTime(m_state_timestamp));
			break;
		
		// @script | Walk_Jump |
		case CRCC(0x83e4bd70, "Walk_Jump"):
		{
			// jump strength scales with the length the jump button has been held
			jump();
//			jump(Mth::Lerp(
//				s_get_gunslinger_param(CRCD(0x246d0bf3, "min_jump_factor")), 
//				1.0f,
//				Mth::ClampMax(mp_input_component->GetControlPad().m_x.GetPressedTime() / s_get_gunslinger_param(CRCD(0x12333ebd, "hold_time_for_max_jump")), 1.0f)
//			));
			break;
		}
		
//		case CRCC(0xeb0d763b, "Walk_HangJump"):
//		{
//			// jump strength scales with the length the jump button has been held
//			jump(s_get_gunslinger_param(CRCD(0xf2fa5845, "hang_jump_factor")), true);
//			break;
//		}
		
		// @script | Walk_SetDragFactor |
		case CRCC(0xc6100a7d, "Walk_SetDragFactor"):
			break;
			
		case CRCC(0x4e4fae43, "Walk_ResetDragFactor"):
			break;
			
		case CRCC(0xaf04b983, "Walk_GetSpeedScale"):
		{
			uint32 checksum;
			if (m_anim_effective_speed < s_get_gunslinger_param(CRCD(0xf3649996, "max_slow_walk_speed")))
			{
				checksum = CRCD(0x1150cabb, "WALK_SLOW");
			}
			else if (m_anim_effective_speed < s_get_gunslinger_param(CRCD(0x6a5805d8, "max_fast_walk_speed")))
			{
				checksum = CRCD(0x131f2a2, "WALK_FAST");
			}
			else if (m_anim_effective_speed < s_get_gunslinger_param(CRCD(0x1c94cc9c, "max_slow_run_speed")))
			{
				checksum = CRCD(0x5606d106, "RUN_SLOW");
			}
			else
			{
				checksum = CRCD(0x4667e91f, "RUN_FAST");
			}
			pScript->GetParams()->AddChecksum(CRCD(0x92c388f, "SpeedScale"), checksum);
			
			break;
		}
		
		// @script | Walk_ScaleAnimSpeed | Sets the manner in which the walk animations speeds should be scaled.
		// @flag Off | No animation speed scaling.
		// @flag Run | Scale animation speeds against running speed.
		// @flag Walk | Scale animation speeds against walking speed.
		case CRCC(0x56112c03, "Walk_ScaleAnimSpeed"):
			if (pParams->ContainsFlag(CRCD(0xd443a2bc, "Off")))
			{
				if (m_anim_scale_speed != OFF)
				{
					m_anim_scale_speed = OFF;
					mp_animation_component->SetAnimSpeed(1.0f, false, true);
				}
			}
			else if (pParams->ContainsFlag(CRCD(0xaf895b3f, "Run")))
			{
				m_anim_scale_speed = RUNNING;
			}
			else if (pParams->ContainsFlag(CRCD(0x6384f1da, "HangMove")))
			{
				m_anim_scale_speed = HANGMOVE;
			}
			else if (pParams->ContainsFlag(CRCD(0xa2bfe505, "LadderMove")))
			{
				m_anim_scale_speed = LADDERMOVE;
			}
			else
			{
				Dbg_MsgAssert(false, ("Walk_ScaleAnimSpeed requires Off, Run, or Walk flag"));
			}
			
			pParams->GetFloat(CRCD(0xb2d59baf, "StandardSpeed"), &m_anim_standard_speed);
			break;
			
		// @script | Walk_AnimWaitComplete | Signal from script that the walk component should leave its animation wait
		case CRCC(0x9d3eebe8, "Walk_AnimWaitComplete"):
			anim_wait_complete();
			break;
				   			
		// @script | Walk_GetHangInitAnimType | Determine which type of initial hang animation should be played
		case CRCC(0xc6cd659e, "Walk_GetHangInitAnimType"):
			// m_initial_hang_animation is set when the hang rail is filtered
			pScript->GetParams()->AddChecksum(CRCD(0x85fa9ac4, "HangInitAnimType"), m_initial_hang_animation);
			break;

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CWalkComponent::GetDebugInfo"));
	
	switch (m_state)
	{
		case WALKING_GROUND:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x58007c97, "GROUND"));
			break;
		case WALKING_AIR:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x439f4704, "AIR"));
			break;
//		case WALKING_HOP:
//			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0xf41aba21, "HOP"));
//			break;										 
		case WALKING_HANG:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x4194ecca, "HANG"));
			break;										 
        case WALKING_LADDER:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0xc84243da, "LADDER"));
			break;										 
        case WALKING_ANIMWAIT:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x4fe6069c, "ANIMWAIT"));
			break;										 
	}

	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::SetAssociatedCamera ( CCompositeObject* camera_obj )
{
	mp_camera = camera_obj;
	Dbg_Assert(mp_camera);
	mp_camera_component = GetWalkCameraComponentFromObject(mp_camera);
	Dbg_MsgAssert(mp_camera_component, ("No WalkCameraComponent in camera object"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::ReadyWalkState ( bool to_ground_state )
{
	// setup the state in preparation for being in walking mode next object update
	MARK;
	
    // always reset the state timestamp
    m_state_timestamp = Tmr::GetTime();

	if (to_ground_state)
	{
		set_state(WALKING_GROUND);
		
		// will be incorrect for one frame
		m_ground_normal.Set(0.0f, 1.0f, 0.0);
		
		m_last_ground_feeler_valid = false;
		
		GetObject()->GetVel()[Y] = 0.0f;
	}
	else
	{
		set_state(WALKING_AIR);
		
		// set primary air direction in the direction of velocity
		m_primary_air_direction = GetObject()->GetVel();
		m_primary_air_direction[Y] = 0.0f;
		float length = m_primary_air_direction.Length();
		if (length < 0.001f)
		{
			// or facing
			m_primary_air_direction = GetObject()->GetMatrix()[Z];
			m_primary_air_direction[Y] = 0.0f;
			length = m_primary_air_direction.Length();
			if (length < 0.001f)
			{
				// or future facing
				m_primary_air_direction = -GetObject()->GetMatrix()[Y];
				m_primary_air_direction[Y] = 0.0f;
				length = m_primary_air_direction.Length();
			}
		}
		m_primary_air_direction /= length;
		
		leave_movable_contact_for_air(GetObject()->GetVel(), GetObject()->GetVel()[Y]);
	}

	m_curb_float_height = 0.0f;
	
	m_last_frame_event = 0;
	
	// TEMP: debounce R1 after a transition
	m_ignore_grab_button = true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::CleanUpWalkState (   )
{
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::go_on_ground_state( void )
{
	// Check for trying to mount a horse, triggered on triangle.
	if( mp_input_component->GetControlPad().m_triangle.GetTriggered())
	{
		mp_input_component->GetControlPad().m_triangle.ClearTrigger();

		// Get the control component.
		CSkaterPhysicsControlComponent*	p_control_component = GetSkaterPhysicsControlComponentFromObject( GetObject());
		p_control_component->CallMemberFunction( CRCD( 0x14c4f16b, "SkaterPhysicsControl_SwitchWalkingToRiding"), NULL, NULL );

		// Send the 'Ride' exception.
		m_frame_event = CRCD( 0x64c2832f, "Ride" );
		GetObject()->SelfEvent( m_frame_event );
		return;
	}

	account_for_movable_contact();
	
	setup_collision_cache();
	
	// calculate initial horizontal speed
	float horizontal_speed = m_horizontal_vel.Length();
	
	calculate_horizontal_speed_and_facing(horizontal_speed);
	
	// calculate this frame's movement
	m_horizontal_vel = horizontal_speed * m_facing;
	
	// prevent movement into walls
	if (adjust_horizonal_vel_for_environment())
	{
		// turn to face newly adjusted velocity
		adjust_facing_for_adjusted_horizontal_vel();
	}
	
	// if we are wall pushing, we may have decided to switch states during adjust_horizonal_vel_for_environment based on our environment
	if (m_state != WALKING_GROUND /*|| should_bail_from_frame()*/)
	{
		CFeeler::sClearDefaultCache();
		return;
	}
	
	// apply movement for this frame
	m_pos += m_horizontal_vel * m_frame_length;
	
	// snap up and down curbs and perhaps switch to air
	respond_to_ground();
	if (m_state != WALKING_GROUND /*|| should_bail_from_frame()*/)
	{
		CFeeler::sClearDefaultCache();
		return;
	}
	
	adjust_curb_float_height();
	
	// insure that we do not slip through the cracks in the collision geometry which are a side-effect of moving collidable objects
	if (CCompositeObject* p_inside_object = mp_movable_contact_component->CheckInsideObjects(m_pos, m_frame_start_pos))
	{
		MESSAGE("WALKING_GROUND, within moving object");
		
		// allow it to push us forward, causing a bit of a stumble
		m_horizontal_vel = p_inside_object->GetVel();
		m_horizontal_vel[Y] = 0.0f;
		m_vertical_vel = 0.0f;
		
		float speed_sqr = m_horizontal_vel.LengthSqr();
		if (speed_sqr > (10.0f * 10.0f))
		{
			m_facing = m_horizontal_vel * (1.0f / sqrtf(speed_sqr));
		}
	}
	
	CFeeler::sClearDefaultCache();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::CollideWithOtherSkaterLost ( CCompositeObject* p_other_skater )
{
	set_state(WALKING_AIR);
	m_primary_air_direction = m_facing;
	
	leave_movable_contact_for_air(GetObject()->GetVel(), GetObject()->GetVel()[Y]);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::calculate_horizontal_speed_and_facing ( float &horizontal_speed )
{
	// calculate user's desired speed
	float desired_speed = calculate_desired_speed();
			   	
	// setup frame's event
	if (desired_speed <= s_get_gunslinger_param(CRCD(0x74e8227d, "max_stand_speed")))
	{
		m_frame_event = CRCD(0x9b46e749, "Stand");
	}
	else
	{
		m_frame_event = CRCD(0xaf895b3f, "Run");
	}

	bool special_acceleration = false;
	
	// adjust facing based on input
	
	if( m_control_magnitude > 0.0f )
	{
		float dot = Mth::DotProduct( m_facing, m_control_direction );
		
		if (horizontal_speed < s_get_gunslinger_param(CRCD(0x52582d5b, "max_rotate_in_place_speed")) && dot < cosf(Mth::DegToRad(s_get_gunslinger_param(CRCD(0x5dff96a4, "max_rotate_in_place_angle")))))
		{
			// low speed rotate to desired orientation with no speed change
			
			float delta_angle = Mth::DegToRad(s_get_gunslinger_param(CRCD(0xb557804b, "rotate_in_place_rate"))) * m_control_magnitude * m_frame_length;
			bool left_turn = -m_facing[Z] * m_control_direction[X] + m_facing[X] * m_control_direction[Z] < 0.0f;
			
			if (!m_run_toggle)
			{
				delta_angle *= s_get_gunslinger_param(CRCD(0x7b446c98, "walk_rotate_factor"));
			}
			
			float cos_delta_angle = cosf(left_turn ? delta_angle : -delta_angle);
			float sin_delta_angle = sinf(left_turn ? delta_angle : -delta_angle);
			float adjusted_vel = cos_delta_angle * m_facing[X] + sin_delta_angle * m_facing[Z];
			m_facing[Z] = -sin_delta_angle * m_facing[X] + cos_delta_angle * m_facing[Z];
			m_facing[X] = adjusted_vel;
			
			// check for overturn
			if (left_turn != (-m_facing[Z] * m_control_direction[X] + m_facing[X] * m_control_direction[Z] < 0.0f))
			{
				m_facing = m_control_direction;
			}
			
			// no acceleration until we reach the desired orientation
			special_acceleration = true;
			
			// setup the event
			m_frame_event = left_turn ? CRCD(0xf28adbfc, "RotateLeft") : CRCD(0x912220f8, "RotateRight");
		}
		else
		{
			if (dot > -cosf(Mth::DegToRad(s_get_gunslinger_param(CRCD(0x2d571c0f, "max_reverse_angle")))))
			{
				// if the turn angle is soft
				
				// below a speed threshold, scale up the turn rate
				float turn_factor;
				if (horizontal_speed < s_get_gunslinger_param(CRCD(0x27815f69, "max_pop_speed")))
				{
					// quick turn
					turn_factor = Mth::Lerp(s_get_gunslinger_param(CRCD(0xb278405d, "best_turn_factor")), s_get_gunslinger_param(CRCD(0x6cb2e5de, "worse_turn_factor")), horizontal_speed / s_get_gunslinger_param(CRCD(0x27815f69, "max_pop_speed"))); 
				}
				else
				{
					// slower turn
					turn_factor = s_get_gunslinger_param(CRCD(0x6cb2e5de, "worse_turn_factor"));
				}
				turn_factor *= m_control_magnitude;
				
				// exponentially approach the new facing
				float turn_ratio = turn_factor * m_frame_length;
				if (turn_ratio >= 1.0f)
				{
					m_facing = m_control_direction;
				}
				else
				{
					m_facing = Mth::Lerp(m_facing, m_control_direction, turn_ratio);
					m_facing.Normalize();
				}
			}
			else
			{
				// the turn angle is hard
				
				if (horizontal_speed > s_get_gunslinger_param(CRCD(0xf1e97e45, "min_skid_speed")))
				{
					// if moving fast enough to require a skidding stop
					special_acceleration = true;
					horizontal_speed -= s_get_gunslinger_param(CRCD(0x9661ed7, "skid_accel")) * m_frame_length;
					horizontal_speed = Mth::ClampMin(horizontal_speed, 0.0f);
					m_frame_event = CRCD(0x1d537eff, "Skid");
				}
				else
				{
					// if max_rotate_in_place_speed is larger than min_skid_speed and max_reverse_angle points farther in reverse than
					// max_rotate_in_place_angle, as they should, then this code should never be run
					Dbg_Message("Unexpected state in CWalkComponent::calculate_horizontal_speed_and_facing");
					
					// to be safe, pop to the new facing
					m_facing = m_control_direction;
				}
			}
		}
	}
	
	if (special_acceleration) return;
	
	// store desired speed for animation speed scaling
	m_anim_effective_speed = desired_speed;
	
	// adjust desired speed for slope
	desired_speed = adjust_desired_speed_for_slope(desired_speed);
	
	// linear acceleration; exponential deceleration
	if (horizontal_speed > desired_speed)
	{
		horizontal_speed = Mth::Lerp(horizontal_speed, desired_speed, s_get_gunslinger_param(CRCD(0xacfa4e0c, "decel_factor")) * m_frame_length);
		
		if (desired_speed == 0.0f)
		{
			if (horizontal_speed > s_get_gunslinger_param(CRCD(0x79d182ad, "walk_speed")))
			{
				m_frame_event = CRCD(0x1d537eff, "Skid");
			}
			else if (m_last_frame_event == CRCD(0x1d537eff, "Skid") && horizontal_speed > s_get_gunslinger_param(CRCD(0x311d02b2, "stop_skidding_speed")))
			{
				m_frame_event = CRCD(0x1d537eff, "Skid");
			}
		}
	}
	else
	{
		if (m_run_toggle)
		{
			horizontal_speed += s_get_gunslinger_param(CRCD(0x4f47c998, "run_accel_rate")) * m_frame_length;
		}
		else
		{
			horizontal_speed += s_get_gunslinger_param(CRCD(0x6590a49b, "walk_accel_rate")) * m_frame_length;
		}
		if (horizontal_speed > desired_speed)
		{
			horizontal_speed = desired_speed;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool  CWalkComponent::adjust_horizonal_vel_for_environment ( void )
{
	// We send out feeler rays to find nearby walls.  We limit velocity to be flush with the first wall found.  If two or more non-parallel walls
	// are found, velocity is zeroed.
	
	float feeler_length = s_get_gunslinger_param(CRCD(0x99978d2b, "feeler_length"));
	float feeler_height = s_get_gunslinger_param(CRCD(0x6da7f696, "feeler_height"));
	
	CFeeler feeler;
	
	bool contact = false;
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		// setup the the feeler
		
		if (n == vNUM_FEELERS)
		{
			// final feeler is for air state only and is at the feet; solves situations in which the feet impact with vertical surfaces which the
			// wall feelers are too high to touch
			if (m_state != WALKING_AIR)
			{
				mp_contacts[vNUM_FEELERS].in_collision = false;
				continue;
			}
			
			feeler.m_start = m_pos;
			feeler.m_end = m_pos + m_horizontal_vel * m_frame_length;
			feeler.m_end[Y] += m_vertical_vel * m_frame_length + 0.5f * -s_get_gunslinger_param(CRCD(0xa5e2da58, "gravity")) * Mth::Sqr(m_frame_length);
		}
		else
		{
			feeler.m_start = m_pos;
			feeler.m_start[Y] += feeler_height;
			feeler.m_end = m_pos + feeler_length * calculate_feeler_offset_direction(n);
			feeler.m_end[Y] += feeler_height;
		}
		
		mp_contacts[n].in_collision = feeler.GetCollision();
		
		if (!mp_contacts[n].in_collision)
		{
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(0, 0, 255, 1);
			}
			#endif
			continue;
		}
		
		contact = true;
		
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(255, 0, 0, 1);
		}
		#endif

		// grab the horizontal normal of the contacted wall
		mp_contacts[n].normal = feeler.GetNormal();
		mp_contacts[n].normal[Y] = 0.0f;
		mp_contacts[n].normal.Normalize();
		
		// if we're on the moving object, don't count its movement when doing collision detection, as the walker's velocity is already measured
		// relative to its movable contact's
		if (feeler.IsMovableCollision()
			&& (!mp_movable_contact_component->HaveContact() || mp_movable_contact_component->GetContact()->GetObject() != feeler.GetMovingObject()))
		{
			mp_contacts[n].movement = Mth::DotProduct(feeler.GetMovingObject()->GetVel(), mp_contacts[n].normal);
		}
		else
		{
			mp_contacts[n].movement = 0.0f;
		}
	}
	
	// check for wall push
	if (m_state == WALKING_GROUND)
	{
		if (check_for_wall_push())
		{
			// if we're wall pushing, we may decide to switch states based on our environment
			
			if (Tmr::ElapsedTime(m_wall_push_test.test_start_time) > s_get_gunslinger_param(CRCD(0x928e6775, "hop_delay")))
			{
				if (maybe_climb_up_ladder() || /*maybe_hop_to_hang() ||*/ maybe_jump_low_barrier()) return false;
			}
			else if (Tmr::ElapsedTime(m_wall_push_test.test_start_time) > s_get_gunslinger_param(CRCD(0x38d36700, "barrier_jump_delay")))
			{
				if (maybe_climb_up_ladder() || maybe_jump_low_barrier()) return false;
			}
		}
		else if (mp_input_component->GetControlPad().m_R1.GetPressed() && !m_ignore_grab_button)
		{
			if (maybe_climb_up_ladder(true)) return false;
		}
	}
	
	if (!contact) return false;
	
	// push away from walls
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		if (!mp_contacts[n].in_collision) continue;
		                
		if (mp_contacts[n].feeler.GetDist() < s_get_param(CRCD(0xa20c43b7, "push_feeler_length")) / feeler_length)
		{
			m_pos += s_get_param(CRCD(0x4d16f37d, "push_strength")) * m_frame_length * mp_contacts[n].normal;
		}
	}

	// from here on we ignore collisions we're moving out of
	contact = false;
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		if (!mp_contacts[n].in_collision) continue;
		
		// don't count collisions we're moving out of
		if (Mth::DotProduct(mp_contacts[n].normal, m_horizontal_vel) >= mp_contacts[n].movement)
		{
			mp_contacts[n].in_collision = false;
		}
		else
		{
			contact = true;
		}
	}
	if (!contact) return false;
	
	// Now we calculate how our movement is effected by our collisions.  The movement must have a non-negative dot product with all collision normals.
	// The algorithm used should be valid for all convex environments.
	
	// if any of the colllision normals are more than right angles to one another, no movement is possible
	// NOTE: not valid with movable contacts; could cause jerky movement in corners where walls are movable
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		if (!mp_contacts[n].in_collision) continue;
		for (int m = n + 1; m < vNUM_FEELERS + 1; m++)
		{
			if (!mp_contacts[m].in_collision) continue;
			if (Mth::DotProduct(mp_contacts[n].normal, mp_contacts[m].normal) <= 0.0f)
			{
				m_horizontal_vel.Set();
				m_anim_effective_speed = Mth::Min(s_get_gunslinger_param(CRCD(0xbd6a05d, "min_anim_run_speed")), m_anim_effective_speed);
				return true;
			}
		}
	}
	
	// direction of proposed movement
	Mth::Vector movement_direction = m_horizontal_vel;
	movement_direction.Normalize();
	
	Mth::Vector adjusted_vel = m_horizontal_vel;
	
	// loop over the contacts (from backward to forward)
	const int contact_idxs[] = { 7, 4, 3, 5, 2, 6, 1, 0 };
	for (int i = 0; i < vNUM_FEELERS + 1; i++)
	{
		int n = contact_idxs[i];
		
		if (!mp_contacts[n].in_collision) continue;
		
		// check to see if the movement still violates this constraint
		float normal_vel = Mth::DotProduct(adjusted_vel, mp_contacts[n].normal);
		if (normal_vel >= mp_contacts[n].movement) continue;
		
		// adjust the movement to the closest direction allowed by this contraint
		adjusted_vel -= (normal_vel - mp_contacts[n].movement) * mp_contacts[n].normal;
		
		// if the mvoement direction no longer points in the direction of the proposed movement, no movement occurs
		if (Mth::DotProduct(adjusted_vel, m_horizontal_vel) <= 0.0f)
		{
			m_horizontal_vel.Set();
			m_anim_effective_speed = Mth::Min(s_get_gunslinger_param(CRCD(0xbd6a05d, "min_anim_run_speed")), m_anim_effective_speed);
			return true;
		}
	}
	
	// insure that the adjusted velocity in the final direction is not larger than the projection of the initial velocity into that direction
	float adjusted_speed = adjusted_vel.Length();
	Mth::Vector adjusted_vel_direction = adjusted_vel;
	adjusted_vel_direction *= 1.0f / adjusted_speed;
	float projected_vel = Mth::DotProduct(m_horizontal_vel, adjusted_vel_direction);
	
	if (adjusted_speed > projected_vel)
	{
		adjusted_vel = adjusted_vel_direction * projected_vel;
	}
	
	// only the velocity along the movement direction is retained
	m_horizontal_vel = adjusted_vel;
	
	float final_horiz_vel = m_horizontal_vel.Length();
	if (m_anim_effective_speed > s_get_gunslinger_param(CRCD(0xbd6a05d, "min_anim_run_speed")))
	{
		m_anim_effective_speed = final_horiz_vel;
		m_anim_effective_speed = Mth::Max(s_get_gunslinger_param(CRCD(0xbd6a05d, "min_anim_run_speed")), m_anim_effective_speed);
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::adjust_facing_for_adjusted_horizontal_vel (   )
{
	// We adjust facing due to adjustment in horizontal velocity due to environment.  Basically, we want to object to turn to face the velocity
	// that the environment has forced upon it.
	
	// IDEA: shift to basing turn amount on angle difference and not speed
	
	float horizontal_speed = m_horizontal_vel.Length();
	
	if (horizontal_speed < s_get_gunslinger_param(CRCD(0x515a933, "wall_turn_speed_threshold"))) return;
	
	// the new facing is in the direction of our adjusted velocity
	Mth::Vector new_facing = m_horizontal_vel;
	new_facing.Normalize();
	
	// smoothly transition between no wall turning to full wall turning
	float turn_ratio;
	if (horizontal_speed > s_get_gunslinger_param(CRCD(0xe6c1cd0d, "max_wall_turn_speed_threshold")))
	{
		turn_ratio = s_get_gunslinger_param(CRCD(0x7a583b9b, "wall_turn_factor")) * m_frame_length;
	}
	else
	{
		turn_ratio = Mth::LinearMap(
			0.0f,
			s_get_gunslinger_param(CRCD(0x7a583b9b, "wall_turn_factor")) * m_frame_length,
			horizontal_speed,
			s_get_gunslinger_param(CRCD(0x0515a933, "wall_turn_speed_threshold")),
			s_get_gunslinger_param(CRCD(0xe6c1cd0d, "max_wall_turn_speed_threshold"))
		);
	}
	
	// exponentially approach new facing
	if (turn_ratio >= 1.0f)
	{
		m_facing = new_facing;
	}
	else
	{
		m_facing = Mth::Lerp(m_facing, new_facing, turn_ratio);
		m_facing.Normalize();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CWalkComponent::adjust_desired_speed_for_slope ( float desired_speed )
{
	// Slow velocity up and down slopes.
	
	// skip if there is no appreciable slope
	if (m_ground_normal[Y] > 0.95f) return desired_speed;
	
	// skip if not running
	if (desired_speed <= s_get_gunslinger_param(CRCD(0x79d182ad, "walk_speed"))) return desired_speed;
	
	// calculate a horizontal vector up the slope
	Mth::Vector up_slope = m_ground_normal;
	up_slope[Y] = 0.0f;
	up_slope.Normalize();
	
	// horizontal factor of velocity if the velocity were pointing along the slope (instead of along the horizontal)
	float movement_factor = m_ground_normal[Y];
	
	// factor of velocity pointing up the slope
	float dot = Mth::Abs(Mth::DotProduct(m_facing, up_slope));
	
	// scale the up-the-slope element of velocity based on the slope strength
	return (1.0f - dot) * desired_speed + dot * movement_factor * desired_speed;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::respond_to_ground (   )
{
	// Look for the ground below us.  If we find it, snap to it.  If not, go to air state.
	
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_start[Y] += s_get_gunslinger_param(CRCD(0xcee3a3e1, "snap_up_height"));
	feeler.m_end = m_pos;
	feeler.m_end[Y] -= s_get_gunslinger_param(CRCD(0xaf3e4251, "snap_down_height"));
	
	if (!feeler.GetCollision())
	{
		// no ground
		
		if (m_last_ground_feeler_valid)
		{
			mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF_EDGE, m_last_ground_feeler);
//			if (should_bail_from_frame()) return;
		}
		
		if (mp_input_component->GetControlPad().m_triangle.GetPressed())
		{
			// need to give the player a change to rail before climbing down ladders and such
			
			// if we just climbed up to a rail and are immediately falling, we need some minute amount of movement in order to find a rail
			if (m_pos == m_frame_start_pos)
			{
				m_frame_start_pos[Y] += 0.001f;
			}
			
			if (maybe_stick_to_rail()) return;
		}
		
		if (maybe_climb_down_ladder() || maybe_drop_to_hang()) return;
		
		// go to air state
		set_state(WALKING_AIR);
		m_primary_air_direction = m_facing;
		leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
		m_frame_event = CRCD(0xabf1f6ac, "WalkOffEdge");
		return;
	}
	
	float snap_distance = feeler.GetPoint()[Y] - m_pos[Y];
	
	// no not send event for very small snaps
	if (Mth::Abs(snap_distance) > s_get_gunslinger_param(CRCD(0xd3193d8e, "max_unnoticed_ground_snap")))
	{
		GetObject()->SelfEvent(snap_distance > 0.0f ? CRCD(0x93fcf3ed, "SnapUpEdge") : CRCD(0x56e21153, "SnapDownEdge"));
	}
	
	// snap position to the ground
	m_pos[Y] = feeler.GetPoint()[Y];
	
	// adjust stair float distance
	m_curb_float_height = Mth::ClampMin(m_curb_float_height - snap_distance, 0.0f);
	
	// see if we've changed sectors
	if (m_last_ground_feeler.GetSector() != feeler.GetSector())
	{
		if (m_last_ground_feeler_valid)
		{
			mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF, m_last_ground_feeler);
//			if (should_bail_from_frame()) return;
		}
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_ONTO, feeler);
//		if (should_bail_from_frame()) return;
	}
	
	// stash the ground feeler so that we can trip the group's triggers at a later time
	m_last_ground_feeler = feeler;
	m_last_ground_feeler_valid = true;
	
	// set the ground normal for next frame's velocity slope adjustment
	m_ground_normal = feeler.GetNormal();
	
	// NOTE: need to repeat this code anywhere we enter the ground state
	mp_movable_contact_component->CheckForMovableContact(feeler);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::adjust_curb_float_height (   )
{
	// adjust m_curb_float_height to smooth out moving up stairs
	
	// When facing a curb, we smoothly increase m_curb_float_height to the height of the curb.  When we snap up the curb, m_curb_float_height is then
	// reduced by an amount equal to the snap distance.
	// When we snap down a curb, m_curb_float_height is increased by the snap distance.  We then drop m_curb_float_height smoothly to zero.
	
	// determine appropriate direction to search for a curb
	Mth::Vector feeler_direction = m_facing;
	feeler_direction.ProjectToPlane(m_ground_normal);
	feeler_direction[Y] = Mth::ClampMin(feeler_direction[Y], 0.0f);
	feeler_direction.Normalize();
	
	// look for a curb
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_start[Y] += 0.5f;
	feeler.m_end = m_pos + s_get_gunslinger_param(CRCD(0x11edcc52, "curb_float_feeler_length")) * feeler_direction;
    feeler.m_end[Y] += 0.5f;
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 255, 0, 1);
	}
	#endif
	
	if (feeler.GetCollision())
	{
		// grab the distance to the curb
		float distance_to_curb = feeler.GetDist();
		
		// look up from the curb to find the curb height
		feeler.m_end = feeler.GetPoint() + 0.5f * feeler_direction;
		feeler.m_start = feeler.m_end;
		feeler.m_start[Y] = m_pos[Y] + s_get_gunslinger_param(CRCD(0xcee3a3e1, "snap_up_height"));
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(0, 255, 255, 1);
		}
		#endif
		
		if (feeler.GetCollision())
		{
			// calculate the m_curb_float_height we should have based on the curb height and distance
			float appropriate_curb_float_height = (1.0f - distance_to_curb) * (feeler.GetPoint()[Y] - m_pos[Y]);
			
			if (Mth::Abs(m_curb_float_height) < 0.01f && m_control_magnitude == 0.0f && m_horizontal_vel.LengthSqr() < Mth::Sqr(s_get_gunslinger_param(CRCD(0x227d72ee, "min_curb_height_adjust_vel"))))
			{
				// don't update the curb height if we're on the ground and standing still; this is mostly to prevent snapping up right after landing a jump
			}
			else
			{
				// lerp to the appropriate height
				m_curb_float_height = Mth::Lerp(m_curb_float_height, appropriate_curb_float_height, s_get_gunslinger_param(CRCD(0x856a80d3, "curb_float_lerp_up_rate")) * m_frame_length);
			}
			
			m_curb_float_height_adjusted = true;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::account_for_movable_contact (   )
{
	if (!mp_movable_contact_component->UpdateContact(m_pos)) return;
	
	m_pos += mp_movable_contact_component->GetContact()->GetMovement();
	
	if (mp_movable_contact_component->GetContact()->IsRotated())
	{
		m_facing = mp_movable_contact_component->GetContact()->GetRotation().Rotate(m_facing);
		if (m_facing[Y] != 0.0f)
		{
			DUMPF(m_facing[Y]);
			m_facing[Y] = 0.0f;
			m_facing.Normalize();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::jump (   )
{
	// switch to air state and give the object an upwards velocity
	
	float strength = 0.0f;
	switch (m_state)
	{
		case WALKING_GROUND:
		case WALKING_AIR:
			// jump strength scales with the length the jump button has been held
			strength = Mth::Lerp(
				s_get_param(CRCD(0x246d0bf3, "min_jump_factor")), 
				1.0f,
				Mth::ClampMax(mp_input_component->GetControlPad().m_x.GetPressedTime() / s_get_param(CRCD(0x12333ebd, "hold_time_for_max_jump")), 1.0f)
			);
			break;
			
		case WALKING_HANG:
		case WALKING_LADDER:
		case WALKING_ANIMWAIT:
			strength = s_get_param(CRCD(0xf2fa5845, "hang_jump_factor"));
			break;
	}
	
	// if we're jumping from the ground, trip the ground's triggers
	if (m_state == WALKING_GROUND && m_last_ground_feeler_valid)
	{
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, m_last_ground_feeler);
		if (mp_physics_control_component->HaveBeenReset()) return;
	}
	
	// Called by script from outside of the component update, so m_vertical_vel is not used.
	GetObject()->GetVel()[Y] = strength * s_get_param(CRCD(0x63d62a21, "jump_velocity"));
	
	// jumps for ladders and hanging get a backwards velocity
	switch (m_state)
	{
		case WALKING_GROUND:
		case WALKING_AIR:
		{
//			extract_state_from_object();
	
			if (m_control_magnitude)
			{
				float min_launch_speed = calculate_desired_speed()
					* s_get_param(CRCD(0x839fe542, "jump_horiz_speed")) / get_run_speed();
				
				if (Mth::DotProduct(m_horizontal_vel, m_control_direction) > 0.0f)
				{
					m_horizontal_vel.ProjectToNormal(m_control_direction);
					if (m_horizontal_vel.Length() < min_launch_speed)
					{
						m_horizontal_vel.Normalize(min_launch_speed);
					}
				}
				else
				{
					m_horizontal_vel = min_launch_speed * m_control_direction;
				}
				
				m_primary_air_direction = m_control_direction;
				m_facing = m_control_direction;
			}
			else
			{
				m_primary_air_direction = m_facing;
			}
			
//			adjust_jump_for_ceiling_obstructions();
			
			// setup primary air direction
			float length_sqr = m_horizontal_vel.LengthSqr();
			if (length_sqr > 0.01f)
			{
				m_primary_air_direction = m_horizontal_vel;
				m_primary_air_direction /= sqrtf(length_sqr);
			}
			else
			{
				m_primary_air_direction = m_facing;
			}
			
			copy_state_into_object();
			
			break;
		}
		
		case WALKING_ANIMWAIT:
			m_false_wall.active = true;
			m_false_wall.distance = Mth::DotProduct(m_false_wall.normal, m_pos + m_critical_point_offset);
			
			GetObject()->GetVel()[X] = 0.0f;
			GetObject()->GetVel()[Z] = 0.0f;
			m_primary_air_direction = m_facing;
			break;
			
		case WALKING_HANG:
			m_false_wall.active = true;
			m_false_wall.normal = m_facing;
			m_false_wall.distance = Mth::DotProduct(m_false_wall.normal, m_pos + m_critical_point_offset);
			m_false_wall.cancel_height = m_pos[Y] - m_vertical_hang_offset;
			
			GetObject()->GetVel()[X] = 0.0f;
			GetObject()->GetVel()[Z] = 0.0f;
			m_primary_air_direction = m_facing;
			break;

		case WALKING_LADDER:
			GetObject()->GetVel()[X] = 0.0f;
			GetObject()->GetVel()[Z] = 0.0f;
			m_primary_air_direction = m_facing;
			break;
	}
	
	leave_movable_contact_for_air(GetObject()->GetVel(), GetObject()->GetVel()[Y]);
	
	set_state(WALKING_AIR);
	
	GetObject()->BroadcastEvent(CRCD(0x8687163a, "SkaterJump"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::go_in_air_state (   )
{
	setup_collision_cache();
	
	// default air event
	m_frame_event = CRCD(0x439f4704, "Air");
	
	// user control of horizontal velocity
	control_horizontal_vel();
	
	// prevent movement into walls
	adjust_horizonal_vel_for_environment();
//	if (should_bail_from_frame()) return;
	
	// check for head bonking
	adjust_vertical_vel_for_ceiling();
	
	// apply movement and acceleration for this frame
	m_pos += m_horizontal_vel * m_frame_length;
	m_pos[Y] += m_vertical_vel * m_frame_length + 0.5f * -s_get_gunslinger_param(CRCD(0xa5e2da58, "gravity")) * Mth::Sqr(m_frame_length);
	
	m_vertical_vel += -s_get_gunslinger_param(CRCD(0xa5e2da58, "gravity")) * m_frame_length;
	
	// see if we've landed yet
	check_for_landing(m_frame_start_pos, m_pos);
	if (m_state != WALKING_AIR /*|| should_bail_from_frame()*/) return;
	
	// maybe grab a rail; delay regrabbing of hang rails
	if (mp_input_component->GetControlPad().m_R1.GetPressed() && !m_ignore_grab_button
		&& ((m_previous_state != WALKING_HANG && m_previous_state != WALKING_LADDER) || Tmr::ElapsedTime(m_state_timestamp) > s_get_gunslinger_param(CRCD(0xe6e0c0a4, "rehang_delay"))))
	{
		if (m_previous_state == WALKING_LADDER)
		{
			// can't regrab ladders
			if (maybe_grab_to_hang(m_frame_start_pos, m_pos))
			{
				CFeeler::sClearDefaultCache();
				return;
			}
		}
		else
		{
			if (maybe_grab_to_hang(m_frame_start_pos, m_pos) || maybe_grab_to_ladder(m_frame_start_pos, m_pos))
			{
				CFeeler::sClearDefaultCache();
				return;
			}
		}
	}
	
	if (mp_input_component->GetControlPad().m_triangle.GetPressed())
	{
		if (maybe_stick_to_rail())
		{
			CFeeler::sClearDefaultCache();
			return;
		}
	}
	
	// insure that we do not slip through the cracks in the collision geometry which are a side-effect of moving collidable objects
	Mth::Vector previous_pos = m_pos;
	if (CCompositeObject* p_inside_object = mp_movable_contact_component->CheckInsideObjects(m_pos, m_frame_start_pos))
	{
		MESSAGE("WALKING_AIR, within moving object");
		
		m_horizontal_vel.Set();
		m_vertical_vel = 0.0f;
		check_for_landing(m_pos, previous_pos);
	}
	
	CFeeler::sClearDefaultCache();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::control_horizontal_vel (   )
{
	// We allow user control over the object's in air velocity.  The algorithm is complicated by the fact that the forward velocity of the jump needs
	// to be accounted for when allowing for velocity adjustment.  It is assumed that the jump direction is the same as the facing.
	
	// remove uncontrollable velocity term
	m_horizontal_vel -= m_uncontrollable_air_horizontal_vel;
	
	// forced run still works in the air
	adjust_control_for_forced_run();
	
	// adjust speed by the script set drag factor
	float adjust_magnitude = m_control_magnitude;
	
	// adjust velocity perpendicular to jump direction
	
	// direction perpendicular to jump direction
	Mth::Vector perp_direction(-m_primary_air_direction[Z], 0.0f, m_primary_air_direction[X]);
	
	// desired perpendicular velocity
	float perp_desired_vel = s_get_gunslinger_param(CRCD(0x896c8888, "jump_adjust_speed")) * adjust_magnitude * Mth::DotProduct(m_control_direction, perp_direction);
	
	// current perpendicular velocity
	float perp_vel = Mth::DotProduct(m_horizontal_vel, perp_direction);
	
	// exponentially approach desired velocity
	perp_vel = Mth::Lerp(perp_vel, perp_desired_vel, s_get_gunslinger_param(CRCD(0xf085443b, "jump_accel_factor")) * m_frame_length);
		
	// adjust velocity parallel to jump direction
	
	// desired parallel velocity
	float para_desired_vel = s_get_gunslinger_param(CRCD(0x896c8888, "jump_adjust_speed")) * adjust_magnitude * Mth::DotProduct(m_control_direction, m_primary_air_direction);
	
	// current parallel velocity
	float para_vel = Mth::DotProduct(m_horizontal_vel, m_primary_air_direction);
	
    // if desired velocity if forward and forward velocity already exceeds adjustment velocity
	if (para_desired_vel >= 0.0f && para_vel > para_desired_vel)
	{
		// do nothing; don't slow down the jump
	}
	else
	{
		// adjust desired velocity to center around current velocity to insure that our in air stopping ability is not too amazing
		if (para_desired_vel < 0.0f && para_vel > 0.0f)
		{
			para_desired_vel += para_vel;
		}
		
		// expondentially approach desired velocity
		para_vel = Mth::Lerp(para_vel, para_desired_vel, s_get_gunslinger_param(CRCD(0xf085443b, "jump_accel_factor")) * m_frame_length);
	}
		
	// rebuild horizontal velocity from parallel and perpendicular components
	m_horizontal_vel = para_vel * m_primary_air_direction + perp_vel * perp_direction;
	
	// reinstitute uncontrollable velocity term
	m_horizontal_vel += m_uncontrollable_air_horizontal_vel;
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::adjust_vertical_vel_for_ceiling (   )
{
	// If we hit our head, zero vertical velocity
	
	// only worry about the ceiling if we're moving upwards
	if (m_vertical_vel <= 0.0f) return;
	
	// look for a collision up through the body to the head
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_end = m_pos;
	feeler.m_end[Y] += s_get_gunslinger_param(CRCD(0x9ea1974a, "walker_height"));
	if (!feeler.GetCollision()) return;
	
	// zero upward velocity
	m_vertical_vel = 0.0f;
	
	GetObject()->SelfEvent(CRCD(0x6e84acf3, "HitCeiling"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::check_for_landing ( const Mth::Vector& previous_pos, const Mth::Vector& final_pos )
{
	// See if our feet have passed through geometry.  If so, snap to it and go to ground state.
	
	CFeeler feeler;
	feeler.m_start = previous_pos;
	feeler.m_end = final_pos;
	if (!feeler.GetCollision()) return;
	
	// snap to the collision point
	m_pos = feeler.GetPoint();
	
	// zero vertical velocity
	m_vertical_vel = 0.0f;
	
	// change to ground state
	set_state(WALKING_GROUND);
	
	// stash the feeler
	m_last_ground_feeler = feeler;
	m_last_ground_feeler_valid = true;
	
	// trip any land trigger
	mp_trigger_component->CheckFeelerForTrigger(TRIGGER_LAND_ON, m_last_ground_feeler);
//	if (should_bail_from_frame()) return;
	
	// setup our ground normal for next frames velocity slope adjustment
	m_ground_normal = feeler.GetNormal();
	
	// check for a moving contact
	mp_movable_contact_component->CheckForMovableContact(feeler);
	if (mp_movable_contact_component->HaveContact())
	{
		m_horizontal_vel -= mp_movable_contact_component->GetContact()->GetObject()->GetVel();
		m_horizontal_vel[Y] = 0.0f;
	}
	
	// retain only that velocity which is parallel to our facing and forward
	if (Mth::DotProduct(m_horizontal_vel, m_facing) > 0.0f)
	{
		m_horizontal_vel.ProjectToNormal(m_facing);
	}
	else
	{
		m_horizontal_vel.Set();
	}
	
	// clear any jump requests
	mp_input_component->GetControlPad().m_x.ClearRelease();
	
	m_frame_event = CRCD(0x57ff2a27, "Land");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::uber_frig (   )
{
	// insure that we don't fall to the center of the earth, even if there are holes in the geometry; also, do lighting since we've got the feeler anyway
	
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_start[Y] += 1.0f;
	feeler.m_end = m_pos;
	feeler.m_end[Y] -= FEET(400);
	
	if (feeler.GetCollision())
	{
		mp_model_component->ApplyLightingFromCollision(feeler);
		return;
	}
	
	MESSAGE("applying uber frig");
	
	// teleport us back to our position at the frame's start; not pretty, but this isn't supposed to be
	m_pos = m_frame_start_pos;
	
	// zero our velocity too
	m_horizontal_vel.Set();
	m_vertical_vel = 0.0f;
	
	// set our state to ground
	set_state(WALKING_GROUND);
			  	
	m_last_ground_feeler_valid = false;
	
	m_ground_normal.Set(0.0f, 1.0f, 0.0f);

	// reset our script state
	m_frame_event = CRCD(0x57ff2a27, "Land");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::lerp_upright (   )										 
{
    if (m_upward[Y] == 1.0f) return;
	
	if (m_upward[Y] > 0.999f)
	{
		m_upward.Set(0.0f, 1.0f, 0.0f);
		return;
	}
	
	m_upward = Mth::Lerp(m_upward, Mth::Vector(0.0f, 1.0f, 0.0f), s_get_gunslinger_param(CRCD(0xf22c135, "lerp_upright_rate")) * Tmr::FrameLength());
	m_upward.Normalize();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::check_for_wall_push (   )
{
	// ensure we have a forward contact
	if (!mp_contacts[0].in_collision && !mp_contacts[1].in_collision && !mp_contacts[vNUM_FEELERS - 1].in_collision)
	{
		return m_wall_push_test.active = false;
	}
	
	// ensure that control is maxed out
	if (!m_control_pegged)
	{
		return m_wall_push_test.active = false;
	}
	
	if (!m_wall_push_test.active)
	{
		// if we're not testing, simply start the test
		m_wall_push_test.test_start_time = Tmr::GetTime();
		m_wall_push_test.active = true;
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_jump_low_barrier (   )
{
	// For each forward contact in collision, we check to see if any lacks a collision at the maximum jump height.  For any that do, we find the first height
	// at which they are in contact.  The lowest such height is used at the target jump height.
	
	const int p_forward_contact_idxs[] = { 0, 1, vNUM_FEELERS - 1 };
	
	// we use the lowest hang height as the maximum autojump barrier height
	float top_feeler_height = s_get_gunslinger_param(CRCD(0x2c942693, "lowest_hang_height"));
	float feeler_length = 2.0f * s_get_gunslinger_param(CRCD(0x99978d2b, "feeler_length"));
	float height_increment = (top_feeler_height - s_get_gunslinger_param(CRCD(0x6da7f696, "feeler_height"))) / vNUM_BARRIER_HEIGHT_FEELERS;
	
	CFeeler feeler;
	
	// setup collision cache
	Mth::CBBox bbox(
		m_pos - Mth::Vector(feeler_length + 1.0f, 0.0f, feeler_length + 1.0f),
		m_pos + Mth::Vector(feeler_length + 1.0f, top_feeler_height + 1.0f, feeler_length + 1.0f)
	);
	Nx::CCollCache* p_coll_cache = Nx::CCollCacheManager::sCreateCollCache();
	p_coll_cache->Update(bbox);
	feeler.SetCache(p_coll_cache);
	
	// loop over forward collisions and check to see if the barrier in each of their directions is jumpable
	bool jumpable = false;
	for (int i = 0; i < 3; i++)
	{
		int n = p_forward_contact_idxs[i];
		
		if (!mp_contacts[n].in_collision) continue;
		
		// first check to see if the collision normal is not too transverse to the control direction, making a autojump unlikely to succeed
		if (Mth::DotProduct(mp_contacts[n].normal, m_control_direction) > -cosf(Mth::DegToRad(s_get_gunslinger_param(CRCD(0x78e6a5ec, "barrier_jump_max_angle")))))
		{
			mp_contacts[n].jumpable = false;
			continue;
		}
		
		feeler.m_start = m_pos;
		feeler.m_start[Y] += top_feeler_height;
		feeler.m_end = feeler.m_start + feeler_length * calculate_feeler_offset_direction(n);
		
		mp_contacts[n].jumpable = !feeler.GetCollision();
		
        if (mp_contacts[n].jumpable)
		{
			jumpable = true;
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(0, 0, 255, 0);
			}
			#endif
		}
		else
		{
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(255, 0, 0, 0);
			}
			#endif
		}
	}
	
	// if the barrier is not jumpable
	if (!jumpable)
	{
		// no autojump
		Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
		return false;
	}
	
	// loop over the jumpable collision directions
	float lowest_height = m_pos[Y] + top_feeler_height;
	for (int i = 0; i < 3; i++)
	{
		int n = p_forward_contact_idxs[i];
		
		if (!mp_contacts[n].in_collision) continue;
		if (!mp_contacts[n].jumpable) continue;
		
		feeler.m_start = m_pos;
		feeler.m_start[Y] += top_feeler_height;
		feeler.m_end = feeler.m_start + feeler_length * calculate_feeler_offset_direction(n);
		
		// look for the barrier height
		for (int h = vNUM_BARRIER_HEIGHT_FEELERS - 1; h--; )
		{
			feeler.m_start[Y] -= height_increment;
			feeler.m_end[Y] -= height_increment;
			if (feeler.GetCollision())
			{
				#ifdef __USER_DAN__
				if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
				{
					feeler.DebugLine(255, 0, 0, 0);
				}
				#endif
				break;
			}
			else
			{
				#ifdef __USER_DAN__
				if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
				{
					feeler.DebugLine(0, 255, 0, 0);
				}
				#endif
			}
		}
		
		// find the lowest barrier of the jumpable directions
		if (lowest_height > feeler.m_start[Y])
		{
			lowest_height = feeler.m_start[Y];
		}
	}
	
	Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
	
	// caluclate the velocity required to clear the barrier
	float jump_height = lowest_height + height_increment + s_get_gunslinger_param(CRCD(0x72660978, "barrier_jump_min_clearance")) - m_pos[Y];
	float required_vertical_velocity = sqrtf(2.0f * s_get_gunslinger_param(CRCD(0xa5e2da58, "gravity")) * jump_height);
	
	// setup the new walking state
	
	m_vertical_vel = required_vertical_velocity;
	
	set_state(WALKING_AIR);
	
	m_primary_air_direction = m_facing;
	leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
	
	m_frame_event = CRCD(0x584cf9e9, "Jump");
	
	
	// stop late jumps after an autojump
	GetObject()->RemoveEventHandler(CRCD(0x6b9ca247, "JumpRequested"));
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::leave_movable_contact_for_air ( Mth::Vector& horizontal_vel, float& vertical_vel )
{
	// use movement from the latest movable contact update call
	
	if (mp_movable_contact_component->HaveContact())
	{
		// keep track of the horizontal velocity due to our old contact
		m_uncontrollable_air_horizontal_vel = mp_movable_contact_component->GetContact()->GetObject()->GetVel();

		if (Mth::DotProduct(m_uncontrollable_air_horizontal_vel, horizontal_vel) > 0.0f)
		{
			// extra kicker; dangerous as there's no collision detection; without this slight extra movement, when we walk off the front of a movable object,
			// the object will move back under us before our next frame, and we will clip its edge and land on it
			m_pos += m_uncontrollable_air_horizontal_vel * m_frame_length;
		}
		
		// add movable contact's velocity into our launch velocity
		vertical_vel += m_uncontrollable_air_horizontal_vel[Y];
		m_uncontrollable_air_horizontal_vel[Y] = 0.0f;
		horizontal_vel += m_uncontrollable_air_horizontal_vel;
	}
	else
	{
		m_uncontrollable_air_horizontal_vel.Set();
	}
	
	mp_movable_contact_component->LoseAnyContact();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::calculate_anim_wait_facing_drift_parameters ( const Mth::Vector& goal_facing )
{
	float initial_angle = atan2f(m_facing[X], m_facing[Z]);
	float goal_angle = atan2f(goal_facing[X], goal_facing[Z]);
	
	m_drift_angle = goal_angle - initial_angle;
	if (Mth::Abs(m_drift_angle) > Mth::PI)
	{
		m_drift_angle -= Mth::Sgn(m_drift_angle) * (2.0f * Mth::PI);
	}
	
	m_drift_goal_facing = goal_facing;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::go_anim_wait_state (   )
{
	if (mp_movable_contact_component->UpdateContact(m_pos))
	{
		m_offset_due_to_movable_contact += mp_movable_contact_component->GetContact()->GetMovement();
		if (mp_movable_contact_component->GetContact()->IsRotated())
		{
			m_drift_goal_facing = mp_movable_contact_component->GetContact()->GetRotation().Rotate(m_drift_goal_facing);
			if (m_drift_goal_facing[Y] != 0.0f)
			{
				m_drift_goal_facing[Y] = 0.0f;
				m_drift_goal_facing.Normalize();
			}
			
		}
	}
	
	float start, current, end;
	mp_animation_component->GetPrimaryAnimTimes(&start, ¤t, &end);
    float animation_completion_factor = Mth::LinearMap(0.0f, 1.0f, current, start, end);
	
	// smoothly drift the position
	m_pos = m_offset_due_to_movable_contact + Mth::Lerp(m_anim_wait_initial_pos, m_anim_wait_goal_pos, animation_completion_factor);
	
	float angle = Mth::Lerp(-m_drift_angle, 0.0f, animation_completion_factor);
	m_facing = m_drift_goal_facing;
	m_facing.RotateY(angle);
	
	m_frame_event = CRCD(0x4fe6069c, "AnimWait");
}
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::anim_wait_complete (   )
{
	GetObject()->SetPos(m_anim_wait_goal_pos + m_offset_due_to_movable_contact);
    
    Mth::Matrix matrix;
    matrix[Z] = m_drift_goal_facing;
    matrix[Y].Set(0.0f, 1.0f, 0.0f);
    matrix[X].Set(m_drift_goal_facing[Z], 0.0f, -m_drift_goal_facing[X]);
	GetObject()->SetMatrix(matrix);
	
    if (mp_anim_wait_complete_callback)
    {
        (this->*mp_anim_wait_complete_callback)();
    }
}
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_stick_to_rail (   )
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
	
void CWalkComponent::setup_collision_cache (   )
{
	float horizontal_reach = 1.0f + s_get_gunslinger_param(CRCD(0x99978d2b, "feeler_length"));
	float vertical_height = 1.0f + s_get_gunslinger_param(CRCD(0x9ea1974a, "walker_height"));;
	float vertical_depth = 1.0f + s_get_gunslinger_param(CRCD(0xaf3e4251, "snap_down_height"));
	
	Mth::CBBox bbox(
		GetObject()->GetPos() - Mth::Vector(horizontal_reach, vertical_depth, horizontal_reach, 0.0f),
		GetObject()->GetPos() + Mth::Vector(horizontal_reach, vertical_height, horizontal_reach, 0.0f)
	);
	
	mp_collision_cache->Update(bbox);
	CFeeler::sSetDefaultCache(mp_collision_cache);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::copy_state_into_object (   )
{
	// build the object's matrix based on our facing
	Mth::Matrix matrix;
	
	// basically, rotate Z upward to perpendicular with m_upward
	matrix[X] = Mth::CrossProduct(m_upward, m_facing);
	matrix[X].Normalize();
	matrix[Y] = m_upward;
	matrix[Z] = Mth::CrossProduct(matrix[X], matrix[Y]);
	matrix[W].Set( 0.0f, 0.0f, 0.0f, 1.0f );
	
	GetObject()->SetPos( m_pos );
	GetObject()->SetMatrix( matrix );
	GetObject()->SetDisplayMatrix( matrix );
	
	// construct the object's velocity
	GetObject()->SetVel(m_horizontal_vel);
	GetObject()->GetVel()[Y] = m_vertical_vel;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::get_controller_input (   )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	Dbg_Assert(mp_camera);
	
	// rotate controller direction into camera's frame
	
	Mth::Vector camera_forward = -mp_camera->m_matrix[Z];
	camera_forward[Y] = 0.0f;
	camera_forward.Normalize();
	
	// allow a tolerance range for pressing directly forward
	float angle = control_pad.m_leftAngle;
	if (Mth::Abs(angle) < Mth::DegToRad(s_get_gunslinger_param(CRCD(0x4676a268, "forward_tolerance"))))
	{
		angle = 0.0f;
	}
	
	float sin_angle = sinf(angle);
	float cos_angle = cosf(angle);
	m_control_direction[X] = cos_angle * camera_forward[X] - sin_angle * camera_forward[Z];
	m_control_direction[Z] = sin_angle * camera_forward[X] + cos_angle * camera_forward[Z];
	
	// different control schemes for analog stick and d-pad
#	if 0
	if (control_pad.m_leftX == 0.0f && control_pad.m_leftY == 0.0f)
	{
		// d-pad control
		if (control_pad.m_leftLength == 0.0f)
		{
			m_control_magnitude = 0.0f;
			m_control_pegged = false;
			
			// don't reset dpad in the air
			if (m_state != WALKING_AIR)
			{
				m_dpad_used_last_frame = false;
			}
		}
		else
		{
			if (!m_dpad_used_last_frame)
			{
				m_dpad_use_time_stamp = Tmr::GetTime();
			}
			m_dpad_used_last_frame = true;
			
			if (m_state == WALKING_GROUND)
			{
				// slowly ramp up to a full run
				
				Tmr::Time elapsed_time = Tmr::ElapsedTime(m_dpad_use_time_stamp);
				
				Tmr::Time full_run_dpad_delay = static_cast< Tmr::Time >(s_get_gunslinger_param(CRCD(0x1832588c, "full_run_dpad_delay")));
				Tmr::Time start_run_dpad_delay = static_cast< Tmr::Time >(s_get_gunslinger_param(CRCD(0x2c386a43, "start_run_dpad_delay")));
				
				if (elapsed_time < start_run_dpad_delay)
				{
					m_control_magnitude = s_get_gunslinger_param(CRCD(0xc1528f7f, "walk_point"));
				}
				else if (elapsed_time < full_run_dpad_delay)
				{
					m_control_magnitude = Mth::SmoothMap(
						s_get_gunslinger_param(CRCD(0xc1528f7f, "walk_point")),
						1.0f,
						elapsed_time,
						start_run_dpad_delay,
						full_run_dpad_delay
					);
				}
				else
				{
					m_control_magnitude = 1.0f;
				}
			}
			else
			{
				m_control_magnitude = 1.0f;
			}
            m_control_pegged = true;
		}
        
		// damp dpad control directions towards forward when running
        if (m_state == WALKING_GROUND && Mth::Abs(angle) < Mth::DegToRad(90.0f + 5.0f) && (forced_run() || m_control_magnitude > s_get_gunslinger_param(CRCD(0xc1528f7f, "walk_point"))))
        {
			if (forced_run() || m_control_magnitude == 1.0f)
			{
				m_control_direction += s_get_gunslinger_param(CRCD(0x3c581621, "dpad_control_damping_factor")) * camera_forward;
			}
			else
			{
				// smoothly interpolate between damping and no damping
				m_control_direction += Mth::SmoothMap(0.0f, s_get_gunslinger_param(CRCD(0x3c581621, "dpad_control_damping_factor")), m_control_magnitude, s_get_gunslinger_param(CRCD(0xc1528f7f, "walk_point")), 1.0f)
					* camera_forward;
			}
			m_control_direction.Normalize();
        }
	}
	else
#	endif
	{
		// analog stick control
		m_control_magnitude = control_pad.GetScaledLeftAnalogStickMagnitude() / 0.85f;
		if (m_control_magnitude >= 1.0f)
		{
			m_control_magnitude = 1.0f;
			m_control_pegged = true;
		}
		else
		{
			m_control_pegged = false;
		}
	}
	
	// during a forward control lock, ignore all input not in the forward direction
	if (m_state == WALKING_GROUND && m_forward_control_lock)
	{
		m_control_magnitude = Mth::ClampMin(m_control_magnitude * Mth::DotProduct(m_control_direction, m_facing), 0.0f);
		m_control_direction = m_facing;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::adjust_control_for_forced_run (   )
{
	if (!forced_run()) return;
	
	// if no direction is pressed
	if (m_control_magnitude == 0.0f)
	{
		// run in the direction of our current facing
		m_control_direction = m_facing;
	}

	// run full speed
	m_control_magnitude = 1.0f;
	m_control_pegged = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CWalkComponent::calculate_desired_speed (   )
{
	// forced run
	adjust_control_for_forced_run();
	
	if (m_control_magnitude == 0.0f) return 0.0f;
	
	float walk_point = s_get_gunslinger_param(CRCD(0xc1528f7f, "walk_point"));
	if (m_control_magnitude <= walk_point)
	{
		m_run_toggle = false;
		return Mth::LinearMap(0.0f, s_get_gunslinger_param(CRCD(0x79d182ad, "walk_speed")), m_control_magnitude, 0.3f, walk_point);
	}
	else
	{
		m_run_toggle = true;
		return Mth::LinearMap(s_get_gunslinger_param(CRCD(0x79d182ad, "walk_speed")), s_get_gunslinger_param(CRCD(0xcc461b87, "run_speed")), m_control_magnitude, walk_point, 1.0f);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CWalkComponent::calculate_feeler_offset_direction ( int contact )
{
	float angle = contact * (2.0f * Mth::PI / vNUM_FEELERS);
	float cos_angle = cosf(angle);
	float sin_angle = sinf(angle);

	Mth::Vector end_offset_direction;
	end_offset_direction[X] = cos_angle * m_facing[X] - sin_angle * m_facing[Z];
	end_offset_direction[Y] = 0.0f;
	end_offset_direction[Z] = sin_angle * m_facing[X] + cos_angle * m_facing[Z];
	end_offset_direction[W] = 1.0f;
	
	return end_offset_direction;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::determine_stand_pos ( const Mth::Vector& proposed_stand_pos, Mth::Vector& stand_pos, CFeeler& feeler )
{
	// upward offset of standing position at maximum standable slope
	float max_height_adjustment = s_get_gunslinger_param(CRCD(0x6da7f696, "feeler_height")) / s_get_gunslinger_param(CRCD(0xa20c43b7, "push_feeler_length")) * s_get_gunslinger_param(CRCD(0x21dfbe77, "pull_up_offset_forward"));
	
	feeler.m_start = proposed_stand_pos;
	feeler.m_start[Y] += max_height_adjustment;
	feeler.m_end = proposed_stand_pos;
	feeler.m_end[Y] -= max_height_adjustment;
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(255, 255, 0, 0);
	}
	#endif
	if (feeler.GetCollision())
	{
		stand_pos = feeler.GetPoint();
		return true;
	}
	
	stand_pos = proposed_stand_pos;
	return false;
}

}



================================================
FILE: Code/Gel/Components/HorseCameraComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       HorseCameraComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  2/10/3
//****************************************************************************

#include 
#include 
									 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 


#define MESSAGE(a) { printf("M:%s:%i: %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPI(a) { printf("D:%s:%i: " #a " = %i\n", __FILE__ + 15, __LINE__, a); }
#define DUMPB(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a ? "true" : "false"); }
#define DUMPF(a) { printf("D:%s:%i: " #a " = %g\n", __FILE__ + 15, __LINE__, a); }
#define DUMPE(a) { printf("D:%s:%i: " #a " = %e\n", __FILE__ + 15, __LINE__, a); }
#define DUMPS(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPP(a) { printf("D:%s:%i: " #a " = %p\n", __FILE__ + 15, __LINE__, a); }
#define DUMPV(a) { printf("D:%s:%i: " #a " = %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z]); }
#define DUMP4(a) { printf("D:%s:%i: " #a " = %g, %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z], (a)[W]); }
#define DUMPM(a) { DUMP4(a[X]); DUMP4(a[Y]); DUMP4(a[Z]); DUMP4(a[W]); }
#define MARK { printf("K:%s:%i: %s\n", __FILE__ + 15, __LINE__, __PRETTY_FUNCTION__); }
#define PERIODIC(n) for (static int p__ = 0; (p__ = ++p__ % (n)) == 0; )

#define vVELOCITY_WEIGHT_DROP_THRESHOLD				MPH_TO_IPS(15.0f)
// #define vLOCK_ATTRACTOR_VELOCITY_THRESHOLD			MPH_TO_IPS(5.0f)
#define vLOCK_ATTRACTOR_VELOCITY_THRESHOLD			MPH_TO_IPS(5000000.0f)
#define vSTATE_CHANGE_DELAY (1.0f)

extern float	screen_angle;
extern bool		gun_fired;

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CHorseCameraComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CHorseCameraComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CHorseCameraComponent::CHorseCameraComponent() : CBaseComponent()
{
	SetType( CRC_HORSECAMERA );
	
	m_state				= STATE_NORMAL;
	m_offset_height		= FEET(5.0f);
	m_offset_distance	= FEET(12.0f);
	m_offset_tilt		= 0.0f;
	m_alignment_rate	= 3.0f;

	m_orientation_matrix.Ident();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CHorseCameraComponent::~CHorseCameraComponent()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::InitFromStructure( Script::CStruct* pParams )
{
	uint32 subject_id;
	
	if (pParams->ContainsComponentNamed(CRCD(0x431c185, "subject")))
	{
		pParams->GetChecksum(CRCD(0x431c185, "subject"), &subject_id, Script::ASSERT);
		mp_subject = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(subject_id));
		Dbg_MsgAssert(mp_subject, ("Vehicle camera given subject which is not a composite object"));
		mp_subject_vehicle_component = static_cast< CHorseComponent* >(mp_subject->GetComponent(CRC_HORSE));
		Dbg_MsgAssert(mp_subject_vehicle_component, ("HorseCameraComponent given subject which contains no HorseComponent"));
	}
		
	pParams->GetFloat( CRCD( 0x9213625f, "alignment_rate" ), &m_alignment_rate);
	pParams->GetFloat( CRCD( 0x14849b6d, "offset_height" ), &m_offset_height);
	pParams->GetFloat( CRCD( 0xbd3d3ca9, "offset_distance" ), &m_offset_distance);

	if( pParams->GetFloat( CRCD( 0x480e14e2, "offset_tilt"), &m_offset_tilt ))
	{
		// Convert tilt from degrees to radians.
		m_offset_tilt = Mth::DegToRad( m_offset_tilt );
	}
	
	reset_camera();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure( pParams );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::Finalize( void )
{
	mp_input_component = GetInputComponentFromObject( GetObject());
	Dbg_Assert( mp_input_component );
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::Update()
{
	// Decide whether we want to be active right now.
	if( mp_subject_vehicle_component )
	{
		if( !mp_subject_vehicle_component->ShouldUpdateCamera())
		{
			return;
		}
	}

	m_frame_length = Tmr::FrameLength();

	get_controller_input();

	calculate_attractor_direction();
	
	// Due to rounding errors this can sometimes be > |1|, which hoses acosf(), so limit here.
	float angular_distance = acosf( Mth::Clamp( Mth::DotProduct( m_direction, m_attractor_direction ), -1.0f, 1.0f ));
	if( angular_distance > Mth::PI / 2.0f )
	{
		angular_distance = Mth::PI - angular_distance;
	}
	
	bool	sign = Mth::CrossProduct( m_direction, m_attractor_direction )[Y] > 0.0f;
	float	step = m_alignment_rate * angular_distance * Tmr::FrameLength();
	if( step > angular_distance )
	{
		step = angular_distance;
	}
	
	m_direction.RotateY(( sign ? 1.0f : -1.0f ) * step );
	
	m_direction.Normalize();
	calculate_orientation_matrix();
	
	// Save off the orientation matrix at this point.
	Mth::Matrix saved_mat = m_orientation_matrix;

	m_orientation_matrix.RotateYLocal( m_lookaround_heading );
	m_orientation_matrix.RotateXLocal( m_lookaround_tilt - m_offset_tilt );

	// Test the weapon component to generate the 'sticky' targetting behavior.
	if( mp_best_target )
	{
		// Need to get the rider so we can get the base object and then the weapon component (messy).
		CCompositeObject*	p_obj		= mp_subject_vehicle_component->GetRider();
		if( p_obj )
		{
			CWeaponComponent* p_weapon	= GetWeaponComponentFromObject( p_obj );
			p_weapon->SetCurrentTarget( mp_best_target );
			Mth::Vector sight_pos = mp_subject->GetPos() + Mth::Vector( 0.0f, m_offset_height, 0.0f, 0.0f );
			p_weapon->SetSightPos( sight_pos );
			p_weapon->SetSightMatrix( m_orientation_matrix );

			float extra_heading_change, extra_tilt_change;
			p_weapon->ProcessStickyTarget( m_lookaround_heading_delta, m_lookaround_tilt_delta, &extra_heading_change, &extra_tilt_change );

			if(( extra_heading_change != 0.0f ) || ( extra_tilt_change != 0.0f ))
			{
				// Reset the matrix to what it was prior to the heading and tilt adjustments.
				m_orientation_matrix = saved_mat;

				m_lookaround_heading += extra_heading_change;
				m_orientation_matrix.RotateYLocal( m_lookaround_heading );

				m_lookaround_tilt += extra_tilt_change;
				m_orientation_matrix.RotateXLocal( m_lookaround_tilt - m_offset_tilt );
			}
		}
	}

	calculate_position();

	ApplyCameraCollisionDetection(
		m_pos, 
		m_orientation_matrix, 
		m_pos - m_offset_distance * m_orientation_matrix[Z], 
		m_pos - m_offset_distance * m_orientation_matrix[Z], 
		false, 
		false
	);
	
	m_pos[W] = 1.0f;
	m_orientation_matrix[X][W] = 0.0f;
	m_orientation_matrix[Y][W] = 0.0f;
	m_orientation_matrix[Z][W] = 0.0f;

	do_reticle();

	GetObject()->SetPos( m_pos );
	GetObject()->SetMatrix( m_orientation_matrix );
	GetObject()->SetDisplayMatrix( m_orientation_matrix );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CHorseCameraComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case CRCC(0x469fd, "VehicleCamera_Reset"):
			RefreshFromStructure(pParams);
			break;
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to C......Component::GetDebugInfo"));
	
	p_info->AddChecksum("mp_subject", mp_subject->GetID());

	CBaseComponent::GetDebugInfo(p_info);	  
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::get_controller_input( void )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();

	// Switch the aiming state if triggered.
	if( control_pad.m_R3.GetTriggered())
	{
		control_pad.m_R3.ClearTrigger();

		// Restore screen angle.
		screen_angle = 72.0f;
		Nx::CViewportManager::sSetScreenAngle( screen_angle );

		if( m_state == STATE_NORMAL )
			m_state = STATE_AIMING;
		else if( m_state == STATE_AIMING )
			m_state = STATE_NORMAL;
	}

	// Fire if triggered.
	gun_fired = false;
	if( control_pad.m_R1.GetTriggered())
	{
		gun_fired = true;
		control_pad.m_R1.ClearTrigger();
	}

	// Switch sniper mode if triggered and in aiming mode.
	if( control_pad.m_up.GetTriggered())
	{
		control_pad.m_up.ClearTrigger();

		// Unfortunately, pressing up on the left control stick will also cause m_up to be triggered, so check this
		// is really the d-pad press.
		if( control_pad.GetScaledLeftAnalogStickMagnitude() == 0.0f )
		{
			if( m_state == STATE_AIMING )
			{
				// This would be a good place to put the field of view setting code for the sniper rifle.
				if( screen_angle == 72.0f )
				{
					screen_angle = 25.0f;
				}
				else if( screen_angle == 25.0f )
				{
					screen_angle = 7.5f;
				}
				else if( screen_angle == 7.5f )
				{
					screen_angle = 72.0f;
				}
				Nx::CViewportManager::sSetScreenAngle( screen_angle );
			}
		}
	}

	// Deal with adjusting aim direction.
	float tilt_target		= control_pad.m_scaled_rightY;
	float heading_target	= control_pad.m_scaled_rightX;

	if( m_state == STATE_NORMAL )
	{
		// Aiming input should be ignored.
		tilt_target		= 0.0f;
		heading_target	= 0.0f;
	}

	// Modulate with the variable used to damp cursor movement when aiming at a heading_target.
	heading_target	= heading_target * m_spin_modulator;
	tilt_target		= tilt_target * m_tilt_modulator;
		
	// Get script values.
	float heading_ka = Script::GetFloat( CRCD( 0xe31bc279, "GunslingerHorseLookaroundHeadingKa" ), Script::ASSERT );
	float heading_ea = Script::GetFloat( CRCD( 0x7d98eff7, "GunslingerHorseLookaroundHeadingEa" ), Script::ASSERT );
	float heading_ks = Script::GetFloat( CRCD( 0x10a2b331, "GunslingerHorseLookaroundHeadingKs" ), Script::ASSERT );
	float heading_es = Script::GetFloat( CRCD( 0x8e219ebf, "GunslingerHorseLookaroundHeadingEs" ), Script::ASSERT );

	// Calculate acceleration.
	float a = heading_ka * powf( Mth::Abs( heading_target ), heading_ea ) * (( heading_target > 0.0f ) ? 1.0f : ( heading_target < 0.0f ) ? -1.0f : 0.0f );

	// Calculate max speed.
	float s = heading_ks * powf( Mth::Abs( heading_target ), heading_es ) * (( heading_target > 0.0f ) ? 1.0f : ( heading_target < 0.0f ) ? -1.0f : 0.0f );

	m_lookaround_heading_angular_speed += a;

	if( s == 0.0f )
	{
		m_lookaround_heading_angular_speed = 0.0f;
	}
	else if((( s > 0.0f ) && ( m_lookaround_heading_angular_speed > s )) || (( s < 0.0f ) && ( m_lookaround_heading_angular_speed < s )))
	{
		m_lookaround_heading_angular_speed = s;
	}

	float lookaround_heading_speed = Script::GetFloat( CRCD( 0x8cf5f0cd, "GunslingerHorseLookaroundHeadingSpeed" ), Script::ASSERT );

	// Control stick left - reticle should move left.
	m_lookaround_heading_delta	= -m_lookaround_heading_angular_speed * lookaround_heading_speed * m_frame_length;
	m_lookaround_heading	   += m_lookaround_heading_delta;

	if( m_lookaround_heading > Mth::PI )
		m_lookaround_heading = m_lookaround_heading - ( 2 * Mth::PI );
	else if( m_lookaround_heading <= -Mth::PI )
		m_lookaround_heading = ( 2 * Mth::PI ) + m_lookaround_heading;

	if( Script::GetInteger( CRCD( 0x9edfc7af, "GunslingerInvertAiming" )) == 0 )
	{
		// Negate value if vertical aiming invert is not enabled.
		tilt_target = -tilt_target;
	}

	// Get script values.
	float tilt_ka = Script::GetFloat( CRCD( 0x3e8e974f, "GunslingerHorseLookaroundTiltKa" ), Script::ASSERT );
	float tilt_ea = Script::GetFloat( CRCD( 0xa00dbac1, "GunslingerHorseLookaroundTiltEa" ), Script::ASSERT );
	float tilt_ks = Script::GetFloat( CRCD( 0xcd37e607, "GunslingerHorseLookaroundTiltKs" ), Script::ASSERT );
	float tilt_es = Script::GetFloat( CRCD( 0x53b4cb89, "GunslingerHorseLookaroundTiltEs" ), Script::ASSERT );

	// Calculate acceleration.
	a = tilt_ka * powf( Mth::Abs( tilt_target ), tilt_ea ) * (( tilt_target > 0.0f ) ? 1.0f : ( tilt_target < 0.0f ) ? -1.0f : 0.0f );

	// Calculate max speed.
	s = tilt_ks * powf( Mth::Abs( tilt_target ), tilt_es ) * (( tilt_target > 0.0f ) ? 1.0f : ( tilt_target < 0.0f ) ? -1.0f : 0.0f );

	m_lookaround_tilt_angular_speed += a;

	if( s == 0.0f )
	{
		m_lookaround_tilt_angular_speed = 0.0f;
	}
	else if((( s > 0.0f ) && ( m_lookaround_tilt_angular_speed > s )) || (( s < 0.0f ) && ( m_lookaround_tilt_angular_speed < s )))
	{
		m_lookaround_tilt_angular_speed = s;
	}

	float lookaround_tilt_speed = Script::GetFloat( CRCD( 0xebbaa7e8, "GunslingerHorseLookaroundTiltSpeed" ), Script::ASSERT );
	m_lookaround_tilt_delta	= m_lookaround_tilt_angular_speed * lookaround_tilt_speed * m_frame_length;
	m_lookaround_tilt	   += m_lookaround_tilt_delta;

	if( m_lookaround_tilt > 1.1f )
		m_lookaround_tilt = 1.1f;
	else if( m_lookaround_tilt < -0.85f )
		m_lookaround_tilt = -0.85f;

	if( m_state == STATE_NORMAL )
	{
		// Smoothly move the lookaround values back to zero.

		m_lookaround_tilt		-= ( m_lookaround_tilt * 0.05f );
		m_lookaround_heading	-= ( m_lookaround_heading * 0.05f );
	}
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::reset_camera( void )
{
	m_state	= STATE_NORMAL;

	m_attractor_direction = -mp_subject->GetMatrix()[Z];
	m_attractor_direction[Y] = 0.0f;
	m_attractor_direction.Normalize();
	
	calculate_attractor_direction();
	
	m_direction = m_attractor_direction;
	
	calculate_orientation_matrix();
	calculate_position();
	
	GetObject()->SetPos( m_pos );
	GetObject()->SetMatrix( m_orientation_matrix );
	GetObject()->SetDisplayMatrix(m_orientation_matrix);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::calculate_attractor_direction( void )
{
	if( m_state == STATE_NORMAL )
	{
		Mth::Vector vel_direction = -mp_subject_vehicle_component->GetVel();
		vel_direction[Y] = 0.0f;
		float vel = vel_direction.Length();
		vel_direction.Normalize();
	
		float vel_weight = Mth::ClampMax(vel / vVELOCITY_WEIGHT_DROP_THRESHOLD, 1.0f) * Mth::DotProduct(vel_direction, -mp_subject->GetMatrix()[Z]);
		
		m_attractor_direction = -mp_subject->GetMatrix()[Z];
		m_attractor_direction[Y] = 0.0f;
		m_attractor_direction.Normalize();
		
		if (vel_weight > 0.0f)
		{
			m_attractor_direction += vel_weight * vel_direction;
			m_attractor_direction.Normalize();
		}
	}
	else if( m_state == STATE_AIMING )
	{
		m_attractor_direction = m_direction;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::calculate_orientation_matrix( void )
{
	m_orientation_matrix[Z] = m_direction;
	m_orientation_matrix[X] = Mth::CrossProduct( Mth::Vector( 0.0f, 1.0f, 0.0f, 0.0f ), m_orientation_matrix[Z] ).Normalize();
	m_orientation_matrix[Y] = Mth::CrossProduct( m_orientation_matrix[Z], m_orientation_matrix[X] );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::calculate_position( void )
{
	Mth::Vector direction = m_orientation_matrix[Z];
	m_pos = mp_subject->GetPos() + Mth::Vector( m_offset_distance * direction[X], m_offset_height + ( m_offset_distance * direction[Y] ), m_offset_distance * direction[Z], 0.0f );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseCameraComponent::do_reticle( void )
{
	// Need to get the rider so we can get the base object and then the weapon component (messy).
	CCompositeObject*	p_obj		= mp_subject_vehicle_component->GetRider();
	if( p_obj )
	{
		Mth::Vector reticle_min	= m_pos;
		Mth::Vector reticle_max;

		CWeaponComponent* p_weapon	= GetWeaponComponentFromObject( p_obj );

		p_weapon->SetSightMatrix( m_orientation_matrix );
		mp_best_target = p_weapon->GetCurrentTarget( reticle_min, &reticle_max );

		if( gun_fired )
		{
			p_weapon->Fire();
		}

		m_spin_modulator = p_weapon->GetSpinModulator();
		m_tilt_modulator = p_weapon->GetTiltModulator();

		p_weapon->DrawReticle();
	}
}












}


================================================
FILE: Code/Gel/Components/HorseCameraComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       HorseCameraComponent.h
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#ifndef __COMPONENTS_HORSECAMERACOMPONENT_H__
#define __COMPONENTS_HORSECAMERACOMPONENT_H__

#include 
#include 

#include 
#include 
#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_HORSECAMERA CRCD(0xf1593757,"HorseCamera")
#define		GetHorseCameraComponent() ((Obj::CHorseCameraComponent*)GetComponent(CRC_HORSECAMERA))
#define		GetHorseCameraComponentFromObject(pObj) ((Obj::CHorseCameraComponent*)(pObj)->GetComponent(CRC_HORSECAMERA))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CHorseCameraComponent : public CBaseComponent
{
public:

	enum EStateType
	{
		STATE_NORMAL,
		STATE_AIMING,
	};
	
									CHorseCameraComponent();
    virtual							~CHorseCameraComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
	virtual void					Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	EStateType						GetState( void )	{ return m_state; }
	CHorseComponent*				GetSubject( void )	{ return mp_subject_vehicle_component; }

private:
	void							reset_camera( void );
	void							calculate_attractor_direction( void );
	void							calculate_orientation_matrix( void );
	void							calculate_position( void );
	void							get_controller_input( void );
	void							do_reticle( void );

private:

	EStateType						m_state;

	float							m_frame_length;

	float							m_spin_modulator;
	float							m_tilt_modulator;

	float							m_lookaround_heading;
	float							m_lookaround_heading_delta;
	float							m_lookaround_heading_angular_speed;
	float							m_lookaround_tilt;
	float							m_lookaround_tilt_delta;
	float							m_lookaround_tilt_angular_speed;

	CCompositeObject*				mp_subject;

	CCompositeObject*				mp_best_target;

	// Peer components.
	CInputComponent*				mp_input_component;
	CHorseComponent*				mp_subject_vehicle_component;
	
	Mth::Vector		   				m_direction;
	Mth::Vector		   				m_attractor_direction;
	
	Mth::Vector						m_pos;
	Mth::Matrix						m_orientation_matrix;
	
	float							m_offset_height;
	float							m_offset_distance;
	float							m_offset_tilt;
	
	float							m_alignment_rate;
};

}

#endif


================================================
FILE: Code/Gel/Components/HorseComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       HorseComponent.cpp
//* OWNER:          Dan Nelson
//* CREATION DATE:  1/31/3
//****************************************************************************

#include 
#include 
#include 
										
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#define MESSAGE(a) { printf("M:%s:%i: %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPI(a) { printf("D:%s:%i: " #a " = %i\n", __FILE__ + 15, __LINE__, a); }
#define DUMPF(a) { printf("D:%s:%i: " #a " = %g\n", __FILE__ + 15, __LINE__, a); }
#define DUMPE(a) { printf("D:%s:%i: " #a " = %e\n", __FILE__ + 15, __LINE__, a); }
#define DUMPS(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPP(a) { printf("D:%s:%i: " #a " = %p\n", __FILE__ + 15, __LINE__, a); }
#define DUMPV(a) { printf("D:%s:%i: " #a " = %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z]); }
#define DUMP4(a) { printf("D:%s:%i: " #a " = %g, %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z], (a)[W]); }
#define DUMPM(a) { DUMP4(a[X]); DUMP4(a[Y]); DUMP4(a[Z]); DUMP4(a[W]); }
#define MARK { printf("K:%s:%i: %s\n", __FILE__ + 15, __LINE__, __PRETTY_FUNCTION__); }
#define PERIODIC(n) for (static int p__ = 0; (p__ = ++p__ % (n)) == 0; )

// THOUGHTS:
//
// - BUG: solve negative collision depth issue for rect collisions; must project collision normal into rect plane before calculating depth (i think)
// - BUG: bad wipeout behavior; IDEAS:
//   - use only line feelers
//   - turn off graivty with > 2 contacts
//   - sleep like a rigidbody
//   - compare algorithm with rigidbody
// - Triggers.
// - Lock motion if very slow and there's no input.
// - Vehicle camera needs to reports its old position to get sounds' pitch correctly.
//
// - two center of masses (collision at zero and suspension) is an issue; actual rotation is done around zero; what sort of poor behavior does this cause
//   when the car is on two wheels? what way is there to retain stable suspension behavior but use a common center of mass?	perhaps simply damp rotational
//   impulses due to the suspension; this may help with the "vert, linear-to-angular energy" freak-out issue as well
// - body-body collisions; treat as boxes
// - prehaps reduce wheel friction when very near vertical to prevent wall riding
// - states: ASLEEP, AWAKE (full brake; rigidbody), DRIVEN, DRONE (?)
// - vertical side rectangle feelers
// - wheel camber  extracted from model
// - side slippage when stopped on hills

namespace Obj
{
	
CHorseComponent::SCollisionPoint CHorseComponent::sp_collision_points[4 * (Nx::MAX_NUM_2D_COLLISIONS_REPORTED + 1)];
								   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CHorseComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CHorseComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CHorseComponent::CHorseComponent() : CBaseComponent()
{
	SetType( CRC_HORSE );
	
	mp_input_component				= NULL;
	mp_animation_component			= NULL;
	mp_movable_contact_component	= NULL;

	mp_collision_cache = Nx::CCollCacheManager::sCreateCollCache();
	
	m_draw_debug_lines = 0;

	mp_rider						= NULL;

	m_vel.Set( 0.0f, 0.0f, 0.0f, 0.0f );
	m_upward.Set( 0.0f, 1.0f, 0.0f, 0.0f );
	m_horizontal_vel.Set( 0.0f, 0.0f, 0.0f, 0.0f );
	m_control_direction					= m_horizontal_vel;
	m_primary_air_direction				= m_horizontal_vel;
	m_uncontrollable_air_horizontal_vel = m_horizontal_vel;

	m_orientation_matrix.Ident();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CHorseComponent::~CHorseComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::InitFromStructure( Script::CStruct* pParams )
{
	m_script_drag_factor	= 1.0f;

	// extract constant parameters from struct; parameters not included in the script are left unchanged
	
	// toggles through the debug line drawing states
	if (pParams->ContainsFlag(CRCD(0x935ab858, "debug")))
	{
		m_draw_debug_lines = ++m_draw_debug_lines % 3;
	}
	else if (pParams->ContainsFlag(CRCD(0x751da48b, "no_debug")))
	{
		m_draw_debug_lines = 0;
	}

	m_pos = GetObject()->GetPos();

	if( pParams->ContainsComponentNamed(CRCD(0x7f261953, "pos" )))
	{
		pParams->GetVector(CRCD(0x7f261953, "pos"), &m_pos);
		GetObject()->SetPos(m_pos);
	}

	// Grab a pointer to the skeleton.
	mp_skeleton_component = static_cast< CSkeletonComponent* >(GetObject()->GetComponent(CRC_SKELETON));
	Dbg_MsgAssert(mp_skeleton_component, ("HorseComponent has no peer skeleton component."));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure( pParams );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::Finalize( void )
{
	mp_model_component				= GetModelComponentFromObject( GetObject());
	mp_input_component				= GetInputComponentFromObject( GetObject());
	mp_animation_component			= GetAnimationComponentFromObject( GetObject());
	mp_movable_contact_component	= GetMovableContactComponentFromObject( GetObject());

	Dbg_Assert( mp_model_component );
	Dbg_Assert( mp_input_component );
	Dbg_Assert( mp_animation_component );
	Dbg_Assert( mp_movable_contact_component );

	// Enable blending for the Horse by default.
	mp_animation_component->EnableBlending( true );

	m_display_slerp_matrix = GetObject()->GetMatrix();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::Update( void )
{
	// zero the frame event
	m_last_frame_event = m_frame_event;
	m_frame_event = 0;
	
	// get input
    get_controller_input();
	
	// extract initial state for this frame from the object
	m_frame_start_pos = m_pos = GetObject()->GetPos();
	
	m_horizontal_vel = GetObject()->GetVel();
	m_horizontal_vel[Y] = 0.0f;
	m_vertical_vel = GetObject()->GetVel()[Y];
	
	// note that m_facing and m_upward will often not be orthogonal, but will always span a plan
	
	// generally straight up, but now after a transition from skating
	m_upward = GetObject()->GetMatrix()[Y];
	
	m_facing = GetObject()->GetMatrix()[Z];
	m_facing[Y] = 0.0f;
	float length = m_facing.Length();
	if (length < 0.001f)
	{
		// upward facing orientation matrix
		m_facing = -GetObject()->GetMatrix()[Y];
		m_facing[Y] = 0.0f;
		m_facing.Normalize();
		
		// since m_upward is now in the same plan as m_facing, push m_upward up a touch
		m_upward[Y] += 0.01f;
		m_upward.Normalize();
	}
	else
	{
		m_facing /= length;
	}
	
	// Set the frame length.
	m_frame_length = Tmr::FrameLength();
	
	// go to our true Y position
	m_curb_float_height_adjusted = false;
	m_pos[Y] -= m_curb_float_height;
	
	// switch logic based on walking state
	switch (m_state)
	{
		case WALKING_GROUND:
			go_on_ground_state();
			break;

		case WALKING_AIR:
			go_in_air_state();
			break;
																	  
		case WALKING_HOP:
//			go_hop_state();
			break;
																	  
		case WALKING_HANG:
//			go_hang_state();
			break;
            
		case WALKING_LADDER:
//			go_ladder_state();
            break;
			
		case WALKING_ANIMWAIT:
//			go_anim_wait_state();
			break;
	}
	
	// the there's no curb to adjust due to, lerp down to zero
	if (!m_curb_float_height_adjusted)
	{
		m_curb_float_height = Mth::Lerp(m_curb_float_height, 0.0f, s_get_param(CRCD(0x9b3388fa, "curb_float_lerp_down_rate")) * m_frame_length);
	}
	
	// adjust back to our curb float Y position
	m_pos[Y] += m_curb_float_height;
	
	// scripts may have restarted us / switched us to skating
//	if (should_bail_from_frame()) return;
	
	// Keep the object from falling through holes in the geometry.
	if( m_state == WALKING_GROUND || m_state == WALKING_AIR )
	{
		uber_frig();
	}
	
	// rotate to upright
	lerp_upright();
	
	// setup the object based on this frame's walking
	copy_state_into_object();
	
	// Position the rider correctly, if one exists.
	position_rider();

	Dbg_Assert(m_frame_event);
	GetObject()->SelfEvent(m_frame_event);
	
	// set the animation speeds
/*	switch (m_anim_scale_speed)
	{
		case RUNNING:
			if (m_anim_standard_speed > 0.0f)
			{
				mp_animation_component->SetAnimSpeed(m_anim_effective_speed / m_anim_standard_speed, false, false);
			}
			break;
			
		case HANGMOVE:
			mp_animation_component->SetAnimSpeed(m_anim_effective_speed / s_get_param(CRCD(0xd77ee881, "hang_move_speed")), false, false);
			break;
					
		case LADDERMOVE:
			mp_animation_component->SetAnimSpeed(m_anim_effective_speed / s_get_param(CRCD(0xab2db54, "ladder_move_speed")), false, false);
			break;
	
		default:
			break;
	}
*/
	
	// camera controls
	// NOTE: script parameters
/*
	switch (m_frame_event)
	{
		case CRCC(0xf41aba21, "Hop"):
			mp_camera_component->SetOverrides(m_facing, 0.05f);
			break;
		
		case CRCC(0x2d9815c3, "HangMoveLeft"):
		{
			Mth::Vector facing = m_facing;
			facing.RotateY(-0.95f);
			mp_camera_component->SetOverrides(facing, 0.05f);
			break;
		}
			
		case CRCC(0x279b1f0b, "HangMoveRight"):
		{
			Mth::Vector facing = m_facing;
			facing.RotateY(0.95f);
			mp_camera_component->SetOverrides(facing, 0.05f);
			break;
		}
					
		case CRCC(0x4194ecca, "Hang"):
			mp_camera_component->SetOverrides(m_facing, 0.05f);
			break;
		
		case CRCC(0xc84243da, "Ladder"):
		case CRCC(0xaf5abc82, "LadderMoveUp"):
		case CRCC(0xfec9dded, "LadderMoveDown"):
			mp_camera_component->SetOverrides(m_facing, 0.05f);
			break;
					
		case CRCC(0x4fe6069c, "AnimWait"):
			if (m_anim_wait_camera_mode == AWC_CURRENT)
			{
				mp_camera_component->SetOverrides(m_facing, 0.05f);
			}
			else
			{
				mp_camera_component->SetOverrides(m_drift_goal_facing, 0.05f);
			}
			break;
	
		default:
			mp_camera_component->UnsetOverrides();
			break;
	}
*/
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent::EMemberFunctionResult CHorseComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | Walk_Ground |
		case CRCC( 0x893213e5, "Walk_Ground" ):
		{
			return m_state == WALKING_GROUND ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;
		}
	
		case CRCC( 0x88bd4924, "Horse_GetSpeedScale" ):
		{
			uint32 checksum;
			if( m_anim_effective_speed < s_get_param( CRCD(0xf3649996, "max_slow_walk_speed")))
			{
				checksum = CRCD(0x20c5a299,"HORSE_WALK");
			}
			else if( m_anim_effective_speed < s_get_param( CRCD(0x6a5805d8, "max_fast_walk_speed")))
			{
				checksum = CRCD(0x8a354e68,"HORSE_TROT");
			}
			else if( m_anim_effective_speed < s_get_param( CRCD(0x1c94cc9c, "max_slow_run_speed")))
			{
				checksum = CRCD(0x1bee30eb,"HORSE_CANTER");
			}
			else
			{
				checksum = CRCD(0x2ca2c118,"HORSE_GALLOP");
			}
			pScript->GetParams()->AddChecksum( CRCD( 0x92c388f, "SpeedScale" ), checksum );
			break;
		}

		// @script | Horse_Jump |
		case CRCC( 0xae7deda, "Horse_Jump" ):
		{
			// Jump strength scales with the length the jump button has been held.
			jump( Mth::Lerp( s_get_param( CRCD( 0x246d0bf3, "min_jump_factor" )), 1.0f,	Mth::ClampMax( mp_input_component->GetControlPad().m_x.GetPressedTime() / s_get_param( CRCD( 0x12333ebd, "hold_time_for_max_jump" )), 1.0f )));
			break;
		}

		// @script | Vehicle_Kick | kicks the vehicle with a force and torque
		case CRCC(0x93b713a6, "Vehicle_Kick"):
		{
			break;
		}

		// @script : Vehicle_Reset | reset any parameters of the vehicle
		case CRCC(0xcdcdc05e, "Vehicle_Reset"):
			RefreshFromStructure(pParams);
			Finalize();
			break;
			
		// @script : Vehicle_MoveToRestart | teleport the vehicle to the restart node
		case CRCC(0x4b0b27dd, "Vehicle_MoveToRestart"):
		{
			uint32 node_name_checksum;
			pParams->GetChecksum(NO_NAME, &node_name_checksum, Script::ASSERT);
			MoveToNode(SkateScript::GetNode(SkateScript::FindNamedNode(node_name_checksum, Script::ASSERT)));
			break;
		}
			
		// @script : Vehicle_PlaceBeforeCamera | moves the object before the active camera
		case CRCC(0xc33608e4, "Vehicle_PlaceBeforeCamera"):
		{
			Gfx::Camera* camera = Nx::CViewportManager::sGetActiveCamera(0);
			if (camera)
			{
				Mth::Vector& cam_pos = camera->GetPos();
				Mth::Matrix& cam_mat = camera->GetMatrix();

				m_pos = cam_pos;
				m_pos += cam_mat[Y] * 12.0f * 12.0f;
				m_pos -= cam_mat[Z] * 12.0f * 12.0f;
				GetObject()->SetPos(m_pos);
				
				m_orientation_matrix[X] = -cam_mat[X];
				m_orientation_matrix[Y] = cam_mat[Y];
				m_orientation_matrix[Z] = -cam_mat[Z];
				GetObject()->SetMatrix( m_orientation_matrix );
			}
			break;
		}

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CHorseComponent::GetDebugInfo ( Script::CStruct *p_info )
{
	Dbg_MsgAssert(p_info, ("NULL p_info sent to CHorseComponent::GetDebugInfo"));

	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::MoveToNode( Script::CStruct* p_node )
{
	// move to the position relative to the given node that a ped car would be at
	Mth::Vector restart_pos;
	SkateScript::GetPosition(p_node, &restart_pos);
	
	Mth::Vector restart_angles;
	SkateScript::GetAngles( p_node, &restart_angles );
	Mth::Matrix restart_matrix;
	restart_matrix.SetFromAngles( restart_angles );
	
	// find ground hieght
	CFeeler feeler( restart_pos + Mth::Vector( 0.0f, 24.0f, 0.0f, 0.0f ), restart_pos + Mth::Vector( 0.0f, -800.0f, 0.0f, 0.0f ));
	if (feeler.GetCollision())
	{
		restart_pos[Y] = feeler.GetPoint()[Y];
	}
	
	// move the car to the restart position and allow it to settle on its suspension
	m_pos = restart_pos;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 CHorseComponent::GetAnimation( void )
{
	return mp_animation_component->GetCurrentSequence();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CHorseComponent::AcceptRiderMount( CCompositeObject* p_rider )
{
	// This is where we want to enable the associated camera.
	CHorseCameraComponent*	p_cam_component = GetHorseCameraComponentFromObject( GetObject());
	if( p_cam_component )
	{
		p_cam_component->Suspend( false );
	}

	// Also want to set the horse camera as active.
	Script::RunScript( CRCD( 0xb08469a2, "MountHorse" ));

	mp_rider = p_rider;

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CHorseComponent::ShouldUpdateCamera( void )
{
	if( mp_rider )
		return true;
	else
		return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CHorseComponent::AcceptRiderDismount( CCompositeObject* p_rider )
{
	// This is where we want to disable the associated camera.
	CHorseCameraComponent*	p_cam_component = GetHorseCameraComponentFromObject( GetObject());
	if( p_cam_component )
	{
		p_cam_component->Suspend( true );
	}

	// Also want to set the horse camera as inactive.
	Script::RunScript( CRCD( 0x400b95f5, "DismountHorse" ));

	mp_rider = NULL;
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::draw_debug_rendering (   ) const
{
	if (m_draw_debug_lines)
	{
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::account_for_movable_contact( void )
{
/*
	if (!mp_movable_contact_component->UpdateContact(m_pos))
		return;
	
	m_pos += mp_movable_contact_component->GetContact()->GetMovement();
	
	if (mp_movable_contact_component->GetContact()->IsRotated())
	{
		m_facing = mp_movable_contact_component->GetContact()->GetRotation().Rotate(m_facing);
		if (m_facing[Y] != 0.0f)
		{
			DUMPF(m_facing[Y]);
			m_facing[Y] = 0.0f;
			m_facing.Normalize();
		}
	}
*/
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::jump( float strength )
{
	// switch to air state and give the object an upwards velocity
	
	// if we're jumping from the ground, trip the ground's triggers
	if (m_state == WALKING_GROUND && m_last_ground_feeler_valid)
	{
//		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, m_last_ground_feeler);
//		if (should_bail_from_frame()) return;
	}
	
	m_primary_air_direction = m_facing;
	
	// Called by script from outside of the component update, so m_vertical_vel is not used.
	GetObject()->GetVel()[Y] = strength * s_get_param( CRCD( 0x63d62a21, "jump_velocity" ));
	
	leave_movable_contact_for_air( GetObject()->GetVel(), GetObject()->GetVel()[Y] );
	
	set_state( WALKING_AIR );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::setup_collision_cache( void )
{
	float horizontal_reach = 1.0f + s_get_param(CRCD(0x99978d2b, "feeler_length"));
	float vertical_height = 1.0f + s_get_param(CRCD(0x9ea1974a, "walker_height"));;
	float vertical_depth = 1.0f + s_get_param(CRCD(0xaf3e4251, "snap_down_height"));
	
	Mth::CBBox bbox(
		GetObject()->GetPos() - Mth::Vector(horizontal_reach, vertical_depth, horizontal_reach, 0.0f),
		GetObject()->GetPos() + Mth::Vector(horizontal_reach, vertical_height, horizontal_reach, 0.0f)
	);
	
	mp_collision_cache->Update(bbox);
	CFeeler::sSetDefaultCache(mp_collision_cache);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float CHorseComponent::calculate_desired_speed( void )
{
	// forced run
//	adjust_control_for_forced_run();
	
//	float walk_point = s_get_param(CRCD(0xc1528f7f, "walk_point"));

	if( m_control_magnitude > 0.0f )
	{
		// Stick is in non-zero position, so adjust target speed.
		// We may want two sets of these, one for speeding up, one for slowing down.
		float speed_ka = 1.0f; /* Script::GetFloat( CRCD( 0xcac0c1d4, "GunslingerLookaroundTiltKa" ), Script::ASSERT ); */
		float speed_ea = 2.0f; /* Script::GetFloat( CRCD( 0x5443ec5a, "GunslingerLookaroundTiltEa" ), Script::ASSERT ); */

		float speed_ks = 1.0f; /* Script::GetFloat( CRCD( 0x3979b09c, "GunslingerLookaroundTiltKs" ), Script::ASSERT ); */
		float speed_es = 1.0f; /* Script::GetFloat( CRCD( 0xa7fa9d12, "GunslingerLookaroundTiltEs" ), Script::ASSERT ); */

		float target = m_control_direction[X] * m_control_magnitude;

		// Calculate acceleration.
		float a = speed_ka * powf( Mth::Abs( target ), speed_ea ) * (( target > 0.0f ) ? 1.0f : ( target < 0.0f ) ? -1.0f : 0.0f );

		// Calculate max speed.
		float s = speed_ks * powf( Mth::Abs( target ), speed_es ) * (( target > 0.0f ) ? 1.0f : ( target < 0.0f ) ? -1.0f : 0.0f );

		m_target_speed_adjustment += a;

		if( s == 0.0f )
		{
			m_target_speed_adjustment = 0.0f;
		}
		else if((( s > 0.0f ) && ( m_target_speed_adjustment > s )) || (( s < 0.0f ) && ( m_target_speed_adjustment < s )))
		{
			m_target_speed_adjustment = s;
		}

		m_target_speed += m_target_speed_adjustment * m_frame_length;

		// Constrain to [0.0, 1.0] limits.
		m_target_speed = ( m_target_speed < 0.0f ) ? 0.0f : (( m_target_speed > 1.0f ) ? 1.0f : m_target_speed );
	}

	return s_get_param( CRCD( 0xcc461b87, "run_speed" )) * m_target_speed;

/*
	{
		float walk_point			= s_get_param(CRCD(0xc1528f7f, "walk_point"));
		float velocity_magnitude	= m_control_direction[X] * m_control_magnitude;

		if( velocity_magnitude <= walk_point )
		{
			m_run_toggle = false;
			return Mth::LinearMap( 0.0f, s_get_param( CRCD( 0x79d182ad, "walk_speed" )), velocity_magnitude, 0.3f, walk_point );
		}
		else
		{
			m_run_toggle = true;
			return Mth::LinearMap( s_get_param( CRCD( 0x79d182ad, "walk_speed" )), s_get_param( CRCD( 0xcc461b87, "run_speed" )), velocity_magnitude, walk_point, 1.0f );
		}
	}
*/
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float CHorseComponent::adjust_desired_speed_for_slope( float desired_speed )
{
	// Slow velocity up and down slopes.
	
	// skip if there is no appreciable slope
	if (m_ground_normal[Y] > 0.95f) return desired_speed;
	
	// skip if not running
	if (desired_speed <= s_get_param(CRCD(0x79d182ad, "walk_speed"))) return desired_speed;
	
	// calculate a horizontal vector up the slope
	Mth::Vector up_slope = m_ground_normal;
	up_slope[Y] = 0.0f;
	up_slope.Normalize();
	
	// horizontal factor of velocity if the velocity were pointing along the slope (instead of along the horizontal)
	float movement_factor = m_ground_normal[Y];
	
	// factor of velocity pointing up the slope
	float dot = Mth::Abs(Mth::DotProduct(m_facing, up_slope));
	
	// scale the up-the-slope element of velocity based on the slope strength
	return (1.0f - dot) * desired_speed + dot * movement_factor * desired_speed;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::calculate_horizontal_speed_and_facing( float &horizontal_speed )
{
	// calculate user's desired speed
	float desired_speed = calculate_desired_speed();
	
	// adjust speed by the script set drag factor
	desired_speed *= m_script_drag_factor;
			   	
	// setup frame's event
	if (desired_speed <= s_get_param(CRCD(0x74e8227d, "max_stand_speed")))
	{
		m_frame_event = CRCD(0x9b46e749, "Stand");
	}
	else
	{
		m_frame_event = CRCD(0xaf895b3f, "Run");
	}

	bool special_acceleration = false;
	
	// HorseComponent version.
	// The required rate of turn depends on the x axis component of the control direction.
//	if( horizontal_speed < s_get_param( CRCD( 0x52582d5b, "max_rotate_in_place_speed" )))
	if( 1 )
	{
		// Low speed rotate to desired orientation with no speed change.
		m_delta_angle = Mth::DegToRad( s_get_param( CRCD( 0xb557804b, "rotate_in_place_rate" ))) * -m_control_direction[Z] * m_control_magnitude * m_frame_length;
		if( !m_run_toggle )
		{
			m_delta_angle *= s_get_param( CRCD( 0x7b446c98, "walk_rotate_factor" ));
		}
			
		if( m_delta_angle != 0.0f )
		{
			float cos_delta_angle	= cosf( m_delta_angle );
			float sin_delta_angle	= sinf( m_delta_angle );
			float adjusted_vel		= cos_delta_angle * m_facing[X] + sin_delta_angle * m_facing[Z];

			m_facing[Z]				= -sin_delta_angle * m_facing[X] + cos_delta_angle * m_facing[Z];
			m_facing[X]				= adjusted_vel;
			
			// check for overturn
//			if (left_turn != (-m_facing[Z] * m_control_direction[X] + m_facing[X] * m_control_direction[Z] < 0.0f))
//			{
//				m_facing = m_control_direction;
//			}
			
			// no acceleration until we reach the desired orientation
//			special_acceleration = true;
			
			// setup the event
			m_frame_event = ( m_delta_angle < 0.0f ) ? CRCD( 0xf28adbfc, "RotateLeft" ) : CRCD( 0x912220f8, "RotateRight" );
		}
	}
	
	if (special_acceleration) return;
	
	// Store desired speed for animation speed scaling.
	m_anim_effective_speed = desired_speed;
	
	// adjust desired speed for slope
	desired_speed = adjust_desired_speed_for_slope( desired_speed );
	
	// linear acceleration; exponential deceleration
	if (horizontal_speed > desired_speed)
	{
		horizontal_speed = Mth::Lerp(horizontal_speed, desired_speed, s_get_param(CRCD(0xacfa4e0c, "decel_factor")) * m_frame_length);
		
		if (desired_speed == 0.0f)
		{
			if (horizontal_speed > s_get_param(CRCD(0x79d182ad, "walk_speed")))
			{
				m_frame_event = CRCD(0x1d537eff, "Skid");
			}
			else if (m_last_frame_event == CRCD(0x1d537eff, "Skid") && horizontal_speed > s_get_param(CRCD(0x311d02b2, "stop_skidding_speed")))
			{
				m_frame_event = CRCD(0x1d537eff, "Skid");
			}
		}
	}
	else
	{
		if (m_run_toggle)
		{
			horizontal_speed += s_get_param(CRCD(0x4f47c998, "run_accel_rate")) * m_frame_length;
		}
		else
		{
			horizontal_speed += s_get_param(CRCD(0x6590a49b, "walk_accel_rate")) * m_frame_length;
		}
		if (horizontal_speed > desired_speed)
		{
			horizontal_speed = desired_speed;
		}
	}
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CHorseComponent::adjust_horizontal_vel_for_environment( bool wall_push_active )
{
	// We send out feeler rays to find nearby walls.  We limit velocity to be flush with the first wall found.  If two or more non-parallel walls
	// are found, velocity is zeroed.
	
	float feeler_length = s_get_param(CRCD(0x99978d2b, "feeler_length"));
	float feeler_height = s_get_param(CRCD(0x6da7f696, "feeler_height"));
	
	CFeeler feeler;
	
	bool contact = false;
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		// setup the the feeler
		
		if (n == vNUM_FEELERS)
		{
			// final feeler is for air state only and is at the feet; solves situations in which the feet impact with vertical surfaces which the
			// wall feelers are too high to touch
			if (m_state != WALKING_AIR)
			{
				mp_contacts[vNUM_FEELERS].in_collision = false;
				continue;
			}
			
			feeler.m_start = m_pos;
			feeler.m_end = m_pos + m_horizontal_vel * m_frame_length;
			feeler.m_end[Y] += m_vertical_vel * m_frame_length + 0.5f * -s_get_param(CRCD(0xa5e2da58, "gravity")) * Mth::Sqr(m_frame_length);
		}
		else
		{
			feeler.m_start = m_pos;
			feeler.m_start[Y] += feeler_height;
			feeler.m_end = m_pos + feeler_length * calculate_feeler_offset_direction(n);
			feeler.m_end[Y] += feeler_height;
		}
		
		mp_contacts[n].in_collision = feeler.GetCollision();
		
		if (!mp_contacts[n].in_collision)
		{
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(0, 0, 255, 1);
			}
			#endif
			continue;
		}
		
		contact = true;
		
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(255, 0, 0, 1);
		}
		#endif

		// grab the horizontal normal of the contacted wall
		mp_contacts[n].normal = feeler.GetNormal();
		mp_contacts[n].normal[Y] = 0.0f;
		mp_contacts[n].normal.Normalize();
		
		// grab the distance of the contact from the object
		mp_contacts[n].distance = feeler.GetDist();
		
		// if we're on the moving object, don't count its movement when doing collision detection, as the walker's velocity is already measured
		// relative to its movable contact's
		if (feeler.IsMovableCollision()
			&& (!mp_movable_contact_component->HaveContact() || mp_movable_contact_component->GetContact()->GetObject() != feeler.GetMovingObject()))
		{
			mp_contacts[n].movement = Mth::DotProduct(feeler.GetMovingObject()->GetVel(), mp_contacts[n].normal);
		}
		else
		{
			mp_contacts[n].movement = 0.0f;
		}
	}
	
	// check for wall push
	if (m_state == WALKING_GROUND)
	{
//		if (wall_push_active && check_for_wall_push())
		if (false)
		{
			// if we're wall pushing, we may decide to switch states based on our environment
			
			if (Tmr::ElapsedTime(m_wall_push_test.test_start_time) > s_get_param(CRCD(0x928e6775, "hop_delay")))
			{
//				if (maybe_climb_up_ladder() || maybe_hop_to_hang() || maybe_jump_low_barrier()) return false;
			}
			else if (Tmr::ElapsedTime(m_wall_push_test.test_start_time) > s_get_param(CRCD(0x38d36700, "barrier_jump_delay")))
			{
//				if (maybe_climb_up_ladder() || maybe_jump_low_barrier()) return false;
			}
		}
//		else if (mp_input_component->GetControlPad().m_R1.GetPressed() && !m_ignore_grab_button)
//		{
//			if (maybe_climb_up_ladder(true)) return false;
//		}
	}
	
	if (!contact) return false;
	
	// push away from walls
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		if (!mp_contacts[n].in_collision) continue;
		                
		if (mp_contacts[n].distance < s_get_param(CRCD(0xa20c43b7, "push_feeler_length")) / feeler_length)
		{
			m_pos += s_get_param(CRCD(0x4d16f37d, "push_strength")) * m_frame_length * mp_contacts[n].normal;
		}
	}
	
	// from here on we ignore collisions we're moving out of
	contact = false;
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		if (!mp_contacts[n].in_collision) continue;
		
		// don't count collisions we're moving out of
		if (Mth::DotProduct(mp_contacts[n].normal, m_horizontal_vel) >= mp_contacts[n].movement)
		{
			mp_contacts[n].in_collision = false;
		}
		else
		{
			contact = true;
		}
	}
	if (!contact) return false;
	
	// Now we calculate how our movement is effected by our collisions.  The movement must have a non-negative dot product with all collision normals.
	// The algorithm used should be valid for all convex environments.
	
	// if any of the colllision normals are more than right angles to one another, no movement is possible
	// NOTE: not valid with movable contacts; could cause jerky movement in corners where walls are movable
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		if (!mp_contacts[n].in_collision) continue;
		for (int m = n + 1; m < vNUM_FEELERS + 1; m++)
		{
			if (!mp_contacts[m].in_collision) continue;
			if (Mth::DotProduct(mp_contacts[n].normal, mp_contacts[m].normal) <= 0.0f)
			{
				m_horizontal_vel.Set();
				m_anim_effective_speed = Mth::Min(s_get_param(CRCD(0xbd6a05d, "min_anim_run_speed")), m_anim_effective_speed);
				return true;
			}
		}
	}
	
	// direction of proposed movement
	Mth::Vector movement_direction = m_horizontal_vel;
	movement_direction.Normalize();
	
	Mth::Vector adjusted_vel = m_horizontal_vel;
	
	// loop over the contacts (from backward to forward)
	const int contact_idxs[] = { 7, 4, 3, 5, 2, 6, 1, 0 };
	for (int i = 0; i < vNUM_FEELERS + 1; i++)
	{
		int n = contact_idxs[i];
		
		if (!mp_contacts[n].in_collision) continue;
		
		// check to see if the movement still violates this constraint
		float normal_vel = Mth::DotProduct(adjusted_vel, mp_contacts[n].normal);
		if (normal_vel >= mp_contacts[n].movement) continue;
		
		// adjust the movement to the closest direction allowed by this contraint
		adjusted_vel -= (normal_vel - mp_contacts[n].movement) * mp_contacts[n].normal;
		
		// if the mvoement direction no longer points in the direction of the proposed movement, no movement occurs
		if (Mth::DotProduct(adjusted_vel, m_horizontal_vel) <= 0.0f)
		{
			m_horizontal_vel.Set();
			m_anim_effective_speed = Mth::Min(s_get_param(CRCD(0xbd6a05d, "min_anim_run_speed")), m_anim_effective_speed);
			return true;
		}
	}
	
	// insure that the adjusted velocity in the final direction is not larger than the projection of the initial velocity into that direction
	float adjusted_speed = adjusted_vel.Length();
	Mth::Vector adjusted_vel_direction = adjusted_vel;
	adjusted_vel_direction *= 1.0f / adjusted_speed;
	float projected_vel = Mth::DotProduct(m_horizontal_vel, adjusted_vel_direction);
	
	if (adjusted_speed > projected_vel)
	{
		adjusted_vel = adjusted_vel_direction * projected_vel;
	}
	
	// only the velocity along the movement direction is retained
	m_horizontal_vel = adjusted_vel;
	
	float final_horiz_vel = m_horizontal_vel.Length();
	if (m_anim_effective_speed > s_get_param(CRCD(0xbd6a05d, "min_anim_run_speed")))
	{
		m_anim_effective_speed = final_horiz_vel;
		m_anim_effective_speed = Mth::Max(s_get_param(CRCD(0xbd6a05d, "min_anim_run_speed")), m_anim_effective_speed);
	}
	
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::adjust_facing_for_adjusted_horizontal_vel( void  )
{
	// We adjust facing due to adjustment in horizontal velocity due to environment.
	// Basically, we want to object to turn to face the velocity that the environment has forced upon it.
	
	// IDEA: shift to basing turn amount on angle difference and not speed
	
	float horizontal_speed = m_horizontal_vel.Length();
	
	// the new facing is in the direction of our adjusted velocity
	Mth::Vector new_facing = m_horizontal_vel;
	new_facing.Normalize();
	
	// Smoothly transition between no wall turning to full wall turning.
	float turn_ratio;
	if( horizontal_speed > s_get_param( CRCD( 0xe6c1cd0d, "max_wall_turn_speed_threshold" )))
	{
		turn_ratio = s_get_param( CRCD( 0x7a583b9b, "wall_turn_factor" )) * m_frame_length;
	}
	else
	{
		turn_ratio = Mth::LinearMap(	0.0f,
										s_get_param( CRCD( 0x7a583b9b, "wall_turn_factor")) * m_frame_length,
										horizontal_speed,
//										s_get_param( CRCD( 0x0515a933, "wall_turn_speed_threshold")),
										0.0f,
										s_get_param( CRCD( 0xe6c1cd0d, "max_wall_turn_speed_threshold" )));
	}
	
	// Exponentially approach new facing.
	if( turn_ratio >= 1.0f )
	{
		m_facing = new_facing;
	}
	else
	{
		m_facing = Mth::Lerp( m_facing, new_facing, turn_ratio );
		m_facing.Normalize();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::go_on_ground_state( void )
{
	account_for_movable_contact();
	
	setup_collision_cache();
	
	// Calculate initial horizontal speed.
	float horizontal_speed = m_horizontal_vel.Length();
	
	calculate_horizontal_speed_and_facing( horizontal_speed );
	
	// Calculate this frame's movement.
	m_horizontal_vel = horizontal_speed * m_facing;
	
	// Prevent movement into walls.
	if( adjust_horizontal_vel_for_environment( true ))
	{
		// Turn to face newly adjusted velocity.
		adjust_facing_for_adjusted_horizontal_vel();
	}
	
	// If we are wall pushing, we may have decided to switch states during adjust_horizontal_vel_for_environment based on our environment.
//	if (m_state != WALKING_GROUND || should_bail_from_frame())
	if( m_state != WALKING_GROUND )
	{
		CFeeler::sClearDefaultCache();
		return;
	}
	
	// Apply movement for this frame.
	m_pos += m_horizontal_vel * m_frame_length;
	
	// Snap up and down curbs and perhaps switch to air.
	respond_to_ground();

//	if (m_state != WALKING_GROUND || should_bail_from_frame())
	if( m_state != WALKING_GROUND )
	{
		CFeeler::sClearDefaultCache();
		return;
	}
	
	adjust_curb_float_height();
	
	// Deal with intelligent path following.
	do_path_following();

	// Ensure that we do not slip through the cracks in the collision geometry which are a side-effect of moving collidable objects.
	if (CCompositeObject* p_inside_object = mp_movable_contact_component->CheckInsideObjects(m_pos, m_frame_start_pos))
	{
		MESSAGE("WALKING_GROUND, within moving object");
		
		// allow it to push us forward, causing a bit of a stumble
		m_horizontal_vel = p_inside_object->GetVel();
		m_horizontal_vel[Y] = 0.0f;
		m_vertical_vel = 0.0f;
		
		float speed_sqr = m_horizontal_vel.LengthSqr();
		if (speed_sqr > (10.0f * 10.0f))
		{
			m_facing = m_horizontal_vel * (1.0f / sqrtf(speed_sqr));
		}
	}
	
	CFeeler::sClearDefaultCache();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::go_in_air_state( void )
{
	setup_collision_cache();
	
	// default air event
	m_frame_event = CRCD(0x439f4704, "Air");
	
	// user control of horizontal velocity
	control_horizontal_vel();
	
	// prevent movement into walls
	adjust_horizontal_vel_for_environment(false);
//	if (should_bail_from_frame()) return;
	
	// check for head bonking
	adjust_vertical_vel_for_ceiling();
	
	// apply movement and acceleration for this frame
	m_pos += m_horizontal_vel * m_frame_length;
	m_pos[Y] += m_vertical_vel * m_frame_length + 0.5f * -s_get_param(CRCD(0xa5e2da58, "gravity")) * Mth::Sqr(m_frame_length);
	
	m_vertical_vel += -s_get_param(CRCD(0xa5e2da58, "gravity")) * m_frame_length;
	
	// see if we've landed yet
	check_for_landing(m_frame_start_pos, m_pos);
//	if (m_state != WALKING_AIR || should_bail_from_frame()) return;
	if (m_state != WALKING_AIR ) return;
	
	// maybe grab a rail; delay regrabbing of hang rails
//	if (mp_input_component->GetControlPad().m_R1.GetPressed() && !m_ignore_grab_button
//		&& ((m_previous_state != WALKING_HANG && m_previous_state != WALKING_LADDER) || Tmr::ElapsedTime(m_state_timestamp) > s_get_param(CRCD(0xe6e0c0a4, "rehang_delay"))))
//	{
//		if (m_previous_state == WALKING_LADDER)
//		{
//			// can't regrab ladders
//			if (maybe_grab_to_hang(m_frame_start_pos, m_pos))
//			{
//				CFeeler::sClearDefaultCache();
//				return;
//			}
//		}
//		else
//		{
//			if (maybe_grab_to_hang(m_frame_start_pos, m_pos) || maybe_grab_to_ladder(m_frame_start_pos, m_pos))
//			{
//				CFeeler::sClearDefaultCache();
//				return;
//			}
//		}
//	}
	
//	if (mp_input_component->GetControlPad().m_triangle.GetPressed())
//	{
//		if (maybe_stick_to_rail())
//		{
//			CFeeler::sClearDefaultCache();
//			return;
//		}
//	}
	
	// insure that we do not slip through the cracks in the collision geometry which are a side-effect of moving collidable objects
	Mth::Vector previous_pos = m_pos;
	if (CCompositeObject* p_inside_object = mp_movable_contact_component->CheckInsideObjects(m_pos, m_frame_start_pos))
	{
		MESSAGE("WALKING_AIR, within moving object");
		
		m_horizontal_vel.Set();
		m_vertical_vel = 0.0f;
		check_for_landing(m_pos, previous_pos);
	}
	
	CFeeler::sClearDefaultCache();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::get_controller_input( void )
{
	// If no rider, ignore input.
	if( mp_rider == NULL )
	{
		// Set it as if we are trying to slow downn to the max.
		m_control_direction[X]	= -1.0f;
		m_control_direction[Z]	= 0.0f;
		m_control_magnitude		= 1.0f;
		return;
	}

	CControlPad& control_pad = mp_input_component->GetControlPad();
	
//	Dbg_Assert(mp_camera);
	
	// rotate controller direction into camera's frame
//	Mth::Vector camera_forward = -mp_camera->m_matrix[Z];
//	camera_forward[Y] = 0.0f;
//	camera_forward.Normalize();
	
	// allow a tolerance range for pressing directly forward
	float angle = control_pad.m_leftAngle;
	if (Mth::Abs(angle) < Mth::DegToRad(s_get_param(CRCD(0x4676a268, "forward_tolerance"))))
	{
		angle = 0.0f;
	}
	
	float sin_angle = sinf(angle);
	float cos_angle = cosf(angle);
//	m_control_direction[X] = cos_angle * camera_forward[X] - sin_angle * camera_forward[Z];
//	m_control_direction[Z] = sin_angle * camera_forward[X] + cos_angle * camera_forward[Z];
	m_control_direction[X] = cos_angle;
	m_control_direction[Z] = sin_angle;
	
	// different control schemes for analog stick and d-pad
#	if 0
	if (control_pad.m_leftX == 0.0f && control_pad.m_leftY == 0.0f)
	{
		// d-pad control
		if (control_pad.m_leftLength == 0.0f)
		{
			m_control_magnitude = 0.0f;
			m_control_pegged = false;
			
			// don't reset dpad in the air
			if (m_state != WALKING_AIR)
			{
				m_dpad_used_last_frame = false;
			}
		}
		else
		{
			if (!m_dpad_used_last_frame)
			{
				m_dpad_use_time_stamp = Tmr::GetTime();
			}
			m_dpad_used_last_frame = true;
			
			if (m_state == WALKING_GROUND)
			{
				// slowly ramp up to a full run
				
				Tmr::Time elapsed_time = Tmr::ElapsedTime(m_dpad_use_time_stamp);
				
				Tmr::Time full_run_dpad_delay = static_cast< Tmr::Time >(s_get_param(CRCD(0x1832588c, "full_run_dpad_delay")));
				Tmr::Time start_run_dpad_delay = static_cast< Tmr::Time >(s_get_param(CRCD(0x2c386a43, "start_run_dpad_delay")));
				
				if (elapsed_time < start_run_dpad_delay)
				{
					m_control_magnitude = s_get_param(CRCD(0xc1528f7f, "walk_point"));
				}
				else if (elapsed_time < full_run_dpad_delay)
				{
					m_control_magnitude = Mth::SmoothMap(
						s_get_param(CRCD(0xc1528f7f, "walk_point")),
						1.0f,
						elapsed_time,
						start_run_dpad_delay,
						full_run_dpad_delay
					);
				}
				else
				{
					m_control_magnitude = 1.0f;
				}
			}
			else
			{
				m_control_magnitude = 1.0f;
			}
            m_control_pegged = true;
		}
        
		// damp dpad control directions towards forward when running
        if (m_state == WALKING_GROUND && Mth::Abs(angle) < Mth::DegToRad(90.0f + 5.0f) && (forced_run() || m_control_magnitude > s_get_param(CRCD(0xc1528f7f, "walk_point"))))
        {
//			if (forced_run() || m_control_magnitude == 1.0f)
//			{
//				m_control_direction += s_get_param(CRCD(0x3c581621, "dpad_control_damping_factor")) * camera_forward;
//			}
//			else
//			{
//				// smoothly interpolate between damping and no damping
//				m_control_direction += Mth::SmoothMap(0.0f, s_get_param(CRCD(0x3c581621, "dpad_control_damping_factor")), m_control_magnitude, s_get_param(CRCD(0xc1528f7f, "walk_point")), 1.0f)
//					* camera_forward;
//			}
			m_control_direction.Normalize();
        }
	}
	else
#	endif
	{
		// analog stick control
		m_control_magnitude = control_pad.GetScaledLeftAnalogStickMagnitude() / 0.85f;
		if (m_control_magnitude >= 1.0f)
		{
			m_control_magnitude = 1.0f;
			m_control_pegged = true;
		}
		else
		{
			m_control_pegged = false;
		}
	}
	
	// during a forward control lock, ignore all input not in the forward direction
	if (m_state == WALKING_GROUND && m_forward_control_lock)
	{
		m_control_magnitude = Mth::ClampMin(m_control_magnitude * Mth::DotProduct(m_control_direction, m_facing), 0.0f);
		m_control_direction = m_facing;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::uber_frig( void )
{
	// Ensure that we don't fall to the center of the earth, even if there are holes in the geometry.
	// Also, do lighting since we've got the feeler anyway.
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_start[Y] += 1.0f;
	feeler.m_end = m_pos;
	feeler.m_end[Y] -= FEET(400);
	
	if( feeler.GetCollision())
	{
		mp_model_component->ApplyLightingFromCollision( feeler );
		return;
	}
	
	// Teleport us back to our position at the frame's start; not pretty, but this isn't supposed to be.
	m_pos = m_frame_start_pos;
	
	// Zero our velocity too.
	m_horizontal_vel.Set();
	m_vertical_vel = 0.0f;
	
	// Set our state to ground
	set_state( WALKING_GROUND );
			  	
	m_last_ground_feeler_valid = false;
	
	m_ground_normal.Set(0.0f, 1.0f, 0.0f);

	// Reset our script state.
	m_frame_event = CRCD(0x57ff2a27, "Land");
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::lerp_upright( void )
{
//    if (m_upward[Y] == 1.0f) return;
	
//	if (m_upward[Y] > 0.999f)
//	{
//		m_upward.Set(0.0f, 1.0f, 0.0f);
//		return;
//	}
	
//	m_upward = Mth::Lerp(m_upward, Mth::Vector(0.0f, 1.0f, 0.0f), s_get_param(CRCD(0xf22c135, "lerp_upright_rate")) * Tmr::FrameLength());
//	m_upward.Normalize();

	m_upward = m_ground_normal;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::copy_state_into_object( void )
{
	// Build the object's matrix based on our facing.
	Mth::Matrix matrix;
	
	// A horse requires special consideration, since it will necessarily tilt to match it's environment along it's long axis,
	// but will need to remain upright (or very nearly so) along it's shortest axis, since otherwise given it's high center
	// of gravity, it would topple over when standing sideways on a steep slope.
	matrix[X]		= Mth::CrossProduct( m_upward, m_facing );
	matrix[X][Y]	= 0.0f;
	matrix[X].Normalize();

	matrix[Y]		= m_upward;
	matrix[Z]		= Mth::CrossProduct( matrix[X], matrix[Y] );
	matrix[Z].Normalize();

	matrix[Y]		= Mth::CrossProduct( matrix[Z], matrix[X] );

	matrix[W].Set( 0.0f, 0.0f, 0.0f, 1.0f );

	GetObject()->SetPos( m_pos );
	GetObject()->SetMatrix( matrix );

	// The display matrix is slerped towards the current matrix.
	Mth::SlerpInterpolator slerper( &m_display_slerp_matrix, &matrix );
			
	// Apply the slerping.
	slerper.getMatrix( &m_display_slerp_matrix, GetTimeAdjustedSlerp( 0.1f, m_frame_length ));
	
	GetObject()->SetDisplayMatrix( m_display_slerp_matrix );

	// Construct the object's velocity.
	GetObject()->SetVel( m_horizontal_vel );
	GetObject()->GetVel()[Y] = m_vertical_vel;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Mth::Vector CHorseComponent::calculate_feeler_offset_direction( int contact )
{
	float angle = contact * (2.0f * Mth::PI / vNUM_FEELERS);
	float cos_angle = cosf(angle);
	float sin_angle = sinf(angle);

	Mth::Vector end_offset_direction;
	end_offset_direction[X] = cos_angle * m_facing[X] - sin_angle * m_facing[Z];
	end_offset_direction[Y] = 0.0f;
	end_offset_direction[Z] = sin_angle * m_facing[X] + cos_angle * m_facing[Z];
	end_offset_direction[W] = 0.0f;
	
	return end_offset_direction;
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::adjust_curb_float_height( void )
{
	// adjust m_curb_float_height to smooth out moving up stairs
	
	// When facing a curb, we smoothly increase m_curb_float_height to the height of the curb.  When we snap up the curb, m_curb_float_height is then
	// reduced by an amount equal to the snap distance.
	// When we snap down a curb, m_curb_float_height is increased by the snap distance.  We then drop m_curb_float_height smoothly to zero.
	
	// determine appropriate direction to search for a curb
	Mth::Vector feeler_direction = m_facing;
	feeler_direction.ProjectToPlane(m_ground_normal);
	feeler_direction[Y] = Mth::ClampMin(feeler_direction[Y], 0.0f);
	feeler_direction.Normalize();
	
	// look for a curb
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_start[Y] += 0.5f;
	feeler.m_end = m_pos + s_get_param(CRCD(0x11edcc52, "curb_float_feeler_length")) * feeler_direction;
    feeler.m_end[Y] += 0.5f;
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 255, 0, 1);
	}
	#endif
	
	if (feeler.GetCollision())
	{
		// grab the distance to the curb
		float distance_to_curb = feeler.GetDist();
		
		// look up from the curb to find the curb height
		feeler.m_end = feeler.GetPoint() + 0.5f * feeler_direction;
		feeler.m_start = feeler.m_end;
		feeler.m_start[Y] = m_pos[Y] + s_get_param(CRCD(0xcee3a3e1, "snap_up_height"));
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(0, 255, 255, 1);
		}
		#endif
		
		if (feeler.GetCollision())
		{
			// calculate the m_curb_float_height we should have based on the curb height and distance
			float appropriate_curb_float_height = (1.0f - distance_to_curb) * (feeler.GetPoint()[Y] - m_pos[Y]);
			
			if (Mth::Abs(m_curb_float_height) < 0.01f && m_control_magnitude == 0.0f && m_horizontal_vel.LengthSqr() < Mth::Sqr(s_get_param(CRCD(0x227d72ee, "min_curb_height_adjust_vel"))))
			{
				// don't update the curb height if we're on the ground and standing still; this is mostly to prevent snapping up right after landing a jump
			}
			else
			{
				// lerp to the appropriate height
				m_curb_float_height = Mth::Lerp(m_curb_float_height, appropriate_curb_float_height, s_get_param(CRCD(0x856a80d3, "curb_float_lerp_up_rate")) * m_frame_length);
			}
			
			m_curb_float_height_adjusted = true;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::respond_to_ground( void )
{
	// Reset ground normal to straight up.
	m_ground_normal.Set( 0.0f, 1.0f, 0.0f, 0.0f );

	// Look for the ground below us.  If we find it, snap to it.  If not, go to air state.
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_start[Y] += s_get_param(CRCD(0xcee3a3e1, "snap_up_height"));
	feeler.m_end = m_pos;
	feeler.m_end[Y] -= s_get_param(CRCD(0xaf3e4251, "snap_down_height"));
	
	if (!feeler.GetCollision())
	{
		// no ground
		
//		if (m_last_ground_feeler_valid)
//		{
//			mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF_EDGE, m_last_ground_feeler);
//			if (should_bail_from_frame()) return;
//		}
		
		// go to air state
		set_state(WALKING_AIR);
		m_primary_air_direction = m_facing;
		leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
		m_frame_event = CRCD(0xabf1f6ac, "WalkOffEdge");
		return;
	}
	
	float snap_distance = feeler.GetPoint()[Y] - m_pos[Y];
	
	// no not send event for very small snaps
	if (Mth::Abs(snap_distance) > s_get_param(CRCD(0xd3193d8e, "max_unnoticed_ground_snap")))
	{
		GetObject()->SelfEvent(snap_distance > 0.0f ? CRCD(0x93fcf3ed, "SnapUpEdge") : CRCD(0x56e21153, "SnapDownEdge"));
	}
	
	// snap position to the ground
	m_pos[Y] = feeler.GetPoint()[Y];
	
	// adjust stair float distance
	m_curb_float_height = Mth::ClampMin(m_curb_float_height - snap_distance, 0.0f);
	
	// see if we've changed sectors
	if (m_last_ground_feeler.GetSector() != feeler.GetSector())
	{
//		if (m_last_ground_feeler_valid)
//		{
//			mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF, m_last_ground_feeler);
//			if (should_bail_from_frame()) return;
//		}
//		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_ONTO, feeler);
//		if (should_bail_from_frame()) return;
	}
	
	// stash the ground feeler so that we can trip the group's triggers at a later time
	m_last_ground_feeler = feeler;
	m_last_ground_feeler_valid = true;
	
	// set the ground normal for next frame's velocity slope adjustment
	m_ground_normal = feeler.GetNormal();

	// NOTE: need to repeat this code anywhere we enter the ground state
	mp_movable_contact_component->CheckForMovableContact(feeler);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::leave_movable_contact_for_air( Mth::Vector& horizontal_vel, float& vertical_vel )
{
	// use movement from the latest movable contact update call
	if (mp_movable_contact_component->HaveContact())
	{
		// keep track of the horizontal velocity due to our old contact
		m_uncontrollable_air_horizontal_vel = mp_movable_contact_component->GetContact()->GetObject()->GetVel();

		if (Mth::DotProduct(m_uncontrollable_air_horizontal_vel, horizontal_vel) > 0.0f)
		{
			// extra kicker; dangerous as there's no collision detection; without this slight extra movement, when we walk off the front of a movable object,
			// the object will move back under us before our next frame, and we will clip its edge and land on it
			m_pos += m_uncontrollable_air_horizontal_vel * m_frame_length;
		}
		
		// add movable contact's velocity into our launch velocity
		vertical_vel += m_uncontrollable_air_horizontal_vel[Y];
		m_uncontrollable_air_horizontal_vel[Y] = 0.0f;
		horizontal_vel += m_uncontrollable_air_horizontal_vel;
	}
	else
	{
		m_uncontrollable_air_horizontal_vel.Set();
	}

	mp_movable_contact_component->LoseAnyContact();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::control_horizontal_vel( void )
{
	// We allow user control over the object's in air velocity.  The algorithm is complicated by the fact that the forward velocity of the jump needs
	// to be accounted for when allowing for velocity adjustment.  It is assumed that the jump direction is the same as the facing.
	
	// remove uncontrollable velocity term
	m_horizontal_vel -= m_uncontrollable_air_horizontal_vel;
	
	// forced run still works in the air
//	adjust_control_for_forced_run();
	
	// adjust speed by the script set drag factor
	float adjust_magnitude = m_control_magnitude * m_script_drag_factor;
	
	// adjust velocity perpendicular to jump direction
	
	// direction perpendicular to jump direction
	Mth::Vector perp_direction( -m_primary_air_direction[Z], 0.0f, m_primary_air_direction[X], 0.0f );
	
	// desired perpendicular velocity
	float perp_desired_vel = s_get_param(CRCD(0x896c8888, "jump_adjust_speed")) * adjust_magnitude * Mth::DotProduct(m_control_direction, perp_direction);
	
	// current perpendicular velocity
	float perp_vel = Mth::DotProduct(m_horizontal_vel, perp_direction);
	
	// exponentially approach desired velocity
	perp_vel = Mth::Lerp(perp_vel, perp_desired_vel, s_get_param(CRCD(0xf085443b, "jump_accel_factor")) * m_frame_length);
		
	// adjust velocity parallel to jump direction
	
	// desired parallel velocity
	float para_desired_vel = s_get_param(CRCD(0x896c8888, "jump_adjust_speed")) * adjust_magnitude * Mth::DotProduct(m_control_direction, m_primary_air_direction);
	
	// current parallel velocity
	float para_vel = Mth::DotProduct(m_horizontal_vel, m_primary_air_direction);
	
    // if desired velocity if forward and forward velocity already exceeds adjustment velocity
	if (para_desired_vel >= 0.0f && para_vel > para_desired_vel)
	{
		// do nothing; don't slow down the jump
	}
	else
	{
		// adjust desired velocity to center around current velocity to insure that our in air stopping ability is not too amazing
		if (para_desired_vel < 0.0f && para_vel > 0.0f)
		{
			para_desired_vel += para_vel;
		}
		
		// expondentially approach desired velocity
		para_vel = Mth::Lerp(para_vel, para_desired_vel, s_get_param(CRCD(0xf085443b, "jump_accel_factor")) * m_frame_length);
	}
		
	// rebuild horizontal velocity from parallel and perpendicular components
	// Dave note - not sure about controlling the horse velocity in midair, so disable for now.
//	m_horizontal_vel = para_vel * m_primary_air_direction + perp_vel * perp_direction;
	
	// reinstitute uncontrollable velocity term
	m_horizontal_vel += m_uncontrollable_air_horizontal_vel;

}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::check_for_landing( const Mth::Vector& previous_pos, const Mth::Vector& final_pos )
{
	// See if our feet have passed through geometry.  If so, snap to it and go to ground state.
	
	CFeeler feeler;
	feeler.m_start = previous_pos;
	feeler.m_end = final_pos;
	if (!feeler.GetCollision()) return;
	
	// snap to the collision point
	m_pos = feeler.GetPoint();
	
	// zero vertical velocity
	m_vertical_vel = 0.0f;
	
	// change to ground state
	set_state(WALKING_GROUND);
	
	// stash the feeler
	m_last_ground_feeler = feeler;
	m_last_ground_feeler_valid = true;
	
	// trip any land trigger
//	mp_trigger_component->CheckFeelerForTrigger(TRIGGER_LAND_ON, m_last_ground_feeler);
//	if (should_bail_from_frame()) return;
	
	// setup our ground normal for next frames velocity slope adjustment
	m_ground_normal = feeler.GetNormal();
	
	// check for a moving contact
	mp_movable_contact_component->CheckForMovableContact(feeler);
	if (mp_movable_contact_component->HaveContact())
	{
		m_horizontal_vel -= mp_movable_contact_component->GetContact()->GetObject()->GetVel();
		m_horizontal_vel[Y] = 0.0f;
	}
	
	// retain only that velocity which is parallel to our facing and forward
	if (Mth::DotProduct(m_horizontal_vel, m_facing) > 0.0f)
	{
		m_horizontal_vel.ProjectToNormal(m_facing);
	}
	else
	{
		m_horizontal_vel.Set();
	}
	
	// clear any jump requests
	mp_input_component->GetControlPad().m_x.ClearRelease();
	
	m_frame_event = CRCD(0x57ff2a27, "Land");
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::adjust_vertical_vel_for_ceiling( void )
{
	// If we hit our head, zero vertical velocity
	
	// only worry about the ceiling if we're moving upwards
	if (m_vertical_vel <= 0.0f) return;
	
	// look for a collision up through the body to the head
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_end = m_pos;
	feeler.m_end[Y] += s_get_param(CRCD(0x9ea1974a, "walker_height"));
	if (!feeler.GetCollision()) return;
	
	// zero upward velocity
	m_vertical_vel = 0.0f;
	
	GetObject()->SelfEvent(CRCD(0x6e84acf3, "HitCeiling"));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::position_rider( void )
{
	// HACK: get player proximity checks, triggers, and the like working
//	CCompositeObject* p_skater = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID( 0 ));
//	p_skater->SetPos( GetObject()->GetPos());
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool get_collision( Mth::Vector& start, Mth::Vector& direction, float length, float* p_worst_length )
{
	CFeeler feeler;
	
	feeler.m_start			= start;
	feeler.m_end			= start + length * direction;

	// No collision as yet.
	bool collision	= false;
	*p_worst_length	= length;

	Gfx::AddDebugLine( feeler.m_start, feeler.m_end, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 1 );

	if( feeler.GetCollision())
	{
		if( Mth::Abs( feeler.GetNormal()[Y] ) < 0.2f )
		{
			collision = true;
			*p_worst_length = length * feeler.GetDist();
			Gfx::AddDebugLine( feeler.m_start, feeler.m_end, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
		}
	}

	Mth::Vector offset( -direction[Z], 0.0f, direction[X], 0.0f );

	// Try 4 inches to the left.
	feeler.m_start			+= offset * 4.0f;
	feeler.m_end			+= offset * 4.0f;
	if( feeler.GetCollision())
	{
		if( Mth::Abs( feeler.GetNormal()[Y] ) < 0.2f )
		{
			collision = true;
			if(( length * feeler.GetDist()) < *p_worst_length )
			{
				*p_worst_length = length * feeler.GetDist();
				Gfx::AddDebugLine( feeler.m_start, feeler.m_end, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
			}
		}
	}

	// Try 8 inches to the left.
	feeler.m_start			+= offset * 4.0f;
	feeler.m_end			+= offset * 4.0f;
	if( feeler.GetCollision())
	{
		if( Mth::Abs( feeler.GetNormal()[Y] ) < 0.2f )
		{
			collision = true;
			if(( length * feeler.GetDist()) < *p_worst_length )
			{
				*p_worst_length = length * feeler.GetDist();
				Gfx::AddDebugLine( feeler.m_start, feeler.m_end, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
			}
		}
	}

	// Try 12 inches to the left.
	feeler.m_start			+= offset * 4.0f;
	feeler.m_end			+= offset * 4.0f;
	if( feeler.GetCollision())
	{
		if( Mth::Abs( feeler.GetNormal()[Y] ) < 0.2f )
		{
			collision = true;
			if(( length * feeler.GetDist()) < *p_worst_length )
			{
				*p_worst_length = length * feeler.GetDist();
				Gfx::AddDebugLine( feeler.m_start, feeler.m_end, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
			}
		}
	}

	// Try 4 inches to the right.
	feeler.m_start			-= offset * 16.0f;
	feeler.m_end			-= offset * 16.0f;
	if( feeler.GetCollision())
	{
		if( Mth::Abs( feeler.GetNormal()[Y] ) < 0.2f )
		{
			collision = true;
			if(( length * feeler.GetDist()) < *p_worst_length )
			{
				*p_worst_length = length * feeler.GetDist();
				Gfx::AddDebugLine( feeler.m_start, feeler.m_end, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
			}
		}
	}

	// Try 8 inches to the right.
	feeler.m_start			-= offset * 4.0f;
	feeler.m_end			-= offset * 4.0f;
	if( feeler.GetCollision())
	{
		if( Mth::Abs( feeler.GetNormal()[Y] ) < 0.2f )
		{
			collision = true;
			if(( length * feeler.GetDist()) < *p_worst_length )
			{
				*p_worst_length = length * feeler.GetDist();
				Gfx::AddDebugLine( feeler.m_start, feeler.m_end, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
			}
		}
	}

	// Try 12 inches to the right.
	feeler.m_start			-= offset * 4.0f;
	feeler.m_end			-= offset * 4.0f;
	if( feeler.GetCollision())
	{
		if( Mth::Abs( feeler.GetNormal()[Y] ) < 0.2f )
		{
			collision = true;
			if(( length * feeler.GetDist()) < *p_worst_length )
			{
				*p_worst_length = length * feeler.GetDist();
				Gfx::AddDebugLine( feeler.m_start, feeler.m_end, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
			}
		}
	}

	return collision;
}







const float AVOID_COLLISION_SCORE			= 1000.0f;
const float	TURN_ANGLE_SCORE_MULT			= 1.0f;
const float DISTANCE_PERCENTAGE_SCORE_MULT	= 0.1f;

const float	PANIC_SCORE						= 900.0f;


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::do_path_following( void )
{
	// Get the horse camera component for this horse. Messy.
	Obj::CHorseCameraComponent* p_cam_component = static_cast( Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRC_HORSECAMERA ));
	while( p_cam_component )
	{
		if( p_cam_component->GetSubject() == this )
		{
			break;
		}
		p_cam_component = static_cast( p_cam_component->GetNextSameType());
	}

	if( p_cam_component )
	{
		if( p_cam_component->GetState() == CHorseCameraComponent::STATE_NORMAL )
		{
			// No path following in normal state. Cleanup if we were doing path following.
			if( mp_nav_nodes )
			{
				cleanup_node_based_path_following();
			}
			return;
		}

		// Did we turn this frame? If so, all bets are off...
		if( m_delta_angle != 0.0f )
		{
			// Cleanup if we were doing path following.
			if( mp_nav_nodes )
			{
				cleanup_node_based_path_following();
			}
			return;
		}

		// Are we path following? In which case, follow the path.
		if( mp_nav_nodes )
		{
			do_node_based_path_following();
			return;
		}

		// See if we are going to hit something soon if we stay on this course.
		float feeler_height = 36.0f;
		float feeler_length	= 50.0f * 12.0f;

		float		angle		= 0.0f;
		float		cos_angle	= cosf( angle );
		float		sin_angle	= sinf( angle );
		float		worst_length;

		Mth::Vector	feeler_start;
		Mth::Vector	feeler_end;
		Mth::Vector	end_offset_direction;

		end_offset_direction[X] = cos_angle * m_facing[X] - sin_angle * m_facing[Z];
		end_offset_direction[Y] = 0.0f;
		end_offset_direction[Z] = sin_angle * m_facing[X] + cos_angle * m_facing[Z];
		end_offset_direction[W] = 0.0f;

		feeler_start			= Mth::Vector( m_pos[X], m_pos[Y] + feeler_height, m_pos[Z], m_pos[W] );
		feeler_end				= feeler_start + feeler_length * end_offset_direction;
		
		if( get_collision( feeler_start, end_offset_direction, feeler_length, &worst_length ))
//		if( feeler.GetCollision())
		{
			float dist = worst_length;

			// We need to figure out if turning a few degrees will enable us to obtain a clear path for
			// a significantly further distance.
			feeler_length *= 2.0f;

			float	best_score					= 0.0f;
			float	best_angle					= Mth::DegToRad( 90.0f );

			// Scan through angles from +9 degrees to -9 degrees in 3 degree steps.
			float step_angle = Mth::DegToRad( -9.0f );
			while( step_angle <= Mth::DegToRad( 9.0f ))
			{
				float score = 0.0f;

				cos_angle	= cosf( step_angle );
				sin_angle	= sinf( step_angle );

				end_offset_direction[X] = cos_angle * m_facing[X] - sin_angle * m_facing[Z];
				end_offset_direction[Y] = 0.0f;
				end_offset_direction[Z] = sin_angle * m_facing[X] + cos_angle * m_facing[Z];
				end_offset_direction[W] = 0.0f;

				feeler_end				= feeler_start + feeler_length * end_offset_direction;

				// Score for avoiding collision.
				bool collision = get_collision( feeler_start, end_offset_direction, feeler_length, &worst_length );

				if( !collision )
				{
					score += AVOID_COLLISION_SCORE;
				}

				// Score for minimal turn distance.
				score -= Mth::Abs( step_angle ) * TURN_ANGLE_SCORE_MULT;

				if( collision )
				{
					// Score for larger distances, use the percentage of this distance over the original collision distance.
					float this_dist	= worst_length;
					if( this_dist > dist )
					{
						float pc = ( this_dist / dist ) * 100.0f;
						score += pc * DISTANCE_PERCENTAGE_SCORE_MULT;
					}
				}

				// Score for being a turn in the same direction as last time.

				// Is this the best score yet?
				if( score > best_score )
				{
					best_score = score;
					best_angle = step_angle;
				}
				step_angle += Mth::DegToRad( 3.0f );
			}

			// Check for being in a potentially nasty situation.
			if( best_score < PANIC_SCORE )
			{
				// Increment panic counter, give a small chance to fix itself.
				++m_path_follow_panic_counter;
				if( m_path_follow_panic_counter > 0 )
				{
					m_path_follow_panic_counter = 0;
					switch_to_node_based_path_following();
					return;
				}
			}

			// Test - Immediately snap the direction to the best alternative.
			if( best_score > 0.0f )
			{
				cos_angle	= cosf( best_angle );
				sin_angle	= sinf( best_angle );

				end_offset_direction[X] = cos_angle * m_facing[X] - sin_angle * m_facing[Z];
				end_offset_direction[Z] = sin_angle * m_facing[X] + cos_angle * m_facing[Z];

				printf( "Changing heading by %f degrees\n", best_angle );

				m_facing[X]				= end_offset_direction[X];
				m_facing[Z]				= end_offset_direction[Z];
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::cleanup_node_based_path_following( void )
{
	delete [] mp_nav_nodes;
	mp_nav_nodes = NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::switch_to_node_based_path_following( void )
{
	// Try various distances, the further the better.
	float target_distance = FEET_TO_INCHES( 500.0f );

	while( target_distance > FEET_TO_INCHES( 50.0f ))
	{
		// Generate a distant target position.
		Mth::Vector start_pos	= m_pos + Mth::Vector( 0.0f, 60.0f, 0.0f, 0.0f );
		Mth::Vector target_pos	= start_pos + ( m_facing * target_distance );

		CFeeler feeler;
		feeler.m_start	= target_pos + Mth::Vector( 0.0f, 12000.0f, 0.0f, 0.0f );
		feeler.m_end	= target_pos - Mth::Vector( 0.0f, 12000.0f, 0.0f, 0.0f );

		if( feeler.GetCollision())
		{
			// Adjust the target position to be 5 feet above ground level.
			target_pos = feeler.GetPoint() + Mth::Vector(  0.0f, 60.0f, 0.0f, 0.0f );
		}
		else
		{
			// Hmm, maybe should try different distances here.
			// For now, Just go with what we started with.
		}

		CNavNode* p_target_node = CalculateNodePath( start_pos, target_pos );

		if( p_target_node == NULL )
		{
			// Try with a shorter distance.
			target_distance -= FEET_TO_INCHES( 50.0f );
			continue;
		}
		
		// A path was found, so set up the state accordingly. First count how many nodes on the path.
		CNavNode*	p_node_counter	= p_target_node;
		int			num_nodes		= 1;
		while( p_node_counter->GetAStarNode()->mp_parent != NULL )
		{
			++num_nodes;
			p_node_counter = p_node_counter->GetAStarNode()->mp_parent;
		}

		// Allocate memory for the path.
		if( mp_nav_nodes )
		{
			delete [] mp_nav_nodes;
		}
		mp_nav_nodes = new CNavNode*[num_nodes];

		// Fill in the array.
		m_nav_node_index = 0;
		while( p_target_node )
		{
			mp_nav_nodes[m_nav_node_index++] = p_target_node;
			p_target_node = p_target_node->GetAStarNode()->mp_parent;
		}
		--m_nav_node_index;

		// mp_nav_nodes[m_nav_node_index] now points to the first node we need to visit.
		break;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CHorseComponent::do_node_based_path_following( void )
{
	// Are we within a reasonable distance of our current node?
	Mth::Vector target_node_pos = mp_nav_nodes[m_nav_node_index]->GetPos() + Mth::Vector( 0.0f, 36.0f, 0.0f, 0.0f );
	float dist = Mth::Distance( m_pos, target_node_pos );
	if( dist < ( 12.0f * 6.0f ))
	{
		// Yes we are. Exit if no more nodes.
		if( m_nav_node_index == 0 )
		{
			cleanup_node_based_path_following();
			return;
		}

		// Switch to the next node.
		--m_nav_node_index;
	}


	for( int l = m_nav_node_index; l >= 0; --l )
	{
		Gfx::AddDebugStar( mp_nav_nodes[l]->GetPos(), 12.0f, MAKE_RGB( 255, 200, 0 ), 1 );

		if( l > 0 )
		{
			Gfx::AddDebugLine( mp_nav_nodes[l]->GetPos(), mp_nav_nodes[l - 1]->GetPos(), MAKE_RGB( 255, 255, 255 ), MAKE_RGB( 255, 255, 255 ), 1 );
		}
	}

	// Head to the next node.
	Mth::Vector heading = mp_nav_nodes[m_nav_node_index]->GetPos() - m_pos;
	heading[Y] = 0.0f;
	heading[W] = 0.0f;
	heading.Normalize();

	m_facing[X]	= heading[X];
	m_facing[Z]	= heading[Z];
}


}


================================================
FILE: Code/Gel/Components/HorseComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       HorseComponent.h
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#ifndef __COMPONENTS_HORSECOMPONENT_H__
#define __COMPONENTS_HORSECOMPONENT_H__

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#define		CRC_HORSE CRCD(0x9d65d0e7,"Horse")
#define		GetHorseComponent() ((Obj::CHorseComponent*)GetComponent(CRC_HORSE))
#define		GetHorseComponentFromObject(pObj) ((Obj::CHorseComponent*)(pObj)->GetComponent(CRC_HORSE))
		 
namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CInputComponent;

#define vVP_GRAVITATIONAL_ACCELERATION								(386.4f)
#define vVP_MAX_NUM_GEARS											(6)
#define vVP_NUM_COLLIDERS											(2)

class CHorseComponent : public CBaseComponent
{
	enum EStateType
	{
		WALKING_GROUND,
		WALKING_AIR,
		WALKING_HOP,
		WALKING_HANG,
		WALKING_LADDER,
		WALKING_ANIMWAIT
	};

	enum EAnimScaleSpeed
	{
		OFF,
		RUNNING,
        HANGMOVE,
		LADDERMOVE
	};

	// inorder to interface with models and skeletons, the number of wheels must be hardcoded to four
	enum { vVP_NUM_WHEELS = 4 };
	
	// HACK for control
	struct SControls {
		// steering; between -1.0 and 1.0
		float steering;
		
		// Spurring the horse on, or reining it in, values in range [-1.0, 1.0].
		float spur_rein;

		// throttle
		bool throttle;
		
		// brake
		bool brake;
		
		// handbrake
		bool handbrake;
		
		// reverse
		bool reverse;
	};
	
	
	struct SCollisionPoint
	{
		// world position
		Mth::Vector pos;
		
		// impact normal
		Mth::Vector normal;
		
		// depth of collision as defined as the dot between the displacement from the nearest collider corner and the collision normal
		float depth;
		
		// normal impulse accumulator; used to set the maximum friction
		float normal_impulse;
		
		bool line;
	};
	
	struct SCollider
	{
		// the rectangle defining the collider in body space
		Mth::Rectangle body;
		
		// that rectangle transformed into world space
		Mth::Rectangle world;
		Mth::Vector first_edge_direction_world;
		Mth::Vector second_edge_direction_world;
		
		// a few precomputable values
		float first_edge_length;
		float second_edge_length;
	};
	
public:
    CHorseComponent();
    virtual ~CHorseComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
	virtual void					Finalize (   );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							MoveToNode ( Script::CStruct* p_node );
	
	const Mth::Vector&				GetPos( void )						{ return GetObject()->GetPos(); }
	const Mth::Vector&				GetVel( void )				const	{ return m_vel; }

	Mth::Matrix&					GetMatrix( void )					{ return GetObject()->GetMatrix(); }
	Mth::Matrix&					GetDisplayMatrix( void )			{ return GetObject()->GetDisplayMatrix(); }

	bool							AcceptRiderMount( CCompositeObject*	p_rider );
	bool							AcceptRiderDismount( CCompositeObject*	p_rider );
	bool							ShouldUpdateCamera( void );
	CCompositeObject*				GetRider( void )					{ return mp_rider; }

	uint32							GetAnimation( void );

private:
	
    void							draw_debug_rendering (   ) const;
	
private:
	
	Obj::CNavNode**					mp_nav_nodes;			// List of pointers to navigation nodes if following a path.
	int								m_nav_node_index;		// Current index within the list (moves to zero as path progesses).

	// dynamic state variables
	
	// dependent state variables
	
	// angular orientation in 3x3 matrix form
	Mth::Matrix m_orientation_matrix;
	
	// linear velocity
	Mth::Vector m_vel;
	
	// rotational velocity
	Mth::Vector m_rotvel;
	
	// accumulators
	
	
	// length of current frame
	float m_time_step;
	
	// constant characteristics
	
	
	// subelements
	
	// input state
//	SControls m_controls;
	
	// collision cache
	Nx::CCollCache m_collision_cache;
	
	
	// shared objects
	
	// shared collision point array
	static SCollisionPoint sp_collision_points[4 * (Nx::MAX_NUM_2D_COLLISIONS_REPORTED + 1)];
	
	CCompositeObject*				mp_rider;

	// peer components
	CSkeletonComponent*				mp_skeleton_component;
	CInputComponent*				mp_input_component;
	CModelComponent*				mp_model_component;
	CAnimationComponent*			mp_animation_component;
	CMovableContactComponent*		mp_movable_contact_component;
	
	// debug
	int m_draw_debug_lines;

	// Stuff from CWalkComponent.

	static float					s_get_param ( uint32 checksum );


	enum
	{
		vNUM_FEELERS = 7,
		vNUM_BARRIER_HEIGHT_FEELERS = 7
	};

	struct SContact
	{
		// If this contact is in collision with the environment.
		bool						in_collision;
		
		// The horizontal normal of the collided geometry.
		Mth::Vector					normal;
		
		// Distance from the collision as a factor of the feeler length.
		float						distance;
		
		// If the collision in this direction is determined to be low enough to jump.
		bool						jumpable;
		
		// Movement of the object we're in contact with (not accounting for its rotation) along its horizontal normal.
		float						movement;
		
	// The walker looks around him in vNUM_FEELERS directions and stores the results here.  The final contact is felt for along the movement of
	// the origin, and is only used while in the air.
	} mp_contacts [ vNUM_FEELERS + 1 ];

	/////////////////////////////////
	// WALKING_AIR state variables
	
	// The forward horizontal direction of the jump which, when coming out of skating or because of in-air velocity control, diverges from the direction of velocity
	Mth::Vector						m_primary_air_direction;
	
	// This term of our horizontal velocity is due to leaping from a movable contact, and is not adjustable via in-air control.
	Mth::Vector						m_uncontrollable_air_horizontal_vel;

	void							get_controller_input( void );
	void							go_on_ground_state( void );
	void							go_in_air_state( void );
	void							lerp_upright( void );
	void							uber_frig( void );
	void							account_for_movable_contact( void );
	void							setup_collision_cache( void );
	float							calculate_desired_speed( void );
	float							adjust_desired_speed_for_slope( float desired_speed );
	bool							adjust_horizontal_vel_for_environment( bool wall_push_active );
	void							copy_state_into_object( void );
	void							adjust_facing_for_adjusted_horizontal_vel( void );
	void							calculate_horizontal_speed_and_facing( float &horizontal_speed );
	Mth::Vector						calculate_feeler_offset_direction ( int contact );
	void							adjust_curb_float_height( void );
	bool							forced_run( void );
	void							respond_to_ground( void );
	void							leave_movable_contact_for_air( Mth::Vector& horizontal_vel, float& vertical_vel );
	void							jump( float strength );
	void							set_state( EStateType state );
	void							control_horizontal_vel( void );
	void							check_for_landing( const Mth::Vector& previous_pos, const Mth::Vector& final_pos );
	void							adjust_vertical_vel_for_ceiling( void );
	void							position_rider( void );

	void							do_path_following( void );
	void							switch_to_node_based_path_following( void );
	void							cleanup_node_based_path_following( void );
	void							do_node_based_path_following( void );

	int								m_path_follow_panic_counter;

	// Accumulates position offset due to a movable contact during an animation wait.
	Mth::Vector						m_offset_due_to_movable_contact;

	EStateType						m_state;

	// Time stamp of when our state last changed.
	Tmr::Time						m_state_timestamp;
	
	// State we changed from when m_state_timestamp was last set.
	EStateType						m_previous_state;

	// Certain things such as rotation rate and camera lerp are increased when this is set.
	bool							m_run_toggle;

	struct SWallPushTest
	{
		// Whether we currently testing.
		bool						active;
		
		// Time at which this test began.
		Tmr::Time					test_start_time;
		
	// Data used to detect when a player is pushing into a wall in order to climb it.
	}								m_wall_push_test;

	// Normal of the ground on which we are standing.  Only meaningful when on the ground.  Set the previous frame.
	Mth::Vector						m_ground_normal;

	// Used to slerp the display matrix towards the actual matrix (can't use CCompositeObject::m_display_matrix since that is reset every frame).
	Mth::Matrix						m_display_slerp_matrix;

	// The collision cache used.
	Nx::CCollCache*					mp_collision_cache;

	// When doing anim speed scaling when running, this is set to the run speed which corresponds to the current animation playing at standard speed.
	float							m_anim_standard_speed;
	
	// A copy of the latest feeler which contacted the groud under us.
	CFeeler							m_last_ground_feeler;
	bool							m_last_ground_feeler_valid;

	// The target fraction of maximum speed [0.0, 1.0] as modified by let analog stick up down movement.
	float							m_target_speed;
	float							m_target_speed_adjustment;

	// Extracted from the object at frame start and back into the object at frame end.
	Mth::Vector						m_pos;
	Mth::Vector						m_horizontal_vel;
	float							m_vertical_vel;
	Mth::Vector						m_facing;
	Mth::Vector						m_upward;

	// The control-based change in heading this frame. Used mainly for path following decisions.
	float							m_delta_angle;

	// Type of animation speed scaling to do.
	EAnimScaleSpeed					m_anim_scale_speed;
	
	// Horizontal speed for the purposes of animation speed scaling.
	float							m_anim_effective_speed;
	
	// Factor multiplied by all velocities.  Set by script.
	float							m_script_drag_factor;

	// Current frame's frame length.
	float							m_frame_length;

	// Current frame's control input.
	Mth::Vector						m_control_direction;
	float							m_control_magnitude;
	bool							m_control_pegged;

 	// Keep track of the previous state of the dpad controls so that we can ramp up to a run speed over time.
	bool							m_dpad_used_last_frame;
	
	// Keep track of when we first started using the dpad so that we can ramp up to a run speed over time.
	Tmr::Time						m_dpad_use_time_stamp;

	// Cache our position at the frame's start
	Mth::Vector						m_frame_start_pos;
	
	// Script event reporting our current state.  Set during each frame update and sent at the end of the update.
	uint32							m_frame_event;
	
	// Last frame's frame event.
	uint32							m_last_frame_event;

	// Used to lock control in the forward direction (during a camera flush request)
	bool							m_forward_control_lock;

	// When on ground, the walker is often floated above the ground in an attempt to smooth out stair/curb climbs.
	float							m_curb_float_height;
	
	// If m_curb_float_height was adjusted this frame.  If not, we lerp it to zero.
	bool							m_curb_float_height_adjusted;

};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline float CHorseComponent::s_get_param( uint32 checksum )
{
	Script::CStruct* p_params = Script::GetStructure(CRCD(0x1b38036f,"GunslingerHorseParameters"));
	Dbg_Assert(p_params);
	
	float param;
	p_params->GetFloat(checksum, ¶m, Script::ASSERT);
	return param;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline bool CHorseComponent::forced_run( void )
{
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline void CHorseComponent::set_state ( EStateType state )
{
    if (state == m_state) return;
	
	m_previous_state = m_state;
    
    if (m_state == WALKING_GROUND)
    {
        m_wall_push_test.active = false;
		m_run_toggle = false;
    }
	
	if (m_state != WALKING_GROUND && m_state != WALKING_AIR)
	{
		m_dpad_used_last_frame = false;
	}
	
	m_state = state;
	m_state_timestamp = Tmr::GetTime();
	
	if (m_state == WALKING_ANIMWAIT)
	{
		m_offset_due_to_movable_contact.Set();
	}
}

}

#endif


================================================
FILE: Code/Gel/Components/InputComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       InputComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/18/3
//****************************************************************************

#include 
#include 
#include 

#include 
								
#include 
#include 
#include 
#include 

#include 
#include 

#include 

namespace Obj
{
	extern bool DebugSkaterScripts;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CInputComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CInputComponent);	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CInputComponent::CInputComponent() : CBaseComponent()
{
	SetType( CRC_INPUT);
	
	m_input_handler = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CInputComponent::~CInputComponent()
{
	if (m_input_handler)
	{
		if (m_input_handler->InList())
		{
			m_input_handler->Remove();
		}
		delete m_input_handler;
	}
	if (m_input_handler2)
	{
		if (m_input_handler2->InList())
		{
			m_input_handler2->Remove();
		}
		delete m_input_handler2;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CInputComponent::InitFromStructure( Script::CStruct* pParams )
{
	int i;
	if (pParams->GetInteger(CRCD(0x67e6859a, "player"), &i))
	{
		BindToController(Mdl::Skate::Instance()->m_device_server_map[i]);
	}
	else if (pParams->GetInteger(CRCD(0xb30d9965, "controller"), &i))
	{
		BindToController(i);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CInputComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CInputComponent::Update()
{
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CInputComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | Input_Debounce | 
		case CRCC(0xbf7d6c46, "Input_Debounce"):
		{
			uint32 ButtonChecksum = 0;
			if (!pParams->GetChecksum(NO_NAME, &ButtonChecksum)) break;

			float time = 1.0f;
			pParams->GetFloat(CRCD(0x906b67ba, "time"), &time);
			DUMPF(time);

			int	clear = 0;
			pParams->GetInteger(CRCD(0x1a4e0ef9, "clear"), &clear);

			Debounce(ButtonChecksum, time, clear);
			break;
		}
			
		// @script | DisablePlayerInput | 
		case CRCC(0x6e24f5fd, "DisablePlayerInput"):
			DisableInput();
			break;
			
        // @script | EnablePlayerInput | 
		case CRCC(0x56c6c0cd, "EnablePlayerInput"):	  
			EnableInput();
			break;

		// @script | NetDisablePlayerInput | 
		case CRCC(0x6df51774, "NetDisablePlayerInput"):
			NetDisableInput();
			break;

		// @script | NetEnablePlayerInput | 
		case CRCC(0x31c6454f, "NetEnablePlayerInput"):	  
			NetEnableInput();
			break;
				
		// @script | PlayerInputIsDisabled	| return true if the player input is disabled
		case CRCC(0x660746a, "PlayerInputIsDisabled"):
			return IsInputDisabled() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;	

        // @script | LeftPressed | true if left is being pressed on the pad
		case CRCC(0x78238598, "LeftPressed"):
			return m_pad.m_left.GetPressed() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;
			
        // @script | RightPressed | true if right is being pressed on the pad
		case CRCC(0x1d604a9d, "RightPressed"):
			return m_pad.m_right.GetPressed() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;
			
        // @script | UpPressed | true if up is being pressed on the pad
		case CRCC(0x89aa9b5f, "UpPressed"):
			return m_pad.m_up.GetPressed() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;
			
        // @script | DownPressed | true if down is being pressed on the pad
		case CRCC(0x83028fc5, "DownPressed"):
			return m_pad.m_down.GetPressed() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;
			
        // @script | HeldLongerThan | true if the specified button has been
        // held longer than the specified amount of time
        // @parm name | Button | button name
        // @uparm 1.0 | time value (default is milliseconds)
        // @flag seconds | time in seconds
        // @flag frames | time in frames
		case CRCC(0x2366469c, "HeldLongerThan"):
		{
			uint32 ButtonChecksum;
			pParams->GetChecksum(CRCD(0xc5f953c2, "Button"), &ButtonChecksum, Script::ASSERT);
				
			float time;
			pParams->GetFloat(NO_NAME, &time, Script::ASSERT);
	
			Tmr::Time TestTime = 0;
			if (pParams->ContainsFlag(CRCD(0xd029f619, "seconds")) || pParams->ContainsFlag(CRCD(0x49e0ee96, "second")))
			{
				TestTime = static_cast< Tmr::Time >(time * 1000.0f);
			}	
			else if (pParams->ContainsFlag(CRCD(0x19176c5, "frames")) || pParams->ContainsFlag(CRCD(0x4a07c332, "frame")))
			{
				TestTime =static_cast< Tmr::Time >(time * (1000.0f / 60.0f));
			}
			else
			{
				TestTime =static_cast< Tmr::Time >(time);	
			}

			CSkaterButton* pButt = m_pad.GetButton(ButtonChecksum);
			if (pButt)
			{
				return (pButt->GetPressed() && pButt->GetPressedTime() > TestTime) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			}
			else
			{
				return CBaseComponent::MF_FALSE;
			}	
			break;
		}
			
        // @script | EnableInputEvents |  Enable the sending of TRIGGER_ and RELEASE events from an Input component
		case	CRCC(0x1090a150,"EnableInputEvents"):
		{
			m_input_events_enabled = true;			
			break;
		}
		
        // @script | DisableInputEvents |  Disable the sending of TRIGGER_ and RELEASE events from an Input component
		case	CRCC(0x28729460,"DisableInputEvents"):
		{
			m_input_events_enabled = false;			
			break;
		}
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CInputComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CInputComponent::GetDebugInfo"));
	
	if (m_input_handler && m_input_handler->m_Device)
	{
		p_info->AddInteger("port", m_input_handler->m_Device->GetPort());
		p_info->AddInteger("slot", m_input_handler->m_Device->GetSlot());
		p_info->AddInteger("index", m_input_handler->m_Device->GetIndex());
		p_info->AddInteger("m_input_events_enabled",m_input_events_enabled);
	}

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CInputComponent::BindToController ( int controller )
{
	if (!m_input_handler)
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

		m_input_handler = new Inp::Handler< CInputComponent >(0, CInputComponent::s_input_logic_code, *this, Tsk::BaseTask::Node::vNORMAL_PRIORITY - 1);
		Inp::Manager::Instance()->AddHandler(*m_input_handler);
		m_input_handler2 = new Inp::Handler< CInputComponent >(1, CInputComponent::s_input_logic_code2, *this, Tsk::BaseTask::Node::vNORMAL_PRIORITY - 1);
		Inp::Manager::Instance()->AddHandler(*m_input_handler2);

		Mem::Manager::sHandle().PopContext();
	}

	Inp::Manager::Instance()->ReassignHandler(*m_input_handler, controller);
	Inp::Manager::Instance()->ReassignHandler(*m_input_handler2, 1);
	
	if (CVibrationComponent* p_vibration_component = GetVibrationComponentFromObject(GetObject()))
	{
		p_vibration_component->SetDevice(m_input_handler->m_Device);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CInputComponent::Debounce ( uint32 Checksum, float time, bool clear )
{
	CSkaterButton* button = m_pad.GetButton(Checksum);
	
	float debounce_time = Tmr::GetTime() + (time * 1000.0f);
	
	if (CTrickComponent* p_trick_component = GetTrickComponentFromObject(GetObject()))
	{
		p_trick_component->Debounce(Inp::GetButtonIndex(Checksum), debounce_time);
	}
	
	button->SetDebounce(static_cast< int >(debounce_time));
	
	if (clear)
	{
		button->SetPressed(false);
		button->ClearTrigger();
		button->ClearRelease();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CInputComponent::ignore_button_presses (   )
{
	// if any operable menus are up in net games, ignore input
	if( !GameNet::Manager::Instance()->InNetGame()) return false;
	
	if (Front::CScreenElementManager::Instance()->GetElement(CRCD(0x2edb780f, "controller_unplugged_dialog_anchor"))) return true;
	
	Front::CScreenElement* p_root_window
		= Front::CScreenElementManager::Instance()->GetElement(CRCD(0x56a1eae3, "root_window"));
	
	if (p_root_window)
	{
		Script::CStruct* pTags = new Script::CStruct();
		p_root_window->CopyTagsToScriptStruct(pTags);
		
		uint32 menu_state;
		pTags->GetChecksum(CRCD(0xcdc00e63, "menu_state"), &menu_state);
		delete pTags;
		
		if (menu_state == CRCD(0xf649d637, "on")) return true;
	}

	if (Front::CScreenElementManager::Instance()->GetElement(CRCD(0x31631b98, "keyboard_anchor"))) return true;

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CInputComponent::handle_input ( Inp::Data* input )
{
	if (GetObject()->IsPaused()) return;
	
	build_input_mask(input);

	bool ignore = ignore_button_presses();
	if (IsInputDisabled() || ignore)
	{
		m_pad.Zero();
		if (ignore)
		{
			m_input_mask = 0;
		}
	}
	else
	{
		#ifdef __NOPT_ASSERT__
		m_pad.Update(input, DebugSkaterScripts && GetObject()->GetID() == 0);
		#else
		m_pad.Update(input);
		#endif
	}
}


void CInputComponent::handle_input2 ( Inp::Data* input )
{
	if (GetObject()->IsPaused()) return;
	
//	build_input_mask(input);

	bool ignore = ignore_button_presses();
	if (IsInputDisabled() || ignore)
	{
		m_pad2.Zero();
		if (ignore)
		{
			m_input_mask = 0;
		}
	}
	else
	{
		#ifdef __NOPT_ASSERT__
		m_pad2.Update(input, DebugSkaterScripts && GetObject()->GetID() == 0);
		#else
		m_pad2.Update(input);
		#endif
	}
}


	
void CInputComponent::update_input_mask ( Inp::Data* input, Inp::Data::AnalogButtonIndex button, Inp::Data::AnalogButtonMask mask, uint32 trigger_event, uint32 release_event, CSkaterButton* skater_button )
{
	if (skater_button && Tmr::GetTime() < skater_button->GetDebounceTime()) return;
	
	if (input->m_Event[button])
	{
		if (!(m_last_mask & mask))
		{
				// Fire Event to self, trigger_event
			if (m_input_events_enabled && !m_input_disabled && !m_net_input_disabled)
			{
						
						//uint32	id = GetObject()->GetID();
						//Obj::CTracker::Instance()->LaunchEvent(trigger_event, id, id);
						GetObject()->SelfEvent(trigger_event);
			}
		}
		m_input_mask |= mask;
	}
	else
	{
		if ((m_last_mask & mask))
		{
			if (m_input_events_enabled && !m_input_disabled && !m_net_input_disabled)
			{
						// Fire Event to self, release_event
						//uint32	id = GetObject()->GetID();
						//Obj::CTracker::Instance()->LaunchEvent(release_event, id, id);
						GetObject()->SelfEvent(release_event);						
			}
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CInputComponent::build_input_mask ( Inp::Data* input )
	{
	m_last_mask = m_input_mask;
	m_input_mask = 0;
	update_input_mask(input, Inp::Data::vA_UP,			Inp::Data::mA_UP, 		CRCD(0x5c8faa09,"Trigger_UP"),        CRCD(0xb27804a2,"Release_UP"),		&m_pad.m_up			);	
	update_input_mask(input, Inp::Data::vA_DOWN,		Inp::Data::mA_DOWN, 	CRCD(0xd26adae9,"Trigger_DOWN"),      CRCD(0xaef7dfd3,"Release_DOWN"),      &m_pad.m_down		);	
	update_input_mask(input, Inp::Data::vA_LEFT,		Inp::Data::mA_LEFT, 	CRCD(0xb4f2adba,"Trigger_LEFT"),      CRCD(0xc86fa880,"Release_LEFT"),      &m_pad.m_left		);	
	update_input_mask(input, Inp::Data::vA_RIGHT,		Inp::Data::mA_RIGHT, 	CRCD(0xedbbc2b,"Trigger_RIGHT"),      CRCD(0xc8abf89c,"Release_RIGHT"),     &m_pad.m_right 		);	
	update_input_mask(input, Inp::Data::vA_CIRCLE,		Inp::Data::mA_CIRCLE, 	CRCD(0xb069b600,"Trigger_CIRCLE"),    CRCD(0xe5aae06b,"Release_CIRCLE"),    &m_pad.m_circle		);	
	update_input_mask(input, Inp::Data::vA_SQUARE,		Inp::Data::mA_SQUARE, 	CRCD(0xa93dbbd0,"Trigger_SQUARE"),    CRCD(0xfcfeedbb,"Release_SQUARE"),    &m_pad.m_square		);	
	update_input_mask(input, Inp::Data::vA_TRIANGLE,	Inp::Data::mA_TRIANGLE, CRCD(0x7f695a77,"Trigger_TRIANGLE"),  CRCD(0x7b684919,"Release_TRIANGLE"),  &m_pad.m_triangle	);	
	update_input_mask(input, Inp::Data::vA_X,			Inp::Data::mA_X, 		CRCD(0x42717176,"Trigger_X"),         CRCD(0xbbbef674,"Release_X"),         &m_pad.m_x	 		);	
	update_input_mask(input, Inp::Data::vA_L1,			Inp::Data::mA_L1, 		CRCD(0xc6547217,"Trigger_L1"),        CRCD(0x28a3dcbc,"Release_L1"),        &m_pad.m_L1	 		);	
	update_input_mask(input, Inp::Data::vA_L2,			Inp::Data::mA_L2, 		CRCD(0x5f5d23ad,"Trigger_L2"),        CRCD(0xb1aa8d06,"Release_L2"),        &m_pad.m_L2 		);	
	update_input_mask(input, Inp::Data::vA_L3,			Inp::Data::mA_L3, 		CRCD(0x285a133b,"Trigger_L3"),        CRCD(0xc6adbd90,"Release_L3"),        &m_pad.m_L3	 		);	
	update_input_mask(input, Inp::Data::vA_R1,			Inp::Data::mA_R1, 		CRCD(0x12154dc8,"Trigger_R1"),        CRCD(0xfce2e363,"Release_R1"),        &m_pad.m_R1 		);	
	update_input_mask(input, Inp::Data::vA_R2,			Inp::Data::mA_R2, 		CRCD(0x8b1c1c72,"Trigger_R2"),        CRCD(0x65ebb2d9,"Release_R2"),        &m_pad.m_R2 		);	
	update_input_mask(input, Inp::Data::vA_R3,			Inp::Data::mA_R3, 		CRCD(0xfc1b2ce4,"Trigger_R3"),        CRCD(0x12ec824f,"Release_R3"),        &m_pad.m_R3			);	
	update_input_mask(input, Inp::Data::vA_BLACK,		Inp::Data::mA_BLACK, 	CRCD(0x33947317,"Trigger_BLACK"),     CRCD(0xf5e437a0,"Release_BLACK"),     NULL				);	
	update_input_mask(input, Inp::Data::vA_WHITE,		Inp::Data::mA_WHITE, 	CRCD(0xf8de049b,"Trigger_WHITE"),     CRCD(0x3eae402c,"Release_WHITE"),     NULL		 		);	
	update_input_mask(input, Inp::Data::vA_Z,			Inp::Data::mA_Z,	 	CRCD(0xac7f105a,"Trigger_Z"),         CRCD(0x55b09758,"Release_Z"),     	NULL		 		);	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CInputComponent::s_input_logic_code ( const Inp::Handler < CInputComponent >& handler )
{
	handler.GetData().handle_input(handler.m_Input);
}

void CInputComponent::s_input_logic_code2 ( const Inp::Handler < CInputComponent >& handler )
{
	handler.GetData().handle_input2(handler.m_Input);
}


}


================================================
FILE: Code/Gel/Components/InputComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       InputComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/18/3
//****************************************************************************

#ifndef __COMPONENTS_INPUTCOMPONENT_H__
#define __COMPONENTS_INPUTCOMPONENT_H__

#include 
#include 

#include 
#include 

#include 

#define		CRC_INPUT CRCD(0x27d7cd28, "Input")

#define		GetInputComponent() ((Obj::CInputComponent*)GetComponent(CRC_INPUT))
#define		GetInputComponentFromObject(pObj) ((Obj::CInputComponent*)(pObj)->GetComponent(CRC_INPUT))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	
class CInputComponent : public CBaseComponent
{
public:
    CInputComponent();
    virtual ~CInputComponent();

public:
    virtual void            			Update();
    virtual void            			InitFromStructure( Script::CStruct* pParams );
    virtual void            			RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   	CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 						GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*				s_create();
	
	uint32								GetInputMask (   ) const { return m_input_mask; }
	CControlPad&						GetControlPad (   ) { return m_pad; }
	CControlPad&						GetControlPad2 (   ) { return m_pad2; }
	
	void								DisableInput (   ) { m_input_disabled = true; }
	void								EnableInput (   ) { m_input_disabled = false; }
	void								NetDisableInput (   ) { m_net_input_disabled = true; }
	void								NetEnableInput (   ) { m_net_input_disabled = false; }
	bool								IsInputDisabled (   ) { return m_input_disabled || m_net_input_disabled; }
	
	void								BindToController ( int controller );
	Inp::Handler< CInputComponent >*	GetInputHandler (   ) { return m_input_handler; }
	
	void								Debounce ( uint32 Checksum, float time, bool clear );
	
	void								PauseDevice (   ) { m_input_handler->m_Device->Pause(); }
	void								UnPauseDevice (   ) { m_input_handler->m_Device->UnPause(); }
	SIO::Device*						GetDevice (   ) { return m_input_handler->m_Device; }
	
private:
	static void							s_input_logic_code ( const Inp::Handler < CInputComponent >& handler );
	static void							s_input_logic_code2 ( const Inp::Handler < CInputComponent >& handler );
	
	void								handle_input ( Inp::Data* input );
	void								handle_input2 ( Inp::Data* input );
	void								build_input_mask ( Inp::Data* input );
	void 								update_input_mask ( Inp::Data* input, Inp::Data::AnalogButtonIndex button, Inp::Data::AnalogButtonMask mask, uint32 trigger_event, uint32 release_event, CSkaterButton* skater_button  );
	
	bool								ignore_button_presses (   );
	
private:
	uint32								m_last_mask;
	uint32								m_input_mask;
	CControlPad							m_pad;
	CControlPad							m_pad2;
	
	Inp::Handler< CInputComponent >*	m_input_handler;
	Inp::Handler< CInputComponent >*	m_input_handler2;
	
	bool								m_input_disabled;
	bool								m_net_input_disabled;
	
	bool								m_input_events_enabled;
};

}

#endif


================================================
FILE: Code/Gel/Components/ModelLightUpdateComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ModelLightUpdateComponent.cpp
//* OWNER:          Garrett
//* CREATION DATE:  07/10/03
//****************************************************************************

// The CModelLightUpdateComponent class is used to update the brightness of
// the model lights if nothing else is currently doing it.  Because it has
// to do extra collision checks, this component should only be used if there
// is no other way to get the brightness from an existing feeler.  For now,
// all you need to do is create the component on an object that already has
// a ModelComponent with "UseModelLights" set in the InitStructure.

#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 

#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CModelLightUpdateComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CModelLightUpdateComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModelLightUpdateComponent::CModelLightUpdateComponent() : CBaseComponent()
{
	SetType( CRC_MODELLIGHTUPDATE );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CModelLightUpdateComponent::~CModelLightUpdateComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelLightUpdateComponent::InitFromStructure( Script::CStruct* pParams )
{
	// ** Add code to parse the structure, and initialize the component

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelLightUpdateComponent::Finalize()
{
	mp_model_component =  GetModelComponentFromObject( GetObject() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelLightUpdateComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelLightUpdateComponent::Update()
{
	// Find the closest ground collision and extract the brightness from it.
	// logic extracted from CAdjustComponent::uber_frig

	CFeeler feeler;
	
	feeler.m_start = GetObject()->m_pos;
	feeler.m_end = GetObject()->m_pos;

	// Very minor adjustment to move origin away from vert walls
	feeler.m_start += GetObject()->m_matrix[Y] * 0.001f;
	
	feeler.m_start[Y] += 8.0f;
	feeler.m_end[Y] -= FEET(400);
		   
	if (!feeler.GetCollision()) return;

	Dbg_Assert(mp_model_component);

	if (feeler.IsBrightnessAvailable())
	{
		Nx::CModelLights *p_model_lights = mp_model_component->GetModel()->GetModelLights();
		if (p_model_lights)
		{
			p_model_lights->SetBrightness(feeler.GetBrightness());
		} 
	}
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CModelLightUpdateComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
/*
        // @script | DoSomething | does some functionality
		case 0xbb4ad101:		// DoSomething
			DoSomething();
			break;

        // @script | ValueIsTrue | returns a boolean value
		case 0x769260f7:		// ValueIsTrue
		{
			return ValueIsTrue() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		break;
*/

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelLightUpdateComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CModelLightUpdateComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*	Example:
	p_info->AddInteger(CRCD(0x7cf2a233,"m_never_suspend"),m_never_suspend);
	p_info->AddFloat(CRCD(0x519ab8e0,"m_suspend_distance"),m_suspend_distance);
	*/
	
// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/ModelLightUpdateComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ModelLightUpdateComponent.h
//* OWNER:          Garrett
//* CREATION DATE:  07/10/03
//****************************************************************************

#ifndef __COMPONENTS_MODELLIGHTUPDATECOMPONENT_H__
#define __COMPONENTS_MODELLIGHTUPDATECOMPONENT_H__

#include 
#include 

#include 

#define		CRC_MODELLIGHTUPDATE CRCD(0xe3fca939,"ModelLightUpdate")
#define		GetModelLightUpdateComponent() ((Obj::CModelLightUpdateComponent*)GetComponent(CRC_MODELLIGHTUPDATE))
#define		GetModelLightUpdateComponentFromObject(pObj) ((Obj::CModelLightUpdateComponent*)(pObj)->GetComponent(CRC_MODELLIGHTUPDATE))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

	class CModelComponent;

class CModelLightUpdateComponent : public CBaseComponent
{
public:
    CModelLightUpdateComponent();
    virtual ~CModelLightUpdateComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
	virtual	void 					Finalize();
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

protected:
	CModelComponent *				mp_model_component;
};

}

#endif


================================================
FILE: Code/Gel/Components/MovableContactComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       MovableContactComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  5/19/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 

#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CMovableContactComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CMovableContactComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMovableContactComponent::CMovableContactComponent() : CBaseComponent()
{
	SetType( CRC_MOVABLECONTACT );
	
	mp_contact = NULL;
	
	m_lost_contact_timestamp = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CMovableContactComponent::~CMovableContactComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovableContactComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovableContactComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovableContactComponent::Update()
{
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CMovableContactComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovableContactComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CMovableContactComponent::GetDebugInfo"));
	
	if (mp_contact && mp_contact->GetObject())
	{
		p_info->AddChecksum(CRCD(0xb39d19c7, "contact"), mp_contact->GetObject()->GetID());
	}

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovableContactComponent::CheckForMovableContact ( CFeeler& feeler )
{
	// we might now be in contact with something new
	if (feeler.IsMovableCollision())
	{
		if (HaveContact())
		{
			if (mp_contact->GetObject() != feeler.GetMovingObject())
			{
				LoseAnyContact();
			}
		}
		
		if (!HaveContact())
		{
			// only create a new contact point if there was not one before, or if we just deleted one
			mp_contact = new CContact(feeler.GetMovingObject());
			return true;
		}
	}
	else
	{
		// when we land on the ground, then we can't be in contact with anything, so delete any existing contact
		if (HaveContact())
		{
			LoseAnyContact();
		}
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCompositeObject* CMovableContactComponent::CheckInsideObjectsToTop ( Mth::Vector& pos )
{
	Mth::Vector top;
	CCompositeObject *p_object = find_inside_object(pos, top);
	if (!p_object) return NULL;
	
	pos = top;
	pos[Y] += 1.0f;
	return p_object;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCompositeObject* CMovableContactComponent::CheckInsideObjects ( Mth::Vector& pos, const Mth::Vector& safe_pos )
{
	// not movable contact functionality per say, but useful a service related to movable collision nonetheless
	
	// Check to see if the skater is inside any moving object and move him outside
	// Algorithm
	// start at a point 100 feet above the skater
	// check collision down to the skater
	// while there is a collision if skater has a moving object collision in the 100 feet above him
	// and no collisions (with that object) going up to that point, then we are inside the object

	Mth::Vector top;
	CCompositeObject *p_object = find_inside_object(pos, top);
	if (!p_object) return NULL;
	
	// check if the last position is now fine...
	Mth::Vector dummy;
	if (!find_inside_object(safe_pos, dummy))
	{
		// first choice: move back to old pos
		pos = safe_pos;
	}
	else
	{
		// recheck last position here, like adding in the contact movement
	
		Mth::Vector test = pos + p_object->GetVel() * Tmr::FrameLength() * 2.0f;  
		
		if (!find_inside_object(test, dummy))
		{
			// first choice: move back to old pos
			pos = test;
		}
		else
		{
			// last resort, snap up 
			pos = top;
			
			// when snapping up, put him an inch above the object; otherwise, you will not hit it next frame, as the point will be in the plane
			pos[Y] += 1.0f;
		}
	}
	
	return p_object;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCompositeObject* CMovableContactComponent::find_inside_object ( const Mth::Vector& pos, Mth::Vector& p_return_pos )
{
	// 	Given a position (pos), then find out if this position is inside this moving object
	//  Assumes anything within 6 inches below the top of the object is actually outside as the code assumes 

	// create a line that goes from 400 inches above this point to six inches above
	CFeeler	down_feeler;
	down_feeler.m_start = pos;
	down_feeler.m_start[Y] += 400.0f;
	down_feeler.m_end = pos;
	down_feeler.m_end[Y] += 6.0f;
	
	CFeeler	up_feeler;
	int loop_detect = 0;
	while (true)
	{
		// return on final builds to handle highly obscure cases I've not forseen....
		if (++loop_detect == 10) return NULL;
		
		// nothing above
		if (!down_feeler.GetCollision()) return NULL;
		
		// something above, but not movable
		if (!down_feeler.IsMovableCollision()) return NULL;
		
		// something movable above
		
		// Now we get the collision point, and check UP to that point from m_pos to a point just above that		
		up_feeler.m_start = pos;
		up_feeler.m_end = down_feeler.GetPoint();
		up_feeler.m_end[Y] += 0.1f;
		
		if (!up_feeler.GetCollision())
		{
			p_return_pos = down_feeler.GetPoint();
			return down_feeler.GetMovingObject(); 
		}
		else
		{
			down_feeler.m_start = down_feeler.GetPoint();
			down_feeler.m_start[Y] -= 0.1f;
		}
	}
}	

}


================================================
FILE: Code/Gel/Components/MovableContactComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       MovableContactComponent.h
//* OWNER:          Dan
//* CREATION DATE:  5/19/3
//****************************************************************************

#ifndef __COMPONENTS_MOVABLECONTACTCOMPONENT_H__
#define __COMPONENTS_MOVABLECONTACTCOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_MOVABLECONTACT CRCD(0x408d2aa9, "MovableContact")

#define		GetMovableContactComponent() ((Obj::CMovableContactComponent*)GetComponent(CRC_MOVABLECONTACT))
#define		GetMovableContactComponentFromObject(pObj) ((Obj::CMovableContactComponent*)(pObj)->GetComponent(CRC_MOVABLECONTACT))

class CFeeler;
 
namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CMovableContactComponent : public CBaseComponent
{
public:
    CMovableContactComponent();
    virtual ~CMovableContactComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	bool							CheckForMovableContact ( CFeeler& feeler );
	void							ObtainContact ( CCompositeObject* p_composite_object );
	bool							HaveContact (   );
	void							LoseAnyContact (   );
	bool							UpdateContact ( const Mth::Vector& pos);
	CContact*						GetContact (   ) { return mp_contact; }
	CCompositeObject*				CheckInsideObjectsToTop ( Mth::Vector& pos );
	CCompositeObject*				CheckInsideObjects ( Mth::Vector& pos, const Mth::Vector& safe_pos );
	
	Tmr::Time						GetTimeSinceLastLostContact (   ) { return Tmr::ElapsedTime(m_lost_contact_timestamp); }
	
private:		
	CCompositeObject*				find_inside_object ( const Mth::Vector& pos, Mth::Vector& p_return_pos );

	CContact*						mp_contact;						// contact point (NULL if no current contact)
	
	Tmr::Time						m_lost_contact_timestamp;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CMovableContactComponent::LoseAnyContact (   )
{
	if (mp_contact)
	{
		delete mp_contact;
		mp_contact = NULL;
		
		m_lost_contact_timestamp = Tmr::GetTime();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CMovableContactComponent::HaveContact (   )
{
	if (!mp_contact) return false;
	
	if (mp_contact->GetObject()) return true;
	
	LoseAnyContact();
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CMovableContactComponent::ObtainContact ( CCompositeObject* p_composite_object )
{
	LoseAnyContact();
	mp_contact = new CContact(p_composite_object);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CMovableContactComponent::UpdateContact ( const Mth::Vector& pos)
{
	if (!HaveContact()) return false;
	
	if (mp_contact->Update(pos)) return true;
	
	LoseAnyContact();
	return false;
}

}

#endif


================================================
FILE: Code/Gel/Components/NearComponent.cpp
================================================
#if 0

//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       NearComponent.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/19/03
//****************************************************************************

#include 

#include 
#include 
#include 
#include 

namespace Obj
{
	
sSweepPruneArray CNearComponent::m_sweep_prune_array[3]	= { sSweepPruneArray( 0 ), sSweepPruneArray( 1 ), sSweepPruneArray( 2 ) };

	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent* CNearComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CNearComponent );	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNearComponent::CNearComponent() : CBaseComponent()
{
	SetType( CRC_NEAR );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNearComponent::~CNearComponent()
{
	// Remove from the axis arrays.
	for( int axis = 0; axis < 3; ++axis )
	{
		m_sweep_prune_array[axis].RemoveComponent( this );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNearComponent::InitFromStructure( Script::CStruct* pParams )
{
	// Not sure where bounding box dimensions will come from.
	m_untransformed_bbox.SetMin( Mth::Vector( -120.0f, -120.0f, -120.0f ));
	m_untransformed_bbox.SetMax( Mth::Vector(  120.0f,  120.0f,  120.0f ));

	// Obtain the position of the parent object, and build the transformed bounding box.
	if( GetObject())
	{
		Mth::Vector t;

		t = m_untransformed_bbox.GetMin() + GetObject()->GetPos();
		m_transformed_bbox.SetMin( t );

		t = m_untransformed_bbox.GetMax() + GetObject()->GetPos();
		m_transformed_bbox.SetMax( t );
	}

	// Now add this component to the axis arrays.
	for( int axis = 0; axis < 3; ++axis )
	{
		m_sweep_prune_array[axis].InsertComponent( this );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNearComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Remove from axis arrays, then reinitialise.
	for( int axis = 0; axis < 3; ++axis )
	{
		m_sweep_prune_array[axis].RemoveComponent( this );
	}

	InitFromStructure( pParams );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNearComponent::Update()
{
	// Re-calculate the bounding box position.
	if( GetObject())
	{
		Mth::Vector t;

		t = m_untransformed_bbox.GetMin() + GetObject()->GetPos();
		m_transformed_bbox.SetMin( t );

		t = m_untransformed_bbox.GetMax() + GetObject()->GetPos();
		m_transformed_bbox.SetMax( t );

		// Resort the component.
		for( int axis = 0; axis < 3; ++axis )
		{
			m_sweep_prune_array[axis].ResortComponent( this );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent::EMemberFunctionResult CNearComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
/*
        // @script | DoSomething | does some functionality
		case 0xbb4ad101:		// DoSomething
			DoSomething();
			break;

        // @script | ValueIsTrue | returns a boolean value
		case 0x769260f7:		// ValueIsTrue
		{
			return ValueIsTrue() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		break;
*/

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNearComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert( p_info, ( "NULL p_info sent to CNearComponent::GetDebugInfo" ));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*	Example:
	p_info->AddInteger("m_never_suspend",m_never_suspend);
	p_info->AddFloat("m_suspend_distance",m_suspend_distance);
	*/
	
	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}



#define MAX_COMPONENTS_PER_WORKSPACE_ARRAY		64
static int				perAxisIntersections[3];
static CNearComponent*	perAxisWorkspace[3][MAX_COMPONENTS_PER_WORKSPACE_ARRAY];
static CNearComponent*	combinedAxisWorkspace[MAX_COMPONENTS_PER_WORKSPACE_ARRAY];




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int sSweepPruneArray::BinarySearchFirstBigger( float value )
{
	int first_bigger	= -1;
	int l				= 0;
	int u				= m_num_entries - 1;
	while( l <= u )
	{
		int m = ( l + u ) / 2;
		if( m_array[m].GetValue( m_axis ) <= value )
		{
			// x[m] <= t
			l = m + 1;
		}
		else
		{
			// x[m] > t
			first_bigger = m;
			u = m - 1;
		}
	}
	return first_bigger;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int sSweepPruneArray::BinarySearchLastSmaller( float value )
{
	int last_smaller	= -1;
	int l				= 0;
	int u				= m_num_entries - 1;
	while( l <= u )
	{
		int m = ( l + u ) / 2;
		if( m_array[m].GetValue( m_axis ) <= value )
		{
			// x[m] <= t
			last_smaller = m;
			l = m + 1;
		}
		else
		{
			// x[m] > t
			u = m - 1;
		}
	}
	return last_smaller;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int sSweepPruneArray::BuildCompositeIntersectingArray( void )
{
	// Now build the composite array of intersections that appear in all three axis lists.
	int final_intersections	= 0;
	int source, check1, check2;

	// Use the smallest list to check against.
	if(( perAxisIntersections[0] <= perAxisIntersections[1] ) && ( perAxisIntersections[0] <= perAxisIntersections[2] ))
	{
		source	= 0;
		check1	= 1;
		check2	= 2;
	}
	else if(( perAxisIntersections[1] <= perAxisIntersections[0] ) && ( perAxisIntersections[1] <= perAxisIntersections[2] ))
	{
		source	= 1;
		check1	= 0;
		check2	= 2;
	}
	else
	{
		source	= 2;
		check1	= 0;
		check2	= 1;
	}

	for( int i = 0; i < perAxisIntersections[source]; ++i )
	{
		bool found;
		
		// Is this intersection also in the first check list?
		found = false;
		for( int j = 0; j < perAxisIntersections[check1]; ++j )
		{
			if( perAxisWorkspace[source][i] == perAxisWorkspace[check1][j] )
			{
				found = true;
				break;
			}
		}

		// If it wasn't in the first check list, there's no intersection.
		if( !found )
		{
			break;
		}

		// Is this intersection also in the second check list?
		found = false;
		for( int j = 0; j < perAxisIntersections[check2]; ++j )
		{
			if( perAxisWorkspace[source][i] == perAxisWorkspace[check2][j] )
			{
				found = true;
				break;
			}
		}

		// If it wasn't in the second check list, there's no intersection.
		if( !found )
		{
			break;
		}

		// At this stage the intersection appeared in all three axis lists, so it is a valid intersection.
		// As a final check, make sure it isn't already in the final list.
		found = false;
		for( int j = 0; j < final_intersections; ++j )
		{
			if( combinedAxisWorkspace[j] == perAxisWorkspace[source][i] )
			{
				found = true;
				break;
			}
		}

		if( !found )
		{
			combinedAxisWorkspace[final_intersections] = perAxisWorkspace[source][i];
			++final_intersections;
		}
	}
	return final_intersections;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNearComponent** CNearComponent::GetIntersectingNearComponents( Mth::Vector &bbmin, Mth::Vector &bbmax, int *p_num_intersecting )
{
	// Zero axis intersection count array.
	perAxisIntersections[0] = perAxisIntersections[1] = perAxisIntersections[2] = 0;

	for( int axis = 0; axis < 3; ++axis )
	{
		// Find the start index.
		int start_index = m_sweep_prune_array[axis].BinarySearchFirstBigger( bbmin[axis] );
		if( start_index == -1 )
		{
			// Nothing was found, therefore there can be no intersections.
			*p_num_intersecting = 0;
			return NULL;
		}

		// Find the end index.
		int end_index = m_sweep_prune_array[axis].BinarySearchLastSmaller( bbmax[axis] );
		if( end_index == -1 )
		{
			// Nothing was found, therefore there can be no intersections.
			*p_num_intersecting = 0;
			return NULL;
		}

		Dbg_Assert( end_index > start_index );

		// Build up the workspace array for this axis.
		int intersections_for_this_axis = 0;
		for( int i = start_index; i <= end_index; ++i )
		{
			// Don't want to include the calling component in the list.
			if( m_sweep_prune_array[axis].m_array[i].mp_component != this )
			{
				perAxisWorkspace[axis][intersections_for_this_axis++] = m_sweep_prune_array[axis].m_array[i].mp_component;
			}
		}

		// If we found no intersections for this axis, there can be no components that intersect.
		if( intersections_for_this_axis == 0 )
		{
			*p_num_intersecting = 0;
			return NULL;
		}

		// Store off the number of intersections for this axis.
		perAxisIntersections[axis] = intersections_for_this_axis;

	}

	// Build the composite array.
	int final_intersections = sSweepPruneArray::BuildCompositeIntersectingArray();

	*p_num_intersecting = final_intersections;
	return ( final_intersections > 0 ) ? &combinedAxisWorkspace[0] : NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNearComponent** CNearComponent::GetIntersectingNearComponents( int *p_num_intersecting )
{
	// Zero axis intersection count array.
	perAxisIntersections[0] = perAxisIntersections[1] = perAxisIntersections[2] = 0;

	// Build up each workspace array.
	for( int axis = 0; axis < 3; ++axis )
	{
		int i;
		for( i = 0; i < m_sweep_prune_array[axis].m_num_entries; ++i )
		{
			if( m_sweep_prune_array[axis].m_array[i].mp_component == this )
			{
				// We have found the start of the span for this component.
				Dbg_Assert( m_sweep_prune_array[axis].m_array[i].m_min_max == 0 );
				++i;
				break;
			}
		}

		// Now add any other components we come across before we hit the end of the span.
		while(( i < m_sweep_prune_array[axis].m_num_entries ) && ( m_sweep_prune_array[axis].m_array[i].mp_component != this ))
		{
			perAxisWorkspace[axis][perAxisIntersections[axis]] = m_sweep_prune_array[axis].m_array[i].mp_component;
			++perAxisIntersections[axis];
			++i;
		}

		// If we found no intersections for this axis, there can be no components that intersect.
		if( perAxisIntersections[axis] == 0 )
		{
			*p_num_intersecting = 0;
			return NULL;
		}
	}

	// Build the composite array.
	int final_intersections = sSweepPruneArray::BuildCompositeIntersectingArray();

	*p_num_intersecting = final_intersections;
	return ( final_intersections > 0 ) ? &combinedAxisWorkspace[0] : NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sSweepPruneEntry::SetValues( void )
{
	Mth::Vector v	= ( m_min_max ) ? mp_component->GetMax() : mp_component->GetMin();
	m_value[0]		= v[0];
	m_value[1]		= v[1];
	m_value[2]		= v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sSweepPruneArray::sSweepPruneArray( int axis )
{
	m_axis			= axis;
	m_num_entries	= 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sSweepPruneArray::~sSweepPruneArray( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sSweepPruneArray::PushUp( int index )
{
	Dbg_MsgAssert( m_num_entries < MAX_SWEEP_PRUNE_ENTRIES_PER_AXIS, ( "CNearComponent::SweepPruneList overflow" ));

	// All array entries from index (inclusive) upwards are moved up one position.
	for( int i = m_num_entries - 1; i >= index; --i )
	{
		m_array[i + 1] = m_array[i];
	}
	++m_num_entries;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sSweepPruneArray::PullDown( int index )
{
	Dbg_MsgAssert( m_num_entries > 0, ( "CNearComponent::SweepPruneList underflow" ));

	// All array entries from index (inclusive) upwards are moved down one position.
	for( int i = index; i < ( m_num_entries - 1 ); ++i )
	{
		m_array[i] = m_array[i + 1];
	}
	--m_num_entries;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sSweepPruneArray::ResortComponent( CNearComponent *p_component )
{
	// Does a simple check to see whether the component is still in the correct order.
	// If it isn't, sorting is applied.

	// Check min first.
	int i;
	for( i = 0; i < m_num_entries; ++i )
	{
		if( m_array[i].mp_component == p_component )
		{
			// This should be the min entry.
			Dbg_Assert( m_array[i].m_min_max == 0 );

			// Set the new min value.
			m_array[i].SetValues();

			// Check this is bigger than or equal to the previous value, if there is one.
			while(( i > 0 ) && ( m_array[i].GetValue( m_axis ) < m_array[i - 1].GetValue( m_axis )))
			{
				// Move this entry down in the array.
				sSweepPruneEntry swap	= m_array[i];
				m_array[i]				= m_array[i - 1];
				m_array[i - 1]			= swap;
				--i;
			}

			// Check this is smaller than or equal to the next value, if there is one.
			while(( i < ( m_num_entries - 1)) && ( m_array[i].GetValue( m_axis ) > m_array[i + 1].GetValue( m_axis )))
			{
				// Move this entry up in the array.
				sSweepPruneEntry swap	= m_array[i];
				m_array[i]				= m_array[i + 1];
				m_array[i + 1]			= swap;
				++i;
			}
			break;
		}
	}
	
	// Now check max.
	++i;
	for( ; i < m_num_entries; ++i )
	{
		if( m_array[i].mp_component == p_component )
		{
			// This should be the max entry.
			Dbg_Assert( m_array[i].m_min_max == 1 );

			// Set the new max value.
			m_array[i].SetValues();

			// Check this is bigger than or equal to the previous value, if there is one.
			while(( i > 0 ) && ( m_array[i].GetValue( m_axis ) < m_array[i - 1].GetValue( m_axis )))
			{
				// Move this entry down in the array.
				sSweepPruneEntry swap	= m_array[i];
				m_array[i]				= m_array[i - 1];
				m_array[i - 1]			= swap;
				--i;
			}

			// Check this is smaller than or equal to the next value, if there is one.
			while(( i < ( m_num_entries - 1)) && ( m_array[i].GetValue( m_axis ) > m_array[i + 1].GetValue( m_axis )))
			{
				// Move this entry up in the array.
				sSweepPruneEntry swap	= m_array[i];
				m_array[i]				= m_array[i + 1];
				m_array[i + 1]			= swap;
				++i;
			}
			break;
		}
	}
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sSweepPruneArray::InsertComponent( CNearComponent *p_component )
{
	float axis_min = p_component->GetMin()[m_axis];
	float axis_max = p_component->GetMax()[m_axis];

	// Scan through until we find an entry that is bigger than the minimum for this axis, or we come to the end of the list.
	for( int i = 0; i <= m_num_entries; ++i )
	{
		if(( i == m_num_entries ) || ( m_array[i].mp_component->GetMin()[m_axis] > axis_min ))
		{
			// Insert this entry here.
			PushUp( i );
			m_array[i].m_min_max	= 0;
			m_array[i].mp_component = p_component;

			// Set the new min value.
			m_array[i].SetValues();
			break;
		}
	}

	// Now scan through until we find an entry that is bigger than the maximum for this axis, or we come to the end of the list.
	for( int i = 0; i <= m_num_entries; ++i )
	{
		if(( i == m_num_entries ) || ( m_array[i].mp_component->GetMax()[m_axis] > axis_max ))
		{
			// Insert this entry here.
			PushUp( i );
			m_array[i].m_min_max	= 1;
			m_array[i].mp_component = p_component;

			// Set the new min value.
			m_array[i].SetValues();
			break;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sSweepPruneArray::RemoveComponent( CNearComponent *p_component )
{
	// The component should appear twice in the list.
	for( int p = 0; p < 2; ++p )
	{
		// Scan through until we find this entry.
		for( int i = 0; i < m_num_entries; ++i )
		{
			if( m_array[i].mp_component == p_component )
			{
				// Remove this entry.
				PullDown( i );
				break;
			}
		}
	}
}















}


#endif


================================================
FILE: Code/Gel/Components/NearComponent.h
================================================
#if 0
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       NearComponent.h
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#ifndef __COMPONENTS_NEARCOMPONENT_H__
#define __COMPONENTS_NEARCOMPONENT_H__

#include 
#include 
#include 
#include 

#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_NEAR							CRCD( 0x89b3e3ee, "near" )

//  Standard accessor macros for getting the component either from within an object, or given an object.
#define		GetNearComponent()					((Obj::CNearComponent*)GetComponent(CRC_NEAR))
#define		GetNearComponentFromObject( pObj )	((Obj::CNearComponent*)(pObj)->GetComponent(CRC_NEAR))

#define		MAX_SWEEP_PRUNE_ENTRIES_PER_AXIS	512

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	
class CNearComponent;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sSweepPruneEntry
{
	uint32				m_min_max;			// 0 for min, 1 for max
	float				m_value[3];			// This proves much quicker than dereferencing the component and reading the bounding box.
	CNearComponent		*mp_component;		// Pointer to the actual component.

	inline float		GetValue( int axis )	{ return m_value[axis]; }
	void				SetValues( void );
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sSweepPruneArray
{
public:
	int					m_axis;
	int					m_num_entries;
	sSweepPruneEntry	m_array[MAX_SWEEP_PRUNE_ENTRIES_PER_AXIS];

						sSweepPruneArray( int axis );
						~sSweepPruneArray( void );

	int					BinarySearchFirstBigger( float value );
	int					BinarySearchLastSmaller( float value );
	void				ResortComponent( CNearComponent *p_component );
	void				InsertComponent( CNearComponent *p_component );
	void				RemoveComponent( CNearComponent *p_component );
	static int			BuildCompositeIntersectingArray( void );

private:
	void				PushUp( int index );
	void				PullDown( int index );
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CNearComponent : public CBaseComponent
{
public:
    CNearComponent();
    virtual ~CNearComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	Mth::Vector						GetMin( void )	{ return m_transformed_bbox.GetMin(); }
	Mth::Vector						GetMax( void )	{ return m_transformed_bbox.GetMax(); }
	CNearComponent**				GetIntersectingNearComponents( int *p_num_intersecting );
	CNearComponent**				GetIntersectingNearComponents( Mth::Vector &bbmin, Mth::Vector &bbmax, int *p_num_intersecting );

	static CBaseComponent*			s_create();

private:

	Mth::CBBox						m_untransformed_bbox;
	Mth::CBBox						m_transformed_bbox;
	static sSweepPruneArray			m_sweep_prune_array[3];
};



}

#endif

#endif


================================================
FILE: Code/Gel/Components/NodeArrayComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       NodeArrayComponent.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/17/03
//****************************************************************************

// The CEmptyComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy emptycomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "Empty" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage
#include 

#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  
CBaseComponent* CNodeArrayComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CNodeArrayComponent );	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CNodeArrayComponent::CNodeArrayComponent() : CBaseComponent()
{
	SetType( CRC_NODEARRAY );

	mp_node_array = NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNodeArrayComponent::~CNodeArrayComponent()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CNodeArrayComponent::InitFromStructure( Script::CStruct* pParams )
{
	// Obtain the model name from the structure. At a later date, perhaps the node could override the .qb name.
	const char *p_model_name = NULL;
	if( !pParams->GetText( 0x286a8d26, &p_model_name ))
	{
		// This is currently an error.
		Dbg_MsgAssert( 0, ( "Attempting to create NodeArrayComponent from node with no model name" ));
		return;
	}

	// Generate the .qb name from the model name.
	char nodearray_filename[256];
	strcpy( nodearray_filename, "models\\" );
	strcat( nodearray_filename, p_model_name );
	char *p_ext = strstr( nodearray_filename, "." );
	if( p_ext )
	{
		strcpy( p_ext, ".qb" );
	}
	else
	{
		strcat( nodearray_filename, ".qb" );
	}

	// Ask the Asset Manager for the .qb - it will be loaded if it is not yet memory resident.
	Ass::CAssMan *p_ass_man = Ass::CAssMan::Instance();
	mp_node_array = (Script::CArray*)p_ass_man->LoadOrGetAsset( nodearray_filename, false, false, false, 0 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CNodeArrayComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Obtain the model name from the structure. At a later date, perhaps the node could override the .qb name.
	const char *p_model_name = NULL;
	if( !pParams->GetText( 0x286a8d26, &p_model_name ))
	{
		// Not an error during a refresh, just exit with existing values maintained.
		return;
	}

	InitFromStructure( pParams );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CNodeArrayComponent::Update()
{
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CNodeArrayComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch( Checksum )
	{
/*
        // @script | DoSomething | does some functionality
		case 0xbb4ad101:		// DoSomething
			DoSomething();
			break;

        // @script | ValueIsTrue | returns a boolean value
		case 0x769260f7:		// ValueIsTrue
		{
			return ValueIsTrue() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		break;
*/

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNodeArrayComponent::GetDebugInfo( Script::CStruct *p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert( p_info, ("NULL p_info sent to CNodeArrayComponent::GetDebugInfo" ));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums
	p_info->AddArray( "mp_node_array", mp_node_array );

	// We call the base component's GetDebugInfo, so we can add info from the common base component.
	CBaseComponent::GetDebugInfo( p_info );
#endif				 
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/NodeArrayComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       NodeArrayComponent.h
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/17/03
//****************************************************************************

#ifndef __COMPONENTS_NODEARRAYCOMPONENT_H__
#define __COMPONENTS_NODEARRAYCOMPONENT_H__

#include 
#include 

#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_NODEARRAY							CRCD( 0xc472ecc5, "NodeArray" )
#define		GetNodeArrayComponent()					((Obj::CNodeArrayComponent*)GetComponent( CRC_NODEARRAY ))
#define		GetNodeArrayComponentFromObject( pObj )	((Obj::CNodeArrayComponent*)(pObj)->GetComponent( CRC_NODEARRAY ))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CNodeArrayComponent : public CBaseComponent
{
public:
    CNodeArrayComponent();
    virtual ~CNodeArrayComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

	virtual Script::CArray*			GetNodeArray( void )	{ return mp_node_array; }

private:
	Script::CArray*					mp_node_array;
};

}

#endif


================================================
FILE: Code/Gel/Components/ObjectHookManagerComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ObjectHookManagerComponent.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/18/03
//****************************************************************************

// The CEmptyComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy emptycomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "Empty" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage
#include 

#include 
#include 

#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  
CBaseComponent* CObjectHookManagerComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CObjectHookManagerComponent );	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CObjectHookManagerComponent::CObjectHookManagerComponent() : CBaseComponent()
{
	SetType( CRC_OBJECTHOOKMANAGER );
	mp_object_hook_manager = NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CObjectHookManagerComponent::~CObjectHookManagerComponent()
{
	// Remove the CObjectHookManager if present.
	if( mp_object_hook_manager )
	{
		delete mp_object_hook_manager;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CObjectHookManagerComponent::InitFromStructure( Script::CStruct* pParams )
{
	// There needs to be a NodeArrayComponent attached for the ObjectHookManagerComponent to operate.
	CNodeArrayComponent *p_nodearray_component = GetNodeArrayComponentFromObject( GetObject());
	Dbg_MsgAssert( p_nodearray_component, ( "ObjectHookManagerComponent created without NodeArrayComponent" ));

	// Remove the CObjectHookManager if present.
	if( mp_object_hook_manager )
	{
		delete mp_object_hook_manager;
	}

	// Create a CObjectHookManager, and parse the node array for the hooks.
	mp_object_hook_manager = new Obj::CObjectHookManager();
	mp_object_hook_manager->AddObjectHooksFromNodeArray( p_nodearray_component->GetNodeArray());
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CObjectHookManagerComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure( pParams );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CObjectHookManagerComponent::Update()
{
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CObjectHookManagerComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch( Checksum )
	{
/*
        // @script | DoSomething | does some functionality
		case 0xbb4ad101:		// DoSomething
			DoSomething();
			break;

        // @script | ValueIsTrue | returns a boolean value
		case 0x769260f7:		// ValueIsTrue
		{
			return ValueIsTrue() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		break;
*/

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CObjectHookManagerComponent::GetDebugInfo( Script::CStruct *p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert( p_info, ("NULL p_info sent to CObjectHookManagerComponent::GetDebugInfo" ));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	if( mp_object_hook_manager )
	{
		p_info->AddString("ObjectHookManager.m_is_transformed", mp_object_hook_manager->IsMoving() ? "yes" : "no" );
	}
	
	// We call the base component's GetDebugInfo, so we can add info from the common base component.
	CBaseComponent::GetDebugInfo( p_info );
#endif				 
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/ObjectHookManagerComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ObjectHookManagerComponent.h
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/18/03
//****************************************************************************

#ifndef __COMPONENTS_OBJECTHOOKMANAGERCOMPONENT_H__
#define __COMPONENTS_OBJECTHOOKMANAGERCOMPONENT_H__

#include 
#include 

#include 
#include 	

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_OBJECTHOOKMANAGER							CRCD( 0x27443708, "ObjectHookManager" )
#define		GetObjectHookManagerComponent()					((Obj::CObjectHookManagerComponent*)GetComponent( CRC_OBJECTHOOKMANAGER ))
#define		GetObjectHookManagerComponentFromObject( pObj )	((Obj::CObjectHookManagerComponent*)(pObj)->GetComponent( CRC_OBJECTHOOKMANAGER ))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CObjectHookManagerComponent : public CBaseComponent
{
public:
										CObjectHookManagerComponent();
    virtual								~CObjectHookManagerComponent();

public:
    virtual void            			Update();
    virtual void            			InitFromStructure( Script::CStruct* pParams );
    virtual void            			RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult		CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 						GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*				s_create();

	virtual Obj::CObjectHookManager*	GetObjectHookManager( void )	{ return mp_object_hook_manager; }

private:
	Obj::CObjectHookManager*			mp_object_hook_manager;
};

}

#endif


================================================
FILE: Code/Gel/Components/ParticleComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ParticleComponent.cpp
//* OWNER:          SPG
//* CREATION DATE:  3/21/03
//****************************************************************************

// The CEmptyComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy emptycomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "Empty" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage

#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CParticleComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CParticleComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CParticleComponent::CParticleComponent() : CBaseComponent()
{
    SetType( CRC_PARTICLE );

	m_update_script = 0;
	mp_particle = NULL;
	m_system_lifetime = 0;
	m_birth_time = Tmr::GetTime();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CParticleComponent::~CParticleComponent()
{	
	if( mp_particle )
	{
		// Remove it from the particle manager
		Nx::CEngine::sGetParticleManager()->KillParticle( mp_particle );
		// call the destroy function (platform specific cleanup)
		mp_particle->Destroy();
		// and delete it
		delete mp_particle;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CParticleComponent::InitFromStructure( Script::CStruct* pParams )
{
	int color;
	float scale;
	Script::CArray* array;
	Nx::CParticleParams part;
	Mth::Vector angles;
	bool generate_name;
	Mth::Matrix orientation;
	// ** Add code to parse the structure, and initialize the component

	scale = 1.0f;
	generate_name = false;
	pParams->GetChecksum( CRCD(0x92276db,"UpdateScript"), &m_update_script );
	
	pParams->GetInteger( CRCD(0x5a235299,"SystemLifetime"), (int*) &m_system_lifetime );
	if( pParams->ContainsFlag(CRCD(0x18c671f0,"noname")))
	{
		// Generate a somewhat-unique id
		generate_name = true;
		part.m_Name = Tmr::GetTime();
	}
	else
	{
		pParams->GetChecksum( CRCD(0xa1dc81f9,"Name"), &part.m_Name );
	}

	pParams->GetChecksum( CRCD(0x7321a8d6,"Type"), &part.m_Type );
	part.m_UseMidpoint = pParams->ContainsFlag(CRCD(0xa7a5bffb,"UseMidPoint"));
	
	pParams->GetFloat( CRCD(0x13b9da7b,"scale"), &scale, false );

	pParams->GetFloat( CRCD(0x3bee67c8,"StartRadius"), &part.m_Radius[Nx::vBOX_START] );
	pParams->GetFloat( CRCD(0x8e39913d,"MidRadius"), &part.m_Radius[Nx::vBOX_MID] );
	pParams->GetFloat( CRCD(0x3f243a3c,"EndRadius"), &part.m_Radius[Nx::vBOX_END] );

	orientation.Ident();
	if( pParams->ContainsFlag( CRCD(0x7045843a,"orient_to_vel" )))
	{
		Mth::Vector vel;
	
        // Create a matrix with -vel as the AT vector of the matrix and transform the system
		// by that matrix
		pParams->GetVector(CRCD(0xc4c809e, "vel"), &vel, Script::ASSERT);
		vel.Normalize();

		orientation[Mth::AT] = vel;
		if( orientation[Mth::AT] == Mth::Vector( 0, 1, 0 ))
		{
			orientation[Mth::RIGHT] = Mth::CrossProduct( vel, Mth::Vector( 0, 0, -1 ));
		}
		else
		{
			orientation[Mth::RIGHT] = Mth::CrossProduct( vel, Mth::Vector( 0, 1, 0 ));
		}
		orientation[Mth::UP] = Mth::CrossProduct( orientation[Mth::AT], orientation[Mth::RIGHT] );
	}

	part.m_Radius[Nx::vBOX_START] *= scale;
	part.m_Radius[Nx::vBOX_MID] *= scale;
	part.m_Radius[Nx::vBOX_END] *= scale;

	pParams->GetFloat( CRCD(0x5400a25,"StartRadiusSpread"), &part.m_RadiusSpread[Nx::vBOX_START] );
	pParams->GetFloat( CRCD(0xff7d9aa9,"MidRadiusSpread"), &part.m_RadiusSpread[Nx::vBOX_MID] );
	pParams->GetFloat( CRCD(0x5539fcee,"EndRadiusSpread"), &part.m_RadiusSpread[Nx::vBOX_END] );

	part.m_RadiusSpread[Nx::vBOX_START] *= scale;
	part.m_RadiusSpread[Nx::vBOX_MID] *= scale;
	part.m_RadiusSpread[Nx::vBOX_END] *= scale;

	pParams->GetInteger( CRCD(0x9e52a20f,"MaxStreams"), &part.m_MaxStreams );
	pParams->GetFloat( CRCD(0x35b65bd4,"EmitRate"), &part.m_EmitRate );
	pParams->GetFloat( CRCD(0xc218cf77,"Lifetime"), &part.m_Lifetime );
	pParams->GetFloat( CRCD(0x520682c6,"MidPointPCT"), &part.m_MidpointPct );
	
	if( pParams->ContainsFlag(CRCD(0x64f194ed,"UseStartPosition")))
	{
		pParams->GetVector( CRCD(0x3d5ea6a4,"StartPosition"), &part.m_BoxPos[Nx::vBOX_START] );
	}
	else
	{
		pParams->GetVector( CRCD(0x7f261953,"Pos"), &part.m_BoxPos[Nx::vBOX_START] );
	}
	
	pParams->GetVector( CRCD(0x4b51a2,"MidPosition"), &part.m_BoxPos[Nx::vBOX_MID] );
	pParams->GetVector( CRCD(0x5854ab9e,"EndPosition"), &part.m_BoxPos[Nx::vBOX_END] );

	part.m_BoxPos[Nx::vBOX_START] *= scale;
	part.m_BoxPos[Nx::vBOX_MID] *= scale;
	part.m_BoxPos[Nx::vBOX_END] *= scale;

	part.m_BoxPos[Nx::vBOX_START] = orientation.Transform( part.m_BoxPos[Nx::vBOX_START] );
	part.m_BoxPos[Nx::vBOX_MID] = orientation.Transform( part.m_BoxPos[Nx::vBOX_MID] );
	part.m_BoxPos[Nx::vBOX_END] = orientation.Transform( part.m_BoxPos[Nx::vBOX_END] );

	pParams->GetVector( CRCD(0x9656f775,"BoxDimsStart"), &part.m_BoxDims[Nx::vBOX_START] );
	pParams->GetVector( CRCD(0xc3cd20a2,"BoxDimsMid"), &part.m_BoxDims[Nx::vBOX_MID] );
	pParams->GetVector( CRCD(0x829fe7dd,"BoxDimsEnd"), &part.m_BoxDims[Nx::vBOX_END] );

	part.m_BoxDims[Nx::vBOX_START] *= scale;
	part.m_BoxDims[Nx::vBOX_MID] *= scale;
	part.m_BoxDims[Nx::vBOX_END] *= scale;

	part.m_BoxDims[Nx::vBOX_START] = orientation.Transform( part.m_BoxDims[Nx::vBOX_START] );
	part.m_BoxDims[Nx::vBOX_MID] = orientation.Transform( part.m_BoxDims[Nx::vBOX_MID] );
	part.m_BoxDims[Nx::vBOX_END] = orientation.Transform( part.m_BoxDims[Nx::vBOX_END] );

	part.m_UseMidcolor = pParams->ContainsFlag(CRCD(0x454962ef,"UseColorMidTime"));

	pParams->GetFloat( CRCD(0x59463c93,"ColorMidTime"), &part.m_MidpointPct );

	if( pParams->GetArray( CRCD(0xdab75b6c,"StartRGB"), &array ))
	{
		color = array->GetInt( 0 );
		part.m_Color[Nx::vBOX_START].r = (uint8) color;
		color = array->GetInt( 1 );
		part.m_Color[Nx::vBOX_START].g = (uint8) color;
		color = array->GetInt( 2 );
		part.m_Color[Nx::vBOX_START].b = (uint8) color;
	}
	if( pParams->GetArray( CRCD(0x444769a,"MidRGB"), &array ))
	{
		color = array->GetInt( 0 );
		part.m_Color[Nx::vBOX_MID].r = (uint8) color;
		color = array->GetInt( 1 );
		part.m_Color[Nx::vBOX_MID].g = (uint8) color;
		color = array->GetInt( 2 );
		part.m_Color[Nx::vBOX_MID].b = (uint8) color;
	}
	if( pParams->GetArray( CRCD(0x5a3728e7,"EndRGB"), &array ))
	{
		color = array->GetInt( 0 );
		part.m_Color[Nx::vBOX_END].r = (uint8) color;
		color = array->GetInt( 1 );
		part.m_Color[Nx::vBOX_END].g = (uint8) color;
		color = array->GetInt( 2 );
		part.m_Color[Nx::vBOX_END].b = (uint8) color;
	}

	if( pParams->GetInteger( CRCD(0x4d83db4c,"StartAlpha"), &color ))
	{
		part.m_Color[Nx::vBOX_START].a = (uint8) color;
	}
	if( pParams->GetInteger( CRCD(0x4aba1ff1,"MidAlpha"), &color ))
	{
		part.m_Color[Nx::vBOX_MID].a = (uint8) color;
	}
	if( pParams->GetInteger( CRCD(0x5cf83aca,"EndAlpha"), &color ))
	{
		part.m_Color[Nx::vBOX_END].a = (uint8) color;
	}
	
	pParams->GetChecksum( CRCD(0xdd6bb3d5,"BlendMode"), &part.m_BlendMode );
	pParams->GetInteger( CRCD(0x9f677d4e,"AlphaCutoff"), &part.m_AlphaCutoff );
	pParams->GetInteger( CRCD(0x13d0be1d,"FixedAlpha"), &part.m_FixedAlpha );
	pParams->GetChecksum( CRCD(0x7d99f28d,"Texture"), &part.m_Texture );
	
	pParams->GetInteger( CRCD(0x4eedfae7,"lod_dist1"), &part.m_LODDistance1 );
	pParams->GetInteger( CRCD(0xd7e4ab5d,"lod_dist2"), &part.m_LODDistance2 );
	pParams->GetInteger( CRCD(0xe4279ba2,"SuspendDistance"), &part.m_SuspendDistance );

	part.m_RotMatrix.Ident();	  // Mick: THis was missing before, so not sure how it could have worked

	angles.Set();
	pParams->GetVector( CRCD(0x9d2d0915,"Angles"), &angles );

	part.m_RotMatrix.RotateX( angles[X] );
	part.m_RotMatrix.RotateY( angles[Y] );
	part.m_RotMatrix.RotateZ( angles[Z] );
	

	if (part.m_LocalCoord = pParams->ContainsFlag(CRCD(0xb7b7240d,"LocalSpace")))
	{
		for (int i = 0; i < Nx::vNUM_BOXES; i++)
		{
			part.m_LocalBoxPos[i] = part.m_BoxPos[i];
		}
	}

	mp_particle = Nx::CEngine::sGetParticleManager()->CreateParticle( &part, generate_name );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CParticleComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	//InitFromStructure(pParams);


}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CParticleComponent::Finalize()
{
	mp_suspend_component =  GetSuspendComponentFromObject( GetObject() );
}
	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CParticleComponent::Hide( bool should_hide )
{

	mp_particle->Hide(should_hide);

}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CParticleComponent::Update()
{
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();

	// If it's a limited-time particle system in net games (like fireballs) never suspend the
	// logic
	if (!mp_suspend_component || !mp_suspend_component->SkipLogic() || 
		( gamenet_man->InNetGame() && ( m_system_lifetime > 0 )))
	{
		// **  You would put in here the stuff that you would want to get run every frame
		// **  for example, a physics type component might update the position and orientation
		// **  and update the internal physics state 
		if( m_update_script )
		{
			Script::RunScript( m_update_script, NULL, GetObject() );
		}
	
		if( m_system_lifetime > 0 )
		{
			if(( Tmr::GetTime() - m_birth_time ) > m_system_lifetime )
			{
				GetObject()->MarkAsDead();
				return;
			}
		}
		// If we are using local coordinates, update all the world parameters in the
		// particle instance.
		if (mp_particle->GetParameters()->m_LocalCoord)
		{
			for (int i = 0; i < Nx::vNUM_BOXES; i++)
			{
				Mth::Vector world_pos(mp_particle->GetParameters()->m_LocalBoxPos[i] + GetObject()->m_pos );
				//Mth::Vector world_pos(mp_particle->GetParameters()->m_LocalBoxPos[i] + Mth::Vector(100.0f, 100.0f, 0.0f));
				
				mp_particle->SetBoxPos(i, &world_pos);
			}
		}
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CParticleComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case 0x88be2262:	// SetStartPos
		{
			Mth::Vector pos;

			pParams->GetVector( NONAME, &pos, true );
			if( mp_particle )
			{
				mp_particle->SetBoxPos( Nx::vBOX_START, &pos );
			}
			break;
		}
        case 0xbb6018f4:	// SetMidPos
		{
			Mth::Vector pos;

			pParams->GetVector( NONAME, &pos, true );
			if( mp_particle )
			{
				mp_particle->SetBoxPos( Nx::vBOX_MID, &pos );
			}
			break;
		}
		case 0xe5134689:	// SetEndPos
		{
			Mth::Vector pos;

			pParams->GetVector( NONAME, &pos, true );
			if( mp_particle )
			{
				mp_particle->SetBoxPos( Nx::vBOX_END, &pos );
			}
			break;
		}
		case 0xf71d696e:	// SetStartBoxDimensions
		{
			Mth::Vector dims;

			pParams->GetVector( NONAME, &dims, true );
			if( mp_particle )
			{
				mp_particle->SetBoxDims( Nx::vBOX_START, &dims );
			}
			break;
		}
		case 0xaab006d:		// SetMidBoxDimensions
		{
			Mth::Vector dims;

			pParams->GetVector( NONAME, &dims, true );
			if( mp_particle )
			{
				mp_particle->SetBoxDims( Nx::vBOX_MID, &dims );
			}
			break;
		}
		case 0xe2b99038:	// SetEndBoxDimensions
		{
			Mth::Vector dims;

			pParams->GetVector( NONAME, &dims, true );
			if( mp_particle )
			{
				mp_particle->SetBoxDims( Nx::vBOX_END, &dims );
			}
			break;
		}
		case 0xc036f4d0:	// SetUpdateScript
		{
			pParams->GetChecksum( NONAME, &m_update_script, true );
			break;
		}
		case 0xc6525c4e:	// SetEmitRate
		{
			float emit_rate;

			pParams->GetFloat( NONAME, &emit_rate, true );
			if( mp_particle )
			{
				mp_particle->SetEmitRate( emit_rate );
			}
			break;
		}
		case 0x31fcc8ed:	// SetLifetime
		{
			float lifetime;

			pParams->GetFloat( NONAME, &lifetime, true );
			if( mp_particle )
			{
				mp_particle->SetLifetime( lifetime );
			}
			break;
		}
		case 0x19af74d1:	// SetMidpointColorPct
		{
			float percent;

			pParams->GetFloat( NONAME, &percent, true );
			if( mp_particle )
			{
				mp_particle->SetMidpointColorPct( percent );
			}
			break;
		}
		case 0x4aa40fb5:	// SetMidPointPCT
		{
			float percent;

			pParams->GetFloat( NONAME, &percent, true );
			if( mp_particle )
			{
				mp_particle->SetMidpointPct( percent );
			}
			break;
		}
		case 0x234ceabb:	// SetStartRadius
		{
			float radius;

			pParams->GetFloat( NONAME, &radius, true );
			if( mp_particle )
			{
				mp_particle->SetRadius( Nx::vBOX_START, radius );
			}
			break;
		}
		case 0x9e100f60:	// SetMidRadius
		{
			float radius;

			pParams->GetFloat( NONAME, &radius, true );
			if( mp_particle )
			{
				mp_particle->SetRadius( Nx::vBOX_MID, radius );
			}
			break;
		}
		case 0x2f0da461:	// SetEndRadius
		{
			float radius;

			pParams->GetFloat( NONAME, &radius, true );
			if( mp_particle )
			{
				mp_particle->SetRadius( Nx::vBOX_END, radius );
			}
			break;
		}
		case 0xeeffae18:	// SetStartColor
		{
			Script::CArray* array;

			pParams->GetArray( NONAME, &array, true );
			if( mp_particle )
			{
				Image::RGBA color;

				color.r = array->GetInt( 0 );
				color.g = array->GetInt( 1 );
				color.b = array->GetInt( 2 );
				color.a = array->GetInt( 3 );
				mp_particle->SetColor( Nx::vBOX_START, &color );
			}
			break;
		}
		case 0xfe869e8:		// SetMidColor
		{
			Script::CArray* array;

			pParams->GetArray( NONAME, &array, true );
			if( mp_particle )
			{
				Image::RGBA color;

				color.r = array->GetInt( 0 );
				color.g = array->GetInt( 1 );
				color.b = array->GetInt( 2 );
				color.a = array->GetInt( 3 );
				mp_particle->SetColor( Nx::vBOX_MID, &color );
			}
			break;
		}
		case 0x19aa4cd3:	// SetEndColor
		{
			Script::CArray* array;

			pParams->GetArray( NONAME, &array, true );
			if( mp_particle )
			{
				Image::RGBA color;

				color.r = array->GetInt( 0 );
				color.g = array->GetInt( 1 );
				color.b = array->GetInt( 2 );
				color.a = array->GetInt( 3 );
				mp_particle->SetColor( Nx::vBOX_END, &color );
			}
			break;
		}
		
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CParticleComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Script::CArray* array;
	Nx::CParticleParams* part;

	Dbg_MsgAssert(p_info,("NULL p_info sent to CParticleComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	if( mp_particle )
	{
		part = mp_particle->GetParameters();

		p_info->AddChecksum( CRCD(0x92276db,"UpdateScript"), m_update_script );
		p_info->AddChecksum( CRCD(0x7321a8d6,"Type"), part->m_Type );
		p_info->AddChecksum( CRCD(0xa7a5bffb,"UseMidPoint"), part->m_UseMidpoint );
		
		p_info->AddFloat(CRCD(0x3bee67c8,"StartRadius"), part->m_Radius[Nx::vBOX_START] );
		p_info->AddFloat(CRCD(0x8e39913d,"MidRadius"), part->m_Radius[Nx::vBOX_MID] );
		p_info->AddFloat(CRCD(0x3f243a3c,"EndRadius"), part->m_Radius[Nx::vBOX_END] );
	
		p_info->AddInteger(CRCD(0x9e52a20f,"MaxStreams"), part->m_MaxStreams );
		p_info->AddFloat(CRCD(0x35b65bd4,"EmitRate"), part->m_EmitRate );
		p_info->AddFloat(CRCD(0xc218cf77,"Lifetime"), part->m_Lifetime );
		p_info->AddFloat(CRCD(0x520682c6,"MidPointPCT"), part->m_MidpointPct );
		
		p_info->AddVector(CRCD(0x3d5ea6a4,"StartPosition"), part->m_BoxPos[Nx::vBOX_START] );
		p_info->AddVector(CRCD(0x4b51a2,"MidPosition"), part->m_BoxPos[Nx::vBOX_MID] );
		p_info->AddVector(CRCD(0x5854ab9e,"EndPosition"), part->m_BoxPos[Nx::vBOX_END] );
	
		p_info->AddVector(CRCD(0x9656f775,"BoxDimsStart"), part->m_BoxDims[Nx::vBOX_START] );
		p_info->AddVector(CRCD(0xc3cd20a2,"BoxDimsMid"), part->m_BoxDims[Nx::vBOX_MID] );
		p_info->AddVector(CRCD(0x829fe7dd,"BoxDimsEnd"), part->m_BoxDims[Nx::vBOX_END] );
	
		p_info->AddChecksum(CRCD(0x454962ef,"UseColorMidTime"), part->m_UseMidcolor );
		
		p_info->AddFloat(CRCD(0x59463c93,"ColorMidTime"), part->m_ColorMidpointPct );
	
		array = new Script::CArray;
		array->SetSizeAndType( 4, ESYMBOLTYPE_INTEGER );
		array->SetInteger( 0, part->m_Color[Nx::vBOX_START].r );
		array->SetInteger( 1, part->m_Color[Nx::vBOX_START].g );
		array->SetInteger( 2, part->m_Color[Nx::vBOX_START].b );
		array->SetInteger( 3, part->m_Color[Nx::vBOX_START].a );
		p_info->AddArray(CRCD(0xfb35aacf,"StartColor"), array );
		delete array;
	
		array = new Script::CArray;
		array->SetSizeAndType( 4, ESYMBOLTYPE_INTEGER );
		array->SetInteger( 0, part->m_Color[Nx::vBOX_MID].r );
		array->SetInteger( 1, part->m_Color[Nx::vBOX_MID].g );
		array->SetInteger( 2, part->m_Color[Nx::vBOX_MID].b );
		array->SetInteger( 3, part->m_Color[Nx::vBOX_MID].a );
		p_info->AddArray(CRCD(0xfc0c6e72,"MidColor"), array );
		delete array;
	
		array = new Script::CArray;
		array->SetSizeAndType( 4, ESYMBOLTYPE_INTEGER );
		array->SetInteger( 0, part->m_Color[Nx::vBOX_END].r );
		array->SetInteger( 1, part->m_Color[Nx::vBOX_END].g );
		array->SetInteger( 2, part->m_Color[Nx::vBOX_END].b );
		array->SetInteger( 3, part->m_Color[Nx::vBOX_END].a );
		p_info->AddArray(CRCD(0xea4e4b49,"EndColor"), array );
		delete array;
	
		p_info->AddChecksum(CRCD(0xdd6bb3d5,"BlendMode"), part->m_BlendMode );
		p_info->AddInteger(CRCD(0x9f677d4e,"AlphaCutoff"), part->m_AlphaCutoff );
		p_info->AddChecksum(CRCD(0x7d99f28d,"Texture"), part->m_Texture );
		
		p_info->AddInteger(CRCD(0x4eedfae7,"lod_dist1"), part->m_LODDistance1 );
		p_info->AddInteger(CRCD(0xd7e4ab5d,"lod_dist2"), part->m_LODDistance2 );
		p_info->AddInteger(CRCD(0xe4279ba2,"SuspendDistance"), part->m_SuspendDistance );
	}

// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/ParticleComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ParticleComponent.h
//* OWNER:          SPG
//* CREATION DATE:  03/21/03
//****************************************************************************

#ifndef __COMPONENTS_PARTICLECOMPONENT_H__
#define __COMPONENTS_PARTICLECOMPONENT_H__

#include 
#include 

#include 
#include 
#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_PARTICLE CRCD(0xa9db601e,"Particle")

//  Standard accessor macros for getting the component either from within an object, or 
//  given an object				 
#define		GetParticleComponent() ((Obj::CParticleComponent*)GetComponent(CRC_PARTICLE))
#define		GetParticleComponentFromObject(pObj) ((Obj::CParticleComponent*)(pObj)->GetComponent(CRC_PARTICLE))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CParticleComponent : public CBaseComponent
{
public:
    CParticleComponent();
    virtual ~CParticleComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void					Hide( bool should_hide );
	virtual void					Finalize();
		
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

private:
	uint32				m_update_script;
	Nx::CNewParticle*	mp_particle;
	CSuspendComponent*	mp_suspend_component;
	Tmr::Time			m_birth_time;
	uint32				m_system_lifetime;
};

}

#endif


================================================
FILE: Code/Gel/Components/PedLogicComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       PedLogicComponent.cpp
//* OWNER:          Brad Bulkley
//* CREATION DATE:  1/9/03
//****************************************************************************

// start autoduck documentation
// @DOC pedlogiccomponent
// @module pedlogiccomponent | None
// @subindex Scripting Database
// @index script | pedlogiccomponent

#include 
#include 

#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 

#include 

#include 

namespace Obj
{

#define	vDEFAULT_RANGE (10.0f)
#define vACTION_WEIGHT_RESOLUTION (1000)

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CPedLogicComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CPedLogicComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPedLogicComponent::CPedLogicComponent() : CBaseComponent()
{
	SetType( CRC_PEDLOGIC );

	// reset whiskers
	for ( int i = 0; i < vNUM_WHISKERS; i++ )
		m_whiskers[i] = 0.0;

	ResetBias();
	m_turn_frames = 0;
	m_max_turn_frames = 0; // Script::GetInteger( CRCD( 0x793535f5, "ped_turn_frames" ), Script::ASSERT );
	mp_path_object_tracker = NULL;

	m_col_dist_above = Script::GetFloat( CRCD( 0x379d93d2, "ped_skater_stick_dist_above" ), Script::ASSERT );
	m_col_dist_below = Script::GetFloat( CRCD( 0x8715b7b2, "ped_skater_stick_dist_below" ), Script::ASSERT );

	m_flags = 0;

	m_original_max_vel = 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPedLogicComponent::~CPedLogicComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::InitFromStructure( Script::CStruct* pParams )
{
	// ** Add code to parse the structure, and initialize the component
	// m_state = CRCD( 0x23db4aea, "Idle" );s

	pParams->GetInteger( CRCD( 0xe50d6573, "nodeIndex" ), &m_node_from, Script::ASSERT );
	// printf("m_node_from = %i\n", m_node_from);
	SkateScript::GetPosition( m_node_from, &m_wp_from );
	m_normal_lerp = 0.0f;
	m_display_normal = m_last_display_normal = m_current_normal = GetObject()->m_matrix[Y];

	pParams->GetFloat( CRCD( 0x367c4a1b, "StickToGroundDistAbove" ), &m_col_dist_above );
	pParams->GetFloat( CRCD( 0x86f46e7b, "StickToGroundDistBelow" ), &m_col_dist_below );

	m_current_display_matrix = GetObject()->GetDisplayMatrix();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::Update()
{	
	m_time = Tmr::FrameLength();
	
	// BB - Switch on the state and call the appropriate update function.
	// This step could be removed by having the logic for each state in 
	// this function, but this will make it easier to split the component 
	// if need be.  Plus it just looks cleaner.
	switch ( m_state )
	{
		case CRCC( 0x69b05e2c, "generic" ):
			GenericPedUpdate();
			break;
		case CRCC( 0xa85af587, "generic_skater" ):
			GenericSkaterUpdate();
			break;
		case CRCC(0x6367167b,"lip_trick"):
			UpdateLipDisplayMatrix();
			break;
		case CRCC( 0x82b45a67, "generic_standing" ):
			// this is currently handled in script
			break;
	default:
		// Dbg_MsgAssert( 0, ( "PedLogicComponent has unknown state %s", Script::FindChecksumName( m_state ) ) );
		break;
	}
	// Gfx::AddDebugArrow( GetObject()->GetDisplayMatrix()[X] + GetObject()->m_pos, GetObject()->GetDisplayMatrix()[X] * 48 + GetObject()->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 1 );
	// Gfx::AddDebugArrow( GetObject()->GetDisplayMatrix()[Y] + GetObject()->m_pos, GetObject()->GetDisplayMatrix()[Y] * 48 + GetObject()->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 0, 128 ), 1 );
	// Gfx::AddDebugArrow( GetObject()->GetDisplayMatrix()[Z] + GetObject()->m_pos, GetObject()->GetDisplayMatrix()[Z] * 48 + GetObject()->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 0, 0, 128 ), 1 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CPedLogicComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | Ped_SetLogicState | set the current state of the ped
		// @uparm name | state - eg, generic, generic_skater, etc.
		case CRCC( 0x3685b765, "Ped_SetLogicState" ):
			pParams->GetChecksum( NONAME, &m_state, Script::ASSERT );
			break;
		// @script | Ped_InitPath | initializes the ped on his linked path and starts movement
		case CRCC( 0xe9906815, "Ped_InitPath" ):
		{
			Script::CStruct* pNodeData = SkateScript::GetNode( m_node_from );
			// m_node_to = SkateScript::GetLink( pNodeData, Mth::Rnd( SkateScript::GetNumLinks( pNodeData ) ) );
			
			#ifdef __NOPT_ASSERT__
			int numLinks = SkateScript::GetNumLinks( m_node_from );
			Dbg_MsgAssert( numLinks, ( "Ped_InitPath called on %s, but path node %i has no links!", Script::FindChecksumName( GetObject()->GetID() ), m_node_from ) );
			#endif

			m_node_to = SkateScript::GetLink( pNodeData, 0 );
			SkateScript::GetPosition( m_node_to, &m_wp_to );
			SelectNextWaypoint();
			m_flags |= PEDLOGIC_MOVING_ON_PATH;
			mp_path_object_tracker = Obj::CPathMan::Instance()->TrackPed( GetObject(), m_node_to );
			
			Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
			Dbg_Assert( pMotionComp );
			pMotionComp->m_movingobj_status |= MOVINGOBJ_STATUS_ON_PATH;

			// turn the ped to face the right direction
			Mth::Vector heading = ( m_wp_to - m_wp_from ).Normalize();
			Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix();
			display_matrix[Z] = heading;
			display_matrix.OrthoNormalizeAbout( Z );
			GetObject()->SetDisplayMatrix( display_matrix );
			m_current_display_matrix = display_matrix;

			Mth::Vector current_pos = GetObject()->m_pos;
			for ( int i = 0; i < vPOS_HISTORY_SIZE; i++ )
				m_pos_history[i] = current_pos;

			break;
		}
		// @script | Ped_StartMoving | start moving on path
		case CRCC( 0xd711542, "Ped_StartMoving" ):
		{
			if ( m_flags & PEDLOGIC_STOPPED )
			{
				Script::CScript* pScript = Script::SpawnScript( CRCD( 0xbed339d4, "ped_skater_start_moving" ) );
				pScript->mpObject = GetObject();
				m_flags &= ~PEDLOGIC_STOPPED;
			}
			m_flags |= PEDLOGIC_MOVING_ON_PATH;
			break;
		}
		// @script | Ped_StopMoving | stop moving on path
		case CRCC( 0xcc85adfe, "Ped_StopMoving" ):
			m_flags &= ~PEDLOGIC_MOVING_ON_PATH;
			break;
		// @script | Ped_IsMoving | returns true if the ped is moving on the path
		case CRCC(0x68d298b0,"Ped_IsMoving"):
			return m_flags & PEDLOGIC_MOVING_ON_PATH ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;
		// @script | Ped_SetStickToGroundDist | Set the distances the ped uses to check for ground
		// when sticking.  This check is not performed straight up and down, but at whatever
		// angle the ped is currently standing (his Y vector).
		// @parmopt float | StickToGroundDistAbove | | distance to check above the ped
		// @parmopt float | StickToGroundDistBelow | | distance to check below the ped
		case CRCC( 0x2cc086dd, "Ped_SetStickToGroundDist" ):
			pParams->GetFloat( "distAbove", &m_col_dist_above );
			pParams->GetFloat( "distBelow", &m_col_dist_below );
			printf("StickToGround distances set to %f (above), %f (below)\n", m_col_dist_above, m_col_dist_below);
			break;
		// @script | Ped_SetIsGrinding | used for manually setting the grind state on and off
		// @uparm 0 | 0 for off, anything else for on
		case CRCC( 0xc643e8f5, "Ped_SetIsGrinding" ):
		{
			int is_grinding = 0;
			pParams->GetInteger( NONAME, &is_grinding, Script::ASSERT );
			if ( is_grinding != 0 )
				m_flags |= PEDLOGIC_GRINDING;
			else
				m_flags &= ~PEDLOGIC_GRINDING;
			break;
		}
		// @script | Ped_GetCurrentVelocity | returns variable named velocity...
		// @flag max | get the maximum velocity, rather than the currnt velocity.
		// This is useful in case the ped is currently accelerating or decelerating 
		// to achieve maximum velocity.
		case CRCC( 0x13713760, "Ped_GetCurrentVelocity" ):
		{
			Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
			Dbg_Assert( pMotionComp );
			pScript->GetParams()->AddFloat( CRCD( 0x41272956, "velocity" ), pMotionComp->m_vel_z );
			break;
		}
		// @script | Ped_StoreMaxVelocity | stores the current max velocity, so you can
		// change it and restore it later
		case CRCC( 0xf5627596, "Ped_StoreMaxVelocity" ):
		{
			Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
			Dbg_Assert( pMotionComp );
			m_original_max_vel = pMotionComp->m_max_vel;
			break;
		}
		// @script | Ped_GetOriginalMaxVelocity | returns original_max_velocity stored when 
		// Ped_StoreMaxVelocity was called
		case CRCC( 0xe575efa9, "Ped_GetOriginalMaxVelocity" ):
		{
			pScript->GetParams()->AddFloat( "original_max_velocity", m_original_max_vel );
			break;
		}
		// @script | Ped_Bail |
		case CRCC( 0xee24a410, "Ped_Bail" ):
			Bail();
			break;
		case CRCC( 0xc076d74, "Ped_SetIsBailing" ):
		{
			int v;
			pParams->GetInteger( NONAME, &v, Script::ASSERT );
			if ( pParams->ContainsFlag( CRCD( 0x255ed86f, "grind" ) ) )
			{
				if ( v == 0 )
					m_flags &= ~PEDLOGIC_GRIND_BAILING;
				else
					m_flags |= PEDLOGIC_GRIND_BAILING;				
			}
			else
			{
				if ( v == 0 )
					m_flags &= ~PEDLOGIC_BAILING;
				else
					m_flags |= PEDLOGIC_BAILING;
			}
			break;
		}
		case CRCC( 0xda7be9e2, "Ped_InitVertTrick" ):
		{
			// get the height and gravity
			float height;
			pParams->GetFloat( CRCD( 0xab21af0, "height" ), &height, Script::ASSERT );
			float gravity;
			pParams->GetFloat( CRCD( 0xa5e2da58, "gravity" ), &gravity, Script::ASSERT );
			Dbg_MsgAssert( gravity < 0.0f, ( "Ped_InitVertTrick given positive number for gravity" ) );

			Mth::Vector pt = m_wp_to - GetObject()->m_pos;
			// adjust height to compensate for difference between ped height
			// and height of waypoint
			// height += pt[Y];
			// pt[Y] = 0.0f;

			// figure the time - the gravity is always expressed as negative 
			// acceleration, but since we're figuring the time it will take to
			// fall from the top at velocity=0, we use positive
			// Multiply by 2 to get time up and down
			// d = vi*t + (a*t^2)/2
			float time = 2 * sqrtf( 2 * -height / gravity );

			// compute the inital y velocity, which equals the final y velocity when
			// falling back down (vf^2 = vi^2 + 2ad)
			float vi = sqrtf( 2 * gravity * -height );
			Dbg_Assert( vi > 0 );

			// find the horizontal distance we need to travel
			float distance = pt.Length() / 2;
			// bump it up a bit so we don't miss landing
			distance *= Script::GetFloat( CRCD( 0x2b5bedba, "ped_skater_vert_jump_slop_distance" ), Script::ASSERT );

			// horizontal velocity
			Mth::Vector vh = ( distance / time ) * pt.Normalize();

			// vertical velocity
			Mth::Vector vy = vi * Mth::Vector(0, 1, 0);

			// total speed and heading
			// float jump_speed = ( ( vh + vy ) * time ).Length();
			float jump_speed = ( vh + vy ).Length();
			Mth::Vector heading = ( vh + vy ).Normalize();

			pScript->GetParams()->AddVector( CRCD( 0xfd4bc03e, "heading" ), heading );
			pScript->GetParams()->AddFloat( CRCD( 0x1c4c5690, "jumpSpeed" ), jump_speed );
			pScript->GetParams()->AddFloat( CRCD( 0x66ced87b, "jumpTime" ), time );
			break;
		}
		// @script | Ped_HitWaypoint | force the ped to register a hit on the
		// to waypoint, regardless of his position.  Don't call this unless
		// you know exactly why you're doing it.
		case CRCC( 0x1affffb3, "Ped_HitWaypoint" ):
			HitWaypoint();
			break;
		// @script | Ped_SetIsSkater | 
		// @uparm 1 | 0 if not skater, anything else for skater
		case CRCC( 0x5c794c57, "Ped_SetIsSkater" ):
		{
			int is_skater;
			pParams->GetInteger( NONAME, &is_skater, Script::ASSERT );
			if ( is_skater == 0 )
				m_flags &= ~PEDLOGIC_IS_SKATER;
			else
				m_flags |= PEDLOGIC_IS_SKATER;
			break;
		}
		// @script | Ped_NearVertNode | true if the next node or the to node
		// is vert
		case CRCC( 0x5bb23915, "Ped_NearVertNode" ):
			return ( ( ( m_flags & PEDLOGIC_TO_NODE_IS_VERT )
					 || ( m_flags & PEDLOGIC_NEXT_NODE_IS_VERT )
					 || ( m_flags & PEDLOGIC_FROM_NODE_IS_VERT ) ) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE );
			break;
		case CRCC( 0x56d9f55f, "Ped_PlayJumpSound" ):
		{
			//Dbg_MsgAssert( m_state == CRCD(0xa85af587,"generic_skater"), ( "%s\nPed_PlayJumpSound called on a non-skater ped %s",pScript->GetScriptInfo(),Script::FindChecksumName(GetObject()->GetID()) ) );
			Obj::CSkaterSoundComponent *pSoundComponent = GetSkaterSoundComponentFromObject( GetObject() );
			if ( pSoundComponent )
				pSoundComponent->PlayJumpSound(m_speed_fraction);
			break;
		}
		case CRCC( 0x749de914, "Ped_PlayLandSound" ):
		{
			Dbg_MsgAssert( m_state == CRCD(0xa85af587,"generic_skater"), ( "Ped_PlayLandSound called on a non-ped skater" ) );
			Obj::CSkaterSoundComponent *pSoundComponent = GetSkaterSoundComponentFromObject( GetObject() );
			if ( pSoundComponent )
				pSoundComponent->PlayLandSound(m_speed_fraction);
			break;
		}
		case CRCC( 0x58a588b5, "Ped_GetCurrentNodeNames" ):
		{
			pScript->GetParams()->AddChecksum( CRCD( 0xd14cd5bc, "node_to" ), m_node_to );
			pScript->GetParams()->AddChecksum( CRCD( 0x8141d319, "node_from" ), m_node_from );
			pScript->GetParams()->AddChecksum( CRCD( 0x3c746255, "node_next" ), m_node_next );
			break;
		}
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to C......Component::GetDebugInfo"));
	p_info->AddChecksum( "m_state", m_state );
	
	Script::CArray* pTempArray = new Script::CArray();
	pTempArray->SetSizeAndType( vNUM_WHISKERS, ESYMBOLTYPE_FLOAT );
	for ( int i = 0; i < vNUM_WHISKERS; i++ )
		pTempArray->SetFloat( i, m_whiskers[i] );
	p_info->AddArray( "m_whiskers", pTempArray );
	for ( int i = 0; i < vNUM_WHISKERS; i++ )
		pTempArray->SetFloat( i, m_bias[i] );
	p_info->AddArray( "m_bias", pTempArray );
	Script::CleanUpArray( pTempArray );
	delete pTempArray;

	p_info->AddInteger( "moving_on_path", m_flags & PEDLOGIC_MOVING_ON_PATH );

	p_info->AddInteger( "m_node_from", m_node_from );
	p_info->AddInteger( "m_node_to", m_node_to );
	p_info->AddInteger( "m_node_next", m_node_next );

	p_info->AddVector( "m_wp_from", m_wp_from );
	p_info->AddVector( "m_wp_to", m_wp_to );
	p_info->AddVector( "m_wp_next", m_wp_next );

	p_info->AddInteger( "m_turn_frames", m_turn_frames );
	p_info->AddInteger( "m_max_turn_frames", m_max_turn_frames );

	p_info->AddInteger( "m_is_grinding", m_flags & PEDLOGIC_GRINDING );

	p_info->AddFloat( "m_col_dist_above", m_col_dist_above );
	p_info->AddFloat( "m_col_dist_below", m_col_dist_below );
	
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPedLogicComponent::GenericPedUpdate()
{	
	if ( !( m_flags & PEDLOGIC_MOVING_ON_PATH ) )
		return;

	Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
	Dbg_Assert( pMotionComp );
	// Obj::CPathObjectTracker* p_path_object_tracker = pMotionComp->mp_path_object_tracker;

	bool is_avoiding = CheckForOtherPeds();	
	bool is_turning = UpdateTargetBias();

	// update bias based on path following
	if ( !is_turning && !is_avoiding )
	{
		UpdatePathBias();
	}
	
	// Gfx::AddDebugLine( m_wp_from, m_wp_to, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
	// Gfx::AddDebugLine( GetObject()->m_pos, m_wp_to, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 1 );

	// move and decay
	pMotionComp->DoPathPhysics();
	UpdatePosition();
	DecayWhiskerBiases();
	pMotionComp->StickToGround();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::GenericSkaterUpdate()
{
	Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
	Dbg_Assert( pMotionComp );
	pMotionComp->DoPathPhysics();
	float distance = m_time * pMotionComp->m_vel_z;
	bool is_jumping = pMotionComp->m_movingobj_status & MOVINGOBJ_STATUS_JUMPING ? true : false;

	// Gfx::AddDebugLine( m_wp_from, m_wp_to, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
	// Gfx::AddDebugLine( GetObject()->m_pos, m_wp_to, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 1 );
	// Gfx::AddDebugLine( m_pos_history[vPOS_HISTORY_SIZE - 2] + Mth::Vector( 0, 5, 1 ), m_pos_history[vPOS_HISTORY_SIZE - 1] + Mth::Vector( 0, 5, 1 ), MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ) );
	// Gfx::AddDebugArrow( GetObject()->GetDisplayMatrix()[Z] + GetObject()->m_pos, GetObject()->GetDisplayMatrix()[Z] * 24 + GetObject()->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 1 );
	
	// check for skater if we're grinding
	if ( m_flags & PEDLOGIC_GRINDING )
	{
		Mdl::Skate* skate_mod = Mdl::Skate::Instance();
		Dbg_Assert( skate_mod );
		Obj::CSkater* pSkater = skate_mod->GetLocalSkater();
		if ( pSkater )
		{
			float d = Mth::DistanceSqr( GetObject()->m_pos, pSkater->GetPos() );
			// printf("skater dist square = %f\n", d );
			if ( d < Script::GetFloat( CRCD( 0xbc2e678a, "ped_skater_min_square_distance_to_skater" ), Script::ASSERT ) )
			{
				GrindBail();
				return;
			}
		}
	}
	
	// see if we've hit the waypoint
	if ( ( m_flags & PEDLOGIC_MOVING_ON_PATH ) || is_jumping )
	{
		Mth::Vector pt;
		if ( ( m_flags & PEDLOGIC_TO_NODE_IS_VERT ) || is_jumping )
		{
			pt = ( GetObject()->m_pos - m_wp_to );
		}
		else
		{
			Mth::Vector wp_to_no_y = m_wp_to;
			wp_to_no_y[Y] = 0.0f;
			Mth::Vector ped_pos_no_y = GetObject()->m_pos;
			ped_pos_no_y[Y] = 0.0f;
			pt = ped_pos_no_y - wp_to_no_y;
		}
		float distance_to_target_square = pt.LengthSqr();
		int min_dist_square = Script::GetInteger( CRCD( 0xfb5d106f, "ped_skater_min_square_distance_to_waypoint" ), Script::ASSERT );
	
		if ( !( m_flags & PEDLOGIC_JUMPING_TO_NODE ) )
		{
			if ( distance_to_target_square < Mth::Sqr( distance ) )
				HitWaypoint();
			else if ( distance_to_target_square < min_dist_square )
				HitWaypoint();
		}

		if ( m_flags & PEDLOGIC_JUMP_AT_TO_NODE )
		{
			int dist_to_crouch = Script::GetInteger( CRCD( 0x52ec432f, "ped_skater_min_square_distance_to_crouch_for_jump" ), Script::ASSERT );
			if ( distance_to_target_square < dist_to_crouch )
			{
				// clear flag!
				m_flags &= ~PEDLOGIC_JUMP_AT_TO_NODE;

				Script::CScript* pScript = Script::SpawnScript( CRCD( 0x9c7ab5f5, "ped_skater_crouch_for_jump" ) );
				pScript->mpObject = GetObject();
			}
		}
	}

	// move
	if ( ( m_flags & PEDLOGIC_MOVING_ON_PATH ) && distance )
	{
		Mth::Vector original_pos = GetObject()->m_pos;
		bool already_adjusted_heading = false;
		if ( ShouldUseBiases() )
		{
			// use biases to update position
			bool is_turning = UpdateTargetBias();
			if ( !is_turning )
			{
				UpdatePathBias();
			}
			UpdatePosition();
			
			// refresh the display matrix if we're jumping, as 
			// AdjustHeading and AdjustNormal won't be called.
			if ( is_jumping )
			{
				RefreshDisplayMatrix();
			}
		}
		else
		{
			// figure the new position
			Mth::Vector new_pos = original_pos + ( distance * ( m_wp_to - GetObject()->m_pos ).Normalize() );
			UpdatePathBias();
			UpdatePosition();			
			GetObject()->m_pos = new_pos;
			AdjustHeading( original_pos );
			already_adjusted_heading = true;
		}

		// stick to ground if we're not grinding or jumping
		if ( !( m_flags & PEDLOGIC_GRINDING )
			 && !( m_flags & PEDLOGIC_GRIND_BAILING )
			 && !is_jumping
			)
		{
			StickToGround();
			// update z vector
			if ( !already_adjusted_heading )
				AdjustHeading( original_pos );
			AdjustNormal();
		}
	}
	else // if ( !( pMotionComp->m_movingobj_status & MOVINGOBJ_STATUS_JUMPING ) )
	{
		RotatePosHistory();
		if ( m_flags & PEDLOGIC_TO_NODE_IS_VERT )
		{
			// AdjustHeading();
		}
		RefreshDisplayMatrix();
		if ( m_flags & PEDLOGIC_DOING_VERT_ROTATION || m_flags & PEDLOGIC_DOING_SPINE )
		{
			DoRotations();
		}
	}

	// update states for looping sounds
	if ( is_jumping )
	{
		UpdateSkaterSoundStates( AIR );
	}
	else if ( m_flags & PEDLOGIC_GRINDING )
	{
		UpdateSkaterSoundStates( RAIL );
	}
	else if ( m_flags & PEDLOGIC_MOVING_ON_PATH )
	{
		UpdateSkaterSoundStates( GROUND );
	}
	else if ( m_flags & PEDLOGIC_DOING_LIP_TRICK )
	{
		UpdateSkaterSoundStates( LIP );
	}
 	
	if ( m_flags & PEDLOGIC_MOVING_ON_PATH )
		m_speed_fraction = ( pMotionComp->m_vel_z / pMotionComp->m_max_vel );
	else
		m_speed_fraction = 0.0f;
	
	Obj::CSkaterLoopingSoundComponent* pLoopingSoundComp = GetSkaterLoopingSoundComponentFromObject( GetObject() );
	if ( pLoopingSoundComp )
	{
		// reset bailing flag
		pLoopingSoundComp->SetIsBailing( m_flags & PEDLOGIC_GRIND_BAILING || m_flags & PEDLOGIC_BAILING );
		pLoopingSoundComp->SetSpeedFraction( m_speed_fraction );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPedLogicComponent::CheckForOtherPeds()
{
	bool is_avoiding = false;
	if ( mp_path_object_tracker )
	{		
		const CSmtPtr* pp_object_list = mp_path_object_tracker->GetObjectList();
		Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix();
		float range = Script::GetFloat( CRCD( 0x7a78f471, "ped_avoid_ped_range" ) );
		float avoid_ped_bias = Script::GetFloat( CRCD( 0xd1e6177b, "ped_avoid_ped_bias" ) );
		Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
		Dbg_Assert( pMotionComp );

		for ( int i = 0; i < CPathObjectTracker::MAX_OBJECTS_PER_PATH; ++i )
		{
			if ( pp_object_list[i] )
			{
				CCompositeObject* p_ob=pp_object_list[i].Convert();
	
				if ( p_ob != GetObject() )
				{
					// ignore peds that are too far above or below
					float max_y_dist = Script::GetFloat( CRCD(0x21b13ffc,"ped_max_y_distance_to_ignore"), Script::ASSERT );
					if ( Mth::Abs( GetObject()->GetPos()[Y] - p_ob->GetPos()[Y] ) > max_y_dist )
					{
						continue;
					}
					
					// ignore peds that are facing the opposite direction and walking away
					float z_dot = Mth::DotProduct( display_matrix[Z], p_ob->GetDisplayMatrix()[Z] );
					if ( z_dot <= 0.0f && Mth::DotProduct( display_matrix[Z], p_ob->GetPos() - GetObject()->GetPos() ) <= 0.0f )
					{
						// printf("walking away from each other\n");
						continue;
					}
					
					// ignore peds that are facing the same direction and going faster than us
					Obj::CMotionComponent* pObMotionComp = GetMotionComponentFromObject( p_ob );
					Dbg_Assert( pObMotionComp );
					if ( z_dot > 0.0f && pObMotionComp->m_max_vel >= pMotionComp->m_max_vel )
					{
						// printf("he's going faster than me\n");
						continue;
					}

					float d = Mth::Distance( GetObject()->GetPos(), p_ob->GetPos() );					
					if ( d <= range )
					{						
						// adjust whisker
						// find the heading
						float theta = acosf( z_dot / ( display_matrix[Z].Length() * p_ob->GetDisplayMatrix()[Z].Length() ) );
						
						// printf("theta = %f\n", theta);
						// reduce to less than 2pi.
						if ( theta > Mth::PI * 2 )
							theta -= Mth::PI * 2 * (int)( theta / ( Mth::PI * 2 ) );
						// if they're coming straight at each other, we'll have to throw in some
						// sideways bias - we don't want them to stop moving or turn around
						// As they start to pass each other, the offset will naturally start
						// to create its own bias
						if ( fabs( theta - Mth::PI ) < Script::GetFloat( CRCD( 0xd2e22cec, "ped_head_on_range" ), Script::ASSERT ) )
						{
							// printf("he's coming right for me!\n");
							// left by default
							theta = Mth::PI / 2;
							// see if we should push them right
							// if ( theta > Mth::PI )
							if ( ( Mth::CrossProduct( display_matrix[Z], p_ob->GetDisplayMatrix()[Z] ) )[Y] < 0 )
								theta *= -1;
						}
						is_avoiding = true;
						AddWhiskerBias( theta, avoid_ped_bias, d, range );
					}
				}
			}
		}
	}
	return is_avoiding;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::UpdatePathBias()
{
	Mth::Vector wp_from_no_y = m_wp_from;
	wp_from_no_y[Y] = 0.0f;
	Mth::Vector wp_to_no_y = m_wp_to;
	wp_to_no_y[Y] = 0.0f;
	Mth::Vector ped_pos_no_y = GetObject()->m_pos;
	ped_pos_no_y[Y] = 0.0f;

	float max_dist_to_path = Script::GetFloat( CRCD( 0xa876aa2c, "ped_max_distance_to_path" ), Script::ASSERT );
	Mth::Vector ft = wp_to_no_y - wp_from_no_y;
	Mth::Vector fp = ped_pos_no_y - wp_from_no_y;

	// need to store this vector so we can check the sign of y...
	// if it's negative, we're on one side of the path, positive for the other
	Mth::Vector ft_cross_fp = Mth::CrossProduct( ft, fp );

	// get the direction we want to push the ped
	Mth::Vector closest_pos_on_path = ( ( Mth::DotProduct( fp, ft ) / ft.LengthSqr() ) * ft ) + m_wp_from;
	closest_pos_on_path[Y] = 0.0f;
	Mth::Vector pos_to_path = closest_pos_on_path - ped_pos_no_y;

	// Gfx::AddDebugArrow( GetObject()->m_pos, closest_pos_on_path, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 1 );

	// Mth::Matrix ob_matrix = GetObject()->m_matrix;
	Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix();
	float angle_to_path = Mth::GetAngle( display_matrix[Z], pos_to_path );
	if ( angle_to_path > 360.0f )
		angle_to_path -= 360.0f * (int)( angle_to_path / 360.0f );
	if ( Mth::CrossProduct( display_matrix[Z], pos_to_path )[Y] > 0 )
		angle_to_path = 360.0f - angle_to_path;

	// float path_length = Mth::Distance( m_wp_from, m_wp_to );
	float path_length = Mth::Distance( wp_from_no_y, wp_to_no_y );
	float current_distance = ft_cross_fp.Length() / path_length;

	// the bias should get stronger as we get farther away, approaching 1 as we 
	// approach the max distance from the path
	float path_bias;
	if ( current_distance > max_dist_to_path )
		path_bias = 1;
	else
		path_bias = 1 - ( ( max_dist_to_path - current_distance ) / max_dist_to_path );

	// reset the bias to get back on the path
	if ( current_distance > Script::GetFloat( CRCD( 0x81085734, "ped_min_distance_to_path" ) ) )
		AdjustBias( Mth::DegToRad( angle_to_path ), path_bias );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPedLogicComponent::UpdateTargetBias()
{
	bool is_turning = false;
	Mth::Vector wp_to_no_y = m_wp_to;
	wp_to_no_y[Y] = 0.0f;
	Mth::Vector ped_pos_no_y = GetObject()->m_pos;
	ped_pos_no_y[Y] = 0.0f;
	Mth::Vector pt = ped_pos_no_y - wp_to_no_y;
	float distance_to_target_square = pt.LengthSqr();
	
	float target_bias = Script::GetFloat( CRCD(0x57342672,"ped_target_node_bias"), Script::ASSERT );
	ResetBias();

	if ( m_node_next == m_node_from )
	{
		float min_square_distance_to_dead_end = Script::GetFloat( CRCD( 0x65b46f32, "ped_walker_min_square_distance_to_dead_end" ), Script::ASSERT );
		if ( distance_to_target_square <= min_square_distance_to_dead_end )
		{
			HitWaypoint();
		}
	}
	else
	{
		int fade_bias_max_distance_square;
		if ( m_flags & PEDLOGIC_IS_SKATER )
			fade_bias_max_distance_square = Script::GetInteger( CRCD( 0x354beda1, "ped_skater_fade_target_bias_max_distance_square" ), Script::ASSERT );
		else
			fade_bias_max_distance_square = Script::GetInteger( CRCD( 0x5b21ab0e, "ped_fade_target_bias_max_distance_square" ), Script::ASSERT );

		if ( m_max_turn_frames && m_turn_frames >= m_max_turn_frames )
		{
			HitWaypoint();
		}
		else if ( m_node_next != -1 && distance_to_target_square < fade_bias_max_distance_square )
		{
			// do we need to reset m_max_turn_frames?
			if ( !m_max_turn_frames )
			{
				// figure how long it will take to get there if we don't turn
				Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
				Dbg_Assert( pMotionComp );
				
				Mth::Vector wp_from_no_y = m_wp_from;
				wp_from_no_y[Y] = 0.0f;
				Mth::Vector wp_to_no_y = m_wp_to;
				wp_to_no_y[Y] = 0.0f;
				Mth::Vector wp_next_no_y = m_wp_next;
				wp_next_no_y[Y] = 0.0f;

				Mth::Vector ft = wp_to_no_y - wp_from_no_y;
				Mth::Vector tn = wp_next_no_y - wp_to_no_y;

				float angle_between_waypoints = Mth::DegToRad( Mth::GetAngle( ft, tn ) );
				float distance_to_waypoint_switch = sqrtf( ( 2 * distance_to_target_square ) * ( 1 + cosf( angle_between_waypoints ) ) );
				int frames = (int)( ( sqrtf( distance_to_waypoint_switch ) / pMotionComp->m_max_vel ) * 60.0f );
				// printf("frames = %i\n", frames);
				m_max_turn_frames = frames;
				if ( m_max_turn_frames <= 0 )
				{
					m_max_turn_frames = 1;
				}
				
				// int frames = (int)( ( sqrtf( distance_to_target_square ) / pMotionComp->m_max_vel ) * 60.0f );
				// m_max_turn_frames = frames / 2;
			}
	
			// add bias for new waypoint and adjust current target bias
			is_turning = true;

			// Guard against potential division by zero here.
			float target_bias_turn_percent;
			if( m_max_turn_frames == 0 )
				target_bias_turn_percent = 0.0f;
			else
				target_bias_turn_percent = 0.5f * ( 1 - (float)( m_max_turn_frames - m_turn_frames ) / (float)m_max_turn_frames );

			m_turn_frames++;
			AddTargetBias( m_wp_next, target_bias * target_bias_turn_percent );
			target_bias -= target_bias * target_bias_turn_percent;
		}
	}
	AddTargetBias( m_wp_to, target_bias );
	return is_turning;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::UpdatePosition()
{
	Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
	Dbg_Assert( pMotionComp );

	float x_bias = GetTotalXBias();
	float z_bias = GetTotalZBias();
	// printf("x_bias = %f, z_bias = %f\n", x_bias, z_bias );

	Mth::Vector last_pos = GetObject()->m_pos;
	
	RotatePosHistory();
	
	float vel = pMotionComp->m_vel_z;
	float distance_to_travel = vel * m_time;
	float x_distance = distance_to_travel * x_bias;
	float z_distance = distance_to_travel * z_bias;
/*	float total_bias = x_bias + z_bias;
	if ( !total_bias )
		return;
	float vel = pMotionComp->m_vel_z;

	float distance_to_travel = vel * m_time;
	float x_distance = distance_to_travel * ( ( total_bias - x_bias ) / total_bias );
	if ( x_bias < 0 )
		x_distance *= -1;
	float z_distance = distance_to_travel * ( ( total_bias - z_bias ) / total_bias );
	if ( z_bias < 0 )
		z_distance *= -1;
*/   
	// printf("x_dist = %f, z_dist = %f\n", x_distance, z_distance);
	// Mth::Matrix mat0 = GetObject()->m_matrix;
	Mth::Matrix mat0 = GetObject()->GetDisplayMatrix();
	Mth::Vector z_vect = mat0[Z] * z_distance;
	Mth::Vector x_vect = mat0[X] * x_distance;
	
	// GetObject()->GetDisplayMatrix()[Z] = new_heading;
	// GetObject()->GetDisplayMatrix().OrthoNormalizeAbout( Z );
	GetObject()->m_pos += x_vect;
	GetObject()->m_pos += z_vect;
	
	// skaters do their own headings
	if ( last_pos != GetObject()->m_pos && !( m_flags & PEDLOGIC_IS_SKATER ) )
		AdjustHeading( last_pos );

/*
	float angle_to_new_heading = atan2( x_distance, z_distance );
	if ( fabs( angle_to_new_heading ) > Script::GetFloat( "ped_max_angle_to_heading", Script::ASSERT ) )
	{
		GetObject()->GetDisplayMatrix().Rotate( mat0[Y], angle_to_new_heading );
		// mat0.Rotate( mat0[Y], angle_to_new_heading );
		// GetObject()->m_matrix.Rotate( mat0[Y], angle_to_new_heading );
		// printf( "angle_to_new_heading = %f\n", Mth::RadToDeg( angle_to_new_heading ) );
	}
*/
	// GetObject()->SetDisplayMatrix( mat0 );
	// GetObject()->m_matrix = mat0;
	// GetObject()->m_pos += adjustment;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::RotatePosHistory()
{
	for ( int i = 0; i < vPOS_HISTORY_SIZE - 1; i++ )
	{
		m_pos_history[i] = m_pos_history[i + 1];
	}
	m_pos_history[vPOS_HISTORY_SIZE - 1] = GetObject()->m_pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::AddTargetBias( Mth::Vector target, float target_bias )
{
	Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix();
	
	Mth::Vector ped_pos_no_y = GetObject()->m_pos;
	ped_pos_no_y[Y] = 0.0f;
	target[Y] = 0.0f;

	// Mth::Vector pt = GetObject()->m_pos - target;
	Mth::Vector pt = ped_pos_no_y - target;
	float angle_to_target = Mth::GetAngle( display_matrix[Z], pt );
	angle_to_target += 180.0f;
	if ( angle_to_target > 360.0f )
		angle_to_target -= 360.0f * (int)( angle_to_target / 360.0f );

	if ( ( Mth::CrossProduct( display_matrix[Z], pt ) )[Y] > 0 )
		angle_to_target = 360.0f - angle_to_target;

	// printf("angle_to_target = %f\n", angle_to_target );
	
	// add bias to get to the target - the target_bias is constant
	AdjustBias( Mth::DegToRad( angle_to_target ), target_bias );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::AddWhiskerBias( float angle, float amount, float distance, float range )
{
	// for now, linearly decrease the bias amount within the range
	amount *= ( range - distance ) / range;

	float segment_arc = Mth::DegToRad( 360 / vNUM_WHISKERS );
	for ( int i = 0; i < vNUM_WHISKERS; i++ )
	{
		float angle_between = ( segment_arc * i ) - angle;
		// make sure this is in the right quandrant
		if ( fabs ( angle_between ) >= Mth::PI / 2 && fabs( angle_between ) <= 3 * Mth::PI / 2 )
			continue;

		// float adjusted_amount = fabs( amount * sinf( angle_between ) );
		float adjusted_amount = amount * cosf( angle_between );
		// printf("adding %f to m_whiskers[%i]\n", adjusted_amount, i );
		// add this bias to the whisker
		m_whiskers[i] += adjusted_amount;

		// cap absolute value to 1
		if ( m_whiskers[i] > 1 )
			m_whiskers[i] = 1;
		if ( m_whiskers[i] < -1 )
			m_whiskers[i] = -1;
	}
/*
	float segment_arc = Mth::DegToRad( 360 / vNUM_WHISKERS );
	for ( int i = 0; i < vNUM_WHISKERS; i++ )
	{
		float angle_between = fabs( ( segment_arc * i ) - angle );
		
		// make sure this is less than 180 from the angle we're looking at
		if ( angle_between >= Mth::PI )
			continue;

		// don't bother with maxed bias
		if ( m_whiskers[i] == 1 )
			continue;
		
		// add this bias to the whisker
		m_whiskers[i] += fabs( amount * cos( angle_between ) );

		// cap to 1
		if ( m_whiskers[i] > 1 )
			m_whiskers[i] = 1;
	}
*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::AdjustBias( float angle, float amount )
{
	// printf("AdjustBias( %f, %f )\n", angle, amount );
	// translate the bias to the whisker(s)
	// makes sure the angle isn't greater than 360
	// printf("angle = %f\n", Mth::RadToDeg( angle ) );
	float segment_arc = Mth::DegToRad( 360 / vNUM_WHISKERS );
	for ( int i = 0; i < vNUM_WHISKERS; i++ )
	{		
		float angle_between = ( segment_arc * i ) - angle;
		
		// make sure this is in the right quandrant
		if ( fabs ( angle_between ) >= Mth::PI / 2 && fabs( angle_between ) <= 3 * Mth::PI / 2 )
			continue;

		// float adjusted_amount = fabs( amount * sinf( angle_between ) );
		float adjusted_amount = amount * cosf( angle_between );
		// printf("adding %f to m_bias[%i]\n", adjusted_amount, i );
		// add this bias to the whisker
		m_bias[i] += adjusted_amount;

		// cap absolute value to 1
		if ( m_bias[i] > 1 )
			m_bias[i] = 1;
		if ( m_bias[i] < -1 )
			m_bias[i] = -1;
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DecayWhiskerBiases()
{
	// multiply each whisker by the decay factor
	float decay_factor = Script::GetFloat( CRCD( 0x98cd0c8c, "ped_whisker_decay_factor" ), Script::ASSERT );
	float min_bias = Script::GetFloat( CRCD( 0x4aba9798, "ped_min_bias" ), Script::ASSERT );
	for ( int i = 0; i < vNUM_WHISKERS; i++ )
	{
		m_whiskers[i] *= decay_factor;
		if ( m_whiskers[i] < min_bias )
			m_whiskers[i] = 0;
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CPedLogicComponent::GetTotalXBias()
{
	float total = 0.0f;
	float segment_arc = Mth::DegToRad( 360 / vNUM_WHISKERS );
	for ( int i = 0; i < vNUM_WHISKERS; i++ )
	{
		// float angle = Mth::PI - ( segment_arc * i );
		float angle = segment_arc * i;
		// phase shift 180
		// angle += Mth::PI;
		float whisker_total = m_whiskers[i] + m_bias[i];
		// float whisker_total = m_whiskers[i];
		// float whisker_total = m_bias[i];
    	if ( whisker_total > 1 )
			whisker_total = 1;
		
		total += -sinf( angle ) * whisker_total;
		
		if ( total >= 1 )
			return 1;
		if ( total <= -1 )
			return -1;
	}
	if ( fabs( total ) < Script::GetFloat( CRCD( 0x4aba9798, "ped_min_bias" ), Script::ASSERT ) )
		total = 0;
	// remember that we're 180 degrees out of phase
	return total;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CPedLogicComponent::GetTotalZBias()
{
	float total = 0.0f;
	float segment_arc = Mth::DegToRad( 360 / vNUM_WHISKERS );
	for ( int i = 0; i < vNUM_WHISKERS; i++ )
	{
		float angle = segment_arc * i;
		// phase shift 180
		// angle += Mth::PI;
		float whisker_total = m_whiskers[i] + m_bias[i];
		// float whisker_total = m_whiskers[i];
		// float whisker_total = m_bias[i];
		if ( whisker_total > 1 )
			whisker_total = 1;
		if ( whisker_total < -1 )
			whisker_total = -1;
		// printf("whisker_total[%i] = %f, angle = %f\n", i, whisker_total, angle);
		// 90 degree phase shift
		// total += cosf( angle ) * whisker_total;
		total += cosf( angle ) * whisker_total;
		if ( total >= 1)
			return 1;
		if ( total <= -1 )
			return -1;
	}
	if ( fabs( total ) < Script::GetFloat( CRCD( 0x4aba9798, "ped_min_bias" ), Script::ASSERT ) )
		total = 0;
	// return total;
	return total;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::ResetBias()
{
	for ( int i = 0; i < vNUM_WHISKERS; i++ )
		m_bias[i] = 0;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::HitWaypoint()
{
	// printf( "CPedLogicComponent::HitWaypoint\n" );
	int hit_node = m_node_to;
	int old_from_node = m_node_from;
	
	m_flags &= ~PEDLOGIC_FROM_NODE_IS_VERT;
	m_flags &= ~PEDLOGIC_NEXT_NODE_IS_VERT;
	if ( m_flags & PEDLOGIC_TO_NODE_IS_VERT )
		m_flags |= PEDLOGIC_FROM_NODE_IS_VERT;

	m_flags &= ~PEDLOGIC_JUMPING_TO_NODE;

	m_node_from = m_node_to;
	m_node_to = m_node_next;
	m_wp_from = m_wp_to;
	m_wp_to = m_wp_next;
	
	// handle dead ends for ped walkers
	if ( !( m_flags & PEDLOGIC_IS_SKATER ) && old_from_node == m_node_to )
	{
		Script::CScript* pDeadEndScript = Script::SpawnScript( CRCD( 0xa80f965e, "ped_walker_hit_dead_end" ) );
		pDeadEndScript->mpObject = GetObject();
	}
	
	// update m_to_node_is_vert for distance checks
	m_flags &= ~PEDLOGIC_TO_NODE_IS_VERT;
	// m_to_node_is_vert = false;
	Script::CStruct* pToNodeData = SkateScript::GetNode( m_node_to );

	m_flags &= ~PEDLOGIC_TO_NODE_IS_LIP;

	uint32 to_node_type;
	if ( pToNodeData->GetChecksum( CRCD( 0x17ea37fb, "PedType" ), &to_node_type, Script::NO_ASSERT )
		 && to_node_type == CRCD( 0x54166acd, "skate" ) )
	{
		uint32 to_node_action;
		pToNodeData->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &to_node_action, Script::ASSERT );
		if ( WaypointIsVert( m_node_to ) )
		{
			m_flags |= PEDLOGIC_TO_NODE_IS_VERT;
			// m_to_node_is_vert = true;
		}
		
		// check for a jump next - we'll need to crouch if we're not
		// already doing some action
		if ( to_node_action == CRCD( 0x7d9d0008, "vert_grab" )
			 || to_node_action == CRCD( 0x584cf9e9, "jump" ) )
		{
			// make sure we're not coming from an action
			Script::CStruct* pFromNodeData = SkateScript::GetNode( m_node_from );
			uint32 from_node_action;
			if ( pFromNodeData->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &from_node_action, Script::ASSERT ) )
			{
				if ( from_node_action != CRCD( 0x255ed86f, "grind" )
					 && !( m_flags & PEDLOGIC_GRINDING )
					 && !( m_flags & PEDLOGIC_FROM_NODE_IS_VERT ) )
				{
					m_flags |= PEDLOGIC_JUMP_AT_TO_NODE;
				}
			}
		}

		if ( to_node_action == CRCD( 0x554dbd64, "vert_lip" ) )
		{
			m_flags |= PEDLOGIC_TO_NODE_IS_LIP;
		}
	}
	
	SelectNextWaypoint();
	if ( WaypointIsVert( m_node_next ) )
		m_flags |= PEDLOGIC_NEXT_NODE_IS_VERT;

	// we don't want to do any actions if we just came from a vert_grab node
	Script::CStruct* pOldFromNodeData = SkateScript::GetNode( old_from_node );
	uint32 old_skate_action;
	Script::CStruct* pNodeData = SkateScript::GetNode( hit_node );
	Dbg_Assert( pNodeData );
	DoGenericNodeActions( pNodeData );

	if ( !( pOldFromNodeData->GetChecksum( CRCD( 0x963dc198, "skateAction" ), &old_skate_action )
		 && old_skate_action == CRCD( 0x7d9d0008, "Vert_Grab" ) ) )
	{
		uint32 waypoint_type;
		if ( pNodeData->GetChecksum( CRCD( 0x17ea37fb, "PedType" ), &waypoint_type, Script::NO_ASSERT ) )
		{
			switch ( waypoint_type )
			{
			case CRCC( 0x726e85aa, "walk" ):
				DoWalkActions( pNodeData );
				break;
			case CRCC( 0x54166acd, "skate" ):
				DoSkateActions( pNodeData );
				break;
			default:
				Dbg_MsgAssert( 0, ( "Waypoint has bad PedType %s", Script::FindChecksumName( waypoint_type ) ) );
			}
		}
	}

	m_turn_frames = m_max_turn_frames = 0;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPedLogicComponent::WaypointIsVert( int waypoint )
{
	Script::CStruct* pNodeData = SkateScript::GetNode( waypoint );
	uint32 node_action;
	if ( pNodeData->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &node_action, Script::NO_ASSERT ) )
	{
		if ( node_action == CRCD( 0x7d9d0008, "vert_grab" )
			 || node_action == CRCD( 0x554dbd64, "vert_lip" )
			 || node_action == CRCD( 0xe6dfaec7, "vert_grind" )
			 || node_action == CRCD( 0xda0723da, "vert_land" )
			 || node_action == CRCD( 0xe8f91257, "vert_flip" )
			 || node_action == CRCD( 0xd5b4f014, "vert_jump" ) )
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoWalkActions( Script::CStruct* pNodeData )
{

}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoGenericNodeActions( Script::CStruct* pNodeData )
{
	Dbg_Assert( pNodeData );
	// check for special things we should do at this node
	if ( pNodeData->ContainsFlag( CRCD( 0x74e4bba5, "AdjustSpeed" ) ) )
	{
		if ( ShouldExecuteAction( pNodeData, CRCD( 0x922c5b59, "AdjustSpeedWeight" ) ) )
		{
			float percent;
			pNodeData->GetFloat( CRCD( 0xa04d16ec, "AdjustSpeedPercent" ), &percent, Script::ASSERT );
			AdjustSpeed( percent );
		}
	}
	if ( pNodeData->ContainsFlag( CRCD( 0x55d98ed7, "RunScript" ) ) )
	{
		if ( ShouldExecuteAction( pNodeData, CRCD( 0x86c9ccce, "RunScriptWeight" ) ) )
		{
			uint32 script_name;
			pNodeData->GetChecksum( CRCD( 0x64b4cd9d, "RunScriptName" ), &script_name, Script::ASSERT );
			Script::CScript* pScript = Script::SpawnScript( script_name );
			pScript->mpObject = GetObject();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoSkateActions( Script::CStruct* pNodeData )
{
	// we don't want to do any actions if we're bailing
	if ( ( m_flags & PEDLOGIC_BAILING ) || ( m_flags & PEDLOGIC_GRIND_BAILING ) )
		return;
	
	Dbg_Assert( pNodeData );
	uint32 skate_action;
	if ( pNodeData->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &skate_action, Script::NO_ASSERT ) )
	{
		switch ( skate_action )
		{
			case CRCC( 0xec1cd520, "continue" ):
				break;
			case CRCC( 0x255ed86f, "grind" ):
			case CRCC( 0xe6dfaec7, "vert_grind" ):
				DoGrind( pNodeData );
				break;
			case CRCC( 0xc870bce5, "grind_off" ):
				DoGrindOff( pNodeData );
				break;
			case CRCC(0xe41199af,"grind_bail"):
				// ignore these nodes...we must have fallen here from a grind
				break;
			case CRCC(0xba1d1e94,"flip_trick"):
				DoFlipTrick( pNodeData );
				break;
			case CRCC( 0xe8f91257, "vert_flip" ):
				DoFlipTrick( pNodeData, true );
				break;
			case CRCC( 0x12797a1b, "grab_trick" ):
				DoGrab( pNodeData );
				break;
			case CRCC( 0x7d9d0008, "vert_grab" ):
				DoGrab( pNodeData, true );
				break;
			case CRCC( 0x554dbd64, "vert_lip" ):
				DoLipTrick( pNodeData );
				break;
			case CRCC( 0xda0723da, "vert_land" ):
				break;
			case CRCC( 0x584cf9e9, "jump" ):
				DoJump( pNodeData );
				break;
			case CRCC( 0xd5b4f014, "vert_jump" ):
				DoJump( pNodeData, true );
				break;
			case CRCC( 0x6d3144bf, "Roll_off" ):
				DoRollOff( pNodeData );
				break;
			case CRCC( 0xef24413b, "manual" ):
				DoManual( pNodeData );
				break;
			case CRCC( 0x9403a0ed, "manual_down" ):
				DoManualDown( pNodeData );
				break;
			case CRCC( 0x46a9e949, "stop" ):
				Stop( pNodeData );
				break;
			default:
				Dbg_MsgAssert( 0, ( "DoSkateActions found unknown action %s\n", Script::FindChecksumName( skate_action ) ) );
				break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::SelectNextWaypoint()
{
	// are we on a grind bail path?
	bool on_grind_bail_path = false;
	Script::CStruct* pNode = SkateScript::GetNode( m_node_from );
	uint32 skate_action;
	if ( pNode->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &skate_action )
		 && skate_action == CRCD( 0xe41199af, "Grind_Bail" ) )
	{
		on_grind_bail_path = true;
	}
	
	int waypoint;
	int numLinks;
	numLinks = SkateScript::GetNumLinks( m_node_to );

	if ( numLinks == 0 )
	{
		// turn around
		waypoint = m_node_from;
	}
	else if ( numLinks == 1 ) // shortcut if there's only one link
	{
		waypoint = SkateScript::GetLink( m_node_to, 0 );
		
		// special case for jumping straight up in a pipe...land where you started
		if ( waypoint == m_node_from )
		{
			Script::CStruct* pToNodeData = SkateScript::GetNode( m_node_to );
			uint32 skate_action;
			if ( pToNodeData->GetChecksum( CRCD( 0x963dc198, "skateAction" ), &skate_action )
				 && skate_action == CRCD( 0x7d9d0008, "Vert_Grab" ) )
			{
				waypoint = m_node_to;
			}
		}

	}
	else
	{
		// sort the links by priority
		Mth::Vector to_from_vector = m_wp_to - m_wp_from;
		int valid_high_priority_choices[20];
		int valid_low_priority_choices[20];
		Dbg_MsgAssert( numLinks < 20, ( "Too many links (max is 20) from node %i\n", m_node_to ) );
		for ( int i = 0; i < numLinks; i++ )
		{
			valid_high_priority_choices[i] = i;
			valid_low_priority_choices[i] = i;
		}
	
		int num_valid_low_choices = 0;
		int num_valid_high_choices = 0;
		int min_inner_angle = Script::GetInteger( CRCD( 0xfe65d822, "ped_min_inner_path_angle" ), Script::ASSERT );
		
		// store the max angle, so we can return the best choice if we don't find any good ones
		float max_angle = 0.0f;
		int max_angle_index = 0;
		for ( int i = 0; i < numLinks; i++ )
		{
			int test_node = SkateScript::GetLink( m_node_to, i );
	
			if ( test_node != m_node_from )
			{
				Script::CStruct* pNodeData = SkateScript::GetNode( test_node );

				// ignore nodes that aren't PedAI waypoints
				uint32 class_type = 0;
				uint32 waypoint_type = 0;
				pNodeData->GetChecksum( CRCD( 0x12b4e660, "class" ), &class_type );
				pNodeData->GetChecksum( CRCD( 0x7321a8d6, "Type" ), &waypoint_type );
				if ( class_type != CRCD( 0x4c23a77e, "Waypoint" ) || waypoint_type != CRCD( 0xcba10ffa, "PedAI" ) )
					continue;
				
				// ignore Grind_Bail nodes unless we're already on a grind_bail path
				if ( !on_grind_bail_path )
				{
					uint32 skate_action;
					if ( pNodeData->GetChecksum( CRCD( 0x963dc198, "SkateAction" ), &skate_action )
						 && skate_action == CRCD( 0xe41199af, "Grind_Bail" ) )
					{
						continue;
					}
				}
				
				Mth::Vector test_node_position;
				SkateScript::GetPosition( test_node, &test_node_position );
				Mth::Vector to_test_vector = m_wp_to - test_node_position;
				float angle = Mth::GetAngle( to_from_vector, to_test_vector );
				if ( angle > 360.0f )
					angle -= 360.0f * (int)( angle / 360.0f );
	
				// update max angle
				if ( angle > max_angle )
				{
					max_angle = angle;
					max_angle_index = i;
				}
		
				if ( angle >= min_inner_angle )
				{
					// sort nodes by priority
					uint32 priority;
					pNodeData->GetChecksum( CRCD( 0x9d5923d8, "priority" ), &priority, Script::ASSERT );
					switch ( priority )
					{
						case CRCC( 0xde7a971b, "normal" ):
							valid_high_priority_choices[num_valid_high_choices] = i;
							num_valid_high_choices++;
							break;
						case CRCC(0x6d77875e,"low"):
							valid_low_priority_choices[num_valid_low_choices] = i;
							num_valid_low_choices++;
							break;
						default:
							Dbg_MsgAssert( 0, ( "Ped node has invalid priority" ) );
							break;
					}
				}
			}
		}
		
		// if there weren't any good choices, pick the best one
		if ( !( num_valid_high_choices || num_valid_low_choices ) )
		{
			waypoint = SkateScript::GetLink( m_node_to, max_angle_index );
		}
		else
		{
			bool find_low_priority = false;
			// decide if we should pick from a low priority or high priority node
			if ( num_valid_high_choices && num_valid_low_choices )
			{			
				float low_priority_probability = Script::GetFloat( "ped_low_priority_waypoint_probability", Script::ASSERT );
				Dbg_MsgAssert( low_priority_probability < 1, ( "ped_low_priority_waypoint_probability is %f", low_priority_probability ) );
				if ( Mth::Rnd( 100 ) <= ( low_priority_probability * 100 ) )
					find_low_priority = true;
			}
			else if ( num_valid_low_choices )
				find_low_priority = true;
			
			// select random index from valid choices
			if ( find_low_priority )
				waypoint = SkateScript::GetLink( m_node_to, valid_low_priority_choices[Mth::Rnd( num_valid_low_choices )] );
			else
				waypoint = SkateScript::GetLink( m_node_to, valid_high_priority_choices[Mth::Rnd( num_valid_high_choices )] );
		}
	}
	
	// make sure we got something
	if ( waypoint == -1 )
	{
		Dbg_MsgAssert( 0, ( "SelectNextWaypoint couldn't find a valid waypoint" ) );
	}
	else
	{
		m_node_next = waypoint;
		SkateScript::GetPosition( m_node_next, &m_wp_next );
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPedLogicComponent::ShouldExecuteAction( Script::CStruct* pNodeData, uint32 weight_name )
{
	float weight;
	pNodeData->GetFloat( weight_name, &weight, Script::ASSERT );
	Dbg_MsgAssert( weight > 0 && weight <= 1, ( "Waypoint %s has a bad action weight of %f\n", Script::FindChecksumName( GetObject()->GetID() ), weight ) );
	if ( weight > 0.99f )
		return true;
	return ( Mth::Rnd( vACTION_WEIGHT_RESOLUTION ) < ( weight * vACTION_WEIGHT_RESOLUTION ) );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::AdjustSpeed( float percent )
{
	Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
	pMotionComp->m_max_vel *= percent;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::AdjustNormal()
{
	Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
	Dbg_Assert( pMotionComp );
	if ( pMotionComp )
	{
		Obj::STriangle last_triangle = pMotionComp->m_last_triangle;
		Mth::Vector v1 = last_triangle.mpVertices[1] - last_triangle.mpVertices[0];
		Mth::Vector v2 = last_triangle.mpVertices[2] - last_triangle.mpVertices[0];
		Mth::Vector normal = ( Mth::CrossProduct( v1, v2 ) ).Normalize();
		if ( normal[Y] < 0.0f )
			normal = -normal;

		if ( normal != m_current_normal )
		{
			m_current_normal = normal;	// remember this, for detecting if it changes
			m_last_display_normal = m_display_normal;	// remember start position for lerping
			m_normal_lerp = 1.0f;	// set lerp counter
		}

		// if m_normal_lerp is 0.0, then we don't need to do anything, as
		// we should already be there
		if ( m_normal_lerp != 0.0f )	   		// if lerping
		{			
			// If the last display normal is the same as the current normal
			// then we can't interpolate between them
			if ( m_last_display_normal == m_current_normal )
			{
				m_normal_lerp = 0.0f;
			}
			else
			{
				// adjust lerp at constant speed from 1.0 to 0.0, accounting for framerate
				// m_normal_lerp -= NORMAL_LERP_SPEED * (mp_physics->m_time * 60.0f);
				m_normal_lerp -= 0.1f * m_time * 60.0f;
		
				// if gone all the way, then clear lerping values
				// and set m_display_normal to be the current face normal
				if (m_normal_lerp <= 0.0f)
				{
					m_normal_lerp = 0.0f;
					m_display_normal = m_current_normal;
				}
				else
				{
					// Still between old and current normal, so
					// calculate intermediate normal
					m_display_normal = Mth::Lerp( m_current_normal, m_last_display_normal, m_normal_lerp );
					m_display_normal.Normalize();		// Must be normalized...
				}
			}
		}
		
		// Now update the orientation matrix.
		// We need our up (Y) vector to be this vector
		// if it changes, rotate the X and Z vectors to match
		Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix();
		if ( display_matrix[Y] != m_display_normal )
		{
			display_matrix[Y] = m_display_normal;
			display_matrix.OrthoNormalizeAbout( Y );
			GetObject()->SetDisplayMatrix( display_matrix );
			m_current_display_matrix = display_matrix;
		}
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// testing out positional history
void CPedLogicComponent::AdjustHeading()
{
	AdjustHeading( m_pos_history[0] );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::AdjustHeading( Mth::Vector original_pos )
{
	Mth::Matrix mat0 = GetObject()->GetDisplayMatrix();
	Mth::Vector new_pos = GetObject()->m_pos;
	if ( m_flags & PEDLOGIC_TO_NODE_IS_LIP )
	{
		original_pos = m_wp_from;
		new_pos = m_wp_to;
	}

	// Gfx::AddDebugLine( original_pos, new_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ) );
	
	// change z vector
	mat0[Z] = ( new_pos - original_pos ).Normalize();
	// get new X and Y vectors
	mat0[X] = Mth::CrossProduct( mat0[Y], mat0[Z] );	
	mat0[Y] = Mth::CrossProduct( mat0[Z], mat0[X] );
	GetObject()->SetDisplayMatrix( mat0 );
	m_current_display_matrix = mat0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::RefreshDisplayMatrix()
{
	GetObject()->SetDisplayMatrix( m_current_display_matrix );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPedLogicComponent::do_collision_check( Mth::Vector *p_v0, Mth::Vector *p_v1, Mth::Vector *p_collisionPoint, 
								 Obj::STriangle *p_lastTriangle )
{
	// First, see if there is a collision with the last triangle, and if there is, use that.
	CFeeler feeler;
	if ( feeler.GetCollision( *p_v0, *p_v1, false ) )
	{
		Nx::CCollObj* p_col_obj=feeler.GetSector();
		Dbg_MsgAssert( p_col_obj->IsTriangleCollision(), ( "Not triangle collision !!!" ) );

		Nx::CCollObjTriData* p_tri_data=p_col_obj->GetGeometry();
		Dbg_MsgAssert( p_tri_data, ( "NULL p_tri_data" ) );

		int face_index=feeler.GetFaceIndex();
		p_lastTriangle->mpVertices[0]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 0));
		p_lastTriangle->mpVertices[1]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 1));
		p_lastTriangle->mpVertices[2]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 2));

		// if there is a collision, then snap to it
		*p_collisionPoint = feeler.GetPoint();

		UpdateSkaterSoundTerrain( feeler.GetTerrain() );

		return true;
	}
	return false;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::StickToGround()
{
	// stick to ground modified from the motion component...uses
	// the ped's normal instead of the world's Y
	Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
	Dbg_Assert( pMotionComp );
	
	Mth::Vector v0, v1;

	// make sure our X vector is correct (got Z from the nav matrix...)
	GetObject()->m_matrix[X] = Mth::CrossProduct( GetObject()->m_matrix[Y], GetObject()->m_matrix[Z] );
	GetObject()->m_matrix[X].Normalize();

	Mth::Matrix display_matrix = GetObject()->GetDisplayMatrix();
	v0 = GetObject()->m_pos + ( display_matrix[Y] * m_col_dist_above );
	v1 = GetObject()->m_pos + ( -display_matrix[Y] * m_col_dist_below );
	
	Mth::Vector collision_point;
	if ( do_collision_check( &v0, &v1, &collision_point, &( pMotionComp->m_last_triangle ) ) )
	{
		GetObject()->m_pos = collision_point;
		// GetObject()->GetDisplayMatrix()[Mth::POS] = collision_point;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::UpdateSkaterSoundStates( EStateType state )
{
	Dbg_MsgAssert( m_state == CRCD( 0xa85af587, "generic_skater" ), ( "CPedLogicComponent::UpdateSkaterSoundStates called on a non-skater ped" ) );

	Obj::CSkaterLoopingSoundComponent *pLoopingSoundComponent = GetSkaterLoopingSoundComponentFromObject( GetObject() );
	if ( pLoopingSoundComponent )
		pLoopingSoundComponent->SetState( state );

	Obj::CSkaterSoundComponent *pSoundComponent = GetSkaterSoundComponentFromObject( GetObject() );
	if ( pSoundComponent )
		pSoundComponent->SetState( state );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::UpdateSkaterSoundTerrain( ETerrainType terrain )
{
	Dbg_MsgAssert( m_state == CRCD(0xa85af587,"generic_skater"), ( "CPedLogicComponent::UpdateSkaterSoundTerrain called on non-skater ped" ) );

	// set the terrain type for looping sound
	Obj::CSkaterLoopingSoundComponent* pLoopingSoundComp = GetSkaterLoopingSoundComponentFromObject( GetObject() );
	if ( pLoopingSoundComp )
		pLoopingSoundComp->SetTerrain( terrain );

	Obj::CSkaterSoundComponent *pSoundComponent = GetSkaterSoundComponentFromObject( GetObject() );
	if ( pSoundComponent )
	{
		pSoundComponent->SetTerrain( terrain );
		pSoundComponent->SetLastTerrain( terrain );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPedLogicComponent::ShouldUseBiases()
{
	return ( m_flags & PEDLOGIC_IS_SKATER
			 && !( m_flags & PEDLOGIC_GRINDING )
			 && !( m_flags & PEDLOGIC_GRIND_BAILING )
			 && !( m_flags & PEDLOGIC_TO_NODE_IS_VERT )
			 && !( m_flags & PEDLOGIC_FROM_NODE_IS_VERT )
			 && !( m_flags & PEDLOGIC_JUMPING_TO_NODE )
		   );
}

/******************************************************************/
/*																  */
/*             			Skate Actions!                            */
/*                                                                */
/******************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CPedLogicComponent::GetSkateActionParams( Script::CStruct* pNodeData )
{
	Script::CStruct* pScriptParams = new Script::CStruct();
	// give higher priority to ped skater data - should this be reversed?	
	pScriptParams->AppendStructure( pNodeData );
	pScriptParams->AppendStructure( GetObject()->GetTags() );
	return pScriptParams;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoGrind( Script::CStruct* pNodeData )
{
	// printf("CPedLogicComponent::DoGrind\n");
	StopSkateActions();
	
	// m_is_grinding = true;
	m_flags |= PEDLOGIC_GRINDING;

	// set the terrain
	uint32 terrain_checksum;
	// TODO: Assert if terrain missing
	if ( pNodeData->GetChecksum( CRCD( 0x54cf8532, "TerrainType" ), &terrain_checksum, Script::NO_ASSERT ) )
	{
		UpdateSkaterSoundTerrain( Env::CTerrainManager::sGetTerrainFromChecksum( terrain_checksum ) );
	}

	Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData );
	Script::CScript* pScript = Script::SpawnScript( CRCD( 0xb7fca430, "ped_skater_grind" ), pScriptParams );
	pScript->mpObject = GetObject();
	delete pScriptParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
void CPedLogicComponent::GrindUpdate()
{
	m_grind_wobble_frames--;
	if ( m_grind_wobble_frames <= 0 )
	{
		printf("setting the wobble target to %f\n", m_grind_wobble_target);
		// set new wobble target
		Obj::CAnimationComponent* pAnimComp = GetAnimationComponentFromObject( GetObject() );
		Dbg_Assert( pAnimComp );
		pAnimComp->SetWobbleTarget( m_grind_wobble_target, false );
		m_grind_wobble_target = -m_grind_wobble_target;
		m_grind_wobble_frames = Script::GetInteger( "ped_skater_grind_wobble_frames", Script::ASSERT );
	}
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoGrindOff( Script::CStruct* pNodeData )
{
	if ( m_flags & PEDLOGIC_GRINDING )
	{
		// m_is_grinding = false;
		m_flags &= ~PEDLOGIC_GRINDING;
	
		Script::CScript* pScript = Script::SpawnScript( CRCD( 0x84c51e26, "ped_skater_grind_off" ) );
		pScript->mpObject = GetObject();
		// delete pScriptParams;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoJump( Script::CStruct* pNodeData, bool is_vert )
{
	StopSkateActions();
	
	// call Obj_Jump on the object
	Script::CStruct* pScriptParams;
	Script::CScript* pScript;
	if ( is_vert )
	{
		m_flags &= ~PEDLOGIC_MOVING_ON_PATH;
		pScriptParams = GetJumpParams( pNodeData, is_vert );
		pScript = Script::SpawnScript( CRCD( 0x990152d7, "ped_skater_vert_jump" ), pScriptParams );
	}
	else
	{
		if ( pNodeData->ContainsFlag( CRCD( 0xb02567ae, "JumpToNextNode" ) ) )
		{
			m_flags &= ~PEDLOGIC_MOVING_ON_PATH;
			pScriptParams = GetJumpParams( pNodeData, is_vert );
			m_flags |= PEDLOGIC_JUMPING_TO_NODE;
		}
		else
		{
			pScriptParams = GetSkateActionParams( pNodeData );
		}
		pScript = Script::SpawnScript( CRCD( 0x28be3d25, "ped_skater_jump" ), pScriptParams );
	}
	pScript->mpObject = GetObject();
	delete pScriptParams;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoLipTrick( Script::CStruct* pNodeData )
{
	StopSkateActions();
	
	// stop moving on path
	m_flags &= ~PEDLOGIC_MOVING_ON_PATH;

	UpdateLipDisplayMatrix();
	
	Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData );
	Script::CScript* pScript = Script::SpawnScript( CRCD( 0x2fd2b4b8, "ped_skater_lip_trick" ), pScriptParams );
	pScript->mpObject = GetObject();
	delete pScriptParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::UpdateLipDisplayMatrix()
{
	// set the display matrix
	Mth::Matrix mat0 = GetObject()->GetDisplayMatrix();	
	
	// change z vector
	mat0[Z] = Mth::Vector(0, 1, 0);
	mat0[Y] = m_last_display_normal.Normalize();
	// get new X and Y vectors
	mat0[X] = Mth::CrossProduct( mat0[Y], mat0[Z] ).Normalize();
	mat0[Y] = Mth::CrossProduct( mat0[Z], mat0[X] ).Normalize();
	GetObject()->SetDisplayMatrix( mat0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoManual( Script::CStruct* pNodeData )
{
	StopSkateActions();
	
	Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData );
	Script::CScript* pScript = Script::SpawnScript( CRCD( 0x1462af22, "ped_skater_manual" ), pScriptParams );
	pScript->mpObject = GetObject();
	delete pScriptParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoManualDown( Script::CStruct* pNodeData )
{
	StopSkateActions();
	
	Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData );
	Script::CScript* pScript = Script::SpawnScript( CRCD( 0x4c0caa11, "ped_skater_manual_down" ), pScriptParams );
	pScript->mpObject = GetObject();
	delete pScriptParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CPedLogicComponent::GetJumpParams( Script::CStruct* pNodeData, bool is_vert )
{
	StopSkateActions();

	// stop moving on path 
	m_flags &= ~PEDLOGIC_DOING_VERT_ROTATION;
	m_flags &= ~PEDLOGIC_DOING_SPINE;

	// reset angles
	m_spine_current_angle = 0.0f;
	m_spine_start_angle = 0.0f;
	m_rot_current_angle = 0.0f;

	Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData );
	
	// figure heading and time
	// get the height and gravity
	float height;
	pNodeData->GetFloat( CRCD( 0x838da447, "jumpHeight" ), &height, Script::ASSERT );
	float gravity = Script::GetFloat( CRCD( 0xe224a5d3, "ped_skater_jump_gravity" ), Script::ASSERT );

	float time_total;

	if ( is_vert || pNodeData->ContainsFlag( CRCD( 0xb02567ae, "JumpToNextNode" ) ) )
	{
		Mth::Vector pt = m_wp_to - GetObject()->m_pos;
		pt[Y] = 0.0f;
		
		if ( GetObject()->m_pos[Y] + height < m_wp_to[Y] )
		{
			height = m_wp_to[Y] - GetObject()->m_pos[Y] + Script::GetFloat( CRCD( 0x23f14b13, "ped_skater_jump_to_next_node_height_slop" ), Script::ASSERT );						
			// Script::PrintContents( pNodeData );
			// Dbg_MsgAssert( 0, ( "JumpToNextNode selected but jumpHeight isn't enough to reach target node." ) );
		}		
		
		// adjust height to compensate for difference between ped height
		// and height of waypoint
		// height += pt[Y];
		// pt[Y] = 0.0f;
		
		// figure the time to rise
		// d = vi*t + (a*t^2)/2
		Dbg_Assert( gravity < 0 );
		float time_to_rise = sqrtf( 2 * -height / gravity );
	
		// figure the time to fall
		float time_to_fall = sqrtf( 2 * Mth::Abs( ( GetObject()->m_pos[Y] + height - m_wp_to[Y] ) / gravity ) );
	
		// compute the inital y velocity
		float vi = ( height / time_to_rise ) - ( 0.5f * gravity * time_to_rise );
		Dbg_Assert( vi > 0 );
	
		// find the horizontal distance we need to travel
		float distance = pt.Length();
	
		// horizontal velocity
		time_total = time_to_rise + time_to_fall;
		Mth::Vector vh = ( distance / time_total ) * pt.Normalize();
	
		// vertical velocity
		Mth::Vector vy = vi * Mth::Vector(0, 1, 0);
	
		// total speed and heading
		float jump_speed = ( vh + vy ).Length();
		Mth::Vector heading = ( vh + vy ).Normalize();
	
		pScriptParams->AddVector( CRCD( 0xfd4bc03e, "heading" ), heading );
		pScriptParams->AddFloat( CRCD( 0x1c4c5690, "jumpSpeed" ), jump_speed );
		pScriptParams->AddFloat( CRCD( 0x66ced87b, "jumpTime" ), time_total );
	}
	else
	{
		// just use the jump height
		float jump_speed = sqrtf( 2 * gravity * -height );
		pScriptParams->AddFloat( CRCD(0x1c4c5690,"jumpSpeed"), jump_speed );

		time_total = 2 * sqrtf( 2 * -height * gravity );
	}

	// setup any rotation
	int rot_angle = 0;
	float rot_float;
	if ( pNodeData->ContainsFlag( CRCD( 0xb4077854, "RandomSpin" ) ) )
	{
		// figure the spin angle based on the time we've got
		float min_180_time = Script::GetFloat( CRCD( 0x8b381ab1, "ped_skater_min_180_spin_time" ), Script::ASSERT );
	
		int mult = (int)( time_total / min_180_time );
		// cap the rotation to 720 degrees
		if ( mult > 4 )
			mult = 4;
	
		rot_angle = 180 * Mth::Rnd( mult + 1 );
	}
	else if ( pNodeData->GetFloat( CRCD( 0x96fb50d9, "SpinAngle" ), &rot_float, Script::NO_ASSERT ) )
	{
		// cast to int but make sure it doesn't get rounded down
		rot_angle = (int)( rot_float + 0.01f );
	}
	
	rot_angle = abs( rot_angle );
	if ( rot_angle > 0 )
	{
		// figure fs or bs
		uint32 spin_direction;
		pNodeData->GetChecksum( CRCD( 0xef0b71a0, "SpinDirection" ), &spin_direction, Script::ASSERT );
		
		Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
		bool is_flipped = pAnimationComponent->IsFlipped();
		
		switch ( spin_direction )
		{
			case CRCC( 0x20e1c4a3, "BS" ):
			{
				if ( is_flipped )
				{
					rot_angle *= -1;
				}
				break;
			}
			case CRCC( 0x448d01a7, "FS" ):
			{
				if ( !is_flipped )
				{
					rot_angle *= -1;
				}
				break;
			}
			case CRCC( 0xe7390a8b, "Rand" ):
			{
				// randomly change direction
				int toggle = Mth::Rnd( 2 );
				if ( toggle == 1 )
				{
					rot_angle *= -1;
				}
				break;
			}
			default:
				Dbg_MsgAssert( 0, ( "Unknown SpinDirection" ) );
				break;
		}
		
		m_rot_angle = rot_angle;
		m_flags |= PEDLOGIC_DOING_VERT_ROTATION;
	}

	bool is_spine = pNodeData->ContainsFlag( CRCD( 0x4aa0cff, "SpineTransfer" ) );
	if ( rot_angle % 360 == 0 )
	{
		if ( !is_spine )
			pScriptParams->AddChecksum( NONAME, CRCD( 0x8868e76a, "should_flip" ) );
	}
	else if ( is_spine )
	{
		pScriptParams->AddChecksum( NONAME, CRCD( 0x8868e76a, "should_flip" ) );
	}

	// setup any spine trasnfer
	if ( is_spine )
	{
		m_flags |= PEDLOGIC_DOING_SPINE;
		
		// initialize spine angle to current angle between ped and vert
		Mth::Vector temp_y(0, 1, 0);
		float spine_start = Mth::GetAngle( m_current_display_matrix[Z], temp_y );
		m_spine_start_angle = spine_start + Script::GetFloat( CRCD(0x95e91d23,"ped_skater_spine_rotation_slop") );
	}

	if ( m_flags & PEDLOGIC_DOING_SPINE || m_flags & PEDLOGIC_DOING_VERT_ROTATION )
	{
		m_rot_start_matrix = m_current_display_matrix;
		m_rot_total_time = time_total * Script::GetFloat( CRCD( 0xc1dfc97b, "ped_skater_vert_rotation_time_slop" ), Script::ASSERT );
		m_rot_current_time = 0.0f;
	}

	// check if we're jumping to the next node
	if ( pNodeData->ContainsFlag( CRCD( 0xb02567ae, "JumpToNextNode" ) ) )
	{
		pScriptParams->AddFloat( CRCD( 0xa1810c27, "land_height" ), m_wp_to[Y] );
	}
	
	return pScriptParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoGrab( Script::CStruct* pNodeData, bool is_vert )
{
	Script::CStruct* pScriptParams;
	if ( is_vert )
	{
		// Obj::CAnimationComponent* pAnimComp = GetAnimationComponentFromObject( GetObject() );
		// printf("flipped = %i\n", pAnimComp->IsFlipped() );

		m_flags &= ~PEDLOGIC_MOVING_ON_PATH;
		pScriptParams = GetJumpParams( pNodeData, is_vert );
		pScriptParams->AddChecksum( NONAME, CRCD( 0x1615618, "is_vert" ) );
	}
	else
	{
		pScriptParams = GetSkateActionParams( pNodeData );

		// see if we're already jumping
		Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
		if ( pMotionComp->m_movingobj_status & MOVINGOBJ_STATUS_JUMPING )
			pScriptParams->AddInteger( CRCD( 0x65b5788d, "is_jumping" ), 1 );
		else
		{
			// figure out how long before we start to fall
			float jumpSpeed = Script::GetFloat( CRCD( 0xf1ff758a, "ped_skater_jump_speed" ), Script::ASSERT );
			float jumpGravity = Script::GetFloat( CRCD( 0xe224a5d3, "ped_skater_jump_gravity" ), Script::ASSERT );
			float time = 2 * sqrtf( Mth::Sqr( jumpSpeed ) / Mth::Sqr( jumpGravity ) );
			pScriptParams->AddInteger( CRCD( 0x66ced87b, "jumpTime" ), time );
			pScriptParams->AddInteger( CRCD( 0x65b5788d, "is_jumping" ), 0 );
		}
	}
		
	Script::CScript* pScript = Script::SpawnScript( CRCD( 0x10585cb3, "ped_skater_grab_trick" ), pScriptParams );
	pScript->mpObject = GetObject();
	
	delete pScriptParams;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoRotations()
{
	m_rot_current_time += m_time;

	float percent = m_time / m_rot_total_time;
	if ( percent > 1.0f )
		percent = 1.0f;

	float spine_target_angle = 180.0f - m_spine_start_angle;
	
	if ( m_flags & PEDLOGIC_DOING_VERT_ROTATION )
		m_rot_current_angle += percent * (float)m_rot_angle;
	if ( m_flags & PEDLOGIC_DOING_SPINE )
		m_spine_current_angle += percent * spine_target_angle;
	
	if ( m_rot_current_time >= m_rot_total_time || percent >= 1.0f )
	{
		if ( m_flags & PEDLOGIC_DOING_VERT_ROTATION )
			m_rot_current_angle = (float)m_rot_angle;
		if ( m_flags & PEDLOGIC_DOING_SPINE )
			m_spine_current_angle = spine_target_angle;

		m_flags &= ~PEDLOGIC_DOING_VERT_ROTATION;
		m_flags &= ~PEDLOGIC_DOING_SPINE;
	}

	// cap spine angle
	if ( m_flags & PEDLOGIC_DOING_SPINE && m_spine_current_angle > spine_target_angle )
	{
		m_spine_current_angle = spine_target_angle;
	}

	Mth::Matrix temp = m_rot_start_matrix;
	temp.RotateLocal( Mth::Vector( Mth::DegToRad( m_spine_current_angle ), Mth::DegToRad( m_rot_current_angle ), 0 ) );
	GetObject()->SetDisplayMatrix( temp );
	m_current_display_matrix = temp;
	m_display_normal = m_last_display_normal = m_current_normal = temp[Y];
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::Stop( Script::CStruct* pNodeData )
{
	StopSkateActions();

	m_flags |= PEDLOGIC_STOPPED;
	
	Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData );
	Script::CScript* pScript = Script::SpawnScript( CRCD( 0x365b2d85, "ped_skater_stop" ), pScriptParams );
	pScript->mpObject = GetObject();
	delete pScriptParams;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::StopSkateActions()
{
	// BB - I moved this from script because it was too damn slow!
	Script::CArray* p_scripts_to_stop = Script::GetArray( CRCD( 0x2ecd9f57, "ped_skater_action_scripts" ), Script::ASSERT );
	int array_size = p_scripts_to_stop->GetSize();
	Obj::CObject* p_object = (Obj::CObject*)GetObject();
	
	for ( int i = 0; i < array_size; i++ )
	{
		uint32 script_to_stop = p_scripts_to_stop->GetChecksum( i );
		Script::StopScriptsUsingThisObject_Proper( p_object, script_to_stop );
	}

	// m_is_grinding = false;
	m_flags &= ~PEDLOGIC_GRINDING;

	m_flags &= ~PEDLOGIC_DOING_VERT_ROTATION;
	m_flags &= ~PEDLOGIC_DOING_SPINE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::Bail()
{
	// kill any running bail scripts
	Script::StopScriptsUsingThisObject_Proper( GetObject(), CRCD(0xe630bf07,"ped_skater_grind_bail") );
	Script::StopScriptsUsingThisObject_Proper( GetObject(), CRCD(0x78131e4f,"ped_skater_generic_bail") );

	// restore the max current velocity before calling this script again!
	if ( m_original_max_vel > 0 )
	{
		Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
		Dbg_Assert( pMotionComp );
		pMotionComp->m_max_vel = m_original_max_vel;
	}
	
	Script::CScript* pScript;
	if ( m_flags & PEDLOGIC_GRINDING )
	{		
		pScript = Script::SpawnScript( CRCD(0xe630bf07,"ped_skater_grind_bail") );
	}
	else 
	{
		pScript = Script::SpawnScript( CRCD(0x78131e4f,"ped_skater_generic_bail") );
	}
	StopSkateActions();
	pScript->mpObject = GetObject();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::GrindBail()
{
	if ( m_flags & PEDLOGIC_GRINDING )
	{
		m_flags &= ~PEDLOGIC_GRINDING;
		m_flags |= PEDLOGIC_GRIND_BAILING;
	
		// select a new next node
		int num_links = SkateScript::GetNumLinks( m_node_to );
	
		// if there's only one link, we have to keep going...shitty
		if ( num_links > 1 )
		{
			// find a grind_bail node!
			for ( int i = 0; i < num_links; i++ )
			{
				int test_node = SkateScript::GetLink( m_node_to, i );
				Script::CStruct* pNodeData = SkateScript::GetNode( test_node );

				uint32 skate_action;
				if ( pNodeData->GetChecksum( CRCD( 0x963dc198, "skateAction" ), &skate_action )
					 && skate_action == CRCD( 0xe41199af, "grind_bail" ) )
				{
					m_node_next = test_node;
					SkateScript::GetPosition( m_node_next, &m_wp_next );
					break;
				}
			}
		}

		// run script!
		Script::CScript* pScript = Script::SpawnScript( CRCD( 0xe630bf07, "ped_skater_grind_bail" ) );
		pScript->mpObject = GetObject();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoRollOff( Script::CStruct* pNodeData )
{
	Script::CScript* pScript = Script::SpawnScript( CRCD( 0xb183d99e, "ped_skater_roll_off" ) );
	pScript->mpObject = GetObject();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPedLogicComponent::DoFlipTrick( Script::CStruct* pNodeData, bool is_vert )
{
	StopSkateActions();
	
	// jump if we need to
	Obj::CMotionComponent* pMotionComp = GetMotionComponentFromObject( GetObject() );
	Dbg_Assert( pMotionComp );
	Script::CStruct* pScriptParams = GetSkateActionParams( pNodeData );

	Script::CStruct *pJumpParams = GetJumpParams( pNodeData, is_vert );
	pScriptParams->AppendStructure( pJumpParams );
	delete pJumpParams;

	if ( is_vert )
	{
		m_flags &= ~PEDLOGIC_MOVING_ON_PATH;		
		pScriptParams->AddChecksum( NONAME, CRCD( 0x1615618, "is_vert" ) );
	}

	// check jumping state
	if ( pMotionComp->m_movingobj_status & MOVINGOBJ_STATUS_JUMPING )
		pScriptParams->AddInteger( CRCD( 0x65b5788d, "is_jumping" ), 1 );
	else
		pScriptParams->AddInteger( CRCD( 0x65b5788d, "is_jumping" ), 0 );

	if ( pNodeData->ContainsFlag( CRCD( 0xb02567ae, "JumpToNextNode" ) ) )
	{
		m_flags &= ~PEDLOGIC_MOVING_ON_PATH;
		m_flags |= PEDLOGIC_JUMPING_TO_NODE;
	}

	Script::CScript *pScript = Script::SpawnScript( CRCD( 0xb83c383c, "ped_skater_flip_trick" ), pScriptParams );
	pScript->mpObject = GetObject();
	delete pScriptParams;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


}


================================================
FILE: Code/Gel/Components/PedLogicComponent.h
================================================

//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       PedLogicComponent.h
//* OWNER:          Brad Bulkley
//* CREATION DATE:  1/9/03
//****************************************************************************

#ifndef __COMPONENTS_PEDLOGICCOMPONENT_H__
#define __COMPONENTS_PEDLOGICCOMPONENT_H__

#include 
#include 

#include 

#include 

#include 

#include 
#include 

#define		CRC_PEDLOGIC CRCD( 0x6efddfb8, "PedLogic" )
#define		GetPedLogicComponent() ((Obj::CPedLogicComponent*)GetComponent(CRC_PEDLOGIC))
#define		GetPedLogicComponentFromObject(pObj) ((Obj::CPedLogicComponent*)(pObj)->GetComponent(CRC_PEDLOGIC))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

	// flags
	enum
	{
		PEDLOGIC_IS_SKATER				= ( 1 << 0	),
		PEDLOGIC_MOVING_ON_PATH			= ( 1 << 1	),
		PEDLOGIC_GRINDING				= ( 1 << 2	),
		PEDLOGIC_TO_NODE_IS_VERT		= ( 1 << 3	),
		PEDLOGIC_FROM_NODE_IS_VERT		= ( 1 << 4	),
		PEDLOGIC_NEXT_NODE_IS_VERT		= ( 1 << 5	),
		PEDLOGIC_BAILING				= ( 1 << 6	),
		PEDLOGIC_GRIND_BAILING			= ( 1 << 7	),
		PEDLOGIC_JUMP_AT_TO_NODE		= ( 1 << 8	),
		PEDLOGIC_DOING_VERT_ROTATION	= ( 1 << 9	),
		PEDLOGIC_DOING_SPINE			= ( 1 << 10	),
		PEDLOGIC_JUMPING_TO_NODE		= ( 1 << 11	),
		PEDLOGIC_STOPPED				= ( 1 << 12	),
		PEDLOGIC_DOING_LIP_TRICK		= ( 1 << 13	),
		PEDLOGIC_DOING_MANUAL			= ( 1 << 14 ),
		PEDLOGIC_TO_NODE_IS_LIP			= ( 1 << 15 ),
	};
	
	const int vPOS_HISTORY_SIZE = 5;


class CPathObjectTracker;

enum
{
    vNUM_WHISKERS=4
};


class CPedLogicComponent : public CBaseComponent
{
public:
    CPedLogicComponent();
    virtual ~CPedLogicComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

private:
	void							SelectNextWaypoint();
	void							HitWaypoint();

	bool							WaypointIsVert( int waypoint );
	
	virtual void					GenericPedUpdate();
	virtual void					GenericSkaterUpdate();
	void							UpdateLipDisplayMatrix();

	// new generic ped logic stuff
	bool							CheckForOtherPeds();
	float							GetTotalXBias();
	float							GetTotalZBias();
	void							UpdatePathBias();
	bool							UpdateTargetBias();
	void							AdjustBias( float angle, float bias );
	void							AddWhiskerBias( float angle, float bias, float distance, float range );
	void							DecayWhiskerBiases();
	void							UpdatePosition();
	void							RotatePosHistory();
	void							ResetBias();
	void							AddTargetBias( Mth::Vector target, float target_bias );

	bool							ShouldExecuteAction( Script::CStruct* pNodeData, uint32 weight_name );
	void							AdjustSpeed( float percent );

	void							DoWalkActions( Script::CStruct* pNodeData );
	void							DoSkateActions( Script::CStruct* pNodeData );
	void							DoGenericNodeActions( Script::CStruct* pNodeData );

	void							AdjustNormal();
	void							AdjustHeading();
	void							AdjustHeading( Mth::Vector original_pos );
	void							RefreshDisplayMatrix();
	void							StickToGround();

	bool							do_collision_check( Mth::Vector *p_v0, Mth::Vector *p_v1, Mth::Vector *p_collisionPoint, Obj::STriangle *p_lastTriangle );

	void							UpdateSkaterSoundStates( EStateType state );
	void							UpdateSkaterSoundTerrain( ETerrainType terrain );

	bool							ShouldUseBiases();
	
	// skate actions
	void							DoGrind( Script::CStruct* pNodeData );
	void							DoGrindOff( Script::CStruct* pNodeData );
	// void							GrindUpdate();
	
	Script::CStruct*				GetJumpParams( Script::CStruct* pNodeData, bool is_vert = false );
	void							DoGrab( Script::CStruct* pNodeData, bool is_vert = false );
	void							DoRotations();

	void							DoLipTrick( Script::CStruct* pNodeData );
	void							DoJump( Script::CStruct* pNodeData, bool is_vert = false );
	void							DoManual( Script::CStruct* pNodeData );
	void							DoManualDown( Script::CStruct* pNodeData );
	void							Stop( Script::CStruct* pNodeData );
	void							Bail();
	void							GrindBail();
	void							DoRollOff( Script::CStruct* pNodeData );
	void							DoFlipTrick( Script::CStruct* pNodeData, bool is_vert = false );

	void							StopSkateActions();

	Script::CStruct*				GetSkateActionParams( Script::CStruct* pNodeData );
protected:
	uint32							m_state;

	uint32							m_flags;
	
	// new generic ped logic stuff
	float							m_bias[vNUM_WHISKERS];
	float							m_whiskers[vNUM_WHISKERS];

	// nodes
	int								m_node_from;
	int								m_node_to;
	int								m_node_next;

	// waypoints of nodes
	Mth::Vector						m_wp_from;
	Mth::Vector						m_wp_to;
	Mth::Vector						m_wp_next;

	int								m_turn_frames;
	int								m_max_turn_frames;

	CPathObjectTracker*				mp_path_object_tracker;

	float							m_time;

	// normal lerp
	Mth::Vector						m_last_display_normal;
	Mth::Vector						m_current_normal;
	Mth::Vector						m_display_normal;
	float							m_normal_lerp;

	// stick to ground
	float							m_col_dist_above;
	float							m_col_dist_below;

	// velocity
	float							m_original_max_vel;

	// rotation
	int								m_rot_angle;
	float							m_rot_current_angle;
	float							m_rot_total_time;
	float							m_rot_current_time;
	Mth::Matrix						m_rot_start_matrix;

	// spine
	float							m_spine_current_angle;
	float							m_spine_start_angle;
	
	// display matrix as of last update
	Mth::Matrix						m_current_display_matrix;

	Mth::Vector						m_pos_history[vPOS_HISTORY_SIZE];
	
	float							m_speed_fraction;
};

}

#endif


================================================
FILE: Code/Gel/Components/ProximTriggerComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ProximTriggerComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  2/28/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent* CProximTriggerComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CProximTriggerComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CProximTriggerComponent::CProximTriggerComponent() : CBaseComponent()
{
	SetType( CRC_PROXIMTRIGGER );
	
	if (!Mdl::Skate::Instance()->mpProximManager)
	{
		Dbg_Message("ProximTriggerComponent created before ProximManager; ProximNodes will not work");
		return;
	}
	
	Dbg_MsgAssert(Mdl::Skate::Instance()->mpProximManager, ("ProximTriggerComponent created before ProximManager"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CProximTriggerComponent::~CProximTriggerComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CProximTriggerComponent::InitFromStructure( Script::CStruct* pParams )
{
	if (pParams->ContainsComponentNamed(CRCD(0x1313e3c, "ProximTriggerTarget")))
	{
		uint32 script_target_id;
		pParams->GetChecksum(CRCD(0x1313e3c, "ProximTriggerTarget"), &script_target_id);
		
		mp_script_target_object = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(script_target_id));
		Dbg_MsgAssert(mp_script_target_object, ("ProximTriggerComponent given unidentifiable target object"));
	}
	
	m_active = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CProximTriggerComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CProximTriggerComponent::Update()
{
	if (!m_active) return;
	ProximUpdate();
}

void CProximTriggerComponent::ProximUpdate()
{

	#if 0	
	
	Dbg_Assert(mp_script_target_object);
	
	uint32 proxim_trigger_mask = Mdl::Skate::Instance()->mpProximManager->GetProximTriggerMask(m_viewport);
	
	Proxim_Update(proxim_trigger_mask, mp_script_target_object, GetObject()->GetPos());
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CProximTriggerComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
	
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CProximTriggerComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info, ("NULL p_info sent to CProximTriggerComponent::GetDebugInfo"));
	
	p_info->AddChecksum("mp_script_target_object", mp_script_target_object->GetID());
	p_info->AddInteger("proxim_trigger_mask", Mdl::Skate::Instance()->mpProximManager->GetProximTriggerMask(m_viewport));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProximTriggerComponent::SetScriptTarget ( CCompositeObject* p_script_target_object )
{
	Dbg_Assert(p_script_target_object);
	mp_script_target_object = p_script_target_object;
}

}


================================================
FILE: Code/Gel/Components/ProximTriggerComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ProximTriggerComponent.h
//* OWNER:          Dan
//* CREATION DATE:  2/28/3
//****************************************************************************

#ifndef __COMPONENTS_PROXIMTRIGGERCOMPONENT_H__
#define __COMPONENTS_PROXIMTRIGGERCOMPONENT_H__

#include 
#include 

#include 

#define		CRC_PROXIMTRIGGER CRCD(0x98952fb6, "ProximTrigger")

#define		GetProximTriggerComponent() ((Obj::CProximTriggerComponent*)GetComponent(CRC_PROXIMTRIGGER))
#define		GetProximTriggerComponentFromObject(pObj) ((Obj::CProximTriggerComponent*)(pObj)->GetComponent(CRC_PROXIMTRIGGER))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CCameraComponent;

class CProximTriggerComponent : public CBaseComponent
{
public:
    CProximTriggerComponent();
    virtual ~CProximTriggerComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							SetScriptTarget ( CCompositeObject* p_script_target_object );
	
	void							SetActive (   ) { m_active = true; }
	void							SetInactive (   ) { m_active = false; }
	
	void							SetViewport ( int viewport ) { m_viewport = viewport; }
	int								GetViewport (   ) { return m_viewport; }
	
	void							ProximUpdate();
	
private:
	// target of the scripts run by proxim nodes which do not have a proxim object ID
	CObject*                        mp_script_target_object;
	
	// if the trigger is active
	bool							m_active;
	
	// the viewport which this component corresponds to; all proxim trigger components on a given viewport share a proxim trigger flag so that switching
	// camera will triggered inside/outside effects correctly; only one proxim trigger component with a given viewport should be active at any one time
	int								m_viewport;
	
	// peer components
	CCameraComponent*				mp_camera_component;
};

}

#endif


================================================
FILE: Code/Gel/Components/RailManagerComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       RailManagerComponent.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/17/03
//****************************************************************************

// The CEmptyComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy emptycomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "Empty" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage
#include 

#include 
#include 

#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  
CBaseComponent* CRailManagerComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CRailManagerComponent );	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CRailManagerComponent::CRailManagerComponent() : CBaseComponent()
{
	SetType( CRC_RAILMANAGER );
	mp_rail_manager = NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CRailManagerComponent::~CRailManagerComponent()
{
	// Remove the CRailManager if present.
	if( mp_rail_manager )
	{
		delete mp_rail_manager;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CRailManagerComponent::InitFromStructure( Script::CStruct* pParams )
{
	// There needs to be a NodeArrayComponent attached for the RailManagerComponent to operate.
	CNodeArrayComponent *p_nodearray_component = GetNodeArrayComponentFromObject( GetObject());
	Dbg_MsgAssert( p_nodearray_component, ( "RailManagerComponent created without NodeArrayComponent" ));

	// Remove any existing CRailManager.
	if( mp_rail_manager )
	{
		delete mp_rail_manager;
	}

	// Create a rail manager, and parse the node array for the rails.
	mp_rail_manager = new Obj::CRailManager();
	mp_rail_manager->AddRailsFromNodeArray( p_nodearray_component->GetNodeArray());
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CRailManagerComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure( pParams );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CRailManagerComponent::Update()
{
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CRailManagerComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch( Checksum )
	{
/*
        // @script | DoSomething | does some functionality
		case 0xbb4ad101:		// DoSomething
			DoSomething();
			break;

        // @script | ValueIsTrue | returns a boolean value
		case 0x769260f7:		// ValueIsTrue
		{
			return ValueIsTrue() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		break;
*/

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CRailManagerComponent::GetDebugInfo( Script::CStruct *p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert( p_info, ("NULL p_info sent to CRailManagerComponent::GetDebugInfo" ));

	// Add the number of Rail Manager nodes.
	if( mp_rail_manager )
	{
		p_info->AddInteger("RailManager.m_num_nodes", mp_rail_manager->GetNumNodes());
		p_info->AddString("RailManager.m_is_transformed", mp_rail_manager->IsMoving() ? "yes" : "no" );
	}

	// We call the base component's GetDebugInfo, so we can add info from the common base component.
	CBaseComponent::GetDebugInfo( p_info );
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Matrix CRailManagerComponent::UpdateRailManager (   )
{
	Dbg_Assert(mp_rail_manager);

	// form a transformation matrix
	Mth::Matrix total_mat = GetObject()->GetMatrix();
	total_mat[X][W] = 0.0f;
	total_mat[Y][W] = 0.0f;
	total_mat[Z][W] = 0.0f;
	total_mat[W] = GetObject()->GetPos();
	total_mat[W][W] = 1.0f;

	mp_rail_manager->UpdateTransform(total_mat);
	
	return total_mat;
}
	
}


================================================
FILE: Code/Gel/Components/RailManagerComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       RailManagerComponent.h
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/17/03
//****************************************************************************

#ifndef __COMPONENTS_RAILMANAGERCOMPONENT_H__
#define __COMPONENTS_RAILMANAGERCOMPONENT_H__

#include 
#include 

#include 

#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_RAILMANAGER								CRCD( 0x1cdfb2a3, "RailManager" )
#define		GetRailManagerComponent()					((Obj::CRailManagerComponent*)GetComponent( CRC_RAILMANAGER ))
#define		GetRailManagerComponentFromObject( pObj )	((Obj::CRailManagerComponent*)(pObj)->GetComponent( CRC_RAILMANAGER ))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CRailManagerComponent : public CBaseComponent
{
public:
    CRailManagerComponent();
    virtual ~CRailManagerComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	Mth::Matrix						UpdateRailManager (   );

	virtual Obj::CRailManager*		GetRailManager( void )	{ return mp_rail_manager; }

private:
	Obj::CRailManager*				mp_rail_manager;
};

}

#endif


================================================
FILE: Code/Gel/Components/RibbonComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       RibbonComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  5/20/3
//****************************************************************************

#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 

#define MESSAGE(a) { printf("M:%s:%i: %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPI(a) { printf("D:%s:%i: " #a " = %i\n", __FILE__ + 15, __LINE__, a); }
#define DUMPB(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a ? "true" : "false"); }
#define DUMPF(a) { printf("D:%s:%i: " #a " = %g\n", __FILE__ + 15, __LINE__, a); }
#define DUMPE(a) { printf("D:%s:%i: " #a " = %e\n", __FILE__ + 15, __LINE__, a); }
#define DUMPS(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPP(a) { printf("D:%s:%i: " #a " = %p\n", __FILE__ + 15, __LINE__, a); }
#define DUMPV(a) { printf("D:%s:%i: " #a " = %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z]); }
#define DUMP4(a) { printf("D:%s:%i: " #a " = %g, %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z], (a)[W]); }
#define DUMPM(a) { DUMP4(a[X]); DUMP4(a[Y]); DUMP4(a[Z]); DUMP4(a[W]); }
#define DUMP2(a) { printf("D:%s:%i " #a " = ", __FILE__ + 15, __LINE__); for (int n = 32; n--; ) { printf("%c", ((a) & (1 << n)) ? '1' : '0'); } printf("\n"); }
#define MARK { printf("K:%s:%i: %s\n", __FILE__ + 15, __LINE__, __PRETTY_FUNCTION__); }
#define PERIODIC(n) for (static int p__ = 0; (p__ = ++p__ % (n)) == 0; )

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CRibbonComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CRibbonComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CRibbonComponent::CRibbonComponent() : CBaseComponent()
{
	SetType( CRC_RIBBON );
	
	mp_links = NULL;
	
	m_last_frame_length = 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CRibbonComponent::~CRibbonComponent()
{
	if (mp_links)
	{
		delete mp_links;
		mp_links = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRibbonComponent::InitFromStructure( Script::CStruct* pParams )
{
	pParams->GetChecksum(CRCD(0xcab94088, "bone"), &m_bone_name, Script::ASSERT);
	pParams->GetInteger(CRCD(0x69feef91, "num_links"), &m_num_links, Script::ASSERT);
	pParams->GetFloat(CRCD(0x9f4625c2, "link_length"), &m_link_length, Script::ASSERT);
	pParams->GetChecksum(CRCD(0x99a9b716, "color"), &m_color, Script::ASSERT);
	
	mp_links = new SLink [ m_num_links ];
	for (int n = m_num_links; n--; )
	{
		mp_links[n].p.Set();
		mp_links[n].last_p.Set();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRibbonComponent::Finalize()
{
	mp_skeleton_component = GetSkeletonComponentFromObject(GetObject());
	
	Dbg_Assert(mp_skeleton_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRibbonComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRibbonComponent::Update()
{
	float frame_length = Tmr::FrameLength();
	
	// update link positions based on velocity
	for (int n = m_num_links; n--; )
	{
		Mth::Vector vel = mp_links[n].p - mp_links[n].last_p;
		vel *= 1.0f / m_last_frame_length;
		
		// damp
		vel -= 10.0f * frame_length * vel;
		
		// momentum
		mp_links[n].next_p = mp_links[n].p + frame_length * vel;
		
		// gravity
		mp_links[n].next_p[Y] -= 800.0f * frame_length * frame_length;
	}
	
	// fetch bone position
	Mth::Vector bone_p;
	mp_skeleton_component->GetBonePosition(m_bone_name, &bone_p);
	bone_p = GetObject()->GetMatrix().Rotate(bone_p);
	bone_p += GetObject()->GetPos();
	
	// update link constraints
	for (int c = 20; c--; )
	{
		// first link special case
		float length = (mp_links[0].next_p - bone_p).Length();
		Mth::Vector axis = (mp_links[0].next_p - bone_p) * (1.0f / length);
		float adjustment = m_link_length - length;
		mp_links[0].next_p += adjustment * axis;
		
		// second and subsequent links
		for (int n = 1; n < m_num_links; n++)
		{
			float length = (mp_links[n].next_p - mp_links[n - 1].next_p).Length();
			Mth::Vector axis = (mp_links[n].next_p - mp_links[n - 1].next_p) * (1.0f / length);
			
			float adjustment = 0.5f * (m_link_length - length);
			
			// hack
			if (length > 20.0f * m_link_length)
			{
				CallMemberFunction(CRCD(0x796084f4, "Ribbon_Reset"), NULL, NULL);
				return;
			}
			
			mp_links[n].next_p += adjustment * axis;
			mp_links[n - 1].next_p -= adjustment * axis;
		}
	}
	
	#if 0
	
	// cheap collision detection
	float min_y = GetObject()->GetPos()[Y] + 0.1f;
	for (int n = m_num_links; n--; )
	{
		mp_links[n].next_p[Y] = Mth::ClampMin(mp_links[n].next_p[Y], min_y);
	}
	
	#else
	
	Mth::CBBox bbox;
	Nx::CCollCache* p_coll_cache = Nx::CCollCacheManager::sCreateCollCache();
	for (int n = m_num_links; n--; )
	{
		bbox.AddPoint(mp_links[n].p);
		bbox.AddPoint(mp_links[n].next_p);
	}
	p_coll_cache->Update(bbox);
	
	CFeeler feeler;
	feeler.SetCache(p_coll_cache);
	// real collision detection
	for (int n = m_num_links; n--; )
	{
		feeler.m_start = mp_links[n].p;
		feeler.m_end = mp_links[n].next_p;
		if (!feeler.GetCollision()) continue;
		
		Mth::Vector movement = mp_links[n].next_p - mp_links[n].p;
		
		mp_links[n].next_p = feeler.GetPoint() + 0.1f * feeler.GetNormal();
		
		// assume no additional collision
		movement *= 1.0f - feeler.GetDist();
		movement.ProjectToPlane(feeler.GetNormal());
		
		mp_links[n].next_p += movement;
	}
	
	Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
	
	#endif
	
	// update state
	for (int n = m_num_links; n--; )
	{
		mp_links[n].last_p = mp_links[n].p;
		mp_links[n].p = mp_links[n].next_p;
	}
	m_last_frame_length = frame_length;
	
	// debug draw
	Gfx::AddDebugLine(bone_p, mp_links[0].p, m_color, 0, 1);
	for (int n = 1; n < m_num_links; n++)
	{
		Gfx::AddDebugLine(mp_links[n].p, mp_links[n - 1].p, m_color, 0, 1);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CRibbonComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case CRCC(0x796084f4, "Ribbon_Reset"):
		{
			MESSAGE("Resetting");
			
			Mth::Vector bone_p;
			mp_skeleton_component->GetBonePosition(m_bone_name, &bone_p);
			bone_p = GetObject()->GetMatrix().Rotate(bone_p);
			bone_p += GetObject()->GetPos();
			
			for (int n = m_num_links; n--; )
			{
				mp_links[n].p = bone_p;
				mp_links[n].last_p = bone_p;
				mp_links[n].p[Y] += 0.1f * n;
				mp_links[n].last_p[Y] += 0.1f * n;
			}
			break;
		}
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRibbonComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CRibbonComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

}


================================================
FILE: Code/Gel/Components/RibbonComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       RibbonComponent.h
//* OWNER:          Dan
//* CREATION DATE:  5/20/3
//****************************************************************************

#ifndef __COMPONENTS_RIBBONCOMPONENT_H__
#define __COMPONENTS_RIBBONCOMPONENT_H__

#include 
#include 

#include 

#define		CRC_RIBBON CRCD(0xee6fc5b, "Ribbon")

#define		GetRibbonComponent() ((Obj::CRibbonComponent*)GetComponent(CRC_RIBBON))
#define		GetRibbonComponentFromObject(pObj) ((Obj::CRibbonComponent*)(pObj)->GetComponent(CRC_RIBBON))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSkeletonComponent;

class CRibbonComponent : public CBaseComponent
{
	struct SLink
	{
		Mth::Vector					p;
		Mth::Vector					last_p;
		Mth::Vector					next_p;
	};
	
public:
    CRibbonComponent();
    virtual ~CRibbonComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
	virtual	void 					Finalize();
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
private:
	uint32							m_bone_name;
	int								m_num_links;
	float							m_link_length;
	uint32							m_color;
	
	float							m_last_frame_length;
	
	SLink*							mp_links;
	
	// peer components
	CSkeletonComponent*				mp_skeleton_component;
};

}

#endif


================================================
FILE: Code/Gel/Components/RiderComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       RiderComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  4/2/3
//****************************************************************************

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 



namespace Obj
{
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent* CRiderComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CRiderComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CRiderComponent::CRiderComponent() : CBaseComponent()
{
	SetType( CRC_RIDER );
	
	mp_collision_cache = Nx::CCollCacheManager::sCreateCollCache();
	
	mp_input_component				= NULL;
	mp_animation_component			= NULL;
	mp_movable_contact_component	= NULL;
	mp_horse_component				= NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CRiderComponent::~CRiderComponent()
{
	Nx::CCollCacheManager::sDestroyCollCache(mp_collision_cache);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CRiderComponent::Finalize()
{
	mp_input_component = GetInputComponentFromObject(GetObject());
	mp_animation_component = GetAnimationComponentFromObject(GetObject());
	mp_model_component = GetModelComponentFromObject(GetObject());
	mp_trigger_component = GetTriggerComponentFromObject(GetObject());
	mp_physics_control_component = GetSkaterPhysicsControlComponentFromObject(GetObject());
	mp_movable_contact_component = GetMovableContactComponentFromObject(GetObject());
	
	Dbg_Assert(mp_input_component);
	Dbg_Assert(mp_animation_component);
	Dbg_Assert(mp_model_component);
	Dbg_Assert(mp_trigger_component);
	Dbg_Assert(mp_physics_control_component);
	Dbg_Assert(mp_movable_contact_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CRiderComponent::InitFromStructure( Script::CStruct* pParams )
{
	uint32 camera_id;
	if (pParams->GetChecksum(CRCD(0xc4e311fa, "camera"), &camera_id))
	{
		CCompositeObject* p_camera = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(camera_id));
		Dbg_MsgAssert(mp_camera, ("No such camera object"));
		SetAssociatedCamera(p_camera);
	}
	
	m_script_drag_factor = 1.0f;
}
		 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CRiderComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CRiderComponent::Update()
{
	if( mp_horse_component == NULL )
	{
		return;
	}

	// TEMP: debounce R1 after a transition
//	if (m_ignore_grab_button && !mp_input_component->GetControlPad().m_R1.GetPressed())
//	{
//		m_ignore_grab_button = false;
//	}
	
	// zero the frame event
	m_last_frame_event = m_frame_event;
	m_frame_event = 0;
	
	// get input
//    get_controller_input();
	
	// extract initial state for this frame from the object
	m_frame_start_pos = m_pos = GetObject()->GetPos();
	
	m_horizontal_vel = GetObject()->GetVel();
	m_horizontal_vel[Y] = 0.0f;
	m_vertical_vel = GetObject()->GetVel()[Y];
	
	// note that m_facing and m_upward will often not be orthogonal, but will always span a plan
	
	// generally straight up, but now after a transition from skating
	m_upward = GetObject()->GetMatrix()[Y];
	
	m_facing = GetObject()->GetMatrix()[Z];
	m_facing[Y] = 0.0f;
	float length = m_facing.Length();
	if (length < 0.001f)
	{
		// upward facing orientation matrix
		m_facing = -GetObject()->GetMatrix()[Y];
		m_facing[Y] = 0.0f;
		m_facing.Normalize();
		
		// since m_upward is now in the same plan as m_facing, push m_upward up a touch
		m_upward[Y] += 0.01f;
		m_upward.Normalize();
	}
	else
	{
		m_facing /= length;
	}
	
	// set the frame length
	m_frame_length = Tmr::FrameLength();
	
	// go to our true Y position
	m_curb_float_height_adjusted = false;
	m_pos[Y] -= m_curb_float_height;
	
	// switch logic based on walking state
	go_on_horse_state();
	
//	switch (m_state)
//	{
//		case WALKING_GROUND:
//			go_on_ground_state();
//			break;
//
//		case WALKING_AIR:
//			go_in_air_state();
//			break;
//																	  
//		case WALKING_HOP:
//			go_hop_state();
//			break;
//																	  
//		case WALKING_HANG:
//			go_hang_state();
//			break;
//            
//		case WALKING_LADDER:
//			go_ladder_state();
//          break;
//			
//		case WALKING_ANIMWAIT:
//			go_anim_wait_state (   );
//			break;
//	}
	
	// the there's no curb to adjust due to, lerp down to zero
//	if (!m_curb_float_height_adjusted)
//	{
//		m_curb_float_height = Mth::Lerp(m_curb_float_height, 0.0f, s_get_param(CRCD(0x9b3388fa, "curb_float_lerp_down_rate")) * m_frame_length);
//	}
	
	// adjust back to our curb float Y position
//	m_pos[Y] += m_curb_float_height;
	
	// scripts may have restarted us / switched us to skating
//	if (should_bail_from_frame()) return;
	
	// keep the object from falling through holes in the geometry
//	if (m_state == WALKING_GROUND || m_state == WALKING_AIR)
//	{
//		uber_frig();
//	}
	
	// rotate to upright
//	lerp_upright();
	
	// setup the object based on this frame's walking
//	copy_state_into_object();
	
//	Dbg_Assert(m_frame_event);
//	GetObject()->SelfEvent(m_frame_event);
	
	// set the animation speeds
//	switch (m_anim_scale_speed)
//	{
//		case RUNNING:
//			if (m_anim_standard_speed > 0.0f)
//			{
//				mp_animation_component->SetAnimSpeed(m_anim_effective_speed / m_anim_standard_speed, false, false);
//			}
//			break;
//			
//		case HANGMOVE:
//			mp_animation_component->SetAnimSpeed(m_anim_effective_speed / s_get_param(CRCD(0xd77ee881, "hang_move_speed")), false, false);
//			break;
//					
//		case LADDERMOVE:
//			mp_animation_component->SetAnimSpeed(m_anim_effective_speed / s_get_param(CRCD(0xab2db54, "ladder_move_speed")), false, false);
//			break;
//	
//		default:
//			break;
//	}
	
	// camera controls
	// NOTE: script parameters
//	switch (m_frame_event)
//	{
//		case CRCC(0xf41aba21, "Hop"):
//			mp_camera_component->SetOverrides(m_facing, 0.05f);
//			break;
//		
//		case CRCC(0x2d9815c3, "HangMoveLeft"):
//		{
//			Mth::Vector facing = m_facing;
//			facing.RotateY(-0.95f);
//			mp_camera_component->SetOverrides(facing, 0.05f);
//			break;
//		}
//			
//		case CRCC(0x279b1f0b, "HangMoveRight"):
//		{
//			Mth::Vector facing = m_facing;
//			facing.RotateY(0.95f);
//			mp_camera_component->SetOverrides(facing, 0.05f);
//			break;
//		}
//					
//		case CRCC(0x4194ecca, "Hang"):
//			mp_camera_component->SetOverrides(m_facing, 0.05f);
//			break;
//		
//		case CRCC(0xc84243da, "Ladder"):
//		case CRCC(0xaf5abc82, "LadderMoveUp"):
//		case CRCC(0xfec9dded, "LadderMoveDown"):
//			mp_camera_component->SetOverrides(m_facing, 0.05f);
//			break;
//					
//		case CRCC(0x4fe6069c, "AnimWait"):
//			if (m_anim_wait_camera_mode == AWC_CURRENT)
//			{
//				mp_camera_component->SetOverrides(m_facing, 0.05f);
//			}
//			else
//			{
//				mp_camera_component->SetOverrides(m_drift_goal_facing, 0.05f);
//			}
//			break;
//	
//		default:
//			mp_camera_component->UnsetOverrides();
//			break;
//	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent::EMemberFunctionResult CRiderComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
#		if 0
		// @script | Rider_Ground |
		case CRCC(0x893213e5, "Rider_Ground"):
			return m_state == WALKING_GROUND ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Rider_Air |
		case CRCC(0x5012082e, "Rider_Air"):
			return m_state == WALKING_AIR ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Rider_Hang |
		case CRCC(0x9a3ca853, "Rider_Hang"):
			return m_state == WALKING_HANG ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Rider_Ladder |
		case CRCC(0x19702ca8, "Rider_Ladder"):
			return m_state == WALKING_LADDER ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Rider_GetStateTime | Loads the time in milliseconds since last state change.
		case CRCC(0xce64576c, "Rider_GetStateTime"):
			pScript->GetParams()->AddInteger(CRCD(0x5ab23cc9, "StateTime"), Tmr::ElapsedTime(m_state_timestamp));
			break;
		
		// @script | Rider_Jump |
		case CRCC(0x83e4bd70, "Rider_Jump"):
		{
			// jump strength scales with the length the jump button has been held
			jump(Mth::Lerp(
				s_get_param(CRCD(0x246d0bf3, "min_jump_factor")), 
				1.0f,
				Mth::ClampMax(mp_input_component->GetControlPad().m_x.GetPressedTime() / s_get_param(CRCD(0x12333ebd, "hold_time_for_max_jump")), 1.0f)
			));
			break;
		}
		
		case CRCC(0xeb0d763b, "Rider_HangJump"):
		{
			// jump strength scales with the length the jump button has been held
			jump(s_get_param(CRCD(0xf2fa5845, "hang_jump_factor")), true);
			break;
		}
		
		// @script | Rider_SetDragFactor |
		case CRCC(0xc6100a7d, "Rider_SetDragFactor"):
			pParams->GetFloat(NO_NAME, &m_script_drag_factor);
			break;
			
		case CRCC(0x4e4fae43, "Rider_ResetDragFactor"):
			m_script_drag_factor = 1.0f;
			break;
			
		case CRCC(0xaf04b983, "Rider_GetSpeedScale"):
		{
			uint32 checksum;
			if (m_anim_effective_speed < s_get_param(CRCD(0xf3649996, "max_slow_walk_speed")))
			{
				checksum = CRCD(0x1150cabb, "WALK_SLOW");
			}
			else if (m_anim_effective_speed < s_get_param(CRCD(0x6a5805d8, "max_fast_walk_speed")))
			{
				checksum = CRCD(0x131f2a2, "WALK_FAST");
			}
			else if (m_anim_effective_speed < s_get_param(CRCD(0x1c94cc9c, "max_slow_run_speed")))
			{
				checksum = CRCD(0x5606d106, "RUN_SLOW");
			}
			else
			{
				checksum = CRCD(0x4667e91f, "RUN_FAST");
			}
			pScript->GetParams()->AddChecksum(CRCD(0x92c388f, "SpeedScale"), checksum);
			
			break;
		}
		
		// @script | Rider_ScaleAnimSpeed | Sets the manner in which the walk animations speeds should be scaled.
		// @flag Off | No animation speed scaling.
		// @flag Run | Scale animation speeds against running speed.
		// @flag Rider | Scale animation speeds against walking speed.
		case CRCC(0x56112c03, "Rider_ScaleAnimSpeed"):
			if (pParams->ContainsFlag(CRCD(0xd443a2bc, "Off")))
			{
				if (m_anim_scale_speed != OFF)
				{
					m_anim_scale_speed = OFF;
					mp_animation_component->SetAnimSpeed(1.0f, false, true);
				}
			}
			else if (pParams->ContainsFlag(CRCD(0xaf895b3f, "Run")))
			{
				m_anim_scale_speed = RUNNING;
			}
			else if (pParams->ContainsFlag(CRCD(0x6384f1da, "HangMove")))
			{
				m_anim_scale_speed = HANGMOVE;
			}
			else if (pParams->ContainsFlag(CRCD(0xa2bfe505, "LadderMove")))
			{
				m_anim_scale_speed = LADDERMOVE;
			}
			else
			{
				Dbg_MsgAssert(false, ("Rider_ScaleAnimSpeed requires Off, Run, or Rider flag"));
			}
			
			pParams->GetFloat(CRCD(0xb2d59baf, "StandardSpeed"), &m_anim_standard_speed);
			break;
			
		// @script | Rider_AnimWaitComplete | Signal from script that the walk component should leave its animation wait
		case CRCC(0x9d3eebe8, "Rider_AnimWaitComplete"):
			anim_wait_complete();
			break;
				   			
		// @script | Rider_GetHangInitAnimType | Determine which type of initial hang animation should be played
		case CRCC(0xc6cd659e, "Rider_GetHangInitAnimType"):
			// m_initial_hang_animation is set when the hang rail is filtered
			pScript->GetParams()->AddChecksum(CRCD(0x85fa9ac4, "HangInitAnimType"), m_initial_hang_animation);
			break;
#		endif
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CRiderComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CRiderComponent::GetDebugInfo"));
	
	switch (m_state)
	{
		case WALKING_GROUND:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x58007c97, "GROUND"));
			break;
		case WALKING_AIR:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x439f4704, "AIR"));
			break;
		case WALKING_HOP:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0xf41aba21, "HOP"));
			break;										 
		case WALKING_HANG:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x4194ecca, "HANG"));
			break;										 
        case WALKING_LADDER:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0xc84243da, "LADDER"));
			break;										 
        case WALKING_ANIMWAIT:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x4fe6069c, "ANIMWAIT"));
			break;										 
	}

	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CRiderComponent::SetAssociatedCamera( CCompositeObject* camera_obj )
{
//	mp_camera = camera_obj;
//	Dbg_Assert(mp_camera);
//	mp_camera_component = GetWalkCameraComponentFromObject(mp_camera);
//	Dbg_MsgAssert(mp_camera_component, ("No RiderCameraComponent in camera object"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CRiderComponent::ReadyRiderState( bool to_ground_state )
{
	// setup the state in preparation for being in walking mode next object update
	MARK;
	
	// Firstly ensure that there is a valid horse nearby.
	mp_horse_component						= NULL;

	Obj::CHorseComponent* p_horse_component = static_cast( Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRC_HORSE ));
	while( p_horse_component )
	{
		Obj::CCompositeObject*	p_horse		= p_horse_component->GetObject();
		Mth::Vector				horse_pos	= p_horse->GetPos();
		float					dist		= Mth::Distance( GetObject()->GetPos(), horse_pos );
		if( dist < 60.0f )
		{
			mp_horse_component				= p_horse_component;
			break;
		}
		p_horse_component					= static_cast( p_horse_component->GetNextSameType());
	}

	if( mp_horse_component == NULL )
	{
		// Don't want to proceed.
		return false;

	}
	
	printf( "ReadyRiderState() found horse\n" );

	mp_horse_component->AcceptRiderMount( GetObject());

	// always reset the state timestamp
    m_state_timestamp = Tmr::GetTime();

	if (to_ground_state)
	{
		set_state(WALKING_GROUND);
		
		// will be incorrect for one frame
		m_ground_normal.Set(0.0f, 1.0f, 0.0);
		
		m_last_ground_feeler_valid = false;
		
		GetObject()->GetVel()[Y] = 0.0f;
	}
	else
	{
		set_state(WALKING_AIR);
		
		// set primary air direction in the direction of velocity
		m_primary_air_direction = GetObject()->GetVel();
		m_primary_air_direction[Y] = 0.0f;
		float length = m_primary_air_direction.Length();
		if (length < 0.001f)
		{
			// or facing
			m_primary_air_direction = GetObject()->GetMatrix()[Z];
			m_primary_air_direction[Y] = 0.0f;
			length = m_primary_air_direction.Length();
			if (length < 0.001f)
			{
				// or future facing
				m_primary_air_direction = -GetObject()->GetMatrix()[Y];
				m_primary_air_direction[Y] = 0.0f;
				length = m_primary_air_direction.Length();
			}
		}
		m_primary_air_direction /= length;
		
//		leave_movable_contact_for_air(GetObject()->GetVel(), GetObject()->GetVel()[Y]);
	}

	m_curb_float_height = 0.0f;
	
	m_last_frame_event = 0;

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CRiderComponent::go_on_horse_state( void )
{
	Mth::Vector pos;
	Mth::Matrix	mat, display_mat;

	pos			= mp_horse_component->GetPos();
	mat			= mp_horse_component->GetMatrix();
	display_mat	= mp_horse_component->GetDisplayMatrix();

	GetObject()->SetPos( pos );
	GetObject()->SetMatrix( mat );
	GetObject()->SetDisplayMatrix( display_mat );

	// Check for trying to dismount a horse, triggered on triangle.
	if( mp_input_component->GetControlPad().m_triangle.GetTriggered())
	{
		mp_input_component->GetControlPad().m_triangle.ClearTrigger();

		// Get the control component.
		CSkaterPhysicsControlComponent*	p_control_component = GetSkaterPhysicsControlComponentFromObject( GetObject());
		p_control_component->CallMemberFunction( CRCD( 0x82604c1e, "SkaterPhysicsControl_SwitchRidingToWalking"), NULL, NULL );

		mp_horse_component->AcceptRiderDismount( GetObject());

		m_frame_event = CRCD( 0x9b46e749, "Stand" );
		GetObject()->SelfEvent( m_frame_event );
		return;
	}

	CAnimationComponent*	p_anim_component	= GetAnimationComponentFromObject( GetObject());

	Script::CStruct* p_temp_struct = new Script::CStruct();

	// Decide which animation to play.
	switch( mp_horse_component->GetAnimation())
	{
		case CRCC(0xae7deda,"Horse_Jump"):
		{
			p_temp_struct->AddChecksum( CRCD(0x98549ba4,"Anim"), CRCD(0xe978150a,"Rider_Jump"));
			p_temp_struct->AddChecksum( (uint32)0, 0xfe09fe09/*"NoRestart"*/);
			p_anim_component->PlayAnim( p_temp_struct, NULL, 0.3f );
			break;
		}
		case CRCC( 0xa337737a, "Horse_Airidle" ):
		{
			p_temp_struct->AddChecksum( CRCD(0x98549ba4,"Anim"), CRCD(0x6600c6f9,"Rider_Airidle"));
			p_temp_struct->AddChecksum( (uint32)0, 0xfe09fe09/*"NoRestart"*/);
			p_temp_struct->AddChecksum( (uint32)0, 0x4f792e6c/*"Cycle"*/);
			p_anim_component->PlayAnim( p_temp_struct, NULL, 0.3f );
			break;
		}
		case CRCC( 0x5540d14, "Horse_Land" ):
		{
			p_temp_struct->AddChecksum( CRCD(0x98549ba4,"Anim"), CRCD(0xe6cbc6c4,"Rider_Land"));
			p_temp_struct->AddChecksum( (uint32)0, 0xfe09fe09/*"NoRestart"*/);
			p_anim_component->PlayAnim( p_temp_struct, NULL, 0.3f );
			break;
		}
		case CRCC(0x20c5a299,"Horse_Walk"):
		{
			p_temp_struct->AddChecksum( CRCD(0x98549ba4,"Anim"), CRCD(0xc35a6949,"Rider_Walk"));
			p_temp_struct->AddChecksum( (uint32)0, 0xfe09fe09/*"NoRestart"*/);
			p_temp_struct->AddChecksum( (uint32)0, 0x4f792e6c/*"Cycle"*/);
			p_anim_component->PlayAnim( p_temp_struct, NULL, 0.3f );
			break;
		}
		case CRCC(0x8a354e68,"Horse_Trot"):
		{
			p_temp_struct->AddChecksum( CRCD(0x98549ba4,"Anim"), CRCD(0x69aa85b8,"Rider_Trot"));
			p_temp_struct->AddChecksum( (uint32)0, 0xfe09fe09/*"NoRestart"*/);
			p_temp_struct->AddChecksum( (uint32)0, 0x4f792e6c/*"Cycle"*/);
			p_anim_component->PlayAnim( p_temp_struct, NULL, 0.3f );
			break;
		}
		case CRCC(0x1bee30eb,"Horse_Canter"):
		{
			p_temp_struct->AddChecksum( CRCD(0x98549ba4,"Anim"), CRCD(0x96600d53,"Rider_Canter"));
			p_temp_struct->AddChecksum( (uint32)0, 0xfe09fe09/*"NoRestart"*/);
			p_temp_struct->AddChecksum( (uint32)0, 0x4f792e6c/*"Cycle"*/);
			p_anim_component->PlayAnim( p_temp_struct, NULL, 0.3f );
			break;
		}
		case CRCC(0x2ca2c118,"Horse_Gallop"):
		{
			p_temp_struct->AddChecksum( CRCD(0x98549ba4,"Anim"), CRCD(0xa12cfca0,"Rider_Gallop"));
			p_temp_struct->AddChecksum( (uint32)0, 0xfe09fe09/*"NoRestart"*/);
			p_temp_struct->AddChecksum( (uint32)0, 0x4f792e6c/*"Cycle"*/);
			p_anim_component->PlayAnim( p_temp_struct, NULL, 0.3f );
			break;
		}
		case CRCC(0xf0ebc152,"Horse_StandIdle"):
		default:
		{
			p_temp_struct->AddChecksum( CRCD(0x98549ba4,"Anim"), CRCD(0x5b4e88ee,"Rider_StandIdle"));
			p_temp_struct->AddChecksum( (uint32)0, 0xfe09fe09/*"NoRestart"*/);
			p_temp_struct->AddChecksum( (uint32)0, 0x4f792e6c/*"Cycle"*/);
			p_anim_component->PlayAnim( p_temp_struct, NULL, 0.3f );
			break;
		}
	}

	delete p_temp_struct;
}








/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRiderComponent::go_on_ground_state (   )
{
#	if 0

	account_for_movable_contact();
	
	setup_collision_cache();
	
	// calculate initial horizontal speed
	float horizontal_speed = m_horizontal_vel.Length();
	
	calculate_horizontal_speed_and_facing(horizontal_speed);
	
	// calculate this frame's movement
	m_horizontal_vel = horizontal_speed * m_facing;
	
	// prevent movement into walls
	if (adjust_horizonal_vel_for_environment(true))
	{
		// turn to face newly adjusted velocity
		adjust_facing_for_adjusted_horizontal_vel();
	}
	
	// if we are wall pushing, we may have decided to switch states during adjust_horizonal_vel_for_environment based on our environment
	if (m_state != WALKING_GROUND || should_bail_from_frame())
	{
		CFeeler::sClearDefaultCache();
		return;
	}
	
	// apply movement for this frame
	m_pos += m_horizontal_vel * m_frame_length;
	
	// snap up and down curbs and perhaps switch to air
	respond_to_ground();
	if (m_state != WALKING_GROUND || should_bail_from_frame())
	{
		CFeeler::sClearDefaultCache();
		return;
	}
	
	adjust_curb_float_height();
	
	// insure that we do not slip through the cracks in the collision geometry which are a side-effect of moving collidable objects
	if (CCompositeObject* p_inside_object = mp_movable_contact_component->CheckInsideObjects(m_pos, m_frame_start_pos))
	{
		MESSAGE("WALKING_GROUND, within moving object");
		
		// allow it to push us forward, causing a bit of a stumble
		m_horizontal_vel = p_inside_object->GetVel();
		m_horizontal_vel[Y] = 0.0f;
		m_vertical_vel = 0.0f;
		
		float speed_sqr = m_horizontal_vel.LengthSqr();
		if (speed_sqr > (10.0f * 10.0f))
		{
			m_facing = m_horizontal_vel * (1.0f / sqrtf(speed_sqr));
		}
	}
	
	CFeeler::sClearDefaultCache();
#	endif
}


}



================================================
FILE: Code/Gel/Components/RiderComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       RiderComponent.h
//* OWNER:          Dave
//* CREATION DATE:  5/30/03
//****************************************************************************

#ifndef __COMPONENTS_RIDERCOMPONENT_H__
#define __COMPONENTS_RIDERCOMPONENT_H__

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#define		CRC_RIDER CRCD(0x15beefca,"Rider")

#define		GetRiderComponent()					((Obj::CRiderComponent*)GetComponent(CRC_RIDER))
#define		GetRiderComponentFromObject(pObj)	((Obj::CRiderComponent*)(pObj)->GetComponent(CRC_RIDER))

class CFeeler;
		
namespace Script
{
    class CScript;
    class CStruct;
}

namespace Nx
{
	class CCollCache;
}
              
namespace Obj
{
	class CAnimationComponent;
	class CModelComponent;
	class CTriggerComponent;
	class CRiderCameraComponent;
	class CSkaterPhysicsControlComponent;
	class CMovableContactComponent;
	
class CRiderComponent : public CBaseComponent
{
public:
	enum EStateType
	{
		WALKING_GROUND,
		WALKING_AIR,
		WALKING_HOP,
		WALKING_HANG,
		WALKING_LADDER,
		WALKING_ANIMWAIT
	};
	
	enum EAnimScaleSpeed
	{
		OFF,
		RUNNING,
        HANGMOVE,
		LADDERMOVE
	};
	
	enum
	{
		vNUM_FEELERS = 7,
		vNUM_BARRIER_HEIGHT_FEELERS = 7
	};
	
	typedef void (Obj::CRiderComponent::*AnimWaitCallbackPtr) (   );
	
public:
    CRiderComponent();
    virtual ~CRiderComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
	virtual void					Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	EStateType						GetState (   ) { return m_state; }
	
private:
	void							set_state ( EStateType state );
	
	void							go_on_horse_state( void );

	void							go_on_ground_state (   );
	void							calculate_horizontal_speed_and_facing ( float &horizontal_speed );
	bool							adjust_horizonal_vel_for_environment ( bool wall_push_active );
	void							adjust_facing_for_adjusted_horizontal_vel (   );
	float							adjust_desired_speed_for_slope ( float desired_speed );
	void							respond_to_ground (   );
	void							adjust_curb_float_height (   );
	void							account_for_movable_contact (   );
	void							jump ( float strength, bool hang_jump = false );
	void							go_in_air_state (   );
	void							control_horizontal_vel (   );
	void							adjust_vertical_vel_for_ceiling (   );
	void							check_for_landing ( const Mth::Vector& previous_pos, const Mth::Vector& final_pos );
	void							uber_frig (   );
	void							lerp_upright (   );
	bool							check_for_wall_push (   );
	void							leave_movable_contact_for_air ( Mth::Vector& horizontal_vel, float& vertical_vel );
	bool							maybe_hop_to_hang (   );
	bool							maybe_drop_to_hang (   );
	bool							maybe_grab_to_hang ( Mth::Vector start_pos, Mth::Vector end_pos );
	void							go_hop_state (   );
	void							go_hang_state (   );
	void							hang_movement (   );
	void							find_neighboring_hang_rail ( float movement, bool descending );
	void							set_pos_from_hang_rail_state (   );
	bool							maybe_pull_up_from_hang (   );
	bool							maybe_jump_low_barrier (   );
	bool							maybe_climb_up_ladder ( bool user_requested = false );
	bool							maybe_climb_down_ladder (   );
	bool							maybe_grab_to_ladder ( const Mth::Vector& start_pos, const Mth::Vector& end_pos );
	void							go_ladder_state (   );
	void							ladder_movement (   );
	void							off_ladder_bottom (   );
	void							off_ladder_top (   );
	void							calculate_anim_wait_facing_drift_parameters ( const Mth::Vector& goal_facing );
    void                            go_anim_wait_state (   );
    void                            anim_wait_complete (   );
	bool							maybe_stick_to_rail (   );
	void							setup_collision_cache (   );
	void							copy_state_into_object (   );
	void							get_controller_input (   );
	void							adjust_control_for_forced_run (   );
	float							calculate_desired_speed (   );
	Mth::Vector						calculate_feeler_offset_direction ( int contact );
	bool							forced_run (   );
	bool							determine_stand_pos ( const Mth::Vector& proposed_stand_pos, Mth::Vector& stand_pos, CFeeler& feeler );
	bool							should_bail_from_frame (   );
	
	void							drop_to_hang_complete (   );
	void							pull_up_from_hang_complete (   );
	void							move_to_ladder_bottom_complete (   );
	void							move_to_ladder_top_complete (   );
	void							off_ladder_bottom_complete (   );
	void							off_ladder_top_to_ground_complete (   );
	void							off_ladder_top_to_air_complete (   );
	
	static float					s_get_param ( uint32 checksum );
	
public:
	bool							IsRunning (   );
	bool							UseDPadCamera (   );
	void							SetForwardControlLock ( bool forward_control_lock );
	void							SetAssociatedCamera ( CCompositeObject* camera_obj );
    
    const Mth::Vector&              GetEffectivePos (   );
	
	bool							ReadyRiderState ( bool to_ground_state );
	
private:
	EStateType						m_state;
	
	// Time stamp of when our state last changed.
	Tmr::Time						m_state_timestamp;
	
	// State we changed from when m_state_timestamp was last set.
	EStateType						m_previous_state;
	
	// Extracted from the object at frame start and back into the object at frame end.
	Mth::Vector						m_pos;
	Mth::Vector						m_horizontal_vel;
	float							m_vertical_vel;
	Mth::Vector						m_facing;
	Mth::Vector						m_upward;
	
	// Cache our position at the frame's start
	Mth::Vector						m_frame_start_pos;
	
	// Script event reporting our current state.  Set during each frame update and sent at the end of the update.
	uint32							m_frame_event;
	
	// Last frame's frame event.
	uint32							m_last_frame_event;
	
	// Current frame's frame length.
	float							m_frame_length;
	
	// Current frame's control input.
	Mth::Vector						m_control_direction;
	float							m_control_magnitude;
	bool							m_control_pegged;
	
	struct SContact
	{
		// If this contact is in collision with the environment.
		bool						in_collision;
		
		// The horizontal normal of the collided geometry.
		Mth::Vector					normal;
		
		// Distance from the collision as a factor of the feeler length.
		float						distance;
		
		// If the collision in this direction is determined to be low enough to jump.
		bool						jumpable;
		
		// Movement of the object we're in contact with (not accounting for its rotation) along its horizontal normal.
		float						movement;
		
	// The walker looks around him in vNUM_FEELERS directions and stores the results here.  The final contact is felt for along the movement of
	// the origin, and is only used while in the air.
	} mp_contacts [ vNUM_FEELERS + 1 ];
	
	// Type of animation speed scaling to do.
	EAnimScaleSpeed					m_anim_scale_speed;
	
	// Horizontal speed for the purposes of animation speed scaling.
	float							m_anim_effective_speed;
	
	// Factor multiplied by all velocities.  Set by script.
	float							m_script_drag_factor;
	
	// Used to lock control in the forward direction (during a camera flush request)
	bool							m_forward_control_lock;
	
	// When on ground, the walker is often floated above the ground in an attempt to smooth out stair/curb climbs.
	float							m_curb_float_height;
	
	// If m_curb_float_height was adjusted this frame.  If not, we lerp it to zero.
	bool							m_curb_float_height_adjusted;
	
	// The collision cache used.
	Nx::CCollCache*					mp_collision_cache;
	
	// Associated camera.
	CCompositeObject*				mp_camera;
	CRiderCameraComponent*			mp_camera_component;
	CHorseComponent*				mp_horse_component;		// This points to the CHorseComponent in the separate CCompositeObject that is the horse being ridden.
	
    // If associated with a rail, this is the rail node and rail manager.
    const CRailNode*              	mp_rail_node;
    CRailManager*                   mp_rail_manager;
	
	// Peer components.
	CInputComponent*				mp_input_component;
	CAnimationComponent*			mp_animation_component;
	CModelComponent*				mp_model_component;
	CTriggerComponent*				mp_trigger_component;
	CSkaterPhysicsControlComponent*	mp_physics_control_component;
	CMovableContactComponent*		mp_movable_contact_component;
    
	/////////////////////////////////
	// WALKING_GROUND state variables
	
	// Normal of the ground on which we are standing.  Only meaningful when on the ground.  Set the previous frame.
	Mth::Vector						m_ground_normal;
	
	// Certain things such as rotation rate and camera lerp are increased when this is set.
	bool							m_run_toggle;
	
	struct SWallPushTest
	{
		// Whether we currently testing.
		bool						active;
		
		// Time at which this test began.
		Tmr::Time					test_start_time;
		
	// Data used to detect when a player is pushing into a wall in order to climb it.
	}								m_wall_push_test;
	
	// When doing anim speed scaling when running, this is set to the run speed which corresponds to the current animation playing at standard speed.
	float							m_anim_standard_speed;
	
	// A copy of the latest feeler which contacted the groud under us.
	CFeeler							m_last_ground_feeler;
	bool							m_last_ground_feeler_valid;
	
	/////////////////////////////////
	// WALKING_AIR state variables
	
	// The forward horizontal direction of the jump which, when coming out of skating or because of in-air velocity control, diverges from the direction of velocity
	Mth::Vector						m_primary_air_direction;
	
	// This term of our horizontal velocity is due to leaping from a movable contact, and is not adjustable via in-air control.
	Mth::Vector						m_uncontrollable_air_horizontal_vel;
	
	/////////////////////////////////
	// WALKING_HANG state variables
	
	// Measures our position along the current rail as a parameter running from 0.0 to 1.0.
	float							m_along_rail_factor;
	
	// Measures the hang position offset from the actual rail we are hanging from.
	float							m_vertical_hang_offset;
	float							m_horizontal_hang_offset;
	
	// Checksum describing the type of initial hang animation to play.
	uint32							m_initial_hang_animation;
    
	/*
	/////////////////////////////////
	// WALKING_HOP state variables
	
    // The position we will have at the end of the hop.
    Mth::Vector                     m_goal_hop_pos;
    
    // The facing we will have at the end of the hop.
    Mth::Vector                     m_goal_hop_facing;
	
	// Hang state variable.  Set at hop start.
	// float						m_along_rail_factor;
	
	// Hang state variable.  Set at hop start.
	// uint32							m_initial_hang_animation;
	*/
    
	/////////////////////////////////
	// WALKING_LADDER state variables
	
	// Shared with hang state.
	// float						m_along_rail_factor;
    
	/////////////////////////////////
	// WALKING_ANIMWAIT state variables
	
	// Position at the start of the animation wait.
	Mth::Vector						m_anim_wait_initial_pos;
	
	// Facing at the start of the aniamtion wait.
	Mth::Vector						m_anim_wait_initial_facing;
	
    // Amount to move the object at the end of the animation wait.
    Mth::Vector                     m_anim_wait_goal_pos;
	
	// Goal facing of the position drift.
	Mth::Vector						m_drift_goal_facing;
	
	// The angle over which the facing must drift.
	float							m_drift_angle;
    
	// Accumulates position offset due to a movable contact during an animation wait.
	Mth::Vector						m_offset_due_to_movable_contact;
	
	// The behavior of the camera during the animation wait.
	enum EAnimWaitCameraModeType			
	{
		AWC_CURRENT,
		AWC_GOAL
	}								m_anim_wait_camera_mode;
	
	// Callback function to call upon completion of the animation wait.
	AnimWaitCallbackPtr				mp_anim_wait_complete_callback;
	
	// WALKING_GROUND state variable sometimes set at start of animation wait based on the animation end ground situation
	// Mth::Vector					m_ground_normal;
	// CFeeler						m_last_ground_feeler;
	// bool							m_last_ground_feeler_valid;
	
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CRiderComponent::set_state ( EStateType state )
{
    if (state == m_state) return;
	
	m_previous_state = m_state;
    
    if (m_state == WALKING_GROUND)
    {
        m_wall_push_test.active = false;
		m_run_toggle = false;
    }
	
	m_state = state;
	m_state_timestamp = Tmr::GetTime();
	
	if (m_state == WALKING_ANIMWAIT)
	{
		m_offset_due_to_movable_contact.Set();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float CRiderComponent::s_get_param ( uint32 checksum )
{
	Script::CStruct* p_walk_params = Script::GetStructure(CRCD(0x6775e538, "walk_parameters"));
	Dbg_Assert(p_walk_params);
	
	float param;
	p_walk_params->GetFloat(checksum, ¶m, Script::ASSERT);
	return param;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CRiderComponent::forced_run (   )
{
	return false;
	if (!mp_input_component->GetControlPad().m_x.GetPressed()) return false;
	
	uint32 pressed_time = mp_input_component->GetControlPad().m_x.GetPressedTime();
	return pressed_time > m_state_timestamp || pressed_time > s_get_param(CRCD(0xa7f5ba13, "forced_run_delay"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CRiderComponent::should_bail_from_frame (   )
{
	// for now, all restarts switch us to skating; so, only bail from frame if we're ever switch out of walking via script
//	return !mp_physics_control_component->IsWalking();
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CRiderComponent::IsRunning (   )
{
	return m_run_toggle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CRiderComponent::SetForwardControlLock ( bool forward_control_lock )
{
	m_forward_control_lock = forward_control_lock;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector& CRiderComponent::GetEffectivePos (   )
{
    if (m_state != WALKING_ANIMWAIT)
    {
        return GetObject()->GetPos();
    }
    else
    {
        return GetObject()->GetPos();
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CRiderComponent::UseDPadCamera (   )
{
	
	return mp_input_component->GetControlPad().m_leftX == 0.0f && mp_input_component->GetControlPad().m_leftY == 0.0f && m_control_pegged;
}

}

#endif


================================================
FILE: Code/Gel/Components/SetDisplayMatrixComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SetDisplayMatrixComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  8/6/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSetDisplayMatrixComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSetDisplayMatrixComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSetDisplayMatrixComponent::CSetDisplayMatrixComponent() : CBaseComponent()
{
	SetType( CRC_SETDISPLAYMATRIX );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSetDisplayMatrixComponent::~CSetDisplayMatrixComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSetDisplayMatrixComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSetDisplayMatrixComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSetDisplayMatrixComponent::Update()
{
	GetObject()->SetDisplayMatrix(GetObject()->GetMatrix());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSetDisplayMatrixComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSetDisplayMatrixComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSetDisplayMatrixComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
}
	
}


================================================
FILE: Code/Gel/Components/SetDisplayMatrixComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SetDisplayMatrixComponent.h
//* OWNER:          Dan
//* CREATION DATE:  8/6/3
//****************************************************************************

#ifndef __COMPONENTS_SETDISPLAYMATRIXCOMPONENT_H__
#define __COMPONENTS_SETDISPLAYMATRIXCOMPONENT_H__

#include 
#include 

#include 

#define		CRC_SETDISPLAYMATRIX CRCD(0x1bbf844d,"SetDisplayMatrix")

#define		GetSetDisplayMatrixComponent() ((Obj::CSetDisplayMatrixComponent*)GetComponent(CRC_SETDISPLAYMATRIX))
#define		GetSetDisplayMatrixComponentFromObject(pObj) ((Obj::CSetDisplayMatrixComponent*)(pObj)->GetComponent(CRC_SETDISPLAYMATRIX))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CSetDisplayMatrixComponent : public CBaseComponent
{
public:
    CSetDisplayMatrixComponent();
    virtual ~CSetDisplayMatrixComponent();

public:
	virtual void					InitFromStructure( Script::CStruct* pParams );
	virtual void					RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Update();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
};

}

#endif


================================================
FILE: Code/Gel/Components/SkaterCameraComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SkaterCameraComponent.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/21/03
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 


namespace Obj
{

const float SKATERCAMERACOMPONENT_LEAN_TO_SKATERCAM_LEAN	= (( Mth::PI / 6.0f ) / -4096.0f );
	
const float	SKATERCAMERACOMPONENT_INTERPOLATOR_TIMESTEP		= ( 1.0f / 60.0f );

// This is the value that mAbove should tend to when the behind value is closer than mBehind.

const float VERT_AIR_LANDED_TIME	= 10.0f / 60.0f;
const float TILT_MAX				= 0.34907f;						// 20 degees.
const float TILT_MIN				= -0.34907f;					// -20 degees.
const float TILT_INCREMENT			= 2.0f * ( TILT_MAX / 60.0f );	// 40 degrees/second.
const float TILT_DECREMENT			= 2.0f * ( TILT_MAX / 60.0f );	// 40 degrees/second.
const float TILT_RESTORE			= 8.0f * ( TILT_MAX / 60.0f );	// 160 degrees/second.
	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void _xrotlocal ( Mth::Matrix& m, float a )
{
	m.RotateXLocal( a );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent* CSkaterCameraComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterCameraComponent );	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSkaterCameraComponent::CSkaterCameraComponent() : CBaseComponent()
{
	SetType( CRC_SKATERCAMERA );
	
	mMode = SKATERCAM_MODE_UNDEFINED;
				  
	// Create the SLERP interpolator here.
	mpSlerp				= new Mth::SlerpInterpolator;

	// Set current zoom and LERP rates.
	mCurrentZoom		= 1.0f;
	mZoomLerp			= 0.0625f;
	mLastDot			= 1.0f;

	mLerpXZ				= 0.25f;
	mLerpY				= 0.5f;
	mVertAirLerpXZ		= 1.0f;
	mVertAirLerpY		= 1.0f;
	mGrindLerp			= 0.1f;
	
	mpSkater= NULL;
	mpSkaterStateComponent = NULL;
	mpSkaterPhysicsControlComponent = NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSkaterCameraComponent::~CSkaterCameraComponent()
{
	delete mpSlerp;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::Finalize (   )
{
	mp_lookaround_component = GetCameraLookAroundComponentFromObject(GetObject());
	
	Dbg_Assert(mp_lookaround_component);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::Enable( bool enable )
{
	CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject());
	Dbg_MsgAssert( p_cam_comp, ( "SkaterCameraComponent requires CameraComponent attached to parent" ));
	p_cam_comp->Enable( enable );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Gfx::Camera* CSkaterCameraComponent::GetCamera( void )
{
	CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject());
	Dbg_MsgAssert( p_cam_comp, ( "SkaterCameraComponent requires CameraComponent attached to parent" ));
	return p_cam_comp->GetCamera();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector& CSkaterCameraComponent::GetPosition( void ) const 
{
	CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject());
	Dbg_MsgAssert( p_cam_comp, ( "SkaterCameraComponent requires CameraComponent attached to parent" ));
	return p_cam_comp->GetPosition();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::SetPosition( Mth::Vector& pos )
{
	CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject());
	Dbg_MsgAssert( p_cam_comp, ( "SkaterCameraComponent requires CameraComponent attached to parent" ));
	p_cam_comp->SetPosition( pos );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Matrix& CSkaterCameraComponent::GetMatrix( void ) const
{
	CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject());
	Dbg_MsgAssert( p_cam_comp, ( "SkaterCameraComponent requires CameraComponent attached to parent" ));
	return p_cam_comp->GetMatrix();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::InitFromStructure( Script::CStruct* pParams )
{

// NOT We need a CCameraComponent attached in order to get camera details.
//	CCameraComponent *p_cam_comp	= GetCameraComponentFromObject( GetObject());
//	Dbg_MsgAssert( p_cam_comp, ( "CSkaterCameraComponent needs CCameraComponent attached to parent" ));

	// Clear the frame matrix, so that future slerps make sense.
	//Mth::Matrix& frame_matrix		= p_cam_comp->GetMatrix();
	Mth::Matrix& frame_matrix		= GetObject()->GetMatrix();
	frame_matrix.Ident();

	mLastActualRight 				= frame_matrix[X];
	mLastActualUp 					= frame_matrix[Y];
	mLastActualAt 					= frame_matrix[Z];


	// The camera requires a "CameraTarget" checksum parameter
	uint32	target_id = 0 ;
	pParams->GetChecksum("CameraTarget", &target_id, true);

	Dbg_MsgAssert(target_id >=0 && target_id <= 15,("Bad CameraTarget 0x%x in SkaterCameraComponent",target_id));

	CSkater* p_skater = static_cast(CCompositeObjectManager::Instance()->GetObjectByID(target_id)); 
	Dbg_MsgAssert(p_skater,("Can't find skater %d in SkaterCameraComponent",target_id));

		
	SetSkater(p_skater);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Reinitialise.
	InitFromStructure( pParams );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::Commit( void )
{
	// Under the normal operation of components, the CSkaterCameraComponent updates the values
	// in CCameraComponent during its Update() function. The CCameraComponent in turn will set
	// the Gfx::CCamera values during it's Update() function.
	// In some cases however, we need the camera changes to be committed to the Gfx::Camera
	// immediately, usually when logic is paused, and the component Update()'s are not taking place.
	CCameraComponent *p_cam_component = GetCameraComponentFromObject( GetObject());
	if( p_cam_component )
	{
		p_cam_component->Update();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCameraComponent::ReadyForActivation ( const SCameraState& state )
{
	Dbg_MsgAssert(mpSkater, ("Skater camera (%s) has NULL target", Script::FindChecksumName(GetObject()->GetID())));
	
	mLastActualRight = state.lastActualMatrix[X];
	mLastActualUp = state.lastActualMatrix[Y];
	mLastActualAt = state.lastActualMatrix[Z];
	mLastTripodPos = state.lastTripodPos;
	mLastDot = state.lastDot;
	mCurrentZoom = state.lastZoom;
	mVertAirLandedTimer = 0.0f;
	mInstantCount = 0;
	
	mp_lookaround_component->mLookaroundHeading = 0.0f;
	mp_lookaround_component->mLookaroundLock = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCameraComponent::Update( void )
{
	if( mpSkater == NULL )
	{
		return;
	}
	
	// optimization KLUDGE
	if (mpSkaterPhysicsControlComponent && mpSkaterPhysicsControlComponent->IsDriving())
	{
		GetObject()->Pause(true);
		return;
	}
	
	// We need a CCameraComponent attached in order to get camera details.
	CCameraComponent *p_cam_comp = GetCameraComponentFromObject( GetObject());
	Dbg_MsgAssert( p_cam_comp, ( "CSkaterCameraComponent needs CCameraComponent attached to parent" ));

	// This used to be passed in as a param for the old CSkaterCam::Update().
	// Now we get it from a flag in the skater
	bool instantly = false;
	
	Dbg_Assert( mpSkater );
	if (mpSkater->HasTeleported())
	{
		instantly = true;
	}

	if( instantly )
	{
		mInstantCount = 3;
	}

	if( mInstantCount > 0 )
	{
		--mInstantCount;
		instantly = true;
	}

		
//	if (instantly)
//	{
//		printf ("%d: Instantly!\n",(int)Tmr::GetRenderFrame());
//	}
		
	// Length of last frame in seconds.
	float delta_time = Tmr::FrameLength();

	// Update any values we are currently interpolating.
	UpdateInterpolators();

	// Would have used a SetPos() function, but the camera update in rwviewer.cpp is spread out all over the place,
	// so it's not easy to do. We'll just store the old position before updating anything...
//	Gfx::Camera::StoreOldPos();
	p_cam_comp->StoreOldPosition();

//	Mth::Matrix& frame_matrix	= Gfx::Camera::GetMatrix();
	Mth::Matrix& frame_matrix	= p_cam_comp->GetMatrix();

	frame_matrix[X]   		= mLastActualRight;
	frame_matrix[Y]			= mLastActualUp;
	frame_matrix[Z]			= mLastActualAt;

	Mth::Vector	current_at_vector( frame_matrix[Z][X], frame_matrix[Z][Y], frame_matrix[Z][Z] ); 

	// Obtain skater state.
	EStateType	state			= mpSkaterStateComponent->GetState();
	Mth::Matrix target;

	if( state == WALL )
	{
#		ifdef	DEBUG_CAMERA
		printf ("SkaterCam Using mLastPreWallSkaterMatrix, as wallriding\n");
#		endif
		target						= mLastPreWallSkaterMatrix;
	}
	else
	{
#		ifdef	DEBUG_CAMERA
		printf ("SkaterCam Using GetCameraDisplayMatrix\n");
#		endif
		// target						= static_cast< CCompositeObject* >(mpSkater)->GetDisplayMatrix();
		if (mpSkater->IsLocalClient())
		{
			target						= mpSkater->GetDisplayMatrix();
		}
		else
		{
			target						= static_cast< CCompositeObject* >(mpSkater)->GetDisplayMatrix();
		}
		mLastPreWallSkaterMatrix	= target;
	}

	// Lerp towards the required lean.
	float cam_lean = 0.0f;
	if( state == RAIL && mpSkater->IsLocalClient())
	{		
		// Need to translate the lean value [-4096, 4096] into a reasonable camera lean angle.
		cam_lean = GetSkaterBalanceTrickComponentFromObject(mpSkater)->mGrind.mManualLean * SKATERCAMERACOMPONENT_LEAN_TO_SKATERCAM_LEAN;
	}

	if( cam_lean != mLean )
	{
		if( instantly )
		{
			mLean = cam_lean;
		}
		else
		{
			mLean += (( cam_lean - mLean ) * mGrindLerp );
		}
	}
		
	Mth::Vector skater_up;
					
	if (state != GROUND)
	{
		skater_up	= target[Y];
	}
	else
	{
		skater_up	= mpSkaterStateComponent->GetCameraDisplayNormal();
	}

//	Gfx::AddDebugLine( p_skater->m_pos, ( target[X] * 72.0f ) + p_skater->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 4 );
//	Gfx::AddDebugLine( p_skater->m_pos, ( target[Y] * 72.0f ) + p_skater->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 4 );
//	Gfx::AddDebugLine( p_skater->m_pos, ( target[Z] * 72.0f ) + p_skater->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 );
		
	// Use vel for forward facing, if moving in xz plane.
	Mth::Vector vel_xz( mpSkater->GetVel().GetX(), 0.0f, mpSkater->GetVel().GetZ());

	bool use_vert_cam = UseVertCam();
	
	// use velocity if physics not paused, and if we are moving fast enough, or if we are doing vert cam
	// or if we are doing spine physics
	if(
		!(mpSkaterPhysicsControlComponent && mpSkaterPhysicsControlComponent->IsPhysicsSuspended()) && (vel_xz.Length() > 0.1f || use_vert_cam  || mpSkaterStateComponent->GetFlag( SPINE_PHYSICS ))
	   )
	{
		if( use_vert_cam )
		{
			// If it's vert, then set target direction to near straight down.
			target[Z].Set( 0.0f, -1.0f, 0.0f );

			// Zero lookaround when in vert air (and not in lookaround locked or override mode, or doing a lip trick).
			if( !mp_lookaround_component->mLookaroundLock && !mp_lookaround_component->mLookaroundOverride && ( state != LIP ))
			{
				mp_lookaround_component->mLookaroundTilt		= 0.0f;
				mp_lookaround_component->mLookaroundHeading	= 0.0f;
			}
		}
		else
		{
			target[Z]		= mpSkater->GetVel();
			
#			if 1	// for use with non-straight spine transfer physics
			if( mpSkaterStateComponent->GetFlag( SPINE_PHYSICS ))
			{
				// Just use the up velocity, plus the velocity over the spine will be straight down when coming down the other side.
				target[Z][X] = 0.0f;
				target[Z][Z] = 0.0f;
				target[Z] += mpSkaterStateComponent->GetSpineVel();
			}
#			endif

			target[Z].Normalize();

//			printf ("Using Velocity (%f, %f, %f)\n", target[Z][X],target[Z][Y], target[Z][Z]);

//			Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 128, 128, 128 ), MAKE_RGB( 128, 128, 128 ), 4 );

			// Flatten out the velocity component perpendicular to the ground. However, if the skater is in the air, the
			// m_last_normal will contain the last ground normal, which we don't want, so set it to be 'up'.
			float			dot;
			Mth::Vector		normal;

			if( state == AIR )
			{
				// Patch for spine physics
				// only reduce component perpendicular to plane by 60%
				// so we still get some up/down camera movmeent 
				// only do this when moving down though, otherwise it looks poo
				if (mpSkaterStateComponent->GetFlag(SPINE_PHYSICS) && target[Z][Y] < 0.0f)
				{
					normal.Set( 0.0f, 1.0f, 0.0f );
					dot =  0.7f * Mth::DotProduct(target[Z],normal);	// change this to 0.8 to look down when coming down other side of a spine
				}
				else
				{
					normal.Set( 0.0f, 1.0f, 0.0f );
					dot		= target[Z].GetY();
				}
				// Set world up as the target vector.
				target[Y].Set( 0.0f, 1.0f, 0.0f );
			}
			else if( state == WALL )
			{
				normal.Set( 0.0f, 1.0f, 0.0f );
				dot		= target[Z].GetY();
			}
			else
			{
				normal	= mpSkaterStateComponent->GetCameraCurrentNormal();
				dot =  0.8f * Mth::DotProduct(target[Z],normal);
			}
			
			target[Z] -= normal * dot;
			target[Z].Normalize();
			
//			Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 256, 256, 256 ), MAKE_RGB( 256, 256, 256 ), 4 );
		}

		// We need to orthonormalize in a specific order, as when the velocity is going backwards, then 
		// the X orient will be wrong, and so plonk the skater upside down.
		target[X]			= Mth::CrossProduct( target[Y], target[Z] );
		target[X].Normalize();
		
		target[Y]			= Mth::CrossProduct( target[Z], target[X] );
		target[Y].Normalize();

//		Gfx::AddDebugLine( mpSkater->m_pos, ( target[X] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 4 );
//		Gfx::AddDebugLine( mpSkater->m_pos, ( target[Y] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 4 );
//		Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 );
	}
	
	if (mpSkater->IsLocalClient() && mpSkaterStateComponent->GetFlag(IN_ACID_DROP))
	{
		CSkaterCorePhysicsComponent* pSkaterPhysicsComponent = GetSkaterCorePhysicsComponentFromObject(mpSkater);
		Dbg_Assert(pSkaterPhysicsComponent);
		
		target = pSkaterPhysicsComponent->m_acid_drop_camera_matrix;
	}
							
	// Tilt further if in regular air (going off a big jump, for example), or doing a lip trick.
	if( state == AIR )
	{
		mTiltAddition += TILT_INCREMENT * Tmr::FrameRatio();
		if( mTiltAddition > TILT_MAX )
		{
			mTiltAddition = TILT_MAX;
		}				
	}
	else if( mTiltAddition > 0.0f )
	{
		mTiltAddition -= TILT_RESTORE * Tmr::FrameRatio();
		if( mTiltAddition < 0.0f )
		{
			mTiltAddition = 0.0f;
		}				
	}
	else if( mTiltAddition < 0.0f )
	{
		mTiltAddition += TILT_RESTORE * Tmr::FrameRatio();
		if( mTiltAddition > 0.0f )
		{
			mTiltAddition = 0.0f;
		}				
	}

	// PJR: Needed to do this to fix a compiler crash on NGC. Hopefully SN will
	// fix this & I can revert this hack...
	target.RotateYLocal( mp_lookaround_component->mLookaroundHeading + mp_lookaround_component->mLookaroundHeadingStartingPoint );

	// Now tilt the matrix down a bit.
	if( state == LIP )
	{
//		target.RotateYLocal( 0.8f );
//		target.RotateXLocal( mLipTrickTilt + mTiltAddition );
		_xrotlocal ( target, mLipTrickTilt + mTiltAddition );
	}					
	else if( !(mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT )  && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS) ) /*|| !mpSkaterStateComponent->GetFlag(IN_ACID_DROP)*/)
	{
//		target.RotateXLocal( mTilt + mTiltAddition );
		_xrotlocal ( target, mTilt + mTiltAddition );
	}
	
	// Now adjust for 'lookaround'.
//	target.RotateXLocal( mLookaroundTilt );
	_xrotlocal( target, mp_lookaround_component->mLookaroundTilt );
	
	static float lip_rotate = 0.0f;

	if( state == LIP )
	{
		lip_rotate = Mth::PI / 2.0f;

		if( mLipSideDecided == 0 )
		{
			target.RotateY( lip_rotate );

			// Do collision check.
			CFeeler feeler;

			Mth::Vector horiz_z( target[Z][X], 0.0f, target[Z][Z] );
			horiz_z.Normalize();
			Mth::Vector a = mpSkater->m_pos - ( target[Z] * 3.0f );
			Mth::Vector b = a - ( horiz_z * 72.0f );

			feeler.SetIgnore(IGNORE_FACE_FLAGS_1, IGNORE_FACE_FLAGS_0);
			bool collision = feeler.GetCollision(a, b, true);
			if( !collision )
			{
				// Side 1 is fine.
				mLipSideDecided = 1;
			}
			else
			{
				float dist = feeler.GetDist();
				target.RotateY( -lip_rotate * 2.0f );
				horiz_z.Set( target[Z][X], 0.0f, target[Z][Z] );
				horiz_z.Normalize();
				a = mpSkater->m_pos - ( target[Z] * 3.0f );
				b = a - ( horiz_z * 72.0f );

				collision = feeler.GetCollision(a, b, true);
				if( !collision )
				{
					// Side 2 is fine.
					mLipSideDecided = 2;
				}
				else
				{
					if( feeler.GetDist() < dist )
					{
						// Side 2 is better than side 1.
						mLipSideDecided = 2;
					}
					else
					{
						// Side 1 is better than side 2.
						target.RotateY( lip_rotate * 2.0f );
						mLipSideDecided = 1;
					}
				}
			}
		}
		else
		{
			if( mLipSideDecided == 1 )
			{
				target.RotateY( lip_rotate );
			}
			else
			{
				target.RotateY( -lip_rotate );
			}
		}
	}
	else
	{
		mLipSideDecided = 0;
	}					

	// Set skater flag to indicate when the liptrick camera has spun to the opposite side.
	if( mpSkater && ( mLipSideDecided == 2 ))
	{
		mpSkater->mScriptFlags |= ( 1 << Script::GetInteger( 0x16b8e4cb /* "FLAG_SKATER_LIPTRICK_CAM_REVERSED" */ ));
	}
	else
	{
		mpSkater->mScriptFlags &= ~( 1 << Script::GetInteger( 0x16b8e4cb /* "FLAG_SKATER_LIPTRICK_CAM_REVERSED" */ ));
	}
	
//	Gfx::AddDebugLine( mpSkater->m_pos, ( target[X] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 4 );
//	Gfx::AddDebugLine( mpSkater->m_pos, ( target[Y] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 4 );
//	Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 );
	
	// Why doing this, when setting below?
	Mth::Matrix t_matrix(0.0f, 0.0f, 0.0f);
	
	// Since camera points in -Z, but player in +Z, we must negate the X and Z axes
	t_matrix[X][X]		= -target[0][0];
	t_matrix[X][Y]		= -target[0][1];
	t_matrix[X][Z]		= -target[0][2];
	t_matrix[X][W]		= 0.0f;
	
	t_matrix[Y][X]		= target[1][0];
	t_matrix[Y][Y]		= target[1][1];
	t_matrix[Y][Z]		= target[1][2];
	t_matrix[Y][W]		= 0.0f;
	
	t_matrix[Z][X]		= -target[2][0];
	t_matrix[Z][Y]		= -target[2][1];
	t_matrix[Z][Z]		= -target[2][2];
	t_matrix[Z][W]		= 0.0f;
			
	t_matrix[W][X]		= 0.0f;
	t_matrix[W][Y]		= 0.0f;
	t_matrix[W][Z]		= 0.0f;
	t_matrix[W][W]		= 1.0f;
        
	frame_matrix[W][X]	= 0.0f;
	frame_matrix[W][Y]	= 0.0f;
	frame_matrix[W][Z]	= 0.0f;
	frame_matrix[W][W]	= 1.0f;

	// if we want an instant update, then just move it
	if (instantly)
	{
		frame_matrix = t_matrix;
	}

	float dotx = Mth::DotProduct( t_matrix[X], frame_matrix[X] );
	float doty = Mth::DotProduct( t_matrix[Y], frame_matrix[Y] );
	float dotz = Mth::DotProduct( t_matrix[Z], frame_matrix[Z] );

	if( dotx > CAMERA_SLERP_STOP && doty > CAMERA_SLERP_STOP && dotz > CAMERA_SLERP_STOP )
	{
		// Do nothing, camera has reached the target so we just leave the frame matrix exactly where it is.
//	    frame_matrix = t_matrix;
	}
	else
	{
		// Initialise the SLERP interpolator, with the current matrix as the start, and the ideal
		// matrix as the end (target).

		// GARY:  is this right?  i assume p_matrix = &t_matrix
		mpSlerp->setMatrices( &frame_matrix, &t_matrix );

		// Copy flags and shit (why copying all this when most of the values will be overwritten?)
		Mth::Matrix stored_frame;
		stored_frame = frame_matrix;
		frame_matrix = t_matrix;			

		// Continue to slerp towards the target, updating frame_matrix with the slerped values.
		if( !(mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS)))
		{
			// If the skater has just landed from big air, use the big air land slerp value.
			if(( mpSkaterStateComponent->GetState() == GROUND ) && ( mVertAirLandedTimer >= delta_time ))
			{
				mVertAirLandedTimer -= delta_time;
				mpSlerp->getMatrix( &frame_matrix, GetTimeAdjustedSlerp( mVertAirLandedSlerp, delta_time ));
			}
			else
			{
				// Clear the vert air landed timer.
				mVertAirLandedTimer = 0.0f;
				
				mpSlerp->getMatrix( &frame_matrix, GetTimeAdjustedSlerp( mSlerp, delta_time ));

				// At this point we can check to see the angle that the camera's 'at' vector moved through this frame.
				// If this angle is too large, we need to limit it, regardless of the slerp. (This is primarily to avoid
				// the nasty jerk when transitioning rails etc.
				float this_dot = ( current_at_vector.GetX() * frame_matrix[Mth::AT][X] ) 
                       + ( current_at_vector.GetY() * frame_matrix[Mth::AT][Y] ) 
                       + ( current_at_vector.GetZ() * frame_matrix[Mth::AT][Z] ); 

				// Dot values will be in the range [0,1], with 1 being no change.
				if( this_dot < ( mLastDot * 0.9998f ))
				{
					// The angular change this frame is too big. Need to recalculate with an adjusted slerp value.
					float new_slerp = mSlerp * ( this_dot / ( mLastDot * 0.9998f ));
					mpSlerp->setMatrices( &stored_frame, &t_matrix );
					mpSlerp->getMatrix( &frame_matrix, GetTimeAdjustedSlerp( new_slerp, delta_time ));

					this_dot = mLastDot * 0.9998f;
				}
				mLastDot = this_dot;
			}
		}
		else
		{
			mpSlerp->getMatrix( &frame_matrix, GetTimeAdjustedSlerp( mVertAirSlerp, delta_time ));

			// Set the vert air landed timer to the max, since the skater is in vert air.
			mVertAirLandedTimer = VERT_AIR_LANDED_TIME;
		}						
	}		

	// Mick:  This is a patch to allow the walk camera to override the updating of the camera frame	
	// At this point, frame_matrix is valid to store.
	mLastActualRight	= frame_matrix[X];
	mLastActualUp		= frame_matrix[Y];
	mLastActualAt		= frame_matrix[Z];
	
	// Set camera position to be the same as the skater.
	Mth::Vector	cam_pos = GetTripodPos( instantly );
	
	// If in the air doing a trick, we slowly reduce the behind value to 'zoom' in on the skater.
	float above, behind;
	if( instantly )
	{
		float temp = mZoomLerp;
		mZoomLerp = 1.0f;					// fully lerp instantly
		CalculateZoom( &above, &behind );
		mZoomLerp = temp;
	}
	else
	{
		CalculateZoom( &above, &behind );
	}
	
	// Note we use the camera's at vector, as this will keep the skater in the middle of the frame.
	//cam_pos -= frame_matrix[Z] * behind;
	cam_pos += frame_matrix[Z] * behind;
		
	// Move camera along the Skater's up vector.
	cam_pos += skater_up * above;
	
	Mth::Vector	actual_focus_pos = mpSkater->m_pos + ( skater_up * above );
	
//	Gfx::AddDebugLine( mpSkater->m_pos, actual_focus_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 );
													  
//	Dbg_Message("Matrix Z  (%5.4f,%5.4f,%5.4f)", frame_matrix[Z][X], frame_matrix[Z][Y], frame_matrix[Z][Z]);
//	Dbg_Message("Matrix UP (%5.4f,%5.4f,%5.4f)", skater_up[X], skater_up[Y], skater_up[Z]);
//	Dbg_Message("Above %f Behind %f", above, behind);
//	Dbg_Message("focus pos (%5.1f,%5.1f,%5.1f)", actual_focus_pos[X], actual_focus_pos[Y], actual_focus_pos[Z]);
//	Dbg_Message("Camera after: pos (%5.1f,%5.1f,%5.1f)", cam_pos[X], cam_pos[Y], cam_pos[Z]);

	// Reorient the camera to look directly at the skater. We don't want to look at the interpolated position,
	// since this can lead to nasty jerkiness, especially on rails etc.
	target[Z].Set( actual_focus_pos.GetX() - cam_pos.GetX(), actual_focus_pos.GetY() - cam_pos.GetY(), actual_focus_pos.GetZ() - cam_pos.GetZ());
	target[Z].Normalize();

	// Read back the Y from the current matrix.
	target[Y][0]	= frame_matrix[Y][X];
	target[Y][1]	= frame_matrix[Y][Y];
	target[Y][2]	= frame_matrix[Y][Z];

	// Generate new orthonormal X and Y axes.
	target[X]		= Mth::CrossProduct( target[Y], target[Z] );
	target[X].Normalize();
	target[Y]		= Mth::CrossProduct( target[Z], target[X] );
	target[Y].Normalize();

	// Here is where lean may safely be applied without moving the focus position on screen.
	if( mLean != 0.0f )
	{
		target[Y].Rotate( target[Z], mLean );
		target[X].Rotate( target[Z], mLean );
	}
	
	// Write back into camera matrix.
	// Since camera points in -Z, but player in +Z, we must negate the X and Z axes
	frame_matrix[X][X]		= -target[0][0];
	frame_matrix[X][Y]		= -target[0][1];
	frame_matrix[X][Z]		= -target[0][2];
	frame_matrix[X][W]		= 0.0f;

	frame_matrix[Y][X]		= target[1][0];
	frame_matrix[Y][Y]		= target[1][1];
	frame_matrix[Y][Z]		= target[1][2];
	frame_matrix[Y][W]		= 0.0f;

	frame_matrix[Z][X]		= -target[2][0];
	frame_matrix[Z][Y]		= -target[2][1];
	frame_matrix[Z][Z]		= -target[2][2];
	frame_matrix[Z][W]		= 0.0f;

	// Update the position if there is a shake active.
	if(	mShakeDuration > 0.0f )
	{
		addShake( cam_pos, frame_matrix );
	}
		
	// Now do collision detection.
	
	bool no_camera_collision;
	if (mpSkater->IsLocalClient())
	{
		CSkaterCorePhysicsComponent* pSkaterPhysicsComponent = GetSkaterCorePhysicsComponentFromObject(mpSkater);
		Dbg_Assert(pSkaterPhysicsComponent);
		
		no_camera_collision = pSkaterPhysicsComponent->mp_movable_contact_component->GetContact() && state == LIP;
	}
	else
	{
		no_camera_collision = false;
	}
	
	if (no_camera_collision)
	{
		// no camera collision if doing a lip trick on a moving object
		//printf ("Lip trick on moving object\n");
	}
	else
	{
		ApplyCameraCollisionDetection(cam_pos, frame_matrix, cam_pos - ((Mth::Vector)( frame_matrix[Z] )) * behind, actual_focus_pos);
	}
	
	cam_pos[W] = 1.0f;
//	Gfx::Camera::SetPos( cam_pos );
	p_cam_comp->SetPosition( cam_pos );
	
	// reset old position if in instant update
	if (instantly)
	{
		p_cam_comp->StoreOldPosition();
	}
	
//	Dbg_Message("Camera Final: pos (%5.1f,%5.1f,%5.1f)", cam_pos[X], cam_pos[Y], cam_pos[Z]);

	// Store the position of the camera in CSkaterCam also (used for proximity nodes).

	// Dave - there is no m_pos anymore...
//	m_pos = cam_pos;

	// Dave - this used to return the value below when it was CSkaterCam::Update()
//	Mth::Vector	to_target = at_pos - cam_pos;
//	return to_target.Length();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterCameraComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | SC_ShakeCamera | shake the skater camera
        // @parm float | duration | duration of the shake (0 clears the shake)
        // @parm float | vert_amp | maximum vertical amplidtude of the shake
        // @parm float | horiz_amp | maximum horizontal amplidtude of the shake
        // @parm float | vert_vel | vertical velocity factor (how many full cycles of movement in 1 second) 
        // @parm float | horiz_vel | horizontal velocity factor (how many full cycles of movement in 1 second) 
		case CRCC(0x75a516d3,"SC_ShakeCamera"):
		{
			float duration	= 0.0f;
			float vert_amp	= 0.0f;
			float horiz_amp	= 0.0f;
			float vert_vel	= 0.0f;
			float horiz_vel	= 0.0f;

			pParams->GetFloat( "duration",	&duration );
			pParams->GetFloat( "vert_amp",	&vert_amp );
			pParams->GetFloat( "horiz_amp",	&horiz_amp );
			pParams->GetFloat( "vert_vel",	&vert_vel );
			pParams->GetFloat( "horiz_vel",	&horiz_vel );

			SetShake( duration, vert_amp, horiz_amp, vert_vel, horiz_vel );
			
			break;
		}
		
				 
		// @script | SC_SetMode | Set the mode of a skater camera
        // @parm integer | mode | mode number 1-4, start out at 2 (normal_medium)
		case CRCC(0xd7867ffe,"SC_SetMode"):
		{
		
			int mode = 0;
			pParams->GetInteger(CRCD(0x6835b854,"mode"),&mode, true);
			SetMode((ESkaterCamMode)mode);	   
			break; 	
		}

	
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCameraComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterCameraComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*	Example:
	p_info->AddInteger("m_never_suspend",m_never_suspend);
	p_info->AddFloat("m_suspend_distance",m_suspend_distance);
	*/
	
	// We call the base component's GetDebugInfo, so we can add info from the common base component.
	CBaseComponent::GetDebugInfo( p_info );
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCameraComponent::ResetLookAround( void )
{
	mp_lookaround_component->mLookaroundTilt		= 0.0f;
	mp_lookaround_component->mLookaroundHeading	= 0.0f;
	SetNoLerpUpdate( true );
	Update();
	Commit();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCameraComponent::ResetMode( )
{
	// Just call SetMode() again with the current mode
	SetMode(mMode);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCameraComponent::SetMode( ESkaterCamMode mode, float time )
{
	// We need a CCameraComponent attached in order to set camera details.
	CCameraComponent *p_cam_comp = GetCameraComponentFromObject(GetObject());
	Dbg_MsgAssert( p_cam_comp, ( "CSkaterCameraComponent needs CCameraComponent attached to parent" ));

	Dbg_MsgAssert( mode < SKATERCAM_NUM_MODES,( "Bad mode" ));

	// Obtain details for this mode.
	float h_fov, behind, above, tilt, origin_offset, zoom_lerp;
	const char* p_name;

	// Obtain a pointer to the array entry for the desired mode. The array is now dependent
	// on which game mode is currently being played, 1 player, 2 player vert, or 2 player horiz.
	Nx::ScreenMode screen_mode = Nx::CViewportManager::sGetScreenMode();

	Script::CArray* p_array;

	switch( screen_mode )
	{
		case Nx::vSPLIT_V:
		{
			p_array = Script::GetArray( "Skater_Camera_2P_Vert_Array" );
			break;
		}

		case Nx::vSPLIT_H:
		{
			p_array = Script::GetArray( "Skater_Camera_2P_Horiz_Array" );
			break;
		}

		default:
		{			
			p_array = Script::GetArray( "Skater_Camera_Array" );
			break;
		}
	}


	// Extra check here, since the array in physics.q may change.
	Dbg_MsgAssert( mode < (int)p_array->GetSize(), ( "Bad mode" ));

	uint32 cs = p_array->GetNameChecksum( mode );
	Script::CScriptStructure* p_struct = Script::GetStructure( cs );

	mMode = mode;

	mLipTrickTilt = 0.0f;

	// Read the values from the array.
	p_struct->GetFloat( CRCD(0x94df1603,"horiz_fov"),				&h_fov );
	p_struct->GetFloat( CRCD(0x52e6c41b,"behind"),					&behind );
	p_struct->GetFloat( CRCD(0xb96ae2d,"above"),					&above );
	p_struct->GetFloat( CRCD(0xe3c07609,"tilt"),						&tilt );
	p_struct->GetFloat( CRCD(0x12f6992,"origin_offset"),			&origin_offset );
	p_struct->GetFloat( CRCD(0x7f0032ac,"lip_trick_tilt"),			&mLipTrickTilt );
	p_struct->GetFloat( CRCD(0xadb6390e,"lip_trick_above"),			&mLipTrickAbove );

	// Get SLERP values.
	p_struct->GetFloat( CRCD(0xf54fb9c5,"slerp"),					&mSlerp );
	p_struct->GetFloat( CRCD(0x2fa11029,"vert_air_slerp"),			&mVertAirSlerp );
	p_struct->GetFloat( CRCD(0x42fd4a0,"vert_air_landed_slerp"),	&mVertAirLandedSlerp );

	// Get LERP values.
	p_struct->GetFloat( CRCD(0xae47899b,"lerp_xz"),					&mLerpXZ );
	p_struct->GetFloat( CRCD(0xe1e4e104,"lerp_y"),					&mLerpY );
	p_struct->GetFloat( CRCD(0xf386f4d9,"vert_air_lerp_xz"),			&mVertAirLerpXZ );
	p_struct->GetFloat( CRCD(0x4882a1fe,"vert_air_lerp_y"),			&mVertAirLerpY );
	p_struct->GetFloat( CRCD(0xbef0d195,"grind_lerp"),				&mGrindLerp );

	// Get zoom values.
	mGrindZoom			= 1.0f;
	mLipTrickZoom		= 1.0f;
	mBigAirTrickZoom	= 1.0f;

	p_struct->GetFloat( CRCD(0x748743a7,"zoom_lerp"),			&zoom_lerp );
	p_struct->GetFloat( CRCD(0x5a7f5cc5,"grind_zoom"),			&mGrindZoom );
	p_struct->GetFloat( CRCD(0xd790b83d,"big_air_trick_zoom"),	&mBigAirTrickZoom );
	p_struct->GetFloat( CRCD(0xd414c22e,"lip_trick_zoom"),		&mLipTrickZoom );
	mZoomLerp			= zoom_lerp;

	// Get camera style name.
	p_struct->GetText( CRCD(0xa1dc81f9,"name"), &p_name );

	// Send the new camera mode to console line 1.
	//HUD::PanelMgr* panelMgr = HUD::PanelMgr::Instance();
	//panelMgr->SendConsoleMessage( p_name, 1 );

	// Need to calculate interpolate timers here.
	mHorizFOV		= h_fov;
	mTilt			= tilt;
	mOriginOffset	= FEET( origin_offset );		// Convert to inches.

	// Set up interpolators for those values that require it.
	if( time > 0.0f )
	{
		behind						= FEET( behind );				// Convert to inches.
		above						= FEET( above );				// Convert to inches.
		mBehindInterpolatorTime		= time;
		mBehindInterpolatorDelta	= (( behind - mBehind ) * ( SKATERCAMERACOMPONENT_INTERPOLATOR_TIMESTEP	/ time ));
		mAboveInterpolatorTime		= time;
		mAboveInterpolatorDelta		= (( above - mAbove ) * ( SKATERCAMERACOMPONENT_INTERPOLATOR_TIMESTEP / time ));
	}
	else
	{
		mBehind						= FEET( behind );
		mAbove						= FEET( above );
		mBehindInterpolatorTime		= 0.0f;
		mAboveInterpolatorTime		= 0.0f;
	}
	
	// Set focal length immediately.
	switch( screen_mode )
	{
		case Nx::vSPLIT_V:
		{
#			ifdef __PLAT_NGC__
			p_cam_comp->SetHFOV( Script::GetFloat( "Skater_Cam_Horiz_FOV" ) / 2.0f );
#			else
			p_cam_comp->SetHFOV( mHorizFOV / 2.0f );
#			endif
			break;
		}

		case Nx::vSPLIT_H:
		{
			p_cam_comp->SetHFOV( mHorizFOV );
			break;
		}

		default:
		{			
			p_cam_comp->SetHFOV( mHorizFOV );
			break;
		}
	}

	switch( mode )
	{
		case ( SKATERCAM_MODE_REPLAY_FRONT ):
		case ( SKATERCAM_MODE_REPLAY_FRONT_ZOOM ):
			mp_lookaround_component->mLookaroundHeadingStartingPoint = Mth::PI;
			break;
		case ( SKATERCAM_MODE_REPLAY_LEFT ):
		case ( SKATERCAM_MODE_REPLAY_LEFT_ZOOM ):
			mp_lookaround_component->mLookaroundHeadingStartingPoint = Mth::PI / 2.0f;
			break;
		case ( SKATERCAM_MODE_REPLAY_BEHIND ):
		case ( SKATERCAM_MODE_REPLAY_BEHIND_ZOOM ):
			mp_lookaround_component->mLookaroundHeadingStartingPoint = 0;
			break;
		case ( SKATERCAM_MODE_REPLAY_RIGHT ):
		case ( SKATERCAM_MODE_REPLAY_RIGHT_ZOOM ):
			mp_lookaround_component->mLookaroundHeadingStartingPoint = -( Mth::PI / 2.0f );
			break;
		default:
			mp_lookaround_component->mLookaroundHeadingStartingPoint = 0;
			break;
	}   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCameraComponent::ToggleMode()
{
	if ( mMode >= SKATERCAM_NUM_NORMAL_MODES )
	{
		return;
	}
	
	mMode = (ESkaterCamMode)( mMode + 1);
	if( mMode >= SKATERCAM_NUM_NORMAL_MODES )
	{
		mMode = SKATERCAM_MODE_FIRST_VALID;
	}

	ResetMode();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::SetShake( float duration, float vert_amp, float horiz_amp, float vert_vel, float horiz_vel )
{
	mShakeDuration			= duration;
	mShakeInitialDuration	= duration;
	mShakeMaxVertAmp		= vert_amp;
	mShakeMaxHorizAmp		= horiz_amp;
	mShakeVertVel			= vert_vel;
	mShakeHorizVel			= horiz_vel;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CSkaterCameraComponent::UseVertCam( void )
{   
	if( mpSkater == NULL )
	{
		return false;
	}
	
	// this logic is used only by cameras observing non-local clients; in all other cases, skating camera logic in suspended when viewing a walking
	// skater; when observing, skater camera logic is always used; this prevents the "birds-eye-view" issue when observing a skater who transitioned to
	// walking during vert air
	if( mpSkaterStateComponent->GetPhysicsState() == WALKING )
	{
		return false;
	}
	
	// Use vert cam when in the lip state.
	if( mpSkaterStateComponent->GetState() == LIP )
	{
#		ifdef DEBUG_CAMERA
		printf ("SkaterCam Using Vert Cam as LIP\n");
#		endif
		return true;
	}
	
	if( mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT )  && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS))
	{
#		ifdef DEBUG_CAMERA
		printf ("SkaterCam Using Vert Cam as VERT_AIR\n");
#		endif
		return true;
	}
	
	if( mpSkaterStateComponent->GetJumpedOutOfLipTrick())
	{
#		ifdef DEBUG_CAMERA
		printf ("SkaterCam Using Vert Cam as Jumped out of lip trick\n");
#		endif
		return true;
	}

#	ifdef DEBUG_CAMERA
	printf ("Skatecam NOT using vert cam\n");
#	endif

	return false;	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::addShake( Mth::Vector& cam, Mth::Matrix& frame )
{
	float damping		= mShakeDuration / mShakeInitialDuration;
	float vert_angle	= mShakeDuration * mShakeVertVel * Mth::PI * 2.0f;
	float horiz_angle	= mShakeDuration * mShakeHorizVel * Mth::PI * 2.0f;
	float vert_offset	= mShakeMaxVertAmp * sinf( vert_angle ) * damping;
	float horiz_offset	= mShakeMaxHorizAmp * sinf( horiz_angle ) * damping;

	cam += frame[X] * horiz_offset;
	cam += frame[Y] * vert_offset;

	mShakeDuration		-= Tmr::FrameLength();
	if( mShakeDuration < 0.0f )
		mShakeDuration = 0.0f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CSkaterCameraComponent::GetTripodPos( bool instantly )
{
	float		lerp_xz;
	float		lerp_y;
	float		delta		= Tmr::FrameLength();

	if( mpSkater == NULL )
	{
		return Mth::Vector( 0.0f, 0.0f, 0.0f );
	}

	Mth::Vector now			= mpSkater->m_pos;

	// The tripod pos is always *tending* towards the skater position, but the rate at which it does so is
	// definable, to allow for some lag, which improves the feeling of speed.
	if( mLerpReductionTimer > 0.0f )
	{
		mLerpReductionTimer -= delta;
		if( mLerpReductionTimer > 0.0f )
		{
			mLerpReductionMult += ( mLerpReductionDelta * delta );
		}
		else
		{
			mLerpReductionMult	= 1.0f;
			mLerpReductionTimer	= 0.0f;
		}
	}
	else
	{
		mLerpReductionMult	= 1.0f;
	}

	Dbg_Assert( mpSkaterStateComponent );
	if(( mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag( SPINE_PHYSICS )))
	{
		#ifdef	DEBUG_CAMERA
		printf ("Skatercam using mVertAirLerp\n");
		#endif

		lerp_xz	= GetTimeAdjustedSlerp( mVertAirLerpXZ * mLerpReductionMult, delta );
		lerp_y	= GetTimeAdjustedSlerp( mVertAirLerpY * mLerpReductionMult, delta );
	}
	else
	{
		lerp_xz	= GetTimeAdjustedSlerp( mLerpXZ * mLerpReductionMult, delta );
		lerp_y	= GetTimeAdjustedSlerp( mLerpY * mLerpReductionMult, delta );

		// Added the following check (Dave 7/16/02) to help eliminate the camera rattle when snapping on/off a curb.
		// The flag is set for one frame only.
		if( mpSkaterStateComponent->GetFlag( SNAPPED_OVER_CURB ))
		{
			lerp_y = 1.0f;
		}
		
		if( mpSkaterStateComponent->GetFlag( SNAPPED ))
		{
			instantly = true;
		}
	}

	if( instantly )
	{
		mLastTripodPos = now;
	}
	else
	{
		mLastTripodPos.Set( mLastTripodPos.GetX() + (( now.GetX() - mLastTripodPos.GetX() ) * lerp_xz ),
							mLastTripodPos.GetY() + (( now.GetY() - mLastTripodPos.GetY() ) * lerp_y ),
							mLastTripodPos.GetZ() + (( now.GetZ() - mLastTripodPos.GetZ() ) * lerp_xz ));
	}

	return mLastTripodPos;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::CalculateZoom( float* p_above, float* p_behind )
{
	if( mpSkater == NULL )
	{
		return;
	}

	mTargetZoom = 1.0f;

	// Deal with zoom for Big Air. Set the flag only when a trick is started.
	if( !mBigAirTrickZoomActive && ( mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS)) && mpSkaterStateComponent->DoingTrick() )
	{
		mBigAirTrickZoomActive = true;
	}
	else if( !( mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag( SPINE_PHYSICS )))
	{
		// Must have landed.
		mBigAirTrickZoomActive = false;
	}

	if( mBigAirTrickZoomActive )
	{
		mTargetZoom = mBigAirTrickZoom;
	}
	else
	{
		EStateType state = mpSkaterStateComponent->GetState();

		// If the skater is grinding, zoom also.
		if( state == RAIL )
		{
			mTargetZoom = mGrindZoom;
		}
		else if( state == LIP )
		{
			mTargetZoom = mLipTrickZoom;
		}
		else if( state == LIP )
		{
			mTargetZoom = mLipTrickZoom;
		}
	}

	
	#if 0
	// If lookaround override is set, factor in the lookaround override zoom.
	if( mp_lookaround_component->mLookaroundOverride && ( mp_lookaround_component->mLookaroundZoom != 1.0f ))
	{
		mCurrentZoom = mTargetZoom;
		mCurrentZoom *= mp_lookaround_component->mLookaroundZoom;
	}
	else
	{
		mCurrentZoom += (( mTargetZoom - mCurrentZoom ) * mZoomLerp );
	}
	#else
	// If lookaround override is set, factor in the lookaround override zoom.
	if( mp_lookaround_component->mLookaroundOverride && ( mp_lookaround_component->mLookaroundZoom != 1.0f ))
	{
		mTargetZoom *= mp_lookaround_component->mLookaroundZoom;
	}
	
	mCurrentZoom += (( mTargetZoom - mCurrentZoom ) * mZoomLerp );
	#endif
	
	*p_behind = mBehind * mCurrentZoom;

	// Behind is also shortened when the lookaround camera is tilting upwards.
	if( mp_lookaround_component->mLookaroundTilt < 0.0f )
	{
		float max_tilt = 3.14f * 0.2f;
		*p_behind = *p_behind * ( 0.4f + ( 0.6f * (( max_tilt + mp_lookaround_component->mLookaroundTilt ) / max_tilt )));
	}

	// Use lip_trick_above when doing a lip trick.
	float above_val = mAbove;
	if( mpSkaterStateComponent->GetState() == LIP )
	{
		above_val = mLipTrickAbove;
	}
	
	// Figure above tending towards the perfect above, if zoom is < 1.0.
	if( mCurrentZoom < 1.0f )
	{
		*p_above = SKATERCAMERACOMPONENT_PERFECT_ABOVE + (( above_val - SKATERCAMERACOMPONENT_PERFECT_ABOVE ) * mCurrentZoom );
	}
	else
	{
		*p_above = above_val;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::UpdateInterpolators(   )
{
	if( mBehindInterpolatorTime > 0.0f )
	{
		mBehindInterpolatorTime -= SKATERCAMERACOMPONENT_INTERPOLATOR_TIMESTEP;
		mBehind += mBehindInterpolatorDelta;		
	}
	else
	{
		mBehindInterpolatorTime = 0.0f;
	}

	if( mAboveInterpolatorTime > 0.0f )
	{
		mAboveInterpolatorTime -= SKATERCAMERACOMPONENT_INTERPOLATOR_TIMESTEP;
		mAbove += mAboveInterpolatorDelta;		
	}
	else
	{
		mAboveInterpolatorTime = 0.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::SetLerpReductionTimer( float time )
{
	if( time > 0.0f )
	{
		mLerpReductionTimer	= time;
		mLerpReductionDelta	= 1.0f / time;
		mLerpReductionMult	= 0.0f;
	}
	else
	{
		mLerpReductionTimer	= 0.0f;
		mLerpReductionDelta	= 0.0f;
		mLerpReductionMult	= 1.0f;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::SetSkater( CSkater* p_skater )
{
	mpSkater = p_skater;
	
	if( p_skater == NULL )
	{
		mpSkaterStateComponent = NULL;
		mpSkaterPhysicsControlComponent = NULL;
	}
	else
	{
		mpSkaterStateComponent = GetSkaterStateComponentFromObject(mpSkater);
		Dbg_Assert(mpSkaterStateComponent);
		
		// non-local clients will not have a CSkaterPhysicsControlComponent
		mpSkaterPhysicsControlComponent = GetSkaterPhysicsControlComponentFromObject(mpSkater);
		
		SetNoLerpUpdate(true);
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSkater* CSkaterCameraComponent::GetSkater( void )
{
	return mpSkater;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCameraComponent::GetCameraState ( SCameraState& state )
{
	state.lastActualMatrix[X] = mLastActualRight;
	state.lastActualMatrix[Y] = mLastActualUp;
	state.lastActualMatrix[Z] = mLastActualAt;
	state.lastActualMatrix[W].Set( 0.0f, 0.0f, 0.0f, 1.0f );
	state.lastTripodPos = mLastTripodPos;
	state.lastDot = mLastDot;
	state.lastZoom = mCurrentZoom;
}
	
}


================================================
FILE: Code/Gel/Components/SkaterCameraComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SkaterCameraComponent.h
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/21/03
//****************************************************************************

#ifndef __COMPONENTS_SKATERCAMERACOMPONENT_H__
#define __COMPONENTS_SKATERCAMERACOMPONENT_H__

#include 
#include 
#include 

#include 
#include 
#include 

#include 

// Replace this with the CRCD of the component you are adding.
#define		CRC_SKATERCAMERA							CRCD( 0x5e43a604, "SkaterCamera" )

//  Standard accessor macros for getting the component either from within an object, or given an object.
#define		GetSkaterCameraComponent()					((Obj::CSkaterCameraComponent*)GetComponent( CRC_SKATERCAMERA ))
#define		GetSkaterCameraComponentFromObject( pObj )	((Obj::CSkaterCameraComponent*)(pObj)->GetComponent( CRC_SKATERCAMERA ))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CInputComponent;
	class CSkaterStateComponent;
	class CSkaterPhysicsControlComponent;
	class CCameraLookAroundComponent;

class CSkaterCameraComponent : public CBaseComponent
{
	friend class CWalkCameraComponent;
	
public:

	enum ESkaterCamMode
	{
		SKATERCAM_MODE_UNDEFINED		= 0,
		SKATERCAM_MODE_FIRST_VALID		= 1,
		SKATERCAM_MODE_NORMAL_NEAR		= 1,
		SKATERCAM_MODE_NORMAL_MEDIUM,
		SKATERCAM_MODE_NORMAL_FAR,
		SKATERCAM_MODE_NORMAL_MEDIUM_LTG,
        SKATERCAM_NUM_NORMAL_MODES,
		SKATERCAM_FIRST_REPLAY_MODE = SKATERCAM_NUM_NORMAL_MODES,
		SKATERCAM_MODE_REPLAY_FRONT = SKATERCAM_NUM_NORMAL_MODES,
		SKATERCAM_MODE_REPLAY_FRONT_ZOOM,
        SKATERCAM_MODE_REPLAY_LEFT,
		SKATERCAM_MODE_REPLAY_LEFT_ZOOM,
		SKATERCAM_MODE_REPLAY_BEHIND,
		SKATERCAM_MODE_REPLAY_BEHIND_ZOOM,
		SKATERCAM_MODE_REPLAY_RIGHT,
		SKATERCAM_MODE_REPLAY_RIGHT_ZOOM,
		SKATERCAM_LAST_REPLAY_MODE = SKATERCAM_MODE_REPLAY_RIGHT_ZOOM,
		SKATERCAM_NUM_MODES,
		SKATERCAM_MODE_ANIM_PLAYBACK,
	};
	
											CSkaterCameraComponent();
    virtual									~CSkaterCameraComponent();

public:
    virtual void            				Update();
    virtual void            				InitFromStructure( Script::CStruct* pParams );
    virtual void            				RefreshFromStructure( Script::CStruct* pParams );
	virtual void							Finalize();
    
    virtual EMemberFunctionResult   		CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 							GetDebugInfo( Script::CStruct* p_info );

	Mth::Vector								GetTripodPos( bool instantly = false );
	void									UpdateInterpolators(   );
	void									CalculateZoom( float* p_above, float* p_behind );
	void									addShake( Mth::Vector& cam, Mth::Matrix& frame );

	void									SetNoLerpUpdate( bool instant )	{ if( instant ) mInstantCount = 3; }
	void									Commit( void );

	void									SetShake( float duration, float vert_amp, float horiz_amp, float vert_vel, float horiz_vel );
	bool									UseVertCam( void );
	void									ActivateLerpIntoBehavior ( float duration );

	void									ResetLookAround( void );
	void									ResetMode( );
	void									SetMode( ESkaterCamMode mode, float time = 1.0f );
	void									ToggleMode( );
	ESkaterCamMode							GetMode( void )								{ return mMode; }
	void									SetLerpReductionTimer( float time );
	void									SetSkater( CSkater* p_skater );
	CSkater*								GetSkater( void );
	
	void									ReadyForActivation ( const SCameraState& state );
	void									GetCameraState ( SCameraState& state );

	static CBaseComponent*					s_create();

	// These functions access the CCameraComponent which is required to be attached.
	void									Enable( bool enable );
	Gfx::Camera*							GetCamera( void );
	const Mth::Vector &						GetPosition( void ) const;
	void									SetPosition( Mth::Vector& pos );
	const Mth::Matrix&						GetMatrix( void ) const;

	bool									mSkipButton;
	bool									mRightButton;
	int										mRightX;
	int										mRightY;

private:
	ESkaterCamMode							mMode;

	int										mInstantCount;				// Used to maintain the 'instantly' flag during update for several frames.

	float									mLastDot;
	Mth::Vector								mLastTripodPos;
	Mth::Vector								mLastActualRight;			// Used because the frame is adjusted after focus LERPING.
	Mth::Vector								mLastActualUp;
	Mth::Vector								mLastActualAt;

	Mth::Matrix								mLastPreWallSkaterMatrix;	// Used because we don't want to use the skater's matrix during wall rides.

	float									mBehind;
	float									mBehindInterpolatorTime;
	float									mBehindInterpolatorDelta;
	float									mAbove;
	float									mAboveInterpolatorTime;
	float									mAboveInterpolatorDelta;

	float									mVertAirLandedTimer;		// Defines for how long the vert air landed slerp gets applied.

	float									mHorizFOV;

	float									mLerpXZ;
	float									mLerpY;
	float									mVertAirLerpXZ;
	float									mVertAirLerpY;
	float									mGrindLerp;

	float									mLerpReductionTimer;
	float									mLerpReductionDelta;
	float									mLerpReductionMult;

	bool									mBigAirTrickZoomActive;
	float									mCurrentZoom;
	float									mTargetZoom;
	float									mZoomLerp;				// LERP rate for zoom.
	float									mBigAirTrickZoom;		// Target zoom for big air trick.
	float									mGrindZoom;				// Target zoom for grind.
	float									mLipTrickZoom;			// Target zoom for lip trick.
	int										mLipSideDecided;

	float									mLean;					// For lean during grind imbalance.

	float									mTilt;
	float									mTiltAddition;
	float									mLipTrickTilt;
	float									mLipTrickAbove;
	float									mWalkSlerp;
	float									mSlerp;
	float									mVertAirSlerp;
	float									mVertAirLandedSlerp;
	float									mOriginOffset;

	float									mShakeDuration;			// Shake duration in seconds (0.0 means no shake).
	float									mShakeInitialDuration;
	float									mShakeMaxVertAmp;
	float									mShakeMaxHorizAmp;
	float									mShakeVertVel;
	float									mShakeHorizVel;
	
	CCameraLookAroundComponent*				mp_lookaround_component;

	Mth::SlerpInterpolator*					mpSlerp;
	CSkater*								mpSkater;
	CSkaterStateComponent*					mpSkaterStateComponent;
	CSkaterPhysicsControlComponent*			mpSkaterPhysicsControlComponent;
};

}

#endif


================================================
FILE: Code/Gel/Components/SkitchComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SkitchComponent.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/19/03
//****************************************************************************

#include 

#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent* CSkitchComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkitchComponent );	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSkitchComponent::CSkitchComponent() : CBaseComponent()
{
	SetType( CRC_SKITCH );

	// Skitching allowed by default.
	m_skitch_allowed = true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSkitchComponent::~CSkitchComponent()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkitchComponent::InitFromStructure( Script::CStruct* pParams )
{
	// There needs to be a ObjectHookManagerComponent attached for the SkitchComponent to operate.
	mp_object_hook_manager_component = GetObjectHookManagerComponentFromObject( GetObject());
	Dbg_MsgAssert( mp_object_hook_manager_component, ( "SkitchComponent created without ObjectHookManagerComponent" ));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkitchComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure( pParams );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkitchComponent::Update()
{
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent::EMemberFunctionResult CSkitchComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | Obj_AllowSkitching | Enable skitching for this object 
		// @flag off | disable skitching
		case 0xc38cce3b:  // Obj_AllowSkitching
		{
			AllowSkitch(( pParams->ContainsFlag( "off" )) ? false : true );
			break;
		}

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkitchComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert( p_info, ( "NULL p_info sent to CSkitchComponent::GetDebugInfo" ));
	
	p_info->AddInteger( "m_skitch_allowed", m_skitch_allowed ? 1 : 0 );

	// we call the base component's GetDebugInfo, so we can add info from the common base component
	CBaseComponent::GetDebugInfo( p_info );
#endif				 
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CSkitchComponent::GetNearestSkitchPoint( Mth::Vector *p_point, Mth::Vector &pos )
{
	int nearest_node_number = -1;	// -1 means not found yet

	// Only continue if we have a parent object (we always should) and a object hook manager component.
	CCompositeObject *p_parent_object = GetObject();
	if( mp_object_hook_manager_component && p_parent_object )
	{
		Obj::CObjectHookManager *p_object_hook_man = mp_object_hook_manager_component->GetObjectHookManager();
		if( p_object_hook_man )
		{
			// Form a transformation matrix.
			Mth::Matrix total_mat	= p_parent_object->GetMatrix();
			total_mat[X][W]			= 0.0f;
			total_mat[Y][W]			= 0.0f;
			total_mat[Z][W]			= 0.0f;
			total_mat[W]			= p_parent_object->GetPos();
			total_mat[W][W]			= 1.0f;

			p_object_hook_man->UpdateTransform( total_mat );

			int node_number = 0;
			CObjectHookNode *p_node		= p_object_hook_man->GetFirstNode();
			CObjectHookNode *p_nearest	= p_node;
			float nearest_dist_squared	= 100000000000000.0f;	
			Mth::Vector nearest_point(0.0f, 0.0f, 0.0f);

			while( p_node )
			{
				// Get the postition of the hook.
				Mth::Vector point = p_object_hook_man->GetPos( p_node );

				// Project the point onto the base plane of the object so we can ignore the height.
				point -= p_parent_object->GetPos();
				point.ProjectToPlane( p_parent_object->GetMatrix()[Y] );
				point += p_parent_object->GetPos();
			
				float dist_squared = ( point - pos ).LengthSqr();
				if( dist_squared < nearest_dist_squared )
				{
					nearest_dist_squared	= dist_squared;
					nearest_point			= point;
					p_nearest				= p_node; 
					nearest_node_number		= node_number;
				}
				p_node = p_node->GetNext();
				node_number++;
			}		
			*p_point = nearest_point;
		 }
	}

	return nearest_node_number;
}



/******************************************************************/
/*                                                                */
/* Given the index of a skitch point, then get the position of	  */
/* that point.													  */
/* Adjust the point to lay on the ground plane of the object.	  */
/* Returns the position in *p_point returns true if object still  */
/* exists, and false otherwise.									  */
/*                                                                */
/******************************************************************/
bool CSkitchComponent::GetIndexedSkitchPoint( Mth::Vector *p_point, int index )
{
	// Only continue if we have a parent object (we always should) and a object hook manager component.
	CCompositeObject *p_parent_object = GetObject();
	if( mp_object_hook_manager_component && p_parent_object )
	{
		Obj::CObjectHookManager *p_object_hook_man = mp_object_hook_manager_component->GetObjectHookManager();
		if( p_object_hook_man )
		{
			// Form a transformation matrix.
			Mth::Matrix total_mat	= p_parent_object->GetMatrix();
			total_mat[X][W]			= 0.0f;
			total_mat[Y][W]			= 0.0f;
			total_mat[Z][W]			= 0.0f;
			total_mat[W]			= p_parent_object->GetPos();
			total_mat[W][W]			= 1.0f;

			p_object_hook_man->UpdateTransform( total_mat );

			int node_number = 0;
			CObjectHookNode *p_node = p_object_hook_man->GetFirstNode();
		
			while( p_node )
			{
				if( node_number == index )
				{
					Mth::Vector point = p_object_hook_man->GetPos( p_node );

					// Project the point onto the base plane of the object so we can ignore the height.
					point -= p_parent_object->GetPos();
					point.ProjectToPlane( p_parent_object->GetMatrix()[Y] );
					point += p_parent_object->GetPos();

					*p_point = point;

					// There was no break here in the original code in MovingObject, but I see no reason
					// to continue the loop iteration at this point.
					break;
				}
				p_node = p_node->GetNext();
				node_number++;
			}
			return true;
		 }
	}
	return false;
}








/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/










}


================================================
FILE: Code/Gel/Components/SkitchComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SkitchComponent.h
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/19/03
//****************************************************************************

#ifndef __COMPONENTS_SKITCHCOMPONENT_H__
#define __COMPONENTS_SKITCHCOMPONENT_H__

#include 
#include 

#include 
#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_SKITCH								CRCD( 0x3506ce64, "Skitch" )

//  Standard accessor macros for getting the component either from within an object, or given an object
#define		GetSkitchComponent()					((Obj::CSkitchComponent*)GetComponent( CRC_SKITCH ))
#define		GetSkitchComponentFromObject( pObj )	((Obj::CSkitchComponent*)(pObj)->GetComponent( CRC_SKITCH ))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CSkitchComponent : public CBaseComponent
{
public:
    CSkitchComponent();
    virtual ~CSkitchComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

	bool							CanSkitch( void )			{ return m_skitch_allowed; }
	void							AllowSkitch( bool allow )	{ m_skitch_allowed = allow; }
	int								GetNearestSkitchPoint( Mth::Vector *p_point, Mth::Vector &pos );
	bool							GetIndexedSkitchPoint( Mth::Vector *p_point, int index );

private:
	bool							m_skitch_allowed;
	CObjectHookManagerComponent*	mp_object_hook_manager_component;
};

}

#endif


================================================
FILE: Code/Gel/Components/StaticVehicleComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       StaticVehicleComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  8/6/3
//****************************************************************************

#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CStaticVehicleComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CStaticVehicleComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CStaticVehicleComponent::CStaticVehicleComponent() : CBaseComponent()
{
	SetType( CRC_STATICVEHICLE );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CStaticVehicleComponent::~CStaticVehicleComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStaticVehicleComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStaticVehicleComponent::Finalize()
{
	CModelComponent* p_model_component = static_cast< CModelComponent* >(GetModelComponentFromObject(GetObject()));
	Dbg_Assert(p_model_component);
	Nx::CModel* p_model = p_model_component->GetModel();
	Dbg_Assert(p_model);
	Nx::CHierarchyObject* p_hierarchy_objects = p_model->GetHierarchy();
	Dbg_Assert(p_hierarchy_objects);
	
	Mth::Vector p_wheels [ CVehicleComponent::vVP_NUM_WHEELS ];
	
	for (int n = CVehicleComponent::vVP_NUM_WHEELS; n--; )
	{
		Mth::Vector& wheel_pos = p_wheels[n];
		
		Mth::Matrix wheel_matrix = (p_hierarchy_objects + 2 + n)->GetSetupMatrix();
		
		// rotate out of max coordinate system
		wheel_pos[X] = -wheel_matrix[W][X];
		wheel_pos[Y] = wheel_matrix[W][Z];
		wheel_pos[Z] = -wheel_matrix[W][Y];
		wheel_pos[W] = 1.0f;
	}
	
	Mth::Matrix body_matrix = (p_hierarchy_objects + 1)->GetSetupMatrix();
	Mth::Vector body_pos;
	body_pos[X] = -body_matrix[W][X];
	body_pos[Y] = body_matrix[W][Z];
	body_pos[Z] = -body_matrix[W][Y];
	body_pos[W] = 1.0f;
	
	CSkeletonComponent* p_skeleton_component = static_cast< CSkeletonComponent* >(GetSkeletonComponentFromObject(GetObject()));
	Dbg_Assert(p_skeleton_component);
	Dbg_Assert(p_skeleton_component->GetSkeleton());
	Mth::Matrix* p_matrices = p_skeleton_component->GetSkeleton()->GetMatrices();
	for (int i = 0; i < p_skeleton_component->GetSkeleton()->GetNumBones(); i++)
	{
		Mth::Matrix& matrix = p_matrices[i];
		
		// setup the matrix for each bone in the skeleton
		
		// shadow
		if (i == 0)
		{
			matrix.Zero();
		}
		
		// body
		else if (i == 1)
		{
			matrix.Zero();
			matrix[X][X] = 1.0f;
			matrix[Y][Z] = -1.0f;
			matrix[Z][Y] = 1.0f;
			matrix[W] = body_pos;
		}
		
		// wheel
		else
		{
			Mth::Vector& wheel_pos = p_wheels[i - 2];
			
			matrix.Zero();
			matrix[X][X] = -1.0f;
			matrix[Y][Z] = 1.0f;
			matrix[Z][Y] = 1.0f;
			matrix[W] = wheel_pos;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStaticVehicleComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStaticVehicleComponent::Update()
{
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CStaticVehicleComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStaticVehicleComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CStaticVehicleComponent::GetDebugInfo"));
	
	CBaseComponent::GetDebugInfo(p_info);	  
}
	
}


================================================
FILE: Code/Gel/Components/StaticVehicleComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       StaticVehicleComponent.h
//* OWNER:          Dan
//* CREATION DATE:  8/6/3
//****************************************************************************

#ifndef __COMPONENTS_STATICVEHICLECOMPONENT_H__
#define __COMPONENTS_STATICVEHICLECOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_STATICVEHICLE CRCD(0x9649d4f9, "StaticVehicle")

#define		GetStaticVehicleComponent() ((Obj::CStaticVehicleComponent*)GetComponent(CRC_STATICVEHICLE))
#define		GetStaticVehicleComponentFromObject(pObj) ((Obj::CStaticVehicleComponent*)(pObj)->GetComponent(CRC_STATICVEHICLE))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CStaticVehicleComponent : public CBaseComponent
{
public:
    CStaticVehicleComponent();
    virtual ~CStaticVehicleComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
	virtual	void 					Finalize();
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
private:
	Mth::Vector						mp_wheels [ CVehicleComponent::vVP_NUM_WHEELS ];
};

}

#endif


================================================
FILE: Code/Gel/Components/StatsManagerComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       StatsManagerComponent.cpp
//* OWNER:          Zac Drake
//* CREATION DATE:  5/29/03
//****************************************************************************

//  This component watches the skater and checks if certain always active goals are met.
//  When these goals are met, the skaters stats are increased. These goals are pulled from
//  the stats_goals script array in stats.q

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include  
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 


namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CStatsManagerComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CStatsManagerComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CStatsManagerComponent::CStatsManagerComponent() : CBaseComponent()
{
	SetType( CRC_STATSMANAGER );
    
    mpSkater= NULL;
    mpSkaterStateComponent = NULL;
    mpSkaterPhysicsControlComponent = NULL;
    mpSkaterCorePhysicsComponent = NULL;

    vert_start_pos.Set( 0.0f, 0.0f, 0.0f, 0.0f );
    vert_end_pos.Set( 0.0f, 0.0f, 0.0f, 0.0f );
    jump_start_pos.Set( 0.0f, 0.0f, 0.0f, 0.0f );
    jump_end_pos.Set( 0.0f, 0.0f, 0.0f, 0.0f );

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CStatsManagerComponent::~CStatsManagerComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CStatsManagerComponent::InitFromStructure( Script::CStruct* pParams )
{
	// ** Add code to parse the structure, and initialize the component
    
    showed_info = false;    // need to grab this from memcard eventually
    stat_goals_on = false;
    
    new_event = NULL;
    last_event = NULL;
    
    vert_set = 0;
    vert_height = 0;
    vert_score = 0;
    vert_start_base=0;
    vert_start_mult=0;
    
    jump_set = 0;
    
    num_fliptricks=0;
    num_grabtricks=0;

    air_combo_score=0;

    m_restarted_this_frame = true;

    // Set p_skater
    uint32 target_id = Obj::CBaseComponent::GetObject()->GetID();
    Dbg_MsgAssert(target_id >=0 && target_id <= 15,("Bad StatsManagerTarget 0x%x in SkaterStatsManagerComponent",target_id));
    
	CSkater* p_skater = static_cast(CCompositeObjectManager::Instance()->GetObjectByID(target_id)); 
	Dbg_MsgAssert(p_skater,("Can't find skater %d in SkaterStatsManagerComponent",target_id));
    
	SetSkater(p_skater);

    // get difficulty level
    Game::CGoalManager* pGoalManager = Game::GetGoalManager();
    Dbg_Assert( pGoalManager );
    dif_level = (int)pGoalManager->GetDifficultyLevel(true);

    printf("STATSManager: difficulty ====================== %i\n", dif_level );

    // should bump stats?
    int bump = Script::GetInteger( "bump_stats", Script::ASSERT );
    if ( bump == 1 )
    {
        bump_stats=true;
    }

    // reset stats to defaults
    /*Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile( 0 );
    pProfile->SetPropertyValue( CRCD(0x439f4704,"air"), 3 );
    pProfile->SetPropertyValue( CRCD(0x9016b4e7,"switch"), 3 );
    pProfile->SetPropertyValue( CRCD(0xedf5db70,"spin"), 3 );
    pProfile->SetPropertyValue( CRCD(0xaf895b3f,"run"), 3 );
    pProfile->SetPropertyValue( CRCD(0xf0d90109,"speed"), 3 );
    pProfile->SetPropertyValue( CRCD(0xae798769,"lip_balance"), 3 );
    pProfile->SetPropertyValue( CRCD(0xf73a13e3,"rail_balance"), 3 );
    pProfile->SetPropertyValue( CRCD(0xb1fc0722,"manual_balance"), 3 );
    pProfile->SetPropertyValue( CRCD(0x6dcb497c,"flip_speed"), 3 );
    pProfile->SetPropertyValue( CRCD(0x9b65d7b8,"ollie"), 3 );
    */
    
    // get initial values from script
    Script::CArray* p_array = Script::GetArray( "stats_goals", Script::ASSERT );
    Dbg_MsgAssert( p_array->GetType() == ESYMBOLTYPE_STRUCTURE, ( "This function only works on arrays of structures." ) );

    int array_size = p_array->GetSize();
    Script::CStruct* p_struct = NULL;
    
    //for( int index = 0; index < NUM_STATS_GOALS; index++)
    for ( int index = 0; index < array_size; index++ )
    {
        p_struct = p_array->GetStructure( index );
        if ( p_struct->ContainsComponentNamed( "goaltype" ) )
        {
            uint32 goal_value;
            p_struct->GetChecksum( "goaltype", &goal_value, Script::ASSERT );
            stat_goals[index].goaltype = goal_value;
        }
        if ( p_struct->ContainsComponentNamed( "stattype" ) )
        {
            uint32 stat_value;
            p_struct->GetChecksum( "stattype", &stat_value, Script::ASSERT );
            stat_goals[index].stattype = stat_value;
        }
        if ( p_struct->ContainsComponentNamed( "value" ) )
        {
            Script::CArray* value_array = NULL;
            int value_value;
            p_struct->GetArray( "value", &value_array, Script::ASSERT );
            value_value = value_array->GetInteger(dif_level);
            stat_goals[index].value = value_value;
        }
        if ( p_struct->ContainsComponentNamed( "value_string" ) )
        {
            Script::CArray* value_string_array = NULL;
            const char*  value_vstring;
            p_struct->GetArray( "value_string", &value_string_array, Script::ASSERT );
            value_vstring = value_string_array->GetString(dif_level);
            stat_goals[index].v_string = value_vstring;
        }
        if ( p_struct->ContainsComponentNamed( "text" ) )
        {
            const char* text_value="";
            p_struct->GetString( "text", &text_value, Script::ASSERT );
            stat_goals[index].text = text_value;
        }
        if ( p_struct->ContainsComponentNamed( "complete" ) )
        {
            int complete_value;
            p_struct->GetInteger( "complete", &complete_value, Script::ASSERT );
            stat_goals[index].complete = complete_value;
        }
        if ( p_struct->ContainsComponentNamed( "value_trick" ) )
        {
            Script::CArray* value_trick_array = NULL;
            uint32  value_vtrick;
            p_struct->GetArray( "value_trick", &value_trick_array, Script::ASSERT );
            value_vtrick = value_trick_array->GetChecksum(dif_level);
            stat_goals[index].trick = value_vtrick;
        }
        if ( p_struct->ContainsComponentNamed( "value_taps" ) )
        {
            Script::CArray* value_taps_array = NULL;
            uint32  value_vtaps;
            p_struct->GetArray( "value_taps", &value_taps_array, Script::ASSERT );
            value_vtaps = value_taps_array->GetInteger(dif_level);
            stat_goals[index].taps = value_vtaps;
        }
        stat_goals[index].shown = false;
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*void CStatsManagerComponent::Finalize()
{
	// Virtual function, can be overridden to provided finialization to 
	// a component after all components have been added to an object
	// Usually this consists of getting pointers to other components
	// Note:  It is GUARENTEED (at time of writing) that
	// Finalize() will be called AFTER all components are added
	// and BEFORE the CCompositeObject::Update function is called
	
	// Example:
	// mp_suspend_component =  GetSuspendComponentFromObject( GetObject() );
    
}*/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CStatsManagerComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calling InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting because everything is missing 
	
	InitFromStructure(pParams);


}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CStatsManagerComponent::Update()
{
	// **  You would put in here the stuff that you would want to get run every frame
	// **  for example, a physics type component might update the position and orientation
	// **  and update the internal physics state

    if( mpSkater == NULL )
	{
		return;
	}

    // Return if not in career mode
    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
    if ( !skate_mod->GetGameMode()->IsTrue( CRCD(0x1ded1ea4,"is_career") ) )
    {
        return;
    }
    
    // Return if stat goals turned off
    if ( !stat_goals_on )
    {
        return;
    }
    
    // Return if in frontend
    if (skate_mod->m_cur_level == CRCD(0x9f2bafb7,"load_skateshop"))
    {
        return;
    }

    if (m_restarted_this_frame)
    {
        vert_set = 0;
        jump_set = 0;

        m_restarted_this_frame = false;

        return;
    }
    
    // Get current trick score this frame???
    Mdl::Score *pScore = mpSkater->GetScoreObject();
    Dbg_Assert( pScore );
    current_score = pScore->GetScorePotValue();
    current_mult = pScore->GetCurrentMult();
    if ( current_mult != 0 )
    {
        current_base = (current_score / current_mult);
    }
    else
    {
        current_base = 0;
    }
    
    // Get skater state
    EStateType state = mpSkaterStateComponent->GetState();

    // reset new event
    new_event = CRCD(0x26a7cadf,"other");
    
    // check if skating
    if (mpSkaterPhysicsControlComponent->IsSkating())
    {
        // check for vert airs and landings
        if ( mpSkaterStateComponent->GetFlag( VERT_AIR ) )
        {
            new_event = CRCD(0xb39b4f1b,"vert_air");
            if ( last_event != CRCD(0xb39b4f1b,"vert_air") )
            {
                // vert take off
                vert_set = 1;
                vert_height = 0;
                vert_start_pos = GetObject()->GetPos();
                vert_start_base = current_base;
                vert_start_mult = current_mult;
                printf("vert_start_score = %i\n", current_score );
                vert_score = 0;
            }
            else
            {
                // vert in air
                Mth::Vector new_pos = GetObject()->GetPos();
                if ( ( new_pos[1] - vert_start_pos[1] ) > vert_height )
                {
                    vert_height = ( new_pos[1] - vert_start_pos[1] );
                }

                // the dif in base times the dif in multiplier
                air_combo_score = ((current_base - vert_start_base) * (current_mult - vert_start_mult));

                if ( air_combo_score >= vert_score )
                {
                    vert_score = air_combo_score;
                }
            }
        }
        else
        {
            // landed from vert ( not really landed... could still revert to manual )
            if ( last_event == CRCD(0xb39b4f1b,"vert_air") )
            {
                int air_combo_mult=0;

                if ( vert_set == 1 )
                {
                    vert_end_pos = GetObject()->GetPos();
                    air_combo_score = ((current_base - vert_start_base) * (current_mult - vert_start_mult));
                    air_combo_mult = (current_mult - vert_start_mult);
                    vert_set = 2;
                }

                // air combo messages
                if ( air_combo_mult > 1 )
                {
                    if ( air_combo_score != 0 )
                    {
                        Script::CStruct* p_params = new Script::CStruct();
                        p_params->AddInteger( "score", air_combo_score );
                        Script::RunScript( Script::GenerateCRC( "show_vert_combo_message" ), p_params );
                        delete p_params;
                    }
                }
                air_combo_score=0;
            }
            // not vert related
            else
            {
                vert_set = 0;
                vert_height = 0;
                vert_score = 0;
            }
        }

        // check for jump takeoffs and landings
        if ( (state == AIR) && (!mpSkaterStateComponent->GetFlag( VERT_AIR )) )
        {
            new_event = CRCD(0x439f4704,"air");
            if ( last_event != CRCD(0x439f4704,"air") )
            {
                if ( !mpSkaterStateComponent->GetFlag(IS_BAILING) )
                {
                    jump_set = 1;
                    jump_start_pos = GetObject()->GetPos();
                }
            }
        }
        else
        {
            if ( last_event == CRCD(0x439f4704,"air") )
            {
                if ( jump_set == 1 )
                {
                    jump_end_pos = GetObject()->GetPos();
                    jump_set = 2;
                }
            }
            else
            {
                jump_set = 0;
            }
        }
    }
    else
    {
        jump_set = 0;
    }
    
    // check for triggered goals
    bool beat_one = false;
    for (int i=0; i < NUM_STATS_GOALS; i++)
    {
        // Combo goals
        if ( stat_goals[i].goaltype == CRCD(0x4ec3cfb5,"combo") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    int value = stat_goals[i].value;
                    if ( current_score >= value )
                    {
                        ShowStatsMessage(i);
                        beat_one=true;
                    }
                }
            }
        }

        // Multiplier goals
        if ( stat_goals[i].goaltype == CRCD(0x5b6556a4,"multiplier") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    int value = stat_goals[i].value;
                    if ( current_mult >= value )
                    {
                        ShowStatsMessage(i);
                        beat_one=true;
                    }
                }
            }
        }

        // Vert Distance
        if ( stat_goals[i].goaltype == CRCD(0xc3ec3eed,"vertdist") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    if ( vert_set == 2 )
                    {
                        float value = float(stat_goals[i].value);
                        float vert_dist = 1;
                        
                        // set Y values to zero
                        vert_end_pos[1] = 0;
                        vert_start_pos[1] = 0;
    
                        vert_dist = Mth::Distance(vert_end_pos, vert_start_pos);
                        //printf("vert_dist = %f value = %f \n", (vert_dist/12), value );
                        
                        if ( vert_dist >= (12*value) ) // value is in feet, vert_dist is in inches
                        {
                            ShowStatsMessage(i);
                            beat_one=true;
                        }
                    }
                }
            }
        }

        // Vert Height
        if ( stat_goals[i].goaltype == CRCD(0x430b80c4,"vertheight") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    if ( vert_set != 0 )
                    {
                        float value = float(stat_goals[i].value);
                        
                        if ( vert_height >= (12*value) ) // value is in feet, vert_height is in inches
                        {
                            ShowStatsMessage(i);
                            beat_one=true;
                        }
                    }
                }
            }
        }

        // Vert Score
        if ( stat_goals[i].goaltype == CRCD(0x1dbbb148,"vertscore") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    if ( vert_set == 2 )
                    {
                        int value = stat_goals[i].value;
                        
                        if ( vert_score >= value )
                        {
                            ShowStatsMessage(i);
                            beat_one=true;
                        }
                    }
                }
            }
        }

        // Vert Spin
        if ( stat_goals[i].goaltype == CRCD(0x509aca95,"vertspin") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    if ( vert_set != 0 )
                    {
                        float value = float(stat_goals[i].value);
                        
                        if ( vert_spin >= value )
                        {
                            ShowStatsMessage(i);
                            beat_one=true;
                        }
                    }
                }
            }
        }

        // Jump Distance
        if ( stat_goals[i].goaltype == CRCD(0x8f6f9f05,"olliedist") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    if ( jump_set == 2 )
                    {
                        float value = float(stat_goals[i].value);
                        float jump_dist = 1;
                        Mth::Vector end;
						Mth::Vector start;
						
                        // grab start and end points minus Y values
                        end[0] = jump_end_pos[0];
                        end[1] = 0;
                        end[2] = jump_end_pos[2];
			end[3] = 1.0f;

                        start[0] = jump_start_pos[0];
                        start[1] = 0;
                        start[2] = jump_start_pos[2];
                        start[3] = 1.0f;

                        jump_dist = Mth::Distance(end, start);
                        //printf("jump_dist = %f value = %f \n", (jump_dist/12), value );
                        
                        if ( jump_dist >= (12*value) ) // value is in feet, jump_dist is in inches
                        {
                            ShowStatsMessage(i);
                            beat_one=true;
                        }
                    }
                }
            }
        }

        // Jump Height
        if ( stat_goals[i].goaltype == CRCD(0x39e7537b,"highollie") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    if ( jump_set == 2 )
                    {
                        float value = float(stat_goals[i].value);
                        float jump_high = 1;
                        
                        jump_high = ( jump_end_pos[1] - jump_start_pos[1] );
                        
                        if ( jump_high >= (12*value) ) // value is in feet, jump_high is in inches
                        {
                            ShowStatsMessage(i);
                            beat_one=true;
                        }
                    }
                }
            }
        }

        // Jump Drop
        if ( stat_goals[i].goaltype == CRCD(0x7e064ad0,"olliedrop") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    if ( jump_set == 2 )
                    {
                        float value = float(stat_goals[i].value);
                        float jump_low = 1;
                        
                        jump_low = ( jump_start_pos[1] - jump_end_pos[1] );
                        
                        if ( jump_low >= (12*value) ) // value is in feet, jump_low is in inches
                        {
                            ShowStatsMessage(i);
                            beat_one=true;
                        }
                    }
                }
            }
        }

        // Num Fliptricks
        if ( stat_goals[i].goaltype == CRCD(0x364e8c16,"numfliptricks") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    int value = (stat_goals[i].value);
                    
                    if ( num_fliptricks >= value )
                    {
                        ShowStatsMessage(i);
                        beat_one=true;
                    }
                }
            }
        }

        // Num grabtricks
        if ( stat_goals[i].goaltype == CRCD(0x68703ae9,"numgrabs") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {
                    int value = (stat_goals[i].value);
                    
                    if ( num_grabtricks >= value )
                    {
                        ShowStatsMessage(i);
                        beat_one=true;
                    }
                }
            }
        }

        // String Counter
        if ( stat_goals[i].goaltype == CRCD(0x9068b4e5,"stringcount") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {   
                    int value = (stat_goals[i].value);
                    const char* string = (stat_goals[i].v_string);
                    //printf("%s", string );

                    if ( pScore->CountStringMatches(string) >= value )
                    {
                        ShowStatsMessage(i);
                        beat_one=true;
                    }
                }
            }
        }

        // Trick Counter
        if ( stat_goals[i].goaltype == CRCD(0x69b0f836,"trickcount") )
        {
            if (!stat_goals[i].complete)
            {
                if (!stat_goals[i].shown)
                {   
                    int value = (stat_goals[i].value);
                    uint32 trick = (stat_goals[i].trick);
                    int num_taps = (stat_goals[i].taps);
                    
                    /*int tricktimes = pScore->GetCurrentNumberOfOccurrencesByName(trick, 0, false, num_taps);
                    if ( tricktimes > 0)
                    {
                        printf("CountTrickMatches = %i\n", tricktimes );
                    }*/
                    
                    if ( pScore->GetCurrentNumberOfOccurrencesByName(trick, 0, false, num_taps) >= value )
                    {
                        ShowStatsMessage(i);
                        beat_one=true;
                    }
                }
            }
        }
    }
    
    if (beat_one)
    {
        Script::RunScript( Script::GenerateCRC( "showed_stat_message_sound" ) );
    }

    vert_spin = 0;
    last_event = new_event;
    last_score = current_score;
    last_base = current_base;
    last_mult = current_mult;
    
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CStatsManagerComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
/*
        // @script | DoSomething | does some functionality
		case 0xbb4ad101:		// DoSomething
			DoSomething();
			break;

        // @script | ValueIsTrue | returns a boolean value
		case 0x769260f7:		// ValueIsTrue
		{
			return ValueIsTrue() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		break;
*/
        
        // @script | StatsManager_ReInit | reinitializes stats manger. used when starting new career/story
        case 0x82ce30ce: // StatsManager_ReInit
		{
			RefreshFromStructure( pParams );
		}
		break;

        // @script | StatsManager_Reset | resets stats manger. used when reinitializing skater
        case 0x57879c96: //StatsManager_Reset
		{
			Bail();
		}
		break;

        // @script | StatsManager_ActivateGoals | activates stats goals
        case 0xee59073c: // StatsManager_ActivateGoals
		{
			stat_goals_on = true;
            Script::RunScript( Script::GenerateCRC( "console_unhide" ) );
		}
		break;

        // @script | StatsManager_DeactivateGoals | deactivates stats goals
        case 0xf1745991: // StatsManager_DeactivateGoals
		{
			stat_goals_on = false;
            Script::RunScript( Script::GenerateCRC( "console_hide" ) );
		}
		break;
        
        // @script | StatsManager_GetGoalStatus | returns status of stats goal with provided index
        case 0x78e7d855: // StatsManager_GetGoalStatus
        {
            int index;
            int status;
            pParams->GetInteger( NONAME, &index, Script::ASSERT );
            status = stat_goals[index].complete;
            pScript->GetParams()->AddInteger( CRCD(0x84ff9ae3,"status"), status );
        }
        break;

        // @script | StatsManager_UnlockAmGoals | unlocks amateur stats goals
        case 0xb341b5a5: // StatsManager_UnlockAmGoals
        {
            for (int i=0; i < NUM_STATS_GOALS; i++)
            {
                if ( stat_goals[i].complete == 2 )
                {
                    stat_goals[i].complete = 0;
                }
            }
        }
        break;

        // @script | StatsManager_UnlockProGoals | unlocks pro stats goals
        case 0xb7fac894: // StatsManager_UnlockProGoals
        {
            for (int i=0; i < NUM_STATS_GOALS; i++)
            {
                if ( stat_goals[i].complete == 3 )
                {
                    stat_goals[i].complete = 0;
                }
            }
        }
        break;

        default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStatsManagerComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CStatsManagerComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*	Example:
	p_info->AddInteger(CRCD(0x7cf2a233,"m_never_suspend"),m_never_suspend);
	p_info->AddFloat(CRCD(0x519ab8e0,"m_suspend_distance"),m_suspend_distance);
	*/
	
// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CStatsManagerComponent::SetSkater( CSkater* p_skater )
{
	mpSkater = p_skater;
	
	if( p_skater == NULL )
	{
		mpSkaterStateComponent = NULL;
        mpSkaterPhysicsControlComponent = NULL;
		//mpSkaterPhysicsControlComponent = NULL;
	}
	else
	{
		mpSkaterStateComponent = GetSkaterStateComponentFromObject(mpSkater);
		Dbg_Assert(mpSkaterStateComponent);

        mpSkaterPhysicsControlComponent = GetSkaterPhysicsControlComponentFromObject(mpSkater);
		Dbg_Assert(mpSkaterPhysicsControlComponent);
		
		// non-local clients will not have a CSkaterPhysicsControlComponent
		//mpSkaterPhysicsControlComponent = GetSkaterPhysicsControlComponentFromObject(mpSkater);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CStatsManagerComponent::SetTrick( char *trick_name, int base_score, bool is_switch )
{
    if ( base_score != last_base_score )
    {
        last_trick_name = trick_name;
//        printf("trick_name = %s\n", trick_name );
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStatsManagerComponent::CheckTimedRecord( uint32 checksum, float time, bool final )
{
    if ( !(Mdl::Skate::Instance()->GetGameMode()->IsTrue( CRCD(0x1ded1ea4,"is_career") )) )
    {
        return;
    }

    if ( !stat_goals_on )
    {
        return;
    }
    
    bool beat_one = false;
    for (int i=0; i < NUM_STATS_GOALS; i++)
    {
        if (!stat_goals[i].complete)
        {
            if (!stat_goals[i].shown)
            {
                int value = stat_goals[i].value;

                switch ( checksum )
                {
                    case 0x0ac90769: //"noseManual"
                    case 0xef24413b: //"manual"
                        if ( stat_goals[i].goaltype == CRCD(0xd9ffc141,"manualtime") )
                        {
                            if ( time >= value )
                            {
                                ShowStatsMessage(i);
                                beat_one=true;
                            }
                        }
                        break;
                    case 0x255ed86f: //"grind"
                    case 0x8d10119d: //"slide"
                        if ( stat_goals[i].goaltype == CRCD(0xc1336019,"grindtime") )
                        {
                            if ( time >= value )
                            {
                                ShowStatsMessage(i);
                                beat_one=true;
                            }
                        }
                        break;
                    case 0xa549b57b: //"lip"
                        if ( stat_goals[i].goaltype == CRCD(0xb67cff14,"liptime") )
                        {
                            if ( time >= value )
                            {
                                ShowStatsMessage(i);
                                beat_one=true;
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
        }
    }

    if (beat_one)
    {
        Script::RunScript( Script::GenerateCRC( "showed_stat_message_sound" ) );
    }
    
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
                                                                    
void CStatsManagerComponent::Bail()
{
    Script::RunScript( Script::GenerateCRC( "stats_message_bail" ) );

    for (int i=0; i < NUM_STATS_GOALS; i++)
    {
        if (!stat_goals[i].complete)
        {
            if (stat_goals[i].shown)
            {
                stat_goals[i].shown = false;
            }
        }
    }

    vert_set = 0;
    jump_set = 0;
    num_fliptricks=0;
    num_grabtricks=0;
    jump_start_pos = jump_end_pos;
    vert_start_pos = vert_end_pos;
    
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStatsManagerComponent::Land()
{
    if ( !(Mdl::Skate::Instance()->GetGameMode()->IsTrue( CRCD(0x1ded1ea4,"is_career") )) )
    {
        return;
    }

    if ( !stat_goals_on )
    {
        return;
    }
    
    Script::CStruct* p_params = new Script::CStruct();

    Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile( 0 );

    uint32 stat;
    bool cleared=false;
    
    for (int i=0; i < NUM_STATS_GOALS; i++)
    {
        if (!stat_goals[i].complete)
        {
            if (stat_goals[i].shown)
            {
                if (!cleared)
                {
                    // clear current messages
                    Script::RunScript( Script::GenerateCRC( "stats_message_land" ) );
                    cleared = true;
                }

                // set goal as complete
                stat_goals[i].complete = 1;

                // increase proper stat
                stat = ( stat_goals[i].stattype );

                switch ( stat )
                {
                    case 0xb1fc0722: //"manual_balance"
                        p_params->AddString( "text", Script::GetString( CRCD(0x69f2596,"manual_increase_text") ) );
                        break;
                    case 0xf73a13e3: //"rail_balance"
                        p_params->AddString( "text", Script::GetString( CRCD(0x6e7a996d,"rail_increase_text") ) );
                        break;
                    case 0xae798769: //"lip_balance"
                        p_params->AddString( "text", Script::GetString( CRCD(0x1f1bcdef,"lip_increase_text") ) );
                        break;
                    case 0xf0d90109: //"speed"
                        p_params->AddString( "text", Script::GetString( CRCD(0x1686ebd3,"speed_increase_text") ) );
                        break;
                    case 0x9b65d7b8: //"ollie"
                        p_params->AddString( "text", Script::GetString( CRCD(0xa8386951,"ollie_increase_text") ) );
                        break;
                    case 0x439f4704: //"air"
                        p_params->AddString( "text", Script::GetString( CRCD(0xa523df6c,"air_increase_text") ) );
                        break;
                    case 0x6dcb497c: //"flip_speed"
                        p_params->AddString( "text", Script::GetString( CRCD(0x585887ca,"flip_increase_text") ) );
                        break;
                    case 0x9016b4e7: //"switch"
                        p_params->AddString( "text", Script::GetString( CRCD(0x1a6b74ea,"switch_increase_text") ) );
                        break;
                    case 0xedf5db70: //"spin"
                        p_params->AddString( "text", Script::GetString( CRCD(0xb959cc23,"spin_increase_text") ) );
                        break;
                    case 0xaf895b3f: //"run"
                        p_params->AddString( "text", Script::GetString( CRCD(0xa17ac668,"run_increase_text") ) );
                        break;
                    default:
                        break;
                }

                // display message
                p_params->AddInteger( "got_it", 1 );
                Script::RunScript( Script::GenerateCRC( "show_stats_message" ), p_params );
                
                if ( bump_stats )
                {
                    // update skater stats
                    int value = ( pProfile->GetStatValue( stat ) + 1 );
                	pProfile->SetPropertyValue( stat, value );
                }
            }
        }
    }

    if (cleared)
    {
        printf("updating skater stats\n");
        mpSkater->UpdateStats(pProfile);
        
        if (!showed_info)
        {
            Game::CGoalManager* pGoalManager = Game::GetGoalManager();
            Dbg_Assert( pGoalManager );
            if ( pGoalManager->CanStartGoal() )
            {
                Script::RunScript( Script::GenerateCRC( "beat_first_stat_goal" ) );
                showed_info = true;
            }
        }
        Script::RunScript( Script::GenerateCRC( "stat_play_win_sound" ) );
    }
    
    delete p_params;
    num_fliptricks=0;
    num_grabtricks=0;
    
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStatsManagerComponent::ShowStatsMessage( int index )
{
    Script::CStruct* p_params = new Script::CStruct();
    p_params->AddString( "string", stat_goals[index].text );
    p_params->AddInteger( "value", stat_goals[index].value );
    if ( stat_goals[index].v_string )
    {
        p_params->AddString( "vstring", stat_goals[index].v_string );
    }
    p_params->AddInteger( "index", index );
    
    Script::RunScript( Script::GenerateCRC( "show_stats_message" ), p_params );
    stat_goals[index].shown = true;
    delete p_params;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStatsManagerComponent::SetSpin( float angle )
{
    //printf("angle = %f\n", angle);
    vert_spin = angle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStatsManagerComponent::SetTrickType( uint32 type )
{
    switch ( type )
    {
        case 0xba15fc7f: //"fliptrick"
            num_fliptricks++;
//            printf("num_fliptricks = %i\n", num_fliptricks );
            break;
        case 0xb9ae26d2: //"grabtrick"
            num_grabtricks++;
//            printf("num_grabtricks = %i\n", num_grabtricks );
            break;
        default:
            break;
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStatsManagerComponent::StatGoalsOn()
{
    stat_goals_on = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStatsManagerComponent::StatGoalsOff()
{
    stat_goals_on = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CStatsManagerComponent::StatGoalGetStatus(int index)
{
    return (stat_goals[index].complete);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStatsManagerComponent::StatGoalSetStatus(int index, int value)
{
    stat_goals[index].complete = value;
    if ( value == 1 )
    {
        // This means we must be loading stats, so don't show the first stat message!
        showed_info = true;
    }
}

}


================================================
FILE: Code/Gel/Components/StatsManagerComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       StatsManagerComponent.h
//* OWNER:          Zac Drake
//* CREATION DATE:  5/29/03
//****************************************************************************

#ifndef __COMPONENTS_STATSMANAGERCOMPONENT_H__
#define __COMPONENTS_STATSMANAGERCOMPONENT_H__

#include 
#include 

#include 

#include 

#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_STATSMANAGER CRCD(0x4ff8dac6,"StatsManager")

//  Standard accessor macros for getting the component either from within an object, or 
//  given an object				 
#define		GetStatsManagerComponent() ((Obj::CStatsManagerComponent*)GetComponent(CRC_STATSMANAGER))
#define		GetStatsManagerComponentFromObject(pObj) ((Obj::CStatsManagerComponent*)(pObj)->GetComponent(CRC_STATSMANAGER))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
    class CSkaterStateComponent;
    class CSkaterPhysicsControlComponent;

const static int NUM_STATS_GOALS=70;

struct stat_goal {
    uint32 goaltype;
    uint32 stattype;
    int value;
    bool shown;
    int complete;
    const char* text;
    const char* v_string;
    uint32 trick;
    int taps;
};


class CStatsManagerComponent : public CBaseComponent
{
public:
    CStatsManagerComponent();
    virtual ~CStatsManagerComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
	//virtual	void 					Finalize();
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
    void							SetSkater( CSkater* p_skater );

    void							SetTrick( char *trick_name, int base_score, bool is_switch );

    void                            CheckTimedRecord( uint32 checksum, float time, bool final );
    void                            Bail();
    void                            Land();
    void                            SetSpin( float angle );
    void                            SetTrickType( uint32 type );
    
    int                             StatGoalGetStatus(int index);
    void                            StatGoalSetStatus(int index, int value);

    void                            StatGoalsOn();
    void                            StatGoalsOff();
    
    bool                            m_restarted_this_frame;
    
private:
    
    stat_goal                       stat_goals[NUM_STATS_GOALS];
    bool                            stat_goals_on;
    bool                            bump_stats;
    bool                            showed_info;

    int                             current_score;
    int                             last_score;
    
    int                             current_base;
    int                             last_base;

    int                             current_mult;
    int                             last_mult;

    char                            *last_trick_name;
    int                             last_base_score;

    int                             dif_level;

    uint32                          new_event;
    uint32                          last_event;

    Mth::Vector                     vert_start_pos;
    Mth::Vector                     vert_end_pos;
    int                             vert_start_base;
    int                             vert_start_mult;
    int                             vert_score;
    int                             vert_set;
    float                           vert_height;

    float                           vert_spin;

    Mth::Vector                     jump_start_pos;
    Mth::Vector                     jump_end_pos;
    int                             jump_set;

    int                             num_fliptricks;
    int                             num_grabtricks;

    int                             air_combo_score;

    CSkater*						mpSkater;
    CSkaterStateComponent*			mpSkaterStateComponent;
    CSkaterPhysicsControlComponent* mpSkaterPhysicsControlComponent;
    //CSkaterScoreComponent*			mpSkaterScoreComponent;
    CSkaterCorePhysicsComponent*	mpSkaterCorePhysicsComponent;

    void                            ShowStatsMessage( int index );
    
};

}

#endif


================================================
FILE: Code/Gel/Components/StreamComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       StreamComponent.cpp
//* OWNER:          ???
//* CREATION DATE:  ???
//****************************************************************************

// The CStreamComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy streamcomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "Stream" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage

#include 

#include 
#include 
#include 
#include 

#include 

#include 

// TODO:  Refactor this - 
#include 
#include 


namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CStreamComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CStreamComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CStreamComponent::CStreamComponent() : CBaseComponent()
{
	SetType( CRC_STREAM );
	mp_emitter = NULL;
	mp_proxim_node = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CStreamComponent::~CStreamComponent()
{
	// If there is a streaming sound attached, remove it.
	int i;
	for( i = 0; i < MAX_STREAMS_PER_OBJECT; i++ )
	{
		if( mStreamingID[i] )
		{
			Pcm::StopStreamFromID( mStreamingID[i] );
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CStreamComponent::InitFromStructure( Script::CStruct* pParams )
{
	uint32 proxim_node_checksum;

	if (pParams->GetChecksum("ProximNode", &proxim_node_checksum))
	{
		//Dbg_Message("StreamComponent: Looking for ProximNode %s", Script::FindChecksumName(proxim_node_checksum));
		mp_proxim_node = CProximManager::sGetNode(proxim_node_checksum);
		//if (mp_proxim_node)
		//{
		//	Dbg_Message("Found ProximNode %s", Script::FindChecksumName(mp_proxim_node->m_name));
		//}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CStreamComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	InitFromStructure(pParams);


}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CStreamComponent::Update()
{
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CStreamComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{

		case 0xdb05a233:  // Obj_PlayStream
			PlayStream( pParams, pScript );
			break;
		
		case 0xa738aeb8:  // Obj_StopStream
		{
			// eventually want to be able to specify channel on here...
			int i;
			for ( i = 0; i < MAX_STREAMS_PER_OBJECT; i++ )
			{
				if ( mStreamingID[ i ] )
				{
					Pcm::StopStreamFromID( mStreamingID[ i ] );
					mStreamingID[ i ] = 0;
				}
			}
			break;
		}
		

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CStreamComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to C......Component::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*	Example:
	p_info->AddInteger("m_never_suspend",m_never_suspend);
	p_info->AddFloat("m_suspend_distance",m_suspend_distance);
	*/
	
// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector	CStreamComponent::GetClosestEmitterPos( Gfx::Camera *pCamera )
{
	if (mp_emitter && pCamera)
	{
		return mp_emitter->GetClosestPoint(pCamera->GetPos());
	}
	else
	{
		return GetObject()->m_pos;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CStreamComponent::GetClosestDropoffPos( Gfx::Camera *pCamera, Mth::Vector & dropoff_pos )
{
	if (pCamera && mp_proxim_node)
	{
		dropoff_pos = mp_proxim_node->GetClosestIntersectionPoint(pCamera->GetPos());
		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


// Returns 0 if no stream slots empty, or the index + 1
int CStreamComponent::StreamSlotEmpty( void )
{
	int j;
	for ( j = 0; j < MAX_STREAMS_PER_OBJECT; j++ )
	{
		if ( !mStreamingID[ j ] )
		{
			return ( j + 1 );
		}
	}
	return ( 0 );
}


bool CStreamComponent::PlayStream( Script::CScriptStructure *pParams, Script::CScript *pScript )
{
	
	if ( !StreamSlotEmpty( ) )
	{
	
		#ifdef	__NOPT_ASSERT__
		printf ("Playing too many streams on object\n");
		#endif
										  
		return ( false ); // can only play 3 stream on each object max!!!
	}
	uint32 streamChecksum;
	uint32 emitterChecksum;
	uint32 dropoffFuncChecksum;
	uint32 id = 0;
	float dropoff = 0.0f;
	float volume = 100.0f;
	float pitch = 100.0f;
	int priority = STREAM_DEFAULT_PRIORITY;
	EDropoffFunc dropoffFunction = DROPOFF_FUNC_STANDARD;
	pParams->GetFloat( 0xf6a36814, &volume ); // "vol"
	pParams->GetFloat( 0xd8604126, &pitch ); // "pitch"
	pParams->GetInteger( 0x9d5923d8, &priority ); // priority
	pParams->GetChecksum( 0x40c698af, &id ); // id
	if ( pParams->GetFloat( 0xff2020ec, &dropoff ) ) // "dropoff"
	{
		dropoff = FEET_TO_INCHES( dropoff );
	}
	if ( pParams->GetChecksum( 0xc6ac50a, &dropoffFuncChecksum ) ) // "dropoff_function"
	{
		dropoffFunction = Sfx::GetDropoffFunctionFromChecksum( dropoffFuncChecksum );
	}
	if (pParams->GetChecksum( 0x8a7132ce, &emitterChecksum ) ) // "emitter"
	{
		mp_emitter = CEmitterManager::sGetEmitter(emitterChecksum);
	}
	else
	{
		mp_emitter = NULL;
	}
	if ( pParams->GetChecksum( NONAME, &streamChecksum ) )
	{
		int use_pos_info = 1;
		pParams->GetInteger( "use_pos_info", &use_pos_info, Script::NO_ASSERT );
		//printf ("Playing stream 0x%x on object %s\n",streamChecksum,Script::FindChecksumName(GetObject()->GetID()));
		return ( Pcm::PlayStreamFromObject( this, streamChecksum, dropoff, volume, pitch, priority, use_pos_info, dropoffFunction, id ) );
	}
	return ( false );
}





	
}


================================================
FILE: Code/Gel/Components/StreamComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       StreamComponent.h
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#ifndef __COMPONENTS_STREAMCOMPONENT_H__
#define __COMPONENTS_STREAMCOMPONENT_H__

#include 
#include 

#include 

#define		CRC_STREAM CRCD(0xf1641e3,"stream")
#define		GetStreamComponent() ((Obj::CStreamComponent*)GetComponent(CRC_STREAM))
#define		GetStreamComponentFromObject(pObj) ((Obj::CStreamComponent*)(pObj)->GetComponent(CRC_STREAM))

#define MAX_STREAMS_PER_OBJECT	3


namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Gfx
{
	class	Camera;
}

namespace Obj
{
	// Forward declarations
	class CEmitterObject;
	class CProximNode;

class CStreamComponent : public CBaseComponent
{
public:
    CStreamComponent();
    virtual ~CStreamComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	Mth::Vector						GetClosestEmitterPos( Gfx::Camera *pCamera );

	bool							HasProximNode() const { return mp_proxim_node != NULL; }
	void							ClearProximNode() { mp_proxim_node = NULL; }								// Needed for early proxim node cleanup
	bool							GetClosestDropoffPos( Gfx::Camera *pCamera, Mth::Vector & dropoff_pos );	// returns true if position defined

	static CBaseComponent*			s_create();


public:
	// PUBLIC FOR NOW!!!!
	uint32		mStreamingID[ MAX_STREAMS_PER_OBJECT ];

	
private:	
	bool		PlayStream( Script::CStruct *pParams, Script::CScript *pScript );
	// if this is non-zero, the object has streaming sound from the CD attached to it.
	// it is cleared and set in music.cpp.
	// Returns 0 if no stream slots empty, or the index + 1
	int			StreamSlotEmpty();

	CEmitterObject *mp_emitter;
	CProximNode *	mp_proxim_node;

// End of Stream stuff
////////////////////////////////////////////
	
	
	
};

}

#endif


================================================
FILE: Code/Gel/Components/SuspendComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SuspendComponent.cpp
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#include 

#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 		// <<<<<<<<<<<   GET RID OF THIS, use the NX camera's directly
#include 	// <<<<<<<<<<<   GET RID OF THIS TOO!

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// This static function is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
CBaseComponent* CSuspendComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSuspendComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// And this one is the optional "register" function that provides on-time initilization
// usually of a component manager
void	CSuspendComponent::s_register()
{
	// Create and initilize the manager
	SuspendManager::Manager::Create();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSuspendComponent::CSuspendComponent() : CBaseComponent()
{
	SetType( CRC_SUSPEND );
	
	m_suspend_distance=Script::GetFloat(CRCD(0xad501995,"DefaultMovingObjectSuspendDistance")) * 12.0f;
	m_suspend_distance_squared=m_suspend_distance*m_suspend_distance;
	m_never_suspend=true;
	m_no_suspend_count = 3; // default to not allowing suspensions in the first 3 frames

	// Mick: We don't want to display things before they have done at least one frame of
	// animation, because they start out in their default position (the "Blair Witch" position)
	// so, we set a default number of animations that we MUST do before we are allowed to
	// not animate.
	// This also overrides the SkipLogic test, so we do a frame of logic
	// which fixes problems where something is moving to the first node of a path
	
	// Since the first couple of frames of the game are hidden behind the loading screen
	// we don't need to worry about framerate glitched due to the large 
	// number of objects being created at start.	
	
	// I Initially had this at 1, which seemed fine
	// however, when you change levels, it needs to be at 3
	// I'm not sure why - it might be good to look into this later.
	m_initial_animations = 3;	 	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSuspendComponent::~CSuspendComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSuspendComponent::InitFromStructure( Script::CStruct* pParams )
{
	// anything that has a suspend distance will allow suspension
	if (pParams->GetFloat(CRCD(0xe4279ba2,"SuspendDistance"),&m_suspend_distance))
	{
		if (m_suspend_distance == 0.0f)
		{
			// if zero suspend dist, then try to get it from lod_dist1
			if (!pParams->GetFloat(CRCD(0x4eedfae7,"Lod_dist1"),&m_suspend_distance))
			{
				// else get the default distance
				m_suspend_distance=Script::GetFloat(CRCD(0xad501995,"DefaultMovingObjectSuspendDistance"));
			}
		}
	
		// if we've got a suspend distance, then convert it into inches
		m_suspend_distance *= 12.0f;
		m_suspend_distance_squared=m_suspend_distance*m_suspend_distance;
		m_never_suspend=false;
	}
	
	// suspension can be overridden by the "NeverSuspend" flag
	if (pParams->ContainsFlag(CRCD(0xcb839dc1,"NeverSuspend")))
	{
		m_never_suspend=true;
	}

	// Get the LOD distances from the node
	if (Script::GetInt(CRCD(0x95edc6f6,"NoLOD"),false))					 
	{
		m_lod_dist[0] = 1000000;
		m_lod_dist[1] = 1000001;
		m_lod_dist[2] = 1000002;  // Note, this is used as a magic number to turn off the high level culling
		m_lod_dist[3] = 1000003;
	}
	else
	{
		Dbg_Assert( pParams );
		m_lod_dist[0] = 50;
		m_lod_dist[2] = 1000001;
		m_lod_dist[3] = 1000002;
		pParams->GetFloat(CRCD(0x4eedfae7,"lod_dist1"),&m_lod_dist[0]);
		m_lod_dist[1] = m_lod_dist[0] + 75;						// default for LOD dist 2 is 75 feet beyond 1
		pParams->GetFloat(CRCD(0xd7e4ab5d,"lod_dist2"),&m_lod_dist[1]);
		pParams->GetFloat(CRCD(0xa0e39bcb,"lod_dist3"),&m_lod_dist[2]);
		pParams->GetFloat(CRCD(0x3e870e68,"lod_dist4"),&m_lod_dist[3]);
		// LOD dist is in feet
		m_lod_dist[0] *= 12.0f;
		m_lod_dist[1] *= 12.0f;
		m_lod_dist[2] *= 12.0f;
		m_lod_dist[3] *= 12.0f;
		if ( m_lod_dist[0]>=m_lod_dist[1] )
		{
			Script::PrintContents( pParams );
			Dbg_MsgAssert(0,("lod_dist1 (%f) >= lod_dist2 (%f) in node",m_lod_dist[0],m_lod_dist[1]));
		}
		if ( m_lod_dist[1]>=m_lod_dist[2] )
		{
			Script::PrintContents( pParams );
			Dbg_MsgAssert(0,("lod_dist2 (%f) >= lod_dist3 (%f) in node",m_lod_dist[1],m_lod_dist[2]));
		}
		if ( m_lod_dist[2]>=m_lod_dist[3] )
		{
			Script::PrintContents( pParams );
			Dbg_MsgAssert(0,("lod_dist3 (%f) >= lod_dist4 (%f) in node",m_lod_dist[2],m_lod_dist[3]));
		}

#if defined( __PLAT_XBOX__ ) || defined( __PLAT_NGC__ )
		// Some platform specific changes to the LOD stuff
		m_lod_dist[0]		*= Script::GetFloat( 0xd3d072f9 /* LOD0DistanceMultiplier */ );
		m_lod_dist[1]		*= Script::GetFloat( 0x0432f2a1 /* LOD1DistanceMultiplier */ );
#endif

	}
}

void	CSuspendComponent::Finalize()
{
	// Might be NULL, if we don't have a model
	mp_model_component = GetModelComponentFromObject( GetObject() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSuspendComponent::Update()
{
	// this funtion does not need to be called, so flag it as such
	CBaseComponent::Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent::EMemberFunctionResult CSuspendComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSuspendComponent::Suspend(bool suspend)
{
	// does nothing, as we need to keep checking for unsuspension
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// returns true if we skip logic on this frame
// this can be true for a number of reasons
// but mostly boils down to LOD
bool CSuspendComponent::SkipLogic()
{
	return m_skip_logic;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// returns true if we skip logic on this frame
// this can be true for a number of reasons
// but mostly boils down to LOD
bool CSuspendComponent::SkipRender()
{
		
	if ( m_camera_distance_squared > m_lod_dist[1]*m_lod_dist[1] )
	{
		return true;
	}	
	
	return false;

}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSuspendComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSuspendComponent::GetDebugInfo"));
	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	

	p_info->AddInteger(CRCD(0x7cf2a233,"m_never_suspend"),m_never_suspend);
	p_info->AddFloat(CRCD(0x519ab8e0,"m_suspend_distance"),m_suspend_distance);
	p_info->AddFloat(CRCD(0x340a53d3,"distance_to_camera"),sqrtf(m_camera_distance_squared));
	p_info->AddFloat(CRCD(0x266306b9,"m_lod_dist_0"),m_lod_dist[0]);
	p_info->AddFloat(CRCD(0x5164362f,"m_lod_dist_1"),m_lod_dist[1]);
	p_info->AddFloat(CRCD(0xc86d6795,"m_lod_dist_2"),m_lod_dist[2]);
	p_info->AddFloat(CRCD(0xbf6a5703,"m_lod_dist_3"),m_lod_dist[3]);
	
	p_info->AddInteger(CRCD(0xd9872f4b,"SKIPLOGIC_RETURNS"),SkipLogic());
	p_info->AddInteger(CRCD(0xd55771a6,"SKIPRENDER_RETURNS"),SkipRender());
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSuspendComponent::should_animate( float *p_dist )
{
	bool should_animate = true;  

	// Mick:  don't update the skeleton if it's a long way from the camera
//	Gfx::Camera * p_camera = Nx::CViewportManager::sGetActiveCamera( 0 );
//	if (p_camera)
	{
//		Mth::Vector cam_pos = p_camera->GetPos();
//		cam_pos = cam_pos - GetObject()->GetPos();
//		float dist = cam_pos.LengthSqr();

		float dist = m_camera_distance_squared;

		// Save this dist if a valid pointer was passed.
		if( p_dist )
		{
			*p_dist = dist;
		}

		if (dist > m_lod_dist[0] * m_lod_dist[0])		// dist at what we stop animating			
		{
		    should_animate = false;
		}

		// update animation intermittently
		// more so as distance from camera increases

		else
		{
#ifndef TESTING_GUNSLINGER

#if defined( __PLAT_XBOX__ ) || defined( __PLAT_NGC__ )
			float interleave2 = Script::GetFloat( 0xdb120fb5 /* AnimLODInterleave2 */ );
			float interleave4 = Script::GetFloat( 0x3271aa80 /* AnimLODInterleave4 */ );
			interleave2 *= interleave2;
			interleave4 *= interleave4;
#else
			float interleave2 = 500.0f * 500.0f;
			float interleave4 = 800.0f * 800.0f;
#endif

			m_interleave++;
			should_animate = false;				
			if (dist > interleave4)						  
			{
				// 1 in 4 for distance 500-1000
				if (!(m_interleave&3))
				{
					should_animate = true;
				}
			}
			else if (dist > interleave2)
			{
				// 1 in 2 for 200 to 500
				if ((m_interleave&1))
				{
					should_animate = true;
				}
			 }
			 else
			 {
				// always animate if < 200
				should_animate = true;
			 }
#endif		
		}

	}
	
	if (m_initial_animations)
	{
		should_animate = true;
	}

	return should_animate;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Set Model's Active/Inactive flag based on other flags
// and if the model is actually visible or not (with culling and occlusion)
// and upon the load_dist2 distance  
void CSuspendComponent::CheckModelActive()
{
	if ( mp_model_component && mp_model_component->GetModel() )
	{
		bool active = false;
		if ( GetObject()->GetFlags() & CObject::vINVISIBLE )
		{
			// just leave it inactive
		}
		else
		{
			// quick check for if the NoLOD flag is set
			// Note use of magic number (but this is only for debugging/screenshots)
			if ( m_lod_dist[2] == 1000002 )
			{
				active =  true ;
			}
			else
			{
				// PATCH for multiple viewports
				// always return true, until we sort out the per-viewport visibility (which might not be needed)				   
				if ( Nx::CViewportManager::sGetNumActiveViewports() > 1 )
				{
					active = true;
				}
				else if ( mp_model_component->HasRefObject() )
				{
					// GJ:  a ref model doesn't actually have a valid bounding sphere
					// so don't let Nx::CEngine::sIsVisible() be called...
					// (alternatively, we could fake a large bounding sphere
					// but that seems kludgy)
					active = true;
				}
				else
				{
					float dist = m_camera_distance_squared;
					
					if ( dist < m_lod_dist[1] * m_lod_dist[1] )
					{		
						Mth::Vector sphere = mp_model_component->GetModel()->GetBoundingSphere();
						Mth::Vector position = sphere;
						position[3] = 1.0f;
						position *= GetObject()->GetMatrix();
						position += GetObject()->GetPos();
						active = ( Nx::CEngine::sIsVisible( position, sphere[3]) );
					}
					else
					{
						active = false;
					}
				}
			}
		}
		bool	was_active = mp_model_component->GetModel()->GetActive();
		mp_model_component->GetModel()->SetActive( active);		
		// If we've just turned the model component back on, then run the update on it
		// so we set it to the right place
		// otherwise it will be in the old place for a frame
		// TODO:  This all might be better IN the modelcomponent
		if (active && !was_active)
		{
			mp_model_component->Update();			
		}
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CSuspendComponent::GetDistanceSquaredToCamera()
{
	Gfx::Camera * p_camera = Nx::CViewportManager::sGetActiveCamera( 0 );
	if ( p_camera )
	{
		return ( p_camera->GetPos() - GetObject()->GetPos() ).LengthSqr();
	}

	printf( "No active camera!\n" );

	// no camera?!?
	return 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/



// The following is the SuspendOb manager, which iterates over the suspendob components 
// to suspend them to whatever they are suspended to

namespace SuspendManager
{
// The purpose of this manager is just to update all the suspended objects, by calling
// CMovingObject::SuspendToObject on all objects that have had Obj_SuspendToObject run on them.
// It is a manager so that it can be given a task with a priority such that it is called
// after all the objects are updated.
// The reason the suspend logic cannot be done in the object update is because sometimes the parent's 
// update might happen after the child's, causing a frame lag. This was making the gorilla appear
// behind the tram seat in the zoo when the tram was being skitched. (TT2324)

DefineSingletonClass( Manager, "Suspended object Manager" )

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::Manager()
{
	mp_task = new Tsk::Task< Manager > ( s_code, *this,
										Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_LOCKED_OBJECT_MANAGER_LOGIC );

	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	mlp_manager->AddLogicTask( *mp_task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager()
{
	delete mp_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::s_code( const Tsk::Task< Manager >& task )
{

	Mdl::SPreCalculatedObjectUpdateInfo* p_info = Mdl::Skate::Instance()->GetPreCalculatedObjectUpdateInfo();
	
	Obj::CSuspendComponent *p_suspend_component = static_cast( Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRC_SUSPEND ));
	while( p_suspend_component )
	{

		// Set camera dist to zero for the purposes of LOD
		p_suspend_component->m_camera_distance_squared = 0;
		p_suspend_component->m_skip_logic = false;
	
		if (p_suspend_component->m_no_suspend_count)
		{
			p_suspend_component->m_no_suspend_count--;
			continue;
		}

		if ( p_info->mDoNotSuspendAnything )
		{
		}	
		else if ( p_suspend_component->m_never_suspend )
		{
		}
		else if ( p_suspend_component->m_initial_animations )
		{
			p_suspend_component->m_initial_animations--;
		}
		else
		{
			p_suspend_component->m_camera_distance_squared = Mth::DistanceSqr(p_suspend_component->GetObject()->GetPos(),p_info->mActiveCameraPosition);
		
			if ( p_suspend_component->m_camera_distance_squared > p_suspend_component->m_suspend_distance_squared )
			{
				p_suspend_component->m_skip_logic = true;
			}	
			else
			{
				p_suspend_component->m_skip_logic = false;
			}
		}
		// suspend or unsuspend the object and its components
		// based on the new state of m_skip_logic
		if (p_suspend_component->m_skip_logic)
		{
			if (!p_suspend_component->GetObject()->IsSuspended())
			{
				p_suspend_component->GetObject()->Suspend(true);
			}
		}
		else
		{
			if (p_suspend_component->GetObject()->IsSuspended())
			{
				p_suspend_component->GetObject()->Suspend(false);
			}
		}
	
		// If it has a model, then check for LOD to nothing	
		if (p_suspend_component->mp_model_component)
		{
			if (p_suspend_component->m_initial_animations)
			{
				if (p_suspend_component->mp_model_component->GetModel())
				{
					p_suspend_component->mp_model_component->GetModel()->SetActive(true);
				}
			}
			else
			{
				p_suspend_component->CheckModelActive();	
			}
		}
		
		p_suspend_component = static_cast( p_suspend_component->GetNextSameType());
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/



}
	
}


================================================
FILE: Code/Gel/Components/SuspendComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SuspendComponent.h
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#ifndef __COMPONENTS_SUSPENDCOMPONENT_H__
#define __COMPONENTS_SUSPENDCOMPONENT_H__

#include 
#include 
#include 
#include 

#include 

#define		CRC_SUSPEND CRCD(0xce0ca665,"Suspend")
#define		GetSuspendComponent() ((Obj::CSuspendComponent*)GetComponent(CRC_SUSPEND))
#define		GetSuspendComponentFromObject(pObj) ((Obj::CSuspendComponent*)(pObj)->GetComponent(CRC_SUSPEND))

namespace Script
{
    class CScript;
    class CStruct;
}


              
namespace Obj
{

	class	CModelComponent;


namespace SuspendManager
{

class Manager  : public Spt::Class
{
	Tsk::Task< Manager > *mp_task;
	static	Tsk::Task< Manager >::Code 	s_code;

public:
	Manager();
	~Manager();
	
private:	
	DeclareSingletonClass( Manager );
};

}



class CSuspendComponent : public CBaseComponent
{
	friend class SuspendManager::Manager;
	 
public:
    CSuspendComponent();
    virtual ~CSuspendComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
	virtual	void					Finalize();	
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual	void					Suspend(bool suspend);
	

	void 							GetDebugInfo(Script::CStruct *p_info);
	bool							SkipLogic();
	bool 							SkipRender();

	
	static CBaseComponent*			s_create();
	static void						s_register();

public:		
	void 							CheckModelActive();
	float							GetDistanceSquaredToCamera();

private:
	bool							m_never_suspend;
	float							m_suspend_distance;
	float							m_suspend_distance_squared; // K: For a fast distance-squared check
	float							m_camera_distance_squared;  // current distance from camera
	int								m_no_suspend_count;	// number of initial frames we cannot suspend in

	CModelComponent* 				mp_model_component;

	bool							m_skip_logic;
	bool							m_skip_render;
	
// public during the transition:
public:
//protected:
	float							m_lod_dist[4];		// Mick: LOD is in the moving object for now....	
	int								m_initial_animations;		// must do at least these initial animations

protected:
	int								m_interleave;	// Mick: counter for interleaving animation at a distance
public:  // just for now	
	bool							should_animate( float *p_dist = NULL );
};




}

#endif


================================================
FILE: Code/Gel/Components/TriggerComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       TriggerComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/3/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CTriggerComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CTriggerComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTriggerComponent::CTriggerComponent() : CBaseComponent()
{
	SetType( CRC_TRIGGER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CTriggerComponent::~CTriggerComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTriggerComponent::InitFromStructure( Script::CStruct* pParams )
{
	m_pos_last_frame = GetObject()->GetPos();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTriggerComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTriggerComponent::Update()
{
	if (m_pos_last_frame == GetObject()->GetPos()) return;
	
	if (GetObject()->HasTeleported())
	{
		m_pos_last_frame = GetObject()->GetPos();
		return;
	}
	
	CFeeler feeler;
	
	feeler.m_start = m_pos_last_frame;
	feeler.m_end = GetObject()->GetPos();
	
	feeler.SetIgnore(0, mFD_NON_COLLIDABLE | mFD_TRIGGER);
	
	feeler.SetCallback(s_through_trigger_callback);
	feeler.SetCallbackData(this);
	
	feeler.GetCollision();
	
	m_pos_last_frame = GetObject()->GetPos();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CTriggerComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | TriggerType | checks if the trigger type is the 
        // same as the specified type.  can also accept an array of types
        // to check
        // @uparmopt name | trigger type
        // @uparmopt [] | array of trigger types
		case CRCC(0x3a717382, "TriggerType"):
		{
			Script::CArray* pArray = NULL;
			pParams->GetArray(NO_NAME, &pArray);
			if (pArray)
			{
				for (int n = pArray->GetSize(); n--; )
				{
					if (m_latest_trigger_event_type == Script::GetInteger(pArray->GetChecksum(n))) return CBaseComponent::MF_TRUE;
				}
				return CBaseComponent::MF_FALSE;
			}
			else
			{
				int type;
				pParams->GetInteger(NO_NAME, &type, Script::ASSERT);
				return m_latest_trigger_event_type == type ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;   
			} 	
			break;
		}
		
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTriggerComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CTriggerComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTriggerComponent::CheckFeelerForTrigger ( TriggerEventType type, CLineFeeler& feeler )
{
	if (!feeler.GetTrigger() || !feeler.GetNodeChecksum()) return;

	if (feeler.GetCallbackObject())
	{
		CNodeArrayComponent* p_node_array_component = GetNodeArrayComponentFromObject(feeler.GetCallbackObject());
		
		TripTrigger(type, feeler.GetNodeChecksum(), p_node_array_component ? p_node_array_component->GetNodeArray() : NULL, feeler.GetCallbackObject());
	}
	else if (feeler.IsMovableCollision())
	{
		if (feeler.GetMovingObject())
		{
			CNodeArrayComponent* p_node_array_component = GetNodeArrayComponentFromObject(feeler.GetMovingObject());
			TripTrigger(type, feeler.GetNodeChecksum(), p_node_array_component ? p_node_array_component->GetNodeArray() : NULL, feeler.GetMovingObject());
		}
	}
	else
	{
		TripTrigger(type, feeler.GetNodeChecksum());
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTriggerComponent::CheckFeelerForTrigger ( TriggerEventType type, CRectFeeler& feeler )
{
	for (int n = feeler.GetNumCollisionSurfaces(); n--; )
	{
		const Nx::S2DCollSurface& coll_surface = feeler.GetCollisionSurface(n);
		if (!coll_surface.trigger) continue;
		
		// only trip the node's trigger if we haven't yet
		int m;
		for (m = n + 1; m < feeler.GetNumCollisionSurfaces(); m++)
		{
			const Nx::S2DCollSurface& handled_coll_surface = feeler.GetCollisionSurface(m);
			if (!coll_surface.trigger) continue;
			if (coll_surface.node_name == handled_coll_surface.node_name) break;
		}
		if (m == feeler.GetNumCollisionSurfaces())
		{
			TripTrigger(type, coll_surface.node_name);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTriggerComponent::TripTrigger ( TriggerEventType type, uint32 node_checksum, Script::CArray* p_node_array, CCompositeObject* p_object)
{
	int node;
	Script::CStruct* p_node = NULL;
	
	// find the triggered node
	if (!p_node_array)
	{
		node = SkateScript::FindNamedNode(node_checksum);
		Dbg_MsgAssert(node != -1, ("Can't find node %s, maybe .SCN out of sync with .QN ?",Script::FindChecksumName(node_checksum)));
		p_node_array = Script::GetArray(CRCD(0xc472ecc5, "NodeArray"));
		p_node = p_node_array->GetStructure(node);
	}
	else
	{
		for (node = p_node_array->GetSize(); node--; )
		{
			p_node = p_node_array->GetStructure(node);
			
			uint32 checksum;
			p_node->GetChecksum(CRCD(0xa1dc81f9, "name"), &checksum);
			if (checksum == node_checksum) break;
		}
	}
	
	Dbg_MsgAssert(node != -1, ("Cannot find node %s", Script::FindChecksumName(node_checksum)));
	
	// get the triggered object's trigger script parameters
	Script::CStruct* p_model_trigger_script_params = NULL;
	if (p_object)
	{
		Script::CStruct* p_object_node = Script::GetArray(CRCD(0xc472ecc5, "NodeArray"))->GetStructure(SkateScript::FindNamedNode(p_object->GetID()));
		if (p_object_node)
		{
			p_object_node->GetStructure(CRCD(0x5180a4db, "ModelTriggerScriptParams"), &p_model_trigger_script_params);
		}
	}
	
	// get the trigger script
	uint32 script_checksum = 0;
	p_node->GetChecksum(CRCD(0x2ca8a299, "TriggerScript"), &script_checksum);
	if (script_checksum == 0) return;
	
	// if this is a net game, don't trigger things from "Absent in netgame" nodes
	if (Mdl::Skate::Instance()->ShouldBeAbsentNode(p_node)) return;
	
	m_latest_trigger_event_type = type;
	
	GetObject()->SpawnAndRunScript(
		script_checksum,
		node,
		p_node->ContainsFlag(CRCD(0x20209c31, "NetEnabled")),
		p_node->ContainsFlag(CRCD(0x23627fd7, "Permanent")),
		p_model_trigger_script_params
	);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTriggerComponent::s_through_trigger_callback ( CFeeler* p_feeler )
{
	static_cast< CTriggerComponent* >(p_feeler->GetCallbackData())->CheckFeelerForTrigger(TRIGGER_THROUGH, *p_feeler);
}

}


================================================
FILE: Code/Gel/Components/TriggerComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       TriggerComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/3/3
//****************************************************************************

#ifndef __COMPONENTS_TRIGGERCOMPONENT_H__
#define __COMPONENTS_TRIGGERCOMPONENT_H__

#include 
#include 

#include 

#include 
#include 

#define		CRC_TRIGGER CRCD(0xe594f0a2, "Trigger")

#define		GetTriggerComponent() ((Obj::CTriggerComponent*)GetComponent(CRC_TRIGGER))
#define		GetTriggerComponentFromObject(pObj) ((Obj::CTriggerComponent*)(pObj)->GetComponent(CRC_TRIGGER))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	// trigger types for skating/walking
	enum ESkaterTriggerType
	{
		TRIGGER_SKATE_OFF_EDGE = 1,
		TRIGGER_JUMP_OFF,
		TRIGGER_LAND_ON,
		TRIGGER_SKATE_OFF,
		TRIGGER_SKATE_ONTO,
		TRIGGER_BONK,
		TRIGGER_LIP_ON,
		TRIGGER_LIP_OFF,
		TRIGGER_LIP_JUMP,
	};

class CTriggerComponent : public CBaseComponent
{
	// users of CTriggerComponent define their own trigger event types, however 0 is reserved for the event of passing through trigger polys
	enum
	{
		TRIGGER_THROUGH = 0
	};

public:
	typedef int TriggerEventType;
	
public:
    CTriggerComponent();
    virtual ~CTriggerComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							CheckFeelerForTrigger ( TriggerEventType type, CLineFeeler& p_feeler );
	void							CheckFeelerForTrigger ( TriggerEventType type, CRectFeeler& feeler );
	void							TripTrigger ( TriggerEventType type, uint32 node_checksum, Script::CArray* p_node_array = NULL, CCompositeObject* p_object = NULL);
	
private:
	static void						s_through_trigger_callback ( CFeeler* p_feeler );
	
private:
	Mth::Vector						m_pos_last_frame;
	TriggerEventType				m_latest_trigger_event_type;
};

}

#endif


================================================
FILE: Code/Gel/Components/VehicleSoundComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VehicleSoundComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  7/2/3
//****************************************************************************

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#define MESSAGE(a) { printf("M:%s:%i: %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPI(a) { printf("D:%s:%i: " #a " = %i\n", __FILE__ + 15, __LINE__, a); }
#define DUMPB(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a ? "true" : "false"); }
#define DUMPF(a) { printf("D:%s:%i: " #a " = %g\n", __FILE__ + 15, __LINE__, a); }
#define DUMPE(a) { printf("D:%s:%i: " #a " = %e\n", __FILE__ + 15, __LINE__, a); }
#define DUMPS(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPP(a) { printf("D:%s:%i: " #a " = %p\n", __FILE__ + 15, __LINE__, a); }
#define DUMPC(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, Script::FindChecksumName(a)); }
#define DUMPV(a) { printf("D:%s:%i: " #a " = %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z]); }
#define DUMP4(a) { printf("D:%s:%i: " #a " = %g, %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z], (a)[W]); }
#define DUMPM(a) { DUMP4((a)[X]); DUMP4((a)[Y]); DUMP4((a)[Z]); DUMP4((a)[W]); }
#define DUMP2(a) { printf("D:%s:%i " #a " = ", __FILE__ + 15, __LINE__); for (int n = 32; n--; ) { printf("%c", ((a) & (1 << n)) ? '1' : '0'); } printf("\n"); }
#define MARK { printf("K:%s:%i: %s\n", __FILE__ + 15, __LINE__, __PRETTY_FUNCTION__); }
#define PERIODIC(n) for (static int p__ = 0; (p__ = ++p__ % (n)) == 0; )

#ifdef __NOPT_ASSERT__
	#define DEBUG_OUTPUT(a) \
		{ \
			if (Script::GetInteger(CRCD(0x54f22205, "DebugPlayerVehicleEngineSound")) \
				|| Script::GetInteger(CRCD(0xdf03ebbf, "DebugPlayerVehicleTireSound"))) \
			{ \
				printf("[DebugEngineSound]: "); \
				printf a; \
				printf("\n"); \
			} \
		}
	#define DEBUG_ENGINE(a) \
		{ \
			if (Script::GetInteger(CRCD(0x54f22205, "DebugPlayerVehicleEngineSound"))) \
			{ \
				printf("[DebugEngineSound]: "); \
				printf a; \
				printf("\n"); \
			} \
		}
	#define DEBUG_TIRES(a) \
		{ \
			if (Script::GetInteger(CRCD(0xdf03ebbf, "DebugPlayerVehicleTireSound"))) \
			{ \
				printf("[DebugTireSound]: "); \
				printf a; \
				printf("\n"); \
			} \
		}
	#define DEBUG_COLLISION(a) \
		{ \
			if (Script::GetInteger(CRCD(0xc2ddef08, "DebugPlayerVehicleCollisionSound"))) \
			{ \
				printf("[DebugCollisionSound]: "); \
				printf a; \
				printf("\n"); \
			} \
		}
#else
	#define DEBUG_OUTPUT(a)
	#define DEBUG_ENGINE(a)
	#define DEBUG_TIRES(a)
	#define DEBUG_COLLISION(a)
#endif

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CVehicleSoundComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CVehicleSoundComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CVehicleSoundComponent::CVehicleSoundComponent() : CBaseComponent()
{
	SetType( CRC_VEHICLESOUND );
	
	mp_vehicle_component = NULL;
	mp_sound_component = NULL;
	
	m_sound_setup_struct = NULL;
	
	m_use_default_sounds = false;
	
	m_effective_speed = 0.0f;
	m_effective_gear = 0;
	m_gear_shift_time_stamp = 0;
	m_airborne_time_stamp = 0;
	m_effective_tire_slip_vel = 0.0f;
	m_latest_collision_sound_time_stamp = 0;
	
	m_engine_sound_id = 0;
	m_tire_sound_id = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CVehicleSoundComponent::~CVehicleSoundComponent()
{
	if (m_engine_sound_id)
	{
		Sfx::CSfxManager::Instance()->StopSound(m_engine_sound_id);
		m_engine_sound_id = 0;
	}
	if (m_tire_sound_id)
	{
		Sfx::CSfxManager::Instance()->StopSound(m_tire_sound_id);
		m_tire_sound_id = 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleSoundComponent::InitFromStructure( Script::CStruct* pParams )
{
	m_use_default_sounds = Ed::CParkEditor::Instance()->UsingCustomPark();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleSoundComponent::Finalize()
{
	mp_vehicle_component = GetVehicleComponentFromObject(GetObject());
	mp_sound_component = GetSoundComponentFromObject(GetObject());
	
	Dbg_Assert(mp_vehicle_component);
	Dbg_Assert(mp_sound_component);
	
	fetch_script_parameters();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleSoundComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleSoundComponent::Update()
{
	DEBUG_OUTPUT(("-- frame start --"));
	
	#ifdef __NOPT_ASSERT__
	if (Script::GetInteger(CRCD(0xcf5f4e16, "DynamicPlayerVehicleSound")))
	{
		fetch_script_parameters();
	}
	#endif
	
	if (!m_sound_setup_struct) return;
	
	update_engine_sounds();
	
	update_tire_sounds();
	
	update_collision_sounds();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CVehicleSoundComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleSoundComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CVehicleSoundComponent::GetDebugInfo"));
	
	p_info->AddChecksum(CRCD(0x2a19e830, "engine_sound"), m_engine_sound_checksum);
	p_info->AddChecksum(CRCD(0xe798fe80, "tire_sound"), m_tire_sound_checksum);
	p_info->AddStructure(CRCD(0x36fa68d2, "collide_sound"), m_collide_sound_struct);


	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleSoundComponent::update_engine_sounds (   )
{
	float frame_length = Tmr::FrameLength();
	
	CVehicleComponent::SWheel* p_wheels = mp_vehicle_component->mp_wheels;
	
	// determine the smallest of all the drive wheels' rotational velocity
	float min_vel = 1e20f;
	for (int n = CVehicleComponent::vVP_NUM_WHEELS; n--; )
	{
		if (!p_wheels[n].drive) continue;
		min_vel = Mth::Min(min_vel, Mth::Abs(p_wheels[n].rotvel) * p_wheels[n].radius);
	}
	float frame_effective_speed = IPS_TO_MPH(min_vel);
	
	// adjust effective speed based on throttle position
	bool throttle = (mp_vehicle_component->m_controls.throttle || mp_vehicle_component->m_controls.reverse) && !mp_vehicle_component->m_controls.brake;
	if (!throttle)
	{
		frame_effective_speed *= get_global_param(CRCD(0xec67bc97, "idle_engine_effective_speed_factor"));
	}
	
	// smooth out frame-to-frame fluxuations
	m_effective_speed = Mth::Lerp(frame_effective_speed, m_effective_speed, get_global_param(CRCD(0x2a7095b, "effective_speed_lerp_rate")) * frame_length);
	if (m_effective_speed < frame_effective_speed)
	{
		m_effective_speed = Mth::ClampMax(m_effective_speed + get_global_param(CRCD(0xca5efd91, "effective_speed_adjust_up_rate")) * frame_length, m_effective_speed);
	}
	else
	{
		m_effective_speed = Mth::ClampMin(m_effective_speed - get_global_param(CRCD(0x77c8e7c8, "effective_speed_adjust_down_rate")) * frame_length, m_effective_speed);
	}
	DEBUG_ENGINE(("speed: %.2f mph", m_effective_speed));
	
	// determine the appropriate gear for this frame
	if (mp_vehicle_component->m_num_wheels_in_contact != 0)
	{
		if (m_effective_gear < m_num_gears - 1 && m_effective_speed > m_gears[m_effective_gear].upshift_point)
		{
			m_effective_gear++;
			m_gear_shift_time_stamp = Tmr::GetTime();
			DEBUG_ENGINE(("upshift to gear: %i", m_effective_gear));
		}
		else if (m_effective_gear > 0 && m_effective_speed < m_gears[m_effective_gear].downshift_point)
		{
			m_effective_gear--;
			m_gear_shift_time_stamp = Tmr::GetTime();
			DEBUG_ENGINE(("downshift to gear: %i", m_effective_gear));
		}
	}
	else if (mp_vehicle_component->m_air_time > get_global_param(CRCD(0xc0f451d4, "engine_airborne_delay")))
	{	 
		// when airborne, drop to lowest gear and use no gear shift velocity damping
		m_effective_gear = 0;
	}
	DEBUG_ENGINE(("gear: %i", m_effective_gear + 1));
	
	// calculate a volume adjustment based on the time since the last gear shift
	float gear_shift_vol_factor = Mth::ClampMax(
		Mth::LinearMap(
			get_global_param(CRCD(0xbfa32092, "gear_shift_min_vol_factor")),
			1.0f,
			Tmr::ElapsedTime(m_gear_shift_time_stamp),
			0.0f,
			get_global_param(CRCD(0xfb6bd20e, "gear_shift_vol_adjustment_duration"))
		),
		1.0f
	);
		
	// calculate this frame's engine rpm based on the current gear and speed
	float frame_engine_rpm = Mth::LinearMap(
		m_gears[m_effective_gear].bottom_rpm,
		m_gears[m_effective_gear].top_rpm,
		m_effective_speed,
		m_effective_gear ? m_gears[m_effective_gear - 1].upshift_point : 0.0f,
		m_gears[m_effective_gear].upshift_point
	);
	// DEBUG_ENGINE(("speed top: %.2f", m_gears[m_effective_gear].upshift_point));
	// DEBUG_ENGINE(("speed bottom: %.2f", m_effective_gear ? m_gears[m_effective_gear - 1].upshift_point : 0.0f));
	// DEBUG_ENGINE(("rpm top: %.2f", m_gears[m_effective_gear].top_rpm));
	// DEBUG_ENGINE(("rpm bottom: %.2f", m_gears[m_effective_gear].bottom_rpm));
	
	// smooth out the engine rpm changes (causes nice shifting effects)
	m_effective_engine_rpm = Mth::Lerp(
		m_effective_engine_rpm,
		frame_engine_rpm,
		get_global_param(CRCD(0x3dd9c040, "engine_rpm_lerp_rate")) * frame_length
	);
	DEBUG_ENGINE(("engine RPM: %.2f", m_effective_engine_rpm));
	
	// calculate a number between 0.0f and 1.0f which will scale the engine sound's pitch and volume
	float sound_factor = Mth::Clamp(
		Mth::LinearMap(
			0.0f,
			1.0f,
			m_effective_engine_rpm,
			get_param(CRCD(0x05ac84a3, "MinEngineRPM")),
			get_param(CRCD(0x95df9449, "MaxEngineRPM"))
		),
		0.0f,
		1.0f
	);
	
	// setup volume
	Sfx::sVolume volume;
	Sfx::CSfxManager::Instance()->SetVolumeFromPos(&volume, GetObject()->GetPos(), Sfx::CSfxManager::Instance()->GetDropoffDist(m_engine_sound_checksum));
	float volume_percent = gear_shift_vol_factor * Mth::Lerp(
		get_param(CRCD(0xb8f81277, "MinEngineVol")),
		get_param(CRCD(0x288b029d, "MaxEngineVol")),
		sound_factor
	);
	volume.PercentageAdjustment(volume_percent);
	DEBUG_ENGINE(("engine volume: %.0f", volume_percent));
	
	// setup pitch
	float pitch = Mth::Lerp(
		get_param(CRCD(0x2660af3b, "MinEnginePitch")),
		get_param(CRCD(0x9f46344a, "MaxEnginePitch")),
		sound_factor
	);
	DEBUG_ENGINE(("engine pitch: %.0f", pitch));
	
	// play or adjust sound
	if (!m_engine_sound_id)
	{
		m_engine_sound_id = Sfx::CSfxManager::Instance()->PlaySound(m_engine_sound_checksum, &volume, pitch, NULL);
	}
	else
	{
		Sfx::CSfxManager::Instance()->UpdateLoopingSound(m_engine_sound_id, &volume, pitch);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleSoundComponent::update_tire_sounds (   )
{
	CVehicleComponent::SWheel* p_wheels = mp_vehicle_component->mp_wheels;
	
	// determine the largest of all the wheels' slip velocity
	float max_slip_vel = 0.0f;
	
	if (mp_vehicle_component->m_controls.brake || mp_vehicle_component->m_controls.handbrake)
	{
		for (int n = CVehicleComponent::vVP_NUM_WHEELS; n--; )
		{
			// handbrake skid states do not depend on the tire's slip velocity, so we check the slip velocity ourselves
			if ((p_wheels[n].state == CVehicleComponent::SWheel::SLIPPING || p_wheels[n].state == CVehicleComponent::SWheel::SKIDDING)
				|| ((p_wheels[n].state == CVehicleComponent::SWheel::HANDBRAKE_LOCKED || p_wheels[n].state == CVehicleComponent::SWheel::HANDBRAKE_THROTTLE)
				&& p_wheels[n].slip_vel > p_wheels[n].max_static_velocity))
			{
				max_slip_vel = Mth::Max(max_slip_vel, p_wheels[n].slip_vel);
			}
		}
	}
	
	m_effective_tire_slip_vel = Mth::Lerp(
		m_effective_tire_slip_vel,
		max_slip_vel,
		get_global_param(CRCD(0x95fbe774, "tire_slip_lerp_rate")) * Tmr::FrameLength()
	);
	DEBUG_TIRES(("tire slip velocity: %.2f", m_effective_tire_slip_vel));
	
	float sound_factor = Mth::Clamp(
		m_effective_tire_slip_vel / get_param(CRCD(0x28f1aad0, "FullTireSlipVelocity")),
		0.0f,
		1.0f
	);
	
	// setup volume
	Sfx::sVolume volume;
	Sfx::CSfxManager::Instance()->SetVolumeFromPos(&volume, GetObject()->GetPos(), Sfx::CSfxManager::Instance()->GetDropoffDist(m_tire_sound_checksum));
	float volume_percent = Mth::Lerp(
		get_param(CRCD(0x43e6cdf2, "MinTireVol")),
		get_param(CRCD(0x42ea5746, "MaxTireVol")),
		sound_factor
	);
	volume.PercentageAdjustment(volume_percent);
	DEBUG_TIRES(("tire volume: %.0f", volume_percent));
	
	// setup pitch
	float pitch = Mth::Lerp(
		get_param(CRCD(0x76f8f76e, "MinTirePitch")),
		get_param(CRCD(0xe68be784, "MaxTirePitch")),
		sound_factor
	);
	DEBUG_TIRES(("tire pitch: %.0f", pitch));
	
	if (!volume.IsSilent())
	{
		if (!m_tire_sound_id)
		{
			m_tire_sound_id = Sfx::CSfxManager::Instance()->PlaySound(m_tire_sound_checksum, &volume, pitch, NULL);
		}
		else
		{
			Sfx::CSfxManager::Instance()->UpdateLoopingSound(m_tire_sound_id, &volume, pitch);
		}
	}
	else if (m_tire_sound_id)
	{
		Sfx::CSfxManager::Instance()->StopSound(m_tire_sound_id);
		m_tire_sound_id = 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleSoundComponent::update_collision_sounds (   )
{
	if (Tmr::ElapsedTime(m_latest_collision_sound_time_stamp) < get_global_param(CRCD(0x71b4b9fe, "collision_mute_delay"))) return;
	
	float sound_factor = mp_vehicle_component->m_max_normal_collision_impulse / get_param(CRCD(0xcea59d0b, "FullCollision"));
	if (sound_factor > get_global_param(CRCD(0xf6981837, "collision_cutoff_factor")))
	{
		DEBUG_COLLISION(("collision: %.2f", mp_vehicle_component->m_max_normal_collision_impulse));
		
		m_collide_sound_struct->AddFloat(CRCD(0x9e497fc6, "Percent"), 100.0f * sound_factor);
		mp_sound_component->PlayScriptedSound(m_collide_sound_struct);
		
		m_latest_collision_sound_time_stamp = Tmr::GetTime();
		
		Script::CStruct* p_params = new Script::CStruct;
		p_params->AddFloat(CRCD(0x29461e27, "Strength"), Mth::Clamp(sound_factor, 0.0f, 1.0f));
		mp_vehicle_component->mp_skater->SelfEvent(CRCD(0xc4a60423, "Vehicle_BodyCollision"), p_params);
		delete p_params;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleSoundComponent::fetch_script_parameters (   )
{
	// get sound setup structure
	Script::CStruct* sound_setups_struct = Script::GetStructure(CRCD(0x3c2da7f5, "PlayerVehicleSounds"), Script::ASSERT);
	sound_setups_struct->GetStructure(mp_vehicle_component->GetSoundSetupChecksum(), &m_sound_setup_struct, Script::ASSERT);
	
	// extract sound checksum
	m_sound_setup_struct->GetChecksum(CRCD(0x306f8a9f, "EngineSound"), &m_engine_sound_checksum);
	m_sound_setup_struct->GetChecksum(CRCD(0x667fa96a, "TireSound"), &m_tire_sound_checksum);
	
	// extract gear information
	
	Script::CArray* gear_array;
	m_sound_setup_struct->GetArray(CRCD(0x83273e6b, "Gears"), &gear_array, Script::ASSERT);
	
	m_num_gears = gear_array->GetSize();
	Dbg_MsgAssert(m_num_gears <= vVS_MAX_NUM_GEARS, ("PlayerVehicleSound '%s' exceeds the maximum number of %i allowed gears", Script::FindChecksumName(mp_vehicle_component->GetSoundSetupChecksum()), vVS_MAX_NUM_GEARS));
	for (int n = 0; n < m_num_gears; n++)
	{
		Script::CStruct* gear_struct = gear_array->GetStructure(n);
		
		gear_struct->GetFloat(CRCD(0xfad20901, "UpshiftPoint"), &m_gears[n].upshift_point, Script::ASSERT);
		gear_struct->GetFloat(CRCD(0xfcf5c8a2, "DownshiftPoint"), &m_gears[n].downshift_point, Script::ASSERT);
        gear_struct->GetFloat(CRCD(0x735b5ce4, "BottomRPM"), &m_gears[n].bottom_rpm, Script::ASSERT);
        gear_struct->GetFloat(CRCD(0xe59a89a6, "TopRPM"), &m_gears[n].top_rpm, Script::ASSERT);
		
		Dbg_MsgAssert(m_gears[n].upshift_point > m_gears[n].downshift_point, ("In gear %i of PlayerVehicleSound '%s,' DownshiftPoint exceeds UpshiftPoint", n + 1, Script::FindChecksumName(mp_vehicle_component->GetSoundSetupChecksum())));
		Dbg_MsgAssert(!n || m_gears[n].downshift_point < m_gears[n - 1].upshift_point, ("In gear %i of PlayerVehicleSound '%s', DownshiftPoint exceeds lower gear's UpshiftPoint", n + 1, Script::FindChecksumName(mp_vehicle_component->GetSoundSetupChecksum())));
	}
	
	// extract collision sound structure
	m_sound_setup_struct->GetStructure(CRCD(0xbf8e0ace, "CollideSound"), &m_collide_sound_struct, Script::ASSERT);
	
	// if we're in a create-a-goal, swap sound checksums for default sound checksums, as the true sounds may not be loaded
	if (m_use_default_sounds)
	{
		Script::CStruct* global_sound_setup = Script::GetStructure(CRCD(0x38ec37ac, "PlayerVehicleSoundGlobalParameters"), Script::ASSERT);
		
		global_sound_setup->GetChecksum(CRCD(0xf5406e1a, "CAGEngineSound"), &m_engine_sound_checksum, Script::ASSERT);
		global_sound_setup->GetChecksum(CRCD(0x60e93a2, "CAGTireSound"), &m_tire_sound_checksum, Script::ASSERT);
		global_sound_setup->GetStructure(CRCD(0x22995285, "CAGCollideSound"), &m_collide_sound_struct, Script::ASSERT);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CVehicleSoundComponent::get_param ( uint32 checksum )
{
	Dbg_Assert(m_sound_setup_struct);
	
	float value;
	m_sound_setup_struct->GetFloat(checksum, &value, Script::ASSERT);
	return value;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CVehicleSoundComponent::get_global_param ( uint32 checksum )
{
	Script::CStruct* global_sound_setup = Script::GetStructure(CRCD(0x38ec37ac, "PlayerVehicleSoundGlobalParameters"), Script::ASSERT);
	
	float value;
	global_sound_setup->GetFloat(checksum, &value, Script::ASSERT);
	return value;
}

}


================================================
FILE: Code/Gel/Components/VehicleSoundComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VehicleSoundComponent.h
//* OWNER:          Dan
//* CREATION DATE:  7/2/3
//****************************************************************************

#ifndef __COMPONENTS_VEHICLESOUNDCOMPONENT_H__
#define __COMPONENTS_VEHICLESOUNDCOMPONENT_H__

#include 
#include 

#include 

#define		CRC_VEHICLESOUND CRCD(0x28c97f3c, "VehicleSound")

#define		GetVehicleSoundComponent() ((Obj::CVehicleSoundComponent*)GetComponent(CRC_VEHICLESOUND))
#define		GetVehicleSoundComponentFromObject(pObj) ((Obj::CVehicleSoundComponent*)(pObj)->GetComponent(CRC_VEHICLESOUND))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CVehicleComponent;
	class CSoundComponent;

class CVehicleSoundComponent : public CBaseComponent
{
private:
	static const int vVS_MAX_NUM_GEARS = 6;
		
public:
    CVehicleSoundComponent();
    virtual ~CVehicleSoundComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
	virtual	void 					Finalize();
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

private:
	void							update_collision_sounds (   );
	void							update_tire_sounds (   );
	void							update_engine_sounds (   );
	
	void							fetch_script_parameters (   );
	float							get_param ( uint32 checksum );
	float							get_global_param ( uint32 checksum );
	
	
private:
	Script::CStruct*				m_sound_setup_struct;
	
	bool							m_use_default_sounds;
	
	uint32							m_engine_sound_id;
	uint32							m_tire_sound_id;
	Script::CStruct*				m_collide_sound_struct;
	
	uint32							m_engine_sound_checksum;
	uint32							m_tire_sound_checksum;
	
	int								m_num_gears;
	struct SGear
	{
		float						upshift_point;
		float						downshift_point;
		float						bottom_rpm;
		float						top_rpm;
	}								m_gears[vVS_MAX_NUM_GEARS];
	
	float							m_effective_speed;
	int								m_effective_gear;
	float							m_effective_engine_rpm;
	Tmr::Time						m_gear_shift_time_stamp;
	Tmr::Time						m_airborne_time_stamp;
	
	float							m_effective_tire_slip_vel;
	
	Tmr::Time						m_latest_collision_sound_time_stamp;
	
	// peer components
	CVehicleComponent*				mp_vehicle_component;
	CSoundComponent*				mp_sound_component;
};

}

#endif


================================================
FILE: Code/Gel/Components/VelocityComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VelocityComponent.cpp
//* OWNER:          SPG
//* CREATION DATE:  7/10/03
//****************************************************************************

// The CEmptyComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy emptycomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "Empty" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage

#include 

#include 
#include 
#include 
#include 
#include 

#include 
namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CVelocityComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CVelocityComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CVelocityComponent::CVelocityComponent() : CBaseComponent()
{
    SetType( CRC_VELOCITY );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CVelocityComponent::~CVelocityComponent()
{   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CVelocityComponent::InitFromStructure( Script::CStruct* pParams )
{
	Mth::Vector vel;
	
	pParams->GetVector(CRCD(0xc4c809e, "vel"), &vel, Script::ASSERT);
	GetObject()->SetVel( vel );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CVelocityComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	//InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVelocityComponent::Finalize()
{
	mp_suspend_component =  GetSuspendComponentFromObject( GetObject() );
}
	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CVelocityComponent::Hide( bool should_hide )
{
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CVelocityComponent::Update()
{
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();

	if (gamenet_man->InNetGame() || !mp_suspend_component->SkipLogic())
	{
		Mth::Vector pos, cur_pos, vel;

		cur_pos = GetObject()->GetPos();
		vel = GetObject()->GetVel();
		pos = cur_pos + ( GetObject()->GetVel() * 0.5f * Tmr::FrameLength());
		GetObject()->SetPos( pos );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CVelocityComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVelocityComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CVelocityComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums
	

// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/VelocityComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VelocityComponent.h
//* OWNER:          SPG
//* CREATION DATE:  07/10/03
//****************************************************************************

#ifndef __COMPONENTS_VELOCITYCOMPONENT_H__
#define __COMPONENTS_VELOCITYCOMPONENT_H__

#include 
#include 

#include 
#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_VELOCITY CRCD(0x41272956,"Velocity")

//  Standard accessor macros for getting the component either from within an object, or 
//  given an object				 
#define		GetVelocityComponent() ((Obj::CVelocityComponent*)GetComponent(CRC_VELOCITY))
#define		GetVelocityComponentFromObject(pObj) ((Obj::CVelocityComponent*)(pObj)->GetComponent(CRC_VELOCITY))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CVelocityComponent : public CBaseComponent
{
public:
    CVelocityComponent();
    virtual ~CVelocityComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void					Hide( bool should_hide );
	virtual void					Finalize();
		
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

private:
	CSuspendComponent*	mp_suspend_component;
};

}

#endif


================================================
FILE: Code/Gel/Components/VibrationComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VibrationComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  2/25/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 

namespace Obj
{
	
// Component giving script control through a composite object over the input pad vibrators of the composite object's input handler.
	
// Only composite objects corresponding to local clients should be given this component.
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CVibrationComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CVibrationComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CVibrationComponent::CVibrationComponent() : CBaseComponent()
{
	SetType( CRC_VIBRATION );
	
	m_active_state = false;
	
	for (int n = vVB_NUM_ACTUATORS; n--; )
	{
		mp_vibration_timers[n].active = false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CVibrationComponent::~CVibrationComponent()
{
	if (mp_input_device)
	{
		// turn off all vibration
		for (int i = vVB_NUM_ACTUATORS; i--; )
		{
			mp_input_device->ActivateActuator(i, 0);
		}
		
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVibrationComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVibrationComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVibrationComponent::Update()
{
	Dbg_Assert(mp_input_device);
	
	if (!m_active_state)
	{
		Suspend(true);
		return;
	}
	
	// check the duration of active actuators
	for (int i = vVB_NUM_ACTUATORS; i--; )
	{
		// actuator inactive or has no associated duration
		if (!mp_vibration_timers[i].active) continue;
		
		// duration not yet up
		if (Tmr::ElapsedTime(mp_vibration_timers[i].start_time) < mp_vibration_timers[i].duration) continue;
		
		// NOTE: ignoring all replay code in VibrationComponent for now
		// Replay::WritePadVibration(i, 0);
		
		// turn off actuator
		mp_input_device->ActivateActuator(i, 0);
		
		mp_vibration_timers[i].active = false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CVibrationComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | Vibrate | 
        // @parm int | Actuator | actuator num
        // @parmopt float | Percent | 0 | 
        // @flag Off | turn off both actuators
        // @parmopt float | Duration | 0 | time to vibrate
		case CRCC(0xdf8a5f0a, "Vibrate"):
		{
			// NOTE: ignoring pause state in VibrationComponent for now
			// Don't let it be switched on if skater is paused
			if (!pParams->ContainsFlag(CRCD(0xd443a2bc, "Off")) && (Mdl::FrontEnd::Instance()->GamePaused() || GetObject()->IsPaused())) break;
		
			Dbg_MsgAssert(mp_input_device, ("No associated input device"));
			
			if (!m_active_state) break;
			
			int actuator_idx = 0;
			pParams->GetInteger(CRCD(0x114fc131, "actuator"), &actuator_idx);
			Dbg_MsgAssert(actuator_idx >= 0 && actuator_idx < vVB_NUM_ACTUATORS, ("%s\nActuator must be between 0 and %d", pScript->GetScriptInfo(), vVB_NUM_ACTUATORS));

			float percent = 0;
			pParams->GetFloat(CRCD(0x9e497fc6, "percent"), &percent);
			Dbg_MsgAssert(percent >= 0 && percent <= 100, ("\n%s\nPercent must be between 0 and 100", pScript->GetScriptInfo()));
		
			if (pParams->ContainsFlag("off"))
			{
				// This will switch off vibration, including if the game is paused.
				// Ie, unpausing won't restore the old vibration. Hooray!
				mp_input_device->StopAllVibrationIncludingSaved();
				
				for (int i = vVB_NUM_ACTUATORS; i--; )
				{
					mp_vibration_timers[i].active = false;
				}
				
				// NOTE: ignoring all replay code in VibrationComponent for now
				// for (int i = vVB_NUM_ACTUATORS; i--; )
				// {
					// Replay::WritePadVibration(i, 0);
				// }
			}	
			else
			{
				// NOTE: ignoring all replay code in VibrationComponent for now
				// Replay::WritePadVibration(Actuator, (int)Percent);
				mp_input_device->ActivateActuator(actuator_idx, static_cast(percent));
			}	
		
			// record duration so that we can later turn off the actuator; a duration of 0 gives an unbounded duration
			float duration = 0;
			if (pParams->GetFloat(CRCD(0x79a07f3f, "duration"), &duration))
			{
				mp_vibration_timers[actuator_idx].active = true;
				mp_vibration_timers[actuator_idx].duration = static_cast(1000.0f * duration);
				mp_vibration_timers[actuator_idx].start_time = Tmr::GetTime();
			}
			else
			{
				mp_vibration_timers[actuator_idx].active = false;
			}
			
			break;
		}
		
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
	
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVibrationComponent::GetDebugInfo ( Script::CStruct *p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CVibrationComponent::GetDebugInfo"));
	
	p_info->AddInteger("m_active_state", m_active_state);
	Script::CArray* p_timers_array = new Script::CArray;
	p_timers_array->SetArrayType(vVB_NUM_ACTUATORS, ESYMBOLTYPE_STRUCTURE);
	for (int i = vVB_NUM_ACTUATORS; i--; )
	{
		Script::CStruct* p_timer_structure = new Script::CStruct;
		p_timer_structure->AddInteger("active", mp_vibration_timers[i].active);
		p_timer_structure->AddInteger("duration", mp_vibration_timers[i].duration);
		p_timers_array->SetStructure(i, p_timer_structure);
	}
	p_info->AddArray("mp_vibration_timers", p_timers_array);

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVibrationComponent::StopAllVibration (   )
{
	if (!m_active_state) return;
	
	Dbg_Assert(mp_input_device);
	
	// NOTE: no real expanation for this, so I'm ignoring it until it breaks the code	
	// #ifndef __PLAT_NGC__
	for (int i = vVB_NUM_ACTUATORS; i--; )
	{
		// NOTE: ignoring all replay code in VibrationComponent for now
		// Replay::WritePadVibration(i, 0);
		
		mp_input_device->ActivateActuator(i, 0);
		
		mp_vibration_timers[i].active = false;
	}
	// #endif // __PLAT_NGC__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVibrationComponent::Reset (   )
{
	Dbg_Assert(mp_input_device);
	
	mp_input_device->ResetActuators();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVibrationComponent::SetActive (   )
{
	if (m_active_state) return;
	
	m_active_state = true;
	Suspend(false);
	
	if (!mp_input_device) return;
	mp_input_device->EnableActuators();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVibrationComponent::SetInactive (   )
{
	if (!m_active_state) return;
	
	m_active_state = false;
	Suspend(true);
	
	if (!mp_input_device) return;
	mp_input_device->StopAllVibrationIncludingSaved();
	mp_input_device->DisableActuators();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVibrationComponent::SetActiveState ( bool state )
{
	if (state == m_active_state) return;
	
	if (state)
	{
		SetActive();
	}
	else
	{
		SetInactive();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVibrationComponent::SetDevice ( SIO::Device* p_input_device )
{
	Dbg_Assert(p_input_device);
	
	mp_input_device = p_input_device;
	
	if (m_active_state)
	{
		mp_input_device->EnableActuators();
	}
	else
	{
		mp_input_device->DisableActuators();
	}
}

}


================================================
FILE: Code/Gel/Components/VibrationComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VibrationComponent.h
//* OWNER:          Dan
//* CREATION DATE:  2/25/3
//****************************************************************************

#ifndef __COMPONENTS_VIBRATIONCOMPONENT_H__
#define __COMPONENTS_VIBRATIONCOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_VIBRATION CRCD(0x404eb936, "Vibration")

#define		GetVibrationComponent() ((Obj::CVibrationComponent*)GetComponent(CRC_VIBRATION))
#define		GetVibrationComponentFromObject(pObj) ((Obj::CVibrationComponent*)(pObj)->GetComponent(CRC_VIBRATION))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CVibrationComponent : public CBaseComponent
{
public:
	static const int vVB_NUM_ACTUATORS = 2;
	
private:	
	// contains the information which defines the state of a vibration actuator
	struct SVibrationTimer
	{
		bool active;
		Tmr::Time start_time;
		Tmr::Time duration;
	};
	
public:
    CVibrationComponent();
    virtual ~CVibrationComponent();

public:
    virtual void            		Update (   );
    virtual void            		InitFromStructure ( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure ( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction ( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo ( Script::CStruct* p_info );

	static CBaseComponent*			s_create (   );
	
	void							StopAllVibration (   );
	void							Reset (   );
	void							SetActive (   );
	void							SetInactive (   );
	void							SetActiveState ( bool state );
	void							SetDevice ( SIO::Device* p_input_device );
	
private:
	bool							m_active_state;
	
	SVibrationTimer					mp_vibration_timers[vVB_NUM_ACTUATORS];
	
	SIO::Device*					mp_input_device;
};

}

#endif


================================================
FILE: Code/Gel/Components/WalkCameraComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       WalkCameraComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  4/9/3
//****************************************************************************

#ifdef TESTING_GUNSLINGER

// Replace the entire contents of this file with the new file.
#include 

#else

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CWalkCameraComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CWalkCameraComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CWalkCameraComponent::CWalkCameraComponent() : CBaseComponent()
{
	SetType( CRC_WALKCAMERA );
	m_last_tripod_pos.Set();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CWalkCameraComponent::~CWalkCameraComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::InitFromStructure( Script::CStruct* pParams )
{
	uint32 target_id = 0 ;
	pParams->GetChecksum("CameraTarget", &target_id, Script::ASSERT);
	
	CCompositeObject* p_target = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(target_id));
	Dbg_MsgAssert(p_target, ("Bad CameraTarget given to WalkCameraComponent"));
		
	set_target(p_target);
	
	m_last_dot = 1.0f;
	m_current_zoom = 1.0f;
	
	m_last_actual_matrix = GetObject()->GetMatrix();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::Finalize()
{
	mp_lookaround_component = GetCameraLookAroundComponentFromObject(GetObject());
	mp_camera_component = GetCameraComponentFromObject(GetObject());
	mp_skater_camera_component = GetSkaterCameraComponentFromObject(GetObject());
	
	Dbg_Assert(mp_lookaround_component);
	Dbg_Assert(mp_camera_component);
	Dbg_Assert(mp_skater_camera_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::Update()
{
	if (!mp_target) return;
	
	// optimization KLUDGE
	if (mp_target_physics_control_component && mp_target_physics_control_component->IsDriving())
	{
		GetObject()->Pause(true);
		return;
	}
	
	if (mp_target->HasTeleported())
	{
		m_instant_count = 3;
	}

	bool instantly;
	if (m_instant_count > 0)
	{
		--m_instant_count;
		instantly = true;
	}
	else if (m_reset)
	{
		instantly = true;
	}
	else
	{
		instantly = false;
	}
	
	mp_camera_component->StoreOldPosition();

	float frame_length = Tmr::FrameLength();
	
	// get input
	float horiz_control = GetInputComponentFromObject(GetObject())->GetControlPad().m_scaled_rightX;

	// restore camera position from last frame, previous to refocusing and collision detection
	GetObject()->GetMatrix() = m_last_actual_matrix;

	Mth::Vector	target_facing = -GetObject()->GetMatrix()[Z];
	target_facing[Y] = 0.0f;
	target_facing.Normalize();
	
	Mth::Vector subject_facing = mp_target->GetMatrix()[Z];
	subject_facing[Y] = 0.0f;
	subject_facing.Normalize();
	
	// two options; we either use our old facing as the target facing or use the subject's facing as the target facing
	bool use_subject_facing = true;
	
	// if in a flush request
	if (m_flush_request_active)
	{
		// always use the subject's matrix
	}
	// if controlling camera
	else if (horiz_control != 0.0f)
	{
		use_subject_facing = false;
	}
	// if the subject's facing is towards the camera
	else if (Mth::DotProduct(target_facing, subject_facing) < cosf(Mth::DegToRad(s_get_param(CRCD(0xb8da8a73, "lock_angle")))))
	{
		use_subject_facing = false;
	}
	// if the subject is moving very slowly
	else // if (!mp_target_walk_component->UseDPadCamera())
	{
		float full_slerp_speed;
		float min_slerp_speed;
		if (!mp_target_walk_component->UseDPadCamera())
		{
			full_slerp_speed = s_get_param(CRCD(0xbcef6dda, "full_slerp_speed"));
			min_slerp_speed = s_get_param(CRCD(0x824349e5, "min_slerp_speed"));
		}
		else
		{
			full_slerp_speed = s_get_param(CRCD(0x73da1ec0, "dpad_full_slerp_speed"));
			min_slerp_speed = s_get_param(CRCD(0xda5cad3, "dpad_min_slerp_speed"));
		}
		
		float target_vel = sqrtf(mp_target->GetVel()[X] * mp_target->GetVel()[X] + mp_target->GetVel()[Z] * mp_target->GetVel()[Z]);
		if (target_vel < full_slerp_speed)
		{
			use_subject_facing = false;
			
			// for a middle range of velocities, lerp to use of the subject's matrix
			if (target_vel > min_slerp_speed)
			{
				target_facing = Mth::LinearMap(
					target_facing,
					subject_facing,
					target_vel,
					min_slerp_speed,
					full_slerp_speed
				);
				target_facing.Normalize();
			}
		}
	}
	
	if (use_subject_facing || m_reset)
	{
		target_facing = subject_facing;
	}
	
	// control over target facing
	if (horiz_control != 0.0f && !m_flush_request_active)
	{
		target_facing.RotateY(s_get_param(CRCD(0xf6f69dc8, "facing_control")) * horiz_control);
	}
	
	if (m_override_active)
	{
		target_facing = m_override_facing;
		target_facing.RotateY(mp_lookaround_component->mLookaroundHeading);
	}
	
	// build target matrix
	Mth::Matrix target_matrix;
	target_matrix[Z] = target_facing;
	target_matrix[Y].Set(0.0f, 1.0f, 0.0f);
	target_matrix[X].Set(target_facing[Z], 0.0f, -target_facing[X]);
	target_matrix[W].Set();
	
	// apply lookaround adjustments to the matrix
	target_matrix.RotateXLocal(mp_skater_camera_component->mTilt + mp_lookaround_component->mLookaroundTilt);
	
	target_matrix[X] = -target_matrix[X];
	target_matrix[Z] = -target_matrix[Z];
	
	// use later for camera position
	Mth::Vector up = mp_target->GetMatrix()[Y];
	
	if (!instantly)
	{
		if (Mth::DotProduct(target_matrix[X], GetObject()->GetMatrix()[X]) > CAMERA_SLERP_STOP
			&& Mth::DotProduct(target_matrix[Y], GetObject()->GetMatrix()[Y]) > CAMERA_SLERP_STOP
			&& Mth::DotProduct(target_matrix[Z], GetObject()->GetMatrix()[Z]) > CAMERA_SLERP_STOP)
		{
			// we're already at our target, so don't do anything
			
			// turn off any flush request
			if (m_flush_request_active)
			{
				m_flush_request_active = false;
				mp_target_walk_component->SetForwardControlLock(false);
			}
		}
		else
		{
			// slerp to the target matrix
			
			Mth::SlerpInterpolator slerper(&GetObject()->GetMatrix(), &target_matrix);
			
			// standard slerp rate
			float slerp = s_get_param(CRCD(0xc39b639, "matrix_slerp_rate"));
			
			// running slerp rate adjustment
			if (mp_target_walk_component->IsRunning())
			{
				slerp *= s_get_param(CRCD(0xd25348eb, "run_slerp_factor"));
			}
			
			// animation wait and ladder slerp rate adjustment
			// NOTE: replace with target facing override?
			if (mp_target_walk_component->GetState() == CWalkComponent::WALKING_ANIMWAIT || mp_target_walk_component->GetState() == CWalkComponent::WALKING_LADDER)
			{
				slerp *= 2.0f;
			}
			
			// if controlling the facing
			if (horiz_control != 0.0f && !m_flush_request_active)
			{
				slerp *= s_get_param(CRCD(0xb2da3c87, "control_slerp_factor")) * Mth::Abs(horiz_control);
			}
			
			// flush request slerp adjustment
			if (m_flush_request_active)
			{
				slerp *= s_get_param(CRCD(0x7178e048, "flush_slerp_factor"));
			}
			
			if (m_override_active)
			{
				// can't override flush speed
				if (m_flush_request_active)
				{
					slerp = s_get_param(CRCD(0xc39b639, "matrix_slerp_rate")) * s_get_param(CRCD(0x7178e048, "flush_slerp_factor"));
				}
				else
				{
					slerp = m_override_slerp_rate;
				}
			}
			
			// apply the slerping
			slerper.getMatrix(&GetObject()->GetMatrix(), GetTimeAdjustedSlerp(slerp, frame_length));

			// calculate for the skater camera
			m_last_dot = Mth::DotProduct(m_last_actual_matrix[Z], GetObject()->GetMatrix()[Z]);
		}
	}
	else
	{
		GetObject()->GetMatrix() = target_matrix;
	}
	
	// At this point, GetObject()->GetMatrix() is valid to store.
	m_last_actual_matrix = GetObject()->GetMatrix();
	
	// Set camera position to be the same as the skater.
	Mth::Vector	camera_pos = get_tripod_pos(instantly);
	
	// Calculate zoom
	float above, behind;
	calculate_zoom(above, behind);
	
	camera_pos += GetObject()->GetMatrix()[Z] * behind + up * above;
	
	Mth::Vector	focus_pos = mp_target->GetPos() + up * above;
	
	// Focus the camera directly on the target object
	
	target_matrix[Z] = focus_pos - camera_pos;
	target_matrix[Z].Normalize();

	// Read back the Y from the current matrix.
	target_matrix[Y] = GetObject()->GetMatrix()[Y];

	// Generate new orthonormal X and Y axes.
	target_matrix[X] = Mth::CrossProduct(target_matrix[Y], target_matrix[Z]);
	target_matrix[X].Normalize();
	target_matrix[Y] = Mth::CrossProduct(target_matrix[Z], target_matrix[X]);
	target_matrix[Y].Normalize();

	// Write back into camera matrix.
	// Since camera points in -Z, but player in +Z, we must negate the X and Z axes
	GetObject()->GetMatrix()[X]	= -target_matrix[X];
	GetObject()->GetMatrix()[Y] = target_matrix[Y];
	GetObject()->GetMatrix()[Z] = -target_matrix[Z];
	
	// clean up matrix
	GetObject()->GetMatrix()[X][W] = 0.0f;
	GetObject()->GetMatrix()[Y][W] = 0.0f;
	GetObject()->GetMatrix()[Z][W] = 0.0f;
	GetObject()->GetMatrix()[W].Set(0.0f, 0.0f, 0.0f, 1.0f);
	
	// Now do collision detection.
	ApplyCameraCollisionDetection(
		camera_pos,
		GetObject()->GetMatrix(),
		camera_pos - GetObject()->GetMatrix()[Z] * behind + mp_target_walk_component->GetCameraCollisionTargetOffset(),
		focus_pos
	);
	
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		Gfx::AddDebugStar(focus_pos, 24.0f, MAKE_RGB(255, 200, 0), 1);
	}
	#endif
	
	camera_pos[W] = 1.0f;
	GetObject()->SetPos(camera_pos);
	
	// reset old position if in instant update
	if (instantly)
	{
		mp_camera_component->StoreOldPosition();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CWalkCameraComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | WalkCamera_FlushRequest | Force the camera to lerp quickly to behind the walker
		case CRCC(0x73febd0f, "WalkCamera_FlushRequest"):
			FlushRequest();
			break;
			
		// @script | WalkCamera_Reset | Teleports the camera to behind the target
		case CRCC(0xd1a485d1, "WalkCamera_Reset"):
			Reset();
			break;
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CWalkCameraComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::ReadyForActivation ( const SCameraState& state )
{
	Dbg_MsgAssert(mp_target, ("Walk camera (%s) has NULL target", Script::FindChecksumName(GetObject()->GetID())));
	
	m_last_tripod_pos = state.lastTripodPos;
	m_last_actual_matrix = state.lastActualMatrix;
	m_last_dot = state.lastDot;
	m_current_zoom = state.lastZoom;
	m_flush_request_active = false;
	mp_target_walk_component->SetForwardControlLock(false);
	m_instant_count = 0;
	
	mp_lookaround_component->mLookaroundHeading = 0.0f;
	mp_lookaround_component->mLookaroundLock = false;
	
	m_override_active = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::GetCameraState ( SCameraState& state )
{
	state.lastActualMatrix = m_last_actual_matrix;
	state.lastTripodPos = m_last_tripod_pos;
	state.lastDot = m_last_dot;
	state.lastZoom = m_current_zoom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::FlushRequest (   )
{
	m_flush_request_active = true;
	mp_target_walk_component->SetForwardControlLock(true);
	
	// flush requests zero skater cam lookaround
	mp_lookaround_component->mLookaroundHeading = 0.0f;
	mp_lookaround_component->mLookaroundTilt = 0.0f;
	mp_lookaround_component->mLookaroundZoom = 1.0f;
	mp_lookaround_component->mLookaroundLock = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::Reset (   )
{
	m_reset = true;
	Update();
	m_reset = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::set_target ( CCompositeObject* p_target )
{
	if (p_target)
	{
		mp_target = p_target;
		
		mp_target_walk_component = GetWalkComponentFromObject(mp_target);
		Dbg_Assert(mp_target_walk_component);
		
		mp_target_physics_control_component = GetSkaterPhysicsControlComponentFromObject(mp_target);
		Dbg_Assert(mp_target_physics_control_component);
	}
	else
	{
		mp_target = NULL;
		mp_target_walk_component = NULL;
		mp_target_physics_control_component = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CWalkCameraComponent::get_tripod_pos( bool instantly )
{
	if (instantly)
	{
		m_last_tripod_pos = mp_target->GetPos();
	}
	else
	{
		float lerp_xz = GetTimeAdjustedSlerp(mp_skater_camera_component->mLerpXZ, Tmr::FrameLength());
		float lerp_y = GetTimeAdjustedSlerp(mp_skater_camera_component->mLerpY, Tmr::FrameLength());
		
		const Mth::Vector& target_pos = mp_target->GetPos();

		m_last_tripod_pos.Set(
			Mth::Lerp(m_last_tripod_pos[X], target_pos[X], lerp_xz),
			Mth::Lerp(m_last_tripod_pos[Y], target_pos[Y], lerp_y),
			Mth::Lerp(m_last_tripod_pos[Z], target_pos[Z], lerp_xz)
		);
	}

	return m_last_tripod_pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkCameraComponent::calculate_zoom ( float& above, float& behind )
{
	float target_zoom = 1.0f;

	// If lookaround override is set, factor in the lookaround override zoom.
	if (mp_lookaround_component->mLookaroundOverride && mp_lookaround_component->mLookaroundZoom != 1.0f)
	{
		target_zoom *= mp_lookaround_component->mLookaroundZoom;
	}
	
	m_current_zoom += ((target_zoom - m_current_zoom) * mp_skater_camera_component->mZoomLerp);
	
	behind = mp_skater_camera_component->mBehind * m_current_zoom;

	// Behind is also shortened when the lookaround camera is tilting upwards.
	if (mp_lookaround_component->mLookaroundTilt < 0.0f)
	{
		float max_tilt = 3.14f * 0.2f;
		behind = behind * (0.4f + (0.6f * ((max_tilt + mp_lookaround_component->mLookaroundTilt) / max_tilt)));
	}

	// Use lip_trick_above when doing a lip trick.
	float above_val = mp_skater_camera_component->mAbove;
	
	// Figure above tending towards the perfect above, if zoom is < 1.0.
	if (m_current_zoom < 1.0f)
	{
		above = SKATERCAMERACOMPONENT_PERFECT_ABOVE + ((above_val - SKATERCAMERACOMPONENT_PERFECT_ABOVE) * m_current_zoom);
	}
	else
	{
		above = above_val;
	}
}

}

#endif // TESTING_GUNSLINGER


================================================
FILE: Code/Gel/Components/WalkCameraComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       WalkCameraComponent.h
//* OWNER:          Dan
//* CREATION DATE:  4/9/3
//****************************************************************************

#ifndef __COMPONENTS_WALKCAMERACOMPONENT_H__
#define __COMPONENTS_WALKCAMERACOMPONENT_H__

#include 
#include 

#include 
#include 
#include 
#include 

#define		CRC_WALKCAMERA CRCD(0xc7d37ec2, "WalkCamera")

#define		GetWalkCameraComponent() ((Obj::CWalkCameraComponent*)GetComponent(CRC_WALKCAMERA))
#define		GetWalkCameraComponentFromObject(pObj) ((Obj::CWalkCameraComponent*)(pObj)->GetComponent(CRC_WALKCAMERA))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CWalkComponent;
	class CCameraLookAroundComponent;
	class CCameraComponent;
	class CSkaterCameraComponent;
	class CSkaterPhysicsControlComponent;
	
class CWalkCameraComponent : public CBaseComponent
{
public:
    CWalkCameraComponent();
    virtual ~CWalkCameraComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							ReadyForActivation ( const SCameraState& state );
	void							GetCameraState ( SCameraState& state );
	void							SetOverrides ( const Mth::Vector& facing, float slerp_rate );
	void							UnsetOverrides (   );
	
	void							FlushRequest (   );
	void							Reset (   );
	
	void							SetSkater ( CCompositeObject* p_skater );
	
private:
	Mth::Vector						get_tripod_pos( bool instantly );
	void							calculate_zoom ( float& above, float& behind );
	
	void							set_target ( CCompositeObject* p_target );
	
	float							s_get_param ( uint32 checksum );
	
private:
	int								m_instant_count;
	bool							m_flush_request_active;
	bool							m_reset;
	
	float							m_current_zoom;
	
	Mth::Vector						m_last_tripod_pos;
	Mth::Matrix						m_last_actual_matrix;
	float							m_last_dot;
	
	bool							m_override_active;
	Mth::Vector						m_override_facing;
	float							m_override_slerp_rate;
	
	CCameraLookAroundComponent*		mp_lookaround_component;
	CCameraComponent*				mp_camera_component;
	CSkaterCameraComponent*			mp_skater_camera_component;
	
	CCompositeObject*				mp_target;
	CWalkComponent*					mp_target_walk_component;
	CSkaterPhysicsControlComponent*	mp_target_physics_control_component;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float CWalkCameraComponent::s_get_param ( uint32 checksum )
{
	Script::CStruct* p_walk_params = Script::GetStructure(CRCD(0x1cda02ae, "walk_camera_parameters"));
	Dbg_Assert(p_walk_params);
	
	float param;
	p_walk_params->GetFloat(checksum, ¶m, Script::ASSERT);
	return param;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CWalkCameraComponent::SetOverrides ( const Mth::Vector& facing, float slerp_rate )
{
	m_override_active = true;
	m_override_facing = facing;
	m_override_slerp_rate = slerp_rate;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CWalkCameraComponent::UnsetOverrides (   )
{
	m_override_active = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CWalkCameraComponent::SetSkater ( CCompositeObject* p_skater )
{
	set_target(p_skater);
	m_instant_count = 3;
}

}

#endif


================================================
FILE: Code/Gel/Components/WalkComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       WalkComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  4/2/3
//****************************************************************************

#ifdef TESTING_GUNSLINGER

// Replace the entire contents of this file with the new file.
#include 

#else

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
	
/*
 * - Camera needs to initialize correctly at restarts.
 * - Faster camera slerp, smaller no-slerp angle during run out.
 * - Retain momentum better when leaving skating.
 * - Accidental manuals when interacting with ledges and ladders.
 * - Catching on curbs right after jump (use snap up code from skater)
 * - Hang
 *    - Jerky rail corners.
 *    - Turn around on wire hangs.
 *    - Drift to next rung effect in hanging too.
 *    - Special pull up to wire animation.
 * - Ladder
 *    - Drift between rungs.
 *    - Match rungs when getting on from the top.
 *    - Match rungs when grabbing from air.
 * - Hang from bottom of ladder.
 * - Ladder to rail across top.  Rail across top to ladder.
 * - Hand-plants, drop-ins, etc.
 * BUGS:
 * - Fall through verts with low frame rate.
 * - Pop in transition from last to first frames of slow cycling (walk/climb) animations.
 */
 
extern bool g_CheatsEnabled;

namespace Obj
{
	const uint32 CWalkComponent::sp_state_names [ CWalkComponent::NUM_WALKING_STATES ] =
	{
		CRCC(0x8cf3cb28, "WALKING_GROUND"),
		CRCC(0x9d1f1a2c, "WALKING_AIR"),
		CRCC(0x74ffc46d, "WALKING_HANG"),
		CRCC(0x1cb1f465, "WALKING_LADDER"),
		CRCC(0x79be4637, "WALKING_ANIMWAIT")
	};
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CWalkComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CWalkComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CWalkComponent::CWalkComponent() : CBaseComponent()
{
	SetType( CRC_WALK );
	
	mp_collision_cache = Nx::CCollCacheManager::sCreateCollCache();
	
	mp_input_component = NULL;
	mp_animation_component = NULL;
	mp_model_component = NULL;
	mp_trigger_component = NULL;
	mp_physics_control_component = NULL;
	mp_movable_contact_component = NULL;
	mp_state_component = NULL;
	mp_core_physics_component = NULL;
	
	m_control_direction.Set();
	m_in_air_drift_vel.Set();
	m_rotate_upright_timer = 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CWalkComponent::~CWalkComponent()
{
	Nx::CCollCacheManager::sDestroyCollCache(mp_collision_cache);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::Finalize()
{
	mp_input_component = GetInputComponentFromObject(GetObject());
	mp_animation_component = GetAnimationComponentFromObject(GetObject());
	mp_model_component = GetModelComponentFromObject(GetObject());
	mp_trigger_component = GetTriggerComponentFromObject(GetObject());
	mp_physics_control_component = GetSkaterPhysicsControlComponentFromObject(GetObject());
	mp_movable_contact_component = GetMovableContactComponentFromObject(GetObject());
	mp_state_component = GetSkaterStateComponentFromObject(GetObject());
	mp_core_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
	
	Dbg_Assert(mp_input_component);
	Dbg_Assert(mp_animation_component);
	Dbg_Assert(mp_model_component);
	Dbg_Assert(mp_trigger_component);
	Dbg_Assert(mp_physics_control_component);
	Dbg_Assert(mp_movable_contact_component);
	Dbg_Assert(mp_state_component);
	Dbg_Assert(mp_core_physics_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::InitFromStructure( Script::CStruct* pParams )
{
	uint32 camera_id;
	if (pParams->GetChecksum(CRCD(0xc4e311fa, "camera"), &camera_id))
	{
		CCompositeObject* p_camera = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(camera_id));
		Dbg_MsgAssert(mp_camera, ("No such camera object"));
		SetAssociatedCamera(p_camera);
	}
	
	#ifdef SCRIPT_WALK_DRAG
	m_script_drag_factor = 1.0f;
	#endif
}
		 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::Update()
{
	uint32 previous_frame_event = m_frame_event;
	
	// TEMP: debounce R1 after a transition
	if (m_ignore_grab_button && !mp_input_component->GetControlPad().m_R1.GetPressed())
	{
		m_ignore_grab_button = false;
	}
	
	// zero the frame event
	m_last_frame_event = m_frame_event;
	m_frame_event = 0;
	
	// get input
    get_controller_input();
	
	// extract initial state for this frame from the object
	extract_state_from_object();
	
	m_frame_start_pos = m_pos;
	
	// set the frame length
	m_frame_length = Tmr::FrameLength();
	
	// go to our true Y position
	m_curb_float_height_adjusted = false;
	m_pos[Y] -= m_curb_float_height;
	DUMP_WPOSITION
	
	// lerp down to standard run speed if we're not in a combo
	update_run_speed_factor();
	
	// switch logic based on walking state
	switch (m_state)
	{
		case WALKING_GROUND:
			go_on_ground_state();
			break;

		case WALKING_AIR:
			go_in_air_state();
			break;
																	  
		// case WALKING_HOP:
			// go_hop_state();
			// break;
																	  
		case WALKING_HANG:
			go_hang_state();
			break;
            
		case WALKING_LADDER:
			go_ladder_state();
            break;
			
		case WALKING_ANIMWAIT:
			go_anim_wait_state (   );
			break;
	}
	if (mp_physics_control_component->HaveBeenReset()) return;
	
	// the there's no curb to adjust due to, lerp down to zero
	if (!m_curb_float_height_adjusted)
	{
		m_curb_float_height = Mth::Lerp(m_curb_float_height, 0.0f, s_get_param(CRCD(0x9b3388fa, "curb_float_lerp_down_rate")) * m_frame_length);
	}
	
	// adjust back to our curb float Y position
	m_pos[Y] += m_curb_float_height;
	DUMP_WPOSITION
	
	// maybe transition into skating via an acid drop from the ground
	if (m_state == WALKING_GROUND)
	{
		maybe_jump_to_acid_drop();
		if (mp_physics_control_component->HaveBeenReset()) return;
	}
	
	// adjust critical point offset
	update_critical_point_offset();
	
	// adjust the display offset
	update_display_offset();
	
	// keep the object from falling through holes in the geometry
	if (m_state == WALKING_GROUND || m_state == WALKING_AIR)
	{
		uber_frig();
	}
	
	// rotate to upright -- non-transition
	lerp_upright();
	
	// setup the object based on this frame's walking
	copy_state_into_object();
	
	// rotate to upright -- transition
	transition_lerp_upright();
	
	// smooth out the anim speed over two frames to minimize poppy behavior
	smooth_anim_speed(previous_frame_event);
	
	Dbg_Assert(m_frame_event);
	GetObject()->SelfEvent(m_frame_event);
	
	// set the animation speeds
	update_anim_speeds();
	
	// camera controls
	set_camera_overrides();
	
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		Gfx::AddDebugStar(GetObject()->GetPos(), 36.0f, MAKE_RGB(255, 255, 255), 1);
		if (m_critical_point_offset.LengthSqr() != 0.0f)
		{
			Gfx::AddDebugStar(GetObject()->GetPos() + m_critical_point_offset, 36.0f, MAKE_RGB(150, 255, 100), 1);
		}
	}
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CWalkComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | Walk_Ground |
		case CRCC(0x893213e5, "Walk_Ground"):
			return m_state == WALKING_GROUND ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Walk_Air |
		case CRCC(0x5012082e, "Walk_Air"):
			return m_state == WALKING_AIR ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Walk_Hang |
		case CRCC(0x9a3ca853, "Walk_Hang"):
			return m_state == WALKING_HANG ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Walk_Ladder |
		case CRCC(0x19702ca8, "Walk_Ladder"):
			return m_state == WALKING_LADDER ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Walk_AnimWait |
		case CRCC(0x8fe2b013, "Walk_AnimWait"):
			return m_state == WALKING_ANIMWAIT ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Walk_GetStateTime | Loads the time in milliseconds since last state change.
		case CRCC(0xce64576c, "Walk_GetStateTime"):
			pScript->GetParams()->AddInteger(CRCD(0x5ab23cc9, "StateTime"), Tmr::ElapsedTime(m_state_timestamp));
			break;
		
		// @script | Walk_Jump |
		case CRCC(0x83e4bd70, "Walk_Jump"):
			jump();
			break;
		
		#ifdef SCRIPT_WALK_DRAG
		// @script | Walk_SetDragFactor |
		case CRCC(0xc6100a7d, "Walk_SetDragFactor"):
			pParams->GetFloat(NO_NAME, &m_script_drag_factor);
			break;
			
		case CRCC(0x4e4fae43, "Walk_ResetDragFactor"):
			m_script_drag_factor = 1.0f;
			break;
		#endif
			
		case CRCC(0xaf04b983, "Walk_GetSpeedScale"):
		{
			uint32 checksum;
			if (m_anim_effective_speed < s_get_param(CRCD(0xf3649996, "max_slow_walk_speed")))
			{
				checksum = CRCD(0x1150cabb, "WALK_SLOW");
			}
			else if (m_anim_effective_speed < s_get_param(CRCD(0x6a5805d8, "max_fast_walk_speed")))
			{
				checksum = CRCD(0x131f2a2, "WALK_FAST");
			}
			else if (m_anim_effective_speed < s_get_param(CRCD(0x1c94cc9c, "max_slow_run_speed")))
			{
				checksum = CRCD(0x5606d106, "RUN_SLOW");
			}
			else
			{
				checksum = CRCD(0x4667e91f, "RUN_FAST");
			}
			pScript->GetParams()->AddChecksum(CRCD(0x92c388f, "SpeedScale"), checksum);
			
			break;
		}
		
		// @script | Walk_ScaleAnimSpeed | Sets the manner in which the walk animations speeds should be scaled.
		// @flag Off | No animation speed scaling.
		// @flag Run | Scale animation speeds against running speed.
		// @flag Walk | Scale animation speeds against walking speed.
		case CRCC(0x56112c03, "Walk_ScaleAnimSpeed"):
			if (pParams->ContainsFlag(CRCD(0xd443a2bc, "Off")))
			{
				if (m_anim_scale_speed != OFF)
				{
					m_anim_scale_speed = OFF;
					mp_animation_component->SetAnimSpeed(1.0f, false, true);
				}
			}
			else if (pParams->ContainsFlag(CRCD(0xaf895b3f, "Run")))
			{
				m_anim_scale_speed = RUNNING;
			}
			else if (pParams->ContainsFlag(CRCD(0x6384f1da, "HangMove")))
			{
				m_anim_scale_speed = HANGMOVE;
			}
			else if (pParams->ContainsFlag(CRCD(0xa2bfe505, "LadderMove")))
			{
				m_anim_scale_speed = LADDERMOVE;
				
				// force a server anim speed update
				m_last_ladder_anim_speed = -1.0f;
			}
			else
			{
				Dbg_MsgAssert(false, ("Walk_ScaleAnimSpeed requires Off, Run, or Walk flag"));
			}
			
			pParams->GetFloat(CRCD(0xb2d59baf, "StandardSpeed"), &m_anim_standard_speed);
			break;
			
		// @script | Walk_AnimWaitComplete | Signal from script that the walk component should leave its animation wait
		case CRCC(0x9d3eebe8, "Walk_AnimWaitComplete"):
			anim_wait_complete();
			break;
				   			
		// @script | Walk_GetHangInitAnimType | Determine which type of initial hang animation should be played
		case CRCC(0xc6cd659e, "Walk_GetHangInitAnimType"):
			// m_initial_hang_animation is set when the hang rail is filtered
			pScript->GetParams()->AddChecksum(CRCD(0x85fa9ac4, "HangInitAnimType"), m_initial_hang_animation);
			break;
			
		// @script | Walk_GetStateDuration | Returns the duration of the current state in StateDuration
		case CRCC(0x55bf1cd6, "Walk_GetStateDuration"):
			pScript->GetParams()->AddFloat(CRCD(0x4b88e4e4, "StateDuration"), (1.0f / 1000.0f) * Tmr::ElapsedTime(m_state_timestamp));
			break;
			
		// @script | Walk_GetPreviousState | Returns the previous state in PreviousState
		case CRCC(0xe9b1cde8, "Walk_GetPreviousState"):
		{
			pScript->GetParams()->AddChecksum(CRCD(0xf78635da, "PreviousState"), sp_state_names[m_previous_state]);
			break;
		}
			
		// @script | Walk_GetPreviousState | Returns the current state in State
		case CRCC(0xac7b36c8, "Walk_GetState"):
		{
			pScript->GetParams()->AddChecksum(CRCD(0x5c6c2d04, "State"), sp_state_names[m_state]);
			break;
		}
			
		// @script | Walk_GetHangAngle | Returns the current angle of the hang rail
		case CRCC(0x7e01b923, "Walk_GetHangAngle"):
		{
			Dbg_MsgAssert(mp_physics_control_component->IsWalking(), ("Called Walk_GetHangAngle when not in walk mode"));
			Dbg_MsgAssert(m_state == WALKING_HANG, ("Called Walk_GetHangAngle when not hanging"));
			
			Mth::Vector rail_direction = (mp_rail_manager->GetPos(mp_rail_end) - mp_rail_manager->GetPos(mp_rail_start)).Normalize();
			float angle = asinf(rail_direction[Y]);
			if (Mth::CrossProduct(rail_direction, m_facing)[Y] < 0.0f)
			{
				angle = -angle;
			}
			pScript->GetParams()->AddFloat(CRCD(0xead7d286, "HangAngle"), Mth::RadToDeg(angle));
			break;
		}

		/*
		case CRCC(0x14e4985b, "Walk_CancelTransitionalMomentum"):
			m_cancel_transition_momentum = true;
			break;
		*/
		
		/*
		// @script | Walk_SuppressInAirControl | turn off in air velocity control for some number of seconds
		case CRCC(0x30a9de5c, "Walk_SuppressInAirControl"):
			pParams->GetFloat(NO_NAME, &m_in_air_control_suppression_timer, Script::ASSERT);
			break;
		*/

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CWalkComponent::GetDebugInfo"));
	
	switch (m_state)
	{
		case WALKING_GROUND:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x58007c97, "GROUND"));
			break;
		case WALKING_AIR:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x439f4704, "AIR"));
			break;
		// case WALKING_HOP:
			// p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0xf41aba21, "HOP"));
			// break;										 
		case WALKING_HANG:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x4194ecca, "HANG"));
			break;										 
        case WALKING_LADDER:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0xc84243da, "LADDER"));
			break;										 
        case WALKING_ANIMWAIT:
			p_info->AddChecksum(CRCD(0x109b9260, "m_state"), CRCD(0x4fe6069c, "ANIMWAIT"));
			break;										 
	}

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::SetAssociatedCamera ( CCompositeObject* camera_obj )
{
	mp_camera = camera_obj;
	Dbg_Assert(mp_camera);
	mp_camera_component = GetWalkCameraComponentFromObject(mp_camera);
	Dbg_MsgAssert(mp_camera_component, ("No WalkCameraComponent in camera object"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::ReadyWalkState ( bool to_ground_state )
{
	// setup the state in preparation for being in walking mode next object update
	
    // always reset the state timestamp
    m_state_timestamp = Tmr::GetTime();
	
	if (GetObject()->GetMatrix()[Y][Y] > 0.999f)
	{
		m_rotate_upright_timer = 0.0f;
	}
	else
	{
		// if we're not upright, get ready to rotate to upright
		if (GetObject()->GetMatrix()[Y][Y] > -0.999f)
		{
			m_rotate_upright_axis.Set(-GetObject()->GetMatrix()[Y][Z], 0.0f, GetObject()->GetMatrix()[Y][X]);
			m_rotate_upright_angle = acosf(Mth::Clamp(GetObject()->GetMatrix()[Y][Y], -1.0f, 1.0f));
		}
		else
		{
			m_rotate_upright_axis = GetObject()->GetMatrix()[X];
			m_rotate_upright_angle = Mth::PI;
		}
        
		m_rotate_upright_timer = s_get_param(CRCD(0xb0675803, "rotate_upright_duration"));
		
		if (GetObject()->GetMatrix()[Y][Y] < 0.0f)
		{
			GetObject()->SetPos(GetObject()->GetPos() + 6.0f * GetObject()->GetMatrix()[Y]);
			to_ground_state = false;
		}
	}

	if (to_ground_state)
	{
		set_state(WALKING_GROUND);
		
		// will be incorrect for one frame
		m_ground_normal.Set(0.0f, 1.0f, 0.0);
		
		m_last_ground_feeler_valid = false;
		
		GetObject()->GetVel()[Y] = 0.0f;
		
		m_disallow_acid_drops = false;

		/*
		if (GetObject()->GetVel().LengthSqr() > Mth::Sqr(450.0f))
		{
			m_cancel_transition_momentum = false;
		}
		else
		{
			m_cancel_transition_momentum = true;
		}
		*/
	}
	else
	{
		set_state(WALKING_AIR);
		
		// give a slight velocity boost when transitioning from vert air
		if (mp_core_physics_component->GetFlag(VERT_AIR) && m_rotate_upright_timer != 0.0f)
		{
			Mth::Vector target_facing = GetObject()->GetMatrix()[Z];
			target_facing.Rotate(m_rotate_upright_axis, m_rotate_upright_angle);
			GetObject()->GetVel() += s_get_param(CRCD(0x17b37748, "initial_vert_vel_boost")) * target_facing;
		}
		
		// set primary air direction in the direction of velocity
		m_primary_air_direction = GetObject()->GetVel();
		m_primary_air_direction[Y] = 0.0f;
		float length = m_primary_air_direction.Length();
		if (length < 0.001f)
		{
			// or facing
			m_primary_air_direction = GetObject()->GetMatrix()[Z];
			m_primary_air_direction[Y] = 0.0f;
			length = m_primary_air_direction.Length();
			if (length < 0.001f)
			{
				// or future facing
				m_primary_air_direction = -GetObject()->GetMatrix()[Y];
				m_primary_air_direction[Y] = 0.0f;
				length = m_primary_air_direction.Length();
			}
		}
		m_primary_air_direction /= length;
		
		leave_movable_contact_for_air(GetObject()->GetVel(), GetObject()->GetVel()[Y]);
		
		m_in_air_control_suppression_timer = 0.0f;
		
		m_false_wall.active = false;
		
		// you have to touch the ground at least once before acid dropping out of walking
		m_disallow_acid_drops = mp_core_physics_component->GetFlag(VERT_AIR)
			|| mp_core_physics_component->GetFlag(AIR_ACID_DROP_DISALLOWED);
		
		// m_cancel_transition_momentum = true;
	}

	m_curb_float_height = 0.0f;
	
	m_special_transition_data.type = NO_SPECIAL_TRANSITION;
	
	m_last_frame_event = 0;
	
	// TEMP: debounce R1 after a transition
	m_ignore_grab_button = true;
	
	m_critical_point_offset.Set();
	
	m_display_offset = 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::CleanUpWalkState (   )
{
	mp_model_component->SetDisplayOffset(Mth::Vector(0.0f, 0.0f, 0.0f));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::CollideWithOtherSkaterLost ( CCompositeObject* p_other_skater )
{
	set_state(WALKING_AIR);
	m_primary_air_direction = GetObject()->GetVel();
	m_primary_air_direction[Y] = 0.0f;
	m_primary_air_direction.Normalize();
	
	leave_movable_contact_for_air(GetObject()->GetVel(), GetObject()->GetVel()[Y]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::set_state ( EStateType state )
{
    if (state == m_state) return;
	
	m_previous_state = m_state;
	m_state = state;
	m_state_timestamp = Tmr::GetTime();
	
    if (m_previous_state == WALKING_GROUND || m_previous_state == WALKING_AIR)
    {
        m_wall_push_test.active = false;
		m_run_toggle = false;
    }
	
	if (m_previous_state == WALKING_AIR)
	{
		m_in_air_control_suppression_timer = 0.0f;
	}

	if (m_previous_state == WALKING_AIR)
	{
		m_in_air_drift_vel.Set();
		
		m_disallow_acid_drops = false;
	}
	
	if (m_state == WALKING_GROUND)
	{
		m_control_pegged_duration = 10000.0f;
		m_last_frame_controller_pegged = true;
	}
	
	if (m_state == WALKING_ANIMWAIT)
	{
		m_offset_due_to_movable_contact.Set();
	}
	
	if (m_state != WALKING_AIR)
	{
		m_false_wall.active = false;
	}
	
	if (m_state == WALKING_GROUND || m_previous_state != WALKING_AIR)
	{
		m_drop_rotation_rate = false;
	}
	
	if (m_state == WALKING_HANG)
	{
		m_hang_move_vel = 0.0f;
	}
	
	if (m_state == WALKING_HANG && m_previous_state == WALKING_AIR)
	{
		CControlPad& control_pad = mp_input_component->GetControlPad();
		control_pad.DebounceLeftAnalogUp(s_get_param(CRCD(0xac96d24b, "hang_control_debounce_time")));
		control_pad.DebounceLeftAnalogDown(s_get_param(CRCD(0xac96d24b, "hang_control_debounce_time")));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::go_on_ground_state (   )
{
	account_for_movable_contact();
	
	setup_collision_cache();
	
	// calculate initial horizontal speed
	float horizontal_speed = m_horizontal_vel.Length();
	
	calculate_horizontal_speed_and_facing(horizontal_speed);
	
	// only skid if you've had the controller pegged for a minimum duration
	if (m_frame_event == CRCD(0x1d537eff, "Skid") && m_control_pegged_duration < s_get_param(CRCD(0x8e1cf27a, "pegged_duration_for_skid")))
	{
		m_frame_event = CRCD(0x9b46e749, "Stand");
	}
	
	// calculate this frame's movement
	m_horizontal_vel = horizontal_speed * m_facing;
	
	// prevent movement into walls
	if (adjust_horizonal_vel_for_environment())
	{
		// turn to face newly adjusted velocity
		adjust_facing_for_adjusted_horizontal_vel();
	}
	else
	{
		m_drop_rotation_rate = false;
	}
	
	// if we are wall pushing, we may have decided to switch states during adjust_horizonal_vel_for_environment based on our environment
	if (m_state != WALKING_GROUND || mp_physics_control_component->HaveBeenReset())
	{
		CFeeler::sClearDefaultCache();
		return;
	}
	
	// apply movement for this frame
	m_pos += m_horizontal_vel * m_frame_length;
	DUMP_WPOSITION
	
	// snap up and down curbs and perhaps switch to air
	respond_to_ground();
	if (m_state != WALKING_GROUND || mp_physics_control_component->HaveBeenReset())
	{
		CFeeler::sClearDefaultCache();
		return;
	}
	
	adjust_curb_float_height();
	
	// insure that we do not slip through the cracks in the collision geometry which are a side-effect of moving collidable objects
	if (CCompositeObject* p_inside_object = mp_movable_contact_component->CheckInsideObjects(m_pos, m_frame_start_pos))
	{
		MESSAGE("WALKING_GROUND, within moving object");
		
		// allow it to push us forward, causing a bit of a stumble
		m_horizontal_vel = p_inside_object->GetVel();
		m_horizontal_vel[Y] = 0.0f;
		m_vertical_vel = 0.0f;
		
		float speed_sqr = m_horizontal_vel.LengthSqr();
		if (speed_sqr > (10.0f * 10.0f))
		{
			m_facing = m_horizontal_vel / sqrtf(speed_sqr);
		}
	}
	
	CFeeler::sClearDefaultCache();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::calculate_horizontal_speed_and_facing ( float &horizontal_speed )
{
	if (m_control_pegged)
	{
		if (!m_last_frame_controller_pegged)
		{
			m_control_pegged_duration = 0.0f;
		}
		m_control_pegged_duration += m_frame_length;
	}
	m_last_frame_controller_pegged = m_control_pegged;
	
	// calculate user's desired speed
	float desired_speed = calculate_desired_speed();
	
	#ifdef SCRIPT_WALK_DRAG
	// adjust speed by the script set drag factor
	desired_speed *= m_script_drag_factor;
	#endif
			   	
	// setup frame's event
	if (desired_speed <= 0.0f)
	{
		m_frame_event = CRCD(0x9b46e749, "Stand");
	}
	else
	{
		m_frame_event = CRCD(0xaf895b3f, "Run");
	}

	bool special_acceleration = false;
	
	// adjust facing based on input
	
	if (m_control_magnitude > 0.0f)
	{
		float dot = Mth::DotProduct(m_facing, m_control_direction);
		
		if ((horizontal_speed < s_get_param(CRCD(0x52582d5b, "max_rotate_in_place_speed")) && dot < cosf(Mth::DegToRad(s_get_param(CRCD(0x5dff96a4, "max_rotate_in_place_angle")))))
			|| (horizontal_speed < s_get_param(CRCD(0xf1e97e45, "min_skid_speed")) && dot < -cosf(Mth::DegToRad(s_get_param(CRCD(0x2d571c0f, "max_reverse_angle"))))))
		{
			// low speed rotate to desired orientation with no speed change
			
			float delta_angle = Mth::DegToRad(s_get_param(CRCD(0xb557804b, "rotate_in_place_rate"))) * m_control_magnitude * m_frame_length;
			bool left_turn = -m_facing[Z] * m_control_direction[X] + m_facing[X] * m_control_direction[Z] < 0.0f;
			
			if (!m_run_toggle || m_drop_rotation_rate)
			{
				delta_angle *= s_get_param(CRCD(0x7b446c98, "walk_rotate_factor"));
			}
			else
			{
				float cos_delta_angle = cosf(left_turn ? delta_angle : -delta_angle);
				float sin_delta_angle = sinf(left_turn ? delta_angle : -delta_angle);
				Mth::Vector new_facing;
				new_facing[X] = cos_delta_angle * m_facing[X] + sin_delta_angle * m_facing[Z];
				new_facing[Y] = 0.0f;
				new_facing[Z] = -sin_delta_angle * m_facing[X] + cos_delta_angle * m_facing[Z];
				
				// kludge to stop popping when running at a wall using the dpad
				CFeeler feeler;
				feeler.m_start = m_pos;
				feeler.m_start[Y] += s_get_param(CRCD(0x6da7f696, "feeler_height"));
				feeler.m_end = feeler.m_start + s_get_param(CRCD(0x99978d2b, "feeler_length")) * new_facing;
				if (feeler.GetCollision(false))
				{
					delta_angle *= s_get_param(CRCD(0x7b446c98, "walk_rotate_factor"));
				}
			}
			
			float cos_delta_angle = cosf(left_turn ? delta_angle : -delta_angle);
			float sin_delta_angle = sinf(left_turn ? delta_angle : -delta_angle);
			float adjusted_vel = cos_delta_angle * m_facing[X] + sin_delta_angle * m_facing[Z];
			m_facing[Z] = -sin_delta_angle * m_facing[X] + cos_delta_angle * m_facing[Z];
			m_facing[X] = adjusted_vel;
			
			// check for overturn
			if (left_turn != (-m_facing[Z] * m_control_direction[X] + m_facing[X] * m_control_direction[Z] < 0.0f))
			{
				m_facing = m_control_direction;
			}
			
			// no acceleration until we reach the desired orientation
			special_acceleration = true;
			
			// setup the event
			m_frame_event = left_turn ? CRCD(0xf28adbfc, "RotateLeft") : CRCD(0x912220f8, "RotateRight");
		}
		else
		{
			if (dot > -cosf(Mth::DegToRad(s_get_param(CRCD(0x2d571c0f, "max_reverse_angle")))))
			{
				// if the turn angle is soft
				
				float worse_factor = Mth::Lerp(
					m_analog_control
						? s_get_param(CRCD(0xa26760f5, "worse_worse_turn_factor"))
						: s_get_param(CRCD(0xab9015bc, "dpad_worse_worse_turn_factor")),
					m_analog_control
						? s_get_param(CRCD(0x6cb2e5de, "worse_turn_factor"))
						: s_get_param(CRCD(0x911f29d7, "dpad_worse_turn_factor")),
					Mth::Clamp(2.0f * (1.0f - Mth::DotProduct(m_control_direction, m_facing)), 0.0f, 1.0f)
				);
				
				// below a speed threshold, scale up the turn rate
				float turn_factor;
				if (horizontal_speed < s_get_param(CRCD(0x27815f69, "max_pop_speed")))
				{
					// quick turn
					turn_factor = Mth::Lerp(
						s_get_param(CRCD(0xb278405d, "best_turn_factor")),
						worse_factor,
						horizontal_speed / s_get_param(CRCD(0x27815f69, "max_pop_speed"))
					);
				}
				else
				{
					// slower turn
					turn_factor = worse_factor;
				}
				turn_factor *= m_control_magnitude;
				
				// exponentially approach the new facing
				float turn_ratio = turn_factor * m_frame_length;
				if (turn_ratio >= 1.0f)
				{
					m_facing = m_control_direction;
				}
				else
				{
					m_facing = Mth::Lerp(m_facing, m_control_direction, turn_ratio);
					m_facing.Normalize();
				}
			}
			else
			{
				// the turn angle is hard
				
				if (horizontal_speed > s_get_param(CRCD(0xf1e97e45, "min_skid_speed")))
				{
					// if moving fast enough to require a skidding stop
					special_acceleration = true;
					horizontal_speed -= s_get_param(CRCD(0x9661ed7, "skid_accel")) * m_frame_length;
					horizontal_speed = Mth::ClampMin(horizontal_speed, 0.0f);
					m_frame_event = CRCD(0x1d537eff, "Skid");
				}
				else
				{
					// if max_rotate_in_place_speed is larger than min_skid_speed and max_reverse_angle points farther in reverse than
					// max_rotate_in_place_angle, as they should, then this code should never be run
					Dbg_Message("Unexpected state in CWalkComponent::calculate_horizontal_speed_and_facing");
					
					// to be safe, pop to the new facing
					m_facing = m_control_direction;
				}
			}
		}
	}
	
	if (special_acceleration) return;
	
	// store desired speed for animation speed scaling
	m_anim_effective_speed = desired_speed;
	
	// adjust desired speed for slope
	desired_speed = adjust_desired_speed_for_slope(desired_speed);
	
	// linear acceleration; exponential deceleration
	if (horizontal_speed > desired_speed)
	{
		float decel_factor = s_get_param(CRCD(0xacfa4e0c, "decel_factor"));
		
		if (desired_speed == 0.0f)
		{
			if (horizontal_speed > s_get_param(CRCD(0x79d182ad, "walk_speed")))
			{
				m_frame_event = CRCD(0x1d537eff, "Skid");
			}
			else
			{
				if (m_last_frame_event == CRCD(0x1d537eff, "Skid") && horizontal_speed > s_get_param(CRCD(0x311d02b2, "stop_skidding_speed")))
				{
					m_frame_event = CRCD(0x1d537eff, "Skid");
				}
				else
				{
					decel_factor = s_get_param(CRCD(0xa2aa811, "low_speed_decel_factor"));
				}
			}
		}
		
		/* needs reworked, at the least
		if (mp_physics_control_component->GetStateSwitchTime() == m_state_timestamp
			&& Tmr::ElapsedTime(mp_physics_control_component->GetStateSwitchTime()) < 400.0f
			&& !m_cancel_transition_momentum
			&& desired_speed < s_get_param(CRCD(0x79d182ad, "walk_speed")))
		{
			decel_factor = 1.0f;
			if (horizontal_speed > Script::GetFloat("start_stand"))
			{
				m_frame_event = CRCD(0x1d537eff, "Skid");
			}
			else
			{
				m_frame_event = CRCD(0x9b46e749, "Stand");
			}
		}
		*/
		
		horizontal_speed = Mth::Lerp(horizontal_speed, desired_speed, decel_factor * m_frame_length);
	}
	else
	{
		if (m_run_toggle)
		{
			horizontal_speed += s_get_param(CRCD(0x4f47c998, "run_accel_rate")) * m_frame_length;
		}
		else
		{
			horizontal_speed += s_get_param(CRCD(0x6590a49b, "walk_accel_rate")) * m_frame_length;
		}
		if (horizontal_speed > desired_speed)
		{
			horizontal_speed = desired_speed;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool  CWalkComponent::adjust_horizonal_vel_for_environment (   )
{
	// We send out feeler rays to find nearby walls.  We limit velocity to be flush with the first wall found.  If two or more non-parallel walls
	// are found, velocity is zeroed.
	
	float feeler_length = s_get_param(CRCD(0x99978d2b, "feeler_length"));
	float feeler_height = s_get_param(CRCD(0x6da7f696, "feeler_height"));
	
	CFeeler feeler;
	
	bool contact = false;
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		// setup the the feeler
		
		if (n == vNUM_FEELERS)
		{
			// final feeler is for air state only and is at the feet; solves situations in which the feet impact with vertical surfaces which the
			// wall feelers are too high to touch
			if (m_state != WALKING_AIR)
			{
				mp_contacts[vNUM_FEELERS].in_collision = false;
				continue;
			}
			
			feeler.m_start = m_pos + m_critical_point_offset;
			feeler.m_end = feeler.m_start + m_horizontal_vel * m_frame_length;
			feeler.m_end[Y] += m_vertical_vel * m_frame_length + 0.5f * -get_gravity() * Mth::Sqr(m_frame_length);
		}
		else
		{
			feeler.m_start = m_pos;
			feeler.m_start[Y] += feeler_height;
			feeler.m_end = m_pos + feeler_length * calculate_feeler_offset_direction(n);
			feeler.m_end[Y] += feeler_height;
		}
		
		mp_contacts[n].in_collision = feeler.GetCollision();
		
		if (!mp_contacts[n].in_collision)
		{
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(0, 0, 255, 1);
			}
			#endif
			continue;
		}
		
		if (n == vNUM_FEELERS)
		{
			// feet collisions only count for walls
			if (feeler.GetNormal()[Y] > 0.707f)
			{
				mp_contacts[n].in_collision = false;
				continue;
			}
		}
		
		// store the feeler
		mp_contacts[n].feeler = feeler;
		
		contact = true;
		
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(255, 0, 0, 1);
		}
		#endif

		// grab the horizontal normal of the contacted wall
		mp_contacts[n].normal = feeler.GetNormal();
		mp_contacts[n].normal[Y] = 0.0f;
		mp_contacts[n].normal.Normalize();
		
		// if we're on the moving object, don't count its movement when doing collision detection, as the walker's velocity is already measured
		// relative to its movable contact's
		if (feeler.IsMovableCollision()
			&& (!mp_movable_contact_component->HaveContact() || mp_movable_contact_component->GetContact()->GetObject() != feeler.GetMovingObject()))
		{
			mp_contacts[n].movement = Mth::DotProduct(feeler.GetMovingObject()->GetVel(), mp_contacts[n].normal);
		}
		else
		{
			mp_contacts[n].movement = 0.0f;
		}
	}
	
	// check for wall push
	if (m_state == WALKING_GROUND)
	{
		if (check_for_wall_push())
		{
			// if we're wall pushing, we may decide to switch states based on our environment
			
			/* no auto-hop-to-hang
			if (Tmr::ElapsedTime(m_wall_push_test.test_start_time) > s_get_param(CRCD(0x928e6775, "hop_delay")))
			{
				if (maybe_climb_up_ladder() || maybe_jump_low_barrier()) return false;
			}
			else
			*/
			if (Tmr::ElapsedTime(m_wall_push_test.test_start_time) > s_get_param(CRCD(0x38d36700, "barrier_jump_delay")))
			{
				if (maybe_climb_up_ladder() || maybe_jump_low_barrier()) return false;
			}
		}
		
		if (mp_input_component->GetControlPad().m_R1.GetPressed() && !m_ignore_grab_button)
		{
			if (maybe_climb_up_ladder(true)/* || maybe_hop_to_hang()*/) return false;
		}
		
		if (mp_physics_control_component->HaveBeenReset()) return false;
	}
	
	// push down steep slopes
	if (m_state == WALKING_GROUND && m_ground_normal[Y] < 0.5f)
	{
		Mth::Vector push_dir = m_ground_normal;
		push_dir[Y] = 0.0f;
		push_dir.Normalize();
		m_pos += s_get_param(CRCD(0x4d16f37d, "push_strength")) * m_frame_length * push_dir;
		DUMP_WPOSITION
	}
	
	if (!contact) return false;
	
	// push away from walls
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		if (!mp_contacts[n].in_collision) continue;
		                
		if (mp_contacts[n].feeler.GetDist() < s_get_param(CRCD(0xa20c43b7, "push_feeler_length")) / feeler_length)
		{
			m_pos += s_get_param(CRCD(0x4d16f37d, "push_strength")) * m_frame_length * mp_contacts[n].normal;
			DUMP_WPOSITION
		}
	}
	
	// from here on we ignore collisions we're moving out of
	contact = false;
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		if (!mp_contacts[n].in_collision) continue;
		
		// don't count collisions we're moving out of
		if (Mth::DotProduct(mp_contacts[n].normal, m_horizontal_vel) >= mp_contacts[n].movement)
		{
			mp_contacts[n].in_collision = false;
		}
		else
		{
			contact = true;
		}
	}
	if (!contact) return false;
	
	// Now we calculate how our movement is effected by our collisions.  The movement must have a non-negative dot product with all collision normals.
	// The algorithm used should be valid for all convex environments.
	
	// if any of the colllision normals are more than right angles to one another, no movement is possible
	// NOTE: not valid with movable contacts; could cause jerky movement in corners where walls are movable
	for (int n = 0; n < vNUM_FEELERS + 1; n++)
	{
		if (!mp_contacts[n].in_collision) continue;
		for (int m = n + 1; m < vNUM_FEELERS + 1; m++)
		{
			if (!mp_contacts[m].in_collision) continue;
			if (Mth::DotProduct(mp_contacts[n].normal, mp_contacts[m].normal) <= 0.0f)
			{
				m_horizontal_vel.Set();
				m_anim_effective_speed = Mth::Min(s_get_param(CRCD(0xbd6a05d, "min_anim_run_speed")), m_anim_effective_speed);
				m_drop_rotation_rate = true;
				return true;
			}
		}
	}
	
	// direction of proposed movement
	Mth::Vector movement_direction = m_horizontal_vel;
	movement_direction.Normalize();
	
	Mth::Vector adjusted_vel = m_horizontal_vel;
	
	// loop over the contacts (from backward to forward)
	const int p_contact_idxs [ vNUM_FEELERS + 1 ] = { 7, 4, 3, 5, 2, 6, 1, 0 };
	for (int i = 0; i < vNUM_FEELERS + 1; i++)
	{
		int n = p_contact_idxs[i];
		
		if (!mp_contacts[n].in_collision) continue;
		
		// check to see if the movement still violates this constraint
		float normal_vel = Mth::DotProduct(adjusted_vel, mp_contacts[n].normal);
		if (normal_vel >= mp_contacts[n].movement) continue;
		
		// adjust the movement to the closest direction allowed by this contraint
		adjusted_vel -= (normal_vel - mp_contacts[n].movement) * mp_contacts[n].normal;
		
		// if the mvoement direction no longer points in the direction of the proposed movement, no movement occurs
		if (Mth::DotProduct(adjusted_vel, m_horizontal_vel) <= 0.0f)
		{
			m_horizontal_vel.Set();
			m_anim_effective_speed = Mth::Min(s_get_param(CRCD(0xbd6a05d, "min_anim_run_speed")), m_anim_effective_speed);
			m_drop_rotation_rate = true;
			return true;
		}
	}
	
	// insure that the adjusted velocity in the final direction is not larger than the projection of the initial velocity into that direction
	float adjusted_speed = adjusted_vel.Length();
	Mth::Vector adjusted_vel_direction = adjusted_vel;
	adjusted_vel_direction *= 1.0f / adjusted_speed;
	float projected_vel = Mth::DotProduct(m_horizontal_vel, adjusted_vel_direction);
	
	if (adjusted_speed > projected_vel)
	{
		adjusted_vel = adjusted_vel_direction * projected_vel;
	}
	
	m_drop_rotation_rate = adjusted_vel.LengthSqr() / m_horizontal_vel.LengthSqr() < Mth::Sqr(0.5f);
	
	// only the velocity along the movement direction is retained
	m_horizontal_vel = adjusted_vel;
	
	float final_horiz_vel = m_horizontal_vel.Length();
	if (m_anim_effective_speed > s_get_param(CRCD(0xbd6a05d, "min_anim_run_speed")))
	{
		m_anim_effective_speed = final_horiz_vel;
		m_anim_effective_speed = Mth::Max(s_get_param(CRCD(0xbd6a05d, "min_anim_run_speed")), m_anim_effective_speed);
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::adjust_facing_for_adjusted_horizontal_vel (   )
{
	// We adjust facing due to adjustment in horizontal velocity due to environment.  Basically, we want to object to turn to face the velocity
	// that the environment has forced upon it.
	
	// IDEA: shift to basing turn amount on angle difference and not speed
	
	float horizontal_speed = m_horizontal_vel.Length();
	
	if (horizontal_speed < s_get_param(CRCD(0x515a933, "wall_turn_speed_threshold"))) return;
	
	// the new facing is in the direction of our adjusted velocity
	Mth::Vector new_facing = m_horizontal_vel;
	new_facing.Normalize();
	
	// smoothly transition between no wall turning to full wall turning
	float turn_ratio;
	if (horizontal_speed > s_get_param(CRCD(0xe6c1cd0d, "max_wall_turn_speed_threshold")))
	{
		turn_ratio = s_get_param(CRCD(0x7a583b9b, "wall_turn_factor")) * m_frame_length;
	}
	else
	{
		turn_ratio = Mth::LinearMap(
			0.0f,
			s_get_param(CRCD(0x7a583b9b, "wall_turn_factor")) * m_frame_length,
			horizontal_speed,
			s_get_param(CRCD(0x0515a933, "wall_turn_speed_threshold")),
			s_get_param(CRCD(0xe6c1cd0d, "max_wall_turn_speed_threshold"))
		);
	}
	
	// exponentially approach new facing
	if (turn_ratio >= 1.0f)
	{
		m_facing = new_facing;
	}
	else
	{
		m_facing = Mth::Lerp(m_facing, new_facing, turn_ratio);
		m_facing.Normalize();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CWalkComponent::adjust_desired_speed_for_slope ( float desired_speed )
{
	// Slow velocity up and down slopes.
	
	// skip if there is no appreciable slope
	if (m_ground_normal[Y] > 0.95f) return desired_speed;
	
	// skip if not running
	if (desired_speed <= s_get_param(CRCD(0x79d182ad, "walk_speed"))) return desired_speed;
	
	// calculate a horizontal vector up the slope
	Mth::Vector up_slope = m_ground_normal;
	up_slope[Y] = 0.0f;
	up_slope.Normalize();
	
	// horizontal factor of velocity if the velocity were pointing along the slope (instead of along the horizontal)
	float movement_factor = m_ground_normal[Y];
	
	// factor of velocity pointing up the slope
	float dot = Mth::Abs(Mth::DotProduct(m_facing, up_slope));
	
	// scale the up-the-slope element of velocity based on the slope strength
	return (1.0f - dot) * desired_speed + dot * movement_factor * desired_speed;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::respond_to_ground (   )
{
	// Look for the ground below us.  If we find it, snap to it.  If not, go to air state.
	
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_start[Y] += s_get_param(CRCD(0xcee3a3e1, "snap_up_height"));
	feeler.m_end = m_pos;
	feeler.m_end[Y] -= s_get_param(CRCD(0xaf3e4251, "snap_down_height"));
	
	if (!feeler.GetCollision() || (feeler.GetFlags() & mFD_NOT_SKATABLE))
	{
		if (feeler.GetCollision())
		{
			m_pos = feeler.GetPoint() + 0.1f * feeler.GetNormal();
			DUMP_WPOSITION
		}
		
		// no ground
		
		if (m_last_ground_feeler_valid)
		{
			mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF_EDGE, m_last_ground_feeler);
			if (mp_physics_control_component->HaveBeenReset()) return;
		}
		
		if (mp_input_component->GetControlPad().m_triangle.GetPressed())
		{
			// need to give the player a change to rail before climbing down ladders and such
			
			// if we just climbed up to a rail and are immediately falling, we need some minute amount of movement in order to find a rail
			if (m_pos == m_frame_start_pos)
			{
				m_frame_start_pos[Y] += 0.001f;
			}
			
			if (maybe_stick_to_rail()) return;
		}
		
		if (maybe_climb_down_ladder() || maybe_drop_to_hang()) return;
		
		// go to air state
		set_state(WALKING_AIR);
		m_primary_air_direction = m_facing;
		leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
		m_frame_event = CRCD(0xabf1f6ac, "WalkOffEdge");
		GetObject()->BroadcastEvent(CRCD(0xd96f01f1, "SkaterOffEdge"));
		return;
	}
	
	float snap_distance = feeler.GetPoint()[Y] - m_pos[Y];
	
	// no not send event for very small snaps
	if (Mth::Abs(snap_distance) > s_get_param(CRCD(0xd3193d8e, "max_unnoticed_ground_snap")))
	{
		GetObject()->SelfEvent(snap_distance > 0.0f ? CRCD(0x93fcf3ed, "SnapUpEdge") : CRCD(0x56e21153, "SnapDownEdge"));
	}
	
	// snap position to the ground
	m_pos[Y] = feeler.GetPoint()[Y];
	DUMP_WPOSITION
	
	// adjust stair float distance
	if (snap_distance > 0.0f || snap_distance < -3.0f)
	{
		m_curb_float_height = Mth::ClampMin(m_curb_float_height - snap_distance, 0.0f);
	}
	
	// see if we've changed sectors
	if (m_last_ground_feeler.GetSector() != feeler.GetSector())
	{
		if (m_last_ground_feeler_valid)
		{
			mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF, m_last_ground_feeler);
			if (mp_physics_control_component->HaveBeenReset()) return;
		}
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_ONTO, feeler);
		if (mp_physics_control_component->HaveBeenReset()) return;
	}
	
	// stash the ground feeler so that we can trip the group's triggers at a later time
	m_last_ground_feeler = feeler;
	m_last_ground_feeler_valid = true;
	
	// set the ground normal for next frame's velocity slope adjustment
	m_ground_normal = feeler.GetNormal();
	
	// NOTE: need to repeat this code anywhere we enter the ground state
	mp_movable_contact_component->CheckForMovableContact(feeler);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::adjust_curb_float_height (   )
{
	// adjust m_curb_float_height to smooth out moving up stairs
	
	// When facing a curb, we smoothly increase m_curb_float_height to the height of the curb.  When we snap up the curb, m_curb_float_height is then
	// reduced by an amount equal to the snap distance.
	// When we snap down a curb, m_curb_float_height is increased by the snap distance.  We then drop m_curb_float_height smoothly to zero.
	
	// determine appropriate direction to search for a curb
	Mth::Vector feeler_direction = m_facing;
	feeler_direction.ProjectToPlane(m_ground_normal);
	feeler_direction[Y] = Mth::ClampMin(feeler_direction[Y], 0.0f);
	feeler_direction.Normalize();
	
	// look for a curb
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_start[Y] += 0.5f;
	feeler.m_end = m_pos + s_get_param(CRCD(0x11edcc52, "curb_float_feeler_length")) * feeler_direction;
    feeler.m_end[Y] += 0.5f;
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 255, 0, 1);
	}
	#endif
	
	if (feeler.GetCollision())
	{
		// grab the distance to the curb
		float distance_to_curb = feeler.GetDist();
		
		// look up from the curb to find the curb height
		feeler.m_end = feeler.GetPoint() + 0.5f * feeler_direction;
		feeler.m_start = feeler.m_end;
		feeler.m_start[Y] = m_pos[Y] + s_get_param(CRCD(0xcee3a3e1, "snap_up_height"));
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(0, 255, 255, 1);
		}
		#endif
		
		if (feeler.GetCollision())
		{
			// calculate the m_curb_float_height we should have based on the curb height and distance
			float appropriate_curb_float_height = (1.0f - distance_to_curb) * (feeler.GetPoint()[Y] - m_pos[Y]);
			
			if (Mth::Abs(m_curb_float_height) < 0.01f && m_control_magnitude == 0.0f && m_horizontal_vel.LengthSqr() < Mth::Sqr(s_get_param(CRCD(0x227d72ee, "min_curb_height_adjust_vel"))))
			{
				// don't update the curb height if we're on the ground and standing still; this is mostly to prevent snapping up right after landing a jump
			}
			else
			{
				// lerp to the appropriate height
				m_curb_float_height = Mth::Lerp(m_curb_float_height, appropriate_curb_float_height, s_get_param(CRCD(0x856a80d3, "curb_float_lerp_up_rate")) * m_frame_length);
			}
			
			m_curb_float_height_adjusted = true;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::account_for_movable_contact (   )
{
	if (!mp_movable_contact_component->UpdateContact(m_pos)) return;
	
	m_pos += mp_movable_contact_component->GetContact()->GetMovement();
	DUMP_WPOSITION
	
	if (mp_movable_contact_component->GetContact()->IsRotated())
	{
		m_facing = mp_movable_contact_component->GetContact()->GetRotation().Rotate(m_facing);
		if (m_facing[Y] != 0.0f)
		{
			m_facing[Y] = 0.0f;
			m_facing.Normalize();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::jump (   )
{
	// switch to air state and give the object an upwards velocity
	
	float strength = 0.0f;
	switch (m_state)
	{
		case WALKING_GROUND:
		case WALKING_AIR:
			// jump strength scales with the length the jump button has been held
			strength = Mth::Lerp(
				s_get_param(CRCD(0x246d0bf3, "min_jump_factor")), 
				1.0f,
				Mth::ClampMax(mp_input_component->GetControlPad().m_x.GetPressedTime() / s_get_param(CRCD(0x12333ebd, "hold_time_for_max_jump")), 1.0f)
			);
			break;
			
		case WALKING_HANG:
		case WALKING_LADDER:
		case WALKING_ANIMWAIT:
			strength = s_get_param(CRCD(0xf2fa5845, "hang_jump_factor"));
			break;
	}
	
	// if we're jumping from the ground, trip the ground's triggers
	if (m_state == WALKING_GROUND && m_last_ground_feeler_valid)
	{
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, m_last_ground_feeler);
		if (mp_physics_control_component->HaveBeenReset()) return;
	}
	
	// Called by script from outside of the component update, so m_vertical_vel is not used.
	GetObject()->GetVel()[Y] = strength * s_get_param(CRCD(0x63d62a21, "jump_velocity"));
	
	// jumps for ladders and hanging get a backwards velocity
	switch (m_state)
	{
		case WALKING_GROUND:
		case WALKING_AIR:
		{
			extract_state_from_object();
	
			if (m_control_magnitude)
			{
				float min_launch_speed = calculate_desired_speed()
					* s_get_param(CRCD(0x839fe542, "jump_horiz_speed")) / get_run_speed();
				
				if (Mth::DotProduct(m_horizontal_vel, m_control_direction) > 0.0f)
				{
					m_horizontal_vel.ProjectToNormal(m_control_direction);
					if (m_horizontal_vel.Length() < min_launch_speed)
					{
						m_horizontal_vel.Normalize(min_launch_speed);
					}
				}
				else
				{
					m_horizontal_vel = min_launch_speed * m_control_direction;
				}
				
				m_primary_air_direction = m_control_direction;
				m_facing = m_control_direction;
			}
			else
			{
				m_primary_air_direction = m_facing;
			}
			
			adjust_jump_for_ceiling_obstructions();
			
			// setup primary air direction
			float length_sqr = m_horizontal_vel.LengthSqr();
			if (length_sqr > 0.01f)
			{
				m_primary_air_direction = m_horizontal_vel;
				m_primary_air_direction /= sqrtf(length_sqr);
			}
			else
			{
				m_primary_air_direction = m_facing;
			}
			
			copy_state_into_object();
			
			break;
		}
		
		case WALKING_ANIMWAIT:
			m_false_wall.active = true;
			m_false_wall.distance = Mth::DotProduct(m_false_wall.normal, m_pos + m_critical_point_offset);
			
			GetObject()->GetVel()[X] = 0.0f;
			GetObject()->GetVel()[Z] = 0.0f;
			m_primary_air_direction = m_facing;
			break;
			
		case WALKING_HANG:
			m_false_wall.active = true;
			m_false_wall.normal = m_facing;
			m_false_wall.distance = Mth::DotProduct(m_false_wall.normal, m_pos + m_critical_point_offset);
			m_false_wall.cancel_height = m_pos[Y] - m_vertical_hang_offset;
			
			GetObject()->GetVel()[X] = 0.0f;
			GetObject()->GetVel()[Z] = 0.0f;
			m_primary_air_direction = m_facing;
			break;

		case WALKING_LADDER:
			GetObject()->GetVel()[X] = 0.0f;
			GetObject()->GetVel()[Z] = 0.0f;
			m_primary_air_direction = m_facing;
			break;
	}
	
	leave_movable_contact_for_air(GetObject()->GetVel(), GetObject()->GetVel()[Y]);
	
	set_state(WALKING_AIR);
	
	GetObject()->BroadcastEvent(CRCD(0x8687163a, "SkaterJump"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::adjust_jump_for_ceiling_obstructions (   )
{
	// check for obstructions above and drift backwards to avoid them if we can; this prevents us from banging our head on overhangs when we're up
	// against the wall and trying to jump to hang on them
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_start[Y] += s_get_param(CRCD(0x9ea1974a, "walker_height"));
	feeler.m_end = feeler.m_start;
	feeler.m_end[Y] += s_get_param(CRCD(0x48c0ac2a, "jump_obstruction_check_height"));
	if (feeler.GetCollision(false))
	{
		float jump_obstruction_check_back = s_get_param(CRCD(0x6ebbab7, "jump_obstruction_check_back"));
		float obstruction_height = feeler.GetPoint()[Y] - feeler.m_start[Y];

		feeler.m_start -= m_facing * jump_obstruction_check_back;
		feeler.m_end -= m_facing * jump_obstruction_check_back;

		if (!feeler.GetCollision(false))
		{

			float acceleration = get_gravity();
			float vel_sqr_at_obstruction_height = Mth::Sqr(m_vertical_vel) - 2.0f * acceleration * obstruction_height;
			if (vel_sqr_at_obstruction_height > 0.0f)
			{
				float time_to_height = (m_vertical_vel - sqrt(vel_sqr_at_obstruction_height)) / acceleration;

				// setup in air drift
				m_in_air_drift_vel = -jump_obstruction_check_back / time_to_height * m_facing;
				m_in_air_drift_stop_height = m_pos[Y] + obstruction_height;

				// suppress control until we return to this height from above
				m_in_air_control_suppression_timer = 2.0f * m_vertical_vel / acceleration - time_to_height;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::go_in_air_state (   )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	setup_collision_cache();
	
	// default air event
	m_frame_event = CRCD(0x439f4704, "Air");
	
	// user control of horizontal velocity
	control_horizontal_vel();
	
	// prevent movement into walls
	if (!adjust_horizonal_vel_for_environment())
	{
		m_drop_rotation_rate = false;
	}
	if (mp_physics_control_component->HaveBeenReset()) return;
	
	// account for the in-air false wall
	adjust_pos_for_false_wall();
	
	// check for head bonking
	adjust_vertical_vel_for_ceiling();
	
	// apply movement and acceleration for this frame
	float acceleration = get_gravity();
	m_pos += m_horizontal_vel * m_frame_length;
	m_pos += m_in_air_drift_vel * m_frame_length;
	m_pos[Y] += m_vertical_vel * m_frame_length + 0.5f * -acceleration * Mth::Sqr(m_frame_length);
	DUMP_WPOSITION
	m_vertical_vel += -acceleration * m_frame_length;
	
	if (m_pos[Y] > m_in_air_drift_stop_height)
	{
		m_in_air_drift_vel.Set();
	}
	
	// see if we've landed yet
	check_for_landing(m_frame_start_pos, m_pos);
	if (m_state != WALKING_AIR || mp_physics_control_component->HaveBeenReset()) return;
	
	// maybe grab a rail; delay regrabbing of hang rails
	if (control_pad.m_R1.GetPressed() && !m_ignore_grab_button
		&& ((m_previous_state != WALKING_HANG && m_previous_state != WALKING_LADDER && m_previous_state != WALKING_ANIMWAIT)
		|| Tmr::ElapsedTime(m_state_timestamp) > s_get_param(CRCD(0xe6e0c0a4, "rehang_delay"))))
	{
		if (m_previous_state == WALKING_LADDER)
		{
			// can't regrab ladders
			if (maybe_grab_to_hang(m_frame_start_pos, m_pos))
			{
				CFeeler::sClearDefaultCache();
				return;
			}
		}
		else
		{
			if (maybe_grab_to_hang(m_frame_start_pos, m_pos) || maybe_grab_to_ladder(m_frame_start_pos, m_pos))
			{
				CFeeler::sClearDefaultCache();
				return;
			}
		}
	}
	
	CFeeler::sClearDefaultCache();
	
	if (control_pad.m_triangle.GetPressed() && maybe_stick_to_rail()) return;
	
	if (maybe_in_air_acid_drop()) return;
	
	maybe_wallplant();
	if (mp_physics_control_component->HaveBeenReset()) return;
	
	// insure that we do not slip through the cracks in the collision geometry which are a side-effect of moving collidable objects
	Mth::Vector previous_pos = m_pos;
	Mth::Vector critical_point = m_pos + m_critical_point_offset;
	Mth::Vector frame_start_critical_point = m_frame_start_pos + m_critical_point_offset;
	if (CCompositeObject* p_inside_object = mp_movable_contact_component->CheckInsideObjects(critical_point, frame_start_critical_point))
	{
		m_pos = critical_point - m_critical_point_offset;
		DUMP_WPOSITION
		
		MESSAGE("WALKING_AIR, within moving object");
		
		m_horizontal_vel.Set();
		m_vertical_vel = 0.0f;
		check_for_landing(m_pos, previous_pos);
	}
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::control_horizontal_vel (   )
{
	// We allow user control over the object's in air velocity.  The algorithm is complicated by the fact that the forward velocity of the jump needs
	// to be accounted for when allowing for velocity adjustment.  It is assumed that the jump direction is the same as the facing.
	
	if (m_in_air_control_suppression_timer != 0.0f)
	{
		m_in_air_control_suppression_timer = Mth::ClampMin(m_in_air_control_suppression_timer - m_frame_length, 0.0f);
		return;
	}
	
	// remove uncontrollable velocity term
	m_horizontal_vel -= m_uncontrollable_air_horizontal_vel;
	
	#ifdef SCRIPT_WALK_DRAG
	// adjust speed by the script set drag factor
	float adjust_magnitude = m_control_magnitude * m_script_drag_factor;
	#else
	float adjust_magnitude = m_control_magnitude;
	#endif
	
	// adjust velocity perpendicular to jump direction
	
	// direction perpendicular to jump direction
	Mth::Vector perp_direction(-m_primary_air_direction[Z], 0.0f, m_primary_air_direction[X]);
	
	// desired perpendicular velocity
	float perp_desired_vel = s_get_param(CRCD(0x896c8888, "jump_adjust_speed")) * adjust_magnitude * Mth::DotProduct(m_control_direction, perp_direction);
	
	// current perpendicular velocity
	float perp_vel = Mth::DotProduct(m_horizontal_vel, perp_direction);
	
	// exponentially approach desired velocity
	perp_vel = Mth::Lerp(perp_vel, perp_desired_vel, s_get_param(CRCD(0xf085443b, "jump_accel_factor")) * m_frame_length);
		
	// adjust velocity parallel to jump direction
	
	// desired parallel velocity
	float para_desired_vel = s_get_param(CRCD(0x896c8888, "jump_adjust_speed")) * adjust_magnitude * Mth::DotProduct(m_control_direction, m_primary_air_direction);
	
	// current parallel velocity
	float para_vel = Mth::DotProduct(m_horizontal_vel, m_primary_air_direction);
	
    // if desired velocity if forward and forward velocity already exceeds adjustment velocity
	if (para_desired_vel >= 0.0f && para_vel > para_desired_vel)
	{
		// do nothing; don't slow down the jump
	}
	else
	{
		// adjust desired velocity to center around current velocity to insure that our in air stopping ability is not too amazing
		if (para_desired_vel < 0.0f && para_vel > 0.0f)
		{
			para_desired_vel += para_vel;
		}
		
		// exponentially approach desired velocity
		para_vel = Mth::Lerp(para_vel, para_desired_vel, s_get_param(CRCD(0xf085443b, "jump_accel_factor")) * m_frame_length);
	}
		
	// rebuild horizontal velocity from parallel and perpendicular components
	m_horizontal_vel = para_vel * m_primary_air_direction + perp_vel * perp_direction;
	
	// reinstitute uncontrollable velocity term
	m_horizontal_vel += m_uncontrollable_air_horizontal_vel;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::adjust_vertical_vel_for_ceiling (   )
{
	// if we hit our head, zero vertical velocity
	
	// no ceiling collisions when a false wall is active; otherwise, ceilings would kill our jump when we jump from hanging on a ledge which sticks out.
	if (m_false_wall.active) return;
	
	float walker_height = s_get_param(CRCD(0x9ea1974a, "walker_height"));
	
	// be more forgiving if we're moving down already
	if (m_vertical_vel < 0.0f)
	{
		walker_height -= 18.0f;
	}
	
	// look for a collision up through the body to the head
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_end = m_pos;
	feeler.m_end[Y] += walker_height;
	if (!feeler.GetCollision()) return;
	
	// project velocity into plane
	m_horizontal_vel[Y] = m_vertical_vel;
	if (Mth::DotProduct(m_horizontal_vel, feeler.GetNormal()) < 0.0f)
	{
		m_horizontal_vel.ProjectToPlane(feeler.GetNormal());
	}
	m_vertical_vel = m_horizontal_vel[Y];
	m_horizontal_vel[Y] = 0.0f;
	
	// determine the displacement vector we must use to get out of the ceiling
	Mth::Vector displacement = feeler.GetPoint() - feeler.m_end;
	displacement.ProjectToNormal(feeler.GetNormal());
	
	// insure there are no collisions along this displacement
	feeler.m_end = m_pos + displacement;
	if (feeler.GetCollision())
	{
		m_pos = feeler.GetPoint() + feeler.GetNormal();
		DUMP_WPOSITION
	}
	else
	{
		m_pos = feeler.m_end;
		DUMP_WPOSITION
	}
	
	GetObject()->SelfEvent(CRCD(0x6e84acf3, "HitCeiling"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::check_for_landing ( const Mth::Vector& previous_pos, const Mth::Vector& final_pos )
{
	// See if our feet have passed through geometry.  If so, snap to it and go to ground state.
	
	CFeeler feeler;
	feeler.m_start = previous_pos;
	feeler.m_end = final_pos;
	if (!feeler.GetCollision()) return;
	
	if (feeler.GetFlags() & mFD_NOT_SKATABLE)
	{
        m_pos = feeler.GetPoint() + 0.1f * feeler.GetNormal();
		DUMP_WPOSITION
		
		Mth::Vector remaining_movement = (1.0f - feeler.GetDist()) * (feeler.m_end - feeler.m_start);
		remaining_movement.ProjectToPlane(feeler.GetNormal());
		
		// slide along the surface, but stop at any new collision
		feeler.m_start = m_pos;
		feeler.m_end = m_pos + remaining_movement;
		if (feeler.GetCollision())
		{
			m_pos = feeler.GetPoint() + 0.05f * feeler.GetNormal();
			DUMP_WPOSITION
		}
		else
		{
			m_pos = feeler.m_end;
			DUMP_WPOSITION
		}
		return;
	}
	
	// snap to the collision point
	m_pos = feeler.GetPoint();
	DUMP_WPOSITION
	
	// zero vertical velocity
	m_vertical_vel = 0.0f;
	
	// change to ground state
	set_state(WALKING_GROUND);
	
	// stash the feeler
	m_last_ground_feeler = feeler;
	m_last_ground_feeler_valid = true;
	
	// trip any land trigger
	mp_trigger_component->CheckFeelerForTrigger(TRIGGER_LAND_ON, m_last_ground_feeler);
	if (mp_physics_control_component->HaveBeenReset()) return;
	
	// setup our ground normal for next frames velocity slope adjustment
	m_ground_normal = feeler.GetNormal();
	
	// check for a moving contact
	mp_movable_contact_component->CheckForMovableContact(feeler);
	if (mp_movable_contact_component->HaveContact())
	{
		m_horizontal_vel -= mp_movable_contact_component->GetContact()->GetObject()->GetVel();
		m_horizontal_vel[Y] = 0.0f;
	}
	
	// retain only that velocity which is parallel to our facing and forward
	if (Mth::DotProduct(m_horizontal_vel, m_facing) > 0.0f)
	{
		m_horizontal_vel.ProjectToNormal(m_facing);
		
		if (m_control_magnitude == 0.0f && m_horizontal_vel.Length() < s_get_param(CRCD(0x530dcf34, "sticky_land_threshold_speed")))
		{
			m_horizontal_vel.Set();
		}
	}
	else
	{
		m_horizontal_vel.Set();
	}

	m_critical_point_offset.Set();

	// clear any jump requests
	mp_input_component->GetControlPad().m_x.ClearRelease();

	m_frame_event = CRCD(0x57ff2a27, "Land");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::maybe_wallplant (   )
{
	if (!mp_input_component->GetControlPad().m_x.GetPressed()) return;
	
	// only wallplant on the way down
	if (m_vertical_vel > 0.0f) return;
					   	
	// not when you're too near the ground
	if (mp_state_component->m_height < Script::GetFloat(CRCD(0xd5349cc6, "Physics_Min_Wallplant_Height"))) return;

	// if (Tmr::ElapsedTime(m_state_timestamp) < s_get_param(CRCD(0x2a2d65c, "min_air_before_wallplant"))) return;
	
	// last wallplant must not have been too recently
	if (Tmr::ElapsedTime(mp_core_physics_component->m_last_wallplant_time_stamp) < Script::GetFloat(CRCD(0x82135dd7, "Physics_Disallow_Rewallplant_Duration"))) return;
	
	// no wallplants immediately after you enter air; stops wallplants during the late jump period
	if (Tmr::ElapsedTime(m_state_timestamp) < Script::GetFloat(CRCD(0x4c2b6df3, "Skater_Late_Jump_Slop"))) return;
	
	// identify the primary contact wall; not that we are ignoring the "feet feeler"
	int n;
	int contact_idx;
	const int p_contact_indxs[vNUM_FEELERS] = { 0, 1, 6, 2, 5, 3, 4 };
	for (n = 0; n < vNUM_FEELERS; n++)
	{
		if (mp_contacts[contact_idx = p_contact_indxs[n]].in_collision) break;
	}
	if (n == vNUM_FEELERS) return;
	
	/*
	// here we attempt to stop wallplant when in is more likely that the player is going for a grind or wants to jump up over the wall
	if (GetObject()->m_vel[Y] > 0.0f)
	{
		Mth::Vector wall_point = mp_contacts[contact_idx].feeler.GetPoint();
		Mth::Vector	wall_normal = mp_contacts[contact_idx].feeler.GetNormal();

		Mth::Vector wall_up_vel(0.0f, GetObject()->m_vel[Y] * 0.15f, 0.0f);		// check 0.15 seconds ahead
		wall_up_vel.RotateToPlane(wall_normal);  

		// check at what height will be in two frames
		wall_point += wall_up_vel;		

		CFeeler feeler(wall_point + wall_normal * 6.0f, wall_point - wall_normal * 6.0f);
		if (!feeler.GetCollision())
		{
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(255, 255, 0, 0);
			}
			#endif
			return;
		}
		else
		{
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(255, 0, 255, 0);
			}
			#endif
		}
	}
	*/
	
	// reflect the control direction off the wall and use it as our facing and boost direction
	Mth::Vector exit_dir;
	if (m_control_magnitude == 0.0f)
	{
		exit_dir = m_primary_air_direction;
	}
	else
	{
		exit_dir = m_control_direction;
	}
	float dot = Mth::DotProduct(exit_dir, mp_contacts[contact_idx].normal);
	if (dot < 0.0f)
	{
		exit_dir -= 2.0f * dot * mp_contacts[contact_idx].normal;
	}
	
	// set vertical velocity to the walking wallplant jump speed
	m_vertical_vel = s_get_param(CRCD(0x420d18bc, "vert_wall_jump_speed")) * Mth::Lerp(
		s_get_param(CRCD(0x246d0bf3, "min_jump_factor")), 
		1.0f,
		Mth::ClampMax(mp_input_component->GetControlPad().m_x.GetPressedTime() / s_get_param(CRCD(0x12333ebd, "hold_time_for_max_jump")), 1.0f)
	);
	
	// jump out from the wall
	m_horizontal_vel = s_get_param(CRCD(0x99510695, "horiz_wall_jump_speed")) * exit_dir;
	m_primary_air_direction = m_facing;

	// if the wall is moving, add on its velocity
	if (mp_contacts[contact_idx].feeler.IsMovableCollision())
	{
		Mth::Vector movable_contact_vel = mp_contacts[contact_idx].feeler.GetMovingObject()->GetVel();
		m_vertical_vel += movable_contact_vel[Y];
		m_horizontal_vel[X] += movable_contact_vel[X];
		m_horizontal_vel[Z] += movable_contact_vel[Z];
	}
	
	adjust_jump_for_ceiling_obstructions();
	
	// trip any triggers
	mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, mp_contacts[contact_idx].feeler);
	if (mp_physics_control_component->HaveBeenReset()) return;
	
	// graffiti the object
	GetTrickComponentFromObject(GetObject())->TrickOffObject(mp_contacts[contact_idx].feeler.GetNodeChecksum());
	
	// time stamp the wallplant
	mp_core_physics_component->m_last_wallplant_time_stamp = Tmr::GetTime();
	
	m_frame_event = CRCD(0xcf74f6b7, "WallPlant");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::uber_frig (   )
{
	// insure that we don't fall to the center of the earth, even if there are holes in the geometry; also, do lighting since we've got the feeler anyway
	
	CFeeler feeler;
	feeler.m_start = m_pos + m_critical_point_offset;
	feeler.m_end = feeler.m_start;
	feeler.m_start[Y] += 1.0f;
	feeler.m_end[Y] -= FEET(400);
	
	if (feeler.GetCollision())
	{
		// set the height for script access
		mp_state_component->m_height = m_pos[Y] - feeler.GetPoint()[Y];
		
		mp_model_component->ApplyLightingFromCollision(feeler);
		
		// Store these values off for the simple shadow calculation.
		CShadowComponent* p_shadow_component = GetShadowComponentFromObject(GetObject());
		p_shadow_component->SetShadowPos(feeler.GetPoint());
		p_shadow_component->SetShadowNormal(feeler.GetNormal()); 
		
		return;
	}
	
	MESSAGE("applying uber frig");
	
	// teleport us back to our position at the frame's start; not pretty, but this isn't supposed to be
	m_pos = m_frame_start_pos;
	DUMP_WPOSITION
	
	// zero our velocity too
	m_horizontal_vel.Set();
	m_vertical_vel = 0.0f;
	
	// set our state to ground
	set_state(WALKING_GROUND);
			  	
	m_last_ground_feeler_valid = false;
	
	m_ground_normal.Set(0.0f, 1.0f, 0.0f);

	// reset our script state
	m_frame_event = CRCD(0x57ff2a27, "Land");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::lerp_upright (   )
{
    if (m_upward[Y] == 1.0f) return;
	
	if (m_upward[Y] > 0.999f)
	{
		m_upward.Set(0.0f, 1.0f, 0.0f);
		m_rotate_upright_timer = 0.0f;
		return;
	}
	
	if (m_rotate_upright_timer == 0.0f)
	{
		m_upward = Mth::Lerp(m_upward, Mth::Vector(0.0f, 1.0f, 0.0f), s_get_param(CRCD(0xf22c135, "lerp_upright_rate")) * Tmr::FrameLength());
		m_upward.Normalize();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::transition_lerp_upright (   )
{
	if (m_rotate_upright_timer != 0.0f)
	{
		float lerp_factor = Mth::LinearMap(0.0f, 1.0f, m_frame_length, 0.0f, s_get_param(CRCD(0xb0675803, "rotate_upright_duration")));
		
		GetObject()->GetMatrix().Rotate(m_rotate_upright_axis, m_rotate_upright_angle * lerp_factor);
		GetObject()->SetDisplayMatrix(GetObject()->GetMatrix());
		
		m_rotate_upright_timer -= m_frame_length;
		m_rotate_upright_timer = Mth::ClampMin(m_rotate_upright_timer, 0.0f);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::check_for_wall_push (   )
{
	// ensure we have a forward contact
	if (!mp_contacts[0].in_collision && !mp_contacts[1].in_collision && !mp_contacts[vNUM_FEELERS - 1].in_collision)
	{
		return m_wall_push_test.active = false;
	}
	
	// ensure that control is maxed out
	if (!m_control_pegged)
	{
		return m_wall_push_test.active = false;
	}
	
	if (!m_wall_push_test.active)
	{
		// if we're not testing, simply start the test
		m_wall_push_test.test_start_time = Tmr::GetTime();
		m_wall_push_test.active = true;
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_jump_low_barrier (   )
{
	// check to see if we're in a bail
	if (mp_core_physics_component->GetFlag(IS_BAILING)) return false;
	
	// For each forward contact in collision, we check to see if any lacks a collision at the maximum jump height.  For any that do, we find the first height
	// at which they are in contact.  The lowest such height is used at the target jump height.
	
	const int p_forward_contact_idxs[] = { 0, 1, vNUM_FEELERS - 1 };
	
	// we use the lowest hang height as the maximum autojump barrier height
	float top_feeler_height = s_get_param(CRCD(0xda85f5ae, "barrier_jump_highest_barrier"));
	float feeler_length = 3.0f * s_get_param(CRCD(0x99978d2b, "feeler_length"));
	float height_increment = (top_feeler_height - s_get_param(CRCD(0x6da7f696, "feeler_height"))) / vNUM_BARRIER_HEIGHT_FEELERS;
	
	CFeeler feeler;
	
	// setup collision cache
	Mth::CBBox bbox(
		m_pos - Mth::Vector(feeler_length + 1.0f, 0.0f, feeler_length + 1.0f),
		m_pos + Mth::Vector(feeler_length + 1.0f, top_feeler_height + 1.0f, feeler_length + 1.0f)
	);
	Nx::CCollCache* p_coll_cache = Nx::CCollCacheManager::sCreateCollCache();
	p_coll_cache->Update(bbox);
	feeler.SetCache(p_coll_cache);
	
	// loop over forward collisions and check to see if the barrier in each of their directions is jumpable
	bool jumpable = false;
	for (int i = 0; i < 3; i++)
	{
		int n = p_forward_contact_idxs[i];
		
		if (!mp_contacts[n].in_collision || (mp_contacts[n].feeler.GetFlags() & mFD_NOT_SKATABLE)) continue;
		
		// first check to see if the collision normal is not too transverse to the control direction, making a autojump unlikely to succeed
		if (Mth::DotProduct(mp_contacts[n].normal, m_control_direction) > -cosf(Mth::DegToRad(s_get_param(CRCD(0x78e6a5ec, "barrier_jump_max_angle")))))
		{
			mp_contacts[n].jumpable = false;
			continue;
		}
		
		feeler.m_start = m_pos;
		feeler.m_start[Y] += top_feeler_height;
		feeler.m_end = feeler.m_start + feeler_length * calculate_feeler_offset_direction(n);
		
		mp_contacts[n].jumpable = !feeler.GetCollision();
		
        if (mp_contacts[n].jumpable)
		{
			jumpable = true;
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(0, 0, 255, 0);
			}
			#endif
		}
		else
		{
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(255, 0, 0, 0);
			}
			#endif
		}
	}
	
	// if the barrier is not jumpable
	if (!jumpable)
	{
		// no autojump
		Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
		return false;
	}
	
	// loop over the jumpable collision directions
	float lowest_height = m_pos[Y] + top_feeler_height;
	for (int i = 0; i < 3; i++)
	{
		int n = p_forward_contact_idxs[i];
		
		if (!mp_contacts[n].in_collision) continue;
		if (!mp_contacts[n].jumpable) continue;
		
		feeler.m_start = m_pos;
		feeler.m_start[Y] += top_feeler_height;
		feeler.m_end = feeler.m_start + feeler_length * calculate_feeler_offset_direction(n);
		
		// look for the barrier height
		for (int h = vNUM_BARRIER_HEIGHT_FEELERS - 1; h--; )
		{
			feeler.m_start[Y] -= height_increment;
			feeler.m_end[Y] -= height_increment;
			if (feeler.GetCollision())
			{
				#ifdef __USER_DAN__
				if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
				{
					feeler.DebugLine(255, 0, 0, 0);
				}
				#endif
				break;
			}
			else
			{
				#ifdef __USER_DAN__
				if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
				{
					feeler.DebugLine(0, 255, 0, 0);
				}
				#endif
			}
		}
		
		// find the lowest barrier of the jumpable directions
		if (lowest_height > feeler.m_start[Y])
		{
			lowest_height = feeler.m_start[Y];
		}
	}
	
	Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
	
	// caluclate the velocity required to clear the barrier
	float jump_height = lowest_height + height_increment + s_get_param(CRCD(0x72660978, "barrier_jump_min_clearance")) - m_pos[Y];
	float required_vertical_velocity = sqrtf(2.0f * get_gravity() * jump_height);
	
	if (m_last_ground_feeler_valid)
	{
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, m_last_ground_feeler);
		if (mp_physics_control_component->HaveBeenReset()) return true;
	}
	
	// setup the new walking state
	
	m_vertical_vel = required_vertical_velocity;
	
	set_state(WALKING_AIR);
	
	m_primary_air_direction = m_facing;
	leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
	
	m_frame_event = CRCD(0x584cf9e9, "Jump");
	
	GetObject()->BroadcastEvent(CRCD(0x8687163a, "SkaterJump"));
	
	// stop late jumps after an autojump
	GetObject()->RemoveEventHandler(CRCD(0x6b9ca247, "JumpRequested"));
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_in_air_acid_drop (   )
{
	if (m_disallow_acid_drops) return false;
	if (mp_physics_control_component->IsBoardMissing()) return false;
	
	CControlPad& control_pad = mp_input_component->GetControlPad();
	if (!WALK_SPINE_BUTTONS) return false;
	
	Mth::Vector vel(m_horizontal_vel[X], m_vertical_vel, m_horizontal_vel[Z]);
	if (!mp_core_physics_component->maybe_acid_drop(false, m_pos, m_frame_start_pos, vel, m_special_transition_data.acid_drop_data)) return false;
	
	m_horizontal_vel = vel;
	m_horizontal_vel[Y] = 0.0f;
	m_vertical_vel = vel[Y];
	m_special_transition_data.type = ACID_DROP_TRANSITION;
	m_frame_event = CRCD(0x2eda83ea, "AcidDrop");
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_jump_to_acid_drop (   )
{
	// we're on the ground; look ahead for acid drops to autojump into
	
	if (m_disallow_acid_drops) return false;
	if (mp_physics_control_component->IsBoardMissing()) return false;
	
	CControlPad& control_pad = mp_input_component->GetControlPad();
	if (!WALK_SPINE_BUTTONS) return false;
	
	Mth::Vector jump_vel = m_horizontal_vel;
	jump_vel[Y] = s_get_param(CRCD(0xc0491d2e, "acid_drop_jump_velocity"));
	
	if (!mp_core_physics_component->maybe_acid_drop(false, m_pos, m_frame_start_pos, jump_vel, m_special_transition_data.acid_drop_data)) return false;
	
	if (m_last_ground_feeler_valid)
	{
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, m_last_ground_feeler);
		if (mp_physics_control_component->HaveBeenReset()) return true;
	}
	
	set_state(WALKING_AIR);
	
	m_horizontal_vel = jump_vel;
	m_horizontal_vel[Y] = 0.0f;
	m_vertical_vel = jump_vel[Y];
	m_special_transition_data.type = ACID_DROP_TRANSITION;
	m_frame_event = CRCD(0x2eda83ea, "AcidDrop");
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::leave_movable_contact_for_air ( Mth::Vector& horizontal_vel, float& vertical_vel )
{
	// use movement from the latest movable contact update call
	
	if (mp_movable_contact_component->HaveContact())
	{
		// keep track of the horizontal velocity due to our old contact
		m_uncontrollable_air_horizontal_vel = mp_movable_contact_component->GetContact()->GetObject()->GetVel();

		if (Mth::DotProduct(m_uncontrollable_air_horizontal_vel, horizontal_vel) > 0.0f)
		{
			// extra kicker; dangerous as there's no collision detection; without this slight extra movement, when we walk off the front of a movable object,
			// the object will move back under us before our next frame, and we will clip its edge and land on it
			m_pos += m_uncontrollable_air_horizontal_vel * m_frame_length;
			DUMP_WPOSITION
		}
		
		// add movable contact's velocity into our launch velocity
		vertical_vel += m_uncontrollable_air_horizontal_vel[Y];
		m_uncontrollable_air_horizontal_vel[Y] = 0.0f;
		horizontal_vel += m_uncontrollable_air_horizontal_vel;
	}
	else
	{
		m_uncontrollable_air_horizontal_vel.Set();
	}
	
	mp_movable_contact_component->LoseAnyContact();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::calculate_anim_wait_facing_drift_parameters ( const Mth::Vector& goal_facing, float rotate_factor )
{
	float initial_angle = atan2f(m_facing[X], m_facing[Z]);
	float goal_angle = atan2f(goal_facing[X], goal_facing[Z]);
	
	m_drift_angle = goal_angle - initial_angle;
	if (Mth::Abs(m_drift_angle) > Mth::PI)
	{
		m_drift_angle -= Mth::Sgn(m_drift_angle) * (2.0f * Mth::PI);
	}
	
	m_drift_goal_facing = goal_facing;
	
	m_drift_goal_rotate_factor = rotate_factor;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::go_anim_wait_state (   )
{
	if (mp_movable_contact_component->UpdateContact(m_pos))
	{
		m_offset_due_to_movable_contact += mp_movable_contact_component->GetContact()->GetMovement();
		if (mp_movable_contact_component->GetContact()->IsRotated())
		{
			m_drift_goal_facing = mp_movable_contact_component->GetContact()->GetRotation().Rotate(m_drift_goal_facing);
			if (m_drift_goal_facing[Y] != 0.0f)
			{
				m_drift_goal_facing[Y] = 0.0f;
				m_drift_goal_facing.Normalize();
			}
			
		}
	}
	
	float start, current, end;
	mp_animation_component->GetPrimaryAnimTimes(&start, ¤t, &end);
    float animation_completion_factor = Mth::LinearMap(0.0f, 1.0f, current, start, end);
	
	// smoothly drift the position
	m_pos = m_offset_due_to_movable_contact + Mth::Lerp(m_anim_wait_initial_pos, m_anim_wait_goal_pos, animation_completion_factor);
	DUMP_WPOSITION
	
	float angle = Mth::Lerp(-m_drift_angle, 0.0f, Mth::ClampMax(animation_completion_factor / m_drift_goal_rotate_factor, 1.0f));
	m_facing = m_drift_goal_facing;
	m_facing.RotateY(angle);
	
	m_display_offset = Mth::Lerp(m_drift_initial_display_offset, m_drift_goal_display_offset, animation_completion_factor);
	
	m_frame_event = CRCD(0x4fe6069c, "AnimWait");
}
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::anim_wait_complete (   )
{
	GetObject()->SetPos(m_anim_wait_goal_pos + m_offset_due_to_movable_contact);
	
    Mth::Matrix matrix;
    matrix[Z] = m_drift_goal_facing;
    matrix[Y].Set(0.0f, 1.0f, 0.0f);
    matrix[X].Set(m_drift_goal_facing[Z], 0.0f, -m_drift_goal_facing[X]);
	matrix[W].Set();
	GetObject()->SetMatrix(matrix);
	
	m_display_offset = m_drift_goal_display_offset;
	
    if (mp_anim_wait_complete_callback)
    {
        (this->*mp_anim_wait_complete_callback)();
    }
}
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_stick_to_rail (   )
{
	// Look for rails.  If we find one, switch to skating and stash the rail data for the skating physics.
	
	if (mp_physics_control_component->IsBoardMissing()) return false;
	
	bool rail_found = false;
	
	if (rail_found = Mdl::Skate::Instance()->GetRailManager()->StickToRail(
		m_frame_start_pos,
		m_pos,
		&m_special_transition_data.rail_data.rail_pos,
		&m_special_transition_data.rail_data.p_node,
		NULL,
		1.0f
	))
	{
		m_special_transition_data.rail_data.p_rail_man = Mdl::Skate::Instance()->GetRailManager();
		m_special_transition_data.rail_data.p_movable_contact = NULL;
	}
	else
	{
		// clean vectors
		m_frame_start_pos[W] = 1.0f;
		m_pos[W] = 1.0f;
		
		for (CRailManagerComponent* p_rail_manager_component = static_cast< CRailManagerComponent* >(CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_RAILMANAGER));
			p_rail_manager_component && !rail_found;
			p_rail_manager_component = static_cast< CRailManagerComponent* >(p_rail_manager_component->GetNextSameType()))
		{
			Mth::Matrix obj_matrix = p_rail_manager_component->UpdateRailManager();
			Mth::Matrix obj_matrix_inv = obj_matrix;
			obj_matrix_inv.Invert();

			// transform into the frame of the moving object
			Mth::Vector obj_frame_frame_start_pos = obj_matrix_inv.Transform(m_frame_start_pos);
			Mth::Vector obj_frame_pos = obj_matrix_inv.Transform(m_pos);

			if (rail_found = p_rail_manager_component->GetRailManager()->StickToRail(
				obj_frame_frame_start_pos,
				obj_frame_pos,
				&m_special_transition_data.rail_data.rail_pos,
				&m_special_transition_data.rail_data.p_node,
				NULL,
				1.0f
			))
			{
				m_special_transition_data.rail_data.p_rail_man = p_rail_manager_component->GetRailManager();
				m_special_transition_data.rail_data.rail_pos = obj_matrix.Transform(m_special_transition_data.rail_data.rail_pos);
				m_special_transition_data.rail_data.p_movable_contact = p_rail_manager_component->GetObject();
			}
		}
	}
		
	if (!rail_found || !mp_core_physics_component->will_take_rail(
		m_special_transition_data.rail_data.p_node,
		m_special_transition_data.rail_data.p_rail_man,
		true
	))
	{
		return false;
	}
	
	// no single node rails while walking
	if (!m_special_transition_data.rail_data.p_node->GetPrevLink() && !m_special_transition_data.rail_data.p_node->GetNextLink()) return false;
	
	// emulate feeler test in CSkaterCorePhysicsComponent::got_rail so that we don't transition to skating and then reject rail
	CFeeler feeler(m_pos, m_special_transition_data.rail_data.rail_pos);
	if (feeler.GetCollision() && (m_special_transition_data.rail_data.rail_pos - feeler.GetPoint()).LengthSqr() > 6.0f * 6.0f) return false;
	
	if (Script::GetInt(CRCD(0x19fb78fa, "output_tracking_lines")))
	{
		printf ("Tracking%d %.2f,%.2f,%.2f,%.2f,%.2f,%.2f\n", 2, m_frame_start_pos[X], m_frame_start_pos[Y], m_frame_start_pos[Z], m_pos[X], m_pos[Y], m_pos[Z]);
	}
	
	// we have stored the rail data so it can be passed on to the skating physics
	
	m_special_transition_data.type = RAIL_TRANSITION;

	m_frame_event = CRCD(0xa6a3147e, "Rail");
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::setup_collision_cache (   )
{
	float horizontal_reach = 1.0f + s_get_param(CRCD(0x99978d2b, "feeler_length"));
	float vertical_height = 1.0f + s_get_param(CRCD(0x9ea1974a, "walker_height"));;
	float vertical_depth = 1.0f + s_get_param(CRCD(0xaf3e4251, "snap_down_height"));
	
	Mth::CBBox bbox(
		GetObject()->GetPos() - Mth::Vector(horizontal_reach, vertical_depth, horizontal_reach, 0.0f),
		GetObject()->GetPos() + Mth::Vector(horizontal_reach, vertical_height, horizontal_reach, 0.0f)
	);
	
	mp_collision_cache->Update(bbox);
	CFeeler::sSetDefaultCache(mp_collision_cache);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::update_critical_point_offset (   )
{
	float critical_point_offset_length_sqr = m_critical_point_offset.LengthSqr();
	if (critical_point_offset_length_sqr != 0.0f)
	{
		switch (m_state)
		{
			case WALKING_HANG:
				break;
				
			case WALKING_ANIMWAIT:
				break;
				
			case WALKING_LADDER:
			case WALKING_GROUND:
			case WALKING_AIR:
			{
				Mth::Vector initial_offset = m_critical_point_offset;
				
				// float adjustment_length = 100.0f * m_frame_length;
				float adjustment_length = 1000.0f * m_frame_length;
				float offset_length = sqrtf(critical_point_offset_length_sqr);
				if (adjustment_length < offset_length)
				{
					m_critical_point_offset -= (adjustment_length / offset_length) * m_critical_point_offset;
				}
				else
				{
					m_critical_point_offset.Set();
				}
				
				if (m_state == WALKING_LADDER) break;
				
				CFeeler feeler;
				feeler.m_start = m_frame_start_pos + initial_offset;
				feeler.m_end = m_pos + m_critical_point_offset;
				if (feeler.GetCollision())
				{
					Mth::Vector new_critical_point = feeler.GetPoint() + 0.1f * feeler.GetNormal();
					Mth::Vector required_pos_adjustment = new_critical_point - feeler.m_end;
					m_pos += required_pos_adjustment;
					MESSAGE("adjusting position due to bad critical point");
					DUMPV(required_pos_adjustment);
					DUMPV(m_critical_point_offset);
					DUMP_WPOSITION
				}
				
				if (m_false_wall.active)
				{
					float offset = Mth::DotProduct(m_pos + m_critical_point_offset, m_false_wall.normal) - m_false_wall.distance;
					if (offset > 0.0f)
					{
						m_pos -= offset * m_false_wall.normal;
						DUMP_WPOSITION
					}
				}
				
				break;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::update_display_offset (   )
{
	switch (m_state)
	{
		case WALKING_ANIMWAIT:
		case WALKING_HANG:
			break;
			
		default:
			m_display_offset = Mth::Lerp(m_display_offset, 0.0f, Tmr::FrameLength() * s_get_param(CRCD(0xb32c972b, "display_offset_restore_rate")));
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::adjust_pos_for_false_wall (   )
{
	if (!m_false_wall.active) return;
	
	if (m_pos[Y] > m_false_wall.cancel_height)
	{
		m_false_wall.active = false;
	}
	else
	{
		float offset = Mth::DotProduct(m_false_wall.normal, m_pos) - m_false_wall.distance;
		if (offset > -s_get_param(CRCD(0xa20c43b7, "push_feeler_length")))
		{
			m_pos -= s_get_param(CRCD(0x4d16f37d, "push_strength")) * m_frame_length * m_false_wall.normal;
			DUMP_WPOSITION
			if (Mth::DotProduct(m_horizontal_vel, m_false_wall.normal))
			{
				m_horizontal_vel -= Mth::DotProduct(m_horizontal_vel, m_false_wall.normal) * m_false_wall.normal;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::extract_state_from_object (   )
{
	m_pos = GetObject()->m_pos;
	DUMP_WPOSITION
	
	m_horizontal_vel = GetObject()->GetVel();
	m_horizontal_vel[Y] = 0.0f;
	m_vertical_vel = GetObject()->GetVel()[Y];
	
	// note that m_facing and m_upward will often not be orthogonal, but will always span a plan
	
	// generally straight up, but now after a transition from skating
	m_upward = GetObject()->GetMatrix()[Y];
	
	m_facing = GetObject()->GetMatrix()[Z];
	m_frame_initial_facing_Y = m_facing[Y];
	m_facing[Y] = 0.0f;
	float length = m_facing.Length();
	if (length < 0.001f)
	{
		// upward facing orientation matrix
		m_facing = -GetObject()->GetMatrix()[Y];
		m_facing[Y] = 0.0f;
		m_facing.Normalize();
		
		// since m_upward is now in the same plan as m_facing, push m_upward up a touch
		m_upward[Y] += 0.01f;
		m_upward.Normalize();
	}
	else
	{
		m_facing /= length;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::copy_state_into_object (   )
{
	// build the object's matrix based on our facing
	Mth::Matrix matrix;
	
	m_facing *= sqrtf(1.0f - Mth::Sqr(m_frame_initial_facing_Y));
	m_facing[Y] = m_frame_initial_facing_Y;
	
	// basically, rotate Z upward to perpendicular with m_upward
	matrix[X] = Mth::CrossProduct(m_upward, m_facing);
	matrix[X].Normalize();
	matrix[Y] = m_upward;
	matrix[Z] = Mth::CrossProduct(matrix[X], matrix[Y]);
	matrix[W].Set();
	
	DUMP_WPOSITION
	GetObject()->SetPos(m_pos);
	GetObject()->SetMatrix(matrix);
	GetObject()->SetDisplayMatrix(matrix);
	
	// construct the object's velocity
	switch (m_state)
	{
		case WALKING_GROUND:
		case WALKING_AIR:
			GetObject()->SetVel(m_horizontal_vel);
			GetObject()->GetVel()[Y] = m_vertical_vel;
			break;
			
		case WALKING_HANG:
			GetObject()->GetVel().Set(-m_facing[Z], 0.0f, m_facing[X]);
			GetObject()->GetVel() *= m_hang_move_vel;
			break;
		
		case WALKING_LADDER:
			GetObject()->GetVel().Set(0.0f, m_anim_effective_speed, 0.0f);
			break;
			
		default:
			GetObject()->GetVel().Set();
			break;
	}
	
	mp_model_component->SetDisplayOffset(m_display_offset * matrix[Y]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWalkComponent::get_controller_input (   )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	Dbg_Assert(mp_camera);
	
	// rotate controller direction into camera's frame
	
	Mth::Vector camera_forward = -mp_camera->m_matrix[Z];
	camera_forward[Y] = 0.0f;
	camera_forward.Normalize();
	
	// allow a tolerance range for pressing directly forward
	float angle = control_pad.m_leftAngle;
	if (Mth::Abs(angle) < Mth::DegToRad(s_get_param(CRCD(0x4676a268, "forward_tolerance"))))
	{
		angle = 0.0f;
	}
	
	float sin_angle = sinf(angle);
	float cos_angle = cosf(angle);
	m_control_direction[X] = cos_angle * camera_forward[X] - sin_angle * camera_forward[Z];
	m_control_direction[Z] = sin_angle * camera_forward[X] + cos_angle * camera_forward[Z];
	
	// different control schemes for analog stick and d-pad
	if (control_pad.m_leftX == 0.0f && control_pad.m_leftY == 0.0f)
	{
		// d-pad control
		m_analog_control = false;
	
		if (control_pad.m_leftLength == 0.0f)
		{
			m_control_magnitude = 0.0f;
			m_control_pegged = false;
		}
		else
		{
			if (m_state == WALKING_GROUND && !control_pad.m_x.GetPressed())
			{
				m_control_magnitude = s_get_param(CRCD(0xc1528f7f, "walk_point"));
			}
			else
			{
				m_control_magnitude = 1.0f;
			}
			
            m_control_pegged = true;
		}
	}
	else
	{
		// analog stick control
		m_analog_control = true;
		
		m_control_magnitude = control_pad.GetScaledLeftAnalogStickMagnitude() / 0.85f;
		if (m_control_magnitude >= 1.0f)
		{
			m_control_magnitude = 1.0f;
			m_control_pegged = true;
		}
		else
		{
			m_control_pegged = false;
		}
	}
	
	// during a forward control lock, ignore all input not in the forward direction
	if (m_state == WALKING_GROUND && m_forward_control_lock)
	{
		m_control_magnitude = Mth::ClampMin(m_control_magnitude * Mth::DotProduct(m_control_direction, m_facing), 0.0f);
		m_control_direction = m_facing;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CWalkComponent::calculate_desired_speed (   )
{
	float desired_speed;
	
	if (m_control_magnitude == 0.0f)
	{
		desired_speed = 0.0f;
	}
	else
	{
		float walk_point = s_get_param(CRCD(0xc1528f7f, "walk_point"));
		if (m_control_magnitude <= walk_point)
		{
			m_run_toggle = false;
			desired_speed = Mth::LinearMap(0.0f, s_get_param(CRCD(0x79d182ad, "walk_speed")), m_control_magnitude, 0.3f, walk_point);
		}
		else
		{
			m_run_toggle = true;
			desired_speed = Mth::LinearMap(s_get_param(CRCD(0x79d182ad, "walk_speed")), get_run_speed(), m_control_magnitude, walk_point, 1.0f);
		}
	}
	
	return desired_speed;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CWalkComponent::calculate_feeler_offset_direction ( int contact )
{
	float angle = contact * (2.0f * Mth::PI / vNUM_FEELERS);
	float cos_angle = cosf(angle);
	float sin_angle = sinf(angle);

	Mth::Vector end_offset_direction;
	end_offset_direction[X] = cos_angle * m_facing[X] - sin_angle * m_facing[Z];
	end_offset_direction[Y] = 0.0f;
	end_offset_direction[Z] = sin_angle * m_facing[X] + cos_angle * m_facing[Z];
	end_offset_direction[W] = 1.0f;
	
	return end_offset_direction;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::determine_stand_pos ( const Mth::Vector& proposed_stand_pos, Mth::Vector& stand_pos, CFeeler& feeler )
{
	// upward offset of standing position at maximum standable slope
	float max_height_adjustment = 2.0f * s_get_param(CRCD(0x6da7f696, "feeler_height"));
	
	feeler.m_start = proposed_stand_pos;
	feeler.m_start[Y] += max_height_adjustment;
	feeler.m_end = proposed_stand_pos;
	feeler.m_end[Y] -= s_get_param(CRCD(0xaebfa9cd, "stand_pos_search_depth"));
	if (feeler.GetCollision())
	{
		stand_pos = feeler.GetPoint();
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(100, 100, 0, 0);
		}
		#endif
		return true;
	}
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(255, 255, 0, 0);
	}
	#endif
	
	stand_pos = proposed_stand_pos;
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::update_run_speed_factor (   )
{
	if (GetSkaterScoreComponentFromObject(GetObject())->GetScore()->GetScorePotValue() > 0)
	{
		m_run_speed_factor = 1.0f;
		return;
	}
	
	m_run_speed_factor = Mth::ClampMin(m_run_speed_factor - s_get_param(CRCD(0x3fb31dfc, "run_adjust_rate")) * Tmr::FrameLength(), 0.0f);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::smooth_anim_speed ( uint32 previous_frame_event )
{
	if (m_state == WALKING_GROUND)
	{
		if (Tmr::ElapsedTime(m_state_timestamp)	< 100)
		{
			// for the first few frames of the ground state, simply use this frame's anim speed
			m_last_frame_anim_effective_speed = m_anim_effective_speed;
		}
		else
		{
			// otherwise, use the smaller of the two anim speeds from this frame and the last
			float this_frame_anim_effective_speed = m_anim_effective_speed;
			m_anim_effective_speed = Mth::Min(m_anim_effective_speed, m_last_frame_anim_effective_speed);
			m_last_frame_anim_effective_speed = this_frame_anim_effective_speed;
			
			// adjust the frame event as appropriate
			if (m_frame_event == CRCD(0xaf895b3f, "Run") && m_anim_effective_speed == 0.0f)
			{
				switch (previous_frame_event)
				{
					case CRCC(0x1d537eff, "Skid"):
					case CRCC(0xf28adbfc, "RotateLeft"):
					case CRCC(0x912220f8, "RotateRight"):
						m_frame_event = previous_frame_event;
						break;
					default:
						m_frame_event = CRCD(0x9b46e749, "Stand");
						break;
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::set_camera_overrides (   )
{
	switch (m_frame_event)
	{
		// case CRCC(0xf41aba21, "Hop"):
			// mp_camera_component->SetOverrides(m_facing, 0.05f);
			// break;
		
		case CRCC(0x2d9815c3, "HangMoveLeft"):
		{
			Mth::Vector facing = m_facing;
			facing.RotateY(-0.95f);
			mp_camera_component->SetOverrides(facing, 0.05f);
			break;
		}
			
		case CRCC(0x279b1f0b, "HangMoveRight"):
		{
			Mth::Vector facing = m_facing;
			facing.RotateY(0.95f);
			mp_camera_component->SetOverrides(facing, 0.05f);
			break;
		}
					
		case CRCC(0x4194ecca, "Hang"):
			mp_camera_component->SetOverrides(m_facing, 0.05f);
			break;
		
		case CRCC(0xc84243da, "Ladder"):
		case CRCC(0xaf5abc82, "LadderMoveUp"):
		case CRCC(0xfec9dded, "LadderMoveDown"):
			mp_camera_component->SetOverrides(m_facing, 0.05f);
			break;
					
		case CRCC(0x4fe6069c, "AnimWait"):
			if (m_anim_wait_camera_mode == AWC_CURRENT)
			{
				mp_camera_component->SetOverrides(m_facing, 0.05f);
			}
			else
			{
				mp_camera_component->SetOverrides(m_drift_goal_facing, 0.05f);
			}
			break;
			
		default:
			mp_camera_component->UnsetOverrides();
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::update_anim_speeds (   )
{
	switch (m_anim_scale_speed)
	{
		case RUNNING:
			if (m_anim_standard_speed > 0.0f)
			{
				mp_animation_component->SetAnimSpeed(m_anim_effective_speed / m_anim_standard_speed, false, false);
				
				// if walking partial anims are active, update the partial anim speed too
				if (mp_physics_control_component->IsBoardMissing())
				{
					Script::CStruct* pParams = new Script::CStruct;
					pParams->AddFloat(CRCD(0xf0d90109, "Speed"), m_anim_effective_speed / m_anim_standard_speed);
					mp_animation_component->CallMemberFunction(CRCD(0xbd4edd44, "SetPartialAnimSpeed"), pParams, NULL);
					delete pParams;
				}
			}
			break;
			
		case HANGMOVE:
		{
			float new_anim_speed = m_anim_effective_speed / s_get_param(CRCD(0x1a47ffab, "hang_move_animation_speed"));
			
			Script::CStruct* pParams = new Script::CStruct;
			pParams->AddFloat(CRCD(0xf0d90109, "Speed"), new_anim_speed);
			mp_animation_component->CallMemberFunction(CRCD(0xbd4edd44, "SetPartialAnimSpeed"), pParams, NULL);
			delete pParams;
			
			mp_animation_component->SetAnimSpeed(new_anim_speed, false, false);
			break;
		}
					
		case LADDERMOVE:
		{
			float new_anim_speed = m_anim_effective_speed / s_get_param(CRCD(0xab2db54, "ladder_move_speed"));
			
			Script::CStruct* pParams = new Script::CStruct;
			pParams->AddFloat(CRCD(0xf0d90109, "Speed"), new_anim_speed);
			mp_animation_component->CallMemberFunction(CRCD(0xbd4edd44, "SetPartialAnimSpeed"), pParams, NULL);
			delete pParams;
			
			// Only ladder animation speed changes are sent to the server in net games.
			if (!GameNet::Manager::Instance()->InNetGame() || Mth::Abs(new_anim_speed - m_last_ladder_anim_speed) < 0.1f)
			{
				mp_animation_component->SetAnimSpeed(new_anim_speed, false, false);
			}
			else
			{
				m_last_ladder_anim_speed = new_anim_speed;
				mp_animation_component->SetAnimSpeed(m_last_ladder_anim_speed, true, false);
			}
			break;
		}
	
		default:
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CWalkComponent::get_gravity (   )
{
	if (!Mdl::Skate::Instance()->GetCareer()->GetCheat(CRCD(0x9c8c6df1, "CHEAT_MOON")))
	{
		return s_get_param(CRCD(0xa5e2da58, "gravity"));
	}
	else
	{
		Mdl::Skate::Instance()->GetCareer()->SetGlobalFlag(Script::GetInteger(CRCD(0x9c8c6df1, "CHEAT_MOON")));
		g_CheatsEnabled = true;
		return 0.5f * s_get_param(CRCD(0xa5e2da58, "gravity"));
	}
}

}

#endif // TESTING_GUNSLINGER


================================================
FILE: Code/Gel/Components/WalkComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       WalkComponent.h
//* OWNER:          Dan
//* CREATION DATE:  4/2/3
//****************************************************************************

#ifndef __COMPONENTS_WALKCOMPONENT_H__
#define __COMPONENTS_WALKCOMPONENT_H__

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 

#ifdef __NOPT_ASSERT__
#define WHEADER { printf("%i:%s:%i: ", Tmr::GetRenderFrame(), __FILE__ + 15, __LINE__); }
#define DUMP_WPOSITION { if (Script::GetInteger(CRCD(0x029ade47, "debug_skater_pos"))) { WHEADER; printf("m_pos = %g, %g, %g\n", m_pos[X], m_pos[Y], m_pos[Z]); } }
#else
#define DUMP_WPOSITION {   }
#endif

#define		CRC_WALK CRCD(0x726e85aa, "Walk")

#define		GetWalkComponent() ((Obj::CWalkComponent*)GetComponent(CRC_WALK))
#define		GetWalkComponentFromObject(pObj) ((Obj::CWalkComponent*)(pObj)->GetComponent(CRC_WALK))

class CFeeler;
		
namespace Script
{
    class CScript;
    class CStruct;
}

namespace Nx
{
	class CCollCache;
}
              
namespace Obj
{
	class CAnimationComponent;
	class CModelComponent;
	class CTriggerComponent;
	class CWalkCameraComponent;
	class CSkaterPhysicsControlComponent;
	class CSkaterStateComponent;
	class CMovableContactComponent;
    class CRailNode;
    class CRailManager;

	struct SHangRailData
	{
		Mth::Vector					hang_point;
		Mth::Vector					hang_facing;
		float						along_rail_factor;
		float						horizontal_hang_offset;
		float						vertical_hang_offset;
		uint32						initial_animation;
		const CRailNode*			p_rail_node;
	};
	
	struct SLadderRailData
	{
		Mth::Vector					climb_point;
		Mth::Vector					climb_facing;
		float						along_rail_factor;
	};
	
class CWalkComponent : public CBaseComponent
{
	friend class CSkaterCorePhysicsComponent;
	
public:
	enum EStateType
	{
		WALKING_GROUND = 0,
		WALKING_AIR,
		// WALKING_HOP,
		WALKING_HANG,
		WALKING_LADDER,
		WALKING_ANIMWAIT,
	};
	
	enum { NUM_WALKING_STATES = 5 };
	
	enum EAnimScaleSpeed
	{
		OFF,
		RUNNING,
        HANGMOVE,
		LADDERMOVE
	};
	
	enum
	{
		vNUM_FEELERS = 7,
		vNUM_BARRIER_HEIGHT_FEELERS = 9
	};
	
	typedef void (Obj::CWalkComponent::*AnimWaitCallbackPtr) (   );
	
private:
	static const uint32 sp_state_names [ NUM_WALKING_STATES ];
	
public:
    CWalkComponent();
    virtual ~CWalkComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
	virtual void					Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	EStateType						GetState (   ) { return m_state; }
	
private:
	void							set_state ( EStateType state );
	
	void							go_on_ground_state (   );
	void							calculate_horizontal_speed_and_facing ( float &horizontal_speed );
	bool							adjust_horizonal_vel_for_environment (   );
	void							adjust_facing_for_adjusted_horizontal_vel (   );
	float							adjust_desired_speed_for_slope ( float desired_speed );
	void							respond_to_ground (   );
	void							adjust_curb_float_height (   );
	void							account_for_movable_contact (   );
	void							jump (   );
	void							adjust_jump_for_ceiling_obstructions (   );
	void							go_in_air_state (   );
	void							control_horizontal_vel (   );
	void							adjust_vertical_vel_for_ceiling (   );
	void							check_for_landing ( const Mth::Vector& previous_pos, const Mth::Vector& final_pos );
	void							maybe_wallplant (   );
	void							uber_frig (   );
	void							lerp_upright (   );
	void							transition_lerp_upright (   );
	bool							check_for_wall_push (   );
	void							leave_movable_contact_for_air ( Mth::Vector& horizontal_vel, float& vertical_vel );
	bool							maybe_hop_to_hang (   );
	bool							maybe_drop_to_hang (   );
	bool							maybe_grab_to_hang ( Mth::Vector start_pos, Mth::Vector end_pos );
	bool							investigate_hang_rail_vicinity ( CFeeler& feeler, const Mth::Vector& preliminary_hang_point, SHangRailData& rail_data );
	bool							vertical_hang_obstruction_check ( CFeeler& feeler, SHangRailData& rail_data, const Mth::Vector& check_offset_direction );
	void							go_hop_state (   );
	void							go_hang_state (   );
	void							hang_movement ( float movement );
	bool							check_environment_for_hang_movement_collisions ( const Mth::Vector& facing, float along_rail_factor, const Mth::Vector& pos, bool left, const Mth::Vector& perp_facing, Mth::Vector rail, const CRailNode* p_rail_start, const CRailNode* p_rail_end );
	void							move_onto_neighboring_hang_rail ( float movement, bool descending, const Mth::Vector& old_rail );
	bool							find_next_rail ( float& along_rail_factor, const CRailNode* p_current_start, const CRailNode* p_current_end, const CRailNode*& p_new_start, const CRailNode*& p_new_end );
	void							set_pos_from_hang_rail_state (   );
	bool							maybe_pull_up_from_hang ( Mth::Vector& proposed_stand_pos );
	bool							maybe_jump_low_barrier (   );
	bool							maybe_in_air_acid_drop (   );
	bool							maybe_jump_to_acid_drop (   );
	bool							maybe_climb_up_ladder ( bool user_requested = false );
	bool							maybe_climb_down_ladder (   );
	bool							maybe_grab_to_ladder ( const Mth::Vector& start_pos, const Mth::Vector& end_pos );
	void							go_ladder_state (   );
	void							ladder_movement (   );
	void							off_ladder_bottom (   );
	void							off_ladder_top (   );
	void							calculate_anim_wait_facing_drift_parameters ( const Mth::Vector& goal_facing, float rotate_factor = 1.0f );
    void                            go_anim_wait_state (   );
    void                            anim_wait_complete (   );
	bool							maybe_stick_to_rail (   );
	void							setup_collision_cache (   );
	void							update_critical_point_offset (   );
	void							update_display_offset (   );
	void							adjust_pos_for_false_wall (   );
	void							extract_state_from_object (   );
	void							copy_state_into_object (   );
	void							get_controller_input (   );
	float							calculate_desired_speed (   );
	Mth::Vector						calculate_feeler_offset_direction ( int contact );
	bool							determine_stand_pos ( const Mth::Vector& proposed_stand_pos, Mth::Vector& stand_pos, CFeeler& feeler );
	float							get_run_speed (   );
	float							get_gravity (   );
	void							update_run_speed (   );
	void							update_run_speed_factor (   );
	void							smooth_anim_speed ( uint32 previous_frame_event );
	void							set_camera_overrides (   );
	void							update_anim_speeds (   );
	float							get_hang_display_offset (   );
	
	void							drop_to_hang_complete (   );
	void							pull_up_from_hang_to_ground_complete (   );
	void							pull_up_from_hang_to_air_complete (   );
	float							get_hang_critical_point_vert_offset (   );
	void							move_to_ladder_bottom_complete (   );
	void							move_to_ladder_top_complete (   );
	void							off_ladder_bottom_complete (   );
	void							off_ladder_top_to_ground_complete (   );
	void							off_ladder_top_to_air_complete (   );
	
	static float					s_get_param ( uint32 checksum );
	
public:
	bool							IsRunning (   );
	bool							UseDPadCamera (   );
	void							SetForwardControlLock ( bool forward_control_lock );
	void							SetAssociatedCamera ( CCompositeObject* camera_obj );
	
	void							CollideWithOtherSkaterLost ( CCompositeObject* p_other_skater );
    
	Mth::Vector						GetCameraCollisionTargetOffset (   );
	
	void							ReadyWalkState ( bool to_ground_state );
	void							CleanUpWalkState (   );
	const SRailData*				GetRailData (   );
	const SAcidDropData*			GetAcidDropData (   );
    
	bool							FilterHangRail ( const Mth::Vector& pos_a, const Mth::Vector& pos_b, const Mth::Vector& preliminary_hang_point, const Mth::Vector& grab_from_point, float along_rail_factor, SHangRailData& rail_data, bool drop_to_hang );
	bool							FilterLadderUpRail ( const Mth::Vector& bottom_pos, const Mth::Vector& top_pos, const Mth::Matrix& orientation, SLadderRailData& rail_data );
	bool							FilterLadderDownRail ( const Mth::Vector& bottom_pos, const Mth::Vector& top_pos, const Mth::Matrix& orientation, SLadderRailData& rail_data );
	bool							FilterLadderAirGrabRail ( const Mth::Vector& bottom_pos, const Mth::Vector& top_pos, const Mth::Matrix& orientation, const Mth::Vector& preliminary_climb_point, float along_rail_factor, SLadderRailData& rail_data );
	
private:
	EStateType						m_state;
	
	// Time stamp of when our state last changed.
	Tmr::Time						m_state_timestamp;
	
	// State we changed from when m_state_timestamp was last set.
	EStateType						m_previous_state;
	
	// Extracted from the object at frame start and back into the object at frame end.
	Mth::Vector						m_pos;
	Mth::Vector						m_horizontal_vel;
	float							m_vertical_vel;
	Mth::Vector						m_facing;
	Mth::Vector						m_upward;
	
	// Cache our position at the frame's start
	Mth::Vector						m_frame_start_pos;
	
	// Script event reporting our current state.  Set during each frame update and sent at the end of the update.
	uint32							m_frame_event;
	
	// Last frame's frame event.
	uint32							m_last_frame_event;
	
	// Current frame's frame length.
	float							m_frame_length;
	
	// Current frame's control input.
	Mth::Vector						m_control_direction;
	float							m_control_magnitude;
	bool							m_control_pegged;
	bool							m_analog_control;
	
	// The grab button is the same as the transition button, and is thus ignored after a transition until it is released
	bool							m_ignore_grab_button;
	
	// After transitioning from skating to walking in air, you are not allowed to acid drop until you first switch out of the air state.
	bool							m_disallow_acid_drops;
	
	// The offset from the skater's true origin at which to display the skater.  Used to get CAS scaled skaters looking correct.
	float							m_display_offset;
	
	struct SContact
	{
		// If this contact is in collision with the environment.
		bool						in_collision;
		
		// The horizontal normal of the collided geometry.
		Mth::Vector					normal;
		
		// If the collision in this direction is determined to be low enough to jump.
		bool						jumpable;
		
		// Movement of the object we're in contact with (not accounting for its rotation) along its horizontal normal.
		float						movement;
		
		// Must store the full feeler in order to trip triggers and get movable contact objects
		CFeeler						feeler;
		
	// The walker looks around him in vNUM_FEELERS directions and stores the results here.  The final contact is felt for along the movement of
	// the origin, and is only used while in the air.
	} mp_contacts [ vNUM_FEELERS + 1 ];
	
	// Type of animation speed scaling to do.
	EAnimScaleSpeed					m_anim_scale_speed;
	
	// Horizontal speed for the purposes of animation speed scaling.
	float							m_anim_effective_speed;
	float							m_last_frame_anim_effective_speed;
	
	#ifdef SCRIPT_WALK_DRAG
	// Factor multiplied by all velocities.  Set by script.
	float							m_script_drag_factor;
	#endif
	
	// Used to lock control in the forward direction (during a camera flush request)
	bool							m_forward_control_lock;
	
	// When on ground, the walker is often floated above the ground in an attempt to smooth out stair/curb climbs.
	float							m_curb_float_height;
	
	// If m_curb_float_height was adjusted this frame.  If not, we lerp it to zero.
	bool							m_curb_float_height_adjusted;
	
	// The critical point is a position relative to the skater which MUST ALWAYS STAY ON THE CORRECT SIDE OF COLLIDABLE GEO.  Normally, this is simply the
	// origin.  However, sometimes this must be adjusted (during hanging).  Once hanging is over, the critical point is pushed back to the origin.
	Mth::Vector						m_critical_point_offset;
	
	// Counts down while we're slerping upright after a transition from skating
	float m_rotate_upright_timer;
	
	// Angle over and axis around which to slerp to upright
	float m_rotate_upright_angle;
	Mth::Vector m_rotate_upright_axis;
	
	// The matrix's [Z][Y] component last time facing was extracted from the matrix.  Stored so it can be restored when rebuilding the matrix, thus
	// preventing unwanted rotations.
	float m_frame_initial_facing_Y;
	
	struct SSpecialTransitionData
	{
		// The type of transition causing the switch to skating.
		ESpecialTransitionType		type;
		
		// The state data for that transition.
		SRailData					rail_data;
		SAcidDropData				acid_drop_data;
		
	// When the walker transitions to skating in some manner which requires additional state information, this structure holds that information before it
	// is passed off to the skate physics.
	}								m_special_transition_data;
	
	// The collision cache used.
	Nx::CCollCache*					mp_collision_cache;
	
	// Associated camera.
	CCompositeObject*				mp_camera;
	CWalkCameraComponent*			mp_camera_component;
	
    // If associated with a rail, this is the rail node and rail manager.
    const CRailNode*              	mp_rail_start;
    const CRailNode*              	mp_rail_end;
    CRailManager*                   mp_rail_manager;
	
	// Peer components.
	CInputComponent*				mp_input_component;
	CAnimationComponent*			mp_animation_component;
	CModelComponent*				mp_model_component;
	CTriggerComponent*				mp_trigger_component;
	CSkaterPhysicsControlComponent*	mp_physics_control_component;
	CMovableContactComponent*		mp_movable_contact_component;
	CSkaterStateComponent*			mp_state_component;
	CSkaterCorePhysicsComponent*	mp_core_physics_component;
    
	/////////////////////////////////
	// WALKING_GROUND state variables
	
	// Normal of the ground on which we are standing.  Only meaningful when on the ground.  Set the previous frame.
	Mth::Vector						m_ground_normal;
	
	// Certain things such as rotation rate and camera lerp are increased when this is set.
	bool							m_run_toggle;
	
	struct SWallPushTest
	{
		// Whether we currently testing.
		bool						active;
		
		// Time at which this test began.
		Tmr::Time					test_start_time;
		
	// Data used to detect when a player is pushing into a wall in order to climb it.
	}								m_wall_push_test;
	
	// When doing anim speed scaling when running, this is set to the run speed which corresponds to the current animation playing at standard speed.
	float							m_anim_standard_speed;
	
	// A copy of the latest feeler which contacted the groud under us.
	CFeeler							m_last_ground_feeler;
	bool							m_last_ground_feeler_valid;
	
	// lerp factor between standard run speed and combo run speed
	float							m_run_speed_factor;
	
	// If true, no transitional period of reduced deceleration is used from skating.
	// bool							m_cancel_transition_momentum;
	
	// Set to true when the walker's speeds is significantly reduced due to interactions with walls.  Causes the rotation rate of the skater to be dropped
	// to walking levels next frame.  A beter solution would be to reduce rotation rate THIS frame.
	bool							m_drop_rotation_rate;
	
	// Latest duration over which the controller has been continuously pegged.  Only used on the ground and reset to a large value whenever you enter ground.
	float							m_control_pegged_duration;
	
	// True if the controller was pegged last frame.  Only updated while on the ground.
	bool							m_last_frame_controller_pegged;
	
	/////////////////////////////////
	// WALKING_AIR state variables
	
	// The forward horizontal direction of the jump which, when coming out of skating or because of in-air velocity control, diverges from the direction of velocity
	Mth::Vector						m_primary_air_direction;
	
	// This term of our horizontal velocity is due to leaping from a movable contact, and is not adjustable via in-air control.
	Mth::Vector						m_uncontrollable_air_horizontal_vel;
	
	// When non-zero, it counts down and suppresses in air velocity control.
	float							m_in_air_control_suppression_timer;
	
	// Extra drift velocity used while in the air.
	Mth::Vector						m_in_air_drift_vel;
	
	// Extra drift is turned off after the skater reaches this height.
	float							m_in_air_drift_stop_height;
	
	struct SFalseWall
	{
		// This tracks if a false wall is active.
		bool						active;
		
		// The false wall is defined by a normal.
		Mth::Vector					normal;
		
		// And a scalar.
		float						distance;
		
		// The false wall is automatically turned off when this height is exceeded.
		float						cancel_height;
		
	// A false wall is sometimes set up while in the air to prevent movement in a certain direction.
	}								m_false_wall;
	
	/////////////////////////////////
	// WALKING_HANG state variables
	
	// Measures our position along the current rail as a parameter running from 0.0 to 1.0.
	float							m_along_rail_factor;
	
	// Measures the hang position offset from the actual rail we are hanging from.
	float							m_vertical_hang_offset;
	float							m_horizontal_hang_offset;
	
	// Checksum describing the type of initial hang animation to play.
	uint32							m_initial_hang_animation;
	
	// Speed the skater is shimmying at
	float							m_hang_move_vel;
    
	/////////////////////////////////
	// WALKING_HOP state variables
	
    // The position we will have at the end of the hop.
    // Mth::Vector                     m_goal_hop_pos;
    
    // The facing we will have at the end of the hop.
    // Mth::Vector                     m_goal_hop_facing;
	
	// Hang state variable.  Set at hop start.
	// float						m_along_rail_factor;
	
	// Hang state variable.  Set at hop start.
	// uint32						m_initial_hang_animation;
    
	/////////////////////////////////
	// WALKING_LADDER state variables
	
	// Shared with hang state.
	// float						m_along_rail_factor;
	
	// Only ladder animation speed changes are sent to the server are updated in net games.  This holds the last animimation speed sent.
	float							m_last_ladder_anim_speed;
    
	/////////////////////////////////
	// WALKING_ANIMWAIT state variables
	
	// Position at the start of the animation wait.
	Mth::Vector						m_anim_wait_initial_pos;
	
	// Facing at the start of the aniamtion wait.
	Mth::Vector						m_anim_wait_initial_facing;
	
    // Amount to move the object at the end of the animation wait.
    Mth::Vector                     m_anim_wait_goal_pos;
	
	// Goal display offset of the animation wait.
	float							m_drift_initial_display_offset;
	float							m_drift_goal_display_offset;
	
	// Goal facing of the position drift.
	Mth::Vector						m_drift_goal_facing;
	
	// Factor of animation in which to do the rotation in.
	float							m_drift_goal_rotate_factor;
	
	// The angle over which the facing must drift.
	float							m_drift_angle;
    
	// Accumulates position offset due to a movable contact during an animation wait.
	Mth::Vector						m_offset_due_to_movable_contact;
	
	// The behavior of the camera during the animation wait.
	enum EAnimWaitCameraModeType			
	{
		AWC_CURRENT,
		AWC_GOAL
	}								m_anim_wait_camera_mode;
	
	// Callback function to call upon completion of the animation wait.
	AnimWaitCallbackPtr				mp_anim_wait_complete_callback;
	
	// WALKING_GROUND state variable sometimes set at start of animation wait based on the animation end ground situation
	// Mth::Vector					m_ground_normal;
	// CFeeler						m_last_ground_feeler;
	// bool							m_last_ground_feeler_valid;
	
	// WALKING_AIR state variable partially setup at the start of an animation wait
	// struct SFalseWall			m_false_wall;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float CWalkComponent::s_get_param ( uint32 checksum )
{
	Script::CStruct* p_walk_params = Script::GetStructure(CRCD(0x6775e538, "walk_parameters"));
	Dbg_Assert(p_walk_params);
	
	float param;
	p_walk_params->GetFloat(checksum, ¶m, Script::ASSERT);
	return param;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float CWalkComponent::get_run_speed (   )
{
	return Mth::Lerp(s_get_param(CRCD(0xcc461b87, "run_speed")), s_get_param(CRCD(0x7213ef07, "combo_run_speed")), m_run_speed_factor);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CWalkComponent::IsRunning (   )
{
	return m_run_toggle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CWalkComponent::SetForwardControlLock ( bool forward_control_lock )
{
	m_forward_control_lock = forward_control_lock;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const SRailData* CWalkComponent::GetRailData (   )
{
	return m_special_transition_data.type == RAIL_TRANSITION ? &m_special_transition_data.rail_data : NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const SAcidDropData* CWalkComponent::GetAcidDropData (   )
{
	return m_special_transition_data.type == ACID_DROP_TRANSITION ? &m_special_transition_data.acid_drop_data : NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CWalkComponent::UseDPadCamera (   )
{
	
	return mp_input_component->GetControlPad().m_leftX == 0.0f && mp_input_component->GetControlPad().m_leftY == 0.0f && m_control_pegged;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Mth::Vector CWalkComponent::GetCameraCollisionTargetOffset (   )
{
	switch (m_state)
	{
		case WALKING_HANG:
		case WALKING_LADDER:
		case WALKING_ANIMWAIT:
			return -18.0f * m_facing;
		
		case WALKING_AIR:
			if ((m_previous_state == WALKING_HANG || m_previous_state ==WALKING_LADDER || m_previous_state ==WALKING_LADDER)
				&& Tmr::ElapsedTime(m_state_timestamp) < 150)
			{
				return -18.0f * m_facing;
			}
			else
			{
				return Mth::Vector(0.0f, 0.0f, 0.0f);
			}
			
		case WALKING_GROUND:
		default:
			return Mth::Vector(0.0f, 0.0f, 0.0f);
	}
}

}

#endif



================================================
FILE: Code/Gel/Components/WalkHangUtil.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       WalkHangUtil.cpp
//* OWNER:          Dan
//* CREATION DATE:  4/2/3
//****************************************************************************

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 

/*
 * Contains those CWalkComponent member functions which are concerned with hanging.
 */

namespace Obj
{
     
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*
bool CWalkComponent::maybe_hop_to_hang (   )
{
	SHangRailData rail_data;
	
	Mth::Vector search_start_pos = m_pos;
	search_start_pos[Y] += s_get_param(CRCD(0x2c942693, "lowest_hang_height"));
	
	// clean vectors; facing is a directional vector
	search_start_pos[W] = 1.0f;
	m_facing[W] = 0.0f;
	
	bool rail_found = false;
	
	float hop_to_hang_reach = s_get_param(CRCD(0x8b785f26, "max_hop_to_hang_height")) - s_get_param(CRCD(0x2c942693, "lowest_hang_height"));
	float max_hop_to_hang_forward_reach = s_get_param(CRCD(0x3b4ca389, "max_hop_to_hang_forward_reach"));
	float max_hop_to_hang_backward_reach = s_get_param(CRCD(0xae806bee, "max_hop_to_hang_backward_reach"));
	
	// check level for appropriate rails
	if (rail_found = Mdl::Skate::Instance()->GetRailManager()->CheckForHopToHangRail(
		search_start_pos,
		hop_to_hang_reach,
		m_facing,
		max_hop_to_hang_forward_reach,
		max_hop_to_hang_backward_reach,
		this,
		rail_data,
		&mp_rail_node
	))
	{
		mp_rail_manager = Mdl::Skate::Instance()->GetRailManager();
		mp_movable_contact_component->LoseAnyContact();
	}
	else
	{
		float max_hop_to_movable_hang_vel_sqr = s_get_param(CRCD(0x511cb4, "max_hop_to_movable_hang_vel"));
		max_hop_to_movable_hang_vel_sqr *= max_hop_to_movable_hang_vel_sqr;
		
		// check moving objects for appropriate rails
		for (CRailManagerComponent* p_rail_manager_component = static_cast< CRailManagerComponent* >(CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_RAILMANAGER));
			p_rail_manager_component && !rail_found;
			p_rail_manager_component = static_cast< CRailManagerComponent* >(p_rail_manager_component->GetNextSameType()))
		{
			// only hop to moving hangs below a threshold velocity
			if (p_rail_manager_component->GetObject()->GetVel().LengthSqr() > max_hop_to_movable_hang_vel_sqr) continue;
			
			Mth::Matrix obj_matrix_inv = p_rail_manager_component->UpdateRailManager();
			obj_matrix_inv.Invert();

			// transform into the frame of the moving object
			Mth::Vector obj_frame_search_start_pos = obj_matrix_inv.Transform(search_start_pos);
			Mth::Vector obj_frame_facing = obj_matrix_inv.Transform(m_facing);

			if (rail_found = p_rail_manager_component->GetRailManager()->CheckForHopToHangRail(
				obj_frame_search_start_pos,
				hop_to_hang_reach,
				obj_frame_facing,
				max_hop_to_hang_forward_reach,
				max_hop_to_hang_backward_reach,
				this,
				rail_data,
				&mp_rail_node
			))
			{
				mp_rail_manager = p_rail_manager_component->GetRailManager();
				mp_movable_contact_component->ObtainContact(p_rail_manager_component->GetObject());
			}
		}
	}
	
	if (!rail_found) return false;
	
	// we've found a rail to hop up to
	m_goal_hop_pos = rail_data.hang_point;
	m_goal_hop_facing = rail_data.hang_facing;
	m_along_rail_factor = rail_data.along_rail_factor;
	m_vertical_hang_offset = rail_data.vertical_hang_offset;
	m_horizontal_hang_offset = rail_data.horizontal_hang_offset;
	m_initial_hang_animation = rail_data.initial_animation;
	
	// calculate the vertical velocity required to jump to the hang point
	#ifdef __NOPT_ASSERT__
	if (m_pos[Y] >= m_goal_hop_pos[Y])
	{
		DUMPV(m_pos);
		DUMPV(m_goal_hop_pos);
	}
	Dbg_Assert(m_pos[Y] < m_goal_hop_pos[Y]);
	#endif
	
	float hop_target_vert_vel = s_get_param(CRCD(0x7aa618cc, "hop_target_vert_vel"));
	float gravity = s_get_param(CRCD(0xa5e2da58, "gravity"));
	
	// ready state for the hop to hang
	
	// calculate the required hop velocity
	m_vertical_vel = sqrtf(Mth::Sqr(hop_target_vert_vel) + 2.0f * gravity * (m_goal_hop_pos[Y] - m_pos[Y]));
	
	// calculate the required hop duration
	float duration = (m_vertical_vel + hop_target_vert_vel) / gravity;
	
	// not neccesarly exactly in the check_direction of the facing
	m_horizontal_vel = (1.0f / duration) * (m_goal_hop_pos - m_pos);
	
	// trip the ground's jump triggers
	if (m_last_ground_feeler_valid)
	{
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, m_last_ground_feeler);
		if (should_bail_from_frame()) return true;
	}
	
	set_state(WALKING_HOP);
    
	m_frame_event = CRCD(0xf41aba21, "Hop");
	
	return true;
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_drop_to_hang (   )
{
	// check to see if we're in a bail
	if (mp_core_physics_component->GetFlag(IS_BAILING)) return false;
	
	// only drop to hang when moving slowly
	if (m_horizontal_vel.LengthSqr() > Mth::Sqr(s_get_param(CRCD(0x1d1e6af, "drop_to_hang_speed_factor")) * get_run_speed())) return false;
	
	// clean vector
	m_frame_start_pos[W] = 1.0f;
	m_pos[W] = 1.0f;
	
	SHangRailData rail_data;
	
	bool rail_found = false;
	
	// check level for appropriate rails
	mp_rail_manager = Mdl::Skate::Instance()->GetRailManager();
	if (rail_found = mp_rail_manager->CheckForHangRail(
		m_frame_start_pos,
		m_pos,
		-m_facing,
		this,
		rail_data,
		Script::GetFloat(CRCD(0xa8276303, "Drop_To_Climb_Max_Snap"))
	))
	{
		mp_movable_contact_component->LoseAnyContact();
	}
	else
	{
		// check moving objects for appropriate rails
		for (CRailManagerComponent* p_rail_manager_component = static_cast< CRailManagerComponent* >(CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_RAILMANAGER));
			p_rail_manager_component && !rail_found;
			p_rail_manager_component = static_cast< CRailManagerComponent* >(p_rail_manager_component->GetNextSameType()))
		{
			Mth::Matrix obj_matrix_inv = p_rail_manager_component->UpdateRailManager();
			obj_matrix_inv.Invert();

			// transform into the frame of the moving object
			Mth::Vector obj_frame_start_pos = obj_matrix_inv.Transform(m_frame_start_pos);
			Mth::Vector obj_pos = obj_matrix_inv.Transform(m_pos);

			mp_rail_manager = p_rail_manager_component->GetRailManager();
			if (rail_found = mp_rail_manager->CheckForHangRail(
				obj_frame_start_pos,
				obj_pos,
				-m_facing,
				this,
				rail_data,
				Script::GetFloat(CRCD(0xa8276303, "Drop_To_Climb_Max_Snap"))
			))
			{
				mp_movable_contact_component->ObtainContact(p_rail_manager_component->GetObject());
			}
		}
	}

	if (!rail_found) return false;
	
	// we've found a rail to drop down to
	
	mp_rail_start = rail_data.p_rail_node;
	mp_rail_end = mp_rail_start->GetNextLink();
	Dbg_Assert(mp_rail_end);
	
	// zero velocities	
	m_vertical_vel = 0.0f;
	m_horizontal_vel.Set();
	
	m_along_rail_factor = rail_data.along_rail_factor;
	m_vertical_hang_offset = rail_data.vertical_hang_offset;
	m_horizontal_hang_offset = rail_data.horizontal_hang_offset;
    
    // setup an anim wait state
	
	m_anim_wait_initial_pos = m_pos;
	m_anim_wait_goal_pos = rail_data.hang_point;
	
    mp_anim_wait_complete_callback = &Obj::CWalkComponent::drop_to_hang_complete;
	
	calculate_anim_wait_facing_drift_parameters(rail_data.hang_facing, s_get_param(CRCD(0xfdc8aec0, "drop_to_hang_rotate_factor")));
	
	m_anim_wait_camera_mode = AWC_GOAL;
	
	m_critical_point_offset = s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * rail_data.hang_facing;
	
	// setup the false wall incase the player jumps out of the animation wait
	m_false_wall.normal = rail_data.hang_facing;
	m_false_wall.cancel_height = m_pos[Y];
	
	m_drift_initial_display_offset = m_display_offset;
	m_drift_goal_display_offset = get_hang_display_offset();

	set_state(WALKING_ANIMWAIT);

	m_frame_event = CRCD(0xeb71d98e, "DropToHang");
    
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::drop_to_hang_complete (   )
{
	m_critical_point_offset = s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * m_facing;
	m_critical_point_offset[Y] = get_hang_critical_point_vert_offset();
	
	set_state(WALKING_HANG);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_grab_to_hang ( Mth::Vector start_pos, Mth::Vector end_pos )
{
	// only grab to hang when moving down
	if (m_vertical_vel > 50.0f) return false;
	
	start_pos[Y] += s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
	end_pos[Y] += s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
	
	// clean vectors
	start_pos[W] = 1.0f;
	end_pos[W] = 1.0f;
	
	SHangRailData rail_data;
	
	bool rail_found = false;
	
	// check level for appropriate rails
	mp_rail_manager = Mdl::Skate::Instance()->GetRailManager();
	if (rail_found = mp_rail_manager->CheckForHangRail(
		start_pos,
		end_pos,
		m_facing,
		this,
		rail_data,
		Script::GetFloat(CRCD(0x30ce7f2c, "Climb_Max_Snap"))
	))
	{
		mp_movable_contact_component->LoseAnyContact();
	}
	else
	{
		// check moving objects for appropriate rails
		for (CRailManagerComponent* p_rail_manager_component = static_cast< CRailManagerComponent* >(CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_RAILMANAGER));
			p_rail_manager_component && !rail_found;
			p_rail_manager_component = static_cast< CRailManagerComponent* >(p_rail_manager_component->GetNextSameType()))
		{
			Mth::Matrix obj_matrix_inv = p_rail_manager_component->UpdateRailManager();
			obj_matrix_inv.Invert();
			
			// transform into the frame of the moving object
			Mth::Vector obj_frame_start_pos = obj_matrix_inv.Transform(start_pos);
			Mth::Vector obj_frame_end_pos = obj_matrix_inv.Transform(end_pos);
			
			mp_rail_manager = p_rail_manager_component->GetRailManager();
			if (rail_found = mp_rail_manager->CheckForHangRail(
				obj_frame_start_pos,
				obj_frame_end_pos,
				m_facing,
				this,
				rail_data,
				Script::GetFloat(CRCD(0x30ce7f2c, "Climb_Max_Snap"))
			))
			{
				mp_movable_contact_component->ObtainContact(p_rail_manager_component->GetObject());
			}
		}
	}
	
	if (!rail_found) return false;
		
	// we've found a rail to hang onto
	
	mp_rail_start = rail_data.p_rail_node;
	mp_rail_end = mp_rail_start->GetNextLink();
	Dbg_Assert(mp_rail_end);
	
	m_pos = rail_data.hang_point;
	DUMP_WPOSITION
	m_facing = rail_data.hang_facing;
	m_along_rail_factor = rail_data.along_rail_factor;
	m_vertical_hang_offset = rail_data.vertical_hang_offset;
	m_horizontal_hang_offset = rail_data.horizontal_hang_offset;
	m_initial_hang_animation = rail_data.initial_animation;
	
	m_critical_point_offset = s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * m_facing;
	m_critical_point_offset[Y] = get_hang_critical_point_vert_offset();
	
	m_horizontal_vel.Set();
	m_vertical_vel = 0.0f;
	
	m_display_offset = get_hang_display_offset();

	set_state(WALKING_HANG);

	m_frame_event = CRCD(0x4194ecca, "Hang");
	
	mp_core_physics_component->SetFlagTrue(SNAPPED);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::FilterHangRail ( const Mth::Vector& pos_a, const Mth::Vector& pos_b, const Mth::Vector& preliminary_hang_point, const Mth::Vector& grab_from_point, float along_rail_factor, SHangRailData& rail_data, bool drop_to_hang )
{
	// check this rail for potential hang use
	
	float hang_vert_origin_offset = s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset")); 
	
	// never snap upward to a hang rail
	if (m_state == WALKING_AIR && preliminary_hang_point[Y] > grab_from_point[Y]) return false;
	
	// never snap too far vertically to a hang rail
	if (m_state == WALKING_AIR && Mth::Abs(preliminary_hang_point[Y] - grab_from_point[Y]) > 12.0f) return false;
	
	// is the rail too steep
	Mth::Vector rail = pos_b - pos_a;
	Mth::Vector rail_direction = rail;
	rail_direction.Normalize();
	if (Mth::Abs(rail_direction[Y]) > sinf(Mth::DegToRad(s_get_param(CRCD(0x98724ebc, "hang_max_rail_ascent"))))) return false;
	
	// calculate the facing we'd have while hanging
	
	rail_data.hang_facing.Set(-(rail[Z]), 0.0f, rail[X]);
	rail_data.hang_facing.Normalize();
	if (!drop_to_hang)
	{
		// normally, we attempt retain our current facing
		if (Mth::DotProduct(rail_data.hang_facing, m_facing) < 0.0f)
		{
			rail_data.hang_facing.Negate();
		}
	}
	else
	{
		// unless dropping to a hang
		if (Mth::DotProduct(rail_data.hang_facing, m_facing) > 0.0f)
		{
			rail_data.hang_facing.Negate();
		}
	}

	bool rail_points_left = rail_data.hang_facing[Z] * rail_direction[X] - rail_data.hang_facing[X] * rail_direction[Z] > 0.0f;
	if ((rail_data.p_rail_node->GetFlag(NO_HANG_WITH_LEFT_ALONG_RAIL) && rail_points_left)
		|| (rail_data.p_rail_node->GetFlag(NO_HANG_WITH_RIGHT_ALONG_RAIL) && !rail_points_left))
	{
		rail_data.hang_facing.Negate();
		rail_points_left = !rail_points_left;
	}
	
	// investigate the hang point to see if its a good hang
	
	CFeeler feeler;
	Nx::CCollCache* p_coll_cache = Nx::CCollCacheManager::sCreateCollCache();
	Mth::CBBox bbox(
		preliminary_hang_point - Mth::Vector(24.0f, 24.0f, 24.0f),
		preliminary_hang_point + Mth::Vector(24.0f, 12.0f + hang_vert_origin_offset, 24.0f)
	);
	p_coll_cache->Update(bbox);
	feeler.SetCache(p_coll_cache);
	
	rail_data.along_rail_factor = along_rail_factor;
	
	if (!investigate_hang_rail_vicinity(feeler, preliminary_hang_point, rail_data))
	{
		// if we're attempting to grab a rail from the air, try both possible facings
		if (m_state == WALKING_AIR || !drop_to_hang)
		{
			// see if we're allowed to try the other facing
			if ((rail_data.p_rail_node->GetFlag(NO_HANG_WITH_LEFT_ALONG_RAIL) && !rail_points_left)
				|| (rail_data.p_rail_node->GetFlag(NO_HANG_WITH_RIGHT_ALONG_RAIL) && rail_points_left))
			{
				Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
				return false;
			}
			
			rail_data.hang_facing.Negate();
			if (!investigate_hang_rail_vicinity(feeler, preliminary_hang_point, rail_data))
			{
				Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
				return false;
			}
		}
		else
		{
			Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
			return false;
		}
	}
	
	rail_data.vertical_hang_offset = rail_data.hang_point[Y] - preliminary_hang_point[Y];
	rail_data.horizontal_hang_offset = Mth::DotProduct(rail_data.hang_facing, rail_data.hang_point - preliminary_hang_point);
	
	// determine which sort of initial hang animation would be used
	feeler.m_start = rail_data.hang_point + s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * rail_data.hang_facing;
	feeler.m_start[Y] += s_get_param(CRCD(0xdbb3d53e, "hang_init_anim_feeler_height"));
	feeler.m_end = feeler.m_start;
	feeler.m_end += s_get_param(CRCD(0x2f83ae83, "hang_init_anim_feeler_length")) * rail_data.hang_facing;
	if (feeler.GetCollision())
	{
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(255, 0, 0, 0);
		}
		#endif
		rail_data.initial_animation = CRCD(0xec0a1009, "WALL");
	}
	else
	{
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(0, 0, 255, 0);
		}
		#endif
		rail_data.initial_animation = CRCD(0x1ee948a5, "SWING");
	}
	
	Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::investigate_hang_rail_vicinity ( CFeeler& feeler, const Mth::Vector& preliminary_hang_point, SHangRailData& rail_data )
{
	/* turning off geo investigation for hang point adjustment
	// run a feeler up/down at the preliminary hang point to find the true top of the ledge
	feeler.SetLine(preliminary_hang_point, preliminary_hang_point);
	feeler.m_start[Y] += s_get_param(CRCD(0x1bc501ab, "ledge_top_feeler_up"));
	feeler.m_end[Y] -= s_get_param(CRCD(0xf5f39a8, "ledge_top_feeler_down"));
	if (feeler.GetCollision())
	{
		// found a ledge top
		rail_data.hang_point = feeler.GetPoint();
	}
	else
	{
		// use the prelimiary hang point
		rail_data.hang_point = preliminary_hang_point;
	}
	
	// run a feeler in an inch below the hang point along the hang facing to find the front of the ledge
	feeler.m_start = rail_data.hang_point - s_get_param(CRCD(0x95fd5dc5, "ledge_front_feeler_back")) * rail_data.hang_facing;
	feeler.m_end = rail_data.hang_point + s_get_param(CRCD(0xccb4f5d2, "ledge_front_feeler_forward")) * rail_data.hang_facing;
	feeler.m_start[Y] -= 1.0f;
	feeler.m_end[Y] -= 1.0f;
	if (feeler.GetCollision())
	{
		// found a ledge front
		rail_data.hang_point = feeler.GetPoint();
		rail_data.hang_point[Y] += 1.0f;
	}
	// hang a touch outside of it
	rail_data.hang_point -= 2.0f * rail_data.hang_facing;
	*/
	
	rail_data.hang_point = preliminary_hang_point;
	
	Mth::Vector left(-rail_data.hang_facing[Z], 0.0f, rail_data.hang_facing[X]);
	
	Mth::Vector rail = mp_rail_manager->GetPos(rail_data.p_rail_node) - mp_rail_manager->GetPos(rail_data.p_rail_node->GetNextLink());
	
	// now that we have the hang position, run two feelers up and down the hang location to insure there is room
	if (!vertical_hang_obstruction_check(feeler, rail_data, left) || !vertical_hang_obstruction_check(feeler, rail_data, -left)) return false;

	// adjust the hang point for the origin of the hanging skater
	rail_data.hang_point[Y] -= s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
	rail_data.hang_point += s_get_param(CRCD(0x262a9467, "hang_horiz_origin_offset")) * rail_data.hang_facing;
	
	// use our move feelers for a look around
	if (check_environment_for_hang_movement_collisions(rail_data.hang_facing, rail_data.along_rail_factor, rail_data.hang_point, true, left, rail, rail_data.p_rail_node, rail_data.p_rail_node->GetNextLink())) return false;
	if (check_environment_for_hang_movement_collisions(rail_data.hang_facing, rail_data.along_rail_factor, rail_data.hang_point, false, left, rail, rail_data.p_rail_node, rail_data.p_rail_node->GetNextLink())) return false;
	
	// adjust the feeler target check point based on hanging's critical point offset
	Mth::Vector critical_point = rail_data.hang_point + s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * rail_data.hang_facing;
	critical_point[Y] += get_hang_critical_point_vert_offset();
	
	// check for obstructions to our hang point; it is critical that our critical point remains on the happy side of collision geometry
	feeler.m_start = m_pos;
	feeler.m_end = critical_point;
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(255, 0, 255, 0);
	}
	#endif
	if (feeler.GetCollision())
	{
		if (m_state == WALKING_GROUND)
		{
			// we found an obstruction taking a direct route; if we'll be hopping to the rail, try an up-and-over route
			
			feeler.m_end = m_pos;
			feeler.m_end[Y] += s_get_param(CRCD(0xa85ec070, "hop_obstruction_feeler_up"));
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(255, 0, 255, 0);
			}
			#endif
			if (feeler.GetCollision())
			{
				#ifdef __USER_DAN__
				if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
				{
					Gfx::AddDebugStar(feeler.GetPoint(), 12.0f, MAKE_RGB(0, 255, 255), 0);
				}
				#endif
				return false;
			}
			
			feeler.m_start = feeler.m_end;
			feeler.m_end = critical_point;
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
			{
				feeler.DebugLine(255, 0, 255, 0);
			}
			#endif
			if (feeler.GetCollision())
			{
				#ifdef __USER_DAN__
				if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
				{
					Gfx::AddDebugStar(feeler.GetPoint(), 12.0f, MAKE_RGB(0, 255, 255), 0);
				}
				#endif
				return false;
			}
		}
		else
		{
			return false;
		}
	}
	
	return true;
}
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::vertical_hang_obstruction_check ( CFeeler& feeler, SHangRailData& rail_data, const Mth::Vector& check_offset_direction )
{
	feeler.m_start = rail_data.hang_point + s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * rail_data.hang_facing
		+ s_get_param(CRCD(0xafbf7687, "hang_obstruction_feeler_side")) * check_offset_direction;
	feeler.m_end = feeler.m_start;
	feeler.m_start[Y] += -s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset")) + get_hang_critical_point_vert_offset();
	feeler.m_end[Y] += s_get_param(CRCD(0x8f35f29f, "hang_obstruction_feeler_up"));
	if (feeler.GetCollision())
	{
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(255, 0, 50, 0);
		}
		#endif
		return false;
	}
	Mth::Swap(feeler.m_start, feeler.m_end);
	if (feeler.GetCollision())
	{
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(0, 255, 50, 0);
		}
		#endif
		return false;
	}
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(200, 200, 50, 0);
	}
	#endif
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*
void CWalkComponent::go_hop_state (   )
{
	// During the hop to hang state, the player has no control.  We simply move the goal position.
	
	if (mp_movable_contact_component->UpdateContact(m_pos))
	{
		m_pos += mp_movable_contact_component->GetContact()->GetMovement();
		
		if (mp_movable_contact_component->GetContact()->IsRotated())
		{
			m_facing = mp_movable_contact_component->GetContact()->GetRotation().Rotate(m_facing);
			m_goal_hop_facing = mp_movable_contact_component->GetContact()->GetRotation().Rotate(m_goal_hop_facing);
			if (m_facing[Y] != 0.0f)
			{
				m_facing[Y] = 0.0f;
				m_facing.Normalize();
			}
			if (m_goal_hop_facing[Y] != 0.0f)
			{
				m_goal_hop_facing[Y] = 0.0f;
				m_goal_hop_facing.Normalize();
			}
		}
	}
	
	if (m_vertical_vel > -s_get_param(CRCD(0x7aa618cc, "hop_target_vert_vel")))
	{
		m_pos += m_horizontal_vel * m_frame_length;
		m_pos[Y] += m_vertical_vel * m_frame_length + 0.5f * -s_get_param(CRCD(0xa5e2da58, "gravity")) * Mth::Sqr(m_frame_length);
		
		m_vertical_vel += -s_get_param(CRCD(0xa5e2da58, "gravity")) * m_frame_length;
		
		// BUG: we really want more control over this; this can cause pops; perhaps hold the hop duration and use the hop fraction complete
		m_facing = Mth::Lerp(m_facing, m_goal_hop_facing, 30.0f * Tmr::FrameLength()); 

		m_frame_event = CRCD(0xf41aba21, "Hop");
	}
	else
	{
		m_horizontal_vel.Set();
		m_vertical_vel = 0.0f;
		
		m_pos = m_goal_hop_pos;
		
		m_facing = m_goal_hop_facing;
		
		set_state(WALKING_HANG);
		
		m_frame_event = CRCD(0x4194ecca, "Hang");
	}
}
*/
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::go_hang_state (   )
{
	// Mth::Vector rail = (mp_rail_manager->GetPos(mp_rail_start) - mp_rail_manager->GetPos(mp_rail_end)).Normalize();
	// DUMPF(Mth::RadToDeg(asin(Mth::Abs(rail[Y]))));
	
	if (mp_movable_contact_component->UpdateContact(m_pos))
	{
		CRailManagerComponent* p_rail_manager_component = GetRailManagerComponentFromObject(mp_movable_contact_component->GetContact()->GetObject());
		Dbg_Assert(p_rail_manager_component);

		p_rail_manager_component->UpdateRailManager();
		
		// calculate our new position
		set_pos_from_hang_rail_state();
	}
	
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0x1a5eab7, "rail_highlights")))
	{
		Gfx::AddDebugLine(mp_rail_manager->GetPos(mp_rail_start), mp_rail_manager->GetPos(mp_rail_end), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
		Gfx::AddDebugLine(mp_rail_manager->GetPos(mp_rail_start) + Mth::Vector(1.0f, 0.0f, 0.0f), mp_rail_manager->GetPos(mp_rail_end) + Mth::Vector(1.0f, 0.0f, 0.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
		Gfx::AddDebugLine(mp_rail_manager->GetPos(mp_rail_start) + Mth::Vector(0.0f, 1.0f, 0.0f), mp_rail_manager->GetPos(mp_rail_end) + Mth::Vector(0.0f, 1.0f, 0.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
		Gfx::AddDebugLine(mp_rail_manager->GetPos(mp_rail_start) + Mth::Vector(0.0f, 0.0f, 1.0f), mp_rail_manager->GetPos(mp_rail_end) + Mth::Vector(0.0f, 0.0f, 1.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
	}
	#endif
	
	// only respond to full up/down controls and only after a frame delay (to allow the debounce a frame to kick in)
	if (m_control_pegged)
	{
		// up; IsLeftAnalogUpPressed allows for analog debouncing
		if (mp_input_component->GetControlPad().IsLeftAnalogUpPressed())
		{
			// potential standing position after pull up
			Mth::Vector stand_pos = m_pos + s_get_param(CRCD(0x21dfbe77, "pull_up_offset_forward")) * m_facing;
			stand_pos[Y] += s_get_param(CRCD(0x52fa7ca6, "pull_up_offset_up"));
			if (maybe_pull_up_from_hang(stand_pos)) return;
			
			// try standing a little closer
			stand_pos = m_pos + 0.5f * s_get_param(CRCD(0x21dfbe77, "pull_up_offset_forward")) * m_facing;
			stand_pos[Y] += s_get_param(CRCD(0x52fa7ca6, "pull_up_offset_up"));
			if (maybe_pull_up_from_hang(stand_pos)) return;
		}
		// down; IsLeftAnalogDownPressed allows for analog debouncing
		else if (mp_input_component->GetControlPad().IsLeftAnalogDownPressed())
		{
			// drop
			set_state(WALKING_AIR);
			m_primary_air_direction = m_facing;
			leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
			m_frame_event = CRCD(0x439f4704, "Air");
			
			return;
		}
	}
	
	// left or right
	float control_vel;
	if (Mth::Abs((Mth::PI / 2.0f) - Mth::Abs(mp_input_component->GetControlPad().m_leftAngle)) < cosf(Mth::DegToRad(s_get_param(CRCD(0x1b877928, "hang_vert_control_tolerance")))))
	{
		control_vel = s_get_param(CRCD(0xd77ee881, "hang_move_speed")) * m_control_magnitude
			* (mp_input_component->GetControlPad().m_leftAngle > 0.0f ? 1.0f : -1.0f);
	}
	else
	{
		control_vel = 0.0f;
	}
	m_hang_move_vel = Mth::Lerp(m_hang_move_vel, control_vel, s_get_param(CRCD(0xc506a97c, "hang_move_lerp_rate")) * m_frame_length);
	
	if (Mth::Abs(m_hang_move_vel) > s_get_param(CRCD(0x228b5376, "hang_move_cutoff")))
	{
		m_anim_effective_speed = Mth::Abs(m_hang_move_vel);
	
		hang_movement(m_hang_move_vel * m_frame_length);

		// calculate our new position
		set_pos_from_hang_rail_state();
	}
	else
	{
		m_frame_event = CRCD(0x4194ecca, "Hang");
	}
	
	/* this doesn't make sense with the new higher critical point
	// run a feeler between our old and new position to insure that our position stays safe
	CFeeler feeler;
	feeler.m_start = m_frame_start_pos + s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * m_facing;
	feeler.m_end = m_pos + s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * m_facing;
	if (feeler.GetCollision())
	{
		// our feet have hit something; often this means the rail has dropped to ground level; drop from our old position
		m_pos = m_frame_start_pos;
		set_state(WALKING_AIR);
		m_primary_air_direction = m_facing;
		leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
		m_frame_event = CRCD(0x439f4704, "Air");
		return;
	}
	*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
		
void CWalkComponent::hang_movement ( float movement )
{
	// cache current critical point location and entire state
	Mth::Vector initial_critical_point = m_pos + m_critical_point_offset;
	Mth::Vector initial_pos = m_pos;
	float initial_along_rail_factor = m_along_rail_factor;
	const CRailNode* p_initial_rail_start = mp_rail_start;
	const CRailNode* p_initial_rail_end = mp_rail_end;
	Mth::Vector initial_critical_point_offset = m_critical_point_offset; 
	Mth::Vector initial_facing = m_facing; 
	
	// allow left/right movement along rail
	
	Mth::Vector perp_facing(-m_facing[Z], 0.0f, m_facing[X]);
	
	// the offset from rail start to rail end
	Mth::Vector rail = mp_rail_manager->GetPos(mp_rail_start) - mp_rail_manager->GetPos(mp_rail_end);
	
	// move left or right
	bool left = movement > 0.0f;
	
	// is rail fractional position parameter ascends or descends with the movement
	bool with_rail;
	if (Mth::DotProduct(perp_facing, rail) < 0.0f)
	{
		with_rail = left;
	}
	else
	{
		with_rail = !left;
	}

	// check for collisions
	if (check_environment_for_hang_movement_collisions(m_facing, m_along_rail_factor, m_pos, left, perp_facing, rail, mp_rail_start, mp_rail_end))
	{
		m_hang_move_vel = 0.0f;
		m_frame_event = CRCD(0x4194ecca, "Hang");
		return;
	}
	
	// the movement in terms of the fraction of the rail
	float delta_factor = Mth::Abs(movement) / rail.Length();
	
	// move along the rail
	m_along_rail_factor += with_rail ? delta_factor : -delta_factor;
	
	// if we're moving off the rail
	if (m_along_rail_factor < 0.0f || m_along_rail_factor > 1.0f)
	{
		bool found_rail = find_next_rail(m_along_rail_factor, mp_rail_start, mp_rail_end, mp_rail_start, mp_rail_end);
			
		// there's no connecting rail
		if (!found_rail)
		{
			// clamp the movement
			m_hang_move_vel = 0.0f;
			m_along_rail_factor = Mth::Clamp(m_along_rail_factor, 0.0f, 1.0f);
			m_frame_event = CRCD(0x4194ecca, "Hang");
		}
		else
		{
			// move onto the neighboring rail
			float remaining_movement = Mth::Abs(m_along_rail_factor - Mth::Clamp(m_along_rail_factor, 0.0f, 1.0f)) * rail.Length();
			move_onto_neighboring_hang_rail(remaining_movement, m_along_rail_factor < 0.0f, with_rail ? rail : -rail);
			m_frame_event = left ? CRCD(0x2d9815c3, "HangMoveLeft") : CRCD(0x279b1f0b, "HangMoveRight");
		}
	}
	else
	{
		m_frame_event = left ? CRCD(0x2d9815c3, "HangMoveLeft") : CRCD(0x279b1f0b, "HangMoveRight");
	}
	
	// check to see if the critical point has gone through geo
	
	Mth::Vector critical_point = m_pos + m_critical_point_offset;
	CFeeler feeler;
	feeler.m_start = initial_critical_point;
	feeler.m_end = critical_point;
	if (feeler.GetCollision())
	{
		// restore entire to state to initial state
		m_pos = initial_pos;
		DUMP_WPOSITION
		m_along_rail_factor = initial_along_rail_factor;
		mp_rail_start = p_initial_rail_start;
		mp_rail_end = p_initial_rail_end;
		m_critical_point_offset = initial_critical_point_offset;
		m_facing = initial_facing;
		
		m_hang_move_vel = 0.0f;
		m_frame_event = CRCD(0x4194ecca, "Hang");
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
		
bool CWalkComponent::check_environment_for_hang_movement_collisions ( const Mth::Vector& facing, float along_rail_factor, const Mth::Vector& pos, bool left, const Mth::Vector& perp_facing, Mth::Vector rail, const CRailNode* p_rail_start, const CRailNode* p_rail_end )
{
	// check environment in the appropriate check_direction for collisions which would prevent hanging movement
	// OPTIMIZATION: use a CCollCache here
	
	bool with_rail;
	if (Mth::DotProduct(perp_facing, rail) < 0.0f)
	{
		with_rail = left;
	}
	else
	{
		with_rail = !left;
	}
	
	// check_direction to the side to check for geo
	Mth::Vector check_direction = left ? perp_facing : -perp_facing;
	
	// adjusted facing due to adjusted check_direction
	Mth::Vector check_direction_facing = facing;

	// see if we're near the end of the rail; if so, we may want to angle our feelers out (so we can make inside turns while hanging)
	float distance_to_end_of_rail = rail.Length() * (with_rail ? 1.0f - along_rail_factor : along_rail_factor);
	if (distance_to_end_of_rail < s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) + 1.0f)
	{
		// pretend we're past the rail when calling find_next_rail
		along_rail_factor += with_rail ? 2.0f : -2.0f;
		
		const CRailNode* p_next_start;
		const CRailNode* p_next_end;
		if (find_next_rail(along_rail_factor, p_rail_start, p_rail_end, p_next_start, p_next_end))
		{
			Mth::Vector new_check_direction = (along_rail_factor > 0.5f ? -1.0f : 1.0f)
				* (mp_rail_manager->GetPos(p_next_start) - mp_rail_manager->GetPos(p_next_end));
			new_check_direction[Y] = 0.0f;
			new_check_direction.Normalize();
			
			// only use adjusted feelers for inside turns
			float dot = Mth::DotProduct(facing, new_check_direction);
			if (dot < 0.0f && dot > -0.87f)
			{
				check_direction = new_check_direction;
				if (left)
				{
					check_direction_facing[X] = check_direction[Z];
					check_direction_facing[Z] = -check_direction[X];
				}
				else
				{
					check_direction_facing[X] = -check_direction[Z];
					check_direction_facing[Z] = check_direction[X];
				}
			}
		}
	}
	
	CFeeler feeler;
	feeler.m_start = pos;
	feeler.m_start[Y] += s_get_param(CRCD(0x84ff931c, "hang_move_collision_up"));
	feeler.m_start -= s_get_param(CRCD(0xcbeb3d89, "hang_move_collision_back")) * facing;
	
	// feel to the side
	feeler.m_end = feeler.m_start;
	feeler.m_end += s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) * check_direction;
	if (feeler.GetCollision())
	{
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(255, 0, 0, 1);
		}
		#endif
		return true;
	}
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 0, 255, 1);
	}
	#endif
	
	// feel up and to the side
	feeler.m_end = feeler.m_start;
	feeler.m_end[Y] += s_get_param(CRCD(0xc774dd6d, "hang_move_collision_side_height"));
	feeler.m_end += s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) * check_direction;
	if (feeler.GetCollision())
	{
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(255, 0, 0, 1);
		}
		#endif
		return true;
	}
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 0, 255, 1);
	}
	#endif
	
	// feel half up and to the side
	feeler.m_end = feeler.m_start;
	feeler.m_end[Y] += 0.5f * s_get_param(CRCD(0xc774dd6d, "hang_move_collision_side_height"));
	feeler.m_end += s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) * check_direction;
	if (feeler.GetCollision())
	{
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(255, 0, 0, 1);
		}
		#endif
		return true;
	}
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 0, 255, 1);
	}
	#endif
	
	// feel down (to critical point) and to the side and back to the critical point
	feeler.m_end = feeler.m_start;
	feeler.m_end[Y] += -s_get_param(CRCD(0x84ff931c, "hang_move_collision_up")) + get_hang_critical_point_vert_offset() - 1.0f;
	feeler.m_end += s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * check_direction_facing;
	feeler.m_end += s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) * check_direction;
	 // add a touch along the rail so we don't get stuck with both directions feelers in the ground
	feeler.m_end += with_rail ? -rail.Normalize() : rail.Normalize();
	if (feeler.GetCollision())
	{
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(255, 0, 0, 1);
		}
		#endif
		return true;
	}
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 0, 255, 1);
	}
	#endif
	
	// feel half down and to the side and back to the critical point
	feeler.m_end = feeler.m_start;
	feeler.m_end[Y] -= 0.5f * s_get_param(CRCD(0xc774dd6d, "hang_move_collision_side_height"));
	feeler.m_end += s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * check_direction_facing;
	feeler.m_end += s_get_param(CRCD(0x3344a6d0, "hang_move_collision_side_length")) * check_direction;
	if (feeler.GetCollision())
	{
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(255, 0, 0, 1);
		}
		#endif
		return true;
	}
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 0, 255, 1);
	}
	#endif
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::move_onto_neighboring_hang_rail ( float movement, bool descending, const Mth::Vector& old_rail )
{
	Mth::Vector next_rail = mp_rail_manager->GetPos(mp_rail_start) - mp_rail_manager->GetPos(mp_rail_end);
	float next_rail_length = next_rail.Length();
	
	// if the next rail is shorter than our movement, just ditch the extra movement
	movement = Mth::ClampMax(movement, next_rail_length);
	
	// we've found the appropriate neighbor rail to move on to, so set up our state
	
	// calculate our position along the rail
	m_along_rail_factor = movement / next_rail_length;
	if (descending)
	{
		m_along_rail_factor = 1.0f - m_along_rail_factor;
	}
	
	// calculate our next facing
	Mth::Vector next_facing(-next_rail[Z], 0.0f, next_rail[X]);
	next_facing.Normalize();
	
	// check its check_direction
	if (Mth::CrossProduct(old_rail, m_facing)[Y] * Mth::CrossProduct(descending ? -next_rail : next_rail, next_facing)[Y] < 0.0f)
	{
		next_facing.Negate();
	}
	
	if (Mth::DotProduct(next_facing, m_facing) < cosf(Mth::DegToRad(60.0f)))
	{
		// move the camera behind us
		mp_camera_component->FlushRequest();
	}
	m_facing = next_facing;
	
	m_critical_point_offset = s_get_param(CRCD(0xc3263e6e, "hang_critical_point_horiz_offset")) * m_facing;
	m_critical_point_offset[Y] = get_hang_critical_point_vert_offset();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::set_pos_from_hang_rail_state (   )
{
	m_pos = Mth::Lerp(mp_rail_manager->GetPos(mp_rail_start), mp_rail_manager->GetPos(mp_rail_end), m_along_rail_factor);
	m_pos[Y] += m_vertical_hang_offset;
	m_pos += m_horizontal_hang_offset * m_facing;
	DUMP_WPOSITION
	
	if (mp_movable_contact_component->HaveContact() && mp_movable_contact_component->GetContact()->IsRotated())
	{
		// recalculate facing
		Mth::Vector new_facing = mp_rail_manager->GetPos(mp_rail_start) - mp_rail_manager->GetPos(mp_rail_end);
		new_facing[Y] = 0.0f;
		new_facing.Normalize();
		new_facing.Set(-new_facing[Z], 0.0f, new_facing[X]);
		if (Mth::DotProduct(new_facing, m_facing) > 0.0f)
		{
			m_facing = new_facing;
		}
		else
		{
			m_facing = -new_facing;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::find_next_rail ( float& along_rail_factor, const CRailNode* p_current_start, const CRailNode* p_current_end, const CRailNode*& p_new_start, const CRailNode*& p_new_end )
{
	// find the neighboring rail, if there is one
	
	Dbg_Assert(along_rail_factor < 0.0f || along_rail_factor > 1.0f);
	
	const CRailNode* next_rail_node = NULL;
	if (along_rail_factor < 0.0f)
	{
		// look for a connection
		if (p_current_start->GetPrevLink() && p_current_start->GetPrevLink()->IsActive() && !p_current_start->GetPrevLink()->GetFlag(NO_CLIMBING)
			&& Rail_ValidInEditor(mp_rail_manager->GetPos(p_current_start->GetPrevLink()), mp_rail_manager->GetPos(p_current_start)))
		{
			p_new_end = p_current_start;
			p_new_start = p_current_start->GetPrevLink();
			// MESSAGE("FOUND CONNECTING RAIL");
			return true;
		}
		
		// look for another node who's position corresponds to the current start node
		if (!mp_rail_manager->CheckForCoincidentRailNode(p_current_start, nBit(NO_CLIMBING) | nBit(LADDER), &next_rail_node))
		{
			// MESSAGE("NO COINCIDENT RAIL");
			return false;
		}
	}
	else
	{
		// look for a connection
		if (p_current_end->GetNextLink() && p_current_end->IsActive() && !p_current_end->GetFlag(NO_CLIMBING)
			&& Rail_ValidInEditor(mp_rail_manager->GetPos(p_current_end), mp_rail_manager->GetPos(p_current_end->GetNextLink())))
		{
			p_new_start = p_current_end;
			p_new_end = p_current_end->GetNextLink();
			// MESSAGE("FOUND CONNECTING RAIL");
			return true;
		}
		
		// look for another node who's position corresponds a given node's position
		if (!mp_rail_manager->CheckForCoincidentRailNode(p_current_end, nBit(NO_CLIMBING) | nBit(LADDER), &next_rail_node))
		{
			// MESSAGE("NO COINCIDENT RAIL");
			return false;
		}
	}
	
	Dbg_Assert(next_rail_node && next_rail_node->GetNextLink());
	
	bool rails_are_flush = mp_rail_manager->RailNodesAreCoincident(next_rail_node, p_current_end)
		|| mp_rail_manager->RailNodesAreCoincident(next_rail_node->GetNextLink(), p_current_start);
    
	Mth::Vector old_rail_dir = (mp_rail_manager->GetPos(p_current_end) - mp_rail_manager->GetPos(p_current_start)).Normalize();
	Mth::Vector new_rail_dir = (mp_rail_manager->GetPos(next_rail_node->GetNextLink()) - mp_rail_manager->GetPos(next_rail_node)).Normalize();
	if (!rails_are_flush)
	{
		new_rail_dir *= -1.0f;
	}
	if (Mth::DotProduct(old_rail_dir, new_rail_dir) < -0.866f)
	{
		MESSAGE("BAD ANGLE ON COINCIDENT RAIL");
		return false;
	}
	
	if (!rails_are_flush)
	{
		along_rail_factor = 1.0f - along_rail_factor;
	}
	p_new_start = next_rail_node;
	p_new_end = next_rail_node->GetNextLink();
	MESSAGE("FOUND COINCIDENT RAIL");
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_pull_up_from_hang ( Mth::Vector& proposed_stand_pos )
{
	// check to see if there's a good place for us to stand if we pull up; if so, do so
	
	Mth::Vector stand_pos;
	
	CFeeler potential_ground_feeler;
	bool ground = determine_stand_pos(proposed_stand_pos, stand_pos, potential_ground_feeler);
	
	if (!ground)
	{
		Mth::Vector initial_stand_pos = stand_pos;
		Mth::Vector left(-m_facing[Z], 0.0f, m_facing[X]);
		
		// if we couldn't find the ground, we may be at the end of a rail which hangs over the geo; look for stand positions to the left and right
		const float SIDEWAYS_SEARCH_DISTANCE = 12.0f;
		proposed_stand_pos += SIDEWAYS_SEARCH_DISTANCE * left;
		ground = determine_stand_pos(proposed_stand_pos, stand_pos, potential_ground_feeler);
		if (!ground)
		{
			proposed_stand_pos -= (2.0f * SIDEWAYS_SEARCH_DISTANCE) * left;
			ground = determine_stand_pos(proposed_stand_pos, stand_pos, potential_ground_feeler);
			if (!ground)
			{
				stand_pos = initial_stand_pos;
			}
		}
	}
	
	// check for walls around the standing position
	CFeeler feeler;
	float push_feeler_length = 0.5f * s_get_param(CRCD(0xa20c43b7, "push_feeler_length"));
	float feeler_height = s_get_param(CRCD(0x6da7f696, "feeler_height"));
	for (int n = 0; n < vNUM_FEELERS; n++)
	{
		float angle = n * (2.0f * Mth::PI / vNUM_FEELERS);
		
		float cos_angle = cosf(angle);
		float sin_angle = sinf(angle);
		
		Mth::Vector end_offset;
		end_offset[X] = cos_angle * m_facing[X] - sin_angle * m_facing[Z];
		end_offset[Y] = 0.0f;
		end_offset[Z] = sin_angle * m_facing[X] + cos_angle * m_facing[Z];
		end_offset[W] = 1.0f;
		end_offset *= push_feeler_length;
		
		feeler.m_start = stand_pos;
		feeler.m_start[Y] += feeler_height;
		feeler.m_end = stand_pos + end_offset;
		feeler.m_end[Y] += feeler_height;
		
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(0, 255, 255, 0);
		}
		#endif
		if (feeler.GetCollision()) return false;
	}
	
	// check line from hang point to standing position for obstructions
	feeler.m_start = m_pos;
	feeler.m_start += -6.0f * m_facing;
	feeler.m_start[Y] += s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset")) + s_get_param(CRCD(0x9b2818cf, "pull_up_obstruction_height"));
	feeler.m_end = stand_pos;
	feeler.m_end[Y] += 1.0f;
	if (feeler.GetCollision())
	{
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
		{
			feeler.DebugLine(0, 100, 100, 0);
		}
		#endif
		return false;
	}
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(100, 0, 100, 0);
	}
	#endif
	
	// pull up looks ok, so let's do it
	
	if (ground)
	{
		mp_anim_wait_complete_callback = &Obj::CWalkComponent::pull_up_from_hang_to_ground_complete;
		
		m_last_ground_feeler = potential_ground_feeler;
		m_last_ground_feeler_valid = true;
		m_ground_normal = potential_ground_feeler.GetNormal();
	}
	else
	{
		mp_anim_wait_complete_callback = &Obj::CWalkComponent::pull_up_from_hang_to_air_complete;
	}
	
	m_anim_wait_initial_pos = m_pos;
	m_anim_wait_goal_pos = stand_pos;

	calculate_anim_wait_facing_drift_parameters(m_facing);
	
	m_anim_wait_camera_mode = AWC_GOAL;
	
	m_drift_initial_display_offset = m_display_offset;
	m_drift_goal_display_offset = 0.0f;
						 	
	m_critical_point_offset = -(6.0f + s_get_param(CRCD(0x21dfbe77, "pull_up_offset_forward"))) * m_facing;
	m_critical_point_offset[Y] = 1.0f;
	
	// setup the false wall incase the player jumps out of the animation wait
	m_false_wall.normal = m_facing;
	m_false_wall.cancel_height = m_pos[Y] - m_vertical_hang_offset;

	set_state(WALKING_ANIMWAIT);

	m_frame_event = CRCD(0x9ef9f8f1, "PullUpFromHang");
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::pull_up_from_hang_to_ground_complete (   )
{
    set_state(WALKING_GROUND);
	
	if (m_last_ground_feeler_valid)
	{
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_ONTO, m_last_ground_feeler);

		// check for a moving contact
		mp_movable_contact_component->CheckForMovableContact(m_last_ground_feeler);
	}
	
	m_critical_point_offset.Set();
	
	m_display_offset = m_drift_goal_display_offset;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::pull_up_from_hang_to_air_complete (   )
{
    set_state(WALKING_AIR);
	
	m_primary_air_direction = m_facing;
	leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
	
	m_display_offset = m_drift_goal_display_offset;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CWalkComponent::get_hang_display_offset (   )
{
    if (mp_model_component->GetModel()->IsScalingEnabled())
	{
		return (1.0f - mp_model_component->GetModel()->GetScale()[Y]) * s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
	}
	else
	{
		return 0.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CWalkComponent::get_hang_critical_point_vert_offset (   )
{
	return s_get_param(CRCD(0xde86132e, "hang_critical_point_vert_offset"))
		- s_get_param(CRCD(0x7201ad48, "max_cas_scaling")) * s_get_param(CRCD(0xa8c13e74, "hang_vert_origin_offset"));
}

}


================================================
FILE: Code/Gel/Components/WalkLadderUtil.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       WalkLadderUtil.cpp
//* OWNER:          Dan
//* CREATION DATE:  4/29/3
//****************************************************************************

#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

// NOTE: The ladder walking code currently ignores movable rail managers!
                 
namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_climb_up_ladder ( bool user_requested )
{
	// check to see if we're in a bail
	if (mp_core_physics_component->GetFlag(IS_BAILING)) return false;
	
	SLadderRailData rail_data;

	// Check for appropriate rails.
    if (!Mdl::Skate::Instance()->GetRailManager()->CheckForLadderRail(
        m_pos,
        user_requested ? s_get_param(CRCD(0xeecd255, "button_horiz_snap_distance")) : s_get_param(CRCD(0xa2735dd9, "max_horiz_snap_distance")),
        s_get_param(CRCD(0xc4c164e8, "max_vert_snap_distance")),
		true,
        this,
        rail_data,
        &mp_rail_start
    )) return false;
	
	// store the relevant rail manager
	mp_rail_manager = Mdl::Skate::Instance()->GetRailManager();
	mp_movable_contact_component->LoseAnyContact();
	
	// zero velocities	
	m_vertical_vel = 0.0f;
	m_horizontal_vel.Set();
    
    // setup an anim wait state
	
	m_anim_wait_initial_pos = m_pos;
	m_anim_wait_goal_pos = rail_data.climb_point;
	
    mp_anim_wait_complete_callback = &Obj::CWalkComponent::move_to_ladder_bottom_complete;
	
	m_drift_initial_display_offset = m_display_offset;
	m_drift_goal_display_offset = 0.0f;
	
	calculate_anim_wait_facing_drift_parameters(rail_data.climb_facing);
	
	m_anim_wait_camera_mode = AWC_GOAL;
		  	
	// setup the false wall incase the player jumps out of the animation wait
	m_false_wall.normal = rail_data.climb_facing;
	m_false_wall.cancel_height = rail_data.climb_point[Y];

	set_state(WALKING_ANIMWAIT);

	m_frame_event = CRCD(0xe2524194, "LadderOntoBottom");
    
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_climb_down_ladder (   )
{
   	// check to see if we're in a bail
	if (mp_core_physics_component->GetFlag(IS_BAILING)) return false;
	
    SLadderRailData rail_data;
    
	// Check for appropriate rails.
    if (!Mdl::Skate::Instance()->GetRailManager()->CheckForLadderRail(
        m_pos,
        s_get_param(CRCD(0xa2735dd9, "max_horiz_snap_distance")),
        s_get_param(CRCD(0xc4c164e8, "max_vert_snap_distance")),
		false,
        this,
        rail_data,
        &mp_rail_start
    )) return false;
	
	// store the relevant rail manager
	mp_rail_manager = Mdl::Skate::Instance()->GetRailManager();
	mp_movable_contact_component->LoseAnyContact();
	
	// zero velocities	
	m_vertical_vel = 0.0f;
	m_horizontal_vel.Set();
	
    // setup an anim wait state
	
	m_anim_wait_initial_pos = m_pos;
	m_anim_wait_goal_pos = rail_data.climb_point;
	
    mp_anim_wait_complete_callback = &Obj::CWalkComponent::move_to_ladder_top_complete;
	
	m_drift_initial_display_offset = m_display_offset;
	m_drift_goal_display_offset = 0.0f;
	
	calculate_anim_wait_facing_drift_parameters(rail_data.climb_facing);
	
	// if (Mth::DotProduct(mp_camera->GetMatrix()[Z], m_facing) > 0.0f)
	// {
		m_anim_wait_camera_mode = AWC_GOAL;
	// }
	// else
	// {
		// m_anim_wait_camera_mode = AWC_CURRENT;
	// }
	   		  	
	// setup the false wall incase the player jumps out of the animation wait
	m_false_wall.normal = rail_data.climb_facing;
	m_false_wall.cancel_height = m_pos[Y];

	set_state(WALKING_ANIMWAIT);

	m_frame_event = CRCD(0xd63adcad, "LadderOntoTop");
    
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::maybe_grab_to_ladder ( const Mth::Vector& start_pos, const Mth::Vector& end_pos )
{
    SLadderRailData rail_data;
	
	// Check for appropriate rails.
    if (!Mdl::Skate::Instance()->GetRailManager()->CheckForAirGrabLadderRail(
		start_pos,
		end_pos,
		this,
		rail_data,
		&mp_rail_start
    )) return false;
	
	// store the relevant rail manager
	mp_rail_manager = Mdl::Skate::Instance()->GetRailManager();
	mp_movable_contact_component->LoseAnyContact();
	
	// we've found a rail to climb
	m_pos = rail_data.climb_point;
	DUMP_WPOSITION
	m_facing = rail_data.climb_facing;
	m_along_rail_factor = rail_data.along_rail_factor;
	
	// zero velocities	
	m_vertical_vel = 0.0f;
	m_horizontal_vel.Set();
	
	set_state(WALKING_LADDER);

	m_frame_event = CRCD(0xc84243da, "Ladder");
    
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::move_to_ladder_bottom_complete (   )
{
	// getting on at the bottom of the ladder
	
	// determine top node
	const CRailNode* p_top_rail_node = mp_rail_start->GetNextLink() ? mp_rail_start->GetNextLink() : mp_rail_start->GetPrevLink();
	Dbg_Assert(p_top_rail_node);
		
	// the offset from the ladder bottom to rail top
	Mth::Vector rail = mp_rail_manager->GetPos(p_top_rail_node) - mp_rail_manager->GetPos(mp_rail_start);
	float rail_length = rail.Length();
	
	// calculate initial positoin along rail
	m_along_rail_factor = s_get_param(CRCD(0x5296e3fd, "ladder_bottom_offset_up")) / rail_length;
	
    set_state(WALKING_LADDER);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::move_to_ladder_top_complete (   )
{
	// getting on at the top of the ladder
	
	// determine top node
	const CRailNode* p_top_rail_node = mp_rail_start->GetNextLink() ? mp_rail_start->GetNextLink() : mp_rail_start->GetPrevLink();
	Dbg_Assert(p_top_rail_node);
		
	// the offset from the ladder bottom to rail top
	Mth::Vector rail = mp_rail_manager->GetPos(p_top_rail_node) - mp_rail_manager->GetPos(mp_rail_start);
	float rail_length = rail.Length();
	
	// calculate initial positoin along rail
	m_along_rail_factor = (rail_length - s_get_param(CRCD(0x76bf49e0, "ladder_top_offset_up"))) / rail_length;
	
    set_state(WALKING_LADDER);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::FilterLadderUpRail ( const Mth::Vector& bottom_pos, const Mth::Vector& top_pos, const Mth::Matrix& orientation, SLadderRailData& rail_data )
{
    // check that we're at the bottom of the ladder
    if (bottom_pos[Y] > top_pos[Y]) return false;
    
    // check that the proposed ladder rail is vertical
    Mth::Vector ladder_direction = top_pos - bottom_pos;
    ladder_direction.Normalize();
    if (ladder_direction[Y] < 0.995f) return false;
	
	// check that we're facing the ladder
	if (Mth::DotProduct(m_facing, orientation[Z]) < cosf(Mth::DegToRad(s_get_param(CRCD(0x456f216d, "max_onto_ladder_angle"))))) return false;
    
    // calculate the climb position
    rail_data.climb_point = bottom_pos + ladder_direction * s_get_param(CRCD(0x5296e3fd, "ladder_bottom_offset_up"));
	rail_data.climb_point -= s_get_param(CRCD(0x35b3bbda, "ladder_climb_offset")) * orientation[Z];
    
    // send feeler to climb position
    CFeeler feeler;
    feeler.m_start = m_pos;
    feeler.m_end = rail_data.climb_point - 6.0f * orientation[Z];
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 0, 255, 0);
	}
	#endif
    if (feeler.GetCollision()) return false;
    
    // store the climb facing
    rail_data.climb_facing = orientation[Z];
    
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::FilterLadderDownRail ( const Mth::Vector& bottom_pos, const Mth::Vector& top_pos, const Mth::Matrix& orientation, SLadderRailData& rail_data )
{
    // check that we're at the top of the ladder
    if (bottom_pos[Y] > top_pos[Y]) return false;
    
    // check that the proposed ladder rail is vertical
    Mth::Vector ladder_direction = top_pos - bottom_pos;
    ladder_direction.Normalize();
    if (ladder_direction[Y] < 0.995f) return false;
    
    // calculate the climb position
    rail_data.climb_point = top_pos - ladder_direction * s_get_param(CRCD(0x76bf49e0, "ladder_top_offset_up"));
	rail_data.climb_point -= s_get_param(CRCD(0x35b3bbda, "ladder_climb_offset")) * orientation[Z];
    
	/*
    // send feeler to climb position
    CFeeler feeler;
    feeler.m_start = m_pos;
    feeler.m_end = rail_data.climb_point - 6.0f * orientation[Z];
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 0, 255, 0);
	}
	#endif
    if (feeler.GetCollision()) return false;
	*/
    
    // store the climb facing
    rail_data.climb_facing = orientation[Z];
    
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWalkComponent::FilterLadderAirGrabRail ( const Mth::Vector& bottom_pos, const Mth::Vector& top_pos, const Mth::Matrix& orientation, const Mth::Vector& preliminary_climb_point, float along_rail_factor, SLadderRailData& rail_data )
{
    // check that the proposed ladder rail is vertical
    Mth::Vector ladder_direction = top_pos - bottom_pos;
    ladder_direction.Normalize();
    if (ladder_direction[Y] < 0.995f) return false;
	
	// insure that the target point on the ladder is above the climb off bottom point
    float off_bottom_height = bottom_pos[Y] + ladder_direction[Y] * s_get_param(CRCD(0x5296e3fd, "ladder_bottom_offset_up"));
	if (preliminary_climb_point[Y] < off_bottom_height) return false;
	
	// insure that the target point on the ladder is below the climb off top point
    float off_top_height = top_pos[Y] - ladder_direction[Y] * s_get_param(CRCD(0x76bf49e0, "ladder_top_offset_up"));
	if (preliminary_climb_point[Y] > off_top_height) return false;
	
	// move the climb point a touch back off the rail
	rail_data.climb_point = preliminary_climb_point - s_get_param(CRCD(0x35b3bbda, "ladder_climb_offset")) * orientation[Z];
	
	// send a feeler to the climb position
	CFeeler feeler;
	feeler.m_start = m_pos;
	feeler.m_end = rail_data.climb_point;
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0xaf90c5fd, "walking_debug_lines")))
	{
		feeler.DebugLine(0, 0, 255, 0);
	}
	#endif
    if (feeler.GetCollision()) return false;
	
	// store the climb facing
	rail_data.climb_facing = orientation[Z];
	
	rail_data.along_rail_factor = along_rail_factor;
	
	mp_core_physics_component->SetFlagTrue(SNAPPED);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::go_ladder_state (   )
{
	if (Mth::Abs((Mth::PI / 2.0f) - Mth::Abs(mp_input_component->GetControlPad().m_leftAngle)) > cosf(Mth::DegToRad(s_get_param(CRCD(0x7ee1b95b, "ladder_control_tolerance")))))
	{
		ladder_movement();
		return;
	}
	
	m_frame_event = CRCD(0xc84243da, "Ladder");
	m_anim_effective_speed = 0.0f;
	return;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::ladder_movement (   )
{
	// movement up and down the ladder
	
	// the desired speed; this will be used to scale the animation speed
	m_anim_effective_speed = s_get_param(CRCD(0xab2db54, "ladder_move_speed")) * m_control_magnitude * cosf(mp_input_component->GetControlPad().m_leftAngle);
	
	// the movement this frame
	float movement = m_anim_effective_speed * m_frame_length;
	
	// get the top rail node; mp_rail_start is always the bottom node
	const CRailNode* p_top_rail_node = mp_rail_start->GetNextLink() ? mp_rail_start->GetNextLink() : mp_rail_start->GetPrevLink();
	Dbg_Assert(p_top_rail_node);
		
	// the offset from the ladder bottom to rail top
	Mth::Vector rail = mp_rail_manager->GetPos(p_top_rail_node) - mp_rail_manager->GetPos(mp_rail_start);
	Dbg_MsgAssert(rail.Length() > s_get_param(CRCD(0x76bf49e0, "ladder_top_offset_up")), ("Ladder too short for ladder animations"));
	
	// the movement in terms of the fraction of the rail
	float delta_factor = movement / rail.Length();
	
	// movement is up or down
	bool up = delta_factor > 0.0f;
	
	// move along the rail
	m_along_rail_factor += delta_factor;
	
	float rail_length = rail.Length();
	
	// see if we've hit the bottom
	float bottom_along_rail_factor = s_get_param(CRCD(0x5296e3fd, "ladder_bottom_offset_up")) / rail_length;
	if (m_along_rail_factor < bottom_along_rail_factor)
	{
		off_ladder_bottom();
		return;
	}
	
	// see if we've hit the top
	float top_along_rail_factor = (rail_length - s_get_param(CRCD(0x76bf49e0, "ladder_top_offset_up"))) / rail_length;
	if (m_along_rail_factor > top_along_rail_factor)
	{
		// we've hit the top
		off_ladder_top();
		return;
	}
	
	// still clamp for now
	m_along_rail_factor = Mth::Clamp(m_along_rail_factor, 0.0f, 1.0f);
	
	if (m_along_rail_factor == 0.0f || m_along_rail_factor == 1.0f)
	{
		m_frame_event = CRCD(0xc84243da, "Ladder");
		m_anim_effective_speed = 0.0f;
	}
	else
	{
		m_frame_event = up ? CRCD(0xaf5abc82, "LadderMoveUp") : CRCD(0xfec9dded, "LadderMoveDown");
	}
	
	// calculate our new position
	m_pos = mp_rail_manager->GetPos(mp_rail_start) + m_along_rail_factor * rail - s_get_param(CRCD(0x35b3bbda, "ladder_climb_offset")) * m_facing;
	DUMP_WPOSITION
	
	// positive animation speed
	m_anim_effective_speed = Mth::Abs(m_anim_effective_speed);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::off_ladder_bottom (   )
{
	Mth::Vector stand_pos = m_pos - s_get_param(CRCD(0xcaf2831b, "ladder_bottom_offset_forward")) * m_facing;
	stand_pos[Y] -= s_get_param(CRCD(0x5296e3fd, "ladder_bottom_offset_up"));
	m_last_ground_feeler_valid = determine_stand_pos(stand_pos, stand_pos, m_last_ground_feeler);
	
	if (m_last_ground_feeler_valid)
	{
		m_ground_normal = m_last_ground_feeler.GetNormal();
	}
	
    // setup an anim wait state
	
	m_anim_wait_initial_pos = m_pos;
	m_anim_wait_goal_pos = stand_pos;
	
    mp_anim_wait_complete_callback = &Obj::CWalkComponent::off_ladder_bottom_complete;
	
	m_drift_initial_display_offset = m_display_offset;
	m_drift_goal_display_offset = 0.0f;
	
	calculate_anim_wait_facing_drift_parameters(m_facing);
	
	m_anim_wait_camera_mode = AWC_GOAL;
	   		  	
	// setup the false wall incase the player jumps out of the animation wait
	m_false_wall.normal = m_facing;
	m_false_wall.cancel_height = m_pos[Y];

	set_state(WALKING_ANIMWAIT);

	m_frame_event = CRCD(0x4db1bbb1, "LadderOffBottom");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::off_ladder_bottom_complete (   )
{
    set_state(WALKING_GROUND);
	
	if (m_last_ground_feeler_valid)
	{
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_ONTO, m_last_ground_feeler);

		// check for a moving contact
		mp_movable_contact_component->CheckForMovableContact(m_last_ground_feeler);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::off_ladder_top (   )
{
	// determine goal position
	
	Mth::Vector stand_pos = m_pos + s_get_param(CRCD(0xb620b580, "ladder_top_offset_forward")) * m_facing; 
	stand_pos[Y] += s_get_param(CRCD(0x76bf49e0, "ladder_top_offset_up"));
	
	CFeeler potential_ground_feeler;
	bool ground = determine_stand_pos(stand_pos, stand_pos, potential_ground_feeler);

	// setup an anim wait state
	
	m_anim_wait_initial_pos = m_pos;
	m_anim_wait_goal_pos = stand_pos;
	
	if (ground)
	{
        mp_anim_wait_complete_callback = &Obj::CWalkComponent::off_ladder_top_to_ground_complete;
		
		m_last_ground_feeler = potential_ground_feeler;
		m_last_ground_feeler_valid = true;
		m_ground_normal = m_last_ground_feeler.GetNormal();
	}
	else
	{
		mp_anim_wait_complete_callback = &Obj::CWalkComponent::off_ladder_top_to_air_complete;
	}
	
	m_drift_initial_display_offset = m_display_offset;
	m_drift_goal_display_offset = 0.0f;
	
	calculate_anim_wait_facing_drift_parameters(m_facing);
	
	m_anim_wait_camera_mode = AWC_GOAL;
	   		  	
	// setup the false wall incase the player jumps out of the animation wait
	m_false_wall.normal = m_facing;
	m_false_wall.cancel_height = stand_pos[Y];
	
	set_state(WALKING_ANIMWAIT);
	
	m_frame_event = CRCD(0x12521bfb, "LadderOffTop");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::off_ladder_top_to_ground_complete (   )
{
    set_state(WALKING_GROUND);
	
	if (m_last_ground_feeler_valid)
	{
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_ONTO, m_last_ground_feeler);

		// check for a moving contact
		mp_movable_contact_component->CheckForMovableContact(m_last_ground_feeler);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWalkComponent::off_ladder_top_to_air_complete (   )
{
    set_state(WALKING_AIR);
	
	m_primary_air_direction = m_facing;
	leave_movable_contact_for_air(m_horizontal_vel, m_vertical_vel);
}

}


================================================
FILE: Code/Gel/Components/animationcomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       AnimationComponent.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/24/2002
//****************************************************************************

#include 

#include 
#include 
#include 

// TODO:  All the network code should
// be at the skater- or the player- level
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
		  
namespace Obj
{
	extern bool DebugSkaterScripts;
	
	// if the animation time increment is too small,
	// then ignore it...
	const float vMIN_SPEED_THRESHOLD = 0.05f;

	#define nxANIMCOMPONENTFLAGS_FLIPPED				(1<<29)
	
	// maximum number of degenerate animations
	const int vNUM_DEGENERATE_ANIMS = 3;

	// maximum number of procedural bones
	const int vMAXPROCEDURALBONES = 12;
	
	static bool s_updating_channels = false;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This static function is what is registered with the component
// factory object, (currently the CCompositeObjectManager) 
CBaseComponent* CAnimationComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CAnimationComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAnimationComponent::CAnimationComponent() : CBaseComponent()
{
	SetType( CRC_ANIMATION );

	m_animScriptName = 0;
	m_animEventTableName = 0;

	m_shouldBlend = false;

	Reset();

	m_numProceduralBones = 0;

	mp_proceduralBones = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CAnimationComponent::~CAnimationComponent()
{
	destroy_blend_channels();
	
	if ( mp_proceduralBones )
	{
		delete[] mp_proceduralBones;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::Reset()
{
	// stops all the animations...
	destroy_blend_channels();

	// resets "global" blend parameters
	mGotBlendPeriodOut = false;
	mBlendPeriodOut = 0.0f;
	
	// reset script animation waits
	if (m_animation_script_block_active)
	{
		m_animation_script_block_active = false;
		if (GetObject()->GetScript())
		{
			GetObject()->GetScript()->UnBlock();
		}
	}
	m_animation_script_unblock_point = 0;
	
	// reset animation frame count
	m_last_animation_time = 0.0f;
	
	m_dont_interrupt=false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::SetAnims( uint32 anim_checksum )
{
    m_animScriptName = anim_checksum;

	PlayPrimarySequence( 0, false, 0.0f, 1000.0f, Gfx::LOOPING_HOLD, 0.3f, 1.0f );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CAnimationComponent::InitFromStructure( Script::CStruct* pParams )
{
	Dbg_Assert( pParams );

	pParams->GetChecksum( CRCD(0x5b8c6dc2,"animEventTableName"), &m_animEventTableName, false );

	uint32 animScriptName;

	if ( pParams->GetChecksum( CRCD(0x6c2bfb7f,"animName"), &animScriptName, false ) )
	{
		SetAnims( animScriptName );
		Reset();

		// safety-check to make sure he doesn't start out in the blair witch position
		// (because of our animation LOD-ing system)
		// this must be done before the models get initialized?
		uint32 defaultAnimName;
		if ( pParams->GetChecksum( CRCD(0xeae64b47,"defaultAnimName"), &defaultAnimName, false ) )
		{
			PlaySequence(defaultAnimName);
			SetLoopingType( Gfx::LOOPING_CYCLE, true );
		}
		else
		{
			// generally, the blair witch position
			PlaySequence( CRCD(0x1ca1ff20,"default") );
		}
	}

	// if it's the local skater, then give it some procedural animation as well
	// (the decision to do this should really be coming from a higher-level...)
 	if ( GetObject()->GetID() == 0 )
	{
		Dbg_MsgAssert( mp_proceduralBones == NULL, ( "Already has procedural bones!" ) );
		mp_proceduralBones = new Gfx::CProceduralBone[vMAXPROCEDURALBONES];

		Script::CStruct* pTempParams = new Script::CStruct;
		pTempParams->AppendStructure( pParams );

		pTempParams->AddChecksum( CRCD(0x7321a8d6,"type"), CRCD(0xfdf0436c,"ProceduralAnim") );
		pTempParams->AddChecksum( CRCD(0x3ed7262b,"bone_list"), CRCD(0x5d5c0e72,"procedural_skater_bones") );
		
		initialize_procedural_bones( pTempParams );

		delete pTempParams;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::ToggleFlipState( void )								   
{
	// Flip the animation to the correct orientation
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
	Net::Client* client = gamenet_man->GetClient( 0 );
	this->FlipAnimation( GetObject()->GetID(), !IsFlipped(), client->m_Timestamp, true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CAnimationComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
    switch ( Checksum )
    {        
        // @script | Obj_EnableAnimBlending | 
        // @parm name | enabled | whether the anim blending should be enabled
		// peds should disable blending, as a speed optimization
		case 0x98a32669:	// Obj_EnableAnimBlending
		{
			int shouldBlend;
			pParams->GetInteger( CRCD(0xaf06447b,"enabled"), &shouldBlend, Script::ASSERT );
			EnableBlending( shouldBlend );
		}
		break;
        
		// @script | Obj_SetAnimCycleMode | 
        // @flag off | turn cycle off (otherwise it will turn it on)
		case 0x58c52f64:	// Obj_SetAnimCycleMode
        {
           if ( pParams->ContainsFlag( CRCD(0xd443a2bc,"off") ) )
           {
               SetLoopingType( Gfx::LOOPING_HOLD, true );
           }
           else
           {
               // maybe we should clear the cycle time or num loops?
               // or should we leave it at whatever was set before?  hmmm...
               SetLoopingType( Gfx::LOOPING_CYCLE, true );
           }
        }
        break;

        // @script | Obj_PlayAnim | 
        // @parm name | Anim | animation to play
        // @flag wobble | enable wobble
        // @parmopt float | BlendPeriod | 0.3f | blend period (default may change)
        // @parmopt int | BlendPeriodPercent | | blend period as a percentage of the anim length (20 means "20 percent")
        // @parmopt float | speed | 1.0 | 
        // @flag NoRestart | 
        // @flag Cycle | 
        // @flag PingPong | 
        // @flag Wobble | 
        // @flag DontInterrupt | Makes further PlayAnims have no effect until the current
		// animation has finished.
        // @parmopt name | From | | start, end, or current
        // @parmopt int | From | | from can be specified as an int (60ths)
        // @parmopt name | To | | start, end, or current
        // @parmopt int | To | | to can be specified as an int (60ths)
        // @flag Backwards | play animation backwards
		case 0x2cb9ad09:	// Obj_PlayAnim
		case 0x0b1e7291:	// PlayAnim
        {
			PlayAnim( pParams, pScript );
        
			Gfx::CBlendChannel* pPrimaryChannel = get_primary_channel();
			if ( pPrimaryChannel && pPrimaryChannel->GetLoopingType() == Gfx::LOOPING_WOBBLE )
			{
				// the skater balance trick needs to do some extra stuff to
				// handle wobbles, but needs to do it after the anim has been
				// launched.  the following gives any component a chance to
				// handle the wobble first...  if it is not handled by the
				// time it gets to the animation component, then it's ignored
				// (this works for the case of the skater balance trick, because
				// it comes early in the component list)
				GetObject()->CallMemberFunction( CRCD(0xea6d0efd,"SetWobbleDetails"), pParams, pScript );
			}
		}
		break;

		case 0xea6d0efd:	// SetWobbleDetails
			// this is a special case to make sure that the skater balance
			// trick component gets a chance to handle wobbles, if such
			// a component exists.
			return MF_TRUE;
        
        // @script | Obj_AnimComplete | returns true when the object
        // is done playing an anim.  This is only valid with "hold on
		// last frame" animations.	
		case 0x2889c6c9:	// Obj_AnimComplete
		case 0x76cc99d5: 	// AnimFinished	
			return ( IsAnimComplete() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE );
			break;

        // @script | Obj_AnimEquals | True if specified animation is
        // the one currently playing on the object, False otherwise
        // @uparmopt name | single animation name to check
        // @uparmopt [] | array of animation names to check
		case 0xb5fb7eb6:  	// Obj_AnimEquals
		case 0x1a0f9646:	// AnimEquals
			return ( AnimEquals( pParams, pScript ) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE );
			break;

        // @script | Obj_GetAnimSpeed | 
        // the one currently playing on the object
        case 0x5195bcbb: // Obj_GetAnimSpeed
        {
            pScript->GetParams()->AddFloat(CRCD(0xf0d90109,"speed"), GetAnimSpeed( pParams, pScript ));
        }
        break;
            
        // @script | BlendPeriodOut | next call to playanim will use this blend period
        // no matter what is specified in the actual call to playanim
        // @uparm 1.0 | blend period out value
		case 0x68c86aec:	// BlendPeriodOut	
		{
			if (!pParams->GetFloat(NONAME,&mBlendPeriodOut))
            {
				Dbg_MsgAssert(0,("\n%s\nBlendPeriodOut requires a floating point value",pScript->GetScriptInfo()));
			}
			// Set the flag so that PlayAnim will use mBlendPeriodOut instead next time.
			mGotBlendPeriodOut=true;	
		}
		break;
        
        // @script | LoopingAnim | true if the current animation will loop forever
		case 0x80fdbdf2:	// LoopingAnim	
			{
				return IsLoopingAnim() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			}
			break;
			
		case 0x42ffc3ce: // GetAnimLength
		{
			#ifdef __USER_DAN__
			printf("GetAnimLength\n");
			Script::PrintContents(pParams);
			#endif
			
			uint32 anim_checksum=0;
			pParams->GetChecksum(CRCD(0x98549ba4, "anim"), &anim_checksum);
			
			if (AnimExists(anim_checksum))
			{
				pScript->GetParams()->AddFloat(CRCD(0xfe82614d, "Length"), fabs(AnimDuration(anim_checksum)));
			}
			break;
		}
		
        // @script | FrameIs | checks if the current frame is equal
        // to the specified frame
        // @uparmopt 1 | frame number
		case 0x922e7d14: // FrameIs
		{
			#ifdef __USER_DAN__
			printf("FrameIs\n");
			Script::PrintContents(pParams);
			#endif
			
			float Start,Current,End;
			GetPrimaryAnimTimes(&Start,&Current,&End);
			
			float t=0;
			pParams->GetFloat(NO_NAME,&t);

			t/=60.0f;
			
			float new_frame=Current-Start;
			float old_frame=m_last_animation_time-Start;

			if (t==new_frame)
			{
				return CBaseComponent::MF_TRUE;
			}
			
			if (old_frameGetFloat(NO_NAME,&t);

			if (pParams->ContainsFlag(0x4a07c332/*"frame"*/) || pParams->ContainsFlag(0x19176c5/*"frames"*/))
			{
				t/=60.0f;
			}
			else if (pParams->ContainsFlag(0x9e497fc6/*"percent"*/))
			{
				t=Start+(End-Start)*t/100.0f; // Dodgy?
			}	
			else if (pParams->ContainsFlag(0x49e0ee96/*"second"*/) || pParams->ContainsFlag(0xd029f619/*"seconds"*/))
			{
				// t is in seconds, so nothing to do.
			}
			else
			{
				// If they did not specify any of the above, then t is in milliseconds, so convert to seconds.
				t/=1000.0f;
			}
			

			if (pParams->ContainsFlag(0x91a4c826/*"relative"*/))	
			{
				if (Start<=End)
				{
					m_animation_script_unblock_point=Current+t;
				}
				else
				{
					m_animation_script_unblock_point=Current-t;
				}	
			}
			else if (pParams->ContainsFlag(0xe18cd075/*"fromend"*/))	
			{
				if (Start<=End)
				{
					m_animation_script_unblock_point=End-t;
				}
				else
				{
					m_animation_script_unblock_point=Start+t;
				}	
			}
			else
			{
				m_animation_script_unblock_point=t;
			}
			
//			Dbg_MsgAssert( m_animation_script_unblock_point>=Start && m_animation_script_unblock_point<=End, ( "WaitAnim time %f out of range of anim (%f %f) in %s", m_animation_script_unblock_point, Start, End, pScript->GetScriptInfo() ) );

			if ( !GetObject()->GetScript() )
			{
				GetObject()->SetScript(new Script::CScript);
			}

			if ((Start<=End && Current>=m_animation_script_unblock_point) || (Start>=End && Current<=m_animation_script_unblock_point))
			{
				GetObject()->GetScript()->UnBlock();
				m_animation_script_block_active=false;
			}	
			else
			{
				GetObject()->GetScript()->Block();
				m_animation_script_block_active=true;
			}	
			
			return CBaseComponent::MF_TRUE;
		}	

		case 0x5f495ae0:	// InvalidateCache
		{
			int num_channels = m_blendChannelList.CountItems();
			Gfx::CBlendChannel* pChannel = (Gfx::CBlendChannel*)m_blendChannelList.GetNext();
			for ( int i = 0; i < num_channels; i++ )
			{
				pChannel->InvalidateCache();
				pChannel = (Gfx::CBlendChannel*)pChannel->GetNext();
			}
		}
		break;

        // @script | Obj_WaitAnimFinished | wait for animation to complete
		case 0xb628a959:  // Obj_WaitAnimFinished
			pScript->SetWait(Script::WAIT_TYPE_OBJECT_ANIM_FINISHED,this);
			break;
			

		// @script | AnimExists | returns true if the given anim exists
		// @param name | animation to look for
		case CRCC(0x9069f357, "AnimExists"):
		{
			uint32 anim_name;
			pParams->GetChecksum(NO_NAME, &anim_name);
			return AnimExists(anim_name) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE; 
		}

		case 0x83654874: // AddAnimController
		case 0x02565bd1: //	SetBoneTransMin
		case 0x3e5b6488: //	SetBoneTransMax
		case 0xc85b9ac0: //	SetBoneTransSpeed
		case 0x5ee9d5bd: //	SetBoneTransCurrent
		case 0x5661fb72: //	SetBoneTransActive
		case 0xa47767f2: //	SetBoneRotMin
		case 0x987a58ab: //	SetBoneRotMax
		case 0x599d3707: //	SetBoneRotSpeed
		case 0x7235daa7: //	SetBoneRotCurrent
		case 0x53f06acc: //	SetBoneRotActive
		case 0xc6889e91: //	SetBoneScaleMin
		case 0xfa85a1c8: //	SetBoneScaleMax
		case 0xd32c2724: //	SetBoneScaleSpeed
		case 0xd12b3713: //	SetBoneScaleCurrent
		case 0xf11daaae: //	SetBoneScaleActive
		case 0xd63a1b81: // GetPartialAnimParams
		case 0xbd4edd44: // SetPartialAnimSpeed
		case 0x6aaeb76f: // IncrementPartialAnimTime
		case 0xf5e2b871: // ReversePartialAnimDirection
			{
				Gfx::CBlendChannel* pPrimaryChannel = get_primary_channel();
				if ( pPrimaryChannel )
				{
					return pPrimaryChannel->CallMemberFunction( Checksum, pParams, pScript ) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
				}
			}
			break;
		
		case 0x986d274e: // RemoveAnimController
			{
				Gfx::CBlendChannel* pChannel = (Gfx::CBlendChannel*)m_blendChannelList.GetNext();
				while ( pChannel )
				{
					pChannel->CallMemberFunction( Checksum, pParams, pScript );
					pChannel = (Gfx::CBlendChannel*)pChannel->GetNext();
				}
				return CBaseComponent::MF_TRUE;
			}
			break;

		// @script | Obj_Flip | 
		case 0x27a98022:  // Obj_Flip
			ToggleFlipState( );
			break;
		
		// @script | Obj_AnimationFlipped | 
		case 0x6eceb234:  // Obj_AnimationFlipped
			return ( m_flags & nxANIMCOMPONENTFLAGS_FLIPPED ) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;
		
		default:
            return CBaseComponent::MF_NOT_EXECUTED;

    }
    
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::AddAnimController( Script::CStruct* pParams )
{
	Gfx::CBlendChannel* pPrimaryChannel = get_primary_channel();
	if ( pPrimaryChannel )
	{
		pPrimaryChannel->AddController( pParams );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::ProcessWait( Script::CScript * pScript )
{
	switch (pScript->GetWaitType())
	{
		case Script::WAIT_TYPE_OBJECT_ANIM_FINISHED:
			if (IsAnimComplete())
			{
				pScript->ClearWait();
			}
			break;
		default:
			Dbg_MsgAssert(0,("\n%s\nWait type of %d not supported by CAnimationComponent",pScript->GetScriptInfo(),pScript->GetWaitType()));
			break;
	}		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::GetDebugInfo( Script::CStruct* p_info )
{

#ifdef	__DEBUG_CODE__

	Dbg_MsgAssert( p_info, ( "NULL p_info sent to CAnimationComponent::GetDebugInfo" ) );

	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);
	
	p_info->AddInteger( CRCD(0x87ed8a3f,"m_shouldblend"), m_shouldBlend );
	p_info->AddInteger( CRCD(0xa2f4a5ea,"mGotBlendPeriodOut"), mGotBlendPeriodOut );
	p_info->AddFloat( CRCD(0xd0902723,"mBlendPeriodOut"), mBlendPeriodOut );
	p_info->AddChecksum( CRCD(0xc2fe9f39,"m_animScriptName"), m_animScriptName );
	p_info->AddInteger(CRCD(0x9fdd4257,"m_dont_interrupt"),m_dont_interrupt);
	
	Script::CStruct* p_tempParams = new Script::CStruct;
	p_tempParams->Clear();
	Gfx::CBlendChannel* pPrimaryChannel = get_primary_channel();
	if ( pPrimaryChannel )
	{
		pPrimaryChannel->GetDebugInfo( p_tempParams );
		p_info->AddStructure( CRCD(0x969f358c,"primary_channel"), p_tempParams );
	}

	delete p_tempParams;
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::CBlendChannel* CAnimationComponent::get_primary_channel()
{
	// just grab the first item from the list...
	Gfx::CBlendChannel* pBlendChannel = NULL;
	pBlendChannel = (Gfx::CBlendChannel*)m_blendChannelList.GetNext();
	return pBlendChannel;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::pack_degenerate_channels()
{
	// this removes all the channels that have expired...

	Gfx::CBlendChannel* pChannel = (Gfx::CBlendChannel*)m_blendChannelList.GetNext();
	while ( pChannel )
	{
		Gfx::CBlendChannel* pNext = (Gfx::CBlendChannel*)pChannel->GetNext();
		
		if ( !pChannel->IsActive() )
		{
			Dbg_MsgAssert(pChannel != get_primary_channel(), ("Removing primary channel"));
			Dbg_MsgAssert( !s_updating_channels, ( "Someone is trying to remove an animation channel while in channel update loop" ) );
			pChannel->Remove();
			delete pChannel;
		}
		
		pChannel = pNext;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::create_new_blend_channel( float blend_period )
{
	// If there	are too many channels, get rid of the tail channels
	Gfx::CBlendChannel* pChannel = (Gfx::CBlendChannel*)m_blendChannelList.GetNext();//Item( 0 );
	int channelCount = 0;

	while ( pChannel )
	{
		channelCount++;

		Gfx::CBlendChannel* pNext = (Gfx::CBlendChannel*)pChannel->GetNext();
		
		if ( channelCount >= vNUM_DEGENERATE_ANIMS )
		{
			// remove the tail items
			pChannel->Remove();
			Dbg_MsgAssert( !s_updating_channels, ( "Someone is trying to remove an animation channel while in channel update loop" ) );
			delete pChannel;
		}

		pChannel = pNext;
	}
		
	// degenerate the existing blend channels
	// make the first channel degenerate
	Gfx::CBlendChannel* pBlendChannel = m_blendChannelList.CountItems() ? get_primary_channel() : NULL;//	(Gfx::CBlendChannel*)m_blendChannelList.GetItem( 0 );
	if ( pBlendChannel )
	{
		if ( pBlendChannel->Degenerate( blend_period ) )
		{
			// degeneration worked, so we want to do this cumulative
			// blend value thing (not really sure what it is though)
			float cumulative_blend_value = 0.0f;
			Gfx::CBlendChannel* pOtherChannels = (Gfx::CBlendChannel*)pBlendChannel->GetNext();
			while ( pOtherChannels )
			{
				// if( !pOtherChannels->IsActive() )
				if( pOtherChannels->IsDegenerating() )
				{
					cumulative_blend_value += 
						pOtherChannels->GetDegenerationTime() 
						* pOtherChannels->GetDegenerationTimeToBlendMultiplier();
				}
				pOtherChannels = (Gfx::CBlendChannel*)pOtherChannels->GetNext();
			}
		
			float blend_value1 = 1.0f - ( cumulative_blend_value );
			pBlendChannel->SetDegenerationTimeToBlendMultiplier( blend_value1 / pBlendChannel->GetDegenerationTime() );
		}
	}
	
	// now add a new channel to the front of the list
	Gfx::CBlendChannel* pPrimaryChannel = new Gfx::CBlendChannel( GetObject() );
	m_blendChannelList.AddToHead( pPrimaryChannel );
	Dbg_MsgAssert( !s_updating_channels, ( "Someone is trying to add an animation channel while in channel update loop" ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAnimationComponent::AnimEquals( Script::CStruct *pParams, Script::CScript *pScript )
{	
	uint32 current_animation=GetCurrentSequence();
	
	Script::CComponent *p_comp=NULL;
	while (true)
	{
		p_comp=pParams->GetNextComponent(p_comp);
		if (!p_comp)
		{
			break;
		}

		if (p_comp->mNameChecksum==0)
		{
			// It's an unnamed component
			
			Script::CArray *p_array=NULL;
			if (p_comp->mType==ESYMBOLTYPE_NAME)
			{
				// It's an unnamed name. Maybe it's the name of a global array ...
				Script::CSymbolTableEntry *p_entry=Script::Resolve(p_comp->mChecksum);
				if (p_entry && p_entry->mType==ESYMBOLTYPE_ARRAY)
				{
					// It is a global array
					p_array=p_entry->mpArray;
				}
				else
				{
					// Nope, it's just a name, so see if it is the name of the current animation.
					if (p_comp->mChecksum==current_animation)
					{
						return true;
					}	
				}
			}
			else if (p_comp->mType==ESYMBOLTYPE_ARRAY)
			{
				p_array=p_comp->mpArray;
			}
			
			if (p_array)
			{
				Dbg_MsgAssert(p_array->GetType()==ESYMBOLTYPE_NAME,("\n%s\nAnimEquals: Array must be of names",pScript->GetScriptInfo()));
				for (uint32 i=0; iGetSize(); ++i)
				{
					if (p_array->GetChecksum(i)==current_animation)
					{
						return true;
					}
				}
			}
		}		
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CAnimationComponent::AnimDuration( uint32 checksum )
{
	Gfx::CBonedAnimFrameData *p_anim = find_actual_anim( checksum );

    Dbg_MsgAssert( p_anim, ( "Trying to get duration on an animation that doesn't exist %s %s %s", 
							 Script::FindChecksumName(m_animScriptName), 
							 Script::FindChecksumName(checksum),
							 Script::FindChecksumName(GetObject()->GetID()) ) );

	return ( p_anim->GetDuration() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAnimationComponent::AnimExists( uint32 checksum )
{
	Gfx::CBonedAnimFrameData *p_anim = find_actual_anim( checksum );

	return p_anim;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::AddTime( float incVal )
{
	Gfx::CBlendChannel* pPrimaryChannel = get_primary_channel();
	if ( pPrimaryChannel )
	{
		pPrimaryChannel->AddTime( incVal );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::CBonedAnimFrameData* CAnimationComponent::find_actual_anim( uint32 checksum )
{
	// need to combine the animation name (Idle) 
	// with the owner animscript (animload_thps5_human)
	// to get the asset name
	return Nx::GetCachedAnim( m_animScriptName + checksum, false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::PrintStatus()
{
	// for debugging the viewer object
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::Update()
{
	if ( Gfx::CBlendChannel* primary_channel = get_primary_channel() )
	{
		m_last_animation_time = primary_channel->GetCurrentAnimTime();
		
		/*
		float s, c, e;
		primary_channel->GetAnimTimes(&s, &c, &e);
		printf("Animation Factor Complete = %f\n", c / (e - s));
		*/
	}
	
	if (m_animation_script_block_active)
	{
		if ( !GetObject()->GetScript() )
		{
			GetObject()->SetScript( new Script::CScript );
		}
		
		// The script should be blocked at this point, if it isn't that must
		// be because the script just got reloaded. So clear the m_animation_script_block_active
		// flag so that it doesn't get stuck on forever.
		if ( !GetObject()->GetScript()->getBlocked() )
		{
			m_animation_script_block_active = false;
		}	
		else
		{
			float start, current, end;
			GetPrimaryAnimTimes( &start, ¤t, &end);
			
			if ( ( start <= end && current >= m_animation_script_unblock_point ) || ( start >= end && current <= m_animation_script_unblock_point ) )
			{
				GetObject()->GetScript()->UnBlock();
				m_animation_script_block_active = false;
			}	
		}	
	}

	// Alwyas update the channels

	s_updating_channels = true;

	int num_channels = m_blendChannelList.CountItems();
	Gfx::CBlendChannel* pChannel = (Gfx::CBlendChannel*)m_blendChannelList.GetNext();
	for ( int i = 0; i < num_channels; i++ )
	{
		pChannel->Update();
		pChannel = (Gfx::CBlendChannel*)pChannel->GetNext();
	}

	s_updating_channels = false;

	// remove channels which have decided they are complete
	pack_degenerate_channels();

	// (Mick) If there is a suspend component
	// then ask it if we should animate
	
	// This call determines whether the object is sufficiently far away that no animation is disabled,
	// or possibly at an intermediate distance, interleaved. The distance from parent object to camera is cached
	// for subsequent animation LOD calculations.
	Dbg_Assert(mp_suspend_component);
	bool animate = mp_suspend_component->should_animate( &m_parent_object_dist_to_camera );
	if ( animate )
	{	
		update_procedural_bones();
		
		// This call determines whether the object is actually on screen; animation is not requried
		// for offscreen objects.
		if ( ShouldAnimate() )
		{
			update_skeleton();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::UpdateSkeleton()
{
	// don't use the animation cache, because
	// the viewer object doesn't call Update(),
	// which normally is responsible for
	// invalidating the cache at the
	// appropriate times
	Gfx::CBlendChannel* pPrimaryChannel = get_primary_channel();
	if ( pPrimaryChannel )
	{
		pPrimaryChannel->InvalidateCache();
	}

	// allows the viewer object to update the skeleton,
	// outside of the normal Update() function...
	// (the viewer object sometimes suspends the
	// component and updates the time manually,
	// in which case we'd still need to apply
	// the animation to the skeleton...)
	update_skeleton();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAnimationComponent::IsAnimComplete( void )
{
	if ( !get_primary_channel() )
	{
		return false;
	}

    return get_primary_channel()->IsAnimComplete();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAnimationComponent::IsLoopingAnim( void )
{
	if ( !get_primary_channel() )
	{
		return false;
	}

    return get_primary_channel()->IsLoopingAnim();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CAnimationComponent::GetCurrentSequence( void )
{
	if ( !get_primary_channel() )
	{
		return 0;
	}

    return get_primary_channel()->GetCurrentAnim();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
					  
void CAnimationComponent::GetPrimaryAnimTimes(float *pStart, float *pCurrent, float *pEnd)
{
	*pStart = 0.0f;
	*pCurrent = 0.0f;
	*pEnd = 0.0f;

	if ( get_primary_channel() )
	{
		get_primary_channel()->GetAnimTimes( pStart, pCurrent, pEnd );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CAnimationComponent::GetCurrentAnimTime( void )
{
	if ( !get_primary_channel() )
	{
		return 0.0f;
	}

    return get_primary_channel()->GetCurrentAnimTime();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::PlaySequence( uint32 checksum, float BlendPeriod )
{
#ifdef __NOPT_ASSERT__
	if ( !AnimExists( checksum ) )
	{
		Dbg_Message( "Couldn't find anim %s\n", Script::FindChecksumName(checksum) );
	}
#endif

	PlayPrimarySequence( checksum, false, 0.0f, AnimDuration(checksum), Gfx::LOOPING_HOLD, BlendPeriod, 1.0f );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::PlayPrimarySequence( uint32 animName, bool propagate, float start_time, float end_time, Gfx::EAnimLoopingType loop_type, float blend_period, float speed )
{
	if ( animName == 0 )
	{
		animName = CRCD(0x1ca1ff20,"default");
	}

	if ( propagate )   
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		GameNet::PlayerInfo* player;
		
		player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID() );
		if ( player && player->IsLocalPlayer())
		{
			GameNet::MsgPlayPrimaryAnim	anim_msg;
			Net::MsgDesc msg_desc;
			char msg[ Net::Manager::vMAX_PACKET_SIZE ];
			char* stream;
			int size;
			Net::Client* client;
                    
			client = gamenet_man->GetClient( player->GetSkaterNumber());
			Dbg_Assert( client );

			//anim_msg.m_Time = client->m_Timestamp;
            anim_msg.m_Index = animName;
			anim_msg.m_ObjId = GetObject()->GetID();
			anim_msg.m_LoopingType = loop_type;
			anim_msg.m_StartTime = (unsigned short )( start_time * 4096.0f );
			anim_msg.m_EndTime = ((unsigned short )( end_time * 4096.0f ));
			anim_msg.m_BlendPeriod = (unsigned short )( blend_period * 4096.0f );
			anim_msg.m_Speed = (unsigned short )( speed * 4096.0f );

			stream = msg;
			//memcpy( stream, &anim_msg.m_Time, sizeof( unsigned int ));
			//stream += sizeof( int );
			*stream++ = anim_msg.m_LoopingType;
			*stream++ = anim_msg.m_ObjId;
			memcpy( stream, &anim_msg.m_Index, sizeof( uint32 ));
			stream += sizeof( uint32 );
			memcpy( stream, &anim_msg.m_StartTime, sizeof( unsigned short ));
			stream += sizeof( unsigned short );
			memcpy( stream, &anim_msg.m_EndTime, sizeof( unsigned short ));
			stream += sizeof( unsigned short );
			memcpy( stream, &anim_msg.m_BlendPeriod, sizeof( unsigned short ));
			stream += sizeof( unsigned short );
			memcpy( stream, &anim_msg.m_Speed, sizeof( unsigned short ));
			stream += sizeof( unsigned short );

			size = (unsigned int) stream - (unsigned int) msg;
            
			msg_desc.m_Data = msg;
			msg_desc.m_Length = size;
			msg_desc.m_Id = GameNet::MSG_ID_PRIM_ANIM_START;
			client->EnqueueMessageToServer( &msg_desc );
		}
	}
    
	// GJ:  Should this be broken up into two commands/
	// to degenerate the old, and start playing the new?

//	printf( "Playing anim %s\n", Script::FindChecksumName( animName ), blend_period );
    
#ifdef __NOPT_ASSERT__
	// Define this to disable blending
	if ( Script::GetInteger( CRCD(0xf098f123,"disable_blending"), Script::NO_ASSERT ) )
	{
		blend_period = 0.0f;
	}
#endif

	if ( blend_period == 0.0f || !ShouldBlend() )
	{
		destroy_blend_channels();
	}

	create_new_blend_channel( blend_period );

	Gfx::CBlendChannel* pPrimaryChannel = get_primary_channel();
	Dbg_Assert( pPrimaryChannel );
    pPrimaryChannel->PlaySequence( animName, start_time, end_time, loop_type, blend_period, speed, IsFlipped() );

	delete_anim_tags();
	add_anim_tags( animName );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::delete_anim_tags()
{
	Script::CStruct* pTags = GetObject()->GetTags();
	if ( pTags )
	{
		pTags->RemoveComponent( CRCD(0x5db4115f,"AnimTags") );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::add_anim_tags( uint32 animName )
{
	Script::CStruct* pAnimTagTable = Script::GetStructure(CRCD(0x95807202,"AnimTagTable"), Script::ASSERT);
	if ( pAnimTagTable )
	{
		Script::CStruct* pSubStruct;
		if ( pAnimTagTable->GetStructure( animName, &pSubStruct, Script::NO_ASSERT ) )
		{
			Script::CStruct* pTempStruct = new Script::CStruct;

			// add a new anim tags structure to the tags
			Script::CStruct* pTags = new Script::CStruct;
			pTags->AppendStructure( pSubStruct );
			pTempStruct->AddStructurePointer(CRCD(0x5db4115f,"AnimTags"), pTags);

			GetObject()->SetTagsFromScript( pTempStruct );

			delete pTempStruct;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::SetWobbleTarget( float alpha, bool propagate )
{
	if ( propagate )
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		GameNet::PlayerInfo* player;
		static unsigned char s_last_alpha = 255;
		
		player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID() );
		if ( player && player->IsLocalPlayer())
		{
			Net::Client* client;
			GameNet::MsgSetWobbleTarget msg;

			client = gamenet_man->GetClient( player->GetSkaterNumber() );
			Dbg_Assert( client );

			if( alpha < 0 )
			{ 
				alpha = 0;
			}
			else if( alpha > 1 )
			{
				alpha = 1;
			}
			
			//msg.m_Time = client->m_Timestamp;
			msg.m_Alpha = (unsigned char ) ( alpha * 255.0f );
			msg.m_ObjId = GetObject()->GetID();
			
			if( s_last_alpha != msg.m_Alpha )
			{
				Net::MsgDesc msg_desc;

				msg_desc.m_Data = &msg;
				msg_desc.m_Length = sizeof( GameNet::MsgSetWobbleTarget );
				msg_desc.m_Id = GameNet::MSG_ID_SET_WOBBLE_TARGET;
				msg_desc.m_Singular = true;
				client->EnqueueMessageToServer( &msg_desc );
				s_last_alpha = msg.m_Alpha;
			}
		}
	}
	
	if ( get_primary_channel() )
	{
		Script::CStruct* pTempParams = new Script::CStruct;

		pTempParams->AddFloat( CRCD(0x4d747fa0,"wobbletargetalpha"), alpha );

		get_primary_channel()->CallMemberFunction( CRCD(0xd0209498,"setwobbletarget"), pTempParams, NULL );

		delete pTempParams;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static int s_get_wobble_mask( int variable, float value )
{   
	int mask;

	mask = 0;
	switch( variable )
	{
		case GameNet::MsgSetWobbleDetails::vWOBBLE_AMP_A:
			if( value == 0.05f )
			{
				mask = 0;
			}
			else if( value == 0.1f )
			{
				mask = 1;
			}
			else if( value == 10.1f )
			{
				mask = 2;
			}
			else
			{
				Dbg_Printf( "value is %f\n", value );
				Dbg_Assert( 0 );
			}
			break;
		case GameNet::MsgSetWobbleDetails::vWOBBLE_AMP_B:
			if( value == 0.04f )
			{
				mask = 0;
			}
			else if( value == 10.1f )
			{
				mask = 1;
			}
			else
			{
				Dbg_Printf( "VARIABLE: %d Value %f\n", variable, value );
				Dbg_Assert( 0 );
			}
			break;
		case GameNet::MsgSetWobbleDetails::vWOBBLE_K1:
			if( value == 0.0022f )
			{
				mask = 0;
			}
			else if( value == 20.0f )
			{
				mask = 1;
			}
			else
			{
				Dbg_Printf( "value is %f\n", value );
				Dbg_Assert( 0 );
			}
			break;
		case GameNet::MsgSetWobbleDetails::vWOBBLE_K2:
			if( value == 0.0017f )
			{
				mask = 0;
			}
			else if( value == 10.0f )
			{
				mask = 1;
			}
			else
			{
				Dbg_Assert( 0 );
			}
			break;
		case GameNet::MsgSetWobbleDetails::vSPAZFACTOR:
			if( value == 1.5f )
			{
				mask = 0;
			}
			else if( value == 1.0f )
			{
				mask = 1;
			}
			else
			{
				Dbg_Printf( "value is %f\n", value );
				Dbg_Assert( 0 );
			}
			break;
		default:
			Dbg_Assert( 0 );
			break;
	}
	
	return mask;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::SetWobbleDetails( const Gfx::SWobbleDetails& wobble_details, bool propagate )
{
	if ( propagate )
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		GameNet::PlayerInfo* player;

		player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID() );
		if ( player && player->IsLocalPlayer())
		{
			Net::Client* client;
			GameNet::MsgSetWobbleDetails msg;
			int mask;
			static char s_last_mask = -1;
			
			client = gamenet_man->GetClient( player->GetSkaterNumber() );
			Dbg_Assert( client );

			//msg.m_Time = client->m_Timestamp;
			
			msg.m_WobbleDetails = 0;
			mask = s_get_wobble_mask( GameNet::MsgSetWobbleDetails::vWOBBLE_AMP_A, wobble_details.wobbleAmpA );
			msg.m_WobbleDetails |= mask;
			mask = s_get_wobble_mask( GameNet::MsgSetWobbleDetails::vWOBBLE_AMP_B, wobble_details.wobbleAmpB );
			msg.m_WobbleDetails |= ( mask << 2 );
			mask = s_get_wobble_mask( GameNet::MsgSetWobbleDetails::vWOBBLE_K1, wobble_details.wobbleK1 );
			msg.m_WobbleDetails |= ( mask << 3 );
			mask = s_get_wobble_mask( GameNet::MsgSetWobbleDetails::vWOBBLE_K2, wobble_details.wobbleK2 );
			msg.m_WobbleDetails |= ( mask << 4 );
			mask = s_get_wobble_mask( GameNet::MsgSetWobbleDetails::vSPAZFACTOR, wobble_details.spazFactor );
			msg.m_WobbleDetails |= ( mask << 5 );
			msg.m_ObjId = GetObject()->GetID();

			// Only propagate if it's actually different from our last wobble details message
			if( s_last_mask != msg.m_WobbleDetails )
			{
				Net::MsgDesc msg_desc;

				msg_desc.m_Data = &msg;
				msg_desc.m_Length = sizeof( GameNet::MsgSetWobbleDetails );
				msg_desc.m_Id = GameNet::MSG_ID_SET_WOBBLE_DETAILS;
				msg_desc.m_Singular = true;

				client->EnqueueMessageToServer( &msg_desc );

				s_last_mask = msg.m_WobbleDetails;
			}
		}
	}
	
	if ( get_primary_channel() )
	{
		Script::CStruct* pTempParams = new Script::CStruct;

		pTempParams->AddFloat( CRCD(0xfd266a26,"wobbleAmpA"), wobble_details.wobbleAmpA );
		pTempParams->AddFloat( CRCD(0x642f3b9c,"wobbleAmpB"), wobble_details.wobbleAmpB );
		pTempParams->AddFloat( CRCD(0x0f43fd49,"wobbleK1"), wobble_details.wobbleK1 );
		pTempParams->AddFloat( CRCD(0x964aacf3,"wobbleK2"), wobble_details.wobbleK2 );
		pTempParams->AddFloat( CRCD(0xf90b0824,"spazFactor"), wobble_details.spazFactor );

 		get_primary_channel()->CallMemberFunction( CRCD(0xea6d0efd,"setwobbledetails"), pTempParams, NULL );

		delete pTempParams;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::SetLoopingType( Gfx::EAnimLoopingType looping_type, bool propagate )
{
	if ( propagate )
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		GameNet::PlayerInfo* player;
		static char s_last_type = -1;
		
		player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID() );
		if ( player && player->IsLocalPlayer())
		{
			Net::Client* client;
			GameNet::MsgSetLoopingType msg;

			client = gamenet_man->GetClient( player->GetSkaterNumber() );
			Dbg_Assert( client );

			//msg.m_Time = client->m_Timestamp;
			msg.m_LoopingType = looping_type;
			msg.m_ObjId = GetObject()->GetID();
			if( s_last_type != looping_type )
			{
				Net::MsgDesc msg_desc;

				msg_desc.m_Data = &msg;
				msg_desc.m_Length = sizeof( GameNet::MsgSetLoopingType );
				msg_desc.m_Id = GameNet::MSG_ID_SET_LOOPING_TYPE;
				client->EnqueueMessageToServer( &msg_desc );
				s_last_type = looping_type;
			}
		}
	}

	if ( get_primary_channel() )
	{
		get_primary_channel()->SetLoopingType( looping_type );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::ReverseDirection( bool propagate )
{
    Dbg_Assert( !propagate );

	if ( get_primary_channel() )
	{
		get_primary_channel()->ReverseDirection();
	}

	// TODO:  eventually turn this into a net message.
	// For now, only the viewer objects should be reversing the direction
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::SetAnimSpeed( float speed, bool propagate, bool all_channels )
{	
	if ( !get_primary_channel() )
	{
		// no primary channel, so do nothing
		return;
	}

	float cur_speed = get_primary_channel()->GetAnimSpeed();

	if( !all_channels && Mth::Abs( cur_speed - speed ) < vMIN_SPEED_THRESHOLD )
	{
		return;
	}

	if ( propagate )
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		Net::Client* client;
		GameNet::MsgSetAnimSpeed msg;
		GameNet::PlayerInfo* player;

		player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID() );
		if ( player && player->IsLocalPlayer())
		{
			client = gamenet_man->GetClient( player->GetSkaterNumber() );
			if( client )
			{
				Net::MsgDesc msg_desc;

				//msg.m_Time = client->m_Timestamp;
				msg.m_AnimSpeed = speed;
				msg.m_ObjId = GetObject()->GetID();

				msg_desc.m_Data = &msg;
				msg_desc.m_Length = sizeof( GameNet::MsgSetAnimSpeed );
				msg_desc.m_Id = GameNet::MSG_ID_SET_ANIM_SPEED;
				client->EnqueueMessageToServer( &msg_desc );
			}
		}
	}

	if (all_channels)
	{
		Gfx::CBlendChannel* pChannel = (Gfx::CBlendChannel*)m_blendChannelList.GetNext();
		while ( pChannel )
		{
			pChannel->SetAnimSpeed( speed );
			pChannel = (Gfx::CBlendChannel*)pChannel->GetNext();
		}
	}
	else
	{
		get_primary_channel()->SetAnimSpeed( speed );
	}
	
	if (DebugSkaterScripts && GetObject()->GetID() == 0)
	{
		printf("%d: Setting Anim Speed: %f\n",(int)Tmr::GetRenderFrame(),speed);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CAnimationComponent::GetAnimSpeed( Script::CStruct* pParams, Script::CScript* pScript )
{	
	if ( !get_primary_channel() )
	{
		// no primary channel, so do nothing
		return 0;
	}

	float cur_speed = get_primary_channel()->GetAnimSpeed();

    return cur_speed;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::EnableBlending( bool enabled )
{
	m_shouldBlend = enabled;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAnimationComponent::ShouldBlend()
{
	return m_shouldBlend;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Plays an animation given a set of parameters contained in pParams.
// This allows both the skater and other moving objects such as pedestrians to share a common-ish interface.

// Returns the checksum of the animation if it did get successfully played, 0 otherwise.
// (This feature is used by some debug code in the CSkater's PlayAnim script command)
uint32 CAnimationComponent::PlayAnim(Script::CStruct *pParams, Script::CScript *pScript, float defaultBlendPeriod )
{
	if (Gfx::CBlendChannel* primary_channel = get_primary_channel())
	{
		m_last_animation_time = primary_channel->GetCurrentAnimTime();
	}

	if (m_dont_interrupt && !IsAnimComplete())
	{
		return 0;
	}
		
    if ( !has_anims() )
    {
        // some items don't have animation data associated with it yet...
        return 0;
    }

	uint32 AnimChecksum=0;
	pParams->GetChecksum( CRCD(0x98549ba4,"Anim"), &AnimChecksum );
	if ( !AnimChecksum )
	{
		Script::PrintContents(pParams);
		Dbg_MsgAssert(0,("\n%s\nobj: %s\nPlayAnim requires an anim name",Script::FindChecksumName(GetObject()->GetID()),pScript->GetScriptInfo()));
	}
	
	float Speed=1.0f;
	pParams->GetFloat( CRCD(0xf0d90109,"Speed"), &Speed );   
	
	uint32 CurrentAnimChecksum = GetCurrentSequence();
	if ( AnimChecksum == CRCD(0x230ccbf4,"Current") )
	{
		AnimChecksum = CurrentAnimChecksum;
	}
	else
	{
		if (CurrentAnimChecksum==AnimChecksum)
		{
			if ( pParams->ContainsFlag( CRCD(0xfe09fe09,"NoRestart") ) )
			{
				// Return without doing anything, since the user specified NoRestart, and the
				// current anim is the same as the requested one.
				return 0;
			}
		}	
	}

	if ( !AnimExists( AnimChecksum ) )
	{
#ifdef __NOPT_ASSERT__
		if ( Script::GetInt( CRCD(0x56d4128e,"AssertOnMissingAnims"), Script::NO_ASSERT ) )
		{
			Dbg_MsgAssert( 0, ( "*** Anim %s (%s) was not defined for object %s!",
							Script::FindChecksumName(AnimChecksum),
							Script::FindChecksumName(m_animScriptName),
							Script::FindChecksumName(GetObject()->GetID() ) ) );
		}
		else if ( Script::GetInt( CRCD(0xf42cb81a,"WarnOnMissingAnims"), Script::NO_ASSERT ) )
		{
			Dbg_Message( "*** Anim %s (%s) was not defined for object %s!",
						 Script::FindChecksumName(AnimChecksum),
						 Script::FindChecksumName(m_animScriptName),
						 Script::FindChecksumName(GetObject()->GetID() ) );
		}
#endif

		// by default, a missing anim will play the christ-pose anim
		AnimChecksum = CRCD(0x1ca1ff20,"default");
	}
	
	float BlendPeriod=defaultBlendPeriod;
	float BlendPeriodPercent=0.0f;
	if (mGotBlendPeriodOut)
	{
		// If a BlendPeriodOut command was issued, use that value to override any
		// value specified by PlayAnim.
		BlendPeriod=mBlendPeriodOut;
		// but clear the flag for next time.
		mGotBlendPeriodOut=false;
	}
	else if (pParams->GetFloat(CRCD( 0x4f3abd6c,"BlendPeriodPercent"), &BlendPeriodPercent) )
	{
		BlendPeriod = AnimDuration(AnimChecksum) * (BlendPeriodPercent/100.0f);
	}
	else
	{
		pParams->GetFloat( CRCD(0x8f0d24ed,"BlendPeriod"), &BlendPeriod );
	}	
	
	float Duration=AnimDuration(AnimChecksum);
	float From=0;
	float Current=0;
	float To=Duration;
	
	if ( pParams->ContainsFlag( CRCD(0x099371ae,"SyncToPreviousAnim") ) )
	{
		float Dummy, PreviousAnimCurrent, PreviousAnimDuration;
		
		GetPrimaryAnimTimes(&Dummy, &PreviousAnimCurrent, &Dummy);
		PreviousAnimDuration = AnimDuration(CurrentAnimChecksum);
		pParams->GetFloat(CRCD(0x4b4a153a, "EffectivePreviousAnimDuration"), &PreviousAnimDuration);
		
		Current = Mth::Clamp(PreviousAnimCurrent / AnimDuration(CurrentAnimChecksum), 0.0f, 1.0f) * Duration;
		if ( pParams->ContainsFlag( CRCD(0xf8cfd515,"Backwards") ) )
		{
			Current = -(Duration - Current);
		}
	}
	else if ( pParams->ContainsFlag( CRCD(0xff5ac79e, "SyncToReversePreviousAnim") ) )
	{
		float Dummy, PreviousAnimCurrent, PreviousAnimDuration;
		
		GetPrimaryAnimTimes(&Dummy, &PreviousAnimCurrent, &Dummy);
		PreviousAnimDuration = AnimDuration(CurrentAnimChecksum);
		pParams->GetFloat(CRCD(0x4b4a153a, "EffectivePreviousAnimDuration"), &PreviousAnimDuration);
		
		Current = Mth::Clamp((PreviousAnimDuration - PreviousAnimCurrent) / PreviousAnimDuration, 0.0f, 1.0f) * Duration;
		if ( pParams->ContainsFlag( CRCD(0xf8cfd515,"Backwards") ) )
		{
			Current = -(Duration - Current);
		}
	}

	// Need TempCurrent, so that it doesn't conflict with regular Current
	// which is possibly modified in the above "SyncToPreviousAnim" code block
	float TempCurrent;
	float Dummy;
	GetPrimaryAnimTimes(&Dummy,&TempCurrent,&Dummy);
	Gfx::GetTimeFromParams( &From, &To, TempCurrent, Duration, pParams, pScript );

	Gfx::EAnimLoopingType LoopingType;
	Gfx::GetLoopingTypeFromParams( &LoopingType, pParams );

	m_dont_interrupt=pParams->ContainsFlag( CRCD(0x84f13067,"DontInterrupt") );
	
#ifdef __NOPT_ASSERT__
	if ( Script::GetInt( CRCD(0xca108bce,"DebugAnims"), false ) )
	{
		if ( GetObject()->GetID() == 0 )
		{
			printf( "DebugAnims:  Playing skater anim %s\n", Script::FindChecksumName(AnimChecksum) );
		}
	}
	
	if (DebugSkaterScripts && AnimChecksum && GetObject()->GetID() == 0)
	{
		printf("%d: Playing anim '%s'\n",(int)Tmr::GetRenderFrame(),Script::FindChecksumName(AnimChecksum));
	}
#endif

	PlayPrimarySequence( AnimChecksum, true, From, To, LoopingType, BlendPeriod, Speed );
	
	if (Current != 0.0f)
	{
		get_primary_channel()->AddTime(Current);
	}
	
	/*
	if ( GetObject()->GetID() == 0 )
	{
		printf("-standard-anim-\n");
		printf("Anim = %s\n", Script::FindChecksumName(AnimChecksum));
		printf("From = %f\n", From);
		printf("To = %f\n", To);
		printf("Duration = %f\n", Duration);
		printf("Current = %f\n", Current);
		printf("Speed = %f\n", Speed);
		printf("BlendPeriod = %f\n", BlendPeriod);
	}
	*/
	
	uint32 PartialAnim;
	if (pParams->GetChecksum(CRCD(0x6c565f57, "PartialAnimOverlay"), &PartialAnim))
	{
		Script::CStruct* pPartialAnimParams = new Script::CStruct;
		
		uint32 partial_anim_id;
		if (pParams->GetChecksum(CRCD(0xf0ce9e0f, "PartialAnimOverlayId"), &partial_anim_id))
		{
			pPartialAnimParams->AddChecksum(CRCD(0x40c698af, "Id"), partial_anim_id);
		}
		pPartialAnimParams->AddChecksum(CRCD(0x7321a8d6, "Type"), CRCD(0x659bf355, "PartialAnim"));
		pPartialAnimParams->AddChecksum(CRCD(0x6c2bfb7f, "AnimName"), PartialAnim);
		pPartialAnimParams->AddFloat(CRCD(0x46e55e8f, "From"), From);
		pPartialAnimParams->AddFloat(CRCD(0x28782d3b, "To"), To);
		pPartialAnimParams->AddChecksum(NO_NAME, CRCD(0xd029f619, "Seconds"));
		pPartialAnimParams->AddFloat(CRCD(0x230ccbf4, "Current"), Current);
		pPartialAnimParams->AddFloat(CRCD(0xf0d90109, "Speed"), Speed);
		switch (LoopingType)
		{
			case Gfx::LOOPING_HOLD:
				break;
			case Gfx::LOOPING_CYCLE:
				pPartialAnimParams->AddChecksum(NO_NAME, CRCD(0x4f792e6c, "Cycle"));
				break;
			case Gfx::LOOPING_PINGPONG:
				pPartialAnimParams->AddChecksum(NO_NAME, CRCD(0x3153e314, "PingPong"));
				break;
			case Gfx::LOOPING_WOBBLE:
				pPartialAnimParams->AddChecksum(NO_NAME, CRCD(0x6d941203, "Wobble"));
				break;
		}
		
		get_primary_channel()->AddController(pPartialAnimParams);
		
		delete pPartialAnimParams;
		
		#ifdef __NOPT_ASSERT__
		if ( GetObject()->GetID() == 0 && Script::GetInt( CRCD(0xca108bce,"DebugAnims"), false ) )
		{
			printf( "DebugAnims:  Playing skater partial anim overlay %s\n", Script::FindChecksumName(PartialAnim) );
		}
		
		if (DebugSkaterScripts && PartialAnim && GetObject()->GetID() == 0)
		{
			printf("%d: Playing partial anim overlay '%s'\n",(int)Tmr::GetRenderFrame(),Script::FindChecksumName(PartialAnim));
		}
		#endif
	}

	return AnimChecksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// static workspace data
static Gfx::CPose	sBlendPoses[vNUM_DEGENERATE_ANIMS];
static float		sBlendValues[vNUM_DEGENERATE_ANIMS];
static Gfx::CPose	sResultPose;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void blend( Gfx::CPose* pPoseList, float* blendVal, int numBones, Gfx::CPose* pResultPose, int numBlendChannels )
{
	// the blend value of the primary animation 
	// should be 1.0, since it's active.
	Dbg_Assert( blendVal[0] == 1.0f );

	float totalBlendVal = 0.0f;
	for ( int i = 1; i < numBlendChannels; i++ )
	{
		if ( blendVal[i] )
		{
			totalBlendVal += 1.0f;
		}
	}

	float degenerateBlendTotal = 0.0f;
	for ( int i = 1; i < numBlendChannels; i++ )
	{
		degenerateBlendTotal += blendVal[i];
	}
	
	// normalize them so that they all add up to total blend value
	blendVal[0] = totalBlendVal - degenerateBlendTotal;

	// Scan through the sets of frames, interpolating between the 2 based on the blend values.
	float sum = 0.0f;

	int i0, i1;
	for ( i1 = (numBlendChannels - 1); i1 > 0; --i1 )
	{
		float blend1 = blendVal[i1];
		if ( blend1 )
		{
			blend1 += sum;

			i0 = i1 - 1;
			while(( blendVal[i0] <= 0.0f ) && ( i0 > 0 ))
			{
				--i0;
			}

			float blend0 = blendVal[i0];

			sum += blendVal[i1];

			// Need to normalise blend0 and blend1 so they sum to 1.0.
			blend0 /= ( blend0 + blend1 );
			blend1 = 1.0f - blend0;

			Mth::Quat* pRotations0 = pPoseList[i0].m_rotations;
			Mth::Quat* pRotations1 = pPoseList[i1].m_rotations;
			Mth::Vector* pTranslations0 = pPoseList[i0].m_translations;
			Mth::Vector* pTranslations1 = pPoseList[i1].m_translations;

			for( int b = 0; b < numBones; b++ )
			{
				*pRotations0 = Mth::FastSlerp( *pRotations0, *pRotations1, 1.0f - blend0 );
				
				*pTranslations0 = Mth::Lerp( *pTranslations0, *pTranslations1, 1.0f - blend0 );

				pRotations0++;
				pRotations1++;
				pTranslations0++;
				pTranslations1++;
			}
		}
	}

	// copy the values into the result pose:

	Mth::Quat* pSrcQuat = &pPoseList[0].m_rotations[0];
	Mth::Vector* pSrcVector = &pPoseList[0].m_translations[0];
	
	Mth::Quat* pDstQuat = &pResultPose->m_rotations[0];
	Mth::Vector* pDstVector = &pResultPose->m_translations[0];

	memcpy( pDstQuat, pSrcQuat, numBones * sizeof(Mth::Quat) );
	memcpy( pDstVector, pSrcVector, numBones * sizeof(Mth::Vector) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CAnimationComponent::get_blend_channel( int blendChannel, Gfx::CPose* pResultPose, float* pBlendVal )
{
	float blendVal = 0.0f;
	Gfx::CBlendChannel* pBlendChannel = NULL;
	if ( 1 || ShouldBlend() )
	{
		Dbg_MsgAssert( blendChannel >= 0 && blendChannel < (int)m_blendChannelList.CountItems(), ( "out of range blend channel %d (0-%d)", blendChannel, m_blendChannelList.CountItems() ) ); 
		pBlendChannel = (Gfx::CBlendChannel*)m_blendChannelList.GetItem(blendChannel);
		blendVal = pBlendChannel->GetBlendValue();
		Dbg_MsgAssert( blendChannel != 0 || blendVal == 1.0f, ( "Expected primary channel to have a blend value of 1.0f" ) );
	}
	else
	{
		switch ( blendChannel )
		{
			case 0:
				pBlendChannel = get_primary_channel();
				blendVal = 1.0f;
				break;
			default:
				Dbg_MsgAssert( 0, ( "Invalid blend channel %d", blendChannel ) );
		}
	}
	
	if ( blendVal <= 0.0f )
	{
		// GJ TODO:  I'm not sure why this case happens,
		// but it happened on THPS4 as well...  i should
		// look into this at some point...
		*pBlendVal = 0.0f;
		return;
	}

	Gfx::CSkeleton* pSkeleton = NULL;

	Dbg_Assert(mp_skeleton_component);
	pSkeleton = mp_skeleton_component->GetSkeleton();

	if ( !pBlendChannel->GetPose( pResultPose ) )//, 
								  // m_flags & nxANIMCOMPONENTFLAGS_FLIPPED,
								  // m_flags & nxANIMCOMPONENTFLAGS_ROTATESKATEBOARD,
								  // pSkeleton ) )
	{
		// couldn't find pose...
		// if it's the primary animation, assert
		if ( blendChannel == 0 )
		{
			Dbg_MsgAssert( 0, ( "No primary animation found!  Is %08x/%s playing a default animation?", GetObject()->GetID(), Script::FindChecksumName(m_animScriptName) ) );
		}
	}
	
	*pBlendVal = blendVal;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAnimationComponent::ShouldAnimate()
{
#if 1

// The model will be inactive if off the screen
	Nx::CModel* p_model = mp_model_component->GetModel();
	return ( p_model && p_model->GetActive() );

#else

// Old version, kind of mixed up	
	
	
	// this function will eventually be used to LOD
	// our animations (i.e. updating every Nth frame
	// based on distance)
	
	// if we did this, we would want to make sure that
	// the model's update function to be called at least
	// once to sync up the skeleton initially (we don't 
	// want to do it in the constructor, because then
	// there's an order dependency on the components)
	
	// GJ:  Checking whether the model is visible
	// is a speed optimization, but it's ugly 
	// because it makes this component dependent on the 
	// model component...  Eventually, I'd like to 
	// find a more elegant solution
		
	// update the skeleton, if it exists
	Dbg_Assert(mp_model_component);
	Nx::CModel* p_model = mp_model_component->GetModel();
	if ( p_model && p_model->GetActive() )
	{
		Mth::Vector sphere = p_model->GetBoundingSphere();
		
		printf ("sphere = (%.2f,%.2f,%.2f,%.2f)\n",sphere[X],sphere[Y],sphere[Z],sphere[W]);

		// GJ:  The bounding sphere may have a position
		// offset built into it, so need to factor that in
		Mth::Vector pos = GetObject()->GetPos() + sphere;
		pos[W] = 1.0f;
		
		// Mick:  This visibility test always returns true
		// when in split screen mode 	
		if ( !Nx::CEngine::sIsVisible( pos,sphere[3] ) )
		{
			return false;
		}
	}

	// for now, just always animate, even if no model, or the model is inactive !?
	return true;
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::update_skeleton()
{
	if ( has_anims() )
	{
		Gfx::CSkeleton* pSkeleton = NULL;
		
		Dbg_Assert(mp_skeleton_component);
		pSkeleton = mp_skeleton_component->GetSkeleton();

		if ( !pSkeleton )
		{
			return;
		}

		// Set the current view distance for the skeleton so that it may decide on an appropriate set of
		// bones to use in the animation.
		pSkeleton->SetBoneSkipDistance( m_parent_object_dist_to_camera );

		int numBlendChannels = m_blendChannelList.CountItems();

		for ( int blendChannel = 0; blendChannel < numBlendChannels; blendChannel++ )
		{
			// this wil loop through each blend channel's animation channels
			get_blend_channel( blendChannel, &sBlendPoses[blendChannel], &sBlendValues[blendChannel] );
		}

		// TODO: Here, we could run any controllers that need to act on the final, post-blend pose...
		if( numBlendChannels > 1 )
		{
			blend( &sBlendPoses[0], &sBlendValues[0], pSkeleton->GetNumBones(), &sResultPose, numBlendChannels );
			pSkeleton->Update( &sResultPose );
		}
		else
		{
			// In the case that there is just one blend channel, there is no requirement to call the blend
			// function, which will needlessly copy the quaternion and translation values for each bone from
			// the sBlendPoses buffer to the sResultPose buffer. Just use the sBlendPoses buffer directly. 
			pSkeleton->Update( &sBlendPoses[0] );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAnimationComponent::FlipAnimation( uint32 objId, bool flip, uint32 time, bool propagate )
{
	if( propagate )
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		GameNet::PlayerInfo* player;
        		
		player = gamenet_man->GetPlayerByObjectID( objId );
		if( player && player->IsLocalPlayer())
		{
			Net::Client* client;
			GameNet::MsgFlipAnim msg;
			Net::MsgDesc msg_desc;
			
			client = gamenet_man->GetClient( player->GetSkaterNumber());
			Dbg_Assert( client );

			msg.m_ObjId = objId;
			msg.m_Flipped = flip;
			//msg.m_Time = time;

			msg_desc.m_Data = &msg;
			msg_desc.m_Length = sizeof( GameNet::MsgFlipAnim );
			msg_desc.m_Id = GameNet::MSG_ID_FLIP_ANIM;
			client->EnqueueMessageToServer( &msg_desc );
		}
	}

	bool oldFlipped = m_flags & nxANIMCOMPONENTFLAGS_FLIPPED;

	SetFlipState( flip );

    return oldFlipped;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CAnimationComponent::SetFlipState( bool shouldFlip )
{
	// TODO:  should actually be in blend channel code
	// since we want certain blend channels to be flipped
	// (the animation component will still need to know
	// which flip state to make new channels)

	m_flags &= ~nxANIMCOMPONENTFLAGS_FLIPPED;
	m_flags |= ( shouldFlip ? nxANIMCOMPONENTFLAGS_FLIPPED : 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CAnimationComponent::destroy_blend_channels()
{
	m_blendChannelList.DestroyAllNodes();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
		
void CAnimationComponent::initialize_procedural_bones( Script::CStruct* pParams )
{
	Script::CArray* pArray;

	pParams->GetArray( CRCD(0x3ed7262b,"bone_list"), &pArray, Script::ASSERT );
	
	for ( uint32 i = 0; i < pArray->GetSize(); i++ )
	{
		this->InitializeProceduralBone( pArray->GetChecksum(i) );
	}
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
		
void CAnimationComponent::update_procedural_bones()
{
	int numProceduralBones = this->GetNumProceduralBones(); 
	Gfx::CProceduralBone* p_current_procedural_bone = this->GetProceduralBones();

	for ( int i = 0; i < numProceduralBones; i++ )
	{
		// TODO:  Factor in the frame rate...
		
		if ( p_current_procedural_bone->transEnabled )
		{
			p_current_procedural_bone->currentTrans += p_current_procedural_bone->deltaTrans;
			p_current_procedural_bone->currentTrans[X] = (int)p_current_procedural_bone->currentTrans[X] & 0xfff;
			p_current_procedural_bone->currentTrans[Y] = (int)p_current_procedural_bone->currentTrans[Y] & 0xfff;
			p_current_procedural_bone->currentTrans[Z] = (int)p_current_procedural_bone->currentTrans[Z] & 0xfff;
		}

		if ( p_current_procedural_bone->rotEnabled )
		{
			p_current_procedural_bone->currentRot += p_current_procedural_bone->deltaRot;
			p_current_procedural_bone->currentRot[X] = (int)p_current_procedural_bone->currentRot[X] & 0xfff;
			p_current_procedural_bone->currentRot[Y] = (int)p_current_procedural_bone->currentRot[Y] & 0xfff;
			p_current_procedural_bone->currentRot[Z] = (int)p_current_procedural_bone->currentRot[Z] & 0xfff;
		}

		if ( p_current_procedural_bone->scaleEnabled )
		{
			p_current_procedural_bone->currentScale += p_current_procedural_bone->deltaScale;
			p_current_procedural_bone->currentScale[X] = (int)p_current_procedural_bone->currentScale[X] & 0xfff;
			p_current_procedural_bone->currentScale[Y] = (int)p_current_procedural_bone->currentScale[Y] & 0xfff;
			p_current_procedural_bone->currentScale[Z] = (int)p_current_procedural_bone->currentScale[Z] & 0xfff;
		}

		p_current_procedural_bone++;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CAnimationComponent::InitializeProceduralBone( uint32 boneName )
{
	if ( m_numProceduralBones >= vMAXPROCEDURALBONES )
	{
		Dbg_MsgAssert( 0, ( "Too many procedural bones" ) );
		return false;
	}

	Gfx::CProceduralBone* pBone = &mp_proceduralBones[m_numProceduralBones];

	pBone->m_name = boneName;

	pBone->transEnabled = false;
	pBone->rotEnabled = true;

	pBone->trans0[X] = Script::GetFloat( CRCD(0x802638f7,"trans_min_x"), Script::NO_ASSERT );
	pBone->trans0[Y] = Script::GetFloat( CRCD(0xf7210861,"trans_min_y"), Script::NO_ASSERT );
	pBone->trans0[Z] = Script::GetFloat( CRCD(0x6e2859db,"trans_min_z"), Script::NO_ASSERT);
	pBone->trans0[W] = 1.0f;

	pBone->trans1[X] = Script::GetFloat( CRCD(0x5d39cfda,"trans_max_x"), Script::NO_ASSERT );
	pBone->trans1[Y] = Script::GetFloat( CRCD(0x2a3eff4c,"trans_max_y"), Script::NO_ASSERT );
	pBone->trans1[Z] = Script::GetFloat( CRCD(0xb337aef6,"trans_max_z"), Script::NO_ASSERT );
	pBone->trans1[W] = 1.0f;

	pBone->currentTrans = Mth::Vector(0.0f,0.0f,0.0f,0.0f);
	pBone->deltaTrans = Mth::Vector(32.0f,32.0f,32.0f,0.0f);

	pBone->rot0[X] = Script::GetFloat( CRCD(0x2dafa9c5,"rot_min_x"), Script::NO_ASSERT ) * 2.0f * Mth::PI / 4096.0f;
	pBone->rot0[Y] = Script::GetFloat( CRCD(0x5aa89953,"rot_min_y"), Script::NO_ASSERT ) * 2.0f * Mth::PI / 4096.0f;
	pBone->rot0[Z] = Script::GetFloat( CRCD(0xc3a1c8e9,"rot_min_z"), Script::NO_ASSERT ) * 2.0f * Mth::PI / 4096.0f;
	pBone->rot0[W] = 1.0f;

	pBone->rot1[X] = Script::GetFloat( CRCD(0xf0b05ee8,"rot_max_x"), Script::NO_ASSERT ) * 2.0f * Mth::PI / 4096.0f;
	pBone->rot1[Y] = Script::GetFloat( CRCD(0x87b76e7e,"rot_max_y"), Script::NO_ASSERT ) * 2.0f * Mth::PI / 4096.0f;
	pBone->rot1[Z] = Script::GetFloat( CRCD(0x1ebe3fc4,"rot_max_z"), Script::NO_ASSERT ) * 2.0f * Mth::PI / 4096.0f;
	pBone->rot1[W] = 1.0f;

	pBone->currentRot = Mth::Vector(0.0f,0.0f,0.0f,0.0f);
	pBone->deltaRot = Mth::Vector(32.0f,32.0f,32.0f,0.0f);

	pBone->scale0[X] = 1.0f; //Script::GetFloat("scale_min_x",Script::NO_ASSERT) * 2.0f * Mth::PI / 4096.0f;
	pBone->scale0[Y] = 1.0f; //Script::GetFloat("scale_min_y",Script::NO_ASSERT) * 2.0f * Mth::PI / 4096.0f;
	pBone->scale0[Z] = 1.0f; //Script::GetFloat("scale_min_z",Script::NO_ASSERT) * 2.0f * Mth::PI / 4096.0f;
	pBone->scale0[W] = 1.0f;

	pBone->scale1[X] = 1.0f; //Script::GetFloat("scale_max_x",Script::NO_ASSERT) * 2.0f * Mth::PI / 4096.0f;
	pBone->scale1[Y] = 1.0f; //Script::GetFloat("scale_max_y",Script::NO_ASSERT) * 2.0f * Mth::PI / 4096.0f;
	pBone->scale1[Z] = 1.0f; //Script::GetFloat("scale_max_z",Script::NO_ASSERT) * 2.0f * Mth::PI / 4096.0f;
	pBone->scale1[W] = 1.0f;

	pBone->currentScale = Mth::Vector(0.0f,0.0f,0.0f,0.0f);
	pBone->deltaScale = Mth::Vector(32.0f,32.0f,32.0f,0.0f);

	m_numProceduralBones++;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Gfx::CProceduralBone* CAnimationComponent::GetProceduralBoneByName( uint32 boneName )
{
	for ( int i = 0; i < m_numProceduralBones; i++ )
	{
		if ( boneName == mp_proceduralBones[i].m_name )
		{
			return &mp_proceduralBones[i];
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CAnimationComponent::GetNumProceduralBones()
{
	return m_numProceduralBones;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::CProceduralBone* CAnimationComponent::GetProceduralBones()
{
	return mp_proceduralBones;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CAnimationComponent::IsFlipped()
{
	return m_flags & nxANIMCOMPONENTFLAGS_FLIPPED;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimationComponent::Finalize()
{
	mp_skeleton_component = GetSkeletonComponentFromObject( GetObject() );
	mp_suspend_component = GetSuspendComponentFromObject( GetObject() );
	mp_model_component = GetModelComponentFromObject( GetObject() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/animationcomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       AnimationComponent.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/24/2002
//****************************************************************************

#ifndef __COMPONENTS_ANIMATIONCOMPONENT_H__
#define __COMPONENTS_ANIMATIONCOMPONENT_H__
								   
#include 
#include 

#include 

#include 

#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_ANIMATION CRCD(0x72ad7b23,"Animation")
#define		GetAnimationComponent() ((Obj::CAnimationComponent*)GetComponent(CRC_ANIMATION))
#define		GetAnimationComponentFromObject(pObj) ((Obj::CAnimationComponent*)(pObj)->GetComponent(CRC_ANIMATION))

namespace Gfx
{
	class CBonedAnimFrameData;
	class CPose;
	class CProceduralBone;
}

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSuspendComponent;
	class CSkeletonComponent;
	class CModelComponent;

class CAnimationComponent : public CBaseComponent
{
public:
    CAnimationComponent();
    virtual ~CAnimationComponent();

public:
	// Basic component functions
    virtual void            		Update();
    virtual void					Finalize( );
    virtual void            		InitFromStructure( Script::CStruct* pParams ); 
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void					GetDebugInfo( Script::CStruct* p_info );
	virtual void					ProcessWait( Script::CScript * pScript );
	
    static CBaseComponent*			s_create();

public:
	// Viewer functions
    void       		            	PrintStatus();
	void							AddTime( float incVal );
	float                       	GetCurrentAnimTime( void );

public:
    void							SetAnims( uint32 anim_checksum );
	void                        	Reset();
	void				        	PlaySequence(uint32 checksum, float BlendPeriod=0.3f );    
    bool				        	AnimEquals(Script::CStruct *pParams, Script::CScript *pScript );
    uint32                      	PlayAnim(Script::CStruct *pParams, Script::CScript *pScript, float defaultBlendPeriod = 0.3f );
	uint32							GetAnimScriptName() const { return m_animScriptName; }
	uint32							GetAnimEventTableName() const { return m_animEventTableName; }

public:
    // TODO:  Eventually, make this class completely independent of Gfx...
    // (You shouldn't need to reference CBonedAnimFrameData from this class...)
    float                       	AnimDuration( uint32 checksum );
    bool                       		AnimExists( uint32 checksum );
    		
protected:
	bool							ShouldBlend();

public:
	void							EnableBlending( bool enabled );
	void 							ToggleFlipState();
	void							UpdateSkeleton();

public:
	// CLIENT FUNCTIONS
	void				        	PlayPrimarySequence( uint32 index, bool propagate, float start_time = 0.0f, float end_time = 1000.0f, Gfx::EAnimLoopingType loop_type = Gfx::LOOPING_CYCLE, float blend_period = 0.3f, float speed = 1.0f );

	void				        	SetLoopingType( Gfx::EAnimLoopingType loopingType, bool propagate );
	void				        	SetAnimSpeed( float speed, bool propagate, bool all_channels = false );
    float				        	GetAnimSpeed( Script::CStruct* pParams, Script::CScript* pScript );
	void							ReverseDirection( bool propagate );	

	// called once per frame, during manuals!
	void				        	SetWobbleTarget(float alpha, bool propagate);

	// should not be called once per frame...  only when the trick changes
	void				        	SetWobbleDetails( const Gfx::SWobbleDetails& wobble_details, bool propagate );
	
	bool				    		FlipAnimation( uint32 objId, bool flip, uint32 time, bool propagate );
	void							SetFlipState( bool shouldFlip );
	void							SetBoneRotation( uint32 boneId, bool shouldRotate );
	bool							IsFlipped();
	void							AddAnimController( Script::CStruct* pParams );
	bool							ShouldAnimate();
	
public:
	// SERVER-ONLY FUNCTIONS
	bool				        	IsAnimComplete( void );
	bool				        	IsLoopingAnim( void );
	uint32				        	GetCurrentSequence( void );
	void				        	GetPrimaryAnimTimes(float *pStart, float *pCurrent, float *pEnd);

protected:
	Gfx::CBlendChannel*				get_primary_channel();
	void							pack_degenerate_channels();
    Gfx::CBonedAnimFrameData*		find_actual_anim( uint32 checksum );
	int								get_num_degenerate_anims();

protected:
	bool							has_anims() { return m_animScriptName != 0; }
	void							update_skeleton();

	void							get_blend_channel( int blendChannel, Gfx::CPose* pResultPose, float* pBlendVal );
	void							destroy_blend_channels();
	void							create_new_blend_channel( float blend_period );
	void							add_anim_tags( uint32 animName );
	void							delete_anim_tags();

protected:
	Lst::Head	m_blendChannelList;

    // Get set by the BlendPeriodOut command.
	bool                        	mGotBlendPeriodOut;
	float                       	mBlendPeriodOut;

	bool							m_shouldBlend;
	
	// If set, then PlayAnim will do nothing until the current anim has finished.
	bool							m_dont_interrupt;
	
    uint32                      	m_animScriptName;
	uint32							m_flags;
	
	// GJ:  This might get split into its own component later on
	// (among other things, it can then have its own suspend logic)
	uint32							m_animEventTableName;
	
	float							m_last_animation_time;
	bool							m_animation_script_block_active;
	float							m_animation_script_unblock_point;

	float							m_parent_object_dist_to_camera;

	// GJ:  The following used to be in the CProceduralAnimController;
	// however, this data needs to be shared among different blend
	// channels so I had to move it here.  Maybe there should be a 
	// way to copy a controller from one blend channel to another?  
	// For now, I'm just going to go with the easy implementation
	// until there's a pressing need for it.  Maybe this should be
	// separated off into a separate CProceduralAnimComponent that 
	// the CProceduralAnimController acts upon?)
protected:
	int								m_numProceduralBones;
	Gfx::CProceduralBone*			mp_proceduralBones;
	bool							InitializeProceduralBone( uint32 boneName );
	void							initialize_procedural_bones( Script::CStruct* pParams );
	void							update_procedural_bones();
	
protected:
	CSuspendComponent*				mp_suspend_component;
	CSkeletonComponent*				mp_skeleton_component;
	CModelComponent*				mp_model_component;

public:
	Gfx::CProceduralBone*			GetProceduralBoneByName( uint32 id );
	int								GetNumProceduralBones();
	Gfx::CProceduralBone*			GetProceduralBones();
};

}

#endif


================================================
FILE: Code/Gel/Components/avoidcomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       AvoidComponent.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/02/02
//****************************************************************************

#include 

#include 
									
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool ped_debug_on()
{
	return ( Script::GetInt( CRCD(0x59f8e435,"ped_debug"), false ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static int get_jump_direction( float rotAng )
{
	// split into forward/left/back/right quadrants 
	// by shifting the angle by 45 degrees

	rotAng += Mth::PI / 4.0f;
	if ( rotAng < 0 )
	{
		rotAng += Mth::PI * 2.0f;
	}

	int direction = (int)( rotAng / ( Mth::PI / 2.0f ) );

	return direction;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static float get_jump_distance( Script::CStruct* pParams, float rotAng )
{
	Dbg_Assert( pParams );

	int direction = get_jump_direction( rotAng );

	float distance = 0.0f;

	switch ( direction )
	{
		case 1:	// left
			pParams->GetFloat( "distLeft", &distance, Script::ASSERT );
			break;

		case 2:	// backward
			pParams->GetFloat( "distBack", &distance, Script::ASSERT );
			break;

		case 3:	// right
			pParams->GetFloat( "distRight", &distance, Script::ASSERT );
			break;
		
		case 0: // forward
		case 4:
		default:
			pParams->GetFloat( "distForward", &distance, Script::ASSERT );
			break;
	}

	return distance;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static bool collides_with( Mth::Vector& vec0, Mth::Vector& vec1 )
{
	CFeeler	feeler;
	
	feeler.m_start = vec0;
	feeler.m_end = vec1;

	return feeler.GetCollision();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*
void CAvoidComponent::draw_collision_circle( int numPoints, float radius, uint32 rgb0 )
{
	Mth::Vector v1, v2;

	Mth::Vector fwd( 0.0f, 0.0f, radius );

	v1 = m_pos + fwd;

	for ( int i = 0; i < numPoints; i++ )
	{
		fwd.RotateY( 2.0f * Mth::PI / (float)numPoints );
		v2 = GetObject()->m_pos + fwd;

		if ( !collides_with( GetObject()->m_pos, v2 ) )
		{
			Gfx::AddDebugLine(v1,v2,rgb0,rgb0,1);
		}

		v1 = v2;
	}
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAvoidComponent::play_appropriate_jump_anim( float rotAng, Script::CStruct* pParams )
{
	// split into forward/left/back/right quadrants 
	// by shifting the angle by 45 degrees

	rotAng += Mth::PI / 4.0f;
	if ( rotAng < 0 )
	{
		rotAng += Mth::PI * 2.0f;
	}

	int direction = (int)( rotAng / ( Mth::PI / 2.0f ) );
	// 0 = forward
	// 1 = left
	// 2 = back
	// 3 = right

	Dbg_Assert( pParams );

	uint32 animChecksum;
	bool flipped;

	switch ( direction )
	{
		case ( 0 ):	// JUMP FORWARD
		case ( 4 ):
//			Dbg_Message( "Ped jumps forward to avoid skater" );
			pParams->GetChecksum( CRCD(0x4de2c9e8,"JumpForward"), &animChecksum, true );
			break;

		case ( 1 ):	// JUMP LEFT
			{
//				Dbg_Message( "Ped jumps left to avoid skater" );
				if ( pParams->GetChecksum( CRCD(0x733da756,"JumpLeft"), &animChecksum, false ) )
				{
					flipped = 0;
				}
				else
				{
					pParams->GetChecksum( CRCD(0xa7a0dd72,"JumpRight"), &animChecksum, true );
					flipped = 1;
				}
				
				Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
				if ( pAnimationComponent )
				{
					pAnimationComponent->FlipAnimation( GetObject()->GetID(), flipped, 0, true );
				}
			}
			break;

		case ( 2 ):	// JUMP BACK
//			Dbg_Message( "Ped jumps back to avoid skater" );
			pParams->GetChecksum( CRCD(0x64948109,"JumpBack"), &animChecksum, true );
			break;

		case ( 3 ):	// JUMP RIGHT
			{
//				Dbg_Message( "Ped jumps right to avoid skater" );
				if ( pParams->GetChecksum( CRCD(0xa7a0dd72,"JumpRight"), &animChecksum, false ) )
				{
					flipped = 0;
				}
				else
				{
					pParams->GetChecksum( CRCD(0x733da756,"JumpLeft"), &animChecksum, true );
					flipped = 1;
				}
				
				Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
				if ( pAnimationComponent )
				{
					pAnimationComponent->FlipAnimation( GetObject()->GetID(), flipped, 0, true );
				}
			}
			break;

		default:
			Dbg_MsgAssert( 0, ( "Ped jumped in unrecognized direction: %d", direction ) );
			return;
			break;
			
	}
	
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );

    if ( pAnimationComponent )
    {
        pAnimationComponent->PlaySequence( animChecksum, 0 );
        m_jumpTime = pAnimationComponent->AnimDuration( animChecksum );
    }
    else
    {
        Dbg_MsgAssert( 0, ( "No animation component" ) );
    }
}
                                                                 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CAvoidComponent::get_ideal_heading()
{
	Mdl::Skate* skate = Mdl::Skate::Instance();
				
	// assume pedestrians only appear in "1 player" game for now...
	// If this assumption becomes incorrect, cycle through skate->GetNumSkaters( )
	// and find the closest.
	CSkater *pSkater = skate->GetLocalSkater();
	Dbg_MsgAssert( pSkater, ( "Ped avoidance only works in 1-player games." ) );
	
	Mth::Vector jumpDir;

	// find the 2 directions perpendicular to the skater's line of motion...
	// and select the direction that will take us further away from the
	// skater
	
	jumpDir = (pSkater->GetDisplayMatrix())[Mth::AT];	
	jumpDir[Y] = 0.0f;
	jumpDir.Normalize( );
	
	Mth::Vector upVect( 0.0f, 1.0f, 0.0f, 0.0f );
	jumpDir = Mth::CrossProduct( jumpDir, upVect );

	// chose the direction that's farther...
	Mth::Vector posPlus;
	Mth::Vector posMinus;
	posPlus = GetObject()->m_pos + jumpDir;
	posMinus = GetObject()->m_pos - jumpDir;
			
	if ( Mth::DistanceSqr( pSkater->m_pos, posPlus ) < Mth::DistanceSqr( pSkater->m_pos, posMinus ) )
	{
		jumpDir[X] = -jumpDir[X];
		jumpDir[Z] = -jumpDir[Z];
	}
	
	return Mth::RadToDeg( Mth::GetAngle( GetObject()->m_matrix, jumpDir ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// This static function is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
CBaseComponent* CAvoidComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CAvoidComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CAvoidComponent::CAvoidComponent() : CBaseComponent()
{
	SetType( CRC_AVOID );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CAvoidComponent::~CAvoidComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CAvoidComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CAvoidComponent::Update()
{
#ifdef __NOPT_ASSERT__
//	if ( ped_debug_on() )
	{
		//Gfx::AddDebugCircle( m_pos, 20, 15.0f, 0x0000ffff, 1 );
		//Gfx::AddDebugCircle( m_stored_pos, 20, 15.0f, 0x00ff00ff, 1 );
		//draw_collision_circle( 100, 50.0f, 0xff0000ff );	
	}
#else
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAvoidComponent::jump( Mth::Matrix& mat0, Mth::Matrix& mat1, float rotAng, float jumpDist, float maxHerdDistance )
{
	mat1 = mat0;
	
	mat1.RotateYLocal( Mth::DegToRad(rotAng) );

	// test for an extra 15 units (1.25 feet) radius of clearance 
	// around the player to account for his actual body
	// (need to rotate it slightly up, to account for peds
	// that are below ground level...  not sure why this
	// happens, but will check it out in THPS5...)
	const float clearanceDist = 15.0f;
	Mth::Vector collisionTestPos = mat1.Transform( Mth::Vector( 0.0f, 10.0f, jumpDist + clearanceDist ) );
	
#ifdef __NOPT_ASSERT__
/*
	if ( ped_debug_on() )
	{
		Gfx::AddDebugCircle( collisionTestPos, 20, 15.0f, 0x00ff00ff, 10 );
	}
*/
#endif

	if ( collides_with( mat0.GetPos(), collisionTestPos ) )
	{
		Dbg_Message( "Collides with something!" );
		return false;
	}
	else
	{
		// build target matrix
		Mth::Vector targetPos = mat1.Transform( Mth::Vector( 0.0f, 0.0f, jumpDist ) );
		mat1[Mth::POS] = targetPos;

		Mdl::Skate* skate = Mdl::Skate::Instance();
		CSkater *pSkater = skate->GetLocalSkater();
		Dbg_MsgAssert( pSkater, ( "Ped avoidance only works in 1-player games." ) );
		
		Mth::Vector toSkater = targetPos - pSkater->m_pos;
		if ( toSkater.Length() < 30.0f )
		{
			printf( "Too close to skater %f!\n", toSkater.Length() );
			return false;
		}

		// check if we're going too far from the original point
		// TODO:  check for distance from the path, not the point
		// don't allow going too far from the original path,
		// or else you'll be able to herd them into weird
		// positions...
		Mth::Vector totalAvoidVec = targetPos - m_avoidOriginalPos;
		totalAvoidVec[Y] = 0.0f;

		if ( totalAvoidVec.Length() >= maxHerdDistance )
		{
			Dbg_Message( "Too far from the original avoid point %f", totalAvoidVec.Length() );
			return false;
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent::EMemberFunctionResult CAvoidComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch (Checksum)
	{
		// @script | Ped_SelectAvoidPoint | Will cause the pedestrian to jump out of
		// the way of the player.  He will land on a circle, JumpDist away from
		// the last postition stored in Obj_StorePos.  Once he lands the jump,
		// the LandedEscapeJump exception is triggered. 
		// @parmopt name | JumpLeft | jumpLeft | name of anim to play when jumping left
		// @parmopt name | JumpRight | jumpRight | name of anim to play when jumping right
		// @parmopt name | JumpForward | jumpForward | name of anim to play when jumping forward
		// @parmopt name | JumpBack | jumpBack | name of anum to play when jumping back
		// position where the ped jumps to 
		case 0x6157cbc0:	// Ped_SelectAvoidPoint
		{
			// this is the starting point...
			Mth::Matrix mat0;
			mat0 = GetObject()->GetDisplayMatrix();
			mat0[Mth::POS] = GetObject()->m_pos;

			// this is where we will eventually end up
			Mth::Matrix mat1;

			float maxHerdDistance = 100.0f;
			pParams->GetFloat( "maxHerdDistance", &maxHerdDistance, Script::NO_ASSERT );

			float idealHeading = get_ideal_heading();

			if ( jump(mat0, mat1, idealHeading, get_jump_distance( pParams, idealHeading ), maxHerdDistance ) )
			{
			   Dbg_Message( "Selecting ideal jump (ang=%f newpos=(%f, %f, %f)",
							idealHeading,
							mat1[Mth::POS][X],
							mat1[Mth::POS][Y],
							mat1[Mth::POS][Z] );   			
			}

			// GJ:  Try a few more directions before trying 
			// to jump back to the original point...
			else if ( jump(mat0, mat1, idealHeading + 180.0f, get_jump_distance( pParams, idealHeading + 180.0f ), maxHerdDistance) )
			{
				// jump to the other side was successful!
				printf( "Jumping to the other side...\n" );
			}
			else if ( jump(mat0, mat1, idealHeading - 90.0f, get_jump_distance( pParams, idealHeading - 90.0f ), maxHerdDistance) )
			{
				// jump away was successful!
				printf( "Jumping away...\n" );
			}
			else 
			{
				Dbg_Message( "Jumping back towards original point %f %f %f",
							 m_avoidOriginalPos[X],
							 m_avoidOriginalPos[Y],
							 m_avoidOriginalPos[Z] );

				// otherwise, jump back towards original point
				Mth::Vector jumpDir;
				jumpDir = m_avoidOriginalPos - mat0[Mth::POS];

				float rotAng;
				rotAng = Mth::GetAngle( GetObject()->m_matrix, jumpDir );

				bool success = jump( mat0, mat1, rotAng, get_jump_distance(pParams, rotAng), maxHerdDistance );

				if ( !success )
				{
					// jump in place
					mat1 = mat0;

					return CBaseComponent::MF_FALSE;
				}
			}  			

			// don't actually want to rotate the ped
			// (just translate it)
			mat1[Mth::RIGHT] = mat0[Mth::RIGHT];
			mat1[Mth::UP] = mat0[Mth::UP];
			mat1[Mth::AT] = mat0[Mth::AT];

			m_avoidInterpolator.setMatrices( &mat0, &mat1 );
			m_avoidAlpha = 0.0f;

#ifdef __NOPT_ASSERT__
/*
			if ( ped_debug_on() )
			{
				Gfx::AddDebugCircle( mat0[Mth::POS], 20, 15.0f, 0x00ff00ff, 100 );
				Gfx::AddDebugCircle( mat1[Mth::POS], 20, 15.0f, 0x00ff00ff, 100 );
			}
*/
#endif

			Mth::Vector jumpDir;
			jumpDir = mat1[Mth::POS] - mat0[Mth::POS];

			float rotAng;
			rotAng = Mth::GetAngle( GetObject()->m_matrix, jumpDir );

			// rotAng of zero means jump forward, so:
			play_appropriate_jump_anim( rotAng, pParams );
		}
		break;

		// @script | Ped_BackOnOriginalPath |
		case 0xd2aa4d2d:	// Ped_BackOnOriginalPath
		{
			float safeDist;
			safeDist = pParams->GetFloat( "dist", &safeDist, Script::ASSERT );

			Mth::CBBox theBox;
			theBox.SetMin( m_avoidOriginalPos - Mth::Vector( safeDist, safeDist, safeDist ) );
			theBox.SetMax( m_avoidOriginalTargetPos + Mth::Vector( safeDist, safeDist, safeDist ) );

			if ( theBox.Within( GetObject()->m_pos ) )
			{
				printf( "Intersects!\n" ); 
			//	return CBaseComponent::MF_TRUE;
			}
			else
			{
				printf( "Doesn't intersect!\n" ); 
				return CBaseComponent::MF_FALSE;
			}

#if 0
			Mth::Vector originalPath;
			originalPath = m_avoidOriginalTargetPos - m_avoidOriginalPos;

			Mth::Vector upVect( 0.0f, 1.0f, 0.0f, 0.0f );

			Mth::Vector normalToOriginalPath;
			normalToOriginalPath = Mth::CrossProduct( originalPath, upVect );

			// is the distance to the original path
			// choose the direction that's closer...
			Mth::Vector posPlus;
			Mth::Vector posMinus;
			posPlus = GetObject()->m_pos + normalToOriginalPath * safeDist;
			posMinus = GetObject()->m_pos - normalToOriginalPath * safeDist;

			Mth::Line originalLine( m_avoidOriginalTargetPos, m_avoidOriginalPos );
			Mth::Line currentLine1( m_avoidOriginalTargetPos, posPlus ); 
			Mth::Line currentLine2( m_avoidOriginalTargetPos, posMinus ); 

			Mth::Vector my_pt;
			Mth::Vector other_pt;
			float dummyFloat;

			if ( Mth::LineLineIntersect( originalLine, currentLine1, &my_pt, &other_pt, &dummyFloat, &dummyFloat, false ))
			{
				Mth::Vector difference = my_pt - other_pt;
				printf( "intersects with current line 1: %f\n", difference.Length() );
			//	return CBaseComponent::MF_FALSE;
			}
			else if ( Mth::LineLineIntersect( originalLine, currentLine2, &my_pt, &other_pt, &dummyFloat, &dummyFloat, false ))
			{
				Mth::Vector difference = my_pt - other_pt;
				printf( "intersects with current line 2: %f\n", difference.Length() );
			//	return CBaseComponent::MF_FALSE;
			}
			else
			{
				printf( "does not intersect\n" );
		//		return CBaseComponent::MF_FALSE;
			}
#endif
			return CBaseComponent::MF_FALSE;
		}
		break;

		// @script | Ped_MoveTowardsAvoidPoint |
		case 0x41226942:	// Ped_MoveTowardsAvoidPoint
		{
			Mth::Matrix mat0;
			m_avoidAlpha += ( m_jumpTime * ( 1.0f / 60.0f ) * Tmr::FrameRatio() );
			m_avoidInterpolator.getMatrix( &mat0, m_avoidAlpha );
			GetObject()->m_pos = mat0[Mth::POS];
			GetObject()->m_matrix = mat0;
			GetObject()->m_matrix[Mth::POS] = Mth::Vector(0.0f, 0.0f, 0.0f, 1.0f);
		}
		break;

		// @script | Ped_AvoidPointReached |
		case 0x3c7f8f3e:	// Ped_AvoidPointReached
		{
			Dbg_Assert( GetAnimationComponentFromObject( GetObject() ) );
			return ( m_avoidAlpha >= 1.0f && GetAnimationComponentFromObject( GetObject() )->IsAnimComplete() ) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		break;

		case 0x6a4bca91:	// Ped_RememberCurrentPosition
			m_avoidOriginalPos = GetObject()->m_pos;
		break;

		case 0x3fb422e8:	// Ped_RememberNextWaypoint
		{
			Obj::CMotionComponent* pMotionComponent = GetMotionComponentFromObject( GetObject() );
			if ( pMotionComponent && pMotionComponent->GetPathOb() )
			{
				m_avoidOriginalTargetPos = pMotionComponent->GetPathOb()->m_wp_pos_to;
			}
			else
			{
				m_avoidOriginalTargetPos = GetObject()->m_pos;
			}
		}
		break;

		case 0x539a32dc:	// Ped_RememberStickToGround
		{
			Obj::CMotionComponent* pMotionComponent = GetMotionComponentFromObject( GetObject() );
			Dbg_Assert( pMotionComponent );
			m_avoidOriginalStickToGround = ( pMotionComponent->m_movingobj_flags |= MOVINGOBJ_FLAG_STICK_TO_GROUND );
		}	
		break;

		case 0x53bc3b4e:	// Ped_RestoreStickToGround
		{
			Obj::CMotionComponent* pMotionComponent = GetMotionComponentFromObject( GetObject() );
			if ( pMotionComponent )
			{
				if ( m_avoidOriginalStickToGround )
				{
					pMotionComponent->m_movingobj_flags |= MOVINGOBJ_FLAG_STICK_TO_GROUND;
				}
				else
				{
					pMotionComponent->m_movingobj_flags &= ~MOVINGOBJ_FLAG_STICK_TO_GROUND;
				}
			}
		}
		break;

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAvoidComponent::GetDebugInfo( Script::CStruct* p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to C......Component::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*
	p_info->AddInteger("m_never_suspend",m_never_suspend);
	p_info->AddFloat("m_suspend_distance",m_suspend_distance);
	*/
	
	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo( p_info );	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/avoidcomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       AvoidComponent.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/02/02
//****************************************************************************

#ifndef __COMPONENTS_AVOIDCOMPONENT_H__
#define __COMPONENTS_AVOIDCOMPONENT_H__

#include 
#include 

#include 

#include 
#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_AVOID CRCD(0x6eb050ed,"Avoid")
#define		GetAvoidComponent() ((CAvoidComponent*)GetComponent(CRC_AVOID))
#define		GetAvoidComponentFromObject(pObj) ((CAvoidComponent*)(pObj)->GetComponent(CRC_AVOID))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CAvoidComponent : public CBaseComponent
{
// share, dammit.
friend class CPedLogicComponent;

public:
    CAvoidComponent();
    virtual ~CAvoidComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
protected:
	void							play_appropriate_jump_anim( float rotAngle, Script::CStruct* pParams );
	bool							jump( Mth::Matrix& mat0, Mth::Matrix& mat1, float rotAng, float jumpDist, float maxHerdDistance );
	float							get_ideal_heading();

protected:
	// Functions/member variables used for getting out of the skater's way:
	float							m_jumpTime;

	Mth::SlerpInterpolator			m_avoidInterpolator;
	float							m_avoidAlpha;
	Mth::Vector						m_avoidOriginalPos;
	Mth::Vector						m_avoidOriginalTargetPos;
	bool							m_avoidOriginalStickToGround;
};

}

#endif


================================================
FILE: Code/Gel/Components/bouncycomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       BouncyComponent.cpp
//* OWNER:          Mick West
//* CREATION DATE:  10/17/2002
//****************************************************************************

//#define	__DEBUG_BOUNCY__

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 

#include 

//#include 		// TODO:  remove - only used for getting the bouncing box via collision
#include 				// and this
#include 				// and this

namespace Obj
{

#define SWITCH_STATE( x )					m_state = x

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent *	CBouncyComponent::s_create()
{
	return static_cast(new CBouncyComponent);	
}
    
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBouncyComponent::CBouncyComponent() : CBaseComponent()
{
	SetType(CRC_BOUNCY);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBouncyComponent::~CBouncyComponent()
{

	if ( mp_bounce_slerp )
		delete mp_bounce_slerp;
	if (mp_collide_script_params)
		delete mp_collide_script_params;
	if (mp_bounce_script_params)
		delete mp_bounce_script_params;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Returns 
//	0 (MF_FALSE) if executed and returned false
//  1 (MF_TRUE)  if executed and returned true
//  2 (MF_NOT_EXECUTRED)    if not executed

CBaseComponent::EMemberFunctionResult CBouncyComponent::CallMemberFunction( uint32 Checksum, Script::CScriptStructure *pParams, Script::CScript *pScript )
{
	
	switch ( Checksum )
	{

        // @script | BouncyObj_PlayerCollisionOn | turn player collision on
		case ( 0xf9055d81 ): // "BouncyObj_PlayerCollisionOn"
			m_bouncyobj_flags &= ~BOUNCYOBJ_FLAG_PLAYER_COLLISION_OFF;
			return MF_TRUE;

        
        // @script | BouncyObj_PlayerCollisionOff | turn player collision off
		case ( 0xf64ef88e ): // "BouncyObj_PlayerCollisionOff"
			m_bouncyobj_flags |= BOUNCYOBJ_FLAG_PLAYER_COLLISION_OFF;
			return MF_TRUE;

        // @script | BouncyObj_Go | The vector can be anything you want...
        // the initial velocity.  It's optional, without it the object will
        // fall from it's current location.  If it's already on the ground
        // probably nothing will happen
        // @uparmopt (0, 0, 0) | vector
		case ( 0xfea0e952 ): // "BouncyObj_Go"
		{
			Mth::Vector vel;
			vel.Set( );
			pParams->GetVector( NONAME, &vel );
			if ( m_state == BOUNCYOBJ_STATE_BOUNCING )
			{
				Dbg_Message( "\n%s\nWarning:  Calling BouncyObj_Go when object is already bouncing.", pScript->GetScriptInfo( ) );
			}
			if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
			{
				vel.ConvertToMaxCoords( );
			}
			bounce( vel );
			return MF_TRUE;
		}
	}
	return MF_NOT_EXECUTED;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CBouncyComponent::InitFromStructure( Script::CStruct* pParams )
{

	m_time = 0.01666666f;			// Patch!!!!

	m_state = BOUNCYOBJ_STATE_INIT;

	m_bounce_mult = 0.9f;
	m_bouncyobj_flags = 0;

	// Bouncy object properties:
	m_random_dir = 10;
	pParams->GetInteger( 0x767f323f, &m_random_dir ); // RandomDirectionOnBounce
	
	m_bounce_mult = 0.6f;
	pParams->GetFloat( 0xaf6034df, &m_bounce_mult ); // BounceMult
	
	m_min_bounce_vel = 3;
	pParams->GetFloat( 0xff08ee44, &m_min_bounce_vel );  // MinBounceVel
	m_min_bounce_vel = FEET_TO_INCHES( m_min_bounce_vel );
	
	m_rot_per_axis = 360;
	pParams->GetInteger( 0x33ee064f, &m_rot_per_axis ); // ConstRot
	
	m_bounce_rot = 45;
	pParams->GetInteger( 0xde14151d, &m_bounce_rot ); // BounceRot
	
	m_rest_orientation_type=0;
	pParams->GetInteger( 0xc0f30f40, &m_rest_orientation_type ); // RestOrientationType

	m_gravity = 32;
	pParams->GetFloat( 0xa5e2da58, &m_gravity ); // Gravity
	m_gravity = FEET_TO_INCHES( m_gravity );
	
	m_bounciness = 1.0f;
	pParams->GetFloat( 0x373d4e7d, &m_bounciness ); // Bounciness
	
	m_bounce_sound=0;
	pParams->GetChecksum( 0x1fb2f60c, &m_bounce_sound ); // BounceSound
	
	m_hit_sound=0;
	pParams->GetChecksum( 0x29e2e9e0, &m_hit_sound ); // HitSound
	
	m_up_mag = 32.0f;
	pParams->GetFloat( 0x3eca5c95, &m_up_mag ); // UpMagnitude
	m_up_mag = FEET_TO_INCHES( m_up_mag );
	
	m_destroy_when_done = !pParams->ContainsFlag( 0x65ba5a75 ); // NoDestroy
	
	m_min_initial_vel = 15; // feet per second...
	pParams->GetFloat( 0x96763c79, &m_min_initial_vel ); // MinInitialVel
	m_min_initial_vel = FEET_TO_INCHES( m_min_initial_vel );
	
	// Set up collision

	uint32	col_type_checksum = Nx::vCOLL_TYPE_NONE;
	pParams->GetChecksum("CollisionMode", &col_type_checksum);
	#if 0

	
	// TODO - handle getting bouncing box from a collision component
	Nx::CollType col_type = Nx::CCollObj::sConvertTypeChecksum(col_type_checksum);
	
	if (col_type != Nx::vCOLL_TYPE_NONE)
	{
		m_bounce_collision_radius=0.0f;
		m_skater_collision_radius_squared=0.0f;
//		InitCollision(col_type, p_coll_tri_data);
		// Calc radius from bounding box.
		if (((CMovingObject*)(GetObject()))->GetCollisionObject())
		{
			Nx::CCollObjTriData *p_tri_data=((CMovingObject*)(GetObject()))->GetCollisionObject()->GetGeometry();
			Dbg_MsgAssert(p_tri_data,("NULL p_tri_data"));
			Mth::CBBox bbox=p_tri_data->GetBBox();
			m_bounce_collision_radius=Mth::Distance(bbox.GetMax(),bbox.GetMin())/2.0f;
			m_skater_collision_radius_squared=m_bounce_collision_radius*m_bounce_collision_radius;
		}
	}
	else
	#endif
	
	{
		m_bounce_collision_radius=0.0f;
		m_skater_collision_radius_squared=50.0f;
		pParams->GetFloat("CollisionRadius",&m_skater_collision_radius_squared);
		m_skater_collision_radius_squared*=m_skater_collision_radius_squared;
	}
	

	// We need to copy in any script struct parameters, as they might have generated temporarily
	pParams->GetChecksum(CRCD(0x58cdc0f0,"CollideScript"),&m_collide_script);
	Script::CStruct * p_collide_script_params = NULL;
	pParams->GetStructure(CRCD(0x24031741,"CollideScriptParams"),&p_collide_script_params);		
	if (p_collide_script_params)
	{
		mp_collide_script_params = new Script::CStruct;
		*mp_collide_script_params += *p_collide_script_params;
	}
	
	pParams->GetChecksum(CRCD(0x2d075f90,"BounceScript"),&m_bounce_script);
	Script::CStruct * p_bounce_script_params = NULL;
	pParams->GetStructure(CRCD(0x346b0c03,"BounceScriptParams"),&p_bounce_script_params);		
	if (p_bounce_script_params)
	{
		mp_bounce_script_params = new Script::CStruct;
		*mp_bounce_script_params += *p_bounce_script_params;
	}
	
	
//	((CMovingObject*)(GetObject()))->RunNodeScript(0x2d075f90/*BounceScript*/,0x346b0c03/*BounceScriptParams*/);
	


	m_pos = GetObject()->m_pos;
	m_matrix = GetObject()->m_matrix;


}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CBouncyComponent::Update()
{

	Mdl::Skate *	skate_mod =  Mdl::Skate::Instance();
	uint32 numSkaters = skate_mod->GetNumSkaters( );

	m_time = Tmr::FrameLength();

	if ( !( m_bouncyobj_flags & BOUNCYOBJ_FLAG_PLAYER_COLLISION_OFF ) )
	{
		if ( m_state != BOUNCYOBJ_STATE_BOUNCING || ( ( !m_destroy_when_done ) && m_bounce_count ) )
		{
			uint32 i;
			for ( i = 0; i < numSkaters; i++ )
			{
				CSkater *pSkater = skate_mod->GetSkater( i );
				
				// Very simple collision check ...
				// Seems to work fine.
				if ( Mth::DistanceSqr( m_pos, pSkater->m_pos ) < m_skater_collision_radius_squared )
				{
					// Trigger any collide-script specified in the object's node.
					
							   
					#if 0
					// TODO - handle this RunNodeScript - IF IT IS ACTUALLY EVER USED!!!!!
					((CMovingObject*)GetObject())->RunNodeScript(0x58cdc0f0/*CollideScript*/,0x24031741/*CollideScriptParams*/);
					#else
					// This is also rather messy.  Seems like it would be better
					// to have this handled by an exception
					if (m_collide_script)
					{
						Script::CScript *p_script=Script::SpawnScript(m_collide_script,mp_collide_script_params);
						#ifdef __NOPT_ASSERT__
						p_script->SetCommentString("CBouncyComponent collide script");
						#endif
						Dbg_MsgAssert(p_script,("NULL p_script"));
						p_script->mpObject=GetObject();;
						p_script->Update();
					}
					#endif			   

					// Trigger the bounce.
					Mth::Vector vel;
					vel = pSkater->GetVel( );
					bounce_from_object_vel( vel, pSkater->m_pos );
					continue;
				}
			}
		}
	}
	
    switch ( m_state )
	{
		case ( BOUNCYOBJ_STATE_INIT ):
			m_state = BOUNCYOBJ_STATE_IDLE;
			break;
			
		case ( BOUNCYOBJ_STATE_IDLE ):
			break;

		case ( BOUNCYOBJ_STATE_BOUNCING ):
			
			// TODO:  The following is needed only to update the position of the object
			// This should be done by updating the postion of the model component
			#if 0
			// TODO - delete this code if it is not used
			((CMovingObject*)GetObject())->MovingObj_Update( );    
			#endif
			
			do_bounce( );
			break;
						
		default:
			Dbg_MsgAssert( 0,( "Unknown substate." ));
			break;
	}


}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


#define DTR DEGREES_TO_RADIANS

float get_closest_allowable_angle( float current, float allowableAngle )
{
	
	float ang;
	float halfAllowable = allowableAngle / 2.0f;
	ang = DTR( -180.0f );
	ang += halfAllowable;
	while ( ang < DTR( 180.0f ) )
	{
		if ( current < ang )
		{
			return ( ang - ( halfAllowable ) );
		}
		ang += allowableAngle;
	}
	return ( ang - halfAllowable );
}

void CBouncyComponent::land_on_top_or_bottom( Mth::Vector &rot )
{
	
	// x to nearest acceptable:
	if ( ( rot[ X ] > DTR( 90.0f ) ) || ( rot[ X ] < DTR( -90.0f ) ) )
		rot[ X ] = DTR( 180.0f );
	else
		rot[ X ] = 0.0f;
	// z to nearest acceptable:
	if ( ( rot[ Z ] > DTR( 90.0f ) ) || ( rot[ Z ] < DTR( -90.0f ) ) )
		rot[ Z ] = DTR( 180.0f );
	else
		rot[ Z ] = 0.0f;
}

void CBouncyComponent::land_on_any_face( Mth::Vector &rot )
{
	
	rot[ X ] = get_closest_allowable_angle( rot[ X ], DTR( 90.0f ) );
	rot[ Y ] = get_closest_allowable_angle( rot[ Y ], DTR( 90.0f ) );
	rot[ Z ] = get_closest_allowable_angle( rot[ Z ], DTR( 90.0f ) );
}

#define CONE_ANGLE	110.0f

void CBouncyComponent::land_traffic_cone( Mth::Vector &rot )
{
	
/*	if ( rot[ X ] < -DTR( CONE_ANGLE / 2.0f ) )
		rot[ X ] = -DTR( CONE_ANGLE );
	else if ( rot[ X ] > DTR( CONE_ANGLE / 2.0f ) )
		rot[ X ] = DTR( CONE_ANGLE );
	else*/
		rot[ X ] = 0;

	if ( rot[ Z ] < -DTR( CONE_ANGLE / 2.0f ) )
		rot[ Z ] = -DTR( CONE_ANGLE );
	else if ( rot[ Z ] > DTR( CONE_ANGLE / 2.0f ) )
		rot[ Z ] = DTR( CONE_ANGLE );
	else
		rot[ Z ] = 0;

//	rot[ Y ] = 0;
}

void CBouncyComponent::set_up_quats( void )
{
	
	Mth::Vector rot;// = currentRot;
	m_matrix.GetEulers( rot );
	Dbg_MsgAssert( rot[ X ] <= DTR( 180.0f ),( "Trig functions range from 0 to 360, not -180 to 180." ));
	Dbg_MsgAssert( rot[ Y ] <= DTR( 180.0f ),( "Trig functions range from 0 to 360, not -180 to 180." ));
	Dbg_MsgAssert( rot[ Z ] <= DTR( 180.0f ),( "Trig functions range from 0 to 360, not -180 to 180." ));
	
	m_quatrot_time_elapsed = 0.0f;

	// find out the amount we have to rotate:
	switch ( m_rest_orientation_type )
	{
		case ( 1 ):  // Upside down, or rightside up:
			land_on_top_or_bottom( rot );
			break;

		case ( 2 ):
			land_on_any_face( rot );
			break;

		case ( 3 ):
			land_traffic_cone( rot );
			break;
			
		case ( 0 ):
		default:
			Dbg_MsgAssert( 0,( "Unknown rest orientation type: %d", m_rest_orientation_type ));
			return;
			break;
	}
	if ( !mp_bounce_slerp )
	{
		mp_bounce_slerp = new Mth::SlerpInterpolator;
	}
	
	Mth::Matrix temp( rot[ X ], rot[ Y ], rot[ Z ] );
	mp_bounce_slerp->setMatrices( &m_matrix, &temp );
	m_quat_initialized = true;
}

#define QUAT_ROT_TIME 0.5f

bool CBouncyComponent::rotate_to_quat( void )
{
	
	float percent;
	m_quatrot_time_elapsed += m_time;
	if ( m_quatrot_time_elapsed >= QUAT_ROT_TIME )
	{
		percent = 1.0f;
	}
	else
	{
		percent = m_quatrot_time_elapsed / QUAT_ROT_TIME;
	}

	mp_bounce_slerp->getMatrix( &m_matrix, percent );
	return ( percent == 1.0f );
}

#define MAX_SKATER_VEL 1000.0f  // not exact... just about what the max would be.

void CBouncyComponent::do_bounce( void )
{
	m_old_pos = m_pos;
	
	int temp;
	switch ( m_substate )
	{
		case ( 0 ):
		
			// generate a new constant rotation:
			temp = Mth::Rnd( 8 );
			m_rotation[ X ] = DEGREES_TO_RADIANS( ( float ) ( ( m_rot_per_axis >> 2 ) + ( Mth::Rnd( ( m_rot_per_axis >> 2 ) * 3 ) ) ) );
			if ( temp & 1 )
			{
				m_rotation[ X ] *= -1.0f;
			}
			m_rotation[ Y ] = DEGREES_TO_RADIANS( ( float ) ( ( m_rot_per_axis >> 2 ) + ( Mth::Rnd( ( m_rot_per_axis >> 2 ) * 3 ) ) ) );
			if ( temp & 2 )
			{
				m_rotation[ Y ] *= -1.0f;
			}
			m_rotation[ Z ] = DEGREES_TO_RADIANS( ( float ) ( ( m_rot_per_axis >> 2 ) + ( Mth::Rnd( ( m_rot_per_axis >> 2 ) * 3 ) ) ) );
			if ( temp & 4 )
			{
				m_rotation[ Z ] *= -1.0f;
			}
			m_rotation[ W ] = 1.0f;
			m_quat_initialized = false;
			m_substate++;
		// intentional fall through:
		case ( 1 ):
		{
			Mth::Vector rot;
			m_pos += m_vel * m_time;
			if ( m_quat_initialized )
			{
				rotate_to_quat( );
			}
			else
			{
				rot = m_rotation;
				rot *= m_time;
				m_matrix.RotateLocal( rot );
			}
			
			
			// add the radius of the object so we don't stick through the ground:
			Mth::Vector velNormalized = m_vel;
			velNormalized.Normalize( );
			Mth::Vector colPoint = m_pos + ( velNormalized * m_bounce_collision_radius );
			m_vel[ Y ] -= m_gravity * m_time;
			CFeeler feeler;
			feeler.SetLine(m_old_pos, colPoint);
			if ( feeler.GetCollision())
			{
				#if 0
				// TODO - make it work, or remove if not used
				((CMovingObject*)(GetObject()))->RunNodeScript(0x2d075f90/*BounceScript*/,0x346b0c03/*BounceScriptParams*/);
				#else
				// This is also rather messy.  Seems like it would be better
				// to have this handled by an exception
				if (m_bounce_script)
				{
					Script::CScript *p_script=Script::SpawnScript(m_bounce_script,mp_bounce_script_params);
					Dbg_MsgAssert(p_script,("NULL p_script"));
					#ifdef __NOPT_ASSERT__
					p_script->SetCommentString("CBouncyComponent bounce script");
					#endif
					p_script->mpObject=GetObject();;
					p_script->Update();
				}
				#endif

				m_bounce_count++;
				Mth::Vector v;
				Mth::Vector r;
				float dP;
				v = m_pos - m_old_pos;
				Mth::Vector n;
				n = feeler.GetNormal();
				dP = Mth::DotProduct( v, n );
				r = ( dP * 2.0f ) * n;
				r -= v;
				float origVel = m_vel.Length( );
				m_vel = -r;
				m_vel.Normalize( origVel * m_bounce_mult );
				m_vel.RotateY( DTR( ( float ) ( Mth::Rnd( 2 * m_random_dir ) - m_random_dir ) ) );
				m_pos = feeler.GetPoint() - ( velNormalized * m_bounce_collision_radius );
				if ( fabsf( m_vel[ Y ] ) < ( m_min_bounce_vel + ( 2.0f *( m_gravity * m_time ) ) ) )
				{
					if ( m_rest_orientation_type )
					{
						if ( !m_quat_initialized )
						{
							set_up_quats( );
						}
						m_substate++;
						break;
					}
					
					
					#if 0
					// TODO - make it work, or remove if not used
					// This "HigerLevel" is probably redundant for bouncy obs
					((CMovingObject*)(GetObject()))->HigherLevel( false );
					#endif					
					
					SWITCH_STATE( BOUNCYOBJ_STATE_IDLE );
					if ( m_destroy_when_done )
					{
						GetObject()->MarkAsDead( );
					}
					else
					{
						#if 0
						// TODO - get exceptions working
						GetExceptionComponentFromObject( GetObject() )->FlagException( 0x61682835 ); // DoneBouncing
						#endif
					}
					return;
				}
				
				if ( origVel > m_min_bounce_vel * 2.0f )
				{
					if ( m_bounce_sound )
					{
						// adjust volume depending on magnitude of current velocity:
						#if 0
						// TODO - Get Bouncy object sounds working with components
						float vol;
						vol = 100.0f * ( origVel / ( MAX_SKATER_VEL * m_bounciness ) );
						float pitch = 80.0f + ( float ) Mth::Rnd( 40 );
						((CMovingObject*)(GetObject()))->PlaySound_VolumeAndPan( m_bounce_sound, vol, pitch );
						#endif
					}

					// bounce rot:
					if ( m_bounce_rot )
					{
						rot[ X ] = DEGREES_TO_RADIANS( ( float )( Mth::Rnd( m_bounce_rot * 2 ) - m_bounce_rot ) );
						rot[ Y ] = DEGREES_TO_RADIANS( ( float )( Mth::Rnd( m_bounce_rot * 2 ) - m_bounce_rot ) );
						rot[ Z ] = DEGREES_TO_RADIANS( ( float )( Mth::Rnd( m_bounce_rot * 2 ) - m_bounce_rot ) );
						m_matrix.RotateLocal( rot );
					}
					m_substate = 0;
				}
				else if ( m_rest_orientation_type )
				{
					if ( !m_quat_initialized )
					{
						// rotate to the resting orientation:
						set_up_quats( );
					}
				}
			}
			break;
		}

		case ( 2 ):
			if ( rotate_to_quat( ) )
			{
				#if 0
				((CMovingObject*)(GetObject()))->HigherLevel( false );
				#endif
				
				SWITCH_STATE( BOUNCYOBJ_STATE_IDLE );
				if ( m_destroy_when_done )
				{
					GetObject()->MarkAsDead( );
				}
			}
			break;
			
		default:
			Dbg_Message( 0, "Woah!  Fire matt immediately." );
			break;
	}

	#ifdef	__DEBUG_BOUNCY__	
//	printf ("%d: %s: (%.2f,%.2f,%.2f)\n",__LINE__,__PRETTY_FUNCTION__,m_pos[X],m_pos[Y],m_pos[Z]); 
	#endif
	
	GetObject()->m_pos = m_pos;
	GetObject()->m_matrix = m_matrix;
	GetObject()->SetDisplayMatrix(m_matrix);
	
	
}

void CBouncyComponent::bounce( const Mth::Vector &vel )
{

	#ifdef	__DEBUG_BOUNCY__	
//	printf ("%d: %s: (%.2f,%.2f,%.2f),(%.2f,%.2f,%.2f)\n",__LINE__,__PRETTY_FUNCTION__,vel[X],vel[Y],vel[Z]); 
	#endif
	
	m_vel = vel;
	if ( m_hit_sound )
	{
		// adjust volume depending on magnitude of current velocity:
		#if 0
		// TODO - sound components for bouncy object
		float vol;
		vol = 100.0f * ( m_vel.Length( ) / MAX_SKATER_VEL );
		float pitch = 80.0f + ( float ) Mth::Rnd( 40 );
		((CMovingObject*)(GetObject()))->PlaySound_VolumeAndPan( m_hit_sound, vol, pitch );
		#endif
	}
	if ( m_bounciness != 1.0f )
	{
		m_vel *= m_bounciness;
	}
	m_bounce_count = 0;
	#if 0
	// TODO - is HigherLevel even needed here?  or just cut and paste from some other object?
	((CMovingObject*)(GetObject()))->HigherLevel( true );
	#endif
	SWITCH_STATE( BOUNCYOBJ_STATE_BOUNCING );
	#if 0
	// TODO - exceptions working as a component
	GetExceptionComponentFromObject( GetObject() )->FlagException( 0x7dc30ba6 ); // Bounce
	#endif
}


// Given an object velocity, then calculate the velocity to bounce by
void CBouncyComponent::bounce_from_object_vel( const Mth::Vector &vel, const Mth::Vector &pos )
{

	#ifdef	__DEBUG_BOUNCY__	
//	printf ("%d: %s: (%.2f,%.2f,%.2f),(%.2f,%.2f,%.2f)\n",__LINE__,__PRETTY_FUNCTION__,vel[X],vel[Y],vel[Z],pos[X],pos[Y],pos[Z]); 
	#endif
	
	Mth::Vector vel2 = vel;
	
	float mag;
	mag = vel2.Length( );
	if ( mag < m_min_initial_vel )
	{
		mag = m_min_initial_vel;
		vel2.Normalize( mag );
	}
	Mth::Vector dir = m_pos - pos;
	dir.Normalize( mag );
	vel2 += dir * ( ( ( float ) ( 50 + Mth::Rnd( 100 ) ) ) / 100.0f );
	vel2.Normalize( mag );
	vel2[ Y ] = ( m_up_mag * ( mag + MAX_SKATER_VEL ) ) / ( MAX_SKATER_VEL * 2.0f );
	bounce(vel2);
}

void CBouncyComponent::GetDebugInfo( Script::CStruct* p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CBouncyComponent::GetDebugInfo"));
	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  

	p_info->AddChecksum("m_collide_script",m_collide_script);
	p_info->AddStructure("mp_collide_script_params",mp_collide_script_params);

	p_info->AddChecksum("m_bounce_script",m_bounce_script);
	p_info->AddStructure("mp_bounce_script_params",mp_bounce_script_params);
#endif				 
}
	
}


================================================
FILE: Code/Gel/Components/carphysicscomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CarPhysicsComponent.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/4/2002
//****************************************************************************

#include 

#include 

#include 
#include 

#include 
#include 
#include 
	 
#include 
#include 
#include 

namespace Obj
{

//	#define vMAX_WHEEL_Y_ROT ( 70.0f )
//	#define vMAX_CAR_X_ROT ( 5.0f )
//	#define vMIN_CAR_X_ROT ( -5.0f )
//	#define vSTEP_CAR_X_ROT ( 0.125f )
//	#define vACCEL_TO_CAR_ROT_FACTOR ( 3.0f )

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// This static function is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
CBaseComponent* CCarPhysicsComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CCarPhysicsComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCarPhysicsComponent::CCarPhysicsComponent() : CBaseComponent()
{
	SetType( CRC_CARPHYSICS );
	
	m_wheelRotationX = 0.0f;
	m_wheelRotationY = 0.0f;
	m_targetWheelRotationY = 0.0f;

	m_shadowEnabled = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCarPhysicsComponent::~CCarPhysicsComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCarPhysicsComponent::InitFromStructure( Script::CStruct* pParams )
{
	// set up some car physics parameters
	m_minCarRot = Script::GetFloat( CRCD(0x877db8da,"accelCarRot"), Script::ASSERT );
	pParams->GetFloat( CRCD(0x877db8da,"accelCarRot"), &m_minCarRot );
	
	m_maxCarRot = Script::GetFloat( CRCD(0xfb3464df,"decelCarRot"), Script::ASSERT );
	pParams->GetFloat( CRCD(0xfb3464df,"decelCarRot"), &m_maxCarRot );
	
	m_stepCarRot = Script::GetFloat( CRCD(0xfd8bc8d6,"speedCarRot"), Script::ASSERT );
	pParams->GetFloat( CRCD(0xfd8bc8d6,"speedCarRot"), &m_stepCarRot );
	
	m_accelToCarRotFactor = Script::GetFloat( CRCD(0xb2c7447b,"accelCarRotFactor"), Script::ASSERT );
	pParams->GetFloat( CRCD(0xb2c7447b,"accelCarRotFactor"), &m_accelToCarRotFactor );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCarPhysicsComponent::Update()
{
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent::EMemberFunctionResult CCarPhysicsComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case CRCC(0xa2331896,"EnableCarShadow"):
			{
				int enabled = 0;
				pParams->GetInteger( NONAME, &enabled, Script::NO_ASSERT );
				m_shadowEnabled = enabled;
			}
			break;

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
			break;

	}

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCarPhysicsComponent::init_car_skeleton()
{	
	// Does some sanity checks
	Obj::CSkeletonComponent* pSkeletonComponent = (Obj::CSkeletonComponent*)(GetObject()->GetComponent( CRC_SKELETON ));
	Gfx::CSkeleton* pSkeleton = NULL;
	if ( pSkeletonComponent )
	{
		pSkeleton = pSkeletonComponent->GetSkeleton();
	}
	
	Obj::CModelComponent* pModelComponent = (Obj::CModelComponent*)(GetObject()->GetComponent( CRC_MODEL ));
	Nx::CModel* pModel = NULL;
	if ( pModelComponent )
	{
		pModel = pModelComponent->GetModel();
	}
	
	Dbg_MsgAssert( pSkeleton, ("Car has no skeleton") );
	Dbg_MsgAssert( pModel, ( "Model hasn't been set up yet" ) );
	
	Dbg_MsgAssert( pModel->GetHierarchy(), ("veh flagged as car has no hierarchy from node %s", Script::FindChecksumName(GetObject()->GetID())));
	Nx::CHierarchyObject* pHierarchyObjects = pModel->GetHierarchy();
	Dbg_MsgAssert(( pHierarchyObjects + 0 ),("Missing matrix in car"));
	Dbg_MsgAssert(( pHierarchyObjects + 2 ),("Missing matrix in car"));
	Dbg_MsgAssert(( pHierarchyObjects + 4 ),("Missing matrix in car"));

	// TODO:  This 5-bone hierarchy shouldn't be so hardcoded...
	// We should somehow make this generic so that we 
	// can use different skeletons with the car physics.

	Mth::Matrix frontTire = ( pHierarchyObjects + 2 )->GetSetupMatrix();
	frontTire *= ( pHierarchyObjects + 0 )->GetSetupMatrix();
	Mth::Matrix backTire = ( pHierarchyObjects + 4 )->GetSetupMatrix();
	backTire *= ( pHierarchyObjects + 0 )->GetSetupMatrix();

	Mth::Vector dist = frontTire[Mth::POS] - backTire[Mth::POS];
	m_distanceBetweenTires = fabs( dist.Length() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/carphysicscomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CarPhysicsComponent.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/4/2002
//****************************************************************************

#ifndef __COMPONENTS_CARPHYSICSCOMPONENT_H__
#define __COMPONENTS_CARPHYSICSCOMPONENT_H__

#include 
#include 

#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_CARPHYSICS CRCD(0xc695136d,"CarPhysics")
#define		GetCarPhysicsComponent() ((Obj::CCarPhysicsComponent*)GetComponent(CRC_CARPHYSICS))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CCarPhysicsComponent : public CBaseComponent
{
public:
    CCarPhysicsComponent();
    virtual ~CCarPhysicsComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	
	static CBaseComponent*			s_create();

public:
	void							init_car_skeleton();
	
public:
	// public only during the transition
	
	// for wheel rotation
	float							m_wheelRotationX;
	float							m_wheelRotationY;
	float							m_targetWheelRotationY;
	float							m_carRotationX;
	float							m_targetCarRotationX;
	float							m_distanceBetweenTires;
	float							m_old_vel_z;

	// for car rocking back and forth
	float							m_minCarRot;
	float							m_maxCarRot;
	float							m_stepCarRot;
	float							m_accelToCarRotFactor;

	bool							m_shadowEnabled;
};

}

#endif


================================================
FILE: Code/Gel/Components/collisioncomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CollisionComponent.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/31/2002
//****************************************************************************

#include 

#include 
#include 
#include 

#include 

#include 
#include 
#include 
							
#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// This static function is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
CBaseComponent* CCollisionComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CCollisionComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCollisionComponent::CCollisionComponent() : CBaseComponent()
{
	SetType( CRC_COLLISION );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCollisionComponent::~CCollisionComponent()
{
	if ( mp_collision )
	{
		Nx::CMovableCollMan::sRemoveCollision( mp_collision );
		// We don't want to do this after we start the PIP loading
		delete mp_collision;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCollisionComponent::InitFromStructure( Script::CStruct* pParams )
{	
	Dbg_MsgAssert( pParams, ( "No node data?" ) );
	Dbg_MsgAssert( !mp_collision, ( "Set up collision twice?" ) );

	uint32 col_type_checksum = Nx::vCOLL_TYPE_NONE;
	pParams->GetChecksum( CRCD(0x2d7e583b,"CollisionMode"), &col_type_checksum );
	
	Nx::CollType col_type = Nx::CCollObj::sConvertTypeChecksum( col_type_checksum );
	if ( col_type != Nx::vCOLL_TYPE_NONE )
	{
		uint32 class_checksum = 0;
		pParams->GetChecksum("class", &class_checksum);
		if ( class_checksum == CRCD(0xb7b3bd86,"LevelObject") )
		{
			uint32 name;
			pParams->GetChecksum(CRCD(0xa1dc81f9,"name"), &name, Script::ASSERT);
			Nx::CSector *p_sector = Nx::CEngine::sGetSector(name);
			Dbg_MsgAssert( p_sector, ( "WARNING: sGetSector(0x%x) returned NULL (%s)\n", name, Script::FindChecksumName(name) ) );
			Nx::CCollObjTriData *p_coll_tri_data = p_sector->GetCollSector()->GetGeometry();
			InitCollision(col_type, p_coll_tri_data);   
		}
		else
		{
			InitCollision(col_type);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCollisionComponent::InitCollision( Nx::CollType type, Nx::CCollObjTriData *p_coll_tri_data )
{		
	Dbg_Assert( !mp_collision );

	if ( p_coll_tri_data )
	{
		mp_collision = Nx::CCollObj::sCreateMovableCollision(type, p_coll_tri_data, 1, GetObject());
	} 
	else
	{
		Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( GetObject() );
		Dbg_MsgAssert( pModelComponent, ( "Initing collision on something with no model component" ) );

		Nx::CModel* pModel = pModelComponent->GetModel();
		Dbg_MsgAssert( pModel, ( "Initing collision on something with no model" ) );

		Ass::CAssMan * ass_man = Ass::CAssMan::Instance();
		Nx::CMesh* pMesh = (Nx::CMesh*)ass_man->GetAsset(pModel->GetFileName(), false);
		if (pMesh)
		{
			mp_collision = Nx::CCollObj::sCreateMovableCollision(type, pMesh->GetCollisionTriDataArray(), pMesh->GetCollisionTriDataArraySize(), GetObject());
		}
		else
		{
			mp_collision = Nx::CCollObj::sCreateMovableCollision(type, NULL, 0, GetObject());
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Nx::CCollObj* CCollisionComponent::GetCollision() const
{
	return mp_collision;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCollisionComponent::Teleport()
{
	// GJ:  the collision component's Update()
	// function doesn't really require finalization
	// right now, but having this assert in here
	// might help catch future errors
	Dbg_MsgAssert( GetObject()->IsFinalized(), ( "Has not been finalized!  Tell Gary!" ) );

	Update();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCollisionComponent::Update()
{
//	GJ:  On THPS4, we did the following test first before
//	updating the collision;  I think this will be handled
//	by the suspend component, so we shouldn't have to do
//  this here...
//	if ( (m_object_flags & vINVISIBLE) && !initialize )
//	{
//		return;
//	}

	// get the collision object
	Nx::CCollObj* p_coll = GetCollision();
	
	// Update collision
	// Might be problem with things being LODed off, but still needing collision?
	if ( p_coll )
	{
		// GJ TODO:  Somehow remove the collision object's dependency on the model!

		Nx::CModel*	p_model = NULL;
		Nx::CHierarchyObject* p_hierarchy = NULL;
		Obj::CModelComponent* p_model_component = GetModelComponentFromObject( GetObject() );
		if ( p_model_component )
		{
			p_model = p_model_component->GetModel();
			Dbg_MsgAssert( p_model, ( "No model?!?" ) );
			p_hierarchy = p_model->GetHierarchy();
		}

		if (p_hierarchy)
		{
			Mth::Vector translation(p_hierarchy->GetSetupMatrix()[W]);
			translation[W] = 0.0f;		// turn into vector
			p_hierarchy->GetSetupMatrix().Transform(translation);		// Re-orients the translation

			p_coll->SetWorldPosition( GetObject()->GetPos() + translation );
			p_coll->SetOrientation( p_hierarchy->GetSetupMatrix() * GetObject()->GetDisplayMatrix() );
		}
		else
		{
			p_coll->SetWorldPosition( GetObject()->GetPos() );
			p_coll->SetOrientation( GetObject()->GetDisplayMatrix() );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent::EMemberFunctionResult CCollisionComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
//    Dbg_Assert( 0 );

    return CBaseComponent::MF_NOT_EXECUTED;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/collisioncomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       CollisionComponent.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/24/2002
//****************************************************************************

#ifndef __COMPONENTS_COLLISIONCOMPONENT_H__
#define __COMPONENTS_COLLISIONCOMPONENT_H__

#include 
#include 

#include 

#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_COLLISION CRCD(0xa432dc41,"Collision")
#define		GetCollisionComponent() ((Obj::CCollisionComponent*)GetComponent(CRC_COLLISION))
#define		GetCollisionComponentFromObject(pObj) ((Obj::CCollisionComponent*)(pObj)->GetComponent(CRC_COLLISION))

namespace Nx
{
	class CCollObj;
	class CCollObjTriData;
}

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CCollisionComponent : public CBaseComponent
{
public:
    CCollisionComponent();
    virtual ~CCollisionComponent();

public:
    virtual void            		Update();
	virtual void            		Teleport();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	
    static CBaseComponent*			s_create();
	
public:
	Nx::CCollObj*					GetCollision() const;

protected:
	virtual void					InitCollision( Nx::CollType type, Nx::CCollObjTriData *p_coll_tri_data = NULL );
	Nx::CCollObj*					mp_collision;
};

}

#endif


================================================
FILE: Code/Gel/Components/emptycomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       EmptyComponent.cpp
//* OWNER:          ???
//* CREATION DATE:  ???
//****************************************************************************

// The CEmptyComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy emptycomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "Empty" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage

#include 

#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CEmptyComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CEmptyComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CEmptyComponent::CEmptyComponent() : CBaseComponent()
{
	SetType( CRC_EMPTY );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CEmptyComponent::~CEmptyComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CEmptyComponent::InitFromStructure( Script::CStruct* pParams )
{
	// ** Add code to parse the structure, and initialize the component

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*
	It's commented out here.  You will also need to comment in the 
	definition in emptycomponent.h
void CEmptyComponent::Finalize()
{
	// Virtual function, can be overridden to provided finialization to 
	// a component after all components have been added to an object
	// Usually this consists of getting pointers to other components
	// Note:  It is GUARENTEED (at time of writing) that
	// Finalize() will be called AFTER all components are added
	// and BEFORE the CCompositeObject::Update function is called
	
	// Example:
	// mp_suspend_component =  GetSuspendComponentFromObject( GetObject() );
										
										
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CEmptyComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	InitFromStructure(pParams);


}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CEmptyComponent::Update()
{
	// **  You would put in here the stuff that you would want to get run every frame
	// **  for example, a physics type component might update the position and orientation
	// **  and update the internal physics state 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CEmptyComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
/*
        // @script | DoSomething | does some functionality
		case 0xbb4ad101:		// DoSomething
			DoSomething();
			break;

        // @script | ValueIsTrue | returns a boolean value
		case 0x769260f7:		// ValueIsTrue
		{
			return ValueIsTrue() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		break;
*/

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEmptyComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CEmptyComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*	Example:
	p_info->AddInteger(CRCD(0x7cf2a233,"m_never_suspend"),m_never_suspend);
	p_info->AddFloat(CRCD(0x519ab8e0,"m_suspend_distance"),m_suspend_distance);
	*/
	
// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/emptycomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       EmptyComponent.h
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#ifndef __COMPONENTS_EMPTYCOMPONENT_H__
#define __COMPONENTS_EMPTYCOMPONENT_H__

#include 
#include 

#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_EMPTY CRCD(0x9738c23b,"Empty")

//  Standard accessor macros for getting the component either from within an object, or 
//  given an object				 
#define		GetEmptyComponent() ((Obj::CEmptyComponent*)GetComponent(CRC_EMPTY))
#define		GetEmptyComponentFromObject(pObj) ((Obj::CEmptyComponent*)(pObj)->GetComponent(CRC_EMPTY))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CEmptyComponent : public CBaseComponent
{
public:
    CEmptyComponent();
    virtual ~CEmptyComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
//	virtual	void 					Finalize();
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
};

}

#endif


================================================
FILE: Code/Gel/Components/lockobjcomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       LockObjComponent.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/31/2002
//****************************************************************************

#include 

#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
												   
// for kludgy skater stuff
#include 
#include 
#include 
#include 


// For the manager ... messy
#include 


namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// This static function is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
CBaseComponent* CLockObjComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CLockObjComponent );	
}


// And this one is the optional "register" function that provides on-time initilization
// usually of a component manager
void	CLockObjComponent::s_register()
{
	// Create and initilize the manager
	LockOb::Manager::Create();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CLockObjComponent::CLockObjComponent() : CBaseComponent()
{
	SetType( CRC_LOCKOBJ );

	m_lock_enabled = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CLockObjComponent::~CLockObjComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CLockObjComponent::InitFromStructure( Script::CStruct* pParams )
{

	m_locked_to_object_name=0;
	m_locked_to_object_id=Obj::CBaseManager::vNO_OBJECT_ID;
	m_locked_to_object_bone_name=0;
	m_lock_offset.Set();
	
	if (pParams->ContainsFlag("Off"))
	{
		EnableLock( false );
		return;
	}

	EnableLock( true );

	#if 0
	// SHOULD ALSO CLEAR OUT SOME OF THE OTHER
	// FLAGS THAT CANNOT CO-EXIST!
	GetObject()->m_movingobj_status &= ~MOVINGOBJ_STATUS_FOLLOWING_LEADER;
	GetObject()->m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
	#endif

	pParams->GetChecksum("ObjectName",&m_locked_to_object_name);
	pParams->GetVector(NONAME,&m_lock_offset);
	pParams->GetChecksum("id",&m_locked_to_object_id);
	pParams->GetChecksum("bone",&m_locked_to_object_bone_name);

	printf( "locking to object %s\n", Script::FindChecksumName(m_locked_to_object_id) );

	m_no_rotation=pParams->ContainsFlag("NoRotation");
	
	m_lag=false;
	
	m_lag_factor_range_a=1.0f;
	m_lag_factor_range_b=1.0f;
	if (pParams->GetPair("LagRange",&m_lag_factor_range_a,&m_lag_factor_range_b))
	{
		m_lag=true;
	}	
	
	m_lag_freq_a=0.0f;
	m_lag_freq_b=0.0f;
	if (pParams->GetPair("LagWobble",&m_lag_freq_a,&m_lag_freq_b))
	{
		m_lag=true;
	}	

	// The random lag phase is to ensure that two objects using the same script won't
	// wobble in phase.
	m_lag_phase=Mth::Rnd(1024);	
	uint32 t=Tmr::ElapsedTime(0)+m_lag_phase;
	t&=1023;
	float x=sinf(m_lag_freq_a*t*2.0f*3.1415926f/1024.0f) *
			sinf(m_lag_freq_b*t*2.0f*3.1415926f/1024.0f);
	
	m_lag_factor=m_lag_factor_range_a+(x+1)*(m_lag_factor_range_b-m_lag_factor_range_a)/2.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CLockObjComponent::Update()
{
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent::EMemberFunctionResult CLockObjComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{

		
	switch ( Checksum )
	{
        // @script | Obj_LockToObject | Makes the object be locked a fixed offset from some other
		// object, and use the same orientation.
        // @uparmopt tev_TramA01 | Name of the object to be locked to
        // @uparmopt (0,0,0) | Offset from the host object.
		// @flag Off | Switch off lock so that object can be moved independently again.
		// @flag NoRotation | Makes the objects rotation be left unaffected.
		// @parmopt pair | LagRange | (1,1) | The range of the lag value, which determines how quickly
		// the object will catch up to the object being followed. The lower the value the further
		// the object will lag behind. 1 means no lag at all.
		// Example which works quite well: (0.05,0.07)
		// @parmopt pair | LagWobble | (0,0) | The frequencies used to wobble the lag value between
		// the two ranges. Two frequencies are specified so that the lag will wobble in an
		// irregular manner. Example which works quite well: (0.779,0.65)
		case 0x785e57c7: // Obj_LockToObject
		{
			#if 0
			#ifdef	__NOPT_ASSERT__
			uint32 foo=0;
			if (!pParams->GetChecksum("ObjectName",&foo))
			{
				Dbg_MsgAssert(0,("\n%s\nMissing ObjectName parameter in Obj_LockToObject",pScript->GetScriptInfo()));
			}
			#endif				
			#endif				
			InitFromStructure( pParams );							
			break;
		}	

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

void CLockObjComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CLockObjComponent::GetDebugInfo"));
	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
	
	p_info->AddChecksum("m_locked_to_object_name",m_locked_to_object_name);
	p_info->AddChecksum("m_locked_to_object_id",m_locked_to_object_id);
	p_info->AddChecksum("m_locked_to_object_bone_name",m_locked_to_object_bone_name);
	p_info->AddVector("m_lock_offset",m_lock_offset.GetX(),m_lock_offset.GetY(),m_lock_offset.GetZ());
	p_info->AddFloat("m_lag_factor",m_lag_factor);
	p_info->AddFloat("m_lag_factor_range_a",m_lag_factor_range_a);
	p_info->AddFloat("m_lag_factor_range_b",m_lag_factor_range_b);
	p_info->AddFloat("m_lag_freq_a",m_lag_freq_a);
	p_info->AddFloat("m_lag_freq_b",m_lag_freq_b);
	p_info->AddFloat("m_lag_phase",m_lag_phase);
	p_info->AddChecksum("m_lag",m_lag ? 0x203b372/*True*/:0xd43297cf/*False*/);
	p_info->AddChecksum("m_no_rotation",m_no_rotation ? 0x203b372/*True*/:0xd43297cf/*False*/);
	p_info->AddChecksum("m_lock_enabled",m_lock_enabled ? 0x203b372/*True*/:0xd43297cf/*False*/);
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCompositeObject* CLockObjComponent::get_locked_to_object()
{
	CCompositeObject* p_obj=NULL;
	if (m_locked_to_object_id!=CBaseManager::vNO_OBJECT_ID)
	{
		p_obj=(CCompositeObject*)Obj::ResolveToObject(m_locked_to_object_id);
	}
	else if (m_locked_to_object_name==0x5b8ab877) // Skater
	{
		// GJ:  phase this usage out...  we shouldn't have anything
		// skater-specific in here!
		Dbg_MsgAssert(Mdl::Skate::Instance()->GetNumSkaters()==1,("Called get_locked_to_object with skater as parent when there is more than one skater"));
		p_obj=(CCompositeObject*)Mdl::Skate::Instance()->GetLocalSkater();
	}
	else
	{
		//p_obj=CMovingObject::m_hash_table.GetItem(m_locked_to_object_name);
		p_obj = (Obj::CCompositeObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID( m_locked_to_object_name );
	
	}
	
	return p_obj;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLockObjComponent::LockToObject( void )
{
	Mth::Matrix old_matrix=GetObject()->m_matrix;
	
	CCompositeObject* p_obj=get_locked_to_object();
	if (!p_obj)
	{
		// Can't find the object, oh well.
		return;
	}	

	Mth::Vector target_pos = GetObject()->m_pos;
	
	if ( m_locked_to_object_bone_name )
	{
		Gfx::CSkeleton* pSkeleton = NULL;
		if ( p_obj->GetComponent(CRC_SKELETON) )
		{
			pSkeleton = GetSkeletonComponentFromObject(p_obj)->GetSkeleton();
		}
		Dbg_MsgAssert( pSkeleton, ( "Locking object to bone on non-skeletal object" ) );
		if ( pSkeleton && pSkeleton->GetBoneMatrix( m_locked_to_object_bone_name, &GetObject()->m_matrix ) )
		{
			Mth::Vector boneOffset = GetObject()->m_matrix[Mth::POS];
			GetObject()->m_matrix[Mth::POS] = Mth::Vector( 0.0f, 0.0f, 0.0f, 1.0f );

		    GetObject()->m_matrix = GetObject()->GetMatrix() * p_obj->GetMatrix();

			target_pos = p_obj->GetDisplayMatrix().Transform( boneOffset );
			target_pos += p_obj->m_pos+m_lock_offset*GetObject()->GetMatrix();
		}
	}
	else
	{
		// Using the display matrix otherwise the object will jitter if following the skater
		// up a half pipe.
		
		// K: If the parent object is doing some extra rotations (by using the RotateDisplay command),
		// the calculation of target pos is slightly different. 
		// However, as a speed optimization only do this for the skater because currently that 
		// is the only object that does extra rotations.
		// Fixes bug where the pizza box special item was not being displayed correctly when used in
		// a create-a-trick with rotations.
		
		bool calculate_extra_rotation = false;

		CCompositeObject* p_composite_object = (CCompositeObject*)p_obj;
		Obj::CModelComponent* p_model_component = (Obj::CModelComponent*)p_composite_object->GetComponent( CRC_MODEL );
		Dbg_Assert( p_model_component );
		if ( p_model_component->HasRefObject() )
		{
			// GJ THPS5:  We're now starting to have non-skater objects needing the extra rotate 
			// display (i.e. the CAT preview skater)...  in theory, we could still do an optimization
			// where each lockobjcomponent is flagged to support the rotatedisplay, but i decided
			// to just kludge it for now by checking for a refObject.
			calculate_extra_rotation = true;
		}
		else if ( p_obj->GetID() < Mdl::Skate::vMAX_SKATERS )
		{
			// If it's the skater ...
			calculate_extra_rotation = true;
		}

		if ( calculate_extra_rotation )
		{
			Mth::Matrix display_matrix;
			p_model_component->GetDisplayMatrixWithExtraRotation( display_matrix );
			
			// Note that p_obj->GetPos() is not added because the offset is contained in the matrix.
			target_pos=m_lock_offset*display_matrix;
			
			// but now zero out the translation part of the matrix otherwise the object will be rendered wrong.
			Mth::Vector zero(0.0f,0.0f,0.0f,1.0f);
			display_matrix.SetPos(zero);
			GetObject()->m_matrix=display_matrix;
		}
		else
		{
			GetObject()->m_matrix=((CCompositeObject*)p_obj)->GetDisplayMatrix();
			target_pos=p_obj->GetPos()+m_lock_offset*GetObject()->m_matrix;
		}
	}

	if (m_lag)
	{
		GetObject()->m_pos=GetObject()->GetPos()+(target_pos-GetObject()->GetPos())*m_lag_factor;
		
		uint32 t=Tmr::ElapsedTime(0)+m_lag_phase;
		t&=1023;
		float x=sinf(m_lag_freq_a*t*2.0f*Mth::PI/1024.0f) *
				sinf(m_lag_freq_b*t*2.0f*Mth::PI/1024.0f);
		
		m_lag_factor=m_lag_factor_range_a+(x+1)*(m_lag_factor_range_b-m_lag_factor_range_a)/2.0f;
	}
	else
	{
		GetObject()->m_pos=target_pos;
	}
	
	// take the object's velocity as well
	GetObject()->SetVel(p_obj->GetVel());
	
	// If the objects rotation is required to be preserved, recover the old value.
	// Alan's ferris wheel cars use this.
	if (m_no_rotation)
	{
		GetObject()->m_matrix[Mth::UP]=old_matrix[Mth::UP];
		GetObject()->m_matrix[Mth::AT]=old_matrix[Mth::AT];
		GetObject()->m_matrix[Mth::RIGHT]=old_matrix[Mth::RIGHT];
		//printf("m_pos=(%f,%f,%f)\n",m_pos[X],m_pos[Y],m_pos[Z]);
	}	
	
	// GJ:  This function is called from a task that
	// happens after all the matrices have already
	// been sent to the rendering code for this frame...
	// so we'll need to re-apply any new m_pos/m_matrix
	// changes to the rendered model
	// (suggestion for THPS5...  have a render task
	// that sends all the matrices to the model after
	// the object update task has finished...)
	Nx::CModel* pModel = NULL;
	if ( GetObject()->GetComponent( CRC_MODEL ) )
	{
		pModel = ((Obj::CModelComponent*)GetObject()->GetComponent(CRC_MODEL))->GetModel();
	}
	
	if ( pModel )
	{
		Gfx::CSkeleton* pSkeleton = NULL;
		if ( GetObject()->GetComponent( CRC_SKELETON ) )
		{
			pSkeleton = ((Obj::CSkeletonComponent*)GetObject()->GetComponent(CRC_SKELETON))->GetSkeleton();
		}

		Mth::Matrix rootMatrix;
		rootMatrix = GetObject()->GetDisplayMatrix();
		rootMatrix[Mth::POS] = GetObject()->GetPos();
		rootMatrix[Mth::POS][W] = 1.0f;
		bool should_animate = false;
		pModel->Render( &rootMatrix, !should_animate, pSkeleton );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/




	
}



// The following is the LockOb manager, which iterates over the lockob components 
// to lock them to whatever they are locked to

namespace LockOb
{
// The purpose of this manager is just to update all the locked objects, by calling
// CMovingObject::LockToObject on all objects that have had Obj_LockToObject run on them.
// It is a manager so that it can be given a task with a priority such that it is called
// after all the objects are updated.
// The reason the lock logic cannot be done in the object update is because sometimes the parent's 
// update might happen after the child's, causing a frame lag. This was making the gorilla appear
// behind the tram seat in the zoo when the tram was being skitched. (TT2324)

DefineSingletonClass( Manager, "Locked object Manager" )

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::Manager()
{
	mp_task = new Tsk::Task< Manager > ( s_code, *this,
										Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_LOCKED_OBJECT_MANAGER_LOGIC );

	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	mlp_manager->AddLogicTask( *mp_task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager()
{
	delete mp_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::s_code( const Tsk::Task< Manager >& task )
{
	
//	if (Mdl::FrontEnd::Instance()->GamePaused())
//	{
//		// Must not update if game paused, because the game could be running a replay,
//		// which would mean the objects' models will have been deleted, causing the 
//		// LockToObject function to assert.
//		return;
//	}

	// new fast way, just go directly to the components, if any
	Obj::CLockObjComponent *p_lock_obj_component = static_cast( Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRC_LOCKOBJ ));
	while( p_lock_obj_component )
	{
		if ( p_lock_obj_component->IsLockEnabled() )
		{
			p_lock_obj_component->LockToObject();
		}	
		p_lock_obj_component = static_cast( p_lock_obj_component->GetNextSameType());
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}



================================================
FILE: Code/Gel/Components/lockobjcomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       LockObjComponent.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/4/2002
//****************************************************************************

#ifndef __COMPONENTS_LOCKOBJCOMPONENT_H__
#define __COMPONENTS_LOCKOBJCOMPONENT_H__

#include 
#include 
#include 
#include 

#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_LOCKOBJ CRCD(0x5cb5cb7e,"LockObj")
#define		GetLockObjComponent() ((Obj::CLockObjComponent*)GetComponent(CRC_LOCKOBJ))
#define		GetLockObjComponentFromObject(pObj) ((Obj::CLockObjComponent*)(pObj)->GetComponent(CRC_LOCKOBJ))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CLockObjComponent : public CBaseComponent
{
public:
    CLockObjComponent();
    virtual ~CLockObjComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );

	virtual void					GetDebugInfo(Script::CStruct *p_info);
	
	static CBaseComponent*			s_create();
	static void						s_register();

protected:
	CCompositeObject*				get_locked_to_object();

public:
	void							LockToObject();
	bool							IsLockEnabled() const {return m_lock_enabled;}
	void							EnableLock( bool enabled ) {m_lock_enabled = enabled;}
	
protected:
	uint32							m_locked_to_object_name;
	uint32							m_locked_to_object_id;
	uint32							m_locked_to_object_bone_name;
	Mth::Vector						m_lock_offset;
	float							m_lag_factor;
	float							m_lag_factor_range_a;
	float							m_lag_factor_range_b;
	float 							m_lag_freq_a;
	float 							m_lag_freq_b;
	uint32							m_lag_phase;
	bool							m_lag;
	bool							m_no_rotation;

protected:
	bool							m_lock_enabled;
};

}

namespace LockOb
{
// The purpose of this manager is just to update all the locked objects, by calling
// CMovingObject::LockToObject on all objects that have had Obj_LockToObject run on them.
// It is a manager so that it can be given a task with a priority such that it is called
// after all the objects are updated.
// The reason the lock logic cannot be done in the object update is because sometimes the parent's 
// update might happen after the child's, causing a frame lag. This was making the gorilla appear
// behind the tram seat in the zoo when the tram was being skitched. (TT2324)

class Manager  : public Spt::Class
{
	Tsk::Task< Manager > *mp_task;
	static	Tsk::Task< Manager >::Code 	s_code;

public:
	Manager();
	~Manager();
	
private:	
	DeclareSingletonClass( Manager );
};
};



#endif


================================================
FILE: Code/Gel/Components/modelcomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ModelComponent.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/17/2002
//****************************************************************************

#include 

#include 
									
#include 
#include 

#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 								
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
					 
namespace Obj
{

#define vMAX_PATH (512)
    
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::init_model_from_level_object( uint32 checksumName )
{
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksumName);
	Dbg_MsgAssert( p_sector, ( "WARNING: sGetSector(0x%x) returned NULL (%s)\n", checksumName, Script::FindChecksumName(checksumName) ) );
	if ( p_sector )
	{
		// need to clone the source, not the instance?
		Nx::CGeom* pGeom = p_sector->GetGeom();
		if( pGeom )
		{
			Nx::CGeom* pClonedGeom = pGeom->Clone( true );
			pClonedGeom->SetActive(true);
			mp_model->AddGeom( pClonedGeom, 0 );
			m_isLevelObject = true;
		}
		// Also get the collision data pointer
//				Dbg_Assert(p_sector->GetCollSector());
//				Nx::CCollObjTriData* p_coll_tri_data = p_sector->GetCollSector()->GetGeometry();
//				Dbg_Assert(p_coll_tri_data);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// This static function is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
CBaseComponent *	CModelComponent::s_create()
{
	return static_cast(new CModelComponent);	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CModelComponent::CModelComponent() : CBaseComponent()
{
	SetType(CRCD(0x286a8d26,"Model"));
    mp_model = Nx::CEngine::sInitModel();
	
	// since it's uninitialized
	m_display_rotation_offset.Set( 0.0f, 0.0f, 0.0f );		
	
	mpDisplayRotationInfo[0].Clear();
	mpDisplayRotationInfo[1].Clear();
	mpDisplayRotationInfo[2].Clear();

	m_numLODs = 0;
	m_isLevelObject = false;
	
	mDisplayOffset.Set();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CModelComponent::~CModelComponent()
{
    Dbg_MsgAssert( mp_model, ( "No model" ) );
    Nx::CEngine::sUninitModel( mp_model );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CModelComponent::InitFromStructure( Script::CStruct* pParams )
{
	// needs to come before the geoms get added,
	// because otherwise we've cause some fragmentaton
	// when we switch models...
	if ( pParams->ContainsFlag( CRCD(0x10079f2d,"UseModelLights") ) )
	{
		if ( !mp_model->GetModelLights() )
		{
			mp_model->CreateModelLights();
			Nx::CModelLights *p_lights = mp_model->GetModelLights();
			p_lights->SetPositionPointer(&GetObject()->m_pos);
		}
	}
	else
	{
		if ( mp_model->GetModelLights() )
		{
			mp_model->DestroyModelLights();
		}
	}

	// this function assumes that the skeletoncomponent
	// will be correctly added BEFORE the modelcomponent
	
	// not all models are initialized using InitFromStructure,
	// so we should find someplace better to put the following 
	// call to SetSkeleton()
	CSkeletonComponent *p_skeleton_component = GetSkeletonComponentFromObject(GetObject());
	if (p_skeleton_component)
	{
		GetModel()->SetSkeleton( p_skeleton_component->GetSkeleton() );
	}

	int doShadowVolume = 0;
	pParams->GetInteger( CRCD(0x2a1f92c0,"shadowVolume"), &doShadowVolume, Script::NO_ASSERT );
	mp_model->EnableShadowVolume( doShadowVolume );
	
	InitModel( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CModelComponent::InitModel( Script::CStruct* pParams )
{
	// should destroy the existing model
	mp_model->ClearGeoms();

	// update its scale, if any
	// (need to do this before init_model(), 
	// which can potentially override the scale)
	Mth::Vector theScale(1.0f,1.0f,1.0f);
	if ( Gfx::GetScaleFromParams( &theScale, pParams ) )
	{
		mp_model->SetScale( theScale );
	}
	
	const char* pModelName;
	uint32 model_name_checksum=0;
	uint32 assetName;
	uint32 refObjectName;
	uint32 cloneID;

	// check first if it is a LevelObject
	// in which case it will have a "name" which is the name of the sector
	uint32 ClassChecksum = 0;
	pParams->GetChecksum( CRCD(0x12b4e660,"Class"), &ClassChecksum );
	if ( ClassChecksum == CRCD(0xb7b3bd86,"LevelObject") )
	{
		uint32	checksumName;	
		pParams->GetChecksum( CRCD(0xa1dc81f9,"Name"), &checksumName, Script::ASSERT );
		init_model_from_level_object(checksumName);
	}
	else
	{
		// by default, use the asset manager
		int useAssetManager = 1;
		pParams->GetInteger( CRCD(0xc63c8d38,"use_asset_manager"), &useAssetManager, Script::NO_ASSERT );

		int texDictOffset = 0;
		pParams->GetInteger( CRCD(0xf891ac27,"texDictOffset"), &texDictOffset, Script::NO_ASSERT );

		if ( pParams->GetChecksum( CRCD(0x153a84de,"refObjectName"), &refObjectName, Script::NO_ASSERT ) )
		{
			m_refObjectName = refObjectName;
			m_hasRefObject = true;
			mp_model->SetRenderMode( Nx::vNONE );
		}
		else if ( pParams->GetChecksum( CRCD(0x86bd5b8f,"assetName"), &assetName, Script::NO_ASSERT ) )
		{														  
			// this means that the asset name was faked somehow
			// (for example, if we've loaded it up from a data stream
			// instead of a filename)
			Dbg_MsgAssert( mp_model, ( "No model" ) );

			int supportMultipleMaterialColors = 0;
			pParams->GetInteger( CRCD(0x92b43e79,"multicolor"), &supportMultipleMaterialColors, Script::NO_ASSERT );
			
			// component name doesn't matter...  give it a dummy
			uint32 componentName = 0;
			mp_model->AddGeom( assetName, componentName, supportMultipleMaterialColors );
		}
		else if ( pParams->GetChecksum( CRCD(0x7dd037b3,"cloneFrom"), &cloneID, Script::NO_ASSERT ) )
		{														  
			// Clone the geometry off of an existing model
			CCompositeObject* pObject = static_cast(CCompositeObjectManager::Instance()->GetObjectByID(cloneID));
			Dbg_MsgAssert( pObject, ( "Couldn't find object id %d to clone", cloneID ) );

			CModelComponent *p_src_model_comp = GetModelComponentFromObject(pObject);
			Dbg_MsgAssert( p_src_model_comp, ( "Couldn't find model component in object id %d", cloneID ) );
			Dbg_MsgAssert( p_src_model_comp->mp_model, ( "Couldn't find CModel in model component of object id %d", cloneID ) );

			uint32 geomName;
			Script::CArray *p_geom_array=NULL;
			if ( pParams->GetChecksum( CRCD(0xe7308c1f,"geom"), &geomName, Script::NO_ASSERT ) )
			{
				Nx::CGeom *p_orig_geom = p_src_model_comp->mp_model->GetGeom(geomName);
				Dbg_MsgAssert(p_orig_geom, ("Couldn't find CGeom %s", Script::FindChecksumName(geomName)));

				Nx::CGeom *p_new_geom = p_orig_geom->Clone(true, mp_model);
				Dbg_MsgAssert(p_new_geom, ("Couldn't clone CGeom %s", Script::FindChecksumName(geomName)));

				p_new_geom->SetActive(true);
				mp_model->AddGeom(p_new_geom, geomName);
			}
			else if (pParams->GetArray(CRCD(0x44e31dff,"geoms"), &p_geom_array))
			{
				uint32 geomName;
				for (uint i = 0; i < p_geom_array->GetSize(); i++)
				{
					geomName = p_geom_array->GetChecksum(i);
					if (geomName)
					{
						Nx::CGeom *p_orig_geom = p_src_model_comp->GetModel()->GetGeom(geomName);
						Dbg_MsgAssert(p_orig_geom, ("Couldn't find CGeom %s", Script::FindChecksumName(geomName)));

						Nx::CGeom *p_new_geom = p_orig_geom->Clone(true, mp_model);
						Dbg_MsgAssert(p_new_geom, ("Couldn't clone CGeom %s", Script::FindChecksumName(geomName)));

						p_new_geom->SetActive(true);
						mp_model->AddGeom(p_new_geom, geomName);
					}
				}
			}
		}
		else if ( pParams->GetText( CRCD(0x286a8d26,"model"), &pModelName, Script::NO_ASSERT )
			|| pParams->GetText( CRCD(0xb974f2fc,"modelName"), &pModelName, Script::NO_ASSERT ) )
		{
			// TODO:  If the model name is "none", then we shouldn't
			// have added the modelcomponent in the first place...
			// we should have some kind of higher-level logic decide
			// whether or not to create the modelcomponent, based on
			// whether the node specifies:  model="none".
			if ( Script::GenerateCRC(pModelName) == CRCD(0x806fff30,"none") )
			{
				;	// do nothing
			}
			else
			{
				char fullModelName[vMAX_PATH];

				uint32 skeletonName;
				if ( pParams->GetChecksum( CRCD(0x9794932,"skeletonName"), &skeletonName, Script::NO_ASSERT ) )
				{
					// if it's a skinned model
					Gfx::GetModelFileName(pModelName, ".skin", fullModelName);
				}
				else
				{
					// if it's a nonskinned model
					Gfx::GetModelFileName(pModelName, ".mdl", fullModelName);
				}

				Str::LowerCase( fullModelName );

				bool forceTexDictLookup = pParams->ContainsFlag( CRCD(0x6c37fdc7,"AllowReplaceTex") );

				// TODO: remove this for thps5 (it's only used for the boards
				// in the skateshop, which need to do texture replacement, and 
				// thus cannot use the asset manager...)
				if ( strstr( fullModelName, "thps4board_" ) )
				{
					useAssetManager = false;
				}

				// Model file name should look like this:  "models/testcar/testcar.mdl"
				Dbg_MsgAssert( strlen(fullModelName) < vMAX_PATH, ( "String too long" ) );
				mp_model->AddGeom( fullModelName, 0, useAssetManager, texDictOffset, forceTexDictLookup );
				
#ifdef __PLAT_NGPS__
				int lodIndex = 0;
				// now load up the other LODs, if they exist
				for ( int i = 0; i < vNUM_LODS; i++ )
				{
					char paramName[256];
					sprintf( paramName, "modelLOD%d", lodIndex + 1 ); 
					if ( pParams->GetText( paramName, &pModelName, Script::NO_ASSERT ) )
					{
						float modelDist;
						sprintf( paramName, "modelLODdist%d", lodIndex + 1 );
						if ( pParams->GetFloat( paramName, &modelDist, Script::NO_ASSERT ) )
						{
							uint32 skeletonName;
							if ( pParams->GetChecksum( CRCD(0x9794932,"skeletonName"), &skeletonName, Script::NO_ASSERT ) )
							{
								// if it's a skinned model
								Gfx::GetModelFileName(pModelName, ".skin", fullModelName);
							}
							else
							{
								// if it's a nonskinned model
								Gfx::GetModelFileName(pModelName, ".mdl", fullModelName);
							}

							Str::LowerCase( fullModelName );
							
							mp_model->AddGeom( fullModelName, lodIndex + 1, useAssetManager, texDictOffset );
							enable_lod( lodIndex + 1, modelDist );
							lodIndex++;
						}
						else
						{
							Dbg_MsgAssert( 0, ( "Expected to find parameter for LOD model dist: %s", paramName ) );
						}
					}
				}
#endif
			}
		}
		else if ( pParams->GetChecksum( CRCD(0x286a8d26,"model"), &model_name_checksum, Script::NO_ASSERT ))
		{
			Nx::CSector *p_source_sector = Nx::CEngine::sGetSector(model_name_checksum);
			Dbg_MsgAssert(p_source_sector,("Could not find sector %s\n",Script::FindChecksumName(model_name_checksum)));
			
			Nx::CGeom *p_orig_geom = p_source_sector->GetGeom();
			Dbg_MsgAssert(p_orig_geom, ("Couldn't find CGeom for %s",Script::FindChecksumName(model_name_checksum)));

			Nx::CGeom *p_new_geom = p_orig_geom->Clone(true, (Nx::CScene*)NULL);
			Dbg_MsgAssert(p_new_geom, ("Couldn't clone CGeom %s", Script::FindChecksumName(model_name_checksum)));
			p_new_geom->SetActive(true);
			
			mp_model->AddGeom(p_new_geom, 0);
		}
	}

	// update its position
	if ( GetObject()->IsFinalized() )
	{
		FinalizeModelInitialization();
	}

	// now that the model has been created,
	// remember the bounding sphere
	m_original_bounding_sphere = mp_model->GetBoundingSphere();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// create-a-ped, create-a-skater
void CModelComponent::InitModelFromProfile( Gfx::CModelAppearance* pAppearance, bool useAssetManager, uint32 texDictOffset, uint32 buildScript  )
{
	Dbg_MsgAssert( mp_model, ( "No model?" ) );
	Nx::CModel* pModel = mp_model;

	// GJ:  shouldn't destroy the existing model
	// it's up to each build script to do this...
	// (in case we just want to re-apply a few
	// operations)
	//	pModel->ClearGeoms();

    Gfx::CSkeleton* pSkeleton = NULL;
	Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( GetObject() );
	if ( pSkeletonComponent )
	{
		pSkeleton = pSkeletonComponent->GetSkeleton();
	}

	Gfx::CModelBuilder theBuilder( useAssetManager, texDictOffset );
	if ( buildScript )
	{
		theBuilder.BuildModel( pAppearance, pModel, pSkeleton, buildScript );
	}
	else
	{
		theBuilder.BuildModel( pAppearance, pModel, pSkeleton );
	}

	// update its position
	if ( GetObject()->IsFinalized() )
	{
		FinalizeModelInitialization();
	}

	// now that the model has been created,
	// remember the bounding sphere
	m_original_bounding_sphere = pModel->GetBoundingSphere();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelComponent::enable_lod(uint32 componentName, float distance)
{
	if ( m_numLODs >= vNUM_LODS )
	{
		Dbg_MsgAssert( 0, ( "Too many LODs!" ) );
		return false;
	}

	Nx::CGeom* pGeom = mp_model->GetGeom( componentName );
	if ( !pGeom )
	{
		Dbg_MsgAssert( 0, ( "Couldn't find geom named %s", Script::FindChecksumName(componentName) ) );
		return false;
	}

//	printf( "Enabled lod %08x\n", componentName );

	// GJ TODO:  should sort the lods by distance, 
	// and check for duplicates!

	m_LODdist[m_numLODs] = distance;

	m_numLODs++;

	// GJ TODO:  each client needs to do this
	// on a case-by-case basis...

	return true;
}
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelComponent::HideGeom( uint32 geomName, bool hidden, bool propagate )
{
    if( propagate )
	{
		GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
		GameNet::PlayerInfo* player;

		player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID() );
		if( player && player->IsLocalPlayer())
		{
			Net::Client* client;
			GameNet::MsgHideAtomic msg;
			Net::MsgDesc msg_desc;

			client = gamenet_man->GetClient( player->GetSkaterNumber() );
			Dbg_Assert( client );

			//msg.m_Time = client->m_Timestamp;
			msg.m_Hide = hidden;
			msg.m_AtomicName = geomName;
			msg.m_ObjId = GetObject()->GetID();

			msg_desc.m_Data = &msg;
			msg_desc.m_Length = sizeof( GameNet::MsgHideAtomic );
			msg_desc.m_Id = GameNet::MSG_ID_SET_HIDE_ATOMIC;
			client->EnqueueMessageToServer( &msg_desc );
		}
	}

	if ( mp_model )
	{
		mp_model->HideGeom( geomName, hidden );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelComponent::GeomHidden( uint32 geomName )
{
    return (mp_model->GeomHidden( geomName ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CModelComponent::GetDisplayMatrixWithExtraRotation( Mth::Matrix& displayMatrix )
{
    CCompositeObject* pObject = GetObject();
    Dbg_MsgAssert( pObject, ( "Couldn't find parent object" ) );
	
	displayMatrix = pObject->GetDisplayMatrix();
	displayMatrix[Mth::POS] = Mth::Vector( 0.0f, 0.0f, 0.0f, 1.0f );
	
	if (mFlipDisplayMatrix)
	{
		displayMatrix[Z] = -displayMatrix[Z];
		displayMatrix[X] = -displayMatrix[X];
	}

	// Record the display matrix for use by the camera before applying any extra rotation.
//	m_camera_display_matrix=m_display_matrix;

	Mth::Matrix extra_display_rotation;
	
	// only do the math if we're not dealing w/ the identity...
	bool is_identity = true;

	float new_angle;
	if (mpDisplayRotationInfo[0].m_active)
	{
		new_angle=mpDisplayRotationInfo[0].CalculateNewAngle();	
		if ( new_angle )
		{
			if (is_identity)
			{
				extra_display_rotation.Ident();
			}
			is_identity = false;
			extra_display_rotation.RotateX(Mth::DegToRad(new_angle));
		}
	}
	
	if (mpDisplayRotationInfo[1].m_active)
	{
		new_angle=mpDisplayRotationInfo[1].CalculateNewAngle();	
		if ( new_angle )
		{
			if (is_identity)
			{
				extra_display_rotation.Ident();
			}
			is_identity = false;
			extra_display_rotation.RotateY(Mth::DegToRad(new_angle));
		}
	}
	
	if (mpDisplayRotationInfo[2].m_active)
	{
		new_angle=mpDisplayRotationInfo[2].CalculateNewAngle();	
		if ( new_angle )
		{
			if (is_identity)
			{
				extra_display_rotation.Ident();
			}
			is_identity = false;
			extra_display_rotation.RotateZ(Mth::DegToRad(new_angle));
		}
	}

	Mth::Vector off(0.0f, 0.0f, 0.0f, 0.0f);	
	if ( !is_identity )
	{
		off=m_display_rotation_offset;	
		if ( !( off[X] == 0.0f && off[Y] == 0.0f && off[Z] == 0.0f ) )
		{
	//		is_identity = false;
			off=off-off*extra_display_rotation;		  // Would zero it if it's the identity
			off=off*displayMatrix;		
		}
	
	//	if ( !is_identity )
		{
			displayMatrix=extra_display_rotation*displayMatrix;
		}
	}

//#ifdef		DEBUG_DISPLAY_MATRIX
//	dodgy_test(); printf("%d: Setting display_matrix[Y][Y] to %f, [X][X] to %f\n",__LINE__,m_display_matrix[Y][Y],m_display_matrix[X][X]);
//#endif

	displayMatrix[Mth::POS] = GetObject()->GetPos();
	displayMatrix[Mth::POS] += off;
	displayMatrix[Mth::POS] += mDisplayOffset;
	displayMatrix[Mth::POS][W] = 1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::Hide( bool shouldHide )
{
	if ( mp_model )
	{
		mp_model->Hide( shouldHide );
	}	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::Teleport()
{
	Dbg_MsgAssert( GetObject()->IsFinalized(), ( "Teleporting unfinalized component!" ) );

	FinalizeModelInitialization();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CModelComponent::Update()
{
	if ( m_hasRefObject )
	{
#ifdef __NOPT_ASSERT__
		Obj::CMovieManager* pMovieManager = Mdl::Skate::Instance()->GetMovieManager();
		Dbg_MsgAssert( !pMovieManager->IsRolling(), ( "Wasn't expecting cutscene to be rolling" ) );
#endif

		// this code was lifted from cutscenedetails.cpp,
		// which is why it has all these weird temp
		// variables referencing its own member vars.
		// it's used for doing the create-a-trick skater
		Obj::CModelComponent* pModelComponent = this;
		Obj::CCompositeObject* pCompositeObject = GetObject();

		uint32 refObjectName = pModelComponent->GetRefObjectName();
		Obj::CCompositeObject* pRefObject = (Obj::CCompositeObject*)Obj::ResolveToObject( refObjectName );
		Dbg_Assert( pRefObject );
		Obj::CModelComponent* pRefModelComponent = GetModelComponentFromObject( pRefObject );
		Dbg_Assert( pRefModelComponent );
		Nx::CModel* pRefModel = pRefModelComponent->GetModel();
		bool should_animate = true;
		Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( pCompositeObject );
		Dbg_MsgAssert( pSkeletonComponent, ( "Was expecting a skeleton component" ) );
		Dbg_Assert( pSkeletonComponent == mp_skeleton_component );

		Mth::Matrix theDisplayMatrix;
		this->GetDisplayMatrixWithExtraRotation( theDisplayMatrix );
		
		// the return matrix already takes the position (plus an additional offset)
		// into account, so we don't have to do the following:
		// theDisplayMatrix[Mth::POS] = GetObject()->GetPos();
		
		pRefModel->Render( &theDisplayMatrix, !should_animate, pSkeletonComponent->GetSkeleton() );
		pRefModel->SetBoneMatrixData( pSkeletonComponent->GetSkeleton() );
	}
	// Mick: Don't need to update it if not active, just leave it where it is
	else if (mp_model && mp_model->GetActive())
	{
		
		Dbg_MsgAssert(GetObject()->IsFinalized(),("Update() to UnFinalized Composite object %s",Script::FindChecksumName(GetObject()->GetID())));
	
		Mth::Matrix theDisplayMatrix;

		GetDisplayMatrixWithExtraRotation( theDisplayMatrix );
			
//			theDisplayMatrix = GetObject()->GetDisplayMatrix();
//			theDisplayMatrix[Mth::POS] = GetObject()->GetPos();
//			theDisplayMatrix[Mth::POS][W] = 1.0f;
		
		// TODO:  The interface between different components
		// should be more generic, maybe...
		Gfx::CSkeleton* pSkeleton = NULL;
		if ( mp_skeleton_component )
		{
			pSkeleton = mp_skeleton_component->GetSkeleton();
		}
		
		// default to true, for skeletal cars...
		bool should_animate = true;
	
		if ( mp_animation_component )
		{
			// either the animation component should cache this
			// data rather than doing the visibility test twice,
			// or it should be able to set some member inside
			// the model component (CModelComponent::MarkAnimationAsDirty?)
			should_animate = mp_animation_component->ShouldAnimate();
		}
	
	#ifdef __PLAT_NGPS__
		// if it has LODs, then hide all the unnecessary ones
		if ( m_numLODs > 0 )
		{
			// first hide all the models, including the base one (0)
			for ( int i = 0; i < m_numLODs + 1; i++ )
			{
				mp_model->HideGeom( i, true );
			}
	
			// now go through and unhide the correct one
			float distanceSqrToCamera = mp_suspend_component->GetDistanceSquaredToCamera();
			
	//		printf( "distance to camera: %f feet\n", sqrtf(distanceSqrToCamera)/12.0f );
	
			bool found = false;
			for ( int i = 0; i < m_numLODs; i++ )
			{
				if ( distanceSqrToCamera < ( m_LODdist[i] * m_LODdist[i] ) )
				{
					mp_model->HideGeom( i, false );
					found = true;
				}
			}
			if ( !found )
			{
				// then the last lod is active
				mp_model->HideGeom( m_numLODs, false );
			}
		}
	#endif
	
		// TODO:  if it's offscreen, the data shouldn't be copied over either...
	
		mp_model->Render( &theDisplayMatrix, !should_animate, pSkeleton );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::Finalize()
{
	mp_skeleton_component =  GetSkeletonComponentFromObject( GetObject() );
	mp_animation_component =  GetAnimationComponentFromObject( GetObject() );
	mp_suspend_component =  GetSuspendComponentFromObject( GetObject() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::SetModelLODDistance( int lodIndex, float distance )
{
	if ( !mp_model )
	{
		Dbg_Message( "No model!" );
		return;
	}

	if ( m_numLODs == 0 )
	{
		Dbg_Message( "Model %s has no lods", Script::FindChecksumName(GetObject()->GetID()) );
	}

	if ( lodIndex < 1 || lodIndex > m_numLODs )
	{
		Dbg_Message( "Was expecting an lod index between 1 and %d", m_numLODs);
		return;
	}

	m_LODdist[lodIndex-1] = distance;
	Dbg_Message( "ModelLODDist%d is now %f", lodIndex, distance );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::SetBoundingSphere( const Mth::Vector& newSphere )
{
	if ( mp_model )
	{
		mp_model->SetBoundingSphere( newSphere );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::UpdateBrightness()
{
	Dbg_MsgAssert(mp_model, ("UpdateBrightness: CModel is NULL"));
	Dbg_MsgAssert(mp_model->GetModelLights(), ("UpdateBrightness: MovingObject has no model lights"));
	mp_model->GetModelLights()->UpdateBrightness();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CModelComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	bool success = true;
    Script::CStruct *p_script_params;
	 
	p_script_params = NULL;
	if( pScript )
	{
		p_script_params = pScript->GetParams();
	}

	switch (Checksum)
	{
		// @script | Obj_EnableScaling | in case you want to dynamically
        // turn on scaling
		case ( 0x347ff11b ):	// Obj_EnableScaling
			if ( mp_model )
			{
				mp_model->EnableScaling( true );
			}
			break;

        // @script | Obj_DisableScaling | in case you want to dynamically
        // turn off scaling
		case ( 0xf951aa64 ):	// Obj_DisableScaling
			if ( mp_model )
			{
				mp_model->EnableScaling( false );
			}
			break;

        // @script | Obj_ApplyScaling | scale on x,y,z 
        // @parm float | x | 
        // @parm float | y | 
        // @parm float | z | 
		case ( 0x657ff1ed ):	// Obj_ApplyScaling
		{
			// GJ:  Scaling only refers to the renderable
			// model, not the collision volume...
			if ( mp_model )
			{
				Mth::Vector theScale(1.0f,1.0f,1.0f);
				if ( Gfx::GetScaleFromParams( &theScale, pParams ) )
				{
					mp_model->SetScale( theScale );
					mp_model->EnableScaling( true );
				}
			}
		}
		break;
        
		// @script | Obj_ClearColor | turns off color modulation on this resetting it to the default color
		case ( 0x6052480a ):	// Obj_ClearColor
		{
			if ( mp_model )
			{
				mp_model->ClearColor( CRCD(0xc4e78e22,"all") );
			}
		}
		break;

        // @script | Obj_SetColor | 
        // @parm int | h | hue - between 0 and 360
        // @parm int | s | saturation - between 0 and 100
        // @parm int | v | value - between 0 and 100
        case ( 0x76ecfa1c ):	// Obj_SetColor
		{
            if ( mp_model )
			{
				int h, s, v;
				if ( pParams->GetInteger( CRCD(0x6e94f918,"h"), &h, false )
					&& pParams->GetInteger( CRCD(0xe4f130f4,"s"), &s, false )
					&& pParams->GetInteger( CRCD(0x949bc47b,"v"), &v, false ) )
				{
					mp_model->ModulateColor( CRCD(0xc4e78e22,"all"), (float)h, (float)s / 100.0f, (float)v / 100.0f );
				}
			}
		}
		break;

        // @script | Obj_ReplaceTexture | Replaces a texture in the model's dictionary
        // @parm text | src | filename of the source texture
        // @parm text | dest | filename of the destination texture
        // @parmopt name | in | all | name of geom in which to look for texture (defaults to global replacement)
		// Ex:  Obj_ReplaceTexture src="knee_l.png" dest="textures/skater_m/knee_scuff"	in=skater_m_legs
		case 0x83f9be15: // Obj_ReplaceTexture
		{
			const char* pSrcTexture;
			const char* pDstTexture;
			
			success = false;
			
			Script::CArray* pArray;
			if ( pParams->GetArray( CRCD(0x5ef31148,"array"), &pArray, Script::NO_ASSERT ) )
			{
				for ( uint32 i = 0; i < pArray->GetSize(); i++ )
				{
					Script::CStruct* pSubParams = pArray->GetStructure( i );
				
					pSubParams->GetText( CRCD(0x9fbbdb72,"src"), &pSrcTexture, Script::ASSERT );
					pSubParams->GetText( CRCD(0x7799d66c,"dest"), &pDstTexture, Script::ASSERT );

					// by default, it searches globally in the model for the correct texture
					uint32 partChecksumToReplace = Nx::CModel::vREPLACE_GLOBALLY;
					pSubParams->GetChecksum( CRCD(0xa01371b1,"in"), &partChecksumToReplace, Script::NO_ASSERT );
					if ( mp_model )
					{
						if ( mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDstTexture ) )
						{
							success = true;
						}
					}
				}
			}
			else
			{
				pParams->GetText( CRCD(0x9fbbdb72,"src"), &pSrcTexture, Script::ASSERT );
				pParams->GetText( CRCD(0x7799d66c,"dest"), &pDstTexture, Script::ASSERT );

				// by default, it searches globally in the model for the correct texture
				uint32 partChecksumToReplace = Nx::CModel::vREPLACE_GLOBALLY;
				pParams->GetChecksum( CRCD(0xa01371b1,"in"), &partChecksumToReplace, Script::NO_ASSERT );
				if ( mp_model )
				{
					if ( mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDstTexture ) )
					{
						success = true;
					}
				}
			}
		}
		break;
        		
		// @script | Obj_ReplaceSpriteTexture | Replaces a texture in the model's dictionary
		// @parm text | src | filename of the source texture
		// @parm name | dest | name of the destination texture
		// @parmopt name | in | all | name of geom in which to look for texture (defaults to global replacement)
		// Ex:  Obj_ReplaceSpriteTexture src="knee_l.png" dest=knee_scuff	in=skater_m_legs
		case 0x9a478afe: // Obj_ReplaceSpriteTexture
		{
			const char* pSrcTexture;
			uint32 dest_checksum = 0;

			pParams->GetText( CRCD(0x9fbbdb72,"src"), &pSrcTexture, Script::ASSERT );
			pParams->GetChecksum( CRCD(0x7799d66c,"dest"), &dest_checksum, Script::ASSERT );

			// by default, it searches globally in the model for the correct texture
			uint32 partChecksumToReplace = Nx::CModel::vREPLACE_GLOBALLY;
			pParams->GetChecksum( CRCD(0xa01371b1,"in"), &partChecksumToReplace, Script::NO_ASSERT );
			if ( mp_model )
			{
				success = mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, dest_checksum );
			}
			else
			{
				success = false;
			}
		}
		break;

		// @script | Obj_ClearGeoms | removes the model's geoms
		case 0xbd18e2e3: // Obj_ClearGeoms
			if ( mp_model )
			{
				mp_model->ClearGeoms();
			}
			break;
        
		// @script | Obj_HasModelLights | tests existence of model lights
		case 0xe11f85e8: // Obj_HasModelLights
			{
				Dbg_MsgAssert(mp_model, ("Obj_HasModelLights: CModel is NULL"));
				
				return mp_model->GetModelLights() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			}
			break;

		// @script | Obj_UpdateBrightness | the normal lighting code assumes
		// that the collision code will be run once a frame to copy over
		// the m_ambient_base_color into the m_ambient_mod_color.  unfortunately,
		// the cutscene objects' ref objects don't go through this code, so I had to
		// add this function to explicitly copy over the data...  there's probably
		// a cleaner way to do it, but I didn't want to break the existing code.
		case 0x1c16332e: // Obj_UpdateBrightness
			Dbg_MsgAssert(mp_model, ("Obj_UpdateBrightness: CModel is NULL"));
			Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_UpdateBrightness: MovingObject has no model lights"));
			mp_model->GetModelLights()->UpdateBrightness();
			break;

		// @script | Obj_EnableAmbientLight | enables the ambient model light
		case 0xa14d6372: // Obj_EnableAmbientLight
			Dbg_MsgAssert(mp_model, ("Obj_EnableAmbientLight: CModel is NULL"));
			Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_EnableAmbientLight: MovingObject has no model lights"));
			mp_model->GetModelLights()->EnableAmbientLight(true);
			break;

		// @script | Obj_DisableAmbientLight | disables the ambient model light
		case 0x8a445e26: // Obj_DisableAmbientLight
			Dbg_MsgAssert(mp_model, ("Obj_DisableAmbientLight: CModel is NULL"));
			Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_DisableAmbientLight: MovingObject has no model lights"));
			mp_model->GetModelLights()->EnableAmbientLight(false);
			break;

		// @script | Obj_EnableDiffuseLight | enables a diffuse model light
		// @parm int | index | Light number
		case 0xed5d1550: // Obj_EnableDiffuseLight
		{
			int index;

			if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
			{
				Dbg_MsgAssert(0, ("Obj_EnableDiffuseLight: Can't find 'index' of light"));
			}

			Dbg_MsgAssert(mp_model, ("Obj_EnableDiffuseLight: CModel is NULL"));
			Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_EnableDiffuseLight: MovingObject has no model lights"));
			mp_model->GetModelLights()->EnableDiffuseLight(index, true);
			break;
		}

		// @script | Obj_DisableDiffuseLight | disables a diffuse model light
		// @parm int | index | Light number
		case 0xc6542804: // Obj_DisableDiffuseLight
		{
			int index;

			if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
			{
				Dbg_MsgAssert(0, ("Obj_DisableDiffuseLight: Can't find 'index' of light"));
			}

			Dbg_MsgAssert(mp_model, ("Obj_DisableDiffuseLight: CModel is NULL"));
			Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_DisableDiffuseLight: MovingObject has no model lights"));
			mp_model->GetModelLights()->EnableDiffuseLight(index, false);
			break;
		}

		// @script | Obj_SetLightAmbientColor | Sets the model light ambient color
		// @parm int | r | Red
		// @parm int | g | Green
		// @parm int | b | Blue
		case 0x3cf63049: // Obj_SetLightAmbientColor
		{
			int r, g, b;
			if (!pParams->GetInteger(CRCD(0x93f60062,"r"), &r))
			{
				Dbg_MsgAssert(0, ("Obj_SetLightAmbientColor: Can't find 'r' color"));
			}
			if (!pParams->GetInteger(CRCD(0xfe2be489,"g"), &g))
			{
				Dbg_MsgAssert(0, ("Obj_SetLightAmbientColor: Can't find 'g' color"));
			}
			if (!pParams->GetInteger(CRCD(0x8e411006,"b"), &b))
			{
				Dbg_MsgAssert(0, ("Obj_SetLightAmbientColor: Can't find 'b' color"));
			}

			Image::RGBA rgb(r, g, b, 0x80);

			Dbg_MsgAssert(mp_model, ("Obj_SetLightAmbientColor: CModel is NULL"));
			Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_SetLightAmbientColor: MovingObject has no model lights"));
			mp_model->GetModelLights()->SetLightAmbientColor(rgb);
			mp_model->GetModelLights()->EnableAmbientLight(true);
			
			break;
		}

		// @script | Obj_SetLightDirection | Sets the unit direction vector of a model light
		// @parm int | index | Light number
		// @parm vector | direction | Unit direction vector (overrides heading and pitch)
		// @parm float | heading | Heading angle (in degrees)
		// @parm float | pitch | Pitch angle (in degrees)
		case 0x46a1f4a4: // Obj_SetLightDirection
		{
			int index;

			if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
			{
				Dbg_MsgAssert(0, ("Obj_SetLightDirection: Can't find 'index' of light"));
			}

			float heading, pitch;
			Mth::Vector direction(0, 0, 1, 0);
			if (pParams->GetVector(CRCD(0xc1b52e4c,"direction"), &direction))
			{
				direction[W] = 0.0f;		// This is the only way to force this to be a vector (as opposed to a point)
				//Dbg_Message("************ direction (%f, %f, %f)", direction[X], direction[Y], direction[Z]);
			}
			else if (pParams->GetFloat(CRCD(0xfd4bc03e,"heading"), &heading) && pParams->GetFloat(CRCD(0xd8604126,"pitch"), &pitch)) 
			{
				direction.RotateX(Mth::DegToRad(pitch));
				direction.RotateY(Mth::DegToRad(heading));
				//Dbg_Message("************ heading and pitch direction (%f, %f, %f)", direction[X], direction[Y], direction[Z]);
			}
			else
			{
				Dbg_MsgAssert(0, ("Obj_SetLightDirection: Can't find 'direction' or 'heading' and 'pitch' of light"));
			}

			Dbg_MsgAssert(mp_model, ("Obj_SetLightDirection: CModel is NULL"));
			Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_SetLightDirection: MovingObject has no model lights"));
			mp_model->GetModelLights()->SetLightDirection(index, direction);

			break;
		}

		// @script | Obj_SetLightDiffuseColor | Sets a model light diffuse color
		// @parm int | index | Light number
		// @parm int | r | Red
		// @parm int | g | Green
		// @parm int | b | Blue
		case 0x70e6466b: // Obj_SetLightDiffuseColor
		{
			int index;

			if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
			{
				Dbg_MsgAssert(0, ("Obj_SetLightDiffuseLight: Can't find 'index' of light"));
			}

			int r, g, b;
			if (!pParams->GetInteger(CRCD(0x93f60062,"r"), &r))
			{
				Dbg_MsgAssert(0, ("Obj_SetLightDiffuseColor: Can't find 'r' color"));
			}
			if (!pParams->GetInteger(CRCD(0xfe2be489,"g"), &g))
			{
				Dbg_MsgAssert(0, ("Obj_SetLightDiffuseColor: Can't find 'g' color"));
			}
			if (!pParams->GetInteger(CRCD(0x8e411006,"b"), &b))
			{
				Dbg_MsgAssert(0, ("Obj_SetLightDiffuseColor: Can't find 'b' color"));
			}

			Image::RGBA rgb(r, g, b, 0x80);

			Dbg_MsgAssert(mp_model, ("Obj_SetLightDiffuseColor: CModel is NULL"));
			Dbg_MsgAssert(mp_model->GetModelLights(), ("Obj_SetLightDiffuseColor: MovingObject has no model lights"));
			mp_model->GetModelLights()->SetLightDiffuseColor(index, rgb);
			mp_model->GetModelLights()->EnableDiffuseLight(index, true);
			break;
		}

		// @script | Obj_SetUVOffset | Sets the UV offset of a material pass
		// @parm name | material | material name
		// @parm int | pass | material pass
		// @parm float | u | u offset
		// @parm float | v | v offset
		case 0x57eb306e: // Obj_SetUVOffset
		{
			uint32 mat_checksum;
			if (!pParams->GetChecksum(CRCD(0x83418a6a,"material"), &mat_checksum))
			{
				Dbg_MsgAssert(0, ("Can't find parameter material"));
			}

			// Extract all the parameters
			int pass = 0;
			float u_offset;
			float v_offset;
			bool found_all;

			found_all  = pParams->GetFloat(CRCD(0xd9295c1,"u"), &u_offset);
			found_all &= pParams->GetFloat(CRCD(0x949bc47b,"v"), &v_offset);
			found_all &= pParams->GetInteger(CRCD(0x318f2bdb,"pass"), &pass);

			Dbg_MsgAssert(found_all, ("Missing one or more of the wibble offset parameters.  Must fill them all out."));

			Dbg_MsgAssert(mp_model, ("Obj_SetUVOffset: CModel is NULL"));
			mp_model->SetUVOffset(mat_checksum, pass, u_offset, v_offset);

			break;
		}

		// @script | Obj_SetUVParams | Sets the UV parameters of a material pass
		// @parm name | material | material name
		// @parm int | pass | material pass
		// @parmopt float | uoff | 0.0 | u offset
		// @parmopt float | voff | 0.0 | v offset
		// @parmopt float | uscale | 1.0 | u scale
		// @parmopt float | vscale | 1.0 | v scale
		// @parmopt float | rot | 0.0 | rotation in degrees
		case 0x812ff44d: // Obj_SetUVParams
		{
			uint32 mat_checksum;
			int pass;
			if (!pParams->GetChecksum(CRCD(0x83418a6a,"material"), &mat_checksum))
			{
				Dbg_MsgAssert(0, ("Can't find parameter material"));
			}
			if (!pParams->GetInteger(CRCD(0x318f2bdb,"pass"), &pass))
			{
				Dbg_MsgAssert(0, ("Can't find parameter pass"));
			}

			// Clear matrix
			Mth::Matrix mat;
			mat.Ident();

			// Scaling
			float uscale = 1.0f;
			float vscale = 1.0f;
			bool scale = pParams->GetFloat(CRCD(0x3fb610b7,"uscale"), &uscale);
			scale = pParams->GetFloat(CRCD(0xb9226219,"vscale"), &vscale) || scale;
			if (scale)
			{
				Dbg_MsgAssert((uscale >= 0.0f) && (vscale >= 0.0f), ("Obj_SetUVParams: Don't use negative scales."));

				mat[0][0] *= uscale;
				mat[1][1] *= vscale;
			}

			// Rotation
			float rot_deg;
			if (pParams->GetFloat(CRCD(0xe2c6589e,"rot"), &rot_deg))
			{
				mat.RotateZ(Mth::DegToRad(rot_deg));
			}

			// Offset
			float uoffset = 0.0f;
			float voffset = 0.0f;
			bool translate = pParams->GetFloat(CRCD(0x56a7f41c,"uoff"), &uoffset);
			translate = pParams->GetFloat(CRCD(0x44125bf2,"voff"), &voffset) || translate;
			if (translate)
			{
				mat[3][0] = uoffset;
				mat[3][1] = voffset;
			}

			Dbg_MsgAssert(mp_model, ("Obj_SetUVParams: CModel is NULL"));
			mp_model->SetUVMatrix(mat_checksum, pass, mat);

			break;
		}
        // @script | EnableDisplayFlip | 
		case 0x721184c7: // EnableDisplayFlip
			mFlipDisplayMatrix=true;
			break;

        // @script | DisableDisplayFlip | 
		case 0x4af3b1f7: // DisableDisplayFlip
			mFlipDisplayMatrix=false;
			break;

        // @script | Obj_InitModel | 
		case 0x4d8e11ab: // Obj_InitModel
			InitModel( pParams );
			break;
			
        // @script | SwitchOffAtomic | 
        // @uparm name | geom name
		case 0xe48fd084: // SwitchOffAtomic
		{
			uint32 atomicName;
			pParams->GetChecksum( NONAME, &atomicName, Script::ASSERT );
			HideGeom(atomicName, true, true);
		}
		break;

        // @script | SwitchOnAtomic | 
        // @uparm name | geom name
		case 0x07d0c128: // SwitchOnAtomic
		{
			uint32 atomicName;
			pParams->GetChecksum( NONAME, &atomicName, Script::ASSERT );
			HideGeom(atomicName, false, true);
		}
		break;

        // @script | AtomicIsHidden | 
        // @uparm name | geom name
        case 0xe7fa7dd0: // AtomicIsHidden
		{
			uint32 atomicName;
			pParams->GetChecksum( NONAME, &atomicName, Script::ASSERT );
            if (GeomHidden( atomicName ))
                pScript->GetParams()->AddInteger(CRCD(0x77a21642,"hidden"), 1);
            else
                pScript->GetParams()->AddInteger(CRCD(0x77a21642,"hidden"), 0);
		}
		break;
		
		// @script | Obj_InitModelFromProfile | used for loading up peds and re-initializing the preview model
		case 0x98ed5c8b: // Obj_InitModelFromProfile
		{
			if ( mp_model )
			{
				int texDictOffset = 0;
				pParams->GetInteger( CRCD(0xf891ac27,"texDictOffset"), &texDictOffset, Script::NO_ASSERT );

				int useAssetManager = 0;
				pParams->GetInteger( CRCD(0xc63c8d38,"use_asset_manager"), &useAssetManager, Script::NO_ASSERT );

				Script::CStruct* pAppearanceParams;
				pParams->GetStructure( CRCD(0x456d28d1,"struct"), &pAppearanceParams, Script::ASSERT );

				uint32 buildScript = 0;
				pParams->GetChecksum( CRCD(0x299ee352,"buildscript"), &buildScript, Script::NO_ASSERT );
				
				// load the skeleton (do i really need to do a sanity check here?)
//              Dbg_MsgAssert( GetSkeleton(), ( "Object has no skeleton" ) )

				Gfx::CModelAppearance theAppearance;
				theAppearance.Load( pAppearanceParams, true );
				InitModelFromProfile( &theAppearance, useAssetManager, texDictOffset, buildScript );
			}
		}
		break;
		
		// @script | Obj_SetBoundingSphere | sets a sphere with the specified radius (assumes position is 0,0,0)
		// @uparm float | radius of bounding sphere
		case 0x452fb315:  // Obj_SetBoundingSphere
		{	
			float radius;
			pParams->GetFloat( NONAME, &radius, Script::ASSERT );
			
			// take the existing position, and change the radius on it
			Mth::Vector newSphere( 0.0f, 0.0f, 0.0f, radius );
			
			SetBoundingSphere( newSphere );
		}
		break;

		// @script | Obj_RestoreBoundingSphere | resets the sphere to whatever it was after the model was initialized
		case 0x5b41da8f:  // Obj_RestoreBoundingSphere
		{
			SetBoundingSphere( m_original_bounding_sphere );
		}
		break;
		
        // @script | RotateDisplay | 
        // @parmopt float | Duration | 0.0 | Duration time (default is ms) The rotation will take this much time to complete.
        // @flag seconds | Units of seconds
        // @flag frames | Units of frames
        // @flag x | Rotate about the x-axis
        // @flag y | Rotate about the y-axis
        // @flag z | Rotate about the z-axis
        // @parmopt float | StartAngle | 0.0 | Start angle, in degrees. The skater will instantly pop to this angle
		// when the RotateDisplay command is issued.
        // @parmopt float | EndAngle | 360.0 | End angle, in degrees. The skater will rotate from
		// the start angle to the end angle over the time period specified by Duration.
        // @parmopt int | SinePower | 0 | If set to zero (the default) the angle will change at a constant speed over
		// the specified duration.
		// A SinePower of 1 makes the speed start instantly but slow down smoothly at the end.
		// A SinePower of greater than one will make the speed smoothly accelerate from zero to a maximum, then
		// smoothly decelerate to zero. Values of 2,3 or 4 seem to work best.
		// A SinePower of -1 will make the speed smoothly accelerate from zero to a constant value. It will not
		// decelerate at the end.
		// Note that the final constant speed will be 1.5707 times the constant speed that would be have been used
		// if the SinePower was 0. The speed has to be a bit higher to make up for the smooth acceleration at the start
		// whilst keeping within the same Duration value.
		// So, if you want to follow with another RotateDisplay that maintains that constant speed, you will need to
		// divide it's duration by 1.5707, for example:
		// RotateDisplay y Duration=3 seconds EndAngle=(360*4) SinePower=-1
		// Wait 3 seconds
		// RotateDisplay y Duration=(3/1.5707) seconds EndAngle=(360*4) SinePower=0
		// @parmopt vector | RotationOffset | (0,30,0) | The offset of the point about which to rotate the skater.
		// @flag HoldOnLastAngle | If specified then the rotation will not pop back to what it was at
		// the end of the duration, but will stick until a CancelRotateDisplay command is issued.
		case 0xa4c25c2f: // RotateDisplay
		{
			#ifdef __USER_DAN__
			printf("RotateDisplay\n");
			Script::PrintContents(pParams);
			#endif
			
			float duration = 0.0f;
			pParams->GetFloat(CRCD(0x79a07f3f, "Duration"), &duration);
			if (pParams->ContainsFlag(CRCD(0xd029f619, "Seconds")) || pParams->ContainsFlag(CRCD(0x49e0ee96, "Second")))
			{
				// Convert from seconds to milliseconds
				duration *= 1000.0f;
			}
			else if (pParams->ContainsFlag(CRCD(0x19176c5, "Frames")) || pParams->ContainsFlag(CRCD(0x4a07c332, "Frame")))
			{
				// Convert from frames to milliseconds
				duration = duration * 1000.0f / 60.0f;
			}
			else
			{
				// Milliseconds is what we want, so nothing to do.
			}
			
			float start_angle = 0.0f;
			pParams->GetFloat(CRCD(0x9de2a72f, "StartAngle"), &start_angle);
			
			float end_angle = 0.0f;
			pParams->GetFloat(CRCD(0x8c9946a9, "EndAngle"), &end_angle);

			int sine_power = 0;
			pParams->GetInteger(CRCD(0xf164f03d, "SinePower"), &sine_power);
			
			m_display_rotation_offset.Set(0.0f, 30.0f, 0.0f);
			pParams->GetVector(CRCD(0x8f38f109, "RotationOffset"), &m_display_rotation_offset);

			bool hold_on_last_angle = pParams->ContainsFlag(CRCD(0xe14bc9ac, "HoldOnLastAngle"));
			
			Tmr::Time start_time = Tmr::ElapsedTime(0);
			
			char flags = 0;
			if (pParams->ContainsFlag(CRCD(0x7323e97c, "X")))
			{
				flags |= 1;
				mpDisplayRotationInfo[0].SetUp(duration,
											   start_time,
											   start_angle,
											   end_angle - start_angle,
											   sine_power,
											   hold_on_last_angle);
			}	
			if (pParams->ContainsFlag(CRCD(0x424d9ea, "Y")))
			{
				flags |= 2;
				mpDisplayRotationInfo[1].SetUp(duration,
											   start_time,
											   start_angle,
											   end_angle - start_angle,
											   sine_power,
											   hold_on_last_angle);
			}	
			if (pParams->ContainsFlag(CRCD(0x9d2d8850, "Z")))
			{
				flags |= 4;
				mpDisplayRotationInfo[2].SetUp(duration,
											   start_time,
											   start_angle,
											   end_angle - start_angle,
											   sine_power,
											   hold_on_last_angle);
			}
				
			// if our object is associated with a local player, send a MsgRotateDisplay message
			GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
			GameNet::PlayerInfo* player = gamenet_man->GetPlayerByObjectID(GetObject()->GetID());
			if (player && player->IsLocalPlayer())
			{
				Net::Client* client;
				GameNet::MsgRotateDisplay msg;
				Net::MsgDesc msg_desc;
				
				client = gamenet_man->GetClient( player->GetSkaterNumber() );
				Dbg_Assert( client );
	
				//msg.m_Time = client->m_Timestamp;
				msg.m_Duration = static_cast< int >( duration );
				msg.m_StartAngle = static_cast< short >( start_angle );
				msg.m_DeltaAngle = static_cast< short >( end_angle - start_angle );
				msg.m_SinePower = static_cast< int >( sine_power );
				msg.m_ObjId = GetObject()->GetID();
				msg.m_HoldOnLastAngle = hold_on_last_angle;
				msg.m_Flags = flags;
	
				msg_desc.m_Data = &msg;
				msg_desc.m_Length = sizeof( GameNet::MsgRotateDisplay );
				msg_desc.m_Id = GameNet::MSG_ID_ROTATE_DISPLAY;
				client->EnqueueMessageToServer( &msg_desc );
			}
		}
		break;
		
        // @script | CancelRotateDisplay | Instantly cancels any rotation due to a RotateDisplay
		// command.
		case 0x4424c267: // CancelRotateDisplay
		{
			mpDisplayRotationInfo[0].Clear();
			mpDisplayRotationInfo[1].Clear();
			mpDisplayRotationInfo[2].Clear();
				
			// if our object is associated with a local player, send a MsgRotateDisplay message
			GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
			GameNet::PlayerInfo* player = gamenet_man->GetPlayerByObjectID(GetObject()->GetID());
			if (player && player->IsLocalPlayer())
			{
				Net::Client* client;
				GameNet::MsgObjMessage msg;
				Net::MsgDesc msg_desc;
				
				client = gamenet_man->GetClient( player->GetSkaterNumber() );
				Dbg_Assert( client );
	
				//msg.m_Time = client->m_Timestamp;
				msg.m_ObjId = GetObject()->GetID();
	
				msg_desc.m_Data = &msg;
				msg_desc.m_Length = sizeof( GameNet::MsgObjMessage );
				msg_desc.m_Id = GameNet::MSG_ID_CLEAR_ROTATE_DISPLAY;
				client->EnqueueMessageToServer( &msg_desc );
			}
		}
		break;

		default:
            return CBaseComponent::MF_NOT_EXECUTED;
    }

    return success ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CModelComponent::GetDebugInfo"));
	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
	
	p_info->AddChecksum(CRCD(0xc3f4169a,"filename"),mp_model->GetFileName());
	
	Mth::Vector scale=mp_model->GetScale();
	p_info->AddVector(CRCD(0x13b9da7b,"scale"),scale.GetX(),scale.GetY(),scale.GetZ());
	
	uint32 mode=CRCD(0x52d95838,"Unknown");
	switch (mp_model->GetRenderMode())
	{
		case Nx::vTEXTURED:	 	mode=CRCD(0xd939b2b6,"vTEXTURED"); break;
		case Nx::vSKELETON:		mode=CRCD(0x22b5274d,"vSKELETON"); break;
		case Nx::vGOURAUD:		mode=CRCD(0xe6d7f928,"vGOURAUD"); break;
		case Nx::vFLAT:			mode=CRCD(0x3b4168d5,"vFLAT"); break;
		case Nx::vWIREFRAME:	mode=CRCD(0xec50e3e1,"vWIREFRAME"); break;
		case Nx::vBBOX:			mode=CRCD(0x2988b32d,"vBBOX"); break;
		case Nx::vNONE:			mode=CRCD(0x119bc25e,"vNONE"); break;
		default:
			break;
	}	
	p_info->AddChecksum(CRCD(0x2f006949,"RenderMode"),mode);		
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Mick:  This function actually gets called TWICE when a model is created
// Once from the call to Teleport which is called from the CCOmpositeObject::Teleport functions
// and then once from InitModelFromProfile(), whcih is called after the model is finalized.
// Only on the second call will the model have its geometry set up correctly.
// This does not seem to cause problems, but should probably be re-worked for future iterations of the
// engine.
void CModelComponent::FinalizeModelInitialization()
{
	// need to synchronize rendered model's position to initial world position
	Update();

	if ( mp_model && mp_model->GetModelLights() )
	{
		UpdateBrightness();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::RefreshModel( Gfx::CModelAppearance* pAppearance, uint32 buildScript )
{
	if ( mp_model )
	{
		// it doesn't matter whether we use the asset manager
		// or which texture dict offset we use, because
		// the build script will only affect colors...
		bool dummyUseAssetManager = true;
		int dummyTexDictOffset = 0;

		InitModelFromProfile( pAppearance, dummyUseAssetManager, dummyTexDictOffset, buildScript );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void SDisplayRotationInfo::Clear()
{
	SDisplayRotation *p_rotation=mpRotations;
	for (int i=0; iClear();
		++p_rotation;
	}	
	m_active = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void SDisplayRotationInfo::SetUp(float duration, Tmr::Time start_time, float start_angle, float change_in_angle, int sine_power, bool holdOnLastAngle)
{
	SDisplayRotation *p_rotation=mpRotations;
	for (int i=0; imDispRotating)
		{
			p_rotation->SetUp(duration, start_time, start_angle, change_in_angle, sine_power, holdOnLastAngle);
			m_active = true;
			return;
		}	
		++p_rotation;
	}	
	
	//Dbg_MsgAssert(0,("Too many rotations overlaid at once, maximum is %d",SDisplayRotationInfo::MAX_ROTATIONS));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float SDisplayRotationInfo::CalculateNewAngle()
{
	float new_angle=0.0f;
	if (m_active)
	{
		m_active = false;
		SDisplayRotation *p_rotation=mpRotations;
		for (int i=0; imDispRotating)
			{
				m_active = true;
				new_angle+=p_rotation->CalculateNewAngle();
			}
			++p_rotation;
		}
	}	
	return new_angle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void SDisplayRotation::Clear()
{
	mDispRotating=false;
	mHoldOnLastAngle=false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void SDisplayRotation::SetUp(float duration, Tmr::Time start_time, float start_angle, float change_in_angle, int sine_power, bool holdOnLastAngle)
{
	mDispRotating=true;
	mDispDuration=duration;
	mDispStartTime=start_time;
	mDispStartAngle=start_angle;
	mDispChangeInAngle=change_in_angle;
	mDispSinePower=sine_power;
	mHoldOnLastAngle=holdOnLastAngle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float SDisplayRotation::CalculateNewAngle()
{
	float new_angle=0.0f;
	
	if (mDispRotating)
	{
		float t=Tmr::ElapsedTime(0)-mDispStartTime;
		if (t>mDispDuration)
		{
			if (mHoldOnLastAngle)
			{
				// Stick forever on the end angle, until the mHoldOnLastAngle gets reset
				// by a CancelRotateDisplay script command.
				new_angle=mDispStartAngle+mDispChangeInAngle;
			}
			else
			{
				mDispRotating=false;
			}	
		}
		else
		{
			if (mDispSinePower)
			{
				float s;
				if (mDispSinePower<0)
				{
					// If a negative sine power is specified, then use an upside-down and
					// back-to-front sine wave. This gives a rotation which smoothly accelerates
					// to a constant speed.
					s=1.0f-sinf(1.570796327f-t*1.570796327f/mDispDuration);
				}
				else
				{
					s=sinf(t*1.570796327f/mDispDuration);
					for (int i=0; iGetID()) ) );
				  
	return m_refObjectName;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::ApplyLightingFromCollision( CFeeler& feeler )
{
	if (!feeler.IsBrightnessAvailable()) return;
	
	if (Nx::CModelLights* p_model_lights = GetModel()->GetModelLights())
	{
		p_model_lights->SetBrightness(feeler.GetBrightness());
	}
	else
	{
		Nx::CLightManager::sSetBrightness(feeler.GetBrightness());
	}
}
									 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelComponent::ApplySceneLighting( Image::RGBA color )
{
	// Mick: if the model is a level object, then apply the scene lighthing to it
	if (m_isLevelObject)
	{
		mp_model->GetGeomByIndex(0)->SetColor(color);
	}
}
									 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
									 
}

#if 0
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// GJ TODO:  these cheat codes are kind of kludgy 
// and should be re-implemented later in a more 
// component-friendly format.

bool apply_cheat_scale( Gfx::CSkeleton* pSkeleton, Nx::CModel* pModel, uint32 cheatName, uint32 bodyShapeName, uint32 animScriptName )
{
	int flag = Script::GetInteger( cheatName, Script::ASSERT );
	
	if ( Mdl::Skate::Instance()->GetCareer()->GetGlobalFlag(flag) )
	{
		Dbg_Assert( pModel );

		if ( animScriptName == CRCD(0x501949bd,"animload_human") 
			 || animScriptName == CRCD(0x105f6aa1,"animload_ped_f") )
		{
			Dbg_MsgAssert( pSkeleton, ( "Skeleton has not been assigned to this model yet" ) );
			Script::CStruct* pBodyShapeStructure = Script::GetStructure( bodyShapeName, Script::ASSERT );
			
			Mth::Vector theScale( 1.0f, 1.0f, 1.0f );
			if ( Gfx::GetScaleFromParams( &theScale, pBodyShapeStructure ) )
			{
				Mth::Vector vec = pModel->GetScale();
				
				vec[X] *= theScale[X];
				vec[Y] *= theScale[Y];
				vec[Z] *= theScale[Z];

				// if the body shape has a scale
				pModel->SetScale( vec );
			}

			pSkeleton->ApplyBoneScale( pBodyShapeStructure );
		}
	
		return true;
	}

	return false;
}
#endif


================================================
FILE: Code/Gel/Components/modelcomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ModelComponent.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/17/2002
//****************************************************************************

#ifndef __COMPONENTS_MODELCOMPONENT_H__
#define __COMPONENTS_MODELCOMPONENT_H__

#include 
#include 

#include 

#include 
#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_MODEL CRCD(0x286a8d26,"Model")
#define		GetModelComponent() ((Obj::CModelComponent*)GetComponent(CRC_MODEL))
#define		GetModelComponentFromObject(pObj) ((Obj::CModelComponent*)(pObj)->GetComponent(CRC_MODEL))

class CFeeler;
		
namespace Gfx
{
	class CModelAppearance;
}

namespace Nx
{
    class CModel;
}

namespace Script
{
	class CScript;
    class CStruct;
}
              
namespace Obj
{

	class CSkeletonComponent;
	class CAnimationComponent;
	class CSuspendComponent;


struct SDisplayRotation
{
	bool mDispRotating;
	bool mHoldOnLastAngle;
	float mDispDuration;
	Tmr::Time mDispStartTime;
	float mDispStartAngle;
	float mDispChangeInAngle;
	int	mDispSinePower;
	
	void SetUp(float duration, Tmr::Time start_time, float start_angle, float change_in_angle, int sine_power, bool holdOnLastAngle);
	float CalculateNewAngle();
	void Clear();
};

struct SDisplayRotationInfo
{
	// Factored out the data members into the SDisplayRotation structure so that
	// there can be 6 operating at once. Needed by Zac for create-a-trick.
	enum
	{
		MAX_ROTATIONS=6
	};
	SDisplayRotation mpRotations[MAX_ROTATIONS];
	bool	m_active;
	
	void SetUp(float duration, Tmr::Time start_time, float start_angle, float change_in_angle, int sine_power, bool holdOnLastAngle);
	float CalculateNewAngle();
	void Clear();
};

class CModelComponent : public CBaseComponent
{
public:
    CModelComponent();
    virtual	~CModelComponent();

public:
    virtual void    		Update();
    virtual void    		InitFromStructure( Script::CStruct* pParams );
    virtual void			Hide( bool should_hide );
    virtual void			Finalize();
	virtual void			Teleport();

    EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	
	// Used by the script debugger code to fill in a structure
	// for transmitting to the monitor.exe utility running on the PC.
	virtual void			GetDebugInfo(Script::CStruct *p_info);
	
	static CBaseComponent*	s_create();

public:
	void					InitModel( Script::CStruct* pParams );
	virtual void			InitModelFromProfile( Gfx::CModelAppearance* pAppearance, bool useAssetManager, uint32 texDictOffset, uint32 buildScript = 0 );
	void 					FinalizeModelInitialization();
	void					RefreshModel( Gfx::CModelAppearance* pAppearance, uint32 buildScript );
    Nx::CModel*     		GetModel();
	bool					HideGeom( uint32 geomName, bool hidden, bool propagate );
    bool					GeomHidden( uint32 geomName );
	void					SetModelLODDistance( int lodIndex, float distance );
	void					SetBoundingSphere( const Mth::Vector& newSphere );
	void					ApplyLightingFromCollision ( CFeeler& feeler );
	void					ApplySceneLighting(Image::RGBA color);
	void					UpdateBrightness();
	// K: This used to be protected, but I made it public for use by CLockObjComponent::LockToObject
	void					GetDisplayMatrixWithExtraRotation( Mth::Matrix& displayMatrix );
	
	void					SetDisplayOffset ( const Mth::Vector& display_offset ) { mDisplayOffset = display_offset; }
	
protected:
	void 					init_model_from_level_object( uint32 checksumName );
	bool					enable_lod(uint32 componentName, float distance);


protected:
	// Sometimes we need to temporarily change the bounding sphere
	// of an object (like when an animation takes a model far
	// from its original bouding sphere)...  the following
	// is used to correctly restore the original bounding sphere
	Mth::Vector				m_original_bounding_sphere;


// the following are used for tweaking the display matrix
// right before the rendered model actually uses it
// public during transition
public:
	
	// The offset relative to the skater's origin about which the display rotation rotates.
	Mth::Vector 			m_display_rotation_offset;
	
	
	// Stuff for rotating just the display matrix, so that
	// the physics and camera don't notice it.
	SDisplayRotationInfo 	mpDisplayRotationInfo[3];

	// Controlled by the script commands EnableDisplayFlip and DisableDisplayFlip.
	// Added to allow the lip script to finish its out-anim whilst on the ground without a glitch.
	// When set, this will cause the display matrix to be the regular matrix flipped, each frame.
	bool					mFlipDisplayMatrix;
	
	// Offset from object's position at which the model is drawn.  Defaults to zero.
	Mth::Vector				mDisplayOffset;

public:
	enum
	{
		vNUM_LODS = 4
	};

protected:
    Nx::CModel*    		 	mp_model;
	CSkeletonComponent*		mp_skeleton_component;
	CAnimationComponent*	mp_animation_component;
	CSuspendComponent*		mp_suspend_component;

protected:
	float				m_LODdist[vNUM_LODS];
	int					m_numLODs;

	// GJ:  ref objects are used by the cutscene code
	// for stealing another object's model
	// (maybe cutscene objects should just have
	// CRefModelComponent?)
public:
	uint32				GetRefObjectName();
	bool				HasRefObject()
	{
		return m_hasRefObject;
	}

protected:
	uint32				m_refObjectName;
	bool				m_hasRefObject;
	
private:
	bool				m_isLevelObject;	// True if it's a level object	
	
};

inline Nx::CModel* CModelComponent::GetModel()
{
    return mp_model;
}

}

#endif


================================================
FILE: Code/Gel/Components/motioncomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       MotionComponent.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/5/2002
//****************************************************************************

// start autoduck documentation
// @DOC motioncomponent
// @module motioncomponent | None
// @subindex Scripting Database
// @index script | motioncomponent

#include 
#include 
#include 
#include 

#include 
									
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 



namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
enum
{
	UNITS_IPS,
	UNITS_FPS,
	UNITS_MPH,
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// This static function is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
CBaseComponent* CMotionComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CMotionComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CMotionComponent::CMotionComponent() : CBaseComponent()
{
	SetType( CRC_MOTION );

	mp_pathOb = NULL;
	
	mp_path_object_tracker = NULL;

	m_point_stick_to_ground = true;

	Reset();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CMotionComponent::~CMotionComponent()
{
	if ( mp_pathOb )
	{
		delete mp_pathOb;
		mp_pathOb = NULL;
	}

	// Note: No need to unregister from mp_path_object_tracker, because the path tracker
	// uses smart pointers.
	
	if ( mp_slerp )
	{
		delete mp_slerp;
		mp_slerp = NULL;
	}
	
	if (mp_follow_ob)
	{
		delete mp_follow_ob;
		mp_follow_ob=NULL;
	}	

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CMotionComponent::InitFromStructure( Script::CStruct* pParams )
{
	int nodeNum = -1;
	pParams->GetInteger( CRCD(0xe50d6573,"nodeIndex"), &nodeNum, Script::NO_ASSERT );
	m_start_node = m_current_node = nodeNum;
	OrientToNode(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CMotionComponent::ProcessWait( Script::CScript * pScript )
{
	switch (pScript->GetWaitType())
	{
		case Script::WAIT_TYPE_OBJECT_MOVE:
			if ( !IsMoving() )
			{
				pScript->ClearWait();
			}
			break;
		
		case Script::WAIT_TYPE_OBJECT_JUMP_FINISHED:
			if ( !(m_movingobj_status & MOVINGOBJ_STATUS_JUMPING) )
			{
				pScript->ClearWait();
			}
			break;

		case Script::WAIT_TYPE_OBJECT_ROTATE:
			if ( !IsRotating() )
			{
				pScript->ClearWait();
			}
			break;
		case Script::WAIT_TYPE_OBJECT_STOP_FINISHED:
			if ( !( m_vel_z > 0.0f ) )
			{
				pScript->ClearWait();
			}
			break;
		default:
			Dbg_MsgAssert(0,("\n%s\nWait type of %d not supported by CMotionComponent",pScript->GetScriptInfo(),pScript->GetWaitType()));
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CMotionComponent::Update()
{

	m_time = Tmr::FrameLength();	
	
	if ( m_movingobj_status & MOVINGOBJ_STATUS_HOVERING )
	{
		// Having these functions next to each other here does mean that objects cannot be
		// moving and hovering at the same time, but currently nothing requires that.
		UndoHover();
		ApplyHover();
	}

	if ( m_movingobj_status & MOVINGOBJ_STATUS_ROTXYZ )
	{
		RotUpdate( );
	}
	
	if ( m_movingobj_status & MOVINGOBJ_STATUS_QUAT_ROT )
	{
		QuatRot( );
	}

	if ( m_movingobj_status & MOVINGOBJ_STATUS_MOVETO )
	{
		if ( Move( ) )
		{
			m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
		}
	}
	else if ( m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH )
	{
		FollowPath( );
	}
	else if ( m_movingobj_status & MOVINGOBJ_STATUS_FOLLOWING_LEADER )
	{
		Dbg_MsgAssert( !(m_movingobj_status & MOVINGOBJ_STATUS_QUAT_ROT), ("Cannot have object rotating when it is following a leader"));
		Dbg_MsgAssert( !(m_movingobj_status & MOVINGOBJ_STATUS_ROTXYZ), ("Cannot have object rotating when it is following a leader"));
		FollowLeader( );
	}

	if ( m_movingobj_status & MOVINGOBJ_STATUS_JUMPING )
	{
		DoJump();
		GetObject()->m_pos = m_jump_pos;
		// m_pos[Y]=m_jump_y;
	}


	// TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
	// BIT OF A PATCH, as the skater has a motion component
	// but we don't want to set his display matrix directly here.
	if (GetObject()->GetID() > 15)
	{
		GetObject()->SetDisplayMatrix(GetObject()->GetMatrix());
	}
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMotionComponent::IsMoving()
{   
	return ( m_movingobj_status & ( MOVINGOBJ_STATUS_ON_PATH | MOVINGOBJ_STATUS_MOVETO ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMotionComponent::IsRotating()
{	
	return ( m_movingobj_status &  ( MOVINGOBJ_STATUS_ROTXYZ | MOVINGOBJ_STATUS_QUAT_ROT ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void s_get_velocity( Script::CStruct* pParams, float* pVel )
{
	if ( pParams->GetFloat( NONAME, pVel ) )
	{
		// default is mph:
		if ( pParams->ContainsFlag( 0xaad7c80f ) ) // 'fps'
		{
			*pVel = FEET_TO_INCHES( *pVel );
		}
		else if ( !( pParams->ContainsFlag( 0xa18b8f32 ) ) ) // 'ips'
		{
			// miles per hour then...
			*pVel = MPH_TO_IPS( *pVel );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void s_get_acceleration( Script::CStruct* pParams, float* pAccel )
{
	if ( pParams->GetFloat( NONAME, pAccel ) )
	{
		// default is mphps:
		if ( pParams->ContainsFlag( 0xf414a5ea ) ) // 'fpsps'
		{
			*pAccel = FEET_TO_INCHES( *pAccel );
		}
		else if ( !( pParams->ContainsFlag( 0x7644323b ) ) ) // 'ipsps'
		{
			// miles per hour per second then...
			*pAccel = MPH_TO_IPS( *pAccel );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::UndoHover( void )
{   
	Mth::Vector pos=GetObject()->GetPos();
	pos[Y]=m_y_before_applying_hover;
	GetObject()->SetPos(pos);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::ApplyHover( void )
{   
	uint32 t=Tmr::ElapsedTime(0)%m_hover_period;
	m_hover_offset=m_hover_amp*sinf(t*2*3.141592653f/m_hover_period);
	Mth::Vector pos=GetObject()->GetPos();
	m_y_before_applying_hover=pos[Y];
	pos[Y]+=m_hover_offset;
	GetObject()->SetPos(pos);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMotionComponent::IsHovering( void )
{
	return (m_movingobj_status & MOVINGOBJ_STATUS_HOVERING) && 
			// If it is doing a Quat rot, then pretend it is not hovering.
			// This is so that the rocking buoy in the shipyard is not indicated as
			// being hovering, otherwise the replay code will show it as just hovering
			// up and down. The replay code optimizes hovering objects by not recording their
			// precise positions and orientations every frame. We need that for the buoy though.
		   !(m_movingobj_status &  (MOVINGOBJ_STATUS_QUAT_ROT));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::GetHoverOrgPos( Mth::Vector* p_orgPos )
{
	Dbg_MsgAssert(p_orgPos,("NULL p_orgPos"));
	Mth::Vector pos=GetObject()->GetPos();
	p_orgPos->Set(pos[X],m_y_before_applying_hover,pos[Z]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent::EMemberFunctionResult CMotionComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | Obj_IsMoving | true if moving
		case 0x21accf0d: 		// Obj_IsMoving
			return ( IsMoving() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE );
			break;
	
        // @script | Obj_IsRotating | true if rotating
		case 0x7bc461ae: 		// Obj_IsRotating
			return ( IsRotating() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE );
			break;

        // @script | Obj_StorePos | stores the current position
		case 0x8e52abea: 		// Obj_StorePos
			m_stored_pos = GetObject()->GetPos();
			break;

        // @script | Obj_StoreNode | stores the next waypoint in the path
		case 0xc8cc6820: 		// Obj_StoreNode
			Dbg_MsgAssert( GetPathOb(),( "\n%s\nObject trying to store waypoint when not on a path.", pScript->GetScriptInfo( ) ));
			m_stored_node = GetPathOb()->m_node_to;
			break;

        // @script | Obj_StopMoving |
		case 0x68e1f022: 		// Obj_StopMoving
			m_movingobj_status &= ~( MOVINGOBJ_STATUS_MOVETO | MOVINGOBJ_STATUS_ON_PATH );
			break;

        // @script | Obj_StopRotating |
		case 0xdfbe3db3: 		// Obj_StopRotating
			m_movingobj_status &= ~( MOVINGOBJ_STATUS_ROTX | MOVINGOBJ_STATUS_ROTY |
				MOVINGOBJ_STATUS_ROTZ | MOVINGOBJ_STATUS_QUAT_ROT | MOVINGOBJ_STATUS_LOOKAT );
			break;

        // @script | Obj_SetPathVelocity | 
        // @uparm 1.0 | velocity
        // @flag mph | default units
        // @flag ips |
        // @flag fps | 
		case 0xfb603ea9:		// Obj_SetPathVelocity
			s_get_velocity( pParams, &m_max_vel );
			break;
		
        // @script | Obj_SetPathMinStopVel | Speed at which friction
        // overcomes momentum and the object stops. If left at zero,
        // the object will take forever to stop... A good value for
        // a car is 1 or 2 mph. Pedestrians are more 'sticky': make
        // it 3 or 4 mph for them. Only used if you call Obj_StopAlongPath
        // @uparm 1.0 | velocity
        // @flag mph | default units
        // @flag ips |
        // @flag fps | 
		case 0x2ca63cdf:		// Obj_SetPathMinStopVel
			s_get_velocity( pParams, &m_min_stop_vel );
			break;
		
        // @script | Obj_SetPathAcceleration | used for when the object
        // is stopped, and needs to get up to speed (set by Obj_SetVelocity).
        // @uparm 1.0 | acceleration
        // @flag mphps | default units
        // @flag ipsps |
        // @flag fpsps | 
		case 0xd6697ad0:		// Obj_SetPathAcceleration
			s_get_acceleration( pParams, &m_acceleration );
			break;

        // @script | Obj_SetPathDeceleration | used if the velocity is
        // decreased while the object is traveling, to slow down to the new speed
        // @uparm 1.0 | acceleration
        // @flag mphps | default units
        // @flag ipsps |
        // @flag fpsps | 		
		case 0xa67fc783:		// Obj_SetPathDeceleration
			s_get_acceleration( pParams, &m_deceleration );
			break;
		
        // @script | Obj_Rotate | rotate object
        // @parmopt float | time | 0.0 | in seconds - if not included, instant rotation
        // @parmopt vector | relative | (0, 0, 0) | rotate relative to the object
        // @parmopt vector | absolute | (0, 0, 0) | rotate oriented along world axes
        // @flag FLAG_MAX_COORDS | with this flag, coordinates will be converted to 
        // correspond to the axes in Max ( Z up, -Y forward, and X right. )
		case 0x5cf1b7d4:  // "Obj_Rotate"
			QuatRot_Init( pParams );
			break;
		
        // @script | Obj_RotX | rotate about x axis
        // @parmopt float | angle | | If not specified, object will rotate forever
        // @parmopt float | speed | | Speed of rotation in degrees per second. 
        // Required unless deltaROT is ZERO
        // @parmopt float | acceleration | | Acceleration in degrees per
        // second per second.  If not set, velocity is set instantly
        // @parmopt float | deceleration | | If specified, velocity is set instantly.
        // Deceleration in degrees per
        // second per second.  Target stops rotating when it reaches the target
        // angle specified, or when speed reaches ZERO
        // @flag FLAG_MAX_COORDS | With this flag, coordinates will be converted to 
        // correspond to the axes in Max ( Z up, -Y forward, and X right. )
		case 0x0b231a9d:  // "Obj_RotX"
			RotAxis_Init( pParams, pScript, X );
			break;

        // @script | Obj_RotY | rotate about Y axis
        // @parmopt float | angle | | If not specified, object will rotate forever
        // @parmopt float | speed | | Speed of rotation in degrees per second. 
        // Required unless deltaROT is ZERO
        // @parmopt float | acceleration | | Acceleration in degrees per
        // second per second.  If not set, velocity is set instantly
        // @parmopt float | deceleration | | If specified, velocity is set instantly.
        // Deceleration in degrees per
        // second per second.  Target stops rotating when it reaches the target
        // angle specified, or when speed reaches ZERO
        // @flag FLAG_MAX_COORDS | With this flag, coordinates will be converted to 
        // correspond to the axes in Max ( Z up, -Y forward, and X right. )
		case 0x7c242a0b:  // "Obj_RotY"
			RotAxis_Init( pParams, pScript, Y );
			break;

        // @script | Obj_RotZ | rotate about z axis
        // @parmopt float | angle | | If not specified, object will rotate forever
        // @parmopt float | speed | | Speed of rotation in degrees per second. 
        // Required unless deltaROT is ZERO
        // @parmopt float | acceleration | | Acceleration in degrees per
        // second per second.  If not set, velocity is set instantly
        // @parmopt float | deceleration | | If specified, velocity is set instantly.
        // Deceleration in degrees per
        // second per second.  Target stops rotating when it reaches the target
        // angle specified, or when speed reaches ZERO
        // @flag FLAG_MAX_COORDS | With this flag, coordinates will be converted to 
        // correspond to the axes in Max ( Z up, -Y forward, and X right. )
		case 0xe52d7bb1:  // "Obj_RotZ"
			RotAxis_Init( pParams, pScript, Z );
			break;

        // @script | Obj_PathHeading | Defaults to on. Sets the
        // heading of the object in the direction of the path
        // @flag off | use to set off
		case 0xd8f4bc32:  // "Obj_PathHeading"
			if ( pParams->ContainsFlag( 0xd443a2bc ) ) // "off"
			{
				m_movingobj_flags |= MOVINGOBJ_FLAG_INDEPENDENT_HEADING;
			}
			else
			{
				m_movingobj_flags &= ~MOVINGOBJ_FLAG_INDEPENDENT_HEADING;
			}
			break;

        // @Script | Obj_SetConstantHeight | sets object height to whatever
        // height the object is currently at
        // @flag off | turn it off (default is on)
		case 0xaa3cb476:  // "Obj_SetConstantHeight"
			if ( pParams->ContainsFlag( 0xd443a2bc ) ) // "off"
			{
				m_movingobj_flags &= ~MOVINGOBJ_FLAG_CONSTANT_HEIGHT;
			}
			else
			{
				m_movingobj_flags |= MOVINGOBJ_FLAG_CONSTANT_HEIGHT;
				pParams->GetFloat( NONAME, &GetObject()->m_pos[ Y ] );
			}
			break;

        // @script | Obj_GetNextObjOnPath | Searches forward along the path the object is on for a max
		// of Range feet, and if another object on the path is encountered, its name is put into a parameter
		// called Ob. If no object is found, no Ob parameter will be created, and if it existed before, it
		// will be removed.
        // @parmopt float | Range | 0.0 | Distance to search, in feet
		case 0xb75589ce: // Obj_GetNextObjOnPath	
		{	
			float range=100.0f;
			pParams->GetFloat(CRCD(0x6c78a5b6,"Range"),&range);
			range*=12.0f;
			
			pScript->GetParams()->RemoveComponent( CRCD(0xffff9a1c,"Ob") );
			
			CCompositeObject* p_closest_ob = GetNextObjectOnPath( range );

			if ( p_closest_ob )
				pScript->GetParams()->AddChecksum( CRCD(0xffff9a1c,"Ob"), p_closest_ob->GetID() );			
			break;
		}

        // @script | Obj_GetRandomLink | Chooses a random node that the object's current node is 
		// linked to, and puts its link number into a parameter called Link.
		// If the object's current node is not linked to anything, no Link parameter will be created.
		case 0x6866312f: // Obj_GetRandomLink
		{
			uint32 num_links = SkateScript::GetNumLinks( m_current_node );
			if (num_links)
			{
				// Note: The +1 is because we're using the convention of numbering the
				// links from 1 to n rather than 0 to n-1
				pScript->GetParams()->AddInteger(CRCD(0xc953660e,"Link"),Mth::Rnd(num_links)+1);
			}
			break;
		}
			
        // @script | Obj_RandomPathMode | Causes Obj_FollowPath commands to
        // pick random links as moving objects traverse paths
        // @flag off | use to turn off - default is on
		case 0x434ea1fb:	// Obj_RandomPathMode
			if ( pParams->ContainsFlag( 0xd443a2bc ) ) // "off"
			{
				m_movingobj_flags &= ~MOVINGOBJ_FLAG_RANDOM_PATH_MODE;
			}
			else
			{
				m_movingobj_flags |= MOVINGOBJ_FLAG_RANDOM_PATH_MODE;
			}
			break;

        // @script | Obj_SetPathTurnDist | The distance from a waypoint 
        // at which the object starts turning
        // @uparm 1.0 | distance in feet
        // @flag inches | use units of inches for distance
		case 0x76003d82: // "Obj_SetPathTurnDist"
		{
			float enterTurnDist;
			if ( pParams->GetFloat( NONAME, &enterTurnDist ) )
			{
				if ( !pParams->ContainsFlag( 0xf915b4f3 ) ) // "inches"
				{
					enterTurnDist = FEET_TO_INCHES( enterTurnDist );
				}
				EnsurePathobExists( GetObject() );
				GetPathOb()->m_enter_turn_dist = enterTurnDist;
			}
			break;
		}
				
        // @script | Obj_FollowPath | Must also have Obj_SetVelocity set.
        // If the node has no links, the object will move to the node and
        // stop. Linked in a circle: object follows loop until stopped.
        // Linked to a finite path: walks to the end of the path. Returns
        // true for Obj_IsMoving until path complete. If used in conjunction
        // with Obj_MoveTo functions, will turn moveto function off, and
        // vice versa
        // @parm name | Name | name of node at beginning of path
		case 0xbfb0031d:  // "Obj_FollowPath"
			m_movingobj_flags &= ~MOVINGOBJ_FLAG_STOP_ALONG_PATH;
			FollowPath_Init( pParams );
			break;

        // @script | Obj_LookAtNode | 
        // @parm name | Name | the name of the node you want to look at
        // @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
        // @parmopt float | time | vel/acceleration | in seconds
        // @parmopt float | speed | 360  | angular velocity in degrees per second
        // @parmopt float | acceleration | 0 | angular acceleration in degrees per second
        // per second 
        // @flag FLAG_MAX_COORDS | With this flag, coordinates will be converted to 
        // correspond to the axes in Max ( Z up, -Y forward, and X right. )
		case 0x103bd253:  // "Obj_LookAtNode"
			LookAtNode_Init( pParams, pScript );
			break;

		// @script | Obj_LookAtNodeLinked | looks at the next node
        // @parmopt int | linkNum | 1 | The link to look at.
		case 0x6419ceff: // "Obj_LookAtNodeLinked"
			LookAtNodeLinked_Init( pParams, pScript );
			break;

        // @script | Obj_LookAtNodeStored | looks at stored node
        // @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
        // @parmopt float | time | vel/acceleration | in seconds
        // @parmopt float | speed | 360  | angular velocity in degrees per second
        // @parmopt float | acceleration | 0 | angular acceleration in degrees per second
        // per second 
		// @parmopt float | AngleThreshold | 0 | If the object is already looking within this
		// many degrees of the correct direction, it will not bother turning.
        // @flag FLAG_MAX_COORDS | With this flag, coordinates will be converted to 
        // correspond to the axes in Max ( Z up, -Y forward, and X right. )
		case 0xa5a2d218: // "Obj_LookAtNodeStored"
		{
			Mth::Vector lookAtPos;
			SkateScript::GetPosition( m_stored_node, &lookAtPos );
			LookAt_Init( pParams, lookAtPos );
			break;
		}

        // @script | Obj_LookAtPos |
        // @uparm (0, 0, 0) | position vector
        // @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
        // @parmopt float | time | vel/acceleration | in seconds
        // @parmopt float | speed | 360  | angular velocity in degrees per second
        // @parmopt float | acceleration | 0 | angular acceleration in degrees per second
        // per second
		case 0x74c30242:  // "Obj_LookAtPos"
			LookAtPos_Init( pParams, true );
			break;

        // @script | Obj_LookAtPosStored | look at stored pos
        // @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
        // @parmopt float | time | vel/acceleration | in seconds
        // @parmopt float | speed | 360  | angular velocity in degrees per second
        // @parmopt float | acceleration | 0 | angular acceleration in degrees per second
        // per second
		// @parmopt float | AngleThreshold | 0 | If the object is already looking within this
		// many degrees of the correct direction, it will not bother turning.
		case 0xe8d121cb: // "Obj_LookAtPosStored"
			LookAt_Init( pParams, m_stored_pos );
			break;

		// @script | Obj_LookAtRelPos | look at position relative to object position
        // @uparm (0, 0, 0) | relative position vector
        // @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
        // @parmopt float | time | vel/acceleration | in seconds
        // @parmopt float | speed | 360  | angular velocity in degrees per second
        // @parmopt float | acceleration | 0 | angular acceleration in degrees per second
        // per second
        case 0x33df698f:  // "Obj_LookAtRelPos"
			LookAtPos_Init( pParams, false );
			break;

        // @script | Obj_StopAlongPath | accurately stop and object
        // @uparm 1.0 | distance in feet.  Note: doesn't use deceleration
        // set in Obj_SetDeceleration
		case 0xfe71453f:  // "Obj_StopAlongPath"
			m_stop_dist = 0;
			m_stop_dist_traveled = 0;
			if ( !m_min_stop_vel )
			{
				m_min_stop_vel = 15;
			}
			pParams->GetFloat( NONAME, &m_stop_dist );
			if ( !pParams->ContainsFlag( 0xf915b4f3 ) ) // inches
			{
				m_stop_dist = FEET_TO_INCHES( m_stop_dist );
				m_vel_z_start = m_vel_z;
			}
			if ( !m_stop_dist )
			{
				m_vel_z = 0;
			}
			m_movingobj_flags |= MOVINGOBJ_FLAG_STOP_ALONG_PATH;
			break;
		
        // @script | Obj_StartAlongPath | Only needs to be used if you've
        // stopped an object along a path, to restart the object moving. 
		case 0xbd6c6807:  // "Obj_StartAlongPath"
			m_movingobj_flags &= ~MOVINGOBJ_FLAG_STOP_ALONG_PATH;
			break;
		
        // @script | Obj_FollowPathLinked | Will cause the object to
        // follow the path that it is linked to.
        // When calling Obj_FollowPathLinked, you can add an
        // optional parameter: 
        // originalNode 
        // to follow a linked path to the node that created the object. 
        // If random mode is on, a random link from the original
        // node will be used
        // @flag originalNode | follow a linked path to the node that 
        // created the object
		case 0x0da2d86d:  // "Obj_FollowPathLinked"
			m_movingobj_flags &= ~MOVINGOBJ_FLAG_STOP_ALONG_PATH;
			FollowPathLinked_Init( pParams, pScript );
			break;

        // @script | Obj_FollowPathStored | Follow the path from current
        // position to the position of the waypoint last stored
        // in the script call to Obj_StoreNode (when a pedestrian, for example, has to jump
        // out of the way of the player, he stores his current position and the current node
        // that he's heading for... then he walks back to his position, and finally ends up
        // heading to the waypoint he was heading for before he left the path to jump out of the way).
		case 0xcc19c48a: // "Obj_FollowPathStored"
			FollowStoredPath_Init( pParams );
			break;

        // @script | Obj_SetGroundOffset | 
		case 0xbf9fffe5: // "Obj_SetGroundOffset"
			Dbg_Message( "Obj_SetGroundOffset is obsolete!" );
            break;
		
        // @script | Obj_MoveToPosStored | move to world position stored
        // using Obj_StorePos
        // @parmopt float | time | | default unit is milliseconds
        // @parmopt float | speed | | used instead of time and acceleration
        // @parmopt float | acceleration | | change in speed per second
		case 0x8b669799: // "Obj_MoveToPosStored"
			Move_Init( pParams, pScript, m_stored_pos );
			break;

        // @script | Obj_MoveForward | Moves a distance in the direction the object is facing.
        // @parmopt float | dist | | Distance to move, in inches.
        // @parmopt float | time | | default unit is milliseconds
        // @parmopt float | speed | | used instead of time and acceleration
        // @parmopt float | acceleration | | change in speed per second
		case 0xf389285d: // Obj_MoveForward
		{
			float dist=0.0f;
			pParams->GetFloat(CRCD(0x7e832f08,"Dist"),&dist);
			Move_Init( pParams, pScript, GetObject()->GetPos() + GetObject()->m_matrix[Z] * dist );	 
			break;
		}

        // @script | Obj_MoveLeft | Moves a distance at 90 degrees to the direction the object is facing.
        // @parmopt float | dist | | Distance to move, in inches.
        // @parmopt float | time | | default unit is milliseconds
        // @parmopt float | speed | | used instead of time and acceleration
        // @parmopt float | acceleration | | change in speed per second
		case 0x5ce058fd: // Obj_MoveLeft
		{
			float dist=0.0f;
			pParams->GetFloat(CRCD(0x7e832f08,"Dist"),&dist);
			Move_Init( pParams, pScript, GetObject()->GetPos() + GetObject()->m_matrix[X] * dist );	 
			break;
		}
			
        // @script | Obj_MoveToNode | move to any node by node name
        // @parm name | name | node name
        // @parmopt float | time | | default unit is milliseconds
        // @parmopt float | speed | | used instead of time and acceleration
        // @flag mph | time in mph
        // @flag IPS | time in IPS
        // @flag fps | time in fps
        // @parmopt float | acceleration | | change in speed per second
		case 0x8819dd8b:  // "Obj_MoveToNode"
			MoveToNode_Init( pParams, pScript );
			break;
			
        // @script | Obj_MoveToPos | moves to the world position specified
        // by the vector
        // @uparm (0, 0, 0) | position vector 
        // @parmopt float | time | | default unit is milliseconds
        // @parmopt float | speed | | used instead of time and acceleration
        // @parmopt float | acceleration | | change in speed per second
		case 0x84ec6600:  // "Obj_MoveToPos"
			MoveToPos_Init( pParams, pScript, true );
			break;

        // @script | Obj_MoveToRelPos | moves to a position relative to the object
        // @uparm (0, 0, 0) | position vector 
        // @parmopt float | time | | default unit is milliseconds
        // @parmopt float | speed | | used instead of time and acceleration
        // @parmopt float | acceleration | | change in speed per second
		case 0xea81a32b:  // "Obj_MoveToRelPos"
			MoveToPos_Init( pParams, pScript, false );
			break;
		
        // @script | Obj_MoveToLink | Moves to the 1st node that this object is linked to 
        // @parmopt float | time | | default unit is milliseconds
        // @parmopt float | speed | | used instead of time and acceleration
        // @parmopt float | acceleration | | change in speed per second
		// @parm int | LinkNum | link number
        case 0x3bcaac3f:  // "Obj_MoveToLink"
			MoveToLink_Init( pParams, pScript );
			break;
	
        // @script | Obj_StickToGround | 
        // @flag on | turn on (optional - default value is on)
        // @flag off | turn off (default is on)
        // @parm float | distAbove | distance above the object to check
        // for collision
        // @parm float | distBelow | distance below the object to check
        // for collision
		case 0xc5eca638:  // "Obj_StickToGround"
			pParams->GetFloat( 0xa8a5a3c7, &m_col_dist_above ); // "distAbove"
			pParams->GetFloat( 0x182d87a7, &m_col_dist_below ); // "distBelow"
			if ( pParams->ContainsFlag( 0xd443a2bc ) ) // "off"
			{
				m_movingobj_flags &= ~MOVINGOBJ_FLAG_STICK_TO_GROUND;
			}
			else
			{
				m_movingobj_flags |= MOVINGOBJ_FLAG_STICK_TO_GROUND;
				if ( ( m_col_dist_above == 0 ) && ( m_col_dist_below == 0 ) )
				{
					Dbg_Message( "\n%s\nTrying to stick to the ground, distAbove and distBelow are zero!!!", pScript->GetScriptInfo() );
					return CBaseComponent::MF_FALSE;
				}
				StickToGround();
			}
			break;

        // @script | Obj_WaitJumpFinished | Waits for jump to finish, which is when the object 
		// has fallen to the same height it was at when the Obj_Jump command was issued.
		case 0x1e094b84:  // Obj_WaitJumpFinished
		{
			pScript->SetWait(Script::WAIT_TYPE_OBJECT_JUMP_FINISHED,this);
			break;
		}

        // @script | Obj_WaitRotate | wait for rotation to complete
		case 0xbdc4c878:  // Obj_WaitRotate
			pScript->SetWait(Script::WAIT_TYPE_OBJECT_ROTATE,this);
			break;

        // @script | Obj_WaitMove | use to wait for move to complete
		case 0xdbfdd02f:  // Obj_WaitMove
			pScript->SetWait(Script::WAIT_TYPE_OBJECT_MOVE,this);
			break;

		// @script | Obj_WaitStop | use to wait for the object to come to a complete stop
		case CRCC( 0x8d95f1e1, "Obj_WaitStop" ):
			pScript->SetWait(Script::WAIT_TYPE_OBJECT_STOP_FINISHED,this);
			break;

		// @script | Obj_SkipToRestart | skips the object to a restart point
		case 0x3407ad3d: // Obj_SkipToRestart
		{
			uint32 node_name=0;
			Mth::Vector node_pos;
			
			pParams->GetChecksum(NONAME,&node_name);
			int node_index = SkateScript::FindNamedNode(node_name);
			Script::CStruct* p_node_data = SkateScript::GetNode(node_index);
			
			SkateScript::GetPosition(p_node_data, &node_pos);
			GetObject()->SetPos( node_pos );
			this->OrientToNode(p_node_data);
		
			// refresh the model with the new pos/orientation
			Obj::CModelComponent* pModelComponent = (Obj::CModelComponent*)GetModelComponentFromObject( GetObject() );
			pModelComponent->Update();
		}
		break;

		case 0xe606eba2: // Obj_Hover
		{
			if (pParams->ContainsFlag(CRCD(0xd443a2bc,"Off")))
			{
				UndoHover();
				m_movingobj_status &= ~MOVINGOBJ_STATUS_HOVERING;
			}
			else
			{
				m_hover_offset=0;
				
				pParams->GetFloat(CRCD(0xc9fde32c,"Amp"),&m_hover_amp);
				float f=1.0f;
				pParams->GetFloat(CRCD(0xa80bea4a,"Freq"),&f);
				m_hover_period=(int)(1000.0f/f);
				// Add 10% of randomness so that groups of hovering things slowly
				// go out of phase with each other rather than hovering in unison.
				int random_number = Mth::Rnd(m_hover_period*20/10);
				m_hover_period += random_number-m_hover_period/10;

//				printf( "hover period = %d random number = %d\n", m_hover_period, random_number );

				if ( m_hover_period <= 0 )
				{
					// GJ:  make sure that hover period is a valid value
					Dbg_MsgAssert( false, ( "Potential divide-by-zero m_hover_period=%d at %s...  tell Mick!", m_hover_period, pScript->GetScriptInfo() ) );
					m_hover_period = 1;
				}
				
				m_y_before_applying_hover=GetObject()->GetPos()[Y];
				m_movingobj_status |= MOVINGOBJ_STATUS_HOVERING;
			}	
			break;
		}
		
        // @script | Obj_GetSpeed | Gets the object's current speed and puts it into a parameter called speed.
		// By default the units will be miles per hour.
		// @flag ips | Make the speed value be in inches per second
		// @flag fps | Make the speed value be in feet per second
		case 0xe76d73d2: // Obj_GetSpeed 
		{
			float speed=0.0f;
			if (m_movingobj_status & MOVINGOBJ_STATUS_MOVETO)
			{
				speed=m_moveto_speed;
			}
			else if ( GetLockObjComponentFromObject(GetObject()) && GetLockObjComponentFromObject(GetObject())->IsLockEnabled() )
			{
				Mth::Vector d=GetObject()->m_pos - GetObject()->m_old_pos;
				float m_time = Tmr::FrameLength();	 	// dodgy
				if (m_time)
				{
					speed=d.Length()/m_time;
				}	
			}
			else
			{
				speed=GetObject()->m_vel.Length();
			}
			
			// speed is now in inches per sec.
			
			if ( pParams->ContainsFlag( 0xa18b8f32 ) ) // "ips"
			{
				// Nothing to do.
			}
			else if ( pParams->ContainsFlag( 0xaad7c80f ) ) // "fps"
			{
				// Convert to feet per second
				speed/=FEET_TO_INCHES(1.0f);
			} 
			else
			{
				// Convert to miles per hour
				speed/=MPH_TO_INCHES_PER_SECOND(1.0f);
			}
				
			pScript->GetParams()->AddFloat("Speed",speed);
			break;
		}		


        // @script | Obj_Jump | Makes the object jump.
		// @parmopt float | Speed | 150.0 | The upwards speed of the jump
		// @parmopt float | Gravity | -360.0 | The gravity applied to the jump
		// @parmopt vector | heading | | use this heading as jump direction rather than
		// jumping straight up
		// @parmopt float | col_dist_above | 6 | distance used for collision checks above the object
		// @parmopt float | col_dist_below | 6 | distance used for collision checks below the object
		// @flag use_current_heading | use the object's current Z vector as jump direction
		case 0x1ae46261:  // Obj_Jump
		{
			m_jump_speed = 150.0f;
			pParams->GetFloat( CRCD(0xf0d90109,"Speed"), &m_jump_speed );
			m_jump_original_speed = m_jump_speed;

			m_jump_gravity = -360.0f;
			pParams->GetFloat( CRCD(0xa5e2da58,"Gravity"), &m_jump_gravity );

			m_jump_start_pos = GetObject()->m_pos;

			m_jump_col_dist_above = 6.0f;
			pParams->GetFloat( CRCD( 0xecc2c699, "col_dist_above" ), &m_jump_col_dist_above, Script::NO_ASSERT );
			m_jump_col_dist_below = 6.0f;
			pParams->GetFloat( CRCD( 0x5c4ae2f9, "col_dist_below" ), &m_jump_col_dist_below, Script::NO_ASSERT );
			
			if ( pParams->GetVector( CRCD( 0xfd4bc03e, "heading" ), &m_jump_heading, Script::NO_ASSERT ) )
			{
				m_jump_use_heading = true;
				m_jump_speed *= m_jump_heading[Y];
				m_jump_heading[Y] = 0.0f;
			}
			else if ( pParams->ContainsFlag( CRCD( 0xfecffe49, "use_current_heading" ) ) )
			{
				// m_jump_heading = Mth::Vector(0, 0, 1);
				m_jump_heading = GetObject()->GetDisplayMatrix()[Z];
				m_jump_use_heading = true;
				m_jump_speed *= m_jump_heading[Y];
				m_jump_heading[Y] = 0.0f;				
			}
			else
			{				
				m_jump_use_heading = false;
			}

			m_jump_use_land_height = false;
			if ( pParams->GetFloat( CRCD( 0xa1810c27, "land_height" ), &m_jump_land_height, Script::NO_ASSERT ) )
			{
				m_jump_use_land_height = true;
			}

			m_movingobj_status |= MOVINGOBJ_STATUS_JUMPING;
			break;
		}
		
        // @script | Obj_FollowLeader | Makes the object follow a fixed distance behind another object.
        // @parm name | Name | Name of the object to follow
        // @parmopt float | Distance | 100 | Distance behind the leader in inches
		// @flag OrientY | Tilt the object to be at right angles to the path direction
		// @flag Off | Switch off following
		// @flag LeaveYUnaffected | The y coordinate of the object will be left unaffected, so
		// that the object can be clamped to the ground and not follow the skater into the air
		// for example.
		case 0x2fad370d: // Obj_FollowLeader
			FollowLeader_Init( pParams );
			break;


		

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::EnsurePathobExists( CCompositeObject* pCompositeObject )
{
	if ( !mp_pathOb )
	{
		mp_pathOb = new CPathOb( pCompositeObject );
		Dbg_MsgAssert( mp_pathOb,( "Couldn't allocate pathob." ));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPathOb* CMotionComponent::GetPathOb()
{
	return mp_pathOb;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Added by Ken. If the object is following a path, this will return the node
// index of the node it is heading towards. Returns -1 otherwise.
int CMotionComponent::GetDestinationPathNode()
{	
	if ( !(m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH) )
	{
		return -1;
	}	

	if ( !mp_pathOb )
	{
		return -1;
	}	
	//Dbg_MsgAssert(mp_pathOb,("NULL GetPathOb() ?"));
	return mp_pathOb->m_node_to;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CMotionComponent::GetPreviousPathNode()
{	
	if ( !(m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH) )
	{
		return -1;
	}	

	if ( !mp_pathOb )
	{
		return -1;
	}	
	//Dbg_MsgAssert(mp_pathOb,("NULL GetPathOb() ?"));
	return mp_pathOb->m_node_from;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CMotionComponent::Reset()
{
	m_turn_ang_target.Set( );
	m_cur_turn_ang.Set( );
	m_turn_speed_target.Set( );
	m_turn_speed.Set( );
	m_delta_turn_speed.Set( );
	m_angular_friction.Set( );
	m_orig_pos.Set( );
	
	m_moveto_pos.Set( );
	m_moveto_dist = 0.0f;
	m_moveto_dist_traveled = 0.0f;
	m_moveto_speed = 0.0f;
	m_moveto_speed_target = 0.0f;
	m_moveto_acceleration = 0.0f;
	
	m_acceleration = 0.0f;
	m_deceleration = 0.0f;
	m_max_vel = 0.0f;
	m_vel_z = 0.0f;
	m_stop_dist = 0.0f;
	m_stop_dist_traveled = 0.0f;
	m_vel_z_start = 0.0f;
	m_min_stop_vel = 0.0f;
	
	m_rot_time_target = 0.0f;
	m_rot_time = 0.0f;
	
	// don't think this is dangerous...
	m_movingobj_status = 0;
	m_movingobj_flags = 0;
	
	// Reset the Triangle collision caches
	
	m_last_triangle.Init();
	m_car_left_rear_last_triangle.Init();
	m_car_right_rear_last_triangle.Init();
	m_car_mid_front_last_triangle.Init();

	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::SetCorrectRotationDirection( int rotAxis )
{
	
	m_turn_speed[ rotAxis ] = Mth::Abs( m_turn_speed[ rotAxis ] );
	m_turn_speed_target[ rotAxis ] = Mth::Abs( m_turn_speed_target[ rotAxis ] );
	m_delta_turn_speed[ rotAxis ] = Mth::Abs( m_delta_turn_speed[ rotAxis ] );
	m_angular_friction[ rotAxis ] = Mth::Abs( m_angular_friction[ rotAxis ] );
	if ( m_turn_ang_target[ rotAxis ] < 0 )
	{
		m_turn_speed[ rotAxis ] *= -1.0f;
		m_turn_speed_target[ rotAxis ] *= -1.0f;
		m_delta_turn_speed[ rotAxis ] *= -1.0f;
		m_angular_friction[ rotAxis ] *= -1.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::InitPath( int nodeNumber )
{
	if (mp_path_object_tracker)
	{
		mp_path_object_tracker->StopTrackingObject( GetObject() );
	}	

	mp_path_object_tracker=Obj::CPathMan::Instance()->TrackObject( GetObject(), nodeNumber );	
	// Note: mp_path_object_tracker could be NULL
	
	EnsurePathobExists( GetObject() );
	GetPathOb()->NewPath( nodeNumber, GetObject()->GetPos() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::FollowPath_Init( Script::CStruct* pParams )
{   
	uint32 nodeChecksum;
	if ( pParams->GetChecksum( 0xa1dc81f9, &nodeChecksum ) ) // "name"
	{
		int nodeNum = SkateScript::FindNamedNode( nodeChecksum );
		InitPath( nodeNum );
		m_movingobj_status |= MOVINGOBJ_STATUS_ON_PATH;
		// these can't coexist:
		m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
	}
	else
	{
		Dbg_MsgAssert( 0,( "Must specify name of node in Obj_FollowPath, using name = nodeName." ));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMotionComponent::PickRandomWaypoint()
{
	return ( m_movingobj_flags & MOVINGOBJ_FLAG_RANDOM_PATH_MODE );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::HitWaypoint( int nodeIndex )
{	
	uint32 scriptChecksum;
	Script::CStruct* pNodeData;
	pNodeData = SkateScript::GetNode( nodeIndex );
	Dbg_MsgAssert( pNodeData,( "Waypoint gave us a bad node index." ));
	m_current_node = nodeIndex;
	
	if ( pNodeData->GetChecksum( 0x3aefe377, &scriptChecksum ) ) 	// "SpawnObjScript"
	{
		Script::CStruct* pScriptParams = NULL;
		pNodeData->GetStructure( CRCD(0x7031f10c,"Params"), &pScriptParams );
#ifdef __NOPT_ASSERT__
		Script::CScript* p_script=GetObject()->SpawnScriptPlease( scriptChecksum, pScriptParams );
		p_script->SetCommentString("Created by CMotionComponent::HitWaypoint");
#else
		GetObject()->SpawnScriptPlease( scriptChecksum, pScriptParams );
#endif
		return;
	}
	
	if ( pNodeData->GetChecksum( 0x86125749, &scriptChecksum ) ) 	// "SwitchObjScript"
	{
		Script::CStruct* pScriptParams = NULL;
		pNodeData->GetStructure( CRCD(0x7031f10c,"Params"), &pScriptParams );
		GetObject()->SwitchScript( scriptChecksum, pScriptParams );
		return;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMotionComponent::LookAt_Init( Script::CStruct* pParams, const Mth::Vector& lookAtPos )
{	
	int deltaDegreesPerSecond = 360;
	int deltaDeltaDegreesPerSecond = 0;
	int lockAxis = ( 1 << Y );
	float time = 0.0f;
	
	if ( !( m_movingobj_status & MOVINGOBJ_STATUS_LOOKAT ) )
	{
		m_movingobj_status |= MOVINGOBJ_STATUS_LOOKAT;
		m_movingobj_status &= ~MOVINGOBJ_STATUS_ROTXYZ;
		m_movingobj_flags &= ~( MOVINGOBJ_FLAG_CONST_ROTX | MOVINGOBJ_FLAG_CONST_ROTY | MOVINGOBJ_FLAG_CONST_ROTZ );
	}

	pParams->GetFloat( 0x906b67ba, &time ); // "time"
	if ( !time )
	{
		pParams->GetInteger( 0xf0d90109, &deltaDegreesPerSecond );		// 'speed'
		pParams->GetInteger( 0x85894a84, &deltaDeltaDegreesPerSecond );	// 'acceleration'
		pParams->GetInteger( 0xa4aecb20, &lockAxis );  // "lockAxis"
	}
	
	float threshold=0.0f;
	pParams->GetFloat(0xf73a4efd,&threshold); // AngleThreshold
	threshold=DEGREES_TO_RADIANS(threshold);
	
	bool rotation_required=false;
	int i;
	for ( i = 0; i < 3; i++ )
	{
		if ( lockAxis & ( 1 << i ) )
		{
			if ( SetUpLookAtPos( lookAtPos, GetObject()->GetPos(), i == Z ? Y : Z, i, threshold ) )
			{
				if ( time )
				{
					m_delta_turn_speed[ i ] = 0.0f;
					m_turn_speed[ i ] = m_turn_ang_target[ i ] / time;
				}
				else
				{
					m_delta_turn_speed[ i ] = DEGREES_TO_RADIANS( deltaDeltaDegreesPerSecond );
					if ( !m_delta_turn_speed[ i ] )
						m_turn_speed[ i ] = DEGREES_TO_RADIANS( deltaDegreesPerSecond );
					else
					{
						m_turn_speed[ i ] = 0.0f;
						m_turn_speed_target[ i ] = 0.0f;
					}
					SetCorrectRotationDirection( i );
				}
				rotation_required=true;
			}
		}
	}
	return rotation_required;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::QuatRot_Setup( Script::CStruct* pParams, const Mth::Matrix& rot )
{
	if ( !mp_slerp )
	{
		mp_slerp = new Mth::SlerpInterpolator;
	}

    Mth::Matrix tempMatrix = rot;
	mp_slerp->setMatrices( &GetObject()->GetMatrix(), &tempMatrix );
	
	m_rot_time = 0.0f;
	m_rot_time_target = 0.0f;
	pParams->GetFloat( 0x906b67ba, &m_rot_time_target ); // "time" ( in seconds )
	m_movingobj_status |= MOVINGOBJ_STATUS_QUAT_ROT;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::QuatRot_Init( Script::CStruct* pParams )
{
	Mth::Vector rot( 0.0f, 0.0f, 0.0f );
	if ( pParams->GetVector( 0x91a4c826, &rot ) )  // relative
	{
		if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
		{
			rot[ Z ] = -rot[ Z ];
			rot.ConvertToMaxCoords( );
		}
		rot.DegreesToRadians( );
		Mth::Matrix temp = GetObject()->GetMatrix();
		temp.RotateLocal( rot );
		QuatRot_Setup( pParams, temp );
	}
	else if ( pParams->GetVector( 0x592089a9, &rot ) ) //absolute
	{
		if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
		{
			rot[ Z ] = -rot[ Z ];
			rot.ConvertToMaxCoords( );
		}
		rot.DegreesToRadians( );
		Mth::Matrix temp( rot[ X ], rot[ Y ], rot[ Z ] );
		QuatRot_Setup( pParams, temp );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMotionComponent::SetUpLookAtPos( const Mth::Vector& lookToPos, const Mth::Vector& currentPos, int headingAxis, int rotAxis, float threshold )
{
	Mth::Vector pathHeading = lookToPos - currentPos;
	
	m_cur_turn_ang[ rotAxis ] = 0.0f;
	m_turn_ang_target[ rotAxis ] = Mth::GetAngle( GetObject()->GetMatrix(), pathHeading, headingAxis, rotAxis );
	if ( fabs(m_turn_ang_target[ rotAxis ]) > threshold )
	{
		m_movingobj_status |= ( MOVINGOBJ_STATUS_ROTX << rotAxis );
/*		Dbg_MsgAssert( ( ( !( m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH ) ) ||
						( m_movingobj_flags & MOVINGOBJ_FLAG_INDEPENDENT_HEADING ) ) ||
						rotAxis == Z,( "Can't rotate on X or Y axis while using auto-heading on path." ));*/
		return true;
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::RotAxis_Init( Script::CStruct* pParams, Script::CScript* pScript, int rotAxis )
{
	
	float mult = 1.0f;

	// Mick - Just being safe here.  	
	Dbg_MsgAssert( rotAxis == X || rotAxis == Y || rotAxis== Z,( "Illegal rotAxis %d", rotAxis ));
	
	
	// get out of lookat mode... and shit.
	if ( m_movingobj_status & MOVINGOBJ_STATUS_LOOKAT )
	{
		m_movingobj_status &= ~MOVINGOBJ_STATUS_LOOKAT;
		m_movingobj_status &= ~MOVINGOBJ_STATUS_ROTXYZ;
	}
	if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
	{
		switch ( rotAxis )
		{
			case ( X ):
				mult = -1.0f;
				break;

			case ( Y ):
				rotAxis = Z;
//				mult = -1.0f;
				break;

			case ( Z ):
				rotAxis = Y;
				break;
			
			default:
				Dbg_MsgAssert( 0,( "Illegal rotAxis" ));
				break;
		}
	}

	// clear this in case it isn't set...
	m_angular_friction[ rotAxis ] = 0.0f;
	m_delta_turn_speed[ rotAxis ] = 0.0f;
	m_turn_ang_target[ rotAxis ] = 0.0f;

	if ( pParams->GetFloat( 0xff7ebaf6, &m_turn_ang_target[ rotAxis ] ) ) // "angle"
	{
		m_turn_ang_target[ rotAxis ] = mult * DEGREES_TO_RADIANS( m_turn_ang_target[ rotAxis ] );
		m_movingobj_flags &= ~( MOVINGOBJ_FLAG_CONST_ROTX << rotAxis );
		m_cur_turn_ang[ rotAxis ] = 0.0f;
		m_turn_speed[ rotAxis ] = 0.0f;
		if ( !m_turn_ang_target[ rotAxis ] )
		{
			// stop the rotation!
			m_movingobj_status &= ~( MOVINGOBJ_STATUS_ROTX << rotAxis );
			return;
		}
	}
	
	if ( pParams->GetFloat( 0xf59ff7d7, &m_angular_friction[ rotAxis ] ) ) // "deceleration"
	{
		// K: If a deceleration is specified, the 'speed' value specified is the target speed.
		// The current m_turn_speed remains unchanged so that it slows down to the target speed.
		// (TT427)
		m_angular_friction[ rotAxis ] = DEGREES_TO_RADIANS( m_angular_friction[ rotAxis ] );
		if ( pParams->GetFloat( 0xf0d90109, &m_turn_speed_target[ rotAxis ] ) ) // "speed"
		{
			m_turn_speed_target[ rotAxis ] = DEGREES_TO_RADIANS( m_turn_speed_target[ rotAxis ] );
		}
		else
		{
			Dbg_MsgAssert( 0,( "\n%s\nMust specify speed for Obj_Rot functions (degrees per second).", pScript->GetScriptInfo( ) ));
		}
	}
	else if ( pParams->GetFloat( 0x85894a84, &m_delta_turn_speed[ rotAxis ] ) ) // acceleration
	{
		m_delta_turn_speed[ rotAxis ] = DEGREES_TO_RADIANS( m_delta_turn_speed[ rotAxis ] );
		if ( pParams->GetFloat( 0xf0d90109, &m_turn_speed_target[ rotAxis ] ) ) // "speed"
		{
			m_turn_speed_target[ rotAxis ] = DEGREES_TO_RADIANS( m_turn_speed_target[ rotAxis ] );
		}
		else
		{
			Dbg_MsgAssert( 0,( "\n%s\nMust specify speed for Obj_Rot functions (degrees per second).", pScript->GetScriptInfo( ) ));
		}
		if ( !( m_movingobj_status & ( MOVINGOBJ_STATUS_ROTX << rotAxis ) ) )
		{
			m_turn_speed[ rotAxis ] = 0.0f;
		}
	}
	else
	{
		if ( pParams->GetFloat( 0xf0d90109, &m_turn_speed[ rotAxis ] ) ) // "speed"
		{
			m_turn_speed[ rotAxis ] = DEGREES_TO_RADIANS( m_turn_speed[ rotAxis ] );
		}
		else
		{
			Dbg_MsgAssert( 0,( "\n%s\nMust specify speed for Obj_Rot functions (degrees per second).", pScript->GetScriptInfo( ) ));
		}
	}
	if ( !( m_turn_ang_target[ rotAxis ] || m_angular_friction[ rotAxis ] ) )
	{
		m_movingobj_flags |= ( MOVINGOBJ_FLAG_CONST_ROTX << rotAxis );
	}
	if ( !( m_movingobj_flags & ( MOVINGOBJ_FLAG_CONST_ROTX << rotAxis ) ) )
	{
		SetCorrectRotationDirection( rotAxis );
	}
	m_movingobj_status |= ( MOVINGOBJ_STATUS_ROTX << rotAxis );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::LookAtNodeLinked_Init( Script::CStruct* pParams, Script::CScript* pScript )
{	
	int numLinks;
	numLinks = SkateScript::GetNumLinks( m_current_node );
	if ( !numLinks )
	{
		Dbg_MsgAssert( 0,( "\n%s\nNo node linked to object calling Obj_LookAtNodeLinked", pScript->GetScriptInfo( ) ));
		return;
	}
	
	int link_num=1;
	pParams->GetInteger( 0xe85997d0, &link_num ); // "linkNum"
	// Designers number the links from 1 to n, so convert to programmer form
	link_num -= 1;
	
	// Print a warning message instead of asserting, to save having to reboot.
	if (link_num<0 || link_num>=numLinks)
	{
		#ifdef __NOPT_ASSERT__
		printf("!!!!!!!!!! Bad LinkNum of %d sent to Obj_LookAtNodeLinked, range is 1 to %d\n",link_num+1,numLinks);
		#endif
		link_num=0;
	}	
	
	Mth::Vector lookAtPos;
	SkateScript::GetPosition( SkateScript::GetLink( m_current_node, link_num ), &lookAtPos );
	LookAt_Init( pParams, lookAtPos );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::LookAtNode_Init( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 checksum = 0;
	
	pParams->GetChecksum( 0xa1dc81f9, &checksum ); // "name"
	if ( !checksum )
	{
		Dbg_MsgAssert( 0,( "\n%s\nNeed node name in Obj_LookAtNode", pScript->GetScriptInfo( ) ));
		return;
	}
	Mth::Vector lookAtPos;
	SkateScript::GetPosition( SkateScript::FindNamedNode( checksum ), &lookAtPos );
	LookAt_Init( pParams, lookAtPos );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::LookAtPos_Init( Script::CStruct* pParams, bool absolute )
{
	
	Mth::Vector vec( 0, 0, 0 );
	pParams->GetVector( NONAME, &vec );
	if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
	{
		vec.ConvertToMaxCoords( );
	}
	if ( !absolute )
	{
		vec += GetObject()->GetPos();
	}
	LookAt_Init( pParams, vec );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::Rot( int rotAxis, float deltaRot )
{
	switch ( rotAxis )
	{
		case ( X ):
			GetObject()->m_matrix.RotateXLocal( deltaRot );
			GetObject()->m_matrix.OrthoNormalizeAbout( X );
			break;
		
		case ( Y ):
			GetObject()->m_matrix.RotateYLocal( deltaRot );
			GetObject()->m_matrix.OrthoNormalizeAbout( Y );
			break;
		
		case ( Z  ):
			GetObject()->m_matrix.RotateZLocal( deltaRot );
			GetObject()->m_matrix.OrthoNormalizeAbout( Z );
			break;
		
		default:
			Dbg_MsgAssert( 0,( "illegal rot axis." ));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::RotateToFacePos( const Mth::Vector pos )
{   
	Mth::Vector desiredHeading;
	desiredHeading = pos - GetObject()->GetPos();
	float deltaY = Mth::GetAngle( GetObject()->GetMatrix(), desiredHeading );
	GetObject()->m_matrix.RotateYLocal( deltaY );
	GetObject()->m_matrix.OrthoNormalizeAbout( Y );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Follow the path that's linked to the current waypoint (node) that the object is sitting at.
void CMotionComponent::FollowPathLinked_Init( Script::CStruct* pParams, Script::CScript* pScript )
{   
	int numLinks;
	int nodeNum;

	if ( pParams->ContainsFlag( 0x422e1fce ) ) // originalNode
	{
		nodeNum = m_start_node;
	}
	else
	{
		nodeNum = m_current_node;
	}
	
	numLinks = SkateScript::GetNumLinks( nodeNum );
	if ( !numLinks )
	{
		Dbg_MsgAssert( 0,( "\n%s\nNo node linked to object (from node %d) calling Obj_FollowPathLinked", pScript->GetScriptInfo( ), nodeNum ));
		return;
	}
	if ( ( numLinks > 1 ) && PickRandomWaypoint( ) )
	{
		InitPath( SkateScript::GetLink( nodeNum, Mth::Rnd( numLinks ) ) );
	}
	else
	{
		InitPath( SkateScript::GetLink( nodeNum, 0 ) );
	}
	m_movingobj_status |= MOVINGOBJ_STATUS_ON_PATH;
	// these can't coexist:
	m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Follow the path from current position to the position of the waypoint last stored
// in the script call to Obj_StoreNode (when a pedestrian, for example, has to jump
// out of the way of the player, he stores his current position and the current node
// that he's heading for... then he walks back to his position, and finally ends up
// heading to the waypoint he was heading for before he left the path to jump out of the way).
void CMotionComponent::FollowStoredPath_Init( Script::CStruct* pParams )
{   
	InitPath( m_stored_node );
	m_movingobj_status |= MOVINGOBJ_STATUS_ON_PATH;
	// these can't coexist:
	m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Physics for when the moving object is following a path.
void CMotionComponent::DoPathPhysics( void )
{	
	if ( m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH )
	{
		// if we're stopping:
		if ( m_movingobj_flags & MOVINGOBJ_FLAG_STOP_ALONG_PATH )
		{
			if ( m_stop_dist_traveled >= m_stop_dist )
			{
				return;
			}
			// keep slowing down...
			m_stop_dist_traveled += m_vel_z * m_time;
			if ( ( !m_stop_dist ) || ( m_stop_dist_traveled >= m_stop_dist ) )
			{
				m_vel_z = 0;
			}
			else
			{
				float percent = m_stop_dist_traveled / m_stop_dist;
				m_vel_z = ( m_vel_z_start * ( 1.0f - percent ) ) + ( m_min_stop_vel * ( percent ) );
			}
			return;
		}
		
		// Adjust velocity...
		if ( m_vel_z < m_max_vel )
		{
			if ( m_acceleration )
			{
				m_vel_z += ( m_acceleration * m_time );
				if ( m_vel_z > m_max_vel )
				{
					m_vel_z = m_max_vel;
				}
			}
			else
			{
				m_vel_z = m_max_vel;
			}
		}
		else if ( m_vel_z > m_max_vel )
		{
			if ( m_deceleration )
			{
				m_vel_z -= ( m_deceleration * m_time );
				if ( m_vel_z < m_max_vel )
				{
					m_vel_z = m_max_vel;
				}
			}
			else
			{
				m_vel_z = m_max_vel;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#if 0

// UNUSED...

// BB: I have added a new version of this function below.  I moved the code
// from the CallMemberFunction func because I needed to do the same thing from c.
// I have left this here for reference, just in case.
CCompositeObject* CMotionComponent::GetNextObjectOnPath( float range )
{
	if (!(m_movingobj_status & MOVINGOBJ_STATUS_ON_PATH) || !GetPathOb() || !mp_path_object_tracker)
	{
		return NULL;
	}	
	
	float distance_covered=0.0f;
	Mth::Vector path_pos=m_pos;
	int next_node=GetPathOb()->m_node_to;
	
	// Copy the non-NULL object pointers from the tracker to a small local array for speed,
	// since they may need to be stepped through several times and the tracker's array
	// contains lots of NULL's
	#define MAX_OBS 20
	CCompositeObject* pp_objects[MAX_OBS];
	int num_objects=0;
	
	const CSmtPtr* pp_object_list=mp_path_object_tracker->GetObjectList();
	for (int i=0; iGetDestinationPathNode()==next_node)
			{
				float d=Mth::Distance(pp_objects[i]->m_pos,path_pos);
				if (distance_covered+d < range)
				{
					if (!p_nearest || dGetArray(0x2e7d5ee7/*Links*/,&p_links);
		
		if (!p_links)
		{
			break;
		}
		
		next_node=p_links->GetInteger(0);	
	}
	
	return p_nearest;
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// returns TRUE if instant move, FALSE otherwise.
bool CMotionComponent::Move_Init( Script::CStruct* pParams, Script::CScript* pScript, Mth::Vector pos, int nodeNum )
{   
	int units = UNITS_MPH;
	float time;
	float speed;

	m_moveto_pos = pos;
	m_moveto_dist = Mth::Distance( m_moveto_pos, GetObject()->GetPos() );
	m_moveto_acceleration = 0.0f;
	
	// initialize everything:
	m_orig_pos = GetObject()->GetPos();
	m_moveto_dist_traveled = 0.0f;
	m_movingobj_status |= MOVINGOBJ_STATUS_MOVETO;
	// can't follow path and moveto at the same time:
	m_movingobj_status &= ~MOVINGOBJ_STATUS_ON_PATH;
	m_moveto_node_num = -1;
	if ( pParams->GetFloat( 0x906b67ba, &time ) ) // "time"
	{
		// time is in seconds:
		Dbg_MsgAssert( time,( "\n%s\nCan't have zero for time...", pScript->GetScriptInfo( ) ));
		m_moveto_speed = m_moveto_dist / time;
	}
	else if ( pParams->GetFloat( 0xf0d90109, &speed ) ) // "speed"
	{	// use speed and acceleration:
		if ( pParams->ContainsFlag( 0x2ce7ee02 ) ) // "mph"
		{
			units = UNITS_MPH;
		}
		else if ( pParams->ContainsFlag( 0xaad7c80f ) ) // "fps"
		{
			units = UNITS_FPS;
		} 
		else if ( pParams->ContainsFlag( 0xa18b8f32 ) ) //IPS
		{
			units = UNITS_IPS;
		}

		pParams->GetFloat( 0x85894a84, &m_moveto_acceleration ); // "acceleration"

		if ( units == UNITS_MPH )
		{
			speed = MPH_TO_IPS( speed );
			m_moveto_acceleration = MPH_TO_IPS( m_moveto_acceleration );
		}
		else if ( units == UNITS_FPS )
		{
			speed *= 12.0f;
			m_moveto_acceleration *= 12.0f;
		}
		
		if ( !m_moveto_acceleration )
		{
			m_moveto_speed = speed;
		}
		else
		{
			m_moveto_speed = 0.0f;
			m_moveto_speed_target = speed;
		}
	}
	else
	{
		// instantaneous move:
		GetObject()->SetPos(m_moveto_pos);
		m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;
		
		if ( nodeNum != -1 )
		{
			if ( pParams->ContainsFlag( 0x90a91232 ) ) // "orient"
			{
				Script::CStruct* pNodeData = SkateScript::GetNode( nodeNum );
				OrientToNode( pNodeData );
			}
		}
		
		// GJ:  Need to re-implement this on THPS5...
		// (used to fix quick-jump bug on Xbox/NGC,
		// where LODing code wasn't refreshing the model's
		// position)

#if 0
#ifndef __PLAT_NGPS__
		// Rebuild display matrix. Gary - you should look at this post THPS4 and generalize.
		Mth::Matrix rootMatrix;
		rootMatrix = m_display_matrix;
		rootMatrix[Mth::POS] = m_pos;
		rootMatrix[Mth::POS][W] = 1.0f;
		if( GetModel() )
		{
			GetModel()->Render( &rootMatrix, true, GetSkeleton() );

			// Also update the shadow if relevant.
			if( mp_shadow && mp_shadow->GetShadowType() != Gfx::vDETAILED_SHADOW )
			{
				update_shadow();
			}
		}
#endif
#endif

		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::MoveToNode_Init( Script::CStruct* pParams, Script::CScript* pScript )
{   
	Mth::Vector pos;
	
	// if we're moving to a named node:
	uint32 checksum = 0;
	pParams->GetChecksum( 0xa1dc81f9, &checksum ); // "name"
	if ( checksum )
	{
		int nodeNum = SkateScript::FindNamedNode( checksum );
		SkateScript::GetPosition( nodeNum, &pos );
		if ( Move_Init( pParams, pScript, pos, nodeNum ) )
		{
			HitWaypoint( nodeNum );
		}
		else
		{
			m_moveto_node_num = nodeNum;
		}
		return;
	}
	Dbg_MsgAssert( 0,( "\n%s\nObj_MoveToNode requires node name specification (name = *)", pScript->GetScriptInfo( ) ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::MoveToPos_Init( Script::CStruct* pParams, Script::CScript* pScript, bool absolute )
{   
	Mth::Vector vec( 0, 0, 0 );
	pParams->GetVector( NONAME, &vec );
	if ( pParams->ContainsFlag( 0x26af9dc9 ) ) // "FLAG_MAX_COORDS"
	{
		vec.ConvertToMaxCoords( );
	}
	if ( !absolute )
	{
		vec += GetObject()->GetPos();
	}
	Move_Init( pParams, pScript, vec );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::MoveToLink_Init( Script::CStruct* pParams, Script::CScript* pScript )
{   
	int linkNum = 0;
	int numLinks;
	numLinks = SkateScript::GetNumLinks( m_current_node );
	if ( !numLinks )
	{
		Dbg_MsgAssert( 0,( "Obj_MoveToLink requires a link to the object's current node (last node hit in path)." ));
		return;
	}
	if ( pParams->GetInteger( 0xe85997d0, &linkNum ) ) // "linkNum"
	{
		linkNum -= 1;
	}
	else if ( ( numLinks > 1 ) && ( pParams->ContainsFlag( 0x90a1c52a ) ) ) // "randomLink"
	{
		linkNum = Mth::Rnd( numLinks );
	}
	Mth::Vector pos;
	Dbg_MsgAssert( linkNum < numLinks,( "\n%s\nLink num is greater than the number of links.", pScript->GetScriptInfo( ) ));
	int nodeNum;
	nodeNum = SkateScript::GetLink( m_current_node, linkNum );
	SkateScript::GetPosition( nodeNum, &pos );
	if ( Move_Init( pParams, pScript, pos ) )
	{
		HitWaypoint( nodeNum );
	}
	else
	{
		m_moveto_node_num = nodeNum;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMotionComponent::Move( void )
{	
	if ( m_moveto_acceleration )
	{
		m_moveto_speed += m_moveto_acceleration * Tmr::FrameRatio( );
		if ( m_moveto_speed >= m_moveto_speed_target )
		{
			m_moveto_speed = m_moveto_speed_target;
			m_moveto_acceleration = 0.0f;
		}
	}
	m_moveto_dist_traveled += m_time * m_moveto_speed;
	if ( m_moveto_dist_traveled >= m_moveto_dist )
	{
		GetObject()->m_pos = m_moveto_pos;
		if ( m_moveto_node_num != -1 )
		{
			HitWaypoint( m_moveto_node_num );
		}
		return true;
	}
	Mth::Vector newPos = m_moveto_pos - m_orig_pos;
	newPos *= ( m_moveto_dist_traveled / m_moveto_dist );
	GetObject()->m_pos = m_orig_pos + newPos;
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::OrientToNode(Script::CStruct* pNodeData)
{
	Mth::Vector rot;
	GetObject()->m_matrix.Ident( );	   		// Set identity before we decided if we need to rotate or not, in case we don't.
	SkateScript::GetAngles( pNodeData, &rot );
	if ( rot[ X ] || rot[ Y ] || rot[ Z ] )
	{
		// AML: This was rotating Z, then X, then Y
		//		See me if there are problems, and I'll change
		//		the exporter to do Z,X,Y

		//m_matrix.RotateZ( rot[ Z ] );
		GetObject()->GetMatrix().RotateX( rot[ X ] );
		GetObject()->GetMatrix().RotateY( rot[ Y ] );
		GetObject()->GetMatrix().RotateZ( rot[ Z ] );
	}	
	//printf ("OrientToNode %d, (%f,%f,%f)\n",nodeNum,rot[X],rot[Y],rot[Z]);
	
	// sync up the display matrix to the physics matrix
	GetObject()->GetDisplayMatrix() = GetObject()->m_matrix;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::QuatRot( void )
{	
	float percent;
	m_rot_time += m_time;
	if ( ( !m_rot_time_target ) || ( m_rot_time >= m_rot_time_target ) )
	{
		percent = 1.0f;
		m_movingobj_status &= ~MOVINGOBJ_STATUS_QUAT_ROT;
	}
	else
	{
		percent = m_rot_time / m_rot_time_target;
	}
	
	Dbg_MsgAssert( mp_slerp,( "Missing mp_slerp..." ));
    mp_slerp->getMatrix( &GetObject()->m_matrix, percent );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::FollowPath( void )
{   
	// temp hack to separate new peds from old path ob stuff
	Obj::CPedLogicComponent* pPedLogicComp = GetPedLogicComponentFromObject( GetObject() );
	if ( pPedLogicComp )
		return;

	Dbg_MsgAssert( GetPathOb(),( "Shouldn't be in FOLLOW_WAYPOINTS state without pathob." ));

    Mth::Vector movement = -GetObject()->GetPos();				
				
	// change velocity:
	DoPathPhysics();

	// do path traversal:
	if ( m_vel_z )
	{
		/*
		Mth::Vector d=GetPathOb()->m_wp_pos_to-GetPathOb()->m_wp_pos_from;
		m_shadow_normal.Set(0.0f,1.0f,0.0f);
		m_shadow_normal=Mth::CrossProduct(m_shadow_normal,d);
		m_shadow_normal=Mth::CrossProduct(m_shadow_normal,d);
		m_shadow_normal.Normalize();
		*/
		
		// send in our forward direction, distance traveled...
		//if ( GetPathOb()->TraversePath( is_being_skitched, m_vel_z * m_time, 
		//	( m_movingobj_flags & MOVINGOBJ_FLAG_INDEPENDENT_HEADING ) ? NULL : &m_matrix[ Z ] ) )
		if ( GetPathOb()->TraversePath( m_vel_z * m_time, 
			( m_movingobj_flags & MOVINGOBJ_FLAG_INDEPENDENT_HEADING ) ? NULL : &GetObject()->m_matrix[ Z ] ) )
		{
			// path is done... turn off flag:
			m_movingobj_status &= ~MOVINGOBJ_STATUS_ON_PATH;
			m_vel_z = 0.0f;
		}

		if ( !( m_movingobj_flags & MOVINGOBJ_FLAG_INDEPENDENT_HEADING ) )
		{
			// set up the matrix according to the nav point heading...
			GetPathOb()->SetHeading( &GetObject()->m_matrix[ Z ] );
			if ( m_movingobj_flags & MOVINGOBJ_FLAG_NO_PITCH_PLEASE )
			{
				if ( ( GetObject()->m_matrix[ Z ] )[ Y ] )
				{
					( GetObject()->m_matrix[ Z ] )[ Y ] = 0.0f;
					GetObject()->m_matrix[ Z ].Normalize( );
				}
			}
			// this heading may be changed in a moment, when we stick
			// the object to the ground... if it isn't stuck, we should
			// normalize the matrix around that heading...
			if ( !( m_movingobj_flags & MOVINGOBJ_FLAG_STICK_TO_GROUND ) )
			{
				// make sure our matrix is orthagonal:
				GetObject()->m_matrix[ X ] = Mth::CrossProduct( GetObject()->m_matrix[ Y ], GetObject()->m_matrix[ Z ]);
				GetObject()->m_matrix[ X ].Normalize();
				// This will work for things whose Y axis is always up...
				// May have to add flags for things that rotate along Z axis or
				// far along X axis:
				GetObject()->m_matrix[ Z ] = Mth::CrossProduct( GetObject()->m_matrix[ X ], GetObject()->m_matrix[ Y ] );
				GetObject()->m_matrix[ Z ].Normalize( );
			}
		}
		if ( m_movingobj_flags & MOVINGOBJ_FLAG_STICK_TO_GROUND )
		{
			// Just move it to the nav pos ( except for y axis ) then stick it to the ground...
			float origY = GetObject()->m_pos[ Y ];
			GetObject()->m_pos = GetPathOb()->m_nav_pos;
			GetObject()->m_pos[ Y ] = origY;

			StickToGround();
		}
		else
		{
			float origY = GetObject()->m_pos[ Y ];
			GetObject()->m_pos = GetPathOb()->m_nav_pos;
			if ( m_movingobj_flags & MOVINGOBJ_FLAG_CONSTANT_HEIGHT )
			{
				GetObject()->m_pos[ Y ] = origY;
			}
		}
	}

	movement += GetObject()->m_pos;				// actually calculate movement	
	GetObject()->m_vel = movement / m_time; 		// calculate the velocity, so we can impart velocity in things we hit, etc
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// call SetUpLookAtPos first, or one of the other rotate
// setup functions, then call this until it returns true:
bool CMotionComponent::Rotate( int rotAxis )
{	

	bool done = false;

	// update angular acceleration/angular velocity:
	if ( m_delta_turn_speed[ rotAxis ] )
	{
		m_turn_speed[ rotAxis ] += m_delta_turn_speed[ rotAxis ] * m_time;
		if ( ( Mth::Abs( m_turn_speed[ rotAxis ] ) > Mth::Abs( m_turn_speed_target[ rotAxis ] ) )
			&& ( ( m_turn_speed[ rotAxis ] > 0.0f ) == ( m_turn_speed_target[ rotAxis ] > 0.0f ) ) )
		{
			// stop angular acceleration...settle on the target:
			m_turn_speed[ rotAxis ] = m_turn_speed_target[ rotAxis ];
		}
	}
	
	if ( m_angular_friction[ rotAxis ] )
	{
		float friction = m_angular_friction[ rotAxis ] * m_time;
		// Ken: Changed this so that it decelerates down to the m_turn_speed_target rather than 0
		if ( Mth::Abs( friction ) > Mth::Abs( m_turn_speed[ rotAxis ] - m_turn_speed_target[ rotAxis ] ) )
		{
			// Settle on the target speed
			m_turn_speed[ rotAxis ] = m_turn_speed_target[ rotAxis ];
			// Switch off the friction so that it deos not slow down any more
			m_angular_friction[ rotAxis ] = 0.0f;
			
			Dbg_Message( "done rotating due to deceleration... %s angle traversed = %f", 
				rotAxis == X ? "X" : ( rotAxis == Y ? "Y" : "Z" ), RADIANS_TO_DEGREES( m_cur_turn_ang[ rotAxis ] ) );
			if (m_turn_speed_target[ rotAxis ]==0.0f)
			{
				return true; // done rotating...
			}
			else
			{
				return false;  
			}	
		}
		else
		{
			m_turn_speed[ rotAxis ] -= friction;
		}
	}
	float cur_turn_dist = m_turn_speed[ rotAxis ] * m_time;
	
	if ( !( m_movingobj_flags & ( MOVINGOBJ_FLAG_CONST_ROTX << rotAxis ) ) )
	{
		bool positive = m_turn_ang_target[ rotAxis ] > m_cur_turn_ang[ rotAxis ];
		m_cur_turn_ang[ rotAxis ] += cur_turn_dist;
		if ( positive != ( m_turn_ang_target[ rotAxis ] > m_cur_turn_ang[ rotAxis ] ) )
		{
			//done rotating!
			done = true;
			// and deltaYRot should be altered so as to hit 0 exactly:
			cur_turn_dist += m_turn_ang_target[ rotAxis ] - m_cur_turn_ang[ rotAxis ];
	//		m_cur_turn_ang[ rotAxis ] = m_turn_ang_target[ rotAxis ];
		}
	}

	Rot( rotAxis, cur_turn_dist );
	
	return ( done );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::RotUpdate( void )
{   
	// rotate states:
	for ( int i = 0; i < 3; i++ )
	{
		if ( m_movingobj_status & ( MOVINGOBJ_STATUS_ROTX << i ) )
		{
			if ( Rotate( i ) )
			{
				m_movingobj_status &= ~( MOVINGOBJ_STATUS_ROTX << i );
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


static bool s_do_collision_check(const Mth::Vector *p_v0, const Mth::Vector *p_v1, Mth::Vector *p_collisionPoint, 
								 STriangle *p_lastTriangle)
{
	// First, see if there is a collision with the last triangle, and if there is, use that.
	Mth::Vector d=*p_v1-*p_v0;
	float alpha=0.0f;
	if (Nx::CCollObj::sRayTriangleCollision(p_v0, &d,
											&p_lastTriangle->mpVertices[0],
											&p_lastTriangle->mpVertices[1],
											&p_lastTriangle->mpVertices[2], &alpha))
	{
		*p_collisionPoint = *p_v0+d*alpha;
		return true;
	}
	else
	{
		CFeeler feeler;
		if (feeler.GetCollision(*p_v0,*p_v1, false))
		{
			Nx::CCollObj* p_col_obj=feeler.GetSector();
			Dbg_MsgAssert(p_col_obj->IsTriangleCollision(),("Not triangle collision !!!"));
	
			Nx::CCollObjTriData* p_tri_data=p_col_obj->GetGeometry();
			Dbg_MsgAssert(p_tri_data,("NULL p_tri_data"));
	
			int face_index=feeler.GetFaceIndex();
			p_lastTriangle->mpVertices[0]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 0));
			p_lastTriangle->mpVertices[1]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 1));
			p_lastTriangle->mpVertices[2]=p_col_obj->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index, 2));
	
			// if there is a collision, then snap to it
			*p_collisionPoint = feeler.GetPoint();
	
			return true;
		}
	}
	return false;	
}


bool CMotionComponent::StickToGround()
{
	if ( ( !m_col_dist_above ) && ( !m_col_dist_below ) )
	{
		return false;
	}	

	if ( m_point_stick_to_ground )
	{
		// standard stick to ground
		// (also need a car version,
		// which will be a separate component)

		Mth::Vector v0, v1;

		// make sure our X vector is correct (got Z from the nav matrix...)
		GetObject()->m_matrix[ X ] = Mth::CrossProduct( GetObject()->m_matrix[ Y ], GetObject()->m_matrix[ Z ]);
		GetObject()->m_matrix[ X ].Normalize();

		v0.Set( 0.0f, FEET_TO_INCHES( fabs( m_col_dist_above ) ), 0.0f );
		v1.Set( 0.0f, -FEET_TO_INCHES( fabs( m_col_dist_below ) ), 0.0f );

		v0 += GetObject()->m_pos;
		v1 += GetObject()->m_pos;

		// get disatnce to ground
		// and snap the skater to it

		Mth::Vector collision_point;
		if (s_do_collision_check(&v0, &v1, &collision_point, &m_last_triangle))
		{
			GetObject()->m_pos=collision_point;
			return true;
		}
		return false;	
	}
	else
	{
		// if it's a car, then it's big
		Mth::Vector v0, v1, vectForward, vectLeft, leftCollidePoint, rightCollidePoint;
		bool leftCollision = false;
		bool rightCollision = false;
		CFeeler		feeler;

		GetObject()->m_matrix[ Y ].Set( 0.0f, 1.0f, 0.0f );
		// make sure our X vector is correct (got Y and Z from the nav matrix...)
		GetObject()->m_matrix[ X ] = Mth::CrossProduct( GetObject()->m_matrix[ Y ], GetObject()->m_matrix[ Z ]);
		GetObject()->m_matrix[ X ].Normalize();

		vectForward = GetObject()->m_matrix[ Z ] * FEET_TO_INCHES( 6.0f );
		vectLeft = GetObject()->m_matrix[ X ] * FEET_TO_INCHES( 2.5f );

		GetObject()->m_pos -= vectForward;
		GetObject()->m_pos += vectLeft;

		v0 = v1 = GetObject()->m_pos;
		v0[ Y ] += FEET_TO_INCHES( m_col_dist_above );
		v1[ Y ] -= FEET_TO_INCHES( m_col_dist_below );

		// find a collision point under the left rear:
		if (s_do_collision_check(&v0, &v1, &leftCollidePoint, &m_car_left_rear_last_triangle))
		{
			leftCollision = true;
		}
		
		GetObject()->m_pos -= vectLeft * 2;

		v0 = v1 = GetObject()->m_pos;
		v0[ Y ] += FEET_TO_INCHES( m_col_dist_above );
		v1[ Y ] -= FEET_TO_INCHES( m_col_dist_below );

		// find a collision point under the right rear:
		if (s_do_collision_check(&v0, &v1, &rightCollidePoint, &m_car_right_rear_last_triangle))
		{
			rightCollision = true;
			if ( leftCollision )
			{
				GetObject()->m_matrix[ X ] = leftCollidePoint - rightCollidePoint;
				GetObject()->m_matrix[ X ].Normalize( );
			}
		}
		
		GetObject()->m_pos += vectForward * 2;
		GetObject()->m_pos += vectLeft;

		v0 = v1 = GetObject()->m_pos;
		v0[ Y ] += FEET_TO_INCHES( m_col_dist_above );
		v1[ Y ] -= FEET_TO_INCHES( m_col_dist_below );

		GetObject()->m_pos -= vectForward;

		Mth::Vector mid_front_collide_point;
		if (s_do_collision_check(&v0, &v1, &mid_front_collide_point, &m_car_mid_front_last_triangle))
		{
			if ( rightCollision && leftCollision )
			{
				GetObject()->m_pos[ Y ] = leftCollidePoint[ Y ] + rightCollidePoint[ Y ];
				GetObject()->m_pos[ Y ] /= 2;
				GetObject()->m_pos[ Y ] += mid_front_collide_point.GetY();
				GetObject()->m_pos[ Y ] /= 2;

				Mth::Vector heading;

				heading = leftCollidePoint + rightCollidePoint;
				heading /= 2;
				heading -= mid_front_collide_point;
				heading *= -1;
				heading.Normalize( );

				GetObject()->m_matrix[ Z ] += heading;
				GetObject()->m_matrix[ Z ] /= 2;

				GetObject()->m_matrix[ Y ] = Mth::CrossProduct( GetObject()->m_matrix[ Z ], GetObject()->m_matrix[ X ] );
				GetObject()->m_matrix[ Y ].Normalize( );

				return true;
			}
		}

		// (Mick) This assertion has been removed, as cars sometimes drive where the skater cannot go, and hence into no collision		
		//	Dbg_MsgAssert( 0, ( "StickToGround car %s was driving on an area with no collision", Script::FindChecksumName(GetID()) ) );

		return false;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::GetDebugInfo( Script::CStruct* p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert( p_info, ( "NULL p_info sent to CMotionComponent::GetDebugInfo" ) );
	CBaseComponent::GetDebugInfo(p_info);
	
	p_info->AddChecksum( CRCD(0x9e6b250,"m_movingobj_status"), m_movingobj_status );
	p_info->AddChecksum( CRCD(0xe5dff85a,"m_movingobj_flags"), m_movingobj_flags );
	p_info->AddFloat( CRCD(0xc2d3b58d,"m_moveto_speed"), m_moveto_speed );
	p_info->AddFloat( CRCD(0x3a3ae81a,"m_acceleration"), m_acceleration );
	p_info->AddFloat( CRCD(0x4a2c5549,"m_deceleration"), m_deceleration );
	p_info->AddFloat( CRCD(0x472b8f9b,"m_max_vel"), m_max_vel );
	p_info->AddFloat( CRCD(0xc37e4e25,"m_vel_z"), m_vel_z );
	p_info->AddFloat( CRCD(0xcab44eaf,"m_stop_dist"), m_stop_dist );

	// p_info->AddFloat( "m_col_dist_above", m_col_dist_above );
	// p_info->AddFloat( "m_col_dist_below", m_col_dist_below );
	// p_info->AddVector( "m_last_triangle_v0", m_last_triangle_v0 );
	// p_info->AddVector( "m_last_triangle_v1", m_last_triangle_v1 );
	// p_info->AddVector( "m_last_triangle_v2", m_last_triangle_v2 );
	// p_info->AddVector( "m_last_normal", m_last_normal );
	p_info->AddInteger( CRCD(0xa13363b0,"m_point_stick_to_ground"), m_point_stick_to_ground );
	
	p_info->AddFloat( CRCD(0xac5b4824,"m_moveto_dist"), m_moveto_dist );
	p_info->AddFloat( CRCD(0x3e3570a6,"m_moveto_dist_traveled"), m_moveto_dist_traveled );
	p_info->AddFloat( CRCD(0x93e73df1,"m_moveto_speed_target"), m_moveto_speed_target );
	p_info->AddFloat( CRCD(0x6819fd8d,"m_moveto_acceleration"), m_moveto_acceleration );	
	
	p_info->AddInteger( CRCD(0xd6d94326,"m_moveto_node_num"), m_moveto_node_num );
	
	// p_info->AddVector( "m_turn_ang_target", m_turn_ang_target );
	// p_info->AddVector( "m_cur_turn_ang", m_cur_turn_ang );
	// p_info->AddVector( "m_turn_speed_target", m_turn_speed_target );
	p_info->AddVector( CRCD(0x9d27cc70,"m_turn_speed"), m_turn_speed );
	// p_info->AddVector( "m_delta_turn_speed", m_delta_turn_speed );
	// p_info->AddVector( "m_angular_friction", m_angular_friction );
	p_info->AddVector( CRCD(0xfa4307e,"m_orig_pos"), m_orig_pos );
	
	// p_info->AddFloat( "m_stop_dist_traveled", m_stop_dist_traveled );
	// p_info->AddFloat( "m_vel_z_start", m_vel_z_start );
	// p_info->AddFloat( "m_min_stop_vel", m_min_stop_vel );
	
	p_info->AddVector( CRCD(0xc3e1cded,"m_stored_pos"), m_stored_pos );
	p_info->AddInteger( CRCD(0x56e54ee5,"m_stored_node"), m_stored_node );
	
	// CPathObjectTracker*		mp_path_object_tracker;
	
	// Mth::SlerpInterpolator*	mp_slerp;
	// p_info->AddFloat( "m_rot_time_target", m_rot_time_target );
	p_info->AddFloat( CRCD(0x622c6c9b,"m_rot_time"), m_rot_time );
	
	p_info->AddInteger( CRCD(0x80dd9065,"m_start_node"), m_start_node );
	p_info->AddInteger( CRCD(0xa80dc42,"m_current_node"), m_current_node );
	
	if (mp_pathOb)
	{
		Script::CStruct* p_pathOb_params = new Script::CStruct();
		mp_pathOb->GetDebugInfo( p_pathOb_params	);
		p_info->AddStructure( CRCD(0x81270c1f,"mp_pathOb"), p_pathOb_params );
		delete p_pathOb_params;
	}	
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCompositeObject* CMotionComponent::GetNextObjectOnPath( float range )
{
	CCompositeObject* p_closest_ob=NULL;

	if ( mp_path_object_tracker )
	{		
		const CSmtPtr* pp_object_list = mp_path_object_tracker->GetObjectList();

		float min_dist = 0.0f;
		for (int i=0; im_matrix[Z],p_ob->GetMatrix()[Z]) >= 0.0f &&
					Mth::DotProduct(GetObject()->m_matrix[Z],p_ob->GetPos()-GetObject()->GetPos()) >= 0.0f
					)
				{
					if (GetMotionComponentFromObject(p_ob)->GetDestinationPathNode()==GetDestinationPathNode() &&
						GetMotionComponentFromObject(p_ob)->GetPreviousPathNode()!=GetPreviousPathNode())
					{
						// Make vehicles on converging paths not collide, otherwise
						// a pile up happens at the start of the car paths in the school
						// Note: Actually, this didn't fix it ... might be a script bug?
					}
					else
					{
						float d=Mth::Distance( GetObject()->GetPos(), p_ob->GetPos());
						if( d <= range )
						{
							if( !p_closest_ob || d < min_dist )
							{
								min_dist = d;
								p_closest_ob = p_ob;
							}
						}
					}	
				}
			}
		}
	}
	return p_closest_ob;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void CMotionComponent::FollowLeader( void )
{   
	Dbg_MsgAssert( mp_follow_ob,( "Shouldn't be in follow-leader state without mp_follow_ob." ));
	
	mp_follow_ob->GetNewPathPointFromObjectBeingFollowed();
	
	float old_y = GetObject()->m_pos[Y];
	mp_follow_ob->CalculatePositionAndOrientation(&GetObject()->m_pos,&GetObject()->m_matrix);
	if (m_leave_y_unaffected_when_following_leader)
	{
		GetObject()->m_pos[Y]=old_y;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::FollowLeader_Init( Script::CStruct* pParams )
{
	if (pParams->ContainsFlag(CRCD(0xd443a2bc,"Off")))
	{
		m_movingobj_status &= ~MOVINGOBJ_STATUS_FOLLOWING_LEADER;
		if (mp_follow_ob)
		{
			delete mp_follow_ob;
			mp_follow_ob=NULL;
		}
		return;
	}		
		
	m_leave_y_unaffected_when_following_leader=pParams->ContainsFlag("LeaveYUnaffected");
	
	m_movingobj_status |= MOVINGOBJ_STATUS_FOLLOWING_LEADER;
	
	// these can't coexist:
	m_movingobj_status &= ~MOVINGOBJ_STATUS_MOVETO;

	//	GetMotionComponent()->m_movingobj_status &= ~MOVINGOBJ_STATUS_LOCKED_TO_OBJECT;
	Obj::CLockObjComponent* pLockObjComponent = GetLockObjComponentFromObject(GetObject());
	pLockObjComponent->EnableLock( false );

	if ( !mp_follow_ob )
	{
		mp_follow_ob = new CFollowOb;
	}	
	mp_follow_ob->Reset();
	
	float distance=0.0f;
	pParams->GetFloat(CRCD(0xe36d657e,"Distance"),&distance);
	mp_follow_ob->SetDistance(distance);
	
	uint32 name=0;
	pParams->GetChecksum(CRCD(0xa1dc81f9,"Name"),&name);
	mp_follow_ob->SetLeaderName(name);
	
	if (pParams->ContainsFlag(CRCD(0xe19e310a,"OrientY")))
	{
		mp_follow_ob->OrientY();
	}	
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMotionComponent::DoJump( void )
{
	Mth::Vector new_pos;

	// Gfx::AddDebugLine( m_jump_start_pos, m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 1 );
	float distance = m_jump_speed * m_time + ( m_jump_gravity * 0.5f * m_time * m_time );
	// printf("m_jump_speed = %f, m_jump_gravity = %f\n, m_jump_original_speed = %f, m_jump_heading = (%f, %f, %f)\n", m_jump_speed, m_jump_gravity, m_jump_original_speed, m_jump_heading[X], m_jump_heading[Y], m_jump_heading[Z]);
	new_pos = GetObject()->m_pos;
	if ( m_jump_use_heading )
	{		
		// first figure the pos if we stayed on our original heading
		new_pos += ( m_jump_heading * m_jump_original_speed * m_time );
		new_pos[Y] = GetObject()->m_pos[Y];
	}
	new_pos[Y] += distance;

	m_jump_speed += m_jump_gravity * m_time;

	// do land checks if we're on our way down
	if ( distance < 0.0f )
	{
		if ( m_jump_use_land_height )
		{
			if ( new_pos[Y] < m_jump_land_height )
			{
				m_movingobj_status &= ~MOVINGOBJ_STATUS_JUMPING;
				new_pos[Y] = m_jump_land_height;
			}
		}
		else
		{
			// use collision checks
			CFeeler feeler;
			feeler.m_start = new_pos + Mth::Vector( 0, m_jump_col_dist_above, 0 );
			feeler.m_end = new_pos + Mth::Vector( 0, -m_jump_col_dist_below, 0 );
			if ( feeler.GetCollision() )
			{
				m_movingobj_status &= ~MOVINGOBJ_STATUS_JUMPING;
				if ( m_jump_start_pos[X] != GetObject()->m_pos[X] || m_jump_start_pos[Z] != GetObject()->m_pos[Z] )
				{
					// printf("StickToGround %s\n",Script::FindChecksumName(GetID()));
					// GetMotionComponent()->StickToGround();
					// m_jump_start_pos = m_pos;
				
					/*if ( new_pos[Y] < m_jump_start_pos[Y])
					{
						new_pos[Y] = m_jump_start_pos[Y];
						GetMotionComponent()->m_movingobj_status &= ~MOVINGOBJ_STATUS_JUMPING;
					}*/
				}
				else
				{
					// new_pos[Y] = m_jump_start_pos[Y];
					// GetMotionComponent()->m_movingobj_status &= ~MOVINGOBJ_STATUS_JUMPING;
				}
			}
		}
	}
	m_jump_pos = new_pos;
}



	
}


================================================
FILE: Code/Gel/Components/motioncomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       MotionComponent.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/5/2002
//****************************************************************************

#ifndef __COMPONENTS_MOTIONCOMPONENT_H__
#define __COMPONENTS_MOTIONCOMPONENT_H__

#include 
#include 
#include 

#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_MOTION CRCD(0xa015e17,"Motion")
#define		GetMotionComponent() ((Obj::CMotionComponent*)GetComponent(CRC_MOTION))
#define		GetMotionComponentFromObject(pObj) ((Obj::CMotionComponent*)(pObj)->GetComponent(CRC_MOTION))

namespace Mth
{
	class SlerpInterpolator;
}

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CPathOb;
	class CPathObjectTracker;
    class CFollowOb;


	enum
	{
		// not used
		// MOVINGOBJ_STATUS_IDLE 				= 0,

		MOVINGOBJ_STATUS_ROTX 				= ( 1 << 0  ),
		MOVINGOBJ_STATUS_ROTY 				= ( 1 << 1  ),
		MOVINGOBJ_STATUS_ROTZ 				= ( 1 << 2  ),
		MOVINGOBJ_STATUS_MOVETO 			= ( 1 << 3  ),
		MOVINGOBJ_STATUS_ON_PATH 			= ( 1 << 4  ),
		MOVINGOBJ_STATUS_QUAT_ROT 			= ( 1 << 5  ),
		MOVINGOBJ_STATUS_LOOKAT 			= ( 1 << 6  ), // will be on in addition to rotx, y or z flags.
		MOVINGOBJ_STATUS_HOVERING 			= ( 1 << 7  ),
		MOVINGOBJ_STATUS_JUMPING  			= ( 1 << 8  ),
		MOVINGOBJ_STATUS_FOLLOWING_LEADER	= ( 1 << 9  ),

		// moved into lock obj component
		// MOVINGOBJ_STATUS_LOCKED_TO_OBJECT	= ( 1 << 10 ),

		// don't do any other processing, until this flag is off:
		// wasn't being used...
		// MOVINGOBJ_STATUS_HIGH_LEVEL 		= ( 1 << 31 ), // running C-code on a high level.
	};

	#define MOVINGOBJ_STATUS_ROTXYZ ( MOVINGOBJ_STATUS_ROTX | MOVINGOBJ_STATUS_ROTY | MOVINGOBJ_STATUS_ROTZ )
	
	// movement flags (to be used with m_movingobj_flags)
	#define MOVINGOBJ_FLAG_STICK_TO_GROUND		( 1 << 2 )
	#define MOVINGOBJ_FLAG_STOP_ALONG_PATH		( 1 << 3 )
	#define MOVINGOBJ_FLAG_INDEPENDENT_HEADING	( 1 << 4 )
	#define MOVINGOBJ_FLAG_CONST_ROTX			( 1 << 5 )
	#define MOVINGOBJ_FLAG_CONST_ROTY			( 1 << 6 )
	#define MOVINGOBJ_FLAG_CONST_ROTZ			( 1 << 7 )
	#define MOVINGOBJ_FLAG_CONSTANT_HEIGHT		( 1 << 8 )
	#define MOVINGOBJ_FLAG_RANDOM_PATH_MODE		( 1 << 9 )
	#define MOVINGOBJ_FLAG_NO_PITCH_PLEASE		( 1 << 10 )

// Used to store the coords of the last triangle collided with,
// so that it can be checked first next time as a collision speed optimization.
struct STriangle
{
	Mth::Vector mpVertices[3];
	void	Init() {mpVertices[0].Set();mpVertices[1].Set();mpVertices[2].Set();}
};

class CMotionComponent : public CBaseComponent
{
public:
    CMotionComponent();
    virtual ~CMotionComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
	virtual void					ProcessWait( Script::CScript * pScript );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	
	static CBaseComponent*			s_create();

	virtual void					GetDebugInfo( Script::CStruct* p_info );
	
public:
	CPathOb*				GetPathOb();
	void					Reset();
	bool					IsMoving( void );
	bool 					IsRotating( void );

	bool					IsHovering();
	void					GetHoverOrgPos( Mth::Vector *p_orgPos );

	// Added by Ken. If the object is following a path, this will return the node
	// index of the node it is heading towards. Returns -1 otherwise.
public:
	void					DoPathPhysics();
	int						GetDestinationPathNode();
	int						GetPreviousPathNode();
	void					EnsurePathobExists( CCompositeObject* pCompositeObj );
	CCompositeObject*   	GetNextObjectOnPath(float range);
	void 					OrientToNode( Script::CStruct* pNodeData );
	
	void					InitPath( int nodeNumber );
	void					HitWaypoint( int nodeIndex );
	bool					PickRandomWaypoint();

	bool					LookAt_Init( Script::CStruct* pParams, const Mth::Vector& lookAtPos );
	void					LookAtNodeLinked_Init( Script::CStruct* pParams, Script::CScript* pScript );
	void					LookAtNode_Init( Script::CStruct* pParams, Script::CScript* pScript );
	void					LookAtPos_Init( Script::CStruct* pParams, bool absolute );
//	bool					LookAtObject_Init( Script::CStruct* pParams, Script::CScript* pScript );
	bool					Move_Init( Script::CStruct* pParams, Script::CScript* pScript, Mth::Vector pos, int nodeNum = -1 );
	void					MoveToPos_Init( Script::CStruct* pParams, Script::CScript* pScript, bool absolute );
	void					MoveToNode_Init( Script::CStruct* pParams, Script::CScript* pScript );
	void					MoveToLink_Init( Script::CStruct* pParams, Script::CScript* pScript );
	void					FollowPath_Init( Script::CStruct* pParams );
	void					FollowPathLinked_Init( Script::CStruct* pParams, Script::CScript* pScript );
	void					FollowStoredPath_Init( Script::CStruct* pParams );
	void					FollowLeader_Init( Script::CStruct* pParams );
	void					QuatRot_Init( Script::CStruct* pParams );
	void					RotAxis_Init( Script::CStruct* pParams, Script::CScript* pScript, int whichAxis );

	// used for looking at a node or position...
	bool					SetUpLookAtPos( const Mth::Vector& lookToPos, const Mth::Vector& currentPos, int headingAxis, int rotAxis, float threshold=0.0f );
	void					QuatRot_Setup( Script::CStruct* pParams, const Mth::Matrix& rot );
	void					SetCorrectRotationDirection( int rotAxis );
	void					RotateToFacePos( const Mth::Vector pos );
	
	void					Rot( int rotAxis, float deltaRot );
	bool					Rotate( int rotAxis );
	void					RotUpdate( void );
	bool					Move( void );
	void					FollowPath( void );
	void					QuatRot( void );
	bool					StickToGround();
	void					UndoHover( void );
	void					ApplyHover( void );
public:
	// GJ:  Had to make this public during the transition,
	// because the moving objects need to access it
	// eventually, everything will be manipulated through
	// accessor functions				
    int						m_movingobj_status;
	int						m_movingobj_flags;
	
	float					m_moveto_speed;
	float					m_acceleration;
	float					m_deceleration;
	float					m_max_vel;
	float					m_vel_z;
	float					m_stop_dist;

	// when sticking to the ground, collision line length:
	float					m_col_dist_above;
	float					m_col_dist_below;
	STriangle				m_last_triangle;
	bool					m_point_stick_to_ground;
	
	STriangle				m_car_left_rear_last_triangle;
	STriangle				m_car_right_rear_last_triangle;
	STriangle				m_car_mid_front_last_triangle;

	// A CPathObjectTracker keeps track of all the objects that are following a
	// particular path. Stored so that the object can un-register with the tracker
	// when it starts following a new path.
	CPathObjectTracker*		mp_path_object_tracker;
protected:
	Mth::Vector				m_moveto_pos;
	float					m_moveto_dist;
	float					m_moveto_dist_traveled;
//	float					m_moveto_speed;			// TEMPORARILY PUBLIC
	float					m_moveto_speed_target;
	float					m_moveto_acceleration;
	
	// so that we can call SpawnObjScript or SwitchObjScript when we arrive...
	int						m_moveto_node_num;

	Mth::Vector				m_turn_ang_target;
	Mth::Vector				m_cur_turn_ang;
	Mth::Vector				m_turn_speed_target;
	Mth::Vector				m_turn_speed;
	Mth::Vector				m_delta_turn_speed;
	Mth::Vector				m_angular_friction;
	Mth::Vector				m_orig_pos;

	// path traversal:
//	float					m_acceleration;			// TEMPORARILY PUBLIC
//	float					m_deceleration;			// TEMPORARILY PUBLIC
//	float					m_max_vel;				// TEMPORARILY PUBLIC
//	float					m_vel_z;				// TEMPORARILY PUBLIC
//	float					m_stop_dist;        	// TEMPORARILY PUBLIC
	float					m_stop_dist_traveled;
	float					m_vel_z_start;
	float					m_min_stop_vel;
	
	// gotta remember where to return to when going to a higher level:
	Mth::Vector				m_stored_pos;
	int						m_stored_node;

	float					m_rot_time_target;
	float					m_rot_time;
	Mth::SlerpInterpolator*	mp_slerp;

	float					m_y_before_applying_hover;
	float					m_hover_offset;
	float					m_hover_amp;
	int						m_hover_period;

	float					m_time;

protected:
	// moved this down from CMovingObject
	// (information about the node that created this object)
	int						m_start_node; 	// renamed, as we want to use it for path objs, but not for node identification
	int						m_current_node;  // this may change if the object is moving... (needs to be at CMovingObject level for querying)

protected:
	CPathOb*				mp_pathOb;
	
// Mo stuff from MovingObject.h		
		
		void					FollowLeader( void );
		
		// Pointer to the follow ob, which contains all the info required to follow
		// an object on a path. Done this way so as to not use up memory for every instance
		// of a CMovingObject. (similar to CPathOb)
		CFollowOb*				mp_follow_ob;

		// K: I think I should have put this in CFollowOb too?
		bool					m_leave_y_unaffected_when_following_leader;

private:		
		
		void					DoJump( void );
		float					m_jump_speed;
		float					m_jump_gravity;
		Mth::Vector				m_jump_heading;
		bool					m_jump_use_heading;
		Mth::Vector				m_jump_pos;
		float					m_jump_original_speed;
		float					m_jump_col_dist_above;
		float					m_jump_col_dist_below;
		float					m_jump_y;
		bool					m_jump_use_land_height;
		float					m_jump_land_height;
		// The whole start pos is stored rather than just the y so that it knows whether 
		// it needs to do another collision check once the jump has got back to the start y.
		// If the x or z has changed then it needs to do another collision check.
		Mth::Vector	 		  	m_jump_start_pos;

	
	
};

}

#endif


================================================
FILE: Code/Gel/Components/rigidbodycomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       RigidBodyComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  1/22/3
//****************************************************************************

// An object component which dictates the motion of an object using an approximation of rigidbody physics.

#include 
#include 

#include 

#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{

Nx::CCollCache CRigidBodyComponent::s_collision_cache;
bool CRigidBodyComponent::s_debug_lines_on = false;
bool CRigidBodyComponent::s_draw_skater_collision_circles = false;
float CRigidBodyComponent::s_skater_head_height;
Tmr::Time CRigidBodyComponent::s_collide_sound_allowed_time = 0;
CRigidBodyComponent::SContactState CRigidBodyComponent::sp_contact_states [ CRigidBodyComponent::vRP_MAX_NUM_CONTACTS ];

#ifdef __NOPT_DEBUG__
bool CRigidBodyComponent::s_contact_states_in_use = false;
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CRigidBodyComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CRigidBodyComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CRigidBodyComponent::CRigidBodyComponent() : CBaseComponent()
{
	SetType( CRC_RIGIDBODY );

	mp_sound_component = NULL;
	
	// setup defaults
	
	m_const_acc = vRP_GRAVITATIONAL_ACCELERATION;
	m_mass_over_moment = vRP_DEFAULT_MASS_OVER_MOMENT;
	m_coeff_restitution = vRP_DEFAULT_COEFF_RESTITUTION;
	m_coeff_friction = vRP_DEFAULT_COEFF_FRICTION;
	m_spring_const = vRP_DEFAULT_SPRING_CONST;
	m_linear_velocity_sleep_point_sqr = vRP_DEFAULT_LINEAR_VELOCITY_SLEEP_POINT * vRP_DEFAULT_LINEAR_VELOCITY_SLEEP_POINT;
	m_angular_velocity_sleep_point_sqr = vRP_DEFAULT_ANGULAR_VELOCITY_SLEEP_POINT * vRP_DEFAULT_ANGULAR_VELOCITY_SLEEP_POINT;
	m_skater_collision_impulse_factor = vRP_DEFAULT_SKATER_COLLISION_IMPULSE_FACTOR;
	m_skater_collision_rotation_factor = vRP_DEFAULT_SKATER_COLLISION_ROTATION_FACTOR;
	m_skater_collision_radius = vRP_DEFAULT_SKATER_COLLISION_RADIUS;
	m_skater_collision_application_radius = vRP_DEFAULT_SKATER_COLLISION_APPLICATION_RADIUS;
	m_cos_skater_collision_assent = cosf(DEGREES_TO_RADIANS(vRP_DEFAULT_SKATER_COLLISION_ASSENT));
	m_sin_skater_collision_assent = sinf(DEGREES_TO_RADIANS(vRP_DEFAULT_SKATER_COLLISION_ASSENT));
	m_ignore_skater_duration = vRP_DEFAULT_IGNORE_SKATER_DURATION;
	m_model_offset.Set(0.0f, 0.0f, 0.0f);
	
	m_num_contacts = 0;
	
	m_script_names.collide = m_script_names.bounce = m_script_names.settle = m_script_names.stuck = 0;
	mp_script_params = NULL;
	
	m_sound_setup.collide_sound = m_sound_setup.bounce_sound = NULL;
	m_sound_setup.collide_mute_delay = vRP_DEFAULT_COLLIDE_MUTE_DELAY;
	m_sound_setup.global_collide_mute_delay = vRP_DEFAULT_GLOBAL_COLLIDE_MUTE_DELAY;
	m_sound_setup.bounce_velocity_callback_threshold = vRP_DEFAULT_BOUNCE_VELOCITY_CALLBACK_THRESHOLD;
	m_sound_setup.bounce_velocity_full_speed = vRP_DEFAULT_BOUNCE_VELOCITY_FULL_SPEED;
	
	m_collide_sound_allowed_time = 0;
	
	m_die_countdown = -1.0f;
	
	mp_contacts = NULL;
	
	#ifdef __NOPT_ASSERT__
	m_sound_type_id = 0;
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CRigidBodyComponent::~CRigidBodyComponent()
{
	delete [] mp_contacts;
	delete mp_script_params;
	delete m_sound_setup.bounce_sound;
	delete m_sound_setup.collide_sound;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::InitFromStructure( Script::CStruct* pParams )
{
	// NOTE: right now rigidbodies ignore moving collidables; if we want to change this, we should account for the moving collidables' velocity
	// in the collision resolution code
	// NOTE: probably want to add some small random rotation when the skater hits the object; two reasons; one, when you hit a line of cones, right
	// now it seems too orderly; two, when you push a cone ahead of you, the cone's dynamics should be more jerky and less smooth
	
	// NOTE: don't make any contacts within a foot of the center of mass or the object is in danger of falling through the ground

	// IDEA: for a skateboard rigidbody, have four of the contacts only feel friction parallel to the wheel's axis

	// potential optimization; collision detect only every other frame, unless the last-ditch feeler says to do it this frame

	m_state = ASLEEP;

	m_vel.Set(0.0f, 0.0f, 0.0f);
	m_rotvel.Set(0.0f, 0.0f, 0.0f);
	
	m_matrix.Ident();
	
	m_pos = GetObject()->GetPos();
	
	m_orientation = GetObject()->GetMatrix();
	m_orientation.Normalize();
	m_orientation.GetMatrix(m_matrix);
	
	pParams->GetVector(CRCD(0xc4c809e, "vel"), &m_vel);
	
	pParams->GetVector(CRCD(0xfb1a83b2, "rotvel"), &m_rotvel);

	pParams->GetVector(CRCD(0x737612c6, "center_of_mass"), &m_center_of_mass);

	pParams->GetFloat(CRCD(0x6fae5c9a, "const_acc"), &m_const_acc);

	pParams->GetFloat(CRCD(0x60b4860d, "coeff_restitution"), &m_coeff_restitution);

	pParams->GetFloat(CRCD(0xf9d75930, "coeff_friction"), &m_coeff_friction);
	
	pParams->GetFloat(CRCD(0xf1ecb30f, "spring_const"), &m_spring_const);

	if (pParams->ContainsComponentNamed(CRCD(0xc11e4dd8, "linear_velocity_sleep_point")))
	{
		pParams->GetFloat(CRCD(0xc11e4dd8, "linear_velocity_sleep_point"), &m_linear_velocity_sleep_point_sqr);
		m_linear_velocity_sleep_point_sqr *= m_linear_velocity_sleep_point_sqr;
	}

	if (pParams->ContainsComponentNamed(CRCD(0xa457ece3, "angular_velocity_sleep_point")))
	{
		pParams->GetFloat(CRCD(0xa457ece3, "angular_velocity_sleep_point"), &m_angular_velocity_sleep_point_sqr);
		m_angular_velocity_sleep_point_sqr *= m_angular_velocity_sleep_point_sqr;
	}

	pParams->GetFloat(CRCD(0x3390d1e5, "skater_collision_impulse_factor"), &m_skater_collision_impulse_factor);
	
	pParams->GetFloat(CRCD(0x5a18f61f, "skater_collision_rotation_factor"), &m_skater_collision_rotation_factor);

	pParams->GetFloat(CRCD(0x85f112c1, "skater_collision_radius"), &m_skater_collision_radius);

	pParams->GetFloat(CRCD(0x8ab2db68, "skater_collision_application_radius"), &m_skater_collision_application_radius);

	if (pParams->ContainsComponentNamed(CRCD(0xe9156e6d, "skater_collision_assent")))
	{
		float skater_collision_assent;
		pParams->GetFloat(CRCD(0xe9156e6d, "skater_collision_assent"), &skater_collision_assent);
		m_cos_skater_collision_assent = cosf(DEGREES_TO_RADIANS(skater_collision_assent));
		m_sin_skater_collision_assent = sinf(DEGREES_TO_RADIANS(skater_collision_assent));
	}

	pParams->GetFloat(CRCD(0x1f4d0ca5, "ignore_skater_duration"), &m_ignore_skater_duration);
	
	pParams->GetChecksum(CRCD(0x58cdc0f0, "CollideScript"), &m_script_names.collide);
	pParams->GetChecksum(CRCD(0x2d075f90, "BounceScript"), &m_script_names.bounce);
	pParams->GetChecksum(CRCD(0x571917fa, "SettleScript"), &m_script_names.settle);
	pParams->GetChecksum(CRCD(0xccb84047, "StuckScript"), &m_script_names.stuck);
	
	if (pParams->ContainsComponentNamed(CRCD(0xdd85bda, "CallbackParams")))
	{
		Script::CStruct* p_struct;
		pParams->GetStructure(CRCD(0xdd85bda, "CallbackParams"), &p_struct, Script::ASSERT);
		
		mp_script_params = new Script::CStruct;
		*mp_script_params += *p_struct;
	}
	
	// get sound setup
	if (pParams->ContainsComponentNamed(CRCD(0x94956e48, "SoundType")))
	{
		uint32 sound_type_id;
		pParams->GetChecksum(CRCD(0x94956e48, "SoundType"), &sound_type_id, Script::ASSERT);
		
		get_sound_setup(sound_type_id);
		
		#ifdef __NOPT_ASSERT__
		m_sound_type_id = sound_type_id;
		#endif
	}
	
	m_flags.Set(DIE_UPON_SLEEP, pParams->ContainsFlag(CRCD(0x5e51924f, "DieUponSettling")));

	// there are currently two ways to setup the contact points; either you pass them in as an array of offsets from the center of mass
	// or you specify a generic geometry and pass parameters to that geometry
	
	EObjectGeometryType object_geometry = NO_GEOMETRY;
	if (m_num_contacts == 0)
	{
		object_geometry = BOX;
	}
	
	if (pParams->ContainsFlag(CRCD(0xf756b7c5, "box")))
	{
		object_geometry = BOX;
	}
	else if (pParams->ContainsFlag(CRCD(0x1cb1a2b6, "pyramid")))
	{
		object_geometry	= PYRAMID;
	}
	else if (pParams->ContainsFlag(CRCD(0x64fba415, "cylinder")))
	{
		object_geometry	= CYLINDER;
	}
	else if (pParams->ContainsFlag(CRCD(0x20689278, "triangle")))
	{
		object_geometry	= TRIANGLE;
	}

	// setup contacts using an array
	if (pParams->ContainsComponentNamed(CRCD(0xccbfea8c, "contacts")))
	{
		Script::CArray* p_array;
		pParams->GetArray(CRCD(0xccbfea8c, "contacts"), &p_array);
		setup_contacts_from_array(p_array);
		object_geometry = NO_GEOMETRY;
	}
	
	// setup contacts using a generic geometry
	switch (object_geometry)
	{
		// a rectangular prism with contacts at each corner
		case BOX:
		{
			Mth::Vector top_half_dimensions(32.0f, 32.0f, 16.0f);
			Mth::Vector bottom_half_dimensions;

			pParams->GetVector("dimensions", &top_half_dimensions);
			bottom_half_dimensions = top_half_dimensions;
			pParams->GetVector("top_dimensions", &top_half_dimensions);
			pParams->GetVector("bottom_dimensions", &bottom_half_dimensions);
			top_half_dimensions /= 2.0f;
			bottom_half_dimensions /= 2.0f;

			setup_contacts_as_box(top_half_dimensions, bottom_half_dimensions);
			
			break;
		}

		// a pyramid with contacts at each corner along the bottom and one on the top
		case PYRAMID:
		{
			float half_height = 42.0f;
			float half_depth = 32.0f;
			
			pParams->GetFloat(CRCD(0xab21af0,"height"), &half_height);
			pParams->GetFloat(CRCD(0x55ce396,"depth"), &half_depth);
			half_height /= 2.0f;
			half_depth /= 2.0f;
			
			setup_contacts_as_pyramid(half_height, half_depth);
			
			break;
		}

		// a cylinder with six contacts around the top and bottom
		case CYLINDER:
		{
			float top_radius = 12.0f;
			float bottom_radius;
			float half_height = 60.0f;
			int edges = 6;
			
			pParams->GetFloat(CRCD(0xc48391a5,"radius"), &top_radius);
			bottom_radius = top_radius;
			
			pParams->GetFloat(CRCD(0x937e3b29,"top_radius"), &top_radius);
			pParams->GetFloat(CRCD(0xe2ac8c3a,"bottom_radius"), &bottom_radius);
			pParams->GetFloat(CRCD(0xab21af0,"height"), &half_height);
			pParams->GetInteger(CRCD(0x4055f24a,"edges"), &edges);
			half_height /= 2.0f;
			
			setup_contacts_as_cylinder(top_radius, bottom_radius, half_height, edges);
			
			break;
		}

		// a triangle with three contacts on the left and right
		case TRIANGLE:
		{
			Mth::Vector half_dimensions(32.0f, 32.0f, 16.0f);
			
			pParams->GetVector(CRCD(0x1d82745a, "dimensions"), &half_dimensions);
			half_dimensions /= 2.0f;
			
			setup_contacts_as_triangle(half_dimensions);
	
			break;
		}
		
		// don't use a generic geometry
		case NO_GEOMETRY:
			Dbg_MsgAssert(m_num_contacts, ("Contact geometry not setup for rigidbody"));
			break;
	}
	
	// setup directional friction
	if (pParams->ContainsComponentNamed(CRCD(0xc35cac0d, "directed_friction")))
	{
		Script::CArray* p_array;
		pParams->GetArray(CRCD(0xc35cac0d, "directed_friction"), &p_array);
		
		Dbg_MsgAssert(p_array->GetSize() == m_num_contacts, ("Array of directed friction specifications must equal the number of contacts"));
		
		for (int n = p_array->GetSize(); n--; )
		{
			Script::CStruct* m_struct = p_array->GetStructure(n);
			if (m_struct->ContainsComponentNamed(CRCD(0x806fff30, "none")))
			{
				mp_contacts[n].directed_friction = false;
			}
			else
			{
				mp_contacts[n].directed_friction = true;
				m_struct->GetVector(NO_NAME, &mp_contacts[n].friction_direction, Script::ASSERT);
			}
		}
	}
			 	
	// adjust contacts based on extra angles
	if (pParams->ContainsComponentNamed(CRCD(0x17b93077, "rotate_contacts")))
	{
		Mth::Vector angles;
		pParams->GetVector(CRCD(0x17b93077, "rotate_contacts"), &angles, Script::ASSERT);
		Mth::Matrix matrix;
		matrix.SetFromAngles(angles);
		
		for (int n = m_num_contacts; n--; )
		{
			mp_contacts[n].p = matrix.Rotate(mp_contacts[n].p);
		}
				 }

	// adjust contacts based on node array angles
	if (pParams->ContainsComponentNamed(CRCD(0x9d2d0915, "Angles")))
	{
		Mth::Vector angles;
		SkateScript::GetAngles(pParams, &angles);
		Mth::Matrix matrix;
		matrix.SetFromAngles(angles);
		
		for (int n = m_num_contacts; n--; )
		{
			mp_contacts[n].p = matrix.Rotate(mp_contacts[n].p);
		}
	}
	
	// setup model offset
	Mth::Vector center_of_mass(0.0f, 0.0f, 0.0f);
	if (pParams->ContainsComponentNamed(CRCD(0x737612c6, "center_of_mass")))
	{
		if (pParams->ContainsComponentNamed(CRCD(0x737612c6, "center_of_mass")))
		{
			pParams->GetVector(CRCD(0x737612c6, "center_of_mass"), ¢er_of_mass);
		}
	}
		
	// if not set (or set to default), calculate the center of mass
	if (center_of_mass[X] == 0.0f && center_of_mass[Y] == 0.0f && center_of_mass[Z] == 0.0f);
	{
		for (int n = m_num_contacts; n--; )
		{
			center_of_mass += mp_contacts[n].p;
		}
		center_of_mass *= (1.0f / m_num_contacts);
	}
	
	m_model_offset = -center_of_mass;
	for (int n = m_num_contacts; n--; )
	{
		mp_contacts[n].p -= center_of_mass;
	}
	
	
	if (!pParams->GetFloat(CRCD(0x967cd3ae, "mass_over_moment"), &m_mass_over_moment))
	{
		// calculate an appropriate mass over moment adjustment; otherwise, you can get some really weird behavior
		float max_dist = 0.0f;
		for (int n = m_num_contacts; n--; )
		{
			max_dist = Mth::Max(max_dist, mp_contacts[n].p.Length());
		}
		if (max_dist > 30.0f)
		{
			m_mass_over_moment /= Mth::Sqr((max_dist / 30.0f));
		}
	}
	
	// setup static variables
	s_skater_head_height = GetPhysicsFloat(CRCD(0x542cf0c7, "Skater_default_head_height"));

	// determine the largest feeler extent; used with the collision cache system
	m_largest_contact_extent = 0.0f;
	for (int n = m_num_contacts; n--; )
	{
		SContact& contact = mp_contacts[n];

		float contact_extent_sqr = contact.p.LengthSqr();
		if (contact_extent_sqr > m_largest_contact_extent)
		{
			m_largest_contact_extent = contact_extent_sqr;
		}
	}
	m_largest_contact_extent = sqrtf(m_largest_contact_extent) + 1.0f;

	m_ignore_skater_countdown = 0.0f;
	
	m_pos -= m_matrix.Rotate(m_model_offset);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::Finalize()
{
	mp_sound_component = GetSoundComponentFromObject(GetObject());
	
	Dbg_Assert(mp_sound_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::Update()
{
	#ifdef __NOPT_DEBUG__
	Dbg_MsgAssert(!s_contact_states_in_use, ("Two CRigidBodyComponents updating simultaneously"));
	s_contact_states_in_use = true;
	#endif
	
	float full_time_step = Tmr::FrameLength();
	
	setup_contact_states();
	
	// only check for skater collisions if we haven't collided with the skater recently; this insures that the stupid skater doesn't cram us
	// into corners and the like
	if (m_ignore_skater_countdown > 0.0f)
	{
		m_ignore_skater_countdown -= full_time_step;
	}
	else if (!m_flags.Test(PLAYER_COLLISION_DISABLED))
	{
		handle_skater_collisions();
	}
	
	if (m_die_countdown >= 0.0f)
	{
		if (m_die_countdown <= full_time_step)
		{
			GetObject()->MarkAsDead();
		}
		else
		{
			m_die_countdown -= full_time_step;
		}
	}
	
	// if we are asleep
	if (m_state == ASLEEP)
	{
		GetObject()->SetDisplayMatrix(m_matrix);

		if (s_debug_lines_on)
		{
			draw_debug_lines();
		}

		// do just about nothing
			
		#ifdef __NOPT_DEBUG__
		s_contact_states_in_use = false;
		#endif

		return;
	}

	int main_update_loop_count = 0;
	bool last_ditch_collision = false;
	float remaining_time_step = full_time_step;
	do {
		float time_step = remaining_time_step;

		// send a feeler from object's center of mass to our new center of mass; most collisions will not be caught in this manner;
		// this is a last-ditch feeler to insure that we don't fly through objects at high speeds
		CFeeler feeler;
		feeler.m_start = m_pos;
		feeler.m_end = m_pos + time_step * m_vel;
		feeler.m_end[Y] += 0.5f * time_step * time_step * m_const_acc;

		last_ditch_collision = feeler.GetCollision(false);
	
		// if we have such a collision, move only to it
		if (last_ditch_collision)
		{
			time_step = (feeler.GetDist() * feeler.Length() - 6.0f) / m_vel.Length();
			if (time_step < 0.0f)
			{
				time_step = 0.0f;
			}
		}
	
		update_dynamic_state(time_step);

		// we do a very simple collision
		if (last_ditch_collision)
		{
			m_vel -= (1.0f + m_coeff_restitution) * Mth::DotProduct(m_vel, feeler.GetNormal()) * feeler.GetNormal();
		}

		remaining_time_step -= time_step;

		// make sure we're not in an infinite loop; a weirdly hanging box is better than a frozen game
		if (++main_update_loop_count == 5)
		{
			MESSAGE("too many last-ditch contacts in a single frame; going to sleep");
			sleep();
			break;
		}
	} while (last_ditch_collision);
	
	if (detect_collisions())
	{
		resolve_collisions();
	}

	consider_sleeping();

	// if we've gotten here, we need to update the object
	
	GetObject()->SetPos(m_pos + m_matrix.Rotate(m_model_offset));
	
	GetObject()->SetMatrix(m_matrix);
	GetObject()->SetDisplayMatrix(m_matrix);
	
	GetObject()->SetVel(m_vel);

	if (s_debug_lines_on)
	{
		draw_debug_lines();
	}
	
	#ifdef __NOPT_DEBUG__
	s_contact_states_in_use = false;
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CRigidBodyComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | RigidBody_IgnoreSkater | rigidbody ignores the skater for a given duration
        // @uparm 0.0 | duration (defaults to seconds)
        // @flag frames | time is given in frames
		case CRCC(0xcc12cf87, "RigidBody_IgnoreSkater"):
		{
			pParams->GetFloat(NO_NAME, &m_ignore_skater_countdown, Script::ASSERT);
			if (pParams->ContainsFlag(CRCD(0x19176c5, "frames")) || pParams->ContainsFlag(CRCD(0x4a07c332, "frame")))
			{
				m_ignore_skater_countdown *= (1.0f / 60.0f);
			}
			break;
		}
		
		// @script | RigidBody_Wake | wakes up the rigidbody
		case CRCC(0x9599c10f, "RigidBody_Wake"):
			wake();
			break;

		// @script | RigidBody_Sleep | puts the rigidbody to sleep
		case CRCC(0xcd5ba67b, "RigidBody_Sleep"):
			sleep();
			break;
		
		// @script | RigidBody_Kick | wakes the rigidbody with a kick
		case CRCC(0xae24df9f, "RigidBody_Kick"):
		{
			Mth::Vector v;
			if (pParams->GetVector(CRCD(0xc4c809e, "vel"), &v))
			{
				GetObject()->SetVel(m_vel += v);
			}
			if (pParams->GetVector(CRCD(0xfb1a83b2, "rotvel"), &v))
			{
				m_rotvel += v;
			}
			if (pParams->GetVector(CRCD(0x7f261953, "pos"), &v))
			{
				GetObject()->SetPos(m_pos += v);
			}
			wake();
			break;
		}

		// @script : RigidBody_Reset | reset any parameters of the rigidbody
		case CRCC(0x92f5db9a, "RigidBody_Reset"):
			InitFromStructure(pParams);
			break;
			
		// @script : RigidBody_DisablePlayerCollision
		case CRCC(0xa2bbb3a, "RigidBody_DisablePlayerCollision"):
			m_flags.Set(PLAYER_COLLISION_DISABLED);
			break;
		
		// @script : RigidBody_EnablePlayerCollision
		case CRCC(0x49f44585, "RigidBody_EnablePlayerCollision"):
			m_flags.Clear(PLAYER_COLLISION_DISABLED);
			break;
			
		// @script : RigidBody_EnablePlayerCollision
		case CRCC(0x949be8a9, "RigidBody_MatchVelocityTo"):
		{
			Script::CComponent* p_component = pParams->GetNextComponent(NULL);
			Dbg_MsgAssert(p_component->mType == ESYMBOLTYPE_NAME, ("RigidBody_MatchVelocityTo requires an object name as its first parameter"));
			CCompositeObject* p_composite_object = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(p_component->mChecksum));
			Dbg_MsgAssert(p_composite_object, ("RigidBody_MatchVelocityTo requires a composite object name as its first parameter"));
			
			bool apply_random_adjustment = pParams->ContainsFlag(CRCD(0x8ace8a0f, "ApplyRandomAdjustment"));
			
			m_vel = p_composite_object->m_vel;
			if (apply_random_adjustment)
			{
				m_vel *= 1.0f + Mth::PlusOrMinus(0.4f);
			}
			GetObject()->SetVel(m_vel);
			
			if (m_state == ASLEEP && Mth::Abs(m_vel[Y]) < 1.0f)
			{
				m_vel[Y] = 1.0f;
			}
			
			// use the rotational velocity also, if it's a rigidbody
			CRigidBodyComponent* p_rigid_body_component = GetRigidBodyComponentFromObject(p_composite_object);
			if (p_rigid_body_component)
			{
				m_rotvel = p_rigid_body_component->m_rotvel;
				if (apply_random_adjustment)
				{
					m_rotvel *= 1.0f * Mth::PlusOrMinus(0.4f);
				}
			}
			
			wake();
			
			break;
		}
		
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info, ("NULL p_info sent to CRigidBodyComponent::GetDebugInfo"));

	uint32 state_checksums[] =
	{
		CRCD(0x4484b712, "ASLEEP"), CRCD(0x99a34896, "AWAKE")
	};
	p_info->AddChecksum("m_state", state_checksums[m_state]);

	p_info->AddFloat("m_orientation", m_orientation.GetScalar());
	p_info->AddVector("m_orientation", m_orientation.GetVector());

	p_info->AddVector("m_vel", m_vel);

	p_info->AddVector("m_rotvel", m_rotvel);

	p_info->AddFloat("m_const_acc", m_const_acc);

	p_info->AddFloat("m_mass_over_moment", m_mass_over_moment);

	p_info->AddFloat("m_coeff_restitution", m_coeff_restitution);

	p_info->AddFloat("m_coeff_friction", m_coeff_friction);
	
	p_info->AddFloat("m_spring_const", m_spring_const);

	p_info->AddFloat("m_skater_collision_impulse_factor", m_skater_collision_impulse_factor);
	
	p_info->AddFloat("m_skater_collision_rotation_factor", m_skater_collision_rotation_factor);
	
	p_info->AddFloat("m_skater_collision_radius", m_skater_collision_radius);

    p_info->AddFloat("m_num_collisions", m_num_collisions);
	
	p_info->AddVector("m_model_offset", m_model_offset);
	
	p_info->AddFloat("CollideMuteDelay", m_sound_setup.collide_mute_delay);
	
	p_info->AddFloat("GlobalCollideMuteDelay", m_sound_setup.global_collide_mute_delay);
	
	p_info->AddFloat("BounceVelocityCallbackThreshold", m_sound_setup.bounce_velocity_callback_threshold);
	
	p_info->AddFloat("BounceVelocityFullSpeed", m_sound_setup.bounce_velocity_full_speed);
	
	Script::CArray* p_array = new Script::CArray;
	p_array->SetSizeAndType(m_num_contacts, ESYMBOLTYPE_VECTOR);
	for (int n = m_num_contacts; n--; )
	{
		Script::CVector* p_vector = new Script::CVector;
		p_vector->mX = mp_contacts[n].p[X];
		p_vector->mY = mp_contacts[n].p[Y];
		p_vector->mZ = mp_contacts[n].p[Z];
		p_array->SetVector(n, p_vector);
	}
	p_info->AddArrayPointer("m_contacts", p_array);
	
	p_info->AddChecksum("CollideScript", m_script_names.collide);
	p_info->AddChecksum("BounceScript", m_script_names.bounce);
	p_info->AddChecksum("SettleScript", m_script_names.settle);
	p_info->AddChecksum("StuckScript", m_script_names.stuck);
	
	if (mp_script_params)
	{
		p_info->AddStructure("CallbackParams", mp_script_params);
	}

	p_info->AddChecksum("DieUponSettling", m_flags.Test(DIE_UPON_SLEEP) ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	p_info->AddChecksum("PlayerCollisionDisabled", m_flags.Test(PLAYER_COLLISION_DISABLED) ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	
	if (m_sound_setup.bounce_sound)
	{
		Script::CStruct* p_struct = new Script::CStruct;
		*p_struct += *m_sound_setup.bounce_sound;
		p_info->AddStructurePointer(CRCD(0x1fb2f60c, "BounceSound"), p_struct);
	}
	if (m_sound_setup.collide_sound)
	{
		Script::CStruct* p_struct = new Script::CStruct;
		*p_struct += *m_sound_setup.collide_sound;
		p_info->AddStructurePointer(CRCD(0xbf8e0ace, "CollideSound"), p_struct);
	}
	
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::sToggleDrawRigidBodyDebugLines (   )
{
	if (s_debug_lines_on)
	{
		/*
		if (s_draw_skater_collision_circles)
		{
			s_debug_lines_on = s_draw_skater_collision_circles = false;
		}
		else
		{
			s_draw_skater_collision_circles = true;
		}
		*/
		s_debug_lines_on = false;
	}
	else
	{
		s_debug_lines_on = true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::setup_contact_states (   )
{
	for (int n = m_num_contacts; n--; )
	{
		sp_contact_states[n].p_world = m_matrix.Rotate(mp_contacts[n].p);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::handle_skater_collisions (   )
{
	for (int n = Mdl::Skate::Instance()->GetNumSkaters(); n--; )
	{
		CSkater& skater = *Mdl::Skate::Instance()->GetSkater(n);

		float radius_boost = skater.GetRigidBodyCollisionRadiusBoost();
		
		// detect collisions within a cylinder of the skater; if objects end up having significant extent in some directions and not others,
		// this detection code may have to become more sophisticated

		// check distance to the skater in the X-Z plane
		float distance_sqr = (m_pos[X] - skater.GetPos()[X]) * (m_pos[X] - skater.GetPos()[X])
			+ (m_pos[Z] - skater.GetPos()[Z]) * (m_pos[Z] - skater.GetPos()[Z]);
		if (distance_sqr > Mth::Sqr(m_skater_collision_radius + radius_boost)) continue;

		// check Y-offset to the skater
		float y_offset = skater.GetPos()[Y] - m_pos[Y];
		if (y_offset > m_skater_collision_application_radius + radius_boost
			|| y_offset < -(m_skater_collision_application_radius + radius_boost + s_skater_head_height)) continue;

		// apply the skater collision impulse; the impulse occurs at a slight assent and in a direction halfway between of the skater's
		// velocity and the line to the object; the magnitude is roughly proportional to the component of the skater's velocity along the
		// line to the object; the impulse will be applied a few inches above the skater's lowest point, along the line between the object
		// and the skater at distance equal to the collision radius of the object

		// unit vector pointing from the skater to the object in the X-Z plane
		Mth::Vector r = m_pos;
		r -= skater.GetPos();
		// r[Y] = 0.0f; // optimized out as r[Y] never used
		float eff_length = sqrtf(r[X] * r[X] + r[Z] * r[Z]);
		r[X] /= eff_length;
		r[Z] /= eff_length;
		
		// skater's relative velocity in the X-Z plane
		Mth::Vector skater_vel = skater.GetVel();
		skater_vel -= m_vel;
		skater_vel[Y] = 0.0f;

		if(( fabsf( skater_vel[X] ) < 0.01f ) && ( fabsf( skater_vel[Z] ) < 0.01f ))
		{
			// Small enough not to worry about - smaller can cause NaN decomposition.
			return;
		}
		
		// unadjusted impulse magnitude
		float magnitude = skater_vel[X] * r[X] + skater_vel[Z] * r[Z];
		if (magnitude <= 0.0f) continue;

		// we adjust the impulse magnitude so the object will be vaulted in front of the skater so as to give the player a nice view of the object's
		// dynamics; also, at low skater velocities, we want only a very small impulse

		// fast collisions
		if (magnitude > (2.0f * vRP_SKATER_COLLISION_VELOCITY_THRESHOLD_INCREMENT))
		{
			// increase the impulse by a constant
			magnitude += ((1.0f + vRP_SKATER_COLLISION_VELOCITY_THRESHOLD_INCREMENT) * vRP_SKATER_COLLISION_VELOCITY_FACTOR);
		}
		// medium collisions
		else if (magnitude > vRP_SKATER_COLLISION_VELOCITY_THRESHOLD_INCREMENT) {
			// connect the functions continuously
			magnitude += (vRP_SKATER_COLLISION_VELOCITY_FACTOR * vRP_SKATER_COLLISION_VELOCITY_THRESHOLD_INCREMENT)
				+ (magnitude - vRP_SKATER_COLLISION_VELOCITY_THRESHOLD_INCREMENT);
		}
		else
		// slow collisions
		{
			// increase the impulse by a factor
			magnitude *= (1.0f + vRP_SKATER_COLLISION_VELOCITY_FACTOR);
		}

		// impulse direction in the X-Z plane is the average of the skater velocity and r directions
		// we know the Y component for both is zero
		Mth::Vector impulse = skater_vel;
		eff_length = sqrtf(impulse[X] * impulse[X] + impulse[Z] * impulse[Z]);
		impulse[X] = impulse[X] / eff_length + r[X];
		impulse[Z] = impulse[Z] / eff_length + r[Z];
		eff_length = sqrtf(impulse[X] * impulse[X] + impulse[Z] * impulse[Z]);
		impulse[X] = impulse[X] / eff_length;
		impulse[Z] = impulse[Z] / eff_length;

		// adjust the impulse direction upwards
		impulse[X] *= m_cos_skater_collision_assent;
		impulse[Y] = m_sin_skater_collision_assent;
		impulse[Z] *= m_cos_skater_collision_assent;

		// factor in the magnitude
		impulse *= m_skater_collision_impulse_factor * magnitude;

		// the impulse's point of application in the XZ-plane is along the line between the skater and the object at the object's impulse application
		// radius

		// we calculate the point of application with respect to the center of mass
        Mth::Vector application_point = skater.GetPos();
		application_point[X] -= m_pos[X];
		application_point[Y] = 0.0f;
		application_point[Z] -= m_pos[Z];
		application_point.Normalize(m_skater_collision_application_radius);

		// calculate the Y-component of the point of application
		if (y_offset > 0.0f)
		{
			// collide with the skater's feet
			application_point[Y] = skater.GetPos()[Y] - m_pos[Y];
		}
		else if (y_offset < -s_skater_head_height)
		{
			// collide with the skater's head
			application_point[Y] = skater.GetPos()[Y] + s_skater_head_height - m_pos[Y];
		}
		else
		{
			// collide with the skater's body; use an offset from the center of mass to cause rotation
			application_point[Y] = -12.0f;
		}

		// apply the impulse; artifically reduce the initial rotation; it looks nice when the objects tumbles more after their first bounce
		m_vel += impulse;
		m_rotvel += m_skater_collision_rotation_factor * 0.6f * m_mass_over_moment * Mth::CrossProduct(application_point, impulse);

		wake();

		// start a countdown; while this countdown is complete, we will ignore the skater
		m_ignore_skater_countdown = m_ignore_skater_duration;
		
		// call the bounce script callback
		if (m_script_names.collide)
		{
			GetObject()->SpawnScriptPlease(m_script_names.collide, mp_script_params)->Update();
		}
		
		// kill the object's shadow
		if (Nx::CSector *p_shadow_sector = Nx::CEngine::sGetMainScene()->GetSector(Crc::ExtendCRCWithString(GetObject()->GetID(), "_Shadow")))
		{
			p_shadow_sector->SetActive(false);
		}
		
		Tmr::Time time = Tmr::GetTime();
		if ((int) (time - m_collide_sound_allowed_time) > 0 && (int) (time - s_collide_sound_allowed_time) > 0)
		{
			#ifdef __NOPT_ASSERT__
			if (m_sound_type_id && Script::GetInteger(CRCD(0xd634a297, "DynamicRigidbodySounds")))
			{
				get_sound_setup(m_sound_type_id);
			}
			#endif
			
			if (m_sound_setup.collide_sound)
			{
				float percent = (100.0f / 1000.0f) * impulse.Length();
				m_sound_setup.collide_sound->AddFloat(CRCD(0x9e497fc6, "Percent"), percent);
				mp_sound_component->PlayScriptedSound(m_sound_setup.collide_sound);
				
				// delay the next collision sound
				m_collide_sound_allowed_time = Tmr::GetTime() + (50 + Mth::Rnd(100)) * m_sound_setup.collide_mute_delay / 100;
				s_collide_sound_allowed_time = Tmr::GetTime() + (50 + Mth::Rnd(100)) * m_sound_setup.global_collide_mute_delay / 100;
			}
		}

		// collide with only a single skater
		break;
	} // END loop over skaters
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::update_dynamic_state ( float time_step )
{
	// a massively shoe-string handling of contact forces
	if (m_num_collisions < 3)
	{
		Mth::Vector delta_pos = m_vel;
		delta_pos[Y] += 0.5f * time_step * m_const_acc;
		delta_pos *= time_step;
		m_pos += delta_pos;

		m_vel[Y] += time_step * m_const_acc;
	}
	else
	{
		m_pos += time_step * m_vel;
	}

	Mth::Quat delta_orientation = m_orientation;
	delta_orientation *= Mth::Quat(-0.5f * m_rotvel[X], -0.5f * m_rotvel[Y], -0.5f * m_rotvel[Z], 0.0f);
	delta_orientation *= time_step;
	m_orientation += delta_orientation;

	m_orientation.Normalize();

	m_orientation.GetMatrix(m_matrix);

	for (int n = m_num_contacts; n--; )
	{
		sp_contact_states[n].p_world = m_matrix.Rotate(mp_contacts[n].p);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::consider_sleeping (   )
{
	// it is possible that we may want to check these conditions over a period of several frames to insure that sleeping is warranted

	// only sleep if we're experiencing three or more collisions; hopefully, a surface-to-surface contact
	if (m_num_collisions < 3) return;

	// only sleep if we're moving slow
	if (m_vel.LengthSqr() > m_linear_velocity_sleep_point_sqr) return;

	// only sleep if we're moving slow
	if (m_rotvel.LengthSqr() > m_angular_velocity_sleep_point_sqr) return;

	sleep();
	
	// if we're not suppose to when going to sleep
	if (!m_flags.Test(DIE_UPON_SLEEP)) return;
	
	// only die if we've moved substantially from our starting point
	if ((m_pos - m_wake_pos).LengthSqr() < 48.0f * 48.0f) return;
	
	m_die_countdown = 1.5f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::wake (   )
{
	if (m_state != AWAKE)
	{
		m_wake_pos = GetObject()->GetPos();
		m_state = AWAKE;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::sleep (   )
{
	if (m_script_names.settle)
	{
		GetObject()->SpawnScriptPlease(m_script_names.settle, mp_script_params)->Update();
	}
	
	m_vel.Set(0.0f, 0.0f, 0.0f);
	m_rotvel.Set(0.0f, 0.0f, 0.0f);
	m_state = ASLEEP;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CRigidBodyComponent::detect_collisions (   )
{
	CFeeler feeler;

	// set up a bounding box around the space within which all collision detection will occur
	Mth::CBBox bounding_box(m_pos - Mth::Vector(m_largest_contact_extent, m_largest_contact_extent, m_largest_contact_extent),
		m_pos + Mth::Vector(m_largest_contact_extent, m_largest_contact_extent, m_largest_contact_extent));
	s_collision_cache.Update(bounding_box);
	feeler.SetCache(&s_collision_cache);

	// loop over the contact points
	m_num_collisions = 0;
	for (int n = m_num_contacts; n--; )
	{
		SContactState& contact_state = sp_contact_states[n];

		// run a feeler from the object's center to the contact point
		feeler.m_start = m_pos;
		feeler.m_end = m_pos;
		feeler.m_end += contact_state.p_world;

		contact_state.collision = feeler.GetCollision(false);

		if (!contact_state.collision) continue;

		contact_state.normal = feeler.GetNormal();
		contact_state.normal_eff_mass_set = false;
		contact_state.normal_impulse_magnitude = 0.0f;
		
		contact_state.depth = Mth::DotProduct(contact_state.normal, (1.0f - feeler.GetDist()) * (feeler.m_end - feeler.m_start));
		contact_state.collision = contact_state.depth <= 0.0f;
		
		if (contact_state.collision)
		{
			m_num_collisions++;
		}
	}

	return m_num_collisions != 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CRigidBodyComponent::calculate_effective_mass ( const Mth::Vector& direction, const SContactState& contact_state ) const
{
	// calculated the effective mass of the object at a contact for forces in a given direction

	Mth::Vector delta_rotvel = Mth::CrossProduct(contact_state.p_world, direction);
	delta_rotvel *= m_mass_over_moment;
	return 1.0f / Mth::DotProduct(direction, direction + Mth::CrossProduct(delta_rotvel, contact_state.p_world));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::resolve_collisions (   )
{
	// collision resolution code of my own devise; simplified and not wholly accurate, it is good enough because we only have a single object
	// colliding with a (mostly) static background; multiple collisions are not resolved simultaneously; instead, an iterative procedure is used to
	// resolve the normal forces; this can cause objects which are colliding face-to-face with a surface to bounce off with odd rotation,
	// as the required collision impulse will not be spread evenly across the contacts (as would be correct); also, friction is not handled in closed
	// form; instead, after the collision is resolved and the normal forces are determined, we go back in and apply frictional forces (using the
	// velocity of the object before the normal forces are applied; this generates friction which is incorrect, but visually reasonable; the friction
	// forces could ruin the resolution of the collision, but we just ignore that possibility, and leave the problem to be fixed on the next frame

	// cache the velocity and use it when determining the frictional forces
	Mth::Vector cache_vel = m_vel;
	Mth::Vector cache_rotvel = m_rotvel;
	
	// maximum collision velocity; used to determine if a bounce callback is warranted
	float max_collision_velocity = 0.0f;

	// loop until the collisions are fully resolved
	int collision_resolution_pass_count = 0;
	bool clean_pass;
	do {
		clean_pass = true;

		// loop over the collision points
		for (int n = m_num_contacts; n--; )
		{
			SContactState& contact_state = sp_contact_states[n];
			if (!contact_state.collision) continue;
	
			// calculate the normal velocity at the collision point
			Mth::Vector vel = m_vel;
			vel += Mth::CrossProduct(m_rotvel, contact_state.p_world);
			float normal_vel = Mth::DotProduct(contact_state.normal, vel);
	
			// skip if we're not colliding
			if (normal_vel > 0.0f) continue;
			clean_pass = false;
			
			if (-normal_vel > max_collision_velocity)
			{
				max_collision_velocity = -normal_vel;
			}
	
			// calculate the goal velocity
			float goal_normal_vel = -m_coeff_restitution * normal_vel;

			// calculate normal effective mass
			if (!contact_state.normal_eff_mass_set)
			{
				contact_state.normal_eff_mass = calculate_effective_mass(contact_state.normal, contact_state);
				contact_state.normal_eff_mass_set = true;
			}
	
			// calculate the normal impulse required to reach the goal velocity
			float normal_impulse = (goal_normal_vel - normal_vel) * contact_state.normal_eff_mass;

			// calculate normal impulse
			Mth::Vector impulse = contact_state.normal;
			impulse *= normal_impulse;

			// accumulate the total normal impulse applied at his contact
			contact_state.normal_impulse_magnitude += normal_impulse;

			// apply the normal impulse
			m_vel += impulse;
			m_rotvel += m_mass_over_moment * Mth::CrossProduct(contact_state.p_world, impulse);
		} // END loop over collision points

		if (++collision_resolution_pass_count == 20)
		{
			Dbg_Message("CRigidBodyComponent::resolve_collisions: unresolveable collision; zeroing velocity");
			// call the sleep script callback
			if (m_script_names.stuck)
			{
				GetObject()->SpawnScriptPlease(m_script_names.stuck, mp_script_params)->Update();
			}
			else
			{
				m_vel.Set(0.0f, 0.0f, 0.0f);
				m_rotvel.Set(0.0f, 0.0f, 0.0f);
			}
			return;
		}
		
		// if there's only one collision point, we need to make only a single pass
		if (m_num_contacts == 1) break;
	} while (!clean_pass);
	// END loop until collision is resolved

	// we've resolved the collision; now calculate and apply the friction using the cached velocities

	// loop over the collision points
	for (int n = m_num_contacts; n--; )
	{
		SContact& contact = mp_contacts[n];
		SContactState& contact_state = sp_contact_states[n];
		if (!contact_state.collision) continue;

		// calculate the tangent direction and velocity
		Mth::Vector vel = cache_vel;
		vel += Mth::CrossProduct(cache_rotvel, contact_state.p_world);
		float normal_vel = Mth::DotProduct(contact_state.normal, vel);
		Mth::Vector tangent = vel + -normal_vel * contact_state.normal;
		
		if (contact.directed_friction)
		{
			Mth::Vector friction_direction_world = m_matrix.Rotate(contact.friction_direction);
			friction_direction_world -= Mth::DotProduct(contact_state.normal, friction_direction_world) * contact_state.normal;
			
			float length = friction_direction_world.Length();
			if (length > 0.001f)
			{
				friction_direction_world *= (1.0f / length);
				tangent.ProjectToNormal(friction_direction_world);
			}
		}
		
		float tangent_vel = tangent.Length();

		// if the tangential velocity is too small, it's direction will be meaningless anyway
		if (tangent_vel < 0.001f) continue;

		// normalize by hand
		tangent /= tangent_vel;

		float tangent_eff_mass = calculate_effective_mass(tangent, contact_state);

		// calculate the tangential impulse required to stop the tangential	velocity
		float tangent_impulse = tangent_vel * tangent_eff_mass;

		// the frictional impulse is only allowed to be so big
		float max_tangent_impulse = m_coeff_friction * contact_state.normal_impulse_magnitude;
		if (tangent_impulse > max_tangent_impulse)
		{
			tangent_impulse = max_tangent_impulse;
		}
		
		// calculate the tangent impulse
		Mth::Vector impulse = tangent;
		impulse *= -tangent_impulse;
		
		if (s_draw_skater_collision_circles)
		{
			if (contact.directed_friction)
			{
				Gfx::AddDebugLine(contact_state.p_world + m_pos + Mth::Vector(0.0f, 1.0f, 0.0f), contact_state.p_world + m_pos + 6.0f * impulse + Mth::Vector(0.0f, 1.0f, 0.0f), MAKE_RGB(255, 255, 0), MAKE_RGB(255, 255, 0), 1);
			}
			else
			{
				Gfx::AddDebugLine(contact_state.p_world + m_pos + Mth::Vector(0.0f, 1.0f, 0.0f), contact_state.p_world + m_pos + 6.0f * impulse + Mth::Vector(0.0f, 1.0f, 0.0f), MAKE_RGB(255, 0, 255), MAKE_RGB(255, 0, 255), 1);
			}
		}

		// apply the tangent impulse to both the true velocity and the velocity we are using to determine the frictional forces

		Mth::Vector delta_rotvel = m_mass_over_moment * Mth::CrossProduct(contact_state.p_world, impulse);

		m_vel += impulse;
		m_rotvel += delta_rotvel;

		cache_vel += impulse;
		cache_rotvel += delta_rotvel;
	} // END loop over collision points
	
	// apply penalty forces to prevent interpenetration
	
	float time_step = Tmr::FrameLength();
	
	for (int n = m_num_contacts; n--; )
	{
		SContactState& contact_state = sp_contact_states[n];
		if (!contact_state.collision) continue;
		
		Mth::Vector impulse = -m_spring_const * contact_state.depth * time_step * contact_state.normal;
		
		Mth::Vector delta_rotvel = m_mass_over_moment * Mth::CrossProduct(contact_state.p_world, impulse);
		
		m_vel += impulse;
		m_rotvel += delta_rotvel;
		
		if (s_draw_skater_collision_circles)
		{
			Gfx::AddDebugLine(contact_state.p_world + m_pos, contact_state.p_world + m_pos + 60.0f * impulse, MAKE_RGB(255, 255, 0), MAKE_RGB(255, 255, 255), 1);
		}
	}
	
	// script callback
	if (max_collision_velocity > m_sound_setup.bounce_velocity_callback_threshold)
	{
		if (m_script_names.bounce)
		{
			GetObject()->SpawnScriptPlease(m_script_names.bounce, mp_script_params)->Update();
		}
		
		#ifdef __NOPT_ASSERT__
		if (m_sound_type_id && Script::GetInteger(CRCD(0xd634a297, "DynamicRigidbodySounds")))
		{
			get_sound_setup(m_sound_type_id);
		}
		#endif
		
		// don't use up the last vRP_NUM_UNTOUCHABLE_VOICES voices
		if (m_sound_setup.bounce_sound && NUM_VOICES - Sfx::CSfxManager::Instance()->GetNumSoundsPlaying() > vRP_NUM_UNTOUCHABLE_VOICES)
		{
			float percent = Mth::Clamp(100.0f * max_collision_velocity / m_sound_setup.bounce_velocity_full_speed, 0.0f, 100.0f);
			m_sound_setup.bounce_sound->AddFloat(CRCD(0x9e497fc6, "Percent"), percent);
			mp_sound_component->PlayScriptedSound(m_sound_setup.bounce_sound);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::get_sound_setup ( uint32 sound_type_id )
{

	Script::CStruct* p_base_struct;
	p_base_struct = Script::GetStructure(CRCD(0xb9738eba, "RigidBodySounds"), Script::ASSERT);

	Script::CStruct* p_sound_struct = NULL;
	p_base_struct->GetStructure(sound_type_id, &p_sound_struct);
	Dbg_MsgAssert(p_sound_struct, ("Sound type '%s' not found in RigidBodySounds", Script::FindChecksumName(sound_type_id)));

	Script::CStruct* p_struct;

	if (p_sound_struct->ContainsComponentNamed(CRCD(0xbf8e0ace, "CollideSound")))
	{
		p_sound_struct->GetStructure(CRCD(0xbf8e0ace, "CollideSound"), &p_struct, Script::ASSERT);
		if (m_sound_setup.collide_sound)
		{
			delete m_sound_setup.collide_sound;
		}
		m_sound_setup.collide_sound = new Script::CStruct;
		*m_sound_setup.collide_sound += *p_struct;
	}

	if (p_sound_struct->ContainsComponentNamed(CRCD(0x1fb2f60c, "BounceSound")))
	{
		p_sound_struct->GetStructure(CRCD(0x1fb2f60c, "BounceSound"), &p_struct, Script::ASSERT);
		if (m_sound_setup.bounce_sound)
		{
			delete m_sound_setup.bounce_sound;
		}
		m_sound_setup.bounce_sound = new Script::CStruct;
		*m_sound_setup.bounce_sound += *p_struct;

		if (p_sound_struct->ContainsComponentNamed(CRCD(0x3818ee47, "CollideMuteDelay")))
		{
			int time;
			p_sound_struct->GetInteger(CRCD(0x3818ee47, "CollideMuteDelay"), &time);
			m_sound_setup.collide_mute_delay = static_cast< Tmr::Time >(time);
		}
		if (p_sound_struct->ContainsComponentNamed(CRCD(0xad67a34d, "GlobalCollideMuteDelay")))
		{
			int time;
			p_sound_struct->GetInteger(CRCD(0xad67a34d, "GlobalCollideMuteDelay"), &time);
			m_sound_setup.global_collide_mute_delay = static_cast< Tmr::Time >(time);
		}
		p_sound_struct->GetFloat(CRCD(0x4511ca8d, "BounceVelocityCallbackThreshold"), &m_sound_setup.bounce_velocity_callback_threshold);
		p_sound_struct->GetFloat(CRCD(0x8fc6519f, "BounceVelocityFullSpeed"), &m_sound_setup.bounce_velocity_full_speed);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::setup_contacts_from_array ( Script::CArray* pArray )
{
	m_num_contacts = pArray->GetSize();
	Dbg_MsgAssert(m_num_contacts <= vRP_MAX_NUM_CONTACTS, ("Number of contacts in rigidbody exceeds limit of %i contacts", vRP_MAX_NUM_CONTACTS));
	
	if (mp_contacts)
	{
		delete [] mp_contacts;
	}
	mp_contacts = new SContact[m_num_contacts];
	
	for (unsigned int n = 0; n < m_num_contacts; n++)
	{
		Script::CVector* p_v = pArray->GetVector(n);
        mp_contacts[n].p[X] = p_v->mX;
        mp_contacts[n].p[Y] = p_v->mY;
        mp_contacts[n].p[Z] = p_v->mZ;
		mp_contacts[n].p[W] = 0.0f;
		mp_contacts[n].directed_friction = false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
		
void CRigidBodyComponent::setup_contacts_as_box ( const Mth::Vector& top_half_dimensions, const Mth::Vector& bottom_half_dimensions )
{
	m_num_contacts = 8;

	if (mp_contacts)
	{
		delete [] mp_contacts;
	}
	mp_contacts = new SContact[m_num_contacts];
	
	int n = 0;
	bool x = false;
	do {
		bool y = false;
		do {
			bool z = false;
			do {
				mp_contacts[n].p[X] = (x ? 1.0f : -1.0f) * (y ? top_half_dimensions[X] : bottom_half_dimensions[X]);
				mp_contacts[n].p[Y] = (y ? top_half_dimensions[Y] : -bottom_half_dimensions[Y]);
				mp_contacts[n].p[Z] = (z ? 1.0f : -1.0f) * (y ? top_half_dimensions[Z] : bottom_half_dimensions[Z]);
				mp_contacts[n].p[W] = 1.0f;
				mp_contacts[n].directed_friction = false;
				n++;
				z = !z;
			} while (z);
			y = !y;
		} while (y);
		x = !x;
	} while (x);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::setup_contacts_as_pyramid ( float half_height, float half_depth )
{
	m_num_contacts = 5;

	if (mp_contacts)
	{
		delete [] mp_contacts;
	}
	mp_contacts = new SContact[m_num_contacts];
	
	mp_contacts[0].p[X] = half_depth;
	mp_contacts[0].p[Y] = -0.6f * half_height;
	mp_contacts[0].p[Z] = half_depth;
	mp_contacts[0].p[W] = 1.0f;
	mp_contacts[0].directed_friction = false;

	mp_contacts[1].p[X] = -half_depth;
	mp_contacts[1].p[Y] = -0.6f * half_height;
	mp_contacts[1].p[Z] = half_depth;
	mp_contacts[1].p[W] = 1.0f;
	mp_contacts[0].directed_friction = false;

	mp_contacts[2].p[X] = -half_depth;
	mp_contacts[2].p[Y] = -0.6f * half_height;
	mp_contacts[2].p[Z] = -half_depth;
	mp_contacts[2].p[W] = 1.0f;
	mp_contacts[0].directed_friction = false;

	mp_contacts[3].p[X] = half_depth;
	mp_contacts[3].p[Y] = -0.6f * half_height;
	mp_contacts[3].p[Z] = -half_depth;
	mp_contacts[3].p[W] = 1.0f;
	mp_contacts[0].directed_friction = false;

	mp_contacts[4].p[X] = 0.0f;
	mp_contacts[4].p[Y] = 1.6f * half_height;
	mp_contacts[4].p[Z] = 0.0f;
	mp_contacts[4].p[W] = 1.0f;
	mp_contacts[0].directed_friction = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::setup_contacts_as_cylinder ( float top_radius, float bottom_radius, float half_height, int edges )
{
	m_num_contacts = 2 * edges;

	if (mp_contacts)
	{
		delete [] mp_contacts;
	}
	mp_contacts = new SContact[m_num_contacts];
	
	int i = 0;
	bool top = false;
	do {
		for (int n = edges; n--; )
		{
			mp_contacts[i].p[X] = (top ? top_radius : bottom_radius) * cosf(n * 2.0f * 3.1415f / edges);
			mp_contacts[i].p[Y] = (top ? half_height : -half_height);
			mp_contacts[i].p[Z] = (top ? top_radius : bottom_radius) * sinf(n * 2.0f * 3.1415f / edges);
			mp_contacts[i].p[W] = 1.0f;
			mp_contacts[i].directed_friction = false;
			i++;
		}
		top = !top;
	} while (top);
}
			

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::setup_contacts_as_triangle ( const Mth::Vector& half_dimensions )
{
	m_num_contacts = 6;

	if (mp_contacts)
	{
		delete [] mp_contacts;
	}
	mp_contacts = new SContact[m_num_contacts];
	
	int i = 0;
	bool left = false;
	do {
		mp_contacts[i].p[X] = half_dimensions[X];
		mp_contacts[i].p[Y] = -half_dimensions[Y];
		mp_contacts[i].p[Z] = (left ? -1.0f : 1.0f) * half_dimensions[Z];
		mp_contacts[i].p[W] = 1.0f;
		mp_contacts[i].directed_friction = false;
		i++;
		
		mp_contacts[i].p[X] = -half_dimensions[X];
		mp_contacts[i].p[Y] = -half_dimensions[Y];
		mp_contacts[i].p[Z] = (left ? -1.0f : 1.0f) * half_dimensions[Z];
		mp_contacts[i].p[W] = 1.0f;
		mp_contacts[i].directed_friction = false;
		i++;
		
		mp_contacts[i].p[X] = 0.0f;
		mp_contacts[i].p[Y] = half_dimensions[Y];
		mp_contacts[i].p[Z] = (left ? -1.0f : 1.0f) * half_dimensions[Z];
		mp_contacts[i].p[W] = 1.0f;
		mp_contacts[i].directed_friction = false;
		i++;
		
		left = !left;
	} while (left);
}			

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRigidBodyComponent::draw_debug_lines (   ) const
{
	int color = 0;
	bool collidable = false;
	switch (m_state)
	{
		case ASLEEP:
			color = MAKE_RGB(0, 0, 200);
			collidable = true;
			break;
			
		case AWAKE:
			if (m_ignore_skater_countdown > 0.0f)
			{
				color = MAKE_RGB(0, 200, 0);
			}
			else
			{
				color = MAKE_RGB(200, 0, 0);
				collidable = true;
			}
			break;
	}
	
	if (collidable && s_draw_skater_collision_circles)
	{
		Gfx::AddDebugCircle(m_pos + Mth::Vector(0.0f, m_skater_collision_application_radius, 0.0f), 8, m_skater_collision_radius, MAKE_RGB(200, 200, 0), 1);
		Gfx::AddDebugCircle(m_pos + Mth::Vector(0.0f, -m_skater_collision_application_radius, 0.0f), 8, m_skater_collision_radius, MAKE_RGB(200, 200, 0), 1);
	}
	
	if (s_draw_skater_collision_circles)
	{
		Gfx::AddDebugStar(m_pos, 36.0f, MAKE_RGB(200, 0, 200), 1);
	}
	
	// draw debug lines in less pretty yet general manner
	for (int n = m_num_contacts; n--; )
	{
		for (int m = 0; m < n; m++)
		{
			Gfx::AddDebugLine(sp_contact_states[n].p_world + m_pos, sp_contact_states[m].p_world + m_pos, color, color, 1);
		}
	}
}

}


================================================
FILE: Code/Gel/Components/rigidbodycomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       RigidBodyComponent.h
//* OWNER:          Dan
//* CREATION DATE:  1/22/3
//****************************************************************************

#ifndef __COMPONENTS_RIGIDBODYCOMPONENT_H__
#define __COMPONENTS_RIGIDBODYCOMPONENT_H__

#include 
#include 

#include 
#include 

#define		CRC_RIGIDBODY CRCD(0xffb39248, "RigidBody")
#define		GetRigidBodyComponent() ((Obj::CRigidBodyComponent*)GetComponent(CRC_RIGIDBODY))
#define		GetRigidBodyComponentFromObject(pObj) ((Obj::CRigidBodyComponent*)(pObj)->GetComponent(CRC_RIGIDBODY))

#define vRP_GRAVITATIONAL_ACCELERATION	 					(-386.4f)
#define vRP_DEFAULT_MASS_OVER_MOMENT						(0.003f)
#define vRP_DEFAULT_COEFF_RESTITUTION	  					(0.4f)
#define vRP_DEFAULT_COEFF_FRICTION	    					(0.5f)
#define vRP_DEFAULT_SPRING_CONST    						(10.0f)
#define vRP_DEFAULT_SKATER_COLLISION_RADIUS	  				(12.0f + 36.0f)
#define vRP_DEFAULT_SKATER_COLLISION_APPLICATION_RADIUS		(12.0f)
#define vRP_DEFAULT_SKATER_COLLISION_ASSENT					(25.0f)
#define vRP_DEFAULT_SKATER_COLLISION_IMPULSE_FACTOR			(1.0f)
#define vRP_DEFAULT_SKATER_COLLISION_ROTATION_FACTOR		(1.0f)
#define vRP_SKATER_COLLISION_VELOCITY_FACTOR 		   		(1.0f)
#define vRP_SKATER_COLLISION_VELOCITY_THRESHOLD_INCREMENT	(50.0f)
#define vRP_DEFAULT_LINEAR_VELOCITY_SLEEP_POINT	   			(10.0f)
#define vRP_DEFAULT_ANGULAR_VELOCITY_SLEEP_POINT   			(0.4f)
#define vRP_DEFAULT_IGNORE_SKATER_DURATION					(5.0f / 60.0f)
#define vRP_DEFAULT_COLLIDE_MUTE_DELAY						(1000)
#define vRP_DEFAULT_GLOBAL_COLLIDE_MUTE_DELAY				(100)
#define vRP_DEFAULT_BOUNCE_VELOCITY_CALLBACK_THRESHOLD		(20.0f)
#define vRP_DEFAULT_BOUNCE_VELOCITY_FULL_SPEED				(300.0f)

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSoundComponent;

class CRigidBodyComponent : public CBaseComponent
{
	enum ERigidBodyStateType
	{
		ASLEEP, AWAKE
	};

	enum EObjectGeometryType
	{
		BOX, PYRAMID, CYLINDER, TRIANGLE, NO_GEOMETRY
	};
	
	enum EFlagType
	{
		DIE_UPON_SLEEP,
		PLAYER_COLLISION_DISABLED
	};

	struct SContact
	{
		// defining element of a rigidbody contact point; position
		Mth::Vector p;
		
		// the axis of friction
		Mth::Vector friction_direction;
		
		// if friction at this contact occurs only along a single axis
		char directed_friction;
	};
	
	struct SContactState
	{
		// offset to the contact point in world space
		Mth::Vector p_world;

		// normal of the surface is it colliding with
		Mth::Vector normal;

		// the effective mass of this contact for impulses applied along the normal
		float normal_eff_mass;

		// keeps track of the total impulse magnitude of the normal force in a frame; used to limit the frictional impulse
		float normal_impulse_magnitude;
		
		// depth of the contact; negative corresponds to interpenetration
		float depth;

		// is this contact in collision
		char collision;

		// false before the normal_inv_mass has been set
		char normal_eff_mass_set;
	};
	
	// if the number of free voices is this number or less, don't play bounce sounds
	static const int vRP_NUM_UNTOUCHABLE_VOICES = 12;
	
	// maximum number of contacts allowed on a single rigidbody
	static const unsigned int vRP_MAX_NUM_CONTACTS = 24;

public:
    CRigidBodyComponent();
    virtual ~CRigidBodyComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
	virtual void					Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	static void 					sToggleDrawRigidBodyDebugLines (   );

private:

	void							setup_contact_states (   );
	void							handle_skater_collisions (   );
	void							update_dynamic_state ( float time_step );
	void							consider_sleeping (   );
	void							wake (   );
	void							sleep (   );
	bool							detect_collisions (   );
	float							calculate_effective_mass ( const Mth::Vector& direction, const SContactState& contact_state ) const;
	void							resolve_collisions (   );
	void							draw_debug_lines (   ) const;
	void							get_sound_setup ( uint32 sound_type_id );
	void							setup_contacts_as_box ( const Mth::Vector& top_half_dimensions, const Mth::Vector& bottom_half_dimensions );
	void							setup_contacts_as_pyramid ( float radius, float half_depth );
	void							setup_contacts_as_cylinder ( float top_radius, float bottom_radius, float half_depth, int edges = 6 );
	void							setup_contacts_as_triangle ( const Mth::Vector& half_dimensions );
	void							setup_contacts_from_array ( Script::CArray *pArray );

private:

	// properties:

	// object's don't have masses or moments of inertia;
	// instead, a single scalar is used for the moment of inertia and only the ratio of mass over moment is used;
	// this ratio controls the object's relative tendency to move linearly or rotationally;
	// if proper external forces are ever applied to rigidbodies, they will need a mass;
	// units are inverse inches squared; should be on the order of the inverse of the square of the characteristic
	// distance between the contact points and the center of mass
	float m_mass_over_moment;

	// object's acceleration due to constant force on its center of mass (gravity)
	float m_const_acc;

	// coefficient of restitution; bounce factor
	float m_coeff_restitution;

	// coefficient of friction
	float m_coeff_friction;
	
	// spring constant used to prevent interpenetration
	float m_spring_const;

	// effectively the sum of the object radius and the skater radius; when the center-to-center distance is below this, there is a collision
	float m_skater_collision_radius;

	// effectively the radius of the object; the impulse is applied at this distance from the object; to have reasonable rotations, m_mass_over_moment
	// should on the order of the inverse of this radius squared; also used in skater collisions to check their Y-interception
	float m_skater_collision_application_radius;

	// sine and cosine of the angle of assent of the skater collision impulse
	float m_cos_skater_collision_assent;
	float m_sin_skater_collision_assent;

	// factor which adjusts the magnitude of skater collision impulses; sort of like an inverse mass
	float m_skater_collision_impulse_factor;
	
	// scales the rotation the object gains from a collision with the skater
	float m_skater_collision_rotation_factor;

	// if the velocities are below these points, the object may go to sleep
	float m_linear_velocity_sleep_point_sqr;
	float m_angular_velocity_sleep_point_sqr;

	// currently, we assume a box; the algorithm is general enough to do anything
	SContact* mp_contacts;
	unsigned short m_num_contacts;
	
	// center of mass of the object
	Mth::Vector m_center_of_mass;
	
	// offset from the center of mass to the origin of the model
	Mth::Vector m_model_offset;

	// number of frames after a skater collision before we begin checking for skater collisions again
	float m_ignore_skater_duration;
	
	// callback script names
	struct SScriptCallbackNames
	{
		uint32 collide;
		uint32 bounce;
		uint32 settle;
		uint32 stuck;
	} m_script_names;
	
	// parameter structure passed to callback scripts
	Script::CStruct* mp_script_params;
	
	// sound setup
	struct SSoundSetup
	{
		Script::CStruct* collide_sound;
		Script::CStruct* bounce_sound;
		Tmr::Time collide_mute_delay;
		Tmr::Time global_collide_mute_delay;
		float bounce_velocity_callback_threshold;
		float bounce_velocity_full_speed;
	} m_sound_setup;
	
	// time when the next collide sound is allowed
	Tmr::Time m_collide_sound_allowed_time;
	
	// time when the any next collide sound is allowed
	static Tmr::Time s_collide_sound_allowed_time;
	
	// used to allow dynamic updating of sounds
	#ifdef __NOPT_ASSERT__
	uint32 m_sound_type_id;
	#endif
	
	// property flags
	Flags m_flags;
	
	// position at which we were last woke
	Mth::Vector m_wake_pos;
	
	// countdown to dying; not active if set to -1.0f
	float m_die_countdown;
	
	// primary state variables:

	// object's state; objects sleep when they are not active
	ERigidBodyStateType m_state;

	// object's center of mass position
	Mth::Vector m_pos;

	// object's velocity
	Mth::Vector m_vel;

	// object's orientation around center of mass
	Mth::Quat m_orientation;

	// object's angular velocity
	Mth::Vector m_rotvel;

	// dependent state variables:

	// rotation matrix based on our orientation
	Mth::Matrix m_matrix;

	// number of contacts in current collision
	char m_num_collisions;

	// count down used to time the interval between a skater collision and the point when we begin looking for skater collisions again
	float m_ignore_skater_countdown;

	// work variables:

	// collision cache; used to improve collision detection turn-around time; shared by all rigidbodies
	static Nx::CCollCache s_collision_cache;

	// longest distance between a contact point and the center of mass; used to generate the collision cache's bounding box
	float m_largest_contact_extent;

	// debug variables
	static bool s_debug_lines_on;
	static bool s_draw_skater_collision_circles;

	// height of the skater; used when determining skater collisions and resulting impulse point of application
	static float s_skater_head_height;
	
	// to save memory, the rigidbodies share an array of structures which hold the state of their contacts during collision resolution
	static SContactState sp_contact_states [ vRP_MAX_NUM_CONTACTS ];
	
	#ifdef __NOPT_DEBUG__
	// insures that only a single rigidbody is using sp_contact_states at a time
	static bool s_contact_states_in_use;
	#endif
	
	// peer components
	CSoundComponent* mp_sound_component;
};

}

#endif


================================================
FILE: Code/Gel/Components/shadowcomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ShadowComponent.cpp
//* OWNER:          gj
//* CREATION DATE:  2/06/03
//****************************************************************************

#include 

#include 
#include 

#include 

#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// This static function is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
CBaseComponent* CShadowComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CShadowComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CShadowComponent::CShadowComponent() : CBaseComponent()
{
	SetType( CRC_SHADOW );

	// no shadow object
	mp_shadow = NULL;					
	m_shadowNormal.Set(0.0f,1.0f,0.0f);
	m_shadowType = CRCD(0x806fff30,"none");
	m_shadowPos.Set(0,0,0);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CShadowComponent::~CShadowComponent()
{
	if ( mp_shadow )
	{
		delete mp_shadow;
		mp_shadow = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CShadowComponent::Finalize()
{
	mp_model_component = GetModelComponentFromObject( GetObject() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CShadowComponent::Teleport()
{									 
	// GJ:  the shadow component's Update()
	// function doesn't really require finalization
	// right now, but having this assert in here
	// might help catch future errors
	Dbg_MsgAssert( GetObject()->IsFinalized(), ( "Has not been finalized!  Tell Gary!" ) );

	Update();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CShadowComponent::InitFromStructure( Script::CStruct* pParams )
{
	m_shadowType = CRCD(0x3e84c2fd,"simple");
	pParams->GetChecksum( CRCD(0x9ac24b18,"ShadowType"), &m_shadowType, Script::NO_ASSERT );

	if ( mp_shadow )
	{
		// get rid of existing shadow, in case it was the wrong type...
		SwitchOffShadow();
	}

	switch ( m_shadowType )
	{
		case 0x3e84c2fd:	// simple
			{
				if ( !mp_shadow )
				{
					SwitchOnShadow( Gfx::vSIMPLE_SHADOW );
				}
				
				float scale = 1.0f;
				pParams->GetFloat( CRCD(0x6f8cd62f,"ShadowScale"), &scale, Script::NO_ASSERT );
				
				const char *p_shadow_model_name = "Ped_Shadow";
				pParams->GetString( CRCD(0x545f8172,"ShadowModel"), &p_shadow_model_name, Script::NO_ASSERT );
						
				Dbg_MsgAssert( mp_shadow, ("NULL mp_shadow") );
				Gfx::CSimpleShadow* pSimpleShadow = (Gfx::CSimpleShadow*)mp_shadow;
				pSimpleShadow->SetScale( scale );
				pSimpleShadow->SetModel( p_shadow_model_name );

				// GJ:  need to immediately change the shadow's position if Obj_ShadowOn gets called
				// this is because sometimes the shadow component will be suspended, and so
				// update_shadow() won't get called (fixes shadow appearing at the origin in SC2)
				pSimpleShadow->UpdatePosition( m_shadowPos, GetObject()->m_matrix, m_shadowNormal );
			}
			break;

		case 0x76a54cd1:	// detailed
			{
				if ( !mp_shadow )
				{
					SwitchOnShadow( Gfx::vDETAILED_SHADOW );
				}
			}
			break;

		case 0x806fff30:	// none
			{
				if ( mp_shadow )
				{
					SwitchOffShadow();
				}
			}
			break;

		default:
			Dbg_MsgAssert( 0, ( "Unrecognized shadow type %s", Script::FindChecksumName(m_shadowType) ) );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CShadowComponent::update_shadow()
{
	if ( mp_shadow )
	{
//		Dbg_MsgAssert(mp_shadow->GetShadowType()!=Gfx::vDETAILED_SHADOW,("Tried to update a detailed shadow in MovingObj_Update, will need to change code to send a higher-up position"));

		
		Nx::CModel* pModel = NULL;
		if ( mp_model_component )
		{
			pModel = mp_model_component->GetModel();
		}
		
		// if we have a model and the model is not active,then don't need to update the shadwo
		if (pModel)
		{
			if (pModel->GetActive())
			{
				switch( mp_shadow->GetShadowType() )
				{
					case Gfx::vDETAILED_SHADOW:
					{
		//				Mth::Vector shadow_target_pos = pos + ( matrix.GetUp() * 36.0f );
						Mth::Vector shadow_target_pos = GetObject()->m_pos + ( GetObject()->m_matrix.GetUp() * 36.0f );

		#ifdef __PLAT_XBOX__
						// K: Moved this in here cos it was giving an unused-variable compile error on PS2
						Mth::Vector ground_dir( 0.2f, -0.8f, 0.3f );

						// If lights are active, set the ground direction to be that of the primary light.
						if( pModel )
						{
							Nx::CModelLights* p_lights = pModel->GetModelLights();
							if( p_lights )
							{
								ground_dir = p_lights->GetLightDirection( 0 ) * -1.0f;
							}
						}
		//				mp_shadow->UpdateDirection( ground_dir );
		#endif	
						mp_shadow->UpdatePosition( shadow_target_pos );
					}
					break;

					case Gfx::vSIMPLE_SHADOW:
					{
						if ( pModel )
						{
							((Gfx::CSimpleShadow*)mp_shadow)->SetScale( pModel->GetScale().GetX() );
						}	

						/*
						TODO:  Commented this section out, because it 
						references m_jump_start_pos, which isn't accessible
						from the shadow code yet...

						if ( GetMotionComponentFromObject( GetObject() )->m_movingobj_status & MOVINGOBJ_STATUS_JUMPING )
						{
							// If jumping, use the jump's start-y so that the shadow stays on the ground.	
							Mth::Vector p=GetObject()->m_pos;
							p[Y]=m_jump_start_pos[Y];
							mp_shadow->UpdatePosition(p,GetObject()->m_matrix,m_shadowNormal);
						}
						else
						*/
						{
							mp_shadow->UpdatePosition( m_shadowPos, GetObject()->m_matrix, m_shadowNormal );
						}
					}
					break;

					default:
						Dbg_MsgAssert(0,("Bad shadow type: %d",mp_shadow->GetShadowType()));
						break;
				}

				mp_shadow->UnHide();
			}
			else
			{
				// model is not active, so we probably don't want the shadow's model to be active either
				mp_shadow->Hide();
			}
		}
	}			
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CShadowComponent::Hide( bool shouldHide )
{
	HideShadow( shouldHide );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CShadowComponent::Update()
{
//	m_shadowNormal.Set( 0.0f, 1.0f, 0.0f );

	/*
	if ( mp_shadow )
	{
		if (mp_shadow->GetShadowType()==Gfx::vSIMPLE_SHADOW)
		{
			mp_shadow->UpdatePosition(m_shadow_pos,m_matrix,pShadowComponent->m_shadownormal);
		}		
		else
		{
			mp_shadow->UpdatePosition(shadow_target_pos); // at this point m_pos is the same as mp_physics->m_pos
		}
	}
	*/

	if ( GetObject()->GetID() >= 0 && GetObject()->GetID() < Mdl::Skate::vMAX_SKATERS )
	{
		// the skater shadows are handled elsewhere by other components
		// (CSkaterAdjustPhysicsComponent or CWalkComponent)
//		SetShadowPos( GetObject()->GetPos() );
	}
	else
	{
		// the ped shadow is handled here...
		SetShadowPos( GetObject()->GetPos() );
	}

	update_shadow();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent::EMemberFunctionResult CShadowComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | Obj_ShadowOff | turn off shadow
		case ( 0xaaf6e513 ): // Obj_ShadowOff
			SwitchOffShadow();
			break;

        // @script | Obj_ShadowOn | turn shadow on
		// @parmopt float | Offset | 0.2 | The y offset of the shadow, > 0 = up
		// @parmopt float | ShadowScale | 1.0 | The scale of the shadow.
		// @parmopt string | ShadowModel | "Ped_Shadow" | The mdl file to use
		// @parmopt name | shadow_type | Either "detailed" or "simple"
		case ( 0xf272c43a ): // Obj_ShadowOn
		{
			// add a default shadow_type
			uint32 shadowType;
			if ( !pParams->GetChecksum( "shadowType", &shadowType, Script::NO_ASSERT ) )
			{
				pParams->AddChecksum( "shadowType", CRCD(0x3e84c2fd,"simple") );
			}

			this->InitFromStructure( pParams );

			if ( mp_shadow )
			{
				if( mp_shadow->GetShadowType() == Gfx::vSIMPLE_SHADOW )
				{
					float offset=0.2f;
					pParams->GetFloat(CRCD(0xa6f5352f,"Offset"),&offset);
					((Gfx::CSimpleShadow*)mp_shadow)->SetOffset(offset);
				}
			}
		}
		break;

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CShadowComponent::SwitchOnShadow( Gfx::EShadowType mode )
{
	
	
	if ( mp_shadow )
	{
		// it's already got a shadow
		return;
	}

// Note we can't use mp_model_component here, as SwitchOnShaodow is called
// from InitFromStructure, which is obviously called before Finalize(); 
	CModelComponent* pModelComponent = GetModelComponentFromObject( GetObject() );
	Nx::CModel* pModel = NULL;
	if ( pModelComponent )
	{
		pModel = pModelComponent->GetModel();
	}


	switch (mode)
	{
		case Gfx::vDETAILED_SHADOW:
		{
			Dbg_MsgAssert( mp_shadow == NULL, ("mp_shadow not NULL?") );
			Dbg_MsgAssert( pModel, ("adding detailed shadow to something with no model?") );
			mp_shadow = new Gfx::CDetailedShadow( pModel );
		}
		break;

		case Gfx::vSIMPLE_SHADOW:
		{
			Dbg_MsgAssert( mp_shadow == NULL, ("mp_shadow not NULL?") );
			Dbg_MsgAssert( pModel, ("adding simple shadow to something with no model?") );
			
			Gfx::CSimpleShadow* pSimpleShadow = new Gfx::CSimpleShadow;
			pSimpleShadow->SetScale( pModel->GetScale().GetX() );
			pSimpleShadow->SetModel( "Ped_Shadow" );
			
			mp_shadow = pSimpleShadow;
			mp_shadow->UpdatePosition( m_shadowPos, GetObject()->m_matrix, m_shadowNormal );
		}	
		break;
		
		default:
			Dbg_Message( "Unrecognized shadow mode %d", mode );
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CShadowComponent::SwitchOffShadow()
{
	if ( mp_shadow )
	{
		if ( mp_model_component )
		{
			Nx::CModel* pModel = mp_model_component->GetModel();
			Dbg_Assert( pModel );
			pModel->EnableShadow( false );
		}

		delete mp_shadow;
		mp_shadow = NULL;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CShadowComponent::HideShadow( bool should_hide )
{
	if ( mp_shadow )
	{
		if ( should_hide )
		{
			mp_shadow->Hide();
		}
		else
		{
			mp_shadow->UnHide();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CShadowComponent::SetShadowPos( const Mth::Vector& pos )
{
	m_shadowPos = pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CShadowComponent::SetShadowNormal( const Mth::Vector& normal )
{
	m_shadowNormal = normal;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CShadowComponent::SetShadowScale( float scale )
{
	if ( m_shadowType == CRCD(0x3e84c2fd,"simple") )
	{
		((Gfx::CSimpleShadow*)mp_shadow)->SetScale( scale );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CShadowComponent::SetShadowDirection( const Mth::Vector& vector )
{
	if( mp_shadow )
	{
		mp_shadow->UpdateDirection( vector );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CShadowComponent::SwitchOnSkaterShadow()
{
	// NOTE: skater specific and should not be here; migrate to somewhere like Mdl::Skate
	
	// only call on shadows attached to skaters
	Dbg_MsgAssert(GetObject()->GetType() == SKATE_TYPE_SKATER, ("CShadowComponent::SwitchOnSkaterShadow called on shadow not attached to skater object"));
	
	CSkater* pSkater = static_cast< CSkater* >(GetObject());
	
	Gfx::EShadowType mode = Gfx::vDETAILED_SHADOW;
	
	// put it on the bottom up heap, because we don't want to fragment the skater geom heap...
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterHeap(pSkater->GetHeapIndex()));

	// In Splitscreen games, shadow type for local player is optional
	if (CFuncs::ScriptInSplitScreenGame(NULL, NULL))
	{
		mode = Mdl::Skate::Instance()->GetShadowMode();
	}

	if (!pSkater->IsLocalClient())
	{
		Dbg_Printf( "************************ SWITCHING ON SKATER SHADOW FOR CLIENT\n" );
		mode = Gfx::vSIMPLE_SHADOW;
	}
	
	Script::CStruct* pTempParams = new Script::CStruct;
	pTempParams->AddFloat(CRCD(0x6f8cd62f, "ShadowScale"), 1.0f);
	pTempParams->AddString(CRCD(0x545f8172, "ShadowModel"), "Ped_Shadow");
	InitFromStructure(pTempParams);
	delete pTempParams;

	SwitchOffShadow();
	SwitchOnShadow(mode);

	Mem::Manager::sHandle().PopContext();
}
	
}


================================================
FILE: Code/Gel/Components/shadowcomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       ShadowComponent.h
//* OWNER:			Gary
//* CREATION DATE:  2/06/03
//****************************************************************************

#ifndef __COMPONENTS_SHADOWCOMPONENT_H__
#define __COMPONENTS_SHADOWCOMPONENT_H__

#include 
#include 

// For EShadowType
#include 

#include 
			
#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_SHADOW CRCD(0x8a897dd2,"shadow")
#define		GetShadowComponent() ((CShadowComponent*)GetComponent(CRC_SHADOW))
#define		GetShadowComponentFromObject(pObj) ((Obj::CShadowComponent*)(pObj)->GetComponent(CRC_SHADOW))

namespace Gfx
{
	class CShadow;
}

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class	CModelComponent;

class CShadowComponent : public CBaseComponent
{
public:
    CShadowComponent();
    virtual ~CShadowComponent();

public:
    virtual void					Finalize( );
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void					Hide( bool should_hide );
	virtual void					Teleport();

    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	
	static CBaseComponent*			s_create();

protected: 
	void							update_shadow();
		
		
public:		
	void							SwitchOnShadow(Gfx::EShadowType mode);
	void 							SwitchOffShadow();
	void 							HideShadow( bool should_hide );
	void							SetShadowPos( const Mth::Vector& vector );
	void							SetShadowNormal( const Mth::Vector& vector );
	void							SetShadowScale( float scale );
	void							SetShadowDirection( const Mth::Vector& vector );
	
	void							SwitchOnSkaterShadow();

protected:
	Gfx::CShadow *					mp_shadow;
	Mth::Vector						m_shadowPos;
	Mth::Vector						m_shadowNormal;
	uint32							m_shadowType;
	CModelComponent * 				mp_model_component;
};

}

#endif


================================================
FILE: Code/Gel/Components/skeletoncomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SkeletonComponent.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/22/2002
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

// TODO:  These won't be needed after the initial
// matrix data gets moved to the SKE file
#include 
#include 
#include 
#include 
#include 

// GJ:  CSkeletonComponent is supposed to be a wrapper
// around the Gfx::CSkeleton, which contains a skeletal
// object's final, post-blend matrix buffer.  It should
// be pretty dumb;  neither the CSkeletonComponent nor
// the Gfx::CSkeleton should contain any blending or 
// procedural anim data or functionality.  Furthermore, 
// it shouldn't know anything about animation flipping
// or skateboard rotation.   	    

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// This static function is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
CBaseComponent* CSkeletonComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkeletonComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkeletonComponent::init_skeleton( uint32 skeleton_name )
{
	Dbg_MsgAssert( mp_skeleton == NULL, ( "Component already has skeleton." ) );
    
	Gfx::CSkeletonData* pSkeletonData = (Gfx::CSkeletonData*)Ass::CAssMan::Instance()->GetAsset( skeleton_name, false );

	if ( !pSkeletonData )
	{
		Dbg_MsgAssert( 0, ("Unrecognized skeleton %s. (Is skeleton.q up to date?)", Script::FindChecksumName(skeleton_name)) );
	}
    
	mp_skeleton = new Gfx::CSkeleton( pSkeletonData );   
    Dbg_MsgAssert( mp_skeleton, ( "Couldn't create skeleton" ) );

    Dbg_MsgAssert( mp_skeleton->GetNumBones() > 0, ( "Skeleton needs at least one bone" ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkeletonComponent::CSkeletonComponent() : CBaseComponent()
{
	SetType( CRC_SKELETON );

    mp_skeleton = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkeletonComponent::~CSkeletonComponent()
{
    Dbg_MsgAssert( mp_skeleton, ( "No skeleton had been initialized" ) );
    
    if ( mp_skeleton )
    {
        delete mp_skeleton;
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkeletonComponent::InitFromStructure( Script::CStruct* pParams )
{
    uint32 skeletonName;

    if ( !pParams->GetChecksum( CRCD(0x222756d5,"skeleton"), &skeletonName, Script::NO_ASSERT ) )
	{
		pParams->GetChecksum( CRCD(0x9794932,"skeletonName"), &skeletonName, Script::ASSERT );
	}

    init_skeleton( skeletonName );

	int maxBoneSkipLOD;
	if(( pParams->GetInteger( CRCD(0xd3982061,"max_bone_skip_lod"), &maxBoneSkipLOD )) && mp_skeleton )
	{
		mp_skeleton->SetMaxBoneSkipLOD( maxBoneSkipLOD );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkeletonComponent::Update()
{
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Gfx::CSkeleton* CSkeletonComponent::GetSkeleton()
{
    return mp_skeleton;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkeletonComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch (Checksum)
    {
		// @script | Obj_SetBoneActive | changes whether the bone should be updated once per frame
		case 0x20f7b992: // Obj_SetBoneActive
		{
			Dbg_MsgAssert( mp_skeleton, ( "Obj_SetBoneActive on object without a skeleton" ) );
			
			uint32 boneName;
			pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, Script::ASSERT );
			
			int enabled;
			pParams->GetInteger( CRCD(0xb4e103fd,"active"), &enabled, Script::ASSERT );

			Dbg_Assert( mp_skeleton );
            mp_skeleton->SetBoneActive( boneName, enabled );

			break;
		}

		case 0xbe1c58ca:	// Obj_GetBonePosition
		{
			Mth::Vector bonePos(0.0f,0.0f,0.0f,1.0f);

			uint32 boneName;
			pParams->GetChecksum( CRCD(0xcab94088, "bone"), &boneName, Script::ASSERT );
			bool success = GetBoneWorldPosition( boneName, &bonePos );
			
			// make sure we have somewhere to return the data
			Dbg_Assert( pScript && pScript->GetParams() );
			pScript->GetParams()->AddFloat( CRCD(0x7323e97c,"x"), bonePos[X] );
			pScript->GetParams()->AddFloat( CRCD(0x424d9ea,"y"), bonePos[Y] );
			pScript->GetParams()->AddFloat( CRCD(0x9d2d8850,"z"), bonePos[Z] );
		
			return success ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		
		// @script | TextureSplat | create a texture splat
        // @parm float | size | 
        // @parmopt float | lifetime | 5.0 | lifetime in seconds
        // @parmopt float | radius | 0.0 | radius
        // @parmopt float | dropdown_length | 3.0 | dropdown length in feet
        // @parmopt float | forward | | distance forward of the origin that this spat goes, assuming no bone specififed		
        // @parmopt string | bone || bone name
        // @parmopt string | name || texture name
        // @parmopt string | trail || indicates that this splat should start or join a trail
		case CRCC(0xb244c9cf,"TextureSplat"):
			
		// @script | Skeleton_SpawnTextureSplat | create a texture splat
		// @parm float | size | 
		// @parmopt float | lifetime | 5.0 | lifetime in seconds
		// @parmopt float | radius | 0.0 | radius
		// @parmopt float | dropdown_length | 3.0 | dropdown length in feet
		// @parmopt float | forward | | distance forward of the origin that this spat goes, assuming no bone specififed		
		// @parmopt string | bone || bone name
		// @parmopt string | name || texture name
		// @parmopt string | trail || indicates that this splat should start or join a trail
		case CRCC(0xc51ce359, "Skeleton_SpawnTextureSplat"):
		{	
			float		size;
			float		radius				= 0.0f;
			float		lifetime			= 5.0f;
			float		dropdown_length		= 3.0f;
			float 		forward 			= 0.0f;
			bool		trail				= pParams->ContainsFlag( 0x4d977a70 /*"trail"*/ );
			bool		dropdown_vertical	= pParams->ContainsFlag( 0x7ee6c892 /*"dropdown_vertical"*/ );
			uint32		bone_name_checksum  = 0;
			const char*	p_texture_name;
			Mth::Vector	pos;

			pParams->GetFloat( CRCD(0x83fdb95,"size"), &size );
			pParams->GetFloat( CRCD(0xc48391a5,"radius"), &radius );
			pParams->GetFloat( CRCD(0xc218cf77,"lifetime"), &lifetime );
			pParams->GetFloat( CRCD(0x12c62572,"dropdown_length"), &dropdown_length );
			
			if( !( pParams->GetText( "name", &p_texture_name )))
			{
				Dbg_MsgAssert( 0, ("%s\nTextureSplat requires a texture name"));
			}

			if( !(pParams->GetChecksumOrStringChecksum( "bone", &bone_name_checksum )))
			{
				if (!pParams->GetFloat( CRCD(0x186ed079,"forward"), &forward ))
				{
					Dbg_MsgAssert( 0, ("%s\nTextureSplat requires a bone name"));
				}
				else
				{
					pos = GetObject()->GetPos();
					pos += forward * GetObject()->GetMatrix()[Z];
				}
			}

			SpawnTextureSplat( pos, bone_name_checksum, size, radius, lifetime, dropdown_length, p_texture_name, trail, dropdown_vertical );
            break;
		}
		
		// @script | Skeleton_SpawnCompositeObject | creates a rigidbody at the specified bone's position and orientation
		// finish autoducking
		case CRCC(0xf99f9faf, "Skeleton_SpawnCompositeObject"):
		{
			uint32 bone_name;
			pParams->GetChecksum(CRCD(0xcab94088, "bone"), &bone_name, Script::ASSERT);
			
			Mth::Vector offset;
			offset.Set();
			pParams->GetVector(CRCD(0xa6f5352f, "offset"), &offset);
			
			Script::CArray* p_component_array;
			pParams->GetArray(CRCD(0x11b70a02, "components"), &p_component_array, Script::ASSERT);
			
			Script::CStruct* p_params;
			pParams->GetStructure(CRCD(0x7031f10c, "params"), &p_params, Script::ASSERT);
			
			Mth::Vector relative_vel;
			pParams->GetVector(CRCD(0xc4c809e, "vel"), &relative_vel, Script::ASSERT);
			
			Mth::Vector relative_rotvel;
			pParams->GetVector(CRCD(0xfb1a83b2, "rotvel"), &relative_rotvel, Script::ASSERT);
			
			float object_vel_factor = 1.0f;
			pParams->GetFloat(CRCD(0x8c6a6e08, "object_vel_factor"), &object_vel_factor);
			
			SpawnCompositeObject(bone_name, p_component_array, p_params, object_vel_factor, relative_vel, relative_rotvel, offset);
			break;
		}

        default:
            return CBaseComponent::MF_NOT_EXECUTED;
    }

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkeletonComponent::GetBonePosition( uint32 boneName, Mth::Vector* pBonePos )
{
	Dbg_Assert(mp_skeleton);
	return mp_skeleton->GetBonePosition(boneName, pBonePos);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkeletonComponent::GetBoneWorldPosition( uint32 boneName, Mth::Vector* pBonePos )
{
	Dbg_Assert(mp_skeleton);
	
	if ( !mp_skeleton->GetBonePosition( boneName, pBonePos ) )
	{
		pBonePos->Set( 0.0f, 0.0f, 0.0f );
	}

	// now tranform it by the position of the object
    Mth::Matrix rootMatrix;
    rootMatrix = GetObject()->GetMatrix();
    rootMatrix[Mth::POS] = GetObject()->GetPos();
	rootMatrix[Mth::POS][W] = 1.0f;
	Mth::Vector vec = rootMatrix.Transform(* pBonePos );
	(*pBonePos) = vec;

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkeletonComponent::SpawnTextureSplat( Mth::Vector& pos, uint32 bone_name_checksum, float size, float radius, float lifetime, float dropdown_length, const char *p_texture_name, bool trail, bool dropdown_vertical )
{
	Mth::Vector bone_pos;
	if (!bone_name_checksum)
	{
		bone_pos = pos;
	}
	else
	{
		GetBoneWorldPosition( bone_name_checksum, &bone_pos );
	}

	// Convert dropdown_length from feet to inches.
	dropdown_length *= 12.0f;
	
	// Calculate the end position by dropping down 3 feet along the skater's up vector, or 'world up',
	// depending on the dropdown_vertical flag.
	Mth::Vector end;
	if( dropdown_vertical )
	{
		end = bone_pos - ( Mth::Vector( 0.0f, 1.0f, 0.0f ) * dropdown_length );
	}
	else
	{
		end = bone_pos - ( GetObject()->GetMatrix().GetUp() * dropdown_length );
	}
	
	// In some cases, the bone can be beneath geometry (trucks during a revert), so move the start position up slightly.
	bone_pos += ( GetObject()->GetMatrix().GetUp() * 12.0f );
	
	// Calculate radial offset for end position.
	if( radius > 0.0f )
	{
		float x_offset = -radius + (( radius * 2.0f ) * (float)rand() / RAND_MAX );
		float z_offset = -radius + (( radius * 2.0f ) * (float)rand() / RAND_MAX );

		end += ( GetObject()->GetMatrix().GetRight() * x_offset );
		end += ( GetObject()->GetMatrix().GetAt() * z_offset );
	}
	
	Nx::TextureSplat( bone_pos, end, size, lifetime, p_texture_name, trail ? bone_name_checksum : 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkeletonComponent::SpawnCompositeObject ( uint32 bone_name, Script::CArray* pArray, Script::CStruct* pParams,
	float object_vel_factor, Mth::Vector& relative_vel, Mth::Vector& relative_rotvel, Mth::Vector& offset )
{
	Dbg_Assert(pArray);
	Dbg_Assert(pParams);
	
	CSkeletonComponent* p_skeleton_component = GetSkeletonComponentFromObject(GetObject());
	Dbg_Assert(p_skeleton_component);

	// get the bone's matrix
	Mth::Matrix bone_matrix;
	p_skeleton_component->GetSkeleton()->GetBoneMatrix(bone_name, &bone_matrix);
	
	// build the object's matrix
	Mth::Matrix object_matrix = GetObject()->GetMatrix();
	object_matrix[W] = GetObject()->GetPos();
	object_matrix[X][W] = 0.0f;
	object_matrix[Y][W] = 0.0f;
	object_matrix[Z][W] = 0.0f;
	
	// NOTE: gludge to fix skater board model's coordinate system to match board bone's system
	Mth::Vector temp;
	temp = bone_matrix[Y];
	bone_matrix[Y] = bone_matrix[Z];
	bone_matrix[Z] = -temp;
	offset[W] = 0.0f;
	bone_matrix[W] += offset;
	
	// map the bone to world space
	bone_matrix *= object_matrix;
	
	// write the bone state into the composite object parameters
	
	pParams->AddVector(CRCD(0x7f261953, "pos"), bone_matrix[W]);
	
	Mth::Quat orientation(bone_matrix);
	if (orientation.GetScalar() > 0.0f)
	{
		pParams->AddVector(CRCD(0xc97f3aa9, "orientation"), orientation.GetVector());
	}
	else
	{
		pParams->AddVector(CRCD(0xc97f3aa9, "orientation"), -orientation.GetVector());
	}
	
	// setup rigidbody's initial velocity
	Mth::Vector vel = object_vel_factor * GetObject()->GetVel() + GetObject()->GetMatrix().Rotate(relative_vel);
	pParams->AddVector(CRCD(0xc4c809e, "vel"), vel);
	
	// setup rigidbody's initial rotational velocity
	Mth::Vector rotvel = GetObject()->GetMatrix().Rotate(relative_rotvel);
	pParams->AddVector(CRCD(0xfb1a83b2, "rotvel"), rotvel);
	
	CCompositeObjectManager::Instance()->CreateCompositeObjectFromNode(pArray, pParams);
}

}



================================================
FILE: Code/Gel/Components/skeletoncomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SkeletonComponent.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/17/2002
//****************************************************************************

#ifndef __COMPONENTS_SKELETONCOMPONENT_H__
#define __COMPONENTS_SKELETONCOMPONENT_H__

#include 
#include 

#include 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// Just thinking about it - a generic way of accessing the component				 
#define		CRC_SKELETON CRCD(0x222756d5,"Skeleton")
#define		GetSkeletonComponent() ((Obj::CSkeletonComponent*)GetComponent(CRC_SKELETON))
#define		GetSkeletonComponentFromObject(pObj) ((Obj::CSkeletonComponent*)(pObj)->GetComponent(CRC_SKELETON))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
namespace Gfx
{
    class CSkeleton;
}

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
class CSkeletonComponent : public CBaseComponent
{
public:
    CSkeletonComponent();
    virtual ~CSkeletonComponent();

public:
    virtual void            Update();
    virtual void            InitFromStructure( Script::CStruct* pParams );
    
    EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	
    static CBaseComponent*	s_create();
    Gfx::CSkeleton*         GetSkeleton();

    bool					GetBonePosition( uint32 boneName, Mth::Vector* pBonePos );
	bool					GetBoneWorldPosition( uint32 boneName, Mth::Vector* pBonePos );
	
	void					SpawnTextureSplat( Mth::Vector& pos, uint32 bone_name_checksum, float size, float radius, float lifetime, float dropdown_length, const char *p_texture_name, bool trail, bool dropdown_vertical );
	void					SpawnCompositeObject ( uint32 bone_name, Script::CArray* pArray, Script::CStruct* pParams, float object_vel_factor, Mth::Vector& relative_vel, Mth::Vector& relative_rotvel, Mth::Vector& offset );

protected:
    void                    init_skeleton( uint32 skeleton_name );
	
protected:
    Gfx::CSkeleton*         mp_skeleton;
};
	
}

#endif


================================================
FILE: Code/Gel/Components/soundcomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SoundComponent.cpp
//* OWNER:          Mick West
//* CREATION DATE:  10/17/2002
//****************************************************************************

// start autoduck documentation
// @DOC soundcomponent.cpp
// @module soundcomponent | None
// @subindex Scripting Database
// @index script | soundcomponent

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

// TODO:  Refactor this - 
#include 
#include 
#include 


namespace Obj
{


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent *	CSoundComponent::s_create()
{
	return static_cast(new CSoundComponent);	
}
    
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSoundComponent::CSoundComponent() : CBaseComponent()
{
	SetType(CRC_SOUND);
	mp_emitter = NULL;
	mp_proxim_node = NULL;
	m_pos.Set();
	m_old_pos.Set();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSoundComponent::~CSoundComponent()
{
	// Tell the sound manager that the sond component is being removed to clean up
	// any sound effects that are attached to this
	Sfx::CSfxManager::Instance()->ObjectBeingRemoved( this );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSoundComponent::InitFromStructure( Script::CStruct* pParams )
{
	uint32 proxim_node_checksum;

	if (pParams->GetChecksum("ProximNode", &proxim_node_checksum))
	{
		//Dbg_Message("SoundComponent: Looking for ProximNode %s", Script::FindChecksumName(proxim_node_checksum));
		mp_proxim_node = CProximManager::sGetNode(proxim_node_checksum);
		//if (mp_proxim_node)
		//{
		//	Dbg_Message("Found ProximNode %s", Script::FindChecksumName(mp_proxim_node->m_name));
		//}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSoundComponent::Update()
{
	
	// For tracking the position of the sound, then 
	// we need to update the old position and the current position
	m_old_pos = m_pos;
	m_pos = GetObject()->m_pos;
	
	
	UpdateMutedSounds();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSoundComponent::Teleport()
{
	// Garrett: Note that this doesn't update currently playing sounds.  That won't happen
	// until the next sound update iteration.
	Update();

	//Dbg_Message("SoundComponent: Teleporting to (%f, %f, %f)", m_pos[X], m_pos[Y], m_pos[Z]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


CBaseComponent::EMemberFunctionResult CSoundComponent::CallMemberFunction( uint32 Checksum, Script::CScriptStructure *pParams, Script::CScript *pScript )
{

	switch (Checksum)
	{
		// @script | Obj_PlaySound | Add your own enums to sfx_*.q.  Up to 8 allowed (0 - 7), 
		// @flag Filename | name of the sound (filename without the quotes) or 
		// Type = [as defined in Obj_SetSound] 
		// @parm name | Type | the type (if this is being called from a car script, for example, 
		// use CARSFX_HONK, or another enum from sfx_car.q (similarly for ped: sfx_ped.q)
		// @parmopt float | vol | 100 | This is the volume of the wav AFTER being modified 
		// by the volume (if specified) in LoadSound 
		// @parmopt float | pitch | 100 | range: 1 to 300 (assuming normal pitch in LoadSound) 
		case 0xafa20e18:  // "Obj_PlaySound"
			PlayScriptedSound( pParams );
			return MF_TRUE;
	
		// @script | Obj_AdjustSound | This function adjusts a looping sound 
		// associated with an object. If no volumeStep is specified, volume is 
		// adjusted immediately, same with pitchStep and pitch.  If volumeStep 
		// or pitchStep are specified, the current volume/pitch is adjusted towards 
		// the target by the step, each frame (or 60 times a second, rather). So 
		// if the current pitch is 100, and you call: Obj_AdjustSound pitch = 40 
		// pitchStep = 1 It will take one second for the pitch to go from 100 to 40. 
		// The percent values (pitchPercent and volumePercent) modify the overall 
		// pitch or volume after taking into consideration the pitch specified globally 
		// during the LoadSound call, then specified per this object on the Obj_
		// PlaySound call (see example).
		// @flag Filename | name of the sound you want to adjust (no quotes)
		// @parm float | VolumePercent | Percentage to adjust the volume
		// @parm float | PitchPercent | Percentage to adjust the pitch
		// @parmopt float | VolumeStep | 0 |  Amount of time in seconds it takes to go from the current volume to new volume.
		// @parmopt float | PitchStep | 0 | Amount of time in seconds it takes to go from the current pith to new pitch.
		case 0x3e5e8023: // "Obj_AdjustSound"
			AdjustObjectSound( pParams, pScript );
			return MF_TRUE;
		
		// @script | Obj_StopSound | Obj_StopSound doesn't take a type = parameter... 
		// it is used only for stopping a looping sound by checksum. If you don't 
		// specify the name of the sound, all sounds that were loaded with PosUpdate 
		// or PosUpdateWithDoppler parameters and are associated with the object 
		// calling this will be stopped.
		// @flag Filename | name of the sound you want to stop (no quotes)
		case 0x4e4132d6: // "Obj_StopSound"
		{
			uint32 soundChecksum = 0;
			pParams->GetChecksum( NONAME, &soundChecksum );
			Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();
			sfx_manager->StopObjectSound( this, soundChecksum );
			// if it's temporarily out of range, we gotta stop it too!
			int i;
			for ( i = 0; i < MAX_NUM_LOOPING_SOUNDS_PER_OBJECT; i++ )
			{
				if ( mSoundInfo[ i ].checksum == soundChecksum )
				{
					// since a sound checksum of 0 is invalid, this turns off the update:
					mSoundInfo[ i ].checksum = 0;
				}
			}
			return MF_TRUE;
		}
		
		// @script | Obj_SetSound | Add your own enums to sfx_*.q.  Up to 8 allowed (0 - 7), 
		// can be increased easily.  Use Obj_PlaySound (with the optional Type = * parameter 
		// instead of the filename) to trigger one of these sounds to be played.  Don't 
		// trigger a looping sound!  The engine loop on the car needs to be set up per car 
		// using this function, but the update happens in the game engine (pun regretted because 
		// it's confusing).  Don't forget to load the sounds specified by 'filename'.
		// @flag Filename | Name of the sound, no quotes
		// @parm name | Type | the type (if this is being called from a car script, for example, 
		// use CARSFX_HONK, or another enum from sfx_car.q (similarly for ped: sfx_ped.q)
		case 0xe8347171:  // "Obj_SetSound"
			SetSound( pParams );
			return MF_TRUE;
	}
	return MF_NOT_EXECUTED;
}

void CSoundComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSoundComponent::GetDebugInfo"));
	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
	
	Script::CArray *p_array=new Script::CArray;
	p_array->SetSizeAndType(MAX_NUM_SOUNDFX_CHECKSUMS,ESYMBOLTYPE_NAME);
	for (int i=0; iSetChecksum(i,mSoundFXChecksums[i]);
	}
	p_info->AddArrayPointer("mSoundFXChecksums",p_array);

	p_array=new Script::CArray;
	p_array->SetSizeAndType(MAX_NUM_LOOPING_SOUNDS_PER_OBJECT,ESYMBOLTYPE_STRUCTURE);
	for (int i=0; iSetStructure(i,p_struct);
	}	
	p_info->AddArrayPointer("mSoundInfo",p_array);
#endif				 
}

#define CHECKSUM_TYPE	0x7321a8d6  // 'type'

// Set individual soundfx for objects...
// Designers can also trigger these sounds to be played by using the Obj_PlaySound
// script command, using type =  (instead of using the checksum) to define the sound.
void CSoundComponent::SetSound( Script::CScriptStructure *pParams )
{
	
	int type = -1;
	uint32 soundChecksum = 0;
	pParams->GetInteger( CHECKSUM_TYPE, &type );
	pParams->GetChecksum( NONAME, &soundChecksum );
	if ( type == -1 )
	{
		Dbg_MsgAssert( 0,( "Script command Obj_SetSound used without specifying sound type." ));
		return;
	}
	if ( !soundChecksum )
	{
		Dbg_MsgAssert( 0,( "Script command Obj_SetSound used without specifying name of sound." ));
		return;
	}
	if ( ( type > MAX_NUM_SOUNDFX_CHECKSUMS ) || ( type == -1 ) )
	{
		Dbg_MsgAssert( 0,( "Matt needs to increase MAX_NUM_SOUND_CHECKSUMS (or bad type %d sent in...)", type ));
		return;
	}
	mSoundFXChecksums[ type ] = soundChecksum;
} // end of SetSound( )


void CSoundComponent::PlayScriptedSound( Script::CScriptStructure *pParams )
{
	uint32 soundChecksum = 0;
	uint32 emitterChecksum = 0;
	uint32 dropoffFuncChecksum;
	float volume = 100.0f;
	float pitch = 100.0f;
	float dropoffDist = 0.0f;
	EDropoffFunc dropoffFunction = DROPOFF_FUNC_STANDARD;
	bool noPosUpdate = false;
    
	pParams->GetChecksum( NONAME, &soundChecksum );
	pParams->GetFloat( 0xf6a36814, &volume ); // "vol"
	pParams->GetFloat( 0xd8604126, &pitch ); // "pitch"
	
	if (pParams->ContainsComponentNamed(CRCD(0x9e497fc6, "Percent")))
	{
		float minVol = 0.0f;
		float maxVol = 100.0f;
		float minPitch = 100.0f;
		float maxPitch = 100.0f;
		
		pParams->GetFloat(CRCD(0x0693daaf, "MaxVol"), &maxVol);
		pParams->GetFloat(CRCD(0x4391992d, "MinVol"), &minVol);
		pParams->GetFloat(CRCD(0xfa3e14c5, "MaxPitch"), &maxPitch);
		pParams->GetFloat(CRCD(0x1c5ebb24, "MinPitch"), &minPitch);
		
		float percent;
		pParams->GetFloat(CRCD(0x9e497fc6, "Percent"), &percent, Script::ASSERT);
		percent *= (1.0f / 100.0f);
		
		volume = Mth::Lerp(minVol, maxVol, percent);
		pitch = Mth::Lerp(minPitch, maxPitch, percent);
	}
	
	if ( pParams->GetFloat( 0xff2020ec, &dropoffDist ) ) // "dropoff"
	{
		dropoffDist = FEET_TO_INCHES( dropoffDist );
	}
	if ( pParams->GetChecksum( 0xc6ac50a, &dropoffFuncChecksum ) ) // "dropoff_function"
	{
		dropoffFunction = Sfx::GetDropoffFunctionFromChecksum( dropoffFuncChecksum );
	}
	if ( pParams->ContainsFlag( 0xbb39837f ) ) // "NoPosUpdate"
	{
		noPosUpdate = true;
	}
	if (pParams->GetChecksum( 0x8a7132ce, &emitterChecksum ) ) // "emitter"
	{
		mp_emitter = CEmitterManager::sGetEmitter(emitterChecksum);
	}
	else
	{
		mp_emitter = NULL;
	}

	if ( !soundChecksum )
	{
		// If the checksum isn't specified, the designer may be requesting
		// to trigger a sound that can be individually specified per object
		// using the script command Obj_SetSound...
		// If that is the case, there will be a 'type = X' included, where
		// X is an enum such as CARSFX_HONK.
		int type = -1;
		pParams->GetInteger( "Type", &type );
		if ( type == -1 )
		{
			Dbg_MsgAssert( 0,( "Obj_PlaySound requires the name of the sound or a type specified (as in type = CARSFX_HONK)" ));
			return;
		}
		if ( type > MAX_NUM_SOUNDFX_CHECKSUMS )
		{
			Dbg_MsgAssert( 0,( "Bad type (%d) sent to Obj_PlaySound", type ));
			return;
		}
		soundChecksum = mSoundFXChecksums[ type ];
		if ( !soundChecksum )
		{
			Dbg_Message( "Object trying to play sound type %d, which hasn't been defined.", type );
			return;
		}
	}
	
	// network stuff for higher level objects (need to add the update flag to this...)
	BroadcastScriptedSound( soundChecksum, volume, pitch );
	
	PlaySound_VolumeAndPan( soundChecksum, volume, pitch, dropoffDist, dropoffFunction, noPosUpdate );
}// end of PlayScriptedSound( )

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector		CSoundComponent::GetClosestEmitterPos( Gfx::Camera *pCamera )
{
	if (mp_emitter && pCamera)
	{
		return mp_emitter->GetClosestPoint(pCamera->GetPos());
	}
	else
	{
		return m_pos;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector		CSoundComponent::GetClosestOldEmitterPos( Gfx::Camera *pCamera )
{
	if (mp_emitter && pCamera)
	{
		// This doesn't take into consideration the old position
		return mp_emitter->GetClosestPoint(pCamera->GetPos());
	}
	else
	{
		return m_old_pos;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CSoundComponent::GetClosestDropoffPos( Gfx::Camera *pCamera, Mth::Vector & dropoff_pos )
{
	if (pCamera && mp_proxim_node)
	{
		dropoff_pos = mp_proxim_node->GetClosestIntersectionPoint(pCamera->GetPos());
		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ObjectSoundInfo *CSoundComponent::GetLoopingSoundInfo( uint32 soundChecksum )
{
	
	int i;
	for ( i = 0; i < MAX_NUM_LOOPING_SOUNDS_PER_OBJECT; i++ )
	{
		if ( mSoundInfo[ i ].checksum == soundChecksum )
			return &mSoundInfo[ i ];
	}
	return ( NULL );
}

void CSoundComponent::UpdateMutedSounds( void )
{
	
	int i;
	Tmr::Time gameTime;
	gameTime = Tmr::GetTime();

	for ( i = 0; i < MAX_NUM_LOOPING_SOUNDS_PER_OBJECT; i++ )
	{
		if ( mSoundInfo[ i ].checksum )
		{
			ObjectSoundInfo *pInfo = &mSoundInfo[ i ];
			UpdateObjectSound( pInfo );
			if ( pInfo->timeForNextDistCheck <= gameTime )
			{
//				Dbg_Message( "checking if we need to turn %s on", Script::FindChecksumName( mSoundInfo[ i ].checksum ) );
				// see if we're dropoffDist*1.33 from the nearest camera...
				// if so, start the sound up again!
				float dist;
				Gfx::Camera *p_camera = Nx::CViewportManager::sGetClosestCamera( m_pos, &dist );
				if ( p_camera && ( dist < ( pInfo->dropoffDist + DIST_FROM_DROPOFF_AT_WHICH_TO_START_SOUND ) ) )
				{
					// start the sound playing again on this object:
					if ( PlaySound_VolumeAndPan( pInfo->checksum, pInfo->origVolume, pInfo->origPitch, pInfo->dropoffDist, pInfo->dropoffFunction ) )
					{
						//Dbg_Message( "sound %s auto on object %x time %d", Script::FindChecksumName( pInfo->checksum ), ( int ) this, gameTime );
						ObjectSoundInfo *tempInfo;
						Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();
						tempInfo = sfx_manager->GetObjectSoundProperties( this, pInfo->checksum );
						// and set up the targets to match the adjustments the scripts made
						// while the sound was turned off:
						tempInfo->targetVolume = pInfo->targetVolume;
						tempInfo->targetPitch = pInfo->targetPitch;
						tempInfo->deltaPitch = pInfo->deltaPitch;
                        // give it a ramp up in volume, so it doesn't frickin'jump in our faces:
						tempInfo->currentVolume = 0.0f;
						tempInfo->deltaVolume = tempInfo->targetVolume / 100.0f;
						// don't forget to clear the slot to avoid updating it:
						pInfo->checksum = 0;
					}
				}
				else
				{
					// vary the time between distance checks, depending on how far
					// away the object is right now...
					pInfo->timeForNextDistCheck = gameTime;
					pInfo->timeForNextDistCheck += ( int )( ( 1000.0f * ( dist - pInfo->dropoffDist ) ) / 600.0f );
					pInfo->timeForNextDistCheck += 333;  // plus a third of a second...
				}
			}
		}
	}
}

void CSoundComponent::UpdateObjectSound( ObjectSoundInfo *pInfo )
{
	
	// adjust pitch and volume, if necessary:
	if ( pInfo->targetPitch != pInfo->currentPitch )
	{
		pInfo->currentPitch = Mth::FRunFilter( pInfo->targetPitch, pInfo->currentPitch, pInfo->deltaPitch * Tmr::FrameRatio( ) );
	}
	if ( pInfo->targetVolume != pInfo->currentVolume )
	{
		pInfo->currentVolume = Mth::FRunFilter( pInfo->targetVolume, pInfo->currentVolume, pInfo->deltaVolume * Tmr::FrameRatio( )  );
	}
}

void CSoundComponent::AdjustObjectSound( Script::CScriptStructure *pParams, Script::CScript *pScript )
{
	
#ifndef __PLAT_NGC__
	ObjectSoundInfo *pInfo;
	float volumePercent = 100.0f;
	float pitchPercent = 100.0f;
	uint32 soundChecksum;

	if ( !pParams->GetChecksum( NONAME, &soundChecksum ) )
	{
		Dbg_MsgAssert( 0,( "\n%s\nSound checksum required on Obj_AdjustSound", pScript->GetScriptInfo( ) ));
	}
	Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();
	pInfo = sfx_manager->GetObjectSoundProperties( this, soundChecksum );
	if ( !pInfo )
	{
		//Dbg_MsgAssert( 0,( "\n%s\nAttempting to adjust object sound that isn't playing.", pScript->GetScriptInfo( ) ));
		
		// Object went out of range, and the sound was stopped.
		// Store the current properties, so that when the sound
		// is started again the pitch/volume are correct!
		
		// NOTE:  If keeping the pitch/volume updated accurately by
		// adjusting by deltaPitch and deltaVolume causes noticable
		// slowdown, just set the pitch and vol immediately in this
		// function if the pInfo comes from the next line:
		pInfo = GetLoopingSoundInfo( soundChecksum );

#		ifdef __PLAT_XBOX__
		if( !pInfo )
		{
			return;
		}
#		endif

		if (!pInfo)
		{
			//Dbg_MsgAssert( 0,( "\n%s\nCan't adjust a sound (%s) that was never played on an object.",  pScript->GetScriptInfo( ), Script::FindChecksumName(soundChecksum)  ));
			Dbg_Message("\n%s\nCan't adjust a sound (%s) that was never played on an object.",  pScript->GetScriptInfo( ), Script::FindChecksumName(soundChecksum));
			return;
		}
	}

	if ( pParams->GetFloat( 0x330f3415, &volumePercent ) ) // volumePercent
	{
		pInfo->targetVolume = PERCENT( pInfo->origVolume, volumePercent );
		if ( !pParams->GetFloat( 0xdaa9a3c2, &pInfo->deltaVolume ) )// volumeStep
		{
			// adjust immediately:
			pInfo->currentVolume = pInfo->targetVolume;
		}
		else
		{
			Dbg_MsgAssert( pInfo->deltaVolume,( "\n%s\nCan't have zero delta volume.", pScript->GetScriptInfo( ) ));
		}
	}
	if ( pParams->GetFloat( 0xee616c13, &pitchPercent ) )	// pitchPercent
	{
		pInfo->targetPitch = PERCENT( pInfo->origPitch, pitchPercent );
		if ( !pParams->GetFloat( 0x7b090774, &pInfo->deltaPitch ) ) // pitchStep
		{
			// adjust immediately:
			pInfo->currentPitch = pInfo->targetPitch;
		}
		else
		{
			Dbg_MsgAssert( pInfo->deltaPitch,( "\n%s\nCan't have zero delta pitch.", pScript->GetScriptInfo( ) ));
		}
	}
#endif		// __PLAT_NGC__
}

void CSoundComponent::SoundOutOfRange( ObjectSoundInfo *pInfo, Tmr::Time gameTime )
{
	
	int i;
	for ( i = 0; i < MAX_NUM_LOOPING_SOUNDS_PER_OBJECT; i++ )
	{
		if ( !mSoundInfo[ i ].checksum  )
		{
			mSoundInfo[ i ] = *pInfo;
			mSoundInfo[ i ].timeForNextDistCheck = gameTime + 2000;
			return;
		}
	}
	Dbg_MsgAssert( 0,( "No empty looping sound slots for (%s) on object... (Limit %d)",
					Script::FindChecksumName( pInfo->checksum ) ,MAX_NUM_LOOPING_SOUNDS_PER_OBJECT ));
}


// Will play a sound, setting the volume and pan according to world position
// from the closest camera(s).
int CSoundComponent::PlaySound_VolumeAndPan( uint32 soundChecksum, float volume, float pitch, float dropoffDist,
											 EDropoffFunc dropoffFunction, bool noPosUpdate )
{
	
	
	Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();
	Sfx::SoundUpdateInfo soundUpdateInfo;
	soundUpdateInfo.volume = volume;
	soundUpdateInfo.pitch = pitch;
	soundUpdateInfo.dropoffDist = dropoffDist;
	soundUpdateInfo.dropoffFunction = dropoffFunction;
	#if 0
	// TODO:  Replay code handling sound components
	Replay::WritePositionalSoundEffect(m_id,soundChecksum,volume,pitch,dropoffDist);
	#endif
	return ( sfx_manager->PlaySoundWithPos( soundChecksum, &soundUpdateInfo, this, noPosUpdate ) );
} // end of PlaySound_VolumeAndPan( )

	
}


================================================
FILE: Code/Gel/Components/soundcomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SoundComponent.h
//* OWNER:          Mick West
//* CREATION DATE:  10/17/2002
//****************************************************************************

#ifndef __COMPONENTS_SOUNDCOMPONENT_H__
#define __COMPONENTS_SOUNDCOMPONENT_H__

#include 
#include 
#include 

#include 
#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_SOUND CRCD(0x7713c7b,"Sound")
#define		GetSoundComponent() ((Obj::CSoundComponent*)GetComponent(CRC_SOUND))
#define		GetSoundComponentFromObject(pObj) ((Obj::CSoundComponent*)(pObj)->GetComponent(CRC_SOUND))

//#define		GetSounds() ((CSoundComponent*)GetComponent(CRC_SOUND))

#define NUM_GENERAL_SOUNDS_PER_OBJECT	( 10 )


namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	// Forward declarations
	class CEmitterObject;
	class CProximNode;


class CSoundComponent : public CBaseComponent
{
public:
    CSoundComponent();
    virtual         ~CSoundComponent();
    virtual void    Update();
	virtual void    Teleport();
    virtual void    InitFromStructure( Script::CStruct* pParams );
	static CBaseComponent *	s_create();


	CBaseComponent::EMemberFunctionResult CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript );

	virtual void GetDebugInfo(Script::CStruct *p_info);
							
// Big old pile of crap....

public:	
	void 			SetSound( Script::CStruct *pParams );
	void			SoundOutOfRange( ObjectSoundInfo *pInfo, Tmr::Time gameTime );
	void			UpdateObjectSound( ObjectSoundInfo *pInfo );
	int 			PlaySound_VolumeAndPan( uint32 soundChecksum, float volume = 100.0f, float pitch = 100.0,
											float dropoffDist = 0.0f, EDropoffFunc dropoffFunction = DROPOFF_FUNC_STANDARD,
											bool noPosUpdate = false );
	virtual void 	BroadcastScriptedSound( uint32 soundChecksum, float volume, float pitch ) { };
	void			PlayScriptedSound( Script::CStruct *pParams );

	Mth::Vector		GetPosition() const { return m_pos; }
	Mth::Vector		GetClosestEmitterPos( Gfx::Camera *pCamera );
	Mth::Vector		GetClosestOldEmitterPos( Gfx::Camera *pCamera );

	bool			HasProximNode() const { return mp_proxim_node != NULL; }
	void			ClearProximNode() { mp_proxim_node = NULL; }								// Needed for early proxim node cleanup
	bool			GetClosestDropoffPos( Gfx::Camera *pCamera, Mth::Vector & dropoff_pos );	// returns true if position defined

	// If the sound is muted due to the object being far from the nearest camera,
	// update the pitch and volume so that when it comes back onto the screen it
	// is how the designer would expect it ( called from GameObj_Update )
	void			UpdateMutedSounds( void );

	#define		MAX_NUM_SOUNDFX_CHECKSUMS	8
	uint32		mSoundFXChecksums[ MAX_NUM_SOUNDFX_CHECKSUMS ];

	void AdjustObjectSound( Script::CStruct *pParams, Script::CScript *pScript );
	ObjectSoundInfo *GetLoopingSoundInfo( uint32 soundChecksum );
	
	#define		MAX_NUM_LOOPING_SOUNDS_PER_OBJECT	2
	ObjectSoundInfo	mSoundInfo[ MAX_NUM_LOOPING_SOUNDS_PER_OBJECT ];
							
protected:
							   
	Mth::Vector	m_pos,m_old_pos;
	CEmitterObject *mp_emitter;
	CProximNode *	mp_proxim_node;

};

}

#endif


================================================
FILE: Code/Gel/Components/specialitemcomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SpecialItemComponent.cpp
//* OWNER:          ???
//* CREATION DATE:  ???
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CSpecialItemComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSpecialItemComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CSpecialItemComponent::CSpecialItemComponent() : CBaseComponent()
{
	SetType( CRC_SPECIALITEM );
	
	for ( int i = 0; i < vMAX_SPECIAL_ITEMS; i++ )
	{
		mp_special_items[i] = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSpecialItemComponent::~CSpecialItemComponent()
{	
	// Cleanup the special items
	DestroyAllSpecialItems();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CSpecialItemComponent::InitFromStructure( Script::CStruct* pParams )
{
	// ** Add code to parse the structure, and initialize the component

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CSpecialItemComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CSpecialItemComponent::Update()
{
	// Doing nothing, so tell code to do nothing next time around
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CSpecialItemComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | CreateSpecialItem | attaches a new special item to the skater in the specified slot
		// @parm int | index | Index of special item to create
		// @parm structure | params | Special item creation data (should look like an entry in the node array)
		// @parmopt name | bone | | The bone to which the special item is attached; otherwise, it will be attached to the root
		// @parmopt vector | offset | (0, 0, 0) | An additional offset to the specified bone (or root) applied to the special item's position
		case 0x827c6bd1:  // CreateSpecialItem
			{
				int index;
				uint32 bone = 0;
				pParams->GetInteger( CRCD(0x7f8c98fe,"index"), &index, true );
				pParams->GetChecksum( "bone", &bone );

				uint32 checksum;
				pParams->GetChecksum( "params", &checksum, Script::ASSERT );
				Script::CStruct* pSpecialItemParams;
				pSpecialItemParams = Script::GetStructure( checksum, Script::ASSERT );
								
				CCompositeObject* pMovingObject = CreateSpecialItem( index, pSpecialItemParams );
				
				// special items need a lock object to work...
				Script::CStruct* pLockParams = new Script::CStruct;
				pLockParams->AppendStructure( pParams ); 			// pass along any bone or offset parameters...
				pLockParams->AddChecksum( "id", GetObject()->GetID() );				  	
				Obj::CLockObjComponent* pLockObjComponent = GetLockObjComponentFromObject( pMovingObject );
				Dbg_MsgAssert( pLockObjComponent, ( "No lock obj component" ) );
				pLockObjComponent->InitFromStructure( pLockParams );				 		
				delete pLockParams;

				GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
				if( gamenet_man->InNetGame())
				{
					Net::Client* client;
					GameNet::MsgSpecialItem msg;
					Net::MsgDesc msg_desc;
	
                    client = gamenet_man->GetClient( 0 );
					Dbg_Assert( client );
	
					msg.m_ObjId = GetObject()->GetID();
					msg.m_Params = checksum;
					msg.m_Index = index;
					msg.m_Bone = bone;
					
					msg_desc.m_Data = &msg;
					msg_desc.m_Length = sizeof( GameNet::MsgSpecialItem );
					msg_desc.m_Id = GameNet::MSG_ID_CREATE_SPECIAL_ITEM;
					client->EnqueueMessageToServer( &msg_desc );
				}
			}
			break;
		
		// @script | DestroySpecialItem | Destroys a special item attached to the skater, if it exists
		// @parm int | index | Index of special item to destroy
		case 0x742188fa:  // DestroySpecialItem
			{
				int index;
				pParams->GetInteger( CRCD(0x7f8c98fe,"index"), &index, true );

				this->DestroySpecialItem( index );
				
				GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
				if( gamenet_man->InNetGame())
				{
					Net::Client* client;
					GameNet::MsgSpecialItem msg;
					Net::MsgDesc msg_desc;
	
                    client = gamenet_man->GetClient( 0 );
					Dbg_Assert( client );
	
					msg.m_ObjId = GetObject()->GetID();
					msg.m_Index = index;
					
					msg_desc.m_Data = &msg;
					msg_desc.m_Length = sizeof( GameNet::MsgSpecialItem );
					msg_desc.m_Id = GameNet::MSG_ID_DESTROY_SPECIAL_ITEM;
					client->EnqueueMessageToServer( &msg_desc );
				}
			}
			break;
		
		// @script | DestroyAllSpecialItems | Destroys all special items currently attached to the skater 
		case 0xa1baa25e:  // DestroyAllSpecialItems
			{
				this->DestroyAllSpecialItems();
			}
			break;
		
		// @script | SpecialItemExists | Returns whether a special item exists
		// @parm int | index | Index of special item to check
		case 0xce887492:  // SpecialItemExists
			{
				int index;
				if ( pParams->GetInteger( CRCD(0x7f8c98fe,"index"), &index, false ) )
				{
					Dbg_MsgAssert( index >= 0 && index < vMAX_SPECIAL_ITEMS, ( "Special item index %d must be between 0 and %d", index, vMAX_SPECIAL_ITEMS ) );
					return ( mp_special_items[index] ) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
				}
				else
				{
					for ( int i = 0; i < vMAX_SPECIAL_ITEMS; i++ )
					{
						if ( mp_special_items[i] )
						{
							return CBaseComponent::MF_TRUE;
						}
					}
					return CBaseComponent::MF_FALSE;
				}
			}
			break;

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSpecialItemComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSpecialItemComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*	Example:
	p_info->AddInteger("m_never_suspend",m_never_suspend);
	p_info->AddFloat("m_suspend_distance",m_suspend_distance);
	*/
	
// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CCompositeObject* CSpecialItemComponent::CreateSpecialItem( int index, Script::CStruct* pNodeData )
{
	Dbg_MsgAssert( index >= 0 && index < vMAX_SPECIAL_ITEMS, ( "Special item index %d must be between 0 and %d", index, vMAX_SPECIAL_ITEMS ) );
	Dbg_MsgAssert( pNodeData, ( "Missing parameter" ) );

	if ( mp_special_items[index] )
	{
		// TODO:  Maybe this should just be a warning?
//		Dbg_MsgAssert( 0, ( "Special item index %d not empty", index ) );
		DestroySpecialItem( index );
	}

//	Dbg_Message( "Adding special item to index %d", index );
	
	// eventually, we'll want to create a generic composite object
	// (maybe by running a script)
	Mem::PushMemProfile("Game Objects");
	Obj::CCompositeObject* pGameObj = Obj::CreateGameObj( Mdl::Skate::Instance()->GetObjectManager(), pNodeData );
	Dbg_Assert( pGameObj );
	Mem::PopMemProfile();

	// GJ:  This is a special-case function that's used for
	// replays...  I'm not bothering to move it over to the
	// CCompositeObject stuff, because we've already made the
	// decision not to make the replay backwards-compatible.
//	pGameObj->FlagAsSpecialItem();
	
	mp_special_items[index] = pGameObj;

	// add the skater's id to the object's tags
	// so that the object knows which skater it's tied to
	Script::CStruct* pTempStructure;
	pTempStructure = new Script::CStruct;
	pTempStructure->AddChecksum( "parentId", GetObject()->GetID() );
	uint32 cleanupScript;
	if ( pNodeData->GetChecksum( CRCD(0x40764820,"CleanupScript"), &cleanupScript ) )
	{
		pTempStructure->AddChecksum( CRCD(0x40764820,"CleanupScript"), cleanupScript );
	}
	mp_special_items[index]->SetTagsFromScript( pTempStructure );
	delete pTempStructure;

	// in case there's any scaling applied to the model,
	// apply that to the special item's model as well...
	Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( GetObject() );
	Nx::CModel* pModel = NULL;
	if ( pModelComponent )
	{
		pModel = pModelComponent->GetModel();
	}
	
	Obj::CModelComponent* pGameObjModelComponent = GetModelComponentFromObject( pGameObj );
	Nx::CModel* pGameObjModel = NULL;
	if ( pGameObjModelComponent )
	{
		pGameObjModel = pGameObjModelComponent->GetModel();
	}

	// (Mick) ... but only if it has a model (might be just collision)
	if ( pModel && pGameObjModel )		
	{
		Mth::Vector scaleVec = pModel->GetScale();
		pGameObjModel->SetScale( scaleVec );		// (Mick) - see check above
	}
	
	return pGameObj;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSpecialItemComponent::DestroySpecialItem( int index )
{
	Dbg_MsgAssert( index >= 0 && index < vMAX_SPECIAL_ITEMS, ( "Special item index %d must be between 0 and %d", index, vMAX_SPECIAL_ITEMS ) );
	
	if ( mp_special_items[index] )
	{
		// run the cleanup script if it exists...
		Script::CStruct* pStruct = new Script::CStruct;
		mp_special_items[index]->CopyTagsToScriptStruct(pStruct);
		uint32 cleanupScript;
		if ( pStruct->GetChecksum( CRCD(0x40764820,"CleanupScript"), &cleanupScript, Script::NO_ASSERT ) )
		{
			// run a cleanup script if it exists
			Script::RunScript( cleanupScript, NULL, mp_special_items[index] );
		}
		delete pStruct;

		// don't actually delete it yet...
		// instead, just mark it as dead, so 
		// that if any objects still refer to
		// it on this frame, it won't crash...
		mp_special_items[index]->MarkAsDead();
		mp_special_items[index] = NULL;
		
		// THPS4:  flushes dead objects so that we can
		// recreate it on the same frame
		// (theoretically, we should have been
		// able to call "wait 1 gameframe",
		// but it turns out that CScript::Update()
		// can be called more than once per frame,
		// if someone uses the MakeSkaterGoto
		// command)
		// THPS5:  doesn't seem to be a problem any
		// more, perhaps due to the subtle changes in
		// order which object/component logic gets
		// run.  having this line in here does cause
		// some issues now, in that it's possible
		// for an object to indirectly delete itself from
		// its update() function, which is bad.  i've
		// taken this line out and will fix any
		// related bugs as they arise.
//		Mdl::Skate::Instance()->GetObjectManager()->FlushDeadObjects();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSpecialItemComponent::DestroyAllSpecialItems()
{
	// This also gets called when we switch levels...
	// (from DeleteResources)
		
	for ( int index = 0; index < vMAX_SPECIAL_ITEMS; index++ )
	{
		DestroySpecialItem( index );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/specialitemcomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SpecialItemComponent.h
//* OWNER:          gj
//* CREATION DATE:  02/13/03
//****************************************************************************

#ifndef __COMPONENTS_SPECIALITEMCOMPONENT_H__
#define __COMPONENTS_SPECIALITEMCOMPONENT_H__

#include 
#include 

#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_SPECIALITEM CRCD(0xf27a294d,"SpecialItem")
#define		GetSpecialItemComponent() ((Obj::CSpecialItemComponent*)GetComponent(CRC_SPECIALITEM))
#define		GetSpecialItemComponentFromObject(pObj) ((Obj::CSpecialItemComponent*)(pObj)->GetComponent(CRC_SPECIALITEM))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CSpecialItemComponent : public CBaseComponent
{
public:
    CSpecialItemComponent();
    virtual ~CSpecialItemComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

public:
	CCompositeObject*				CreateSpecialItem( int index, Script::CStruct* pNodeData );
	void 							DestroySpecialItem( int index );
	void 							DestroyAllSpecialItems();

protected:
	enum
	{
		vMAX_SPECIAL_ITEMS = 2
	};
	CCompositeObject*				mp_special_items[vMAX_SPECIAL_ITEMS];
};

}

#endif


================================================
FILE: Code/Gel/Components/trickcomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       TrickComponent.cpp
//* OWNER:          Mr Kendall Stuart Harrison Esq.
//* CREATION DATE:  3 Jan 2003
//****************************************************************************

#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include  // Needed for GetPhysicsInt(...)
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{

static bool s_is_direction_button(uint32 buttonName);
#ifdef	__DEBUG_CODE__
static void s_add_array_of_names(Script::CStruct *p_info, int size, uint32 *p_source_array, const char *p_name);
#endif				 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Used by ExtraGapTrickLogic
static bool s_is_direction_button(uint32 buttonName)
{
	switch (buttonName)
	{
		case 0xbc6b118f: // Up
		case 0xe3006fc4: // Down
		case 0x85981897: // Left
		case 0x4b358aeb: // Right
		case 0xb7231a95: // UpLeft
		case 0xa50950c5: // UpRight
		case 0xd8847efa: // DownLeft
		case 0x786b8b68: // DownRight
			return true;
			break;
				
		default:
			break;
	}
	return false;	
}

#ifdef	__DEBUG_CODE__
// Converts an array of uint32's to a script array and inserts it into the passed structure, naming it p_name
static void s_add_array_of_names(Script::CStruct *p_info, int size, uint32 *p_source_array, const char *p_name)
{
	Script::CArray *p_array=new Script::CArray;
	if (size)
	{
		p_array->SetSizeAndType(size,ESYMBOLTYPE_NAME);
		
		for (int i=0; iSetChecksum(i,p_source_array[i]);
		}
	}
	p_info->AddArrayPointer(p_name,p_array);
}
#endif				 

// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CTrickComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CTrickComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CTrickComponent::CTrickComponent() : CBaseComponent()
{
	SetType( CRC_TRICK );
	
	mpTrickMappings=NULL;
	mp_score=NULL;
	mp_input_component=NULL;
	mp_skater_balance_trick_component=NULL;
	mp_skater_core_physics_component=NULL;
	mp_skater_flip_and_rotate_component=NULL;
	mp_skater_state_component=NULL;
    mp_stats_manager_component=NULL;
				 
	Clear();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CTrickComponent::~CTrickComponent()
{
	if (mpTrickMappings)
	{
		delete mpTrickMappings;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickComponent::Finalize()
{
	mp_input_component = GetInputComponentFromObject(GetObject());
	mp_skater_balance_trick_component = GetSkaterBalanceTrickComponentFromObject(GetObject());
	mp_skater_core_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
	mp_skater_flip_and_rotate_component = GetSkaterFlipAndRotateComponentFromObject(GetObject());
	mp_skater_state_component = GetSkaterStateComponentFromObject(GetObject());
    mp_stats_manager_component = GetStatsManagerComponentFromObject(GetObject());
            
	Dbg_Assert(mp_input_component);
	Dbg_Assert(mp_skater_balance_trick_component);
	Dbg_Assert(mp_skater_core_physics_component);
	Dbg_Assert(mp_skater_flip_and_rotate_component);
	Dbg_Assert(mp_skater_state_component);
    Dbg_Assert(mp_stats_manager_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Gets called from the CTrickComponent constructor, and also from CSkater::ResetEverything()
void CTrickComponent::Clear()
{
	m_num_runtrick_recursions=0;
	mNumButtonsToIgnore=0;

	mUseSpecialTrickText=false;
	
	mFrontTrick = -1;
	mBackTrick = -1;
	
	ClearQueueTricksArrays();
	
	mSpecialTricksArrayChecksum = 0;
	mDoNotIgnoreMask=0;

	ClearManualTrick();
	mSpecialManualTricksArrayChecksum=0;
	
	ClearExtraGrindTrick();
	mSpecialExtraGrindTrickArrayChecksum=0;
	
	mGotExtraTricks = false;
	mExtraTricksInfiniteDuration = false;
	mExtraTricksDuration = 0;
	mExtraTricksStartTime = 0;
	
	mNumExtraTrickArrays = 0;
	mSpecialExtraTricksArrayChecksum = 0;
	mExcludedSpecialExtraTricks = 0;
	
	ClearEventBuffer();
	for (int i=0; iGetStructure(CRCD(0xce42ee1a,"XBox_Trigger"),&p_trigger))
		{
			p_struct->GetStructure(CRCD(0xe594f0a2,"Trigger"),&p_trigger,Script::ASSERT);
		}
	}
	else if (Config::GetHardware()==Config::HARDWARE_NGC)
	{
		if (!p_struct->GetStructure(CRCD(0xcb0e600c,"NGC_Trigger"),&p_trigger))
		{
			p_struct->GetStructure(CRCD(0xe594f0a2,"Trigger"),&p_trigger,Script::ASSERT);
		}
	}
	else
	{
		p_struct->GetStructure(CRCD(0xe594f0a2,"Trigger"),&p_trigger,Script::ASSERT);
	}
	
	return p_trigger;
}

// K: 6/16/03 This is to implement a new feature, where as well as a Trigger structure, an Alt_Trigger
// structure can be specified. If either of Trigger or Alt_Trigger is satisfied, the trick triggers.
// The Alt_Trigger is the same for all three platforms.
Script::CStruct *CTrickComponent::get_alternate_trigger_structure(Script::CStruct *p_struct)
{
	Script::CStruct *p_trigger=NULL;
	// Note: Not passing Script::ASSERT as in get_trigger_structure above, because the Alt_Trigger is optional.
	p_struct->GetStructure(CRCD(0x68b74085,"Alt_Trigger"),&p_trigger);
	
	return p_trigger;
}
	
void CTrickComponent::ClearQueueTricksArrays()
{
	mNumQueueTricksArrays=0;
	for (int i=0; iClear();

	// "max_specials" is not stored in the skater profile by
	// the time the skater gets here...  so just loop through
	// all 10 slots
	
	// get the special tricks from the skater profile
	// they're stored in a different format than the
	// regular tricks, so we can't just append the structure
	for ( int i = 0; i < Obj::CSkaterProfile::vMAXSPECIALTRICKSLOTS; i++ )
	{
		SSpecialTrickInfo theInfo = pSkaterProfile->GetSpecialTrickInfo( i );
		if ( !theInfo.IsUnassigned() )
		{
			if ( theInfo.m_isCat )
			{
				mpTrickMappings->AddComponent( theInfo.m_TrickSlot, ESYMBOLTYPE_INTEGER, (int)theInfo.m_TrickName );
			}
			else
				mpTrickMappings->AddComponent( theInfo.m_TrickSlot, ESYMBOLTYPE_NAME, (int)theInfo.m_TrickName );
		}
	}

	// get the regular tricks from the skater profile
	mpTrickMappings->AppendStructure( pSkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") ) );
}

/******************************************************************/
/*  Adds a single new event and returns a pointer to it.		  */
/*                                                                */
/******************************************************************/

SButtonEvent *CTrickComponent::AddEvent(EEventType EventType)
{
	Dbg_MsgAssert(mNumEvents<=MAX_EVENTS,("mNumEvents too big, = %d",mNumEvents));
	Dbg_MsgAssert(mLastEvent=MAX_EVENTS)
	{
		mLastEvent=0;
	}
	if (mNumEvents= OlderThan)
			{
				mpButtonEvents[Index].Used=0xffffffff;
			}	
		}	
		--Index;
		if (Index<0)
		{
			Index+=MAX_EVENTS;
		}
	}
}

bool CTrickComponent::TriggeredInLastNMilliseconds(uint32 ButtonNameChecksum, uint32 Duration, uint32 IgnoreMask)
{
	
	
	int Index=mLastEvent;
	for (int i=0; i=Duration)
		{
			return false;
		}

		if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
			mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
			mpButtonEvents[Index].ButtonNameChecksum==ButtonNameChecksum)
		{
			mpButtonEvents[Index].MaybeUsed=true;
			return true;
		}	
		
		--Index;
		if (Index<0)
		{
			Index+=MAX_EVENTS;
		}	
	}
	return false;
}

bool CTrickComponent::BothTriggeredNothingInBetween(uint32 Button1, uint32 Button2, uint32 Duration, uint32 IgnoreMask)
{
	bool GotButton1=false;	
	bool GotButton2=false;	
	bool AnyOtherCancel=false;
	
	int Index=mLastEvent;
	for (int i=0; i=Duration)
		{
			return false;
		}

		if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
			mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED)
		{
			if (mpButtonEvents[Index].ButtonNameChecksum==Button1)
			{
				mpButtonEvents[Index].MaybeUsed=true;
				if (GotButton2)
				{
					return true;
				}
				GotButton1=true;
				AnyOtherCancel=true;
			}	
			else if (mpButtonEvents[Index].ButtonNameChecksum==Button2)
			{
				mpButtonEvents[Index].MaybeUsed=true;
				if (GotButton1)
				{
					return true;
				}
				GotButton2=true;
				AnyOtherCancel=true;
			}	
			else
			{
				if (AnyOtherCancel)
				{
					return false;
				}
			}		
		}	
		
		--Index;
		if (Index<0)
		{
			Index+=MAX_EVENTS;
		}	
	}
	return false;
}

// Reads the button names and time duration out of a 'Trigger' structure.
uint32 CTrickComponent::get_buttons_and_duration(Script::CComponent *p_comp, int num_buttons, uint32 *p_button_names)
{
	Dbg_MsgAssert(p_comp,("NULL p_comp"));
	Dbg_MsgAssert(p_button_names,("NULL p_button_names"));
	Dbg_MsgAssert(num_buttons<=MAX_TRICK_BUTTONS,("Bad num_buttons"));
	
	for (int i=0; imType==ESYMBOLTYPE_NAME,("Bad component type, expected name"));
		
		switch (p_comp->mChecksum)
		{
			case 0x9a7dc229: // Parent1
				p_button_names[i]=mp_trick_button[0];
				break;
			case 0x03749393: // Parent2
				p_button_names[i]=mp_trick_button[1];
				break;
			case 0x7473a305: // Parent3
				p_button_names[i]=mp_trick_button[2];
				break;
			default:
				p_button_names[i]=p_comp->mChecksum;
				break;
		}
				
		p_comp=p_comp->mpNext;
	}	
	
	Dbg_MsgAssert(p_comp,("Missing duration"));
	if (p_comp->mType==ESYMBOLTYPE_INTEGER)
	{
		return p_comp->mIntegerValue;
	}	
	Dbg_MsgAssert(p_comp->mType==ESYMBOLTYPE_NAME,("Bad duration value in trick, must be either an integer or a named integer"));
	return GetPhysicsInt(p_comp->mChecksum);
}

void CTrickComponent::record_last_tricks_buttons(uint32 *p_button_names)	
{
	Dbg_MsgAssert(p_button_names,("NULL p_button_names"));
	
	// Remember what buttons were used by the last trick that got triggered.
	// They are stored so that the extra-trick trigger can use one of the parent trick's buttons,
	// rather than being hard wired. This is needed in case the player re-maps the parent trick to
	// a different button combination.
	uint32 *p_dest=mp_trick_button;
	for (int i=0; i mButtonDebounceTime[Button]))
		{
			mButtonDebounceTime[Button] = 0;
		}
		else
		{
			return;
		}
	}
		 
	if (mButtonState[Button]==Pressed)
	{
		return;
	}	
	
	mButtonState[Button]=Pressed;


	uint32 ButtonChecksum=Inp::GetButtonChecksum( Button );
	
	// Perhaps ignore the button.
	// This feature is used by the skater to ignore the button events used to control the balance
	// when doing a manual, otherwise ChrisR can't do kickflips by very quickly jumping out of a 
	// grind & kickflipping by rolling from X to Square.
	for (int i=0; iButtonNameChecksum=ButtonChecksum;
	}
	else
	{
		SButtonEvent *pEvent=AddEvent(EVENT_BUTTON_RELEASED);
		Dbg_MsgAssert(pEvent,("NULL pEvent ?"));
		pEvent->ButtonNameChecksum=ButtonChecksum;
	}
}

bool CTrickComponent::GetButtonState(uint32 Checksum)
{
	return mButtonState[Inp::GetButtonIndex(Checksum)];
}

// Note: This probably needs a lot of optimization, hmmm.
bool CTrickComponent::QueryEvents(Script::CStruct *pQuery, uint32 UsedMask, uint32 IgnoreMask)
{
	if (!pQuery)
	{
		return false;
	}	
	
	if (mp_input_component->IsInputDisabled())
	{
		return false;
	}
		
	//Script::PrintContents(pQuery);	
    Script::CComponent *pComp=pQuery->GetNextComponent(NULL);
	if (!pComp)
	{
		return false;
	}	
	
	Dbg_MsgAssert(pComp->mType==ESYMBOLTYPE_NAME,("Bad component type, expected name"));
	uint32 NameChecksum=pComp->mChecksum;
	pComp=pComp->mpNext;
	
	uint32 p_button_names[MAX_TRICK_BUTTONS];
	for (int b=0; b=Duration)
				{
					return false;
				}
				
				switch (Count)
				{
				case 0:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=1;
					}	
					break;
				case 1:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						ConvertMaybeUsedToUsed(UsedMask);
						record_last_tricks_buttons(p_button_names);
						return true;
					}	
					break;
				default:
					break;
				}
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	
		case 0x2c434c90: // TapTwiceRelease
		{
			// Get two button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,2,p_button_names);

			int Index=mLastEvent;
			int Count=0;
			for (int i=0; i=Duration)
				{
					return false;
				}
			
				switch (Count)
				{
				case 0:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						(mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED && 
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1]))
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=1;
					}	
					break;
				case 1:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=2;
					}	
					break;
				case 2:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						ConvertMaybeUsedToUsed(UsedMask);
						record_last_tricks_buttons(p_button_names);
						return true;
					}	
					break;
				default:
					break;
				}
				
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	
		case 0x696e5e66: // ReleaseAndTap
		{
			// Get two button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,2,p_button_names);

			int e[3]={-1,-1,-1};
				
			int index=mLastEvent;
			int count=0;
			for (int i=0; i=Duration ||
				Tmr::ElapsedTime(mpButtonEvents[e[1]].Time)>=Duration ||
				Tmr::ElapsedTime(mpButtonEvents[e[2]].Time)>=Duration)
			{
				return false;
			}	

			if (mpButtonEvents[e[0]].EventType==EVENT_BUTTON_RELEASED && 
				mpButtonEvents[e[0]].ButtonNameChecksum==p_button_names[1] &&
                mpButtonEvents[e[1]].EventType==EVENT_BUTTON_PRESSED && 
				mpButtonEvents[e[1]].ButtonNameChecksum==p_button_names[1] &&
                mpButtonEvents[e[2]].EventType==EVENT_BUTTON_RELEASED && 
				mpButtonEvents[e[2]].ButtonNameChecksum==p_button_names[0])
			{
				mpButtonEvents[e[0]].MaybeUsed=true;
				mpButtonEvents[e[1]].MaybeUsed=true;
				mpButtonEvents[e[2]].MaybeUsed=true;
				ConvertMaybeUsedToUsed(UsedMask);
				record_last_tricks_buttons(p_button_names);
				return true;
			}
			break;
		}	
		case 0x3c3028f4: // PressTwo
		{
			// Get two button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,2,p_button_names);
			
			if (GetButtonState(p_button_names[0]) && GetButtonState(p_button_names[1]))
			{
				if (BothTriggeredNothingInBetween(p_button_names[0],p_button_names[1],Duration,IgnoreMask))
				{
					ConvertMaybeUsedToUsed(UsedMask);
					record_last_tricks_buttons(p_button_names);
					return true;
				}
			}		
			break;
		}	
		
		case 0x7d482318: // InOrder
		{
			// Get two button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,2,p_button_names);
			
			int Index=mLastEvent;
			int Count=0;
			for (int i=0; i=Duration)
				{
					return false;
				}
			
				switch (Count)
				{
				case 0:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=1;
					}	
					break;
				case 1:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						ConvertMaybeUsedToUsed(UsedMask);
						record_last_tricks_buttons(p_button_names);
						return true;
					}	
					break;
				default:
					break;

				}
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	

		case 0x3f369070: // PressTwoAnyOrder
		{
			int i;
			int Index;
			
			// Get two button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,2,p_button_names);
			
			bool Found = false;
			
			bool FirstPressed = false;
			bool SecondPressed = false;
			Index=mLastEvent;
			for (i=0; i=Duration)
				{
					return false;
				}
			
				if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
					mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
				{
					if (mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED)
					{
						mpButtonEvents[Index].MaybeUsed=true;
						FirstPressed=false;
						SecondPressed=false;
					}
					else if (mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED)
					{
						mpButtonEvents[Index].MaybeUsed=true;
						if (SecondPressed)
						{
							Found=true;
							break;
						}
						FirstPressed=true;
					}
				}	
			
				if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
					mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
				{
					if (mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED)
					{
						mpButtonEvents[Index].MaybeUsed=true;
						FirstPressed=false;
						SecondPressed=false;
					}
					else if (mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED)
					{
						mpButtonEvents[Index].MaybeUsed=true;
						if (FirstPressed)
						{
							Found=true;
							break;
						}
						SecondPressed=true;
					}
				}	
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			
			if (Found)
			{
				mpButtonEvents[Index].MaybeUsed=true;
				ConvertMaybeUsedToUsed(UsedMask);
				record_last_tricks_buttons(p_button_names);
				return true;
			}
			else
			{
				return false;
			}
		}	
		
		// Various different TripleInOrder's, which became necessary when using it for different types of
		// tricks such as the boneless, and the special grinds.
		// It was found that the logic needs to be quite sloppy for the boneless so that they can get triggered
		// easily otherwise it feels wrong, whereas it needs to be stricter for special grinds otherwise 
		// they go off all the time.
		
		// Requires that Button1 be pressed, followed by Button2 pressed, followed by Button3 pressed.
		// Doesn't care when they get released.
		case 0xc1ad35c0: // TripleInOrderSloppy
		{
			// Get three button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,3,p_button_names);
			
			int Index=mLastEvent;
			int Count=0;
			for (int i=0; i=Duration)
				{
					return false;
				}
			
				switch (Count)
				{
				case 0:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[2])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=1;
					}	
					break;
				case 1:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=2;
					}	
					break;
				case 2:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						ConvertMaybeUsedToUsed(UsedMask);
						record_last_tricks_buttons(p_button_names);
						return true;
					}	
					break;
				default:
					break;

				}
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	
		// Button1 must be pressed then released, then Button2 must be pressed then released,
		// then Button3 must be pressed. Doesn't require that Button3 be released, because it
		// feels better if the logic takes effect exactly on the 3rd press.
		case 0x3fe7c311: // TripleInOrderStrict
		{
			// Get three button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,3,p_button_names);
			
			int Index=mLastEvent;
			int Count=0;
			for (int i=0; i=Duration)
				{
					return false;
				}
			
				switch (Count)
				{
				case 0:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[2])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=1;
					}	
					break;
				case 1:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=2;
					}	
					break;
				case 2:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=3;
					}	
					break;
				case 3:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=4;
					}	
					break;
				case 4:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						ConvertMaybeUsedToUsed(UsedMask);
						record_last_tricks_buttons(p_button_names);
						return true;
					}	
					break;
				default:
					break;

				}
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	
		// Button1 must be pressed then released, followed by either button2 pressed then button3 pressed,
		// or button3 pressed then button2 pressed.
		// Sort of strict & sloppy at the same time.
		case 0xa2e042d7: // TripleInOrder
		{
			// Get three button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,3,p_button_names);
			
			int Index=mLastEvent;
			int Count=0;
			for (int i=0; i=Duration)
				{
					return false;
				}
			
				switch (Count)
				{
				case 0:
					// Even though it's called TripleInOrder, buttons 2 and 3
					// can be pressed in either order, so we do a bit of convoluted
					// branching here to check for both cases.
					if (!(mpButtonEvents[Index].Used & IgnoreMask))
					{
						if (mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
							mpButtonEvents[Index].ButtonNameChecksum==p_button_names[2])
						{
							mpButtonEvents[Index].MaybeUsed=true;
							Count=1;
							break;
						}	
						if (mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
							mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
						{
							mpButtonEvents[Index].MaybeUsed=true;
							Count=2;
							break;
						}	
					}	
					break;
					
					
				case 1:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=3;
					}	
					break;
					
				case 2:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[2])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=3;
					}	
					break;
					
					
				// Button 1 has to be pressed and released before the other 2.	
				case 3:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=4;
					}	
					break;
				case 4:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						ConvertMaybeUsedToUsed(UsedMask);
						record_last_tricks_buttons(p_button_names);
						return true;
					}	
					break;
				default:
					break;

				}
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}
		case 0x823b8342: // Press
		{
			// Get one button name followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,1,p_button_names);
			
			int Index=mLastEvent;
			for (int i=0; i=Duration)
				{
					return false;
				}
			
				if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
					mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
					mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
				{
					mpButtonEvents[Index].Used|=UsedMask;
					record_last_tricks_buttons(p_button_names);
					return true;
				}	
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	
		case 0x61b8fce2: // Release
		{
			// Get one button name followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,1,p_button_names);
			
			int Index=mLastEvent;
			for (int i=0; i=Duration)
				{
					return false;
				}
				
				if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
					mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED &&
					mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
				{
					mpButtonEvents[Index].Used|=UsedMask;
					record_last_tricks_buttons(p_button_names);
					return true;
				}	
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	
		case 0x7fa5cdbb: // Tap
		{
			// Get one button name followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,1,p_button_names);
			
			int Count=0;
			int Index=mLastEvent;
			for (int i=0; i=Duration)
				{
					return false;
				}
				
				if (Count == 0)
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count++;
					}
				}
				else
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						ConvertMaybeUsedToUsed(UsedMask);
						return true;
					}
				}
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	
		case 0xf630dae5: // HoldTwoAndPress
		{
			// Get three button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,3,p_button_names);
			
			int Index=mLastEvent;
			int Count=0;
			for (int i=0; i=Duration)
				{
					return false;
				}
			
				switch (Count)
				{
				case 0:
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[2])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						Count=1;
						break;
					}	
					break;
					
				case 1:
					if (!(mpButtonEvents[Index].Used & IgnoreMask))
					{
						if (mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
						{
							if (mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED)
							{
								mpButtonEvents[Index].MaybeUsed=true;
								Count=2;
							}
							else if (mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED)
							{
								return false;
							}	
						}
						else if (mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
						{
							if (mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED)
							{
								mpButtonEvents[Index].MaybeUsed=true;
								Count=3;
							}
							else if (mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED)
							{
								return false;
							}	
						}
					}		
					break;
					
				case 2:
					if (!(mpButtonEvents[Index].Used & IgnoreMask))
					{
						if (mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
						{
							if (mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED)
							{
								mpButtonEvents[Index].MaybeUsed=true;
								ConvertMaybeUsedToUsed(UsedMask);
								record_last_tricks_buttons(p_button_names);
								return true;
							}
							else if (mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED)
							{
								return false;
							}	
						}
					}		
					break;

				case 3:
					if (!(mpButtonEvents[Index].Used & IgnoreMask))
					{
						if (mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
						{
							if (mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED)
							{
								mpButtonEvents[Index].MaybeUsed=true;
								ConvertMaybeUsedToUsed(UsedMask);
								record_last_tricks_buttons(p_button_names);
								return true;
							}
							else if (mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED)
							{
								return false;
							}	
						}
					}		
					break;
					
				default:
					break;

				}
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	

		case 0xac2b1445: // ReleaseTwoAndPress
		{
			// Get three button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,3,p_button_names);
			
			int Index=mLastEvent;
			bool got_a_released=false;
			bool got_b_released=false;
			bool got_c_pressed=false;
			
			for (int i=0; i=Duration)
				{
					return false;
				}

				if (!got_a_released)
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						got_a_released=true;
					}	
				}					
				if (!got_b_released)
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						got_b_released=true;
					}	
				}					
				if (!got_c_pressed)
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[2])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						got_c_pressed=true;
					}	
				}					


				if (got_a_released && !got_b_released)
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						return false;
					}	
				}					
				if (got_b_released && !got_a_released)
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
					{
						return false;
					}	
				}					
				
				if (got_a_released && got_b_released && got_c_pressed)
				{
					ConvertMaybeUsedToUsed(UsedMask);
					record_last_tricks_buttons(p_button_names);
					return true;
				}	
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	

		case 0x395ec2d4: // ReleaseTwo
		{
			// Get two button names followed by a duration value.
			uint32 Duration=get_buttons_and_duration(pComp,2,p_button_names);
			
			int Index=mLastEvent;
			bool got_a_released=false;
			bool got_b_released=false;
			
			for (int i=0; i=Duration)
				{
					return false;
				}

				if (!got_a_released)
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						got_a_released=true;
					}	
				}					
				if (!got_b_released)
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
					{
						mpButtonEvents[Index].MaybeUsed=true;
						got_b_released=true;
					}	
				}					

				if (got_a_released && !got_b_released)
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						return false;
					}	
				}					
				if (got_b_released && !got_a_released)
				{
					if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
						mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED &&
						mpButtonEvents[Index].ButtonNameChecksum==p_button_names[1])
					{
						return false;
					}	
				}					
				
				if (got_a_released && got_b_released)
				{
					ConvertMaybeUsedToUsed(UsedMask);
					record_last_tricks_buttons(p_button_names);
					return true;
				}	
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}
			break;
		}	

		case 0xb6258557: // ExtraGrabTrickLogic
		{
			uint32 last_direction_button=0;
			for (int b=0; b=Duration)
				{
					break;
				}
			
				if (!(mpButtonEvents[Index].Used & IgnoreMask) &&
					mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED)
				{
					if (mpButtonEvents[Index].ButtonNameChecksum==p_button_names[0])
					{
						// OK, so the button has been pressed, but don't return true
						// just yet cos we want to keep looking back in time & kill any
						// direction button events that are the same direction button as
						// used by last trick.
						do_trigger=true;
						pressed_index=Index;
					}	
					else if (s_is_direction_button(mpButtonEvents[Index].ButtonNameChecksum))
					{
						// A direction button has gotten pushed during the same time interval ...
						
						if (mpButtonEvents[Index].ButtonNameChecksum != last_direction_button)
						{
							// It is not the same as the previous trick's direction button,
							// so do not trigger at all. Run away without killing any events.
							return false;
						}	
						else
						{
							// It is the same as the previous trick's direction button,
							// so flag it as maybe used.
							// If we survive the rest of this loop without the above 'return true'
							// firing then the MaybeUsed flags will get converted to used after
							// the loop has finished.
							mpButtonEvents[Index].MaybeUsed=true;
						}	
					}
				}	
				
				--Index;
				if (Index<0)
				{
					Index+=MAX_EVENTS;
				}	
			}

			if (do_trigger)
			{
				// Kill the events for sure.
				mpButtonEvents[pressed_index].Used|=UsedMask;
				ConvertMaybeUsedToUsed(UsedMask);
				record_last_tricks_buttons(p_button_names);
				return true;
			}
			break;
		}

		case 0xa8123ecf: // HoldThree			
		{
			Script::CComponent *p_comp=pComp;
			
			Dbg_MsgAssert(p_comp && p_comp->mType==ESYMBOLTYPE_NAME,("HoldThree logic requires 3 button names"));
			uint32 button1=p_comp->mChecksum;
			p_comp=p_comp->mpNext;
			Dbg_MsgAssert(p_comp && p_comp->mType==ESYMBOLTYPE_NAME,("HoldThree logic requires 3 button names"));
			uint32 button2=p_comp->mChecksum;
			p_comp=p_comp->mpNext;
			Dbg_MsgAssert(p_comp && p_comp->mType==ESYMBOLTYPE_NAME,("HoldThree logic requires 3 button names"));
			uint32 button3=p_comp->mChecksum;
			
			return GetButtonState(button1) && GetButtonState(button2) && GetButtonState(button3);
			break;
		}
			
		default:
			Dbg_MsgAssert(0,("Unexpected name '%s'",Script::FindChecksumName(NameChecksum)));
			break;
	}		

	ResetMaybeUsedFlags();
	return false;
}

/*****************************************************************************
**																			**
**							Trick queue stuff								**
**																			**
*****************************************************************************/

void CTrickComponent::ClearTrickQueue()
{
	
	mFrontTrick=mBackTrick=-1;
}

void CTrickComponent::AddTrick(uint32 ArrayChecksum, uint Index, bool UseSpecialTrickText)
{
	int i=0;
	if (mFrontTrick==-1 && mBackTrick==-1)
	{
		// The queue is empty, so initialise the front and back.
		mFrontTrick=i;
		mBackTrick=i;
	}
	else
	{
		Dbg_MsgAssert(mFrontTrick!=-1,("mFrontTrick = -1 ??"));
		Dbg_MsgAssert(mBackTrick!=-1,("mBackTrick = -1 ??"));
		
		// Go to the current back of the queue
		i=mBackTrick;
		// Advance to the next entry
		++i;
		if (i>=TRICK_QUEUE_SIZE)
		{
			i=0;
		}
		// If reached the front again then there is no room left,
		// so don't do anything.
		if (i==mFrontTrick)
		{
			return;
		}
		// Got a new back of the queue.
		mBackTrick=i;			
	}	
	
	// Write in the info.
	mpTricks[i].ArrayChecksum=ArrayChecksum;
	mpTricks[i].Index=Index;
	mpTricks[i].UseSpecialTrickText=UseSpecialTrickText;
}

void CTrickComponent::RemoveFrontTrick()
{
	
	// If the queue is empty then there is nothing to do.
	if (mFrontTrick==-1)
	{
		return;
	}	
	
	Dbg_MsgAssert(mBackTrick!=-1,("mBackTrick = -1 ??"));
	
	// If only one element in the queue, then clear the queue.
	if (mFrontTrick==mBackTrick)
	{
		ClearTrickQueue();
		return;
	}	
	
	// Advance the front trick to the next entry.
	++mFrontTrick;
	if (mFrontTrick>=TRICK_QUEUE_SIZE)
	{
		mFrontTrick=0;
	}	
}

// This will remove all tricks that came from a certain trick array.
// This is used by the ClearTricksFrom command, which is used by air-trick
// scripts to remove any boneless's as soon as the air trick is triggered, so that 
// the boneless does not trigger afterwards. It is possible to trigger a boneless for
// a short time after becoming airborne due to the ground-gone exception, this is
// the 'late-jump'.
void CTrickComponent::ClearTricksFrom(uint32 ArrayChecksum)
{
	
	
	int i;
	
	// Remove the array from the list of arrays being checked.
	for (i=0; iGetChecksum(0xa6d2d890/* Scr */,&ScriptChecksum))
	{
		return true;
	}
	if (pTrick->GetChecksum(0x22e168c1/* Scripts */,&ScriptChecksum))
	{
		return true;
	}
	Script::CArray *p_template_array=NULL;
	if (pTrick->GetArray(0x689fe07c/* Template */,&p_template_array))
	{
		return true;
	}


	// Get the checksum of the slot ...
	uint32 TrickSlotChecksum=0;
	if (pTrick->GetChecksum(0xa92a2280/* TrickSlot */,&TrickSlotChecksum))
	{
		// Look up this slot in the trick mappings ...
		Dbg_MsgAssert(mpTrickMappings,("NULL mpTrickMappings ?"));
		uint32 TrickChecksum=0;
		mpTrickMappings->GetChecksum(TrickSlotChecksum,&TrickChecksum);
	
		if (TrickChecksum)
		{
			// Now, look up the global structure with this name.
			if (Script::GetStructure(TrickChecksum))
			{
				return true;
			}
		}
		else
		{
			// If the trick slot is defined to be an integer, then it is referring
			// to a create-a-trick.
			int create_a_trick=0;
			if (mpTrickMappings->GetInteger(TrickSlotChecksum,&create_a_trick))
			{
				return true;
			}	
		}
	}			
	return false;
}

bool CTrickComponent::RunTrick(Script::CStruct *pTrick, uint32 optionalFlag, Script::CStruct* pExtraParams)
{
	Dbg_MsgAssert(pTrick,("NULL pTrick"));
	
    uint32 ScriptChecksum=0;
    pTrick->GetChecksum(CRCD(0xa6d2d890,"Scr"),&ScriptChecksum);
	
	// Dan: HACK
	// final check to insure that we don't do a grind extra trick when we're not on a rail; if the scripts were perfect, this would never occur; however,
	// this should prevent an obscure net crash
	if (ScriptChecksum == CRCD(0x255ed86f, "Grind") && mp_skater_core_physics_component->GetRailNode() == -1)
	{
		return false;
	}
    
    // counting fliptricks and grabtricks for stats goals
    Script::CStruct* pParams;
    if(pTrick->GetStructure(CRCD(0x7031f10c,"Params"),&pParams))
    {
        if (!pParams->ContainsComponentNamed(CRCD(0x7a16aca0,"IsExtra")) )
        {
            mp_stats_manager_component->SetTrickType( ScriptChecksum );
        }
    }
	
#ifdef __NOPT_ASSERT__
	if (m_num_runtrick_recursions>5)
	{
		#ifdef	__NOPT_ASSERT__
		printf("WARNING! More than 5 RunTrick recursions\nScriptChecksum='%s'",Script::FindChecksumName(ScriptChecksum));
		#endif
		return false;
	}	
#endif		// __NOPT_ASSERT__
	
	if (ScriptChecksum)
	{
		// Increment this so that too many recursions can be detected.
		++m_num_runtrick_recursions;
		
		// A trick is triggered, so clear any manual or special grind trick that might have got triggered before.
		mGotManualTrick=false;
		mGotExtraGrindTrick=false;
	
		// Also clear the do-not-ignore mask, which may have got set by the last SetQueueTricks command.
		mDoNotIgnoreMask=0;
		
		// Change the skater script to be the trick script.
		Script::CStruct *pScriptParams=NULL;
		pTrick->GetStructure(CRCD(0x7031f10c,"Params"),&pScriptParams);
	
		// Run the script.
		mp_skater_flip_and_rotate_component->DoAnyFlipRotateOrBoardRotateAfters(); // <- See huge comment above definition of this function.
		
		CCompositeObject *p_object=GetObject();
		Dbg_MsgAssert(p_object,("Trick component has NULL object ?"));
		p_object->SwitchScript(ScriptChecksum,pScriptParams);

		// If a flag value was passed in, insert it into the script's parameters.
		if (optionalFlag)
		{
			// Note that the flag is being inserted into the script's params after calling
			// SetScript rather than putting it into pScriptParams.
			// This is because pScriptParams could be pointing into some global structure, and
			// that should be kept read-only.
			Script::CStruct *p_script_params=p_object->GetScript()->GetParams();
			Dbg_MsgAssert(p_script_params,("NULL p_script_params"));
			p_script_params->AddChecksum(NONAME,optionalFlag);
		}
		
		p_object->GetScript()->GetParams()->AppendStructure(pExtraParams);
			
		p_object->GetScript()->Update();
	
		// Set the mDoingTrick flag so that the camera can detect that a trick is being done.
		mp_skater_state_component->SetDoingTrick( true );
	
		// Increment the trick count, which may set pedestrian exceptions to
		// make them go "oooOOOOOooooh"
		IncrementNumTricksInCombo();
		
		// Decrement the recursion counter now that this chunk of code has completed.
		--m_num_runtrick_recursions;
		return true;
	}	
	else
	{
		#ifdef	__NOPT_ASSERT__
		uint32 trickslot=0;
		if (!pTrick->GetChecksum(CRCD(0xa92a2280,"TrickSlot"),&trickslot))
		{
			printf("WARNING! Scr not defined in trick structure!\n");
			Script::PrintContents(pTrick);
		}	
		#endif
	}			
	
	return false;
}

/******************************************************************/
/* Runs the next trick in the queue.							  */
/******************************************************************/
void CTrickComponent::TriggerNextQueuedTrick(uint32 scriptToRunFirst, Script::CStruct *p_scriptToRunFirstParams, Script::CStruct* pExtraParams)
{
	mp_skater_state_component->SetDoingTrick( false );
	mUseSpecialTrickText=false;

	while (true)
	{
		if (mFrontTrick==-1)
		{
			// No tricks left in the queue, so break.
			break;
		}
			
		// Grab the array checksum & index of the trick within the array,
		// then remove the trick from the queue.
		// Removing the trick straight away in case the new script also does
		// a DoNextTrick, which would cause infinite recursion if this trick
		// was still in the queue.
		uint32 ArrayChecksum=mpTricks[mFrontTrick].ArrayChecksum;
		uint Index=mpTricks[mFrontTrick].Index;
		// Set this flag so that any special trick scripts that do get executed during the RunTrick
		// will use the yellow text if they execute a Display command. 
		mUseSpecialTrickText=mpTricks[mFrontTrick].UseSpecialTrickText;
		
		RemoveFrontTrick();

		// The ArrayChecksum might be zero, this would indicate that 
		// the trick got cancelled by a call to ClearTricksFrom().
		if (ArrayChecksum)
		{
			// The DoNextTrick command can specify a script that must be run before the trick script.
			// So if one was specified, run it.
			if (scriptToRunFirst)
			{
				GetObject()->AllocateScriptIfNeeded();
				GetObject()->GetScript()->Interrupt(scriptToRunFirst, p_scriptToRunFirstParams);
			}
				
			// Get the trick array that the trick belongs to.
			Script::CArray *pArray=Script::GetArray(ArrayChecksum);
			Dbg_MsgAssert(pArray,("NULL pArray ?"));
			
			// Get the structure defining the trick.
			Script::CStruct *pTrickStruct=pArray->GetStructure(Index);
			Dbg_MsgAssert(pTrickStruct,("NULL pTrickStruct ???"));
			
			// Try running the trick the old way, which is where the trick script is
			// specified directly in the structure. 
			if (!RunTrick(pTrickStruct, 0, pExtraParams))
			{
				// That didn't work, so they must be wanting to specify a trick slot
				// instead, so get the trickslot ...
				
				// Get the checksum of the slot ...
				uint32 TrickSlotChecksum=0;
				pTrickStruct->GetChecksum(CRCD(0xa92a2280,"TrickSlot"),&TrickSlotChecksum);
			
				if (TrickSlotChecksum)
				{
					// Look up this slot in the trick mappings ...
					Dbg_MsgAssert(mpTrickMappings,("NULL mpTrickMappings ?"));
				
					// Maybe it is a create-a-trick, in which case it is referred to by an
					// integer index value.
					int create_a_trick=0;
					if (mpTrickMappings->GetInteger(TrickSlotChecksum,&create_a_trick))
					{
						// There is a create-a-trick defined!
						// Run the special CreateATrick script passing the index as a parameter.
						Script::CStruct *p_params=new Script::CStruct;
						p_params->AddInteger(CRCD(0xb4a39841,"trick_index"),create_a_trick);
						
						p_params->AppendStructure(pExtraParams);
						
						CCompositeObject *p_object=GetObject();
						Dbg_MsgAssert(p_object,("Trick component has NULL object ?"));
						p_object->SwitchScript(CRCD(0x2d90485d,"CreateATrick"),p_params);
						delete p_params;
							
						p_object->GetScript()->Update();
						
						// Set the mDoingTrick flag so that the camera can detect that a trick is being done.
						mp_skater_state_component->SetDoingTrick( true );
					
						// Increment the trick count, which may set pedestrian exceptions to
						// make them go "oooOOOOOooooh"
						IncrementNumTricksInCombo();
					}
					else
					{
						uint32 TrickChecksum=0;
						mpTrickMappings->GetChecksum(TrickSlotChecksum,&TrickChecksum);
					
						if (TrickChecksum)
						{
							// Now, look up the global structure with this name.
							
							Script::CStruct *pTrick=Script::GetStructure(TrickChecksum);
							if (pTrick)
							{
								RunTrick(pTrick, 0, pExtraParams);
							}
						}
					}
				}			
			}
			
			// The ArrayChecksum was not zero, so break.
			// We only keep looping if the ArrayChecksum is zero, so that all the zeros get skipped over.
			// If the ArrayChecksum is zero this indicates that the trick got cancelled by a call to
			// ClearTricksFrom().
			break;
		}
	}
}

/******************************************************************/
/* Scans through a trick array, and adds any tricks that got   	  */
/* triggered to the trick queue.                                  */
/******************************************************************/
void CTrickComponent::MaybeAddTrick(uint32 ArrayChecksum, bool UseSpecialTrickText, uint32 DoNotIgnoreMask)
{
	

	Dbg_MsgAssert(ArrayChecksum,("Zero ArrayChecksum sent to MaybeAddTrick"));
		
	// Resolve the checksum into a CArray pointer.
	// The array is stored by checksum in case it gets reloaded.
	Script::CArray *pArray=Script::GetArray(ArrayChecksum);
		
	// The above would have asserted if the array wasn't found, but check pArray isn't NULL anyway.
	if (pArray)
	{
		// Scan through the array checking each trick's trigger condition.
		int Size=pArray->GetSize();
		for (int i=0; iGetStructure(i);
			Dbg_MsgAssert(pStruct,("NULL pStruct ???"));
			// pStruct is the structure defining the trick.
			
			if (TrickIsDefined(pStruct))
			{
				// Get the trigger, which is a structure defining the button combination that triggers the trick.
				Script::CStruct *p_trigger=get_trigger_structure(pStruct);
				// An alternate trigger may also be specified. This will also trigger the trick.
				Script::CStruct *p_alternate_trigger=get_alternate_trigger_structure(pStruct);
			
				// p_trigger could be NULL, but OK cos QueryEvents will return false in that case.
				// The DoNotIgnoreMask specifies additional used-events that should not be ignored.
				uint32 ignore_mask=(~USED_BY_MANUAL_TRICK) & (~DoNotIgnoreMask);
				
				if (QueryEvents(p_trigger,USED_BY_REGULAR_TRICK,ignore_mask) ||
					QueryEvents(p_alternate_trigger,USED_BY_REGULAR_TRICK,ignore_mask))
				{
					// The conditions are met, so add the trick to the trick queue.
					// The trick is specified by the name of the array it is in, and its index within that array.
					AddTrick(ArrayChecksum,i,UseSpecialTrickText);
				}
			}	
		}
	}
}
	
void CTrickComponent::AddTricksToQueue()
{
	

	// If a special tricks array is specified, and we're in the special state, check them.
	if (mSpecialTricksArrayChecksum)
	{
		Mdl::Score *pScore=GetScoreObject();
		if (pScore->GetSpecialState())
		{
			MaybeAddTrick(mSpecialTricksArrayChecksum,USE_SPECIAL_TRICK_TEXT,mDoNotIgnoreMask);
		}
	}	

	// Check the usual arrays.
	for (int TrickArrayIndex=0; TrickArrayIndexGetChecksum(CRCD(0x148fee96,"ScriptToRunFirst"),&scriptToRunFirst);
	pExtraParams->GetStructure(CRCD(0x7031f10c,"Params"),&pScriptToRunFirstParams);

	mUseSpecialTrickText=false;
	
	if (mGotManualTrick)
	{
		// Now that the trick is being done, remove it.
		// Removing it before changing the script rather than after to
		// prevent any possibility of infinite recursion.
		mGotManualTrick=false;
		
		
		// The DoNextManualTrick command can specify a script that must be run before the trick script.
		// So if one was specified, run it.
		if (scriptToRunFirst)
		{
			// we need to copy and restore the script parameters as they will be corrupted by the interrupt
			Script::CStruct extraParamsCopy(*pExtraParams);
			GetObject()->AllocateScriptIfNeeded();
			GetObject()->GetScript()->Interrupt(scriptToRunFirst, pScriptToRunFirstParams);
			*pExtraParams = extraParamsCopy;
		}
		
		// Get the trick array that the trick belongs to.
		Script::CArray *pArray=Script::GetArray(mManualTrick.ArrayChecksum);
		if (pArray)
		{
			// Get the structure defining the trick.
			Script::CStruct *pStruct=pArray->GetStructure(mManualTrick.Index);
			Dbg_MsgAssert(pStruct,("NULL pStruct ???"));
			

			// Get the checksum of the slot if there is one ...
			uint32 TrickSlotChecksum=0;
			pStruct->GetChecksum(CRCD(0xa92a2280,"TrickSlot"),&TrickSlotChecksum);
			
			if (TrickSlotChecksum)
			{
				// Look up this slot in the trick mappings ...
				Dbg_MsgAssert(mpTrickMappings,("NULL mpTrickMappings ?"));
				uint32 TrickChecksum=0;
				mpTrickMappings->GetChecksum(TrickSlotChecksum,&TrickChecksum);
				
				if (TrickChecksum)
				{
					// Now, look up the global structure with this name.
					
					Script::CStruct *pTrick=Script::GetStructure(TrickChecksum);
					if (pTrick)
					{
						// Now change pStruct to be pTrick, so that Scr and Params get read out of it instead.
						pStruct=pTrick;
					}
				}
			}
			// If there is no TrickSlot specified, then use pStruct as is, to maintain backwards compatibility.			

			
			// Read the script checksum of the trick.
			uint32 ScriptChecksum=0;
			pStruct->GetChecksum(CRCD(0xa6d2d890,"Scr"),&ScriptChecksum);
			if (ScriptChecksum)
			{
				mp_skater_flip_and_rotate_component->DoAnyFlipRotateOrBoardRotateAfters(); // <- See huge comment above definition of this function.
				mUseSpecialTrickText=mManualTrick.UseSpecialTrickText;
			
				// Change the skater script to be the trick script.
				Script::CStruct *pScriptParams=NULL;
				pStruct->GetStructure(CRCD(0x7031f10c,"Params"),&pScriptParams);
				
				CCompositeObject *p_object=GetObject();
				Dbg_MsgAssert(p_object,("Trick component has NULL object ?"));

				if (pExtraParams)
				{
					Dbg_MsgAssert(pExtraParams!=pScriptParams,("Eh ??"));
					
					// If extra params (params following the DoNextTrick command) were
					// specified, then merge the pScriptParams onto them and use them.
					pExtraParams->AppendStructure(pScriptParams);
					
					p_object->SwitchScript(ScriptChecksum,pExtraParams);
				}
				else
				{
					p_object->SwitchScript(ScriptChecksum,pScriptParams);
				}
				p_object->GetScript()->Update();
								
				// Set the mDoingTrick flag so that the camera can detect that a trick is being done.
				mp_skater_state_component->SetDoingTrick( true );
				
				// Increment the trick count, which may set pedestrian exceptions to
				// make them go "oooOOOOOooooh"
				IncrementNumTricksInCombo();
			}	
		}
	}
}
	
void CTrickComponent::CheckManualTrickArray(uint32 ArrayChecksum, uint32 IgnoreMask, bool UseSpecialTrickText)
{
	
	Dbg_MsgAssert(ArrayChecksum,("Zero ArrayChecksum sent to CheckManualTrickArray"));
	
	// Resolve the checksum into a CArray pointer.
	// The array is stored by checksum in case it gets reloaded.
	Script::CArray *pArray=Script::GetArray(ArrayChecksum);
		
	// The above would have asserted if the array wasn't found, but check pArray isn't NULL anyway.
	if (pArray)
	{
		// Scan through the array checking each trick.
		int Size=pArray->GetSize();
		for (int t=0; tGetStructure(t);
			Dbg_MsgAssert(pStruct,("NULL pStruct ???"));
			// pStruct is the structure defining the trick.
			
			if (TrickIsDefined(pStruct))
			{
				// Get the trigger, which is a structure defining the button combination that triggers the trick.
				Script::CStruct *p_trigger=get_trigger_structure(pStruct);
				// An alternate trigger may also be specified. This will also trigger the trick.
				Script::CStruct *p_alternate_trigger=get_alternate_trigger_structure(pStruct);
				
				
				// p_trigger could be NULL, but OK cos QueryEvents will return false in that case.
				if (QueryEvents(p_trigger,USED_BY_MANUAL_TRICK,IgnoreMask) ||
					QueryEvents(p_alternate_trigger,USED_BY_MANUAL_TRICK,IgnoreMask))
				{
					// The conditions are met, so add the manual trick.
					mManualTrick.ArrayChecksum=ArrayChecksum;
					mManualTrick.Index=t;
					mManualTrick.Time=Tmr::GetTime();
					mManualTrick.Duration=0xffffffff;
					mManualTrick.UseSpecialTrickText=UseSpecialTrickText;
					pStruct->GetInteger(0x79a07f3f/*Duration*/,(int*)&mManualTrick.Duration);
					mGotManualTrick=true;
				}
			}	
		}
	}
}
	
// Always called every frame.
void CTrickComponent::MaybeQueueManualTrick()
{
	

	// Check any special manual tricks.
	if (mSpecialManualTricksArrayChecksum)
	{
		// Got extra manual tricks ...
		Mdl::Score *pScore=GetScoreObject();
		if (pScore->GetSpecialState())
		{
			// And we're special, so check the tricks.
			// The ~USED_BY_MANUAL_TRICK means don't ignore button events that have already been
			// used by manuals. This is so that any normal manual that has just got queued won't
			// steal the events needed by a up-down-triangle special manual for example.
			CheckManualTrickArray(mSpecialManualTricksArrayChecksum,~USED_BY_MANUAL_TRICK,USE_SPECIAL_TRICK_TEXT);
			if (mGotManualTrick)
			{
				return;
			}	
		}
	}
	
	// For each of the trick arrays, check whether any of the tricks listed within are triggered.
	for (int i=0; imManualTrick.Duration)
		{
			mGotManualTrick=false;
		}	
	}	
}

/*****************************************************************************
**																			**
**							Extra trick stuff								**
**																			**
*****************************************************************************/

bool CTrickComponent::TriggerAnyExtraTrick(uint32 ArrayChecksum, uint32 ExcludedTricks)
{
	

	bool TriggeredATrick=false;
	
	Dbg_MsgAssert(ArrayChecksum,("Zero ArrayChecksum sent to TriggerAnyExtraTrick"));
	Script::CArray *pArray=Script::GetArray(ArrayChecksum);
	
	// The above would have asserted if the array wasn't found, but check pArray isn't NULL anyway.
	if (pArray)
	{
		// Scan through the array checking each trick.
		int Size=pArray->GetSize();
		// Only 32 bits available in the mpExcludedExtraTricks entries.
		Dbg_MsgAssert(Size<=32,("Extra trick array '%s' has more than 32 entries",Script::FindChecksumName(ArrayChecksum)));
		
		for (int i=0; iGetStructure(i);
				Dbg_MsgAssert(pTrickStruct,("NULL pTrickStruct ???"));
				// pTrickStruct is the structure defining the trick.
				
				if (TrickIsDefined(pTrickStruct))
				{
					// Get the trigger, which is a structure defining the button combination that triggers the trick.
					Script::CStruct *p_trigger=get_trigger_structure(pTrickStruct);
					// An alternate trigger may also be specified. This will also trigger the trick.
					Script::CStruct *p_alternate_trigger=get_alternate_trigger_structure(pTrickStruct);
					
					// p_trigger could be NULL, but OK cos QueryEvents will return false in that case.
					
					// The USED_BY_EXTRA_TRICK flags value means flag any button events used as being
					// used by the extra tricks.
					if (QueryEvents(p_trigger,USED_BY_EXTRA_TRICK,~USED_BY_MANUAL_TRICK) ||
						QueryEvents(p_alternate_trigger,USED_BY_EXTRA_TRICK,~USED_BY_MANUAL_TRICK))
					{
						// Run the trick ...
					
						// Try running the trick the old way, which is where the trick script is
						// specified directly in the structure. 
						// Passing the flag IsExtra to the script, so that the script can tell
						// whether it was run as an extra trick or a regular trick.
						if (!RunTrick(pTrickStruct,0x7a16aca0/*IsExtra*/))
						{
							// That didn't work, so they must be wanting to specify a trick slot
							// instead, so get the trickslot ...
					
							// Get the checksum of the slot ...
							uint32 TrickSlotChecksum=0;
							pTrickStruct->GetChecksum(0xa92a2280/*TrickSlot*/,&TrickSlotChecksum);
					
							if (TrickSlotChecksum)
							{
								// Look up this slot in the trick mappings ...
								Dbg_MsgAssert(mpTrickMappings,("NULL mpTrickMappings ?"));
								uint32 TrickChecksum=0;
								mpTrickMappings->GetChecksum(TrickSlotChecksum,&TrickChecksum);
					
								if (TrickChecksum)
								{
									// Now, look up the global structure with this name.
					
									Script::CStruct *pTrick=Script::GetStructure(TrickChecksum);
									if (pTrick)
									{
										// Passing the flag IsExtra to the script, so that the script can tell
										// whether it was run as an extra trick or a regular trick.
										if (RunTrick(pTrick,0x7a16aca0/*IsExtra*/))
										{
											TriggeredATrick=true;
										}	
									}
								}
							}
						}
						else
						{
							TriggeredATrick=true;
						}	
					}
							
					// Check the status of mGotExtraTricks again, because it might have got
					// reset by a call to KillExtraTricks in any script run above.		
					if (!mGotExtraTricks)
					{
						return TriggeredATrick;
					}	
				}	
			}
		}
	}	
	
	return TriggeredATrick;
}

// Called every frame.
void CTrickComponent::TriggerAnyExtraTricks()
{
	

	// Check whether any special extra tricks need to get triggered.
	if (mGotExtraTricks)
	{
		if (!mExtraTricksInfiniteDuration && Tmr::ElapsedTime(mExtraTricksStartTime)>mExtraTricksDuration)
		{
			// If not infinite duration, and the time is up, then stop checking for extra tricks.
			mGotExtraTricks=false;
		}
		else
		{
			// Check any special extra tricks if there are some & we're special.
			if (mSpecialExtraTricksArrayChecksum)
			{
				Mdl::Score *pScore=GetScoreObject();
				if (pScore->GetSpecialState())
				{
					// Set this flag so that any trick scripts that do get executed during the TriggerAnyExtraTrick
					// will use the yellow text if they execute a Display command. 
					mUseSpecialTrickText=true;
					if (!TriggerAnyExtraTrick(mSpecialExtraTricksArrayChecksum,mExcludedSpecialExtraTricks))
					{
						// No trick was run, so reset this flag just to be sure that it doesn't cause a non-special
						// trick triggered later to get displayed in yellow.
						mUseSpecialTrickText=false;
					}	
					
					// Check the status of mGotExtraTricks again, because it might have got
					// reset by a call to KillExtraTricks in any script run above.		
					if (!mGotExtraTricks)
					{
						return;
					}	
				}	
				else
				{
					mUseSpecialTrickText=false;
				}	
			}
			
			// Check all the extra-trick arrays
			for (int i=0; iGetFloat(CRCD(0x79a07f3f,"Duration"),&Duration))
	{
		mExtraTricksInfiniteDuration=false;
		mExtraTricksStartTime=Tmr::ElapsedTime(0);
		// Convert Duration, which is assumed to be a number of 60ths, to milliseconds.
		mExtraTricksDuration=(uint32)(Duration*100/6.0f);
	}	

	mNumExtraTrickArrays=0;

	// If a single Tricks parameter is specified, then use that. 
	uint32 ExtraTrickArrayChecksum=0;
	if (pParams->GetChecksum(CRCD(0x1e26fd3e,"Tricks"),&ExtraTrickArrayChecksum))
	{
		mNumExtraTrickArrays=1;
		mpExtraTrickArrays[0]=ExtraTrickArrayChecksum;
	}
	else
	{
		// Otherwise, assume all the un-named names in the parameter list are the trick arrays required.
		Script::CComponent *pComp=NULL;
		while (true)
		{
			pComp=pParams->GetNextComponent(pComp);
			if (!pComp)
			{
				break;
			}
		
			if (pComp->mNameChecksum==0 && pComp->mType==ESYMBOLTYPE_NAME)
			{
				// Found a name, so add it to the array.
				Dbg_MsgAssert(mNumExtraTrickArraysGetScriptInfo()));
				Dbg_MsgAssert(pComp->mChecksum,("Zero checksum ???"));
				mpExtraTrickArrays[mNumExtraTrickArrays++]=pComp->mChecksum;
			}	
		}
	}

	// Set any special extra tricks required.
	mSpecialExtraTricksArrayChecksum=0;
	pParams->GetChecksum(CRCD(0xb394c01c,"Special"),&mSpecialExtraTricksArrayChecksum);
	
	// Make sure no tricks are going to be excluded first of all ...
	for (int i=0; iGetLocalText(CRCD(0xf277291d,"Ignore"),&pExtraTrickToIgnore))
	{
		ExcludeExtraTricks(pExtraTrickToIgnore);
	}	
	else if (pParams->GetArray(CRCD(0xf277291d,"Ignore"),&pArrayOfTricksToIgnore))
	{
		Dbg_MsgAssert(pArrayOfTricksToIgnore,("Eh ? NULL pArrayOfTricksToIgnore ??"));
		int Size=pArrayOfTricksToIgnore->GetSize();
		for (int i=0; iGetLocalString(i));
		}
	}		
}
	
void CTrickComponent::KillExtraTricks()
{
	
	mGotExtraTricks=false;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// 									Extra grind trick stuff
//
////////////////////////////////////////////////////////////////////////////////////////////////////

// Called by the ClearExtraGrindTrick script command.
void CTrickComponent::ClearExtraGrindTrick()
{
	mGotExtraGrindTrick=false;
	mNumExtraGrindTrickArrays=0;
	
	// Just to be sure, zero the lot of 'em.
	for (int i=0; iGetStructure(mExtraGrindTrick.Index);
			Dbg_MsgAssert(pStruct,("NULL pStruct ???"));
			
			Script::CArray *pScriptArray=NULL;
			pStruct->GetArray(CRCD(0x22e168c1,"Scripts"),&pScriptArray);
			
			// The scripts array can alternatively be specified using a template array and a prefix.
			Script::CArray *p_template_array=NULL;
			pStruct->GetArray(CRCD(0x689fe07c,"Template"),&p_template_array);
			const char *p_prefix=NULL;
			pStruct->GetString(CRCD(0x6c4e7971,"Prefix"),&p_prefix);
			
			if (!(pScriptArray || p_template_array))
			{
				// No array parameters found, so look for a TrickSlot
				uint32 TrickSlotChecksum=0;
				pStruct->GetChecksum(CRCD(0xa92a2280,"TrickSlot"),&TrickSlotChecksum);
				if (TrickSlotChecksum)
				{
					// Look up this slot in the trick mappings ...
					Dbg_MsgAssert(mpTrickMappings,("NULL mpTrickMappings ?"));
					uint32 TrickChecksum=0;
					mpTrickMappings->GetChecksum(TrickSlotChecksum,&TrickChecksum);
				
					if (TrickChecksum)
					{
						// Now, look up the global structure with this name.
						Script::CStruct *pFoo=Script::GetStructure(TrickChecksum);
						if (pFoo)
						{
							pFoo->GetArray(CRCD(0x22e168c1,"Scripts"),&pScriptArray);
							pFoo->GetArray(CRCD(0x689fe07c,"Template"),&p_template_array);
							pFoo->GetString(CRCD(0x6c4e7971,"Prefix"),&p_prefix);
						}
					}		
				}
			}	

			// Calculate the index into the scripts array, which ahs size 16
			uint Index=0;
			if (Right) Index|=1;
			if (Parallel) Index|=2;
			if (Backwards) Index|=4;
			if (Regular) Index|=8;

				
			uint32 script_checksum=0;
			if (pScriptArray)
			{
				script_checksum=pScriptArray->GetNameChecksum(Index);
			}
			else
			{
				// The grind script arrays all tend to follow the same pattern, with all the
				// script names having a common prefix, with the suffix following a fixed pattern.
				// So to save Scott having to add a whole new array for each new grind trick, a
				// template array can be specified instead.
				// This template array is an array of 16 suffix strings. The full script name can 
				// then be calculated given the prefix.
				Dbg_MsgAssert(p_template_array,("No template array found for entry %d of array '%s'",mExtraGrindTrick.Index,Script::FindChecksumName(mExtraGrindTrick.ArrayChecksum)));
				Dbg_MsgAssert(p_prefix,("No script prefix specified for entry %d of array '%s'",mExtraGrindTrick.Index,Script::FindChecksumName(mExtraGrindTrick.ArrayChecksum)));
				
				// Look up the suffix to use.
				const char *p_suffix=p_template_array->GetString(Index);
				char p_temp[100];
				Dbg_MsgAssert(strlen(p_prefix)+strlen(p_suffix)<100,("Oops, grind script name '%s%s' too long",p_prefix,p_suffix));
				// Generate the full script name
				sprintf(p_temp,"%s%s",p_prefix,p_suffix);
				// and hence calculate the script name checksum.
				script_checksum=Script::GenerateCRC(p_temp);
			}
				
			Script::CStruct *pParams=NULL;
			pStruct->GetStructure(CRCD(0x7031f10c,"Params"),&pParams);
			

			// Initialise mGrindTweak, which should get set by a SetGrindTweak command
			// in the script that is about to be run.
			// TODO: Is there a neater way of doing this?
			CCompositeObject *p_object=GetObject();
			if (p_object->GetType()==SKATE_TYPE_SKATER)
			{
				mp_skater_core_physics_component->ResetGrindTweak();
			}

			// Set this flag so that any special trick scripts that do get executed during the mp_script->Update()
			// will use the yellow text if they execute a Display command. 
			mUseSpecialTrickText=mExtraGrindTrick.UseSpecialTrickText;
		
			mp_skater_flip_and_rotate_component->DoAnyFlipRotateOrBoardRotateAfters(); // <- See huge comment above definition of this function.
			
			p_object->SwitchScript(script_checksum,pParams);
			p_object->GetScript()->Update();
			
			// Set the mDoingTrick flag so that the camera can detect that a trick is being done.
			mp_skater_state_component->SetDoingTrick( true );

			// Increment the trick count, which may set pedestrian exceptions to
			// make them go "oooOOOOOooooh"
			IncrementNumTricksInCombo();
			
			return true;
		}
	}
	
	return false;
}

// Checks a single array of grind tricks.
void CTrickComponent::MaybeQueueExtraGrindTrick(uint32 ArrayChecksum, bool UseSpecialTrickText)
{
	
	Dbg_MsgAssert(ArrayChecksum,("Zero ArrayChecksum sent to MaybeQueueExtraGrindTrick"));

	// Resolve the checksum into a CArray pointer.
	// The array is stored by checksum in case it gets reloaded.
	Script::CArray *pArray=Script::GetArray(ArrayChecksum);
		
	// The above would have asserted if the array wasn't found, but check pArray isn't NULL anyway.
	if (pArray)
	{
		// Scan through the array checking each trick.
		int Size=pArray->GetSize();
		for (int t=0; tGetStructure(t);
			Dbg_MsgAssert(pStruct,("NULL pStruct ???"));
			// pStruct is the structure defining the trick.
			
			if (TrickIsDefined(pStruct))
			{
				// Get the trigger, which is a structure defining the button combination that triggers the trick.
				Script::CStruct *p_trigger=get_trigger_structure(pStruct);
				// An alternate trigger may also be specified. This will also trigger the trick.
				Script::CStruct *p_alternate_trigger=get_alternate_trigger_structure(pStruct);
				
				// p_trigger could be NULL, but OK cos QueryEvents will return false in that case.
				if (QueryEvents(p_trigger,USED_BY_EXTRA_GRIND_TRICK,~USED_BY_MANUAL_TRICK) ||
					QueryEvents(p_alternate_trigger,USED_BY_EXTRA_GRIND_TRICK,~USED_BY_MANUAL_TRICK))
				{
					// The conditions are met, so add the special grind trick.
					mExtraGrindTrick.ArrayChecksum=ArrayChecksum;
					mExtraGrindTrick.Index=t;
					mExtraGrindTrick.Time=Tmr::GetTime();
					mExtraGrindTrick.Duration=0xffffffff;
					mExtraGrindTrick.UseSpecialTrickText=UseSpecialTrickText;
					pStruct->GetInteger(0x79a07f3f/*Duration*/,(int*)&mExtraGrindTrick.Duration);
					mGotExtraGrindTrick=true;
				}
			}	
		}
	}
}
	
// Always called every frame.
void CTrickComponent::MaybeQueueExtraGrindTrick()
{
	
	
	// If got a special special grind trick array, and we're special, check it.
	if (mSpecialExtraGrindTrickArrayChecksum)
	{
		Mdl::Score *pScore=GetScoreObject();
		if (pScore->GetSpecialState())
		{
			MaybeQueueExtraGrindTrick(mSpecialExtraGrindTrickArrayChecksum,USE_SPECIAL_TRICK_TEXT);
		}	
	}
	
	// Note: Should this function carry on checking once mGotExtraGrindTrick becomes true?
	// Currently it does, so the first trick triggered will be overridden by any further special
	// grind. Check with Scott.
	
	// For each of the trick arrays, check whether any of the tricks listed within are triggered.
	for (int i=0; imExtraGrindTrick.Duration)
		{
			// It's past its sell by date, so kill it.
			mGotExtraGrindTrick=false;
		}	
	}	
}

////////////////////////////////////////////////////////////////////////////////////////////////////

// Returns true if the passed trick has the name pIgnoreName.
bool CTrickComponent::IsExcluded(Script::CStruct *pTrick, const char *pIgnoreName)
{
	
	
	Dbg_MsgAssert(pTrick,("NULL pTrick"));
	
	// See if it has a trick slot.
	uint32 TrickSlotChecksum=0;
	if (pTrick->GetChecksum(0xa92a2280/*TrickSlot*/,&TrickSlotChecksum))
	{
		// Look up this slot in the trick mappings ...
		Dbg_MsgAssert(mpTrickMappings,("NULL mpTrickMappings ?"));
		uint32 TrickChecksum=0;
		mpTrickMappings->GetChecksum(TrickSlotChecksum,&TrickChecksum);
							
		if (TrickChecksum)
		{
			// Now, look up the global structure with this name.
			pTrick=Script::GetStructure(TrickChecksum);
			if (!pTrick)
			{
				return false;
			}
		}
	}

	Script::CStruct *pParams=NULL;
	Dbg_MsgAssert(pTrick,("Eh ?  NULL pTrick"));
	pTrick->GetStructure(CRCD(0x7031f10c,"Params"),&pParams);
	if (pParams)
	{
		const char *pName=NULL;
		pParams->GetLocalText(CRCD(0xa1dc81f9,"Name"),&pName);
		if (pName)
		{
			Dbg_MsgAssert(pIgnoreName,("NULL pIgnoreName"));
			// Compare pName and pIgnoreName.
			// If they match, return true so that the trick gets excluded.
			if (stricmp(pName,pIgnoreName)==0)
			{
				return true;
			}	
		}
	}		
		
	return false;
}				

// Returns a bitfield indicating which of the entries in the passed array are excluded because they have the passed Id.
uint32 CTrickComponent::CalculateIgnoreMask(uint32 ArrayChecksum, const char *pIgnoreName)
{
	
	Dbg_MsgAssert(ArrayChecksum,("Zero ArrayChecksum sent to CalculateIgnoreMask"));
	Script::CArray *pArray=Script::GetArray(ArrayChecksum);
	
	int Size=pArray->GetSize();
	// Only 32 bits available ...
	Dbg_MsgAssert(Size<=32,("Extra-trick array '%s' has more than 32 entries",Script::FindChecksumName(ArrayChecksum)));
			
	uint32 Mask=0;	
	for (int i=0; iGetStructure(i);
		if (IsExcluded(pTrick,pIgnoreName))
		{
			Mask |= (1<GetInputMask();
	
	uint32 Direction = CSkaterPad::sGetDirection(
		input_mask & Inp::Data::mA_UP,
		input_mask & Inp::Data::mA_DOWN,
		input_mask & Inp::Data::mA_LEFT,
		input_mask & Inp::Data::mA_RIGHT
	);
	
	ButtonRecord(PAD_U, Direction == PAD_U);
	ButtonRecord(PAD_D, Direction == PAD_D);
	ButtonRecord(PAD_L, Direction== PAD_L);
	ButtonRecord(PAD_R, Direction == PAD_R);
	ButtonRecord(PAD_UL, Direction == PAD_UL);
	ButtonRecord(PAD_UR, Direction == PAD_UR);
	ButtonRecord(PAD_DL, Direction == PAD_DL);
	ButtonRecord(PAD_DR, Direction == PAD_DR);
	
	ButtonRecord(PAD_CIRCLE, input_mask & Inp::Data::mA_CIRCLE);
	ButtonRecord(PAD_SQUARE, input_mask & Inp::Data::mA_SQUARE);
	ButtonRecord(PAD_TRIANGLE, input_mask & Inp::Data::mA_TRIANGLE);
	ButtonRecord(PAD_X, input_mask & Inp::Data::mA_X);
	ButtonRecord(PAD_L1, input_mask & Inp::Data::mA_L1);
	ButtonRecord(PAD_L2, input_mask & Inp::Data::mA_L2);
	ButtonRecord(PAD_L3, input_mask & Inp::Data::mA_L3);
	ButtonRecord(PAD_R1, input_mask & Inp::Data::mA_R1);
	ButtonRecord(PAD_R2, input_mask & Inp::Data::mA_R2);
	ButtonRecord(PAD_R3, input_mask & Inp::Data::mA_R3);
	ButtonRecord(PAD_BLACK, input_mask & Inp::Data::mA_BLACK);
	ButtonRecord(PAD_WHITE, input_mask & Inp::Data::mA_WHITE);
	ButtonRecord(PAD_Z, input_mask & Inp::Data::mA_Z);
}

void CTrickComponent::DumpEventBuffer()
{
	printf("Event buffer:\n");
	int Index=mLastEvent;
	int n=mNumEvents;
	if (n>10) n=10;
	for (int i=0; iSetAnimSpeed(GetBashFactor(), true);
}

// Calculates the amount by which the skater's animation needs to be sped up
// due to the player bashing buttons.
// The animation speed needs to be multiplied by the value that this returns.
float CTrickComponent::GetBashFactor()
{
	uint32 Duration=GetPhysicsInt(CRCD(0x6c83fdbf,"BashPeriod"));
	
	// Scan through all the events in the last BashPeriod milliseconds,
	// counting all the button press and release events.
	int Bashes=0;
	int Index=mLastEvent;
	for (int i=0; i=Duration)
		{
			break;
		}
	
		if (mpButtonEvents[Index].EventType==EVENT_BUTTON_PRESSED ||
			mpButtonEvents[Index].EventType==EVENT_BUTTON_RELEASED)
		{
			// Flag the event as used, so that it won't register once the bail has finished.
			mpButtonEvents[Index].Used=0xffffffff;
			++Bashes;
		}	
		
		--Index;
		if (Index<0)
		{
			Index+=MAX_EVENTS;
		}	
	}
	
	// Increase the anim speed based on the number of bashes.
	
	float bash_factor=Bashes*GetPhysicsFloat(CRCD(0xced14273, "BashSpeedupFactor"));
	float max=GetPhysicsFloat(CRCD(0xd24abfa3, "BashMaxPercentSpeedup"))/100.0f;
	if (bash_factor > max)
	{
		bash_factor=max;
	}	
	
	return 1.0f+bash_factor;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently isfrequently the contents of a node
// but you can pass in anything you like.	
void CTrickComponent::InitFromStructure( Script::CStruct* pParams )
{
	Dbg_MsgAssert(GetObject()->GetType() == SKATE_TYPE_SKATER, ("CTrickComponent added to non-skater composite object"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickComponent::Update()
{
	
	// nonlocal clients have a SkaterCorePhysicsComponent only to provide uber_frig functionality
	if (!GetSkater()->IsLocalClient())
	{
		Suspend(true);
		return;
	}
	
	RecordButtons();
	
	mp_skater_balance_trick_component->ExcludeBalanceButtons(mNumButtonsToIgnore, mpButtonsToIgnore);
	
	TriggerAnyExtraTricks();
	AddTricksToQueue();
	MaybeQueueManualTrick();
	MaybeExpireManualTrick();
	MaybeQueueExtraGrindTrick();
	MaybeExpireExtraGrindTrick();
	HandleBashing();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CTrickComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | DumpEventBuffer | dumps the last ten button events
		case 0x72c926ca: // DumpEventBuffer
			DumpEventBuffer();
			break;

        // @script | Held | true if the specified button is being held
        // @uparmopt name | Single button name
		// @parmopt array | Buttons | | Optional array of button names. If specified, then if
		// any of those buttons are held it will return true.
		case 0xeda2772e: // Held
		{
			Script::CArray *p_array=NULL;
			if (pParams->GetArray(CRCD(0xbca37a49,"Buttons"),&p_array))
			{
				// If an array of buttons is specified, check each of them.
				int n=p_array->GetSize();
				for (int i=0; iGetChecksum(i)))
					{
						return CBaseComponent::MF_TRUE;
					}
				}
				return CBaseComponent::MF_FALSE;		
			}
			else
			{
			uint32 ButtonChecksum=0;
			pParams->GetChecksum(NONAME,&ButtonChecksum);
			if (!ButtonChecksum)
			{
				return CBaseComponent::MF_FALSE;
			}	
			return GetButtonState(ButtonChecksum) ? CBaseComponent::MF_TRUE:CBaseComponent::MF_FALSE;
			}	
			break;
		}
			
        // @script | Released | true if the specified button is released
        // @uparm X | button name
		case 0x4ba9ee9: // Released	
		{
			uint32 ButtonChecksum=0;
			pParams->GetChecksum(NONAME,&ButtonChecksum);
			if (!ButtonChecksum)
			{
				return CBaseComponent::MF_FALSE;
			}	
			return GetButtonState(ButtonChecksum) ? CBaseComponent::MF_FALSE:CBaseComponent::MF_TRUE;
			break;
		}

/*
        // @script | DoNextTrick | runs the next trick in the queue
		// @parmopt name | ScriptToRunFirst | | Script that will be run first before any trick script.
		// @parmopt structure | Params | | Parameters to pass to ScriptToRunFirst
		
		case 0x09d58f25: // DoNextTrick
		{
			uint32 scriptToRunFirst=0;
			Script::CStruct *pScriptToRunFirstParams=NULL;
			pParams->GetChecksum(CRCD(0x148fee96,"ScriptToRunFirst"),&scriptToRunFirst);
			pParams->GetStructure(CRCD(0x7031f10c,"Params"),&pScriptToRunFirstParams);
			TriggerNextQueuedTrick(scriptToRunFirst, pScriptToRunFirstParams);
			break;
		}
*/
		
         // @script | ClearTricksFrom | 
		case 0xcb30572d: // ClearTricksFrom
		{
			// Run through all the parameters.
			Script::CComponent *pComp=NULL;
			while (true)
			{
				pComp=pParams->GetNextComponent(pComp);
				if (!pComp)
				{
					break;
				}
			
				if (pComp->mNameChecksum==0 && pComp->mType==ESYMBOLTYPE_NAME)
				{
					// It's an unnamed name, so remove the tricks that came from the array with that name.
					ClearTricksFrom(pComp->mChecksum);
				}	
			}
			break;
		}

        // @script | SetQueueTricks | sets the tricks queue
        // @uparmopt name | trick array.  if no array specified
        // a default air tricks array will be used
        // @parmopt name | Special | | special tricks array
		case 0xd8c79bb0: // SetQueueTricks
		{
			mNumQueueTricksArrays=0;

			Script::CComponent *pComp=NULL;
			while (true)
			{
				pComp=pParams->GetNextComponent(pComp);
				if (!pComp)
				{
					break;
				}
			
				if (pComp->mNameChecksum==0 && pComp->mType==ESYMBOLTYPE_NAME)
				{
					// Found a name, so add it to the array.
					Dbg_MsgAssert(mNumQueueTricksArraysGetScriptInfo()));
					Dbg_MsgAssert(pComp->mChecksum,("Zero checksum ???"));
					mpQueueTricksArrays[mNumQueueTricksArrays++]=pComp->mChecksum;
				}	
			}
			
			if (mNumQueueTricksArrays==1)
			{
				// Check if the first array exists. If not, use DefaultAirTricks instead. This array is required to exist, otherwise
				// AddTricksToQueue will assert.
				
				Script::CArray *pArray=Script::GetArray(mpQueueTricksArrays[0]);
				if (!pArray)
				{
					mpQueueTricksArrays[0]=CRCD(0x9e22d268,"DefaultAirTricks");
				}	
			}	
			
			// Set any special tricks array required.
			mSpecialTricksArrayChecksum=0;
			pParams->GetChecksum(CRCD(0xb394c01c,"Special"),&mSpecialTricksArrayChecksum);
			break;
		}

        // @script | UseGrindEvents | 
		case 0xd4099d35: // UseGrindEvents
			mDoNotIgnoreMask=USED_BY_EXTRA_GRIND_TRICK;		
			break;
			
        // @script | AddTricksToQueue | This will check the button events and add any triggered tricks 
		// to the trick queue. The c-code actually does this every frame anyway, but this function
		// allows it to be done immediately when necessary.
		case 0xde98de7a: // AddTricksToQueue
			AddTricksToQueue();
			break;
			
        // @script | ClearTrickQueue | clears the trick queue
		case 0x54c33b20: // ClearTrickQueue
			ClearTrickQueue();		
			break;
			
        // @script | DoNextManualTrick | does next trick while in a manual
		case 0x788f9a4e: //	DoNextManualTrick
			TriggerAnyManualTrick(pParams);
			break;
			
        // @script | SetManualTricks | 
        // @uparm name | trick array
		case 0x1fcf080a: // SetManualTricks
		{
			mNumManualTrickArrays=0;

			Script::CComponent *pComp=NULL;
			while (true)
			{
				pComp=pParams->GetNextComponent(pComp);
				if (!pComp)
				{
					break;
				}
			
				if (pComp->mNameChecksum==0 && pComp->mType==ESYMBOLTYPE_NAME)
				{
					// Found a name, so add it to the array.
					Dbg_MsgAssert(mNumManualTrickArraysGetScriptInfo()));
					Dbg_MsgAssert(pComp->mChecksum,("Zero checksum ???"));
					mpManualTrickArrays[mNumManualTrickArrays++]=pComp->mChecksum;
				}	
			}
			
			// Set any special manual tricks array required.
			mSpecialManualTricksArrayChecksum=0;
			pParams->GetChecksum(CRCD(0xb394c01c,"Special"),&mSpecialManualTricksArrayChecksum);
			break;
		}
				
        // @script | ClearManualTrick | 
		case 0x8e3d3bec: // ClearManualTrick
			ClearManualTrick();
			break;
			
		/////////////////////////////////////////////////////////////////////////////////////////////
		//
		// Extra-Grind trick stuff, which has a very similar interface to the manual stuff above,
		// except that there is no script command corresponding to DoNextManualTrick because the
		// C-code will automatically run any queued extra grind trick when it snaps the player
		// to a rail in CSkater::MaybeStickToRail
		//
		/////////////////////////////////////////////////////////////////////////////////////////////

        // @script | SetExtraGrindTricks | 
        // @uparm name | trick array
		case 0x6ba35ab4: // SetExtraGrindTricks
		{
			mNumExtraGrindTrickArrays=0;

			Script::CComponent *pComp=NULL;
			while (true)
			{
				pComp=pParams->GetNextComponent(pComp);
				if (!pComp)
				{
					break;
				}
			
				if (pComp->mNameChecksum==0 && pComp->mType==ESYMBOLTYPE_NAME)
				{
					// Found a name, so add it to the array.
					Dbg_MsgAssert(mNumExtraGrindTrickArraysGetScriptInfo()));
					Dbg_MsgAssert(pComp->mChecksum,("Zero checksum ???"));
					mpExtraGrindTrickArrays[mNumExtraGrindTrickArrays++]=pComp->mChecksum;
				}	
			}
			
			mSpecialExtraGrindTrickArrayChecksum=0;
			pParams->GetChecksum(CRCD(0xb394c01c,"Special"),&mSpecialExtraGrindTrickArrayChecksum);
			break;
		}
				
        // @script | ClearExtraGrindTrick | 
		case 0xab6b8dd9: // ClearExtraGrindTrick
			ClearExtraGrindTrick();
			break;

        // @script | ClearEventBuffer |
        // @parmopt array | Buttons | | buttons array
        // @parmopt int | OlderThan | 0 | used in conjuction with optional buttons array
		case 0x86928082: // ClearEventBuffer	
		{
			Script::CArray *pButtonArray=NULL;
			if (pParams->GetArray(CRCD(0xbca37a49,"Buttons"),&pButtonArray))
			{
				int OlderThan=0;
				pParams->GetInteger(CRCD(0xcdcfee8a,"OlderThan"),&OlderThan);
				int Size=pButtonArray->GetSize();
				for (int i=0; iGetNameChecksum(i),OlderThan);
				}
			}
			else
			{
				ClearEventBuffer();
			}	
			break;
		}

        // @script | RemoveXEvents | Run through all the recorded button
        // events, and remove any involving button X.
		// This is used as an easy way of fixing the boneless bug.
	    // This function is called at the start of the land script, 
        // which prevents any previously
		// queued bonelesses from being detected.
		case 0x70e934d: // RemoveXEvents
		{
			for (int i=0; iGetInteger(CRCD(0x79a07f3f,"Duration"),&duration);
			
			uint32 used_by=0;
			pParams->GetChecksum(CRCD(0x9694fc41,"UsedBy"),&used_by);
			
			uint32 mask=0;
			switch (used_by)
			{
				case 0xb58efc2b: // Regular
					mask=USED_BY_REGULAR_TRICK;
					break;
				case 0xb2c0f29a: // Extra
					mask=USED_BY_EXTRA_TRICK;
					break;
				case 0xef24413b: // Manual
					mask=USED_BY_MANUAL_TRICK;
					break;
				case 0xec066b6a: // ExtraGrind
					mask=USED_BY_EXTRA_GRIND_TRICK;
					break;
				default:
					break;
			}
				
			int index=mLastEvent;
			for (int i=0; i= (uint32)duration)
				{
					break;
				}
		
				if (mpButtonEvents[index].Used & mask)
				{
					mpButtonEvents[index].Used&=~mask;
				}	
				
				--index;
				if (index<0)
				{
					index+=MAX_EVENTS;
				}	
			}
			
			break;
		}

        // @script | SetExtraTricks | 
        // @parmopt float | Duration | | 
        // @parmopt name | Tricks | | trick array
        // @parmopt name | Special | | special tricks array
        // @parmopt local string | Ignore | | trick to ignore
        // @parmopt array | Ignore | | array of tricks to ignore
		case 0x7627dc71: // SetExtraTricks
			SetExtraTricks(pParams,pScript);
			break;
			
        // @script | KillExtraTricks | 
		case 0xd5813251: // KillExtraTricks
			KillExtraTricks();
			break;

        // @script | SetSlotTrick | 
        // @parm name | Slot | slot name
        // @parm name | Trick | 
		case 0xb42a1230: // SetSlotTrick
		{
			uint32 Slot=0;
			pParams->GetChecksum(CRCD(0x53f1df98,"Slot"),&Slot);
			Dbg_MsgAssert(Slot,("\n%s\nSetSlotTrick requires a slot name",pScript->GetScriptInfo()));
			uint32 Trick=0;
			pParams->GetChecksum(CRCD(0x270f56e1,"Trick"),&Trick);
			
			Dbg_MsgAssert(mpTrickMappings,("NULL mpTrickMappings"));
			// This will just replace the slot if it exists already.
			mpTrickMappings->AddComponent(Slot,(uint8)ESYMBOLTYPE_NAME,Trick);
			break;
		}

        // @script | ChangeProTricks | 
		case 0xabff7889: // ChangeProTricks 
		{
			// Have to use GetNextComponent rather than GetChecksum, because GetChecksum won't work if
			// the checksum is unnamed, because by convention an unnamed checksum that resolves to a structure
			// effectively makes that structure part of the params, & the name of the structure is just considered
			// an intermediate thing which is not part of the params.
			Script::CComponent *pComp=pParams->GetNextComponent(NULL);
			if (pComp && pComp->mNameChecksum==0 && pComp->mType==ESYMBOLTYPE_NAME)
			{
				Script::CStruct *pNewTrickMappings=Script::GetStructure(pComp->mChecksum);
				if (pNewTrickMappings)
				{
					// Found the new structure, so load it in to mpTrickMappings.
					Dbg_MsgAssert(mpTrickMappings,("NULL mpTrickMappings"));
					mpTrickMappings->Clear();
					mpTrickMappings->AppendStructure(pNewTrickMappings);
				}
				else
				{
					#ifdef __NOPT_ASSERT__
					printf("\n%s\nWarning: Protrick mapping '%s' not found\n",pScript->GetScriptInfo(),Script::FindChecksumName(pComp->mChecksum));
					#endif
				}
			}
			else
			{
				#ifdef __NOPT_ASSERT__
				printf("\n%s\nWarning: ChangeProTricks command requires a name.\n",pScript->GetScriptInfo());
				#endif
			}			
			break;
		}
					
        // @script | BashOn | turns bash on (for speeding up bails?)
		case 0xd872002d: // BashOn
			mBashingEnabled=true;
			// Clear the event buffer so that button presses done before the bail will not
			// speed it up.
			ClearEventBuffer();
			break;
			
        // @script | BashOff | turns bash off
		case 0x290f6010: // BashOff
			mBashingEnabled=false;
			break;
		
        // @script | SetTrickName | 
        // @uparmopt 'local string' | trick name
        // @uparmopt "string" | trick name
		case 0x4b2ee2bc: // SetTrickName
		{
			const char *pName=NULL;
			pParams->GetString(NO_NAME,&pName);
			// Make it accept regular strings too, because trick names probably won't get translated.
			if (pName)
			{
				Dbg_MsgAssert(strlen(pName)GetScriptInfo(),MAX_TRICK_NAME_CHARS));
				strcpy(mpTrickName, pName);
			}
			else
			{
				mpTrickName[0]=0;
			}
			break;
		}

        // @script | SetTrickScore | sets trick score flag
        // @uparm 1 | trick score
		case 0xcb3a8fd2: // SetTrickScore
			pParams->GetInteger(NO_NAME,&mTrickScore);	  
			break;
                          
        // @script | GetSpin | Gets the current spin value, and adds it as an integer parameter
		// called Spin. It will always be positive and a multiple of 180. It takes into account
		// the spin slop.
		case 0x4ba7719f: // GetSpin
		{
			int spin = static_cast< int >(Mth::Abs(mTallyAngles) + GetPhysicsFloat(CRCD(0x50c5cc2f, "spin_count_slop")));
			pScript->GetParams()->AddFloat(CRCD(0xedf5db70, "Spin"), (spin / 180) * 180.0f);
			break;
		}
		
        // @script | ResetSpin | 
		case 0xe50d471e: // ResetSpin
			mTallyAngles = 0.0f;
			break;
		
        // @script | Display | display trick
        // @flag Deferred | If deferred (currently only used for the ollie)
        // then store it away, because we may not want to add it to the
        // combo after all. Eg, a 180 ollie on its own is counted,
		// but if you do a 180 kickflip, you'll get the kickflip but not 
        // the ollie as well. 
        // @flag BlockSpin |
		// @parmopt int | AddSpin | 0 | If this is specified and the current trick is the
		// same as the last trick, then the spin will be added to the last trick rather than
		// adding it as a new trick.
		case 0xf32e8d5c: // Display				  
		{
			if (mpTrickName)
			{
				Mdl::Score *pScore=GetScoreObject();
				int spin=0;
				
				if (pParams->ContainsFlag(CRCD(0x5c9b1765,"Deferred")))
				{
					strcpy(mpDeferredTrickName,mpTrickName);
					mDeferredTrickScore=mTrickScore;
					if (mp_skater_core_physics_component->IsSwitched())
					{
						mDeferredTrickFlags|=Mdl::Score::vSWITCH;
					}
				}
				// If an AddSpin value is specified and the current trick is the same as
				// the last one in the score, then instead of adding the trick again just
				// give the last one some more spin. Used by the truckstand2 spin.
				// (See the ManualLink script in groundtricks.q)
				else if (pParams->GetInteger(CRCD(0x83cb0082,"AddSpin"),&spin) && 
						 pScore->GetLastTrickId()==Script::GenerateCRC(mpTrickName))
				{
					mTallyAngles+=spin;
					if (GetSkater()->IsLocalClient())
					{
						pScore->UpdateSpin(mTallyAngles);
					}	
				}
				else
				{
					Mdl::Score::Flags Flags=0;
					if (pParams->ContainsFlag(CRCD(0xc2150bb0,"BlockSpin")))
					{
						Flags|=Mdl::Score::vBLOCKING;
					}
					if (pParams->ContainsFlag(CRCD(0x3c24ac46,"NoDegrade")))
					{
						Flags|=Mdl::Score::vNODEGRADE;
					}	
					if (mp_skater_core_physics_component->IsSwitched())
					{
						Flags|=Mdl::Score::vSWITCH;
					}
					if (mUseSpecialTrickText)
					{
						Flags|=Mdl::Score::vSPECIAL;
					}
					if ( pParams->ContainsFlag( CRCD(0x61a1bc57,"cat") ) )
					{
						Flags |= Mdl::Score::vCAT;
					}
					
					// Need to also include nollie check later.
						
					if( GetSkater()->IsLocalClient())
					{
						// if on a rail, then might need to degrade the rail score
						if (mp_skater_core_physics_component->GetState()==RAIL && mTrickScore)
						{
							printf ("RAIL score %d * %2.3f = %d\n", mTrickScore,pScore->GetRobotRailMult(),(int) (pScore->GetRobotRailMult()*mTrickScore));
							mTrickScore = (int) (mTrickScore * pScore->GetRobotRailMult());
						}
					
						// add a trick to the series
						pScore->Trigger(mpTrickName, mTrickScore, Flags);
						
						if (mpTrickName[0])
						{
							// tell any observers to do the chicken dance:
							GetObject()->BroadcastEvent(CRCD(0x11d8bc9e, "SkaterTrickDisplayed"));
							
							if (mp_skater_core_physics_component->GetFlag(VERT_AIR) || mp_skater_core_physics_component->GetTrueLandedFromVert())
							{
								// If vert, only count it if the spin is at least 360
								if (Mth::Abs(mTallyAngles)>=360.0f-GetPhysicsFloat(CRCD(0x50c5cc2f,"spin_count_slop")) + 0.1f)
								{
									pScore->SetSpin(mTallyAngles);
								}
							}
							else
							{
								pScore->SetSpin(mTallyAngles);
							}
						}
						else
						{
							// K: If the trick was a 'Null' trick, then do not add the spin.
							// This is to fix TT6057
							// A null trick is a trick whose name is set using SetTrickName ""
							// and is often used to do a BlockSpin without displaying a trick name.
						}
					}
					
					if (pParams->ContainsFlag(CRCD(0xc2150bb0,"BlockSpin")))
					{
						mTallyAngles=0.0f;
					}
						
					// Clear the deferred trick, so that you won't get a 180 kickflip and a 180 ollie.
					//dodgy_test(); printf("Clearing deferred trick\n");
					mpDeferredTrickName[0]=0;
				}	
            }
			
			// Only clear the mUseSpecialTrickText flag if it is not a 'null' trick, which is
			// a trick whose name is just '' used to insert spin blocks.
			// Want to preserve the mUseSpecialTrickText for the next proper trick.
			if (mpTrickName[0])
			{
				mUseSpecialTrickText=false;
			}	
			break;
		}	
		
        // @script | ClearPanel_Landed | end of trick combo...scoring and such
		case 0x11ca5c42: // ClearPanel_Landed
		{
			Mdl::Score *pScore=GetScoreObject();

			if(GetSkater()->IsLocalClient())
			{
				// If there is a deferred trick stored up, then decide whether to add it
				// to the combo depending on how much spin there is.
				if (mpDeferredTrickName[0])
				{
					//dodgy_test(); printf("Got a deferred trick, mTallyAngles=%f\n",mTallyAngles);
					// Note: Using the new 'mp_skater_core_physics_component->m_true_landed_from_vert' rather than 'mp_skater_core_physics_component->mLandedFromVert', because
					// mp_skater_core_physics_component->mLandedFromVert can be cleared by a script command, and if landing from vert it
					// will be cleared at this point, due to some script logic for detecting reverts.
					if (mp_skater_core_physics_component->GetTrueLandedFromVert())
					{
						// If vert, only count it if the spin is at least 360
						if (Mth::Abs(mTallyAngles)>= 360.0f - GetPhysicsFloat(CRCD(0x50c5cc2f,"spin_count_slop")) + 0.1f)	// (Mick) adkisted for slop
						{
							// add the deferred trick to the series
							pScore->Trigger(mpDeferredTrickName, mDeferredTrickScore, mDeferredTrickFlags);
							pScore->SetSpin(mTallyAngles);
						}	
					}
					else
					{
						// Only count it if the spin is at least 180, cos just ollieing is easy.
						if (Mth::Abs(mTallyAngles)>=180.0f - GetPhysicsFloat(CRCD(0x50c5cc2f,"spin_count_slop")) + 0.1f)  // (Mick) adkisted for slop
						{
							// add the deferred trick to the series
							pScore->Trigger(mpDeferredTrickName, mDeferredTrickScore, mDeferredTrickFlags);
							pScore->SetSpin(mTallyAngles);
						}	
					}
						
					// Clear the deferred trick.
					mpDeferredTrickName[0]=0;
				}
			
                // check for special trick if we're in a special trick goal
                Game::CGoalManager* pGoalManager = Game::GetGoalManager();
                Dbg_MsgAssert( pGoalManager, ( "Unable to get goal manager\n" ) );
                pGoalManager->CheckTrickText();
				
				if (pScore->GetScorePotValue() != 0)
				{
					Script::CStruct* p_params = new Script::CStruct;
					p_params->AddChecksum(CRCD(0x5b24faaa, "SkaterId"), GetObject()->GetID());
					GetObject()->BroadcastEvent(CRCD(0x4b3ce1fe, "SkaterExitCombo"), p_params);
					delete p_params;
				}

				///////////////////////////////////////////////////////////////////////
				// K: Warning! I moved AwardPendingGaps() to before Land(), so that AwardPendingGaps()
				// can query the score to see if the required trick was got in the case of the
				// gap being part of a created goal.
				// Possible alternative solution in case this causes problems: Modify the c-code of
				// StartGap so that it detects if the gap is part of a created goal, and if so sets the
				// tricktext according to the trick required by the goal. 
				GetSkaterGapComponentFromObject(GetObject())->AwardPendingGaps();
                
				pScore->Land();
				mTallyAngles=0.0f;		// Mick: Cleared, so a manual will not get it
				///////////////////////////////////////////////////////////////////////
				
				
                mp_stats_manager_component->Land();
			}
			
			// tell any observers to cheer if they want:
			GetObject()->BroadcastEvent( CRCD(0xc98ba111,"skaterLanded"));

			// allows us to end the run if we're in horse mode
			if ( m_first_trick_started )
				m_first_trick_completed = true;

			mNumTricksInCombo=0;
			
			mp_skater_balance_trick_component->UpdateRecord();
			mp_skater_balance_trick_component->Reset();
			
			// Clear the special friction index (used by Reverts)
			mp_skater_core_physics_component->ResetSpecialFrictionIndex();

			break;
		}
		  		
        // @script | ClearPanel_Bailed | trick combo ended in bail...
		case 0xb8045433: // ClearPanel_Bailed
		{
			if( GetSkater()->IsLocalClient())
			{
				if (GetScoreObject()->GetScorePotValue() != 0)
				{
					Script::CStruct* p_params = new Script::CStruct;
					p_params->AddChecksum(CRCD(0x5b24faaa, "SkaterId"), GetObject()->GetID());
					GetObject()->BroadcastEvent(CRCD(0x4b3ce1fe, "SkaterExitCombo"), p_params);
					delete p_params;
				}
				
				GetScoreObject()->Bail();
			}

			// tell any observers to balk if they want:
			GetObject()->BroadcastEvent( CRCD(0x6045a960,"skaterBailed"));
		   
 
			m_first_trick_started = true;
			m_first_trick_completed = true;

			// clear out the graffiti tricks
			SetGraffitiTrickStarted( false );
			
			mNumTricksInCombo=0;
			
			mp_skater_balance_trick_component->Reset();
			
            mp_skater_core_physics_component->StopSkitch();
			
			// Mick: Cleared, so a manual will not get it
			mTallyAngles=0.0f;
			
			// Clear the special friction index (used by Reverts)
			mp_skater_core_physics_component->ResetSpecialFrictionIndex();
			m_pending_tricks.FlushTricks();
			
			GetSkaterGapComponentFromObject(GetObject())->ClearPendingGaps();

            mp_stats_manager_component->Bail();

			break;
		}		

        // @script | TrickOffObject | 
        // @uparm name | object name
		case 0x36b2be57: // TrickOffObject
		{
			uint32 clusterName;
			pParams->GetChecksum( NO_NAME, &clusterName, Script::ASSERT );
			TrickOffObject( clusterName );
			break;
		}

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CTrickComponent::GetDebugInfo"));
	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	p_info->AddStructure("mpTrickMappings",mpTrickMappings);
 
	Script::CArray *p_events_array=new Script::CArray;
	if (mNumEvents)
	{
		p_events_array->SetSizeAndType(mNumEvents,ESYMBOLTYPE_STRUCTURE);
		
		int index=mLastEvent;
		for (int i=0; iAddChecksum(CRCD(0xc5f953c2,"Button"),mpButtonEvents[index].ButtonNameChecksum);
			
			uint32 event_type=0;
			switch (mpButtonEvents[index].EventType)
			{
				case EVENT_NONE:  event_type=CRCD(0x806fff30,"None"); break;
				case EVENT_BUTTON_PRESSED:  event_type=CRCD(0xe4ab4785,"Pressed"); break;
				case EVENT_BUTTON_RELEASED:  event_type=CRCD(0x4ba9ee9,"Released"); break;
				default: break;
			}
			p_event->AddChecksum(NONAME,event_type);

			p_event->AddInteger(CRCD(0x906b67ba,"Time"),mpButtonEvents[index].Time);
			p_event->AddInteger(CRCD(0x86b89ce7,"Used"),mpButtonEvents[index].Used);
				
			p_events_array->SetStructure(i,p_event);
				
			--index;
			if (index<0)
			{
				index+=MAX_EVENTS;
			}
		}		
	}
    p_info->AddArrayPointer(CRCD(0xac78a8b5,"Events"),p_events_array);

	s_add_array_of_names(p_info,mNumQueueTricksArrays,mpQueueTricksArrays,"QueueTrickArrays");
	
	p_info->AddInteger(CRCD(0x50cab1f1,"GotManualTrick"),mGotManualTrick);
	s_add_array_of_names(p_info,mNumManualTrickArrays,mpManualTrickArrays,"ManualTrickArrays");
	p_info->AddChecksum(CRCD(0x9c93be77,"SpecialManualTricksArray"),mSpecialManualTricksArrayChecksum);
	
	p_info->AddInteger(CRCD(0xdf2c1464,"GotExtraGrindTrick"),mGotExtraGrindTrick);
	s_add_array_of_names(p_info,mNumExtraGrindTrickArrays,mpExtraGrindTrickArrays,"ExtraGrindTrickArrays");
	p_info->AddChecksum(CRCD(0x4cb1bfe,"SpecialExtraGrindTrickArray"),mSpecialExtraGrindTrickArrayChecksum);

	p_info->AddInteger(CRCD(0x33660920,"GotExtraTricks"),mGotExtraTricks);
	p_info->AddInteger(CRCD(0x15d82e35,"ExtraTricksInfiniteDuration"),mExtraTricksInfiniteDuration);
	p_info->AddInteger(CRCD(0x4c85f690,"ExtraTricksDuration"),mExtraTricksDuration);
	p_info->AddInteger(CRCD(0x9737fa16,"ExtraTricksStartTime"),mExtraTricksStartTime);
	s_add_array_of_names(p_info,mNumExtraTrickArrays,mpExtraTrickArrays,"ExtraTrickArrays");
	p_info->AddChecksum(CRCD(0x42569716,"SpecialExtraTricksArray"),mSpecialExtraTricksArrayChecksum);
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickComponent::Debounce ( int button, float time )
{
	mButtonState[button] = false;
	mButtonDebounceTime[button] = time;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Increment the trick count, which may set pedestrian exceptions to
// make them go "oooOOOOOooooh"

// Note: This count won't be totally accurate, because if an 'extra' trick is triggered it
// will increment the count too, making the count get incremented more times than it should.
// But this shouldn't be a big problem, cos it'll just mean the croud will more likely to cheer if
// you do lots of extra tricks.
// NOTE: move to trick component
void CTrickComponent::IncrementNumTricksInCombo()
{
	++mNumTricksInCombo;
	if ( mNumTricksInCombo > 3 )
	{
		// tell any observers to start taking notice:
		GetObject()->BroadcastEvent(CRCD(0x94182a08,"skaterStartingRun"));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickComponent::SetGraffitiTrickStarted( bool started )
{
	m_graffiti_trick_started = started;
	if ( !started )
	{
		m_pending_tricks.FlushTricks();
	}
	else
	{
		TrickOffObject( mp_skater_core_physics_component->GetLastNodeChecksum() );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickComponent::TrickOffObject( uint32 node_name )
{
	if ( GraffitiTrickStarted() )
	{
		m_pending_tricks.TrickOffObject( node_name );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickComponent::ClearMiscellaneous()
{
	mNumTricksInCombo = 0;
	m_first_trick_completed = false;
	m_first_trick_started = false;
	SetGraffitiTrickStarted( false );
	m_pending_tricks.FlushTricks();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CTrickComponent::WritePendingTricks( uint32* p_buffer, uint32 max_size )
{
	uint32 size = m_pending_tricks.WriteToBuffer( p_buffer, max_size );

	// resets the graffiti trick list
	SetGraffitiTrickStarted( false );
	
	return size;
}


// Experimenting with a new way of binding script commands
// Doing it like this is twice as fast (11us vs 22 us) which can add up for
// things that are called every frame (like the skater physics scripts) 
// It's a bit cumbersome to do it like this though
// Perhaps there could be a more general database of script commands
// with registration info about which component it goes to.
// and a pointer to the memeber function
 
// @script | DoNextTrick | runs the next trick in the queue
// @parmopt name | ScriptToRunFirst | | Script that will be run first before any trick script.
// @parmopt structure | Params | | Parameters to pass to ScriptToRunFirst
bool ScriptDoNextTrick( Script::CStruct *pParams, Script::CScript *pScript )
{
	CCompositeObject *p_object = (CCompositeObject *) (pScript->mpObject.Convert());
	Dbg_MsgAssert(p_object, ("Can't run DoNextTrick with no object"));
	CTrickComponent* p_trick = GetTrickComponentFromObject(p_object);
	Dbg_MsgAssert(p_trick, ("Can't run DoNextTrick with no object"));
		
	uint32 scriptToRunFirst=0;
	Script::CStruct *pScriptToRunFirstParams=NULL;
	pParams->GetChecksum(CRCD(0x148fee96,"ScriptToRunFirst"),&scriptToRunFirst);
	pParams->GetStructure(CRCD(0x7031f10c,"Params"),&pScriptToRunFirstParams);
	
	Script::CStruct *pExtraTrickParams=NULL;
	pParams->GetStructure(CRCD(0x31261d2f, "TrickParams"),&pExtraTrickParams);
	if (pExtraTrickParams)
	{
		Script::CStruct *pCopyOfExtraTrickParams=pCopyOfExtraTrickParams = new Script::CStruct(*pExtraTrickParams);
		p_trick->TriggerNextQueuedTrick(scriptToRunFirst, pScriptToRunFirstParams, pCopyOfExtraTrickParams);
		delete pCopyOfExtraTrickParams;
	}
	else
	{
		p_trick->TriggerNextQueuedTrick(scriptToRunFirst, pScriptToRunFirstParams);
	}
	return true;
} 
 
}


================================================
FILE: Code/Gel/Components/trickcomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       TrickComponent.h
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#ifndef __COMPONENTS_TRICKCOMPONENT_H__
#define __COMPONENTS_TRICKCOMPONENT_H__

#include 
#include 

#include 
#include 

#ifndef __OBJECT_BASECOMPONENT_H__
#include 
#endif

#ifndef __GEL_INPMAN_H
#include 
#endif

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_TRICK CRCD(0x270f56e1,"Trick")
#define		GetTrickComponent() ((Obj::CTrickComponent*)GetComponent(CRC_TRICK))
#define		GetTrickComponentFromObject(pObj) ((Obj::CTrickComponent*)(pObj)->GetComponent(CRC_TRICK))

namespace Script
{
    class CScript;
    class CStruct;
    class CComponent;
}
              
namespace Mdl
{
	class Score;
};
	
namespace Obj
{

class CSkaterProfile;
class CSkaterBalanceTrickComponent;
class CSkaterFlipAndRotateComponent;
class CSkaterStateComponent;
class CSkaterRunTimerComponent;

// An element of the trick queue.
struct STrick
{
	uint32 ArrayChecksum;	// Checksum of the script array, eg AirTricks
	uint Index;				// Index within the array of the trick.
	
	// These next two are only used by manual tricks and special grind tricks.
	// These have a finite duration. For example, if a manual is queued up early on
	// in a jump, it may expire before the player lands.
	uint32 Time;			// The time when the trick was triggered.
	uint32 Duration;		// How long after Time that the trick will last for.
	bool UseSpecialTrickText; // Whether to use the yellow text when displaying the trick.
};

enum EEventType
{
	// All the events in the buffer have this type initially.
	EVENT_NONE,
	
	EVENT_BUTTON_PRESSED,
	EVENT_BUTTON_RELEASED,
	
	EVENT_BUTTON_PRESSURE_CHANGE,
	
	EVENT_ON_RAIL,
	EVENT_OFF_RAIL,
};

#define MAX_EVENTS 100
struct SButtonEvent
{
	uint32 Time;
	EEventType EventType;
	uint32 ButtonNameChecksum;
	int ButtonPressure;
	bool MaybeUsed;
	uint32 Used;
};

#define USED_BY_REGULAR_TRICK (1<<0)
#define USED_BY_EXTRA_TRICK (1<<1)
#define USED_BY_MANUAL_TRICK (1<<2)
#define USED_BY_EXTRA_GRIND_TRICK (1<<3)

#define USE_SPECIAL_TRICK_TEXT true

class CTrickComponent : public CBaseComponent
{
	// Used to detect infinite recursion of RunTrick
	uint32 m_num_runtrick_recursions;
	
	Mdl::Score *mp_score;
	
	CInputComponent* 				mp_input_component;
	CSkaterBalanceTrickComponent*	mp_skater_balance_trick_component;
	CSkaterCorePhysicsComponent*	mp_skater_core_physics_component;
	CSkaterFlipAndRotateComponent*	mp_skater_flip_and_rotate_component;
	CSkaterStateComponent*			mp_skater_state_component;
    CStatsManagerComponent*			mp_stats_manager_component;
	
	Script::CStruct*				get_trigger_structure(Script::CStruct *p_struct);
	Script::CStruct*				get_alternate_trigger_structure(Script::CStruct *p_struct);
	
public:
	CSkater*						GetSkater (   ) { return static_cast< CSkater* >(GetObject()); }
	
	void UpdateTrickMappings( Obj::CSkaterProfile* pSkaterProfile );

	Mdl::Score *GetScoreObject();
	void SetAssociatedScore(Mdl::Score *p_score) {mp_score=p_score;}

	// Gets called from the CTrickComponent constructor, and also from CSkater::ResetEverything()
	void Clear();

	void RecordButtons();
	void Debounce ( int button, float time );

	// The Button Event buffer.
	SButtonEvent mpButtonEvents[MAX_EVENTS];
	// How many events are in the buffer. When the skater is first created this will be zero,
	// but will increase until it equals MAX_EVENTS then will stay equal to MAX_EVENTS from then on.
	uint16 mNumEvents;
	// Index of the last event in the buffer.
	uint16 mLastEvent;

	// If a trick gets triggered, the buttons which triggered it will be stored here.
	// Eg, for a kickflip, button 0 will be Square (checksum of), and button 1 will be Left.
	// They are stored so that the extra-trick trigger can use one of the parent trick's buttons,
	// rather than being hard wired. This is needed in case the player re-maps the parent trick to
	// a different button combination.
	#define MAX_TRICK_BUTTONS 3
	uint32 mp_trick_button[MAX_TRICK_BUTTONS];
	// Reads the button names and time duration out of a 'Trigger' structure.
	uint32 get_buttons_and_duration(Script::CComponent *p_comp, int num_buttons, uint32 *p_button_names);
	void record_last_tricks_buttons(uint32 *p_button_names);

	float mButtonDebounceTime[PAD_NUMBUTTONS];
	bool mButtonState[PAD_NUMBUTTONS];
	
	bool GetButtonState(uint32 Checksum);

	int mNumButtonsToIgnore;
	#define MAX_BUTTONS_TO_IGNORE 2
	uint32 mpButtonsToIgnore[MAX_BUTTONS_TO_IGNORE];
	void ButtonRecord(uint Button, bool Pressed);
	
	bool TriggeredInLastNMilliseconds(uint32 ButtonNameChecksum, uint32 Duration, uint32 IgnoreMask=0xffffffff);
	bool BothTriggeredNothingInBetween(uint32 Button1, uint32 Button2, uint32 Duration, uint32 IgnoreMask);
	bool QueryEvents(Script::CStruct *pQuery, uint32 UsedMask=USED_BY_REGULAR_TRICK, uint32 IgnoreMask=0xffffffff);
	void ResetMaybeUsedFlags();
	void ConvertMaybeUsedToUsed(uint32 UsedMask);
	void RemoveOldButtonEvents(uint32 Button, uint32 OlderThan);
	SButtonEvent *AddEvent(EEventType EventType);
	void ClearEventBuffer();
	void DumpEventBuffer();
	
	// If set, then the next call to Display will use the yellow text.
	bool mUseSpecialTrickText;

	// This maps trick slots to trick names. Each pro has their own set of default
	// mappings, stored in the profile.
	// The default mappings are defined in script, eg the structure HawkTricks.
	Script::CStruct *mpTrickMappings;

	//////////////////////////////////////////////////////////
	// 					Trick queue stuff
	//////////////////////////////////////////////////////////
	
	int mFrontTrick;	// The trick that is at the front of the queue, ie, next to be executed.
	int mBackTrick;		// The trick at the back of the queue, ie the last to be added.
	// mFrontTrick and mBackTrick are both -1 to indicate no tricks in queue.
	#define TRICK_QUEUE_SIZE 100
	STrick mpTricks[TRICK_QUEUE_SIZE];
	// Called by the ClearTrickQueue script command.
	void ClearTrickQueue();
	
	void AddTrick(uint32 ArrayChecksum, uint Index, bool UseSpecialTrickText=false);
	void RemoveFrontTrick();
	void ClearTricksFrom(uint32 ArrayChecksum);

	bool TrickIsDefined(Script::CStruct *pTrick);
	bool RunTrick(Script::CStruct *pTrick, uint32 optionalFlag=0, Script::CStruct *pExtraParams=NULL);

	// Called by the DoNextTrick script command.
	void TriggerNextQueuedTrick(uint32 scriptToRunFirst=0, Script::CStruct *p_scriptToRunFirstParams=NULL, Script::CStruct* pExtraParams=NULL);
	
	int mNumQueueTricksArrays;
	// Storing array checksums rather than pointers to the arrays, in case they get reloaded.
	#define MAX_QUEUE_TRICK_ARRAYS 10
	uint32 mpQueueTricksArrays[MAX_QUEUE_TRICK_ARRAYS];
	void ClearQueueTricksArrays();
	
	// Checksum of any special tricks array specified by the last SetQueueTricks script command.
	uint32 mSpecialTricksArrayChecksum;
	
	// This can be set by the SetQueueTricks command, and allows events that have already been
	// used by certain tricks, eg extra grinds, to be not ignored.
	uint32 mDoNotIgnoreMask;
	
	void MaybeAddTrick(uint32 ArrayChecksum, bool UseSpecialTrickText=false, uint32 DoNotIgnoreMask=0);
	
	// Always called every frame.
	void AddTricksToQueue();

	//////////////////////////////////////////////////////////
	// 					Manual stuff
	//////////////////////////////////////////////////////////
	
	int mNumManualTrickArrays;
	// Storing array checksums rather than pointers to the arrays, in case they get reloaded.
	#define MAX_MANUAL_TRICK_ARRAYS 10
	uint32 mpManualTrickArrays[MAX_MANUAL_TRICK_ARRAYS];
	
	// Checksum of any special manual tricks specified by the SetManualTricks script command.
	uint32 mSpecialManualTricksArrayChecksum;
	
	bool mGotManualTrick;
	// A special one entry 'queue' for manual tricks.
	STrick mManualTrick;
	// Called by the DoNextManualTrick script command.
	void TriggerAnyManualTrick(Script::CStruct *pExtraParams=NULL);
	
	// Checks a single array to see if any trick needs to be put in the manual queue.
	// Called from MaybeQueueManualTrick()
	void CheckManualTrickArray(uint32 ArrayChecksum, uint32 IgnoreMask=0xffffffff, bool UseSpecialTrickText=false);
	
	// These two functions are always called every frame.
	void MaybeQueueManualTrick();
	void MaybeExpireManualTrick();
	// Called by the ClearManualTrick script command.
	void ClearManualTrick();

	//////////////////////////////////////////////////////////
	// 					Extra-grind stuff
	//////////////////////////////////////////////////////////
	
	int mNumExtraGrindTrickArrays;
	// Storing array checksums rather than pointers to the arrays, in case they get reloaded.
	#define MAX_EXTRA_GRIND_TRICK_ARRAYS 10
	uint32 mpExtraGrindTrickArrays[MAX_EXTRA_GRIND_TRICK_ARRAYS];
	
	uint32 mSpecialExtraGrindTrickArrayChecksum;
	
	bool mGotExtraGrindTrick;
	// A special one entry 'queue' for extra grind tricks.
	STrick mExtraGrindTrick;
	
	// Called from CSkater::MaybeStickToRail()
	bool TriggerAnyExtraGrindTrick(bool Right, bool Parallel, bool Backwards, bool Regular);
	
	// For checking an single array. Called from MaybeQueueExtraGrindTrick()
	void MaybeQueueExtraGrindTrick(uint32 ArrayChecksum, bool UseSpecialTrickText=false);
	
	// These two functions are always called every frame.
	void MaybeQueueExtraGrindTrick();
	void MaybeExpireExtraGrindTrick();
	// Called by the ClearExtraGrindTrick script command.
	void ClearExtraGrindTrick();
	
	////////////////////////////////////////////////////////////////
	//
	// 'Extra' trick stuff.
	//
	////////////////////////////////////////////////////////////////
	
	// Whether we've got extra tricks to check for.
	bool mGotExtraTricks;
	// Whether to check for extra tricks forever.
	bool mExtraTricksInfiniteDuration;
	// If not infinite duration, this is how long after the start time to check for extra tricks for.
	uint32 mExtraTricksDuration;
	// The start time.
	uint32 mExtraTricksStartTime;
	
	// How many array checksums are in the array.
	int mNumExtraTrickArrays;
	#define MAX_EXTRA_TRICK_ARRAYS 10
	uint32 mpExtraTrickArrays[MAX_EXTRA_TRICK_ARRAYS];
	// Bitfields indicating which tricks in each array are excluded. (Hence each array can't have more than 32 tricks in it)
	// Used when an extra trick can branch to another trick in the same array, but we
	// don't want the same trick again, eg rail balance to another rail balance.
	uint32 mpExcludedExtraTricks[MAX_EXTRA_TRICK_ARRAYS];

	// Array checksum & exclusion bitfield for the special extra tricks, which are only checked for when special.
	uint32 mSpecialExtraTricksArrayChecksum;
	uint32 mExcludedSpecialExtraTricks;
	
	bool TriggerAnyExtraTrick(uint32 ArrayChecksum, uint32 ExcludedTricks);
	void TriggerAnyExtraTricks();
	void SetExtraTricks(Script::CStruct *pParams, Script::CScript *pScript);
	void KillExtraTricks();
	uint32 CalculateIgnoreMask(uint32 ArrayChecksum, const char *pIgnoreName);
	void ExcludeExtraTricks(const char *pIgnoreName);
	bool IsExcluded(Script::CStruct *pTrick, const char *pIgnoreName);

	bool mBashingEnabled;
	
	void HandleBashing();
	float GetBashFactor();

	// Name of the current trick, which gets displayed when the trick is triggered.
	char mpTrickName[MAX_TRICK_NAME_CHARS+1];
	int mTrickScore;

	// Name of the deferred trick, which gets displayed only if a 'display blockspin' or ClearPanel_Landed is done.
	char mpDeferredTrickName[MAX_TRICK_NAME_CHARS+1];
	int mDeferredTrickScore;
	Mdl::Score::Flags mDeferredTrickFlags;

	float mTallyAngles;
	
	// for knowing when to stop horse mode
	bool m_first_trick_started;
	bool m_first_trick_completed;
	
	int mNumTricksInCombo;
	void IncrementNumTricksInCombo();
    void ClearMiscellaneous();
	
	void SetGraffitiTrickStarted( bool started );
	bool GraffitiTrickStarted() { return m_graffiti_trick_started; }
	
	// for knowing when to accumulate graffiti objects
	bool m_graffiti_trick_started;
	
	void TrickOffObject( uint32 node_name );
		
	void SetFirstTrickStarted( bool started ) { m_first_trick_started = started; }
	bool FirstTrickStarted() { return m_first_trick_started; }
	bool FirstTrickCompleted() { return m_first_trick_completed; }
	
	uint32 WritePendingTricks( uint32* p_buffer, uint32 max_size );
	
	Obj::CPendingTricks m_pending_tricks;

    CTrickComponent();
    virtual ~CTrickComponent();

public:
    virtual void            		Update();
    virtual void            		Finalize();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
};

inline Mdl::Score* CTrickComponent::GetScoreObject()
{
	Dbg_MsgAssert(mp_score,("NULL pScore"));
	return mp_score;
}	

bool ScriptDoNextTrick( Script::CStruct *pParams, Script::CScript *pScript );


}

#endif


================================================
FILE: Code/Gel/Components/vehiclecameracomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VehicleCameraComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  2/10/3
//****************************************************************************

#include 
#include 
									 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#define MESSAGE(a) { printf("M:%s:%i: %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPI(a) { printf("D:%s:%i: " #a " = %i\n", __FILE__ + 15, __LINE__, a); }
#define DUMPB(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a ? "true" : "false"); }
#define DUMPF(a) { printf("D:%s:%i: " #a " = %g\n", __FILE__ + 15, __LINE__, a); }
#define DUMPE(a) { printf("D:%s:%i: " #a " = %e\n", __FILE__ + 15, __LINE__, a); }
#define DUMPS(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPP(a) { printf("D:%s:%i: " #a " = %p\n", __FILE__ + 15, __LINE__, a); }
#define DUMPV(a) { printf("D:%s:%i: " #a " = %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z]); }
#define DUMP4(a) { printf("D:%s:%i: " #a " = %g, %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z], (a)[W]); }
#define DUMPM(a) { DUMP4(a[X]); DUMP4(a[Y]); DUMP4(a[Z]); DUMP4(a[W]); }
#define MARK { printf("K:%s:%i: %s\n", __FILE__ + 15, __LINE__, __PRETTY_FUNCTION__); }
#define PERIODIC(n) for (static int p__ = 0; (p__ = ++p__ % (n)) == 0; )

#define vVELOCITY_WEIGHT_DROP_THRESHOLD				MPH_TO_IPS(15.0f)
#define vLOCK_ATTRACTOR_VELOCITY_THRESHOLD			MPH_TO_IPS(5000000.0f)
#define vSTATE_CHANGE_DELAY (1.0f)

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CVehicleCameraComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CVehicleCameraComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CVehicleCameraComponent::CVehicleCameraComponent() : CBaseComponent()
{
	SetType( CRC_VEHICLECAMERA );
	
	m_offset_height = FEET(5.0f);
	m_offset_distance = FEET(12.0f);
	
	m_alignment_rate = 3.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CVehicleCameraComponent::~CVehicleCameraComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleCameraComponent::InitFromStructure( Script::CStruct* pParams )
{
	uint32 subject_id;
	
	if (pParams->ContainsComponentNamed(CRCD(0x431c185, "subject")))
	{
		pParams->GetChecksum(CRCD(0x431c185, "subject"), &subject_id, Script::ASSERT);
		mp_subject = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(subject_id));
		Dbg_MsgAssert(mp_subject, ("Vehicle camera given subject which is not a composite object"));
		mp_subject_vehicle_component = static_cast< CVehicleComponent* >(mp_subject->GetComponent(CRC_VEHICLE));
		Dbg_MsgAssert(mp_subject_vehicle_component, ("Vehicle camera given subject which contains no vehicle component"));
	}
		
	pParams->GetFloat(CRCD(0x9213625f, "alignment_rate"), &m_alignment_rate);
	
	pParams->GetFloat(CRCD(0x14849b6d, "offset_height"), &m_offset_height);
	
	pParams->GetFloat(CRCD(0xbd3d3ca9, "offset_distance"), &m_offset_distance);
	
	pParams->GetFloat(CRCD(0xff7ebaf6, "angle"), &m_angle);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleCameraComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleCameraComponent::Finalize (   )
{
	mp_camera_lookaround_component = GetCameraLookAroundComponentFromObject(GetObject());
	
	Dbg_Assert(mp_camera_lookaround_component);
	
	reset_camera();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleCameraComponent::Update()
{
	// NOTE: plenty of room for optimiziation
	
	GetCameraComponentFromObject(GetObject())->StoreOldPosition();
	
	calculate_attractor_direction();
	
	// Due to rounding errors this can sometimes be > |1|, which hoses acosf(), so limit here.
	float angular_distance = acosf(Mth::Clamp(Mth::DotProduct(m_direction, m_attractor_direction), -1.0f, 1.0f));
	if (angular_distance > Mth::PI / 2.0f)
	{
		angular_distance = Mth::PI - angular_distance;
	}
	
	bool sign = Mth::CrossProduct(m_direction, m_attractor_direction)[Y] > 0.0f;
	
	float step = m_alignment_rate * angular_distance * Tmr::FrameLength();
	
	if (step > angular_distance)
	{
		step = angular_distance;
	}
	
	m_direction.RotateY((sign ? 1.0f : -1.0f) * step);
	m_direction.Normalize();
	
	calculate_dependent_variables();
	
	ApplyCameraCollisionDetection(
		m_pos, 
		m_orientation_matrix, 
		m_pos - m_offset_distance * m_orientation_matrix[Z], 
		m_pos - m_offset_distance * m_orientation_matrix[Z], 
		false, 
		false
	);
	
	apply_state();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CVehicleCameraComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case CRCC(0x469fd, "VehicleCamera_Reset"):
			RefreshFromStructure(pParams);
			Finalize();
			break;
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleCameraComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to C......Component::GetDebugInfo"));
	
	p_info->AddChecksum("mp_subject", mp_subject->GetID());

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleCameraComponent::reset_camera (   )
{
	mp_camera_lookaround_component->mLookaroundHeading = 0.0f;
	mp_camera_lookaround_component->mLookaroundTilt = 0.0f;
	mp_camera_lookaround_component->mLookaroundLock = false;
	
	m_attractor_direction = -mp_subject->GetMatrix()[Z];
	m_attractor_direction[Y] = 0.0f;
	m_attractor_direction.Normalize();
	
	calculate_attractor_direction();
	
	m_direction = m_attractor_direction;
	
	calculate_dependent_variables();
	
	apply_state();
	
	GetCameraComponentFromObject(GetObject())->Update();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleCameraComponent::calculate_attractor_direction (   )
{
	Mth::Vector vel_direction = -mp_subject_vehicle_component->GetVel();
	vel_direction[Y] = 0.0f;
	float vel = vel_direction.Length();
	vel_direction.Normalize();
	
	if (mp_subject_vehicle_component->GetNumWheelsInContact() < 2)
	{
		// if vel under certain threshold, we lock the attractor
		if (vel < vLOCK_ATTRACTOR_VELOCITY_THRESHOLD)
		{
			return;
		}
		
		m_attractor_direction = vel_direction;
	}
	else
	{
		float vel_weight = Mth::ClampMax(vel / vVELOCITY_WEIGHT_DROP_THRESHOLD, 1.0f) * Mth::DotProduct(vel_direction, -mp_subject->GetMatrix()[Z]);
		
		m_attractor_direction = -mp_subject->GetMatrix()[Z];
		m_attractor_direction[Y] = 0.0f;
		m_attractor_direction.Normalize();
		
		if (vel_weight > 0.0f)
		{
			m_attractor_direction += vel_weight * vel_direction;
			m_attractor_direction.Normalize();
		}
		
		// NOTE: potential bug if car is pointing straight down
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleCameraComponent::calculate_dependent_variables (   )
{
	Mth::Vector frame_direction = m_direction;
	frame_direction.RotateY(mp_camera_lookaround_component->mLookaroundHeading);
	
	m_orientation_matrix[X].Set(frame_direction[Z], 0.0f, -frame_direction[X]);
	m_orientation_matrix[Y].Set(0.0f, 1.0f, 0.0f);
	m_orientation_matrix[Z] = frame_direction;
	m_orientation_matrix[W].Set();
	
	m_pos = mp_subject->GetPos() + Mth::Vector(m_offset_distance * frame_direction[X], m_offset_height, m_offset_distance * frame_direction[Z]);
	
	m_orientation_matrix.RotateZLocal(DEGREES_TO_RADIANS(m_angle));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleCameraComponent::apply_state (   )
{
	m_pos[W] = 1.0f;
	m_orientation_matrix[X][W] = 0.0f;
	m_orientation_matrix[Y][W] = 0.0f;
	m_orientation_matrix[Z][W] = 0.0f;
	
	GetObject()->SetPos(m_pos);
	GetObject()->SetMatrix(m_orientation_matrix);
	GetObject()->SetDisplayMatrix(m_orientation_matrix);
}

}


================================================
FILE: Code/Gel/Components/vehiclecameracomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VehicleCameraComponent.h
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#ifndef __COMPONENTS_VEHICLECAMERACOMPONENT_H__
#define __COMPONENTS_VEHICLECAMERACOMPONENT_H__

#include 
#include 

#include 
#include 

// Just thinking about it - a generic way of accessing the component				 
#define		CRC_VEHICLECAMERA CRCD(0x2c747d8a, "VehicleCamera")
#define		GetVehicleCameraComponent() ((Obj::CVehicleCameraComponent*)GetComponent(CRC_VEHICLECAMERA))
#define		GetVehicleCameraComponentFromObject(pObj) ((Obj::CVehicleCameraComponent*)(pObj)->GetComponent(CRC_VEHICLECAMERA))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CCameraLookAroundComponent;

class CVehicleCameraComponent : public CBaseComponent
{
public:
    CVehicleCameraComponent();
    virtual ~CVehicleCameraComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
	virtual void					Finalize (   );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
private:
	void							reset_camera (   );
	void							calculate_attractor_direction (   );
	void							calculate_dependent_variables (   );
	void							apply_state (   );

private:
	CCompositeObject*				mp_subject;
	CVehicleComponent*				mp_subject_vehicle_component;
	
	Mth::Vector		   				m_direction;
	Mth::Vector		   				m_attractor_direction;
	
	Mth::Vector						m_pos;
	Mth::Matrix						m_orientation_matrix;
	
	float							m_offset_height;
	float							m_offset_distance;
	float							m_angle;
	
	float							m_alignment_rate;
	
	CCameraLookAroundComponent*		mp_camera_lookaround_component;
};

}

#endif


================================================
FILE: Code/Gel/Components/vehiclecomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VehicleComponent.cpp
//* OWNER:          Dan Nelson
//* CREATION DATE:  1/31/3
//****************************************************************************

#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#define MESSAGE(a) { printf("M:%s:%i: %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPI(a) { printf("D:%s:%i: " #a " = %i\n", __FILE__ + 15, __LINE__, a); }
#define DUMPF(a) { printf("D:%s:%i: " #a " = %g\n", __FILE__ + 15, __LINE__, a); }
#define DUMPE(a) { printf("D:%s:%i: " #a " = %e\n", __FILE__ + 15, __LINE__, a); }
#define DUMPS(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPP(a) { printf("D:%s:%i: " #a " = %p\n", __FILE__ + 15, __LINE__, a); }
#define DUMPV(a) { printf("D:%s:%i: " #a " = %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z]); }
#define DUMP4(a) { printf("D:%s:%i: " #a " = %g, %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z], (a)[W]); }
#define DUMPM(a) { DUMP4(a[X]); DUMP4(a[Y]); DUMP4(a[Z]); DUMP4(a[W]); }
#define MARK { printf("K:%s:%i: %s\n", __FILE__ + 15, __LINE__, __PRETTY_FUNCTION__); }
#define PERIODIC(n) for (static int p__ = 0; (p__ = ++p__ % (n)) == 0; )

// THOUGHTS:
//
// - BUG: solve negative collision depth issue for rect collisions; must project collision normal into rect plane before calculating depth (i think)
// - BUG: bad wipeout behavior; IDEAS:
//   - use only line feelers
//   - turn off graivty with > 2 contacts
//   - sleep like a rigidbody
//   - compare algorithm with rigidbody
// - Triggers.
// - Lock motion if very slow and there's no input.
// - Vehicle camera needs to reports its old position to get sounds' pitch correctly.
//
// - two center of masses (collision at zero and suspension) is an issue; actual rotation is done around zero; what sort of poor behavior does this cause
//   when the car is on two wheels? what way is there to retain stable suspension behavior but use a common center of mass?	perhaps simply damp rotational
//   impulses due to the suspension; this may help with the "vert, linear-to-angular energy" freak-out issue as well
// - body-body collisions; treat as boxes
// - prehaps reduce wheel friction when very near vertical to prevent wall riding
// - states: ASLEEP, AWAKE (full brake; rigidbody), DRIVEN, DRONE (?)
// - vertical side rectangle feelers
// - wheel camber  extracted from model
// - side slippage when stopped on hills

namespace Obj
{
	
CVehicleComponent::SCollisionPoint CVehicleComponent::sp_collision_points[4 * (Nx::MAX_NUM_2D_COLLISIONS_REPORTED + 1)];
								   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CVehicleComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CVehicleComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CVehicleComponent::CVehicleComponent() : CBaseComponent()
{
	SetType( CRC_VEHICLE );
	
	mp_input_component = NULL;
	
	m_draw_debug_lines = 0;
	
	m_update_suspension_only = false;
	
	m_steering_display = 0.0f;
	
	m_skater_pos.Set();
	
	mp_wheels = NULL;
	
	m_sound_setup_checksum = CRCD(0x1ca1ff20, "Default");
	
	m_num_collision_points = 0;
	
	m_artificial_collision_timer = 0.0f;
	
	m_state = AWAKE;
	
	m_consider_sleeping_count = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CVehicleComponent::~CVehicleComponent()
{
	delete [] mp_wheels;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::InitFromStructure( Script::CStruct* pParams )
{
	// extract constant parameters from struct; parameters not included in the script are left unchanged
	
	// toggles through the debug line drawing states
	if (pParams->ContainsFlag(CRCD(0x935ab858, "debug")))
	{
		m_draw_debug_lines = ++m_draw_debug_lines % 3;
	}
	else if (pParams->ContainsFlag(CRCD(0xe6b5fcd9, "full_debug")))
	{
		m_draw_debug_lines = (m_draw_debug_lines + 2) % 3;
	}
	else if (pParams->ContainsFlag(CRCD(0x751da48b, "no_debug")))
	{
		m_draw_debug_lines = 0;
	}
	
	// center of mass location (in)
	// the point around which the car rotates; a lower center of mass reduces roll tendency; the XZ location affects the
	// vehicle's behavior during jumps
	if (pParams->ContainsComponentNamed(CRCD(0x93346b8c, "suspension_center_of_mass")))
	{
		float suspension_center_of_mass_offset;
		pParams->GetFloat(CRCD(0x93346b8c, "suspension_center_of_mass"), &suspension_center_of_mass_offset);
		m_suspension_center_of_mass.Set(0.0f, suspension_center_of_mass_offset, 0.0f);
	}
	
	// mass (lbs)
	// affects acceleration, suspension behavior, and resistance to drag
	if (pParams->ContainsComponentNamed(CRCD(0x93fca499, "mass")))
	{
		pParams->GetFloat(CRCD(0x93fca499, "mass"), &m_inv_mass);
		m_inv_mass = vVP_GRAVITATIONAL_ACCELERATION / m_inv_mass;
	}

	// moment of inertia (in^2 lbs)
	// resistance to changes in rotation along the three axes; natural value is about the weight of the car times the
	// square of the average radius of the car along the axes in question
	// X: resistance to rolling
	// Y: resistance to turning
	// Z: resistance to pitcing during acceleration, braking, and jumps
	if (pParams->ContainsComponentNamed(CRCD(0x8c6473ec, "moment_of_inertia")))
	{
		pParams->GetVector(CRCD(0x8c6473ec, "moment_of_inertia"), &m_inv_moment_body);
		m_inv_moment_body[X] = vVP_GRAVITATIONAL_ACCELERATION / m_inv_moment_body[X];
		m_inv_moment_body[Y] = vVP_GRAVITATIONAL_ACCELERATION / m_inv_moment_body[Y];
		m_inv_moment_body[Z] = vVP_GRAVITATIONAL_ACCELERATION / m_inv_moment_body[Z];
	}
	
	// vehicle body's coefficient of restitution
	pParams->GetFloat(CRCD(0x79c3b862, "body_restitution"), &m_body_restitution);
	
	// vehicle body's coefficient of friction
	pParams->GetFloat(CRCD(0xf476273c, "body_friction"), &m_body_friction);
	
	// vehicle body's coefficient of friction
	pParams->GetFloat(CRCD(0x4a23a53c, "body_wipeout_friction"), &m_body_wipeout_friction);
	
	// vehicle body's penalty-method interpenetration-prevention spring constant
	pParams->GetFloat(CRCD(0x1dd4890, "body_spring"), &m_body_spring);
	
	// factor of normal impulse over which you can control via steering
	pParams->GetFloat(CRCD(0xb9f026ed, "collision_control"), &m_collision_control);
	
	// horizontal velocity cutoff below which no in-air slerping to face velocity occurs
	pParams->GetFloat(CRCD(0x8c14709e, "in_air_slerp_velocity_cutoff"), &m_in_air_slerp_vel_cutoff);
	
	// time over which in-air slerping lerps to full strength after takeoff
	pParams->GetFloat(CRCD(0x2c91e49b, "in_air_slerp_time_delay"), &m_in_air_slerp_time_delay);
	
	// in-air slerping strength
	pParams->GetFloat(CRCD(0x280f0e0b, "in_air_slerp_strength"), &m_in_air_slerp_strength);
	
	// special slerping below standard velocity threshold
	m_vert_correction = pParams->ContainsFlag(CRCD(0x5341806a, "vert_correction"));
	
	// maximum steering angle (degrees)
	if (pParams->ContainsComponentNamed(CRCD(0xc1e5abdd, "max_steering_angle")))
	{
        pParams->GetFloat(CRCD(0xc1e5abdd, "max_steering_angle"), &m_max_steering_angle);
		m_max_steering_angle = DEGREES_TO_RADIANS(m_max_steering_angle);
	}
	
	// rotational damping parameters which prevent fishtailing and drift
	pParams->GetFloat(CRCD(0x386837db, "constant_rotational_damping"), &m_const_rotvel_damping);
	pParams->GetFloat(CRCD(0x469cf79a, "quadratic_rotational_damping"), &m_quad_rotvel_damping);
	
	// if the car can be exited via triangle
	m_exitable = pParams->ContainsFlag(CRCD(0xc2a136cc, "exitable"));
	
	// if the car has a handbrke
	m_no_handbrake = pParams->ContainsFlag(CRCD(0xad6c0a46, "no_handbrake"));
	
	// the vehicle's body's interaction with the environment is expressed as an array of rectangular colliders
	if (pParams->ContainsComponentNamed(CRCD(0xfc8c4ac6, "colliders")))
	{
		Script::CArray* p_colliders_array;
		pParams->GetArray(CRCD(0xfc8c4ac6, "colliders"), &p_colliders_array, Script::ASSERT);
		
		float average_distance = 0.0f;
		
		int num_colliders = p_colliders_array->GetSize();
		// Dbg_MsgAssert(num_colliders == vVP_NUM_COLLIDERS, ("Number of colliders for CVehicleComponent is incorrect"));
		
		for (int collider_idx = num_colliders; collider_idx--; )
		{
            SCollider& collider = mp_colliders[collider_idx];
			
			Script::CArray* p_corner_array = p_colliders_array->GetArray(collider_idx);
			Dbg_MsgAssert(p_corner_array->GetSize() == 3, ("Incorrect number of corner vectors in collider array"));
			
			Script::CVector* p_corner_vector;
            p_corner_vector = p_corner_array->GetVector(0);
            collider.body.m_corner[X] = p_corner_vector->mX;
            collider.body.m_corner[Y] = p_corner_vector->mY;
            collider.body.m_corner[Z] = p_corner_vector->mZ;
			p_corner_vector = p_corner_array->GetVector(1);
			collider.body.m_first_edge[X] = p_corner_vector->mX;
			collider.body.m_first_edge[Y] = p_corner_vector->mY;
			collider.body.m_first_edge[Z] = p_corner_vector->mZ;
			collider.body.m_first_edge -= collider.body.m_corner;
			p_corner_vector = p_corner_array->GetVector(2);
			collider.body.m_second_edge[X] = p_corner_vector->mX;
			collider.body.m_second_edge[Y] = p_corner_vector->mY;
			collider.body.m_second_edge[Z] = p_corner_vector->mZ;
			collider.body.m_second_edge -= collider.body.m_corner;
			
			// update maximum_distance
			average_distance += collider.body.m_corner.Length();
			average_distance += (collider.body.m_corner + collider.body.m_first_edge).Length();
			average_distance += (collider.body.m_corner + collider.body.m_second_edge).Length();
			
			// calculate the precomputable values
			collider.first_edge_length = collider.body.m_first_edge.Length();
			collider.second_edge_length = collider.body.m_second_edge.Length();
			
			Dbg_MsgAssert(collider.first_edge_length > 0.0f, ("Collider of zero area"));
			Dbg_MsgAssert(collider.second_edge_length > 0.0f, ("Collider of zero area"));
		}
		
		// set the skater's rigid body collision radius while in the car
		Mdl::Skate::Instance()->GetLocalSkater()->SetRigidBodyCollisionRadiusBoost(average_distance / 3.0f / num_colliders - 48.0f);
	}
	
	if (pParams->ContainsComponentNamed(CRCD(0x1757e572, "engine")))
	{
		Script::CStruct* p_engine_struct;
		pParams->GetStructure(CRCD(0x1757e572, "engine"), &p_engine_struct, Script::ASSERT);
		
		// base drive torque of the engine; multiplied by the gear and differential ratio (ft-lbs)
		if (p_engine_struct->ContainsComponentNamed(CRCD(0x9aa9faee, "drive_torque")))
		{
			p_engine_struct->GetFloat(CRCD(0x9aa9faee, "drive_torque"), &m_engine.drive_torque);
			m_engine.drive_torque *= 12.0f;
		}
		
		// base drag torque of the engine; multiplied by the square of the gear ratio (ft-lbs)
		if (p_engine_struct->ContainsComponentNamed(CRCD(0xb2268648, "drag_torque")))
		{
			p_engine_struct->GetFloat(CRCD(0xb2268648, "drag_torque"), &m_engine.drag_torque);
			m_engine.drag_torque *= 12.0f;
		}
		
		// when rpm reaches this speed, the transmition upshifts (rpm)
		if (p_engine_struct->ContainsComponentNamed(CRCD(0x33921583, "upshift_rpm")))
		{
			p_engine_struct->GetFloat(CRCD(0x33921583, "upshift_rpm"), &m_engine.upshift_rotvel);
			m_engine.upshift_rotvel = RPM_TO_RADIANS_PER_SECOND(m_engine.upshift_rotvel);
		}
		
		// base gear ratio; torque and rpm are multiplied by this when translated up and down the drive train
		p_engine_struct->GetFloat(CRCD(0x94ea6601, "differential_ratio"), &m_engine.differential_ratio);
		
		// instead of having a true reverse gear, all gears are scaled down by this ratio when in reverse
		p_engine_struct->GetFloat(CRCD(0x82bcbb10, "reverse_torque_ratio"), &m_engine.reverse_torque_ratio);
		
		// gear ratios; torque and rpm are multiplied by these when translated up and down the drive train
		if (p_engine_struct->ContainsComponentNamed(CRCD(0xc023fa75, "gear_ratios")))
		{
			Script::CArray* p_engine_gear_ratios_array;
			p_engine_struct->GetArray(CRCD(0xc023fa75, "gear_ratios"), &p_engine_gear_ratios_array, Script::ASSERT);
			
			m_engine.num_gears = p_engine_gear_ratios_array->GetSize();
			Dbg_MsgAssert(m_engine.num_gears <= vVP_MAX_NUM_GEARS, ("Number of gears exceeds the maximum allowed number"));
			
			for (int n = 0; n < m_engine.num_gears; n++)
			{
				m_engine.p_gear_ratios[n] = p_engine_gear_ratios_array->GetFloat(n);
				
				if (n == 0) continue;
				
				Dbg_MsgAssert(n == 0 || m_engine.p_gear_ratios[n - 1] > m_engine.p_gear_ratios[n],
					("Low gear has lower gear ratio than high gear"));
			}
		}
	}
	
	// you can setup all wheels at once or each individually
	
	// an array of wheel structures
	if (pParams->ContainsComponentNamed(CRCD(0xb3f8557e, "wheels")))
	{
		Script::CArray* p_wheels_array = NULL;
		pParams->GetArray(CRCD(0xb3f8557e, "wheels"), &p_wheels_array, Script::ASSERT);
		
		Dbg_MsgAssert(!mp_wheels || m_num_wheels == p_wheels_array->GetSize(), ("Changed number of wheels"));
		
		m_num_wheels = p_wheels_array->GetSize();
			
		if (!mp_wheels)
		{
			mp_wheels = new SWheel[m_num_wheels];
		}
			
		for (int n = m_num_wheels; n--; )
		{
			Script::CStruct* p_wheel_struct = NULL;
			p_wheel_struct = p_wheels_array->GetStructure(n);
			
			// see update_wheel_from_structure() for documentation on wheel parameters
			update_wheel_from_structure(mp_wheels[n], p_wheel_struct);
		} // END loop over wheels
	} // END if wheel array specified

	Dbg_MsgAssert(mp_wheels, ("Wheels not created"));
	
	// a structure of wheel parameters which acts on all wheels
	if (pParams->ContainsComponentNamed(CRCD(0x371badff, "all_wheels")))
	{
		Script::CStruct* p_wheel_struct;
		pParams->GetStructure(CRCD(0x371badff, "all_wheels"), &p_wheel_struct, Script::ASSERT);
		
		for (int n = m_num_wheels; n--; )
		{
			// see update_wheel_from_structure() for documentation on wheel parameters
			update_wheel_from_structure(mp_wheels[n], p_wheel_struct);
		} // END loop over wheels
	} // END if all_wheel structure specified
	
	// calculate dependent constant characteristics
	
	// count the number of drive wheels
	m_num_drive_wheels = 0;
	for (int n = m_num_wheels; n--; )
	{
		if (mp_wheels[n].drive)
		{
			m_num_drive_wheels++;
		}
	}
	
	// setup position and orientation based on the object's state
	m_orientation = GetObject()->GetMatrix();
	m_orientation.Normalize();
	if (pParams->ContainsComponentNamed(CRCD(0xaa99c521, "save")))
	{
		Mth::Vector orientation_vector;
		orientation_vector.Set();
		pParams->GetVector(CRCD(0xc97f3aa9, "orientation"), &orientation_vector);
		m_orientation.SetVector(orientation_vector);
		m_orientation.Normalize();
		m_orientation.GetMatrix(m_orientation_matrix);
		m_orientation_matrix[W].Set();
		GetObject()->SetMatrix(m_orientation_matrix);
		GetObject()->SetDisplayMatrix(m_orientation_matrix);
	}
	
	m_pos = GetObject()->GetPos();
	if (pParams->ContainsComponentNamed(CRCD(0x7f261953, "pos")))
	{
		pParams->GetVector(CRCD(0x7f261953, "pos"), &m_pos);
		GetObject()->SetPos(m_pos);
	}
	
	m_skater_visible = m_skater_visible || pParams->ContainsFlag(CRCD(0x2ed67657, "make_skater_visible"));
	if (pParams->ContainsFlag(CRCD(0x2ed67657, "make_skater_visible")))
	{
		pParams->GetVector(CRCD(0xec86ef7a, "skater_pos"), &m_skater_pos, Script::ASSERT);
		pParams->GetChecksum(CRCD(0xda75a33e, "skater_anim"), &m_skater_anim, Script::ASSERT);
	}
	
	pParams->GetChecksum(CRCD(0xedcf90e, "Sounds"), &m_sound_setup_checksum);
	
	// zero velocities and accumulators
	
	m_mom.Set(0.0f, 0.0f, 0.0f);
	m_rotmom.Set(0.0f, 0.0f, 0.0f);
	
	m_force.Set(0.0f, 0.0f, 0.0f);
	m_torque.Set(0.0f, 0.0f, 0.0f);
	
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		wheel.state = SWheel::OUT_OF_CONTACT;
 		wheel.orientation = 0.0f;
		wheel.rotvel = 0.0f;
		wheel.y_offset = wheel.y_offset_hang;
		wheel.steering_angle = 0.0f;
		wheel.steering_angle_display = 0.0f;
		
		wheel.rotacc = 0.0f;

		// set normal force history to their default values
		for (int i = vVP_NORMAL_FORCE_HISTORY_LENGTH; i--; )
		{
			wheel.normal_force_history[i] = vVP_GRAVITATIONAL_ACCELERATION / m_inv_mass / m_num_wheels;
		}
	}
	m_next_normal_force_history_idx = 0;
	
	m_gravity_override_timer = 0.0f;
	m_gravity_override_fraction = 1.0f;
	
	m_in_flip = false;
	
	// grab a pointer to the vehicle's skeleton
	mp_skeleton_component = static_cast< CSkeletonComponent* >(GetObject()->GetComponent(CRC_SKELETON));
	Dbg_MsgAssert(mp_skeleton_component, ("Vehicle component has no peer skeleton component."));
	Dbg_MsgAssert(mp_skeleton_component->GetSkeleton()->GetNumBones() == static_cast< int >(2 + m_num_wheels), ("Vehicle component's peer skeleton component has an unexpected number of bones"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	Dbg_Assert(false);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::Finalize (   )
{
	mp_input_component = GetInputComponentFromObject(GetObject());
	mp_model_component = GetModelComponentFromObject(GetObject());
	
	Dbg_Assert(mp_input_component);
	Dbg_Assert(mp_model_component);
	
	// extract information about the car from the model
	
	Dbg_MsgAssert(m_num_wheels == vVP_NUM_WHEELS, ("Number of wheels must equal CVehicleComponent::vVP_NUM_WHEELS"));
	
	CModelComponent* p_model_component = static_cast< CModelComponent* >(GetModelComponentFromObject(GetObject()));
	Dbg_Assert(p_model_component);
	Nx::CModel* p_model = p_model_component->GetModel();
	Dbg_Assert(p_model);
	Nx::CHierarchyObject* p_hierarchy_objects = p_model->GetHierarchy();
	Dbg_Assert(p_hierarchy_objects);
	
	for (int n = vVP_NUM_WHEELS; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		Mth::Matrix wheel_matrix = (p_hierarchy_objects + 2 + n)->GetSetupMatrix();
		
		// rotate out of max coordinate system
		wheel.pos[X] = -wheel_matrix[W][X];
		wheel.pos[Y] = wheel_matrix[W][Z];
		wheel.pos[Z] = -wheel_matrix[W][Y];
		wheel.pos[W] = 1.0f;
	}
	
	Mth::Matrix body_matrix = (p_hierarchy_objects + 1)->GetSetupMatrix();
	m_body_pos[X] = -body_matrix[W][X];
	m_body_pos[Y] = body_matrix[W][Z];
	m_body_pos[Z] = -body_matrix[W][Y];
	m_body_pos[W] = 1.0f;
	
	// extract axle and wheelbase information
	int left_steering_tire = -1;
	int right_steering_tire = -1;
	int rear_tire = -1;
	for (int n = vVP_NUM_WHEELS; n--; )
	{
		switch (mp_wheels[n].steering)
		{
			case SWheel::LEFT:
				left_steering_tire = n;
				break;
			case SWheel::RIGHT:
				right_steering_tire = n;
				break;
			case SWheel::FIXED:
				rear_tire = n;
				break;
		}
	}
	Dbg_Assert(left_steering_tire != -1);
	Dbg_Assert(right_steering_tire != -1);
	Dbg_Assert(rear_tire != -1);
	m_cornering_wheelbase = Mth::Abs(
		(p_hierarchy_objects + 2 + left_steering_tire)->GetSetupMatrix()[W][Y] - (p_hierarchy_objects + 2 + rear_tire)->GetSetupMatrix()[W][Y]
	);
	m_cornering_axle_length = Mth::Abs(
		(p_hierarchy_objects + 2 + left_steering_tire)->GetSetupMatrix()[W][X] - (p_hierarchy_objects + 2 + right_steering_tire)->GetSetupMatrix()[W][X]
	);
	
	// calculate the true wheel positions based on the desired wheel positions with vehicle weight applied
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
	
		// place the wheel in the desired location based on the vehicle mass and the suspension spring rate; the suspension feeler starts at two
		// radii above the desired position
		float desired_y_pos = wheel.pos[Y];
		wheel.pos[Y] = desired_y_pos + 2.0f * wheel.radius;
		wheel.y_offset_hang = -wheel.pos[Y] + desired_y_pos - vVP_GRAVITATIONAL_ACCELERATION / (m_inv_mass * m_num_wheels * wheel.spring);
	}
	
	// determine the lowest collider point
	float lowest_collider_height = mp_colliders[0].body.m_corner[Y];
	for (int n = vVP_NUM_COLLIDERS; n--; )
	{
		lowest_collider_height = Mth::Min(mp_colliders[n].body.m_corner[Y], lowest_collider_height);
		lowest_collider_height = Mth::Min(mp_colliders[n].body.m_corner[Y] + mp_colliders[n].body.m_first_edge[Y], lowest_collider_height);
		lowest_collider_height = Mth::Min(mp_colliders[n].body.m_corner[Y] + mp_colliders[n].body.m_second_edge[Y], lowest_collider_height);
	}
	
	// ready the skater for control
	mp_skater = Mdl::Skate::Instance()->GetLocalSkater();
	mp_skater_core_physics_component = GetSkaterCorePhysicsComponentFromObject(mp_skater);
	mp_skater_trigger_component = GetTriggerComponentFromObject(mp_skater);
	Dbg_Assert(mp_skater_core_physics_component);
	Dbg_Assert(mp_skater_trigger_component);
	
	if (!m_skater_visible)
	{
		mp_skater->Hide(true);
	}
	else
	{
		mp_skater_animation_component = GetAnimationComponentFromObject(mp_skater);
		Dbg_Assert(mp_skater_animation_component);
	}
	
	// calculate the center of mass we will use based on the wheel locations
	Mth::Vector center_of_mass(0.0f, 0.0f, 0.0f, 0.0f);
	for (int n = m_num_wheels; n--; )
	{
		center_of_mass += mp_wheels[n].pos;
	}
	center_of_mass /= m_num_wheels;
	center_of_mass[Y] = lowest_collider_height;
	center_of_mass[W] = 0.0f;
	
	// move wheels and colliders so that they are relative to the correct center of mass
	for (int n = m_num_wheels; n--; )
	{
		mp_wheels[n].pos -= center_of_mass;
	}
	for (int n = vVP_NUM_COLLIDERS; n--; )
	{
		mp_colliders[n].body.m_corner -= center_of_mass;
	}
	m_body_pos -= center_of_mass;
	m_skater_pos -= center_of_mass;
	
	update_dependent_variables();
	
	GetObject()->SetPos(m_pos);
	GetObject()->SetVel(m_vel);
	GetObject()->SetMatrix(m_orientation_matrix);
	GetObject()->SetDisplayMatrix(GetObject()->GetMatrix());
	
	update_skeleton();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::Update()
{
	m_reset_this_frame = false;
	
	if (!m_update_suspension_only)
	{
		get_input();
	}
	else
	{
		zero_input();
	}
	
	if (m_state == ASLEEP)
	{
		// reduced work set if we're asleep
		GetObject()->SetDisplayMatrix(GetObject()->GetMatrix());
		update_steering_angles();
		if (m_controls.brake)
		{
			for (int n = m_num_wheels; n--; )
			{
				mp_wheels[n].rotvel = 0.0f;
			}
		}
		update_wheel_dynamic_state();
		update_skeleton();
		draw_shadow();
		control_skater();
		return;
	}
	
	int num_time_steps;
	float frame_length = Tmr::FrameLength();
	
	if (in_artificial_collision())
	{
		m_artificial_collision_timer -= frame_length;
	}
	
	// count down timers
	if (m_gravity_override_timer != 0.0f)
	{
		m_gravity_override_timer -= frame_length;
		if (m_gravity_override_timer <= 0.0f || m_num_wheels_in_contact > 1)
		{
			MESSAGE("Ending vehicle gravity override");
			m_gravity_override_timer = 0.0f;
			m_gravity_override_fraction = 1.0f;
		}
	}
	
	// the physics is unstable at low frame rates, so we take multiple physics steps during long frame; if vehicle physics is a significant fraction
	// of CPU time, this could exacerbate whatever frame rate problems are occuring
	if (frame_length >= (1.0f / 30.0f))
	{
		num_time_steps = static_cast< int >(ceilf(frame_length / (1.0f / 60.0f)));
		if (num_time_steps > 6)
		{
			num_time_steps = 6;
		}
		m_time_step = frame_length / num_time_steps;
		Dbg_Message("CVehicleComponent::Update: using %i steps this frame", num_time_steps);
	}
	else
	{
		num_time_steps = 1;
		m_time_step = frame_length;
	}
	
	for (int step = num_time_steps; step--; )
	{
		update_dynamic_state();
		
		m_force.Set(0.0f, 0.0f, 0.0f);
		m_torque.Set(0.0f, 0.0f, 0.0f);
		for (int n = m_num_wheels; n--; )
		{
			mp_wheels[n].rotacc = 0.0f;
		}
		
		if (!m_update_suspension_only)
		{
			apply_environment_collisions();
			if (reset_this_frame()) return;
		}
		
		// teleport wheels to the ground, if within reach of the suspension
		update_wheel_heights();
		if (reset_this_frame()) return;
		
		// update the steering wheels' angles
		update_steering_angles();
		
		// damp out rotations to create a more drivable car
		damp_rotation();
		
		// accumulate all forces and torques on the body and wheels
		accumulate_forces();

		/////////////
		// update state observers

		if (m_num_wheels_in_contact > 0)
		{
			m_air_time = 0.0f;
		}
		else
		{
			m_air_time += m_time_step;
		}
		
		if (m_num_wheels_in_contact > 0 || m_max_normal_collision_impulse > 0.0f)
		{
			m_air_time_no_collision = 0.0f;
		}
		else
		{
			m_air_time_no_collision += m_time_step;
		}
	}
	
	if (m_update_suspension_only) return;
	
	// draw debug lines
	draw_debug_rendering();
	
	// update object's position and orientation
	GetObject()->SetPos(m_pos);
	GetObject()->SetVel(m_vel);
	GetObject()->SetMatrix(m_orientation_matrix);
	GetObject()->SetDisplayMatrix(GetObject()->GetMatrix());
	
	consider_sleeping();
	
	update_skeleton();

	// Hack to draw shadow
	draw_shadow();
	
	#if 0
	Mth::Vector forward(0.0f, 0.0f, 1.0f);
	forward = m_orientation_matrix.Rotate(forward);
	float vel = IPS_TO_MPH(Mth::Abs(Mth::DotProduct(m_vel, forward)));
	PERIODIC(60) DUMPF(vel);
	#endif
	
	// HACK: get player proximity checks, triggers, driving animations, and the like working
	control_skater();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CVehicleComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | Vehicle_Kick | kicks the vehicle with a force and torque
		case CRCC(0x93b713a6, "Vehicle_Kick"):
		{
			Mth::Vector linear;
			Mth::Vector angular;
			
			m_state = AWAKE;
			m_consider_sleeping_count = 0;
			
			pParams->GetVector("linear", &linear);
			pParams->GetVector("angular", &angular);
			
			linear = m_orientation_matrix.Rotate(linear);
			angular = m_orientation_matrix.Rotate(angular);
			
			m_mom += linear / m_inv_mass;
			m_rotmom += angular / m_inv_moment_body[X];
			
			update_velocities();
			
			break;
		}
		
		// @script | Vehicle_Wake | wakes the vehicle if asleep
		case CRCC(0xa80a0d36, "Vehicle_Wake"):
			m_state = AWAKE;
			m_consider_sleeping_count = 0;
			break;

		// @script | Vehicle_MoveToRestart | teleport the vehicle to the restart node
		case CRCC(0x4b0b27dd, "Vehicle_MoveToRestart"):
		{
			uint32 node_name_checksum;
			if (pParams->GetChecksum(NO_NAME, &node_name_checksum))
			{
				// a node is specifically specified
				MoveToNode(SkateScript::GetNode(SkateScript::FindNamedNode(node_name_checksum, Script::ASSERT)));
			}
			else
			{
				// find a linked restart node
				int node = pScript->mNode;
				Dbg_MsgAssert(node !=  -1,( "Vehicle_MoveToRestart called from non-node script with no target node specified"));
				{
					int num_links = SkateScript::GetNumLinks(node);
					for (int n = 0; n < num_links; n++)
					{
						int linked_node = SkateScript::GetLink(node, n);
						if (IsRestart(linked_node))
						{
							MoveToNode(SkateScript::GetNode(linked_node));
							return CBaseComponent::MF_TRUE;
						}
					}
					if (Ed::CParkEditor::Instance()->UsingCustomPark())
					{
						MoveToNode(SkateScript::GetNode(Mdl::Skate::Instance()->find_restart_node(0)));
					}
					else
					{
						Dbg_MsgAssert(0, ("Vehicle_MoveToRestart called but node %d not linked to restart", node));			
					}
				}
			}			
			break;
		}
			
		// @script | Vehicle_PlaceBeforeCamera | moves the object before the active camera
		case CRCC(0xc33608e4, "Vehicle_PlaceBeforeCamera"):
		{
			Gfx::Camera* camera = Nx::CViewportManager::sGetActiveCamera(0);
			if (camera)
			{
				Mth::Vector& cam_pos = camera->GetPos();
				Mth::Matrix& cam_mat = camera->GetMatrix();

				m_pos = cam_pos;
				m_pos += cam_mat[Y] * 12.0f * 12.0f;
				m_pos -= cam_mat[Z] * 12.0f * 12.0f;
				GetObject()->SetPos(m_pos);
				
				m_orientation_matrix[X] = -cam_mat[X];
				m_orientation_matrix[Y] = cam_mat[Y];
				m_orientation_matrix[Z] = -cam_mat[Z];
				m_orientation = m_orientation_matrix;
				m_orientation.Normalize();
				m_orientation.GetMatrix(m_orientation_matrix);
				m_orientation_matrix[W].Set();
				GetObject()->SetMatrix(m_orientation_matrix);
				
				update_dependent_variables();
			}
			break;
		}
			
		// @script | Vehicle_AdjustGravity | adjusts effective gravity for a given duration or until the vehicle has two or more wheels on the ground,
		// whichever occurs first
		// @parm float | Percent | Percent of standard gravity.
		// @parm float | Duration | Duration in seconds over which to override gravity.
		case CRCC(0xdb35aad8, "Vehicle_AdjustGravity"):
			pParams->GetFloat(CRCD(0x9e497fc6, "Percent"), &m_gravity_override_fraction, Script::ASSERT);
			m_gravity_override_fraction *= 1.0f / 100.0f;
			pParams->GetFloat(CRCD(0x79a07f3f, "Duration"), &m_gravity_override_timer, Script::ASSERT);
			Dbg_MsgAssert(m_gravity_override_timer > 0.0f, ("Vehicle_AdjustGravity must have positive Duration"));
			MESSAGE("Initiating vehicle gravity override");
			break;
			
		// @script | Vehicle_ForceBrake | forces on the brake
		// case CRCC(0x1ad6b6bc, "Vehicle_ForceBrake"):
			// m_force_brake = true;
			// break;
			
		// @script | Vehicle_HandbrakeActive | returns true if the car has a handbrake
		case CRCC(0x5008b253, "Vehicle_HandbrakeActive"):
			return m_force_brake ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		// @script | Vehicle_AllWheelsAreInContact | returns true if all of the wheels are in contact with geo
		case CRCC(0x279602ed, "Vehicle_AllWheelsAreInContact"):
			return m_num_wheels_in_contact == m_num_wheels ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;

		// @script | Vehicle_LostCollision | lost a net collision; respond appropriately
		case CRCC(0x4c73526b, "Vehicle_LostCollision"):
		{
			Mth::Vector offset;
			pParams->GetVector(CRCD(0xa6f5352f, "Offset"), &offset, Script::ASSERT);
			ApplyArtificialCollision(offset);
			break;
		}
		
		// @script | Vehicle_IsSkaterVisible | true if skater should be visible while driving
		case CRCC(0x81faac21, "Vehicle_IsSkaterVisible"):
			return m_skater_visible ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE; 
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::GetDebugInfo ( Script::CStruct *p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info, ("NULL p_info sent to CVehicleComponent::GetDebugInfo"));
	
	p_info->AddVector("m_pos", m_pos);
	p_info->AddVector("m_mom", m_mom);
	p_info->AddVector("m_rotmom", m_rotmom);
	p_info->AddFloat("m_orientation", m_orientation.GetScalar());
	p_info->AddVector("m_orientation", m_orientation.GetVector());
	
	p_info->AddVector("m_force", m_force);
	p_info->AddVector("m_torque", m_torque);
	
	p_info->AddVector("m_vel", m_vel);
	p_info->AddVector("m_rotvel", m_rotvel);
	
	p_info->AddVector("m_suspension_center_of_mass", m_suspension_center_of_mass);
	p_info->AddFloat("mass", vVP_GRAVITATIONAL_ACCELERATION / m_inv_mass);
	p_info->AddVector("moment_of_inertia",
		vVP_GRAVITATIONAL_ACCELERATION / m_inv_moment_body[X],
		vVP_GRAVITATIONAL_ACCELERATION / m_inv_moment_body[Y],
		vVP_GRAVITATIONAL_ACCELERATION / m_inv_moment_body[Z]
	);
	p_info->AddFloat("body_restitution", m_body_restitution);
	p_info->AddFloat("body_friction", m_body_friction);
	p_info->AddFloat("body_spring", m_body_spring);
	p_info->AddFloat("collision_control", m_collision_control);
	p_info->AddFloat("in_air_slerp_velocity_cutoff", m_in_air_slerp_vel_cutoff);
	p_info->AddFloat("in_air_slerp_time_delay", m_in_air_slerp_time_delay);
	p_info->AddFloat("in_air_slerp_strength", m_in_air_slerp_strength);
	p_info->AddChecksum("vert_correction", m_vert_correction ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	
	p_info->AddFloat("wipeout_body_friction", m_body_wipeout_friction);
	p_info->AddFloat("max_steering_angle", RADIANS_TO_DEGREES(m_max_steering_angle));
	p_info->AddFloat("cornering_wheelbase", m_cornering_wheelbase);
	p_info->AddFloat("cornering_axle_length", m_cornering_axle_length);
	p_info->AddFloat("constant_rotational_damping", m_const_rotvel_damping);
	p_info->AddFloat("quadratic_rotational_damping", m_quad_rotvel_damping);
	
	Script::CStruct* p_engine_info = new Script::CStruct;
	p_engine_info->AddFloat("drive_torque", m_engine.drive_torque / 12.0f);
	p_engine_info->AddFloat("drag_torque", m_engine.drag_torque / 12.0f);
	p_engine_info->AddFloat("upshift_rpm", RADIANS_PER_SECOND_TO_RPM(m_engine.upshift_rotvel));
	p_engine_info->AddFloat("differential_ratio", m_engine.differential_ratio);
	p_engine_info->AddFloat("reverse_torque_ratio", m_engine.reverse_torque_ratio);
	Script::CArray* p_engine_gear_ratio_info = new Script::CArray;
	p_engine_gear_ratio_info->SetSizeAndType(m_engine.num_gears, ESYMBOLTYPE_FLOAT);
	for (int n = m_engine.num_gears; n--; )
	{
		p_engine_gear_ratio_info->SetFloat(n, m_engine.p_gear_ratios[n]);
	}
	p_engine_info->AddArrayPointer("gear_ratios", p_engine_gear_ratio_info);
	p_info->AddStructurePointer("m_engine", p_engine_info);
	
	Script::CArray* p_wheels_info = new Script::CArray;
	p_wheels_info->SetSizeAndType(m_num_wheels, ESYMBOLTYPE_STRUCTURE);
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		Script::CStruct* p_local_info = new Script::CStruct;
		
		uint32 state_checksums[] =
		{
			CRCC(0xba2b31d7, "NO_STATE"),
			CRCC(0xcd419ee6, "OUT_OF_CONTACT"),
			CRCC(0x927c0fed, "UNDER_GRIPPING"),
			CRCC(0xe9bc148a, "GRIPPING"),
			CRCC(0x26acccc8, "SLIPPING"),
			CRCC(0xa91003cc, "SKIDDING"),
			CRCC(0x2e7ef449, "HANDBRAKE_THROTTLE"),
			CRCC(0xbf6d6529, "HANDBRAKE_LOCKED"),
		};
		uint32 steering_checksums[] =
		{
			CRCC(0x613631cd, "FIXED"),
			CRCC(0x85981897, "LEFT"),
			CRCC(0x4b358aeb, "RIGHT")
		};
		p_local_info->AddChecksum("state", state_checksums[wheel.state]);
		p_local_info->AddFloat("rotvel", wheel.rotvel);
        p_local_info->AddFloat("y_offset", wheel.y_offset);
        p_local_info->AddFloat("steering_angle", RADIANS_TO_DEGREES(wheel.steering_angle));
        p_local_info->AddFloat("steering_angle_display", RADIANS_TO_DEGREES(wheel.steering_angle_display));
        p_local_info->AddFloat("rotacc", wheel.rotacc);
		p_local_info->AddFloat("orientation", wheel.orientation);
		p_local_info->AddVector("pos", wheel.pos);
		p_local_info->AddFloat("y_offset_hang", wheel.y_offset_hang);
		p_local_info->AddFloat("max_draw_y_offset", wheel.max_draw_y_offset);
		p_local_info->AddChecksum("steering", steering_checksums[wheel.steering]);
		p_local_info->AddInteger("drive", wheel.drive);
		p_local_info->AddFloat("radius", wheel.radius);
		p_local_info->AddFloat("moment", vVP_GRAVITATIONAL_ACCELERATION / wheel.inv_moment);
		p_local_info->AddFloat("spring_rate", wheel.spring);
		p_local_info->AddFloat("damping_rate", wheel.damping);
		p_local_info->AddFloat("static_friction", wheel.static_friction);
		p_local_info->AddFloat("dynamic_friction", wheel.dynamic_friction);
		p_local_info->AddFloat("handbrake_throttle_friction", wheel.handbrake_throttle_friction);
		p_local_info->AddFloat("handbrake_locked_friction", wheel.handbrake_locked_friction);
		p_local_info->AddFloat("min_static_grip_velocity", IPS_TO_MPH(wheel.min_static_velocity));
		p_local_info->AddFloat("max_static_grip_velocity", IPS_TO_MPH(wheel.max_static_velocity));
		p_local_info->AddFloat("min_dynamic_grip_velocity", IPS_TO_MPH(wheel.min_dynamic_velocity));
		p_local_info->AddFloat("brake_torque", wheel.brake.torque / 12.0f);
		p_local_info->AddFloat("handbrake_torque", wheel.brake.handbrake_torque / 12.0f);
		
		p_wheels_info->SetStructure(n, p_local_info);
	}
	p_info->AddArrayPointer("mp_wheels", p_wheels_info);
	
	Script::CStruct* p_controls_info = new Script::CStruct;
	p_controls_info->AddFloat("steering", m_controls.steering);
	p_controls_info->AddInteger("throttle", m_controls.throttle);
	p_controls_info->AddInteger("brake", m_controls.brake);
	p_controls_info->AddInteger("handbrake", m_controls.handbrake);
	p_controls_info->AddInteger("reverse", m_controls.reverse);
	p_info->AddStructurePointer("m_controls", p_controls_info);
	
	if (m_skater_visible)
	{
		p_info->AddVector(CRCD(0xec86ef7a, "skater_pos"), m_skater_pos);
		p_info->AddChecksum(CRCD(0xda75a33e, "skater_anim"), m_skater_anim);
	}

	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::MoveToNode ( Script::CStruct* p_node )
{
	// move to the position relative to the given node that a ped car would be at
	
	Mth::Vector restart_pos;
	SkateScript::GetPosition(p_node, &restart_pos);
	
	Mth::Vector restart_angles;
	SkateScript::GetAngles(p_node, &restart_angles);
	Mth::Matrix restart_matrix;
	restart_matrix.SetFromAngles(restart_angles);
	
	// calculate appropriate offset from the ground based on estimated wheel offsets
	float avg_ground_offset = 0.0f;
	for (int n = vVP_NUM_WHEELS; n--; )
	{
		avg_ground_offset += mp_wheels[n].y_offset_hang + mp_wheels[n].pos[Y] + vVP_GRAVITATIONAL_ACCELERATION / (m_inv_mass * m_num_wheels * mp_wheels[n].spring);
		avg_ground_offset -= mp_wheels[n].radius;
	}
	avg_ground_offset /= vVP_NUM_WHEELS;
	
	// find ground height
	CFeeler feeler(restart_pos + Mth::Vector(0.0f, 24.0f, 0.0f), restart_pos + Mth::Vector(0.0f, -240.0f, 0.0f));
	if (feeler.GetCollision(false))
	{
		restart_pos[Y] = feeler.GetPoint()[Y] - avg_ground_offset;
	}
	
	// ped cars have their origin between the rear wheels
	int rear_wheel_idx = -1;
	for (int n = vVP_NUM_WHEELS; n--; )
	{
		if (mp_wheels[n].steering == SWheel::FIXED)
		{
			rear_wheel_idx = n;
			break;
		}
	}
	Dbg_Assert(rear_wheel_idx != -1);
	restart_pos -= restart_matrix[Z] * mp_wheels[rear_wheel_idx].pos[Z];

	// move the car to the restart position and allow it to settle on its suspension
	
	m_pos = restart_pos;
	m_orientation = restart_matrix;
	
	m_mom.Set(0.0f, 0.0f, 0.0f);
	m_rotmom.Set(0.0f, 0.0f, 0.0f);
	
	// zero wheels
	
	m_update_suspension_only = true;
	for (int n = 60; n--; )
	{
		// lock wheels each frame
		for (int n = m_num_wheels; n--; )
		{
			mp_wheels[n].rotvel = 0.0f;
		}
		
		Update();
	}
	m_update_suspension_only = false;
	
	m_mom.Set(0.0f, 0.0f, 0.0f);
	m_rotmom.Set(0.0f, 0.0f, 0.0f);
	
	m_reset_this_frame = true;
	
	m_state = ASLEEP;
	
	// update object's position and orientation
	GetObject()->SetPos(m_pos);
	GetObject()->SetVel(m_vel);
	GetObject()->SetMatrix(m_orientation_matrix);
	GetObject()->SetDisplayMatrix(GetObject()->GetMatrix());
	
	update_skeleton();
	
	control_skater();
	
	GetObject()->SetTeleported();
	mp_skater->SetTeleported();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::ApplyArtificialCollision ( const Mth::Vector& offset )
{
	Mth::Vector impulse_direction = offset;
	impulse_direction[Y] = 0.0f;
	impulse_direction.Normalize();
	
	Mth::Vector sideways(-impulse_direction[Z], 0.0f, impulse_direction[X]);
	
	float impulse_forward = (1.0f + Mth::PlusOrMinus(0.5f)) * Script::GetFloat(CRCD(0x3db3fa83, "vehicle_physics_netcoll_forward_impulse"));
	float impulse_sideways = Mth::PlusOrMinus(1.5f) * Script::GetFloat(CRCD(0x39e99fec, "vehicle_physics_netcoll_sideways_impulse"));
	float impulse_upwards = (1.0f + Mth::PlusOrMinus(0.2f)) * Script::GetFloat(CRCD(0x738500fd, "vehicle_physics_netcoll_upwards_impulse"));
	
	Mth::Vector impulse = impulse_forward * impulse_direction + impulse_sideways * sideways;
	impulse[Y] += impulse_upwards;
	
	float rotate_spin = Mth::PlusOrMinus(1.5f) * Script::GetFloat(CRCD(0x14ddb3ef, "vehicle_physics_netcoll_spin_impulse"));
	float rotate_flip = (1.0f + Mth::PlusOrMinus(0.8f)) * Script::GetFloat(CRCD(0x4088e6ec, "vehicle_physics_netcoll_flip_impulse"));
	
	Mth::Vector rotate = rotate_flip * sideways;
	rotate[Y] += rotate_spin;
	
	m_mom += impulse / m_inv_mass;
	m_rotmom += rotate / m_inv_moment_body[X];

	update_velocities();
	
	m_artificial_collision_timer = Script::GetFloat(CRCD(0x771922a6, "vehicle_physics_artificial_collision_duration"));
	
	m_state = AWAKE;
	m_consider_sleeping_count = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::update_flip (   )
{
	m_orientation.GetMatrix(m_orientation_matrix);
	m_orientation_matrix[W].Set();
	
	if (m_orientation_matrix[Y][Y] > 0.999f) // || !m_controls.throttle)
	{
		m_in_flip = false;
		return;
	}
	
	bool flipping_appropriate_this_frame = m_vel.LengthSqr() < Mth::Sqr(300.0f)
		&& m_rotvel.LengthSqr() < Mth::Sqr(Mth::PI / 4.0f)
		&& m_max_normal_collision_impulse > 0.0f
		&& m_orientation_matrix[Y][Y] < 0.25f
		&& (m_num_wheels_in_contact == 0 || m_orientation_matrix[Y][Y] < 0.0f)
		&& m_controls.throttle;

	if (!m_in_flip && !flipping_appropriate_this_frame) return;
	
	// starting a flip
	if (!m_in_flip)
	{
		m_mom.Set();
		m_rotmom.Set();
		
		m_in_flip = true;
		m_flip_start_time_stamp = Tmr::GetTime();
	}
	
	// check for maximum flip duration
	if (Tmr::ElapsedTime(m_flip_start_time_stamp) > vVP_FLIP_DURATION + vVP_FLIP_DELAY)
	{
		m_in_flip = false;
		return;
	}
	
	// put a short delay before the flip actually has an effect
	if (Tmr::ElapsedTime(m_flip_start_time_stamp) < vVP_FLIP_DELAY)
	{
		return;
	}
	
	// setup this frame's slerp
	Mth::Matrix goal_orientation;
	goal_orientation[Z] = m_orientation_matrix[Z];
	goal_orientation[Z][Y] = 0.0f;
	goal_orientation[Z].Normalize();
	goal_orientation[Y].Set(0.0f, 1.0f, 0.0f);
	goal_orientation[X].Set(goal_orientation[Z][Z], 0.0f, -goal_orientation[Z][X]);
	Mth::SlerpInterpolator slerper;
	slerper.setMatrices(&m_orientation_matrix, &goal_orientation);
	
	// calculate the new orientation matrix
	slerper.getMatrix(
		&m_orientation_matrix,
		Mth::ClampMax(vVP_FLIP_RATE * Tmr::FrameLength() / slerper.getRadians(), 1.0f)
	);
	m_orientation = m_orientation_matrix;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::update_wheel_from_structure ( SWheel& wheel, Script::CStruct* p_wheel_struct )
{
	uint32 checksum;
	
	// wheel's steering type
	if (p_wheel_struct->ContainsComponentNamed(CRCD(0x7560e63, "steering")))
	{
		p_wheel_struct->GetChecksum(CRCD(0x7560e63, "steering"), &checksum);
		switch (checksum)
		{
			case CRCC(0x85981897, "left"):
				wheel.steering = SWheel::LEFT;
				break;
			case CRCC(0x4b358aeb, "right"):
				wheel.steering = SWheel::RIGHT;
				break;
			case CRCC(0x613631cd, "fixed"):
				wheel.steering = SWheel::FIXED;
				break;
			default:
				Dbg_MsgAssert(false, ("Bad wheel steering setting"));
		}
	}

	// if the wheel is a drive wheel
	if (p_wheel_struct->ContainsComponentNamed(CRCD(0x97e20a70, "drive")))
	{
		p_wheel_struct->GetChecksum(CRCD(0x97e20a70, "drive"), &checksum);
		switch (checksum)
		{
			case CRCC(0x8a18ca56, "yes"):
				wheel.drive = true;
				break;
			case CRCC(0x9855d7e0, "no"):
				wheel.drive = false;
				break;
			default:
				Dbg_MsgAssert(false, ("Bad wheel drive setting"));
		}
	}

	// suspension spring rate (lbs / in)
	// strength of the suspension spring; multiplied by the compresion to get the spring's force; must be scaled
	// with the vehicle weight; large spring rates give stiffer suspensions; large values may cause instability in
	// physics model
	p_wheel_struct->GetFloat(CRCD(0x9e931fdf, "spring_rate"), &wheel.spring);

	// suspension damping; bump rate (lbs s / in)
	// bounce damping strength of the suspension; smaller values cause bouncier suspensions; very large values may
	// cause instability in the physics model
	p_wheel_struct->GetFloat(CRCD(0xafa960ff, "damping_rate"), &wheel.damping);

	// wheel radius (in)
	// larger radius wheels are harder for the engine to rotate and the brake and rolling resistance to stop
	p_wheel_struct->GetFloat(CRCD(0xc48391a5, "radius"), &wheel.radius);

	if (p_wheel_struct->ContainsComponentNamed(CRCD(0xca73775d, "moment")))
	{
		p_wheel_struct->GetFloat(CRCD(0xca73775d, "moment"), &wheel.inv_moment);
		wheel.inv_moment = vVP_GRAVITATIONAL_ACCELERATION / wheel.inv_moment;
	}
	
	p_wheel_struct->GetFloat(CRCD(0xd87d0183, "max_draw_y_offset"), &wheel.max_draw_y_offset);

	// the following parameters affect the shape of the tire's friction curve
	// the friction curve gives the strength of the frictional force as a function of the tire's slip velocity; our
	// simple curve rises linearly from zero at zero slip velocity, reaches static_friction at a slip velocity of
	// min_static_velocity, remains at static_friction up to a slip velocity of max_static_velocity, drops linearly
	// to dynamic_friction until a slip velocity of min_dynamic_velocity, and then remains constant at dynamic_friction

	// normalized friction strength when the wheel is not skidding
	// low values will cause the vehicle to be spin-out and skid easily; high values cause the tires to feel sticky;
	// real world values are on the order of 1.3; fun values are much higher
	p_wheel_struct->GetFloat(CRCD(0xf36b97c8, "static_friction"), &wheel.static_friction);

	// normalized friction strength when the wheel is skidding
	// should be lower than static_friction; values much lower than static_friction cause the vehicle to be unforgiving;
	// once you lose grip, it is harder to regain control of the vehicle; values near static_friction cause the vehicle
	// to be very forgiving, with skidding causing little loss of handling 
	p_wheel_struct->GetFloat(CRCD(0x674852f6, "dynamic_friction"), &wheel.dynamic_friction);
	
	// normalized friction strength when the handbrake is applied
	// currently specially tuned in script to force a fishtail when the handbrake and throttle are both applied and spin-outs when only handbrake is applied
	p_wheel_struct->GetFloat(CRCD(0x52242919, "handbrake_throttle_friction"), &wheel.handbrake_throttle_friction);
	p_wheel_struct->GetFloat(CRCD(0x6d615f67, "handbrake_locked_friction"), &wheel.handbrake_locked_friction);

	// slip speed below which tires undergrip (mph)
	// should be around 1 to 2 mph
	if (p_wheel_struct->ContainsComponentNamed(CRCD(0x60b2c097, "min_static_grip_velocity")))
	{
		p_wheel_struct->GetFloat(CRCD(0x60b2c097, "min_static_grip_velocity"), &wheel.min_static_velocity);
		wheel.min_static_velocity = MPH_TO_IPS(wheel.min_static_velocity);
	}

	// maximum slip speed before tires begin to lose their grip (mph)
	// should be larger than min_static_velocity; with a low value the vehicle will skid more readily; a high value
	// causes there to be a large range of slip velocties with maximum tire grip
	if (p_wheel_struct->ContainsComponentNamed(CRCD(0xbb0d9370, "max_static_grip_velocity")))
	{
		p_wheel_struct->GetFloat(CRCD(0xbb0d9370, "max_static_grip_velocity"), &wheel.max_static_velocity);
		wheel.max_static_velocity = MPH_TO_IPS(wheel.max_static_velocity);
	}

	// minimum slip speed before tires begin skidding (imph)
	// should be larger than max_static_velocity; with a low values, tires are very unforgiving and begin to skid as soon
	// as they begin to slip; with a larger value, the loss of control and onset of skidding will be more gradual
	if (p_wheel_struct->ContainsComponentNamed(CRCD(0xb71803ad, "min_dynamic_grip_velocity")))
	{
		p_wheel_struct->GetFloat(CRCD(0xb71803ad, "min_dynamic_grip_velocity"), &wheel.min_dynamic_velocity);
		wheel.min_dynamic_velocity = MPH_TO_IPS(wheel.min_dynamic_velocity);
	}

	// torque which the brake exerts on the wheel (ft-lbs)
	if (p_wheel_struct->ContainsComponentNamed(CRCD(0x3bccbadc, "brake_torque")))
	{
		p_wheel_struct->GetFloat(CRCD(0x3bccbadc, "brake_torque"), &wheel.brake.torque);
		wheel.brake.torque *= 12.0f;
	}

	// torque which the handbrake exerts on the wheel when steering is straight and the throttle is down (ft-lbs)
	if (p_wheel_struct->ContainsComponentNamed(CRCD(0x9439d144, "handbrake_torque")))
	{
		p_wheel_struct->GetFloat(CRCD(0x9439d144, "handbrake_torque"), &wheel.brake.handbrake_torque);
		wheel.brake.handbrake_torque *= 12.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::update_dynamic_state (   )
{
	// update body state
	
	// calculate accelerations
	
	Mth::Vector acc = m_inv_mass * m_force;
	Mth::Vector rotacc = m_inv_moment.Rotate(m_torque);
	
	// update state variables
	
	m_mom += m_time_step * m_force;
	update_pos_with_uber_frig(m_time_step * (m_vel + 0.5f * m_time_step * acc));
	
	Mth::Vector delta_orientation_vector = -0.5f * m_time_step * (m_rotvel + 0.5f * m_time_step * rotacc);
	Mth::Quat delta_orientation = m_orientation * Mth::Quat(delta_orientation_vector[X], delta_orientation_vector[Y], delta_orientation_vector[Z], 0.0f);
	m_orientation += delta_orientation;
	m_orientation.Normalize();

    m_rotmom += m_time_step * m_torque;
	
	update_wheel_dynamic_state();
	
    // if we're in the air and moving fast enough horizontally
	if (m_num_wheels_in_contact == 0 && m_max_normal_collision_impulse == 0.0f)
	{
		slerp_to_face_velocity();
	}
	
	update_flip();
    
	update_dependent_variables();
	
#if 0
	PERIODIC(60) {
		float K = 0.5f * Mth::DotProduct(m_vel, m_mom) + 0.5f * Mth::DotProduct(m_rotvel, m_rotmom);
		float U = vVP_GRAVITATIONAL_ACCELERATION * m_pos[Y] / m_inv_mass;
		DUMPF(K + U);
		Mth::Vector a, b;
		a = m_rotmom;
		b = m_rotvel;
		DUMPF(Mth::DotProduct(a.Normalize(), b.Normalize()));
		// DUMPF(m_rotvel.Length());
		// DUMPV(m_rotvel);
	}
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::update_wheel_dynamic_state (   )
{
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		// wheel rotvel updates as torques are applied; thus, wheel rotvel is currently the frame's final rotvel 
		wheel.orientation += m_time_step * (wheel.rotvel - 0.5f * m_time_step * wheel.rotacc);
		
		if (wheel.orientation > (200.0f * Mth::PI))
		{
			wheel.orientation -= (100.0f * Mth::PI);
		}
		else if (wheel.orientation < 0.0f)
		{
			wheel.orientation += (100.0f * Mth::PI);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::update_dependent_variables (   )
{
	m_orientation.GetMatrix(m_orientation_matrix);
	m_orientation_matrix[W].Set();
	
	m_suspension_center_of_mass_world = m_orientation_matrix.Rotate(m_suspension_center_of_mass);
	
	calculate_inverse_moment();
	
	update_velocities();
	
	// update wheel feeler endpoint positions
	for	(int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		wheel.feeler_start_world = m_pos + m_orientation_matrix.Rotate(wheel.pos);
		wheel.feeler_end_world = wheel.pos;
		wheel.feeler_end_world[Y] += wheel.y_offset_hang - wheel.radius;
		wheel.feeler_end_world = m_pos + m_orientation_matrix.Rotate(wheel.feeler_end_world);
	}
	
	// update collider world positions
	for (int collider_idx = vVP_NUM_COLLIDERS; collider_idx--; )
	{
		SCollider& collider = mp_colliders[collider_idx];
		
		collider.world.m_corner = m_pos + m_orientation_matrix.Rotate(collider.body.m_corner);
		collider.world.m_first_edge = m_orientation_matrix.Rotate(collider.body.m_first_edge);
        collider.world.m_second_edge = m_orientation_matrix.Rotate(collider.body.m_second_edge);
		
		collider.first_edge_direction_world = collider.world.m_first_edge / collider.first_edge_length;
		collider.second_edge_direction_world = collider.world.m_second_edge / collider.second_edge_length;
	}
	
	update_collision_cache();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CVehicleComponent::calculate_friction_coefficient ( SWheel& wheel, float velocity ) const
{
	// friction based on section 2.4 and 2.7 of Race Car Vehicle Dynamics by Milliken
	
	// the tire friction coefficient curve is very simple; just four concatenated lines
	
	if (m_controls.handbrake)
	{
		// handbrake friction is a total scam
		
		float friction;
		if (m_controls.throttle)
		{
			friction = wheel.handbrake_throttle_friction;
			wheel.state = SWheel::HANDBRAKE_THROTTLE;
		}
		else
		{
			friction = wheel.handbrake_locked_friction;
			wheel.state = SWheel::HANDBRAKE_LOCKED;
		}
		
		if (velocity < wheel.min_static_velocity)
		{
			// under gripped handbrake "skidding"
			wheel.state = SWheel::UNDER_GRIPPING;
			return friction * velocity / wheel.min_static_velocity;
		}
		else
		{
			// handbrake "skidding"
			return friction;
		}
	}
	
	float multiplier = m_controls.brake ? 2.0f : 1.0f;
	
	if (velocity < wheel.min_static_velocity)
	{
		// under gripped
		wheel.state = SWheel::UNDER_GRIPPING;
		return wheel.static_friction * velocity / wheel.min_static_velocity;
	}
	else if (velocity < wheel.max_static_velocity)
	{
		// maximum grip
		wheel.state = SWheel::GRIPPING;
		return multiplier * wheel.static_friction;
	}
	else if (velocity < wheel.min_dynamic_velocity)
	{
		// on the verge of skidding
		wheel.state = SWheel::SLIPPING;
		return multiplier * wheel.dynamic_friction + (wheel.static_friction - wheel.dynamic_friction)
			* (velocity - wheel.max_static_velocity) / (wheel.min_dynamic_velocity - wheel.max_static_velocity);
	}
	else
	{
		// skidding
		wheel.state = SWheel::SKIDDING;
		return multiplier * wheel.dynamic_friction;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::calculate_friction_coefficients (   )
{
	// calculate the wheels' friction coefficients this frame
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		if (wheel.state == SWheel::OUT_OF_CONTACT) continue;
		
		// project the wheel's velocity due to the body's motion into the plane of the contact surface
		Mth::Vector projected_vel = wheel.vel_world;
		projected_vel.ProjectToPlane(wheel.contact_normal);
	
		// calculate the forward direction of the wheel
		Mth::Vector wheel_direction(sinf(wheel.steering_angle), 0.0f, cosf(wheel.steering_angle));
		wheel_direction = m_orientation_matrix.Rotate(wheel_direction);
		
		// project that forward direction onto the contact surface
		wheel_direction.ProjectToPlane(wheel.contact_normal);
		wheel_direction.Normalize();
		
		// NOTE: from this point and up, the calculation is repeated exactly in apply_friction_forces(); we cache the rotated and projected
		// wheel direction and the wheel velocity due to body velocity; because the wheel velocity will change before apply_friction_forces(),
		// the calculation below must be repeated
		
		wheel.cache_projected_direction = wheel_direction;
		wheel.cache_projected_vel = projected_vel;
	
		// subtract the contact patch velocity; positive wheel rotational velocity equals negative contact patch velocity
		projected_vel -= wheel.rotvel * wheel.radius * wheel_direction;
	
		// calculate the friction coefficient
		wheel.friction_coefficient = calculate_friction_coefficient(wheel, projected_vel.Length());
	}
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::calculate_inverse_moment (   )
{
	// m_inv_moment = m_orientation_matrix * m_inv_moment_body_diag_matrix * m_orientation_matrix_transpose
	
	m_inv_moment[X][X] = m_inv_moment_body[X] * m_orientation_matrix[X][X];
	m_inv_moment[X][Y] = m_inv_moment_body[Y] * m_orientation_matrix[X][Y];
	m_inv_moment[X][Z] = m_inv_moment_body[Z] * m_orientation_matrix[X][Z];
	m_inv_moment[Y][X] = m_inv_moment_body[X] * m_orientation_matrix[Y][X];
	m_inv_moment[Y][Y] = m_inv_moment_body[Y] * m_orientation_matrix[Y][Y];
	m_inv_moment[Y][Z] = m_inv_moment_body[Z] * m_orientation_matrix[Y][Z];
	m_inv_moment[Z][X] = m_inv_moment_body[X] * m_orientation_matrix[Z][X];
	m_inv_moment[Z][Y] = m_inv_moment_body[Y] * m_orientation_matrix[Z][Y];
	m_inv_moment[Z][Z] = m_inv_moment_body[Z] * m_orientation_matrix[Z][Z];
	
	Mth::Matrix orientation_matrix_transpose;
	orientation_matrix_transpose.Transpose(m_orientation_matrix);
	m_inv_moment = m_inv_moment * orientation_matrix_transpose;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CVehicleComponent::determine_effective_gear ( float wheel_rotvel )
{
	int gear = 0;
	
	do
	{
		float engine_rotvel = wheel_rotvel * m_engine.differential_ratio * m_engine.p_gear_ratios[gear];
		
		if (engine_rotvel > m_engine.upshift_rotvel)
		{
			if (gear < m_engine.num_gears - 1)
			{
				gear++;
				continue;
			}
		}
		return gear;
	}
	while (true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::update_collision_cache (   )
{
	Mth::CBBox collision_bbox;
	
	// add wheel location feelers
	for	(int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		collision_bbox.AddPoint(wheel.feeler_start_world);
		collision_bbox.AddPoint(wheel.feeler_end_world);
	}
	
	// add body collision feelers
	for (int collider_idx = vVP_NUM_COLLIDERS; collider_idx--; )
	{
		SCollider& collider = mp_colliders[collider_idx];
		
        collision_bbox.AddPoint(collider.world.m_corner);
		collision_bbox.AddPoint(collider.world.m_corner + collider.world.m_first_edge);
		collision_bbox.AddPoint(collider.world.m_corner + collider.world.m_second_edge);
		collision_bbox.AddPoint(collider.world.m_corner + collider.world.m_first_edge + collider.world.m_second_edge);
	}
	
	m_collision_cache.Update(collision_bbox);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::update_wheel_heights (   )
{
	// We crap out and don't do real wheel dynamics.  Instead, if the ground is within a wheel's hang point, we teleport the wheel to the
	// ground.  If not, we teleport the wheel to its hang point.
	
	CFeeler feeler;
	
	feeler.SetCache(&m_collision_cache);
	
	m_num_wheels_in_contact = 0;
	for	(int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		// y_offset a feeler from the top of the wheel's y_offset to the wheel's hang point plus its radius
		feeler.m_start = wheel.feeler_start_world;
		feeler.m_end = wheel.feeler_end_world;
		feeler.SetIgnore(mFD_NON_COLLIDABLE, 0);
		
		if (feeler.GetCollision(false))
		{
			// trip any triggers
			if (wheel.state != SWheel::OUT_OF_CONTACT)
			{
				if (feeler.GetSector() != wheel.last_ground_feeler.GetSector())
				{
					trip_trigger(TRIGGER_SKATE_OFF, wheel.last_ground_feeler);
					if (reset_this_frame()) return;
					trip_trigger(TRIGGER_SKATE_ONTO, feeler);
					if (reset_this_frame()) return;
				}
			}
			else
			{
				trip_trigger(TRIGGER_SKATE_ONTO, feeler);
				if (reset_this_frame()) return;
			}
			
			wheel.last_ground_feeler = feeler;
			
			wheel.state = SWheel::NO_STATE;
			wheel.contact_normal = feeler.GetNormal();
			wheel.y_offset = feeler.GetDist() * (wheel.y_offset_hang - wheel.radius) + wheel.radius;
			
			m_num_wheels_in_contact++;
			
			feeler.m_end = feeler.GetPoint();
		}
		else
		{
			// trip any triggers
			if (wheel.state != SWheel::OUT_OF_CONTACT)
			{
				trip_trigger(TRIGGER_SKATE_OFF, wheel.last_ground_feeler);
				if (reset_this_frame()) return;
			}
			
			wheel.state = SWheel::OUT_OF_CONTACT;
			wheel.y_offset = wheel.y_offset_hang;
		}
		
		// Allows CAP kill planes to work on the car.
		if (Ed::CParkEditor::Instance()->UsingCustomPark())
		{
			// now check for non-collidable trigger polys
			feeler.SetIgnore(0, mFD_NON_COLLIDABLE | mFD_TRIGGER);
			if (feeler.GetCollision(false))
			{
				trip_trigger(TRIGGER_BONK, feeler);
				if (reset_this_frame()) return;
			}
		}
		
	} // END loop over wheels
	
	// update the wheels' dependent variables which depend of the wheel heights
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		wheel.pos_world = wheel.pos;
		wheel.pos_world[Y] += wheel.y_offset - wheel.radius;
		wheel.pos_world = m_orientation_matrix.Rotate(wheel.pos_world);
		
		wheel.vel_world = calculate_body_point_velocity(wheel.pos_world);
	} // END loop over wheels
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::update_steering_angles (   )
{
	// NOTE: could easily use a table here
	
	m_steering_display = Mth::Lerp(m_steering_display, m_controls.steering, 10.0f * m_time_step);
	
	float left_steering_angle;
	float right_steering_angle;
	float left_steering_angle_display;
	float right_steering_angle_display;
	
	if (Mth::Abs(m_controls.steering) > 0.001f)
	{
		float turning_radius = m_cornering_wheelbase / tanf(m_controls.steering * m_max_steering_angle);
		 
		if (turning_radius > 0.0f)
		{
			left_steering_angle = -atan2f(m_cornering_wheelbase, turning_radius - m_cornering_axle_length / 2.0f);
			right_steering_angle = -atan2f(m_cornering_wheelbase, turning_radius + m_cornering_axle_length / 2.0f);
		}
		else
		{
			left_steering_angle = atan2f(m_cornering_wheelbase, -turning_radius + m_cornering_axle_length / 2.0f);
			right_steering_angle = atan2f(m_cornering_wheelbase, -turning_radius - m_cornering_axle_length / 2.0f);
		}
	}
	else
	{
		left_steering_angle = right_steering_angle = 0.0f;
	}
		
	if (Mth::Abs(m_steering_display) > 0.001f)
	{
		float turning_radius_display = m_cornering_wheelbase / tanf(m_steering_display * m_max_steering_angle);
		 
		if (turning_radius_display > 0.0f)
		{
			left_steering_angle_display = -atan2f(m_cornering_wheelbase, turning_radius_display - m_cornering_axle_length / 2.0f);
			right_steering_angle_display = -atan2f(m_cornering_wheelbase, turning_radius_display + m_cornering_axle_length / 2.0f);
		}
		else
		{
			left_steering_angle_display = atan2f(m_cornering_wheelbase, -turning_radius_display + m_cornering_axle_length / 2.0f);
			right_steering_angle_display = atan2f(m_cornering_wheelbase, -turning_radius_display - m_cornering_axle_length / 2.0f);
		}
	}
	else
	{
		left_steering_angle_display = right_steering_angle_display = 0.0f;
	}
	
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		if (wheel.steering == SWheel::FIXED) continue;
		
        wheel.steering_angle = (wheel.steering == SWheel::LEFT ? left_steering_angle : right_steering_angle);
        wheel.steering_angle_display = (wheel.steering == SWheel::LEFT ? left_steering_angle_display : right_steering_angle_display);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::damp_rotation (   )
{
	// no damping when handbraking on the ground
	if (m_controls.handbrake && m_num_wheels_in_contact > 2) return;
	
	if (!m_controls.throttle && m_controls.steering != 0.0f && m_num_wheels_in_contact > 2) return;
	
	if (in_artificial_collision()) return;
	
	// apply quadtratic damping
	
	// quadratic damping in the air makes jump recovery much easier; the vehicle lands on its wheels much more often
	float quad_damping = m_time_step * m_quad_rotvel_damping * m_rotmom.LengthSqr();
	
	// prevent reversal
	if (quad_damping > m_rotmom.Length())
	{
		m_rotmom.Set(0.0f, 0.0f, 0.0f);
	}
	else
	{
		Mth::Vector direction = m_rotmom;
		direction.Normalize();
		m_rotmom -= quad_damping * direction;
	}

	// apply constant damping; only applied in the Y direction
	
	// apply only if we're not steering, we're on the ground, and we're relatively upright
	if (m_controls.steering == 0.0f && m_num_wheels_in_contact > 2 && m_orientation_matrix[Y][Y] > 0.5f)
	{
		float const_damping = m_time_step * m_const_rotvel_damping;;
		
		// prevent reversal
		if (Mth::Abs(const_damping) > Mth::Abs(m_rotmom[Y]))
		{
			m_rotmom[Y] = 0.0f;
		}
		else
		{
			m_rotmom[Y] += (m_rotmom[Y] > 0.0f ? -const_damping : const_damping);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::accumulate_forces (   )
{
	// forces and torques on the body accumulate and are applied at the end of the frame; torques on the wheels are too large to accumulate;
	// they are applied to wheel velocity as they occur; thus, the order of application of wheel torques is critical
	
	apply_gravitational_forces();
	
	apply_suspension_forces();
	
    // we calculate the friction coefficients before applying engine torques so that friction will get the chance to counteract rotational
	// accelerations using static friction before the wheels spin out due to engine torque
	calculate_friction_coefficients();
	
	if (!m_controls.handbrake)
	{
		apply_engine_forces();
		
		apply_drag_forces();
		
		// give the brakes a chance to cancel the rotation due to engine torque and to lock up the wheels before friction effects
		apply_brake_forces();
		
		apply_friction_forces();
		
		// after friction, the brakes are given the opportunity to apply additional torque and relock the wheels
		apply_spare_brake_forces();
	}
	else
	{
		if (m_controls.throttle)
		{
			apply_engine_forces();
			
            // scale the handbrake's braking effect with the steering factor; thus, you power through sharp handbrake turns; yet, the handbrake
			// still brakes on straight aways
			apply_handbrake_forces(1.0f - Mth::Abs(m_controls.steering));

			apply_friction_forces();

			apply_spare_brake_forces();
		}
		else
		{
			apply_friction_forces();
			
			// lock wheels
			for (int n = m_num_wheels; n--; )
			{
				mp_wheels[n].rotvel = 0.0f;
				mp_wheels[n].rotacc = 0.0f;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::apply_gravitational_forces (   )
{
	// use reduced gravity when flipping the car
	if (m_in_flip && Tmr::ElapsedTime(m_flip_start_time_stamp) > vVP_FLIP_DELAY)
	{
		m_force[Y] -= vVP_FLIP_GRAVITY_FACTOR * m_gravity_override_fraction * vVP_GRAVITATIONAL_ACCELERATION / m_inv_mass;
	}
	else
	{
		m_force[Y] -= m_gravity_override_fraction * vVP_GRAVITATIONAL_ACCELERATION / m_inv_mass;
	}
	
	// we do our own force accumulation for this simple and constant force, having the same effect as the following code
	// Mth::Vector force(0.0f, -vVP_GRAVITATIONAL_ACCELERATION / m_inv_mass, 0.0f);
	// Mth::Vector location = m_suspension_center_of_mass;
	// accumulate_force(force, location, MAKE_RGB(50, 50, 0));
	
	if (m_draw_debug_lines)
	{
		Gfx::AddDebugLine(m_pos, m_pos + 0.05f * Mth::Vector(0.0f, -vVP_GRAVITATIONAL_ACCELERATION / m_inv_mass, 0.0f), MAKE_RGB(50, 50, 0), MAKE_RGB(50, 50, 0), 1);
	}
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::apply_suspension_forces (   )
{
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		// if not in contact with the ground, y_offset == y_offset_hang
		if (wheel.state == SWheel::OUT_OF_CONTACT)
		{
			// set this frame's history to the default value to smooth out landings
			wheel.normal_force_history[m_next_normal_force_history_idx] = vVP_GRAVITATIONAL_ACCELERATION / m_inv_mass / m_num_wheels;
			continue;
		}
		
		// direction of the suspension line; commented version is slower but explicit
		// Mth::Vector direction = m_orientation_matrix.Rotate(Mth::Vector(0.0f, 1.0f, 0.0f));
		Mth::Vector direction = m_orientation_matrix[Y];
		
		// magnitude of the spring force along the contact normal
		float magnitude = -wheel.spring * (wheel.y_offset_hang - wheel.y_offset) * Mth::DotProduct(wheel.contact_normal, direction);
		
		// velocity of the wheel projected into the contact normal direction
		float projected_vel = Mth::DotProduct(wheel.vel_world, wheel.contact_normal);
		
		// magnitude adjusted for damping
		magnitude += -wheel.damping * projected_vel;
		
		// suspension force
		Mth::Vector force = magnitude * wheel.contact_normal;
		
		accumulate_force(force, wheel.pos_world, MAKE_RGB(100, 100, 0));
		
		// set the normal force magnitude; used in the friction force code
		
		wheel.normal_force_history[m_next_normal_force_history_idx] = magnitude;
		
		wheel.normal_force = 0.0f;
		for (int i = vVP_NORMAL_FORCE_HISTORY_LENGTH; i--; )
		{
			wheel.normal_force += wheel.normal_force_history[i];
		}
		wheel.normal_force /= vVP_NORMAL_FORCE_HISTORY_LENGTH;
	}
	
	m_next_normal_force_history_idx = ++m_next_normal_force_history_idx % vVP_NORMAL_FORCE_HISTORY_LENGTH;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::apply_friction_forces (   )
{
	// NOTE: we've handled longitudinal friction reversal successfully; however, tangential friction reversal is still a significant issue for large
	// friction coefficients and high centers of mass
	
	// we use a simple tire model were the friction force is opposite the velocity of the tire contact and proportional to the normal force and a
	// friction coefficient; the friction coefficient is a function of the velocity of the contact with the following form:
	// 0 < v < min_static_velocity: rises linearly from zero to static_friction
	// min_static_velocity < v < max_static_velocity: constant at static_friction
	// max_static_velocity < v < min_dynamic_velocity: falls linearly from static_friction to dynamic_friction
	// min_dynamic_velocity < v: constant at dynamic_friction
	
	// NOTE: because friction is zero at zero slip velocity, hill slippage is an issue
	
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		if (wheel.state == SWheel::OUT_OF_CONTACT) continue;
		
		// grab the cached wheel direction and velocity projected into the contact plane
		Mth::Vector projected_wheel_direction = wheel.cache_projected_direction;
		Mth::Vector projected_vel = wheel.cache_projected_vel;
		
		// subtract the contact patch velocity; positive wheel rotational velocity equals negative contact patch velocity
		projected_vel -= wheel.rotvel * wheel.radius * projected_wheel_direction;
		
		// store the slip vel for later display
		wheel.slip_vel = projected_vel.Length();
		
		// if the velocity is too small, its direction is meaningless
		if (wheel.slip_vel < 0.000001) continue;
		
		// propose a frictional force
		Mth::Vector vel_direction = projected_vel;
		vel_direction.Normalize();
		Mth::Vector proposed_force = -wheel.friction_coefficient * wheel.normal_force * vel_direction;
		
		// now we must check that this force is not more that the force which would stop the relative longitudinal velocity
		
		// calculate the torque required to stick the wheel contact patch to the ground
		float longitudinal_vel = Mth::DotProduct(projected_vel, projected_wheel_direction);
		float stopping_rotvel = longitudinal_vel / wheel.radius;
		float stopping_torque = -calculate_stopping_torque(wheel, stopping_rotvel);
		
		// calculate the proposed torque
		float proposed_torque = -wheel.radius * Mth::DotProduct(proposed_force, projected_wheel_direction);
		
		Mth::Vector force;
		float torque;
		
		// if the proposed torque is less than what is required to stop the rotation
		if (Mth::Abs(proposed_torque) < Mth::Abs(stopping_torque))
		{
			// we're ok
			force = proposed_force;
			torque = proposed_torque;
		}
		else
		{
			// otherwise, reduce the proposed longitudinal force to only the stopping force
			float proposed_longitudinal_force = Mth::DotProduct(proposed_force, projected_wheel_direction);
			float stopping_longitudinal_force = -stopping_torque / wheel.radius;
			force = proposed_force + (stopping_longitudinal_force - proposed_longitudinal_force) * projected_wheel_direction;
			
			torque = stopping_torque;
		}
		
		// apply the force to the body
		accumulate_force(force, wheel.pos_world, MAKE_RGB(0, 0, 255));
		
		// apply the torque to the wheel
		apply_wheel_torque(wheel, torque);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::apply_drag_forces (   )
{
	if (m_controls.throttle || m_controls.reverse || m_controls.brake) return;
	
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		if (!wheel.drive || wheel.state == SWheel::OUT_OF_CONTACT) continue;
		
		// determine the effective engine gear
		int effective_gear = determine_effective_gear(Mth::Abs(wheel.rotvel));
		
		// drag torque scales like the square of the gear ratio
		float torque = m_engine.drag_torque * m_engine.p_gear_ratios[effective_gear] * m_engine.p_gear_ratios[effective_gear] / m_num_drive_wheels;
		
		// prevent reversal
		float maximum_torque = Mth::Abs(calculate_stopping_torque(wheel, wheel.rotvel));
		if (torque > maximum_torque)
		{
			torque = maximum_torque;
		}
		
		apply_wheel_torque(wheel, wheel.rotvel < 0.0f ? torque : -torque);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::apply_engine_forces (   )
{
	// NOTE: test
	float wall_climb_factor = Mth::ClampMax(m_orientation_matrix[Y][Y] * m_orientation_matrix[Y][Y] / 0.9f, 1.0f);
	
	if ((!m_controls.throttle && !m_controls.reverse) || m_controls.brake) return;
	
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		if (!wheel.drive) continue;
		
		// turn off power to front wheels when power handbraking
		if (m_controls.handbrake && wheel.steering != SWheel::FIXED) continue;
		
		// determine effective engine gear
		int effective_gear;
		if (!m_controls.reverse)
		{
			effective_gear = determine_effective_gear(wheel.rotvel > 0.0f ? wheel.rotvel : 0.0f);
		}
		else
		{
			effective_gear = determine_effective_gear(wheel.rotvel < 0.0f ? -wheel.rotvel : 0.0f);
		}
		
		float torque = m_engine.drive_torque * m_engine.differential_ratio * m_engine.p_gear_ratios[effective_gear] / m_num_drive_wheels;
		torque *= wall_climb_factor;
		
		// reverse gears are scaled down by a factor
		if (m_controls.reverse)
		{
			torque *= -m_engine.reverse_torque_ratio;
		}
		
		// account for low of power in front wheels in four-wheel drive car when power handbraking
		if (m_controls.handbrake && m_num_drive_wheels == 4) torque *= 2.0f;
		
		apply_wheel_torque(wheel, torque);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::apply_brake_forces (   )
{
	if (!m_controls.brake) return;
	
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		float torque = wheel.brake.torque;
		
		float maximum_torque = Mth::Abs(calculate_stopping_torque(wheel, wheel.rotvel));
		
		if (torque > maximum_torque)
		{
			wheel.brake.spare_torque = torque - maximum_torque;
			torque = maximum_torque;
		}
		else
		{
			wheel.brake.spare_torque = 0.0f;
		}
		
		apply_wheel_torque(wheel, wheel.rotvel < 0.0f ? torque : -torque);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::apply_handbrake_forces ( float application_factor )
{
	Dbg_Assert(m_controls.handbrake);
	
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		float torque = application_factor * wheel.brake.handbrake_torque;
		
		float maximum_torque = Mth::Abs(calculate_stopping_torque(wheel, wheel.rotvel));
		
		if (torque > maximum_torque)
		{
			wheel.brake.spare_torque = torque - maximum_torque;
			torque = maximum_torque;
		}
		else
		{
			wheel.brake.spare_torque = 0.0f;
		}
		
		apply_wheel_torque(wheel, wheel.rotvel < 0.0f ? torque : -torque);
	}
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::apply_spare_brake_forces (   )
{
	if (!m_controls.brake) return;
	
	for (int n = m_num_wheels; n--; )
	{
		SWheel& wheel = mp_wheels[n];
		
		if (wheel.brake.spare_torque == 0.0f) continue;
		
		float torque = wheel.brake.spare_torque;
		
		float maximum_torque = Mth::Abs(calculate_stopping_torque(wheel, wheel.rotvel));
		
		if (torque > maximum_torque)
		{
			torque = maximum_torque;
		}
		
		apply_wheel_torque(wheel, wheel.rotvel < 0.0f ? torque : -torque);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
			
float CVehicleComponent::calculate_collision_depth ( const SCollisionPoint& collision_point, const SCollider& collider ) const
{
	enum EDepthDirection
	{
		WITH_FIRST_EDGE, AGAINST_FIRST_EDGE, WITH_SECOND_EDGE, AGAINST_SECOND_EDGE
	};
	
	// calculate displacement from base corner
	Mth::Vector displacement = collision_point.pos - collider.world.m_corner;
	
	// determine the minimum depth and correponding depth direction
	
	float min_depth = Mth::DotProduct(displacement, collider.first_edge_direction_world);
	EDepthDirection depth_direction = WITH_FIRST_EDGE;
	
	float depth = collider.first_edge_length - min_depth;
	if (depth < min_depth)
	{
		min_depth = depth;
		depth_direction = AGAINST_FIRST_EDGE;
	}
	
	depth = Mth::DotProduct(displacement, collider.second_edge_direction_world);
	if (depth < min_depth)
	{
		min_depth = depth;
		depth_direction = WITH_SECOND_EDGE;
	}
	
	depth = collider.second_edge_length - depth;
	if (depth < min_depth)
	{
		min_depth = depth;
		depth_direction = AGAINST_SECOND_EDGE;
	}
	
	if (min_depth < 0.01f)
	{
		return 0.0f;
	}
	
	switch (depth_direction)
	{
		case WITH_FIRST_EDGE:
			depth = min_depth / Mth::DotProduct(collider.first_edge_direction_world, collision_point.normal);
			break;
			
		case AGAINST_FIRST_EDGE:
			depth = -min_depth / Mth::DotProduct(collider.first_edge_direction_world, collision_point.normal);
			break;
		
		case WITH_SECOND_EDGE:
			depth = min_depth / Mth::DotProduct(collider.second_edge_direction_world, collision_point.normal);
			break;
		
		case AGAINST_SECOND_EDGE:
			depth = -min_depth / Mth::DotProduct(collider.second_edge_direction_world, collision_point.normal);
			break;
	}
	
	#ifdef __USER_DAN__
	if (depth < 0.0f)
	{
		MESSAGE("negative depth bug");
	}
	#endif
	
	return depth;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
			
bool CVehicleComponent::check_for_capping ( SCollisionPoint& collision_point, const CRectFeeler& rect_feeler, const SCollider& collider, int collision_line_idx, int collision_point_end_idx ) const
{
	// check to see if this culled collision is the back of a cappable collision with a 2D object
	for (int check_line_idx = rect_feeler.GetNumMergedCollisionSurfaces(); check_line_idx--; )
	{
		if (check_line_idx == collision_line_idx) continue;
		
		for (int end = 2; end--; )
		{
			if (very_close(collision_point.pos, rect_feeler.GetMergedCollisionSurface(check_line_idx).ends[end].point)
				&& Mth::DotProduct(collision_point.normal, rect_feeler.GetMergedCollisionSurface(check_line_idx).normal) < -0.99)
			{
				// we'll use this point to cap the collision with the 2D object; the tangent cross the normal will point out of the triangle
				collision_point.normal = Mth::CrossProduct(
					rect_feeler.GetMergedCollisionSurface(collision_line_idx).ends[collision_point_end_idx].tangent,
					collision_point.normal
				);
				collision_point.depth = calculate_collision_depth(collision_point, collider);
				
				if (m_draw_debug_lines)
				{
					Gfx::AddDebugLine(
						collision_point.pos,
						collision_point.pos + (72) * collision_point.normal,
						MAKE_RGB(255, 100, 255), MAKE_RGB(255, 100, 255), 1
					);
				}
				
				return true;
			}
		} // END loop over checked collision line ends
	} // END loop over all other collision lines

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CVehicleComponent::consider_culling_point ( const SCollisionPoint& collision_point ) const
{
	// cull collision points which have a normal direction along their body offset
	
	Mth::Vector offset = collision_point.pos - m_pos;
	bool cull = Mth::DotProduct(offset, collision_point.normal) >= 0.0f;
	if (m_draw_debug_lines && cull)
	{
		uint32 c = MAKE_RGB(0, 0, 0);
		Gfx::AddDebugLine(collision_point.pos, collision_point.pos + 48.0f * collision_point.normal, c, c, 1);
	}
	return cull;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::apply_environment_collisions (   )
{
	m_max_normal_collision_impulse = 0.0f;
	
	CRectFeeler rect_feeler;
	CLineFeeler line_feeler;
	
	rect_feeler.SetCache(&m_collision_cache);
	line_feeler.SetCache(&m_collision_cache);
	
	m_num_collision_points = 0;
	for (int collider_idx = vVP_NUM_COLLIDERS; collider_idx--; )
	{
		SCollider& collider = mp_colliders[collider_idx];
		
		// first we use a rectangular feeler
		
		// setup the feeler
		rect_feeler.SetRectangle(collider.world);
		
		// detect collisions
		if (rect_feeler.GetCollision())
		{
			trip_trigger(TRIGGER_BONK, rect_feeler);
			if (reset_this_frame()) return;
			
			rect_feeler.MergeCollisionSurfaces();
			
			// copy out the collision points from the feeler output
			for (int collision_line_idx = rect_feeler.GetNumMergedCollisionSurfaces(); collision_line_idx--; )
			{
				for (int end = 2; end--; )
				{
					// add the collision line's start to the list of collision points
					SCollisionPoint& new_collision_point = sp_collision_points[m_num_collision_points];
					
					new_collision_point.line = false;
					
					new_collision_point.pos = rect_feeler.GetMergedCollisionSurface(collision_line_idx).ends[end].point;
					new_collision_point.normal = rect_feeler.GetMergedCollisionSurface(collision_line_idx).normal;
					if (!consider_culling_point(new_collision_point))
					{
						// if this is a keeper, calculate the collision depth
						if (rect_feeler.GetMergedCollisionSurface(collision_line_idx).ends[end].tangent_exists)
						{
							new_collision_point.depth = calculate_collision_depth(new_collision_point, collider);
						}
						else
						{
							new_collision_point.depth = 0.0f;
						}
						m_num_collision_points++;
					}
					else if (rect_feeler.GetMergedCollisionSurface(collision_line_idx).ends[end].tangent_exists)
					{
						if (check_for_capping(new_collision_point, rect_feeler, collider, collision_line_idx, end))
						{
							m_num_collision_points++;
						}
					}
				} // END loop over collision line ends
			} // END loop over collision surfaces
		} // END if we had a collision this collider
    
		if (m_draw_debug_lines)
		{
			rect_feeler.DebugLines(255, 255, 255, 1);
		}
		
		// line feelers

		line_feeler.m_start = m_pos;

		line_feeler.m_end = rect_feeler.m_corner;
		if (line_feeler.GetCollision(false))
		{
			trip_trigger(TRIGGER_BONK, line_feeler);
			if (reset_this_frame()) return;
			
			sp_collision_points[m_num_collision_points].pos = line_feeler.GetPoint();
			sp_collision_points[m_num_collision_points].normal = line_feeler.GetNormal();
			if (!consider_culling_point(sp_collision_points[m_num_collision_points]))
			{
				sp_collision_points[m_num_collision_points].depth = calculate_collision_depth(line_feeler);
				m_num_collision_points++;
				sp_collision_points[m_num_collision_points].line = true;
			}
		}
		if (m_draw_debug_lines)
		{
			line_feeler.DebugLine(255, 255, 255, 1);
		}


		line_feeler.m_end += rect_feeler.m_first_edge;
		if (line_feeler.GetCollision(false))
		{
			trip_trigger(TRIGGER_BONK, line_feeler);
			if (reset_this_frame()) return;
			
			sp_collision_points[m_num_collision_points].pos = line_feeler.GetPoint();
			sp_collision_points[m_num_collision_points].normal = line_feeler.GetNormal();
			if (!consider_culling_point(sp_collision_points[m_num_collision_points]))
			{
				sp_collision_points[m_num_collision_points].depth = calculate_collision_depth(line_feeler);
				m_num_collision_points++;
				sp_collision_points[m_num_collision_points].line = true;
			}
		}
		if (m_draw_debug_lines)
		{
			line_feeler.DebugLine(255, 255, 255, 1);
		}

		line_feeler.m_end += rect_feeler.m_second_edge;
		if (line_feeler.GetCollision(false))
		{
			trip_trigger(TRIGGER_BONK, line_feeler);
			if (reset_this_frame()) return;
			
			sp_collision_points[m_num_collision_points].pos = line_feeler.GetPoint();
			sp_collision_points[m_num_collision_points].normal = line_feeler.GetNormal();
			if (!consider_culling_point(sp_collision_points[m_num_collision_points]))
			{
				sp_collision_points[m_num_collision_points].depth = calculate_collision_depth(line_feeler);
				m_num_collision_points++;
				sp_collision_points[m_num_collision_points].line = true;
			}
		}
		if (m_draw_debug_lines)
		{
			line_feeler.DebugLine(255, 255, 255, 1);
		}

		line_feeler.m_end -= rect_feeler.m_first_edge;
		if (line_feeler.GetCollision(false))
		{
			trip_trigger(TRIGGER_BONK, line_feeler);
			if (reset_this_frame()) return;
			
			sp_collision_points[m_num_collision_points].pos = line_feeler.GetPoint();
			sp_collision_points[m_num_collision_points].normal = line_feeler.GetNormal();
			if (!consider_culling_point(sp_collision_points[m_num_collision_points]))
			{
				sp_collision_points[m_num_collision_points].depth = calculate_collision_depth(line_feeler);
				m_num_collision_points++;
				sp_collision_points[m_num_collision_points].line = true;
			}
		}
		if (m_draw_debug_lines)
		{
			line_feeler.DebugLine(255, 255, 255, 1);
		}
	} // END loop over colliders
	
	if (m_num_collision_points == 0) return;
	
	// zero accumulators
	for (int collision_idx = m_num_collision_points; collision_idx--; )
	{
		sp_collision_points[collision_idx].normal_impulse = 0.0f;
	}
	
	// cache the momentums and velocities before normal impulses are applied; they are used when determining the frictional forces
	Mth::Vector cache_mom = m_mom;
	Mth::Vector cache_rotmom = m_rotmom;
	Mth::Vector cache_vel = m_vel;
	Mth::Vector cache_rotvel = m_rotvel;
	
	// apply normal impulses
	
	// loop over normal impulse points until all collisions are fully resolved
	int pass_count = 0;
	bool clean_pass;
	do
	{
		clean_pass = true;
		
		for (int collision_idx = m_num_collision_points; collision_idx--; )
		{
			SCollisionPoint& collision = sp_collision_points[collision_idx];
			
			// calculate the collision position in body space
			Mth::Vector pos_local = collision.pos - m_pos;
			
			// calculate the velocity and normal velocity of the collision point
			Mth::Vector vel = calculate_body_point_velocity(pos_local);
			float normal_vel = Mth::DotProduct(vel, collision.normal);
			
			// ignore if we're not impacting
			if (normal_vel > 0.0f) continue;
			clean_pass = false;
			
			// calculate the goal velocity of the collision
			float goal_normal_velocity = -m_body_restitution * normal_vel;
			
			// calculate the effective mass of the collision point
			float effective_mass = calculate_body_point_effective_mass(pos_local, collision.normal);
			
			// calculate the normal impulse required to reach the body velocity
			float normal_impulse = (goal_normal_velocity - normal_vel) * effective_mass;
			
			// accumulate the total normal impulse applied to this collision point
			collision.normal_impulse += normal_impulse;
			
			// setup the required impulse vector
			Mth::Vector impulse = normal_impulse * collision.normal;
			
			// apply the normal impulse immediately
			apply_impulse(impulse, pos_local);
		}

		if (++pass_count > 5)
		{
			#ifdef __USER_DAN__
			MESSAGE("environment collision resolution pass count limit exceeded");
			#endif
			break;
		}
	} while (!clean_pass);
	
    if (m_draw_debug_lines)
	{
		for (int collision_idx = m_num_collision_points; collision_idx--; )
		{
			// SCollisionPoint& collision = sp_collision_points[collision_idx];
			// Gfx::AddDebugLine(
				// collision.pos + collision.normal,
				// collision.pos + collision.normal + collision.normal_impulse / 2.0f * collision.normal,
				// MAKE_RGB(0, 100, 100), MAKE_RGB(0, 100, 100), 1
			// );
		}
	} // END loop over collision points

	// apply penalty forces to prevent interpenetration
	for (int collision_idx = m_num_collision_points; collision_idx--; )
	{
		SCollisionPoint& collision = sp_collision_points[collision_idx];
		
		// collision normal is facing opposite the depth
		// BUG: too many rect feeler collisions have negative depth!!!
		if (collision.depth <= 0.0f) continue;
		
		// extremely deep collisions are mostly collisions with the underbody which are actually shallow in the Y direction; for now we'll cap such
		// collisions and perhaps allow the car to sink down into geometry which comes from below but misses the wheels
		if (collision.depth > 2.0f)
		{
			collision.depth = 2.0f;
		}
		
		Mth::Vector force = m_body_spring * collision.depth * collision.normal;
		
		accumulate_collision_force(force, collision.pos - m_pos);
		
		// Gfx::AddDebugLine(
			// collision.pos,
			// collision.pos + (2 * collision.depth) * collision.normal,
			// MAKE_RGB(255, 255, 0), MAKE_RGB(255, 255, 0), 1
		// );
	}
	
	float body_friction = m_orientation_matrix[Y][Y] > 0.707f ? m_body_friction : m_body_wipeout_friction;
	
	// apply a friction impulse at each point
	for (int collision_idx = m_num_collision_points; collision_idx--; )
	{
		SCollisionPoint& collision = sp_collision_points[collision_idx];
		
		if (collision.normal_impulse == 0.0f) continue;
		
		// calculate the local collision position
		Mth::Vector pos_local = collision.pos - m_pos;
		
		// calculate the tangential velocity
		Mth::Vector	vel = cache_vel + Mth::CrossProduct(cache_rotvel, pos_local);
		Mth::Vector tanjent = vel - Mth::DotProduct(vel, collision.normal) * collision.normal;
		float tanjent_vel = tanjent.Length();
		if (tanjent_vel < 0.001f) continue;
		tanjent /= tanjent_vel;
		
		// calculate the effective mass of the collision point
		float effective_mass = calculate_body_point_effective_mass(pos_local, tanjent);
		
		// calculate the tanjential impulse required to stop the tanjential velocity
		float tanjent_impulse = tanjent_vel * effective_mass;
		
		// cap the frictional force at the normal force times the coefficient of friction
		float max_tanjent_impulse = body_friction * collision.normal_impulse;
		
		if (tanjent_impulse > max_tanjent_impulse)
		{
			tanjent_impulse = max_tanjent_impulse;
		}
		
		// setup the tanjent impulse vector
		Mth::Vector impulse = -tanjent_impulse * tanjent;
		
		// apply the friction impulse to the true state
		apply_impulse(impulse, pos_local);
		
		if (m_draw_debug_lines)
		{
			// Gfx::AddDebugLine(
				// collision.pos + collision.normal,
				// collision.pos + collision.normal + impulse / 2.0f,
				// MAKE_RGB(100, 0, 100), MAKE_RGB(100, 0, 100), 1
			// );
		}
		
		// apply the friction impulse to the state we are using for friction calculations
		cache_mom += impulse;
		cache_rotmom += Mth::CrossProduct(pos_local, impulse);
		cache_vel = m_inv_mass * cache_mom;
		cache_rotvel = m_inv_moment.Rotate(cache_rotmom);
	} // END loop over collision points
	
	// determine the maximum normal impulse this frame
	for (int collision_idx = m_num_collision_points; collision_idx--; )
	{
		SCollisionPoint& collision = sp_collision_points[collision_idx];
		
		m_max_normal_collision_impulse = Mth::Max(collision.normal_impulse, m_max_normal_collision_impulse);
	}
	
	// no player controlled impulses in the air	or with handbrake on
	if (m_controls.steering == 0.0f || m_num_wheels_in_contact < 3 || m_controls.handbrake) return;
	
	// extra player controled collision impulse; allows one slight control over the direction on collision impulses
	for (int collision_idx = m_num_collision_points; collision_idx--; )
	{
		SCollisionPoint& collision = sp_collision_points[collision_idx];
				
		Mth::Vector pos_local = collision.pos - m_pos;
		
		float strength = m_controls.steering * m_collision_control * Mth::DotProduct(m_orientation_matrix[Z], collision.normal) * collision.normal_impulse;
		
		Mth::Vector direction(-collision.normal[Z], 0.0f, collision.normal[X]);
		
		apply_impulse(strength * direction, pos_local);
		
		if (m_draw_debug_lines)	  
		{
			Gfx::AddDebugLine(
				collision.pos + collision.normal,
				collision.pos + collision.normal + strength * direction / 5.0f,
				MAKE_RGB(255, 0, 100), MAKE_RGB(255, 0, 100), 1
			);
		}
	}
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::update_pos_with_uber_frig ( const Mth::Vector& movement )
{
	// The vehicle has two uber frigs.  The first treats the c.o.m. as a point particle with respect to the geo.  The seconds is a traditional uber frig.
	
	Mth::Vector frame_start_pos = m_pos;
	
	// look for collisions along the origin's movement; only do a single bounce, assuming the remaining movement is ok
	CFeeler feeler(m_pos, m_pos + movement);
	if (feeler.GetCollision(false))
	{
		// bounce off the surface
		MESSAGE("less than uber frig");
		
		m_pos = feeler.GetPoint() + feeler.GetNormal();
		
		Mth::Vector remaining_movement = feeler.GetDist() * movement;
		remaining_movement -= 2.0f * Mth::DotProduct(remaining_movement, feeler.GetNormal()) * feeler.GetNormal();
		m_pos += remaining_movement;
		
		// use coeff of restit of 0.5
		m_mom -= 1.5f * Mth::DotProduct(m_mom, feeler.GetNormal()) * feeler.GetNormal();
	}
	else
	{
		m_pos += movement;
	}
	
	// now, a traditional uber frig
	feeler.m_start = m_pos;
	feeler.m_end = feeler.m_start;
	feeler.m_end[Y] -= FEET(400.0f);
	if (!feeler.GetCollision(false))
	{
		MESSAGE("uber frig");
		m_pos = frame_start_pos;
		m_mom *= 1.0f;
		m_rotmom *= 1.0f;
	}
	else
	{
		mp_model_component->ApplyLightingFromCollision(feeler);
	}
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::slerp_to_face_velocity (  )
{
	// slerp to face our velocity
	
	if (m_in_flip) return;
	
	if (in_artificial_collision()) return;
	
	// only slerp if horizontal velocity is below a given threshold
	bool vel_below_threshold = m_vel[X] * m_vel[X] + m_vel[Z] * m_vel[Z] < m_in_air_slerp_vel_cutoff * m_in_air_slerp_vel_cutoff;
	if (vel_below_threshold && !m_vert_correction) return;

	// extract the current orientation matrix
	m_orientation.GetMatrix(m_orientation_matrix);
	m_orientation_matrix[W].Set();

	// construct our target matrix
	Mth::Matrix target;
	if (vel_below_threshold)
	{
		target[Z] = m_orientation_matrix[Z];
		target[Z][Y] = 0.0f;
		target[Z].Normalize();
	}
	else
	{
		target[Z] = m_vel;
		target[Z].Normalize();
	}
	if (Mth::DotProduct(target[Z], m_orientation_matrix[Z]) < 0.0f)
	{
		target[Z] *= -1.0f;
	}
	target[Y].Set(0.0f, 1.0f, 0.0f);
	target[X] = Mth::CrossProduct(target[Y], target[Z]);
	target[Y] = Mth::CrossProduct(target[Z], target[X]);
	target[W].Set();

	// setup the slerp
	Mth::SlerpInterpolator slerper;
	slerper.setMatrices(&m_orientation_matrix, &target);

	// lerp up to full strength over time
	if (m_vel[X] * m_vel[X] + m_vel[Z] * m_vel[Z] > 350.0f * 350.0f)
	{
		slerper.getMatrix(&m_orientation_matrix, Mth::LinearMap(
			0.0f,
			m_in_air_slerp_strength * m_time_step,
			Mth::Min(m_air_time_no_collision, m_in_air_slerp_time_delay),
			0.0f,
			m_in_air_slerp_time_delay
		));
	}
	else
	{
		slerper.getMatrix(&m_orientation_matrix, Mth::LinearMap(
			0.0f,
			0.3f * m_in_air_slerp_strength * m_time_step,
			Mth::Min(m_air_time_no_collision, m_in_air_slerp_time_delay),
			0.0f,
			m_in_air_slerp_time_delay
		));
	}
	m_orientation = m_orientation_matrix;
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CVehicleComponent::calculate_body_point_effective_mass ( const Mth::Vector& pos, const Mth::Vector& direction ) const
{
	// calculate the effect on momentums due to a unit impulse applied at pos towards direction
	Mth::Vector delta_mom = direction;
	Mth::Vector delta_rotmom = Mth::CrossProduct(pos, direction);
	
	// calculate the resulting change in velocities
	Mth::Vector delta_vel = m_inv_mass * delta_mom;
	Mth::Vector delta_rotvel = m_inv_moment.Rotate(delta_rotmom);
	
	// calculate the corresponding change in the body point's velocity
	Mth::Vector delta_vel_body_point = delta_vel + Mth::CrossProduct(delta_rotvel, pos);
	
	// extract the change in velocity along the direction of interest
	float delta_vel_direction = Mth::DotProduct(direction, delta_vel_body_point);
	
	// return the effective mass of the body point in the direction of interest
	Dbg_Assert(delta_vel_direction != 0.0f);
	return 1.0f / delta_vel_direction;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::consider_sleeping (   )
{
	int last_consider_sleeping_count = m_consider_sleeping_count;
	m_consider_sleeping_count = 0;
	
	if (m_controls.throttle) return;
	
	if (m_controls.reverse && m_num_wheels_in_contact != 0) return;
	
	// only sleep if we're experiencing three or more collisions; hopefully, a surface-to-surface contact
	if (m_num_wheels_in_contact + m_num_collision_points < 3) return;
	
	// only sleep if we've got four-on-the-floor or are completely flipped
	if (m_num_wheels_in_contact != 0 && m_num_wheels_in_contact != m_num_wheels) return;

	// only sleep if we're moving slow
	if (m_vel.LengthSqr() > Mth::Sqr(vVP_SLEEP_VEL)) return;
	#ifdef __USER_DAN__
	DUMPF(m_vel.Length());
	#endif

	// only sleep if we're moving slow
	if (m_rotvel.LengthSqr() > Mth::Sqr(vVP_SLEEP_ROTVEL)) return;
	#ifdef __USER_DAN__
	DUMPF(m_rotvel.Length());
	#endif
	
	// never sleep during an artificial collision period
	if (in_artificial_collision()) return;
	
	m_consider_sleeping_count = last_consider_sleeping_count + 1;
	if (m_consider_sleeping_count < 3)
	{
		return;
	}
	
	// sleep
	m_mom.Set();
	m_rotmom.Set();
	m_force.Set(0.0f, 0.0f, 0.0f);
	m_torque.Set(0.0f, 0.0f, 0.0f);
	for (int n = m_num_wheels; n--; )
	{
		mp_wheels[n].rotacc = 0.0f;
	}
	m_state = ASLEEP;
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::get_input (   )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	// analog control
	// m_controls.steering = control_pad.m_scaled_leftX;
	m_controls.steering = control_pad.m_scaled_leftX * Mth::Abs(control_pad.m_scaled_leftX);
	
	// dpad control
	if (m_controls.steering == 0.0f)
	{
		if (control_pad.m_left.GetPressed() && !control_pad.m_right.GetPressed())
		{
			m_controls.steering = -1.0f;
		}
		else if (control_pad.m_right.GetPressed() && !control_pad.m_left.GetPressed())
		{
			m_controls.steering = 1.0f;
		}
	}
	
	m_controls.throttle = control_pad.m_x.GetPressed();
	m_controls.handbrake = control_pad.m_R1.GetPressed() && !m_no_handbrake;
	
	bool brake_pressed = control_pad.m_square.GetPressed() || (m_exitable && control_pad.m_triangle.GetPressed());
	bool exit_request = brake_pressed && !control_pad.m_square.GetPressed();
	
	// decide between brake and reverse
	if (brake_pressed)
	{
		if ((m_controls.reverse || (Mth::DotProduct(m_vel, m_orientation_matrix[Z]) < 300.0f && m_num_wheels_in_contact != 0))
			&& (m_state != ASLEEP || m_num_wheels_in_contact != 0))
		{
			// reverse
			m_controls.reverse = true;
			m_controls.brake = false;
		}
		else
		{
			// brake
			m_controls.brake = true;
			m_controls.reverse = false;
		}
		
		if (exit_request)
		{
			if (Mth::DotProduct(m_vel, m_orientation_matrix[Z]) < 0.0f)
			{
				// brake
				m_controls.brake = true;
				m_controls.reverse = false;
			}
			
			if (Mth::Abs(Mth::DotProduct(m_vel, m_orientation_matrix[Z])) < 30.0f)
			{
				// signal an exit
				GetObject()->BroadcastEvent(CRCD(0xcbaa3476, "ExitVehicleRequest"));
			}
		}
	}
	else
	{
		m_controls.reverse = false;
		m_controls.brake = false;
	}
	
	if (m_force_brake)
	{
		m_controls.brake = true;
		m_controls.reverse = false;
	}
	
	if (m_state == ASLEEP && (m_controls.throttle || m_controls.reverse))
	{
		m_state = AWAKE;
		m_consider_sleeping_count = 0;
	}
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::zero_input (   )
{
	m_controls.brake = false;
	m_controls.handbrake = false;
	m_controls.throttle = false;
	m_controls.reverse = false;
	m_controls.steering = 0.0f;
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::update_skeleton (   )
{
	Mth::Matrix* p_matrices = mp_skeleton_component->GetSkeleton()->GetMatrices();
	for (int i = mp_skeleton_component->GetSkeleton()->GetNumBones(); i--; )
	{
		Mth::Matrix& matrix = p_matrices[i];
		
		// setup the matrix for each bone in the skeleton
		
		if (m_draw_debug_lines == 2)
		{
			matrix.Zero();
			continue;
		}
		
		// shadow
		if (i == 0)
		{
			matrix.Zero();
		}
		
		// body
		else if (i == 1)
		{
			matrix.Zero();
			matrix[X][X] = 1.0f;
			matrix[Y][Z] = -1.0f;
			matrix[Z][Y] = 1.0f;
			matrix[W] = m_body_pos;
		}
		
		// wheel
		else
		{
			SWheel& wheel = mp_wheels[i - 2];
			
			matrix.Zero();
			matrix[X][X] = -1.0f;
			matrix[Y][Z] = 1.0f;
			matrix[Z][Y] = 1.0f;
			
			matrix.RotateZLocal(wheel.steering_angle_display);
			matrix.RotateXLocal(-wheel.orientation);
			matrix[W] = wheel.pos;
			#ifdef __NOPT_ASSERT__
			if (Script::GetInteger("use_max_y_offset"))
			{
				matrix[W][Y] += wheel.max_draw_y_offset;
			}
			else
			{
				matrix[W][Y] += Mth::ClampMax(wheel.y_offset, wheel.max_draw_y_offset);
			}
			#else
			matrix[W][Y] += Mth::ClampMax(wheel.y_offset, wheel.max_draw_y_offset);
			#endif
			matrix[W][W] = 1.0f;
		}
	}
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::draw_shadow (   )
{
	//Dbg_Message("Drawing shadow for car");

#if 0	// Experiment to draw a shadow using texture splats
	Mth::Vector start_pos = GetObject()->GetPos();
	Mth::Vector end_pos = start_pos;
	start_pos[Y] += 12.0f;
	end_pos[Y] -= 120.0f;

	float y_rot = 360.0f - m_orientation.GetVector()[Y];
	while (y_rot < 0.0f)
		y_rot += 360.0f;

	Nx::TextureSplat( start_pos, end_pos, 120.0f, 100.0f, 2.0f / (float) Config::FPS(), "blood_01", y_rot);
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::control_skater (   )
{
    // HACKY; setup the skater's position, orientation, and animation each frame
	
    mp_skater->SetPos(GetObject()->GetPos() + GetObject()->GetMatrix().Rotate(m_skater_pos));
	mp_skater->SetMatrix(GetObject()->GetMatrix());
	mp_skater_core_physics_component->ResetLerpingMatrix();
	mp_skater->SetVel(GetObject()->GetVel());
	
	if (!m_skater_visible) return;
	
	float target_time = Mth::LinearMap(
		0.0f,
		mp_skater_animation_component->AnimDuration(m_skater_anim),
		mp_skater_core_physics_component->GetFlag(FLIPPED) ? m_steering_display : -m_steering_display,
		-1.0f,
		1.0f
	);
	
	if (!GameNet::Manager::Instance()->InNetGame())
	{
		mp_skater_animation_component->PlayPrimarySequence(m_skater_anim, false, target_time, 1000.0f, Gfx::LOOPING_CYCLE, 0.0f, 0.0f);
	}
	else
	{
		// HACK to reduce number of animation events when driving cars
		static int anim_send_count = 0;
		if (++anim_send_count == 15)
		{
			anim_send_count = 0;
			mp_skater_animation_component->PlayPrimarySequence(m_skater_anim, true, target_time, 1000.0f, Gfx::LOOPING_CYCLE, 0.0f, 0.0f);
		}
		else
		{
			mp_skater_animation_component->PlayPrimarySequence(m_skater_anim, false, target_time, 1000.0f, Gfx::LOOPING_CYCLE, 0.0f, 0.0f);
		}
	}
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const int num_edges = 12;

Mth::Vector CVehicleComponent::wheel_point ( const SWheel& wheel, int i, bool side ) const
{
	Mth::Vector point(
		(side ? 1.0f : -1.0f) * 3.85f,
		wheel.radius * cosf(2.0f * 3.141592f * i / num_edges + wheel.orientation),
		wheel.radius * sinf(2.0f * 3.141592f * i / num_edges + wheel.orientation)
	);
	Mth::Matrix steering_rotation(Mth::Vector(0.0f, 1.0f, 0.0f), wheel.steering_angle);
	point = steering_rotation.Rotate(point);
	point[Y] += wheel.y_offset;
	point += wheel.pos;
	point = m_pos + m_orientation_matrix.Rotate(point);
	return point;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CVehicleComponent::draw_debug_rendering (   ) const
{
	if (m_draw_debug_lines)
	{
		for (int n = m_num_wheels; n--; )
		{
			SWheel& wheel = mp_wheels[n];
			
			int32 color = 0;
			switch (wheel.state)
			{
				case SWheel::UNDER_GRIPPING:
					color = MAKE_RGB(0, 255, 0);
					break;
				case SWheel::GRIPPING:
					color = MAKE_RGB(0, 0, 0);
					break;
				case SWheel::SLIPPING:
					color = MAKE_RGB(255, 200, 0);
					break;
				case SWheel::SKIDDING:
					color = MAKE_RGB(255, 0, 0);
					break;
				case SWheel::HANDBRAKE_THROTTLE:
					color = MAKE_RGB(150, 0, 150);
					break;
				case SWheel::HANDBRAKE_LOCKED:
					color = MAKE_RGB(0, 150, 150);
					break;
				case SWheel::NO_STATE:
				case SWheel::OUT_OF_CONTACT:
				default:
					color = MAKE_RGB(255, 255, 255);
					break;
			}
			
			for (int i = num_edges; i--; )
			{
				Mth::Vector start = wheel_point(wheel, i, true);
				Mth::Vector end = wheel_point(wheel, i + 1, true);
				Gfx::AddDebugLine(start, end, color, color, 1);
	
				start = wheel_point(wheel, i, false);
				end = wheel_point(wheel, i + 1, false);
				Gfx::AddDebugLine(start, end, color, color, 1);
				
				start = wheel_point(wheel, i, true);
				end = wheel_point(wheel, i, false);
				Gfx::AddDebugLine(start, end, color, color, 1);
				
				#if 1
				// draw axis of rotation lines
				Mth::Matrix steering_rotation(Mth::Vector(0.0f, 1.0f, 0.0f), wheel.steering_angle);
				start.Set(-240.0f, 0.0f, 0.0f);
				start = steering_rotation.Rotate(start);
				start += wheel.pos;
				start[Y] += wheel.y_offset;
				start = m_pos + m_orientation_matrix.Rotate(start);
				end.Set(240.0f, 0.0f, 0.0f);
				end = steering_rotation.Rotate(end);
				end += wheel.pos;
				end[Y] += wheel.y_offset;
				end = m_pos + m_orientation_matrix.Rotate(end);
				Gfx::AddDebugLine(start, end, MAKE_RGB(0, 0, 0), MAKE_RGB(0, 0, 0), 1);
				#endif
			}
		} // END loop over wheels
	}
	
	#if 1
	if (m_draw_debug_lines)
	{
		Gfx::AddDebugStar(m_pos + m_suspension_center_of_mass_world, 12.0f, MAKE_RGB(100, 0, 100), 1);
		Gfx::AddDebugStar(m_pos, 12.0f, MAKE_RGB(0, 100, 100), 1);
	}
	#endif
}

}


================================================
FILE: Code/Gel/Components/vehiclecomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       VehicleComponent.h
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#ifndef __COMPONENTS_VEHICLECOMPONENT_H__
#define __COMPONENTS_VEHICLECOMPONENT_H__

#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 

#define		CRC_VEHICLE CRCD(0xe47f1b79, "Vehicle")
#define		GetVehicleComponent() ((Obj::CVehicleComponent*)GetComponent(CRC_VEHICLE))
#define		GetVehicleComponentFromObject(pObj) ((Obj::CVehicleComponent*)(pObj)->GetComponent(CRC_VEHICLE))
		 
namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CInputComponent;
	class CAnimationComponent;
	class CSkaterCorePhysicsComponent;
	class CModelComponent;

#define vVP_GRAVITATIONAL_ACCELERATION								(386.4f)
#define vVP_MAX_NUM_GEARS											(6)
#define vVP_NUM_COLLIDERS											(2)

#define vVP_FLIP_DURATION											(5000)
#define vVP_FLIP_DELAY												(300)
#define vVP_FLIP_RATE												(3.0f)
#define vVP_FLIP_GRAVITY_FACTOR										(0.3f)
#define vVP_SLEEP_VEL												(1.0f)
#define vVP_SLEEP_ROTVEL											(0.01f)

class CVehicleComponent : public CBaseComponent
{
	friend class CVehicleSoundComponent;
	
	// number of frames of normal force history to record for each tire
	enum { vVP_NORMAL_FORCE_HISTORY_LENGTH = 10 };
	
	// HACK for control
	struct SControls {
		// steering; between -1.0 and 1.0
		float steering;
		
		// throttle
		bool throttle;
		
		// brake
		bool brake;
		
		// handbrake
		bool handbrake;
		
		// reverse
		bool reverse;
	};
	
	struct SEngine
	{
		// state variables
		// int gear;
		
		// constant characteristics
		
		// engine's base torque output before applying gear ratios
		float drive_torque;
		float drag_torque;
		
		int num_gears;
		
		float p_gear_ratios[vVP_MAX_NUM_GEARS];
		
		float reverse_torque_ratio;
		
		float differential_ratio;
		
		// float differential_stiffness;
		
		// float downshift_rotvel;
		
		float upshift_rotvel;
	};
	
	struct SBrake
	{
		// state variables
		
		// brake torque is applied both after engine torque and friction torque; here we track the unused torque between the applications
		float spare_torque;
		
		// constant characteristics
		
		// braking torque per wheel
		float torque;
		
		// strength of the handbrake torque when throttle is down and steering is forward
		float handbrake_torque;
	};
	
	struct SWheel
	{
		
		enum EStateType {
			NO_STATE, OUT_OF_CONTACT, UNDER_GRIPPING, GRIPPING, SLIPPING, SKIDDING, HANDBRAKE_THROTTLE, HANDBRAKE_LOCKED
		};
		
		enum ESteeringType {
			FIXED, LEFT, RIGHT
		};
		
		// state variables
		
		// offset along Y-axis of wheel's position in vehicle's frame
		float y_offset;
		
		// is the wheel out of contact, gripped, skidding, etc
		EStateType state;
		
		// angular velocity about the axle
		float rotvel;
		
		// angular position about the axle
		float orientation;
		
		// angle of deflection from forward
		float steering_angle;
		
		// displayed angle of deflection from forward; lerps behind steering_angle
		float steering_angle_display;
		
		// dependent variables
		
		// world frame position of the bottom of the wheel
		Mth::Vector pos_world;
		
		// world frame velocity of the bottom of the wheel, not counting rotation
		Mth::Vector vel_world;
		
		// magnitude of the normal force applied by the suspension through the tire; set in apply_suspension_forces(); used in apply_friction_forces()
		float normal_force;
		
		// magnitude of the normal force for the last few frames; friction uses a smoothed normal force to even out control
		float normal_force_history [ vVP_NORMAL_FORCE_HISTORY_LENGTH ];
		
		// normal of the wheel's contact surface
		Mth::Vector contact_normal;
		
		// friction coefficient this frame
		float friction_coefficient;
		
		// ground detector feeler start and end positions in world frame
		Mth::Vector feeler_start_world;
		Mth::Vector feeler_end_world;
		
		// NOTE: used only to draw skid indicators; remove if not used for anything else
		float slip_vel;
		
		// accumulators
		
		// total rotational acceleration applied so far this frame
		float rotacc;
		
		// constant characteristics
		
		// start of the wheel's downward pointing collision feeler; basically the top of the wheel's position in the vehicle's frame
		Mth::Vector pos;
		
		// type of wheel with respect to steering
		ESteeringType steering;
		
		// true if the wheel is a drive wheel
		bool drive;
		
		// hang point; wheel's y_offset will drop only to this point; the effective equilibrium point for the spring, if one accounts for the wheel's
		// weight ahead of time
		float y_offset_hang;
		
		// the wheels are never rendered above this y threshold; this has no affect on their location from the physics code's perspective
		float max_draw_y_offset;
		
		// radius of wheel and tire (in)
		float radius;
		
		// inverse moment of the wheel and tire around the axis (1 / lb / in^2)
		float inv_moment;
		
		// suspension's spring constant (lb / in); spring rate
		float spring;
		
		// suspension's damping constant (lb s / in); bump rate
		float damping;
		
		// the friction coefficient is a set of concatenated lines; the transitions are made as these velocities; a more sophisticated
		float min_static_velocity;
		float max_static_velocity;
		float min_dynamic_velocity;
		
		// static friction coefficient
		float static_friction;
		
		// dynamic friction coefficient
		float dynamic_friction;
		
		// friction coefficient during handbraking
		float handbrake_throttle_friction;
		float handbrake_locked_friction;
		
		// subelements
		
		// brake
        SBrake brake;
		
		// caches to avoid repeating calculations between friction coefficient calculation and friction application
		Mth::Vector cache_projected_direction;
		Mth::Vector cache_projected_vel;
		
		// the last feeler for this wheel's height which touched the ground
		CFeeler last_ground_feeler;
	};
	
	struct SCollisionPoint
	{
		// world position
		Mth::Vector pos;
		
		// impact normal
		Mth::Vector normal;
		
		// depth of collision as defined as the dot between the displacement from the nearest collider corner and the collision normal
		float depth;
		
		// normal impulse accumulator; used to set the maximum friction
		float normal_impulse;
		
		bool line;
	};
	
	struct SCollider
	{
		// the rectangle defining the collider in body space
		Mth::Rectangle body;
		
		// that rectangle transformed into world space
		Mth::Rectangle world;
		Mth::Vector first_edge_direction_world;
		Mth::Vector second_edge_direction_world;
		
		// a few precomputable values
		float first_edge_length;
		float second_edge_length;
	};
	
public:
	// inorder to interface with models and skeletons, the number of wheels must be hardcoded to four
	enum { vVP_NUM_WHEELS = 4 };
	
public:
    CVehicleComponent();
    virtual ~CVehicleComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
	virtual void					Finalize (   );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							MoveToNode ( Script::CStruct* p_node );
	void							ApplyArtificialCollision ( const Mth::Vector& offset );
	
	
	const Mth::Vector&				GetVel (   ) const { return m_vel; }
	int								GetNumWheelsInContact (   ) const { return m_num_wheels_in_contact; }
	bool							IsOnGround (   ) const;
	
	uint32							GetSoundSetupChecksum (   ) const;
	
private:
	
	void							get_input (   );
	void							zero_input (   );
	
	void							update_wheel_from_structure ( SWheel& wheel, Script::CStruct* p_wheel_struct );
	
	void							update_dynamic_state (   );
	void							update_wheel_dynamic_state (   );
	void							update_dependent_variables (   );
	void							update_velocities (   );
	void							update_collision_cache (   );
	
	void							damp_rotation (   );
	
	void							calculate_inverse_moment (   );
	float							calculate_friction_coefficient ( SWheel& wheel, float velocity ) const;
	void							calculate_friction_coefficients (   );
	Mth::Vector						calculate_body_point_velocity ( const Mth::Vector& pos ) const;
	float							calculate_body_point_effective_mass ( const Mth::Vector& pos, const Mth::Vector& direction ) const;
	
	int								determine_effective_gear ( float wheel_rotvel );
	void							update_wheel_heights (   );
	void							update_steering_angles (   );
	
	void							accumulate_forces (   );
	void							apply_gravitational_forces (   );
	void							apply_suspension_forces (   );
	void							apply_engine_forces (   );
	void							apply_drag_forces (   );
	void							apply_friction_forces (   );
	void							apply_brake_forces (   );
	void							apply_handbrake_forces ( float application_factor );
	void							apply_spare_brake_forces (   );
	
	void							accumulate_force ( const Mth::Vector& force, const Mth::Vector& location, uint32 color = MAKE_RGB(255, 255, 255) );
	void							accumulate_collision_force ( const Mth::Vector& force, const Mth::Vector& location );
	void							apply_wheel_torque ( SWheel& wheel, float torque );
	float							calculate_stopping_torque ( const SWheel& wheel, float rotvel ) const;
	
	void							update_flip (   );
	void							slerp_to_face_velocity (  );
	void							consider_sleeping (   );
	bool							in_artificial_collision (    );
	
	
									// environment collision members
	float							calculate_collision_depth ( const SCollisionPoint& collision_point, const SCollider& collider ) const;
	float							calculate_collision_depth ( const CLineFeeler& line_feeler ) const;
	bool							check_for_capping ( SCollisionPoint& collision_point, const CRectFeeler& rect_feeler, const SCollider& collider, int collision_line_idx, int collision_point_end_idx ) const;
	bool							consider_culling_point ( const SCollisionPoint& collision_point ) const;
	bool							very_close ( const Mth::Vector p, const Mth::Vector q ) const;
	bool							reset_this_frame (   ) const;
	void							apply_environment_collisions (   );
	void							update_pos_with_uber_frig ( const Mth::Vector& movement );
	void							apply_impulse ( const Mth::Vector& impulse, const Mth::Vector& location);
	
	void							trip_trigger ( ESkaterTriggerType trigger_type, CLineFeeler &feeler );
	void							trip_trigger ( ESkaterTriggerType trigger_type, CRectFeeler &feeler );
	
	void							control_skater (   );
	
	void							update_skeleton (   );
	
	void							draw_shadow (   );

	Mth::Vector						wheel_point ( const SWheel& wheel, int i, bool side ) const;
    void							draw_debug_rendering (   ) const;
	
private:
	
	// dynamic state variables
	
	// position
	Mth::Vector m_pos;
	
	// linear momentum
	Mth::Vector m_mom;
	
	// angular orientation
	Mth::Quat m_orientation;
	
	// rotational momentum
	Mth::Vector m_rotmom;
	
	// like a rigidbody, the vehicle can go to sleep
	enum EStateType
	{
		ASLEEP, AWAKE
	}
	m_state;
	
	// counting the number of consecutive sleep-worthy frames; after a certain number, we will go to sleep
	int m_consider_sleeping_count;
	
	// number of collision contacts during latest collision test
	int m_num_collision_points;
	
	// dependent state variables
	
	// angular orientation in 3x3 matrix form
	Mth::Matrix m_orientation_matrix;
	
	// linear velocity
	Mth::Vector m_vel;
	
	// rotational velocity
	Mth::Vector m_rotvel;
	
	// inverse moment of inertia in world frame
	Mth::Matrix m_inv_moment;
	
	// offset of center of mass used for suspension and friction, but not collisions
	Mth::Vector m_suspension_center_of_mass_world;
	
	// counts the number of wheels in contact with the ground
	unsigned int m_num_wheels_in_contact;
	
	// pointer to the next normal force history to use (the oldest one)
	char m_next_normal_force_history_idx;

	// if true, we were reset from a trigger script and should bail from the frame's update logic
	bool m_reset_this_frame;
	
	// if true, we are currently in the midst of flipping the car over
	bool m_in_flip;
	
	// if we're flipping the car, this is a time stamp of when the flipping began
	Tmr::Time m_flip_start_time_stamp;
	
	// during an artificial collision, this timer counts down to the end of the duration
	float m_artificial_collision_timer;
	
	// accumulators
	
	// accumulates the force on the body in a frame
	Mth::Vector m_force;
	
	// accumulates the torque on the body in a frame
	Mth::Vector m_torque;
	
	// length of current frame
	float m_time_step;
	
	// constant characteristics
	
	// vehicle's center of mass
	Mth::Vector m_suspension_center_of_mass;
	
	// inverse mass (1 / lb)
	float m_inv_mass;
	
	// inverse moment of inertia in vehicle's frame (1 / lb / in^2)
	Mth::Vector m_inv_moment_body;
	
	// coefficient of restitution of the body
	float m_body_restitution;
	
	// coefficient of friction of the body
	float m_body_friction;
	
	// spring constant of the collision penalty system
	float m_body_spring;
	
	// coefficient of friction of the body used when the vehicle is not upright
	float m_body_wipeout_friction;
	
	// factor of normal collision impulse which you can control via your steering
	float m_collision_control;
	
	// horizontal velocity cutoff below which no in-air slerping to face velocity occurs
	float m_in_air_slerp_vel_cutoff;
	
	// time over which in-air slerping lerps to full strength after takeoff
	float m_in_air_slerp_time_delay;
	
	// in-air slerping strength
	float m_in_air_slerp_strength;
	
	// special slerping at low speeds
	bool m_vert_correction;

	// number of wheels
	unsigned int m_num_wheels;
	
	// number of drive wheels; engine torque is divided between this number of wheels
	unsigned int m_num_drive_wheels;
	
	// maximum steering angle
	float m_max_steering_angle;
	
	// effective distance between fixed and steering axles (in); used when determining turning radius
	float m_cornering_wheelbase;
	
	// distance between center of steering wheels (in); used when determining steering angles
	float m_cornering_axle_length;
	
	// constant and quadratic rotational damp coefficients
	float m_const_rotvel_damping;
	float m_quad_rotvel_damping;
	
	// vehicles have rectangular feelers which they use to detect collisions with their environment
	SCollider mp_colliders[vVP_NUM_COLLIDERS];
	
	// offset from the vehicle's center of mass the model's origin
	// float m_body_model_offset;
	
	// body model's position
	Mth::Vector m_body_pos;
	
	// if true, triangle acts as a brake and throws an ExitCar exception when the car stops
	bool m_exitable;
	
	// if true, the car has no handbrake
	bool m_no_handbrake;
	
	// subelements
	
	// wheels
	SWheel* mp_wheels;
	
	// engine
	SEngine m_engine;
	
	// input state
	SControls m_controls;
	
	// collision cache
	Nx::CCollCache m_collision_cache;
	
	// parameters controlling skater while vehicle is active
	
	// true if the skater should be visible while in the vehicle
	bool m_skater_visible;
	
	// position offset of skater model
	Mth::Vector m_skater_pos;
	
	// skater animation to use while in the vehicle
	uint32 m_skater_anim;
	
	// controls the steering used for the displayed wheels and driving animation; lerps to m_controls.steering
	float m_steering_display;
	
	// name of the sound setup structure from PlayerVehicleSounds that which vehicle uses
	uint32 m_sound_setup_checksum;
	
	// dynamic script-controllable parameters
	
	// the effective gravity can be modified via script; graviy is multiplied by m_gravity_fraction over a duration of m_gravity_override_timer
	float m_gravity_override_fraction;
	float m_gravity_override_timer;
	
	// if true, the brakes are forced to be on
	bool m_force_brake;
	
	// state observers
	
	// time since we last had a wheel on the ground (plus no body contact)
	float m_air_time;
	float m_air_time_no_collision;
	
	// latest updates maximum normal collision impulse
	float m_max_normal_collision_impulse;
	
	// peer components
	
	CSkeletonComponent* mp_skeleton_component;
	CInputComponent* mp_input_component;
	CModelComponent* mp_model_component;
	
	// shared objects
	
	// shared collision point array
	static SCollisionPoint sp_collision_points[4 * (Nx::MAX_NUM_2D_COLLISIONS_REPORTED + 1)];
	
	// debug
	int m_draw_debug_lines;
	
	// if true, no collision detection is done and the skeleton is not updated; this is used to allow a car to settle on its suspension "off camera"
	bool m_update_suspension_only;
	
	// the driver
	CCompositeObject* mp_skater;
	CAnimationComponent* mp_skater_animation_component;
	CSkaterCorePhysicsComponent* mp_skater_core_physics_component;
	CTriggerComponent* mp_skater_trigger_component;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CVehicleComponent::IsOnGround (   ) const
{
	return m_num_wheels_in_contact > 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint32 CVehicleComponent::GetSoundSetupChecksum (   ) const
{
	return m_sound_setup_checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CVehicleComponent::update_velocities (   )
{
	m_vel = m_inv_mass * m_mom;

	// Zero the W component here to avoid possible NaN propogation issues.
	m_vel[W] = 0.0f;
	m_rotvel = m_inv_moment.Rotate(m_rotmom);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Mth::Vector CVehicleComponent::calculate_body_point_velocity ( const Mth::Vector& pos ) const
{
	return m_vel + Mth::CrossProduct(m_rotvel, pos);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CVehicleComponent::accumulate_force ( const Mth::Vector& force, const Mth::Vector& location, uint32 color )
{
	m_force += force;
	m_torque += Mth::CrossProduct(location - m_suspension_center_of_mass_world, force);
	
	#if 0
	if (m_draw_debug_lines)
	{
		Mth::Vector adjusted_location = location;
		adjusted_location[Y] += 1.0f; // pull out of the ground
		Gfx::AddDebugLine(m_pos + adjusted_location, m_pos + adjusted_location + 0.05f * force, color, color, 1);
	}
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CVehicleComponent::accumulate_collision_force ( const Mth::Vector& force, const Mth::Vector& location )
{
	m_force += force;
	m_torque += Mth::CrossProduct(location, force);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CVehicleComponent::apply_wheel_torque ( SWheel& wheel, float torque )
{
	float rotacc = wheel.inv_moment * torque;
	
	wheel.rotvel += rotacc * m_time_step;
	
	wheel.rotacc += rotacc;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float CVehicleComponent::calculate_stopping_torque ( const SWheel& wheel, float rotvel ) const
{
	return -rotvel / (m_time_step * wheel.inv_moment);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
			
inline float CVehicleComponent::calculate_collision_depth ( const CLineFeeler& line_feeler ) const
{
	return (1.0f - line_feeler.GetDist()) * Mth::DotProduct(line_feeler.GetNormal(), line_feeler.m_start - line_feeler.m_end);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CVehicleComponent::apply_impulse ( const Mth::Vector& impulse, const Mth::Vector& location )
{
	m_mom += impulse;
	m_rotmom += Mth::CrossProduct(location, impulse);
	
	update_velocities();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CVehicleComponent::very_close ( const Mth::Vector p, const Mth::Vector q ) const
{
	return Mth::Abs(p[X] - q[X]) < 0.1f && Mth::Abs(p[Y] - q[Y]) < 0.1f && Mth::Abs(p[Z] - q[Z]) < 0.1f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CVehicleComponent::reset_this_frame (   ) const
{
	return m_reset_this_frame || GetObject()->IsDead();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CVehicleComponent::trip_trigger ( ESkaterTriggerType trigger_type, CLineFeeler &feeler )
{                            
	mp_skater_trigger_component->CheckFeelerForTrigger(trigger_type, feeler);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CVehicleComponent::trip_trigger ( ESkaterTriggerType trigger_type, CRectFeeler &feeler )
{                            
	mp_skater_trigger_component->CheckFeelerForTrigger(trigger_type, feeler);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CVehicleComponent::in_artificial_collision (    )
{                            
	return m_artificial_collision_timer > 0.0f;
}

}

#endif


================================================
FILE: Code/Gel/Components/weaponcomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       WeaponComponent.cpp
//* OWNER:          Dave
//* CREATION DATE:  06/10/03
//****************************************************************************

// The CWeaponComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy Weaponcomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "Weapon" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage

#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 

#include 


namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent* CWeaponComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CWeaponComponent );	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CWeaponComponent::CWeaponComponent() : CBaseComponent()
{
	SetType( CRC_WEAPON );

	m_sight_pos.Set( 0.0f, 0.0f, 0.0f, 1.0f );
	m_sight_matrix.Identity();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CWeaponComponent::~CWeaponComponent()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CWeaponComponent::InitFromStructure( Script::CStruct* pParams )
{
	// ** Add code to parse the structure, and initialize the component

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*
	It's commented out here.  You will also need to comment in the 
	definition in Weaponcomponent.h
void CWeaponComponent::Finalize()
{
	// Virtual function, can be overridden to provided finialization to 
	// a component after all components have been added to an object
	// Usually this consists of getting pointers to other components
	// Note:  It is GUARENTEED (at time of writing) that
	// Finalize() will be called AFTER all components are added
	// and BEFORE the CCompositeObject::Update function is called
	
	// Example:
	// mp_suspend_component =  GetSuspendComponentFromObject( GetObject() );
										
										
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CWeaponComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	InitFromStructure(pParams);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CWeaponComponent::Update()
{
	// **  You would put in here the stuff that you would want to get run every frame
	// **  for example, a physics type component might update the position and orientation
	// **  and update the internal physics state 
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent::EMemberFunctionResult CWeaponComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
/*
        // @script | DoSomething | does some functionality
		case 0xbb4ad101:		// DoSomething
			DoSomething();
			break;

        // @script | ValueIsTrue | returns a boolean value
		case 0x769260f7:		// ValueIsTrue
		{
			return ValueIsTrue() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		break;
*/

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CWeaponComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CWeaponComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*	Example:
	p_info->AddInteger(CRCD(0x7cf2a233,"m_never_suspend"),m_never_suspend);
	p_info->AddFloat(CRCD(0x519ab8e0,"m_suspend_distance"),m_suspend_distance);
	*/
	
// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CWeaponComponent::ProcessStickyTarget( float heading_change, float tilt_change, float *p_extra_heading_change, float *p_extra_tilt_change )
{
	// Zero the extra chnage values.
	*p_extra_heading_change = 0.0f;
	*p_extra_tilt_change	= 0.0f;

	// If the player is trying to aim, this section of code tries to 'nudge' the aim in the right direction imperceptibly by
	// moving the cursor just a little more or less based on the requested movement of the cursor.
	if( mp_current_target )
	{
		if( heading_change != 0.0f )
		{
			// Figure default adjustment amount.
			float		heading_cursor_suck		= Script::GetFloat( CRCD( 0xd90b2dfb, "GunslingerLookaroundHeadingCursorSuck" ), Script::ASSERT );
			float		default_adjustment		= heading_change * heading_cursor_suck;

			Mth::Vector	immediate_camera_pos	= m_sight_pos;
			Mth::Vector ped_pos					= mp_current_target->GetPos() + Mth::Vector( 0.0f, 36.0f, 0.0f );
			Mth::Vector	ped_to_cam				= ped_pos - immediate_camera_pos;
			ped_to_cam[Y] = 0.0f;
			ped_to_cam.Normalize();

			// Figure exactly what the angular difference is.
			float		dp						= Mth::DotProduct( ped_to_cam, -m_sight_matrix[Z] );
			float		angular_difference		= acosf( dp );

			// If the angular difference is actually smaller than the default adjustment amount, make the default adjustment amount the angular difference.
			if( Mth::Abs( angular_difference ) < Mth::Abs( default_adjustment ))
			{
				default_adjustment = angular_difference;
			}

			// Now save off this matrix, and increase the rotation by some percentage of the rotation added to the lookaround heading.
			Mth::Matrix	saved_mat				= m_sight_matrix;
			m_sight_matrix.RotateYLocal( default_adjustment );

			// See if this brings us closer.
			float		dp2						= Mth::DotProduct( ped_to_cam, -m_sight_matrix[Z] );
			if( dp2 > dp )
			{
				// Yes it did, so store this change.
				*p_extra_heading_change			= default_adjustment;
			}
			else
			{
				// No it didn't so revert to the old matrix.
				m_sight_matrix = saved_mat;

				// Maybe this change went too far? Try undoing 10% of the last change.
				m_sight_matrix.RotateYLocal( -default_adjustment );

				// See if this brings us closer.
				float	dp3						= Mth::DotProduct( ped_to_cam, -m_sight_matrix[Z] );
				if( dp3 > dp )
				{
					// Yes it did, so store this change.
					*p_extra_heading_change		= -default_adjustment;
				}
				else
				{
					// No it didn't so revert to the old matrix.
					m_sight_matrix				= saved_mat;
				}
			}
		}
	}

	if( mp_current_target )
	{
		if( tilt_change != 0.0f )
		{
			// Figure default adjustment amount.
			float		tilt_cursor_suck		= Script::GetFloat( CRCD( 0x4ece2698, "GunslingerLookaroundTiltCursorSuck" ), Script::ASSERT );
			float		default_adjustment		= tilt_change * tilt_cursor_suck;

			Mth::Vector	immediate_camera_pos	= m_sight_pos;
			Mth::Vector ped_pos					= mp_current_target->GetPos() + Mth::Vector( 0.0f, 36.0f, 0.0f );
			Mth::Vector	ped_to_cam				= ped_pos - immediate_camera_pos;
			ped_to_cam.Normalize();

			// Figure exactly what the angular difference is.
			float		dp						= Mth::DotProduct( ped_to_cam, -m_sight_matrix[Z] );
			float		angular_difference		= acosf( dp );

			// If the angular difference is actually smaller than the default adjustment amount, make the default adjustment amount the angular difference.
			if( Mth::Abs( angular_difference ) < Mth::Abs( default_adjustment ))
			{
				default_adjustment = angular_difference;
			}

			// Now save off this matrix, and increase the rotation by some percentage of the rotation added to the lookaround heading.
			Mth::Matrix	saved_mat				= m_sight_matrix;
			m_sight_matrix.RotateXLocal( default_adjustment );

			// See if this brings us closer.
			float		dp2						= Mth::DotProduct( ped_to_cam, -m_sight_matrix[Z] );
			if( dp2 > dp )
			{
				// Yes it did, so store this change.
				*p_extra_tilt_change			= default_adjustment;
			}
			else
			{
				// No it didn't so revert to the old matrix.
				m_sight_matrix = saved_mat;

				// Maybe this change went too far? Try undoing 10% of the last change.
				m_sight_matrix.RotateXLocal( -default_adjustment );

				// See if this brings us closer.
				float	dp3						= Mth::DotProduct( ped_to_cam, -m_sight_matrix[Z] );

				if( dp3 > dp )
				{
					// Yes it did, so store this change.
					*p_extra_tilt_change		= -default_adjustment;
				}
				else
				{
					// No it didn't so revert to the old matrix.
					m_sight_matrix				= saved_mat;
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CWeaponComponent::DrawReticle( void )
{
	// Draw the reticle, red if enemy is within sights, white otherwise.
	if( m_spin_modulator < 1.0f )
	{
		Gfx::AddDebugStar( m_reticle_max, 24.0f, MAKE_RGB( 255, 0, 0 ), 1 );
	}
	else
	{
		Gfx::AddDebugStar( m_reticle_max, 24.0f, MAKE_RGB( 255, 255, 255 ), 1 );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CWeaponComponent::Fire( void )
{
	if( mp_current_target )
	{
		mp_current_target->SelfEvent( CRCD( 0xfaeec40f, "SkaterInAvoidRadius" ));
	}

	Script::RunScript( CRCD( 0xe03997d0, "PlayGunshot" ));
}



// These values should really be specified per-weapon.
const float SPIN_MODULATION_ANGLE = 0.9848f;	// Cosine of 10 degrees.


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CCompositeObject* CWeaponComponent::GetCurrentTarget( Mth::Vector& start_pos, Mth::Vector* p_reticle_max )
{
	// This is the maximum distance that we will check for targets.
	float target_distance = Script::GetFloat( CRCD( 0x89495011,"GunslingerTargetDistance" ), Script::ASSERT );

	Mth::Vector reticle_min		= start_pos;
	m_reticle_max				= reticle_min - ( m_sight_matrix[Z] * target_distance );

	// Ignore faces based on face flags.
	CFeeler feeler;
	feeler.SetIgnore((uint16)( mFD_NON_COLLIDABLE | mFD_NON_CAMERA_COLLIDABLE ), 0 );
	feeler.SetLine( reticle_min, m_reticle_max );
	bool collision = feeler.GetCollision( true );
	if( collision )
	{
		m_reticle_max = feeler.GetPoint();
		
		// Set the new target distance at the point of contact, to avoid considering targets beyond the collision point.
		target_distance	= target_distance * feeler.GetDist();
	}

	// Only targets within the allowed angle will be considered.
							mp_current_target	= NULL;
	float					best_dp				= SPIN_MODULATION_ANGLE;
	Mth::Vector				best_pos;

	// Rather than cycling through the ped logic components, eventually everything that is targettable should contain a targetting component.
	Obj::CPedLogicComponent *p_ped_logic_component = static_cast( Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRC_PEDLOGIC ));
	while( p_ped_logic_component )
	{
		Obj::CCompositeObject *p_ped = p_ped_logic_component->GetObject();

		// Have to hack in a height adjustment here - the default position appears to be at ground level.
		Mth::Vector ped_pos		= p_ped->GetPos() + Mth::Vector( 0.0f, 36.0f, 0.0f );

		// Want to get the position of this ped.
		Mth::Vector	ped_to_cam	= ped_pos - reticle_min;

		// Check not too far away.
		if( ped_to_cam.LengthSqr() < ( target_distance * target_distance ))
		{
			ped_to_cam.Normalize();

			// Calculate angle this ped subtends at the camera position.
			float		dp			= Mth::DotProduct( ped_to_cam, -m_sight_matrix[Z] );
			if( dp > best_dp )
			{
				best_dp				= dp;
				best_pos			= ped_pos;
				mp_current_target	= p_ped;
			}
		}
		p_ped_logic_component			= static_cast( p_ped_logic_component->GetNextSameType());
	}

	// Reset spin and tilt modulators.
	m_spin_modulator	= 1.0f;
	m_tilt_modulator	= 1.0f;

	if( mp_current_target )
	{
		// We have a potential candidate for targetting.
		// However we still want to check that it is close enough to our line of sight.
		float dist_to_ped	= Mth::Distance( best_pos, reticle_min );
		float dist_to_line	= dist_to_ped * sqrtf( 1.0f - ( best_dp * best_dp ));

		// This value should really be per-weapon in some script array.
		if( dist_to_line < Script::GetFloat( CRCD( 0x755235db, "GunslingerLookaroundModulationDistance" ), Script::ASSERT ))
		{
			// Is this the same target as last time?
//			if( mp_best_target == p_selected_target )
//			{
//				// If so, increase the targetting timer.
//				target_selection_timer += frame_length;
//			}
//			else
//			{
//				// Otherwise, reset the timer.
//				p_selected_target		= mp_best_target;
//				target_selection_timer	= frame_length;
//			}

			// Modulator gets smaller as result tends to 1.0.
			float max_damping = Script::GetFloat( CRCD( 0xef314a12, "GunslingerLookaroundDamping" ), Script::ASSERT );
			m_spin_modulator = 1.0f - ( max_damping * (( best_dp - SPIN_MODULATION_ANGLE ) / ( 1.0f - SPIN_MODULATION_ANGLE )));

//			if( gun_fired )
//			{
//				// Make this ped fall down.
//				mp_best_target->SelfEvent( CRCD( 0xfaeec40f, "SkaterInAvoidRadius" ));
//			}
		}
		else
		{
			mp_current_target = NULL;
		}
	}

	// Currently, set tilt modulator to be the same as spin modulator.
	m_tilt_modulator = m_spin_modulator;

	if( p_reticle_max )
	{
		*p_reticle_max = m_reticle_max;
	}

	return mp_current_target;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Gel/Components/weaponcomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       WeaponComponent.h
//* OWNER:          Dave
//* CREATION DATE:  06/10/03
//****************************************************************************

#ifndef __COMPONENTS_WEAPONCOMPONENT_H__
#define __COMPONENTS_WEAPONCOMPONENT_H__

#include 
#include 

#include 

#define		CRC_WEAPON CRCD( 0x96cc5819, "Weapon" )

#define		GetWeaponComponent()				((Obj::CWeaponComponent*)GetComponent(CRC_WEAPON))
#define		GetWeaponComponentFromObject(pObj)	((Obj::CWeaponComponent*)(pObj)->GetComponent(CRC_WEAPON))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CWeaponComponent : public CBaseComponent
{
public:
									CWeaponComponent();
									virtual ~CWeaponComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
//	virtual	void 					Finalize();
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	CCompositeObject*				GetCurrentTarget( Mth::Vector& start_pos, Mth::Vector* p_reticle_max );
	void							SetCurrentTarget( CCompositeObject* p_obj )	{ mp_current_target = p_obj; }
	void							SetSightPos( Mth::Vector& p )				{ m_sight_pos = p; }
	void							SetSightMatrix( Mth::Matrix& s )			{ m_sight_matrix = s; }
	void							DrawReticle( void );
	void							ProcessStickyTarget( float heading_change, float tilt_change, float *p_extra_heading_change, float *p_extra_tilt_change );
	void							Fire( void );

	float							GetSpinModulator( void )					{ return m_spin_modulator; }
	float							GetTiltModulator( void )					{ return m_tilt_modulator; }

	static CBaseComponent*			s_create();

private:

	Mth::Matrix						m_sight_matrix;
	Mth::Vector						m_sight_pos;
	Mth::Vector						m_reticle_max;			// Position of reticle max point from last collision check.
	CCompositeObject*				mp_current_target;

	float							m_spin_modulator;
	float							m_tilt_modulator;
};

}

#endif


================================================
FILE: Code/Gel/Environment/terrain.cpp
================================================
/*
	This is just a database for arrays of skater sounds on different terrains.
	
	For each type of sound in sounds.h (like grind, land, slide, wheel roll, etc...)
	there is an array of sounds to play, one for each possible terrain type.
	
	A terrain type of zero indicates that the sound is the default (for surfaces not
	defined in the list).
*/


#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 



namespace Env
{

#ifdef __NOPT_ASSERT__
bool					CTerrainManager::s_terrain_loaded[ vNUM_TERRAIN_TYPES ];
#endif __NOPT_ASSERT__

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ETerrainType			CTerrainManager::sGetTerrainFromChecksum(uint32 checksum)
{
	switch (checksum)
	{
	case 0x64967688: // TERRAIN_DEFAULT
		return vTERRAIN_DEFAULT;

	case 0x9b37fa90: // TERRAIN_CONCSMOOTH
		return vTERRAIN_CONCSMOOTH;   

	case 0x9383172: // TERRAIN_CONCROUGH
		return vTERRAIN_CONCROUGH;

	case 0xa9ecf4e9: // TERRAIN_METALSMOOTH
		return vTERRAIN_METALSMOOTH;

	case 0xa5aab5e: // TERRAIN_METALROUGH
		return vTERRAIN_METALROUGH;   

	case 0x67af2cb5: // TERRAIN_METALCORRUGATED
		return vTERRAIN_METALCORRUGATED;  

	case 0x1134b31a: // TERRAIN_METALGRATING
		return vTERRAIN_METALGRATING;

	case 0xd814ef41: // TERRAIN_METALTIN
		return vTERRAIN_METALTIN; 

	case 0x39075ea5: // TERRAIN_WOOD
		return vTERRAIN_WOOD;   

	case 0x3afca53f: // TERRAIN_WOODMASONITE
		return vTERRAIN_WOODMASONITE;

	case 0x6651a5d0: // TERRAIN_WOODPLYWOOD
		return vTERRAIN_WOODPLYWOOD;

	case 0xd11a620d: // TERRAIN_WOODFLIMSY
		return vTERRAIN_WOODFLIMSY;   

	case 0xd3e04021: // TERRAIN_WOODSHINGLE
		return vTERRAIN_WOODSHINGLE;

	case 0x866b2b83: // TERRAIN_WOODPIER
		return vTERRAIN_WOODPIER; 

	case 0x3d5a952c: // TERRAIN_BRICK
		return vTERRAIN_BRICK;

	case 0x7315eeac: // TERRAIN_TILE
		return vTERRAIN_TILE;   

	case 0x6aa69ead: // TERRAIN_ASPHALT
		return vTERRAIN_ASPHALT;  

	case 0x32d3fc0a: // TERRAIN_ROCK
		return vTERRAIN_ROCK;   

	case 0x67f6ee4c: // TERRAIN_GRAVEL
		return vTERRAIN_GRAVEL;   

	case 0x103bb3b8: // TERRAIN_SIDEWALK
		return vTERRAIN_SIDEWALK; 

	case 0xa207c1e3: // TERRAIN_GRASS
		return vTERRAIN_GRASS;   

	case 0x802c8f14: // TERRAIN_GRASSDRIED
		return vTERRAIN_GRASSDRIED;   

	case 0x9dfda61e: // TERRAIN_DIRT
		return vTERRAIN_DIRT;   

	case 0xd748f9b7: // TERRAIN_DIRTPACKED
		return vTERRAIN_DIRTPACKED;   

	case 0xf1394aca: // TERRAIN_WATER
		return vTERRAIN_WATER;   

	case 0x5e354fef: // TERRAIN_ICE
		return vTERRAIN_ICE;   

	case 0x3319e21b: // TERRAIN_SNOW
		return vTERRAIN_SNOW;   

	case 0xa5e0d5b9: // TERRAIN_SAND
		return vTERRAIN_SAND;   

	case 0x6362cc4f: // TERRAIN_PLEXIGLASS
		return vTERRAIN_PLEXIGLASS;   

	case 0x66987ee0: // TERRAIN_FIBERGLASS
		return vTERRAIN_FIBERGLASS;   

	case 0x8e6a5d9d: // TERRAIN_CARPET
		return vTERRAIN_CARPET;   

	case 0x2ee8ef42: // TERRAIN_CONVEYOR
		return vTERRAIN_CONVEYOR; 

	case 0xbe7c0e6e: // TERRAIN_CHAINLINK
		return vTERRAIN_CHAINLINK;

	case 0x14259681: // TERRAIN_METALFUTURE
		return vTERRAIN_METALFUTURE;

	case 0xbb1ac0d4: // TERRAIN_GENERIC1
		return vTERRAIN_GENERIC1; 

	case 0x2213916e: // TERRAIN_GENERIC2
		return vTERRAIN_GENERIC2;

	case 0x83b85f36: // TERRAIN_WHEELS
		return vTERRAIN_WHEELS;   

	case 0x22dd3d51: // TERRAIN_WETCONC
		return vTERRAIN_WETCONC;

	case 0xfa298f50: // TERRAIN_METALFENCE
		return vTERRAIN_METALFENCE;

	case 0x69803ba4: // TERRAIN_GRINDTRAIN
		return vTERRAIN_GRINDTRAIN;

	case 0xed035a39: // TERRAIN_GRINDROPE
		return vTERRAIN_GRINDROPE;

	case 0xec66b43b: // TERRAIN_GRINDWIRE
		return vTERRAIN_GRINDWIRE;

	case CRCC(0x3884f029,"TERRAIN_GRINDCONC"):
		return vTERRAIN_GRINDCONC;

	case CRCC(0x92a2112a,"TERRAIN_GRINDROUNDMETALPOLE"):
		return vTERRAIN_GRINDROUNDMETALPOLE;

	case CRCC(0xd7e88122,"TERRAIN_GRINDCHAINLINK"):
		return vTERRAIN_GRINDCHAINLINK;

	case CRCC(0xf5842bce,"TERRAIN_GRINDMETAL"):
		return vTERRAIN_GRINDMETAL;

	case CRCC(0xffc0b5e,"TERRAIN_GRINDWOODRAILING"):
		return vTERRAIN_GRINDWOODRAILING;

	case CRCC(0x8aadc3d0,"TERRAIN_GRINDWOODLOG"):
		return vTERRAIN_GRINDWOODLOG;

	case CRCC(0x60809403,"TERRAIN_GRINDWOOD"):
		return vTERRAIN_GRINDWOOD;

	case CRCC(0xf862cfe0,"TERRAIN_GRINDPLASTIC"):
		return vTERRAIN_GRINDPLASTIC;

	case CRCC(0x2bb33575,"TERRAIN_GRINDELECTRICWIRE"):
		return vTERRAIN_GRINDELECTRICWIRE;

	case CRCC(0x110f1bd3,"TERRAIN_GRINDCABLE"):
		return vTERRAIN_GRINDCABLE;

	case CRCC(0x84e4c7cd,"TERRAIN_GRINDCHAIN"):
		return vTERRAIN_GRINDCHAIN;

	case CRCC(0x3f1c35c5,"TERRAIN_GRINDPLASTICBARRIER"):
		return vTERRAIN_GRINDPLASTICBARRIER;

	case CRCC(0x6c4a1c6a,"TERRAIN_GRINDNEONLIGHT"):
		return vTERRAIN_GRINDNEONLIGHT;

	case CRCC(0xcaf52d56,"TERRAIN_GRINDGLASSMONSTER"):
		return vTERRAIN_GRINDGLASSMONSTER;

	case CRCC(0x8da017a8,"TERRAIN_GRINDBANYONTREE"):
		return vTERRAIN_GRINDBANYONTREE;

	case CRCC(0x7825b83c,"TERRAIN_GRINDBRASSRAIL"):
		return vTERRAIN_GRINDBRASSRAIL;

	case CRCC(0x5ce9c824,"TERRAIN_GRINDCATWALK"):
		return vTERRAIN_GRINDCATWALK;

	case CRCC(0x66ab4005,"TERRAIN_GRINDTANKTURRET"):
		return vTERRAIN_GRINDTANKTURRET;

	case 0x24dca61: // TERRAIN_MEATALSMOOTH		// old typo that still exists
		Dbg_Message("Metal is not spelled 'Meatal'");
	default:
		Dbg_MsgAssert(0, ("Unknown terrain checksum %x\n", checksum));
		return vTERRAIN_DEFAULT;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ETerrainActionType		CTerrainManager::sGetActionFromChecksum(uint32 checksum)
{
	switch (checksum)
	{
	case 0xcaebd8e: // TA_ROLL
		return vTABLE_WHEELROLL;

	case 0x7e5d9202: // TA_GRIND
		return vTABLE_GRIND;

	case 0xc0669dd5: // TA_OLLIE
		return vTABLE_JUMP;

	case 0x8a1b5a98: // TA_LAND
		return vTABLE_LAND;

	case 0xf0e51d30: // TA_BONK
		return vTABLE_BONK;

	case 0x6ac1023d: // TA_GRINDJUMP
		return vTABLE_GRINDJUMP;

	case 0x6572d1f3: // TA_GRINDLAND
		return vTABLE_GRINDLAND;

	case 0xd6135bf0: // TA_SLIDE
		return vTABLE_SLIDE;

	case 0x6e27388f: // TA_SLIDEJUMP
		return vTABLE_SLIDEJUMP;

	case 0x6194eb41: // TA_SLIDELAND
		return vTABLE_SLIDELAND;

	case 0x20744cde: // TA_REVERT
		return vTABLE_CESS;

	default:
		Dbg_MsgAssert(0, ("Unknown terrain action checksum %x\n", checksum));
		return vTABLE_WHEELROLL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32					CTerrainManager::s_get_action_checksum(ETerrainActionType action)
{
	switch (action)
	{
	case vTABLE_WHEELROLL:
		return 0xcaebd8e; // TA_ROLL

	case vTABLE_GRIND:
		return 0x7e5d9202; // TA_GRIND

	case vTABLE_JUMP:
		return 0xc0669dd5; // TA_OLLIE

	case vTABLE_LAND:
		return 0x8a1b5a98; // TA_LAND

	case vTABLE_BONK:
		return 0xf0e51d30; // TA_BONK

	case vTABLE_GRINDJUMP:
		return 0x6ac1023d; // TA_GRINDJUMP

	case vTABLE_GRINDLAND:
		return 0x6572d1f3; // TA_GRINDLAND

	case vTABLE_SLIDE:
		return 0xd6135bf0; // TA_SLIDE

	case vTABLE_SLIDEJUMP:
		return 0x6e27388f; // TA_SLIDEJUMP

	case vTABLE_SLIDELAND:
		return 0x6194eb41; // TA_SLIDELAND

	case vTABLE_CESS:
		return 0x20744cde; // TA_REVERT

	default:
		Dbg_MsgAssert(0, ("Unknown terrain action %d\n", action));
		return 0xcaebd8e; // TA_ROLL
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool					CTerrainManager::s_get_terrain_actions_struct(ETerrainType terrain, Script::CStruct *p_actions)
{
	Dbg_MsgAssert(p_actions, ("s_get_terrain_actions_struct given NULL CStruct"));

	Script::CStruct *p_default_actions = NULL;
	Script::CStruct *p_terrain_actions = NULL;
	Script::CStruct *p_level_default_actions = NULL;
	Script::CStruct *p_level_terrain_actions = NULL;

	uint32 terrain_checksum;
	ETerrainType terrain_type;

	// Search Default Terrain array first
	Script::CArray *pDefaultTerrainArray = Script::GetArray(CRCD(0x1c2c4218,"standard_terrain"));
	Dbg_MsgAssert(pDefaultTerrainArray, ("standard_terrain not found"));

	for (uint i = 0; (!p_default_actions || !p_terrain_actions) && i < pDefaultTerrainArray->GetSize(); i++)
	{
		// Get next terrain struct
		Script::CStruct *pTerrainStruct = pDefaultTerrainArray->GetStructure(i);
		Dbg_MsgAssert(pTerrainStruct, ("terrain array not made of structures"));

		pTerrainStruct->GetChecksum(CRCD(0x3789ac4e,"Terrain"), &terrain_checksum, Script::ASSERT);
		terrain_type = sGetTerrainFromChecksum(terrain_checksum);

		if (terrain_type == vTERRAIN_DEFAULT)
		{
			pTerrainStruct->GetStructure(CRCD(0x5c969c50,"SoundActions"), &p_default_actions);
			//Dbg_Message("Found default terrain:");
			//Script::PrintContents(p_default_actions);
		}
		else if (terrain_type == terrain)
		{
			pTerrainStruct->GetStructure(CRCD(0x5c969c50,"SoundActions"), &p_terrain_actions);
			//Dbg_Message("Found terrain:");
			//Script::PrintContents(p_terrain_actions);
		}

		if (p_default_actions && p_terrain_actions)
		{
			break;
		}
	}

	// Search Level Terrain array
	Script::CArray *pLevelTerrainArray = Script::GetArray(CRCD(0x96218951,"level_terrain"));
	if (pLevelTerrainArray)
	{
		for (uint i = 0; i < pLevelTerrainArray->GetSize(); i++)
		{
			// Get next terrain struct
			Script::CStruct *pTerrainStruct = pLevelTerrainArray->GetStructure(i);
			Dbg_MsgAssert(pTerrainStruct, ("terrain array not made of structures"));

			pTerrainStruct->GetChecksum(CRCD(0x3789ac4e,"Terrain"), &terrain_checksum, Script::ASSERT);
			terrain_type = sGetTerrainFromChecksum(terrain_checksum);

			if (terrain_type == vTERRAIN_DEFAULT)
			{
				pTerrainStruct->GetStructure(CRCD(0x5c969c50,"SoundActions"), &p_level_default_actions);
			} else if (terrain_type == terrain)
			{
				pTerrainStruct->GetStructure(CRCD(0x5c969c50,"SoundActions"), &p_level_terrain_actions);
			}

			if (p_level_default_actions && p_level_terrain_actions)
			{
				break;
			}
		}
	}

	// Combine them together
	*p_actions = *p_default_actions;

	if (p_level_default_actions)
	{
		*p_actions += *p_level_default_actions;
	}

	if (p_terrain_actions)
	{
		*p_actions += *p_terrain_actions;
	}

	if (p_level_terrain_actions)
	{
		*p_actions += *p_level_terrain_actions;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct *		CTerrainManager::s_choose_sound_action(Script::CStruct *p_sound_action_struct)
{
	Script::CStruct* p_sound;
	
	// soundAction is a single stucture, so just use it
	if (p_sound_action_struct->GetStructure(CRCD(0x25f654c8, "soundAction"), &p_sound))
	{
		Dbg_MsgAssert(!p_sound->ContainsComponentNamed(CRCD(0xdbc4c4db, "chance")),
			("A sound may only include a chance parameter when it is an element of a random-choice list of sounds"));
		return p_sound;
	}
	
	// soundAction is an array of structures, so choose one randomly based on their given chances
	Script::CArray* p_random_indexed_sound_array;
	p_sound_action_struct->GetArray(CRCD(0x25f654c8, "soundAction"), &p_random_indexed_sound_array, Script::ASSERT);
	
	int total_chance = 0;
	for (int sound_idx = p_random_indexed_sound_array->GetSize(); sound_idx--; )
	{
		p_sound = p_random_indexed_sound_array->GetStructure(sound_idx);
		
		int chance;
		p_sound->GetInteger(CRCD(0xdbc4c4db, "chance"), &chance, Script::ASSERT);
		
		total_chance += chance;
	}
	
	int choice = Mth::Rnd(total_chance);
	
	for (int sound_idx = p_random_indexed_sound_array->GetSize(); sound_idx--; )
	{
		p_sound = p_random_indexed_sound_array->GetStructure(sound_idx);
		
		int chance;
		p_sound->GetInteger(CRCD(0xdbc4c4db, "chance"), &chance, Script::ASSERT);
		
		choice -= chance;
		if (choice < 0)
		{
			return p_sound;
		}
	}
	
	Dbg_Assert(false);
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct *		CTerrainManager::s_get_action_sound_struct(Script::CStruct *p_actions, ETerrainActionType action, float soundChoice)
{
	Dbg_MsgAssert(p_actions, ("s_get_action_sound_struct given NULL action CStruct"));

	Script::CStruct *p_sound;
	
	uint32 checksum = s_get_action_checksum(action);

	// sound is a single sound structure, so just use it
	if (p_actions->GetStructure(checksum, &p_sound))
	{
		return p_sound;
	}
	
	// sound is an array of structures, one of which must be choosen depending upon speed
	Script::CArray* p_float_indexed_sound_array;
	p_actions->GetArray(checksum, &p_float_indexed_sound_array, Script::ASSERT);
	
	// check to insure that the final structure in the array has no useUpTo parameter and thus operates as a default choice
	Dbg_MsgAssert(!p_float_indexed_sound_array->GetStructure(p_float_indexed_sound_array->GetSize() - 1)->ContainsComponentNamed(CRCD(0x9ee9a09a, "useUpTo")),
		("Final sound in a speed-indexed sound list must not include useUpTo"));
	
	for (uint32 sound_action_idx = 0; sound_action_idx < p_float_indexed_sound_array->GetSize(); sound_action_idx++)
	{
		// each structure contains a useUpTo parameter which is checked against speed in turn; if found to be larger
		// than speed, the sound is used; a structure lacking useUpTo is always used
		Script::CStruct* p_sound_action_struct = p_float_indexed_sound_array->GetStructure(sound_action_idx);
		
		float up_to_speed;
		if (!p_sound_action_struct->GetFloat(CRCD(0x9ee9a09a, "useUpTo"), &up_to_speed))
		{
			Dbg_MsgAssert(sound_action_idx == p_float_indexed_sound_array->GetSize() - 1,
				("Only the final sound in a float-indexed sound list may fail to include useUpTo"));
			return s_choose_sound_action(p_sound_action_struct);
		}
		
		if (soundChoice <= up_to_speed)
		{
			return s_choose_sound_action(p_sound_action_struct);
		}
	}
	
	Dbg_Assert(false);
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool					CTerrainManager::s_get_sound_info(Script::CStruct *p_sound, STerrainSoundInfo *p_info)
{
	Dbg_MsgAssert(p_info, ("s_get_sound_info given NULL STerrainSoundInfo"));

	// Set defaults
	p_info->mp_soundName = NULL;
	p_info->m_soundChecksum = 0;
	p_info->m_maxPitch = 100.0f;
	p_info->m_minPitch = 100.0f;
	p_info->m_maxVol = 100.0f;
	p_info->m_minVol = 0.0f;
	p_info->m_loadPitch = 100.0f;
	p_info->m_loadVol = 100.0f;

	p_sound->GetString( CRCD(0x7713c7b,"sound"), &p_info->mp_soundName );
	if ( !p_info->mp_soundName )
	{
		Dbg_MsgAssert(0, ( "Need the name of a sound in s_get_sound_info" ));
		return false;
	}

	// Calculate checksum
	const char	*pNameMinusPath	= p_info->mp_soundName;
	int			stringLength	= strlen( p_info->mp_soundName );
	for( int i = 0; i < stringLength; i++ )
	{
		if(( p_info->mp_soundName[i] == '\\' ) || ( p_info->mp_soundName[i] == '/' ))
			pNameMinusPath = &p_info->mp_soundName[i + 1];
	}
	
	p_info->m_soundChecksum = Script::GenerateCRC( pNameMinusPath );

	p_sound->GetFloat( CRCD(0xfa3e14c5,"maxPitch"), &p_info->m_maxPitch );
	p_sound->GetFloat( CRCD(0x1c5ebb24,"minPitch"), &p_info->m_minPitch );
	
	p_sound->GetFloat( CRCD(0x693daaf,"maxVol"), &p_info->m_maxVol );
	p_sound->GetFloat( CRCD(0x4391992d,"minVol"), &p_info->m_minVol );

	p_sound->GetFloat( CRCD(0xf573682f,"loadPitch"), &p_info->m_loadPitch );
	p_sound->GetFloat( CRCD(0x312ccbcf,"loadVol"), &p_info->m_loadVol );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CTerrainManager::s_load_action_sound( Script::CStruct* p_sound )
{
	Dbg_MsgAssert(p_sound, ("s_load_action_sound given NULL sound CStruct"));
	
	STerrainSoundInfo soundInfo;
	#ifdef	__NOPT_ASSERT__
	bool result = 
	#endif
	s_get_sound_info(p_sound, &soundInfo);
	Dbg_Assert(result);

	Sfx::CSfxManager::Instance()->LoadSound( soundInfo.mp_soundName, 0, 0.0f, soundInfo.m_loadPitch, soundInfo.m_loadVol );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CTerrainManager::s_load_action_sound_struct( Script::CStruct* p_actions, ETerrainActionType action )
{
	Dbg_MsgAssert(p_actions, ("s_load_action_sound_struct given NULL action CStruct"));

	Script::CStruct *p_sound;

	uint32 checksum = s_get_action_checksum(action);

	// sound is a single sound structure, so just load it
	if (p_actions->GetStructure(checksum, &p_sound))
	{
		s_load_action_sound(p_sound);
		return;
	}
	
	// sound is an array of sound structures, all of which must be loaded
	Script::CArray* p_float_indexed_sound_array;
	p_actions->GetArray(checksum, &p_float_indexed_sound_array, Script::ASSERT);
	
	for (uint32 i = 0; i < p_float_indexed_sound_array->GetSize(); i++)
	{
		Script::CStruct* p_sound_action_struct = p_float_indexed_sound_array->GetStructure(i);
		
		// this entry is a single sound structure, so just load it
		if (p_sound_action_struct->GetStructure(CRCD(0x25f654c8, "soundAction"), &p_sound))
		{
			s_load_action_sound(p_sound);
			continue;
		}
		
		// this entry is an array of sounds, all of which must be loaded
		Script::CArray* p_random_indexed_sound_array;
		p_sound_action_struct->GetArray(CRCD(0x25f654c8, "soundAction"), &p_random_indexed_sound_array, Script::ASSERT);
		
		for (uint32 j = 0; j < p_random_indexed_sound_array->GetSize(); j++)
		{
			p_sound = p_random_indexed_sound_array->GetStructure(j);
			s_load_action_sound(p_sound);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef __NOPT_ASSERT__
void					CTerrainManager::s_check_terrain_loaded(ETerrainType terrain)
{
	// Garrett: Comment the assert back in when the new plugin is released (on or after 2/20/03)
	if (!s_terrain_loaded[terrain])
	{
//		Dbg_Message("****** ERROR: Trying to use terrain %d, which isn't loaded ******", terrain);
	}
//	Dbg_MsgAssert(s_terrain_loaded[terrain], ("****** ERROR: Trying to use terrain %d, which isn't loaded ******", terrain));
}
#endif __NOPT_ASSERT__

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CTerrainManager::sReset()
{
#ifdef __NOPT_ASSERT__
	// Clear terrain loaded array
	for (int i = 0; i < vNUM_TERRAIN_TYPES; i++)
	{
		s_terrain_loaded[i] = false;
	}
#endif __NOPT_ASSERT__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool					CTerrainManager::sGetTerrainSoundInfo( STerrainSoundInfo *p_info, ETerrainType terrain,
															   ETerrainActionType action, float soundChoice )
{
	bool result = false;

	if (Sfx::NoSoundPlease()) return false;

	Dbg_MsgAssert((terrain >= 0) && (terrain < vNUM_TERRAIN_TYPES), ("Terrain type out of range"));

#ifdef __NOPT_ASSERT__
	// Check that terrain is loaded
	s_check_terrain_loaded(terrain);
#endif __NOPT_ASSERT__

	Dbg_MsgAssert(p_info, ("sGetTerrainSoundInfo given NULL STerrainSoundInfo"));

	// Allocate new script structure for combining data
	Script::CStruct *p_actions = new Script::CStruct;
	Script::CStruct *p_sound;

	if (s_get_terrain_actions_struct(terrain, p_actions))
	{
		p_sound = s_get_action_sound_struct(p_actions, action, soundChoice);
		if (p_sound)
		{
			//Dbg_Message("Extracted sound:");
			//Script::PrintContents(p_sound);

			result = s_get_sound_info(p_sound, p_info);
		}
	}

	// Done with script data
	delete p_actions;

	return result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool					CTerrainManager::sLoadTerrainSounds( ETerrainType terrain )
{
	if (Sfx::NoSoundPlease()) return false;

	Dbg_MsgAssert((terrain >= 0) && (terrain < vNUM_TERRAIN_TYPES), ("Terrain type out of range"));

	Script::CStruct *p_actions = new Script::CStruct;

//	Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
//	Dbg_Assert(sfx_manager);

	if (s_get_terrain_actions_struct(terrain, p_actions))
	{
		// Go through each action
		for (int action_idx = 0; action_idx < vNUM_ACTION_TYPES; action_idx++)
		{
			s_load_action_sound_struct(p_actions, (ETerrainActionType) action_idx);
		}
	}

	// Done with script data
	delete p_actions;

#ifdef __NOPT_ASSERT__
	// Mark terrain loaded
	s_terrain_loaded[terrain] = true;
#endif __NOPT_ASSERT__

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CTerrainManager::sPlaySound( ETerrainActionType action, ETerrainType terrain, const Mth::Vector &pos,
													 float volPercent, float pitchPercent, float soundChoice, bool propogate )
{
	if (Sfx::NoSoundPlease()) return;
	
	Dbg_MsgAssert((terrain >= 0) && (terrain < vNUM_TERRAIN_TYPES), ("Terrain type out of range"));

#ifdef __NOPT_ASSERT__
	// Check that terrain is loaded
	s_check_terrain_loaded(terrain);
#endif __NOPT_ASSERT__

	// need to write pitch as well
	// Replay::WriteSkaterSoundEffect(action,terrain,pos,volPercent);
	
	Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();
	GameNet::Manager * gamenet_manager = GameNet::Manager::Instance();
    
	STerrainSoundInfo soundInfo;
	bool found = sGetTerrainSoundInfo( &soundInfo, terrain, action, soundChoice );

	if ( !found )
	{
		// no sounds are supposed to be played for this surface on this transition:
		return;
	}
	
	if( propogate )
	{
		Net::Client* client;
		GameNet::MsgPlaySound msg;
		Net::MsgDesc msg_desc;

		client = gamenet_manager->GetClient( 0 );
		Dbg_Assert( client );

		msg.m_WhichArray = (char) action;
		msg.m_SurfaceFlag = (char) terrain;
		msg.m_Pos[0] = (short) pos[X];
		msg.m_Pos[1] = (short) pos[Y];
		msg.m_Pos[2] = (short) pos[Z];
		msg.m_VolPercent = (char) volPercent;
		msg.m_PitchPercent = (char) pitchPercent;
		msg.m_SoundChoice = (char) soundChoice;

		msg_desc.m_Data = &msg;
		msg_desc.m_Length = sizeof( GameNet::MsgPlaySound );
		msg_desc.m_Id = GameNet::MSG_ID_PLAY_SOUND;
		client->EnqueueMessageToServer( &msg_desc );
	}

	Sfx::sVolume vol;
	sfx_manager->SetVolumeFromPos( &vol, pos, sfx_manager->GetDropoffDist( soundInfo.m_soundChecksum ));

	// Adjust volume according to speed.
	volPercent = sGetVolPercent( &soundInfo, volPercent );
	vol.PercentageAdjustment( volPercent );
	
	if (vol.IsSilent()) return;
	
	pitchPercent = sGetPitchPercent( &soundInfo, pitchPercent );
	
	sfx_manager->PlaySound( soundInfo.m_soundChecksum, &vol, pitchPercent, 0, NULL, soundInfo.mp_soundName );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// set the volume according to the range specified by the designers...
float					CTerrainManager::sGetVolPercent( STerrainSoundInfo *pInfo, float volPercent, bool clipToMaxVol )
{
	
	Dbg_MsgAssert(pInfo, ("pInfo set to NULL."));

	if ( !( ( pInfo->m_minVol == 0.0f ) && ( pInfo->m_maxVol == 100.0f ) ) )
	{
		volPercent = ( pInfo->m_minVol + PERCENT( ( pInfo->m_maxVol - pInfo->m_minVol ), volPercent ) );
	}
	
	if ( clipToMaxVol )
	{
		if ( volPercent > pInfo->m_maxVol )
			volPercent = pInfo->m_maxVol;
	}
	return ( volPercent );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float					CTerrainManager::sGetPitchPercent( STerrainSoundInfo *pInfo, float pitchPercent, bool clipToMaxPitch )
{
	
	Dbg_MsgAssert(pInfo, ("pInfo set to NULL."));

	if ( !( ( pInfo->m_minPitch == 0.0f ) && ( pInfo->m_maxPitch == 100.0f ) ) )
	{
		pitchPercent = ( pInfo->m_minPitch + PERCENT( ( pInfo->m_maxPitch - pInfo->m_minPitch ), pitchPercent ) );
	}
	
	if ( clipToMaxPitch )
	{
		if ( pitchPercent > pInfo->m_maxPitch )
			pitchPercent = pInfo->m_maxPitch;
	}
	return ( pitchPercent );
}

#if 0
// Sound FX for the level...
/*	The surface flag indicates which surface the skater is currently on (grass, cement, wood, metal)
	whichSound is the checksum from the name of the looping sound (should be loaded using LoadSound
		in the script file for each level)
	whichArray indicates whether this sound belongs in the list of wheels rolling sounds, or
		grinding sounds, etc...
*/
void CSk3SfxManager::SetSkaterSoundInfo( int surfaceFlag, uint32 whichSound, int whichArray,
	float maxPitch, float minPitch, float maxVol, float minVol )
{
	
	if (Sfx::NoSoundPlease()) return;
	
	// must initialize PInfo!
	int i;
	Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();

	if ( NULL == sfx_manager->GetWaveTableIndex( whichSound ) )
	{
		Dbg_MsgAssert( 0,( "Terrain sound not loaded! surface %d sound %s checksum %d soundType %d",
			surfaceFlag, Script::FindChecksumName( whichSound ), whichSound, whichArray ));
		return;
	}
	int numEntries = mNumEntries[ whichArray ];
	SkaterSoundInfo	*pArray = mSoundArray[ whichArray ];
	SkaterSoundInfo *pInfo = NULL;
	
	for ( i = 0; i < numEntries; i++ )
	{
		if ( pArray[ i ].surfaceFlag == surfaceFlag )
		{
			Dbg_Message( "Re-defining soundtype %d for surfaceFlag %d", whichArray, surfaceFlag );
			pInfo = &pArray[ i ];
			break;
		}
	}
	if ( !pInfo )
	{
		pInfo = &pArray[ mNumEntries[ whichArray ] ];
		mNumEntries[ whichArray ] += 1;
		Dbg_MsgAssert( mNumEntries[ whichArray ] < vMAX_NUM_ENTRIES,( "Array too small type %d.  Increase MAX_NUM_ENTRIES.", whichArray ));
	}
	
	Dbg_MsgAssert( pInfo,( "Please fire Matt immediately after kicking him in the nuts." ));
	
	// surfaceFlag of zero will be used for the default
	pInfo->surfaceFlag = surfaceFlag;
	// if soundChecksum is zero, no sound will play on this surface.
	pInfo->soundChecksum = whichSound;
	pInfo->maxPitch = maxPitch;
	pInfo->minPitch = minPitch;
	pInfo->maxVol = maxVol;
	pInfo->minVol = minVol;

} // end of SetSkaterSoundInfo( )

#endif

} // namespace Env


================================================
FILE: Code/Gel/Environment/terrain.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Gel Module			 									**
**																			**
**	File name:		gel/environment/terrain.h								**
**																			**
**	Created by:		Garrett Feb. 2003										**
**																			**
**	Description:	Terrains and its properties (soundFX, etc.)				**
**																			**
*****************************************************************************/

#ifndef __GEL_TERRAIN_H
#define __GEL_TERRAIN_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

namespace Script
{
	class CScriptStructure;
	class CScript;
	class CStruct;
}

namespace Env
{

// these sound trigger values are also used to
// communicate with the sounds.cpp module and scripted
// soundfx for jumping/landing...
enum ETerrainActionType
{
	vTABLE_WHEELROLL = 0,
	vTABLE_GRIND, // on a rail...
	vTABLE_JUMP,
	vTABLE_LAND,
	vTABLE_BONK,
	vTABLE_GRINDJUMP,
	vTABLE_GRINDLAND,
	vTABLE_SLIDE, // on a rail
	vTABLE_SLIDEJUMP,
	vTABLE_SLIDELAND,
	vTABLE_CESS,
	vNUM_ACTION_TYPES,
};
	
struct STerrainSoundInfo
{
	const char *mp_soundName;
	uint32		m_soundChecksum;		// Checksum of sound filename
	float		m_maxPitch;
	float		m_minPitch;
	float		m_maxVol;
	float		m_minVol;
	float		m_loadPitch;			// Values to use when the sound is loaded
	float		m_loadVol;
};

struct STerrainInfo
{
	ETerrainType		m_type;
	bool				m_loaded;		// tells if being used by current level
	STerrainSoundInfo	m_sounds[vNUM_ACTION_TYPES];

	float				m_friction;
	// etc, etc, etc
};

/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

class  CTerrainManager
{
	
public:
	static void					sReset();

	static bool					sGetTerrainSoundInfo( STerrainSoundInfo *p_info, ETerrainType terrain,
													  ETerrainActionType action, float soundChoice = 0.0f );

	static bool					sLoadTerrainSounds( ETerrainType terrain );
	static float				sGetVolPercent( STerrainSoundInfo *pInfo, float volPercent, bool clipToMaxVol = false );
	static float				sGetPitchPercent( STerrainSoundInfo *pInfo, float pitchPercent, bool clipToMaxVol = false );

	static void 				sPlaySound( ETerrainActionType action, ETerrainType terrain, const Mth::Vector &pos,
											float volPercent, float pitchPercent, float soundChoice, bool propogate = true );

	static ETerrainType			sGetTerrainFromChecksum(uint32 checksum);
	static ETerrainActionType	sGetActionFromChecksum(uint32 checksum);


private:

	static uint32				s_get_action_checksum(ETerrainActionType action);

	static bool					s_get_terrain_actions_struct(ETerrainType terrain, Script::CStruct *p_actions);
	static Script::CStruct *	s_get_action_sound_struct(Script::CStruct *p_actions, ETerrainActionType action, float soundChoice);
	static bool					s_get_sound_info(Script::CStruct *p_sound, STerrainSoundInfo *p_info);
	static Script::CStruct *	s_choose_sound_action(Script::CStruct *p_sound_action_struct);
	
	static void					s_load_action_sound( Script::CStruct* p_sound );
	static void					s_load_action_sound_struct( Script::CStruct* p_actions, ETerrainActionType action );
	
	
	//STerrainInfo 				sDefaultTerrainArray[ vNUM_TERRAIN_TYPES ];	// This one is inited at the beginning and never changes
	//STerrainInfo 				sCurrentTerrainArray[ vNUM_TERRAIN_TYPES ];	// This one is derived from the default and the level array

#ifdef __NOPT_ASSERT__
								// We check this array to make sure non-loaded terrains
	static void					s_check_terrain_loaded(ETerrainType terrain);

	static bool					s_terrain_loaded[ vNUM_TERRAIN_TYPES ];
#endif __NOPT_ASSERT__
};

}  // namespace Env

#endif


================================================
FILE: Code/Gel/Event.h
================================================
#ifndef __GEL_EVENT_H
#define __GEL_EVENT_H

#ifndef __GEL_OBJECT_H
#include 
#endif

namespace Script
{
	class CScript;
	class CStruct;
	class CArray;
}


namespace Obj
{

class CObject;
class CTracker;


class CEvent
{
	friend class CTracker;

public:

	enum EEventLevel
	{
		// applies to target or source
		vSYSTEM_EVENT =	0x36b2ee74		// "system"
	};

	// should also add new event types to EventLog.q
	enum EEventType
	{
		// applies to type
		TYPE_FOCUS					= 0x9d3fb516,
		TYPE_UNFOCUS				= 0x4adf0cd3,
		TYPE_NOTIFY_CHILD_LOCK		= 0x3056434f,
		TYPE_NOTIFY_CHILD_UNLOCK	= 0x4a57316b,
		TYPE_PAD_UP					= 0x30a4f836,
		TYPE_PAD_DOWN  				= 0x0fd1ac26,
		TYPE_PAD_LEFT  				= 0x6949db75,
		TYPE_PAD_RIGHT 				= 0x05ddd87c,
		TYPE_PAD_CHOOSE				= 0x1e3e253b,
		TYPE_PAD_BACK				= 0x7ee0fd2a,
		TYPE_PAD_SQUARE	   			= 0x5c3979e3,
		TYPE_PAD_CIRCLE	   			= 0x456d7433,
		TYPE_PAD_L1					= 0xaa7f2028,
		TYPE_PAD_L2					= 0x33767192,
		TYPE_PAD_L3					= 0x44714104,
		TYPE_PAD_R1					= 0x7e3e1ff7,
		TYPE_PAD_R2					= 0xe7374e4d,
		TYPE_PAD_R3					= 0x90307edb,
		TYPE_PAD_START 				= 0x2e6ef8e7,
		TYPE_PAD_SELECT				= 0xda28fb8a,
		
		// these have been deprecated
		// Ryan: now they are reprecated again!
		TYPE_PAD_X					= 0x6fb6a322,
		TYPE_PAD_TRIANGLE			= 0xfe6869df,
	};

	enum EFlags
	{
		mREAD						= (1<<0),
		mHANDLED					= (1<<1),
		mCONTROLS_OWN_DATA			= (1<<2),
	};

	uint32						GetType() {return m_type;}
	uint32						GetSource() {return m_source_id;}
	uint32						GetTarget() {return m_target_id;}
	Script::CStruct *			GetData() {return mp_data;}

	void						MarkRead(uint32 receiverId = 0, uint32 script = 0);
	void						MarkHandled(uint32 receiverId = 0, uint32 script = 0);
	bool						WasHandled() {return (m_flags & mHANDLED);}

	/*
		Helper functions 
	*/

	// for focus, pad events
	static int					sExtractControllerIndex(CEvent *pEvent);

protected:

								CEvent();
								~CEvent();
	
	uint32						m_type;
	uint32						m_source_id;
	uint32						m_target_id;
	uint32						m_flags;

	Script::CStruct *			mp_data;
};




/*
	Notes:
	-the event_filter() function automatically locks the associated CObject, so that no 
	event handling code or script will delete it during event_filter() or pass_event_to_listener(). 
	The danger is the CObject will delete the CEventListener.
*/
class CEventListener
{
	friend class CTracker;
	
public:

								CEventListener();
	virtual						~CEventListener();
	void						RegisterWithTracker(CObject *pObject);

protected:

	void						event_filter(CEvent *pEvent);
	
	// implement this function in subclasses for class-specific event handling
	virtual void				pass_event_to_listener(CEvent *pEvent) = 0;

	
	CObjectPtr					mp_object;
	CEventListener *			mp_next_listener;
	bool						m_registered;
	int							m_ref_count;
};




class CEventHandlerTable
{
	friend class Script::CScript;
	friend class CObject;
	friend class CTracker;

public:
	enum 
	{
		// when used, gets assigned to 'script' member of entry
		vDEAD_ENTRY				= CRCC(0xc377c572, "null_script")
	};
	
	enum
	{
		vDEFAULT_GROUP			= CRCC(0x1ca1ff20, "Default")
	};
	
	
	void	GetDebugInfo(Script::CStruct *p_info);

public:  // temp public, for skater cleanup to prevent fragmentation
#ifdef	__SCRIPT_EVENT_TABLE__			
	void						unregister_all(Script::CScript *p_script);
#else
	void						unregister_all(CObject *p_object);
#endif

	void						AddEvent(uint32 ex, uint32 scr, uint32 group, bool exception, Script::CStruct *p_params);
	
	static void					sPrintTable ( CEventHandlerTable* table );
protected:

								CEventHandlerTable();
								~CEventHandlerTable();


	void						add_from_script(Script::CArray *pArray, bool replace = false);
	void						remove_entry(uint32 type);
	void						remove_group(uint32 group = vDEFAULT_GROUP);
	void						compress_table();
	void						set_event_enable(uint32 type, bool state);
	
#ifdef	__SCRIPT_EVENT_TABLE__		
	void						pass_event(CEvent *pEvent, Script::CScript *pScript, bool broadcast = false);
	void						register_all(Script::CScript *p_script);
#else	
	void						pass_event(CEvent *pEvent, CObject *pObject, bool broadcast = false);
	void						register_all(CObject *p_object);
#endif	
	
	struct Entry
	{
		uint32					type;
		uint32					script;
		uint32					group;
		
		// if false, script won't be run when event
		// of matching type received
		bool					enabled;
		
		// if "exception" is true, then the script is jumped to, rather than called.
		bool					exception;
		
		// if "broadcast" is true, then this handler will also respont to broadcast events
		bool					broadcast;
			
		Script::CStruct		*	p_params;
	};

#ifdef	__NOPT_ASSERT__
public:
#endif
	Entry *						mp_tab;
	int							m_num_entries;
	bool						m_valid;
	bool						m_changed;		// set if table changed while running an event
	int							m_in_immediate_use_counter; // by pass_event()
};



}
#endif


================================================
FILE: Code/Gel/Input/InpMan.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Input (INP) 		 									**
**																			**
**	File name:		inpman.cpp												**
**																			**
**	Created:		05/26/2000	-	spg										**
**																			**
**	Description:	Input manager code										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#ifdef __NOPT_DEBUG__
#include 
#endif

#include 
#include 
 
#include 

#include  
#include  

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Inp
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "Input Manager" );

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

Manager::Manager ( void )
{
	
	int i;
	
	SIO::Manager * sio_manager = SIO::Manager::Instance();

	m_process_handlers_task = new Tsk::Task< Manager >( process_handlers, *this, Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_PROCESS_HANDLERS );
	for( i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		m_server[i].m_device = sio_manager->GetDeviceByIndex( i );
	}

	m_override_pad_with_stick = true;
	Dbg_AssertType( m_process_handlers_task, Tsk::Task< Manager > );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
	
	
	Dbg_AssertType( m_process_handlers_task, Tsk::Task< Manager > );
	delete m_process_handlers_task;   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::process_handlers( const Tsk::Task< Manager >& task )
{
	

	int i;
	Manager&	manager = task.GetData();      

	for( i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		// See if we hit the end of our record/playback buffers
		if( ( manager.m_server[i].m_data_in ) &&
			( manager.m_server[i].m_data_in >= manager.m_server[i].m_data_in_end ))
		{
			manager.m_server[i].m_data_in = NULL;
		}
		if( ( manager.m_server[i].m_data_out ) &&
			( manager.m_server[i].m_data_out >= manager.m_server[i].m_data_out_end ))
		{
			manager.m_server[i].m_data_out = NULL;
		}
		
        manager.m_server[i].service_handlers();
	}   
}

// Note: The order of these checksums must match the corresponding order of the PAD_ defines
// for the buttons in inpman.h
static uint32 ButtonChecksums[PAD_NUMBUTTONS]=
{
	0x0,		// Nowt
	0xbc6b118f, // Up
	0xe3006fc4, // Down
	0x85981897, // Left
	0x4b358aeb, // Right
	0xb7231a95, // UpLeft
	0xa50950c5, // UpRight
	0xd8847efa, // DownLeft
	0x786b8b68, // DownRight
	0x2b489a86, // Circle
	0x321c9756, // Square
	0x7323e97c, // X
	0x20689278, // Triangle
	0x26b0c991, // L1
	0xbfb9982b, // L2
	0xc8bea8bd, // L3
	0xf2f1f64e, // R1
	0x6bf8a7f4, // R2
	0x1cff9762, // R3
	0x767a45d7, // Black
	0xbd30325b, // White
	0x9d2d8850, // Z
};	

uint32 GetButtonChecksum( int whichButton )
{
	
	Dbg_MsgAssert( ( whichButton > 0 ) && ( whichButton < PAD_NUMBUTTONS ), ( "button %d out of range", whichButton ) );
	return ( ButtonChecksums[ whichButton ] );
}

int GetButtonIndex(uint32 Checksum)
{
	
	for (int i=1; i=0 && iEnabled();
	}	
	else
	{
		return false;
	}	
}

/******************************************************************/
/*                                                                */
/* K: Disables the actuators on all devices.                      */
/* Used by the VibrationOff script command.						  */
/*                                                                */
/******************************************************************/

void	Manager::DisableActuators( void )
{
	

	int i;

	for( i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		if (m_server[i].m_device)
		{
			m_server[i].m_device->DisableActuators();
		}	
	}
}

// Disables just one actuator. Called by the VibrationOff script command.
void	Manager::DisableActuator( int i )
{
	

	Dbg_MsgAssert(i>=0 && iDisableActuators();
	}	
}

/******************************************************************/
/*                                                                */
/* K: Enables the actuators on all devices.                       */
/* Used by the VibrationOn script command.						  */
/*                                                                */
/******************************************************************/

void	Manager::EnableActuators( void )
{
	

	int i;

	for( i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		if (m_server[i].m_device)
		{
			m_server[i].m_device->EnableActuators();
		}	
	}
}

// Enables just one actuator. Called by the VibrationOn script command.
void	Manager::EnableActuator( int i )
{
	
	Dbg_MsgAssert(i>=0 && iEnableActuators();
	}	
}


/******************************************************************/
/*                                                                */
/* Mick: stops the actuators on all devices.                      */
/*                                                                */
/******************************************************************/

void	Manager::ResetActuators( void )
{
	

	int i;

	for( i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		if (m_server[i].m_device)
		{
			m_server[i].m_device->ResetActuators();
		}	
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetAnalogStickOverride( bool should_override_pad )
{
	m_override_pad_with_stick = should_override_pad;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ShouldAnalogStickOverride( void )
{
	return m_override_pad_with_stick;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Inp


================================================
FILE: Code/Gel/Input/inpserv.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Input (INP) 		 									**
**																			**
**	File name:		inpserv.cpp												**
**																			**
**	Created:		05/31/2000	-	spg										**
**																			**
**	Description:	Input server code										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 



#ifdef __NOPT_DEBUG__
#include 
#endif

#include 
#include 
#include 
 
#include 

#include 

#ifdef	__PLAT_NGPS__
#include 	  		// Mick:  needed for low level input patch bypassing task system
#include 					// Mick: needed for low level inactivity timeout on PS2 demo disk
#elif defined( __PLAT_NGC__ )
#include 
#endif

#include 

#ifdef	__PLAT_NGPS__
extern	sceDemoEndReason demo_exit_reason;
extern  int		inactive_time;
extern  int		inactive_countdown;
extern  int		gameplay_time;
#endif

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Inp
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

// Used to map digital input to analog events in case of no analog button support
static  int adc_table[][2] = 
{
    { Data::mD_L3,        Data::vA_L3 },
    { Data::mD_R3,        Data::vA_R3 },
    { Data::mD_L2,        Data::vA_L2 },
    { Data::mD_R2,        Data::vA_R2 },
    { Data::mD_L1,        Data::vA_L1 },
    { Data::mD_R1,        Data::vA_R1 },
    { Data::mD_TRIANGLE,  Data::vA_TRIANGLE },
    { Data::mD_CIRCLE,    Data::vA_CIRCLE },
    { Data::mD_X,         Data::vA_X },
    { Data::mD_SQUARE,    Data::vA_SQUARE },
    { Data::mD_UP,        Data::vA_UP },
    { Data::mD_RIGHT,     Data::vA_RIGHT },
    { Data::mD_DOWN,      Data::vA_DOWN },
    { Data::mD_LEFT,      Data::vA_LEFT },
    { Data::mD_BLACK,     Data::vA_BLACK },
    { Data::mD_WHITE,     Data::vA_WHITE },
    { Data::mD_Z,         Data::vA_Z },
//	{ Data::mD_SELECT,	  Data::vA_SELECT },
    { 0,                  0 }
};

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

// Mick: Controller info is made public for quick and easy debug test
// it sould not be used for any shippable code....
uint32	gDebugButtons[SIO::vMAX_DEVICES];
uint32	gDebugBreaks[SIO::vMAX_DEVICES];
uint32	gDebugMakes[SIO::vMAX_DEVICES];

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Server::service_handlers( void )
{
//    
    
    unsigned char *control_data;
	Data *handler_data;
    Flags caps;

	// If we are getting our input from recorded data, set all depended controller data from recorded data   
	if( m_data_in && m_data_in->m_valid )
	{
		int i;
		
		m_data.m_prev = m_data.m_cur;
		m_data.m_cur = m_data_in->m_cur;		// Get digital input from buffer
		m_data.m_new = m_data.m_prev ^ m_data.m_cur;
		m_data.m_Buttons = m_data.m_cur;
		m_data.m_Makes = m_data.m_new & m_data.m_cur;
		m_data.m_Breaks = m_data.m_new & ~m_data.m_cur;
		
		for( i = 0; i < Data::vMAX_ANALOG_EVENTS; i++ )	// Get analog input from buffer
		{      
			m_data.m_Event[i] = m_data_in->m_event[i];
		}
		
		m_handler_stack.Process();
		
		m_data_in++;
		return;
	}
			
	if( ( m_device ) &&
		( m_device->HasValidControlData()))
    {   
		Inp::Manager * inp_manager = Inp::Manager::Instance();

        control_data = m_device->GetControlData();
        handler_data = &m_data;
        caps = m_device->GetCapabilities();
        Dbg_Assert( control_data );
		
		if( m_data_out )
		{
			m_data_out->m_valid = FALSE;
		}

        if( control_data[0] == 0 )  // Valid controller communication
        {
            unsigned char controller_type;
            
            controller_type = control_data[1] >> 4;  // top 4 bits contain controller type
            handler_data->m_prev = handler_data->m_cur;
			// Regular buttons
            handler_data->m_cur = 0xFFFF ^ (( control_data[2] << 8 ) | control_data[3] );
			// The XBox black & white and NGC Z buttons.
			handler_data->m_cur |= (control_data[20]<<16);
			
			
			handler_data->m_new = handler_data->m_prev ^ handler_data->m_cur;
			
			handler_data->m_Buttons = handler_data->m_cur;         
            handler_data->m_Makes = handler_data->m_new & handler_data->m_cur;
            handler_data->m_Breaks = handler_data->m_new & ~handler_data->m_cur;
            
            switch( controller_type )
            {
            case SIO::Device::vNEGI_COM:
                // TODO: Handle 
                Dbg_MsgAssert( 0,( "Unsupported Device Type\n" ));
                break;

            case SIO::Device::vKONAMI_GUN:
                // TODO: Handle 
                Dbg_MsgAssert( 0,( "Unsupported Device Type\n" ));
                break;

            case SIO::Device::vDIGITAL_CTRL:
                handler_data->ConvertDigitalToAnalog();
                break;                                                   

            case SIO::Device::vNAMCO_GUN:
                // TODO: Handle 
                Dbg_MsgAssert( 0,( "Unsupported Device Type\n" ));
                break;

            case SIO::Device::vJOYSTICK:
                handler_data->m_Event[Data::vA_RIGHT_X] = control_data[4];
                handler_data->m_Event[Data::vA_RIGHT_Y] = control_data[5];
                handler_data->m_Event[Data::vA_LEFT_X] = control_data[6];
                handler_data->m_Event[Data::vA_LEFT_Y] = control_data[7];
                handler_data->ConvertDigitalToAnalog();
				if( inp_manager->ShouldAnalogStickOverride())
				{
					handler_data->OverrideAnalogPadWithStick();
				}
                break;

            case SIO::Device::vANALOG_CTRL:
                //if( caps.TestMask( SIO::mANALOG_BUTTONS ))
				if( m_device->GetButtonMode() == SIO::vANALOG )
                {
                    memcpy( handler_data->m_Event, &control_data[4], Data::vMAX_ANALOG_EVENTS );
                }
                else
                {
                    handler_data->m_Event[Data::vA_RIGHT_X] = control_data[4];
                    handler_data->m_Event[Data::vA_RIGHT_Y] = control_data[5];
                    handler_data->m_Event[Data::vA_LEFT_X] = control_data[6];
                    handler_data->m_Event[Data::vA_LEFT_Y] = control_data[7];
                    handler_data->ConvertDigitalToAnalog();
                    handler_data->handle_analog_tolerance();
					if( inp_manager->ShouldAnalogStickOverride())
					{
						handler_data->OverrideAnalogPadWithStick();
					}
                }
                break;

            case SIO::Device::vFISHING_CTRL:
                // TODO: Handle 
                Dbg_MsgAssert( 0,( "Unsupported Device Type\n" ));
                break;
                
            case SIO::Device::vJOG_CTRL:        
                // TODO: Handle 
                Dbg_MsgAssert( 0,( "Unsupported Device Type\n" ));
                break;

            }
            
			if( m_data_out )
			{
				int i;
				
				m_data_out->m_cur = handler_data->m_cur;
				m_data_out->m_valid = TRUE;
				for( i = 0; i < Data::vMAX_ANALOG_EVENTS; i++ )	// Get analog input from buffer
				{      
					m_data_out->m_event[i] = handler_data->m_Event[i];
				}
				
				m_data_out++;
			}

			// Make simple controller buttons accessible at global level
			// for debugging tools			
			
			if (m_device)
			{
				gDebugButtons[m_device->GetIndex()] = m_data.m_Buttons; 							
				gDebugMakes[m_device->GetIndex()] = m_data.m_Makes; 							
				gDebugBreaks[m_device->GetIndex()] = m_data.m_Breaks; 							
								
				#ifdef	__PLAT_NGPS__
				MemView_Input(m_data.m_Buttons, m_data.m_Makes, m_data.m_Breaks );
							
				// Mick: added test for "Select" which will now exit the game if in demo mode
				
				if (Config::Bootstrap())
				{

					bool exit = false;
		
					if (m_data.m_Buttons ||			
						(handler_data->m_Event[Data::vA_RIGHT_X] !=128)||
						(handler_data->m_Event[Data::vA_RIGHT_Y] !=128)	||
						(handler_data->m_Event[Data::vA_LEFT_X] !=128) 	||
						(handler_data->m_Event[Data::vA_LEFT_Y] !=128))
					{
						//printf ("%d activity....\n", inactive_countdown);
						inactive_countdown = inactive_time;				
					}
		
					if (m_device->GetIndex() == 0)
					{
						
						if (inactive_countdown)
						{
							inactive_countdown --;
							if (inactive_countdown == 0)
							{
								printf ("Exiting due to inactivity\n");
								exit = true;  
								demo_exit_reason = SCE_DEMO_ENDREASON_PLAYABLE_INACTIVITY_TIMEOUT;
							}
						}
						if (gameplay_time)
						{
							gameplay_time--;
							if (gameplay_time == 0)
							{
								printf ("Exiting due to gameplay timer exiting\n");
								exit = true;  
								demo_exit_reason = SCE_DEMO_ENDREASON_PLAYABLE_GAMEPLAY_TIMEOUT;
							}
						}
					}
					
					if ((m_data.m_Buttons & Inp::Data::mD_SELECT) && (m_data.m_Buttons & Inp::Data::mD_START))
					{
						exit = true;  
						demo_exit_reason = SCE_DEMO_ENDREASON_PLAYABLE_QUIT;
					}
					
					if ( exit )
					{
                        Sys::ExitDemo();
					}
				}
				#endif				  
			}
			
            // At this point, we have obtained and converted valid controller information
            // Now propogate it to clients
            m_handler_stack.Process();
        }
    }            
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Data::handle_analog_tolerance( void )
{

	// copy unclamped values over before we clamp them										  
	m_Event[Data::vA_RIGHT_X_UNCLAMPED] = m_Event[Data::vA_RIGHT_X];	
	m_Event[Data::vA_RIGHT_Y_UNCLAMPED] = m_Event[Data::vA_RIGHT_Y];	
	m_Event[Data::vA_LEFT_X_UNCLAMPED] = m_Event[Data::vA_LEFT_X];	
	m_Event[Data::vA_LEFT_Y_UNCLAMPED] = m_Event[Data::vA_LEFT_Y];	
					 
					 
	if( Mth::Abs( vANALOGUE_CENTER - m_Event[Data::vA_RIGHT_X] ) <= vANALOGUE_TOL )
    {
        m_Event[Data::vA_RIGHT_X] = vANALOGUE_CENTER;
    }

    if( Mth::Abs( vANALOGUE_CENTER - m_Event[Data::vA_RIGHT_Y] ) <= vANALOGUE_TOL )
    {
        m_Event[Data::vA_RIGHT_Y] = vANALOGUE_CENTER;
    }

    if( Mth::Abs( vANALOGUE_CENTER - m_Event[Data::vA_LEFT_X] ) <= vANALOGUE_TOL )
    {
        m_Event[Data::vA_LEFT_X] = vANALOGUE_CENTER;
    }

    if( Mth::Abs( vANALOGUE_CENTER - m_Event[Data::vA_LEFT_Y] ) <= vANALOGUE_TOL )
    {
        m_Event[Data::vA_LEFT_Y] = vANALOGUE_CENTER;
    }

// "unclamped", only zero the controllers if BOTH X and Y are in the dead zone
// that way, we can smoothly go around the periphery, and get much better aiming ability
// (improves the look-around camera, and the walking)

	if( Mth::Abs( vANALOGUE_CENTER - m_Event[Data::vA_RIGHT_X_UNCLAMPED] ) <= vANALOGUE_TOL 
	&& Mth::Abs( vANALOGUE_CENTER - m_Event[Data::vA_RIGHT_Y_UNCLAMPED] ) <= vANALOGUE_TOL )
    {
        m_Event[Data::vA_RIGHT_X_UNCLAMPED] = vANALOGUE_CENTER;
        m_Event[Data::vA_RIGHT_Y_UNCLAMPED] = vANALOGUE_CENTER;
    }

    if( Mth::Abs( vANALOGUE_CENTER - m_Event[Data::vA_LEFT_X_UNCLAMPED] ) <= vANALOGUE_TOL 
	&& Mth::Abs( vANALOGUE_CENTER - m_Event[Data::vA_LEFT_Y_UNCLAMPED] ) <= vANALOGUE_TOL )
    {
        m_Event[Data::vA_LEFT_X_UNCLAMPED] = vANALOGUE_CENTER;
        m_Event[Data::vA_LEFT_Y_UNCLAMPED] = vANALOGUE_CENTER;
    }
	
	
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Server::Server( void )
{
//    
    
	
	m_data_in = NULL;
	m_data_out = NULL;
	m_data_in_end = NULL;
	m_data_out_end = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Server::RecordInput( RecordedData *data_buffer )
{
//    
    
	
	Dbg_Assert( data_buffer );
	
	m_data_out = data_buffer;   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Server::PlaybackInput( RecordedData *recorded_input )
{
//    
    
	
	Dbg_Assert( recorded_input );
	
	m_data_in = recorded_input;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
    
void Data::MaskDigitalInput( int button_mask )
{
//    
    

	m_Buttons &= ~button_mask;   
	
#if 0 	// old way of masking
	m_new = m_prev ^ m_Buttons;
	m_Makes = m_new & m_Buttons;
	m_Breaks = m_new & ~m_Buttons;
#endif
	
	m_Makes &= ~button_mask;
	m_Breaks &= ~button_mask;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Data::MaskAnalogInput( int button_mask )
{
//    
    

	int i, mask;   
		
    mask = 1;
	for( i = 0; i < vMAX_ANALOG_EVENTS; i++, mask <<= 1 )
	{
		if( button_mask & mask )
		{
            // Analog stick is neutral at 128, all other buttons are neutral at 0
            if( 
				( i == vA_RIGHT_X ) ||
                ( i == vA_RIGHT_Y ) ||
                ( i == vA_LEFT_X  ) ||
                ( i == vA_LEFT_Y  )	||
				( i == vA_RIGHT_X_UNCLAMPED ) ||
                ( i == vA_RIGHT_Y_UNCLAMPED ) ||
                ( i == vA_LEFT_X_UNCLAMPED  ) ||
                ( i == vA_LEFT_Y_UNCLAMPED  ))
            {   
                m_Event[i] = 128;
            }
            else
            {
                m_Event[i] = 0;
            }
		}
	}
}

/******************************************************************/
/* Overrides Analog Pad with Analog Stick values if pad is not in */
/* in use                                                         */
/******************************************************************/

void Data::OverrideAnalogPadWithStick( void )
{
	

	// Only override analog pad if no analog input is present
	if( m_Buttons & ( mD_UP | mD_RIGHT | mD_DOWN | mD_LEFT ))
	{
		return;
	}

	if( m_Event[vA_LEFT_X] > vANALOGUE_CENTER )
	{
		m_Event[vA_RIGHT] = m_Event[vA_LEFT_X] - vANALOGUE_CENTER;
		m_Buttons |= mD_RIGHT;
	}
	else if( m_Event[vA_LEFT_X] < vANALOGUE_CENTER )
	{
		m_Event[vA_LEFT] = Mth::Abs( vANALOGUE_CENTER - m_Event[vA_LEFT_X] );
		m_Buttons |= mD_LEFT;
	}
    
	if( m_Event[vA_LEFT_Y] < vANALOGUE_CENTER )
	{
		m_Event[vA_UP] = m_Event[Data::vA_LEFT_Y] - vANALOGUE_CENTER;
		m_Buttons |= mD_UP;
	}
	else if( m_Event[vA_LEFT_Y] > vANALOGUE_CENTER )
	{
		m_Event[vA_DOWN] = Mth::Abs( vANALOGUE_CENTER - m_Event[vA_LEFT_Y] );
		m_Buttons |= mD_DOWN;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Data::ConvertDigitalToAnalog( void )
{
	int i;

    for( i = 0; adc_table[i][0] > 0; i++ )
	{
		if( m_Buttons & adc_table[i][0] )  // If we have digital "down" convert to fully-pressed analog input
		{
			m_Event[adc_table[i][1]] = vMAX_ANALOG_VALUE;
		}
        else
        {
            m_Event[adc_table[i][1]] = 0;
        }
	}  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Data::Data( void )
{
    memset( m_Event, 0, vMAX_ANALOG_EVENTS );
    m_Buttons = 0;
	m_cur = 0;
    m_new = 0;
    m_prev = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Inp


================================================
FILE: Code/Gel/MainLoop/Mainloop.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Main Loop (ML) 											**
**																			**
**	File name:		mainloop.cpp											**
**																			**
**	Created:		05/27/99	-	mjb										**
**																			**
**	Description:	Main loop and support code								**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 

#include 

#ifndef __PLAT_WN32__
    #include 
#endif

#include 

#ifdef	__PLAT_NGPS__
    #include 
    #include 

    namespace NxPs2
    {
        void	WaitForRendering();
		void 	StuffAfterGSFinished();
    }
#endif

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

Dbg_DefineProject( GEL, "GEL Library" )

namespace Mdl
{
	void	Rail_DebugRender();			// for debugging
}

namespace Mlp
{




/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "Main Loop Manager" );

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

Manager::Manager( void )
{
	
	
	start_render_hook = NULL;
	end_render_hook = NULL;
	done = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void		Manager::service_system( void )
{
	

#ifndef __PLAT_WN32__
#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PushContext( 255, 0, 0 );
#	endif // __USE_PROFILER__
#endif

	system_task_stack.Process (currently_profiling);

#ifndef __PLAT_WN32__
#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PopContext();
#	endif // __USE_PROFILER__
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void		Manager::game_logic( void )
{
	
	
#ifndef __PLAT_WN32__
#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PushContext( 0, 255, 0 );
#	endif // __USE_PROFILER__
#endif

//	printf ("\nTiming Logic\n"); 
	logic_task_stack.Process(currently_profiling);

//	printf ("\nDumping Task List\n\n");	
//	logic_task_stack.Dump();

#ifndef __PLAT_WN32__
#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PopContext();
#	endif // __USE_PROFILER__
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::render_frame( void )
{
	
	
//	printf ("############################## render_frame #############################\n");	
	

#ifndef __PLAT_WN32__
#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PushContext( 0, 0, 255 );
#	endif // __USE_PROFILER__
#endif

#	ifdef __PLAT_NGC__
	if( start_render_hook )
	{
		start_render_hook->Call();
	}
	if( !display_tasks_paused )
	{
		display_task_stack.Process( currently_profiling );
	}
#	else
	if (!display_tasks_paused)
	{
		// If paused don't call the start_render_hook
		// as this clears the none-visible frame buffer
		// which will result in a flash when display is unpaused
		if ( start_render_hook )			// set up for rendering
		{
			start_render_hook->Call();
		}

//		printf ("\nTiming render\n"); 
	
		display_task_stack.Process(currently_profiling);		// service rendering routines
	}
#	endif // __PLAT_NGC__
	
#ifndef __PLAT_WN32__
#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PopContext();
	Sys::CPUProfiler->PushContext( 255, 255, 0 );
#	endif // __USE_PROFILER__
#endif

	if ( end_render_hook )				// end rendering
	{
		end_render_hook->Call();
	}
#ifndef __PLAT_WN32__
#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PopContext();
#	endif // __USE_PROFILER__
#endif
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

void		Manager::MainLoop( void )
{
	

	bool								old_flag = done;	// push the current done flag

	done = false;

	while ( !done )
	{
	
		if (trigger_profiling)
		{
			printf ("\nProfiling.....Start of main loop\n\n");
			trigger_profiling--;
			currently_profiling = 1;
		}
		

#ifndef __PLAT_WN32__

#ifdef	__PLAT_NGPS__		
// for profiling on the PS2, we do a bit of the code from sPreRender here
// so we can time the final waiting for GS		
#	ifdef __USE_PROFILER__
		Sys::CPUProfiler->PushContext( 0,0,255 );		// blue = wait for VU/DMA/GPU to finish
#	endif		
		NxPs2::WaitForRendering();		// PS2 Specific, wait for prior frame's DMA to finish	
		NxPs2::StuffAfterGSFinished();		// PS2 Specific, DMA altering stuff run after previous frame's DMA finished
#	ifdef __USE_PROFILER__
		Sys::CPUProfiler->PopContext(  );
		Sys::CPUProfiler->PushContext( 128,0,0 );		// red = wait for vblank
#	endif		
		int framerate = Script::GetInteger(0x3214c818/*"lock_framerate"*/);
		if (framerate)	// normal vsync
		{
			static uint64 next_vblanks= 0;
			// Still call VSync, as it updates some functions
			Tmr::VSync();
			while (Tmr::GetVblanks()PopContext(  );
#	endif		
#endif				
#ifndef	__PLAT_NGC__		
#	ifdef __USE_PROFILER__
		Sys::Profiler::sStartFrame();		  		
#	endif		
#endif		
		
		Nx::CEngine::sPreRender();			 			// start rendering previous frame's DMA list

#ifdef	__PLAT_NGPS__		
		Sfx::CSpuManager::sUpdateStatus();				// Garrett: This should go into some system task, but I'll put it here for now
#endif

//		Sys::Profiler::sStartFrame();		  		
#endif

#ifdef	__PLAT_NGC__		
#	ifdef __USE_PROFILER__
		Sys::Profiler::sStartFrame();		  		
#	endif		
#	endif		
					 
		service_system();

#if	defined(__PLAT_NGPS__) && defined(BATCH_TRI_COLLISION)
		// Enable VU0 collision
		bool got_vu0 = Nx::CBatchTriCollMan::sUseVU0Micro();
		Dbg_Assert(got_vu0);
#endif

 #ifdef	__PLAT_NGPS__		
//		snProfSetRange( -1, (void*)0, (void*)-1);
//		snProfSetFlagValue(0x01);
 #endif

		game_logic();		

 #ifdef	__PLAT_NGPS__		
//		snProfSetRange( 4, (void*)NULL, (void*)-1);
 #endif		


#if	defined(__PLAT_NGPS__) && defined(BATCH_TRI_COLLISION)
		// Disable VU0 collision
		if (got_vu0)
		{
			Nx::CBatchTriCollMan::sDisableVU0Micro();
		}
#endif

#ifndef __PLAT_WN32__
		// Display the memory contents, (if memview is active)
		MemView_Display();
#	ifdef __USE_PROFILER__
		Sys::CPUProfiler->PushContext( 255, 255, 0 );  // yellow = render world
#	endif		

 #ifdef	__PLAT_NGPS__		
//		snProfSetRange( -1, (void*)0, (void*)-1);
//		snProfSetFlagValue(0x01);
 #endif
		
		Nx::CEngine::sRenderWorld();		
#ifdef	__PLAT_NGC__		
#ifdef		__USE_PROFILER__
		Sys::Render_Profiler();		
#endif
#endif
		// Mick: bit of a patch here, we need some better debug hooks
		Mdl::Rail_DebugRender();
#	ifdef __USE_PROFILER__
			Sys::CPUProfiler->PushContext( 0, 0, 0 );	 	// Black (Under Yellow) = sPostRender
#	endif // __USE_PROFILER__
		Nx::CEngine::sPostRender();		  // Previous frames profiler is rendered here
#	ifdef __USE_PROFILER__
		Sys::CPUProfiler->PopContext(  );
#	endif		


	

#	ifdef __USE_PROFILER__
		Sys::CPUProfiler->PopContext(  );
#	endif		
		Tmr::OncePerRender();
 #ifdef	__PLAT_NGPS__		
//		snProfSetRange( 4, (void*)NULL, (void*)-1);
 #endif		
#endif		
		
		currently_profiling = false;

	}

	done = old_flag;
}

void Manager::ProfileTasks(int n)
{
	trigger_profiling = n;
	currently_profiling = 0;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::QuitLoop( void )
{
	

	Dbg_Notify( "Exiting..." );

	done = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::DoGameLogic( void )
{
	game_logic();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mlp



================================================
FILE: Code/Gel/Module/modman.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Module (MDL)		 									**
**																			**
**	File name:		modman.cpp												**
**																			**
**	Created:		05/27/99	-	mjb										**
**																			**
**	Description:	Module manager code										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#ifdef __NOPT_DEBUG__
#include 
#endif

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Mdl
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "Module Manager" );

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

Manager::Manager ( void )
{
	

	process_modules_task = new Tsk::Task< Manager >( process_modules, *this, Tsk::BaseTask::Node::vSYSTEM_TASK_PRIORITY_PROCESS_MODULES );
	Dbg_AssertType( process_modules_task, Tsk::Task< Manager > );

	control_change = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
	
	
	StopAllModules();
		
	Dbg_AssertType( process_modules_task, Tsk::Task< Manager > );
	delete process_modules_task;
	
	Dbg_MsgAssert( module_list.IsEmpty(),( 
		"%d module%s still registered\n",
			module_list.CountItems(),
			module_list.CountItems() > 1 ? "s" : "" ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::process_modules( const Tsk::Task< Manager >& task )
{
	

	Module*					next;
	Module*					current;
	Lst::Search< Module >	sh;

	Manager&	manager = task.GetData();

	if ( !manager.control_change )
	{
		return; // nothing to do
	}

	manager.control_change = false;
	
	next = sh.FirstItem( manager.module_list );

	while ( next )
	{
		current = next;
		next = sh.NextItem();			// get next item before excuting callback

		switch ( current->state )
		{
			case Module::vSTOPPED :
			{
				if (( current->command == Module::vSTART ) ||
					( current->command == Module::vRESTART ))
				{
#ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
#ifdef __NOPT_DEBUG__
				Dbg_Notify ( "Starting module %s @ %d", current->GetName(), 
					Tmr::GetTime() ); // should use Game Clock not system clock
#endif
#endif
#endif
					current->v_start_cb();
					current->state = Module::vRUNNING;
					current->command = Module::vNONE;
				}
				
				break;
			}
			
			case Module::vRUNNING :
			{
				if ( current->command == Module::vSTOP )
				{
#ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
#ifdef __NOPT_DEBUG__
					Dbg_Notify ( "Stopping module %s @ %d", current->GetName(), 
						 Tmr::GetTime() ); // should use Game Clock not system clock
#endif
#endif
#endif
					current->v_stop_cb();
					current->state = Module::vSTOPPED;
					current->command = Module::vNONE;
				}
				else if ( current->command == Module::vRESTART ) 
				{
#ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
#ifdef __NOPT_DEBUG__
					Dbg_Notify ( "Restarting module %s @ %d", current->GetName(), 
						Tmr::GetTime() ); // should use Game Clock not system clock
#endif
#endif
#endif
					current->v_stop_cb();
					current->v_start_cb();               
					current->command = Module::vNONE;
				}

				break;
			}        

			default:
			{
				Dbg_MsgAssert( false,( "Invalid module control state" ));
			}
		}
	}
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

void	Manager::RegisterModule( Module &module )
{
	
	
	Dbg_AssertType ( &module, Module );

	module_list.AddToTail ( module.node );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::UnregisterModule( Module &module )
{
	
	
	Dbg_AssertType( &module, Module );

	Dbg_MsgAssert( module.node->InList(),( "Module not Registered with Manager" ));

	module.node->Remove();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::StartModule( Module &module )
{
	
	
	Dbg_MsgAssert( module.node->InList(),( "Module not Registered with Manager" ));

	if ( !module.Locked() )
	{
		module.command = Module::vSTART;
		control_change = true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::RestartModule( Module &module )
{
	
	
	Dbg_MsgAssert( module.node->InList(),( "Module not Registered with Manager" ));

	if ( !module.Locked() )
	{
		module.command = Module::vRESTART;
		control_change = true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::StopModule( Module &module )
{
	

	if ( !module.Locked() )
	{
		module.command = Module::vSTOP;
		control_change = true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::StartAllModules( void )
{
	

	Module*					next;
	Module*					mdl;
	Lst::Search< Module >	sh;

	next = sh.FirstItem( module_list );

	while ( next )
	{
		mdl = next;				
		next = sh.NextItem();	// get next item before excuting callback

		if ( !mdl->Locked() )
		{
			if ( mdl->state == Module::vSTOPPED )
			{
				mdl->v_start_cb();
				mdl->command = Module::vNONE;
				mdl->state = Module::vRUNNING;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::StopAllModules( void )
{
	

	Module*					next;
	Module*					mdl;
	Lst::Search< Module >	sh;

	next = sh.FirstItem( module_list );

	while ( next )
	{
		mdl = next;				
		next = sh.NextItem();	// get next item before excuting callback
								
		if ( !mdl->Locked() )
		{
			if ( mdl->state == Module::vRUNNING )
			{
				mdl->v_stop_cb();
				mdl->command = Module::vNONE;
				mdl->state = Module::vSTOPPED;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::LockAllModules( void )
{
	

	Module*					mdl;
	Lst::Search< Module >	sh;

	mdl = sh.FirstItem( module_list );

	while ( mdl )
	{
		mdl->Lock();
		mdl = sh.NextItem();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::UnlockAllModules ( void )
{
	

	Module*					mdl;
	Lst::Search< Module >	sh;

	mdl = sh.FirstItem ( module_list );

	while ( mdl )
	{
		mdl->Unlock();
		mdl = sh.NextItem();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mdl


================================================
FILE: Code/Gel/Module/module.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Module (MDL)		 									**
**																			**
**	File name:		module.cpp												**
**																			**
**	Created:		05/27/99	-	mjb										**
**																			**
**	Description:	Module code												**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Mdl
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Module::Module ( void )
{
	

	state = vSTOPPED;
	locked = false;

	node = new Lst::Node< Module > ( this );
	Dbg_AssertType( node, Lst::Node< Module > );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Module::~Module ()
{
	

	Dbg_AssertType( node, Lst::Node< Module > );
	delete node;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mdl




================================================
FILE: Code/Gel/Movies/Movies.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			GEL					 									**
**																			**
**	File name:		movies.cpp												**
**																			**
**	Created:		5/14/1	-	mjd											**
**																			**
**	Description:	streaming movies										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

#ifdef __PLAT_NGPS__
#include 
#elif defined( __PLAT_XBOX__ )
#include 
#elif defined( __PLAT_NGC__ )
#include 
#endif

#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Flx
{



void PlayMovie( const char *pMovieName )
{
	PMovies_PlayMovie( pMovieName );
}

}  // namespace Flx


================================================
FILE: Code/Gel/Movies/Movies.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			movies													**
**																			**
**	File name:		gel/movies/movies.h  									**
**																			**
**	Created: 		5/14/01	-	mjd											**
**																			**
*****************************************************************************/

#ifndef __GEL_MOVIES_H
#define __GEL_MOVIES_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
//#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Flx
{



void PlayMovie( const char *pMovieName );

} // namespace Flx

#endif	// __GEL_MOVIES_H





================================================
FILE: Code/Gel/Movies/Ngps/audiodec.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "sys/config/config.h"
#include "gel/movies/ngps/defs.h"
#include "gel/movies/ngps/audiodec.h"
#include "gel/movies/ngps/p_movies.h"
#include "gel/music/music.h"
#include "gel/soundfx/soundfx.h"

#define AU_HEADER_SIZE 40
#define UNIT_SIZE 1024
#define PRESET_VALUE(count)	(count)

namespace Flx
{



#define MAX_VOL				 ( ( float ) 0x3fff )

static void iopGetArea(int *pd0, int *d0, int *pd1, int *d1,
	AudioDec *ad, int pos);
static int sendToIOP2area(int pd0, int d0, int pd1, int d1,
	u_char *ps0, int s0, u_char *ps1, int s1);
static int sendToIOP(int dst, u_char *src, int size);
//static void changeMasterVolume(u_int val);
static void changeInputVolume(u_int val);

void SetMovieVolume( void )
{
    // ///////////////////////////////////////
    //
    // Change input volume
    //
	Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();
	float musicVol = Pcm::GetVolume( ) * Config::GetMasterVolume()/100.0f;
	float sfxVol = sfx_manager->GetMainVolume( )  * Config::GetMasterVolume()/100.0f;
	float vol = musicVol > sfxVol ? musicVol : sfxVol;	
	int inputVol = ( int ) ( ( vol * MAX_VOL ) / 100.0f );
    changeInputVolume( inputVol );
}

// ////////////////////////////////////////////////////////////////
//
// Create audio decoder
//
int audioDecCreate(
    AudioDec *ad,
    u_char *buff,
    int buffSize,
    int iopBuffSize,
	int pIopBuff
)
{
	SetMovieVolume( );
    ad->state = AU_STATE_INIT;
    ad->hdrCount = 0;
    ad->data = buff;
    ad->put = 0;
    ad->count = 0;
    ad->size = buffSize;
    ad->totalBytes = 0;
    ad->totalBytesSent = 0;

    ad->iopBuffSize = iopBuffSize;
    ad->iopLastPos = 0;
    ad->iopPausePos = 0;

    // ///////////////////////////////////////
    //
    // Audio data buffer on IOP
    //
    ad->iopBuff = pIopBuff; //(int)sceSifAllocIopHeap(iopBuffSize);
    if(ad->iopBuff < 0) {
	printf( "Cannot allocate IOP memory\n");
	return 0;
    }
    printf("IOP memory 0x%08x(size:%d) is allocated\n",
    	ad->iopBuff, iopBuffSize);

    // ///////////////////////////////////////
    //
    // Zero data buffer on IOP
    //
    ad->iopZero = pIopBuff + iopBuffSize; //(int)sceSifAllocIopHeap(ZERO_BUFF_SIZE);
    if(ad->iopZero < 0) {
	printf( "Cannot allocate IOP memory\n");
	return 0;
    }
    printf("IOP memory 0x%08x(size:%d) is allocated\n",
    	ad->iopZero, ZERO_BUFF_SIZE);

    // send zero data to IOP
    memset ( MOVIE_MEM_PTR _0_buf, 0, ZERO_BUFF_SIZE);
    sendToIOP(ad->iopZero, ( u_char * ) MOVIE_MEM_PTR _0_buf, ZERO_BUFF_SIZE);

    // ///////////////////////////////////////
    //
    // Change master volume
    //
//    changeMasterVolume(0x3fff);

    return 1;
}

// ////////////////////////////////////////////////////////////////
//
// Delete audio decoder
//
int audioDecDelete(AudioDec *ad)
{
//    sceSifFreeIopHeap((void *)ad->iopBuff);
//    sceSifFreeIopHeap((void *)ad->iopZero);

    // ///////////////////////////////////////
    //
    // Change master volume
    //
//    changeMasterVolume(0x0000);
    changeInputVolume(0x0000);

    return 1;
}

// ////////////////////////////////////////////////////////////////
//
// Pause
//
void audioDecPause(AudioDec *ad)
{
    int ret;
    ad->state = AU_STATE_PAUSE;

    // ///////////////////////////////////////
    //
    // Change input volume
    //
    changeInputVolume(0x0000);

    // ///////////////////////////////////////
    //
    // Stop DMA and save the position to be played
    //
    ret = sceSdRemote(1, rSdBlockTrans, AUTODMA_CH,
			   SD_TRANS_MODE_STOP, NULL, 0) & 0x00FFFFFF;
    ad->iopPausePos = ret - ad->iopBuff;

    // ///////////////////////////////////////
    //
    // Clear SPU2 buffer
    //
    sceSdRemote(1, rSdVoiceTrans, AUTODMA_CH,
		 SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA,
		 ad->iopZero, 0x4000, ZERO_BUFF_SIZE);
}

// ////////////////////////////////////////////////////////////////
//
// Resume
//
void audioDecResume(AudioDec *ad)
{
	SetMovieVolume( );
    sceSdRemote(1, rSdBlockTrans, AUTODMA_CH,
    		(SD_TRANS_MODE_WRITE_FROM | SD_BLOCK_LOOP),
		 ad->iopBuff, (ad->iopBuffSize/UNIT_SIZE)*UNIT_SIZE,
		 ad->iopBuff + ad->iopPausePos);

    ad->state = AU_STATE_PLAY;
}

// ////////////////////////////////////////////////////////////////
//
// Start to play audio data
//
void audioDecStart(AudioDec *ad)
{
//    return audioDecResume(ad);
    audioDecResume(ad);
}

// ////////////////////////////////////////////////////////////////
//
// Re-initialize audio decoder
//
void audioDecReset(AudioDec *ad)
{
    // ///////////////////////////////////////
    //
    // Stop audio
    //
    audioDecPause(ad);

    ad->state = AU_STATE_INIT;
    ad->hdrCount = 0;
    ad->put = 0;
    ad->count = 0;
    ad->totalBytes = 0;
    ad->totalBytesSent = 0;
    ad->iopLastPos = 0;
    ad->iopPausePos = 0;
}

// ////////////////////////////////////////////////////////////////
//
// Get empty areas
//
void audioDecBeginPut(AudioDec *ad,
	u_char **ptr0, int *len0, u_char **ptr1, int *len1)
{
    int len;

    // ///////////////////////////////////////
    //
    // return ADS header area when (state == AU_STATE_INIT)
    //
    if (ad->state == AU_STATE_INIT) {
    	*ptr0 = (u_char*)&ad->sshd + ad->hdrCount;
	*len0 = AU_HDR_SIZE - ad->hdrCount;
	*ptr1 = (u_char*)ad->data;
	*len1 = ad->size;

	return;
    }

    // ///////////////////////////////////////
    //
    // Return the empty areas
    //
    len = ad->size - ad->count;

    if (ad->size -  ad->put >= len) { // area0
    	*ptr0 = ad->data + ad->put;
	*len0 = len;
	*ptr1 = NULL;
	*len1 = 0;
    } else {			    // area0 + area1
    	*ptr0 = ad->data + ad->put;
	*len0 = ad->size - ad->put;
	*ptr1 = ad->data;
	*len1 = len - (ad->size - ad->put);
    }
}

// ////////////////////////////////////////////////////////////////
//
// Update pointer
//
void audioDecEndPut(AudioDec *ad, int size)
{
    if (ad->state == AU_STATE_INIT) {
    	int hdr_add = min( size, AU_HDR_SIZE - ( int )ad->hdrCount);
    	ad->hdrCount += hdr_add;

	if ( ( int ) ad->hdrCount >= AU_HDR_SIZE) {
	    ad->state = AU_STATE_PRESET;

	    printf("-------- audio information --------------------\n");
	    printf("[%c%c%c%c]\n"
	       "header size:                            %d\n"
	       "type(0:PCM big, 1:PCM little, 2:ADPCM): %d\n"
	       "sampling rate:                          %dHz\n"
	       "channels:                               %d\n"
	       "interleave size:                        %d\n"
	       "interleave start block address:         %d\n"
	       "interleave end block address:           %d\n",
		    ad->sshd.id[0],
		    ad->sshd.id[1],
		    ad->sshd.id[2],
		    ad->sshd.id[3],
		    ad->sshd.size,
		    ad->sshd.type,
		    ad->sshd.rate,
		    ad->sshd.ch,
		    ad->sshd.interSize,
		    ad->sshd.loopStart,
		    ad->sshd.loopEnd
	    );

	    printf("[%c%c%c%c]\n"
	       "data size:                              %d\n",
		    ad->ssbd.id[0],
		    ad->ssbd.id[1],
		    ad->ssbd.id[2],
		    ad->ssbd.id[3],
		    ad->ssbd.size
	    );

	}
	size -= hdr_add;
    }
    ad->put = (ad->put + size) % ad->size;
    ad->count += size;
    ad->totalBytes += size;
}

// ////////////////////////////////////////////////////////////////
//
// Check to see if enough data is already sent to IOP or not
//
int audioDecIsPreset(AudioDec *ad)
{
    return ad->totalBytesSent >= PRESET_VALUE(ad->iopBuffSize);
}

// ////////////////////////////////////////////////////////////////
//
// Send data to IOP
//
int audioDecSendToIOP(AudioDec *ad)
{
    int pd0, pd1, d0, d1;
    u_char *ps0, *ps1;
    int s0, s1;
    int count_sent = 0;
    int countAdj;
    int pos = 0;

    switch (ad->state) {
        case AU_STATE_INIT:
	    return 0;
	    break;

        case AU_STATE_PRESET:
	    pd0 = ad->iopBuff + (ad->totalBytesSent) % ad->iopBuffSize;
	    d0 = ad->iopBuffSize - ad->totalBytesSent;
	    pd1 = 0;
	    d1 = 0;
	    break;

        case AU_STATE_PLAY:
	    pos = ((sceSdRemote(1, rSdBlockTransStatus, AUTODMA_CH)
	    	& 0x00FFFFFF) - ad->iopBuff);
	    iopGetArea(&pd0, &d0, &pd1, &d1, ad, pos);
	    break;

        case AU_STATE_PAUSE:
	    return 0;
	    break;
    }

    ps0 = ad->data + (ad->put - ad->count + ad->size) % ad->size;
    ps1 = ad->data;

    // adjust to UNIT_SIZE boundary
    countAdj = (ad->count / UNIT_SIZE) * UNIT_SIZE;

    s0 = min(ad->data + ad->size - ps0, countAdj);
    s1 = countAdj - s0;

    if (d0 + d1 >= UNIT_SIZE && s0 + s1 >= UNIT_SIZE) {
    	count_sent = sendToIOP2area(pd0, d0, pd1, d1, ps0, s0, ps1, s1);
    }

    ad->count -= count_sent;

    ad->totalBytesSent += count_sent;
    ad->iopLastPos = (ad->iopLastPos + count_sent) % ad->iopBuffSize;

    return count_sent;
}

// ////////////////////////////////////////////////////////////////
//
// Get empty area of IOP audio buffer
//
static void iopGetArea(int *pd0, int *d0, int *pd1, int *d1,
					AudioDec *ad, int pos)
{
    int len = (pos + ad->iopBuffSize - ad->iopLastPos - UNIT_SIZE)
    			% ad->iopBuffSize;

    // adjust to UNIT_SIZE boundary
    len = (len / UNIT_SIZE) * UNIT_SIZE;

    if (ad->iopBuffSize -  ad->iopLastPos >= len) { // area0
    	*pd0 = ad->iopBuff + ad->iopLastPos;
	*d0 = len;
	*pd1 = 0;
	*d1 = 0;
    } else {			    // area0 + area1
    	*pd0 = ad->iopBuff + ad->iopLastPos;
	*d0 = ad->iopBuffSize - ad->iopLastPos;
	*pd1 = ad->iopBuff;
	*d1 = len - (ad->iopBuffSize - ad->iopLastPos);
    }
}


// ////////////////////////////////////////////////////////////////
//
// Send data to IOP
//
static int sendToIOP2area(int pd0, int d0, int pd1, int d1,
		u_char *ps0, int s0, u_char *ps1, int s1)
{
    if (d0 + d1 < s0 + s1) {
    	int diff = (s0 + s1) - (d0 + d1);
	if (diff >= s1) {
	    s0 -= (diff - s1);
	    s1 = 0;
	} else {
	    s1 -= diff;
	}
    }

    //
    // (d0 + d1 >= s0 + s1)
    //
    if (s0 >= d0) {
    	sendToIOP(pd0,			ps0,		d0);
    	sendToIOP(pd1,			ps0 + d0,	s0 - d0);
    	sendToIOP(pd1 + s0 - d0,	ps1,		s1);
    } else { // s0 < d0
    	if (s1 >= d0 - s0) {
	    sendToIOP(pd0,		ps0,		s0);
	    sendToIOP(pd0 + s0,		ps1,		d0 - s0);
	    sendToIOP(pd1,		ps1 + d0 - s0,	s1 - (d0 - s0));
	} else { // s1 < d0 - s0
	    sendToIOP(pd0,		ps0,		s0);
	    sendToIOP(pd0 + s0,		ps1,		s1);
	}
    }
    return s0 + s1;
}

// ////////////////////////////////////////////////////////////////
//
// Send data to IOP
//
static int sendToIOP(int dst, u_char *src, int size)
{
    sceSifDmaData transData;
    int did;

    if (size <= 0) {
        return 0;
    }

    transData.data = (u_int)src;
    transData.addr = (u_int)dst;
    transData.size = size;
    transData.mode = 0; // caution
    FlushCache(0);

    did = sceSifSetDma( &transData, 1 );

    while (sceSifDmaStat(did) >= 0)
    	;

    return size;
}

// ////////////////////////////////////////////////////////////////
//
// Change master volume
//
/*
static void changeMasterVolume(u_int val)
{
    int i;
    for( i = 0; i < 2; i++ ) {
	sceSdRemote(1, rSdSetParam, i | SD_P_MVOLL, val);
	sceSdRemote(1, rSdSetParam, i | SD_P_MVOLR, val);
    }
} */

// ////////////////////////////////////////////////////////////////
//
// Change input volume
//
static void changeInputVolume(u_int val)
{
    sceSdRemote(1, rSdSetParam, AUTODMA_CH | SD_P_BVOLL, val);
    sceSdRemote(1, rSdSetParam, AUTODMA_CH | SD_P_BVOLR, val);
}

} // namespace Flx


================================================
FILE: Code/Gel/Movies/Ngps/audiodec.h
================================================
#ifndef _AUDIODEC_H_
#define _AUDIODEC_H_

#include 

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
#include 

// ///////////////////////////////////////////////////////
//
// Audio decoder state
//
#define AU_STATE_INIT		0
#define AU_STATE_PRESET		1
#define AU_STATE_PLAY		2
#define AU_STATE_PAUSE		3

#define AU_HDR_SIZE		( ( int )(sizeof(SpuStreamHeader) 					+ sizeof(SpuStreamBody)) )

// ///////////////////////////////////////////////////////
//
// Spu stream header
//
struct SpuStreamHeader{
    char id[4];		// 'S''S''h''d'
    int size;		// 24
    int type;		// 0: 16bit big endian
    			// 1: 16bit little endian
			// 2: SPU2-ADPCM (VAG) 
    int rate;		// sampling rate
    int ch;		// number of channels
    int interSize;	// interleave size ... needs to be 512
    int loopStart;	// loop start block address
    int loopEnd;	// loop end block sddress
};

// ///////////////////////////////////////////////////////
//
// Spu stream body
//
struct SpuStreamBody{
    char id[4];		// 'S''S''b''d'
    int size;		// size of audio data
};

// ///////////////////////////////////////////////////////
//
// Audio decoder
//
struct AudioDec{

    int state;

    // header of ADS format
	struct SpuStreamHeader sshd;
	struct SpuStreamBody   ssbd;
    int hdrCount;

    // audio buffer
    u_char *data;
    int put;
    int count;
    int size;
    int totalBytes;

    // buffer on IOP
    int iopBuff;
    int iopBuffSize;
    int iopLastPos;
    int iopPausePos;
    int totalBytesSent;
    int iopZero;

};

namespace Flx
{



// ///////////////////////////////////////////////////////
//
// Functions
//
int audioDecCreate(
    struct AudioDec *ad,
    u_char *buff,
    int buffSize,
    int iopBuffSize,
	int pIopBuf
);
int audioDecDelete( struct AudioDec *ad);
void audioDecBeginPut( struct AudioDec *ad,
	u_char **ptr0, int *len0, u_char **ptr1, int *len1);
void audioDecEndPut(struct AudioDec *ad, int size);
int audioDecIsPreset(struct AudioDec *ad);
void audioDecStart(struct AudioDec *ad);
int audioDecSendToIOP(struct AudioDec *ad);
void audioDecReset(struct AudioDec *ad);
void audioDecPause(struct AudioDec *ad);
void audioDecResume(struct AudioDec *ad);

} // namespace Flx

#endif _AUDIODEC_H_





================================================
FILE: Code/Gel/Movies/Ngps/defs.h
================================================
#ifndef _DEFS_H_
#define _DEFS_H_

#include 
#include 
#include 

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#define STRFILE_NUM_STREAMING_SECTORS 80

#define UNCMASK 0x0fffffff
#define UNCBASE 0x20000000

#define MAX_WIDTH 720
#define MAX_HEIGHT 576

#define MOVIE_THREAD_PRIORITY 1
#define AUTODMA_CH 0

#define N_LDTAGS (MAX_WIDTH/16 * MAX_HEIGHT/16 * 6 + 10)
#define TS_NONE (-1)

#define bound(val, x) ((((val) + (x) - 1) / (x))*(x))
#define min(x, y) (((x) > (y))? (y): (x))
#define max(x, y) (((x) < (y))? (y): (x))

extern inline void *DmaAddr(void *val)
{
    return (void*)((u_int)val & UNCMASK);
}

extern inline void *UncAddr(void *val)
{
    return (void*)(((u_int)val & UNCMASK)|UNCBASE);
}
 
namespace Flx
{



void ErrMessage(char *message);
void switchThread();
void proceedAudio();

}



#endif // _DEFS_H_


================================================
FILE: Code/Gel/Movies/Ngps/disp.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "gel/movies/ngps/defs.h"
#include "gel/movies/ngps/disp.h"
#include "gel/movies/ngps/videodec.h"
#include "gel/movies/ngps/vobuf.h"
#include "gel/movies/ngps/p_movies.h"
#include 

extern sceGsDBuff gDB;

volatile int isCountVblank = 0;
volatile int vblankCount = 0;
volatile int isFrameEnd = 0;
volatile int oddeven = 0;
volatile int handler_error = 0;

extern int frd;

typedef struct {
  int x, y, w, h;
} Rect;

namespace Flx
{



///////////////////////////////////////////////////////////////////
//
// Delete previous image of frame buffer and texture buffer
//
void clearGsMem(int r, int g, int b, int disp_width, int disp_height)
{
	const u_long giftag_clear[2] = { SCE_GIF_SET_TAG(0, 1, 0, 0, 0, 1), 
				 0x000000000000000eL };
	sceGifPacket packet;
	
	u_long128 packetBase[ 6 ];

	// Abort if gDB hasn't been initialized
	if (*((uint64 *) &gDB.disp[0].display) == 0)
	{
		return;
	}
	
	sceDmaChan* dmaGif = sceDmaGetChan(SCE_DMA_GIF);
	
	// setting GIF tag for gDB.draw0 structure
	SCE_GIF_CLEAR_TAG(&gDB.giftag0);
	gDB.giftag0.NLOOP = 8;
	gDB.giftag0.EOP = 1;
	gDB.giftag0.NREG = 1;
	gDB.giftag0.REGS0 = 0xe; // A_D
	
	// define GS memory as a one big drawing area
	// "bound(DISP_HEIGHT/2, 32)*2" is for frame buffer * 2
	// "bound(DISP_HEIGHT, 32)*2" is for texture buffer
	sceGsSetDefDrawEnv(&gDB.draw0, SCE_GS_PSMCT32, disp_width,
			 bound(disp_height/2, 32)*2 + bound(disp_height, 32)*2,
			 0, 0);
	*(u_long *)&gDB.draw0.xyoffset1 = SCE_GS_SET_XYOFFSET_1(0, 0);
	
	FlushCache(0);
	sceGsSyncPath(0, 0);
	sceGsPutDrawEnv(&gDB.giftag0);
	
//	sceGifPkInit(&packet, MOVIE_MEM_PTR packetBase);
	sceGifPkInit(&packet, packetBase);	  		// Mick: Use a local one, so I can use this function for laodng screen clearing
	sceGifPkReset(&packet);
	
	sceGifPkEnd(&packet, 0, 0, 0);
	
	// packet for a big sprite polygon
	{
	sceGifPkOpenGifTag(&packet, *(u_long128*)&giftag_clear);
	sceGifPkAddGsAD(&packet, SCE_GS_PRIM,
			SCE_GS_SET_PRIM(6, 0, 0, 0, 0, 0, 0, 0, 0));
	sceGifPkAddGsAD(&packet, SCE_GS_RGBAQ, SCE_GS_SET_RGBAQ(r, g, b, 0, 0));
	sceGifPkAddGsAD(&packet, SCE_GS_XYZ2,
			SCE_GS_SET_XYZ2(0 << 4,0 << 4, 0)); 
	sceGifPkAddGsAD(&packet, SCE_GS_XYZ2,
			SCE_GS_SET_XYZ2( MAX_WIDTH << 4, MAX_HEIGHT*5 << 4, 0)); 
	sceGifPkCloseGifTag(&packet);
	}
	
	sceGifPkTerminate(&packet);
	
	FlushCache(0);
	sceGsSyncPath(0, 0);
	sceDmaSend(dmaGif, (u_long128*)((u_int)packet.pBase));
	sceGsSyncPath(0, 0);
//	free(packetBase);
}

// ////////////////////////////////////////////////////////////////
//
// Set tags for image transfer using path3
//
void setImageTag(u_int *tags, void *image, int index, int image_w, int image_h)
{
  u_int dbp, dbw, dpsm;
  u_int dir, dsax, dsay;
  u_int rrw, rrh;
  u_int xdir;
  int mbx = image_w >> 4;
  int mby = image_h >> 4;
  int i, j;
  Rect tex;
  Rect poly;

  sceGifPacket packet;	
  const u_long giftag[2] = { SCE_GIF_SET_TAG(0, 0, 0, 0, 0, 1), 
			     0x000000000000000eL };
  const u_long giftag_eop[2] = { SCE_GIF_SET_TAG(0, 1, 0, 0, 0, 1), 
				 0x000000000000000eL };
  sceGifPkInit(&packet, (u_long128*)UncAddr(tags));
  sceGifPkReset(&packet);
  
  if (index == 0) {  // set image data to packet

    //  set params
    dbp = (bound(MAX_WIDTH, 64) * bound((MAX_HEIGHT/2), 32) * 2)/64;
    dbw = bound(MAX_WIDTH, 64)/64;
    dpsm = SCE_GS_PSMCT32;

    dir = 0;
    dsax = 0;
    dsay = 0;

    rrw = 16;
    rrh = 16;

    xdir = 0;

    sceGifPkCnt(&packet, 0, 0, 0);

    // eop == 0
    {    
      sceGifPkOpenGifTag(&packet, *(u_long128*)&giftag);
      sceGifPkAddGsAD(&packet, SCE_GS_BITBLTBUF,
		      SCE_GS_SET_BITBLTBUF(0, 0, 0, dbp, dbw, dpsm));
      sceGifPkAddGsAD(&packet, SCE_GS_TRXREG, SCE_GS_SET_TRXREG(rrw, rrh));
      sceGifPkCloseGifTag(&packet);
    }

    // //////////////////////////////////////
    // 
    //  create packet for image data
    // 
    for (i = 0; i < mbx; i++) {
      for (j = 0; j < mby; j++) {

	sceGifPkCnt(&packet, 0, 0, 0);

	// eop == 0
	{
	  sceGifPkOpenGifTag(&packet, *(u_long128*)giftag);
	  sceGifPkAddGsAD(&packet, SCE_GS_TRXPOS,
			  SCE_GS_SET_TRXPOS(0, 0, 16*i+dsax, 16*j+dsay, 0));
	  sceGifPkAddGsAD(&packet, SCE_GS_TRXDIR, SCE_GS_SET_TRXDIR(xdir));
	  sceGifPkCloseGifTag(&packet);
	}
	
	{
	  u_long* const tag = (u_long*)sceGifPkReserve(&packet, 4);
	  tag[0] = SCE_GIF_SET_TAG(16*16*4/16, 0, 0, 0, 2, 0);
		tag[1] = 0;
	}
	sceGifPkRef(&packet, (u_long128 *) DmaAddr(image), 16*16*4/16, 0, 0, 0);
	image = (u_char*)image + 16*16*4;
      }
    }
  }

  tex.x = 8; // 0.5
  tex.y = 8; // 0.5
  tex.w = image_w << 4;
  tex.h = image_h << 4;
  
	if (Config::PAL())
	{
		poly.x = (2048 - 320) << 4;
		poly.y = (2048 - (512/2)/2) << 4;
		poly.w = 640 << 4;
		poly.h = (512/2) << 4;
	}  
	else
	{
		poly.x = (2048 - 320) << 4;
		poly.y = (2048 - (480/2)/2) << 4;
		poly.w = 640 << 4;
		poly.h = (480/2) << 4;
	}  
  // --------------------------------
  sceGifPkEnd(&packet, 0, 0, 0);

  // eop == 1
  {
    sceGifPkOpenGifTag(&packet, *(u_long128*)giftag_eop);
    sceGifPkAddGsAD(&packet, SCE_GS_TEXFLUSH, 0);
    sceGifPkAddGsAD(&packet, SCE_GS_TEX1_1,
		    SCE_GS_SET_TEX1_1(0, 0, 1, 1, 0, 0, 0));
    sceGifPkAddGsAD(&packet, SCE_GS_TEX0_1,
		    SCE_GS_SET_TEX0_1((bound(MAX_WIDTH, 64) 
				       * bound((MAX_HEIGHT/2), 32) * 2)/64,
				      bound(MAX_WIDTH, 64)/64, SCE_GS_PSMCT32, 
				      10, 10, 0, 1, 0, 0, 0, 0, 0));
    sceGifPkAddGsAD(&packet, SCE_GS_PRIM,
		    SCE_GS_SET_PRIM(6, 0, 1, 0, 0, 0, 1, 0, 0));
    sceGifPkAddGsAD(&packet, SCE_GS_UV, SCE_GS_SET_UV(tex.x, tex.y)); 
    sceGifPkAddGsAD(&packet, SCE_GS_XYZ2, SCE_GS_SET_XYZ2(poly.x, poly.y, 0)); 
    sceGifPkAddGsAD(&packet, SCE_GS_UV,
		    SCE_GS_SET_UV(tex.x + tex.w, tex.y + tex.h)); 
    sceGifPkAddGsAD(&packet, SCE_GS_XYZ2, 
		    SCE_GS_SET_XYZ2(poly.x + poly.w, poly.y + poly.h, 0)); 
    sceGifPkCloseGifTag(&packet);
  }

  // finish making packet
  sceGifPkTerminate(&packet);
}

// /////////////////////////////////////////////////////////////////////
//
// vblank handler
//
int vblankHandler(int val)
{
	
	
	CHECK_MOVIE_MEM;
	
	sceDmaChan* dmaGif_loadimage = sceDmaGetChan(SCE_DMA_GIF);
	oddeven = ((*GS_CSR) >> 13) & 1; // odd == 1, even == 0

	if (isCountVblank)
	{

		VoTag *tag;
		vblankCount++;
		handler_error = sceGsSyncPath(1, 0);
		if (!handler_error)
		{ // no error
			tag = voBufGetTag(& MOVIE_MEM_PTR voBuf);
			if (!tag)
			{
				frd++;
				ExitHandler();
				return 0;
			}
			
			sceGsSetHalfOffset((oddeven&1)?(sceGsDrawEnv1*)UncAddr(&gDB.draw1)
				:(sceGsDrawEnv1*)UncAddr(&gDB.draw0), 2048, 2048, oddeven^0x1);
			
			if ((oddeven == 0) && (tag->status == VOBUF_STATUS_FULL))
			{
				sceGsSwapDBuff(&gDB, 0);
				
				// Load image data to GS using path3
				sceGsSyncPath(0, 0);
				sceDmaSend(dmaGif_loadimage, (u_long128*)((u_int)tag->v[0]));
				
				tag->status = VOBUF_STATUS_TOPDONE;
				
			}
			else if ((oddeven == 1) && tag->status == VOBUF_STATUS_TOPDONE)
			{
				sceGsSwapDBuff(&gDB, 1);
				
				// Load image data to GS using path3
				sceGsSyncPath(0, 0);
				sceDmaSend(dmaGif_loadimage, (u_long128*)((u_int)tag->v[1]));
				tag->status = VOBUF_STATUS_;
				
				isFrameEnd = 1;
			}
		}
	}
	ExitHandler();
	return 0;
}

// ///////////////////////////////////////////////////////////////
// 
//  Handler to check the end of image transfer
// 
int handler_endimage(int val)
{
  if (isFrameEnd) {
    voBufDecCount(& MOVIE_MEM_PTR voBuf);
    isFrameEnd = 0;
  }
  ExitHandler();
  return 0;
}

// ///////////////////////////////////////////////////////////////////
// 
//  Wait until even/odd field
//  Start to count vblank
// 
void startDisplay(int waitEven)
{
  // wait untill even field
  while (sceGsSyncV(0) == waitEven)
    ;
  
  frd = 0;
  isCountVblank = 1;
  vblankCount = 0;
}

// ///////////////////////////////////////////////////////////////////
// 
//  Stop to count vblank
// 
void endDisplay()
{
  isCountVblank =  0;
  frd = 0;
}

} // namespace Flx


================================================
FILE: Code/Gel/Movies/Ngps/disp.h
================================================
#ifndef _DISP_H_
#define _DISP_H_

#include 

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

namespace Flx
{



// ////////////////////////////////////////////////////////////////
//
// Functions
//
void clearGsMem(int r, int g, int b, int disp_width, int disp_height);
void setImageTag(u_int *tags, void *image, int index,
		 int image_w, int image_h);
void startDisplay(int waitEven);
void endDisplay();

} // namespace Flx

#endif _DISP_H_


================================================
FILE: Code/Gel/Movies/Ngps/p_movies.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "gfx/gfxman.h"

#include "gfx/ngps/nx/nx_init.h"
#include "gfx/ngps/nx/pcrtc.h"
#include "gfx/ngps/nx/resource.h"

#include "gel/music/ngps/p_music.h"

#include "gel/movies/movies.h"
#include "gel/movies/ngps/p_movies.h"
#include "gel/movies/ngps/defs.h"
#include "gel/movies/ngps/videodec.h"
#include "gel/movies/ngps/disp.h"
#include "gel/movies/ngps/readbuf.h"
#include "gel/movies/ngps/vobuf.h"
#include "gel/movies/ngps/audiodec.h"
#include "gel/movies/ngps/strfile.h"
#include "sys/timer.h"

#include 
#include 
#include 
#include 

// K: To switch this off, make it 0 rather than undefining it, cos it won't compile otherwise
#define FORCE_MOVIES_FROM_CD_PLEASE 0

int videoDecTh;
int defaultTh;
int frd;
u_int controller_val;
int isWithAudio = 1;

bool hitStart = false;
uint64 hitStartFrame;

sceGsDBuff gDB;

namespace Flx
{



static bool initAll( const char *bsfilename );
static void termAll();
static void defMain(void *dummy);
static u_int movie( const char *name );
static int readMpeg(VideoDec *vd, ReadBuf *rb, StrFile *file);
static int isAudioOK();

void dispMain(void);
void videoDecMain( void *dummy);
int videoCallback(sceMpeg *mp, sceMpegCbDataStr *str, void *data);
int pcmCallback(sceMpeg *mp, sceMpegCbDataStr *str, void *data);
int vblankHandler(int);
int handler_endimage(int);
void loadModule(char *moduleName);

#define NUM_VIDEO_BUFFERS	3
#define VRAM_TOTAL_SIZE		( 1024 * 4096 ) // 4 megs of video ram
#define MAX_PIXEL_DATA_SIZE	( 32767 * 16 )

#define ZBUF_ADDR(w, h)		( (((w) + 63) / 64) * (((h) + 31) / 32) * 2)

SMovieMem *gpMovieMem = NULL;

int GetThreadPriority( )
{
	struct ThreadParam info;
	// Fill info with zeros to be sure the following call is doing
	// something to info
	memset(&info,0,sizeof(info));
	// return value not defined in 2.0 PDF EE lib ref
	ReferThreadStatus( GetThreadId(), &info );
	return ( info.currentPriority );
}

// ////////////////////////////////////////////////////////////////
//
// Play Movie
//

#define RESTORE_VRAM 1
#define PLAY_MOVIE_PLEASE 1
#define SAVE_SCRATCHPAD 0
#define SCRATCHPAD_SIZE	8192

void PMovies_PlayMovie( const char *pName )
{
	// Re-enabled for CD even though we don't have a Sony fix.
//	// Garrett: Disabled CD movies until I know the sceCdSt() calls are working again in 2.7.2
//	if (Config::CD() || FORCE_MOVIES_FROM_CD_PLEASE)
//	{
//		return;
//	}
	
	int origThreadPriority;
//	int origArenaSize;
	int width=0;
	int height=0;
//	int origDepth;
//	char *pOrigVram;
//	char *pNonAllignedOrigVram;

	//Dbg_Message("*************** Movie: In render frame %d", Tmr::GetRenderFrame());

	// Check to see if we need to skip movies
	if (hitStart && (hitStartFrame == Tmr::GetRenderFrame()))
	{
		return;
	}
	
	// stop music and streams (we're gonna be using the IOP memory)
	Pcm::StopMusic( );
	Pcm::StopStreams( );

	// Suspend Nx engine and its interrupts
	NxPs2::SuspendEngine();
		

	if (Config::PAL())
	{
		width = 640;
		height = 512;
	} else {
		width = 640;
		height = 480;
	}

	// clear vram onscreen:
	if (Config::PAL())
	{
		clearGsMem(0x00, 0x00, 0x00, 640, ( height * width * 2 ) / 640 );
	}
	else
	{
		clearGsMem(0x00, 0x00, 0x00, width, height * 2);
	}

	// get current thread priority:
	origThreadPriority = GetThreadPriority( );
    ChangeThreadPriority(GetThreadId(), MOVIE_THREAD_PRIORITY );
	

#if PLAY_MOVIE_PLEASE
    isWithAudio = 1; // withAudio;
    
	Dbg_MsgAssert( pName, ( "No movie name specified" ) );

	Dbg_MsgAssert( !gpMovieMem, ( "Movie memory already allocated!" ) );
									  
	// Allocate memory on high heap
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

	void *pNonAllignedMovieMem = (void *) Mem::Malloc( sizeof( SMovieMem ) + 64 );
	
	if ( !pNonAllignedMovieMem )
	{
		Dbg_MsgAssert( 0, ( "Ran out of memory... can't play movie %s.", pName ) );
		return;
	}
	gpMovieMem = ( SMovieMem * )( ( ( int )pNonAllignedMovieMem ) + ( 64 - ( ( ( int ) pNonAllignedMovieMem ) & 63 ) ) );

	Dbg_MsgAssert( !( ( ( int )& MOVIE_MEM_PTR voBufData & 63 ) ||
		( ( int )& MOVIE_MEM_PTR voBufTag & 63 ) ||
        ( ( int )& MOVIE_MEM_PTR viBufTag & 63 ) ||
		( ( int )& MOVIE_MEM_PTR mpegWork & 63 ) ||
		( ( int )& MOVIE_MEM_PTR defStack & 63 ) ||
		( ( int )& MOVIE_MEM_PTR audioBuff & 63 ) ||
		( ( int )& MOVIE_MEM_PTR viBufData & 63 ) ||
		( ( int )& MOVIE_MEM_PTR videoDecStack & 63 ) ||
		( ( int )& MOVIE_MEM_PTR timeStamp & 63 ) ||
//		( ( int )& MOVIE_MEM_PTR controller_dma_buf & 63 ) ||
		( ( int )& MOVIE_MEM_PTR readBuf & 63 ) ||
        ( ( int )& MOVIE_MEM_PTR infile & 63 ) ||
		( ( int )& MOVIE_MEM_PTR videoDec & 63 ) ||
		( ( int )& MOVIE_MEM_PTR audioDec & 63 ) ||
		( ( int )& MOVIE_MEM_PTR voBuf & 63 ) ), ( "Bad allignment in SMovieMem structure." ) );
/*
	Dbg_Message( "Allignment: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", ( int )& MOVIE_MEM_PTR voBufData & 63, ( int )& MOVIE_MEM_PTR voBufTag & 63,
	 ( int )& MOVIE_MEM_PTR viBufTag & 63, ( int )& MOVIE_MEM_PTR mpegWork & 63, ( int )& MOVIE_MEM_PTR defStack & 63, ( int )& MOVIE_MEM_PTR audioBuff & 63, ( int )& MOVIE_MEM_PTR viBufData & 63,
	 ( int )& MOVIE_MEM_PTR videoDecStack & 63, ( int )& MOVIE_MEM_PTR timeStamp & 63, ( int )& MOVIE_MEM_PTR controller_dma_buf & 63, ( int )& MOVIE_MEM_PTR readBuf & 63,
	 ( int )& MOVIE_MEM_PTR infile & 63, ( int )& MOVIE_MEM_PTR videoDec & 63, ( int )& MOVIE_MEM_PTR audioDec & 63, ( int )& MOVIE_MEM_PTR voBuf & 63 );
*/
	Dbg_Message( "Size of SMovieMem struct: %d k", sizeof( SMovieMem ) / 1024 );


#if SAVE_SCRATCHPAD
	// remember scratchpad:
	unsigned char *pScratchpad = ( unsigned char * )Mem::Malloc( SCRATCHPAD_SIZE );
	Dbg_MsgAssert( pScratchpad, ( "Ran out of memory allocating scratchpad." ) );
	int i;
	for ( i = 0; i < SCRATCHPAD_SIZE; i++ )
	{
		pScratchpad[ i ] = ( ( unsigned char * ) 0x70000000 )[ i ];
	}
#else
	// Make sure scratchpad is available
	bool got_scratch = NxPs2::CSystemResources::sRequestResource(NxPs2::CSystemResources::vSCRATCHPAD_MEMORY);
	Dbg_Assert(got_scratch);
#endif // SAVE_SCRATCHPAD

	// Pop top-down context
	Mem::Manager::sHandle().PopContext();

	sceGsResetPath();
    sceDmaReset(1);

	NxPs2::SetupPCRTC(1, SCE_GS_FRAME);

	// Old way of initializing.  We can't use sceGsResetGraph() anymore.  It interferes with SetupPCRTC().
	// Also, clearGsMem() did nothing before because it used gDB, which you can see was not initialized until
	// after the calls.
#if 0  
		//clearGsMem(0x00, 0x00, 0x00, 640, 480);
		//VSync( );
		
		//sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FRAME);

		//  Initialize GS memory
		//clearGsMem(0x00, 0x00, 0x00, 640, 480);
#endif
	
	sceGsSetDefDBuff(&gDB, SCE_GS_PSMCT32, width, (height/2),
			 SCE_GS_ZNOUSE, 0, SCE_GS_CLEAR);

	int zbuf_addr = ZBUF_ADDR(width, (height/2));

	// Garrett: Since sceGsResetGraph() saves some register settings in internal library variables (including FRAME),
	// we have to update some settings in gDB with the correct values.
	gDB.disp[0].smode2.FFMD = SCE_GS_FRAME;
	gDB.disp[1].smode2.FFMD = SCE_GS_FRAME;
	gDB.disp[0].dispfb.FBP	= 0x0;
	gDB.disp[1].dispfb.FBP	= zbuf_addr >> 1;
	gDB.disp[0].display.DH	= height-1;
	gDB.disp[1].display.DH	= height-1;
	gDB.draw0.frame1.FBP	= zbuf_addr >> 1;
	gDB.draw1.frame1.FBP	= 0x0;
	gDB.draw0.zbuf1.ZBP		= zbuf_addr;
	gDB.draw1.zbuf1.ZBP		= zbuf_addr;
				 
    FlushCache(0);

	// Wait for all the music and streams to really stop, since we are stealing their IOP memory
	uint32 target_vblank = Tmr::GetVblanks() + (5 * Config::FPS());		// 5 seconds
	while (Tmr::GetVblanks() < target_vblank)
	{
		Pcm::PCMAudio_Update();

		if (Pcm::PCMAudio_GetMusicStatus() != Pcm::PCM_STATUS_FREE)
			continue;
		
		bool done = true;
		for (int streamNum = 0; streamNum < NUM_STREAMS; streamNum++)
		{
			if (Pcm::PCMAudio_GetStreamStatus( streamNum ) != Pcm::PCM_STATUS_FREE)
				done = false;
		}

		if (done)
		{
			break;
		}
	}

	movie( pName );
    //while ( ( movie( pName ) ) != MOVIE_ABORTED)
    //	;

	// Clear double buffers to black:
	clearGsMem(0x00, 0x00, 0x00, width, height);
	
	
	// wait for any sound transfer to complete and shit...
	if ( sceSdRemote( 1, rSdVoiceTransStatus, AUTODMA_CH, SD_TRANS_STATUS_WAIT ) == -1 )
	{
		Dbg_MsgAssert( 0,( "Complete SPU2 Transfer Failed\n"));
	}

	Mem::Free( pNonAllignedMovieMem );
	gpMovieMem = NULL;
	
#if SAVE_SCRATCHPAD
	// restore scratchpad:
	for ( i = 0; i < SCRATCHPAD_SIZE; i++ )
	{
		( ( unsigned char * ) 0x70000000 )[ i ] = pScratchpad[ i ];
	}
	Mem::Free( pScratchpad );
#else
	if (got_scratch)
	{
		NxPs2::CSystemResources::sFreeResource(NxPs2::CSystemResources::vSCRATCHPAD_MEMORY);
	}
#endif // SAVE_SCRATCHPAD

//	VSync( );
//	while ( Tmr::GetVblanks() & 1 );

#endif // playmovieplease


	ChangeThreadPriority(GetThreadId(), origThreadPriority );

	Tmr::VSync( );

	return;
}

// ////////////////////////////////////////////////////////////////
//
// Decode MPEG bitstream
//
// ret:
//   1: ok
//   0: error
//  -1: abort
static u_int movie( const char *name )
{
	CHECK_MOVIE_MEM;

    static int count = 0;

    printf("========================== decode MPEG2 ============= %d ===========\n", count++);
	
    if (initAll(name ))
	{
		printf( "done w/ initall\n" );	
		readMpeg(& MOVIE_MEM_PTR videoDec, & MOVIE_MEM_PTR readBuf, & MOVIE_MEM_PTR infile);
	}

	termAll();

    return controller_val;
}

// ////////////////////////////////////////////////////////////////
//
// Read MPEG data
//
// return value
//     1: normal end
//     -1: aborted
static int readMpeg(VideoDec *vd, ReadBuf *rb, StrFile *file)
{
	
    u_int ctrlmask =  SCE_PADRdown | SCE_PADstart;
    u_char *put_ptr;
    u_char *get_ptr;
    int putsize;
    int getsize;
    int readrest = file->size;
    int writerest = file->size;
    int count;
    int proceed;
    int isStarted = 0;
    u_int button_old = 0;
    u_int pushed = 0;
    u_char cdata[32];
    int isPaused = 0;

    // writerest > 4: to skip the last 4 bytes
    while (isPaused
    	|| (writerest > 4 && videoDecGetState(vd) != VD_STATE_END)) {

    	// /////////////////////////////////////////////////
	//
	// Get controller information
	//
   	if (scePadRead(0, 0, cdata) > 0) {
	    controller_val = 0xffff ^ ((cdata[2] << 8) | cdata[3]);
	} else {
	    controller_val = 0;
	}
	pushed = (button_old ^ controller_val)
			& controller_val & ctrlmask;
	button_old = controller_val;

	CHECK_MOVIE_MEM;

	if (pushed && vd->mpeg.frameCount > 10)
	{

/*	    if (pushed & SCE_PADRleft) {
	    	if (isPaused) {
		    startDisplay(1);
		    if (isWithAudio) {
			audioDecResume(& MOVIE_MEM_PTR audioDec);
		    }
		} else {
		    endDisplay();
		    if (isWithAudio) {
			audioDecPause(& MOVIE_MEM_PTR audioDec);
		    }
		}
		isPaused ^= 1;
	    } else if (!isPaused) {
*/
		// /////////////////////////////////////////////////
		//
		// Abort decoding
		//
		videoDecAbort(& MOVIE_MEM_PTR videoDec);
	//    }

		// Record frame number if we hit START so we skip all movies
		if (pushed & SCE_PADstart)
		{
			hitStartFrame = Tmr::GetRenderFrame();
			hitStart = true;
		} else {
			hitStart = false;
		}
	}
   	
	// /////////////////////////////////////////////////
	//
	// Read data to the read buffer
	//
        putsize = readBufBeginPut(rb, &put_ptr);
	if (readrest > 0 && putsize >= READ_UNIT_SIZE) {
	    count = strFileRead(file, put_ptr, READ_UNIT_SIZE);
	    readBufEndPut(rb, count);
	    readrest -= count;
	}

	switchThread();

    	// /////////////////////////////////////////////////
	//
	// De-multiplex and put data on video/audio input buffer
	//
	getsize = readBufBeginGet(rb, &get_ptr);
	if (getsize > 0) {

	    proceed = sceMpegDemuxPssRing(&vd->mpeg,
	    		get_ptr, getsize, rb->data, rb->size);

	    readBufEndGet(rb, proceed);
	    writerest -= proceed;

	}

    	// /////////////////////////////////////////////////
	//
	// Send audio data to IOP
	//
	proceedAudio();

    	// /////////////////////////////////////////////////
	//
	// Wait until video and audio output buffer become full
	//
	CHECK_MOVIE_MEM;
	
	if (!isStarted && voBufIsFull(& MOVIE_MEM_PTR voBuf) && isAudioOK()) {

	    startDisplay(1);		// start video
	    if (isWithAudio) {
		audioDecStart(& MOVIE_MEM_PTR audioDec);	// start audio
	    }
	    isStarted = 1;
	}
    }

    // try to flush buffers inside decoder
    while (!videoDecFlush(vd)) {
	switchThread();
    }

    // wait till buffers are flushed
    while (!videoDecIsFlushed(vd)
    	&& videoDecGetState(vd) != VD_STATE_END) {

	switchThread();
    }

    endDisplay();
    if (isWithAudio)
	{
		audioDecReset(& MOVIE_MEM_PTR audioDec);
    }

    return 1;
}

// /////////////////////////////////////////////////
//
// Switch to another thread
//
void switchThread()
{
    RotateThreadReadyQueue( MOVIE_THREAD_PRIORITY );
}

// /////////////////////////////////////////////////
//
// Check audio
//
static int isAudioOK()
{
	
    CHECK_MOVIE_MEM;
	return (isWithAudio)? audioDecIsPreset(& MOVIE_MEM_PTR audioDec): 1;
}

// ////////////////////////////////////////////////////////////////
//
// Initialize all modules
//
static bool initAll( const char *bsfilename )
{
	
    struct ThreadParam th_param;

    *D_CTRL = (*D_CTRL | 0x003);
    *D_STAT = 0x4; // clear D_STAT.CIS2

    // /////////////////////////////
    // 
    //  Create read buffer
    // 
    CHECK_MOVIE_MEM;
	
	readBufCreate(& MOVIE_MEM_PTR readBuf);

    // /////////////////////////////
    // 
    //  Initialize video decoder
    // 
    sceMpegInit();
    videoDecCreate(& MOVIE_MEM_PTR videoDec,
    	 MOVIE_MEM_PTR mpegWork, MPEG_WORK_SIZE,
    	 MOVIE_MEM_PTR viBufData,  MOVIE_MEM_PTR viBufTag, VIBUF_SIZE,  MOVIE_MEM_PTR timeStamp, VIBUF_TS_SIZE);

    // /////////////////////////////
    // 
    //  Initialize audio decoder
    // 
#define NEED_TO_INIT_SD_AND_SHIT 0
#if NEED_TO_INIT_SD_AND_SHIT    
	sceSdRemoteInit();
    sceSdRemote(1, rSdInit, SD_INIT_COLD);  // i think having this in would fuck up soundfx module...
#endif
    audioDecCreate(& MOVIE_MEM_PTR audioDec,  MOVIE_MEM_PTR audioBuff, AUDIO_BUFF_SIZE,
			IOP_BUFF_SIZE, Pcm::PCMAudio_GetIopMemory( ) );

    ///////////////////////////////
    // 
    //  Choose stream to be played
    // 
    videoDecSetStream(& MOVIE_MEM_PTR videoDec,
	    sceMpegStrM2V, 0, (sceMpegCallback)videoCallback, & MOVIE_MEM_PTR readBuf);
    if (isWithAudio) {
	videoDecSetStream(& MOVIE_MEM_PTR videoDec,
	    sceMpegStrPCM, 0, (sceMpegCallback)pcmCallback, & MOVIE_MEM_PTR readBuf);
    }

    // /////////////////////////////
    // 
    //  Initialize video output buffer
    // 
    voBufCreate(& MOVIE_MEM_PTR voBuf, ( VoData * )UncAddr( MOVIE_MEM_PTR voBufData),  MOVIE_MEM_PTR voBufTag, N_VOBUF);

    // /////////////////////////////
    // 
    //  Create 'default' thread
    // 
	Dbg_Message( "Starting default thread" );
    th_param.entry = defMain;
    th_param.stack =  MOVIE_MEM_PTR defStack;
    th_param.stackSize = MOVIE_DEF_STACK_SIZE;
    th_param.initPriority = MOVIE_THREAD_PRIORITY;
    th_param.gpReg = &_gp;
    th_param.option = 0;
    defaultTh = CreateThread(&th_param);
    StartThread(defaultTh, NULL);
    
	// /////////////////////////////
    // 
    //  Create docode thread
    // 
	Dbg_Message( "Starting video decoder thread" );
    th_param.entry = videoDecMain;
    th_param.stack =  MOVIE_MEM_PTR videoDecStack;
    th_param.stackSize = MOVIE_STACK_SIZE;
    th_param.initPriority = MOVIE_THREAD_PRIORITY;
    th_param.gpReg = &_gp;
    th_param.option = 0;
    videoDecTh = CreateThread(&th_param);
    StartThread(videoDecTh, NULL );

    // /////////////////////////////
    // 
    //  Initialize controller
    // 

/*	static int isFirst = 1;
	if (isFirst)
	{
	    scePadInit(0);
	    scePadPortOpen(0, 0,  MOVIE_MEM_PTR controller_dma_buf);
	    isFirst = 0;
	}*/

	char filename[ 256 ];
	if (Config::CD() || FORCE_MOVIES_FROM_CD_PLEASE)
	{
		sprintf( filename, "cdrom0:\\%s%s.PSS;1",Config::GetDirectory(), bsfilename );
		unsigned int schraw;
		for ( schraw = 7; schraw < strlen( filename ); schraw++ ) // start after "cdrom0:\"
		{
			if ( filename[ schraw ] >= 'a' && filename[ schraw ] <= 'z' )
			{
				filename[ schraw ] += 'A' - 'a';
			}
			else if ( filename[ schraw ] == '/' )
			{
				filename[ schraw ] = '\\';
			}
		}
	}	
	else
	{
		sprintf( filename, "host0:\\%s.pss", bsfilename );
	}	
	
    // /////////////////////////////
    // 
    //  Open bitstream file
    // 
	File::StopStreaming( );
	if ( Pcm::UsingCD( ) )
	{
		Dbg_MsgAssert( 0,( "Can't load IRX modules when CD is busy." ));
		return false;
	}
    
	bool	loaded = false;
	for (int i = 0;i <10; i++)
	{
		printf ("Attempt %d\n",i);
		if (!strFileOpen(& MOVIE_MEM_PTR infile, filename,
			Pcm::PCMAudio_GetIopMemory( ) + IOP_BUFF_SIZE + ( 2048 * STRFILE_NUM_STREAMING_SECTORS + 64 ) ))			 
		{
			printf ("Can't Open file %s\n", filename);
		}
		else
		{
			loaded = true;
			break;
		}
	}
	
    // /////////////////////////////
    // 
    //  Set Interrupt handlers
    // 
	MOVIE_MEM_PTR videoDec.hid_vblank = AddIntcHandler(INTC_VBLANK_S, vblankHandler, 0);
	EnableIntc(INTC_VBLANK_S);

	MOVIE_MEM_PTR videoDec.hid_endimage = AddDmacHandler(DMAC_GIF, handler_endimage, 0);
	EnableDmac(DMAC_GIF);
	
	return loaded;
}

// ////////////////////////////////////////////////////////////////
//
// Terminate all modules
//
static void termAll()
{
	
	
	CHECK_MOVIE_MEM;

    TerminateThread(videoDecTh);
    DeleteThread(videoDecTh);

    TerminateThread(defaultTh);
    DeleteThread(defaultTh);

    DisableDmac(DMAC_GIF);
    RemoveDmacHandler(DMAC_GIF,  MOVIE_MEM_PTR videoDec.hid_endimage);
	//EnableDmac(DMAC_GIF);

    DisableIntc(INTC_VBLANK_S);
    RemoveIntcHandler(INTC_VBLANK_S,  MOVIE_MEM_PTR videoDec.hid_vblank);
	EnableIntc(INTC_VBLANK_S);
    
	readBufDelete(& MOVIE_MEM_PTR readBuf);
    voBufDelete(& MOVIE_MEM_PTR voBuf);

    videoDecDelete(& MOVIE_MEM_PTR videoDec);
    audioDecDelete(& MOVIE_MEM_PTR audioDec);

    strFileClose(& MOVIE_MEM_PTR infile);

	// Re-init nx engine
	NxPs2::ResetEngine();

	// Re-init quick filesystem (since the CD stream buffer has changed)
	if (Config::CD())
	{
		File::ResetQuickFileSystem();
	}
}

// ////////////////////////////////////////////////////////////////
//
// Main function of default thread
//
static void defMain(void * dummy)
{
    while (1) {
	switchThread();
    }
}

// ////////////////////////////////////////////////////////////////
//
// Print error message
//
void ErrMessage(char *message)
{
    printf("[ Error ] %s\n", message);
}

// ////////////////////////////////////////////////////////////////
//
//  Send audio data to IOP
//
void proceedAudio()
{
	
    
	CHECK_MOVIE_MEM;
	
	audioDecSendToIOP(& MOVIE_MEM_PTR audioDec);
}



} // namespace Flx



================================================
FILE: Code/Gel/Movies/Ngps/p_movies.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			ps2 movies												**
**																			**
**	File name:		gel/movies/ngps/p_movies.h 								**
**																			**
**	Created: 		5/14/01	-	mjd											**
**																			**
*****************************************************************************/

#ifndef __P_MOVIES_H
#define __P_MOVIES_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#include 
#include 
#include 

#include "gel/movies/ngps/defs.h"
#include "gel/movies/ngps/vobuf.h"
#include "gel/movies/ngps/vibuf.h"
#include "gel/movies/ngps/strfile.h"
#include "gel/movies/ngps/readbuf.h"
#include "gel/movies/ngps/videodec.h"
#include "gel/movies/ngps/audiodec.h"

namespace Flx
{



#define MOVIE_STACK_SIZE    (16*1024)
#define MOVIE_DEF_STACK_SIZE    2048
#define MAX_MBX		(MAX_WIDTH/16)
#define MAX_MBY		(MAX_HEIGHT/16)
#define IOP_BUFF_SIZE (12288*2) // 512 * 48
#define MOVIE_ABORTED SCE_PADRdown
//#define DEF_PRIORITY       32
#define ERR_STOP while(1)

#define MPEG_WORK_SIZE ( SCE_MPEG_BUFFER_SIZE(MAX_WIDTH, MAX_HEIGHT) )
#define AUDIO_BUFF_SIZE ( IOP_BUFF_SIZE * 2 )

#define ZERO_BUFF_SIZE	0x800
	  
// send in size, returns the size needed to create a 64-byte boundary:
#define PAD_FOR_64( x ) ( 64 - ( ( x ) & 63 ) )
// send in size, returns the size to the next 64 byte boundary:
#define PADDED_64( x ) ( ( x ) + PAD_FOR_64( x ) )

// This structure contains everything that was previously just
// global (and taking up loads of memory)...
struct SMovieMem{

	u_long128 packetBase[ 6 ];
	char pad665[ PAD_FOR_64( sizeof( u_long128 ) * 6 ) ];
		 
	char _0_buf [ ZERO_BUFF_SIZE ];
	char pad666[ PAD_FOR_64( ZERO_BUFF_SIZE ) ];
	
	// ******* NOTE ::: KEEP ALL THESE ON 64 - BYTE BOUNDARIES AND SHIT!! ********

	// These variables could be accessed from Uncached Area
	VoData voBufData[ N_VOBUF ];
	char pad0[ PAD_FOR_64( ( sizeof( VoData ) * N_VOBUF ) ) ];

	VoTag voBufTag[ N_VOBUF ];
	char pad1[ PAD_FOR_64( ( sizeof( VoTag ) * N_VOBUF ) ) ];

	u_long128 viBufTag[ VIBUF_SIZE + 1 ];
	char pad2[ PAD_FOR_64( ( sizeof( u_long128 ) * ( VIBUF_SIZE + 1 ) ) ) ];

	// -------------- this needs to be 64 byte boudary -------------------
	
	// These variables are NOT accessed from Uncached Area
	u_char mpegWork[ PADDED_64( MPEG_WORK_SIZE ) ];
	char defStack[ PADDED_64( MOVIE_DEF_STACK_SIZE )];
	u_char audioBuff[PADDED_64( IOP_BUFF_SIZE*2 )];
	u_long128 viBufData[ PADDED_64( VIBUF_SIZE * VIBUF_ELM_SIZE/16 ) ];
	char videoDecStack[ PADDED_64 ( MOVIE_STACK_SIZE )];
	TimeStamp timeStamp[ VIBUF_TS_SIZE ];
	char pad3[ PAD_FOR_64( sizeof( TimeStamp ) * VIBUF_TS_SIZE ) ];

//	u_long128 controller_dma_buf[ scePadDmaBufferMax ];
//	char pad4[ PAD_FOR_64( ( sizeof( u_long128 ) * ( scePadDmaBufferMax ) ) ) ];

	struct ReadBuf readBuf;
	char pad5[ PAD_FOR_64( sizeof( ReadBuf ) ) ];
	
	struct StrFile infile;
	char pad6[ PAD_FOR_64( sizeof( StrFile ) ) ];
	struct VideoDec videoDec;
	char pad7[ PAD_FOR_64( sizeof( VideoDec ) ) ];
	struct AudioDec audioDec;
	char pad8[ PAD_FOR_64( sizeof( AudioDec ) ) ];
	struct VoBuf voBuf;
};

extern SMovieMem *gpMovieMem;

#define MOVIE_MEM_PTR	gpMovieMem->
#define CHECK_MOVIE_MEM	Dbg_MsgAssert( gpMovieMem, ( "Movie Memory not initialized." ) )

void PMovies_PlayMovie( const char *pName );

void FuckUpVram( char color ); // testing what the fuck vram funcs are doing...

} // namespace Flx

#endif	// __P_MOVIES_H





================================================
FILE: Code/Gel/Movies/Ngps/read.cpp
================================================
#include 
#include 
#include 
#include 
#include "gel/movies/ngps/p_movies.h"
#include "gel/movies/ngps/readbuf.h"
#include "gel/movies/ngps/videodec.h"
#include "gel/movies/ngps/defs.h"
#include "gel/movies/ngps/audiodec.h"

namespace Flx
{



static int copy2area(u_char *pd0, int d0, u_char *pd1, int d1,
    u_char *ps0, int s0, u_char *ps1, int s1);

// ////////////////////////////////////////////////////////////////
//
// Stream callback function for MPEG2 video stream
//
int videoCallback(sceMpeg *mp, sceMpegCbDataStr *str, void *data)
{
	
    
	CHECK_MOVIE_MEM;
	
	ReadBuf *rb = (ReadBuf*)data;
    u_char *ps0 = str->data;
    u_char *ps1 = rb->data;
    int s0 = min( ( int )rb->data + ( int )rb->size - ( int )str->data, ( int )str->len);
    int s1 = str->len - s0;
    u_char *pd0;
    u_char *pd1;
    u_char *pd0Unc;
    u_char *pd1Unc;
    int d0, d1;
    int len;

    videoDecBeginPut(& MOVIE_MEM_PTR videoDec, &pd0, &d0, &pd1, &d1);
    pd0Unc = (u_char*)UncAddr(pd0);
    pd1Unc = (u_char*)UncAddr(pd1);

    len = copy2area(pd0Unc, d0, pd1Unc, d1, ps0, s0, ps1, s1);

    // set PTS
    if (len > 0) {
	if (!videoDecPutTs(& MOVIE_MEM_PTR videoDec, str->pts, str->dts, pd0, len)) {
	    ErrMessage("pts buffer overflow\n");
	}
    }

    videoDecEndPut(& MOVIE_MEM_PTR videoDec, len);

    // ////////////////////////////////////////////
    //
    // Return 0 if no data is put
    //
    return (len > 0)? 1: 0;
}

// ////////////////////////////////////////////////////////////////
//
// Stream callback function for PS2 PCM stream
//
int pcmCallback(sceMpeg *mp, sceMpegCbDataStr *str, void *data)
{
    ReadBuf *rb = (ReadBuf*)data;
    u_char *ps0 = str->data;
    u_char *ps1 = rb->data;
    int s0;
    int s1;
    u_char *pd0;
    u_char *pd1;
    int d0, d1;
    int len;
    int ret;

    // skip
    // sub_stream_id
    ps0 = str->data + 4;
    if (ps0 >= rb->data + rb->size) {
    	ps0 -= rb->size;
    }
    len = str->len - 4;

    ps1 = rb->data;
    s0 = min(rb->data + rb->size - ps0, len);
    s1 = len - s0;

    audioDecBeginPut(& MOVIE_MEM_PTR audioDec, &pd0, &d0, &pd1, &d1);
    ret = copy2area(pd0, d0, pd1, d1, ps0, s0, ps1, s1);

    audioDecEndPut(& MOVIE_MEM_PTR audioDec, ret);

    // ////////////////////////////////////////////
    //
    // Return 0 if no data is put
    //
    return (ret > 0)? 1: 0;
}

// ////////////////////////////////////////////////////////////////
//
// Copy two areas
//
static int copy2area(u_char *pd0, int d0, u_char *pd1, int d1,
    u_char *ps0, int s0, u_char *ps1, int s1)
{
    if (d0 + d1 < s0 + s1) {
        return 0;
    }

    if (s0 >= d0) {
    	memcpy(pd0,		ps0,		d0);
    	memcpy(pd1,		ps0 + d0,	s0 - d0);
    	memcpy(pd1 + s0 - d0,	ps1,		s1);
    } else { // s0 < d0
    	if (s1 >= d0 - s0) {
	    memcpy(pd0,		ps0,		s0);
	    memcpy(pd0 + s0,	ps1,		d0 - s0);
	    memcpy(pd1,		ps1 + d0 - s0,	s1 - (d0 - s0));
	} else { // s1 < d0 - s0
	    memcpy(pd0,		ps0,		s0);
	    memcpy(pd0 + s0,	ps1,		s1);
	}
    }
    return s0 + s1;
}

} // namespace Flx


================================================
FILE: Code/Gel/Movies/Ngps/readbuf.cpp
================================================
#include "gel/movies/ngps/defs.h"
#include "gel/movies/ngps/readbuf.h"

#define BUFF_SIZE (N_READ_UNIT * READ_UNIT_SIZE)

namespace Flx
{



// ////////////////////////////////////////////////////////////////
//
// Create read buffer
//
void readBufCreate(ReadBuf *b)
{
    b->put = b->count = 0;
    b->size = BUFF_SIZE;
}

// ////////////////////////////////////////////////////////////////
//
// Delete read buffer
//
void readBufDelete(ReadBuf *b)
{
}

// ////////////////////////////////////////////////////////////////
//
// Get empty area
//
int readBufBeginPut(ReadBuf *b, u_char **ptr)
{
    int size = b->size - b->count;
    if (size) {
        *ptr = b->data + b->put;
    }
    return size;
}

// ////////////////////////////////////////////////////////////////
//
// Proceed 'write' pointer
//
int readBufEndPut(ReadBuf *b, int size)
{
    int size_ok = min(b->size - b->count, size);

    b->put = (b->put + size_ok) % b->size;
    b->count += size_ok;

    return size_ok;
}

// ////////////////////////////////////////////////////////////////
//
// Get data area
//
int readBufBeginGet(ReadBuf *b, u_char **ptr)
{
    if (b->count) {
        *ptr = b->data + (b->put - b->count + b->size) % b->size;
    }
    return b->count;
}

// ////////////////////////////////////////////////////////////////
//
// Proceed 'read' pointer
//
int readBufEndGet(ReadBuf *b, int size)
{
    int size_ok = min(b->count, size);

    b->count -= size_ok;

    return size_ok;
}

} // namespace Flx


================================================
FILE: Code/Gel/Movies/Ngps/readbuf.h
================================================
#ifndef _READBUF_H_
#define _READBUF_H_

#include 

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 


#define READ_UNIT_SIZE (64*1024)
#define N_READ_UNIT     5

// ////////////////////////////////////////////////////////////////
//
// Read buffer
//
struct ReadBuf{
    u_char data[N_READ_UNIT * READ_UNIT_SIZE];
    int put;
    int count;
    int size;
};

namespace Flx
{



// ////////////////////////////////////////////////////////////////
//
// Functions
//
void readBufCreate(ReadBuf *buff);
void readBufDelete(ReadBuf *buff);
int readBufBeginPut(ReadBuf *buff, u_char **ptr);
int readBufEndPut(ReadBuf *buff, int size);
int readBufBeginGet(ReadBuf *buff, u_char **ptr);
int readBufEndGet(ReadBuf *buff, int size);

} // namespace Flx

#endif // _READBUF_H_



================================================
FILE: Code/Gel/Movies/Ngps/strfile.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include "gel/movies/ngps/defs.h"
#include "gel/movies/ngps/strfile.h"
#include 

namespace Flx
{



int isStrFileInit = 0;

// ////////////////////////////////////////////////////////////////
//
//  Open a file to read and check its size
//
//	< file name conversion >
//	   = from HD =
//	    dir/file.pss           -> host0:dir/file.pss
//	    host0:dir/file.pss     -> host0:dir/file.pss
//
//	   = from CD/DVD =
//	    cdrom0:\dir\file.pss;1 -> cdrom0:\DIR\FILE.PSS;1
//	    cdrom0:/dir/file.pss;1 -> cdrom0:\DIR\FILE.PSS;1
//	    cdrom0:/dir/file.pss   -> cdrom0:\DIR\FILE.PSS;1
//
int strFileOpen(StrFile *file, char *filename, int pIopBuff )
{
    int ret;
    char *body = NULL;
    char fn[256];
    char devname[64];

    body = index(filename, ':');

	if (body)
	{
		int dlen;

		// copy device name
		dlen = body - filename;
		strncpy(devname, filename, dlen);
		devname[dlen] = 0;

		body += 1;

		if (!strcmp(devname, "cdrom0"))
		{ // CD/DVD
			int i;
			int len = strlen(body);
			const char *tail;

			file->isOnCD = 1;

			for (i = 0; i < len; i++)
			{
				if (body[i] == '/')
				{
					body[i] = '\\';
				}
				body[i] = toupper(body[i]);
			}

			tail = (index(filename, ';'))? "": ";1";
			sprintf(fn, "%s%s", body, tail);

		}
		else
		{			 // HD
			file->isOnCD = 0;
			sprintf(fn, "%s:%s", devname, body+1);  // Mick:  The +1 is to skip the initial \, so file names are the same off HD aas CD 
		}
	}
	else
	{				 // HD (default)
		body = filename;            
		strcpy(devname, "host0");
		file->isOnCD = 0;
		sprintf(fn, "%s:%s", devname, body);
	}

    printf("file: %s\n", fn);

    if (file->isOnCD)
	{
		sceCdRMode mode;
		if (!Config::CD())
		{
			if (!isStrFileInit)
			{
				sceCdInit(SCECdINIT);
				sceCdMmode(SCECdDVD);
				sceCdDiskReady(0);
		
				isStrFileInit = 1;
			}
		}	
		file->iopBuf = ( u_char * )pIopBuff; //( u_char * )sceSifAllocIopHeap((2048 * 80) + 16);
		sceCdStInit(80, 5, bound((u_int)file->iopBuf, 16));
	
		if(!sceCdSearchFile(&file->fp, fn)){
	
			printf("Cannot open '%s'(sceCdSearchFile)\n", fn);
			return 0;
		}
	
		file->size = file->fp.size;
		mode.trycount = 0;
		mode.spindlctrl = SCECdSpinStm;
		mode.datapattern = SCECdSecS2048;
		sceCdStStart(file->fp.lsn, &mode);
    }
	else
	{

		file->fd = sceOpen(fn, SCE_RDONLY);
		if (file->fd < 0)
		{
			printf("Cannot open '%s'(sceOpen)\n", fn);
			return 0;
		}
		file->size = sceLseek(file->fd, 0, SCE_SEEK_END);
		if (file->size < 0)
		{
			printf("sceLseek() fails (%s): %d\n", fn, file->size);
			sceClose(file->fd);
			return 0;
		}
	
		ret = sceLseek(file->fd, 0, SCE_SEEK_SET);
		if (ret < 0)
		{
			printf("sceLseek() fails (%s)\n", fn);
			sceClose(file->fd);
			return 0;
		}
    }

    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  Close a file
//
int strFileClose(StrFile *file)
{
    if (file->isOnCD) {
    	sceCdStStop();
//        sceSifFreeIopHeap((void *)file->iopBuf);
    } else {
	sceClose(file->fd);
    }
    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  Read data
//
int strFileRead(StrFile *file, void *buff, int size)
{
    int count;
    if (file->isOnCD) {
	u_int err;

        count= sceCdStRead(size >> 11, (u_int *)buff, STMBLK, &err);
	count <<= 11;

    } else {
	count = sceRead(file->fd, buff, size);
    }
    return count;
}

}


================================================
FILE: Code/Gel/Movies/Ngps/strfile.h
================================================
#ifndef _STRFILE_H_
#define _STRFILE_H_

#include 
#include 

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

// ////////////////////////////////////////////////////////////////
//
//  Structure to read data from CD/DVD or HD
//
struct StrFile{
    int isOnCD;		// CD/DVD or HD
    int size;

    sceCdlFILE fp;	// for CD/DVD stream
    u_char *iopBuf;

    int fd;		// for HD stream
};

namespace Flx
{



int strFileOpen(StrFile *file, char *filename, int pIopBuff);
int strFileClose(StrFile *file);
int strFileRead(StrFile *file, void *buff, int size);

} // namespace Flx

#endif // _STRFILE_H_


================================================
FILE: Code/Gel/Movies/Ngps/vibuf.cpp
================================================
#include 
#include 
#include "gel/movies/ngps/defs.h"
#include "gel/movies/ngps/vibuf.h"
#include "gel/movies/ngps/p_movies.h"

// ////////////////////////////////////////////////////////////////
//
// Definitions 
//
#define DMA_ID_REFE	0
#define DMA_ID_NEXT	2
#define DMA_ID_REF	3

#define WAITSEMA(v) WaitSema(v)
#define SIGNALSEMA(v) SignalSema(v)

#define REST	2

#define TAG_ADDR(i)	((u_int)DmaAddr(f->tag + i))
#define DATA_ADDR(i)	((u_int)f->data + VIBUF_ELM_SIZE * (i))
#define WRAP_ADDR(addr) ((u_int)(f->data)     + (((u_int)(addr) - (u_int)(f->data)) % (VIBUF_ELM_SIZE * f->n)))

#define IsInRegion(i,start,len,n)  (     (0 <= (((i) + (n) - (start)) % (n))) &&     ((((i) + (n) - (start)) % (n)) < (len)))

#define FS(f)	((f->dmaStart + f->dmaN) * VIBUF_ELM_SIZE)
#define FN(f)	((f->n - REST -  f->dmaN) * VIBUF_ELM_SIZE)

namespace Flx
{




// ////////////////////////////////////////////////////////////////
//
// definition of QWORD
//
typedef union {
    u_long128	q;
    u_long 	l[2];
    u_int  	i[4];
    u_short	s[8];
    u_char	c[16];
} QWORD;

extern inline int IsPtsInRegion(int tgt, int pos, int len, int size)
{
    int tgt1 = (tgt + size - pos) % size;
    return tgt1 < len;
}

int getFIFOindex(ViBuf *f, void *addr)
{
    if (addr == DmaAddr(f->tag + (f->n + 1))) {
	return 0;
    } else {
	return ((u_int)addr - (u_int)f->data) / VIBUF_ELM_SIZE;
    }
}

void setD3_CHCR(u_int val)
{
    DI();
    *D_ENABLEW = ((*D_ENABLER)|0x00010000);	// pause all channels
    *D3_CHCR = val;
    *D_ENABLEW = ((*D_ENABLER)&~0x00010000);	// restart all channels
    EI();
}

void setD4_CHCR(u_int val)
{
    DI();
    *D_ENABLEW = ((*D_ENABLER)|0x00010000);	// pause all channels
    *D4_CHCR = val;
    *D_ENABLEW = ((*D_ENABLER)&~0x00010000);	// restart all channels
    EI();
}

void scTag2(
    QWORD *q,
    void *addr,
    u_int id,
    u_int qwc
)
{
    q->l[0] =
    	  (u_long)(u_int)addr << 32
    	| (u_long)id << 28
    	| (u_long)qwc << 0;
}

// ////////////////////////////////////////////////////////////////
//
// Create video input buffer
//
int viBufCreate(ViBuf *f,
    u_long128 *data, u_long128 *tag, int size,
    TimeStamp *ts, int n_ts)
{
    struct SemaParam param;

    f->data = data;
    f->tag = ( u_long128 * )UncAddr(tag);
    f->n = size;
    f->buffSize = size * VIBUF_ELM_SIZE;

    f->ts = ts;
    f->n_ts = n_ts;

    // ////////////////////////////////
    //
    // Create Semaphore
    //
    param.initCount = 1;
    param.maxCount = 1;
    f->sema = CreateSema(¶m);

    // ////////////////////////////////
    //
    // Reset
    //
    viBufReset(f);

    f->totalBytes = 0;

    return TRUE;
}

// ////////////////////////////////////////////////////////////////
//
// Reset video input buffer
//
int viBufReset(ViBuf *f)
{
    int i;

    f->dmaStart = 0;
    f->dmaN = 0;
    f->readBytes = 0;
    f->isActive = TRUE;

    f->count_ts = 0;
    f->wt_ts = 0;
    for (i = 0; i < f->n_ts; i++) {
	f->ts[i].pts = TS_NONE;
	f->ts[i].dts = TS_NONE;
	f->ts[i].pos = 0;
	f->ts[i].len = 0;
    }

    // ////////////////////////////////
    //
    // Init DMA tags
    //
    for (i = 0; i < f->n; i++) {
    	scTag2(
	    (QWORD*)(f->tag + i),
	    DmaAddr((char*)f->data + VIBUF_ELM_SIZE * i),
	    DMA_ID_REF,
	    VIBUF_ELM_SIZE/16
	);
    }
    scTag2(
	(QWORD*)(f->tag + i),
	DmaAddr(f->tag),
	DMA_ID_NEXT,
	0
    );

    *D4_QWC = 0;
    *D4_MADR = (u_int)DmaAddr(f->data);
    *D4_TADR = (u_int)DmaAddr(f->tag);
    setD4_CHCR((0<<8) | (1<<2) | 1);

    return TRUE;
}

// ////////////////////////////////////////////////////////////////
//
// Get areas to put data
//
void viBufBeginPut(ViBuf *f,
	u_char **ptr0, int *len0, u_char **ptr1, int *len1)
{
	
    int es;
    int en;
    int fs;
    int fn;

    WAITSEMA(f->sema);

    fs = FS(f);
    fn = FN(f);

    es = (fs + f->readBytes) % f->buffSize;
    en = fn - f->readBytes;

    if (f->buffSize - es >= en) {	// area0
	*ptr0 = (u_char*)f->data + es;
    	*len0 = en;
	*ptr1 = NULL;
	*len1 = 0;
    } else {				// area0 + area1
	*ptr0 = (u_char*)f->data + es;
    	*len0 = f->buffSize - es;
	*ptr1 = (u_char*)f->data;
	*len1 = en - (f->buffSize - es);
    }

    SIGNALSEMA(f->sema);
}

// ////////////////////////////////////////////////////////////////
//
// Proceed inner pointer
//
void viBufEndPut(ViBuf *f, int size)
{
    WAITSEMA(f->sema);

    f->readBytes += size;
    f->totalBytes += size;

    SIGNALSEMA(f->sema);
}

// ////////////////////////////////////////////////////////////////
//
// Add bitstream data to DMA tag list
//
int viBufAddDMA(ViBuf *f)
{
    int i;
    int index;
    int id;
    int last;
    u_int d4chcr;
    int isNewData = 0;
    int consume;
    int read_start, read_n;

    WAITSEMA(f->sema);

    if (!f->isActive) {
	ErrMessage("DMA ADD not active\n");
        return FALSE;
    }

    // ////////////////////////////////////////
    //
    // STOP DMA ch4
    //
    // d4chcr:
    //	(1) DMA was running
    //	    ORIGINAL DMA tag
    //	(2) DMA was not running
    //	    REFE tag
    setD4_CHCR((DMA_ID_REFE<<28) | (0<<8) | (1<<2) | 1);
    d4chcr = *D4_CHCR;

    // ////////////////////////////////////////
    //
    // update dma pointer using D4_MADR
    //
    index = getFIFOindex(f, (void*)*D4_MADR);
    consume = (index + f->n - f->dmaStart) % f->n;
    f->dmaStart = (f->dmaStart + consume) % f->n;
    f->dmaN -= consume;

    // ////////////////////////////////////////
    //
    // update read pointer
    //
    read_start = (f->dmaStart + f->dmaN) % f->n;
    read_n = f->readBytes/VIBUF_ELM_SIZE;
    f->readBytes %= VIBUF_ELM_SIZE;

    // ////////////////////////////////////////
    //
    // the last REFE -> REF
    //
    if (read_n > 0) {
	last = (f->dmaStart + f->dmaN - 1 + f->n) % f->n;
	scTag2(
	    (QWORD*)(f->tag + last),
	    (char*)f->data + VIBUF_ELM_SIZE * last, 
	    DMA_ID_REF,
	    VIBUF_ELM_SIZE/16
	);
	isNewData = 1;
    }

    index = read_start;
    for (i = 0; i < read_n; i++) {
    	id = (i == read_n - 1)? DMA_ID_REFE: DMA_ID_REF;
	scTag2(
	    (QWORD*)(f->tag + index),
	    (char*)f->data + VIBUF_ELM_SIZE * index, 
	    id,
	    VIBUF_ELM_SIZE/16
	);
	index = (index + 1) % f->n;
    }

    f->dmaN += read_n;

    // ////////////////////////////////////////
    //
    // RESTART DMA ch4
    //
    if (f->dmaN) {
	if (isNewData) {
	    // change ref/refe ----> ref
	    d4chcr = (d4chcr & 0x0fffffff) | (DMA_ID_REF << 28);
	}
	setD4_CHCR(d4chcr | 0x100);
    }

    SIGNALSEMA(f->sema);

    return TRUE;
}

// ////////////////////////////////////////////////////////////////
//
// Stop DMA and save DMA environment
//
int viBufStopDMA(ViBuf *f)
{
    WAITSEMA(f->sema);

    f->isActive = FALSE;

    setD4_CHCR((0<<8) | (1<<2) | 1);		// STR: 0, DIR: 1

    f->env.d4madr = *D4_MADR;
    f->env.d4tadr = *D4_TADR;
    f->env.d4qwc =  *D4_QWC;
    f->env.d4chcr = *D4_CHCR;

    // wait till ofc becomes 0
    while (DGET_IPU_CTRL() & 0xf0)
	;

    // DMA ch3
    setD3_CHCR((0<<8) | 0);		// STR: 0, DIR: 0
    f->env.d3madr = *D3_MADR;
    f->env.d3qwc =  *D3_QWC;
    f->env.d3chcr = *D3_CHCR;

    f->env.ipubp = DGET_IPU_BP();
    f->env.ipuctrl = DGET_IPU_CTRL();

    SIGNALSEMA(f->sema);

    return TRUE;
}

// ////////////////////////////////////////////////////////////////
//
// Restore DMA environment and restart DMA
//
int viBufRestartDMA(ViBuf *f)
{
    int bp = f->env.ipubp & 0x7f;
    int fp = (f->env.ipubp >> 16) & 0x3;
    int ifc = (f->env.ipubp >> 8) & 0xf;
    u_int d4madr_next = f->env.d4madr - ((fp + ifc) << 4);
    u_int d4qwc_next = f->env.d4qwc + (fp + ifc);
    u_int d4tadr_next = f->env.d4tadr;
    u_int d4chcr_next = f->env.d4chcr | 0x100;
    int index;
    int index_next;
    int id;

    WAITSEMA(f->sema);

    //
    // check wrap around
    //
    if (d4madr_next < (u_int)f->data) {
	d4qwc_next = (DATA_ADDR(0) - d4madr_next) >> 4;
    	d4madr_next += (u_int)(f->n * VIBUF_ELM_SIZE);
	d4tadr_next = TAG_ADDR(0);
	id = (f->env.d4madr == DATA_ADDR(0)
		|| f->env.d4madr == DATA_ADDR(f->n))?
	    DMA_ID_REFE:
	    DMA_ID_REF;
	d4chcr_next = (f->env.d4chcr & 0x0fffffff)
				| (id << 28) | 0x100;

	if (!IsInRegion(0, f->dmaStart, f->dmaN, f->n)) {
	    f->dmaStart = f->n - 1;
	    f->dmaN++;
	}
    } else if ((index = getFIFOindex(f, (void*)f->env.d4madr))
    		!= (index_next = getFIFOindex(f, (void*)d4madr_next))) {
	d4tadr_next = TAG_ADDR(index);
	d4qwc_next = (DATA_ADDR(index) - d4madr_next) >> 4;
	id = (WRAP_ADDR(f->env.d4madr)
		== DATA_ADDR((f->dmaStart + f->dmaN) % f->n))?
	    DMA_ID_REFE:
	    DMA_ID_REF;
	d4chcr_next = (f->env.d4chcr & 0x0fffffff)
				| (id << 28) | 0x100;

	if (!IsInRegion(index_next, f->dmaStart, f->dmaN, f->n)) {
	    f->dmaStart = index_next;
	    f->dmaN++;
	}
    }

    // Restart DMA ch3
    if (f->env.d3madr && f->env.d3qwc) {
	*D3_MADR = f->env.d3madr;
	*D3_QWC  = f->env.d3qwc;
	setD3_CHCR(f->env.d3chcr | 0x100);
    }

    if (f->dmaN) {
	while (sceIpuIsBusy())
	    ;
	// BCLR
	sceIpuBCLR(bp);
	while (sceIpuIsBusy())
	    ;
    }

    // Restart DMA ch4
    *D4_MADR = d4madr_next;
    *D4_TADR = d4tadr_next;
    *D4_QWC  = d4qwc_next;
    if (f->dmaN) {
	setD4_CHCR(d4chcr_next);
    }

    *IPU_CTRL = f->env.ipuctrl;

    f->isActive = TRUE;

    SIGNALSEMA(f->sema);

    return TRUE;
}

// ////////////////////////////////////////////////////////////////
//
// Delete video input buffer
//
int viBufDelete(ViBuf *f)
{
    setD4_CHCR((0<<8) | (1<<2) | 1);	// STR: 0, chain, DIR: 1
    *D4_QWC = 0;
    *D4_MADR = 0;
    *D4_TADR = 0;

    DeleteSema(f->sema);
    return TRUE;
}

// ////////////////////////////////////////////////////////////////
//
// Check to see if decoder is in CSC period or not (0: CSC period)
//
int viBufIsActive(ViBuf *f)
{
    int ret;

    WAITSEMA(f->sema);

    ret = f->isActive;

    SIGNALSEMA(f->sema);

    return ret;
}

// ////////////////////////////////////////////////////////////////
//
// Data size in video input buffer
//
int viBufCount(ViBuf *f)
{
    int ret;

    WAITSEMA(f->sema);

    ret = f->dmaN * VIBUF_ELM_SIZE + f->readBytes;

    SIGNALSEMA(f->sema);

    return ret;
}

// ////////////////////////////////////////////////////////////////
//
// Flush video input buffer
//
void viBufFlush(ViBuf *f)
{
    WAITSEMA(f->sema);

    f->readBytes = bound(f->readBytes, VIBUF_ELM_SIZE);

    SIGNALSEMA(f->sema);
}

// ////////////////////////////////////////////////////////////////
//
// Add new time stamp and remove old one from the buffer
//
int viBufModifyPts(ViBuf *f, TimeStamp *new_ts)
{
    TimeStamp *ts;
    int rd = (f->wt_ts - f->count_ts + f->n_ts) % f->n_ts;
    int datasize =  VIBUF_ELM_SIZE * f->n;
    int loop = 1;

    if (f->count_ts > 0) {
	while (loop) {
	    ts = f->ts + rd;

	    if (ts->len == 0 || new_ts->len == 0) {
		break;
	    }

	    if (IsPtsInRegion(ts->pos, new_ts->pos, new_ts->len, datasize)) {
		int len = min(new_ts->pos + new_ts->len - ts->pos, ts->len);

		ts->pos = (ts->pos + len) % datasize;
		ts->len -= len;

		if (ts->len == 0) {
		    if (ts->pts >= 0) {
/*			ErrMessage("pts is not used");*/
			ts->pts = TS_NONE;
			ts->dts = TS_NONE;
			ts->pos = 0;
			ts->len = 0;
		    }
		    f->count_ts = max(f->count_ts - 1, 0);
		}
	    } else {
		loop = 0;
	    }

	    rd = (rd + 1) % f->n_ts;
	}
    }

    return 0;
}

// ////////////////////////////////////////////////////////////////
//
// Add new time stamp
//
int viBufPutTs(ViBuf *f, TimeStamp *ts)
{
    int ret = 0;

    WAITSEMA(f->sema);

    if (f->count_ts < f->n_ts) {

	viBufModifyPts(f, ts);

	if (ts->pts >= 0 || ts->dts >= 0) {

	    f->ts[f->wt_ts].pts = ts->pts;
	    f->ts[f->wt_ts].dts = ts->dts;
	    f->ts[f->wt_ts].pos = ts->pos;
	    f->ts[f->wt_ts].len = ts->len;

	    f->count_ts++;
	    f->wt_ts = (f->wt_ts + 1) % f->n_ts;
	}
	ret = 1;
    } 

    SIGNALSEMA(f->sema);

    return ret;
}
// ////////////////////////////////////////////////////////////////
//
// Get time stamp
//
int viBufGetTs(ViBuf *f, TimeStamp *ts)
{
    u_int d4madr = *D4_MADR;
    u_int ipubp = DGET_IPU_BP();
    int bp = f->env.ipubp & 0x7f;
    int fp = (ipubp >> 16) & 0x3;
    int ifc = (ipubp >> 8) & 0xf;
    u_int d4madr_next = d4madr - ((fp + ifc) << 4);
    u_int stop;
    int datasize =  VIBUF_ELM_SIZE * f->n;
    int isEnd = 0;
    int tscount;
    int wt;
    int i;

    WAITSEMA(f->sema);

    ts->pts = TS_NONE;
    ts->dts = TS_NONE;

    stop = (d4madr_next + (bp >> 3) + datasize - (u_int)f->data)
    							% datasize;

    tscount = f->count_ts;
    wt = f->wt_ts;

    for (i = 0; i < tscount && !isEnd; i++) {

	int rd = (wt - tscount + f->n_ts + i) % f->n_ts;

	if (IsPtsInRegion(stop,
		f->ts[rd].pos, f->ts[rd].len, datasize)) {

	    ts->pts = f->ts[rd].pts;
	    ts->dts = f->ts[rd].dts;
	    f->ts[rd].pts = TS_NONE;
	    f->ts[rd].dts = TS_NONE;

	    isEnd = 1;
	    f->count_ts -= min(1, f->count_ts);
	}
    }

    SIGNALSEMA(f->sema);

    return 1;
}

} // namespace Flx


================================================
FILE: Code/Gel/Movies/Ngps/vibuf.h
================================================
#ifndef _VIBUF_H_
#define _VIBUF_H_

#include 
#include 

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#define VIBUF_ELM_SIZE 2048
#include "gel/movies/ngps/vibuf.h"
#define VIBUF_SIZE 256
#define VIBUF_TS_SIZE (VIBUF_SIZE*2)

namespace Flx
{



// ////////////////////////////////////////////////////////////////
//
//  Time stamp
//
struct TimeStamp{
    long pts;
    long dts;
    int pos;
    int len;
};

// ////////////////////////////////////////////////////////////////
//
//  Video input buffer
//
struct ViBuf{
    u_long128 *data;	// data array
    u_long128 *tag;	// tag array
    int n;		// the number of data/tag element in ViBuf
    int dmaStart;	// DMA area start position
    int dmaN;		// DMA area size
    int readBytes;	// read area size
    int buffSize;	// buffer size of ViBuf(bytes)
    sceIpuDmaEnv env;	// DMA environment
    int sema;		// semaphore
    int isActive;	// flag to check CSC period
    long totalBytes;	// total bytes of data which sent to ViBuf

    // Time Stamp
    TimeStamp *ts;	// time stamp array
    int n_ts;		// time stamp array size
    int count_ts;	// the number of time stamps in the array
    int wt_ts;		// write position of time stamp array

};

// ////////////////////////////////////////////////////////////////
//
//  Functions
//
int viBufCreate(ViBuf *f,
    u_long128 *data, u_long128 *tag, int size,
    TimeStamp *ts, int n_ts);
int viBufReset(ViBuf *f);
int viBufAddDMA(ViBuf *f);
int viBufDelete(ViBuf *f);
int viBufStopDMA(ViBuf *f);
int viBufRestartDMA(ViBuf *f);
void viBufPrint(ViBuf *f);
int viBufIsActive(ViBuf *f);
void viBufBeginPut(ViBuf *f,
	u_char **ptr0, int *len0, u_char **ptr1, int *len1);
void viBufEndPut(ViBuf *f, int size);
int viBufPutTs(ViBuf *f, TimeStamp *ts);
int viBufGetTs(ViBuf *f, TimeStamp *ts);
int viBufCount(ViBuf *f);
void viBufFlush(ViBuf *f);

} // namespace Flx

#endif // _VIBUF_H_



================================================
FILE: Code/Gel/Movies/Ngps/videodec.cpp
================================================
#include 
#include 
#include "gel/movies/ngps/p_movies.h"
#include "gel/movies/ngps/defs.h"
#include "gel/movies/ngps/vobuf.h"
#include "gel/movies/ngps/videodec.h"
#include "gel/movies/ngps/disp.h"

namespace Flx
{



void videoDecMain( void * dummy);
int decBs0(VideoDec *vd);
int mpegError(sceMpeg *mp, sceMpegCbDataError *cberror, void *anyData);
int mpegNodata(sceMpeg *mp, sceMpegCbData *cbdata, void *anyData);
int mpegStopDMA(sceMpeg *mp, sceMpegCbData *cbdata, void *anyData);
int mpegRestartDMA(sceMpeg *mp, sceMpegCbData *cbdata, void *anyData);
int mpegTS(sceMpeg *mp, sceMpegCbDataTimeStamp *cbts, void *anyData);
static int cpy2area(u_char *pd0, int d0, u_char *pd1, int d1,
    u_char *ps0, int s0, u_char *ps1, int s1);

extern int vblankCount;

// ////////////////////////////////////////////////////////////////
//
//  Create video decoder
//
int videoDecCreate(VideoDec *vd,
    u_char *mpegWork, int mpegWorkSize,
    u_long128 *data, u_long128 *tag,
    int tagSize, TimeStamp *pts, int n_pts)
{
    // Create sceMpeg
    sceMpegCreate(&vd->mpeg, mpegWork, mpegWorkSize);

    // Add Callbacks
    sceMpegAddCallback(&vd->mpeg,
    	sceMpegCbError, (sceMpegCallback)mpegError, NULL);
    sceMpegAddCallback(&vd->mpeg,
    	sceMpegCbNodata, mpegNodata, NULL);
    sceMpegAddCallback(&vd->mpeg,
    	sceMpegCbStopDMA, mpegStopDMA, NULL);
    sceMpegAddCallback(&vd->mpeg,
    	sceMpegCbRestartDMA, mpegRestartDMA, NULL);
    sceMpegAddCallback(&vd->mpeg, sceMpegCbTimeStamp,
    	(sceMpegCallback)mpegTS, NULL);

    videoDecReset(vd);

    // Create input video buffer
    viBufCreate(&vd->vibuf, data, tag, tagSize, pts, n_pts);

    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  Set decode mode
//
void videoDecSetDecodeMode(VideoDec *vd, int ni, int np, int nb)
{
    sceMpegSetDecodeMode(&vd->mpeg, ni, np, nb);
}

// ////////////////////////////////////////////////////////////////
//
//  Choose stream to be decoded
//
int videoDecSetStream(VideoDec *vd, int strType, int ch,
	sceMpegCallback cb, void *data)
{
    sceMpegAddStrCallback(&vd->mpeg, ( sceMpegStrType )strType, ch, cb, data);
    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  Get areas to put data in video input buffer
//
void videoDecBeginPut(VideoDec *vd,
	u_char **ptr0, int *len0, u_char **ptr1, int *len1)
{
    viBufBeginPut(&vd->vibuf, ptr0, len0, ptr1, len1);
}

// ////////////////////////////////////////////////////////////////
//
//  Proceed pointers of video input buffer
//
void videoDecEndPut(VideoDec *vd, int size)
{
    viBufEndPut(&vd->vibuf, size);
}

// ////////////////////////////////////////////////////////////////
//
//  Reset video decoder
//
void videoDecReset(VideoDec *vd)
{
    vd->state = VD_STATE_NORMAL;
}

// ////////////////////////////////////////////////////////////////
//
//  Delete video decoder
//
int videoDecDelete(VideoDec *vd)
{
    viBufDelete(&vd->vibuf);
    sceMpegDelete(&vd->mpeg);

    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  Abort decoding
//
void videoDecAbort(VideoDec *vd)
{
    vd->state = VD_STATE_ABORT;
}

// ////////////////////////////////////////////////////////////////
//
//  Get state of video decoder
//
u_int videoDecGetState(VideoDec *vd)
{
    return vd->state;
}

// ////////////////////////////////////////////////////////////////
//
//  Set state of video decoder
//
u_int videoDecSetState(VideoDec *vd, u_int state)
{
    u_int old = vd->state;

    vd->state = state;

    return old;
}

// ////////////////////////////////////////////////////////////////
//
//  Put time stamp to the video decoder
//
int videoDecPutTs(VideoDec *vd, long pts_val,
    long dts_val, u_char *start, int len)
{
    TimeStamp ts;

    // Set PTS
    ts.pts = pts_val;
    ts.dts = dts_val;
    ts.pos = start - (u_char*)vd->vibuf.data;
    ts.len = len;
    return viBufPutTs(& MOVIE_MEM_PTR videoDec.vibuf, &ts);
}

// ////////////////////////////////////////////////////////////////
//
//  Data size in video input buffer
//
int videoDecInputCount(VideoDec *vd)
{
    return viBufCount(&vd->vibuf);
}

// ////////////////////////////////////////////////////////////////
//
//  Empty space size in video input buffer
//
int videoDecInputSpaceCount(VideoDec *vd)
{
    u_char *ptr0;
    u_char *ptr1;
    int len0, len1;

    videoDecBeginPut(vd, &ptr0, &len0, &ptr1, &len1);

    return len0 + len1;
}

// ////////////////////////////////////////////////////////////////
//
//  Flush video decoder
//
int videoDecFlush(VideoDec *vd)
{
    u_char *pd0;
    u_char *pd1;
    u_char *pd0Unc;
    u_char *pd1Unc;
    u_char seq_end_code[4] = {0x00, 0x00, 0x01, 0xb7};
    int d0, d1;
    int len;

    videoDecBeginPut(vd, &pd0, &d0, &pd1, &d1);

    if (d0 + d1 < 4) {
    	return 0;
    }

    pd0Unc = (u_char*)UncAddr(pd0);
    pd1Unc = (u_char*)UncAddr(pd1);

    len = cpy2area(pd0Unc, d0, pd1Unc, d1, seq_end_code, 4, NULL, 0);

    videoDecEndPut(& MOVIE_MEM_PTR videoDec, len);

    viBufFlush(&vd->vibuf);

    if (vd->state == VD_STATE_NORMAL) {
	vd->state = VD_STATE_FLUSH;
    }

    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  Check to see if input buffer and reference image buffer are
//  flushed
//
int videoDecIsFlushed(VideoDec *vd)
{
    return (videoDecInputCount(vd) == 0)
    	&& sceMpegIsRefBuffEmpty(&vd->mpeg);
}

// ////////////////////////////////////////////////////////////////
//
//  Main function of decode thread
//
void videoDecMain( void * dummy)
{
	
	
	CHECK_MOVIE_MEM;
	
//	Dbg_MsgAssert( ( & MOVIE_MEM_PTR videoDec ) == vd, ( "What the fuck assface?" ) );
    viBufReset(& MOVIE_MEM_PTR videoDec.vibuf);
//    viBufReset(&vd->vibuf);

    voBufReset(& MOVIE_MEM_PTR voBuf);

    decBs0(& MOVIE_MEM_PTR videoDec);

    while ( MOVIE_MEM_PTR voBuf.count)
    	;

     videoDecSetState( & MOVIE_MEM_PTR videoDec, VD_STATE_END);
}

// ////////////////////////////////////////////////////////////////
//
// Decode bitstream using MPEG decoder
//
// return value
//     1: normal end
//     -1: aborted
int decBs0(VideoDec *vd)
{
	
    
	CHECK_MOVIE_MEM;
	
	VoData *voData;
    sceIpuRGB32 *rgb32;
    int ret;
    int status = 1;
    sceMpeg *mp = &vd->mpeg;

    // ////////////////////////////
    // 
    //  Main loop to decode MPEG bitstream
    // 
    while (!sceMpegIsEnd(&vd->mpeg)) {

	if (videoDecGetState(vd) == VD_STATE_ABORT) {
	    status = -1;
	    printf("decode thread: aborted\n");
	    break;
	}

	// ////////////////////////////////////////////////
	// 
	//  Get next available ouput buffer from voBuf
	//  switch to another thread if voBuf is full
	//
	while (!(voData = voBufGetData(& MOVIE_MEM_PTR voBuf))) {
	    switchThread();
	}
	rgb32 = (sceIpuRGB32*)voData->v;

	// ////////////////////////////////////////////////
	// 
	//  Get decoded picture in sceIpuRGB32 format
	//
        ret = sceMpegGetPicture(mp, rgb32, MAX_WIDTH/16 * MAX_HEIGHT/16);

        if (ret < 0) {
	    ErrMessage("sceMpegGetPicture() decode error");
	}

	if (mp->frameCount == 0) {
	    int i;
	    int image_w, image_h;

	    image_w = mp->width;
	    image_h = mp->height;

	    for (i = 0; i <  MOVIE_MEM_PTR voBuf.size; i++) {

	      // packet with texture data
	      setImageTag( MOVIE_MEM_PTR voBuf.tag[i].v[0],  MOVIE_MEM_PTR voBuf.data[i].v,
			  0, image_w, image_h);
	      
	      // packet without texture data
	      setImageTag( MOVIE_MEM_PTR voBuf.tag[i].v[1],  MOVIE_MEM_PTR voBuf.data[i].v,
			  1, image_w, image_h);
	    }
	}

	// ////////////////////////////
	// 
	//  Increment video output buffer count
	// 
	voBufIncCount(& MOVIE_MEM_PTR voBuf);

	switchThread();
    }

    sceMpegReset(mp);

    return status;
}

// ////////////////////////////////////////////////////////////////
//
//  Callback function for sceMpegCbError
//
int mpegError(sceMpeg *mp, sceMpegCbDataError *cberror, void *anyData)
{
    printf("%s\n", cberror->errMessage);
    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  Callback function for sceMpegCbNodata
//
int mpegNodata(sceMpeg *mp, sceMpegCbData *cbdata, void *anyData)
{
    switchThread();
    viBufAddDMA(& MOVIE_MEM_PTR videoDec.vibuf);
    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  Callback function for sceMpegCbStopDMA
//
int mpegStopDMA(sceMpeg *mp, sceMpegCbData *cbdata, void *anyData)
{
    // Stop DMA
    viBufStopDMA(& MOVIE_MEM_PTR videoDec.vibuf);
    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  Callback function for sceMpegCbRestartDMA
//
int mpegRestartDMA(sceMpeg *mp, sceMpegCbData *cbdata, void *anyData)
{
    // Restart DMA
    viBufRestartDMA(& MOVIE_MEM_PTR videoDec.vibuf);
    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  Callback function for sceMpegCbTimeStamp
//	retruns PTS and DTS
//
int mpegTS(sceMpeg *mp, sceMpegCbDataTimeStamp *cbts, void *anyData)
{
    TimeStamp ts;

    viBufGetTs(& MOVIE_MEM_PTR videoDec.vibuf, &ts);
    cbts->pts = ts.pts;
    cbts->dts = ts.dts;
    return 1;
}

// ////////////////////////////////////////////////////////////////
//
//  copy 2 areas
//
static int cpy2area(u_char *pd0, int d0, u_char *pd1, int d1,
    u_char *ps0, int s0, u_char *ps1, int s1)
{
    if (d0 + d1 < s0 + s1) {
        return 0;
    }

    if (s0 >= d0) {
    	memcpy(pd0,		ps0,		d0);
    	memcpy(pd1,		ps0 + d0,	s0 - d0);
    	memcpy(pd1 + s0 - d0,	ps1,		s1);
    } else { // s0 < d0
    	if (s1 >= d0 - s0) {
	    memcpy(pd0,		ps0,		s0);
	    memcpy(pd0 + s0,	ps1,		d0 - s0);
	    memcpy(pd1,		ps1 + d0 - s0,	s1 - (d0 - s0));
	} else { // s1 < d0 - s0
	    memcpy(pd0,		ps0,		s0);
	    memcpy(pd0 + s0,	ps1,		s1);
	}
    }
    return s0 + s1;
}


} // namespace Flx


================================================
FILE: Code/Gel/Movies/Ngps/videodec.h
================================================
#ifndef _VIDEODEC_H_
#define _VIDEODEC_H_

#include 

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#include "gel/movies/ngps/vibuf.h"

// ////////////////////////////////////////////////////////////////
//
//  Video decoder state
//
#define VD_STATE_NORMAL    0
#define VD_STATE_ABORT     1
#define VD_STATE_FLUSH     2
#define VD_STATE_END       3

namespace Flx
{



// ////////////////////////////////////////////////////////////////
//
//  Video Decoder
//
struct VideoDec{
    sceMpeg mpeg;	// MPEG decoder
    ViBuf vibuf;	// video input buffer
    u_int state;	// video decoder state
    int sema;		// semaphore

    int hid_endimage;	// handler to check the end of image transfer
    int hid_vblank;	// vlbank handler

};

// ////////////////////////////////////////////////////////////////
//
//  Functions
//
void videoDecReset(VideoDec *vd);
int videoDecCreate(VideoDec *vd,
    u_char *mpegWork, int mpegWorkSize,
    u_long128 *data, u_long128 *tag,
    int tagSize, TimeStamp *pts, int n_pts);
int videoDecDelete(VideoDec *vd);
void videoDecAbort(VideoDec *vd);
u_int videoDecGetState(VideoDec *vd);
u_int videoDecSetState(VideoDec *vd, u_int state);
int videoDecInputCount(VideoDec *vd);
int videoDecInputSpaceCount(VideoDec *vd);
void videoDecSetDecodeMode(VideoDec *vd, int ni, int np, int nb);
int videoDecFlush(VideoDec *vd);
int videoDecIsFlushed(VideoDec *vd);
int videoDecSetStream(VideoDec *vd, int strType, int ch,
	sceMpegCallback h, void *data);
void videoDecBeginPut(VideoDec *vd,
	u_char **ptr0, int *len0, u_char **ptr1, int *len1);
void videoDecEndPut(VideoDec *vd, int size);
int videoDecPutTs(VideoDec *vd, long pts_val,
    long dts_val, u_char *start, int len);

} // namespace Flx

#endif // _VIDEODEC_H_


================================================
FILE: Code/Gel/Movies/Ngps/vobuf.cpp
================================================
#include 
#include "gel/movies/ngps/defs.h"
#include "gel/movies/ngps/vobuf.h"

namespace Flx
{




// ////////////////////////////////////////////////////////////////
//
// Functions called from decoding thread
//
void voBufCreate(
    VoBuf *f,
    VoData *data,
    VoTag *tag,
    int size
)
{
    int i;

    f->data = data;
    f->tag = tag;
    f->size = size;
    f->count = 0;
    f->write = 0;

    for (i = 0; i < size; i++) {
        f->tag[i].status = VOBUF_STATUS_;
    }
}

void voBufDelete(VoBuf *f)
{
}

void voBufReset(VoBuf *f)
{
    f->count = 0;
    f->write = 0;
}

int voBufIsFull(VoBuf *f)
{
    return f->count == f->size;
}

void voBufIncCount(VoBuf *f)
{
    // disable interrupt
    DI();

    f->tag[f->write].status = VOBUF_STATUS_FULL;
    f->count++;
    f->write = (f->write + 1) % f->size;

    // enable interrupt
    EI();
}

VoData *voBufGetData(VoBuf *f)
{
    return voBufIsFull(f)? (VoData*)NULL: f->data + f->write;
}

// ////////////////////////////////////////////////////////////////
//
// Functions called from interrupt handler
//
int voBufIsEmpty(VoBuf *f)
{
    return f->count == 0;
}

VoTag *voBufGetTag(VoBuf *f)
{
    return voBufIsEmpty(f)? (VoTag*)NULL:
    	f->tag + ((f->write - f->count + f->size) % f->size);
}

void voBufDecCount(VoBuf *f)
{
    if (f->count > 0) {
	f->count--;
    }
}

} // namespace Flx


================================================
FILE: Code/Gel/Movies/Ngps/vobuf.h
================================================
#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#ifndef _VOBUF_H_
#define _VOBUF_H_

// ////////////////////////////////////////////////////////////////
//
//  Definitions
//
#define N_VOBUF 2

#define VOBUF_STATUS_    0
#define VOBUF_STATUS_TOPDONE  1
#define VOBUF_STATUS_FULL     2

#define MAX_MBX		(MAX_WIDTH/16)
#define MAX_MBY		(MAX_HEIGHT/16)

namespace Flx
{




// ////////////////////////////////////////////////////////////////
//
//  Element data for video output buffer
//
struct VoData{
    u_char v[MAX_WIDTH * MAX_HEIGHT * 4];
};

// ////////////////////////////////////////////////////////////////
//
//  Tag for video output buffer
//
struct VoTag{
    int status;		// status
    int dummy[15];	// this is to adjust D$ line
    u_int v[N_VOBUF][bound((N_LDTAGS+100)*4, 64)];
};

// ////////////////////////////////////////////////////////////////
//
//  Video output buffer
//
struct VoBuf{
    VoData *data;	    // data array
    VoTag *tag;		    // tag array for path3 transfer
    volatile int write;	    // write position
    volatile int count;	    // the number of images in VoBuf
    int size;		    // total number of elements in VoBuf
};

// ////////////////////////////////////////////////////////////////
//
// Functions called from decoding thread
//
struct _Display;

void voBufCreate(
    VoBuf *f,
    VoData *data,
    VoTag *tag,
    int size
);
void voBufReset(VoBuf *f);
int voBufSetTags(VoBuf *f, struct _Display *d);
int voBufIsFull(VoBuf *f);
void voBufIncCount(VoBuf *f);
VoData *voBufGetData(VoBuf *f);
void voBufDelete(VoBuf *f);

// ////////////////////////////////////////////////////////////////
//
// Functions called from interrupt handler
//
int voBufIsEmpty(VoBuf *f);
VoTag *voBufGetTag(VoBuf *f);
void voBufDecCount(VoBuf *f);

extern VoBuf voBuf;

} // namespace Flx

#endif // _VOBUF_H_


================================================
FILE: Code/Gel/Movies/Xbox/p_movies.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SK3														**
**																			**
**	Module:			Game Engine (GEL)	 									**
**																			**
**	File name:		p_movies.cpp											**
**																			**
**	Created:		07/24/01	-	dc										**
**																			**
**	Description:	Xbox specific .bik Bink movie streaming code			**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/
extern DWORD PixelShader4;

namespace Flx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/




/*****************************************************************************
**								Private Types								**
*****************************************************************************/

typedef struct
{
	float	sx,sy,sz;
	float	rhw;
	uint32	color;
	float	tu,tv;
}
MOVIE_VERT;

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

bool	alphaPixels					= false;
uint32	currentPlaybackSurfaceType	= BINKSURFACE32;
float	playbackWidth				= 640.0f;
float	playbackHeight				= 480.0f;
float	textureWidth				= 640.0f;
float	textureHeight				= 480.0f;
float	playbackWidthScale			= 1.0f;
float	playbackHeightScale			= 1.0f;
HBINK	playbackBinkHandle			= NULL;

DWORD	cullMode;
DWORD	multisampleAntialias;
DWORD	minFilter;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

LPDIRECT3DTEXTURE8 openPlaybackImage( uint32 width, uint32 height )
{
	// Need to this prevent the single poly being culled.
	D3DDevice_GetRenderState( D3DRS_CULLMODE,				&cullMode );
	D3DDevice_SetRenderState( D3DRS_CULLMODE,				D3DCULL_NONE );

	D3DDevice_GetRenderState( D3DRS_MULTISAMPLEANTIALIAS,	&multisampleAntialias );
	D3DDevice_SetRenderState( D3DRS_MULTISAMPLEANTIALIAS,	FALSE );
	
	D3DDevice_SetRenderState( D3DRS_LIGHTING,				FALSE );

	NxXbox::set_render_state( RS_ZWRITEENABLE,	0 );
	NxXbox::set_render_state( RS_ZTESTENABLE,	0 );
	
	D3DDevice_GetTextureStageState( 0, D3DTSS_MINFILTER,	&minFilter );
	D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER,	D3DTEXF_LINEAR );

	textureWidth	= (float)width;
	textureHeight	= (float)height;
	
	// Adjust for our current screen buffer.
	playbackWidth	= (float)NxXbox::EngineGlobals.backbuffer_width;
	playbackHeight	= (float)NxXbox::EngineGlobals.backbuffer_height;
	
	// Create a surface for our texture with the DirectDraw handle.
	LPDIRECT3DTEXTURE8 p_texture_surface;
	if( SUCCEEDED( D3DDevice_CreateTexture( width, height, 1, 0, D3DFMT_LIN_X8R8G8B8, 0, &p_texture_surface )))
	{
		return p_texture_surface;
	}

	return NULL;
}



void closePlaybackImage( LPDIRECT3DTEXTURE8 p_image )
{
	// Restore various states.
	D3DDevice_SetRenderState( D3DRS_CULLMODE,				cullMode );
	D3DDevice_SetRenderState( D3DRS_MULTISAMPLEANTIALIAS,	multisampleAntialias );
	D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER,	minFilter );
	
	NxXbox::set_render_state( RS_ZWRITEENABLE,	1 );
	NxXbox::set_render_state( RS_ZTESTENABLE,	1 );

	if( p_image )
	{
		ULONG refcount = p_image->Release();
		Dbg_Assert( refcount == 0 );
	}

	NxXbox::set_texture( 0, NULL );
}



// Advance a Bink file by one frame into a 3D image buffer.
static void decompressFrame( HBINK bink, LPDIRECT3DTEXTURE8 p_image, bool copy_all )
{
	// Decompress the Bink frame.
	BinkDoFrame( bink );

	// Lock the 3D image so that we can copy the decompressed frame into it.
	D3DLOCKED_RECT lock_rect;
	if( SUCCEEDED( p_image->LockRect( 0, &lock_rect, 0, 0 )))
	{
		void*	pixels		= lock_rect.pBits;
		uint32	pixel_pitch	= lock_rect.Pitch;

		// Copy the decompressed frame into the 3D image.
		BinkCopyToBuffer(	bink,
							pixels,
							pixel_pitch,
							bink->Height,
							0,					// Left pixel offset.
							0,					// Top pixel offset.
							currentPlaybackSurfaceType | (( copy_all ) ? BINKCOPYALL : 0 ));

		// Unlock the 3D image.
		p_image->UnlockRect( 0 );
	}
}



// Blit a 3D image onto the render target.
void blitImage( LPDIRECT3DTEXTURE8 p_image, float x_offset, float y_offset, float x_scale, float y_scale, float alpha_level )
{
	if( p_image == NULL )
	{
		return;
	}

	NxXbox::set_blend_mode( NxXbox::vBLEND_MODE_DIFFUSE );
	
	// Turn on clamping so that the linear textures work
	NxXbox::set_render_state( RS_UVADDRESSMODE0, 0x00010001UL );
	
	// Use a default vertex and pixel shader
	NxXbox::set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 );
	NxXbox::set_pixel_shader( PixelShader4 );

	// Select the texture.
	NxXbox::set_texture( 0, NULL );
	NxXbox::set_texture( 0, p_image );

	// Setup up the vertices.
	MOVIE_VERT vertices[4];

	vertices[0].sx		= x_offset;
	vertices[0].sy		= y_offset;
	vertices[0].sz		= 0.0f;
	vertices[0].rhw		= 0.0f;
	vertices[0].color	= ((int)(( alpha_level * 255.0f )) << 24 ) | 0x808080;
	vertices[0].tu		= -0.5f;
	vertices[0].tv		= -0.5f;

	vertices[1]			= vertices[ 0 ];
	vertices[1].sx		= x_offset + (((float)playbackWidth ) * x_scale );
	vertices[1].tu		= ((float)textureWidth ) - 0.5f;

	vertices[2]			= vertices[0];
	vertices[2].sy		= y_offset + (((float)playbackHeight ) * y_scale );
	vertices[2].tv		= ((float)textureHeight ) - 0.5f;

	vertices[3]			= vertices[1];
	vertices[3].sy		= vertices[2].sy;
	vertices[3].tv		= ((float)textureHeight ) - 0.5f;

	// Draw the vertices.
	D3DDevice_DrawVerticesUP( D3DPT_TRIANGLESTRIP, 4, vertices, sizeof( MOVIE_VERT ));
}






static void showFrame( HBINK bink, LPDIRECT3DTEXTURE8 p_image )
{
	// Begin a 3D frame.
	D3DDevice_Clear( 0, 0, D3DCLEAR_TARGET, 0, 1.0f, 0 );

	// Draw the image on the screen (centered)...
	float x = 0.0f;
	float y = 0.0f;
	blitImage( p_image, x, y, playbackWidthScale, playbackHeightScale, 1.0f );

	// End a 3D frame.
	D3DDevice_Swap( D3DSWAP_DEFAULT );

	// Keep playing the movie.
	BinkNextFrame( bink );
}




/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

static bool sDebounceCheck(uint16 *p_debounceFlags, uint8 *p_data, uint16 mask)
{
	if (p_data==NULL)
	{
		return false;
	}
		
	Dbg_MsgAssert(p_debounceFlags,("NULL p_debounceFlags"));
	
	uint16 data=(p_data[2]<<8)|p_data[3];
	if ((data & mask)==0)
	{
		// Button is pressed.
		
		if (*p_debounceFlags & mask)
		{
			// OK to return true, because the button got released at some point in the past.
			return true;
		}	
	}
	else
	{
		// Button is not pressed, so set the appropriate debounce flag so that next time it is
		// pressed it will be detected.
		*p_debounceFlags |= mask;
	}
	return false;	
}

void PMovies_PlayMovie( const char* pName )
{
	// Figure the volume we want to play at.
	Spt::SingletonPtr< Sfx::CSfxManager > sfx_manager;
	float music_vol	= Pcm::GetVolume();
	float sfx_vol	= sfx_manager->GetMainVolume( );
	float vol		= music_vol > sfx_vol ? music_vol : sfx_vol;

	// Incoming movie name is in the form movies\, convert to d:\data\movies\bik\.bik.
	char name_conversion_buffer[256] = "d:\\data\\movies\\bik\\";
	int length		= strlen( pName );
	int backwards	= length;
	while( backwards )
	{
		if( pName[backwards] == '\\' )
		{
			++backwards;
			break;
		}
		--backwards;
	}
	strncpy( name_conversion_buffer + 19, pName + backwards, length - backwards );
	length = strlen( name_conversion_buffer );
	name_conversion_buffer[length] = '.';
	name_conversion_buffer[length + 1] = 'b';
	name_conversion_buffer[length + 2] = 'i';
	name_conversion_buffer[length + 3] = 'k';
	name_conversion_buffer[length + 4] = 0;

	// Stop music and streams.
	Pcm::StopMusic();
	Pcm::StopStreams();
	
	playbackBinkHandle = BinkOpen( name_conversion_buffer, 0 );
	if( playbackBinkHandle == NULL )
	{
		// Movie not there, just quit.
		return;
	}

	// Switch the presentation interval to 30fps.
	DWORD presentation_interval;
	D3DDevice_GetRenderState( D3DRS_PRESENTATIONINTERVAL, &presentation_interval );
	D3DDevice_SetRenderState( D3DRS_PRESENTATIONINTERVAL, D3DPRESENT_INTERVAL_ONE );
	
	// Open a 3D image for the Bink.
	LPDIRECT3DTEXTURE8 p_image = openPlaybackImage( playbackBinkHandle->Width, playbackBinkHandle->Height );
	Dbg_Assert( p_image );

	// K: Call XGetDeviceChanges once before going into the loop, otherwise the first time this function
	// is run it will think new cards and pads have been inserted.
//	bool running_demo_movie=Script::GetInt("RunningDemoMovie");
	bool running_demo_movie=false;
	if (running_demo_movie)
	{
		DWORD insertions=0;
		DWORD removals=0;
		XGetDeviceChanges(XDEVICE_TYPE_MEMORY_UNIT, &insertions, &removals);
		XGetDeviceChanges(XDEVICE_TYPE_GAMEPAD, &insertions, &removals);
	}				
	
	if( p_image )
	{
		BinkSetSoundOnOff( playbackBinkHandle, 1 );

		// For BinkSetVolume(), 32768 is the 'nornal volume', effectively full volume on systems that don't
		// do amplification, like Xbox.
		BinkSetVolume( playbackBinkHandle, 0, (int)( 16384 * ( vol * 0.01f )));

		// Will need this in the loop...
		Spt::SingletonPtr< SIO::Manager > sio_manager;
		uint16 debounce_flags[4] = { 0,0,0,0 };

		// Start the playback loop.
		while( true )
		{
			if( !BinkWait( playbackBinkHandle ))
			{
				decompressFrame( playbackBinkHandle, p_image, true );
				showFrame( playbackBinkHandle, p_image );

				if( playbackBinkHandle->FrameNum >= ( playbackBinkHandle->Frames - 1 ))
				{
					break;
				}

				// Check for a button being pressed to skip out of movie playback.
				sio_manager->ProcessDevices();

				// If running a demo movie, then pad or mem card insertions must make it quit. (TRC requirement)
				if( running_demo_movie )
				{
					// Use the XGetDeviceChanges function to determine if a card or pad has been inserted ...
					DWORD insertions=0;
					DWORD removals=0;
					if( XGetDeviceChanges( XDEVICE_TYPE_MEMORY_UNIT, &insertions, &removals ))
					{
						if( insertions )
						{
							break;
						}	
					}	
					if( XGetDeviceChanges( XDEVICE_TYPE_GAMEPAD, &insertions, &removals ))
					{
						if( insertions )
						{
							break;
						}	
					}	
				}				

				bool quit = false;
				for( int d = 0; d < 4; ++d )
				{
					SIO::Device* p_device = sio_manager->GetDeviceByIndex( d );
					if( p_device )
					{
						unsigned char* p_data = p_device->GetControlData();

						if( sDebounceCheck( &debounce_flags[d], p_data, 0x0800 ) ||	// Start
							sDebounceCheck( &debounce_flags[d], p_data, 0x0040 ))		// A
						{
							quit = true;
							break;
						}							
						
						// If running a demo movie, then these buttons must also make it quit. (TRC requirement)
						if( running_demo_movie )
						{
							if( sDebounceCheck(&debounce_flags[d], p_data, 0x0100) ||	// Back
								sDebounceCheck(&debounce_flags[d], p_data, 0x0020) ||	// B
								sDebounceCheck(&debounce_flags[d], p_data, 0x0080) ||	// X
								sDebounceCheck(&debounce_flags[d], p_data, 0x0010) ||	// Y
								sDebounceCheck(&debounce_flags[d], p_data, 0x0002) ||	// Black
								sDebounceCheck(&debounce_flags[d], p_data, 0x0001) ||	// White
								sDebounceCheck(&debounce_flags[d], p_data, 0x0200) ||	// Thumbstick 1
								sDebounceCheck(&debounce_flags[d], p_data, 0x0400 ))	// Thumbstick 2
							{
								quit = true;
								break;
							}							
						}
					}
				}
				if( quit )
				{
					break;
				}
			}
		}
	}

	closePlaybackImage( p_image );
	BinkClose( playbackBinkHandle );
	playbackBinkHandle = NULL;

	// Switch the presentation interval bacl to what it was.
	D3DDevice_SetRenderState( D3DRS_PRESENTATIONINTERVAL, presentation_interval );
}

} // namespace Flx



================================================
FILE: Code/Gel/Movies/Xbox/p_movies.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			ps2 movies												**
**																			**
**	File name:		gel/movies/ngps/p_movies.h 								**
**																			**
**	Created: 		6/27/01	-	dc											**
**																			**
*****************************************************************************/

#ifndef __P_MOVIES_H
#define __P_MOVIES_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

#include 

namespace Flx
{

	void PMovies_PlayMovie( const char *pName );

} // namespace Flx

#endif	// __P_MOVIES_H





================================================
FILE: Code/Gel/Movies/ngc/p_movies.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SK3														**
**																			**
**	Module:			Game Engine (GEL)	 									**
**																			**
**	File name:		p_movies.cpp											**
**																			**
**	Created:		08/27/01	-	dc										**
**																			**
**	Description:	Gamecube specific movie streaming code					**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef DVDETH

#undef __GX_H__
#define _GX_H_

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "sys\ngc\p_aram.h"
#include 
#include	
#include 
#include 
#include	
#include	
#include	
#include "sys/ngc/p_prim.h"
#include "gel\music\ngc\p_music.h"
#include "gfx/ngc/nx/nx_init.h"
#include 
#include 
#include 
#include 

#include "VIDSimpleDEMO.h"
#include "VIDSimplePlayer.h"
#include "VIDSimpleAudio.h"
#include "VIDSimpleDraw.h"

#include 
#include 

#define MY_DEBUG

extern PADStatus		padData[PAD_MAX_CONTROLLERS]; // game pad state

extern GXColor messageColor;

#undef ASSERT
#define ASSERT(exp)                                             \
    (void) ((exp) ||                                            \
            (OSPanic(__FILE__, __LINE__, "Failed assertion " #exp), 0))

extern GXRenderModeObj *rmode;

//#define USE_DIRECT_XFB

//! Base address for 'Locked Cache' simple memory manager
static u8* 	lcMemBase = 0;

//! Locked cache base address for XFB conversion stuff
//! This is only required, if USE_DIRECT_XFB is set
static void* xfbLCStart = 0;		

//#define	VIDEO_FILENAME		"movies/peacemaker.vid"
//#define	VIDEO_FILENAME		"movies/video3.vid"
//#define	VIDEO_FILENAME		"movies/doomraiders.vid"
//#define	VIDEO_FILENAME		"movies/pu.vid"
//#define	VIDEO_FILENAME		"movies/nslogo.vid"
//#define	VIDEO_FILENAME		"movies/out4.vid"
//#define	VIDEO_FILENAME		"movies/hom.vid"
//#define	VIDEO_FILENAME		"movies/out5.vid"

extern bool	g_legal;

/******************************************************************************
 *  GLOABL VARIABLES
 ******************************************************************************/
extern void*   hwCurrentBuffer;         // current XFB frame buffer allocated in DEMO library 
VidSimplePlayer player;

#ifndef MIN
#define MIN(a,b) ((a) < (b) ? a : b)
#endif

/******************************************************************************
 *  LOCAL VARIABLES
 ******************************************************************************/
static VidChunk workChunk ATTRIBUTE_ALIGN(32);
static void dvdDoneCallback(s32 result, DVDFileInfo *videoInfo);

/*!
 ******************************************************************************
 * \brief
 *		Initializes all player structures.
 *
 ******************************************************************************
 */
void VIDSimpleInit(VIDAllocator _cbAlloc, VIDDeallocator _cbFree, VIDAllocator _cbLockedCache)
	{
	memset(&player, 0, sizeof(player));
	player.cbAlloc = _cbAlloc;
	player.cbFree = _cbFree;
	player.cbLockedCache = _cbLockedCache;
	}

/*!
 ******************************************************************************
 * \brief
 *		Request an async file transfer.
 *
 *		This function starts the transfer of the next frame into a free
 *		buffer.
 *
 ******************************************************************************
 */
static void ReadFrameAsync(void)
	{
	if (!player.error && player.preFetchState == TRUE)
		{
		if (player.currentFrameCount >  player.videoInfo.maxFrameCount - 1)
			{
			if (player.loopMode)
				{
				player.currentFrameCount = 0;
				player.nextFrameOffset = player.firstFrameOffset;
				player.nextFrameSize  = player.firstFrameSize;
				}
			else
				return;
			}
		
		player.asyncDvdRunning = TRUE;

		if (DVDReadAsync(&player.fileHandle,
						 player.readBuffer[player.readIndex].ptr,
						 (s32)player.nextFrameSize,
						 (s32)player.nextFrameOffset, dvdDoneCallback) != TRUE )
			{
			player.asyncDvdRunning = FALSE;
			player.error = TRUE;
			}
		}
	}

/*!
 ******************************************************************************
 * \brief
 *		DVD callback if async transfer is finished (or aborted)
 *	
 *		The idea here is to transfer ONE frame and additional 32 bytes for the
 *		HEADER of the NEXT frame in one transfer step. We store the size of
 *		the next frame, which is used in ReadFrameAsync().
 *
 *		
 * \note
 *		There a 32 padding bytes at the end of the .vid file. So, the reading
 *		of 32 additional bytes is even possible for the LAST frame. (Otherwise,
 *		we would 'point' out of the file)
 *
 *		See Dolphin documentation for information about parameters.
 *
 ******************************************************************************
 */
static void dvdDoneCallback(s32 result, DVDFileInfo * _UNUSED(videoInfo))
	{
	if (result == DVD_RESULT_FATAL_ERROR)
		{
		player.error = TRUE;
		return;
		}
	else if (result == DVD_RESULT_CANCELED)
		{
		return;
		}

	player.asyncDvdRunning = FALSE;

	player.readBuffer[player.readIndex].frameNumber = player.currentFrameCount;
	player.readBuffer[player.readIndex].size = (u32)result;	
	player.readBuffer[player.readIndex].valid = TRUE;
	
	player.currentFrameCount++;

	// move file pointer
	player.nextFrameOffset += player.nextFrameSize;
	
	if(player.currentFrameCount < player.videoInfo.maxFrameCount)
		{
		// set read size for next 'FRAM' chunk
		u32* nextFrameStart = (u32*)(player.readBuffer[player.readIndex].ptr + player.nextFrameSize - 32);
	
		// some check if file structure is okay
		ASSERT(nextFrameStart[0] == VID_FCC('F','R','A','M'));
		
		// get the size of the next 'FRAM' chunk to read
		player.nextFrameSize = nextFrameStart[1];
		ASSERT(player.nextFrameSize);
		}
	else
		player.nextFrameSize = 0;	// at end of file we have a size of '0'. This should be reinitialized later
									// using the size of the first frame somwhere else! Otherwise, we get an assertion

	// use next buffer
	player.readIndex = (player.readIndex + 1) % VID_NUM_READ_BUFFERS;

	// continue loading if we have a free buffer
	if (!player.readBuffer[player.readIndex].valid)
		ReadFrameAsync();
	}

/*!
 ******************************************************************************
 * \brief
 *		Allocate buffer memory for asynchronous dvd read
 *
 * \param memAlloc
 *		Pointer to memory allocation function
 *
 * \return
 *		TRUE if DVD buffer setup was successful.
 *
 ******************************************************************************
 */
BOOL VIDSimpleAllocDVDBuffers(void)
	{
	u32 i;
	u32 bufferSize;
	u8* ptr;

	bufferSize = player.videoInfo.maxBufferSize;
	ASSERT(bufferSize);
	
	bufferSize += VID_CHUNK_HEADER_SIZE;	// 'fram' header
	bufferSize += VID_CHUNK_HEADER_SIZE;	// 'vidd' header
	bufferSize = OSRoundUp32B(bufferSize);
	
	ASSERT(player.cbAlloc);
	player.readBufferBaseMem = (u8*)((*player.cbAlloc)(bufferSize * VID_NUM_READ_BUFFERS));
	
	if(!player.readBufferBaseMem)
		return FALSE;	// out of mem
	
	ptr = player.readBufferBaseMem;
	for (i = 0; i < VID_NUM_READ_BUFFERS ; i++)
		{
		player.readBuffer[i].ptr = ptr;
		ptr += bufferSize;
		player.readBuffer[i].valid = FALSE;
		}
	
	return TRUE;
	}

/*!
 ******************************************************************************
 * \brief
 *		Free buffer memory used for dvd read
 *
 * \param memFree
 *		Pointer to memory deallocation function
 *
 ******************************************************************************
 */
void VIDSimpleFreeDVDBuffers(void)
	{
	ASSERT(player.cbFree);
	ASSERT(player.readBufferBaseMem);
	(*player.cbFree)(player.readBufferBaseMem);
	}

/*!
 ******************************************************************************
 * \brief
 *		Create a new decoder instance.
 *
 *		The required parameters about the decoding process will be supplied
 *		in the VIDDecoderSetup structure.
 *
 * \param supportBFrames
 *		Set to TRUE for enabling b-frame support.
 *
 * \return
 *		TRUE if decoder creation was successful
 *
 ******************************************************************************
 */
BOOL VIDSimpleCreateDecoder(BOOL supportBFrames)
	{
	VIDDecoderSetup	setup;
	
	setup.size = sizeof(VIDDecoderSetup);
	setup.width = player.videoInfo.width;
	setup.height = player.videoInfo.height;
	setup.flags = supportBFrames ? VID_DECODER_B_FRAMES : 0;
	setup.cbMemAlloc = player.cbAlloc;
	setup.cbMemFree = player.cbFree;
	setup.cbMemAllocLockedCache = player.cbLockedCache;

	// Check if we want to setup audio decoding.
	// The audio header info must be already preset here!
	if(player.audioInfo.audioID == VID_FCC('V','A','U','D'))
		{
		u32 skip;
		ASSERT(player.audioHeaderChunk);
		ASSERT(player.audioInfo.vaud.maxHeap > 0);
		ASSERT(player.audioInfo.vaud.preAlloc > 0);
		
		setup.flags |= VID_DECODER_AUDIO;

		skip = VID_CHUNK_HEADER_SIZE + sizeof(u32) + (player.audioInfo.vaudex.version > 0 ? player.audioInfo.vaudex.size : sizeof(VidAUDHVAUD));
		setup.audio.headerInfo = player.audioHeaderChunk + skip;
        setup.audio.headerInfoBytes = ((VidChunk*)player.audioHeaderChunk)->len - skip;
		setup.audio.maxHeap = player.audioInfo.vaud.maxHeap;
		setup.audio.preAlloc = player.audioInfo.vaud.preAlloc;
		}
	
	player.decoder = VIDCreateDecoder(&setup);

	// check if decoder creation failed!
	return player.decoder ? TRUE : FALSE;
	}/*!
 ******************************************************************************
 * \brief
 *		Destroy decoder instance.
 *
 *		At this point the decoder returns all allocated memory by using
 *		the cbFree callback.
 *
 ******************************************************************************
 */
void VIDSimpleDestroyDecoder(void)
	{
	ASSERT(player.decoder);
	VIDDestroyDecoder(player.decoder);
	}

/*!
 ******************************************************************************
 * \brief
 *		Preload the allocated buffers.
 *
 *		This functions fills all buffers with initial data
 *
 * \param loopMode
 *		TRUE if we want to operate in loop mode
 *
 * \return
 *		TRUE if preload was okay 		
 *
 ******************************************************************************
 */
BOOL VIDSimpleLoadStart(BOOL loopMode)
	{
	u8* ptr;
	u32	i, readNum;
	u32* nextFrame;

	if (player.open && player.preFetchState == FALSE)
		{
		
		readNum = VID_NUM_READ_BUFFERS;

		// in 'non-loop' mode we must take care if we have LESS frames than preloading buffers
        if (!loopMode && player.videoInfo.maxFrameCount < VID_NUM_READ_BUFFERS)
                readNum = player.videoInfo.maxFrameCount;
				
		for(i = 0; i < readNum; i++)
			{
			
			ptr = player.readBuffer[player.readIndex].ptr;
			
			// read total 'FRAM' chunk and 32 bytes of NEXT chunk
			if (DVDRead(&player.fileHandle, ptr, (s32)player.nextFrameSize, (s32)player.nextFrameOffset) < 0 )
				{
#ifdef MY_DEBUG
				OSReport("*** VIDSimpleLoadStart: Failed to read from file.\n");
#endif
				player.error = TRUE;
				return FALSE;
				}
			
			player.nextFrameOffset += player.nextFrameSize;
			player.readBuffer[player.readIndex].size = player.nextFrameSize;

            // set read size for next 'FRAM' chunk
			nextFrame = (u32*)(ptr + player.nextFrameSize - 32);
			
			// some sanity check if file structure is valid!
			ASSERT(nextFrame[0] == VID_FCC('F','R','A','M'));
			
			player.nextFrameSize = nextFrame[1];
			ASSERT(player.nextFrameSize);

			player.readBuffer[player.readIndex].valid = TRUE;
			player.readBuffer[player.readIndex].frameNumber = player.currentFrameCount;

			// use next buffer
			player.readIndex = (player.readIndex + 1) % VID_NUM_READ_BUFFERS;

			player.currentFrameCount++;

			if (player.currentFrameCount >  player.videoInfo.maxFrameCount - 1)
				{
				if (loopMode)
					{
					player.currentFrameCount = 0;
					player.nextFrameOffset = player.firstFrameOffset;
					player.nextFrameSize  = player.firstFrameSize;
					}
				}
			}
			
			player.loopMode = loopMode;
			player.preFetchState = TRUE;

			return TRUE;
		}
	return FALSE;
	}

/*!
 ******************************************************************************
 * \brief
 *		Stops the asynchronous loading process.
 *
 * \return
 *		TRUE if player could be stopped!		
 *
 ******************************************************************************
 */
BOOL VIDSimpleLoadStop(void)
	{
	u32 i;

	if (player.open)
		{
		// stop preloading process
		player.preFetchState = FALSE;

		if (player.asyncDvdRunning)
			{
			DVDCancel(&player.fileHandle.cb);
			player.asyncDvdRunning = FALSE;
			}

		// invalidate all buffers
		for (i = 0 ; i < VID_NUM_READ_BUFFERS; i++)
			player.readBuffer[i].valid = FALSE;

		player.nextFrameOffset = player.firstFrameOffset;
		player.nextFrameSize = player.firstFrameSize;
		player.currentFrameCount = 0;
		
		player.error 	   		= FALSE;
		player.readIndex   		= 0;
		player.decodeIndex 		= 0;

		return TRUE;
		}
	return FALSE;
    }

/*!
 ******************************************************************************
 * \brief
 *		Open video file.
 *
 *		This functions opens a video file and parses some basic file
 *		information.
 *
 * \param fileName
 *		Name of file to open
 *
 * \return
 *		TRUE if file could be opened and is in valid format!
 *
 ******************************************************************************
 */
BOOL VIDSimpleOpen(char* fileName, BOOL suppressAudio)
	{
	u32 fileOffset = 0;
	u32 headSize;
	u32 audioInfoSize;
	
    if (player.open)
		{
#ifdef _DEBUG
		OSReport("*** Cannot open '%s', because player already open.\n");
#endif
		return FALSE;
		}

	if (DVDOpen(fileName, &player.fileHandle) == FALSE)
		{
#ifdef _DEBUG
		OSReport("*** Cannot open: '%s'\n", fileName);
#endif
		return FALSE;
		}
		
	// Read 'VID1' chunk from file and check for correct version
    if (DVDRead(&player.fileHandle, &workChunk, 32, 0) < 0)
		{
#ifdef _DEBUG
		OSReport("*** Failed to read the header.\n");
#endif
		DVDClose(&player.fileHandle);
		return FALSE;
		}
		
	fileOffset += 32;
	
	// Check file id	
	if(workChunk.id != VID_FCC('V','I','D','1')  )
		{
#ifdef _DEBUG
		OSReport("*** No VID1 file: '%s'\n", fileName);
#endif
		DVDClose(&player.fileHandle);
		return FALSE;
		}
	
	// Check for correct version of vid chunk.
	// If we find this version we assume a 'special' alignment and chunk ordering which may be invalid
	// in another version of the file format.
	if(workChunk.vid.versionMajor != 1 || workChunk.vid.versionMinor != 0)
		{
#ifdef _DEBUG
		OSReport("*** Unsupported file version: major: %d, minor: %d\n",
				  workChunk.vid.versionMajor, workChunk.vid.versionMajor);
#endif
		DVDClose(&player.fileHandle);
		return FALSE;
		}
	
#ifdef _DEBUG
	// Sometimes, it's required to check for a special build of the VidConv converter.
	{
	u32 version = VID_VERSION(workChunk.vid.vidConvMajor, workChunk.vid.vidConvMinor, workChunk.vid.vidConvBuild);
	if(version < VID_VERSION(1,6,4))
		OSReport("*** WARNING: Vid file created using an unsupported converter version: %d.%d.%d\n", (u32)workChunk.vid.vidConvMajor, (u32)workChunk.vid.vidConvMinor, (u32)workChunk.vid.vidConvBuild);
	}
#endif

	// Check types of chunks we have in this file.
	// !!! Note that we assume start of 'HEAD' chunk at byte offset 32 from file start !!!
	if (DVDRead(&player.fileHandle, &workChunk, 32, (s32)fileOffset) < 0)
		{
#ifdef _DEBUG
		OSReport("*** Failed to read 'HEAD' chunk.\n");
#endif
		DVDClose(&player.fileHandle);
		return FALSE;
		}
	
	if(workChunk.id != VID_FCC('H','E','A','D')  )
		{
#ifdef _DEBUG
		OSReport("*** No HEAD chunk found at expected offset\n");
#endif
		DVDClose(&player.fileHandle);
		return FALSE;
		}
	
	// Calculate the start of the first frame chunk
	// (we know the header chunk starts at offset 32)
	player.nextFrameOffset = workChunk.len + 32;

	// Skip 'HEAD' chunk id, len and version fields
	fileOffset += VID_CHUNK_HEADER_SIZE;
	
	// We initialize audio codec info to a known value
	// (this way we can detect the absence of any audio data)
	player.audioInfo.audioID = VID_FCC('N','O','N','E');
	
	// The header chunk contains one or more header chunks for the different data types contained
	// in the stream. Parse them all...

	headSize = workChunk.len - VID_CHUNK_HEADER_SIZE;
	while(headSize >= 32)
		{
		if (DVDRead(&player.fileHandle, &workChunk, 32, (s32)fileOffset) < 0)
			{
#ifdef _DEBUG
			OSReport("*** Error reading file at offset %d\n", fileOffset);
#endif
			DVDClose(&player.fileHandle);
			return FALSE;
			}
		
		fileOffset += 32;
		headSize -= 32;

		// We analyze the 1st 32 bytes of the chunk for a known header format
		
		// Video header?
		if(workChunk.id == VID_FCC('V','I','D','H') )
			{
			// check if we have an old vid file.
			if(workChunk.version == 0)
				{
				workChunk.vidh.frameRateScale = (u16)(*((u32*)&workChunk.vidh.frameRateScale));
				workChunk.vidh.flags = 0;
				}
			
			// Yes...
			ASSERT(workChunk.len <= 32);
			ASSERT(workChunk.len <= (sizeof(VidVIDH) + VID_CHUNK_HEADER_SIZE));
			memcpy(&player.videoInfo, &workChunk.vidh, sizeof(VidVIDH));
			}
		// It's an audio header chunk! May we initialize it?
		else if(workChunk.id == VID_FCC('A','U','D','H') && !suppressAudio)
			{
			// Allocate memory for audio header chunk
            player.audioHeaderChunk = (u8*)((*player.cbAlloc)(workChunk.len));
			audioInfoSize = workChunk.len - VID_CHUNK_HEADER_SIZE;
			
			// Copy the already loaded part
			memcpy(player.audioHeaderChunk, &workChunk, 32);
			workChunk.len -= 32;
			
			// Read additional audio header bytes if the audio header is greater that 32 bytes
			if(workChunk.len >= 32)
				{
				ASSERT((workChunk.len&31)==0);
				if (DVDRead(&player.fileHandle, player.audioHeaderChunk+32, workChunk.len, (s32)fileOffset) < 0)
					{
#ifdef _DEBUG
					OSReport("*** Error reading file at offset %d\n", fileOffset);
#endif
					DVDClose(&player.fileHandle);
					return FALSE;
					}
				fileOffset += workChunk.len;
				headSize -= workChunk.len;
				}

			// Setup and calc the number of bytes which we are allowed to copy into the audioInfo struct
			memcpy(&player.audioInfo, player.audioHeaderChunk+VID_CHUNK_HEADER_SIZE, MIN(audioInfoSize, sizeof(player.audioInfo) + sizeof(player.adpcmInfo)));
			}
		// Skip unknown chunks. We already read 32 bytes for the header which we must subtract here.
		else
			{
			fileOffset += workChunk.len - 32;
			headSize -= workChunk.len - 32;
			}
        }
	
	ASSERT(player.videoInfo.width && player.videoInfo.height);
	ASSERT(player.videoInfo.maxBufferSize);
	
	player.fps = (f32)player.videoInfo.frameRate / (f32)player.videoInfo.frameRateScale;
	
	// read beginning of 1st frame chunk to get required size information
	if (DVDRead(&player.fileHandle, &workChunk, 32 , (s32)player.nextFrameOffset) < 0 )
		{
#ifdef _DEBUG
		OSReport("*** Failed to read 'FRAM' chunk.\n");
#endif
		DVDClose(&player.fileHandle);
		return FALSE;
		}

	if(workChunk.id != VID_FCC('F','R','A','M')  )
		{
#ifdef _DEBUG
		OSReport("*** No FRAM chunk found.");
#endif
		DVDClose(&player.fileHandle);
		return FALSE;
		}

	player.nextFrameSize = workChunk.len; 		// 32 bytes of this chunk are already consumed, but
													// we want to 'preload' the NEXT chunk's FRAM header
	player.nextFrameOffset += 32;
	
	player.firstFrameOffset = player.nextFrameOffset;
	player.firstFrameSize   = player.nextFrameSize;

	strncpy(player.fileName, fileName, 64);
	player.fileName[63] = 0;
	
	player.open 			 	= TRUE;

	player.readIndex 			= 0;
	player.decodeIndex 			= 0;
	player.lastDecodedFrame		= 0;
	player.error 			 	= FALSE;
	player.preFetchState 	 	= FALSE;
	player.loopMode			 	= FALSE;
	player.asyncDvdRunning		= FALSE;
	player.currentFrameCount 	= 0;
	player.readBufferBaseMem	= 0;

	return TRUE;
	} 	

/*!
 ******************************************************************************
 * \brief
 *		Close open video file
 *
 * \return
 *		TRUE if file is closed sucessfully.
 *
 ******************************************************************************
 */
BOOL VIDSimpleClose(void)
	{
	if (player.open)
		{
		if (player.preFetchState == FALSE)
			{
			if (!player.asyncDvdRunning)
				{
				player.open = FALSE;
				DVDClose(&player.fileHandle);
				if(player.audioHeaderChunk != NULL)
					{
					(*player.cbFree)(player.audioHeaderChunk);
					player.audioHeaderChunk = NULL;
					}
				return TRUE;
				}
			}
		}
	return FALSE;
	}

/*!
 ******************************************************************************
 * \brief
 *		Decode all frame data
 *
 *		This function operates on the full frame input data. It forwards this
 *		data to the required decoder.
 *
 ******************************************************************************
 */
BOOL VIDSimpleDecode(void)
	{
	BOOL enabled;
	u8* chunkStart;
	u32 chunkSize;
	u32 frameSize;

	if ( player.readBuffer[player.decodeIndex].valid )
		{
		
		// ptr to our (pre-) loaded data INSIDE (!) 'FRAM' chunk
		// (in other words, the 'FRAM' chunk itself is not visible here)
		chunkStart = player.readBuffer[player.decodeIndex].ptr;

		// usually, we read additional 32 bytes for getting info about the NEXT chunk.
		// We only deal with the actual 'FRAM' chunk data here and adjust the size by 32 bytes.
        frameSize = player.readBuffer[player.decodeIndex].size - 32;
		
		// loop across ALL chunks inside 'FRAM'
		while(frameSize >= 32)
			{
			chunkSize = VID_CHUNK_LEN(chunkStart);
			
			if( VID_CHUNK_ID(chunkStart) == VID_FCC('V','I','D','D') )
				{
				if(! VIDVideoDecode(player.decoder, chunkStart + VID_CHUNK_HEADER_SIZE, chunkSize - VID_CHUNK_HEADER_SIZE, &player.image))
					{
#ifdef _DEBUG
					OSReport("*** VIDVideoDecode failed!\n");
#endif
					}
				}
			else if( VID_CHUNK_ID(chunkStart) == VID_FCC('A','U','D','D') )
				{
				// This is audio data!
				
				// Get the data to the audio system...
				if(! VIDSimpleAudioDecode(chunkStart + VID_CHUNK_HEADER_SIZE, chunkSize - VID_CHUNK_HEADER_SIZE))
					{
#ifdef _DEBUG
					OSReport("*** VIDAudioDecode failed!\n");
#endif
					}
				}
#ifdef _DEBUG
			else
				{
				OSReport("*** VIDSimpleDecode: unknown chunk type!\n");
				}
#endif
			
			// goto next chunk
			chunkStart += chunkSize;
			frameSize -= chunkSize;
			}
			
		player.lastDecodedFrame = player.readBuffer[player.decodeIndex].frameNumber;
		player.readBuffer[player.decodeIndex].valid = FALSE;
		player.decodeIndex = (player.decodeIndex + 1) % VID_NUM_READ_BUFFERS;

		// check if loading is still running
		enabled = OSDisableInterrupts();
		if (!player.readBuffer[player.readIndex].valid && !player.asyncDvdRunning)
			ReadFrameAsync();
		OSRestoreInterrupts(enabled);
		
        return TRUE;
		}

#ifdef _DEBUG
	OSReport("*** VIDSimpleDecode: No valid decode buffer found (?).\n");
#endif
	return FALSE;

	}
/*!
 ******************************************************************************
 * \brief
 *		Draw a decoded video frame.
 *
 * \param rmode
 *		Required info about current rendering mode
 * \param x
 *		current x pos of drawing surface
 * \param y
 *		current y pos of drawing surface
 * \param width
 *		current width of drawing surface
 * \param height
 *		current height of drawing surface
 *
 ******************************************************************************
 */
void VIDSimpleDraw(GXRenderModeObj *rmode, u32 x, u32 y, u32 width, u32 height)
	{
	VIDDrawGXYuv2RgbSetup(rmode);
	VIDDrawGXYuv2RgbDraw((s16)x, (s16)y, (s16)width, (s16)height, player.image);
	VIDDrawGXRestore();
	}
/*!
 ******************************************************************************
 * \brief
 *		Draw decoded frame directely into the XFB
 *
 * \param rmode
 *		Required info about current rendering mode
 * \param lcMem
 *		Pointer to free locked cache mem used for conversion.
 *
 ******************************************************************************
 */
void VIDSimpleDrawXFB(GXRenderModeObj *rmode, void* lcMem)
	{
	VIDXFBDraw(player.image, hwCurrentBuffer, rmode->fbWidth, rmode->xfbHeight, lcMem);
	}

/*!
 ******************************************************************************
 * \brief
 *		Get width and height of loaded video file.
 *
 ******************************************************************************
 */
void VIDSimpleGetVideoSize(u32* width, u32* height)
	{
	// can only be returned if player has a loaded file
	ASSERT(player.open);
	
	*width = player.videoInfo.width;
	*height = player.videoInfo.height;
	}
	
/*!
 ******************************************************************************
 * \brief
 *		Get FPS rate of loaded video file.
 *
 ******************************************************************************
 */
f32 VIDSimpleGetFPS(void)
	{
	return(player.fps);
	}
/*!
 ******************************************************************************
 * \brief
 *		Check if the currently loaded video is in interlace mode or not
 *
 ******************************************************************************
 */
BOOL VIDSimpleIsInterlace(void)
	{
	return(((player.videoInfo.flags & VID_VIDH_INTERLACED) != 0));
	}

/*!
 ******************************************************************************
 * \brief
 *		Get number of frames of loaded video file.
 *
 ******************************************************************************
 */
u32 VIDSimpleGetFrameCount(void)
	{
	return(player.videoInfo.maxFrameCount);
	}

/*!
 ******************************************************************************
 * \brief
 *		Get audio sample rate in Hz.
 *
 ******************************************************************************
 */
u32 VIDSimpleGetAudioSampleRate(void)
	{
	return(player.audioInfo.vaud.frq);
	}
/*!
 ******************************************************************************
 * \brief
 *		Check for drive status
 *
 ******************************************************************************
 */
BOOL VIDSimpleCheckDVDError(void)
	{
	switch (DVDGetDriveStatus())
		{
		case DVD_STATE_FATAL_ERROR:
			{
			OSReport("DVDGetDriveStatus()=DVD_STATE_FATAL_ERROR\n");
			break;
			}
		case DVD_STATE_NO_DISK:
			{
			OSReport("DVDGetDriveStatus()=DVD_STATE_NO_DISK\n");
			break;
			}
		case DVD_STATE_COVER_OPEN:
			{
			OSReport("DVDGetDriveStatus()=DVD_STATE_COVER_OPEN\n");
			break;
			}
		case DVD_STATE_WRONG_DISK:
			{
			OSReport("DVDGetDriveStatus()=DVD_STATE_WRONG_DISK\n");
			break;
			}
		case DVD_STATE_RETRY:
			{
			OSReport("DVDGetDriveStatus()=DVD_STATE_RETRY\n");
			break;
			}
		default:
			return(TRUE);
		}
	
	return(FALSE);
	}

/*!
 ******************************************************************************
 * \brief
 *		Memory allocation callback.
 *
 *		The player calls this function for all its memory needs. In this example
 *		an OSAlloc() is all we need to do.
 *
 * \note
 *		The returned memory address MUST be aligned on a 32 byte boundary.
 *		Otherwise, the player will fail!
 *
 * \param size
 *		Number of bytes to allocate
 *
 * \return
 *		Ptr to allocated memory (aligned to 32 byte boundary)
 *	
 ******************************************************************************
 */
static void* myAlloc(u32 size)
{
	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
	Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
	void * p = new u8[size]; 
	Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
	Mem::Manager::sHandle().PopContext();
	return p;
}

/*!
 ******************************************************************************
 * \brief
 *		Memory free callback.
 *
 * \param ptr
 *		Memory address to free
 *	
 ******************************************************************************
 */
static void myFree(void* ptr)
	{
	ASSERT(ptr);	// free on address 0 is only valid in C++ delete
	delete ptr;
	}

/*!
 ******************************************************************************
 * \brief
 *		Memory allocation callback for 'Locked Cache' memory.
 *
 *		If the system should operate in 'Locked Cache' mode, you must
 *		supply a callback which is called for any 'locked cache'
 *		memory requirements.
 *
 *		Note that there's no 'free' for the 'locked cache' memory,
 *		because if the player is destroyed ANY 'locked cache' memory is
 *		avaiable immediately.
 *
 * \note
 *		The returned memory address MUST be aligned on a 32 byte boundary.
 *		Otherwise, the player will fail!
 *
 * \param size
 *		Number of bytes to allocate
 *
 * \return
 *		Ptr to allocated memory (aligned to 32 byte boundary)
 *	
 ******************************************************************************
 */
static void* myAllocFromLC(u32 size)
	{
#ifdef MY_DEBUG
	u32 lockCacheMem;
#endif
	void* ret = lcMemBase;
	ASSERT(ret);
	
	lcMemBase += size;
	lcMemBase = (u8*)OSRoundUp32B(lcMemBase);

#ifdef MY_DEBUG
	lockCacheMem = (u32)(lcMemBase - ((u8*)LCGetBase()));
	//OSReport("myMallocFromLC: Used 'Locked Cache' Mem: %d kB.\n", lockCacheMem/1024);
	ASSERTMSG(lockCacheMem < (15*1024), "myMallocFromLC: Too much locked cache mem needed!\n");
#endif
	return ret;
	}

/******************************************************************************
 *  DEFINES
 ******************************************************************************/

/*!
 ******************************************************************************
 * \brief
 *		Restore GX graphics context to some 'defaults'
 *
 ******************************************************************************
 */
void VIDDrawGXRestore(void)
	{
    GXSetZMode(GX_ENABLE, GX_ALWAYS, GX_DISABLE);
    GXSetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_SET);

    GXSetNumTexGens(1);
    GXSetNumChans(0);
    GXSetNumTevStages(1);
    GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
    GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);

    // Swap mode settings
    GXSetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0);
    GXSetTevSwapMode(GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0);
    GXSetTevSwapMode(GX_TEVSTAGE2, GX_TEV_SWAP0, GX_TEV_SWAP0);
    GXSetTevSwapMode(GX_TEVSTAGE3, GX_TEV_SWAP0, GX_TEV_SWAP0);

    GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED,   GX_CH_GREEN,
                                        GX_CH_BLUE,  GX_CH_ALPHA); // RGBA
    GXSetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED,   GX_CH_RED,
                                        GX_CH_RED,   GX_CH_ALPHA); // RRRA
    GXSetTevSwapModeTable(GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN,
                                        GX_CH_GREEN, GX_CH_ALPHA); // GGGA
    GXSetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE,  GX_CH_BLUE,
                                        GX_CH_BLUE,  GX_CH_ALPHA); // BBBA

	}

/*!
 ******************************************************************************
 * \brief
 *		Setup GX for YUV conversion
 *
 ******************************************************************************
 */
void VIDDrawGXYuv2RgbSetup(GXRenderModeObj *rmode)
	{
    s32         scrWidth;
    s32         scrHeight;
    Mtx44       pMtx;

    scrWidth  = rmode->fbWidth;
    scrHeight = rmode->efbHeight;

	GXSetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);

	MTXOrtho(pMtx, 0.0f, (f32)scrHeight, 0.0f, (f32)scrWidth, 0.0f, -1.0F);

	GXSetProjection(pMtx, GX_ORTHOGRAPHIC);
    GXSetViewport(0.0F, 0.0F, (f32)scrWidth, (f32)scrHeight, 0.0F, 1.0F);
	GXSetScissor(0, 0, (u32)scrWidth, (u32)scrHeight);
	
    GXSetCurrentMtx(GX_IDENTITY);

    // Framebuffer
    GXSetZMode(GX_ENABLE, GX_ALWAYS, GX_DISABLE);
    GXSetBlendMode(GX_BM_NONE, GX_BL_ONE, GX_BL_ZERO, GX_LO_CLEAR);
    GXSetColorUpdate(GX_TRUE);
    GXSetAlphaUpdate(GX_FALSE);
    GXSetDispCopyGamma(GX_GM_1_0);
	
	// Color channels
    GXSetNumChans(0);

    // Texture coord generation
    GXSetNumTexGens(2);
    GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
	GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX1, GX_IDENTITY);

    // Texture cache
    GXInvalidateTexAll();

    // Vertex formats
    GXClearVtxDesc();
    GXSetVtxDesc(GX_VA_POS,  GX_DIRECT);
    GXSetVtxDesc(GX_VA_TEX0, GX_DIRECT);
    GXSetVtxDesc(GX_VA_TEX1, GX_DIRECT);
	
    GXSetVtxAttrFmt(GX_VTXFMT7, GX_VA_POS,  GX_POS_XYZ, GX_S16, 0);
    GXSetVtxAttrFmt(GX_VTXFMT7, GX_VA_TEX0, GX_TEX_ST,  GX_U16, 0);
    GXSetVtxAttrFmt(GX_VTXFMT7, GX_VA_TEX1, GX_TEX_ST,  GX_U16, 0);

	// Setup TEV environment to perform color space conversion.
	// The function will return the number of TEV stages need and will
	// use the following HW resources:
	//
	//	GX_TEVPREV
	//	GX_TEVREG0
	//	GX_TEVREG1
	//	GX_TEVREG2
	//
	//	GX_KCOLOR0
	//	GX_KCOLOR1
	//	GX_KCOLOR2
	//	GX_KCOLOR3
	//
	// plus everything visible in this source
	//
	GXSetNumTevStages(VIDSetupTEV(VID_YUVCONV_HIGHPRECISION));
	}

/*!
 ******************************************************************************
 * \brief
 *		Draw a textured polygon using the decoded image a texture.
 *
 * \param x
 *		xpos of polygon on screen
 * \param y
 *		ypos of polygon on screen
 * \param polygonWidth
 *		width of polygon to draw
 * \param polygonHeight
 *		height of polygon to draw
 * \param image
 *		ptr to VIDImage containing the required YUV pointers
 ******************************************************************************
 */
void VIDDrawGXYuv2RgbDraw(s16 x, s16 y, s16 polygonWidth, s16 polygonHeight, const VIDImage* image)
	{
	u16 textureWidth2, textureHeight2;
	GXTexObj tobj0, tobj1, tobj2;
	
	textureWidth2 = (u16)(image->texWidth >> 1);
	textureHeight2 = (u16)(image->texHeight >> 1);
	
	// Y Texture
	GXInitTexObj(&tobj0, image->y, image->texWidth, image->texHeight,
                 GX_TF_I8, GX_CLAMP, GX_CLAMP, GX_FALSE);
	GXInitTexObjLOD(&tobj0, GX_LINEAR, GX_LINEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
	GXLoadTexObj(&tobj0, GX_TEXMAP0);

	// Cb Texture
	GXInitTexObj(&tobj1, image->u, textureWidth2, textureHeight2,
                 GX_TF_I8, GX_CLAMP, GX_CLAMP, GX_FALSE);
	GXInitTexObjLOD(&tobj1, GX_LINEAR, GX_LINEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
	GXLoadTexObj(&tobj1, GX_TEXMAP1);

	// Cr Texture
	GXInitTexObj(&tobj2, image->v, textureWidth2, textureHeight2,
                 GX_TF_I8, GX_CLAMP, GX_CLAMP, GX_FALSE);
	GXInitTexObjLOD(&tobj2, GX_LINEAR, GX_LINEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
	GXLoadTexObj(&tobj2, GX_TEXMAP2);

	GXSetTexCoordScaleManually(GX_TEXCOORD0, GX_ENABLE, 1, 1);
	GXSetTexCoordScaleManually(GX_TEXCOORD1, GX_ENABLE, 1, 1);

	// Draw a textured quad
	GXBegin(GX_QUADS, GX_VTXFMT7, 4);
		GXPosition3s16(x, y, 0);
		GXTexCoord2u16(0, 0);
		GXTexCoord2u16(0, 0);
		
		GXPosition3s16((s16)(x+polygonWidth), y, 0);
		GXTexCoord2u16(image->width, 0);
		GXTexCoord2u16((u16)(image->width>>1), 0);

		GXPosition3s16((s16)(x+polygonWidth), (s16)(y+polygonHeight), 0);
		GXTexCoord2u16(image->width, image->height);
		GXTexCoord2u16((u16)(image->width>>1), (u16)(image->height>>1));
		
		GXPosition3s16(x, (s16)(y+polygonHeight), 0);
		GXTexCoord2u16(0, image->height);
		GXTexCoord2u16(0, (u16)(image->height>>1));
	GXEnd();

	GXSetTexCoordScaleManually(GX_TEXCOORD0, GX_DISABLE, 1, 1);
	GXSetTexCoordScaleManually(GX_TEXCOORD1, GX_DISABLE, 1, 1);
	
	}

/******************************************************************************
 *  DEFINES
 ******************************************************************************/

#define	VID_AUDIO_READAHEADFRAMES	8			// Number of video frames worth of audio data that should be able to be stored prio to being routed into the AI buffer
												// (the data stream will contain MORE data than needed at times -- esspecially at the beginning of the stream.
												//  Hence an intermediate buffer is needed!)

#define	VID_AUDIO_NUMLEADFRAMES		4			// Number of lead in frames for audio in data file (VIDCONV default)
#define	VID_AUDIO_LEADFACTOR		1.5f		// Lead in data ratio factor in lead in frames (VIDCONV default)

#define	VID_AUDIO_AIBUFFERSAMPLES	(2*256)		// 10.6ms of 48KHz data per AI buffer (32 byte multiple), that'll be about 15.9ms at 32Khz
#define	VID_AUDIO_NUMAIBUFFERS		2			// Number of AI playback buffers (this has an impact on audio latency. 2 is the minimum needed)
#define VID_AUDIO_NUMAIREQUESTS		16

#define	AX_ARAM_BUFFER_SIZE			(VID_AUDIO_AIBUFFERSAMPLES * VID_AUDIO_NUMAIBUFFERS)

//#define	AX_ARAM_LEFT_CHANNEL		0x200000	// @ 4MB (16-Bit addressing for DSP!)
#define	AX_ARAM_LEFT_CHANNEL		( NxNgc::EngineGlobals.aram_stream0 >> 1 )
#define	AX_ARAM_RIGHT_CHANNEL		(AX_ARAM_LEFT_CHANNEL + AX_ARAM_BUFFER_SIZE)

/******************************************************************************
 *  LOCAL VARIABLES & LOCAL EXTERNAL REFERENCES
 ******************************************************************************/

extern VidSimplePlayer	player;										// Player instance

static void				*audioReadBuffer;							// Buffer to store audio data received from the data stream
static u32				audioReadBufferNumSamples;					// Size of the above buffer in samples
static u32				audioReadBufferWritePos;					// Write position in the above buffer in samples
static u32				audioReadBufferReadPos;						// Read position in the above buffer in samples
static u8				audioReadBufferNumChannels;					// Number of channels stored in the read buffer

static void				*audioPlayBuffer[VID_AUDIO_NUMAIBUFFERS];	// AI playback buffers
static u8				audioPlayBufferWriteIndex;					// Index to next AI buffer to be written to from the read buffer
static u32				audioPlayBufferFrq;							// Playback frequency of AI in Hz
static volatile BOOL	audioPlayBufferEnabled;						// TRUE if playback is enabled. AI will operate at all times, but zeros will be filled in instead of real data if this is FALSE.
static volatile BOOL	audioPlayBackPossible;						// TRUE if read buffer is initialized and playback may start
																	// Normally this should be 1. It should never be greater or equal to the number of AI buffers.

static void (*VIDSimpleDoAudioDecode)(s16* dest,u32 channels,const s16** samples,u32 sampleOffset,u32 sampleNum); // vector to the current decoder function
static u32 (*VIDSimpleAudioBytesFromSamples)(u32 samples);					// vector to the current bytes to samples conversion function
static u32 (*VIDSimpleAudioSamplesFromBytes)(u32 bytes);					// vector to the current samples to bytes conversion function

static const u32		*audioPlayMaskArray;						// Pointer to an array of channel play masks (each set bit signals an active channel)
static u32				audioNumPlayMasks;							// Number of play masks specified (0 indicates all channels should be active)
static u32				audioNumActiveVoices;						// Number of active voices

static AXVPB			*axVoice[2];								// AX voice structures
static ARQRequest		arqRequest[2][VID_AUDIO_NUMAIREQUESTS];		// Enough ARQ request structures for worst case scenario
static u32				axLastAddr;									// Last known address DSP read from for 1st voice
static u32				axPlayedSamples;							// Number of samples played on first voice since last update
static f32				axCurrentFrq;								// Current frequency used for playback
static u32				axMinAvailFrames;							// Minimum number of frames available in the read buffer at which we are still "happy" and play at the specified frequency			

typedef enum VID_AXPHASE {
		AX_PHASE_STARTUP = 0,
		AX_PHASE_START,
		AX_PHASE_PLAY
} VID_AXPHASE;

static VID_AXPHASE		axPhase;

/******************************************************************************
 *  LOCAL PROTOTYPES
 ******************************************************************************/

static void AXCallback(void);

static void VIDSimpleDoAudioDecodePCM16(s16* dest,u32 channels,const s16** samples,u32 sampleOffset,u32 sampleNum);
static u32 VIDSimpleAudioBytesFromSamplesPCM16(u32 samples);
static u32 VIDSimpleAudioSamplesFromBytesPCM16(u32 bytes);

static void VIDSimpleDoAudioDecodeVAUD(s16* dest,u32 channels,const s16** samples,u32 sampleOffset,u32 sampleNum);
static u32 VIDSimpleAudioBytesFromSamplesVAUD(u32 samples);
static u32 VIDSimpleAudioSamplesFromBytesVAUD(u32 bytes);

/*!
 ******************************************************************************
 * \brief
 *		Initialize audio decoder
 *
 *		This function allocates all neccessary memory for the audio processing
 *		and sets the audio decoder into an idle state, waiting for first data.
 *		A file must be opened with the VIDSimplePlayer before calling this
 *		function.
 *
 * \param cbAlloc
 *		Pointer to a memory allocation function to be used
 *
 * \return
 *		TRUE if any problem was detected
 *
 ******************************************************************************
 */
BOOL VIDSimpleInitAudioDecoder(void)
	{
	u32	i;
	BOOL old;

	AXPBMIX	  axMix[2];
	AXPBVE    axVE;
	AXPBSRC	  axSRC;
	AXPBADDR  axAddr;
	AXPBADPCM axADPCM;
	u32		  ratio;

	// Do we have any audio data?
	if (player.audioInfo.audioID != VID_FCC('N','O','N','E'))
		{
		// VAUD?
		if (player.audioInfo.audioID == VID_FCC('V','A','U','D'))
			{
			// Calculate buffer size to allocate proper memry to keep a bit of "extra" audio data around...
			audioReadBufferNumSamples = (u32)(((f32)VID_AUDIO_READAHEADFRAMES * player.audioInfo.vaud.frq) / player.fps);
			audioReadBufferNumChannels = player.audioInfo.vaud.numChannels <= 2 ? player.audioInfo.vaud.numChannels : 2;

			// Setup decoder & conversion functions to be used
			VIDSimpleDoAudioDecode = VIDSimpleDoAudioDecodeVAUD;
			VIDSimpleAudioBytesFromSamples = VIDSimpleAudioBytesFromSamplesVAUD;
			VIDSimpleAudioSamplesFromBytes = VIDSimpleAudioSamplesFromBytesVAUD;
			}
		else
			{
			// PCM16?
	        if (player.audioInfo.audioID == VID_FCC('P','C','1','6'))
				{
		        // Calculate buffer size to allocate proper memry to keep a bit of "extra" audio data around...
		        audioReadBufferNumSamples = (u32)(((f32)VID_AUDIO_READAHEADFRAMES * player.audioInfo.pcm16.frq) / player.fps);
        
		        if (player.audioInfo.pcm16.numChannels <= 2)
			        audioReadBufferNumChannels = player.audioInfo.pcm16.numChannels;
		        else
			        audioReadBufferNumChannels = 2;
		        
		        // Setup decoder & conversion functions to be used
		        VIDSimpleDoAudioDecode = VIDSimpleDoAudioDecodePCM16;
		        VIDSimpleAudioBytesFromSamples = VIDSimpleAudioBytesFromSamplesPCM16;
		        VIDSimpleAudioSamplesFromBytes = VIDSimpleAudioSamplesFromBytesPCM16;
		        }
	        else
		        {
		        if (player.audioInfo.audioID == VID_FCC('A','P','C','M'))
			        {
			        // [...]
			        }
		        else
			        {
			        // Other audio codecs might be implemented here
			        // (the idea being to decode the data into the audioReadBuffer allocated below)
			        // [...]
			        ASSERT(FALSE);
			        }
		        }
			}
			
		// Allocate read buffer
		audioReadBuffer = player.cbAlloc(audioReadBufferNumSamples * sizeof(s16) * player.audioInfo.pcm16.numChannels);
		if (audioReadBuffer == NULL)
			return TRUE;					// error
		
		// Reset ring buffer
		audioReadBufferReadPos = audioReadBufferWritePos = 0;
		
		// What AI frquency is best?
		audioPlayBufferFrq = player.audioInfo.pcm16.frq;
		
		// Allocate AI playback buffer
		audioPlayBuffer[0] = player.cbAlloc(2 * sizeof(s16) * VID_AUDIO_AIBUFFERSAMPLES * VID_AUDIO_NUMAIBUFFERS);
		if (audioPlayBuffer[0] == NULL)
			return TRUE;					// error
		
		for(i=1; i> 16);
		axSRC.ratioLo = (u16)ratio;
		
		axCurrentFrq = (f32)audioPlayBufferFrq;

		// Calculate what we deem is a save amount of extra data in our read buffers
		axMinAvailFrames = (u32)((audioPlayBufferFrq * (VID_AUDIO_LEADFACTOR - 1.0f) * ((f32)VID_AUDIO_NUMLEADFRAMES / player.fps)) / (f32)VID_AUDIO_AIBUFFERSAMPLES);
		
		AXSetVoiceSrcType(axVoice[0],AX_SRC_TYPE_4TAP_16K);
		AXSetVoiceSrc(axVoice[0],&axSRC);
		AXSetVoiceSrcType(axVoice[1],AX_SRC_TYPE_4TAP_16K);
		AXSetVoiceSrc(axVoice[1],&axSRC);
		
        *(u32 *)&axAddr.currentAddressHi = AX_ARAM_LEFT_CHANNEL;
		*(u32 *)&axAddr.loopAddressHi = AX_ARAM_LEFT_CHANNEL;
		*(u32 *)&axAddr.endAddressHi = AX_ARAM_LEFT_CHANNEL + AX_ARAM_BUFFER_SIZE - 1;
		axAddr.format = AX_PB_FORMAT_PCM16;
		axAddr.loopFlag = AXPBADDR_LOOP_ON;
		AXSetVoiceAddr(axVoice[0],&axAddr);
		
		*(u32 *)&axAddr.currentAddressHi = AX_ARAM_RIGHT_CHANNEL;
		*(u32 *)&axAddr.loopAddressHi = AX_ARAM_RIGHT_CHANNEL;
		*(u32 *)&axAddr.endAddressHi = AX_ARAM_RIGHT_CHANNEL + AX_ARAM_BUFFER_SIZE - 1;
		AXSetVoiceAddr(axVoice[1],&axAddr);

		memset(&axADPCM,0,sizeof(axADPCM));
		axADPCM.gain = 0x0800;
		
		AXSetVoiceAdpcm(axVoice[0],&axADPCM);
		AXSetVoiceAdpcm(axVoice[1],&axADPCM);
		
		AXSetVoiceType(axVoice[0],AX_PB_TYPE_STREAM);
		AXSetVoiceType(axVoice[1],AX_PB_TYPE_STREAM);
		
		AXRegisterCallback(AXCallback);
		
		axLastAddr = AX_ARAM_LEFT_CHANNEL;
		axPlayedSamples = VID_AUDIO_AIBUFFERSAMPLES * VID_AUDIO_NUMAIBUFFERS;
		axPhase = AX_PHASE_STARTUP;
		
		// All is setup for the voices. We'll start them inside the AX callback as soon as we got data in the ARAM buffers
        OSRestoreInterrupts(old);
		}
		
	return FALSE;			// no error
	}

/*!
 ******************************************************************************
 * \brief
 *		Shutdown audio decoder and free resources
 *
 * \param cbFree
 *		Pointer to a fucntion to be used to free the allocated memory
 *
 ******************************************************************************
 */
void VIDSimpleExitAudioDecoder(void)
	{
	// Any audio?
	if (player.audioInfo.audioID != VID_FCC('N','O','N','E'))
		{
		// Yes. Unregister callback & stop AI DMA
		BOOL old = OSDisableInterrupts();
		
		AXRegisterCallback(NULL);
		AXSetVoiceState(axVoice[0],AX_PB_STATE_STOP);
		AXSetVoiceState(axVoice[1],AX_PB_STATE_STOP);
		AXFreeVoice(axVoice[0]);
		AXFreeVoice(axVoice[1]);
		
		axVoice[0] = axVoice[1] = NULL;
		
		OSRestoreInterrupts(old);
		
		// Any codec related resources should be freed, too
		// [...]
		
		// Free allocated resources...
		player.cbFree(audioPlayBuffer[0]);
		player.cbFree(audioReadBuffer);
		}
	}


/*!
 ******************************************************************************
 * \brief
 *		Stop audio playback without shutting down AI etc.
 *
 ******************************************************************************
 */
void VIDSimpleAudioReset(void)
	{
	BOOL	old;
	
	if (player.audioInfo.audioID != VID_FCC('N','O','N','E'))
		{
		old = OSDisableInterrupts();
		
		audioReadBufferWritePos = 0;
		audioReadBufferReadPos = 0;
		
		audioPlayBufferEnabled = FALSE;
		audioPlayBackPossible = FALSE;
		
		// ADPCM?
		if (player.audioInfo.audioID != VID_FCC('A','P','C','M'))
			{
			// [...]
			}
		else
			{
			// Other codecs could reset their state right here...
			// [...]
			}
			
		OSRestoreInterrupts(old);
		}
	}

/*!
 ******************************************************************************
 * \brief
 *		Return some information about current audio stream.
 *
 ******************************************************************************
 */
void VIDSimpleAudioGetInfo(VidAUDH* audioHeader)
	{
	ASSERT(audioHeader);
	memcpy(audioHeader, &player.audioInfo, sizeof(*audioHeader));
    }

/*!
 ******************************************************************************
 * \brief
 *		'Decode' PCM16 to PCM16
 *
 *		This function simply copies over new PCM16 samples into the read buffer.
 *		Other codecs might use a more elaborate decoding of course ;-)
 *
 ******************************************************************************
 */
static void VIDSimpleDoAudioDecodePCM16(s16* dest, u32 channels, const s16** samples, u32 sampleOffset, u32 sampleNum)
	{
	u32		i,b,s;
    u32 		bytes  = VIDSimpleAudioBytesFromSamplesPCM16(sampleNum);
	const void*	src    = (const u8*)samples[0] + VIDSimpleAudioBytesFromSamplesPCM16(sampleOffset);
	
	// Do we have any playback masks?
	if (audioNumPlayMasks != 0)
		{
		// Yes! How many samples?
		u32 numSamples = bytes / (player.audioInfo.pcm16.numChannels * sizeof(s16));
		
		// Scan through all playback masks and update channels as they come
		u32 c = 0;
		u32 rc = 0;
		for(i=0; i= audioReadBufferReadPos)
		{
		freeSamples = audioReadBufferNumSamples - (audioReadBufferWritePos - audioReadBufferReadPos);
		
		if (freeSamples < sampleNum)
			{
			OSRestoreInterrupts(old);
			#ifndef FINAL
			OSReport("*** audioDecode: overflow\n");
			#endif
			return FALSE;				// overflow!
			}
		
		// We might have a two buffer update to do. Check for it...
		if ((len1 = (audioReadBufferNumSamples - audioReadBufferWritePos)) >= sampleNum)
			{
			// No. We got ourselfs a nice, simple single buffer update.
			VIDSimpleDoAudioDecode((s16 *)((u32)audioReadBuffer + audioReadBufferWritePos * sampleSize),numChannels,samples,0,sampleNum);
			}
		else
			{
			// Dual buffer case
			VIDSimpleDoAudioDecode((s16 *)((u32)audioReadBuffer + audioReadBufferWritePos * sampleSize),numChannels,samples,0,len1);
			VIDSimpleDoAudioDecode((s16 *)audioReadBuffer,numChannels,samples,len1,sampleNum-len1);
			}
		}
	else
		{
		freeSamples = audioReadBufferReadPos - audioReadBufferWritePos;
		
		if (freeSamples < sampleNum)
			{
			OSRestoreInterrupts(old);
			#ifndef FINAL
			OSReport("*** audioDecode: overflow\n");
			#endif
			return FALSE;				// overflow!
			}
            // We're save to assume to have a single buffer update in any case...
			VIDSimpleDoAudioDecode((s16 *)((u32)audioReadBuffer + audioReadBufferWritePos * sampleSize),numChannels,samples,0,sampleNum);
		}

	// Advance write position...
	audioReadBufferWritePos += sampleNum;
		
	if (audioReadBufferWritePos >= audioReadBufferNumSamples)
		audioReadBufferWritePos -= audioReadBufferNumSamples;
		
	// We're done with all critical stuff. IRQs may be enabled again...
	OSRestoreInterrupts(old);
		
	return TRUE;
	}


/*!
 ******************************************************************************
 * \brief
 *		Receive data from bitstream and direct to required encoding facility
 *
 *		This function will receive the AUDD chunck from the VIDSimplePlayer's
 *		decode function. 
 *
 * \param bitstream
 *		Pointer to the data of a AUDD chunk
 *
 * \param bitstreamLen
 *		Length of the data in the AUDD chunk pointed to by above pointer
 *
 * \return
 *		FALSE if data could not be interpreted properly
 *
 ******************************************************************************
 */
BOOL VIDSimpleAudioDecode(const u8* bitstream, u32 bitstreamLen)
	{
	// Any audio data present?
	if (player.audioInfo.audioID != VID_FCC('N','O','N','E'))
		{
        // Select requested audio decoding method
        if(player.audioInfo.audioID == VID_FCC('V','A','U','D'))
			{
			u32 headerSize = ((VidAUDDVAUD*)bitstream)->size;
			// a channel mask if 0x3 selects the first two channels...
			if(!VIDAudioDecode(player.decoder, bitstream+headerSize, bitstreamLen-headerSize, 0x3, audioDecode, NULL))
				return FALSE;
			
			}
		else
			{
			// Calc data and call audio decode callback by ourself
			const u8* data = bitstream + sizeof(u32);
			u32 dataLength = *(const u32 *)bitstream;
			
			// We always assume 1 source data pointer
			if(!audioDecode(1, (const s16**)&data, VIDSimpleAudioSamplesFromBytes(dataLength), NULL))
				return FALSE;

			}
		audioPlayBackPossible = TRUE;
		}
	return TRUE;
	}

/*!
 ******************************************************************************
 * \brief
 *		Enable AI playback (within about 5ms)
 *
 ******************************************************************************
 */
				
void VIDSimpleAudioStartPlayback(const u32 *playMaskArray,u32 numMasks)
	{
	BOOL old = OSDisableInterrupts();
	
	if (audioPlayBackPossible)
		{
		audioPlayBufferEnabled = TRUE;
		
		VIDSimpleAudioChangePlayback(playMaskArray,numMasks);
		}
	
	OSRestoreInterrupts(old);
	}
	
/*!
 ******************************************************************************
 * \brief
 *		Change the active audio channels
 *
 ******************************************************************************
 */
				
void VIDSimpleAudioChangePlayback(const u32 *playMaskArray,u32 numMasks)
	{
	u32		i,b;
	BOOL	old = OSDisableInterrupts();
	
	audioPlayMaskArray = playMaskArray;
	audioNumPlayMasks = numMasks;
	
	// Any playback mask specified?
	if (audioNumPlayMasks != 0)
		{
		// Yes. Count the active voices...
		audioNumActiveVoices = 0;
		
		for(i=0; i= audioReadBufferNumSamples)
		audioReadBufferReadPos -= audioReadBufferNumSamples;
	}

/*!
 ******************************************************************************
 * \brief
 *		AX callback
 *
 ******************************************************************************
 */
static void AXCallback(void)
	{
	u32		availSamples,availFrames,numUpdate,i,n;
	u32		audioPlayBufferNeededAudioFrames;
	u32		currentAddr;
	u32		leftSource, rightSource, leftTarget, rightTarget;

	// First thing to do here is call the regular soundfx callback.
	Sfx::AXUserCBack();
	
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	
	if (axPhase == AX_PHASE_START)
	{
		AXSetVoiceState(axVoice[0],AX_PB_STATE_RUN);
		AXSetVoiceState(axVoice[1],AX_PB_STATE_RUN);
		axPhase = AX_PHASE_PLAY;
	}
	else if( axPhase == AX_PHASE_PLAY )
	{
		if( axVoice[0]->pb.state == AX_PB_STATE_STOP )
		{
			return;
		}
	}
		
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	
	currentAddr = *(u32 *)&axVoice[0]->pb.addr.currentAddressHi;

	if (currentAddr >= axLastAddr)
		axPlayedSamples += currentAddr - axLastAddr;
	else
		axPlayedSamples += ((AX_ARAM_LEFT_CHANNEL + AX_ARAM_BUFFER_SIZE) - axLastAddr) + (currentAddr - AX_ARAM_LEFT_CHANNEL);
		
	axLastAddr = currentAddr;

	//OSReport("%d\n",axPlayedSamples);
		
	if (axPlayedSamples >= VID_AUDIO_AIBUFFERSAMPLES)
	{
		audioPlayBufferNeededAudioFrames = axPlayedSamples / VID_AUDIO_AIBUFFERSAMPLES;

		// Make sure that we never get an underrun we don't notice...
		if(!(audioPlayBufferNeededAudioFrames <= VID_AUDIO_NUMAIBUFFERS))
		{
//			OSReport("AX audio buffer underrun!\n");
			audioPlayBufferEnabled = false;
		}
		
		//ASSERT(audioPlayBufferNeededAudioFrames <= VID_AUDIO_NUMAIBUFFERS);

		// Is actual audio playback enabled?
		if (audioPlayBufferEnabled)
		{
			// How many samples could we get from the read buffer?
			if (audioReadBufferWritePos >= audioReadBufferReadPos)
				availSamples = audioReadBufferWritePos - audioReadBufferReadPos;
			else
				availSamples = audioReadBufferNumSamples - (audioReadBufferReadPos - audioReadBufferWritePos);
			
			// That's how many audio frames?
			availFrames = availSamples / VID_AUDIO_AIBUFFERSAMPLES;
    
			// Are the voice already playing?
			if (axPhase == AX_PHASE_PLAY)
			{
				f32 targetFrq;
				
				// Yes. We better watch out for the buffer status, so we don't get under-runs...
				if (availFrames < axMinAvailFrames)
					targetFrq = audioPlayBufferFrq * (1.0f - (1.0f/200.0f) * (axMinAvailFrames - availFrames));
				else
					targetFrq = (f32)audioPlayBufferFrq;
				
				// Track the target frequency quite slowly to avoid audible artifacts
                if (axCurrentFrq < targetFrq)
				{
					axCurrentFrq += audioPlayBufferFrq * 0.00125f;
					if (axCurrentFrq > targetFrq)
						axCurrentFrq = targetFrq;
				}
				else
				{
					axCurrentFrq -= audioPlayBufferFrq * 0.00125f;
					if (axCurrentFrq < targetFrq)
						axCurrentFrq = targetFrq;
				}
				
				//OSReport("%d %f (%d)\n",availFrames,axCurrentFrq,axMinAvailFrames);
					
				// Set frequency
				AXSetVoiceSrcRatio(axVoice[0],axCurrentFrq / (f32)AX_IN_SAMPLES_PER_SEC);
				AXSetVoiceSrcRatio(axVoice[1],axCurrentFrq / (f32)AX_IN_SAMPLES_PER_SEC);
			}

			//OSReport("AX: %d %d (%d)\n",availSamples,audioPlayBufferNeededAudioFrames * VID_AUDIO_AIBUFFERSAMPLES,axPlayedSamples);
	
			// So, how many can we update?
			numUpdate = (availFrames > audioPlayBufferNeededAudioFrames) ? audioPlayBufferNeededAudioFrames : availFrames;
	
			// If anything... go do it!
			if (numUpdate != 0)
			{
				axPlayedSamples -= numUpdate * VID_AUDIO_AIBUFFERSAMPLES;
				
				// Perform updates on each AI buffer in need of data...
				for(i=0; iviYOrigin = 0;
			}
			break;
		default:
			break;
	}
	VISetBlack( TRUE );
    VIConfigure(rmode);
    VIFlush();
    VIWaitForRetrace();

	g_legal = false;

	unsigned short last = ( padData[0].button | padData[1].button );

	Pcm::StopMusic();

#	ifndef DVDETH
//	GXRenderModeObj *rmode;	//, demoMode;
	u32		videoWidth, videoHeight;
	s32		surfaceX, surfaceY;
	s32		surfaceWidth, surfaceHeight;
//	u16 	buttonsDown;
	f64		totalTime = 0.0;
	f64		idealTime = 0.0;
	f32		idealTimeInc;
	BOOL	first = TRUE;
	BOOL 	xfbMode = FALSE;

#	ifdef USE_DIRECT_XFB
	xfbMode = TRUE;
#	endif

	OSInitFastCast();



	// Reset a bunch of GX state.

	// Color definitions
	
	#define GX_DEFAULT_BG (GXColor){64, 64, 64, 255}
	#define BLACK (GXColor){0, 0, 0, 0}
	#define WHITE (GXColor){255, 255, 255, 255}
	
	//
	// Render Mode
	//
	// (set 'rmode' based upon VIGetTvFormat(); code not shown)
	
	//
	// Geometry and Vertex
	//
	GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
	GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX1, GX_IDENTITY);
	GXSetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_TEX2, GX_IDENTITY);
	GXSetTexCoordGen(GX_TEXCOORD3, GX_TG_MTX2x4, GX_TG_TEX3, GX_IDENTITY);
	GXSetTexCoordGen(GX_TEXCOORD4, GX_TG_MTX2x4, GX_TG_TEX4, GX_IDENTITY);
	GXSetTexCoordGen(GX_TEXCOORD5, GX_TG_MTX2x4, GX_TG_TEX5, GX_IDENTITY);
	GXSetTexCoordGen(GX_TEXCOORD6, GX_TG_MTX2x4, GX_TG_TEX6, GX_IDENTITY);
	GXSetTexCoordGen(GX_TEXCOORD7, GX_TG_MTX2x4, GX_TG_TEX7, GX_IDENTITY);
	GXSetNumTexGens(1);
	GXSetCurrentMtx(GX_PNMTX0);
	GXSetCullMode(GX_CULL_BACK);
	GXSetClipMode(GX_CLIP_ENABLE);
	GXSetNumChans(0); // no colors by default
	
	GXSetChanCtrl(
	GX_COLOR0A0,
	GX_DISABLE,
	GX_SRC_REG,
	GX_SRC_VTX,
	GX_LIGHT_NULL,
	GX_DF_NONE,
	GX_AF_NONE );
	
	GXSetChanAmbColor(GX_COLOR0A0, BLACK);
	GXSetChanMatColor(GX_COLOR0A0, WHITE);
	
	GXSetChanCtrl(
	GX_COLOR1A1,
	GX_DISABLE,
	GX_SRC_REG,
	GX_SRC_VTX,
	GX_LIGHT_NULL,
	GX_DF_NONE,
	GX_AF_NONE );
	
	GXSetChanAmbColor(GX_COLOR1A1, BLACK);
	GXSetChanMatColor(GX_COLOR1A1, WHITE);
	
	GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
	GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR0A0);
	GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD2, GX_TEXMAP2, GX_COLOR0A0);
	GXSetTevOrder(GX_TEVSTAGE3, GX_TEXCOORD3, GX_TEXMAP3, GX_COLOR0A0);
	GXSetTevOrder(GX_TEVSTAGE4, GX_TEXCOORD4, GX_TEXMAP4, GX_COLOR0A0);
	GXSetTevOrder(GX_TEVSTAGE5, GX_TEXCOORD5, GX_TEXMAP5, GX_COLOR0A0);
	GXSetTevOrder(GX_TEVSTAGE6, GX_TEXCOORD6, GX_TEXMAP6, GX_COLOR0A0);
	GXSetTevOrder(GX_TEVSTAGE7, GX_TEXCOORD7, GX_TEXMAP7, GX_COLOR0A0);
	GXSetTevOrder(GX_TEVSTAGE8, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
	GXSetTevOrder(GX_TEVSTAGE9, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
	GXSetTevOrder(GX_TEVSTAGE10,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
	GXSetTevOrder(GX_TEVSTAGE11,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
	GXSetTevOrder(GX_TEVSTAGE12,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
	GXSetTevOrder(GX_TEVSTAGE13,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
	GXSetTevOrder(GX_TEVSTAGE14,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
	GXSetTevOrder(GX_TEVSTAGE15,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
	GXSetNumTevStages(1);
	GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);
	GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
	for ( int i = GX_TEVSTAGE0; i < GX_MAX_TEVSTAGE; i++) {
	GXSetTevKColorSel((GXTevStageID) i, GX_TEV_KCSEL_1_4 );
	GXSetTevKAlphaSel((GXTevStageID) i, GX_TEV_KASEL_1 );
	GXSetTevSwapMode ((GXTevStageID) i, GX_TEV_SWAP0, GX_TEV_SWAP0 );
	}





//	demoMode = GXNtsc480IntDf;
//	demoMode.fbWidth   = 640; //512;
//	
//	_DEMOInit(&demoMode, xfbMode);
//	
//	// Init AI interface in case we got audio data
//	if ( !AICheckInit() ) AIInit(NULL);
//	if ( !ARCheckInit() ) ARInit(NULL,0);
//	if ( !ARQCheckInit() ) ARQInit();
//	AXInit();

	// If we want to use the 'Locked Cache', we need to enable it here!
	// The usage of 'Locked Cache' is optional, but speeds up decoding time a little bit
	LCEnable();
	lcMemBase = (u8*)LCGetBase();
	
	// In xfb mode, we need some memory for the conversion stuff.
	// We offset our locked cache 'base addr' by the required number of bytes!
	// NOTE: This mem is only used TEMPORARY during the VIDXFBDraw() function!
	if(xfbMode)
		{
		xfbLCStart = lcMemBase;
		lcMemBase += VIDXFBGetLCSize();
		}

//	rmode = DEMOGetRenderModeObj();

	VIDSimpleInit(myAlloc, myFree, myAllocFromLC);

//	if (VIDSimpleOpen(VIDEO_FILENAME, FALSE) == FALSE)




	// Incoming movie name is in the form movies\, convert to movies/vid\.vid.
	char name_conversion_buffer[256] = "movies/vid/";
	int length		= strlen( pName );
	int backwards	= length;
	while( backwards )
	{
		if( pName[backwards] == '\\' )
		{
			++backwards;
			break;
		}
		--backwards;
	}
	strncpy( name_conversion_buffer + 11, pName + backwards, length - backwards );
	length = strlen( name_conversion_buffer );
	name_conversion_buffer[length] = '.';
	name_conversion_buffer[length + 1] = 'v';
	name_conversion_buffer[length + 2] = 'i';
	name_conversion_buffer[length + 3] = 'd';
	name_conversion_buffer[length + 4] = 0;







	if (VIDSimpleOpen(name_conversion_buffer, FALSE) == FALSE) goto quit;
//		OSHalt("*** VIDSimpleOpen failed!\n");

	if (VIDSimpleAllocDVDBuffers() == FALSE)
	{
		VIDSimpleLoadStop();
		VIDSimpleClose();

        goto quit;
	}
//		OSHalt("*** VIDSimpleAllocDVDBuffers failed!\n");
	
	if (VIDSimpleCreateDecoder(TRUE) == FALSE)
	{
		VIDSimpleLoadStop();
		VIDSimpleClose();
		VIDSimpleFreeDVDBuffers();

        goto quit;
	}
//		OSHalt("*** VIDSimpleCreateDecoder failed!\n");
		
	if (VIDSimpleInitAudioDecoder())
	{
		VIDSimpleLoadStop();
		VIDSimpleClose();
		VIDSimpleFreeDVDBuffers();
		VIDSimpleDestroyDecoder();

        goto quit;
	}
//		OSHalt("*** VIDSimpleInitAudioDecoder failed!\n");






	// Preload all DVD buffers and set 'loop' mode
//	VIDSimpleLoadStart(TRUE);
	VIDSimpleLoadStart(FALSE);

	VIDSimpleGetVideoSize(&videoWidth, &videoHeight);
	
	// Calculate width and height to be used to display movie
	surfaceWidth = rmode->fbWidth;
	surfaceHeight = (s32)(videoHeight * ((f32)rmode->fbWidth / (f32)videoWidth));
	
	// Get the pixels "square"
	surfaceHeight = (s32)(surfaceHeight * (640.0f / (f32)rmode->fbWidth));	// assuming 480 vertical at all times!
	
	// Calculate offset to put it into the center of the screen
	surfaceX = 0;
	surfaceY = ((s32)rmode->xfbHeight - (s32)surfaceHeight) / 2;
	if (surfaceY < 0)
		surfaceY = 0;
	
//#ifdef MY_DEBUG
//	OSReport("Resolution: %dx%d\n",videoWidth,videoHeight);
//	OSReport("Display resolution: %dx%d\n",surfaceWidth,surfaceHeight);
//	OSReport("Rate: %f fps\n",VIDSimpleGetFPS());
//	OSReport("Frames: %d\n",VIDSimpleGetFrameCount());
//	VIDSimpleAudioGetInfo(&header);
//	if(header.audioID != VID_FCC('N','O','N','E'))
//		{
//		OSReport("Audio codec: %4s\n", &header.audioID);
//		OSReport("Channels: %d\n", (u32)header.pcm16.numChannels);
//		OSReport("Sample rate: %d Hz\n", header.pcm16.frq);
//		}
//#endif

	idealTimeInc = 1000.0f / VIDSimpleGetFPS();

		while (1)
		{
		OSTick startTicks;
	
		startTicks = OSGetTick();

		VISetBlack( FALSE );
		NsDisplay::begin();
		NsRender::begin();
//		_DEMOBeforeRender();
		
		// Decode one frame
		if (VIDSimpleDecode() == FALSE)
		{
			// Decode failed, usually just due to the end of the movie being reached.
			break;
		}

		// We deomstrate two different drawing methods
		if(!xfbMode)
		{
			// Render decoded frame as texture. YUV conversion is handled by hardware.
			VIDSimpleDraw(rmode, (u32)surfaceX, (u32)surfaceY, (u32)surfaceWidth, (u32)surfaceHeight);
		}
		
		// Wait for frame buffer swap to be done (we might have triggered one in the last loop)
//		_DEMOWaitFrameBuffer();
		
		if (xfbMode)
			{
			// Render image into the XFB immediately
			VIDSimpleDrawXFB(rmode, xfbLCStart);
			}
			
		s32 error;
		error = DVDGetDriveStatus();
		if ( ( error != DVD_STATE_END ) &&
			 ( error != DVD_STATE_BUSY ) &&
			 ( error != DVD_STATE_CANCELED ) &&
			 ( error != DVD_STATE_PAUSING ) &&
			 ( error != DVD_STATE_WAITING ) )


			{
				switch (VIGetTvFormat())
				{
					case VI_PAL:
					case VI_DEBUG_PAL:
						if ( !NxNgc::EngineGlobals.use_60hz )
						{
							rmode->viYOrigin = 23;
						}
						break;
					default:
						break;
				}
				VIConfigure(rmode);

				// Stop audio, turn volume to 0.
				AXSetVoiceState(axVoice[0],AX_PB_STATE_STOP);
				AXSetVoiceState(axVoice[1],AX_PB_STATE_STOP);

				hwGXInit();
	
				GXSetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
				GXSetColorUpdate(GX_ENABLE);
				GXSetAlphaUpdate(GX_ENABLE);

				NxNgc::EngineGlobals.screen_brightness = 1.0f;

				// Display n
				while ( 1 )
				{
					error = DVDGetDriveStatus();
//					printf( "Error code: %d\n", error );

					NsDisplay::doReset();

					if ( error == DVD_STATE_END ) break;
					if ( error == DVD_STATE_BUSY ) continue;
					if ( error == DVD_STATE_CANCELED ) break;
					if ( error == DVD_STATE_PAUSING ) continue;
					if ( error == DVD_STATE_WAITING ) continue;

					// Render the text.
					NsDisplay::begin();
					NsRender::begin();
	
					NsCamera cam;
					cam.orthographic( 0, 0, rmode->fbWidth, 448 );
	
					// Draw the screen.
					NsPrim::begin();
	
					cam.begin();
	
					GXSetZMode( GX_FALSE, GX_ALWAYS, GX_TRUE );
	
					NxNgc::set_blend_mode( NxNgc::vBLEND_MODE_BLEND );
	
		//			if ( NsDisplay::shouldReset() )
		//			{
		//				// Reset message.
		//				Script::RunScript( "ngc_reset" );
		//			}
		//			else
					{
						// DVD Error message.
						switch ( error )
						{
							case DVD_STATE_FATAL_ERROR:
								Script::RunScript( "ngc_dvd_fatal" );
								NxNgc::EngineGlobals.disableReset = true;
								break;
							case DVD_STATE_RETRY:
								Script::RunScript( "ngc_dvd_retry" );
								break;
							case DVD_STATE_COVER_OPEN:
								Script::RunScript( "ngc_dvd_cover_open" );
								NxNgc::EngineGlobals.disableReset = false;
								break;
							case DVD_STATE_NO_DISK:
								Script::RunScript( "ngc_dvd_no_disk" );
								NxNgc::EngineGlobals.disableReset = false;
								break;
							case DVD_STATE_WRONG_DISK:
								Script::RunScript( "ngc_dvd_wrong_disk" );
								NxNgc::EngineGlobals.disableReset = false;
								break;
							default:
								break;
						}
					}
	
					NsDisplay::setBackgroundColor( messageColor );
	
					cam.end();
	
					NsPrim::end();
	
					NsRender::end();
					NsDisplay::end( true );
				}

				// Reset a bunch of GX state.
			
				NsDisplay::setBackgroundColor( (GXColor){0,0,0,0} );

				// Color definitions
				
				#define GX_DEFAULT_BG (GXColor){64, 64, 64, 255}
				#define BLACK (GXColor){0, 0, 0, 0}
				#define WHITE (GXColor){255, 255, 255, 255}
				
				//
				// Render Mode
				//
				// (set 'rmode' based upon VIGetTvFormat(); code not shown)
				
				//
				// Geometry and Vertex
				//
				GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);
				GXSetTexCoordGen(GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX1, GX_IDENTITY);
				GXSetTexCoordGen(GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_TEX2, GX_IDENTITY);
				GXSetTexCoordGen(GX_TEXCOORD3, GX_TG_MTX2x4, GX_TG_TEX3, GX_IDENTITY);
				GXSetTexCoordGen(GX_TEXCOORD4, GX_TG_MTX2x4, GX_TG_TEX4, GX_IDENTITY);
				GXSetTexCoordGen(GX_TEXCOORD5, GX_TG_MTX2x4, GX_TG_TEX5, GX_IDENTITY);
				GXSetTexCoordGen(GX_TEXCOORD6, GX_TG_MTX2x4, GX_TG_TEX6, GX_IDENTITY);
				GXSetTexCoordGen(GX_TEXCOORD7, GX_TG_MTX2x4, GX_TG_TEX7, GX_IDENTITY);
				GXSetNumTexGens(1);
				GXSetCurrentMtx(GX_PNMTX0);
				GXSetCullMode(GX_CULL_BACK);
				GXSetClipMode(GX_CLIP_ENABLE);
				GXSetNumChans(0); // no colors by default
				
				GXSetChanCtrl(
				GX_COLOR0A0,
				GX_DISABLE,
				GX_SRC_REG,
				GX_SRC_VTX,
				GX_LIGHT_NULL,
				GX_DF_NONE,
				GX_AF_NONE );
				
				GXSetChanAmbColor(GX_COLOR0A0, BLACK);
				GXSetChanMatColor(GX_COLOR0A0, WHITE);
				
				GXSetChanCtrl(
				GX_COLOR1A1,
				GX_DISABLE,
				GX_SRC_REG,
				GX_SRC_VTX,
				GX_LIGHT_NULL,
				GX_DF_NONE,
				GX_AF_NONE );
				
				GXSetChanAmbColor(GX_COLOR1A1, BLACK);
				GXSetChanMatColor(GX_COLOR1A1, WHITE);
				
				GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
				GXSetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR0A0);
				GXSetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD2, GX_TEXMAP2, GX_COLOR0A0);
				GXSetTevOrder(GX_TEVSTAGE3, GX_TEXCOORD3, GX_TEXMAP3, GX_COLOR0A0);
				GXSetTevOrder(GX_TEVSTAGE4, GX_TEXCOORD4, GX_TEXMAP4, GX_COLOR0A0);
				GXSetTevOrder(GX_TEVSTAGE5, GX_TEXCOORD5, GX_TEXMAP5, GX_COLOR0A0);
				GXSetTevOrder(GX_TEVSTAGE6, GX_TEXCOORD6, GX_TEXMAP6, GX_COLOR0A0);
				GXSetTevOrder(GX_TEVSTAGE7, GX_TEXCOORD7, GX_TEXMAP7, GX_COLOR0A0);
				GXSetTevOrder(GX_TEVSTAGE8, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
				GXSetTevOrder(GX_TEVSTAGE9, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
				GXSetTevOrder(GX_TEVSTAGE10,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
				GXSetTevOrder(GX_TEVSTAGE11,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
				GXSetTevOrder(GX_TEVSTAGE12,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
				GXSetTevOrder(GX_TEVSTAGE13,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
				GXSetTevOrder(GX_TEVSTAGE14,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
				GXSetTevOrder(GX_TEVSTAGE15,GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
				GXSetNumTevStages(1);
				GXSetTevOp(GX_TEVSTAGE0, GX_REPLACE);
				GXSetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0);
				for ( int i = GX_TEVSTAGE0; i < GX_MAX_TEVSTAGE; i++) {
				GXSetTevKColorSel((GXTevStageID) i, GX_TEV_KCSEL_1_4 );
				GXSetTevKAlphaSel((GXTevStageID) i, GX_TEV_KASEL_1 );
				GXSetTevSwapMode ((GXTevStageID) i, GX_TEV_SWAP0, GX_TEV_SWAP0 );

				// Restart audio, volume to full.
				AXSetVoiceState(axVoice[0],AX_PB_STATE_RUN);
				AXSetVoiceState(axVoice[1],AX_PB_STATE_RUN);

				}

				switch (VIGetTvFormat())
				{
					case VI_PAL:
					case VI_DEBUG_PAL:
						if ( !NxNgc::EngineGlobals.use_60hz )
						{
							rmode->viYOrigin = 0;
						}
						break;
					default:
						break;
				}
				VIConfigure(rmode);

//			OSHalt("*** DVD error occured. A true DVD error handler is not part of this demo.\n");
				startTicks = OSGetTick();
			}
			
		// Final processing for this frame (copy out etc.)
//		_DEMODoneRender();
		NsRender::end();
		NsDisplay::end();

//		// handle GC controller
//		DEMOPadRead();
//		buttonsDown = DEMOPadGetButtonDown(0);
//		if(buttonsDown & PAD_BUTTON_START)
//			break;
			
		unsigned short current = ( padData[0].button | padData[1].button );
		unsigned short press = ( current ^ last ) & current;
		last = current;

		if ( press & ( PAD_BUTTON_START | PAD_BUTTON_A ) ) break;

		// Control timing
		if (!first)
			{
			f64	dt;
			idealTime += 1000.0 / VIDSimpleGetFPS();
			do
				{
				dt = OSTicksToMilliseconds((f64)(OSGetTick() - startTicks));
				}
			while((totalTime + dt) < idealTime);
			totalTime += dt;
			}
		else
			first = FALSE;
			
		} // while (1)
		
	// free our ressources. Just as demonstration what you need to do for a 'clean' exit
	VIDSimpleLoadStop();
	VIDSimpleClose();

	VIDSimpleFreeDVDBuffers();
	VIDSimpleDestroyDecoder();
	VIDSimpleExitAudioDecoder();

quit:
	NsDisplay::setBackgroundColor( (GXColor){0,0,0,0} );

//	GXSetPixelFmt(/*GX_PF_RGB8_Z24*/GX_PF_RGBA6_Z24, GX_ZC_LINEAR);

	GQRSetup6( GQR_SCALE_64,		// Pos
			   GQR_TYPE_S16,
			   GQR_SCALE_64,
			   GQR_TYPE_S16 );
	GQRSetup7( 14,		// Normal
			   GQR_TYPE_S16,
			   14,
			   GQR_TYPE_S16 );

	hwGXInit();

	GXSetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
	GXSetColorUpdate(GX_ENABLE);
	GXSetAlphaUpdate(GX_ENABLE);

	// Register our default AX callback.
	AXRegisterCallback( Sfx::AXUserCBack );

	LCDisable();

#	ifdef MY_DEBUG
	OSReport("VidPlayer done!\n");	
#	endif	

#	endif		// DVDETH

	switch (VIGetTvFormat())
	{
		case VI_PAL:
		case VI_DEBUG_PAL:
			if ( !NxNgc::EngineGlobals.use_60hz )
			{
				rmode->viYOrigin = 23;
			}
			break;
		default:
			break;
	}
	VISetBlack( TRUE );
    VIConfigure(rmode);
    VIFlush();
    VIWaitForRetrace();
	VISetBlack( FALSE );
}
	

bool Movie_Render ( void )
{
//	if ( !playing ) return false;
//
//	//
//	// Decompress the Bink frame.
//	//
//	
//	BinkDoFrame( Bink );
//	
//	//
//	// Draw the next frame.
//	//
//	
//	Show_frame( Bink );
//	
//	//
//	// Keep playing the movie.
//	//
//	
//	BinkNextFrame( Bink );
//
//	return true;

	return false;
}

} // namespace Flx






================================================
FILE: Code/Gel/Movies/ngc/p_movies.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Movies													**
**																			**
**	File name:		gel/movies/ngc/p_movies.h 								**
**																			**
**	Created: 		8/27/01	-	dc											**
**																			**
*****************************************************************************/

#ifndef __P_MOVIES_H
#define __P_MOVIES_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

namespace Flx
{

void PMovies_PlayMovie( const char *pName );

bool Movie_Render( void );

} // namespace Flx

#endif	// __P_MOVIES_H





================================================
FILE: Code/Gel/Music/Ngps/Bgm/PathDefs
================================================
 AR      = C:/usr/local/sce/iop/gcc/bin/iop-elf-ar
 AS      = C:/usr/local/sce/iop/gcc/mipsel-scei-elfl/bin/as
 CC      = C:/usr/local/sce/iop/gcc/bin/iop-elf-gcc
 GCC     = C:/usr/local/sce/iop/gcc/bin/iop-elf-gcc
 LD      = C:/usr/local/sce/iop/gcc/mipsel-scei-elfl/bin/ld
 NM      = C:/usr/local/sce/iop/gcc/bin/iop-elf-nm
 SIZE    = C:/usr/local/sce/iop/gcc/bin/iop-elf-size
 STRIP   = C:/usr/local/sce/iop/gcc/bin/iop-elf-strip
 RANLIB  = C:/usr/local/sce/iop/gcc/bin/iop-elf-ranlib
 OBJCOPY = C:/usr/local/sce/iop/gcc/bin/iop-elf-objcopy
 OBJDUMP = C:/usr/local/sce/iop/gcc/bin/iop-elf-objdump
 IFIXUP  = iopfixup
 ILBGEN  = ioplibgen
 ILBLD   = ioplibld
 ILBDUMP = ioplibdump
 BIN2OBJ = bin2elf


================================================
FILE: Code/Gel/Music/Ngps/Bgm/bgm_com.c
================================================
// This code builds an IRX module that handles PCM streaming from
// the CD to SPU2 Sound Data Input area...

#include 
#include 
#include 
#include "sif.h"
#include "sifcmd.h"
#include "sifrpc.h"
#include 
#include "bgm_i.h"

int gRpcArg[16];	//--- Receiving channel for RPC arguments transferred from EE


static void* bgmFunc(unsigned int fno, void *data, int size);

extern int  BgmInit(int ch, int useCD);
extern void BgmQuit(int channel, int status );
extern int  BgmOpen( int ch, char* filename );
extern void BgmClose(int ch, int status);
extern void BgmCloseNoWait(int ch, int status);
extern int  BgmPreLoad( int ch, int status );
extern void BgmStart(int ch, int status);
extern void BgmStop(int ch, int status);
extern void BgmSeek( int ch, unsigned int vol );
extern int BgmSetVolume( int ch, unsigned int vol );
extern int BgmRaw2Spu( int ch, int which, int mode );
extern int BgmSetVolumeDirect( int ch, unsigned int vol );
// fuck this you big assbreath.
//extern void BgmSetMasterVolume( int ch, unsigned int vol );
extern unsigned int BgmGetMode( int ch, int status );
//extern void BgmSetMode( int ch, u_int mode );
extern void BgmSdInit(int ch, int status );


/* ------------------------------------------------------------------------
   Main thread for the ezbgm module.
   After execution, initialize interrupt environment, register command, and
   then wait until there is a request from the EE.
   ------------------------------------------------------------------------ */
int sce_bgm_loop()
{
	sceSifQueueData qd;
	sceSifServeData sd;

	//-- Initialize interrupt environment in advance.

	CpuEnableIntr();
	EnableIntr( INUM_DMA_4 );
	EnableIntr( INUM_DMA_7 );
	
	//--- Register function that is called according to request


	sceSifInitRpc(0);

	sceSifSetRpcQueue( &qd, GetThreadId() );
	sceSifRegisterRpc( &sd, EZBGM_DEV, bgmFunc, (void*)gRpcArg, NULL, NULL, &qd );
	PRINTF(("goto bgm cmd loop\n"));

	//--- Command-wait loop

	sceSifRpcLoop(&qd);

	return 0;
}


/* ------------------------------------------------------------------------
   Function that is awakened by a request from the EE.
   The argument is stored in *data.  The leading four bytes are reserved and are not used.
   The return value of this function becomes the return value of the EE side's RPC.
   When the argument is a structure, it is sent to gRpcData and used.
   When a structure is returned to the EE, the value is sent to the address of the first argument (on the EE side).
   ------------------------------------------------------------------------ */
int ret = 0;

static void* bgmFunc(unsigned int command, void *data, int size)
{ 
	int ch;

//	asm volatile( "break 1");

//	PRINTF(( " bgmfunc %x, %x, %x, %x\n", *((int*)data + 0), 
//		*((int*)data + 1), *((int*)data + 2),*((int*)data + 3) ));

	ch = command&0x000F;

	switch( command&0xFFF0 )
	{
	case EzBGM_INIT:
		ret = BgmInit( ch, *((int*)data) );
		break;
	case EzBGM_QUIT:
		BgmQuit ( ch, *((int*)data) );
		break;
	case EzBGM_OPEN:
		ret = BgmOpen ( ch, (char*)((int*)data) );
		break;
	case EzBGM_CLOSE:
		BgmClose( ch, *((int*)data) );
		break;
	case EzBGM_CLOSE_NO_WAIT:
		BgmCloseNoWait( ch, *((int*)data) );
		break;
	case EzBGM_PRELOAD:
		ret = BgmPreLoad ( ch, *((int*)data) );
		break;
	case EzBGM_START:
		BgmStart( ch, *((int*)data) );
		break;
	case EzBGM_STOP:
		BgmStop( ch, *((int*)data) );
		break;
	case EzBGM_SEEK:
		BgmSeek( ch, (unsigned int)*((int*)data) );
		break;
	case EzBGM_SETVOL:
		ret = BgmSetVolume( ch, (unsigned int)*((int*)data) );
		break;
	case EzBGM_SETVOLDIRECT:
		ret = BgmSetVolumeDirect( ch, (unsigned int)*((int*)data) );
		break;
	case EzBGM_GETMODE:
		ret = BgmGetMode( ch, *((int*)data) );
		break;
	case EzBGM_SDINIT:
		BgmSdInit( ch, *((int*)data) );
		break;
	default:
		ERROR(("EzBGM driver error: unknown command %d \n", *((int*)data) ));
		break;
	}
//	PRINTF(( "return value = %x \n", ret )); 
	return (void*)(&ret);
}


/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* DON'T ADD STUFF AFTER THIS */



================================================
FILE: Code/Gel/Music/Ngps/Bgm/bgm_entr.c
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "bgm_i.h"

ModuleInfo Module = {"pcmaudio_driver", 0x0102 };

extern int sce_bgm_loop();
extern volatile int gStThid;

int create_th();

int start ()
{
	struct ThreadParam param;
	int th;

	CpuEnableIntr();

	if( ! sceSifCheckInit() )
		sceSifInit();
	sceSifInitRpc(0);

	printf("Entering PCM Audio Driver\n");

	param.attr         = TH_C;
	param.entry        = sce_bgm_loop;
	param.initPriority = BASE_priority-2;
	param.stackSize    = 0x800;
	param.option       = 0;
	th = CreateThread(¶m);
	if (th > 0) {
		StartThread(th,0);
		printf("Exit PCM Audio Driver\n");
		return 0;
	}else{
		return 1;
	}
}


/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* DON'T ADD STUFF AFTER THIS */



================================================
FILE: Code/Gel/Music/Ngps/Bgm/bgm_i.h
================================================
//--- Highest bit indicates presence of return value
#define EzBGM_INIT           0x8000
#define EzBGM_QUIT           0x0010
#define EzBGM_OPEN           0x8020
#define EzBGM_CLOSE          0x0030
#define EzBGM_PRELOAD        0x0040
#define EzBGM_START          0x0050
#define EzBGM_STOP           0x0060
#define EzBGM_SEEK           0x0070
#define EzBGM_SETVOL         0x8080
#define EzBGM_SETVOLDIRECT   0x8090
#define EzBGM_CLOSE_NO_WAIT  0x0100
//#define EzBGM_SETMASTERVOL 0x00a0
#define EzBGM_GETMODE        0x80b0
#define EzBGM_SETMODE        0x80c0
#define EzBGM_SDINIT         0x00d0


//-- SET AVAILABLE
#define BGM_MODE_REPEAT_OFF      0x0000
#define BGM_MODE_REPEAT_DEFAULT  0x0001
#define BGM_MODE_REPEAT_FORCED   0x0002

#define BGM_MODE_STEREO          0x0000
#define BGM_MODE_MONO            0x0010

//-- GET ONLY
#define BGM_MODE_IDLE            0x0000
#define BGM_MODE_RUNNING         0x1000
#define BGM_MODE_PAUSE           0x2000
//#define BGM_MODE_KICKSTART       0x4000
#define BGM_MODE_TERMINATE       0x8000


#define BGM_MASK_STATUS          0x0FFF
#define BGM_MASK_REPEAT          0xFFF0
#define BGM_MASK_STEREO          0xFF0F

#define WAV_STEREO_BIT           ( 1 << 1 )


/* ----------------------------------------------
   module ID number
  ----------------------------------------------- */
#define EZBGM_DEV   0x000666


#define PRINTF(x) printf x
//#define PRINTF(x) 

#define ERROR(x) printf x
//#define ERROR(x) 

#define BASE_priority  42

#define OLDLIB 0
#define TRANS_CH  0



================================================
FILE: Code/Gel/Music/Ngps/Bgm/bgm_play.c
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "bgm_i.h"

#define DEBUGGING_PLEASE 0
#if DEBUGGING_PLEASE
#define Dbg_Printf(A...)  printf(##A) 		
#else
#define Dbg_Printf(A...)
#endif

// wav format ------------------------
#define RIFF_HEADER_SIZE 44
typedef struct {
	unsigned char     chunkID[4];
	unsigned int      chunkSize;
	unsigned short*   data;
} DATAHeader;

typedef struct {
	unsigned char     chunkID[4];
	unsigned int      chunkSize;
	unsigned short    waveFmtType;
	unsigned short    channel;
	unsigned int      samplesPerSec;
	unsigned int      bytesPerSec;
	unsigned short    blockSize;
	unsigned short    bitsPerSample;
} FMTHeader;

typedef struct {
	unsigned char     chunkID[4];
	unsigned int      chunkSize;
	unsigned char     formType[4];
	FMTHeader         fmtHdr;
	DATAHeader        dataHdr;
} RIFFHeader;
//------------------------------------

typedef struct {
	unsigned int     size;
//	unsigned int     offset;
	unsigned int     pos;
} BGM_WAVE;


BGM_WAVE gWave[2];

int gMemInitializedForCD = 0;
short gBgmPause[2] = { 0, 0 };
short gBgmPreload[ 2 ] = { 0, 0 };
char gFileFromCD[ 2 ] = { 0, 0 };
char gBgmVolumeSet[2] = { 0, 0 };
volatile char gFileOpened[2] = { 0, 0 };
volatile char gBgmIntr[2] = { 0, 0 };
int gThid = 0;
int gSem = 0;
int gFd[2];
int gBuffRaw[2];
int gBuffSpu[2] = {0, 0};
int gRPacketSize[2];
int gSPacketSize[2];
int gAllockedSize[2];
int gBgmVolume[2];
int gBgmMode[2] = { BGM_MODE_REPEAT_OFF, BGM_MODE_REPEAT_OFF };
int gBgmStereoMode[ 2 ] = { 0, 0 };

#define MAX_FILENAME_LENGTH		50
char gFilename[ 2 ][ MAX_FILENAME_LENGTH ];

// Larger buffers will prevent skipping when loading in the background...
// The more streams, the larger the buffers have to be.
// Reading from the CDVD will be faster than debug stations from the host over the network,
// so don't freak if you notice corruption on a dev system w/o CD.
#define SECTOR_SIZE					2048
#define NUM_SECTORS_PER_READ		2
#define BUFFER_SIZE_FOR_HOST_FILE	( 2048 * 132 ) // multiple of 6
#define BUFFER_SIZE_FOR_CD_FILE		( SECTOR_SIZE * NUM_SECTORS_PER_READ * 6 )

#define RING_BUFFER_NUM_SECTORS		48
#define RING_BUFFER_ALLIGNMENT		64
#define RING_BUFFER_NUM_PARTITIONS	3
unsigned char gRingBuffer[ 2 ][ RING_BUFFER_NUM_SECTORS * 2048 ] __attribute__((aligned ( RING_BUFFER_ALLIGNMENT )));
unsigned char gCDBuffSpu[ 2 ][ BUFFER_SIZE_FOR_CD_FILE ]__attribute__((aligned ( 64 )));

int  _BgmPlay( int status );
void _BgmRaw2Spu(u_long *src, u_long *dst,  u_long block_count );
void _BgmRaw2SpuMono(u_long *src, u_long *dst,  u_long block_count );
int	BgmPreLoad( int ch, int status );
void SetStereoOn( int ch, int stereo );

static int IntFunc0( void* common )
{
	gBgmIntr[0] = 1;
	iSignalSema( gSem );
	return 1;  //-- Required for re-enabling interrupts
}

static int IntFunc1( void* common )
{
	gBgmIntr[1] = 1;
	iSignalSema( gSem );
	return 1;  //-- Required for re-enabling interrupts
}


static int makeMyThread( void )
{
    struct ThreadParam param;
    int	thid;

    param.attr         = TH_C;
    param.entry        = _BgmPlay;
    param.initPriority = BASE_priority-3;
    param.stackSize    = 0x800;
    param.option = 0;

    /* Create thread */
	thid = CreateThread(¶m);

	return thid;
}

static int makeMySem( void )
{
    struct SemaParam sem;

    sem.initCount = 0;
    sem.maxCount = 1;
    sem.attr = AT_THFIFO;

    /* Create semaphore.  */
    return CreateSema(&sem);
}


int BgmRaw2Spu( int ch, int which )
{
    if ( gBgmStereoMode[ ch ] )
	{
		_BgmRaw2Spu( (u_long *)(gBuffRaw[ch]+ (gRPacketSize[ch])*which)
			, (u_long *)(gBuffSpu[ch] + (gSPacketSize[ch])*which)
			, gSPacketSize[ch]/1024 );
	}
	else
	{
		_BgmRaw2SpuMono ((u_long *)(gBuffRaw [ch] + (gRPacketSize [ch]) * which),
				 (u_long *)(gBuffSpu [ch] + (gSPacketSize [ch]) * which),
				 gSPacketSize [ch] / 1024);
    }
	return 0;
}

// returns zero if no longer playing...
int BgmSetVolumeDirect( int ch, unsigned int vol )
{
	sceSdSetParam( (ch)|SD_P_BVOLR, vol>>16 );
	sceSdSetParam( (ch)|SD_P_BVOLL, vol&0xFFFF);
	return ( gBgmMode[ch] & ~BGM_MASK_STATUS );
}



void BgmSdInit(int ch, int status )
{
	// This is already called from the soundFX module.
	//sceSdInit(0);
	return;
}


int BgmInit( int ch, int useCD )
{

	if( gSem == 0 ){
		gSem = makeMySem();
	}
	if( gThid == 0 ){
		gThid = makeMyThread();
		Dbg_Printf("PCM Audio Streamer: create thread ID= %d, ", gThid );
		/* Activate thread  */
		StartThread( gThid, (u_long)NULL );
	}

	if ( !gBuffSpu[ ch ] )
	{
		gAllockedSize[ch] = useCD ? BUFFER_SIZE_FOR_CD_FILE : BUFFER_SIZE_FOR_HOST_FILE;
		if ( useCD )
		{
			gBuffSpu[ ch ] = ( int )gCDBuffSpu[ ch ]; //(int)AllocSysMemory( 0, gAllockedSize[ ch ], NULL );
		}
		else
		{
			gBuffSpu[ ch ] = ( int )AllocSysMemory( 0, gAllockedSize[ ch ], NULL );
		}
		Dbg_Printf(" PCM spu buffer mem: 0x%x - 0x%x\n", gBuffSpu[ch], gBuffSpu[ch]+gAllockedSize[ ch ] );
	}
	// the memory should work for mono or stereo 44k files ( raw buffer always the same... )
	gRPacketSize[ ch ] = gAllockedSize[ ch ] / 6;
	gBuffRaw[ ch ] = ( int )( gBuffSpu[ ch ] + ( gRPacketSize[ ch ] * 4 ) );
	gMemInitializedForCD = useCD;

	return  gThid;
}

void BgmQuit(int ch, int status )
{
	if ( ( gBuffSpu[ ch ] ) && ( gBuffSpu[ ch ] != ( int ) gCDBuffSpu ) )
		FreeSysMemory( (void*)gBuffSpu[ch] );
	gBuffSpu[ch] = 0;
	//-- Frees resources unless another channel is used.
	if( gBuffSpu[1-ch] == 0 ){
		if (gThid != 0) TerminateThread( gThid );
		if (gSem != 0) DeleteSema( gSem );
		if (gThid != 0) DeleteThread( gThid );
		gThid = 0;
		gSem = 0;
	}
	return;
}

/*
void WaitForCDReady( char *description )
{
	printf( "wait %s", description );
	while( SCECdComplete != sceCdDiskReady( 1 ) )
	{
		printf( "." );
	}
	printf( "\n" );
}*/

#define NUM_BYTES_TO_CLEAR	512 // must be divisible by 4!!!

int BgmOpen( int data, char* filename )
{
//	unsigned int channel;
	RIFFHeader wavHdr;
	int offset;
	int *addr;
	int i;
	int ch = data & 1;
	int stereo = data & WAV_STEREO_BIT;
	
	SetStereoOn( ch, stereo );

	for ( i = 0; i < 2; i++ )
	{
		if ( gFileOpened[ i ] )
		{
			printf( "%s: Failed: Audio stream already running: %s\n", filename, gFilename[ i ] );
//			Dbg_Printf( "File already opened on that channel...\n" );
			return -1;
		}
	}
	printf("pcm filename %s\n", filename );
	
	if ( !strncmp( filename, "host", 4 ) )
	{
		if ( gMemInitializedForCD )
		{
			printf( "Loading file from host when mem configured for CD... SOUND QUALITY WILL SUCK.\n" );
			printf( "If calling ezBGM_INIT with useCD (2nd param), send CDROM0 in filename...\n" );
		}
		
		// load the file from the dev system...
		if (( gFd[ch] = open ( filename, O_RDONLY)) < 0)
		{
			ERROR(("file open failed. %s \n", filename));
			return -1;
		}
		
		if ( read (gFd[ch], (unsigned char*)(&wavHdr)
			,RIFF_HEADER_SIZE ) != RIFF_HEADER_SIZE ) {
			ERROR(("file read failed. %s \n", filename));
			return -1;
		}
		if ( wavHdr.fmtHdr.channel == 2 )
		{
			if ( !stereo )
				printf( "WARNING: Playing stereo sound %s in mono mode.\n", filename );
		}
		else if ( stereo )
		{
			printf( "WARNING: Playing mono sound %s in stereo mode.\n", filename );
		}
		
		gWave[ch].size   = wavHdr.dataHdr.chunkSize;
		gWave[ch].pos    = 0;
	
		//--- Seek to start of data
		offset = (unsigned int)&(wavHdr.dataHdr.data) -(unsigned int)&wavHdr;
	
		lseek( gFd[ ch ], offset , SEEK_SET );
	
		gFileFromCD[ ch ] = 0;
		gFileOpened[ ch ] = 1;
		gBgmPreload[ ch ] = 3;
	}
	else
	{
		sceCdlFILE fp;
		sceCdRMode mode;
		int retVal;
		unsigned int stringLength;
		unsigned int lsn;
		int foundCDData = 0;
		int byteIndex;
		// open a stream and load from CD...
		// 1st arg: size in num sectors ( actual size / 2048 ).
//		WaitForCDReady( "st init" );
//		sceCdDiskReady( 0 );
		sceCdStInit( RING_BUFFER_NUM_SECTORS, RING_BUFFER_NUM_PARTITIONS, ( u_int )( gRingBuffer[ ch ] ) ); //+ ( RING_BUFFER_ALLIGNMENT - 1 ) )  );
//		printf( "wait for ready." );
//		sceCdDiskReady( 0 );
//		WaitForCDReady( "init" );
//		printf( "cdSearchFile\n" );
		stringLength = strlen( filename );
		if ( filename[ stringLength + 1 ] == '@' )
		{
			foundCDData = 1;
			lsn = 0;
			for ( byteIndex = 0; byteIndex < 4; byteIndex++ )
			{
				lsn |= ( ( ( unsigned int )( filename[ stringLength + 2 + byteIndex ] ) ) & 255 ) << ( 8 * byteIndex );
			}
		}
		if ( !foundCDData )
		{
			if ( !( retVal = sceCdSearchFile( &fp, filename ) ) )
			{
				ERROR(("cd filesearch failed %d. file %s \n", retVal, filename));
				return -1;
			}
			lsn = fp.lsn;
		}
	
//		WaitForCDReady( "find" );
//		sceCdDiskReady( 0 );
		mode.trycount = 0;
		mode.spindlctrl = SCECdSpinNom;
		mode.datapattern = SCECdSecS2048;
		sceCdStStart( lsn, &mode );
		gFileFromCD[ ch ] = 1;
		gBgmPreload[ ch ] = 3;
		gFileOpened[ ch ] = 1;
		gWave[ ch ].pos = 0;
		gWave[ ch ].size = gRPacketSize[ ch ] * 4;
	}
	
//	printf("wave size %d  offset %d\n", gWave[ch].size, gWave[ch].offset );
	
	BgmSetVolumeDirect( ch, 0 );

	strncpy( gFilename[ ch ], filename, MAX_FILENAME_LENGTH  );

	// clear out the end of the second half of the last buffer...
	addr = ( int * )( gBuffRaw[ ch ] + ( gRPacketSize[ ch ] ) + ( gRPacketSize[ ch ] >> 1 ) );
	for ( i = 0; i < ( ( gRPacketSize[ ch ] >> 1 ) >> 2 ); i++ )
	{
		*( addr++ ) = 0;
	}
	
	if( ch == 0 )
	{
		sceSdSetTransCallback( ch, IntFunc0 );
	}
	else
	{
		sceSdSetTransCallback( ch, IntFunc1 );
	}
		
	return ( gWave[ ch ].size );
}

void _BgmClose(int ch, int status)
{
	if ( !gFileOpened[ ch ] )
	{
		PRINTF(("Calling BgmClose when no file opened on that channel...\n"));
		return;
	}
	Dbg_Printf( "_BgmClose\n" );
	if ( gFileFromCD[ ch ] )
	{
		sceCdStStop( );
	}
	else
	{
		close( gFd[ch] );
		//FreeSysMemory( ( void* )gBuffSpu[ ch ] );
		//gBuffSpu[ ch ] = 0;
	}
	gFileOpened[ ch ] = 0;
	return;
}

void BgmStart(int ch, int status )
{
	int retVal;
	if ( !gFileOpened[ ch ] )
	{
		printf( "trying to start track not loaded.\n" );
		return;
	}
	Dbg_Printf( "starting block trans from bgm start/unpause\n" );
	if ( gBgmPause[ ch ] )
	{
		Dbg_Printf( "Starting music on channel where pause is requested...\n" );
		gBgmPause[ ch ] = 0;
	}
	while( -1 == sceSdBlockTrans( ch, SD_TRANS_MODE_WRITE|SD_BLOCK_LOOP, (u_char*)gBuffSpu[ch], (gSPacketSize[ch]*2) ) )
	{
		for ( retVal = 0; retVal < 10000; retVal++ )
			;
		Dbg_Printf( "failed to start loop.\n" );
	}
	if ( !( gBgmPreload[ ch ] ) )
	{
		BgmSetVolumeDirect(ch, gBgmVolume[ch]);
	}
	gBgmMode[ch] &= BGM_MASK_STATUS;
	gBgmMode[ch] |= BGM_MODE_RUNNING;

	return;
}

void BgmCloseNoWait( int ch, int status )
{
	if ( !gFileOpened[ ch ] )
	{
		PRINTF(("Calling BgmClose when no file opened on that channel...\n"));
		return;
	}
	if ( gBgmPause[ ch ] )
	{
		printf( "stopping music when pause is requested..." );
		return;
	}
	// if the pause has already stopped the callback, it's safe to unload now:
	if ( gBgmMode[ ch ] & BGM_MODE_PAUSE )
	{
		// callback isn't happening right now... can terminate with no worries here:
		Dbg_Printf( "unpausing from BgmClose\n" );
		gBgmMode[ch] &= BGM_MASK_STATUS; // Switch to IDLE mode
		_BgmClose( ch, status );
		Dbg_Printf( "calling _BgmClose from BgmClose\n" );
		return;
	}
	gBgmMode[ch] &= BGM_MASK_STATUS;
	gBgmMode[ch] |= BGM_MODE_TERMINATE;
}

void BgmClose( int ch, int status )
{
	int i;
	BgmCloseNoWait( ch, status );
	printf( "PCM close" );
	while ( ( gBgmMode[ ch ] & BGM_MODE_TERMINATE ) )
	{
		for ( i = 0; i < 20000; i++ )
			;
		printf( "." );
	}
	printf( "\n" );
}

int BgmPreLoad( int ch, int status )
{
	if ( ( status == 666 ) || ( ( gWave[ ch ].size - gWave[ ch ].pos ) < gRPacketSize[ ch ] * 4 ) )
	{
		int i;
		int *addr;
		Dbg_Printf( "preloading at end of song. zero buffers.\n" );
		// we're at the end of the song... fill up read buffers with zero,
		// and convert...
		addr = ( int * )( gBuffRaw[ ch ] );
		for ( i = 0; i < ( ( gRPacketSize[ ch ] * 2 ) >> 2 ); i++ )
		{
			*( addr++ ) = 0;
		}
		// fill up the spu with both buffers...
		BgmRaw2Spu( ch, 0 );
		BgmRaw2Spu( ch, 1 );
		return ( 0 );
	}
	
	// it's still in preload mode... no need to preload ourselves...
	if ( gBgmPreload[ ch ] == 3 )
		return ( 0 );

	if ( gFileFromCD[ ch ] )
	{
		unsigned int err;
		sceCdStRead( NUM_SECTORS_PER_READ * 2, ( u_int * )gBuffRaw[ ch ], STMBLK, &err );
		if ( err )
		{
			printf( "PCM A Disk error code 0x%08x\n", err );
		}

		// fill up the spu with both buffers...
		BgmRaw2Spu( ch, 0 );
		BgmRaw2Spu( ch, 1 );
		
		// fill up the buffers again...
		sceCdStRead( NUM_SECTORS_PER_READ * 2, ( u_int * )gBuffRaw[ ch ], STMBLK, &err );
		if ( err )
		{
			printf( "PCM B Disk error code 0x%08x\n", err );
		}
		
		gWave[ ch ].pos += gRPacketSize[ ch ] * 2;
		return 0;
	}
	// reading from a file...
	if ( read (gFd[ch], (unsigned char*)(gBuffRaw[ch])
	, gRPacketSize[ch]*2 ) != gRPacketSize[ch]*2 ) {
		ERROR (("BgmPreLoad: read failed \n")); return -1;
	}

	BgmRaw2Spu( ch, 0 );
	BgmRaw2Spu( ch, 1 );

	if ( read (gFd[ch], (unsigned char*)(gBuffRaw[ch])
	, (gRPacketSize[ch]*2) ) != gRPacketSize[ch]*2) {
		ERROR (("BgmPreLoad: read failed \n")); return -1;
	}

	gWave[ch].pos += gRPacketSize[ch]*4;

	return 0;
}

void _BgmPause(int ch, int status)
{
	Dbg_Printf( "stopping spu block trans in _BgmPause\n" );
	sceSdBlockTrans( ch, SD_TRANS_MODE_STOP, NULL, 0 );
	gBgmMode[ch] &= BGM_MASK_STATUS; // Switch to PAUSE mode
	gBgmMode[ch] |= BGM_MODE_PAUSE;
	return;
}

void _BgmStopAndUnload(int ch, int status)
{
	Dbg_Printf( "stopping block trans from _BgmStopAndUnload\n" );
	sceSdBlockTrans( ch, SD_TRANS_MODE_STOP, NULL, 0 );
	gBgmMode[ch] &= BGM_MASK_STATUS; // Switch to IDLE mode
	Dbg_Printf( "bgm close from _BgmStopAndUnload\n" );
	_BgmClose( ch, status );
	return;
}

// should be fucking called "BgmPause"
void BgmStop( int ch, unsigned int vol )
{
	int i;

	if ( gBgmMode[ ch ] & BGM_MODE_TERMINATE )
	{
		Dbg_Printf( "trying to pause PCM when stopped.\n" );
		return;
	}
	if ( !gBgmMode[ ch ] & BGM_MODE_RUNNING )
	{
		Dbg_Printf( "trying to pause PCM when not running.\n" );
		return;
	}
	gBgmPause[ch] = 1;
	printf( "waiting for pause" );
	while ( !( gBgmMode[ ch ] & BGM_MODE_PAUSE ) )
	{
		for ( i = 0; i < 10000; i++ )
			;
		printf( "." );
	}
	printf( "\n" );
	// don't set the flag until the callback has paused it:
//	gBgmMode[ch] &= BGM_MASK_STATUS;
//	gBgmMode[ch] |= BGM_MODE_PAUSE;
	return;
}


int BgmSetVolume( int ch, unsigned int vol )
{
	gBgmVolumeSet[ch] = 1;
	gBgmVolume[ch] = vol;
	return ( gBgmMode[ch] & ~BGM_MASK_STATUS );
}


unsigned int BgmGetMode( int ch, int status )
{
	return gBgmMode[ch];
}

void BgmSeek( int ch, unsigned int value )
{
//	lseek(gFd[ch], gWave[ch].offset+value, SEEK_SET );
//	gWave[ch].pos = value;
	printf( "seek not supported\n" );
	return;
}

void SetStereoOn( int ch, int stereo )
{
    if ( stereo )
	{
		gSPacketSize[ ch ] = gAllockedSize[ ch ] / 6;
		gBgmStereoMode[ ch ] = 1;
	}
	else
	{
		gSPacketSize[ ch ] = gAllockedSize [ ch ] / 3;
		gBgmStereoMode[ ch ] = 0;
	}
}

void PreloadFile( int which, int ch )
{
	switch ( gBgmPreload[ ch ] )
	{
		case ( 3 ):
			if ( gFileFromCD[ ch ] )
			{
				RIFFHeader *pWavHdr;
				int i;
				int offset;
				unsigned char *pData;
				pWavHdr = ( RIFFHeader * )( gBuffRaw[ ch ]+gRPacketSize[ch]*which );
				if ( pWavHdr->dataHdr.chunkSize < gWave[ ch ].size )
				{
					printf( "BIG WARNING:  Cd audio file currently playing is TOO SMALL!\n" );
				}
				if ( pWavHdr->fmtHdr.channel == 2 )
				{
					if ( !gBgmStereoMode[ ch ] )
						printf( "WARNING: Playing stereo sound %s in mono mode.\n", gFilename[ ch ] );
				}
				else if ( gBgmStereoMode[ ch ] )
				{
					printf( "WARNING: Playing mono sound %s in stereo mode.\n", gFilename[ ch ] );
				}
				
				SetStereoOn( ch, pWavHdr->fmtHdr.channel == 2 );
				gWave[ch].size   = pWavHdr->dataHdr.chunkSize;
				//gWave[ch].offset = (unsigned int)&( pWavHdr->dataHdr.data ) - ( unsigned int )pWavHdr;
				offset = (unsigned int)&( pWavHdr->dataHdr.data ) - ( unsigned int )pWavHdr;
				// clear out the area where the header is... don't want some loud obnoxious sounds...
				pData = ( unsigned char * )( gBuffRaw[ ch ]+gRPacketSize[ch]*which );
				if ( offset > gRPacketSize[ ch ] )
					offset = gRPacketSize[ ch ]; // don't fuck up memory if some strange read happened...
				for ( i = 0; i < offset; i++ )
				{
					pData[ i ] = 0;
				}
			}
			break;
		
		case ( 2 ):
			break;
		
		case ( 1 ):
			// turn the volume back up, the spooler is at the beginning of
			// the good SPU buffer...
			BgmSetVolume( ch, gBgmVolume[ch] );
			break;
		
		default:
			printf( "unknown preload state... fire Matt.\n" );
			break;
	}
	--gBgmPreload[ ch ];
}

int _BgmPlay( int status )
{
	int i, ch, read_size, which;
	int *addr, remain;
	Dbg_Printf( "entering _BgmPlay\n" );
	while ( 1 )
	{
		//-- Wait for playing of buffer to finish
		WaitSema(gSem);

		//-- Which channel is the interrupt from?
		if( (gBgmIntr[0] == 1) && ( ch != 0 ) )  ch = 0;
		else if( (gBgmIntr[1] == 1) && ( ch != 1 ) )  ch = 1;
		else if( gBgmIntr[0] == 1 )  ch = 0;
		else if( gBgmIntr[1] == 1 )  ch = 1;
		else continue;

		gBgmIntr[ch] = 0;

		which = 1 - (sceSdBlockTransStatus( ch, 0 )>>24);

		//--- Stopped due to end of data (no looping)
		if( ( gBgmMode[ ch ] & BGM_MODE_TERMINATE ) != 0 )
		{
			WaitSema( gSem ); // Wait until another interrupt is received
			_BgmStopAndUnload( ch, 0 );
			BgmSetVolumeDirect( ch, 0x0 );
			continue;
		}

		//--- Volume change event
		if ( ( gBgmVolumeSet[ ch ] == 1 ) && ( !gBgmPreload[ ch ] ) )
		{
			BgmSetVolumeDirect( ch, gBgmVolume[ch] );
			gBgmVolumeSet[ch] = 0;
		}

		//--- Convert buffer

		BgmRaw2Spu( ch, which );

		//--- File READ for buffer

		remain = gWave[ ch ].size - gWave[ ch ].pos;
		if ( remain > gRPacketSize[ch] )
		{
			if ( gFileFromCD[ ch ] )
			{
				//--- Not end of data
				unsigned int err;
				sceCdStRead( NUM_SECTORS_PER_READ, ( u_int * )( gBuffRaw[ ch ]+gRPacketSize[ch]*which ), STMBLK, &err );
				if ( err )
				{
					printf( "PCM C Disk error code 0x%08x\n", err );
				}
				read_size = gRPacketSize[ ch ];
			}
			else
			{
				read_size = read (gFd[ch], (unsigned char*)(gBuffRaw[ch]+gRPacketSize[ch]*which), gRPacketSize[ch] );
			}
			if ( read_size < gRPacketSize[ch] )
				continue; //retry
			if ( gBgmPreload[ ch ] )
			{
				PreloadFile( which, ch );
			}
			gWave[ch].pos += read_size;
		}
		else  //--- End of data
		{
			if ( gFileFromCD[ ch ] )
			{
				//--- Not end of data
				unsigned int err;
				sceCdStRead( NUM_SECTORS_PER_READ, ( u_int * )( gBuffRaw[ ch ]+gRPacketSize[ch]*which ), STMBLK, &err );
				if ( err )
				{
					printf( "PCM C Disk error code 0x%08x\n", err );
				}
				read_size = gRPacketSize[ ch ];
			}
			else
			{
				read_size = read (gFd[ch], (unsigned char*)(gBuffRaw[ch]+gRPacketSize[ch]*which), remain );
			}
			
			if( read_size < remain ) continue; //retry
	    	
			PRINTF(("end of PCM track - ch %d\n", ch));

			addr = ( int * )( gBuffRaw[ ch ] + ( gRPacketSize[ ch ] * which ) + remain );
			for( i = 0; i < ((gRPacketSize[ch]-remain)>>2); i++ )
			{
				*(addr++) = 0;
			}
			gBgmMode[ch] &= BGM_MASK_STATUS;
			gBgmMode[ch] |= BGM_MODE_TERMINATE;
		}

		//-- Stop event
		if( (gBgmPause[ch] == 1) )
		{
			//printf( "_BgmPlay :: pause\n" );
			_BgmPause( ch, 0 );
			BgmSetVolumeDirect(ch, 0x0);
			gBgmPause[ch] = 0;
		}
	}
	return 0;
}

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* This file ends here, DON'T ADD STUFF AFTER THIS */

// Ha ha I added some stuff... FUCK YOU SONY!


================================================
FILE: Code/Gel/Music/Ngps/Bgm/bgm_r2s.s
================================================
/* SCEI CONFIDENTIAL
 "PlayStation 2" Programmer Tool Runtime Library  Release 2.0
 */
/* 
 *                  I/O Proseccor sample program
 *                          Version 1.20
 *                           Shift-JIS
 *
 *      Copyright (C) 1998-1999 Sony Computer Entertainment Inc.
 *                        All Rights Reserved.
 *
 *                       ezbgm.irx - bgm_r2s.c
 *                          raw to spu pcm
 *
 *   Version   Date            Design    Log
 *  --------------------------------------------------------------------
 *   1.20      Nov.23.1999     morita    modify for EzBGM
 *   0.01      Nov.18.1999     ishii     optimize for IOP
 */

#include 

#define src  a0
#define dst  a1
#define blk  a2
#define cnt  a3

	.globl	_BgmRaw2Spu
	.ent	_BgmRaw2Spu
_BgmRaw2Spu:
	subu	sp, (8*4)
	sw	s0, 0*4(sp)	; 	sw	s1, 1*4(sp)
	sw	s2, 2*4(sp)	; 	sw	s3, 3*4(sp)
	sw	s4, 4*4(sp)	; 	sw	s5, 5*4(sp)
	sw	s6, 6*4(sp)	; 	sw	s7, 7*4(sp)
	move	v0, zero
	move	v1, zero
	addi	cnt,zero, 256

					.set noreorder
	/* AŖ߂̂Ȃт𐧌B */
pcm_separate_loop:
	lw	t0, 0*4(src)	/* 1 + 4 clock */
	lw	t1, 1*4(src)	/* 1 + 2 clock */

	/* 7 clock */
	and	s0, t0, 0xffff		/* t0 ̉ʂ s0  */
	srl	s1, t0, 16		/* t0 ̏ʂ s1  */
	sll	t0, t1, 16		/* t1 ̉ʂɃVtg s0  */
	or	s0, t0
	srl	t1, t1, 16		/* t1 ̏ʂ}XN s1  */
	sll	t1, t1, 16
	or	s1, t1

	/* ɃLbVɓǂݍ܂Ă͂ */
	lw	t2, 2*4(src)	/* 1 clock */
	lw	t3, 3*4(src)	/* 1 clock */
	/* ̃LbVCǂݍނ */
	lw	t4, 4*4(src)	/* 1 + 4 clock */

	/* 7 clock */
	and	s2, t2, 0xffff		/* t2 ̉ʂ s2  */
	srl	s3, t2, 16		/* t2 ̏ʂ s3  */
	sll	t2, t3, 16		/* t3 ̉ʂɃVtg s2  */
	or	s2, t2
	srl	t3, t3, 16		/* t3 ̏ʂ}XN s3  */
	sll	t3, t3, 16
	or	s3, t3

	/* ɃLbVɓǂݍ܂Ă͂ */
	lw	t5, 5*4(src)	/* 1 clock */
	lw	t6, 6*4(src)	/* 1 clock */
	lw	t7, 7*4(src)	/* 1 clock */
	add	src, 8*4	/* 1 clock */

	/* 7 clock */
	and	s4, t4, 0xffff		/* t4 ̉ʂ s4  */
	srl	s5, t4, 16		/* t4 ̏ʂ s5  */
	sll	t4, t5, 16		/* t5 ̉ʂɃVtg s4  */
	or	s4, t4
	srl	t5, t5, 16		/* t5 ̏ʂ}XN s5  */
	sll	t5, t5, 16
	or	s5, t5

	/* store  dst1(s0, s2),  dst2(s1, s3) */
	/* 4 clock */

	sw	s0, 0*4(dst)	;	sw	s2, 1*4(dst)
	sw	s1, (0*4+512)(dst); 	sw	s3, (1*4+512)(dst)
	/* ȉƕs +2, 4+2 clock ŏ܂͂ */

	/* 7 clock */
	and	s6, t6, 0xffff		/* t6 ̉ʂ s6  */
	srl	s7, t6, 16		/* t6 ̏ʂ s7  */
	sll	t6, t7, 16		/* t7 ̉ʂɃVtg s6  */
	or	s6, t6
	srl	t7, t7, 16		/* t7 ̏ʂ}XN s7  */
	sll	t7, t7, 16
	or	s7, t7

	/* store  dst1(s4, s6),  dst2(s5, s7) */
	/* 4 clock */
	sw	s4, 2*4(dst)	; 	sw	s6, 3*4(dst)
	sw	s5, (2*4+512)(dst)	; 	sw	s7, (3*4+512)(dst)
	/* ȉƕs +2, 4+2 clock ŏ܂͂ */

	add	dst, 4*4		/* 1 clock */


	/* ܂ŁAŖ߂̂Ȃт𐧌B */
					.set reorder

	add	v0, 8			/* 1 clock */
	blt	v0, cnt, pcm_separate_loop	/* 1 clock */

	add	dst, 512
	move	v0, zero

	add	v1, 1
	blt	v1, blk, pcm_separate_loop

	lw	s0, 0*4(sp)	; 	lw	s1, 1*4(sp)
	lw	s2, 2*4(sp)	; 	lw	s3, 3*4(sp)
	lw	s4, 4*4(sp)	; 	lw	s5, 5*4(sp)
	lw	s6, 6*4(sp)	; 	lw	s7, 7*4(sp)
	addu	sp, (8*4)
	j	ra
	.end	_BgmRaw2Spu



================================================
FILE: Code/Gel/Music/Ngps/Bgm/bgm_r2sm.c
================================================
#include 

#define SPU_BLOCK 512

// Using SP will speed things up, but ...

void _BgmRaw2SpuMono( unsigned int *src, unsigned int *dst, unsigned int block )
{
	int i;

	for ( i = 0; i < block; i++ )
	{
		memcpy( (void*)((int)dst+i*SPU_BLOCK*2), (void*)((int)src+i*SPU_BLOCK), SPU_BLOCK );
		memcpy( (void*)((int)dst+i*SPU_BLOCK*2+SPU_BLOCK), (void*)((int)src+i*SPU_BLOCK) , SPU_BLOCK );
	}

	return;
}




================================================
FILE: Code/Gel/Music/Ngps/Bgm/makefile
================================================
ifeq ($(wildcard PathDefs),)
PathDefs:
	iop-path-setup > PathDefs || (rm -f PathDefs ; exit 1)
	make all
else
include PathDefs
endif

INCDIR	= -I/usr/local/sce/common/include -I/usr/local/sce/iop/install/include

CFLAGS  = $(INCDIR) -I. -Wall -G0 -g
ASFLAGS  = $(INCDIR) -G0

COMPILE.s = $(CC) -xassembler-with-cpp $(ASFLAGS) $(CPPFLAGS) $(TARGET_MACH) -c

#----------- customize section --------------
PROGNAME = ezbgm

OBJS     = bgm_entr.o bgm_com.o bgm_play.o bgm_r2s.o bgm_r2sm.o
#ILIBS	= -ilb=$(TOP)lib/iop.ilb 
ILIBS	= 
LIBI	= /usr/local/sce/iop/install/lib

#----------- rules --------------
all:	$(PROGNAME).irx

clean:
	rm -f *.o $(PROGNAME).irx *.obj *.map

$(PROGNAME).irx: $(OBJS)
	$(LINK.o)  -o $@ \
		$(OBJS) -L../../../lib -L./ -L$(LIBI) -ilb=libsd.ilb  -ilb=cdvdman.ilb



================================================
FILE: Code/Gel/Music/Ngps/Pcm/Makefile
================================================
ifeq ($(wildcard PathDefs),)
PathDefs:
	iop-path-setup
	make all
else
include PathDefs
endif

TOPDIR = /usr/local/sce
INCOPT = -I$(TOPDIR)/common/include -I$(TOPDIR)/iop/install/include

CFLAGS  = $(INCOPT) -I. -Wall -G0 -g -D__PLAT_NGPS__
ASFLAGS = $(INCOPT) -G0
RM          = /bin/rm -f

COMPILE.s = $(CC) -xassembler-with-cpp $(ASFLAGS) $(CPPFLAGS) $(TARGET_MACH) -c

#----------- customize section --------------
PROGNAME = ezpcm

OBJS     = pcm_ent.o pcm_com.o pcm_sound.o 
#ILIBS	= -ilb=$(TOP)lib/iop.ilb 
ILIBS	=
LIBI	= /usr/local/sce/iop/install/lib

#----------- rules --------------
all:	$(PROGNAME).irx

clean:
	rm -f *.o $(PROGNAME).irx *.obj *.map

$(PROGNAME).irx: $(OBJS)
	$(LINK.o)  -o $@ \
		$(OBJS) -L../../../lib -L./ -L$(LIBI) -ilb=libsd.ilb

pcm_ent.o:	pcm.h pcmiop.h
pcm_com.o:	pcm.h pcmiop.h
pcm_sound.o:	pcm.h pcmiop.h



================================================
FILE: Code/Gel/Music/Ngps/Pcm/PathDefs
================================================
AR	 = snarl.exe
AS	 = ps2cc -iop
CC	 = ps2cc -iop
GCC	 = ps2cc -iop
LD	 = ps2cc -iop
NM	 = C:/usr/local/sce/iop/gcc/bin/iop-nm.exe
SIZE	 = iop-size.exe
STRIP	 = C:/usr/local/sce/iop/gcc/bin/iop-strip.exe
RANLIB	 = C:/usr/local/sce/iop/gcc/bin/iop-ranlib.exe
OBJCOPY	 = C:/usr/local/sce/iop/gcc/bin/iop-objcopy.exe
OBJDUMP	 = C:/usr/local/sce/iop/gcc/bin/iop-objdump.exe
IFIXUP	 = iopfixup.exe
ILBGEN	 = C:/usr/local/sce/iop/gcc/bin/ioplibgen.exe
ILBLD	 = C:/usr/local/sce/iop/gcc/mipsel-scei-elfl/bin/ioplibld.exe
ILBDUMP	 = C:/usr/local/sce/iop/gcc/mipsel-scei-elfl/bin/ioplibdump.exe
BIN2OBJ	 = bin2elf.exe


================================================
FILE: Code/Gel/Music/Ngps/Pcm/pcm.h
================================================

#ifndef	__GEL_MUSIC_NGPS_PCM_PCM_H__
#define	__GEL_MUSIC_NGPS_PCM_PCM_H__


#ifdef	__PLAT_NGPS__
#include 
#endif

#define NUM_STREAMS 3

#define PCM_RPC_ARG_NUM_INTS 16

#define DEFAULT_PITCH	0x1000

// VAG header structure
struct VagHeader
{
	char ID[4];
	int version;
	char reserved1[4];
	int dataSize;
	int sampleFreq;
	char reserved2[12];
	char name[16];
} ;

#define SIZE_OF_VAGHEADER ( sizeof( VagHeader ) )

// sound stuff:
#define SB_BUF_SIZE ( 1024 * 32 )		// hex 2000
#define SB_BUF_HALF ( SB_BUF_SIZE / 2 )	// hex 1000
#define SB_BUF_SIZE_WITH_PADDING	( SB_BUF_SIZE + 0x40 )

// spu ram layout:
// first valid address for use in SPU RAM
#define SPU_RAM_SIZE					0x1fffff
#define SB_FIRST_USEABLE_ADDR			0x5010

//#define STRAY_VOICE_BLOCKER_SIZE		128
//#define STRAY_VOICE_BLOCKER_SPU_ADDR	SB_TOP
//#define STREAM_SPU_ADDR					( STRAY_VOICE_BLOCKER_SPU_ADDR + STRAY_VOICE_BLOCKER_SIZE )
#define STREAM_SPU_ADDR					SB_FIRST_USEABLE_ADDR

// slip in room for 3 streaming VAGS ( 2 for stereo music and one extra stream ):
//#define SINGLE_STREAM_BUFFER_SIZE 2048
//#define TOTAL_SPU_REQUIRED_FOR_STREAMS		( SB_BUF_SIZE_WITH_PADDING * 4 )  // Assumes 2 streams
#define TOTAL_SPU_REQUIRED_FOR_STREAMS		( SB_BUF_SIZE_WITH_PADDING * ( 2 + NUM_STREAMS ) )
#define MUSIC_L_SPU_BUF_LOC					( STREAM_SPU_ADDR + 0x40 )
#define MUSIC_R_SPU_BUF_LOC					( MUSIC_L_SPU_BUF_LOC + SB_BUF_SIZE_WITH_PADDING )
#define STREAM_SPU_BUF_LOC( i )				( MUSIC_R_SPU_BUF_LOC + ( ( 1 + ( i ) ) * SB_BUF_SIZE_WITH_PADDING ) )

#define END_OF_STREAMS						( STREAM_SPU_ADDR + TOTAL_SPU_REQUIRED_FOR_STREAMS + 0x40 )

//#define RAM_NEEDED_FOR_EFFECTS		0	// FUCK the effects...
#define	RAM_NEEDED_FOR_EFFECTS			0xade0 // enough for HALL reverb
//#define	RAM_NEEDED_FOR_EFFECTS		0xF6C0 // enough for space echo
 
#define REVERB_ONLY_ON_CORE_0			1

#if !REVERB_ONLY_ON_CORE_0

#define CORE_1_EFFECTS_START_ADDRESS	END_OF_STREAMS
#define CORE_1_EFFECTS_END_ADDRESS		0x02ffff // has to be on a 0xffff boundary!!
#if ( ( CORE_1_EFFECTS_END_ADDRESS - CORE_1_EFFECTS_START_ADDRESS ) < RAM_NEEDED_FOR_EFFECTS )
#error "not enough space for core 0 effects!"
#endif

#define BASE_WAVE_DATA_ADDR				CORE_0_EFFECTS_END_ADDRESS

#else
#define BASE_WAVE_DATA_ADDR				END_OF_STREAMS
#endif

#define CORE_0_EFFECTS_START_ADDRESS	( SPU_RAM_SIZE - RAM_NEEDED_FOR_EFFECTS )
#define CORE_0_EFFECTS_END_ADDRESS		( SPU_RAM_SIZE )
#define END_WAVE_DATA_ADDR				( CORE_0_EFFECTS_START_ADDRESS  - 0x40 )

// used by temporary sounds and permanently loaded sounds...
#define MAX_SPU_RAM_AVAILABLE			( END_WAVE_DATA_ADDR - BASE_WAVE_DATA_ADDR )

//#define MUSIC_R_SPU_BUF_LOC		SB_TOP
//#define MUSIC_L_SPU_BUF_LOC		( MUSIC_R_SPU_BUF_LOC + SB_BUF_SIZE )
//#define STREAM_SPU_BUF_LOC		( MUSIC_L_SPU_BUF_LOC + SB_BUF_SIZE )

// iop buffer taking streams off of the CD:
#define MUSIC_IOP_BUFFER_SIZE				( 1024 * (/*192*/192) )  // must be a multiple of 4k!
#define MUSIC_HALF_IOP_BUFFER_SIZE			( MUSIC_IOP_BUFFER_SIZE / 2 )  // must be a multiple of 2k!
//#define STREAM_IOP_BUFFER_SIZE				( 1024 * (/*128*/96) )  // must be a multiple of 4k!
#define STREAM_IOP_BUFFER_SIZE				( 1024 * (/*128*/64) )  // must be a multiple of 4k!
#define STREAM_HALF_IOP_BUFFER_SIZE			( STREAM_IOP_BUFFER_SIZE / 2 )  // must be a multiple of 2k!

// must match values in pcm_com.c!!!
#define TOTAL_IOP_BUFFER_SIZE_NEEDED		( ( MUSIC_IOP_BUFFER_SIZE * 2 ) + ( STREAM_IOP_BUFFER_SIZE * NUM_STREAMS ) )
#define ALLIGN_REQUIREMENT					64
#define SECTOR_SIZE							( 2048 )
#define NUM_SECTORS_PER_STREAM_BUFFER		( STREAM_IOP_BUFFER_SIZE / SECTOR_SIZE )
#define NUM_SECTORS_PER_STREAM_HALF_BUFFER	( NUM_SECTORS_PER_STREAM_BUFFER / 2 )
#define NUM_SECTORS_PER_MUSIC_BUFFER		( MUSIC_IOP_BUFFER_SIZE / SECTOR_SIZE )
#define NUM_SECTORS_PER_MUSIC_HALF_BUFFER	( NUM_SECTORS_PER_MUSIC_BUFFER / 2 )

#define MUSIC_L_IOP_OFFSET	0
#define MUSIC_R_IOP_OFFSET	( MUSIC_HALF_IOP_BUFFER_SIZE )  // interwoven with the L IOP buffer... and shit
#define STREAM_IOP_OFFSET( ch )				( ( 2 * MUSIC_IOP_BUFFER_SIZE ) + ( STREAM_IOP_BUFFER_SIZE * ( ch ) ) )

#define MUSIC_L_VOICE	22
#define MUSIC_R_VOICE	23
#define MUSIC_CORE		1

#define STREAM_VOICE( i ) ( 23 - ( i ) )
#define STREAM_CORE		0

// RPC command:
#define EzADPCM_COMMAND_MASK			0xfff0
#define EzADPCM_CH_MASK					0x000f
	
#define EzADPCM_INIT					0x0000
#define EzADPCM_QUIT					0x0010
#define EzADPCM_PLAYMUSIC				0x0020
#define EzADPCM_STOPMUSIC				0x0030
#define EzADPCM_PLAYSTREAM				0x0040
#define EzADPCM_STOPSTREAM				0x0070
#define EzADPCM_STOPSTREAMS				0x00a0
#define EzADPCM_SETMUSICVOL				0x00b0
#define EzADPCM_SETSTREAMVOL			0x00c0
#define EzADPCM_SETSTREAMVOLANDPITCH	0x00d0
#define EzADPCM_GETMUSICSTATUS			0x00f0
#define EzADPCM_PAUSEMUSIC				0x0100
#define EzADPCM_PAUSESTREAM				0x0110
#define EzADPCM_PAUSESTREAMS			0x0120
#define EzADPCM_SETSTREAMGLOBVOL		0x0130
#define EzADPCM_PRELOADMUSIC			0x0140
#define EzADPCM_PRELOADSTREAM			0x0150
#define EzADPCM_PLAYPRELOADEDMUSIC		0x0160
#define EzADPCM_PLAYPRELOADEDSTREAM		0x0170
#define EzADPCM_SDINIT					0x7ff0

// for GETSTREAMSTATUS or GETMUSICSTATUS:
#define PCM_STATUS					0x0001f000
#define PCM_STATUS_IDLE		   		0x00000000
#define PCM_STATUS_PRELOAD			0x00001000
#define PCM_STATUS_READY_TO_STOP	0x00002000
#define PCM_STATUS_NEED_UPDATE		0x00003000
#define PCM_STATUS_RUNNING			0x00005000
#define PCM_STATUS_TERMINATE		0x00006000

// flags for GETSTATUS
#define PCMSTATUS_NEED_MUSIC_BUFFER_0	( 1 << 0 ) // don't change the order of these!!! IMPORTANT!
#define PCMSTATUS_NEED_STREAM0_BUFFER_0	( 1 << 1 )
#define PCMSTATUS_NEED_STREAM1_BUFFER_0	( 1 << 2 )
#define PCMSTATUS_NEED_STREAM2_BUFFER_0	( 1 << 3 )
#define PCMSTATUS_NEED_MUSIC_BUFFER_1	( 1 << 4 )
#define PCMSTATUS_NEED_STREAM0_BUFFER_1	( 1 << 5 )
#define PCMSTATUS_NEED_STREAM1_BUFFER_1	( 1 << 6 )
#define PCMSTATUS_NEED_STREAM2_BUFFER_1	( 1 << 7 )
#define PCMSTATUS_MUSIC_PLAYING			( 1 << 8 )
#define PCMSTATUS_STREAM0_PLAYING		( 1 << 9 )
#define PCMSTATUS_STREAM1_PLAYING		( 1 << 10 )
#define PCMSTATUS_STREAM2_PLAYING		( 1 << 11 )
#define PCMSTATUS_MUSIC_READY			( 1 << 12 )
#define PCMSTATUS_STREAM0_READY			( 1 << 13 )
#define PCMSTATUS_STREAM1_READY			( 1 << 14 )
#define PCMSTATUS_STREAM2_READY			( 1 << 15 )
#define PCMSTATUS_LOAD_MUSIC			( 1 << 16 )
#define PCMSTATUS_LOAD_STREAM0			( 1 << 17 )
#define PCMSTATUS_LOAD_STREAM1			( 1 << 18 )
#define PCMSTATUS_LOAD_STREAM2			( 1 << 19 )
#define PCMSTATUS_INITIALIZED			( 1 << 20 )
#define PCMSTATUS_PANIC					( 1 << 30 )	// Irrecoverable error occurred

// flags for LOAD_STATUS
#define LOAD_STATUS_DONE_MUSIC_BUFFER_0		( 1 << 0 ) // don't change the order of these!!! IMPORTANT!
#define LOAD_STATUS_DONE_MUSIC_BUFFER_1		( 1 << 1 )
#define LOAD_STATUS_DONE_STREAM0_BUFFER_0	( 1 << 2 )
#define LOAD_STATUS_DONE_STREAM0_BUFFER_1	( 1 << 3 )
#define LOAD_STATUS_DONE_STREAM1_BUFFER_0	( 1 << 4 )
#define LOAD_STATUS_DONE_STREAM1_BUFFER_1	( 1 << 5 )
#define LOAD_STATUS_DONE_STREAM2_BUFFER_0	( 1 << 6 )
#define LOAD_STATUS_DONE_STREAM2_BUFFER_1	( 1 << 7 )

// macros:
#define PCMSTATUS_NEED_STREAM_BUFFER_0( xxx )	( PCMSTATUS_NEED_STREAM0_BUFFER_0 << ( xxx ) )
#define PCMSTATUS_NEED_STREAM_BUFFER_1( xxx )	( PCMSTATUS_NEED_STREAM0_BUFFER_1 << ( xxx ) )
#define PCMSTATUS_STREAM_PLAYING( xxx )			( PCMSTATUS_STREAM0_PLAYING << ( xxx ) )
#define PCMSTATUS_STREAM_READY( xxx )			( PCMSTATUS_STREAM0_READY << ( xxx ) )
#define PCMSTATUS_LOAD_STREAM( xxx )			( PCMSTATUS_LOAD_STREAM0 << ( xxx ) )

// These following macros work for both music and streams, where xxx is 0 for music and
// starts at 1 for streams
#define PCMSTATUS_NEED_AUDIO_BUFFER_0( xxx )	( PCMSTATUS_NEED_MUSIC_BUFFER_0 << ( xxx ) )
#define PCMSTATUS_NEED_AUDIO_BUFFER_1( xxx )	( PCMSTATUS_NEED_MUSIC_BUFFER_1 << ( xxx ) )
#define PCMSTATUS_AUDIO_PLAYING( xxx )			( PCMSTATUS_MUSIC_PLAYING << ( xxx ) )
#define PCMSTATUS_AUDIO_READY( xxx )			( PCMSTATUS_MUSIC_READY << ( xxx ) )
#define PCMSTATUS_LOAD_AUDIO( xxx )				( PCMSTATUS_LOAD_MUSIC << ( xxx ) )

/////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef	__PLAT_NGPS__
// Make sure this doesn't break the alignment of SSifCmdStreamReqPacket
typedef struct
{
	int				m_command;
	int				m_param[3];
} SStreamRequest;

// Make sure this doesn't break the alignment of SSifCmdSoundReqPacket
typedef struct
{
	unsigned short	m_function;
	unsigned short	m_entry;
	unsigned int	m_value;
	unsigned int	m_arg_0;
	unsigned int	m_arg_1;
	unsigned int	m_arg_2;
	unsigned int	m_ee_dma_id;		// Used on EE side; not sent to IOP side
	unsigned int	m_pad[2];
} SSoundRequest;

// SifCmd structures ( keeping them 128-bit aligned )

///////////////////////////////////
// A stream request from the EE to the IOP
typedef struct
{
	sceSifCmdHdr	m_header;
	SStreamRequest	m_request;
} SSifCmdStreamRequestPacket;

///////////////////////////////////
// The result of a stream request
typedef struct
{
	sceSifCmdHdr	m_header;
	int				m_return_value;
	int				m_status_flags;
	int				m_pad[2];
} SSifCmdStreamResultPacket;

///////////////////////////////////
// This packet is sent from the IOP to the EE after the status flags are updated on the IOP
typedef struct
{
	sceSifCmdHdr	m_header;
	int				m_status_flags;
	int				m_pad[3];
} SSifCmdStreamStatusPacket;

///////////////////////////////////
// This packet is sent from the EE to the IOP after a load finishes
typedef struct
{
	sceSifCmdHdr	m_header;
	int				m_stream_num;		// -1 for music
	int				m_buffer_num;
	int				m_bytes_loaded;
	int				m_pad[1];
} SSifCmdStreamLoadStatusPacket;

///////////////////////////////////
// This packet requests that a function is executed on the SPU2 chip
typedef struct
{
	sceSifCmdHdr	m_header;
	SSoundRequest	m_request;
} SSifCmdSoundRequestPacket;

///////////////////////////////////
// The result of any GET function (not all request packets get results)
typedef struct
{
	sceSifCmdHdr	m_header;
	unsigned short	m_function;
	unsigned short	m_entry;
	unsigned int	m_result;
	unsigned int	m_panic;			// indicates that request caused a panic on the IOP side
	int				m_pad;
} SSifCmdSoundResultPacket;

///////////////////////////////////
// Stores the request
typedef struct
{
	SStreamRequest	m_request;
	int				m_request_id;
} SStreamRequestPacket;

// EE to IOP commands
#define STREAM_REQUEST_COMMAND		(0)
										// command number 1 used by fileio.irx
#define STREAM_LOAD_STATUS_COMMAND	(2)
#define SOUND_REQUEST_COMMAND		(3)

// IOP to EE commands
#define STREAM_RESULT_COMMAND		(0)
										// command number 1 used by fileio.irx
#define STREAM_STATUS_COMMAND		(2)
#define SOUND_RESULT_COMMAND		(3)

//#define NUM_COMMAND_HANDLERS 	(0x10)

#endif // __PLAT_NGPS__

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* DON'T ADD STUFF AFTER THIS */

#endif	//	__GEL_MUSIC_NGPS_PCM_PCM_H__




================================================
FILE: Code/Gel/Music/Ngps/Pcm/pcm_com.c
================================================
/* Vag streaming into SPU2 -- from sony sample -- matt may 2001 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "pcm.h"
#include "pcmiop.h"

// ================================================================

#define STREAM_PITCH( x ) ( ( ( x ) * 22050 ) / 48000 )

#define CHECK_TRANSFER_INTEGRITY	0
#define TEST_PLAY_TIMING			0

#define SHOW_STREAM_INFO	0
#define SHOW_STATE	0
#define SHOW_SMALL_STREAM_STATE	0
#define SHOW_SMALL_MUSIC_STATE	0
#define SHOW_ACTION 0
#define SHOW_SMALL_ACTION 0
#define DBUGPRINTF	0
#define SHOW_TIMING	0
#define SHOW_STOP	0
#define SHOW_SMALL_COMMANDS	0
#define PRINT_EXCESSIVE_WAIT 0

#if SHOW_STREAM_INFO
#define ShowStreamInfo( x ) _ShowStreamInfo( x )
#else
#define ShowStreamInfo( x ) 
#endif

#if SHOW_SMALL_COMMANDS
#define ShowSmallCommand printf
#else
#define ShowSmallCommand( x )
#endif

#if SHOW_STOP
#define ShowStop printf
#else
#define ShowStop( x )
#endif

#if SHOW_TIMING
#define ShowTime	printf
#else
#define ShowTime DoNothing
void DoNothing( char* text, ... ) { }
#endif

#if SHOW_ACTION
#define ShowAction printf
#else
#define ShowAction( x )
#endif

#if SHOW_SMALL_STREAM_STATE
#define SmallShowStreamState printf( "  %d", whichStream ), printf
#else
#define SmallShowStreamState( x )
#endif

#if SHOW_SMALL_MUSIC_STATE
#define SmallShowMusicState printf
#else
#define SmallShowMusicState( x )
#endif

#if SHOW_SMALL_ACTION
#define SmallShowAction printf
#else
#define SmallShowAction( x )
#endif

#if SHOW_STATE
#define ShowState printf
#else
#define ShowState( x )
#endif

#if DBUGPRINTF
#define Dbug_Printf( x ) printf( x )
#else
#define Dbug_Printf( x )
#endif

// the program running on the EE side will check to see if any of the buffers need to be filled:
volatile unsigned int gEECommand = 0;

#define TRANS_DMA_CH_MUSIC		1
#define TRANS_DMA_CH_STREAM		0

#define _1st 0
#define _2nd 1

#define TRUE  1
#define FALSE 0

unsigned int VCount=0;
unsigned int gThid = 0;
unsigned int gSem = 0;
unsigned int gpIopBuf;		// IOP SMEM

struct StreamInfo gStreamInfo[ NUM_STREAMS ];
struct StreamInfo gMusicInfo;
volatile int gUsingStreamDMA = 0;
volatile int gSPUUsingStreamDMA = 0;

// Done on the EE now
//volatile unsigned int gStreamVolPercent = 100;

// Timing test code
#if TEST_PLAY_TIMING
static 			int		test_timing_request_id = 2;
static			int		test_timing_send_timing_result = FALSE;
static			int		test_timing_hit_point = FALSE;
#endif // TEST_PLAY_TIMING

u_int GetNAX(int core,int ch);
int OutsideMusicSPULoadBuffer();
int OutsideStreamSPULoadBuffer(int whichStream);
int DownloadMusic(int whichChannel);
int DownloadStream(int whichStream);
void IncMusicBuffer();
void IncStreamBuffer(int whichStream);
void StartMusic();
void StartStream(int whichStream);
int RefreshStreams( int status );
void SendStatus();

static int _AdpcmDmaIntMusic( int, void* );
static int _AdpcmDmaIntStream( int, void* );

#define _L 0
#define _R 1
#define _Lch(x) ((x >> 16) & 0xffff)
#define _Rch(x) (x & 0xffff)

// Timer structure.
typedef struct TimerCtx {
    int thread_id;
    int timer_id;
    int count;
} TimerCtx;

TimerCtx gTimer;

#define ONE_HSCAN 1000*1000/60	// Approx 60 frames per second

/**********************************************************************************
IRQ Interrupt - called, say, once per frame
***********************************************************************************/
int TimerFunc( void *common)
{
    TimerCtx *tc = (TimerCtx *) common;
		VCount++;
		iSignalSema(gSem);	// Signal StreamADPCM to be called
    return (tc->count);
}

/**********************************************************************************
SetTimer - IRQ Interrupt
***********************************************************************************/
int SetTimer( TimerCtx* timer )
{
    struct SysClock clock;
    int timer_id;

    USec2SysClock (ONE_HSCAN, & clock);
    timer->count = clock.low;	/* within 32-bit */

    // Use sysclock timer
    if ((timer_id = AllocHardTimer (TC_SYSCLOCK, 32, 1)) <= 0) {
	printf ("Can NOT allocate hard timer ...\n");
	return (-1);
    }
    timer->timer_id = timer_id;

    if (SetTimerHandler (timer_id, timer->count,
			 (void *)TimerFunc, (void *) timer) != KE_OK) {
	printf ("Can NOT set timeup timer handler ...\n");
	return (-1);
    }

    if (SetupHardTimer (timer_id, TC_SYSCLOCK, TM_NO_GATE, 1) != KE_OK) {
	printf ("Can NOT setup hard timer ...\n");
	return (-1);
    }
    return 0;

}

/**********************************************************************************
startTimer - IRQ Interrupt
***********************************************************************************/
int StartTimer( TimerCtx* timer )
{
    if (StartHardTimer (timer->timer_id) != KE_OK)
    {
		printf ("Can NOT start hard timer ...\n");
		return (-1);
    }
    return 0;
}

// EzADPCM_SDINIT
void AdpcmSdInit( void )
{
    sceSdInit (0);

    //	Disk media: DVD
    //	Output format: PCM
    //	Copy guard: normal (one generation recordable / default)
    sceSdSetCoreAttr (SD_C_SPDIF_MODE, (SD_SPDIF_MEDIA_DVD |
					SD_SPDIF_OUT_PCM   |
					SD_SPDIF_COPY_NORMAL));
    return;
}

void AdpcmSetUpVoice( int core, int vnum, int pSpuBuf )
{
    sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_VOLL, 0 );
    sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_VOLR, 0 );
    sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_PITCH, core == MUSIC_CORE ? DEFAULT_PITCH : STREAM_PITCH( DEFAULT_PITCH ) );
//    sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_ADSR1, 0x000f );
//    sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_ADSR2, 0x1fc0 );
    sceSdSetAddr  ( core | ( vnum << 1 ) | SD_VA_SSA,  pSpuBuf );
    return;
}

// EzADPCM_INIT
int AdpcmInit( int pIopBuf )
{
	int i;
	if ( gEECommand & PCMSTATUS_INITIALIZED )
	{
		return ( 0 );
	}

    if ( gSem == 0 )
	{
		struct SemaParam sem;
		sem.initCount = 0;
		sem.maxCount = 1;
		sem.attr = AT_THFIFO;
		gSem = CreateSema (&sem);
    }
    if (gThid == 0)
	{
		struct ThreadParam param;
		param.attr         = TH_C;
		param.entry        = RefreshStreams;
		param.initPriority = BASE_priority-3;
		param.stackSize    = 0x800; // was 800
		param.option = 0;
		gThid = CreateThread (¶m);
		printf( "EzADPCM: create thread ID= %d\n", gThid );
		StartThread (gThid, (u_long) NULL);
    }

	sceSdSetTransIntrHandler( TRANS_DMA_CH_MUSIC, ( sceSdTransIntrHandler ) _AdpcmDmaIntMusic, ( void * ) &gSem );
    sceSdSetTransIntrHandler( TRANS_DMA_CH_STREAM, ( sceSdTransIntrHandler ) _AdpcmDmaIntStream, ( void * ) &gSem );
	SetTimer(&gTimer);		// Setup IRQ interrupt
	StartTimer(&gTimer);		// Start IRQ interrupt (calls TimerFunc)
	
	AdpcmSetUpVoice( MUSIC_CORE, MUSIC_L_VOICE, MUSIC_L_SPU_BUF_LOC );
	AdpcmSetUpVoice( MUSIC_CORE, MUSIC_R_VOICE, MUSIC_R_SPU_BUF_LOC );
	for ( i = 0; i < NUM_STREAMS; i++ )
	{
		AdpcmSetUpVoice( STREAM_CORE, STREAM_VOICE( i ), STREAM_SPU_BUF_LOC( i ) );
	}

	memset( gStreamInfo, 0, sizeof( struct StreamInfo ) * NUM_STREAMS );
	memset( &gMusicInfo, 0, sizeof( struct StreamInfo ) );
	
	gpIopBuf = pIopBuf;
	gEECommand |= PCMSTATUS_INITIALIZED;
	//printf( "PCM Irx iop buf %d\n", gpIopBuf );
	//Dbug_Printf( "PCM irx is initialized\n" );
    return gThid;
}

// EzADPCM_QUIT
void AdpcmQuit( void )
{
    DeleteThread (gThid);
    gThid = 0;
#if 0
    DeleteSema (gSem);
    gSem = 0;
#endif
    return;
}

// EzADPCM_PRELOADMUSIC
// EzADPCM_PLAYMUSIC
int AdpcmPlayMusic( int size, int preload_only )
{
    extern void AdpcmSetMusicVolumeDirect( unsigned int );

    if ( gMusicInfo.status != PCM_STATUS_IDLE )
	{
		ShowAction( "NOTE NOTE NOTE NOTE Can't play music -- music isn't in Idle state.\n" );
		return -1;
    }

    AdpcmSetMusicVolumeDirect( gMusicInfo.volume );

	if (preload_only)
	{
		gMusicInfo.m_preloadMode = TRUE;
	}
	else
	{
		gMusicInfo.m_preloadMode = FALSE;
	}
				
	gMusicInfo.status = PCM_STATUS_PRELOAD;
	gMusicInfo.loadState = MUSIC_LOAD_STATE_IDLE;
	gMusicInfo.size = size;
	gMusicInfo.m_iopBufLoaded[0] = TRUE;
	gMusicInfo.m_iopBufLoaded[1] = FALSE;
	gEECommand &= ~PCMSTATUS_NEED_MUSIC_BUFFER_0;
	if (size > MUSIC_HALF_IOP_BUFFER_SIZE)
	{
		gEECommand |= PCMSTATUS_NEED_MUSIC_BUFFER_1;
	}
	gEECommand |= PCMSTATUS_MUSIC_PLAYING;
	ShowAction( "Starting music\n" );
	SmallShowAction( "-mb" );
    return 0;
}

void AdpcmSetStreamVolume( unsigned int vol, int whichStream );

// EzADPCM_PRELOADMUSIC
// EzADPCM_PLAYSTREAM
int AdpcmPlayStream( int size, int whichStream, unsigned int vol, unsigned int pitch, int preload_only )
{
    if ( gStreamInfo[ whichStream ].status != PCM_STATUS_IDLE )
	{
		printf( "trying to play stream when stream already playing!\n" );
		return -1;
    }

	if (preload_only)
	{
		gStreamInfo[ whichStream ].m_preloadMode = TRUE;
	}
	else
	{
		gStreamInfo[ whichStream ].m_preloadMode = FALSE;
		AdpcmSetStreamVolume( vol, whichStream );
		gStreamInfo[ whichStream ].pitch = STREAM_PITCH( pitch );
	}

	// have to make sure it's okay to use the stream DMA first...
	gStreamInfo[ whichStream ].status = PCM_STATUS_PRELOAD;
	gStreamInfo[ whichStream ].size = size;
	gStreamInfo[ whichStream ].m_iopBufLoaded[0] = TRUE;
	gStreamInfo[ whichStream ].m_iopBufLoaded[1] = FALSE;
	gEECommand &= ~( PCMSTATUS_NEED_STREAM0_BUFFER_0 << whichStream );
	if (size > STREAM_HALF_IOP_BUFFER_SIZE)
	{
		gEECommand |= PCMSTATUS_NEED_STREAM0_BUFFER_1 << whichStream;
	}
	gStreamInfo[ whichStream ].loadState = STREAM_LOAD_STATE_IDLE;
	gEECommand |= ( PCMSTATUS_STREAM_PLAYING( whichStream ) );
	ShowAction( "Starting stream\n" );
	SmallShowAction( "-sb" );
	return 0;
}

// EzADPCM_PLAYPRELOADEDMUSIC
int AdpcmPlayPreloadedMusic()
{
	// Tell update loop it is OK to play
	gMusicInfo.m_preloadMode = FALSE;
	SignalSema(gSem);	// Signal RefreshStreams() to be called

	ShowAction( "Playing preloaded music\n" );
	SmallShowAction( "-ppm" );
	return 0;
}

// EzADPCM_PLAYPRELOADEDSTREAM
int AdpcmPlayPreloadedStream(int whichStream, unsigned int vol, unsigned int pitch)
{
	AdpcmSetStreamVolume( vol, whichStream );
	gStreamInfo[ whichStream ].pitch = STREAM_PITCH( pitch );

	// Tell update loop it is OK to play
	gStreamInfo[ whichStream ].m_preloadMode = FALSE;
	SignalSema(gSem);	// Signal RefreshStreams() to be called

	ShowAction( "Playing preloaded stream\n" );
	SmallShowAction( "-pps" );
	return 0;
}

/* internal */
void _AdpcmSetMusicVoiceMute( void )
{
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1) | SD_VP_VOLL, 0);
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1) | SD_VP_VOLR, 0);
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1) | SD_VP_VOLL, 0);
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1) | SD_VP_VOLR, 0);
    return;
}

/* internal */
void _AdpcmSetStreamVoiceMute( int whichVoice )
{
    sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( whichVoice ) << 1) | SD_VP_VOLL, 0);
    sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( whichVoice ) << 1) | SD_VP_VOLR, 0);
    return;
}

void StopMusic( void )
{
	sceSdSetCoreAttr( MUSIC_CORE | SD_C_IRQ_ENABLE, 0 );
	_AdpcmSetMusicVoiceMute( );
	sceSdSetSwitch( MUSIC_CORE | SD_S_KOFF, ( 1 << MUSIC_R_VOICE ) | ( 1 << MUSIC_L_VOICE ) );
	ShowAction( "Stopping music\n" );
    SmallShowAction( "-!sm!" );
	gMusicInfo.stop = 0;
	gMusicInfo.loadState = MUSIC_LOAD_STATE_IDLE;
	//gMusicInfo.m_iopBufLoaded[0] = FALSE;		// This should not be necessary
	//gMusicInfo.m_iopBufLoaded[1] = FALSE;
	gEECommand &= ~( PCMSTATUS_MUSIC_PLAYING | PCMSTATUS_MUSIC_READY | PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 );
	gMusicInfo.status = PCM_STATUS_IDLE;
}

void StopStream( int whichStream )
{
	sceSdSetCoreAttr( STREAM_CORE | SD_C_IRQ_ENABLE, 0 );
	_AdpcmSetStreamVoiceMute( whichStream );
	sceSdSetSwitch( STREAM_CORE | SD_S_KOFF, ( 1 << STREAM_VOICE( whichStream ) ) );
	ShowAction( "Stopping stream\n" );
    SmallShowAction( "-!ss!" );
	gStreamInfo[ whichStream ].stop = 0;
	gStreamInfo[ whichStream ].loadState = STREAM_LOAD_STATE_IDLE;
	//gStreamInfo[ whichStream ].m_iopBufLoaded[0] = FALSE;		// This should not be necessary
	//gStreamInfo[ whichStream ].m_iopBufLoaded[1] = FALSE;
	gEECommand &= ~( ( PCMSTATUS_STREAM0_PLAYING | PCMSTATUS_STREAM0_READY | PCMSTATUS_NEED_STREAM0_BUFFER_0 | PCMSTATUS_NEED_STREAM0_BUFFER_1 ) << whichStream );
	gStreamInfo[ whichStream ].status = PCM_STATUS_IDLE;
}

#define NUM_WAIT_CLICKS 10000

// EzADPCM_STOPMUSIC
int AdpcmStopMusic( void )
{
	int i;
	int j = 0;

	if ( gMusicInfo.status != PCM_STATUS_IDLE )
	{
		ShowStop( "stopmusic" );
		gMusicInfo.stop++;
		SignalSema(gSem);	// Signal RefreshStreams() to be called
		// Why the hell are we waiting????  This is VERY lame!
		while ( gMusicInfo.status != PCM_STATUS_IDLE )
		{
			for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
				;
			if ( j++ > NUM_WAIT_CLICKS )
			{
				j = 0;
				printf( "...Waiting for music stop...\n" );
			}
		}
		ShowStop( "\n" );
		return ( 1 );
    }
	return ( 0 );
}

// EzADPCM_STOPSTREAM
int AdpcmStopStream( int whichStream )
{
	int i;
	int j = 0;

	if ( gStreamInfo[ whichStream ].status != PCM_STATUS_IDLE )
	{
		ShowStop( "Stop stream" );
		gStreamInfo[ whichStream ].stop++;
		SignalSema(gSem);	// Signal RefreshStreams() to be called
		// Why the hell are we waiting????  This is VERY lame!
		while ( gStreamInfo[ whichStream ].status != PCM_STATUS_IDLE )
		{
			for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
				;
			if ( j++ > NUM_WAIT_CLICKS )
			{
				printf( "...Waiting for stream %d stop...\n", whichStream );
				j = 0;
			}
		}
		ShowStop( "\n" );
		SmallShowAction( "-ss" );
		ShowAction( "Stopped stream\n" );
		return ( 1 );
    }
	return ( 0 );
}

// EzADPCM_SETMUSICVOL
void AdpcmSetMusicVolume( unsigned int vol )
{
    gMusicInfo.volume = vol;
    gMusicInfo.volumeSet = 1;
    return;
}

// EzADPCM_SETMUSICVOLDIRECT
void AdpcmSetMusicVolumeDirect( unsigned int vol )
{
    gMusicInfo.volume = vol;
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_VOLL, vol );
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_VOLR, 0 );
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_VOLL, 0 );
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_VOLR, vol );
    return;
}

// Done on the EE now
//void AdpcmSetStreamGlobVol( unsigned int volPercent )
//{
//	gStreamVolPercent = volPercent;
//}

#define PERCENT_MULT ( ( 1 << 16 ) / 100 )
#define PERCENT( x, y ) ( ( ( x ) * ( y ) * PERCENT_MULT ) >> 16 )

// EzADPCM_SETSTREAMVOL
void AdpcmSetStreamVolume( unsigned int vol, int whichStream )
{
    gStreamInfo[ whichStream ].volume = vol;
    gStreamInfo[ whichStream ].volumeSet = 1;
    return;
}

// EzADPCM_SETSTREAMVOLDIRECT
void AdpcmSetStreamVolumeDirect( unsigned int vol, int whichStream )
{
    //sceSdSetParam ( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_VOLL, PERCENT( gStreamVolPercent, _Lch( vol ) ) );
    //sceSdSetParam ( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_VOLR, PERCENT( gStreamVolPercent, _Rch( vol ) ) );
    sceSdSetParam ( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_VOLL, _Lch( vol ) );
    sceSdSetParam ( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_VOLR, _Rch( vol ) );
    return;
}

// Shouldn't need these unless debugging or something --
// Instead just get gEECommand each frame and act accordingly.

// EzADPCM_GETMUSICSTATUS
unsigned int AdpcmGetMusicStatus( void )
{
	return gMusicInfo.status;
}

// EzADPCM_GETSTREAMSTATUS
/*unsigned int AdpcmGetStreamStatus( int whichStream )
{
    return gStreamInfo[ whichStream ].status;
} */

static volatile unsigned int sEELastCommand = 0xFFFFFFFF;

// EzADPCM_GETSTATUS
unsigned int AdpcmGetStatus( void )
{
	unsigned int temp;
	temp = gEECommand;
	gEECommand &= ~( PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 | 
					PCMSTATUS_NEED_STREAM0_BUFFER_0 | PCMSTATUS_NEED_STREAM0_BUFFER_1 |
					PCMSTATUS_NEED_STREAM1_BUFFER_0 | PCMSTATUS_NEED_STREAM1_BUFFER_1 |
					PCMSTATUS_NEED_STREAM2_BUFFER_0 | PCMSTATUS_NEED_STREAM2_BUFFER_1 );
	sEELastCommand = gEECommand;
#if SHOW_SMALL_COMMANDS
	if ( temp & ( PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 | 
					PCMSTATUS_NEED_STREAM0_BUFFER_0 | PCMSTATUS_NEED_STREAM0_BUFFER_1 |
					PCMSTATUS_NEED_STREAM1_BUFFER_0 | PCMSTATUS_NEED_STREAM1_BUFFER_1 |
					PCMSTATUS_NEED_STREAM2_BUFFER_0 | PCMSTATUS_NEED_STREAM2_BUFFER_1 ) )
	{
		ShowSmallCommand( " <_L_>" );
	}
#endif
	return temp;
}

int AdpcmHasStatusChanged( void )
{
	return sEELastCommand != gEECommand;
}

// ================================================================

#define _ADPCM_MARK_START 0x04
#define _ADPCM_MARK_LOOP  0x02
#define _ADPCM_MARK_END   0x01

#define _AdpcmSetMarkFINAL(a,s) { \
  *( ( unsigned int * ) ( ( a ) + ( s ) - 16 ) ) = 0; \
  *( ( unsigned int * ) ( ( a ) + ( s ) - 12 ) ) = 0; \
  *( ( unsigned int * ) ( ( a ) + ( s ) - 8 ) ) = 0; \
  *( ( unsigned int * ) ( ( a ) + ( s ) - 4 ) ) = 0; \
  *((unsigned char *)((a)+(s)-0x0f)) = (_ADPCM_MARK_LOOP | _ADPCM_MARK_START | _ADPCM_MARK_END); \
  FlushDcache (); }


// Start of loop - note that a (address is either the start or mid address of the IOP buffer. s is half the buffer size)
#define _AdpcmSetMarkSTART(a,s) { \
  *((unsigned char *)((a)+1)) = \
	(_ADPCM_MARK_LOOP | _ADPCM_MARK_START); \
  *((unsigned char *)((a)+0x10+1)) = \
	_ADPCM_MARK_LOOP; \
  *((unsigned char *)((a)+(s)-0x0f)) = \
	_ADPCM_MARK_LOOP; \
	FlushDcache (); }

// End of loop - note that a (address is either the start or mid address of the IOP buffer. s is half the buffer size)
#define _AdpcmSetMarkEND(a,s) { \
  *((unsigned char *)((a)+1)) = \
	 _ADPCM_MARK_LOOP; \
  *((unsigned char *)((a)+0x10+1)) = \
	_ADPCM_MARK_LOOP; \
  *((unsigned char *)((a)+(s)-0x0f)) = \
	(_ADPCM_MARK_LOOP | _ADPCM_MARK_END); \
	FlushDcache (); }

// Preload Start marker - note that a (address is the start address of the IOP buffer. s is buffer size)

#define _AdpcmSetMarkSTARTpre(a,s) { \
  *((unsigned char *)((a)+1)) = \
	(_ADPCM_MARK_LOOP | _ADPCM_MARK_START); \
  *((unsigned char *)((a)+0x10+1)) = \
	_ADPCM_MARK_LOOP; \
	FlushDcache (); }

// Preload End marker - note that a (address is the start address of the IOP buffer. s is buffer size)
#define _AdpcmSetMarkENDpre(a,s) { \
  *((unsigned char *)((a)+(s)-0xf)) = \
	(_ADPCM_MARK_LOOP | _ADPCM_MARK_END); \
	FlushDcache (); }

/* internal */
static int _AdpcmDmaIntMusic(int ch, void *common)	// DMA Interrupt -- when transfering data to SPU2 from IOP
{
	//Kprintf("Got music IRQ for channel %d\n", ch);
	if ( gMusicInfo.status != PCM_STATUS_IDLE )
	{
		gMusicInfo.m_spuTransDone = TRUE;
		return 1;
	}
	return 0;
}

/* internal */
static int _AdpcmDmaIntStream(int ch, void *common)	// DMA Interrupt -- when transfering data to SPU2 from IOP
{
	if ( gUsingStreamDMA )
	{
		int whichStream = gUsingStreamDMA - 1;
		//Kprintf("Received Stream DMA 0 callback\n");
		if ( gStreamInfo[ whichStream ].status != PCM_STATUS_IDLE )
		{
			gStreamInfo[ whichStream ].m_spuTransDone = TRUE;
			gUsingStreamDMA = 0;
			return 1;
		}
	}
	else if (gSPUUsingStreamDMA)
	{
		gSPUUsingStreamDMA = 0;
		//Kprintf("Received SPU DMA 0 callback\n");
	}
	else
	{
		//Kprintf("Received outside DMA 0 callback\n");
	}
	return 0;
}

void UpdateMusic( void )
{
	struct StreamInfo *pInfo;
	pInfo = &gMusicInfo;
	switch ( pInfo->status )
	{
		case PCM_STATUS_IDLE:
			break;

		case PCM_STATUS_PRELOAD:
			switch ( pInfo->loadState )
			{
				case ( MUSIC_LOAD_STATE_IDLE ):
				{
					ShowState( "music preload l\n" );
					SmallShowMusicState( "-MPL" );
					pInfo->m_spuBufSide = _1st;
					pInfo->m_iopBufIndex = _1st;
					pInfo->m_iopOffset = 0;
					pInfo->remaining = pInfo->size;

					// preload left:
					if (DownloadMusic(_L))
					{
						pInfo->loadState++;
					}

					break;
				}
				case ( MUSIC_LOAD_STATE_PRELOADING_L ):
				{
					// Check that transfer is done
					if (!pInfo->m_spuTransDone)
					{
						break;
					}

					if ( pInfo->stop )		// Don't stop until we know the first CD stream and DMA to SPU have finished
					{
						StopMusic( );
						break;
					}

					ShowState( "music preload r\n" );
					SmallShowMusicState( "-MPR" );

					// preload right:
					DownloadMusic(_R);

					pInfo->loadState++;
					break;
				}	
				case ( MUSIC_LOAD_STATE_PRELOADING_R ):
				{
					// Check that transfer is done
					if (!pInfo->m_spuTransDone)
					{
						break;
					}

					if ( pInfo->stop )		// Don't stop until we the DMA to SPU has finished
					{
						StopMusic( );
						break;
					}

					// This will let the EE know that we are ready to play
					gEECommand |= PCMSTATUS_MUSIC_READY;

					// Now check if we are in a preload_only mode
					if (pInfo->m_preloadMode)
					{
						// We need to wait until we get a message from the EE to start
						break;
					}

					ShowState( "Music starting -- waiting for IRQ\n" );
					SmallShowMusicState( " -MSIRQ" );

					pInfo->status = PCM_STATUS_RUNNING;
					IncMusicBuffer();

					// Play music
					StartMusic();
#if TEST_PLAY_TIMING
					if (test_timing_send_timing_result)
					{
						test_timing_hit_point = TRUE;
					}
#endif // TEST_PLAY_TIMING

					pInfo->loadState++;
					break;
				}
			}
			break;

		case PCM_STATUS_RUNNING:
			pInfo->m_spuCurAddr = GetNAX( MUSIC_CORE, MUSIC_L_VOICE );

			switch ( pInfo->loadState )
			{
				case ( MUSIC_LOAD_STATE_WAITING_FOR_REFRESH ):
				{
					if ( pInfo->stop )
					{
						//pInfo->status = PCM_STATUS_READY_TO_STOP;
						StopMusic( );
						return;
					}

					// Wait for SPU buffer cross
					if (!OutsideMusicSPULoadBuffer())
					{
						break;
					}

					ShowState( "Music Running -- Wait For Last Load\n" );
					SmallShowStreamState( "-MRWLL" );

					// Change state and go immediately into it
					pInfo->loadState++;
				}

				case ( MUSIC_LOAD_STATE_WAITING_FOR_LAST_LOAD ):
				{
					if ( pInfo->stop )
					{
						StopMusic( );
						break;
					}

					// After we got this far, we've crossed the SPU buffer and we
					// know we need to download.  But if the new IOP buffer isn't
					// loaded and download before we cross SPU buffers AGAIN, then 
					// we know the stream has failed.  Time to panic!
					if (!OutsideMusicSPULoadBuffer())
					{
						if (!(gEECommand & PCMSTATUS_PANIC))
						{
							// Print only once
							Kprintf("ERROR: Music did not load data in time.  In SPU buffer %d and pos %x\n", pInfo->m_spuBufSide, pInfo->m_spuCurAddr - MUSIC_L_SPU_BUF_LOC);
						}
						gEECommand |= PCMSTATUS_PANIC;
						break;
					}

					ShowState( "Music Running -- Loading L\n" );
					SmallShowMusicState( "-MRLL" );

					// load in left side:
					if (DownloadMusic(_L))
					{
						pInfo->loadState++;
					}

					break;
				}

				case ( MUSIC_LOAD_STATE_LOADING_L ):
				{
					// Check that transfer is done
					if (!pInfo->m_spuTransDone)
					{
						break;
					}

					//Kprintf( "Music Running -- Loading R at offset %x and address %x\n", pInfo->m_iopOffset, gpIopBuf + MUSIC_R_IOP_OFFSET + pInfo->m_iopOffset );
					ShowState( "Music Running -- Loading R\n" );
					SmallShowMusicState( "-MRLR" );
					
					// load in right side:
					DownloadMusic(_R);

					pInfo->loadState++;
					break;
				}
				case ( MUSIC_LOAD_STATE_LOADING_R ):
				{
					// Check that transfer is done
					if (!pInfo->m_spuTransDone)
					{
						break;
					}

					ShowState( "Music Running -- Waiting for IRQ\n" );
					SmallShowMusicState( "-MRIRQ" );

					IncMusicBuffer();

					pInfo->loadState = MUSIC_LOAD_STATE_WAITING_FOR_REFRESH;
					break;
				}
				default:
					printf( "Unknown music loading state %d\n", pInfo->loadState );
					break;
			}
			break;

		case PCM_STATUS_TERMINATE: 
			pInfo->m_spuCurAddr = GetNAX( MUSIC_CORE, MUSIC_L_VOICE );

			if ( !pInfo->stop && ( ( pInfo->m_spuCurAddr > pInfo->m_spuEndAddr ) || ( pInfo->m_spuCurAddr <= pInfo->m_spuEndAddr - 16 ) ) )
			{
				break;
			}

			ShowState( "Music at end\n" );

		case PCM_STATUS_READY_TO_STOP:
			ShowState( "Music terminate\n" );
			SmallShowMusicState( "-MT!!!" );
			StopMusic( );
			break;

		default:
			printf( "unknown music status in pcm irx!!!\n" );
			break;
	}
}

void UpdateStream( int whichStream )
{
	struct StreamInfo *pInfo;
	pInfo = &gStreamInfo[ whichStream ];

	switch ( pInfo->status )
	{
		case PCM_STATUS_IDLE:
			break;

		case PCM_STATUS_PRELOAD:
			switch ( pInfo->loadState )
			{
				case ( STREAM_LOAD_STATE_IDLE ):
				{
					ShowState( "stream preload l\n" );
					SmallShowStreamState( "-SPL" );

					pInfo->m_spuBufSide = _1st;
					pInfo->m_iopBufIndex = _1st;
					pInfo->m_iopOffset = 0;
					pInfo->remaining = pInfo->size;

					if (DownloadStream(whichStream))
					{
						pInfo->loadState++;
					}

					break;
				}
				case ( STREAM_LOAD_STATE_PRELOADING ):
				{
					ShowState( "Stream starting -- waiting for IRQ\n" );
					SmallShowStreamState( "-SSIRQ" );

					// Check that transfer is done
					if (!pInfo->m_spuTransDone)
					{
						break;
					}

					if ( pInfo->stop )		// Don't stop until we know the first CD stream and DMA to SPU have finished
					{
						StopStream( whichStream );
						break;
					}

					// This will let the EE know that we are ready to play
					gEECommand |= PCMSTATUS_STREAM_READY(whichStream);

					// Now check if we are in a preload_only mode
					if (pInfo->m_preloadMode)
					{
						// We need to wait until we get a message from the EE to start
						break;
					}

					pInfo->status = PCM_STATUS_RUNNING;
					IncStreamBuffer(whichStream);

					// Play the stream
					StartStream(whichStream);
					//Kprintf("Started playing stream %d; paused = %d; volume = %x\n", whichStream, gStreamInfo[ whichStream ].paused, pInfo->volume);

					pInfo->loadState++;
					break;
				}
			}
			break;

		case PCM_STATUS_RUNNING:
			pInfo->m_spuCurAddr = GetNAX( STREAM_CORE, STREAM_VOICE( whichStream ) );

			switch ( pInfo->loadState )
			{
				case ( STREAM_LOAD_STATE_WAITING_FOR_REFRESH ):
				{
					if ( pInfo->stop )
					{
						//pInfo->status = PCM_STATUS_READY_TO_STOP;
						StopStream( whichStream );
						return;
					}

					// Wait for SPU buffer cross
					if (!OutsideStreamSPULoadBuffer(whichStream))
					{
						break;
					}

					ShowState( "Stream Running -- Wait For Last Load\n" );
					SmallShowStreamState( "-SRWLL" );

					// Inc state and start it directly
					pInfo->loadState++;
				}

				case ( STREAM_LOAD_STATE_WAITING_FOR_LAST_LOAD ):
				{
					if ( pInfo->stop )
					{
						StopStream( whichStream );
						break;
					}

					// After we got this far, we've crossed the SPU buffer and we
					// know we need to download.  But if the new IOP buffer isn't
					// loaded and download before we cross SPU buffers AGAIN, then 
					// we know the stream has failed.  Time to panic!
					if (!OutsideStreamSPULoadBuffer(whichStream))
					{
						if (!(gEECommand & PCMSTATUS_PANIC))
						{
							// Print only once
							Kprintf("ERROR: Stream %d did not load data in time.  In SPU buffer %d and pos %x\n", whichStream, pInfo->m_spuBufSide, pInfo->m_spuCurAddr - STREAM_SPU_BUF_LOC( whichStream ));
						}
						gEECommand |= PCMSTATUS_PANIC;
						break;
					}

					ShowState( "Stream Running -- Loading\n" );
					SmallShowStreamState( "-SRL" );

					if (DownloadStream(whichStream))
					{
						pInfo->loadState++;
					}

					break;
				}
				case ( STREAM_LOAD_STATE_LOADING ):
				{
					// Check that transfer is done
					if (!pInfo->m_spuTransDone)
					{
						break;
					}

					ShowState( "Stream Running -- Waiting for IRQ\n" );
					SmallShowStreamState( "-SRIRQ" );

					IncStreamBuffer(whichStream);

					pInfo->loadState = STREAM_LOAD_STATE_WAITING_FOR_REFRESH;
					break;
				}
				default:
					printf( "Unknown Stream loading state %d\n", pInfo->loadState );
					break;
			}
			break;

		case PCM_STATUS_TERMINATE: 
			pInfo->m_spuCurAddr = GetNAX( STREAM_CORE, STREAM_VOICE( whichStream ) );

			if ( !pInfo->stop && ( ( pInfo->m_spuCurAddr > pInfo->m_spuEndAddr ) || ( pInfo->m_spuCurAddr <= pInfo->m_spuEndAddr - 16 ) ) )
			{
				break;
			}

		case PCM_STATUS_READY_TO_STOP:
			if ( gUsingStreamDMA != (whichStream + 1) )
			{
				ShowState( "Stream terminate\n" );
				SmallShowStreamState( "-ST!!!" );
				StopStream( whichStream );
			}
			break;

		default:
			printf( "unknown stream status in pcm irx!!!\n" );
			break;
	}
}

// Reserve Stream DMA channel
int LockDMA( int whichStream )
{
	if ( gSPUUsingStreamDMA )
	{
		SmallShowStreamState( " -|spu_no" );		
		//Kprintf("******* Can't lock DMA for Stream %d because of SPU\n", whichStream);
		return FALSE;
	}
	if ( gUsingStreamDMA )
	{
		SmallShowStreamState( " -|no" );		
		//Kprintf("******* Can't lock DMA for Stream %d\n", whichStream);
		return FALSE;
	}
	else
	{
		SmallShowStreamState( " -|ok" );		
		gUsingStreamDMA = 1 + whichStream;
		//Kprintf("Locked DMA for Stream %d\n", whichStream);
		return TRUE;
	}
}

/**********************************************************************************
GetNAX
	Get current NAX (Address where channel is playing in SPU RAM)
	Code must check the NAX 3 times to decide which result is correct.
***********************************************************************************/
u_int GetNAX(int core,int ch)
{
	u_int pos,pos2,pos3;
	ch <<= 1;

	while(1)
	{
		pos=sceSdGetAddr(core|ch|SD_VA_NAX);
		pos2=sceSdGetAddr(core|ch|SD_VA_NAX);
		pos3=sceSdGetAddr(core|ch|SD_VA_NAX);

		if (pos == pos2)
			return(pos);
		else if (pos2 == pos3)
			return(pos2);
	}
}

#if CHECK_TRANSFER_INTEGRITY
void CheckTransfer( int dmaCh )
{
	if ( !sceSdVoiceTransStatus( dmaCh, SD_TRANS_STATUS_CHECK ) )
	{
		int i;
		printf( "waiting for voicetrans" );
		while ( !sceSdVoiceTransStatus( dmaCh, SD_TRANS_STATUS_CHECK ) )
		{
			printf( "." );
			for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
				;
		}
		printf( "\n" );
	}
}
#endif					

// Checks to see if current SPU playing address is outside the loading buffer
int OutsideMusicSPULoadBuffer()
{
	struct StreamInfo *pInfo;
	pInfo = &gMusicInfo;

	if ( pInfo->m_spuBufSide == _1st )
	{
		if ( pInfo->m_spuCurAddr > ( MUSIC_L_SPU_BUF_LOC + SB_BUF_HALF ) )
		{
#if CHECK_TRANSFER_INTEGRITY
			CheckTransfer( TRANS_DMA_CH_MUSIC );
#endif					
			return TRUE;
			//Kprintf("music position past end for SPU buffer %d\n", pInfo->bufSide);
		}
	}
	else
	{
		if ( pInfo->m_spuCurAddr < ( MUSIC_L_SPU_BUF_LOC + SB_BUF_HALF ) )
		{
#if CHECK_TRANSFER_INTEGRITY
			CheckTransfer( TRANS_DMA_CH_MUSIC );
#endif					
			return TRUE;
			//Kprintf("music position past end for SPU buffer %d\n", pInfo->bufSide);
		}
	}

	return FALSE;
}

// Checks to see if current SPU playing address is outside the loading buffer
int OutsideStreamSPULoadBuffer(int whichStream)
{
	struct StreamInfo *pInfo;
	pInfo = &gStreamInfo[ whichStream ];

	if ( pInfo->m_spuBufSide == _1st )
	{
		if ( pInfo->m_spuCurAddr > ( STREAM_SPU_BUF_LOC( whichStream ) + SB_BUF_HALF ) )
		{
#if CHECK_TRANSFER_INTEGRITY
			CheckTransfer( TRANS_DMA_CH_STREAM );
#endif
			return TRUE;
		}
	}
	else
	{
		if ( pInfo->m_spuCurAddr < ( STREAM_SPU_BUF_LOC( whichStream ) + SB_BUF_HALF ) )
		{
#if CHECK_TRANSFER_INTEGRITY
			CheckTransfer( TRANS_DMA_CH_STREAM );
#endif					
			return TRUE;
		}
	}

	return FALSE;
}

// Downloads music data from the IOP to a SPU buffer
int DownloadMusic(int whichChannel)
{
	int music_iop_offset;
	int music_spu_addr;

	struct StreamInfo *pInfo;
	pInfo = &gMusicInfo;

	if (whichChannel == _L)
	{
		//int iopBufferIndex = (pInfo->m_iopOffset < MUSIC_HALF_IOP_BUFFER_SIZE) ? 0 : 1;
		if (!pInfo->m_iopBufLoaded[pInfo->m_iopBufIndex])
		{
			//Kprintf("****** ERROR: Music buffer #%d not loaded.\n", pInfo->m_iopBufIndex);
			return FALSE;
		}

		pInfo->m_spuTransSize = SB_BUF_HALF;
		pInfo->remaining -= SB_BUF_HALF;
		if ( pInfo->remaining < 0 )
		{
			pInfo->m_spuTransSize += pInfo->remaining;
		}

		music_iop_offset = MUSIC_L_IOP_OFFSET;
		music_spu_addr = MUSIC_L_SPU_BUF_LOC;
	}
	else
	{
		music_iop_offset = MUSIC_R_IOP_OFFSET;
		music_spu_addr = MUSIC_R_SPU_BUF_LOC;
	}

	// load in a side:
	if ( pInfo->m_spuBufSide == _1st )
	{
		_AdpcmSetMarkSTART( gpIopBuf + music_iop_offset + pInfo->m_iopOffset, pInfo->m_spuTransSize );
	}
	if ( pInfo->remaining <= 0 )
	{
		_AdpcmSetMarkFINAL( gpIopBuf + music_iop_offset + pInfo->m_iopOffset, pInfo->m_spuTransSize );
		pInfo->m_spuEndAddr = MUSIC_L_SPU_BUF_LOC + pInfo->m_spuTransSize + SB_BUF_HALF * pInfo->m_spuBufSide;
	}
	else if ( pInfo->m_spuBufSide == _2nd )
	{
		_AdpcmSetMarkEND( gpIopBuf + music_iop_offset + pInfo->m_iopOffset, pInfo->m_spuTransSize );
	}

	pInfo->m_spuTransDone = FALSE;
	sceSdVoiceTrans( TRANS_DMA_CH_MUSIC, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
		(unsigned char *) gpIopBuf + music_iop_offset + pInfo->m_iopOffset,
		( music_spu_addr + SB_BUF_HALF * pInfo->m_spuBufSide ), pInfo->m_spuTransSize );
	//Kprintf("Downloading music channel %d from IOP %x to SPU %x of size %d\n", whichChannel, gpIopBuf + music_iop_offset + pInfo->m_iopOffset,
	//		( music_spu_addr + SB_BUF_HALF * pInfo->m_spuBufSide ), pInfo->m_spuTransSize );


	return TRUE;
}

// Downloads audio stream data from the IOP to a SPU buffer
int DownloadStream(int whichStream)
{
	struct StreamInfo *pInfo;
	pInfo = &gStreamInfo[ whichStream ];

	//iopBufferIndex = (pInfo->m_iopOffset < STREAM_HALF_IOP_BUFFER_SIZE) ? 0 : 1;
	if (!pInfo->m_iopBufLoaded[pInfo->m_iopBufIndex])
	{
		//Kprintf("****** ERROR: Stream %d buffer #%d not loaded.\n", whichStream, pInfo->m_iopBufIndex);
		return FALSE;
	}

	if (!LockDMA(whichStream))
	{
		return FALSE;
	}

	pInfo->m_spuTransSize = SB_BUF_HALF;
	pInfo->remaining -= SB_BUF_HALF;
	if ( pInfo->remaining < 0 )
	{
		pInfo->m_spuTransSize += pInfo->remaining;
	}
	//Kprintf( "Stream: IOP offset %d, transfer %d, remaining %d\n", pInfo->m_iopOffset, pInfo->m_spuTransSize, pInfo->remaining);

	// load in left side:
	if ( pInfo->m_spuBufSide == _1st )
	{
		_AdpcmSetMarkSTART( gpIopBuf + STREAM_IOP_OFFSET( whichStream ) + pInfo->m_iopOffset, pInfo->m_spuTransSize );

	}
	if ( pInfo->remaining <= 0 )
	{
		_AdpcmSetMarkFINAL( gpIopBuf + STREAM_IOP_OFFSET( whichStream ) + pInfo->m_iopOffset, pInfo->m_spuTransSize );
		pInfo->m_spuEndAddr = STREAM_SPU_BUF_LOC( whichStream ) + pInfo->m_spuTransSize + SB_BUF_HALF * pInfo->m_spuBufSide;
	}
	else if ( pInfo->m_spuBufSide == _2nd )
	{
		_AdpcmSetMarkEND( gpIopBuf + STREAM_IOP_OFFSET( whichStream ) + pInfo->m_iopOffset, pInfo->m_spuTransSize );
	}

	pInfo->m_spuTransDone = FALSE;
	sceSdVoiceTrans( TRANS_DMA_CH_STREAM, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
		(unsigned char *) gpIopBuf + STREAM_IOP_OFFSET( whichStream ) + pInfo->m_iopOffset,
		( STREAM_SPU_BUF_LOC( whichStream ) + SB_BUF_HALF * pInfo->m_spuBufSide ), pInfo->m_spuTransSize );

	//Kprintf("Downloaded Stream %d buffer #%d\n", whichStream, iopBufferIndex);

	return TRUE;
}

void IncMusicBuffer()
{
	struct StreamInfo *pInfo;
	pInfo = &gMusicInfo;

	pInfo->m_iopOffset += SB_BUF_HALF;
	pInfo->m_spuBufSide = ( pInfo->m_spuBufSide == _1st ) ? _2nd : _1st;

	if ( pInfo->remaining <= 0 )
	{
		// the vag data up to the end should contain all zeros,
		// so it doesn't matter that the interrupt won't happen until the end.
		// stop the music when it gets to the end:
		pInfo->status = PCM_STATUS_TERMINATE;
		ShowState( "Music last buffer\n" );
	}
	else
	{
		if ( (pInfo->m_iopBufIndex == _1st) && (pInfo->m_iopOffset >= MUSIC_HALF_IOP_BUFFER_SIZE) )
		{
			//Dbug_Printf( "music move over\n" );
			pInfo->m_iopOffset += MUSIC_HALF_IOP_BUFFER_SIZE; // move over by half the size since L/R load in together...
			pInfo->m_iopBufIndex = _2nd;
			if ( pInfo->remaining > MUSIC_HALF_IOP_BUFFER_SIZE )
			{
				gEECommand |= PCMSTATUS_NEED_MUSIC_BUFFER_0;
				pInfo->m_iopBufLoaded[0] = FALSE;
			}
		}
		else if ( (pInfo->m_iopBufIndex == _2nd) && ( pInfo->m_iopOffset >= ( MUSIC_IOP_BUFFER_SIZE + MUSIC_HALF_IOP_BUFFER_SIZE ) ) )
		{
			//Dbug_Printf( "music move back\n" );
			pInfo->m_iopOffset = 0;
			pInfo->m_iopBufIndex = _1st;
			if ( pInfo->remaining > MUSIC_HALF_IOP_BUFFER_SIZE )
			{
				gEECommand |= PCMSTATUS_NEED_MUSIC_BUFFER_1;
				pInfo->m_iopBufLoaded[1] = FALSE;
			}
		}
	}
}

void IncStreamBuffer(int whichStream)
{
	struct StreamInfo *pInfo;
	pInfo = &gStreamInfo[ whichStream ];

	pInfo->m_iopOffset += SB_BUF_HALF;
	pInfo->m_spuBufSide = ( pInfo->m_spuBufSide == _1st ) ? _2nd : _1st;
	if ( pInfo->remaining <= 0 )
	{
		// the vag data up to the end should contain all zeros,
		// so it doesn't matter that the interrupt won't happen until the end.
		// stop the stream when it gets to the end:
		pInfo->status = PCM_STATUS_TERMINATE;
	}
	else
	{
		// Must check if it has crossed from one IOP buffer to the other
		if ( (pInfo->m_iopBufIndex == _1st) && (pInfo->m_iopOffset >= STREAM_HALF_IOP_BUFFER_SIZE) )
		{
			SmallShowStreamState( "-SMO" );
			pInfo->m_iopBufIndex = _2nd;
			if ( pInfo->remaining > STREAM_HALF_IOP_BUFFER_SIZE )
			{
				gEECommand |= ( PCMSTATUS_NEED_STREAM0_BUFFER_0 << whichStream );
				pInfo->m_iopBufLoaded[0] = FALSE;
				//Kprintf("Asking for Stream %d buffer #%d\n", whichStream, 0);
			}
		}
		else if ( (pInfo->m_iopBufIndex == _2nd) && pInfo->m_iopOffset >= STREAM_IOP_BUFFER_SIZE )
		{
			SmallShowStreamState( "-SMB" );
			pInfo->m_iopOffset -= STREAM_IOP_BUFFER_SIZE;
			pInfo->m_iopBufIndex = _1st;
			if ( pInfo->remaining > STREAM_HALF_IOP_BUFFER_SIZE )
			{
				gEECommand |= ( PCMSTATUS_NEED_STREAM0_BUFFER_1 << whichStream );
				pInfo->m_iopBufLoaded[1] = FALSE;
				//Kprintf("Asking for Stream %d buffer #%d\n", whichStream, 1);
			}
		}
	}
}

// Start the music that is already preloaded
void StartMusic()
{
	AdpcmSetMusicVolumeDirect( gMusicInfo.volume );
	sceSdSetSwitch( MUSIC_CORE | SD_S_KON, ( ( 1 << MUSIC_R_VOICE ) | ( 1 << MUSIC_L_VOICE ) ) );
}

// Start the stream that is already preloaded
void StartStream(int whichStream)
{
	AdpcmSetStreamVolumeDirect( gStreamInfo[ whichStream ].volume, whichStream );
	// Here's the bug! This was causing a recently paused stream to un-pause...
	if ( !gStreamInfo[ whichStream ].paused )
	{
		sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_PITCH, gStreamInfo[ whichStream ].pitch );
	}
	sceSdSetSwitch( STREAM_CORE | SD_S_KON, 1 << STREAM_VOICE( whichStream ) );

	//Kprintf("Stream %d using SPU memory (%6x-%6x)\n", whichStream, STREAM_SPU_BUF_LOC( whichStream ), STREAM_SPU_BUF_LOC( whichStream ) + SB_BUF_SIZE - 1);
}

/* internal */
int RefreshStreams( int status )
{
	int i;

    while ( 1 )
	{
		WaitSema(gSem);

		// Update everything
		for ( i = 0; i < NUM_STREAMS; i++ )
		{
			UpdateStream( i );
		}
		UpdateMusic( );

		if ( gMusicInfo.volumeSet )
		{
			gMusicInfo.volumeSet = 0;
			AdpcmSetMusicVolumeDirect( gMusicInfo.volume );
		}
		for ( i = 0; i < NUM_STREAMS; i++ )
		{
			if ( gStreamInfo[ i ].volumeSet )
			{
				gStreamInfo[ i ].volumeSet = 0;
				AdpcmSetStreamVolumeDirect( gStreamInfo[ i ].volume, i );
			}
		}

		SendStatus();
    }
    return 0;
}

void _ShowStreamInfo( struct StreamInfo *pInfo )
{
	printf( "\nStream Info:\n" );
	printf( "paused     %d\n", pInfo->paused );
	printf( "m_spuEndAddr %d\n", pInfo->m_spuEndAddr );
	printf( "m_spuBufSide %d\n", pInfo->m_spuBufSide );
	printf( "m_spuTransSize %d\n", pInfo->m_spuTransSize );
	printf( "loadState  %d\n", pInfo->loadState );
	printf( "volume     %d\n", pInfo->volume );
	printf( "volumeSet  %d\n", pInfo->volumeSet );
	printf( "stop       %d\n", pInfo->stop );
	printf( "status     %d\n", pInfo->status );
	printf( "size       %d\n", pInfo->size );
	printf( "remaining  %d\n", pInfo->remaining );
	printf( "m_iopOffset %d\n", pInfo->m_iopOffset );
	printf( "pitch      %d\n", pInfo->pitch );
	printf( "eecom      %d\n", gEECommand );
}

void PauseStream( int whichStream, int pause )
{
	ShowStreamInfo( &gStreamInfo[ whichStream ] );
	if ( pause )
	{
		if ( !gStreamInfo[ whichStream ].paused )
		{
			sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_PITCH, 0 );
			gStreamInfo[ whichStream ].paused = 1;
			ShowAction( "Pausing stream\n" );
			SmallShowAction( "-ps" );
		}
	}
	else
	{
		if ( gStreamInfo[ whichStream ].paused )
		{
			sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( whichStream ) << 1 ) | SD_VP_PITCH, gStreamInfo[ whichStream ].pitch );
			gStreamInfo[ whichStream ].paused = 0;
			ShowAction( "Unpausing stream\n" );
			SmallShowAction( "-ups" );
		}
	}
}

volatile int ret = 0;

#define U_DATA( x )	( *( ( unsigned int * ) data_ + x ) ) 
#define DATA( x )	( *( ( int * ) data_ + x ) )

static void *dispatch( unsigned int command, void *data_, int size )
{ 
    int ch;

//    printf( "size %d", size );
//	printf("# dispatch [%04x] %x, %x, %x, %x\n", command, *((int*) data_ + 0), *((int*) data_ + 1), *((int*) data_ + 2), *((int*) data_ + 3));

    ch = command & EzADPCM_CH_MASK;
    switch ( command & EzADPCM_COMMAND_MASK )
	{
		case EzADPCM_INIT:
			ret = AdpcmInit( DATA( 0 ) ); // iop buffer pointer
			break;

		case EzADPCM_QUIT:
			AdpcmQuit( );
			break;

		case EzADPCM_PRELOADMUSIC:
			ret = AdpcmPlayMusic( DATA( 0 ), TRUE );  // size of the entire PCM data in the file
			break;

		case EzADPCM_PRELOADSTREAM:
			ret = AdpcmPlayStream( DATA( 0 ), ch, 0, 0, TRUE); // size of the entire PCM data in the file
			break;

		case EzADPCM_PLAYPRELOADEDMUSIC:
			ret = AdpcmPlayPreloadedMusic();
			break;

		case EzADPCM_PLAYPRELOADEDSTREAM:
			ret = AdpcmPlayPreloadedStream(ch, DATA(0), DATA(1));
			break;

		case EzADPCM_PLAYMUSIC:
			ret = AdpcmPlayMusic( DATA( 0 ), FALSE );  // size of the entire PCM data in the file
			break;

		case EzADPCM_PLAYSTREAM:
			ret = AdpcmPlayStream( DATA( 0 ), ch, DATA( 1 ), DATA( 2 ), FALSE ); // size of the entire PCM data in the file
			break;
		
		case EzADPCM_PAUSEMUSIC:
			if ( DATA( 0 ) )
			{
				if ( !gMusicInfo.paused )
				{
					sceSdSetParam( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_PITCH, 0 );
					sceSdSetParam( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_PITCH, 0 );
					gMusicInfo.paused = 1;
					ShowAction( "Pausing music\n" );
					SmallShowAction( "-pm" );
				}
			}
			else
			{
				if ( gMusicInfo.paused )
				{
					sceSdSetParam( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_PITCH, DEFAULT_PITCH );
					sceSdSetParam( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_PITCH, DEFAULT_PITCH );
					gMusicInfo.paused = 0;
					ShowAction( "Unpausing music\n" );
					SmallShowAction( "-upm" );
				}
			}
			break;

		case EzADPCM_PAUSESTREAMS:
		{
			int whichStream;
			
			for ( whichStream = 0; whichStream < NUM_STREAMS; whichStream++ )
			{
				PauseStream( whichStream, DATA( 0 ) );
			}
			break;
		}
		
		case EzADPCM_PAUSESTREAM:
			PauseStream( ch, DATA( 0 ) );
			break;
		
		case EzADPCM_STOPMUSIC:
			ret = AdpcmStopMusic( );
			break;

		case EzADPCM_STOPSTREAMS:
		{
			int i;
			for ( i = 0; i < NUM_STREAMS; i++ )
			{
				ret = AdpcmStopStream( i );
			}
			break;
		}
		
		case EzADPCM_STOPSTREAM:
			if ( NUM_STREAMS > DATA( 0 ) )
				ret = AdpcmStopStream( DATA( 0 )  );
			break;
		
		case EzADPCM_SETMUSICVOL:
			AdpcmSetMusicVolume( U_DATA( 0 ) );
			break;

		case EzADPCM_SETSTREAMVOLANDPITCH:
			sceSdSetParam( STREAM_CORE | ( STREAM_VOICE( ch ) << 1 ) | SD_VP_PITCH, ( U_DATA( 1 ) / 2 ) );
			// Adjust for pitch --> 22,050 hz when standard is 48000
			gStreamInfo[ ch ].pitch = STREAM_PITCH( U_DATA( 1 ) );
			// intentional fall through:
		case EzADPCM_SETSTREAMVOL:
			AdpcmSetStreamVolume( U_DATA( 0 ), ch );
			break;

#if 0
		case EzADPCM_SETSTREAMGLOBVOL:
			AdpcmSetStreamGlobVol( U_DATA( 0 ) );
			break;

		case EzADPCM_GETMUSICSTATUS:
			ret = AdpcmGetMusicStatus( );
			break;
#endif

		case EzADPCM_SDINIT:
			AdpcmSdInit( );
			break;

		default:
			ERROR (("EzADPCM driver error: unknown command %d \n", DATA( 0 ) ) );
			break;
    }
    //printf( "! return value = %x \n", ret );
    return (void*)(&ret);
}

// SifCmd variables

//static sceSifCmdData cmdbuffer[NUM_COMMAND_HANDLERS];
unsigned int gCmdSem;

// Note these can be changed in an interrupt
#define NUM_STREAM_REQUESTS	(10)

static volatile SStreamRequestPacket StreamRequestArray[NUM_STREAM_REQUESTS];
static volatile int FirstStreamRequest;		// Interrupt only reads this value.
static volatile	int FreeStreamRequest;		// This is the main variable that changes in the interrupt

static SSifCmdStreamResultPacket StreamResult;

void request_callback(void *p, void *q);
void load_status_callback(void *p, void *q);

int sce_adpcm_loop( void )
{
	int oldStat;
	int last_cmd_id = -1;

	// Create semaphore to signal command came in
	struct SemaParam sem;
	sem.initCount = 0;
	sem.maxCount = 1;
	sem.attr = AT_THFIFO;
	gCmdSem = CreateSema (&sem);

	// Init the stream queue
	FirstStreamRequest = 0;
	FreeStreamRequest = 0;

	sceSifInitRpc(0);

	// set local buffer & functions
	CpuSuspendIntr(&oldStat);

	// SIFCMD
	// No longer need to call sceSifSetCmdBuffer() since we share it with fileio.irx
	//sceSifSetCmdBuffer( &cmdbuffer[0], NUM_COMMAND_HANDLERS);

	sceSifAddCmdHandler(STREAM_REQUEST_COMMAND, (void *) request_callback, NULL );
	sceSifAddCmdHandler(STREAM_LOAD_STATUS_COMMAND, (void *) load_status_callback, NULL );

	CpuResumeIntr(oldStat);

	// The loop
	while (1) {
		//printf("waiting for pcm command semaphore\n");
		WaitSema(gCmdSem);		// Get signal from callback
		//printf("got pcm command semaphore\n");

		// Note that FreeStreamRequest can change in the interrupt at any time
		// Also, FirstStreamRequest is examined in the interrupt, but just to make sure we don't overflow the buffer
		//Dbg_Assert(FreeStreamRequest != FirstStreamRequest);
		while (FreeStreamRequest != FirstStreamRequest)
		{
			int *p_ret;
			volatile SStreamRequestPacket *p_request = &(StreamRequestArray[FirstStreamRequest]);

#if TEST_PLAY_TIMING
			if (p_request->m_request.m_command == EzADPCM_PLAYPRELOADEDMUSIC)
			{
				test_timing_request_id = p_request->m_request_id;
				test_timing_send_timing_result = TRUE;
				test_timing_hit_point = FALSE;
			}
#endif // TEST_PLAY_TIMING

			//printf("EzPcm: got request id %d with command %x\n", p_request->m_request_id, p_request->m_request.m_command);
			p_ret = dispatch(p_request->m_request.m_command, (void *) p_request->m_request.m_param, 0);

			// Send the result back
			if (last_cmd_id >= 0)	// Wait for previous send to complete (it should already be done, though)
			{
               while(sceSifDmaStat(last_cmd_id) >= 0)
				   printf("Waiting for PCM DMA\n");
			}
			// Gotta copy the id into SStreamRequest
			StreamResult.m_header.opt = p_request->m_request_id;	// Copy id
			StreamResult.m_return_value = *p_ret;
			StreamResult.m_status_flags = AdpcmGetStatus();
            last_cmd_id = sceSifSendCmd(STREAM_RESULT_COMMAND, &StreamResult, sizeof(StreamResult), 0, 0, 0);

			// Increment request index; Note that interrupt can look at this
			FirstStreamRequest = (FirstStreamRequest + 1) % NUM_STREAM_REQUESTS;
		}
	}

    return 0;
}

void request_callback(void *p, void *q)
{
	SSifCmdStreamRequestPacket *h = (SSifCmdStreamRequestPacket *) p;

	// Check to make sure we can add
	int nextFreeReq = (FreeStreamRequest + 1) % NUM_STREAM_REQUESTS;
	if (nextFreeReq == FirstStreamRequest)
	{
		// We can't allow a request to be ignored.  We must abort
		//Dbg_Assert(0);
	}

	// Copy the request into the reuest queue
	StreamRequestArray[FreeStreamRequest].m_request = h->m_request;
	StreamRequestArray[FreeStreamRequest].m_request_id = h->m_header.opt;
	FreeStreamRequest = nextFreeReq;

	// And wake up the dispatch thread
	iSignalSema(gCmdSem);

	return;
}

void load_status_callback(void *p, void *q)
{
	SSifCmdStreamLoadStatusPacket *h = (SSifCmdStreamLoadStatusPacket *) p;

	// Mark buffer as loaded
	if (h->m_stream_num == -1)
	{
		//Kprintf("Music buffer #%d finished loading.\n", h->m_buffer_num);
		gMusicInfo.m_iopBufLoaded[h->m_buffer_num] = TRUE;
	}
	else
	{
		//Kprintf("Stream %d buffer #%d finished loading.\n", h->m_stream_num, h->m_buffer_num);
		gStreamInfo[h->m_stream_num].m_iopBufLoaded[h->m_buffer_num] = TRUE;
	}
}

void SendStatus()
{
	static SSifCmdStreamStatusPacket StreamStatus;

#if TEST_PLAY_TIMING
	if (test_timing_send_timing_result && test_timing_hit_point)
	{
		sEELastCommand = 0xFFFFFFFF;
		StreamStatus.m_header.opt = test_timing_request_id;
		test_timing_send_timing_result = FALSE;
	}
	else
	{
		StreamStatus.m_header.opt = 0;
	}
#endif // TEST_PLAY_TIMING

	// Only send updates when it has changed
	if (AdpcmHasStatusChanged())
	{
		StreamStatus.m_status_flags = AdpcmGetStatus();
		sceSifSendCmd(STREAM_STATUS_COMMAND, &StreamStatus, sizeof(StreamStatus), 0, 0, 0);
	}
}

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */

/* DON'T ADD STUFF AFTER THIS */


================================================
FILE: Code/Gel/Music/Ngps/Pcm/pcm_ent.c
================================================
/* Vag streaming into SPU2 -- Converted from Sony samples -- matt may 2001 */

#include 
#include 
#include 
#include 
#include 
#include 
#include "pcm.h"
#include "pcmiop.h"

ModuleInfo Module = {"pcm_driver", 0x0102};

// in command.c
extern int sce_adpcm_loop (void);
extern int sce_sound_loop (void);

int start ( void )
{
    struct ThreadParam param;
    int th;

    if (! sceSifCheckInit ())
	sceSifInit ();
    sceSifInitRpc (0);

    printf ("PCM driver version 666\n");

    param.attr         = TH_C;
    param.entry        = sce_adpcm_loop;
    param.initPriority = BASE_priority - 2;
    param.stackSize    = 0x800;
    param.option       = 0;
    th = CreateThread (¶m);
    if (th > 0) {
		StartThread (th, 0);
    } else {
		return 1;
    }

    param.attr         = TH_C;
    param.entry        = sce_sound_loop;
    param.initPriority = BASE_priority - 2;
    param.stackSize    = 0x800;
    param.option       = 0;
    th = CreateThread (¶m);
    if (th > 0) {
		StartThread (th, 0);
        printf (" Exit PCM loader thread \n");
		return 0;
	} else {
		return 1;
	}
}

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* DON'T ADD STUFF AFTER THIS */


================================================
FILE: Code/Gel/Music/Ngps/Pcm/pcm_sound.c
================================================
/* Vag streaming into SPU2 -- from sony sample -- matt may 2001 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "pcm.h"
#include "pcmiop.h"

#define PRINT_DROPPED_REQUESTS	0

// ================================================================

#define _1st 0
#define _2nd 1

#define TRUE  1
#define FALSE 0

#define TRANS_DMA_CH_MUSIC		1
#define TRANS_DMA_CH_STREAM		0

#define NUM_CORES				2

unsigned int gSndSem;

extern volatile int gUsingStreamDMA;
extern volatile int gSPUUsingStreamDMA;

#if PRINT_DROPPED_REQUESTS
static int s_received_requests = 0;
#endif

//	sceSdSetTransIntrHandler( TRANS_DMA_CH_MUSIC, ( sceSdTransIntrHandler ) _AdpcmDmaIntMusic, ( void * ) &gSem );
//  sceSdSetTransIntrHandler( TRANS_DMA_CH_STREAM, ( sceSdTransIntrHandler ) _AdpcmDmaIntStream, ( void * ) &gSem );

// Used to hold the queued up SPU switch registers
volatile static uint32			s_spu_reg_kon[NUM_CORES];
volatile static uint32			s_spu_reg_koff[NUM_CORES];
volatile static uint32			s_spu_kon_changed[NUM_CORES];
volatile static uint32			s_spu_koff_changed[NUM_CORES];
volatile static uint32			s_spu_reg_endx[NUM_CORES];
volatile static uint32			s_switch_alarm_set;
volatile static uint32			s_send_spu_status;

#define	SET_PARAM_ALARM_TIME	(100)			// Number of microseconds to wait before setting SPU switches (must be greater
												// than 1/24000 seconds)

uint32 switch_set_callback(void *p_nothing)
{
	int core;

	for (core = 0; core < NUM_CORES; core++)
	{
		// First, get the status
		s_spu_reg_endx[core] = sceSdGetSwitch( core | SD_S_ENDX );

		if (s_spu_kon_changed[core])
		{
			sceSdSetSwitch( core | SD_S_KON, s_spu_reg_kon[core] );
			s_spu_kon_changed[core] = FALSE;
			s_spu_reg_endx[core] &= ~s_spu_reg_kon[core];	// Update the status since SPU won't do it for another tick
			s_spu_reg_kon[core] = 0;
		}

		if (s_spu_koff_changed[core])
		{
			sceSdSetSwitch( core | SD_S_KOFF, s_spu_reg_koff[core] );
			s_spu_koff_changed[core] = FALSE;
			s_spu_reg_endx[core] |= s_spu_reg_koff[core];	// Update the status since SPU won't do it for another tick
			s_spu_reg_koff[core] = 0;
		}
	}

	s_switch_alarm_set = FALSE;
	s_send_spu_status = TRUE;

	// And wake up the dispatch thread
	iSignalSema(gSndSem);

	return 0;
}

// This function will set an alarm so that multiple KON and KOFF settings are combined.  If it isn't these type
// of switches, it will be passed through
void queue_set_switch(unsigned short entry, unsigned int value)
{
	int core = entry & 0x1;
	int switch_reg = entry & 0xFF00;
	int oldStat;

	// Make sure alarm callback doesn't run here
	CpuSuspendIntr(&oldStat);

	switch (switch_reg)
	{
	case SD_S_KON:
		if(s_spu_reg_koff[core] & value)
		{
			//Dbg_MsgAssert(0, ("Turning on a voice that was turned off this frame."));
			Kprintf("Turning on a voice that was turned off this SPU tick.\n");
			s_spu_reg_koff[core] &= ~(s_spu_reg_koff[core] & value);	// turn off those bits
		}

		s_spu_reg_kon[core] |= value;
		s_spu_kon_changed[core] = TRUE;

		break;

	case SD_S_KOFF:
		if(s_spu_reg_kon[core] & value)
		{
			//Dbg_MsgAssert(0, ("Turning off a voice that was turned on this frame."));
			Kprintf("Turning off a voice that was turned on this SPU tick.\n");
			s_spu_reg_kon[core] &= ~(s_spu_reg_kon[core] & value);		// turn off those bits
		}

		s_spu_reg_koff[core] |= value;
		s_spu_koff_changed[core] = TRUE;

		break;

	default:
		// Just set SPU and exit (so we don't set alarm)
		sceSdSetSwitch( entry, value );
		CpuResumeIntr(oldStat);
		return;
	}

	// Turn on alarm if it isn't already on
	if (!s_switch_alarm_set)
	{
		struct SysClock clock;
		USec2SysClock(SET_PARAM_ALARM_TIME, &clock);

		SetAlarm(&clock, switch_set_callback, NULL);

		s_switch_alarm_set = TRUE;
	}

	CpuResumeIntr(oldStat);
}

// This function belongs in pcm_sound.c, but because of some SN Sys link error, it has to be in here for now.
int execute_sound_request(unsigned short function, unsigned short entry, unsigned int value,
						  unsigned int arg0, unsigned int arg1, unsigned int arg2, ESendSoundResult *p_send_result)
{ 
	int ret = -1;
	*p_send_result = SEND_SOUND_RESULT_FALSE;

	switch (function)
	{
	case rSdInit:
		ret = sceSdInit( entry );
		*p_send_result = SEND_SOUND_RESULT_TRUE;
		break;

	case rSdSetParam:
		sceSdSetParam( entry, value );
		break;

	case rSdGetParam:
		ret = sceSdGetParam( entry );
		*p_send_result = SEND_SOUND_RESULT_TRUE;
		break;

	case rSdSetSwitch:
		queue_set_switch( entry, value );
		break;

	case rSdGetSwitch:
		ret = sceSdGetSwitch( entry );
		*p_send_result = SEND_SOUND_RESULT_TRUE;
		break;

	case rSdSetAddr:
		sceSdSetAddr( entry, value );
		//Kprintf("Setting addr %x to %x\n", entry, value);
		break;

	case rSdGetAddr:
		ret = sceSdGetAddr( entry );
		*p_send_result = SEND_SOUND_RESULT_TRUE;
		break;

	case rSdSetCoreAttr:
		sceSdSetCoreAttr( entry, value );
		break;

	case rSdGetCoreAttr:
		ret = sceSdGetCoreAttr( entry );
		*p_send_result = SEND_SOUND_RESULT_TRUE;
		break;

	case rSdNote2Pitch:
		ret = sceSdNote2Pitch ( entry /*center_note*/, value /*center_fine*/, arg0 /*note*/, arg1 /*fine*/);
		*p_send_result = SEND_SOUND_RESULT_TRUE;
		break;

	case rSdPitch2Note:
		ret = sceSdPitch2Note ( entry /*center_note*/, value /*center_fine*/, arg0 /*pitch*/);
		*p_send_result = SEND_SOUND_RESULT_TRUE;
		break;

	case rSdVoiceTrans:
		sceSdVoiceTrans( entry /*channel*/, value /*mode*/, (u_char *) arg0 /*m_addr*/, arg1 /*s_addr*/, arg2 /*size*/ );
		break;

	case rSdBlockTrans:
		sceSdBlockTrans( entry /*channel*/, value /*mode*/, (u_char *) arg0 /*m_addr*/, arg1 /*size*/, (u_char *) arg2 /*start_addr*/ );
		break;

	case rSdVoiceTransStatus:
		ret = sceSdVoiceTransStatus (entry /*channel*/, value /*flag*/);
		*p_send_result = SEND_SOUND_RESULT_TRUE;
		break;

	case rSdBlockTransStatus:
		ret = sceSdBlockTransStatus (entry /*channel*/, value /*flag*/);
		*p_send_result = SEND_SOUND_RESULT_TRUE;
		break;

	case rSdSetEffectAttr:
		{
			sceSdEffectAttr attr;

			attr.core		= entry;
			attr.mode		= value;
			attr.depth_L	= (short) (arg0 >> 16);
			attr.depth_R	= (short) (arg0 & 0xFFFF);
			attr.delay		= arg1;
			attr.feedback	= arg2;

			//Kprintf("About to set core %d reverb mode to %d\n", entry, value);
			ret = sceSdSetEffectAttr(entry /*core*/, &attr);

			// Wait for Stream DMA (0) to finish
			while (gUsingStreamDMA)
				;

			// Clear out reverb memory (and make sure it doesn't interfere with streams)
			gSPUUsingStreamDMA = 1;
            sceSdCleanEffectWorkArea(entry, 0, value);
            sceSdVoiceTransStatus(0, SD_TRANS_STATUS_WAIT);
			if ((value == SD_REV_MODE_OFF) && gSPUUsingStreamDMA)		// This mode doesn't seem to cause a DMA callback
			{
				gSPUUsingStreamDMA = 0;
				//Kprintf("Manually clearing DMA flag\n");
			}
			//Kprintf("Set core %d reverb mode to %d with return value of %d and %d\n", entry, value, ret);
		}
		*p_send_result = SEND_SOUND_RESULT_TRUE;
		break;

		// This is a meaningless call because we can't send the data back
//	case rSdGetEffectAttr:
//		sceSdGetEffectAttr(entry /*core*/, (sceSdEffectAttr *) value /*attr*/);
//		break;

//#define rSdClearEffectWorkArea   0x8150
//#define rSdStopTrans             0x8180
//#define rSdCleanEffectWorkArea   0x8190
//#define rSdSetEffectMode         0x81a0
//#define rSdSetEffectModeParams   0x81b0

	default:
		ret = -1;
		*p_send_result = SEND_SOUND_RESULT_PANIC;
		Kprintf("Unknown or unsupported SPU function %x\n", function);
		break;
	}

    return ret;
}

// SifCmd variables

// Note these can be changed in an interrupt
#define NUM_SOUND_REQUESTS	(128)

static volatile SSoundRequest SoundRequestArray[NUM_SOUND_REQUESTS];
static volatile int FirstSoundRequest;		// Interrupt only reads this value.
static volatile	int FreeSoundRequest;		// This is the main variable that changes in the interrupt

// Set to TRUE if we hit a problem
static volatile int global_sound_panic = FALSE;

static SSifCmdSoundResultPacket SoundResult;

void sound_request_callback(void *p, void *q);

int sce_sound_loop( void )
{
	int oldStat;
	int last_cmd_id = -1;

	// Create semaphore to signal command came in
	struct SemaParam sem;
	sem.initCount = 0;
	sem.maxCount = 1;
	sem.attr = AT_THFIFO;
	gSndSem = CreateSema (&sem);

	// Init the stream queue
	FirstSoundRequest = 0;
	FreeSoundRequest = 0;

	// set local buffer & functions
	CpuSuspendIntr(&oldStat);

	// SIFCMD
	// No longer need to call sceSifSetCmdBuffer() since we share it with fileio.irx
	sceSifAddCmdHandler(SOUND_REQUEST_COMMAND, (void *) sound_request_callback, NULL );

	CpuResumeIntr(oldStat);

	// The loop
	while (1) {
		//printf("waiting for sound command semaphore\n");
		WaitSema(gSndSem);		// Get signal from callback
		//printf("got sound command semaphore\n");

		// Note that FreeSoundRequest can change in the interrupt at any time
		// Also, FirstSoundRequest is examined in the interrupt, but just to make sure we don't overflow the buffer
		//Dbg_Assert(FreeSoundRequest != FirstSoundRequest);
		while (FreeSoundRequest != FirstSoundRequest)
		{
			int ret;
			ESendSoundResult send_result;

			volatile SSoundRequest *p_request = &(SoundRequestArray[FirstSoundRequest]);
			//Kprintf("Sound: %d executing command %x with entry %x and value %x\n", FirstSoundRequest, p_request->m_function, p_request->m_entry, p_request->m_value);
			ret = execute_sound_request(p_request->m_function, p_request->m_entry, p_request->m_value,
										p_request->m_arg_0, p_request->m_arg_1, p_request->m_arg_2, &send_result);

			if ((send_result != SEND_SOUND_RESULT_FALSE) || global_sound_panic)
			{
				//Kprintf("Sending sound result from command %x with entry %x and result %x\n", p_request->m_function, p_request->m_entry, ret);

				// Send the result back
				if (last_cmd_id >= 0)	// Wait for previous send to complete (it should already be done, though)
				{
					while(sceSifDmaStat(last_cmd_id) >= 0)
						;
				}

				// Gotta copy the function and entry
				SoundResult.m_function	= p_request->m_function;
				SoundResult.m_entry 	= p_request->m_entry;
				SoundResult.m_result	= ret;
				SoundResult.m_panic		= (send_result == SEND_SOUND_RESULT_PANIC) || global_sound_panic;
				last_cmd_id = sceSifSendCmd(SOUND_RESULT_COMMAND, &SoundResult, sizeof(SoundResult), 0, 0, 0);
			}

			// Increment request index; Note that interrupt can look at this
			FirstSoundRequest = (FirstSoundRequest + 1) % NUM_SOUND_REQUESTS;
		}

		// Send the status back, if it has been updated
		if (s_send_spu_status)
		{
			int core;

			//Kprintf("Sending sound status back\n");

			for (core = 0; core < NUM_CORES; core ++)
			{
				if (last_cmd_id >= 0)	// Wait for previous send to complete
				{
					while(sceSifDmaStat(last_cmd_id) >= 0)
						;
				}

				// Gotta copy the function and entry
				SoundResult.m_function	= rSdGetSwitch;
				SoundResult.m_entry 	= SD_S_ENDX | core;
				SoundResult.m_result	= s_spu_reg_endx[core];
				SoundResult.m_panic		= FALSE;
				last_cmd_id = sceSifSendCmd(SOUND_RESULT_COMMAND, &SoundResult, sizeof(SoundResult), 0, 0, 0);
			}
			s_send_spu_status = FALSE;
		}
	}

    return 0;
}

void sound_request_callback(void *p, void *q)
{
	SSifCmdSoundRequestPacket *h = (SSifCmdSoundRequestPacket *) p;

	// Check to make sure we can add
	int nextFreeReq = (FreeSoundRequest + 1) % NUM_SOUND_REQUESTS;
	if (nextFreeReq == FirstSoundRequest)
	{
		// We can't allow a request to be ignored.  We must abort
		Kprintf("Fuck, we've overrun the sound request buffer\n");
		global_sound_panic = TRUE;
	}

#if PRINT_DROPPED_REQUESTS
	if ((++s_received_requests) != h->m_request.m_pad[0])
	{
		Kprintf("ERROR: Missed sound request.  Expected %d but only got up to %d\n", h->m_request.m_pad[0], s_received_requests);
		s_received_requests = h->m_request.m_pad[0];
	}
#endif PRINT_DROPPED_REQUESTS

	// Copy the request into the reuest queue
	SoundRequestArray[FreeSoundRequest] = h->m_request;
	FreeSoundRequest = nextFreeReq;

	//Kprintf("Sound: %d got command %x with entry %x and value %x\n", FreeSoundRequest, h->m_request.m_function, h->m_request.m_entry, h->m_request.m_value);

	// And wake up the dispatch thread
	iSignalSema(gSndSem);

	return;
}

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* DON'T ADD STUFF AFTER THIS */


================================================
FILE: Code/Gel/Music/Ngps/Pcm/pcmiop.h
================================================
#if 0
#define PRINTF(x) printf x
#else
#define PRINTF(x) 
#endif
#define ERROR(x) printf x
#define xPRINTF(x) 

#define BASE_priority  32

#define OLDLIB 0
#define TRANS_CH  0

typedef char				int8;
typedef short				int16;
typedef int					int32;

typedef unsigned char		uint8;
typedef unsigned short		uint16;
typedef unsigned int		uint32;


typedef enum
{
	SEND_SOUND_RESULT_FALSE = 0,
	SEND_SOUND_RESULT_TRUE,
	SEND_SOUND_RESULT_PANIC,
} ESendSoundResult;

enum{
	MUSIC_LOAD_STATE_IDLE = 0, // Idle state must be zero!
	MUSIC_LOAD_STATE_PRELOADING_L,
	MUSIC_LOAD_STATE_PRELOADING_R,
	MUSIC_LOAD_STATE_WAITING_FOR_REFRESH,
	MUSIC_LOAD_STATE_WAITING_FOR_LAST_LOAD,
	MUSIC_LOAD_STATE_LOADING_L,
	MUSIC_LOAD_STATE_LOADING_R,
};

enum{
	STREAM_LOAD_STATE_IDLE = 0, // Idle state must be zero!
	STREAM_LOAD_STATE_PRELOADING,
	STREAM_LOAD_STATE_WAITING_FOR_REFRESH,
	STREAM_LOAD_STATE_WAITING_FOR_LAST_LOAD,
	STREAM_LOAD_STATE_LOADING,
};

// Holds all the data necessary for an audio or music stream
struct StreamInfo{
	// State variables
	volatile uint32	loadState;
	volatile uint32	status;
	volatile uint16	m_preloadMode;

	volatile uint16	m_iopBufLoaded[2];

	// Stream parameters
	volatile uint16	volumeSet;
	volatile uint32	volume;
	volatile uint16	paused;
	volatile uint16	stop;
	int				pitch;

	// Stream size info
	volatile uint32	size;
	int 			remaining;

	// Current buffer and address info
	uint16			m_iopBufIndex;
	uint32			m_iopOffset;
	uint32			m_spuBufSide;			// buffer we are LOADING to
	uint32			m_spuTransSize;
	volatile uint16 m_spuTransDone;
	uint32			m_spuCurAddr;
	uint32			m_spuEndAddr;
};


================================================
FILE: Code/Gel/Music/Ngps/p_music.cpp
================================================
// Streaming music off the cd...



#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 

// temp for debugging ghost voice:
#include 
#include 
#include 

// temp for stream sim Rnd( ) call:
#include 

#include "p_music.h"

#define TEST_MOTHERFUCKING_CD 0			// Doubt this even works now
#define TEST_PLAY_TIMING	  0

namespace Pcm
{
	
#define MAX_VOL					( 0x3fff )
#define NORMAL_VOL				( 0x3fff >> 1 )

#define MUSIC_PRIORITY			( DEFAULT_ASYNC_PRIORITY - 20 )
#define STREAM_PRIORITY			( DEFAULT_ASYNC_PRIORITY - 10 )		// The lower the number, the higher the priority

int	gIopBuffer = 0;
int	gNonAllignedIopBuffer = 0;

int	gPcmStatus = 0;

// All the Wad file info for the music and audio streams
SWadInfo gWadInfo[ 2 ];

CFileStreamInfo gStreamList [ NUM_FILE_STREAMS ] = {
		{ FILE_STREAM_MUSIC, LOAD_STATE_DONE, PLAY_STATE_DONE, NULL, 0, 0, 0 },					// Music stream
		{ FILE_STREAM_STREAM0, LOAD_STATE_DONE, PLAY_STATE_DONE, NULL, 0, 0, 0 },				// Audio stream 0
		{ FILE_STREAM_STREAM1, LOAD_STATE_DONE, PLAY_STATE_DONE, NULL, 0, 0, 0 },   			// Audio stream 1
		{ FILE_STREAM_STREAM2, LOAD_STATE_DONE, PLAY_STATE_DONE, NULL, 0, 0, 0 }				// Audio stream 2
};

CFileStreamInfo * gpMusicInfo = &gStreamList[FILE_STREAM_MUSIC];
CFileStreamInfo * gpStreamInfo[ NUM_STREAMS ] = { &gStreamList[FILE_STREAM_STREAM0], &gStreamList[FILE_STREAM_STREAM1],
												  &gStreamList[FILE_STREAM_STREAM2] };

unsigned int gStreamVolume = 100;

// Communication to RPC -- ezpcm.irx:
// EzPcm is the function that the EE uses to call the IRX program EzPcm.irx.
// EzPcm.irx is a separate code module that runs on the IOP chip (the playstation one chip).
// The EzPcm module is build from the pcm* files in music\ngps\pcm.
// The files in the music\ngps\bgm folder were used to build a different irx that is
// no longer used for streaming music, so don't worry about the BGM files.

#define MAX_QUEUED_REQUESTS	(10)

// SifCmd packets
static SSifCmdStreamRequestPacket s_cmd_requests[MAX_QUEUED_REQUESTS] __attribute__ ((aligned(16)));
static SSifCmdStreamLoadStatusPacket s_cmd_load_status __attribute__ ((aligned(16)));

static uint32 s_cmd_request_dma_ids[MAX_QUEUED_REQUESTS] = { 0 };
static uint32 s_cmd_load_status_last_id = 0;
static int s_cmd_request_free_index = 0;

static int s_request_id = 0;
static volatile bool s_request_done;
static volatile int s_request_result;
static volatile int s_request_result_id;

// These are set by sifcmds sent directly from the IOP
volatile int sSentStatus = 0;
volatile bool sNewStatus = false;

// Timing test code
#if TEST_PLAY_TIMING
static volatile uint32 test_timing_start;
static volatile uint32 test_timing_end;
static 			int	   test_timing_request_id = -1;
#endif // TEST_PLAY_TIMING

void result_callback(void *p, void *q)
{
	SSifCmdStreamResultPacket *h = (SSifCmdStreamResultPacket *) p;

	s_request_result = h->m_return_value;
	s_request_result_id = h->m_header.opt;
	sSentStatus |= h->m_status_flags;
	sNewStatus = true;
	s_request_done = true;

	//scePrintf("*************** EzPcm() result ID %d result = %d\n", s_request_result_id, s_request_result);

#if TEST_PLAY_TIMING
	if (s_request_result_id == test_timing_request_id)
	{
		test_timing_end = Tmr::GetTimeInUSeconds();
		scePrintf("EzPcm() turnaround time Frame %d Time %d (%x - %x)\n", Tmr::GetVblanks(), (test_timing_end - test_timing_start), test_timing_end, test_timing_start);
	}
#endif // TEST_PLAY_TIMING

	ExitHandler();
}

void status_callback(void *p, void *q)
{
	SSifCmdStreamStatusPacket *h = (SSifCmdStreamStatusPacket *) p;

	sSentStatus |= h->m_status_flags;
	sNewStatus = true;

#if TEST_PLAY_TIMING
	if ((int) h->m_header.opt == test_timing_request_id)
	{
		test_timing_end = Tmr::GetTimeInUSeconds();
		scePrintf("EzPcm() final turnaround time Frame %d Time %d (%x - %x)\n", Tmr::GetVblanks(), (test_timing_end - test_timing_start), test_timing_end, test_timing_start);
	}
#endif // TEST_PLAY_TIMING

	ExitHandler();
}

bool get_status(int & status)
{
	bool new_stat;

	// Disable interrupts just in case we get a sifcmd callback
	DI();
	status = sSentStatus;
	new_stat = sNewStatus;
	sSentStatus = 0;
	sNewStatus = false;
	EI();

	return new_stat;
}

// prototype:
void EzPcm( int command, int data, int data2 = 0, int data3 = 0 );

// IOP
void EzPcm( int command, int data, int data2, int data3 )
{
	//scePrintf("Doing EzPcm request id %d with command %x\n", s_request_id, command);

	#if TEST_PLAY_TIMING
	if (command == EzADPCM_PLAYPRELOADEDMUSIC)
	{
		test_timing_start = Tmr::GetTimeInUSeconds();
		test_timing_request_id = s_request_id;
		Dbg_Message("Doing EzPcm request id %d with command %x", test_timing_request_id, command);
	}
	#endif // TEST_PLAY_TIMING

	int nextFreeIndex = (s_cmd_request_free_index + 1) % MAX_QUEUED_REQUESTS;
	SSifCmdStreamRequestPacket *p_packet = &s_cmd_requests[s_cmd_request_free_index];

	// Make sure this request packet is not still in the Sif DMA queue
	if (s_cmd_request_dma_ids[s_cmd_request_free_index])
	{
		while(sceSifDmaStat(s_cmd_request_dma_ids[s_cmd_request_free_index]) >= 0)
			;
	}

	p_packet->m_header.opt = s_request_id++;
	p_packet->m_request.m_command = command;
	p_packet->m_request.m_param[0] = data;
	p_packet->m_request.m_param[1] = data2;
	p_packet->m_request.m_param[2] = data3;

	s_request_done = false;		// Callback will change it to true
	s_cmd_request_dma_ids[s_cmd_request_free_index] = sceSifSendCmd(STREAM_REQUEST_COMMAND, p_packet, sizeof(*p_packet), 0, 0, 0);

	s_cmd_request_free_index = nextFreeIndex;

	//Dbg_MsgAssert(request_result_id == (request_id -1), ("Result id (%d) differs from request id (%d)", request_result_id, request_id - 1));
}

// Tell IOP that the buffer is done loading
void send_load_done(EFileStreamType streamType, int buffer_num, int bytes_loaded)
{
	Dbg_Assert((buffer_num >= 0) && (buffer_num <= 1));

	// Wait for last send to complete
	if (s_cmd_load_status_last_id)
	{
		while(sceSifDmaStat(s_cmd_load_status_last_id) >= 0)
			;
	}

	s_cmd_load_status.m_stream_num = streamType - 1;
	s_cmd_load_status.m_buffer_num = buffer_num;
	s_cmd_load_status.m_bytes_loaded = bytes_loaded;

	//FlushCache(0);

	s_cmd_load_status_last_id = sceSifSendCmd(STREAM_LOAD_STATUS_COMMAND, &s_cmd_load_status, sizeof(s_cmd_load_status), 0, 0, 0);
	//scePrintf("Audio stream %d buffer #%d finished loading\n", streamType, buffer_num);
}

//////////////////////////////////////////////////////////////////////////////////////////////////
#include 

void InitCDPlease( void )
{
	if (Config::CD())
	{
		// already done since we're building in CD mode...
		return;
	}
	
	#if TEST_MOTHERFUCKING_CD
	static bool initializedAndShit = false;
	if ( !initializedAndShit )
	{
		sceCdInit(SCECdINIT);
		// this may oughtta be changed to SCDCdDVD...
		// Actually, a smart thing to do would be to have this defined somewhere
		// and switched between SCECdCD or SCDCdDVD depending on the build?
		//sceCdMmode(SCECdCD);
		sceCdMmode(SCECdDVD);
		initializedAndShit = true;
	}
	#endif
}	

static bool CheckForCDErrors( void )
{
	int cdErr;
	cdErr = sceCdGetError( );
	switch ( cdErr )
	{
		case ( SCECdErNO ): // no error
			break;
		case ( SCECdErTRMOPN ):	// cover opened during playback
		case ( SCECdErREAD ):	// read error
			return ( true );
		default:
			printf( "cd err %d\n", cdErr );
			break;
	}
	return ( false );
}

///////////////////////////////////////////////////////////////////////////////////////
// CFileStreamInfo
//

// Preloads the SPU audio buffers (because this can take a few frames)
void			CFileStreamInfo::preload_spu_buffers()
{
	// Check if it is music or normal audio
	if (IsMusicStream())
	{
		// got initial music buffers loaded - start preloading SPU buffers
		EzPcm( EzADPCM_PRELOADMUSIC, m_fileSize / 2 ); // because there are two channels L and R
	}
	else
	{
		// got initial stream buffers loaded - start preloading SPU buffers
		EzPcm( EzADPCM_PRELOADSTREAM | GetAudioStreamIndex(), m_fileSize );
	}
}

// Starts playing a preloaded audio stream
void			CFileStreamInfo::start_preloaded_audio()
{
	gPcmStatus &= ~PCMSTATUS_LOAD_AUDIO( m_streamType );
	gPcmStatus |= PCMSTATUS_AUDIO_PLAYING( m_streamType );

	// Check if it is music or normal audio
	if (IsMusicStream())
	{
		// got initial music buffers loaded on the SPU - start it playing:
		EzPcm( EzADPCM_PLAYPRELOADEDMUSIC, 0 );
	}
	else
	{
		// got initial stream buffers loaded on the SPU - start it playing:
		EzPcm( EzADPCM_PLAYPRELOADEDSTREAM | GetAudioStreamIndex(), m_volume, m_pitch );
	}
}

// Starts playing the audio stream
void			CFileStreamInfo::start_audio()
{
	gPcmStatus &= ~PCMSTATUS_LOAD_AUDIO( m_streamType );
	gPcmStatus |= PCMSTATUS_AUDIO_PLAYING( m_streamType );

	// Check if it is music or normal audio
	if (IsMusicStream())
	{
		// got initial music buffers loaded - start it playing:
		EzPcm( EzADPCM_PLAYMUSIC, m_fileSize / 2 ); // because there are two channels L and R
	}
	else
	{
		// got initial stream buffers loaded - start it playing:
		EzPcm( EzADPCM_PLAYSTREAM | GetAudioStreamIndex(), m_fileSize, m_volume, m_pitch );
	}
}

// Starts the load of an audio stream chunk
void			CFileStreamInfo::load_chunk(int buffer)
{
	if ( !mp_fileHandle )
	{
		Dbg_MsgAssert( 0,( "s'it, yo... no file opened for stream index %d", GetAudioStreamIndex() ));
		return;
	}

	// Check if it is music or normal audio
	if (IsMusicStream())
	{
		void *p_iop_addr = (void *) (gIopBuffer + ( MUSIC_IOP_BUFFER_SIZE * buffer ));
		mp_fileHandle->Read( p_iop_addr, 1, MUSIC_IOP_BUFFER_SIZE );
		//scePrintf("Reading music buffer %d from disk to IOP buffer %x of size %d\n", buffer, p_iop_addr, MUSIC_IOP_BUFFER_SIZE);

		m_offset += MUSIC_IOP_BUFFER_SIZE;
	}
	else
	{
		int whichStream =  GetAudioStreamIndex();
		void *p_iop_addr = (void *) (gIopBuffer + STREAM_IOP_OFFSET( whichStream ) + ( STREAM_HALF_IOP_BUFFER_SIZE * buffer ));
		mp_fileHandle->Read( p_iop_addr, 1, STREAM_HALF_IOP_BUFFER_SIZE );

		m_offset += STREAM_HALF_IOP_BUFFER_SIZE;
	}
}

// Checks to see if the IOP module is requesting another stream chunk
ELoadState		CFileStreamInfo::check_for_load_requests()
{
	ELoadState state = LOAD_STATE_IDLE;

	if ( gPcmStatus & PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) )
	{
		load_chunk( 0 );
		state = LOAD_STATE_LOADING0;
		gPcmStatus &= ~PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType );
	}
	else if ( gPcmStatus & PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ) )
	{
		load_chunk( 1 );
		state = LOAD_STATE_LOADING1;
		gPcmStatus &= ~PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType );
	}

	return state;
}

// Checks to see if loading of stream chunk is done
// errno will be non-zero if we were busy AND there was an error
bool			CFileStreamInfo::is_chunk_load_done(int & errno)
{
	Dbg_Assert(mp_fileHandle);

	// Clear error var
	errno = 0;

	// Check for delay errors
	// Garrett: We should only be concerned if we get a request for the buffer we are currently loading.  The
	// only problem to this is the fact that the IOP doesn't check to see if a buffer is loaded at all.  We
	// must add this so we can get rid of the conservative check of making sure we don't get ANY new load requests.
//	if ( gPcmStatus & ( PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) | PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ) ) )
	int load_flag = (m_loadState == LOAD_STATE_LOADING0) ? PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) : PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType );
	if ( gPcmStatus & load_flag )
	{
		int cur_buffer_load = (int) (m_loadState - LOAD_STATE_LOADING0);
		Dbg_Message("Load State: %d", m_loadState);
		Dbg_MsgAssert( 0, ( "If repeatable, tell Garrett that audio #%d buffer #%d didn't load on time. Status: %x", m_streamType, cur_buffer_load, gPcmStatus ));
		printf( "If repeatable, tell Garrett audio #%d buffer #%d didn't load on time -- must increase IOP streaming buf size. Status %x", m_streamType, cur_buffer_load, gPcmStatus );
		errno = (IsMusicStream()) ? -1 : m_streamType;
	}

	if (!mp_fileHandle->IsBusy())
	{
		int cur_buffer_load = (int) (m_loadState - LOAD_STATE_LOADING0);

		// Tell IOP that we are done
		send_load_done(m_streamType, cur_buffer_load, IsMusicStream() ? MUSIC_IOP_BUFFER_SIZE : STREAM_HALF_IOP_BUFFER_SIZE);

		return true;
	}

	// Return that we aren't done
	return false;
}

// returns true and closes fine if we read the whole file
bool			CFileStreamInfo::file_finished()
{
	if ( m_offset < m_fileSize )
	{
		return false;
	} else {
		Dbg_Assert(mp_fileHandle);		// It shouldn't be closed yet

		File::CAsyncFileLoader::sClose( mp_fileHandle );
		mp_fileHandle = NULL;

		return true;
	}
}

// Starts an audio stream
bool			CFileStreamInfo::StartStream(File::SHedFile *pHedFile, bool play)
{
	// make sure CD isn't being used by another system:
	// Garrett: This should go away
	File::StopStreaming( );

	// Make sure these load flags aren't set
	if ( gPcmStatus & ( PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) | PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ) ) )
	{
		Dbg_Message("Load State: %d", m_loadState);
		Dbg_MsgAssert( 0, ( "Start Stream on audio #%d still has load buffer flag. Status: %x", m_streamType, gPcmStatus ));
		printf( "Start Stream on audio #%d still has load buffer flag. Status %x", m_streamType, gPcmStatus );
	}

	// Find correct wad filename
	char *p_wadName;
	unsigned int wadLsn;
	int priority;

	if (IsMusicStream())
	{
		wadLsn = gWadInfo[MUSIC_CHANNEL].m_lsn;
		p_wadName = gWadInfo[MUSIC_CHANNEL].m_fileName;
		priority = MUSIC_PRIORITY;
	}
	else
	{
		wadLsn = gWadInfo[EXTRA_CHANNEL].m_lsn;
		p_wadName = gWadInfo[EXTRA_CHANNEL].m_fileName;
		priority = STREAM_PRIORITY;
	}

	// load Audio wad:
	if ( mp_fileHandle )
	{
		Dbg_Message( "What?  Audio file opened still: %d", mp_fileHandle );
		Dbg_MsgAssert( 0,( "What?  Stream file still (or already) opened." ));
		return false;
	}

	if (Config::CD() || TEST_MOTHERFUCKING_CD)
	{
		mp_fileHandle = File::CPs2AsyncFileLoader::sRawOpen(wadLsn, false, priority);
	}
	else
	{
		if (pHedFile->HasNoWad())
		{
			char local_file[256];

			strcpy(local_file, pHedFile->p_filename);
			if (IsMusicStream())
			{
				strcat(local_file, ".ivg");
			}
			else
			{
				strcat(local_file, ".vag");
			}
			mp_fileHandle = File::CAsyncFileLoader::sOpen(&local_file[1], false, priority);
		}
		else
		{
			mp_fileHandle = File::CAsyncFileLoader::sOpen(p_wadName, false, priority);
		}
	}

	if ( !mp_fileHandle )
	{
		Dbg_MsgAssert( 0,( "Play Audio: couldn't find audio wad file %s", p_wadName ));
		printf( "play audio failed to find audio wad file %s\n", p_wadName );
		return false;
	}

	m_offset = 0;
	mp_fileHandle->SetDestination(File::MEM_IOP);

	if (pHedFile->HasNoWad())
	{
		// .ivg files don't have the VAG header
		if (!IsMusicStream()) mp_fileHandle->Seek( SIZE_OF_VAGHEADER, SEEK_SET );
	}
	else
	{
		mp_fileHandle->Seek( pHedFile->Offset, SEEK_SET );
	}

	m_fileSize = pHedFile->GetFileSize();
	m_loadState = LOAD_STATE_LOADING0;
	m_playState = (play) ? PLAY_STATE_START : PLAY_STATE_PRELOAD_EE;

	// Start loading the data, but don't play it yet
	load_chunk(0);

	// Change the state
	gPcmStatus |= PCMSTATUS_LOAD_AUDIO(m_streamType);

	return true;
}

// Plays a preloaded audio stream
bool			CFileStreamInfo::PlayStream()
{
	switch (m_playState)
	{
	case PLAY_STATE_DONE:
	case PLAY_STATE_PLAYING:
		return false;

	case PLAY_STATE_PAUSED:
		// Not implemented yet
		Dbg_MsgAssert(0, ("Playing a paused stream not implemented yet"));
		return false;

	case PLAY_STATE_PRELOAD_EE:
		Dbg_MsgAssert(0, ("Starting a preloaded stream that isn't finished preloading"));
		return false;

	case PLAY_STATE_PRELOAD_IOP:
	case PLAY_STATE_STOP:
	case PLAY_STATE_START:
		switch (m_loadState)
		{
		case LOAD_STATE_DONE:
		case LOAD_STATE_IDLE:
		case LOAD_STATE_LOADING1:			// Since it may already be loading the next section
			m_playState = PLAY_STATE_PLAYING;

			//start_audio();
			start_preloaded_audio();

			break;

		case LOAD_STATE_LOADING0:
			m_playState = PLAY_STATE_START;
			Dbg_MsgAssert(0, ("Starting a preloaded stream that isn't finished preloading. Load state: %d", m_loadState));

			break;
		}

		break;
	}

	return true;
}

// Stops an audio stream
bool			CFileStreamInfo::StopStream()
{
	int status = IsMusicStream() ? PCMAudio_GetMusicStatus( ) : PCMAudio_GetStreamStatus( GetAudioStreamIndex() );

	// Check states
	if ( status == PCM_STATUS_PLAYING )
	{
		if (IsMusicStream())
		{
			EzPcm( EzADPCM_STOPMUSIC, 0 );
		}
		else
		{
			EzPcm( EzADPCM_STOPSTREAM, GetAudioStreamIndex() );
		}
	}

	// Close async file
	if ( mp_fileHandle )
	{
		m_loadState = LOAD_STATE_DONE;
		File::CAsyncFileLoader::sClose( mp_fileHandle );
		mp_fileHandle = NULL;
	}

	// Clear flags
	PCMAudio_UpdateStatus();
	m_playState = PLAY_STATE_DONE;
	gPcmStatus &= ~( PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) |
					 PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ) |
					 PCMSTATUS_LOAD_AUDIO( m_streamType ) |
					 PCMSTATUS_AUDIO_PLAYING( m_streamType ) |
					 PCMSTATUS_AUDIO_READY(m_streamType) );

	return true;
}

// Update: executed on each stream every frame
// returns error number on error, 0 otherwise...
int				CFileStreamInfo::Update()
{
	int errno = 0;

	switch (m_loadState)
	{
	case LOAD_STATE_IDLE:
		// Make sure IOP didn't drop out (would probably be a state problem)
		//Dbg_MsgAssert(gPcmStatus & PCMSTATUS_AUDIO_PLAYING( m_streamType ), ("Generic stream %d not playing while in LOAD_STATE_IDLE", m_streamType));

		m_loadState = check_for_load_requests();

		break;

	case LOAD_STATE_LOADING0:
	case LOAD_STATE_LOADING1:
		// Make sure IOP didn't drop out (would probably be a state problem)
		//Dbg_MsgAssert(gPcmStatus & PCMSTATUS_AUDIO_PLAYING( m_streamType ), ("Generic stream %d not playing while in LOAD_STATE_LOADING", m_streamType));

		if (!is_chunk_load_done(errno))
		{
			if (errno != 0)
			{
				return errno;
			}

			break; // still busy loading
		}

		// Check if it needs starting
		if (m_playState == PLAY_STATE_START)
		{
			m_playState = PLAY_STATE_PLAYING;

			start_audio();
		}
		else if (m_playState == PLAY_STATE_PRELOAD_EE)
		{
			m_playState = PLAY_STATE_PRELOAD_IOP;

			preload_spu_buffers();
		}

		// Change state and possibly close file
		if (file_finished())
		{
			m_loadState = LOAD_STATE_DONE;
		} else {
			m_loadState = LOAD_STATE_IDLE;
		}

		break;

	case LOAD_STATE_DONE:
		// Just wait for the audio to finish playing
		if ( !(gPcmStatus & PCMSTATUS_AUDIO_PLAYING( m_streamType )) )
		{
			m_playState = PLAY_STATE_DONE;
			// Clear out any old sent load request flags
			gPcmStatus &= ~( PCMSTATUS_NEED_AUDIO_BUFFER_0( m_streamType ) | PCMSTATUS_NEED_AUDIO_BUFFER_1( m_streamType ) );
		}

		break;
	}

	return errno;
}

#define DEBUG_LOAD_FLAGS 0

#if DEBUG_LOAD_FLAGS
int old_load_state[NUM_FILE_STREAMS];
int old_play_state[NUM_FILE_STREAMS];
#endif

// Combine the IOP sent status into gPcmStatus
void PCMAudio_UpdateStatus()
{
	int new_status;

	if (get_status(new_status))
	{
		for ( int i = 0; i < NUM_FILE_STREAMS; i++ )
		{
			gPcmStatus &= ~( PCMSTATUS_AUDIO_PLAYING( i ) | PCMSTATUS_AUDIO_READY( i ) );
		}
		gPcmStatus |= new_status;
	}
}


// returns error number on error, 0 otherwise...
int PCMAudio_Update( void )
{
	int i;

#if DEBUG_LOAD_FLAGS
	for (int i = 0; i < NUM_FILE_STREAMS; i++)
	{
		old_load_state[i] = gStreamList[ i ].m_loadState;
		old_play_state[i] = gStreamList[ i ].m_playState;
	}
#endif

	// Clear the flags
	PCMAudio_UpdateStatus();

	static bool print_panic = true;		// Using this now until we can clear the PANIC flag on the IOP
	if ((gPcmStatus & PCMSTATUS_PANIC) && print_panic)
	{
		print_panic = false;
		scePrintf("ezpcm.irx module set PANIC flag: Look at its last error message.\n");
		//Dbg_MsgAssert(0, ("ezpcm.irx module set PANIC flag: Look at its last error message."));
	}

	// Make sure there aren't any CD errors
	if (0 && (Config::CD() || TEST_MOTHERFUCKING_CD) && CheckForCDErrors( ))
	{
		return ( -1 );
	}

	// Go through each stream and update
	for (i = 0; i < NUM_FILE_STREAMS; i++)
	{
		CFileStreamInfo *pInfo = &gStreamList[ i ];

		int ret_val = pInfo->Update();

		if (ret_val != 0)
		{
			return ret_val;
		}
	}

#if 0
		// If file loads go out of sync over a busy network, keep it from crashing:
		if ( gMusicInfo.mp_fileHandle && !( gPcmStatus & PCMSTATUS_MUSIC_PLAYING ) )
		{
			File::CAsyncFileLoader::sClose( gMusicInfo.mp_fileHandle );
			gMusicInfo.mp_fileHandle = NULL;
			Dbg_Message( "Symptom of a busy network: Closing music file." );
		}
		for ( i = 0; i < NUM_STREAMS; i++ )
		{
			if ( gStreamInfo[ i ].mp_fileHandle && !( gPcmStatus & (PCMSTATUS_STREAM_PLAYING( i ) | PCMSTATUS_LOAD_STREAM( i ))) )
			{
				File::CAsyncFileLoader::sClose( gStreamInfo[i].mp_fileHandle );
				gStreamInfo[ i ].mp_fileHandle = NULL;
				Dbg_Message( "Symptom of a busy network: Closing stream file." );
			}
		}
#endif

#if 0
	Dbg_MsgAssert(!(gPcmStatus & ( PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 |
						PCMSTATUS_NEED_STREAM0_BUFFER_0 | PCMSTATUS_NEED_STREAM0_BUFFER_1 |
						PCMSTATUS_NEED_STREAM1_BUFFER_0 | PCMSTATUS_NEED_STREAM1_BUFFER_1 |
						PCMSTATUS_NEED_STREAM2_BUFFER_0 | PCMSTATUS_NEED_STREAM2_BUFFER_1 )), ("Skipped a buffer load: status %x", gPcmStatus));
#else
	if (gPcmStatus & ( PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 |
						PCMSTATUS_NEED_STREAM0_BUFFER_0 | PCMSTATUS_NEED_STREAM0_BUFFER_1 |
						PCMSTATUS_NEED_STREAM1_BUFFER_0 | PCMSTATUS_NEED_STREAM1_BUFFER_1 |
						PCMSTATUS_NEED_STREAM2_BUFFER_0 | PCMSTATUS_NEED_STREAM2_BUFFER_1 ))
	{
		Dbg_Message("************Skipped a buffer load: status %x", gPcmStatus);
#if DEBUG_LOAD_FLAGS
		for (int i = 0; i < NUM_FILE_STREAMS; i++)
		{
			Dbg_Message("Audio Stream %d: load state = %d", i, gStreamList[ i ].m_loadState);
			Dbg_Message("Audio Stream %d: play state = %d", i, gStreamList[ i ].m_playState);
			Dbg_Message("Audio Stream %d: old load state = %d", i, old_load_state[i]);
			Dbg_Message("Audio Stream %d: old play state = %d", i, old_play_state[i]);
		}
#endif
	}
#endif

	return ( 0 );
}

/* ------------
   Initialize rpc  -- Just call once at the very beginning!
   ------------ */
void PCMAudio_Init( void )
{
	Dbg_MsgAssert(NUM_FILE_STREAMS == (NUM_STREAMS + 1),
				  ("Number of file slots allocated for audio streams (%d) different than number of IOP audio streams (%d)", NUM_FILE_STREAMS - 1, NUM_STREAMS));

	// Check to make sure stream array is filled
	for (int i = 0; i < NUM_STREAMS; i++)
	{
		Dbg_Assert(gpStreamInfo[i]);
	}

	DI();

	printf( "Setting up Sif Cmd with EzPCM Streaming IRX...\n" );
	// No longer need to call sceSifSetCmdBuffer() since we share it with async filesys
//	static sceSifCmdData cmdbuffer[NUM_COMMAND_HANDLERS];
//	sceSifSetCmdBuffer( &cmdbuffer[0], NUM_COMMAND_HANDLERS);

	sceSifAddCmdHandler(STREAM_RESULT_COMMAND, result_callback, NULL );
	sceSifAddCmdHandler(STREAM_STATUS_COMMAND, status_callback, NULL );

	EI();

	// if CD system hasn't been initialized,
	// but you want to test music from CD,
	// this will do it...
	InitCDPlease( );


	Dbg_MsgAssert( !gIopBuffer,( "What the fuck - buffer already exists?" ));

	FlushCache( 0 );
	
	PCMAudio_GetIopMemory( );
	
	EzPcm( EzADPCM_INIT, gIopBuffer );
	//EzPcm( EzADPCM_SETSTREAMGLOBVOL, gStreamVolume );
} // end of PCMAudio_Init( )

int PCMAudio_GetIopMemory( void )
{
	
	
	if ( !gNonAllignedIopBuffer )
	{
		// streaming buffers:
		gNonAllignedIopBuffer = ( int )sceSifAllocIopHeap( TOTAL_IOP_BUFFER_SIZE_NEEDED + ALLIGN_REQUIREMENT );
		gIopBuffer = gNonAllignedIopBuffer + ALLIGN_REQUIREMENT - ( gNonAllignedIopBuffer & ( ALLIGN_REQUIREMENT - 1 ) );
	
		if ( !gNonAllignedIopBuffer )
		{
			Dbg_MsgAssert( 0,( "Failed to allocate IOP memory - %d k\n", TOTAL_IOP_BUFFER_SIZE_NEEDED / 1024 ));
			return 0;
		}
	}
	return ( gIopBuffer );
}
void PCMAudio_SetStreamGlobalVolume( unsigned int volume )
{
	gStreamVolume = volume;
	//if ( gPcmStatus ) // make sure we've initialized PCM stuff:
	//{
	//	EzPcm( EzADPCM_SETSTREAMGLOBVOL, gStreamVolume );
	//}
}

bool PCMAudio_SetMusicVolume( float volume )
{
	int vol = ( int )PERCENT( volume, MAX_VOL );
	if ( vol > MAX_VOL )
		vol = MAX_VOL;

	EzPcm( EzADPCM_SETMUSICVOL, ( vol << 16 ) | vol );
	return true;
}// end of PCMAudio_SetMusicVolume( )


/*int PCMAudio_SetStreamVolumeAndPitch( float volumeL, float volumeR, float fPitch, int whichStream )
{
	Dbg_Message( "pitch %d", pitch );
   	return ( EzPcm( EzADPCM_SETSTREAMVOLANDPITCH | whichStream, ( ( volL << 16 ) | volR ), pitch ) );
}// end of PCMAudio_SetVolumeAndPitch( )
*/

// Should check before calling this to make sure the sound is playing and has the uniqueID of the sound you are
// trying to adjust:
bool PCMAudio_SetStreamVolume( float volumeL, float volumeR, int whichStream )
{
	int volL = ( int )PERCENT( volumeL, gStreamVolume );
	volL = ( int )PERCENT( volL, NORMAL_VOL );
	if ( volL > MAX_VOL )
		volL = MAX_VOL;
	else if ( volL < -MAX_VOL )
		volL = -MAX_VOL;
	int volR;
	if ( volumeR == volumeL )
	{
		volR = volL;
	}
	else
	{
		volR = ( int )PERCENT( volumeR, gStreamVolume );
		volR = ( int )PERCENT( volR, NORMAL_VOL );
		if ( volR > MAX_VOL )
			volR = MAX_VOL;
		else if ( volR < -MAX_VOL )
			volR = -MAX_VOL;
	}

	// for phasing effects (of sounds behind you), set the volume
	// to 0x7fff
	if ( volL < 0 )
	{
		volL = 0x7fff + volL;
	}
	if ( volR < 0 )		// Just in case we start phase shifting the right side instead
	{
		volR = 0x7fff + volR;
	}

	gpStreamInfo[ whichStream ]->m_volume = ( ( (volL & 0x7FFF) << 16 ) | (volR & 0x7FFF) );

	if (gpStreamInfo[ whichStream ]->m_playState == PLAY_STATE_PLAYING)
	{
		EzPcm( EzADPCM_SETSTREAMVOL | whichStream, gpStreamInfo[ whichStream ]->m_volume);
	}

	return true;
}// end of PCMAudio_SetVolume( )

// Currently, this only works when the stream isn't playing
bool PCMAudio_SetStreamPitch( float fPitch, int whichStream )
{
	if ( fPitch > 100.0f )
	{
		fPitch = 100.0f;
	}
	int pitch;
	pitch = ( int ) PERCENT( fPitch, ( float )DEFAULT_PITCH ); 
	if ( pitch == 0 )
	{
		Dbg_Message( "Trying to set stream to zero pitch." );
		return ( false );
	}

	if (gpStreamInfo[ whichStream ]->m_playState == PLAY_STATE_PLAYING)
	{
		Dbg_MsgAssert(0, ("Can't change pitch of stream %d while it is playing", whichStream));
		return false;
	}
	else
	{
		gpStreamInfo[ whichStream ]->m_pitch = pitch;
		return true;
	}
}

bool PCMAudio_PlayMusicTrack( const char *filename )
{
	if ( PCMAudio_GetMusicStatus( ) != PCM_STATUS_FREE )
	{
		Dbg_MsgAssert( 0 , ( "Playing new track without stopping the first track." ) );
		return ( false );
	}

	if (!(Config::CD() || TEST_MOTHERFUCKING_CD))
	{
		int testMusic = 0;
		testMusic = Script::GetInteger( 0x47ac7ba5 ); // checksum 'testMusicFromHost'
		if ( !testMusic )
		{
			return ( false );
		}
	}

	// make sure CD is not in use by another system:
	File::StopStreaming( );

	if ( !gWadInfo[MUSIC_CHANNEL].mp_hed )
	{
		Dbg_Message( "Music header not loaded, can't play track %s", filename );
		return ( false );
	}

	File::SHedFile *pHed = FindFileInHed( filename, gWadInfo[MUSIC_CHANNEL].mp_hed );
	if ( !pHed )
	{
		Dbg_Message( "Track %d not found in music header.", filename );
		return ( false );
	}

	// Start the actual stream
	return gpMusicInfo->StartStream(pHed, true);
} // end of PCMAudio_PlayMusicTrack( )


bool PCMAudio_PlayMusicStream( uint32 checksum )
{
	if ( PCMAudio_GetMusicStatus( ) != PCM_STATUS_FREE )
	{
		Dbg_MsgAssert( 0 , ( "Playing music stream without stopping the last music track." ) );
		return ( false );
	}

	if (!(Config::CD() || TEST_MOTHERFUCKING_CD))
	{
		int testMusic = 0;
		testMusic = Script::GetInteger( 0x47ac7ba5 ); // checksum 'testMusicFromHost'
		if ( !testMusic )
		{
			return ( false );
		}
	}

	// make sure CD is not in use by another system:
	File::StopStreaming( );

	if ( !gWadInfo[MUSIC_CHANNEL].mp_hed )
	{
		Dbg_Message( "Music header not loaded, can't play music stream %s", Script::FindChecksumName(checksum) );
		return ( false );
	}

	File::SHedFile *pHed = FindFileInHedUsingChecksum( checksum, gWadInfo[MUSIC_CHANNEL].mp_hed );
	if ( !pHed )
	{
		Dbg_Message( "Music Stream %s not found in music header.", Script::FindChecksumName(checksum) );
		return ( false );
	}

	// Start the actual stream
	return gpMusicInfo->StartStream(pHed, true);
}

bool PCMAudio_PlayStream( uint32 checksum, int whichStream, float volumeL, float volumeR, float fPitch )
{
	Dbg_MsgAssert( whichStream < NUM_STREAMS, ( "Stream is out of range" ) );

	if ( PCMAudio_GetStreamStatus( whichStream ) != PCM_STATUS_FREE )
	{
		Dbg_Message("Stream %d not free: status is %x", whichStream, PCMAudio_GetStreamStatus( whichStream ));
		return ( false );
	}

	if (!(Config::CD() || TEST_MOTHERFUCKING_CD))
	{
		int testStreams = 0;
		testStreams = Script::GetInteger( 0x62df9442 ); // checksum 'testStreamsFromHost'
		if ( !testStreams )
		{
			return ( false );
		}
	}	
	
	//Dbg_Message("Playing stream %x on %d", checksum, whichStream);

	// load from vag wad:
	if ( !gWadInfo[EXTRA_CHANNEL].mp_hed )
	{
		//Dbg_Message( "Stream header not loaded, can't play track %s", filename );
		return ( false );
	}
	File::SHedFile *pHed = FindFileInHedUsingChecksum( checksum, gWadInfo[EXTRA_CHANNEL].mp_hed );
	if ( !pHed )
	{
		Dbg_Message( "Stream %s not found in stream header.", Script::FindChecksumName(checksum) );
		return ( false );
	}

	// Set pitch and volume
	PCMAudio_SetStreamPitch(fPitch, whichStream);
	PCMAudio_SetStreamVolume(volumeL, volumeR, whichStream);

	// Start the actual stream
	return gpStreamInfo[ whichStream ]->StartStream(pHed, true);
} // end of PCMAudio_PlayStream( )

void PCMAudio_StopMusic( bool waitPlease )
{
	gpMusicInfo->StopStream();
}

void PCMAudio_StopStream( int whichStream, bool waitPlease )
{
	gpStreamInfo[ whichStream ]->StopStream();
}

void PCMAudio_StopStreams( void )
{
	int i;
	for ( i = 0; i < NUM_STREAMS; i++ )
	{
		PCMAudio_StopStream( i, true );
	}
}

// Get a stream loaded into a buffer, but don't play yet
bool PCMAudio_PreLoadStream( uint32 checksum, int whichStream )
{
	Dbg_MsgAssert( whichStream < NUM_STREAMS, ( "Stream is out of range" ) );

	if ( PCMAudio_GetStreamStatus( whichStream ) != PCM_STATUS_FREE )
	{
		return ( false );
	}

	if (!(Config::CD() || TEST_MOTHERFUCKING_CD))
	{
		int testStreams = 0;
		testStreams = Script::GetInteger( 0x62df9442 ); // checksum 'testStreamsFromHost'
		if ( !testStreams )
		{
			return ( false );
		}
	}

	// load from vag wad:
	if ( !gWadInfo[EXTRA_CHANNEL].mp_hed )
	{
		//Dbg_Message( "Stream header not loaded, can't play track %s", filename );
		return ( false );
	}
	File::SHedFile *pHed = FindFileInHedUsingChecksum( checksum, gWadInfo[EXTRA_CHANNEL].mp_hed );
	if ( !pHed )
	{
		//Dbg_Message( "Track %d not found in stream header.", filename );
		return ( false );
	}

	// Start the actual stream
	return gpStreamInfo[ whichStream ]->StartStream(pHed, false);
}

// Returns true if preload done.  Assumes that caller is calling this on a preloaded, but not yet played, stream.
// The results are meaningless otherwise.
bool PCMAudio_PreLoadStreamDone( int whichStream )
{
	Dbg_MsgAssert( whichStream < NUM_STREAMS, ( "Stream is out of range" ) );
	Dbg_MsgAssert( (gpStreamInfo[whichStream]->m_playState == PLAY_STATE_PRELOAD_EE) ||
				   (gpStreamInfo[whichStream]->m_playState == PLAY_STATE_PRELOAD_IOP), ( "PreLoadStreamDone(): This stream on channel %d is either playing or wasn't preloaded.  Load state %d.  Play State %d. IOP flags %x",
																						 whichStream, gpStreamInfo[whichStream]->m_loadState, gpStreamInfo[whichStream]->m_playState, gPcmStatus ) );

	//return gpStreamInfo[whichStream]->m_loadState != LOAD_STATE_LOADING0;
	if (gPcmStatus & PCMSTATUS_STREAM_READY(whichStream))
	{
		Dbg_Message("PCMAudio_PreLoadStreamDone true for stream channel %d.  Load state %d.  Play State %d. IOP flags %x", whichStream,
					gpStreamInfo[whichStream]->m_loadState, gpStreamInfo[whichStream]->m_playState, gPcmStatus);
	}
	return (gPcmStatus & PCMSTATUS_STREAM_READY(whichStream)) && (gpStreamInfo[whichStream]->m_playState == PLAY_STATE_PRELOAD_IOP);
}

// Tells a preloaded stream to start playing.  Must call PCMAudio_PreLoadStreamDone() first to guarantee that
// it starts immediately.
bool PCMAudio_StartPreLoadedStream( int whichStream, float volumeL, float volumeR, float pitch )
{
	Dbg_MsgAssert( whichStream < NUM_STREAMS, ( "Stream is out of range" ) );

	// Set pitch and volume
	PCMAudio_SetStreamPitch(pitch, whichStream);
	PCMAudio_SetStreamVolume(volumeL, volumeR, whichStream);

	// Start playing the actual stream
	return gpStreamInfo[ whichStream ]->PlayStream();
}

bool PCMAudio_PreLoadMusicStream( uint32 checksum )
{
	if ( PCMAudio_GetMusicStatus( ) != PCM_STATUS_FREE )
	{
		Dbg_MsgAssert( 0 , ( "Preloading music stream without stopping the last music track." ) );
		return ( false );
	}

	if (!(Config::CD() || TEST_MOTHERFUCKING_CD))
	{
		int testMusic = 0;
		testMusic = Script::GetInteger( 0x47ac7ba5 ); // checksum 'testMusicFromHost'
		if ( !testMusic )
		{
			return ( false );
		}
	}

	// load from vag wad:
	if ( !gWadInfo[MUSIC_CHANNEL].mp_hed )
	{
		//Dbg_Message( "Stream header not loaded, can't play track %s", filename );
		return ( false );
	}
	File::SHedFile *pHed = FindFileInHedUsingChecksum( checksum, gWadInfo[MUSIC_CHANNEL].mp_hed );
	if ( !pHed )
	{
		Dbg_Message( "Music Stream %s not found in music header.", Script::FindChecksumName(checksum) );
		return ( false );
	}

	// Start the actual stream
	return gpMusicInfo->StartStream(pHed, false);
}

bool PCMAudio_PreLoadMusicStreamDone( void )
{
	Dbg_MsgAssert( (gpMusicInfo->m_playState == PLAY_STATE_PRELOAD_EE) ||
				   (gpMusicInfo->m_playState == PLAY_STATE_PRELOAD_IOP), ( "PreLoadMusicStreamDone(): This stream is either playing or wasn't preloaded." ) );

	//return gpMusicInfo->m_loadState != LOAD_STATE_LOADING0;
	return (gPcmStatus & PCMSTATUS_MUSIC_READY) && (gpMusicInfo->m_playState == PLAY_STATE_PRELOAD_IOP);
}

bool PCMAudio_StartPreLoadedMusicStream( void )
{
	// Start playing the actual stream
	return gpMusicInfo->PlayStream();
}

int PCMAudio_GetMusicStatus( void )
{
	

	if ( gPcmStatus & PCMSTATUS_LOAD_MUSIC )
	{
		return ( PCM_STATUS_LOADING );
	}
	else if ( gPcmStatus & PCMSTATUS_MUSIC_PLAYING )
	{
		return ( PCM_STATUS_PLAYING );
	}
	else if ( gpMusicInfo->m_playState == PLAY_STATE_STOP)
	{
		return ( PCM_STATUS_STOPPED );
	}
	else if ( gpMusicInfo->m_playState == PLAY_STATE_PLAYING)		// EE thinks it is playing, but not IOP
	{
		return ( PCM_STATUS_END );
	}
	else
	{
		//Dbg_Message("Music status is free %x", gPcmStatus);
		return ( PCM_STATUS_FREE );
	}
	Dbg_MsgAssert( 0,( "Sell your stock in GCC." ));
	return ( 0 );
}

int PCMAudio_GetStreamStatus( int whichStream )
{
	
	if ( (whichStream < 0) || (whichStream >= NUM_STREAMS) )
	{
		Dbg_MsgAssert( 0, ( "Checking stream status on stream %d, past valid range ( 0 to %d ).", whichStream, NUM_STREAMS - 1 ) );
		return ( false );
	}
	if ( gPcmStatus & PCMSTATUS_LOAD_STREAM( whichStream ) )
	{
		return ( PCM_STATUS_LOADING );
	}
	else if ( gPcmStatus & PCMSTATUS_STREAM_PLAYING( whichStream ) )
	{
		return ( PCM_STATUS_PLAYING );
	}
	else if ( gpStreamInfo[whichStream]->m_playState == PLAY_STATE_STOP)
	{
		return ( PCM_STATUS_STOPPED );
	}
	else if ( gpStreamInfo[whichStream]->m_playState == PLAY_STATE_PLAYING)		// EE thinks it is playing, but not IOP
	{
		return ( PCM_STATUS_END );
	}
	else
	{
		return ( PCM_STATUS_FREE );
	}
	Dbg_MsgAssert( 0,( "Sell your stock in GCC." ));
	return ( 0 );
}

void PCMAudio_Pause( bool pause, int ch )
{
	if ( ch == MUSIC_CHANNEL )
	{
		EzPcm( EzADPCM_PAUSEMUSIC, pause );
	}
	else
	{
		EzPcm( EzADPCM_PAUSESTREAMS, pause );
	}
} // end of PCMAudio_Pause( )

void PCMAudio_PauseStream( bool pause, int whichStream )
{
	EzPcm( EzADPCM_PAUSESTREAM | whichStream, pause );
}

unsigned int GetCDLocation( const char *pWadName )
{
	

	InitCDPlease( );

	File::StopStreaming( );
	sceCdSync( 0 );

	char tempFilename[ 255 ];
	sprintf( tempFilename, "\\%s%s.WAD;1", Config::GetDirectory(), pWadName );
	int i;
	for ( i = strlen( tempFilename ) - 1; i > 0; i-- )
	{
		if ( tempFilename[ i ] >= 'a' && tempFilename[ i ] <= 'z' )
		{
			tempFilename[ i ] += 'A' - 'a';
		}
	}
	sceCdlFILE fileInfo;
	int retVal = 0;
	if ( !( retVal = sceCdSearchFile( &fileInfo, tempFilename ) ) )
	{
		printf( "Wad %s not found -- Err %d\n", tempFilename, retVal );
		return ( 0 );
	}
	return ( fileInfo.lsn );
}

bool PCMAudio_TrackExists( const char *pTrackName, int ch )
{
	if ( !gWadInfo[ch].mp_hed )
	{
		return ( false );
	}
	if ( !FindFileInHed( pTrackName, gWadInfo[ch].mp_hed ) )
	{
		Dbg_Message( "Audio file %s not found in header file.", pTrackName );
		return ( false );
	}
	return ( true );
	
}

bool PCMAudio_LoadMusicHeader( const char *nameOfFile )
{
	bool no_wad = false;

	if (!(Config::CD() || TEST_MOTHERFUCKING_CD))
	{
		no_wad = true;
	}	

	return gWadInfo[MUSIC_CHANNEL].LoadHeader(nameOfFile, no_wad);
}

bool PCMAudio_LoadStreamHeader( const char *nameOfFile )
{
	bool no_wad = false;

	if (!(Config::CD() || TEST_MOTHERFUCKING_CD))
	{
		int testStreams = 0;
		testStreams = Script::GetInteger( 0x62df9442 ); // checksum 'testStreamsFromHost'
		if ( !testStreams )
		{
			return ( false );
		}

		no_wad = true;
	}	
	
	return gWadInfo[EXTRA_CHANNEL].LoadHeader(nameOfFile, no_wad);
}

uint32 PCMAudio_FindNameFromChecksum( uint32 checksum, int ch )
{
	
	File::SHed *pHed;

	pHed = gWadInfo[ch].mp_hed;

	if ( !pHed )
	{
		return ( NULL );
	}

	File::SHedFile* pHedFile = File::FindFileInHedUsingChecksum( checksum, pHed );
	if( pHedFile )
	{
		return pHedFile->Checksum;
	}
	return NULL;
}

///////////////////////////////////////////////////////////////////////////////////////
// SWadInfo
//
bool			SWadInfo::LoadHeader(const char *nameOfFile, bool no_wad, bool assertOnError)
{
	if ( mp_hed )
	{
		Mem::Free( mp_hed );
		mp_hed = NULL;
	}

	mp_hed = File::LoadHed( nameOfFile, TEST_MOTHERFUCKING_CD, no_wad );
	if ( !mp_hed )
	{
		Dbg_Message( "Couldn't find audio header %s", nameOfFile );
		Dbg_MsgAssert( !assertOnError, ( "Couldn't find audio header %s", nameOfFile ));
		return false;
	}

	if (Config::CD() || TEST_MOTHERFUCKING_CD)
	{
		Dbg_MsgAssert(!no_wad, ("Can't use a no-wad version of a hed file on the CD"));

		m_lsn = GetCDLocation( nameOfFile );
		if ( !m_lsn )
		{
			Mem::Free( mp_hed );
			mp_hed = NULL;
			Dbg_Message( "Couldn't find audio wad %s", nameOfFile );
			Dbg_MsgAssert( !assertOnError,( "Couldn't find audio wad %s", nameOfFile ));
			return false;
		}
	}
	else
	{
		 sprintf( m_fileName, "host:%s.WAD", nameOfFile );
	}	 

	return (bool) mp_hed;
}

} // namespace PCM


================================================
FILE: Code/Gel/Music/Ngps/p_music.h
================================================
// Audio streaming function prototypes:
// mjd jan 2001

#ifndef __P_MUSIC_H__
#define __P_MUSIC_H__

// Forward declarations
namespace File
{
	class CAsyncFileHandle;
	struct SHed;
	struct SHedFile;
}

namespace Pcm
{

// Stream types
enum EFileStreamType 
{
	FILE_STREAM_MUSIC = 0,
	FILE_STREAM_STREAM0,
	FILE_STREAM_STREAM1,
	FILE_STREAM_STREAM2,
	NUM_FILE_STREAMS
};

// The load state of a stream
enum ELoadState
{
	LOAD_STATE_IDLE = 0,
	LOAD_STATE_LOADING0,
	LOAD_STATE_LOADING1,
	LOAD_STATE_DONE,
};

// The play state of a stream
enum EPlayState
{
	PLAY_STATE_STOP = 0,
	PLAY_STATE_PRELOAD_EE,
	PLAY_STATE_PRELOAD_IOP,
	PLAY_STATE_START,
	PLAY_STATE_PLAYING,
	PLAY_STATE_PAUSED,
	PLAY_STATE_DONE,
};

// allows one channel for music, another for audio:
enum{
	EXTRA_CHANNEL,
	MUSIC_CHANNEL,
};


// Holds the info for each stream
class CFileStreamInfo
{
public:

	bool					IsMusicStream() const;
	int						GetAudioStreamIndex() const;		// returns a negative number if it isn't a audio stream

	bool					StartStream(File::SHedFile *pHedFile, bool play);
	bool					PlayStream();
	bool					StopStream();
	int						Update();

protected:
	void					start_audio();
	void					load_chunk(int buffer);

	// For preloading
	void					preload_spu_buffers();
	void					start_preloaded_audio();

	ELoadState				check_for_load_requests();
	bool					is_chunk_load_done(int & errno);	// errno will be non-zero if we were busy AND there was an error
	bool					file_finished();					// returns true and closes fine if we read the whole file

// This section should eventually become "protected"
public:
	EFileStreamType			m_streamType;			// Either music or audio stream
	ELoadState				m_loadState;
	EPlayState				m_playState;
	File::CAsyncFileHandle *mp_fileHandle;
	int						m_fileSize;
	int						m_offset;
	int						m_offsetInWad;
	int						m_pitch;
	uint32					m_volume;
};

// All the Wad information for music and audio
struct SWadInfo
{
	bool					LoadHeader(const char *nameOfFile, bool no_wad = false, bool assertOnError = false);

	char  					m_fileName[ 256 ];
	int						m_lsn;
	File::SHed *			mp_hed;
};



// Onetime call once upon loading the game...
void PCMAudio_Init( void );

// Combine the IOP sent status into gPcmStatus
void PCMAudio_UpdateStatus();

// Call every frame to make sure music and stream buffers are loaded and current status is checked each frame...
// Returns true if there is a CD error, false otherwise...
int PCMAudio_Update( void );

// Load a track and start it playing...
// You wanna loop a track?  Wait until this track is done, and PLAY IT AGAIN SAM!
bool PCMAudio_PlayMusicTrack( const char *filename );
bool PCMAudio_PlayMusicStream( uint32 checksum );
bool PCMAudio_PlayStream( uint32 checksum, int whichStream, float volumeL, float volumeR, float pitch );

// keep song loaded, stop playing it (or continue playing paused song)
void PCMAudio_Pause( bool pause = true, int ch = MUSIC_CHANNEL );

void PCMAudio_PauseStream( bool pause, int whichStream );

void PCMAudio_StopMusic( bool waitPlease );
void PCMAudio_StopStream( int whichStream, bool waitPlease = true );
void PCMAudio_StopStreams( void );

// Preload streams.  By preloading, you can guarantee they will start at a certain time.
bool PCMAudio_PreLoadStream( uint32 checksum, int whichStream );
bool PCMAudio_PreLoadStreamDone( int whichStream );
bool PCMAudio_StartPreLoadedStream( int whichStream, float volumeL, float volumeR, float pitch );

// Preload Music Streams.  By preloading, you can guarantee they will start at a certain time.
bool PCMAudio_PreLoadMusicStream( uint32 checksum );
bool PCMAudio_PreLoadMusicStreamDone( void );
bool PCMAudio_StartPreLoadedMusicStream( void );

// set the music volume ( 0 to 100 )
bool	PCMAudio_SetMusicVolume( float volume );
bool	PCMAudio_SetStreamVolume( float volumeL, float volumeR, int whichStream );
bool	PCMAudio_SetStreamPitch( float pitch, int whichStream );
//int PCMAudio_SetStreamVolumeAndPitch( float volumeL, float volumeR, float pitch, int whichStream );

void PCMAudio_SetStreamGlobalVolume( unsigned int volume );

enum{
	PCM_STATUS_FREE 			= ( 1 << 0 ),
	PCM_STATUS_PLAYING			= ( 1 << 1 ),
	PCM_STATUS_LOADING 			= ( 1 << 2 ),
	PCM_STATUS_END 				= ( 1 << 3 ),		// Done playing on IOP, but not completely cleaned up on the IOP
	PCM_STATUS_STOPPED 			= ( 1 << 4 ),
};

// Return one of the PCM_STATUS values from above...
int PCMAudio_GetMusicStatus( );
int PCMAudio_GetStreamStatus( int whichStream );

// preload any CD location info (to avoid a hitch in framerate on PS2):
unsigned int PCMAudio_GetCDLocation( const char *pTrackName );

bool PCMAudio_TrackExists( const char *pTrackName, int ch );
bool PCMAudio_LoadMusicHeader( const char *nameOfFile );
bool PCMAudio_LoadStreamHeader( const char *nameOfFile );

uint32 PCMAudio_FindNameFromChecksum( uint32 checksum, int ch );

// borrow this memory for the movies and shit...
int PCMAudio_GetIopMemory( void );
		 
//////////////////////////////////////////////////////////////////////////////
// Inlines
//

inline bool		CFileStreamInfo::IsMusicStream() const
{
	return m_streamType == FILE_STREAM_MUSIC;
}

inline int		CFileStreamInfo::GetAudioStreamIndex() const
{
	return (int) (m_streamType - FILE_STREAM_STREAM0);
}

} // namespace PCM

#endif // __P_MUSIC_H__


================================================
FILE: Code/Gel/Music/Xbox/p_adpcmfilestream.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:																	**
**																			**
**	File name:		p_adpcmfilestream.cpp									**
**																			**
**	Created:		01/27/03	-	dc										**
**																			**
**	Description:	Xbox specific .pcm streaming code						**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include "p_music.h"
#include "p_adpcmfilestream.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

namespace Pcm
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// Define the maximum amount of packets we will ever submit to the ADPCM renderer
#define ADPCMSTRM_PACKET_COUNT					8

// Define the ADPCM renderer packet size. See the comment block above for an explanation.
// This value is hard-coded assuming an ADPCM frame of 36 samples and 16 bit stereo (128 frames per packet)
#define ADPCMSTRM_16BIT_MONO_PACKET_BYTES		( 1 * 2 * 36 * 128 ) 
#define ADPCMSTRM_16BIT_STEREO_PACKET_BYTES		( 2 * 2 * 36 * 128 ) 

// Read size is 16k (most efficient size for DVD reads).
#define BYTES_PER_READ				16384

	
/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

//-----------------------------------------------------------------------------
// Name: CADPCMFileStream()
// Desc: Object constructor.
//-----------------------------------------------------------------------------
CADPCMFileStream::CADPCMFileStream( bool use_3d )
{
    m_pSourceFilter		= NULL;
    m_pRenderFilter		= NULL;
    m_pvSourceBuffer	= NULL;
    m_pFileBuffer		= NULL;
	m_hFile				= INVALID_HANDLE_VALUE;
	m_hThread			= NULL;
	m_bUse3D			= use_3d;
	m_bOkayToPlay		= true;

    for( DWORD i = 0; i < ADPCMSTRM_PACKET_COUNT; i++ )
	{
        m_adwPacketStatus[i] = XMEDIAPACKET_STATUS_SUCCESS;
	}

    m_dwStartingDataOffset	= 0;
	m_Completed				= false;
	m_Paused				= false;

	// Grab a pointer to the next overlapped structure.
	m_pOverlapped			= PCMAudio_GetNextOverlapped();
}



//-----------------------------------------------------------------------------
// Name: ~CADPCMFileStream()
// Desc: Object destructor.
//-----------------------------------------------------------------------------
CADPCMFileStream::~CADPCMFileStream()
{
	if( m_hThread )
	{
		CloseHandle( m_hThread );
	}
	
	// If the file i/o is still active, we need to remove the event and close the file.
	if(( m_hFile != INVALID_HANDLE_VALUE ) && !m_bUseWAD )
	{
		CloseHandle( m_hFile );
	}

    if( m_pSourceFilter )
	{
		m_pSourceFilter->Release();
	}

    if( m_pRenderFilter )
	{
		m_pRenderFilter->Release();
	}

	if( m_pvSourceBuffer )
	{
		delete[] m_pvSourceBuffer;
	}
}



//-----------------------------------------------------------------------------
// Name: AsyncRead()
// Desc: Called while the async read is in progress. If the read is still
//       underway, simply returns. If the read has completed, sets up the
//       next read. If the file has been completely read, closes the file.
//-----------------------------------------------------------------------------
void CADPCMFileStream::AsyncRead( void )
{
	Dbg_Assert( m_hFile != INVALID_HANDLE_VALUE );

    // If paused, do nothing.
	if( m_Paused )
	{
        return;
    }

    // See if the previous read is complete.
	DWORD	dwBytesTransferred;
	BOOL	bIsReadDone = GetOverlappedResult( m_hFile, m_pOverlapped, &dwBytesTransferred, FALSE );
	DWORD	dwLastError = GetLastError();

    // If the read isn't complete, keep going.
	if( !bIsReadDone )
	{
		Dbg_Assert( dwLastError == ERROR_IO_INCOMPLETE );
		if( dwLastError != ERROR_IO_INCOMPLETE )
		{
			m_AwaitingDeletion = true;
		}
		return;
    }

	// If we get here, the read is complete.
    m_pOverlapped->Offset	+= dwBytesTransferred;
	m_FileBytesRead			+= dwBytesTransferred;

	++m_SuccessiveReads;

	if( dwBytesTransferred < BYTES_PER_READ )
	{ 
		// We've reached the end of the file during the call to ReadFile.

        // Close the file (if not using the global WAD file).
		if( !m_bUseWAD )
		{
			BOOL bSuccess = CloseHandle( m_hFile );
	        Dbg_Assert( bSuccess );
		}

        m_hFile = INVALID_HANDLE_VALUE;

        // All done
		m_ReadComplete = true;
    }
    else
    {
		if( m_bUseWAD && ( m_FileBytesRead >= (int)m_dwWADLength ))
		{
			m_hFile = INVALID_HANDLE_VALUE;

			// All done
			m_ReadComplete = true;
			return;
		}
		
		// We still have more data to read. Start another asynchronous read from the file.
		BOOL bComplete	= ReadFile( m_hFile, (BYTE*)m_pFileBuffer + ( m_FileBytesRead % PCMAudio_GetFilestreamBufferSize()), BYTES_PER_READ, NULL, m_pOverlapped );
		dwLastError		= GetLastError();

		// Deal with hitting EOF (for files that are some exact multiple of BYTES_PER_READ bytes).
		if( bComplete || ( !bComplete && ( dwLastError == ERROR_HANDLE_EOF )))
		{
			// Close the file
			if( !m_bUseWAD )
			{
				BOOL bSuccess = CloseHandle( m_hFile );
				Dbg_Assert( bSuccess );
			}
			m_hFile = INVALID_HANDLE_VALUE;

			// All done
			m_ReadComplete = true;
		}
		else
		{
			Dbg_MsgAssert( bComplete || ( !bComplete && ( dwLastError == ERROR_IO_PENDING )), ( "ReadFile error: %x\n", dwLastError ));
			if( !bComplete && ( dwLastError != ERROR_IO_PENDING ))
			{
				// There was a problem, so shut this stream down.
				m_AwaitingDeletion = true;
			}
		}
    }
}



//-----------------------------------------------------------------------------
// Name: Initialize()
// Desc: Initializes the wave file streaming subsystem.
//-----------------------------------------------------------------------------
HRESULT CADPCMFileStream::Initialize( HANDLE h_file, unsigned int offset, unsigned int length, void* fileBuffer )
{
    m_dwPercentCompleted = 0;
    
	// At this stage we don't want to create the decoder or the stream. We do want to just allocate the read
	// buffer, and start pulling in the data. Once sufficient data has been grabbed, we can analyze the header
	// and create the required objects for playback.
	m_hFile			= h_file;
	m_bUseWAD		= true;
	m_dwWADOffset	= offset;	
	m_dwWADLength	= length;
	m_pFileBuffer = fileBuffer;

	Dbg_Assert( DWORD( m_pFileBuffer ) % sizeof( DWORD ) == 0 );

	return PostInitialize();
}



//-----------------------------------------------------------------------------
// Name: PostInitialize()
// Desc: Initialisation stuff following the file creation
//-----------------------------------------------------------------------------
HRESULT CADPCMFileStream::PostInitialize( void )
{
	// Start the asynchronous read from the start of the file.
	m_FirstRead				= true;
	m_ReadComplete			= false;
	m_FileBytesRead			= 0;
	m_FileBytesProcessed	= 0;
	m_SuccessiveReads		= 0;

	if( m_bUseWAD )
	{
		m_pOverlapped->Offset	= m_dwWADOffset;
	}
	else
	{
		m_pOverlapped->Offset	= 0;
	}
	m_pOverlapped->OffsetHigh	= 0;

	BOOL bComplete = ReadFile( m_hFile, m_pFileBuffer, BYTES_PER_READ, NULL, m_pOverlapped );
	if( !bComplete )
	{
		DWORD dwLastError = GetLastError();
		Dbg_Assert( dwLastError == ERROR_IO_PENDING );
		if( dwLastError != ERROR_IO_PENDING )
		{
			return HRESULT_FROM_WIN32( dwLastError );
		}
	}
	else
	{
		m_ReadComplete = true;
	}

	// That's it for now.
    return S_OK;
}



//-----------------------------------------------------------------------------
// Name: InitializeFormatBlock()
// Desc: 
//-----------------------------------------------------------------------------
HRESULT CADPCMFileStream::InitializeFormatBlock( uint32 *p_header_data )
{
	// Ensure the format block is where we expect it to be.
	if( p_header_data[3] == 0x20746D66UL )
	{
		// Read format and number of channels.
		m_wfxExtendedSourceFormat.m_wfxSourceFormat.wFormatTag		= (uint16)( p_header_data[5] & 0xFFFFUL );
		m_wfxExtendedSourceFormat.m_wfxSourceFormat.nChannels		= (uint16)( p_header_data[5] >> 16 );

		// Make sure this is Xbox ADPCM.
		if( m_wfxExtendedSourceFormat.m_wfxSourceFormat.wFormatTag != WAVE_FORMAT_XBOX_ADPCM )
		{
			return E_FAIL;
		}

		// Read samples per second.
		m_wfxExtendedSourceFormat.m_wfxSourceFormat.nSamplesPerSec	= p_header_data[6];

		// Read average bytes per second.
		m_wfxExtendedSourceFormat.m_wfxSourceFormat.nAvgBytesPerSec	= p_header_data[7];

		// Read block alignment and bits per sample.
		m_wfxExtendedSourceFormat.m_wfxSourceFormat.nBlockAlign		= (uint16)( p_header_data[8] & 0xFFFFUL );
		m_wfxExtendedSourceFormat.m_wfxSourceFormat.wBitsPerSample	= (uint16)( p_header_data[8] >> 16 );

		// Extra information.
		m_wfxExtendedSourceFormat.m_wfxSourceFormat.cbSize			= (uint16)( p_header_data[9] & 0xFFFFUL );
		m_wfxExtendedSourceFormat.m_extendedInfo					= (uint16)( p_header_data[9] >> 16 );

		// We have now processed the first 48 bytes of data.
		m_FileBytesProcessed = 48;

		// Now that we know the format, we can set the packet size.
		if( m_wfxExtendedSourceFormat.m_wfxSourceFormat.nChannels == 1 )
			m_PacketBytes = ADPCMSTRM_16BIT_MONO_PACKET_BYTES;
		else
			m_PacketBytes = ADPCMSTRM_16BIT_STEREO_PACKET_BYTES;

		return S_OK;
	}
	return E_FAIL;
}



//-----------------------------------------------------------------------------
// Name: CreateSourceBuffer()
// Desc: 
//-----------------------------------------------------------------------------
bool CADPCMFileStream::CreateSourceBuffer( void )
{
	// Allocate data buffers. The source buffer holds the CPU decompressed packets ready to submit to the stream.
	// The size of the buffer will depend on the format of the stream - stereo requires double the packet size of mono.
	// Explicitly allocate from the bottom up heap.
	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().BottomUpHeap());
	m_pvSourceBuffer = new BYTE[m_PacketBytes * ADPCMSTRM_PACKET_COUNT];
	Mem::Manager::sHandle().PopContext();

	return ( m_pvSourceBuffer != NULL );
}



//-----------------------------------------------------------------------------
// Name: PreLoadDone()
// Desc: 
//-----------------------------------------------------------------------------
bool CADPCMFileStream::PreLoadDone( void )
{
	if( m_DecoderCreation == 1 )
	{
		if( m_ReadComplete || ( m_FileBytesRead >= ( m_FileBytesProcessed + m_PacketBytes )))
		{
			return true;
		}
	}
	return false;
}



//-----------------------------------------------------------------------------
// Name: Process()
// Desc: Performs any work necessary to keep the stream playing.
//-----------------------------------------------------------------------------
HRESULT CADPCMFileStream::Process( void )
{
    HRESULT			hr;
	DSSTREAMDESC	dssd;
    DWORD			dwPacketIndex;
    
	// Do nothing if waiting to die.
	if( m_AwaitingDeletion )
	{
		return S_OK;
	}

	// Do we need to kick off another read of data?
	// Don't read anymore if we have read ahead to the point where we are within 32k of free read buffer space.
	if( !m_ReadComplete )
	{
		if( m_FileBytesRead <= ( m_FileBytesProcessed + ((int)PCMAudio_GetFilestreamBufferSize() - 32768 )))
		{
			AsyncRead();
		}
	}

	// Has the first block of raw data been read? If so we need to instantiate the playback objects and data buffers.
	if( m_FirstRead && ( m_FileBytesRead > 1024 ))
	{
		if( m_pSourceFilter == NULL )
		{
			// Create the thread which will create the in-memory decoder. 
			m_DecoderCreation = 0;

			// For WMA format, here is where we create the decoder. However, since there is no CPU-side decompression required
			// for ADPCM format, there is no requirement for a decoder.
			m_DecoderCreation	= 1;
		}

		if( m_DecoderCreation == 0 )
		{
			// Still waiting to create decoder.
			return S_OK;
		}
		else if( m_DecoderCreation == 2 )
		{
			// Failed to create decoder, just mark for deletion.
			m_AwaitingDeletion = true;
			return S_OK;
		}
		else if( m_DecoderCreation == 1 )
		{
			// Managed to create decoder.
			m_FirstRead = false;

			// Set up the format information.
			if( InitializeFormatBlock((uint32*)m_pFileBuffer ) == E_FAIL )
			{
				// Failed to decode format information, just mark for deletion.
				m_AwaitingDeletion = true;
				return S_OK;
			}

			// Create the render (DirectSoundStream) filter.
			DSMIXBINS			dsmixbins;
			DSMIXBINVOLUMEPAIR	dsmbvp[DSMIXBIN_ASSIGNMENT_MAX];
			ZeroMemory( &dssd, sizeof( dssd ));

			dssd.dwFlags					= 0;
			dssd.dwMaxAttachedPackets		= ADPCMSTRM_PACKET_COUNT;
			dssd.lpwfxFormat				= &m_wfxExtendedSourceFormat.m_wfxSourceFormat;
			dssd.lpMixBins					= &dsmixbins;

			if( m_bUse3D )
			{
				// This is only designed to be fed a mono signal.
				Dbg_Assert( m_wfxExtendedSourceFormat.m_wfxSourceFormat.nChannels == 1 );

				dsmixbins.dwMixBinCount			= 6;
				dsmixbins.lpMixBinVolumePairs	= dsmbvp;
				dsmbvp[0].dwMixBin				= DSMIXBIN_3D_FRONT_LEFT;
				dsmbvp[0].lVolume				= DSBVOLUME_EFFECTIVE_MIN;
				dsmbvp[1].dwMixBin				= DSMIXBIN_3D_FRONT_RIGHT;
				dsmbvp[1].lVolume				= DSBVOLUME_EFFECTIVE_MIN;
				dsmbvp[2].dwMixBin				= DSMIXBIN_3D_BACK_LEFT;
				dsmbvp[2].lVolume				= DSBVOLUME_EFFECTIVE_MIN;
				dsmbvp[3].dwMixBin				= DSMIXBIN_3D_BACK_RIGHT;
				dsmbvp[3].lVolume				= DSBVOLUME_EFFECTIVE_MIN;
				dsmbvp[4].dwMixBin				= DSMIXBIN_FRONT_CENTER;
				dsmbvp[4].lVolume				= DSBVOLUME_EFFECTIVE_MIN;
				dsmbvp[5].dwMixBin				= DSMIXBIN_I3DL2;
				dsmbvp[5].lVolume				= DSBVOLUME_EFFECTIVE_MIN;

				m_Mixbins						= ( 1 << DSMIXBIN_3D_FRONT_LEFT ) |
												  ( 1 << DSMIXBIN_3D_FRONT_RIGHT ) |
												  ( 1 << DSMIXBIN_3D_BACK_LEFT ) |
												  ( 1 << DSMIXBIN_3D_BACK_RIGHT ) |
												  ( 1 << DSMIXBIN_FRONT_CENTER ) |
												  ( 1 << DSMIXBIN_I3DL2 );
				m_NumMixbins					= dsmixbins.dwMixBinCount;
			}
			else
			{
				// If we are playing a music track, and if proper 5.1 output is selected, we want to feed the music to the left and
				// right back speakers with a slight echo via mixbins 5 and 6.
				// This is currently disabled.
				if( false && ( XGetAudioFlags() & XC_AUDIO_FLAGS_ENABLE_AC3 ))
				{
					// This is only designed to be fed a stereo signal.
					Dbg_Assert( m_wfxExtendedSourceFormat.m_wfxSourceFormat.nChannels == 2 );

					dsmixbins.dwMixBinCount			= 4;
					dsmixbins.lpMixBinVolumePairs	= dsmbvp;
					dsmbvp[0].dwMixBin				= DSMIXBIN_FRONT_LEFT;
					dsmbvp[0].lVolume				= DSBVOLUME_MIN;
					dsmbvp[1].dwMixBin				= DSMIXBIN_FRONT_RIGHT;
					dsmbvp[1].lVolume				= DSBVOLUME_MIN;
					dsmbvp[2].dwMixBin				= DSMIXBIN_FXSEND_5;
					dsmbvp[2].lVolume				= DSBVOLUME_MIN;
					dsmbvp[3].dwMixBin				= DSMIXBIN_FXSEND_6;
					dsmbvp[3].lVolume				= DSBVOLUME_MIN;

					m_Mixbins						= ( 1 << DSMIXBIN_FRONT_LEFT ) |
													  ( 1 << DSMIXBIN_FRONT_RIGHT ) |
													  ( 1 << DSMIXBIN_FXSEND_5 ) |
													  ( 1 << DSMIXBIN_FXSEND_6 );
					m_NumMixbins					= dsmixbins.dwMixBinCount;
				}
				else
				{
					// This is only designed to be fed a stereo signal.
					Dbg_Assert( m_wfxExtendedSourceFormat.m_wfxSourceFormat.nChannels == 2 );

					dsmixbins.dwMixBinCount			= 4;
					dsmixbins.lpMixBinVolumePairs	= dsmbvp;
					dsmbvp[0].dwMixBin				= DSMIXBIN_FRONT_LEFT;
					dsmbvp[0].lVolume				= DSBVOLUME_MIN;
					dsmbvp[1].dwMixBin				= DSMIXBIN_FRONT_RIGHT;
					dsmbvp[1].lVolume				= DSBVOLUME_MIN;
					dsmbvp[2].dwMixBin				= DSMIXBIN_BACK_LEFT;
					dsmbvp[2].lVolume				= DSBVOLUME_MIN;
					dsmbvp[3].dwMixBin				= DSMIXBIN_BACK_RIGHT;
					dsmbvp[3].lVolume				= DSBVOLUME_MIN;

					m_Mixbins						= ( 1 << DSMIXBIN_FRONT_LEFT ) |
													  ( 1 << DSMIXBIN_FRONT_RIGHT ) |
													  ( 1 << DSMIXBIN_BACK_LEFT ) |
													  ( 1 << DSMIXBIN_BACK_RIGHT );
					m_NumMixbins					= dsmixbins.dwMixBinCount;
				}
			}

			hr = DirectSoundCreateStream( &dssd, &m_pRenderFilter );
			if( FAILED( hr ))
			{
				Dbg_Assert( 0 );
				m_pRenderFilter		= NULL;
				m_AwaitingDeletion	= true;
				return S_OK;
			}

			// Set deferred volume if present.
			SetDeferredVolume();

			// Handle deferred pause.
			if( m_Paused )
			{
				m_pRenderFilter->Pause( 1 );
			}
		}
	}

	// We only want to do processing when there is sufficient data available.
	if( m_ReadComplete || ( m_FileBytesRead >= ( m_FileBytesProcessed + m_PacketBytes )))
	{
		if( !m_Completed && m_pRenderFilter )
		{
			// Find a free packet. If there's none free, we don't have anything to do.
			if( FindFreePacket( &dwPacketIndex ))
			{
				// Read from the source filter.
				if( m_bOkayToPlay )
				{
					hr = ProcessSource( dwPacketIndex );
					if( FAILED( hr ))
					{
						return hr;
					}

					if( !m_Paused )
					{
						// Send the data to the renderer
						hr = ProcessRenderer( dwPacketIndex );
						if( FAILED( hr ))
						{
							 return hr;
						}
					}
				}
			}
		}
	}
    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: FindFreePacket()
// Desc: Finds a render packet available for processing.
//-----------------------------------------------------------------------------
BOOL CADPCMFileStream::FindFreePacket( DWORD* pdwPacketIndex )
{
    for( DWORD dwPacketIndex = 0; dwPacketIndex < ADPCMSTRM_PACKET_COUNT; ++dwPacketIndex )
    {
        if( XMEDIAPACKET_STATUS_PENDING != m_adwPacketStatus[dwPacketIndex] )
        {
            if( pdwPacketIndex )
			{
                (*pdwPacketIndex) = dwPacketIndex;
			}
            return TRUE;
        }
    }
    return FALSE;
}




//-----------------------------------------------------------------------------
// Name: ProcessSource()
// Desc: Reads data from the source filter.
//-----------------------------------------------------------------------------
HRESULT CADPCMFileStream::ProcessSource( DWORD dwPacketIndex )
{
	if( m_pvSourceBuffer == NULL )
	{
		if( CreateSourceBuffer() == false )
		{
			// Failed to create the source buffer, mark for deletion.
			m_AwaitingDeletion = true;
			return E_FAIL;
		}
	}
    
	// We just want to copy a full packet's worth of data from the file buffer directly into the source buffer...
	uint8*	p_destination		= (BYTE*)m_pvSourceBuffer + ( dwPacketIndex * m_PacketBytes );

	// However we don't want to overrun the file buffer when copying.
	uint32	file_buffer_offset	= m_FileBytesProcessed % PCMAudio_GetFilestreamBufferSize();
	if(( PCMAudio_GetFilestreamBufferSize() - file_buffer_offset ) < (uint32)m_PacketBytes )
	{
		// Copying the data in one chunk will take us beyond the edge of the file buffer.
		// So we need to do the copy in two chunks.
		uint8*	p_source			= (BYTE*)m_pFileBuffer + file_buffer_offset;
		uint32	first_chunk_bytes	= PCMAudio_GetFilestreamBufferSize() - file_buffer_offset;
		CopyMemory( p_destination, p_source, first_chunk_bytes );

		// Wrap file buffer back round to start.
		p_source					= (BYTE*)m_pFileBuffer + 0;
		p_destination				= p_destination + first_chunk_bytes;
		CopyMemory( p_destination, p_source, m_PacketBytes - first_chunk_bytes );
	}
	else
	{
		// Copying the data in one chunk is fine.
		uint8* p_source = (BYTE*)m_pFileBuffer + file_buffer_offset;
		CopyMemory( p_destination, p_source, m_PacketBytes );
	}

	// Now these bytes have been processed.
	m_FileBytesProcessed += m_PacketBytes;

	// If we've caught up with the number of bytes read, it's because we've finished processing.
    if( m_FileBytesProcessed >= m_FileBytesRead )
	{
		// Set completion flag.
		m_Completed = true;

		// Zero remaining part of packet.
		uint32 bytes_to_zero = m_FileBytesProcessed - m_FileBytesRead;
		if( bytes_to_zero > 0 )
		{
			p_destination = (BYTE*)m_pvSourceBuffer + ( dwPacketIndex * m_PacketBytes ) + m_PacketBytes - bytes_to_zero;
			ZeroMemory( p_destination, bytes_to_zero );
		}
	}
    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: ProcessRenderer()
// Desc: Sends data to the renderer.
//-----------------------------------------------------------------------------
HRESULT CADPCMFileStream::ProcessRenderer( DWORD dwPacketIndex )
{
    XMEDIAPACKET xmp;
    HRESULT      hr;

	Dbg_Assert( m_pvSourceBuffer != NULL );

	// There's a full packet's worth of data ready for us to send to the renderer.  We want to track the status
	// of this packet since the render filter is asychronous and we need to know when the packet is completed.
    ZeroMemory( &xmp, sizeof( xmp ));
    xmp.pvBuffer  = (BYTE*)m_pvSourceBuffer + ( dwPacketIndex * m_PacketBytes );
    xmp.dwMaxSize = m_PacketBytes;
    xmp.pdwStatus = &m_adwPacketStatus[dwPacketIndex];

	if( m_Completed )
	{
		// Store index of last packet, since we will need to test the status of this for proper completion test.
		m_LastPacket = dwPacketIndex;
	}

    hr = m_pRenderFilter->Process( &xmp, NULL );

	if( m_Completed )
	{
		// Tell the renderer not to expect any more data.
		m_pRenderFilter->Discontinuity();
	}
	
	if( FAILED( hr ))
	{
		return hr;
	}
	
	return S_OK;
}



//-----------------------------------------------------------------------------
// Name: Pause
// Desc: Pauses and resumes stream playback
//-----------------------------------------------------------------------------
void CADPCMFileStream::Pause( DWORD dwPause )
{
	m_Paused = ( dwPause > 0 );

	// Possible that the render filter hasn't been created yet.
	if( m_pRenderFilter )
	{
		m_pRenderFilter->Pause(( dwPause > 0 ) ? DSSTREAMPAUSE_PAUSE : DSSTREAMPAUSE_RESUME );
	}
}



//-----------------------------------------------------------------------------
// Name: SetVolume
// Desc: 
//-----------------------------------------------------------------------------
void CADPCMFileStream::SetVolume( float volume )
{
	if( m_pRenderFilter )
	{
		int i_volume		= DSBVOLUME_EFFECTIVE_MIN;
		int i_volume_rear	= DSBVOLUME_EFFECTIVE_MIN;
		if( volume > 0.0f )
		{
			// Figure base volume.
			float attenuation	= 20.0f * log10f( volume * 0.01f );
			i_volume			= DSBVOLUME_MAX + (int)( attenuation * 100.0f );
			if( i_volume < DSBVOLUME_EFFECTIVE_MIN )
				i_volume = DSBVOLUME_EFFECTIVE_MIN;
			else if( i_volume > DSBVOLUME_MAX )
				i_volume = DSBVOLUME_MAX;

			// Also figure half volume, in case we are routing to the back speakers.
			attenuation			= 20.0f * log10f( volume * 0.5f * 0.01f );
			i_volume_rear		= DSBVOLUME_MAX + (int)( attenuation * 100.0f );
			if( i_volume_rear < DSBVOLUME_EFFECTIVE_MIN )
				i_volume_rear = DSBVOLUME_EFFECTIVE_MIN;
			else if( i_volume_rear > DSBVOLUME_MAX )
				i_volume_rear = DSBVOLUME_MAX;
		}
		
		// Set individual mixbins for panning.
		DSMIXBINS			dsmixbins;
		DSMIXBINVOLUMEPAIR	dsmbvp[DSMIXBIN_ASSIGNMENT_MAX];

		dsmixbins.dwMixBinCount			= 0;
		dsmixbins.lpMixBinVolumePairs	= dsmbvp;

		if( i_volume > DSBVOLUME_EFFECTIVE_MIN )
		{
			// Set the volume up depending on how the initial mixbins were set up.
			int mbbf = 0;
			for( uint32 mb = 0; mb < m_NumMixbins; ++mb )
			{
				while(( m_Mixbins & ( 1 << mbbf )) == 0 )
				{
					++mbbf;
					Dbg_Assert( mbbf < 32 );
				}
				dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= mbbf;

				// For rear speakers (or mixbins that route to the rear), use half volume.
				if(( mbbf == DSMIXBIN_FXSEND_5 ) || ( mbbf == DSMIXBIN_FXSEND_6 ) || ( mbbf == DSMIXBIN_BACK_LEFT ) || ( mbbf == DSMIXBIN_BACK_RIGHT ))
				{
					dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volume_rear;
				}
				else
				{
					dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volume;
				}
				++dsmixbins.dwMixBinCount;
				++mbbf;
			}
		}

		// Set all speaker volumes.
		m_pRenderFilter->SetMixBinVolumes( &dsmixbins );

		// Set overall buffer volume.
		if( dsmixbins.dwMixBinCount > 0 )
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MAX );
		}
		else
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MIN );
		}
	}
	else
	{
		m_SetDeferredVolume = true;
		m_DeferredVolume[0]	= volume;
	}
}



//-----------------------------------------------------------------------------
// Name: SetVolume
// Desc: 
//-----------------------------------------------------------------------------
void CADPCMFileStream::SetVolume( float volL, float volR )
{
	if( m_pRenderFilter )
	{
		// This array will hold individual volumes for the five speakers.
		// In order, they are: front left, center, front right, rear right, rear left.
		float	volumes[5];
		int		i_volumes[5], max_i_volume;
		memset( volumes, 0, sizeof( float ) * 5 );
		
		if(( volL == 0.0f ) && ( volR == 0.0f ))
		{
			// Pointless doing any more work.
		}
		else
		{
			// Get the length of the vector here which will be used to multiply out the normalised speaker volumes.
			Mth::Vector test( fabsf( volL ), fabsf( volR ), 0.0f, 0.0f );
			float amplitude = test.Length();

			// Look just at the normalized right component to figure the sound angle from Matt's calculations.
			test.Normalize();

			float angle;
			angle	= asinf( test[Y] );
			angle	= ( angle * 2.0f ) - ( Mth::PI * 0.5f );
			angle	= ( volL < 0.0f ) ? ( Mth::PI - angle ) : angle;
		
			// Now figure volumes based on speaker coverage.
			angle	= Mth::RadToDeg( angle );
		
			Spt::SingletonPtr< Sfx::CSfxManager > sfx_manager;
			sfx_manager->Get5ChannelMultipliers( angle, &volumes[0] );

			// Now readjust the relative values...
			for( int v = 0; v < 5; ++v )
			{
				// Scale back up to original amplitude.
				volumes[v] *= amplitude;

				if( volumes[v] > 100.0f )
					volumes[v] = 100.0f;
			}
		}
		
		// Now figure the attenuation of the sound. To convert to a decibel value, figure the ratio of requested
		// volume versus max volume, then calculate the log10 and multiply by (10 * 2). (The 2 is because sound
		// power varies as square of pressure, and squaring doubles the log value).
		max_i_volume = DSBVOLUME_EFFECTIVE_MIN;
		for( int v = 0; v < 5; ++v )
		{
			if( volumes[v] > 0.0f )
			{
				float attenuation	= 20.0f * log10f( volumes[v] * 0.01f );
				i_volumes[v]		= DSBVOLUME_MAX + (int)( attenuation * 100.0f );
				if( i_volumes[v] < DSBVOLUME_EFFECTIVE_MIN )
					i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
				else if( i_volumes[v] > DSBVOLUME_MAX )
					i_volumes[v] = DSBVOLUME_MAX;

				if( i_volumes[v] > max_i_volume )
					max_i_volume = i_volumes[v];
			}
			else
			{
				i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
			}
		}
		
		// Set individual mixbins for panning.
		DSMIXBINS			dsmixbins;
		DSMIXBINVOLUMEPAIR	dsmbvp[DSMIXBIN_ASSIGNMENT_MAX];

		dsmixbins.dwMixBinCount			= 0;
		dsmixbins.lpMixBinVolumePairs	= dsmbvp;

		if( i_volumes[0] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_FRONT_LEFT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[0];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[1] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_FRONT_RIGHT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[1];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[2] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_BACK_LEFT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[2];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[3] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_BACK_RIGHT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[3];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[4] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_FRONT_CENTER;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[4];
            dsmixbins.dwMixBinCount++;
		}
		if( dsmixbins.dwMixBinCount > 0 )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_I3DL2;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= DSBVOLUME_MAX;
            dsmixbins.dwMixBinCount++;
		}

		// Set all speaker volumes.
		m_pRenderFilter->SetMixBinVolumes( &dsmixbins );

		// Set overall buffer volume.
		if( dsmixbins.dwMixBinCount > 0 )
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MAX );
		}
		else
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MIN );
		}
	}
	else
	{
		m_SetDeferredVolumeLR	= true;
		m_DeferredVolume[0]		= volL;
		m_DeferredVolume[1]		= volR;
	}
}



//-----------------------------------------------------------------------------
// Name: SetVolume
// Desc: 
//-----------------------------------------------------------------------------
void CADPCMFileStream::SetVolume( float v0, float v1, float v2, float v3, float v4 )
{
	if( m_pRenderFilter )
	{
		float volumes[5];
		volumes[0] = ( v0 > 100.0f ) ? 100.0f : v0;
		volumes[1] = ( v1 > 100.0f ) ? 100.0f : v1;
		volumes[2] = ( v2 > 100.0f ) ? 100.0f : v2;
		volumes[3] = ( v3 > 100.0f ) ? 100.0f : v3;
		volumes[4] = ( v4 > 100.0f ) ? 100.0f : v4;

		int i_volumes[5], max_i_volume;

		// Now figure the attenuation of the sound. To convert to a decibel value, figure the ratio of requested
		// volume versus max volume, then calculate the log10 and multiply by (10 * 2). (The 2 is because sound
		// power varies as square of pressure, and squaring doubles the log value).
		max_i_volume = DSBVOLUME_EFFECTIVE_MIN;
		for( int v = 0; v < 5; ++v )
		{
			if( volumes[v] > 0.0f )
			{
				float attenuation	= 20.0f * log10f( volumes[v] * 0.01f );
				i_volumes[v]		= DSBVOLUME_MAX + (int)( attenuation * 100.0f );
				if( i_volumes[v] < DSBVOLUME_EFFECTIVE_MIN )
					i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
				else if( i_volumes[v] > DSBVOLUME_MAX )
					i_volumes[v] = DSBVOLUME_MAX;

				if( i_volumes[v] > max_i_volume )
					max_i_volume = i_volumes[v];
			}
			else
			{
				i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
			}
		}
		
		// Set individual mixbins for panning.
		DSMIXBINS			dsmixbins;
		DSMIXBINVOLUMEPAIR	dsmbvp[DSMIXBIN_ASSIGNMENT_MAX];

		dsmixbins.dwMixBinCount			= 0;
		dsmixbins.lpMixBinVolumePairs	= dsmbvp;

		if( i_volumes[0] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_FRONT_LEFT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[0];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[1] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_FRONT_RIGHT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[1];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[2] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_BACK_LEFT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[2];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[3] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_BACK_RIGHT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[3];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[4] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_FRONT_CENTER;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[4];
            dsmixbins.dwMixBinCount++;
		}
		if( dsmixbins.dwMixBinCount > 0 )
		{
			dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_I3DL2;
			dsmbvp[dsmixbins.dwMixBinCount].lVolume		= DSBVOLUME_MAX;
			dsmixbins.dwMixBinCount++;
		}

		// Set all speaker volumes.
		m_pRenderFilter->SetMixBinVolumes( &dsmixbins );

		// Set overall buffer volume.
		if( dsmixbins.dwMixBinCount > 0 )
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MAX );
		}
		else
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MIN );
		}
	}
	else
	{
		m_SetDeferredVolume5Channel	= true;
		m_DeferredVolume[0]			= v0;
		m_DeferredVolume[1]			= v1;
		m_DeferredVolume[2]			= v2;
		m_DeferredVolume[3]			= v3;
		m_DeferredVolume[4]			= v4;
	}
}



//-----------------------------------------------------------------------------
// Name: SetDeferredVolume
// Desc: 
//-----------------------------------------------------------------------------
void CADPCMFileStream::SetDeferredVolume( void )
{
	if( m_SetDeferredVolume )
	{
		m_SetDeferredVolume = false;
		SetVolume( m_DeferredVolume[0] );
	}

	if( m_SetDeferredVolumeLR )
	{
		m_SetDeferredVolumeLR = false;
		SetVolume( m_DeferredVolume[0], m_DeferredVolume[1] );
	}

	if( m_SetDeferredVolume5Channel )
	{
		m_SetDeferredVolume5Channel = false;
		SetVolume( m_DeferredVolume[0], m_DeferredVolume[1], m_DeferredVolume[2], m_DeferredVolume[3], m_DeferredVolume[4] );
	}
}



//-----------------------------------------------------------------------------
// Name: IsSafeToDelete
// Desc: 
//-----------------------------------------------------------------------------
bool CADPCMFileStream::IsSafeToDelete( void )
{
	return true;
}




/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/


} // namespace PCM


================================================
FILE: Code/Gel/Music/Xbox/p_adpcmfilestream.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Game Engine												**
**																			**
**	File name:		p_adpcmfilesteam.h										**
**																			**
**	Created: 		01/27/2003	-	dc										**
**																			**
*****************************************************************************/

#ifndef __P_ADPCMFILESTREAM_H
#define __P_ADPCMFILESTREAM_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#ifndef __CORE_DEFINES_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Pcm
{

// Define the maximum amount of packets we will ever submit to the renderer.
#define ADPCMSTRM_PACKET_COUNT		8

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/


/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

struct sADPCMExtendedWaveFormatEx
{
	WAVEFORMATEX	m_wfxSourceFormat;
	uint16			m_extendedInfo;
};
	
	
/******************************************************************/
/*																  */
/* ADPCM file streaming object, designed for fully asynchronous	  */
/* streaming from disc.											  */
/*																  */
/******************************************************************/
class CADPCMFileStream : public Spt::Class
{
	private:

	HANDLE				m_hFile;
	HANDLE				m_hThread;
	OVERLAPPED*			m_pOverlapped;								// OVERLAPPED structure for asynchronous file access.
	bool				m_FirstRead;								// Flag to indicate first request for more data to be streamed from disc.
	bool				m_ReadComplete;
	int					m_SuccessiveReads;							// Counts how many read operations performed in total.
	bool				m_bUseWAD;
	bool				m_bUse3D;									// Sets whether 2D or 3D mixbins are set up.
	DWORD				m_dwWADOffset;	
	DWORD				m_dwWADLength;

	public:

    IDirectSoundStream* m_pRenderFilter;							// Render (DirectSoundStream) filter
    LPVOID              m_pvSourceBuffer;							// Source filter data buffer
    LPVOID              m_pvRenderBuffer;							// Render filter data buffer
    DWORD               m_adwPacketStatus[ADPCMSTRM_PACKET_COUNT];	// Packet status array
	int					m_PacketBytes;								// Size of each packet (will differ for mono and stereo).
	DWORD               m_dwStartingDataOffset;						// Offset into wma file where data begins.
    DWORD				m_dwPercentCompleted;						// Percent completed
	bool				m_Paused;
	bool				m_Completed;								// For single-shot, indicates loading of final packet to renderer is completed
    DWORD               m_LastPacket;								// Last packet array index
	bool				m_SetDeferredVolume;
	bool				m_SetDeferredVolumeLR;
	bool				m_SetDeferredVolume5Channel;
	bool				m_bOkayToPlay;								// Used when exact syncing is required - will load buffers but won't start decompression until this flag is true.
	float				m_DeferredVolume[5];
	uint32				m_Mixbins;									// Bitfield indicating which mixbins are active for this buffer.
	uint32				m_NumMixbins;

    // Packet processing
    BOOL				FindFreePacket( DWORD* pdwPacketIndex );
    HRESULT				ProcessSource( DWORD dwPacketIndex );
    HRESULT				ProcessRenderer( DWORD dwPacketIndex );

	sADPCMExtendedWaveFormatEx	m_wfxExtendedSourceFormat;
    XFileMediaObject*   m_pSourceFilter;							// Source (wave file) filter
	LPVOID				m_pFileBuffer;								// Buffer for async read of raw data.
	int					m_FileBytesRead;							// Total number of raw bytes read from disc.
	int					m_FileBytesProcessed;						// Total number of raw bytes processed by XMO.
	int					m_DecoderCreation;
	bool				m_AwaitingDeletion;

    // Processing
    HRESULT				Process();

    // Initialization
	HRESULT				InitializeFormatBlock( uint32 *p_header_data );
	HRESULT				Initialize( HANDLE h_file, unsigned int offset, unsigned int length, void* fileBuffer );
	HRESULT				PostInitialize( void );
	bool				CreateSourceBuffer( void );
    
    // Play control
    void				Pause( DWORD dwPause );
	void				SetVolume( float volume );
	void				SetVolume( float volumeL, float volumeR );
	void				SetVolume( float v0, float v1, float v2, float v3, float v4 );
	void				SetDeferredVolume( void );

	// Query
	IDirectSoundStream*	GetSoundStream( void )					{ return m_pRenderFilter; }
	bool				IsCompleted( void )						{ return ( m_Completed && ( XMEDIAPACKET_STATUS_PENDING != m_adwPacketStatus[m_LastPacket] )); }
	bool				IsSafeToDelete( void );
	bool				PreLoadDone( void );

	// Asynchronous stuff.
	void				AsyncRead( void );


    CADPCMFileStream( bool use_3d = false );
    ~CADPCMFileStream();
};

	
	
/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Pcm

#endif	// __P_ADPCMFILESTREAM_H






================================================
FILE: Code/Gel/Music/Xbox/p_music.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Game Engine (GEL)	 									**
**																			**
**	File name:		p_music.cpp												**
**																			**
**	Created:		07/24/01	-	dc										**
**																			**
**	Description:	Xbox specific .wma streaming code						**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include "p_music.h"
#include "p_wmafilestream.h"
#include "p_adpcmfilestream.h"
#include "p_soundtrack.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

namespace Pcm
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define STREAMS_ARE_PCM

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sFileStreamInfo
{
	enum FileStreamType
	{
		FILESTREAM_TYPE_WMA		= 0,
		FILESTREAM_TYPE_ADPCM	= 1
	};

	// These two could really be a union...
	CWMAFileStream*		p_wma_filestream;
	CADPCMFileStream*	p_adpcm_filestream;

	int					pitch;
	float				volume;
	bool				paused;

	void				CreateFileStream( FileStreamType type, bool use_3d = false );
	void				DestroyFileStream( void );
	
	HRESULT				Initialize( HANDLE h_file, void* fileBuffer );
    HRESULT				Initialize( HANDLE h_file, unsigned int offset, unsigned int length, void* fileBuffer );
	HRESULT				Process( void );
	void				Pause( uint32 pause );
	void				SetOkayToPlay( bool okay_to_play );
	bool				GetOkayToPlay( void );
	bool				HasFileStream( void )	{ return (( p_wma_filestream != NULL ) || ( p_adpcm_filestream != NULL )); }
	bool				IsCompleted( void );
	bool				IsAwaitingDeletion( void );
	void				SetAwaitingDeletion( bool is_awaiting );
	bool				IsSafeToDelete( void );
	void				Flush( void );
	bool				IsPreLoadDone( void );
	void				SetVolume( float v0 );
	void				SetVolume( float v0, float v1 );
	void				SetVolume( float v0, float v1, float v2, float v3, float v4 );
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sFileStreamInfo::CreateFileStream( FileStreamType type, bool use_3d )
{
	Dbg_Assert(( p_wma_filestream == NULL ) && ( p_adpcm_filestream == NULL ));
	if( type == FILESTREAM_TYPE_WMA )
	{
		p_wma_filestream = new CWMAFileStream( use_3d );
	}
	else if( type == FILESTREAM_TYPE_ADPCM )
	{
		p_adpcm_filestream = new CADPCMFileStream( use_3d );
	}
	else
	{
		Dbg_Assert( 0 );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sFileStreamInfo::DestroyFileStream( void )
{
	if( p_wma_filestream )
	{
		delete p_wma_filestream;
		p_wma_filestream = NULL;
	}

	if( p_adpcm_filestream )
	{
		delete p_adpcm_filestream;
		p_adpcm_filestream = NULL;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
HRESULT sFileStreamInfo::Initialize( HANDLE h_file, void* fileBuffer )
{
	if( p_wma_filestream )
	{
		return p_wma_filestream->Initialize( h_file, fileBuffer );
	}
	else if( p_adpcm_filestream )
	{
		Dbg_Assert( 0 );
	}
	return -1;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
HRESULT sFileStreamInfo::Initialize( HANDLE h_file, unsigned int offset, unsigned int length, void* fileBuffer )
{
	if( p_wma_filestream )
	{
		return p_wma_filestream->Initialize( h_file, offset, length, fileBuffer );
	}
	else if( p_adpcm_filestream )
	{
		return p_adpcm_filestream->Initialize( h_file, offset, length, fileBuffer );
	}
	return -1;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
HRESULT sFileStreamInfo::Process( void )
{
	if( p_wma_filestream )
	{
		return p_wma_filestream->Process();
	}
	else if( p_adpcm_filestream )
	{
		return p_adpcm_filestream->Process();
	}
	return -1;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sFileStreamInfo::Pause( uint32 pause )
{
	if( p_wma_filestream )
	{
		p_wma_filestream->Pause( pause );
	}
	else if( p_adpcm_filestream )
	{
		p_adpcm_filestream->Pause( pause );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sFileStreamInfo::SetOkayToPlay( bool okay_to_play )
{
	if( p_wma_filestream )
	{
		p_wma_filestream->m_bOkayToPlay = okay_to_play;
	}
	else if( p_adpcm_filestream )
	{
		p_adpcm_filestream->m_bOkayToPlay = okay_to_play;
	}
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool sFileStreamInfo::GetOkayToPlay( void )
{
	if( p_wma_filestream )
	{
		return p_wma_filestream->m_bOkayToPlay;
	}
	else if( p_adpcm_filestream )
	{
		return p_adpcm_filestream->m_bOkayToPlay;
	}
	return false;
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool sFileStreamInfo::IsCompleted( void )
{
	if( p_wma_filestream )
	{
		return p_wma_filestream->IsCompleted();
	}
	else if( p_adpcm_filestream )
	{
		return p_adpcm_filestream->IsCompleted();
	}
	return false;
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool sFileStreamInfo::IsAwaitingDeletion( void )
{
	if( p_wma_filestream )
	{
		return p_wma_filestream->m_AwaitingDeletion;
	}
	else if( p_adpcm_filestream )
	{
		return p_adpcm_filestream->m_AwaitingDeletion;
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sFileStreamInfo::SetAwaitingDeletion( bool is_awaiting )
{
	if( p_wma_filestream )
	{
		p_wma_filestream->m_AwaitingDeletion = is_awaiting;
	}
	else if( p_adpcm_filestream )
	{
		p_adpcm_filestream->m_AwaitingDeletion = is_awaiting;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool sFileStreamInfo::IsSafeToDelete( void )
{
	if( p_wma_filestream )
	{
		return p_wma_filestream->IsSafeToDelete();
	}
	else if( p_adpcm_filestream )
	{
		return p_adpcm_filestream->IsSafeToDelete();
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool sFileStreamInfo::IsPreLoadDone( void )
{
	if( p_wma_filestream )
	{
		return p_wma_filestream->PreLoadDone();
	}
	else if( p_adpcm_filestream )
	{
		return p_adpcm_filestream->PreLoadDone();
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sFileStreamInfo::Flush( void )
{
	if( p_wma_filestream ) 
	{
		if( p_wma_filestream->m_pRenderFilter )
		{
			p_wma_filestream->m_pRenderFilter->FlushEx( 0, DSSTREAMFLUSHEX_ASYNC );
		}
	}
	else if( p_adpcm_filestream )
	{
		if( p_adpcm_filestream->m_pRenderFilter )
		{
			p_adpcm_filestream->m_pRenderFilter->FlushEx( 0, DSSTREAMFLUSHEX_ASYNC );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sFileStreamInfo::SetVolume( float v0 )
{
	if( p_wma_filestream ) 
	{
		p_wma_filestream->SetVolume( v0 );
	}
	else if( p_adpcm_filestream )
	{
		p_adpcm_filestream->SetVolume( v0 );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sFileStreamInfo::SetVolume( float v0, float v1 )
{
	if( p_wma_filestream ) 
	{
		p_wma_filestream->SetVolume( v0, v1 );
	}
	else if( p_adpcm_filestream )
	{
		p_adpcm_filestream->SetVolume( v0, v1 );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sFileStreamInfo::SetVolume( float v0, float v1, float v2, float v3, float v4 )
{
	if( p_wma_filestream ) 
	{
		p_wma_filestream->SetVolume( v0, v1, v2, v3, v4 );
	}
	else if( p_adpcm_filestream )
	{
		p_adpcm_filestream->SetVolume( v0, v1, v2, v3, v4 );
	}
}



/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

sFileStreamInfo	gMusicInfo;
sFileStreamInfo	gStreamInfo[NUM_STREAMS];

const uint32	FILESTREAM_BUFFER_SIZE	= 80 * 1024;

#pragma pack( 16 )
// Grab an 80k read buffer. Must be DWORD aligned.
// This is big enough for (9 * 8k packets) for WMA, where last packet mirrors the first packet,
// for cases when the decoder reads past the end of the ring buffer.
// Also big enough for ( 5 * 16k packets) for ADPCM, where no wraparound is required.
DWORD			gMusicFileBuffer[FILESTREAM_BUFFER_SIZE / 4];
DWORD			gStreamFileBuffer[NUM_STREAMS][FILESTREAM_BUFFER_SIZE / 4];
#pragma pack()

const int		NUM_OVERLAPPED	= 256;
OVERLAPPED		gOverlapped[NUM_OVERLAPPED];
int				gNextOverlap = 0;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

HANDLE	ghWADFile				= INVALID_HANDLE_VALUE;
uint32	*pWADData				= NULL;
uint32	numWADFileEntries		= 0;

HANDLE	ghMusicWADFile			= INVALID_HANDLE_VALUE;
uint32	*pMusicWADData			= NULL;
uint32	numMusicWADFileEntries	= 0;



/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
OVERLAPPED *PCMAudio_GetNextOverlapped( void )
{
	OVERLAPPED *p_return = &( gOverlapped[gNextOverlap] );
	if( ++gNextOverlap >= NUM_OVERLAPPED )
	{
		gNextOverlap = 0;
	}

	ZeroMemory( p_return, sizeof( OVERLAPPED ));
	return p_return;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 PCMAudio_GetFilestreamBufferSize( void )
{
	return FILESTREAM_BUFFER_SIZE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_Init( void )
{
	// Zero the music and filestream info arrays.
	ZeroMemory( &gMusicInfo, sizeof( sFileStreamInfo ));
	ZeroMemory( &gStreamInfo[0], sizeof( sFileStreamInfo ) * NUM_STREAMS );

	// Enumerate user soundtracks.
	GetNumSoundtracks();

	// Figure out the language, and open appropriate file.
	Config::ELanguage lang = Config::GetLanguage();

	// Assume English.
#	ifdef STREAMS_ARE_PCM
	ghWADFile		= CreateFile( "d:\\data\\streams\\pcm\\pcm.wad",
#	else
	ghWADFile		= CreateFile( "d:\\data\\streams\\wma\\wma.wad",
#	endif
								GENERIC_READ,
								FILE_SHARE_READ,								// Share mode.
								NULL,											// Ignored (security attributes).
								OPEN_EXISTING,									// File has to exist already.
								FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,	// Xbox has no asynchronous i/o buffering.
								NULL );											// Ignored (template file).
#	ifdef STREAMS_ARE_PCM
	ghMusicWADFile = CreateFile( "d:\\data\\streams\\pcm\\music_pcm.wad",
#	else
	ghMusicWADFile = CreateFile( "d:\\data\\streams\\wma\\music_wma.wad",
#	endif
								GENERIC_READ,
								FILE_SHARE_READ,								// Share mode.
								NULL,											// Ignored (security attributes).
								OPEN_EXISTING,									// File has to exist already.
								FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,	// Xbox has no asynchronous i/o buffering.
								NULL );											// Ignored (template file).

	// Now read in the data files used for indexing into the WAD files.
#	ifdef STREAMS_ARE_PCM
	HANDLE wad_data = CreateFile( "d:\\data\\streams\\pcm\\pcm.dat",
#	else
	HANDLE wad_data = CreateFile( "d:\\data\\streams\\wma\\wma.dat",
#	endif
									GENERIC_READ,
									FILE_SHARE_READ,								// Share mode.
									NULL,											// Ignored (security attributes).
									OPEN_EXISTING,									// File has to exist already.
									FILE_FLAG_SEQUENTIAL_SCAN,
									NULL );											// Ignored (template file).

	if( wad_data != INVALID_HANDLE_VALUE )
	{
		uint32 bytes_read;
		ReadFile( wad_data, &numWADFileEntries, sizeof( uint32 ), &bytes_read, NULL );
		pWADData = new uint32[numWADFileEntries * 3];
		ReadFile( wad_data, pWADData, sizeof( uint32 ) * numWADFileEntries * 3, &bytes_read, NULL );
		CloseHandle( wad_data );
	}
			
	// Sort the wad file entries into increasing checksum order, so that we can use a binary search algorithm to
	// find the checksum quickly.
	for( uint32 i = 0; i < numWADFileEntries; ++i )
	{
		for( uint32 j = i + 1; j < numWADFileEntries; ++j )
		{
			if( pWADData[i * 3] > pWADData[j * 3] )
			{
				uint32 temp[3];
				temp[0]				= pWADData[i * 3];
				temp[1]				= pWADData[i * 3 + 1];
				temp[2]				= pWADData[i * 3 + 2];
				pWADData[i * 3]		= pWADData[j * 3];
				pWADData[i * 3 + 1]	= pWADData[j * 3 + 1];
				pWADData[i * 3 + 2]	= pWADData[j * 3 + 2];
				pWADData[j * 3]		= temp[0];
				pWADData[j * 3 + 1]	= temp[1];
				pWADData[j * 3 + 2]	= temp[2];
			}
		}
	}
			
#	ifdef STREAMS_ARE_PCM
	wad_data = CreateFile( "d:\\data\\streams\\pcm\\music_pcm.dat",
#	else
	wad_data = CreateFile( "d:\\data\\streams\\wma\\music_wma.dat",
#	endif
							GENERIC_READ,
							FILE_SHARE_READ,								// Share mode.
							NULL,											// Ignored (security attributes).
							OPEN_EXISTING,									// File has to exist already.
							FILE_FLAG_SEQUENTIAL_SCAN,
							NULL );											// Ignored (template file).

	if( wad_data != INVALID_HANDLE_VALUE )
	{
		uint32 bytes_read;
		ReadFile( wad_data, &numMusicWADFileEntries, sizeof( uint32 ), &bytes_read, NULL );
		pMusicWADData = new uint32[numMusicWADFileEntries * 3];
		ReadFile( wad_data, pMusicWADData, sizeof( uint32 ) * numMusicWADFileEntries * 3, &bytes_read, NULL );
		CloseHandle( wad_data );
	}
}



/******************************************************************/
/*                                                                */
/* Call every frame to make sure music and stream buffers are	  */
/* loaded and current status is checked each frame...			  */
/*                                                                */
/******************************************************************/
int PCMAudio_Update( void )
{
	if( gMusicInfo.HasFileStream())
	{
		HRESULT hr = gMusicInfo.Process();

		if( gMusicInfo.IsCompleted())
		{
			gMusicInfo.DestroyFileStream();
		}
		else if( gMusicInfo.IsAwaitingDeletion() && gMusicInfo.IsSafeToDelete())
		{
			gMusicInfo.DestroyFileStream();
		}
	}

	for( int i = 0; i < NUM_STREAMS; ++i )
	{
		if( gStreamInfo[i].HasFileStream())
		{
			HRESULT hr = gStreamInfo[i].Process();

			if( gStreamInfo[i].IsCompleted())
			{
				gStreamInfo[i].DestroyFileStream();
			}
			else if( gStreamInfo[i].IsAwaitingDeletion() && gStreamInfo[i].IsSafeToDelete())
			{
				gStreamInfo[i].DestroyFileStream();
			}
		}
	}

	// A non-zero return value singnals an error condition.
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_StopMusic( bool waitPlease )
{
	if( gMusicInfo.HasFileStream())
	{
		if( gMusicInfo.IsSafeToDelete())
		{	
			gMusicInfo.Flush();
			gMusicInfo.DestroyFileStream();
		}
		else
		{
			gMusicInfo.SetAwaitingDeletion( true );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_StopStream( int whichStream, bool waitPlease )
{
	if( gStreamInfo[whichStream].HasFileStream())
	{
		if( gStreamInfo[whichStream].IsSafeToDelete())
		{
			gStreamInfo[whichStream].Flush();
			gStreamInfo[whichStream].DestroyFileStream();
		}
		else
		{
			gStreamInfo[whichStream].SetAwaitingDeletion( true );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_StopStreams( void )
{
	for( int i = 0; i < NUM_STREAMS; ++i )
	{
		PCMAudio_StopStream( i, false );
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This is temp code for the preload streams.  It just calls the normal one.

static uint32 sPreLoadChecksum[NUM_STREAMS];
static uint32 sPreLoadMusicChecksum;



/******************************************************************/
/*                                                                */
/* Get a stream loaded into a buffer, but don't play yet		  */
/*                                                                */
/******************************************************************/
bool PCMAudio_PreLoadStream( uint32 checksum, int whichStream )
{
	Dbg_Assert(( whichStream >= 0 ) && ( whichStream < NUM_STREAMS ));
	sPreLoadChecksum[whichStream] = checksum;

	// Start the track as normal...
	if( PCMAudio_PlayStream( checksum, whichStream, NULL, 0.0f, false ))
	{
		// ...but then flag it as not okay to play until we say so.
		if( gStreamInfo[whichStream].HasFileStream())
		{
			gStreamInfo[whichStream].SetOkayToPlay( false );
		}
        return true;
	}
	return false;
}



/******************************************************************/
/*                                                                */
/* Returns true if preload done. Assumes that caller is calling	  */
/* this on a preloaded, but not yet played, stream. The results	  */
/* are meaningless otherwise.									  */
/*                                                                */
/******************************************************************/
bool PCMAudio_PreLoadStreamDone( int whichStream )
{
	if( gStreamInfo[whichStream].HasFileStream())
	{
		return gStreamInfo[whichStream].IsPreLoadDone();
	}
	return true;
}



/******************************************************************/
/*                                                                */
/* Tells a preloaded stream to start playing.					  */
/* Must call PCMAudio_PreLoadStreamDone() first to guarantee that */
/* it starts immediately.										  */
/*                                                                */
/******************************************************************/
bool PCMAudio_StartPreLoadedStream( int whichStream, Sfx::sVolume *p_volume, float pitch )
{
	// Maybe we should check here to make sure the checksum of the music info filestream matches that
	// passed in when the music stream preload request came in.
	if( gStreamInfo[whichStream].HasFileStream())
	{
		gStreamInfo[whichStream].SetOkayToPlay( true );
		PCMAudio_SetStreamVolume( p_volume, whichStream );
		return true;
	}
	return false;	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_PreLoadMusicStream( uint32 checksum )
{
	sPreLoadMusicChecksum = checksum;

	// Start the track as normal...
	if( PCMAudio_PlayMusicTrack( sPreLoadMusicChecksum ))
	{
		// ...but then flag it as not okay to play until we say so.
		if( gMusicInfo.HasFileStream())
		{
			gMusicInfo.SetOkayToPlay( false );
		}
        return true;
	}

	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_PreLoadMusicStreamDone( void )
{
	if( gMusicInfo.HasFileStream())
	{
		return gMusicInfo.IsPreLoadDone();
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_StartPreLoadedMusicStream( void )
{
	// Maybe we should check here to make sure the checksum of the music info filestream matches that
	// passed in when the music stream preload request came in.
	if( gMusicInfo.HasFileStream())
	{
		// Call update immediately to start playback ASAP.
		gMusicInfo.SetOkayToPlay( true );
		PCMAudio_Update();

		return true;
	}
	return false;	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int PCMAudio_GetMusicStatus( void )
{
	if( gMusicInfo.HasFileStream())
	{
		return PCM_STATUS_PLAYING;
	}
	else
	{
		return PCM_STATUS_FREE;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int PCMAudio_GetStreamStatus( int whichStream )
{
	int start, end;

	// Negative one is used to signal 'any stream'.
	if( whichStream == -1 )
	{
		start	= 0;
		end		= NUM_STREAMS;
	}
	else
	{
		start	= whichStream;
		end		= start + 1;
	}

	for( int s = start; s < end; ++s )
	{
		if( !gStreamInfo[s].HasFileStream())
		{		
			return PCM_STATUS_FREE;
		}
	}

	return PCM_STATUS_PLAYING;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_Pause( bool pause, int ch )
{
	if( ch == MUSIC_CHANNEL )
	{
		gMusicInfo.paused = pause;
		if( gMusicInfo.HasFileStream())
		{
			if( pause )
			{
				gMusicInfo.Pause( 1 );
			}
			else
			{
				gMusicInfo.Pause( 0 );
			}
		}
	}
	else
	{
		for( int s = 0; s < NUM_STREAMS; ++s )
		{
			if( gStreamInfo[s].HasFileStream())
			{
				if( pause )
				{
					gStreamInfo[s].Pause( 1 );
					gStreamInfo[s].paused = true;
				}
				else
				{
					gStreamInfo[s].Pause( 0 );
					gStreamInfo[s].paused = false;
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_TrackExists( const char *pTrackName, int ch )
{
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_LoadMusicHeader( const char *nameOfFile )
{
	// Legacy call left over from PS2 code.
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_LoadStreamHeader( const char *nameOfFile )
{
	// Legacy call left over from PS2 code.
	return true;
}



/******************************************************************/
/*                                                                */
/* Return (any) position if t in sorted x[0..n-1] or -1 if t is	  */
/* not present.                                                    */
/*                                                                */
/******************************************************************/
static int binarySearch( uint32 checksum )
{
	int l = 0;
	int u = numWADFileEntries - 1;
	while( l <= u )
	{
		int m = ( l + u ) / 2;
		if( pWADData[m * 3] < checksum )
			l = m + 1;
		else if ( pWADData[m * 3] == checksum )
			return m;
		else // x[m] > t
			u = m - 1;
	}
	return -1;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 PCMAudio_FindNameFromChecksum( uint32 checksum, int ch )
{
	if( ch != EXTRA_CHANNEL )
		return 0;

	int rv = binarySearch( checksum );
	
	return ( rv == -1 ) ? 0 : checksum;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_SetStreamVolume( Sfx::sVolume *p_volume, int whichStream )
{
	if( gStreamInfo[whichStream].HasFileStream())
	{
		// Adjust volumes for overall sound volume.
		Spt::SingletonPtr< Sfx::CSfxManager > sfx_manager;

		switch( p_volume->GetVolumeType())
		{
			case Sfx::VOLUME_TYPE_5_CHANNEL_DOLBY5_1:
			{
				float v0	= PERCENT( sfx_manager->GetMainVolume(), p_volume->GetChannelVolume( 0 ));
				float v1	= PERCENT( sfx_manager->GetMainVolume(), p_volume->GetChannelVolume( 1 ));
				float v2	= PERCENT( sfx_manager->GetMainVolume(), p_volume->GetChannelVolume( 2 ));
				float v3	= PERCENT( sfx_manager->GetMainVolume(), p_volume->GetChannelVolume( 3 ));
				float v4	= PERCENT( sfx_manager->GetMainVolume(), p_volume->GetChannelVolume( 4 ));

				gStreamInfo[whichStream].volume = ( v0 + v1 + v2 + v3 + v4 ) * ( 1.0f / 5.0f );
				gStreamInfo[whichStream].SetVolume( v0, v1, v2, v3, v4 );
				break;
			}
			case Sfx::VOLUME_TYPE_2_CHANNEL_DOLBYII:
			{
				float v0	= PERCENT( sfx_manager->GetMainVolume(), p_volume->GetChannelVolume( 0 ));
				float v1	= PERCENT( sfx_manager->GetMainVolume(), p_volume->GetChannelVolume( 1 ));

				gStreamInfo[whichStream].volume = v0;
				gStreamInfo[whichStream].SetVolume( v0, v1 );
				break;
			}
			case Sfx::VOLUME_TYPE_BASIC_2_CHANNEL:
			{
				float v0	= PERCENT( sfx_manager->GetMainVolume(), p_volume->GetChannelVolume( 0 ));

				gStreamInfo[whichStream].volume = v0;
				gStreamInfo[whichStream].SetVolume( v0 );
				break;
			}
			default:
			{
				Dbg_Assert( 0 );
				break;
			}
		}
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int PCMAudio_SetMusicVolume( float volume )
{
	if( gMusicInfo.HasFileStream())
	{
		gMusicInfo.volume = volume;
		gMusicInfo.SetVolume( volume );
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_SetStreamPitch( float fPitch, int whichStream )
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_PlayMusicTrack( uint32 checksum )
{
	// Find the entry in the offset array.
	bool found = false;
	
	unsigned int samplelength, sampleoffset;
	
	// English.
	for( unsigned int entry = 0; entry < numMusicWADFileEntries; ++entry )
	{
		if( pMusicWADData[entry * 3] == checksum )
		{
			sampleoffset	= pMusicWADData[entry * 3 + 1];
			samplelength	= pMusicWADData[entry * 3 + 2];
			found			= true;
			break;
		}
	}

	if( !found )
	{
		return false;
	}

	// Just a stream like everything else.
	if( PCMAudio_GetMusicStatus() != PCM_STATUS_FREE )
	{
		return false;
	}

	// Don't want to use 3d processing for the music track.
#	ifdef STREAMS_ARE_PCM
	gMusicInfo.CreateFileStream( sFileStreamInfo::FILESTREAM_TYPE_ADPCM, false );
#	else
	gMusicInfo.CreateFileStream( sFileStreamInfo::FILESTREAM_TYPE_WMA, false );
#	endif

	HRESULT hr = gMusicInfo.Initialize( ghMusicWADFile, sampleoffset, samplelength, gMusicFileBuffer );
	if( hr == S_OK )
	{
		// All started fine. Pause music if paused flag is set.
		if( gMusicInfo.paused )
		{
			PCMAudio_Pause( true, MUSIC_CHANNEL );
		}
		return true;
	}
	else
	{
		// Failed to initialize the stream.
		gMusicInfo.DestroyFileStream();
		Dbg_MsgAssert( 0, ( "Failed to initialize music stream: %x", checksum ));
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_PlayMusicTrack( const char *filename )
{
	const char	*samplename = filename;
	char		*locate;
	
	// Search for the last directory seperator, and cut off all of the path prior to it.
	if( locate = strrchr( samplename, '\\' ))
	{
		samplename = locate + 1;
	}
	if( locate = strrchr( samplename, '/' ))
	{
		samplename = locate + 1;
	}

	// Now generate the checksum for this samplename.
	uint32 checksum = Crc::GenerateCRCFromString( samplename );

	bool rv = PCMAudio_PlayMusicTrack( checksum );
	if( rv == false )
	{
		Dbg_Message( "Failed to find stream: %s", filename );
	}
	return rv;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_PlaySoundtrackMusicTrack( int soundtrack, int track )
{
	// Just a stream like everything else.
	if( gMusicInfo.HasFileStream())
	{
		Dbg_MsgAssert( 0, ( "Playing new track without stopping the first track." ));
	}
	else
	{
		// Don't want to use 3d processing for the music track.
		gMusicInfo.CreateFileStream( sFileStreamInfo::FILESTREAM_TYPE_WMA, false );

		HANDLE h_song = GetSoundtrackWMAHandle( soundtrack, track );
		if( h_song == INVALID_HANDLE_VALUE )
		{
			return false;
		}

		HRESULT hr = gMusicInfo.Initialize( h_song, gMusicFileBuffer );
		if( hr == S_OK )
		{
			// All started fine. Pause music if paused flag is set.
			if( gMusicInfo.paused )
			{
				PCMAudio_Pause( true, MUSIC_CHANNEL );
			}
			return true;
		}
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_StartStream( int whichStream )
{
	if( gStreamInfo[whichStream].HasFileStream())
	{
		if( gStreamInfo[whichStream].GetOkayToPlay() == false )
		{
			// Now okay to start playing.
			gStreamInfo[whichStream].SetOkayToPlay( true );
		}
		return true;
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_PlayStream( uint32 checksum, int whichStream, Sfx::sVolume *p_volume, float fPitch, bool preload )
{
	unsigned int samplelength, sampleoffset;

	// Perform a binary search on the checksum table to find the correct entry.	
	int entry = binarySearch( checksum );
	if( entry >= 0 )
	{
		sampleoffset	= pWADData[entry * 3 + 1];
		samplelength	= pWADData[entry * 3 + 2];
	}
	else
	{
		return false;
	}
	
	Dbg_Assert(( whichStream >= 0 ) && ( whichStream < NUM_STREAMS ));

	// Just a stream like everything else.
	if( PCMAudio_GetStreamStatus( whichStream ) != PCM_STATUS_FREE )
	{
		return false;
	}

#	ifdef STREAMS_ARE_PCM
	gStreamInfo[whichStream].CreateFileStream( sFileStreamInfo::FILESTREAM_TYPE_ADPCM, true );
#	else
	gStreamInfo[whichStream].CreateFileStream( sFileStreamInfo::FILESTREAM_TYPE_WMA, true );
#	endif

	// The preload parameter indicates that we want to get this stream in a position where it is ready to play
	// immediately at some later point in time. The m_bOkayToPlay member is used to signal this.
	if( preload )
	{
		// Not okay to start playing until told to do so.
		gStreamInfo[whichStream].SetOkayToPlay( false );
	}

	HRESULT hr = gStreamInfo[whichStream].Initialize( ghWADFile, sampleoffset, samplelength, &gStreamFileBuffer[whichStream][0] );

	if( hr == S_OK )
	{
		// All started fine.
		if( p_volume )
		{
			PCMAudio_SetStreamVolume( p_volume, whichStream );
		}
		return true;
	}
	else
	{
		// Failed to initialize the stream.
		gStreamInfo[whichStream].DestroyFileStream();
	}
	return false;
}


} // namespace PCM


================================================
FILE: Code/Gel/Music/Xbox/p_music.h
================================================
// Audio streaming function prototypes:
// mjd jan 2001

#ifndef __P_MUSIC_H__
#define __P_MUSIC_H__

// Needed so music.cpp will build. Grrr.
#define NUM_STREAMS 3

namespace Pcm
{

// allows one channel for music, another for audio:
enum{
	EXTRA_CHANNEL,
	MUSIC_CHANNEL,
};

// Onetime call once upon loading the game...
void PCMAudio_Init( void );

OVERLAPPED *PCMAudio_GetNextOverlapped( void );

// Call every frame to make sure music and stream buffers are loaded and current status is checked each frame...
int PCMAudio_Update( void );

// Load a track and start it playing...
// You wanna loop a track?  Wait until this track is done, and PLAY IT AGAIN SAM!
bool PCMAudio_PlayMusicTrack( const char *filename );
bool PCMAudio_PlayMusicTrack( uint32 checksum );
bool PCMAudio_PlaySoundtrackMusicTrack( int soundtrack, int track );
//bool PCMAudio_PlayStream( uint32 checksum, int whichStream, float volumeL, float volumeR, float pitch, bool preload = false );
bool PCMAudio_PlayStream( uint32 checksum, int whichStream, Sfx::sVolume *p_volume, float pitch, bool preload = false );
bool PCMAudio_StartStream( int whichStream );

// keep song loaded, stop playing it (or continue playing paused song)
void PCMAudio_Pause( bool pause = true, int ch = MUSIC_CHANNEL );

void PCMAudio_StopMusic( bool waitPlease );
void PCMAudio_StopStream( int whichStream, bool waitPlease = true );
void PCMAudio_StopStreams( void );

uint32	PCMAudio_GetFilestreamBufferSize( void );

// Preload streams. By preloading, you can guarantee they will start at a certain time.
bool PCMAudio_PreLoadStream( uint32 checksum, int whichStream );
bool PCMAudio_PreLoadStreamDone( int whichStream );
//bool PCMAudio_StartPreLoadedStream( int whichStream, float volumeL, float volumeR, float pitch );
bool PCMAudio_StartPreLoadedStream( int whichStream, Sfx::sVolume *p_volume, float pitch );

// Preload music streams. By preloading, you can guarantee they will start at a certain time.
bool PCMAudio_PreLoadMusicStream( uint32 checksum );
bool PCMAudio_PreLoadMusicStreamDone( void );
bool PCMAudio_StartPreLoadedMusicStream( void );

// set the music volume ( 0 to 100 )
int PCMAudio_SetMusicVolume( float volume );
bool	PCMAudio_SetStreamPitch( float pitch, int whichStream );
//int PCMAudio_SetStreamVolumeAndPitch( float volL, float volR, float pitch, int whichStream );

enum{
	PCM_STATUS_FREE 			= ( 1 << 0 ),
	PCM_STATUS_PLAYING			= ( 1 << 1 ),
	PCM_STATUS_LOADING 			= ( 1 << 2 ),
};

bool PCMAudio_SetStreamVolume( Sfx::sVolume *p_volume, int whichStream );

// Return one of the PCM_STATUS values from above...
// Return one of the PCM_STATUS values from above...
int PCMAudio_GetMusicStatus( );
int PCMAudio_GetStreamStatus( int whichStream );

bool PCMAudio_TrackExists( const char *pTrackName, int ch );
bool PCMAudio_LoadMusicHeader( const char *nameOfFile );
bool PCMAudio_LoadStreamHeader( const char *nameOfFile );

uint32 PCMAudio_FindNameFromChecksum( uint32 checksum, int ch );
	 
} // namespace PCM

#endif // __P_MUSIC_H__


================================================
FILE: Code/Gel/Music/Xbox/p_soundtrack.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Game Engine (GEL)	 									**
**																			**
**	File name:		p_soundtrack.cpp										**
**																			**
**	Created:		11/29/01	-	dc										**
**																			**
**	Description:	Xbox specific user soundtrack code						**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 

#include "p_music.h"
#include "p_soundtrack.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

namespace Pcm
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define MAX_SOUNDTRACKS	100

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

XSOUNDTRACK_DATA	soundtrackData[MAX_SOUNDTRACKS];
bool				initialised = false;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

int gNumSoundtracks = 0;

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int GetNumSoundtracks( void )
{
	BOOL				rv;
	HANDLE				h_strack;

	if( initialised )
	{
		return gNumSoundtracks;
	}

	initialised		= true;
	gNumSoundtracks	= 0;

	h_strack = XFindFirstSoundtrack( &soundtrackData[gNumSoundtracks] );
	if( h_strack != INVALID_HANDLE_VALUE )
	{
		do
		{
			++gNumSoundtracks;

			// Don't go over the maximum (should be a system-limit of 100 anyway).
			if( gNumSoundtracks >= MAX_SOUNDTRACKS )
			{
				break;
			}

			rv = XFindNextSoundtrack( h_strack, &soundtrackData[gNumSoundtracks] );
		}	
		while( rv );
	}

	return gNumSoundtracks;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const WCHAR* GetSoundtrackName( int soundtrack )
{
	if( soundtrack < gNumSoundtracks )
	{
		return soundtrackData[soundtrack].szName;
	}
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
unsigned int GetSoundtrackNumSongs( int soundtrack )
{
	if( soundtrack < gNumSoundtracks )
	{
		return soundtrackData[soundtrack].uSongCount;
	}
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const WCHAR* GetSongName( int soundtrack, int track )
{
	static WCHAR wcSongName[MAX_SONG_NAME];

	if( soundtrack < gNumSoundtracks )
	{
		// Check the track is within limits.
		if((UINT)track < soundtrackData[soundtrack].uSongCount )
		{
			DWORD dwSongID;
			DWORD dwSongLength;

			BOOL rv = XGetSoundtrackSongInfo(	soundtrackData[soundtrack].uSoundtrackId,
												track,
												&dwSongID,
												&dwSongLength,
												wcSongName,
												MAX_SONG_NAME );

			if( rv )
			{
				return wcSongName;
			}
		}
	}
	return NULL;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
HANDLE GetSoundtrackWMAHandle( int soundtrack, int track )
{
	if( soundtrack < gNumSoundtracks )
	{
		// Check the track is within limits.
		if((UINT)track < soundtrackData[soundtrack].uSongCount )
		{
			DWORD dwSongID;
			DWORD dwSongLength;

			BOOL rv = XGetSoundtrackSongInfo(	soundtrackData[soundtrack].uSoundtrackId,
												track,
												&dwSongID,
												&dwSongLength,
												NULL,
												0 );

			if( rv )
			{
				// Second parameter is true for asynchronous mode reads.
				HANDLE h_song = XOpenSoundtrackSong( dwSongID, TRUE );
				return h_song;
			}
		}
	}
	return INVALID_HANDLE_VALUE;
}




} // namespace PCM


================================================
FILE: Code/Gel/Music/Xbox/p_soundtrack.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Game Engine												**
**																			**
**	File name:		p_soundtrack.h											**
**																			**
**	Created: 		11/29/2001	-	dc										**
**																			**
*****************************************************************************/

#ifndef __MODULES_P_SOUNDTRACK_H
#define __MODULES_P_SOUNDTRACK_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
 
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Pcm
{


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/


/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

int				GetNumSoundtracks( void );
const WCHAR*	GetSoundtrackName( int soundtrack );
unsigned int	GetSoundtrackNumSongs( int soundtrack );
const WCHAR*	GetSongName( int soundtrack, int track );
HANDLE			GetSoundtrackWMAHandle( int soundtrack, int track );


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Pcm

#endif	// __MODULES_P_SOUNDTRACK_H


================================================
FILE: Code/Gel/Music/Xbox/p_wmafilestream.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:																	**
**																			**
**	File name:		p_wmafilestream.cpp										**
**																			**
**	Created:		01/27/03	-	dc										**
**																			**
**	Description:	Xbox specific .wma streaming code						**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 

#include "p_music.h"
#include "p_wmafilestream.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

namespace Pcm
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// Define the source packet size:
// This value is hard-coded assuming a WMA file of stero, 16bit resolution.  If
// this value can by dynamically set based on the wma format, keeping in mind
// that wma needs enough buffer for a minimum of 2048 samples worth of PCM data
#define WMASTRM_SOURCE_PACKET_BYTES	( 2048 * 2 * 2 )



// This is a homegrown value which is used in conjunction with the existing
// DSound packet status values. This indicates a packet which has been written
// to, but which has not yet been submitted to the renderer. The low-order 24
// bits contain a timestamp. Packets should be submitted lowest-timestamp first.
#define XMEDIAPACKET_STATUS_AWAITING_RENDER		0x40000000UL

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

uint32 CALLBACK WMAXMediaObjectDataCallback( LPVOID pContext, uint32 offset, uint32 num_bytes, LPVOID *ppData )
{
	Dbg_Assert( pContext != NULL );
	Dbg_Assert( ppData != NULL );

	CWMAFileStream *p_this = (CWMAFileStream*)pContext;
	Dbg_Assert(( p_this->m_DecoderCreation == 0 ) || ( p_this->m_DecoderCreation == 1 ));

	*ppData = (BYTE*)( p_this->m_pFileBuffer ) + ( offset % ( 8 * 8192 ));

    // Update current progress.
    p_this->m_FileBytesProcessed	= offset;

    return num_bytes;
}



/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

//-----------------------------------------------------------------------------
// Name: CWMAFileStream()
// Desc: Object constructor.
//-----------------------------------------------------------------------------
CWMAFileStream::CWMAFileStream( bool use_3d )
{
    m_pSourceFilter		= NULL;
    m_pRenderFilter		= NULL;
    m_pvSourceBuffer	= NULL;
    m_pFileBuffer		= NULL;
	m_hFile				= INVALID_HANDLE_VALUE;
	m_hThread			= NULL;
	m_bUse3D			= use_3d;
	m_bOkayToPlay		= true;

    for( uint32 i = 0; i < WMASTRM_PACKET_COUNT; i++ )
	{
        m_adwPacketStatus[i] = XMEDIAPACKET_STATUS_SUCCESS;
	}

    m_dwStartingDataOffset	= 0;
	m_Completed				= false;
	m_Paused				= false;

	// Grab a pointer to the next overlapped structure.
	m_pOverlapped			= PCMAudio_GetNextOverlapped();
}



//-----------------------------------------------------------------------------
// Name: ~CWMAFileStream()
// Desc: Object destructor.
//-----------------------------------------------------------------------------
CWMAFileStream::~CWMAFileStream()
{
	if( m_hThread )
	{
		CloseHandle( m_hThread );
	}
	
	// If the file i/o is still active, we need to remove the event and close the file.
	if(( m_hFile != INVALID_HANDLE_VALUE ) && !m_bUseWAD )
	{
		CloseHandle( m_hFile );
	}

    if( m_pSourceFilter )
	{
		m_pSourceFilter->Release();
	}

    if( m_pRenderFilter )
	{
		m_pRenderFilter->Release();
	}

	if( m_pvSourceBuffer )
	{
		delete[] m_pvSourceBuffer;
	}
}



//-----------------------------------------------------------------------------
// Name: AsyncRead()
// Desc: Called while the async read is in progress. If the read is still
//       underway, simply returns. If the read has completed, sets up the
//       next read. If the file has been completely read, closes the file.
//-----------------------------------------------------------------------------
void CWMAFileStream::AsyncRead( void )
{
	Dbg_Assert( m_hFile != INVALID_HANDLE_VALUE );

    // If paused, do nothing.
	if( m_Paused )
	{
        return;
    }

    // See if the previous read is complete.
	uint32	dwBytesTransferred;
	bool	bIsReadDone = GetOverlappedResult( m_hFile, m_pOverlapped, &dwBytesTransferred, false );
	uint32	dwLastError = GetLastError();

    // If the read isn't complete, keep going.
	if( !bIsReadDone )
	{
		Dbg_Assert( dwLastError == ERROR_IO_INCOMPLETE );
		if( dwLastError != ERROR_IO_INCOMPLETE )
		{
			m_AwaitingDeletion = true;
		}
		return;
    }

    // If we get here, the read is complete.
    m_pOverlapped->Offset	+= dwBytesTransferred;
	m_FileBytesRead			+= dwBytesTransferred;

	// If we just read to the first block in the buffer, copy to the extra block at the end, to ensure jitter-free playback.
	if(( m_SuccessiveReads & 0x07 ) == 0 )
	{
		CopyMemory((BYTE*)m_pFileBuffer + ( 8 * 8192 ), (BYTE*)m_pFileBuffer, 8192 );
	}

	++m_SuccessiveReads;

#	define BYTES_PER_CALL	8192

	if( dwBytesTransferred < 8192 )
	{ 
		// We've reached the end of the file during the call to ReadFile.

        // Close the file (if not using the global WAD file).
		if( !m_bUseWAD )
		{
			bool bSuccess = CloseHandle( m_hFile );
	        Dbg_Assert( bSuccess );
		}

        m_hFile = INVALID_HANDLE_VALUE;

        // All done
		m_ReadComplete = true;
    }
    else
    {
		if( m_bUseWAD && ( m_FileBytesRead >= (int)m_dwWADLength ))
		{
			m_hFile = INVALID_HANDLE_VALUE;

			// All done
			m_ReadComplete = true;
			return;
		}
		
		// We still have more data to read. Start another asynchronous read from the file.
		bool bComplete	= ReadFile( m_hFile, (BYTE*)m_pFileBuffer + ( m_FileBytesRead % ( 8 * 8192 )), BYTES_PER_CALL, NULL, m_pOverlapped );
		dwLastError		= GetLastError();

		// Deal with hitting EOF (for files that are some exact multiple of 8192 bytes).
		if( bComplete || ( !bComplete && ( dwLastError == ERROR_HANDLE_EOF )))
		{
			// Close the file
			if( !m_bUseWAD )
			{
				bool bSuccess = CloseHandle( m_hFile );
				Dbg_Assert( bSuccess );
			}
			m_hFile = INVALID_HANDLE_VALUE;

			// All done
			m_ReadComplete = true;
		}
		else
		{
			Dbg_MsgAssert( bComplete || ( !bComplete && ( dwLastError == ERROR_IO_PENDING )), ( "ReadFile error: %x\n", dwLastError ));
			if( !bComplete && ( dwLastError != ERROR_IO_PENDING ))
			{
				// There was a problem, so shut this stream down.
				m_AwaitingDeletion = true;
			}
		}
    }
}



//-----------------------------------------------------------------------------
// Name: Initialize()
// Desc: Initializes the wave file streaming subsystem.
//-----------------------------------------------------------------------------
HRESULT CWMAFileStream::Initialize( HANDLE h_file, unsigned int offset, unsigned int length, void* fileBuffer )
{
    m_dwPercentCompleted = 0;
    
	// At this stage we don't want to create the decoder or the stream. We do want to just allocate the read
	// buffer, and start pulling in the data. Once sufficient data has been grabbed, we can analyze the header
	// and create the required objects for playback.
	m_hFile			= h_file;
	m_bUseWAD		= true;
	m_dwWADOffset	= offset;	
	m_dwWADLength	= length;
	
	// Grab a 72k (9 * 8k packets) read buffer. Must be uint32 aligned.
	// The last packet mirrors the first packet, for cases when the decoder reads past the end of the ring buffer.
	m_pFileBuffer = fileBuffer;
	Dbg_Assert( uint32( m_pFileBuffer ) % sizeof( uint32 ) == 0 );

	return PostInitialize();
}



//-----------------------------------------------------------------------------
// Name: Initialize()
// Desc: Initializes the wave file streaming subsystem.
//-----------------------------------------------------------------------------
HRESULT CWMAFileStream::Initialize( HANDLE h_song, void* fileBuffer )
{
    HRESULT			hr;

    m_dwPercentCompleted = 0;
    
	// At this stage we don't want to create the decoder or the stream. We do want to just allocate the read
	// buffer, and start pulling in the data. Once sufficient data has been grabbed, we can analyze the header
	// and create the required objects for playback.
	m_hFile = h_song;
	if( m_hFile == INVALID_HANDLE_VALUE )
	{
		hr = HRESULT_FROM_WIN32( GetLastError());
        return hr;
    }

	// Grab a 72k (9 * 8k packets) read buffer. Must be uint32 aligned.
	// The last packet mirrors the first packet, for cases when the decoder reads past the end of the ring buffer.
	m_pFileBuffer = fileBuffer;
	Dbg_Assert( uint32( m_pFileBuffer ) % sizeof( uint32 ) == 0 );

	return PostInitialize();
}



//-----------------------------------------------------------------------------
// Name: PostInitialize()
// Desc: Initialisation stuff following the file creation
//-----------------------------------------------------------------------------
HRESULT CWMAFileStream::PostInitialize( void )
{
	// Start the asynchronous read from the start of the file.
	m_FirstRead				= true;
	m_ReadComplete			= false;
	m_FileBytesRead			= 0;
	m_FileBytesProcessed	= 0;
	m_SuccessiveReads		= 0;

	if( m_bUseWAD )
	{
		m_pOverlapped->Offset	= m_dwWADOffset;
	}
	else
	{
		m_pOverlapped->Offset	= 0;
	}
	m_pOverlapped->OffsetHigh	= 0;

	bool bComplete = ReadFile( m_hFile, m_pFileBuffer, BYTES_PER_CALL, NULL, m_pOverlapped );
	if( !bComplete )
	{
		uint32 dwLastError = GetLastError();
		Dbg_Assert( dwLastError == ERROR_IO_PENDING );
		if( dwLastError != ERROR_IO_PENDING )
		{
			return HRESULT_FROM_WIN32( dwLastError );
		}
	}
	else
	{
		m_ReadComplete = true;
	}

	// That's it for now.
    return S_OK;
}



//-----------------------------------------------------------------------------
// Name: CreateSourceBuffer()
// Desc: 
//-----------------------------------------------------------------------------
void CWMAFileStream::CreateSourceBuffer( void )
{
	// Allocate data buffers. The source buffer holds the CPU decompressed packets ready to submit to the stream.
	// Explicitly allocate from the bottom up heap.
	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().BottomUpHeap());
	m_pvSourceBuffer = new BYTE[WMASTRM_SOURCE_PACKET_BYTES * WMASTRM_PACKET_COUNT];
	Mem::Manager::sHandle().PopContext();
	Dbg_Assert( m_pvSourceBuffer != NULL );
}



//-----------------------------------------------------------------------------
// Name: PreLoadDone()
// Desc: 
//-----------------------------------------------------------------------------
bool CWMAFileStream::PreLoadDone( void )
{
	if( m_DecoderCreation == 1 )
	{
		if( m_ReadComplete || ( m_FileBytesRead >= ( m_FileBytesProcessed + 8192 )))
		{
			return true;
		}
	}
	return false;
}



//-----------------------------------------------------------------------------
// Name: Process()
// Desc: Performs any work necessary to keep the stream playing.
//-----------------------------------------------------------------------------
HRESULT CWMAFileStream::Process( void )
{
    HRESULT			hr;
	DSSTREAMDESC	dssd;
    uint32			dwPacketIndex;
    
	// Do nothing if waiting to die.
	if( m_AwaitingDeletion )
	{
		return S_OK;
	}

	// Do we need to kick off another read of data? Don't read anymore if we have read ahead more than 32k.
	if( !m_ReadComplete )
	{
		if( m_FileBytesRead > ( m_FileBytesProcessed + 32768 ))
		{
//			OutputDebugString( "waiting...\n" );
		}
		else
		{
			AsyncRead();
//			OutputDebugString( "reading...\n" );
		}
	}
	else
	{
//		OutputDebugString( "complete...\n" );
	}

	// Has the first block of raw data been read? If so we need to instantiate the playback objects and data buffers.
	if( m_FirstRead && ( m_FileBytesRead > 1024 ))
	{
		if( m_pSourceFilter == NULL )
		{
			// Create the thread which will create the in-memory decoder. 
			m_DecoderCreation = 0;

			HRESULT hr = WmaCreateInMemoryDecoder(	WMAXMediaObjectDataCallback,			// Callback pointer.
													this,									// Callback context pointer.
													3,										// Yield rate during decoding.
													&m_wfxSourceFormat,						// Source format description.
													(LPXMEDIAOBJECT*)&m_pSourceFilter );	// Pointer to media object.
			if( FAILED( hr ))
			{
				// Signal a failed creation.
				m_DecoderCreation	= 2;
				m_pSourceFilter		= NULL;
			}
			else
			{
				m_DecoderCreation	= 1;
			}
			
			// That's all we can do until the decoder has been instantiated.
		}

		if( m_DecoderCreation == 0 )
		{
			// Still waiting to create decoder.
			return S_OK;
		}
		else if( m_DecoderCreation == 2 )
		{
			// Failed to create decoder, just mark for deletion.
			m_AwaitingDeletion = true;
			return S_OK;
		}
		else if( m_DecoderCreation == 1 )
		{
			// Managed to create decoder.
			Dbg_Assert( m_pSourceFilter != NULL );

			m_FirstRead = false;

			// Create the render (DirectSoundStream) filter.
			DSMIXBINS			dsmixbins;
			DSMIXBINVOLUMEPAIR	dsmbvp[7];
			ZeroMemory( &dssd, sizeof( dssd ));

			dssd.dwFlags					= 0;
			dssd.dwMaxAttachedPackets		= WMASTRM_PACKET_COUNT;
			dssd.lpwfxFormat				= &m_wfxSourceFormat;
			dssd.lpMixBins					= &dsmixbins;

			if( m_bUse3D )
			{
				// This is only designed to be fed a mono signal.
				Dbg_Assert( m_wfxSourceFormat.nChannels == 1 );

				dsmixbins.dwMixBinCount			= 6;
				dsmixbins.lpMixBinVolumePairs	= dsmbvp;
				dsmbvp[0].dwMixBin				= DSMIXBIN_3D_FRONT_LEFT;
				dsmbvp[0].lVolume				= DSBVOLUME_EFFECTIVE_MIN;
				dsmbvp[1].dwMixBin				= DSMIXBIN_3D_FRONT_RIGHT;
				dsmbvp[1].lVolume				= DSBVOLUME_EFFECTIVE_MIN;
				dsmbvp[2].dwMixBin				= DSMIXBIN_3D_BACK_LEFT;
				dsmbvp[2].lVolume				= DSBVOLUME_EFFECTIVE_MIN;
				dsmbvp[3].dwMixBin				= DSMIXBIN_3D_BACK_RIGHT;
				dsmbvp[3].lVolume				= DSBVOLUME_EFFECTIVE_MIN;
				dsmbvp[4].dwMixBin				= DSMIXBIN_FRONT_CENTER;
				dsmbvp[4].lVolume				= DSBVOLUME_EFFECTIVE_MIN;
				dsmbvp[5].dwMixBin				= DSMIXBIN_I3DL2;
				dsmbvp[5].lVolume				= DSBVOLUME_EFFECTIVE_MIN;

				m_Mixbins						= ( 1 << DSMIXBIN_3D_FRONT_LEFT ) |
												  ( 1 << DSMIXBIN_3D_FRONT_RIGHT ) |
												  ( 1 << DSMIXBIN_3D_BACK_LEFT ) |
												  ( 1 << DSMIXBIN_3D_BACK_RIGHT ) |
												  ( 1 << DSMIXBIN_FRONT_CENTER ) |
												  ( 1 << DSMIXBIN_I3DL2 );
				m_NumMixbins					= dsmixbins.dwMixBinCount;
			}
			else
			{
				// If we are playing a music track, and if proper 5.1 output is selected, we want to feed the music to the left and
				// right back speakers with a slight echo via mixbins 5 and 6.
				if( XGetAudioFlags() & XC_AUDIO_FLAGS_ENABLE_AC3 )
				{
					// This is only designed to be fed a stereo signal.
					Dbg_Assert( m_wfxSourceFormat.nChannels == 2 );

					dsmixbins.dwMixBinCount			= 4;
					dsmixbins.lpMixBinVolumePairs	= dsmbvp;
					dsmbvp[0].dwMixBin				= DSMIXBIN_FRONT_LEFT;
					dsmbvp[0].lVolume				= DSBVOLUME_MIN;
					dsmbvp[1].dwMixBin				= DSMIXBIN_FRONT_RIGHT;
					dsmbvp[1].lVolume				= DSBVOLUME_MIN;
					dsmbvp[2].dwMixBin				= DSMIXBIN_FXSEND_5;
					dsmbvp[2].lVolume				= DSBVOLUME_MIN;
					dsmbvp[3].dwMixBin				= DSMIXBIN_FXSEND_6;
					dsmbvp[3].lVolume				= DSBVOLUME_MIN;

					m_Mixbins						= ( 1 << DSMIXBIN_FRONT_LEFT ) |
													  ( 1 << DSMIXBIN_FRONT_RIGHT ) |
													  ( 1 << DSMIXBIN_FXSEND_5 ) |
													  ( 1 << DSMIXBIN_FXSEND_6 );
					m_NumMixbins					= dsmixbins.dwMixBinCount;
				}
				else
				{
					// This is only designed to be fed a stereo signal.
					Dbg_Assert( m_wfxSourceFormat.nChannels == 2 );

					dsmixbins.dwMixBinCount			= 4;
					dsmixbins.lpMixBinVolumePairs	= dsmbvp;
					dsmbvp[0].dwMixBin				= DSMIXBIN_FRONT_LEFT;
					dsmbvp[0].lVolume				= DSBVOLUME_MIN;
					dsmbvp[1].dwMixBin				= DSMIXBIN_FRONT_RIGHT;
					dsmbvp[1].lVolume				= DSBVOLUME_MIN;
					dsmbvp[2].dwMixBin				= DSMIXBIN_BACK_LEFT;
					dsmbvp[2].lVolume				= DSBVOLUME_MIN;
					dsmbvp[3].dwMixBin				= DSMIXBIN_BACK_RIGHT;
					dsmbvp[3].lVolume				= DSBVOLUME_MIN;

					m_Mixbins						= ( 1 << DSMIXBIN_FRONT_LEFT ) |
													  ( 1 << DSMIXBIN_FRONT_RIGHT ) |
													  ( 1 << DSMIXBIN_BACK_LEFT ) |
													  ( 1 << DSMIXBIN_BACK_RIGHT );
					m_NumMixbins					= dsmixbins.dwMixBinCount;
				}
			}

			hr = DirectSoundCreateStream( &dssd, &m_pRenderFilter );
			if( FAILED( hr ))
			{
				Dbg_Assert( 0 );
				m_pRenderFilter		= NULL;
				m_AwaitingDeletion	= true;
				return S_OK;
			}

			// Set deferred volume if present.
			SetDeferredVolume();

			// Handle deferred pause.
			if( m_Paused )
			{
				m_pRenderFilter->Pause( DSSTREAMPAUSE_PAUSE );
			}
		}
	}

	// We only want to do processing when there is sufficient data available.
	if( m_ReadComplete || ( m_FileBytesRead >= ( m_FileBytesProcessed + 8192 )))
	{
		if( !m_Completed && m_pSourceFilter && m_pRenderFilter )
		{
			// Find a free packet. If there's none free, we don't have anything to do.
			if( FindFreePacket( &dwPacketIndex ))
			{
				// Read from the source filter.
				hr = ProcessSource( dwPacketIndex );
				if( FAILED( hr ) )
				{
					return hr;
				}
			}

			// Don't want to submit if we're paused, or we're flagged not to play. (Flagging is used to allow
			// the stream to be spooled up ready to play instantly when triggered).
			if(( !m_Paused ) && m_bOkayToPlay )
			{
				if( FindRenderablePacket( &dwPacketIndex ))
				{
					// Send the data to the renderer
					hr = ProcessRenderer( dwPacketIndex );
					if( FAILED( hr ))
					{
						 return hr;
					}
				}
			}
		}
	}
    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: FindFreePacket()
// Desc: Finds a render packet available for processing.
//-----------------------------------------------------------------------------
bool CWMAFileStream::FindFreePacket( uint32* pdwPacketIndex )
{
    for( uint32 dwPacketIndex = 0; dwPacketIndex < WMASTRM_PACKET_COUNT; ++dwPacketIndex )
    {
		if(( m_adwPacketStatus[dwPacketIndex] != XMEDIAPACKET_STATUS_PENDING ) &&
		   (( m_adwPacketStatus[dwPacketIndex] & XMEDIAPACKET_STATUS_AWAITING_RENDER ) != XMEDIAPACKET_STATUS_AWAITING_RENDER ))
        {
            if( pdwPacketIndex )
			{
				*pdwPacketIndex = dwPacketIndex;

				// Mark this packet as awaiting rendering.
				uint32 timestamp = (uint32)Tmr::GetVblanks();
				m_adwPacketStatus[dwPacketIndex] = XMEDIAPACKET_STATUS_AWAITING_RENDER | ( timestamp & 0xFFFFFFUL );
			}
            return true;
        }
    }
    return false;
}



//-----------------------------------------------------------------------------
// Name: FindFreePacket()
// Desc: Finds a render packet available for rendering.
//-----------------------------------------------------------------------------
bool CWMAFileStream::FindRenderablePacket( uint32* pdwPacketIndex )
{
	// Found nothing yet.
	uint32 lowest_timestamp	= 0xFFFFFFUL;
	uint32 lowest_index		= WMASTRM_PACKET_COUNT;

	for( uint32 dwPacketIndex = 0; dwPacketIndex < WMASTRM_PACKET_COUNT; ++dwPacketIndex )
    {
		if(( m_adwPacketStatus[dwPacketIndex] & XMEDIAPACKET_STATUS_AWAITING_RENDER ) == XMEDIAPACKET_STATUS_AWAITING_RENDER )
        {
			uint32 timestamp = m_adwPacketStatus[dwPacketIndex] & 0xFFFFFFUL;
			if( timestamp < lowest_timestamp )
			{
				lowest_timestamp	= timestamp;
				lowest_index		= dwPacketIndex;
			}
		}
    }

	// If we found a packet, return its index.
	if( lowest_index < WMASTRM_PACKET_COUNT )
	{
		if( pdwPacketIndex )
		{
			*pdwPacketIndex = lowest_index;
		}
		return true;
	}

	// No packets awaiting rendering.
	return false;
}



//-----------------------------------------------------------------------------
// Name: ProcessSource()
// Desc: Reads data from the source filter.
//-----------------------------------------------------------------------------
HRESULT CWMAFileStream::ProcessSource( uint32 dwPacketIndex )
{
    uint32        dwTotalSourceUsed   = 0;
    uint32        dwSourceUsed;
    XMEDIAPACKET xmp;
    HRESULT      hr;
    
	if( m_pvSourceBuffer == NULL )
	{
		CreateSourceBuffer();
	}
    
	// We're going to read a full packet's worth of data into the source buffer.
    ZeroMemory( &xmp, sizeof( xmp ));
    xmp.pvBuffer         = (BYTE*)m_pvSourceBuffer + (dwPacketIndex * WMASTRM_SOURCE_PACKET_BYTES);
    xmp.dwMaxSize        = WMASTRM_SOURCE_PACKET_BYTES;
    xmp.pdwCompletedSize = &dwSourceUsed;

	// Read from the source.
	hr = m_pSourceFilter->Process( NULL, &xmp );
	if( FAILED( hr ))
	{
		return hr;
	}

	// Add the amount read to the total
	dwTotalSourceUsed += dwSourceUsed;

	// If we read less than the amount requested, it's because we hit the end of the file.
    if( dwSourceUsed < xmp.dwMaxSize )
	{
		// Set completion flag.
		m_Completed = true;

		// Zero remaining part of packet.
		xmp.pvBuffer  = (BYTE*)xmp.pvBuffer + dwSourceUsed;
		xmp.dwMaxSize = xmp.dwMaxSize - dwSourceUsed;
		ZeroMemory( xmp.pvBuffer, xmp.dwMaxSize );
	}

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: ProcessRenderer()
// Desc: Sends data to the renderer.
//-----------------------------------------------------------------------------
HRESULT CWMAFileStream::ProcessRenderer( uint32 dwPacketIndex )
{
    XMEDIAPACKET xmp;
    HRESULT      hr;

	Dbg_Assert( m_pvSourceBuffer != NULL );

	// There's a full packet's worth of data ready for us to send to the renderer.  We want to track the status
	// of this packet since the render filter is asychronous and we need to know when the packet is completed.
    ZeroMemory( &xmp, sizeof( xmp ));
    xmp.pvBuffer  = (BYTE*)m_pvSourceBuffer + ( dwPacketIndex * WMASTRM_SOURCE_PACKET_BYTES );
    xmp.dwMaxSize = WMASTRM_SOURCE_PACKET_BYTES;
    xmp.pdwStatus = &m_adwPacketStatus[dwPacketIndex];

	if( m_Completed )
	{
		// Store index of last packet, since we will need to test the status of this for proper completion test.
		m_LastPacket = dwPacketIndex;
	}

    hr = m_pRenderFilter->Process( &xmp, NULL );

	if( m_Completed )
	{
		// Tell the renderer not to expect any more data.
		m_pRenderFilter->Discontinuity();
	}
	
	if( FAILED( hr ))
	{
		return hr;
	}
	
	return S_OK;
}



//-----------------------------------------------------------------------------
// Name: Pause
// Desc: Pauses and resumes stream playback
//-----------------------------------------------------------------------------
void CWMAFileStream::Pause( uint32 dwPause )
{
	m_Paused = ( dwPause > 0 );

	// Possible that the render filter hasn't been created yet.
	if( m_pRenderFilter )
	{
		m_pRenderFilter->Pause(( dwPause > 0 ) ? DSSTREAMPAUSE_PAUSE : DSSTREAMPAUSE_RESUME );
	}
}



//-----------------------------------------------------------------------------
// Name: SetVolume
// Desc: 
//-----------------------------------------------------------------------------
void CWMAFileStream::SetVolume( float volume )
{
	if( m_pRenderFilter )
	{
		int i_volume		= DSBVOLUME_EFFECTIVE_MIN;
		int i_volume_rear	= DSBVOLUME_EFFECTIVE_MIN;
		if( volume > 0.0f )
		{
			// Figure base volume.
			float attenuation	= 20.0f * log10f( volume * 0.01f );
			i_volume			= DSBVOLUME_MAX + (int)( attenuation * 100.0f );
			if( i_volume < DSBVOLUME_EFFECTIVE_MIN )
				i_volume = DSBVOLUME_EFFECTIVE_MIN;
			else if( i_volume > DSBVOLUME_MAX )
				i_volume = DSBVOLUME_MAX;

			// Also figure half volume, in case we are routing to the back speakers.
			attenuation			= 20.0f * log10f( volume * 0.5f * 0.01f );
			i_volume_rear		= DSBVOLUME_MAX + (int)( attenuation * 100.0f );
			if( i_volume_rear < DSBVOLUME_EFFECTIVE_MIN )
				i_volume_rear = DSBVOLUME_EFFECTIVE_MIN;
			else if( i_volume_rear > DSBVOLUME_MAX )
				i_volume_rear = DSBVOLUME_MAX;
		}
		
		// Set individual mixbins for panning.
		DSMIXBINS			dsmixbins;
		DSMIXBINVOLUMEPAIR	dsmbvp[DSMIXBIN_ASSIGNMENT_MAX];

		dsmixbins.dwMixBinCount			= 0;
		dsmixbins.lpMixBinVolumePairs	= dsmbvp;

		if( i_volume > DSBVOLUME_EFFECTIVE_MIN )
		{
			// Set the volume up depending on how the initial mixbins were set up.
			int mbbf = 0;
			for( uint32 mb = 0; mb < m_NumMixbins; ++mb )
			{
				while(( m_Mixbins & ( 1 << mbbf )) == 0 )
				{
					++mbbf;
					Dbg_Assert( mbbf < 32 );
				}
				dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= mbbf;

				// For rear speakers (or mixbins that route to the rear), use half volume.
				if(( mbbf == DSMIXBIN_FXSEND_5 ) || ( mbbf == DSMIXBIN_FXSEND_6 ) || ( mbbf == DSMIXBIN_BACK_LEFT ) || ( mbbf == DSMIXBIN_BACK_RIGHT ))
				{
					dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volume_rear;
				}
				else
				{
					dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volume;
				}
				++dsmixbins.dwMixBinCount;
				++mbbf;
			}
		}

		// Set all speaker volumes.
		m_pRenderFilter->SetMixBinVolumes( &dsmixbins );

		// Set overall buffer volume.
		if( dsmixbins.dwMixBinCount > 0 )
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MAX );
		}
		else
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MIN );
		}
	}
	else
	{
		m_SetDeferredVolume = true;
		m_DeferredVolume[0]	= volume;
	}
}



//-----------------------------------------------------------------------------
// Name: SetVolume
// Desc: 
//-----------------------------------------------------------------------------
void CWMAFileStream::SetVolume( float volL, float volR )
{
	if( m_pRenderFilter )
	{
		// This array will hold individual volumes for the five speakers.
		// In order, they are: front left, center, front right, rear right, rear left.
		float	volumes[5];
		int		i_volumes[5], max_i_volume;
		memset( volumes, 0, sizeof( float ) * 5 );
		
		if(( volL == 0.0f ) && ( volR == 0.0f ))
		{
			// Pointless doing any more work.
		}
		else
		{
			// Get the length of the vector here which will be used to multiply out the normalised speaker volumes.
			Mth::Vector test( fabsf( volL ), fabsf( volR ), 0.0f, 0.0f );
			float amplitude = test.Length();

			// Look just at the normalized right component to figure the sound angle from Matt's calculations.
			test.Normalize();

			float angle;
			angle	= asinf( test[Y] );
			angle	= ( angle * 2.0f ) - ( Mth::PI * 0.5f );
			angle	= ( volL < 0.0f ) ? ( Mth::PI - angle ) : angle;
		
			// Now figure volumes based on speaker coverage.
			angle	= Mth::RadToDeg( angle );
		
			Spt::SingletonPtr< Sfx::CSfxManager > sfx_manager;
			sfx_manager->Get5ChannelMultipliers( angle, &volumes[0] );

			// Now readjust the relative values...
			for( int v = 0; v < 5; ++v )
			{
				// Scale back up to original amplitude.
				volumes[v] *= amplitude;

				if( volumes[v] > 100.0f )
					volumes[v] = 100.0f;
			}
		}
		
		// Now figure the attenuation of the sound. To convert to a decibel value, figure the ratio of requested
		// volume versus max volume, then calculate the log10 and multiply by (10 * 2). (The 2 is because sound
		// power varies as square of pressure, and squaring doubles the log value).
		max_i_volume = DSBVOLUME_EFFECTIVE_MIN;
		for( int v = 0; v < 5; ++v )
		{
			if( volumes[v] > 0.0f )
			{
				float attenuation	= 20.0f * log10f( volumes[v] * 0.01f );
				i_volumes[v]		= DSBVOLUME_MAX + (int)( attenuation * 100.0f );
				if( i_volumes[v] < DSBVOLUME_EFFECTIVE_MIN )
					i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
				else if( i_volumes[v] > DSBVOLUME_MAX )
					i_volumes[v] = DSBVOLUME_MAX;

				if( i_volumes[v] > max_i_volume )
					max_i_volume = i_volumes[v];
			}
			else
			{
				i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
			}
		}
		
		// Set individual mixbins for panning.
		DSMIXBINS			dsmixbins;
		DSMIXBINVOLUMEPAIR	dsmbvp[DSMIXBIN_ASSIGNMENT_MAX];

		dsmixbins.dwMixBinCount			= 0;
		dsmixbins.lpMixBinVolumePairs	= dsmbvp;

		if( i_volumes[0] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_FRONT_LEFT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[0];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[1] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_FRONT_RIGHT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[1];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[2] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_BACK_LEFT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[2];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[3] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_BACK_RIGHT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[3];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[4] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_FRONT_CENTER;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[4];
            dsmixbins.dwMixBinCount++;
		}
		if( dsmixbins.dwMixBinCount > 0 )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_I3DL2;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= DSBVOLUME_MAX;
            dsmixbins.dwMixBinCount++;
		}

		// Set all speaker volumes.
		m_pRenderFilter->SetMixBinVolumes( &dsmixbins );

		// Set overall buffer volume.
		if( dsmixbins.dwMixBinCount > 0 )
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MAX );
		}
		else
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MIN );
		}
	}
	else
	{
		m_SetDeferredVolumeLR	= true;
		m_DeferredVolume[0]		= volL;
		m_DeferredVolume[1]		= volR;
	}
}



//-----------------------------------------------------------------------------
// Name: SetVolume
// Desc: 
//-----------------------------------------------------------------------------
void CWMAFileStream::SetVolume( float v0, float v1, float v2, float v3, float v4 )
{
	if( m_pRenderFilter )
	{
		float volumes[5];
		volumes[0] = ( v0 > 100.0f ) ? 100.0f : v0;
		volumes[1] = ( v1 > 100.0f ) ? 100.0f : v1;
		volumes[2] = ( v2 > 100.0f ) ? 100.0f : v2;
		volumes[3] = ( v3 > 100.0f ) ? 100.0f : v3;
		volumes[4] = ( v4 > 100.0f ) ? 100.0f : v4;

		int i_volumes[5], max_i_volume;

		// Now figure the attenuation of the sound. To convert to a decibel value, figure the ratio of requested
		// volume versus max volume, then calculate the log10 and multiply by (10 * 2). (The 2 is because sound
		// power varies as square of pressure, and squaring doubles the log value).
		max_i_volume = DSBVOLUME_EFFECTIVE_MIN;
		for( int v = 0; v < 5; ++v )
		{
			if( volumes[v] > 0.0f )
			{
				float attenuation	= 20.0f * log10f( volumes[v] * 0.01f );
				i_volumes[v]		= DSBVOLUME_MAX + (int)( attenuation * 100.0f );
				if( i_volumes[v] < DSBVOLUME_EFFECTIVE_MIN )
					i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
				else if( i_volumes[v] > DSBVOLUME_MAX )
					i_volumes[v] = DSBVOLUME_MAX;

				if( i_volumes[v] > max_i_volume )
					max_i_volume = i_volumes[v];
			}
			else
			{
				i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
			}
		}
		
		// Set individual mixbins for panning.
		DSMIXBINS			dsmixbins;
		DSMIXBINVOLUMEPAIR	dsmbvp[DSMIXBIN_ASSIGNMENT_MAX];

		dsmixbins.dwMixBinCount			= 0;
		dsmixbins.lpMixBinVolumePairs	= dsmbvp;

		if( i_volumes[0] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_FRONT_LEFT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[0];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[1] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_FRONT_RIGHT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[1];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[2] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_BACK_LEFT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[2];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[3] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_BACK_RIGHT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[3];
            dsmixbins.dwMixBinCount++;
		}
		if( i_volumes[4] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_FRONT_CENTER;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[4];
            dsmixbins.dwMixBinCount++;
		}
		if( dsmixbins.dwMixBinCount > 0 )
		{
			dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_I3DL2;
			dsmbvp[dsmixbins.dwMixBinCount].lVolume		= DSBVOLUME_MAX;
			dsmixbins.dwMixBinCount++;
		}

		// Set all speaker volumes.
		m_pRenderFilter->SetMixBinVolumes( &dsmixbins );

		// Set overall buffer volume.
		if( dsmixbins.dwMixBinCount > 0 )
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MAX );
		}
		else
		{
			m_pRenderFilter->SetVolume( DSBVOLUME_MIN );
		}
	}
	else
	{
		m_SetDeferredVolume5Channel	= true;
		m_DeferredVolume[0]			= v0;
		m_DeferredVolume[1]			= v1;
		m_DeferredVolume[2]			= v2;
		m_DeferredVolume[3]			= v3;
		m_DeferredVolume[4]			= v4;
	}
}



//-----------------------------------------------------------------------------
// Name: SetDeferredVolume
// Desc: 
//-----------------------------------------------------------------------------
void CWMAFileStream::SetDeferredVolume( void )
{
	if( m_SetDeferredVolume )
	{
		m_SetDeferredVolume = false;
		SetVolume( m_DeferredVolume[0] );
	}

	if( m_SetDeferredVolumeLR )
	{
		m_SetDeferredVolumeLR = false;
		SetVolume( m_DeferredVolume[0], m_DeferredVolume[1] );
	}

	if( m_SetDeferredVolume5Channel )
	{
		m_SetDeferredVolume5Channel = false;
		SetVolume( m_DeferredVolume[0], m_DeferredVolume[1], m_DeferredVolume[2], m_DeferredVolume[3], m_DeferredVolume[4] );
	}
}



//-----------------------------------------------------------------------------
// Name: IsSafeToDelete
// Desc: 
//-----------------------------------------------------------------------------
bool CWMAFileStream::IsSafeToDelete( void )
{
	return true;
}



//-----------------------------------------------------------------------------
// Name: WMAFileStreamThreadProc
//-----------------------------------------------------------------------------
uint32 WINAPI WMAFileStreamThreadProc( LPVOID lpParameter )
{
	CWMAFileStream*	p_filestream = (CWMAFileStream*)lpParameter;

	Dbg_Assert( p_filestream->m_DecoderCreation == 0 );

	// Create the memory-based XMO.
	HRESULT hr = WmaCreateInMemoryDecoder(	WMAXMediaObjectDataCallback,						// Callback pointer.
											p_filestream,										// Callback context pointer.
											3,													// Yield rate during decoding.
											&p_filestream->m_wfxSourceFormat,					// Source format description.
											(LPXMEDIAOBJECT*)&p_filestream->m_pSourceFilter );	// Pointer to media object.
	if( FAILED( hr ))
	{
		// Signal a failed creation.
		p_filestream->m_DecoderCreation	= 2;
		p_filestream->m_pSourceFilter	= NULL;
	}
	else
	{
		p_filestream->m_DecoderCreation	= 1;
	}

	// Terminate the thread.
	return 0;
}




} // namespace PCM


================================================
FILE: Code/Gel/Music/Xbox/p_wmafilestream.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Game Engine												**
**																			**
**	File name:		p_wmafilesteam.h										**
**																			**
**	Created: 		01/27/2003	-	dc										**
**																			**
*****************************************************************************/

#ifndef __P_WMAFILESTREAM_H
#define __P_WMAFILESTREAM_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// Define the maximum amount of packets we will ever submit to the renderer.
#define WMASTRM_PACKET_COUNT		8

namespace Pcm
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/******************************************************************/
/*																  */
/* WMA file streaming object, designed for fully asynchronous	  */
/* streaming from disc.											  */
/* Note that WMA decompression does incur a CPU overhead.		  */
/*																  */
/******************************************************************/
class CWMAFileStream : public Spt::Class
{
	private:

	HANDLE				m_hFile;
	HANDLE				m_hThread;
	OVERLAPPED*			m_pOverlapped;							// OVERLAPPED structure for asynchronous file access.
	bool				m_FirstRead;							// Flag to indicate first request for more data to be streamed from disc.
	bool				m_ReadComplete;
	int					m_SuccessiveReads;						// Counts how many read operations performed in total.
	bool				m_bUseWAD;
	bool				m_bUse3D;								// Sets whether 2D or 3D mixbins are set up.
	uint32				m_dwWADOffset;	
	uint32				m_dwWADLength;

	public:

    IDirectSoundStream* m_pRenderFilter;                         // Render (DirectSoundStream) filter
    LPVOID              m_pvSourceBuffer;                        // Source filter data buffer
    LPVOID              m_pvRenderBuffer;                        // Render filter data buffer
    uint32				m_adwPacketStatus[WMASTRM_PACKET_COUNT]; // Packet status array
    uint32				m_dwStartingDataOffset;                  // Offset into wma file where data begins.
    uint32				m_dwPercentCompleted;					 // Percent completed
	bool				m_Paused;
	bool				m_Completed;							// For single-shot, indicates loading of final packet to renderer is completed
    uint32				m_LastPacket;							// Last packet array index
	bool				m_SetDeferredVolume;
	bool				m_SetDeferredVolumeLR;
	bool				m_SetDeferredVolume5Channel;
	bool				m_bOkayToPlay;							// Used when exact syncing is required - will load buffers but won't start decompression until this flag is true.
	float				m_DeferredVolume[5];
	uint32				m_Mixbins;								// Bitfield indicating which mixbins are active for this buffer.
	uint32				m_NumMixbins;

    // Packet processing
    bool				FindFreePacket( uint32* pdwPacketIndex );
	bool				FindRenderablePacket( uint32* pdwPacketIndex );
    HRESULT				ProcessSource( uint32 dwPacketIndex );
    HRESULT				ProcessRenderer( uint32 dwPacketIndex );

	WAVEFORMATEX		m_wfxSourceFormat;
    XFileMediaObject*   m_pSourceFilter;							// Source (wave file) filter
	LPVOID				m_pFileBuffer;								// Buffer for async read of raw data.
	int					m_FileBytesRead;							// Total number of raw bytes read from disc.
	int					m_FileBytesProcessed;						// Total number of raw bytes processed by XMO.
	int					m_DecoderCreation;
	bool				m_AwaitingDeletion;

    // Processing
    HRESULT				Process();

    // Initialization
    HRESULT				Initialize( HANDLE h_file, unsigned int offset, unsigned int length, void* fileBuffer );
    HRESULT				Initialize( HANDLE h_file, void* fileBuffer );
	HRESULT				PostInitialize( void );
	void				CreateSourceBuffer( void );
    
    // Play control
    void				Pause( uint32 dwPause );
	void				SetVolume( float volume );
	void				SetVolume( float volumeL, float volumeR );
	void				SetVolume( float v0, float v1, float v2, float v3, float v4 );
	void				SetDeferredVolume( void );

	// Query
	IDirectSoundStream*	GetSoundStream( void )					{ return m_pRenderFilter; }
	bool				IsCompleted( void )						{ return ( m_Completed && ( XMEDIAPACKET_STATUS_PENDING != m_adwPacketStatus[m_LastPacket] )); }
	bool				IsSafeToDelete( void );
	bool				PreLoadDone( void );

	// Asynchronous stuff.
	void				AsyncRead( void );


    CWMAFileStream( bool use_3d = false );
    ~CWMAFileStream();
};



/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/
	
	
/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Pcm

#endif	// __P_WMAFILESTREAM_H






================================================
FILE: Code/Gel/Music/music.cpp
================================================
/*  
	Music.cpp -- highest level
	p_music.cpp -- platform specific
	pcm*.c --  the code that runs on the IOP.
	
	This module should actually be renamed now, as it isn't just for
	music any more!

	This module should be called Streaming.cpp.
	
	This module handles streaming music, and soundfx.
	
	The stream data is read into IOP memory from the DVD, into a
	double buffer in IOP memory, about once every three seconds.
	
	The IOP chip has a thread that is running on a timer interrupt (see the pcm
	code, which compiles as an IOP module EzPcm.irx).  60 times per second,
	this thread updates small areas of memory on the SPU2 chip -- double
	buffers for each stream that need updating approximately every second
	and a half (or longer if the pitch has been turned down lower).

	These voices work just like the other voices for soundfx, except that they
	loop over a small area that is updated with the sound data streaming off
	the DVD.  You can change the pitch and volume of these voices on the fly
	just like soundFX, so streaming sounds can be adjusted positionally over time.

	Oh, yeah, just one more thing:  yo mamma, yo pappa, yo greasy bald gradma!
*/

#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 

#include 

#ifdef __PLAT_NGPS__
#include 
#include 
#elif defined( __PLAT_XBOX__ )
#include 
#include 
#elif defined( __PLAT_NGC__ )
#include 
#include 
#endif
#include 
#include 
#include 

#define TEST_FROM_CD 0
#define WAIT_AFTER_STOP_STREAM		0		// Set to 1 if we need to wait for stream to clear after stopping a stream

namespace Pcm
{

static bool music_hed_there = true;		// assume music and streams are there to start with, so we can load the hed
static bool streams_hed_there = true;	// if hed is not there, then these get set to false
										// which will disable all music and/or streams.

bool NoMusicPlease()
{
	#if NO_MUSIC_PLEASE
	return true;
	#else
	// Cannot have music if building for proview, because there is not enough IOP memory
	// (ProView uses up some IOP memory with it's monitor)
	if (Config::GetHardware()==Config::HARDWARE_PS2_PROVIEW)
	{
		return true;
	}
	
	return !music_hed_there;	
	#endif
}

bool StreamsDisabled()
{
	#if DISABLE_STREAMS
	return true;
	#else
	// Cannot have music if building for proview, because there is not enough IOP memory
	// (ProView uses up some IOP memory with it's monitor)
	if (Config::GetHardware()==Config::HARDWARE_PS2_PROVIEW)
	{
		return true;
	}
	
	return !streams_hed_there;	
	#endif
}

// prototypes...
bool TrackIsPlaying( int whichList, int whichTrack );

static int gLastStreamChannelPlayed = 0;

CurrentStreamInfo gCurrentStreamInfo[ NUM_STREAMS ];

TrackTitle PermTrackTitle[ MAX_NUM_TRACKS ];

// Static globals
static int          current_music_track = 0;
static TrackList	gTrackLists[ NUM_TRACKLISTS ];
static int			gCurrentTrackList = TRACKLIST_PERM;  // start out playing songs...
static char			gTrackName[ MAX_TRACKNAME_STRING_LENGTH ];
static float		gMusicVolume = DEFAULT_MUSIC_VOLUME;
static float		gMusicStreamVolume = DEFAULT_MUSIC_STREAM_VOLUME;
static bool 		gMusicInRandomMode = true;
static bool			gMusicLooping = false;
static int			sCounter = 1;
static bool			gPcmInitialized = false;
static int			gNumStreams = 0;
static Obj::CStreamComponent	*gpStreamingObj[ NUM_STREAMS ]; // should have as many of these as streams...

// Music stream data

static EMusicStreamType	gMusicStreamType = MUSIC_STREAM_TYPE_NONE;
#if WAIT_AFTER_STOP_STREAM
static uint32 			gMusicStreamChecksum;	// Checksum of music stream
static float 			gMusicStreamVolume;		// Volume of music stream
static bool 			gMusicStreamWaitingToStart;	// true if we are still waiting for the channel to become free
#endif // WAIT_AFTER_STOP_STREAM

// Limit is actually 500 ...
#define			MAX_USER_SONGS					600

#ifdef __PLAT_XBOX__
static bool		s_xbox_play_user_soundtracks	= false;
static int		s_xbox_user_soundtrack			= 0;
static uint32	s_xbox_user_soundtrack_song		= 0;
static bool		s_xbox_user_soundtrack_random	= false;
static uint32	s_xbox_random_index				= 0;

static int		sp_xbox_randomized_songs[MAX_USER_SONGS];
#endif

static bool     shuffle_random_songs=true;
static int      num_random_songs = 1;
static int      random_song_index = 0;
static int		sp_random_song_order[MAX_USER_SONGS];


// Stream ID functions
bool IDAvailable(uint32 id)
{
	for ( int i = 0; i < NUM_STREAMS; i++ )
	{
		if ( (gCurrentStreamInfo[ i ].uniqueID == id) && (PCMAudio_GetStreamStatus(gCurrentStreamInfo[ i ].voice) != PCM_STATUS_FREE))
		{
			return false;
		}
	}

	return true;
}

int GetChannelFromID(uint32 id, bool assert_if_duplicate = true)
{
	int i;

	for ( i = 0; i < NUM_STREAMS; i++ )
	{
		if ( (gCurrentStreamInfo[ i ].uniqueID == id) && (PCMAudio_GetStreamStatus(gCurrentStreamInfo[ i ].voice) != PCM_STATUS_FREE))
		{
			return i;
		}
	}

	for ( i = 0; i < NUM_STREAMS; i++ )
	{
		if ( (gCurrentStreamInfo[ i ].controlID == id) && (PCMAudio_GetStreamStatus(gCurrentStreamInfo[ i ].voice) != PCM_STATUS_FREE))
		{
			Dbg_MsgAssert(!assert_if_duplicate, ("Trying to control duplicate stream %s by checksum", Script::FindChecksumName(id)));
			return i;
		}
	}

	return -1;
}

uint32 GenerateUniqueID(uint32 id)
{
	// Keep incrementing ID until one works.
	while (!IDAvailable(id))
		id++;

	return id;
}

void StopMusic( void )
{
	if (NoMusicPlease()) return;
	
	// the counter has to reach SONG_UPDATE_INTERVAL before a new song is played...
	// so resetting the counter keeps a new song from playing right away.
	sCounter = 1;
	gMusicStreamType = MUSIC_STREAM_TYPE_NONE;		// In case we were in this mode
	PCMAudio_StopMusic( true );
}

// If a channel is specified, stop a specific stream (might be a streaming soundFX on an object that's getting
// destroyed, for example).
// If channel parameter is -1, stop all non-music streams!
void StopStreams( int channel )
{
	Replay::WriteStopStream(channel);
	if (StreamsDisabled()) return;
	
	Dbg_MsgAssert( ( channel >= -1 ) && ( channel < NUM_STREAMS ), ( "Invalid stream channel %d", channel ) );
	if ( channel == -1 )
	{
		PCMAudio_StopStreams( );
	}
	else
	{
		PCMAudio_StopStream( channel );
	}
}

// if uniqueID isn't specified, stop whatever stream is playing!
void StopStreamFromID( uint32 streamID )
{
	if (StreamsDisabled()) return;
	
	int channel = GetChannelFromID(streamID);

	if (channel >= 0)
	{
		PCMAudio_StopStream( channel, true );
		streamID = gCurrentStreamInfo[ channel ].uniqueID;		// change over to uniqueID
	}

	for ( int i = 0; i < NUM_STREAMS; i++ )
	{
		if ( gpStreamingObj[ i ] )
		{
			for ( int j = 0; j < MAX_STREAMS_PER_OBJECT; j++ )
			{
				if ( gpStreamingObj[ i ]->mStreamingID[ j ] == streamID )
				{
					gpStreamingObj[ i ]->mStreamingID[ j ] = 0;
					gpStreamingObj[ i ] = NULL;
					return;
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool SetStreamVolumeFromID( uint32 streamID, Sfx::sVolume *p_volume )
{
	if (StreamsDisabled()) return true;
	
	int channel = GetChannelFromID(streamID);

	if (channel >= 0)
	{
#		ifdef __PLAT_XBOX__
		PCMAudio_SetStreamVolume( p_volume, channel );
#		else
		PCMAudio_SetStreamVolume( p_volume->GetChannelVolume( 0 ), p_volume->GetChannelVolume( 1 ), channel );
#		endif

		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool SetStreamPitchFromID( uint32 streamID, float pitch )
{
	if (StreamsDisabled()) return true;
	
	int channel = GetChannelFromID(streamID);

	if (channel >= 0)
	{
		PCMAudio_SetStreamPitch( pitch, channel );

		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#ifdef __PLAT_XBOX__
static void sGenerateRandomSongOrder( void )
{
	int num_songs=Pcm::GetSoundtrackNumSongs( s_xbox_user_soundtrack );

	if( num_songs == 0 )
	{
		// Nothing to do.
		return;
	}
		
	// Limit to MAX_USER_SONGS to prevent any buffer overwriting.
	if (num_songs>MAX_USER_SONGS)
	{
		num_songs=MAX_USER_SONGS;
	}	

	// Remember the last song in the previous order, so we can make sure that
	// the first song in the new order is different from it.
	// Note: OK if the last song was just an uninitialised random value, won't cause any problems.
	int last_song=sp_xbox_randomized_songs[num_songs-1];
	
	// Initialise the order to be 0,1,2,3 ... etc
	for (int i=0; i1)
		{
			// Make b not able to equal 0, so that we don't swap the first with itself.
			b=1+Mth::Rnd(num_songs-1);
		}
		else
		{
			// Unless there is only one song, in which case just swap it with itself. No point but what the heck.
			b=0;
		}
		
		// Do the swap.
		int temp=sp_xbox_randomized_songs[a];
		sp_xbox_randomized_songs[a]=sp_xbox_randomized_songs[b];
		sp_xbox_randomized_songs[b]=temp;
	}
}
#endif // __PLAT_XBOX__



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void UseUserSoundtrack( int soundtrack )
{
#	ifdef __PLAT_XBOX__

	Dbg_MsgAssert(soundtrack>=0 && soundtrackAddComponent(Script::GenerateCRC("UserSoundtrackIndex"),ESYMBOLTYPE_INTEGER,s_xbox_user_soundtrack);

		char p_buf[100];
		const WCHAR* p_soundtrack_name_wide=Pcm::GetSoundtrackName(s_xbox_user_soundtrack);
		if (p_soundtrack_name_wide)
		{
			wsprintfA( p_buf, "%ls", p_soundtrack_name_wide);
		}	
		else
		{
			p_buf[0]=0;
		}	
				
		pStuff->AddComponent(Script::GenerateCRC("UserSoundtrackName"),ESYMBOLTYPE_STRING,p_buf);
	}	
#	endif // __PLAT_XBOX__
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void GetSoundtrackFromStructure( Script::CStruct *pStuff)
{
#	ifdef __PLAT_XBOX__
	Dbg_MsgAssert(pStuff,("NULL pStuff"));
	
	int user_soundtrack_index=0;
	if (pStuff->GetInteger("UserSoundtrackIndex",&user_soundtrack_index))
	{
		// A user soundtrack index is specified, but maybe the name does not match what is on this
		// machine, ie, they could have put the mem card into a different xbox ...
		
		// Get the name of this soundtrack on this machine
		char p_buf[100];
		const WCHAR* p_soundtrack_name_wide=Pcm::GetSoundtrackName(user_soundtrack_index);
		if (p_soundtrack_name_wide)
		{
			wsprintfA( p_buf, "%ls", p_soundtrack_name_wide);
		}	
		else
		{
			p_buf[0]=0;
		}	
	
		// Get the name as stored on the mem card
		const char *p_stored_name="";
		pStuff->GetText("UserSoundtrackName",&p_stored_name);
		
		if (strcmp(p_stored_name,p_buf)==0)
		{
			// They match! So use that soundtrack.
			UseUserSoundtrack(user_soundtrack_index);
			return;
		}	
	}	
	
	// Oh well, use the standard soundtrack instead.
	UseStandardSoundtrack();
#	endif	// __PLAT_XBOX__
}



bool _PlayMusicTrack( const char *filename, float volume )
{
	if (NoMusicPlease()) return false;
	
	Dbg_MsgAssert( gPcmInitialized,( "Calling playtrack %s, when PCM Audio not initialized.", filename ));
	Dbg_MsgAssert( strlen( filename ) < 200,( "Filename too long" ));

#	ifdef __PLAT_XBOX__
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if( s_xbox_play_user_soundtracks && !skate_mod->GetGameMode()->IsFrontEnd())
	{
		if( Pcm::GetSoundtrackNumSongs( s_xbox_user_soundtrack ) == 0 )
		{
			return false;
		}
			
		if( !PCMAudio_PlaySoundtrackMusicTrack( s_xbox_user_soundtrack, s_xbox_user_soundtrack_song ))
		{
			return false;
		}
		else
		{
			if( s_xbox_user_soundtrack_random )
			{
				++s_xbox_random_index;
				if( s_xbox_random_index >= Pcm::GetSoundtrackNumSongs( s_xbox_user_soundtrack ))
				{
					sGenerateRandomSongOrder();
					s_xbox_random_index = 0;
				}	
				s_xbox_user_soundtrack_song=sp_xbox_randomized_songs[s_xbox_random_index];
			}	
			else
			{
				++s_xbox_user_soundtrack_song;
				if( s_xbox_user_soundtrack_song >= Pcm::GetSoundtrackNumSongs( s_xbox_user_soundtrack ))
				{
					s_xbox_user_soundtrack_song = 0;
				}
			}	
		}		
	}
	else
	{
		if( !PCMAudio_PlayMusicTrack( filename ))
		{
			return false;
		}
	}		
#	else
	if( !PCMAudio_PlayMusicTrack( filename ))
	{
		return false;
	}
#	endif // __PLAT_XBOX__

//	volume = volume * Config::GetMasterVolume()/100.0f;
//	if (volume <0.1f)
//	{
//		volume = 0.0f;
//	}
	PCMAudio_SetMusicVolume( volume );
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool _PlayMusicStream( uint32 checksum, float volume )
{
	if (NoMusicPlease()) return false;
	
	Dbg_MsgAssert( gPcmInitialized,( "Calling playtrack %s, when PCM Audio not initialized.", Script::FindChecksumName(checksum) ));

#ifdef __PLAT_NGPS__
	if( !PCMAudio_PlayMusicStream( checksum ))
	{
		Dbg_MsgAssert(0, ( "Failed playing track %s", Script::FindChecksumName(checksum) ) );
		Dbg_Message( "Failed playing track %s", Script::FindChecksumName(checksum) );
		return false;
	}
#else
	Dbg_MsgAssert(0, ("PCMAudio_PlayMusicStream() not implemented on this platform."));
#endif // __PLAT_NGPS__

	PCMAudio_SetMusicVolume( volume );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// returns -1 on failure, otherwise the index into which voice played the sound.
int _PlayStream( uint32 checksum, Sfx::sVolume *p_volume, float pitch, int priority, uint32 controlID, bool preload_only )
{
	if (StreamsDisabled()) return -1;
	
#	ifdef __PLAT_XBOX__
	Dbg_MsgAssert( gPcmInitialized,( "Calling _PlayStream(), when PCM Audio not initialized." ));
#	endif // __PLAT_XBOX__

	// Initialize lowest to a valid entry
	int lowest_priority = gCurrentStreamInfo[ 0 ].priority;
	int lowest_priority_channel = 0;
	uint32 lowest_priority_start_frame = gCurrentStreamInfo[ 0 ].start_frame;

	bool success;

	for ( int i = 0; i < NUM_STREAMS; i++ )
	{
		if ( PCMAudio_GetStreamStatus( i ) == PCM_STATUS_FREE )
		{
			if (preload_only)
			{
				success = PCMAudio_PreLoadStream( checksum, i );
			}
			else
			{
//				success = PCMAudio_PlayStream( checksum, i, volumeL, volumeR, pitch );
#				ifdef __PLAT_XBOX__
				success = PCMAudio_PlayStream( checksum, i, p_volume, pitch );
#				else
				success = PCMAudio_PlayStream( checksum, i, p_volume->GetChannelVolume( 0 ), p_volume->GetChannelVolume( 1 ), pitch );
#				endif
			}

			if ( !success )
			{
				return ( -1 );
			}

	   		gCurrentStreamInfo[ i ].controlID = controlID;
	   		gCurrentStreamInfo[ i ].uniqueID = GenerateUniqueID(controlID);
	   		gCurrentStreamInfo[ i ].voice = i;
	   		gCurrentStreamInfo[ i ].priority = priority;
	   		gCurrentStreamInfo[ i ].start_frame = (uint32)Tmr::GetRenderFrame();
			gCurrentStreamInfo[ i ].p_frame_amp = CStreamFrameAmpManager::sGetFrameAmp(checksum);
			return ( i );
		}
		else if ( gCurrentStreamInfo[ i ].priority <= lowest_priority )
		{
			// If it is equal priority, we want to find the oldest one
			if ((gCurrentStreamInfo[ i ].priority == lowest_priority) &&
				(gCurrentStreamInfo[ i ].start_frame >= lowest_priority_start_frame))
			{
				continue;
			}

			lowest_priority = gCurrentStreamInfo[ i ].priority;
			lowest_priority_channel = i;
			lowest_priority_start_frame = gCurrentStreamInfo[ i ].start_frame;
		}
	}

	// Couldn't find a free one, so lets see if we can knock one off
	if (priority >= lowest_priority)
	{
		PCMAudio_StopStream( lowest_priority_channel );

	#if WAIT_AFTER_STOP_STREAM
		Dbg_MsgAssert(0, ("Priority streams not implemented on this system"));		// Not implemented
		return ( -1 );
	#else
		if (preload_only)
		{
			success = PCMAudio_PreLoadStream( checksum, lowest_priority_channel );
		}
		else
		{
//			success = PCMAudio_PlayStream( checksum, lowest_priority_channel, volumeL, volumeR, pitch );
#			ifdef __PLAT_XBOX__
			success = PCMAudio_PlayStream( checksum, lowest_priority_channel, p_volume, pitch );
#			else
			success = PCMAudio_PlayStream( checksum, lowest_priority_channel, p_volume->GetChannelVolume( 0 ), p_volume->GetChannelVolume( 1 ), pitch );
#			endif
		}

		if ( !success )
		{
			Dbg_MsgAssert(0, ("Higher priority stream could not start.  Make sure there was another error (like file not found)."));
			return ( -1 );
		}

		gCurrentStreamInfo[ lowest_priority_channel ].controlID = controlID;
		gCurrentStreamInfo[ lowest_priority_channel ].uniqueID = GenerateUniqueID(controlID);
		gCurrentStreamInfo[ lowest_priority_channel ].voice = lowest_priority_channel;
		gCurrentStreamInfo[ lowest_priority_channel ].priority = priority;
		gCurrentStreamInfo[ lowest_priority_channel ].start_frame = (uint32)Tmr::GetRenderFrame();
		gCurrentStreamInfo[ lowest_priority_channel ].p_frame_amp = CStreamFrameAmpManager::sGetFrameAmp(checksum);
	#endif // WAIT_AFTER_STOP_STREAM
		return ( lowest_priority_channel );
	}

	return ( -1 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool _PreLoadMusicStream( uint32 checksum )
{
	if (NoMusicPlease()) return false;

	Dbg_MsgAssert( gPcmInitialized,( "Calling _PreLoadMusicStream %s, when PCM Audio not initialized.", Script::FindChecksumName(checksum) ));

	return PCMAudio_PreLoadMusicStream( checksum );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool _StartPreLoadedMusicStream( float volume )
{
	if( !PCMAudio_StartPreLoadedMusicStream( ))
	{
		return false;
	}

	PCMAudio_SetMusicVolume( volume );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#define MIN_STREAM_VOL		2.0f



uint32 PlayStreamFromObject( Obj::CStreamComponent *pComponent, uint32 streamNameChecksum, float dropoff, float volume,
							 float pitch, int priority, int use_pos_info, EDropoffFunc dropoffFunc, uint32 controlID )
{
	Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();

	// Don't start a stream if it won't be heard
	if( sfx_manager->PositionalSoundSilenceMode() )
	{
		return 0;
	}

	Replay::WritePositionalStream(pComponent->GetObject()->GetID(),streamNameChecksum,dropoff,volume,pitch,priority,use_pos_info);
	if (StreamsDisabled()) return 0;
	
	Sfx::sVolume vol;
	vol.SetSilent();
	
	// Let the priority system figure this out
	//if (!StreamAvailable())
	//	return 0;
	
	// Set up volume according to position of object to camera.
	if ( use_pos_info != 0 )
	{
		Gfx::Camera *pCamera = Nx::CViewportManager::sGetClosestCamera( pComponent->GetObject()->m_pos );

		if (pCamera)
		{
			Mth::Vector dropoff_pos;
			Mth::Vector *p_dropoff_pos = NULL;
			if (pComponent->GetClosestDropoffPos(pCamera, dropoff_pos))
			{
				p_dropoff_pos = &dropoff_pos;
			}

			sfx_manager->SetVolumeFromPos( &vol, pComponent->GetClosestEmitterPos(pCamera), dropoff, dropoffFunc, pCamera, p_dropoff_pos );
		}
		else
		{
			vol.SetSilent();
		}

		// Seems strange to cancel out a stream if it starting low
		if( fabsf( vol.GetLoudestChannel()) < MIN_STREAM_VOL )
		{
			if( Script::GetInteger( 0xd7bb618d /* DebugSoundFx */, Script::NO_ASSERT ))
			{
				Dbg_Message("I wanted to cancel stream %s", Script::FindChecksumName(streamNameChecksum));
			}
//			return 0;
		}
	}
	else
	{
		vol.SetChannelVolume( 0, volume );
		vol.SetChannelVolume( 1, volume );
	}
	
	// K: The 'false' on the end means do not record the call in the replay, because this
	// call to PlayStreamFromObject has already been recorded.
	uint32 uniqueID = PlayStream( streamNameChecksum, &vol, pitch, priority, controlID, false );
	if ( uniqueID )
	{
		Dbg_MsgAssert( ( ( gLastStreamChannelPlayed >= 0 ) && ( gLastStreamChannelPlayed < MAX_STREAMS_PER_OBJECT ) ), ( "Tell Matt to fix object streams" ) );
		if ( gpStreamingObj[ gLastStreamChannelPlayed ] )
		{
			// the object might not have called StreamUpdate yet, so the slot might
			// still think it's in use... clear the slot if that's the case:
			gpStreamingObj[ gLastStreamChannelPlayed ]->mStreamingID[ gLastStreamChannelPlayed ] = 0;
			gpStreamingObj[ gLastStreamChannelPlayed ] = NULL;
		}
//		Dbg_MsgAssert( !gpStreamingObj[ gLastStreamChannelPlayed ], ( "Have Matt fix object streams... Kick him in the nutsack while you're at it." ) );
//		Dbg_MsgAssert( !pObject->mStreamingID[ gLastStreamChannelPlayed ], ( "Have Matt fix object streams... Hell, fire matt!!!" ) );
		gpStreamingObj[ gLastStreamChannelPlayed ] = pComponent;
		pComponent->mStreamingID[ gLastStreamChannelPlayed ] = uniqueID;
		// if volume modifier sent in, store that:
		if ( volume != 100.0f )
		{
			int i;
			for ( i = 0; i < NUM_STREAMS; i++ )
			{
				if ( gCurrentStreamInfo[ i ].uniqueID == uniqueID )
				{
					gCurrentStreamInfo[ i ].volume = PERCENT( gCurrentStreamInfo[ i ].volume, volume );
					gCurrentStreamInfo[ i ].dropoff = dropoff;
				}
			}
		}

		// store state of use_pos_info
		if ( !use_pos_info )
		{
			for ( int i = 0; i < NUM_STREAMS; i++ )
			{
				if ( gCurrentStreamInfo[i].uniqueID == uniqueID )
				{
					gCurrentStreamInfo[i].use_pos_info = false;
				}
			}
		}
		else
		{
			for ( int i = 0; i < NUM_STREAMS; i++ )
			{
				if ( gCurrentStreamInfo[i].uniqueID == uniqueID )
				{
					gCurrentStreamInfo[i].use_pos_info = true;
					gCurrentStreamInfo[i].dropoffFunction = dropoffFunc;
				}
			}
		}
	}
	return ( uniqueID );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 PlayStream( uint32 checksum, Sfx::sVolume *p_volume, float pitch, int priority, uint32 controlID, bool record_in_replay )
{
	if (record_in_replay)
	{
		Replay::WritePlayStream( checksum, p_volume, pitch, priority );
	}	
	if (StreamsDisabled()) return 0;
	
	// Just use checksum if no controlID is set
	if (controlID == 0)
	{
		controlID = checksum;
	}

	if (!IDAvailable(controlID))
	{
		Dbg_Message("Playing stream %s with same checksum as one already being played, won't be able to control directly.", Script::FindChecksumName(controlID));
	}
	
	int whichStream;

	// allow streams to play with default values, if not pre-loaded.
	whichStream = _PlayStream( checksum, p_volume, pitch, priority, controlID, false );
	if ( whichStream != -1 )
	{
		gCurrentStreamInfo[ whichStream ].dropoff = DEFAULT_STREAM_DROPOFF;
		gCurrentStreamInfo[ whichStream ].volume = 100.0f;
		gLastStreamChannelPlayed = whichStream;
		return gCurrentStreamInfo[ whichStream ].uniqueID;
	}

//	Dbg_MsgAssert( 0,( "Tried to play track %s not in header.", Script::FindChecksumName( checksum ) ));
	return ( 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 PreLoadStream( uint32 checksum, int priority )
{
	if (StreamsDisabled()) return 0;
	
	// Since this isn't hooked up to a script, we just supply the checksum
	uint32 controlID = checksum;

	int whichStream;

	// allow streams to play with default values, if not in list.
	Sfx::sVolume vol;
	vol.SetSilent();
	whichStream = _PlayStream( checksum, &vol, 0.0, priority, controlID, true );

	if ( whichStream != -1 )
	{
#ifdef __PLAT_NGPS__
		Dbg_Message("PreLoadStream for stream %s on channel %d", Script::FindChecksumName( checksum ), whichStream);
#endif 
		gCurrentStreamInfo[ whichStream ].dropoff = DEFAULT_STREAM_DROPOFF;
		gCurrentStreamInfo[ whichStream ].volume = 100.0f;
		return gCurrentStreamInfo[ whichStream ].uniqueID;
	}

//	Dbg_MsgAssert( 0,( "Tried to play track %s not in header.", Script::FindChecksumName( checksum ) ));
	return ( 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool PreLoadStreamDone( uint32 streamID )
{
	if (StreamsDisabled()) {
		return false;
	}

	int channel = GetChannelFromID(streamID);

	if ( channel >= 0 )
	{
#ifdef __PLAT_NGPS__
		if (PCMAudio_PreLoadStreamDone( channel ))
		{
			Dbg_Message("PreLoadStreamDone for stream %s on channel %d is true", Script::FindChecksumName( streamID ), channel );
		}
#endif 
		return PCMAudio_PreLoadStreamDone( channel );
	}

	// Couldn't find stream
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool StartPreLoadedStream( uint32 streamID, Sfx::sVolume *p_volume, float pitch )
{
	if (StreamsDisabled()) return false;
	
	int channel = GetChannelFromID(streamID);

	if ( channel >= 0 )
	{
#ifdef __PLAT_NGPS__
		Dbg_Message("StartPreLoadStream for stream %s on channel %d", Script::FindChecksumName( streamID ), channel);
#endif 

//		return PCMAudio_StartPreLoadedStream( channel, volumeL, volumeR, pitch );
#		ifdef __PLAT_XBOX__
		return PCMAudio_StartPreLoadedStream( channel, p_volume, pitch );
#		else
		return PCMAudio_StartPreLoadedStream( channel, p_volume->GetChannelVolume( 0 ), p_volume->GetChannelVolume( 1 ), pitch );
#		endif
	}

	// Couldn't find stream
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool PreLoadMusicStream( uint32 checksum )
{
	if (NoMusicPlease()) return false;

	Dbg_MsgAssert( gPcmInitialized,( "Calling Pcm::PlayMusicStream '%s', when PCM Audio not initialized.", Script::FindChecksumName(checksum) ));
	Dbg_MsgAssert( !gMusicStreamType,( "Calling Pcm::PlayMusicStream '%s', when one is already playing.", Script::FindChecksumName(checksum) ));

	// Stop any previous track
	PCMAudio_StopMusic(false);
	
	bool success = false;

#if WAIT_AFTER_STOP_STREAM
	gMusicStreamChecksum = checksum;
	gMusicStreamWaitingToStart = true;
#else
	// allow streams to play with default values, if not in list.
	success = _PreLoadMusicStream( checksum );
#endif // WAIT_AFTER_STOP_STREAM

	gMusicStreamType = MUSIC_STREAM_TYPE_PRELOAD;

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool PreLoadMusicStreamDone( void )
{
	if (NoMusicPlease()) return false;
	Dbg_MsgAssert( gMusicStreamType == MUSIC_STREAM_TYPE_PRELOAD,( "Calling Pcm::PreLoadMusicStreamDone while in normal music mode." ));

	return PCMAudio_PreLoadMusicStreamDone( );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool StartPreLoadedMusicStream( float volume )
{
	if (NoMusicPlease()) return false;

	Dbg_MsgAssert( gMusicStreamType == MUSIC_STREAM_TYPE_PRELOAD,( "Calling Pcm::StartPreLoadedMusicStream while in normal music mode." ));

	if ( volume < 0.0f )
	{
		volume = gMusicStreamVolume;
	}
	
	return _StartPreLoadedMusicStream( volume );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool SetStreamVolume( int objStreamIndex )
{
	if ( StreamsDisabled() ) return false;
	
	Obj::CStreamComponent *pComp;
	pComp = gpStreamingObj[ objStreamIndex ];

	CurrentStreamInfo *pInfo = NULL;

	int i;
	for ( i = 0; i < NUM_STREAMS; i++ )
	{
		if ( gCurrentStreamInfo[ i ].uniqueID == pComp->mStreamingID[ objStreamIndex ] )
		{
			if ( PCMAudio_GetStreamStatus( gCurrentStreamInfo[ objStreamIndex ].voice ) == PCM_STATUS_FREE )
			{
				return ( false );
			}	
			pInfo = &gCurrentStreamInfo[ i ];

			// Update volume from dropoff/pan for the object responsible for streaming.
			Sfx::sVolume vol;

			Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();
			if ( pInfo->use_pos_info )
			{
				Gfx::Camera *pCamera = Nx::CViewportManager::sGetClosestCamera( pComp->GetObject()->m_pos );

				if (pCamera)
				{
					Mth::Vector dropoff_pos;
					Mth::Vector *p_dropoff_pos = NULL;
					if (pComp->GetClosestDropoffPos(pCamera, dropoff_pos))
					{
						p_dropoff_pos = &dropoff_pos;
					}

					sfx_manager->SetVolumeFromPos( &vol, pComp->GetClosestEmitterPos(pCamera), pInfo->dropoff, pInfo->dropoffFunction, pCamera, p_dropoff_pos );
				}
				else
				{
					vol.SetSilent();
				}
			}
			else
			{
				vol.SetChannelVolume( 0, pInfo->volume );
				vol.SetChannelVolume( 1, pInfo->volume );
			}

			if( pInfo->volume != 100.0f )
			{
				vol.PercentageAdjustment( pInfo->volume );
			}

#			ifdef __PLAT_XBOX__
			// We need the sign-correct version for Xbox to calculate proper 5.1 values.
			PCMAudio_SetStreamVolume( &vol, pInfo->voice );
#			else
			PCMAudio_SetStreamVolume( vol.GetChannelVolume( 0 ), vol.GetChannelVolume( 1 ), pInfo->voice );
#			endif
			return ( true );
		}
	}
	return ( false );
}

void Init( void )
{
	// Zero these out at startup.
	for( int s = 0; s < NUM_STREAMS; ++s )
	{
		gpStreamingObj[s] = NULL;
	}

	if (NoMusicPlease() && StreamsDisabled()) return;
	
	PCMAudio_Init( );
	gPcmInitialized = true;
	gMusicVolume = (float)Script::GetInteger( 0xabd4a575 ); // checksum 'MusicVolume'
	if ( !gMusicVolume )
	{
		gMusicVolume = DEFAULT_MUSIC_VOLUME;
	}
//	SetVolume( gMusicVolume );
	gMusicStreamVolume = (float)Script::GetInteger(CRCD(0x73f8e03b,"MusicStreamVolume"));
	if ( !gMusicStreamVolume )
	{
		gMusicStreamVolume = DEFAULT_MUSIC_STREAM_VOLUME;
	}

	int i;
	for ( i = 0; i < NUM_TRACKLISTS; i++ )
	{
		gTrackLists[ i ].numTracks = 0;
		gTrackLists[ i ].trackForbidden0 = 0;
        gTrackLists[ i ].trackForbidden1 = 0;
		gTrackLists[ i ].trackPlayed0 = 0;
        gTrackLists[ i ].trackPlayed1 = 0;
		// no tracks, so all tracks forbidden, right?
		gTrackLists[ i ].allTracksForbidden = true;
	}
	for ( i = 0; i < NUM_STREAMS; i++ )
	{
		gpStreamingObj[ i ] = NULL;
		gCurrentStreamInfo[ i ].controlID = 0;
		gCurrentStreamInfo[ i ].uniqueID = 0;
	}
} // end of Init( )


// Should be renamed to StopUsingCD( ) as if the cd is in use
// by streams, this function stops the streams and frees up the
// CD.
bool UsingCD( void )
{
	
	#if defined( __PLAT_NGC__ )
	if ( !( gPcmInitialized ) )
	{
		return ( false );
	}
	StopMusic( );
	StopStreams( );
	
	#else
	
	if (Config::CD() || TEST_FROM_CD)
	{
		if ( !( gPcmInitialized ) )
		{
	//		Dbg_Message( "uninitialized" );
			return ( false );
		}
		StopMusic( );
		StopStreams( );
	}	
	#endif
		
	return ( false );
} // end of UsingCD( )

// plays a music track.
void PlayTrack( const char *filename, bool loop )
{
	if (NoMusicPlease()) return;

	if (gMusicStreamType)
	{
		Dbg_MsgAssert(0, ("Trying to play a music track in stereo stream mode."));
		return;
	}
	
	Dbg_MsgAssert( gPcmInitialized,( "Calling Pcm::PlayTrack '%s', when PCM Audio not initialized.", filename ));
	Dbg_MsgAssert( strlen( filename ) < MAX_TRACKNAME_STRING_LENGTH,( "CD Audio Filename too long." ));
	strcpy( gTrackName, filename );
	
	int trackList = gCurrentTrackList;
	float volume;
	if ( trackList == TRACKLIST_PERM )
	{
		volume = gMusicVolume;
	}
	else
	{
		Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();
		volume = sfx_manager->GetMainVolume( );
	}
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if ( skate_mod->GetGameMode( )->IsFrontEnd( ) )
	{
		trackList = TRACKLIST_LEVEL_SPECIFIC;
		volume = gMusicVolume;
	}

	SetLoopingMode(loop);
	
	if ( !_PlayMusicTrack( gTrackName, volume ) )
	{
		if (Config::CD() || TEST_FROM_CD)
		{
			Dbg_Message( "Failed playing track %s", filename );
		}	
		else
		{
			// see if this is a track in the current playlists...
			// if so, don't keep trying to play it...
			TrackList *pTrackList = &gTrackLists[ trackList ];
			int i;
			for ( i = 0; i < pTrackList->numTracks; i++ )
			{
				if ( Script::GenerateCRC( filename ) == Script::GenerateCRC( pTrackList->trackInfo[ i ].trackName ) )
				{
					SetTrackForbiddenStatus( i, true, trackList );
					return;
				}
			}
		}	
	}
}

// Plays a stereo stream through the music channels
void PlayMusicStream( uint32 checksum, float volume )
{
	if (NoMusicPlease()) return;

	Dbg_MsgAssert( gPcmInitialized,( "Calling Pcm::PlayMusicStream '%s', when PCM Audio not initialized.", Script::FindChecksumName(checksum) ));
	Dbg_MsgAssert( !gMusicStreamType,( "Calling Pcm::PlayMusicStream '%s', when one is already playing.", Script::FindChecksumName(checksum) ));

	// Stop any previous track
	PCMAudio_StopMusic(false);

	if ( volume < 0.0f )
	{
		volume = gMusicStreamVolume;
	}

#if WAIT_AFTER_STOP_STREAM
	gMusicStreamChecksum = checksum;
	gMusicStreamVolume = volume;
	gMusicStreamWaitingToStart = true;
#else
	_PlayMusicStream( checksum, volume );
#endif

	gMusicStreamType = MUSIC_STREAM_TYPE_NORMAL;
}

static bool sMusicIsPaused=false;
void PauseMusic( int pause )
{
	
	
	static bool wasPaused = false;

	if (NoMusicPlease()) return;
	
	Dbg_MsgAssert( gPcmInitialized,( "Calling Pcm::Pause when PCM Audio not initialized." ));
	switch ( pause )
	{
		case ( -1 ):
			// special case... unpause only if it was unpaused last time pause was called...
			if ( !wasPaused )
			{
				// recursive... unpause this bitch...
				PauseMusic( 0 );
				return;
			}
			break;
		case ( 1 ):
			wasPaused = sMusicIsPaused;
			sMusicIsPaused = true;
			PCMAudio_Pause( true, MUSIC_CHANNEL );
			break;
		case ( 0 ):
			sMusicIsPaused = false;
			PCMAudio_Pause( false, MUSIC_CHANNEL );
			break;
		default:
			Dbg_MsgAssert( 0, ( "Unknown pause state %d", pause ) );
			break;
	}
}

bool MusicIsPaused()
{
	return sMusicIsPaused;
}
	
void PauseStream( int pause )
{
	
	
	static bool paused = false;
	static bool wasPaused = false;

	if (StreamsDisabled()) return;
	
	Dbg_MsgAssert( gPcmInitialized,( "Calling Pcm::Pause when PCM Audio not initialized." ));
	switch ( pause )
	{
		case ( -1 ):
			// special case... unpause only if it was unpaused last time pause was called...
			if ( !wasPaused )
			{
				// recursive... unpause this bitch...
				PauseStream( 0 );
				return;
			}
			break;
		case ( 1 ):
			wasPaused = paused;
			paused = true;
			PCMAudio_Pause( true, EXTRA_CHANNEL );
			break;
		case ( 0 ):
			paused = false;
			PCMAudio_Pause( false, EXTRA_CHANNEL );
			break;
		default:
			Dbg_MsgAssert( 0, ( "Unknown pause state %d", pause ) );
			break;
	}
}

void SetVolume( float volume )
{
	
	if (NoMusicPlease()) return;
	
	Dbg_MsgAssert( gPcmInitialized,( "Calling Pcm::SetVolume, when PCM Audio not initialized." ));
	if ( ( volume > 100.0f ) || ( volume < 0.0f ) )
	{
		Dbg_MsgAssert( 0,( "Volume (%f) not between 0 and 100", volume ));
		return;
	}
	
	
	gMusicVolume = volume;
	if ( gPcmInitialized )
	{
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		if ( ( gMusicStreamType == MUSIC_STREAM_TYPE_NONE) &&
			 ( ( gCurrentTrackList == TRACKLIST_PERM ) ||
			 ( skate_mod->GetGameMode( )->IsFrontEnd( ) ) ) )
		{
//			volume = volume * Config::GetMasterVolume()/100.0f;
//			if (volume <0.1f)
//			{
//				volume = 0.0f;
//			}
			PCMAudio_SetMusicVolume( volume );
		}
	}
}

float GetVolume( void )
{
	
	return ( gMusicVolume );
}

void SetMusicStreamVolume( float volume )
{
	if (NoMusicPlease()) return;
	
	Dbg_MsgAssert( gPcmInitialized,( "Calling Pcm::SetVolume, when PCM Audio not initialized." ));
	if ( ( volume > 100.0f ) || ( volume < 0.0f ) )
	{
		Dbg_MsgAssert( 0,( "Volume (%f) not between 0 and 100", volume ));
		return;
	}
	
	gMusicStreamVolume = volume;
	if ( gPcmInitialized )
	{
		if (gMusicStreamType == MUSIC_STREAM_TYPE_NORMAL)
		{
			PCMAudio_SetMusicVolume( volume );
		}
	}
}

float GetMusicStreamVolume( void )
{
	return gMusicStreamVolume;
}

void AddTrackToPlaylist( const char *trackName, int whichList, const char *trackTitle )
{
	
	if (NoMusicPlease()) return;
	

	Dbg_MsgAssert( trackName,( "What the fuck?" ));
	Dbg_MsgAssert( whichList < NUM_TRACKLISTS,( "Dumbass." ));
	TrackList *pTrackList = &gTrackLists[ whichList ];

	// Ken: This is a fix to TT6484. When an ambient track finished, it was choosing
	// a new random ambient track, rather than playing the same one again.
	// So to fix, just force numTracks to zero if adding an ambient track, so that there
	// can be only one. That way, it will be forced to play the same one again. Nya ha!
	if (whichList==TRACKLIST_LEVEL_SPECIFIC)
	{
		pTrackList->numTracks=0;
	}
	
	if ( pTrackList->numTracks >= MAX_NUM_TRACKS )
	{
		Dbg_MsgAssert( 0,( "Too many tracks in playlist (increase MAX_NUM_TRACKS or clear list)." ));
		return;
	}
	Dbg_MsgAssert( strlen( trackName ) < MAX_TRACKNAME_STRING_LENGTH,( "Track name too long." ));
	char *pTrackName = pTrackList->trackInfo[ pTrackList->numTracks ].trackName;
	strcpy( pTrackName, trackName );
	
	uint i;
	for ( i = 0; i < strlen( pTrackName ); i++ )
	{
		if ( ( pTrackName[ i ] >= 'a' ) && ( pTrackName[ i ] <= 'z' ) )
		{
			pTrackName[ i ] = pTrackName[ i ] - 'a' + 'A';
		}
	}
	// Music tracks should have a title (for the user to be able to turn songs on/off):
	if ( whichList == TRACKLIST_PERM )
	{
		char *pTrackTitle = PermTrackTitle[ pTrackList->numTracks ].trackTitle;
		if ( trackTitle )
		{
			Dbg_MsgAssert( strlen( trackTitle ) < TRACK_TITLE_MAX_SIZE, ( "Track title %s too long (max %d chars)", trackTitle, TRACK_TITLE_MAX_SIZE ) );
			strncpy( pTrackTitle, trackTitle, TRACK_TITLE_MAX_SIZE );
			pTrackTitle[ TRACK_TITLE_MAX_SIZE - 1 ] = '\0';
		}
		else
		{
	   		Dbg_MsgAssert( 0, ( "Track %s not given a title.", trackName ) );
			strcpy( pTrackTitle, "No title provided" );
		}
	}
	if ( !PCMAudio_TrackExists( pTrackName, MUSIC_CHANNEL ) )
	{
		Dbg_MsgAssert(!Config::CD(),("Could not find music track '%s'\n",pTrackName));
		Dbg_Message("Could not find music track '%s'",pTrackName);
		return;
	}	
	pTrackList->allTracksForbidden = false;
    pTrackList->numTracks++;
}

void ClearPlaylist( int whichList )
{
	
	if (NoMusicPlease()) return;
	
	Dbg_MsgAssert( whichList < NUM_TRACKLISTS,( "Dumbshit." ));
	TrackList *pTrackList = &gTrackLists[ whichList ];
	pTrackList->numTracks = 0;
	pTrackList->allTracksForbidden = true; // if there are no tracks, all are forbidden, no?
	pTrackList->trackForbidden0 = 0;
    pTrackList->trackForbidden1 = 0;
	pTrackList->trackPlayed0 = 0;
    pTrackList->trackPlayed1 = 0;
	
	gNumStreams = 0;
}

void SkipMusicTrack( void )
{
	
	if (NoMusicPlease()) return;
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if ( skate_mod->GetGameMode( )->IsFrontEnd( ) )
	{
		return;
	}

	if ( gCurrentTrackList == TRACKLIST_PERM )
	{
		// will stop the current song, auto update will
		// cause music to continue with the next track...
		StopMusic( );
	}
}

void SetRandomMode( int randomModeOn )
{
	if (NoMusicPlease()) return;

	gMusicInRandomMode = randomModeOn;

#	ifdef __PLAT_XBOX__
	s_xbox_user_soundtrack_random = ( randomModeOn > 0 );
	if( randomModeOn )
	{
		// Generate a new random order now, since the current one may be invalid for this soundtrack.
		sGenerateRandomSongOrder();
		s_xbox_random_index = 0;
		s_xbox_user_soundtrack_song = sp_xbox_randomized_songs[0];
	}
#	endif // __PLAT_XBOX__
	
	Dbg_MsgAssert(gCurrentTrackList>=0 && gCurrentTrackListtrackPlayed0=0;
    pTrackList->trackPlayed1=0;
}

int GetRandomMode( void )
{
	return ( gMusicInRandomMode );
}

void SetLoopingMode( bool loop )
{
	gMusicLooping = loop;
}

#if WAIT_AFTER_STOP_STREAM
static void _StartMusicStream()
{
	switch (gMusicStreamType)
	{
	case MUSIC_STREAM_TYPE_NORMAL:
		_PlayMusicStream( gMusicStreamChecksum, gMusicStreamVolume );
		break;

	case MUSIC_STREAM_TYPE_PRELOAD:
		_PreLoadMusicStream( gMusicStreamChecksum );
		break;

	case MUSIC_STREAM_TYPE_NONE:
		Dbg_MsgAssert(0, ("Bad start music stream state"));
		break;
	}

	// No longer waiting
	gMusicStreamWaitingToStart = false;
}
#endif // WAIT_AFTER_STOP_STREAM

static void sSetCorrectMusicVolume()
{
	#ifdef __PLAT_XBOX__
	if (s_xbox_play_user_soundtracks)
	{
		// This 'if' is part of the fix to TT6957, where user soundtracks were getting sfx vol
		// when music vol turned down to zero.
		PCMAudio_SetMusicVolume( Pcm::GetVolume() );
	}
	else if (gCurrentTrackList == TRACKLIST_LEVEL_SPECIFIC)
	{
		Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
		PCMAudio_SetMusicVolume( sfx_manager->GetMainVolume() );
	}
	#else
	// Ken: I added this as a fix to TT5588, 'ambient tracks don't play when music vol is set to zero'
	// This forces the ambient track to use the sfx volume rather than gMusicVolume.
	// Hopefully there is not too much of an overhead to calling PCMAudio_SetMusicVolume ...
	// This bit of code won't be executed every frame though, but every SONG_UPDATE_INTERVAL frames,
	// so I think it'll be OK ...
	if (gCurrentTrackList == TRACKLIST_LEVEL_SPECIFIC)
	{
		Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
		//printf("Playing ambient track: Forcing volume to %f ...\n",sfx_manager->GetMainVolume());
		PCMAudio_SetMusicVolume( sfx_manager->GetMainVolume() );
	}
	#endif
}

void RandomTrackUpdate( void )
{
	
	TrackList *pTrackList = &gTrackLists[ gCurrentTrackList ];
	
	if (NoMusicPlease()) return;
	
	
	if ( pTrackList->allTracksForbidden )
	{
//		Dbg_Message( "all tracks forbidden.. random mode" );
		return;
	}
	int pcmStatus = PCMAudio_GetMusicStatus( );
	if ( pcmStatus == PCM_STATUS_FREE )
	{
	#if WAIT_AFTER_STOP_STREAM
		// If we were waiting for StopMusic() to finish, we need to start the music stream
		if (gMusicStreamWaitingToStart)
		{
			_StartMusicStream();
			return;
		}
	#endif // WAIT_AFTER_STOP_STREAM

		gMusicStreamType = MUSIC_STREAM_TYPE_NONE;		// Just in case we were playing a music stream
		sSetCorrectMusicVolume();

		// Replay track if looping
		if (gMusicLooping)
		{
			PlayTrack( gTrackName, true );
			return;
		}
		
		if ( !pTrackList->numTracks )
		{
			Dbg_MsgAssert( 0,( "AllTracksForbidden flag should be set if no tracks exist." ));
			return;
		}
		
        if ( shuffle_random_songs )
        {
            // Remember the last song in the previous order, so we can make sure that
        	// the first song in the new order is different from it.
        	// Note: OK if the last song was just an uninitialised random value, won't cause any problems.
        	int last_song=sp_random_song_order[num_random_songs-1];
            
            // count active tracks
            num_random_songs=0;
            for (int i=0; inumTracks; ++i)
            {
                if ( i < 64)
                {
                    if ( !( pTrackList->trackForbidden0 & ( ((uint64)1) << i ) ) )
                    {
                        // Intialize order
                        sp_random_song_order[num_random_songs]=i;
                        // add one
                        num_random_songs++;
                    }
                }
                else
                {
                    if ( !( pTrackList->trackForbidden1 & ( ((uint64)1) << (i-64) ) ) )
                    {
                        // Intialize order
                        sp_random_song_order[num_random_songs]=i;
                        // add one
                        num_random_songs++;
                    }
                }
            }

            // need to shuffle order
            printf("shuffling random song order num_songs=%i\n", num_random_songs );
            
            // Jumble it up.		
        	for (int n=0; n<2000; ++n)
        	{
        		int a=Mth::Rnd(num_random_songs);
        		int b=Mth::Rnd(num_random_songs);
        		
                if ( a != b )
                {
                    int temp=sp_random_song_order[a];
            		sp_random_song_order[a]=sp_random_song_order[b];
            		sp_random_song_order[b]=temp;
                }
        	}
        	
            // If the first song in the new order equals the last song of the last order,
        	// do one further swap to make sure it is different.
        	if (sp_random_song_order[0]==last_song)
        	{
        		// a is the first song.
        		int a=0;
        		
        		// Choose b to be a random one of the other songs.
        		int b;
        		if (num_random_songs>1)
        		{
        			// Make b not able to equal 0, so that we don't swap the first with itself.
        			b=1+Mth::Rnd(num_random_songs-1);
        		}
        		else
        		{
        			// Unless there is only one song, in which case just swap it with itself. No point but what the heck.
        			b=0;
        		}
        		
        		// Do the swap.
        		int temp=sp_random_song_order[a];
        		sp_random_song_order[a]=sp_random_song_order[b];
        		sp_random_song_order[b]=temp;
        	}
            
            // reset index
            random_song_index=0;
            shuffle_random_songs=false;
            pTrackList->trackPlayed0 = 0;
            pTrackList->trackPlayed1 = 0;
        }
        else
        {
            random_song_index++;
            if ( random_song_index >= (num_random_songs-1) )
            {
                shuffle_random_songs=true;
            }
        }
        
        for ( int i = 0; i < pTrackList->numTracks; i++ )
		{
			int t = ( sp_random_song_order[random_song_index] );
            //printf("index = %i song = %i\n", random_song_index, sp_random_song_order[random_song_index] );

            if ( ( !( pTrackList->trackForbidden0 & ( ((uint64)1) << t ) ) && ( t < 64 ) && 
                   !( pTrackList->trackPlayed0 & ( ((uint64)1) << t ) ) ) )
			{
				pTrackList->trackPlayed0 |= ( ((uint64)1) << t );
				PlayTrack( pTrackList->trackInfo[ t ].trackName );
                current_music_track=t;
				Dbg_Message( "Playing track %s %i %i", pTrackList->trackInfo[ t ].trackName, t, current_music_track );

                // update track text on screen
                Script::CStruct *pParams = new Script::CStruct;
                pParams->AddInteger(CRCD(0x8d02705d,"current_track"),current_music_track);
                Script::RunScript( "spawn_update_music_track_text", pParams );
                delete pParams;

				return;
			}

            if ( ( !( pTrackList->trackForbidden1 & ( ((uint64)1) << (t-64) ) ) && ( t >= 64 ) && 
                   !( pTrackList->trackPlayed1 & ( ((uint64)1) << (t-64) ) ) )
               )
			{
				pTrackList->trackPlayed1 |= ( ((uint64)1) << (t-64) );
				PlayTrack( pTrackList->trackInfo[ t ].trackName );
                current_music_track=t;
				Dbg_Message( "Playing track %s %i %i", pTrackList->trackInfo[ t ].trackName, t, current_music_track );

                // update track text on screen
                Script::CStruct *pParams = new Script::CStruct;
                pParams->AddInteger(CRCD(0x8d02705d,"current_track"),current_music_track);
                Script::RunScript( "spawn_update_music_track_text", pParams );
                delete pParams;

				return;
			}
		}
	}
}

void TrackUpdate( void )
{
	

	if (NoMusicPlease()) return;
	
	int trackList = gCurrentTrackList;
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if ( skate_mod->GetGameMode( )->IsFrontEnd( ) )
	{
		trackList = TRACKLIST_LEVEL_SPECIFIC;
	}

	TrackList *pTrackList = &gTrackLists[ trackList ];
	
	if ( pTrackList->allTracksForbidden )
	{
//		Dbg_Message( "all tracks forbidden.. sequential mode" );
		return;
	}
	int pcmStatus = PCMAudio_GetMusicStatus( );
	if ( pcmStatus == PCM_STATUS_FREE )
	{
	#if WAIT_AFTER_STOP_STREAM
		// If we were waiting for StopMusic() to finish, we need to start the music stream
		if (gMusicStreamWaitingToStart)
		{
			_StartMusicStream();
			return;
		}
	#endif // WAIT_AFTER_STOP_STREAM

		gMusicStreamType = MUSIC_STREAM_TYPE_NONE;		// Just in case we were playing a music stream
		sSetCorrectMusicVolume();
		
		// Replay track if looping
		if (gMusicLooping)
		{
			PlayTrack( gTrackName, true );
			return;
		}

		// don't have a random function.  just play next track...
		int i;
		for ( i = 0; i < pTrackList->numTracks; i++ )
		{
            if ( ( !( pTrackList->trackForbidden0 & ( ((uint64)1) << i ) ) && ( i < 64 ) && 
                   !( pTrackList->trackPlayed0 & ( ((uint64)1) << i ) ) ) )
			{
				pTrackList->trackPlayed0 |= ( ((uint64)1) << i );
				PlayTrack( pTrackList->trackInfo[ i ].trackName );
                current_music_track=i;
				Dbg_Message( "Playing track %s %i %i", pTrackList->trackInfo[ i ].trackName, i, current_music_track );

                // update track text on screen
                Script::CStruct *pParams = new Script::CStruct;
                pParams->AddInteger(CRCD(0x8d02705d,"current_track"),current_music_track);
                Script::RunScript( "spawn_update_music_track_text", pParams );
                delete pParams;

				return;
			}

            if ( ( !( pTrackList->trackForbidden1 & ( ((uint64)1) << (i-64) ) ) && ( i >= 64 ) && 
                   !( pTrackList->trackPlayed1 & ( ((uint64)1) << (i-64) ) ) ) )
			{
				pTrackList->trackPlayed1 |= ( ((uint64)1) << (i-64) );
				PlayTrack( pTrackList->trackInfo[ i ].trackName );
                current_music_track=i;
				Dbg_Message( "Playing track %s %i %i", pTrackList->trackInfo[ i ].trackName, i, current_music_track );

                // update track text on screen
                Script::CStruct *pParams = new Script::CStruct;
                pParams->AddInteger(CRCD(0x8d02705d,"current_track"),current_music_track);
                Script::RunScript( "spawn_update_music_track_text", pParams );
                delete pParams;

				return;
			}
		}
		// all the tracks have been played... reset:
		pTrackList->trackPlayed0 = 0;
        pTrackList->trackPlayed1 = 0;
	}
}

//powers of 2 please...
#define SONG_UPDATE_INTERVAL					64
#define STREAM_POSITIONAL_UPDATE_INTERVAL		4

// Call this function every frame...
// keeps tracks playing during levels and shit like that...
void Update( void )
{
	
	//static int streamingObjToUpdate = 0;

	if (NoMusicPlease() && StreamsDisabled()) return;
	
	Dbg_MsgAssert( gPcmInitialized,( "Calling Pcm::Update when PCM Audio not initialized." ));

	int err_no = PCMAudio_Update( );
	if ( err_no != 0 )
	{
		if (err_no < 0)
		{
			StopStreams( );
			StopMusic( );
		} else {
			// Even though we are returning the stream number, lets just kill them all because we are at the end of the project
			StopStreams( );
		}
	}

	// Garrett: This should go away, since the IOP communication is async now.  But I left it in for
	// the music now since I don't know if there is a CPU hit there.
	sCounter++;

	//if ( !( sCounter & ( STREAM_POSITIONAL_UPDATE_INTERVAL - 1 ) ) )
	for (int streamingObjToUpdate = 0; streamingObjToUpdate < NUM_STREAMS; streamingObjToUpdate++)
	{
		if ( gpStreamingObj[ streamingObjToUpdate ] )
		{
			if ( !SetStreamVolume( streamingObjToUpdate ) )
			{
				gpStreamingObj[ streamingObjToUpdate ]->mStreamingID[ streamingObjToUpdate ] = 0;
				gpStreamingObj[ streamingObjToUpdate ] = NULL;
			}
		}
		//// update the other one next time...
		//streamingObjToUpdate++;
		//if ( streamingObjToUpdate >= NUM_STREAMS )
		//{
		//	streamingObjToUpdate = 0;
		//}
	}

	// Free any frame amp data that was used by a stream that already stopped
	for (int i = 0; i < NUM_STREAMS; i++)
	{
		if (gCurrentStreamInfo[i].p_frame_amp && (PCMAudio_GetStreamStatus(gCurrentStreamInfo[i].voice) == PCM_STATUS_FREE))
		{
			Dbg_Message("Clearing StreamFrameAmp %s", Script::FindChecksumName(gCurrentStreamInfo[i].controlID));
			CStreamFrameAmpManager::sFreeFrameAmp(gCurrentStreamInfo[i].p_frame_amp);
			gCurrentStreamInfo[i].p_frame_amp = NULL;
		}
	}
	
	if ( sCounter & ( SONG_UPDATE_INTERVAL - 1 ) )
		return;

	// if volume is turned all the way down, the current song will finish
	// playing and then we'll keep returning here until volume isn't zero,
	// as soon as it is turned up a new song will begin playing.
	if ( gMusicVolume == 0.0f && gCurrentTrackList != TRACKLIST_LEVEL_SPECIFIC) 
	{
		return;
	}

	#ifdef __PLAT_XBOX__
	// This is part of the fix to TT6957, 
	// where user soundtracks were getting sfx vol when music vol turned down to zero.
	// (The other part of the fix is a bit further down, search for TT6957)
	if ( gMusicVolume == 0.0f && s_xbox_play_user_soundtracks) 
	{
		return;
	}
	#endif
	
	if (sMusicIsPaused)
	{
		return;
	}


	// K: Commented this out since it does not appear to be needed, and was stopping random
	// track mode from working on XBox & Gamecube, I think because is_frontend is defined to
	// be 1 in mode_skateshop in gamemode.q. I commented this out instead of changing
	// is_frontend to 0 in case that broke something else.		
	
	// don't play music in the frontend: <- old comment
	/*
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if ( skate_mod->GetGameMode( )->IsFrontEnd( ) )
	{
		TrackUpdate( );
		return;
	}
	*/

		
	// if song is finished, start a new one playing...
	if ( gMusicInRandomMode || ( gCurrentTrackList == TRACKLIST_LEVEL_SPECIFIC ) )
		RandomTrackUpdate( );
	else
		TrackUpdate( );
}

void GetPlaylist( uint64* flags1, uint64* flags2 )
{
    *flags1 = gTrackLists[ TRACKLIST_PERM ].trackForbidden0;
    *flags2 = gTrackLists[ TRACKLIST_PERM ].trackForbidden1;
}

void SetPlaylist( uint64 flags1, uint64 flags2 )
{
	gTrackLists[ TRACKLIST_PERM ].trackForbidden0 = flags1;
    gTrackLists[ TRACKLIST_PERM ].trackForbidden1 = flags2;
}

// Ambient volume is the same as SoundFX volume...
void SetAmbientVolume( float volPercent )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if ( skate_mod->GetGameMode( )->IsFrontEnd( ) )
	{
		return;
	}
	if ( gCurrentTrackList == TRACKLIST_LEVEL_SPECIFIC )
	{
		PCMAudio_SetMusicVolume( volPercent );
	}
}

// set a particular song as 'forbidden' (don't play it, the player hates that song...)
void SetTrackForbiddenStatus( int trackNum, bool forbidden, int whichList )
{
    if (NoMusicPlease()) return;
	
    shuffle_random_songs=true;

	int i;
	Dbg_MsgAssert( whichList < NUM_TRACKLISTS,( "Dumbass." ));
	TrackList *pTrackList = &gTrackLists[ whichList ];
	Dbg_MsgAssert( trackNum < pTrackList->numTracks,( "Not that many tracks in list." ));
	if ( forbidden )
	{
		if ( trackNum < 64)
        {
            pTrackList->trackForbidden0 |= ( ((uint64)1) << trackNum );
        }
        else
        {
            pTrackList->trackForbidden1 |= ( ((uint64)1) << (trackNum-64) );
        }
        
        if ( TrackIsPlaying( whichList, trackNum ) )
		{
			StopMusic( );
		}
		for ( i = 0; i < pTrackList->numTracks; i++ )
		{
			if ( !( pTrackList->trackForbidden0 & ( ((uint64)1) << i ) ) || !( pTrackList->trackForbidden1 & ( ((uint64)1) << (i-64) ) ) )
			{
				pTrackList->allTracksForbidden = false;
				return;
			}
		}
		pTrackList->allTracksForbidden = true;
		return;
	}
	
    if ( trackNum < 64)
    {
        pTrackList->trackForbidden0 &= ~( ((uint64)1) << trackNum );
    }
    else
    {
        pTrackList->trackForbidden1 &= ~( ((uint64)1) << (trackNum-64) );
    }
	pTrackList->allTracksForbidden = false;
}

/*void CheckLockedTracks()
{
    if ( !Mdl::Skate::Instance()->GetCareer()->GetGlobalFlag(286) )
    {
        // if the kiss songs aren't unlocked make sure they stay forbidden
        SetTrackForbiddenStatus( 32, true, Pcm::TRACKLIST_PERM );
        SetTrackForbiddenStatus( 33, true, Pcm::TRACKLIST_PERM );
    }
}*/

bool TrackIsPlaying( int whichList, int whichTrack )
{
	
	if (NoMusicPlease()) return false;
	
	Dbg_MsgAssert( gPcmInitialized,( "Calling Pcm func when PCM Audio not initialized." ));
	int pcmStatus = PCMAudio_GetMusicStatus( );
	if ( pcmStatus == PCM_STATUS_FREE )
		return ( false );
	if ( whichTrack >= gTrackLists[ whichList ].numTracks )
		return ( false );
	// can't do a strcmp, because files in gTrackName have been converted to all uppercase...
	if ( Script::GenerateCRC( gTrackName ) == Script::GenerateCRC( gTrackLists[ whichList ].trackInfo[ whichTrack ].trackName ) )
		return ( true );
	return ( false );
}

int GetNumTracks( int whichList )
{
	

	if (NoMusicPlease()) return 0;
	

	Dbg_MsgAssert( whichList < NUM_TRACKLISTS,( "Dumbass." ));
	TrackList *pTrackList = &gTrackLists[ whichList ];
	return ( pTrackList->numTracks );
}

int GetTrackForbiddenStatus( int trackNum, int whichList )
{
	

	if (NoMusicPlease()) return 1;
	
	
	Dbg_MsgAssert( whichList < NUM_TRACKLISTS,( "Dumbass." ));
	TrackList *pTrackList = &gTrackLists[ whichList ];
	Dbg_MsgAssert( trackNum < pTrackList->numTracks,( "Requesting forbidden status on invalid track." ));
	
    if ( trackNum < 64 )
    {
        if (pTrackList->trackForbidden0 & ( ((uint64)1) << trackNum ))
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    else
    {
        if (pTrackList->trackForbidden1 & ( ((uint64)1) << (trackNum-64) ))
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
}

const char *GetTrackName( int trackNum, int whichList )
{
	

	if (NoMusicPlease())
	{
		strcpy( gTrackLists[ 0 ].trackInfo[ 0 ].trackName, "Empty." );
		return ( gTrackLists[ 0 ].trackInfo[ 0 ].trackName );
	}
	
	//if ( whichList == TRACKLIST_PERM )
	//{
	//	Dbg_MsgAssert( trackNum < MAX_NUM_TRACKS, ( "Invalid track num specified." ) );
	//	return ( PermTrackTitle[ trackNum ].trackTitle );
	//}

	Dbg_MsgAssert( whichList < NUM_TRACKLISTS,( "Dumbass." ));
	TrackList *pTrackList = &gTrackLists[ whichList ];
	Dbg_MsgAssert( trackNum < pTrackList->numTracks,( "Requesting invalid track name." ));
	int i;
	char *pText = pTrackList->trackInfo[ trackNum ].trackName;
	char *pRet = pText;
	int len = strlen( pText );
	for ( i = 0; i < len; i++ )
	if ( ( pText[ i ] == '\\' ) || ( pText[ i ] == '/' ) )
		pRet = &pText[ i + 1 ];
	return ( pRet );
}

// Ambient (TRACKLIST_LEVEL_SPECIFIC) or music (TRACKLIST_PERM)
void SetActiveTrackList( int whichList )
{
	

	if (NoMusicPlease()) return;
	
	Dbg_MsgAssert( whichList < NUM_TRACKLISTS,( "Dumbass." ));
	if ( gCurrentTrackList != whichList )
	{
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		if ( !skate_mod->GetGameMode( )->IsFrontEnd( ) )
		{
			StopMusic( );
		}
	}
	gCurrentTrackList = whichList;
}

// A user can choose to listen to music or ambience during gameplay.
int GetActiveTrackList( void )
{
	
	return ( gCurrentTrackList );
}

// A music header contains the names and sizes of all the music files available:
bool LoadMusicHeader( const char *nameOfFile )
{
	if (NoMusicPlease()) return false;
	
	music_hed_there =( PCMAudio_LoadMusicHeader( nameOfFile ) );
	
	return	music_hed_there; 
}

// Headers contain all available streams (probably will be one header per level).
bool LoadStreamHeader( const char *nameOfFile )
{
	if (StreamsDisabled()) return false;
	
	streams_hed_there = PCMAudio_LoadStreamHeader( nameOfFile );

	return streams_hed_there;
}

int GetNumStreamsAvailable( void )
{
	return ( NUM_STREAMS );
}

bool StreamAvailable( int whichStream )
{
	if (StreamsDisabled()) return false;
	
	if ( PCMAudio_GetStreamStatus( whichStream ) == PCM_STATUS_FREE )
	{
		return ( true );
	}
	return ( false );
}

bool StreamAvailable( void )
{

	if (StreamsDisabled()) return false;
	
	if (!(Config::CD() || TEST_FROM_CD))
	{
		int testStreams = 0;
		testStreams = Script::GetInt( 0x62df9442, false ); // checksum 'testStreamsFromHost'
		if ( !testStreams )
		{
			return ( true );
		}
	}

//	return ( PCMAudio_GetStreamStatus( 0 ) == PCM_STATUS_FREE );

	int i;
	for ( i = 0; i < NUM_STREAMS; i++ )
	{
		if ( PCMAudio_GetStreamStatus( i ) == PCM_STATUS_FREE )
		{
			return ( true );
		}
	}
	return ( false );
}

bool StreamExists( uint32 streamChecksum )
{
	// try to find the name in the header file using the checksum:
	uint32 streamName = PCMAudio_FindNameFromChecksum( streamChecksum, EXTRA_CHANNEL );
	return ( streamName != NULL );
}

bool StreamLoading( uint32 streamID )
{
	if (StreamsDisabled()) return false;
	
	if (!(Config::CD() || TEST_FROM_CD))
	{
		int testStreams = 0;
		testStreams = Script::GetInt( 0x62df9442, false ); // checksum 'testStreamsFromHost'
		if ( !testStreams )
		{
			return ( false );
		}
	}

	int channel = GetChannelFromID(streamID);

	if ( channel >= 0 )
	{
		return ( PCMAudio_GetStreamStatus( channel ) == PCM_STATUS_LOADING );
	}

	return ( false );
}

bool StreamPlaying( uint32 streamID )
{
	if (StreamsDisabled()) return false;
	
	int channel = GetChannelFromID(streamID, false);

	if ( channel >= 0 )
	{
		return true;	// Even if it is in loading state
	}

	return false;
}

int GetCurrentTrack()
{
    return ( current_music_track );
}

void SetCurrentTrack( int value )
{
    current_music_track=value;
}

///////////////////////////////////////////////////////////////////////////////////
// Pcm::CStreamFrameAmp

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CStreamFrameAmp::CStreamFrameAmp()
{
	Init();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CStreamFrameAmp::~CStreamFrameAmp()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CStreamFrameAmp::Init()
{
	m_checksum = vNOT_ALLOCATED;
	m_num_samples = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8				CStreamFrameAmp::GetSample(int frame) const
{
	Dbg_Assert(is_allocated());
	Dbg_Assert(frame < m_num_samples);

	return m_frame_amp_samples[frame];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const uint8 *		CStreamFrameAmp::GetSamples() const
{
	Dbg_Assert(is_allocated());

	return m_frame_amp_samples;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const uint8 *		CStreamFrameAmp::GetSamples(int frame) const
{
	Dbg_Assert(is_allocated());
	Dbg_Assert(frame < m_num_samples);

	return &m_frame_amp_samples[frame];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					CStreamFrameAmp::GetNumSamples() const
{
	return m_num_samples;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CStreamFrameAmp::load(uint32 stream_checksum)
{
	File::CAsyncFileHandle *p_handle;
	char filename[256];

	// Generate filename from checksum
	sprintf(filename,"fam\\%x.fam", stream_checksum);

	// Open with async blocking for now.  Look to make truly async later.
	p_handle = File::CAsyncFileLoader::sOpen(filename, true);
	if (p_handle)
	{
		m_num_samples = p_handle->GetFileSize();
		if (m_num_samples >= vMAX_SAMPLES)
		{
			Dbg_MsgAssert(0, (".fam file has too many samples: %d", m_num_samples));
			File::CAsyncFileLoader::sClose(p_handle);
			return false;
		}

		p_handle->Read(m_frame_amp_samples, 1, m_num_samples);
		File::CAsyncFileLoader::sClose(p_handle);

		m_checksum = stream_checksum;
		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CStreamFrameAmp::free()
{
	if (is_allocated())
	{
		// Easiest way to clear, since there is nothing to de-allocate.
		Init();

		return true;
	}
	else
	{
		return false;
	}
}

///////////////////////////////////////////////////////////////////////////////////
// Pcm::CStreamFrameAmpManager

CStreamFrameAmp		CStreamFrameAmpManager::s_frame_amp_data[vMAX_BUFFERS];

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CStreamFrameAmp *	CStreamFrameAmpManager::sLoadFrameAmp(uint32 stream_checksum)
{
	for (int i = 0; i < vMAX_BUFFERS; i++)
	{
		if (!s_frame_amp_data[i].is_allocated())
		{
			if (s_frame_amp_data[i].load(stream_checksum))
			{
				return &s_frame_amp_data[i];
			}
			else
			{
				// If we got here, there weren't any free slots
				Dbg_MsgAssert(0, ("Couldn't load StreamFrameAmp %s", Script::FindChecksumName(stream_checksum)));
				return NULL;
			}
		}
	}

	// If we got here, there weren't any free slots
	Dbg_MsgAssert(0, ("Can't load StreamFrameAmp %s: no free slots", Script::FindChecksumName(stream_checksum)));
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CStreamFrameAmp *	CStreamFrameAmpManager::sGetFrameAmp(uint32 stream_checksum)
{
	for (int i = 0; i < vMAX_BUFFERS; i++)
	{
		if (s_frame_amp_data[i].GetStreamNameCRC() == stream_checksum)
		{
			return &s_frame_amp_data[i];
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CStreamFrameAmpManager::sFreeFrameAmp(uint32 stream_checksum)
{
	for (int i = 0; i < vMAX_BUFFERS; i++)
	{
		if (s_frame_amp_data[i].GetStreamNameCRC() == stream_checksum)
		{
			return s_frame_amp_data[i].free();
		}
	}

	// If we got here, we didn't find it
	Dbg_MsgAssert(0, ("Can't find StreamFrameAmp %s data to free", Script::FindChecksumName(stream_checksum)));
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CStreamFrameAmpManager::sFreeFrameAmp(CStreamFrameAmp *p_fam_data)
{
	return p_fam_data->free();
}

}  // namespace Pcm


================================================
FILE: Code/Gel/Music/music.h
================================================
/*	Stream audio off the disc...
	Play music tracks or what not.
	
	md Jan 2001
*/

#ifndef __MUSIC_H__
#define __MUSIC_H__

#ifndef __GEL_OBJECT_H
#include 
#endif

#include 

// change the following line to compile out music or streams:
#define NO_MUSIC_PLEASE	0
#define DISABLE_STREAMS	0

namespace Obj
{
	class CStreamComponent;
}
			
namespace Pcm
{

// Forward declarations
class CStreamFrameAmp;
class CStreamFrameAmpManager;


#define NUM_CHANNELS				2

#define DEFAULT_MUSIC_VOLUME		50.0f
#define DEFAULT_MUSIC_STREAM_VOLUME	100.0f

#define MAX_NUM_TRACKS				80
#define MAX_TRACKNAME_STRING_LENGTH	40
#define TRACK_TITLE_MAX_SIZE		100

#define DEFAULT_STREAM_DROPOFF		DEFAULT_DROPOFF_DIST

#define STREAM_DEFAULT_PRIORITY		(50)		// Stream priority that is used when not set

enum EMusicStreamType
{
	MUSIC_STREAM_TYPE_NONE = 0,
	MUSIC_STREAM_TYPE_NORMAL,
	MUSIC_STREAM_TYPE_PRELOAD,
};

// Structures
struct CurrentStreamInfo
{
	uint32				controlID;		// ID used from script to control stream (default value is checksum of stream)
	uint32				uniqueID;		// ID used within code (same as controlID unless this stream is playing on more than 1 channel)
	float				volume;
	float				dropoff;
	int					voice;
	int					priority;
	uint32				start_frame;
	bool				use_pos_info;
	EDropoffFunc		dropoffFunction;
	CStreamFrameAmp *	p_frame_amp;
};

struct TrackInfo
{
	char trackName[ MAX_TRACKNAME_STRING_LENGTH ];
};

struct TrackTitle
{
	char trackTitle[ TRACK_TITLE_MAX_SIZE ];
};

struct TrackList
{
	// on PS2 version, the sceCdlFILE info is put into the end of this string:
	TrackInfo 		trackInfo[ MAX_NUM_TRACKS ];
	int				numTracks;
	// the following are bitflags:
	uint64			trackForbidden0;
    uint64			trackForbidden1;
	uint64			trackPlayed0;
    uint64			trackPlayed1;
	bool			allTracksForbidden;
};


void	Init( void );

bool NoMusicPlease();
bool StreamsDisabled();

int GetNumStreamsAvailable( void );
int GetCurrentTrack( void );
void SetCurrentTrack( int value );

// Call update every frame...
// this function checks once every second to see if the previous
// song finished playing... if so it starts a new song.
// Also handles Stop/Play commands.
void	Update( void );

void	PlayTrack( const char *filename, bool loop = false );
void	PlayMusicStream( uint32 checksum, float volume = -1 );		// Plays a stereo stream through the music channels

// These five only for XBox.
void UseUserSoundtrack( int soundtrack );
void UseStandardSoundtrack();
bool UsingUserSoundtrack();
void SaveSoundtrackToStructure( Script::CStruct *pStuff );
void GetSoundtrackFromStructure( Script::CStruct *pStuff );

uint32	PlayStream( uint32 checksum, Sfx::sVolume *p_volume, float pitch = 100, int priority = STREAM_DEFAULT_PRIORITY,
					uint32 controlID = 0, bool record_in_replay=true );
uint32	PlayStreamFromObject( Obj::CStreamComponent *pObject, uint32 streamNameChecksum, float dropoff = 0, float volume = 0,
							  float pitch = 100, int priority = STREAM_DEFAULT_PRIORITY, int use_pos_info = 0,
							  EDropoffFunc dropoffFunc = DROPOFF_FUNC_STANDARD, uint32 controlID = 0 );

// Control functions
bool	SetStreamVolumeFromID( uint32 streamID, Sfx::sVolume *p_volume );		// For non-object streams
bool	SetStreamPitchFromID( uint32 streamID, float pitch );					// For non-object streams
void	StopStreamFromID( uint32 streamID );
void	StopStreams( int channel = -1 );
bool	StreamAvailable( void );
bool	StreamAvailable( int whichStream );

bool	StreamExists( uint32 streamChecksum );

bool	StreamLoading( uint32 streamID );
bool	StreamPlaying( uint32 streamID );

// Preload streams.  By preloading, you can guarantee they will start at a certain time.
uint32	PreLoadStream( uint32 checksum, int priority = STREAM_DEFAULT_PRIORITY );
bool	PreLoadStreamDone( uint32 streamID );
bool	StartPreLoadedStream( uint32 streamID, Sfx::sVolume* p_volume, float pitch = 100 );

// Preload Music streams.  By preloading, you can guarantee they will start at a certain time.
bool	PreLoadMusicStream( uint32 checksum );
bool	PreLoadMusicStreamDone( void );
bool	StartPreLoadedMusicStream( float volume = -1 );

/*	Arg:	-1 to unpause only if it was unpaused last time pause was called
			1 to pause
			0 to unpause
*/
void	PauseMusic( int pause );
bool	MusicIsPaused();
void	PauseStream( int pause );

void	StopMusic( void );

// ambient tracks use the soundfx volume instead of music volume:
void	SetAmbientVolume( float volPercent );

void	SetVolume( float volume );
float	GetVolume( void );
void	SetMusicStreamVolume( float volume );
float	GetMusicStreamVolume( void );
void	GetPlaylist( uint64* flags1, uint64* flags2 );
void	SetPlaylist( uint64 flags1, uint64 flags2 );

enum{
		TRACKLIST_PERM,
		TRACKLIST_LEVEL_SPECIFIC,
		NUM_TRACKLISTS,
};

// Music track management:
void	AddTrackToPlaylist( const char *trackName, int whichList, const char *trackTitle = NULL );
void	ClearPlaylist( int whichList );

const char *GetTrackName( int trackNum, int whichList );
int		GetNumTracks( int whichList );

bool UsingCD( void );

// set a particular song as 'forbidden' (don't play it, the player hates that song...)
void	SetTrackForbiddenStatus( int trackNum, bool forbidden, int whichList );
//void	CheckLockedTracks( );
int		GetTrackForbiddenStatus( int trackNum, int whichList );
void	SkipMusicTrack( void );
void	SetRandomMode( int randomModeOn );
int		GetRandomMode( void );
void	SetLoopingMode( bool loop );

// Perm list? Or ambient list?
void	SetActiveTrackList( int whichList );
int		GetActiveTrackList( void );

bool LoadMusicHeader( const char *nameOfFile );
bool LoadStreamHeader( const char *nameOfFile );

///////////////////////////////////////////////////////////////////////////////////
// Pcm::CStreamFrameAmp

class CStreamFrameAmp
{
public:
	// Constants
	enum
	{
		vNOT_ALLOCATED			= 0,				// 0 indicates not allocated
		vMAX_SAMPLES			= 4096,
	};

								CStreamFrameAmp();
								~CStreamFrameAmp();

	void						Init();
	uint32						GetStreamNameCRC() const;

	uint8						GetSample(int frame) const;
	const uint8 *  				GetSamples() const;
	const uint8 *  				GetSamples(int frame) const;
	int							GetNumSamples() const;

protected:

	bool						load(uint32 stream_checksum);
	bool						free();
	bool						is_allocated() const;

	uint32						m_checksum;
	int							m_num_samples;
	uint8						m_frame_amp_samples[vMAX_SAMPLES] nAlign(128);	// Aligned data for the loading code

	// Friends
	friend CStreamFrameAmpManager;
};

///////////////////////////////////////////////////////////////////////////////////
// Pcm::CStreamFrameAmpManager
class CStreamFrameAmpManager
{
public:
	// Constants
	enum
	{
		vMAX_BUFFERS			= 1,
	};

	static CStreamFrameAmp *	sLoadFrameAmp(uint32 stream_checksum);
	static CStreamFrameAmp *	sGetFrameAmp(uint32 stream_checksum);
	static bool					sFreeFrameAmp(uint32 stream_checksum);
	static bool					sFreeFrameAmp(CStreamFrameAmp *p_fam_data);

protected:

	static CStreamFrameAmp		s_frame_amp_data[vMAX_BUFFERS];
};

///////////////////////////////////////////////////////////////////////////////////

inline uint32			CStreamFrameAmp::GetStreamNameCRC() const
{
	return m_checksum;
}

inline bool				CStreamFrameAmp::is_allocated() const
{
	return m_checksum != vNOT_ALLOCATED;
}


}  // namespace Pcm

#endif // __MUSIC_H__



================================================
FILE: Code/Gel/Music/ngc/bgm/bgm_com.c
================================================
// This code builds an IRX module that handles PCM streaming from
// the CD to SPU2 Sound Data Input area...

#include 
#include 
#include 
#include "sif.h"
#include "sifcmd.h"
#include "sifrpc.h"
#include 
#include "bgm_i.h"

int gRpcArg[16];	//--- Receiving channel for RPC arguments transferred from EE


static void* bgmFunc(unsigned int fno, void *data, int size);

extern int  BgmInit(int ch, int useCD);
extern void BgmQuit(int channel, int status );
extern int  BgmOpen( int ch, char* filename );
extern void BgmClose(int ch, int status);
extern void BgmCloseNoWait(int ch, int status);
extern int  BgmPreLoad( int ch, int status );
extern void BgmStart(int ch, int status);
extern void BgmStop(int ch, int status);
extern void BgmSeek( int ch, unsigned int vol );
extern int BgmSetVolume( int ch, unsigned int vol );
extern int BgmRaw2Spu( int ch, int which, int mode );
extern int BgmSetVolumeDirect( int ch, unsigned int vol );
// fuck this you big assbreath.
//extern void BgmSetMasterVolume( int ch, unsigned int vol );
extern unsigned int BgmGetMode( int ch, int status );
//extern void BgmSetMode( int ch, u_int mode );
extern void BgmSdInit(int ch, int status );


/* ------------------------------------------------------------------------
   Main thread for the ezbgm module.
   After execution, initialize interrupt environment, register command, and
   then wait until there is a request from the EE.
   ------------------------------------------------------------------------ */
int sce_bgm_loop()
{
	sceSifQueueData qd;
	sceSifServeData sd;

	//-- Initialize interrupt environment in advance.

	CpuEnableIntr();
	EnableIntr( INUM_DMA_4 );
	EnableIntr( INUM_DMA_7 );
	
	//--- Register function that is called according to request


	sceSifInitRpc(0);

	sceSifSetRpcQueue( &qd, GetThreadId() );
	sceSifRegisterRpc( &sd, EZBGM_DEV, bgmFunc, (void*)gRpcArg, NULL, NULL, &qd );
	PRINTF(("goto bgm cmd loop\n"));

	//--- Command-wait loop

	sceSifRpcLoop(&qd);

	return 0;
}


/* ------------------------------------------------------------------------
   Function that is awakened by a request from the EE.
   The argument is stored in *data.  The leading four bytes are reserved and are not used.
   The return value of this function becomes the return value of the EE side's RPC.
   When the argument is a structure, it is sent to gRpcData and used.
   When a structure is returned to the EE, the value is sent to the address of the first argument (on the EE side).
   ------------------------------------------------------------------------ */
int ret = 0;

static void* bgmFunc(unsigned int command, void *data, int size)
{ 
	int ch;

//	asm volatile( "break 1");

//	PRINTF(( " bgmfunc %x, %x, %x, %x\n", *((int*)data + 0), 
//		*((int*)data + 1), *((int*)data + 2),*((int*)data + 3) ));

	ch = command&0x000F;

	switch( command&0xFFF0 )
	{
	case EzBGM_INIT:
		ret = BgmInit( ch, *((int*)data) );
		break;
	case EzBGM_QUIT:
		BgmQuit ( ch, *((int*)data) );
		break;
	case EzBGM_OPEN:
		ret = BgmOpen ( ch, (char*)((int*)data) );
		break;
	case EzBGM_CLOSE:
		BgmClose( ch, *((int*)data) );
		break;
	case EzBGM_CLOSE_NO_WAIT:
		BgmCloseNoWait( ch, *((int*)data) );
		break;
	case EzBGM_PRELOAD:
		ret = BgmPreLoad ( ch, *((int*)data) );
		break;
	case EzBGM_START:
		BgmStart( ch, *((int*)data) );
		break;
	case EzBGM_STOP:
		BgmStop( ch, *((int*)data) );
		break;
	case EzBGM_SEEK:
		BgmSeek( ch, (unsigned int)*((int*)data) );
		break;
	case EzBGM_SETVOL:
		ret = BgmSetVolume( ch, (unsigned int)*((int*)data) );
		break;
	case EzBGM_SETVOLDIRECT:
		ret = BgmSetVolumeDirect( ch, (unsigned int)*((int*)data) );
		break;
	case EzBGM_GETMODE:
		ret = BgmGetMode( ch, *((int*)data) );
		break;
	case EzBGM_SDINIT:
		BgmSdInit( ch, *((int*)data) );
		break;
	default:
		ERROR(("EzBGM driver error: unknown command %d \n", *((int*)data) ));
		break;
	}
//	PRINTF(( "return value = %x \n", ret )); 
	return (void*)(&ret);
}


/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* DON'T ADD STUFF AFTER THIS */



================================================
FILE: Code/Gel/Music/ngc/bgm/bgm_entr.c
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "bgm_i.h"

ModuleInfo Module = {"pcmaudio_driver", 0x0102 };

extern int sce_bgm_loop();
extern volatile int gStThid;

int create_th();

int start ()
{
	struct ThreadParam param;
	int th;

	CpuEnableIntr();

	if( ! sceSifCheckInit() )
		sceSifInit();
	sceSifInitRpc(0);

	printf("Entering PCM Audio Driver\n");

	param.attr         = TH_C;
	param.entry        = sce_bgm_loop;
	param.initPriority = BASE_priority-2;
	param.stackSize    = 0x800;
	param.option       = 0;
	th = CreateThread(¶m);
	if (th > 0) {
		StartThread(th,0);
		printf("Exit PCM Audio Driver\n");
		return 0;
	}else{
		return 1;
	}
}


/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* DON'T ADD STUFF AFTER THIS */



================================================
FILE: Code/Gel/Music/ngc/bgm/bgm_i.h
================================================
//--- Highest bit indicates presence of return value
#define EzBGM_INIT           0x8000
#define EzBGM_QUIT           0x0010
#define EzBGM_OPEN           0x8020
#define EzBGM_CLOSE          0x0030
#define EzBGM_PRELOAD        0x0040
#define EzBGM_START          0x0050
#define EzBGM_STOP           0x0060
#define EzBGM_SEEK           0x0070
#define EzBGM_SETVOL         0x8080
#define EzBGM_SETVOLDIRECT   0x8090
#define EzBGM_CLOSE_NO_WAIT  0x0100
//#define EzBGM_SETMASTERVOL 0x00a0
#define EzBGM_GETMODE        0x80b0
#define EzBGM_SETMODE        0x80c0
#define EzBGM_SDINIT         0x00d0


//-- SET AVAILABLE
#define BGM_MODE_REPEAT_OFF      0x0000
#define BGM_MODE_REPEAT_DEFAULT  0x0001
#define BGM_MODE_REPEAT_FORCED   0x0002

#define BGM_MODE_STEREO          0x0000
#define BGM_MODE_MONO            0x0010

//-- GET ONLY
#define BGM_MODE_IDLE            0x0000
#define BGM_MODE_RUNNING         0x1000
#define BGM_MODE_PAUSE           0x2000
//#define BGM_MODE_KICKSTART       0x4000
#define BGM_MODE_TERMINATE       0x8000


#define BGM_MASK_STATUS          0x0FFF
#define BGM_MASK_REPEAT          0xFFF0
#define BGM_MASK_STEREO          0xFF0F

#define WAV_STEREO_BIT           ( 1 << 1 )


/* ----------------------------------------------
   module ID number
  ----------------------------------------------- */
#define EZBGM_DEV   0x000666


#define PRINTF(x) printf x
//#define PRINTF(x) 

#define ERROR(x) printf x
//#define ERROR(x) 

#define BASE_priority  42

#define OLDLIB 0
#define TRANS_CH  0



================================================
FILE: Code/Gel/Music/ngc/bgm/bgm_play.c
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "bgm_i.h"

#define DEBUGGING_PLEASE 0
#if DEBUGGING_PLEASE
#define Dbg_Printf(A...)  printf(##A) 		
#else
#define Dbg_Printf(A...)
#endif

// wav format ------------------------
#define RIFF_HEADER_SIZE 44
typedef struct {
	unsigned char     chunkID[4];
	unsigned int      chunkSize;
	unsigned short*   data;
} DATAHeader;

typedef struct {
	unsigned char     chunkID[4];
	unsigned int      chunkSize;
	unsigned short    waveFmtType;
	unsigned short    channel;
	unsigned int      samplesPerSec;
	unsigned int      bytesPerSec;
	unsigned short    blockSize;
	unsigned short    bitsPerSample;
} FMTHeader;

typedef struct {
	unsigned char     chunkID[4];
	unsigned int      chunkSize;
	unsigned char     formType[4];
	FMTHeader         fmtHdr;
	DATAHeader        dataHdr;
} RIFFHeader;
//------------------------------------

typedef struct {
	unsigned int     size;
//	unsigned int     offset;
	unsigned int     pos;
} BGM_WAVE;


BGM_WAVE gWave[2];

int gMemInitializedForCD = 0;
short gBgmPause[2] = { 0, 0 };
short gBgmPreload[ 2 ] = { 0, 0 };
char gFileFromCD[ 2 ] = { 0, 0 };
char gBgmVolumeSet[2] = { 0, 0 };
volatile char gFileOpened[2] = { 0, 0 };
volatile char gBgmIntr[2] = { 0, 0 };
int gThid = 0;
int gSem = 0;
int gFd[2];
int gBuffRaw[2];
int gBuffSpu[2] = {0, 0};
int gRPacketSize[2];
int gSPacketSize[2];
int gAllockedSize[2];
int gBgmVolume[2];
int gBgmMode[2] = { BGM_MODE_REPEAT_OFF, BGM_MODE_REPEAT_OFF };
int gBgmStereoMode[ 2 ] = { 0, 0 };

#define MAX_FILENAME_LENGTH		50
char gFilename[ 2 ][ MAX_FILENAME_LENGTH ];

// Larger buffers will prevent skipping when loading in the background...
// The more streams, the larger the buffers have to be.
// Reading from the CDVD will be faster than debug stations from the host over the network,
// so don't freak if you notice corruption on a dev system w/o CD.
#define SECTOR_SIZE					2048
#define NUM_SECTORS_PER_READ		2
#define BUFFER_SIZE_FOR_HOST_FILE	( 2048 * 132 ) // multiple of 6
#define BUFFER_SIZE_FOR_CD_FILE		( SECTOR_SIZE * NUM_SECTORS_PER_READ * 6 )

#define RING_BUFFER_NUM_SECTORS		48
#define RING_BUFFER_ALLIGNMENT		64
#define RING_BUFFER_NUM_PARTITIONS	3
unsigned char gRingBuffer[ 2 ][ RING_BUFFER_NUM_SECTORS * 2048 ] __attribute__((aligned ( RING_BUFFER_ALLIGNMENT )));
unsigned char gCDBuffSpu[ 2 ][ BUFFER_SIZE_FOR_CD_FILE ]__attribute__((aligned ( 64 )));

int  _BgmPlay( int status );
void _BgmRaw2Spu(u_long *src, u_long *dst,  u_long block_count );
void _BgmRaw2SpuMono(u_long *src, u_long *dst,  u_long block_count );
int	BgmPreLoad( int ch, int status );
void SetStereoOn( int ch, int stereo );

static int IntFunc0( void* common )
{
	gBgmIntr[0] = 1;
	iSignalSema( gSem );
	return 1;  //-- Required for re-enabling interrupts
}

static int IntFunc1( void* common )
{
	gBgmIntr[1] = 1;
	iSignalSema( gSem );
	return 1;  //-- Required for re-enabling interrupts
}


static int makeMyThread( void )
{
    struct ThreadParam param;
    int	thid;

    param.attr         = TH_C;
    param.entry        = _BgmPlay;
    param.initPriority = BASE_priority-3;
    param.stackSize    = 0x800;
    param.option = 0;

    /* Create thread */
	thid = CreateThread(¶m);

	return thid;
}

static int makeMySem( void )
{
    struct SemaParam sem;

    sem.initCount = 0;
    sem.maxCount = 1;
    sem.attr = AT_THFIFO;

    /* Create semaphore.  */
    return CreateSema(&sem);
}


int BgmRaw2Spu( int ch, int which )
{
    if ( gBgmStereoMode[ ch ] )
	{
		_BgmRaw2Spu( (u_long *)(gBuffRaw[ch]+ (gRPacketSize[ch])*which)
			, (u_long *)(gBuffSpu[ch] + (gSPacketSize[ch])*which)
			, gSPacketSize[ch]/1024 );
	}
	else
	{
		_BgmRaw2SpuMono ((u_long *)(gBuffRaw [ch] + (gRPacketSize [ch]) * which),
				 (u_long *)(gBuffSpu [ch] + (gSPacketSize [ch]) * which),
				 gSPacketSize [ch] / 1024);
    }
	return 0;
}

// returns zero if no longer playing...
int BgmSetVolumeDirect( int ch, unsigned int vol )
{
	sceSdSetParam( (ch)|SD_P_BVOLR, vol>>16 );
	sceSdSetParam( (ch)|SD_P_BVOLL, vol&0xFFFF);
	return ( gBgmMode[ch] & ~BGM_MASK_STATUS );
}



void BgmSdInit(int ch, int status )
{
	// This is already called from the soundFX module.
	//sceSdInit(0);
	return;
}


int BgmInit( int ch, int useCD )
{

	if( gSem == 0 ){
		gSem = makeMySem();
	}
	if( gThid == 0 ){
		gThid = makeMyThread();
		Dbg_Printf("PCM Audio Streamer: create thread ID= %d, ", gThid );
		/* Activate thread  */
		StartThread( gThid, (u_long)NULL );
	}

	if ( !gBuffSpu[ ch ] )
	{
		gAllockedSize[ch] = useCD ? BUFFER_SIZE_FOR_CD_FILE : BUFFER_SIZE_FOR_HOST_FILE;
		if ( useCD )
		{
			gBuffSpu[ ch ] = ( int )gCDBuffSpu[ ch ]; //(int)AllocSysMemory( 0, gAllockedSize[ ch ], NULL );
		}
		else
		{
			gBuffSpu[ ch ] = ( int )AllocSysMemory( 0, gAllockedSize[ ch ], NULL );
		}
		Dbg_Printf(" PCM spu buffer mem: 0x%x - 0x%x\n", gBuffSpu[ch], gBuffSpu[ch]+gAllockedSize[ ch ] );
	}
	// the memory should work for mono or stereo 44k files ( raw buffer always the same... )
	gRPacketSize[ ch ] = gAllockedSize[ ch ] / 6;
	gBuffRaw[ ch ] = ( int )( gBuffSpu[ ch ] + ( gRPacketSize[ ch ] * 4 ) );
	gMemInitializedForCD = useCD;

	return  gThid;
}

void BgmQuit(int ch, int status )
{
	if ( ( gBuffSpu[ ch ] ) && ( gBuffSpu[ ch ] != ( int ) gCDBuffSpu ) )
		FreeSysMemory( (void*)gBuffSpu[ch] );
	gBuffSpu[ch] = 0;
	//-- Frees resources unless another channel is used.
	if( gBuffSpu[1-ch] == 0 ){
		if (gThid != 0) TerminateThread( gThid );
		if (gSem != 0) DeleteSema( gSem );
		if (gThid != 0) DeleteThread( gThid );
		gThid = 0;
		gSem = 0;
	}
	return;
}

/*
void WaitForCDReady( char *description )
{
	printf( "wait %s", description );
	while( SCECdComplete != sceCdDiskReady( 1 ) )
	{
		printf( "." );
	}
	printf( "\n" );
}*/

#define NUM_BYTES_TO_CLEAR	512 // must be divisible by 4!!!

int BgmOpen( int data, char* filename )
{
//	unsigned int channel;
	RIFFHeader wavHdr;
	int offset;
	int *addr;
	int i;
	int ch = data & 1;
	int stereo = data & WAV_STEREO_BIT;
	
	SetStereoOn( ch, stereo );

	for ( i = 0; i < 2; i++ )
	{
		if ( gFileOpened[ i ] )
		{
			printf( "%s: Failed: Audio stream already running: %s\n", filename, gFilename[ i ] );
//			Dbg_Printf( "File already opened on that channel...\n" );
			return -1;
		}
	}
	printf("pcm filename %s\n", filename );
	
	if ( !strncmp( filename, "host", 4 ) )
	{
		if ( gMemInitializedForCD )
		{
			printf( "Loading file from host when mem configured for CD... SOUND QUALITY WILL SUCK.\n" );
			printf( "If calling ezBGM_INIT with useCD (2nd param), send CDROM0 in filename...\n" );
		}
		
		// load the file from the dev system...
		if (( gFd[ch] = open ( filename, O_RDONLY)) < 0)
		{
			ERROR(("file open failed. %s \n", filename));
			return -1;
		}
		
		if ( read (gFd[ch], (unsigned char*)(&wavHdr)
			,RIFF_HEADER_SIZE ) != RIFF_HEADER_SIZE ) {
			ERROR(("file read failed. %s \n", filename));
			return -1;
		}
		if ( wavHdr.fmtHdr.channel == 2 )
		{
			if ( !stereo )
				printf( "WARNING: Playing stereo sound %s in mono mode.\n", filename );
		}
		else if ( stereo )
		{
			printf( "WARNING: Playing mono sound %s in stereo mode.\n", filename );
		}
		
		gWave[ch].size   = wavHdr.dataHdr.chunkSize;
		gWave[ch].pos    = 0;
	
		//--- Seek to start of data
		offset = (unsigned int)&(wavHdr.dataHdr.data) -(unsigned int)&wavHdr;
	
		lseek( gFd[ ch ], offset , SEEK_SET );
	
		gFileFromCD[ ch ] = 0;
		gFileOpened[ ch ] = 1;
		gBgmPreload[ ch ] = 3;
	}
	else
	{
		sceCdlFILE fp;
		sceCdRMode mode;
		int retVal;
		unsigned int stringLength;
		unsigned int lsn;
		int foundCDData = 0;
		int byteIndex;
		// open a stream and load from CD...
		// 1st arg: size in num sectors ( actual size / 2048 ).
//		WaitForCDReady( "st init" );
//		sceCdDiskReady( 0 );
		sceCdStInit( RING_BUFFER_NUM_SECTORS, RING_BUFFER_NUM_PARTITIONS, ( u_int )( gRingBuffer[ ch ] ) ); //+ ( RING_BUFFER_ALLIGNMENT - 1 ) )  );
//		printf( "wait for ready." );
//		sceCdDiskReady( 0 );
//		WaitForCDReady( "init" );
//		printf( "cdSearchFile\n" );
		stringLength = strlen( filename );
		if ( filename[ stringLength + 1 ] == '@' )
		{
			foundCDData = 1;
			lsn = 0;
			for ( byteIndex = 0; byteIndex < 4; byteIndex++ )
			{
				lsn |= ( ( ( unsigned int )( filename[ stringLength + 2 + byteIndex ] ) ) & 255 ) << ( 8 * byteIndex );
			}
		}
		if ( !foundCDData )
		{
			if ( !( retVal = sceCdSearchFile( &fp, filename ) ) )
			{
				ERROR(("cd filesearch failed %d. file %s \n", retVal, filename));
				return -1;
			}
			lsn = fp.lsn;
		}
	
//		WaitForCDReady( "find" );
//		sceCdDiskReady( 0 );
		mode.trycount = 0;
		mode.spindlctrl = SCECdSpinNom;
		mode.datapattern = SCECdSecS2048;
		sceCdStStart( lsn, &mode );
		gFileFromCD[ ch ] = 1;
		gBgmPreload[ ch ] = 3;
		gFileOpened[ ch ] = 1;
		gWave[ ch ].pos = 0;
		gWave[ ch ].size = gRPacketSize[ ch ] * 4;
	}
	
//	printf("wave size %d  offset %d\n", gWave[ch].size, gWave[ch].offset );
	
	BgmSetVolumeDirect( ch, 0 );

	strncpy( gFilename[ ch ], filename, MAX_FILENAME_LENGTH  );

	// clear out the end of the second half of the last buffer...
	addr = ( int * )( gBuffRaw[ ch ] + ( gRPacketSize[ ch ] ) + ( gRPacketSize[ ch ] >> 1 ) );
	for ( i = 0; i < ( ( gRPacketSize[ ch ] >> 1 ) >> 2 ); i++ )
	{
		*( addr++ ) = 0;
	}
	
	if( ch == 0 )
	{
		sceSdSetTransCallback( ch, IntFunc0 );
	}
	else
	{
		sceSdSetTransCallback( ch, IntFunc1 );
	}
		
	return ( gWave[ ch ].size );
}

void _BgmClose(int ch, int status)
{
	if ( !gFileOpened[ ch ] )
	{
		PRINTF(("Calling BgmClose when no file opened on that channel...\n"));
		return;
	}
	Dbg_Printf( "_BgmClose\n" );
	if ( gFileFromCD[ ch ] )
	{
		sceCdStStop( );
	}
	else
	{
		close( gFd[ch] );
		//FreeSysMemory( ( void* )gBuffSpu[ ch ] );
		//gBuffSpu[ ch ] = 0;
	}
	gFileOpened[ ch ] = 0;
	return;
}

void BgmStart(int ch, int status )
{
	int retVal;
	if ( !gFileOpened[ ch ] )
	{
		printf( "trying to start track not loaded.\n" );
		return;
	}
	Dbg_Printf( "starting block trans from bgm start/unpause\n" );
	if ( gBgmPause[ ch ] )
	{
		Dbg_Printf( "Starting music on channel where pause is requested...\n" );
		gBgmPause[ ch ] = 0;
	}
	while( -1 == sceSdBlockTrans( ch, SD_TRANS_MODE_WRITE|SD_BLOCK_LOOP, (u_char*)gBuffSpu[ch], (gSPacketSize[ch]*2) ) )
	{
		for ( retVal = 0; retVal < 10000; retVal++ )
			;
		Dbg_Printf( "failed to start loop.\n" );
	}
	if ( !( gBgmPreload[ ch ] ) )
	{
		BgmSetVolumeDirect(ch, gBgmVolume[ch]);
	}
	gBgmMode[ch] &= BGM_MASK_STATUS;
	gBgmMode[ch] |= BGM_MODE_RUNNING;

	return;
}

void BgmCloseNoWait( int ch, int status )
{
	if ( !gFileOpened[ ch ] )
	{
		PRINTF(("Calling BgmClose when no file opened on that channel...\n"));
		return;
	}
	if ( gBgmPause[ ch ] )
	{
		printf( "stopping music when pause is requested..." );
		return;
	}
	// if the pause has already stopped the callback, it's safe to unload now:
	if ( gBgmMode[ ch ] & BGM_MODE_PAUSE )
	{
		// callback isn't happening right now... can terminate with no worries here:
		Dbg_Printf( "unpausing from BgmClose\n" );
		gBgmMode[ch] &= BGM_MASK_STATUS; // Switch to IDLE mode
		_BgmClose( ch, status );
		Dbg_Printf( "calling _BgmClose from BgmClose\n" );
		return;
	}
	gBgmMode[ch] &= BGM_MASK_STATUS;
	gBgmMode[ch] |= BGM_MODE_TERMINATE;
}

void BgmClose( int ch, int status )
{
	int i;
	BgmCloseNoWait( ch, status );
	printf( "PCM close" );
	while ( ( gBgmMode[ ch ] & BGM_MODE_TERMINATE ) )
	{
		for ( i = 0; i < 20000; i++ )
			;
		printf( "." );
	}
	printf( "\n" );
}

int BgmPreLoad( int ch, int status )
{
	if ( ( status == 666 ) || ( ( gWave[ ch ].size - gWave[ ch ].pos ) < gRPacketSize[ ch ] * 4 ) )
	{
		int i;
		int *addr;
		Dbg_Printf( "preloading at end of song. zero buffers.\n" );
		// we're at the end of the song... fill up read buffers with zero,
		// and convert...
		addr = ( int * )( gBuffRaw[ ch ] );
		for ( i = 0; i < ( ( gRPacketSize[ ch ] * 2 ) >> 2 ); i++ )
		{
			*( addr++ ) = 0;
		}
		// fill up the spu with both buffers...
		BgmRaw2Spu( ch, 0 );
		BgmRaw2Spu( ch, 1 );
		return ( 0 );
	}
	
	// it's still in preload mode... no need to preload ourselves...
	if ( gBgmPreload[ ch ] == 3 )
		return ( 0 );

	if ( gFileFromCD[ ch ] )
	{
		unsigned int err;
		sceCdStRead( NUM_SECTORS_PER_READ * 2, ( u_int * )gBuffRaw[ ch ], STMBLK, &err );
		if ( err )
		{
			printf( "PCM A Disk error code 0x%08x\n", err );
		}

		// fill up the spu with both buffers...
		BgmRaw2Spu( ch, 0 );
		BgmRaw2Spu( ch, 1 );
		
		// fill up the buffers again...
		sceCdStRead( NUM_SECTORS_PER_READ * 2, ( u_int * )gBuffRaw[ ch ], STMBLK, &err );
		if ( err )
		{
			printf( "PCM B Disk error code 0x%08x\n", err );
		}
		
		gWave[ ch ].pos += gRPacketSize[ ch ] * 2;
		return 0;
	}
	// reading from a file...
	if ( read (gFd[ch], (unsigned char*)(gBuffRaw[ch])
	, gRPacketSize[ch]*2 ) != gRPacketSize[ch]*2 ) {
		ERROR (("BgmPreLoad: read failed \n")); return -1;
	}

	BgmRaw2Spu( ch, 0 );
	BgmRaw2Spu( ch, 1 );

	if ( read (gFd[ch], (unsigned char*)(gBuffRaw[ch])
	, (gRPacketSize[ch]*2) ) != gRPacketSize[ch]*2) {
		ERROR (("BgmPreLoad: read failed \n")); return -1;
	}

	gWave[ch].pos += gRPacketSize[ch]*4;

	return 0;
}

void _BgmPause(int ch, int status)
{
	Dbg_Printf( "stopping spu block trans in _BgmPause\n" );
	sceSdBlockTrans( ch, SD_TRANS_MODE_STOP, NULL, 0 );
	gBgmMode[ch] &= BGM_MASK_STATUS; // Switch to PAUSE mode
	gBgmMode[ch] |= BGM_MODE_PAUSE;
	return;
}

void _BgmStopAndUnload(int ch, int status)
{
	Dbg_Printf( "stopping block trans from _BgmStopAndUnload\n" );
	sceSdBlockTrans( ch, SD_TRANS_MODE_STOP, NULL, 0 );
	gBgmMode[ch] &= BGM_MASK_STATUS; // Switch to IDLE mode
	Dbg_Printf( "bgm close from _BgmStopAndUnload\n" );
	_BgmClose( ch, status );
	return;
}

// should be fucking called "BgmPause"
void BgmStop( int ch, unsigned int vol )
{
	int i;

	if ( gBgmMode[ ch ] & BGM_MODE_TERMINATE )
	{
		Dbg_Printf( "trying to pause PCM when stopped.\n" );
		return;
	}
	if ( !gBgmMode[ ch ] & BGM_MODE_RUNNING )
	{
		Dbg_Printf( "trying to pause PCM when not running.\n" );
		return;
	}
	gBgmPause[ch] = 1;
	printf( "waiting for pause" );
	while ( !( gBgmMode[ ch ] & BGM_MODE_PAUSE ) )
	{
		for ( i = 0; i < 10000; i++ )
			;
		printf( "." );
	}
	printf( "\n" );
	// don't set the flag until the callback has paused it:
//	gBgmMode[ch] &= BGM_MASK_STATUS;
//	gBgmMode[ch] |= BGM_MODE_PAUSE;
	return;
}


int BgmSetVolume( int ch, unsigned int vol )
{
	gBgmVolumeSet[ch] = 1;
	gBgmVolume[ch] = vol;
	return ( gBgmMode[ch] & ~BGM_MASK_STATUS );
}


unsigned int BgmGetMode( int ch, int status )
{
	return gBgmMode[ch];
}

void BgmSeek( int ch, unsigned int value )
{
//	lseek(gFd[ch], gWave[ch].offset+value, SEEK_SET );
//	gWave[ch].pos = value;
	printf( "seek not supported\n" );
	return;
}

void SetStereoOn( int ch, int stereo )
{
    if ( stereo )
	{
		gSPacketSize[ ch ] = gAllockedSize[ ch ] / 6;
		gBgmStereoMode[ ch ] = 1;
	}
	else
	{
		gSPacketSize[ ch ] = gAllockedSize [ ch ] / 3;
		gBgmStereoMode[ ch ] = 0;
	}
}

void PreloadFile( int which, int ch )
{
	switch ( gBgmPreload[ ch ] )
	{
		case ( 3 ):
			if ( gFileFromCD[ ch ] )
			{
				RIFFHeader *pWavHdr;
				int i;
				int offset;
				unsigned char *pData;
				pWavHdr = ( RIFFHeader * )( gBuffRaw[ ch ]+gRPacketSize[ch]*which );
				if ( pWavHdr->dataHdr.chunkSize < gWave[ ch ].size )
				{
					printf( "BIG WARNING:  Cd audio file currently playing is TOO SMALL!\n" );
				}
				if ( pWavHdr->fmtHdr.channel == 2 )
				{
					if ( !gBgmStereoMode[ ch ] )
						printf( "WARNING: Playing stereo sound %s in mono mode.\n", gFilename[ ch ] );
				}
				else if ( gBgmStereoMode[ ch ] )
				{
					printf( "WARNING: Playing mono sound %s in stereo mode.\n", gFilename[ ch ] );
				}
				
				SetStereoOn( ch, pWavHdr->fmtHdr.channel == 2 );
				gWave[ch].size   = pWavHdr->dataHdr.chunkSize;
				//gWave[ch].offset = (unsigned int)&( pWavHdr->dataHdr.data ) - ( unsigned int )pWavHdr;
				offset = (unsigned int)&( pWavHdr->dataHdr.data ) - ( unsigned int )pWavHdr;
				// clear out the area where the header is... don't want some loud obnoxious sounds...
				pData = ( unsigned char * )( gBuffRaw[ ch ]+gRPacketSize[ch]*which );
				if ( offset > gRPacketSize[ ch ] )
					offset = gRPacketSize[ ch ]; // don't fuck up memory if some strange read happened...
				for ( i = 0; i < offset; i++ )
				{
					pData[ i ] = 0;
				}
			}
			break;
		
		case ( 2 ):
			break;
		
		case ( 1 ):
			// turn the volume back up, the spooler is at the beginning of
			// the good SPU buffer...
			BgmSetVolume( ch, gBgmVolume[ch] );
			break;
		
		default:
			printf( "unknown preload state... fire Matt.\n" );
			break;
	}
	--gBgmPreload[ ch ];
}

int _BgmPlay( int status )
{
	int i, ch, read_size, which;
	int *addr, remain;
	Dbg_Printf( "entering _BgmPlay\n" );
	while ( 1 )
	{
		//-- Wait for playing of buffer to finish
		WaitSema(gSem);

		//-- Which channel is the interrupt from?
		if( (gBgmIntr[0] == 1) && ( ch != 0 ) )  ch = 0;
		else if( (gBgmIntr[1] == 1) && ( ch != 1 ) )  ch = 1;
		else if( gBgmIntr[0] == 1 )  ch = 0;
		else if( gBgmIntr[1] == 1 )  ch = 1;
		else continue;

		gBgmIntr[ch] = 0;

		which = 1 - (sceSdBlockTransStatus( ch, 0 )>>24);

		//--- Stopped due to end of data (no looping)
		if( ( gBgmMode[ ch ] & BGM_MODE_TERMINATE ) != 0 )
		{
			WaitSema( gSem ); // Wait until another interrupt is received
			_BgmStopAndUnload( ch, 0 );
			BgmSetVolumeDirect( ch, 0x0 );
			continue;
		}

		//--- Volume change event
		if ( ( gBgmVolumeSet[ ch ] == 1 ) && ( !gBgmPreload[ ch ] ) )
		{
			BgmSetVolumeDirect( ch, gBgmVolume[ch] );
			gBgmVolumeSet[ch] = 0;
		}

		//--- Convert buffer

		BgmRaw2Spu( ch, which );

		//--- File READ for buffer

		remain = gWave[ ch ].size - gWave[ ch ].pos;
		if ( remain > gRPacketSize[ch] )
		{
			if ( gFileFromCD[ ch ] )
			{
				//--- Not end of data
				unsigned int err;
				sceCdStRead( NUM_SECTORS_PER_READ, ( u_int * )( gBuffRaw[ ch ]+gRPacketSize[ch]*which ), STMBLK, &err );
				if ( err )
				{
					printf( "PCM C Disk error code 0x%08x\n", err );
				}
				read_size = gRPacketSize[ ch ];
			}
			else
			{
				read_size = read (gFd[ch], (unsigned char*)(gBuffRaw[ch]+gRPacketSize[ch]*which), gRPacketSize[ch] );
			}
			if ( read_size < gRPacketSize[ch] )
				continue; //retry
			if ( gBgmPreload[ ch ] )
			{
				PreloadFile( which, ch );
			}
			gWave[ch].pos += read_size;
		}
		else  //--- End of data
		{
			if ( gFileFromCD[ ch ] )
			{
				//--- Not end of data
				unsigned int err;
				sceCdStRead( NUM_SECTORS_PER_READ, ( u_int * )( gBuffRaw[ ch ]+gRPacketSize[ch]*which ), STMBLK, &err );
				if ( err )
				{
					printf( "PCM C Disk error code 0x%08x\n", err );
				}
				read_size = gRPacketSize[ ch ];
			}
			else
			{
				read_size = read (gFd[ch], (unsigned char*)(gBuffRaw[ch]+gRPacketSize[ch]*which), remain );
			}
			
			if( read_size < remain ) continue; //retry
	    	
			PRINTF(("end of PCM track - ch %d\n", ch));

			addr = ( int * )( gBuffRaw[ ch ] + ( gRPacketSize[ ch ] * which ) + remain );
			for( i = 0; i < ((gRPacketSize[ch]-remain)>>2); i++ )
			{
				*(addr++) = 0;
			}
			gBgmMode[ch] &= BGM_MASK_STATUS;
			gBgmMode[ch] |= BGM_MODE_TERMINATE;
		}

		//-- Stop event
		if( (gBgmPause[ch] == 1) )
		{
			//printf( "_BgmPlay :: pause\n" );
			_BgmPause( ch, 0 );
			BgmSetVolumeDirect(ch, 0x0);
			gBgmPause[ch] = 0;
		}
	}
	return 0;
}

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* This file ends here, DON'T ADD STUFF AFTER THIS */

// Ha ha I added some stuff... FUCK YOU SONY!


================================================
FILE: Code/Gel/Music/ngc/bgm/bgm_r2s.s
================================================
/* SCEI CONFIDENTIAL
 "PlayStation 2" Programmer Tool Runtime Library  Release 2.0
 */
/* 
 *                  I/O Proseccor sample program
 *                          Version 1.20
 *                           Shift-JIS
 *
 *      Copyright (C) 1998-1999 Sony Computer Entertainment Inc.
 *                        All Rights Reserved.
 *
 *                       ezbgm.irx - bgm_r2s.c
 *                          raw to spu pcm
 *
 *   Version   Date            Design    Log
 *  --------------------------------------------------------------------
 *   1.20      Nov.23.1999     morita    modify for EzBGM
 *   0.01      Nov.18.1999     ishii     optimize for IOP
 */

#include 

#define src  a0
#define dst  a1
#define blk  a2
#define cnt  a3

	.globl	_BgmRaw2Spu
	.ent	_BgmRaw2Spu
_BgmRaw2Spu:
	subu	sp, (8*4)
	sw	s0, 0*4(sp)	; 	sw	s1, 1*4(sp)
	sw	s2, 2*4(sp)	; 	sw	s3, 3*4(sp)
	sw	s4, 4*4(sp)	; 	sw	s5, 5*4(sp)
	sw	s6, 6*4(sp)	; 	sw	s7, 7*4(sp)
	move	v0, zero
	move	v1, zero
	addi	cnt,zero, 256

					.set noreorder
	/* AŖ߂̂Ȃт𐧌B */
pcm_separate_loop:
	lw	t0, 0*4(src)	/* 1 + 4 clock */
	lw	t1, 1*4(src)	/* 1 + 2 clock */

	/* 7 clock */
	and	s0, t0, 0xffff		/* t0 ̉ʂ s0  */
	srl	s1, t0, 16		/* t0 ̏ʂ s1  */
	sll	t0, t1, 16		/* t1 ̉ʂɃVtg s0  */
	or	s0, t0
	srl	t1, t1, 16		/* t1 ̏ʂ}XN s1  */
	sll	t1, t1, 16
	or	s1, t1

	/* ɃLbVɓǂݍ܂Ă͂ */
	lw	t2, 2*4(src)	/* 1 clock */
	lw	t3, 3*4(src)	/* 1 clock */
	/* ̃LbVCǂݍނ */
	lw	t4, 4*4(src)	/* 1 + 4 clock */

	/* 7 clock */
	and	s2, t2, 0xffff		/* t2 ̉ʂ s2  */
	srl	s3, t2, 16		/* t2 ̏ʂ s3  */
	sll	t2, t3, 16		/* t3 ̉ʂɃVtg s2  */
	or	s2, t2
	srl	t3, t3, 16		/* t3 ̏ʂ}XN s3  */
	sll	t3, t3, 16
	or	s3, t3

	/* ɃLbVɓǂݍ܂Ă͂ */
	lw	t5, 5*4(src)	/* 1 clock */
	lw	t6, 6*4(src)	/* 1 clock */
	lw	t7, 7*4(src)	/* 1 clock */
	add	src, 8*4	/* 1 clock */

	/* 7 clock */
	and	s4, t4, 0xffff		/* t4 ̉ʂ s4  */
	srl	s5, t4, 16		/* t4 ̏ʂ s5  */
	sll	t4, t5, 16		/* t5 ̉ʂɃVtg s4  */
	or	s4, t4
	srl	t5, t5, 16		/* t5 ̏ʂ}XN s5  */
	sll	t5, t5, 16
	or	s5, t5

	/* store  dst1(s0, s2),  dst2(s1, s3) */
	/* 4 clock */

	sw	s0, 0*4(dst)	;	sw	s2, 1*4(dst)
	sw	s1, (0*4+512)(dst); 	sw	s3, (1*4+512)(dst)
	/* ȉƕs +2, 4+2 clock ŏ܂͂ */

	/* 7 clock */
	and	s6, t6, 0xffff		/* t6 ̉ʂ s6  */
	srl	s7, t6, 16		/* t6 ̏ʂ s7  */
	sll	t6, t7, 16		/* t7 ̉ʂɃVtg s6  */
	or	s6, t6
	srl	t7, t7, 16		/* t7 ̏ʂ}XN s7  */
	sll	t7, t7, 16
	or	s7, t7

	/* store  dst1(s4, s6),  dst2(s5, s7) */
	/* 4 clock */
	sw	s4, 2*4(dst)	; 	sw	s6, 3*4(dst)
	sw	s5, (2*4+512)(dst)	; 	sw	s7, (3*4+512)(dst)
	/* ȉƕs +2, 4+2 clock ŏ܂͂ */

	add	dst, 4*4		/* 1 clock */


	/* ܂ŁAŖ߂̂Ȃт𐧌B */
					.set reorder

	add	v0, 8			/* 1 clock */
	blt	v0, cnt, pcm_separate_loop	/* 1 clock */

	add	dst, 512
	move	v0, zero

	add	v1, 1
	blt	v1, blk, pcm_separate_loop

	lw	s0, 0*4(sp)	; 	lw	s1, 1*4(sp)
	lw	s2, 2*4(sp)	; 	lw	s3, 3*4(sp)
	lw	s4, 4*4(sp)	; 	lw	s5, 5*4(sp)
	lw	s6, 6*4(sp)	; 	lw	s7, 7*4(sp)
	addu	sp, (8*4)
	j	ra
	.end	_BgmRaw2Spu



================================================
FILE: Code/Gel/Music/ngc/bgm/bgm_r2sm.c
================================================
#include 

#define SPU_BLOCK 512

// Using SP will speed things up, but ...

void _BgmRaw2SpuMono( unsigned int *src, unsigned int *dst, unsigned int block )
{
	int i;

	for ( i = 0; i < block; i++ )
	{
		memcpy( (void*)((int)dst+i*SPU_BLOCK*2), (void*)((int)src+i*SPU_BLOCK), SPU_BLOCK );
		memcpy( (void*)((int)dst+i*SPU_BLOCK*2+SPU_BLOCK), (void*)((int)src+i*SPU_BLOCK) , SPU_BLOCK );
	}

	return;
}




================================================
FILE: Code/Gel/Music/ngc/divx/AUDSimpleAudio.cpp
================================================
#ifndef DVDETH
/*!
 ******************************************************************************
 * \file AUDSimpleAudio.cpp
 *
 * \brief
 *		This file provides the required player audio functions.
 *
 * \note
 *      This is a demonstration source only!
 *
 * \date
 *		08/19/02
 *		04/28/03 - updated to AX audio playback
 *
 * \version
 *		1.0
 *
 * \author
 *		Thomas Engel
 *
 ******************************************************************************
 */

//
// Note about sample frequencies on Nintendo GameCube:
//
// GameCube's AI knows two native sample frequencies. Generally these are
// referenced as 48KHz and 32KHz. The DSP (no matter if driven by MusyX or AX)
// is using 32KHz.
//
// The frequencies that are in fact used are not exactly these frequencies, though!
//
// While this is not important when handling sound effects or even streamed audio,
// it is very important when interlaeving audio and video data since buffer under-runs
// might be triggered if no care is taken.
//
// The actual AI output frequencies are 48043Hz and 32028.66Hz.
//
// Streamed audio data interleaved with video should match these frequencies as
// closely as possible.
//

/******************************************************************************
 *  INCLUDES
 ******************************************************************************/
#include 
#include 

#include "AUDSimplePlayer.h"
#include "AUDSimpleAudio.h"
#include "gel/soundfx/ngc/p_sfx.h"
#include "gfx/ngc/nx/nx_init.h"

/******************************************************************************
 *  DEFINES
 ******************************************************************************/

// Number of audio frames that should be able to be stored prio to being routed into the AX buffer
#define	AUD_AUDIO_READAHEADFRAMES	0.5f

#define	AUD_AUDIO_AIBUFFERSAMPLES	(2*256)		// 10.6ms of 48KHz data per AI buffer (32 byte multiple), that'll be about 15.9ms at 32Khz
#define	AUD_AUDIO_NUMAIBUFFERS		2			// Number of AI playback buffers (this has an impact on audio latency. 2 is the minimum needed)

#define	AX_ARAM_BUFFER_SIZE			(AUD_AUDIO_AIBUFFERSAMPLES * AUD_AUDIO_NUMAIBUFFERS)
//#define	AX_ARAM_LEFT_CHANNEL		0x200000	// @ 4MB (16-Bit addressing for DSP!)
//#define	AX_ARAM_RIGHT_CHANNEL		(AX_ARAM_LEFT_CHANNEL + AX_ARAM_BUFFER_SIZE)
#define	AX_ARAM_LEFT_CHANNEL		( NxNgc::EngineGlobals.aram_music >> 1 )
#define	AX_ARAM_RIGHT_CHANNEL		(AX_ARAM_LEFT_CHANNEL + AX_ARAM_BUFFER_SIZE)

#define	AUD_NUM_ARQ_REQUESTS		16

/******************************************************************************
 *  LOCAL VARIABLES & LOCAL EXTERNAL REFERENCES
 ******************************************************************************/

extern AudSimplePlayer	audio_player;								// Player instance

static void				*audioReadBuffer;							// Buffer to store audio data received from the data stream
static u32				audioReadBufferNumSamples;					// Size of the above buffer in samples
static u32				audioReadBufferWritePos;					// Write position in the above buffer in samples
static u32				audioReadBufferReadPos;						// Read position in the above buffer in samples
static u8				audioReadBufferNumChannels;					// Number of channels stored in the read buffer

static void				*audioPlayBuffer[AUD_AUDIO_NUMAIBUFFERS];	// AI playback buffers
static u8				audioPlayBufferWriteIndex;					// Index to next AI buffer to be written to from the read buffer
static u32				audioPlayBufferFrq;							// Playback frequency of AI in Hz
static volatile BOOL	audioPlayBufferEnabled;						// TRUE if playback is enabled. AI will operate at all times, but zeros will be filled in instead of real data if this is FALSE.

static const u32		*audioPlayMaskArray;						// Pointer to an array of channel play masks (each set bit signals an active channel)
static u32				audioNumPlayMasks;							// Number of play masks specified (0 indicates all channels should be active)
static u32				audioNumActiveVoices;						// Number of active voices

static AXVPB			*axVoice[2];								// AX voice structures
static ARQRequest		arqRequest[2][AUD_NUM_ARQ_REQUESTS];		// Enough ARQ request structures for worst case scenario
static u32				axLastAddr;									// Last known address DSP read from for 1st voice
static u32				axPlayedSamples;							// Number of samples played on first voice since last update
static u32				axPlayedSamplesTotal;						// Number of samples played on first voice since playback began

typedef enum VID_AXPHASE {
		AX_PHASE_STARTUP = 0,
		AX_PHASE_START,
		AX_PHASE_PLAY
		} VID_AXPHASE;

static VID_AXPHASE		axPhase;


/******************************************************************************
 *  LOCAL PROTOTYPES
 ******************************************************************************/

static void AXCallback(void);

/*!
 ******************************************************************************
 * \brief
 *		Initialize audio decoder
 *
 *		This function allocates all neccessary memory for the audio processing
 *		and sets the audio decoder into an idle state, waiting for first data.
 *		A file must be opened with the VIDSimplePlayer before calling this
 *		function.
 *
 * \return
 *		FALSE if any problem was detected
 *
 ******************************************************************************
 */
BOOL AUDSimpleInitAudioDecoder(void)
	{
	u32	 		i, ratio;
	BOOL 		old;
	AXPBMIX		axMix[2];
	AXPBVE 		axVE;
	AXPBSRC		axSRC;
	AXPBADDR 	axAddr;
	AXPBADPCM 	axADPCM;

	// Calculate buffer size to allocate proper memry to keep a bit of "extra" audio data around...
	audioReadBufferNumSamples = (u32)((f32)AUD_AUDIO_READAHEADFRAMES * audio_player.audioInfo.vaud.frq);
	audioReadBufferNumChannels = audio_player.audioInfo.vaud.numChannels <= 2 ? audio_player.audioInfo.vaud.numChannels : 2;

	// Allocate read buffer
	audioReadBuffer = audio_player.cbAlloc(audioReadBufferNumSamples * sizeof(s16) * audio_player.audioInfo.vaud.numChannels);
	if (audioReadBuffer == NULL)
		return FALSE;					// error
	
	// Reset ring buffer
	audioReadBufferReadPos = audioReadBufferWritePos = 0;

	// What frquency is best?
	audioPlayBufferFrq = audio_player.audioInfo.vaud.frq;
	
	// Allocate AI playback buffer
	audioPlayBuffer[0] = audio_player.cbAlloc(2 * sizeof(s16) * AUD_AUDIO_AIBUFFERSAMPLES * AUD_AUDIO_NUMAIBUFFERS);
	if (audioPlayBuffer[0] == NULL)
		return FALSE;					// error
	
	for(i=1; i> 16);
	axSRC.ratioLo = (u16)ratio;
	
	AXSetVoiceSrcType(axVoice[0],AX_SRC_TYPE_4TAP_16K);
	AXSetVoiceSrc(axVoice[0],&axSRC);
	AXSetVoiceSrcType(axVoice[1],AX_SRC_TYPE_4TAP_16K);
	AXSetVoiceSrc(axVoice[1],&axSRC);
	
	*(u32 *)&axAddr.currentAddressHi = AX_ARAM_LEFT_CHANNEL;
	*(u32 *)&axAddr.loopAddressHi = AX_ARAM_LEFT_CHANNEL;
	*(u32 *)&axAddr.endAddressHi = AX_ARAM_LEFT_CHANNEL + AX_ARAM_BUFFER_SIZE - 1;
	axAddr.format = AX_PB_FORMAT_PCM16;
	axAddr.loopFlag = AXPBADDR_LOOP_ON;
	AXSetVoiceAddr(axVoice[0],&axAddr);
	
	*(u32 *)&axAddr.currentAddressHi = AX_ARAM_RIGHT_CHANNEL;
	*(u32 *)&axAddr.loopAddressHi = AX_ARAM_RIGHT_CHANNEL;
	*(u32 *)&axAddr.endAddressHi = AX_ARAM_RIGHT_CHANNEL + AX_ARAM_BUFFER_SIZE - 1;
	AXSetVoiceAddr(axVoice[1],&axAddr);

	memset(&axADPCM,0,sizeof(axADPCM));
	axADPCM.gain = 0x0800;
	
	AXSetVoiceAdpcm(axVoice[0],&axADPCM);
	AXSetVoiceAdpcm(axVoice[1],&axADPCM);
	
	AXSetVoiceType(axVoice[0],AX_PB_TYPE_STREAM);
	AXSetVoiceType(axVoice[1],AX_PB_TYPE_STREAM);
	
	AXRegisterCallback( AXCallback );
	
	axLastAddr				= AX_ARAM_LEFT_CHANNEL;
	axPlayedSamples			= AUD_AUDIO_AIBUFFERSAMPLES * AUD_AUDIO_NUMAIBUFFERS;
	axPlayedSamplesTotal	= 0;
	axPhase					= AX_PHASE_STARTUP;
	
	// All is setup for the voices. We'll start them inside the AX callback as soon as we got data in the ARAM buffers
	OSRestoreInterrupts(old);

	return TRUE;
    }

/*!
 ******************************************************************************
 * \brief
 *		Shutdown audio decoder and free resources
 *
 ******************************************************************************
 */
void AUDSimpleExitAudioDecoder(void)
	{
	// Yes. Unregister callback & stop AI DMA
	BOOL old = OSDisableInterrupts();
	
	// Register our default AX callback.
	AXRegisterCallback( Sfx::AXUserCBack );
	
	AXSetVoiceState(axVoice[0],AX_PB_STATE_STOP);
	AXSetVoiceState(axVoice[1],AX_PB_STATE_STOP);
	AXFreeVoice(axVoice[0]);
	AXFreeVoice(axVoice[1]);
	
	axVoice[0] = axVoice[1] = NULL;
	
	OSRestoreInterrupts(old);

	// Free allocated resources...
	audio_player.cbFree(audioPlayBuffer[0]);
	audio_player.cbFree(audioReadBuffer);
	}

/*!
 ******************************************************************************
 * \brief
 *		Stop audio playback without shutting down AI etc.
 *
 ******************************************************************************
 */
void AUDSimpleAudioReset(void)
	{
	BOOL	old;

	old = OSDisableInterrupts();
	
	audioReadBufferWritePos = 0;
	audioReadBufferReadPos = 0;
	
	audioPlayBufferEnabled = FALSE;
		
	OSRestoreInterrupts(old);
	}

/*!
 ******************************************************************************
 * \brief
 *		Return some information about current audio stream.
 *
 ******************************************************************************
 */
void AUDSimpleAudioGetInfo(VidAUDH* audioHeader)
{
	ASSERT(audioHeader);
	memcpy(audioHeader, &audio_player.audioInfo, sizeof(*audioHeader));
}



void AUDSimpleAudioSetVolume( u16 vl, u16 vr )
{
	AXPBVE axVE;
	
	axVE.currentDelta	= 0;
	axVE.currentVolume	= vl;
	AXSetVoiceVe( axVoice[0], &axVE );
	
	axVE.currentVolume	= vr;
	AXSetVoiceVe( axVoice[1], &axVE );
}



/*!
 ******************************************************************************
 * \brief
 *
 ******************************************************************************
 */
static void writeChannelData(s16* dest, u32 channels, const s16** samples, u32 sampleOffset, u32 sampleNum)
	{
    u32 j;
	if(channels == 1)
		{
		const s16* in = samples[0] + sampleOffset;
		for(j = 0; j < sampleNum; j++)
			*dest++ = in[j];
		}
	else
		{
		const s16* inL;
		const s16* inR;

		ASSERT(channels == 2);
        inL = samples[0] + sampleOffset;
		inR = samples[1] + sampleOffset;
        for(j = 0; j < sampleNum; j++)
			{
			*dest++ = *inL++;
			*dest++ = *inR++;
			}
		}
	}


u32 AUDSimpleAudioGetFreeReadBufferSamples( void )
{
	u32 freeSamples;
	
	if( audioReadBufferWritePos >= audioReadBufferReadPos )
	{
		freeSamples = audioReadBufferNumSamples - (audioReadBufferWritePos - audioReadBufferReadPos);
	}
	else
	{
		freeSamples = audioReadBufferReadPos - audioReadBufferWritePos;
	}
	return freeSamples;
}





/*!
 ******************************************************************************
 * \brief
 *		Main audio data decode function.
 *
 *		This function will be used as a callback from the VIDAudioDecode
 *		function or is called directely in case of PCM or ADPCM.
 *
 * \param numChannels
 *		Number of channels present in the sample array
 *
 * \param samples
 *		Array of s16 pointers to the sample data
 *
 * \param sampleNum
 *		Number of samples in the array. All arrays have the same amount
 *		of sample data
 *
 * \param userData
 *		Some user data
 *
 * \return
 *		FALSE if data could not be interpreted properly
 *
 ******************************************************************************
 */
static BOOL audioDecode(u32 numChannels, const s16 **samples, u32 sampleNum, void* _UNUSED(userData))
	{
	u32	 freeSamples;
	u32	 sampleSize;
	u32	 len1;
	BOOL old;
	
	// we can only play mono or stereo!
	ASSERT(numChannels <= 2);

	// Disable IRQs. We must make sure we don't get interrupted by the AI callback.
	old = OSDisableInterrupts();

	// Did the video decoder just jump back to the beginning of the stream?
	if (audio_player.readBuffer[audio_player.decodeIndex].frameNumber < audio_player.lastDecodedFrame)
		{
		// Yes! We have to reset the internal read buffer and disable any audio output
		// until we got new video data to display...
		//
		// Note: we have to reset our buffers because the stream contains more audio data than
		// neccessary to cover a single video frame within the first few frames to accumulate
		// some safety buffer. If the stream would just be allowed to loop we would get an
		// overflow (unless we used up the extra buffer due to read / decode delays) after a few
		// loops...
		//
		AUDSimpleAudioReset();
		}

	// Calculate the read buffer's sample size
	sampleSize = sizeof(s16) * audioReadBufferNumChannels;

	// How many samples could we put into the buffer?
	if (audioReadBufferWritePos >= audioReadBufferReadPos)
	{
		freeSamples = audioReadBufferNumSamples - (audioReadBufferWritePos - audioReadBufferReadPos);

		if( freeSamples < sampleNum )
		{
			OSRestoreInterrupts(old);
			#ifndef FINAL
			OSReport("*** audioDecode: overflow case 1\n");
			#endif
			return FALSE;				// overflow!
		}

		// We might have a two buffer update to do. Check for it...
		if ((len1 = (audioReadBufferNumSamples - audioReadBufferWritePos)) >= sampleNum)
		{
			// No. We got ourselfs a nice, simple single buffer update.
			writeChannelData((s16 *)((u32)audioReadBuffer + audioReadBufferWritePos * sampleSize),numChannels,samples,0,sampleNum);
		}
		else
		{
			// Dual buffer case
			writeChannelData((s16 *)((u32)audioReadBuffer + audioReadBufferWritePos * sampleSize),numChannels,samples,0,len1);
            writeChannelData((s16 *)audioReadBuffer,numChannels,samples,len1,sampleNum-len1);
		}
	}
	else
	{
		freeSamples = audioReadBufferReadPos - audioReadBufferWritePos;

		if (freeSamples < sampleNum)
		{
			OSRestoreInterrupts(old);
			#ifndef FINAL
			OSReport("*** audioDecode: overflow case 2\n");
			#endif
			return FALSE;				// overflow!
		}

		// We're save to assume to have a single buffer update in any case...
		writeChannelData((s16 *)((u32)audioReadBuffer + audioReadBufferWritePos * sampleSize),numChannels,samples,0,sampleNum);
	}

	// Advance write position...
	audioReadBufferWritePos += sampleNum;

	if (audioReadBufferWritePos >= audioReadBufferNumSamples)
		audioReadBufferWritePos -= audioReadBufferNumSamples;

	// We're done with all critical stuff. IRQs may be enabled again...
	OSRestoreInterrupts(old);

	return TRUE;
	}


/*!
 ******************************************************************************
 * \brief
 *		Receive data from bitstream and direct to required encoding facility
 *
 *		This function will receive the AUDD chunck from the VIDSimplePlayer's
 *		decode function.
 *
 * \param bitstream
 *		Pointer to the data of a AUDD chunk
 *
 * \param bitstreamLen
 *		Length of the data in the AUDD chunk pointed to by above pointer
 *
 * \return
 *		FALSE if data could not be interpreted properly
 *
 ******************************************************************************
 */
BOOL AUDSimpleAudioDecode(const u8* bitstream, u32 bitstreamLen)
	{

	// select first two channels by default
	u32 channelSelectMask = audioPlayMaskArray ? audioPlayMaskArray[0] : 0x3;		
	
	u32 headerSize = ((VidAUDDVAUD*)bitstream)->size;
	if(!VAUDDecode(audio_player.decoder, bitstream+headerSize, bitstreamLen-headerSize, channelSelectMask, audioDecode, NULL))
		return FALSE;
	
	audioPlayBufferEnabled = TRUE;
	
	return TRUE;
	}

/*!
 ******************************************************************************
 * \brief
 *		Change the active audio channels
 *
 ******************************************************************************
 */
				
void AUDSimpleAudioChangePlayback(const u32 *playMaskArray,u32 numMasks)
	{
	u32		i,b;
	BOOL	old = OSDisableInterrupts();
	
	audioPlayMaskArray = playMaskArray;
	audioNumPlayMasks = numMasks;
	
	// Any playback mask specified?
	if (audioNumPlayMasks != 0)
		{
		// Yes. Count the active voices...
		audioNumActiveVoices = 0;
		
		for(i=0; i= audioReadBufferNumSamples)
		audioReadBufferReadPos -= audioReadBufferNumSamples;
	}

/*!
 ******************************************************************************
 * \brief
 *		AX callback
 *
 ******************************************************************************
 */
static void AXCallback(void)
{
	// First thing to do here is call the regular soundfx callback.
	Sfx::AXUserCBack();

	u32		availSamples,availFrames,numUpdate,i,n;
	u32		audioPlayBufferNeededAudioFrames;
	u32		currentAddr;

	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	
	if (axPhase == AX_PHASE_START)
	{
		AXSetVoiceState(axVoice[0],AX_PB_STATE_RUN);
		AXSetVoiceState(axVoice[1],AX_PB_STATE_RUN);
		axPhase = AX_PHASE_PLAY;
	}
		
	// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	
	currentAddr = *(u32 *)&axVoice[0]->pb.addr.currentAddressHi;

	if( currentAddr >= axLastAddr )
	{
		axPlayedSamples			+= currentAddr - axLastAddr;
		axPlayedSamplesTotal	+= currentAddr - axLastAddr;
	}
	else
	{
		axPlayedSamples			+= (( AX_ARAM_LEFT_CHANNEL + AX_ARAM_BUFFER_SIZE ) - axLastAddr ) + ( currentAddr - AX_ARAM_LEFT_CHANNEL );
		axPlayedSamplesTotal	+= (( AX_ARAM_LEFT_CHANNEL + AX_ARAM_BUFFER_SIZE ) - axLastAddr ) + ( currentAddr - AX_ARAM_LEFT_CHANNEL );
	}
	
	axLastAddr = currentAddr;

	// If we have played the required number of samples, stop the voice.
	if( axPlayedSamplesTotal >= audio_player.audioInfo.vaudex.totalSampleCount )
	{
		AXSetVoiceState( axVoice[0], AX_PB_STATE_STOP );
		AXSetVoiceState( axVoice[1], AX_PB_STATE_STOP );
		audio_player.playbackComplete = true;
		return;
	}
		
	if( axPlayedSamples >= AUD_AUDIO_AIBUFFERSAMPLES )
	{
		audioPlayBufferNeededAudioFrames = axPlayedSamples / AUD_AUDIO_AIBUFFERSAMPLES;

		// Make sure that we never get an underrun we don't notice...
		if( !( audioPlayBufferNeededAudioFrames <= AUD_AUDIO_NUMAIBUFFERS ))
		{
//			OSReport( "AX audio buffer underrun!\n" );

			// Disable playback.
			audioPlayBufferEnabled = false;
		}

		// Is actual audio playback enabled?
		if( audioPlayBufferEnabled )
		{
			// How many samples could we get from the read buffer?
			if( audioReadBufferWritePos >= audioReadBufferReadPos )
				availSamples = audioReadBufferWritePos - audioReadBufferReadPos;
			else
				availSamples = audioReadBufferNumSamples - ( audioReadBufferReadPos - audioReadBufferWritePos );
			
			// That's how many audio frames?
			availFrames = availSamples / AUD_AUDIO_AIBUFFERSAMPLES;

			//OSReport("AX: %d %d (%d)\n",availSamples,audioPlayBufferNeededAudioFrames * VID_AUDIO_AIBUFFERSAMPLES,axPlayedSamples);
	
			// So, how many can we update?
			numUpdate = ( availFrames > audioPlayBufferNeededAudioFrames ) ? audioPlayBufferNeededAudioFrames : availFrames;
	
			// If anything... go do it!
			if( numUpdate != 0 )
			{
				axPlayedSamples -= numUpdate * AUD_AUDIO_AIBUFFERSAMPLES;
				
				// Perform updates on each AI buffer in need of data...
				for( i = 0; i < numUpdate; i++ )
				{
					u32 leftSource, rightSource, leftTarget, rightTarget;

					// Can we copy everything from a single source or does the data wrap around?
					if(( n = audioReadBufferNumSamples - audioReadBufferReadPos) < AUD_AUDIO_AIBUFFERSAMPLES )
					{
						// It wraps...
						audioCopy( 0, n );
						audioCopy( n, AUD_AUDIO_AIBUFFERSAMPLES - n );
					}
					else
					{
						// We got one continous source buffer
						audioCopy( 0, AUD_AUDIO_AIBUFFERSAMPLES );
					}
						
					// Make sure the data ends up in real physical memory
					DCFlushRange( audioPlayBuffer[audioPlayBufferWriteIndex], AUD_AUDIO_AIBUFFERSAMPLES * sizeof( s16 ) * 2 );
					
					leftSource	= (u32)audioPlayBuffer[audioPlayBufferWriteIndex];
					rightSource	= (u32)audioPlayBuffer[audioPlayBufferWriteIndex] + ( AUD_AUDIO_AIBUFFERSAMPLES * sizeof( s16 ));
					leftTarget	= 2 * ( AX_ARAM_LEFT_CHANNEL + audioPlayBufferWriteIndex * AUD_AUDIO_AIBUFFERSAMPLES );
					rightTarget	= 2 * ( AX_ARAM_RIGHT_CHANNEL + audioPlayBufferWriteIndex * AUD_AUDIO_AIBUFFERSAMPLES );
					
					// Make sure we get this into ARAM ASAP...
					ARQPostRequest( &arqRequest[0][i%AUD_NUM_ARQ_REQUESTS], 0, ARQ_TYPE_MRAM_TO_ARAM,ARQ_PRIORITY_HIGH, leftSource, leftTarget, AUD_AUDIO_AIBUFFERSAMPLES * sizeof( s16 ), NULL );
					ARQPostRequest( &arqRequest[1][i%AUD_NUM_ARQ_REQUESTS], 1, ARQ_TYPE_MRAM_TO_ARAM,ARQ_PRIORITY_HIGH, rightSource, rightTarget, AUD_AUDIO_AIBUFFERSAMPLES * sizeof( s16 ), NULL );
					
					// Advance write index...
					audioPlayBufferWriteIndex = (u8)(( audioPlayBufferWriteIndex + 1 ) % AUD_AUDIO_NUMAIBUFFERS );
				}
				
				if( axPhase == AX_PHASE_STARTUP )
				{
					axPhase = AX_PHASE_START;
				}
			}
		}
		else
		{
			// Update buffer(s) with silence...
			axPlayedSamples -= audioPlayBufferNeededAudioFrames * AUD_AUDIO_AIBUFFERSAMPLES;
			
			for( i = 0; i < audioPlayBufferNeededAudioFrames; i++ )
			{
				u32 leftSource, rightSource, leftTarget, rightTarget;

				memset(audioPlayBuffer[audioPlayBufferWriteIndex],0,2 * sizeof(s16) * AUD_AUDIO_AIBUFFERSAMPLES);
				DCFlushRange(audioPlayBuffer[audioPlayBufferWriteIndex],2 * sizeof(s16) * AUD_AUDIO_AIBUFFERSAMPLES);
	
				leftSource = (u32)audioPlayBuffer[audioPlayBufferWriteIndex];
				rightSource = (u32)audioPlayBuffer[audioPlayBufferWriteIndex] + (AUD_AUDIO_AIBUFFERSAMPLES * sizeof(s16)) / 2;
				leftTarget = 2 * (AX_ARAM_LEFT_CHANNEL + audioPlayBufferWriteIndex * AUD_AUDIO_AIBUFFERSAMPLES);
				rightTarget = 2 * (AX_ARAM_RIGHT_CHANNEL + audioPlayBufferWriteIndex * AUD_AUDIO_AIBUFFERSAMPLES);
				
				// Make sure we get this into ARAM ASAP...
				ARQPostRequest(&arqRequest[0][i%AUD_NUM_ARQ_REQUESTS],0,ARQ_TYPE_MRAM_TO_ARAM,ARQ_PRIORITY_HIGH,leftSource,leftTarget,AUD_AUDIO_AIBUFFERSAMPLES * sizeof(s16),NULL);
				ARQPostRequest(&arqRequest[1][i%AUD_NUM_ARQ_REQUESTS],1,ARQ_TYPE_MRAM_TO_ARAM,ARQ_PRIORITY_HIGH,rightSource,rightTarget,AUD_AUDIO_AIBUFFERSAMPLES * sizeof(s16),NULL);
				
				audioPlayBufferWriteIndex = (u8)((audioPlayBufferWriteIndex + 1) % AUD_AUDIO_NUMAIBUFFERS);
			}
			
			if (axPhase == AX_PHASE_STARTUP)
				axPhase = AX_PHASE_START;
		}
	}
}

#endif		// DVDETH



================================================
FILE: Code/Gel/Music/ngc/divx/AUDSimpleAudio.h
================================================
/*!
 ******************************************************************************
 * \file AUDSimpleAudio.h
 *
 * \brief
 *		Header file for AUDSimpleAudio interface
 *
 * \date
 *		5/22/03
 *
 * \version
 *		1.0
 *
 * \author
 *		Thomas Engel/Achim Moller
 *
 ******************************************************************************
 */
#ifndef __AUDSIMPLE_AUDIO_H__
#define __AUDSIMPLE_AUDIO_H__

#ifdef __cplusplus
extern "C" {
#endif

/******************************************************************************
 *  INCLUDES
 ******************************************************************************/
#include 
#include 
#include "vaud.h"

/******************************************************************************
 *  DEFINES
 ******************************************************************************/

/******************************************************************************
 *  STRUCTS AND TYPES
 ******************************************************************************/

/******************************************************************************
 *  PROTOTYPES
 ******************************************************************************/

//! Initialize audio decoding / playback
extern BOOL AUDSimpleInitAudioDecoder(void);

//! Shutdown audio decoding / playback
extern void AUDSimpleExitAudioDecoder(void);

//! Decode audio data for one frame
extern BOOL AUDSimpleAudioDecode(const u8* bitstream, u32 bitstreamLen);

//! Change currently active audio channels
extern void AUDSimpleAudioChangePlayback(const u32 *playMaskArray,u32 numMasks);

//! Stop audio decode / output
extern void AUDSimpleAudioReset(void);

//! Return some information about running audio
extern void AUDSimpleAudioGetInfo(VidAUDH* audioHeader);

extern u32	AUDSimpleAudioGetFreeReadBufferSamples( void );
extern void	AUDSimpleAudioSetVolume( u16 vl, u16 vr );

extern void AUDSimplePause( bool pause );


#ifdef __cplusplus
}
#endif

#endif	// __AUDSIMPLE_PLAYER_H__




================================================
FILE: Code/Gel/Music/ngc/divx/AUDSimplePlayer.cpp
================================================
#ifndef DVDETH
/*!
 ******************************************************************************
 * \file AUDSimplePlayer.cpp
 *
 * \brief
 *		This file provides the required player control functions.
 *
 * \note
 *      This is a demonstration source only!
 *
 * \date
 *		05/21/03
 *
 * \version
 *		1.0
 *
 * \author
 *		Achim Moller
 *
 ******************************************************************************
 */

/******************************************************************************
 *  INCLUDES
 ******************************************************************************/
#include 
#include 

#include "AUDSimplePlayer.h"
#include "AUDSimpleAudio.h"

/******************************************************************************
 *  GLOABL VARIABLES
 ******************************************************************************/
AudSimplePlayer audio_player;

#ifndef MIN
#define MIN(a,b) ((a) < (b) ? a : b)
#endif

/******************************************************************************
 *  LOCAL VARIABLES
 ******************************************************************************/
static VidChunk workChunk ATTRIBUTE_ALIGN(32);
static void dvdDoneCallback(s32 result, DVDFileInfo *videoInfo);

/*!
 ******************************************************************************
 * \brief
 *		Initializes all audio_player structures.
 *
 ******************************************************************************
 */
void AUDSimpleInit(VAUDAllocator _cbAlloc, VAUDDeallocator _cbFree)
	{
	memset(&audio_player, 0, sizeof(audio_player));
	audio_player.cbAlloc = _cbAlloc;
	audio_player.cbFree = _cbFree;
	}

/*!
 ******************************************************************************
 * \brief
 *		Request an async file transfer.
 *
 *		This function starts the transfer of the next frame into a free
 *		buffer.
 *
 ******************************************************************************
 */
static void ReadFrameAsync(void)
	{
	if (!audio_player.error && audio_player.preFetchState == TRUE)
		{
		if (audio_player.currentFrameCount >  audio_player.audioInfo.vaudex.frameCount - 1)
			{
			if (audio_player.loopMode)
				{
				audio_player.currentFrameCount = 0;
				audio_player.nextFrameOffset = audio_player.firstFrameOffset;
				audio_player.nextFrameSize  = audio_player.firstFrameSize;
				}
			else
				return;
			}
		
		audio_player.asyncDvdRunning = TRUE;

		if (DVDReadAsync(&audio_player.fileHandle,
						 audio_player.readBuffer[audio_player.readIndex].ptr,
						 (s32)audio_player.nextFrameSize,
						 (s32)audio_player.nextFrameOffset, dvdDoneCallback) != TRUE )
			{
			audio_player.asyncDvdRunning = FALSE;
			audio_player.error = TRUE;
			}
		}
	}

/*!
 ******************************************************************************
 * \brief
 *		DVD callback if async transfer is finished (or aborted)
 *	
 *		The idea here is to transfer ONE frame and additional 32 bytes for the
 *		HEADER of the NEXT frame in one transfer step. We store the size of
 *		the next frame, which is used in ReadFrameAsync().
 *
 *		
 * \note
 *		There a 32 padding bytes at the end of the .vid file. So, the reading
 *		of 32 additional bytes is even possible for the LAST frame. (Otherwise,
 *		we would 'point' out of the file)
 *
 *		See Dolphin documentation for information about parameters.
 *
 ******************************************************************************
 */
static void dvdDoneCallback(s32 result, DVDFileInfo * _UNUSED(videoInfo))
	{
	if (result == DVD_RESULT_FATAL_ERROR)
		{
		audio_player.error = TRUE;
		return;
		}
	else if (result == DVD_RESULT_CANCELED)
		{
		return;
		}

	audio_player.asyncDvdRunning = FALSE;

	audio_player.readBuffer[audio_player.readIndex].frameNumber = audio_player.currentFrameCount;
	audio_player.readBuffer[audio_player.readIndex].size = (u32)result;	
	audio_player.readBuffer[audio_player.readIndex].valid = TRUE;
	
	audio_player.currentFrameCount++;

	// move file pointer
	audio_player.nextFrameOffset += audio_player.nextFrameSize;
	
	if(audio_player.currentFrameCount < audio_player.audioInfo.vaudex.frameCount)
		{
		// set read size for next 'FRAM' chunk
		u32* nextFrameStart = (u32*)(audio_player.readBuffer[audio_player.readIndex].ptr + audio_player.nextFrameSize - 32);
	
		// some check if file structure is okay
		ASSERT(nextFrameStart[0] == VID_FCC('F','R','A','M'));
		
		// get the size of the next 'FRAM' chunk to read
		audio_player.nextFrameSize = nextFrameStart[1];
		ASSERT(audio_player.nextFrameSize);
		}
	else
		audio_player.nextFrameSize = 0;	// at end of file we have a size of '0'. This should be reinitialized later
									// using the size of the first frame somwhere else! Otherwise, we get an assertion

	// use next buffer
	audio_player.readIndex = (audio_player.readIndex + 1) % AUD_NUM_READ_BUFFERS;

	// continue loading if we have a free buffer
	if (!audio_player.readBuffer[audio_player.readIndex].valid)
		ReadFrameAsync();
	}

/*!
 ******************************************************************************
 * \brief
 *		Allocate buffer memory for asynchronous dvd read
 *
* \return
 *		TRUE if DVD buffer setup was successful.
 *
 ******************************************************************************
 */
BOOL AUDSimpleAllocDVDBuffers(void)
	{
	u32 i;
	u32 bufferSize;
	u8* ptr;

	bufferSize = audio_player.audioInfo.vaudex.maxBufferSize;
	ASSERT(bufferSize);
	
	bufferSize += VID_CHUNK_HEADER_SIZE;	// 'fram' header
	bufferSize += VID_CHUNK_HEADER_SIZE;	// 'vidd' header
	bufferSize = OSRoundUp32B(bufferSize);
	
	ASSERT(audio_player.cbAlloc);
	audio_player.readBufferBaseMem = (u8*)((*audio_player.cbAlloc)(bufferSize * AUD_NUM_READ_BUFFERS));
	
	if(!audio_player.readBufferBaseMem)
		return FALSE;	// out of mem
	
	ptr = audio_player.readBufferBaseMem;
	for (i = 0; i < AUD_NUM_READ_BUFFERS ; i++)
		{
		audio_player.readBuffer[i].ptr = ptr;
		ptr += bufferSize;
		audio_player.readBuffer[i].valid = FALSE;
		}
	
	return TRUE;
	}

/*!
 ******************************************************************************
 * \brief
 *		Free buffer memory used for dvd read
 *
 * \param memFree
 *		Pointer to memory deallocation function
 *
 ******************************************************************************
 */
void AUDSimpleFreeDVDBuffers(void)
	{
	ASSERT(audio_player.cbFree);
	ASSERT(audio_player.readBufferBaseMem);
	(*audio_player.cbFree)(audio_player.readBufferBaseMem);
	}

/*!
 ******************************************************************************
 * \brief
 *		Create a new decoder instance.
 *
 * \return
 *		TRUE if decoder creation was successful
 *
 ******************************************************************************
 */
BOOL AUDSimpleCreateDecoder(void)
	{
	u32 skip;
	
	audio_player.decoder = VAUDCreateDecoder(audio_player.cbAlloc, audio_player.cbFree, audio_player.audioInfo.vaud.maxHeap);
	
	if(!audio_player.decoder)
		return FALSE;

	ASSERT(audio_player.audioHeaderChunk);
	ASSERT(audio_player.audioInfo.vaud.maxHeap > 0);
	ASSERT(audio_player.audioInfo.vaud.preAlloc > 0);
	skip = VID_CHUNK_HEADER_SIZE + sizeof(u32) + (audio_player.audioInfo.vaudex.version > 0 ? audio_player.audioInfo.vaudex.size : sizeof(VidAUDHVAUD));

	if(!VAUDInitDecoder(audio_player.decoder, audio_player.audioHeaderChunk + skip, ((VidChunk*)audio_player.audioHeaderChunk)->len - skip, audio_player.audioInfo.vaud.preAlloc))
		{
		VAUDDestroyDecoder(audio_player.decoder);
		return FALSE;
		}
	
	return TRUE;
	}
/*!
 ******************************************************************************
 * \brief
 *		Destroy decoder instance.
 *
 *		At this point the decoder returns all allocated memory by using
 *		the cbFree callback.
 *
 ******************************************************************************
 */
void AUDSimpleDestroyDecoder(void)
{
	ASSERT(audio_player.decoder);
	VAUDDestroyDecoder(audio_player.decoder);
	
	// Set the decoder to NULL.
	audio_player.decoder = NULL;
}



static u32 readNum = 0;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void AUDSimpleLoadStartDVDCallback( s32 result, DVDFileInfo* fileInfo )
{
	// Deal with errors.
	if( audio_player.error )
	{
		return;
	}
	
	audio_player.nextFrameOffset += audio_player.nextFrameSize;
	audio_player.readBuffer[audio_player.readIndex].size = audio_player.nextFrameSize;

	// Set read size for next 'FRAM' chunk.
	u32* nextFrame = (u32*)(audio_player.readBuffer[audio_player.readIndex].ptr + audio_player.nextFrameSize - 32);

	// Some sanity check if file structure is valid!
	ASSERT( nextFrame[0] == VID_FCC( 'F','R','A','M' ));

	audio_player.nextFrameSize = nextFrame[1];
	ASSERT( audio_player.nextFrameSize );

	audio_player.readBuffer[audio_player.readIndex].valid = TRUE;
	audio_player.readBuffer[audio_player.readIndex].frameNumber = audio_player.currentFrameCount;

	// Use next buffer.
	audio_player.readIndex = (audio_player.readIndex + 1) % AUD_NUM_READ_BUFFERS;
	audio_player.currentFrameCount++;
	
	if( --readNum > 0 )
	{
		if( DVDReadAsync( &audio_player.fileHandle, audio_player.readBuffer[audio_player.readIndex].ptr, (s32)audio_player.nextFrameSize, (s32)audio_player.nextFrameOffset, AUDSimpleLoadStartDVDCallback ) < 0 )
		{
#			ifdef _DEBUG
			OSReport("*** AUDSimpleLoadStart: Failed to read from file.\n");
#			endif
			audio_player.error = TRUE;
			return;
		}
	}
	else
	{
		// All done.
		audio_player.loopMode		= FALSE;
		audio_player.preFetchState	= TRUE;
	}
}






/*!
 ******************************************************************************
 * \brief
 *		Preload the allocated buffers.
 *
 *		This functions fills all buffers with initial data
 *
 * \param loopMode
 *		TRUE if we want to operate in loop mode
 *
 * \return
 *		TRUE if preload was okay 		
 *
 ******************************************************************************
 */
BOOL AUDSimpleLoadStart(BOOL loopMode)
{
//	u32		i, readNum;
//	u32		i;
//	u32*	nextFrame;

	ASSERT( loopMode == FALSE );
	
	if( audio_player.open && audio_player.preFetchState == FALSE )
	{
		readNum = AUD_NUM_READ_BUFFERS;

		// In 'non-loop' mode we must take care if we have LESS frames than preloading buffers
        if( !loopMode && audio_player.audioInfo.vaudex.frameCount < AUD_NUM_READ_BUFFERS )
			readNum = audio_player.audioInfo.vaudex.frameCount;
				
//		for( i = 0; i < readNum; i++ )
		if( readNum > 0 )
		{
			// Read total 'FRAM' chunk and 32 bytes of NEXT chunk.
//			if( DVDRead( &audio_player.fileHandle, ptr, (s32)audio_player.nextFrameSize, (s32)audio_player.nextFrameOffset ) < 0 )
			if( DVDReadAsync( &audio_player.fileHandle, audio_player.readBuffer[audio_player.readIndex].ptr, (s32)audio_player.nextFrameSize, (s32)audio_player.nextFrameOffset, AUDSimpleLoadStartDVDCallback ) < 0 )
			{
#				ifdef _DEBUG
				OSReport("*** AUDSimpleLoadStart: Failed to read from file.\n");
#				endif
				audio_player.error = TRUE;
				return FALSE;
			}
			
//			audio_player.nextFrameOffset += audio_player.nextFrameSize;
//			audio_player.readBuffer[audio_player.readIndex].size = audio_player.nextFrameSize;

            // set read size for next 'FRAM' chunk
//			nextFrame = (u32*)(audio_player.readBuffer[audio_player.readIndex].ptr + audio_player.nextFrameSize - 32);
			
			// some sanity check if file structure is valid!
//			ASSERT(nextFrame[0] == VID_FCC('F','R','A','M'));
			
//			audio_player.nextFrameSize = nextFrame[1];
//			ASSERT( audio_player.nextFrameSize );

//			audio_player.readBuffer[audio_player.readIndex].valid = TRUE;
//			audio_player.readBuffer[audio_player.readIndex].frameNumber = audio_player.currentFrameCount;

			// Use next buffer
//			audio_player.readIndex = (audio_player.readIndex + 1) % AUD_NUM_READ_BUFFERS;

//			audio_player.currentFrameCount++;

//			if (audio_player.currentFrameCount >  audio_player.audioInfo.vaudex.frameCount - 1)
//			{
//				if (loopMode)
//				{
//					audio_player.currentFrameCount = 0;
//					audio_player.nextFrameOffset = audio_player.firstFrameOffset;
//					audio_player.nextFrameSize  = audio_player.firstFrameSize;
//				}
//			}
		}
//		audio_player.loopMode = loopMode;
//		audio_player.preFetchState = TRUE;
		return TRUE;
	}
	return FALSE;
}

/*!
 ******************************************************************************
 * \brief
 *		Stops the asynchronous loading process.
 *
 * \return
 *		TRUE if audio_player could be stopped!		
 *
 ******************************************************************************
 */
BOOL AUDSimpleLoadStop(void)
	{
	u32 i;

	if (audio_player.open)
		{
		// stop preloading process
		audio_player.preFetchState = FALSE;

		if (audio_player.asyncDvdRunning)
			{
			DVDCancel(&audio_player.fileHandle.cb);
			audio_player.asyncDvdRunning = FALSE;
			}

		// invalidate all buffers
		for (i = 0 ; i < AUD_NUM_READ_BUFFERS; i++)
			audio_player.readBuffer[i].valid = FALSE;

		audio_player.nextFrameOffset = audio_player.firstFrameOffset;
		audio_player.nextFrameSize = audio_player.firstFrameSize;
		audio_player.currentFrameCount = 0;
		
		audio_player.error 	   		= FALSE;
		audio_player.readIndex   		= 0;
		audio_player.decodeIndex 		= 0;

		return TRUE;
		}
	return FALSE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void AUDSimpleOpenDVDCallback( s32 result, DVDFileInfo* fileInfo )
{
	static u32	fileOffset		= 0;
	static u32	headSize		= 0;
	static u32	audioInfoSize	= 0;
	
	// Deal with errors, possibly flagged as an indication to shut down immediately.
	if( audio_player.error )
	{
		// Set this back to zero so the player will be regarded as 'free' again.
		audio_player.asyncOpenCallbackStatus = 0;
		return;
	}
	
	switch( audio_player.asyncOpenCallbackStatus )
	{
		case 1:
		{
			// The read of the 'VID1' chunk has completed.
			fileOffset = 32;
	
			// Check file id.
			if( workChunk.id != VID_FCC('V','I','D','1' ))
			{
#				ifdef _DEBUG
				OSReport("*** No VID1 file: '%s'\n", fileName);
#				endif
				DVDClose( &audio_player.fileHandle );
				audio_player.asyncOpenCallbackStatus = ASYNC_OPEN_FAIL;
				return;
			}
	
			// Check for correct version of vid chunk.
			// If we find this version we assume a 'special' alignment and chunk ordering which may be invalid
			// in another version of the file format.
			if( workChunk.vid.versionMajor != 1 || workChunk.vid.versionMinor != 0 )
			{
#				ifdef _DEBUG
				OSReport("*** Unsupported file version: major: %d, minor: %d\n", workChunk.vid.versionMajor, workChunk.vid.versionMajor);
#				endif
				DVDClose( &audio_player.fileHandle );
				audio_player.asyncOpenCallbackStatus = ASYNC_OPEN_FAIL;
				return;
			}
	
#			ifdef _DEBUG
			// Sometimes, it's required to check for a special build of the VidConv converter.
			{
				u32 version = VID_VERSION(workChunk.vid.vidConvMajor, workChunk.vid.vidConvMinor, workChunk.vid.vidConvBuild);
				if( version < VID_VERSION( 1, 0, 1 ))
					OSReport("*** WARNING: Vid file created using an unsupported converter version: %d.%d.%d\n", (u32)workChunk.vid.vidConvMajor, (u32)workChunk.vid.vidConvMinor, (u32)workChunk.vid.vidConvBuild);
			}
#			endif

			// Set callback status to indicate 'VID1' chunk is read.
			audio_player.asyncOpenCallbackStatus = 2;
			
			// Check types of chunks we have in this file.
			// !!! Note that we assume start of 'HEAD' chunk at byte offset 32 from file start !!!
			if( DVDReadAsync( &audio_player.fileHandle, &workChunk, 32, (s32)fileOffset, AUDSimpleOpenDVDCallback ) < 0 )
			{
#				ifdef _DEBUG
				OSReport("*** Failed to read 'HEAD' chunk.\n");
#				endif
				DVDClose( &audio_player.fileHandle );
				audio_player.asyncOpenCallbackStatus = ASYNC_OPEN_FAIL;
				return;
			}
			break;
		}
	
		case 2:
		{
			// The read of the chunk type info has completed.
			if( workChunk.id != VID_FCC('H','E','A','D' ))
			{
#				ifdef _DEBUG
				OSReport("*** No HEAD chunk found at expected offset\n");
#				endif
				DVDClose( &audio_player.fileHandle );
				audio_player.asyncOpenCallbackStatus = ASYNC_OPEN_FAIL;
				return;
			}

			// Calculate the start of the first frame chunk (we know the header chunk starts at offset 32).
			audio_player.nextFrameOffset = workChunk.len + 32;

			// Skip 'HEAD' chunk id, len and version fields.
			fileOffset += VID_CHUNK_HEADER_SIZE;

			// The header chunk contains one or more header chunks for the different data types contained
			// in the stream. Parse them all...

			headSize = workChunk.len - VID_CHUNK_HEADER_SIZE;
			
			audio_player.asyncOpenCallbackStatus = 3;
			if( DVDReadAsync( &audio_player.fileHandle, &workChunk, 32, (s32)fileOffset, AUDSimpleOpenDVDCallback ) < 0 )
			{
#				ifdef _DEBUG
				OSReport("*** Error reading file at offset %d\n", fileOffset);
#				endif
				DVDClose( &audio_player.fileHandle );
				audio_player.asyncOpenCallbackStatus = ASYNC_OPEN_FAIL;
				return;
			}
			break;
		}
	
		case 3:
		{
			fileOffset	+= 32;
			headSize	-= 32;

			// We analyze the 1st 32 bytes of the chunk for a known header format.
			if( workChunk.id == VID_FCC( 'A','U','D','H' ))
			{
				// Allocate memory for audio header chunk.
				audio_player.audioHeaderChunk	= (u8*)((*audio_player.cbAlloc)(workChunk.len));
				audioInfoSize					= workChunk.len - VID_CHUNK_HEADER_SIZE;

				// Copy the already loaded part.
				memcpy( audio_player.audioHeaderChunk, &workChunk, 32 );
				workChunk.len -= 32;

				// Read additional audio header bytes if the audio header is greater that 32 bytes
				if( workChunk.len >= 32 )
				{
					ASSERT(( workChunk.len & 31 ) == 0 );
					audio_player.asyncOpenCallbackStatus = 4;
					if( DVDReadAsync( &audio_player.fileHandle, audio_player.audioHeaderChunk + 32, workChunk.len, (s32)fileOffset, AUDSimpleOpenDVDCallback ) < 0 )
					{
#						ifdef _DEBUG
						OSReport("*** Error reading file at offset %d\n", fileOffset);
#						endif
						DVDClose( &audio_player.fileHandle );
						audio_player.asyncOpenCallbackStatus = ASYNC_OPEN_FAIL;
						return;
					}
				}
				else
				{
					ASSERT( 0 );
				}
			}
			else
			{
				ASSERT( 0 );
			}
			break;
		}
					
		case 4:
		{
			fileOffset	+= workChunk.len;
			headSize	-= workChunk.len;

			// Setup and calc the number of bytes which we are allowed to copy into the audioInfo struct
			memcpy( &audio_player.audioInfo, audio_player.audioHeaderChunk + VID_CHUNK_HEADER_SIZE, MIN( audioInfoSize, sizeof( audio_player.audioInfo )));
			
			// Check if we have the correct vaud file version (>0).
			if( audio_player.audioInfo.vaudex.version == 0 )
			{
#				ifdef _DEBUG
				OSReport("*** Invalid version in vaud file.");
#				endif
				DVDClose( &audio_player.fileHandle );
				audio_player.asyncOpenCallbackStatus = ASYNC_OPEN_FAIL;
				return;
			}

			// We can only play audio files which have the following fields set.
			// Note that in case of VIDEO files this fields are allowed to be 0.
			ASSERT( audio_player.audioInfo.vaudex.maxBufferSize > 0 );
			ASSERT( audio_player.audioInfo.vaudex.frameCount > 0 );
			ASSERT( audio_player.audioInfo.vaudex.frameTimeMs > 0 );

			// Read beginning of 1st frame chunk to get required size information.
			audio_player.asyncOpenCallbackStatus = 5;
			if( DVDReadAsync( &audio_player.fileHandle, &workChunk, 32 , (s32)audio_player.nextFrameOffset, AUDSimpleOpenDVDCallback ) < 0 )
			{
#				ifdef _DEBUG
				OSReport("*** Failed to read 'FRAM' chunk.\n");
#				endif
				DVDClose( &audio_player.fileHandle );
				audio_player.asyncOpenCallbackStatus = ASYNC_OPEN_FAIL;
				return;
			}
			break;
		}
			
		case 5:
		{
			if( workChunk.id != VID_FCC('F','R','A','M') )
			{
#				ifdef _DEBUG
				OSReport("*** No FRAM chunk found.");
#				endif
				audio_player.asyncOpenCallbackStatus = ASYNC_OPEN_FAIL;
				DVDClose( &audio_player.fileHandle );
				return;
			}

			// 32 bytes of this chunk are already consumed, but we want to 'preload' the NEXT chunk's FRAM header.
			audio_player.nextFrameSize				= workChunk.len; 		
			audio_player.nextFrameOffset			+= 32;

			audio_player.firstFrameOffset			= audio_player.nextFrameOffset;
			audio_player.firstFrameSize				= audio_player.nextFrameSize;

//			strncpy( audio_player.fileName, fileName, 64 );
//			audio_player.fileName[63]				= 0;

			audio_player.open 			 			= TRUE;

			audio_player.asyncOpenCallbackStatus	= ASYNC_OPEN_SUCCESS;
			audio_player.readIndex 					= 0;
			audio_player.decodeIndex 				= 0;
			audio_player.lastDecodedFrame			= 0;
			audio_player.error 			 			= FALSE;
			audio_player.preFetchState 	 			= FALSE;
			audio_player.loopMode		 			= FALSE;
			audio_player.asyncDvdRunning			= FALSE;
			audio_player.currentFrameCount 			= 0;
			audio_player.readBufferBaseMem			= 0;
			break;
		}
	
		default:
		{
			ASSERT( 0 );
			break;
		}
	}
}





/*!
 ******************************************************************************
 * \brief
 *		Open video file.
 *
 *		This functions opens a video file and parses some basic file
 *		information.
 *
 * \param fileName
 *		Name of file to open
 *
 * \return
 *		TRUE if file could be opened and is in valid format!
 *
 ******************************************************************************
 */
BOOL AUDSimpleOpen( char* fileName )
{
//	u32 fileOffset = 0;
//	u32 headSize;
//	u32 audioInfoSize;
	
	if( audio_player.open )
	{
#		ifdef _DEBUG
		OSReport( "*** Cannot open '%s' because audio_player already open.\n", fileName );
#		endif
		return FALSE;
	}

	// Initialise the callback status.
	audio_player.asyncOpenCallbackStatus = 0;
	
	s32 entry_num = DVDConvertPathToEntrynum( fileName );
	if( entry_num == -1 )
	{
#		ifdef _DEBUG
		OSReport( "*** Cannot find '%s'\n", filename );
#		endif
		return FALSE;
	}
	if( DVDFastOpen( entry_num, &audio_player.fileHandle ) == FALSE )
	{
#		ifdef _DEBUG
		OSReport( "*** Cannot open: '%s'\n", fileName );
#		endif
		return FALSE;
	}
		
	// Set callback status to indicate file is now open.
	audio_player.asyncOpenCallbackStatus = 1;
	
	// Read 'VID1' chunk from file and check for correct version.
//	if( DVDRead( &audio_player.fileHandle, &workChunk, 32, 0 ) < 0 )
	if( DVDReadAsync( &audio_player.fileHandle, &workChunk, 32, 0, AUDSimpleOpenDVDCallback ) < 0 )
	{
#		ifdef _DEBUG
		OSReport( "*** Failed to read the header for %s.\n", fileName );
#		endif
		DVDClose( &audio_player.fileHandle );
		return FALSE;
	}

	strncpy( audio_player.fileName, fileName, 64 );
	audio_player.fileName[63]				= 0;

	// Nothing more to do here.
	return TRUE;
		
//	fileOffset += 32;
	
	// Check file id	
//	if( workChunk.id != VID_FCC('V','I','D','1' ))
//	{
//#		ifdef _DEBUG
//		OSReport("*** No VID1 file: '%s'\n", fileName);
//#		endif
//		DVDClose( &audio_player.fileHandle );
//		return FALSE;
//	}
	
	// Check for correct version of vid chunk.
	// If we find this version we assume a 'special' alignment and chunk ordering which may be invalid
	// in another version of the file format.
//	if( workChunk.vid.versionMajor != 1 || workChunk.vid.versionMinor != 0 )
//	{
//#		ifdef _DEBUG
//		OSReport("*** Unsupported file version: major: %d, minor: %d\n", workChunk.vid.versionMajor, workChunk.vid.versionMajor);
//#		endif
//		DVDClose(&audio_player.fileHandle);
//		return FALSE;
//	}
	
//#	ifdef _DEBUG
	// Sometimes, it's required to check for a special build of the VidConv converter.
//	{
//		u32 version = VID_VERSION(workChunk.vid.vidConvMajor, workChunk.vid.vidConvMinor, workChunk.vid.vidConvBuild);
//		if(version < VID_VERSION(1,0,1))
//		OSReport("*** WARNING: Vid file created using an unsupported converter version: %d.%d.%d\n", (u32)workChunk.vid.vidConvMajor, (u32)workChunk.vid.vidConvMinor, (u32)workChunk.vid.vidConvBuild);
//	}
//#	endif

	// Check types of chunks we have in this file.
	// !!! Note that we assume start of 'HEAD' chunk at byte offset 32 from file start !!!
//	if( DVDRead( &audio_player.fileHandle, &workChunk, 32, (s32)fileOffset ) < 0 )
//	{
//#		ifdef _DEBUG
//		OSReport("*** Failed to read 'HEAD' chunk.\n");
//#		endif
//		DVDClose( &audio_player.fileHandle );
//		return FALSE;
//	}
	
//	if( workChunk.id != VID_FCC('H','E','A','D' ))
//	{
//#		ifdef _DEBUG
//		OSReport("*** No HEAD chunk found at expected offset\n");
//#		endif
//		DVDClose(&audio_player.fileHandle);
//		return FALSE;
//	}
	
	// Calculate the start of the first frame chunk
	// (we know the header chunk starts at offset 32)
//	audio_player.nextFrameOffset = workChunk.len + 32;

	// Skip 'HEAD' chunk id, len and version fields
//	fileOffset += VID_CHUNK_HEADER_SIZE;
	
	// The header chunk contains one or more header chunks for the different data types contained
	// in the stream. Parse them all...
//	headSize = workChunk.len - VID_CHUNK_HEADER_SIZE;
//	while( headSize >= 32 )
//	{
//		if( DVDRead( &audio_player.fileHandle, &workChunk, 32, (s32)fileOffset ) < 0 )
//		{
//#			ifdef _DEBUG
//			OSReport("*** Error reading file at offset %d\n", fileOffset);
//#			endif
//			DVDClose(&audio_player.fileHandle);
//			return FALSE;
//		}
		
//		fileOffset += 32;
//		headSize -= 32;

		// We analyze the 1st 32 bytes of the chunk for a known header format
//		if(workChunk.id == VID_FCC('A','U','D','H'))
//		{
			// Allocate memory for audio header chunk
//			audio_player.audioHeaderChunk = (u8*)((*audio_player.cbAlloc)(workChunk.len));
//			audioInfoSize = workChunk.len - VID_CHUNK_HEADER_SIZE;
			
			// Copy the already loaded part
//			memcpy(audio_player.audioHeaderChunk, &workChunk, 32);
//			workChunk.len -= 32;
			
			// Read additional audio header bytes if the audio header is greater that 32 bytes
//			if(workChunk.len >= 32)
//			{
//				ASSERT((workChunk.len&31)==0);
//				if( DVDRead( &audio_player.fileHandle, audio_player.audioHeaderChunk + 32, workChunk.len, (s32)fileOffset ) < 0 )
//				{
//#					ifdef _DEBUG
//					OSReport("*** Error reading file at offset %d\n", fileOffset);
//#					endif
//					DVDClose(&audio_player.fileHandle);
//					return FALSE;
//				}
//				fileOffset += workChunk.len;
//				headSize -= workChunk.len;
//			}

			// Setup and calc the number of bytes which we are allowed to copy into the audioInfo struct
//			memcpy(&audio_player.audioInfo, audio_player.audioHeaderChunk+VID_CHUNK_HEADER_SIZE, MIN(audioInfoSize, sizeof(audio_player.audioInfo)));
//		}
//		else
//		{
//			// Skip unknown chunks. We already read 32 bytes for the header which we must subtract here.
//			fileOffset += workChunk.len - 32;
///			headSize -= workChunk.len - 32;
//		}
//	}
	
	// check if we have the correct vaud file version (>0)
//	if(audio_player.audioInfo.vaudex.version == 0)
//	{
//#		ifdef _DEBUG
//		OSReport("*** Invalid version in vaud file.");
//#		endif
//		DVDClose(&audio_player.fileHandle);
//		return FALSE;
//	}

	// we can only play audio files which have the following fiels set.
	// Note that in case of VIDEO files this fields are allowed to be 0.
//	ASSERT(audio_player.audioInfo.vaudex.maxBufferSize > 0);
//	ASSERT(audio_player.audioInfo.vaudex.frameCount > 0);
//	ASSERT(audio_player.audioInfo.vaudex.frameTimeMs > 0);
	
	// read beginning of 1st frame chunk to get required size information
//	if( DVDRead( &audio_player.fileHandle, &workChunk, 32 , (s32)audio_player.nextFrameOffset ) < 0 )
//	{
//#		ifdef _DEBUG
//		OSReport("*** Failed to read 'FRAM' chunk.\n");
//#		endif
//		DVDClose(&audio_player.fileHandle);
//		return FALSE;
//	}

//	if( workChunk.id != VID_FCC('F','R','A','M') )
//	{
//#		ifdef _DEBUG
//		OSReport("*** No FRAM chunk found.");
//#		endif
//		DVDClose(&audio_player.fileHandle);
//		return FALSE;
//	}

//	audio_player.nextFrameSize = workChunk.len; 		// 32 bytes of this chunk are already consumed, but we want to 'preload' the NEXT chunk's FRAM header
//	audio_player.nextFrameOffset += 32;
	
//	audio_player.firstFrameOffset = audio_player.nextFrameOffset;
//	audio_player.firstFrameSize   = audio_player.nextFrameSize;

//	strncpy(audio_player.fileName, fileName, 64);
//	audio_player.fileName[63] = 0;
	
//	audio_player.open 			 	= TRUE;

//	audio_player.readIndex 			= 0;
//	audio_player.decodeIndex 			= 0;
//	audio_player.lastDecodedFrame		= 0;
//	audio_player.error 			 	= FALSE;
//	audio_player.preFetchState 	 	= FALSE;
//	audio_player.loopMode			 	= FALSE;
//	audio_player.asyncDvdRunning		= FALSE;
//	audio_player.currentFrameCount 	= 0;
//	audio_player.readBufferBaseMem	= 0;

//	return TRUE;
} 	

/*!
 ******************************************************************************
 * \brief
 *		Close open video file
 *
 * \return
 *		TRUE if file is closed sucessfully.
 *
 ******************************************************************************
 */
BOOL AUDSimpleClose(void)
	{
	if (audio_player.open)
		{
		if (audio_player.preFetchState == FALSE)
			{
			if (!audio_player.asyncDvdRunning)
				{
				audio_player.open = FALSE;
				DVDClose(&audio_player.fileHandle);
				if(audio_player.audioHeaderChunk != NULL)
					{
					(*audio_player.cbFree)(audio_player.audioHeaderChunk);
					audio_player.audioHeaderChunk = NULL;
					}
				return TRUE;
				}
			}
		}
	return FALSE;
	}



/*!
 ******************************************************************************
 * \brief
 *		Decode all frame data
 *
 *		This function operates on the full frame input data. It forwards this
 *		data to the required decoder.
 *
 ******************************************************************************
 */
BOOL AUDSimpleDecode(void)
{
	BOOL enabled;
	u8* chunkStart;
	u32 chunkSize;
	u32 frameSize;

	if( audio_player.readBuffer[audio_player.decodeIndex].valid )
	{
		
		// ptr to our (pre-) loaded data INSIDE (!) 'FRAM' chunk
		// (in other words, the 'FRAM' chunk itself is not visible here)
		chunkStart = audio_player.readBuffer[audio_player.decodeIndex].ptr;

		// usually, we read additional 32 bytes for getting info about the NEXT chunk.
		// We only deal with the actual 'FRAM' chunk data here and adjust the size by 32 bytes.
        frameSize = audio_player.readBuffer[audio_player.decodeIndex].size - 32;
		
		// loop across ALL chunks inside 'FRAM'
		while(frameSize >= 32)
		{
			chunkSize = VID_CHUNK_LEN(chunkStart);
			
			if( VID_CHUNK_ID(chunkStart) == VID_FCC('A','U','D','D') )
			{
				// Get the data to the audio system...
				if(! AUDSimpleAudioDecode(chunkStart + VID_CHUNK_HEADER_SIZE, chunkSize - VID_CHUNK_HEADER_SIZE))
				{
#ifdef _DEBUG
					OSReport("*** AUDSimpleAudioDecode failed!\n");
#endif
				}
			}
#ifdef _DEBUG
			else
			{
				OSReport("*** AUDSimpleDecode: unknown chunk type!\n");
			}
#endif
			
			// goto next chunk
			chunkStart += chunkSize;
			frameSize -= chunkSize;
		}
			
		audio_player.lastDecodedFrame = audio_player.readBuffer[audio_player.decodeIndex].frameNumber;
		audio_player.readBuffer[audio_player.decodeIndex].valid = FALSE;
		audio_player.decodeIndex = (audio_player.decodeIndex + 1) % AUD_NUM_READ_BUFFERS;

		// check if loading is still running
		enabled = OSDisableInterrupts();
		if (!audio_player.readBuffer[audio_player.readIndex].valid && !audio_player.asyncDvdRunning)
			ReadFrameAsync();
		OSRestoreInterrupts(enabled);
		
        return TRUE;
	}

#ifdef _DEBUG
	OSReport("*** AUDSimpleDecode: No valid decode buffer found (?).\n");
#endif
	return FALSE;

}



/*!
 ******************************************************************************
 * \brief
 *		Change active play mask
 *
 ******************************************************************************
 */
void AUDSimplePlayMaskChange(void)
	{
	static u32 myPlayMask = 0x3;
	u32 maxChannels = audio_player.audioInfo.vaud.numChannels;
	ASSERT(maxChannels > 0 && maxChannels <= 32);

	myPlayMask <<= 1;
	if(myPlayMask >= ((u32)(1<
#include 
#include "vaud.h"

/******************************************************************************
 *  DEFINES
 ******************************************************************************/

//! Number of 'read-ahead' buffers
#define AUD_NUM_READ_BUFFERS	10
#define	ASYNC_OPEN_FAIL			666
#define	ASYNC_OPEN_SUCCESS		999

/******************************************************************************
 *  STRUCTS AND TYPES
 ******************************************************************************/

typedef struct {
	u8*				ptr;
	u32				size;
	u32				frameNumber;
	volatile BOOL 	valid;
} AudReadBuffer;

//! Required fields for simple demonstation player
typedef struct {

	VAUDAllocator 	cbAlloc;			// memory allocator
	VAUDDeallocator cbFree;				// memory deallocator

    DVDFileInfo		fileHandle;
	char			fileName[64];

	VidAUDH			audioInfo;			// common header and first 2 channels (or all channels if not ADPCM)
    u8*				audioHeaderChunk;
	
	BOOL            open;
	BOOL            error;
	BOOL			preFetchState;
	BOOL			loopMode;
	BOOL			asyncDvdRunning;
	u32				asyncOpenCallbackStatus;	// Value indicating status of callback position for async startup

	BOOL			decodeComplete;
	BOOL			playbackComplete;
	
	u32				nextFrameOffset;
	u32				nextFrameSize;
	u32				currentFrameCount;
	u32 			lastDecodedFrame;
	
	u32 			firstFrameOffset;
	u32				firstFrameSize;
	
	u32             readIndex;
	u32				decodeIndex;
	
	AudReadBuffer	readBuffer[AUD_NUM_READ_BUFFERS];
	u8*				readBufferBaseMem;
	
	VAUDDecoder		decoder;

} AudSimplePlayer;

/******************************************************************************
 *  PROTOTYPES
 ******************************************************************************/
extern void AUDSimpleInit(VAUDAllocator cbAlloc, VAUDDeallocator cbFree);

extern BOOL AUDSimpleAllocDVDBuffers(void);
extern void AUDSimpleFreeDVDBuffers(void);

extern BOOL AUDSimpleCreateDecoder(void);
extern void AUDSimpleDestroyDecoder(void);

extern BOOL AUDSimpleLoadStart(BOOL loop);
extern BOOL AUDSimpleLoadStop(void);

extern BOOL AUDSimpleOpen(char* fileName);
extern BOOL AUDSimpleClose(void);

extern BOOL AUDSimpleDecode(void);
extern u32  AUDSimpleGetAudioSampleRate(void);
extern u32  AUDSimpleGetFrameTime(void);

extern void AUDSimplePlayMaskChange(void);

extern BOOL AUDSimpleCheckDVDError(void);

#ifdef __cplusplus
}
#endif

#endif	// __AUDSIMPLE_PLAYER_H__




================================================
FILE: Code/Gel/Music/ngc/p_music.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2001 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL Library												**
**																			**
**	Module:			Music (Pcm)			 									**
**																			**
**	File name:		ngc/p_music.cpp											**
**																			**
**	Created by:		08/23/01	-	dc										**
**																			**
**	Description:	NGC audio streaming										**
**																			**
*****************************************************************************/

#ifndef DVDETH
#define USE_VORBIS
#endif		// DVDETH

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef USE_VORBIS
#include 
#include 
#endif

#include 
#include  
//#include 
#include "p_music.h"
#include 
#include "gfx\ngc\nx\nx_init.h"
#include 

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/

#ifdef USE_VORBIS
// Player instance
extern AudSimplePlayer audio_player;
#endif

//bool g_using_dtk = false;
//extern bool g_in_cutscene;

// Info about last 8 reads.
static DVDFileInfo* last_fileInfo[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
static void * last_addr[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; 
static s32 last_length[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
static s32 last_offset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

// Callback will happen 8 frames later.
DVDCallback last_callback[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };  
s32 last_callback_length[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 
DVDFileInfo* last_callback_fileInfo[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
int last_callback_counter[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; 

namespace Pcm
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define MAX_DTK_VOL				0xFF				// Max volume for DTK hardware streamed audio tracks.
#define MAX_VOL					0x7FFF
#define NORMAL_VOL				( MAX_VOL >> 1 )
#define UNALTERED_PITCH			0x10000L

#define	QUIT_DMA_INACTIVE			0
#define	QUIT_DMA_PENDING			1
#define	QUIT_DMA_COMPLETE			2

#define STREAM_BUFFER_HEADER_SIZE	96				// Size of the header for each stream
#define STREAM_BUFFER_SIZE			16384

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

struct FileStreamInfo
{
	int				fileSize;
	int				offset;
	int				hedOffset;
	int				offsetInARAM;
	float			pitch;
	float			pitchAdjustmentForSamplingRate;
	float			volumeL;
	float			volumeR;
	bool			paused;
	bool			hasStartedPlaying;
	unsigned int	currentFileInfo;
	DVDFileInfo		fileInfo[8];
	AXVPB*			p_voice;
	uint32			nextWrite;		// 0 for first half of ARAM buffer, 1 for second half of ARAM buffer.
	bool			has_paused;
	bool			preloadActive;
};

// This is also defined in p_sfx.cpp - should probably share a common definition.
struct sDSPADPCM
{
	// for WAV header generation during decode
	u32 num_samples;		// total number of RAW samples
	u32 num_adpcm_nibbles;	// number of ADPCM nibbles (including frame headers)
	u32 sample_rate;		// Sample rate, in Hz

	// DSP addressing and decode context
	u16 loop_flag; // 1=LOOPED, 0=NOT LOOPED
	u16 format; // Always 0x0000, for ADPCM
	u32 sa; // Start offset address for looped samples (zero for non-looped)
	u32 ea; // End offset address for looped samples
	u32 ca; // always zero
	u16 coef[16]; // decode coefficients (eight pairs of 16-bit words)

	// DSP decoder initial state
	u16 gain; // always zero for ADPCM
	u16 ps; // predictor/scale
	u16 yn1; // sample history
	u16 yn2; // sample history

	// DSP decoder loop context
	u16 lps; // predictor/scale for loop context
	u16 lyn1; // sample history (n-1) for loop context
	u16 lyn2; // sample history (n-2) for loop context
	u16 pad[10]; // reserved
};



/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

//#ifndef USE_VORBIS
//#ifndef DVDETH
//static DTKTrack	s_dtk_track;
//#endif		// DVDETH
//#endif		// USE_VORBIS

FileStreamInfo		gStreamInfo[NUM_STREAMS];
int					gPcmStatus					= 0;
unsigned int		gStreamVolume				= 100;
File::SHed*			gpStreamHed					= NULL;
int					stream_base[3];
int					stream_size[3];
static char			stream_0_mem[STREAM_BUFFER_SIZE + STREAM_BUFFER_HEADER_SIZE] __attribute__((aligned( 32 )));	// Must be 32 for DVD.
static char			stream_1_mem[STREAM_BUFFER_SIZE + STREAM_BUFFER_HEADER_SIZE] __attribute__((aligned( 32 )));	// Must be 32 for DVD.
static char			stream_2_mem[STREAM_BUFFER_SIZE + STREAM_BUFFER_HEADER_SIZE] __attribute__((aligned( 32 )));	// Must be 32 for DVD.
static char*		stream_mem_buffer[3]		= { stream_0_mem, stream_1_mem, stream_2_mem };
static ARQRequest	stream_arq_request[NUM_STREAMS];
bool				musicPaused					= false;
float				musicVolume					= 0.0f;
bool				musicPreloadActive			= false;
bool				streamsPaused				= false;

static uint32		sPreLoadChecksum[NUM_STREAMS];
static uint32		sPreLoadMusicChecksum;
static char			sPreLoadMusicFilename[256];

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

BOOL _DVDReadAsync( DVDFileInfo* fileInfo, void* addr, s32 length, s32 offset, DVDCallback callback )
{
	// See if this is a repeat read.
	bool repeat_read = false;
	for ( int lp = 0; lp < 8; lp++ )
	{
		if ( ( fileInfo == last_fileInfo[lp] ) &&
			 ( addr == last_addr[lp] ) &&
			 ( length == last_length[lp] ) &&
			 ( offset == last_offset[lp] ) )
		{
			repeat_read = true;
			break;
		}
	}

	if ( repeat_read )
	{
		// Find an empty slot for this callback.
		int cb = -1;
		for ( int lp = 0; lp < 8; lp++ )
		{
			if ( last_callback_counter[lp] == -1 )
			{
				cb = lp;
				break;
			}
		}

		if ( cb == -1 )
		{
#			ifdef __NOPT_FINAL__
			OSReport( "Warning: Overflowed DVD callback buffer\n" );
#			else
			Dbg_MsgAssert( 0, ( "Overflowed DVD callback buffer" ));
#			endif
			// Issue read normally in this case.
			DVDReadAsync( fileInfo, addr, length, offset, callback ); 
		}
		last_callback[cb] = callback;
		last_callback_length[cb] = length;
		last_callback_fileInfo[cb] = fileInfo;
		last_callback_counter[cb] = 8;
		
		return TRUE;
	}
	else
	{
		// Shunt last reads down & fill in new one.
		for ( int lp = 7; lp > 0; lp-- )
		{
			last_fileInfo[lp] = last_fileInfo[lp-1];
			last_addr[lp] = last_addr[lp-1];
			last_length[lp] = last_length[lp-1];
			last_offset[lp] = last_offset[lp-1];
		}
		last_fileInfo[0] = fileInfo;
		last_addr[0] = addr;
		last_length[0] = length;
		last_offset[0] = offset;

		return DVDReadAsync( fileInfo, addr, length, offset, callback );
	}
}

void setDolby( AXPB * p_pb, AXVPB * p_vpb, float volL, float volR, unsigned int pitch, bool set_pitch, float volume_percent )
{
	float	volumes[4];
	int		i_volumes[4];	//, max_i_volume;
	volumes[0] = 0.0f;
	volumes[1] = 0.0f;
	volumes[2] = 0.0f;
	volumes[3] = 0.0f;
	
	if( Sfx::isStereo )
	{
		if(( volL == 0.0f ) && ( volR == 0.0f ))
		{
			// Dave note - in theory setting the mixerCtrl to zero should cause the sound not to play.
			// In practice however it seems to lead to small audio 'pops'.
//			p_pb->mixerCtrl = 0;
//			p_vpb->sync |= AX_SYNC_USER_MIXCTRL;
			
			// Pointless doing any more work.
			p_pb->mix.vL	= 0;
			p_pb->mix.vR	= 0;
			p_pb->mixerCtrl = AX_PB_MIXCTRL_L | AX_PB_MIXCTRL_R;
			p_vpb->sync	   |= AX_SYNC_USER_MIX | AX_SYNC_USER_MIXCTRL;
		}
		else
		{
			// Get the length of the vector here which will be used to multiply out the normalised speaker volumes.
			Mth::Vector test( fabsf( volL ), fabsf( volR ), 0.0f, 0.0f );
			float amplitude = test.Length();

			// Look just at the normalized right component to figure the sound angle from Matt's calculations.
			test.Normalize();

			float angle;
			angle	= asinf( test[Y] );
			angle	= ( angle * 2.0f ) - ( Mth::PI * 0.5f );
			angle	= ( volL < 0.0f ) ? ( Mth::PI - angle ) : angle;

			// Now figure volumes based on speaker coverage.
			angle	= Mth::RadToDeg( angle );

			// Left front channel.
			if(( angle >= 225.0f ) || ( angle <= 45.0f ))
			{
				// Because of the discontinuity, shift this angle round to the [0,180] range.
				float shift_angle = angle + 135;
				shift_angle = ( shift_angle >= 360.0f ) ? ( shift_angle - 360.0f ) : shift_angle;
				volumes[0]	= ( shift_angle / 180.0f ) * Mth::PI;
				volumes[0]	= sqrtf( sinf( volumes[0] ));
			}

//			// Center channel.
//			if(( angle >= -60.0f ) && ( angle <= 60.0f ))
//			{
//				// Scale this into [0,PI] space so we can get smooth fadeout.
//				volumes[1]	= (( angle + 60.0f ) / 120.0f ) * Mth::PI;
//				volumes[1]	= sqrtf( sinf( volumes[1] ));
//			}

			// Right front channel.
			if(( angle >= -45.0f ) && ( angle <= 135.0f ))
			{
				// Scale this into [0,PI] space so we can get smooth fadeout.
				volumes[1]	= (( angle + 45.0f ) / 180.0f ) * Mth::PI;
				volumes[1]	= sqrtf( sinf( volumes[1] ));
			}

			// Right rear channel.
			if(( angle >= 45.0f ) && ( angle <= 225.0f ))
			{
				// Scale this into [0,PI] space so we can get smooth fadeout.
				volumes[2]	= (( angle - 45.0f ) / 180.0f ) * Mth::PI;
				volumes[2]	= sqrtf( sinf( volumes[2] ));
			}

			// Left rear channel.
			if(( angle >= 135.0f ) || ( angle <= -45.0f ))
			{
				// Because of the discontinuity, shift this angle round to the [0,180] range.
				float shift_angle = angle + 225;
				shift_angle = ( shift_angle >= 360.0f ) ? ( shift_angle - 360.0f ) : shift_angle;
				volumes[3]	= ( shift_angle / 180.0f ) * Mth::PI;
				volumes[3]	= sqrtf( sinf( volumes[3] ));
			}

			// Now readjust the relative values...
			for( int v = 0; v < 4; ++v )
			{
				// Scale back up to original amplitude.
				volumes[v] *= amplitude;

				// Then adjust for SFX volume level, and scale into limits.
//				volumes[v] = PERCENT( volume_percent, volumes[v] );
//				volumes[v] = fabsf( PERCENT( sfx_manager->GetMainVolume(), volumes[v] ));

				if( volumes[v] > 100.0f )
					volumes[v] = 100.0f;

			}

			// Now figure the attenuation of the sound. To convert to a decibel value, figure the ratio of requested
			// volume versus max volume, then calculate the log10 and multiply by (10 * 2). (The 2 is because sound
			// power varies as square of pressure, and squaring doubles the log value).
//#define DSBVOLUME_EFFECTIVE_MIN -6400
//#define DSBVOLUME_MAX 0
//			max_i_volume = DSBVOLUME_EFFECTIVE_MIN;
			for( int v = 0; v < 4; ++v )
			{
//				if( volumes[v] > 0.0f )
//				{
//					float attenuation	= 20.0f * log10f( volumes[v] * 0.01f );
//					i_volumes[v]		= DSBVOLUME_MAX + (int)( attenuation * 100.0f );
//					if( i_volumes[v] < DSBVOLUME_EFFECTIVE_MIN )
//						i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
//
//					if( i_volumes[v] > max_i_volume )
//						max_i_volume = i_volumes[v];
//				}
//				else
//				{
//					i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
//				}
				// GameCube:: Change from -6400 -> 0 to 0 -> 0x1fff.
//				i_volumes[v] = ( ( i_volumes[v] + -DSBVOLUME_EFFECTIVE_MIN ) * 0x1fff ) / -DSBVOLUME_EFFECTIVE_MIN;
//				i_volumes[v] = (int)PERCENT( volume_percent, (float)i_volumes[v] );
				i_volumes[v] = (int)(( volumes[v] * (float)0x3fff ) / 100.0f);
				i_volumes[v] = (int)PERCENT( volume_percent, (float)i_volumes[v] );
			}

			// Set individual mixbins for panning. Clampo very low volumes to zero, and for zero volumes, early out
			// without setting the Dolby stuff up.
			if(( i_volumes[0] <= 32 ) && ( i_volumes[1] <= 32 ) && ( i_volumes[2] <= 32 ) && ( i_volumes[3] <= 32 ))
			{
				// Pointless doing any more work.
				p_pb->mix.vL	= 0;
				p_pb->mix.vR	= 0;
				p_pb->mixerCtrl = AX_PB_MIXCTRL_L | AX_PB_MIXCTRL_R;
				p_vpb->sync	   |= AX_SYNC_USER_MIX | AX_SYNC_USER_MIXCTRL;
			}
			else
			{
				p_pb->mixerCtrl = AX_PB_MIXCTRL_B_DPL2;
			
				if( i_volumes[0] > 0 )
				{
					p_pb->mix.vL = i_volumes[0];
					p_pb->mixerCtrl |= AX_PB_MIXCTRL_L;
				}
	
				if( i_volumes[1] > 0 )
				{
					p_pb->mix.vR = i_volumes[1];
					p_pb->mixerCtrl |= AX_PB_MIXCTRL_R;
				}
	
				if( i_volumes[2] > 0 )
				{
					p_pb->mix.vAuxBL = i_volumes[2];
					p_pb->mixerCtrl |= AX_PB_MIXCTRL_B_L;
				}
	
				if( i_volumes[3] > 0 )
				{
					p_pb->mix.vAuxBR = i_volumes[3];
					p_pb->mixerCtrl |= AX_PB_MIXCTRL_B_R;
				}
				p_vpb->sync |= AX_SYNC_USER_MIX | AX_SYNC_USER_MIXCTRL;
			}
		}
	}
	else
	{
		// Adjust for SFX volume level.
		volL = PERCENT( volume_percent, volL );
		volR = PERCENT( volume_percent, volR );

		// Adjust for channel volume.
		int lVol = (int)PERCENT( 0x3fff, volL );	
		int rVol = (int)PERCENT( 0x3fff, volR );

		// Clamp to maximum allowed volume.
		if ( lVol > 0x7fff )
			lVol = 0x7fff;
		else if ( lVol < 0 )
			lVol = 0;

		if ( rVol > 0x7fff )
			rVol = 0x7fff;
		else if ( rVol < 0 )
			rVol = 0;

		p_pb->mix.vL	= ( lVol + rVol ) / 2;
		p_pb->mix.vR	= ( lVol + rVol ) / 2;
		p_pb->mixerCtrl = AX_PB_MIXCTRL_L | AX_PB_MIXCTRL_R;
		p_vpb->sync	   |= AX_SYNC_USER_MIX | AX_SYNC_USER_MIXCTRL;
	}
	
	if ( set_pitch )
	{
		p_pb->src.ratioHi		= ( pitch >> 16 );
		p_pb->src.ratioLo		= ( pitch & 0xFFFF );
		p_vpb->sync			   |= AX_SYNC_USER_SRCRATIO;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void ax_voice_reacquisition_callback( void* p )
{
	// Should never happen for stream voices.
	Dbg_Assert( 0 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
////#ifndef USE_VORBIS
//#ifndef DVDETH
//static void s_dtk_callback( u32 event_mask )
//{
//	if( event_mask & DTK_EVENT_TRACK_ENDED )
//	{
//		// Stop the music track.
//		DTKSetState( DTK_STATE_STOP );
//		gPcmStatus &= ~PCMSTATUS_MUSIC_PLAYING;
//	}
//
//	if( event_mask & DTK_EVENT_PLAYBACK_STARTED )
//	{
//		if( musicPaused )
//		{
//			// Pause the music track.
//			DTKSetState( DTK_STATE_PAUSE );
//		}
//	}
//}
//#endif		// DVDETH
////#endif		// USE_VORBIS



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void s_start_voice_callback( u32 pointerToARQRequest )
{
	// Figure which stream this is for.
	int stream = -1;
	
	for( int s = 0; s < NUM_STREAMS; ++s )
	{
		if( pointerToARQRequest == (u32)( &stream_arq_request[s] ))
		{
			stream = s;
			break;
		}
	}
	
	if( stream == -1 )
	{
		// Something has gone very wrong.
		Dbg_MsgAssert( 0, ( "Unsynced dvd callback" ));
		return;
	}

	// Deal with the voice already having been deleted.
	if( gStreamInfo[stream].p_voice == NULL )
	{
		return;
	}

	// Set the voice to run.
	gStreamInfo[stream].hasStartedPlaying	= true;
	gStreamInfo[stream].paused				= streamsPaused;
	if( !streamsPaused && !gStreamInfo[stream].preloadActive )
	{
		if( gStreamInfo[stream].p_voice == NULL )
		{
			Dbg_Assert( 0 );
			return;			
		}
		AXSetVoiceState( gStreamInfo[stream].p_voice, AX_PB_STATE_RUN );
	}

	// Adjust pitch to account for lower sampling rates:
	float			pitch	= PERCENT( gStreamInfo[stream].pitch, gStreamInfo[stream].pitchAdjustmentForSamplingRate );
	unsigned int	u_pitch	= (unsigned int)PERCENT( UNALTERED_PITCH, pitch );

	int old = OSDisableInterrupts();
	AXPB*	p_pb						= &( gStreamInfo[stream].p_voice->pb );
	Spt::SingletonPtr< Sfx::CSfxManager > sfx_manager;
	setDolby( p_pb, gStreamInfo[stream].p_voice, gStreamInfo[stream].volumeL, gStreamInfo[stream].volumeR, u_pitch, true, sfx_manager->GetMainVolume() );
	OSRestoreInterrupts( old );

	gPcmStatus |= PCMSTATUS_STREAM_PLAYING( stream );
	gPcmStatus &= ~PCMSTATUS_LOAD_STREAM( stream );
}

						

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void s_dvd_callback( s32 result, DVDFileInfo* fileInfo )
{
	// Figure which stream this is for.
	int stream	= -1;

	for( unsigned int i = 0; i < 8; ++i )
	{
		for( uint32 s = 0; s < NUM_STREAMS; ++s )
		{
			if( fileInfo == &( gStreamInfo[s].fileInfo[i] ))
			{
				if( i == gStreamInfo[s].currentFileInfo )
				{
					stream = s;
					break;
				}
			}
		}
		if( stream >= 0 )
			break;
	}

	if( stream == -1 )
	{
		// Possibly the result of a read in which we are no longer interested.
		return;
	}

	// Handle read errors by just stopping the stream.
	if(( result < 0 ) || gStreamInfo[stream].has_paused )
	{
		int enabled = OSDisableInterrupts();
		if( gStreamInfo[stream].p_voice )
		{
			// This should be the case.
			AXFreeVoice( gStreamInfo[stream].p_voice );
		}
		gStreamInfo[stream].p_voice			= NULL;
		gStreamInfo[stream].preloadActive	= false;
		gStreamInfo[stream].currentFileInfo	= ( gStreamInfo[stream].currentFileInfo + 1 ) & 0x07;
		OSRestoreInterrupts( enabled );

		gPcmStatus &= ~PCMSTATUS_LOAD_STREAM( stream );
		gPcmStatus &= ~PCMSTATUS_STREAM_PLAYING( stream );
		return;
	}

	// Check if has already been stopped.
	AXVPB* p_axvpb = gStreamInfo[stream].p_voice;
	if( p_axvpb == NULL )
	{
		return;
	}

	// If this is the first chunk read for this voice, need to grab the voice and look at the header information.
	if( gStreamInfo[stream].offset == 0 )
	{
		AXPBADDR        addr;
		AXPBADPCM       adpcm;
		AXPBVE			ve;
		
		int enabled = OSDisableInterrupts();
		// Grab pointer to header (first 96 byes of data).
		sDSPADPCM*	p_header		= (sDSPADPCM*)( stream_mem_buffer[stream] );

		addr.format					= AX_PB_FORMAT_ADPCM;   

		// Depending on the number of bytes in the sound, we may not need to loop.
		// Also depending on how many times we will be uploading to ARAM, we may want to start the stream
		// halfway through the ARAM buffer, in order to ensure that we always write the last block to the upper half of ARAM
		// (in order that we can safely set the end position).
		int num_adpcm_bytes			= p_header->num_adpcm_nibbles / 2;

		// Convert from byte to nibble address, and split into low and high words.
		u32 start_address			= ( stream_base[stream] * 2 ) + 2;
		addr.currentAddressHi		= (u16)( start_address >> 16 );
		addr.currentAddressLo		= (u16)( start_address & 0xFFFFU );

		if( num_adpcm_bytes <= 32768 )
		{
			// Stream fits in to ARAM buffer completely. No need to loop.
			u32 end_address					= ( stream_base[stream] * 2 ) + p_header->num_adpcm_nibbles - 1;
		
			addr.endAddressHi				= (u16)( end_address >> 16 );
			addr.endAddressLo				= (u16)( end_address & 0xFFFFU );
		
			u32 zero_buffer_address			= NxNgc::EngineGlobals.aram_zero * 2;
			addr.loopAddressHi				= (u16)( zero_buffer_address >> 16 );
			addr.loopAddressLo				= (u16)( zero_buffer_address & 0xFFFFU );
		
			addr.loopFlag					= AXPBADDR_LOOP_OFF;
		
			gStreamInfo[stream].nextWrite	= 1;
//			OSReport( "Stream: %d is simple <= 32768 bytes\n", stream );
		}
		else
		{
			// Stream does not fit in to ARAM buffer completely. Needs to loop.
			uint32 num_uploads				= num_adpcm_bytes / STREAM_BUFFER_SIZE;
			if(( num_adpcm_bytes % STREAM_BUFFER_SIZE ) > 0 )
				++num_uploads;
			Dbg_Assert( num_uploads > 2 );

			addr.loopAddressHi		= addr.currentAddressHi;
			addr.loopAddressLo		= addr.currentAddressLo;
			
			if( num_uploads & 0x01 )
			{
				// This is the tricky situation, where we start halfway through the ARAM buffer.
				gStreamInfo[stream].offsetInARAM   += STREAM_BUFFER_SIZE;
				start_address					   += ( STREAM_BUFFER_SIZE * 2 );
				addr.currentAddressHi				= (u16)( start_address >> 16 );
				addr.currentAddressLo				= (u16)( start_address & 0xFFFFU );
				gStreamInfo[stream].nextWrite		= 0;
//				OSReport( "Stream: %d is complex tricky %d bytes\n", stream, num_adpcm_bytes );
			
			}
			else
			{
				// This is the easy situation, where we start at the start of the ARAM buffer.
				gStreamInfo[stream].offsetInARAM	= 0;
				gStreamInfo[stream].nextWrite		= 1;
//				OSReport( "Stream: %d is complex non-tricky %d bytes\n", stream, num_adpcm_bytes );
			}
			
			u32 end_address			= ( stream_base[stream] * 2 ) + ( 32768 * 2 ) - 1;
			addr.endAddressHi		= (u16)( end_address >> 16 );
			addr.endAddressLo		= (u16)( end_address & 0xFFFFU );
		
			addr.loopFlag			= AXPBADDR_LOOP_ON;
		}

		memcpy( &adpcm.a[0][0], p_header->coef, sizeof( u16 ) * 16 );
		adpcm.gain					= p_header->gain;
		adpcm.pred_scale			= p_header->ps;
		adpcm.yn1					= p_header->yn1;
		adpcm.yn2					= p_header->yn2;

		ve.currentVolume			= 0x7FFF;
		ve.currentDelta				= 0;

		// Set voice parameters.
		AXSetVoiceType( p_axvpb, AX_PB_TYPE_NORMAL );		// No loop context.
		AXSetVoiceSrcType( p_axvpb, AX_PB_SRCSEL_LINEAR );	// Use linear interpolation.
		AXSetVoiceVe( p_axvpb, &ve );						// Set overall volume.
		AXSetVoiceAddr( p_axvpb, &addr );					// Input addressing.
		AXSetVoiceAdpcm( p_axvpb, &adpcm );					// ADPCM coefficients.

//		p_axvpb->sync |= AX_SYNC_USER_ALLPARAMS;
//		AXSetVoiceSrcRatio( p_axvpb, (48000.0f / ((float)AX_IN_SAMPLES_PER_SEC)) );

		// If this chunk is less than buffersize, zero out the excess bytes to prevent sound pop.
		if( result < ( STREAM_BUFFER_SIZE + STREAM_BUFFER_HEADER_SIZE ))
		{
			memset( stream_mem_buffer[stream] + result, 0, ( STREAM_BUFFER_SIZE + STREAM_BUFFER_HEADER_SIZE ) - result );
			DCFlushRange( stream_mem_buffer[stream] + result, ( STREAM_BUFFER_SIZE + STREAM_BUFFER_HEADER_SIZE ) - result );
		}
		OSRestoreInterrupts( enabled );

		// DMA this chunk up to ARAM, then use the ARQ callback to start the voice.
		ARQPostRequest(	&stream_arq_request[stream],							// Address to user-allocated storage for an ARQTask data structure.
						0,														// User-enumerated identifier for the owner of the ARAM transaction.
						ARQ_TYPE_MRAM_TO_ARAM,									// Direction.
						ARQ_PRIORITY_HIGH,										// Priority.
						(u32)( stream_mem_buffer[stream] + 96 ),				// Source address.
						stream_base[stream] + gStreamInfo[stream].offsetInARAM,	// Destination address.
						STREAM_BUFFER_SIZE,										// Transfer length (bytes). Must be multiple of 4.
						&s_start_voice_callback );								// DMA complete callback function.

		gStreamInfo[stream].pitchAdjustmentForSamplingRate	= ((float)p_header->sample_rate * 100.0f ) / 32000.0f;
		
		// Increment the ARAM offset and wrap when appropriate.
		gStreamInfo[stream].offsetInARAM += STREAM_BUFFER_SIZE;
		if( gStreamInfo[stream].offsetInARAM >= ( STREAM_BUFFER_SIZE * 2 ))
		{
			gStreamInfo[stream].offsetInARAM -= ( STREAM_BUFFER_SIZE * 2 );
		}
		
		// Increment the file pointer (remember we read the extra buffer bytes for the first read).
		gStreamInfo[stream].offset += STREAM_BUFFER_SIZE + STREAM_BUFFER_HEADER_SIZE;
	}
	else
	{
		// If this chunk is less than buffersize, zero out the excess bytes to prevent sound pop.
		if( result < STREAM_BUFFER_SIZE )
		{
			memset( stream_mem_buffer[stream] + result, 0, STREAM_BUFFER_SIZE - result );
			DCFlushRange( stream_mem_buffer[stream] + result, STREAM_BUFFER_SIZE - result );
		}

		// We want to DMA this chunk up to ARAM, but only if safe to do so, i.e. only when the playback position is in
		// the opposite half of the ARAM buffer.
		int		current_aram_pos	= ((uint32)( p_axvpb->pb.addr.currentAddressHi ) << 16 ) | (uint32)( p_axvpb->pb.addr.currentAddressLo );
		bool	safe_to_dma			= false;

		// Convert from current ARAM position (in nibbles) to current offset within buffer (in bytes).
		current_aram_pos			= ( current_aram_pos - ( stream_base[stream] * 2 )) / 2;
		
		if( current_aram_pos < STREAM_BUFFER_SIZE )
		{
			// Safe to write to the second half of the ARAM buffer, if that is what needs updating.
			if( gStreamInfo[stream].nextWrite == 1 )
			{
				safe_to_dma = true;
//				OSReport( "Stream: %d ARAM: %d writing to second half\n", stream, current_aram_pos );
			}
		}
		else
		{
			// Safe to write to the first half of the ARAM buffer, if that is what needs updating.
			if( gStreamInfo[stream].nextWrite == 0 )
			{
				safe_to_dma = true;
//				OSReport( "Stream: %d ARAM: %d writing to first half\n", stream, current_aram_pos );
			}
		}
		
		if( safe_to_dma )
		{
			// DMA this chunk up to ARAM.
			ARQPostRequest(	&stream_arq_request[stream],							// Address to user-allocated storage for an ARQTask data structure.
							0,														// User-enumerated identifier for the owner of the ARAM transaction.
							ARQ_TYPE_MRAM_TO_ARAM,									// Direction.
							ARQ_PRIORITY_HIGH,										// Priority.
							(u32)( stream_mem_buffer[stream] ),						// Source address.
							stream_base[stream] + gStreamInfo[stream].offsetInARAM,	// Destination address.
							STREAM_BUFFER_SIZE,										// Transfer length (bytes). Must be multiple of 4.
							NULL );													// DMA complete callback function.

			// Increment the ARAM offset and wrap when appropriate.
			gStreamInfo[stream].offsetInARAM += STREAM_BUFFER_SIZE;
			if( gStreamInfo[stream].offsetInARAM >= ( STREAM_BUFFER_SIZE * 2 ))
			{
				gStreamInfo[stream].offsetInARAM -= ( STREAM_BUFFER_SIZE * 2 );
			}
	
			// We have now processed another chunk.
			gStreamInfo[stream].nextWrite = ( gStreamInfo[stream].nextWrite == 0 ) ? 1 : 0;
		
			// Increment the file pointer.
			gStreamInfo[stream].offset += STREAM_BUFFER_SIZE;
		}
		else
		{
//			OSReport( "Stream: %d ARAM: %d discarding file buffer\n", stream, current_aram_pos );
		}
	}

	// If there is more data, kick off another read. NOTE this could potentially write over what we are about to DMA to ARAM.
	int total_bytes_after_next_read	= gStreamInfo[stream].offset + STREAM_BUFFER_SIZE;
	int bytes_to_read;
	if( total_bytes_after_next_read <= gStreamInfo[stream].fileSize )
	{
		bytes_to_read = STREAM_BUFFER_SIZE;
	}
	else
	{
		bytes_to_read = STREAM_BUFFER_SIZE - ( total_bytes_after_next_read - gStreamInfo[stream].fileSize );

		// Reduce bytes_to_read down to the next lowest multiple of 32.
		bytes_to_read &= ~31;
	}

	if( bytes_to_read > 0 )
	{
		_DVDReadAsync(	&( gStreamInfo[stream].fileInfo[gStreamInfo[stream].currentFileInfo] ),
						stream_mem_buffer[stream],												// Base pointer of memory to read data into.
						bytes_to_read,															// Bytes to read.
						gStreamInfo[stream].hedOffset + gStreamInfo[stream].offset,				// Offset in file.
						s_dvd_callback );														// Read complete callback.
	}
	else
	{
		// Here we may need to set the stop point for the voice, and switch from looping to non-looping.
		if( p_axvpb->pb.addr.loopFlag == AXPBADDR_LOOP_ON )
		{
//			OSReport( "Stream: %d switching to loop off\n", stream );
			p_axvpb->pb.addr.loopFlag		= AXPBADDR_LOOP_OFF;
			u32 zero_buffer_address			= NxNgc::EngineGlobals.aram_zero * 2;
			p_axvpb->pb.addr.loopAddressHi	= (u16)( zero_buffer_address >> 16 );
			p_axvpb->pb.addr.loopAddressLo	= (u16)( zero_buffer_address & 0xFFFFU );
			p_axvpb->sync |= ( AX_SYNC_USER_LOOP | AX_SYNC_USER_LOOPADDR );
		}
	}
}



/*****************************************************************************
**							    Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_Init( void )
{
////#	ifdef USE_VORBIS
////#	else
//#	ifndef DVDETH
//	DTKInit();
//	DTKSetVolume( 64, 64 );		// Defaults to half volume.
//	DTKSetRepeatMode( DTK_MODE_NOREPEAT );
//#	endif		// DVDETH
////#	endif		// VORBIS

	memset( gStreamInfo, 0, sizeof( FileStreamInfo ) * NUM_STREAMS );

	// Just zero out the audio_player structure.
	memset( &audio_player, 0, sizeof( audio_player ));
	
	Sfx::GetStreamBufferInfo( &stream_base[0], &stream_base[1], &stream_base[2],
							  &stream_size[0], &stream_size[1], &stream_size[2] );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int PCMAudio_SetMusicVolume( float volume )
{
	// Save this value off so it can be used again for async startup.
	musicVolume = volume;
	
	// Normalize down to match PS2.
	volume *= 0.5f;

//#	ifdef USE_VORBIS
//	if ( !g_using_dtk )
	{
#		ifdef USE_VORBIS
		if( PCMAudio_GetMusicStatus() == PCM_STATUS_PLAYING )
		{
			uint32 vol = (uint32)PERCENT( volume, 0x7FFF );
			AUDSimpleAudioSetVolume( vol, vol );
		}
#		endif	// VORBIS
	}
//	else
//	{
//		int vol = (int)PERCENT( volume, MAX_DTK_VOL );
//		if( vol > MAX_DTK_VOL )
//		{
//			vol = MAX_DTK_VOL;
//		}
//	#	ifndef DVDETH
//		DTKSetVolume( vol, vol );
//	#	endif	// DVDETH
//	}
////#	else	
////#	endif	// VORBIS

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_LoadMusicHeader( const char *nameOfFile )
{
	// For now, just fix this as true.
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_TrackExists( const char *pTrackName, int ch )
{
	if( ch == MUSIC_CHANNEL )
	{
		// For now, assume all music tracks exist.
		return true;

/*
		if( !gpMusicHed )
		{
			return false;
		}

		if( !FindFileInHed( pTrackName, gpMusicHed ))
		{
			Dbg_Message( "Track %s not found in header file.", pTrackName );
			return false;
		}

		return true;
*/
	}

	if( !gpStreamHed )
	{
		return false;
	}
	if( !FindFileInHed( pTrackName, gpStreamHed ))
	{
		return false;
	}
	return true;
}













#ifdef USE_VORBIS
/*!
 ******************************************************************************
 * \brief
 *		Memory allocation callback.
 *
 *		The player calls this function for all its memory needs. In this example
 *		an OSAlloc() is all we need to do.
 *
 * \note
 *		The returned memory address MUST be aligned on a 32 byte boundary.
 *		Otherwise, the player will fail!
 *
 * \param size
 *		Number of bytes to allocate
 *
 * \return
 *		Ptr to allocated memory (aligned to 32 byte boundary)
 *	
 ******************************************************************************
 */

static void* myAlloc(u32 size)
{
//	if ( g_in_cutscene )
//	{
//		int mem_available;
//
//		Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
//		mem_available = Mem::Manager::sHandle().Available();
//		if ( (int)size < ( mem_available - ( 30 * 1024 ) ) )
//		{
//		}
//		else
//		{
//			Mem::Manager::sHandle().PopContext();
//			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
//			mem_available = Mem::Manager::sHandle().Available();
//			if ( (int)size < ( mem_available - ( 5 * 1024 ) ) )
//			{
//			}
//			else
//			{
//				Mem::Manager::sHandle().PopContext();
//				Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ScriptHeap() );
//				mem_available = Mem::Manager::sHandle().Available();
//				if ( (int)size < ( mem_available - ( 15 * 1024 ) ) )
//				{
//				}
//				else
//				{
//					Mem::Manager::sHandle().PopContext();
//					Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneTopDownHeap());	
//				}
//			}
//		}
//	}
//	else
//	{

//	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
//	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().BottomUpHeap());
	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().AudioHeap());

//	}
	Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
	void * p = new u8[size]; 
	Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
	Mem::Manager::sHandle().PopContext();

	return p;
}

/*!
 ******************************************************************************
 * \brief
 *		Memory free callback.
 *
 * \param ptr
 *		Memory address to free
 *	
 ******************************************************************************
 */
static void myFree(void* ptr)
{
	ASSERT(ptr);	// free on address 0 is only valid in C++ delete
	delete [] ptr;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_DecodeLoop( void )
{
	// Deal with the asynchronous opening of music tracks.
	if( audio_player.asyncOpenCallbackStatus > 0 )
	{
		gPcmStatus |= PCMSTATUS_MUSIC_PLAYING;
	
		if( audio_player.asyncOpenCallbackStatus == ASYNC_OPEN_SUCCESS )
		{
			// Reset the callback status.
			audio_player.asyncOpenCallbackStatus = 0;
			
			// At this point we can complete the initialization process.
			if( AUDSimpleAllocDVDBuffers() == FALSE )
				OSHalt("*** AUDSimpleAllocDVDBuffers failed!\n");
			if( AUDSimpleCreateDecoder() == FALSE )
				OSHalt("*** AUDSimpleCreateDecoder failed!\n");
			if( AUDSimpleInitAudioDecoder() == FALSE )
				OSHalt("*** AUDSimpleInitAudioDecoder failed!\n");
			
			// Reset the music volume, now that we have voices allocated.
			PCMAudio_SetMusicVolume( musicVolume );
			
			// Preload all DVD buffers and set 'loop' mode to false.
			AUDSimpleLoadStart( FALSE );
		}
		return;
	}
	
	// Decode one frame.
	if( audio_player.decoder && !musicPaused )
	{
		gPcmStatus |= PCMSTATUS_MUSIC_PLAYING;
		
		if(	audio_player.decodeComplete )
		{
			if(	audio_player.playbackComplete )
			{
//				OSReport( "Ending song\n" );
			
				// Come to the end of the music data. Free our resources.
				AUDSimpleLoadStop();
				AUDSimpleClose();
				AUDSimpleFreeDVDBuffers();
				
				AUDSimpleDestroyDecoder();
				AUDSimpleExitAudioDecoder();
			
				gPcmStatus &= ~PCMSTATUS_MUSIC_PLAYING;
			}
		}
		else if( audio_player.preFetchState && !musicPreloadActive )
		{
			// See how many free samples in the read buffer (the big buffer into which the bitstream
			// is decoded). Overflowing this buffer causes DivX to behave very badly.
			u32	free_samples = AUDSimpleAudioGetFreeReadBufferSamples();
//			OSReport( "Free samples is: %d\n", free_samples );

			if( free_samples > 20000 )
			{
				// 20000 samples was derived empirically.
				if( AUDSimpleDecode() == FALSE )
				{
//					OSReport( "Decode complete\n" );
					audio_player.decodeComplete = true;
				}
			}
			else if( audio_player.playbackComplete )
			{
				// Seems like if the CPU is starved, the playbackComplete flag can be set despite the 
				// decodeComplete flag not being set. Try to cater for this situation here.
				if( AUDSimpleDecode() == FALSE )
				{
//					OSReport( "Decode complete\n" );
					audio_player.decodeComplete = true;
				}
			}
		}
	}

	// The decoder changes these, so reset them, otherwise animations/particles are messed up.
    GQRSetup6( GQR_SCALE_64,		// Pos
               GQR_TYPE_S16,
			   GQR_SCALE_64,
               GQR_TYPE_S16 );
    GQRSetup7( 14,		// Normal
               GQR_TYPE_S16,
               14,
               GQR_TYPE_S16 );
//	if( AUDSimpleCheckDVDError() == FALSE )
//	{
//		OSHalt( "*** DVD error occured. A true DVD error handler is not part of this demo.\n" );
//	}


}
#endif







/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_PlayMusicTrack( const char *filename, bool preload )
{
	// Set the preload flag.
	musicPreloadActive = preload;
	
	if ( NxNgc::EngineGlobals.disableReset ) return false;

	if( PCMAudio_GetMusicStatus() != PCM_STATUS_FREE )
	{
		Dbg_MsgAssert( 0, ( "Playing new track without stopping the first track." ));
		return false;
	}

	File::StopStreaming();

//#	ifdef USE_VORBIS
#	ifdef USE_VORBIS
	{
		// Fix up the filename, which comes in the form music\vag\songs\, and add the .ogg extension.
		static char	name_buffer[256] = "music/ogg/";
		strcpy( &name_buffer[10], &filename[10] );
		strcpy( name_buffer + strlen( name_buffer ), ".ogg" );
		char* p = name_buffer;
		while( *p != '\0' )
		{
			if ( *p == '\\' ) *p = '/';
			++p;
		}

		AUDSimpleInit( myAlloc, myFree );
		
		// This will open the file asynchronously, so nothing after this point except wait.
		if( AUDSimpleOpen( name_buffer ) == FALSE )
		{
			// File not found.
			return false;
		}

		audio_player.decodeComplete		= false;
		audio_player.playbackComplete	= false;
		
//		if( AUDSimpleAllocDVDBuffers() == FALSE )
//			OSHalt("*** AUDSimpleAllocDVDBuffers failed!\n");
//		if( AUDSimpleCreateDecoder() == FALSE )
//			OSHalt("*** AUDSimpleCreateDecoder failed!\n");
//		if( AUDSimpleInitAudioDecoder() == FALSE )
//			OSHalt("*** AUDSimpleInitAudioDecoder failed!\n");

		// Preload all DVD buffers and set 'loop' mode
//		AUDSimpleLoadStart( FALSE );
		
		return true;
	}
#	endif		// USE_VORBIS
////#	else
//
//	// Allow turning off of music from script global.
////	int testMusic = Script::GetInt( 0x47ac7ba5, false ); // checksum 'testMusicFromHost'
////	if( !testMusic )
////	{
////		return false;
////	}
//
//	{
//		// Fix up the filename, and add the .dtk extension.
//		static char	name_buffer[256] = "music/dtk/";
//		strcpy( &name_buffer[10], &filename[10] );
//		strcpy( name_buffer + strlen( name_buffer ), ".dtk" );
//		char* p = name_buffer;
//		while( *p != '\0' )
//		{
//			if ( *p == '\\' ) *p = '/';
//			++p;
//		}
//
//	#	ifndef DVDETH
//		u32 dtk_result = DTKQueueTrack( name_buffer, &s_dtk_track, 0xff, s_dtk_callback );
//	//	Dbg_MsgAssert( dtk_result == DTK_QUEUE_SUCCESS, ( "Failed to play: %s\n", name_buffer ));
//
//		if( dtk_result == DTK_QUEUE_SUCCESS )
//		{
//			if(	musicPaused )
//			{
//				// The callback will handle pausing the track.
//				DTKSetState( DTK_STATE_RUN );
//	//			DTKSetState( DTK_STATE_PAUSE );
//			}
//			else
//			{
//				DTKSetState( DTK_STATE_RUN );
//			}
//
//			gPcmStatus |= PCMSTATUS_MUSIC_PLAYING;
//			return true;
//		}
//	#	endif		// DVDETH
//		// If we get here then the track did not get queued.
//	}
//	return false;
////#	endif // USE_VORBIS
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_StopMusic( bool waitPlease )
{
//	int status = PCMAudio_GetMusicStatus();

//	if ( g_using_dtk )
//	{
//		if( status == PCM_STATUS_LOADING )
//		{
//			Dbg_MsgAssert( 0, ( "What, how can we be loading?" ));
//		}
//		else if( status == PCM_STATUS_PLAYING )
//		{
//			// Stop the track playing.
//#			ifndef DVDETH
//			DTKSetState( DTK_STATE_STOP );
//#			endif		// DVDETH
//		}
//		gPcmStatus &= ~PCMSTATUS_MUSIC_PLAYING;
//	}
//	else
	{
#		ifdef USE_VORBIS
		if( audio_player.asyncOpenCallbackStatus > 0 )
		{
			// Player is in async startup state, hasn't allocated buffers yet.
			audio_player.error = TRUE;
		}
		else if( audio_player.decoder )
		{
			if( !audio_player.preFetchState )
			{
				// Still seeding the DVD buffers, so dangerous to stop the music immediately.
				s32 tick = OSGetTick();
				while( audio_player.preFetchState == false )
				{
					s32 diff = OSDiffTick( OSGetTick(), tick );
					
					// Wait no more than quarter of a second.
					if( diff > (s32)( OS_TIMER_CLOCK / 4 ))
					{
						break;
					}
				}
			}
			
			AUDSimpleAudioReset();
			AUDSimpleLoadStop();
			AUDSimpleClose();
	
			AUDSimpleFreeDVDBuffers();
			AUDSimpleDestroyDecoder();
			AUDSimpleExitAudioDecoder();
		}
#	endif
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int PCMAudio_GetMusicStatus( void )
{
//	if ( g_using_dtk )
//	{
//		if( gPcmStatus & PCMSTATUS_LOAD_MUSIC )
//		{
//			return PCM_STATUS_LOADING;
//		}
//		else if( gPcmStatus & PCMSTATUS_MUSIC_PLAYING )
//		{
//			return PCM_STATUS_PLAYING;
//		}
//		else
//		{
//			// Sanity check.
//	#		ifndef DVDETH
//			u32 state = DTKGetState();
//			if( state == DTK_STATE_BUSY )
//			{
//				return PCM_STATUS_PLAYING;
//			}
//	#		endif		// DVDETH
//			return PCM_STATUS_FREE;
//		}
//		return 0;
//	}
//	else
	{
#		ifdef USE_VORBIS
		if( audio_player.asyncOpenCallbackStatus > 0 )
		{
			return PCM_STATUS_LOADING;
		}
		if( audio_player.decoder )
		{
			return PCM_STATUS_PLAYING;
		}
#		endif // USE_VORBIS
		return PCM_STATUS_FREE;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_Pause( bool pause, int ch )
{
	if( ch == MUSIC_CHANNEL )
	{
		musicPaused = pause;

//		if ( g_using_dtk )
//		{
//#			ifndef DVDETH
//			u32 state = DTKGetState();
//			if( pause && ( state == DTK_STATE_RUN ))
//			{
//				DTKSetState( DTK_STATE_PAUSE );
//			}
//			else if( !pause && ( state == DTK_STATE_PAUSE ))
//			{
//				DTKSetState( DTK_STATE_RUN );
//			}
//#			endif	// DVDETH
//		}
//		else
		{
#			ifdef USE_VORBIS
			if( audio_player.decoder )
			{
				AUDSimplePause( pause );
			}

//			PCMAudio_StopMusic( true );
#			endif // USE_VORBIS
		}
//#		ifdef USE_VORBIS
//#		else
//#		endif	// USE_VORBIS
	}
	else
	{
		streamsPaused = pause;

		for( int i = 0; i < NUM_STREAMS; ++i )
		{
			if( gStreamInfo[i].p_voice )
			{
				if( pause && !gStreamInfo[i].paused )
				{
					gStreamInfo[i].paused = true;
					AXSetVoiceState( gStreamInfo[i].p_voice, AX_PB_STATE_STOP );
				}
				else if( !pause && gStreamInfo[i].paused )
				{
					gStreamInfo[i].paused = false;
					AXSetVoiceState( gStreamInfo[i].p_voice, AX_PB_STATE_RUN );
				}
			}
		}
		if ( pause )
		{
			for( int s = 0; s < NUM_STREAMS; ++s )
			{
				gStreamInfo[s].has_paused = true;
				if( gStreamInfo[s].p_voice )
					setDolby( &gStreamInfo[s].p_voice->pb, gStreamInfo[s].p_voice, 0, 0 );
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/* Returns true if there is a pcm error.                          */
/*                                                                */
/******************************************************************/
//static uint64 last_vb = 0;
int PCMAudio_Update( void )
{
//	uint64 vb = Tmr::GetVblanks();
//	if ( last_vb == vb ) return true;
//	last_vb = vb;

	gPcmStatus &= ~PCMSTATUS_MUSIC_PLAYING;

//	if ( g_using_dtk )
//	{
//#		ifndef DVDETH
//		u32 state = DTKGetState();
//	
//		if( state != DTK_STATE_STOP )
//		{
//			gPcmStatus |= PCMSTATUS_MUSIC_PLAYING;
//		}
//#		endif	// DVDETH
//	}
//	else
	{
#		ifdef USE_VORBIS
		PCMAudio_DecodeLoop();
#		endif		// USE_VORBIS
	}
#	ifdef USE_VORBIS
#	else
#	endif	// USE_VORBIS

	// Check the stream voices to see if they are finished playing.
	bool check = false;
	for( int i = 0; i < NUM_STREAMS; ++i )
	{
		if( gStreamInfo[i].p_voice )
		{
			if(( gStreamInfo[i].p_voice->pb.state == AX_PB_STATE_STOP ) && ( gStreamInfo[i].hasStartedPlaying ) && !gStreamInfo[i].paused && !gStreamInfo[i].preloadActive )
			{
				// Playback on this voice has ended, so free up the voice.
				int enabled = OSDisableInterrupts();
				AXFreeVoice( gStreamInfo[i].p_voice );
				gStreamInfo[i].p_voice = NULL;
				gStreamInfo[i].currentFileInfo = ( gStreamInfo[i].currentFileInfo + 1 ) & 0x07;
				OSRestoreInterrupts( enabled );

				gPcmStatus &= ~PCMSTATUS_LOAD_STREAM( i );
				gPcmStatus &= ~PCMSTATUS_STREAM_PLAYING( i );
//				OSReport( "freed voice for stream: %d\n", i );
			}
			else
			{
				// Check to see whether the sound has played beyond the amount of data already dma'ed.
				int offset = gStreamInfo[i].p_voice->pb.addr.currentAddressLo + ((int)gStreamInfo[i].p_voice->pb.addr.currentAddressHi * 65536 );

				// Convert offset from nibble to byte address, and make relative to stream base.
				offset = ( offset / 2 ) - stream_base[i];
//				OSReport( "stream %d offset is at: %d\n", i, offset );

				if( offset > gStreamInfo[i].offsetInARAM )
				{
//					OSReport( "play point beyond byuffer...\n" );
				}

				// Flag this stream as playing.
				gPcmStatus |= PCMSTATUS_STREAM_PLAYING( i );
				check = true;
			}
		}
	}
	// Deal with disk errors.
	if ( gPcmStatus & PCMSTATUS_MUSIC_PLAYING ) check = true;
	if ( check ) DVDCheckAsync();
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 PCMAudio_FindNameFromChecksum( uint32 checksum, int ch )
{
	File::SHed*	pHed;

	if( ch == EXTRA_CHANNEL )
	{
		pHed = gpStreamHed;
	}
	else
	{
		// Don't use the music hed file.
		return NULL;
	}

	if( !pHed )
	{
		return NULL;
	}

	File::SHedFile* pHedFile = File::FindFileInHedUsingChecksum( checksum, pHed );
	if( pHedFile )
	{
		return pHedFile->Checksum;
	}
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_PlayStream( uint32 checksum, int whichStream, float volumeL, float volumeR, float fPitch, bool preload )
{
//#ifdef 
	
	if( PCMAudio_GetStreamStatus( whichStream ) != PCM_STATUS_FREE )
	{
		return false;
	}

//	int testStreams = Script::GetInt( 0x62df9442, false ); // checksum 'testStreamsFromHost'
//	if( !testStreams )
//	{
//		return false;
//	}

	// Incoming filenames are either of the form '\skateshp\menu', or '\streams\la\la_argue51' etc.
	// Strip the preceeding '\streams' if present.
//	if( strnicmp( filename, "\\streams", 8 ) == 0 )
//	{
//		filename += 8;
//	}
//
//	static char name_buffer[128] = "streams/dsp";
//	const int MUSIC_PREPEND_START_POS = 11;
//
//	// Need to append file type. Copy string into a buffer. Check there is room to add the extension.
//	int length = strlen( filename );
//	Dbg_Assert( length <= ( 128 - ( MUSIC_PREPEND_START_POS + 4 )));
//	strcpy( name_buffer + MUSIC_PREPEND_START_POS, filename );
//	strcpy( name_buffer + MUSIC_PREPEND_START_POS + length, ".dsp" );
//
//	// Fix directory separators.
//	char* p = name_buffer;
//	while( *p != '\0' )
//	{
//		if ( *p == '\\' ) *p = '/';
//		++p;
//	}

	File::SHedFile *pHed = FindFileInHedUsingChecksum( checksum, gpStreamHed );

#ifdef DVDETH
	BOOL result = false;
#else
	BOOL result = DVDFastOpen( DVDConvertPathToEntrynum( "streams/streamsngc.wad" ), &( gStreamInfo[whichStream].fileInfo[gStreamInfo[whichStream].currentFileInfo] ) );
#endif
//	DVDSeek( &( gStreamInfo[whichStream].fileInfo[gStreamInfo[whichStream].currentFileInfo] ), pHed->Offset );
	if( result )
	{
		if ( pHed && pHed->FileSize )
		{
			// Allocate a voice for use, and store it for later.
			AXVPB*		p_axvpb							= AXAcquireVoice( AX_PRIORITY_NODROP, ax_voice_reacquisition_callback, 0 );
			Dbg_Assert( p_axvpb );
			gStreamInfo[whichStream].p_voice			= p_axvpb;
			gStreamInfo[whichStream].paused				= false;
			gStreamInfo[whichStream].hasStartedPlaying	= false;
	
			// Flag that we are loading the stream up.
			gPcmStatus |= PCMSTATUS_LOAD_STREAM( whichStream );
	
			// Read in the first chunk.
			gStreamInfo[whichStream].offset				= 0;
			gStreamInfo[whichStream].offsetInARAM		= 0;
			gStreamInfo[whichStream].nextWrite			= 0;
	
			gStreamInfo[whichStream].hedOffset			= pHed->Offset;
			
			// Round filesize up to 32 byte alignment.
			gStreamInfo[whichStream].fileSize			= pHed->FileSize;
			gStreamInfo[whichStream].pitch				= fPitch;
			gStreamInfo[whichStream].volumeL			= volumeL;
			gStreamInfo[whichStream].volumeR			= volumeR;
//			OSReport( "%s %d\n", name_buffer, gStreamInfo[whichStream].fileSize );
	
			// Shhhh.....
			setDolby( &p_axvpb->pb, p_axvpb, 0, 0 );

			// Set the preload flag.
			gStreamInfo[whichStream].preloadActive = preload;
			
			// The first chunk we read is STREAM_BUFFER_HEADER_SIZE bigger than normal, so that even with the header in
			// the first read, we can still DMA the full amount up to ARAM.
			int bytes_to_read = ( pHed->FileSize >= ( STREAM_BUFFER_SIZE + STREAM_BUFFER_HEADER_SIZE )) ? ( STREAM_BUFFER_SIZE + STREAM_BUFFER_HEADER_SIZE ) : ( pHed->FileSize & ~31 );
			gStreamInfo[whichStream].has_paused = false;
//			OSReport( "Starting stream %x on channel %d\n", checksum, whichStream );
			result = DVDReadAsync(	&( gStreamInfo[whichStream].fileInfo[gStreamInfo[whichStream].currentFileInfo] ),
									stream_mem_buffer[whichStream],		// Base pointer of memory to read data into.
									bytes_to_read,						// Bytes to read.
									pHed->Offset,						// Offset in file.
									s_dvd_callback );					// Read complete callback.
	
			// Nothing more required for now.
			return true;
		}
		else
		{
			DVDClose( &( gStreamInfo[whichStream].fileInfo[gStreamInfo[whichStream].currentFileInfo] ));
		}
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_SetStreamGlobalVolume( unsigned int volume )
{
	gStreamVolume = volume;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_SetStreamVolume( float volumeL, float volumeR, int whichStream )
{
	if( gStreamInfo[whichStream].p_voice )
	{
		int old = OSDisableInterrupts();

		AXPB*	p_pb	= &( gStreamInfo[whichStream].p_voice->pb );

		Spt::SingletonPtr< Sfx::CSfxManager > sfx_manager;
		volumeL = PERCENT( sfx_manager->GetMainVolume() / 2.0f, volumeL );
		volumeR = PERCENT( sfx_manager->GetMainVolume() / 2.0f, volumeR );
		setDolby( p_pb, gStreamInfo[whichStream].p_voice, volumeL, volumeR );

		OSRestoreInterrupts( old );

//		// Adjust stream volume based on global sound effect volume.
//		Spt::SingletonPtr< Sfx::CSfxManager > sfx_manager;
//		p_pb->mix.vL						= (unsigned int)(( volL * sfx_manager->GetMainVolume()) * 0.01f );
//		p_pb->mix.vR						= (unsigned int)(( volR * sfx_manager->GetMainVolume()) * 0.01f );
//
//		if( !Sfx::isStereo )
//		{
//			unsigned int mix	= ( p_pb->mix.vL + p_pb->mix.vR ) / 2;
//			p_pb->mix.vL		= mix;
//			p_pb->mix.vR		= mix;
//		}
//
//		gStreamInfo[whichStream].p_voice->sync  |= ( AX_SYNC_USER_MIX );
	}

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_SetStreamPitch( float fPitch, int whichStream )
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_StopStream( int whichStream, bool waitPlease )
{
	// Clear the preload flag to be safe.
	gStreamInfo[whichStream].preloadActive	= false;
	sPreLoadChecksum[whichStream]			= 0;
	
	if( gStreamInfo[whichStream].p_voice )
	{
		int enabled = OSDisableInterrupts();
		AXFreeVoice( gStreamInfo[whichStream].p_voice );
		gStreamInfo[whichStream].p_voice			= NULL;
		gStreamInfo[whichStream].currentFileInfo	= ( gStreamInfo[whichStream].currentFileInfo + 1 ) & 0x07;
		OSRestoreInterrupts( enabled );

		gPcmStatus &= ~PCMSTATUS_LOAD_STREAM( whichStream );
		gPcmStatus &= ~PCMSTATUS_STREAM_PLAYING( whichStream );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_StopStreams( void )
{
	for( int i = 0; i < NUM_STREAMS; i++ )
	{
		PCMAudio_StopStream( i, true );
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/******************************************************************/
/*                                                                */
/* Get a stream loaded into a buffer, but don't play yet.		  */
/*                                                                */
/******************************************************************/
bool PCMAudio_PreLoadStream( uint32 checksum, int whichStream )
{
	bool rv = PCMAudio_PlayStream( checksum, whichStream, 0.0f, 0.0f, 100.0f, true );
	if( rv )
	{
		sPreLoadChecksum[whichStream] = checksum;
	}
	return rv;
}



/******************************************************************/
/*                                                                */
/* Returns true if preload done. Assumes that caller is calling	  */
/* this on a preloaded, but not yet played, stream. The results	  */
/* are meaningless otherwise.									  */
/*                                                                */
/******************************************************************/
bool PCMAudio_PreLoadStreamDone( int whichStream )
{
	if( gStreamInfo[whichStream].preloadActive )
	{
		// The hasStartedPlaying member will be set to true in the start voice callback, which is when
		// we are ready to allow playback.
		if( gStreamInfo[whichStream].hasStartedPlaying )
		{
			return true;
		}
	}
	return false;
}



/******************************************************************/
/*                                                                */
/* Tells a preloaded stream to start playing. Must call			  */
/* PCMAudio_PreLoadStreamDone() first to guarantee that it starts */
/* immediately.													  */
/*                                                                */
/******************************************************************/
bool PCMAudio_StartPreLoadedStream( int whichStream, float volumeL, float volumeR, float pitch )
{
	Dbg_Assert( gStreamInfo[whichStream].offset > 0 );
	
	sPreLoadChecksum[whichStream] = 0;
	
	if( gStreamInfo[whichStream].p_voice )
	{
		gStreamInfo[whichStream].preloadActive = false;
		
		// If we are not paused, set voice to RUN.
		if( !streamsPaused )
		{
			AXSetVoiceState( gStreamInfo[whichStream].p_voice, AX_PB_STATE_RUN );
		}
		
		// Set the correct volume.
		PCMAudio_SetStreamVolume( volumeL, volumeR, whichStream );
		PCMAudio_SetStreamPitch( pitch, whichStream );
		return true;
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_PreLoadMusicStream( uint32 checksum )
{
	sPreLoadMusicChecksum = checksum;

	static char* p_filename[] =
	{
		"AU/AU_01V_Female",
		"AU/AU_01V_Male",
		"AU/AU_02_Female",
		"AU/AU_02_Male",
		"Final/Final_Female",
		"Final/Final_Male",
		"FL/FL_01_Female",
		"FL/FL_01_Male",
		"FL/FL_02_Female",
		"FL/FL_02_Male",
		"FL/FL_03_Female",
		"FL/FL_03_Male",
		"FL/FL_04_Female",
		"FL/FL_04_Male",
		"HI/HI_01_Female",
		"HI/HI_01_Male",
		"HI/HI_02V_Female",
		"HI/HI_02V_Male",
		"HI/HI_03_Female",
		"HI/HI_03_Male",
		"HI/HI_04_Female",
		"HI/HI_04_Male",
		"HI/HI_05_Female",
		"HI/HI_05_Male",
		"Intro/Intro_01_Female",
		"Intro/Intro_01_Male",
		"Intro/Intro_02_Female",
		"Intro/Intro_02_Male",
		"NJ/NJ_01V_Female",
		"NJ/NJ_01V_Male",
		"NJ/NJ_02A_Female",
		"NJ/NJ_02A_Male",
		"NJ/NJ_02B_Female",
		"NJ/NJ_02B_Male",
		"NJ/NJ_02_Female",
		"NJ/NJ_02_Male",
		"NJ/NJ_03_Female",
		"NJ/NJ_03_Male",
		"NJ/NJ_04_Female",
		"NJ/NJ_04_Male",
		"NJ/NJ_05B_Female",
		"NJ/NJ_05B_Male",
		"NJ/NJ_05_Female",
		"NJ/NJ_05_Male",
		"NJ/NJ_06_Female",
		"NJ/NJ_06_Male",
		"NJ/NJ_07_Female",
		"NJ/NJ_07_Male",
		"NJ/NJ_08_Female",
		"NJ/NJ_08_Male",
		"NJ/NJ_09_ALT_Female",
		"NJ/NJ_09_ALT_Male",
		"NJ/NJ_09_Female",
		"NJ/NJ_09_Male",
		"NJ/NJ_10_Female",
		"NJ/NJ_10_Male",
		"NJ/NJ_Pool_Female",
		"NJ/NJ_Pool_Male",
		"NY/NY_01V_Female",
		"NY/NY_01V_Male",
		"NY/NY_02_Female",
		"NY/NY_02_Male",
		"NY/NY_03_Female",
		"NY/NY_03_Male",
		"RU/RU_01V_Female",
		"RU/RU_01V_Male",
		"RU/RU_02_Female",
		"RU/RU_02_Male",
		"RU/RU_03B_Female",
		"RU/RU_03B_Male",
		"RU/RU_03_Female",
		"RU/RU_03_Male",
		"SD/SD_01_Female",
		"SD/SD_01_Male",
		"SD/SD_02_Female",
		"SD/SD_02_Male",
		"SD/SD_03_Female",
		"SD/SD_03_Male",
		"SJ/SJ_01A_Female",
		"SJ/SJ_01A_Male",
		"SJ/SJ_01B_Female",
		"SJ/SJ_01B_Male",
		"SJ/SJ_02_Female",
		"SJ/SJ_02_Male",
		"VC/VC_01V_Female",
		"VC/VC_01V_Male",
		"VC/VC_02_Female",
		"VC/VC_02_Male"
	};

	// Clear the preload flag.
	musicPreloadActive = false;
	
	for ( uint lp = 0; lp < ( sizeof( p_filename ) / sizeof( char* )); lp++ )
	{
		char *p_name = strchr( p_filename[lp], '/' );
		if( p_name )
		{
			uint32 name_checksum = Crc::GenerateCRCFromString( p_name + 1 );
			if( checksum == name_checksum )
			{
				// Got a match - hook up filename here....
				sprintf( sPreLoadMusicFilename, "music/vag/cutscenes/%s", p_filename[lp] );
				return PCMAudio_PlayMusicTrack( sPreLoadMusicFilename, true );
			}
		}
	}
	sPreLoadMusicFilename[0] = 0;
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_PreLoadMusicStreamDone( void )
{
	if( musicPreloadActive )
	{
		if( audio_player.preFetchState )
		{
			return true;
		}
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_StartPreLoadedMusicStream( void )
{
	// Maybe we should check here to make sure the checksum of the music info filestream matches that
	// passed in when the music stream preload request came in.
	if( sPreLoadMusicFilename[0] > 0 )
	{
		// Turn off the preload flag, which will allow decoding to start.
		musicPreloadActive = false;

		// Call the update loop immediately.
		PCMAudio_DecodeLoop();
		return true;
	}
	return false;	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int PCMAudio_GetStreamStatus( int whichStream )
{
	if( whichStream >= NUM_STREAMS )
	{
		Dbg_MsgAssert( 0, ( "Checking stream status on stream %d, past valid range ( 0 to %d ).", whichStream, NUM_STREAMS - 1 ) );
		return false;
	}

	if( sPreLoadChecksum[whichStream] > 0 )
	{
		return PCM_STATUS_LOADING;
	}

	if( gPcmStatus & PCMSTATUS_LOAD_STREAM( whichStream ))
	{
		return PCM_STATUS_LOADING;
	}
	else if( gPcmStatus & PCMSTATUS_STREAM_PLAYING( whichStream ))
	{
		return PCM_STATUS_PLAYING;
	}
	else
	{
		return PCM_STATUS_FREE;
	}

	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PCMAudio_PauseStream( bool pause, int whichStream )
{
	// Not sure how to do this yet...
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool PCMAudio_LoadStreamHeader( const char *nameOfFile )
{
	if( gpStreamHed )
	{
		Mem::Free( gpStreamHed );
		gpStreamHed = NULL;
	}

	gpStreamHed = File::LoadHed( nameOfFile );

	if( !gpStreamHed )
	{
		Dbg_Message( "Couldn't find stream header %s\n", nameOfFile );
		return false;
	}
	
	return true;
}







} // namespace Pcm


================================================
FILE: Code/Gel/Music/ngc/p_music.h
================================================
// Audio streaming function prototypes:
// mjd jan 2001

#ifndef __P_MUSIC_H__
#define __P_MUSIC_H__

#include 
#include 
#include 

namespace Pcm
{

// allows one channel for music, another for audio:
enum{
	EXTRA_CHANNEL,
	MUSIC_CHANNEL,
};


// Onetime call once upon loading the game...
void PCMAudio_Init( void );

// Call every frame to make sure music and stream buffers are loaded and current status is checked each frame...
int PCMAudio_Update( void );

// Load a track and start it playing...
// You wanna loop a track?  Wait until this track is done, and PLAY IT AGAIN SAM!
bool PCMAudio_PlayMusicTrack( const char *filename, bool preload = false );
bool PCMAudio_PlayStream( uint32 checksum, int whichStream, float volumeL, float volumeR, float pitch, bool preload = false );

// keep song loaded, stop playing it (or continue playing paused song)
void PCMAudio_Pause( bool pause = true, int ch = MUSIC_CHANNEL );

void PCMAudio_PauseStream( bool pause, int whichStream );

void PCMAudio_StopMusic( bool waitPlease );
void PCMAudio_StopStream( int whichStream, bool waitPlease = true );
void PCMAudio_StopStreams( void );

// Preload streams. By preloading, you can guarantee they will start at a certain time.
bool PCMAudio_PreLoadStream( uint32 checksum, int whichStream );
bool PCMAudio_PreLoadStreamDone( int whichStream );
bool PCMAudio_StartPreLoadedStream( int whichStream, float volumeL, float volumeR, float pitch );

// Preload music streams. By preloading, you can guarantee they will start at a certain time.
bool PCMAudio_PreLoadMusicStream( uint32 checksum );
bool PCMAudio_PreLoadMusicStreamDone( void );
bool PCMAudio_StartPreLoadedMusicStream( void );

// set the music volume ( 0 to 100 )
int		PCMAudio_SetMusicVolume( float volume );
bool	PCMAudio_SetStreamVolume( float volumeL, float volumeR, int whichStream );
bool	PCMAudio_SetStreamPitch( float pitch, int whichStream );
//int PCMAudio_SetStreamVolumeAndPitch( float volumeL, float volumeR, float pitch, int whichStream );

void PCMAudio_SetStreamGlobalVolume( unsigned int volume );

enum{
	PCM_STATUS_FREE 			= ( 1 << 0 ),
	PCM_STATUS_PLAYING			= ( 1 << 1 ),
	PCM_STATUS_LOADING 			= ( 1 << 2 ),
};

// Return one of the PCM_STATUS values from above...
int PCMAudio_GetMusicStatus( );
int PCMAudio_GetStreamStatus( int whichStream );

// preload any CD location info (to avoid a hitch in framerate on PS2):
unsigned int PCMAudio_GetCDLocation( const char *pTrackName );

bool PCMAudio_TrackExists( const char *pTrackName, int ch );
bool PCMAudio_LoadMusicHeader( const char *nameOfFile );
bool PCMAudio_LoadStreamHeader( const char *nameOfFile );

uint32 PCMAudio_FindNameFromChecksum( uint32 checksum, int ch );

// borrow this memory for the movies and shit...
int PCMAudio_GetIopMemory( void );
		 
void setDolby( AXPB * p_pb, AXVPB * p_vpb, float volL, float volR, unsigned int pitch = 0, bool set_pitch = false, float volume_percent = 100.0f );

} // namespace PCM

#endif // __P_MUSIC_H__


================================================
FILE: Code/Gel/Music/ngc/pcm/pcm.h
================================================
#ifndef __MODULES_PCM_PCM_H
#define __MODULES_PCM_PCM_H

#define NUM_STREAMS 3

#define PCM_RPC_ARG_NUM_INTS 16

#define DEFAULT_PITCH	0x1000

// VAG header structure
typedef struct
{
	char ID[4];
	int version;
	char reserved1[4];
	int dataSize;
	int sampleFreq;
	char reserved2[12];
	char name[16];
} VagHeader;

#define SIZE_OF_VAGHEADER ( sizeof( VagHeader ) )

// sound stuff:
#define SB_BUF_SIZE ( 1024 * 32 )		// hex 2000
#define SB_BUF_HALF ( SB_BUF_SIZE / 2 )	// hex 1000
#define SB_BUF_SIZE_WITH_PADDING	( SB_BUF_SIZE + 0x40 )

// spu ram layout:
// first valid address for use in SPU RAM
#define SPU_RAM_SIZE					0x1fffff
#define SB_FIRST_USEABLE_ADDR			0x5010

//#define STRAY_VOICE_BLOCKER_SIZE		128
//#define STRAY_VOICE_BLOCKER_SPU_ADDR	SB_TOP
//#define STREAM_SPU_ADDR					( STRAY_VOICE_BLOCKER_SPU_ADDR + STRAY_VOICE_BLOCKER_SIZE )
#define STREAM_SPU_ADDR					SB_FIRST_USEABLE_ADDR

// slip in room for 3 streaming VAGS ( 2 for stereo music and one extra stream ):
//#define SINGLE_STREAM_BUFFER_SIZE 2048
#define TOTAL_SPU_REQUIRED_FOR_STREAMS		( SB_BUF_SIZE_WITH_PADDING * 4 )
#define MUSIC_L_SPU_BUF_LOC					( STREAM_SPU_ADDR + 0x40 )
#define MUSIC_R_SPU_BUF_LOC					( MUSIC_L_SPU_BUF_LOC + SB_BUF_SIZE_WITH_PADDING )
#define STREAM_SPU_BUF_LOC( i )				( MUSIC_R_SPU_BUF_LOC + ( ( 1 + ( i ) ) * SB_BUF_SIZE_WITH_PADDING ) )

#define END_OF_STREAMS						( STREAM_SPU_ADDR + TOTAL_SPU_REQUIRED_FOR_STREAMS + 0x40 )

//#define RAM_NEEDED_FOR_EFFECTS		0	// FUCK the effects...
#define	RAM_NEEDED_FOR_EFFECTS			0xade0 // enough for HALL reverb
//#define	RAM_NEEDED_FOR_EFFECTS		0xF6C0 // enough for space echo
 
#define REVERB_ONLY_ON_CORE_0			1

#if !REVERB_ONLY_ON_CORE_0

#define CORE_1_EFFECTS_START_ADDRESS	END_OF_STREAMS
#define CORE_1_EFFECTS_END_ADDRESS		0x02ffff // has to be on a 0xffff boundary!!
#if ( ( CORE_1_EFFECTS_END_ADDRESS - CORE_1_EFFECTS_START_ADDRESS ) < RAM_NEEDED_FOR_EFFECTS )
#error "not enough space for core 0 effects!"
#endif

#define BASE_WAVE_DATA_ADDR				CORE_0_EFFECTS_END_ADDRESS

#else
#define BASE_WAVE_DATA_ADDR				END_OF_STREAMS
#endif

#define CORE_0_EFFECTS_START_ADDRESS	( SPU_RAM_SIZE - RAM_NEEDED_FOR_EFFECTS )
#define CORE_0_EFFECTS_END_ADDRESS		( SPU_RAM_SIZE )
#define END_WAVE_DATA_ADDR				( CORE_0_EFFECTS_START_ADDRESS  - 0x40 )

// used by temporary sounds and permanently loaded sounds...
#define MAX_SPU_RAM_AVAILABLE			( END_WAVE_DATA_ADDR - BASE_WAVE_DATA_ADDR )

//#define MUSIC_R_SPU_BUF_LOC		SB_TOP
//#define MUSIC_L_SPU_BUF_LOC		( MUSIC_R_SPU_BUF_LOC + SB_BUF_SIZE )
//#define STREAM_SPU_BUF_LOC		( MUSIC_L_SPU_BUF_LOC + SB_BUF_SIZE )

// iop buffer taking streams off of the CD:
#define MUSIC_IOP_BUFFER_SIZE				( 1024 * 192 )  // must be a multiple of 4k!
#define MUSIC_HALF_IOP_BUFFER_SIZE			( MUSIC_IOP_BUFFER_SIZE / 2 )  // must be a multiple of 2k!
#define STREAM_IOP_BUFFER_SIZE				( 1024 * /*128*/64 )  // must be a multiple of 4k!
#define STREAM_HALF_IOP_BUFFER_SIZE			( STREAM_IOP_BUFFER_SIZE / 2 )  // must be a multiple of 2k!

// must match values in pcm_com.c!!!
#define TOTAL_IOP_BUFFER_SIZE_NEEDED		( ( MUSIC_IOP_BUFFER_SIZE * 2 ) + ( STREAM_IOP_BUFFER_SIZE * NUM_STREAMS ) )
#define ALLIGN_REQUIREMENT					64
#define SECTOR_SIZE							( 2048 )
#define NUM_SECTORS_PER_STREAM_BUFFER		( STREAM_IOP_BUFFER_SIZE / SECTOR_SIZE )
#define NUM_SECTORS_PER_STREAM_HALF_BUFFER	( NUM_SECTORS_PER_STREAM_BUFFER / 2 )
#define NUM_SECTORS_PER_MUSIC_BUFFER		( MUSIC_IOP_BUFFER_SIZE / SECTOR_SIZE )
#define NUM_SECTORS_PER_MUSIC_HALF_BUFFER	( NUM_SECTORS_PER_MUSIC_BUFFER / 2 )

#define MUSIC_L_IOP_OFFSET	0
#define MUSIC_R_IOP_OFFSET	( MUSIC_HALF_IOP_BUFFER_SIZE )  // interwoven with the L IOP buffer... and shit
#define STREAM_IOP_OFFSET( ch )				( ( 2 * MUSIC_IOP_BUFFER_SIZE ) + ( STREAM_IOP_BUFFER_SIZE * ( ch ) ) )

#define MUSIC_L_VOICE	22
#define MUSIC_R_VOICE	23
#define MUSIC_CORE		1

#define STREAM_VOICE( i ) ( 23 - ( i ) )
#define STREAM_CORE		0

// RPC command:
#define EzADPCM_COMMAND_MASK			0xfff0
#define EzADPCM_CH_MASK					0x000f
	
#define EzADPCM_INIT					0x0000
#define EzADPCM_QUIT					0x0010
#define EzADPCM_PLAYMUSIC				0x0020
#define EzADPCM_STOPMUSIC				0x0030
#define EzADPCM_PLAYSTREAM				0x0040
#define EzADPCM_STOPSTREAM				0x0070
#define EzADPCM_STOPSTREAMS				0x00a0
#define EzADPCM_SETMUSICVOL				0x00b0
#define EzADPCM_SETSTREAMVOL			0x00c0
#define EzADPCM_SETSTREAMVOLANDPITCH	0x00d0
#define EzADPCM_GETSTATUS				0x00e0
#define EzADPCM_GETMUSICSTATUS			0x00f0
#define EzADPCM_PAUSEMUSIC				0x0100
#define EzADPCM_PAUSESTREAM				0x0110
#define EzADPCM_PAUSESTREAMS			0x0120
#define EzADPCM_SETSTREAMGLOBVOL		0x0130
#define EzADPCM_SDINIT					0x7ff0

// for GETSTREAMSTATUS or GETMUSICSTATUS:
#define PCM_STATUS					0x0001f000
#define PCM_STATUS_IDLE		   		0x00000000
#define PCM_STATUS_PRELOAD			0x00001000
#define PCM_STATUS_READY_TO_STOP	0x00002000
#define PCM_STATUS_NEED_UPDATE		0x00003000
#define PCM_STATUS_RUNNING			0x00005000
#define PCM_STATUS_TERMINATE		0x00006000

// flags for GETSTATUS
#define PCMSTATUS_NEED_MUSIC_BUFFER_0	( 1 << 0 )
#define PCMSTATUS_NEED_MUSIC_BUFFER_1	( 1 << 1 )
#define PCMSTATUS_NEED_STREAM0_BUFFER_0	( 1 << 2 ) // don't change the order of these!!! IMPORTANT!
#define PCMSTATUS_NEED_STREAM1_BUFFER_0	( 1 << 3 )
#define PCMSTATUS_NEED_STREAM2_BUFFER_0	( 1 << 4 )
#define PCMSTATUS_NEED_STREAM0_BUFFER_1	( 1 << 5 )
#define PCMSTATUS_NEED_STREAM1_BUFFER_1	( 1 << 6 )
#define PCMSTATUS_NEED_STREAM2_BUFFER_1	( 1 << 7 )
#define PCMSTATUS_MUSIC_PLAYING			( 1 << 8 )
#define PCMSTATUS_STREAM0_PLAYING		( 1 << 9 )
#define PCMSTATUS_STREAM1_PLAYING		( 1 << 10 )
#define PCMSTATUS_STREAM2_PLAYING		( 1 << 11 )
#define PCMSTATUS_LOAD_MUSIC			( 1 << 12 )
#define PCMSTATUS_LOAD_STREAM0			( 1 << 13 )
#define PCMSTATUS_LOAD_STREAM1			( 1 << 14 )
#define PCMSTATUS_LOAD_STREAM2			( 1 << 15 )
#define PCMSTATUS_INITIALIZED			( 1 << 16 )

// macros:
#define PCMSTATUS_NEED_STREAM_BUFFER_0( xxx )	( PCMSTATUS_NEED_STREAM0_BUFFER_0 << ( xxx ) )
#define PCMSTATUS_NEED_STREAM_BUFFER_1( xxx )	( PCMSTATUS_NEED_STREAM0_BUFFER_1 << ( xxx ) )
#define PCMSTATUS_STREAM_PLAYING( xxx )			( PCMSTATUS_STREAM0_PLAYING << ( xxx ) )
#define PCMSTATUS_LOAD_STREAM( xxx )			( PCMSTATUS_LOAD_STREAM0 << ( xxx ) )
// Module ID number
#define EzADPCM_DEV 0x00012345

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
#endif // __MODULES_PCM_PCM_H
/* DON'T ADD STUFF AFTER THIS */


================================================
FILE: Code/Gel/Music/ngc/pcm/pcm_com.c
================================================
/* Vag streaming into SPU2 -- from sony sample -- matt may 2001 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "pcm.h"
#include "pcmiop.h"

// ================================================================
#define DATA_INTEGRITY_CHECK 1

#define SHOW_STATE	0
#define SHOW_SMALL_STATE 1
#define SHOW_ACTION 0
#define SHOW_SMALL_ACTION 1
#define DBUGPRINTF	0
#define SHOW_TIMING	1
#define SHOW_STOP	1
#define SHOW_SMALL_COMMANDS	1

#if SHOW_SMALL_COMMANDS
#define ShowSmallCommand printf
#else
#define ShowSmallCommand( x )
#endif

#if SHOW_STOP
#define ShowStop printf   //printf( " %d}{", GetTime( ) ), printf
#else
#define ShowStop( x )
#endif

#if SHOW_TIMING
#define ShowTime	printf
#else
#define ShowTime DoNothing
void DoNothing( char* text, ... ) { }
#endif

#if SHOW_ACTION
#define ShowAction printf( "Time %d", GetTime( ) ), printf
#else
#define ShowAction( x )
#endif

#if SHOW_SMALL_STATE
#define SmallShowState printf
#else
#define SmallShowState( x )
#endif

#if SHOW_SMALL_ACTION
#define SmallShowAction printf( " %d*", GetTime( ) ), printf
#else
#define SmallShowAction( x )
#endif

#if SHOW_STATE
#define ShowState printf( "Time %d                  ", GetTime( ) ), printf
#else
#define ShowState( x )
#endif

#if DBUGPRINTF
#define Dbug_Printf( x ) printf( x )
#else
#define Dbug_Printf( x )
#endif

//volatile int gTestPause = 0;

#define SHARE_DMA_CH	0

// the program running on the EE side will check to see if any of the buffers need to be filled:
volatile unsigned int gEECommand = 0;

#if SHARE_DMA_CH
#define TRANS_DMA_CH_COMMON		0
#define TRANS_DMA_CH_MUSIC		TRANS_DMA_CH_COMMON
#define TRANS_DMA_CH_STREAM		TRANS_DMA_CH_COMMON

enum{
	LOAD_STATUS_IDLE = 				0,
	LOAD_STATUS_LOADING_MUSIC =		( 1 << 0 ),
	LOAD_STATUS_LOADING_STREAM = 	( 1 << 1 ),
	LOAD_STATUS_NEED_MUSIC =		( 1 << 2 ),
	LOAD_STATUS_NEED_STREAM =		( 1 << 3 ),
};

volatile unsigned int gLoadStatus = LOAD_STATUS_IDLE;

#else
#define TRANS_DMA_CH_MUSIC		1
#define TRANS_DMA_CH_STREAM		0
#endif


#define _1st 0
#define _2nd 1

#define DEFAULT_PITCH	0x1000

unsigned int gThid = 0;
unsigned int gSem = 0;
unsigned int gpIopBuf;		// IOP SMEM
unsigned int gMusicIopOffset;
unsigned int gStreamIopOffset;

enum{
	MUSIC_LOAD_STATE_IDLE,
	MUSIC_LOAD_STATE_PRELOADING_L,
	MUSIC_LOAD_STATE_PRELOADING_R,
	MUSIC_LOAD_STATE_WAITING_FOR_IRQ,
	MUSIC_LOAD_STATE_LOADING_L,
	MUSIC_LOAD_STATE_LOADING_R,
};

volatile unsigned int gMusicPaused = 0;
volatile unsigned int gStreamPaused = 0;
volatile unsigned int gStreamTimeOffset = 0;  // when paused, keep track of time passed since last IRQ
volatile unsigned int gMusicTimeOffset = 0;   // when paused, keep track of time passed since last IRQ

#define RF_START_MUSIC			( 1 << 0 )
#define RF_START_STREAM			( 1 << 1 )
#define RF_UNPAUSE_MUSIC		( 1 << 2 )
#define RF_UNPAUSE_STREAM		( 1 << 3 )

volatile unsigned int gRequestFlags = 0;

volatile unsigned int gTimeOfLastMusicUpdate = 0;
volatile unsigned int gTimeOfLastStreamUpdate = 0;

volatile unsigned int gMusicBufSide = _1st;
volatile unsigned int gMusicTransSize = 0;
volatile unsigned int gMusicLoadState = MUSIC_LOAD_STATE_IDLE;
volatile unsigned int gMusicVolume;
volatile unsigned int gMusicVolumeSet = 0;
volatile unsigned int gMusicStop = 0;
volatile unsigned int gMusicStatus = EzADPCM_STATUS_IDLE;
volatile unsigned int gUpdateMusic = 0;
volatile unsigned int gMusicSize;
volatile unsigned int gMusicRemaining;

enum{
	STREAM_LOAD_STATE_IDLE,
	STREAM_LOAD_STATE_PRELOADING,
	STREAM_LOAD_STATE_WAITING_FOR_IRQ,
	STREAM_LOAD_STATE_LOADING,
};


volatile unsigned int gStreamBufSide = _1st;
volatile unsigned int gStreamTransSize = 0;
volatile unsigned int gStreamLoadState = STREAM_LOAD_STATE_IDLE;
volatile unsigned int gStreamVolume;
volatile unsigned int gStreamVolumeSet = 0;
volatile unsigned int gStreamStop = 0;
volatile unsigned int gStreamStatus = EzADPCM_STATUS_IDLE;
volatile unsigned int gUpdateStream = 0;
volatile unsigned int gStreamSize;
volatile unsigned int gStreamRemaining;

int _AdpcmPlay( int status );

#if SHARE_DMA_CH
static int _AdpcmDmaIntCommon( int, void* );
#else
static int _AdpcmDmaIntMusic( int, void* );
static int _AdpcmDmaIntStream( int, void* );
#endif
static int _AdpcmSpu2Int( int, void * );

#define _L 0
#define _R 1
#define _Lch(x) ((x >> 16) & 0xffff)
#define _Rch(x) (x & 0xffff)

unsigned int GetTime( void )
{
	unsigned int sex, usex, msex;
	struct SysClock sysClock;
	GetSystemTime( &sysClock );
	SysClock2USec( &sysClock, &sex, &usex );
	msex = ( sex * 1000 ) + ( usex / 1000 );
	return ( msex );
}

// EzADPCM_SDINIT
void AdpcmSdInit( void )
{
    sceSdInit (0);

    //	Disk media: DVD
    //	Output format: PCM
    //	Copy guard: normal (one generation recordable / default)
    sceSdSetCoreAttr (SD_C_SPDIF_MODE, (SD_SPDIF_MEDIA_DVD |
					SD_SPDIF_OUT_PCM   |
					SD_SPDIF_COPY_NORMAL));
    return;
}

void AdpcmSetUpVoice( int core, int vnum, int pSpuBuf )
{
    sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_VOLL, 0 );
    sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_VOLR, 0 );
    sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_PITCH, DEFAULT_PITCH );
    sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_ADSR1, 0x000f );
    sceSdSetParam ( core | ( vnum << 1 ) | SD_VP_ADSR2, 0x1fc0 );
    sceSdSetAddr  ( core | ( vnum << 1 ) | SD_VA_SSA,  pSpuBuf );
    return;
}

/*const char StrayVoiceData[ STRAY_VOICE_BLOCKER_SIZE ] =
{
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x0C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x0C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x0C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x0C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x0C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x0C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};*/

// EzADPCM_INIT
int AdpcmInit( int pIopBuf )
{
	if ( gEECommand & PCMSTATUS_INITIALIZED )
	{
		return ( 0 );
	}

    if ( gSem == 0 )
	{
		struct SemaParam sem;
		sem.initCount = 0;
		sem.maxCount = 1;
		sem.attr = AT_THFIFO;
		gSem = CreateSema (&sem);
    }
    if (gThid == 0)
	{
		struct ThreadParam param;
		param.attr         = TH_C;
		param.entry        = _AdpcmPlay;
		param.initPriority = 5;// BASE_priority-3;
		param.stackSize    = 0x800; // was 800
		param.option = 0;
		gThid = CreateThread (¶m);
		printf( "EzADPCM: create thread ID= %d\n", gThid );
		StartThread (gThid, (u_long) NULL);
    }
	
/*
	// This data represents an empty looping sound...
	// The voices wandering across SPU ram will get
	// stuck here, so as not to trigger the interrupts
	// reserved for the stream and music voices and shit.
	strcpy( ( char * )pIopBuf, StrayVoiceData );
	// send that to the SPU ram:
	sceSdVoiceTrans( 0, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
		(unsigned char *) pIopBuf, STRAY_VOICE_BLOCKER_SPU_ADDR, STRAY_VOICE_BLOCKER_SIZE );
	sceSdVoiceTransStatus( 0, SD_TRANS_STATUS_WAIT );*/
		
#if SHARE_DMA_CH
	sceSdSetTransIntrHandler( TRANS_DMA_CH_COMMON, ( sceSdTransIntrHandler ) _AdpcmDmaIntCommon, ( void * ) &gSem );
#else
	sceSdSetTransIntrHandler( TRANS_DMA_CH_MUSIC, ( sceSdTransIntrHandler ) _AdpcmDmaIntMusic, ( void * ) &gSem );
    sceSdSetTransIntrHandler( TRANS_DMA_CH_STREAM, ( sceSdTransIntrHandler ) _AdpcmDmaIntStream, ( void * ) &gSem );
#endif
    sceSdSetSpu2IntrHandler( ( sceSdSpu2IntrHandler ) _AdpcmSpu2Int, ( void * ) &gSem );

	AdpcmSetUpVoice( MUSIC_CORE, MUSIC_L_VOICE, MUSIC_L_SPU_BUF_LOC );
	AdpcmSetUpVoice( MUSIC_CORE, MUSIC_R_VOICE, MUSIC_R_SPU_BUF_LOC );
	AdpcmSetUpVoice( STREAM_CORE, STREAM_VOICE, STREAM_SPU_BUF_LOC );

	gpIopBuf = pIopBuf;
	gEECommand |= PCMSTATUS_INITIALIZED;
	//printf( "PCM Irx iop buf %d\n", gpIopBuf );
	//Dbug_Printf( "PCM irx is initialized\n" );
    return gThid;
}

// EzADPCM_QUIT
void AdpcmQuit( void )
{
    DeleteThread (gThid);
    gThid = 0;
#if 0
    DeleteSema (gSem);
    gSem = 0;
#endif
    return;
}

// EzADPCM_PLAYMUSIC
int AdpcmPlayMusic( int size )
{
    extern void AdpcmSetMusicVolumeDirect( unsigned int );

    if ( gMusicStatus != EzADPCM_STATUS_IDLE )
	{
		ShowAction( "NOTE NOTE NOTE NOTE Can't play music -- music isn't in Idle state.\n" );
		return -1;
    }

    AdpcmSetMusicVolumeDirect( gMusicVolume );
				
	gMusicStatus = EzADPCM_STATUS_PRELOAD;
	gMusicLoadState = MUSIC_LOAD_STATE_IDLE;
	gMusicSize = size;
	gMusicTimeOffset = 0;
	if ( gMusicSize < 64 )
	{
		gMusicSize = 64;
	}
	gEECommand &= ~( PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 );
	gEECommand |= PCMSTATUS_MUSIC_PLAYING;

	// stagger the two streams so there are no IRQ or DMA interrupt conflicts:
	gRequestFlags |= RF_START_MUSIC;
    return 0;
}

// EzADPCM_PLAYSTREAM
int AdpcmPlayStream( int size )
{
    extern void AdpcmSetStreamVolumeDirect( unsigned int );

    if ( gStreamStatus != EzADPCM_STATUS_IDLE )
	{
		printf( "trying to play stream when stream already playing!\n" );
		return -1;
    }

    AdpcmSetStreamVolumeDirect( gStreamVolume );

	gStreamStatus = EzADPCM_STATUS_PRELOAD;
	gStreamSize = size;
	if ( gStreamSize < 64 )
	{
		gStreamSize = 64;
	}
	gStreamLoadState = STREAM_LOAD_STATE_IDLE;
	gEECommand &= ~( PCMSTATUS_NEED_STREAM_BUFFER_0 | PCMSTATUS_NEED_STREAM_BUFFER_1 );
	gEECommand |= PCMSTATUS_STREAM_PLAYING;
	gStreamTimeOffset = 0;
	gRequestFlags = RF_START_STREAM;
	ShowAction( "Requesting stream start\n" );
    SmallShowAction( "-rsb" );
	return 0;
}

/* internal */
void _AdpcmSetMusicVoiceMute( void )
{
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1) | SD_VP_VOLL, 0);
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1) | SD_VP_VOLR, 0);
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1) | SD_VP_VOLL, 0);
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1) | SD_VP_VOLR, 0);
    return;
}

/* internal */
void _AdpcmSetStreamVoiceMute( void )
{
    sceSdSetParam( STREAM_CORE | ( STREAM_VOICE << 1) | SD_VP_VOLL, 0);
    sceSdSetParam( STREAM_CORE | ( STREAM_VOICE << 1) | SD_VP_VOLR, 0);
    return;
}

void StopMusic( void )
{
	sceSdSetCoreAttr( MUSIC_CORE | SD_C_IRQ_ENABLE, 0 );
	gMusicStatus = EzADPCM_STATUS_IDLE;
	gEECommand &= ~( PCMSTATUS_MUSIC_PLAYING | PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 );
	_AdpcmSetMusicVoiceMute( );
	sceSdSetSwitch( MUSIC_CORE | SD_S_KOFF, ( 1 << MUSIC_L_VOICE ) );
	sceSdSetSwitch( MUSIC_CORE | SD_S_KOFF, ( 1 << MUSIC_R_VOICE ) );
	ShowAction( "Stopping music\n" );
    SmallShowAction( "-!sm!" );
}

void StopStream( void )
{
	sceSdSetCoreAttr( STREAM_CORE | SD_C_IRQ_ENABLE, 0 );
	gStreamStatus = EzADPCM_STATUS_IDLE;
	gEECommand &= ~( PCMSTATUS_STREAM_PLAYING | PCMSTATUS_NEED_STREAM_BUFFER_0 | PCMSTATUS_NEED_STREAM_BUFFER_1 );
	_AdpcmSetStreamVoiceMute( );
	sceSdSetSwitch( STREAM_CORE | SD_S_KOFF, ( 1 << STREAM_VOICE ) );
	ShowAction( "Stopping stream\n" );
    SmallShowAction( "-!ss!" );
}

#define NUM_WAIT_CLICKS 25000

// EzADPCM_STOPMUSIC
int AdpcmStopMusic( void )
{
	int i;
	int j = 0;

	if ( gMusicStatus != EzADPCM_STATUS_IDLE )
	{
		if ( gRequestFlags & RF_START_MUSIC )
		{
			gRequestFlags &= ~RF_START_MUSIC;
			gMusicStatus = EzADPCM_STATUS_IDLE;
			ShowAction( "Supressing music request\n" );
			SmallShowAction( "-smr" );
		}
		else if ( gMusicPaused )
		{
			// if loading is happening, wait then just stop:
			ShowStop( "stop music %d", GetTime( ) );
			while ( gMusicLoadState != MUSIC_LOAD_STATE_WAITING_FOR_IRQ )
			{
				for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
					;
				j++;
				if ( j > NUM_WAIT_CLICKS )
				{
					j = 0;
					ShowStop( "." );
				}
			}
//			ShowStop( "\n" );
			ShowAction( "Stopped paused music\n" );
			SmallShowAction( "-spm" );
			StopMusic( );
		}
		else
		{
			ShowStop( " %d-stopmusic", GetTime( ) );
			gMusicStop++;
			while ( gMusicStatus != EzADPCM_STATUS_IDLE )
			{
				for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
					;
				if ( j++ > NUM_WAIT_CLICKS )
				{
					j = 0;
					ShowStop( "." );
					//printf( " ms %d mls %d rf %d eecom %d\n", gMusicStatus, gMusicLoadState, gRequestFlags, gEECommand );
				}
			}
			//ShowStop( "\n" );
		}
		gEECommand &= ~ ( PCMSTATUS_MUSIC_PLAYING | PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 );
		return ( 1 );
    }
	return ( 0 );
}

// EzADPCM_STOPSTREAM
int AdpcmStopStream( void )
{
	int i;
	int j = 0;

	if ( gStreamStatus != EzADPCM_STATUS_IDLE )
	{
		if ( gRequestFlags & RF_START_STREAM )
		{
			gRequestFlags &= ~RF_START_STREAM;
			gStreamStatus = EzADPCM_STATUS_IDLE;
			ShowAction( "Supressing stream request\n" );
			SmallShowAction( "-ssr" );
		}
		else if ( gStreamPaused )
		{
			// if loading is happening, wait then just stop:
			ShowStop( " %d-Stop p stream", GetTime( ) );
			while ( gStreamLoadState != STREAM_LOAD_STATE_WAITING_FOR_IRQ )
			{
				for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
					;
				ShowStop( "." );
			}
			//ShowStop( "\n" );
			StopStream( );
		}
		else
		{
			ShowStop( " %d-Stop stream", GetTime( ) );
			gStreamStop++;
			while ( gStreamStatus != EzADPCM_STATUS_IDLE )
			{
				for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
					;
				if ( j++ > NUM_WAIT_CLICKS )
				{
					ShowStop( "." );
//					printf( " ss %x sls %d rf %d eecom %d\n", gStreamStatus, gStreamLoadState, gRequestFlags, gEECommand );
					j = 0;
				}
			}
			//ShowStop( "\n" );
			SmallShowAction( "-ss" );
			ShowAction( "Stopped stream\n" );
		}
		gEECommand &= ~ ( PCMSTATUS_STREAM_PLAYING | PCMSTATUS_NEED_STREAM_BUFFER_0 | PCMSTATUS_NEED_STREAM_BUFFER_1 );
		return ( 1 );
    }
	return ( 0 );
}

// EzADPCM_SETMUSICVOL
void AdpcmSetMusicVolume( unsigned int vol )
{
    gMusicVolumeSet = 1;
    gMusicVolume = vol;
    return;
}

// EzADPCM_SETMUSICVOLDIRECT
void AdpcmSetMusicVolumeDirect( unsigned int vol )
{
    gMusicVolume = vol;
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_VOLL, vol );
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_VOLR, 0 );
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_VOLL, 0 );
    sceSdSetParam ( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_VOLR, vol );
    return;
}

// EzADPCM_SETSTREAMVOL
void AdpcmSetStreamVolume( unsigned int vol )
{
    gStreamVolumeSet = 1;
    gStreamVolume = vol;
    return;
}

// EzADPCM_SETSTREAMVOLDIRECT
void AdpcmSetStreamVolumeDirect( unsigned int vol )
{
    gStreamVolume = vol;
    sceSdSetParam ( STREAM_CORE | ( STREAM_VOICE << 1 ) | SD_VP_VOLL, _Lch( vol ) );
    sceSdSetParam ( STREAM_CORE | ( STREAM_VOICE << 1 ) | SD_VP_VOLR, _Rch( vol ) );
    return;
}

// Shouldn't need these unless debugging or something --
// Instead just get gEECommand each frame and act accordingly.

// EzADPCM_GETMUSICSTATUS
unsigned int AdpcmGetMusicStatus( void )
{
	return gMusicStatus;
}

// EzADPCM_GETSTREAMSTATUS
unsigned int AdpcmGetStreamStatus( void )
{
    return gStreamStatus;
}

// actual time 300 ms across one buffer (600ms across double buffer)
#define SINGLE_BUFFER_MS	300
#define MIN_INTERRUPT_SEPARATION_MS		90
#define MAX_MS_BETWEEN_STREAMS			( SINGLE_BUFFER_MS - MIN_INTERRUPT_SEPARATION_MS )
#define MIN_MS_BETWEEN_STREAMS			( MIN_INTERRUPT_SEPARATION_MS )

int SafeForStreamToGo( unsigned int time )
{
	if ( gMusicStatus == EzADPCM_STATUS_IDLE )
	{
		SmallShowState( "[ss]" );
		return ( 1 );
	}
	else
	{
		time -= gStreamTimeOffset;
		if ( gMusicPaused )
		{
			if ( time > gMusicPaused + MIN_MS_BETWEEN_STREAMS )
			{
				SmallShowState( "[ss0]" );
				return ( 1 );
			}
		}
		else
		{
			int musicMod;
			int streamMod;
			musicMod = gTimeOfLastMusicUpdate % SINGLE_BUFFER_MS;
			streamMod = time % SINGLE_BUFFER_MS;
			if ( musicMod > streamMod )
			{
				if ( ( musicMod - streamMod > MIN_MS_BETWEEN_STREAMS ) &&
					( ( ( streamMod + SINGLE_BUFFER_MS ) - musicMod ) > MIN_MS_BETWEEN_STREAMS ) )
				{
					SmallShowState( "[ss1]" );
					return ( 1 );
				}
			}
			else if ( ( streamMod - musicMod > MIN_MS_BETWEEN_STREAMS ) &&
					( ( ( musicMod + SINGLE_BUFFER_MS ) - streamMod ) > MIN_MS_BETWEEN_STREAMS ) )
			{
				SmallShowState( "[ss2]" );
				return ( 1 );
			}
		}
/*		
		else if ( ( time > gTimeOfLastMusicUpdate + MIN_MS_BETWEEN_STREAMS ) &&
			 ( time < gTimeOfLastMusicUpdate + MAX_MS_BETWEEN_STREAMS ) )
		{
			SmallShowState( "[ss1]" );
			return ( 1 );
		}
		else
		{
			unsigned int predictedTimeOfInterrupt;
			predictedTimeOfInterrupt = ( time + SINGLE_BUFFER_MS );
			if ( ( predictedTimeOfInterrupt > ( gTimeOfLastMusicUpdate + MIN_MS_BETWEEN_STREAMS ) )
				&& ( predictedTimeOfInterrupt < ( gTimeOfLastMusicUpdate + MAX_MS_BETWEEN_STREAMS ) ) )
			{
				SmallShowState( "[ss2]" );
				return ( 1 );
			}
			else
			{
				//printf( " <>gsto %d<>", gStreamTimeOffset );
				SmallShowState( "[nss]" ); //fe ss %x sls %d eecom %d\n", gStreamStatus, gStreamLoadState, gEECommand );
			}
		}*/
	}
	SmallShowState( "[nss]" ); //fe ss %x sls %d eecom %d\n", gStreamStatus, gStreamLoadState, gEECommand );
	return ( 0 );
}

int SafeForMusicToGo( unsigned int time )
{
	if ( gStreamStatus == EzADPCM_STATUS_IDLE )
	{
		SmallShowState( "[ms]" ); //fe ss %x sls %d eecom %d\n", gStreamStatus, gStreamLoadState, gEECommand );
		return ( 1 );
	}
	else
	{
		time -= gMusicTimeOffset;
		if ( gStreamPaused )
		{
			if ( time > gStreamPaused + MIN_MS_BETWEEN_STREAMS )
			{
				SmallShowState( "[ms0]" ); //fe ss %x sls %d eecom %d\n", gStreamStatus, gStreamLoadState, gEECommand );
				return ( 1 );
			}
		}
		else
		{
			int streamMod;
			int musicMod;
			streamMod = gTimeOfLastStreamUpdate % SINGLE_BUFFER_MS;
			musicMod = time % SINGLE_BUFFER_MS;
			if ( musicMod > streamMod )
			{
				if ( ( musicMod - streamMod > MIN_MS_BETWEEN_STREAMS ) &&
					( ( ( streamMod + SINGLE_BUFFER_MS ) - musicMod ) > MIN_MS_BETWEEN_STREAMS ) )
				{
					SmallShowState( "[ms1]" );
					return ( 1 );
				}
			}
			else if ( ( streamMod - musicMod > MIN_MS_BETWEEN_STREAMS ) &&
					( ( ( musicMod + SINGLE_BUFFER_MS ) - streamMod ) > MIN_MS_BETWEEN_STREAMS ) )
			{
				SmallShowState( "[ms2]" );
				return ( 1 );
			}
		}
/*		
        else if ( ( time > gTimeOfLastStreamUpdate + MIN_MS_BETWEEN_STREAMS ) &&
			 ( time < gTimeOfLastStreamUpdate + MAX_MS_BETWEEN_STREAMS ) )
		{
			SmallShowState( "[ms1]" ); //fe ss %x sls %d eecom %d\n", gStreamStatus, gStreamLoadState, gEECommand );
			return ( 1 );
		}
		else
		{
			unsigned int predictedTimeOfInterrupt;
			predictedTimeOfInterrupt = ( time + SINGLE_BUFFER_MS );
			if ( ( predictedTimeOfInterrupt > ( gTimeOfLastStreamUpdate + MIN_MS_BETWEEN_STREAMS ) )
				&& ( predictedTimeOfInterrupt < ( gTimeOfLastStreamUpdate + MAX_MS_BETWEEN_STREAMS ) ) )
			{
				SmallShowState( "[ms2]" ); //fe ss %x sls %d eecom %d\n", gStreamStatus, gStreamLoadState, gEECommand );
				return ( 1 );
			}
			else
			{
				SmallShowState( "[nsm]" ); //fe ss %x sls %d eecom %d\n", gStreamStatus, gStreamLoadState, gEECommand );
			}
		}*/
		
	}
	SmallShowState( "[nsm]" ); //fe ss %x sls %d eecom %d\n", gStreamStatus, gStreamLoadState, gEECommand );
	return ( 0 );
}

// EzADPCM_GETSTATUS
unsigned int AdpcmGetStatus( void )
{
	unsigned int temp;
	unsigned int time;
	
	if ( gRequestFlags & ( RF_START_MUSIC | RF_START_STREAM | RF_UNPAUSE_MUSIC | RF_UNPAUSE_STREAM ) )
	{
		time = GetTime( );
		if ( ( gRequestFlags & RF_START_STREAM ) && ( SafeForStreamToGo( time ) ) )
		{
			gUpdateStream = 1;
			gTimeOfLastStreamUpdate = time;
			ShowAction( "Starting stream\n" );
			SmallShowAction( "-sb" );
			SignalSema( gSem );
			gRequestFlags &= ~RF_START_STREAM;
		}
		else if ( ( gRequestFlags & RF_START_MUSIC ) && ( SafeForMusicToGo( time ) ) )
		{
			gUpdateMusic = 1;
			gTimeOfLastStreamUpdate = time;
			ShowAction( "Starting music\n" );
			SmallShowAction( "-mb" );
			SignalSema( gSem );
			gRequestFlags &= ~RF_START_MUSIC;
		}
		else  // unpause one or both of these motherfuckers:
		{
			if ( ( gRequestFlags & RF_UNPAUSE_STREAM ) && ( SafeForStreamToGo( time ) )	)
			{
				sceSdSetParam( STREAM_CORE | ( STREAM_VOICE << 1 ) | SD_VP_PITCH, DEFAULT_PITCH );
				gStreamPaused = 0;
				gTimeOfLastStreamUpdate = time - gStreamTimeOffset;
				gStreamTimeOffset = 0;
				gRequestFlags &= ~RF_UNPAUSE_STREAM;
				ShowAction( "Unpausing stream\n" );
				SmallShowAction( "-ups" );
			}
			if ( ( gRequestFlags & RF_UNPAUSE_MUSIC ) && ( SafeForMusicToGo( time ) ) )
			{
				sceSdSetParam( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_PITCH, DEFAULT_PITCH );
				sceSdSetParam( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_PITCH, DEFAULT_PITCH );
				gMusicPaused = 0;
				gTimeOfLastMusicUpdate = time - gMusicTimeOffset;
				gMusicTimeOffset = 0;
				gRequestFlags &= ~RF_UNPAUSE_MUSIC;
				ShowAction( "Unpausing music\n" );
				SmallShowAction( "-upm" );
			}
		}
	}
	
	temp = gEECommand;
	gEECommand &= ~( PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 | 
					PCMSTATUS_NEED_STREAM_BUFFER_0 | PCMSTATUS_NEED_STREAM_BUFFER_1 );
#if SHOW_SMALL_COMMANDS
	if ( temp & ( PCMSTATUS_NEED_MUSIC_BUFFER_0 | PCMSTATUS_NEED_MUSIC_BUFFER_1 | 
					PCMSTATUS_NEED_STREAM_BUFFER_0 | PCMSTATUS_NEED_STREAM_BUFFER_1 ) )
	{
		ShowSmallCommand( " <_L_>" );
	}
#endif
	return temp;
}

// ================================================================

#define _ADPCM_MARK_START 0x04
#define _ADPCM_MARK_LOOP  0x02
#define _ADPCM_MARK_END   0x01

#define _AdpcmSetMarkSTART(a,s) { \
  *((unsigned char *)((a)+1)) = \
	(_ADPCM_MARK_LOOP | _ADPCM_MARK_START); \
  *((unsigned char *)((a)+0x10+1)) = \
	_ADPCM_MARK_LOOP; \
  *((unsigned char *)((a)+(s)-0x0f)) = \
	_ADPCM_MARK_LOOP; \
	FlushDcache (); }
#define _AdpcmSetMarkEND(a,s) { \
  *((unsigned char *)((a)+1)) = \
	 _ADPCM_MARK_LOOP; \
  *((unsigned char *)((a)+0x10+1)) = \
	_ADPCM_MARK_LOOP; \
  *((unsigned char *)((a)+(s)-0x0f)) = \
	(_ADPCM_MARK_LOOP | _ADPCM_MARK_END); \
	FlushDcache (); }

#define _AdpcmSetMarkFINAL(a,s) { \
  *((unsigned char *)((a)+(s)-0x0f)) = \
	(_ADPCM_MARK_LOOP | _ADPCM_MARK_START | _ADPCM_MARK_END); \
	FlushDcache (); }

#define _AdpcmSetMarkSTARTpre(a,s) { \
  *((unsigned char *)((a)+1)) = \
	(_ADPCM_MARK_LOOP | _ADPCM_MARK_START); \
  *((unsigned char *)((a)+0x10+1)) = \
	_ADPCM_MARK_LOOP; \
	FlushDcache (); }
#define _AdpcmSetMarkENDpre(a,s) { \
  *((unsigned char *)((a)+(s)-0x0f)) = \
	(_ADPCM_MARK_LOOP | _ADPCM_MARK_END); \
	FlushDcache (); }

#if SHARE_DMA_CH

/* internal */
static int _AdpcmDmaIntCommon(int ch, void *common) // DMA Interrupt -- when transfering data to SPU2 from IOP
{
	if ( gLoadStatus & LOAD_STATUS_LOADING_MUSIC )
	{
		if ( gMusicStatus != EzADPCM_STATUS_IDLE )
		{
			gUpdateMusic++;
			iSignalSema (* (int *) common);
			return 1;
		}
	}
	else if ( gLoadStatus & LOAD_STATUS_LOADING_STREAM )
	{
		if ( gStreamStatus != EzADPCM_STATUS_IDLE )
		{
			gUpdateStream++;
			iSignalSema (* (int *) common);
			return 1;
		}
	}
	return 0;
}

#else

/* internal */
static int _AdpcmDmaIntMusic(int ch, void *common)	// DMA Interrupt -- when transfering data to SPU2 from IOP
{
	if ( gMusicStatus != EzADPCM_STATUS_IDLE )
	{
		gUpdateMusic++;
		iSignalSema (* (int *) common);
		return 1;
	}
	return 0;
}

/* internal */
static int _AdpcmDmaIntStream(int ch, void *common)	// DMA Interrupt -- when transfering data to SPU2 from IOP
{
	if ( gStreamStatus != EzADPCM_STATUS_IDLE )
	{
		gUpdateStream++;
		iSignalSema (* (int *) common);
		return 1;
	}
	return 0;
}

#endif

/* internal */
static int _AdpcmSpu2Int( int core, void *common ) // SPU2 Voice Interrupt
{
	if ( core == ( 1 << MUSIC_CORE ) )
	{
		if ( gMusicLoadState != MUSIC_LOAD_STATE_WAITING_FOR_IRQ )
		{
			int shit;
			for ( shit = 0; shit < 666; shit++ )
			{
				printf( "shit\n" );
				return 0;
			}
		}
		gUpdateMusic++;
	}
	else
	{
		if ( gStreamLoadState != STREAM_LOAD_STATE_WAITING_FOR_IRQ )
		{
			int shit;
			for ( shit = 0; shit < 666; shit++ )
			{
				printf( "shit\n" );
				return 0;
			}
		}
		gUpdateStream++;
	}
	iSignalSema (* (int *) common);
    return 1;
}

#define LAST_VAG_POS 15

//prototype:
void UpdateStream( void );

void UpdateMusic( void )
{
	switch ( gMusicStatus )
	{
		case EzADPCM_STATUS_IDLE:
			break;

		case EzADPCM_STATUS_PRELOAD:
			switch ( gMusicLoadState )
			{
				case ( MUSIC_LOAD_STATE_IDLE ):
				{
#if SHARE_DMA_CH					
					if ( gLoadStatus & LOAD_STATUS_LOADING_STREAM )
					{
						gLoadStatus |= LOAD_STATUS_NEED_MUSIC;
						return;
					}
					gLoadStatus |= LOAD_STATUS_LOADING_MUSIC;
#endif
					ShowState( "music preload l\n" );
					SmallShowState( "-MPL" );
					gMusicBufSide = _1st;
					gMusicIopOffset = 0;
					gMusicRemaining = gMusicSize;
					gMusicRemaining -= SB_BUF_SIZE;
					gMusicTransSize = SB_BUF_SIZE;
					if ( gMusicRemaining < 0 )
					{
						gMusicTransSize += gMusicRemaining;
					}

#if DATA_INTEGRITY_CHECK
					{
						int gronk;
						for ( gronk = 0; gronk < gMusicTransSize >> 4; gronk++ )
						{
							if ( *( ( char * )( 1 + gpIopBuf + MUSIC_L_IOP_OFFSET + ( gronk * 16 ) ) ) != 0 )
							{
								printf( "Fucked up data!!!!" );
							}
						}
					}
#endif
					
					// preload left:
					_AdpcmSetMarkSTARTpre( gpIopBuf + MUSIC_L_IOP_OFFSET, gMusicTransSize );
					if ( gMusicRemaining > 0 )
					{
						_AdpcmSetMarkENDpre( gpIopBuf + MUSIC_L_IOP_OFFSET, gMusicTransSize );
					}
					else
					{
						_AdpcmSetMarkFINAL( gpIopBuf + MUSIC_L_IOP_OFFSET, gMusicTransSize );
					}
					sceSdVoiceTrans( TRANS_DMA_CH_MUSIC, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
						(unsigned char *) ( gpIopBuf + MUSIC_L_IOP_OFFSET ),
						MUSIC_L_SPU_BUF_LOC, gMusicTransSize );
					gMusicLoadState++;
					break;
				}
				case ( MUSIC_LOAD_STATE_PRELOADING_L ):
				{
					ShowState( "music preload r\n" );
					SmallShowState( "-MPR" );
					// preload right:
#if DATA_INTEGRITY_CHECK
					{
						int gronk;
						for ( gronk = 0; gronk < gMusicTransSize >> 4; gronk++ )
						{
							if ( *( ( char * )( 1 + gpIopBuf + MUSIC_R_IOP_OFFSET + ( gronk * 16 ) ) ) != 0 )
							{
								printf( "Fucked up data!!!!" );
							}
						}
					}
#endif
					_AdpcmSetMarkSTARTpre( gpIopBuf + MUSIC_R_IOP_OFFSET, gMusicTransSize );
					if ( gMusicRemaining > 0 )
					{
						_AdpcmSetMarkENDpre( gpIopBuf + MUSIC_R_IOP_OFFSET, gMusicTransSize );
					}
					else
					{
						_AdpcmSetMarkFINAL( gpIopBuf + MUSIC_R_IOP_OFFSET, gMusicTransSize );
					}
					sceSdVoiceTrans( TRANS_DMA_CH_MUSIC, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
						(unsigned char *) ( gpIopBuf + MUSIC_R_IOP_OFFSET ),
						MUSIC_R_SPU_BUF_LOC, gMusicTransSize );
					gMusicLoadState++;
					break;
				}	
				case ( MUSIC_LOAD_STATE_PRELOADING_R ):
				{
					ShowState( "Music starting -- waiting for IRQ\n" );
					SmallShowState( " %d-MSIRQ", GetTime( ) );
					gMusicIopOffset = SB_BUF_SIZE;
					if ( gMusicRemaining <= 0 )
					{
						sceSdSetAddr( MUSIC_CORE | SD_A_IRQA, MUSIC_L_SPU_BUF_LOC + gMusicTransSize - LAST_VAG_POS );
						gMusicStatus = EzADPCM_STATUS_TERMINATE;
					}
					else
					{
						sceSdSetAddr( MUSIC_CORE | SD_A_IRQA, MUSIC_L_SPU_BUF_LOC + SB_BUF_HALF );
						gMusicStatus = EzADPCM_STATUS_RUNNING;
					}
					AdpcmSetMusicVolumeDirect( gMusicVolume );
					sceSdSetCoreAttr( MUSIC_CORE | SD_C_IRQ_ENABLE, 1 );
					sceSdSetSwitch( MUSIC_CORE | SD_S_KON, ( ( 1 << MUSIC_R_VOICE ) | ( 1 << MUSIC_L_VOICE ) ) );
					gMusicLoadState++;
					gTimeOfLastMusicUpdate = GetTime( );
//					printf( "music wait IRQ...\n" );
#if SHARE_DMA_CH					
					gLoadStatus &= ~LOAD_STATUS_LOADING_MUSIC;
					if ( gLoadStatus & LOAD_STATUS_NEED_STREAM )
					{
						gLoadStatus &= ~LOAD_STATUS_NEED_STREAM;
						UpdateStream( );
					}
#endif
					break;
				}
			}
			break;

		case EzADPCM_STATUS_RUNNING:
			switch ( gMusicLoadState )
			{
				case ( MUSIC_LOAD_STATE_WAITING_FOR_IRQ ):
				{
#if SHARE_DMA_CH					
					if ( gLoadStatus & LOAD_STATUS_LOADING_STREAM )
					{
						gLoadStatus |= LOAD_STATUS_NEED_MUSIC;
						return;
					}
					gLoadStatus |= LOAD_STATUS_LOADING_MUSIC;
#endif					
					ShowState( "Music Running -- Loading L\n" );
					SmallShowState( "-MRLL" );
					sceSdSetCoreAttr ( MUSIC_CORE | SD_C_IRQ_ENABLE, 0);
					gMusicTransSize = SB_BUF_HALF;
					gMusicRemaining -= SB_BUF_HALF;
					if ( gMusicRemaining < 0 )
					{
						gMusicTransSize += gMusicRemaining;
					}
#if DATA_INTEGRITY_CHECK
					{
						int gronk;
						for ( gronk = 0; gronk < gMusicTransSize >> 4; gronk++ )
						{
							if ( *( ( char * )( 1 + gpIopBuf + MUSIC_L_IOP_OFFSET + gMusicIopOffset + ( gronk * 16 ) ) ) != 0 )
							{
								printf( "Fucked up data!!!!" );
							}
						}
					}
#endif
					
					// load in left side:
					if ( gMusicBufSide == _1st )
					{
						_AdpcmSetMarkSTART( gpIopBuf + MUSIC_L_IOP_OFFSET + gMusicIopOffset, gMusicTransSize );
					}
					if ( gMusicRemaining <= 0 )
					{
						_AdpcmSetMarkFINAL( gpIopBuf + MUSIC_L_IOP_OFFSET + gMusicIopOffset, gMusicTransSize );
					}
					else if ( gMusicBufSide == _2nd )
					{
						_AdpcmSetMarkEND( gpIopBuf + MUSIC_L_IOP_OFFSET + gMusicIopOffset, gMusicTransSize );
					}
					
					sceSdVoiceTrans( TRANS_DMA_CH_MUSIC, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
						(unsigned char *) gpIopBuf + MUSIC_L_IOP_OFFSET + gMusicIopOffset,
						( MUSIC_L_SPU_BUF_LOC + SB_BUF_HALF * gMusicBufSide ), gMusicTransSize );
					gMusicLoadState++;
					break;
				}
				case ( MUSIC_LOAD_STATE_LOADING_L ):
				{
					ShowState( "Music Running -- Loading R\n" );
					SmallShowState( "-MRLR" );
#if DATA_INTEGRITY_CHECK
					{
						int gronk;
						for ( gronk = 0; gronk < gMusicTransSize >> 4; gronk++ )
						{
							if ( *( ( char * )( 1 + gpIopBuf + MUSIC_R_IOP_OFFSET + gMusicIopOffset + ( gronk * 16 ) ) ) != 0 )
							{
								printf( "Fucked up data!!!!" );
							}
						}
					}
#endif
					
					// load in right side:
					if ( gMusicBufSide == _1st )
					{
						_AdpcmSetMarkSTART( gpIopBuf + MUSIC_R_IOP_OFFSET + gMusicIopOffset, gMusicTransSize );
					}
					if ( gMusicRemaining <= 0 )
					{
						_AdpcmSetMarkFINAL( gpIopBuf + MUSIC_R_IOP_OFFSET + gMusicIopOffset, gMusicTransSize );
					}
					else if ( gMusicBufSide == _2nd )
					{
						_AdpcmSetMarkEND( gpIopBuf + MUSIC_R_IOP_OFFSET + gMusicIopOffset, gMusicTransSize );
					}
					sceSdVoiceTrans( TRANS_DMA_CH_MUSIC, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
						(unsigned char *) gpIopBuf + MUSIC_R_IOP_OFFSET + gMusicIopOffset,
						( MUSIC_R_SPU_BUF_LOC + SB_BUF_HALF * gMusicBufSide ), gMusicTransSize );
					gMusicLoadState++;
					break;
				}
				case ( MUSIC_LOAD_STATE_LOADING_R ):
				{
					ShowState( "Music Running -- Waiting for IRQ\n" );
					SmallShowState( " %d-MRIRQ", GetTime( ) );
					// reset the interrupt:
					if ( gMusicRemaining <= 0 )
					{
						sceSdSetAddr( MUSIC_CORE | SD_A_IRQA, gMusicTransSize - LAST_VAG_POS + MUSIC_L_SPU_BUF_LOC + SB_BUF_HALF * gMusicBufSide );
					}
					else
					{
						sceSdSetAddr( MUSIC_CORE | SD_A_IRQA, MUSIC_L_SPU_BUF_LOC + SB_BUF_HALF * gMusicBufSide );
					}
					gTimeOfLastMusicUpdate = GetTime( );
					gMusicIopOffset += SB_BUF_HALF;
					gMusicBufSide = ( gMusicBufSide == _1st ) ? _2nd : _1st;
					
					if ( gMusicRemaining <= 0 )
					{
						// the vag data up to the end should contain all zeros,
						// so it doesn't matter that the interrupt won't happen until the end.
						// stop the music when it gets to the end:
						gMusicStop++;
					}
					else
					{
						if ( gMusicIopOffset == HALF_BUFFER_SIZE )
						{
							//Dbug_Printf( "music move over\n" );
							gMusicIopOffset += HALF_BUFFER_SIZE; // move over by half the size since L/R load in together...
							if ( gMusicRemaining > HALF_BUFFER_SIZE )
							{
								gEECommand |= PCMSTATUS_NEED_MUSIC_BUFFER_0;
							}
						}
						else if ( gMusicIopOffset == ( SINGLE_STREAM_BUFFER_SIZE + HALF_BUFFER_SIZE ) )
						{
							//Dbug_Printf( "music move back\n" );
							gMusicIopOffset = 0;
							if ( gMusicRemaining > HALF_BUFFER_SIZE )
							{
								gEECommand |= PCMSTATUS_NEED_MUSIC_BUFFER_1;
							}
						}
					}
					if ( gMusicStop )
					{
						gMusicStop = 0;
						gMusicStatus = EzADPCM_STATUS_TERMINATE;
					}
					sceSdSetCoreAttr( MUSIC_CORE | SD_C_IRQ_ENABLE, 1);
					gMusicLoadState = MUSIC_LOAD_STATE_WAITING_FOR_IRQ;
//					printf( "music wait IRQ\n" );
#if SHARE_DMA_CH
					gLoadStatus &= ~LOAD_STATUS_LOADING_MUSIC;
					if ( gLoadStatus & LOAD_STATUS_NEED_STREAM )
					{
						gLoadStatus &= ~LOAD_STATUS_NEED_STREAM;
						UpdateStream( );
					}
#endif
					break;
				}
				default:
					printf( "Unknown music loading state %d\n", gMusicLoadState );
					break;
			}
			break;

		case EzADPCM_STATUS_TERMINATE: 
			ShowState( "Music terminate\n" );
			SmallShowState( "-MT!!!" );
			StopMusic( );
			break;

		default:
			printf( "unknown music status in pcm irx!!!\n" );
			break;
	}
}

void UpdateStream( void )
{
//	printf( "hmmm %d %d bufside %d left %d\n", gStreamStatus, gStreamLoadState, gStreamBufSide, gStreamRemaining );
	switch ( gStreamStatus )
	{
		case EzADPCM_STATUS_IDLE:
			break;

		case EzADPCM_STATUS_PRELOAD:
			switch ( gStreamLoadState )
			{
				case ( STREAM_LOAD_STATE_IDLE ):
				{
#if SHARE_DMA_CH					
					if ( gLoadStatus & LOAD_STATUS_LOADING_MUSIC )
					{
						gLoadStatus |= LOAD_STATUS_NEED_STREAM;
						return;
					}
					gLoadStatus |= LOAD_STATUS_LOADING_STREAM;
#endif					
					ShowState( "stream preload l\n" );
					SmallShowState( "-SPL" );
					gStreamBufSide = _1st;
					gStreamIopOffset = 0;
					gStreamRemaining = gStreamSize;
					gStreamRemaining -= SB_BUF_SIZE;
					gStreamTransSize = SB_BUF_SIZE;
					if ( gStreamRemaining < 0 )
					{
						gStreamTransSize += gStreamRemaining;
					}
#if DATA_INTEGRITY_CHECK
					{
						int gronk;
						for ( gronk = 0; gronk < gStreamTransSize >> 4; gronk++ )
						{
							if ( *( ( char * )( 1 + gpIopBuf + STREAM_IOP_OFFSET + ( gronk * 16 ) ) ) != 0 )
							{
								printf( "Fucked up data!!!!" );
							}
						}
					}
#endif
					// preload left:
					_AdpcmSetMarkSTARTpre( gpIopBuf + STREAM_IOP_OFFSET, gStreamTransSize );
					if ( gStreamRemaining > 0 )
					{
						_AdpcmSetMarkENDpre( gpIopBuf + STREAM_IOP_OFFSET, gStreamTransSize );
					}
					else
					{
						_AdpcmSetMarkFINAL( gpIopBuf + STREAM_IOP_OFFSET, gStreamTransSize );
					}
					sceSdVoiceTrans( TRANS_DMA_CH_STREAM, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
						(unsigned char *) ( gpIopBuf + STREAM_IOP_OFFSET ),
						STREAM_SPU_BUF_LOC, gStreamTransSize );
					gStreamLoadState++;
					break;
				}
				case ( STREAM_LOAD_STATE_PRELOADING ):
				{
					ShowState( "Stream starting -- waiting for IRQ\n" );
					SmallShowState( " %d-SSIRQ", GetTime( ) );
					gStreamIopOffset = SB_BUF_SIZE;
					if ( gStreamRemaining <= 0 )
					{
						sceSdSetAddr( STREAM_CORE | SD_A_IRQA, STREAM_SPU_BUF_LOC + gStreamTransSize - LAST_VAG_POS );
						//printf( "tiny stream!!! addr %x transsize %x remaining %x", STREAM_SPU_BUF_LOC + gStreamTransSize - LAST_VAG_POS, gStreamTransSize, gStreamRemaining );
						gStreamStatus = EzADPCM_STATUS_TERMINATE;
					}
					else
					{
						sceSdSetAddr( STREAM_CORE | SD_A_IRQA, STREAM_SPU_BUF_LOC + SB_BUF_HALF );
						gStreamStatus = EzADPCM_STATUS_RUNNING;
					}
					AdpcmSetStreamVolumeDirect( gStreamVolume );
					sceSdSetCoreAttr( STREAM_CORE | SD_C_IRQ_ENABLE, 1 );
					sceSdSetSwitch( STREAM_CORE | SD_S_KON, 1 << STREAM_VOICE );
					gStreamLoadState++;
					gTimeOfLastStreamUpdate = GetTime( );
#if SHARE_DMA_CH
					gLoadStatus &= ~LOAD_STATUS_LOADING_STREAM;
					if ( gLoadStatus & LOAD_STATUS_NEED_MUSIC )
					{
						gLoadStatus &= ~LOAD_STATUS_NEED_MUSIC;
						UpdateMusic( );
					}
#endif
					break;
				}
			}
			break;

		case EzADPCM_STATUS_RUNNING:
			switch ( gStreamLoadState )
			{
				case ( STREAM_LOAD_STATE_WAITING_FOR_IRQ ):
				{
					ShowState( "Stream Running -- Loading\n" );
					SmallShowState( "-SRL" );
#if SHARE_DMA_CH					
					if ( gLoadStatus & LOAD_STATUS_LOADING_MUSIC )
					{
						gLoadStatus |= LOAD_STATUS_NEED_STREAM;
						return;
					}
					gLoadStatus |= LOAD_STATUS_LOADING_STREAM;
#endif					
					sceSdSetCoreAttr ( STREAM_CORE | SD_C_IRQ_ENABLE, 0);
					gStreamTransSize = SB_BUF_HALF;
					gStreamRemaining -= SB_BUF_HALF;
					if ( gStreamRemaining < 0 )
					{
						gStreamTransSize += gStreamRemaining;
					}

#if DATA_INTEGRITY_CHECK
					{
						int gronk;
						for ( gronk = 0; gronk < gStreamTransSize >> 4; gronk++ )
						{
							if ( *( ( char * )( 1 + gpIopBuf + gStreamIopOffset + STREAM_IOP_OFFSET + ( gronk * 16 ) ) ) != 0 )
							{
								printf( "Fucked up data!!!!" );
							}
						}
					}
#endif
					
					// load in left side:
					if ( gStreamBufSide == _1st )
					{
						_AdpcmSetMarkSTART( gpIopBuf + STREAM_IOP_OFFSET + gStreamIopOffset, gStreamTransSize );
						
					}
					if ( gStreamRemaining <= 0 )
					{
						_AdpcmSetMarkFINAL( gpIopBuf + STREAM_IOP_OFFSET + gStreamIopOffset, gStreamTransSize );
					}
					else if ( gStreamBufSide == _2nd )
					{
						_AdpcmSetMarkEND( gpIopBuf + STREAM_IOP_OFFSET + gStreamIopOffset, gStreamTransSize );
					}
					sceSdVoiceTrans( TRANS_DMA_CH_STREAM, ( SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA ),
						(unsigned char *) gpIopBuf + STREAM_IOP_OFFSET + gStreamIopOffset,
						( STREAM_SPU_BUF_LOC + SB_BUF_HALF * gStreamBufSide ), gStreamTransSize );
					gStreamLoadState++;
					break;
				}
				case ( STREAM_LOAD_STATE_LOADING ):
				{
					ShowState( "Stream Running -- Waiting for IRQ\n" );
					SmallShowState( " %d-SRIRQ", GetTime( ) );
					// reset the interrupt:
					if ( gStreamRemaining <= 0 )
					{
						sceSdSetAddr( STREAM_CORE | SD_A_IRQA, gStreamTransSize - LAST_VAG_POS + STREAM_SPU_BUF_LOC + SB_BUF_HALF * gStreamBufSide );
					}
					else
					{
						sceSdSetAddr( STREAM_CORE | SD_A_IRQA, STREAM_SPU_BUF_LOC + SB_BUF_HALF * gStreamBufSide );
					}
					gTimeOfLastStreamUpdate = GetTime( );
		
					gStreamIopOffset += SB_BUF_HALF;
					gStreamBufSide = ( gStreamBufSide == _1st ) ? _2nd : _1st;
					
					if ( gStreamRemaining <= 0 )
					{
						// the vag data up to the end should contain all zeros,
						// so it doesn't matter that the interrupt won't happen until the end.
						// stop the stream when it gets to the end:
						gStreamStop++;
					}
					else
					{
						if ( gStreamIopOffset == HALF_BUFFER_SIZE )
						{
							//Dbug_Printf( "Stream move over\n" );
							if ( gStreamRemaining > HALF_BUFFER_SIZE )
							{
								gEECommand |= PCMSTATUS_NEED_STREAM_BUFFER_0;
							}
						}
						else if ( gStreamIopOffset == SINGLE_STREAM_BUFFER_SIZE )
						{
							//Dbug_Printf( "Stream move back\n" );
							gStreamIopOffset = 0;
							if ( gStreamRemaining > HALF_BUFFER_SIZE )
							{
								gEECommand |= PCMSTATUS_NEED_STREAM_BUFFER_1;
							}
						}
					}
					if ( gStreamStop )
					{
						gStreamStop = 0;
						gStreamStatus = EzADPCM_STATUS_TERMINATE;
					}
					sceSdSetCoreAttr( STREAM_CORE | SD_C_IRQ_ENABLE, 1);
					gStreamLoadState = STREAM_LOAD_STATE_WAITING_FOR_IRQ;
//					printf( "stream wait IRQ\n" );
#if SHARE_DMA_CH
					gLoadStatus &= ~LOAD_STATUS_LOADING_STREAM;
					if ( gLoadStatus & LOAD_STATUS_NEED_MUSIC )
					{
						gLoadStatus &= ~LOAD_STATUS_NEED_MUSIC;
						UpdateMusic( );
					}
#endif
					break;
				}
				default:
					printf( "Unknown Stream loading state %d\n", gStreamLoadState );
					break;
			}
			break;

		case EzADPCM_STATUS_TERMINATE: 
			ShowState( "Stream terminate\n" );
			SmallShowState( "-ST!!!" );
			StopStream( );
			break;

		default:
			printf( "unknown stream status in pcm irx!!!\n" );
			break;
	}
}

/* internal */
int _AdpcmPlay( int status )
{
    while ( 1 )
	{
//        int st, mu;
		WaitSema(gSem);
		
//		ShowTime( "ssig us %d um %d ss %d ms %d ee %d time %d\n", gUpdateStream, gUpdateMusic, gStreamStatus, gMusicStatus, gEECommand, GetTime( ) );
//		SmallShowTime( " <%d>", GetTime( ) );
//		st = gUpdateStream;
//		mu = gUpdateMusic;
		while ( gUpdateStream )
		{
//printf( "s u\n" );		
			gUpdateStream--;
			UpdateStream( );
		}
		while ( gUpdateMusic )
		{
//printf( "m u\n" );		
			gUpdateMusic--;
			UpdateMusic( );
		}
		
/*		
		if ( gTestPause == 666 )
		{
			sceSdSetParam( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_PITCH, 0 );
			sceSdSetParam( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_PITCH, 0 );
			sceSdSetParam( STREAM_CORE | ( STREAM_VOICE << 1 ) | SD_VP_PITCH, 0 );
			gPaused = 1;
		}
		if ( ( !gTestPause ) && ( !st && !mu ) )
		{
			printf( "MOTHERFUCKER MOTHERFUCKER MOTHERFUCKER MOTHERFUCKER\n" );
			gTestPause = 30;
		}*/

//printf( "hmm NAX %x\n", sceSdGetAddr( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VA_NAX ) );
		if ( gMusicVolumeSet )
		{
			gMusicVolumeSet = 0;
			AdpcmSetMusicVolumeDirect( gMusicVolume );
		}
		if ( gStreamVolumeSet )
		{
			gStreamVolumeSet = 0;
			AdpcmSetStreamVolumeDirect( gStreamVolume );
		}
    }
    return 0;
}

static int rpc_arg [16];
volatile int ret = 0;

static void *dispatch( unsigned int command, void *data_, int size )
{ 
    int ch;
    int          data  = *((         int *) data_);
    unsigned int dataU = *((unsigned int *) data_);

/*    PRINTF (("# dispatch [%04x] %x, %x, %x, %x\n",
	      command,
	      *((int*) data_ + 0), 
	      *((int*) data_ + 1),
	      *((int*) data_ + 2),
          *((int*) data_ + 3)));*/

    ch = command & EzADPCM_CH_MASK;
    switch ( command & EzADPCM_COMMAND_MASK )
	{
		case EzADPCM_INIT:
			ret = AdpcmInit( data ); // iop buffer pointer
			break;

		case EzADPCM_QUIT:
			AdpcmQuit( );
			break;

		case EzADPCM_PLAYMUSIC:
			ret = AdpcmPlayMusic( data );  // size of the entire PCM data in the file
			break;

		case EzADPCM_PLAYSTREAM:
			ret = AdpcmPlayStream( data ); // size of the entire PCM data in the file
			break;

		case EzADPCM_PAUSEMUSIC:
			if ( data )
			{
				if ( !gMusicPaused )
				{
					sceSdSetParam( MUSIC_CORE | ( MUSIC_L_VOICE << 1 ) | SD_VP_PITCH, 0 );
					sceSdSetParam( MUSIC_CORE | ( MUSIC_R_VOICE << 1 ) | SD_VP_PITCH, 0 );
					gMusicPaused = GetTime( );
					gMusicTimeOffset = gMusicPaused - gTimeOfLastMusicUpdate;
					if ( gMusicTimeOffset > SINGLE_BUFFER_MS )
					{
/*						if ( gMusicStatus != EzADPCM_STATUS_TERMINATE )
						{
							printf( "\n\nfuck your mom %x \n\n", gMusicStatus );
						}*/
						gMusicTimeOffset -= SINGLE_BUFFER_MS;
					}
					ShowAction( "Pausing music\n" );
					SmallShowAction( "-pm" );
				}
				gRequestFlags &= ~RF_UNPAUSE_MUSIC;
			}
			else
			{
				if ( gMusicPaused )
				{
					gRequestFlags |= RF_UNPAUSE_MUSIC;
					ShowAction( "Requesting unpause music\n" );
					SmallShowAction( "-rupm" );
				}
			}
			break;

		case EzADPCM_PAUSESTREAM:
			if ( data )
			{
				if ( !gStreamPaused )
				{
					sceSdSetParam( STREAM_CORE | ( STREAM_VOICE << 1 ) | SD_VP_PITCH, 0 );
					gStreamPaused = GetTime( );
					gStreamTimeOffset = gStreamPaused - gTimeOfLastStreamUpdate;
					if ( gStreamTimeOffset > SINGLE_BUFFER_MS )
					{
/*						if ( gStreamStatus != EzADPCM_STATUS_TERMINATE )
						{
							printf( "\n\nfuck your mom %x \n\n", gStreamStatus );
						}*/
						gStreamTimeOffset -= SINGLE_BUFFER_MS;
					}
					ShowAction( "Pausing stream\n" );
					SmallShowAction( "-ps" );
				}
				gRequestFlags &= ~( RF_UNPAUSE_STREAM );
			}
			else
			{
				if ( gStreamPaused )
				{
					gRequestFlags |= RF_UNPAUSE_STREAM;
					ShowAction( "Requesting unpause stream\n" );
					SmallShowAction( "-rups" );
				}
			}
			break;
		
		case EzADPCM_STOPMUSIC:
			ret = AdpcmStopMusic( );
			break;

		case EzADPCM_STOPSTREAM:
			ret = AdpcmStopStream( );
			break;

		case EzADPCM_SETMUSICVOL:
			AdpcmSetMusicVolume( dataU );
			break;

		case EzADPCM_SETMUSICVOLDIRECT:
			AdpcmSetMusicVolumeDirect( dataU );
			break;

		case EzADPCM_SETSTREAMVOL:
			AdpcmSetStreamVolume( dataU );
			break;

		case EzADPCM_SETSTREAMVOLDIRECT:
			AdpcmSetStreamVolumeDirect( dataU );
			break;

		case EzADPCM_GETSTATUS:
			ret = AdpcmGetStatus( );
			break;

		case EzADPCM_GETMUSICSTATUS:
			ret = AdpcmGetMusicStatus( );
			break;

		case EzADPCM_GETSTREAMSTATUS:
			ret = AdpcmGetStreamStatus( );
			break;

		case EzADPCM_SDINIT:
			AdpcmSdInit( );
			break;

		default:
			ERROR (("EzADPCM driver error: unknown command %d \n", data));
			break;
    }
    //printf( "! return value = %x \n", ret );
    return (void*)(&ret);
}

int sce_adpcm_loop( void )
{
    sceSifQueueData qd;
    sceSifServeData sd;

    sceSifInitRpc (0);
    sceSifSetRpcQueue (&qd, GetThreadId ());
    sceSifRegisterRpc (&sd, EzADPCM_DEV, dispatch, (void*)rpc_arg, NULL, NULL, &qd);
    //Dbug_Printf(("goto adpcm cmd loop\n"));
    
    sceSifRpcLoop (&qd);

    return 0;
}

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* DON'T ADD STUFF AFTER THIS */


================================================
FILE: Code/Gel/Music/ngc/pcm/pcm_ent.c
================================================
/* Vag streaming into SPU2 -- Converted from Sony samples -- matt may 2001 */

#include 
#include 
#include 
#include 
#include 
#include 
#include "pcm.h"
#include "pcmiop.h"

ModuleInfo Module = {"pcm_driver", 0x0102};

// in command.c
extern int sce_adpcm_loop (void);

int start ( void )
{
    struct ThreadParam param;
    int th;

    if (! sceSifCheckInit ())
	sceSifInit ();
    sceSifInitRpc (0);

    printf ("PCM driver version 666\n");

    param.attr         = TH_C;
    param.entry        = sce_adpcm_loop;
    param.initPriority = BASE_priority - 2;
    param.stackSize    = 0x800;
    param.option       = 0;
    th = CreateThread (¶m);
    if (th > 0) {
	StartThread (th, 0);
	printf (" Exit PCM loader thread \n");
	return 0;
    }else{
	return 1;
    }
}

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */
/* DON'T ADD STUFF AFTER THIS */


================================================
FILE: Code/Gel/Music/ngc/pcm/pcmiop.h
================================================
#if 0
#define PRINTF(x) printf x
#else
#define PRINTF(x) 
#endif
#define ERROR(x) printf x
#define xPRINTF(x) 

#define BASE_priority  32

#define OLDLIB 0
#define TRANS_CH  0



================================================
FILE: Code/Gel/Net/App/netapp.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Net (OBJ) 												**
**																			**
**	File name:		netapp.cpp												**
**																			**
**	Created:		01/29/01	-	spg										**
**																			**
**	Description:	Network app code										**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 

#include 

#ifndef __PLAT_WN32__
#include 
#endif// __PLAT_WN32__

#include 

#include 


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

//extern "C"
//{
	void NS_SetSemaphores( int send_semaphore, int recv_semaphore );
//}

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

//#define DEBUG_MESSAGES
 
/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Net
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

const unsigned int App::MAX_LATENCY = 2000;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/* Initialize the socket and set socket options					  */
/* Also, add default message handlers                             */
/******************************************************************/

bool	App::init( void )
{	
#ifndef __PLAT_NGPS__
	if( !IsLocal())
	{
		// create socket
		m_socket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
#ifdef __PLAT_NGC__
		if( m_socket < 0 )
#else
		if( m_socket == INVALID_SOCKET )
#endif
		{
			ReportError();
			return false;
		}
	
#ifdef __PLAT_NGC__
		int val;
		 
		val = SOFcntl( m_socket, SO_F_GETFL, 0);
		if( val < 0 )
		{
			ReportError();
			return false;
		}
		val = SOFcntl( m_socket, SO_F_SETFL, val | SO_O_NONBLOCK);
		if( val < 0 )
		{
			ReportError();
			return false;
		}
#else
		unsigned long argp = 1L;	// non-zero enables non-blocking mode

		if( ioctlsocket( m_socket, FIONBIO, &argp ) == SOCKET_ERROR )
		{
			ReportError();
			return false;
		}
	
		if( m_flags.TestMask( mBROADCAST ))
		{
			if(( setsockopt( m_socket, SOL_SOCKET, SO_BROADCAST, 
					(char*) &argp, sizeof(argp))) == SOCKET_ERROR )
			{
				ReportError();
				return false;
			}
		}
#endif 	// __PLAT_NGC
	}
#else // __PLAT_NGPS__
	if( !IsLocal())
	{
		sn_int32 true_val, false_val;
	
		true_val = 1;
		false_val = 0;
		
		if(( m_socket = socket( AF_INET, SOCK_DGRAM, PF_INET )) == INVALID_SOCKET )
		{
			ReportError();
			return false;
		}
		
		// make socket non-blocking
		if( setsockopt( m_socket, SOL_SOCKET, SO_NBIO, &true_val, sizeof( sn_int32 )) == SOCKET_ERROR )
		{
			ReportError();
			return false;
		}
	
		if( m_flags.TestMask( mBROADCAST ))
		{
			if(( setsockopt( m_socket, SOL_SOCKET, SO_BROADCAST, 
					(char*) &true_val, sizeof( sn_int32 ))) == SOCKET_ERROR )
			{
				ReportError();
				return false;
			}
		}
		else
		{
			if(( setsockopt( m_socket, SOL_SOCKET, SO_BROADCAST, 
					(char*) &false_val, sizeof( sn_int32 ))) == SOCKET_ERROR )
			{
				ReportError();
				return false;
			}
		}
	}

	m_shutting_down = false;

	m_socket_thread_data.m_pEntry = threaded_transfer_data;
	m_socket_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
	m_socket_thread_data.m_pStackBase = m_socket_thread_stack;
	m_socket_thread_data.m_iStackSize = vSOCKET_THREAD_STACK_SIZE;
	m_socket_thread_data.m_utid = vBASE_SOCKET_THREAD_ID + m_net_man->NumApps();
	Thread::CreateThread( &m_socket_thread_data );
	m_socket_thread_id = m_socket_thread_data.m_osId;
	
	struct SemaParam params;

	params.initCount = 1;
	params.maxCount = 10;

	m_send_semaphore_id = CreateSema( ¶ms );
	m_receive_semaphore_id = CreateSema( ¶ms );
	m_transfer_semaphore_id = CreateSema( ¶ms );
	m_active_semaphore_id = CreateSema( ¶ms );

    if( !IsLocal())
	{
        NS_SetSemaphores( m_send_semaphore_id, m_receive_semaphore_id );
	}

	#ifdef __USER_STEVE__
	Dbg_Printf( "Send Semaphore %d\n", m_send_semaphore_id );
	Dbg_Printf( "Receive Semaphore %d\n", m_receive_semaphore_id );
	Dbg_Printf( "Transfer Semaphore %d\n", m_transfer_semaphore_id );
	#endif

	StartThread( m_socket_thread_id, this );
#endif	// __PLAT_NGPS__

	m_num_connections = 0;	
	m_TotalBytesIn = 0;
	m_TotalBytesOut = 0;
	m_LostPackets = 0;
	m_LatePackets = 0;
	m_DupePackets = 0;
	m_FrameCounter = 0;

	m_Dispatcher.Init();
    
	m_Dispatcher.AddHandler( MSG_ID_ACK, handle_ack, mHANDLE_LATE | mHANDLE_FOREIGN );
	m_Dispatcher.AddHandler( MSG_ID_SEQUENCED, handle_sequenced_messages, mHANDLE_LATE );
	m_Dispatcher.AddHandler( MSG_ID_STREAM_START, handle_stream_messages, mHANDLE_LATE );
	m_Dispatcher.AddHandler( MSG_ID_STREAM_DATA, handle_stream_messages, mHANDLE_LATE );
	m_Dispatcher.AddHandler( MSG_ID_PING_TEST, handle_latency_test, mHANDLE_LATE );
	m_Dispatcher.AddHandler( MSG_ID_PING_RESPONSE, handle_latency_response, mHANDLE_LATE );

	return true;
}

/******************************************************************/
/* Bind a socket                                      			  */
/*                                                                */
/******************************************************************/

bool App::bind_app_socket( int address, unsigned short port )
{
	struct sockaddr_in host_address;
	int address_len, result;

	// bind the socket to an address.

	memset( &host_address, 0, sizeof(host_address));
	host_address.sin_family = AF_INET;
	host_address.sin_port = htons( port );
	// This should basically check if we're using DHCP, but right now modem is the only
	// device using it
	if( m_net_man->GetConnectionType() != vCONN_TYPE_MODEM )
	{
#ifdef __PLAT_XBOX__
		host_address.sin_addr.s_addr = INADDR_ANY;
#else
#ifdef __PLAT_NGC__
		host_address.s_addr.s_addr = address;
#else
		host_address.sin_addr.s_addr = address;
#endif
#endif				
	}
#ifdef __PLAT_NGC__
	host_address.len = sizeof( SOSockAddrIn );
#else
	memset ( &host_address.sin_zero, 0, 8 );  
#endif

	// bind(name) socket
	result = bind( m_socket, (struct sockaddr *)&host_address, sizeof(host_address));
	if( result < 0 )// == SOCKET_ERROR )
	{
#ifdef WIN32
		int err = WSAGetLastError();
		if( err == WSAEADDRINUSE )  
#else
#ifdef __PLAT_NGPS__
		int err = sn_errno( m_socket );
		if( err == EADDRINUSE )
#else
#ifdef __PLAT_NGC__
		if( result == SO_EADDRINUSE )
#endif
#endif
#endif

		{
			ReportError();
			host_address.sin_port = htons( 0 );
			result = bind( m_socket, (struct sockaddr *)&host_address, sizeof(host_address));
			if( result < 0 )// == SOCKET_ERROR )
			{
				ReportError();
				return false;
			}
		}
		else
		{
			ReportError();
			return false;
		}
	}		
    
#ifdef __PLAT_NGC__
	m_local_address.len = sizeof( SOSockAddrIn );
#endif
	address_len = sizeof( m_local_address );
	result = getsockname( m_socket, (sockaddr*) &m_local_address, &address_len );
	if( result < 0 )// == SOCKET_ERROR )
	{
		ReportError();
		return false;
	}

	return true;
}

/******************************************************************/
/* Free up memory used by the network app. Remove msg handlers    */
/*                                                                */
/******************************************************************/

void	App::deinit( void )
{
	

	Lst::Search< Conn > sh;
	Conn *conn, *next;

	if( !IsLocal())
	{
		closesocket( m_socket );
	}
	
	m_Dispatcher.Deinit();

	for( conn = sh.FirstItem( m_connections ); conn; conn = next )
	{
		next = sh.NextItem();
		delete conn;
	}
}

/******************************************************************/
/* Get the next free available handle                             */
/*                                                                */
/******************************************************************/

int		App::get_free_handle( void )
{
	int i;

	

	for( i = Conn::vHANDLE_FIRST; i < 256; i++ )
	{
		if( !GetConnectionByHandle( i ))
		{
			return i;
		}
	}

	Dbg_MsgAssert( 0,( "No free handles\n" ));
	return Conn::vHANDLE_INVALID;
}

/******************************************************************/
/* Process the list of sequenced messages that we have queued up  */
/*                                                                */
/******************************************************************/

void	App::process_sequenced_messages( Conn *conn )
{
	int i;
	Lst::Search< MsgSeqLink > sh;
	MsgSeqLink *msg_link, *next;
	MsgHandlerContext msg_context;
	int result;

	

	Dbg_Assert( conn );

	result = HANDLER_CONTINUE;	
	for( i = 0; i < MAX_SEQ_GROUPS; i++ )
	{
		for( msg_link = sh.FirstItem( conn->m_SequencedBuffer[i] );
				msg_link; msg_link = next )
		{
			next = sh.NextItem();
			
			if( msg_link->m_SequenceId == conn->m_WaitingForSequenceId[msg_link->m_GroupId] )
			{				
				msg_context.m_Conn = conn;
				msg_context.m_Msg = NULL;
				
				if( msg_link->m_QMsg->m_Data )
				{
					msg_context.m_Msg = msg_link->m_QMsg->m_Data;
				}
				msg_context.m_App = this;
				msg_context.m_PacketFlags = 0;
				msg_context.m_MsgId = msg_link->m_QMsg->m_MsgId;
				msg_context.m_MsgLength = msg_link->m_QMsg->m_MsgLength;

				conn->m_WaitingForSequenceId[msg_link->m_GroupId]++;

				result = m_Dispatcher.DispatchMessage( &msg_context );
								
				if( result != HANDLER_MSG_DESTROYED )
				{
					delete msg_link->m_QMsg;
					delete msg_link;
				}

				// If we've gotten a false result, that means we should
				// stop processing this connection
				if( result != HANDLER_CONTINUE )
				{
					return;
				}
			}
			else
			{
#ifdef NET_DEBUG_MESSAGES
				static unsigned int stall = 0;

				if( stall != msg_link->m_SequenceId )
				{
					Dbg_Printf( "*** Stalling on seq: %d, waiting for %d : msg %d, group %d\n", 
								msg_link->m_SequenceId, conn->m_WaitingForSequenceId[msg_link->m_GroupId],
								msg_link->m_QMsg->m_MsgId, msg_link->m_GroupId );
					stall = msg_link->m_SequenceId;
				}
#endif
				break;
			}

		}
	}
}

/******************************************************************/
/* Kill off invalid connections. Should be done at a safe time.   */
/* i.e. when you're not processing connections anymore            */
/******************************************************************/

void	App::terminate_invalid_connections( void )
{
	Conn *conn, *next_conn;
	Lst::Search< Conn > sh;

#ifdef __PLAT_NGPS__
	WaitSema( m_transfer_semaphore_id );
#endif	// __PLAT_NGPS__    
	for( conn = FirstConnection( &sh ); conn;
			conn = next_conn )
	{
		next_conn = NextConnection( &sh );

		// If we're in the process of disconnecting them and we have no more messages
		// to send them, go ahaed and disconnect them
		if( conn->TestStatus( Conn::mSTATUS_DISCONNECTING ))
		{
			if( MessagesToSend( conn ) == false )
			{
				conn->Invalidate();
			}
		}

		// Destroy invalid connections
		if( conn->IsValid() == false )
		{
			if( ( MessagesToProcess( conn ) == false ) && 
				( MessagesToSend( conn ) == false ))
			{
				TerminateConnection( conn );
			}
		}
	}       
#ifdef __PLAT_NGPS__
	SignalSema( m_transfer_semaphore_id );
#endif	// __PLAT_NGPS__    
}

/******************************************************************/
/* Processes any pending stream messages. Enqueuing data if the	  */
/* sliding window permits it                                      */
/******************************************************************/

void    App::process_stream_messages( void )
{
	int size, packet_len, msg_len;
	MsgStreamData data_msg;
	MsgDesc msg_desc;
	StreamDesc* stream_desc;
	StreamLink* stream_link, *next_link;
	Conn *conn;
	Lst::Search< Conn > sh;
	Lst::Search< StreamLink > stream_sh;
	int num_pending, num_to_send;
	Net::Manager* net_man = Net::Manager::Instance();

	for( conn = FirstConnection( &sh ); conn; conn = NextConnection( &sh ))
	{
		int pending_threshold, max_to_send;

		pending_threshold = 5;
		max_to_send = 3;
		if(( net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM ) ||
		   ( conn->GetBandwidthType() == Conn::vNARROWBAND ))
		{
			pending_threshold = 2;
			max_to_send = 1;
		}
		// If we have any pending streams, see if there is any room to queue up chunks
		if( conn->m_StreamOutList.CountItems() > 0 )
		{
			//Dbg_Printf( "******** %d items remain to stream\n", conn->m_StreamOutList.CountItems());
			num_pending = conn->GetNumPendingStreamMessages();
			if( num_pending < pending_threshold ) // Should be configurable and dynamic
			{
				num_to_send = pending_threshold - num_pending;
				if( num_to_send > max_to_send )
				{
					num_to_send = max_to_send;
				}
				for( stream_link = stream_sh.FirstItem( conn->m_StreamOutList ); stream_link; stream_link = next_link )
				{
					next_link = stream_sh.NextItem();

					stream_desc = stream_link->m_Desc;
					size = (unsigned int) stream_desc->m_DataPtr - (unsigned int) stream_desc->m_Data;
					msg_len = stream_desc->m_Size;
					while(( size < msg_len ) && ( num_to_send > 0 ))
					{
						packet_len = msg_len - size;
						if( packet_len > MAX_STREAM_CHUNK_LENGTH )
						{
							packet_len = MAX_STREAM_CHUNK_LENGTH;
						}
				
						data_msg.m_StreamId = stream_desc->m_StreamId;
						memcpy( data_msg.m_Data, stream_desc->m_DataPtr, packet_len );
						
						msg_desc.m_Id = MSG_ID_STREAM_DATA;
						msg_desc.m_Data = &data_msg;
						msg_desc.m_Length = packet_len + sizeof( int );
						msg_desc.m_Queue = QUEUE_SEQUENCED;
						msg_desc.m_GroupId = stream_desc->m_GroupId;
						msg_desc.m_StreamMessage = 1;
						msg_desc.m_ForcedSequenceId = stream_desc->m_SequenceId++;
				
						//Dbg_Printf( "%d ******** [%d] Streaming %d bytes. ForcedSequence: %d\n", m_FrameCounter, stream_desc->m_StreamId, packet_len, stream_desc->m_SequenceId );
						EnqueueMessage( conn->GetHandle(), &msg_desc );
				
						stream_desc->m_DataPtr += packet_len;
						size += packet_len;

						num_to_send--;
					}

					if( size >= msg_len )
					{
						//Dbg_Printf( "******** [%d] Finished streaming. Destroying Stream Link\n", stream_desc->m_StreamId );
						delete stream_desc;
						delete stream_link;
					}
				}
			}
		}
	}
}

/******************************************************************/
/* Read the data from the stack and store in appropriate conn     */
/*                                                                */
/******************************************************************/

void	App::read_network_data( const Tsk::Task< App > &task )
{
	App *app;
    
    app = &task.GetData();

#ifdef __PLAT_NGPS__
	app->m_double_timestamp += ( Tmr::UncappedFrameLength() * 60.0f ) * 
									( Tmr::vRESOLUTION / 60.0f );
	app->m_Timestamp = (unsigned int) app->m_double_timestamp;
#else
	app->m_Timestamp = Tmr::GetTime();
#endif // _PLAT_NGPS__

	app->terminate_invalid_connections();
	
#ifdef __PLAT_NGPS__
	WaitSema( app->m_transfer_semaphore_id );
	SignalSema( app->m_transfer_semaphore_id );
	WakeupThread( app->m_socket_thread_id );
#else
	app->ReceiveData();
#endif
}

/******************************************************************/
/* Service metrics data                 					  	  */
/*                                                                */
/******************************************************************/

void	App::service_network_metrics( const Tsk::Task< App > &task )
{
	App *app;
	Conn *conn;
	Lst::Search< Conn > sh;
    
    app = &task.GetData();
	Dbg_Assert( app );

	for( conn = app->FirstConnection( &sh ); conn; conn = app->NextConnection( &sh ))
	{
		conn->GetInboundMetrics()->CalculateBytesPerSec( app->m_Timestamp );
		conn->GetOutboundMetrics()->CalculateBytesPerSec( app->m_Timestamp );
	}
}

/******************************************************************/
/* Process network data task                 					  */
/*                                                                */
/******************************************************************/

void	App::process_network_data( const Tsk::Task< App > &task )
{
	App *app;
    
    app = &task.GetData();
	Dbg_Assert( app );
	
		app->m_FrameCounter++;

#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PushContext( 255, 255, 255 );
#	endif // __USE_PROFILER__

#ifdef __PLAT_NGPS__
	WaitSema( app->m_receive_semaphore_id );
#endif	// __PLAT_NGPS__    
	app->ProcessData(); 
#ifdef __PLAT_NGPS__
	SignalSema( app->m_receive_semaphore_id );
#endif	// __PLAT_NGPS__    

#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PopContext();
#	endif // __USE_PROFILER__
}

/******************************************************************/
/* Threaded send/receive data for the PS2						  */
/*                                                                */
/******************************************************************/

#ifdef __PLAT_NGPS__

void	App::threaded_transfer_data( void *data )
{
	
    Net::App* app = (Net::App *) data;
    
	WaitSema( app->m_active_semaphore_id );

	if( !app->IsLocal())
	{
#ifdef DEBUG_MESSAGES
		Dbg_Printf( "Registering transfer thread %d with stack\n", GetThreadId());
#endif
	
#ifdef __NOPT_ASSERT__
		int result = 
#endif		
		sockAPIregthr();
		Dbg_Assert( result == 0 );
	}

	app->m_socket_thread_active = true;
	
	app->TransferData();

	if( !app->IsLocal())
	{   
#ifdef DEBUG_MESSAGES
		Dbg_Printf( "DeRegistering transfer thread %d with stack\n", GetThreadId());
#endif
		sockAPIderegthr();
	}

	app->m_socket_thread_active = false;
	
	SignalSema( app->m_active_semaphore_id );
}

#endif

/******************************************************************/
/* Send queued messages across the wire                           */
/*                                                                */
/******************************************************************/

void	App::send_network_data( const Tsk::Task< App > &task )
{
	App *app;

	app = &task.GetData();
	Dbg_Assert( app );

	app->process_stream_messages();

#ifdef __PLAT_NGPS__
	WaitSema( app->m_transfer_semaphore_id );
	SignalSema( app->m_transfer_semaphore_id );
	WakeupThread( app->m_socket_thread_id );
#else
	app->SendData( true );    
#endif

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	App::crc_and_copy_stream( char* in_stream, char* out_stream, int in_len, int* out_len )
{
	uint32 actual_crc;	
	int i;
	unsigned char* in, *out;
	unsigned char key;
	unsigned short hi_word, lo_word, sum_crc;

	
	
	Dbg_Assert( out_stream );
	Dbg_Assert( in_stream );
	Dbg_Assert( in_len > 0 );
	
	actual_crc = Crc::GenerateCRCCaseSensitive( in_stream, in_len );
	hi_word = ( actual_crc & 0xFFFF0000 ) >> 16;
	lo_word = ( actual_crc & 0xFFFF );
	sum_crc = hi_word + lo_word;

	memcpy( out_stream, &sum_crc, sizeof( unsigned short ));

	// Encrypt by XOring each byte with the lsB of the checksum
	out = (unsigned char*) ( out_stream + sizeof( unsigned short ));
	in = (unsigned char*) in_stream;
	key = (unsigned char) sum_crc;
	for( i = 0; i < in_len; i++ )
	{
		*out++ = ( *in++ ) ^ key;
	}
	
	*out_len = in_len + sizeof( unsigned short );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	App::validate_and_copy_stream( char* in_stream, char* out_stream, int in_len )
{
    uint32 actual_crc;
	int out_len;
	unsigned char key;
	unsigned short hi_word, lo_word;
	unsigned short packet_crc, sum_crc;
	unsigned char* in;
	int i;
	char packet_data[ Manager::vMAX_PACKET_SIZE ];

	
	
	Dbg_Assert( out_stream );
	Dbg_Assert( in_stream );
		
	if( in_len < Manager::vMIN_PACKET_SIZE )
	{
		return false;
	}

	// If this isn't a secure app, just process the data as unencrypted and un-checksumed
	if(( m_flags & mSECURE ) == 0 )
	{
		memcpy( out_stream, in_stream, in_len );
		return true;
	}

	out_len = in_len - sizeof( unsigned short );
	memcpy( &packet_crc, in_stream, sizeof( unsigned short ));
	memcpy( packet_data, in_stream + sizeof( unsigned short ), out_len );	// skip CRC

	key = (unsigned char) packet_crc;	// use the lsB of the packet CRC to decrypt the packet
	in = (unsigned char*) packet_data;
	for( i = 0; i < out_len; i++ )
	{
		*in = (*in) ^ key;
		in++;
	}

	actual_crc = Crc::GenerateCRCCaseSensitive( packet_data, out_len );
	hi_word = ( actual_crc & 0xFFFF0000 ) >> 16;
	lo_word = ( actual_crc & 0xFFFF );
	sum_crc = hi_word + lo_word;
	if( sum_crc != packet_crc )
	{
#ifdef DEBUG_MESSAGES
		Dbg_Printf( "CRC Check Failed!!! Got %d instead of %d\n", packet_crc, sum_crc );
#endif
		return false;
	}
	
	memcpy( out_stream, packet_data, out_len );

#ifdef WRITE_OUT_NET_PACKETS
	static int f = 0;
	char path[32];

	sprintf( path, "jpacket%d.net", f );
	void* handle;

	handle = File::Open( path, "wb");
	if( handle )
	{
		File::Write( out_stream, 1, out_len, handle );
		File::Close( handle );	
		f++;
	}
#endif

	return true;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

LatencyTest::LatencyTest( void )
{
	memset( m_TimeTests, 0, sizeof( int ) * NET_NUM_LATENCY_TESTS );
	m_AveLatency = Manager::vSTANDARD_RESEND_THRESHOLD / 2;
	m_CurrentTest = 0;
	m_SendTime = 0;
	m_ReceiveTime = 0;
}

/******************************************************************/
/* Add data to our latency calculator                             */
/*                                                                */
/******************************************************************/

void	LatencyTest::InputLatencyValue( int latency )
{
	int i, num_tests, total;

	m_TimeTests[ m_CurrentTest ] = latency;
	m_CurrentTest = ( m_CurrentTest + 1 ) % NET_NUM_LATENCY_TESTS;

	total = 0;
	num_tests = 0;
	// compute new average
	for( i = 0; i < NET_NUM_LATENCY_TESTS; i++ )
	{
		if( m_TimeTests[i] != 0 )
		{
			total += m_TimeTests[i];
			num_tests++;
		}
	}

	if( num_tests > 0 )
	{
        m_AveLatency = total/num_tests;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

App::App( int flags )
: m_Dispatcher( this ), m_node( this )
{
	m_network_metrics_task = new Tsk::Task< App > ( service_network_metrics, *this, 
														 Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_PROCESS_NETWORK_METRICS );
	m_receive_network_data_task = new Tsk::Task< App > ( read_network_data, *this, 
														 Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_RECEIVE_NETWORK_DATA );
	m_process_network_data_task = new Tsk::Task< App > ( process_network_data, *this,
														 Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_PROCESS_NETWORK_DATA );
	m_double_timestamp = 0;
	m_flags = flags;
	m_Timestamp = 0;
	m_foreign_handler = NULL;
#ifdef	__PLAT_NGPS__											  
	Dbg_MsgAssert(Mem::SameContext(this,Mem::Manager::sHandle().NetworkHeap()),("Net::App not on network heap"));	
#endif		//	__PLAT_NGPS__											  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

App::~App( void )
{
	
	BannedConn* banned_conn, *next;
	Lst::Search< BannedConn > sh;
	
	for( banned_conn = sh.FirstItem( m_banned_connections ); banned_conn;
			banned_conn = next )
	{
		next = sh.NextItem();
		delete banned_conn;
	}
	
    
	delete m_send_network_data_task;
	delete m_receive_network_data_task;
    delete m_process_network_data_task;
	delete m_network_metrics_task;
}

/******************************************************************/
/* Processes the messages in the input queue and also sequenced   */
/* messages. Sends them off to the dispatcher                     */
/******************************************************************/

void	App::ProcessData( void )
{
	Conn *conn, *next_conn;
	Lst::Search< Conn > sh;
	int result;

	for( conn = FirstConnection( &sh ); conn;
			conn = next_conn )
	{   
		next_conn = NextConnection( &sh );

		if( conn->IsLocal() &&
			conn->m_alias_connection )
		{   
			int num_bytes;
			
			num_bytes = (int) conn->m_alias_connection->m_write_ptr - 
							(int) conn->m_alias_connection->m_write_buffer;
			memcpy( conn->m_read_ptr, conn->m_alias_connection->m_write_buffer, num_bytes );
			conn->m_alias_connection->m_write_ptr = conn->m_alias_connection->m_write_buffer;
			conn->m_read_ptr += num_bytes;
		}

		result = m_Dispatcher.DispatchMsgHandlers( conn, 0 );
		if( result == HANDLER_ERROR )
		{
			// output that we've received mal-formatted data
		}
		else if( result == HANDLER_HALT )	// something terminable happened
		{
			return;
		}
		process_sequenced_messages( conn );
	}		
}

/******************************************************************/
/* Given an ip/port combo, return the corresponding connection    */
/* if one exists                                                  */
/******************************************************************/

Conn *App::GetConnectionByAddress( int ip, unsigned short port )
{
	Lst::Search< Conn > sh;
	Conn *conn;

	for( conn = sh.FirstItem( m_connections ); 
			conn; conn = sh.NextItem())
	{
		if(( conn->GetIP() == ip ) && ( conn->GetPort() == port ))
		{
			return conn;
		}
	}

	return NULL;
}

/******************************************************************/
/* Given a handle, return the connection if there is a match      */
/*                                                                */
/******************************************************************/

Conn *App::GetConnectionByHandle( int handle )
{
	Lst::Search< Conn > sh;
	Conn *conn;

	for( conn = sh.FirstItem( m_connections ); 
			conn; conn = sh.NextItem())
	{
		if( conn->GetHandle() == handle )
		{
			return conn;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int			App::GetID( void )
{
	return m_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char		*App::GetName( void )
{
	return m_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Tsk::BaseTask&	App::GetSendDataTask( void )
{
	return *m_send_network_data_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Tsk::BaseTask&	App::GetReceiveDataTask( void )
{
	return *m_receive_network_data_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Tsk::BaseTask&	App::GetProcessDataTask( void )
{
	return *m_process_network_data_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Tsk::BaseTask&	App::GetNetworkMetricsTask( void )
{
	return *m_network_metrics_task;
}

/******************************************************************/
/*  Create a new connection for an ip/port combo                  */
/*                                                                */
/******************************************************************/

Conn *App::NewConnection( int ip, unsigned short port, int flags )
{
	Conn *conn;
	
	

	conn = NULL;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	if( m_num_connections < m_max_connections )
	{
		conn = new Conn( flags );
		conn->SetIP( ip );
		conn->SetPort( port );
		conn->m_handle = get_free_handle();
		conn->m_app = this;
		conn->m_last_comm_time = Tmr::GetTime();
		m_connections.AddToTail( &conn->m_node );
	}

	Mem::Manager::sHandle().PopContext();

	return conn;
}

/******************************************************************/
/* Allocate a new sequenced message with a header                 */
/*                                                                */
/******************************************************************/

QueuedMsgSeq*	App::AllocateNewSeqMessage( unsigned char msg_id, unsigned short msg_len, char* data, unsigned char group_id )
{
	QueuedMsgSeq* queued_msg;

	queued_msg = new QueuedMsgSeq( msg_id, msg_len, data, group_id );

	return queued_msg;
}

/******************************************************************/
/* Build the outgoing stream to send to a connection.  This       */
/* will also re-send important messages which haven't been acked  */
/******************************************************************/

bool App::BuildMsgStream( Conn *conn, QUEUE_TYPE queue, bool resends_only )
{
	bool all_fit;
	unsigned short total_msg_len;

	all_fit = true;

	Dbg_Assert( conn );
	// For each queue, copy all messages into what will be the final
	// buffer to send over the net
	switch( queue )
	{
		case QUEUE_DEFAULT:
		{
			Lst::Search< MsgLink > sh;
			MsgLink *msg_link, *next_link;
			QueuedMsg* queued_msg;
						
			for( msg_link = sh.FirstItem( conn->m_normal_msg_list ); msg_link; msg_link = next_link )
			{   
				int msg_header_size;

				next_link = sh.NextItem();
				if( m_Timestamp < msg_link->m_SendTime )
				{
					continue;
				}

				if( BandwidthExceeded( conn ))
				{
					break;
				}

				queued_msg = msg_link->m_QMsg;              
				if( m_net_man->GetMessageFlags( queued_msg->m_MsgId ) & mMSG_SIZE_UNKNOWN )
				{
					msg_header_size = Manager::vMSG_HEADER_LENGTH;
				}
				else
				{
					msg_header_size = Manager::vMSG_HEADER_LENGTH_WITH_SIZE;
				}
				total_msg_len = msg_header_size + queued_msg->m_MsgLength;
				if((( conn->m_write_ptr - conn->m_write_buffer ) + total_msg_len ) < Manager::vMAX_PAYLOAD )
				{
					memcpy( conn->m_write_ptr, &queued_msg->m_MsgId, sizeof( unsigned char ));
					conn->m_write_ptr += sizeof( unsigned char );
					// Optionally write out the message's size to the stream
					if(( m_net_man->GetMessageFlags( queued_msg->m_MsgId ) & mMSG_SIZE_UNKNOWN ) == 0 )
					{
						memcpy( conn->m_write_ptr, &queued_msg->m_MsgLength, sizeof( unsigned short ));
						conn->m_write_ptr += sizeof( unsigned short );
					}
					if( queued_msg->m_MsgLength > 0 )
					{
						memcpy( conn->m_write_ptr, queued_msg->m_Data, queued_msg->m_MsgLength );
						conn->m_write_ptr += queued_msg->m_MsgLength;
					}
					conn->GetOutboundMetrics()->AddMessage( queued_msg->m_MsgId, queued_msg->m_MsgLength );
					
					// K: Moved this here so that multiple EnqueueMessage's can be done in one frame
					// without earlier ones being dropped.
					// Otherwise CScriptDebugger::SplitAndEnqueueMessage will not work, because
					// only the last message enqueued will get through.
					queued_msg->m_Ref.Release();
					if( queued_msg->m_Ref.InUse() == false )
					{   
						delete queued_msg;
					}
					delete msg_link;
				}
				else
				{
					all_fit = false;
				}
			}

			break;
		}

		case QUEUE_IMPORTANT:
		{
			MsgImpLink *msg_link;
			Lst::Search< MsgImpLink > sh;
			QueuedMsg* queued_msg;
						
			for( msg_link = sh.FirstItem( conn->m_important_msg_list ); msg_link; 
					msg_link = sh.NextItem())
			{
				if( m_Timestamp < msg_link->m_SendTime )
				{
					continue;
				}

				if( BandwidthExceeded( conn ))
				{
					break;
				}

				if(( msg_link->m_Timestamp == 0 ) && resends_only )
				{
					continue;
				}

				// resend if it takes longer than roughly twice the latency
				if( ( msg_link->m_Timestamp == 0 ) ||	// i.e. hasn't been sent yet
					(( m_Timestamp - msg_link->m_Timestamp ) > (unsigned int) conn->GetResendThreshold()))
				{
					int msg_header_size;

					queued_msg = msg_link->m_QMsg;
#ifdef DEBUG_MESSAGES
					if( conn->IsRemote())
					{
						if( msg_link->m_Timestamp != 0 )
						{
							Dbg_Printf( "** RESEND #%d (%d) Conn %d: (%d) %s T %d %d, P %d %d, L %d %d\n", 
								msg_link->m_NumResends + 1,
								m_FrameCounter,
								conn->GetHandle(),
	
								queued_msg->m_MsgId,
								m_net_man->GetMessageName( queued_msg->m_MsgId ),
								
								m_Timestamp,
								msg_link->m_Timestamp,
								
								conn->m_latest_sent_packet_stamp,
								msg_link->m_Packetstamp,
								
								conn->GetResendThreshold(),
								conn->GetAveLatency());							
						}
					}
#endif
					if( m_net_man->GetMessageFlags( queued_msg->m_MsgId ) & mMSG_SIZE_UNKNOWN )
					{
						msg_header_size = Manager::vMSG_HEADER_LENGTH;
					}
					else
					{
						msg_header_size = Manager::vMSG_HEADER_LENGTH_WITH_SIZE;
					}

					total_msg_len = msg_header_size + queued_msg->m_MsgLength;
					if((( conn->m_write_ptr - conn->m_write_buffer ) + total_msg_len ) < Manager::vMAX_PAYLOAD )
					{
						memcpy( conn->m_write_ptr, &queued_msg->m_MsgId, sizeof( unsigned char ));
						conn->m_write_ptr += sizeof( unsigned char );
						// Optionally write out the message's size to the stream
						if(( m_net_man->GetMessageFlags( queued_msg->m_MsgId ) & mMSG_SIZE_UNKNOWN ) == 0 )
						{
							memcpy( conn->m_write_ptr, &queued_msg->m_MsgLength, sizeof( unsigned short ));
							conn->m_write_ptr += sizeof( unsigned short );
						}
						
						if( queued_msg->m_MsgLength > 0 )
						{
							memcpy( conn->m_write_ptr, queued_msg->m_Data, queued_msg->m_MsgLength );
							conn->m_write_ptr += queued_msg->m_MsgLength;
						}
						conn->GetOutboundMetrics()->AddMessage( queued_msg->m_MsgId, queued_msg->m_MsgLength );
						
						if( msg_link->m_Timestamp != 0 )
						{   
							msg_link->m_NumResends++;							
						}
						msg_link->m_Timestamp = m_Timestamp;
						msg_link->m_Packetstamp = conn->m_latest_sent_packet_stamp;
					}					
					else
					{
						all_fit = false;
					}
				}
			}

			break;
		}

		case QUEUE_SEQUENCED:
		{
			MsgSeqLink *msg_link;
			Lst::Search< MsgSeqLink > sh;
			QueuedMsgSeq* queued_msg;
			unsigned char seq_msg_id;
			unsigned short seq_msg_length;
						
			seq_msg_id = MSG_ID_SEQUENCED;
			for( msg_link = sh.FirstItem( conn->m_sequenced_msg_list ); msg_link; 
					msg_link = sh.NextItem())
			{   
				if( m_Timestamp < msg_link->m_SendTime )
				{
					continue;
				}

				if( BandwidthExceeded( conn ))
				{
					break;
				}

				if(( msg_link->m_Timestamp == 0 ) && resends_only )
				{
					continue;
				}

				// resend if it takes longer than roughly twice the latency
				if( ( msg_link->m_Timestamp == 0 ) ||	// i.e. hasn't been sent yet
					(( m_Timestamp - msg_link->m_Timestamp ) > (unsigned int) conn->GetResendThreshold()))
				{
					queued_msg = msg_link->m_QMsg;
#ifdef DEBUG_MESSAGES
					if( conn->IsRemote())
					{
						if( msg_link->m_Timestamp != 0 )
						{
							Dbg_Printf( "** RESEND #%d (%d) Conn %d: (%d) %s T %d %d, P %d %d, Seq: %d Group: %d\n", 
								msg_link->m_NumResends + 1,
								m_FrameCounter,
								conn->GetHandle(),
								
								queued_msg->m_MsgId,
								m_net_man->GetMessageName( queued_msg->m_MsgId ),
								
								m_Timestamp,
								msg_link->m_Timestamp,
								
								conn->m_latest_sent_packet_stamp,
								msg_link->m_Packetstamp,
								
								msg_link->m_SequenceId,
								msg_link->m_GroupId );							
						}						
					}
#endif
					total_msg_len = Manager::vMSG_HEADER_LENGTH_WITH_SIZE + Manager::vMSG_SEQ_HEADER_LENGTH + queued_msg->m_MsgLength;
					seq_msg_length = Manager::vMSG_SEQ_HEADER_LENGTH + queued_msg->m_MsgLength;
					if((( conn->m_write_ptr - conn->m_write_buffer ) + total_msg_len ) < ( Manager::vMAX_PAYLOAD ))
					{						
						memcpy( conn->m_write_ptr, &seq_msg_id, sizeof( unsigned char ));
						conn->m_write_ptr += sizeof( unsigned char );
                        memcpy( conn->m_write_ptr, &seq_msg_length, sizeof( unsigned short ));
						conn->m_write_ptr += sizeof( unsigned short );
                        memcpy( conn->m_write_ptr, &msg_link->m_GroupId, sizeof( char ));
						conn->m_write_ptr += sizeof( char );
						memcpy( conn->m_write_ptr, &msg_link->m_SequenceId, sizeof( unsigned int ));
						conn->m_write_ptr += sizeof( unsigned int );
						memcpy( conn->m_write_ptr, &queued_msg->m_MsgId, sizeof( unsigned char ));
						conn->m_write_ptr += sizeof( unsigned char );
						if( queued_msg->m_MsgLength > 0 )
						{
							memcpy( conn->m_write_ptr, queued_msg->m_Data, queued_msg->m_MsgLength );
							conn->m_write_ptr += queued_msg->m_MsgLength;
						}
						
						conn->GetOutboundMetrics()->AddMessage( queued_msg->m_MsgId, queued_msg->m_MsgLength );
						if( msg_link->m_Timestamp != 0 )
						{
							msg_link->m_NumResends++;
						}
						msg_link->m_Timestamp = m_Timestamp;
						msg_link->m_Packetstamp = conn->m_latest_sent_packet_stamp;
					}                   
					else
					{
						all_fit = false;
					}
				}
			}
			break;
		}
	}

	return all_fit;
}

/******************************************************************/
/* Any pending messages to process from this client??     		  */
/*                                                                */
/******************************************************************/

bool	App::MessagesToProcess( Conn* conn )
{
	if( conn->m_read_ptr > conn->m_read_buffer )
	{
		return true;
	}
		
	return false;
}

/******************************************************************/
/* Are there any important messages to send?					  */
/*                                                                */
/******************************************************************/

bool	App::ImportantMessagesToSend( Conn* conn )
{
	{
		MsgImpLink *msg_link;
		Lst::Search< MsgImpLink > imp_sh;
		for( msg_link = imp_sh.FirstItem( conn->m_important_msg_list ); 
				msg_link; msg_link = imp_sh.NextItem())
		{
			// resend if it takes longer than roughly twice the latency
			if(	( msg_link->m_Timestamp == 0 ) ||
				(( m_Timestamp - msg_link->m_Timestamp ) > (unsigned int) conn->GetResendThreshold()))
			{
				return true;
			}
		}
	}

	{
		MsgSeqLink *msg_link;
		Lst::Search< MsgSeqLink > seq_sh;
		for( msg_link = seq_sh.FirstItem( conn->m_sequenced_msg_list ); 
				msg_link; msg_link = seq_sh.NextItem())
		{
			// resend if it takes longer than roughly twice the latency
			if( ( msg_link->m_Timestamp == 0 ) || 
				(( m_Timestamp - msg_link->m_Timestamp ) > (unsigned int) conn->GetResendThreshold()))
			{
				return true;		
			}
		}
	}

	return false;
}

/******************************************************************/
/* Are there any messages to send to a particular connection?     */
/*                                                                */
/******************************************************************/

bool	App::MessagesToSend( Conn* conn )
{   
	{
		Lst::Search< MsgLink > sh;
		MsgLink* msg_link;
		for( msg_link = sh.FirstItem( conn->m_normal_msg_list ); 
				msg_link; msg_link = sh.NextItem())
		{
			if( m_Timestamp < msg_link->m_SendTime )
			{
				continue;
			}
			return true;
		}
	}

	{
		MsgImpLink *msg_link;
		Lst::Search< MsgImpLink > imp_sh;
		for( msg_link = imp_sh.FirstItem( conn->m_important_msg_list ); 
				msg_link; msg_link = imp_sh.NextItem())
		{
			// resend if it takes longer than roughly twice the latency
			if( ( msg_link->m_Timestamp == 0 ) ||
				( m_Timestamp < msg_link->m_SendTime ) ||
				(( m_Timestamp - msg_link->m_Timestamp ) > (unsigned int) conn->GetResendThreshold()))
			{
				return true;
			}
		}
	}

	{
		MsgSeqLink *msg_link;
		Lst::Search< MsgSeqLink > seq_sh;
		for( msg_link = seq_sh.FirstItem( conn->m_sequenced_msg_list ); 
				msg_link; msg_link = seq_sh.NextItem())
		{
			// resend if it takes longer than roughly twice the latency
			if(	( msg_link->m_Timestamp == 0 ) || 
				( m_Timestamp < msg_link->m_SendTime ) ||
				(( m_Timestamp - msg_link->m_Timestamp ) > (unsigned int) conn->GetResendThreshold()))
			{
				return true;		
			}
		}
	}

	return false;
}

/******************************************************************/
/* Dequeue Messages of type    							  */
/*                                                                */
/******************************************************************/

void App::DequeueMessagesByType( Net::Conn* conn, QUEUE_TYPE queue, unsigned char msg_id )
{
	

    switch( queue )
	{
		case QUEUE_DEFAULT:
		{
			Lst::Search< MsgLink > sh;
			MsgLink *msg_link, *next;
			QueuedMsg *msg;

			for( msg_link = sh.FirstItem( conn->m_normal_msg_list ); msg_link;
					msg_link = next )
			{
				next = sh.NextItem();
				msg = msg_link->m_QMsg;
				if( msg->m_MsgId == msg_id )
				{
					msg->m_Ref.Release();
					if( msg->m_Ref.InUse() == false )
					{
						delete msg;
					}
					delete msg_link;
				}
			}
			
			break;
		}

		case QUEUE_IMPORTANT:
		{
			Lst::Search< MsgImpLink > sh;
			MsgImpLink *msg_link, *next;
			QueuedMsg *msg;

			for( msg_link = sh.FirstItem( conn->m_important_msg_list ); msg_link;
					msg_link = next )
			{
				next = sh.NextItem();
				msg = msg_link->m_QMsg;
				if( msg->m_MsgId == msg_id )
				{
					msg->m_Ref.Release();
					if( msg->m_Ref.InUse() == false )
					{
						delete msg;
					}
					delete msg_link;
				}
			}
			break;
		}

		case QUEUE_SEQUENCED:
		{
			Lst::Search< MsgSeqLink > sh;
			MsgSeqLink *msg_link, *next;
			QueuedMsgSeq *msg;

			for( msg_link = sh.FirstItem( conn->m_sequenced_msg_list ); msg_link;
					msg_link = next )
			{
				next = sh.NextItem();
				msg = msg_link->m_QMsg;
				if( msg->m_MsgId == msg_id )
				{
					msg->m_Ref.Release();
					if( msg->m_Ref.InUse() == false )
					{
						delete msg;
					}
					delete msg_link;
				}
			}
			break;
		}
	}
}

/******************************************************************/
/* Enqueue a new message to be sent to a connection (by handle)   */
/*                                                                */
/******************************************************************/

void App::EnqueueMessage( int handle, MsgDesc* desc )
{
	Conn *conn;
	Lst::Search< Conn > sh;

#ifdef __PLAT_NGPS__
	WaitSema( m_send_semaphore_id );
#endif
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());
	// Make sure the message + headers isn't larger than a packet or else it will never be sent
	Dbg_MsgAssert( desc->m_Length < ( Manager::vMAX_PAYLOAD - 64 ), ("Network Message size (%d) is bigger than %d",desc->m_Length, Manager::vMAX_PAYLOAD - 64) );
	
	// Add new message to the appropriate queue	
	switch( desc->m_Queue )
	{
		case QUEUE_DEFAULT:
		{
			MsgLink *msg_link;
			QueuedMsg *queued_msg;
			
			queued_msg = new QueuedMsg( desc->m_Id, desc->m_Length, desc->m_Data );
			if( handle == HANDLE_ID_BROADCAST )
			{	
				for( conn = sh.FirstItem( m_connections ); conn; conn = sh.NextItem())
				{   // Only enqueue the message if the connection is ready and valid
					if( conn->GetStatus() == Conn::mSTATUS_READY )
					{
						if( desc->m_Singular )
						{
							DequeueMessagesByType( conn, desc->m_Queue, desc->m_Id );
						}

						queued_msg->m_Ref.Acquire();
						msg_link = new MsgLink( queued_msg );
						msg_link->SetPri( desc->m_Priority );
						msg_link->m_SendTime = m_Timestamp + desc->m_Delay;
						conn->m_normal_msg_list.AddNodeFromTail( msg_link );
					}
				}
			}
			else if( handle & HANDLE_ID_EXCLUDE_BROADCAST )
			{
				int excluded_handle;

				excluded_handle = handle & ~HANDLE_ID_EXCLUDE_BROADCAST;
				for( conn = sh.FirstItem( m_connections ); conn; conn = sh.NextItem())
				{
					// Broadcast to all but one handle
					if( conn->GetHandle() == excluded_handle )
					{
						continue;
					}
					// Only enqueue the message if the connection is ready and valid
					if( conn->GetStatus() == Conn::mSTATUS_READY )
					{
						if( desc->m_Singular )
						{
							DequeueMessagesByType( conn, desc->m_Queue, desc->m_Id );
						}
						queued_msg->m_Ref.Acquire();
						msg_link = new MsgLink( queued_msg );
						msg_link->SetPri( desc->m_Priority );
						msg_link->m_SendTime = m_Timestamp + desc->m_Delay;
						conn->m_normal_msg_list.AddNodeFromTail( msg_link );
					}
				}
			}
			else
			{
				conn = this->GetConnectionByHandle( handle );
				Dbg_Assert( conn );

				// Only enqueue the message if the connection is ready and valid
				if(	( conn->GetStatus() == Conn::mSTATUS_READY ) ||
					( desc->m_Id == MSG_ID_TIMESTAMP ))
				{
					if( desc->m_Singular )
					{
						DequeueMessagesByType( conn, desc->m_Queue, desc->m_Id );
					}
					queued_msg->m_Ref.Acquire();
					msg_link = new MsgLink( queued_msg );
					msg_link->SetPri( desc->m_Priority );
					msg_link->m_SendTime = m_Timestamp + desc->m_Delay;
					conn->m_normal_msg_list.AddNodeFromTail( msg_link );
				}
			}
			// If it didn't actually get enqueued, delete it
			if( queued_msg->m_Ref.InUse() == false )
			{
				delete queued_msg;
			}
			break;
		}

		case QUEUE_IMPORTANT:
		{
			MsgImpLink* msg_link;
			QueuedMsg* queued_msg;
			
			queued_msg = new QueuedMsg( desc->m_Id, desc->m_Length, desc->m_Data );
			if( handle == HANDLE_ID_BROADCAST )
			{	
				for( conn = sh.FirstItem( m_connections ); conn; conn = sh.NextItem())
				{
					if( !( conn->TestStatus( Conn::mSTATUS_DISCONNECTING )))
					{
						if( desc->m_Singular )
						{
							DequeueMessagesByType( conn, desc->m_Queue, desc->m_Id );
						}
						queued_msg->m_Ref.Acquire();
						msg_link = new MsgImpLink( queued_msg );
						msg_link->SetPri( desc->m_Priority );
						msg_link->m_SendTime = m_Timestamp + desc->m_Delay;
						msg_link->m_Timestamp = 0;
						conn->m_important_msg_list.AddNodeFromTail( msg_link ); 
					}
				}
			}
			else if( handle & HANDLE_ID_EXCLUDE_BROADCAST )
			{
				int excluded_handle;

				excluded_handle = handle & ~HANDLE_ID_EXCLUDE_BROADCAST;
				for( conn = sh.FirstItem( m_connections ); conn; conn = sh.NextItem())
				{
					// Broadcast to all but one handle
					if( conn->GetHandle() == excluded_handle )
					{
						continue;
					}
					if( !( conn->TestStatus( Conn::mSTATUS_DISCONNECTING )))
					{
						if( desc->m_Singular )
						{
							DequeueMessagesByType( conn, desc->m_Queue, desc->m_Id );
						}
						queued_msg->m_Ref.Acquire();
						msg_link = new MsgImpLink( queued_msg );
						msg_link->SetPri( desc->m_Priority );
						msg_link->m_SendTime = m_Timestamp + desc->m_Delay;
						msg_link->m_Timestamp = 0;
						conn->m_important_msg_list.AddNodeFromTail( msg_link ); 
					}
				}
			}
			else
			{
				conn = this->GetConnectionByHandle( handle );
				Dbg_Assert( conn );
			
				if( !( conn->TestStatus( Conn::mSTATUS_DISCONNECTING )))
				{
					if( desc->m_Singular )
					{
						DequeueMessagesByType( conn, desc->m_Queue, desc->m_Id );
					}
					queued_msg->m_Ref.Acquire();
					msg_link = new MsgImpLink( queued_msg );
					msg_link->SetPri( desc->m_Priority );
					msg_link->m_SendTime = m_Timestamp + desc->m_Delay;
					msg_link->m_Timestamp = 0;
					conn->m_important_msg_list.AddNodeFromTail( msg_link ); 
				}
			}
			// If it didn't actually get enqueued, delete it
			if( queued_msg->m_Ref.InUse() == false )
			{
				delete queued_msg;
			}
			break;
		}

		case QUEUE_SEQUENCED:
		{
			MsgSeqLink *msg_link;
			QueuedMsgSeq* queued_msg;
			
			queued_msg = AllocateNewSeqMessage( desc->m_Id, desc->m_Length, (char *) desc->m_Data, desc->m_GroupId );
						
			if( handle == HANDLE_ID_BROADCAST )
			{	
				for( conn = sh.FirstItem( m_connections ); conn; conn = sh.NextItem())
				{
					if( !( conn->TestStatus( Conn::mSTATUS_DISCONNECTING )))
					{
						Dbg_Assert( desc->m_Singular == false );
						
						queued_msg->m_Ref.Acquire();
						msg_link = new MsgSeqLink( queued_msg );
						msg_link->SetPri( desc->m_Queue );
						msg_link->m_SendTime = m_Timestamp + desc->m_Delay;
						msg_link->m_Timestamp = 0;
						msg_link->m_GroupId = desc->m_GroupId;
						if( desc->m_ForcedSequenceId > 0 )
						{
							msg_link->m_SequenceId = desc->m_ForcedSequenceId;
						}
						else
						{
							msg_link->m_SequenceId = conn->m_SequenceId[desc->m_GroupId]++;
						}
						
						msg_link->m_StreamMessage = desc->m_StreamMessage;
						conn->m_sequenced_msg_list.AddNodeFromTail( msg_link );
					}
				}
			}
			else if( handle & HANDLE_ID_EXCLUDE_BROADCAST )
			{
				int excluded_handle;

				excluded_handle = handle & ~HANDLE_ID_EXCLUDE_BROADCAST;
				for( conn = sh.FirstItem( m_connections ); conn; conn = sh.NextItem())
				{
					// Broadcast to all but one handle
					if( conn->GetHandle() == excluded_handle )
					{
						continue;
					}
					if( !( conn->TestStatus( Conn::mSTATUS_DISCONNECTING )))
					{
						Dbg_Assert( desc->m_Singular == false );

						queued_msg->m_Ref.Acquire();
						msg_link = new MsgSeqLink( queued_msg );
						msg_link->SetPri( desc->m_Priority );
						msg_link->m_SendTime = m_Timestamp + desc->m_Delay;
						msg_link->m_Timestamp = 0;
						msg_link->m_GroupId = desc->m_GroupId;
						if( desc->m_ForcedSequenceId > 0 )
						{
							msg_link->m_SequenceId = desc->m_ForcedSequenceId;
						}
						else
						{
							msg_link->m_SequenceId = conn->m_SequenceId[desc->m_GroupId]++;
						}
						msg_link->m_StreamMessage = desc->m_StreamMessage;
						conn->m_sequenced_msg_list.AddNodeFromTail( msg_link );
					}
				}
			}
			else
			{
				conn = GetConnectionByHandle( handle );
				Dbg_Assert( conn );

				if( !( conn->TestStatus( Conn::mSTATUS_DISCONNECTING )))
				{
					Dbg_Assert( desc->m_Singular == false );

					queued_msg->m_Ref.Acquire();
					msg_link = new MsgSeqLink( queued_msg );
					msg_link->SetPri( desc->m_Priority );
					msg_link->m_SendTime = m_Timestamp + desc->m_Delay;
					msg_link->m_Timestamp = 0;
					msg_link->m_GroupId = desc->m_GroupId;
					if( desc->m_ForcedSequenceId > 0 )
					{
						msg_link->m_SequenceId = desc->m_ForcedSequenceId;
						//Dbg_Printf( "Sending stream data sequence %d\n", msg_link->m_SequenceId );
					}
					else
					{
						msg_link->m_SequenceId = conn->m_SequenceId[desc->m_GroupId]++;
						//Dbg_Printf( "Sending stream start sequence %d\n", msg_link->m_SequenceId );
					}
					msg_link->m_StreamMessage = desc->m_StreamMessage;
					conn->m_sequenced_msg_list.AddNodeFromTail( msg_link );
				}
			}
			// If it didn't actually get enqueued, delete it
			if( queued_msg->m_Ref.InUse() == false )
			{
				delete queued_msg;
			}
			break;
		}

		default:
			Dbg_Assert( 0 );		// unsupported queue type
			break;

	}
	
	Mem::Manager::sHandle().PopContext();

#ifdef __PLAT_NGPS__
	SignalSema( m_send_semaphore_id );
#endif
}

/******************************************************************/
/* Stream a new message, given a connection instead of a handle	  */
/*                                              				  */
/******************************************************************/

void	App::StreamMessageToConn( Net::Conn* conn, unsigned char msg_id, unsigned short msg_len, void* data, char* desc, unsigned char group_id,
								  bool all_at_once, bool send_in_place )
{
	char* data_ptr;
	int size, packet_len;
	MsgStreamStart start_msg;
	MsgStreamData data_msg;
	MsgDesc msg_desc;
	StreamDesc* stream_desc;
	StreamLink* stream_link;

	Dbg_Assert( desc );
	Dbg_Assert( conn );

    // Also, for now, just split the data up into chunks no larger than MAX_STREAM_CHUNK_LENGTH
	// but eventually, maybe let the user decide what that chunk size should be.

    start_msg.m_Size = msg_len;
	start_msg.m_StreamId = conn->m_NextStreamId;
	start_msg.m_MsgId = msg_id;
	start_msg.m_Checksum = Crc::GenerateCRCCaseSensitive((char*) data, msg_len );
	strcpy( start_msg.m_StreamDesc, desc );
	size = 0;

	memcpy( start_msg.m_Data, data, ( msg_len <= MAX_STREAM_CHUNK_LENGTH ) ? msg_len : MAX_STREAM_CHUNK_LENGTH );
	size += ( msg_len <= MAX_STREAM_CHUNK_LENGTH ) ? msg_len : MAX_STREAM_CHUNK_LENGTH;
	packet_len = ( sizeof( start_msg ) - MAX_STREAM_CHUNK_LENGTH ) + size;

	msg_desc.m_Id = MSG_ID_STREAM_START;
	msg_desc.m_Length = packet_len;
	msg_desc.m_Data = &start_msg;
	msg_desc.m_Queue = QUEUE_SEQUENCED;
	msg_desc.m_GroupId = group_id;
	msg_desc.m_StreamMessage = 1;

	//Dbg_Printf( "******** [%d] Streaming %d bytes\n", conn->m_NextStreamId, packet_len );
    EnqueueMessage( conn->GetHandle(), &msg_desc );

	if( msg_len > MAX_STREAM_CHUNK_LENGTH )
	{
		if( all_at_once )
		{
			data_ptr = (char*) data + size;
			while( size < msg_len )
			{
				packet_len = msg_len - size;
				if( packet_len > MAX_STREAM_CHUNK_LENGTH )
				{
					packet_len = MAX_STREAM_CHUNK_LENGTH;
				}
		
				data_msg.m_StreamId = conn->m_NextStreamId;
				memcpy( data_msg.m_Data, data_ptr, packet_len );
				
				msg_desc.m_Id = MSG_ID_STREAM_DATA;
				msg_desc.m_Data = &data_msg;
				msg_desc.m_Length = packet_len + sizeof( int );
				msg_desc.m_Queue = QUEUE_SEQUENCED;
				msg_desc.m_GroupId = group_id;
				msg_desc.m_StreamMessage = 1;
		
				//Dbg_Printf( "%d ******** [%d] Streaming %d bytes.\n", m_FrameCounter, conn->m_NextStreamId, packet_len );
				EnqueueMessage( conn->GetHandle(), &msg_desc );
		
				data_ptr += packet_len;
				size += packet_len;
			}
		}
		else
		{
			// If this message is larger than one chunk, add it to our list of out-bound streams so that we can
			// send it to the connection with a sliding window
			int num_extra_msgs;
			char* send_data;
	
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());
	
			num_extra_msgs = ( msg_len - 1 ) / MAX_STREAM_CHUNK_LENGTH;
			
			if( send_in_place )
			{
				send_data = (char*) data;
			}
			else
			{
				send_data = new char[msg_len];
				memcpy( send_data, data, msg_len );
			}
				
			data_ptr = (char*) send_data + size;
	
			stream_desc = new StreamDesc;
			stream_link = new StreamLink( stream_desc );
		
			stream_desc->m_StreamId = conn->m_NextStreamId;
			stream_desc->m_Size = msg_len;
			stream_desc->m_MsgId = msg_id;
			strcpy( stream_desc->m_StreamDesc, desc );
			stream_desc->m_Data = (char*) send_data;
			stream_desc->m_DataPtr = data_ptr;
			stream_desc->m_GroupId = group_id;
			stream_desc->m_SequenceId = conn->m_SequenceId[group_id];
			stream_desc->m_SendInPlace = send_in_place;
	
			conn->m_StreamOutList.AddToTail( stream_link );
	
			//Dbg_Printf( "******** [%d] Stream rest (%d bytes, %d chunks) later....\n", conn->m_NextStreamId, msg_len - size, num_extra_msgs );
	
			conn->m_SequenceId[group_id] += num_extra_msgs;
			
			Mem::Manager::sHandle().PopContext();
		}
	}

	conn->m_NextStreamId++;
}

/******************************************************************/
/* Stream a new message to send to the connection.				  */
/*                                              				  */
/******************************************************************/

void	App::StreamMessage( int handle, unsigned char msg_id, unsigned short msg_len, void* data, char* desc, unsigned char group_id,
							bool all_at_once, bool send_in_place )
{
	Lst::Search< Conn > sh;
	Conn* conn;

	Dbg_Assert( desc );
	
	if( handle == HANDLE_ID_BROADCAST )
	{	
		for( conn = sh.FirstItem( m_connections ); conn; conn = sh.NextItem())
		{
			if( conn->IsValid())
			{
				StreamMessageToConn( conn, msg_id, msg_len, data, desc, group_id, all_at_once, send_in_place );
			}
		}
	}
	else
	{
		conn = GetConnectionByHandle( handle );
		Dbg_Assert( conn );

		if( conn->IsValid())
		{
			StreamMessageToConn( conn, msg_id, msg_len, data, desc, group_id, all_at_once, send_in_place );
		}
	}
}

/******************************************************************/
/* Stream a new message to send to the server. Just a shortcut	  */
/* to EnqueueMessage                                              */
/******************************************************************/

void	App::StreamMessageToServer( unsigned char msg_id, unsigned short msg_len, void* data, char* desc, unsigned char group_id,
									bool all_at_once, bool send_in_place )
{
	Conn *conn;
	Lst::Search< Conn > sh;

	if(( conn = FirstConnection( &sh )))
	{
		StreamMessageToConn( conn, msg_id, msg_len, data, desc, group_id, all_at_once, send_in_place );
	}
}

/******************************************************************/
/* Enqueue a new message to send to the server. Just a shortcut   */
/* to EnqueueMessage                                              */
/******************************************************************/

void	App::EnqueueMessageToServer( MsgDesc* desc )
{
	Conn *conn;
	Lst::Search< Conn > sh;

	

	if(( conn = FirstConnection( &sh )))
	{
		EnqueueMessage( conn->GetHandle(), desc );
	}
}

/******************************************************************/
/* Free the message queue for a given connection                  */
/*                                                                */
/******************************************************************/

void	App::FreeConnMessageQueue( Conn *conn, QUEUE_TYPE queue )
{
		
	

	Dbg_Assert( conn );

	switch( queue )
	{
		case QUEUE_DEFAULT:
		{
			Lst::Search< MsgLink > sh;
			MsgLink *msg_link, *next;
			QueuedMsg *msg;

			for( msg_link = sh.FirstItem( conn->m_normal_msg_list ); msg_link;
					msg_link = next )
			{
				next = sh.NextItem();
				msg = msg_link->m_QMsg;
				msg->m_Ref.Release();
				if( msg->m_Ref.InUse() == false )
				{
					delete msg;
				}
				delete msg_link;

			}
			
			break;
		}

		case QUEUE_IMPORTANT:
		{
			Lst::Search< MsgImpLink > sh;
			MsgImpLink *msg_link, *next;
			QueuedMsg *msg;

			for( msg_link = sh.FirstItem( conn->m_important_msg_list ); msg_link;
					msg_link = next )
			{
				next = sh.NextItem();
				msg = msg_link->m_QMsg;
				msg->m_Ref.Release();
				if( msg->m_Ref.InUse() == false )
				{
					delete msg;
				}
				delete msg_link;

			}
			break;
		}

		case QUEUE_SEQUENCED:
		{
			Lst::Search< MsgSeqLink > sh;
			MsgSeqLink *msg_link, *next;
			QueuedMsgSeq *msg;

			for( msg_link = sh.FirstItem( conn->m_sequenced_msg_list ); msg_link;
					msg_link = next )
			{
				next = sh.NextItem();
				msg = msg_link->m_QMsg;
				msg->m_Ref.Release();
				if( msg->m_Ref.InUse() == false )
				{
					delete msg;
				}
				delete msg_link;

			}
			break;
		}
	}
}

/******************************************************************/
/* Free message queues of a certain type from all connections     */
/*                                                                */
/******************************************************************/

void	App::FreeMessageQueue( QUEUE_TYPE queue )
{
	Conn *conn;
	Lst::Search< Conn > sh;

	for( conn = FirstConnection( &sh ); conn; conn = NextConnection( &sh ))
	{
		FreeConnMessageQueue( conn, queue );
	}
}

/******************************************************************/
/* Medium-level send function.  Works as the message level		  */
/*                                                                */
/******************************************************************/

bool	App::SendMessageTo( unsigned char msg_id, unsigned short msg_len, void* data,
							int ip, unsigned short port, int flags )
{
	char	msg_data[Manager::vMAX_PACKET_SIZE];
	bool result;

	msg_data[0] = msg_id;
	memcpy( &msg_data[1], &msg_len, sizeof( unsigned short ));
	if( msg_len > 0 )
	{
		memcpy( &msg_data[3], data, msg_len ); 
	}
    
#ifdef __PLAT_NGPS__	
	WaitSema( m_send_semaphore_id );
#endif // __PLAT_NGPS__
	
	result = SendTo( ip, port, msg_data, msg_len + Manager::vMSG_HEADER_LENGTH_WITH_SIZE, flags );

#ifdef __PLAT_NGPS__    
	SignalSema( m_send_semaphore_id );
#endif // __PLAT_NGPS__
	
	return result;
}

/******************************************************************/
/* Lower level, pre-connection network send function              */
/*                                                                */
/******************************************************************/

bool	App::SendTo( int ip, unsigned short port, char *data, int len, int flags )
{
	struct sockaddr_in	to_address;
#ifdef __PLAT_NGC__
    to_address.len = sizeof( sockaddr );
#else
	int addr_len = sizeof(to_address);
#endif
	int	result = 0;
	int send_len;
	
	

	Dbg_Assert( data );
	Dbg_Assert( len <= Manager::vMAX_PAYLOAD );

	// Send data immediately to a destination socket
	memset( &to_address, 0, sizeof(to_address));
	to_address.sin_family = AF_INET;
	to_address.sin_port = htons( port );
		
	// use ip = INADDR_BROADCAST to broadcast
	to_address.sin_addr.s_addr = ip;
	if( m_flags & mSECURE )
	{
		crc_and_copy_stream( data, m_out_packet_buffer, len, &send_len );
		result = sendto( m_socket, m_out_packet_buffer, send_len, flags, 
				(struct sockaddr *) &(to_address), addr_len );
	}
	else
	{
		result = sendto( m_socket, data, len, flags, (struct sockaddr *) &(to_address), addr_len );
	}
	
#ifndef __PLAT_NGC__
	if(	result == SOCKET_ERROR )
	{
		int err;
#if defined( __PLAT_WN32__ ) || defined( __PLAT_XBOX__ )
		err = WSAGetLastError();
		if(	( err != WSAEWOULDBLOCK ) &&
			( err != WSAEINPROGRESS ))
#else
#ifdef __PLAT_NGPS__
		err = sn_errno( m_socket );
		if( ( err != EWOULDBLOCK ) &&
			( err != EINPROGRESS ))
#endif
#endif
		{
#ifdef DEBUG_MESSAGES
		Dbg_Printf( "Sendto Error: packet length %d\n", len );
			ReportError();
#endif
			return false;
		}
	}
#endif

	return true;
}

/******************************************************************/
/* Lower level, pre-(app-level) connection network send function  */
/*                                                                */
/******************************************************************/

bool	App::Send( char *data, int len, int flags )
{
	int	result = 0;
	int send_len;
		
	

	Dbg_Assert( data );
	
	if( m_connected == false )
	{
		return false;
	}

	Dbg_Assert( len <= Manager::vMAX_PAYLOAD );

	// Send data immediately to a destination socket
	if( m_flags & mSECURE )
	{
		crc_and_copy_stream( data, m_out_packet_buffer, len, &send_len );
		result = send( m_socket, m_out_packet_buffer, send_len, flags );
	}
	else
	{
		result = send( m_socket, data, len, flags );
	}

	if( result == SOCKET_ERROR )
	{
#ifdef DEBUG_MESSAGES
		Dbg_Printf( "Send Error: packet length %d\n", len );
		ReportError();
#endif
		return false;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Conn	*App::FirstConnection( Lst::Search< Conn > *sh )
{
	

	Dbg_Assert( sh );

	return( sh->FirstItem( m_connections ));	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Conn	*App::NextConnection( Lst::Search< Conn > *sh )
{
	

	Dbg_Assert( sh );

	return( sh->NextItem());
}

/******************************************************************/
/* Immediately terminate connection.  Don't use this in msg		  */
/* handlers. Instead, invalidate the connection only              */
/******************************************************************/

void	App::TerminateConnection( Conn* conn )
{
	
	
	Dbg_Assert( conn );

	struct in_addr address;

	address.s_addr = conn->GetIP();
	conn->m_node.Remove();
	delete conn;
}

/******************************************************************/
/* Report the last error on the socket                            */
/*                                                                */
/******************************************************************/

void		App::ReportError( void )
{
	

#ifdef WIN32
	char msg[1024];

	//Dbg_Printf( "%s: Error %d\n", m_name, WSAGetLastError());
	sprintf( msg, "%s: Error %d\n", m_name, WSAGetLastError());
	OutputDebugString( msg );
#else
#ifdef __PLAT_NGPS__
#ifdef DEBUG_MESSAGES
	Dbg_Printf( "(%d) %s: Error %d\n", m_FrameCounter, m_name, sn_errno( m_socket ));
#endif
#endif
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager*	App::GetManager( void )
{
	return m_net_man;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		App::SendEnqueuedMessages( void )
{
	Conn *conn;
	Lst::Search< Conn > sh;
		
	
    
#ifdef __PLAT_NGPS__
    WaitSema( m_send_semaphore_id );
#endif	// __PLAT_NGPS__    
	for( conn = FirstConnection( &sh );
			conn; conn = NextConnection( &sh ))
	{
		SendEnqueuedMessages( conn );
	}
	
#ifdef __PLAT_NGPS__
	SignalSema( m_send_semaphore_id );
#endif	// __PLAT_NGPS__

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#ifdef __PLAT_NGPS__

void		App::TransferData( void )
{
	while( 1 )
	{
		// Receive data
		SleepThread();
		if( m_shutting_down )
		{
			break;
		}
		WaitSema( m_transfer_semaphore_id );
		ReceiveData();
		SignalSema( m_transfer_semaphore_id );
		
		// Now wait until that data has been processed and it is time to send new data
		// outbound
		SleepThread();

		// Send Data
		if( m_shutting_down )
		{
			break;
		}
		WaitSema( m_transfer_semaphore_id );
		SendEnqueuedMessages();
		SignalSema( m_transfer_semaphore_id );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		App::WaitForAsyncCallsToFinish( void )
{
	
    
	WaitForTransferSemaphore();
    SignalTransferSemaphore(); 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		App::WaitForTransferSemaphore( void )
{   
	WaitSema( m_transfer_semaphore_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		App::SignalTransferSemaphore( void )
{   
	SignalSema( m_transfer_semaphore_id );
}

#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		App::SendData( bool scheduled_send )
{
	
	Lst::Search< Conn > sh;
	Conn* conn;

	// If someone is requesting a send of all curretly enqueued data before its time
	// we need to tell each connection to ignore the logic to send every N frames and rather
	// send immediately
	if( scheduled_send == false )
	{   
		m_Timestamp++;	// This will ensure that the timestamp is unique. Otherwise
						// the recipient will think it's a dupe packet
		for( conn = FirstConnection( &sh ); conn; conn = NextConnection( &sh ))
		{
			conn->SetForceSendThisFrame( true );
		}
	}

#ifdef __PLAT_NGPS__
	if( !IsLocal() && m_socket_thread_active )
	{   
		WaitSema( m_transfer_semaphore_id );
	}
#endif

	SendEnqueuedMessages();

#ifdef __PLAT_NGPS__
	if( !IsLocal() && m_socket_thread_active )
	{   
		SignalSema( m_transfer_semaphore_id );
	}
#endif	// __PLAT_NGPS_
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		App::ShouldSendThisFrame( Conn* conn )
{
	if( (( m_Timestamp - conn->m_last_send_time ) >= (unsigned int) conn->GetSendInterval()) ||
		( conn->GetForceSendThisFrame() ))
		
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	App::ShutDown( void )
{
	
#ifdef DEBUG_MESSAGES
	Dbg_Printf( "Shutting NetApp %s Down\n", GetName());
#endif

#ifdef __PLAT_NGPS__
	m_shutting_down = true;

	//if( m_socket_thread_active )
	{   
		WakeupThread( m_socket_thread_id );
	}
#ifdef DEBUG_MESSAGES	
	Dbg_Printf( "Waiting for active semaphore\n" );
#endif
	WaitSema( m_active_semaphore_id );

#ifdef DEBUG_MESSAGES
    Dbg_Printf( "Deleting semaphores\n" );
#endif
	DeleteSema( m_send_semaphore_id );
	DeleteSema( m_receive_semaphore_id );
	DeleteSema( m_transfer_semaphore_id );
	DeleteSema( m_active_semaphore_id );
	
#ifdef DEBUG_MESSAGES
	Dbg_Printf( "Deleting socket thread\n" );
#endif
	DeleteThread( m_socket_thread_id );
    
#endif
    
    deinit();
#ifdef DEBUG_MESSAGES
	Dbg_Printf( "NetApp %s successfully shut down\n", GetName());
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	App::AliasConnections( Conn* server_conn, Conn* client_conn )
{
	

	Dbg_Assert( server_conn->IsLocal() && client_conn->IsLocal());
	server_conn->m_alias_connection = client_conn;
	client_conn->m_alias_connection = server_conn;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	App::IsLocal( void )
{
	return m_flags.TestMask( mLOCAL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	App::AcceptForeignConnections( bool accept )
{
	if( accept )
	{
		m_flags.SetMask( mACCEPT_FOREIGN_CONN );
	}
	else
	{
		m_flags.ClearMask( mACCEPT_FOREIGN_CONN );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	App::AcceptsForeignConnections( void )
{
	return m_flags.TestMask( mACCEPT_FOREIGN_CONN );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SOCKET	App::GetSocket( void )
{
	return m_socket;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	App::SetForeignPacketHandler( ForeignPacketHandlerCode* code )
{
	m_foreign_handler = code;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	App::BanConnection( Conn* conn )
{
	BannedConn* banned_conn;

	banned_conn = new BannedConn;

	banned_conn->m_Ip = conn->GetIP();
	m_banned_connections.AddToTail( banned_conn );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	App::IsConnectionBanned( Conn* conn )
{
	BannedConn* banned_conn;
	Lst::Search< BannedConn > sh;
	
	for( banned_conn = sh.FirstItem( m_banned_connections ); banned_conn;
			banned_conn = sh.NextItem())
	{
		if( banned_conn->m_Ip == conn->GetIP())
		{
			return true;
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		App::BandwidthUsed( void )
{
	int total_bandwidth;
	Conn* conn;
	Lst::Search< Conn > sh;

	total_bandwidth = 0;
	for( conn = FirstConnection( &sh ); conn; conn = NextConnection( &sh ))
	{
		if( conn->IsRemote())
		{
			Metrics* metrics;
	
			metrics = conn->GetOutboundMetrics();
			total_bandwidth += metrics->GetBytesPerSec();
		}
	}

	return total_bandwidth;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	App::BandwidthExceeded( Conn* conn )
{
	Metrics* metrics;
	int total_bandwidth, per_user_bandwidth, num_remote_connections;
	Conn* tmp_conn;
	Lst::Search< Conn > sh;

    metrics = conn->GetOutboundMetrics();
	total_bandwidth = BandwidthUsed();
	// First, make sure we haven't exceeded the total bandwidth of our connection
	if( total_bandwidth >= m_net_man->GetBandwidth())
	{
		Dbg_Printf( "Total bandwidth exceeded : %d  %d\n", total_bandwidth, m_net_man->GetBandwidth() );
		return true;
	}

	// Then, make sure we haven't flooded the client
	if( metrics->GetBytesPerSec() >= conn->GetBandwidth())
	{
		Dbg_Printf( "(%d) Client flooded: %d %d\n", m_FrameCounter, metrics->GetBytesPerSec(), conn->GetBandwidth() );
		return true;
	}

	num_remote_connections = 0;
	for( tmp_conn = FirstConnection( &sh ); tmp_conn; tmp_conn = NextConnection( &sh ))
	{
		if( tmp_conn->IsRemote() && !tmp_conn->IsForeign())
		{
			num_remote_connections++;
		}
	}

	if( num_remote_connections == 0 )
	{
		num_remote_connections = 1;	// Avoid DBZ
	}
	// Also, check if we've used up this client's share of our bandwidth
	per_user_bandwidth = ( m_net_man->GetBandwidth() / num_remote_connections );
	if( metrics->GetBytesPerSec() > per_user_bandwidth )
	{
		Dbg_Printf( "Share flooded: %d %d\n",  metrics->GetBytesPerSec(), per_user_bandwidth );
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}	// namespace Net


================================================
FILE: Code/Gel/Net/Client/netclnt.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Net (OBJ) 												**
**																			**
**	File name:		netclnt.cpp												**
**																			**
**	Created:		01/29/01	-	spg										**
**																			**
**	Description:	Network client code										**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

#include 

#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Net
{
								  


/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

bool	Client::init( void )
{
	

#ifdef	__PLAT_NGPS__											  
	Dbg_MsgAssert(Mem::SameContext(this,Mem::Manager::sHandle().NetworkHeap()),("Client not on network heap"));	
#endif		//	__PLAT_NGPS__											  


	m_connected = false;
	m_Timestamp = 0;
	if( App::init() == false )
	{
		return false;
	}
	
	m_Dispatcher.AddHandler( MSG_ID_CONNECTION_ACCEPTED, handle_connection_accepted, 
							 mHANDLE_FOREIGN | mHANDLE_LATE );
	m_Dispatcher.AddHandler( MSG_ID_CONNECTION_REFUSED, handle_connection_refusal, mHANDLE_LATE );
	m_Dispatcher.AddHandler( MSG_ID_CONNECTION_TERMINATED, handle_connection_refusal, mHANDLE_LATE );
	m_Dispatcher.AddHandler( MSG_ID_TIMESTAMP, handle_timestamp, mHANDLE_LATE | mHANDLE_FOREIGN );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Client::deinit( void )
{
	

	App::deinit();
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/* Connect (socket-level) to a server at a given IP/Port          */
/*                                                                */
/******************************************************************/

bool Client::ConnectToServer( int ip, unsigned short port )
{
	int result;
	bool connected;
	
	memset( &m_server_address, 0, sizeof(m_server_address));
	m_server_address.sin_family = AF_INET;
	m_server_address.sin_port = htons( port );	
	m_server_address.sin_addr.s_addr = ip;
#ifdef __PLAT_NGC__
	m_server_address.len = sizeof( sockaddr_in );
#else
	memset ( &m_server_address.sin_zero, 0, 8 );  
#endif

	Dbg_Printf( "%s Connecting to server\n", GetName());
	connected = false;
	do
	{
		if(( result = connect( m_socket, (struct sockaddr *)&m_server_address, 
					sizeof( m_server_address ))) == SOCKET_ERROR )
		{
#if defined( __PLAT_WN32__ ) || defined( __PLAT_XBOX__ )
			if( WSAGetLastError() != WSAEWOULDBLOCK )
#else
#ifdef __PLAT_NGPS__
			if( sn_errno( m_socket ) != EWOULDBLOCK )
#endif
#endif
			{
				ReportError();
				return false;
			}
		}
		else
		{
			connected = true;
		}
	} while( !connected );

	// "Connecting" to INADDR_ANY disassociates our socket from all addresses
	if( ip == INADDR_ANY )
	{   
		m_connected = false;
	}
	else
	{   
		m_connected = true;
	}
	return true;
}

/******************************************************************/
/* Client's send function.                                        */
/*                                                                */
/******************************************************************/

void	Client::SendEnqueuedMessages( Conn* conn )
{
	int buf_len;
	MsgPacketStamp msg;
	bool buffer_full;
    
	// Should we send this frame?
	if( ShouldSendThisFrame( conn ) == false )
	{
		return;
	}

	buffer_full = false;
    while( !buffer_full && MessagesToSend( conn ) && !BandwidthExceeded( conn ))
	{
		// Tack on a timestamp (for acking) if we are sending important
		// messages in this packet
		if( ImportantMessagesToSend( conn ))
		{
			MsgDesc msg_desc;
#ifdef __PLAT_NGPS__
			SignalSema( m_send_semaphore_id );
#endif  
			msg.m_Packetstamp = (unsigned short) conn->m_latest_sent_packet_stamp;
            //msg.m_Handle = conn->GetHandle();
			
			msg_desc.m_Data = &msg;
			msg_desc.m_Length = sizeof( MsgPacketStamp );
			msg_desc.m_Id = MSG_ID_TIMESTAMP;
			msg_desc.m_Priority = HIGHEST_PRIORITY;
			EnqueueMessage( conn->GetHandle(), &msg_desc );
#ifdef __PLAT_NGPS__
			WaitSema( m_send_semaphore_id );
#endif
		}
	
		// Send Latency Tests/Responses if applicable
		if( ( conn->IsRemote()) &&
			( conn->IsForeign() == false ) &&
			( m_flags.TestMask( App::mDYNAMIC_RESEND )))
		{
			MsgDesc msg_desc;
			MsgTimestamp latency_msg;
			unsigned int cur_time;
	
			cur_time = m_Timestamp;
			latency_msg.m_Timestamp = cur_time;
			// send out a new latency test, keeping track of the time at which
			// we sent it
			if(	( conn->m_latency_test.m_SendTime == 0 ) ||
				( ( cur_time - conn->m_latency_test.m_SendTime ) > App::MAX_LATENCY ))
			{
				// If we never got a response, simulate an increased latency
				if( conn->m_latency_test.m_SendTime > conn->m_latency_test.m_ReceiveTime )
				{
					unsigned int latency_value;
	
					latency_value = conn->GetAveLatency();
					latency_value += 100;
	
					if( latency_value > App::MAX_LATENCY )
					{
						latency_value = App::MAX_LATENCY;
					}
					conn->m_latency_test.InputLatencyValue( latency_value );
				}
	
				conn->m_latency_test.m_SendTime = cur_time;
	
#ifdef __PLAT_NGPS__
				SignalSema( m_send_semaphore_id );
#endif  
				msg_desc.m_Data = &latency_msg;
				msg_desc.m_Id = MSG_ID_PING_TEST;
				msg_desc.m_Length = sizeof( MsgTimestamp );
				EnqueueMessage( conn->GetHandle(), &msg_desc ); 
	
#ifdef __PLAT_NGPS__
				WaitSema( m_send_semaphore_id );
#endif
			}			
		}
		
		if( !BuildMsgStream( conn, QUEUE_DEFAULT ))
		{
			buffer_full = true;
		}
		// First, use up our bandwidth with re-sends or else they might remain un-sent
		// indefinitely, causing a bad backup on the client if he's waiting on 
		// one particular sequence
		if( !BuildMsgStream( conn, QUEUE_SEQUENCED, true ))
		{
			buffer_full = true;
		}
		if( !BuildMsgStream( conn, QUEUE_IMPORTANT, true ))
		{
			buffer_full = true;
		}
		if( !BuildMsgStream( conn, QUEUE_SEQUENCED ))
		{
			buffer_full = true;
		}
		if( !BuildMsgStream( conn, QUEUE_IMPORTANT ))
		{
			buffer_full = true;
		}
		
		buf_len = conn->m_write_ptr - conn->m_write_buffer;
		// If there is data to send
		if(	( buf_len > 0 ) &&
			( conn->IsRemote()))
		{
			if( m_connected )
			{           
				if( Send( conn->m_write_buffer, buf_len, 0 ))
				{
					m_TotalBytesOut += buf_len;
					conn->GetOutboundMetrics()->AddPacket( buf_len + vUDP_PACKET_OVERHEAD, m_Timestamp );
					conn->m_last_send_time = m_Timestamp;
					conn->SetForceSendThisFrame( false );
				}
				else
				{
					// If it didn't send the messages properly, flag them for resend next frame
					conn->FlagMessagesForResending( conn->m_latest_sent_packet_stamp );
					conn->m_write_ptr = conn->m_write_buffer;
					break;
				}
	
				conn->m_write_ptr = conn->m_write_buffer;								
			}
			else
			{
				if( SendTo( conn->GetIP(), conn->GetPort(), conn->m_write_buffer, buf_len, 0 ))
				{
					m_TotalBytesOut += buf_len;
					conn->GetOutboundMetrics()->AddPacket( buf_len + vUDP_PACKET_OVERHEAD, m_Timestamp );
					conn->m_last_send_time = m_Timestamp;
					conn->SetForceSendThisFrame( false );
				}
				else
				{
					Dbg_Printf( "*** SendTo Error: Flagging messages for resending\n" );
					// If it didn't send the messages properly, flag them for resend next frame
					conn->FlagMessagesForResending( conn->m_latest_sent_packet_stamp );
					conn->m_write_ptr = conn->m_write_buffer;
					break;
				}
				conn->m_write_ptr = conn->m_write_buffer;								
			}
		}
		
		conn->m_latest_sent_packet_stamp++;
	}
}

/******************************************************************/
/* Client's receive function                                      */
/*                                                                */
/******************************************************************/

void	Client::ReceiveData( void )
{
	Conn *conn;
	int num_bytes, actual_data_len;
	struct sockaddr from_address;
#ifdef __PLAT_NGC__
	from_address.len = sizeof( sockaddr );
#else
	int addr_len = sizeof( from_address );
#endif
	struct sockaddr_in *foreign_address;
	
		
	
	
	// Local clients never really receive. Their data is automatically "received" when
	// the local server writes its data into the client's receive buffer automatically
	if( IsLocal())
	{
		return;
	}
	// read data into local buffers
	do
	{   
		if( !m_connected )
		{   
			num_bytes = recvfrom( m_socket, m_in_packet_buffer, 
				Manager::vMAX_PACKET_SIZE, 0, &from_address, &addr_len );
		}
		else
		{   
			num_bytes = recv( m_socket, m_in_packet_buffer, Manager::vMAX_PACKET_SIZE, 0 );
		}
		if ( num_bytes < 0 )//== SOCKET_ERROR ) 
		{
#if defined( __PLAT_WN32__ ) || defined( __PLAT_XBOX__ )
			if( WSAGetLastError() != WSAEWOULDBLOCK )
#else
#ifdef __PLAT_NGPS__
			if( sn_errno( m_socket ) != EWOULDBLOCK )
#else
#ifdef __PLAT_NGC__
			if( num_bytes != SO_EWOULDBLOCK )
#endif
#endif
#endif
            {
#ifdef NET_DEBUG_MESSAGES
				Dbg_Printf( "Client Receive Error :" );
#ifdef __PLAT_WN32__
				OutputDebugString( "Client Receive Error!\n" );
#endif
				ReportError();
#endif
			}
			break;
		}
		else
		{   
#ifdef __PLAT_NGPS__
			WaitSema( m_receive_semaphore_id );
#endif	// __PLAT_NGPS__
			m_TotalBytesIn += num_bytes;
			if( m_flags & App::mSECURE )
			{
				actual_data_len = num_bytes - sizeof( unsigned short );	// total size - CRC size
			}
			else
			{
				actual_data_len = num_bytes;
			}
			
			conn = GetConnectionByAddress( m_server_address.sin_addr.s_addr, 
											ntohs( m_server_address.sin_port ));
            if( conn )
			{
				if(( conn->m_read_ptr + actual_data_len ) < ( conn->m_read_buffer + Conn::vREAD_BUFFER_LENGTH ))
				{
					Tmr::Time cur_time;

					cur_time = Tmr::GetTime();
					if( !validate_and_copy_stream( m_in_packet_buffer, conn->m_read_ptr, num_bytes ))
					{
#ifdef __PLAT_NGPS__
						SignalSema( m_receive_semaphore_id );
#endif	// __PLAT_NGPS__
						// If the game would like to handle this foreign data, allow it to do so now
						if( m_foreign_handler )
						{
							m_foreign_handler( m_in_packet_buffer, num_bytes, &from_address );
						}
						continue;
					}
					conn->m_read_ptr += actual_data_len;
					conn->GetInboundMetrics()->AddPacket( num_bytes + vUDP_PACKET_OVERHEAD, m_Timestamp );
					if( cur_time > conn->m_last_comm_time )
					{
						conn->m_last_comm_time = cur_time;
					}					
				}
				else
				{
#ifdef __PLAT_WN32__
					char message[1024];
					sprintf( message, "****** %d: Dropped Packet\n", m_Timestamp );
					OutputDebugString( message ); 
#endif	// __PLAT_WN32__					
				}
			}
			else if( !m_connected )
			{
				Conn* foreign_conn;

				foreign_address = (struct sockaddr_in*) &from_address;
				// Foreign connection. Create temporary connection to process this foreign message
				foreign_conn = NewConnection( foreign_address->sin_addr.s_addr, 
												ntohs( foreign_address->sin_port ), 
											  Conn::mREMOTE | 
											  Conn::mFOREIGN );
				if( foreign_conn )
				{
					foreign_conn->m_app = this;
					//memcpy( foreign_conn->m_read_buffer, m_read_buffer, actual_data_len );				
					if( !validate_and_copy_stream( m_in_packet_buffer, foreign_conn->m_read_ptr, num_bytes ))
					{
#ifdef __PLAT_NGPS__
						SignalSema( m_receive_semaphore_id );
#endif	// __PLAT_NGPS__
						// If the game would like to handle this foreign data, allow it to do so now
						if( m_foreign_handler )
						{
							m_foreign_handler( m_in_packet_buffer, num_bytes, &from_address );
						}
						delete foreign_conn;
						continue;
					}
					foreign_conn->m_read_ptr = foreign_conn->m_read_buffer + actual_data_len;				
					foreign_conn->Invalidate();
				}
			}
#ifdef __PLAT_NGPS__
			SignalSema( m_receive_semaphore_id );
#endif	// __PLAT_NGPS__
		}		

	} while( num_bytes > 0 );
}

#ifdef USE_ALIASES
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Client::ClearAliasTable( void )
{
	int i;

	for( i = 0; i < vNUM_ALIASES; i++ )
	{
		m_alias_table[i].m_Id = 0;
		m_alias_table[i].m_Expiration = -1;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

unsigned char	Client::GetObjectAlias( int obj_id, int cur_time )
{
	int hash, i;
	bool quit;

	hash = obj_id & 0xff;	// mask off all but lowest byte and use as a hash into the table
	hash %= vNUM_ALIASES;
	quit = false;
	i = hash;
	do
	{   
		if( m_alias_table[i].m_Id == obj_id )
		{   
			if( cur_time > m_alias_table[i].m_Expiration )
			{
				return (unsigned char) vNO_ALIAS;
			}
			return i;
		}
		
		i = ( i + 1 ) % vNUM_ALIASES;
		if( i == hash )	// complete revolution
		{
			break;
		}
	} while( !quit );

	return (unsigned char) vNO_ALIAS;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

unsigned char	Client::NewObjectAlias( int obj_id, int cur_time, bool expires )
{
	int hash, i;
	bool quit;

	

	hash = obj_id & 0xff;	// mask off all but lowest byte and use as a hash into the table
	hash %= vNUM_ALIASES;
	quit = false;
	i = hash;
	do
	{
		if( m_alias_table[i].m_Id == obj_id )
		{   
			m_alias_table[i].m_Expiration = cur_time + vALIAS_DURATION;
			return (unsigned char) i;
		}
		else if(( cur_time - m_alias_table[i].m_Expiration ) > vALIAS_DURATION )
		{
			m_alias_table[i].m_Id = obj_id;
			m_alias_table[i].m_Expiration = cur_time + vALIAS_DURATION;
			return (unsigned char) i;
		}
		i = ( i + 1 ) % vNUM_ALIASES;
		if( i == hash )	// complete revolution
		{
			break;
		}
	} while( !quit );

	// no free aliases
	return (unsigned char) vNO_ALIAS;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Client::GetObjectId( unsigned char alias )
{
	
    
	//Dbg_MsgAssert(( m_LatestPacketStamp <= m_alias_table[ alias ].m_Expiration ), "Server using expired alias\n" );
	return m_alias_table[ alias ].m_Id;
}

#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Client::Client( int flags )
: App( flags )
{
	m_send_network_data_task = new Tsk::Task< App > ( send_network_data, *this, 
													  Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_CLIENT_SEND_NETWORK_DATA );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}	// namespace Net


================================================
FILE: Code/Gel/Net/Client/netclnt.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			NET  (Net)												**
**																			**
**	File name:		gel/netclnt.h											**
**																			**
**	Created: 		01/29/01	-	spg										**
**																			**
*****************************************************************************/

#ifndef __NETCLNT_H__
#define __NETCLNT_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Net
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class Client : public App
{
	friend class Manager;

public:
			Client( int flags = 0 );

	bool	ConnectToServer( int ip, unsigned short port );	
	void	ReceiveData( void );	
	void	SendEnqueuedMessages( Conn* conn );
	
#ifdef USE_ALIASES
	// Alias functions
	void	ClearAliasTable( void );
	unsigned char	GetObjectAlias( int obj_id, int cur_time );
	unsigned char	NewObjectAlias( int obj_id, int cur_time, bool expires = true );
	int		GetObjectId( unsigned char alias );
private:
	
	AliasEntry	m_alias_table[ vNUM_ALIASES ];
#endif
    
protected:
	bool	init( void );
	void	deinit( void );

	static	MsgHandlerCode	handle_timestamp;
	static	MsgHandlerCode	handle_connection_refusal;

	struct	sockaddr_in	m_server_address;

};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}	// namespace Net

#endif // __NETCLNT_H__

================================================
FILE: Code/Gel/Net/Dispatch/netdsptch.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Net (OBJ) 												**
**																			**
**	File name:		netdsptch.cpp											**
**																			**
**	Created:		01/29/01	-	spg										**
**																			**
**	Description:	Network Dispatching code								**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

//#define NET_PRINT_MESSAGES 1 
/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Net
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

MsgHandler::MsgHandler( MsgHandlerCode *code, int flags, void *data, int pri )
: Lst::Node< MsgHandler >( this, pri ), m_code( code ), m_data( data ), m_flags( flags )
{   
}

/******************************************************************/
/* Register a handler for a net msg                               */
/*                                                                */
/******************************************************************/

MsgHandler*	Dispatcher::AddHandler( unsigned char net_msg_id, MsgHandlerCode *code, int flags, 
						void *data, int pri )
{	
	MsgHandler* handler;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	handler = new MsgHandler( code, flags, data, pri );

	m_handler_list[net_msg_id].AddNode( handler );

	Mem::Manager::sHandle().PopContext();

	return handler;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Dispatcher::Init( void )
{
	int i;

	for( i = 0; i < MAX_NET_MSG_ID; i++ )
	{
		m_handler_list[i].DestroyAllNodes();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Dispatcher::Deinit( void )
{
	int i;

	

	for( i = 0; i < MAX_NET_MSG_ID; i++ )
	{   
		Lst::Search< MsgHandler > sh;
		MsgHandler* handler, *next;
		
		for( handler = sh.FirstItem( m_handler_list[i] ); handler;
				handler = next )
		{
			next = sh.NextItem();

			delete handler;
		}
	}
}

/******************************************************************/
/* Dispatch messages to their appropriate handlers                */
/*                                                                */
/******************************************************************/

int	Dispatcher::DispatchMsgHandlers( Conn *conn, int flags )
{
	MsgHandlerContext msg_context;
	char msg_data[MAX_STREAM_LENGTH];
	int result;
	char *data;

	Dbg_Assert( conn != NULL );	

	msg_context.m_Conn = conn;
	msg_context.m_App = conn->m_app;
	msg_context.m_PacketFlags = flags;
	msg_context.m_Msg = msg_data;
	msg_context.m_MsgLength = 0;

	data = conn->m_read_buffer;		

	// loop through received data and handle messages
	while( data < conn->m_read_ptr )
	{		
		bool size_known;

        // *** IMPORTANT.  Because of alignment issues, I now copy the data from the byte stream
		// into an aligned message buffer.  Indexing into the buffer and casting to Msg's caused
		// load errors
		memcpy( &msg_context.m_MsgId, data, sizeof( unsigned char ));
		data += sizeof( unsigned char );

		// Check to see if the size of this message type is stored after the msg id
		size_known = (( conn->m_app->GetManager()->GetMessageFlags( msg_context.m_MsgId ) & mMSG_SIZE_UNKNOWN ) == 0 );
		//if( conn->IsRemote())
		//{
			//Dbg_Printf( "Got message %s with size_known of %d\n", conn->m_app->GetManager()->GetMessageName(msg_context.m_MsgId ), size_known);
		//}
		
		if( size_known )
		{
			unsigned short temp;
			memcpy( &temp, data, sizeof( unsigned short ));
			msg_context.m_MsgLength = (unsigned long)temp;

			data += sizeof( unsigned short );		
		}
        
		if( msg_context.m_MsgId == MSG_ID_SEQUENCED )
		{
			unsigned char embedded_msg_id;
			unsigned short embedded_msg_length;
            
			Dbg_Assert( size_known );

			memcpy( &embedded_msg_id, data + 5, sizeof( unsigned char ));
			embedded_msg_length = msg_context.m_MsgLength - Manager::vMSG_SEQ_HEADER_LENGTH;
						
			conn->GetInboundMetrics()->AddMessage( embedded_msg_id, msg_context.m_MsgLength );
#ifdef NET_PRINT_MESSAGES
			if( conn->IsRemote())
			{
				unsigned char group_id;
				unsigned int seq_id;

				memcpy( &group_id, data, sizeof( unsigned char ));
				memcpy( &seq_id, data + 1, sizeof( unsigned int ));
				Dbg_Printf( "(%d) Dispatching Sequenced Message (%d) Length (%d) Emb. Length (%d) : %s. Seq: %d Group: %d\n", 
							conn->m_app->m_FrameCounter,
							embedded_msg_id,
							msg_context.m_MsgLength,
							embedded_msg_length,
							conn->m_app->GetManager()->GetMessageName( embedded_msg_id ),
							seq_id,
							group_id );
			}
#endif NET_PRINT_MESSAGES
		}
		
		// If we know the size, just copy the message into the aligned buffer
		// otherwise, copy the maximum size of a message into the buffer and have the
		// handler tell us the size of the message by changint the context's m_MsgLength member
		if( size_known )
		{
			if( msg_context.m_MsgLength > 0 )
			{
				memcpy( msg_context.m_Msg, data, msg_context.m_MsgLength );
			}
		}
		else
		{
			memcpy( msg_context.m_Msg, data, Manager::vMAX_PACKET_SIZE );
		}

		if( conn->IsForeign())
		{
			msg_context.m_PacketFlags |= mHANDLE_FOREIGN;
		}
		result = DispatchMessage( &msg_context );
		if(	( result == HANDLER_ERROR ) ||
			( result == HANDLER_HALT ))
		{
			// output error indicating mal-formmated data
			Dbg_Printf( "Stream processing halted on message %d\n", msg_context.m_MsgId );
			conn->m_read_ptr = conn->m_read_buffer;
			return HANDLER_HALT;
		}

		// By this time we know the message size, whether it was explicit or implicit
		// so we can do the metrics calculation and the debug printout
		if( msg_context.m_MsgId != MSG_ID_SEQUENCED )
		{
			conn->GetInboundMetrics()->AddMessage( msg_context.m_MsgId, msg_context.m_MsgLength );
#ifdef NET_PRINT_MESSAGES
			/*if( conn->IsRemote())
			{
				Dbg_Printf( "(%d) Dispatching Message (%d) Length (%d) : %s\n", 
							conn->m_app->m_FrameCounter,
							msg_context.m_MsgId,
							msg_context.m_MsgLength,
							conn->m_app->GetManager()->GetMessageName( msg_context.m_MsgId ));
			}*/
#endif
		}

		data += msg_context.m_MsgLength;
	}

	conn->m_read_ptr = conn->m_read_buffer;
	return HANDLER_CONTINUE;
}

/******************************************************************/
/* Dispatch a message to its appropriate handler. Stop if higher  */
/* priority handlers say to                                       */
/******************************************************************/

int	Dispatcher::DispatchMessage( MsgHandlerContext *context )
{
	Lst::Search< MsgHandler > sh;
	MsgHandler *handler, *next;
	int result;

	

	Dbg_Assert( context );

    // If this is a message we want to handle, call the handler
	for( handler = sh.FirstItem( m_handler_list[context->m_MsgId] );
			handler; handler = next )
	{
		next = sh.NextItem();

		if(( handler->m_flags & context->m_PacketFlags ) != context->m_PacketFlags )
		{
			Dbg_Printf( "*** Conn %d packetflag mismatch on id %d : 0x%x 0x%x\n", context->m_Conn->GetHandle(), context->m_MsgId, handler->m_flags, context->m_PacketFlags );
			continue;
		}
		
		context->m_Data = handler->m_data;
		result = handler->m_code( context );
		// Check the result from the handler
		if(	( result == HANDLER_ERROR ) ||
			( result == HANDLER_HALT ) ||
			( result == HANDLER_MSG_DESTROYED ))
		{
			// Stop processing the buffer
			return result;
		}		
		// A higher int handler has chosen to mask this message from the rest
		if( ( result == HANDLER_MSG_DONE ))
		{
			return HANDLER_CONTINUE;
		}
	}

	return HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}	// namespace Net

================================================
FILE: Code/Gel/Net/Handler/nethndlr.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Net (OBJ) 												**
**																			**
**	File name:		net.cpp													**
**																			**
**	Created:		01/29/01	-	spg										**
**																			**
**	Description:	Network Manager code									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

#include 

#include 
#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

//#define DEBUG_MESSAGES

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Net
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/* Ack important messages                               		  */
/*                                                                */
/******************************************************************/

int	handle_ack( MsgHandlerContext *context )
{
	MsgImpLink *msg_imp_link, *next_imp;
	MsgSeqLink *msg_seq_link, *next_seq;
	Lst::Search< MsgImpLink > imp_sh;
	Lst::Search< MsgSeqLink > seq_sh;
	MsgAck *ack_msg;
	QueuedMsg* msg;
	QueuedMsgSeq* msg_seq;
	unsigned int send_time;
		
	

	Dbg_Assert( context );

	ack_msg = (MsgAck*) context->m_Msg;
	send_time = 0;

#ifdef DEBUG_MESSAGES
	if( context->m_Conn->IsRemote())
	{
		Dbg_Printf( "(%d) Conn %d: (%d) Got Ack for Packetstamp %d\n", 
			context->m_App->m_FrameCounter, 
			context->m_Conn->GetHandle(),
			context->m_App->m_Timestamp, 
			ack_msg->m_Packetstamp );
	}
#endif

#ifdef __PLAT_NGPS__
	/*if( context->m_Conn->GetHandle() != ack_msg->m_Handle )
	{
		Lst::Search< Conn > sh;
		Conn* conn;

		for( conn = context->m_App->FirstConnection( &sh ); conn; conn = context->m_App->NextConnection( &sh ))
		{
			Dbg_Printf( "*** Conn %d : %p. Ip: 0x%x Port: %d\n", conn->GetHandle(), conn, conn->GetIP(), conn->GetPort());
		}
	}

	Dbg_MsgAssert( context->m_Conn->GetHandle() == ack_msg->m_Handle, ("*** Different handles! %d %d\n", context->m_Conn->GetHandle(), ack_msg->m_Handle ));*/
#endif

	// Tell the dispatcher the size of this message because it does not know (optimization)
	context->m_MsgLength = sizeof( MsgAck );

	// Don't ack foreign acks. We only even handle them because we need to tell the dispatcher
	// the length of the message
	if( context->m_PacketFlags & mHANDLE_FOREIGN )
	{
	    return HANDLER_MSG_DONE;
	}

	for( msg_imp_link = imp_sh.FirstItem( context->m_Conn->m_important_msg_list ); 
			msg_imp_link; msg_imp_link = next_imp )
	{
		next_imp = imp_sh.NextItem();
		if(	( ack_msg->m_Packetstamp == (unsigned short) msg_imp_link->m_Packetstamp ) &&
			( msg_imp_link->m_Timestamp != 0 ))	// guard against acking packet stamp of zero where zero is the initialized value
		{
			msg = msg_imp_link->m_QMsg;
			msg->m_Ref.Release();
			if( msg->m_Ref.InUse() == false )
			{
				delete msg;
			}
			send_time = msg_imp_link->m_Timestamp;
			delete msg_imp_link;
		}
	}	

	for( msg_seq_link = seq_sh.FirstItem( context->m_Conn->m_sequenced_msg_list ); 
			msg_seq_link; msg_seq_link = next_seq )
	{
		next_seq = seq_sh.NextItem();
		// ACK the message
		if( ( ack_msg->m_Packetstamp == (unsigned short) msg_seq_link->m_Packetstamp ) &&
			( msg_seq_link->m_Timestamp != 0 ))	// guard against acking packet stamp of zero where zero is the initialized value
		{
			msg_seq = msg_seq_link->m_QMsg;
			msg_seq->m_Ref.Release();
			// If this message is no longer being referenced, free it
			if( msg_seq->m_Ref.InUse() == false )
			{
				delete msg_seq;
			}
			send_time = msg_seq_link->m_Timestamp;
			delete msg_seq_link;
		}
	}

	// If we can detect that the connection is starting to take longer to ack
	// our messages then affect his calculated latency so that we don't expect
	// acks back so quickly (which prompts resends )
	if(	( context->m_Conn->IsRemote()) &&
		( context->m_Conn->TestStatus( Conn::mSTATUS_READY )) &&
		( send_time > 0 ))
	{
		unsigned int latency_value;

		latency_value = context->m_App->m_Timestamp - send_time;
		if( latency_value > (unsigned int) context->m_Conn->GetAveLatency())
		{
            if( latency_value > App::MAX_LATENCY )
			{
				latency_value = App::MAX_LATENCY;
			}
			
			context->m_Conn->m_latency_test.InputLatencyValue( latency_value );
		}
	}

	return HANDLER_CONTINUE;
}

/******************************************************************/
/* Aggregate stream messages									  */
/*                                                                */
/******************************************************************/

int	App::handle_stream_messages( MsgHandlerContext *context )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	//Dbg_Printf( "******** Got Stream Message\n" );

	switch( context->m_MsgId )
	{
		case MSG_ID_STREAM_START:
		{
			//Dbg_Printf( "******** Got Stream Start\n" );
			MsgStreamStart* msg;
			StreamLink* str_link;
			StreamDesc* new_stream;
			Lst::Search< StreamLink > str_sh;
			int packet_len;

			msg = (MsgStreamStart*) context->m_Msg;
			
			for( str_link = str_sh.FirstItem( context->m_Conn->m_StreamInList ); str_link; 
					str_link = str_sh.NextItem())
			{
				StreamDesc* desc;

				desc = str_link->m_Desc;
				if( desc->m_StreamId == msg->m_StreamId )
				{
					Mem::Manager::sHandle().PopContext();
					return HANDLER_MSG_DONE;
				}
			}
            
			new_stream = new StreamDesc;
			strcpy( new_stream->m_StreamDesc, msg->m_StreamDesc );
			Dbg_Printf( "StreamDesc: %s\n", new_stream->m_StreamDesc );
			new_stream->m_Size = msg->m_Size;
			new_stream->m_StreamId = msg->m_StreamId;
			new_stream->m_Checksum = msg->m_Checksum;
			new_stream->m_MsgId = msg->m_MsgId;
			new_stream->m_Data = new char[ msg->m_Size ];
			new_stream->m_DataPtr = new_stream->m_Data;
			packet_len = ( msg->m_Size < MAX_STREAM_CHUNK_LENGTH ) ? msg->m_Size : MAX_STREAM_CHUNK_LENGTH;
			memcpy( new_stream->m_Data, msg->m_Data, packet_len );
			new_stream->m_DataPtr += packet_len;
			str_link = new StreamLink( new_stream );
			context->m_Conn->m_StreamInList.AddToTail( str_link );

			if(((unsigned int) new_stream->m_DataPtr - (unsigned int) new_stream->m_Data ) >= (unsigned int) new_stream->m_Size )
			{
				MsgHandlerContext msg_context;
				int result;

				// Shouldn't go over
				Dbg_Assert( ((unsigned int) new_stream->m_DataPtr - (unsigned int) new_stream->m_Data ) == (unsigned int) new_stream->m_Size );

				msg_context.m_Conn = context->m_Conn;
				msg_context.m_App = context->m_App;
				msg_context.m_PacketFlags = 0;
				msg_context.m_MsgId = new_stream->m_MsgId;
				msg_context.m_MsgLength = new_stream->m_Size;
				msg_context.m_Msg = new char[new_stream->m_Size];
				
                memcpy( msg_context.m_Msg, new_stream->m_Data, new_stream->m_Size );

				result = context->m_App->m_Dispatcher.DispatchMessage( &msg_context );
				if( result != HANDLER_MSG_DESTROYED )
				{
					delete str_link->m_Desc;
					delete str_link;
				}

				delete [] msg_context.m_Msg;
				Mem::Manager::sHandle().PopContext();
				return result;
			}
			break;
		}
		
		case MSG_ID_STREAM_DATA:
		{
			MsgStreamData* msg;
			StreamLink* str_link;
			Lst::Search< StreamLink > str_sh;

			msg = (MsgStreamData*) context->m_Msg;

			//Dbg_Printf( "******** Got Stream Data\n" );
			for( str_link = str_sh.FirstItem( context->m_Conn->m_StreamInList ); str_link; 
					str_link = str_sh.NextItem())
			{
				StreamDesc* desc;
				int packet_len;

				desc = str_link->m_Desc;
				if( desc->m_StreamId == msg->m_StreamId )
				{   
					packet_len = context->m_MsgLength - sizeof( int );	// subtract the size of the stream id
					memcpy( desc->m_DataPtr, msg->m_Data, packet_len );
					desc->m_DataPtr += packet_len;

					if(((unsigned int) desc->m_DataPtr - (unsigned int) desc->m_Data ) >= (unsigned int) desc->m_Size )
					{
						MsgHandlerContext msg_context;
						int result;
						uint32 actual_crc;

						// Shouldn't go over
						Dbg_Assert( ((unsigned int) desc->m_DataPtr - (unsigned int) desc->m_Data ) == (unsigned int) desc->m_Size );

						msg_context.m_Conn = context->m_Conn;
						msg_context.m_App = context->m_App;
						msg_context.m_PacketFlags = 0;
						msg_context.m_MsgId = desc->m_MsgId;
						msg_context.m_MsgLength = desc->m_Size;
						msg_context.m_Msg = new char[desc->m_Size];
						
						memcpy( msg_context.m_Msg, desc->m_Data, desc->m_Size );
						actual_crc = Crc::GenerateCRCCaseSensitive( msg_context.m_Msg, desc->m_Size );
						if( actual_crc != desc->m_Checksum )
						{
							msg_context.m_PacketFlags |= mHANDLE_CRC_MISMATCH;
							Dbg_Printf( "CRC MISMATCH!!!\n" );
						}

						Dbg_Printf( "handling stream: %s\n", desc->m_StreamDesc );
						result = context->m_App->m_Dispatcher.DispatchMessage( &msg_context );
						if( result != HANDLER_MSG_DESTROYED )
						{
							delete str_link->m_Desc;
							delete str_link;
						}

						delete [] msg_context.m_Msg;

						Mem::Manager::sHandle().PopContext();
						return result;
					}
				}
			}
			break;
		}
	}

	Mem::Manager::sHandle().PopContext();
	return HANDLER_CONTINUE;
}

/******************************************************************/
/* Put sequenced messages in their queue in order in their group  */
/*                                                                */
/******************************************************************/

int	App::handle_sequenced_messages( MsgHandlerContext *context )
{
	MsgSeqLink *msg_link;	
	char* data;
	unsigned short	msg_len;
	QueuedMsgSeq* queued_msg;
	unsigned char group_id, msg_id;	
	unsigned int seq_id;
	Lst::Search< MsgHandler > sh;
	
	

	Dbg_Assert( context );

	data = context->m_Msg;
	memcpy( &group_id, data, sizeof( char ));
	data += sizeof( char );
	memcpy( &seq_id, data, sizeof( unsigned int ));
	data += sizeof (unsigned int );	
		
	// don't accept old sequenced messages
	if( seq_id < context->m_Conn->m_WaitingForSequenceId[ group_id ] )
	{
		return HANDLER_CONTINUE;
	}
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	memcpy( &msg_id, data, sizeof( unsigned char ));
	data += sizeof( unsigned char );

	msg_len = context->m_MsgLength - Manager::vMSG_SEQ_HEADER_LENGTH;
	
	queued_msg = new QueuedMsgSeq( msg_id, msg_len, data, group_id );

	msg_link = new MsgSeqLink( queued_msg );
	msg_link->SetPri( seq_id );
	msg_link->m_Timestamp = 0;
	msg_link->m_Packetstamp = 0;
	msg_link->m_GroupId = group_id;
	msg_link->m_SequenceId = seq_id;
    
	Mem::Manager::sHandle().PopContext();

	// This might be dangerous in some apps, but it makes sense right now.  Don't add
	// this message to the sequenced queue if we dont have a handler for it.
	/*if( sh.FirstItem( context->m_App->m_Dispatcher.m_handler_list[msg_id] ) == NULL )
	{
		Dbg_MsgAssert( 0, ( "No handler for msg %d\n", msg_id ));
		delete msg_link->m_QMsg;
		delete msg_link;	
		return HANDLER_MSG_DONE;
	}*/
		// Don't re-add identical sequenced messages
	if( context->m_Conn->m_SequencedBuffer[group_id].AddUniqueSequence( msg_link ) == false )
	{
	    delete msg_link->m_QMsg;
		delete msg_link;
		return HANDLER_MSG_DONE;
	}

	return HANDLER_CONTINUE;
}

/******************************************************************/
/* Ack the packet immediately when we get it                      */
/*                                                                */
/******************************************************************/

int	Server::handle_timestamp( MsgHandlerContext *context )
{
	MsgDesc msg_desc;
	MsgPacketStamp *time_msg;
	MsgAck ack_msg;
	
	Dbg_Assert( context );

	// Tell the dispatcher the size of this message because it does not know (optimization)
	context->m_MsgLength = sizeof( MsgPacketStamp );

	// Don't ack foreign timestamps. We only even handle them because we need to tell the dispatcher
	// the length of the message
	if( context->m_PacketFlags & mHANDLE_FOREIGN )
	{
		return HANDLER_MSG_DONE;
	}
	
	time_msg = (MsgPacketStamp *) context->m_Msg;	
	ack_msg.m_Packetstamp = time_msg->m_Packetstamp;
	//ack_msg.m_Handle = time_msg->m_Handle;

#ifdef DEBUG_MESSAGES
	if( context->m_Conn->IsRemote())
	{
		Dbg_Printf( "(%d) Conn %d: (%d) Got Packetstamp %d\n", 
			context->m_App->m_FrameCounter, 
			context->m_Conn->GetHandle(),
			context->m_App->m_Timestamp, 
			time_msg->m_Packetstamp );
	}
#endif

	msg_desc.m_Data = &ack_msg;
	msg_desc.m_Length = sizeof( MsgAck );
	msg_desc.m_Id = MSG_ID_ACK;
	context->m_App->EnqueueMessage( context->m_Conn->GetHandle(), &msg_desc );
	
	return HANDLER_CONTINUE;
}

/******************************************************************/
/*     															  */
/*                                                         		  */
/******************************************************************/

int Server::handle_disconn_req( MsgHandlerContext* context )
{
	MsgDesc msg_desc;

#ifdef DEBUG_MESSAGES
	Dbg_Message( "Got disconnection request. Setting connection status to mSTATUS_DISCONNECTING\n" );
#endif
	// Don't re-disconnect someone
	if( context->m_Conn->TestStatus( Conn::mSTATUS_DISCONNECTING ))
	{
		return HANDLER_MSG_DONE;
	}
	// Enqueue a disconn acceptance message before the cutoff point 
	// for enqueuing messages( Conn::mSTATUS_DISCONNECTING )
    msg_desc.m_Id = MSG_ID_DISCONN_ACCEPTED;
	context->m_App->EnqueueMessage( context->m_Conn->GetHandle(), &msg_desc );
	context->m_Conn->SetStatus( Conn::mSTATUS_DISCONNECTING );
	return HANDLER_CONTINUE;
}

/******************************************************************/
/* Handle the timestamp from the server. Check for lost & late    */
/* packets                                                        */
/******************************************************************/

int	Client::handle_timestamp( MsgHandlerContext *context )
{
	MsgPacketStamp *time_msg;
	MsgAck ack_msg;
	MsgDesc msg_desc;

	Dbg_Assert( context );

	// Tell the dispatcher the size of this message because it does not know (optimization)
	context->m_MsgLength = sizeof( MsgPacketStamp );

	// Don't ack foreign timestamps. We only even handle them because we need to tell the dispatcher
	// the length of the message
	if( context->m_PacketFlags & mHANDLE_FOREIGN )
	{
		return HANDLER_MSG_DONE;
	}
    
	time_msg = (MsgPacketStamp *) context->m_Msg;
	ack_msg.m_Packetstamp = time_msg->m_Packetstamp;
	//ack_msg.m_Handle = time_msg->m_Handle;

#ifdef DEBUG_MESSAGES
	if( context->m_Conn->IsRemote())
	{
		Dbg_Printf( "(%d) Conn %d: (%d) Got Packetstamp %d\n", 
			context->m_App->m_FrameCounter, 
			context->m_Conn->GetHandle(),
			context->m_App->m_Timestamp, 
			time_msg->m_Packetstamp );
	}
#endif
	
	msg_desc.m_Data = &ack_msg;
	msg_desc.m_Length = sizeof( MsgAck );
	msg_desc.m_Id = MSG_ID_ACK;
	context->m_App->EnqueueMessageToServer( &msg_desc );

	// Todo : Change this logic as packetstamps DO wraparound
	// if == 0, we just connected : accept first timestamp unconditionally
	if( context->m_Conn->m_latest_packet_stamp == 0 )
	{
		context->m_Conn->m_latest_packet_stamp = time_msg->m_Packetstamp;
	}
	else
	{
		// Check for late packets, but also make sure it's actually late, and not just wraparound
		if( time_msg->m_Packetstamp < context->m_Conn->m_latest_packet_stamp )
		{
			if(( context->m_Conn->m_latest_packet_stamp - time_msg->m_Packetstamp ) > 5000 )
			{
				context->m_Conn->m_latest_packet_stamp = time_msg->m_Packetstamp;
			}
			else
			{
				context->m_App->m_LatePackets++;
				context->m_App->m_LostPackets--;
				context->m_PacketFlags |= mHANDLE_LATE;
			}
		}
		else if( time_msg->m_Packetstamp == context->m_Conn->m_latest_packet_stamp )
		{
			context->m_App->m_DupePackets++;
			Dbg_Printf( "**** Got Duplicate Packet: %d\n", context->m_App->m_DupePackets ); 
			return HANDLER_ERROR;	// don't process dupes
		}
		else if(( time_msg->m_Packetstamp - context->m_Conn->m_latest_packet_stamp ) > 1 )
		{
			// Check for lost packets and make sure it's not just wraparound
			if(( time_msg->m_Packetstamp - context->m_Conn->m_latest_packet_stamp ) > 5000 )
			{
				context->m_App->m_LatePackets++;
				context->m_App->m_LostPackets--;
				context->m_PacketFlags |= mHANDLE_LATE;
			}
			else
			{
				context->m_App->m_LostPackets += 
						(( time_msg->m_Packetstamp - context->m_Conn->m_latest_packet_stamp ) - 1 );
				context->m_Conn->m_latest_packet_stamp = time_msg->m_Packetstamp;  
			}
		}
		else
		{
			context->m_Conn->m_latest_packet_stamp = time_msg->m_Packetstamp;  
		}
	}

	return HANDLER_CONTINUE;
}

/******************************************************************/
/* Respond to ping test                                           */
/*                                                                */
/******************************************************************/

int	handle_latency_test( MsgHandlerContext *context )
{
	MsgDesc msg_desc;
	MsgTimestamp latency_response_msg;
    MsgTimestamp *time_msg;
	Conn *conn;

	Dbg_Assert( context );
    
	time_msg = (MsgTimestamp *) context->m_Msg;
	conn = context->m_Conn;
    
	// "Pong" back to the ping
	latency_response_msg.m_Timestamp = time_msg->m_Timestamp;

	msg_desc.m_Data = &latency_response_msg;
	msg_desc.m_Length = sizeof( MsgTimestamp );
	msg_desc.m_Id = MSG_ID_PING_RESPONSE;
	context->m_App->EnqueueMessage( conn->GetHandle(), &msg_desc );   
    
	return HANDLER_CONTINUE;
}

/******************************************************************/
/* Take note of latency value                                     */
/*                                                                */
/******************************************************************/

int	handle_latency_response( MsgHandlerContext *context )
{
	MsgTimestamp *time_msg;
	Conn *conn;
	int cur_time;

	Dbg_Assert( context );

	time_msg = (MsgTimestamp *) context->m_Msg;
	conn = context->m_Conn;

	if( conn->IsLocal())
	{
		return HANDLER_CONTINUE;
	}
	
	cur_time = context->m_App->m_Timestamp;

	// if it's the one we're waiting for
	if( time_msg->m_Timestamp == conn->m_latency_test.m_SendTime )
	{   
		unsigned int latency_value;

		conn->m_latency_test.m_ReceiveTime = time_msg->m_Timestamp;
		latency_value = ( cur_time - time_msg->m_Timestamp );
		if( latency_value > App::MAX_LATENCY )
		{
			latency_value = App::MAX_LATENCY;
		}
		conn->m_latency_test.InputLatencyValue( latency_value );		
	}

	return HANDLER_CONTINUE;
}

/******************************************************************/
/* We've been accepted to the server                              */
/*                                                                */
/******************************************************************/

int	App::handle_connection_accepted( MsgHandlerContext *context )
{
	
#ifdef DEBUG_MESSAGES
	Dbg_Message( "Connection was accepted!\n" );
#endif
	return HANDLER_CONTINUE;
}

/******************************************************************/
/* The server rejected our connection request (app_level)         */
/*                                                                */
/******************************************************************/

int	Client::handle_connection_refusal( MsgHandlerContext *context )
{
	Client *client;

	

	Dbg_Assert( context );

	client = (Client *) context->m_App;
	// disconnect
	//client->ConnectToServer( INADDR_ANY, 0 );
	client->TerminateConnection( context->m_Conn );

	return HANDLER_HALT;
}

/******************************************************************/
/* Someone is requesting connection (app-level)                   */
/*                                                                */
/******************************************************************/

int	App::handle_connection_request( MsgHandlerContext *context )
{
	MsgDesc msg_desc;

	Dbg_Assert( context );

#ifdef DEBUG_MESSAGES
	Dbg_Message( "Got Connection Request!\n" );
#endif

	// Redundancy check
    if( context->m_PacketFlags & mHANDLE_FOREIGN )
	{
		int ip;

		if( context->m_App->AcceptsForeignConnections() == false )
		{
			return HANDLER_MSG_DONE;
		}

		ip = context->m_Conn->GetIP();
        
#ifndef __PLAT_NGC__
		/*Dbg_Message( "Accepting connection from IP:%s Port:%d\n", 
					 inet_ntoa( *(struct in_addr *) &ip), 
					 context->m_Conn->GetPort());*/
#endif		// __PLAT_NGC__
		context->m_App->SendMessageTo( MSG_ID_CONNECTION_ACCEPTED, 0, NULL,
									   context->m_Conn->GetIP(), context->m_Conn->GetPort(), 0 );

		return HANDLER_CONTINUE;
	}
	else if( context->m_Conn->IsLocal())
	{
		msg_desc.m_Id = MSG_ID_CONNECTION_ACCEPTED;
		msg_desc.m_Priority = HIGHEST_PRIORITY;
		msg_desc.m_Queue = QUEUE_IMPORTANT;
		context->m_App->EnqueueMessage( context->m_Conn->GetHandle(), &msg_desc );

		return HANDLER_CONTINUE;
	}
	
	return HANDLER_MSG_DONE;
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}	// namespace Net


================================================
FILE: Code/Gel/Net/Server/netserv.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Net (OBJ) 												**
**																			**
**	File name:		net.cpp													**
**																			**
**	Created:		01/29/01	-	spg										**
**																			**
**	Description:	Network Manager code									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 
#include 

#include 
#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Net
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

bool	Server::init( void )
{
	m_Timestamp = 0;
	m_connected = false;
	m_max_connections = 0;

	if( App::init() == false )
	{
		return false;
	}
    
#ifdef __PLAT_XBOX__
	//DWORD dwStatus = XNetGetEthernetLinkStatus();
	//if( dwStatus == 0 )
	//{
		//return false;
	//}

	// Get host XNADDR (asynchronous).
	while( XNetGetTitleXnAddr/*XNetGetDebugXnAddr*/( &m_XboxAddr ) == XNET_GET_XNADDR_PENDING )
	{
		// Can do other work/rendering here ...
	}

	// Create session key and register the session with the SNL.
	XNetCreateKey( &m_XboxKeyId, &m_XboxKey );
	XNetRegisterKey( &m_XboxKeyId, &m_XboxKey );	// != NO_ERROR	   
#endif

	m_Dispatcher.AddHandler( MSG_ID_CONNECTION_REQ, handle_connection_request, 
							 mHANDLE_FOREIGN | mHANDLE_LATE, 
							 NULL, 
							 HIGHEST_PRIORITY );
	m_Dispatcher.AddHandler( MSG_ID_TIMESTAMP, handle_timestamp, mHANDLE_LATE | mHANDLE_FOREIGN );
	m_Dispatcher.AddHandler( MSG_ID_DISCONN_REQ, handle_disconn_req,mHANDLE_LATE,
								NULL, HIGHEST_PRIORITY );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Server::deinit( void )
{
#ifdef USE_ALIASES
	int i;

	for( i = 0; i < vNUM_ALIASES; i++ )
	{
		if( m_alias_table[i] )
		{
			delete [] m_alias_table[i];
		}
	}
#endif

	App::deinit();

#ifdef __PLAT_XBOX__
	XNetUnregisterKey( &m_XboxKeyId );
#endif
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/* Server's send function                                         */
/*                                                                */
/******************************************************************/

void	Server::SendEnqueuedMessages( Conn* conn )
{
	int buf_len;
	MsgPacketStamp msg;
	int num_resends;
	MsgDesc timestamp_msg_desc;
	bool buffer_full;
	
	// Should we send this frame?
	if(	ShouldSendThisFrame( conn ) == false )
	{
		return;
	}

	buffer_full = false;
	while( !buffer_full && MessagesToSend( conn ) && !BandwidthExceeded( conn ))
	{    
		// Send Latency Tests/Responses if applicable
		if( ( conn->IsRemote()) &&
			( conn->IsForeign() == false ) &&
			( m_flags.TestMask( App::mDYNAMIC_RESEND )))
		{
			MsgDesc msg_desc;
			MsgTimestamp latency_msg;
			unsigned int cur_time;
	
			cur_time = m_Timestamp;
			latency_msg.m_Timestamp = cur_time;
			// send out a new latency test, keeping track of the time at which
			// we sent it
			if(	( conn->m_latency_test.m_SendTime == 0 ) ||
				( ( cur_time - conn->m_latency_test.m_SendTime ) > App::MAX_LATENCY ))
			{   
				// If we never got a response, simulate an increased latency
				if( conn->m_latency_test.m_SendTime > conn->m_latency_test.m_ReceiveTime )
				{
					unsigned int latency_value;
	
					latency_value = conn->GetAveLatency();
					latency_value += 100;
	
					if( latency_value > App::MAX_LATENCY )
					{
						latency_value = App::MAX_LATENCY;
					}
					conn->m_latency_test.InputLatencyValue( latency_value );
				}
#ifdef __PLAT_NGPS__
				SignalSema( m_send_semaphore_id );
#endif
				conn->m_latency_test.m_SendTime = cur_time;
	
				msg_desc.m_Data = &latency_msg;
				msg_desc.m_Length = sizeof( MsgTimestamp );
				msg_desc.m_Id = MSG_ID_PING_TEST;
				EnqueueMessage( conn->GetHandle(), &msg_desc );
#ifdef __PLAT_NGPS__
				WaitSema( m_send_semaphore_id );
#endif
			}
		}	
    
#ifdef __PLAT_NGPS__
		SignalSema( m_send_semaphore_id );
#endif
	
		msg.m_Packetstamp = (unsigned short) conn->m_latest_sent_packet_stamp;
		//msg.m_Handle = conn->GetHandle();
	
		timestamp_msg_desc.m_Data = &msg;
		timestamp_msg_desc.m_Length = sizeof( MsgPacketStamp );
		timestamp_msg_desc.m_Id = MSG_ID_TIMESTAMP;
		timestamp_msg_desc.m_Priority = HIGHEST_PRIORITY;
		EnqueueMessage( conn->GetHandle(), ×tamp_msg_desc );

#ifdef __PLAT_NGPS__
		WaitSema( m_send_semaphore_id );
#endif

		num_resends = conn->GetNumResends();

		if( !BuildMsgStream( conn, QUEUE_DEFAULT ))
		{
			buffer_full = true;
		}
		// First, use up our bandwidth with re-sends or else they might remain un-sent
		// indefinitely, causing a bad backup on the client if he's waiting on 
		// one particular sequence
		if( !BuildMsgStream( conn, QUEUE_SEQUENCED, true ))
		{
			buffer_full = true;
		}
		if( !BuildMsgStream( conn, QUEUE_IMPORTANT, true ))
		{
			buffer_full = true;
		}
		if( !BuildMsgStream( conn, QUEUE_SEQUENCED ))
		{
			buffer_full = true;
		}
		if( !BuildMsgStream( conn, QUEUE_IMPORTANT ))
		{
			buffer_full = true;
		}

		// If we had to resend some messages this frame, affect the latency for this client
		// so that we don't expect so much from their connection.
		// Also, reduce their estimated bandwidth
		if( conn->GetNumResends() > num_resends )
		{
			conn->m_latency_test.InputLatencyValue( App::MAX_LATENCY );
			conn->SetBandwidth( conn->GetBandwidth() - 500 );
			// Make sure that we'll still be able to send at least one large (max-size) packet.
			// Otherwise, we might get deadlocked.
			if( conn->GetBandwidth() < ( MAX_UDP_PACKET_SIZE + vUDP_PACKET_OVERHEAD ))
			{
				conn->SetBandwidth( MAX_UDP_PACKET_SIZE + vUDP_PACKET_OVERHEAD );
			}
		}

		buf_len = conn->m_write_ptr - conn->m_write_buffer;
		// If there is data to send
		if( ( buf_len > 0 ) &&
			( conn->IsRemote()))
		{
#ifdef NET_DEBUG_MESSAGES
			/*Dbg_Printf( "(%d) Conn %d: Sending pstamp %d at time %d\n", 
							m_FrameCounter, 
							conn->GetHandle(),
							conn->m_latest_sent_packet_stamp,
							m_Timestamp );*/
#endif																	
			if ( SendTo( conn->GetIP(), conn->GetPort(), conn->m_write_buffer, buf_len, 0 ))
			{
				m_TotalBytesOut += buf_len;
				conn->GetOutboundMetrics()->AddPacket( buf_len + vUDP_PACKET_OVERHEAD, m_Timestamp );
				conn->SetForceSendThisFrame( false );
				conn->m_last_send_time = m_Timestamp;
			}
			else
			{
				// If it didn't send the messages properly, flag them for resend next frame
				conn->FlagMessagesForResending( conn->m_latest_sent_packet_stamp );
				conn->m_write_ptr = conn->m_write_buffer;		
				break;
			}
			
			conn->m_write_ptr = conn->m_write_buffer;		
		}

		conn->m_latest_sent_packet_stamp++;
	}

	// If we're successfully sending data at/near the connection's estimated max bandwidth and there
	// are no problems, try ramping up the estimated bandwidth for this connection
	if( BandwidthExceeded( conn ) && ( conn->GetNumResends() == 0 ))
	{
		conn->SetBandwidth( conn->GetBandwidth() + 1000 );
	}
}

/******************************************************************/
/* Server's Receive function                                      */
/*                                                                */
/******************************************************************/

void	Server::ReceiveData( void )
{
	Conn *conn;
	struct sockaddr from_address;
	struct sockaddr_in *client_address;
	int num_bytes, actual_data_len;
    
#ifdef __PLAT_NGC__
	from_address.len = sizeof( sockaddr );
#else
	int addr_len = sizeof( from_address );
#endif

	// Local servers (i.e. non-network play) never really receive. Their data is automatically 
	// "received" when the local client writes its data into the server's receive buffer transparently
	if( IsLocal())
	{
		return;
	}
	
	do
	{
		num_bytes = recvfrom( m_socket, m_in_packet_buffer, 
			Manager::vMAX_PACKET_SIZE, 0, &from_address, &addr_len );
		if ( num_bytes < 0 )// == SOCKET_ERROR ) 
		{
#if defined( __PLAT_WN32__ ) || defined( __PLAT_XBOX__ )
			if( WSAGetLastError() != WSAEWOULDBLOCK )
#else
#ifdef __PLAT_NGPS__
			if( sn_errno( m_socket ) != EWOULDBLOCK )
#else
#ifdef __PLAT_NGC__
			if( num_bytes != SO_EWOULDBLOCK )
#endif
#endif
#endif
			{
#ifdef NET_DEBUG_MESSAGES
				Dbg_Printf( "Server Receive Error: " );
				ReportError();
#endif
			}

			break;
		}
		else
		{   
#ifdef __PLAT_NGPS__
			WaitSema( m_receive_semaphore_id );
#endif	// __PLAT_NGPS__
			m_TotalBytesIn += num_bytes;
			if( m_flags & App::mSECURE )
			{
				actual_data_len = num_bytes - sizeof( unsigned short );	// subtract size of CRC
			}
			else
			{
				actual_data_len = num_bytes;
			}
			
			client_address = (struct sockaddr_in*) &from_address;
			//Dbg_Printf( "**** Received data from client 0x%x : %d, addr_len : %d\n", client_address->sin_addr.s_addr,
						//client_address->sin_port, addr_len );
			conn = GetConnectionByAddress( client_address->sin_addr.s_addr, 
											ntohs( client_address->sin_port ));
			if( conn )
			{
				if( conn->IsValid() || (( m_flags & App::mSECURE ) == 0 ))
				{
					if( ( conn->m_read_ptr + actual_data_len ) < ( conn->m_read_buffer + Conn::vREAD_BUFFER_LENGTH ))
					{
						if( !validate_and_copy_stream( m_in_packet_buffer, conn->m_read_ptr, num_bytes ))
						{
	#ifdef __PLAT_NGPS__
							SignalSema( m_receive_semaphore_id );
	#endif	// __PLAT_NGPS__
							
							// If the game would like to handle this foreign data, allow it to do so now
							if( m_foreign_handler )
							{
								m_foreign_handler( conn->m_read_ptr, num_bytes, &from_address );
							}
							continue;
						}
                        
						conn->m_read_ptr += actual_data_len;
						conn->GetInboundMetrics()->AddPacket( num_bytes + vUDP_PACKET_OVERHEAD, m_Timestamp );
						conn->m_last_comm_time = Tmr::GetTime();
					}
				}
			}
			else
			{
				Conn* foreign_conn;
								
				// Foreign connection. Create temporary connection to process this foreign message
				foreign_conn = NewConnection( client_address->sin_addr.s_addr, 
											  ntohs( client_address->sin_port ), 
											  Conn::mREMOTE |
											  Conn::mFOREIGN );

				if( foreign_conn )
				{
					foreign_conn->m_app = this;
					if( !validate_and_copy_stream( m_in_packet_buffer, foreign_conn->m_read_ptr, num_bytes ))
					{
#ifdef __PLAT_NGPS__
						SignalSema( m_receive_semaphore_id );
#endif	// __PLAT_NGPS__
						// If the game would like to handle this foreign data, allow it to do so now
						if( m_foreign_handler )
						{
							m_foreign_handler( m_in_packet_buffer, num_bytes, &from_address );
						}
						delete foreign_conn;
						continue;
					}
					foreign_conn->m_read_ptr = foreign_conn->m_read_buffer + actual_data_len;
					foreign_conn->Invalidate();
				}
			}
#ifdef __PLAT_NGPS__
			SignalSema( m_receive_semaphore_id );
#endif	// __PLAT_NGPS__
		}       
	} while( num_bytes > 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef USE_ALIASES

void	Server::AllocateAliasTables( void )
{
	int i;

	
    
	Dbg_Assert( m_max_connections > 0 );

	for( i = 0; i < vNUM_ALIASES; i++ )
	{
		m_alias_table[i] = new AliasEntry[ m_max_connections ];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Server::ClearAliasTables( void )
{
	

	int i, j;

	for( i = 0; i < m_max_connections; i++ )
	{
		for( j = 0; j < vNUM_ALIASES; j++ )
		{
			m_alias_table[j][i].m_Id = ~0;
			m_alias_table[j][i].m_Expiration = -1;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Server::ClearAliasTable( int handle )
{
	

	int i;

	for( i = 0; i < vNUM_ALIASES; i++ )
	{
		m_alias_table[i][handle].m_Id = 0;
		m_alias_table[i][handle].m_Expiration = -1;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

unsigned char	Server::GetObjectAlias( int handle, int obj_id, int cur_time )
{
	int hash, i;
	bool quit;

	hash = obj_id & 0xff;	// mask off all but lowest byte and use as a hash into the table
	hash %= vNUM_ALIASES;
	quit = false;
	i = hash;
	do
	{
		if( m_alias_table[i][handle].m_Id == obj_id )
		{
			if(	( m_alias_table[i][handle].m_Expiration == -1 ) ||
				( cur_time > m_alias_table[i][handle].m_Expiration ))
			{   
				return (unsigned char) vNO_ALIAS;
			}
			return (unsigned char) i;
		}
		
		i = ( i + 1 ) % vNUM_ALIASES;
		if( i == hash )	// complete revolution
		{
			break;
		}
	} while( !quit );
    
	return (unsigned char) vNO_ALIAS;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Server::SetObjectAlias( int handle, unsigned char alias, int obj_id, int expiration )
{
	m_alias_table[alias][handle].m_Id = obj_id;
	m_alias_table[alias][handle].m_Expiration = expiration;
}

#endif // USE_ALIASES

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Server::Server( int flags )
: App( flags )
{
	m_send_network_data_task = new Tsk::Task< App > ( send_network_data, *this, 
													  Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_SERVER_SEND_NETWORK_DATA );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}	// namespace Net


================================================
FILE: Code/Gel/Net/Server/netserv.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			NET  (Net)												**
**																			**
**	File name:		gel/net.h												**
**																			**
**	Created: 		01/29/01	-	spg										**
**																			**
*****************************************************************************/

#ifndef __NETSERV_H__
#define __NETSERV_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Net
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class Server : public App
{
	friend class Manager;	
public:
			Server( int flags = 0 );

	void	ReceiveData( void );	
	void	SendEnqueuedMessages( Conn* conn );
		
#ifdef USE_ALIASES
	// Alias Functionality
	void	AllocateAliasTables( void );
	void	ClearAliasTables( void );
	void	ClearAliasTable( int handle );
	unsigned char	GetObjectAlias( int handle, int obj_id, int cur_time );
	void	SetObjectAlias( int handle, unsigned char alias, int obj_id, int expiration );

private:
	AliasEntry*	m_alias_table[vNUM_ALIASES];
#endif

private:
	bool	init( void );
	void	deinit( void );
    
	static	MsgHandlerCode	handle_timestamp;		
	static	MsgHandlerCode	handle_disconn_req;

	AliasEntry*	m_alias_table[vNUM_ALIASES];
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}	// namespace Net

#endif // __NETSERV_H__

================================================
FILE: Code/Gel/Net/net.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Net (OBJ) 												**
**																			**
**	File name:		net.cpp													**
**																			**
**	Created:		01/29/01	-	spg										**
**																			**
**	Description:	Network Manager code									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 

#ifndef __PLAT_WN32__
#include 
#endif

#ifdef __PLAT_XBOX__
//#include 
#include 
#endif

#include 

#ifdef __PLAT_NGPS__

#include 
#include 

#endif
/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

//#define USE_DECI2

#define DEFAULT_SNPS2_SUB_MSK	"255.255.255.0"
#define DEFAULT_SNPS2_GATEWAY	"192.168.0.1"

const char  spduartArgs[]   = "-nogci\0dial=cdrom0:\\IOP\\DIAL_SPD.CNF;1";

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/



namespace Net
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "Network Manager" )

#ifdef __PLAT_NGPS__
/* Set up the following list with one or more suitable DNS servers   */

/*static const sn_char* dns_servers[] =
{   
	    "205.147.0.100",
		"205.147.0.102",
		""                // List is terminated by null string
};*/
static sn_char custom_built_script[SN_MAX_SCRIPT_LINES][SN_MAX_SCRIPT_LEN+1];
static const char *s_custom_isp_script[] =
    {
        "input 30 ogin:",      /* Line 0 */
        "output ",             /* Line 1 */
        "input 10 word:",      /* Line 2 */
        "output ",             /* Line 3 */
        "input 10 ing PPP",    /* Line 4 */
        ""                     /* script is terminated with null string     */
    };

#define vCUSTOM_USERNAME_LINE  1
#define vCUSTOM_PASSWORD_LINE  3

static bool s_cancel_dialup_conn;
static int  s_conn_semaphore;
#endif // __PLAT_NGPS__

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

#ifdef __PLAT_NGPS__

#define vPOWEROFF_STACK_SIZE		(2 * 1024)

static u_char s_poweroff_stack[vPOWEROFF_STACK_SIZE] __attribute__ ((aligned(16)));

/******************************************************************/
/* The thread which waits for			                                  					  */
/*                                                                */
/******************************************************************/

static void s_power_off_thread(void *arg) 
{
	int sid = (int)arg;
	int stat;
	while( 1 ) 
	{
		WaitSema(sid);
		// dev9 power off, need to power off PS2
		//while( sceDevctl("dev9x:", DDIOC_OFF, NULL, 0, NULL, 0 ) < 0 );
		while( sceDevctl("dev9x:", DDIOC_OFF, NULL, 0, NULL, 0 ) < 0 );
		// PS2 power off
		while( !sceCdPowerOff( &stat ) || stat );
    }
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

static void s_power_off_handler(void *arg) 
{
	int sid = (int)arg;
    iSignalSema(sid);
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

static void s_prepare_power_off(void) 
{
    struct ThreadParam tparam;
    struct SemaParam   sparam;
    int tid, sid;

	sparam.initCount = 0;
    sparam.maxCount  = 1;
    sparam.option    = 0;
    sid = CreateSema(&sparam);

    tparam.stackSize = vPOWEROFF_STACK_SIZE;
    tparam.gpReg = &_gp;
    tparam.entry = s_power_off_thread;
    tparam.stack = (void *) s_poweroff_stack;
    tparam.initPriority = 1;
    tid = CreateThread( &tparam );
    StartThread( tid, (void *) sid );

    sceCdPOffCallback( s_power_off_handler, (void *) sid );
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

static sn_int32 set_host_name( sn_char* host_name )
{
    sndev_set_dhost_type optval;
    sn_int32 host_size, r;

	

	Dbg_Assert( host_name );

    host_size = strlen(host_name) + 1;

    // Check host_name isn't too big
    if (host_size > (sn_int32) sizeof(optval.host))
    {
        Dbg_Printf("EE:set_host_name():error host_name too big\n");
        return -1;
    }

    // I'm going to fill optval with zeros to start with, not strictly
    // necessary but this is only an example.
    memset(&optval,0,sizeof(optval));

    // Fill in the optval structure
    optval.flags = 0;        // Steve, you could set one of these two
                             // values SN_DFLAG_EXCL_DIS or
                             // SN_DFLAG_EXCL_REQ in the flags to
                             // *exclude* either the discovery or
                             // request message from the operation with
                             // flags set to zero the option will be
                             // applied to both messages

	if( host_name[0] == '\0' )
	{
		Dbg_Printf( "Clearing Host Name Option\n" );
		optval.clear_option = 1; // Steve, you would set this to 1 if you
								 // wanted to remove the host name
	}
	else
	{
		Dbg_Printf( "Setting Host Name Option to %s\n", host_name );
		optval.clear_option = 0;
	}
    

    optval.include_null = 0; // Steve, if you wanted the null terminator
                             // included in the host name that's sent in
                             // the DHCP msg you would set this to 1

    optval.reserved = 0;    // Must be 0

    memcpy(optval.host, host_name, host_size);

    // Send the option to the IOP

    r = sndev_set_options(0, SN_DEV_SET_DHOST, &optval, sizeof(optval));

    if (r != 0)
    {
        Dbg_Printf("EE:sndev_set_options():error %d\n",r);
        return -1;
    }

    return 0;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

bool	Manager::stop_stack( void )
{
	

	int result, stack_state;

	Dbg_Printf( "EE:Stopping the TCP/IP stack\n" );
	
	result = sn_stack_state(SN_STACK_STATE_STOP, &stack_state);

	if( result != 0 )
	{
		Dbg_Printf( "EE:sn_stack_sate() failed %d\n", result );
		SetError( vRES_ERROR_GENERAL );
		return false;
	}

	Dbg_Printf( "EE:Stack Stopped\n" );

	return true;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

bool	Manager::start_stack( void )
{
	sn_int32	result, stack_state;
	Tmr::Time 	start_time;

	

	// Start the stack
    Dbg_Printf("EE:Starting the TCP/IP stack\n");

    result = sn_stack_state( SN_STACK_STATE_START, &stack_state );
    
    if( result != 0 )
    {
        Dbg_Printf( "EE:sn_stack_sate() failed %d\n", result );
		SetError( vRES_ERROR_GENERAL );
        return false;
    }

	Dbg_Printf( "EE:Stack Started\n" );
	if(( GetConnectionType() == vCONN_TYPE_MODEM ) ||
	   ( GetConnectionType() == vCONN_TYPE_PPPOE ))
	{
		int modem_state, prev_modem_state;
		Tmr::Time start_time;

        modem_state      = -1; // Invalid
		prev_modem_state = -2; // Ivalid and != modem_state

		start_time = Tmr::GetTime();
		Dbg_Printf( "EE:Calling snmdm_get_state() - until modem ready\n" );
		while( modem_state != SN_MODEM_READY )
		{
			result = snmdm_get_state( &modem_state );
	
			if (result != 0)
			{
				Dbg_Printf("EE:snmdm_get_state() failed %d\n", result);
				SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
				return false;
			}
	
			if (modem_state == prev_modem_state)
			{
				sn_delay(10);
			}
			else
			{
				prev_modem_state = modem_state;
				Dbg_Printf("  Modem state = %d %s\n", modem_state, sntc_str_modem_state( modem_state ));
			}

			// After 5 seconds, time out and say there was no dialtone
			if(( Tmr::GetTime() - start_time ) > Tmr::Seconds( 10 ))
			{
				SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
				
				return false;
			}
		}

		Dbg_Printf( "EE:Ready\n" );
	}


    // Wait for the stack to come up
    Dbg_Printf( "EE:Waiting for socket API to be ready\n" );
	start_time = Tmr::GetTime();
    while( sn_socket_api_ready() == SN_FALSE )
	{
		// Delay to avoid hogging the processor
		sn_delay( 500 );
		if(( Tmr::GetTime() - start_time ) > Tmr::Seconds( 10 ))
		{
			Dbg_Printf( "EE:Timed out waiting for socket API to be ready\n" );
			return false;
		}
    }

	if( ShouldUseDHCP())
	{
		struct hostent* hentp       = NULL;
		sn_bool         got_ip_addr = SN_FALSE;
		struct in_addr  ip_addr;
	
		Dbg_Printf( "EE:Waiting for DHCP server to supply IP addr etc\n" );
		start_time = Tmr::GetTime();
		do
		{
			// A way of getting the local IP address
			hentp = gethostbyname(LOCAL_NAME);
	
			if(( hentp != NULL ) && ( hentp->h_addr_list[0] != NULL ))
			{
				// Read the IP address from the hostent struct
				memcpy( &ip_addr,hentp->h_addr_list[0], sizeof( ip_addr ));
	
				if( ip_addr.s_addr != 0 )
				{
					got_ip_addr = SN_TRUE;
					Dbg_Printf( "DHCP allocated IP addr %s\n", inet_ntoa( ip_addr ));
				}
			}
	
			// Delay to avoid hogging the processor
			if( got_ip_addr == SN_FALSE )
			{
				sn_delay(500);
			}
			if(( Tmr::GetTime() - start_time ) > Tmr::Seconds( 10 ))
			{
				Dbg_Printf( "EE:Timed out waiting for DHCP response\n" );
				SetError( vRES_ERROR_DHCP );
				return false;
			}
		} while( got_ip_addr == SN_FALSE );

		strcpy( m_local_ip, inet_ntoa( ip_addr ));
    }

	return true;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

bool	Manager::setup_ethernet_params( void )
{
	sndev_set_ether_ip_type params;
	int result;

    if( ShouldUseDHCP())
    {
		Dbg_Printf( "\n\n\n*********************** USING DHCP ******************* \n\n\n" );
        memset( ¶ms,0, sizeof( params ));
    }
    else
    {   
        inet_aton( GetLocalIP(), (struct in_addr*) ¶ms.ip_addr );
		Dbg_Printf( "======================== Ip is %d : %d\n", params.ip_addr, htonl( params.ip_addr ));
        inet_aton( GetSubnetMask(), (struct in_addr*) ¶ms.sub_mask );
        inet_aton( GetGateway(), (struct in_addr*) ¶ms.gateway );
	}

    result = sndev_set_options( 0, SN_DEV_SET_ETHER_IP, ¶ms, sizeof(params));

    if( result != 0 )
    {
        Dbg_Printf( "EE:Error sndev_set_options() returned %d\n", result );
		SetError( vRES_ERROR_GENERAL );
        return false;
    }

	if( ShouldUseDHCP())
	{
		result = set_host_name( m_host_name );
		if( result != 0 )
		{   
			SetError( vRES_ERROR_GENERAL );
			return false;
		}
	}

	return true;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

bool	Manager::initialize_device( void )
{
	sn_int32            result;
    sn_int32            device_type;
    sn_int16            idVendor;
    sn_int16            idProduct;
    sn_bool             first_time;
	Tmr::Time 			start_time;
	Tmr::Time			timeout;

    // Initialise the socket API, if fails print error and return
    Dbg_Printf( "EE:Initialising socket API\n" );

    result = sockAPIinit( 6 );
    if(	( result != 0 ) &&
		( result != SN_EALRDYINIT ))
    {
        Dbg_Printf( "EE:sockAPIinit() failed %d\n", result );
		SetError( vRES_ERROR_GENERAL );
        return false;
    }
    
    // If we've already init'd the sockets API, don't re-register this thread
	// just let it run through the rest of setup
	if( result != SN_EALRDYINIT )
	{
		// Register this thread with the socket API
		result = sockAPIregthr();
	
		if (result != 0)
		{
			Dbg_Printf( "EE:sockAPIregthr() failed %d\n", result );
			SetError( vRES_ERROR_GENERAL );
			return false;
		}
	}

    // Now wait for DECI2 'device' to be 'attached'
    device_type = SN_DEV_TYPE_NONE;
    first_time  = SN_TRUE;
	start_time = Tmr::GetTime();
	// It takes longer to init the sony modem
	if( GetDeviceType() == vDEV_TYPE_SONY_MODEM )
	{
		timeout = Tmr::Seconds( 15 );
	}
	else
	{
		timeout = Tmr::Seconds( 10 );
	}
	
    while( device_type == SN_DEV_TYPE_NONE )
    {
        result = sndev_get_attached( 0, &device_type, &idVendor, &idProduct );

        if( result != 0 )
        {
            Dbg_Printf( "EE:sndev_get_attached() failed %d\n", result );
			SetError( vRES_ERROR_GENERAL );
            return false;
        }

        if( device_type == SN_DEV_TYPE_NONE )
        {
            if( first_time == SN_TRUE )
            {
                first_time = SN_FALSE;
                Dbg_Printf( "EE:Waiting for network device to be attached ...\n" );
            }
            sn_delay( 10 );
        }

		if(( Tmr::GetTime() - start_time ) > timeout )
		{
			Dbg_Printf( "EE:Timed out waiting for network device to be attached\n" );
			break;
		}
    }

	switch( device_type )
	{
		case SN_DEV_TYPE_DECI2:
			Dbg_Printf( "Using DECI-2 ethernet emulation\n" );
			break;
		case SN_DEV_TYPE_USB_MODEM:
			Dbg_Printf("EE:USB-Modem Attached (idVendor=0x%04X idProduct=0x%04X)\n",
					((int)idVendor)  & 0xFFFF,
					((int)idProduct) & 0xFFFF);
			break;
		case SN_DEV_TYPE_USB_ETHER:
			Dbg_Printf("EE:USB-Ethernet Attached (idVendor=0x%04X idProduct=0x%04X)\n",
					((int)idVendor)  & 0xFFFF,
					((int)idProduct) & 0xFFFF);
			break;
		default:
		{
			if( idVendor == 0 )
			{
				Dbg_Printf( "Unknown Device %d\n", device_type );
				SetError( vRES_ERROR_UNKNOWN_DEVICE );
			}
			else
			{
				Dbg_Assert( idVendor == 1 );
				SetError( vRES_ERROR_DEVICE_NOT_HOT );
			}
			
			return false;
		}
	}
    
	return true;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

bool	Manager::sn_stack_setup( void )
{
	// Set up the list of DNS servers
	char* dns_servers[3];
	int result, i;
	sn_int32	stack_state;
		
	

	sn_stack_state( SN_STACK_STATE_READ, &stack_state );
	if( stack_state == SN_STACK_STATE_START )
	{
		if( stop_stack() == false )
		{
			return false;
		}
	}
		
	if( GetConnectionType() == vCONN_TYPE_ETHERNET ) 
	{
		if(( setup_ethernet_params() == false ))
		{
			return false;
		}
	}

	for( i = 0; i < 3; i++ )
	{
		dns_servers[i] = m_dns_servers[i];
		Dbg_Printf( "DNS Server %d is %s\n", i, dns_servers[i] );
	}
	result = sntc_set_dns_server_list((const sn_char**) dns_servers);

	if( result != 0 )
	{
		Dbg_Printf( "EE:sntc_set_dns_server_list() failed %d\n", result );
		SetError( vRES_ERROR_GENERAL );
		
		return false;
	}

	if( GetConnectionType() == vCONN_TYPE_PPPOE )
	{
		sndev_set_pppoe_opt_type pppoe;

		// Enable PPPoE
		pppoe.flags = 1;

		Dbg_Printf( "Enabling PPPoE\n" );
		result = sndev_set_options( 0, SN_DEV_SET_PPPOE_OPT, &pppoe, sizeof(pppoe));

		Dbg_Printf( "EE:sndev_set_options(pppoe) returned %d\n", result );
	}
	if(( GetConnectionType() == vCONN_TYPE_MODEM ) ||
	   ( GetConnectionType() == vCONN_TYPE_PPPOE ))
	{
		// Now, we have a valid modem. Try to initialize
		//result = snmdm_set_mdm_init( "AT&F S0=0" );
		result = snmdm_set_mdm_init( "AT&F S0=0 W2" );
	
		if( result != 0 )
		{
			Dbg_Printf( "EE:snmdm_set_mdm_init() failed %d\n", result );
			SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
			
			return false;
		}

		Dbg_Printf( "EE:snmdm_set_mdm_init() worked ok\n" );
	}

    if( start_stack() == false )
	{
		return false;
	}

	m_options_changed = false;
	return true;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

static sn_int32 custom_construct_script( sn_int32 isp_type, sn_char* user_name, 
										 sn_char* password)
{
    sn_int32  line_index;
    sn_bool   copy_done;

    copy_done  = SN_FALSE;
    line_index = 0;

    // Copy the preset script to custom_built_script
    while( copy_done == SN_FALSE )
    {
        // Copy this line of the script
        strcpy( custom_built_script[line_index], s_custom_isp_script[line_index] );

        // If this line was a null string then finished copy
        if( s_custom_isp_script[line_index][0] == 0 )
		{
             copy_done = SN_TRUE;
		}
        else
		{
			line_index++;
		}
    }

    sn_strcat( custom_built_script[vCUSTOM_USERNAME_LINE], user_name );
    sn_strcat( custom_built_script[vCUSTOM_USERNAME_LINE], "\\r" );

    // Concatenate password\r on to line index CUSTOM_PASSWORD_LINE
    sn_strcat( custom_built_script[vCUSTOM_PASSWORD_LINE], password );
    sn_strcat( custom_built_script[vCUSTOM_PASSWORD_LINE], "\\r" );

    return 0; // Success
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

sn_int32 custom_connect_modem(	sn_char*               phone_no,
								sn_int32               isp_type,
								sn_char*               user_name,
								sn_char*               password,
								sn_int32               timeout_secs,
								sntc_mdmstate_callback callback )
{
    sn_int32           result;
    sn_int32           modem_state;
    sn_bool            done_script;
    sn_int32           script_index;
    sn_int32           prev_modem_state;
    sn_bool            connect_started;
    sn_int32           timeout_ms;
    sn_int32           connect_err;
    
    result = snmdm_set_phone_no( phone_no );

    // Check that the above function call worked ok
    if( result != 0 )
    {
		Dbg_Printf( "Failed to set phone number: %d\n", result );
        return SNTC_ERR_MDMAPI;
    }

    // Construct the log in script file (in custom_built_script)
    result = custom_construct_script( isp_type, user_name, password );
    if( result != 0 )
    {
        Dbg_Printf( "Failed to build login script: %d\n", result );
        return SNTC_ERR_BSCRIPT;
    }

    // Store the log in script file via the modem API
    // Send a null string first, this resets the script write ptr to 0,
    // it should already be at 0, but just being defensive.
    result = snmdm_set_script("");
    if( result != 0 )
    {
		Dbg_Printf( "snmdm_set_script() failed: %d\n", result );
        return SNTC_ERR_MDMAPI;
    }

    // Now send the script file to the modem API
    script_index = 0;
    done_script  = SN_FALSE;
    while( done_script == SN_FALSE )
    {
        result = snmdm_set_script( custom_built_script[ script_index ] );
        if( result != 0 )
        {
			Dbg_Printf( "snmdm_set_script() failed: %d\n", result );
            return SNTC_ERR_MDMAPI;
        }

        // Check for null line, which is last line
        if( custom_built_script[script_index][0] == 0 )
		{
			 done_script = SN_TRUE;
		}
		else
		{
			script_index++;
		}
    }

    // Everything is ready, so ask the modem to connect
	result = snmdm_connect();
    if( result != 0 )
    {
		Dbg_Printf( "snmdm_connect() failed: %d\n", result );
        return SNTC_ERR_MDMAPI;
    }

    // Now wait for the modem to become connected
    modem_state      = -1; /* Invalid */
    prev_modem_state = -2; /* Ivalid and != modem_state */
    connect_started  = SN_FALSE;
    timeout_ms       = timeout_secs * 1000;

    while(( modem_state != SN_MODEM_PPP_UP ) &&
		  ( s_cancel_dialup_conn == false ))
    {

        // Get the current state of the modem link
        result = snmdm_get_state(&modem_state);
        if( result != 0 )
        {
			Dbg_Printf( "snmdm_get_state() failed: %d\n", result );
            return SNTC_ERR_MDMAPI;
        }

        // Monitor for modem connection process starting, then if it
        // goes back to ready we know it's failed to connect.
        if( connect_started == SN_FALSE )
        {
            // Any of the following means modem connection started
            if(  (modem_state == SN_MODEM_DIALING)
               ||(modem_state == SN_MODEM_LOGIN)
               ||(modem_state == SN_MODEM_CONNECTED)
               ||(modem_state == SN_MODEM_PPP_UP))
            {
                connect_started = SN_TRUE;
            }
        }
        else
        {
            // Having started the connection, then unless it's in
            // one of the following states it's failed to connect
            if(  (modem_state != SN_MODEM_DIALING)
               &&(modem_state != SN_MODEM_LOGIN)
               &&(modem_state != SN_MODEM_CONNECTED)
               &&(modem_state != SN_MODEM_PPP_UP))
            {
                // Read the reason why the connect failed.
                result = snmdm_get_connect_err( &connect_err );
                if( result != 0 )
                {
					Dbg_Printf( "snmdm_get_connect_err() failed: %d\n", result );
                    return SNTC_ERR_MDMAPI;
                }

                switch( connect_err )
                {
                    case SN_CONERR_BUSY:
                       Dbg_Printf( "Busy\n" );
                       return SNTC_ERR_BUSY;
                    break;

                    case SN_CONERR_NOCARRIER:
                       Dbg_Printf( "No carrier\n" );
                       return SNTC_ERR_NOCARRIER;
                    break;

					case SN_CONERR_NOANSWER:
						Dbg_Printf( "No answer\n" );
						return SNTC_ERR_NOANSWER;
                    break;

					case SN_CONERR_NODIALTONE:
						Dbg_Printf( "No dialtone\n" );
						return SNTC_ERR_NODIALTONE;
                    break;

                    default:
                        Dbg_Printf( "connect started then modem state=sntc_str_modem_state(modem_state)\n" );
                        return SNTC_ERR_CONNECT;
                }
            }
        }

        // Now check whether the modem state has changed since the
        // previous time round this loop, and if so call the user
        // callback function (unless it's NULL), otherwise check
        // for time-out / do a delay
        if( modem_state != prev_modem_state )
        {
            prev_modem_state = modem_state;
            if( callback != NULL )
			{
				(*callback)(modem_state);
			}
        }
        else
        {
            if( timeout_ms <= 0 )
            {
				Dbg_Printf( "Connect timed out in modem state %s\n", sntc_str_modem_state(modem_state) );
                return SNTC_ERR_TIMEOUT;
            }

            sn_delay( 10 );
            timeout_ms -= 10;
        }
    }

    // If we get to here, the modem has successfully connected
    Dbg_Printf( "Connected!\n" );
    
	return 0;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	Manager::threaded_modem_conn( void *data )
{
	
	Manager* man = (Manager *) data;
	int result;
    
	Dbg_Printf( "Registering modem thread %d with stack\n", GetThreadId());
	
	result = sockAPIregthr();
	Dbg_Assert( result == 0 );

	WaitSema( s_conn_semaphore );

	Dbg_Printf("EE:Calling sntc_connect_modem()\n");

	// Set the script write pointer back to zero
    result = snmdm_set_script("");

	// Set up Authentication parameters.
	sndev_set_chap_type ChapOptions = {0};
	
    ChapOptions.accept_chap = man->ShouldUseDialupAuthentication();
	Dbg_Printf( "Setting chap to %d\n", ChapOptions.accept_chap );
	ChapOptions.require_chap = 0;
	strcpy(ChapOptions.locl_name, man->m_isp_user_name );
	strcpy(ChapOptions.locl_secr, man->m_isp_password );
	strcpy(ChapOptions.chal_name,"*"); // Accept any challenge name
	
	Dbg_Printf( "Chap username: %s password: %s\n", man->m_isp_user_name, man->m_isp_password );
	result = sndev_set_options( 0, SN_DEV_SET_CHAP, &ChapOptions, sizeof(ChapOptions));
	Dbg_Printf( "EE:sndev_set_options(SN_DEV_SET_CHAP) returned %d\n", result );

	sndev_set_pap_type PapOptions = {0};

	PapOptions.accept_pap = man->ShouldUseDialupAuthentication();
	Dbg_Printf( "Setting pap to %d\n", PapOptions.accept_pap );
	PapOptions.require_pap = 0;
	strcpy(PapOptions.locl_name, man->m_isp_user_name );
	strcpy(PapOptions.locl_pass, man->m_isp_password );
	Dbg_Printf( "Pap username: %s password: %s\n", man->m_isp_user_name, man->m_isp_password );

	result = sndev_set_options( 0, SN_DEV_SET_PAP, &PapOptions, sizeof(PapOptions));
	Dbg_Printf( "EE:sndev_set_options(SN_DEV_SET_PAP) returned %d\n", result );

	man->m_modem_err = 0;
	if( man->GetConnectionType() == vCONN_TYPE_MODEM )
	{   
		Dbg_Printf( "Dialing %s user: %s pass: %s\n", man->m_isp_phone_no, man->m_isp_user_name, man->m_isp_password );
		result = custom_connect_modem
				 ( man->m_isp_phone_no,			// phone_no
				   SNTC_ISP_GENERIC,		// isp_type
				   man->m_isp_user_name,			// user_name
				   man->m_isp_password,			// password
				   vMODEM_CONNECT_TIMEOUT,	// timeout_secs
				   man->conn_modem_state_callback );// callback
	}
	else if( man->GetConnectionType() == vCONN_TYPE_PPPOE )
	{
		result = custom_connect_modem
				 ( "",			// phone_no
				   0,		// isp_type
				   "",			// user_name
				   "",			// password
				   vMODEM_CONNECT_TIMEOUT,	// timeout_secs
				   man->conn_modem_state_callback );// callback
	}
	// Check whether connection succeeded
	if( result == 0 )
	{
        sn_int32 result, statval, statlen;

		statval = 1234; // so can see it's modified
		statlen = sizeof(statval);

        result = sndev_get_status(0, SN_DEV_STAT_BAUD, &statval, &statlen);
        man->m_modem_baud_rate = statval;
		
		man->m_online = true;
	}
	else
	{   
		sntc_disconnect_modem(	vMODEM_DISCONNECT_TIMEOUT,	// timeout_secs
								NULL,		// callback
								NULL );				// error_message
		man->SetModemState( vMODEM_STATE_ERROR );
		man->m_modem_err = result;
		Dbg_Printf( "EE:sntc_connect_modem() failed: %d\n", result );
		man->m_online = false;
	}   

	Dbg_Printf( "DeRegistering modem thread %d with stack\n", GetThreadId());
	sockAPIderegthr();
	SignalSema( s_conn_semaphore );
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	Manager::threaded_modem_disconn( void *data )
{
	
	Manager* man = (Manager *) data;
	sn_char* err_msg_ptr;
	bool modem_disconnected;
	int result;
    
	Dbg_Printf( "Registering modem thread %d with stack\n", GetThreadId());
	
	result = sockAPIregthr();
	Dbg_Assert( result == 0 );
		
	modem_disconnected = false;
	if( man->m_online )
	{
		Dbg_Printf( "EE:Calling sntc_disconnect_modem()\n" );
		result = sntc_disconnect_modem(	vMODEM_DISCONNECT_TIMEOUT,	// timeout_secs
										disconn_modem_state_callback,		// callback
										&err_msg_ptr );				// error_message
	
		// Check whether disconnection succeeded
		if (result == 0)
		{
			Dbg_Printf("EE:sntc_disconnect_modem() worked:%s\n",err_msg_ptr);
			modem_disconnected = true;
		}
		else
		{
			Dbg_Printf("EE:sntc_disconnect_modem() failed:%s\n",err_msg_ptr);
			modem_disconnected = false;
		}
	}

	// If the modem didn't connect and disconnect ok, then reset it
	if( !modem_disconnected )
	{
		bool modem_reset;

		do
		{
			Dbg_Printf( "EE:Calling sntc_reset_modem()\n" );

			result = sntc_reset_modem
						(	vMODEM_RESET_TIMEOUT,	// timeout_secs
							disconn_modem_state_callback,	// callback
							&err_msg_ptr );     	// error_message

			// Check whether reset succeeded
			if( result == 0 )
			{
				Dbg_Printf( "EE:sntc_reset_modem() worked:%s\n", err_msg_ptr );
				modem_reset = true;
			}
			else
			{
				Dbg_Printf( "EE:sntc_reset_modem() failed:%s\n", err_msg_ptr );
				modem_reset = false;
				// If their modem is no longer plugged in, just consider it "hung up"
				if( result == SNTC_ERR_NOMODEM )
				{
					break;
				}
				sn_delay( 1000 ); // avoid excessive printf if unplugged
			}
		} while( !modem_reset );
	}
	
	man->SetModemState( vMODEM_STATE_DISCONNECTED );
	man->m_online = false;

	Dbg_Printf( "DeRegistering modem thread %d with stack\n", GetThreadId());
	sockAPIderegthr();
}

#endif	// __PLAT_NGPS__

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/* Add logic tasks to the current task list                       */
/*                                                                */
/******************************************************************/

void	Manager::AddLogicTasks( App* app )
{
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
    
	mlp_manager->AddLogicTask( app->GetReceiveDataTask());
	mlp_manager->AddLogicTask( app->GetSendDataTask());
	mlp_manager->AddLogicTask( app->GetProcessDataTask());
	mlp_manager->AddLogicTask( app->GetNetworkMetricsTask());
}

/******************************************************************/
/* Add logic tasks to the push logic task list                    */
/*                                                                */
/******************************************************************/

void	Manager::AddLogicPushTasks( App* app )
{
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();

	app->GetSendDataTask().Remove();
	app->GetReceiveDataTask().Remove();
	app->GetProcessDataTask().Remove();
	app->GetNetworkMetricsTask().Remove();

	mlp_manager->AddLogicPushTask( app->GetReceiveDataTask());
	mlp_manager->AddLogicPushTask( app->GetSendDataTask());
	mlp_manager->AddLogicPushTask( app->GetProcessDataTask());
	mlp_manager->AddLogicPushTask( app->GetNetworkMetricsTask());
}

/******************************************************************/
/* Removes network logic tasks                    				  */
/*                                                                */
/******************************************************************/

void	Manager::RemoveNetworkTasks( App* app )
{
	app->GetSendDataTask().Remove();
	app->GetReceiveDataTask().Remove();
	app->GetProcessDataTask().Remove();
	app->GetNetworkMetricsTask().Remove();
}

/******************************************************************/
/* Creates a new Server at the given address and port			  */
/*                                                                */
/******************************************************************/

Server*	Manager::CreateNewAppServer( int id, char* appName, int max_clients, 
									  unsigned short port, int address, int flags )
{
	Server *new_app;
		
	

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	// If we're using DHCP, we should have gotten our real IP address by now
	// from the DHCP server. Use that address from now on
	if( ShouldUseDHCP())
	{
		address = inet_addr( m_local_ip );		
	}

	new_app = new Server( flags );
	new_app->m_net_man = this;
	new_app->init();
    
	new_app->m_id = id;	
	strncpy( new_app->m_name, appName, MAX_LEN_APP_NAME );
	new_app->m_max_connections = max_clients;
    
	m_net_servers.AddToTail( &new_app->m_node );
	if( new_app->IsLocal())
	{                  
		new_app->m_connected = true;
	}
	else
	{
		new_app->bind_app_socket( address, port );
		if( flags & App::mBROADCAST )
		{
			new_app->m_connected = true;
		}
	}

#ifdef USE_ALIASES
	if( flags & App::mALIAS_SUPPORT )
	{
		new_app->AllocateAliasTables();
		new_app->ClearAliasTables();
	}
#endif
    
	AddLogicTasks( new_app );

#ifndef __PLAT_NGC__
    Dbg_Printf( "Created new: %s server %p, Max Clients: %d, IP: %s, Port: %d", appName, new_app, max_clients,
		   					inet_ntoa( *(struct in_addr*) &address ), port );
#endif		// __PLAT_NGC__

	m_num_apps++;
	
	Mem::Manager::sHandle().PopContext();
	return new_app;
}

/******************************************************************/
/* Creates a new client socket									  */
/*                                                                */
/******************************************************************/

Client	*Manager::CreateNewAppClient( 	int id, char* appName, unsigned short port, int address,
										int flags )
{
	Client *new_app;

	

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	// If we're using DHCP, we should have gotten our real IP address by now
	// from the DHCP server. Use that address from now on
	if( ShouldUseDHCP())
	{
		address = inet_addr( m_local_ip );
	}

	new_app = new Client( flags );
	strncpy( new_app->m_name, appName, MAX_LEN_APP_NAME );
	new_app->m_net_man = this;
    new_app->init();

	new_app->m_id = id; 
	new_app->m_max_connections = 1;
    
	m_net_clients.AddToTail( &new_app->m_node );
	if( !new_app->IsLocal())
	{
#ifndef __PLAT_XBOX__
		new_app->bind_app_socket( address, port );
#endif
	}
	
#ifdef USE_ALIASES
	new_app->ClearAliasTable();
#endif
    
	AddLogicTasks( new_app );

	Dbg_Printf( "Created new: %s client %p on port %d\n", appName, new_app, port );
	m_num_apps++;

	Mem::Manager::sHandle().PopContext();
	return new_app;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::DestroyApp( App *app )
{
	app->m_node.Remove();
	app->ShutDown();
	delete app;
	m_num_apps--;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

Metrics::Metrics( void )
{
	m_num_packets = 0;
	m_total_bytes = 0;
	m_bytes_per_sec = 0;
	memset( m_num_messages, 0, sizeof( int ) * MAX_MSG_IDS );
	memset( m_size_messages, 0, sizeof( int ) * MAX_MSG_IDS );
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	Metrics::CalculateBytesPerSec( int cur_time )
{
	int i, num_bytes;
		
	num_bytes = 0; 
	// Sum up number of bytes transferred over the last second
	for( i = 0; i < vNUM_BUFFERED_PACKETS; i++ )
	{
		if( i >= m_num_packets )
		{
			break;
		}

		if(( cur_time - m_packets[i].GetTime()) < (int) Tmr::Seconds( 1 ))
		{
			num_bytes += m_packets[i].GetNumBytes();
		}
	}

	m_bytes_per_sec = num_bytes;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

int		Metrics::GetBytesPerSec( void )
{
	return m_bytes_per_sec;
}
	
/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

int		Metrics::GetTotalBytes( void )
{
	return m_total_bytes;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

int		Metrics::GetTotalMessageData( int msg_id )
{
	return m_size_messages[ msg_id ];
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

int		Metrics::GetTotalNumMessagesOfId( int msg_id )
{
	return m_num_messages[ msg_id ];
}
	
/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	Metrics::AddPacket( int size, int time )
{
	int index;

	index = m_num_packets % vNUM_BUFFERED_PACKETS;

	m_packets[ index ].SetNumBytes( size );
	m_packets[ index ].SetTime( time );
	m_total_bytes += size;
	m_num_packets++;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	Metrics::AddMessage( int msg_id, int size )
{
	m_num_messages[ msg_id ]++;
	m_size_messages[ msg_id ] += size;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

int		PacketInfo::GetNumBytes( void )
{
	return m_num_bytes;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

int		PacketInfo::GetTime( void )
{
	return m_time;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	PacketInfo::SetNumBytes( int size )
{
	m_num_bytes = size;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/
	
void	PacketInfo::SetTime( int time )
{
	m_time = time;
}

/******************************************************************/
/* Iterator                                                       */
/*                                                                */
/******************************************************************/

Server *Manager::FirstServer( Lst::Search< App > *sh )
{
	

	Dbg_Assert( sh );

	return((Server*) sh->FirstItem( m_net_servers ));
}

/******************************************************************/
/* Iterator                                                       */
/*                                                                */
/******************************************************************/

Client *Manager::FirstClient( Lst::Search< App > *sh )
{
	

	Dbg_Assert( sh );

	return((Client*) sh->FirstItem( m_net_clients ));
}

/******************************************************************/
/* Iterator                                                       */
/*                                                                */
/******************************************************************/

App *Manager::NextApp( Lst::Search< App > *sh )
{
	

	Dbg_Assert( sh );

	return( sh->NextItem());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::NumApps( void )
{
	return m_num_apps;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef __PLAT_NGC__
static	void*    s_so_alloc( u32 name, s32 size )
{
	return Mem::Malloc( size );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static	void     s_so_free( u32 name, void* ptr, s32 size )
{
	Mem::Free( ptr );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::initialize_ngc( void )
{
	int result;
    SOConfig config;

	memset( &config, 0, sizeof( SOConfig ));
	
	config.vendor = SO_VENDOR_NINTENDO;
	config.version = SO_VERSION;
	config.alloc = s_so_alloc;
	config.free = s_so_free;
	if( ShouldUseDHCP())
    {
		config.flag = SO_FLAG_DHCP;
    }
    else
    {   
		config.flag = 0;

        inet_aton( GetLocalIP(), (struct in_addr*) &config.addr );
        inet_aton( GetSubnetMask(), (struct in_addr*) &config.netmask );
        inet_aton( GetGateway(), (struct in_addr*) &config.router );
		inet_aton( GetDNSServer( 0 ), (struct in_addr*) &config.dns1 );
		inet_aton( GetDNSServer( 1 ), (struct in_addr*) &config.dns2 );
	}

	result = SOStartup( &config );
	if( result != 0 )
	{
		SOCleanup();
		return false;
	}

	return true;
}

#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#ifdef __PLAT_NGPS__

bool	Manager::load_irx_files( void )
{

//////////////////////////////
#if 0	// MOVED TO SIOMAN.CPP

	int result;

	// Load the stack IRX file
#ifdef __NOPT_DEBUG__
	result = SIO::LoadIRX( "SNSTKDBG" );
#else
	result = SIO::LoadIRX( "SNSTKREL" );
#endif

    if( result < 0 )
    {
		Dbg_MsgAssert( 0,( "EE:Can't load module snstkrel/dbg.irx. Error : %d\n", result ));
		SetError( vRES_ERROR_INVALID_IRX );
		
		return false;
	}
#endif	
//////////////////////////////



#ifdef USE_DECI2
    // Load the DECI2 driver IRX file
    SIO::LoadIRX( "sndrv000" );
#else

	switch( GetConnectionType())
	{
		case vCONN_TYPE_PPPOE:
		{
			switch( GetDeviceType())
			{
				case vDEV_TYPE_USB_ETHERNET:
				{
					// Load the PPPoE Driver IRX file
					if( SIO::LoadIRX( "sndrv200" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					// Load the USB-Ethernet Driver IRX file
					if( SIO::LoadIRX( "sndrv201" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					break;
				}
				case vDEV_TYPE_PC_ETHERNET:
				{
					// Load the PPPoE Driver IRX file
					if( SIO::LoadIRX( "sndrv200" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					// Load the SN Wrapper (PPPoE variant) for Sony Ether
					if( SIO::LoadIRX( "sndrv202" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					} 
					// Load the Sony pcmcia irx
					if( SIO::LoadIRX( "dev9" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					s_prepare_power_off();
					// Load the Sony Ethernet driver IRX file
					if( SIO::LoadIRX( "smap", 0, NULL, false ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					break;
				}
				default:
					SetError( vRES_ERROR_GENERAL );
					return false;
			}
			break;
		}
		case vCONN_TYPE_ETHERNET:
		{
			switch( GetDeviceType())
			{
				case vDEV_TYPE_USB_ETHERNET:
				{
					// Load the USB-Ethernet Driver IRX file
					if( SIO::LoadIRX( "sndrv001" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					break;
				}
				case vDEV_TYPE_PC_ETHERNET:
				{
                    if( SIO::LoadIRX( "sndrv100" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					// Load the Sony pcmcia irx
					if( SIO::LoadIRX( "dev9" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					s_prepare_power_off();
					// Load the Sony Ethernet driver IRX file
					if( SIO::LoadIRX( "smap", 0, NULL, false ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					break;
				}
				default:
					SetError( vRES_ERROR_GENERAL );
					return false;
			}
			break;
		}
		case vCONN_TYPE_MODEM:
		{
			switch( GetDeviceType())
			{
				case vDEV_TYPE_USB_MODEM:
				{
					// Load the USB-Modem Driver IRX file
					if( SIO::LoadIRX( "sndrv002" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					break;
				}

				case vDEV_TYPE_SONY_MODEM:
				{
					if (Config::PAL())
					{
						// Sony Modem Not Supported in PAL territories
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					
					// Load the SN modem wrapper irx
					if( SIO::LoadIRX( "sndrv101" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					// Load the Sony pcmcia irx
					if( SIO::LoadIRX( "dev9" ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					s_prepare_power_off();
                    // Load the Sony modem driver irx
					if( SIO::LoadIRX( "spduart", sizeof(spduartArgs),  (char*) spduartArgs ) < 0 )
					{
						SetError( vRES_ERROR_DEVICE_NOT_CONNECTED );
						return false;
					}
					break;
				}
				default:
					SetError( vRES_ERROR_GENERAL );
					return false;
			}
			break;
		}
		default:
			// No valid device specified
			SetError( vRES_ERROR_GENERAL );
			return false;
	}
	
#endif	// USE_DECI2

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::initialize_ps2( void )
{
	
	
	bool result;

	Dbg_Printf( "initializing PS2\n" );
    
    if( m_net_drivers_loaded == false )
	{   
		bool success;

		File::StopStreaming( );
		if ( Pcm::UsingCD( ) )
		{
			Dbg_MsgAssert( 0,( "Can't load IRX modules when CD is busy." ));
			return false;
		}
	
		Dbg_Printf( "initializing PS2_2\n" );
		success = load_irx_files();
		m_net_drivers_loaded = true;
		if( success == false )
		{
			return false;
		}
	}
	else
	{
		if( m_device_changed )
		{
			SetError( vRES_ERROR_DEVICE_CHANGED );
			return false;
		}
	}
    
	result = initialize_device();
	if( result == false )
	{
		return false;
	}

	if( !m_stack_setup || m_options_changed )
	{
		m_stack_setup = sn_stack_setup();
	}

	m_device_changed = false;
	return m_stack_setup;
}

#endif	// __PLAT_NGPS__

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::Manager( void )
{	
	int msg_id, i;

#if( defined ( __PLAT_WN32__ ) || defined ( __PLAT_XBOX__ ))
	int err;
	WORD version_required;
	WSADATA wsa_data;

#ifdef __PLAT_XBOX__
	XNetStartupParams xnsp;
	ZeroMemory( &xnsp, sizeof(xnsp) );
	xnsp.cfgSizeOfStruct = sizeof(xnsp);

#ifdef __NOPT_NOASSERTIONS__
	xnsp.cfgFlags = 0;
#else
	xnsp.cfgFlags = XNET_STARTUP_BYPASS_SECURITY;// | XNET_STARTUP_BYPASS_DHCP;
#endif

	err = XNetStartup( &xnsp );
	if( err )
	{
		XNetCleanup();
		return;
	}	
#endif

	version_required = MAKEWORD( 2, 2 );
	if( err = WSAStartup ( version_required, &wsa_data ))
	{
		Dbg_MsgAssert( 0,( "Failed to start WinSock\n" ));
		WSACleanup();	
#ifdef __PLAT_XBOX__
		XNetCleanup();
#endif
		return;
	}	
	if (	( LOBYTE( wsa_data.wVersion ) != 2 ) ||
			( HIBYTE( wsa_data.wVersion ) != 2 ))
	{
		Dbg_MsgAssert( 0,( "Failed to start WinSock\n" ));
		WSACleanup();	
#ifdef __PLAT_XBOX__
		XNetCleanup();
#endif
		return;
	}

#ifdef __PLAT_XBOX__	
#	if 0
	HRESULT hr;
	XONLINE_STARTUP_PARAMS xosp = { 0 };

	hr = XOnlineStartup( &xosp );
	if( FAILED( hr ))
	{
		XOnlineCleanup();
		return;
	}
#	endif
#endif

#else    
#ifdef __PLAT_NGPS__
	ChangeThreadPriority( GetThreadId(), vMAIN_THREAD_PRIORITY );
	m_stack_setup = false;
	m_options_changed = false;
	m_device_changed = false;
	m_net_drivers_loaded = false;
#endif	// __PLAT_NGPS__
#endif	// __PLAT_WN32__

	for( msg_id = 0; msg_id < 255; msg_id++ )
	{
		SetMessageName( msg_id, "" );
		SetMessageFlags( msg_id, 0 );
	}

	SetMessageName( MSG_ID_PING_TEST, "Ping Test" );
	SetMessageName( MSG_ID_PING_RESPONSE, "Ping Response" );
	SetMessageName( MSG_ID_CONNECTION_REQ, "Connection Request" );
	SetMessageName( MSG_ID_CONNECTION_ACCEPTED, "Connection Accepted" );
	SetMessageName( MSG_ID_CONNECTION_REFUSED, "Connection Refused" );
	SetMessageName( MSG_ID_CONNECTION_TERMINATED, "Connection Terminated" );
	SetMessageName( MSG_ID_SEQUENCED, "Sequenced Message" );
	SetMessageName( MSG_ID_ACK, "Ack" );
	SetMessageName( MSG_ID_FIND_SERVER, "Find Server" );
	SetMessageName( MSG_ID_SERVER_RESPONSE, "Server Find Response" );
	SetMessageName( MSG_ID_TIMESTAMP, "Timestamp" );
	SetMessageName( MSG_ID_ALIAS, "New Alias" );
	SetMessageName( MSG_ID_DISCONN_REQ,	"Disconn Request" );
	SetMessageName( MSG_ID_DISCONN_ACCEPTED, "Disconn Accepted" );

	SetMessageFlags( MSG_ID_TIMESTAMP, mMSG_SIZE_UNKNOWN );
	SetMessageFlags( MSG_ID_ACK, mMSG_SIZE_UNKNOWN );

	sprintf( m_local_ip, "" );
	sprintf( m_gateway, DEFAULT_SNPS2_GATEWAY );
	sprintf( m_subnet, DEFAULT_SNPS2_SUB_MSK );
			 
	m_num_apps = 0;
	m_conn_type = vCONN_TYPE_NONE;
	m_use_dhcp = false;
	m_online = false;
	m_use_dialup_auth = false;
	m_last_error = vRES_SUCCESS;
	m_modem_state = vMODEM_STATE_DISCONNECTED;
	m_modem_err = 0;

	for( i = 0; i < 3; i++ )
	{
		m_dns_servers[i][0] = '\0';
	}

#ifdef __PLAT_NGPS__
	m_bandwidth = 4200;	// Default to a 33.6kbps modem's approximate payload threshold (i.e. including packet overhead)
#else
	m_bandwidth = 400000;	// On Xbox, just assume broadband
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
	
#if( defined ( __PLAT_WN32__ ) || defined ( __PLAT_XBOX__ ))
	WSACleanup();	
#endif

#ifdef __PLAT_XBOX__
	XNetCleanup();
#	if 0
	XOnlineCleanup();
#	endif
#endif

#ifdef __PLAT_NGC__
	SOCleanup();
#endif


#ifdef __PLAT_NGPS__
	sn_int32	stack_state, result;
	
	if( m_stack_setup )
	{
		// Stop the stack
		result = sn_stack_state(SN_STACK_STATE_STOP, &stack_state );
		Dbg_MsgAssert( result == 0,( "EE:sn_stack_sate() failed %d\n", result ));
			
		// De-Register this thread with the socket API
		Dbg_Printf( "EE:Calling sockAPIderegthr()\n" );
		sockAPIderegthr();
	}
#endif // __PLAY_NGPS__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#ifdef __PLAT_NGPS__

void Manager::conn_modem_state_callback( sn_int32 modem_state )
{
	
	Net::Manager * net_man = Net::Manager::Instance();
		
	Dbg_Printf("  Modem state %d = %s\n", modem_state, sntc_str_modem_state( modem_state ));
	switch( modem_state )
	{
		case SN_MODEM_READY:
		case SN_MODEM_DIALING:
			net_man->SetModemState( vMODEM_STATE_DIALING );
			
			// If we're using PAP/CHAP, clear the login script. We do it here instead of before
			// sntc_connect_modem() because that call resets to the default login script.
			if( net_man->ShouldUseDialupAuthentication())
			{
				sndev_set_null_scrpt_type clr;
				int result;

				clr.reserved = 0;

				result = sndev_set_options(0, SN_DEV_SET_NULL_SCRPT, &clr, sizeof(clr));
                Dbg_Printf( "EE:sndev_set_options(clr) returned %d\n", result );
			}
			
			Dbg_Printf( "Setting modem state to modem state dialing\n" );
			break;
		case SN_MODEM_LOGIN:
			Dbg_Printf( "Setting modem state to modem state connected\n" );
			net_man->SetModemState( vMODEM_STATE_CONNECTED );
			break;
		case SN_MODEM_PPP_UP:
			Dbg_Printf( "Setting modem state to modem state logged in\n" );
			net_man->SetModemState( vMODEM_STATE_LOGGED_IN );
			break;
		default:
			return;
	};
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::disconn_modem_state_callback( sn_int32 modem_state )
{
	Net::Manager * net_man = Net::Manager::Instance();
    
    Dbg_Printf("  Modem state = %s\n", sntc_str_modem_state( modem_state ));
	switch( modem_state )
	{
		case SN_MODEM_HANGINGUP:
			net_man->SetModemState( vMODEM_STATE_HANGING_UP );
			break;
		default:
			return;
	};
}

#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


bool	Manager::NetworkEnvironmentSetup( void )
{
#ifdef __PLAT_NGPS__
	return initialize_ps2();
#endif
#ifdef __PLAT_NGC__
	return initialize_ngc();
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::NeedToTestNetworkEnvironment( void )
{
#ifdef __PLAT_NGPS__
	if( m_net_drivers_loaded && m_device_changed )
	{
		return true;
	}
	if( !m_stack_setup || m_options_changed )
	{
		return true;
	}
#endif
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ConnectToInternet( void )
{
	

#ifdef __PLAT_NGPS__

	if(( IsOnline() == false ) || m_options_changed )
	{
        if( GetConnectionType() == vCONN_TYPE_ETHERNET )
		{
			m_online = true;
		}
		else if(( GetConnectionType() == vCONN_TYPE_MODEM ) ||
				( GetConnectionType() == vCONN_TYPE_PPPOE ))
		{
			int result, device_type;
			int modem_state;
            
			s_cancel_dialup_conn = false;
            result = sndev_get_attached(0, &device_type, NULL, NULL);
			// Check that the above function call worked ok
			if( result != 0 )
			{
				SetModemState( vMODEM_STATE_ERROR );
				m_modem_err = SNTC_ERR_NOMODEM;
				return false;
			}
		
			// Check that there is a compatible modem attached
			if( device_type != SN_DEV_TYPE_USB_MODEM )
			{
				SetModemState( vMODEM_STATE_ERROR );
				m_modem_err = SNTC_ERR_NOMODEM;
				return false;
			}

			result = snmdm_get_state( &modem_state );
			// Check that the above function call worked ok
			if( result != 0 )
			{
				SetModemState( vMODEM_STATE_ERROR );
				m_modem_err = SNTC_ERR_NOMODEM;
				return false;
			}
		
			// Make sure that the modem is ready to dial, if not reset it
			if(	( modem_state != SN_MODEM_READY ) && 
				( modem_state != SN_MODEM_READY_AUTOANS ))
			{
				sn_char* err_msg_ptr;

				Dbg_Printf( "EE:Calling sntc_reset_modem()\n" );
				// Attempt to reset the modem
				result = sntc_reset_modem(	vMODEM_RESET_TIMEOUT,	// timeout_secs
											disconn_modem_state_callback,	// callback
											&err_msg_ptr );     	// error_message
				// If failed to reset the modem, then the result, and
				// error_message will have been set up by sntc_reset_modem.
				if( result != 0 )
				{
					SetModemState( vMODEM_STATE_ERROR );
					m_modem_err = SNTC_ERR_NOMODEM;
					return false;
				}
			}

			{
				struct SemaParam params;

				params.initCount = 1;
				params.maxCount = 10;
			
				s_conn_semaphore = CreateSema( ¶ms );

                // Clear the modem state before we start
				SetModemState( -1 );

				m_modem_thread_data.m_pEntry = threaded_modem_conn;
				m_modem_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
				m_modem_thread_data.m_pStackBase = m_modem_thread_stack;
				m_modem_thread_data.m_iStackSize = vMODEM_THREAD_STACK_SIZE;
				m_modem_thread_data.m_utid = 0x150;//vBASE_SOCKET_THREAD_ID + NumApps();
				Thread::CreateThread( &m_modem_thread_data );
				m_modem_thread_id = m_modem_thread_data.m_osId;
                
				StartThread( m_modem_thread_id, this );
			}
		}   
	}
#else
	m_online = true;
#endif

	return m_online;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::DisconnectFromInternet( void )
{
	

#ifdef __PLAT_NGPS__
	if(	m_conn_type == vCONN_TYPE_ETHERNET )
	{
		m_online = false;
	}
	else if( ( m_conn_type == vCONN_TYPE_MODEM ) ||
			 ( m_conn_type == vCONN_TYPE_PPPOE ))
	{
        // Just in case the modem thread is running, stop it
		s_cancel_dialup_conn = true;
		WaitSema( s_conn_semaphore );
		DeleteSema( s_conn_semaphore );
		TerminateThread( m_modem_thread_id );
		DeleteThread( m_modem_thread_id );

		SetModemState( vMODEM_STATE_DISCONNECTING );
		m_modem_err = 0;

		m_modem_thread_data.m_pEntry = threaded_modem_disconn;
		m_modem_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
		m_modem_thread_data.m_pStackBase = m_modem_thread_stack;
		m_modem_thread_data.m_iStackSize = vMODEM_THREAD_STACK_SIZE;
		m_modem_thread_data.m_utid = 0x14F;//vBASE_SOCKET_THREAD_ID + NumApps();
		Thread::CreateThread( &m_modem_thread_data );
		m_modem_thread_id = m_modem_thread_data.m_osId;
		
		StartThread( m_modem_thread_id, this );
	}
#else
	m_online = false;
#endif

	return ( m_online == false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetModemBaudRate( void )
{
	return m_modem_baud_rate;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetBandwidth( int bytes_per_sec )
{
	m_bandwidth = bytes_per_sec;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetBandwidth( void )
{
	return m_bandwidth;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::IsOnline( void )
{   
#ifdef __PLAT_NGPS__
	if(	( GetConnectionType() == vCONN_TYPE_MODEM ) ||
		( GetConnectionType() == vCONN_TYPE_PPPOE ))
	{
		int modem_state;
        
		m_online = false;
		if( snmdm_get_state( &modem_state ) == 0 )
		{
			if( modem_state == SN_MODEM_PPP_UP )
			{
				m_online = true;
			}
		}
	}
#endif// __PLAT_NGPS__

	return m_online;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetISPPhoneNumber( char* phone_no )
{
	

	Dbg_Assert( phone_no );

	strcpy( m_isp_phone_no, phone_no );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetISPLogin( char* login )
{
	

	Dbg_Assert( login );

	strcpy( m_isp_user_name, login );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetISPPassword( char* password )
{
	

	Dbg_Assert( password );

	strcpy( m_isp_password, password );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetGateway( char* ip )
{
	

	if( strcmp( ip, m_gateway ))
	{
		strcpy( m_gateway, ip );
#ifdef __PLAT_NGPS__
		m_options_changed = true;
#endif		// __PLAT_NGC__
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetSubnetMask( char* ip )
{
	

	if( strcmp( ip, m_subnet ))
	{
		strcpy( m_subnet, ip );
#ifdef __PLAT_NGPS__
		m_options_changed = true;
#endif		// __PLAT_NGC__
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetPublicIP( unsigned int ip )
{
	m_public_ip = ip;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetLocalIP( char* ip )
{
	

	if( strcmp( ip, m_local_ip ))
	{
		strcpy( m_local_ip, ip );
#ifdef __PLAT_NGPS__
		m_options_changed = true;
#endif		// __PLAT_NGC__
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetConnectionType( ConnType conn_type )
{
	if( m_conn_type != conn_type )
	{
#ifdef __PLAT_NGPS__
		if( m_conn_type != vCONN_TYPE_NONE )
		{
			m_device_changed = true;
		}
#endif		// __PLAT_NGC__
		m_conn_type = conn_type;

        // Some default values for bandwidth limiting
		switch( m_conn_type )
		{
			case vCONN_TYPE_MODEM:
				SetBandwidth( 4200 );	
				break;
			case vCONN_TYPE_ETHERNET:
				SetBandwidth( 400000 );
				break;
			case vCONN_TYPE_PPPOE:
				SetBandwidth( 300000 );
				break;
			default:
				break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetDeviceType( DeviceType dev_type )
{
	if( m_device_type != dev_type )
	{
#ifdef __PLAT_NGPS__
		if( m_device_type != vDEV_TYPE_NONE )
		{
			m_device_changed = true;
		}
#endif		// __PLAT_NGC__
		m_device_type = dev_type;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetDHCP( bool use_dhcp )
{    
	if( m_use_dhcp != use_dhcp )
	{
		m_use_dhcp = use_dhcp;
#ifdef __PLAT_NGPS__
		m_options_changed = true;
#endif		// __PLAT_NGC__
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetDialupAuthentication( bool authenticate )
{
	m_use_dialup_auth = authenticate;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetDNSServer( int index, char* ip )
{
	
	
	Dbg_Assert(( index >= 0 ) && ( index < 3 ));
	Dbg_Assert( ip );

	if( strcmp( ip, m_dns_servers[index] ))
	{
		strcpy( m_dns_servers[index], ip );
#ifdef __PLAT_NGPS__
		m_options_changed = true;
#endif		// __PLAT_NGC__
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetHostName( char* host )
{
	

	// Either they pass us NULL or they pass us a value less than 32 chars long
	Dbg_Assert( !host || ( strlen( host ) < 32 ));

    if( host == NULL )
	{
		m_host_name[0] = '\0';
	}

	strcpy( m_host_name, host );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetDomainName( char* domain )
{
	

	// Either they pass us NULL or they pass us a value less than 32 chars long
	Dbg_Assert( !domain || ( strlen( domain ) < 32 ));

    if( domain == NULL )
	{
		m_domain_name[0] = '\0';
	}

	strcpy( m_domain_name, domain );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	Manager::GetDNSServer( int index )
{
	

	Dbg_Assert(( index >= 0 ) && ( index < 3 ));

	return m_dns_servers[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	Manager::GetISPPhoneNumber( void )
{
	if( GetConnectionType() == vCONN_TYPE_PPPOE )
	{
		return "";
	}
	return m_isp_phone_no;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ShouldUseDHCP( void )
{
	if(( GetConnectionType() == vCONN_TYPE_PPPOE ) ||
	   ( GetConnectionType() == vCONN_TYPE_MODEM ))
	{
		return false;
	}

	return m_use_dhcp;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ShouldUseDialupAuthentication( void )
{
	if( GetConnectionType() == vCONN_TYPE_PPPOE )
	{
		return true;
	}
	return m_use_dialup_auth;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	Manager::GetGateway( void )
{
	

	return m_gateway;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	Manager::GetSubnetMask( void )
{
	

	return m_subnet;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	Manager::GetHostName( void )
{
	

	return m_host_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	Manager::GetDomainName( void )
{
	

	return m_domain_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

unsigned int	Manager::GetPublicIP( void )
{
	if( m_public_ip == 0 )
	{
		return inet_addr( m_local_ip );
	}
	
	return m_public_ip;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	Manager::GetLocalIP( void )
{   
	

#if defined( __PLAT_NGPS__ ) || defined( __PLAT_XBOX__ ) || defined( __PLAT_NGC__ )
	return m_local_ip;
#else
	struct hostent *host, *local_host;
	struct in_addr address;
	char *ip_str;
	int ip;

	if(( local_host = gethostbyname( "localhost" )))
	{	
		if(( host = gethostbyname( local_host->h_name )))
		{
			ip = *(unsigned long *) host->h_addr_list[0];			
			address.s_addr = ip;
			ip_str = inet_ntoa( address ); 
			return ip_str;
		}
		else
		{
			ip = *(unsigned long *) local_host->h_addr_list[0];			
			address.s_addr = ip;
			ip_str = inet_ntoa( address ); 
			return ip_str;
		}
	}
	
	return "";
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ConnType	Manager::GetConnectionType( void )
{
	return m_conn_type;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

DeviceType	Manager::GetDeviceType( void )
{
#ifdef __PLAT_XBOX__
	return vDEV_TYPE_USB_ETHERNET;
#else
	return m_device_type;
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::SetError( int error )
{
	m_last_error = error;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int			Manager::GetLastError( void )
{
	return m_last_error;
}

/******************************************************************/
/* Get the name associated with a message id 					  */
/*                                                                */
/******************************************************************/

char*	Manager::GetMessageName( unsigned char msg_id )
{
#ifdef	NET_DEBUG_MESSAGES
	return m_message_names[ msg_id ];
#else
	return "";
#endif
}

/******************************************************************/
/* Associate a text name with a message id for debugging purposes */
/*                                                                */
/******************************************************************/

void	Manager::SetMessageName( unsigned char msg_id, char* msg_name )
{
#ifdef NET_DEBUG_MESSAGES
	Dbg_Assert( msg_name );

	strncpy( m_message_names[ msg_id ], msg_name, vMAX_MSG_NAME_LEN - 1 );
	m_message_names[msg_id][vMAX_MSG_NAME_LEN - 1] = '\0';
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char	Manager::GetMessageFlags( unsigned char msg_id )
{
	return m_message_flags[ msg_id ];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetMessageFlags( unsigned char msg_id, char flags )
{
	m_message_flags[ msg_id ] = flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetModemState( void )
{
	return m_modem_state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetModemState( int state )
{
	m_modem_state = state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetModemError( void )
{
	return m_modem_err;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::CanChangeDevices( void )
{
#ifdef __PLAT_NGPS__
	return !m_net_drivers_loaded;
#else
	return true;
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

MsgLink::MsgLink( QueuedMsg *msg )
: Lst::Node< MsgLink > ( this ), m_QMsg( msg ), m_SendTime( 0 )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

MsgImpLink::MsgImpLink( QueuedMsg *msg )
: Lst::Node< MsgImpLink > ( this ), m_QMsg( msg ), m_SendTime( 0 )
{
	m_NumResends = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

MsgSeqLink::MsgSeqLink( QueuedMsgSeq *msg )
: Lst::Node< MsgSeqLink > ( this ), m_StreamMessage( 0 ), m_SendTime( 0 ), m_QMsg( msg )
{
	m_NumResends = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

StreamLink::StreamLink( StreamDesc* desc )
: Lst::Node< StreamLink > ( this ), m_Desc ( desc )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

StreamDesc::StreamDesc( void )
: m_Size( 0 ), m_Data( NULL ), m_DataPtr( NULL ), m_GroupId( 0 ), m_SendInPlace( false )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

StreamDesc::~StreamDesc( void )
{
	if( m_Data && !m_SendInPlace )
	{
		delete [] m_Data;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

MsgDesc::MsgDesc( void )
: m_Id( 0 ), m_StreamMessage( 0 ), m_Length( 0 ), m_Data( 0 ), m_Priority( NORMAL_PRIORITY ), m_Queue( QUEUE_DEFAULT ), m_GroupId( GROUP_ID_DEFAULT ),
	m_Singular( false ), m_Delay( 0 ), m_ForcedSequenceId( 0 )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

QueuedMsg::QueuedMsg( unsigned char msg_id, unsigned short msg_len, void* data )
{
	
	
	m_Data = NULL;
	if( msg_len > 0 )
	{
		m_Data = new char[ msg_len ];
		memcpy( m_Data, data, msg_len );
	}
	
	m_MsgId = msg_id;
	m_MsgLength = msg_len;
#ifdef	__PLAT_NGPS__											  
	Dbg_MsgAssert(Mem::SameContext(this,Mem::Manager::sHandle().NetworkHeap()),("QueuedMsg not on network heap"));	
#endif		//	__PLAT_NGPS__											  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

QueuedMsg::~QueuedMsg( void )
{
	
	if( m_Data )
	{
		delete [] m_Data;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

QueuedMsgSeq::QueuedMsgSeq( unsigned char msg_id, unsigned short msg_len, void* data, unsigned char group_id )
{
	
	
	m_Data = NULL;
	if( msg_len > 0 )
	{
		m_Data = new char[ msg_len ];
		memcpy( m_Data, data, msg_len );
	}
	
	m_MsgId = msg_id;
	m_MsgLength = msg_len;
	m_GroupId = group_id;	
#ifdef	__PLAT_NGPS__											  
	Dbg_MsgAssert(Mem::SameContext(this,Mem::Manager::sHandle().NetworkHeap()),("QueuedMsgSeq not on network heap"));	
#endif		//	__PLAT_NGPS__											  

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

QueuedMsgSeq::~QueuedMsgSeq( void )
{
	if( m_Data )
	{
		delete [] m_Data;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

BitStream::BitStream( void )
{
	m_data = NULL;
	m_bits_left = 32;
	m_size = 0;
	m_cur_val = 0;
	m_bits_processed = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BitStream::SetInputData( char* data, int size )
{
	m_data = (unsigned int*) data;
	m_start_data = m_data;
	m_size = size;
	m_bits_left = 0;
	m_bits_processed = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BitStream::SetOutputData( char* data, int size )
{
	m_data = (unsigned int*) data;
	m_start_data = m_data;
	m_size = size;
	m_bits_left = 32;
	m_bits_processed = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	BitStream::GetByteLength( void )
{
	return (( m_bits_processed + 7 ) >> 3 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BitStream::WriteValue( int value, int num_bits )
{
	m_bits_processed += num_bits;
	
	// If we cannot fit all bits into our current int, we'll have to span it over
	// two ints
	if( m_bits_left < num_bits )
	{
		// Write out the first portion, then advance to the next int

        // First clear all top (unwritten) bits
		m_cur_val &= ( 1 << ( 32 - m_bits_left )) - 1;
		
		// Now write them (without having to mask low bits off)
		m_cur_val |= value << ( 32 - m_bits_left );
		num_bits -= m_bits_left;
		value >>= m_bits_left;
		memcpy( m_data, &m_cur_val, sizeof( unsigned int ));
		m_data++;
		m_cur_val = 0;
		m_bits_left = 32;
	}

    // First clear all top (unwritten) bits
	m_cur_val &= ( 1 << ( 32 - m_bits_left )) - 1;
	// Now write them (without having to mask low bits off)
	m_cur_val |= value << ( 32 - m_bits_left );
	m_bits_left -= num_bits;
	
	// Flush bits if full
	if( m_bits_left == 0 )
	{
		memcpy((char*) m_data, (char*) &m_cur_val, sizeof( unsigned int ));
		m_data++;
		m_cur_val = 0;
		m_bits_left = 32;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void BitStream::WriteFloatValue( float value )
{
	WriteValue( *(int*) &value, sizeof( float ) * 8 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void BitStream::Flush( void )
{
	// Only flush if we have bits to flush
	if( m_bits_left != 32)
	{
		memcpy( m_data, &m_cur_val, sizeof( unsigned int ));
		m_data++;
		m_cur_val = 0;
		m_bits_left = 32;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float			BitStream::ReadFloatValue( void )
{
	float res;
	int num_bits;
	
	num_bits = 32;
	m_bits_processed += num_bits;

	// If we're all out of bits (like at start), read a full word
	if( m_bits_left == 0 )
	{
		memcpy( &m_cur_val, m_data, sizeof( unsigned int ));
		m_data++;
		m_bits_left = 32;
	}
	
	// Do we hold all bits requested?
	if( m_bits_left >= num_bits )
	{
		// Yes, easy peasy case
		m_bits_left -= num_bits;
		if( num_bits == 32 )
		{
			memcpy( &res, &m_cur_val, sizeof( float ));
		}
		else
		{
			unsigned int int_val;

			int_val = m_cur_val & ((1 << num_bits) - 1);
			memcpy( &res, &int_val, sizeof( float ));
		}
		
		m_cur_val >>= num_bits;
	}
	else
	{
		unsigned int int_val;
		// Nope, grab those we have and fetch more from stream

		int_val = m_cur_val & (( 1 << m_bits_left ) - 1);
		num_bits -= m_bits_left;
		memcpy( &m_cur_val, m_data, sizeof( unsigned int ));
		m_data++;
		int_val |= ( m_cur_val & (( 1 << num_bits ) - 1)) << m_bits_left;
		m_cur_val >>= num_bits;
		m_bits_left = 32 - num_bits;

		memcpy( &res, &int_val, sizeof( float ));
	}
	
	return res;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

unsigned int	BitStream::ReadUnsignedValue( int num_bits )
{
	unsigned long res;
	
	Dbg_Assert(( num_bits >= 0 ) && ( num_bits <= 32 ));
   
	m_bits_processed += num_bits;

	// If we're all out of bits (like at start), read a full word
	if( m_bits_left == 0 )
	{
		memcpy( &m_cur_val, m_data, sizeof( unsigned int ));
		m_data++;
		m_bits_left = 32;
	}
	
	// Do we hold all bits requested?
	if( m_bits_left >= num_bits )
	{
				// Yes, easy peasy case
		m_bits_left -= num_bits;
		if( num_bits == 32 )
		{
			res = m_cur_val;
		}
		else
		{
			res = m_cur_val & ((1 << num_bits) - 1);
		}
		
		m_cur_val >>= num_bits;
	}
	else
	{
		// Nope, grab those we have and fetch more from stream
		res = m_cur_val & (( 1 << m_bits_left ) - 1);
		num_bits -= m_bits_left;
		memcpy( &m_cur_val, m_data, sizeof( unsigned int ));
		m_data++;
		res |= ( m_cur_val & (( 1 << num_bits ) - 1)) << m_bits_left;
		m_cur_val >>= num_bits;
		m_bits_left = 32 - num_bits;
	}
	
	return res;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	BitStream::ReadSignedValue( int num_bits )
{
	long res;

	Dbg_Assert(( num_bits >= 0 ) && ( num_bits <= 32 ));

	// Cheasy call to that other function for simplicity
	res = ReadUnsignedValue( num_bits );

	// Sign extend result if sign bit set
	if( res & ( 1 << ( num_bits - 1 )))
	{
		res |= ~(( 1 << num_bits ) - 1 );
	}

	return res;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}	// namespace Net

================================================
FILE: Code/Gel/Net/net.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			NET  (Net)												**
**																			**
**	File name:		gel/net.h												**
**																			**
**	Created: 		01/29/01	-	spg										**
**																			**
*****************************************************************************/

#ifndef __NET_H__
#define __NET_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#include 
#include 
#include 

#include 

#if __PLAT_NGPS__
#include         /* For sceSifInitRpc()                    */
#include         /* For sceSifLoadModule()                 */

#include "sntypes.h"       /* SN Systems types                       */
#include "sneeutil.h"      /* SN Systems PS2 EE Utilites (General)   */
#include "snsocket.h"      /* SN Systems socket API header file      */
#include "sntcutil.h"      /* SN Systems PS2 EE Utilites (TCP/IP)    */
#endif	// __PLAT_NGPS__

#ifdef __PLAT_WN32__
#include 
#endif // __PLAT_WN32__

#ifdef __PLAT_XBOX__
#include 
#include 
#endif // __PLAT_XBOX__

#ifdef __PLAT_NGC__
//// GameCube only.
//#include 
//#include "sys/ngc/p_dl.h"
//#include "sys/ngc/p_scene.h"
//#include "sys/ngc/p_slerp.h"
//#include "sys/ngc/p_vector.h"
//#include "sys/ngc/p_matrix.h"
//#include "sys/ngc/p_texman.h"
#include 
#define NsWorldSector	NsDL
#define gethostbyname(name) SOGetHostByName(name)
#define gethostname(name,namelen) 0
#define inet_addr(cp) 0
#define inet_aton(cp,addr) SOInetAtoN(cp, addr)
#define inet_ntoa(in) SOInetNtoA(in)
#define in_addr SOInAddr
#define sockaddr_in SOSockAddrIn
#define sockaddr SOSockAddr
#define AF_INET         SO_PF_INET              /* internetwork: UDP, TCP, etc. */
#define PF_INET         AF_INET
#define htonl(l)        SOHtoNl(l)
#define ntohl(l)        SONtoHl(l)
#define htons(s)        SOHtoNs(s)
#define ntohs(s)        SONtoHs(s)
#define sin_family family
#define sin_port port
#define sin_addr addr
#define sin_zero zero
#define s_addr addr
#define INADDR_ANY	SO_INADDR_ANY
#define IPPROTO_UDP 0

#define accept(s,addr,addrlen) SOAccept(s,addr)
#define bind(s,addr,addrlen) SOBind(s,addr)
#define closesocket(s) SOClose(s)
#define connect(s,addr,addrlen) SOConnect(s,addr)
#define getpeername(s,addr,addrlen) SOGetPeerName(s,addr)
#define getsockname(s,addr,addrlen) SOGetSockName(s,addr)
#define getsockopt(s,level,optname,optval,optlen) SOGetSockOpt(s,level,optname,optval)
#define listen(s,backlog) SOListen(s,backlog)
#define recv(s,buf,len,flags) SORecv(s,buf,len,flags)
#define recvfrom(s,buf,len,flags,from,fromlen) SORecvFrom(s,buf,len,flags,from)
#define recvmsg(s,msg,flags) 0
#define select(nfds,readfs,writefs,exceptfs,timeout) 0
#define send(s,buf,len,flags) SOSend(s,buf,len,flags)
#define sendmsg(s,msg,flags) 0
#define sendto(s,buf,len,flags,to,tolen) SOSendTo(s,buf,len,flags,to)
#define setsockopt(s,level,optname,optval,optlen) SOSetSockOpt(s,level,optname,optval)
#define shutdown(s,how) SOShutdown(s,how)
#define sockAPIinit(maxthreads) 0
#define sockAPIreinit() 0
#define sockAPIregthr() 0
#define sockAPIderegthr() 0
#define socket(af,type,protocol) SOSocket(af,type,protocol)


#define ENOBUFS         1
#define ETIMEDOUT       2
#define EISCONN         3
#define EOPNOTSUPP      4
#define ECONNABORTED    5
#define EWOULDBLOCK     6
#define ECONNREFUSED    7
#define ECONNRESET      8
#define ENOTCONN        9
#define EALREADY        10
#define EINVAL          11
#define EMSGSIZE        12
#define EPIPE           13
#define EDESTADDRREQ    14
#define ESHUTDOWN       15
#define ENOPROTOOPT     16
#define EHAVEOOB        17
#define ENOMEM          18
#define EADDRNOTAVAIL   19
#define EADDRINUSE      20
#define EAFNOSUPPORT    21
#define EINPROGRESS     22
#define ELOWER          23
#define EBADF           24

#define SOCK_STREAM     1               /* stream socket */
#define SOCK_DGRAM      2               /* datagram socket */

#define SO_DEBUG        0x0001          /* turn on debugging info recording */
#define SO_ACCEPTCONN   0x0002          /* socket has had listen() */
#define SO_REUSEADDR    0x0004          /* allow local address reuse */
#define SO_KEEPALIVE    0x0008          /* keep connections alive */
#define SO_DONTROUTE    0x0010          /* just use interface addresses */
#define SO_BROADCAST    0x0020          /* permit sending of broadcast msgs */
#define SO_USELOOPBACK  0x0040          /* bypass hardware when possible */
#define SO_LINGER       0x0080          /* linger on close if data present */
#define SO_OOBINLINE    0x0100          /* leave received OOB data in line */

/* Additional options not kept in so_options */

#define SO_SNDBUF       0x1001          /* send buffer size */
#define SO_RCVBUF       0x1002          /* receive buffer size */
#define SO_SNDLOWAT     0x1003          /* send low-water mark */
#define SO_RCVLOWAT     0x1004          /* receive low-water mark */
#define SO_SNDTIMEO     0x1005          /* send timeout */
#define SO_RCVTIMEO     0x1006          /* receive timeout */
#define SO_ERROR        0x1007          /* get error status and clear */
#define SO_TYPE         0x1008          /* get socket type */

#define SO_HOPCNT       0x1009          /* Get hop count to destination */
#define SO_MAXMSG       0x1010          /* Get TCP_MSS (max segment size) */

/* Option extensions */

#define SO_RXDATA       0x1011          /* Get count of received bytes */
#define SO_MYADDR       0x1012          /* Get local IP address */
#define SO_NBIO         0x1013          /* Set socket non-blocking */
#define SO_BIO          0x1014          /* Set socket blocking */
#define SO_NONBLOCK     0x1015          /* Set/get blocking state */

#define MSG_OOB         0x1             /* process out-of-band data */
#define MSG_PEEK        0x2             /* peek at incoming message */
#define MSG_DONTROUTE   0x4             /* send without using routing tables */
#define MSG_DONTWAIT    0x20            /* this message should be nonblocking */

#define SD_RECEIVE  0
#define SD_SEND     1
#define SD_BOTH     2
#endif // __PLAT_NGC__

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#ifdef __PLAT_NGPS__
#define vBASE_SOCKET_THREAD_ID	0x110

#define vSOCKET_THREAD_PRIORITY	10
#define vMAIN_THREAD_PRIORITY	11

#define vSOCKET_THREAD_STACK_SIZE	( 8 * 1024 )
#define vMODEM_THREAD_STACK_SIZE	( 12 * 1024 )
#endif

#ifndef SOCKET_ERROR
#	define SOCKET_ERROR -1
#endif

#ifndef INVALID_SOCKET
#	define INVALID_SOCKET -1
#endif

#if( defined( __PLAT_NGPS__ ) || defined( __PLAT_NGC__ ))
typedef int	SOCKET;	// Xbox type SOCKET is defined in winsockx.h.
#endif

//#define NET_DEBUG_MESSAGES

namespace Net
{



enum
{
	MSG_ID_PING_TEST			= 1,		// = 1 : Server->Client
	MSG_ID_PING_RESPONSE,					// = 2 : Client->Server
	MSG_ID_CONNECTION_REQ,					// = 3 : Client->Server
	MSG_ID_CONNECTION_ACCEPTED,				// = 4 : Server->Client
	MSG_ID_CONNECTION_REFUSED,				// = 5 : Server->Client
	MSG_ID_CONNECTION_TERMINATED,			// = 6 : Server->Client
	MSG_ID_SEQUENCED,						// = 7 : Bi-directional sequenced identifier
	MSG_ID_ACK,								// = 8 : Bi-directional acknowledgement
	MSG_ID_TIMESTAMP,						// = 9 : Server->Client
	MSG_ID_ALIAS,							// = 10 : Client->Server New alias request
	MSG_ID_DISCONN_REQ,						// = 11 : Client->Server Request to disconnect
	MSG_ID_DISCONN_ACCEPTED,				// = 12 : Server->Client Go ahead and quit
	MSG_ID_KEEPALIVE,						// = 13 : Bi-directional general keepalive message		
	MSG_ID_STREAM_START,					// = 14 : Bi-directional general stream start message
	MSG_ID_STREAM_DATA,						// = 15 : Bi-directional general stream data message
	MSG_ID_SERVER_RESPONSE,					// = 16 : Server->Client
	MSG_ID_FIND_SERVER,						// = 17 : Client->Server Broadcast

	MSG_ID_USER					= 32,		// Game-Specific messages start here
};

enum
{
	GROUP_ID_DEFAULT,
	GROUP_ID_LATENCY_TESTS,
	GROUP_ID_STREAMS,
};

#define MAX_NET_MSG_ID	255
#define MAX_MSG_IDS		256
#define MAX_SEQ_GROUPS	256
#define MAX_LEN_APP_NAME 64
#define MAX_STREAM_CHUNK_LENGTH	256
#define MAX_STREAM_LENGTH (Net::Conn::vREAD_BUFFER_LENGTH)
#define MAX_UDP_PACKET_SIZE 1300
#define vUDP_PACKET_OVERHEAD 28 // 20 for IPv4 header, 8 for UDP Header

#define NET_NUM_LATENCY_TESTS	10

enum
{
	HANDLE_ID_EXCLUDE_BROADCAST	= 0x80,	// Or this with a handle ID and it will send to all
										// handles except that id
	HANDLE_ID_BROADCAST 		= 255,
};

enum
{
	vNO_ALIAS  		= 255,
	vNUM_ALIASES	= 255,
	vALIAS_DURATION = 2000	// number of ms before an alias expires (2 seconds)
};

enum
{
	LOWEST_PRIORITY = 0,
	NORMAL_PRIORITY	= 128,
	HIGHEST_PRIORITY = 255
};

// MSG_HANDLER_FLAGS
enum
{
	mHANDLE_FOREIGN 		= 0x0001,
	mHANDLE_LATE			= 0x0002,
	mHANDLE_CRC_MISMATCH	= 0x0004,
};

enum
{
	HANDLER_ERROR,
	HANDLER_CONTINUE,
	HANDLER_HALT,
	HANDLER_MSG_DONE,
	HANDLER_MSG_DESTROYED,
};
	
typedef enum
{
	QUEUE_DEFAULT,
	QUEUE_IMPORTANT,
	QUEUE_SEQUENCED

} QUEUE_TYPE;

enum ConnType
{
	vCONN_TYPE_NONE,
	vCONN_TYPE_MODEM,
	vCONN_TYPE_ETHERNET,
	vCONN_TYPE_PPPOE
};

enum DeviceType
{
	vDEV_TYPE_NONE,
	vDEV_TYPE_USB_ETHERNET,
	vDEV_TYPE_USB_MODEM,
	vDEV_TYPE_PC_ETHERNET,
	vDEV_TYPE_SONY_MODEM,
};

enum
{
	vMODEM_CONNECT_TIMEOUT		= 60,	// seconds
	vMODEM_DISCONNECT_TIMEOUT	= 10,	// seconds
	vMODEM_RESET_TIMEOUT		= 10,	// seconds
};

enum
{
	vRES_SUCCESS,
	vRES_ERROR_GENERAL,
	vRES_ERROR_UNKNOWN_DEVICE,
	vRES_ERROR_INVALID_IRX,
	vRES_ERROR_DEVICE_NOT_CONNECTED,
	vRES_ERROR_DEVICE_NOT_HOT,
	vRES_ERROR_DHCP,
	vRES_ERROR_DEVICE_CHANGED,
};

enum
{
	vMODEM_STATE_DIALING,
	vMODEM_STATE_CONNECTED,
	vMODEM_STATE_LOGGED_IN,
	vMODEM_STATE_DISCONNECTING,
	vMODEM_STATE_HANGING_UP,
	vMODEM_STATE_DISCONNECTED,
	vMODEM_STATE_READY_TO_TRANSMIT,
	vMODEM_STATE_ERROR,
};

enum
{
	mMSG_SIZE_UNKNOWN = 0x01
};

class App;
class Server;
class Client;
class Conn;
class Manager;

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/
// STANDARD NET MESSAGES
class MsgMax
{
public:
	char	m_Data[4096];
};

class MsgTimestamp
{
public:
	unsigned int m_Timestamp;
};

class MsgPacketStamp
{
public:
	//unsigned char  m_Handle;
	unsigned short m_Packetstamp;
};

typedef MsgPacketStamp	MsgAck;

class MsgAlias
{
public:
	unsigned char	m_Alias;
	int				m_ObjId;
	int				m_Expiration;	// Frame of expiration
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class MsgStreamStart
{
public:
	char	m_StreamDesc[32];
	int		m_Size;
	unsigned int	m_StreamId;
	uint32	m_Checksum;
	unsigned char	m_MsgId;
	char	m_Data[ MAX_STREAM_CHUNK_LENGTH ];
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class MsgStreamData
{
public:
	unsigned int	m_StreamId;
	char			m_Data[ MAX_STREAM_CHUNK_LENGTH ];
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class StreamDesc
{
public:
	StreamDesc( void );
	~StreamDesc( void );
	int			m_Size;
	unsigned int	m_StreamId;
	unsigned char		m_MsgId;
	char		m_StreamDesc[32];
	char*		m_Data;
	char* 		m_DataPtr;
	unsigned char	m_GroupId;
	unsigned int	m_SequenceId;
	uint32		m_Checksum;
	bool		m_SendInPlace;
};

class StreamLink : public Lst::Node< StreamLink >
{
public:
	StreamLink( StreamDesc* desc );

	StreamDesc*	m_Desc;
};

// NET MESSAGE LINKS
class QueuedMsg
{
public:
	QueuedMsg( unsigned char msg_id, unsigned short msg_len, void* data );
	~QueuedMsg( void );

	unsigned char	m_MsgId;
	unsigned short	m_MsgLength;
	char*			m_Data;
	Spt::Ref		m_Ref;
};

class QueuedMsgSeq
{
public:
	QueuedMsgSeq( unsigned char msg_id, unsigned short msg_len, void* data, unsigned char group_id );
	~QueuedMsgSeq( void );

	unsigned char	m_MsgId;
	unsigned short	m_MsgLength;
	char*			m_Data;
	char			m_GroupId;
	
	Spt::Ref	m_Ref;
};

class MsgLink : public Lst::Node< MsgLink >
{
public:
	MsgLink( QueuedMsg* msg );

	QueuedMsg*		m_QMsg;
	unsigned int	m_SendTime;
};

class MsgImpLink : public Lst::Node< MsgImpLink >
{	
public:
	MsgImpLink( QueuedMsg* msg );

public:
	unsigned int 	m_Timestamp;
	QueuedMsg*		m_QMsg;
	unsigned int	m_Packetstamp;
	unsigned char	m_NumResends;
	unsigned int	m_SendTime;
};

class MsgSeqLink : public Lst::Node< MsgSeqLink >
{
public:
	MsgSeqLink( QueuedMsgSeq* msg );

	unsigned char	m_NumResends;
	unsigned char	m_GroupId;
	char			m_StreamMessage;	// boolean
	unsigned int	m_Packetstamp;
	unsigned int 	m_Timestamp;
	unsigned int	m_SequenceId;   
	unsigned int	m_SendTime;

	QueuedMsgSeq*	m_QMsg;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class AliasEntry
{
public:
	int	m_Id;			// full id of aliased object
	int m_Expiration;	// frame of expiration
};

class MsgHandlerContext
{
public:
	
	char*			m_Msg;
	unsigned char	m_MsgId;
	unsigned long   m_MsgLength;
	App*			m_App;
	Conn*			m_Conn;
	int				m_PacketFlags;
	void*			m_Data;
};

typedef int	(MsgHandlerCode)( MsgHandlerContext *context );

class MsgHandler : public Lst::Node< MsgHandler >
{
	friend class Dispatcher;
	friend class Server;
	friend class Client;

public:
	MsgHandler( MsgHandlerCode *code, int flags = 0, 
						void *data = NULL, int pri = NORMAL_PRIORITY );

private:
	MsgHandlerCode	*m_code;
	void			*m_data;
	int				m_flags;
	
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class LatencyTest
{
public:

	LatencyTest ();

	void	InputLatencyValue( int latency );

	int				m_AveLatency;
	unsigned int	m_TimeTests[NET_NUM_LATENCY_TESTS];
	unsigned int	m_CurrentTest;
	unsigned int	m_SendTime;
	unsigned int	m_ReceiveTime;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  PacketInfo  : public Spt::Class
{	
public:
	int			GetNumBytes( void );
	int			GetTime( void );

	void		SetNumBytes( int size );
	void		SetTime( int time );

private:
	int			m_num_bytes;
	int			m_time;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  Metrics  : public Spt::Class
{
public:
	enum
	{
		vNUM_BUFFERED_PACKETS = 256
	};

				Metrics( void );

	void		CalculateBytesPerSec( int cur_time );
	int			GetBytesPerSec( void );
	
	int			GetTotalBytes( void );
	int			GetTotalMessageData( int msg_id );
	int			GetTotalNumMessagesOfId( int msg_id );
		
	void		AddPacket( int size, int time );
	void		AddMessage( int msg_id, int size );
    
private:

	PacketInfo	m_packets[ vNUM_BUFFERED_PACKETS ];
	int			m_num_packets;
		
	int			m_num_messages[ MAX_MSG_IDS ];		// total # messages per msg id
	int			m_size_messages[ MAX_MSG_IDS ];		// total # bytes per msg id

	int			m_total_bytes;
	int			m_bytes_per_sec;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// NET CONNECTION

class Conn
{
	friend class Client;
	friend class Server;
	friend class App;
	friend class Dispatcher;

private:
	
	Lst::Head< MsgLink >	m_normal_msg_list;
	Lst::Head< MsgImpLink >	m_important_msg_list;
	Lst::Head< MsgSeqLink >	m_sequenced_msg_list;

	friend	MsgHandlerCode	handle_ack;	
	friend	MsgHandlerCode	handle_latency_response;
	friend	MsgHandlerCode	handle_latency_test;

	int				m_handle;
	int				m_ip;
	unsigned short	m_port;
	
	char*			m_write_buffer;
	char*			m_write_ptr;
	char*			m_read_buffer;
	char*			m_read_ptr;
	int				m_num_bytes_waiting;

	
	Flags< int >	m_status;
	Flags< int >	m_flags;
    
	App*			m_app;	
	Tmr::Time		m_last_comm_time;	// The last time we've communicated with this connection
    int				m_last_send_time;
	int				m_send_interval;
public:
	enum Status
	{
		mSTATUS_READY 			= 0x0001,
		mSTATUS_BUSY			= 0x0002,
		mSTATUS_INVALID			= 0x0004,
		mSTATUS_DISCONNECTING	= 0x0008
	};

	enum
	{
		mLOCAL		= 0x01,	// local connection: i.e. on this machine
		mREMOTE		= 0x02,	// remote connection: i.e. on another machine
		mFOREIGN	= 0x04,	// foreign connection: i.e. one we don't recognize
	};

	enum
	{   
		vHANDLE_INVALID	= 0,
		vHANDLE_FIRST 	= 1,
	};

	enum
	{
#ifdef __PLAT_WN32__
		vREAD_BUFFER_LENGTH = 10*MAX_UDP_PACKET_SIZE // Used to be 2048
#else
		vREAD_BUFFER_LENGTH = 2*MAX_UDP_PACKET_SIZE // Used to be 2048
#endif
	};

	enum
	{
		vBROADBAND,
		vNARROWBAND,
		vLAN,
	};

	Conn( int flags );
	~Conn( void );

	void			DestroyAllMessageData( void );
    void			AckAllMessages( void );
	void			DestroyMessageQueues( void );
	void			DestroyImportantMessageQueues( void );

	int				GetHandle( void );
	unsigned short	GetPort( void );
	int				GetIP( void );
	int				GetResendThreshold( void );
	Tmr::Time		GetLastCommTime( void );
	Tmr::Time		GetTimeElapsedSinceCommunication( void );
	void			UpdateCommTime( Tmr::Time extra_time = 0 );
	
	void			SetStatus( int status_mask );
	void			ClearStatus( int status_mask );
	bool			TestStatus( int status_mask );
	int				GetStatus( void );

	void			SetIP( int ip );
	void			SetPort( unsigned short port );

	void			Invalidate( void );
	bool			IsValid( void );
	bool			IsForeign( void );
	bool			IsLocal( void );
	bool			IsRemote( void );
    
	void			SetSendInterval( int interval );		// in milliseconds
	int				GetSendInterval( void );
	void			SetForceSendThisFrame( bool force_send );
	bool			GetForceSendThisFrame( void );
	int				GetBandwidthType( void );
    void			SetBandwidthType( int type );

	int				GetAveLatency( void );

	Metrics*		GetOutboundMetrics( void );
	Metrics*		GetInboundMetrics( void );

	void			SetBandwidth( int in_bytes_per_sec );
	int				GetBandwidth( void );

	void			ClearNumResends( void );
	int				GetNumResends( void );
	void			FlagMessagesForResending( unsigned int packet_stamp );

	int				GetNumPendingStreamMessages( void );

	unsigned int	m_SequenceId[MAX_SEQ_GROUPS];			// per-group, per-client array of 'next' sequence ids		
	unsigned int	m_WaitingForSequenceId[MAX_SEQ_GROUPS];	// per-group, per-client array of 'waiting for' sequence ids			
	unsigned int	m_NextStreamId;			// Next stream id
	Lst::Head< MsgSeqLink >	m_SequencedBuffer[MAX_SEQ_GROUPS];	
	Lst::Head< StreamLink > m_StreamInList;
	Lst::Head< StreamLink > m_StreamOutList;
    
private:
	void			destroy_pending_sequenced_messages( void );

	LatencyTest 	m_latency_test;
	Metrics			m_metrics_in;
	Metrics			m_metrics_out;
	bool			m_force_send_packet;
	Lst::Node< Conn > m_node;
	Conn*			m_alias_connection;
	int				m_bandwidth_type;
	unsigned int 	m_latest_packet_stamp;
	unsigned int 	m_latest_sent_packet_stamp;
	int				m_bandwidth;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class Dispatcher
{	
	friend class App;

public:
	Dispatcher( App* app ) 
		: m_app( app ) {}
	MsgHandler*	AddHandler( unsigned char net_msg_id, MsgHandlerCode *code, int flags = 0, 
						void *data = NULL, int pri = NORMAL_PRIORITY );
	void		Init( void );
	void		Deinit( void );
	int			DispatchMsgHandlers( Conn *conn, int flags );
	int			DispatchMessage( MsgHandlerContext *context );

private:
	Lst::Head< MsgHandler > m_handler_list[MAX_NET_MSG_ID];
	App*		m_app;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  Manager  : public Spt::Class
{
	
public:
	enum
	{
		vMAX_MSG_NAME_LEN	= 32,
		vMAX_PACKET_SIZE 	= MAX_UDP_PACKET_SIZE,
		vMIN_PACKET_SIZE	= 3,		// msg should be sizeof( crc) plus at least 1 byte of data
		vPORT_ANY 			= 0,
		vSTANDARD_RESEND_THRESHOLD 	= 2000,	// ms
		vMINIMUM_RESEND_THRESHOLD 	= 500,		// ms
		vMSG_HEADER_LENGTH 			= 1,
		vMSG_HEADER_LENGTH_WITH_SIZE= 3,
		vMSG_SEQ_HEADER_LENGTH	= 6,
		vMSG_CRC_LEN = 2,
		vMAX_PAYLOAD = vMAX_PACKET_SIZE - vMSG_CRC_LEN,
	};

	void		SetError( int error );
	int			GetLastError( void );

	bool		ConnectToInternet( void );
	bool		DisconnectFromInternet( void );
	bool		IsOnline( void );

	Server*		CreateNewAppServer( int id, char* appName, int max_clients, unsigned short port, 
									int address, int flags = 0 );
	Client*		CreateNewAppClient( int id, char* appName, unsigned short port, int address,
									int flags = 0 );
	
	void		DestroyApp( App *app );
		
	void		SetConnectionType( ConnType conn_type );
	void		SetDeviceType( DeviceType dev_type );
	void		SetPublicIP( unsigned int ip );
	void		SetLocalIP( char* ip );
	void		SetGateway( char* ip );
	void		SetSubnetMask( char* ip );
	void		SetISPPhoneNumber( char* phone_no );
	void		SetISPLogin( char* login );
	void		SetISPPassword( char* password );
	void		SetDHCP( bool use_dhcp );
	void		SetDialupAuthentication( bool authenticate );
	void		SetDNSServer( int index, char* ip );
	void		SetHostName( char* host );
	void		SetDomainName( char* host );
	ConnType	GetConnectionType( void );
	DeviceType	GetDeviceType( void );
	char*		GetLocalIP( void );
	unsigned int	GetPublicIP( void );
	char*		GetGateway( void );
	char*		GetSubnetMask( void );
	char*		GetDNSServer( int index );
	char*		GetISPPhoneNumber( void );
	char*		GetHostName( void );
	char*		GetDomainName( void );
	bool		ShouldUseDialupAuthentication( void );
	bool		ShouldUseDHCP( void );

	Server*		FirstServer( Lst::Search< App > *sh );
	Client*		FirstClient( Lst::Search< App > *sh );
	App*		NextApp( Lst::Search< App > *sh );
	int			NumApps( void );

	void		SetMessageName( unsigned char msg_id, char* msg_name );
	char*		GetMessageName( unsigned char msg_id );
	void		SetMessageFlags( unsigned char msg_id, char flags );
	char		GetMessageFlags( unsigned char msg_id );

	void		AddLogicTasks( App* app );
	void		AddLogicPushTasks( App* app );
	void		RemoveNetworkTasks( App* app );

	bool		NeedToTestNetworkEnvironment( void );
	bool		NetworkEnvironmentSetup( void );

	int			GetModemState( void );
	void		SetModemState( int state );
	int			GetModemError( void );
	int			GetModemBaudRate( void );

	void		SetBandwidth( int bytes_per_sec );
	int			GetBandwidth( void );

	bool		CanChangeDevices( void );
	
protected:	
	Lst::Head< App >	m_net_servers;
	Lst::Head< App >	m_net_clients;
	
private:
	Manager();
	~Manager();

#ifdef NET_DEBUG_MESSAGES
	char		m_message_names[ MAX_MSG_IDS ][ vMAX_MSG_NAME_LEN ];	// Text names for each message
#endif
	char		m_message_flags[ MAX_MSG_IDS ];
	ConnType	m_conn_type;
	DeviceType	m_device_type;
	char		m_local_ip[16];
	unsigned int	m_public_ip;
	char		m_dns_servers[3][16];
	char		m_gateway[16];
	char		m_subnet[16];
	char		m_isp_phone_no[32];
	char		m_isp_user_name[80];
	char		m_isp_password[80];
	char		m_host_name[32];
	char		m_domain_name[32];
	int			m_num_apps;
	int			m_last_error;

	bool		m_online;
	bool		m_use_dhcp;
	bool		m_use_dialup_auth;
	int			m_modem_state;
	int			m_modem_err;	
	int			m_modem_baud_rate;
	int			m_bandwidth;
#ifdef __PLAT_NGC__
	bool		initialize_ngc( void );
#endif
#ifdef __PLAT_NGPS__
	bool		initialize_ps2( void );
	bool		sn_stack_setup( void );
	bool		setup_ethernet_params( void );
	bool		initialize_device( void );
	bool		load_irx_files( void );
	bool		start_stack( void );
	bool		stop_stack( void );
	bool		m_stack_setup;
	bool		m_net_drivers_loaded;
	bool		m_options_changed;
	bool		m_device_changed;
    

	// Sn Modem Setup
	static 	void 			conn_modem_state_callback( sn_int32 modem_state );
	static 	void 			disconn_modem_state_callback( sn_int32 modem_state );
	char					m_modem_thread_stack[ vMODEM_THREAD_STACK_SIZE ]	__attribute__ ((aligned(16)));
	static 	void			threaded_modem_conn( void *data );
	static 	void			threaded_modem_disconn( void *data );
	Thread::PerThreadStruct	m_modem_thread_data;
	Thread::THREADID		m_modem_thread_id;

#endif // __PLAT_NGPS__
    
	DeclareSingletonClass( Manager );
};

class BitStream
{
public:
	BitStream( void );

	void			SetInputData( char* data, int size );
	void			SetOutputData( char* data, int size );

	void			WriteValue( int value, int num_bits );
	void			WriteFloatValue( float value );
	void 			Flush( void );
	float			ReadFloatValue( void );
	unsigned int	ReadUnsignedValue( int num_bits );
	int				ReadSignedValue( int num_bits );

	int				GetByteLength( void );

private:
	unsigned int*	m_data;
	unsigned int*	m_start_data;
	unsigned int	m_cur_val;
	int				m_bits_left;
	int				m_size;
	int				m_bits_processed;
};

class MsgDesc
{
public:
	MsgDesc( void );

	unsigned char	m_Id;
	char			m_StreamMessage;	// bool
	unsigned short	m_Length;
	void*			m_Data;
	int				m_Priority;
	QUEUE_TYPE		m_Queue;
	unsigned char	m_GroupId;
    bool			m_Singular;
	int				m_Delay;
	unsigned int	m_ForcedSequenceId;

};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class BannedConn : public Lst::Node< BannedConn >
{
public:
	BannedConn( void ) : Lst::Node< BannedConn > ( this ) {}
	int	m_Ip;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class App
{
friend class Manager;

public:
	enum
	{
		mBROADCAST				= 0x01,
		mALIAS_SUPPORT			= 0x02,
		mDYNAMIC_RESEND			= 0x04,
		mLOCAL					= 0x08,	// Local client (i.e. no real port/address info associated with it)
		mACCEPT_FOREIGN_CONN	= 0x10,
		mSECURE					= 0x20,
	};
    
	App( int flags = 0 );
	virtual	~App( void );

	static	const	unsigned int	MAX_LATENCY;
	typedef void	(ForeignPacketHandlerCode)( char* packet, int len, struct sockaddr* sender );
    
	Conn	*GetConnectionByHandle( int handle );
	Conn	*GetConnectionByAddress( int ip, unsigned short port );
	Conn	*NewConnection( int ip, unsigned short port, int flags = Conn::mREMOTE );
	void	BanConnection( Conn* conn );
	bool	IsConnectionBanned( Conn* conn );

	void	StreamMessageToConn( Net::Conn* conn, unsigned char msg_id, unsigned short msg_len, void* data, char* desc, unsigned char group_id = GROUP_ID_DEFAULT,
								 bool all_at_once = true, bool send_in_place = false );
	void	StreamMessage( int handle, unsigned char msg_id, unsigned short msg_len, void* data, char* desc, unsigned char group_id = GROUP_ID_DEFAULT,
						   bool all_at_once = true, bool send_in_place = false );
	void	StreamMessageToServer( unsigned char msg_id, unsigned short msg_len, void* data, char* desc, unsigned char group_id = GROUP_ID_DEFAULT,
								   bool all_at_once = true, bool send_in_place = false );
	void	EnqueueMessage( int handle, MsgDesc* desc );
	void	EnqueueMessageToServer( MsgDesc* desc );
	void 	DequeueMessagesByType( Net::Conn* conn, QUEUE_TYPE queue, unsigned char msg_id );
	void	FreeConnMessageQueue( Conn *conn, QUEUE_TYPE queue );
	void	FreeMessageQueue( QUEUE_TYPE queue );
	bool	BuildMsgStream( Conn *conn, QUEUE_TYPE queue, bool resends_only = false );
	QueuedMsgSeq*	AllocateNewSeqMessage( unsigned char msg_id, unsigned short msg_len, char* data, unsigned char group_id );
	bool	SendMessageTo( unsigned char msg_id, unsigned short msg_len, void* data,
							int ip, unsigned short port, int flags );
	bool	SendTo( int ip, unsigned short port, char *data, int len, int flags );
	bool	Send( char *data, int len, int flags );
	
	Conn	*FirstConnection( Lst::Search< Conn > *sh );
	Conn	*NextConnection( Lst::Search< Conn > *sh );	
	void	TerminateConnection( Conn* conn );

	Manager*	GetManager( void );
	
	int		GetID( void );
	char*	GetName( void );

	bool	IsLocal( void );
	
	void	AcceptForeignConnections( bool accept );
	bool	AcceptsForeignConnections( void );

	bool	ShouldSendThisFrame( Conn* conn );
	bool	MessagesToProcess( Conn* conn );	
	bool	MessagesToSend( Conn *conn );
	bool	ImportantMessagesToSend( Conn* conn );

	int		BandwidthUsed( void );
	bool	BandwidthExceeded( Conn* conn );

	void	ReportError( void );

	void	AliasConnections( Conn* server_conn, Conn* client_conn );

	Tsk::BaseTask&	GetSendDataTask( void );
	Tsk::BaseTask&	GetReceiveDataTask( void );
	Tsk::BaseTask&	GetProcessDataTask( void );
	Tsk::BaseTask&	GetNetworkMetricsTask( void );
	    
	virtual	void	ProcessData( void );
    
	void			SendData( bool scheduled_send = false );
	virtual	void	ReceiveData( void ) = 0;	

	void	SendEnqueuedMessages( void );
	virtual void	SendEnqueuedMessages( Conn* conn ) = 0;
	
	void			ShutDown( void );
    		
	SOCKET			GetSocket( void );
	void			SetForeignPacketHandler( ForeignPacketHandlerCode* code );

	unsigned int	m_Timestamp;
	Dispatcher		m_Dispatcher;

	unsigned int m_TotalBytesOut;
	unsigned int m_TotalBytesIn;
	unsigned int m_LostPackets;
	unsigned int m_LatePackets;
	unsigned int m_DupePackets;
	unsigned int m_FrameCounter;

#ifdef __PLAT_XBOX__
	XNADDR		m_XboxAddr;
	XNKID		m_XboxKeyId;
	XNKEY		m_XboxKey;
	BYTE		m_Nonce[8];
#endif

protected:

	virtual bool	init( void );
	virtual	void	deinit( void );

	bool			bind_app_socket( int address, unsigned short port );

	Lst::Node< App >	m_node;
	SOCKET		m_socket;
	struct	sockaddr_in	m_local_address;
	bool	m_connected;
	
	Manager *m_net_man;	

	Tsk::Task< App >*	m_process_network_data_task;
	Tsk::Task< App >*	m_send_network_data_task;
	Tsk::Task< App >*	m_receive_network_data_task;        
	Tsk::Task< App >*	m_network_metrics_task;

    void    process_stream_messages( void );
	void	terminate_invalid_connections( void );
	void	crc_and_copy_stream( char* in_stream, char* out_stream, int in_len, int* out_len );
	bool	validate_and_copy_stream( char* in_stream, char* out_stream, int in_len );

    int 	m_num_connections;
	int 	m_max_connections;

	static	MsgHandlerCode	handle_sequenced_messages; 	
	static	MsgHandlerCode	handle_connection_request;
	static	MsgHandlerCode	handle_connection_accepted;
	static	MsgHandlerCode	handle_stream_messages; 	

	char	m_out_packet_buffer[ Manager::vMAX_PACKET_SIZE ];
	char	m_in_packet_buffer[ Manager::vMAX_PACKET_SIZE ];

	Flags< int > m_flags;

	ForeignPacketHandlerCode*	m_foreign_handler;
#ifdef __PLAT_NGPS__

public:
	void			TransferData( void );
	
	void			WaitForAsyncCallsToFinish( void );
	void			WaitForTransferSemaphore( void );
	void			SignalTransferSemaphore( void );

protected:
	char	m_socket_thread_stack[ vSOCKET_THREAD_STACK_SIZE ]	__attribute__ ((aligned(16)));

	bool	m_socket_thread_active;

	Thread::THREADID	m_socket_thread_id;
    
	int		m_transfer_semaphore_id;
	int		m_send_semaphore_id;
	int		m_receive_semaphore_id;
	int 	m_active_semaphore_id;
	
	Thread::PerThreadStruct	m_socket_thread_data;
		
	static 	void	threaded_transfer_data( void *data );

	bool	m_shutting_down;
#endif // __PLAT_NGPS__

	static	Tsk::Task< App >::Code	process_network_data;
	static	Tsk::Task< App >::Code	send_network_data;
	static	Tsk::Task< App >::Code	read_network_data;
	static	Tsk::Task< App >::Code	service_network_metrics;
    
private:

	void	process_sequenced_messages( Conn *conn );   
    
	int	get_free_handle( void );
	
    Lst::Head< Conn >		m_connections;
	Lst::Head< BannedConn >	m_banned_connections;
	
	int		m_id;
	char	m_name[MAX_LEN_APP_NAME];
	bool	m_loop_read;
	double	m_double_timestamp;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	unsigned short	Conn::GetPort( void )
{
	return m_port;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	int		Conn::GetIP( void )
{
	return m_ip;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	int Conn::GetHandle( void )
{
	return m_handle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	int Conn::GetAveLatency( void )
{
	if( IsLocal())
	{
		return 0;
	}
	return m_latency_test.m_AveLatency;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void	Conn::SetIP( int ip )
{
	m_ip = ip;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void	Conn::SetPort( unsigned short port )
{
	m_port = port;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void		Conn::SetStatus( int status_mask )
{
	m_status.SetMask( status_mask );
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void		Conn::ClearStatus( int status_mask )
{
	m_status.ClearMask( status_mask );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool		Conn::TestStatus( int status_mask )
{
	return m_status.TestMask( status_mask );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline int		Conn::GetStatus( void )
{
	return m_status;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	Metrics*	Conn::GetInboundMetrics( void )
{
	return &m_metrics_in;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	Metrics*	Conn::GetOutboundMetrics( void )
{
	return &m_metrics_out;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


}	// namespace Net


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

#ifdef __PLAT_XBOX__
// Need this to be outside the Net:: namespace...
inline char* inet_ntoa( struct in_addr addr )
{
	IN_ADDR		in_a;
	in_a.S_un	= addr.S_un;

	const int	STRING_BUFFER_SIZE = 100;
//	static char	string_buffer[STRING_BUFFER_SIZE];
//	int			rv = XNetInAddrToString( in_a, string_buffer, STRING_BUFFER_SIZE );

	// Cheesy hack for now.
	static char	string_buffer[STRING_BUFFER_SIZE] = "0.0.0.0";

	return string_buffer;
}
#endif

#endif // __NET_H__



================================================
FILE: Code/Gel/Net/netconn.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Net (OBJ) 												**
**																			**
**	File name:		netconn.cpp												**
**																			**
**	Created:		08/09/01	-	spg										**
**																			**
**	Description:	Network Connection code									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

#include 
#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Net
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Conn::Conn( int flags )
: m_node( this )
{
	m_flags = flags;
	m_status.ClearAll();
	m_status.SetMask( mSTATUS_READY );
	m_read_buffer = new char[ vREAD_BUFFER_LENGTH ];	
	m_read_ptr = m_read_buffer;
	m_write_buffer = new char[ Manager::vMAX_PACKET_SIZE ];
	m_write_ptr = m_write_buffer;
	m_send_interval = 0;
	m_last_send_time = 0;
	m_force_send_packet = false;
	m_alias_connection = NULL;
	m_latest_packet_stamp = 0;
	m_latest_sent_packet_stamp = 0;
#ifdef __PLAT_NGPS__
	m_bandwidth = 4200;	// Start with a 33.6kbps modem's payload threshold (i.e. including packet overhead)
#else
	m_bandwidth = 400000;	// On xbox, just assume broadband
#endif
	
	m_NextStreamId = 0;
	memset( m_SequenceId, 0, MAX_SEQ_GROUPS );	
	memset( m_WaitingForSequenceId, 0, MAX_SEQ_GROUPS );
#ifdef	__PLAT_NGPS__											  
	Dbg_MsgAssert(Mem::SameContext(this,Mem::Manager::sHandle().NetworkHeap()),("Conn not on network heap"));	
#endif		//	__PLAT_NGPS__		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Conn::~Conn( void )
{
	DestroyAllMessageData();
    
	delete [] m_read_buffer;
	delete [] m_write_buffer;
}

/******************************************************************/
/*  Destroy pending sequenced messages							  */
/*                                                                */
/******************************************************************/

void 	Conn::destroy_pending_sequenced_messages( void )
{
	int i;
	Lst::Search< MsgSeqLink > sh;
    MsgSeqLink *msg_link, *next;
    
	

    for( i = 0; i < MAX_SEQ_GROUPS; i++ )
	{
        for( msg_link = sh.FirstItem( m_SequencedBuffer[i] );
				msg_link; msg_link = next )
		{
			next = sh.NextItem();
			
			delete msg_link->m_QMsg;
			delete msg_link;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Conn::DestroyAllMessageData( void )
{
	destroy_pending_sequenced_messages();

	AckAllMessages();
	DestroyMessageQueues();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Conn::DestroyMessageQueues( void )
{
	int i;
	Lst::Search< MsgLink >	msg_sh;
	Lst::Search< MsgImpLink >	imp_sh;
	Lst::Search< MsgSeqLink >	seq_sh;
	Lst::Search< StreamLink > stream_sh;

	MsgLink *msg_link, *next_msg_link;
	MsgImpLink *imp_link, *next_imp_link;
	MsgSeqLink *seq_link, *next_seq_link;
	StreamLink* str_link, *next_str_link;

	for( msg_link = msg_sh.FirstItem( m_normal_msg_list ); msg_link; msg_link = next_msg_link )
	{
		next_msg_link = msg_sh.NextItem();
        
		delete msg_link;
	}

	for( imp_link = imp_sh.FirstItem( m_important_msg_list ); imp_link; imp_link = next_imp_link )
	{
		next_imp_link = imp_sh.NextItem();
		
		delete imp_link;
	}

	for( seq_link = seq_sh.FirstItem( m_sequenced_msg_list ); seq_link; seq_link = next_seq_link )
	{
		next_seq_link = seq_sh.NextItem();
		
		delete seq_link;
	}

	for( i = 0; i < MAX_SEQ_GROUPS; i++ )
	{
		for( seq_link = seq_sh.FirstItem( m_SequencedBuffer[i] ); seq_link; seq_link = next_seq_link )
		{
			next_seq_link = seq_sh.NextItem();
			
			delete seq_link;
		}
	}

	//Dbg_Printf( "** Destroying StreamIn list\n" );
	for( str_link = stream_sh.FirstItem( m_StreamInList ); str_link; str_link = next_str_link )
	{
		next_str_link = stream_sh.NextItem();

		delete str_link->m_Desc;
		delete str_link;
	}

	//Dbg_Printf( "** Destroying StreamOut list\n" );
	for( str_link = stream_sh.FirstItem( m_StreamOutList ); str_link; str_link = next_str_link )
	{
		next_str_link = stream_sh.NextItem();

		delete str_link->m_Desc;
		delete str_link;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Conn::DestroyImportantMessageQueues( void )
{
	MsgImpLink *msg_imp_link, *next_imp;
	MsgSeqLink *msg_seq_link, *next_seq;
	Lst::Search< MsgLink > msg_sh;
	Lst::Search< MsgImpLink > imp_sh;
	Lst::Search< MsgSeqLink > seq_sh;
	QueuedMsg* msg;
	QueuedMsgSeq* msg_seq;
		
	for( msg_imp_link = imp_sh.FirstItem( m_important_msg_list ); 
			msg_imp_link; msg_imp_link = next_imp )
	{
		next_imp = imp_sh.NextItem();
		msg = msg_imp_link->m_QMsg;
		msg->m_Ref.Release();
		if( msg->m_Ref.InUse() == false )
		{
			delete msg;
		}
		
		delete msg_imp_link;
	}	

	for( msg_seq_link = seq_sh.FirstItem( m_sequenced_msg_list ); 
			msg_seq_link; msg_seq_link = next_seq )
	{
		next_seq = seq_sh.NextItem();
		// ACK the message
		msg_seq = msg_seq_link->m_QMsg;
		msg_seq->m_Ref.Release();

		Dbg_Printf( "*** Destroying seq message %d, still on queue\n", msg_seq->m_MsgId );
		// If this message is no longer being referenced, free it
		if( msg_seq->m_Ref.InUse() == false )
		{
			delete msg_seq;
		}

		delete msg_seq_link;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Conn::AckAllMessages( void )
{
	MsgLink* msg_link, *next_msg_link;
	MsgImpLink *msg_imp_link, *next_imp;
	MsgSeqLink *msg_seq_link, *next_seq;
	Lst::Search< MsgLink > msg_sh;
	Lst::Search< MsgImpLink > imp_sh;
	Lst::Search< MsgSeqLink > seq_sh;
	QueuedMsg* msg;
	QueuedMsgSeq* msg_seq;
		
	for( msg_link = msg_sh.FirstItem( m_normal_msg_list ); msg_link; msg_link = next_msg_link )
	{
		next_msg_link = msg_sh.NextItem();
		msg = msg_link->m_QMsg;
		msg->m_Ref.Release();
		if( msg->m_Ref.InUse() == false )
		{
			delete msg;
		}
	}

	for( msg_imp_link = imp_sh.FirstItem( m_important_msg_list ); 
			msg_imp_link; msg_imp_link = next_imp )
	{
		next_imp = imp_sh.NextItem();
		msg = msg_imp_link->m_QMsg;
		msg->m_Ref.Release();
		if( msg->m_Ref.InUse() == false )
		{
			delete msg;
		}
	}	

	for( msg_seq_link = seq_sh.FirstItem( m_sequenced_msg_list ); 
			msg_seq_link; msg_seq_link = next_seq )
	{
		next_seq = seq_sh.NextItem();
		// ACK the message
		msg_seq = msg_seq_link->m_QMsg;
		msg_seq->m_Ref.Release();
		// If this message is no longer being referenced, free it
		if( msg_seq->m_Ref.InUse() == false )
		{
			delete msg_seq;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Tmr::Time	Conn::GetLastCommTime( void )
{
	if( IsLocal())
	{
		return Tmr::GetTime();
	}
	return m_last_comm_time;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Conn::UpdateCommTime( Tmr::Time extra_time )
{
	Tmr::Time cur_time;

	cur_time = Tmr::GetTime();
	if(( cur_time + extra_time ) > m_last_comm_time )
	{
		m_last_comm_time = cur_time + extra_time;
	}
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Tmr::Time	Conn::GetTimeElapsedSinceCommunication( void )
{
	Tmr::Time cur_time;

	cur_time = Tmr::GetTime();

	if( cur_time < m_last_comm_time )
	{
		return 0;
	}

    return cur_time - m_last_comm_time;
}

/******************************************************************/
/* Used to invalidate a connection. This way, you can defer the   */
/* Destruction of the connection until later, but ignore it now   */
/******************************************************************/

void	Conn::Invalidate( void )
{
	//Dbg_Message( "Invalidating Connection\n" );
	ClearStatus( mSTATUS_READY );
	SetStatus( mSTATUS_INVALID );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Conn::IsValid( void )
{
	

	return( TestStatus( mSTATUS_INVALID ) == false );
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Conn::IsForeign( void )
{
	return m_flags.TestMask( mFOREIGN );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Conn::IsLocal( void )
{
	return m_flags.TestMask( mLOCAL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Conn::IsRemote( void )
{
	return m_flags.TestMask( mREMOTE );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Conn::GetResendThreshold( void )
{
	int threshold;
	 
	// Don't resend as often if the client is busy
	if( TestStatus( mSTATUS_BUSY ))
	{
		return Tmr::Seconds( 1 );
	}

	// by default, the resend threshold is two times your average latency
	threshold = 2 * GetAveLatency();

	// We have a threshold for resending.  So that clients don't spam the server with resends
	// while he's busy
	if( threshold < Manager::vMINIMUM_RESEND_THRESHOLD )
	{
		return Manager::vMINIMUM_RESEND_THRESHOLD;
	}

	return threshold;
}

/******************************************************************/
/* Send every N milliseconds                                  	  */
/*                                                                */
/******************************************************************/

void	Conn::SetSendInterval( int interval )
{
	m_send_interval = interval;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

int		Conn::GetSendInterval( void )
{
	return m_send_interval;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	Conn::SetForceSendThisFrame( bool force_send )
{
	m_force_send_packet = force_send;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

bool	Conn::GetForceSendThisFrame( void )
{
	return m_force_send_packet;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

int		Conn::GetNumResends( void )
{
	MsgImpLink *msg_imp_link;
	MsgSeqLink *msg_seq_link;
	Lst::Search< MsgImpLink > imp_sh;
	Lst::Search< MsgSeqLink > seq_sh;
	int highest_num_resends;
    
	

	highest_num_resends = 0;

	for( msg_imp_link = imp_sh.FirstItem( m_important_msg_list ); 
			msg_imp_link; msg_imp_link = imp_sh.NextItem())
	{
		if( msg_imp_link->m_NumResends > highest_num_resends )
		{
			highest_num_resends = msg_imp_link->m_NumResends;
		}
	}	

	for( msg_seq_link = seq_sh.FirstItem( m_sequenced_msg_list ); 
			msg_seq_link; msg_seq_link = seq_sh.NextItem())
	{
		if( msg_seq_link->m_NumResends > highest_num_resends )
		{
			highest_num_resends = msg_seq_link->m_NumResends;
		}
	}

	return highest_num_resends;
}

/******************************************************************/
/* Get the number of stream messages still pending for this conn. */
/* If num_pending > our sliding window's size, don't send any more*/
/******************************************************************/

int		Conn::GetNumPendingStreamMessages( void )
{
	MsgSeqLink *msg_seq_link;
	Lst::Search< MsgSeqLink > seq_sh;
	int num_pending;

	num_pending = 0;
	for( msg_seq_link = seq_sh.FirstItem( m_sequenced_msg_list ); msg_seq_link; msg_seq_link = seq_sh.NextItem())
	{
		if( msg_seq_link->m_StreamMessage )
		{
			num_pending++;
		}
	}

	return num_pending;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	Conn::ClearNumResends( void )
{
	MsgImpLink *msg_imp_link;
	MsgSeqLink *msg_seq_link;
	Lst::Search< MsgImpLink > imp_sh;
	Lst::Search< MsgSeqLink > seq_sh;
	
	

    for( msg_imp_link = imp_sh.FirstItem( m_important_msg_list ); 
			msg_imp_link; msg_imp_link = imp_sh.NextItem())
	{
		msg_imp_link->m_NumResends = 0;
	}	

	for( msg_seq_link = seq_sh.FirstItem( m_sequenced_msg_list ); 
			msg_seq_link; msg_seq_link = seq_sh.NextItem())
	{
		msg_seq_link->m_NumResends = 0;
	}
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

int		Conn::GetBandwidthType( void )
{
	return m_bandwidth_type;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	Conn::SetBandwidthType( int type )
{
	m_bandwidth_type = type;
	
	// Some default values for bandwidth limiting
	switch( m_bandwidth_type )
	{
		case vNARROWBAND:
			SetBandwidth( 4200 );	
			break;
		case vBROADBAND:
			SetBandwidth( 400000 );
			break;
		case vLAN:
			SetBandwidth( 800000 );
			break;
		default:
			break;
	}
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	Conn::SetBandwidth( int bytes_per_sec )
{
	m_bandwidth = bytes_per_sec;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

int		Conn::GetBandwidth( void )
{
	return m_bandwidth;
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

void	Conn::FlagMessagesForResending( unsigned int packet_stamp )
{
	MsgImpLink *msg_imp_link;
	MsgSeqLink *msg_seq_link;
	Lst::Search< MsgImpLink > imp_sh;
	Lst::Search< MsgSeqLink > seq_sh;

	// Mark each message of a matching packet stamp with a timestamp of zero.
	// Doing so signifies that it should be sent next frame
	for( msg_imp_link = imp_sh.FirstItem( m_important_msg_list ); 
			msg_imp_link; msg_imp_link = imp_sh.NextItem() )
	{
		if( msg_imp_link->m_Packetstamp == packet_stamp )
		{
			msg_imp_link->m_Timestamp = 0;
		}
	}	

	for( msg_seq_link = seq_sh.FirstItem( m_sequenced_msg_list ); 
			msg_seq_link; msg_seq_link = seq_sh.NextItem() )
	{
		if( msg_seq_link->m_Packetstamp == packet_stamp )
		{
			msg_seq_link->m_Timestamp = 0;
		}
	}
}

/******************************************************************/
/* 			                                  					  */
/*                                                                */
/******************************************************************/

}	// namespace Net

================================================
FILE: Code/Gel/ObjPtr.h
================================================
#ifndef __GEL_OBJPTR_H
#define __GEL_OBJPTR_H

namespace Obj
{



class CRefCounted;



/*
	-Self assignment
	-http://web.ftech.net/~honeyg/articles/smartp.htm
*/

template
class CSmtPtr
{
	friend class CRefCounted;

public:							
							CSmtPtr();
							CSmtPtr(T* pT);
							CSmtPtr(CSmtPtr &ref);
							~CSmtPtr();

	CSmtPtr &			operator=(T* pT);
	CSmtPtr &			operator=(CSmtPtr &ref);
	T *						operator->() const;
	T &						operator*() const;
	T **					operator&() const;

	bool					operator!() const {return (mp_object == NULL);}
	
							operator T*() const {return mp_object;}

	friend bool 			operator==(const CSmtPtr& lhs, T* pRhs) {return lhs.mp_object == pRhs;}
	friend bool 			operator!=(const CSmtPtr& lhs, T* pRhs) {return lhs.mp_object != pRhs;}

	T *						Convert() const {return mp_object;}
	void					Kill() const;

private:

	void					add_ref();
	void					remove_ref();
	
	T *						mp_object;
	
	CSmtPtr *	mp_prev_ptr;
	CSmtPtr *	mp_next_ptr;
};




template
inline CSmtPtr::CSmtPtr()
{
	mp_prev_ptr = NULL;
	mp_next_ptr = NULL;
	
	mp_object = NULL;
}




template
inline CSmtPtr::CSmtPtr(T *pT)
{
	mp_prev_ptr = NULL;
	mp_next_ptr = NULL;
	
	mp_object = pT;
	add_ref();
	//printf("WWW in copy constructor for 0x%x\n", mp_object);
}




template
inline CSmtPtr::CSmtPtr(CSmtPtr &ref)
{
	mp_prev_ptr = NULL;
	mp_next_ptr = NULL;
	
	mp_object = ref.mp_object;
	add_ref();
	//printf("WWW in copy constructor 2 for pointer at 0x%x, object 0x%x\n", this, mp_object);
}




template
inline CSmtPtr::~CSmtPtr()
{
	//printf("WWW in destructor for 0x%x\n", mp_object);
	remove_ref();
}




template
inline CSmtPtr & CSmtPtr::operator=(T *pT)
{
	//printf("WWW in CSmtPtr::operator=(T *pT) for pointer at 0x%x, object 0x%x (old) and 0x%x (new)\n", this, mp_object, pT);
	remove_ref();
	mp_object = pT;
	add_ref();
	return *this;
}




template
inline CSmtPtr & CSmtPtr::operator=(CSmtPtr &ref)
{
	//printf("WWW in CSmtPtr::operator=(const CSmtPtr &ref) for pointer at 0x%x, object 0x%x (old) and 0x%x (new)\n", this, mp_object, ref.mp_object);
	remove_ref();
	mp_object = ref.mp_object;
	add_ref();
	return *this;
}




template
inline T * CSmtPtr::operator->() const
{
	Dbg_MsgAssert(mp_object, ("NULL smart pointer!"));
	return (T *) mp_object;
}




template
inline T & CSmtPtr::operator*()	const
{
	Dbg_MsgAssert(mp_object, ("NULL smart pointer!"));
	return *((T *) mp_object);
}




template
inline T ** CSmtPtr::operator&()	const
{
	Dbg_MsgAssert(0, ("operator& not supported, comrade!"));
}




template
inline void CSmtPtr::Kill() const 
{
	Dbg_MsgAssert(mp_object, ("attempting delete with NULL smart pointer!"));
	// this will lead to remove_ref() being called on this
	delete mp_object;
}




template
inline void CSmtPtr::add_ref()
{
	if (mp_object)
	{
		//printf("WWW adding ref to 0x%x\n", mp_object);
		mp_object->AddSmartPointer((CSmtPtr *) this);
	}
}




template
inline void CSmtPtr::remove_ref()
{
	if (mp_object)
	{
		//printf("WWW removing ref from 0x%x\n", mp_object);
		mp_object->RemoveSmartPointer((CSmtPtr *) this);
		mp_object = NULL;
	}
}




}

#endif


================================================
FILE: Code/Gel/Object/Event.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#include 
#ifdef __NOPT_ASSERT__
#include 
#endif

namespace Obj
{
	
#ifdef __NOPT_ASSERT__
extern bool DebugSkaterScripts;
#endif




CEvent::CEvent()
{
	m_flags = 0;
}




CEvent::~CEvent()
{
	if (m_flags & mCONTROLS_OWN_DATA && mp_data)
		delete mp_data;
}




void CEvent::MarkRead(uint32 receiverId, uint32 script)
{
	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
	p_tracker->LogEventRead(this, receiverId, script);
	m_flags |= mREAD;
}




void CEvent::MarkHandled(uint32 receiverId, uint32 script)
{
	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
	p_tracker->LogEventHandled(this, receiverId, script);
	m_flags |= mHANDLED;
}




int	CEvent::sExtractControllerIndex(CEvent *pEvent)
{
	int controller = 0;
	Script::CStruct *pData = pEvent->GetData();
	if (pData)
		pData->GetInteger("controller", &controller);

	return controller;
}




CEventListener::CEventListener()
{
	m_registered = false;
	m_ref_count = 0;
}




CEventListener::~CEventListener()
{
	CTracker* p_tracker = CTracker::Instance();
	p_tracker->UnregisterEventListener(this);
	Dbg_MsgAssert(m_ref_count == 0, ("Event listener still being referenced"));
}




/*
	The flags are for optimization purposes. Don't allow events that aren't
	targeted to the attached object if it isn't necessary -- this will 
	speed things up.
	
	pObject = NULL means no associated CObject, which is legal, but you must
	set mALL_ALL_EVENTS
*/
void CEventListener::RegisterWithTracker(CObject *pObject)
{
	mp_object = pObject;

	CTracker* p_tracker = CTracker::Instance();
	p_tracker->RegisterEventListener(this);
}




/*
	Called by CTracker singleton on every event listener that is registered,
	whenever CTracker receives an event.
*/
void CEventListener::event_filter(CEvent *pEvent)
{	
	if (mp_object)
	{
		#ifdef __NOPT_ASSERT__
//		if (mp_object->GetFlags() & Front::CScreenElement::vIS_SCREEN_ELEMENT)
//			((Front::CScreenElement *) mp_object.Convert())->debug_verify_integrity();
		#endif
	}
	
	// TRICKY DELETE
	// Can in theory lead to the destruction of any CObject or any listener.
	// Will assert if this listener gets destroyed
	pass_event_to_listener(pEvent);
	
	if (mp_object)
	{
		#ifdef __NOPT_ASSERT__
//		if (mp_object->GetFlags() & Front::CScreenElement::vIS_SCREEN_ELEMENT)
//			((Front::CScreenElement *) mp_object.Convert())->debug_verify_integrity();
		#endif
	}
}




CEventHandlerTable::CEventHandlerTable()
{
	m_num_entries = 0; 
	mp_tab = NULL;
	m_valid = true;
	m_in_immediate_use_counter = 0;
	m_changed = false;
}




CEventHandlerTable::~CEventHandlerTable()
{
	Dbg_Assert(!m_in_immediate_use_counter);

	
	if (mp_tab)
	{
		for (int i = 0; i < m_num_entries; i++)
		{
			if (mp_tab[i].p_params)
			{
				delete mp_tab[i].p_params;
			}
		}
		delete [] mp_tab;
	}
}


void CEventHandlerTable::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	if (mp_tab && m_num_entries)
	{
	
		Script::CArray *p_array=new Script::CArray;
		p_array->SetSizeAndType(m_num_entries,ESYMBOLTYPE_STRUCTURE);
		for (int i=0; iAddChecksum(CRCD(0x7321a8d6,"Type"), mp_tab[i].type);		
				p_ex->AddChecksum(CRCD(0xd1e70f97,"Event_Script"), mp_tab[i].script);		
				p_ex->AddChecksum(CRCD(0x923fbb3a, "Group"), mp_tab[i].group);
				p_array->SetStructure(i,p_ex);
			}
			else
			{
				Script::CStruct *p_ex = new Script::CStruct;
				p_ex->AddChecksum(NO_NAME, CRCD(0xd3d5f556, "DeadEntry"));
				p_array->SetStructure(i,p_ex);
			}
		}
		p_info->AddArrayPointer(CRCD(0x3e55ff39,"mp_event_handler_table"),p_array);

	}
#endif				 
}



// Add a single event to the table
// replacing any existing event handler with the same ex type
void	CEventHandlerTable::AddEvent(uint32 ex, uint32 scr, uint32 group, bool exception, Script::CStruct *p_params)
{
	Entry *p_entry = NULL;	

// if there is no mp_tab, then we'll need to create a single entry one 
	if (!mp_tab)
	{
		m_num_entries = 1;
		mp_tab = new Entry[1];
		p_entry = mp_tab;
	}
	else
	{
	// otherwise see it it exists, and if so, then simply replace it
		int i;

		for (i = 0; ip_params)
				{
					delete p_entry->p_params;
				}
				goto GOT_ENTRY;				// goto useful
			}
		}
		
		// check for empty slots, and use them	

		for (i = 0; ienabled = true;
	p_entry->exception = exception;
	p_entry->group = group;
	if (p_params)
	{
//		printf ("Allocating parameters\n");
		// if params are passed, then we need to make a copy of them
		p_entry->p_params = new Script::CStruct(*p_params);
	}
	else
	{
		p_entry->p_params = NULL;  
	}
	p_entry->script = scr;
	p_entry->type = ex;

	// Need to flag it as changed, so we can break out of pass_event
	// if an event causes a change in the table (which we are iterating over)   
	m_changed = true;

}


void CEventHandlerTable::add_from_script(Script::CArray *pArray, bool replace)
{
	Dbg_Assert(pArray);

	
	int new_entries = pArray->GetSize();
	if (replace && !new_entries)
	{
		// if no new entries, and we are "replacing", then delete any existing table
		// and return 
		if (mp_tab)
		{
			for (int i = 0; i < m_num_entries; i++)
			{
				if (mp_tab[i].p_params)
				{
					delete mp_tab[i].p_params;
				}
			}
			delete [] mp_tab;
			mp_tab = NULL;
			m_num_entries = 0;
			m_changed = true;
		}
		return;
	}

/*
	// Optimization for adding a single entry
	if (new_entries == 1)
	{

		// get the type, script pair, and any params and flags
		Script::CStruct *pEventStruct = pArray->GetStructure(0);
		
		Script::CComponent *p_left = pEventStruct->GetNextComponent(NULL);
		Dbg_MsgAssert(p_left, ("missing 'type' half of event handler pair"));
		Dbg_Assert(p_left->mType == ESYMBOLTYPE_NAME);

		Script::CComponent *p_right = pEventStruct->GetNextComponent(p_left);
		Dbg_MsgAssert(p_right, ("missing 'handler' half of event handler pair"));
		Dbg_Assert(p_right->mType == ESYMBOLTYPE_NAME);

		uint32	type = p_left->mChecksum;

		Entry *p_entry = NULL;


		// if it's a "replace" entry, then we scan through to see if we already have an entry
		// then we delete it, and can use that slot
		if (replace)
		{
			for (int i = 0; i < m_num_entries; i++)
			{
				if (mp_tab[i].type == type)
				{
					mp_tab[i].script = vDEAD_ENTRY;
					// delete the original parameters, whilst (while!) we are at it
					if (mp_tab[i].p_params)
					{
						delete mp_tab[i].p_params;
						mp_tab[i].p_params = NULL;
					}
					p_entry = &mp_tab[i];
					break;
				}
			}
		}
		
		// if not got one via replace, we need to look for an empty slot
		// 95% of the time there should be one, as the table
		// will expand to the max size required, and will not shrink
		// so we only need to add slots during expansion.

		for (int i = 0; i < m_num_entries; i++)
		{
			if (mp_tab[i].script == vDEAD_ENTRY)
			{
				p_entry = &mp_tab[i];
				break;
			}
		}

		if (p_entry)
		{

			// DANG!  This is a chunk of cut and paste code from below
			// this event handler container is in serious need
			// of refactoring
			
			// Mick, if the event handler has a "params" structure
			// then add it to the entry.
			// (note, have to be careful in cleaning these up)
			Script::CStruct *p_params;
			if (pEventStruct->GetStructure(CRCD(0x7031f10c,"params"),&p_params))
			{
				printf ("Allocating Params\n");
				p_entry->p_params = new Script::CStruct(*p_params);
			}
			else
			{
				p_entry->p_params = NULL;
			}
			
			if (!pEventStruct->GetChecksum(CRCD(0x923fbb3a, "Group"), &p_entry->group))
			{
				p_entry->group = vDEFAULT_GROUP;
			}
	
			p_entry->exception = pEventStruct->ContainsFlag(CRCD(0x80367192,"Exception"));
			
			p_entry->type = p_left->mChecksum;
			p_entry->script = p_right->mChecksum;
			Dbg_MsgAssert(p_entry->script,("Adding Null script in event handler"));
			p_entry->enabled = true;
			m_changed = true;
			return;
		}
	}

*/
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());

	// create new table	(this is the one with problems with p_params)
	// the values are not initialized
	// so if it's allocated over an old one, then
	// errors might occur
	
//	printf("Allocating memory for %d new entries\n",new_entries);
	
	Entry *p_edit_tab = new Entry[m_num_entries + new_entries];
	#ifdef __NOPT_ASSERT__ 
	int first_edit_entry = m_num_entries;	
	#endif


	int	new_entry_index = 0;

	// transfer the new entries from script array into new table
	for (int i = 0; i < new_entries; i++)
	{
		// get the type, script pair, and any params and flags
		Script::CStruct *pEventStruct = pArray->GetStructure(i);
		
		Script::CComponent *p_left = pEventStruct->GetNextComponent(NULL);
		Dbg_MsgAssert(p_left, ("missing 'type' half of event handler pair"));
		Dbg_Assert(p_left->mType == ESYMBOLTYPE_NAME);

		Script::CComponent *p_right = pEventStruct->GetNextComponent(p_left);
		Dbg_MsgAssert(p_right, ("missing 'handler' half of event handler pair"));
		Dbg_Assert(p_right->mType == ESYMBOLTYPE_NAME);

		if (replace)
		{
			// remove entry from old table, if it exists in there
			remove_entry(p_left->mChecksum);
		}
		
		Entry *p_entry = p_edit_tab + new_entry_index++;

		// Mick, if the event handler has a "params" structure
		// then add it to the entry.
		// (note, have to be careful in cleaning these up)
		Script::CStruct *p_params;
		if (pEventStruct->GetStructure(CRCD(0x7031f10c,"params"),&p_params))
		{
			Dbg_MsgAssert( (i + first_edit_entry) < (m_num_entries + new_entries),
						   ( "Array overflow" ) );
			
			p_entry->p_params = new Script::CStruct(*p_params);
		}
		else
		{
			p_entry->p_params = NULL;
		}
		
		if (!pEventStruct->GetChecksum(CRCD(0x923fbb3a, "Group"), &p_entry->group))
		{
			p_entry->group = vDEFAULT_GROUP;
		}

		p_entry->exception = pEventStruct->ContainsFlag(CRCD(0x80367192,"Exception"));
		
		p_entry->type = p_left->mChecksum;
		p_entry->script = p_right->mChecksum;
		Dbg_MsgAssert(p_entry->script,("Adding Null script in event handler"));
		p_entry->enabled = true;
		m_changed = true;
	}
	
	// if member table already exists, copy it into new one, at the end
	// (append to items already added)
	// Note that some entries in the original table might have been deleted, so
	// the size of mp_tab might end up being bigger than that indicated by m_num_entries
	// but the extra at the end is just garbage.
	// This is most common when you Set individual events on
	// and already existing table that contains that event
	// (old code would have left an uninitialized gap, causing very obscure bugs)

	if (mp_tab)
	{
		for (int i = 0; i < m_num_entries; i++)
		{
			p_edit_tab[new_entry_index++] = mp_tab[i];
		}
	}
	delete [] mp_tab;  					// old table has been coped over, so we can delete it
	mp_tab = p_edit_tab;				// and make the newly constructed table the active table
	m_num_entries = new_entry_index;	// set the number of entries to the actual counted entries (not the size of the array)
	
	Mem::Manager::sHandle().PopContext();
	
}




// doesn't change the array size, just marks entry dead (and deletes p_params struct)
void CEventHandlerTable::remove_entry(uint32 type)
{
	for (int i = 0; i < m_num_entries; i++)
	{
		if (mp_tab[i].type == type)
		{
			mp_tab[i].type = 0;
			mp_tab[i].script = vDEAD_ENTRY;
			// delete the original parameters, whilst (while!) we are at it
			if (mp_tab[i].p_params)
			{
				delete mp_tab[i].p_params;
				mp_tab[i].p_params = NULL;
			}
		}
	}
	
	// Note, we don't set m_changed here, as the table has not really changed
	// just one entry has changed
	// we will set the m_changed flag if compress_table later removed this entry
	
}


// removes all entries with the given group id
// or remove them all if "all_groups" is specifed
void CEventHandlerTable::remove_group(uint32 group)
{
	for (int i = 0; i < m_num_entries; i++)
	{
		if (mp_tab[i].group == group || group == CRCD(0x8b713e0e,"all_groups"))
		{
			mp_tab[i].script = vDEAD_ENTRY;
			if (mp_tab[i].p_params)
			{
				delete mp_tab[i].p_params;
				mp_tab[i].p_params = NULL;
			}
		}
	}
}




// changes size of table, removing dead entries
void CEventHandlerTable::compress_table()
{
	if (!mp_tab) return;

	// count up dead entries
	int num_dead = 0;
	int in = 0;
	for (; in < m_num_entries; in++)
	{
		if (mp_tab[in].script == vDEAD_ENTRY)
			num_dead++;
	}

	if (num_dead == 0) return;

	int new_size = m_num_entries - num_dead;

	m_changed = true;

	// Mick - If new table has zero size, then don't allocate it	
	// just delete the old table, and set it to NULL
	if ( 0 == new_size)
	{
		for (int i = 0; i < m_num_entries; i++)
		{
			// we're about to remove an entry
			// so delete its params if necessary
			if ( mp_tab[i].p_params )
			{
				delete mp_tab[i].p_params;
				mp_tab[i].p_params = NULL;
			}
		}

		delete[]	mp_tab;
		mp_tab = NULL;
		m_num_entries = 0;
	}
	else
	{
		
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		Entry *p_new_tab = new Entry[new_size];		
		Mem::Manager::sHandle().PopContext();
		
		int out = 0;
		for (in = 0; in < m_num_entries; in++)
		{
			if (mp_tab[in].script != vDEAD_ENTRY)
			{
				p_new_tab[out++] = mp_tab[in];
			}
			else
			{
				// we're about to remove an entry
				// so delete its params if necessary
				if ( mp_tab[in].p_params )
				{
					delete mp_tab[in].p_params;
					mp_tab[in].p_params = NULL;
				}
			}
		}
		delete[] mp_tab;
		mp_tab = p_new_tab;
		m_num_entries = new_size;
	}
}




void CEventHandlerTable::set_event_enable(uint32 type, bool state)
{
	for (int i = 0; i < m_num_entries; i++)
	{
		if (mp_tab[i].type == type)
			mp_tab[i].enabled = state;
	}
}




#ifdef	__SCRIPT_EVENT_TABLE__		
void CEventHandlerTable::pass_event(CEvent *pEvent, Script::CScript *pScript, bool broadcast)
{
	// if it's a screen element, check that events aren't blocked
#ifdef __NOPT_ASSERT__
//	if ( ( pObject->GetFlags() & Front::CScreenElement::vIS_SCREEN_ELEMENT ) && 
//		 ( pObject->GetFlags() & Front::CScreenElement::vEVENTS_BLOCKED ) )
//	{
//		return;
//	}
#endif		// __NOPT_ASSERT__



	#if 1
	bool	old_m_changed = m_changed;
	m_changed = false;		
	#else	 
	// only clear the m_changed flag if we are not recursing into here
	if (!m_in_immediate_use_counter)
	{	 	
			m_changed = false;		
	}
	#endif
	
	m_in_immediate_use_counter++;

#ifndef __PLAT_WN32__
	// Need to assert that mp_tab is valid (or we have no entries in it)
	Dbg_MsgAssert(!m_num_entries || Mem::Valid(mp_tab),("Invalid event handler table for Event %s", Script::FindChecksumName(pEvent->GetType())));
#endif

	
	Entry *p_entry = mp_tab;
	for (int i = 0; i < m_num_entries; i++)
	{  
		if (p_entry->type == pEvent->GetType() && p_entry->script != vDEAD_ENTRY && p_entry->enabled)
		{
			
			Script::CScript *p_new_script = NULL;
			Script::CStruct	*p_params = NULL;
			Script::CStruct	*p_passed_params = NULL;
			
			
			
	//		printf ("%s calls %s\n",Script::FindChecksumName(p_entry->type),Script::FindChecksumName(p_entry->script));
			
			if (pEvent->GetData())
			{
				// there is event data, so copy it into a new structure
				p_params = new Script::CStruct();
				// and then merge the parameters in with it
				if (p_entry->p_params)
				{
					*p_params += *(p_entry->p_params);
				}
				*p_params += *(pEvent->GetData());
				p_passed_params = p_params;
				// using p_params like this is safe, since SpawnScript makes its own copy
			}
			else
			{
				p_passed_params = p_entry->p_params;
			}			

			if (p_entry->exception)
			{
				#ifdef __NOPT_ASSERT__
				if (DebugSkaterScripts && (pEvent->GetTarget() == 0 || pEvent->GetSource()))
				{
					printf("%d: Exception %s, Script %s, Target %s, Source %s\n",
						(int)Tmr::GetRenderFrame(),
						Script::FindChecksumName(pEvent->GetType()),
						Script::FindChecksumName(p_entry->script),
						pEvent->GetTarget() ? Script::FindChecksumName(pEvent->GetTarget()) : "Skater",
						pEvent->GetSource() ? Script::FindChecksumName(pEvent->GetSource()) : "Skater"
					);
				}
				#endif
				
				
				// If it's an exception, we check to see if the object
				// has flagged an "OnExceptionRun" 

				if (pScript->GetOnExceptionScriptChecksum())
				{
					// Pass in the name of the exception so that certain exceptions can be ignored.
					Script::CStruct *pFoo=new Script::CStruct;
					pFoo->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int)p_entry->exception);
					// Script::RunScript(pScript->GetOnExceptionScriptChecksum(), pFoo, pScript->mpObject );
					// Dan: interrupt instead of run
					uint32 checksum = pScript->GetOnExceptionScriptChecksum();
					pScript->SetOnExceptionScriptChecksum(0);	// clear it once it has been run
					pScript->Interrupt(checksum, pFoo);
					delete pFoo;
				}
				
				// the OnException script may alter the table
				if (p_entry->script != vDEAD_ENTRY)
				{
					// Exceptions act like a GOTO, so we just set the script we are running on to this new script
					// the object reamins the same
					pScript->SetScript(p_entry->script,p_passed_params,pScript->mpObject);
					pScript->Update();
				}
			}
			else
			{
				// Current Screen element code relies on spawned scripts
				// as they don't update their own scripts
				// but we are moving to an "Interrupt" model, so we
				// only support this "Spawned" model for the screen elements
				
				if (pScript->mpObject &&  pScript->mpObject->GetFlags() & Front::CScreenElement::vIS_SCREEN_ELEMENT)
				{
					// Normal events spawn a new script running on the same object as the current script (if any)
					p_new_script = Script::SpawnScript(p_entry->script, p_passed_params, 0, NULL);
					#ifdef __NOPT_ASSERT__
					p_new_script->SetCommentString("Spawned by CEventHandlerTable::pass_event, 1");
					#endif
					p_new_script->mpObject = pScript->mpObject;	   
					p_new_script->Update(); 
				}
				else
				{
					// Instead of spawning, just interrupt the current script
					pScript->Interrupt(p_entry->script, p_passed_params);					
				}				
			}

			if (p_params)
			{
				delete p_params;
			}
			
			Dbg_MsgAssert((*(uint32*)this) != 0x01010101,("%s\nCEventHandlerTable deleted whilst being used",p_new_script->GetScriptInfo()));
			//p_new_script->mpObject = NULL;

			// do logging
			//pEvent->MarkHandled(pObject->GetID(), p_entry->script);
			pEvent->MarkHandled(0, p_entry->script);	// receiver id not important
		}

		p_entry++;

		if (!m_valid)
		{
			// Looks like the spawned script deleted the CObject, invalidating this event handler table.
			// Exit now. If the outermost pass_event() in a recursive chain, then kill self.
			m_in_immediate_use_counter--;
			if (!m_in_immediate_use_counter)
				delete this;
			return;
		}
		
		if (m_changed)
		{
			break;
		}
		
	}

	// if we were previously flagged as changed, or some other function flagged us as changed
	// then say we have changed	
	m_changed = m_changed || old_m_changed;

	m_in_immediate_use_counter--;
}

#else

void CEventHandlerTable::pass_event(CEvent *pEvent, CObject *pObject, bool broadcast)
{
	// if it's a screen element, check that events aren't blocked
#ifdef __NOPT_ASSERT__
	if ( ( pObject->GetFlags() & Front::CScreenElement::vIS_SCREEN_ELEMENT ) )
	{
		Front::CScreenElement* pScreenElement = (Front::CScreenElement*)pObject;
		if ( pScreenElement->EventsBlocked() )
		{
			return;
		}		
	}
#endif		// __NOPT_ASSERT__

	// only clear the m_changed flag if we are not recursing into here
	if (!m_in_immediate_use_counter)
	{	 	
			m_changed = false;		
	}
	
	m_in_immediate_use_counter++;
	
	Entry *p_entry = mp_tab;
	for (int i = 0; i < m_num_entries; i++)
	{  
		 
//		if (broadcast && !p_entry->broadcast)
//		{
//			p_entry++;
//			continue;
//		}		
	
		if (p_entry->type == pEvent->GetType() && p_entry->script != vDEAD_ENTRY && p_entry->enabled)
		{
			
			Script::CScript *p_new_script = NULL;
			Script::CStruct	*p_params = NULL;
			Script::CStruct	*p_passed_params = NULL;
			
			// visual debugging, if there is a source and target, and they are different
			// then draw a line between the two objects
			#ifdef	__NOPT_ASSERT__
			if (Script::GetInteger(CRCD(0xffc8c5f8,"Display_event_arrows")))
			{
				if (pObject->GetFlags() & Obj::CObject::vCOMPOSITE)
				{
					uint32		source_id = pEvent->GetSource();
	//				printf ("Source = 0x%x  %s,  target = 0x%x, %s\n",source_id, Script::FindChecksumName(source_id),pObject->GetID(),Script::FindChecksumName(pObject->GetID()));
					if ( source_id != pObject->GetID() )
					{
						Obj::CObject * p_source_object = Obj::CTracker::Instance()->GetObject(source_id);
						if (p_source_object)
						{
	//						printf ("objects types %d, %d\n",pObject->GetType(), p_source_object->GetType());
							// and we only want to do it if they are composite object
							if (pObject->GetFlags() & Obj::CObject::vCOMPOSITE && p_source_object->GetFlags() & Obj::CObject::vCOMPOSITE)	// eek!
							{	
								Gfx::AddDebugArrow(((CCompositeObject*)p_source_object)->GetPos(),((CCompositeObject*)pObject)->GetPos(),0xff00ff,0xff00ff,200);
							}
						}
						else
						{
							printf ("No Source object found\n");
						}
					}
				}			
			}
			
			if (DebugSkaterScripts && (pObject->GetID() == 0 || pEvent->GetSource() == 0))
			{
				printf("%d: Exception %s, Script %s, Target %s, Source %s\n",
					(int)Tmr::GetRenderFrame(),
					Script::FindChecksumName(pEvent->GetType()),
					Script::FindChecksumName(p_entry->script),
					pEvent->GetTarget() ? Script::FindChecksumName(pEvent->GetTarget()) : "Skater",
					pEvent->GetSource() ? Script::FindChecksumName(pEvent->GetSource()) : "Skater"
				);
			}
			
			#endif
			
			
			if (pEvent->GetData())
			{
				// there is event data, so copy it into a new structure
				p_params = new Script::CStruct();
				// and then merge the parameters in with it
				if (p_entry->p_params)
				{
					*p_params += *(p_entry->p_params);
				}
				*p_params += *(pEvent->GetData());
				p_passed_params = p_params;
				// using p_params like this is safe, since SpawnScript makes its own copy
			}
			else
			{
				p_passed_params = p_entry->p_params;
			}			

			if (p_entry->exception)
			{
				// If it's an exception, we check to see if the object
				// has flagged an "OnExceptionRun" 


				// Ooh, we need to run a script, so better make sure mp_script points to something.
				if (pObject->GetScript()==NULL)
				{
					pObject->SetScript( new Script::CScript);
					#ifdef __NOPT_ASSERT__
					pObject->GetScript()->SetCommentString("Created in CEventHandlerTable::pass_event(...) (exception)");
					#endif
				}	
				// Now we're safe. Note: mp_script will get cleaned up by the CObject destructor.

				
				if (pObject->GetOnExceptionScriptChecksum())
				{
//					printf ("Running onException script %s\n",Script::FindChecksumName(pObject->GetOnExceptionScriptChecksum()));
					// Pass in the name of the exception so that certain exceptions can be ignored.
					Script::CStruct *pFoo=new Script::CStruct;
					pFoo->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int)p_entry->exception);
					Script::RunScript(pObject->GetOnExceptionScriptChecksum(), pFoo, pObject );
					delete pFoo;
					pObject->SetOnExceptionScriptChecksum(0);	// clear it once it has been run
				}
			
				
				// An exception will act like a "goto"
				Script::CScript *p_old_script = pObject->GetScript();
//				printf("RUNNING exception %s script %s on object %s\n",Script::FindChecksumName(p_entry->type),Script::FindChecksumName(p_entry->script),Script::FindChecksumName(pObject->GetID()));
				p_old_script->SetScript(p_entry->script,p_passed_params,pObject);
				p_old_script->Update();
			}
			else
			{
				// Normal events spawn a script running on an object
//				printf("SPAWNING exception %s script %s on object %s\n",Script::FindChecksumName(p_entry->type),Script::FindChecksumName(p_entry->script),Script::FindChecksumName(pObject->GetID()));
				p_new_script = Script::SpawnScript(p_entry->script, p_passed_params, 0, NULL);
				#ifdef __NOPT_ASSERT__
				p_new_script->SetCommentString("Spawned by CEventHandlerTable::pass_event, 1");
				#endif
				p_new_script->mpObject = pObject;			   
				p_new_script->Update();
			}

			if (p_params)
			{
				delete p_params;
			}
			
			Dbg_MsgAssert((*(uint32*)this) != 0x01010101,("%s\nCEventHandlerTable deleted whilst being used",p_new_script->GetScriptInfo()));
			//p_new_script->mpObject = NULL;

			// do logging
			pEvent->MarkHandled(pObject->GetID(), p_entry->script);
		}

		p_entry++;

		if (!m_valid)
		{
			// Looks like the spawned script deleted the CObject, invalidating this event handler table.
			// Exit now. If the outermost pass_event() in a recursive chain, then kill self.
			m_in_immediate_use_counter--;
			if (!m_in_immediate_use_counter)
				delete this;
			return;
		}
		
		if (m_changed)
		{
			break;
		}
		
	}

	m_in_immediate_use_counter--;
}
#endif




#ifdef	__SCRIPT_EVENT_TABLE__		
// Register all the entries in this even table with the event receiver table in the tracker
// (Hey, maybe store the script in here, allow multiple entries and do away with this table?)
void	CEventHandlerTable::register_all(Script::CScript *p_script)
{
	Entry *p_entry = mp_tab;
	for (int i = 0; i < m_num_entries; i++)
	{
		// if it's a live entry, then add it as a receiver
		if (p_entry->type && p_entry->script && p_entry->script != vDEAD_ENTRY)
		{
			Obj::CTracker::Instance()->RegisterEventReceiver(p_entry->type, p_script);
		}
		p_entry++;
	}  
}

// Flush out all references from the Event Receiver table to this object, based on the event handler table
void	CEventHandlerTable::unregister_all(Script::CScript *p_script)
{
	Entry *p_entry = mp_tab;
	for (int i = 0; i < m_num_entries; i++)
	{
		if (p_entry->type)
		{
			Obj::CTracker::Instance()->UnregisterEventReceiver(p_entry->type, p_script);
		}
		p_entry++;
	}  
}

#else

// Register all the entries in this even table with the event receiver table in the tracker
// (Hey, maybe store the script in here, allow multiple entries and do away with this table?)
void	CEventHandlerTable::register_all(CObject *p_object)
{
	Entry *p_entry = mp_tab;
	for (int i = 0; i < m_num_entries; i++)
	{
		if (p_entry->type && p_entry->script)
		{
			Obj::CTracker::Instance()->RegisterEventReceiver(p_entry->type, p_object);
		}
		p_entry++;
	}  
}

// Flush out all references from the Event Receiver table to this object, based on the event handler table
void	CEventHandlerTable::unregister_all(CObject *p_object)
{
	Entry *p_entry = mp_tab;
	for (int i = 0; i < m_num_entries; i++)
	{
		if (p_entry->type)
		{
			Obj::CTracker::Instance()->UnregisterEventReceiver(p_entry->type, p_object);
		}
		p_entry++;
	}  
}

#endif

// Dump the given table
void	CEventHandlerTable::sPrintTable ( CEventHandlerTable* table )
{
#	if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
	printf("====================================\n");
	printf("Event Handler Table:\n");
	if (table)
	{
		for (int i = 0; i < table->m_num_entries; i++)
		{
			Entry* p_entry = table->mp_tab + i;
			
			printf("------------------------------------\n");
			printf("  Type     : %s\n", Script::FindChecksumName(p_entry->type));
			printf("  Script   : %s\n", Script::FindChecksumName(p_entry->script));
			printf("  Group    : %s\n", Script::FindChecksumName(p_entry->group));
			printf("  Exception: %s\n", p_entry->exception ? "yes" : "no");
		}
	}
	else
	{
		printf("  Emtpy\n");
	}
	printf("====================================\n");
#endif		// __NOPT_FINAL__
}

}


================================================
FILE: Code/Gel/Object/ObjPtr.cpp
================================================
#include 
#include 
#include 

namespace Obj
{




}


================================================
FILE: Code/Gel/Object/RefCounted.cpp
================================================
#include 
#include 
#include 

namespace Obj
{




CRefCounted::CRefCounted()
{
	mp_smart_ptr_list = NULL;
}




CRefCounted::~CRefCounted()
{
	CSmtPtr *p_smt = mp_smart_ptr_list;
	while(p_smt)
	{
		CSmtPtr *p_next = p_smt->mp_next_ptr;
		Dbg_Assert(p_smt->mp_object);
		Dbg_Assert(p_smt->mp_object == this);
		*p_smt = NULL;
		p_smt = p_next;
	}
}




void CRefCounted::AddSmartPointer(CSmtPtr *pSmtPtr)
{
	//debug_validate_smart_pointers();
	
	Dbg_Assert(!pSmtPtr->mp_prev_ptr);
	Dbg_Assert(!pSmtPtr->mp_next_ptr);
	pSmtPtr->mp_next_ptr = mp_smart_ptr_list;
	if (mp_smart_ptr_list)
		mp_smart_ptr_list->mp_prev_ptr = pSmtPtr;
	mp_smart_ptr_list = pSmtPtr;

	//debug_validate_smart_pointers();
}




void CRefCounted::RemoveSmartPointer(CSmtPtr *pSmtPtr)
{
	Dbg_Assert(pSmtPtr->mp_object == this);
	//debug_validate_smart_pointers(pSmtPtr);

	if (pSmtPtr->mp_prev_ptr)
	{
		Dbg_Assert(pSmtPtr->mp_prev_ptr->mp_next_ptr == pSmtPtr);
		pSmtPtr->mp_prev_ptr->mp_next_ptr = pSmtPtr->mp_next_ptr;
	}
	else
		mp_smart_ptr_list = pSmtPtr->mp_next_ptr;

	if (pSmtPtr->mp_next_ptr)
	{
		Dbg_Assert(pSmtPtr->mp_next_ptr->mp_prev_ptr == pSmtPtr);
		pSmtPtr->mp_next_ptr->mp_prev_ptr = pSmtPtr->mp_prev_ptr;
	}

	pSmtPtr->mp_prev_ptr = NULL;
	pSmtPtr->mp_next_ptr = NULL;
	
	//debug_validate_smart_pointers();
}




void CRefCounted::debug_validate_smart_pointers(CSmtPtr *pPtrToCheckForInclusion)
{
	bool included = false;
	
	CSmtPtr *p_entry = mp_smart_ptr_list;
	while(p_entry)
	{
		Dbg_Assert(p_entry->mp_object == this);
		if (p_entry->mp_prev_ptr)
			Dbg_Assert(p_entry->mp_prev_ptr->mp_next_ptr == p_entry);
		if (p_entry->mp_next_ptr)
			Dbg_Assert(p_entry->mp_next_ptr->mp_prev_ptr == p_entry);

		if (pPtrToCheckForInclusion == p_entry)
			included = true;

		p_entry = p_entry->mp_next_ptr;
	}

	Dbg_MsgAssert(!pPtrToCheckForInclusion || included, ("smart pointer not in list as expected"));
}




}


================================================
FILE: Code/Gel/Object/basecomponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Object
//* FILENAME:       basecomponent.cpp
//* OWNER:          Mick West
//* CREATION DATE:  10/17/2002
//****************************************************************************

#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent::CBaseComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent::~CBaseComponent()
{
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBaseComponent::Finalize()
{
	// Virtual function, can be overridden to provided finialization to 
	// a component after all components have been added to an object
}


void CBaseComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// This is primarily for pre-existing components that we 
	// need to update
	//
	// Components that are created from EmptyComponent.cpp/h should
	// have the funtion automatically overridden

	printf ("WARNING:  Refreshing component %s using InitFromStructure\n");  
	InitFromStructure(pParams);
}

void CBaseComponent::ProcessWait( Script::CScript * pScript )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// By default, a component has no member functions
// so attempting to call one will just return MF_NOT_EXECUTED
CBaseComponent::EMemberFunctionResult CBaseComponent::CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript )
{
	return MF_NOT_EXECUTED;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBaseComponent::Suspend(bool suspend)
{
	m_flags.Set(BC_NO_UPDATE, suspend);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBaseComponent::Hide( bool shouldHide )
{
	// the base component does nothing,
	// but some components need to do extra stuff
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBaseComponent::Teleport()
{
	// the base component does nothing,
	// but some components need to do extra stuff
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Used by the script debugger code to fill in a structure
// for transmitting to the monitor.exe utility running on the PC.
void CBaseComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CBaseComponent::GetDebugInfo"));
	
	#ifdef __NOPT_ASSERT__
	p_info->AddInteger("CPUTime",m_update_time);
	#endif
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}


================================================
FILE: Code/Gel/Object/basecomponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Object
//* FILENAME:       BaseComponent.h
//* OWNER:          Mick West
//* CREATION DATE:  10/17/2002
//****************************************************************************

#ifndef __OBJECT_BASECOMPONENT_H__
#define __OBJECT_BASECOMPONENT_H__

#include 
#include 

#include 

namespace Script
{
    class CStruct;
    class CScript;
}

namespace Obj
{

// CBaseComponent is a virtual base class for object components
class CBaseComponent : public Spt::Class
{
	friend	class	CCompositeObject;

	
public:
    CBaseComponent();
    virtual ~CBaseComponent();

	// Possible results from calling CallMemberFunction
	// (If the order of these change, then please
	// also change Gfx::EAnimFunctionResult)
	enum EMemberFunctionResult
	{
		MF_FALSE 		= 0,
		MF_TRUE  		= 1,
		MF_NOT_EXECUTED = 2
	};
	
	enum EBaseComponentFlags
	{
		BC_NO_UPDATE
	};


public:
	virtual	void 					Update() = 0;
    virtual void 					InitFromStructure( Script::CStruct* pParams ) = 0;
	virtual	void 					Finalize();
    virtual void 					RefreshFromStructure( Script::CStruct* pParams );
	virtual void					ProcessWait( Script::CScript * pScript );
	
	void							SetObject(CCompositeObject* p_object) {mp_object = p_object;}
	CCompositeObject*				GetObject() const {return mp_object;}
	CBaseComponent*					GetNext() const {return mp_next;}
	CBaseComponent*					GetNextSameType() const {return mp_next_same_type;}
	void							SetNextSameType( CBaseComponent *p_component ){ mp_next_same_type = p_component; }
	uint32							GetType() const {return m_type;}
	virtual	void					Suspend(bool suspend);
	bool							IsSuspended() const { return m_flags.Test(BC_NO_UPDATE); }
	virtual void					Hide(bool shouldHide);
	virtual void					Teleport();

	// Used by the script debugger code to fill in a structure
	// for transmitting to the monitor.exe utility running on the PC.
	virtual void					GetDebugInfo(Script::CStruct *p_info);

public:
	virtual EMemberFunctionResult 	CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript );
	
protected:
	void							SetType(uint32 type) {m_type = type;}

private:
	uint32							m_type;	   			// Unique ID of the component, stuck here during constuction
	Flags		m_flags;
	CCompositeObject	* 			mp_object; 			// Parent object that contains this component
	CBaseComponent * 				mp_next;			// next component in the list
	CBaseComponent * 				mp_next_same_type;	// next component in the list that is of the same type

	#ifdef __NOPT_ASSERT__
	// The time spent (in microseconds) executing ::Update(), for displaying in the script debugger.
	int								m_update_time;
	#endif	
};

}

#endif


================================================
FILE: Code/Gel/Object/compositeobject.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Object
//* FILENAME:       compositeobject.cpp
//* OWNER:          Mick West
//* CREATION DATE:  10/17/2002
//****************************************************************************

/***************************************************************************
 A composite object is a generic object which basically consists of:
 - a position and orientation
 - some flags
 - a script
 - a list of components
 
 The purpose of the Composite Object is to represent a thing that exists 
 in the game world.  For example:
 
  - Cars and other vechicals
  - Pedestrians, animals, etc
  - Pickup Icons
  - 3D sound emitters
  - Particle systems
  - Skaters or other player type objects
  - Bouncy Objects like trash cans

 CCompositeObject is derived from CObject, so all CCompositeObjects have
 a mp_script (which might be NULL)
 
 A composite object is normally comprised of several components.  These are 
 generally independent things such as:
  - A Model	componet
  - A physics component (e.g., drive around, or skating)
  - A suspend component
  - and various other components as needed
  
 There is one global list of all composite objects.  This is managed by the 
 CCompositeObjectManager class, whcih is also responsible for creating
 the composite objects.
 
 A composite object is typically create from an array of structs
 and a structe that contains the initialization parameters.
 
 Here's an example of the array of structs.  Note that some components
 have default parameters.  These can be overridden by the struct of parameters
 
 gameobj_composite_structure = [
    { component = suspend }
    { component = model  }
    { component = exception }
    { component = collision }
 ]

****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  // Needed by GetDebugInfo
#include 


namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCompositeObject::CCompositeObject()
{
    // set position and orientation to a known state (at the origin, identity matrix)	
	m_vel.Set( 0.0f, 0.0f, 0.0f );
	m_pos.Set( 0.0f, 0.0f, 0.0f );
	m_matrix.Ident();
	m_display_matrix.Ident();

	mp_component_list = NULL;
	m_composite_object_flags.ClearAll();

	SetFlags( GetFlags() | vCOMPOSITE);   // Kind of a temp solution for now

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCompositeObject::~CCompositeObject()
{   
	while ( mp_component_list )
    {
		// Get the component at the head of the list		
		CBaseComponent* pComponent = mp_component_list;
		
		// advance the list past this component, effectivly isolating it
		// (unless someone else is storing a pointer to it)
		mp_component_list =  pComponent->mp_next;
		
		// remove the component from the by-type list of components maintained by the CompositeObjectManager
		Obj::CCompositeObjectManager::Instance()->RemoveComponentByType( pComponent );

		// delete the isolated component
		delete pComponent;   
		     
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
#ifdef __NOPT_ASSERT__
// Runs through all the components and zeroes their update times.
void CCompositeObject::zero_component_update_times()
{
	CBaseComponent* pComponent = mp_component_list;
    while ( pComponent )
    {
		pComponent->m_update_time=0;
        pComponent = pComponent->mp_next;
    }
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompositeObject::Update()
{
#ifdef __USE_PROFILER__
	Sys::CPUProfiler->PushContext( m_profile_color );
#endif // __USE_PROFILER__

	Dbg_MsgAssert(IsFinalized(),("Update called on UnFinalized Composite object %s",Script::FindChecksumName(GetID())));

#ifdef	__NOPT_ASSERT__
	// GJ:  don't do this if the composite object is paused,
	// because some lock obj component might be trying
	// to lock to this object while the game is paused
	if (!m_composite_object_flags.Test(CO_SUSPENDED)
		&& !m_composite_object_flags.Test(CO_PAUSED))
	{
		// clear out the display matrix, to make 
		// sure that no one's relying on the
		// last frame's matrix                                                                                               
		m_display_matrix.Ident();
	}
#endif
	
#ifdef __NOPT_ASSERT__
	// Make sure the component update times are initialized to zero so that they do not
	// show incorrect old values in the debugger if this function returns before
	// getting to the component update loop.
	zero_component_update_times();
	
	m_total_script_update_time=0;
	m_do_game_logic_time=0;
#endif

	if (m_composite_object_flags.Test(CO_PAUSED))
	{
#ifdef __USE_PROFILER__
	Sys::CPUProfiler->PopContext();
#endif // __USE_PROFILER__
		return;
	}


	if (!m_composite_object_flags.Test(CO_SUSPENDED))
	{
#ifdef __NOPT_ASSERT__
		Tmr::CPUCycles time_before=Tmr::GetTimeInCPUCycles();
#endif
		if ( mp_script )
		{
			if ( mp_script->Update() == Script::ESCRIPTRETURNVAL_FINISHED )
			{
				// if we have script based events then we only want to kill the script if
				// it has an empty event handler table
				// as a script can finish, but still have event handlers
				// this kind of logic will be duplicated in various places in the code
				// so we could do with a bit of refactoring
				// like a mp_script->CanBeDeleted() function
				#ifdef	__SCRIPT_EVENT_TABLE__	 
					// in practice it seems okay to just leave the script
					// probably not a big issue   
				#else
					delete mp_script;
					mp_script = NULL;
				#endif
			}
		}
		
		// if a script has called "Die",
		// then don't update the components
		if ( IsDead() )
		{
			#	ifdef __USE_PROFILER__
				Sys::CPUProfiler->PopContext();
			#	endif // __USE_PROFILER__
			return;
		}
#ifdef __NOPT_ASSERT__
		// Convert to microseconds by dividing by 150
		m_total_script_update_time=(Tmr::GetTimeInCPUCycles()-time_before)/150;
#endif

		// transition-only function call,
		// call each specific object's
		// DoGameLogic() function (previously,
		// this was called by each object's task)
#ifdef __NOPT_ASSERT__
		time_before=Tmr::GetTimeInCPUCycles();
#endif
		// Mick:  DoGameLogic is Deprecated, and only exists for a few misc objects
		// it should eventually be removed
		DoGameLogic();
#ifdef __NOPT_ASSERT__
		// Convert to microseconds by dividing by 150
		m_do_game_logic_time=(Tmr::GetTimeInCPUCycles()-time_before)/150;
#endif

	}

	CBaseComponent* pComponent = mp_component_list;
    while ( pComponent )
    {
		if ( ! (pComponent->m_flags.Test(CBaseComponent::BC_NO_UPDATE)))
		{
			#ifdef __NOPT_ASSERT__
			Tmr::CPUCycles time_before_components=Tmr::GetTimeInCPUCycles();
			#endif
			
			pComponent->Update();
			
			#ifdef __NOPT_ASSERT__
			// Convert to microseconds by dividing by 150
			pComponent->m_update_time=(Tmr::GetTimeInCPUCycles()-time_before_components)/150;
			#endif

			// If a component update has killed the object
			// then we don't process any more components
			// as they might attempt to fire an event, or reference this object in some way
			// and it won't be in the tracking system any more
			if (IsDead())
			{
				break;
			}
			
			
		}
        pComponent = pComponent->mp_next;
    }
	#	ifdef __USE_PROFILER__
		Sys::CPUProfiler->PopContext();
	#	endif // __USE_PROFILER__
	
	m_composite_object_flags.Clear(CO_TELEPORTED);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCompositeObject::Hide( bool should_hide )
{
	if ( should_hide == m_composite_object_flags.Test(CO_HIDDEN) )
	{
		return;
	}

	m_composite_object_flags.Set(CO_HIDDEN, should_hide);
		
	// loop through all the components
	// and call their individual Hide functions...
	CBaseComponent* pComponent = mp_component_list;
	while ( pComponent )
	{
		pComponent->Hide( should_hide );
		pComponent = pComponent->mp_next;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Suspending a composite object tells it not to update the script
// but the individual components take care of suspending themselves
// (Generally handled by the BC_NO_UPDATE flag being set)	
// "suspending" is generally soemthing done when the object goes a
// certain distance from the camera
void CCompositeObject::Suspend(bool suspended)
{
    if (suspended == m_composite_object_flags.Test(CO_SUSPENDED)) return;
	
	m_composite_object_flags.Set(CO_SUSPENDED, suspended);
	
	// suspend the individual components
	CBaseComponent* pComponent = mp_component_list;
	while ( pComponent )
	{
		pComponent->Suspend(suspended);
		pComponent = pComponent->mp_next;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompositeObject::Teleport()
{
	// calls each component's Teleport() function so
	// that things like the model, the collision, and the
	// shadow can be updated immediately when the
	// object has radically changed position
	CBaseComponent* pComponent = mp_component_list;
	while ( pComponent )
	{
		pComponent->Teleport();
		pComponent = pComponent->mp_next;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompositeObject::AddComponent( CBaseComponent* pComponent )
{
#ifdef __NOPT_ASSERT__
	if ( GetComponent( pComponent->GetType() ) )
	{
		Dbg_MsgAssert( 0, ( "Object already has a component of type '%s'", Script::FindChecksumName(pComponent->GetType()) ) );
	}
#endif

	Dbg_MsgAssert(!IsFinalized(),("Adding Component %s to Finalized Composite object %s",Script::FindChecksumName(pComponent->GetType()),Script::FindChecksumName(GetID())));

	if (!mp_component_list)
	{
		mp_component_list = pComponent;
	}
	else
	{
		CBaseComponent* p_tail = mp_component_list;
		while(p_tail->mp_next)
		{
			p_tail = p_tail->mp_next;
		}
		p_tail->mp_next = pComponent;
	}

    // now that the component is "officially" associated with
    // this object, we can set the component's object ptr
    pComponent->SetObject( this );

	// add the component to the by-type list of components maintained by the CompositeObjectManager
	Obj::CCompositeObjectManager::Instance()->AddComponentByType( pComponent );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given a single component definition in the form:
// {
// 		component = ...
//      .... other params
// }
//
// Then create a component of the correct type
// add it to this object
// and initialize it with this structure
//
// Optionally supply a seperate additional p_params structure from which to initialize it

void CCompositeObject::CreateComponentFromStructure(Script::CStruct *p_struct, Script::CStruct *p_params)
{
	uint32	component_name;
	p_struct->GetChecksum("component",&component_name,Script::ASSERT);
	Obj::CBaseComponent* p_component = Obj::CCompositeObjectManager::Instance()->CreateComponent(component_name);
	AddComponent(p_component);	 // Add it first, as InitFromStructure might need to query the object
	if (p_params)
	{
		// If we have additional parameters, then add then in to the parameters in the array
		// this will override the array paramaeters, which are assumed to be defaults
		Script::CScriptStructure *p_combinedParams=new Script::CScriptStructure;
		*p_combinedParams += *p_struct;
		*p_combinedParams += *p_params;
		p_component->InitFromStructure(p_combinedParams);
		delete p_combinedParams;
	}
	else
	{
		// No additional parameters, so just initialze from the params in the structure in the array
		p_component->InitFromStructure(p_struct);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given an array of component definitions in the form:
// [
//   { component = ......
//     ... other params .....
//   }
//   {
//    ... other components
//   }
// ]
// then iterate over the array, and add the components to the composite object
//
// Optionally supply a "p_params" structure that contains all the initialization info
// instead of interleaving it in the array 
													  
void CCompositeObject::CreateComponentsFromArray(Script::CArray *p_array, Script::CStruct* p_params)
{
	int num_components = p_array->GetSize();
	for (int i=0;iGetStructure(i);
        CreateComponentFromStructure(p_struct, p_params);
	}
}


// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently isfrequently the contents of a node
// but you can pass in anything you like.	
void CCompositeObject::InitFromStructure( Script::CStruct* pParams )
{
	uint32 NameChecksum;
	if ( pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &NameChecksum ))
	{
		SetID(NameChecksum);
	}
	
	SkateScript::GetPosition(pParams,&m_pos);
	
	SkateScript::GetOrientation(pParams,&m_matrix);
	
#	ifdef __USE_PROFILER__
	int ProfileColor = 0x800000;			// default to blue profile color		
	pParams->GetInteger( CRCD(0x72444899,"ProfileColor"), &ProfileColor );
	SetProfileColor(ProfileColor);
#	endif // __USE_PROFILER__

	if (pParams->ContainsFlag(CRCD(0x23627fd7,"permanent")))
	{
		SetFlags( GetFlags() | vLOCKED);
	}
}


// Finalize is called after all components have been added
// and will call the virtual Finalize() function on each component
// This is intended for any components that are depended on other components
// but where the initialization order can't be guaranteed
void CCompositeObject::Finalize()
{
	Dbg_MsgAssert(!IsFinalized(),("Finalizing Composite object %s twice",Script::FindChecksumName(GetID())));
	CBaseComponent *p_component = mp_component_list;
	while (p_component)
	{
		p_component->Finalize();
		p_component = p_component->GetNext();
	}
	m_composite_object_flags.Set(CO_FINALIZED);

	// now that the component is finalized,
	// update the components that depend
	// on the position of the object
	Teleport();
}


// RefreshFromStructure is passed the same parameters as the above
// but will use them to update 
void CCompositeObject::RefreshFromStructure( Script::CStruct* pParams )
{
	// Make sure they are not trying to change the id to something else
	#ifdef	__NOPT_ASSERT__
	uint32 NameChecksum;
	if ( pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &NameChecksum ))
	{
		Dbg_MsgAssert(GetID() == NameChecksum,("Attempting to refresh id from %s to %s\n", Script::FindChecksumName(GetID()),NameChecksum));
	}
	#endif

	// Update the position	
	// Note this is just cut and paste from above
	// so if we're going to be doing more initting of the composite object
	// from a script struct, then we might want to factor it out
	SkateScript::GetPosition(pParams,&m_pos);
	
	// Now iterate over the components, and Refresh them
	CBaseComponent* pComponent = mp_component_list;
    while ( pComponent )
    {
		pComponent->RefreshFromStructure(pParams);
        pComponent = pComponent->mp_next;
    }


}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CCompositeObject::GetComponent( uint32 id ) const
{
	CBaseComponent *p_component = mp_component_list;
	while (p_component)
	{
		if (p_component->GetType() == id)
		{	
			return p_component;
		}
		p_component = p_component->GetNext();
	}
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool 	CCompositeObject::CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript )
{


	// This should probably go in some debug component?
	switch ( Checksum )
	{
		// @script | Obj_PrintDetails | This is a debug function.  Add this
        // command to an object's script
		// and it will print the details.  Add anything you want to the Dbg_Message( )
		// below.
        // @uparmopt "string" | text to print
		case 0xc2404947: // Obj_PrintDetails
		{
			const char* pText;
			if ( pParams->GetText( NONAME, &pText ) )
			{
				Dbg_Message( "%s", pText );
			}
			Dbg_Message( "Obj details:\n  pos %f %f %f\n  time %d\n  type %d",
				m_pos[ X ], m_pos[ Y ], m_pos[ Z ], Tmr::GetTime(), m_type );
			return true;
		}
		break;

		// @script | CreateComponentFromStructure |
		case 0x406998a5: // CreateComponentFromStructure
		{
			CreateComponentFromStructure( pParams, NULL );
			return true;
		}
		break;

		// @script | Obj_GetPosition |
		case 0xe90aad2d: // Obj_GetPosition
		{									 
			pScript->GetParams()->AddVector( CRCD(0x7f261953,"pos"), m_pos[X], m_pos[Y], m_pos[Z] );
			return true;
		}
		break;

		// @script | Obj_SetPosition | Sets the world position of the object.
		// @parmopt vector | Position | | Position to give to the object.
		case 0xf7251a64: // Obj_SetPosition
		{									 
			pParams->GetVector(CRCD(0xb9d31b0a,"Position"),&m_pos);
			return true;
		}
		break;
		
		// @script | Obj_SetOrientation |
		// @parmopt float | x | 0.0 | The x angle, in degrees
		// @parmopt float | y | 0.0 | The y angle, in degrees
		// @parmopt float | z | 0.0 | The z angle, in degrees
		// @parmopt vector | dir | (0,0,1) | Direction vector (an alternative to specifying angles)
		// The direction vector does not need to be normalized.
		case 0xc6f3baa5: // Obj_SetOrientation
		{
			Mth::Vector dir(0.0f,0.0f,1.0f);
			if (pParams->GetVector(CRCD(0x455485ef,"dir"),&dir))
			{
				Mth::Matrix mat;
				mat.Ident();
				
				mat[Z]=dir;
				mat[Z].Normalize();
				mat[X] = Mth::CrossProduct(mat[Y], mat[Z]);
				mat[X].Normalize();
				mat[Y] = Mth::CrossProduct(mat[Z], mat[X]);
				mat[Y].Normalize();
				
				SetMatrix(mat);
				SetDisplayMatrix(mat);
			}
			else
			{
				float p=0.0f;
				float h=0.0f;
				float r=0.0f;
				pParams->GetFloat(CRCD(0x7323e97c,"x"),&p);
				pParams->GetFloat(CRCD(0x424d9ea,"y"),&h);
				pParams->GetFloat(CRCD(0x9d2d8850,"z"),&r);
				p*=3.141592654f/180.0f;
				h*=3.141592654f/180.0f;
				r*=3.141592654f/180.0f;
				Mth::Matrix mat(p,h,r);
				SetMatrix(mat);
				SetDisplayMatrix(mat);
			}	
			return true;
		}	
		break;

        // @script | Obj_ForceUpdate | Does a single call to the object's Update function
		case 0xc1bff0f3: // Obj_ForceUpdate
		{
			Update();
			return true;
		}	
		break;
		
		// @script | Obj_GetVelocity |
		case 0x11fe9f71: // Obj_GetVelocity
		{								   
			pScript->GetParams()->AddVector( CRCD(0x0c4c809e,"vel"), m_vel[X], m_vel[Y], m_vel[Z] );
			return true;
		}
		break;
		
		// @script | GetSpeed |
		case 0xc0caac4a: // GetSpeed
		{								   
			pScript->GetParams()->AddFloat( CRCD(0xf0d90109,"speed"), m_vel.Length() );
			return true;
		}
		break;
		
		// @script | Hide | hides object
		case 0x5b6634d4: // Hide
		{
			Hide( true );
			return true;
		}
		break;

		// @script | Unhide | unhides object
		case 0xb60d1f35: // Unhide
		{
			Hide( false );
			return true;
		}
		break;

		// @script | IsHidden | checks the hide flag of object
		case 0xb16619ae: // IsHidden
		{
			return m_composite_object_flags.Test(CO_HIDDEN);
		}
		break;
		
        // @script | Move |
        // @parmopt float | x | 0.0 | x component
        // @parmopt float | y | 0.0 | y component
        // @parmopt float | z | 0.0 | z component
		case 0x10c1c887: // Move	
		{
			float distance = 0.0f;
			pParams->GetFloat(CRCD(0x7323e97c, "x"), &distance);
			m_pos += distance * m_matrix[X];
			distance = 0.0f;
			pParams->GetFloat(CRCD(0x424d9ea, "y"), &distance);
			m_pos += distance * m_matrix[Y];
			distance = 0.0f;
			pParams->GetFloat(CRCD(0x9d2d8850, "z"), &distance);
			m_pos += distance * m_matrix[Z];
			return true;
		}
		
		case 0xce0ca665: // Suspend
			Suspend(true);
			return true;
			break;

		case 0xca3c59a6: // Unsuspend
			Suspend(false);
			return true;
			break;
			
		case 0x28656d12: // Pause
			Pause(true);
			return true;
			break;

		case 0xff85d4d7: // Unpause
			Pause(false);
			return true;
			break;

// MOVED FROM CMOVINGOBJECT.CPP		
		
		// @script | Obj_GetCollision | Checks to see if there is a collision along a line in front
		// of the object.
        // @parmopt float | Height | 3.0 | Height above origin of start point in feet
        // @parmopt float | Lnegth | 3.0 | length of ocllision line in feet
        // @flag side | Check side collision instead of forward collision
        // @flag debug | display green debug lines at each collision test, white if there is a collision
		case 0x168b09c:	// Obj_GetCollision
			{
				CFeeler	feeler;
				//if (pParams->ContainsFlag(0xc4e78e22/*All*/))
				
				float	length = 3.0f;
				pParams->GetFloat(0xfe82614d /*length*/,&length);
				length *= 12.0f;
				float	height = 3.0f;
				pParams->GetFloat(0xab21af0 /*height*/,&height);
				height *= 12.0f;
				
				feeler.m_start = m_pos + m_matrix[Y] * height;;
				feeler.m_end = feeler.m_start;
				if (pParams->ContainsFlag(0xdc7ee44a/*side*/))
				{
					feeler.m_end += length * m_matrix[X];
				}
				else
				{
					feeler.m_end += length * m_matrix[Z];
				}
				
				if (feeler.GetCollision())
				{
					#ifdef	__NOPT_ASSERT__
					if (pParams->ContainsFlag(0x935ab858/*debug*/))
					{
						feeler.DebugLine(255,255,255);	 // White line = collision
					}
					#endif
					return true;
				}
				else
				{
					#ifdef	__NOPT_ASSERT__
					if (pParams->ContainsFlag(0x935ab858/*debug*/))
					{
						feeler.DebugLine(0,255,0);	 // green line = no collision
					}
					#endif
					return false;
				}
				
			}
		
        // @script | Obj_GetOrientation | Gets the X,Y,Z vector representing orientation 
		case 0xb9ed09d2: // Obj_GetOrientation
		{
			Mth::Vector atVector;
			atVector = m_display_matrix[Mth::AT];
			atVector.Normalize();

			pScript->GetParams()->AddFloat( "x", atVector[X] );
			pScript->GetParams()->AddFloat( "y", atVector[Y] );
			pScript->GetParams()->AddFloat( "z", atVector[Z] );
			return true;
		}
		break;
		
		case 0xd96c0a20: // Obj_GetDistToNode
		{
			uint32 node_name=0;
			pParams->GetChecksum(NONAME,&node_name);
			int node = SkateScript::FindNamedNode(node_name);
			Mth::Vector node_pos;
			SkateScript::GetPosition(node, &node_pos);
			pScript->GetParams()->AddFloat("Dist",(m_pos-node_pos).Length());
			return true;
		}
		
        // @script | Obj_GetDistanceToObject | Calculates the distance to the specified object,
		// and puts the result, in units of feet, into ObjectDistance
        // @uparmopt name | Name of the object. May be Skater.
		case 0x4af57bbb: // Obj_GetDistanceToObject	
		{
			uint32 object_name=0;
			pParams->GetChecksum(NONAME,&object_name);
			CCompositeObject* p_obj=(CCompositeObject*)Obj::ResolveToObject(object_name);
			
			float dist=0.0f;
			if (p_obj)
			{
				Mth::Vector d = m_pos - p_obj->GetPos();
				dist=d.Length()/12.0f;
			}
			pScript->GetParams()->AddFloat("ObjectDistance",dist);
			return true;
		}

		// @script | Obj_GetOrientationToObject |
        // @uparm name | Name of the object. May be Skater.
		case 0x2a26ffa2: // Obj_GetOrientationToObject	
		{
			uint32 object_name=0;
			pParams->GetChecksum(NONAME,&object_name,Script::ASSERT);
			CCompositeObject* p_obj=(CCompositeObject*)Obj::ResolveToObject(object_name);
			Dbg_MsgAssert( p_obj, ( "Couldn't find object %s to get orientation", Script::FindChecksumName(object_name) ) );
			
    		float dotProd=0.0f;
			float orientation=0.0f;

			if ( p_obj )
			{
				// first get the object's current right vector
				Mth::Vector atVector;
				atVector = m_matrix[Mth::RIGHT];
				atVector[W] = 0.0f;
				atVector.Normalize();

				// now take the vector to the object
				Mth::Vector	toObjVector;
				toObjVector = p_obj->m_pos - m_pos;
				toObjVector[W] = 0.0f;
				toObjVector.Normalize();
                
				// dot product tells us whether the right
				// vector is on the left or the right hand
				// side of the toObject vector
				dotProd = Mth::DotProduct( atVector, toObjVector );

				// subtracting 90 gets us whether the at vector is on
				// the left or the right...
				orientation = Mth::RadToDeg( Mth::Kenacosf( dotProd ) ) - 90.0f;	// add 90 to get the at vector
			}

			pScript->GetParams()->AddFloat(CRCD(0x7c6a7d7c,"DotProd"),dotProd);
			pScript->GetParams()->AddFloat(CRCD(0xc97f3aa9,"Orientation"),orientation);
			return true;
		}

        // @script | Backwards | Returns true if the skater is
        // facing backwards from his direction of travel.
		case CRCC(0xf8cfd515, "Backwards"):
			return Mth::DotProduct(m_vel, m_matrix[Z]) < 0.0f ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | SpeedEquals | true if skater speed is within 0.1 
        // of the specified speed
        // @uparm 1.0 | speed
		case CRCC(0x7dcc5fb9, "SpeedEquals"):	 
		{
			float TestSpeed = 0;
			pParams->GetFloat(NO_NAME, &TestSpeed);
			return Mth::Abs(m_vel.Length() - TestSpeed) < 0.1f ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE; 
		}
			
        // @script | SpeedGreaterThan | true if the skater speed is 
        // greater than the specified speed
        // @uparm 1.0 | speed
		case CRCC(0xe5df66d7, "SpeedGreaterThan"):
		{
			float TestSpeed = 0;
			pParams->GetFloat(NO_NAME, &TestSpeed);
			return m_vel.Length() > TestSpeed ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE; 
		}

        // @script | SpeedLessThan | true if the skater speed is
        // less than the specified speed
        // @uparm 1.0 | speed
		case CRCC(0xdd468509, "SpeedLessThan"):
		{
			float TestSpeed = 0;
			pParams->GetFloat(NO_NAME, &TestSpeed);
			return m_vel.Length() < TestSpeed ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE; 
		}
	}


	Dbg_MsgAssert(IsFinalized(),("CallMemberFunction %s to UnFinalized Composite object %s",Script::FindChecksumName(Checksum),Script::FindChecksumName(GetID())));
	CBaseComponent *p_component = mp_component_list;
	while (p_component)
	{
		switch (p_component->CallMemberFunction(Checksum, pParams, pScript))
		{
			case CBaseComponent::MF_TRUE:
				return true;
			case CBaseComponent::MF_FALSE:
				return false;
			default:
				break;
		}
		p_component = p_component->GetNext();
	}
	
	return CObject::CallMemberFunction( Checksum, pParams, pScript );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Used by the script debugger code (gel\scripting\debugger.cpp) to fill in a structure
// for transmitting to the monitor.exe utility running on the PC.

// This adds basic info about the CCompositeObject such as the id, position etc.
// It also adds the debug info for each of the components in the list.
// If classes derived from CCompositeObject override this function, then they should call
// CCompositeObject::GetDebugInfo at the end.
// It would not matter too much if they called it at the start instead, that would just mean
// that derived classes info would appear at the end of the structure (after the list of components)
// rather than at the start.
void CCompositeObject::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CCompositeObject::GetDebugInfo"));
	
	int node_index=SkateScript::FindNamedNode(m_id,false); // false means don't assert if not found.
	if (node_index >= 0 && m_id) // The && m_id is cos FindNamedNode erroneously returns 0 in that case.
	{
		Script::CStruct *p_node=SkateScript::GetNode(node_index);
		p_info->AddInteger("NodeIndex",node_index);
		p_info->AddStructure("NodeInfo",p_node);
	}
	else
	{
		p_info->AddChecksum(NONAME,CRCD(0xc39ae7b3,"NoNode"));
	}	
	
	#ifdef __NOPT_ASSERT__
	p_info->AddInteger("CPUTime",m_update_time);
	p_info->AddInteger("m_total_script_update_time",m_total_script_update_time);
	p_info->AddInteger("m_do_game_logic_time",m_do_game_logic_time);
	#endif

	CObject::GetDebugInfo(p_info);
	
	uint32 type_name=0;

	switch (m_type)
	{
		case SKATE_TYPE_UNDEFINED:
			type_name=CRCD(0x4cb79b3b,"SKATE_TYPE_UNDEFINED");
			break;
		case SKATE_TYPE_SKATER: 			
			type_name=CRCD(0xdbb4aeb6,"SKATE_TYPE_SKATER");
			break;
		case SKATE_TYPE_PED:
			type_name=CRCD(0xa88987b7,"SKATE_TYPE_PED");
			break;
		case SKATE_TYPE_CAR:
			type_name=CRCD(0x2651eacb,"SKATE_TYPE_CAR");
			break;
		case SKATE_TYPE_GAME_OBJ:			
			type_name=CRCD(0x84f6eb36,"SKATE_TYPE_GAME_OBJ");
			break;
		case SKATE_TYPE_BOUNCY_OBJ:		
			type_name=CRCD(0x5f4886c1,"SKATE_TYPE_BOUNCY_OBJ");
			break;
		case SKATE_TYPE_CASSETTE:			
			type_name=CRCD(0xd5021817,"SKATE_TYPE_CASSETTE");
			break;
		case SKATE_TYPE_ANIMATING_OBJECT:	
			type_name=CRCD(0x42018493,"SKATE_TYPE_ANIMATING_OBJECT");
			break;
		case SKATE_TYPE_CROWN:	
			type_name=CRCD(0xf4f7488e,"SKATE_TYPE_CROWN");
			break;
		case SKATE_TYPE_PARTICLE:			
			type_name=CRCD(0x8feeb37e,"SKATE_TYPE_PARTICLE");
			break;
		case SKATE_TYPE_REPLAY_DUMMY:		
			type_name=CRCD(0xf4ac5a55,"SKATE_TYPE_REPLAY_DUMMY");
			break;
		case SKATE_TYPE_COMPOSITE:	
			type_name=CRCD(0x8a61a62c,"SKATE_TYPE_COMPOSITE");
			break;
		default:
			type_name=0;
			break;
		
	}
	if (type_name)
	{
		p_info->AddChecksum("m_type",type_name);
	}
	else
	{
		// If the value is missing from the above switch statement then just add it as an integer.
		p_info->AddInteger("m_type",m_type);
	}
	
	p_info->AddChecksum("m_object_flags",m_object_flags);

	// CCompositeObject stuff
	if (m_composite_object_flags.Test(CO_PAUSED))
	{
		p_info->AddChecksum(NONAME,Script::GenerateCRC("Paused"));
	}
	
	p_info->AddVector("m_pos",m_pos.GetX(),m_pos.GetY(),m_pos.GetZ());
	p_info->AddVector("m_vel",m_vel.GetX(),m_vel.GetY(),m_vel.GetZ());
	
	Script::CArray *p_mat=new Script::CArray;
	p_mat->SetSizeAndType(3,ESYMBOLTYPE_VECTOR);
	Script::CVector *p_vec=new Script::CVector;
	p_vec->mX=m_matrix[X][X]; p_vec->mY=m_matrix[X][Y]; p_vec->mZ=m_matrix[X][Z];
	p_mat->SetVector(0,p_vec);
	p_vec=new Script::CVector;
	p_vec->mX=m_matrix[Y][X]; p_vec->mY=m_matrix[Y][Y]; p_vec->mZ=m_matrix[Y][Z];
	p_mat->SetVector(1,p_vec);
	p_vec=new Script::CVector;
	p_vec->mX=m_matrix[Z][X]; p_vec->mY=m_matrix[Z][Y]; p_vec->mZ=m_matrix[Z][Z];
	p_mat->SetVector(2,p_vec);
	p_info->AddArrayPointer("m_matrix",p_mat);

	p_mat=new Script::CArray;
	p_mat->SetSizeAndType(3,ESYMBOLTYPE_VECTOR);
	p_vec=new Script::CVector;
	p_vec->mX=m_display_matrix[X][X]; p_vec->mY=m_display_matrix[X][Y]; p_vec->mZ=m_display_matrix[X][Z];
	p_mat->SetVector(0,p_vec);
	p_vec=new Script::CVector;
	p_vec->mX=m_display_matrix[Y][X]; p_vec->mY=m_display_matrix[Y][Y]; p_vec->mZ=m_display_matrix[Y][Z];
	p_mat->SetVector(1,p_vec);
	p_vec=new Script::CVector;
	p_vec->mX=m_display_matrix[Z][X]; p_vec->mY=m_display_matrix[Z][Y]; p_vec->mZ=m_display_matrix[Z][Z];
	p_mat->SetVector(2,p_vec);
	p_info->AddArrayPointer("m_display_matrix",p_mat);

	Script::CStruct *p_components_structure=new Script::CStruct;
	CBaseComponent *p_comp=mp_component_list;
	while (p_comp)
	{
		Script::CStruct *p_component_structure=new Script::CStruct;
		p_comp->GetDebugInfo(p_component_structure);
		
		// Using the component type as the name given to the structure, since the
		// type is the checksum of the type name.
		p_components_structure->AddStructurePointer(p_comp->GetType(),p_component_structure);
		p_comp=p_comp->GetNext();
	}
		
	p_info->AddStructurePointer("Components",p_components_structure);
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompositeObject::SetTeleported( bool update_components )
{
	m_composite_object_flags.Set(CO_TELEPORTED);

	if ( update_components )
	{
		Teleport();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}



================================================
FILE: Code/Gel/Object/compositeobject.h
================================================
//****************************************************************************
//* MODULE:         Gel/Object
//* FILENAME:       CompositeObject.h
//* OWNER:          Mick West
//* CREATION DATE:  10/17/2002
//****************************************************************************

#ifndef __OBJECT_COMPOSITEOBJECT_H__
#define __OBJECT_COMPOSITEOBJECT_H__

#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{

// CComposite object contains a list of simple object components
// which can be combined to make up complex stuff like cars and stuff

class	CCompositeObject : public CObject
{

public:
									CCompositeObject();
    virtual 						~CCompositeObject();

	enum ECompositeObjectFlags
	{
		CO_PAUSED,
		CO_SUSPENDED,
		CO_HIDDEN,
		
		// components should not consider the object's change in position since the beginning of this frame to have been smooth
		CO_TELEPORTED,
		
		// Used by the script debugger.
		CO_SENDING_DEBUG_INFO_EVERY_FRAME,
		
		// Used to indicate the object has been finalized
		// so we can't add any more components to it
		CO_FINALIZED,
	};

    void 							Update();
	void 							Pause( bool paused )				{ m_composite_object_flags.Set(CO_PAUSED, paused); }
	bool 							IsPaused( void )  const  			{ return m_composite_object_flags.Test(CO_PAUSED); }
	void 							Suspend( bool suspended );
	bool							IsSuspended( void )	const			{ return m_composite_object_flags.Test(CO_SUSPENDED); }
	void							Hide( bool should_hide );
	bool 							IsHidden( void ) const				{ return m_composite_object_flags.Test(CO_HIDDEN); }
	bool 							IsFinalized( void )	const			{ return m_composite_object_flags.Test(CO_FINALIZED); }

	void							SetTeleported( bool update_components = true );
	void							ClearTeleported( void )				{ m_composite_object_flags.Clear(CO_TELEPORTED); }
	bool							HasTeleported( void )	const		{ return m_composite_object_flags.Test(CO_TELEPORTED); }

	// called when a model has radically changed position
	void							Teleport();

    void 							AddComponent( CBaseComponent* pComponent );
	
	void            				InitFromStructure( Script::CStruct* pParams );
	void							Finalize();
	void            				RefreshFromStructure( Script::CStruct* pParams );
	void 							CreateComponentFromStructure(Script::CStruct *p_struct, Script::CStruct* p_params = NULL);
	void 							CreateComponentsFromArray(Script::CArray *p_array, Script::CStruct * p_params = NULL);
	CBaseComponent* 				GetComponent( uint32 id ) const;
	virtual bool 					CallMemberFunction (uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript);

	
	void							LoadComponent ( CBaseComponent** pComp, uint32 id );
	
	// Used by the script debugger code (gel\scripting\debugger.cpp) to fill in a structure
	// for transmitting to the monitor.exe utility running on the PC.
	virtual void					GetDebugInfo(Script::CStruct *p_info);
	void							SetDebuggerEveryFrameFlag(bool state) { m_composite_object_flags.Set(CO_SENDING_DEBUG_INFO_EVERY_FRAME,state); }
	bool							GetDebuggerEveryFrameFlag()			{ return m_composite_object_flags.Test(CO_SENDING_DEBUG_INFO_EVERY_FRAME); }
	
		
	// when a composite object is paused, spawned (non-main) scripts running on it should be paused, if the script has its mPauseWithObject flag set
	virtual bool					ShouldUpdatePauseWithObjectScripts (   ) { return !IsPaused(); }

	
public:
	const Mth::Vector&					GetPos( void )	  const   { return m_pos; }
	void 							SetPos(const Mth::Vector& pos) 		{ m_pos = pos; }
	Mth::Matrix&					GetMatrix( void )			{ return m_matrix; }
	void							SetMatrix( const Mth::Matrix& matrix )	{ m_matrix = matrix; }
	void							SetDisplayMatrix( const Mth::Matrix& matrix )	{ m_display_matrix = matrix; }
	Mth::Matrix&					GetDisplayMatrix( void )	{ return m_display_matrix; }

//////////////////////////////
// These are public only while transitioning from CMovingObject to CComposititeobject
public:
	Mth::Vector						m_pos;
	Mth::Vector						m_old_pos;
	Mth::Matrix						m_matrix;
///////////////////////////////	
	
	// velocity data doesn't belong here,
	// but it's needed during the transition
	// so that it doesn't break the collision code
	Mth::Vector&					GetVel( void )  { return m_vel; }
	void							SetVel( const Mth::Vector& vel )			{ m_vel = vel; }
	Mth::Vector						m_vel;

protected:
	Mth::Matrix						m_display_matrix;		// final matrix used for display.  You need to rebuild this every frame
	
	// this is needed during the transition, only
	// until we move all of the specific objects'
	// logic into components.  this allows us
	// to get rid of the tasks
	virtual void					DoGameLogic() {}

private:	
	CBaseComponent*					mp_component_list;
	
	Flags	m_composite_object_flags;
	
	#ifdef __NOPT_ASSERT__
	// The time (in microseconds) spent executing ::Update(), for displaying in the script debugger.
	int								m_update_time;		
	
	int								m_total_script_update_time;
	int								m_do_game_logic_time;
	
	// Runs through all the components and zeroes their update times.
	void							zero_component_update_times();
public:
	void							SetUpdateTime(int t) {m_update_time=t;}
	int								GetUpdateTime() const {return m_update_time;}
private:	
	#endif	

#ifdef __USE_PROFILER__
public:	
	void 							SetProfileColor(uint32 ProfileColor) {m_profile_color = ProfileColor;} 
	uint32 							GetProfileColor() const {return m_profile_color;} 
private:
	uint32							m_profile_color;
#else
public:	
	void 							SetProfileColor(uint32 ProfileColor) {;} 
#endif	
	
	// Composite objects have some common components hard wired, as it
	// much simpler and more efficient to do it that way	
};

inline void CCompositeObject::LoadComponent ( CBaseComponent** pComp, uint32 id )
{
	if (!*pComp)
	{
		*pComp = GetComponent(id);
	}
	Dbg_Assert(*pComp);
}

}

#endif




================================================
FILE: Code/Gel/Object/compositeobjectmanager.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Object
//* FILENAME:       CompositeObjectManager.cpp
//* OWNER:          Mick West
//* CREATION DATE:  10/17/2002
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 

// move these to the component factory
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef TESTING_GUNSLINGER
#include 
#include 
#include 
#include 
#endif



namespace Obj
{

DefineSingletonClass( CCompositeObjectManager, "Composite Manager" );

CBaseComponent*	CCompositeObjectManager::mp_components_by_type[vMAX_COMPONENTS];

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCompositeObjectManager::CCompositeObjectManager()
{
    mp_logic_task = new Tsk::Task< CCompositeObjectManager > ( CCompositeObjectManager::s_logic_code, *this, Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_COMPOSITE_MANAGER );
	
    Mlp::Manager* mlp_manager =  Mlp::Manager::Instance(); 
	mlp_manager->AddLogicTask( *mp_logic_task );

	// Register the default component objects
	// This also might be better moved to a CComponentFactory class
	// but let's see how it works first.
	
	RegisterComponent(CRC_SKATERSTANCEPANEL,	CSkaterStancePanelComponent::s_create);
	RegisterComponent(CRC_SKATERLOOPINGSOUND,	CSkaterLoopingSoundComponent::s_create);
	RegisterComponent(CRC_SKATERSOUND,			CSkaterSoundComponent::s_create);
	RegisterComponent(CRC_SKATERGAP,			CSkaterGapComponent::s_create);
	RegisterComponent(CRC_SKATERROTATE,			CSkaterRotateComponent::s_create);
	RegisterComponent(CRC_SKATERPHYSICSCONTROL,	CSkaterPhysicsControlComponent::s_create);
	RegisterComponent(CRC_SKATERLOCALNETLOGIC,	CSkaterLocalNetLogicComponent::s_create);
	RegisterComponent(CRC_SKATERNONLOCALNETLOGIC,	CSkaterNonLocalNetLogicComponent::s_create);
	RegisterComponent(CRC_SKATERSCORE,			CSkaterScoreComponent::s_create);
	RegisterComponent(CRC_SKATERMATRIXQUERIES,	CSkaterMatrixQueriesComponent::s_create);
	RegisterComponent(CRC_SKATERFLOATINGNAME,	CSkaterFloatingNameComponent::s_create);
	RegisterComponent(CRC_SKATERSTATEHISTORY,	CSkaterStateHistoryComponent::s_create);
	RegisterComponent(CRC_SKATERCOREPHYSICS,	CSkaterCorePhysicsComponent::s_create);
	RegisterComponent(CRC_SKATERADJUSTPHYSICS,	CSkaterAdjustPhysicsComponent::s_create);
	RegisterComponent(CRC_SKATERFINALIZEPHYSICS,	CSkaterFinalizePhysicsComponent::s_create);
	RegisterComponent(CRC_SKATERCLEANUPSTATE,	CSkaterCleanupStateComponent::s_create);
	RegisterComponent(CRC_SKATERENDRUN,			CSkaterEndRunComponent::s_create);
	RegisterComponent(CRC_SKATERBALANCETRICK,	CSkaterBalanceTrickComponent::s_create);
	RegisterComponent(CRC_SKATERFLIPANDROTATE,	CSkaterFlipAndRotateComponent::s_create);
	RegisterComponent(CRC_SKATERSTATE,			CSkaterStateComponent::s_create);
	RegisterComponent(CRC_GOALEDITOR,			CGoalEditorComponent::s_create); 	
	RegisterComponent(CRC_RAILEDITOR,			CRailEditorComponent::s_create); 	
	RegisterComponent(CRC_SKATERPROXIMITY,		CSkaterProximityComponent::s_create);
	RegisterComponent(CRC_EDITORCAMERA,			CEditorCameraComponent::s_create);
	RegisterComponent(CRC_SKATERRUNTIMER,		CSkaterRunTimerComponent::s_create);
	
	RegisterComponent(CRC_MODEL,				CModelComponent::s_create); 
	RegisterComponent(CRC_MODELLIGHTUPDATE,		CModelLightUpdateComponent::s_create); 
	RegisterComponent(CRC_BOUNCY,				CBouncyComponent::s_create); 
	RegisterComponent(CRC_SKELETON,		    	CSkeletonComponent::s_create); 
	RegisterComponent(CRC_ANIMATION,			CAnimationComponent::s_create); 
	RegisterComponent(CRC_SOUND,		    	CSoundComponent::s_create); 
	RegisterComponent(CRC_COLLISION,			CCollisionComponent::s_create); 
	RegisterComponent(CRC_LOCKOBJ,				CLockObjComponent::s_create, CLockObjComponent::s_register); 
	RegisterComponent(CRC_CARPHYSICS,			CCarPhysicsComponent::s_create);
	RegisterComponent(CRC_MOTION,				CMotionComponent::s_create); 
	RegisterComponent(CRC_SUSPEND,				CSuspendComponent::s_create, CSuspendComponent::s_register); 
	RegisterComponent(CRC_TRICK,				CTrickComponent::s_create);
	RegisterComponent(CRC_AVOID,				CAvoidComponent::s_create);
	RegisterComponent(CRC_PEDLOGIC,				CPedLogicComponent::s_create);
	RegisterComponent(CRC_RIGIDBODY,			CRigidBodyComponent::s_create);
	RegisterComponent(CRC_SHADOW,				CShadowComponent::s_create);
	RegisterComponent(CRC_STREAM,				CStreamComponent::s_create);
	RegisterComponent(CRC_SPECIALITEM,			CSpecialItemComponent::s_create);
	RegisterComponent(CRC_VEHICLE,				CVehicleComponent::s_create); 
	RegisterComponent(CRC_VEHICLECAMERA,		CVehicleCameraComponent::s_create); 
	RegisterComponent(CRC_NODEARRAY,			CNodeArrayComponent::s_create); 
	RegisterComponent(CRC_RAILMANAGER,			CRailManagerComponent::s_create); 
	RegisterComponent(CRC_OBJECTHOOKMANAGER,	CObjectHookManagerComponent::s_create); 
	RegisterComponent(CRC_SKITCH,				CSkitchComponent::s_create);
//	RegisterComponent(CRC_NEAR,					CNearComponent::s_create);
	RegisterComponent(CRC_CAMERA,				CCameraComponent::s_create);
	RegisterComponent(CRC_SKATERCAMERA,			CSkaterCameraComponent::s_create);
	RegisterComponent(CRC_VIBRATION,			CVibrationComponent::s_create);
//	RegisterComponent(CRC_PROXIMTRIGGER,		CProximTriggerComponent::s_create);
	RegisterComponent(CRC_TRIGGER,				CTriggerComponent::s_create);
	RegisterComponent(CRC_FLOATINGLABEL,		CFloatingLabelComponent::s_create);
	RegisterComponent(CRC_INPUT,				CInputComponent::s_create);
	RegisterComponent(CRC_PARTICLE,				CParticleComponent::s_create);
	RegisterComponent(CRC_WALK,					CWalkComponent::s_create);
	RegisterComponent(CRC_WALKCAMERA,			CWalkCameraComponent::s_create);
	RegisterComponent(CRC_CAMERALOOKAROUND,		CCameraLookAroundComponent::s_create);
	RegisterComponent(CRC_MOVABLECONTACT,		CMovableContactComponent::s_create);
    RegisterComponent(CRC_STATSMANAGER,			CStatsManagerComponent::s_create); 
    RegisterComponent(CRC_VEHICLESOUND,			CVehicleSoundComponent::s_create); 
	RegisterComponent(CRC_VELOCITY,				CVelocityComponent::s_create);
	RegisterComponent(CRC_COLLIDEANDDIE,		CCollideAndDieComponent::s_create);
	RegisterComponent(CRC_PROJECTILECOLLISION,	CProjectileCollisionComponent::s_create);
	RegisterComponent(CRC_SETDISPLAYMATRIX,		CSetDisplayMatrixComponent::s_create);
	RegisterComponent(CRC_STATICVEHICLE,		CStaticVehicleComponent::s_create);


#	ifdef TESTING_GUNSLINGER
	RegisterComponent(CRC_HORSE,				CHorseComponent::s_create);
	RegisterComponent(CRC_HORSECAMERA,			CHorseCameraComponent::s_create);
	RegisterComponent(CRC_RIDER,				CRiderComponent::s_create);
	RegisterComponent(CRC_WEAPON,				CWeaponComponent::s_create);
#	endif
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCompositeObjectManager::~CCompositeObjectManager()
{
    if ( mp_logic_task )
    {
        mp_logic_task->Remove();
        delete mp_logic_task;
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCompositeObject*	CCompositeObjectManager::CreateCompositeObject()
{
	Obj::CCompositeObject *p_composite = new Obj::CCompositeObject;
	
	// printf("+++> Frame: %i\n", Tmr::GetRenderFrame());
	
	// DODGY SETTING OF COMPOSITE TYPE
	// FOR TEMP PATCHING OF SYSTEMS THAT ASSUME EVERYTHING IS A CMovingObject
	p_composite->SetType(11);	// SKATE_TYPE_COMPOSITE;

    RegisterObject( *p_composite );
	return p_composite;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given a composite object definition array, and a "node" style structure
// with a "name" and "position"
// then create the Composite object at that position, with the id or the name
// and create the componets for it from the array
// NOTE: this is kind of a specific utility function, added
// to ease the transition from old nodearray style contruction of objects 

CCompositeObject* CCompositeObjectManager::CreateCompositeObjectFromNode(Script::CArray *pArray, Script::CStruct *pNodeData, bool finalize)
{
	Obj::CCompositeObject* pObj = CreateCompositeObject();
	pObj->InitFromStructure(pNodeData);	
	pObj->CreateComponentsFromArray(pArray, pNodeData);	
	if (finalize)
	{
		pObj->Finalize();		// Finalize the interfaces between components
	}
	// printf("+++> Creating Composite Object:  %s\n", Script::FindChecksumName(pObj->GetID()));
	return pObj;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Registering components simply adds them to the list of components that
// the compositeobject manager can create
// All that is passed in is a 32 bit id (of the component, e.g. CRC_MODEL)
// and the callback function pointer to the s_create function
	
void	CCompositeObjectManager::RegisterComponent(uint32 id, CBaseComponent *(p_create_function)(), void(p_register_function)()) 
{
	Dbg_MsgAssert(m_num_components < vMAX_COMPONENTS,("Too many components (%d)",vMAX_COMPONENTS));
	m_registered_components[m_num_components].mComponentID = id;
	m_registered_components[m_num_components].mpCreateFunction = p_create_function;
	m_num_components++;

	// I'm letting the component manager control calling the "register" function
	// no good reason, just better encapsulation.  Prevents bugs.
	if (p_register_function)
	{
		p_register_function();
	}
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent*		CCompositeObjectManager::CreateComponent(uint32 id)
{
	for (uint32 i=0;i sh;
	
	uint32 stamp_mask = m_stamp_bit_manager.RequestBit();
	
	// clear the appropriate stamp bit
	for (CObject* pObject = sh.FirstItem(m_object_list); pObject; pObject = sh.NextItem())
	{
		pObject->ClearStampBit(stamp_mask);
	}
	
	// traverse the object list
	do
	{
		// clear the appropriate list changed bit
		m_list_changed &= ~stamp_mask;
		
        CObject* pNextObject = sh.FirstItem(m_object_list);
		
		while (pNextObject)
		{
			CObject* pObject = pNextObject;
			pNextObject = sh.NextItem();
			
			if (!pObject->CheckStampBit(stamp_mask))
			{
				pObject->SetStampBit(stamp_mask);
				
				#ifdef __NOPT_ASSERT__
				Tmr::CPUCycles time_before = Tmr::GetTimeInCPUCycles();
				CSmtPtr< CObject > p_smart_object = pObject;
				uint32 obj_id = pObject->GetID(); 
				#endif
				
				static_cast< CCompositeObject* >(pObject)->Update();
				
				#ifdef __NOPT_ASSERT__
				Dbg_MsgAssert(p_smart_object, ("Object %s has deleted itself in its Update() function", Script::FindChecksumName(obj_id)));
				// Convert to microseconds by dividing by 150
				static_cast< CCompositeObject* >(pObject)->SetUpdateTime((Tmr::GetTimeInCPUCycles() - time_before) / 150);
				#endif
				
				if (m_list_changed & stamp_mask) break;
			}
		}
	}
	while (m_list_changed & stamp_mask);
	
	m_stamp_bit_manager.ReturnBit(stamp_mask);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompositeObjectManager::Pause(bool paused)
{
	Lst::Search< CObject >	sh;
	
	uint32 stamp_mask = m_stamp_bit_manager.RequestBit();
	
	// clear the appropriate stamp bit
	for (CObject* pObject = sh.FirstItem(m_object_list); pObject; pObject = sh.NextItem())
	{
		pObject->ClearStampBit(stamp_mask);
	}
	
	// traverse the object list
	do
	{
		// clear the appropriate list changed bit
		m_list_changed &= ~stamp_mask;
		
        CObject* pNextObject = sh.FirstItem(m_object_list);
		
		while (pNextObject)
		{
			CObject* pObject = pNextObject;
			pNextObject = sh.NextItem();
			
			if (!pObject->CheckStampBit(stamp_mask))
			{
				pObject->SetStampBit(stamp_mask);
				
				static_cast< CCompositeObject* >(pObject)->Pause(paused);
				
				if (m_list_changed & stamp_mask) break;
			}
		}
	}
	while (m_list_changed & stamp_mask);
	
	m_stamp_bit_manager.ReturnBit(stamp_mask);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompositeObjectManager::s_logic_code ( const Tsk::Task< CCompositeObjectManager >& task )
{
    Dbg_AssertType ( &task, Tsk::Task< CCompositeObjectManager > );

	CCompositeObjectManager& obj_man = task.GetData();

	obj_man.Update();    
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CBaseComponent* CCompositeObjectManager::GetFirstComponentByType( uint32 id )
{
	for( uint32 i = 0; i < m_num_components; ++i )
	{
		if( m_registered_components[i].mComponentID == id )
		{
			return mp_components_by_type[i];
		}
	}
	Dbg_MsgAssert( 0, ( "Component %s not registered", Script::FindChecksumName( id )));
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCompositeObjectManager::AddComponentByType( CBaseComponent *p_component )
{
	uint32 id = p_component->GetType();

	for( uint32 i = 0; i < m_num_components; ++i )
	{
		if( m_registered_components[i].mComponentID == id )
		{
			p_component->SetNextSameType( mp_components_by_type[i] );
			mp_components_by_type[i] = p_component;
			return;
		}
	}
	Dbg_MsgAssert( 0, ( "Component %s not registered", Script::FindChecksumName( id )));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CCompositeObjectManager::RemoveComponentByType( CBaseComponent *p_component )
{
	uint32 id = p_component->GetType();

	for( uint32 i = 0; i < m_num_components; ++i )
	{
		if( m_registered_components[i].mComponentID == id )
		{
			if( mp_components_by_type[i] == p_component )
			{
				mp_components_by_type[i] = p_component->GetNextSameType();
				p_component->SetNextSameType( NULL );
				return;
			}
			else
			{
				CBaseComponent *p_loop = mp_components_by_type[i];
				while( p_loop )
				{
					if( p_loop->GetNextSameType() == p_component )
					{
						p_loop->SetNextSameType( p_component->GetNextSameType());
						p_component->SetNextSameType( NULL );
						return;
					}
					p_loop = p_loop->GetNextSameType();
				}
			}
		}
	}
	Dbg_MsgAssert( 0, ( "Component %s not registered", Script::FindChecksumName( id )));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

	
}


================================================
FILE: Code/Gel/Object/compositeobjectmanager.h
================================================
//****************************************************************************
//* MODULE:         Gel/Object
//* FILENAME:       compositeobjectmanager.h
//* OWNER:          Mick West
//* CREATION DATE:  10/17/2002
//****************************************************************************

#ifndef __OBJECT_COMPOSITEMANAGER_H__
#define __OBJECT_COMPOSITEMANAGER_H__

#include 
#include 
#include 
#include 
#include 
#include 
                          
namespace Obj
{

// Pointer to member.  See Bjarne, Page 418
// not sure if we can use these instead of function pointers to static functions
// (for now, use the old fasioned way)
//typedef	CBaseComponent * (Obj::CBaseComponent *PComponent)();	// Pointer to member of CBaseComponent

struct	SRegisteredComponent
{
	uint32				mComponentID;		   		
	CBaseComponent*		(*mpCreateFunction)();
};


class CCompositeObjectManager : public Obj::CGeneralManager
{

	enum 
	{
				vMAX_COMPONENTS=128						
	};

public:
						CCompositeObjectManager();
    virtual 			~CCompositeObjectManager();

	void 				Update();
	void 				Pause( bool paused );
	
	CCompositeObject*	CreateCompositeObject();
	CCompositeObject* 	CreateCompositeObjectFromNode(Script::CArray *pArray, Script::CStruct *pNodeData, bool finalize=true);

	void				RegisterComponent(uint32 id, CBaseComponent *(p_create_function)(), void(p_register_function)() = NULL); 
	CBaseComponent*		CreateComponent(uint32 id);
    
	CBaseComponent*		GetFirstComponentByType( uint32 id );
	void				AddComponentByType( CBaseComponent *p_component );
	void				RemoveComponentByType( CBaseComponent *p_component );

protected:
	static Tsk::Task< CCompositeObjectManager >::Code   	s_logic_code; 
	Tsk::Task< CCompositeObjectManager >*				    mp_logic_task;	

	uint32													m_num_components;
	SRegisteredComponent									m_registered_components[vMAX_COMPONENTS];

	static CBaseComponent									*mp_components_by_type[vMAX_COMPONENTS];
	
	DeclareSingletonClass( CCompositeObjectManager );
};

}

#endif


================================================
FILE: Code/Gel/Object/object.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Objects (OBJ) 											**
**																			**
**	File name:		object.cpp												**
**																			**
**	Created:		05/27/99	-	mjb										**
**																			**
**	Description:	Game object code										**
**																			**
*****************************************************************************/

// start autoduck documentation
// @DOC object.cpp
// @module object | None
// @subindex Scripting Database
// @index script | object

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/



namespace Script
{
	extern void PrintContents(CStruct *p_structure, int indent);
}

namespace Obj
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

// temp
// temp	  REMOVE THIS WHEN script.h is implemented
// temp
//class	CScript
//{
//};
// temp
// temp
// temp


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/*
	Creates CObject, does not give it an ID or register it.
*/
CObject::CObject() :
	m_node(this)
{
	m_id = CBaseManager::vNO_OBJECT_ID;	
	m_type = CBaseManager::vNO_OBJECT_TYPE;	
#ifndef	__SCRIPT_EVENT_TABLE__		
	mp_event_handler_table = NULL;
#endif	
	mp_tags = NULL;
	mp_script = NULL;
	mp_manager = NULL;
	m_object_flags = 0;
	
	m_ref_count = 0;
	
	m_stamp = 0;
}




/*
	Creates CObject, registers it with manager, giving it automatic ID.
*/
CObject::CObject( CBaseManager* p_obj_manager )
	: m_node(this)
//: manager( obj_manager )

{
	Dbg_Assert(p_obj_manager);	

	m_id = CBaseManager::vNO_OBJECT_ID;	
	m_type = CBaseManager::vNO_OBJECT_TYPE;	
	p_obj_manager->RegisterObject( *this );	// add to manager's object list
#ifndef	__SCRIPT_EVENT_TABLE__		
	mp_event_handler_table = NULL;
#endif	
	mp_tags = NULL;
	mp_script = NULL;
	mp_manager = NULL;
	m_object_flags = 0;
	
	m_ref_count = 0;


}




/*
	Destroying CObject disconnects it from manager, stops all scripts running on it.
*/
CObject::~CObject()
{
	
	#ifdef	__NOPT_ASSERT__	
	if (GetLockAssert())
	{
		if (mp_script)
		{	
			Dbg_MsgAssert(0,("\n%s\n Object %s at %p has been deleted while updating a script running on it, maybe goal manager callback? See Mick.",mp_script->GetScriptInfo(),Script::FindChecksumName(GetID()),this));
		}
		Dbg_MsgAssert(0,("Object %s at %p has been deleted while updating a script running on it, maybe goal manager callback? See Mick.",Script::FindChecksumName(GetID()),this));
	}
	#endif
	
	Dbg_MsgAssert(!IsLocked(), ("can't destroy CObject, is locked"));
	
	if (mp_tags)
		delete mp_tags;

#ifndef	__SCRIPT_EVENT_TABLE__		
	if (mp_event_handler_table)
	{
		// Remove the references to the event handler from the tracker
		mp_event_handler_table->unregister_all(this);
		
		if (mp_event_handler_table->m_in_immediate_use_counter)
		{
			// table still in use by its pass_event() function,
			// so leave it up to that function to destroy the table
			mp_event_handler_table->m_valid = false;
		}
		else
		{
			delete mp_event_handler_table;
		}
	}
#endif

	if (mp_manager)
	{
		mp_manager->UnregisterObject( *this );
	}

	if (mp_script)
	{
		delete mp_script;
		mp_script = NULL;
	}
	
	// Stop any scripts that have this object as their parent.
	// Note: This won't actually delete the scripts, to prevent dangling pointers.
	Script::StopAllScriptsUsingThisObject(this);
}




/*
	Used by object managers when they delete all but a few objects (which have been locked).
*/
void CObject::DestroyIfUnlocked()
{	   
	if ( !IsLocked() )
	{
		delete this;
	}
}




/*
	Sets the global ID of this CObject. A CObject needs an ID in order to function
	in the system. If no ID is assigned, it will automatically be assigned one 
	when registered with a manager.
	
	Calling this function on a CObject that already has an ID will attempt to change
	its ID globally (whereever that ID is used).
*/
void CObject::SetID(uint32 id) 
{	
	if (m_id != CBaseManager::vNO_OBJECT_ID && mp_manager)
	{
		mp_manager->ReregisterObject(*this, id);
	}
	m_id = id;
	SetChecksumTag(CRCD(0x40c698af, "id"), m_id);
}




/*
	Type is pretty much just the checksum of the class, minus the "C"
*/
void CObject::SetType(sint type) 
{
	Dbg_MsgAssert(m_type == CBaseManager::vNO_OBJECT_TYPE, ("CObject already assigned a type"));
	m_type = type;
	SetChecksumTag(CRCD(0x7321a8d6, "type"), m_type);
}


#ifndef	__SCRIPT_EVENT_TABLE__		

/*
	Sets up the table that specifies which scripts to run in response to which events.
	
	See object scripting document.
*/
void CObject::SetEventHandlers(Script::CArray *pArray, EReplaceEventHandlers replace)
{
	
	if (!mp_event_handler_table)
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mp_event_handler_table = new CEventHandlerTable();
		Mem::Manager::sHandle().PopContext();
	}
	else
	{
		mp_event_handler_table->unregister_all(this);	
	}
	mp_event_handler_table->add_from_script(pArray, replace);
//	mp_event_handler_table->compress_table();
	
	mp_event_handler_table->register_all(this);

}



/*
	Removes an entry in the event table with the given type id
*/
void CObject::RemoveEventHandler(uint32 type)
{
	if (!mp_event_handler_table) return;
	mp_event_handler_table->remove_entry(type);

// Mick:  by not compressing the event handler table after removing entries
// we should minimize the amount of allocs needed
// new event handlers can just re-use these "removed" sloes
//	mp_event_handler_table->compress_table();

// Refresh the Obj::Tracker

	Obj::CTracker::Instance()->UnregisterEventReceiver(type, this); 
		
}



/*
	Removes all entries in the event table with the given group id
*/
void CObject::RemoveEventHandlerGroup(uint32 group)
{
	if (!mp_event_handler_table) return;
	
	mp_event_handler_table->unregister_all(this);	
	
	mp_event_handler_table->remove_group(group);

// Mick:  by not compressing the event handler table after removing entries
// we should minimize the amount of allocs needed
// new event handlers can just re-use these "removed" sloes
//	mp_event_handler_table->compress_table();
	
	mp_event_handler_table->register_all(this);	

}

#else


/*
	Sets up the table that specifies which scripts to run in response to which events.
	
	See object scripting document.
*/
void CObject::SetEventHandlers(Script::CArray *pArray, EReplaceEventHandlers replace)
{
	// TEMP - Pass into script object.  Eventually handled soley by script with no need for object
	AllocateScriptIfNeeded();
	mp_script->SetEventHandlers(pArray, replace);
}

/*
	Removes an entry in the event table with the given type id
*/
void CObject::RemoveEventHandler(uint32 type)
{
	if (!mp_script)
	{
		return;
	}
	mp_script->RemoveEventHandler(type );
}

/*
	Removes all entries in the event table with the given group id
*/
void CObject::RemoveEventHandlerGroup(uint32 group)
{
	if (!mp_script)
	{
		return;
	}
	mp_script->RemoveEventHandlerGroup(group);
}



#endif


void CObject::SetIntegerTag(uint32 name, int value)
{
	allocate_tags_if_needed();

	mp_tags->AddInteger(name, value);
}




void CObject::SetChecksumTag(uint32 name, uint32 value)
{
	allocate_tags_if_needed();

	mp_tags->AddChecksum(name, value);
}




void CObject::RemoveFlagTag(uint32 name)
{
	if (mp_tags)
		mp_tags->RemoveFlag(name);
}




bool CObject::GetIntegerTag(uint32 name, int *pResult) const
{
	if (mp_tags)
		return mp_tags->GetInteger(name, pResult);
	return false;
}




bool CObject::GetChecksumTag(uint32 name, uint32 *pResult) const 
{
	if (mp_tags)
		return mp_tags->GetChecksum(name, pResult);
	return false;
}




bool CObject::ContainsFlagTag(uint32 name) const
{
	if (mp_tags)
		return mp_tags->ContainsFlag(name);
	return false;
}


// Add a vector to the tag structure with name "name", and copy in the vector "v"
void CObject::SetVectorTag(uint32 name, Mth::Vector	v)
{
	allocate_tags_if_needed();

	mp_tags->AddVector(name, v[X], v[Y], v[Z]);
}


// get the value a vector tag.
// returning false if not found
bool CObject::GetVectorTag(uint32 name, Mth::Vector *pResult) const
{
	if (mp_tags)
		return mp_tags->GetVector(name, pResult);
	return false;
}

#ifndef	__SCRIPT_EVENT_TABLE__		

void 	CObject::SetOnExceptionScriptChecksum(uint32 OnExceptionScriptChecksum) 
{
	mOnExceptionScriptChecksum = OnExceptionScriptChecksum;
}

uint32 	CObject::GetOnExceptionScriptChecksum() const 
{
	return mOnExceptionScriptChecksum;
}
#else
// Todo - Pass these to the script

#endif


void CObject::SetTagsFromScript(Script::CStruct *pStruct)
{
	allocate_tags_if_needed();

	mp_tags->AppendStructure(pStruct);
	//printf("Set tags of object %s to:\n", Script::FindChecksumName(m_id));
	//Script::PrintContents(mp_tags, 4);
}




void CObject::RemoveTagsFromScript(Script::CArray *pNameArray)
{
	if (!mp_tags) return;

	for (uint i = 0; i < pNameArray->GetSize(); i++)
	{
		uint32 name_crc = pNameArray->GetChecksum(i);
		mp_tags->RemoveComponent(name_crc);
		mp_tags->RemoveFlag(name_crc);
	}
	
	if (mp_tags->IsEmpty())
	{
		delete mp_tags;
		mp_tags = NULL;
	}
}




void CObject::CopyTagsToScriptStruct(Script::CStruct *pStruct)
{
	Dbg_Assert(pStruct);
	
	if (mp_tags)	
	{
		pStruct->AppendStructure(mp_tags);
		//printf("Fetching tags of object %s:\n", Script::FindChecksumName(m_id));
		//Script::PrintContents(pStruct, 4);
	}
}




/*
	Property setting script commands enter through this function.
*/
void CObject::SetProperties(Script::CStruct *pProps)
{
	bool replace_handlers = pProps->ContainsFlag("replace_handlers");

	Script::CArray *p_event_handler_table;
	if (pProps->GetArray("event_handlers", &p_event_handler_table))
	{
#ifdef	__SCRIPT_EVENT_TABLE__		
		CObject::SetEventHandlers(p_event_handler_table, EReplaceEventHandlers(replace_handlers));
#else	
		CObject::SetEventHandlers(p_event_handler_table, EReplaceEventHandlers(replace_handlers));
#endif	
	}
	/*
	else if (pProps->GetArray("event_handler", &p_event_handler_table))
	{
		// just a single entry (makes for easier-to-read script)
		CObject::SetEventHandlers(p_event_handler_table, replace_handlers, true);
	}
	*/

	Script::CStruct *p_tags;
	if (pProps->GetStructure("tags", &p_tags)) 
		SetTagsFromScript(p_tags);

	Script::CArray *p_remove_tags;
	if (pProps->GetArray("remove_tags", &p_remove_tags))
		RemoveTagsFromScript(p_remove_tags);
}




/******************************************************************/
/*   CObject::CallMemberFunction                                  */
/*   Call a member function, based on a checksum                  */
/*   This is usually the checksum of the name of the function     */
/*   but can actually be any number, as it just uses a switch     */
/*   note this is a virtual function, so the same checksum        */
/*   can do differnet things for different objects                */
/*                                                                */
/*                                                                */
/******************************************************************/

bool CObject::CallMemberFunction( uint32 Checksum, Script::CScriptStructure *pParams, Script::CScript *pScript )
{
    
	Dbg_MsgAssert(pScript,("NULL pScript"));

	switch(Checksum)
	{
		case CRCC(0x3611c136, "GetTags"):
			CopyTagsToScriptStruct(pScript->GetParams());
			break;

		case CRCC( 0xa58079eb, "SetTags" ):
			SetTagsFromScript( pParams );
			break;
			 
		case 0xc6870028:// "Die"
			MarkAsDead( );
			break;	
		        
		case 0xb3c262ec:// "DisassociateFromObject"
			pScript->DisassociateWithObject(this);
			break;	
		        
		// @script | Obj_GetId | 
		case 0x500eb224: // Obj_GetId
			Dbg_Assert( pScript && pScript->GetParams() );
			pScript->GetParams()->AddChecksum( "objId", m_id ); 
		break;
		
		// @script | Obj_FlagSet | Check to see if a flag has been set.  Flags can 
		// be defined anywhere, but you should keep them in your personal scripts file.  
		// It is important that you prefix all flags with your initials to ensure that 
		// there are no conflicts with the other designers.
		// Example 1:
		// MJD_LAMP_GOT_BROKEN = 14
		// MJD_LAMP_GOT_HIT = 15
		// MJD_LAMP_GOT_DISABLED = 16
		// if Obj_FlagSet 15
		//     Obj_ClearFlag MJD_LAMP_GOT_HIT
		//     WiggleProfusely
		// endif
		// Example 2:
		// if Obj_FlagSet MJD_LAMP_GOT_HIT clear
		//     WiggleProfusely
		// endif
		// Example 3:
		// If Obj_FlagSet JKU_FLAG_PED_START  // JKU_FLAG_PED_START would be defined in JKU_Scripts.q
		//      Printf Yes
		// endif
		// @flag all | Clear all flags
		// @flag clear | Clear the flag
		// @flag reset | I just added a 'reset' flag you can send in to clear 
		// the flag after checking it, if it's set
		case 0x4babc987: // Obj_FlagSet
		{
			uint32 scriptFlags = mScriptFlags;
			uint32 Flags = 0;
			Flags = GetFlags( pParams, pScript );
			if ( !Flags )
			{
				Dbg_MsgAssert( 0,( "\n%s\nObj_FlagSet command requires a flag to be specified.\n(Either an integer or a name defined to be an integer)",pScript->GetScriptInfo()));
				return ( false );
			}
			if ( pParams->ContainsFlag( 0x1a4e0ef9 ) ) // clear
			{
				mScriptFlags &= ~( Flags );
			}
			if ( scriptFlags & Flags )
			{
				return true;
			}
			else
			{
				return false;
			}
			break;
		}		
		
		// @script | Obj_FlagNotSet | Check to see if a flag has not been set.
		case 0x53ebee03: // Obj_FlagNotSet
		{
			uint32 Flags = 0;
			Flags = GetFlags( pParams, pScript );
			if ( !Flags )
			{
				Dbg_MsgAssert( 0,( "\n%s\nObj_FlagNotSet command requires a flag to be specified.\n(Either an integer or a name defined to be an integer)",pScript->GetScriptInfo()));
				return ( false );
			}
			if ( mScriptFlags & ( Flags ) )
			{
				return false;
			}
			else
			{
				return true;
			}
			break;
		}		
		
		// @script | Obj_ClearFlag | Object member function which clears the specified flag or flags.
		// There are 3 ways of using it, it can clear just one flag, or an array of flags, 
		// or all the flags.
		// Example 1:
		// Obj_ClearFlag JKU_FLAG_PED_START  // Set the flag to 0
		// Example 2:
		// Obj_ClearFlag [ JKU_FLAG_PED_START JKU_FLAG_PED_STOP ]  // Clears an array of flags
		// Example 3:
		// Obj_ClearFlag All  // Clears all the flags.
		// @flag all | Clear all the flags
		case 0x6c2b67f9: // Obj_ClearFlag
		{
			if (pParams->ContainsFlag(0xc4e78e22/*All*/))
			{
				mScriptFlags=0;
				return true;
			}	
			uint32 Flags = 0;
	
			Flags = GetFlags( pParams, pScript );
			if ( !Flags )
			{
				Dbg_MsgAssert( 0,( "\n%s\nObj_ClearFlag command requires a flag to be specified.\n(Either an integer or a name defined to be an integer)",pScript->GetScriptInfo()));
				return ( false );
			}
			mScriptFlags &= ~Flags;
			break;
		}
	
		// @script | Obj_SetFlag | Sets the flag (or an array of flags) on an object.
		// Same parameters as Obj_ClearFlag (except doesn't recognize the 
		// 'all' parameter).
		case 0xbe563426: // Obj_SetFlag
		{
			uint32 Flags = 0;
			Flags = GetFlags( pParams, pScript );
			if ( !Flags )
			{
				Dbg_MsgAssert( 0,( "\n%s\nObj_SetFlag command requires a flag to be specified.\n(Either an integer or a name defined to be an integer)",pScript->GetScriptInfo()));
				return ( false );
			}
			mScriptFlags |= Flags;
			break;
		}

		// @script | Obj_KillSpawnedScript | Kills a spawned script.  Can be passeed a name or id.
		// @parm name | name | Name of the script you want to spawn (no quotes)
		// @parm name | id | id of the script you want to spawn (no quotes)
		case 0xfbd89cd5: // Obj_KillSpawnedScript
		{
			uint32 ScriptChecksum=0;
			if (pParams->GetChecksum(CRCD(0xa1dc81f9,"Name"),&ScriptChecksum))
			{
				// Got a script name, so kill all spawned scripts that ran that script.
				// BUT NOT THE SCRIPT CALLING THIS!
				Script::KillSpawnedScriptsWithObjectAndName( this, ScriptChecksum, pScript );
				return true;
			}
		
			uint32 Id=0;										   
			if (pParams->GetChecksum(CRCD(0x40c698af,"Id"),&Id))
			{
				// They specified an Id, so kill all spawned scripts with this Id,
				// BUT NOT THE SCRIPT CALLING THIS!
				Script::KillSpawnedScriptsWithObjectAndId( this, Id, pScript );
				return ( true );
			}
			Dbg_MsgAssert( 0,( "\n%s\nMust specify Name or ID on Obj_KillSpawnedScript", pScript->GetScriptInfo() ));
			return true;
			break;
		}
	
		// @script | Obj_SpawnScript | Causes the object to run a script, in parallel
		// to whatever script is running on the object.
		// @flag ScriptName | Name of the script you want to spawn (no quotes)
		// @parmopt name | Params | {} | Any parameters you want to pass to the script being
		// spawned.  Must surround params in { }
		case 0x23a4e5c2: // Obj_SpawnScript
		{
			Script::CComponent* p_component = pParams->GetNextComponent();
			if ( p_component && p_component->mType == ESYMBOLTYPE_NAME )
			{
				uint32 scriptChecksum = p_component->mChecksum;
				
				// The spawned script can optionally be given an Id, so that it can be deleted
				// by KillSpawnedScript.
				uint32 Id=0;
				// keep the same ID as the parent if not specified...
				Id = Script::FindSpawnedScriptID(pScript);
				pParams->GetChecksum("Id",&Id);
				Script::CScriptStructure *pScriptParams = NULL;
				pParams->GetStructure( "Params", &pScriptParams );
				#ifdef __NOPT_ASSERT__	
				Script::CScript *p_script=SpawnScriptPlease( scriptChecksum, pScriptParams, Id, pParams->ContainsFlag(CRCD(0x8757d0bb, "PauseWithObject")) );
				p_script->SetCommentString("Created by Obj_SpawnScript");
				p_script->SetOriginatingScriptInfo(pScript->GetCurrentLineNumber(),pScript->mScriptChecksum);
				#else
				SpawnScriptPlease( scriptChecksum, pScriptParams, Id );
				#endif

			}
			break;
		}
	
		// @script | Obj_SwitchScript | Causes the object to replace the current script 
		// attached to it with the script specified by ScriptName. 
		// Can use the pass params just like Obj_SpawnScript.
		// @flag ScriptName | Name of the script you want to spawn (no quotes)
		case 0x714937c7: // Obj_SwitchScript
		{
			uint32 scriptChecksum;
			if ( pParams->GetChecksum( NONAME, &scriptChecksum ) )
			{
				Script::CScriptStructure *pScriptParams = NULL;
				pParams->GetStructure( "Params", &pScriptParams );
				SwitchScript( scriptChecksum, pScriptParams );
			}
			break;
		}
		
		
		// case	CRCC(0x6df6caef,"SetProperties"):
		case	CRCC(0x6c63c7c5,"SetProps"):
		{
			// Lowest level SetProperties, allowing ANY object to set event handlers
			SetProperties(pParams);
			break;
		}
		
/*		
		case	CRCC(0x1127430c, "ClearEventHandler"):
		{
			uint32 type;
			pParams->GetChecksum(NO_NAME, &type, Script::ASSERT);
			RemoveEventHandler(type);
			break;
		}
			
			
		case	CRCC(0x8968da7f, "ClearEventHandlerGroup"):
		{
			uint32 group = CEventHandlerTable::vDEFAULT_GROUP;
			pParams->GetChecksum(NO_NAME, &group);
			RemoveEventHandlerGroup(group);
			break;
		}
		
// @script | OnExceptionRun | run the specified script on exception
// @uparm name | script name to run
// can be called without a parameter to clear it
//		case 0x2c0c9e7b: // OnExceptionRun	
		case CRCC(0x2c0c9e7b,"OnExceptionRun"):
		{
			uint32 OnExceptionScriptChecksum = 0;
			pParams->GetChecksum( NONAME, &OnExceptionScriptChecksum);
			#ifdef	__SCRIPT_EVENT_TABLE__		
			if (mp_script)
			{
				mp_script->SetOnExceptionScriptChecksum(OnExceptionScriptChecksum);
			}
			#else
			SetOnExceptionScriptChecksum(OnExceptionScriptChecksum);
			#endif
			break;
		}

		case CRCC(0xbefaa466,"OnExitRun"):
		{
			uint32 OnExitScriptChecksum = 0;
			pParams->GetChecksum( NONAME, &OnExitScriptChecksum);
			#ifdef	__SCRIPT_EVENT_TABLE__		
			if (mp_script)
			{
				mp_script->SetOnExitScriptChecksum(OnExitScriptChecksum);
			}
			#else
			SetOnExitScriptChecksum(OnExitScriptChecksum);
			#endif
			break;
		}

*/

		case 0x20d125d7: // Obj_Visible
			m_object_flags &= ~vINVISIBLE;
			break;

		case 0x3578e5a9: // Obj_Invisible
			m_object_flags |= vINVISIBLE;
			break;


		
		default:
			Dbg_MsgAssert(0,("\n%s\nNo CObject member function found for '%s'",pScript->GetScriptInfo(),Script::FindChecksumName(Checksum)));

			
	}

	return true;
}



uint32 CObject::GetFlags( Script::CScriptStructure *pParams, Script::CScript *pScript ) const
{
	
	uint32 Flags = 0;
	int Flag = 0;

	// Scan through any array of flags specified.
	Script::CArray *pArray=NULL;
	pParams->GetArray(NONAME,&pArray);
	if (pArray)
	{
		for (uint32 i=0; iGetSize(); ++i)
		{
			uint32 Checksum=pArray->GetNameChecksum(i);
			int Flag=Script::GetInteger(Checksum);
			Dbg_MsgAssert(Flag>=0 && Flag<32,("\n%s\nBad flag value %s=%d, value must be between 0 and 31",pScript->GetScriptInfo(),Script::FindChecksumName(Checksum),Flag));
			Flags |= ( 1 << Flag );
		}	
	}
	else if ( pParams->GetInteger( NONAME, &Flag ) )
	{
		Dbg_MsgAssert(Flag>=0 && Flag<32,("\n%s\nBad flag value of %d, value must be between 0 and 31",pScript->GetScriptInfo(),Flag));
		Flags = ( 1 << Flag );
	}
	return Flags;
}

/*
	If an event is targeted specifically to this CObject (using the same ID), it will be
	passed through this function.
*/
bool CObject::PassTargetedEvent(CEvent *pEvent, bool broadcast)
{
#ifdef	__SCRIPT_EVENT_TABLE__		
	if (!mp_script)
	{
		return true;
	}
#endif
	
	Obj::CSmtPtr p = this;
	
#ifdef	__SCRIPT_EVENT_TABLE__		
	mp_script->PassTargetedEvent(pEvent, broadcast);
#else	
	if (mp_event_handler_table)
		mp_event_handler_table->pass_event(pEvent, this, broadcast);
#endif

	return (p != NULL);
}


// Send myself an event.  Just a useful shortcut for a commonly done thing
void CObject::SelfEvent(uint32 event, Script::CStruct* pParams)
{
	Obj::CTracker::Instance()->LaunchEvent(event,GetID(),GetID(),pParams); 
}


void CObject::BroadcastEvent(uint32 event, Script::CStruct *pData, float radius)
{
	// no target
	// radius not implemented yet
	Obj::CTracker::Instance()->LaunchEvent( event, 0xffffffff, GetID(), pData, true /*, radius */  );
}



/*
	Has the same effect as deleting the CObject, except the actual deletion is deferred until next frame.
	So that objects can be killed and not mess up list traversal
*/
void CObject::MarkAsDead( void )
{
	
	// make sure we don't continue running anything on this script!
	if ( mp_script )
	{
		mp_script->Block( );
	}

	// The following line will set the mp_object of any referring scripts to NULL
	Script::StopAllScriptsUsingThisObject(this);  // Mick: we don't want any other scripts to continue running on a dead object 	
	
	Dbg_MsgAssert( mp_manager,( "No object manager associated with this object..." ));
	// who locks an object?
	SetLockOff();

	
	// mp_manager will be cleared by UnregisterObject()
	CBaseManager *p_manager = mp_manager;
	p_manager->UnregisterObject( *this );
	p_manager->KillObject( *this );  // add object to kill list, to be purged next frame...	
	m_object_flags |= ( vDEAD );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CObject::allocate_tags_if_needed()
{
	if (!mp_tags) 
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mp_tags = new Script::CStruct();
		Mem::Manager::sHandle().PopContext();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CObject::AllocateScriptIfNeeded()
{
	if (!mp_script) 
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
		mp_script = new Script::CScript();
		mp_script->SetScript(CRCD(0x3f5cdb8a,"empty_script"),NULL,this);
		Mem::Manager::sHandle().PopContext();
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CObject::SwitchScript( uint32 scriptChecksum, Script::CScriptStructure *pParams )
{
	
	if ( mp_script )
	{
		mp_script->SetScript( scriptChecksum, pParams, this );
	}
	else
	{
		mp_script = new Script::CScript;
		mp_script->SetScript( scriptChecksum, pParams, this );
		#ifdef __NOPT_ASSERT__
		mp_script->SetCommentString("Created in CObject::SwitchScript(...)");
		#endif
	}
	if ( !mp_script->GotScript( ) )
	{
		Dbg_MsgAssert( 0,( "Couldn't find script specified: %s", Script::FindChecksumName( scriptChecksum ) ));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CScript *CObject::SpawnScriptPlease( uint32 scriptChecksum, Script::CScriptStructure *pParams, int Id, bool pause_with_object )
{
	
	Script::CScript *pScript;
	pScript = Script::SpawnScript( scriptChecksum, pParams, 0, NULL, -1, Id, false, false, false, pause_with_object ); // K: The 0,NULL means no callback specified.
	pScript->mpObject = this;
	return pScript;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// spawn and immediatly run a script
// script will be deleted after it terminates
void CObject::SpawnAndRunScript ( uint32 ScriptChecksum, int node, bool net_script, bool permanent, Script::CStruct *p_params )
{
    // Send this event to other clients if applicable
	if( net_script )
	{
		// Ken: TODO: Currently p_params are not being passed on in net games, fix if necessary ...
		// Currently p_params is only used to store the contents of any ModelTriggerScriptParams
		// that may be specified in an object's node and which are required to be passed on to
		// any TriggerScript in it's local node array. (Used by a goal in London)
		// (Note: The NULL below means a NULL p_object)
		Script::SendSpawnScript( ScriptChecksum, NULL, node, permanent );
	}

	Script::CScript* pScript = Script::SpawnScript( ScriptChecksum, p_params, 0, NULL, node );
	
	#ifdef __NOPT_ASSERT__
	pScript->SetCommentString( "Spawned by CObject::SpawnAndRun(...)" );
	#endif
	
	pScript->mpObject = this; 
	
	pScript->Update(); 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CObject::SpawnAndRunScript( const char *pScriptName, int node, bool net_script, bool permanent, Script::CStruct *p_params )
{
	SpawnAndRunScript(Script::GenerateCRC(pScriptName),node, net_script, permanent, p_params );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CObject::CallScript( uint32 ScriptChecksum, Script::CStruct *pParams )
{
	if ( !mp_script )
	{
		mp_script = new Script::CScript;
		
		// K: Added this in case the script being called contains member functions, which
		// it does when a two player trick-attack game is started.
		mp_script->mpObject=this;
	}

	mp_script->Interrupt(ScriptChecksum, pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			DestroyIfUnlocked( Obj::CObject* obj, void* data )
{
	obj->DestroyIfUnlocked();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			SetLockOff( CObject* obj, void* data )
{
	obj->SetLockOff();
}

void			CObject::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
		// CObject stuff
	Script::CStruct *p_scripts=new Script::CStruct;
	Script::CScript *p_script=Script::GetNextScript(NULL);
	while (p_script)
	{
		if (p_script->mpObject==this)
		{
			Script::CStruct *p_script_info=new Script::CStruct;
			
			#ifdef __NOPT_ASSERT__
			// Convert to microseconds by dividing by 150
			p_script_info->AddInteger("CPUTime",p_script->m_last_time/150);
			#endif
			
			p_script_info->AddChecksum("m_unique_id",p_script->GetUniqueId());
			if (p_script->mIsSpawned)
			{
				p_script_info->AddChecksum(NONAME,CRCD(0xf697fda7,"Spawned"));
			}
			
			#ifdef	__SCRIPT_EVENT_TABLE__		
				if (p_script->GetEventHandlerTable())
				{
					p_script->GetEventHandlerTable()->GetDebugInfo(p_script_info);
				}
			#endif
			
			
			p_scripts->AddStructurePointer(p_script->GetBaseScript(),p_script_info);
		}
			
		p_script=Script::GetNextScript(p_script);
	}	
	p_info->AddStructurePointer("Scripts",p_scripts);
	
	p_info->AddChecksum("m_id",m_id);
	if (mp_tags)
	{
		p_info->AddStructure("mp_tags",mp_tags);
	}
	else
	{
		p_info->AddChecksum("mp_tags",CRCD(0xda3403b0,"NULL"));
	}
	
#ifndef	__SCRIPT_EVENT_TABLE__		
	if (mp_event_handler_table)
	{
		mp_event_handler_table->GetDebugInfo(p_info);
	}
#endif
#endif				 

}

// Leftover from when this function was more complex
// just retained for convenience
CObject *ResolveToObject(uint32 id)
{
	return Obj::CTracker::Instance()->GetObject(id); 	
}


} // namespace Obj



================================================
FILE: Code/Gel/Object/objman.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Objects (OBJ) 											**
**																			**
**	File name:		objman.cpp												**
**																			**
**	Created:		05/27/99	-	mjb										**
**																			**
**	Description:	Object Manager											**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 
#include 

#include 
#include 
#include 

#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

//#define __DEBUG_OBJ_MAN__

namespace Obj
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

const uint32 CBaseManager::vNO_GROUP = 0;
const uint32 CBaseManager::vNO_OBJECT_ID		=	vUINT32_MAX;

const sint CBaseManager::vNO_OBJECT_TYPE	=	0;

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

void	CGeneralManager::flush_dead_objects( const Tsk::Task< CGeneralManager >& task )
{
	 

	CGeneralManager&		obj_manager = task.GetData();
	
	obj_manager.FlushDeadObjects();

}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseManager::CBaseManager( void )
: m_list_changed( 0x00000000 )
{
	m_momentary_removal = false;
}




/*
	Adds the specified CObject to the manager, registers it with the CTracker singleton.
	
	If the CObject has no ID, it is automatically assigned one at this point.
*/
void CBaseManager::RegisterObject ( CObject& obj )
{
	CTracker* p_tracker = CTracker::Instance();
	Dbg_MsgAssert(!obj.mp_manager, ("object already registered with manager"));

   	obj.mp_manager = this;
	m_object_list.AddNode ( &obj.m_node );
	
	// set all list changed bits
	m_list_changed = 0xFFFFFFFF;
	
	if (obj.GetID() == vNO_OBJECT_ID)
		obj.SetID(p_tracker->GetFreshId());

	p_tracker->addObject(&obj);
}




/*
	Removes the specified CObject to the manager, unregisters it from the CTracker singleton.
	
	After this, the CObject is no longer "in the system", and probably should be deleted. Its
	ID will be available for usage by other CObjects again.
*/
void CBaseManager::UnregisterObject( CObject& obj )
{
	CTracker* p_tracker = CTracker::Instance();
	Dbg_MsgAssert(obj.mp_manager == this, ("object not registered with this manager"));
	Dbg_AssertType( &obj, CObject );

	p_tracker->removeObject(&obj, m_new_id_of_object_being_momentarily_removed, m_momentary_removal);
	
	if ( obj.m_node.InList() )
	{
		obj.m_node.Remove();
		obj.mp_manager = NULL;

		// set all list changed bits
		m_list_changed = 0xFFFFFFFF;
	}
}




/*
	Called by CObject::SetID() when a CObject's ID is changed. Shouldn't be called
	from any other place.
*/
void CBaseManager::ReregisterObject(CObject& obj, uint32 newId)
{
	/* 
		In the CTracker singleton, we are concerned with keeping:
		-Aliases. They don't use object ID, just pointers
		-Event listeners: don't use ID, so no worries
		-Waiting script entries: scan through and change stored ID
	*/
	
	m_new_id_of_object_being_momentarily_removed = newId;
	m_momentary_removal = true;
	UnregisterObject(obj);
	obj.m_id = newId;
	RegisterObject(obj);
	m_momentary_removal = false;
	
}

/*
	Changes the priority of an object, then reregisters.
*/
void CBaseManager::SetObjectPriority ( CObject& obj, Lst::Node< CObject >::Priority priority )
{
	obj.m_node.SetPri(priority);
	ReregisterObject(obj, obj.GetID());
}






/*
	Deletes CObjects that have been killed by CObject::mark_as_dead() (which is called
	by the "Die" member command). These CObjects will be "out of play" by this point, and
	only need be deleted.
*/
void CGeneralManager::FlushDeadObjects()
{
	Lst::Head< CObject >*		head = &m_kill_list;
	Lst::Node< CObject >*		next = head->GetNext();

	while ( next )
	{
		Lst::Node< CObject >*	kill = next;
		next = next->GetNext();
		kill->Remove();
		CObject *pObject = kill->GetData( );
		#ifdef __NOPT_ASSERT__
		// Lock Assert is not needed for objects that have been flagged for deletion  	
		pObject->SetLockAssertOff();
		#endif
		delete pObject;
	}	
}


CGeneralManager::CGeneralManager( void )
{	
	// add flush task
	mp_kill_task = new Tsk::Task< CGeneralManager > ( flush_dead_objects, *this, Tsk::BaseTask::Node::vSYSTEM_TASK_PRIORITY_FLUSH_DEAD_OBJECTS );
	Dbg_AssertType ( mp_kill_task, Tsk::Task< CGeneralManager > );

	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	mlp_man->AddSystemTask ( *mp_kill_task ); 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeneralManager::~CGeneralManager( void )
{
	UnlockAllObjects();
	DestroyAllObjects();
		
	Dbg_AssertType ( mp_kill_task, Tsk::Task< CGeneralManager > );
	delete mp_kill_task;
}




/*
	Runs the specified function on all CObjects of the given type. Deleting is allowed.
*/
void			CGeneralManager::ProcessAllObjectsOfType( sint type, Callback* process, void* data )
{
	Lst::Search< CObject >	sh;
	
	uint32 stamp_mask = m_stamp_bit_manager.RequestBit();
	
	// clear the appropriate stamp bit
	for (CObject* pObject = sh.FirstItem(m_object_list); pObject; pObject = sh.NextItem())
	{
		pObject->ClearStampBit(stamp_mask);
	}
	
	// traverse the object list
	do
	{
		// clear the appropriate list changed bit
		m_list_changed &= ~stamp_mask;
		
        CObject* pNextObject = sh.FirstItem(m_object_list);
		
		while (pNextObject)
		{
			CObject* pObject = pNextObject;
			pNextObject = sh.NextItem();
			
			if (pObject->GetType() == type && !pObject->CheckStampBit(stamp_mask))
			{
				pObject->SetStampBit(stamp_mask);
				
				process(pObject, data);
				
				if (m_list_changed & stamp_mask) break;
			}
		}
	}
	while (m_list_changed & stamp_mask);
	
	m_stamp_bit_manager.ReturnBit(stamp_mask);
}




/*
	Runs the specified function on all CObjects in manager. Deleting is allowed.
*/
void			CGeneralManager::ProcessAllObjects( Callback* process, void *data )
{
	Lst::Search< CObject >	sh;
	
	uint32 stamp_mask = m_stamp_bit_manager.RequestBit();
	
	// clear the appropriate stamp bit
	for (CObject* pObject = sh.FirstItem(m_object_list); pObject; pObject = sh.NextItem())
	{
		pObject->ClearStampBit(stamp_mask);
	}
	
	// traverse the object list
	do
	{
		// clear the appropriate list changed bit
		m_list_changed &= ~stamp_mask;
		
        CObject* pNextObject = sh.FirstItem(m_object_list);
		
		while (pNextObject)
		{
			CObject* pObject = pNextObject;
			pNextObject = sh.NextItem();
			
			if (!pObject->CheckStampBit(stamp_mask))
			{
				pObject->SetStampBit(stamp_mask);
				
				process(pObject, data);
				
				if (m_list_changed & stamp_mask) break;
			}
		}
	}
	while (m_list_changed & stamp_mask);
	
	m_stamp_bit_manager.ReturnBit(stamp_mask);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

sint			CGeneralManager::CountObjectsOfType( sint type )
{
	

	sint					count = 0;
	CObject*					obj;
	Lst::Search< CObject >	sh;


	obj = sh.FirstItem( m_object_list );

	while ( obj )
	{
		Dbg_AssertType( obj, CObject );

		if (obj->GetType() == type)
		{
			count++;
		}
		obj = sh.NextItem();
	}

	return count;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeneralManager::AssertIfObjectsRemainApartFrom( sint *pApartFromThisType )
{
	

	#ifdef __NOPT_ASSERT__
	
	CObject* pObj;
	Lst::Search< CObject > sh;

	bool GotRemainingObjects=false;
	
	pObj = sh.FirstItem( m_object_list );

	while ( pObj )
	{
		Dbg_AssertType( pObj, CObject );

		bool Match=false;
		sint *pType=pApartFromThisType;
		while (*pType!=-1)
		{
			if (pObj->GetType()==*pType)
			{
				Match=true;
				break;
			}	
			++pType;
		}	
		
		if ( !Match )
		{
			printf("Object type = %d\n",pObj->GetType());
			GotRemainingObjects=true;
		}
		pObj = sh.NextItem();
	}

	Dbg_MsgAssert(!GotRemainingObjects,("Objects remain !!!"));
	
	#endif
}




/*
	Called by CObject::mark_as_dead(). Shouldn't call from anywhere else.
*/
void		CGeneralManager::KillObject( CObject& obj )
{	
	Dbg_AssertType( &obj, CObject );
	//Dbg_MsgAssert( obj.mp_node, ( "CObject does not have a node" ));
	Dbg_MsgAssert( !obj.m_node.InList(),( "CObject has not been unregistered" ));
	
	m_kill_list.AddToHead( &obj.m_node );
}




/*
	Returns a (currently) unused ID.
*/
uint32 CGeneralManager::NewObjectID( void )
{
	CTracker* p_tracker = CTracker::Instance();
	return p_tracker->GetFreshId();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CObject*		CGeneralManager::GetObjectByID( uint32 id )
{
	CTracker* p_tracker = CTracker::Instance();
	return p_tracker->GetObject(id);	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj



================================================
FILE: Code/Gel/Object/objsearch.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Objects (OBJ) 											**
**																			**
**	File name:		objsearch.cpp											**
**																			**
**	Created:		05/27/99	-	mjb										**
**																			**
**	Description:	Object search code										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

#include 
#include 


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Obj
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

CObject*			Search::FindFirstObjectOfType( Lst::Head< CObject >& head, sint type )
{
	
	
	Dbg_AssertType( &head, Lst::Head< CObject > );

	CObject*	obj = FirstItem( head );
	
	obj_type = type;

	while ( obj )
	{
		Dbg_AssertType( obj, CObject );

		if ( obj->GetType() == obj_type )
		{
			return obj;
		}
		
		obj = NextItem();
	}
	
	return obj;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CObject*			Search::FindNextObjectOfType( void )
{
	

	CObject*	obj = NextItem();

	while ( obj )
	{
		Dbg_AssertType( obj, CObject );
	
		if ( obj->GetType() == obj_type )
		{
			return obj;
		}
			
		obj = NextItem();
	}
	
	return obj;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj




================================================
FILE: Code/Gel/Object/objtrack.cpp
================================================
#include 
#include 

#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 


// start autoduck documentation
// @DOC objtrack
// @module objtrack | None
// @subindex Scripting Database
// @index script | bails

namespace Obj
{



CEventLog::CEventLog()
{
	m_next_entry = 0;
	m_wrapped = false;
	
	mp_event_type_hash_table = NULL;
	m_event_depth = 0;
	m_num_new_entries = 0;
}



CEventLog::~CEventLog()
{
	Dbg_Assert(mp_event_type_hash_table);
	delete mp_event_type_hash_table;
}



void CEventLog::AddEntry(uint32 type, uint32 target, uint32 source, uint32 script, uint32 receiverID, EOccurenceType occurenceType)
{
	if (!mp_event_type_hash_table)
	{
		mp_event_type_hash_table = new Lst::HashTable(8);
		Script::CArray *p_event_type_array = Script::GetArray(CRCD(0x8114f90,"event_type_array"), Script::ASSERT);
		for (int i = 0; i < (int) p_event_type_array->GetSize(); i++)
		{
			strcpy(m_event_type_table[i].mName, p_event_type_array->GetString(i));
			uint32 name_crc = Script::GenerateCRC(p_event_type_array->GetString(i));
			mp_event_type_hash_table->PutItem(name_crc, &m_event_type_table[i]);
		}
	}
			
	int last_entry = m_next_entry - 1;
	if (last_entry < 0)
		last_entry = MAX_LOG_ENTRIES - 1;
	
	if (occurenceType == vUPDATE && m_table[last_entry].m_occurence_type == vUPDATE)
	{
		// no need to keep piling on update entries, just increase tick count
		m_table[last_entry].m_tick_count++;
	}
	else
	{
		if (occurenceType == vHANDLED)
			m_event_depth--;

		Entry *p_entry = m_table + m_next_entry;
		if (occurenceType == vUPDATE)
			p_entry->m_tick_count		= 0;
		else
			p_entry->m_type 			= type;
		p_entry->m_target 				= target;
		p_entry->m_source 				= source;
		p_entry->m_script 				= script;
		p_entry->m_receiver_id			= receiverID;
		p_entry->m_occurence_type		= occurenceType;
		p_entry->m_depth				= m_event_depth;
	
		m_next_entry++;
		if (m_next_entry == MAX_LOG_ENTRIES)
		{
			m_next_entry = 0;
			m_wrapped = true;
		}
		
		if (occurenceType == vLAUNCHED)
			m_event_depth++;
		
		m_num_new_entries++;
		
		//PrintLatestEntry();
	}
}



void CEventLog::Print(bool onlyPrintNewEntries, int maxEntriesToPrint)
{
	int index = 0;
	int count = m_next_entry;
	if (m_wrapped)
	{
		index = m_next_entry;
		count = MAX_LOG_ENTRIES;
	}

	if (onlyPrintNewEntries)
	{
		maxEntriesToPrint = m_num_new_entries;
	}
	
	printf("========================================================\n");
	if (onlyPrintNewEntries)
		printf("PRINTING EVENT LOG (ONLY NEW ENTRIES)\n\n");
	else
		printf("PRINTING EVENT LOG\n\n");
	
	while(count > 0)
	{
		if (count <= maxEntriesToPrint)		
			print_entry(index);
		index++;
		if (index == MAX_LOG_ENTRIES) index = 0;
		count--;
	}
	
	printf("========================================================\n");

	m_num_new_entries = 0;
}



void CEventLog::PrintLatestEntry()
{
	int last_entry = m_next_entry - 1;
	if (last_entry < 0)
		last_entry = MAX_LOG_ENTRIES - 1;
	print_entry(last_entry);
	m_num_new_entries = 0;
}



void CEventLog::print_entry(int index)
{
#ifdef __NOPT_ASSERT__
	Entry *p_entry = m_table + index;

	//printf("%d ", p_entry->m_depth);
	for (int i = 0; i < p_entry->m_depth; i++) printf(" ");
	
	// entry format: (<...> denotes optional)
	// "ACTION: type=TYPE    "
	// or: "ACTION: type=TYPE   "
	
	if (p_entry->m_occurence_type == vLAUNCHED || p_entry->m_occurence_type == vHANDLED || p_entry->m_occurence_type == vREAD) 
	{
		if (p_entry->m_occurence_type == vLAUNCHED) 
			printf("Launched event: type=%s ", get_type_name(p_entry->m_type));
		else if (p_entry->m_occurence_type == vHANDLED) 
		{
			if (p_entry->m_receiver_id == CTracker::vID_OBJECT_TRACKER)
				printf("Event expired: type=%s ", get_type_name(p_entry->m_type));
			else
				printf("Handled event: type=%s ", get_type_name(p_entry->m_type));
		}
		else if (p_entry->m_occurence_type == vREAD) 
			printf("Read event: type=%s ", get_type_name(p_entry->m_type));
		
		bool reciever_equals_target = (p_entry->m_target == p_entry->m_receiver_id);

		if (p_entry->m_target != CEvent::vSYSTEM_EVENT) 
		{
			const char *p_target_name;
			p_target_name = Script::FindChecksumName(p_entry->m_target);
			
			if (reciever_equals_target)
				printf("target/receiver=%s ", p_target_name);
			else
				printf("target=%s ", p_target_name);
		}
		if (!reciever_equals_target && p_entry->m_receiver_id != CTracker::vID_UNSPECIFIED_RECEIVER) 
		{
			const char *p_receiver_name;
			if (p_entry->m_receiver_id == CTracker::vID_SCREEN_ELEMENT_MANAGER) 
				p_receiver_name = "Screen Element Manager";
			else if (p_entry->m_receiver_id == CTracker::vID_SUSPENDED_SCRIPT) 
				p_receiver_name = "Suspended Script";
			else if (p_entry->m_receiver_id == CTracker::vID_OBJECT_TRACKER) 
				p_receiver_name = "(Killed By) Object Tracker";
			else
				p_receiver_name = Script::FindChecksumName(p_entry->m_target);
			
			printf("receiver=%s ", p_receiver_name);
		}
		if (p_entry->m_source != CEvent::vSYSTEM_EVENT) 
			printf("source=%s ", Script::FindChecksumName(p_entry->m_source));
		if (p_entry->m_script) 
			printf("script=%s ", Script::FindChecksumName(p_entry->m_script));
		printf("\n");
	}
	else if (p_entry->m_occurence_type == vOBJECT_ADD || p_entry->m_occurence_type == vOBJECT_REMOVE) 
	{
		if (p_entry->m_occurence_type == vOBJECT_ADD)
			printf("Added object: %s", Script::FindChecksumName(p_entry->m_target));
		else
			printf("Removed object: %s", Script::FindChecksumName(p_entry->m_target));
		printf("\n");
	}
	else if (p_entry->m_occurence_type == vUPDATE) 
	{
		printf("Tick, counts = %d\n", p_entry->m_tick_count);
	}
#endif  
}



const char *CEventLog::get_type_name(uint32 type)
{
	EventType *p_type_entry = mp_event_type_hash_table->GetItem(type);
	if (p_type_entry)
	{
		return p_type_entry->mName;
	}
	else
		return Script::FindChecksumName(type);
}



DefineSingletonClass( CTracker, "CObject Tracker" );



/*
	Every CObject that is registered with a CBaseManager should also be registered with CTracker.
*/
void CTracker::addObject(CObject *pObject)
{
	Dbg_MsgAssert(pObject->GetID() != 0xFFFFFFFF, ("CObject has no ID"));
	Dbg_MsgAssert(!mp_hash_table->GetItem(pObject->GetID()), ("CObject with ID %s already in tracking system", Script::FindChecksumName(pObject->GetID())));
	
	// if object ID already being used as alias, remove the alias
	CObject *p_alias_obj = mp_alias_table->GetItem(pObject->GetID());
	if (p_alias_obj)
	{
		p_alias_obj->RemoveReference();
		mp_alias_table->FlushItem(pObject->GetID());
	}
	
	mp_hash_table->PutItem(pObject->GetID(), pObject);
#ifdef __DEBUG_OBJ_MAN__
	printf("*** Added object %s to global tracker\n", Script::FindChecksumName(pObject->GetID()));
#endif

	m_event_log.AddEntry(0, pObject->GetID(), CEvent::vSYSTEM_EVENT, 0, 0, CEventLog::vOBJECT_ADD);
}




/*
	Ryan Old Comment: The 'newIdOfObjectBeingMomentarilyRemoved' parameter is set if we are just changing the ID of the object,
	which requires removing it, then adding it again. Otherwise, this parameter will be zero (the id of the skater, fool!)
	Mick New comment: now we have an additional boolean passed top indicate if the value in newIdOfObjectBeingMomentarilyRemoved
	is valid, as it might be 0, if we are changing the id of a client skater back to 0
*/

void CTracker::removeObject(CObject *pObject, uint32 newIdOfObjectBeingMomentarilyRemoved, bool momentary_removal)
{
	mp_hash_table->FlushItem(pObject->GetID());
	if (momentary_removal)
	{
		// go through all the scripts waiting on object, change the ID
		for (int i = 0; i < vMAX_SCRIPT_ENTRIES; i++)
		{
			if (m_waiting_script_tab[i].mObjectId == pObject->GetID())
			{
				m_waiting_script_tab[i].mObjectId = newIdOfObjectBeingMomentarilyRemoved;
			}
		}
	}
	else
	{
		// only remove aliases to an object if it is going away "permanently"
		remove_aliases(pObject);
	}

#ifdef __DEBUG_OBJ_MAN__
	printf("*** Removed object %s from global tracker\n", Script::FindChecksumName(pObject->GetID()));
#endif

	m_event_log.AddEntry(0, pObject->GetID(), CEvent::vSYSTEM_EVENT, 0, 0, CEventLog::vOBJECT_REMOVE);
}




/*
	Forwards the event to listeners associated with the object. Handy for forwarding global
	events to specific recipients.
	
	If no object specified, event goes to all listeners.
*/
void CTracker::forward_event_to_listeners(CEvent *pEvent, CObject *pObject)
{
	Dbg_Assert(pEvent);
	
	// Try event on all registered listeners
	// The ref counting stuff will fire an assert if an	event listener gets deleted
	CEventListener *p_listener = mp_event_listener_list;
	while (p_listener)
	{
		// prevents listener from being deleted (without an assert)
		p_listener->m_ref_count++;
		if (!pObject || p_listener->mp_object == pObject)
			p_listener->event_filter(pEvent);
		p_listener->m_ref_count--;
		p_listener = p_listener->mp_next_listener;
	}
}




/*
	When a CObject is removed from tracking, all aliases that point to it must be removed.
*/
void CTracker::remove_aliases(CObject *pObject)
{
	while(1)
	{
		//if (mp_alias_table->GetSize() == 0)
		//	break;
		
		mp_alias_table->IterateStart();
		uint32 entry_key;
		CObject *p_entry = mp_alias_table->IterateNext(&entry_key);
		while(p_entry)
		{
			if (p_entry->GetID() == pObject->GetID())
			{
				// Found match. Remove it and repeat outer loop
				p_entry->RemoveReference();
				mp_alias_table->FlushItem(entry_key);
				break;
			}
			p_entry = mp_alias_table->IterateNext(&entry_key);
		}

		if (!p_entry)
			// no more matches left, we're done
			break;
	}
}




CTracker::CTracker()
{
	mp_hash_table = new Lst::HashTable(8);
	mp_alias_table = new Lst::HashTable(4);
	mp_event_receiver_table = new Lst::HashTable(8);	
	
	m_id_seed = 0;

	mp_event_listener_list = NULL;
	m_block_event_launching = false;
	m_event_recurse_depth = 0;

	m_next_event_script = 0;

	for (int i = 0; i < vMAX_SCRIPT_ENTRIES; i++) 
		m_waiting_script_tab[i].mEventType = vDEAD_SCRIPT_ENTRY;
	
	// XXX
	m_debug = false;
}




CTracker::~CTracker()
{
	Dbg_MsgAssert(mp_hash_table->GetSize(), ("entries still in tracker"));
	delete mp_hash_table;
}




/*
	Returns a pointer to the CObject whose ID matches the one given, returns NULL if not found.
	Will return the object if it has an alias, which is used in the front end for stuff like "MainMenu"
	But is also now used for "Skater" and "Skater2"
*/
CObject *CTracker::GetObject(uint32 id)
{
	CObject *p_obj = mp_hash_table->GetItem(id);
	if (!p_obj)
	{
		p_obj = mp_alias_table->GetItem(id);
	}
	return p_obj;
}




CObject *CTracker::GetObjectByAlias(uint32 aliasId)
{
	return mp_alias_table->GetItem(aliasId);
}




void CTracker::AddAlias(uint32 alias, CObject *pObject)
{
	// make sure alias not already being used for object ID
	Dbg_MsgAssert(!mp_hash_table->GetItem(alias), ("CObject with ID %s already in tracking system", Script::FindChecksumName(alias)));
	
	// if desired alias already being used as alias, remove old one
	CObject *p_alias_obj = mp_alias_table->GetItem(alias);
	if (p_alias_obj)
	{
		p_alias_obj->RemoveReference();
		mp_alias_table->FlushItem(alias);
	}

	pObject->AddReference();
	mp_alias_table->PutItem(alias, pObject);
}




/*
	Returns an ID that is not currently being used. Pretty high probability of not
	colliding with a user-created ID.
*/
uint32 CTracker::GetFreshId()
{
	while(1)
	{
		char name_string[64];
		sprintf(name_string, "autoid%d", m_id_seed++);
		if (m_id_seed >= 1000000)
			m_id_seed = 0;
		uint32 id = Script::GenerateCRC(name_string);
		if (!mp_hash_table->GetItem(id))
		{
			return id;
		}
	}
}

#ifdef	__NOPT_ASSERT__

// Debug function to track down corruption in scripts event handler tables
// we iterate over all the event receivers, and check that the tables that
// they point to are valid

void CTracker::ValidateReceivers()
{
	
	mp_event_receiver_table->IterateStart();
	uint32 entry_key;
	CEventReceiverList *p_entry = mp_event_receiver_table->IterateNext(&entry_key);
	while(p_entry)
	{
		p_entry = mp_event_receiver_table->IterateNext(&entry_key);
		if (p_entry)
		{
				Lst::Node< Script::CScript >*p_node = p_entry->FirstItem();
				while (p_node)
				{
					Script::CScript * p_script =  p_node->GetData();
					// Got a pointer to a node, so first validate that
					// then validate the event handler table
					
#ifndef __PLAT_WN32__	// These script functions are not necessary from PC tools

					Obj::CEventHandlerTable * p_event_handler_table = p_script->GetEventHandlerTable();
					// Validate the table object
					Dbg_MsgAssert(Mem::Valid(p_event_handler_table),("Corrupt Event handler table object for event %s\n",Script::FindChecksumName(entry_key)));
					// and the table it contains
					Dbg_MsgAssert(!p_event_handler_table->m_num_entries || Mem::Valid(p_event_handler_table->mp_tab),("Corrupt Event handler table actual table for event %s\n",Script::FindChecksumName(entry_key)));

#endif

					p_node = p_node->GetNext();					
				}

			
		}
	
	}
}
#endif


// returns true if event was handled
bool CTracker::LaunchEvent(uint32 type, uint32 target, uint32 source, Script::CStruct *pData, bool broadcast)
{
//	printf("launch event, type=%s, target=0x%x, source= 0x%x, pData = %p\n", Script::FindChecksumName(type),target,source, pData);
	
//	Dbg_MsgAssert(!broadcast,("Don't use the broadcast flag yet!!!"));
	
	Dbg_MsgAssert(!m_block_event_launching, ("event launches are blocked"));
	Dbg_MsgAssert(m_event_recurse_depth < vMAX_EVENT_RECURSE_DEPTH, ("too many nested LaunchEvents -- check your scripts"));
	//printf("### launch event depth %d\n", m_event_recurse_depth);
	m_event_recurse_depth++;
	
	// log event
	m_event_log.AddEntry(type, target, source, m_next_event_script, 0, CEventLog::vLAUNCHED);
	m_next_event_script = 0;
	
	CEvent event;
	event.m_type = type;
	event.m_target_id = target;
	event.m_source_id = source;
	event.mp_data = pData;
	
	


	
	if (broadcast)
	{	
		#ifdef __SCRIPT_EVENT_TABLE__

		// broadcast to each script that has an event handler table
		CEventReceiverList *p_event_list = mp_event_receiver_table->GetItem(type);
		if (p_event_list)
		{
			int scripts = p_event_list->CountItems();
			Dbg_MsgAssert(scripts, ("Empty list for event %s",Script::FindChecksumName(type)));
			if (scripts == 1)
			{
				// Just one object, so do a quicker sending of the event
				Lst::Node< Script::CScript >*p_node =   p_event_list->FirstItem();
				Script::CScript *p_script = p_node->GetData();
				p_script->PassTargetedEvent(&event, broadcast);			
			}
			else
			{
				// For multiple scripts, we need to make a copy of the the list of scripts
				// that will receive this. Otherwise chaos ensues, as scripts change their event handlers based on events. 
				// We also use a list of smart pointers to the mp_reference in a script, so if one event handler deletes
				// another object (and hence its script)
				// then we can safely ignore that object (since it no longer exists)
				// as the pointer will be set to NULL.			
				
				Script::CScript ** pp_scripts = new Script::CScript*[scripts];
				CSmtPtr * pp_refs = new CSmtPtr[scripts];
				
				Lst::Node< Script::CScript >*p_node = p_event_list->FirstItem();
				int n=0;
				while (p_node)
				{
					pp_scripts[n] = p_node->GetData();
					CRefCounted * p_ref = &(p_node->GetData()->m_reference_counter);
					pp_refs[n] = p_ref;
					
					n++;
					p_node = p_node->GetNext();					
				}
				for (n = 0; nPassTargetedEvent(&event, broadcast);			
					}
				}
				delete [] pp_refs;	 
				delete [] pp_scripts;
				
				
			}
			
		}
		
		
		#else
		// broadcast to each object that has an event handler table
		CEventReceiverList *p_event_list = mp_event_receiver_table->GetItem(type);
		if (p_event_list)
		{
			int objects = p_event_list->CountItems();
			Dbg_MsgAssert(objects, ("Empty list for event %s",Script::FindChecksumName(type)));
			if (objects == 1)
			{
				// Just one object, so do a quicker sending of the event
				Lst::Node< Obj::CObject >*p_node =   p_event_list->FirstItem();
				Obj::CObject *p_obj = p_node->GetData();
				p_obj->PassTargetedEvent(&event, broadcast);			
			}
			else
			{
				// For multiple objects, we need to make a copy of the the list of objects
				// that will receive this. Otherwise chaos ensues, as objects change their event handlers based on events. 
				// We use a list of smart pointers, so if one object handler deletes another object
				// then we can safely ignore that object (since it no longer exists)
				// as the pointer will be set to NULL.			
				CObjectPtr * pp_objects = new CObjectPtr[objects];
				Lst::Node< Obj::CObject >*p_node = p_event_list->FirstItem();
				int n=0;
				while (p_node)
				{
					pp_objects[n++] = p_node->GetData();
					p_node = p_node->GetNext();
				}
				for (n = 0; nPassTargetedEvent(&event, broadcast);			
					}
				}
				delete [] pp_objects;
				
				
			}
			
		}
		#endif
	}
	else
	{
		
		
		// try event on all registered listeners
		forward_event_to_listeners(&event, NULL);
	
		// if event has a specific target, then send to that CObject
		if (event.m_target_id != CEvent::vSYSTEM_EVENT && !event.WasHandled())
		{
			// look for a object with the target id
			CObject *p_object = GetObject(event.m_target_id);
			if (!p_object)
			{
				// or look for a script with the target id
				Script::CScript *p_script = Script::FindSpawnedScriptWithID(event.m_target_id);
				
				if (!p_script)
				{
					m_event_log.Print(256);
					#ifdef __NOPT_ASSERT__			
					mp_hash_table->PrintContents();
					#endif
				}
				
				Dbg_MsgAssert(p_script, ("couldn't find object or script id %s", Script::FindChecksumName(event.m_target_id)));
				
				p_script->PassTargetedEvent(&event);
			}
			else
			{
				p_object->PassTargetedEvent(&event);
			}
			// p_object may have been deleted
		}
	}
	
	/* 
		See if any scripts were waiting on the event.
		
		Unblock script if type matches AND:
		-target matches
		-OR script not associated with object
		-OR a system event
	*/
	for (int i = 0; i < vMAX_SCRIPT_ENTRIES; i++)
	{
		//if (m_waiting_script_tab[i].mEventType == Script::GenerateCRC("showed_wait_message"))
		//	Ryan("   hoorah: 0x%x 0x%x\n", m_waiting_script_tab[i].mObjectId, target);

		if (m_waiting_script_tab[i].mEventType == type && 
			(m_waiting_script_tab[i].mObjectId == target || m_waiting_script_tab[i].mObjectId == 0 || target == CEvent::vSYSTEM_EVENT || broadcast))
		{			
			// We found a suspended script of the correct type and target.
			// Can remove entry.
			m_waiting_script_tab[i].mEventType = vDEAD_SCRIPT_ENTRY;

			// does the script actually exist right now?
			Script::CScript *p_script = Script::GetScriptWithUniqueId(m_waiting_script_tab[i].mScriptId);
			if (p_script)
			{
				p_script->UnBlock();
				event.MarkHandled(vID_SUSPENDED_SCRIPT, p_script->mScriptChecksum);
				//printf("unblocking script %s (id=%d)\n", Script::FindChecksumName(p_script->mScriptChecksum), m_waiting_script_tab[i].mScriptId);
				// alright, the event is handled, so leave loop
				break;
			}
			else
			{
				//printf("script to unblock doesn't exist (id=%d)\n", m_waiting_script_tab[i].mScriptId);
			}

			// the event is NOT handled
		}
	}

	if (!event.WasHandled())
		// no one handled event, mark as killed by object tracker
		LogEventHandled(&event, vID_OBJECT_TRACKER);
	
	m_event_recurse_depth--;

	return event.WasHandled();
}




// Call right before calling LaunchEvent()
void CTracker::LogEventScript(uint32 script)
{
	m_next_event_script = script;
}



void CTracker::LogEventHandled(CEvent *pEvent, uint32 receiverID, uint32 script)
{
	// log event
	m_event_log.AddEntry(pEvent->GetType(), pEvent->GetTarget(), pEvent->GetSource(), script, receiverID, CEventLog::vHANDLED);
}



void CTracker::LogEventRead(CEvent *pEvent, uint32 receiverID, uint32 script)
{
	// log event
	m_event_log.AddEntry(pEvent->GetType(), pEvent->GetTarget(), pEvent->GetSource(), script, receiverID, CEventLog::vREAD);
}



void CTracker::LogTick()
{
	m_event_log.AddEntry(0, 0, 0, 0, 0, CEventLog::vUPDATE);
}



void CTracker::PrintEventLog(bool mostRecentOnly, int maxToPrint)
{
	m_event_log.Print(mostRecentOnly, maxToPrint);
}



void CTracker::RegisterEventListener(CEventListener *pListener)
{
	Dbg_MsgAssert(!pListener->m_registered, ("listener already registered"));

	pListener->mp_next_listener = mp_event_listener_list;
	mp_event_listener_list = pListener;
	pListener->m_registered = true;
}




void CTracker::UnregisterEventListener(CEventListener *pListener)
{
	Dbg_MsgAssert(pListener->m_registered, ("listener not registered"));

	CEventListener *p_prev = NULL;
	CEventListener *p_current = mp_event_listener_list;
	while(p_current)
	{
		if (p_current == pListener)
		{
			if (p_prev)
				p_prev->mp_next_listener = pListener->mp_next_listener;
			else
				mp_event_listener_list = pListener->mp_next_listener;
			break;
		}
		
		p_prev = p_current;
		p_current = p_current->mp_next_listener;
	}
	pListener->m_registered = false;
}

// Add this object to the list of objects that are listening for
// events of this "type", so any broadcast event of this type will go directly there
#ifdef	__SCRIPT_EVENT_TABLE__		
void		CTracker::RegisterEventReceiver(uint32 type, Script::CScript *p_obj)
#else
void		CTracker::RegisterEventReceiver(uint32 type, CObject *p_obj)
#endif
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
//	printf ("Registering Event %s from Object %s\n",Script::FindChecksumName(type), Script::FindChecksumName(p_obj->GetID()));
// If there is no CEventList in the hash table for "type" then create one
	CEventReceiverList *p_event_list = mp_event_receiver_table->GetItem(type);
	if (!p_event_list)
	{
		p_event_list = new CEventReceiverList();
		mp_event_receiver_table->PutItem(type,p_event_list); 
	}
	
// Add this object to the that list, if it is not already added
	p_event_list->RegisterEventReceiverObject(p_obj);
	
	Mem::Manager::sHandle().PopContext();		
}



// Remove this object from the list of objects that are listening for
// events of this "type"
#ifdef	__SCRIPT_EVENT_TABLE__		
void		CTracker::UnregisterEventReceiver(uint32 type, Script::CScript *p_obj)
#else
void		CTracker::UnregisterEventReceiver(uint32 type, CObject *p_obj)
#endif
{
//	printf ("Unregistering Event %s from Object %s\n",Script::FindChecksumName(type), Script::FindChecksumName(p_obj->GetID()));
	// If there is a reciever list for this "type"
	CEventReceiverList *p_event_list = mp_event_receiver_table->GetItem(type);
	if (p_event_list)
	{
		// then remove the object from the CEventList
		p_event_list->UnregisterEventReceiverObject(p_obj);
	
		// If the list is empty
		if (p_event_list->IsEmpty())
		{		
			// Then remove it from the hash table
			mp_event_receiver_table->FlushItem(type);
			// and delete it
			delete p_event_list; 
		}
	}
}


CEventReceiverList::CEventReceiverList()
{

}


// Register an object with this event receiver list
// all this does is add it to the list, so events of this "type" are sent to it
// it will not be added to the same list twice (just ignores additional registers)
	
#ifdef	__SCRIPT_EVENT_TABLE__		
void		CEventReceiverList::RegisterEventReceiverObject(Script::CScript *p_script)
{
	// See if the object is already in the list, and return if it is
	Lst::Node * p_search = m_objects.FirstItem();
	while (p_search)
	{
		if (p_search->GetData() == p_script)
			return;
		p_search = p_search->GetNext();
	}
	
	// Create a new node for adding to the list
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	Lst::Node* p_node = new Lst::Node(p_script);
	Mem::Manager::sHandle().PopContext();	
	// Just add it to the tail of the list
	m_objects.AddToTail(p_node);
}
#else

void		CEventReceiverList::RegisterEventReceiverObject(CObject *p_obj)
{
	// See if the object is already in the list, and return if it is
	Lst::Node * p_search = m_objects.FirstItem();
	while (p_search)
	{
		if (p_search->GetData() == p_obj)
			return;
		p_search = p_search->GetNext();
	}
	
	// Create a new node for adding to the list
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	Lst::Node* p_node = new Lst::Node(p_obj);
	Mem::Manager::sHandle().PopContext();	
	// Just add it to the tail of the list
	m_objects.AddToTail(p_node);
}
#endif

// remove an object from this list (if it is in it)
// if it is not in the list, it will just be ignored
#ifdef	__SCRIPT_EVENT_TABLE__		
void		CEventReceiverList::UnregisterEventReceiverObject(Script::CScript *p_script)
{
	// Get the node from the list

	Lst::Node* p_node = NULL;
	Lst::Node * p_search = m_objects.FirstItem();
	while (p_search)
	{
		if (p_search->GetData() == p_script)
		{
			p_node = p_search;
			break;
		}
		p_search = p_search->GetNext();
	}
	if (p_node)
	{
		
		// Just remove it from the list
		p_node->Remove();
		
		// and delete the node (might be left with an empty list, but the tracker is responsible for cleaning it up)
		delete p_node;
	}	
}
#else

void		CEventReceiverList::UnregisterEventReceiverObject(CObject *p_obj)
{
	// Get the node from the list

	Lst::Node* p_node = NULL;
	Lst::Node * p_search = m_objects.FirstItem();
	while (p_search)
	{
		if (p_search->GetData() == p_obj)
		{
			p_node = p_search;
			break;
		}
		p_search = p_search->GetNext();
	}
	if (p_node)
	{
		
		// Just remove it from the list
		p_node->Remove();
		
		// and delete the node (might be left with an empty list, but the tracker is responsible for cleaning it up)
		delete p_node;
	}	
}
#endif



/*
	Causes a script to be blocked until the (future) arrival of an event of the designated type.
	If the script is associated with an object AND the event has a target, then the target
	must be that object.
*/
void CTracker::SuspendScriptUntilEvent(Script::CScript *pScript, uint32 event_type)
{
	// find an unused entry

	int unused_entry_index = -1;
	int i = 0;
	for (; i < vMAX_SCRIPT_ENTRIES; i++) 
	{
		if (m_waiting_script_tab[i].mEventType == vDEAD_SCRIPT_ENTRY)
		{
			unused_entry_index = i;
			break;
		}
	}
	
	if (unused_entry_index < 0)
	{
		// hmm, no unused entries, so find entry whose script is dead
		for (i = 0; i < vMAX_SCRIPT_ENTRIES; i++) 
		{
			Script::CScript *p_script = Script::GetScriptWithUniqueId(m_waiting_script_tab[i].mScriptId);
			if (!p_script)
			{
				unused_entry_index = i;
				break;
			}
		}
	}

	if (unused_entry_index >= 0)
	{
		//printf("suspending script %s (id=%d)\n", Script::FindChecksumName(pScript->mScriptChecksum), pScript->GetUniqueId());
		m_waiting_script_tab[unused_entry_index].mScriptId = pScript->GetUniqueId();
		if (pScript->mpObject)
			m_waiting_script_tab[unused_entry_index].mObjectId = pScript->mpObject->GetID();
		else
			m_waiting_script_tab[unused_entry_index].mObjectId = 0;
		m_waiting_script_tab[unused_entry_index].mEventType = event_type;

		#ifdef __NOPT_ASSERT__
		m_waiting_script_tab[unused_entry_index].mScriptName = pScript->mScriptChecksum;
		#endif

		// suspend the script until later notice
		pScript->Block();
	}
	else
	{
		#ifdef __NOPT_ASSERT__
		printf("out of script entries, scripts still waiting for events:\n");
		for (i = 0; i < vMAX_SCRIPT_ENTRIES; i++) 
		{
			printf("   %s\n", Script::FindChecksumName(m_waiting_script_tab[i].mScriptName));
		}
		Dbg_MsgAssert(0, ("%s\nout of script entries (%d)in WaitForEvent",pScript->GetScriptInfo(),vMAX_SCRIPT_ENTRIES));
		#endif
	}
}



#ifndef __PLAT_WN32__	// These script functions are not necessary from PC tools

// @script | LaunchEvent | 
// @parm name | type | event type
// @parm structure | data | 
bool ScriptLaunchEvent(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Although events aren't necessarily tied to the Screen Element system, it is
	// convenient to enable this function to support complex ID's, for when we
	// are dealing with Screen Elements
	Front::CScreenElementManager* pManager = Front::CScreenElementManager::Instance();
	uint32 target = pManager->ResolveComplexID(pParams, CRCD(0xb990d003,"target"));
	if (target)	
	{
		if (target == CRCD(0x36b2ee74, "system"))
			target = CEvent::vSYSTEM_EVENT;			
	}
	else
	{
		if (pScript->mpObject)
			target = pScript->mpObject->GetID();
		else
			target = CEvent::vSYSTEM_EVENT;
	}
	
	uint32 source = pManager->ResolveComplexID(pParams, CRCD(0xa075808c,"source"));
	if (source)	
	{
		if (source == CRCD(0x36b2ee74, "system"))
			source = CEvent::vSYSTEM_EVENT;			
	}
	else
	{
		if (pScript->mpObject)
			source = pScript->mpObject->GetID();
		else
			source = CEvent::vSYSTEM_EVENT;
	}	
		
	Script::CStruct *pData = NULL;
	pParams->GetStructure(CRCD(0x520c0c9c,"data"), &pData);
	
	bool broadcast = pParams->ContainsFlag(CRCD(0x640e830a,"broadcast"));
	
	CTracker* p_tracker = CTracker::Instance();	
	
	uint32 type;
	if (pParams->GetChecksum(CRCD(0x7321a8d6,"type"), &type))
	{
		p_tracker->LogEventScript(pScript->mScriptChecksum);
		p_tracker->LaunchEvent(type, target, source, pData, broadcast);
	}
	else
	{
		Script::CArray* pTypes;
		if (pParams->GetArray(CRCD(0x7321a8d6,"type"), &pTypes))
		{
			Dbg_Assert(pTypes->GetType() == ESYMBOLTYPE_NAME)
			unsigned num_events = pTypes->GetSize();
			for (unsigned n = 0; n < num_events; n++)
			{
				p_tracker->LogEventScript(pScript->mScriptChecksum);
				p_tracker->LaunchEvent(pTypes->GetChecksum(n), target, source, pData, broadcast);
			}
		}
		else
		{
			Script::CStruct* pTypes;
			if (pParams->GetStructure(CRCD(0x7321a8d6,"type"), &pTypes))
			{
				for (Script::CComponent* pComp = pTypes->GetNextComponent(); pComp; pComp = pTypes->GetNextComponent(pComp))
				{
					Dbg_Assert(pComp->mType == ESYMBOLTYPE_NAME);
					p_tracker->LogEventScript(pScript->mScriptChecksum);
					p_tracker->LaunchEvent(pComp->mChecksum, target, source, pData, broadcast);
				}
			}
			else
			{
				Dbg_MsgAssert(0, ("can't launch event without type"));
			}
		}
	}
	
	return true;
}


bool ScriptWaitForEvent(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 type;
	if (!pParams->GetChecksum(CRCD(0x7321a8d6,"type"), &type))
		Dbg_MsgAssert(0, ("no event type specified"));;
	
	CTracker* p_tracker = CTracker::Instance();	
	p_tracker->SuspendScriptUntilEvent(pScript, type);

	return true;
}




bool ScriptObjectExists(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Although events aren't necessarily tied to the Screen Element system, it is
	// convenient to enable this function to support complex ID's, for when we
	// are dealing with Screen Elements
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	uint32 id = p_manager->ResolveComplexID(pParams, CRCD(0x40c698af,"id"));
	
	CTracker* p_tracker = CTracker::Instance();
	return (p_tracker->GetObject(id) != NULL);
}




bool ScriptTerminateObjectsScripts(Script::CStruct *pParams, Script::CScript *pScript)
{
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	uint32 id = p_manager->ResolveComplexID(pParams, CRCD(0x40c698af,"id"));
	
	CTracker* p_tracker = CTracker::Instance();
	CObject *p_object = p_tracker->GetObject(id);
	
	
	// Brad - the use_proper_version flag is a last minute fix at the end of THPS4.
	// StopScriptsUsingThisObject is broken, but fixing it may break other things.  The
	// proper version works as it always should have.  It is only used when the user 
	// specifically requests it.
	bool use_proper = pParams->ContainsFlag( CRCD(0xc89f3564,"use_proper_version") );
	
	// see if array of script names
	Script::CArray *p_array;
	if (pParams->GetArray(CRCD(0x22e168c1,"scripts"), &p_array))
	{
		for (uint i = 0; i < p_array->GetSize(); i++)
		{
			if ( use_proper )
				Script::StopScriptsUsingThisObject_Proper(p_object, p_array->GetChecksum(i));
			else
				Script::StopScriptsUsingThisObject(p_object, p_array->GetChecksum(i));
		}
	}
	else
	{
		uint32 script_to_stop = 0; // means 'stop all'
		pParams->GetChecksum(CRCD(0x6166f3ad,"script_name"), &script_to_stop);
		if ( use_proper )
			Script::StopScriptsUsingThisObject_Proper(p_object, script_to_stop);
		else
			Script::StopScriptsUsingThisObject(p_object, script_to_stop);
	}

	return true;
}




bool ScriptAssignAlias(Script::CStruct *pParams, Script::CScript *pScript)
{
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	uint32 id_of_original = p_manager->ResolveComplexID(pParams, CRCD(0x40c698af,"id"));
	
	CTracker* p_tracker = CTracker::Instance();	
	CObject *p_object_to_alias = p_tracker->GetObject(id_of_original);
	Dbg_Assert(p_object_to_alias);
	
	uint32 alias;
	pParams->GetChecksum(CRCD(0x1e93946b,"alias"), &alias, Script::ASSERT);

	p_tracker->AddAlias(alias, p_object_to_alias);
	
	return true;
}




bool ScriptSetObjectProperties(Script::CStruct *pParams, Script::CScript *pScript)
{
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	uint32 id_of_original = p_manager->ResolveComplexID(pParams, CRCD(0x40c698af,"id"));
	
	CTracker* p_tracker = CTracker::Instance();	
	CObject *p_object = p_tracker->GetObject(id_of_original);
	Dbg_Assert(p_object);
	
	p_object->SetProperties(pParams);
	
	return true;
}




bool ScriptPrintEventLog(Script::CStruct *pParams, Script::CScript *pScript)
{
	bool only_print_new = true;
	
	int num_to_print = CEventLog::MAX_LOG_ENTRIES;
	if (pParams->GetInteger(NONAME, &num_to_print))
		only_print_new = false;
	
	CTracker* p_tracker = CTracker::Instance();	
	p_tracker->PrintEventLog(only_print_new, num_to_print);
	return true;
}


#endif

}


================================================
FILE: Code/Gel/Prefs/Prefs.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate3													**
**																			**
**	Module:			GameNet					 								**
**																			**
**	File name:		GameNet.cpp												**
**																			**
**	Created:		03/12/2001	-	gj										**
**																			**
**	Description:	Network Preferences 									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 


#include 

#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Prefs
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Preferences::Preferences()
{
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Preferences::~Preferences()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 Preferences::WriteToBuffer(uint8 *pBuffer, uint32 BufferSize)
{
	return Script::WriteToBuffer(&m_root, pBuffer, BufferSize);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Preferences::ReadFromBuffer(uint8 *pBuffer)
{
	Script::ReadFromBuffer(&m_root,pBuffer);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* Preferences::GetPreference( uint32 field_id )
{
	Script::CStruct* p_structure = NULL;
	m_root.GetStructure( field_id, &p_structure );
	return p_structure;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CArray* Preferences::GetPreferenceArray( uint32 field_id )
{
	Script::CArray* p_array = NULL;
	m_root.GetArray( field_id, &p_array );

	return p_array;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Preferences::RemoveComponent( uint32 field_id, uint32 sub_field_id )
{
	Script::CStruct* p_structure = NULL;
	m_root.GetStructure( field_id, &p_structure );

	p_structure->RemoveComponent( sub_field_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Preferences::SetRoot(Script::CStruct* pStuff)
{
	
	// OK if pStuff is NULL, so no need to assert
	// Probably still an error if it is NULL, but AppendStructure won't crash if it is.
	
	// Don't clear out the options structure. New stuff should just override old options
	//m_root.Clear();
	m_root.AppendStructure(pStuff); 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* Preferences::GetPreferenceString( uint32 field_id, uint32 sub_field_id )
{
	Script::CStruct* p_structure = NULL;
	m_root.GetStructure( field_id, &p_structure );

	const char* p_string;
	p_structure->GetText( sub_field_id, &p_string, true );
	return p_string;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Preferences::GetPreferenceValue( uint32 field_id, uint32 sub_field_id )
{
	Script::CStruct* p_structure = NULL;
	m_root.GetStructure( field_id, &p_structure );

	int returnVal;
	p_structure->GetInteger( sub_field_id, &returnVal, true );
	return returnVal;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 Preferences::GetPreferenceChecksum( uint32 field_id, uint32 sub_field_id )
{
	Script::CStruct* p_structure = NULL;
	m_root.GetStructure( field_id, &p_structure, true );

	uint32 returnVal;
	p_structure->GetChecksum( sub_field_id, &returnVal, true );
	return returnVal;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Preferences::SetPreference( uint32 field_id, Script::CStruct* p_to_append )
{
	
	
	Script::CStruct* p_structure = NULL;

	// TODO:  Decide if we can add preferences at runtime,
	// or whether all the categories already exist at load-time
	if ( !m_root.GetStructure( field_id, &p_structure ) )
	{
		// structure doesn't already exist, so it's not a valid preference
		Dbg_MsgAssert( 0,( "Trying to add an invalid preference" ));
		return false;
	}
	
	p_structure->AppendStructure( p_to_append );

//	p_structure->PrintContents();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Preferences::SetPreference( uint32 field_id, Script::CArray* p_to_append )
{
	Script::CArray* p_array = NULL;

	// TODO:  Decide if we can add preferences at runtime,
	// or whether all the categories already exist at load-time
	if ( !m_root.GetArray( field_id, &p_array ) )
	{
		// structure doesn't already exist, so it's not a valid preference
		Dbg_MsgAssert( 0,( "Trying to add an invalid preference" ));
		return false;
	}
	
	m_root.AddArray( field_id, p_to_append );

	//PrintContents();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Preferences::Load( uint32 structure_checksum )
{
	

	// Don't clear out the options structure. New stuff should just override old options
	//Reset();
	
	Script::CStruct* p_structure = Script::GetStructure( structure_checksum, Script::ASSERT );

	m_root.AppendStructure( p_structure );
	
#if 0
	uint8 testbuffer[2048];
	m_root.WriteToBuffer(testbuffer, 2048);
	Script::CStruct* p_new_structure = new Script::CStruct;
	p_new_structure->Clear();
	p_new_structure->ReadFromBuffer(testbuffer);
	p_new_structure->PrintContents();
	delete p_new_structure;
#endif
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Preferences::Reset()
{
	m_root.Clear();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Preferences::UpdateUIElement( uint32 control_id, uint32 field_id, bool mask_password )
{
	
	
	Script::CStruct* pStructure = NULL;
	m_root.GetStructure( field_id, &pStructure, true );

	char buf[256];
	const char* pText = NULL;
	int value = 0;
	if ( pStructure->GetText( "ui_string", &pText ) )
	{
		Dbg_Assert( strlen( pText ) < 256 );
		strcpy( buf, pText );
	}
	else if ( pStructure->GetInteger( "value", &value ) )
	{
		sprintf( buf, "%d", value );
	}
	else if ( pStructure->GetText( NONAME, &pText, true ) )
	{
		Dbg_Assert( strlen( pText ) < 256 );
		strcpy( buf, pText );
	}
	else
	{
		Dbg_MsgAssert( "Couldn't find valid parameters in %s",( Script::FindChecksumName( control_id ) ));
	}

	if ( mask_password )
	{
		// replace all the letters with stars
		for ( uint32 i = 0; i < strlen(buf); i++ )
		{
			buf[i] = '*';
		}
	}
	
	//Front::SendString( control_id, buf );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Preferences::PrintContents()
{
#ifdef __NOPT_ASSERT__
	Script::PrintContents(&m_root);
#endif
}

} // namespace Prefs






================================================
FILE: Code/Gel/Prefs/Prefs.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate3													**
**																			**
**	Module:			Gel						 								**
**																			**
**	File name:		Prefs.h													**
**																			**
**	Created:		03/12/2001	-	gj										**
**																			**
**	Description:	Generic Preferences Class								**
**																			**
*****************************************************************************/

#ifndef __GEL_PREFS_H
#define __GEL_PREFS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

//#include 

#ifndef	__SCRIPTING_STRUCT_H
#include 
#endif


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Prefs
{

						


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  Preferences  : public Spt::Class
{
	

public:
	Preferences();
	virtual						~Preferences();

public:
	uint32						WriteToBuffer(uint8 *pBuffer, uint32 BufferSize);
	void						ReadFromBuffer(uint8 *pBuffer);

public:
	bool						Reset();
	bool						Load( uint32 structure_checksum );
	void						PrintContents();
	bool						UpdateUIElement( uint32 control_id, uint32 field_id, bool mask_password = false );

public:
	Script::CScriptStructure*	GetPreference( uint32 field_id );
	bool						SetPreference( uint32 field_id, Script::CScriptStructure* p_to_append );
	bool						SetPreference( uint32 field_id, Script::CArray* p_to_append );
	Script::CArray*				GetPreferenceArray( uint32 field_id );
	const char*					GetPreferenceString( uint32 field_id, uint32 sub_field_id );
	int							GetPreferenceValue( uint32 field_id, uint32 sub_field_id );
	uint32						GetPreferenceChecksum( uint32 field_id, uint32 sub_field_id );
	void						RemoveComponent( uint32 field_id, uint32 sub_field_id );

	Script::CScriptStructure*	GetRoot() {return &m_root;}
	void						SetRoot(Script::CScriptStructure* pStuff);
	
private:
	Script::CScriptStructure	m_root;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Prefs

#endif	// __GEL_PREFS_H




================================================
FILE: Code/Gel/RefCounted.h
================================================
#ifndef __GEL_REFCOUNTED_H
#define __GEL_REFCOUNTED_H

namespace Obj
{

template class CSmtPtr;




class CRefCounted : public Spt::Class
{
public:
							CRefCounted();
	virtual					~CRefCounted();

	void					AddSmartPointer(CSmtPtr *pSmtPtr);
	void					RemoveSmartPointer(CSmtPtr *pSmtPtr);

	void 					debug_validate_smart_pointers(CSmtPtr *pPtrToCheckForInclusion);

protected:

	CSmtPtr *	mp_smart_ptr_list;
};




}
#endif


================================================
FILE: Code/Gel/Scripting/array.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// array.cpp		KSH 22 Oct 2001
//
// CArray class member functions.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 

DefinePoolableClass(Script::CArray);

namespace Script
{

CArray::CArray()
{
	// Initialise everything. CArray is not derived from CClass so we don't get
	// the auro-zeroing.
	m_type=ESYMBOLTYPE_NONE;
	mp_array_data=NULL;
	m_size=0;
}

CArray::~CArray()
{
	Clear();
}

bool CArray::operator==( const CArray& v ) const
{
	// TODO ...
	#ifdef __NOPT_ASSERT__
	printf("CArray comparisons are not supported yet ... implement when needed\n");
	#endif
	return false;
}

bool CArray::operator!=( const CArray& v ) const
{
	return !(*this==v);
}

// Deletes the array buffer if it exists, asserting if it contains any non-NULL pointers.
// Sets type to NONE and size to 0.
void CArray::Clear()
{
	if (m_size==1)
	{
		// Memory optimization:
		// Special case for size 1. In this case, no memory block has been allocated.
		
		if (m_union)
		{
			// The element is not zero ...
			
			#ifdef __NOPT_ASSERT__
			// Check that no references to things remain in the array.
			switch (m_type)
			{
				case ESYMBOLTYPE_INTEGER:
				case ESYMBOLTYPE_FLOAT:
				case ESYMBOLTYPE_NAME:
					// No need for the user to have zeroed these.
					break;
					
				case ESYMBOLTYPE_STRING:
				case ESYMBOLTYPE_LOCALSTRING:
				case ESYMBOLTYPE_PAIR:
				case ESYMBOLTYPE_VECTOR:
				case ESYMBOLTYPE_STRUCTURE:
				case ESYMBOLTYPE_ARRAY:
				{
					// The array contains a pointer to something.
					// The CArray cannot delete it itself because this would cause cyclic dependencies.
					Dbg_MsgAssert(0,("Tried to delete a CArray that still contains non-NULL data: size=%d type='%s'",m_size,GetTypeName(m_type)));
					break;
				}	
				
				default:
					Dbg_MsgAssert(0,("Bad CArray::m_type of '%s'",GetTypeName(m_type)));
					break;
			}		
			#endif
		
			m_union=0;
		}	
	}
	else
	{
		if (mp_array_data)
		{
			#ifdef __NOPT_ASSERT__
			// Check that no references to things remain in the array.
			switch (m_type)
			{
				case ESYMBOLTYPE_INTEGER:
				case ESYMBOLTYPE_FLOAT:
				case ESYMBOLTYPE_NAME:
					// No need for the user to have zeroed these.
					break;
					
				case ESYMBOLTYPE_STRING:
				case ESYMBOLTYPE_LOCALSTRING:
				case ESYMBOLTYPE_PAIR:
				case ESYMBOLTYPE_VECTOR:
				case ESYMBOLTYPE_STRUCTURE:
				case ESYMBOLTYPE_ARRAY:
				{
					// The array is of pointers, so make sure that the user of CArray has deleted and zeroed these before deleting the array.
					// The CArray cannot delete them itself because this would cause cyclic dependencies.
					for (uint32 i=0; im_size,("Tried to resize CArray to a smaller size, not supported yet ..."));
	// TODO: Make it able to make the CArray smaller, if a need arises. To do, factor out some of the
	// code from CleanUpArray so that the leftover bit can be cleaned up.
	Dbg_MsgAssert(newSize>1,("Resizing arrays to size 1 not supported yet ..."));
	// TODO: Support the above if need be. Need to not allocate a new buffer in that case.
	
	// Allocate the new buffer.
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	uint32 *p_new_buffer=(uint32*)Mem::Malloc(newSize*sizeof(uint32));
	Mem::Manager::sHandle().PopContext();
	
	// Copy the contents of the old buffer into the new.
	uint32 *p_source=GetArrayPointer();
	// Note: Does not support resizing zero size arrays because it does not know what the type of the 
	// new array should be.
	Dbg_MsgAssert(p_source,("NULL array pointer ?"));
	uint32 *p_dest=p_new_buffer;
	uint32 i;
	for (i=0; i 1)
	{
		Mem::Free(mp_array_data);
	}	
	mp_array_data=p_new_buffer;
	
	m_size=newSize;
	Dbg_MsgAssert(m_size>1,("Expected array size to be > 1 ??")); // Just to be sure
}

uint32 *CArray::GetArrayPointer() const
{
	if (m_size==1)
	{
		return (uint32*)&m_union;
	}
	return mp_array_data;
}

void CArray::SetString(uint32 index, char *p_string)
{
	Dbg_MsgAssert(m_type==ESYMBOLTYPE_STRING,("Called CArray::SetString when m_type was '%s'",GetTypeName(m_type)));
	Dbg_MsgAssert(index
#endif

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

#ifndef	__SCRIPTING_SYMBOLTYPE_H
#include  // For ESymbolType
#endif

namespace Script
{

class CVector;
class CPair;
class CStruct;

#ifdef __PLAT_WN32__
class CArray
#else
class CArray : public Mem::CPoolable
#endif
{
	// Pointer to the array data.
	union
	{
		// Generic pointer.
		// Used when calling Mem::Free.
		uint32 *mp_array_data;
		
		int *mp_integers;
		float *mp_floats;
		uint32 *mp_checksums;
		
		char **mpp_strings;
		char **mpp_local_strings;
		CPair **mpp_pairs;
		CVector **mpp_vectors;
		CStruct **mpp_structures;
		CArray **mpp_arrays;
		
		// In the case of the array containing only 1 element, the element itself is
		// stored here, rather than allocating a block of memory for it.
		// This is a memory optimization.
		// Each memory block uses 16 bytes for the header, and the data is padded to
		// occupy 16 bytes. So in the case of an array of 1 element this saves 32 bytes.
		// There are lots of arrays of 1 element, eg the links arrays in each node of 
		// the NodeArray often only contain 1 link.
		int m_integer;
		float m_float;
		uint32 m_checksum;
		char *mp_string;
		char *mp_local_string;
		CPair *mp_pair;
		CVector *mp_vector;
		CStruct *mp_structure;
		CArray *mp_array;
		// Used to zero the single element.
		uint32 m_union;
	};

	// The type of the things in the array.
	ESymbolType m_type;
	
	// The number of items in the array.
	uint32 m_size;

public:
	CArray();
	~CArray();

	// These cannot be defined because it would cause a cyclic dependency, because
	// a CArray member function can't create things. So declare them but leave them undefined
	// so that it will not link if they are attempted to be used.
	CArray( const CArray& rhs );
	CArray& operator=( const CArray& rhs );
	
	// This is used when interpreting switch statements.
	bool operator==( const CArray& v ) const;
	bool operator!=( const CArray& v ) const;
	
	void Clear();
	void SetSizeAndType(int size, ESymbolType type);
	void Resize(uint32 newSize);
	
	// TODO: Remove later. Only included for back compatibility.
	void SetArrayType(int size, ESymbolType type) {SetSizeAndType(size,type);}
	
	void 	  SetString(uint32 index, char *p_string);
	void SetLocalString(uint32 index, char *p_string);
	void 	 SetInteger(uint32 index, int int_val);
	void 	   SetFloat(uint32 index, float float_val);
	void 	SetChecksum(uint32 index, uint32 checksum);
	void 	  SetVector(uint32 index, CVector *p_vector);
	void 		SetPair(uint32 index, CPair *p_pair);
	void   SetStructure(uint32 index, CStruct *p_struct);
	void 	   SetArray(uint32 index, CArray *p_array);

	char 			*GetString(uint32 index) const;
	char 	   *GetLocalString(uint32 index) const;
	int 			GetInteger(uint32 index) const;
	float 			  GetFloat(uint32 index) const;
	uint32 		   GetChecksum(uint32 index) const;
	CVector			*GetVector(uint32 index) const;
	CPair 			  *GetPair(uint32 index) const;
	CStruct 	 *GetStructure(uint32 index) const;
	CArray			 *GetArray(uint32 index) const;

	////////////////////////////////////////////////////////////////////////////////////
	// TODO: Remove these later, only needed for back compatibility.
	uint32	   GetNameChecksum(uint32 index) const {return GetChecksum(index);}
	int 				GetInt(uint32 index) const {return GetInteger(index);}
	////////////////////////////////////////////////////////////////////////////////////
	
	uint32 		GetSize() const {return m_size;};
	ESymbolType GetType() const {return m_type;};
	
	// Needed by CleanUpArray and CopyArray in struct.cpp so that they can
	// quickly scan through the array data without having to use the access functions
	// to get each element.
	uint32 *GetArrayPointer() const;
};

} // namespace Script

#endif // #ifndef	__SCRIPTING_ARRAY_H


================================================
FILE: Code/Gel/Scripting/checksum.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// checksum.cpp		KSH 22 Oct 2001
//
// Checksum name lookup stuff for use by asserts.
// Uses up a heck of a lot of memory, but only when __NOPT_ASSERT__ is defined.
//
///////////////////////////////////////////////////////////////////////////////////////

#ifdef __PLAT_WN32__
#ifdef __QDEBUG__
#include <../tools/src/monitor/stdafx.h> // Needed for the message box
#endif
#endif

#include 
#include 	// For stricmp
#include 
#include 
#include 

// These are only referenced in the Playstation version.
// They are set in NGPS.lk
#ifdef __PLAT_NGPS__
extern char _script_debugger_start[];
extern char _script_start[];
extern char _script_end[];
#endif

namespace Script
{

// The number of bits is set to be high to speed up AddChecksumName, so that
// LoadQB does not take ages to execute.
#define CHECKSUM_LOOKUP_HASH_BITS 16
// This probably does not need to be so big, but we've got lots of memory in debug so it's OK.
#define CHECKSUM_ENTRIES_PER_SLOT 10

// There are (1<=hash_table_size+CHECKSUM_NAME_SPACE,(
				  "Chunk of debug memory reserved for script stuff is not big enough.\nRequired=%d, got=%d",
				  hash_table_size+CHECKSUM_NAME_SPACE, script_debug_mem_size));
	#endif				  
				  
	sp_checksum_name_hash_table=(SChecksumName*)_script_start;
	sp_checksum_names=(char*)(sp_checksum_name_hash_table+((1<mppStart=&sp_checksum_names;
	p_checksum_name_info->mppEnd=&sp_end_of_checksum_names;
	
	#else

	// Just allocate the arrays off the heap.
	
	// Checksum-name lookup stuff.
	sp_checksum_name_hash_table=(SChecksumName*)Mem::Malloc((1<mpName)
	{
		// Already a name here.
		// See if it has the same checksum.
		if (p_first->mChecksum==checksum)
		{
			// It does! Check whether it is the same name.
			if (stricmp(p_first->mpName,p_name)==0)
			{
				// Phew, the name matches. No need to do anything.
				return;
			}
			else
			{
				// Oh bugger, a checksum clash.
				#ifdef __PLAT_WN32__
				#ifdef __QDEBUG__
				char p_foo[1024];
				sprintf(p_foo,"Checksum clash between %s and %s, both have checksum 0x%08x",p_name,p_first->mpName,checksum);
				MessageBox(NULL,p_foo,"Warning",MB_OK);
				#endif
				// Carry on anyway, won't cause a crash.
				#else				
				Dbg_MsgAssert(0,("Checksum clash between %s and %s, both have checksum 0x%08x",p_name,p_first->mpName,checksum));
				#endif
				return;
			}
		}
		
		// Not the same checksum. Onto the next entry.
		++c;
		Dbg_MsgAssert(cmChecksum=checksum;
	p_first->mpName=sp_end_of_checksum_names;
	
	int space_left=CHECKSUM_NAME_SPACE-(sp_end_of_checksum_names-sp_checksum_names);
	while (*p_name)
	{
		Dbg_MsgAssert(space_left>0,("Need to increase CHECKSUM_NAME_SPACE in checksum.cpp"));
		*sp_end_of_checksum_names++=*p_name++;
		--space_left;
	}	
	Dbg_MsgAssert(space_left>0,("Need to increase CHECKSUM_NAME_SPACE in checksum.cpp"));
	*sp_end_of_checksum_names++=0;
}

static char sp_unknown_checksum_buf[100];

// Returns NULL if not found.
// This version is not safe to use in a printf because it may return NULL, but can be handy for
// when the calling code needs to do something special if the name is not found.
const char *FindChecksumNameNULL(uint32 checksum)
{
	if (!Config::GotExtraMemory())
	{
		return NULL;
	}
	
	Dbg_MsgAssert(sp_checksum_name_hash_table,("NULL sp_checksum_name_hash_table"));
	SChecksumName *p_first=&sp_checksum_name_hash_table[(checksum&((1<mpName)
	{
		if (p_first->mChecksum==checksum)
		{
			return p_first->mpName;
		}	
		--c;
		if (c==0)
		{
			// Run out of slots.
			break;
		}	
		++p_first;
	}
	
	return NULL;		
}

// Returns "Unknown" if not found.
// This version is always safe to use in a printf.
const char *FindChecksumName(uint32 checksum)
{
	const char *p_name=FindChecksumNameNULL(checksum);
	if (p_name)	
	{
		return p_name;
	}
	else
	{
		#ifdef __PLAT_WN32__
		// The monitor.exe application requires that the unknown checksum be
		// displayed like this.
		sprintf(sp_unknown_checksum_buf,"0x%08x",checksum);	
		#else
		sprintf(sp_unknown_checksum_buf,"Unknown(0x%08x)",checksum);	
		#endif
		return sp_unknown_checksum_buf;		
	}	
}


} // namespace Script



================================================
FILE: Code/Gel/Scripting/checksum.h
================================================
#ifndef	__SCRIPTING_CHECKSUM_H
#define	__SCRIPTING_CHECKSUM_H

#ifndef __CORE_DEFINES_H
#include 
#endif

namespace Script
{

void AllocateChecksumNameLookupTables();
void DeallocateChecksumNameLookupTables();
void GetChecksumNamesBuffer(char **pp_start, char **pp_end);
void AddChecksumName(uint32 checksum, const char *pName);
const char *FindChecksumNameNULL(uint32 checksum);
const char *FindChecksumName(uint32 checksum);

} // namespace Script

#endif // #ifndef	__SCRIPTING_CHECKSUM_H



================================================
FILE: Code/Gel/Scripting/component.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// component.cpp		KSH 17 Oct 2001
//
// Notes:
// When a CComponent is deleted, it does not delete any CStruct, CArray or string that
// it may contain a pointer to. This is so that a cyclic dependency between component and struct is avoided.
// It is up to the parent CStruct to clean these up before deleting the CComponent.
// 
// This cpp just declares the constructor, and the pool statics.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 

#ifndef	__SCRIPTING_SCRIPTDEFS_H
#include 
#endif

DefinePoolableClass(Script::CComponent);

namespace Script
{

// Used in parkgen.cpp, but only in a debug message, so make it just return 0 for now.
uint32 GetNumUsedComponents()
{
	return 0;
}

CComponent::CComponent()
{
	// Initialise everything. CComponent is not derived from CClass so we don't get
	// the auro-zeroing.
	mType=ESYMBOLTYPE_NONE;
	mNameChecksum=NO_NAME;
	mScriptSize=0;
	mUnion=0;
	mpNext=NULL;
}

// The destructor does not delete anything to avoid cyclic dependencies, so just assert
// if it look like there could be a non-NULL pointer left.
#ifdef __NOPT_ASSERT__
CComponent::~CComponent()
{
	Dbg_MsgAssert(mUnion==0,("CComponent still contains data, possibly an undeleted pointer"));
	Dbg_MsgAssert(mScriptSize==0,("CComponent::mScriptSize not zero in destructor ?"));
}
#endif

// Note: No destructor.
// When a CComponent is deleted, it does not delete any CStruct, CArray or string that
// it may contain a pointer to.
// This is so that a cyclic dependency between component and struct is avoided, because to
// delete a CStruct from here would require the inclusion of struct.h.

// It is up to the parent CStruct to delete anything in the component before deleting the CComponent.

} // namespace Script



================================================
FILE: Code/Gel/Scripting/component.h
================================================
#ifndef	__SCRIPTING_COMPONENT_H
#define	__SCRIPTING_COMPONENT_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

#ifndef	__SCRIPTING_SYMBOLTYPE_H
#include  // For ESymbolType
#endif

namespace Script
{

class CPair;
class CVector;
class CStruct;
class CArray;

// Note: This is not derived from CClass to avoid the extra memory overhead due to the virtual destructor.
// There will be loads of CComponents, in THPS3 there were 58000.
#ifdef __PLAT_WN32__
class CComponent
#else
class CComponent : public Mem::CPoolable
#endif
{
public:	
	CComponent();
	#ifdef __NOPT_ASSERT__
	~CComponent();
	#endif

	// These cannot be defined because it would cause a cyclic dependency, because
	// a CComponent member function can't create things. So declare them but leave them undefined
	// so that it will not link if they are attempted to be used.
	CComponent( const CComponent& rhs );
	CComponent& operator=( const CComponent& rhs );

	// These are used when interpreting switch statements and expressions
	bool operator==( const CComponent& v ) const;
	bool operator!=( const CComponent& v ) const;
	bool operator<( const CComponent& v ) const;
	bool operator>( const CComponent& v ) const;
	bool operator<=( const CComponent& v ) const;
	bool operator>=( const CComponent& v ) const;

	// Making this use just one byte keeps the size of CComponent to 16 bytes.
	// There are 64000 CComponents so they need to be as small as possible.
	// It seems that the compact pool class has an overhead of one byte, so if the
	// size of this class is 3 mod 4 it will be making optimal use of space.
	// Hence we have 2 bytes spare if needed later.
	uint8 mType;
	
	// Well, this has used up those 2 spare bytes mentioned above.
	// This is the size of the mpScript buffer in the case of mType being ESYMBOLTYPE_QSCRIPT
	// The size is needed when copying a component, so that the new component knows how big a
	// buffer to allocate. In theory the size could be calculated, but that would introduce
	// a circular dependency because struct.cpp would have to include parse.h in order to call
	// SkipOverScript.
	uint16 mScriptSize;
	
	uint32 mNameChecksum;	
	union
	{
		int mIntegerValue;
		float mFloatValue;
		char *mpString;
		char *mpLocalString;
		CPair *mpPair;
		CVector *mpVector;
		CStruct *mpStructure;
		CArray *mpArray;
		uint8 *mpScript;
		uint32 mChecksum;
		uint32 mUnion; // For when all the above need to be zeroed 
	};
	
	CComponent *mpNext;
};
	
void AllocateComponentPool(uint32 maxComponents);
void DeallocateComponentPool();
	
uint32 GetNumUsedComponents();
	
} // namespace Script

#endif // #ifndef	__SCRIPTING_COMPONENT_H


================================================
FILE: Code/Gel/Scripting/debugger.cpp
================================================
#include "gel/scripting/debugger.h"
#include "sk/gamenet/scriptdebugger.h"
#include "gel/mainloop.h"
#include "gel/scripting\symboltable.h"
#include "gel/scripting\checksum.h"
#include "gel/scripting\script.h"
#include "gel/scripting\symboltype.h"
#include "gel/scripting\array.h"
#include "gel/scripting\struct.h"
#include "gel/scripting\utils.h"
#include "gel/scripting\component.h"
#include "gel/scripting\scriptcache.h"
#include "gel/object/compositeobjectmanager.h"
#include "gel/components/modelcomponent.h"
#include "gfx/nxviewman.h"
#include "sk/modules/skate/skate.h"
#include "sk/objects/skater.h"
#include "sk/objects/skatercam.h"
#include "gel/event.h"


#ifdef __NOPT_ASSERT__


namespace Dbg
{

uint32 gpDebugInfoBuffer[SCRIPT_DEBUGGER_BUFFER_SIZE/4];

static Script::CScript *s_find_cscript_given_id(uint32 id)
{
	Script::CScript *p_script=Script::GetNextScript(NULL);
	while (p_script)
	{
		if (p_script->GetUniqueId()==id)
		{
			return p_script;
		}	
		p_script=Script::GetNextScript(p_script);
	}	
	return NULL;
}

static Script::CScript *s_find_cscript_given_name(uint32 name)
{
	Script::CScript *p_script=Script::GetNextScript(NULL);
	while (p_script)
	{
		if (p_script->RefersToScript(name))
		{
			return p_script;
		}	
		p_script=Script::GetNextScript(p_script);
	}	
	return NULL;
}

static Obj::CCompositeObject *s_find_nearest_object(int screen_x, int screen_y, float search_radius)
{
	Nx::CViewport *p_viewport = Nx::CViewportManager::sGetActiveViewport();
	Dbg_MsgAssert(p_viewport, ("Can't find an active viewport"));
	
	Lst::Search sh;
	Obj::CObject *p_ob = (Obj::CObject*) sh.FirstItem( Obj::CCompositeObjectManager::Instance()->GetRefObjectList() );
	Obj::CCompositeObject *p_nearest_ob=NULL;
	float min_d=1000000.0f;
	while (p_ob)
	{
		float x,y;
		Nx::ZBufferValue z;
		p_viewport->TransformToScreenCoord(((Obj::CCompositeObject*)p_ob)->GetPos(),x,y,z);
		
		float dx=x-screen_x;
		float dy=y-screen_y;
		float d=sqrtf(dx*dx+dy*dy);
		if (dGetComponent(CRCD(0x286a8d26,"Model"));
		if (p_model)
		{
			Nx::CModel *p_cmodel=p_model->GetModel();
			if (p_cmodel)
			{
				old_render_mode=p_cmodel->GetRenderMode();
				p_cmodel->SetRenderMode(new_render_mode);
			}
		}
	}
	
	return old_render_mode;
}

DefineSingletonClass( CScriptDebugger, "Script debugger module" );

CScriptDebugger::CScriptDebugger()
{
	// Note: Split message id's start at 1 because 0 is used to indicate no message.
	m_split_message_id=1;
	
	m_state=SCRIPT_DEBUGGER_STATE_IDLE;
	mp_checksum_names_current_pos=NULL;
	mp_checksum_names_end=NULL;
	mp_server=NULL;
	mp_logic_task=NULL;
	
	m_num_watched_scripts=0;
	
	m_mouse_on_screen=false;
	m_got_valid_mouse_position=false;
	m_mouse_x=0;
	m_mouse_y=0;
	mp_currently_selected_object=NULL;
	m_original_object_render_mode=Nx::vNONE;
	m_left_button_down=false;	
	m_right_button_down=false;	

	m_state_when_left_pressed.mTime=0;
	m_state_when_left_pressed.mMouseX=0;
	m_state_when_left_pressed.mMouseY=0;
	m_state_when_left_pressed.mCamPhi=0.0f;
	m_state_when_left_pressed.mCamTheta=0.0f;
	
	m_state_when_right_pressed.mCamRadius=0.0f;
	m_state_when_right_pressed.mMouseX=0;
	m_state_when_right_pressed.mMouseY=0;
	
	mp_viewed_object=NULL;
	setup_default_camera_viewing_position();
	
	m_in_object_single_step_mode=false;
	m_single_step_object_id=0;
	
	m_object_update_total_bytes_sent=0;
	m_frames_since_last_object_update=0;
}

CScriptDebugger::~CScriptDebugger()
{
	if (mp_server)
	{
		delete mp_server;
	}	
	if (mp_logic_task)
	{
		delete mp_logic_task;
	}	
}

void CScriptDebugger::v_start_cb ( void )
{
// Stick it all on the network heap, as it's kind of network releated
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());


	Net::Manager * p_net_man = Net::Manager::Instance();
	mp_server = p_net_man->CreateNewAppServer( 0, "Script debugger", 4, GameNet::vSCRIPT_DEBUG_PORT,
												inet_addr( p_net_man->GetLocalIP()), Net::App::mACCEPT_FOREIGN_CONN | Net::App::mDYNAMIC_RESEND );
	
	// Note: If the default task priority is used then the 'view object' functionality does not work.
	// s_logic_code needs to be called after all the normal camera code has executed, so that the camera
	// position can be overridden. Hence a priority lower than the default needs to be used. 
	mp_logic_task = new Tsk::Task< CScriptDebugger > ( CScriptDebugger::s_logic_code, *this, Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_SCRIPT_DEBUGGER );

	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	mlp_manager->AddLogicTask( *mp_logic_task );
	
	mp_server->m_Dispatcher.AddHandler(	Net::MSG_ID_CONNECTION_REQ, 
										s_handle_connection_request, 
										Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
										
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_WATCH_THIS, 
										s_handle_watch_this, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_WATCH_SCRIPT, 
										s_handle_watch_script, 
										Net::mHANDLE_LATE, this);
										
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_STOP, 
										s_handle_stop, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_STEP_INTO, 
										s_handle_step_into, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_STEP_OVER, 
										s_handle_step_over, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_GO, 
										s_handle_go, 
										Net::mHANDLE_LATE, this);

	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_CLEAR_SCRIPT_WATCHES, 
										s_handle_clear_script_watches, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_REFRESH, 
										s_handle_refresh, 
										Net::mHANDLE_LATE, this);
										
	mp_server->m_Dispatcher.AddHandler(	Net::MSG_ID_DISCONN_REQ, 
										s_handle_disconnect, 
										Net::mHANDLE_LATE, this);

	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_SEND_CSCRIPT_LIST, 
										s_handle_send_cscript_list, 
										Net::mHANDLE_LATE, this);

	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_SEND_WATCH_LIST, 
										s_handle_send_watch_list, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_STOP_WATCHING_THIS_INDEX, 
										s_handle_stop_watching_this_index, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_STOP_WATCHING_THIS_CSCRIPT, 
										s_handle_stop_watching_this_cscript, 
										Net::mHANDLE_LATE, this);

	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_SEND_COMPOSITE_OBJECT_INFO, 
										s_handle_send_composite_object_info, 
										Net::mHANDLE_LATE, this);

	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_MOUSE_POSITION, 
										s_handle_mouse_position, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_MOUSE_ON_SCREEN, 
										s_handle_mouse_on_screen, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_MOUSE_OFF_SCREEN, 
										s_handle_mouse_off_screen, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_MOUSE_LEFT_BUTTON_DOWN, 
										s_handle_mouse_left_button_down, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_MOUSE_LEFT_BUTTON_UP, 
										s_handle_mouse_left_button_up, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_MOUSE_RIGHT_BUTTON_DOWN, 
										s_handle_mouse_right_button_down, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_MOUSE_RIGHT_BUTTON_UP, 
										s_handle_mouse_right_button_up, 
										Net::mHANDLE_LATE, this);

										
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_SEND_COMPOSITE_OBJECT_LIST, 
										s_handle_send_composite_object_list, 
										Net::mHANDLE_LATE, this);
										
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_SET_OBJECT_UPDATE_MODE, 
										s_handle_set_object_update_mode, 
										Net::mHANDLE_LATE, this);
	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_VIEW_OBJECT, 
										s_handle_view_object, 
										Net::mHANDLE_LATE, this);

	mp_server->m_Dispatcher.AddHandler(	GameNet::vMSG_ID_DBG_SEND_SCRIPT_GLOBAL_INFO, 
										s_handle_send_script_global_info, 
										Net::mHANDLE_LATE, this);
										
	m_paused = false;
	
	Net::Manager* net_man = Net::Manager::Instance();
	net_man->SetBandwidth( 200000 );

	Mem::Manager::sHandle().PopContext();
	
}

void CScriptDebugger::v_stop_cb ( void )
{
	mp_logic_task->Remove();
	mp_server->m_Dispatcher.Deinit();
}

void CScriptDebugger::transmit_cscript_list()
{
	Script::CStruct *p_list=new Script::CStruct;

	//int total_num_return_addresses=0;
	//int total_scripts_using_big_buffer=0;
	int num_scripts=0;
	Script::CScript *p_script=Script::GetNextScript(NULL);
	while (p_script)
	{
		//total_num_return_addresses+=p_script->GetNumReturnAddresses();
		//if (p_script->UsingBigLoopBuffer())
		//{
		//	++total_scripts_using_big_buffer;
		//}
			
		p_script=Script::GetNextScript(p_script);
		++num_scripts;
	}	
	p_list->AddInteger(CRCD(0x62f3d8fa,"NumScripts"),num_scripts);
	//p_list->AddInteger(CRCD(0xed4b5c6e,"NumScriptsUsingBigBuffer"),total_scripts_using_big_buffer);
	//p_list->AddFloat(CRCD(0x4f73b852,"AverageNumReturnAddresses"),((float)total_num_return_addresses)/num_scripts);


	// Add the script cache info.	
	Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
	Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));
	p_script_cache->GetDebugInfo(p_list);

	p_script=Script::GetNextScript(NULL);
	while (p_script)
	{
		Script::CStruct *p_script_info=new Script::CStruct;
		
		p_script_info->AddChecksum("m_unique_id",p_script->GetUniqueId());
		
		//p_script_info->AddInteger(CRCD(0x46b0d38c,"NumReturnAddresses"),p_script->GetNumReturnAddresses());
		//p_script_info->AddInteger(CRCD(0x477f712b,"UsingBigCallstackBuffer"),p_script->UsingBigCallstackBuffer());
		
		Obj::CObject *p_object=p_script->mpObject;
		if (p_object)
		{
			p_script_info->AddChecksum("Object",p_object->GetID());
		}	
		
		if (p_script->mIsSpawned)
		{
			p_script_info->AddChecksum(NONAME,CRCD(0xf697fda7,"Spawned"));
		}
		
		#ifdef	__SCRIPT_EVENT_TABLE__		
			if (p_script->GetEventHandlerTable())
			{
				p_script->GetEventHandlerTable()->GetDebugInfo(p_script_info);
			}
		#endif

		
		// Convert to microseconds by dividing by 150
		p_script_info->AddInteger("CPUTime",p_script->m_last_time/150);

		// Create a new structure component for p_script_info, and name it using the name of the script.
		// Then add this component to the p_list structure using AppendComponentPointer.
		// The reason it is done this way rather than simply using AddStructurePointer is that
		// AddStructurePointer will remove any previously added components of the same name.
		// There may be many instances of the same script running, so we want those to all appear.
		// AppendComponentPointer will add the new component to the structure and will leave any
		// existing components untouched. (it was written specially for this function)
		Script::CComponent *p_comp=new Script::CComponent;
		p_comp->mType=ESYMBOLTYPE_STRUCTURE;
		p_comp->mpStructure=p_script_info;
		
		uint32 base_script=p_script->GetBaseScript();
		if (base_script)
		{
			p_comp->mNameChecksum=base_script;
		}
		else	
		{
			// Occasionally the base_script might be zero (sometimes when scrolling
			// through certain menus)
			// Name it using the unique id of the script.
			p_comp->mNameChecksum=p_script->GetUniqueId();
		}

		p_list->AppendComponentPointer(p_comp);
				
		p_script=Script::GetNextScript(p_script);
	}	

	int structure_bytes_written=Script::WriteToBuffer(p_list,(uint8*)gpDebugInfoBuffer,SCRIPT_DEBUGGER_BUFFER_SIZE);
	delete p_list;
	
	Net::MsgDesc msg;
	msg.m_Data = gpDebugInfoBuffer;
	msg.m_Length = structure_bytes_written;
	msg.m_Id = GameNet::vMSG_ID_DBG_CSCRIPT_LIST;
	StreamMessage(&msg);
}

void CScriptDebugger::transmit_watch_list()
{
	SWatchInfo *p_watch_info=(SWatchInfo*)gpDebugInfoBuffer;
	Dbg_MsgAssert(m_num_watched_scripts*sizeof(SWatchInfo)<=SCRIPT_DEBUGGER_BUFFER_SIZE,("gpDebugInfoBuffer overflow"));

	for (int i=0; imScriptName=mp_watched_scripts[i].mChecksum;
		p_watch_info->mStopImmediately=mp_watched_scripts[i].mStopScriptImmediately;
		++p_watch_info;
	}		

	Net::MsgDesc msg;
	msg.m_Data = gpDebugInfoBuffer;
	msg.m_Length = m_num_watched_scripts*sizeof(SWatchInfo);
	msg.m_Id = GameNet::vMSG_ID_DBG_WATCH_LIST;
	StreamMessage(&msg);
}

void CScriptDebugger::transmit_composite_object_list()
{
	Script::CStruct *p_list=new Script::CStruct;
	
	Lst::Search sh;
	Obj::CObject *p_ob = (Obj::CObject*) sh.FirstItem( Obj::CCompositeObjectManager::Instance()->GetRefObjectList() );
	while (p_ob)
	{
		Script::CStruct *p_object_info=new Script::CStruct;
		p_object_info->AddInteger("CPUTime",((Obj::CCompositeObject*)p_ob)->GetUpdateTime());
		if (p_ob->GetID())
		{
			p_list->AddStructurePointer(p_ob->GetID(),p_object_info);
		}
		else
		{
			p_list->AddStructurePointer("Skater",p_object_info);
		}	
		
		p_ob = sh.NextItem();
	}
	
	int structure_bytes_written=Script::WriteToBuffer(p_list,(uint8*)gpDebugInfoBuffer,SCRIPT_DEBUGGER_BUFFER_SIZE);
	delete p_list;
	
	Net::MsgDesc msg;
	msg.m_Data = gpDebugInfoBuffer;
	msg.m_Length = structure_bytes_written;
	msg.m_Id = GameNet::vMSG_ID_DBG_COMPOSITE_OBJECT_LIST;
	Dbg::CScriptDebugger::Instance()->StreamMessage(&msg);
}

void CScriptDebugger::transmit_compression_lookup_table()
{   
	Script::CArray *p_table=Script::GetArray(0x35115a20/*WriteToBuffer_CompressionLookupTable_8*/);
	Dbg_MsgAssert(p_table->GetType()==ESYMBOLTYPE_NAME,("Bad array type"));
	Dbg_MsgAssert(p_table->GetSize()<=256,("Bad array size"));
	
    Net::MsgDesc msg;
	msg.m_Data = p_table->GetArrayPointer();
	msg.m_Length = p_table->GetSize()*4;
	msg.m_Id = GameNet::vMSG_ID_DBG_COMPRESSION_LOOKUP_TABLE;
	StreamMessage(&msg);

	p_table=Script::GetArray(0x25231f42/*WriteToBuffer_CompressionLookupTable_16*/);
	Dbg_MsgAssert(p_table->GetSize()==0,("Need to update transmit_compression_lookup_table to handle WriteToBuffer_CompressionLookupTable_16, since it is no longer empty ..."));
}	

SWatchedScript *CScriptDebugger::find_watched_script(uint32 checksum)
{
	for (int i=0; iWatchInDebugger(stopScriptImmediately);
	}
		
	SWatchedScript *p_watched_script=find_watched_script(checksum);
	if (p_watched_script)
	{
		p_watched_script->mStopScriptImmediately=stopScriptImmediately;
		return;
	}

	Dbg_MsgAssert(m_num_watched_scriptsBeingWatchedInDebugger())
		{
			// DEFINITELY_TRANSMIT means override the optimization whereby a packet is not
			// sent if it's checksum matches that of the last sent.
			p_script->TransmitInfoToDebugger(Script::DEFINITELY_TRANSMIT);
		}	
		p_script=Script::GetNextScript(p_script);
	}	
}

void CScriptDebugger::stop_watching_script(int index)
{
	if (index<0 || index >= m_num_watched_scripts)
	{
		return;
	}	

	if (m_num_watched_scripts)
	{
		for (int i=index; iGetDebugInfo(p_info);
		
		Dbg_MsgAssert(SCRIPT_DEBUGGER_BUFFER_SIZE>4,("Oops"));
		gpDebugInfoBuffer[0]=p_obj->GetID();
		uint8 *p_buf=(uint8*)(gpDebugInfoBuffer+1);
		int structure_bytes_written=Script::WriteToBuffer(p_info,p_buf,SCRIPT_DEBUGGER_BUFFER_SIZE-4);
		delete p_info;
		
		Net::MsgDesc msg;
		msg.m_Data = gpDebugInfoBuffer;
		msg.m_Length = 4+structure_bytes_written;
		msg.m_Id = GameNet::vMSG_ID_DBG_COMPOSITE_OBJECT_INFO;

		StreamMessage(&msg);
		num_bytes_sent=msg.m_Length;
		
		printf("Sent composite object info for '%s'\n",Script::FindChecksumName(p_obj->GetID()));
	}	
	return num_bytes_sent;
}

void CScriptDebugger::send_composite_object_info(uint32 id)
{
	send_composite_object_info((Obj::CCompositeObject*)Obj::CCompositeObjectManager::Instance()->GetObjectByID( id ));
}

void CScriptDebugger::set_object_update_mode(SObjectUpdateMode *pObjectUpdateMode)
{
	// Make sure the game is not paused, which it will be if in single step mode.
	Dbg::CScriptDebugger::Instance()->ScriptDebuggerUnpause();
	
	switch (pObjectUpdateMode->mMode)
	{
		case Dbg::EVERY_FRAME:
		{
			Obj::CCompositeObject* p_obj=(Obj::CCompositeObject*)Obj::CCompositeObjectManager::Instance()->GetObjectByID(pObjectUpdateMode->mObjectId);
			if (p_obj)
			{
				p_obj->SetDebuggerEveryFrameFlag(pObjectUpdateMode->mOn);
			}	
			break;
		}	
		case Dbg::SINGLE_STEP:
			if (pObjectUpdateMode->mOn)
			{
				// Set the single step mode flag to true, which means that the next time in to
				// s_logic_code it will send a packet of info for this object, then pause the game.
				// When paused, it will still respond to incoming messages, so the next time
				// the user clicks the 'single step' button it will call this function, hence
				// unpausing the game for one frame until it gets in to s_logic_code again, where
				// it will send another packet of info, then pause again etc.
				m_in_object_single_step_mode=true;
				m_single_step_object_id=pObjectUpdateMode->mObjectId;
			}	
			else
			{
				m_in_object_single_step_mode=false;
				m_single_step_object_id=0;
			}
			break;
		default:	
			break;
	}	
}

void CScriptDebugger::view_object(SViewObject *pViewObject)
{
	Obj::CCompositeObject *p_obj=NULL;
	
	if (pViewObject->mDoViewObject)
	{
		p_obj=(Obj::CCompositeObject*)Obj::CCompositeObjectManager::Instance()->GetObjectByID(pViewObject->mObjectId);
	}
	mp_viewed_object=p_obj;
	
	setup_default_camera_viewing_position();
}

void CScriptDebugger::send_any_single_step_object_info()
{
	if (m_in_object_single_step_mode)
	{
		Obj::CCompositeObject* p_obj=(Obj::CCompositeObject*)Obj::CCompositeObjectManager::Instance()->GetObjectByID(m_single_step_object_id);
		if (p_obj)
		{
			// Send a packet of info, then pause the game until the user clicks the 'single step'
			// button again, which will cause CScriptDebugger::set_object_update_mode to be called.
			send_composite_object_info(p_obj);
			Dbg::CScriptDebugger::Instance()->ScriptDebuggerPause();
		}
	}	
}

void CScriptDebugger::send_script_global_info(uint32 id)
{
	// Look up the symbol to see if it is the name of some global.
	Script::CSymbolTableEntry *p_entry=Script::LookUpSymbol(id);
	if (p_entry)
	{
		int message_size=0;		
		uint8 *p_buf=(uint8*)gpDebugInfoBuffer;

		// Write in the name, type, and source file name.		
		Dbg_MsgAssert(SCRIPT_DEBUGGER_BUFFER_SIZE > 8,("SCRIPT_DEBUGGER_BUFFER_SIZE is like way too small dude"));
		*(uint32*)p_buf=id;
		p_buf+=4;
		*(uint32*)p_buf=p_entry->mType;
		p_buf+=4;
		message_size+=8;
		
		const char *p_source_filename=Script::FindChecksumName(p_entry->mSourceFileNameChecksum);
		int len=strlen(p_source_filename)+1;
		Dbg_MsgAssert(SCRIPT_DEBUGGER_BUFFER_SIZE-message_size > len,("SCRIPT_DEBUGGER_BUFFER_SIZE is too small"));
		
		strcpy((char*)p_buf,p_source_filename);
		p_buf+=len;
		message_size+=len;

		// For pure data types, write in a structure containing the value.
		switch (p_entry->mType)
		{
			case ESYMBOLTYPE_INTEGER:
			case ESYMBOLTYPE_FLOAT:
			case ESYMBOLTYPE_STRING:
			case ESYMBOLTYPE_LOCALSTRING:
			case ESYMBOLTYPE_PAIR:
			case ESYMBOLTYPE_VECTOR:
			case ESYMBOLTYPE_STRUCTURE:
			case ESYMBOLTYPE_ARRAY:
			case ESYMBOLTYPE_NAME:
			{
				// Copy the name and value into a structure for sending to the
				// script debugger for display.
				Script::CStruct *p_struct=new Script::CStruct;
		
				// Create a structure component and fill it in with the value of the global.
				Script::CComponent *p_source=new Script::CComponent;
				p_source->mType=p_entry->mType;
				p_source->mUnion=p_entry->mUnion; // What mUnion represents depends on mType
				
				// Now use CopyComponent to make a copy. This needs to be done because mUnion may
				// be a pointer to some entity, and we don't want that to be deleted when p_struct
				// gets deleted.
				Script::CComponent *p_new=new Script::CComponent;
				Script::CopyComponent(p_new,p_source);
				// Delete p_source now that we've got a copy.
				p_source->mUnion=0;
				delete p_source;
				
				// Name the component using id so that it appears as expected in the structure.
				// Ie, if Foo is a global with value 6, then it will appear as Foo=6 in the structure.
				p_new->mNameChecksum=id;
				p_struct->AddComponent(p_new);
		
				
				int structure_bytes_written=Script::WriteToBuffer(p_struct,p_buf,SCRIPT_DEBUGGER_BUFFER_SIZE-message_size);
				delete p_struct;
				
				p_buf+=structure_bytes_written;
				message_size+=structure_bytes_written;
				break;
			}	
			default:	
				break;
		}			
		
		// Send the info to the debugger.
		Net::MsgDesc msg;
		msg.m_Data = gpDebugInfoBuffer;
		msg.m_Length = message_size;
		msg.m_Id = GameNet::vMSG_ID_DBG_SCRIPT_GLOBAL_INFO;
		StreamMessage(&msg);
	}	
}

void CScriptDebugger::stop_debugging()
{
	m_num_watched_scripts=0;
	
	Script::CScript *p_script=Script::GetNextScript(NULL);
	while (p_script)
	{
		p_script->StopWatchingInDebugger();
		p_script=Script::GetNextScript(p_script);
	}	

	stop_all_every_frame_object_updates();
	
	ScriptDebuggerUnpause();
	
	switch_off_mouse_cursor();
}

int	CScriptDebugger::s_handle_connection_request( Net::MsgHandlerContext* context )
{   
	Dbg_Assert( context );

	Dbg_Printf( "Got Join Request from ip %x port %d\n", context->m_Conn->GetIP(), context->m_Conn->GetPort());

	// We'll just accept local clients
	if( context->m_Conn->IsRemote())
	{
		// Rule out redundancy
		if( context->m_PacketFlags & Net::mHANDLE_FOREIGN )
		{
			Net::Conn* conn;

			// Are we currently accepting players?
			if( context->m_App->AcceptsForeignConnections() == false )
			{
				return Net::HANDLER_MSG_DONE;
			}
			
			// Create a more permanent connection
			conn = context->m_App->NewConnection( context->m_Conn->GetIP(), context->m_Conn->GetPort());
			conn->SetBandwidthType( Net::Conn::vNARROWBAND );//Net::Conn::vLAN );
			conn->SetSendInterval( 0 );
			
			context->m_Conn->Invalidate();
			context->m_Conn = conn;	// the rest of the chain will use this new, valid connection
		}
	}


	// Make sure that the debugger is updated with the latest info.
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->refresh();
	
	return Net::HANDLER_CONTINUE;
}

// This function is kind of redundant at the moment, since there is now a seperate s_handle_watch_script
// and there will probably soon be a s_handle_watch_object.
int	CScriptDebugger::s_handle_watch_this( Net::MsgHandlerContext* context )
{   
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	
	Dbg_MsgAssert(context->m_MsgLength==4,("Unexpected length of %d for vMSG_ID_DBG_WATCH_THIS", context->m_MsgLength));
	
	uint32 checksum=*(uint32*)context->m_Msg;

	// Determine what kind of thing the checksum is referring to ...
	
	Script::CSymbolTableEntry *p_sym=Script::LookUpSymbol(checksum);
	if (p_sym)
	{
		switch (p_sym->mType)
		{
			case ESYMBOLTYPE_QSCRIPT:
			{
				// false means do not stop the script immediately when detected.
				p_this->add_to_watched_scripts(checksum,false);
				break;
			}	
			default:
				break;
		}
	}

	return Net::HANDLER_CONTINUE;
}

int	CScriptDebugger::s_handle_watch_script( Net::MsgHandlerContext* context )
{   
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	
	Dbg_MsgAssert(context->m_MsgLength==12,("Unexpected length of %d for vMSG_ID_DBG_WATCH_SCRIPT", context->m_MsgLength));
	
	uint32 *p_data=(uint32*)context->m_Msg;   			
	bool stop_script_immediately=p_data[0];
	
	uint32 type_of_checksum=p_data[1];
	uint32 checksum=p_data[2];

	if (type_of_checksum==CHECKSUM_IS_SCRIPT_ID)
	{
		Script::CScript *p_script=s_find_cscript_given_id(checksum);  
		if (p_script)
		{
			p_script->WatchInDebugger(stop_script_immediately);
			// Make sure the debugger at least gets some info, otherwise it won't get
			// any if the scripts update function is not called.
			p_script->TransmitInfoToDebugger();
		}	
		return Net::HANDLER_CONTINUE;
	}

	p_this->add_to_watched_scripts(checksum,stop_script_immediately);
	
	return Net::HANDLER_CONTINUE;	
}

int	CScriptDebugger::s_handle_stop( Net::MsgHandlerContext* context )
{   
	Dbg_MsgAssert(context->m_MsgLength==4,("Unexpected length of %d for vMSG_ID_DBG_STOP", context->m_MsgLength));
	uint32 id=*(uint32*)context->m_Msg;
	
	Script::CScript *p_script=s_find_cscript_given_id(id);
	if (!p_script)
	{
		return Net::HANDLER_CONTINUE;
	}

	p_script->DebugStop();
	
	return Net::HANDLER_CONTINUE;
}

int	CScriptDebugger::s_handle_step_into( Net::MsgHandlerContext* context )
{   
	Dbg_MsgAssert(context->m_MsgLength==4,("Unexpected length of %d for vMSG_ID_DBG_STOP", context->m_MsgLength));
	uint32 id=*(uint32*)context->m_Msg;

	Script::CScript *p_script=s_find_cscript_given_id(id);
	if (!p_script)
	{
		return Net::HANDLER_CONTINUE;
	}
	
	p_script->DebugStepInto();
	
	return Net::HANDLER_CONTINUE;
}

int	CScriptDebugger::s_handle_step_over( Net::MsgHandlerContext* context )
{   
	Dbg_MsgAssert(context->m_MsgLength==4,("Unexpected length of %d for vMSG_ID_DBG_STOP", context->m_MsgLength));
	uint32 id=*(uint32*)context->m_Msg;

	Script::CScript *p_script=s_find_cscript_given_id(id);
	if (!p_script)
	{
		return Net::HANDLER_CONTINUE;
	}
	
	p_script->DebugStepOver();
	
	return Net::HANDLER_CONTINUE;
}

int	CScriptDebugger::s_handle_go( Net::MsgHandlerContext* context )
{   
	Dbg_MsgAssert(context->m_MsgLength==4,("Unexpected length of %d for vMSG_ID_DBG_STOP", context->m_MsgLength));
	uint32 id=*(uint32*)context->m_Msg;

	Script::CScript *p_script=s_find_cscript_given_id(id);
	if (!p_script)
	{
		return Net::HANDLER_CONTINUE;
	}
	
	p_script->DebugGo();
	
	return Net::HANDLER_CONTINUE;
}

// This gets called whenever the "Clear script watches" menu option in the script debugger
// is chosen.
int	CScriptDebugger::s_handle_clear_script_watches( Net::MsgHandlerContext* context )
{   
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	
	// Clear the list of scripts being watched.
	p_this->m_num_watched_scripts=0;
	// Make sure the list gets updated in the debugger.
	p_this->transmit_watch_list();
	
	// Then run through all existing scripts making sure none of them are transmitting any more.
	Script::CScript *p_script=Script::GetNextScript(NULL);
	while (p_script)
	{
		p_script->StopWatchingInDebugger();
		
		// Also force any window open in the debugger for this script to close, by
		// sending a script-died message.
		uint32 id=p_script->GetUniqueId();
		Net::MsgDesc msg;
		msg.m_Data = &id;;
		msg.m_Length = 4;
		msg.m_Id = GameNet::vMSG_ID_DBG_SCRIPT_DIED;
		p_this->StreamMessage(&msg);
		
		p_script=Script::GetNextScript(p_script);
	}	
	
	return Net::HANDLER_CONTINUE;
}

int	CScriptDebugger::s_handle_refresh( Net::MsgHandlerContext* context )
{   
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->refresh();
	
	return Net::HANDLER_CONTINUE;
}

int	CScriptDebugger::s_handle_disconnect( Net::MsgHandlerContext* context )
{   
	Dbg_Printf("Got disconnect message from monitor.exe\n");
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->stop_debugging();
	
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_send_cscript_list( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->transmit_cscript_list();
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_send_watch_list( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->transmit_watch_list();
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_send_composite_object_list( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->transmit_composite_object_list();
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_set_object_update_mode( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->set_object_update_mode((SObjectUpdateMode*)context->m_Msg);
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_view_object( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->view_object((SViewObject*)context->m_Msg);
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_stop_watching_this_index( Net::MsgHandlerContext* context )
{
	uint32 index=*(uint32*)context->m_Msg;

	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->stop_watching_script(index);
		
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_stop_watching_this_cscript( Net::MsgHandlerContext* context )
{
	uint32 id=*(uint32*)context->m_Msg;
	
	Script::CScript *p_script=s_find_cscript_given_id(id);
	if (p_script)
	{
		p_script->StopWatchingInDebugger();
	}	
		
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_send_composite_object_info( Net::MsgHandlerContext* context )
{
	uint32 id=*(uint32*)context->m_Msg;
	
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->send_composite_object_info(id);
	
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_send_script_global_info( Net::MsgHandlerContext* context )
{
	uint32 id=*(uint32*)context->m_Msg;
	
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->send_script_global_info(id);
	
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_mouse_position( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	
	// Ignore the message if the m_mouse_on_screen flag is not set, because that would
	// mean it was a late message, received after the mouse had gone off screen.
	if (p_this->m_mouse_on_screen)
	{
		sint16 *p_coords=(sint16*)context->m_Msg;
		int x=p_coords[0];
		int y=p_coords[1];
	
		p_this->m_mouse_x=x;	
		p_this->m_mouse_y=y;	
		p_this->m_got_valid_mouse_position=true;
		
		Script::CStruct *p_params=new Script::CStruct;
		p_params->AddInteger("x",x);
		p_params->AddInteger("y",y);
		Script::RunScript("UpdateDebuggerMousePosition",p_params);
		delete p_params;
	}	
	
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_mouse_off_screen( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->switch_off_mouse_cursor();
	
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_mouse_left_button_down( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->left_button_down();
	
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_mouse_left_button_up( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->left_button_up();
	
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_mouse_right_button_down( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->right_button_down();
	
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_mouse_right_button_up( Net::MsgHandlerContext* context )
{
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->m_right_button_down=false;
	
	return Net::HANDLER_CONTINUE;
}

int CScriptDebugger::s_handle_mouse_on_screen( Net::MsgHandlerContext* context )
{
	// Set the m_mouse_on_screen flag so that mouse position messages are not ignored.
	CScriptDebugger *p_this=(CScriptDebugger*)context->m_Data;
	p_this->m_mouse_on_screen=true;
	
	// Initialise these flags to false so that the buttons have to be released first.
	p_this->m_left_button_down=false;
	p_this->m_right_button_down=false;
	
	return Net::HANDLER_CONTINUE;
}

void CScriptDebugger::EnqueueMessage(Net::MsgDesc *p_message)
{
	if (mp_server)
	{
		mp_server->EnqueueMessage(Net::HANDLE_ID_BROADCAST,p_message);
	}	
}

void CScriptDebugger::StreamMessage(Net::MsgDesc *p_message)
{
	if (mp_server)
	{
		mp_server->StreamMessage(	Net::HANDLE_ID_BROADCAST,
									p_message->m_Id,
									p_message->m_Length,
									p_message->m_Data,
									"",
									GameNet::vSCRIPT_DEBUG_GROUP );
	}									
}

void CScriptDebugger::send_sub_packet(Net::MsgDesc *p_message, uint32 size, char *p_source,
									  int packetIndex, int totalNumPackets)
{
	if (!mp_server)
	{
		return;
	}	
	
	Dbg_MsgAssert(size <= SPLIT_MESSAGE_MAX_SIZE,("Sub packet size too big"));
	
	static char sp_packet_data[sizeof(SSplitMessageHeader)+SPLIT_MESSAGE_MAX_SIZE];
	SSplitMessageHeader *p_header=(SSplitMessageHeader*)sp_packet_data;
		
	p_header->mOriginalMessageId=p_message->m_Id;
	p_header->mOriginalMessageSize=p_message->m_Length;
	p_header->mThisMessageId=m_split_message_id;
	p_header->mThisMessageIndex=packetIndex;
	p_header->mThisMessageSize=size;
	p_header->mTotalNumMessages=totalNumPackets;
	
	//printf("Id=%x, index=%d, total packets=%d\n",p_header->mThisMessageId,
	//p_header->mThisMessageIndex,
	//p_header->mTotalNumMessages);
	
	memcpy(p_header+1,p_source,size);
	
	Net::MsgDesc msg;
	msg.m_Id = GameNet::vMSG_ID_DBG_PACKET;
	msg.m_Data = sp_packet_data;
	msg.m_Length = sizeof(SSplitMessageHeader)+size;
	
	printf("Transmitting sub packet, size=%d\n",size);
	
	mp_server->EnqueueMessage(Net::HANDLE_ID_BROADCAST,&msg);
}

// This can be used in place of EnqueueMessage or StreamMessage when the message
// data is large, ie bigger than the maximum packet size that EnqueueMessage will allow,
// which is about 1300 bytes.
// The advantage of EnqueueMessage is that the messages arrive very quickly at the other end.
// The disadvantage is that it asserts for packet sizes > 1300 bytes or so.
// The advantage of StreamMessage is that it does allow for large packets.
// The disadvantage is that the packets can take a long time to arrive, since it guarantees that they
// arrive in the correct order, a feature that the script debugger does not always need.
// This means that composite object info cannot be sent every frame, because the packets start piling
// up at the PS2 side and it eventually asserts.

// SplitAndEnqueueMessage tries to get the advantages of both Enqueue and Stream.
// It allows for large packets because it splits them up into smaller packets and sends each of them
// using EnqueueMessage.
void CScriptDebugger::SplitAndEnqueueMessage(Net::MsgDesc *p_message)
{
	if (!mp_server)
	{
		return;
	}	

	printf("Splitting message of size %d\n",p_message->m_Length);
	
	int num_packets=p_message->m_Length / SPLIT_MESSAGE_MAX_SIZE;
	int total_num_packets=num_packets;
	int remainder=p_message->m_Length % SPLIT_MESSAGE_MAX_SIZE;
	if (remainder)
	{
		++total_num_packets;
	}
	
	char *p_source_data=(char*)p_message->m_Data;
	for (int i=0; iGetReceiveDataTask().vCall();
		mp_server->GetProcessDataTask().vCall();
		mp_server->GetSendDataTask().vCall();
		mp_server->GetNetworkMetricsTask().vCall();
		while(( Tmr::GetTime() - start_loop_time ) < 17 );
	}
}

void CScriptDebugger::ScriptDebuggerPause( void )
{
	//Dbg_Printf( "Pausing Game\n" );
	process_net_tasks_loop();
}

void CScriptDebugger::ScriptDebuggerUnpause( void )
{
	//Dbg_Printf( "Unpausing Game\n" );
	m_paused = false;
}

void CScriptDebugger::TransmitExceptionInfo(Obj::SException *p_exception, Obj::CCompositeObject* p_obj)
{
	#if 0
	if (mp_server)
	{
		//SCRIPT_DEBUGGER_BUFFER_SIZE
		gpDebugInfoBuffer[0]=(uint32)(p_obj->GetID());
		gpDebugInfoBuffer[1]=p_exception->ExceptionNameChecksum;
		gpDebugInfoBuffer[2]=p_exception->ScriptChecksum;
		
		Net::MsgDesc msg;
		msg.m_Data = gpDebugInfoBuffer;
		msg.m_Length = 3*4;
		msg.m_Id = GameNet::vMSG_ID_DBG_EXCEPTION_INFO;
		Dbg::CScriptDebugger::Instance()->EnqueueMessage(&msg);
	}
	#endif
}

// SPEEDOPT: This gets called by the CScript code whenever the script changes.
// It may need to be optimized ... or maybe not.
SWatchedScript *CScriptDebugger::GetScriptWatchInfo(uint32 checksum)
{
	for (int i=0; i sh;
	for( Net::Conn *conn = mp_server->FirstConnection(&sh); conn; conn = mp_server->NextConnection(&sh) )
	{
		if (conn->GetNumResends() > 3)
		{
			conn->Invalidate();
			mp_server->TerminateConnection( conn );
			printf("Dropping PC connection due to exceeding 3 resends ...\n");
		}
	}
}

// A third of a second
#define DOUBLE_CLICK_TIME_WINDOW (150000000/3)
void CScriptDebugger::left_button_down()
{
	m_left_button_down=true;
	
	// If a double-click is done then automatically view the object.
	Tmr::CPUCycles time=Tmr::GetTimeInCPUCycles();
	if (time-m_state_when_left_pressed.mTime < DOUBLE_CLICK_TIME_WINDOW)
	{
		if (mp_currently_selected_object)
		{
			SViewObject v;
			v.mDoViewObject=true;
			v.mObjectId=mp_currently_selected_object->GetID();
			view_object(&v);
			
			m_left_button_down=false;
		}
	}
	m_state_when_left_pressed.mTime=time;
	
	m_state_when_left_pressed.mMouseX=m_mouse_x;
	m_state_when_left_pressed.mMouseY=m_mouse_y;
	m_state_when_left_pressed.mCamPhi=m_cam_phi;
	m_state_when_left_pressed.mCamTheta=m_cam_theta;
	
	if (0)//mp_viewed_object)
	{
		// Mouse clicks do not send object info when viewing an object. Instead, holding the left button
		// down allows the mouse to rotate the camera around the object.
	}
	else
	{
		// Run a script to make the mouse text do something so that we know the click has had an effect.
		Script::RunScript("DoMouseClickEffect");
		send_composite_object_info(mp_currently_selected_object);
	}	
}

void CScriptDebugger::left_button_up()
{
	m_left_button_down=false;
}

void CScriptDebugger::right_button_down()
{
	m_right_button_down=true;
	
	m_state_when_right_pressed.mMouseX=m_mouse_x;
	m_state_when_right_pressed.mMouseY=m_mouse_y;
	m_state_when_right_pressed.mCamRadius=m_cam_radius;
}

enum
{
	MOUSE_PICK_SEARCH_RADIUS=70
};
	
void CScriptDebugger::update_mouse_cursor()
{
	// The m_got_valid_mouse_position is to prevent glitches due to m_mouse_on_screen being
	// set but no mouse position message received yet.
	if (m_mouse_on_screen && m_got_valid_mouse_position)
	{
		Obj::CCompositeObject *p_ob=NULL;
															
		if (0)//mp_viewed_object)
		{
			// If we are viewing an object, then the mouse will be used to rotate the camera view
			// around the object, so don't search for a new object cos the text will just get in the way.
		}
		else
		{
			p_ob=s_find_nearest_object(	m_mouse_x,
										m_mouse_y,
										MOUSE_PICK_SEARCH_RADIUS);
		}
		
		if (p_ob != mp_currently_selected_object)
		{
			s_change_render_mode(mp_currently_selected_object,m_original_object_render_mode);
			
			mp_currently_selected_object=p_ob;
			m_original_object_render_mode=s_change_render_mode(p_ob,Nx::vBBOX);
		}
		
		if (mp_currently_selected_object)
		{
			Script::CStruct *p_struct=new Script::CStruct;
			p_struct->AddInteger("x",m_mouse_x);
			p_struct->AddInteger("y",m_mouse_y);
			p_struct->AddString("text",Script::FindChecksumName(mp_currently_selected_object->GetID()));
			Script::RunScript("SetMouseText",p_struct);
			delete p_struct;
		}	
		else
		{
			Script::RunScript("DestroyMouseText");
		}
	}		
}

void CScriptDebugger::switch_off_mouse_cursor()
{
	// Destroy the mouse cursor screen elements, then set m_mouse_on_screen to false 
	// so that any late mouse position messages get ignored.
	Script::RunScript("DestroyMouseCursor");
	m_mouse_on_screen=false;
	m_got_valid_mouse_position=false;
	// Reset these too so that there is no confusion.
	m_left_button_down=false;
	m_right_button_down=false;
	
	
	if (mp_currently_selected_object)
	{
		s_change_render_mode(mp_currently_selected_object,m_original_object_render_mode);
	}
	mp_currently_selected_object=NULL;	
	m_original_object_render_mode=Nx::vNONE;
	
}

void CScriptDebugger::do_any_every_frame_object_updates()
{
	++m_frames_since_last_object_update;
	
	// Restrict the rate at which data is sent to be less than 200K per second, as a safeguard.
	if (m_object_update_total_bytes_sent * 60 / m_frames_since_last_object_update > 200000)
	{
		return;
	}
		
	m_object_update_total_bytes_sent=0;
	m_frames_since_last_object_update=0;
	
	Lst::Search sh;
	Obj::CCompositeObject *p_ob = (Obj::CCompositeObject*) sh.FirstItem( Obj::CCompositeObjectManager::Instance()->GetRefObjectList() );
	while (p_ob)
	{
		if (p_ob->GetDebuggerEveryFrameFlag())
		{
			m_object_update_total_bytes_sent+=send_composite_object_info(p_ob);
		}
		p_ob = (Obj::CCompositeObject*)sh.NextItem();
	}
}

void CScriptDebugger::stop_all_every_frame_object_updates()
{
	Lst::Search sh;
	Obj::CCompositeObject *p_ob = (Obj::CCompositeObject*) sh.FirstItem( Obj::CCompositeObjectManager::Instance()->GetRefObjectList() );
	while (p_ob)
	{
		p_ob->SetDebuggerEveryFrameFlag(false);
		p_ob = (Obj::CCompositeObject*)sh.NextItem();
	}
}

void CScriptDebugger::setup_default_camera_viewing_position()
{
	int type=SKATE_TYPE_UNDEFINED;
	if (mp_viewed_object)
	{
		type=mp_viewed_object->GetType();
	}
		
	switch (type)
	{
	case SKATE_TYPE_PED:
	case SKATE_TYPE_SKATER:
		m_cam_radius=100.0f;
		m_cam_theta=1.107f;
		m_cam_focus_offset=40;
		break;
	case SKATE_TYPE_CAR:
		m_cam_radius=200.0f;
		m_cam_theta=1.326f;
		m_cam_focus_offset=40;
		break;
	default:
		m_cam_radius=100.0f;
		m_cam_theta=1.107f;
		m_cam_focus_offset=0;
		break;
	}
	m_cam_phi=0.0f;

	m_state_when_left_pressed.mCamPhi=m_cam_phi;
	m_state_when_left_pressed.mCamTheta=m_cam_theta;
	m_state_when_right_pressed.mCamRadius=m_cam_radius;
	
	// If the viewed object has a bounding radius defined then add that to the initial camera radius
	// too, so that the camera does not start off inside big things.
	if (mp_viewed_object)
	{
		Obj::CModelComponent *p_model_component=(Obj::CModelComponent*)mp_viewed_object->GetComponent(CRCD(0x286a8d26,"Model"));
		if (p_model_component)
		{
			Nx::CModel *p_model=p_model_component->GetModel();
			if (p_model)
			{
				Mth::Vector r=p_model->GetBoundingSphere();
				
				// Apply any scaling, needed for the big alligator in the zoo.
				Mth::Vector scale=p_model->GetScale();
				float s=scale.GetX();
				if (scale.GetY()>s) s=scale.GetY();
				if (scale.GetZ()>s) s=scale.GetZ();
				
				m_cam_radius+=r.Length()*s;
			}	
		}
	}	
}

// This will override the camera position and angles so as to view the mp_viewed_object.
void CScriptDebugger::update_camera()
{
	// Note: mp_viewed_object is a smart pointer, so will become NULL if the object dies.
	if (!mp_viewed_object)
	{
		// Nothing to see
		return;
	}	

	// Get the skater's camera.
	Mdl::Skate * p_skate_mod =  Mdl::Skate::Instance();
	Obj::CSkater *p_skater = p_skate_mod->GetSkater(0);
	if (!p_skater)
	{
		// No skater
		return;
	}
		
	Gfx::Camera *p_skater_cam=p_skater->GetActiveCamera();
	if (!p_skater_cam)
	{
		// No skater camera
		return;
	}	


	if (m_mouse_on_screen && m_got_valid_mouse_position)
	{
		if (m_left_button_down)
		{
			m_cam_theta=m_state_when_left_pressed.mCamTheta+Script::GetFloat("MouseRotationUpDownFactor")*(m_mouse_y-m_state_when_left_pressed.mMouseY);
			
			if (m_cam_theta > Mth::PI-0.01f)
			{
				m_cam_theta = Mth::PI-0.01f;
			}	
			else if (m_cam_theta < 0.01f)
			{
				m_cam_theta = 0.01f;
			}	
			
			m_cam_phi=m_state_when_left_pressed.mCamPhi+Script::GetFloat("MouseRotationLeftRightFactor")*(m_mouse_x-m_state_when_left_pressed.mMouseX);
			if (m_cam_phi > Mth::PI*2.0f)
			{
				m_cam_phi -= Mth::PI*2.0f;
			}
			else if (m_cam_phi < -Mth::PI*2.0f)
			{
				m_cam_phi += Mth::PI*2.0f;
			}
		}
			
		if (m_right_button_down)
		{
			m_cam_radius=m_state_when_right_pressed.mCamRadius+Script::GetFloat("MouseZoomFactor")*(m_mouse_y-m_state_when_right_pressed.mMouseY);
			
			if (m_cam_radius < 0.01f)
			{
				m_cam_radius=0.01f;
			}	
		}
	}

	Mth::Vector cam_radius_vector;
	cam_radius_vector[Y]=cosf(m_cam_theta);
	float s=sinf(m_cam_theta);
	cam_radius_vector[X]=s*cosf(m_cam_phi);
	cam_radius_vector[Z]=s*sinf(m_cam_phi);
	cam_radius_vector.Normalize(); // Just to be sure
	cam_radius_vector *= m_cam_radius;

	// Rotate using the object's matrix so that the camera's frame of
	// reference is that of the object, otherwise the camera will not turn with the object.
	cam_radius_vector *= mp_viewed_object->GetDisplayMatrix();
	
	Mth::Vector focus_pos=mp_viewed_object->GetPos();
	focus_pos[Y]+=m_cam_focus_offset;
	Mth::Vector camera_pos=focus_pos+cam_radius_vector;

	camera_pos[W] = 1.0f;
	p_skater_cam->SetPos(camera_pos);

	Mth::Matrix mat;
	mat.Ident();
	mat[Z]=focus_pos-camera_pos;
	mat[Z].Normalize();
	mat[X]	= Mth::CrossProduct( mat[Y], mat[Z] );
	mat[X].Normalize();
	mat[Y]	= Mth::CrossProduct( mat[Z], mat[X] );
	mat[Y].Normalize();

	Mth::Matrix& frame_matrix = p_skater_cam->GetMatrix();
	frame_matrix[X][X] = -mat[0][0];
	frame_matrix[X][Y] = -mat[0][1];
	frame_matrix[X][Z] = -mat[0][2];
	frame_matrix[Y][X] = mat[1][0];
	frame_matrix[Y][Y] = mat[1][1];
	frame_matrix[Y][Z] = mat[1][2];
	frame_matrix[Z][X] = -mat[2][0];
	frame_matrix[Z][Y] = -mat[2][1];
	frame_matrix[Z][Z] = -mat[2][2];
}

void CScriptDebugger::s_logic_code ( const Tsk::Task< CScriptDebugger >& task )
{
	CScriptDebugger&	mdl = task.GetData();
	Dbg_AssertType ( &task, Tsk::Task< CScriptDebugger > );
	
	mdl.check_for_timeouts();

	mdl.update_camera();

	mdl.update_mouse_cursor();	

	mdl.do_any_every_frame_object_updates();
	mdl.send_any_single_step_object_info();
	
	switch (mdl.m_state)
	{
	case SCRIPT_DEBUGGER_STATE_IDLE:
	{
		break;
	}
			
	default:
		break;
	}			
}

} // namespace Dbg
#endif


================================================
FILE: Code/Gel/Scripting/debugger.h
================================================
#ifndef __GEL_SCRIPTING_DEBUGGER_H__
#define __GEL_SCRIPTING_DEBUGGER_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __GEL_MODULE_H
#include 
#endif

#ifndef __NET_H__
#include 
#endif

#ifndef __NETSERV_H__
#include 
#endif

#ifndef	__GFX_NXMODEL_H__
#include 
#endif

#ifndef __GEL_OBJPTR_H
#include 
#endif

// This is outside the #ifdef __NOPT_ASSERT__ so that they can be used in the PC code,
// which includes this file but does not have __NOPT_ASSERT__ defined.
namespace Obj
{
	class CObject;
	class CCompositeObject;
	struct SException;
}

namespace Dbg
{

// Points to the fixed area in high memory where the game code stores stuff for the
// script debugger to read using the target manager API.
// (Note: The script debugger app includes this file, so has access to these defines)
// Currently, the first thing stored there is a SChecksumNameInfo structure.
// The game code will assert if this value does not match the symbol _script_debugger_start
enum
{
	SCRIPT_DEBUGGER_MEMORY_START=0x06E00080
};

// Contains pointers to the start and end of the big list of names whose checksums are
// loaded. (Eg, every word in every q file currently loaded)
// This lets the script debugger register all those checksums when it starts up, so that
// it can resolve checksums to names. 
struct SChecksumNameInfo
{
	char **mppStart;
	char **mppEnd;
};



// Used in the vMSG_ID_DBG_WATCH_SCRIPT packet.
enum
{
	CHECKSUM_IS_SCRIPT_NAME,
	CHECKSUM_IS_SCRIPT_ID,
};

// An array of these gets passed from the PS2 to the debugger in the
// vMSG_ID_DBG_WATCH_LIST meesage
struct SWatchInfo
{
	uint32 mScriptName;
	bool mStopImmediately;
};

enum EObjectUpdateMode
{
	EVERY_FRAME,
	SINGLE_STEP,
};	
	
// One of these gets passed to the PS2 from the debugger in the
// vMSG_ID_DBG_SET_OBJECT_UPDATE_MODE message.
struct SObjectUpdateMode
{
	uint32 mObjectId;
	EObjectUpdateMode mMode;
	bool mOn;
};

// One of these gets passed to the PS2 from the debugger in the
// vMSG_ID_DBG_VIEW_OBJECT message.

struct SViewObject
{
	uint32 mObjectId;
	bool mDoViewObject;
};

struct SSplitMessageHeader
{
	char mOriginalMessageId;
	uint32 mOriginalMessageSize;
	
	int mTotalNumMessages;
	
	uint32 mThisMessageId;
	uint32 mThisMessageSize;
	int mThisMessageIndex;
};

enum
{
	// This makes each sub-packet be as big as possible. Netapp.cpp will assert if the total
	// packet size is >= Net::Manager::vMAX_PACKET_SIZE - 64
	// The sub-packet includes a SSplitMessageHeader, hence that size has to be subtracted too.
	SPLIT_MESSAGE_MAX_SIZE=Net::Manager::vMAX_PACKET_SIZE - 65 - sizeof(SSplitMessageHeader),
};

}

#ifdef __NOPT_ASSERT__

namespace Net
{
	class MsgDesc;
}	

namespace Dbg
{

enum
{
	SCRIPT_DEBUGGER_BUFFER_SIZE=60*1024, // Must be a multiple of 4
};	

enum EWatchType
{
	NONE=0,
	SCRIPT,
	SCRIPT_GLOBAL,
	OBJECT_ID,
	EXCEPTION,
};

#define MAX_WATCHED_SCRIPTS 100
struct SWatchedScript
{
	uint32 mChecksum;
	bool mStopScriptImmediately;
};

struct SStateWhenLeftPressed
{
	int mMouseX;
	int mMouseY;
	float mCamTheta;
	float mCamPhi;
	Tmr::CPUCycles mTime;
};

struct SStateWhenRightPressed
{
	int mMouseX;
	int mMouseY;
	float mCamRadius;
};

class CScriptDebugger  : public Mdl::Module
{
	DeclareSingletonClass( CScriptDebugger );

	Net::Server *mp_server;
	// Used by the SplitAndEnqueueMessage function to give an id to the message
	// that is being split. Each of the sub-messages that the message is split into
	// is given this id so that they can be reconstructed at the other end.
	// It increments by 1 each time it is used.
	uint32 m_split_message_id;
	void send_sub_packet(Net::MsgDesc *p_message, uint32 size, char *p_source,
						 int packetIndex, int totalNumPackets);
	
	enum EState
	{
		SCRIPT_DEBUGGER_STATE_IDLE,
	};
		
	EState m_state;
	char *mp_checksum_names_current_pos;
	char *mp_checksum_names_end;

	int m_num_watched_scripts;
	bool m_paused;
	SWatchedScript mp_watched_scripts[MAX_WATCHED_SCRIPTS];
	
	bool m_mouse_on_screen;
	bool m_got_valid_mouse_position;
	int m_mouse_x;
	int m_mouse_y;
	Obj::CCompositeObject *mp_currently_selected_object; // TODO: Change to be a smart pointer !!!
	Nx::ERenderMode m_original_object_render_mode;
	bool m_left_button_down;	
	SStateWhenLeftPressed m_state_when_left_pressed;
	
	bool m_right_button_down;	
	SStateWhenRightPressed m_state_when_right_pressed;
	
	void left_button_down();
	void left_button_up();
	void right_button_down();

	void update_mouse_cursor();
	void switch_off_mouse_cursor();
	
	Obj::CSmtPtr mp_viewed_object;
	bool m_in_object_single_step_mode;
	uint32 m_single_step_object_id;

	int m_object_update_total_bytes_sent;
	int m_frames_since_last_object_update;
	
	float m_cam_radius;
	float m_cam_theta;
	float m_cam_phi;
	float m_cam_focus_offset;
	void setup_default_camera_viewing_position();
	void update_camera();
	
	void v_start_cb ( void );
	void v_stop_cb ( void );
	
	static Tsk::Task< CScriptDebugger >::Code s_logic_code;       
	Tsk::Task< CScriptDebugger > *mp_logic_task;
	
	static Net::MsgHandlerCode s_handle_connection_request;
	static Net::MsgHandlerCode s_handle_watch_this;
	static Net::MsgHandlerCode s_handle_watch_script;

	static Net::MsgHandlerCode s_handle_stop;
	static Net::MsgHandlerCode s_handle_step_into;
	static Net::MsgHandlerCode s_handle_step_over;
	static Net::MsgHandlerCode s_handle_go;
	static Net::MsgHandlerCode s_handle_clear_script_watches;
	static Net::MsgHandlerCode s_handle_refresh;
	static Net::MsgHandlerCode s_handle_disconnect;
	static Net::MsgHandlerCode s_handle_send_cscript_list;
	static Net::MsgHandlerCode s_handle_send_watch_list;
	static Net::MsgHandlerCode s_handle_stop_watching_this_index;
	static Net::MsgHandlerCode s_handle_stop_watching_this_cscript;
	static Net::MsgHandlerCode s_handle_send_composite_object_info;
	static Net::MsgHandlerCode s_handle_mouse_position;
	static Net::MsgHandlerCode s_handle_mouse_on_screen;
	static Net::MsgHandlerCode s_handle_mouse_off_screen;
	static Net::MsgHandlerCode s_handle_mouse_left_button_down;
	static Net::MsgHandlerCode s_handle_mouse_left_button_up;
	static Net::MsgHandlerCode s_handle_mouse_right_button_down;
	static Net::MsgHandlerCode s_handle_mouse_right_button_up;
	static Net::MsgHandlerCode s_handle_send_composite_object_list;
	static Net::MsgHandlerCode s_handle_set_object_update_mode;
	static Net::MsgHandlerCode s_handle_view_object;
	static Net::MsgHandlerCode s_handle_send_script_global_info;

	void add_to_watched_scripts(uint32 checksum, bool stopScriptImmediately);
	SWatchedScript *find_watched_script(uint32 checksum);
	void refresh();
	void stop_debugging();
	void stop_watching_script(int index);
	int  send_composite_object_info(Obj::CCompositeObject* p_obj);
	void send_composite_object_info(uint32 id);
	void send_script_global_info(uint32 id);
	void do_any_every_frame_object_updates();
	void stop_all_every_frame_object_updates();
	void send_any_single_step_object_info();
	
	void set_object_update_mode(SObjectUpdateMode *pObjectUpdateMode);
	void view_object(SViewObject *pViewObject);
	
	void process_net_tasks_loop( void );
	void check_for_timeouts( void );
	
	void transmit_cscript_list();
	void transmit_watch_list();
	void transmit_composite_object_list();
	void transmit_compression_lookup_table();

public:
	CScriptDebugger();
	virtual	~CScriptDebugger();
	
	void EnqueueMessage(Net::MsgDesc *p_message);
	void StreamMessage(Net::MsgDesc *p_message);
	
	void SplitAndEnqueueMessage(Net::MsgDesc *p_message);
	
	void ScriptDebuggerPause( void );
	void ScriptDebuggerUnpause( void );

	void TransmitExceptionInfo(Obj::SException *p_exception, Obj::CCompositeObject* p_obj);
	SWatchedScript *GetScriptWatchInfo(uint32 checksum);
};

extern uint32 gpDebugInfoBuffer[];

} // namespace Dbg
#endif // __NOPT_ASSERT__

#endif // #ifndef __GEL_SCRIPTING_DEBUGGER_H__



================================================
FILE: Code/Gel/Scripting/eval.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Script
{

// This is a static rather than a local var of execute_operation() to prevent the 
// constructor being called every time execute_operation is called.
// Saves a tiny bit of time.
CComponent spResult;

static int sPrecedence[]=
{
	-1, // ESCRIPTTOKEN_ENDOFFILE,			// 0
	-1, // ESCRIPTTOKEN_ENDOFLINE,			// 1
	-1, // ESCRIPTTOKEN_ENDOFLINENUMBER,   // 2
	-1, // ESCRIPTTOKEN_STARTSTRUCT,       // 3
	-1, // ESCRIPTTOKEN_ENDSTRUCT,         // 4
	100, // ESCRIPTTOKEN_STARTARRAY,        // 5
	-1, // ESCRIPTTOKEN_ENDARRAY,          // 6
	76, // ESCRIPTTOKEN_EQUALS,            // 7
	100, // ESCRIPTTOKEN_DOT,               // 8
	-1, // ESCRIPTTOKEN_COMMA,             // 9
	98, // ESCRIPTTOKEN_MINUS,             // 10
	98, // ESCRIPTTOKEN_ADD,               // 11
	99, // ESCRIPTTOKEN_DIVIDE,            // 12
	100, // ESCRIPTTOKEN_MULTIPLY,          // 13
	-1, // ESCRIPTTOKEN_OPENPARENTH,       // 14
	-1, // ESCRIPTTOKEN_CLOSEPARENTH,      // 15
	-1, // ESCRIPTTOKEN_DEBUGINFO,			// 16
	-1, // ESCRIPTTOKEN_SAMEAS,			// 17
	80, // ESCRIPTTOKEN_LESSTHAN,			// 18
	79, // ESCRIPTTOKEN_LESSTHANEQUAL,     // 19
	78, // ESCRIPTTOKEN_GREATERTHAN,       // 20
	77, // ESCRIPTTOKEN_GREATERTHANEQUAL,  // 21
	-1, // ESCRIPTTOKEN_NAME,				// 22
	-1, // ESCRIPTTOKEN_INTEGER,			// 23
	-1, // ESCRIPTTOKEN_HEXINTEGER,        // 24
    -1, // ESCRIPTTOKEN_ENUM,              // 25
	-1, // ESCRIPTTOKEN_FLOAT,             // 26
	-1, // ESCRIPTTOKEN_STRING,            // 27
	-1, // ESCRIPTTOKEN_LOCALSTRING,       // 28
	-1, // ESCRIPTTOKEN_ARRAY,             // 29
	-1, // ESCRIPTTOKEN_VECTOR,            // 30
	-1, // ESCRIPTTOKEN_PAIR,				// 31
	-1, // ESCRIPTTOKEN_KEYWORD_BEGIN,		// 32
	-1, // ESCRIPTTOKEN_KEYWORD_REPEAT,    // 33
	-1, // ESCRIPTTOKEN_KEYWORD_BREAK,     // 34
	-1, // ESCRIPTTOKEN_KEYWORD_SCRIPT,    // 35
	-1, // ESCRIPTTOKEN_KEYWORD_ENDSCRIPT, // 36
	-1, // ESCRIPTTOKEN_KEYWORD_IF,        // 37
	-1, // ESCRIPTTOKEN_KEYWORD_ELSE,      // 38
	-1, // ESCRIPTTOKEN_KEYWORD_ELSEIF,    // 39
	-1, // ESCRIPTTOKEN_KEYWORD_ENDIF,		// 40
	-1, // ESCRIPTTOKEN_KEYWORD_RETURN,	// 41
    -1, // ESCRIPTTOKEN_UNDEFINED,			// 42
	-1, // ESCRIPTTOKEN_CHECKSUM_NAME,		// 43
	-1, // ESCRIPTTOKEN_KEYWORD_ALLARGS,	// 44
	-1, // ESCRIPTTOKEN_ARG,				// 45
	-1, // ESCRIPTTOKEN_JUMP,				// 46
	-1, // ESCRIPTTOKEN_KEYWORD_RANDOM,    // 47
	-1, // ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE,	// 48
	-1, // ESCRIPTTOKEN_AT,				// 49
	58, // ESCRIPTTOKEN_OR,				// 50
	60, // ESCRIPTTOKEN_AND,				// 51
	59, // ESCRIPTTOKEN_XOR,				// 52
	90, // ESCRIPTTOKEN_SHIFT_LEFT,		// 53
	89, // ESCRIPTTOKEN_SHIFT_RIGHT,		// 54
	-1, // ESCRIPTTOKEN_KEYWORD_RANDOM2,		// 55
	-1, // ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE2, // 56
	-1, // ESCRIPTTOKEN_KEYWORD_NOT,			// 57
	-1, // ESCRIPTTOKEN_KEYWORD_AND,			// 58
	-1, // ESCRIPTTOKEN_KEYWORD_OR,            // 59
	-1, // ESCRIPTTOKEN_KEYWORD_SWITCH,       	// 60
	-1, // ESCRIPTTOKEN_KEYWORD_ENDSWITCH,   	// 61
	-1, // ESCRIPTTOKEN_KEYWORD_CASE,          // 62
	-1, // ESCRIPTTOKEN_KEYWORD_DEFAULT,		// 63
	-1,	// ESCRIPTTOKEN_KEYWORD_RANDOM_NO_REPEAT,	// 64
	-1,	// ESCRIPTTOKEN_KEYWORD_RANDOM_PERMUTE,	// 65
	-1, // ESCRIPTTOKEN_COLON,		// 66
	-1, // ESCRIPTTOKEN_RUNTIME_CFUNCTION,	// 67
	-1, // ESCRIPTTOKEN_RUNTIME_MEMBERFUNCTION, // 68
};

static bool sSameOrLowerPrecedence(EScriptToken a, EScriptToken b)
{
	//printf("Precedence of %s=%d, %s=%d\n",GetTokenName(a),sPrecedence[a],GetTokenName(b),sPrecedence[b]);
	if (a==b)
	{
		return true;
	}
	
	return sPrecedence[a]<=sPrecedence[b];
}

CExpressionEvaluator::CExpressionEvaluator()
{
	Clear();
	EnableErrorChecking();
}

CExpressionEvaluator::~CExpressionEvaluator()
{
	Clear();
}

void CExpressionEvaluator::Clear()
{
	int i;

	for (i=0; imType==ESYMBOLTYPE_INTEGER)
	{
		return p_comp->mIntegerValue;
	}
	else if (p_comp->mType==ESYMBOLTYPE_FLOAT)
	{
		return p_comp->mFloatValue?1:0;
	}
	else
	{
		#ifdef __NOPT_ASSERT__
		// So that the calling code can set an error message.
		// Not asserting at this point because there is no way of printing the line number of the error.
		return 2;
		#else
		return 0;
		#endif	
	}
}

void CExpressionEvaluator::execute_operation()
{
	if (m_value_stack_top<1)
	{
		set_error("Not enough values in stack to execute operation");
		return;
	}	
	if (mp_operator_stack[m_operator_stack_top].mParenthesesCount)
	{
		set_error("Non-zero parentheses count");
		return;
	}	
	
	// Apply the top operator to the top two values, and replace them with the new value.
	// Then remove the top operator.
	
	EScriptToken op=mp_operator_stack[m_operator_stack_top].mOperator;
	if (op==NOP)
	{
		set_error("Tried to execute with no operator");
		return;
	}

	CComponent *pA=&mp_value_stack[m_value_stack_top-1];
	CComponent *pB=&mp_value_stack[m_value_stack_top];
	
	spResult.mType=ESYMBOLTYPE_NONE;
	spResult.mUnion=0;

	//printf("Executing '%s'\n",GetTokenName(op));
	switch (op)
	{
	case ESCRIPTTOKEN_OR:
	{
		spResult.mType=ESYMBOLTYPE_INTEGER;
		int a=GetBool(pA);
		int b=GetBool(pB);
		#ifdef __NOPT_ASSERT__
		if (a==2 || b==2)
		{
			set_error("Bad data types for 'or' operator");
		}
		#endif
		spResult.mIntegerValue=a | b;
		break;
	}
	case ESCRIPTTOKEN_AND:
	{
		spResult.mType=ESYMBOLTYPE_INTEGER;
		int a=GetBool(pA);
		int b=GetBool(pB);
		#ifdef __NOPT_ASSERT__
		if (a==2 || b==2)
		{
			set_error("Bad data types for 'or' operator");
		}
		#endif
		spResult.mIntegerValue=a & b;
		break;
	}
	
	case ESCRIPTTOKEN_EQUALS:
		spResult.mType=ESYMBOLTYPE_INTEGER;
		spResult.mIntegerValue=*pA==*pB;
		break;
		
	case ESCRIPTTOKEN_LESSTHAN:
		spResult.mType=ESYMBOLTYPE_INTEGER;
		spResult.mIntegerValue=*pA<*pB;
		break;

	case ESCRIPTTOKEN_GREATERTHAN:
		spResult.mType=ESYMBOLTYPE_INTEGER;
		spResult.mIntegerValue=*pA>*pB;
		break;
	
	case ESCRIPTTOKEN_ADD:
		switch (pA->mType)
		{
		case ESYMBOLTYPE_INTEGER:
			if (pB->mType==ESYMBOLTYPE_INTEGER)
			{
				spResult.mType=ESYMBOLTYPE_INTEGER;
				spResult.mIntegerValue=pA->mIntegerValue+pB->mIntegerValue;
			}
			else if (pB->mType==ESYMBOLTYPE_FLOAT)
			{
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=(float)pA->mIntegerValue+pB->mFloatValue;
			}
			else
			{
				set_error("Second arg cannot be added to an integer");
			}	
			break;
		case ESYMBOLTYPE_FLOAT:
			if (pB->mType==ESYMBOLTYPE_INTEGER)
			{
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=pA->mFloatValue+(float)pB->mIntegerValue;
			}
			else if (pB->mType==ESYMBOLTYPE_FLOAT)
			{
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=pA->mFloatValue+pB->mFloatValue;
			}
			else
			{
				set_error("Second arg cannot be added to a float");
			}	
			break;
		// Checksums may have integers added to them to give a new checksum.
		// This was added cos Steve needed to be able to give new unique id's
		// to some screen elements by adding an offset to a base checksum, 
		// using id=(achecksum+1) for example.
 		case ESYMBOLTYPE_NAME:
			if (pB->mType==ESYMBOLTYPE_INTEGER)
			{
				spResult.mType=ESYMBOLTYPE_NAME;
				spResult.mChecksum=pA->mChecksum+pB->mIntegerValue;
			}
			else
			{
				set_error("Second arg cannot be added to a checksum");
			}	
			break;
		case ESYMBOLTYPE_VECTOR:
			if (pB->mType==ESYMBOLTYPE_VECTOR)
			{
				spResult.mType=ESYMBOLTYPE_VECTOR;
				spResult.mpVector=new CVector;
				spResult.mpVector->mX=pA->mpVector->mX+pB->mpVector->mX;
				spResult.mpVector->mY=pA->mpVector->mY+pB->mpVector->mY;
				spResult.mpVector->mZ=pA->mpVector->mZ+pB->mpVector->mZ;
			}
			else
			{
				set_error("Second arg cannot be added to a vector");
			}	
			break;
		case ESYMBOLTYPE_PAIR:
			if (pB->mType==ESYMBOLTYPE_PAIR)
			{
				spResult.mType=ESYMBOLTYPE_PAIR;
				spResult.mpPair=new CPair;
				spResult.mpPair->mX=pA->mpPair->mX+pB->mpPair->mX;
				spResult.mpPair->mY=pA->mpPair->mY+pB->mpPair->mY;
			}
			else
			{
				set_error("Second arg cannot be added to a pair");
			}	
			break;
		case ESYMBOLTYPE_STRING:
			if (pB->mType==ESYMBOLTYPE_STRING)
			{
				if (strlen(pA->mpString)+strlen(pB->mpString)+1>EVALUATOR_STRING_BUF_SIZE)
				{
					set_error("Result of string addition too long");
				}
				else
				{
					spResult.mType=ESYMBOLTYPE_STRING;
					char p_buf[EVALUATOR_STRING_BUF_SIZE];
					strcpy(p_buf,pA->mpString);
					strcat(p_buf,pB->mpString);
					spResult.mpString=CreateString(p_buf);
				}	
			}
			else
			{
				set_error("Second arg cannot be added to a string");
			}	
			break;
		case ESYMBOLTYPE_LOCALSTRING:
			if (pB->mType==ESYMBOLTYPE_LOCALSTRING)
			{
				if (strlen(pA->mpLocalString)+strlen(pB->mpLocalString)+1>EVALUATOR_STRING_BUF_SIZE)
				{
					set_error("Result of local-string addition too long");
				}
				else
				{
					spResult.mType=ESYMBOLTYPE_LOCALSTRING;
					char p_buf[EVALUATOR_STRING_BUF_SIZE];
					strcpy(p_buf,pA->mpLocalString);
					strcat(p_buf,pB->mpLocalString);
					spResult.mpLocalString=CreateString(p_buf);
				}	
			}
			else
			{
				set_error("Second arg cannot be added to a local-string");
			}	
			break;
		case ESYMBOLTYPE_STRUCTURE:
		{
			if (pB->mType!=ESYMBOLTYPE_STRUCTURE)
			{
				set_error("Structure add operator requires 2nd arg to be a structure");
				break;
			}

			spResult.mType=ESYMBOLTYPE_STRUCTURE;
			spResult.mpStructure=new CStruct;
			spResult.mpStructure->AppendStructure(pA->mpStructure);
			spResult.mpStructure->AppendStructure(pB->mpStructure);
			break;
		}	
		default:
			set_error("Addition not supported for this type");
			break;
		}	
		break;
	case ESCRIPTTOKEN_MINUS:
		switch (pA->mType)
		{
		case ESYMBOLTYPE_INTEGER:
			if (pB->mType==ESYMBOLTYPE_INTEGER)
			{
				spResult.mType=ESYMBOLTYPE_INTEGER;
				spResult.mIntegerValue=pA->mIntegerValue-pB->mIntegerValue;
			}
			else if (pB->mType==ESYMBOLTYPE_FLOAT)
			{
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=(float)pA->mIntegerValue-pB->mFloatValue;
			}
			else
			{
				set_error("Second arg cannot be subtracted from an integer");
			}	
			break;
		case ESYMBOLTYPE_FLOAT:
			if (pB->mType==ESYMBOLTYPE_INTEGER)
			{
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=pA->mFloatValue-(float)pB->mIntegerValue;
			}
			else if (pB->mType==ESYMBOLTYPE_FLOAT)
			{
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=pA->mFloatValue-pB->mFloatValue;
			}
			else
			{
				set_error("Second arg cannot be subtracted from a float");
			}	
			break;
		case ESYMBOLTYPE_VECTOR:
			if (pB->mType==ESYMBOLTYPE_VECTOR)
			{
				spResult.mType=ESYMBOLTYPE_VECTOR;
				spResult.mpVector=new CVector;
				spResult.mpVector->mX=pA->mpVector->mX-pB->mpVector->mX;
				spResult.mpVector->mY=pA->mpVector->mY-pB->mpVector->mY;
				spResult.mpVector->mZ=pA->mpVector->mZ-pB->mpVector->mZ;
			}
			else
			{
				set_error("Second arg cannot be subtracted from a vector");
			}	
			break;
		case ESYMBOLTYPE_PAIR:
			if (pB->mType==ESYMBOLTYPE_PAIR)
			{
				spResult.mType=ESYMBOLTYPE_PAIR;
				spResult.mpPair=new CPair;
				spResult.mpPair->mX=pA->mpPair->mX-pB->mpPair->mX;
				spResult.mpPair->mY=pA->mpPair->mY-pB->mpPair->mY;
			}
			else
			{
				set_error("Second arg cannot be subtracted from a pair");
			}	
			break;
		case ESYMBOLTYPE_STRUCTURE:
		{
			// If the LHS is a structure, then the subtract operator is defined
			// so as to allow parameters to be removed from the structure,
			// cos that's quite handy.
			
			// If the RHS is a single name, then all parameter with that name
			// will be removed, including flags.
			// If the RHS is an array of names then each of those parameters will
			// be removed.
			// If the RHS is a structure, then all the parameters in the LHS which have
			// matching name AND type to something in the RHS will be removed, but if the RHS
			// contains unnamed-names then all parameters with that name will be removed.
			// So {x=3 x="blaa"}-{x=9} gives {x="blaa"}
			// But {x=3 x="blaa"}-{x} gives {}
			
			if (pB->mType==ESYMBOLTYPE_NAME)
			{
				spResult.mType=ESYMBOLTYPE_STRUCTURE;
				spResult.mpStructure=new CStruct;
				spResult.mpStructure->AppendStructure(pA->mpStructure);
				spResult.mpStructure->RemoveComponent(pB->mChecksum);
				spResult.mpStructure->RemoveFlag(pB->mChecksum);
			}
			else if (pB->mType==ESYMBOLTYPE_ARRAY)
			{
				Script::CArray *p_array=pB->mpArray;
				Dbg_MsgAssert(p_array,("NULL p_array"));
				
				if (!(p_array->GetType() == ESYMBOLTYPE_NAME || 
					  p_array->GetType() == ESYMBOLTYPE_NONE)) // Allowing NONE so that empty arrays work.
				{
					set_error("Subtracting an array from a structure requires the array to be an array of names");
				}
				else
				{
					spResult.mType=ESYMBOLTYPE_STRUCTURE;
					spResult.mpStructure=new CStruct;
					spResult.mpStructure->AppendStructure(pA->mpStructure);
					
					for (uint32 i=0; iGetSize(); ++i)
					{
						spResult.mpStructure->RemoveComponent(p_array->GetChecksum(i));
						spResult.mpStructure->RemoveFlag(p_array->GetChecksum(i));
					}
				}
			}
			else if (pB->mType==ESYMBOLTYPE_STRUCTURE)
			{
				spResult.mType=ESYMBOLTYPE_STRUCTURE;
				spResult.mpStructure=new CStruct;
				spResult.mpStructure->AppendStructure(pA->mpStructure);
				
				Script::CStruct *p_struct=pB->mpStructure;
				Dbg_MsgAssert(p_struct,("NULL p_struct"));
				Script::CComponent *p_comp=p_struct->GetNextComponent();
				while (p_comp)
				{
					if (p_comp->mNameChecksum)
					{
						// If the component is named, remove all components of the same name and type.
						spResult.mpStructure->RemoveComponentWithType(p_comp->mNameChecksum,p_comp->mType);
					}
					else if (p_comp->mType==ESYMBOLTYPE_NAME)
					{
						// If it's an unnamed-name (ie a flag) remove all components with that name,
						// including flags.
						spResult.mpStructure->RemoveComponent(p_comp->mChecksum);
						spResult.mpStructure->RemoveFlag(p_comp->mChecksum);
					}					
					p_comp=p_struct->GetNextComponent(p_comp);
				}	
			}
			else
			{
				set_error("Structure minus operator requires 2nd arg to be a name, an array of names, or a structure");
			}			
			break;
		}	
		default:
			set_error("Subtraction not supported for this type");
			break;
		}	
		break;
	case ESCRIPTTOKEN_MULTIPLY:
		switch (pA->mType)
		{
		case ESYMBOLTYPE_INTEGER:
			switch (pB->mType)
			{
			case ESYMBOLTYPE_INTEGER:
				spResult.mType=ESYMBOLTYPE_INTEGER;
				spResult.mIntegerValue=pA->mIntegerValue*pB->mIntegerValue;
				break;
			case ESYMBOLTYPE_FLOAT:
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=(float)pA->mIntegerValue*pB->mFloatValue;
				break;
			case ESYMBOLTYPE_VECTOR:
				spResult.mType=ESYMBOLTYPE_VECTOR;
				spResult.mpVector=new CVector;
				spResult.mpVector->mX=(float)pA->mIntegerValue*pB->mpVector->mX;
				spResult.mpVector->mY=(float)pA->mIntegerValue*pB->mpVector->mY;
				spResult.mpVector->mZ=(float)pA->mIntegerValue*pB->mpVector->mZ;
				break;
			case ESYMBOLTYPE_PAIR:
				spResult.mType=ESYMBOLTYPE_PAIR;
				spResult.mpPair=new CPair;
				spResult.mpPair->mX=(float)pA->mIntegerValue*pB->mpPair->mX;
				spResult.mpPair->mY=(float)pA->mIntegerValue*pB->mpPair->mY;
				break;
			default:
				set_error("Second arg cannot be multiplied by an integer");
				break;
			}	
			break;
		case ESYMBOLTYPE_FLOAT:
			switch (pB->mType)
			{
			case ESYMBOLTYPE_INTEGER:
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=pA->mFloatValue*(float)pB->mIntegerValue;
				break;
			case ESYMBOLTYPE_FLOAT:
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=pA->mFloatValue*pB->mFloatValue;
				break;
			case ESYMBOLTYPE_VECTOR:
				spResult.mType=ESYMBOLTYPE_VECTOR;
				spResult.mpVector=new CVector;
				spResult.mpVector->mX=pA->mFloatValue*pB->mpVector->mX;
				spResult.mpVector->mY=pA->mFloatValue*pB->mpVector->mY;
				spResult.mpVector->mZ=pA->mFloatValue*pB->mpVector->mZ;
				break;
			case ESYMBOLTYPE_PAIR:
				spResult.mType=ESYMBOLTYPE_PAIR;
				spResult.mpPair=new CPair;
				spResult.mpPair->mX=pA->mFloatValue*pB->mpPair->mX;
				spResult.mpPair->mY=pA->mFloatValue*pB->mpPair->mY;
				break;
			default:
				set_error("Second arg cannot be multiplied by a float");
			}	
			break;
		case ESYMBOLTYPE_VECTOR:
			switch (pB->mType)
			{
			case ESYMBOLTYPE_VECTOR:
				// Take multiplication of two vectors to mean the cross-product.
				spResult.mType=ESYMBOLTYPE_VECTOR;
				spResult.mpVector=new CVector;
				spResult.mpVector->mX=pA->mpVector->mY*pB->mpVector->mZ-pA->mpVector->mZ*pB->mpVector->mY;
				spResult.mpVector->mY=pA->mpVector->mZ*pB->mpVector->mX-pA->mpVector->mX*pB->mpVector->mZ;
				spResult.mpVector->mZ=pA->mpVector->mX*pB->mpVector->mY-pA->mpVector->mY*pB->mpVector->mX;
				break;
			case ESYMBOLTYPE_FLOAT:
				spResult.mType=ESYMBOLTYPE_VECTOR;
				spResult.mpVector=new CVector;
				spResult.mpVector->mX=pA->mpVector->mX*pB->mFloatValue;
				spResult.mpVector->mY=pA->mpVector->mY*pB->mFloatValue;
				spResult.mpVector->mZ=pA->mpVector->mZ*pB->mFloatValue;
				break;
			case ESYMBOLTYPE_INTEGER:
				spResult.mType=ESYMBOLTYPE_VECTOR;
				spResult.mpVector=new CVector;
				spResult.mpVector->mX=pA->mpVector->mX*(float)pB->mIntegerValue;
				spResult.mpVector->mY=pA->mpVector->mY*(float)pB->mIntegerValue;
				spResult.mpVector->mZ=pA->mpVector->mZ*(float)pB->mIntegerValue;
				break;
			default:
				set_error("Vector cannot be multiplied by second arg");
				break;
			}	
			break;
		case ESYMBOLTYPE_PAIR:
			switch (pB->mType)
			{
			case ESYMBOLTYPE_FLOAT:
				spResult.mType=ESYMBOLTYPE_PAIR;
				spResult.mpPair=new CPair;
				spResult.mpPair->mX=pA->mpPair->mX*pB->mFloatValue;
				spResult.mpPair->mY=pA->mpPair->mY*pB->mFloatValue;
				break;
			case ESYMBOLTYPE_INTEGER:
				spResult.mType=ESYMBOLTYPE_PAIR;
				spResult.mpPair=new CPair;
				spResult.mpPair->mX=pA->mpPair->mX*(float)pB->mIntegerValue;
				spResult.mpPair->mY=pA->mpPair->mY*(float)pB->mIntegerValue;
				break;
			default:
				set_error("Pair cannot be multiplied by second arg");
				break;
			}	
			break;
		default:
			set_error("Multiplication not supported for this type");
			break;
		}	
		break;
	case ESCRIPTTOKEN_DIVIDE:
		if (pB->mType==ESYMBOLTYPE_INTEGER)
		{
			if (pB->mIntegerValue==0)
			{
				set_error("Integer division by zero");
				break;
			}
		}
		else if (pB->mType==ESYMBOLTYPE_FLOAT)
		{
			if (pB->mFloatValue==0.0f)
			{
				set_error("Float division by zero");
				break;
			}
		}
		
		switch (pA->mType)
		{
		case ESYMBOLTYPE_INTEGER:
			if (pB->mType==ESYMBOLTYPE_INTEGER)
			{
				spResult.mType=ESYMBOLTYPE_INTEGER;
				spResult.mIntegerValue=pA->mIntegerValue/pB->mIntegerValue;
			}
			else if (pB->mType==ESYMBOLTYPE_FLOAT)
			{
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=(float)pA->mIntegerValue/pB->mFloatValue;
			}
			else
			{
				set_error("Integer cannot be divided by second arg");
			}	
			break;
		case ESYMBOLTYPE_FLOAT:
			if (pB->mType==ESYMBOLTYPE_INTEGER)
			{
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=pA->mFloatValue/(float)pB->mIntegerValue;
			}
			else if (pB->mType==ESYMBOLTYPE_FLOAT)
			{
				spResult.mType=ESYMBOLTYPE_FLOAT;
				spResult.mFloatValue=pA->mFloatValue/pB->mFloatValue;
			}
			else
			{
				set_error("Float cannot be divided by second arg");
			}	
			break;
		case ESYMBOLTYPE_VECTOR:
			if (pB->mType==ESYMBOLTYPE_INTEGER)
			{
				spResult.mType=ESYMBOLTYPE_VECTOR;
				spResult.mpVector=new CVector;
				spResult.mpVector->mX=pA->mpVector->mX/(float)pB->mIntegerValue;
				spResult.mpVector->mY=pA->mpVector->mY/(float)pB->mIntegerValue;
				spResult.mpVector->mZ=pA->mpVector->mZ/(float)pB->mIntegerValue;
			}
			else if (pB->mType==ESYMBOLTYPE_FLOAT)
			{
				spResult.mType=ESYMBOLTYPE_VECTOR;
				spResult.mpVector=new CVector;
				spResult.mpVector->mX=pA->mpVector->mX/pB->mFloatValue;
				spResult.mpVector->mY=pA->mpVector->mY/pB->mFloatValue;
				spResult.mpVector->mZ=pA->mpVector->mZ/pB->mFloatValue;
			}
			else
			{
				set_error("Vector cannot be divided by second arg");
			}	
			break;
		case ESYMBOLTYPE_PAIR:
			if (pB->mType==ESYMBOLTYPE_INTEGER)
			{
				spResult.mType=ESYMBOLTYPE_PAIR;
				spResult.mpPair=new CPair;
				spResult.mpPair->mX=pA->mpPair->mX/(float)pB->mIntegerValue;
				spResult.mpPair->mY=pA->mpPair->mY/(float)pB->mIntegerValue;
			}
			else if (pB->mType==ESYMBOLTYPE_FLOAT)
			{
				spResult.mType=ESYMBOLTYPE_PAIR;
				spResult.mpPair=new CPair;
				spResult.mpPair->mX=pA->mpPair->mX/pB->mFloatValue;
				spResult.mpPair->mY=pA->mpPair->mY/pB->mFloatValue;
			}
			else
			{
				set_error("Pair cannot be divided by second arg");
			}	
			break;
		default:
			set_error("Division not supported for this type");
			break;
		}	
		break;
	case ESCRIPTTOKEN_DOT:
		switch (pA->mType)
		{
		case ESYMBOLTYPE_VECTOR:
			if (pB->mType!=ESYMBOLTYPE_VECTOR)
			{
				set_error("Vector can only be dot producted with another vector");
				break;
			}
			spResult.mType=ESYMBOLTYPE_FLOAT;
			spResult.mFloatValue=pA->mpVector->mX*pB->mpVector->mX+
								 pA->mpVector->mY*pB->mpVector->mY+
								 pA->mpVector->mZ*pB->mpVector->mZ;
			break;	
		case ESYMBOLTYPE_PAIR:
			if (pB->mType!=ESYMBOLTYPE_PAIR)
			{
				set_error("Pair can only be dot producted with another pair");
				break;
			}
			spResult.mType=ESYMBOLTYPE_FLOAT;
			spResult.mFloatValue=pA->mpPair->mX*pB->mpPair->mX+
								 pA->mpPair->mY*pB->mpPair->mY;
			break;	
		case ESYMBOLTYPE_STRUCTURE:
		{
			if (pB->mType!=ESYMBOLTYPE_NAME)
			{
				set_error("Structure dot operator requires 2nd arg to be a name");
				break;
			}
			Dbg_MsgAssert(pA->mpStructure,("NULL pA->mpStructure"));
			CComponent *p_found=pA->mpStructure->FindNamedComponentRecurse(pB->mChecksum);
			if (p_found)
			{
				CopyComponent(&spResult,p_found);
			}	
			break;
		}	
		default:
			set_error("Dot product not supported for this type");
			break;
		}
		break;
	case ESCRIPTTOKEN_STARTARRAY:
	{
		if (pA->mType!=ESYMBOLTYPE_ARRAY)
		{
			set_error("Array element operator not supported for this type");
			break;
		}
		Dbg_MsgAssert(pA->mpArray,("NULL pA->mpArray"));
		
		if (pB->mType!=ESYMBOLTYPE_INTEGER)
		{
			set_error("[] index must be an integer");
			break;
		}
		uint32 index=pB->mIntegerValue;
		
		if (index>=pA->mpArray->GetSize())
		{
			set_error("[] index out of range");
			break;
		}
		
		CopyArrayElementIntoComponent(pA->mpArray,index,&spResult);
		if (spResult.mType==ESYMBOLTYPE_NONE)
		{
			set_error("Array type cannot be accessed using [] yet ...");
			break;
		}		
				
		break;
	}	
	default:
		set_error("Operator not supported");
		break;
	}
				
	CleanUpComponent(pA);
	CleanUpComponent(pB);
	pA->mType=spResult.mType;
	pA->mUnion=spResult.mUnion;
	spResult.mType=ESYMBOLTYPE_NONE;
	spResult.mUnion=0;
	
	--m_value_stack_top;
	
	mp_operator_stack[m_operator_stack_top].mOperator=NOP;
	if (m_operator_stack_top)
	{
		--m_operator_stack_top;
	}
	else
	{
		m_got_operators=false;
	}	
	
	//printf("m_operator_stack_top=%d\n",m_operator_stack_top);
}

void CExpressionEvaluator::add_new_operator(EScriptToken op)
{
	++m_operator_stack_top;
	if (m_operator_stack_top>=OPERATOR_STACK_SIZE)
	{
		set_error("Operator stack overflow");
		return;
	}
		
	mp_operator_stack[m_operator_stack_top].mOperator=op;	
	mp_operator_stack[m_operator_stack_top].mParenthesesCount=0;
	
	m_got_operators=true;
}

void CExpressionEvaluator::Input(EScriptToken op)
{
	while (m_got_operators)
	{
		if (mp_operator_stack[m_operator_stack_top].mParenthesesCount)
		{
			// The most recent operator is 'protected' by parentheses, 
			// so do not execute it.
			break;
		}	
	
		if (sSameOrLowerPrecedence(op,mp_operator_stack[m_operator_stack_top].mOperator))
		{
			// The new operator has the same or lower precedence than the last, so execute the
			// last operator.
			// Ie, in 2*3+5, we execute the * once we get the +
			execute_operation();
		}
		else
		{
			break;
		}	
	}	
				
	// Insert the new operator.
	add_new_operator(op);
}

void CExpressionEvaluator::Input(const CComponent *p_value)
{
	if (m_value_stack_top==0 && mp_value_stack[0].mType==ESYMBOLTYPE_NONE)
	{
	}
	else
	{
		++m_value_stack_top;
		if (m_value_stack_top>=VALUE_STACK_SIZE)
		{
			set_error("Value stack overflow");
			return;
		}
	}
		
	Dbg_MsgAssert(p_value,("NULL p_value"));
	CopyComponent(&mp_value_stack[m_value_stack_top],p_value);
}

void CExpressionEvaluator::OpenParenthesis()
{
	++mp_operator_stack[m_operator_stack_top].mParenthesesCount;
}

void CExpressionEvaluator::CloseParenthesis()
{
	while (true)
	{
		if (mp_operator_stack[m_operator_stack_top].mParenthesesCount)
		{
			--mp_operator_stack[m_operator_stack_top].mParenthesesCount;
			break;
		}
		else
		{
			execute_operation();
			if (mp_error_string)
			{
				break;
			}	
		}	
	}	
}

bool CExpressionEvaluator::GetResult(CComponent *p_result)
{
	Dbg_MsgAssert(p_result,("NULL p_result"));
	Dbg_MsgAssert(p_result->mType==ESYMBOLTYPE_NONE,("Non-empty component sent to GetResult, type = %s",GetTypeName(p_result->mType)));
	Dbg_MsgAssert(p_result->mUnion==0,("CComponent::mUnion not zero, type = %s",GetTypeName(p_result->mType)));
	
	while (true)
	{
		if (mp_operator_stack[m_operator_stack_top].mParenthesesCount)
		{
			set_error("Not enough close parentheses");
			break;
		}
			
		if (m_value_stack_top==0)
		{
			if (mp_operator_stack[0].mOperator!=0 || m_operator_stack_top)
			{
				set_error("Too many operators in expression");
				break;
			}	
			CopyComponent(p_result,&mp_value_stack[0]);
			CleanUpComponent(&mp_value_stack[0]);
			return true;
		}			
		
		execute_operation();
		if (mp_error_string)
		{
			break;
		}	
	}
	return false;
}

const char *CExpressionEvaluator::GetError()
{
	return mp_error_string;
}

} // namespace Script



================================================
FILE: Code/Gel/Scripting/eval.h
================================================
#ifndef	__SCRIPTING_EVAL_H
#define	__SCRIPTING_EVAL_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef	__SCRIPTING_COMPONENT_H
#include 
#endif

#ifndef __SCRIPTING_TOKENS_H
#include 
#endif

namespace Script
{

#define EVALUATOR_STRING_BUF_SIZE 200

#define NOP ((EScriptToken)0)

#define VALUE_STACK_SIZE 20
#define OPERATOR_STACK_SIZE VALUE_STACK_SIZE

struct SOperator
{
	EScriptToken mOperator;
	int mParenthesesCount;
};
	
class CExpressionEvaluator : public Spt::Class
{
	CComponent mp_value_stack[VALUE_STACK_SIZE];
	int m_value_stack_top;
	
	SOperator mp_operator_stack[OPERATOR_STACK_SIZE];
	int m_operator_stack_top;
	bool m_got_operators;

	// mp_token points to the position in the qb file being parsed.
	// The expression evaluator itself never does any parsing of the qb data,
	// it only stores this so that it can print the line number if an error occurs.
	uint8 *mp_token;	
	const char *mp_error_string;
	
	// Sometimes, if an expression contains an error such as a missing parameter value, we
	// do not want it to assert or print a warning message, but just return a void value instead.
	// For example, when scanning through all the scripts looking for calls to EndGap, the
	// function AddComponentsUntilEndOfLine is used, but often parameters are missing then
	// because the script is not being run, just scanned through. Don't wan't it to stop with
	// an error in that case.
	bool m_errors_enabled;
	
	void set_error(const char *p_error);
	void execute_operation();
	void add_new_operator(EScriptToken op);
									 
public:	
	CExpressionEvaluator();
	~CExpressionEvaluator();
	
	void EnableErrorChecking() {m_errors_enabled=true;}
	void DisableErrorChecking() {m_errors_enabled=false;}
	bool ErrorCheckingEnabled() {return m_errors_enabled;}
							 
	// These are not defined, just declared so that the code won't link if they
	// are attempted to be used.
	CExpressionEvaluator( const CExpressionEvaluator& rhs );
	CExpressionEvaluator& operator=( const CExpressionEvaluator& rhs );

	void Clear();	
	void ClearIfNeeded();
	void Input(EScriptToken op);
	void Input(const CComponent *p_value);
	void OpenParenthesis();
	void CloseParenthesis();
	bool GetResult(CComponent *p_result);
	const char *GetError();
	void SetTokenPointer(uint8 *p_token);
};
	
} // namespace Script

#endif // #ifndef	__SCRIPTING_EVAL_H



================================================
FILE: Code/Gel/Scripting/file.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// file.cpp		KSH 31 Oct 2001
//
// Functions for loading and unloading qb files.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  // For Crc::GenerateCRCFromString
#include 

namespace Script
{
// TODO: Need another LoadQB in the game-specific script namespace, which will call this LoadQB
// and then do any game-specific stuff that needs to be done when a qb is reloaded, such as 
// generating the node name hash table & prefix info, reloading the skater exceptions, updating the decks on
// the skateshop wall, etc...

void restart_dirty_scripts()
{
	// For each symbol, check if it got reloaded by the above load, and if it did and it is a script
	// then run through all the existing CScript's restarting those that referred to it.
	// This is essential, because otherwise those CScripts' program counter pointers will now be invalid.
	CSymbolTableEntry *p_sym=GetNextSymbolTableEntry();
	while (p_sym)
	{
		if (p_sym->mGotReloaded && p_sym->mType==ESYMBOLTYPE_QSCRIPT)
		{
			CScript *p_script=GetNextScript();
			while (p_script)
			{
				if (p_script->RefersToScript(p_sym->mNameChecksum))
				{
					p_script->Restart();
				}
			
				p_script=GetNextScript(p_script);
			}	
			
			// Reset the reloaded flag, otherwise it will stick on forever.
			p_sym->mGotReloaded=false;
		}
		p_sym=GetNextSymbolTableEntry(p_sym);
	}		
}

// Loads a QB file.
// It will open the file, load it into memory and parse it, creating all the
// symbols (scripts, arrays, integers etc) defined within in.
void LoadQB(const char *p_fileName, EBoolAssertIfDuplicateSymbols assertIfDuplicateSymbols)
{
	Dbg_MsgAssert(p_fileName,("NULL p_fileName"));
#ifndef __PLAT_NGC__
	Dbg_MsgAssert(strcmp(p_fileName+strlen(p_fileName)-3,".qb")==0,("File does not have extension .qb. File %s",p_fileName));
#endif __PLAT_NGC__

	// Mick - Pip::Load is not going to load it from a Pip::Pre, just a regular pre
	// so I'm sticking it on the top-down heap to avoid fragmentation							  
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	uint8 *p_qb=(uint8*)Pip::Load(p_fileName);
	Mem::Manager::sHandle().PopContext();
		
	// Parse the QB, which creates all the symbols defined within it.
	ParseQB(p_fileName,p_qb,assertIfDuplicateSymbols);
	
	Pip::Unload(p_fileName);	

	restart_dirty_scripts();
}

// Loads a QB file from memory
void LoadQBFromMemory(const char* p_fileName, uint8* p_qb, EBoolAssertIfDuplicateSymbols assertIfDuplicateSymbols)
{
	// even though there's no actually filename,
	// we'd still need a dummy string, which will
	// be used for printing up Assert messages...

	Dbg_MsgAssert(p_fileName,("NULL p_fileName"));
#ifndef __PLAT_NGC__
	Dbg_MsgAssert(strcmp(p_fileName+strlen(p_fileName)-3,".qb")==0,("File does not have extension .qb. File %s",p_fileName));
#endif __PLAT_NGC__

	// do not create the hash table if it's not
	// the node array (it's a 50K memory hog on
	// the script heap, and it's really only
	// needed for doing prefix stuff anyway)
	ParseQB(p_fileName,p_qb,assertIfDuplicateSymbols,false);

	RemoveChecksumNameLookupHashTable();

	restart_dirty_scripts();
}

// TODO: Need another UnloadQB in the game-specific script namespace, which will call this UnloadQB
// and then do any game-specific stuff that needs to be done when a qb is unloaded, such as 
// resetting the node name hash table & prefix info.

// Deletes all the symbols that were defined in the passed QB file.
void UnloadQB(uint32 fileNameChecksum)
{
	// Do nothing if the checksum is zero. This is essential, because symbols
	// that have a mSourceFileNameChecksum of 0 are those that did not come
	// from a qb file, ie C-functions. These must never be deleted.
	if (!fileNameChecksum)
	{
		return;
	}
		
	// Scan through all the symbols in the symbol table.
	CSymbolTableEntry *p_sym=GetNextSymbolTableEntry();
	while (p_sym)
	{											
		if (p_sym->mSourceFileNameChecksum==fileNameChecksum)
		{
			// This symbol was defined in the passed qb file, so remove it.
			CleanUpAndRemoveSymbol(p_sym);
			// Need to start checking from the start of the list again rather than storing
			// a p_next pointer before calling CleanUpAndRemoveSymbol.
			// This is because CleanUpAndRemoveSymbol may cause the symbol above to move down,
			// invalidating p_next.
			// SPEEDOPT Starting again from the start of the list is inefficient though, because
			// it may have to scan through thousands of symbols just to delete one, and may have to
			// delete say a hundred. Optimize later. (Seems fast enough at the moment)
			p_sym=NULL;
		}	
		p_sym=GetNextSymbolTableEntry(p_sym);
	}
	
	// Scan through all the existing CScripts stopping any that are referring to a script
	// that got deleted above.
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		if (!p_script->AllScriptsInCallstackStillExist())
		{
			p_script->ClearScript();
			p_script->ClearEventHandlerTable();
		}
	
		p_script=GetNextScript(p_script);
	}	
}

// Note: There is no Script::UnloadQB(const char *p_fileName)
// This is because we may not want to do a GenerateCRCFromString on the p_fileName as it stands.
// We may want to make sure the file name is prefixed with the complete path before calculating
// the crc.
// Since the path is game specific, the code to do this is in the SkateScript namespace.
// See SkateScript::UnloadQB(const char *p_fileName) and SkateScript::GenerateFileNameChecksum

} // namespace Script



================================================
FILE: Code/Gel/Scripting/file.h
================================================
#ifndef	__SCRIPTING_FILE_H
#define	__SCRIPTING_FILE_H

#ifndef	__SCRIPTING_SCRIPTDEFS_H
#include  // For enums
#endif

namespace Script
{
	
void LoadQB(const char *p_fileName, 
			EBoolAssertIfDuplicateSymbols assertIfDuplicateSymbols=NO_ASSERT_IF_DUPLICATE_SYMBOLS);
void LoadQBFromMemory(const char* p_fileName, uint8* p_qb, EBoolAssertIfDuplicateSymbols assertIfDuplicateSymbols);
void UnloadQB(uint32 fileNameChecksum);

} // namespace Script

#endif // #ifndef	__SCRIPTING_FILE_H



================================================
FILE: Code/Gel/Scripting/init.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// init.cpp		KSH 19 Oct 2001
//
// Functions for initialising various script stuff.
// 
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Script
{

// MEMOPT This is where all the pools of script things get allocated.
void AllocatePools()
{
	// Sets up the checksum name-lookup hash table.
	// On the PS2 this does not actually do any dynamic allocation, but uses the space set aside
	// in NGPS.lk and defined by _script_start and _script_end
	// On other platforms it does dynamically allocate the space.
	// The function will not do anything if Config::GotExtraMemory() returns false.
	Mem::PushMemProfile("AllocateChecksumNameLookupTables");
	AllocateChecksumNameLookupTables();
	Mem::PopMemProfile();
	
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());

	#ifndef __PLAT_WN32__
	
	#ifdef NO_SCRIPT_CACHING
	#else
	Mem::PushMemProfile("CScriptCacheEntry");
	// 32ish bytes each
	CScriptCacheEntry::SCreatePool(MAX_DECOMPRESSED_SCRIPTS, "CScriptCacheEntry");
	Mem::PopMemProfile();
	#endif
		
	Mem::PushMemProfile("CComponent and Reserve CComponent");
	
	// 16 bytes each
	CComponent::SCreatePool(84000, "CComponent");  // Mick:  increased by 2000 (82000 to 84000) to account for gap lists

	Mem::PopMemProfile();
	

	Mem::PushMemProfile("CStruct");
	// 4 bytes each  (Actually 8, or 12 with asserts)
	CStruct::SCreatePool(18800, "CStruct");
	Mem::PopMemProfile();
	
	// 12 bytes each  (Actually 16)
	Mem::PushMemProfile("CVector");
	CVector::SCreatePool(9600, "CVector");
	Mem::PopMemProfile();
	
	// 8 bytes each	  (Actually 12)
	Mem::PushMemProfile("CPair");
	CPair::SCreatePool(1000, "CPair");	 // Mick: was 5000, but only used 308 at last count
	Mem::PopMemProfile();
	
	// 12 bytes each
	Mem::PushMemProfile("CArray");
	//CArray::SCreatePool(6000, "CArray");
	CArray::SCreatePool(7500, "CArray");
	Mem::PopMemProfile();
	
	
	// 12 bytes each (actually 16)
	Mem::PushMemProfile("CSymbolTableEntry");
	CSymbolTableEntry::SCreatePool(8500, "CSymbolTableEntry");
	Mem::PopMemProfile();

	Mem::PushMemProfile("CScript");
	// 1080 bytes each, bloody hell (Actually ~1238!!!)
	CScript::SCreatePool(MAX_CSCRIPTS, "CScript");
	Mem::PopMemProfile();

	Mem::PushMemProfile("CStoredRandom");
	// 80 bytes each (100)
	CStoredRandom::SCreatePool(MAX_STORED_RANDOMS,"CStoredRandom");
	Mem::PopMemProfile();
	#endif


	// This will create a further 4096 CSymbolTableEntry's, but as a contiguous array.
//	Mem::PushMemProfile("CreateSymbolHashTable");
//	CreateSymbolHashTable();
//	Mem::PopMemProfile();
	
	// 60500 bytes was needed for foreign languages on THPS3, 48000 for English.
	// 4000 is the max number of strings.
	//AllocatePermanentStringHeap(60500,4000);
	Mem::PushMemProfile("AllocatePermanentStringHeap");
	AllocatePermanentStringHeap(160000,7000);	
	Mem::PopMemProfile();
	
	Mem::Manager::sHandle().PopContext();
}

void DeallocatePools()
{
	DeallocatePermanentStringHeap();
	DestroySymbolHashTable();
	
	#ifndef __PLAT_WN32__
	CScript::SRemovePool();
	CSymbolTableEntry::SRemovePool();
	CArray::SRemovePool();
	CStruct::SRemovePool();
	CPair::SRemovePool();
	CVector::SRemovePool();
	CComponent::SRemovePool();
	#endif
		
	DeallocateChecksumNameLookupTables();
}

// Adds all the c-functions listed in the passed array to the symbol table.
void RegisterCFunctions(SCFunction *p_cFunctions, uint32 numFunctions)
{
	Dbg_MsgAssert(p_cFunctions,("NULL p_cFunctions"));
	
	for (uint32 i=0; imType==ESYMBOLTYPE_CFUNCTION || p_existing_entry->mType==ESYMBOLTYPE_MEMBERFUNCTION ? "ftables.cpp":FindChecksumName(p_existing_entry->mSourceFileNameChecksum),
					  GetTypeName(p_existing_entry->mType)));
		#endif		
		
		CSymbolTableEntry *p_new=CreateNewSymbolEntry(name_checksum);
		Dbg_MsgAssert(p_new,("NULL p_new ??"));
	
		p_new->mType=ESYMBOLTYPE_CFUNCTION;
		p_new->mpCFunction=p_cFunctions[i].mpFunction;
		// Note: Not setting p_new->mSourceFileNameChecksum to anything, just leaving it as 0, since there is no
		// source qb file.
	}
}

// Adds all the member functions listed in the passed array to the symbol table.
void RegisterMemberFunctions(const char **pp_memberFunctions, uint32 numFunctions)
{
	Dbg_MsgAssert(pp_memberFunctions,("NULL pp_memberFunctions"));
	
	for (uint32 i=0; imType==ESYMBOLTYPE_CFUNCTION || p_existing_entry->mType==ESYMBOLTYPE_MEMBERFUNCTION ? "ftables.cpp":FindChecksumName(p_existing_entry->mSourceFileNameChecksum),
					  GetTypeName(p_existing_entry->mType)));
		#endif
		
		CSymbolTableEntry *p_new=CreateNewSymbolEntry(name_checksum);
		Dbg_MsgAssert(p_new,("NULL p_new ??"));
	
		p_new->mType=ESYMBOLTYPE_MEMBERFUNCTION;
		// No need to set the value to anything, since the name of the symbol specifies the member function.
		
		// Note: Not setting p_new->mSourceFileNameChecksum to anything, just leaving it as 0, since there is no
		// source qb file.
	}
}

} // namespace Script


================================================
FILE: Code/Gel/Scripting/init.h
================================================
#ifndef	__SCRIPTING_INIT_H
#define	__SCRIPTING_INIT_H

#ifndef __CORE_DEFINES_H
#include 
#endif

namespace Script
{

enum
{
	MAX_CSCRIPTS=300, // If this changes, the script debugger (qdebug.exe) will need to be rebuilt.
};
	
class CStruct;
class CScript;
struct SCFunction
{
    const char *mpName;
    bool (*mpFunction)(Script::CStruct *, Script::CScript *);
};

void AllocatePools();
void DeallocatePools();
void RegisterCFunctions(SCFunction *p_cFunctions, uint32 numFunctions);
void RegisterMemberFunctions(const char **pp_memberFunctions, uint32 numFunctions);

} // namespace Script

#endif // #ifndef __SCRIPTING_INIT_H


================================================
FILE: Code/Gel/Scripting/parse.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// parse.cpp		KSH 23 Oct 2001
//
// Functions for parsing qb files.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  // For Mth::Rnd and Mth::Rnd2
#include  // For Crc::GenerateCRCFromString
#include 

#ifdef __PLAT_NGC__
#include 
#include 
#endif		// __PLAT_NGC__

DefinePoolableClass(Script::CStoredRandom);

namespace Script
{

class CScript;
// Declaring this seperately rather than including script.h to avoid a cyclic dependency.
// The full class declaration of CScript is not required.
extern CScript *GetCurrentScript();

static uint8 *sCalculateRandomRange(uint8 *p_token, CComponent *p_comp, bool useRnd2);
static uint8 *sSkipType(uint8 *p_token);
static bool   sIsEndOfLine(const uint8 *p_token);
static uint8 *sInitArrayFromQB(CArray *p_dest, uint8 *p_token, CStruct *p_args=NULL);
static uint8 *sAddComponentFromQB(CStruct *p_dest, uint32 nameChecksum, uint8 *p_token, CStruct *p_args=NULL);
static uint8 *sAddComponentsWithinCurlyBraces(CStruct *p_dest, uint8 *p_token, CStruct *p_args=NULL);
static CSymbolTableEntry *sCreateScriptSymbol(uint32 nameChecksum, uint32 contentsChecksum, const uint8 *p_data, uint32 size, const char *p_fileName);
static uint8 *sCreateSymbolOfTheFormNameEqualsValue(uint8 *p_token, const char *p_fileName, EBoolAssertIfDuplicateSymbols assertIfDuplicateSymbols);
static CStoredRandom *sFindStoredRandom(const uint8 *p_token, EScriptToken type, int numItems);
static CStoredRandom *sCreateNewStoredRandom();

static CStoredRandom *sp_first_stored_random=NULL;
static CStoredRandom *sp_last_stored_random=NULL;
static int s_num_stored_randoms=0;

static uint32 s_qb_being_parsed=0;

// The SkipToken function is in skiptoken.cpp, so that it can also be included in PC code,
// such as qcomp.
#include 

#ifdef __NOPT_ASSERT__
void CheckForPossibleInfiniteLoops(uint32 scriptName, uint8 *p_token, const char *p_fileName)
{
	#define MAX_NESTED_LOOPS 100
	bool loop_is_ok[MAX_NESTED_LOOPS];
	int loop_index=0;
	for (int i=0; i=0)
				{
					loop_is_ok[i]=true;
					--i;
				}	
				p_token=SkipToken(p_token);
				break;
			}	
			case ESCRIPTTOKEN_KEYWORD_REPEAT:
				p_token=SkipToken(p_token);
				
				Dbg_MsgAssert(loop_index,("Zero loop_index"));
				if (*p_token==ESCRIPTTOKEN_ENDOFLINE || *p_token==ESCRIPTTOKEN_ENDOFLINENUMBER)
				{
					if (!loop_is_ok[loop_index-1])
					{
						bool allow=false;
						if (p_ok_scripts)
						{
							for (uint32 i=0; iGetSize(); ++i)
							{
								if (p_ok_scripts->GetChecksum(i)==scriptName)
								{
									allow=true;
								}
							}
						}
						
						if (allow)
						{
						}
						else
						{
							Dbg_MsgAssert(0, ("Warning! Possible infinite loop: Line %d of %s\n",Script::GetLineNumber(p_token),p_fileName));
						}	
					}
				}
				else
				{
					// If repeat is not followed by an end-of-line then it is probably followed by some
					// count value, so it is not an infinite loop.
				}
				
				--loop_index;	
				break;

			case ESCRIPTTOKEN_NAME:
			{
				++p_token;
				uint32 name=Read4Bytes(p_token).mChecksum;
				p_token+=4;
				
				if (p_blocking_functions)
				{
					uint32 *p_function_names=p_blocking_functions->GetArrayPointer();
					uint32 size=p_blocking_functions->GetSize();
					
					for (uint32 i=0; i=0)
								{
									loop_is_ok[i]=true;
									--i;
								}	
							}	
							break;
						}
					}		
				}
				break;
			}	
				
			default:
				p_token=SkipToken(p_token);
				break;
		}
	}	
}
#endif

// Used for evaluating the ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE operator.
static uint8 *sCalculateRandomRange(uint8 *p_token, CComponent *p_comp, bool useRnd2)
{
	// RandomRange token must be followed by a pair.
	Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_PAIR,("RandomRange operator must be followed by a pair of values in parentheses, File %s, line %d",GetSourceFile(p_token),GetLineNumber(p_token)));
	++p_token;
	// Get the range values, which are floats cos it's a pair.
	float x=Read4Bytes(p_token).mFloat;
	p_token+=4;
	float y=Read4Bytes(p_token).mFloat;
	p_token+=4;
	
	// Get the integer values.
	// Offset the floats up or down by 0.01 to ensure they round down to the expected integer values.
	int a, b;
	a=(int)( (x<0) ? x-0.1f:x+0.1f);
	b=(int)( (y<0) ? y-0.1f:y+0.1f);
	
	bool they_are_integers=true;
	if (fabs(x-a) > 0.00001f || fabs(y-b) > 0.00001f)
	{
		they_are_integers=false;
	}	
	
	if (they_are_integers)
	{
		// Make sure that a<=b
		if (a>b)
		{
			// Swap them.
			int temp=a;
			a=b;
			b=temp;
		}	
		// Get val, which is between a and b inclusive.
		// This assert is to make sure the calculations in Rnd don't overflow.
		Dbg_MsgAssert(b-a+1<=32767,("File %s, line %d: RandomRange limits are too far apart, max is 32767",GetSourceFile(p_token),GetLineNumber(p_token)));
		int val;
		if (useRnd2)
		{
			val=a+Mth::Rnd2(b-a+1);
		}
		else
		{
			val=a+Mth::Rnd(b-a+1);
		}	
		// Make totally sure the value is OK, cos could lead to hard-to-track-down bugs otherwise.
//		Dbg_MsgAssert(val>=a && val<=b,("File %s, line %d: Internal error in RandomRange (%f %f %f), fire Ken.",GetSourceFile(p_token),GetLineNumber(p_token),x,y,val));
		
		p_comp->mType=ESYMBOLTYPE_INTEGER;
		p_comp->mIntegerValue=val;
	}
	else
	{
		// Make sure that x<=y
		if (x>y)
		{
			// Swap them.
			float temp=x;
			x=y;
			y=temp;
		}	
		// Get val, which is between x and y inclusive.
		float val;
		// Don't make FLOAT_RES bigger than 32766 because the calculations in Rnd will overflow otherwise.
		#define FLOAT_RES 32760.0f
		if (useRnd2)
		{
			val=x+((float)Mth::Rnd2((int)(FLOAT_RES+1.0f))*(y-x))/FLOAT_RES;
		}
		else
		{
			val=x+((float)Mth::Rnd((int)(FLOAT_RES+1.0f))*(y-x))/FLOAT_RES;
		}	
		
		// Make totally sure the value is OK, cos could lead to hard-to-track-down bugs otherwise.
//		Dbg_MsgAssert(val>=x && val<=y,("File %s, line %d: Internal error in RandomRange (%f %f %f), fire Ken.",GetSourceFile(p_token),GetLineNumber(p_token),x,y,val));
		
		p_comp->mType=ESYMBOLTYPE_FLOAT;
		p_comp->mFloatValue=val;
	}
	
	return p_token;
}

// Skips over a data type token. Eg, if p_token points to a structure, it will
// skip over the whole structure.
static uint8 *sSkipType(uint8 *p_token)
{
    switch (*p_token)
    {
        case ESCRIPTTOKEN_NAME:
        case ESCRIPTTOKEN_INTEGER:
        case ESCRIPTTOKEN_HEXINTEGER:
        case ESCRIPTTOKEN_FLOAT:
            p_token+=1+4;
            break;
        case ESCRIPTTOKEN_PAIR:
            p_token+=1+2*4;
            break;
        case ESCRIPTTOKEN_VECTOR:
            p_token+=1+3*4;
            break;
        case ESCRIPTTOKEN_STRING:
        case ESCRIPTTOKEN_LOCALSTRING:
            p_token+=1+4+Read4Bytes(p_token+1).mUInt;
            break;
        case ESCRIPTTOKEN_STARTSTRUCT:
		{
            int StructCount=1;
            while (StructCount)
            {
                p_token=SkipToken(p_token);
                if (*p_token==ESCRIPTTOKEN_STARTSTRUCT) 
				{
					++StructCount;
				}	
                else if (*p_token==ESCRIPTTOKEN_ENDSTRUCT)
				{
                    --StructCount;
				}	
            }
            ++p_token;
            break;
		}	
        case ESCRIPTTOKEN_STARTARRAY:
		{
            int ArrayCount=1;
            while (ArrayCount)
            {
                p_token=SkipToken(p_token);
                if (*p_token==ESCRIPTTOKEN_STARTARRAY)
				{
                    ++ArrayCount;
				}	
                else if (*p_token==ESCRIPTTOKEN_ENDARRAY)
				{
                    --ArrayCount;
				}	
            }
            ++p_token;
            break;
		}	
        default:
            Dbg_MsgAssert(0,("Unrecognized type"));
            break;
    }
    return p_token;
}

// Returns true if pToken points to and end-of-line, end-of-line-number or end-of-file token.
static bool sIsEndOfLine(const uint8 *p_token)
{
	Dbg_MsgAssert(p_token,("NULL p_token"));
    return (*p_token==ESCRIPTTOKEN_ENDOFLINE || *p_token==ESCRIPTTOKEN_ENDOFLINENUMBER || *p_token==ESCRIPTTOKEN_ENDOFFILE);
}

// Given that p_token points to a ESCRIPTTOKEN_STARTARRAY token, this will parse the
// array tokens adding the elements to the CArray.
// Gives error messages if all the elements are not of the same type.
static uint8 *sInitArrayFromQB(CArray *p_dest, uint8 *p_token, CStruct *p_args)
{
	Dbg_MsgAssert(p_dest,("NULL p_dest"));
	Dbg_MsgAssert(p_token,("NULL p_token"));
    Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_STARTARRAY,("p_token does not point to an array"));
	
	// Remember the start, since we're going to do a first pass through to determine the array type and size.
	uint8 *p_start=p_token;	
    
	// Skip over the startarray token.
    ++p_token;
	// Execute any random operators.
	p_token=DoAnyRandomsOrJumps(p_token);

	ESymbolType type=ESYMBOLTYPE_NONE;
	uint32 size=0;
	
    while (*p_token!=ESCRIPTTOKEN_ENDARRAY)
    {
		// Determine type.
        switch (*p_token)
        {
            case ESCRIPTTOKEN_ENDOFLINE:
            case ESCRIPTTOKEN_ENDOFLINENUMBER:
            case ESCRIPTTOKEN_COMMA:
                break;
            case ESCRIPTTOKEN_NAME:
                type=ESYMBOLTYPE_NAME;
                break;
            case ESCRIPTTOKEN_INTEGER:
            case ESCRIPTTOKEN_HEXINTEGER:
				// Integers don't override floats.
                if (type!=ESYMBOLTYPE_FLOAT) 
				{
					type=ESYMBOLTYPE_INTEGER;
				}	
                break;
            case ESCRIPTTOKEN_FLOAT:
                type=ESYMBOLTYPE_FLOAT;
                break;
            case ESCRIPTTOKEN_PAIR:
                type=ESYMBOLTYPE_PAIR;
                break;
            case ESCRIPTTOKEN_VECTOR:
                type=ESYMBOLTYPE_VECTOR;
                break;
	        case ESCRIPTTOKEN_STRING:
                type=ESYMBOLTYPE_STRING;
                break;
            case ESCRIPTTOKEN_LOCALSTRING:
                type=ESYMBOLTYPE_LOCALSTRING;
                break;
            case ESCRIPTTOKEN_STARTSTRUCT:
                type=ESYMBOLTYPE_STRUCTURE;
                break;
            case ESCRIPTTOKEN_STARTARRAY:
                type=ESYMBOLTYPE_ARRAY;
                break;
            default:
                Dbg_MsgAssert(0,("Unrecognized data type in array, File %s, line %d\n",GetSourceFile(p_token),GetLineNumber(p_token)));
                break;
        }
		
		// Update the size and advance p_token.
        switch (*p_token)
        {
            case ESCRIPTTOKEN_ENDOFLINE:
            case ESCRIPTTOKEN_ENDOFLINENUMBER:
            case ESCRIPTTOKEN_COMMA:
                p_token=SkipToken(p_token);
                break;
            case ESCRIPTTOKEN_NAME:
            case ESCRIPTTOKEN_INTEGER:
            case ESCRIPTTOKEN_HEXINTEGER:
            case ESCRIPTTOKEN_FLOAT:
            case ESCRIPTTOKEN_PAIR:
            case ESCRIPTTOKEN_VECTOR:
	        case ESCRIPTTOKEN_STRING:
            case ESCRIPTTOKEN_LOCALSTRING:
            case ESCRIPTTOKEN_STARTSTRUCT:
            case ESCRIPTTOKEN_STARTARRAY:
                ++size;
                p_token=sSkipType(p_token);
                break;
            default:
                Dbg_MsgAssert(0,("Unrecognized data type in array, File %s, line %d\n",GetSourceFile(p_token),GetLineNumber(p_token)));
                break;
        }
		
		// Execute any random operators. This has to be done each time p_token is advanced.
		p_token=DoAnyRandomsOrJumps(p_token);
    }

	if (type==ESYMBOLTYPE_NONE)
	{
		// Skip over the ESCRIPTTOKEN_ENDARRAY
		++p_token;
		// Finished, empty array.
		return p_token;
	}

	// Rewind.										  
	p_token=p_start;	
	
	// Now that the array size and type are known, set up the CArray and fill it in.
	
	// Make sure we're using the script heap, because the CArray is not hard-wired to
	// allocate it's buffer off it.
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	p_dest->SetSizeAndType(size,type);
	Mem::Manager::sHandle().PopContext();

	// Just to be totally sure, cos we're about to write into it ...
	Dbg_MsgAssert(p_dest->GetArrayPointer(),("NULL array pinter ???"));
	
	#ifdef __NOPT_ASSERT__
	int size_check=size;
	#endif
		
    switch (type)
    {
		case ESYMBOLTYPE_INTEGER:
		{
			int *p_int=(int*)p_dest->GetArrayPointer();
			
			// Skip over the ESCRIPTTOKEN_STARTARRAY
			++p_token;
			// Execute any random operators. This has to be done each time p_token is advanced.
			p_token=DoAnyRandomsOrJumps(p_token);
			while (*p_token!=ESCRIPTTOKEN_ENDARRAY)
			{
				switch (*p_token++)
				{
					case ESCRIPTTOKEN_ENDOFLINE:
					case ESCRIPTTOKEN_COMMA:
						break;
					case ESCRIPTTOKEN_ENDOFLINENUMBER:
						p_token+=4;
						break;
					case ESCRIPTTOKEN_INTEGER:
					case ESCRIPTTOKEN_HEXINTEGER:
						#ifdef __NOPT_ASSERT__
						Dbg_MsgAssert(size_check,("Array size mismatch"));
						--size_check;
						#endif
						
						*p_int++=Read4Bytes(p_token).mInt;
						p_token+=4;
						break;
					default:
						Dbg_MsgAssert(0,("Array elements must be of the same type, File %s, line %d\n",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
						break;
				}
				// Execute any random operators. This has to be done each time p_token is advanced.
				p_token=DoAnyRandomsOrJumps(p_token);
			}    
			#ifdef __NOPT_ASSERT__
			Dbg_MsgAssert(size_check==0,("Array size mismatch"));
			#endif
			// Skip over the ESCRIPTTOKEN_ENDARRAY                                     
			++p_token;
			break;
		}    
		case ESYMBOLTYPE_FLOAT:
		{
			float *p_float=(float*)p_dest->GetArrayPointer();
			
			// Skip over the ESCRIPTTOKEN_STARTARRAY
			++p_token;
			p_token=DoAnyRandomsOrJumps(p_token);
			while (*p_token!=ESCRIPTTOKEN_ENDARRAY)
			{
				switch (*p_token++)
				{
					case ESCRIPTTOKEN_ENDOFLINE:
					case ESCRIPTTOKEN_COMMA:
						break;
					case ESCRIPTTOKEN_ENDOFLINENUMBER:
						p_token+=4;
						break;
					case ESCRIPTTOKEN_INTEGER:
					case ESCRIPTTOKEN_HEXINTEGER:
						#ifdef __NOPT_ASSERT__
						Dbg_MsgAssert(size_check,("Array size mismatch"));
						--size_check;
						#endif
						
						*p_float++=Read4Bytes(p_token).mInt;
						p_token+=4;
						break;
					case ESCRIPTTOKEN_FLOAT:
						#ifdef __NOPT_ASSERT__
						Dbg_MsgAssert(size_check,("Array size mismatch"));
						--size_check;
						#endif
						
						*p_float++=Read4Bytes(p_token).mFloat;
						p_token+=4;
						break;
					default:
						Dbg_MsgAssert(0,("Array elements must be of the same type, File %s, line %d\n",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
						break;
				}
				// Execute any random operators. This has to be done each time p_token is advanced.
				p_token=DoAnyRandomsOrJumps(p_token);
			}    
			#ifdef __NOPT_ASSERT__
			Dbg_MsgAssert(size_check==0,("Array size mismatch"));
			#endif
			// Skip over the ESCRIPTTOKEN_ENDARRAY                                     
			++p_token;
			break;
		}    
		case ESYMBOLTYPE_NAME:
		{
			uint32 *p_checksum=(uint32*)p_dest->GetArrayPointer();
			
			// Skip over the ESCRIPTTOKEN_STARTARRAY
			++p_token;
			p_token=DoAnyRandomsOrJumps(p_token);
			while (*p_token!=ESCRIPTTOKEN_ENDARRAY)
			{
				switch (*p_token++)
				{
					case ESCRIPTTOKEN_ENDOFLINE:
					case ESCRIPTTOKEN_COMMA:
						break;
					case ESCRIPTTOKEN_ENDOFLINENUMBER:
						p_token+=4;
						break;
					case ESCRIPTTOKEN_NAME:
						#ifdef __NOPT_ASSERT__
						Dbg_MsgAssert(size_check,("Array size mismatch"));
						--size_check;
						#endif
						
						*p_checksum++=Read4Bytes(p_token).mChecksum;
						p_token+=4;
						break;
					default:
						Dbg_MsgAssert(0,("Array elements must be of the same type, File %s, line %d\n",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
						break;
				}
				// Execute any random operators. This has to be done each time p_token is advanced.
				p_token=DoAnyRandomsOrJumps(p_token);
			}    
			#ifdef __NOPT_ASSERT__
			Dbg_MsgAssert(size_check==0,("Array size mismatch"));
			#endif
			// Skip over the ESCRIPTTOKEN_ENDARRAY                                     
			++p_token;
			break;
		}    
		case ESYMBOLTYPE_STRUCTURE:
		{
			CStruct **pp_structures=(CStruct**)p_dest->GetArrayPointer();

			// For finding out which node in Chad's qn is causing a syntax error
			//int index=0; // REMOVE!
						
			// Skip over the ESCRIPTTOKEN_STARTARRAY
			++p_token;
			p_token=DoAnyRandomsOrJumps(p_token);
			while (*p_token!=ESCRIPTTOKEN_ENDARRAY)
			{
				switch (*p_token)
				{
					case ESCRIPTTOKEN_ENDOFLINE:
					case ESCRIPTTOKEN_COMMA:
						++p_token;
						break;
					case ESCRIPTTOKEN_ENDOFLINENUMBER:
						p_token+=5;
						break;
					case ESCRIPTTOKEN_STARTSTRUCT:
					{
						#ifdef __NOPT_ASSERT__
						Dbg_MsgAssert(size_check,("Array size mismatch"));
						--size_check;
						#endif
						
						// Each structure is individually allocated.
						CStruct *p_struct=new CStruct;
						p_token=sAddComponentsWithinCurlyBraces(p_struct,p_token,p_args);
						
						*pp_structures++=p_struct;
						
						//printf("Created array structure %d\n",index++); // REMOVE!
						
						break;
					}    
					default:
						Dbg_MsgAssert(0,("Array elements must be of the same type, File %s, line %d\n",GetSourceFile(p_token),GetLineNumber(p_token)));
						break;
				}
				// Execute any random operators. This has to be done each time p_token is advanced.
				p_token=DoAnyRandomsOrJumps(p_token);
			}    
			#ifdef __NOPT_ASSERT__
			Dbg_MsgAssert(size_check==0,("Array size mismatch"));
			#endif
			// Skip over the ESCRIPTTOKEN_ENDARRAY                                     
			++p_token;
			break;
		}    
		case ESYMBOLTYPE_ARRAY:
		{
			CArray **pp_arrays=(CArray**)p_dest->GetArrayPointer();
			
			// Skip over the ESCRIPTTOKEN_STARTARRAY
			++p_token;
			p_token=DoAnyRandomsOrJumps(p_token);
			while (*p_token!=ESCRIPTTOKEN_ENDARRAY)
			{
				switch (*p_token++)
				{
					case ESCRIPTTOKEN_ENDOFLINE:
					case ESCRIPTTOKEN_COMMA:
						break;
					case ESCRIPTTOKEN_ENDOFLINENUMBER:
						p_token+=4;
						break;
					case ESCRIPTTOKEN_STARTARRAY:
					{
						#ifdef __NOPT_ASSERT__
						Dbg_MsgAssert(size_check,("Array size mismatch"));
						--size_check;
						#endif
						
						// Each array is individually allocated.											
						CArray *p_array=new CArray;
						// The -1 is to rewind p_token back to pointing to ESCRIPTTOKEN_STARTARRAY,
						// which sInitArrayFromQB requires.
						p_token=sInitArrayFromQB(p_array,p_token-1,p_args);
							 
						*pp_arrays++=p_array;
						break;
					}	
					default:
						Dbg_MsgAssert(0,("Array elements must be of the same type, File %s, line %d\n",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
						break;
				}
				// Execute any random operators. This has to be done each time p_token is advanced.
				p_token=DoAnyRandomsOrJumps(p_token);
			}    
			#ifdef __NOPT_ASSERT__
			Dbg_MsgAssert(size_check==0,("Array size mismatch"));
			#endif
			// Skip over the ESCRIPTTOKEN_ENDARRAY                                     
			++p_token;
			break;
		}    

		case ESYMBOLTYPE_STRING:
		{
			char **pp_strings=(char**)p_dest->GetArrayPointer();
			
			// Skip over the ESCRIPTTOKEN_STARTARRAY
			++p_token;
			p_token=DoAnyRandomsOrJumps(p_token);
			while (*p_token!=ESCRIPTTOKEN_ENDARRAY)
			{
				switch (*p_token++)
				{
					case ESCRIPTTOKEN_ENDOFLINE:
					case ESCRIPTTOKEN_COMMA:
						break;
					case ESCRIPTTOKEN_ENDOFLINENUMBER:
						p_token+=4;
						break;
					case ESCRIPTTOKEN_STRING:
					{
						#ifdef __NOPT_ASSERT__
						Dbg_MsgAssert(size_check,("Array size mismatch"));
						--size_check;
						#endif

						int string_length=Read4Bytes(p_token).mInt;
						Dbg_MsgAssert(string_length,("Zero string_length?"));
						p_token+=4;
						
						*pp_strings++=CreateString((const char *)p_token);
						p_token+=string_length;
						break;
					}	
					default:
						Dbg_MsgAssert(0,("Array elements must be of the same type, File %s, line %d\n",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
						break;
				}
				// Execute any random operators. This has to be done each time p_token is advanced.
				p_token=DoAnyRandomsOrJumps(p_token);
			}    
			#ifdef __NOPT_ASSERT__
			Dbg_MsgAssert(size_check==0,("Array size mismatch"));
			#endif
			// Skip over the ESCRIPTTOKEN_ENDARRAY                                     
			++p_token;
			break;
		}    

		case ESYMBOLTYPE_LOCALSTRING:
		{
			char **pp_strings=(char**)p_dest->GetArrayPointer();
			
			// Skip over the ESCRIPTTOKEN_STARTARRAY
			++p_token;
			p_token=DoAnyRandomsOrJumps(p_token);
			while (*p_token!=ESCRIPTTOKEN_ENDARRAY)
			{
				switch (*p_token++)
				{
					case ESCRIPTTOKEN_ENDOFLINE:
					case ESCRIPTTOKEN_COMMA:
						break;
					case ESCRIPTTOKEN_ENDOFLINENUMBER:
						p_token+=4;
						break;
					case ESCRIPTTOKEN_LOCALSTRING:
					{
						#ifdef __NOPT_ASSERT__
						Dbg_MsgAssert(size_check,("Array size mismatch"));
						--size_check;
						#endif

						int string_length=Read4Bytes(p_token).mInt;
						Dbg_MsgAssert(string_length,("Zero string_length?"));
						p_token+=4;
						
						*pp_strings++=CreateString((const char *)p_token);
						p_token+=string_length;
						break;
					}	
					default:
						Dbg_MsgAssert(0,("Array elements must be of the same type, File %s, line %d\n",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
						break;
				}
				// Execute any random operators. This has to be done each time p_token is advanced.
				p_token=DoAnyRandomsOrJumps(p_token);
			}    
			#ifdef __NOPT_ASSERT__
			Dbg_MsgAssert(size_check==0,("Array size mismatch"));
			#endif
			// Skip over the ESCRIPTTOKEN_ENDARRAY                                     
			++p_token;
			break;
		}    

		case ESYMBOLTYPE_VECTOR:
		{
			CVector **pp_vectors=(CVector**)p_dest->GetArrayPointer();
			
			// Skip over the ESCRIPTTOKEN_STARTARRAY
			++p_token;
			p_token=DoAnyRandomsOrJumps(p_token);
			while (*p_token!=ESCRIPTTOKEN_ENDARRAY)
			{
				switch (*p_token++)
				{
					case ESCRIPTTOKEN_ENDOFLINE:
					case ESCRIPTTOKEN_COMMA:
						break;
					case ESCRIPTTOKEN_ENDOFLINENUMBER:
						p_token+=4;
						break;
					case ESCRIPTTOKEN_VECTOR:
					{
						#ifdef __NOPT_ASSERT__
						Dbg_MsgAssert(size_check,("Array size mismatch"));
						--size_check;
						#endif
						
						CVector *p_new_vector=new CVector;
						p_new_vector->mX=Read4Bytes(p_token).mFloat;
						p_token+=4;
						p_new_vector->mY=Read4Bytes(p_token).mFloat;
						p_token+=4;
						p_new_vector->mZ=Read4Bytes(p_token).mFloat;
						p_token+=4;
						
						*pp_vectors++=p_new_vector;
						break;
					}	
					default:
						Dbg_MsgAssert(0,("Array elements must be of the same type, File %s, line %d\n",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
						break;
				}
				// Execute any random operators. This has to be done each time p_token is advanced.
				p_token=DoAnyRandomsOrJumps(p_token);
			}    
			#ifdef __NOPT_ASSERT__
			Dbg_MsgAssert(size_check==0,("Array size mismatch"));
			#endif
			// Skip over the ESCRIPTTOKEN_ENDARRAY                                     
			++p_token;
			break;
		}    

		case ESYMBOLTYPE_PAIR:
		{
			CPair **pp_pairs=(CPair**)p_dest->GetArrayPointer();
			
			// Skip over the ESCRIPTTOKEN_STARTARRAY
			++p_token;
			p_token=DoAnyRandomsOrJumps(p_token);
			while (*p_token!=ESCRIPTTOKEN_ENDARRAY)
			{
				switch (*p_token++)
				{
					case ESCRIPTTOKEN_ENDOFLINE:
					case ESCRIPTTOKEN_COMMA:
						break;
					case ESCRIPTTOKEN_ENDOFLINENUMBER:
						p_token+=4;
						break;
					case ESCRIPTTOKEN_PAIR:
					{
						#ifdef __NOPT_ASSERT__
						Dbg_MsgAssert(size_check,("Array size mismatch"));
						--size_check;
						#endif
						
						CPair *p_new_pair=new CPair;
						p_new_pair->mX=Read4Bytes(p_token).mFloat;
						p_token+=4;
						p_new_pair->mY=Read4Bytes(p_token).mFloat;
						p_token+=4;
						
						*pp_pairs++=p_new_pair;
						break;
					}	
					default:
						Dbg_MsgAssert(0,("Array elements must be of the same type, File %s, line %d\n",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
						break;
				}
				// Execute any random operators. This has to be done each time p_token is advanced.
				p_token=DoAnyRandomsOrJumps(p_token);
			}    
			#ifdef __NOPT_ASSERT__
			Dbg_MsgAssert(size_check==0,("Array size mismatch"));
			#endif
			// Skip over the ESCRIPTTOKEN_ENDARRAY                                     
			++p_token;
			break;
		}    
		
		default:
			Dbg_MsgAssert(0,("Unsupported array type, File %s, line %d\n",GetSourceFile(p_token),GetLineNumber(p_token)));
			break;
    }

	return p_token;
}

// Adds one component to p_dest.
// The component is given the name nameChecksum, and it's type and value is defined by whatever
// p_token is pointing to.
// p_args contains the structure defining the value of any args referred to using the <,> operators.
// Returns a pointer to the next token after parsing the value pointed to by p_token.
static uint8 *sAddComponentFromQB(CStruct *p_dest, uint32 nameChecksum, uint8 *p_token, CStruct *p_args)
{
	Dbg_MsgAssert(p_dest,("NULL p_dest"));
	Dbg_MsgAssert(p_token,("NULL p_token"));
	
	bool use_rnd2=false;
	
	switch (*p_token++)
	{
		case ESCRIPTTOKEN_NAME:
		{
			uint32 checksum=Read4Bytes(p_token).mChecksum;
			p_token+=4;
			p_dest->AddChecksum(nameChecksum,checksum);
			break;
		}    
		case ESCRIPTTOKEN_INTEGER:
		{
			int integer=Read4Bytes(p_token).mInt;
			p_token+=4;
			p_dest->AddInteger(nameChecksum,integer);
			break;
		}    
		case ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE2:
			use_rnd2=true;
			// Intentional lack of a break here.
		case ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE:
		{
			CComponent *p_new_component=new CComponent;
			p_token=sCalculateRandomRange(p_token,p_new_component,use_rnd2);
			p_new_component->mNameChecksum=nameChecksum;
			p_dest->AddComponent(p_new_component);
			break;
		}    
		
		case ESCRIPTTOKEN_FLOAT:
		{
			float float_val=Read4Bytes(p_token).mFloat;
			p_token+=4;
			p_dest->AddFloat(nameChecksum,float_val);
			break;
		}    
		case ESCRIPTTOKEN_VECTOR:
		{
			float x=Read4Bytes(p_token).mFloat;
			p_token+=4;
			float y=Read4Bytes(p_token).mFloat;
			p_token+=4;
			float z=Read4Bytes(p_token).mFloat;
			p_token+=4;
			
			// Alert! This next 'if' is kind of game specific ...
			// It should probably be the exporter that does not include the angles in the node array if they are 
			// close to zero, rather than having this remove them from all structures.
			// Note: We can't just remove zero angles from the node array after it has loaded, because there
			// is not enough memory to store them initially.
			if (nameChecksum==CRCD(0x9d2d0915,"Angles"))
			{
				// If the vector is close to (0,0,0) then don't bother adding it.
				// This is a memory optimization. All nodes in levels are getting exported with
				// an angles component, which most of the time is (0,0,0). 
				// This will work OK so long as whenever GetVector is used in the code to get the angles, 
				// the vector being loaded into is initialised with (0,0,0).
				// That way, if the vector is not there, it will remain as (0,0,0) as required.
				float dx=x;
				if (dx<0.0f) dx=-dx;
				float dy=y;
				if (dy<0.0f) dy=-dy;
				float dz=z;
				if (dz<0.0f) dz=-dz;
				if (dx<0.01f && dy<0.01f && dz<0.01f)
				{
					break;
				}	
			}
			
			p_dest->AddVector(nameChecksum,x,y,z);
			break;
		}    
		case ESCRIPTTOKEN_PAIR:
		{
			float x=Read4Bytes(p_token).mFloat;
			p_token+=4;
			float y=Read4Bytes(p_token).mFloat;
			p_token+=4;
			p_dest->AddPair(nameChecksum,x,y);
			break;
		}    
		case ESCRIPTTOKEN_STRING:
		{
			int len=Read4Bytes(p_token).mInt;
			p_token+=4;
			p_dest->AddString(nameChecksum,(const char *)p_token);
			p_token+=len;
			break;
		}    
		case ESCRIPTTOKEN_LOCALSTRING:
		{
			int len=Read4Bytes(p_token).mInt;
			p_token+=4;
			p_dest->AddLocalString(nameChecksum,(const char *)p_token);
			p_token+=len;
			break;
		}    
		case ESCRIPTTOKEN_STARTSTRUCT:
		{
			// Rewind p_token to point to ESCRIPTTOKEN_STARTSTRUCT, because the 
			// sAddComponentsWithinCurlyBraces call requires it.
			--p_token;
			
			// No need to set which heap we're using, cos CStruct's and their CComponent's come off
			// their own pools.
			CStruct *p_structure=new CStruct;
			p_token=sAddComponentsWithinCurlyBraces(p_structure,p_token,p_args);
			p_dest->AddStructurePointer(nameChecksum,p_structure);
			// Note: Not deleting p_structure, because it has been given to p_dest, which will delete
			// it in its destructor.
			break;
		}    
		case ESCRIPTTOKEN_STARTARRAY:
		{
			// Rewind p_token to point to ESCRIPTTOKEN_STARTARRAY, because the CArray::Parse call requires it.
			--p_token;
			
			// The CArray constructor is not hard-wired to use the script heap for it's buffer, so need
			// to make sure we're using the script heap here.
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
			CArray *p_array=new CArray;
			Mem::Manager::sHandle().PopContext();			
			
			p_token=sInitArrayFromQB(p_array,p_token,p_args);
			p_dest->AddArrayPointer(nameChecksum,p_array);
			// Note: Not deleting p_array, because it has been given to p_dest, which will delete
			// it in its destructor.
			break;
		}
		case ESCRIPTTOKEN_KEYWORD_SCRIPT:
		{
			// When adding a local script defined in a structure, we'd expect the nameChecksum passed to
			// this function to be zero, since the name comes after the script keyword.
			Dbg_MsgAssert(nameChecksum==0,("nameChecksum expected to be 0 for a local script ??, File %s, line %d\n",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
			
			Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_NAME,("\nKeyword 'script' must be followed by a name, File %s, line %d\n",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
			++p_token; // Skip over the ESCRIPTTOKEN_NAME
			nameChecksum=Read4Bytes(p_token).mChecksum;
			p_token+=4; // Skip over the name checksum
			
			// Skip p_token over the script tokens, and calculate the size.
			const uint8 *p_script=p_token;
			p_token=SkipOverScript(p_token);
			uint32 size=p_token-p_script;
			
			// This will create a new buffer and copy in the script data.
			p_dest->AddScript(nameChecksum,p_script,size);
 			break;
		}	
		case ESCRIPTTOKEN_ARG:
		{
			Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_NAME,("Expected '<' token to be followed by a name, File %s, line %d",GetSourceFile(p_token-1),GetLineNumber(p_token-1)));
			++p_token;
			uint32 arg_checksum=Read4Bytes(p_token).mChecksum;
			p_token+=4;
			if (p_args)
			{
				// Look for a parameter named arg_checksum in p_args, recursing into any referenced structures
				CComponent *p_comp=p_args->FindNamedComponentRecurse(arg_checksum);
				if (p_comp)
				{
					if (nameChecksum==0 && p_comp->mType==ESYMBOLTYPE_STRUCTURE)
					{
						// Trying to add an unnamed structure, in which case it's contents
						// should be merged onto p_dest.
						p_dest->AppendStructure(p_comp->mpStructure);
						// Note: OK to call AppendStructure with NULL, so no need to check mpStructure.
					}
					else
					{
						// Create a copy of the component and add it to p_dest.
						
						CComponent *p_new_component=new CComponent;
						// Note: There is no copy-constructor for CComponent, because that would cause a
						// cyclic dependency between component.cpp/h and struct.cpp/h.
						// Instead, use the CopyComponent function defined in struct.cpp
						CopyComponent(p_new_component,p_comp);
						// Then change it's name to nameChecksum
						p_new_component->mNameChecksum=nameChecksum;
						// And add it to p_dest
						p_dest->AddComponent(p_new_component);
					}	
				}
			}
			// Don't assert if p_args is NULL, cos it will be when this function is used to skip over
			// parameter lists in FindReferences
			break;	
		}
		case ESCRIPTTOKEN_KEYWORD_ALLARGS:
		{
			// Don't assert if p_args is NULL, cos it will be when this function is used to skip over
			// parameter lists in FindReferences
			if (p_args)
			{
				if (nameChecksum)
				{
					// If it's a named structure, copy the passed arguments into a new structure and insert a 
					// pointer to it.
					CStruct *p_structure=new CStruct;
					*p_structure=*p_args;
					p_dest->AddStructurePointer(nameChecksum,p_structure);
				}
				else	
				{
					// Otherwise just merge in the passed parameters.
					// Un-named structures are always considered 'part of' the structure that they are in,
					// eg {x=9 {y=3}} is the same as {x=9 y=3}
					*p_dest+=*p_args;
				}
			}	
			break;	
		}
		case ESCRIPTTOKEN_COMMA:
			break;
		default:
			--p_token;
			Dbg_MsgAssert(0,("\nBad token '%s' encountered when creating component, File %s, line %d",GetTokenName((EScriptToken)*p_token),GetSourceFile(p_token),GetLineNumber(p_token)));
			break;
	}
    return p_token;    
}

// Parses the components in QB format pointed to by p_token, and adds them to p_dest until the 
// close-curly-brace token is reached.
// p_token must initially point to an open-curly-brace token.
// Any end-of-lines in between are ignored.
// For example, p_token may point to: {x=9 foo="Blaa"}
// Used when creating sub-structures and global structures.
// p_args contains the structure defining the value of any args referred to using the <,> operators.
// Returns a pointer to the next token after parsing the tokens pointed to by p_token.
static uint8 *sAddComponentsWithinCurlyBraces(CStruct *p_dest, uint8 *p_token, CStruct *p_args)
{
	Dbg_MsgAssert(p_dest,("NULL p_dest"));
	Dbg_MsgAssert(p_token,("NULL p_token"));
    Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_STARTSTRUCT,("p_token expected to point to ESCRIPTTOKEN_STARTSTRUCT, File %s, line %d",GetSourceFile(p_token),GetLineNumber(p_token)));
	// Skip over the ESCRIPTTOKEN_STARTSTRUCT
    ++p_token;

    while (true)
    {
		p_token=SkipEndOfLines(p_token);
		// Execute any random operators. This has to be done each time p_token is advanced.
		p_token=DoAnyRandomsOrJumps(p_token);
		if (*p_token==ESCRIPTTOKEN_ENDSTRUCT)
		{
			break;
		}	
		
        switch (*p_token)
        {
            case ESCRIPTTOKEN_NAME:
            {
                uint8 *p_name_token=p_token;
                ++p_token;
                uint32 name_checksum=Read4Bytes(p_token).mChecksum;
                p_token+=4;
                p_token=SkipEndOfLines(p_token);
				p_token=DoAnyRandomsOrJumps(p_token);
                if (*p_token==ESCRIPTTOKEN_EQUALS)
                {
                    ++p_token;
                    p_token=SkipEndOfLines(p_token);
					p_token=DoAnyRandomsOrJumps(p_token);
					
					if (*p_token==ESCRIPTTOKEN_OPENPARENTH)
					{
						CComponent *p_comp=new CComponent;
						p_token=Evaluate(p_token,p_args,p_comp);
						if (p_comp->mType!=ESYMBOLTYPE_NONE)
						{
							p_comp->mNameChecksum=name_checksum;
							p_dest->AddComponent(p_comp);
						}
						else
						{
							delete p_comp;
						}	
					}
					else
					{
						p_token=sAddComponentFromQB(p_dest,name_checksum,p_token,p_args);
					}
                }
                else
				{
                    p_token=sAddComponentFromQB(p_dest,NO_NAME,p_name_token,p_args);
				}	
                break;
            }    
            case ESCRIPTTOKEN_STARTSTRUCT:
                p_token=sAddComponentsWithinCurlyBraces(p_dest,p_token,p_args);
                break;
			case ESCRIPTTOKEN_COMMA:	
				++p_token;
				break;
			case ESCRIPTTOKEN_ENDOFLINE:
			case ESCRIPTTOKEN_ENDOFLINENUMBER:
				p_token=SkipEndOfLines(p_token);				
				break;
            default:
			{
				if (*p_token==ESCRIPTTOKEN_OPENPARENTH)
				{
					CComponent *p_comp=new CComponent;
					p_token=Evaluate(p_token,p_args,p_comp);
					
					if (p_comp->mType==ESYMBOLTYPE_STRUCTURE)
					{
						// The CStruct::AddComponent function does not allow unnamed structure
						// components to be added, so merge in the contents of the structure instead.
						p_dest->AppendStructure(p_comp->mpStructure);
						// Now p_comp does have to be cleaned up and deleted, because it has not
						// been given to p_dest.
						CleanUpComponent(p_comp);
						delete p_comp;
					}
					else
					{
						if (p_comp->mType!=ESYMBOLTYPE_NONE)
						{
							p_comp->mNameChecksum=NO_NAME;
							p_dest->AddComponent(p_comp);
						}
						else
						{
							delete p_comp;
						}	
					}				
				}
				else
				{
					p_token=sAddComponentFromQB(p_dest,NO_NAME,p_token,p_args);
				}	
                break;
			}	
        }            
    } 

	// Skip over the ESCRIPTTOKEN_ENDSTRUCT
    ++p_token;
    return p_token;
}

// Creates a new script symbol entry, allocates memory for the script data and copies it in, prefixing
// the data with the contents checksum.
static CSymbolTableEntry *sCreateScriptSymbol(uint32 nameChecksum, uint32 contentsChecksum, const uint8 *p_data, uint32 size, const char *p_fileName)
{
	Dbg_MsgAssert(p_data,("NULL p_data ??"));
	Dbg_MsgAssert(p_fileName,("NULL p_fileName"));

	#ifdef __NOPT_ASSERT__
	#ifndef __PLAT_WN32__
	CheckForPossibleInfiniteLoops(nameChecksum,(uint8*)(uint32)p_data, p_fileName);
	#endif
	#endif

	CSymbolTableEntry *p_new=CreateNewSymbolEntry(nameChecksum);
	Dbg_MsgAssert(p_new,("NULL p_new ??"));
	
	p_new->mType=ESYMBOLTYPE_QSCRIPT;
	
	#ifdef NO_SCRIPT_CACHING
	// Allocate space for the content checksum (4 bytes) plus the script data.
	uint8 *p_new_script=(uint8*)Mem::Malloc(4+size);
	
	// Write in the contents checksum.
	// p_new_script will be long-word aligned so OK to cast to a uint32*
	*(uint32*)p_new_script=contentsChecksum;
	
	uint8 *p_dest=p_new_script+4;
	const uint8 *p_source=p_data;
	for (uint32 i=0; impScript=p_new_script;
	PreProcessScript(p_new_script+4);
	
	#else	
	
	
	enum
	{
		COMPRESS_BUFFER_SIZE=20000,
	};	
	uint8 *p_compress_buffer=(uint8*)Mem::Malloc(COMPRESS_BUFFER_SIZE);
	
	// Compress the script data.
	int compressed_size=Encode((char*)p_data,(char*)p_compress_buffer,size,false);
	Dbg_MsgAssert(compressed_size <= COMPRESS_BUFFER_SIZE,("Compress buffer overflow! Compressed size of script %s is %d",Script::FindChecksumName(nameChecksum),compressed_size));
	
	// If it compressed to a bigger size, replace the compressed 
	// data with a copy of the original instead.
	if (compressed_size >= (int)size)
	{
		const uint8 *p_source=p_data;
		uint8 *p_dest=p_compress_buffer;
		for (uint32 i=0; imScriptOffset = NsARAM::alloc( SCRIPT_HEADER_SIZE+compressed_size, NsARAM::SCRIPT );
	NsDMA::toARAM( p_new->mScriptOffset, p_new_script, SCRIPT_HEADER_SIZE+compressed_size );
	Mem::Free( p_new_script );
#else
	p_new->mpScript=p_new_script;
#endif		// __PLAT_NGC__
	
	Mem::Free(p_compress_buffer);
	
	// Now that the new script has been loaded, the script cache needs to be refreshed in case any existing
	// CScript's are running this script. They will all get restarted later (see file.cpp)
	Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
	// Not asserting if p_script_cache is NULL, because it will be when all the q-files are loaded on startup.
	if (p_script_cache)	
	{
		p_script_cache->RefreshAfterReload(nameChecksum);
	}
	#endif

	// Store the name of the source qb in the symbol so that the qb is able to be unloaded.
	p_new->mSourceFileNameChecksum=Crc::GenerateCRCFromString(p_fileName);
	
	return p_new;
}

// Creates a symbol defined by a name, followed by equals, followed by a value.
static uint8 *sCreateSymbolOfTheFormNameEqualsValue(uint8 *p_token, const char *p_fileName, EBoolAssertIfDuplicateSymbols assertIfDuplicateSymbols)
{
	Dbg_MsgAssert(p_token,("NULL p_token"));
	Dbg_MsgAssert(p_fileName,("NULL p_fileName"));
	
	Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_NAME,("\nExpected ESCRIPTTOKEN_NAME"));
	++p_token;
	uint32 name_checksum=Read4Bytes(p_token).mChecksum;
	p_token+=4;
	p_token=SkipEndOfLines(p_token);
	Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_EQUALS,("\nExpected an equals sign to follow '%s' at line %d, %s\nThe most likely explanation is that you have a command\noutside of a script ... endscript",FindChecksumName(name_checksum),GetLineNumber(p_token),p_fileName));
	++p_token;
	p_token=SkipEndOfLines(p_token);

	// We have a name followed by an equals, so this is defining some symbol with that name,
	// eg Foo=6, or Foo="Blaa"
	// So first of all, see if any symbol already exists with that name, and if it does then
	// remove it, or assert.
	CSymbolTableEntry *p_existing_entry=LookUpSymbol(name_checksum);
	if (p_existing_entry)
	{
		if (assertIfDuplicateSymbols)
		{
			Dbg_MsgAssert(0,("The symbol '%s' is defined twice, in %s and %s\n Try running CleanAss ",FindChecksumName(name_checksum),FindChecksumName(p_existing_entry->mSourceFileNameChecksum),p_fileName));
		}
		
		CleanUpAndRemoveSymbol(p_existing_entry);
	}		

	// Create a new symbol with the given name.
	CSymbolTableEntry *p_new=CreateNewSymbolEntry(name_checksum);
	Dbg_MsgAssert(p_new,("NULL p_new ??"));

	// Store the name of the source qb in the symbol so that the qb is able to be unloaded.
	// Note: This used to be after the switch statement below. Moved it here so that if any
	// assert goes off in the switch statement the file name info will be present in the symbol.
	p_new->mSourceFileNameChecksum=Crc::GenerateCRCFromString(p_fileName);

	// Now see what type of value follows the equals, and fill in the new symbol accordingly.
	switch (*p_token)
	{
		case ESCRIPTTOKEN_OPENPARENTH:
		{
			CComponent *p_comp=new CComponent;
			p_token=Evaluate(p_token,NULL,p_comp);
			Dbg_MsgAssert(p_comp->mType!=ESYMBOLTYPE_NONE,("Global symbol '%s' did not evaluate to anything ...",FindChecksumName(p_comp->mNameChecksum)));
			p_new->mType=p_comp->mType;
			p_new->mUnion=p_comp->mUnion;
			p_comp->mUnion=0;
			delete p_comp;
			break;
		}
		
		case ESCRIPTTOKEN_INTEGER:
		case ESCRIPTTOKEN_FLOAT:
		case ESCRIPTTOKEN_VECTOR:
		case ESCRIPTTOKEN_PAIR:
		case ESCRIPTTOKEN_STRING:
		case ESCRIPTTOKEN_LOCALSTRING:
		case ESCRIPTTOKEN_NAME:
		{
			CComponent *p_comp=new CComponent;
			p_token=FillInComponentUsingQB(p_token,NULL,p_comp);
			p_new->mType=p_comp->mType;
			p_new->mUnion=p_comp->mUnion;
			p_comp->mUnion=0;
			delete p_comp;
			break;
		}

		case ESCRIPTTOKEN_STARTSTRUCT:
		{
			p_new->mType=ESYMBOLTYPE_STRUCTURE;
			p_new->mpStructure=new CStruct;
			
			p_token=sAddComponentsWithinCurlyBraces(p_new->mpStructure,p_token);
			break;
		}
		case ESCRIPTTOKEN_STARTARRAY:
		{
			p_new->mType=ESYMBOLTYPE_ARRAY;
			
			p_new->mpArray=new CArray;
			p_token=sInitArrayFromQB(p_new->mpArray,p_token);
			break;                
		}	
			
		default:
			Dbg_MsgAssert(0,("\nUnexpected type value of %d given to symbol '%s', line %d of %s",*p_token,FindChecksumName(name_checksum),GetLineNumber(p_token),p_fileName));
			break;
	} // switch (*p_token)
	
	return p_token;
}

// Given a pointer to an un-preprocessed script, this will parse through it converting function names
// to either cfunction pointers or member function tokens.
void PreProcessScript(uint8 *p_token)
{
	// Skip over the default params
	p_token=SkipToStartOfNextLine(p_token);
	
	while (*p_token!=ESCRIPTTOKEN_KEYWORD_ENDSCRIPT) 
	{
		switch (*p_token)
		{
			case ESCRIPTTOKEN_KEYWORD_IF:
				++p_token;
				break;
				
			case ESCRIPTTOKEN_NAME:
			{
				++p_token;
				uint32 name_checksum=Read4Bytes(p_token).mChecksum;
				p_token+=4;
	
				// Look up the name to see if it is a cfunction or member function.
				CSymbolTableEntry *p_entry=Resolve(name_checksum);
				// Must not assert if p_entry is NULL, cos they might just be loading in
				// a qb file that refers to a script that has not been written yet.
				if (p_entry)
				{
					if (p_entry->mType==ESYMBOLTYPE_CFUNCTION)
					{
						// Change the token type and replace the checksum with the
						// function pointer to save having to resolve it later.
						*(p_token-5)=ESCRIPTTOKEN_RUNTIME_CFUNCTION;
						Dbg_MsgAssert(p_entry->mpCFunction,("NULL p_entry->mpCFunction"));
						Write4Bytes(p_token-4, (uint32)p_entry->mpCFunction);
					}
					else if (p_entry->mType==ESYMBOLTYPE_MEMBERFUNCTION)
					{
						// Saves having to look up the checksum later to find out that it is
						// a member function.
						*(p_token-5)=ESCRIPTTOKEN_RUNTIME_MEMBERFUNCTION;
					}	
				}		
				p_token=SkipToStartOfNextLine(p_token);
				break;
			}	
			default:
				p_token=SkipToStartOfNextLine(p_token);
				break;
		}
	}	
}

static CStoredRandom *sFindStoredRandom(const uint8 *p_token, EScriptToken type, int numItems)
{
	CScript *p_current_script=GetCurrentScript();
	
	CStoredRandom *p_search=sp_first_stored_random;
	while (p_search)
	{
		if (p_search->mpToken==p_token &&
			p_search->mType==type &&
			p_search->mNumItems==numItems)
		{
			if (p_search->mpScript==p_current_script || p_search->mpScript==NULL)
			{
				return p_search;
			}
		}
			
		p_search=p_search->mpNext;
	}	
	return NULL;
}

static CStoredRandom *sCreateNewStoredRandom()
{
	if (s_num_stored_randoms>=MAX_STORED_RANDOMS)
	{
		Dbg_MsgAssert(sp_last_stored_random,("NULL sp_last_stored_random ?"));
		
		CStoredRandom *p_new_last=sp_last_stored_random->mpPrevious;
		delete sp_last_stored_random;
		
		sp_last_stored_random=p_new_last;
		if (p_new_last)
		{
			p_new_last->mpNext=NULL;
		}	
	}
	
	CStoredRandom *p_new=new CStoredRandom;
	p_new->mpNext=sp_first_stored_random;
	p_new->mpPrevious=NULL;
	
	if (sp_first_stored_random)
	{
		sp_first_stored_random->mpPrevious=p_new;
	}	
	else
	{
		Dbg_MsgAssert(sp_last_stored_random==NULL,("sp_last_stored_random not NULL?"));
		sp_last_stored_random=p_new;
	}
	
	sp_first_stored_random=p_new;
	return p_new;
}

S4Bytes Read4Bytes(const uint8 *p_long)
{
    S4Bytes four_bytes;
	
	#ifdef __PLAT_NGPS__
	if ( (((uint32)p_long)&3)==0 )
	{
		// Small speed opt
		four_bytes.mUInt=*(uint32*)p_long;
	}
	else
	{
		four_bytes.mUInt=(p_long[0])|(p_long[1]<<8)|(p_long[2]<<16)|(p_long[3]<<24);
	}	
	#else
	four_bytes.mUInt=(p_long[0])|(p_long[1]<<8)|(p_long[2]<<16)|(p_long[3]<<24);
	#endif
    return four_bytes;
}

// Gets 2 bytes from p_short, which may not be long word aligned.
S2Bytes Read2Bytes(const uint8 *p_short)
{
    S2Bytes two_bytes;
    two_bytes.mUInt=p_short[0]|(p_short[1]<<8);
    return two_bytes;
}

// Used by WriteToBuffer and ReadFromBuffer in utils.cpp
// Moved to parse.cpp just so that all these byte reading/writing functions are in one place.
uint8 *Write4Bytes(uint8 *p_buffer, uint32 val)
{
	*p_buffer++=val;
	*p_buffer++=val>>8;
	*p_buffer++=val>>16;
	*p_buffer++=val>>24;
	return p_buffer;
}

uint8 *Write4Bytes(uint8 *p_buffer, float floatVal)
{
	uint32 val=*(uint32*)&floatVal;
	*p_buffer++=val;
	*p_buffer++=val>>8;
	*p_buffer++=val>>16;
	*p_buffer++=val>>24;
	return p_buffer;
}

uint8 *Write2Bytes(uint8 *p_buffer, uint16 val)
{
	*p_buffer++=val;
	*p_buffer++=val>>8;
	return p_buffer;
}

// Skips over end-of-line tokens.    
uint8 *SkipEndOfLines(uint8 *p_token)
{
	Dbg_MsgAssert(p_token,("NULL p_token"));
    while (true)
    {
		if (*p_token==ESCRIPTTOKEN_ENDOFLINE)
		{
            ++p_token;
		}	
        else if (*p_token==ESCRIPTTOKEN_ENDOFLINENUMBER)
		{
            // ESCRIPTTOKEN_ENDOFLINENUMBER contains the line number of the previous line,
            // so that the line number of errors can be displayed.
            p_token+=5;
		}	
        else   
		{
            break;
		}	
    }
    return p_token;
}

CStoredRandom::CStoredRandom()
{
	mpNext=NULL;
	mpPrevious=NULL;
	
	++s_num_stored_randoms;
}

CStoredRandom::~CStoredRandom()
{
	--s_num_stored_randoms;
}

// This gets called whenever CScript::ClearScript is called.
void ReleaseStoredRandoms(CScript *p_script)
{
	CStoredRandom *p_search=sp_first_stored_random;
	while (p_search)
	{
		if (p_search->mpScript==p_script)
		{
			p_search->mpScript=NULL;
		}
		p_search=p_search->mpNext;
	}	
}

void CStoredRandom::InitIndices()
{
	Dbg_MsgAssert(mNumItems<=MAX_RANDOM_INDICES,("mNumItems too big"));
	
	for (uint16 i=0; i=2,("mNumItems must be at least 2 for RandomNoRepeat"));
	uint8 last=mpIndices[mNumItems-1];
	
	uint32 num_iterations=10*mNumItems;
	for (uint32 n=0; n= num_items ???"));
			}
			
			p_token+=2*num_items;					// Skip over the weights
			p_token+=4*chosen_index;				// Jump to the chosen offset value
			sint32 offset=Read4Bytes(p_token).mInt;	// Get the offset
			p_token+=4; 							// Skip over the offset
			p_token+=offset;						// Jump to the item
			break;
		}
		case ESCRIPTTOKEN_KEYWORD_RANDOM_NO_REPEAT:
		{
			++p_token; // Skip over the token.
			uint32 num_items=Read4Bytes(p_token).mUInt;
			Dbg_MsgAssert(num_items>1,("num_items must be greater than 1 in RandomNoRepeat operator"));
			p_token+=4; // Skip over the num_items
			p_token+=2*num_items;					// TODO: Skip over weights
			
			CStoredRandom *p_stored=sFindStoredRandom(p_token,ESCRIPTTOKEN_KEYWORD_RANDOM_NO_REPEAT,num_items);
			if (!p_stored)
			{
				p_stored=sCreateNewStoredRandom();
				Dbg_MsgAssert(p_stored,("NULL return value from sCreateNewStoredRandom()"));
				p_stored->mpToken=p_token;
				p_stored->mType=ESCRIPTTOKEN_KEYWORD_RANDOM_NO_REPEAT;
				p_stored->mNumItems=num_items;
				
				p_stored->mLastIndex=Mth::Rnd(num_items);
			}
			p_stored->mpScript=GetCurrentScript();
			
			uint16 jump_index=Mth::Rnd(num_items);
			// num_items is bigger than one, so in theory it should never get stuck in
			// an infinite loop here ...
			int c=0; // But have a counter just in case 
			while (jump_index==p_stored->mLastIndex)
			{
				++c;
				if (c>100) break;
				jump_index=Mth::Rnd(num_items);
			}	
			p_stored->mLastIndex=jump_index;
			
			p_token+=4*jump_index;	// Jump to the offset
			sint32 offset=Read4Bytes(p_token).mInt;	// Get the offset
			p_token+=4; 							// Skip over the offset
			p_token+=offset;						// Jump to the item
			break;
		}
		case ESCRIPTTOKEN_KEYWORD_RANDOM_PERMUTE:
		{
			++p_token; // Skip over the token.
			uint32 num_items=Read4Bytes(p_token).mUInt;
			Dbg_MsgAssert(num_items,("Zero num_items in random operator ?"));
			p_token+=4; // Skip over the num_items
			p_token+=2*num_items;					// TODO: Skip over weights
			
			CStoredRandom *p_stored=sFindStoredRandom(p_token,ESCRIPTTOKEN_KEYWORD_RANDOM_PERMUTE,num_items);
			if (!p_stored)
			{
				p_stored=sCreateNewStoredRandom();
				Dbg_MsgAssert(p_stored,("NULL return value from sCreateNewStoredRandom()"));
				p_stored->mpToken=p_token;
				p_stored->mType=ESCRIPTTOKEN_KEYWORD_RANDOM_PERMUTE;
				p_stored->mNumItems=num_items;

				p_stored->mCurrentIndex=0;
				Dbg_MsgAssert(num_items<=MAX_RANDOM_INDICES,("Too many entries in RandomPermute, max is %d. Line %d of file %s",MAX_RANDOM_INDICES,GetLineNumber(p_token),GetSourceFile(p_token)));
			
				p_stored->InitIndices();
				p_stored->RandomizeIndices();
			}
			p_stored->mpScript=GetCurrentScript();
			
			++p_stored->mCurrentIndex;
			if (p_stored->mCurrentIndex>=p_stored->mNumItems)
			{
				p_stored->RandomizeIndices(MAKE_NEW_FIRST_DIFFER_FROM_OLD_LAST);
				p_stored->mCurrentIndex=0;
			}
			uint16 jump_index=p_stored->mpIndices[p_stored->mCurrentIndex];
			Dbg_MsgAssert(jump_indexmType==ESYMBOLTYPE_QSCRIPT)
		{
			uint8 *p_script=p_sym->mpScript;
			Dbg_MsgAssert(p_script,("NULL p_sym->mpScript ?"));
			// Skip over the contents checksum.
			p_script+=4;
			
			if (PointsIntoScript(p_token,p_script))
			{
				return FindChecksumName(p_sym->mSourceFileNameChecksum);
			}		
		}
		p_sym=GetNextSymbolTableEntry(p_sym);
	}
	#else
	
	// Script caching is on, so query the script cache to find out what the script name is.
	// Cannot scan through the symbols mpScript's as above because when script caching is
	// on mpScript will point to compressed data, which cannot be stepped through.
	// So ask the script cache to step through its decompressed scripts instead.
	Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
	Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));
	return p_script_cache->GetSourceFile(p_token);
	
	#endif
	
	// Oh well.
	return "Unknown";	
}

#if 0
void CalcSpaceUsedByLineNumberInfo()
{
	int space_used=0;
	
	CSymbolTableEntry *p_sym=GetNextSymbolTableEntry(NULL);
	while (p_sym)
	{
		// For every script ...
		if (p_sym->mType==ESYMBOLTYPE_QSCRIPT)
		{
			uint8 *p_script=p_sym->mpScript;
			Dbg_MsgAssert(p_script,("NULL p_sym->mpScript ?"));
			// Skip over the contents checksum.
			p_script+=4;
			
			// Scan through the whole script looking for line number tokens.
			while (*p_script!=ESCRIPTTOKEN_KEYWORD_ENDSCRIPT)
			{
				if (*p_script==ESCRIPTTOKEN_ENDOFLINENUMBER)
				{
					space_used+=4;
				}	
				p_script=SkipToken(p_script);
			}		
		}
		p_sym=GetNextSymbolTableEntry(p_sym);
	}
	printf("Script heap space used by line number info = %d bytes\n");
}
#endif

uint8 *FillInComponentUsingQB(uint8 *p_token, CStruct *p_args, CComponent *p_comp)
{
	Dbg_MsgAssert(p_token,("NULL p_token"));
	Dbg_MsgAssert(p_comp,("NULL p_comp"));
	bool use_rnd2=false;
	
	switch (*p_token++)
	{
		case ESCRIPTTOKEN_INTEGER:
			p_comp->mType=ESYMBOLTYPE_INTEGER;
			p_comp->mIntegerValue=Read4Bytes(p_token).mInt;
			p_token+=4;
			break;
		case ESCRIPTTOKEN_FLOAT:
			p_comp->mType=ESYMBOLTYPE_FLOAT;
			p_comp->mFloatValue=Read4Bytes(p_token).mFloat;
			p_token+=4;
			break;
		case ESCRIPTTOKEN_NAME:
			p_comp->mType=ESYMBOLTYPE_NAME;
			p_comp->mChecksum=Read4Bytes(p_token).mChecksum;
			p_token+=4;
			break;
		case ESCRIPTTOKEN_STRING:
		{
			p_comp->mType=ESYMBOLTYPE_STRING;
			int len=Read4Bytes(p_token).mInt;
			p_token+=4;
			p_comp->mpString=CreateString((const char *)p_token);
			p_token+=len;
			break;
		}
		case ESCRIPTTOKEN_LOCALSTRING:
		{
			p_comp->mType=ESYMBOLTYPE_LOCALSTRING;
			int len=Read4Bytes(p_token).mInt;
			p_token+=4;
			p_comp->mpString=CreateString((const char *)p_token);
			p_token+=len;
			break;
		}
		case ESCRIPTTOKEN_VECTOR:
		{
			p_comp->mType=ESYMBOLTYPE_VECTOR;
			float x=Read4Bytes(p_token).mFloat;
			p_token+=4;
			float y=Read4Bytes(p_token).mFloat;
			p_token+=4;
			float z=Read4Bytes(p_token).mFloat;
			p_token+=4;
			p_comp->mpVector=new CVector;
			p_comp->mpVector->mX=x;
			p_comp->mpVector->mY=y;
			p_comp->mpVector->mZ=z;
			break;
		}	
		case ESCRIPTTOKEN_PAIR:
		{
			p_comp->mType=ESYMBOLTYPE_PAIR;
			float x=Read4Bytes(p_token).mFloat;
			p_token+=4;
			float y=Read4Bytes(p_token).mFloat;
			p_token+=4;
			p_comp->mpPair=new CPair;
			p_comp->mpPair->mX=x;
			p_comp->mpPair->mY=y;
			break;
		}	
		case ESCRIPTTOKEN_STARTSTRUCT:
		{
			// Rewind p_token to point to ESCRIPTTOKEN_STARTSTRUCT, because the 
			// sAddComponentsWithinCurlyBraces call requires it.
			--p_token;
			
			// No need to set which heap we're using, cos CStruct's and their CComponent's come off
			// their own pools.
			CStruct *p_structure=new CStruct;
			p_token=sAddComponentsWithinCurlyBraces(p_structure,p_token,p_args);
			
			p_comp->mType=ESYMBOLTYPE_STRUCTURE;
			p_comp->mpStructure=p_structure;
			break;
		}    
		case ESCRIPTTOKEN_STARTARRAY:
		{
			// Rewind p_token to point to ESCRIPTTOKEN_STARTARRAY, because the CArray::Parse call requires it.
			--p_token;
			
			// The CArray constructor is not hard-wired to use the script heap for it's buffer, so need
			// to make sure we're using the script heap here.
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
			CArray *p_array=new CArray;
			Mem::Manager::sHandle().PopContext();			
			
			p_token=sInitArrayFromQB(p_array,p_token,p_args);
			
			p_comp->mType=ESYMBOLTYPE_ARRAY;
			p_comp->mpArray=p_array;
			break;
		}
		case ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE2:
			use_rnd2=true;
			// Intentional lack of a break here.
		case ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE:
		{
			p_token=sCalculateRandomRange(p_token,p_comp,use_rnd2);
			break;
		}    

		case ESCRIPTTOKEN_KEYWORD_SCRIPT:
		{
			Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_NAME,("\nKeyword 'script' must be followed by a name, line %d (file name not known, sorry)",GetLineNumber(p_token)));
			++p_token; // Skip over the ESCRIPTTOKEN_NAME
			p_comp->mNameChecksum=Read4Bytes(p_token).mChecksum;
			p_token+=4; // Skip over the name checksum
			
			// Skip p_token over the script tokens, and calculate the size.
			const uint8 *p_script=p_token;
			p_token=SkipOverScript(p_token);
			uint32 size=p_token-p_script;
			
			p_comp->mType=ESYMBOLTYPE_QSCRIPT;
			
			// Allocate a buffer off the script heap
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
			Dbg_MsgAssert(size,("Zero script size"));
			uint8 *p_new_script=(uint8*)Mem::Malloc(size);
			Mem::Manager::sHandle().PopContext();
			
			// Copy the script into the new buffer.
			const uint8 *p_source=p_script;
			uint8 *p_dest=p_new_script;
			for (uint32 i=0; impScript=p_new_script;
			
 			break;
		}	

		case ESCRIPTTOKEN_ARG:
		{
			Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_NAME,("Expected '<' token to be followed by a name, File %s, line %d",GetSourceFile(p_token),GetLineNumber(p_token)));
			++p_token;
			uint32 arg_checksum=Read4Bytes(p_token).mChecksum;
			p_token+=4;
			if (p_args)
			{
				// Look for a parameter named arg_checksum in p_args, recursing into any referenced structures
				CComponent *p_found=p_args->FindNamedComponentRecurse(arg_checksum);
				if (p_found)
				{
					// Note: There is no copy-constructor for CComponent, because that would cause a
					// cyclic dependency between component.cpp/h and struct.cpp/h.
					// Instead, use the CopyComponent function defined in struct.cpp
					CopyComponent(p_comp,p_found);
				}
			}
			break;	
		}
		
		case ESCRIPTTOKEN_KEYWORD_ALLARGS:
		{
			if (p_args)
			{
				CStruct *p_structure=new CStruct;
				*p_structure=*p_args;
				
				p_comp->mType=ESYMBOLTYPE_STRUCTURE;
				p_comp->mpStructure=p_structure;
			}
			break;
		}		
		
		default:
			break;
	}
	return p_token;	
}

// This will evaluate the expression pointed to by p_token.
// Any args referred to by the <,> symbols are looked for in p_args.

// Expressions are terminated by any token that does not make sense as part of the expression.
// If the expression gets terminated unexpectedly, eg if there are still open braces, 
// then it will assert.
static CExpressionEvaluator sExpressionEvaluator;

void EnableExpressionEvaluatorErrorChecking()
{
	sExpressionEvaluator.EnableErrorChecking();
}

void DisableExpressionEvaluatorErrorChecking()
{
	sExpressionEvaluator.DisableErrorChecking();
}
	
static CComponent sTemp;
uint8 *Evaluate(uint8 *p_token, CStruct *p_args, CComponent *p_result)
{
	Dbg_MsgAssert(p_result,("NULL p_result"));
	Dbg_MsgAssert(p_token,("NULL p_token"));
	
	// SPEEDOPT: Make the Clear function faster
	sExpressionEvaluator.ClearIfNeeded();
	sExpressionEvaluator.SetTokenPointer(p_token);
	
	Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_OPENPARENTH,("Expected expression to begin with a '(', at line %d of %s",GetLineNumber(p_token),GetSourceFile(p_token)));
	++p_token;
	
	int parenth_count=0;
	bool generate_minus_operator=false;
	bool expecting_value=true;
	bool operator_is_dot=false;
	
	bool in_expression=true;
	while (in_expression)
	{
		p_token=DoAnyRandomsOrJumps(p_token);
		uint8 token_value=*p_token;
		
		switch (token_value)	
		{
		case ESCRIPTTOKEN_OPENPARENTH:
			if (!expecting_value)
			{
				in_expression=false;
				break;
			}	
			++p_token;
			sExpressionEvaluator.OpenParenthesis();
			generate_minus_operator=false;
			expecting_value=true;
			++parenth_count;
			break;
		case ESCRIPTTOKEN_CLOSEPARENTH:
			++p_token;
			if (parenth_count)
			{
				sExpressionEvaluator.CloseParenthesis();
				generate_minus_operator=true;
				expecting_value=false;
				--parenth_count;
			}
			else
			{
				in_expression=false;
			}	
			break;
		case ESCRIPTTOKEN_INTEGER:
			if (expecting_value)
			{
				Dbg_MsgAssert(!generate_minus_operator,("Eh? generate_minus_operator is set?"));
				p_token=FillInComponentUsingQB(p_token,p_args,&sTemp);
				
				sExpressionEvaluator.Input(&sTemp);
				CleanUpComponent(&sTemp);
				generate_minus_operator=true;
				expecting_value=false;
			}	
			else
			{
				uint8 *p_new_token=FillInComponentUsingQB(p_token,p_args,&sTemp);
				
				if (sTemp.mIntegerValue<0 && generate_minus_operator)
				{
					sExpressionEvaluator.Input(ESCRIPTTOKEN_MINUS);
					
					sTemp.mIntegerValue=-sTemp.mIntegerValue;
					sExpressionEvaluator.Input(&sTemp);
					CleanUpComponent(&sTemp);
					
					p_token=p_new_token;
					generate_minus_operator=true;
					expecting_value=false;
				}	
				else
				{
					in_expression=false;
				}	
			}	
			break;
			
		case ESCRIPTTOKEN_FLOAT:
			if (expecting_value)
			{
				Dbg_MsgAssert(!generate_minus_operator,("Eh? generate_minus_operator is set?"));
				p_token=FillInComponentUsingQB(p_token,p_args,&sTemp);
				
				sExpressionEvaluator.Input(&sTemp);
				CleanUpComponent(&sTemp);
				generate_minus_operator=true;
				expecting_value=false;
			}	
			else
			{
				uint8 *p_new_token=FillInComponentUsingQB(p_token,p_args,&sTemp);
				
				if (sTemp.mFloatValue<0.0f && generate_minus_operator)
				{
					sExpressionEvaluator.Input(ESCRIPTTOKEN_MINUS);
					
					sTemp.mFloatValue=-sTemp.mFloatValue;
					sExpressionEvaluator.Input(&sTemp);
					CleanUpComponent(&sTemp);
					
					p_token=p_new_token;
					generate_minus_operator=true;
					expecting_value=false;
				}	
				else
				{
					in_expression=false;
				}	
			}	
			break;
		case ESCRIPTTOKEN_NAME:
		{
			if (!expecting_value)
			{
				in_expression=false;
				break;
			}	
			p_token=FillInComponentUsingQB(p_token,p_args,&sTemp);
			
			// TODO: Use ResolveNameComponent instead of this switch ...
            CSymbolTableEntry *p_entry=NULL;
			if (operator_is_dot)
			{
				// This is a bit of a hack ...
				// If the operator is the dot operator, then do not check if the right-hand-side
				// is a global. This is because when the . is used to access members of a structure,
				// it may be that the name of the parameter being accessed is also the name of a 
				// global, which if substituted in place of the name would result in a nonsense 
				// expression.
				// The only disadvantage of this hack is that it means that a vector can no longer be
				// dot producted with a global vector, at least not directly.
				// I don't think there even are any global vectors at the moment anyway, and if there
				// were they could still be dot-producted with by simply copying them into a parameter 
				// and dotting with that instead.
				
				// The more general solution to the problem would be to have some sort of special
				// syntax to indicate when a name is not to be resolved. Eg, Foo.Blaa would mean 
				// that Blaa would be resolved, whereas Foo.~Blaa would tell the interpreter not to
				// resolve it.
				// That would be more complex to implement though.
			}
			else
			{
				p_entry=Resolve(sTemp.mChecksum);
			}
				
			if (p_entry)
			{
				switch (p_entry->mType)
				{
				case ESYMBOLTYPE_FLOAT:
					sTemp.mType=ESYMBOLTYPE_FLOAT;
					sTemp.mFloatValue=p_entry->mFloatValue;
					sExpressionEvaluator.Input(&sTemp);
					break;
				case ESYMBOLTYPE_INTEGER:
					sTemp.mType=ESYMBOLTYPE_INTEGER;
					sTemp.mIntegerValue=p_entry->mIntegerValue;
					sExpressionEvaluator.Input(&sTemp);
					break;
				case ESYMBOLTYPE_VECTOR:
					sTemp.mType=ESYMBOLTYPE_VECTOR;
					sTemp.mpVector=p_entry->mpVector;
					sExpressionEvaluator.Input(&sTemp);
					break;
				case ESYMBOLTYPE_PAIR:
					sTemp.mType=ESYMBOLTYPE_PAIR;
					sTemp.mpPair=p_entry->mpPair;
					sExpressionEvaluator.Input(&sTemp);
					break;
				case ESYMBOLTYPE_STRING:
					sTemp.mType=ESYMBOLTYPE_STRING;
					sTemp.mpString=p_entry->mpString;
					sExpressionEvaluator.Input(&sTemp);
					break;
				case ESYMBOLTYPE_LOCALSTRING:
					sTemp.mType=ESYMBOLTYPE_LOCALSTRING;
					sTemp.mpLocalString=p_entry->mpLocalString;
					sExpressionEvaluator.Input(&sTemp);
					break;
				case ESYMBOLTYPE_STRUCTURE:
					sTemp.mType=ESYMBOLTYPE_STRUCTURE;
					sTemp.mpStructure=p_entry->mpStructure;
					sExpressionEvaluator.Input(&sTemp);
					break;
				case ESYMBOLTYPE_ARRAY:
					sTemp.mType=ESYMBOLTYPE_ARRAY;
					sTemp.mpArray=p_entry->mpArray;
					sExpressionEvaluator.Input(&sTemp);
					break;
				case ESYMBOLTYPE_CFUNCTION:
				{
					CScript *p_script=GetCurrentScript();
					Dbg_MsgAssert(p_script,("Tried to execute the cfunc '%s' in an expression with no parent script, line %d of file %s",FindChecksumName(sTemp.mChecksum),GetLineNumber(p_token),GetSourceFile(p_token)));
					
					sTemp.mType=ESYMBOLTYPE_INTEGER;
				
					CStruct *p_function_params=new CStruct;
					// TODO: Problem if AddComponentsUntilEndOfLine calls Evaluate() again,
					// since only one instance of CExpressionEvaluator.
					p_token=AddComponentsUntilEndOfLine(p_function_params,p_token,p_args);
	                sTemp.mIntegerValue=(*p_entry->mpCFunction)(p_function_params,p_script);
					delete p_function_params;
					
					sExpressionEvaluator.Input(&sTemp);
					break;
				}	
				
				case ESYMBOLTYPE_MEMBERFUNCTION:
					Dbg_MsgAssert(0,("Sorry, cannot have calls to member functions in expressions!\nTried to call member function '%s' in an expression, line %d of file %s",FindChecksumName(sTemp.mChecksum),GetLineNumber(p_token),GetSourceFile(p_token)));
					break;
				
				default:
					sExpressionEvaluator.Input(&sTemp);
					break;
				}
			}
			else
			{
				sExpressionEvaluator.Input(&sTemp);
			}
			// Must not call CleanUpComponent because any pointer in the component will
			// have been borrowed from the global symbol.
			sTemp.mType=ESYMBOLTYPE_NONE;
			sTemp.mUnion=0;
			generate_minus_operator=true;
			expecting_value=false;
			break;
		}	
		case ESCRIPTTOKEN_STRING:
		case ESCRIPTTOKEN_LOCALSTRING:
		case ESCRIPTTOKEN_VECTOR:
		case ESCRIPTTOKEN_PAIR:
		//case ESCRIPTTOKEN_STARTARRAY:
		case ESCRIPTTOKEN_STARTSTRUCT:
		case ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE:
			if (!expecting_value)
			{
				in_expression=false;
				break;
			}	
			p_token=FillInComponentUsingQB(p_token,p_args,&sTemp);
			sExpressionEvaluator.Input(&sTemp);
			CleanUpComponent(&sTemp);
			generate_minus_operator=true;
			expecting_value=false;
			break;
		
		case ESCRIPTTOKEN_ARG:
		{
			if (!expecting_value)
			{
				in_expression=false;
				break;
			}	
			++p_token;
			Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_NAME,("Expected '<' token to be followed by a name, File %s, line %d",GetSourceFile(p_token),GetLineNumber(p_token)));
			++p_token;
			uint32 arg_checksum=Read4Bytes(p_token).mChecksum;
			p_token+=4;
			if (p_args)
			{
				// Look for a parameter named arg_checksum in p_args, recursing into any referenced structures
				CComponent *p_comp=p_args->FindNamedComponentRecurse(arg_checksum);
				if (p_comp)
				{
					sTemp.mType=p_comp->mType;
					sTemp.mUnion=p_comp->mUnion;
					// It might be a name that resolves to some global, so resolve it.
					ResolveNameComponent(&sTemp);

					sExpressionEvaluator.Input(&sTemp);
					
					// Must not call CleanUpComponent because any pointer in the component will
					// have been borrowed from the global symbol or p_comp.
					sTemp.mType=ESYMBOLTYPE_NONE;
					sTemp.mUnion=0;
				}
			}	
			generate_minus_operator=true;
			expecting_value=false;
			break;	
		}
		case ESCRIPTTOKEN_KEYWORD_ALLARGS:
		{
			if (!expecting_value)
			{
				in_expression=false;
				break;
			}	
			++p_token;
			
			if (p_args)
			{
				sTemp.mType=ESYMBOLTYPE_STRUCTURE;
				sTemp.mpStructure=p_args;
				sExpressionEvaluator.Input(&sTemp);
			}	
			generate_minus_operator=true;
			expecting_value=false;
			break;	
		}
		
		case ESCRIPTTOKEN_ADD:
		case ESCRIPTTOKEN_MINUS:
		case ESCRIPTTOKEN_MULTIPLY:
		case ESCRIPTTOKEN_DIVIDE:
		case ESCRIPTTOKEN_DOT:
		case ESCRIPTTOKEN_OR:
		case ESCRIPTTOKEN_AND:
		case ESCRIPTTOKEN_LESSTHAN:
		case ESCRIPTTOKEN_GREATERTHAN:
		case ESCRIPTTOKEN_EQUALS:
		case ESCRIPTTOKEN_STARTARRAY:
			++p_token;
			sExpressionEvaluator.Input((EScriptToken)token_value);
			generate_minus_operator=false;
			expecting_value=true;
			if (token_value==ESCRIPTTOKEN_DOT)
			{
				operator_is_dot=true;
			}	
			else
			{
				operator_is_dot=false;
			}	
			break;
			
		case ESCRIPTTOKEN_ENDARRAY:
		case ESCRIPTTOKEN_ENDOFLINE:
			++p_token;
			break;
		case ESCRIPTTOKEN_ENDOFLINENUMBER:
			p_token+=5;
			break;
			
		case ESCRIPTTOKEN_ENDOFFILE:
		case ESCRIPTTOKEN_KEYWORD_ENDSCRIPT:
		case ESCRIPTTOKEN_COMMA:
			Dbg_MsgAssert(0,("Unexpected token '%s' in expression, line %d of %s",GetTokenName((EScriptToken)token_value),GetLineNumber(p_token),GetSourceFile(p_token)));
			break;	
			
		default:
			// Unrecognized expression
			Dbg_MsgAssert(0,("Don't know how to evaluate '%s', at line %d of %s",GetTokenName((EScriptToken)token_value),GetLineNumber(p_token),GetSourceFile(p_token)));
			break;	
		}	
		
		// This cannot be commented in all the time, because often the evaluator will report
		// errors when it gets called via the FindReferences function, which scans through scripts
		// but does not execute them. Errors occur in this case when script parameters are used
		// in expressions, because when FindReferences is scanning through the script it has no
		// parameters. Need to fix FindReferences so that it can scan over expressions without
		// executing them.
		//if (sExpressionEvaluator.GetError())
		//{
		//	Dbg_MsgAssert(0,("Evaluator error: File %s, line %d",GetSourceFile(p_token),GetLineNumber(p_token)));
		//}	
		#ifdef	__NOPT_ASSERT__
		if (sExpressionEvaluator.ErrorCheckingEnabled() && sExpressionEvaluator.GetError())
		{
			printf("Evaluator error: File %s, line %d",GetSourceFile(p_token),GetLineNumber(p_token));
		}	
		#endif
	}
		
	sExpressionEvaluator.GetResult(p_result);
	#ifdef	__NOPT_ASSERT__
	if (sExpressionEvaluator.ErrorCheckingEnabled() && sExpressionEvaluator.GetError())
	{
		printf("Evaluator error: File %s, line %d\n",GetSourceFile(p_token),GetLineNumber(p_token));
	}	
	#endif
	return p_token;
}

// Parses the components in QB format pointed to by p_token, and adds them to p_dest until the 
// end-of-line is reached.
// For example, p_token may point to: x=9 foo="Blaa"
// Used when creating the structure of function parameters, and the structure of default parameters
// for scripts.
// p_args contains the structure defining the value of any args referred to using the <,> operators.
// Returns a pointer to the next token after parsing the tokens pointed to by p_token.
//
// Note: This gets called for every line of script executed, hence is probably a bottleneck in script
// execution speed. Could speed up a lot by pre-generating any function parameters that are constant, ie
// contain no <,> operators.
uint8 *AddComponentsUntilEndOfLine(CStruct *p_dest, uint8 *p_token, CStruct *p_args)
{
	Dbg_MsgAssert(p_dest,("NULL p_dest"));
	Dbg_MsgAssert(p_token,("NULL p_token"));
	
    while (true)
    {
		// Execute any random operators.
		p_token=DoAnyRandomsOrJumps(p_token);

        if (sIsEndOfLine(p_token)) 
		{
			break;
		}	
		
		// This is so that function calls can be put in expressions, which are always enclosed
		// in parentheses.
		if (*p_token==ESCRIPTTOKEN_CLOSEPARENTH)
		{
			break;
		}
			
        switch (*p_token)
        {
            case ESCRIPTTOKEN_NAME:
            {
                uint8 *p_name_token=p_token;
                ++p_token;
                uint32 name_checksum=Read4Bytes(p_token).mChecksum;
                p_token+=4;
				// Execute any random operators. This has to be done each time p_token is advanced.
				p_token=DoAnyRandomsOrJumps(p_token);

                if (*p_token==ESCRIPTTOKEN_EQUALS)
                {
                    ++p_token;
					p_token=DoAnyRandomsOrJumps(p_token);
					
                    Dbg_MsgAssert(!sIsEndOfLine(p_token),("Syntax error, nothing following '=', File %s, line %d",GetSourceFile(p_token),GetLineNumber(p_token)));
					
					if (*p_token==ESCRIPTTOKEN_OPENPARENTH)
					{
						CComponent *p_comp=new CComponent;
						p_token=Evaluate(p_token,p_args,p_comp);
						if (p_comp->mType!=ESYMBOLTYPE_NONE)
						{
							p_comp->mNameChecksum=name_checksum;
							p_dest->AddComponent(p_comp);
						}
						else
						{
							delete p_comp;
						}
					}
					else
					{
						p_token=sAddComponentFromQB(p_dest,name_checksum,p_token,p_args);					
					}	
                }
                else
				{
                    p_token=sAddComponentFromQB(p_dest,NO_NAME,p_name_token,p_args);
				}	
                break;
            }
			case ESCRIPTTOKEN_KEYWORD_ALLARGS:
                ++p_token;
				if (p_args)
				{
					*p_dest+=*p_args;
				}	
				break;
            case ESCRIPTTOKEN_STARTSTRUCT:
                p_token=sAddComponentsWithinCurlyBraces(p_dest,p_token,p_args);
                break;
			case ESCRIPTTOKEN_ARG:
				p_token=sAddComponentFromQB(p_dest,NO_NAME,p_token,p_args);
				break;
            case ESCRIPTTOKEN_ENDOFLINE:
            case ESCRIPTTOKEN_ENDOFLINENUMBER:
                break;
            case ESCRIPTTOKEN_ENDOFFILE:
				Dbg_MsgAssert(0,("End of file reached during AddComponentsUntilEndOfLine ?"));
                break;
			
			case ESCRIPTTOKEN_COMMA:
				++p_token;
				break;
				
            default:
			{
				if (*p_token==ESCRIPTTOKEN_OPENPARENTH)
				{
					CComponent *p_comp=new CComponent;
					p_token=Evaluate(p_token,p_args,p_comp);
					
					if (p_comp->mType==ESYMBOLTYPE_STRUCTURE)
					{
						// The CStruct::AddComponent function does not allow unnamed structure
						// components to be added, so merge in the contents of the structure instead.
						p_dest->AppendStructure(p_comp->mpStructure);
						// Now p_comp does have to be cleaned up and deleted, because it has not
						// been given to p_dest.
						CleanUpComponent(p_comp);
						delete p_comp;
					}
					else
					{
						if (p_comp->mType!=ESYMBOLTYPE_NONE)
						{
							p_comp->mNameChecksum=NO_NAME;
							p_dest->AddComponent(p_comp);
						}
						else
						{
							delete p_comp;
						}	
					}				
				}
				else
				{
					p_token=sAddComponentFromQB(p_dest,NO_NAME,p_token,p_args);
				}	
                break;
			}	
        }            
    } 

	// Note: Does not skip over the end-of-lines. This is so that if any asserts go off during the
	// C-function or member function call that this structure is going to be used as parameters too,
	// the assert will print the correct line number, rather than the one following.
    return p_token;
}

// Deletes any entity pointed to by p_sym, then removes p_sym from the symbol table.
void CleanUpAndRemoveSymbol(CSymbolTableEntry *p_sym)
{
	Dbg_MsgAssert(p_sym,("NULL p_sym"));
	switch (p_sym->mType)
	{
		case ESYMBOLTYPE_INTEGER:
		case ESYMBOLTYPE_FLOAT:
		case ESYMBOLTYPE_NAME:
			// Nothing to delete
			break;		
			
		case ESYMBOLTYPE_STRING:
			Dbg_MsgAssert(p_sym->mpString,("NULL p_sym->mpString"));
			DeleteString(p_sym->mpString);
			break;
		case ESYMBOLTYPE_LOCALSTRING:
			Dbg_MsgAssert(p_sym->mpLocalString,("NULL p_sym->mpLocalString"));
			DeleteString(p_sym->mpLocalString);
			break;
		case ESYMBOLTYPE_PAIR:
			Dbg_MsgAssert(p_sym->mpPair,("NULL p_sym->mpPair"));
			delete p_sym->mpPair;
			break;
		case ESYMBOLTYPE_VECTOR:
			Dbg_MsgAssert(p_sym->mpVector,("NULL p_sym->mpVector"));
			delete p_sym->mpVector;
			break;
		case ESYMBOLTYPE_QSCRIPT:
#ifdef __PLAT_NGC__
			NsARAM::free( p_sym->mScriptOffset );
#else
			Dbg_MsgAssert(p_sym->mpScript,("NULL p_sym->mpScript"));
			Mem::Free(p_sym->mpScript);
#endif		// __PLAT_NGC__
			break;
		case ESYMBOLTYPE_STRUCTURE:
			Dbg_MsgAssert(p_sym->mpStructure,("NULL p_sym->mpStructure"));
			delete p_sym->mpStructure;
			break;
		case ESYMBOLTYPE_ARRAY:
			Dbg_MsgAssert(p_sym->mpArray,("NULL p_sym->mpArray"));
			CleanUpArray(p_sym->mpArray);
			delete p_sym->mpArray;
			break;
		default:
			Dbg_MsgAssert(0,("Bad type of '%s' in p_sym",GetTypeName(p_sym->mType)));
			break;
	}
	p_sym->mUnion=0;

	RemoveSymbol(p_sym);
}

// TODO: Remove? Currently used in cfuncs.cpp to delete the NodeArray, but now that we
// have the ability to unload qb files that should not be necessary any more.
// Note: Just found it is also needed in parkgen.cpp, so don't remove after all. 
void CleanUpAndRemoveSymbol(uint32 checksum)
{
	CSymbolTableEntry *p_sym=LookUpSymbol(checksum);
	if (p_sym)
	{
		CleanUpAndRemoveSymbol(p_sym);
	}	
}

void CleanUpAndRemoveSymbol(const char *p_symbolName)
{
	CleanUpAndRemoveSymbol(Crc::GenerateCRCFromString(p_symbolName));
}
	
#define HASHBITS 12
struct SChecksum
{
	uint32 mChecksum;
	char *mpName;
	SChecksum *mpNext;
};

static SChecksum *sp_hash_table=NULL;

const char *GetChecksumNameFromLastQB(uint32 checksum)
{
	Dbg_MsgAssert(sp_hash_table,("NULL sp_hash_table"));
	
	SChecksum *p_entry=&sp_hash_table[checksum&((1<mChecksum==checksum)
		{
			return p_entry->mpName;
		}	
		p_entry=p_entry->mpNext;
	}	
	
	return NULL;
}	

void RemoveChecksumNameLookupHashTable()
{
	if (sp_hash_table)
	{
		SChecksum *p_entry=sp_hash_table;
		for (uint32 i=0; i<(1<mpNext;
			while (p_ch)
			{
				SChecksum *p_next=p_ch->mpNext;
				// The dynamically allocated entries must have had their mpName set.
				Dbg_MsgAssert(p_ch->mpName,("NULL p_ch->mpName ?"));
				Mem::Free(p_ch->mpName);
				Mem::Free(p_ch);
				p_ch=p_next;
			}
			// The entries in the contiguous array may not have their mpName set.	
			if (p_entry->mpName)
			{
				Mem::Free(p_entry->mpName);
			}	
			++p_entry;
		}	
	
		Mem::Free(sp_hash_table);
		sp_hash_table=NULL;
	}	
}

uint8 *SkipToStartOfNextLine(uint8 *p_token)
{
	int curly_bracket_count=0;
	int square_bracket_count=0;
	int parenth_count=0;
	
	while (true)
	{
		if (*p_token==ESCRIPTTOKEN_STARTSTRUCT) ++curly_bracket_count;
		if (*p_token==ESCRIPTTOKEN_ENDSTRUCT) --curly_bracket_count;
		Dbg_MsgAssert(curly_bracket_count>=0,("Negative curly_bracket_count ??"));

		if (*p_token==ESCRIPTTOKEN_STARTARRAY) ++square_bracket_count;
		if (*p_token==ESCRIPTTOKEN_ENDARRAY) --square_bracket_count;
		Dbg_MsgAssert(square_bracket_count>=0,("Negative square_bracket_count ??"));

		if (*p_token==ESCRIPTTOKEN_OPENPARENTH) ++parenth_count;
		if (*p_token==ESCRIPTTOKEN_CLOSEPARENTH) --parenth_count;
		Dbg_MsgAssert(parenth_count>=0,("Negative parenth_count, File %s, line %d",GetSourceFile(p_token),GetLineNumber(p_token)));
		
		if (curly_bracket_count==0 && 
			square_bracket_count==0 &&
			parenth_count==0 &&
			sIsEndOfLine(p_token))
		{
			break;
		}		
		p_token=SkipToken(p_token);
	}
	
	p_token=SkipToken(p_token);
	return p_token;
}

// Parses a qb file, creating all the symbols (scripts, arrays etc) defined within.
// If the assertIfDuplicateSymbols is set, then it will assert if any of the symbols in the qb
// exist already.
// This flag is set when loading all the startup qb's, because we don't want clashing symbols.
// It is not set when reloading a qb during development though, since that involves reloading
// existing symbols.
// The file name is also passed so that each symbol knows what qb it came from, which allows
// all the symbols from a particular qb to be unloaded using the UnloadQB function (in file.cpp)
void ParseQB(const char *p_fileName, uint8 *p_qb, EBoolAssertIfDuplicateSymbols assertIfDuplicateSymbols, bool allocateChecksumNameLookupTable)
{
	Dbg_MsgAssert(p_fileName,("NULL p_fileName"));
	Dbg_MsgAssert(p_qb,("NULL p_qb"));

	// Do a first parse through the qb to register the checksum names.
	// They get added to a lookup table that can be queried using GetChecksumNameFromLastQB defined above.
	// The lookup table only contains the checksum names defined in this qb.
	// GetChecksumNameFromLastQB is currently only used by the game-specific nodearray.cpp to generate a lookup
	// table of node name prefixes.
	//
	// If __NOPT_ASSERT__ is set the checksum names also get added to a big permanent lookup table so that
	// the FindChecksumName function can be used at any time in asserts.
	// 
    uint8 *p_token=p_qb;
    bool end_of_file=false;

	// Remove any existing checksum-name-lookup hash table.
	RemoveChecksumNameLookupHashTable();

	// K: Added the passed flag allocateChecksumNameLookupTable so that we can avoid allocating the table
	// if we know it will not be needed. Gary needs this for the cutscenes where there is not enough memory
	// to allocate the table.
	// By default the flag is set to true however because the table is needed when generating prefix info
	// for the nodearray, and we don't know whether the qb contains the nodearray until after finishing
	// parsing it.
	if (allocateChecksumNameLookupTable)
	{
		// Reallocate it.
		bool use_debug_heap=false;
		#ifdef	__NOPT_ASSERT__	
		if (!assertIfDuplicateSymbols)
		{
			// They're doing a qbr ...
			if (Mem::Manager::sHandle().GetHeap(0x70cb0238/*DebugHeap*/))
			{
				// The debug heap exists, so use it.
				// Need to do this because there is often not enough memory otherwise.
				use_debug_heap=true;
			}	
		}	
		#endif
	
		if (use_debug_heap)
		{
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
		}	
		else
		{
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
		}
		Dbg_MsgAssert(sp_hash_table==NULL,("sp_hash_table not NULL ?"));
		sp_hash_table=(SChecksum*)Mem::Malloc((1<mChecksum=0;
			p_entry->mpName=NULL;
			p_entry->mpNext=NULL;
			++p_entry;
		}	
	}
	
	// Line number info in qb's is excluded when doing a release build to save memory.
	// Need to stop with an error message if there is line number info when there should not
	// be, otherwise the release build will run out of memory & it will be hard to track down
	// why since there will be no asserts.
	// This flag indicates whether to give the error message or not.
	bool allow_line_number_info=false;
	if (!assertIfDuplicateSymbols)
	{
		// Doing a qbr, so allow line number info, otherwise
		// we won't be able to qbr when __NOPT_ASSERT__ is not defined.
		allow_line_number_info=true;
	}	
	#ifdef __NOPT_ASSERT__
	// Allow line number info when asserts are on, cos the script heap will have an extra 200K for them.
	allow_line_number_info=true;
	#endif
	#ifdef __PLAT_WN32__
	// PC has lots of memory.
	allow_line_number_info=true;
	#endif
		

    while (!end_of_file)
	{
		switch (*p_token)
		{
		case ESCRIPTTOKEN_CHECKSUM_NAME: 
		{
			++p_token;
			uint32 checksum=Read4Bytes(p_token).mChecksum;
			const char *p_name=(const char*)(p_token+4);
	
			if (allocateChecksumNameLookupTable)
			{
				// Add it to the table.
				SChecksum *p_entry=&sp_hash_table[checksum&((1<mpName)
				{
					// The first slot is already occupied, so create a new SChecksum and
					// link it in between the first and the rest.
					SChecksum *p_new=(SChecksum*)Mem::Malloc(sizeof(SChecksum));
					p_new->mChecksum=checksum;
					p_new->mpName=(char*)Mem::Malloc(strlen(p_name)+1);
					strcpy(p_new->mpName,p_name);
					
					p_new->mpNext=p_entry->mpNext;
					p_entry->mpNext=p_new;
				}
				else
				{
					// First slot is not occupied, so stick it in there.
					p_entry->mChecksum=checksum;
					p_entry->mpName=(char*)Mem::Malloc(strlen(p_name)+1);
					strcpy(p_entry->mpName,p_name);
					p_entry->mpNext=NULL;
				}	
			}
						
			// Add to the big lookup table of all checksum names, used by FindChecksumName
			// Doing this here rather than the main parsing loop after this one so that the names 
			// are registered first in case any asserts go off.
			AddChecksumName(checksum,p_name);
				
			// Skip over the checksum.
			p_token+=4;
			// Skip over the name.
			while (*p_token)
			{
				++p_token;
			}
			// Skip over the terminator.
			++p_token;	
			break;
		}
			
        case ESCRIPTTOKEN_ENDOFFILE:
            end_of_file=true;
            break;

		case ESCRIPTTOKEN_ENDOFLINENUMBER:
		{
			if (allow_line_number_info)
			{
				p_token=SkipToken(p_token);
			}
			else
			{
				printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n");
				printf("Found line number info in '%s' !\nLine number info needs to be removed when __NOPT_ASSERT__ is not defined\nin order to save memory.\nTry doing a 'cleanass nolinenumbers' and running again.\n",p_fileName);
				while (1);
			}	
			break;
		}
            
        default:
            p_token=SkipToken(p_token);
            break;
        }    
    }
    p_token=p_qb;
	end_of_file=false;
	// That's all the checksum names added.


	// Register the file name checksum so that FindChecksumName will be able to find it.
	// (Required if a script assert goes off that needs to print the source file name)
	AddChecksumName(Crc::GenerateCRCFromString(p_fileName),p_fileName);

	// Store the checksum of the file name so that if an assert goes off during parsing the file name can be printed.
	s_qb_being_parsed=Crc::GenerateCRCFromString(p_fileName);

	// Make sure we're using the script heap, cos we're about to create a bunch of scripty stuff.
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	
    while (!end_of_file)
    {
        p_token=SkipEndOfLines(p_token);
		
        switch (*p_token)
        {
			case ESCRIPTTOKEN_NAME:
			{
				p_token=sCreateSymbolOfTheFormNameEqualsValue(p_token,p_fileName,assertIfDuplicateSymbols);
				break;    
			}
			case ESCRIPTTOKEN_KEYWORD_SCRIPT:
			{
				// Get the new script's name checksum ...
				++p_token; // Skip over the ESCRIPTTOKEN_KEYWORD_SCRIPT
				Dbg_MsgAssert(*p_token==ESCRIPTTOKEN_NAME,("\nKeyword 'script' must be followed by a name, line %d of %s",GetLineNumber(p_token),p_fileName));
				++p_token; // Skip over the ESCRIPTTOKEN_NAME
				uint32 name_checksum=Read4Bytes(p_token).mChecksum;
				p_token+=4; // Skip over the name checksum
	
	
				// Calculate the length of the script data in bytes.
				uint8 *p_script_data=p_token;
				p_token=SkipOverScript(p_token);
				uint32 script_data_size=p_token-p_script_data;
	
				
				// Calculate a checksum of the contents of the new script.
				// The purpose of the contents checksum is to prevent unnecessary restarting of CScript's
				// when a qb is reloaded. It may be that only one script in the qb has changed, so we only
				// want the CScript's that were referencing that script to restart.
				
				// Note: The LoadQB function in gel\scripting\file.cpp does the restarting of existing
				// CScripts by checking the mGotReloaded flag in the CSymbolTableEntry for the script.
				
				uint32 new_contents_checksum=CalculateScriptContentsChecksum(p_script_data);
				
				// Check to see if a script with this name exists already.			
				CSymbolTableEntry *p_existing_entry=LookUpSymbol(name_checksum);
				if (p_existing_entry)
				{
					if (assertIfDuplicateSymbols)
					{
						Dbg_MsgAssert(0,("The symbol '%s' is defined twice, in %s and %s",FindChecksumName(name_checksum),FindChecksumName(p_existing_entry->mSourceFileNameChecksum),p_fileName));
					}
				
					if (p_existing_entry->mType==ESYMBOLTYPE_QSCRIPT)
					{
						// A script with the same name does already exist.
						// Compare its contents checksum with the new one.
#ifdef __PLAT_NGC__
						uint32 header[(SCRIPT_HEADER_SIZE/4)];
						NsDMA::toMRAM( header, p_existing_entry->mScriptOffset, SCRIPT_HEADER_SIZE );
						uint32 old_contents_checksum=header[0];
#else
						Dbg_MsgAssert(p_existing_entry->mpScript,("NULL p_existing_entry->mpScript ??"));
						uint32 old_contents_checksum=*(uint32*)(p_existing_entry->mpScript);
#endif		// __PLAT_NGC__
						
						if (new_contents_checksum != old_contents_checksum)
						{
							// The contents of the new script differ from the old!
							// So remove the old one, and create the new.
							CleanUpAndRemoveSymbol(p_existing_entry);
							sCreateScriptSymbol(name_checksum,
												new_contents_checksum,
												p_script_data,
												script_data_size,
												p_fileName);
						}
						else
						{
							// The contents of the script have not changed!
							// So there's nothing to do.
						}
					}
					else
					{
						// Remove the existing symbol, whatever it is. (It isn't a script)
						CleanUpAndRemoveSymbol(p_existing_entry);
						// Create the new script symbol.
						sCreateScriptSymbol(name_checksum,new_contents_checksum,p_script_data,script_data_size,p_fileName);
					}
				}	
				else
				{
					// No symbol currently exists with this name, so create the new script symbol.
					sCreateScriptSymbol(name_checksum,new_contents_checksum,p_script_data,script_data_size,p_fileName);
				}			
				break;
			}
	
			case ESCRIPTTOKEN_CHECKSUM_NAME: 
				// These have been done already, so skip over.
				p_token=SkipToken(p_token);
				break;
				
			case ESCRIPTTOKEN_ENDOFFILE:
				end_of_file=true;
				break;
				
			default:
				Dbg_MsgAssert(0,("\nConfused by line %d of %s.\nExpected either a script definition (Script ... EndScript)\nor a constant definition (Something=...)",GetLineNumber(p_token),p_fileName));
				break;
        }    
    }

	Mem::Manager::sHandle().PopContext();
	s_qb_being_parsed=0;
}

// Used by the EditorCameraComponent when it checks polys to see if they are Kill polys
bool ScriptContainsName(uint8 *p_script, uint32 searchName)
{
	Dbg_MsgAssert(p_script,("NULL p_script"));

	//int num_names_found=0;
	//uint32 last_name=0;
	
	uint8 *p_token=p_script;
	while (*p_token!=ESCRIPTTOKEN_KEYWORD_ENDSCRIPT) 
	{
		switch (*p_token)
		{
			case ESCRIPTTOKEN_NAME:
			{
				++p_token;
				uint32 name=Read4Bytes(p_token).mChecksum;
				p_token+=4;
				if (name == searchName)
				{
					return true;
				}	
				
				//++num_names_found;
				//last_name=name;
				break;
			}	
				
			default:
				p_token=SkipToken(p_token);
				break;
		}
	}	
	
	/*
	// I put this in so that scripts that consist of a single call to another script will have that
	// scanned. This was to fix a bug where the kill poly over the swimming pool in Hawaii was not
	// being detected. However, this fix then meant that the kill polys around the hotel would get
	// detected, meaning that the cursor could never be got off the hotel. So commented it out
	// for the moment, since at least the first bug does not make the cursor get stuck.
	if (num_names_found==1)
	{
		Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
		Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));
	
		p_script=p_script_cache->GetScript(last_name);
		if (p_script)
		{
			bool contains_name=ScriptContainsName(p_script, searchName);
			p_script_cache->DecrementScriptUsage(last_name);
			return contains_name;
		}
	}
	*/
	
	return false;
}

// Used by the EditorCameraComponent when it checks polys to see if they are Kill polys
bool ScriptContainsAnyOfTheNames(uint32 scriptName, uint32 *p_names, int numNames)
{
	Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
	Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));

	uint8 *p_script=p_script_cache->GetScript(scriptName);
	Dbg_MsgAssert(p_script,("NULL p_script for %s",Script::FindChecksumName(scriptName)));

	bool contains_name=false;
	
	Dbg_MsgAssert(p_names,("NULL p_names"));
	uint32 *p_name=p_names;
	for (int i=0; iDecrementScriptUsage(scriptName);
	
	return contains_name;		
}

} // namespace Script





================================================
FILE: Code/Gel/Scripting/parse.h
================================================
#ifndef	__SCRIPTING_PARSE_H
#define	__SCRIPTING_PARSE_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef	__SCRIPTING_SCRIPTDEFS_H
#include  // For enums
#endif

#ifndef __SCRIPTING_TOKENS_H
#include 
#endif

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

#ifdef __PLAT_NGC__
#define SCRIPT_HEADER_SIZE 32
#else
#define SCRIPT_HEADER_SIZE 12
#endif

namespace Script
{

class CStruct;
class CComponent;
class CSymbolTableEntry;
class CScript;

// Used when extracting 4 bytes from a .qb, where the 4 bytes could represent
// different types of thing. (Saves messy casting)
struct S4Bytes
{
    union
    {
        uint32 mChecksum;
		uint32 mUInt;
        int mInt;
        float mFloat;
        bool (*mpCFunction)(CStruct *pParams, CScript *pCScript);
    };
};

// Same, but for 2 bytes.
struct S2Bytes
{
    union
    {
		uint16 mUInt;
        sint16 mInt;
    };
};

// Gets 4 bytes from p_long, which may not be long word aligned.
S4Bytes Read4Bytes(const uint8 *p_long);
S2Bytes Read2Bytes(const uint8 *p_short);
uint8 *Write4Bytes(uint8 *p_buffer, uint32 val);
uint8 *Write4Bytes(uint8 *p_buffer, float floatVal);
uint8 *Write2Bytes(uint8 *p_buffer, uint16 val);

#define MAX_STORED_RANDOMS 100

#define MAKE_NEW_FIRST_DIFFER_FROM_OLD_LAST true
#ifdef __PLAT_WN32__
class CStoredRandom
#else
class CStoredRandom : public Mem::CPoolable
#endif
{
public:	
	CStoredRandom();
	~CStoredRandom();
	
	// These identify which Random operator this 'belongs' to.
	// mpToken alone is not enough because there may be two instances of a CScript running
	// that script.
	CScript *mpScript;
	const uint8 *mpToken;
	// These are also stored to further verify that this is the correct one if a match for
	// mpToken is found. They could differ due to a script reload.
	uint16 mNumItems;
	EScriptToken mType;
	
	// The following members are used by the RandomPermute operator
	uint16 mCurrentIndex;
	
	#define MAX_RANDOM_INDICES 64
	uint8 mpIndices[MAX_RANDOM_INDICES];

	void InitIndices();
	void RandomizeIndices(bool makeNewFirstDifferFromOldLast=false);
	
	// This is used by the RandomNoRepeat operator
	uint32 mLastIndex;

	
	CStoredRandom *mpNext;
	CStoredRandom *mpPrevious;
};

void ReleaseStoredRandoms(CScript *p_script);

// Used by CScript::Update
uint8 *SkipToken(uint8 *p_token);
uint8 *SkipEndOfLines(uint8 *p_token);
uint8 *DoAnyRandomsOrJumps(uint8 *p_token);

uint8 *SkipOverScript(uint8 *p_token);

void EnableExpressionEvaluatorErrorChecking();
void DisableExpressionEvaluatorErrorChecking();
uint8 *Evaluate(uint8 *p_token, CStruct *p_args, CComponent *p_result);
uint8 *FillInComponentUsingQB(uint8 *p_token, CStruct *p_args, CComponent *p_comp);

// Exported because needed by CScript when generating the structure of function 
// parameters when executing each line of script.
uint8 *AddComponentsUntilEndOfLine(CStruct *p_dest, uint8 *p_token, CStruct *p_args=NULL);

// Used by UnloadQB in file.cpp
void CleanUpAndRemoveSymbol(CSymbolTableEntry *p_sym);
// TODO: Remove? Currently used in cfuncs.cpp to delete the NodeArray, but now that we
// have the ability to unload qb files that should not be necessary any more.
// Note: Just found it is also needed in parkgen.cpp, so don't remove after all. 
void CleanUpAndRemoveSymbol(uint32 checksum);
void CleanUpAndRemoveSymbol(const char *p_symbolName);


// Used by various asserts in script.cpp
int GetLineNumber(uint8 *p_token);
const char *GetSourceFile(uint8 *p_token);
bool PointsIntoScript(uint8 *p_token, uint8 *p_script);

void CheckForPossibleInfiniteLoops(uint32 scriptName, uint8 *p_token, const char *p_fileName);

const char *GetChecksumNameFromLastQB(uint32 checksum);
void RemoveChecksumNameLookupHashTable();
uint8 *SkipToStartOfNextLine(uint8 *p_token);
void PreProcessScript(uint8 *p_token);


// Called from LoadQB in file.cpp

// By default the allocateChecksumNameLookupTable flag is set to true because the table is needed when
// generating prefix info for the nodearray, and we don't know whether the qb contains the nodearray until
// after finishing parsing it.
// The flag can be set to false when we know that checksum lookup info won't be needed & we're short of memory.
// (Eg, Gary uses this when loading qb's for the cutscenes)
void ParseQB(const char *p_fileName, uint8 *p_qb, EBoolAssertIfDuplicateSymbols assertIfDuplicateSymbols=NO_ASSERT_IF_DUPLICATE_SYMBOLS, bool allocateChecksumNameLookupTable=true);

bool ScriptContainsName(uint8 *p_script, uint32 searchName);
bool ScriptContainsAnyOfTheNames(uint32 scriptName, uint32 *p_names, int numNames);

} // namespace Script

#endif // #ifndef	__SCRIPTING_PARSE_H


================================================
FILE: Code/Gel/Scripting/script.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// script.cpp		KSH 1 Nov 2001
//
// CScript class member functions and misc script functions.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include  // for SKATE_TYPE_SKATER
#include 
#include  // For Net::Client class
#ifndef __PLAT_WN32__
#include  // For GameNet::Manager
#endif
#include 

#ifdef	__SCRIPT_EVENT_TABLE__		
#include 
#include 
#endif

//char foo[sizeof(Script::SReturnAddress)/0];

//#define SEND_SCRIPT_NAMES_TO_DEBUGGER

DefinePoolableClass(Script::CScript);

namespace Obj
{
	#ifdef __NOPT_ASSERT__
	extern bool DebugSkaterScripts;
	#endif

}

namespace Script
{

static CScript * sCurrentlyUpdating = NULL;

// Parse.cpp needs this, for getting the script pointer to send to any cfunc it
// encounters when evaluating an expression.
// It also uses it when processing RandomNoRepeat tokens.
CScript *GetCurrentScript()
{
	return sCurrentlyUpdating;
}
	
#ifdef	__NOPT_ASSERT__
// for debugging, return the info of script we are currently updating
// as the function that Asserts by it might not have a pointer 
// to the script 									  
const char  *GetCurrentScriptInfo()
{
	if (sCurrentlyUpdating)
	{
		return sCurrentlyUpdating->GetScriptInfo();
	}
	else
	{
		return "No Script currently updating";
	}
}

#ifdef SEND_SCRIPT_NAMES_TO_DEBUGGER
static void s_send_script_name(uint32 script_name, int numReturnAddresses)
{
	static uint32 sp_ignored_script_names[]=
	{
		CRCC(0x1f8909cb,"timeupscript"),
		CRCC(0x1aef674f,"game_update"),
		CRCC(0x18b3b291,"waitonegameframe"),
		CRCC(0x7c4264dd,"checkforswitchvehicles"),
		CRCC(0x98f4c454,"just_coasting"),
		CRCC(0x30e72e33,"do_random_effect"),
		CRCC(0x7737d7b8,"do_random_effect2"),
		CRCC(0x99443e63,"do_blur_effect"),
		CRCC(0x3e6ab5cb,"do_blur_effect_accept"),
		CRCC(0x9c9dfcf8,"disable_two_player_option"),
		CRCC(0x380ec7a7,"playbrakeanim"),
		CRCC(0x838116e9,"playbrakeidle"),
		CRCC(0x7ae0d8aa,"handbrake"),
		CRCC(0x9f6a63e3,"playturnanimorturnidle"),
		CRCC(0x642587a0,"cessbrake"),
		CRCC(0x603ea7f3,"waitanimfinished"),
		CRCC(0x76792736,"checkfornetbrake"),
		CRCC(0xa4f522e9,"parked_make_set_menu_item"),
		CRCC(0xbeed50b6,"make_text_sub_menu_item"),
		CRCC(0x277b7b33,"parked_make_piece_menu_item"),
		CRCC(0x3ee4627d,"doapush"),
		CRCC(0x493a52f,"helper_text_update_element"),
		CRCC(0x5479735a,"show_panel_message"),
		CRCC(0x7a449180,"DestroyMemStatsScreenElements"),
		CRCC(0x901fefcf,"setexception"),
		CRCC(0xe9242afd,"vibrateoff"),
		CRCC(0xc4c8c400,"switchonboard"),
		CRCC(0xf8d27a88,"switchoffboard"),
		CRCC(0x1195c40a,"bloodparticlesoff"),
		CRCC(0x1946686b,"skaterbloodoff"),
		CRCC(0x243e716,"verifyparam"),
		CRCC(0x2c0c9e7b,"onexceptionrun"),
		CRCC(0x64df5e4c,"WaitAnimWhilstChecking"),
		CRCC(0xcf14cc35,"ClothingLandBounce"),
		CRCC(0x46f7302f,"OnGroundExceptions"),
		CRCC(0x500eb95b,"InAirExceptions"),
		CRCC(0x10da1c88,"ClearExceptions"),
		CRCC(0xaaef4fb5,"ClearOnExceptionRun"),
		CRCC(0xac281817,"model_hide_geom"),
		CRCC(0x3b9039a5,"process_cas_command"),
		CRCC(0xdea7fe56,"goal_editor_update_cursor_position"),
	};
	
	uint32 *p_ignored_script_names=sp_ignored_script_names;
	for (uint32 i=0; iStreamMessage(&msg);
}
#endif

#endif


static CScript *sp_scripts=NULL;
static uint32 s_num_cscripts=0;

static bool	s_done_one_per_frame;

int CScript::s_next_unique_id = 0;

uint32 GetNumCScripts()
{
	return s_num_cscripts;
}
	
// For use when external functions want to step through all existing scripts.
// Pass it NULL to get the first CScript.
// Should probably eventually change all the script code to use the iterators in the 
// standard container library at some point I guess.
CScript *GetNextScript(CScript *p_script)
{
	if (p_script==NULL)
	{
		// They want the first CScript
		return sp_scripts;
	}
	else
	{
		return p_script->mp_next;
	}
}

CScript::CScript()
{
	// Zero all the members. CScript is not derived from CClass so we don't get the auto-zeroing.
	uint32 size=sizeof(CScript);
	Dbg_MsgAssert((size&3)==0,("sizeof(CScript) not a multiple of 4 ??"));
	size>>=2;
	uint32 *p_longs=(uint32*)this;
	for (uint32 i=0; imp_previous=this;
	}	
	sp_scripts=this;

	++s_num_cscripts;

	m_unique_id = s_next_unique_id++;
	
	mNode = -1;
	
	#ifdef __NOPT_ASSERT__										
	m_last_instruction_time_taken=-1.0f;
	m_total_instruction_time_taken=0.0f;
	#endif
	
	#if	0
// if we go over 100 scripts, tell us why
	static uint32 max = 100;
	if (s_num_cscripts > max)
	{
		max = s_num_cscripts;
		printf ("Record number of scripts: %d of %d\nvvvvvvvvvvvvvvvvvvvvvvvvvvv\n",CScript::SGetNumUsedItems(),CScript::SGetTotalItems());
		Script::DumpScripts();
		DumpUnwindStack(20,0);
		printf (" ^^^^^^^^^^^^^^^^^^^^^^^^\nRecord number of scripts: %d of %d\n",CScript::SGetNumUsedItems(),CScript::SGetTotalItems());

	}
	#endif

#ifdef	__SCRIPT_EVENT_TABLE__		
	mp_event_handler_table = NULL;
#endif	 

	mp_return_addresses=mp_return_addresses_small_buffer;
	mp_loops=mp_loops_small_buffer;
}

CScript::~CScript()
{
	ClearScript();
	if (mpCallbackScriptParams)
	{
		delete mpCallbackScriptParams;
	}
	// Check to see if the script has already been deleted
	// perhaps we are deleteing it twice???
	
	Dbg_MsgAssert(mp_next != (CScript *)-1,("mp_next is -1 in script we are trying to delete!!!"));
	Dbg_MsgAssert(mp_previous != (CScript *)-1,("mp_previous is -1 in script we are trying to delete!!!"));
	
	// Remove from the linked list.
	if (mp_previous==NULL) 
	{
		sp_scripts=mp_next;
	}	
	if (mp_next) 
	{
		Dbg_MsgAssert(mp_next!=mp_previous,("mp_next same as mp_prev for script we are deleting"));
		mp_next->mp_previous=mp_previous;
	}	
	if (mp_previous) 
	{
		Dbg_MsgAssert(mp_next!=mp_previous,("mp_next same as mp_prev for script we are deleting"));
		mp_previous->mp_next=mp_next;
	}	

	#ifdef	__NOPT_ASSERT__	
	mp_next = (CScript *)-1;
	mp_previous = (CScript *)-1;
	#endif
	
	// Release any stored RandomNoRepeat or RandomPermute info.
	ReleaseStoredRandoms(this);

	#ifdef	__NOPT_ASSERT__	
	#ifndef __PLAT_WN32__
	if (m_being_watched_in_debugger)
	{
		// Tell the debugger that this script has died.
		Net::MsgDesc msg;
		msg.m_Data = &m_unique_id;;
		msg.m_Length = 4;
		msg.m_Id = GameNet::vMSG_ID_DBG_SCRIPT_DIED;
		Dbg::CScriptDebugger::Instance()->StreamMessage(&msg);
	}	
	#endif
	#endif

#ifdef	__SCRIPT_EVENT_TABLE__		
	if (mp_event_handler_table)
	{
		// Remove the references to the event handler from the tracker

//		mp_event_handler_table->unregister_all(this);
		ClearEventHandlerTable();   // Need this to clean up structs and stuff
	
		if (mp_event_handler_table->m_in_immediate_use_counter)
		{
			// table still in use by its pass_event() function,
			// so leave it up to that function to destroy the table
			mp_event_handler_table->m_valid = false;
		}
		else
		{
			delete mp_event_handler_table;
		}
	}
#endif

	Dbg_MsgAssert(s_num_cscripts,("Zero s_num_cscripts"));
	--s_num_cscripts;
}

#ifdef __NOPT_ASSERT__
#ifndef __PLAT_WN32__

// This gets called whenever mScriptChecksum is changed.
void CScript::check_if_needs_to_be_watched_in_debugger()
{
	Dbg::SWatchedScript *p_watched_script_info=Dbg::CScriptDebugger::Instance()->GetScriptWatchInfo(mScriptChecksum);
	if (p_watched_script_info)
	{
		WatchInDebugger(p_watched_script_info->mStopScriptImmediately);
	}	
}

uint32 *CScript::write_callstack_entry( uint32 *p_buf, int bufferSize, 
										uint32 scriptNameChecksum, 
										uint8 *p_PC,
										CStruct *p_params,
										Obj::CObject *p_ob)
{
	Dbg_MsgAssert(bufferSize >= 8,("write_callstack_entry buffer overflow"));
	*p_buf++=scriptNameChecksum;
	*p_buf++=Script::GetLineNumber(p_PC);
	bufferSize-=8;
		
	// Find out the source file and write it in.
	const char *p_source_file_name="";
	CSymbolTableEntry *p_sym=LookUpSymbol(scriptNameChecksum);
	if (p_sym)
	{
		p_source_file_name=FindChecksumName(p_sym->mSourceFileNameChecksum);
	}
	int len=strlen(p_source_file_name)+1;
	len=(len+3)&~3;
	Dbg_MsgAssert(bufferSize >= len,("write_callstack_entry buffer overflow"));	
	strcpy((char*)p_buf,p_source_file_name);
	p_buf+=len/4;
	bufferSize-=len;

	Dbg_MsgAssert(bufferSize >= 4,("write_callstack_entry buffer overflow"));
	*p_buf++=(uint32)p_ob;
	bufferSize -= 4;
	if (p_ob)
	{
		Dbg_MsgAssert(bufferSize >= 12,("write_callstack_entry buffer overflow"));
		*p_buf++=p_ob->GetType();
		*p_buf++=p_ob->GetID();
		*p_buf++=p_ob->MainScriptIs(this);
		bufferSize -= 12;
		
		// Write out the object's tags.
		int bytes_written=Script::WriteToBuffer(p_ob->GetTags(),(uint8*)p_buf,bufferSize);
		bytes_written=(bytes_written+3)&~3;
		Dbg_MsgAssert(bufferSize >= bytes_written,("write_callstack_entry buffer overflow"));
		p_buf+=bytes_written/4;
		bufferSize-=bytes_written;
	}
	
	// Write out the parameters.
	int bytes_written=Script::WriteToBuffer(p_params,(uint8*)p_buf,bufferSize);
	bytes_written=(bytes_written+3)&~3;
	Dbg_MsgAssert(bufferSize >= bytes_written,("write_callstack_entry buffer overflow"));
	p_buf+=bytes_written/4;
	
	return p_buf;
}

// Sets the comment string, which the script debugger displays.
// This is so that the c-code can describe where the script was created from.
void CScript::SetCommentString(const char *p_comment)
{
	int len=strlen(p_comment);
	if (len>MAX_COMMENT_STRING_CHARS)
	{
		len=MAX_COMMENT_STRING_CHARS;
	}
	for (int i=0; i= 16,("Dbg::gpDebugInfoBuffer overflow"));
	// Stuff that identifies the CScript
	*p_buf++=(uint32)this;
	*p_buf++=m_unique_id;
	*(float*)p_buf=m_last_instruction_time_taken;
	++p_buf;
	*(float*)p_buf=m_total_instruction_time_taken;
	++p_buf;
	
	// The originating script info, ie which script this got spawned from if it was created
	// by a SpawnScript command. Will be two zeros if no such info is available.
	*p_buf++=m_originating_script_line_number;
	*p_buf++=m_originating_script_name;
	space_left-=24;

	// Write out the comment string.	
	int comment_len=strlen(mp_comment_string)+1;
	int comment_space_needed=(comment_len+3)&~3;
	Dbg_MsgAssert(space_left >= comment_space_needed,("Dbg::gpDebugInfoBuffer overflow"));
	char *p_source=mp_comment_string;
	char *p_dest=(char*)p_buf;
	for (int i=0; i= structure_bytes_written,("Dbg::gpDebugInfoBuffer overflow"));
	p_buf+=structure_bytes_written/4;
	space_left-=structure_bytes_written;
	
	// Write out the callstack
	Dbg_MsgAssert(space_left >= 4,("Dbg::gpDebugInfoBuffer overflow"));
	// Adding one because the current script is considered the last entry in the callstack
	// when displayed in the debugger.
	*p_buf++=m_num_return_addresses+1;
	space_left-=4;
	
	uint32 *p_old=p_buf;
    for (int i=0; i= 20,("Dbg::gpDebugInfoBuffer overflow"));
	
	// Whether spawned or not
	*p_buf++=mIsSpawned;
	
	// Wait state
	*p_buf++=m_wait_type;
	
	// Paused or not
	*p_buf++=mPaused;
	
	// Whether 'session specific' or not
	*p_buf++=mNotSessionSpecific;
	
	// Script debugger needs this so that it knows to expand and highlight the
	// last entry in the callstack when it receives the info.
	*p_buf++=m_single_step_mode;
	
	space_left-=20;
	
	int message_size=((uint8*)p_buf)-((uint8*)Dbg::gpDebugInfoBuffer);
	Dbg_MsgAssert(message_size <= Dbg::SCRIPT_DEBUGGER_BUFFER_SIZE,("Script info message too big !"));
	Dbg_MsgAssert(message_size+space_left == Dbg::SCRIPT_DEBUGGER_BUFFER_SIZE,("What ??"));
	
	uint32 checksum=Crc::GenerateCRCCaseSensitive((const char *)Dbg::gpDebugInfoBuffer,message_size);
	if (checksum != m_last_transmitted_info_checksum || definitely_transmit)
	{
		printf("CScript id=0x%08x Sending script info, message size=%d\n",m_unique_id,message_size);
		
		Net::MsgDesc msg;
		msg.m_Data = Dbg::gpDebugInfoBuffer;
		msg.m_Length = message_size;
		msg.m_Id = GameNet::vMSG_ID_DBG_CSCRIPT_INFO;
		Dbg::CScriptDebugger::Instance()->StreamMessage(&msg);
		
		m_last_transmitted_info_checksum=checksum;
	}	
}

void CScript::TransmitBasicInfoToDebugger()
{
	uint32 *p_buf=Dbg::gpDebugInfoBuffer;
	
	*p_buf++=m_unique_id;
	*p_buf++=mScriptChecksum;
	*p_buf++=Script::GetLineNumber(mp_pc);
		
	// Find out the source file and write it in.
	const char *p_source_file_name="";
	CSymbolTableEntry *p_sym=LookUpSymbol(mScriptChecksum);
	if (p_sym)
	{
		p_source_file_name=FindChecksumName(p_sym->mSourceFileNameChecksum);
	}
	int len=strlen(p_source_file_name)+1;
	len=(len+3)&~3;
	strcpy((char*)p_buf,p_source_file_name);
	p_buf+=len/4;

	int message_size=((uint8*)p_buf)-((uint8*)Dbg::gpDebugInfoBuffer);
	Dbg_MsgAssert(message_size <= Dbg::SCRIPT_DEBUGGER_BUFFER_SIZE,("Script info message too big !"));
	
	//printf("Sending vMSG_ID_DBG_BASIC_CSCRIPT_INFO\n");
	Net::MsgDesc msg;
	msg.m_Data = Dbg::gpDebugInfoBuffer;
	msg.m_Length = message_size;
	msg.m_Id = GameNet::vMSG_ID_DBG_BASIC_CSCRIPT_INFO;
	Dbg::CScriptDebugger::Instance()->StreamMessage(&msg);
}

void CScript::WatchInDebugger(bool stopScriptImmediately)
{
	m_being_watched_in_debugger=true;
	
	if (stopScriptImmediately)
	{
		m_single_step_mode=STEP_INTO;
	}
	else
	{
		m_single_step_mode=OFF;
	}	
}

void CScript::StopWatchingInDebugger()
{
	m_being_watched_in_debugger=false;
}	

void CScript::DebugStop()
{
	bool was_running=m_single_step_mode==OFF;
	m_single_step_mode=WAITING;
	
	if (was_running)
	{
		// This is so that the debugger registers the fact that m_single_step_mode has changed
		// and hence updates the window to indicate that the script has stopped.
		TransmitInfoToDebugger();
	}	
}

void CScript::DebugStepInto()
{
	m_single_step_mode=STEP_INTO;
	Dbg::CScriptDebugger::Instance()->ScriptDebuggerUnpause();	
}

void CScript::DebugStepOver()
{
	m_single_step_mode=STEP_OVER;
	Dbg::CScriptDebugger::Instance()->ScriptDebuggerUnpause();	
}

void CScript::DebugGo()
{
	m_single_step_mode=OFF;
	Dbg::CScriptDebugger::Instance()->ScriptDebuggerUnpause();	
}
#endif // #ifndef __PLAT_WN32__
#endif // #ifdef __NOPT_ASSERT__

void CScript::SetWait(EWaitType type, Obj::CBaseComponent *p_component)
{
	m_wait_type=type;
	Dbg_MsgAssert(mp_wait_component==NULL,("\n%s\nExpected mp_wait_component to be NULL here?",GetScriptInfo()));
	mp_wait_component=p_component;
}

void CScript::ClearWait()
{
	m_wait_type=WAIT_TYPE_NONE;
	mp_wait_component=NULL;
}

void CScript::Block()
{
	m_wait_type=WAIT_TYPE_BLOCKED;
}

void CScript::UnBlock()
{
	m_wait_type=WAIT_TYPE_NONE;
}

bool CScript::getBlocked()
{
	return m_wait_type==WAIT_TYPE_BLOCKED;
}

bool CScript::GotScript()
{
	return mp_pc?true:false;
}

void CScript::SwitchOnIfDebugging()
{
#ifdef __NOPT_ASSERT__
	m_if_debug_on=true;
#endif
}

void CScript::SwitchOffIfDebugging()
{
#ifdef __NOPT_ASSERT__
	m_if_debug_on=false;
#endif
}

void CScript::set_script(uint32 scriptChecksum, uint8 *p_script, CStruct *p_params, Obj::CObject *p_object)
{
	#ifdef	__NOPT_ASSERT__	
	#ifdef SEND_SCRIPT_NAMES_TO_DEBUGGER
	// Tell the debugger that this script is begin run.
	s_send_script_name(scriptChecksum,0);
	#endif
	#endif
	
	#ifdef __NOPT_ASSERT__
	if (Obj::DebugSkaterScripts)
	{
		if (p_object && p_object->GetType()==SKATE_TYPE_SKATER)
		{
			printf("%d: ############### %s ###############  [%i]\n",(int)Tmr::GetRenderFrame(),FindChecksumName(scriptChecksum),m_unique_id);
		}
	}	
	#endif

	CStruct *p_params_copy=MakeParamsSafeFromDeletionByClearScript(p_params);
	
	// Reset the script.
	ClearScript();
	
	Dbg_MsgAssert(mp_function_params==NULL,("mp_function_params not NULL"));
	mp_function_params=new CStruct;
	
	#ifdef __NOPT_ASSERT__ 
	// This is so that the structure can print line number info in case one of
	// the CStruct member function asserts goes off.
	mp_function_params->SetParentScript(this);
	#endif
		
	mScriptChecksum=scriptChecksum;
	#ifdef __NOPT_ASSERT__ 
	check_if_needs_to_be_watched_in_debugger();
	#endif	
    mpObject=p_object;
	
	Dbg_MsgAssert(mp_params==NULL,("mp_params not NULL"));
	mp_params=new CStruct;
	#ifdef __NOPT_ASSERT__ 
	// This is so that the structure can print line number info in case one of
	// the CStruct member function asserts goes off.
	mp_params->SetParentScript(this);
	#endif
	
	if (p_script)
	{
		mp_pc=p_script;	
	}
	else
	{
		Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
		Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));
	
		mp_pc=p_script_cache->GetScript(scriptChecksum);
		if (!mp_pc)
		{
			#ifdef	__NOPT_ASSERT__
			if (Script::GetInteger(CRCD(0x22d1f89,"AssertOnMissingScripts")))
			{
				Dbg_MsgAssert(false, ("Script %s not found.",FindChecksumName(scriptChecksum)));
			}
			#endif
			if (p_params_copy)
			{
				delete p_params_copy;
			}
			ClearScript();
			ClearEventHandlerTable();
			return;	
		}
	}
	
	// Load the default parameters into mp_params
	mp_pc=AddComponentsUntilEndOfLine(mp_params, mp_pc);
		
	// Now merge what is in p_params onto mp_params.
	if (p_params_copy)
	{
		mp_params->AppendStructure(p_params_copy);
		delete p_params_copy;
	}
	else
	{
		mp_params->AppendStructure(p_params);
	}
	
	if (GetOnExitScriptChecksum())
	{
		// Pass in the name of the exception so that certain exceptions can be ignored.
		Script::CStruct *pFoo=new Script::CStruct;
// Mick - that has no meaning now, as this is triggered by everything
//		pFoo->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int)p_entry->exception);
		// RunScript(GetOnExitScriptChecksum(), pFoo, mpObject );
		// Dan: interrupt instead of run
		uint32 checksum = GetOnExitScriptChecksum();
		SetOnExitScriptChecksum(0);	// clear it once it has been run
		Interrupt(checksum, pFoo);
		delete pFoo;
	}
}

void CScript::SetScript(const SStructScript *p_structScript, CStruct *p_params, Obj::CObject *p_object)
{
	Dbg_MsgAssert(p_structScript,("NULL p_structScript"));
	Dbg_MsgAssert(p_structScript->mNameChecksum,("Zero p_structScript->mNameChecksum"));
	Dbg_MsgAssert(p_structScript->mpScriptTokens,("NULL p_structScript->mpScriptTokens"));
	
	// Calculate the size of the script
	uint32 size=SkipOverScript(p_structScript->mpScriptTokens)-p_structScript->mpScriptTokens;
	
	// Allocate a buffer and make a copy of the script (since the source may get deleted)
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	uint8 *p_new_script=(uint8*)Mem::Malloc(size);
	Mem::Manager::sHandle().PopContext();
	
	uint8 *p_source=p_structScript->mpScriptTokens;
	uint8 *p_dest=p_new_script;
	for (uint32 i=0; imNameChecksum,p_new_script,p_params,p_object);
	
	// Now set mp_struct_script, so that the buffer can be deleted later.
	// Note: The setting of mp_struct_script cannot be done before set_script, because set_script calls 
	// ClearScript, which would have deleted mp_struct_script if it was not NULL.
	mp_struct_script=p_new_script;
}

void CScript::SetScript(uint32 scriptChecksum, CStruct *p_params, Obj::CObject *p_object)
{
	set_script(scriptChecksum,NULL,p_params,p_object);
}

void CScript::SetScript(const char *p_scriptName, CStruct *p_params, Obj::CObject *p_object)
{
	SetScript(Crc::GenerateCRCFromString(p_scriptName),p_params,p_object);
}

CStruct *CScript::MakeParamsSafeFromDeletionByClearScript(CStruct *p_params)
{
	// It may be that p_params is mp_params or mp_function_params, or is a substructure of one
	// of those.
	// For example, when a manual is triggered, the passed p_params will be mp_function_params.
	// Since ClearScript() deletes both mp_params and mp_function_params,
	// this will result in p_params getting deleted too.
	// So if either mp_params or mp_function_params references p_params, store the contents
	// of p_params in p_new_params & return it.
	// This is a speed optimization, because it would be slow to always store the contents of
	// p_params. (Copying structures can be slow)
	CStruct *p_new_params=NULL;
	if (p_params)
	{
		if ((mp_function_params && mp_function_params->References(p_params)) || 
			(mp_params && mp_params->References(p_params)))
		{
			p_new_params=new CStruct;
			p_new_params->AppendStructure(p_params);
		}
	}		
	
	return p_new_params;
}

// Stops the script from executing, and deletes the parameters, clears the stack, etc.
// (REQUIREMENT: Must set mp_pc to NULL, so script will be deleted by UpdateSpawnedScripts)
void CScript::ClearScript()
{
	if (mp_struct_script)
	{
		Mem::Free(mp_struct_script);
		mp_struct_script=NULL;
	}

	Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
	Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));
	if (mp_pc)
	{
		// If a script was being executed, then decrement its usage in the script cache since
		// it is no longer needed by this CScript.
		p_script_cache->DecrementScriptUsage(mScriptChecksum);
	}
	
	mp_pc=NULL;	
	
	if (mp_params)
	{
		delete mp_params;
		mp_params=NULL;
	}
	if (mp_function_params)
	{
		delete mp_function_params;
		mp_function_params=NULL;
	}	
	
    for (int i=0; iDecrementScriptUsage(mp_return_addresses[i].mScriptNameChecksum);
		
        Dbg_MsgAssert(mp_return_addresses[i].mpParams,("NULL mpParams in return stack"));
		if (mp_return_addresses[i].mpParams)
		{
			delete mp_return_addresses[i].mpParams;
			mp_return_addresses[i].mpParams=NULL;
		}	
    }    
	
	m_num_return_addresses=0;
	if (mp_return_addresses != mp_return_addresses_small_buffer)
	{
		Dbg_MsgAssert(mp_return_addresses,("NULL mp_return_addresses ?"));
		Mem::Free(mp_return_addresses);
	}
	mp_return_addresses=mp_return_addresses_small_buffer;


	mp_current_loop=NULL;
	if (mp_loops != mp_loops_small_buffer)
	{
		Dbg_MsgAssert(mp_loops,("NULL mp_loops ?"));
		Mem::Free(mp_loops);
	}
	mp_loops=mp_loops_small_buffer;
	
	
	m_wait_timer=0;
	m_wait_type=WAIT_TYPE_NONE;
	mp_wait_component=NULL;
	
	mpObject=NULL;
	mScriptChecksum=0;

	m_interrupted=false;
	m_skip_further_process_waits=false;
	
		
	#ifdef __NOPT_ASSERT__
	m_if_status=0;
	#endif
}

// Cleans up the script's event handler table.  Call after ClearScript if you are not about to reset the script
// (REQUIREMENT: Must set mp_pc to NULL, so script will be deleted by UpdateSpawnedScripts)
void CScript::ClearEventHandlerTable()
{
	#ifdef	__SCRIPT_EVENT_TABLE__		
	if (mp_event_handler_table)
	{
		// Remove the references to the event handler from the tracker
		mp_event_handler_table->unregister_all(this);
		
		mp_event_handler_table->remove_group(CRCD(0x8b713e0e, "all_groups"));
	}
	#endif
}

// Restarts the script.
void CScript::Restart()
{
	// This is a structure for temporarily storing the old parameters, since the SetScript call will clear
	// everything in the script, including deleting the structure pointed to by mp_params, and the call stack.
	CStruct *p_params=new CStruct;
	
	if (m_num_return_addresses)
	{
		// If there are things in the call stack, then the script that we want to restart is
		// the first one in the call stack, since this is the original script.
		Dbg_MsgAssert(mp_return_addresses[0].mpParams,("NULL p_params in return stack"));
		*p_params=*mp_return_addresses[0].mpParams;
		SetScript(mp_return_addresses[0].mScriptNameChecksum,p_params,mp_return_addresses[0].mpObject);
	}
	else
	{
		// If there is nothing in the call stack, restart the current script.
		*p_params=*mp_params;
		// The parameters are now safe in p_params, so restart the script by reinitialising it.
		SetScript(mScriptChecksum,p_params,mpObject);
	}
		
	// Delete the temporary structure.
	delete p_params;
}

// Stops the script from being associated with an object, ie, sets mpObject to NULL.
// Allows the script to continue executing though, so the script must not contain any
// further member functions, otherwise the code will assert.
// This function also sets m_wait_type=WAIT_TYPE_NONE; to ensure the Update function does
// not assert if it is waiting for the object to finish doing something.
void CScript::DisassociateWithObject(Obj::CObject *pObjectToDisconnectFrom)
{
	if (!pObjectToDisconnectFrom) return;

	#ifdef	__NOPT_ASSERT__
	pObjectToDisconnectFrom->SetLockAssertOff();	
	#endif

	if (mpObject == pObjectToDisconnectFrom)
	{
		mpObject=NULL;
	}
	for (int i=0; imSourceFileNameChecksum),FindChecksumName(mScriptChecksum));
			}
		}
		else
		{
			sprintf(sp_script_info,"GetScriptInfo: Script symbol not found. Strange.");
		}	
	}
		
	strcat(sp_script_info,"\nScript call stack:\n");
	CSymbolTableEntry *p_sym=LookUpSymbol(mScriptChecksum);
	if (p_sym)
	{
		strcat(sp_script_info,FindChecksumName(p_sym->mSourceFileNameChecksum));
	}
	else
	{
		strcat(sp_script_info,"Unknown source file");
	}	
	sprintf(sp_script_info+strlen(sp_script_info)," line %d, in ",GetLineNumber(mp_pc));
	strcat(sp_script_info,FindChecksumName(mScriptChecksum));
	strcat(sp_script_info,"\n");
    for (int i=m_num_return_addresses-1; i>=0; --i)
    {
		if (mp_return_addresses[i].mpParams)
		{
			sprintf(sp_script_info+strlen(sp_script_info)," line %d, in ",GetLineNumber(mp_return_addresses[i].mpReturnAddress));
			strcat(sp_script_info,FindChecksumName(mp_return_addresses[i].mScriptNameChecksum));
			strcat(sp_script_info,"\n");
		}	
		else
		{
			strcat(sp_script_info,"-\n");
		}	
    }    
		
		
	Dbg_MsgAssert(strlen(sp_script_info)mSourceFileNameChecksum);
	}
	else
	{
		return "Unknown";
	}
}		
#else
// Need these to be fast on Xbox because they are used in Dbg_MsgAsserts(), which will call it
// regardless of whether the assert condition is false.
const char *CScript::GetScriptInfo()
{
	return "";
}
int CScript::GetCurrentLineNumber(void)
{
	return 0;
}
const char *CScript::GetSourceFile(void)
{
	return "";
}		
#endif // #ifndef __PLAT_XBOX__
#endif


#ifdef __NOPT_ASSERT__
#ifndef __PLAT_WN32__
// For testing how many scripts end up needing the bigger buffer.
bool CScript::UsingBigCallstackBuffer()
{
	if (mp_return_addresses == mp_return_addresses_small_buffer)
	{
		return false;
	}
	return true;	
}

bool CScript::UsingBigLoopBuffer()
{
	if (mp_loops == mp_loops_small_buffer)
	{
		return false;
	}
	return true;	
}

#endif
#endif

// Used by CScript::Update when calling a script.
// call_script is also used by CScript::Interrupt below.
void CScript::call_script(uint32 newScriptChecksum, uint8 *p_script, CStruct *p_params, Obj::CObject *p_object, bool interrupt)
{			  
	// K: If calling a script when nothing is currently being run, jump to it instead.
	// This is so that the mp_function_params gets created.
	// Fixes a bug where when a 2-player trick attack game was started, it would immediately
	// assert because call_script was called straight away on a newly created CScript.
	// (CGoal::RunCallbackScript would run a script which would do a MakeSkaterGosub, and the
	// skater's script was not running anything at that point)
	if (!mp_pc)
	{
		set_script(newScriptChecksum,p_script,p_params,p_object);
		
		// This may not really be necessary since there is no script to return to that might be affected,
		// but set it anyway for consistency.
		m_interrupted=interrupt;
		return;
	}

	// Calling a subroutine, so store the current script info on the stack.
	
	if (mp_return_addresses == mp_return_addresses_small_buffer && 
		m_num_return_addresses == RETURN_ADDRESSES_SMALL_BUFFER_SIZE)
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
		mp_return_addresses=(SReturnAddress*)Mem::Malloc(MAX_RETURN_ADDRESSES * sizeof(SReturnAddress));
		Mem::Manager::sHandle().PopContext();
		
		for (int i=0; imScriptNameChecksum=mScriptChecksum;
    p_stack_top->mpParams=mp_params;
    p_stack_top->mpReturnAddress=mp_pc;
	p_stack_top->mpObject=mpObject;
	p_stack_top->mpLoop=mp_current_loop;
	#ifdef __NOPT_ASSERT__
	p_stack_top->m_if_status=m_if_status;
	#endif
	
	p_stack_top->mWaitType=m_wait_type;
	p_stack_top->mpWaitComponent=mp_wait_component;
	p_stack_top->mWaitTimer=m_wait_timer;
	p_stack_top->mWaitPeriod=m_wait_period;
	p_stack_top->mStartTime=m_start_time;
	
	p_stack_top->mInterrupted=m_interrupted;
    
	// Set the new mScriptChecksum
    mScriptChecksum=newScriptChecksum;
	#ifdef __NOPT_ASSERT__ 
	check_if_needs_to_be_watched_in_debugger();
	#endif	
	
	// Set the new mp_pc
    mp_pc=p_script;
    Dbg_MsgAssert(mp_pc,("NULL p_script sent to call_script"));
	
	// Create a new parameters structure, and fill it in using the default values listed after
	// the script name.
	// Note: Not asserting if mp_params is not NULL, because it probably won't be.
	// The old value of mp_params has been safely stored in the mp_return_addresses array above.
    mp_params=new CStruct;
	#ifdef __NOPT_ASSERT__ 
	// This is so that the structure can print line number info in case one of
	// the CStruct member function asserts goes off.
	mp_params->SetParentScript(this);
	#endif
	mp_pc=AddComponentsUntilEndOfLine(mp_params,mp_pc);
	
	// Now overlay the parameters passed to the script.
	if (p_params)
	{
		*mp_params+=*p_params;
	}	
	
	// Set the object pointer, which may get changed if executing another object's
	// script using the : operator.
	mpObject=p_object;
	
	ClearWait();
	
	#ifdef __NOPT_ASSERT__
	// Not in an if-statement on starting the new routine.
	m_if_status=0;
	#endif
	
	m_interrupted=interrupt;
}

// Uses the above function to implement an interrupt.
// This will jump to the passed script and also do one call to update.
// Once the interrupt script is finished it will return to what it was doing before.
EScriptReturnVal CScript::Interrupt(uint32 newScriptChecksum, CStruct *p_params)
{
	Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
	Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));
	uint8 *p_script=p_script_cache->GetScript(newScriptChecksum);

	// The true means interrupt the script, which means that the call to Update will not continue
	// execution of the interrupted script when the called script finishes.
	call_script(newScriptChecksum,p_script,p_params,mpObject,true);
	return Update();
}

bool CScript::Finished()
{
	return mp_pc && *mp_pc==ESCRIPTTOKEN_KEYWORD_ENDSCRIPT;
}

#ifdef __NOPT_ASSERT__
static bool sExcludeFromSkaterDebug(uint32 name)
{
	CArray *p_array=GetArray(0xdaa3a3a5/*ExcludeFromSkaterDebug*/,NO_ASSERT);
	if (!p_array)
	{
		return false;
	}
		
	for (uint32 i=0; iGetSize(); ++i)
	{
		if (name==p_array->GetChecksum(i))
		{
			return true;
		}
	}
	
	return false;		
}
#endif

// This looks at m_wait_type, does the appropriate wait logic, and resets
// m_wait_type back to WAIT_TYPE_NONE if the wait has finished.
// Otherwise m_wait_type is left the same. 
void CScript::process_waits()
{
	switch (m_wait_type)
	{
		case WAIT_TYPE_NONE:
			break;
			
		case WAIT_TYPE_COUNTER:
			if (m_wait_timer) 
			{
				--m_wait_timer;
			}
			else
			{
				// Finished counting down, so reset wait type 
				// so that script execution continues.
				m_wait_type=WAIT_TYPE_NONE;
			}	
			break;

		case WAIT_TYPE_ONE_PER_FRAME:
			if (m_wait_timer) 
			{
				--m_wait_timer;
			}
			else
			{
				if (s_done_one_per_frame)
				{
					// already done one this frame
				}
				else
				{
					// Finished counting down, so reset wait type 
					// so that script execution continues.
					s_done_one_per_frame = true;
					m_wait_type=WAIT_TYPE_NONE;
				}
			}
			break;

			
		case WAIT_TYPE_TIMER:
		{
//			if (Tmr::ElapsedTime(m_start_time) >= m_wait_period)
			// Mick, instead of using global time, we decrement the timer whenever 
			// the script is updated.  That way paused scripts will be genuinely paused
			//
			// Note, I use m_start_time as a patch variable
			// if it is not zero, I know we have not been round this loop
			// at least once.  
			// If you feel like changing this, then note that m_start_time gets pushed on the stack 
			// when calling another script, you you will need to duplicate that functionality
			// 
			int tick = (int) (Tmr::FrameLength() * 1000.0f);			
			if ((int)m_wait_period < tick)			
			{
				if (m_start_time == 0)	   	// only if we've waited at least one frame
				{
					// Finished counting down, so reset wait type 
					// so that script execution continues.
					m_wait_type=WAIT_TYPE_NONE;
				}
				else
				{
					// finished counting down, but this was the first frame.
					// so we don't want to exit, as we cannot have a zero frame delay if 
					// the time specified was not zero
					// be we want for sure to exit next time around, so set the wait period to 0
					//
					m_wait_period = 0;
				}
			}
			else
			{
				m_wait_period -= tick;
			}
			m_start_time = 0;			// flag that we have waited at least one frame
			break;	
		}	
		case WAIT_TYPE_BLOCKED:	
			// Nothing to do except wait for whatever blocked this script to unblock it.
			break;
			
		// These are the object wait options, which only apply if the script
		// has a parent object.		
		case WAIT_TYPE_OBJECT_MOVE:	
		case WAIT_TYPE_OBJECT_ANIM_FINISHED:	
		case WAIT_TYPE_OBJECT_JUMP_FINISHED:	
		case WAIT_TYPE_OBJECT_STOP_FINISHED:
		case WAIT_TYPE_OBJECT_ROTATE:
		case WAIT_TYPE_STREAM_FINISHED:
			// Call the parent object's wait function.
			Dbg_MsgAssert(mp_wait_component,("\n%s\nNULL mp_wait_component, cannot call ProcessWait",GetScriptInfo()));
			mp_wait_component->ProcessWait(this);
			break;
			
		default:
			Dbg_MsgAssert(0,("\n%s\nBad wait type value of %d",GetScriptInfo(),m_wait_type));
			break;
	}		
}

void CScript::load_function_params()
{
	#ifdef STOPWATCH_STUFF
	pFunctionParamsStopWatch->Start();
	#endif
	
	// Clear the function parameters structure and add the new params, using mp_params to get
	// any parameters referenced using the <,> operators.
	Dbg_MsgAssert(mp_function_params,("NULL mp_function_params"));
	mp_function_params->Clear();
	// AddComponentsUntilEndOfLine will assert if mp_pc is NULL
	mp_pc=AddComponentsUntilEndOfLine(mp_function_params,mp_pc,mp_params);
	
	#ifdef STOPWATCH_STUFF
	pFunctionParamsStopWatch->Stop();
	#endif
}

// Runs the specified member function. Uses mpObject by default, but uses p_substituteObject 
// if it is not NULL. p_substituteObject defaults to NULL if it is not specified.
// Does not affect mp_pc
bool CScript::run_member_function(uint32 functionName, Obj::CObject *p_substituteObject)
{
	Obj::CObject *p_obj;
	// By default member function calls operate on mpObject, but if
	// a substitute object has been specified use that instead.
	if (p_substituteObject)
	{
		p_obj=p_substituteObject;
	}	
	else
	{
		p_obj=mpObject;
	}	
	
	bool return_value=false;
	
	if (p_obj)
	{
		#ifdef STOPWATCH_STUFF
		pUpdateStopWatch->Stop();
		pCFunctionStopWatch->Start();
		#endif
		
		#ifdef STOPWATCH_STUFF
		Tmr::CPUCycles TimeBefore,TimeAfter;
		TimeBefore=Tmr::GetTimeInCPUCycles();
		#endif
		
		return_value=p_obj->CallMemberFunction(functionName,mp_function_params,this);
		
		#ifdef STOPWATCH_STUFF
		TimeAfter=Tmr::GetTimeInCPUCycles();
		float TimeTaken;
		TimeTaken=((int)(TimeAfter-TimeBefore))/150.0f;
		if (TimeTaken>200)
		{
			//SSymbolTableEntry *p_sym=LookUpSymbol(mScriptChecksum);
			//printf("Memb: %s, %f (%s, line %d)\n",FindChecksumName(NameChecksum),TimeTaken,p_sym->pFilename,GetLineNumber(mp_pc));
		}	
		
		p_entry->mAverageExecutionTime = (p_entry->mAverageExecutionTime*p_entry->mNumCalls+TimeTaken)/(p_entry->mNumCalls+1);
		++p_entry->mNumCalls;
		p_entry->mNumCallsInThisFrame=600;
		#endif
		
		#ifdef STOPWATCH_STUFF
		pCFunctionStopWatch->Stop();
		pUpdateStopWatch->Start();
		#endif
	}	
	else
	{
		#ifdef __PLAT_WN32__
		if (functionName != 0xb3c262ec) // "DisassociateFromObject" is okay, no harm in trying to break non-existent association
		{
			Dbg_MsgAssert(0,("\n%s\nTried to call member function %s from a script\nnot associated with a CObject",GetScriptInfo(),FindChecksumName(functionName)));
		}
		#else
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

		// It network games, it's expected that some objects will no longer exist by the time
		// some object member functions are called via remote scripts.  One example being, when
		// someone joins a game-in-progress, they will be instructed to execute a series of scripts
		// and it's possible that the objects which originally spawned those scripts are no 
		// longer in the game or that the client has yet to create skaters for them.  So in 
		// those cases, just print a warning and try to continue processing the script
		if( gamenet_man->InNetGame())
		{   
			Dbg_Warning( "\n%s\nTried to call member function %s from a script\nnot associated with a CObject",GetScriptInfo(),FindChecksumName(functionName));
		}
		else if (functionName != 0xb3c262ec) // "DisassociateFromObject" is okay, no harm in trying to break non-existent association
		{
			Dbg_MsgAssert(0,("\n%s\nTried to call member function %s from a script\nnot associated with a CObject",GetScriptInfo(),FindChecksumName(functionName)));
		}
		#endif
	}
	
	return return_value;
}

// Executes the passed c-function pointer, using the current mp_function_params as parameters.
bool CScript::run_cfunction(bool (*p_cfunc)(CStruct *pParams, CScript *pCScript))
{
	Dbg_MsgAssert(p_cfunc,("\n%s\nNULL p_cfunc",GetScriptInfo()));
	
	#ifdef STOPWATCH_STUFF
	pUpdateStopWatch->Stop();
	pCFunctionStopWatch->Start();
	#endif
	
	#ifdef STOPWATCH_STUFF
	Tmr::CPUCycles TimeBefore,TimeAfter;
	TimeBefore=Tmr::GetTimeInCPUCycles();
	#endif

	/*
	Tmr::Time TimeBefore,TimeAfter;
	TimeBefore=Tmr::GetTimeInCPUCycles();
	*/
	
	bool return_value=(*p_cfunc)(mp_function_params,this);

	/*
	TimeAfter=Tmr::GetTimeInCPUCycles();
	float TimeTaken;
	TimeTaken=((int)(TimeAfter-TimeBefore))/150.0f;
	if (TimeTaken>Script::GetFloat("Threshold"))
	{
		CArray *p_exclude=Script::GetArray("Exclude");
		bool exclude=false;
		for (uint32 i=0; iGetSize(); ++i)
		{
			if (p_exclude->GetChecksum(i)==name_checksum)
			{
				exclude=true;
				break;
			}
		}
		if (!exclude)
		{
			CSymbolTableEntry *p_sym=LookUpSymbol(mScriptChecksum);
			printf("CFunc: %s, %f (%s, line %d)\n",FindChecksumName(name_checksum),TimeTaken,FindChecksumName(p_sym->mSourceFileNameChecksum),GetLineNumber(mp_pc));
		}	
	}	
	*/

	
	#ifdef STOPWATCH_STUFF
	TimeAfter=Tmr::GetTimeInCPUCycles();
	float TimeTaken;
	TimeTaken=((int)(TimeAfter-TimeBefore))/150.0f;
	if (TimeTaken>200)
	{
		//SSymbolTableEntry *p_sym=LookUpSymbol(mScriptChecksum);
		//printf("CFunc: %s, %f (%s, line %d)\n",FindChecksumName(NameChecksum),TimeTaken,p_sym->pFilename,GetLineNumber(mp_pc));
	}	
	p_entry->mAverageExecutionTime = (p_entry->mAverageExecutionTime*p_entry->mNumCalls+TimeTaken)/(p_entry->mNumCalls+1);
	++p_entry->mNumCalls;
	p_entry->mNumCallsInThisFrame=600;
	#endif
	
	
	#ifdef STOPWATCH_STUFF
	pCFunctionStopWatch->Stop();
	pUpdateStopWatch->Start();
	#endif
	
	return return_value;
}

// Gets the name from mp_pc.
// If mp_pc points to a <,> type name, it will look up that name in mp_params.
// It will advance mp_pc to after the name.
uint32 CScript::get_name()
{
	uint32 name_checksum=0;
	
	switch (*mp_pc)
	{
		case ESCRIPTTOKEN_KEYWORD_ALLARGS:
		{
			++mp_pc;
			mp_params->GetChecksum(NO_NAME,&name_checksum);
			break;
		}
			
		case ESCRIPTTOKEN_ARG:
		{
			++mp_pc;
			Dbg_MsgAssert(*mp_pc==ESCRIPTTOKEN_NAME,("\n%s\nExpected '<' token to be followed by a name.",GetScriptInfo()));
			++mp_pc;
			uint32 arg_checksum=Read4Bytes(mp_pc).mChecksum;
			mp_pc+=4;
			mp_params->GetChecksum(arg_checksum,&name_checksum);
			break;
		}

		case ESCRIPTTOKEN_NAME:
		{
			++mp_pc;
			name_checksum=Read4Bytes(mp_pc).mChecksum;
			mp_pc+=4;
			break;
		}
		
		default:
		{
			Dbg_MsgAssert(0,("\n%s\nUnexpected '%s' token when expecting some sort of name",GetScriptInfo(),GetTokenName((EScriptToken)*mp_pc)));
			break;
		}	
	}
	
	return name_checksum;
}

// Executes the command pointed to by mp_pc, and returns true or false, which is the value to
// be used by any preceding if-statement.
// After executing the command, mp_pc will have changed. It might be pointing to the next command in
// the current script, or to a command in a different script, or it might even be NULL, who knows?
// A 'command' is either a cfunc,member func, or script call, in which the command has the form of
// some sort of name followed by a list of parameters to be sent to it.
// It also covers the direct setting of one of the script's parameters, such as =3
// It also covers expressions enclosed in parentheses that can be used in if's, eg if (1>0)
// Note that like in C, expressions on their own are valid commands, eg (23*67), but unless preceded
// by an if their result will be discarded.
//
// The term 'command' does not cover things like if's, begin-repeat's, or switch statements.
// Generally the logic for interpreting that stuff is done in CScript::Update.
bool CScript::execute_command()
{
	uint8 token=*mp_pc;

	// Check for the pre-processed member function token.
	if (token==ESCRIPTTOKEN_RUNTIME_MEMBERFUNCTION)
	{
		++mp_pc;
		uint32 member_function_checksum=Read4Bytes(mp_pc).mChecksum;
		mp_pc+=4;
		
		load_function_params();
		return run_member_function(member_function_checksum);
	}
		
	// Check for the pre-processed cfunction token.
	if (token==ESCRIPTTOKEN_RUNTIME_CFUNCTION)
	{
		++mp_pc;
        bool (*p_cfunc)(CStruct *pParams, CScript *pCScript)=Read4Bytes(mp_pc).mpCFunction;
		mp_pc+=4;
		
		load_function_params();
		return run_cfunction(p_cfunc);		
	}

	// Check for an expression enclosed in parentheses
	if (token==ESCRIPTTOKEN_OPENPARENTH)
	{
		// Note: Not skipping past the open-parenth token because Evaluate() expects it.
		CComponent *p_comp=new CComponent;
		mp_pc=Evaluate(mp_pc,mp_params,p_comp);
				
		Dbg_MsgAssert(p_comp->mType==ESYMBOLTYPE_INTEGER,("\n%s\nBad type of '%s' returned by expression, expected integer",GetScriptInfo(),GetTypeName(p_comp->mType)));
		bool return_value=p_comp->mIntegerValue;

		CleanUpComponent(p_comp);
		delete p_comp;
		
		return return_value;
	}	
	
	// Otherwise, expect some sort of name, ie Blaa or 
	uint32 name=get_name();
	
	// Check if the name is followed by a colon, in which case the name is the id of some object.
	if (*mp_pc==ESCRIPTTOKEN_COLON)
	{
		Obj::CObject *p_substitute_object = NULL;
		// Find the object
#ifndef __PLAT_WN32__
		p_substitute_object=Obj::ResolveToObject(name);
#endif
		Dbg_MsgAssert(p_substitute_object,("\n%s\nCould not resolve '%s' to a CObject instance",GetScriptInfo(),FindChecksumName(name)));
		if( p_substitute_object == NULL )
		{
			return true;
		}

//		if (p_substitute_object == mpObject)
//		{
//			printf("\n\n----------------------------------->\n\%s\nThis script is already running on %s\n",GetScriptInfo(),FindChecksumName(name));
//		}
		
		++mp_pc;
		mp_pc=DoAnyRandomsOrJumps(mp_pc);

		// Now we're expecting some sort of function to run on the object.
		
		// Check for a pre-processed member function
		if (*mp_pc==ESCRIPTTOKEN_RUNTIME_MEMBERFUNCTION)
		{
			++mp_pc;
			uint32 member_function_checksum=Read4Bytes(mp_pc).mChecksum;
			mp_pc+=4;
			
			load_function_params();
			return run_member_function(member_function_checksum,p_substitute_object);
		}

		// No pre-processed member function, so expect some sort of name.
		uint32 function_checksum=get_name();
		
		// Get the parameters that follow the name.
		load_function_params();

		// Look-up what kind of function it is.
		CSymbolTableEntry *p_entry=Resolve(function_checksum);
		
		// if the script is "runmenow" then a syntax error 
		// should just printf a warning and return
		if (!p_entry && mScriptChecksum == 0xfb11e6cd)   // RunMeNow
		{
			#ifdef	__NOPT_ASSERT__
			printf("WARNING !! Confused by '%s', which does not appear to be defined anywhere.\nIf it is a C-function or member function, it needs to be listed in ftables.cpp.",FindChecksumName(function_checksum));
			#endif
			return false;
		}
	
		Dbg_MsgAssert(p_entry,("\n%s\nConfused by '%s', which does not appear to be defined anywhere.\nIf it is a C-function or member function, it needs to be listed in ftables.cpp.",GetScriptInfo(),FindChecksumName(function_checksum)));

		// Expecting the function to be either a member function, or a script.	
		switch (p_entry->mType)
		{
		case ESYMBOLTYPE_QSCRIPT:
		{
			#ifdef __NOPT_ASSERT__
			if (Obj::DebugSkaterScripts)
			{
				if (p_substitute_object->GetType()==SKATE_TYPE_SKATER)
				{
					if (!sExcludeFromSkaterDebug(function_checksum))
					{	 
						printf("%d: Calling %s [%i]\n",(int)Tmr::GetRenderFrame(),FindChecksumName(function_checksum),m_unique_id);
					}	
				}
			}		
			#endif

			Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
			Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));
			uint8 *p_script=p_script_cache->GetScript(p_entry->mNameChecksum);
			
			call_script(function_checksum,p_script,mp_function_params,p_substitute_object);
			return true;
			break;
		}	
		case ESYMBOLTYPE_MEMBERFUNCTION:
			return run_member_function(function_checksum,p_substitute_object);
			break;
		default:
			Dbg_MsgAssert(0,("\n%s\n'%s' is not a member function or script",GetScriptInfo(),FindChecksumName(function_checksum)));
			return false;
			break;
		}		
	}
	
	// Check if the name is followed by equals.
	// In this case, the name is the name of the parameter we want to set.
	if (*mp_pc==ESCRIPTTOKEN_EQUALS)
	{
		// The get_name call will have looked up any <,> name in the structure, so re-get the
		// preceding name, because we want both x and  to mean x.
		mp_pc-=5;
		Dbg_MsgAssert(*mp_pc==ESCRIPTTOKEN_NAME,("\n%s\nEquals must be preceded by a param name",GetScriptInfo()));
		++mp_pc;
		name=Read4Bytes(mp_pc).mChecksum;
		mp_pc+=5; // +5 to skip over the equals too.

		mp_pc=DoAnyRandomsOrJumps(mp_pc);

		// Calculate the value of whatever follows the equals, and store it in p_comp		
		CComponent *p_comp=new CComponent;
		if (*mp_pc==ESCRIPTTOKEN_OPENPARENTH)
		{
			// It's an expression, so evaluate it.
			mp_pc=Evaluate(mp_pc,mp_params,p_comp);
		}
		else
		{
			// It's not an expression, so just load the value in.
			mp_pc=FillInComponentUsingQB(mp_pc,mp_params,p_comp);
		}

		if (p_comp->mType!=ESYMBOLTYPE_NONE)
		{
			// Got some sort of value, so name it and stick it into mp_params.
			p_comp->mNameChecksum=name;
			mp_params->AddComponent(p_comp);
			// Not deleting p_comp because it has been given to mp_params
		}
		else
		{
			// Did not get a value, so clean up p_comp
			delete p_comp;
		}	
		
		return true;
	}
	
	// The name is not followed by a colon or an equals, so it must be a function call.
	// Load in the parameters that follow.
	load_function_params();
	
	// Look up the function to see what it is.
    CSymbolTableEntry *p_entry=Resolve(name);
	
	// if the script is "runmenow" then a syntax error 
	// should just printf a warning and return
	if (!p_entry && mScriptChecksum == 0xfb11e6cd)   // RunMeNow
	{
		#ifdef	__NOPT_ASSERT__
		printf("WARNING !! Confused by '%s', which does not appear to be defined anywhere.\nIf it is a C-function or member function, it needs to be listed in ftables.cpp.",FindChecksumName(name));
		#endif
		return false;
	}

	if (!p_entry)
	{
		#ifdef	__NOPT_ASSERT__
		if (Script::GetInteger(CRCD(0x22d1f89,"AssertOnMissingScripts")))
		{
			Dbg_MsgAssert(p_entry,("\n%s\nConfused by '%s', which does not appear to be defined anywhere.\nIf it is a C-function or member function, it needs to be listed in ftables.cpp.",GetScriptInfo(),FindChecksumName(name)));
		}
		else
		#endif
		{
			#ifdef __PLAT_WN32__
			// Don't printf if compiling on PC, otherwise LevelAssetLister prints lots
			// of warning messages when running the load sound scripts.
			#else
			printf ("WARNING: script %s not found, ignoring in default level.\n",FindChecksumName(name));
			#endif
			return true;
		}
	}
									 

    switch (p_entry->mType)
	{
		case ESYMBOLTYPE_CFUNCTION:
			return run_cfunction(p_entry->mpCFunction);
			break;
		case ESYMBOLTYPE_MEMBERFUNCTION:
			return run_member_function(name);
			break;
		case ESYMBOLTYPE_QSCRIPT:
		{
			#ifdef __NOPT_ASSERT__
			if (Obj::DebugSkaterScripts)
			{
				if (mpObject && mpObject->GetType()==SKATE_TYPE_SKATER)
				{
					if (!sExcludeFromSkaterDebug(name))
					{
						printf("%d: Calling %s [%i]\n",(int)Tmr::GetRenderFrame(),FindChecksumName(name),m_unique_id);
					}	
				}
			}		
			#endif

			Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
			Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));
			uint8 *p_script=p_script_cache->GetScript(p_entry->mNameChecksum);
		 
			call_script(name,p_script,mp_function_params,mpObject);
			// Script calls always return true.
			return true;
			break;
		}	
		default:
			Dbg_MsgAssert(0,("\n%s\n'%s' cannot be called, because it's a '%s'",GetScriptInfo(),FindChecksumName(name),GetTypeName(p_entry->mType)));
			break;
	}		
	
	return false;
}

void CScript::execute_if()
{
	Dbg_MsgAssert(mp_pc,("NULL mp_pc"));
	Dbg_MsgAssert(*mp_pc==ESCRIPTTOKEN_KEYWORD_IF,("Unexpected *mp_pc='%s', expected keyword 'if'",GetTokenName((EScriptToken)*mp_pc)));

	// Skip over the if token.	
	++mp_pc;
	
	bool negate=false;
	if (*mp_pc==ESCRIPTTOKEN_KEYWORD_NOT)
	{
		++mp_pc;
		negate=true;
	}	
		
	bool return_value=execute_command();
	
	// mp_pc will have changed, and might be NULL.
	if (!mp_pc)
	{
		return;
	}	
	
	if (negate)
	{
		return_value=!return_value;
	}	

	#ifdef __NOPT_ASSERT__
	Dbg_MsgAssert((m_if_status&0x80000000)==0,("\n%s\nToo many nested if's",GetScriptInfo()));
	m_if_status<<=1;
	#endif
	
	// If the return value is false, skip forward to the 'else' or 'endif' statement.
	if (!return_value)
	{
		int in_nested_if=0;

		// Skip mp_pc to the current if's 'else' or 'endif',
		// but ignore the else & endifs belonging to nested ifs.
		#ifdef STOPWATCH_STUFF
		pIfSkipStopWatch->Start();
		#endif
		while (in_nested_if || 
			   !(*mp_pc==ESCRIPTTOKEN_KEYWORD_ELSE || *mp_pc==ESCRIPTTOKEN_KEYWORD_ENDIF))
		{
			Dbg_MsgAssert(*mp_pc!=ESCRIPTTOKEN_KEYWORD_ENDSCRIPT,("endif or else keywords not found after if keyword."));
			
			// Keep track of nested ifs.
			if (*mp_pc==ESCRIPTTOKEN_KEYWORD_IF)
			{
				++in_nested_if;
			}
			else if (*mp_pc==ESCRIPTTOKEN_KEYWORD_ENDIF)
			{
				Dbg_MsgAssert(in_nested_if,("Got endif within true part of if statement without corresponding nested if"));
				--in_nested_if;
			}	
				
			mp_pc=SkipToken(mp_pc);
		}    
		#ifdef __NOPT_ASSERT__
		if (*mp_pc==ESCRIPTTOKEN_KEYWORD_ENDIF)
		{
			m_if_status>>=1;
		}
		#endif
		// Skip over the 'else' or 'endif'
		++mp_pc;
		
		#ifdef STOPWATCH_STUFF
		pIfSkipStopWatch->Stop();
		#endif
	}
	else
	{
		// Otherwise, if the return value was true, mp_pc remains unchanged so that the instructions
		// following the if get executed.
		
		// Record the if-status so that spurious else's can be detected.
		#ifdef __NOPT_ASSERT__
		m_if_status|=1;
		#endif
	}	

	// I'm pretty sure if-debug isn't used, check with Scott
	/*
	#ifdef __NOPT_ASSERT__
	if (m_if_debug_on)
	{
		printf("%s: If %s ",FindChecksumName(mScriptChecksum),FindChecksumName(name_checksum));
		if (return_value)
		{
			printf("TRUE\n");
		}
		else
		{
			printf("FALSE\n");
		}	
	}
	#endif
	*/
}

// On entry, mp_pc must point to an else token.
// Skips forward till it hits an endif, then skips over the endif.
void CScript::execute_else()
{
	Dbg_MsgAssert(mp_pc,("NULL mp_pc"));
	Dbg_MsgAssert(*mp_pc==ESCRIPTTOKEN_KEYWORD_ELSE,("Unexpected *mp_pc='%s', expected keyword 'else'",GetTokenName((EScriptToken)*mp_pc)));
	// else's are only allowed in the true blocks of if-statements.
	// Bit 0 of m_if_status indicates the status of the last if.
	Dbg_MsgAssert(m_if_status&1,("\n%s\nSpurious 'else'",GetScriptInfo()));
	
	#ifdef STOPWATCH_STUFF
	pIfSkipStopWatch->Start();
	#endif
			
	// Skip over the code within the else, since the code before it must have executed.
	int in_nested_if=0;
	while (in_nested_if || *mp_pc!=ESCRIPTTOKEN_KEYWORD_ENDIF)
	{
		Dbg_MsgAssert(*mp_pc!=ESCRIPTTOKEN_KEYWORD_ENDSCRIPT,("endif keyword not found after else keyword."));
		
		// Keep track of nested ifs.
		if (*mp_pc==ESCRIPTTOKEN_KEYWORD_IF)
		{
			++in_nested_if;
		}
		else if (*mp_pc==ESCRIPTTOKEN_KEYWORD_ENDIF)
		{
			Dbg_MsgAssert(in_nested_if,("Got endif within else part of if statement without corresponding nested if"));
			--in_nested_if;
		}	
		
		mp_pc=SkipToken(mp_pc);
	}    
	// We've hit the endif, so skip over it.
	++mp_pc;

	#ifdef __NOPT_ASSERT__
	m_if_status>>=1;
	#endif
	
	#ifdef STOPWATCH_STUFF
	pIfSkipStopWatch->Stop();
	#endif
}

// We can hit an endif either by finishing the 'true' block of an if-statement
// that has no else, or by finishing the 'false' block following an else.
// Either way there is nothing to do, so just skip over it.
void CScript::execute_endif()
{
	Dbg_MsgAssert(mp_pc,("NULL mp_pc"));
	Dbg_MsgAssert(*mp_pc==ESCRIPTTOKEN_KEYWORD_ENDIF,("Unexpected *mp_pc='%s', expected keyword 'endif'",GetTokenName((EScriptToken)*mp_pc)));
	
	#ifdef STOPWATCH_STUFF
	pIfSkipStopWatch->Start();
	#endif
	
	++mp_pc;

	#ifdef __NOPT_ASSERT__
	m_if_status>>=1;
	#endif

	#ifdef STOPWATCH_STUFF
	pIfSkipStopWatch->Stop();
	#endif
}		

// Skips mp_pc forward till it finds an endswitch, skipping over any nested switch
// statements in between.
// Afterwards, mp_pc will point to the token following the endswitch.
void CScript::skip_to_after_endswitch()
{
	Dbg_MsgAssert(mp_pc,("NULL mp_pc"));
	
	while (*mp_pc!=ESCRIPTTOKEN_KEYWORD_ENDSWITCH)
	{
		if (*mp_pc==ESCRIPTTOKEN_KEYWORD_SWITCH)
		{
			// Skip over the switch
			++mp_pc;
			skip_to_after_endswitch();
		}
		else
		{
			mp_pc=SkipToken(mp_pc);
		}
	}
	
	// Skip over the endswitch
	++mp_pc;
}

// Given that mp_pc points to a switch token, this will skip mp_pc forward to the 
// code of the matching case. Or if none matches, it will point to after the endswitch.
void CScript::execute_switch()
{
	Dbg_MsgAssert(mp_pc,("NULL mp_pc"));
	Dbg_MsgAssert(*mp_pc==ESCRIPTTOKEN_KEYWORD_SWITCH,("Unexpected *mp_pc='%s', expected keyword 'switch'",GetTokenName((EScriptToken)*mp_pc)));
	
	#ifdef __NOPT_ASSERT__
	// Remember the old pc value so that the printed line number is correct if 
	// no endswitch is found before the end of file is hit.
	uint8 *start_of_switch_pc=mp_pc;
	#endif

	// Skip over the switch token
	++mp_pc;
	
	// Put the parameters following the switch keyword into mp_function_params
	Dbg_MsgAssert(mp_function_params,("NULL mp_function_params"));
	mp_function_params->Clear();
	mp_pc=AddComponentsUntilEndOfLine(mp_function_params,mp_pc,mp_params);
	
	// Get the first component and make a copy of it, putting it into p_comp.
	// Making a copy cos mp_function_params is needed later to hold the params following
	// each case statement that follows.
	CComponent *p_switch_comp=new CComponent;
	CComponent *p_first_comp=mp_function_params->GetNextComponent();
	if (p_first_comp)
	{
		Dbg_MsgAssert(mp_function_params->GetNextComponent(p_first_comp)==NULL,("\n%s\nswitch argument contains more than one component",GetScriptInfo()));
		CopyComponent(p_switch_comp,p_first_comp);
	}			
	
	// Skip forward till we hit a matching case, or a default, or endswitch
	// But, if we hit another switch statement whilst looking, 
	// skip over it, ignoring its cases.
	bool found=false;
	while (!found)
	{
		switch (*mp_pc)
		{
		case ESCRIPTTOKEN_KEYWORD_SWITCH:
		{
			// Skip over the switch
			++mp_pc;
			skip_to_after_endswitch();
			// mp_pc now points to the token following the ESCRIPTTOKEN_KEYWORD_ENDSCRIPT
			break;
		}
			
		case ESCRIPTTOKEN_KEYWORD_CASE:
		{
			++mp_pc;
			
			mp_function_params->Clear();
			mp_pc=AddComponentsUntilEndOfLine(mp_function_params,mp_pc,mp_params);
			
			CComponent *p_case_comp=mp_function_params->GetNextComponent();
			if (p_case_comp)
			{
				Dbg_MsgAssert(mp_function_params->GetNextComponent(p_case_comp)==NULL,("\n%s\ncase argument contains more than one component",GetScriptInfo()));
				
				if (*p_switch_comp==*p_case_comp)
				{
					found=true;
				}
			}
			
			if (found)
			{
				// We've found a match, but we need to skip over any case statements
				// that immediately follow. This is for when multiple cases want to
				// execute the same chunk of code.
				while (true)
				{
					mp_pc=SkipEndOfLines(mp_pc);
					if (*mp_pc==ESCRIPTTOKEN_KEYWORD_CASE)
					{
						++mp_pc;
						mp_function_params->Clear();
						mp_pc=AddComponentsUntilEndOfLine(mp_function_params,mp_pc,mp_params);
					}							
					else
					{
						break;
					}
				}		
			}
			break;
		}	
		case ESCRIPTTOKEN_KEYWORD_DEFAULT:
		case ESCRIPTTOKEN_KEYWORD_ENDSWITCH:
			++mp_pc;
			found=true;
			break;
		case ESCRIPTTOKEN_KEYWORD_ENDSCRIPT:
			#ifdef __NOPT_ASSERT__
			mp_pc=start_of_switch_pc;
			Dbg_MsgAssert(0,("\n%s\nMissing endswitch",GetScriptInfo()));
			#endif
			break;
		default:
			mp_pc=SkipToken(mp_pc);
			break;
		}	
	}
	
	CleanUpComponent(p_switch_comp);
	delete p_switch_comp;
}

void CScript::execute_begin()
{
	Dbg_MsgAssert(mp_pc,("NULL mp_pc"));
	Dbg_MsgAssert(*mp_pc==ESCRIPTTOKEN_KEYWORD_BEGIN,("Unexpected *mp_pc='%s', expected keyword 'begin'",GetTokenName((EScriptToken)*mp_pc)));
	++mp_pc;
	
	// First, check whether we're about to run out of space on the small statically allocated loop buffer.
	if (mp_current_loop==mp_loops_small_buffer+NESTED_BEGIN_REPEATS_SMALL_BUFFER_SIZE-1)
	{
		// There won't be enough room for the new loop!

		// A quick check, to ensure no memory leaks.
		Dbg_MsgAssert(mp_loops==mp_loops_small_buffer,("Expected mp_loops==mp_loops_small_buffer, %x, %x",mp_loops,mp_loops_small_buffer));
		
		// Dynamically allocate a bigger buffer, and change mp_loops to point to that instead
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
		mp_loops=(SLoop*)Mem::Malloc(MAX_NESTED_BEGIN_REPEATS * sizeof(SLoop));
		Mem::Manager::sHandle().PopContext();
		
		// Then copy over the contents of the small buffer, and update mp_current_loop to point into the new buffer.
		int i;

		for (i=0; i= mp_loops_small_buffer && mp_return_addresses[i].mpLoop < mp_loops_small_buffer+NESTED_BEGIN_REPEATS_SMALL_BUFFER_SIZE,("Bad mp_return_addresses[i].mpLoop"));
				
				mp_return_addresses[i].mpLoop=(mp_return_addresses[i].mpLoop-mp_loops_small_buffer)+mp_loops;
			}
		}    
	}
	
	if (mp_current_loop)
	{            
		Dbg_MsgAssert(mp_current_loop-mp_loopsmpStart=mp_pc;
	// These get filled in once the repeat is reached.
	mp_current_loop->mpEnd=NULL;
	mp_current_loop->mGotCount=false;
	mp_current_loop->mNeedToReadCount=true;
	mp_current_loop->mCount=0;
}


void CScript::execute_repeat()
{
	Dbg_MsgAssert(mp_pc,("NULL mp_pc"));
	Dbg_MsgAssert(*mp_pc==ESCRIPTTOKEN_KEYWORD_REPEAT,("Unexpected *mp_pc='%s', expected keyword 'repeat'",GetTokenName((EScriptToken)*mp_pc)));
	Dbg_MsgAssert(mp_current_loop,("\n%s\nEncountered repeat with NULL mp_current_loop",GetScriptInfo()));
	
	if (mp_current_loop->mNeedToReadCount)
	{
		// Skip over the repeat token. 
		++mp_pc;
		Dbg_MsgAssert(mp_function_params,("NULL mp_function_params"));
		mp_function_params->Clear();
		mp_current_loop->mpEnd=AddComponentsUntilEndOfLine(mp_function_params,mp_pc,mp_params);
		mp_current_loop->mGotCount=mp_function_params->GetInteger(NO_NAME,&mp_current_loop->mCount);
		if (!mp_current_loop->mGotCount)
		{
			Dbg_MsgAssert(mp_current_loop->mpEnd==mp_pc,("\n%s\nNo count value found following 'repeat'",GetScriptInfo()));
		}		
		
		mp_current_loop->mNeedToReadCount=false;
	}	
		
	if (!mp_current_loop->mGotCount)
	{
		// It's an infinite loop.
		mp_pc=mp_current_loop->mpStart;
	}
	else
	{
		Dbg_MsgAssert(mp_current_loop->mCount,("\n%s\nZero count given to a begin-repeat loop",GetScriptInfo()));
		--mp_current_loop->mCount;
		if (mp_current_loop->mCount)
		{
			// Finite loop, but it has not finished yet, so jump back to the start.
			mp_pc=mp_current_loop->mpStart;
		}
		else
		{
			// The loop has finished!
			
			// Jump PC to the next instruction after the repeat.
			Dbg_MsgAssert(mp_current_loop->mpEnd,("NULL mp_current_loop->pEnd ??")); 
			mp_pc=mp_current_loop->mpEnd;
			
			// Rewind to the previous loop.
			if (mp_current_loop==mp_loops)
			{
				mp_current_loop=NULL;
			}
			else
			{
				--mp_current_loop;
				Dbg_MsgAssert(mp_current_loop>=mp_loops,("\n%s\nBad mp_current_loop",GetScriptInfo()));
			}
		}
	}
}

void CScript::execute_break()
{
	Dbg_MsgAssert(mp_pc,("NULL mp_pc"));
	Dbg_MsgAssert(*mp_pc==ESCRIPTTOKEN_KEYWORD_BREAK,("Unexpected *mp_pc='%s', expected keyword 'break'",GetTokenName((EScriptToken)*mp_pc)));
	Dbg_MsgAssert(mp_current_loop,("\n%s\nEncountered break with NULL mp_current_loop",GetScriptInfo()));
	
	// Step over every token until repeat is reached.

	#ifdef __NOPT_ASSERT__
	int nested_if_count=0;
	#endif	
	int nested_loop_count=0;
	bool in_loop=true;
	// Skip mp_pc to the end of the loop.
	while (in_loop)
	{
		switch (*mp_pc)
		{
		case ESCRIPTTOKEN_KEYWORD_BEGIN:
			++nested_loop_count;
			break;
			
		case ESCRIPTTOKEN_KEYWORD_REPEAT:
			if (nested_loop_count)
			{
				--nested_loop_count;
			}
			else
			{
				in_loop=false;
			}
			break;
			
		#ifdef __NOPT_ASSERT__
		case ESCRIPTTOKEN_KEYWORD_IF:
			++nested_if_count;
			break;
			
		case ESCRIPTTOKEN_KEYWORD_ENDIF:
			if (nested_if_count)
			{
				--nested_if_count;
			}
			else
			{
				// Make sure that the stored if-status is rewound back to its
				// state before the loop was entered, otherwise the
				// 'too many nested ifs' assert will go off erroneously.
				m_if_status>>=1;
			}
			break;
		#endif
		
		case ESCRIPTTOKEN_KEYWORD_ENDSCRIPT:
			Dbg_MsgAssert(0,("\n%s\nRepeat keyword not found after break keyword.",GetScriptInfo()));
			break;
		}
			
		mp_pc=SkipToken(mp_pc);
	}    
	
	// Use AddComponentsUntilEndOfLine just to step over the possible repeat argument.
	// Ugh! Kind of ugly, but shouldn't be too slow because repeat will probably be followed
	// by nothing or just an integer.
	Dbg_MsgAssert(mp_function_params,("NULL mp_function_params"));
	mp_function_params->Clear();
	mp_pc=AddComponentsUntilEndOfLine(mp_function_params,mp_pc);

	
	// Rewind to the previous loop.
	if (mp_current_loop==mp_loops)
	{
		mp_current_loop=NULL;
	}
	else
	{
		--mp_current_loop;
		Dbg_MsgAssert(mp_current_loop>=mp_loops,("\n%s\nBad mp_current_loop",GetScriptInfo()));
	}
}

// Returns from a sub-script by popping the info (mp_pc etc) off the stack.
// If there is nothing on the stack, it sets mp_pc to NULL
// Returns true if the script being returned from was a script triggered by Interrupt.
// This then allows Update() to not continue execution of the script that was interrupted.
bool CScript::execute_return()
{
	// The current script is finished with, so decrement its usage in the script cache.
	Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
	Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));
	p_script_cache->DecrementScriptUsage(mScriptChecksum);

	bool was_interrupted=m_interrupted;
	
	if (m_num_return_addresses)
	{
		// This script was called from another, either by a regular call or by an interrupt.
		
		--m_num_return_addresses;
		
		// Delete the current mp_params, because it was created when this routine was called.
		Dbg_MsgAssert(mp_params,("\n%s\nNULL p_params",GetScriptInfo()));
		delete mp_params;
		
		// Restore the info stored on the stack.
		SReturnAddress *p_info=mp_return_addresses+m_num_return_addresses;
		
		mScriptChecksum = p_info->mScriptNameChecksum;
		mp_params		= p_info->mpParams;
		mp_pc			= p_info->mpReturnAddress;
		mpObject		= p_info->mpObject;
		mp_current_loop = p_info->mpLoop;
		#ifdef __NOPT_ASSERT__
		m_if_status		= p_info->m_if_status;
		#endif

		m_wait_type		= p_info->mWaitType;
		if (m_wait_type)
		{
			m_wait_timer	= p_info->mWaitTimer;
			m_wait_period	= p_info->mWaitPeriod;
			m_start_time	= p_info->mStartTime;
			mp_wait_component = p_info->mpWaitComponent;
		}	
		
		m_interrupted=p_info->mInterrupted;
		
		#ifdef __NOPT_ASSERT__ 
		check_if_needs_to_be_watched_in_debugger();
		#endif	
		
		// Check that the script being returned to does still exist, ie mp_pc is valid.
		// However, if we're running an embedded script, don't do the check, because the
		// script name may be the name of the embedded script rather than a global script.
		if (!mp_struct_script)
		{
			Dbg_MsgAssert(LookUpSymbol(mScriptChecksum),("Non-existent script '%s' on call stack",FindChecksumName(mScriptChecksum)));
		}
			
		// Actually, we need a better check to see if mp_pc is still valid, because the
		// script we're returning to might have got reloaded and hence moved in memory.
		// Should use a smart pointer.
		// But, doesn't seem to have caused any problems so far, so leave for the moment ...
	}
	else
	{
		// Nothing to return to, so stop the script by setting mp_pc to NULL.
		// This occurs when an endscript is hit.
		mp_pc=NULL;
	}	
	
	return was_interrupted;
}

#ifdef	__NOPT_ASSERT__
#ifndef __PLAT_WN32__
void CScript::advance_pc_to_next_line_and_halt()
{
	if (m_being_watched_in_debugger)
	{
		if (m_single_step_mode==STEP_OVER || 
			m_single_step_mode==STEP_INTO)
		{
			// Record the current return address count so that it can be detected whether
			// the next command is a script call. Needed for stepping over script calls.
			m_last_num_return_addresses_when_halted=m_num_return_addresses;
			
			if (mp_pc)
			{
				mp_pc=SkipEndOfLines(mp_pc);
			}	
			
			TransmitInfoToDebugger();
			m_last_instruction_time_taken=0.0f;
			Dbg::CScriptDebugger::Instance()->ScriptDebuggerPause();
		}
	}
}			
#endif
#endif

//static int sInstructionCount=0;
//static uint64 sLastVBlanks=0;

// Update the script, executing instructions
// REQUIREMENT: is mp_pc is NULL, then return  ESCRIPTRETURNVAL_FINISHED, so the script is deleted by UpdateSpawnedScript
EScriptReturnVal CScript::Update()
{
	#ifdef	__NOPT_ASSERT__
	m_last_instruction_time_taken=0.0f;	
	m_total_instruction_time_taken=0.0f;
	if (m_being_watched_in_debugger)
	{
		TransmitInfoToDebugger();
	}	
	#endif

	#ifdef STOPWATCH_STUFF
	pUpdateStopWatch->Start();
	#endif

	#ifdef	__NOPT_ASSERT__
	Tmr::CPUCycles start_time = Tmr::GetTimeInCPUCycles();
	#endif


	//if (sLastVBlanks!=Tmr::GetVblanks())
	//{
	//	sLastVBlanks=Tmr::GetVblanks();
	//	printf("Instruction count = %d\n",sInstructionCount);
	//	sInstructionCount=0;
	//}	
	

	#ifdef	__NOPT_ASSERT__	
	Obj::CObjectPtr p_obj = NULL;
	if (mpObject)
	{
		p_obj = mpObject;	   			// remember the object we locked
//		mpObject->SetLockAssertOn();
	}
	#endif
	
	m_skip_further_process_waits=false;
	
	// This loop is going to keep executing script commands until either mp_pc
	// becomes NULL somehow, or if some sort of wait command gets executed.
    while (true)
    {
		if (!mp_pc) 
		{
			// Break out if mp_pc became NULL during the execution of the
			// last command. 
			#ifdef STOPWATCH_STUFF
			pUpdateStopWatch->Stop();
			#endif
			sCurrentlyUpdating=NULL;
	#ifdef	__NOPT_ASSERT__	
	
//			if (mpObject)
//			{
//				// check the object we locked is the same one we are unlocking (or deleted)
//				Dbg_MsgAssert(p_obj == mpObject,("\n%s\nFinished Script has changed objects to %p! Original Object %p Still Locked! Tell Mick\n",GetScriptInfo(),mpObject.Convert(),p_obj.Convert()));
//				mpObject->SetLockAssertOff();
//			}
			// If script is waiting, then we might be on a different object
			// if the original object still exists, then unlock it.
			if (p_obj)
			{
				p_obj->SetLockAssertOff();
			}
	#endif																	
			#ifdef	__NOPT_ASSERT__
			start_time = Tmr::GetTimeInCPUCycles() - start_time;
			m_last_time = (int)start_time;	 // experiment with uint etc....
			m_total_time += m_last_time;
			#endif
			
			// Seems like a reasonable thing to do ...
			m_interrupted=false;
			
			return ESCRIPTRETURNVAL_FINISHED;
		}	
		else
		{
			// Make sure sCurrentlyUpdating is correct, because it may have
			// got changed during the execution of the last command.
			sCurrentlyUpdating=this;
		}	
	
		#ifdef	__NOPT_ASSERT__
		bool was_waiting_before;
		if (m_wait_type==WAIT_TYPE_NONE)
		{
			was_waiting_before=false;
		}
		else
		{
			was_waiting_before=true;
		}
		#endif
		
		if (!m_skip_further_process_waits)
		{
			process_waits();
		}	

		// Return straight away if waiting.
		if (m_wait_type!=WAIT_TYPE_NONE)
		{
			#ifdef STOPWATCH_STUFF
			pUpdateStopWatch->Stop();
			#endif
			sCurrentlyUpdating=NULL;

	#ifdef	__NOPT_ASSERT__	
			// If script is waiting, then we might be on a different object
			// if the original object still exists, then unlock it.
			if (p_obj)
			{
				p_obj->SetLockAssertOff();
			}
	#endif	 

	#ifdef	__NOPT_ASSERT__
			start_time = Tmr::GetTimeInCPUCycles() - start_time;
			m_last_time = (int)start_time;	 // experiment with uint etc....
			m_total_time += m_last_time;
	#endif
	   	
			// If an interrupt script hits a blocking command, then clear the interrupted flag.
			// Reason:
			// If an interrupt script contains no blocking functions, then a single call to CScript::Update()
			// would execute the whole script, and as soon as the endscript was reached it was exit the Update()
			// function (due to the interrupt flag being on) hence returning control to the original Update()
			// function that was executing the interrupted script, and all would be well.
			
			// However, if the interrupt script did hit a blocking function, that would cause an early exit from
			// the Update() function, leaving it to the original Update() function, ie the one that was executing the
			// interrupted script, to finish completion of the interrupt script. In that case, we don't want
			// the interrupt flag to be on any more, because otherwise it would cause a premature exit from
			// the original Update() function as soon as the endscript was reached.
			m_interrupted=false;
	
			// Set this to prevent any previous CScript::Update() loop that the return may return to
			// from processing the waits further, otherwise if an interrupt script contains a wait n gameframes
			// it will end up waiting only n-1 gameframes, due to the interrupted scripts Update() loop calling
			// process_waits again.
			m_skip_further_process_waits=true;
			
			if (m_wait_type==WAIT_TYPE_BLOCKED)
			{
				return ESCRIPTRETURNVAL_BLOCKED;
			}
			else
			{
				return ESCRIPTRETURNVAL_WAITING;
			}
		}		
		else
		{
			#ifdef	__NOPT_ASSERT__
			if (was_waiting_before)
			{
				advance_pc_to_next_line_and_halt();
			}	
			#endif
		}		

		// Execute whatever is at mp_pc
		#ifdef	__NOPT_ASSERT__
		Tmr::CPUCycles instruction_start_time = Tmr::GetTimeInCPUCycles();
		#endif
		
		switch (*mp_pc)
		{
		case ESCRIPTTOKEN_KEYWORD_IF:
			//++sInstructionCount;
			// This will execute the function call or expression following the if,
			// and if the command or expression returns false, it will skip
			// mp_pc forward to the token after the else or endif.
			// Otherwise, mp_pc will be left pointing to the first token of the 'true' block.
			execute_if();
			// Note: mp_pc may be NULL at this point
			break;
			
		case ESCRIPTTOKEN_KEYWORD_ELSE:
			//++sInstructionCount;
			// The only time an else token should be hit is at the end of executing
			// the 'true' block following the if.
			// So this will just skip mp_pc over the 'false' block to after the endif.
			execute_else();
			break;
			
		case ESCRIPTTOKEN_KEYWORD_ENDIF:
			//++sInstructionCount;
			// We can hit an endif either by finishing the 'true' block of an if-statement
			// that has no else, or by finishing the 'false' block following an else.
			// Either way there is nothing to do, so just skip over it, but assert if we're
			// not in an if-statement to catch spurious else's.
			execute_endif();
			break;

        case ESCRIPTTOKEN_KEYWORD_SWITCH:
			//++sInstructionCount;
			// Skips mp_pc forward to the token following the matching case. 
			// Or if none matches, it will point to after the endswitch.
			execute_switch();
			break;

        case ESCRIPTTOKEN_KEYWORD_CASE:
		case ESCRIPTTOKEN_KEYWORD_DEFAULT:
        case ESCRIPTTOKEN_KEYWORD_ENDSWITCH:
			//++sInstructionCount;
			// If we hit a case, default or endswitch, it must be due to the completion of the
			// previous case statement's commands.
			// So skip mp_pc forward so that it points to after the endswitch token.
			skip_to_after_endswitch();
			break;
			
        case ESCRIPTTOKEN_KEYWORD_BEGIN:
			//++sInstructionCount;
			// Initialise a new loop counter
			execute_begin();
            break;        

        case ESCRIPTTOKEN_KEYWORD_REPEAT:
			//++sInstructionCount;
			// This will either jump mp_pc back to the start of the loop,
			// or skip it past the repeat if the loop has finished.
			execute_repeat();
            break;

        case ESCRIPTTOKEN_KEYWORD_BREAK:
			//++sInstructionCount;
			// This will skip mp_pc to after the repeat of the current loop
			execute_break();
            break;
		    
        case ESCRIPTTOKEN_KEYWORD_ENDSCRIPT:
			//++sInstructionCount;
			if (execute_return())
			{
				// If we returned from a script call caused by an interrupt, then do not continue
				// with execution of the interrupted script, since an interrupt should not affect the
				// interrupted script.
				return ESCRIPTRETURNVAL_FINISHED_INTERRUPT;
			}	
			// Note: mp_pc may be NULL at this point, if there is no calling script.
            break;
			
        case ESCRIPTTOKEN_KEYWORD_RETURN:
			//++sInstructionCount;
			
			// The return keyword works very similar to hitting the endscript token.
			// The difference is that 'return' will merge the parameters that follow the
			// return keyword onto the parameters of the calling script, if there is one.
			
			// Skip over the return token. 
			++mp_pc;
			
			// Put any parameters that follow into mp_function_params
			Dbg_MsgAssert(mp_function_params,("NULL mp_function_params"));
			mp_function_params->Clear();
			mp_pc=AddComponentsUntilEndOfLine(mp_function_params,mp_pc,mp_params);

			if (execute_return())
			{
				// If we returned from a script call caused by an interrupt, then do not continue
				// with execution of the interrupted script, since an interrupt should not affect the
				// interrupted script.
				// For the same reason we are bailing out here before the returned parameters are merged onto
				// the interrupted scripts params, so that they will not be affected by the interrupt either.
				return ESCRIPTRETURNVAL_FINISHED_INTERRUPT;
			}	
			
			// Note: mp_pc may be NULL at this point, if there is no calling script.

			// Merge the return values onto the parameters of the calling script.
			Dbg_MsgAssert(mp_params,("NULL mp_params ?"));
			mp_params->AppendStructure(mp_function_params);
			break;

        case ESCRIPTTOKEN_KEYWORD_RANDOM:
        case ESCRIPTTOKEN_KEYWORD_RANDOM2:
		case ESCRIPTTOKEN_KEYWORD_RANDOM_NO_REPEAT:
		case ESCRIPTTOKEN_KEYWORD_RANDOM_PERMUTE:
        case ESCRIPTTOKEN_JUMP:
			// Modify mp_pc according to any jump or random operators, and repeat until mp_pc no
			// longer points to a jump or random.
			mp_pc=DoAnyRandomsOrJumps(mp_pc);
			break;

        case ESCRIPTTOKEN_ENDOFLINE:
            ++mp_pc;
            break;
            
	    case ESCRIPTTOKEN_ENDOFLINENUMBER:
			mp_pc+=5; // 1 for the token, 4 for the line number.
            break;
		
        default:
			//++sInstructionCount;
			//printf("Running line %d in script '%s'\n",GetLineNumber(mp_pc),FindChecksumName(mScriptChecksum));
			execute_command();
			// Note: mp_pc may be NULL at this point
            break;
		}	
		
		#ifdef	__NOPT_ASSERT__
		
		if (m_being_watched_in_debugger)
		{
			Tmr::CPUCycles t=Tmr::GetTimeInCPUCycles()-instruction_start_time;
			float last_instruction_time_taken=((float)((int)t))/150.0f;
			
			m_last_instruction_time_taken+=last_instruction_time_taken;
			m_total_instruction_time_taken+=last_instruction_time_taken;
		}		
		
		if (m_single_step_mode==STEP_OVER &&
			m_num_return_addresses > m_last_num_return_addresses_when_halted)
		{
			// If the number of return addresses has increased beyond what it was when last halted
			// in the debugger, then the last command must have been a script call, so do not halt
			// but keep executing instructions until the script call has finished, which will be 
			// indicated by the return address count returning to what it was or lower.
			// (It would be lower if say a goto occurred during the script call)
			
		}
		else
		{
			if (m_wait_type == WAIT_TYPE_NONE)
			{
				advance_pc_to_next_line_and_halt();
			}	
		}	
		#endif	
    }
}

void CScript::Wait(int num_frames)
{
	m_wait_type=WAIT_TYPE_COUNTER;
    m_wait_timer=num_frames;
}

void CScript::WaitOnePerFrame(int num_frames)
{
	m_wait_type=WAIT_TYPE_ONE_PER_FRAME;
    m_wait_timer=num_frames;		 		
}


void CScript::WaitTime(Tmr::Time period)
{
	m_wait_type=WAIT_TYPE_TIMER;
	m_wait_period=period;
	// Record the start time.
	m_start_time=Tmr::GetTime();
}

bool CScript::RefersToScript(uint32 checksum)
{
	if (mScriptChecksum==checksum) 
	{
		return true;
	}	
	
	for (int i=0; iInNetGame())
	{
		Net::MsgDesc msg_desc;
		msg.m_ScriptName = scriptChecksum;
		msg.m_ObjID = -1;
		if( p_object )
		{
			msg.m_ObjID = p_object->GetID();
		}

		size = WriteToBuffer(p_params, (uint8 *) msg.m_Data, GameNet::MsgRunScript::vMAX_SCRIPT_PARAMS_LEN );
		Dbg_MsgAssert( size <= GameNet::MsgRunScript::vMAX_SCRIPT_PARAMS_LEN,( "Script too large to send over the net\n" ));
		
		client = gamenet_man->GetClient( 0 );
		Dbg_Assert( client );

		msg_size = ( sizeof( GameNet::MsgRunScript ) - GameNet::MsgRunScript::vMAX_SCRIPT_PARAMS_LEN ) +
					size;
		msg_desc.m_Data = &msg;
		msg_desc.m_Length = msg_size;
		msg_desc.m_Id = GameNet::MSG_ID_RUN_SCRIPT;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
		client->EnqueueMessageToServer( &msg_desc );
	
	}
	#endif
}

#ifdef __NOPT_ASSERT__
static const char *sGetRunScriptName(uint32 scriptChecksum, const char *p_scriptName)
{
	if (p_scriptName)
	{
		return p_scriptName;
	}
	return FindChecksumName(scriptChecksum);
}	
#endif

// Used for running a simple script from start to end.
void RunScript(uint32 scriptChecksum, CStruct *p_params, Obj::CObject *p_object, bool netScript, const char *p_scriptName )
{
	// First, see what type of symbol scriptChecksum is referring to.
    CSymbolTableEntry *p_entry=Resolve(scriptChecksum);
	if (!p_entry)
	{
		#ifdef __NOPT_ASSERT__
		printf("Warning! RunScript could not find '%s'\n",sGetRunScriptName(scriptChecksum,p_scriptName));
		#endif
		return;
	}	
	if (p_entry->mType!=ESYMBOLTYPE_QSCRIPT && 
		p_entry->mType!=ESYMBOLTYPE_CFUNCTION &&
		p_entry->mType!=ESYMBOLTYPE_MEMBERFUNCTION)
	{
		#ifdef __NOPT_ASSERT__
		printf("Warning! RunScript sent '%s' which is not a script, a c-function or a member function. Type=%s\n",sGetRunScriptName(scriptChecksum,p_scriptName),GetTypeName(p_entry->mType));
		#endif
		return;
	}


	if( netScript )
	{
		SendScript( scriptChecksum, p_params, p_object );
	}

	switch (p_entry->mType)
	{
		case ESYMBOLTYPE_CFUNCTION:
		{
			// If the symbol is actually a c-function rather than a script, then run the c-function.
			// This is handy sometimes because it saves having to write a special script just to run 
			// one c-function.
			Dbg_MsgAssert(p_entry->mpCFunction,("NULL pCFunction"));

			// Mick:  We now pass in NULL, as creating a dummy script is very slow			
			(*p_entry->mpCFunction)(p_params,NULL);
			
			break;
		}
		
		case ESYMBOLTYPE_MEMBERFUNCTION:
		{
			Dbg_MsgAssert(p_object,("Tried to run member function '%s' on NULL p_object",Script::FindChecksumName(scriptChecksum)));
	
			// Mick:  We now pass in NULL, as creating a dummy script is very slow			
			p_object->CallMemberFunction(scriptChecksum,p_params,NULL);
			
			break;
		}
		
		case ESYMBOLTYPE_QSCRIPT:
		{
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
			CScript *p_script=new CScript;
			#ifdef __NOPT_ASSERT__
			p_script->SetCommentString("Created by RunScript(...)");
			#endif
			p_script->SetScript(scriptChecksum,p_params,p_object);
			Mem::Manager::sHandle().PopContext();
		
			while (true)
			{
				EScriptReturnVal ret_val=p_script->Update();
				if (ret_val==ESCRIPTRETURNVAL_FINISHED)
				{
					break;
				}
				//Dbg_MsgAssert(0,("\n%s\nScript did not finish when run by RunScript.",p_script->GetScriptInfo()));
				// Script must not get blocked, otherwise it'll hang in this loop forever.
				Dbg_MsgAssert(ret_val!=ESCRIPTRETURNVAL_BLOCKED,("\n%s\nScript got blocked when being run by RunScript.",p_script->GetScriptInfo()));
				// Note: OK if the script returns ESCRIPTRETURNVAL_WAITING, because the wait period will eventually end.
			
			}
			delete p_script;
			break;
		}	
		
		default:
			break;
	}	
}

void RunScript(const char *p_scriptName, CStruct *p_params, Obj::CObject *p_object, bool netScript )
{
	// Quite often, if a script cannot be found then FindChecksumName will not be able to find
	// the checksum name either, so since we know the name here it gets passed along to
	// the other version of RunScript so that the name can be printed in any warning message.
	RunScript(Crc::GenerateCRCFromString(p_scriptName), p_params, p_object, netScript, p_scriptName);
}

/////////////////////////////////////////////////////////////////
//
// 					Spawning script stuff
//
/////////////////////////////////////////////////////////////////

static bool s_updating_scripts = false;
static bool s_delete_scripts_pending = false;

// This gets called from within DeleteSymbols.
void DeleteSpawnedScripts()
{
	// Guard against deleting spawned scripts while we are updating them
	if( s_updating_scripts )
	{
		s_delete_scripts_pending = true;
		return;
	}

	CScript *p_script=GetNextScript();
	while (p_script)
	{
		CScript *p_next=GetNextScript(p_script);
		// Don't kill the script if it is not session-specific.
		// This feature is currently used by one of Steve's net scripts which needs to not
		// be killed by ScriptCleanup
		if (p_script->mIsSpawned && !p_script->mNotSessionSpecific)
		{
			delete p_script;
		}	
		p_script=p_next;
	}	
}

// This gets called from Front::PauseGame
void PauseSpawnedScripts(bool status)
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		if (p_script->mIsSpawned)
		{
			p_script->mPaused=status;
		}
		p_script=GetNextScript(p_script);
	}	
}

void UnpauseSpawnedScript(CScript* p_script)
{
	if (p_script && p_script->mIsSpawned)
	{
		p_script->mPaused=false;
	}
}

// Note: Does this need to be fast?
uint32 NumSpawnedScriptsRunning()
{
	uint32 num_spawned_scripts=0;
	
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		if (p_script->mIsSpawned)
		{
			++num_spawned_scripts;
		}
		p_script=GetNextScript(p_script);
	}	
	
	return num_spawned_scripts;
}

// Called once a frame. Currently called from Skate::DoUpdate()
void UpdateSpawnedScripts()
{

	s_updating_scripts = true;
	s_done_one_per_frame = false;
	
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		CScript *p_next;
		// get the next actual spawned script
		p_next=GetNextScript(p_script);		
		// skip any non-spawned scripts
		// since they might get deleted in an unsafe manner
		while (p_next && !p_next->mIsSpawned)
		{
			p_next=GetNextScript(p_next);				
		}
		
		Dbg_MsgAssert(GetNextScript(p_next) != (CScript*)-1,("Next script in spawned list has been deleted sometime earlier!"));
		if (p_next == p_script)
		{
			Dbg_MsgAssert(0,("%s\nLoop in list of spawned scripts!!",p_script->GetScriptInfo()));
		}
		
		// K: Added the || !p_script->GotScript() so that spawned scripts which have been cleared still get
		// cleaned up when the game is paused. This was to fix a bug (TT1453) where cleared scripts where accumulating
		// during the cutscenes. The scripts were the scripts of goal peds, which got cleared when the peds were
		// killed. 
		if (p_script->mIsSpawned && (!p_script->mPaused || !p_script->GotScript()) && (!p_script->mPauseWithObject || !p_script->mpObject || p_script->mpObject->ShouldUpdatePauseWithObjectScripts()))
		{
			if (p_script->Update()==ESCRIPTRETURNVAL_FINISHED)
			{
				// just doing the assertion before we delete the script  
				Dbg_MsgAssert(GetNextScript(p_next) != (CScript*)-1,("%s\nNext script in spawned list has been deleted by this script updating",p_script->GetScriptInfo()));
				// If it had a callback script specified, run it.
				if (p_script->mCallbackScript)
				{
					RunScript(p_script->mCallbackScript,
							  p_script->mpCallbackScriptParams,
							  p_script->mpObject);
				}

				// just doing the assertion before we delete the script  
				Dbg_MsgAssert(GetNextScript(p_next) != (CScript*)-1,
				("Next script in spawned list has been deleted by callback script (%s)",FindChecksumName(p_script->mCallbackScript)));
				// Kill it now that it has finished.
				delete p_script;
			}
			else
			{
				Dbg_MsgAssert(GetNextScript(p_next) != (CScript*)-1,("%s\nNext script in spawned list has been deleted by this script",p_script->GetScriptInfo()));
			}
		}	
			
		p_script=p_next;
		Dbg_MsgAssert(GetNextScript(p_script) != (CScript*)-1,("Next script in spawned list has been deleted by this script"));
	}	


	s_updating_scripts = false;
	if( s_delete_scripts_pending )
	{
		DeleteSpawnedScripts();
		s_delete_scripts_pending = false;
	}


}

// Sned spawn script events to other clients
void SendSpawnScript( uint32 scriptChecksum, Obj::CObject *p_object, int node, bool permanent )
{
	#ifdef __PLAT_WN32__
	// No GameNet stuff if compiling on PC
	#else
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Net::Client* client;
	GameNet::MsgSpawnAndRunScript msg;
    
	if( gamenet_man->InNetGame())
	{
		Net::MsgDesc msg_desc;

		msg.m_ScriptName = scriptChecksum;
		msg.m_ObjID = -1;
		msg.m_Node = node;
		msg.m_Permanent = (char) permanent;
		if( p_object )
		{
			msg.m_ObjID = p_object->GetID();
		}

		client = gamenet_man->GetClient( 0 );
		Dbg_Assert( client );

		msg_desc.m_Data = &msg;
		msg_desc.m_Length = sizeof( GameNet::MsgSpawnAndRunScript );
		msg_desc.m_Id = GameNet::MSG_ID_SPAWN_AND_RUN_SCRIPT;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
		client->EnqueueMessageToServer( &msg_desc );
	}
	#endif
}


CScript *GetScriptWithUniqueId(uint32 id)
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		if (p_script->GetUniqueId() == id) 
			return p_script;
		p_script = GetNextScript(p_script);
	}	

	return NULL;
}

// Called from ScriptSpawnScript in cfuncs.cpp
// also called by the triggering code in skater.cpp
// returns the new script if sucessful, asserts if not
// optional "node" parameter is the node number that is
// responsible for spawning this script.
// Also now takes an optional Id, to allow individual spawned script instances to be killed.
CScript* SpawnScript(uint32 scriptChecksum, CStruct *p_scriptParams, uint32 callbackScript, CStruct *p_callbackParams, int node, uint32 id,
					 bool netEnabled, bool permanent, bool not_session_specific, bool pause_with_object )
{
	Dbg_MsgAssert(scriptChecksum,("Zero checksum sent to SpawnScript"));
	
    CSymbolTableEntry *p_entry=Resolve(scriptChecksum);
    if (p_entry)
    {
        if (p_entry->mType==ESYMBOLTYPE_CFUNCTION)
		{
			Dbg_MsgAssert(callbackScript==0,("A callbackScript cannot currently be specified when running SpawnScript on a c-function. (cfunc='%s' callback='%s')",FindChecksumName(scriptChecksum),FindChecksumName(callbackScript)));
			
			// Creating a dummy script to send to the c-function. Normally the c-function would
			// only be called from within a script's update function, and it uses the passed script 
			// pointer in any call to GetScriptInfo if an assert goes off. 
			// So we need to pass a dummy rather than NULL so that it does not crash if it
			// dereferences the pointer.
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
			CScript *p_dummy=new CScript;
			Mem::Manager::sHandle().PopContext();
			
			(*p_entry->mpCFunction)(p_scriptParams,p_dummy);
			
			delete p_dummy;
			return NULL;
		}
		Dbg_MsgAssert(p_entry->mType!=ESYMBOLTYPE_MEMBERFUNCTION,("SpawnScript cannot run the member function '%s'",FindChecksumName(scriptChecksum)));
	}	
	
	
	CScript *p_script=new CScript;
	p_script->SetScript(scriptChecksum, p_scriptParams, NULL);
	#ifdef __NOPT_ASSERT__
	p_script->SetCommentString("Created by SpawnScript");
	#endif
	
	
	p_script->mNode = node;
	p_script->mIsSpawned = true;
	p_script->mId = id;
	p_script->mPaused = false;
	p_script->mNotSessionSpecific=not_session_specific;
	p_script->mPauseWithObject = pause_with_object;
	
	Dbg_MsgAssert(p_script->mpCallbackScriptParams==NULL,("p_script->mpCallbackScriptParams not NULL ?"));
	if (callbackScript)
	{
		p_script->mCallbackScript=callbackScript;
		p_script->mpCallbackScriptParams=new CStruct;
		if (p_callbackParams)
		{
			*p_script->mpCallbackScriptParams+=*p_callbackParams;
		}	
	}
		
	#ifdef __PLAT_WN32__
	// No GameNet stuff if compiling on PC
	#else
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	if( netEnabled && gamenet_man->InNetGame())
	{
		// TODO: Should pass on the not_session_specific flag ...
		SendSpawnScript( scriptChecksum, NULL, node, permanent );
	}
	#endif

	return p_script;
}


bool	ScriptExists(uint32 scriptNameChecksum)
{
	CSymbolTableEntry *p_entry=Resolve(scriptNameChecksum);
	if (p_entry && p_entry->mType==ESYMBOLTYPE_QSCRIPT)
	{
		return true;
	}
	return false;
}

CScript* SpawnScript(const char *p_scriptName, CStruct *p_scriptParams, uint32 callbackScript, CStruct *p_callbackParams, int node, uint32 id, 
					 bool netEnabled, bool permanent, bool not_session_specific, bool pause_with_object )
{
    
	return SpawnScript(Crc::GenerateCRCFromString(p_scriptName),p_scriptParams,callbackScript,p_callbackParams,node,id, netEnabled, permanent, not_session_specific, pause_with_object);
}

// Kills a spawned script, given a pointer to the actual script
void KillSpawnedScript(CScript *p_script)
{
    if (p_script && p_script->mIsSpawned)
	{
		delete p_script;
	}	
}


// Kills all spawned scripts that are either currently running this script directly,
// or have it in their call stack somewhere. (Ie, they will return to it later)
void KillSpawnedScriptsThatReferTo(uint32 checksum)
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		CScript *p_next=GetNextScript(p_script);
		if (p_script->mIsSpawned && p_script->RefersToScript(checksum))
		{
//			delete p_script;
			p_script->ClearScript();
			p_script->ClearEventHandlerTable();
		}	
		p_script=p_next;
	}	
}

// Kills all scripts with this particular Id.
void KillSpawnedScriptsWithId(uint32 id)
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		CScript *p_next=GetNextScript(p_script);
		if (p_script->mIsSpawned && (p_script->mId==id || p_script->GetUniqueId()==id))
		{
// Mick, instead of deleteing the script, we now just clear it
// this stops it from executing, but leaves it in the linked list
// so it can be cleaned up next time around "UpdateSpawnedScripts"
// Scripts that have ClearScript() called will automatically be deleted
// as they have mp_pc set to NULL
//			delete p_script;
			p_script->ClearScript();
			p_script->ClearEventHandlerTable();
		}	
		p_script=p_next;
	}	
}

// Kills all spawned scripts that have the passed object as their parent object.
// This gets called from within Script::KillAllScriptsWithObject.
void KillSpawnedScriptsWithObject(Obj::CObject *p_object)
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		CScript *p_next=GetNextScript(p_script);
		if (p_script->mIsSpawned && p_script->RefersToObject(p_object))
		{
//			delete p_script;
			p_script->ClearScript();
			p_script->ClearEventHandlerTable();
		}	
		p_script=p_next;
	}	
}

// Kills all spawned scripts with a particular Id & parent object.
// Called by Matt's Obj_KillSpawnedScript CObject script member function.
void KillSpawnedScriptsWithObjectAndId(Obj::CObject *p_object, uint32 id, CScript *p_callingScript)
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		CScript *p_next=GetNextScript(p_script);
		if (p_script->mIsSpawned && 
			p_script!=p_callingScript &&
			p_script->RefersToObject(p_object) &&
			p_script->mId==id)
		{
//			delete p_script;
			p_script->ClearScript();
			p_script->ClearEventHandlerTable();
		}	
		p_script=p_next;
	}	
}

// Kills all spawned scripts with a particular Id & parent object.
// Called by Matt's Obj_KillSpawnedScript CObject script member function.
void KillSpawnedScriptsWithObjectAndName(Obj::CObject *p_object, uint32 name, CScript *p_callingScript )
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		CScript *p_next=GetNextScript(p_script);
		if (p_script->mIsSpawned && 
			p_script!=p_callingScript &&
			p_script->RefersToObject(p_object) &&
			p_script->mScriptChecksum==name)
		{
//			delete p_script;
			p_script->ClearScript();
			p_script->ClearEventHandlerTable();
		}	
		p_script=p_next;
	}	
}

// Returns: 0 if spawned script has no ID, or if the script isn't a spawned script
// Otherwise, returns the ID:
uint32 FindSpawnedScriptID(CScript *p_script)
{
	if (!p_script)
	{
		return 0;
	}
	
	if (!p_script->mIsSpawned)
	{
		return 0;
	}

	return p_script->mId;		
}

CScript* FindSpawnedScriptWithID(uint32 id)
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		CScript *p_next=GetNextScript(p_script);
		if (p_script->mIsSpawned && 
			p_script->mId==id)
		{
			return p_script;
		}	
		p_script=p_next;
	}
	
	return NULL;
}


// TODO: This just included to allow compilation of GenerateCRC, remove later.
uint32 GenerateCRC(const char *p_string)
{
	return Crc::GenerateCRCFromString(p_string);
}

// Stops all scripts.
// Currently only used by the StopAllScripts script function, which currently isn't used
// anywhere, but might be handy one day.
void StopAllScripts()
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		p_script->ClearScript();
		p_script->ClearEventHandlerTable();
		p_script=GetNextScript(p_script);
	}	
}

// Stops all scripts with a particular object as their parent, but does not delete them.
// They cannot be restarted after being stopped.
// This is basically a way of killing the scripts without actually
// deleting them, so it won't cause any invalid script pointers.
// Currently gets called at the end of the CObject destructor.
void StopAllScriptsUsingThisObject(Obj::CObject *p_object)
{
	StopScriptsUsingThisObject(p_object, 0);
}

// If scriptCrc != 0, then only stop scripts with that ID, otherwise stop all scripts using object
void StopScriptsUsingThisObject(Obj::CObject *p_object, uint32 scriptCrc)
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		if (p_script->RefersToObject(p_object) && (!scriptCrc || p_script->mScriptChecksum == scriptCrc))
		{
			p_script->ClearScript();
			p_script->ClearEventHandlerTable();
		}	
		p_script=GetNextScript(p_script);
	}	
}

// TODO: brad - this is a last minute fix to StopScriptsUsingThisObject.
// Rather than fixing the existing function so close to release, I've added
// this version to be called only when we know we want to use it.
void StopScriptsUsingThisObject_Proper(Obj::CObject *p_object, uint32 scriptCrc)
{
	CScript *p_script=GetNextScript();
	while ( p_script )
	{
		if ( p_script->RefersToObject( p_object ) && ( !scriptCrc || p_script->RefersToScript( scriptCrc ) ) )
		{
			p_script->ClearScript();
			p_script->ClearEventHandlerTable();
		}	
		p_script=GetNextScript( p_script );
	}	
}


// Kills any stopped scripts, ie scripts with their mpPC null. These
// would be scripts stopped by the above StopAllScriptsUsingThisObject
void KillStoppedScripts()
{
	CScript *p_script=GetNextScript();
	while (p_script)
	{
		CScript *p_next=GetNextScript(p_script);
		
		// Delete the script if it has stopped (mpPC==NULL), but not if it is a spawned script,
		// because otherwise it will leave a dangling pointer in the array of spawned scripts,
		// which will then get dereferenced in UpdateSpawnedScripts and crash.
		// If the script has stopped, the next call to UpdateSpawnedScripts will delete it, so
		// the script is guaranteed to get cleaned up eventually.
		
		#ifdef	__SCRIPT_EVENT_TABLE__		
		if (!p_script->GotScript() && !p_script->mIsSpawned && !p_script->GetEventHandlerTable())
		#else
		if (!p_script->GotScript() && !p_script->mIsSpawned)
		#endif
		{
			
			
			#ifdef __NOPT_ASSERT__
			printf("Cleaning up script '%s'\n",FindChecksumName(p_script->mScriptChecksum));
			#endif
			
			delete p_script;
		}	
		
		p_script=p_next;
	}	
}

// Run by the DumpScripts script function.
// Prints out the names of all the currently existing scripts.
void DumpScripts()
{
	#ifdef __NOPT_ASSERT__
	printf("###########################################################\n\n");
	printf("All the CScripts that currently exist ...\n\n");
	
	CScript *p_scr=GetNextScript();
	int n = 0;
	while (p_scr)
	{
		printf ("%3d %8d ",p_scr->m_last_time/150, p_scr->m_total_time/150);
		p_scr->m_last_time = 0;
		p_scr->m_total_time = 0;	
					  
		printf ("%3d: ",n++);
		if (p_scr->mIsSpawned)
		{
			printf("S ");
		}		
		else
		{
			printf("  ");
		}

		switch (p_scr->GetWaitType())
		{
		case WAIT_TYPE_NONE:
			printf("None            ");
			break;
		case WAIT_TYPE_COUNTER:
			printf("Counter         ");
			break;
		case WAIT_TYPE_ONE_PER_FRAME:
			printf("one_per_frame   ");
			break;
		case WAIT_TYPE_TIMER:
			printf("Timer           ");
			break;
		case WAIT_TYPE_BLOCKED:
			printf("Blocked         ");
			break;
		case WAIT_TYPE_OBJECT_MOVE:
			printf("Ob Move         ");
			break;
		case WAIT_TYPE_OBJECT_ANIM_FINISHED:
			printf("Ob AnimFinished ");
			break;
		case WAIT_TYPE_OBJECT_JUMP_FINISHED:
			printf("Ob JumpFinished ");
			break;
		case WAIT_TYPE_OBJECT_ROTATE:
			printf("Ob Rotate       ");
			break;
		default:
			printf("Unknown ??????  ");
			break;
		}	

		if (p_scr->Finished())
		{
			printf("[Fin] ");
		}
			
		uint32 checksum=p_scr->GetBaseScript();
		if (checksum)
		{
			CSymbolTableEntry *p_sym=LookUpSymbol(checksum);
			Dbg_MsgAssert(p_sym,("NULL pSym ??"));
	
//			printf("%s: %s\n",FindChecksumName(p_sym->mSourceFileNameChecksum),FindChecksumName(checksum));
			printf("%s\n",FindChecksumName(checksum));
		}
		else
		{
			printf (", Checksum is NULL, probably dead script\n");
		}
		
		p_scr=GetNextScript(p_scr);
	}	
	printf("\n");
	#endif
}


#ifdef	__SCRIPT_EVENT_TABLE__		

/*
	Sets up the table that specifies which scripts to run in response to which events.
	
	See object scripting document.
*/
void CScript::SetEventHandlers(Script::CArray *pArray, EReplaceEventHandlers replace)
{
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
	if (!mp_event_handler_table)
	{
		mp_event_handler_table = new Obj::CEventHandlerTable();
	}
	else
	{
		mp_event_handler_table->unregister_all(this);	
	}
	mp_event_handler_table->add_from_script(pArray, replace);
	
	// Mick:  We still need the compress_table for this way of adding event handlers
	// as the "replace" logic otherwise leads to a rapidly growing table.
	// especially on the on-screen keyboard
	mp_event_handler_table->compress_table();
	
	mp_event_handler_table->register_all(this);

	Mem::Manager::sHandle().PopContext();

}


void	CScript::SetEventHandler(uint32 ex, uint32 scr, uint32 group, bool exception, CStruct *p_params)
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());

	// Need to create the table if not there	
	if (!mp_event_handler_table)
	{
		mp_event_handler_table = new Obj::CEventHandlerTable();
	}
	
	// Add the event to the table
	mp_event_handler_table->AddEvent(ex,scr,group, exception, p_params); 	

	// Inregistering the event receiver, all we are doing is adding an
	// script pointer to the list of scripts that handle this 
	// type of exception
	// there is nothing specific to the actual handler
	// if the script is already in the list, then nothing needs changing. 
	Obj::CTracker::Instance()->RegisterEventReceiver(ex, this); 

	Mem::Manager::sHandle().PopContext();
}



/*
	Removes an entry in the event table with the given type id
*/
void CScript::RemoveEventHandler(uint32 type)
{
	if (!mp_event_handler_table) return;
	mp_event_handler_table->remove_entry(type);
//	mp_event_handler_table->compress_table();

// Refresh the Obj::Tracker

	Obj::CTracker::Instance()->UnregisterEventReceiver(type, this); 
		
}



/*
	Removes all entries in the event table with the given group id
*/
void CScript::RemoveEventHandlerGroup(uint32 group)
{
	if (!mp_event_handler_table) return;
	
	mp_event_handler_table->unregister_all(this);	
	
	mp_event_handler_table->remove_group(group);
//	mp_event_handler_table->compress_table();
	
	mp_event_handler_table->register_all(this);	

}

/*
	Dumps the event table
*/
void CScript::PrintEventHandlerTable (   )
{
	Obj::CEventHandlerTable::sPrintTable(mp_event_handler_table);
}

bool CScript::PassTargetedEvent(Obj::CEvent *pEvent, bool broadcast)
{
	if (mp_event_handler_table)
		mp_event_handler_table->pass_event(pEvent, this, broadcast);
	return true;
}

void 	CScript::SetOnExceptionScriptChecksum(uint32 OnExceptionScriptChecksum) 
{
	mOnExceptionScriptChecksum = OnExceptionScriptChecksum;
}

uint32 	CScript::GetOnExceptionScriptChecksum() const 
{
	return mOnExceptionScriptChecksum;
}

void 	CScript::SetOnExitScriptChecksum(uint32 OnExitScriptChecksum) 
{
	mOnExitScriptChecksum = OnExitScriptChecksum;
}

uint32 	CScript::GetOnExitScriptChecksum() const 
{
	return mOnExitScriptChecksum;
}



#endif

} // namespace Script




================================================
FILE: Code/Gel/Scripting/script.h
================================================
#ifndef	__SCRIPTING_SCRIPT_H
#define	__SCRIPTING_SCRIPT_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef	__SCRIPTING_SCRIPTDEFS_H
// TODO: Remove this at some point, only necessary for the CScriptStructure define, to make everything compile.
#include 
#endif

#ifndef __SYS_TIMER_H
#include  // For Tmr::Time
#endif

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

#ifndef __GEL_OBJECT_H
#include 
#endif


#include 


namespace Obj
{
	class CBaseComponent;
	class CEvent;
	class CEventHandlerTable;
}
	
namespace Script
{

// TODO: Remove this at some point, only necessary to make the other game-code headers compile without having
// go through them all including vecpair.h
// Currently they assume everything is declared in script.h
class CPair;

class CStruct;
class CSymbolTableEntry;
struct SStructScript;

// The maximum level of Begin-Repeat nesting.
#define MAX_NESTED_BEGIN_REPEATS 10
#define NESTED_BEGIN_REPEATS_SMALL_BUFFER_SIZE 1

// The maximum number of nested calls to other scripts.                              
#define MAX_RETURN_ADDRESSES 16
// The number of SReturnAddress structures in the small buffer in each CScript
// Mostly 2 is all that is needed, so a buffer of MAX_RETURN_ADDRESSES is only allocated when needed.
#define RETURN_ADDRESSES_SMALL_BUFFER_SIZE 2

// Return values from CScript::Update()
enum EScriptReturnVal
{
    ESCRIPTRETURNVAL_FINISHED,
    ESCRIPTRETURNVAL_BLOCKED,
	ESCRIPTRETURNVAL_WAITING,
    ESCRIPTRETURNVAL_ERROR,
	ESCRIPTRETURNVAL_STOPPED_IN_DEBUGGER,
	ESCRIPTRETURNVAL_FINISHED_INTERRUPT,
};

// Values for CScript::m_wait_type
enum EWaitType
{
	WAIT_TYPE_NONE=0,
	WAIT_TYPE_COUNTER,
	WAIT_TYPE_TIMER,
	WAIT_TYPE_BLOCKED,
	WAIT_TYPE_OBJECT_MOVE,
	WAIT_TYPE_OBJECT_ANIM_FINISHED,
	WAIT_TYPE_OBJECT_JUMP_FINISHED,
	WAIT_TYPE_OBJECT_STOP_FINISHED,
	WAIT_TYPE_OBJECT_ROTATE,
	WAIT_TYPE_STREAM_FINISHED,
	WAIT_TYPE_ONE_PER_FRAME,
};

enum ESingleStepMode
{
	OFF=0,
	WAITING,
	STEP_INTO,
	STEP_OVER,
};

enum
{
	DEFINITELY_TRANSMIT=1
};
	
// Begin-repeat loop structure.    
struct SLoop
{
    // Pointer to the start of the loop, which is the
    // token following the Begin.
    uint8 *mpStart;
	
	// Pointer to the end of the loop, ie the next instruction after the loop.
	uint8 *mpEnd;
    
	bool mGotCount;			// Whether it is a finite loop with a count value.
	bool mNeedToReadCount;	// Whether the count value following the repeat needs to be read.
    int mCount;				// Counts down to zero, and skips past the repeat once it reaches zero.
};

// Saves the original location & other info when calling a subroutine.
struct SReturnAddress
{
    uint32 mScriptNameChecksum;
    CStruct *mpParams;
    uint8 *mpReturnAddress;
	Obj::CObject *mpObject;
	SLoop *mpLoop;
	#ifdef __NOPT_ASSERT__
	uint32 m_if_status;
	#endif
	EWaitType mWaitType;
	Obj::CBaseComponent *mpWaitComponent;
    int mWaitTimer;
	Tmr::Time mStartTime;
	Tmr::Time mWaitPeriod;
	bool mInterrupted;
};

// Script class.
// To run a script, one must create one of these, then call the SetScript member function to
// set which script it is to run.
//
// Then the Update function must be called repeatedly.
// The script will never die by itself, it must be deleted.
//
// Poolable to speed up allocation & deallocation.
// MEMOPT sizeof(CScript) was 1080 last time I checked, due to the arrays of 
// loops, return addresses and if's. Could maybe allocate these off pools instead, if the number
// of CScript's ever gets big enough to be using up a substantial amount of memory.
// There would be a slight speed hit if pools were used, but probably not much, 
// allocating/deallocating off a pool is pretty quick.
#ifdef __PLAT_WN32__
class CScript
#else
class CScript : public Mem::CPoolable
#endif
{
	// CScripts are linked into a list.
	CScript *mp_next;
	CScript *mp_previous;
	friend CScript *GetNextScript(CScript *p_script=NULL);
	
	// If this CScript got setup using a SStructScript (via the SetScript member function below)
	// then this will be a pointer to a copy of the script, and mp_pc will point somewhere into it.
	// Otherwise mp_struct_script will be left as NULL.
	uint8 *mp_struct_script;
	
    // The program counter.
    uint8 *mp_pc;
	
	// Holds the parameters for passing to function calls.
	CStruct *mp_function_params;

    // The input parameters, which get accessed within the script using the <,> operator.
    CStruct *mp_params;
	
    // Begin-Repeat loop stuff.
    // NULL if not in a loop, otherwise points into the following array.
    SLoop *mp_current_loop;
	// If the number of loops needs to be bigger than NESTED_BEGIN_REPEATS_SMALL_BUFFER_SIZE then
	// mp_loops will point to a dynamically allocated array of MAX_NESTED_BEGIN_REPEATS SLoop structures,
	// otherwise mp_loops will equal mp_loops_small_buffer
    SLoop mp_loops_small_buffer[NESTED_BEGIN_REPEATS_SMALL_BUFFER_SIZE];
	SLoop *mp_loops;
	
	

    // Call stack.
    int m_num_return_addresses;
    SReturnAddress mp_return_addresses_small_buffer[RETURN_ADDRESSES_SMALL_BUFFER_SIZE];
	// If the num return addresses needs to be bigger than RETURN_ADDRESSES_SMALL_BUFFER_SIZE then
	// mp_return_addresses will point to a dynamically allocated array of MAX_RETURN_ADDRESSES SReturnAddress structures,
	// otherwise mp_return_addresses will equal mp_return_addresses_small_buffer
	SReturnAddress *mp_return_addresses;
	
	#ifdef __NOPT_ASSERT__
	// Used to determine when to step-over a script command when the script
	// is being stepped through in the debugger.
	int m_last_num_return_addresses_when_halted;
	#endif
	
    // Used by the Wait function to cause a script to hiccup.
    int m_wait_timer;
	
	// Used by the WaitTime function to cause the script to wait for an
	// absolute amount of time.
	Tmr::Time m_start_time;
	Tmr::Time m_wait_period;

	// How the script is waiting.
	EWaitType m_wait_type;
	Obj::CBaseComponent *mp_wait_component;

	

	// a helpful member added by Ryan
	uint32	m_unique_id;
	// used for computing unique IDs
	static int s_next_unique_id;
	
	// Gets set to true if the script was forced to call another script by a call to Interrupt.
	// This will get stored in the call stack.
	// Needed to prevent the call to Update() that Interrupt does from continuing the execution of
	// the interrupted script when the called script finishes.
	bool m_interrupted;

	void call_script(uint32 newScriptChecksum, uint8 *p_script, CStruct *p_params, Obj::CObject *p_object, bool interrupt=false);
	void set_script(uint32 scriptChecksum, uint8 *p_script, CStruct *p_params, Obj::CObject *p_object);
	
	// This flag is to prevent multiple calls to process_waits in nested calls to CScript::Update(),
	// such as when a script command causes an interrupt to occur on itself.
	bool m_skip_further_process_waits;	
	
	void process_waits();
	
	void load_function_params();
	bool run_member_function(uint32 functionName, Obj::CObject *p_substituteObject=NULL);
	bool run_cfunction(bool (*p_cfunc)(CStruct *pParams, CScript *pCScript));
	uint32 get_name();
	
	bool execute_command();
	
	
	#ifdef __NOPT_ASSERT__
	bool 			m_if_debug_on;
	uint32 			m_if_status; 			// For detecting spurious else's
	#endif


	#ifdef __NOPT_ASSERT__
	// Used by script debugger code.
	uint32 m_last_transmitted_info_checksum;
	bool m_being_watched_in_debugger;
	ESingleStepMode m_single_step_mode;
	enum
	{
		MAX_COMMENT_STRING_CHARS=63,
	};
	char mp_comment_string[MAX_COMMENT_STRING_CHARS+1];
	int m_originating_script_line_number;
	uint32 m_originating_script_name;
	float m_last_instruction_time_taken;
	float m_total_instruction_time_taken;
	
	#ifdef __PLAT_WN32__
	uint32 *write_callstack_entry(uint32 *p_buf, int bufferSize, uint32 scriptNameChecksum, uint8 *p_PC, CStruct *p_params, Obj::CObject *p_ob) {}
	void check_if_needs_to_be_watched_in_debugger() {}
	void advance_pc_to_next_line_and_halt() {}
	#else
	uint32 *write_callstack_entry(uint32 *p_buf, int bufferSize, uint32 scriptNameChecksum, uint8 *p_PC, CStruct *p_params, Obj::CObject *p_ob);
	void check_if_needs_to_be_watched_in_debugger();
	void advance_pc_to_next_line_and_halt();
	#endif
	#endif
	
	void execute_if();
	void execute_else();
	void execute_endif();
	
	
	void skip_to_after_endswitch();
	void execute_switch();
	
	void execute_begin();
	void execute_repeat();
	void execute_break();
	
	bool execute_return();

public:

	#ifdef __NOPT_ASSERT__
	#ifdef __PLAT_WN32__
	void SetCommentString(const char *p_comment) {}
	void SetOriginatingScriptInfo(int lineNumber, uint32 scriptName) {}
	void TransmitInfoToDebugger(bool definitely_transmit=false) {}
	void TransmitBasicInfoToDebugger() {}
	void WatchInDebugger(bool stopScriptImmediately) {}
	bool BeingWatchedInDebugger() {return false;}
	void StopWatchingInDebugger() {}
	void DebugStop() {}
	void DebugStepInto() {}
	void DebugStepOver() {}
	void DebugGo() {}
	#else	
	void SetCommentString(const char *p_comment);
	void SetOriginatingScriptInfo(int lineNumber, uint32 scriptName);
	// This gets called for all CScript's every frame.
	// It transmits lots of info about the script to the script debugger running on the PC
	void TransmitInfoToDebugger(bool definitely_transmit=false);
	void TransmitBasicInfoToDebugger();
	void WatchInDebugger(bool stopScriptImmediately);
	bool BeingWatchedInDebugger() {return m_being_watched_in_debugger;}
	void StopWatchingInDebugger();
	void DebugStop();
	void DebugStepInto();
	void DebugStepOver();
	void DebugGo();
	
	int GetNumReturnAddresses() {return m_num_return_addresses;}
	bool UsingBigCallstackBuffer();
	bool UsingBigLoopBuffer();
	#endif
	#endif

#ifdef	__SCRIPT_EVENT_TABLE__		
	// Event Handler/Exception Stuff
	void				SetEventHandler(uint32 ex, uint32 scr, uint32 group, bool exception, CStruct* p_params);
	void  				SetEventHandlers(Script::CArray *pArray, EReplaceEventHandlers replace = DONT_REPLACE_HANDLERS);
	void				RemoveEventHandler(uint32 type);
	void				RemoveEventHandlerGroup(uint32 group);
	void 				SetOnExceptionScriptChecksum(uint32 OnExceptionScriptChecksum);
	uint32 				GetOnExceptionScriptChecksum() const; 
	void 				SetOnExitScriptChecksum(uint32 OnExceptionScriptChecksum);
	uint32 				GetOnExitScriptChecksum() const; 
	bool				PassTargetedEvent(Obj::CEvent *pEvent, bool broadcast = false);	// return true if object still valid
#endif

	void				PrintEventHandlerTable (   );
	
	CStruct *GetParams() {Dbg_MsgAssert(mp_params,("NULL mp_params ?")); return mp_params;}

	int	mNode;		// Number of the node that caused this script to be spawned, -1 if none specific

    // If NULL then the script is a free script, eg StartUp.
    // If non-NULL, then the script is associated with some object.
    Obj::CObjectPtr mpObject;

    // Checksum of the name of this script.
    uint32 mScriptChecksum;

	void SetWait(EWaitType type, Obj::CBaseComponent *p_component);
	void ClearWait();
	EWaitType GetWaitType() {return m_wait_type;}


	bool mIsSpawned:1;	// True if this is a spawned script.
	// Note: These next members only apply if the script is spawned.
	bool mNotSessionSpecific:1; // If this is true then the spawned script will not get
								// deleted by DeleteSpawnedScripts
	bool mPaused:1;
	bool mPauseWithObject:1;	// If this is true then the spawned script will pause when its object's ShouldUpdatePauseWithObjectScripts
								// returns false.  CCompositeObjects return	false when they are paused.
	uint32 mId;
	// An optional callback script, which gets run as soon as the spawned script completes.
	uint32 mCallbackScript;
	CStruct *mpCallbackScriptParams;

	CScript();
    ~CScript();

    void SetScript(uint32 scriptChecksum, CStruct *p_params=NULL, Obj::CObject *p_object=NULL);
    void SetScript(const char *p_scriptName, CStruct *p_params=NULL, Obj::CObject *p_object=NULL);
    void SetScript(const SStructScript *p_structScript, CStruct *p_params=NULL, Obj::CObject *p_object=NULL);
	
	CStruct *MakeParamsSafeFromDeletionByClearScript(CStruct *p_params);
	void ClearScript();
	void ClearEventHandlerTable();
	bool RefersToScript(uint32 checksum);
	bool RefersToObject(Obj::CObject *p_object);
	void RemoveReferencesToObject(Obj::CObject *p_object);
	
	bool AllScriptsInCallstackStillExist();
	uint32 GetBaseScript();
	void Restart();
	EScriptReturnVal Interrupt(uint32 newScriptChecksum, CStruct *p_params=NULL);
	
    EScriptReturnVal Update();
    bool Finished();

	void Block();
	void UnBlock();
	bool getBlocked();

	void Clear();
    void Wait(int numFrames);
    void WaitOnePerFrame(int numFrames);
	void WaitTime(Tmr::Time period);
	bool GotScript();

	uint32 GetUniqueId() {return m_unique_id;}
	
	// Stops the script from being associated with an object, ie, sets mpObject to NULL.
	// Allows the script to continue executing though, so the script must not contain any
	// further member functions, otherwise the code will assert.
	// This function also sets mWaitType=WAIT_TYPE_NONE; to ensure the Update function does
	// not assert if it is waiting for the object to finish doing something.
	void DisassociateWithObject(Obj::CObject *pObjectToDisconnectFrom);
	
	#ifdef __NOPT_ASSERT__
	const char *GetScriptInfo();
	int GetCurrentLineNumber(void);
	const char *GetSourceFile(void);
	
	// and let's have it for debugging for now, in case people (Mick!) leave it in by mistake and screw up the build
	const inline char *GetScriptInfo( void ) const { return ""; }
	#else // #ifdef __NOPT_ASSERT__
#	ifdef __PLAT_XBOX__
	const inline char *GetScriptInfo( void ) const { return ""; }
#	endif // ifdef __PLAT_XBOX__

	#ifdef __PLAT_WN32__
	const char *GetScriptInfo() const {return "";}
	#endif
	
	#endif // #ifdef __NOPT_ASSERT__
	
	void SwitchOnIfDebugging();
	void SwitchOffIfDebugging();

// These are pseudo-private, for debugging profiling only
	#ifdef __NOPT_ASSERT__
	int				m_last_time;			// time spend in last update	
	int 			m_total_time;			// total time spent updating
	#endif
	
#ifdef	__SCRIPT_EVENT_TABLE__		
private:	
	Obj::CEventHandlerTable *	mp_event_handler_table;
	
	// Script to run when there is an exception	
	// (needed here, since we are getting rid of CExceptionComponent, to replace it with one 
	// single event system
	uint32 			mOnExceptionScriptChecksum;
	uint32 			mOnExitScriptChecksum;
public:
	Obj::CEventHandlerTable * GetEventHandlerTable() {return mp_event_handler_table;}
	Obj::CRefCounted			m_reference_counter;		// having a reference counter allows us to have a safe smart pointer	
#endif	 



};

void SendScript( uint32 scriptChecksum, CStruct *p_params, Obj::CObject *p_object );

void RunScript(const char *p_scriptName, CStruct *p_params=NULL, Obj::CObject *p_object=NULL,
				bool netScript = false );
void RunScript(uint32 scriptChecksum, CStruct *p_params=NULL, Obj::CObject *p_object=NULL,
				bool netScript = false, const char *p_scriptName=NULL );

void DeleteSpawnedScripts();
void UpdateSpawnedScripts();
void PauseSpawnedScripts(bool status);
void UnpauseSpawnedScript(CScript* p_script);
uint32 NumSpawnedScriptsRunning();
bool	ScriptExists(uint32 scriptNameChecksum);

CScript* SpawnScript(uint32 scriptChecksum, CStruct *p_script_params=NULL, 
					 uint32 callbackScript=NO_NAME, CStruct *p_callbackParams=NULL,
					 int node = -1, uint32 id=0,
					 bool netEnabled = false, bool permanent = false, bool not_session_specific=false, bool pause_with_object=false );
CScript* SpawnScript(const char *pScriptName, CStruct *p_script_params=NULL,
					 uint32 callbackScript=NO_NAME, CStruct *p_callbackParams=NULL,
					 int node = -1, uint32 id=0,
					 bool netEnabled = false, bool permanent = false, bool not_session_specific=false, bool pause_with_object=false );
void KillSpawnedScript(CScript *p_script);
void KillSpawnedScriptsThatReferTo(uint32 checksum);
void KillSpawnedScriptsWithId(uint32 id);
void KillSpawnedScriptsWithObject(Obj::CObject *p_object);
void KillSpawnedScriptsWithObjectAndId(Obj::CObject *p_object, uint32 id, CScript *p_callingScript);
void KillSpawnedScriptsWithObjectAndName(Obj::CObject *p_object, uint32 name, CScript *p_callingScript);
uint32 FindSpawnedScriptID(CScript *p_script);
CScript* FindSpawnedScriptWithID(uint32 id);
void SendSpawnScript( uint32 scriptChecksum, Obj::CObject *p_object, int node, bool permanent );


CScript *GetScriptWithUniqueId(uint32 id);

// These should not be here, but should be in the game-specific scripting directory.
// Just putting them here for the moment to get things to compile ...
class CArray;

// TODO: This just included to allow compilation of GenerateCRC, remove later.
uint32 GenerateCRC(const char *p_string);

void StopAllScripts();
void StopAllScriptsUsingThisObject(Obj::CObject *p_object);
void StopScriptsUsingThisObject(Obj::CObject *p_object, uint32 scriptCrc);
void StopScriptsUsingThisObject_Proper(Obj::CObject *p_object, uint32 scriptCrc);
void KillStoppedScripts();
void DumpScripts();

uint32 GetNumCScripts();

CScript *GetCurrentScript();

#ifdef	__NOPT_ASSERT__
const char * GetCurrentScriptInfo();
#endif

} // namespace Script

#endif	// __SCRIPTING_SCRIPT_H



================================================
FILE: Code/Gel/Scripting/scriptcache.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef __PLAT_NGC__
#include 
#include 
#endif		// __PLAT_NGC__

#ifdef NO_SCRIPT_CACHING
#else
DefinePoolableClass(Script::CScriptCacheEntry);
#endif

// Make CScriptCache::GetDebugInfo determine how much the cache is 'thrashing' (ie, average number of new script
// entries created per frame over last second, & indicate the max)
// Make scripts not get removed from the cache straight away when their usage hits 0, but just
// remove the oldest zero-usage script when the heap is full and a new script needs to go in.
// Make CScriptCache::GetDebugInfo sort the array of scripts in order of usage

// Implement compression in qcomp.
// Test, test qbr, test size of cache required, test speed.
// Make it possible to switch back to no caching very easily. (mybe even dynamically?)
namespace Script
{

DefineSingletonClass( CScriptCache, "Script cache module" );

#ifdef NO_SCRIPT_CACHING
CScriptCache::CScriptCache()
{
}

CScriptCache::~CScriptCache()
{
}

void CScriptCache::v_start_cb ( void )
{
}

void CScriptCache::v_stop_cb ( void )
{
}

uint8 *CScriptCache::GetScript(uint32 scriptName)
{
    CSymbolTableEntry *p_entry=Resolve(scriptName);
    if (p_entry)
    {
        Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_QSCRIPT,("Symbol %s is not a QScript",FindChecksumName(scriptName)));
        Dbg_MsgAssert(p_entry->mpScript,("NULL p_entry->mpScript"));

		return p_entry->mpScript+4; // +4 to skip over the contents checksum
	}
	
	return NULL;
}

void CScriptCache::DecrementScriptUsage(uint32 scriptName)
{
}

void CScriptCache::RefreshAfterReload(uint32 scriptName)
{
}

void CScriptCache::DeleteZeroUsageStraightAway()
{
}

void CScriptCache::DeleteOldestZeroUsageWhenNecessary()
{
}

#ifdef __NOPT_ASSERT__
void CScriptCache::GetDebugInfo( Script::CStruct* p_info )
{
	p_info->AddChecksum(NONAME,CRCD(0xf34b1528,"ScriptCachingDisabled"));
}
#endif

#else

CScriptCacheEntry::CScriptCacheEntry()
{
	mpDecompressedScript=NULL;
	mUsage=0;
	mScriptNameChecksum=0;
	mpNext=NULL;
	mpPrevious=NULL;
}

CScriptCacheEntry::~CScriptCacheEntry()
{
	// This is just to make sure nothing accidentally deletes an entry in the CScriptCache's
	// list of zero-usage entries. It won't work if there is only one entry in the list, but
	// it's better than nothing.
	Dbg_MsgAssert(mpNext==NULL,("CScriptCacheEntry::mpNext not NULL !"));
	Dbg_MsgAssert(mpPrevious==NULL,("CScriptCacheEntry::mpPrevious not NULL !"));
	
	if (mpDecompressedScript)
	{
		Mem::Free(mpDecompressedScript);
	}	
}	

CScriptCache::CScriptCache()
{
	Dbg_MsgAssert(IDEAL_MAX_DECOMPRESSED_SCRIPTS < MAX_DECOMPRESSED_SCRIPTS,("IDEAL_MAX_DECOMPRESSED_SCRIPTS should be less than MAX_DECOMPRESSED_SCRIPTS"));
	
	#ifdef __NOPT_ASSERT__
	mp_logic_task=NULL;
	m_current_decompress_count_index=0;
	for (int i=0; i(8);
	Mem::Manager::sHandle().PopContext();
	
	mp_first_zero_usage=NULL;
	mp_last_zero_usage=NULL;
	
	m_delete_zero_usage_straight_away=false;
}

CScriptCache::~CScriptCache()
{
	mp_cache_hash_table->IterateStart();
	CScriptCacheEntry *p_cache_entry = mp_cache_hash_table->IterateNext();
	while (p_cache_entry)
	{
		// Would it be better to just flush all the items after this loop instead?
		// (Ie, could flushing here confuse IterateNext ?)
		mp_cache_hash_table->FlushItem(p_cache_entry->mScriptNameChecksum);
		
		// Clear these to prevent ~CScriptCacheEntry from asserting.
		p_cache_entry->mpNext=NULL;
		p_cache_entry->mpPrevious=NULL;
		delete p_cache_entry;
		
		p_cache_entry = mp_cache_hash_table->IterateNext();
	}

	delete mp_cache_hash_table;
	
	#ifdef __NOPT_ASSERT__
	if (mp_logic_task)
	{
		delete mp_logic_task;
	}	
	#endif	
}

void CScriptCache::v_start_cb ( void )
{
	#ifdef __NOPT_ASSERT__
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	mp_logic_task = new Tsk::Task< CScriptCache > ( CScriptCache::s_logic_code, *this );

	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	mlp_manager->AddLogicTask( *mp_logic_task );
	Mem::Manager::sHandle().PopContext();
	#endif
}

void CScriptCache::v_stop_cb ( void )
{
	#ifdef __NOPT_ASSERT__
	mp_logic_task->Remove();
	#endif
}

void CScriptCache::add_to_zero_usage_list(CScriptCacheEntry *p_entry)
{
	Dbg_MsgAssert(p_entry,("NULL p_entry sent to add_to_zero_usage_list"));
	
	#ifdef __NOPT_ASSERT__
	CScriptCacheEntry *p_search=mp_first_zero_usage;
	while (p_search)
	{
		Dbg_MsgAssert(p_search != p_entry,("p_entry is already in the zero-usage list !"));
		p_search=p_search->mpNext;
	}	
	#endif
	
	p_entry->mpPrevious=NULL;
	p_entry->mpNext=mp_first_zero_usage;
	mp_first_zero_usage=p_entry;
	if (p_entry->mpNext)
	{
		p_entry->mpNext->mpPrevious=p_entry;
	}
	if (!mp_last_zero_usage)
	{
		mp_last_zero_usage=mp_first_zero_usage;
	}	
}

void CScriptCache::remove_from_zero_usage_list(CScriptCacheEntry *p_entry)
{
	Dbg_MsgAssert(p_entry,("NULL p_entry sent to remove_from_zero_usage_list"));
	
	// Unlink the entry from the list.
	if (p_entry->mpPrevious)
	{
		p_entry->mpPrevious->mpNext=p_entry->mpNext;
	}
	if (p_entry->mpNext)
	{
		p_entry->mpNext->mpPrevious=p_entry->mpPrevious;
	}
		
	// Update mp_last_zero_usage and mp_first_zero_usage
	if (p_entry==mp_first_zero_usage)
	{
		mp_first_zero_usage=p_entry->mpNext;
	}
	if (p_entry==mp_last_zero_usage)
	{
		mp_last_zero_usage=p_entry->mpPrevious;
	}
	
	p_entry->mpNext=NULL;
	p_entry->mpPrevious=NULL;
}

// This function must be used to delete a CScriptCacheEntry rather than deleting it directly,
// since this function will update the list of zero-usage entries if necessary.
void CScriptCache::delete_entry(CScriptCacheEntry *p_entry)
{
	Dbg_MsgAssert(p_entry,("NULL p_entry sent to delete_entry"));
	
	mp_cache_hash_table->FlushItem(p_entry->mScriptNameChecksum);
	// Note: If the entry is not in the zero-usage list, this function won't do anything, so safe to call.
	remove_from_zero_usage_list(p_entry);
	delete p_entry;
}

// The scriptName is only passed so that the assert can print the name of the script
// if no space could be freed up for it.
void CScriptCache::remove_some_old_scripts(int space_required, uint32 scriptName)
{
	while (true)
	{
		int largest_malloc_possible=Mem::Manager::sHandle().ScriptHeap()->LargestFreeBlock();
		largest_malloc_possible-=128; // Actually, 32 seems OK, but have a bigger margin just to be sure.
		
		if (space_required <= largest_malloc_possible)
		{
			// Hurrah! It's all clear to decompress another script since we know there is enough heap to hold it.
			
			// Try to keep the total number of decompressed scripts within IDEAL_MAX_DECOMPRESSED_SCRIPTS
			// by removing unused ones until either enough have been removed or there are no more to remove.
			
			// This way the average memory usage can be controlled by tweaking IDEAL_MAX_DECOMPRESSED_SCRIPTS
			// whilst spikes in the number of scripts are still permitted up to the limit set by
			// the pool size of MAX_DECOMPRESSED_SCRIPTS. If that limit is reached, the code will assert.
			while (CScriptCacheEntry::SGetNumUsedItems() >= IDEAL_MAX_DECOMPRESSED_SCRIPTS && mp_last_zero_usage)
			{
				delete_entry(mp_last_zero_usage);
			}	
			break;
		}	

		// Free up some space by deleting the oldest zero-usage decompressed script.
		
		if (!mp_last_zero_usage)
		{
			// Eeeek! There's nothing left to delete ...
			Dbg_MsgAssert(0,("Script heap overflow when trying to allocate decompressed script '%s' of size %d bytes!",Script::FindChecksumName(scriptName),space_required));
		}
		
		//printf("%d: Deleting %s from cache, new free space = %d\n",space_required,Script::FindChecksumName(mp_last_zero_usage->mScriptNameChecksum),Mem::Manager::sHandle().ScriptHeap()->LargestFreeBlock()-128);
		delete_entry(mp_last_zero_usage);
	}	
}

uint8 *CScriptCache::GetScript(uint32 scriptName)
{
    CSymbolTableEntry *p_entry=Resolve(scriptName);
    if (p_entry)
    {
        Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_QSCRIPT,("Symbol %s is not a QScript",FindChecksumName(scriptName)));
#ifndef __PLAT_NGC__
        Dbg_MsgAssert(p_entry->mpScript,("NULL p_entry->mpScript"));
#endif		// __PLAT_NGC__
		
		// Change the passed scriptName to be the actual scriptName so that when decompressed it gets registered in
		// the cache under the correct name.
		// Otherwise, if a SpawnScript Foo is done after Foo has been changed using the 'change' command, it
		// will spawn the old script again, since it will be in the cache under Foo.
		scriptName=p_entry->mNameChecksum;
	}	


	CScriptCacheEntry *p_cache_entry=mp_cache_hash_table->GetItem(scriptName);
	if (p_cache_entry)
	{
		++p_cache_entry->mUsage;
		
		#ifdef __NOPT_ASSERT__
		if (p_cache_entry->mUsage==1)
		{
			++m_num_used_scripts;
			if (m_num_used_scripts > m_max_used_scripts)
			{
				m_max_used_scripts=m_num_used_scripts;
			}	
		}	
		#endif
		
		remove_from_zero_usage_list(p_cache_entry);
		return p_cache_entry->mpDecompressedScript;
	}	
		
    if (p_entry)
    {
#ifdef __PLAT_NGC__
		enum
		{
			COMPRESS_BUFFER_SIZE=20000,
		};	
		uint8 p_compress_buffer[COMPRESS_BUFFER_SIZE];
		uint32 header[(SCRIPT_HEADER_SIZE/4)];
		NsDMA::toMRAM( header, p_entry->mScriptOffset, SCRIPT_HEADER_SIZE );
		uint32 uncompressed_size		= header[1];
		uint32 compressed_size			= header[2];
		NsDMA::toMRAM( p_compress_buffer, p_entry->mScriptOffset + SCRIPT_HEADER_SIZE, compressed_size );
#else
		// Note: The script contents checksum is at offset 0, but it is not needed.
		uint32 uncompressed_size		= *(uint32*)(p_entry->mpScript+4);
		uint32 compressed_size			= *(uint32*)(p_entry->mpScript+8);
#endif		// __PLAT_NGC__
		
		remove_some_old_scripts(uncompressed_size,scriptName);

		CScriptCacheEntry *p_cache_entry=new CScriptCacheEntry;
		
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
		uint8 *p_new_script=(uint8*)Mem::Malloc(uncompressed_size);
		Mem::Manager::sHandle().PopContext();
		
		if (uncompressed_size > compressed_size)
		{
			#ifdef	__NOPT_ASSERT__		
			uint8 *p_end=
			#endif
#ifdef __PLAT_NGC__
				DecodeLZSS(p_compress_buffer,p_new_script,compressed_size);
#else
				DecodeLZSS(p_entry->mpScript+SCRIPT_HEADER_SIZE,p_new_script,compressed_size);
#endif		// __PLAT_NGC__
			Dbg_MsgAssert(p_end==p_new_script+uncompressed_size,("Eh? p_end is not right?"));
		}
		else
		{
			// The script is uncompressed so just copy it over. Saves, errr, 1K altogether, oh well.
			Dbg_MsgAssert(uncompressed_size == compressed_size,("Expected uncompressed_size==compressed_size"));
#ifdef __PLAT_NGC__

			uint8 *p_source=p_compress_buffer;
#else
			uint8 *p_source=p_entry->mpScript+SCRIPT_HEADER_SIZE;
#endif		// __PLAT_NGC__
			uint8 *p_dest=p_new_script;
			for (uint32 i=0; impDecompressedScript=p_new_script;
		p_cache_entry->mUsage=1;
		p_cache_entry->mScriptNameChecksum=scriptName;
		
		#ifdef __NOPT_ASSERT__
		++m_num_used_scripts;
		if (m_num_used_scripts > m_max_used_scripts)
		{
			m_max_used_scripts=m_num_used_scripts;
		}	
		#endif
		
		
		mp_cache_hash_table->PutItem(scriptName,p_cache_entry);

		#ifdef __NOPT_ASSERT__
		++mp_decompress_counts[m_current_decompress_count_index];
		#endif
		
		return p_cache_entry->mpDecompressedScript;
	}
	
	return NULL;
}

void CScriptCache::DecrementScriptUsage(uint32 scriptName)
{
	CScriptCacheEntry *p_cache_entry=mp_cache_hash_table->GetItem(scriptName);
	if (p_cache_entry)
	{
		Dbg_MsgAssert(p_cache_entry->mUsage,("Zero cache entry usage for script '%s'",Script::FindChecksumName(scriptName)));
		
		--p_cache_entry->mUsage;
		if (p_cache_entry->mUsage==0)
		{
			#ifdef __NOPT_ASSERT__
			--m_num_used_scripts;
			#endif
			
			if (m_delete_zero_usage_straight_away)
			{
				// For testing
				delete_entry(p_cache_entry);
			}
			else
			{
				add_to_zero_usage_list(p_cache_entry);
			}	
		}
	}
}

// Needed for when a script is qbr'd.
void CScriptCache::RefreshAfterReload(uint32 scriptName)
{
	CScriptCacheEntry *p_cache_entry=mp_cache_hash_table->GetItem(scriptName);
	if (p_cache_entry)
	{
		// Store the old usage
		uint32 old_usage=p_cache_entry->mUsage;
		
		// Remove from the cache, then add it again so that it is up to date.
		delete_entry(p_cache_entry);
		GetScript(scriptName);
		
		// Put the usage value back.
		p_cache_entry=mp_cache_hash_table->GetItem(scriptName);
		Dbg_MsgAssert(p_cache_entry,("NULL p_cache_entry ?"));
		p_cache_entry->mUsage=old_usage;
	}	
}

void CScriptCache::DeleteZeroUsageStraightAway()
{
	m_delete_zero_usage_straight_away=true;
}

void CScriptCache::DeleteOldestZeroUsageWhenNecessary()
{
	m_delete_zero_usage_straight_away=false;
}

// Scans through the currently decompressed scripts looking to see
// which one p_token points into, and returns the name of the script, or Unknown
// if it can;t find it.
const char *CScriptCache::GetSourceFile(uint8 *p_token)
{
	mp_cache_hash_table->IterateStart();
	CScriptCacheEntry *p_cache_entry = mp_cache_hash_table->IterateNext();
	while (p_cache_entry)
	{
		if (PointsIntoScript(p_token,p_cache_entry->mpDecompressedScript))
		{
		    CSymbolTableEntry *p_entry=Resolve(p_cache_entry->mScriptNameChecksum);
			Dbg_MsgAssert(p_entry,("Eh? Decompressed script has no source symbol table entry ?"));
			return Script::FindChecksumName(p_entry->mSourceFileNameChecksum);
		}
		
		p_cache_entry = mp_cache_hash_table->IterateNext();		
	}
	
	return "Unknown";
}

#ifdef __NOPT_ASSERT__
void CScriptCache::s_logic_code ( const Tsk::Task< CScriptCache >& task )
{
	CScriptCache&	mdl = task.GetData();
	Dbg_AssertType ( &task, Tsk::Task< CScriptCache > );

	++mdl.m_current_decompress_count_index;
	if (mdl.m_current_decompress_count_index >= MAX_DECOMPRESS_COUNTS)
	{
		mdl.m_current_decompress_count_index=0;
	}	
	mdl.mp_decompress_counts[mdl.m_current_decompress_count_index]=0;
}

void CScriptCache::GetDebugInfo( Script::CStruct* p_info )
{
	Script::CStruct *p_script_cache_info=new Script::CStruct;
	
	p_script_cache_info->AddInteger(CRCD(0xe8081c20,"NumUsedScripts"),m_num_used_scripts);
	p_script_cache_info->AddInteger(CRCD(0x746c6f5b,"MaxUsedScripts"),m_max_used_scripts);
	
	int max=0;
	int total=0;
	for (int i=0; i max)
		{
			max=mp_decompress_counts[i];
		}
		total+=mp_decompress_counts[i];
	}	
	p_script_cache_info->AddInteger(CRCD(0xc5eb1eb2,"MaxDecompressCount"),max);
	p_script_cache_info->AddInteger(CRCD(0xdd84ffd8,"AverageDecompressCount"),total/MAX_DECOMPRESS_COUNTS);
	
	mp_cache_hash_table->IterateStart();
	int num_scripts_in_cache=0;
	CScriptCacheEntry *p_cache_entry = mp_cache_hash_table->IterateNext();
	while (p_cache_entry)
	{
		++num_scripts_in_cache;
		p_cache_entry = mp_cache_hash_table->IterateNext();		
	}

	p_script_cache_info->AddInteger(CRCD(0xb6d1536,"NumScriptsInCache"),num_scripts_in_cache);

	if (num_scripts_in_cache)
	{
		Script::CArray *p_scripts_array=new Script::CArray;
		p_scripts_array->SetSizeAndType(num_scripts_in_cache,ESYMBOLTYPE_STRUCTURE);
		
		int alloc_total=0;
		mp_cache_hash_table->IterateStart();
		int array_index=0;
		p_cache_entry = mp_cache_hash_table->IterateNext();
		while (p_cache_entry)
		{
			Script::CStruct *p_cache_entry_info=new Script::CStruct;
			p_cache_entry_info->AddChecksum(CRCD(0xe37e78c5,"Script"),p_cache_entry->mScriptNameChecksum);
			p_cache_entry_info->AddInteger(CRCD(0x2f14a18f,"Usage"),p_cache_entry->mUsage);
			
			int alloc_size=Mem::GetAllocSize(p_cache_entry->mpDecompressedScript);
			p_cache_entry_info->AddInteger(CRCD(0x454a3b10,"ScriptSize"),alloc_size);
			alloc_total+=alloc_size;
			
			p_scripts_array->SetStructure(array_index,p_cache_entry_info);
			p_cache_entry = mp_cache_hash_table->IterateNext();		
			++array_index;
		}
		
		p_script_cache_info->AddInteger(CRCD(0x43dde78c,"TotalScriptSizes"),alloc_total);
		p_script_cache_info->AddArrayPointer(CRCD(0xf964b9dd,"CachedScripts"),p_scripts_array);
	}
		
	p_info->AddStructurePointer(CRCD(0x3d2bde05,"ScriptCacheInfo"),p_script_cache_info);
}
#endif // #ifdef __NOPT_ASSERT__

#endif // #ifdef NO_SCRIPT_CACHING

} // namespace Script



================================================
FILE: Code/Gel/Scripting/scriptcache.h
================================================
#ifndef	__SCRIPTING_SCRIPTCACHE_H
#define	__SCRIPTING_SCRIPTCACHE_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

#ifndef __CORE_LIST_HASHTABLE_H
#include 
#endif

#ifndef __GEL_MODULE_H
#include 
#endif

// If this is defined then GetScript will just return the mpScript as stored in the
// CSymbolTableEntry.
#ifdef __PLAT_WN32__
// No caching when compiled on PC since it is not needed and would require linking in memory manager stuff.
#define NO_SCRIPT_CACHING
#endif
//#define NO_SCRIPT_CACHING

// The total size of the CScriptCacheEntry pool.
// This is the max number of decompressed scripts that can exist at once.
// The code will assert if this number of scripts is exceeded.
// This may be set to be quite large since the CScriptCacheEntry is quite small, 24 bytes each.
// It needs to be fairly large to allow for spikes in the number of scripts executing (see TT1417)
#define MAX_DECOMPRESSED_SCRIPTS 300

// The code will attempt to keep the total number of decompressed scripts to no more than this value
// by deleting unused scripts.
// This allows us to control the average number of decompressed scripts whilst still allowing spikes
// up to MAX_DECOMPRESSED_SCRIPTS without the code asserting.
// Allowing spikes is necessary because sometimes a lot of scripts may exist which don't use up much
// script heap altogether.

// The larger IDEAL_MAX_DECOMPRESSED_SCRIPTS is, the less often the code will have to
// decompress scripts during gameplay, hence less cpu time will be used.
// However, the bigger it is, the more script heap will be used up storing decompressed scripts.
#define IDEAL_MAX_DECOMPRESSED_SCRIPTS 100

namespace Script
{
class CStruct;

#ifdef NO_SCRIPT_CACHING
#else
class CScriptCacheEntry : public Mem::CPoolable
{
public:
	CScriptCacheEntry();
	~CScriptCacheEntry();

	// The script name is stored here so that the make_enough_space function
	// is able to flush entries from the hash table, given only a CScriptCacheEntry pointer.
	// (Entries can only be flushed from the hash table if their key is known)
	uint32 mScriptNameChecksum;
	
	uint8 *mpDecompressedScript;
	uint32 mUsage;
	
	CScriptCacheEntry *mpNext;
	CScriptCacheEntry *mpPrevious;
};
#endif // #ifdef NO_SCRIPT_CACHING

#ifdef NO_SCRIPT_CACHING
class CScriptCache   // No deriving it from Mdl::Module if not usieng it, as that foces uncessary linkages
#else
class CScriptCache  : public Mdl::Module
#endif
{
	DeclareSingletonClass( CScriptCache );
	
	void v_start_cb ( void );
	void v_stop_cb ( void );

	#ifdef NO_SCRIPT_CACHING
	#else

	void add_to_zero_usage_list(CScriptCacheEntry *p_entry);
	void remove_from_zero_usage_list(CScriptCacheEntry *p_entry);
	void delete_entry(CScriptCacheEntry *p_entry);
	void remove_some_old_scripts(int space_required, uint32 scriptName);

	#ifdef __NOPT_ASSERT__
	static Tsk::Task< CScriptCache >::Code s_logic_code;       
	Tsk::Task< CScriptCache > *mp_logic_task;
	
	enum
	{
		MAX_DECOMPRESS_COUNTS=60
	};	
	int mp_decompress_counts[MAX_DECOMPRESS_COUNTS];
	int m_current_decompress_count_index;
	
	int m_num_used_scripts; // A count of how many script entries have a usage > 0
	int m_max_used_scripts; // The max value that the above reached, used for choosing a suitable MAX_DECOMPRESSED_SCRIPTS
	#endif
	
	Lst::HashTable *mp_cache_hash_table;
	
	// Points to the first (youngest) of the zero-usage entries.
	CScriptCacheEntry *mp_first_zero_usage;
	// Points to the last (oldest) of the zero-usage entries.
	CScriptCacheEntry *mp_last_zero_usage;
	
	bool m_delete_zero_usage_straight_away;

	#endif // #ifdef NO_SCRIPT_CACHING
public:
	CScriptCache();
	virtual	~CScriptCache();

	uint8 *GetScript(uint32 scriptName);
	void DecrementScriptUsage(uint32 scriptName);
	void RefreshAfterReload(uint32 scriptName);

	void DeleteZeroUsageStraightAway();
	void DeleteOldestZeroUsageWhenNecessary();

	#ifdef NO_SCRIPT_CACHING
	#else
	const char *GetSourceFile(uint8 *p_token);
	#endif
		
	#ifdef __NOPT_ASSERT__
	void GetDebugInfo( Script::CStruct* p_info );
	#endif
};
	
} // namespace Script

#endif // #ifndef	__SCRIPTING_SCRIPTCACHE_H


================================================
FILE: Code/Gel/Scripting/scriptdefs.h
================================================
#ifndef	__SCRIPTING_SCRIPTDEFS_H
#define	__SCRIPTING_SCRIPTDEFS_H

#ifndef __CORE_DEFINES_H
#include 
#endif

namespace Script
{
// A checksum of 0, used to represent no-name.    
#define NO_NAME ((uint32)0)

enum EAssertType
{
	NO_ASSERT=0,
	ASSERT
};

enum EBoolAssertIfDuplicateSymbols
{
	NO_ASSERT_IF_DUPLICATE_SYMBOLS=0,
	ASSERT_IF_DUPLICATE_SYMBOLS
};
	
// So that the old code still compiles without having to change a million things.
#define CScriptStructure CStruct
#define SPair CPair
#define NONAME NO_NAME

} // namespace Script

#endif // #ifndef	__SCRIPTING_SCRIPTDEFS_H


================================================
FILE: Code/Gel/Scripting/skiptoken.cpp
================================================
// Definition of the SkipToken function.
// This can be #included in the game code, and in PC utilities.
// It is in this file so that only this file needs to be updated when SkipToken
// needs to be modified.

// If included in PC code, then uint8 and Dbg_MsgAssert will need to be defined.

								   
// Returns a pointer to the next token after p_token.
// It won't necessarily skip over the complete format of the data that is expected
// to follow, it will just skip over enough that it returns a pointer to a token again.
// For example, ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE must be followed by a ESCRIPTTOKEN_PAIR,
// but SkipToken will not check for that and skip over the pair token too, it will just skip over the
// RANDOM_RANGE token and return a pointer to the ESCRIPTTOKEN_PAIR
// So if SkipToken is passed a pointer to a token, it is guaranteed to return a pointer to token, namely
// the nearest next one.
uint8 *SkipToken(uint8 *p_token)
{
    switch (*p_token)
    {
        case ESCRIPTTOKEN_ENDOFFILE:
            Dbg_MsgAssert(0,("Tried to skip past EndOfFile token"));
            break;
	    case ESCRIPTTOKEN_ENDOFLINE:
        case ESCRIPTTOKEN_EQUALS:
        case ESCRIPTTOKEN_DOT:
        case ESCRIPTTOKEN_COMMA:
        case ESCRIPTTOKEN_MINUS:
        case ESCRIPTTOKEN_ADD:
        case ESCRIPTTOKEN_DIVIDE:
        case ESCRIPTTOKEN_MULTIPLY:
        case ESCRIPTTOKEN_OPENPARENTH:
        case ESCRIPTTOKEN_CLOSEPARENTH:
        case ESCRIPTTOKEN_SAMEAS:
        case ESCRIPTTOKEN_LESSTHAN:
        case ESCRIPTTOKEN_LESSTHANEQUAL:
        case ESCRIPTTOKEN_GREATERTHAN:
        case ESCRIPTTOKEN_GREATERTHANEQUAL:
        case ESCRIPTTOKEN_STARTSTRUCT:
        case ESCRIPTTOKEN_STARTARRAY:
        case ESCRIPTTOKEN_ENDSTRUCT:
        case ESCRIPTTOKEN_ENDARRAY:
        case ESCRIPTTOKEN_KEYWORD_BEGIN:
        case ESCRIPTTOKEN_KEYWORD_REPEAT:
        case ESCRIPTTOKEN_KEYWORD_BREAK:
        case ESCRIPTTOKEN_KEYWORD_SCRIPT:
        case ESCRIPTTOKEN_KEYWORD_ENDSCRIPT:
        case ESCRIPTTOKEN_KEYWORD_IF:
        case ESCRIPTTOKEN_KEYWORD_ELSE:
        case ESCRIPTTOKEN_KEYWORD_ELSEIF:
        case ESCRIPTTOKEN_KEYWORD_ENDIF:
        case ESCRIPTTOKEN_KEYWORD_RETURN:
		case ESCRIPTTOKEN_KEYWORD_ALLARGS:
		case ESCRIPTTOKEN_ARG:
		case ESCRIPTTOKEN_OR:
		case ESCRIPTTOKEN_AND:
		case ESCRIPTTOKEN_XOR:
		case ESCRIPTTOKEN_SHIFT_LEFT:
		case ESCRIPTTOKEN_SHIFT_RIGHT:
		case ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE:
		case ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE2:
		case ESCRIPTTOKEN_KEYWORD_NOT:
		case ESCRIPTTOKEN_KEYWORD_AND:
		case ESCRIPTTOKEN_KEYWORD_OR:
		case ESCRIPTTOKEN_KEYWORD_SWITCH:
		case ESCRIPTTOKEN_KEYWORD_ENDSWITCH:
		case ESCRIPTTOKEN_KEYWORD_CASE:
		case ESCRIPTTOKEN_KEYWORD_DEFAULT:
		case ESCRIPTTOKEN_COLON:
            ++p_token;
			break;
            
        case ESCRIPTTOKEN_NAME:
        case ESCRIPTTOKEN_INTEGER:
        case ESCRIPTTOKEN_HEXINTEGER:
        case ESCRIPTTOKEN_FLOAT:
	    case ESCRIPTTOKEN_ENDOFLINENUMBER:
		case ESCRIPTTOKEN_JUMP:
		case ESCRIPTTOKEN_RUNTIME_MEMBERFUNCTION:
		case ESCRIPTTOKEN_RUNTIME_CFUNCTION:
			p_token+=5;
            break;
        case ESCRIPTTOKEN_VECTOR:
            p_token+=13;
            break;
        case ESCRIPTTOKEN_PAIR:
            p_token+=9;
            break;
	    case ESCRIPTTOKEN_STRING:
        case ESCRIPTTOKEN_LOCALSTRING:
		{
            ++p_token;
			uint32 num_bytes=*p_token++;
			num_bytes+=(*p_token++)<<8;
			num_bytes+=(*p_token++)<<16;
			num_bytes+=(*p_token++)<<24;
            p_token+=num_bytes;
            break;
		}	
		case ESCRIPTTOKEN_CHECKSUM_NAME: 
			// Skip over the token and checksum.
			p_token+=5;
			// Skip over the string.
			while (*p_token)
			{
				++p_token;
			}
			// Skip over the terminator.
			++p_token;	
			break;			
		case ESCRIPTTOKEN_KEYWORD_RANDOM:
		case ESCRIPTTOKEN_KEYWORD_RANDOM2:
		case ESCRIPTTOKEN_KEYWORD_RANDOM_NO_REPEAT:
		case ESCRIPTTOKEN_KEYWORD_RANDOM_PERMUTE:
		{
            ++p_token;
			
			uint32 num_jumps=*p_token++;
			num_jumps+=(*p_token++)<<8;
			num_jumps+=(*p_token++)<<16;
			num_jumps+=(*p_token++)<<24;
			
			// Skip over all the weight & jump offsets.
			p_token+=2*num_jumps+4*num_jumps;
			break;
		}
			
        default:
            Dbg_MsgAssert(0,("Unrecognized script token sent to SkipToken()"));
            break;
    }
    return p_token;
}



================================================
FILE: Code/Gel/Scripting/string.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// string.cpp		KSH 17 Oct 2001
//
// Code for managing allocation and deallocation of strings within the script system.
// For example it manages the 'permanent string' heap, which is for strings that get created
// when all the qb's are loaded and never get deleted. They can therefore all be squashed
// together for optimum use of memory.
//
// Note: Not defining a CString class, because it would have to have a char pointer so
// would just be an unneccessary layer of indirection that would waste space.
//
// Instead CreateString and DeleteString are used to allocate and deallocate strings.
//
// Could add cleverer memory management here later, eg a pool of fixed sized strings
// for strings that need to be alloc'd and dealloc'd fast.
//
///////////////////////////////////////////////////////////////////////////////////////

// Note: Small memory saving possible by deleting the sp_permanent_string_checksums array
// once you know that no more permanent strings will need to be allocated. Will save 32K ish,
// depending on maxStrings.
// (There's no function here yet for doing that, but easy to add)

#include 
#ifndef __CORE_CRC_H
#include 
#endif

namespace Script
{

static char *sAddToSpecialStringHeap(const char *p_string);

static bool s_use_permanent_string_heap=false;

// The permanent string buffer.
static char *sp_permanent_string_heap=NULL;
static char *sp_permanent_string_heap_top=NULL;
static uint32 s_permanent_string_heap_size=0;

// Array of string checksums to allow quick checking to see if a particular string
// is already in the permanent string buffer without having to do string comparisons.
// Needed because when loading several thousand strings, several million string comparisons
// would have to be done, causing a significant pause in loading the game.
struct SSpecialStringChecksum
{
	uint32 mChecksum;
	char *mpString;
};
static SSpecialStringChecksum *sp_permanent_string_checksums=NULL;
static uint32 s_num_permanent_string_checksums=0;
static uint32 s_max_permanent_string_checksums=0;

// Needs to be called once at the start of the game.
// For THPS3, maxSize was 48000 for English, 60500 for other languages. maxStrings was 4000
void AllocatePermanentStringHeap(uint32 maxSize, uint32 maxStrings)
{
	// Uses whatever the current heap is set to, which is set to the script heap in Script::AllocatePools
	// which is where this function is called from.
	s_permanent_string_heap_size=maxSize;
	s_max_permanent_string_checksums=maxStrings;
	
	// Allocate the string buffer.
	Dbg_MsgAssert(sp_permanent_string_heap==NULL,("sp_permanent_string_heap not NULL ???"));
	sp_permanent_string_heap=(char*)Mem::Malloc(s_permanent_string_heap_size);
	sp_permanent_string_heap_top=sp_permanent_string_heap;
	
	// Allocate the array of checksums.
	Dbg_MsgAssert(sp_permanent_string_checksums==NULL,("sp_permanent_string_checksums not NULL ???"));
	sp_permanent_string_checksums=(SSpecialStringChecksum*)Mem::Malloc(s_max_permanent_string_checksums*sizeof(SSpecialStringChecksum));
	s_num_permanent_string_checksums=0;
}

void DeallocatePermanentStringHeap()
{
	// Deallocate the string buffer.
	Dbg_MsgAssert(sp_permanent_string_heap,("NULL sp_permanent_string_heap ?"));
	Mem::Free(sp_permanent_string_heap);
	sp_permanent_string_heap=NULL;
	sp_permanent_string_heap_top=NULL;
	s_permanent_string_heap_size=0;
	
	// Deallocate the array of checksums.
	// MEMOPT: TODO: Destroy this when UseRegularStringHeap is called too, to free up memory ?
	Dbg_MsgAssert(sp_permanent_string_checksums,("NULL sp_permanent_string_checksums ?"));
	Mem::Free(sp_permanent_string_checksums);
	sp_permanent_string_checksums=NULL;
	s_num_permanent_string_checksums=0;
	s_max_permanent_string_checksums=0;
}

// Adds the contents of the passed pString to the special string heap, and returns
// the pointer to the string within the heap.
// If the string is already found in the heap, it won't add it again.
static char *sAddToSpecialStringHeap(const char *p_string)
{
	Dbg_MsgAssert(p_string,("NULL p_string"));

	// Check to see if it already exists in the special string heap ...

	// Calculate the checksum of the string, then search for this in the array of
	// checksums of all the special strings so far.
	// Quicker than doing byte comparisons, probably due to the long time it takes to
	// read single bytes from memory.
	// Needs to be fast, otherwise it can add a significant delay to the startup time.
	// This method adds about 0.3 seconds altogether (in THPS3, couple of thousand strings)
	uint32 len=strlen(p_string);
	uint32 checksum=Crc::GenerateCRCCaseSensitive(p_string,len);
	
	Dbg_MsgAssert(sp_permanent_string_checksums,("NULL sp_permanent_string_checksums ??"));
	SSpecialStringChecksum *p_ch=sp_permanent_string_checksums;
	for (uint32 i=0; imChecksum==checksum)
		{
			return p_ch->mpString;
		}
		++p_ch;
	}		

	char *p_heap_string=NULL;
	
	// Not found, so add to the pile of strings.
	Dbg_MsgAssert(sp_permanent_string_heap,("NULL sp_permanent_string_heap ??"));
	
	Dbg_MsgAssert(s_permanent_string_heap_size-(sp_permanent_string_heap_top-sp_permanent_string_heap)>=len+1,("Out of special string heap"));
	p_heap_string=sp_permanent_string_heap_top;
	strcpy(p_heap_string,p_string);
	// Update sp_permanent_string_heap_top
	sp_permanent_string_heap_top+=len;
	++sp_permanent_string_heap_top; // Skip over the terminator

	// Store the checksum too.
	Dbg_MsgAssert(s_num_permanent_string_checksums=sp_permanent_string_heap && p_string
#endif

namespace Script
{

void AllocatePermanentStringHeap(uint32 maxSize, uint32 maxStrings);
void DeallocatePermanentStringHeap();

void UsePermanentStringHeap();
void UseRegularStringHeap();
	
char *CreateString(const char *p_string);
void DeleteString(char *p_string);

void SetScriptString(uint32 n, const char *p_string);
char* GetScriptString(uint32 n);

} // namespace Script

#endif // #ifndef	__SCRIPTING_STRING_H


================================================
FILE: Code/Gel/Scripting/struct.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// struct.cpp		KSH 22 Oct 2001
//
// CStruct class member functions.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

							 
//#define	__DEBUG_STRUCT_ALLOCS
							 
// This does technically cause a cyclic dependency, but what the heck, it's only needed
// for some debugging. (Making the Get function print the contents of the struct in
// the event of not finding the required parameter)
#include 

DefinePoolableClass(Script::CStruct);

namespace Script
{

#ifdef	__DEBUG_STRUCT_ALLOCS
#define	MAX_LAST	200
static int head = 0;
static bool init_last = true;
static CStruct * last[MAX_LAST];
#endif

void DumpLastStructs()
{
#ifdef	__DEBUG_STRUCT_ALLOCS
	for (int i=0;imType)
	{
		case ESYMBOLTYPE_STRING:
		case ESYMBOLTYPE_LOCALSTRING:
			if (p_comp->mpString)
			{
				DeleteString(p_comp->mpString);
			}
			break;
		case ESYMBOLTYPE_PAIR:
			if (p_comp->mpPair)
			{
				delete p_comp->mpPair;
			}
			break;
		case ESYMBOLTYPE_VECTOR:
			if (p_comp->mpVector)
			{
				delete p_comp->mpVector;
			}
			break;
		case ESYMBOLTYPE_STRUCTURE:
			if (p_comp->mpStructure)
			{
				delete p_comp->mpStructure;
			}
			break;
		case ESYMBOLTYPE_ARRAY:
			if (p_comp->mpArray)
			{
				CleanUpArray(p_comp->mpArray);
				delete p_comp->mpArray;
			}
			break;
		case ESYMBOLTYPE_QSCRIPT:
            if (p_comp->mpScript)
			{
				Mem::Free(p_comp->mpScript);
			}
			p_comp->mScriptSize=0;
			break;	
		default:
			break;
	}	
	// This will zero the union, which includes all the above pointers.
	p_comp->mUnion=0;
	
	p_comp->mType=ESYMBOLTYPE_NONE;
}

// Copies the contents of p_source into p_dest, but without copying any pointers over.
// Eg, if p_source contains an array, a new array will be created for p_dest.
void CopyComponent(CComponent *p_dest, const CComponent *p_source)
{
	Dbg_MsgAssert(p_dest,("NULL p_dest"));
	Dbg_MsgAssert(p_source,("NULL p_source"));

	// Make sure p_dest is cleaned up first.	
	if (p_dest->mUnion) // This 'if' is just a small speed optimization to avoid an unnecessay call
	{
		CleanUpComponent(p_dest);
	}	
	
	p_dest->mType=p_source->mType;
	p_dest->mNameChecksum=p_source->mNameChecksum;
	
	switch (p_source->mType)
	{
	case ESYMBOLTYPE_NONE:
		break;
		
    case ESYMBOLTYPE_INTEGER:
    case ESYMBOLTYPE_FLOAT:
	case ESYMBOLTYPE_NAME:
		p_dest->mUnion=p_source->mUnion;
		break;
		
    case ESYMBOLTYPE_STRING:
		Dbg_MsgAssert(p_source->mpString,("NULL p_source->mpString ?"));
		p_dest->mpString=CreateString(p_source->mpString);
		break;
		
    case ESYMBOLTYPE_LOCALSTRING:
		Dbg_MsgAssert(p_source->mpLocalString,("NULL p_source->mpLocalString ?"));
		p_dest->mpLocalString=CreateString(p_source->mpLocalString);
		break;
		
    case ESYMBOLTYPE_PAIR:
		Dbg_MsgAssert(p_source->mpPair,("NULL p_source->mpPair ?"));
		p_dest->mpPair=new CPair;
		p_dest->mpPair->mX=p_source->mpPair->mX;
		p_dest->mpPair->mY=p_source->mpPair->mY;
		break;
		
    case ESYMBOLTYPE_VECTOR:
		Dbg_MsgAssert(p_source->mpVector,("NULL p_source->mpVector ?"));
		p_dest->mpVector=new CVector;
		p_dest->mpVector->mX=p_source->mpVector->mX;
		p_dest->mpVector->mY=p_source->mpVector->mY;
		p_dest->mpVector->mZ=p_source->mpVector->mZ;
		break;
		
    case ESYMBOLTYPE_STRUCTURE:
		Dbg_MsgAssert(p_source->mpStructure,("NULL p_source->mpStructure ?"));
		p_dest->mpStructure=new CStruct;
		*p_dest->mpStructure=*p_source->mpStructure;
		break;
		
    case ESYMBOLTYPE_ARRAY:
		Dbg_MsgAssert(p_source->mpArray,("NULL p_source->mpArray ?"));
		p_dest->mpArray=new CArray;
		CopyArray(p_dest->mpArray,p_source->mpArray);
		break;

	case ESYMBOLTYPE_QSCRIPT:
	{
		Dbg_MsgAssert(p_source->mpScript,("NULL p_source->mpScript ?"));
		
		// Allocate a buffer off the script heap
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
		Dbg_MsgAssert(p_source->mScriptSize,("Zero source script size"));
		p_dest->mpScript=(uint8*)Mem::Malloc(p_source->mScriptSize);
		p_dest->mScriptSize=p_source->mScriptSize;
		Mem::Manager::sHandle().PopContext();
		
		// Copy the script into the new buffer.
		const uint8 *p_from=p_source->mpScript;
		uint8 *p_to=p_dest->mpScript;
		for (uint32 i=0; imScriptSize; ++i)
		{
			*p_to++=*p_from++;
		}
		
		break;
	}
		
	default:
		Dbg_MsgAssert(0,("Bad p_source->mType of '%s'",GetTypeName(p_source->mType)));
		break;
	}
}

// This will run through all the elements of p_array deleting any entities pointed to by it.
// It will also call the array's Clear function to delete the array buffer, so it is not
// necessary to call Clear again after calling this.
void CleanUpArray(CArray *p_array)
{
	Dbg_MsgAssert(p_array,("NULL p_array"));
	
	ESymbolType type=p_array->GetType();
	uint32 size=p_array->GetSize();
	uint32 *p_array_data=p_array->GetArrayPointer();
	if (size)
	{
		Dbg_MsgAssert(p_array_data,("NULL p_array_data ?"));
	}	
	
	switch (type)
	{
		case ESYMBOLTYPE_NONE:
		case ESYMBOLTYPE_INTEGER:
		case ESYMBOLTYPE_FLOAT:
		case ESYMBOLTYPE_NAME:
			// Nothing to delete
			break;
			
		case ESYMBOLTYPE_STRING:
		case ESYMBOLTYPE_LOCALSTRING:
		{
			char **pp_string=(char**)p_array_data;
			for (uint32 i=0; iClear();
}

// Copies the contents of p_source into p_dest. No pointers will be copied over, new entities will be
// created for p_dest. So p_source can safely be deleted afterwards.
void CopyArray(CArray *p_dest, const CArray *p_source)
{
	Dbg_MsgAssert(p_source,("NULL p_source ?"));
	Dbg_MsgAssert(p_dest,("NULL p_dest ?"));
	
	// Make sure that p_dest is cleaned up first.
	CleanUpArray(p_dest);
		
	ESymbolType type=p_source->GetType();
	uint32 size=p_source->GetSize();
	
	p_dest->SetSizeAndType(size,type);

	if (size==0)
	{
		// Finished.
		return;
	}
		
	uint32 *p_source_array=p_source->GetArrayPointer();
	Dbg_MsgAssert(p_source_array,("NULL p_source_array ?"));
	
	uint32 *p_dest_array=p_dest->GetArrayPointer();
	Dbg_MsgAssert(p_dest_array,("NULL p_dest_array ?"));
			
	switch (type)
	{
		case ESYMBOLTYPE_INTEGER:
		case ESYMBOLTYPE_FLOAT:
		case ESYMBOLTYPE_NAME:
		{
			uint32 *p_source_word=p_source_array;
			uint32 *p_dest_word=p_dest_array;
			for (uint32 i=0; imax)
	{
		max = CStruct::SGetNumUsedItems();
		DumpUnwindStack(20,0);
	}
	#endif
}

// Copy constructor.
CStruct::CStruct( const CStruct& rhs )
{
	// Initialise everything. CStruct is not derived from CClass so we don't get
	// the auro-zeroing.
	init();

	// use the overridden assignment operator
	*this = rhs;
	
	x++;
	if (x>max)
	{
		max = x;
//		DumpUnwindStack(20,0);
	}
}

// Assignement operator.
CStruct& CStruct::operator=( const CStruct& rhs )
{
	// don't try to assign to yourself
	if ( &rhs == this )
	{
		return *this;
	}

	this->Clear();
	*this+=rhs;

	return *this;
}

// This will merge the contents of the rhs into this structure.
// Functionally the same as the old AppendStructure function, except AppendStructure would accept a NULL pointer.
CStruct& CStruct::operator+=( const CStruct& rhs )
{
	CComponent *p_source_component=rhs.mp_components;
	while (p_source_component)
	{
		CComponent *p_new=new CComponent;
		CopyComponent(p_new, p_source_component);
		AddComponent(p_new);
		p_source_component=p_source_component->mpNext;
	}	
	return *this;
}

// TODO: Remove at some point. Provided for back-compatibility.
void CStruct::AppendStructure(const CStruct *p_struct)
{
	if (p_struct)
	{
		*this+=*p_struct;
	}	
}

// Written specially for use by CScriptDebugger::transmit_cscript_list.
// This will append the passed component to the structure's list of components
// and will leave the existing list untouched, ie it will not remove any of the same name.
// This is so that the script debugger code can store a list of CScript instances into a structure
// even though many may have the same name.
void CStruct::AppendComponentPointer(CComponent *p_comp)
{
	CComponent *p_last=NULL;
	CComponent *p_scan=mp_components;
	
	// Find p_last, the last component in the list.
	while (p_scan)
	{
		p_last=p_scan;
		p_scan=p_scan->mpNext;
	}		
	
	// Tag p_comp onto the end of the list.
	if (p_last)
	{
		p_last->mpNext=p_comp;
	}
	else
	{
		mp_components=p_comp;
	}	
	p_comp->mpNext=NULL;
}

CStruct::~CStruct()
{

#ifdef	__DEBUG_STRUCT_ALLOCS
	for (int i=0;impNext;
		// Note: The CComponent destructor cannot clean up, because that would cause cyclic dependencies.
		CleanUpComponent(p_comp);
		delete p_comp;
		p_comp=p_next;
	}
	mp_components=NULL;
}

void CStruct::RemoveComponent(uint32 nameChecksum)
{
	CComponent *p_last=NULL;
	CComponent *p_comp=mp_components;
	while (p_comp)
	{
		CComponent *p_next=p_comp->mpNext;
		
		if (p_comp->mNameChecksum==nameChecksum)
		{
			// p_comp must be removed.
			
			// Unlink it.
			if (p_last)
			{
				p_last->mpNext=p_next;
			}
			else	
			{
				Dbg_MsgAssert(p_comp==mp_components,("Eh ? p_comp!=mp_components ??"));
				mp_components=p_next;
			}
			
			// Note: The CComponent destructor cannot clean up, because that would cause cyclic dependencies.
			CleanUpComponent(p_comp);
			delete p_comp;
			
			// Carries on, in case there is more than one component with the given name.
			p_comp=p_next;
		}	
		else
		{
			p_last=p_comp;
			p_comp=p_next;
		}	
	}	
}

void CStruct::RemoveComponent(const char *p_name)
{
	RemoveComponent(Crc::GenerateCRCFromString(p_name));
}

// Same as RemoveComponent except the type must matched the passed type too.
// Used by eval.cpp when subtracting a structure from another structure.
void CStruct::RemoveComponentWithType(uint32 nameChecksum, uint8 type)
{
	CComponent *p_last=NULL;
	CComponent *p_comp=mp_components;
	while (p_comp)
	{
		CComponent *p_next=p_comp->mpNext;
		
		if (p_comp->mNameChecksum==nameChecksum && p_comp->mType==type)
		{
			// p_comp must be removed.
			
			// Unlink it.
			if (p_last)
			{
				p_last->mpNext=p_next;
			}
			else	
			{
				Dbg_MsgAssert(p_comp==mp_components,("Eh ? p_comp!=mp_components ??"));
				mp_components=p_next;
			}
			
			// Note: The CComponent destructor cannot clean up, because that would cause cyclic dependencies.
			CleanUpComponent(p_comp);
			delete p_comp;
			
			// Carries on, in case there is more than one component with the given name.
			p_comp=p_next;
		}	
		else
		{
			p_last=p_comp;
			p_comp=p_next;
		}	
	}	
}

void CStruct::RemoveComponentWithType(const char *p_name, uint8 type)
{
	RemoveComponentWithType(Crc::GenerateCRCFromString(p_name),type);
}

// Note: Logical inconsistencies that could be an issue later maybe:
// If the unnamed-name resolves to a global structure, should the component still be removed?
// If there is a referenced global structure, should it have any of the specified flag removed
// too, and so on recursively? (probably not, since global structures should remain constant)
void CStruct::RemoveFlag(uint32 checksum)
{
	CComponent *p_last=NULL;
	CComponent *p_comp=mp_components;
	while (p_comp)
	{
		CComponent *p_next=p_comp->mpNext;
		
		if (p_comp->mNameChecksum==0 && p_comp->mChecksum==checksum)
		{
			// p_comp must be removed.
			
			// Unlink it.
			if (p_last)
			{
				p_last->mpNext=p_next;
			}
			else	
			{
				Dbg_MsgAssert(p_comp==mp_components,("Eh ? p_comp!=mp_components ??"));
				mp_components=p_next;
			}
			
			// Note: The CComponent destructor cannot clean up, because that would cause cyclic dependencies.
			CleanUpComponent(p_comp);
			// Note: All the CleanUpComponent call will have done will be to zero the union,
			// cos a flag component does not contain any pointers that need deleting.
			// So I could have just set p_comp->mChecksum to 0 instead. Just calling
			// CleanUpComponent for consistency.
			delete p_comp;
			
			// Carries on, in case there is more than one flag with the given name.
			// There shouldn't be, but check anyway.
			p_comp=p_next;
		}	
		else
		{
			p_last=p_comp;
			p_comp=p_next;
		}	
	}	
}

void CStruct::RemoveFlag(const char *p_name)
{
	RemoveFlag(Crc::GenerateCRCFromString(p_name));
}

// Returns true if the structure contains no components.
bool CStruct::IsEmpty() const
{
	return mp_components==NULL;
}

// Searches for a component with the given name, but will also recurse into substructures.
// Used when resolving the  syntax.
CComponent *CStruct::FindNamedComponentRecurse(uint32 nameChecksum) const
{
	CComponent *p_found=NULL;
	
    CComponent *p_comp=mp_components;
    while (p_comp)
    {
        if (p_comp->mNameChecksum==nameChecksum) 
		{
			p_found=p_comp;
		}	
		else if (p_comp->mNameChecksum==0 && p_comp->mType==ESYMBOLTYPE_NAME)
		{
            CSymbolTableEntry *p_entry=Resolve(p_comp->mChecksum);
            if (p_entry && p_entry->mType==ESYMBOLTYPE_STRUCTURE)
            {
                Dbg_MsgAssert(p_entry->mpStructure,("NULL p_entry->mpStructure"));
                CComponent *p_new_found=p_entry->mpStructure->FindNamedComponentRecurse(nameChecksum);
				if (p_new_found)
				{
					p_found=p_new_found;
				}	
            }
		}
        p_comp=p_comp->mpNext;
    }
	
    return p_found;
}

// If passed NULL this will return the first (leftmost) component.
// If passed non-NULL it return the next component (to the right) 
// Returns NULL if the passed component is the last component.
CComponent *CStruct::GetNextComponent(CComponent *p_comp) const
{
	if (p_comp==NULL)
	{
		return mp_components;
	}
	
	return p_comp->mpNext;	
}

// This will copy the contents of this structure into p_dest, but in such a way that
// p_dest will contain no unnamed-structure references, they will all get expanded.
// This was added because sometimes one wants to step through each of the components of
// a structure using GetNextComponent, but GetNextComponent itself cannot recurse into
// unnamed structures, because when it reaches the end of one it won't know how to get back
// to the parent.
// So instead just create a new structure, copy the other structure into it using ExpandInto,
// then step through that using GetNextComponent, then delete the new structure once finished.
// It's a bit memory intensive, but saves having to write special code to resolve unnamed
// structures & recursing whenever one wants to scan through the components.
// Could be handy for other things later too.
// recursionCount is just to catch infinite recursion, so no need to pass it a value. (defaults to 0)
void CStruct::ExpandInto(CStruct *p_dest, int recursionCount) const
{
	Dbg_MsgAssert(recursionCount<=20,("Possible infinite recursion of CStruct::ExpandInto! More than 20 recursions have occurred."));
	Dbg_MsgAssert(p_dest,("NULL p_dest sent to ExpandInto"));
	
	CComponent *p_comp=mp_components;
	while (p_comp)
	{
		bool added_structure=false;
		
		if (p_comp->mNameChecksum==0)
		{
			Dbg_MsgAssert(p_comp->mType!=ESYMBOLTYPE_STRUCTURE,("Unexpected unnamed structure in CStruct"));
			
			if (p_comp->mType==ESYMBOLTYPE_NAME)
			{
				CSymbolTableEntry *p_global=Resolve(p_comp->mChecksum);
				if (p_global && p_global->mType==ESYMBOLTYPE_STRUCTURE)
				{
					Dbg_MsgAssert(p_global->mpStructure,("NULL p_global->mpStructure"));
					p_global->mpStructure->ExpandInto(p_dest,recursionCount+1);
					added_structure=true;
				}	
			}	
		}
		
		if (!added_structure)
		{
			CComponent *p_new_component=new CComponent;
			CopyComponent(p_new_component,p_comp);
			p_dest->AddComponent(p_new_component);
		}
		
		p_comp=p_comp->mpNext;
	}
}

// Adds the component p_comp to the end of the list of components, removing any existing components
// that have the same name and type, and asserting if any components exist with the same name but different
// type.
// However, if p_comp is a flag (an un-named component of type ESYMBOLTYPE_NAME) then it will always add it,
// because otherwise we would not be able to have more than one flag in a structure.
//
// Note: Even two identical flags are allowed, because sometimes this is handy. For example I use a list
// of words in a structure as a way for Scott to represent trick button sequences (see airtricks.q) and 
// sometimes two of the words might need to be identical.
//
// Another note: This function needs to be pretty fast because it will need to be called many times for
// each line of script executed. Optimize later.
//
void CStruct::AddComponent(CComponent *p_comp)
{
	Dbg_MsgAssert(p_comp,("NULL p_comp"));

	// Unnamed structure components are not allowed, because to support them would require 
	// modifying the search_for function. No need, since when structures are created from lists
	// of tokens any substructures get expanded and added component by component at that stage.
	// So in theory no components of type structure should get added.
	Dbg_MsgAssert(!(p_comp->mNameChecksum==0 && p_comp->mType==ESYMBOLTYPE_STRUCTURE),("Tried to add an un-named structure component ..."));

	Dbg_MsgAssert(p_comp->mType!=ESYMBOLTYPE_NONE,("Tried to add a structure component with no type, name='%s' ...",FindChecksumName(p_comp->mNameChecksum)));
	
	CComponent *p_last=NULL;
	CComponent *p_scan=mp_components;
    bool remove=false;
	
	while (p_scan)
	{
		if (p_scan->mNameChecksum==p_comp->mNameChecksum)
		{
			// Same name ...
		 
		    remove=false;
			
			if (p_scan->mType==p_comp->mType)
			{
				if (p_scan->mType==ESYMBOLTYPE_NAME && 
					p_scan->mNameChecksum==0 && 
					p_scan->mChecksum!=p_comp->mChecksum)
				{
					// Allow multiple un-named checksums if their values are different, because
					// these are flags.
				}
				else
				{
					remove=true;
				}
			}
			else
			{
				// Consider floats and ints to be the same type, so that x=3.1 overrides x=3 and vice versa				
				if ((p_scan->mType==ESYMBOLTYPE_FLOAT && p_comp->mType==ESYMBOLTYPE_INTEGER) ||
					(p_scan->mType==ESYMBOLTYPE_INTEGER && p_comp->mType==ESYMBOLTYPE_FLOAT))
				{
					remove=true;
				}
			}
			
			if (remove)		
			{
				// Remove this component.
				if (p_last)
				{
					p_last->mpNext=p_scan->mpNext;
					CleanUpComponent(p_scan);
					delete p_scan;
					p_scan=p_last->mpNext;
				}	
				else
				{
					mp_components=p_scan->mpNext;
					CleanUpComponent(p_scan);
					delete p_scan;
					p_scan=mp_components;
				}	
			}
			else
			{
				// Same name but different type.
				// The old code would assert in this case, but the new code allows it.
				// This is because there is no problem if a structure contains {x=7 x="Blaa"}
				// Getting an integer called x from it would give 7, getting a string called x would give "Blaa"
				// Also, it would allow {x=7 x=foo} where foo may be defined elsewhere to be a different integer,
				// which is useful.
				p_last=p_scan;
				p_scan=p_scan->mpNext;
			}	
		}	
		else
		{
			p_last=p_scan;
			p_scan=p_scan->mpNext;
		}	
	}	
	
	// Now, p_last points to the last component in the list, and may be NULL.
	// Tag p_comp onto the end of the list.
	if (p_last)
	{
		p_last->mpNext=p_comp;
	}
	else
	{
		mp_components=p_comp;
	}	
	p_comp->mpNext=NULL;
}

#ifdef __NOPT_ASSERT__ 
void CStruct::SetParentScript(CScript *p_parentScript)
{
	mp_parent_script=p_parentScript;
}

CScript *CStruct::GetParentScript() const
{
	return mp_parent_script;
}
#endif


void CStruct::AddString(uint32 nameChecksum, const char *p_string)
{
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_STRING;
	p_new->mpString=CreateString(p_string);
	
	AddComponent(p_new);
}

void CStruct::AddString(const char *p_name, const char *p_string)
{
	AddString(Crc::GenerateCRCFromString(p_name),p_string);
}

void CStruct::AddLocalString(uint32 nameChecksum, const char *p_string)
{
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_LOCALSTRING;
	p_new->mpString=CreateString(p_string);
	
	AddComponent(p_new);
}

void CStruct::AddLocalString(const char *p_name, const char *p_string)
{
	AddLocalString(Crc::GenerateCRCFromString(p_name),p_string);
}

void CStruct::AddInteger(uint32 nameChecksum, int integer)
{
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_INTEGER;
	p_new->mIntegerValue=integer;
	
	AddComponent(p_new);
}

void CStruct::AddInteger(const char *p_name, int integer)
{
	AddInteger(Crc::GenerateCRCFromString(p_name),integer);
}

void CStruct::AddFloat(uint32 nameChecksum, float float_val)
{
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_FLOAT;
	p_new->mFloatValue=float_val;
	
	AddComponent(p_new);
}

void CStruct::AddFloat(const char *p_name, float float_val)
{
	AddFloat(Crc::GenerateCRCFromString(p_name),float_val);
}

void CStruct::AddChecksum(uint32 nameChecksum, uint32 checksum)
{
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_NAME;
	p_new->mChecksum=checksum;
	
	AddComponent(p_new);
}

void CStruct::AddChecksum(const char *p_name, uint32 checksum)
{
	AddChecksum(Crc::GenerateCRCFromString(p_name),checksum);
}

void CStruct::AddVector(uint32 nameChecksum, float x, float y, float z)
{
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_VECTOR;
	p_new->mpVector=new CVector;
	p_new->mpVector->mX=x;
	p_new->mpVector->mY=y;
	p_new->mpVector->mZ=z;
	
	AddComponent(p_new);
}

void CStruct::AddVector(const char *p_name, float x, float y, float z)
{
	AddVector(Crc::GenerateCRCFromString(p_name),x,y,z);
}

void CStruct::AddVector( uint32 nameChecksum, Mth::Vector vector )
{
	AddVector( nameChecksum, vector.GetX(), vector.GetY(), vector.GetZ() );
}

void CStruct::AddVector( const char* p_name, Mth::Vector vector )
{
	AddVector( Crc::GenerateCRCFromString( p_name ), vector.GetX(), vector.GetY(), vector.GetZ() );
}

void CStruct::AddPair(uint32 nameChecksum, float x, float y)
{
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_PAIR;
	p_new->mpPair=new CPair;
	p_new->mpPair->mX=x;
	p_new->mpPair->mY=y;
	
	AddComponent(p_new);
}

void CStruct::AddPair(const char *p_name, float x, float y)
{
	AddPair(Crc::GenerateCRCFromString(p_name),x,y);
}

// Creates a new array & copies in the contents of the passed array.
void CStruct::AddArray(uint32 nameChecksum, const CArray *p_array)
{
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_ARRAY;
	p_new->mpArray=new CArray;
	CopyArray(p_new->mpArray,p_array);

	AddComponent(p_new);
}

void CStruct::AddArray(const char *p_name, const CArray *p_array)
{
	AddArray(Crc::GenerateCRCFromString(p_name),p_array);
}

// This stores the passed pointer rather than copying the contents, so in this case it
// is important for the calling code not to delete the passed pointer.
// The CStruct will delete it in its destructor.
// Faster than copying the contents.
// Used parse.cpp, when creating the structure for holding function params.
void CStruct::AddArrayPointer(uint32 nameChecksum, CArray *p_array)
{
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_ARRAY;
	p_new->mpArray=p_array;

	AddComponent(p_new);
}

void CStruct::AddArrayPointer(const char *p_name, CArray *p_array)
{
	AddArrayPointer(Crc::GenerateCRCFromString(p_name),p_array);
}

// Creates a new structure & copies in the contents of the passed structure.
void CStruct::AddStructure(uint32 nameChecksum, const CStruct *p_structure)
{
	Dbg_MsgAssert(p_structure,("NULL p_structure"));
	
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_STRUCTURE;
	p_new->mpStructure=new CStruct;
	*p_new->mpStructure=*p_structure;

	AddComponent(p_new);
}

void CStruct::AddStructure(const char *p_name, const CStruct *p_structure)
{
	AddStructure(Crc::GenerateCRCFromString(p_name),p_structure);
}

// This stores the passed pointer rather than copying the contents, so in this case it
// is important for the calling code not to delete the passed pointer.
// The CStruct will delete it in its destructor.
// Faster than copying the contents.
// Used parse.cpp, when creating the structure for holding function params.
void CStruct::AddStructurePointer(uint32 nameChecksum, CStruct *p_structure)
{
	Dbg_MsgAssert(p_structure,("NULL p_structure"));
	
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_STRUCTURE;
	p_new->mpStructure=p_structure;

	AddComponent(p_new);
}

void CStruct::AddStructurePointer(const char *p_name, CStruct *p_structure)
{
	AddStructurePointer(Crc::GenerateCRCFromString(p_name),p_structure);
}

void CStruct::AddScript(uint32 nameChecksum, const uint8 *p_scriptTokens, uint32 size)
{
	CComponent *p_new=new CComponent;
	
	p_new->mNameChecksum=nameChecksum;
	p_new->mType=ESYMBOLTYPE_QSCRIPT;
	p_new->mScriptSize=size;
	
	// Allocate a buffer off the script heap
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	Dbg_MsgAssert(size,("Zero script size"));
	uint8 *p_new_script=(uint8*)Mem::Malloc(size);
	Mem::Manager::sHandle().PopContext();
	
	// Copy the script into the new buffer.
	Dbg_MsgAssert(p_scriptTokens,("NULL p_scriptTokens"));
	const uint8 *p_source=p_scriptTokens;
	uint8 *p_dest=p_new_script;
	for (uint32 i=0; impScript=p_new_script;
		
	AddComponent(p_new);
}

void CStruct::AddScript(const char *p_name, const uint8 *p_scriptTokens, uint32 size)
{
	AddScript(Crc::GenerateCRCFromString(p_name),p_scriptTokens,size);
}

///////////////////////////////////////////////////////////////////////////////////
// TODO: Remove all these AddComponent functions at some point.
// They are only included to provide back compatibility with the old code without
// having to change thousands of occurrences of calls to AddComponent.
// Gradually phase out the old AddComponent instead.
//

// String or local string.
void CStruct::AddComponent(uint32 nameChecksum, ESymbolType type, const char *p_string)
{
    Dbg_MsgAssert(type==ESYMBOLTYPE_LOCALSTRING || type==ESYMBOLTYPE_STRING,("Bad type sent to AddComponent"));
    Dbg_MsgAssert(p_string,("NULL p_string"));

	if (type==ESYMBOLTYPE_STRING)
	{
		AddString(nameChecksum,p_string);
	}
	else	
	{
		AddLocalString(nameChecksum,p_string);
	}
}

// Integer or any other 4 byte value.
void CStruct::AddComponent(uint32 nameChecksum, ESymbolType type, int integer)
{
	switch (type)
	{
		case ESYMBOLTYPE_INTEGER: 
			AddInteger(nameChecksum,integer);
			break;
		case ESYMBOLTYPE_FLOAT:
			AddFloat(nameChecksum,*(float*)&integer);
			break;
		case ESYMBOLTYPE_STRUCTUREPOINTER:
			AddStructurePointer(nameChecksum,(CStruct*)integer);
			break;
		case ESYMBOLTYPE_ARRAY:
			AddArrayPointer(nameChecksum,(CArray*)integer);
			break;
		case ESYMBOLTYPE_NAME:
			AddChecksum(nameChecksum,(uint32)integer);
			break;
		default:	
			Dbg_MsgAssert(0,("Bad type of '%s' sent to AddComponent",GetTypeName(type)));
			break;
	}		
}

// Vector
void CStruct::AddComponent(uint32 nameChecksum, float x, float y, float z)
{
	AddVector(nameChecksum,x,y,z);
}

// Pair
void CStruct::AddComponent(uint32 nameChecksum, float x, float y)
{
	AddPair(nameChecksum,x,y);
}

// Array
void CStruct::AddComponent(uint32 nameChecksum, CArray *p_array)
{
	AddArray(nameChecksum,p_array);
}

///////////////////////////////////////////////////////////////////////////////////


struct SWhatever
{
	union
	{
		int mIntegerValue;
		float mFloatValue;
		char *mpString;
		char *mpLocalString;
		CPair *mpPair;
		CVector *mpVector;
		CStruct *mpStructure;
		CArray *mpArray;
		uint8 *mpScript;
		uint32 mChecksum;
		uint32 mUnion; 
	};
};

// Infinite recursion of search_for could occur, for example if we have a global structure foo={foo}
// which is trying to include itself.
// This counter will make an assert go off if search_for tries to recurse a suspicious number of times.
#ifdef __NOPT_ASSERT__ 
static uint32 s_num_search_for_recursions=0;
#endif

bool CStruct::search_for(uint32 nameChecksum, ESymbolType type, SWhatever *p_value) const
{
	Dbg_MsgAssert(s_num_search_for_recursions<10,("Possible infinite recursion of the CStruct::search_for function, bailing out!\nLast search was for parameter named '%s' of type '%s'",FindChecksumName(nameChecksum),GetTypeName(type)));
	#ifdef __NOPT_ASSERT__
	++s_num_search_for_recursions;
	#endif
	
	Dbg_MsgAssert(p_value,("NULL p_value"));
	
	bool found=false;
	
	CComponent *p_comp=mp_components;
	while (p_comp)
	{
		if (p_comp->mNameChecksum==nameChecksum)
		{
			// The name of the component matches that required.
			if (p_comp->mType==type)
			{
				// The type also matches, so the required component has been found.
				// Only set a flag though. Need to keep searching in case the structure
				// has the form {x=7 x=foo} where foo is a global defined to be an integer,
				// and which must override the previous 7.
				
				p_value->mUnion=p_comp->mUnion;
				found=true;
			}
			else
			{
				// The type does not match.
				if (p_comp->mType==ESYMBOLTYPE_NAME)
				{
					// The name of the component matches what we're looking for, but the type
					// does not and is of type name.
					// For example, we might be searching for an integer called x,
					// but we've found a component x=foo
					
					// So, see if foo is the name of a global defined somewhere.
					CSymbolTableEntry *p_global=Resolve(p_comp->mChecksum);
					if (p_global)
					{
						// It is! So check if this global has the type that we're looking for ...
						if (p_global->mType==type)
						{
							// It does! So we have a match.
							// Just set the found flag though, because we need to keep searching in
							// case the structure has the form {x=foo x=7} where the later 7 needs to
							// override what we've just found.
							p_value->mUnion=p_global->mUnion;
							found=true;
						}
					}
				}
			}				
		}
		else
		{
			// The name of the component does not match what we're looking for.
			
			// See if the component is an un-named name, eg the blaa in {a=3 blaa}
			if (p_comp->mNameChecksum==0 && p_comp->mType==ESYMBOLTYPE_NAME)
			{
				// It is, so check whether it resolves to a global structure.
				// If a global structure is referenced by name in another structure, then
				// it is considered 'pasted in' to that structure, so it needs to be searched too.
				CSymbolTableEntry *p_global=Resolve(p_comp->mChecksum);
				if (p_global)
				{
					// It is a global something ...
					if (p_global->mType==ESYMBOLTYPE_STRUCTURE)
					{
						// It is a structure. So search it for the required component also.
						Dbg_MsgAssert(p_global->mpStructure,("NULL p_global->mpStructure ?"));
						SWhatever value;
						if (p_global->mpStructure->search_for(nameChecksum,type,&value))
						{
							// Found! Load the value, set the found flag and carry on.
							*p_value=value;
							found=true;
						}
					}
				}			
			}	
		}
			
		p_comp=p_comp->mpNext;
	}	

	Dbg_MsgAssert(s_num_search_for_recursions,("Eh ???"));
	#ifdef __NOPT_ASSERT__
	--s_num_search_for_recursions;
	#endif
	
	return found;
}


bool CStruct::GetString(uint32 nameChecksum, const char **pp_text, EAssertType assert) const
{
	Dbg_MsgAssert(pp_text,("NULL pp_text"));
	
	SWhatever value;
	if (search_for(nameChecksum,ESYMBOLTYPE_STRING,&value))
	{
		*pp_text=value.mpString;
		return true;
	}
	if (search_for(nameChecksum,ESYMBOLTYPE_LOCALSTRING,&value))
	{
		*pp_text=value.mpString;
		return true;
	}
	if (assert)
	{
		PrintContents(this);
		Dbg_MsgAssert(0,("Could not find string component named '%s' in structure",FindChecksumName(nameChecksum)));
	}	
	return false;	
}

bool CStruct::GetString(const char *p_paramName, const char **pp_text, EAssertType assert) const
{
	return GetString(Crc::GenerateCRCFromString(p_paramName),pp_text,assert);
}

bool CStruct::GetLocalString(uint32 nameChecksum, const char **pp_text, EAssertType assert) const
{
	return GetString(nameChecksum,pp_text,assert);
}

bool CStruct::GetLocalString(const char *p_paramName, const char **pp_text, EAssertType assert) const
{
	return GetString(Crc::GenerateCRCFromString(p_paramName),pp_text,assert);
}

bool CStruct::GetInteger(uint32 nameChecksum, int *p_integerValue, EAssertType assert) const
{
	Dbg_MsgAssert(p_integerValue,("NULL p_integerValue"));
	
	SWhatever value;
	if (search_for(nameChecksum,ESYMBOLTYPE_INTEGER,&value))
	{
		*p_integerValue=value.mIntegerValue;
		return true;
	}
	if (assert)
	{
		PrintContents(this);
		Dbg_MsgAssert(0,("Could not find integer component named '%s' in structure",FindChecksumName(nameChecksum)));
	}	
	return false;	
}

bool CStruct::GetInteger(const char *p_paramName, int *p_integerValue, EAssertType assert) const
{
	return GetInteger(Crc::GenerateCRCFromString(p_paramName),p_integerValue,assert);
}

bool CStruct::GetFloat(uint32 nameChecksum, float *p_floatValue, EAssertType assert) const
{
	Dbg_MsgAssert(p_floatValue,("NULL p_floatValue"));
	
	SWhatever value;
	if (search_for(nameChecksum,ESYMBOLTYPE_FLOAT,&value))
	{
		*p_floatValue=value.mFloatValue;
		return true;
	}
	// If a float was not found, check for any int's with the same name, and if found cast to a float.
	// Note that the extra search means that {x=3.0} will result in x being found quicker than {x=3}
	// Could speed this up by making search_for() match integers with floats, but that would slightly slow
	// down the other Get... functions. Need to do some tests to see what is best.
	if (search_for(nameChecksum,ESYMBOLTYPE_INTEGER,&value))
	{
		*p_floatValue=value.mIntegerValue;
		return true;
	}
	if (assert)
	{
		PrintContents(this);
		Dbg_MsgAssert(0,("Could not find float component named '%s' in structure",FindChecksumName(nameChecksum)));
	}	
	return false;	
}

bool CStruct::GetFloat(const char *p_paramName, float *p_floatValue, EAssertType assert) const
{
	return GetFloat(Crc::GenerateCRCFromString(p_paramName),p_floatValue,assert);
}

bool CStruct::GetVector(uint32 nameChecksum, Mth::Vector *p_vector, EAssertType assert) const
{
	Dbg_MsgAssert(p_vector,("NULL p_vector"));
	
	SWhatever value;
	if (search_for(nameChecksum,ESYMBOLTYPE_VECTOR,&value))
	{
		Dbg_MsgAssert(value.mpVector,("NULL value.mpVector"));
		p_vector->Set(value.mpVector->mX,value.mpVector->mY,value.mpVector->mZ);
		return true;
	}
	if (assert)
	{
		PrintContents(this);
		Dbg_MsgAssert(0,("Could not find vector component named '%s' in structure",FindChecksumName(nameChecksum)));
	}	
	return false;	
}

bool CStruct::GetVector(const char *p_paramName, Mth::Vector *p_vector, EAssertType assert) const
{
	return GetVector(Crc::GenerateCRCFromString(p_paramName),p_vector,assert);
}

bool CStruct::GetPair(uint32 nameChecksum, float *p_x, float *p_y,	EAssertType assert) const
{
	Dbg_MsgAssert(p_x,("NULL p_x"));
	Dbg_MsgAssert(p_y,("NULL p_y"));
	
	SWhatever value;
	if (search_for(nameChecksum,ESYMBOLTYPE_PAIR,&value))
	{
		Dbg_MsgAssert(value.mpPair,("NULL value.mpPair"));
		*p_x=value.mpPair->mX;
		*p_y=value.mpPair->mY;
		return true;
	}
	if (assert)
	{
		PrintContents(this);
		Dbg_MsgAssert(0,("Could not find pair component named '%s' in structure",FindChecksumName(nameChecksum)));
	}	
	return false;	
}

bool CStruct::GetPair(const char *p_paramName, float *p_x, float *p_y, EAssertType assert) const
{
	return GetPair(Crc::GenerateCRCFromString(p_paramName),p_x,p_y,assert);
}

bool CStruct::GetStructure(uint32 nameChecksum, CStruct **pp_structure,	EAssertType assert) const
{
	Dbg_MsgAssert(pp_structure,("NULL pp_structure"));
	
	SWhatever value;
	if (search_for(nameChecksum,ESYMBOLTYPE_STRUCTURE,&value))
	{
		Dbg_MsgAssert(value.mpStructure,("NULL value.mpStructure"));
		*pp_structure=value.mpStructure;
		return true;
	}
	if (assert)
	{
		PrintContents(this);
		Dbg_MsgAssert(0,("Could not find structure component named '%s' in structure",FindChecksumName(nameChecksum)));
	}	
	return false;	
}

bool CStruct::GetStructure(const char *p_paramName, CStruct **pp_structure,	EAssertType assert) const
{
	return GetStructure(Crc::GenerateCRCFromString(p_paramName),pp_structure,assert);
}

bool CStruct::GetArray(uint32 nameChecksum, CArray **pp_array, EAssertType assert) const
{
	Dbg_MsgAssert(pp_array,("NULL pp_array"));
	
	SWhatever value;
	if (search_for(nameChecksum,ESYMBOLTYPE_ARRAY,&value))
	{
		Dbg_MsgAssert(value.mpArray,("NULL value.mpArray"));
		*pp_array=value.mpArray;
		return true;
	}
	if (assert)
	{
		PrintContents(this);
		Dbg_MsgAssert(0,("Could not find array component named '%s' in structure",FindChecksumName(nameChecksum)));
	}	
	return false;	
}

bool CStruct::GetArray(const char *p_paramName, CArray **pp_array, EAssertType assert) const
{
	return GetArray(Crc::GenerateCRCFromString(p_paramName),pp_array,assert);
}

bool CStruct::GetScript(uint32 nameChecksum, SStructScript *p_structScript, EAssertType assert) const
{
	Dbg_MsgAssert(p_structScript,("NULL p_structScript"));
	
	SWhatever value;
	if (search_for(nameChecksum,ESYMBOLTYPE_QSCRIPT,&value))
	{
		Dbg_MsgAssert(value.mpScript,("NULL value.mpScript"));
		p_structScript->mNameChecksum=nameChecksum;
		p_structScript->mpScriptTokens=value.mpScript;
		return true;
	}
	if (assert)
	{
		PrintContents(this);
		Dbg_MsgAssert(0,("Could not find script component named '%s' in structure",FindChecksumName(nameChecksum)));
	}	
	return false;	
}

bool CStruct::GetScript(const char *p_paramName, SStructScript *p_structScript, EAssertType assert) const
{
	return GetScript(Crc::GenerateCRCFromString(p_paramName),p_structScript,assert);
}

bool CStruct::GetChecksum(uint32 nameChecksum, uint32 *p_checksum, EAssertType assert) const
{
	Dbg_MsgAssert(p_checksum,("NULL p_checksum"));

	// Does not use the search_for function, because GetChecksum needs to ignore unnamed names
	// that resolve to structures.
	
	bool found=false;
	CComponent *p_comp=mp_components;
	while (p_comp)
	{
		if (p_comp->mNameChecksum==nameChecksum)
		{
			if (p_comp->mType==ESYMBOLTYPE_NAME)
			{
				uint32 ch=p_comp->mChecksum;
						
				// The name matches and the type matches, but ignore any un-named names which
				// resolve to structures since these are considered 'part of' the original structure.				
				CSymbolTableEntry *p_entry=Resolve(ch);	
				if (p_entry && p_entry->mType==ESYMBOLTYPE_STRUCTURE && p_comp->mNameChecksum==0)
				{
					// Do nothing.
				}
				else
				{
					*p_checksum=ch;
					found=true;
				}	
			}
		}
		else if (p_comp->mNameChecksum==0 && p_comp->mType==ESYMBOLTYPE_NAME)
		{
			// It's an unnamed name, so check whether it is a structure.
			uint32 ch=p_comp->mChecksum;
			CSymbolTableEntry *p_entry=Resolve(ch);
			if (p_entry && p_entry->mType==ESYMBOLTYPE_STRUCTURE)
			{
				// It is a structure, so search that.
                Dbg_MsgAssert(p_entry->mpStructure,("NULL p_entry->mpStructure"));
				// Note: Must not assert if not found, cos could be found later.
                if (p_entry->mpStructure->GetChecksum(nameChecksum,p_checksum)) 
				{
					found=true;
				}	
			}
		}
		
		p_comp=p_comp->mpNext;
	}
	if (assert && !found)
	{
		PrintContents(this);
		Dbg_MsgAssert(0,("Checksum parameter '%s' not found in structure",FindChecksumName(nameChecksum)));
	}	
	
    return found;
}

bool CStruct::GetChecksum(const char *p_paramName, uint32 *p_checksum, EAssertType assert) const
{
	return GetChecksum(Crc::GenerateCRCFromString(p_paramName),p_checksum,assert);
}

// Looks for a name, and if no name component is found, looks for a string instead, and if found
// calculates the checksum of that.
bool CStruct::GetChecksumOrStringChecksum(uint32 nameChecksum, uint32 *p_checksum, EAssertType assert) const
{
	Dbg_MsgAssert(p_checksum,("NULL p_checksum"));
	
	if (GetChecksum(nameChecksum,p_checksum,assert))
	{
		return true;
	}
	
	const char *p_string=NULL;
	if (GetString(nameChecksum,&p_string,assert))
	{
		Dbg_MsgAssert(p_string,("NULL p_string ?"));
		*p_checksum=Crc::GenerateCRCFromString(p_string);
		return true;
	}
	
	return false;	
}

bool CStruct::GetChecksumOrStringChecksum(const char *p_paramName, uint32 *p_checksum, EAssertType assert) const
{
	return GetChecksumOrStringChecksum(Crc::GenerateCRCFromString(p_paramName),p_checksum,assert);
}

// Infinite recursion of ContainsComponentNamed could occur, for example if we have a global structure foo={foo}
// which is trying to include itself.
// This counter will make an assert go off if ContainsComponentNamed tries to recurse a suspicious number of times.
#ifdef __NOPT_ASSERT__ 
static uint32 s_num_contains_component_named_recursions=0;
#endif

// Checks to see if there is any component with the given name.
// This is similar to ContainsFlag, except it doesn't care what the type is.
// This function is used by the script function GotParam.
bool CStruct::ContainsComponentNamed(uint32 checksum) const
{
	Dbg_MsgAssert(s_num_contains_component_named_recursions<10,("Possible infinite recursion of ContainsComponentNamed when searching for component named '%s'",FindChecksumName(checksum)));
	#ifdef __NOPT_ASSERT__
	++s_num_contains_component_named_recursions;
	#endif
	
    CComponent *p_comp=mp_components;
    while (p_comp)
    {
		if (p_comp->mNameChecksum==checksum)
		{
			// The name of the component matches, so return true.
			Dbg_MsgAssert(s_num_contains_component_named_recursions,("Eh ?"));
			#ifdef __NOPT_ASSERT__
			--s_num_contains_component_named_recursions;
			#endif
			return true;
		}
			
		if (p_comp->mNameChecksum==0)
		{
			// It's an unnamed component, so maybe it's a single isolated name, or maybe it
			// is a name referring to a structure defined elsewhere ...
			if (p_comp->mType==ESYMBOLTYPE_NAME)
			{
				// It is a name. Check first to see if it is the name being searched for ...
				uint32 ch=p_comp->mChecksum;
				if (ch==checksum)
				{
					// It is.
					Dbg_MsgAssert(s_num_contains_component_named_recursions,("Eh ?"));
					#ifdef __NOPT_ASSERT__
					--s_num_contains_component_named_recursions;
					#endif
					return true;
				}
				
				// Oh well, maybe the name is referring to a global structure ...
				CSymbolTableEntry *p_global=Resolve(ch);
				if (p_global)
				{
					// It is a global ...
					if (p_global->mType==ESYMBOLTYPE_STRUCTURE)
					{
						// It is a structure, so call this function on that.
						Dbg_MsgAssert(p_global->mpStructure,("NULL p_global->mpStructure"));
						if (p_global->mpStructure->ContainsComponentNamed(checksum))
						{
							Dbg_MsgAssert(s_num_contains_component_named_recursions,("Eh ?"));
							#ifdef __NOPT_ASSERT__
							--s_num_contains_component_named_recursions;
							#endif
							return true;
						}	
					}	
				}
			}	
		}	
		
        p_comp=p_comp->mpNext;
    }

	Dbg_MsgAssert(s_num_contains_component_named_recursions,("Eh ?"));
	#ifdef __NOPT_ASSERT__
	--s_num_contains_component_named_recursions;
	#endif
	return false;
}

bool CStruct::ContainsComponentNamed(const char *p_name) const
{
	return ContainsComponentNamed(Crc::GenerateCRCFromString(p_name));
}

// Infinite recursion of ContainsFlag could occur, for example if we have a global structure foo={foo}
// which is trying to include itself.
// This counter will make an assert go off if ContainsFlag tries to recurse a suspicious number of times.
#ifdef __NOPT_ASSERT__ 
static uint32 s_num_contains_flag_recursions=0;
#endif

// Returns true if the structure contains an unnamed component of type Name, whose checksum
// matches that passed.
// Eg, the structure Foo={Type=type_car Position=(0,0,0) CreatedAtStart} does contain an
// unnamed component of type name, with value GenerateCRC("CreatedAtStart"), so CreatedAtStart
// is like a flag.
bool CStruct::ContainsFlag(uint32 checksum) const
{
	Dbg_MsgAssert(s_num_contains_flag_recursions<10,("Possible infinite recursion of ContainsFlag when searching for flag '%s'",FindChecksumName(checksum)));
	#ifdef __NOPT_ASSERT__
	++s_num_contains_flag_recursions;
	#endif
	
    CComponent *p_comp=mp_components;
    while (p_comp)
    {
        if (p_comp->mNameChecksum==0 && p_comp->mType==ESYMBOLTYPE_NAME)
		{
			// Found an unnamed component of type name.
			uint32 name_checksum=p_comp->mChecksum;
			
			if (name_checksum==checksum)
			{	
				Dbg_MsgAssert(s_num_contains_flag_recursions,("Eh ?"));
				#ifdef __NOPT_ASSERT__
				--s_num_contains_flag_recursions;
				#endif
				return true;
			}
			
			// The component might be a structure.
			CSymbolTableEntry *p_global=Resolve(name_checksum);
			if (p_global)
			{
				// It is a global ...
				if (p_global->mType==ESYMBOLTYPE_STRUCTURE)
				{
					// It is a structure so check that structure to see if it contains the flag.
					Dbg_MsgAssert(p_global->mpStructure,("NULL p_global->mpStructure"));
					if (p_global->mpStructure->ContainsFlag(checksum))
					{
						Dbg_MsgAssert(s_num_contains_flag_recursions,("Eh ?"));
						#ifdef __NOPT_ASSERT__
						--s_num_contains_flag_recursions;
						#endif
						return true;
					}	
				}	
			}
		}	
		
        p_comp=p_comp->mpNext;
    }
	
	Dbg_MsgAssert(s_num_contains_flag_recursions,("Eh ?"));
	#ifdef __NOPT_ASSERT__
	--s_num_contains_flag_recursions;
	#endif
    return false;
}

bool CStruct::ContainsFlag(const char *p_name) const
{
	return ContainsFlag(Crc::GenerateCRCFromString(p_name));
}

// Returns true if this structure contains p_struct as a substructure anywhere.
// This is used in CScript::set_script, to determine whether clearing a structure
// will result in the clearing of another structure.
bool CStruct::References(CStruct *p_struct)
{
	if (p_struct==this)
	{
		return true;
	}
	
    CComponent *p_comp=mp_components;
    while (p_comp)
    {
        if (p_comp->mType==ESYMBOLTYPE_STRUCTURE)
		{
			Dbg_MsgAssert(p_comp->mpStructure,("NULL p_comp->mpStructure"));
			if (p_comp->mpStructure->References(p_struct))
			{
				return true;
			}
		}		
        p_comp=p_comp->mpNext;
    }
	
	return false;
}


///////////////////////////////////////////////////////////////////////////////////
// TODO: Remove the following Get... functions at some point.
// They are only included to provide back compatibility with the old code without
// having to change thousands of occurrences.
//
bool CStruct::GetText(uint32 nameChecksum, const char **pp_text, bool assert) const
{
	return GetString(nameChecksum,pp_text,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetText(const char *p_paramName, const char **pp_text, bool assert) const
{
	return GetString(p_paramName,pp_text,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetLocalText(uint32 nameChecksum, const char **pp_text, bool assert) const
{
	return GetString(nameChecksum,pp_text,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetLocalText(const char *p_paramName, const char **pp_text, bool assert) const
{
	return GetString(p_paramName,pp_text,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetInteger(uint32 nameChecksum, int *p_integerValue, bool assert) const
{
	return GetInteger(nameChecksum,p_integerValue,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetInteger(const char *p_paramName, int *p_integerValue, bool assert) const
{
	return GetInteger(p_paramName,p_integerValue,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetFloat(uint32 nameChecksum, float *p_float, bool assert) const
{
	return GetFloat(nameChecksum,p_float,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetFloat(const char *p_paramName, float *p_floatValue, bool assert) const
{
	return GetFloat(p_paramName,p_floatValue,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetVector(uint32 nameChecksum, Mth::Vector *p_vector, bool assert) const
{
	return GetVector(nameChecksum,p_vector,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetVector(const char *p_paramName, Mth::Vector *p_vector, bool assert) const
{
	return GetVector(p_paramName,p_vector,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetPair(uint32 nameChecksum, CPair *p_pair, bool assert) const
{
	float x=0.0f;
	float y=0.0f;
	bool ret_val=GetPair(nameChecksum,&x,&y,assert?ASSERT:NO_ASSERT);
	Dbg_MsgAssert(p_pair,("NULL p_pair"));
	p_pair->mX=x;
	p_pair->mY=y;
	return ret_val;
}

bool CStruct::GetPair(const char *p_paramName, CPair *p_pair, bool assert) const
{
	float x=0.0f;
	float y=0.0f;
	bool ret_val=GetPair(p_paramName,&x,&y,assert?ASSERT:NO_ASSERT);
	Dbg_MsgAssert(p_pair,("NULL p_pair"));
	p_pair->mX=x;
	p_pair->mY=y;
	return ret_val;
}

bool CStruct::GetStructure(uint32 nameChecksum, CStruct **pp_struct, bool assert) const
{
	return GetStructure(nameChecksum,pp_struct,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetStructure(const char *p_paramName, CStruct **pp_struct, bool assert) const
{
	return GetStructure(p_paramName,pp_struct,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetChecksum(uint32 nameChecksum, uint32 *p_checksum, bool assert) const
{
	return GetChecksum(nameChecksum,p_checksum,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetChecksum(const char *p_paramName, uint32 *p_checksum, bool assert) const
{
	return GetChecksum(p_paramName,p_checksum,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetChecksumOrStringChecksum(uint32 nameChecksum, uint32 *p_checksum, bool assert) const
{
	return GetChecksumOrStringChecksum(nameChecksum,p_checksum,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetChecksumOrStringChecksum(const char *p_paramName, uint32 *p_checksum, bool assert) const
{
	return GetChecksumOrStringChecksum(p_paramName,p_checksum,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetArray(uint32 nameChecksum, CArray **pp_array, bool assert) const
{
	return GetArray(nameChecksum,pp_array,assert?ASSERT:NO_ASSERT);
}

bool CStruct::GetArray(const char *p_paramName, CArray **pp_array, bool assert) const
{
	return GetArray(p_paramName,pp_array,assert?ASSERT:NO_ASSERT);
}

#if 0
// Go throught the raw pool and see which entries are valid, and dump them
//	uint8 *				mp_buffer;
//	uint8 *				mp_buffer_end;
//	int					m_totalItems; // that we have room for
//	int					m_itemSize;


void DumpStructs()
{
	Mem::CCompactPool * p_pool = Mem::CPoolable::sp_pool[Mem::CPoolable::s_currentPool];
	CStruct *p_struct = (CStruct *)p_pool->mp_buffer;
	while (p_struct <  (CStruct *)p_pool->mp_buffer_end)
	{
		if (p_pool->IsInPool(p_struct))
		{
			PrintContents(p_struct);
		}
		
		p_struct++;		
	}
}

#endif


///////////////////////////////////////////////////////////////////////////////////
		
} // namespace Script



================================================
FILE: Code/Gel/Scripting/struct.h
================================================
#ifndef	__SCRIPTING_STRUCT_H
#define	__SCRIPTING_STRUCT_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

#ifndef	__SCRIPTING_SCRIPTDEFS_H
#include  // For EAssertType
#endif

#ifndef	__SCRIPTING_SYMBOLTYPE_H
#include  // For ESymbolType
#endif

namespace Mth
{
class Vector;
}

namespace Script
{

class CPair; // TODO: Remove once the old GetPair member function is removed.

class CComponent;
class CScript;
class CArray;
struct SWhatever;

// This defines a reference to a script that is defined in a CStruct.
// A pointer to one of these can then be passed to CScript::SetScript.
// Only exists as a convenient way of passing the data from GetScript to SetScript.
struct SStructScript
{
	// The name of the script, which is the same as its parameter name in the source structure.
	uint32 mNameChecksum;
	// Pointer to the script data in the structure.
	// Note that this means that this pointer will become invalid once the source structure is deleted.
	// OK though, because SStructScript is only used to pass the info to CScript::SetScript, which
	// will make its own copy of the script data.
	uint8 *mpScriptTokens;
	
	SStructScript() {mNameChecksum=NO_NAME; mpScriptTokens=NULL;}
};

#ifdef __PLAT_WN32__
class CStruct
#else
class CStruct : public Mem::CPoolable
#endif
{
	// Head pointer of the list of components.
	CComponent *mp_components;
    
	#ifdef __NOPT_ASSERT__ 
	// The script that created this structure. Only valid (non NULL) if this is 
	// a mpFunctionParams structure created by a script.
	// Needed so that if the Get... functions assert they can print info about 
	// the parent script, eg the line number of the error.
	CScript *mp_parent_script;
	#endif

	void init();
	bool search_for(uint32 nameChecksum, ESymbolType type, SWhatever *p_value) const;
	
public:
    CStruct();
    ~CStruct();

	// These are defined, and will create new instances of any entities pointed to by the
	// source structure, so the two structures will not share any pointers.
	// Careful when using these though, because if copying a complex structure with lots
	// of nested sub-structures and arrays it could potentially take a long time to execute.
	CStruct( const CStruct& rhs );
	CStruct& operator=( const CStruct& rhs );
	
	// This will merge the contents of the rhs into this structure.
	// Functionally the same as the old AppendStructure function.
	CStruct& operator+=( const CStruct& rhs );
	
    // Deletes all components, deleting any arrays or structures referenced by the components.
    void Clear();

	// Removes the component with the given name.
	void RemoveComponent(uint32 nameChecksum);
	void RemoveComponent(const char *p_name);

	// Same as RemoveComponent except the type must match too.
	void RemoveComponentWithType(uint32 nameChecksum, uint8 type);
	void RemoveComponentWithType(const char *p_name, uint8 type);
	
	// Removes the flag (unnamed name) with the given name.
	void RemoveFlag(uint32 checksum);
	void RemoveFlag(const char *p_name);

	// Adds p_comp to the end of the list of components, removing any existing components with
	// the same name.
	void AddComponent(CComponent *p_comp);
	
	#ifdef __NOPT_ASSERT__ 
	void SetParentScript(CScript *p_parentScript);
	#endif

	// If a component of the same name and type already exists it will be replaced with the new value.
	// If a component of the same name but different type exists, it will assert.
	// NameChecksum can be 0, meaning no name.
	// If all OK, returns a pointer to the new component.
    
	// These will copy in the actual string data, not store the pointer.
	void AddString(uint32 nameChecksum, const char *p_string);
	void AddString(const char *p_name, const char *p_string);
	void AddLocalString(uint32 nameChecksum, const char *p_string);
	void AddLocalString(const char *p_name, const char *p_string);
	
	void AddInteger(uint32 nameChecksum, int integer);
	void AddInteger(const char *p_name, int integer);
	
	void AddFloat(uint32 nameChecksum, float float_val);
	void AddFloat(const char *p_name, float float_val);

	void AddChecksum(uint32 nameChecksum, uint32 checksum);
	void AddChecksum(const char *p_name, uint32 checksum);
	
	void AddVector(uint32 nameChecksum, float x, float y, float z);
	void AddVector(const char* p_name, float x, float y, float z);
	void AddVector( uint32 nameChecksum, Mth::Vector vector );
	void AddVector( const char* p_name, Mth::Vector vector );

	
	void AddPair(uint32 nameChecksum, float x, float y);
	void AddPair(const char *p_name, float x, float y);

	// Creates a new array & copies in the contents of the passed array.
	void AddArray(uint32 nameChecksum, const CArray *p_array);
	void AddArray(const char *p_name, const CArray *p_array);

	// Stores the passed pointer. Faster than copying the contents.
	// The passed pointer is no longer const because the CStruct will delete it when destroyed.
	void AddArrayPointer(uint32 nameChecksum, CArray *p_array);
	void AddArrayPointer(const char *p_name, CArray *p_array);

	// Creates a new structure & copies in the contents of the passed structure.
	void AddStructure(uint32 nameChecksum, const CStruct *p_structure);
	void AddStructure(const char *p_name, const CStruct *p_structure);
	
	// Stores the passed pointer. Faster than copying the contents.
	// The passed pointer is no longer const because the CStruct will delete it when destroyed.
	void AddStructurePointer(uint32 nameChecksum, CStruct *p_structure);
	void AddStructurePointer(const char *p_name, CStruct *p_structure);

	// Creates a new script component and copies in the passed script.
	void AddScript(uint32 nameChecksum, const uint8 *p_scriptTokens, uint32 size);
	void AddScript(const char *p_name, const uint8 *p_scriptTokens, uint32 size);
	
	///////////////////////////////////////////////////////////////////////////////////
	// TODO: Remove all these AddComponent functions at some point.
	// They are only included to provide back compatibility with the old code without
	// having to change thousands of occurrences of calls to AddComponent.
	// Gradually phase out the old AddComponent instead.
	//
	
    // String or local string.
	void AddComponent(uint32 nameChecksum, ESymbolType type, const char *p_string); // This will copy the actual string data
    // Integer or any other 4 byte value.
	void AddComponent(uint32 nameChecksum, ESymbolType type, int integer);
    // Vector
	void AddComponent(uint32 nameChecksum, float x, float y, float z);
    // Pair
	void AddComponent(uint32 nameChecksum, float x, float y);
	// Array
	void AddComponent(uint32 nameChecksum, CArray *p_array); // Copies the data in the array
	///////////////////////////////////////////////////////////////////////////////////


	bool GetString		(uint32 nameChecksum, 		const char **pp_text, 	EAssertType assert=NO_ASSERT) const;
	bool GetString		(const char *p_paramName, 	const char **pp_text, 	EAssertType assert=NO_ASSERT) const;
    bool GetLocalString	(uint32 nameChecksum, 		const char **pp_text, 	EAssertType assert=NO_ASSERT) const;
	bool GetLocalString	(const char *p_paramName, 	const char **pp_text, 	EAssertType assert=NO_ASSERT) const;
	bool GetInteger		(uint32 nameChecksum, 		int *p_integerValue, 	EAssertType assert=NO_ASSERT) const;
	bool GetInteger		(const char *p_paramName, 	int *p_integerValue, 	EAssertType assert=NO_ASSERT) const;
	bool GetFloat		(uint32 nameChecksum, 		float *p_floatValue,	EAssertType assert=NO_ASSERT) const;
	bool GetFloat		(const char *p_paramName, 	float *p_floatValue, 	EAssertType assert=NO_ASSERT) const;
	bool GetVector		(uint32 nameChecksum, 		Mth::Vector *p_vector, 	EAssertType assert=NO_ASSERT) const;
	bool GetVector		(const char *p_paramName, 	Mth::Vector *p_vector, 	EAssertType assert=NO_ASSERT) const;
	bool GetPair		(uint32 nameChecksum, 		float *p_x, float *p_y,	EAssertType assert=NO_ASSERT) const;
	bool GetPair		(const char *p_paramName, 	float *p_x, float *p_y,	EAssertType assert=NO_ASSERT) const;
	bool GetStructure	(uint32 nameChecksum, 		CStruct **pp_structure,	EAssertType assert=NO_ASSERT) const;
	bool GetStructure	(const char *p_paramName, 	CStruct **pp_structure,	EAssertType assert=NO_ASSERT) const;
	bool GetChecksum	(uint32 nameChecksum, 		uint32 *p_checksum, 	EAssertType assert=NO_ASSERT) const;
	bool GetChecksum	(const char *p_paramName, 	uint32 *p_checksum, 	EAssertType assert=NO_ASSERT) const;
	bool GetArray		(uint32 nameChecksum, 		CArray **pp_array, 		EAssertType assert=NO_ASSERT) const;
	bool GetArray		(const char *p_paramName, 	CArray **pp_array, 		EAssertType assert=NO_ASSERT) const;
	bool GetScript		(uint32 nameChecksum, 		SStructScript *p_structScript, EAssertType assert=NO_ASSERT) const;
	bool GetScript		(const char *p_paramName,	SStructScript *p_structScript, EAssertType assert=NO_ASSERT) const;
	bool GetChecksumOrStringChecksum(uint32 nameChecksum, uint32 *p_checksum, EAssertType assert=NO_ASSERT) const;
	bool GetChecksumOrStringChecksum(const char *p_paramName, uint32 *p_checksum, EAssertType assert=NO_ASSERT) const;
	bool ContainsComponentNamed(uint32 checksum) const;
	bool ContainsComponentNamed(const char *p_name) const;
	bool ContainsFlag	(uint32 checksum) const;
	bool ContainsFlag	(const char *p_name) const;

	///////////////////////////////////////////////////////////////////////////////////
	// TODO: Remove all these GetText functions at some point.
	// They are only included to provide back compatibility with the old code without
	// having to change thousands of occurrences of calls to GetText.
	// Some of them do not have a default value for assert given, to prevent ambiguities with the above functions.
    bool GetText(uint32 nameChecksum, const char **pp_text, bool assert=false) const;
	bool GetText(const char *p_paramName, const char **pp_text, bool assert=false) const;
    bool GetLocalText(uint32 nameChecksum, const char **pp_text, bool assert=false) const;
	bool GetLocalText(const char *p_paramName, const char **pp_text, bool assert=false) const;
	bool GetInteger(uint32 nameChecksum, int *p_integerValue, bool assert) const;
	bool GetInteger(const char *p_paramName, int *p_integerValue, bool assert) const;
	bool GetFloat(uint32 nameChecksum, float *p_float, bool assert) const;
	bool GetFloat(const char *p_paramName, float *p_floatValue, bool assert) const;
	bool GetVector(uint32 nameChecksum, Mth::Vector *p_vector, bool assert) const;
	bool GetVector(const char *p_paramName, Mth::Vector *p_vector, bool assert) const;
	bool GetPair(uint32 nameChecksum, CPair *p_pair, bool assert=false) const;
	bool GetPair(const char *p_paramName, CPair *p_pair, bool assert=false) const;
	bool GetStructure(uint32 nameChecksum, CStruct **pp_struct, bool assert) const;
	bool GetStructure(const char *p_paramName, CStruct **pp_struct, bool assert) const;
	bool GetChecksum(uint32 nameChecksum, uint32 *p_checksum, bool assert) const;
	bool GetChecksum(const char *p_paramName, uint32 *p_checksum, bool assert) const;
	bool GetChecksumOrStringChecksum(uint32 nameChecksum, uint32 *p_checksum, bool assert) const;
	bool GetChecksumOrStringChecksum(const char *p_paramName, uint32 *p_checksum, bool assert) const;
	bool GetArray(uint32 nameChecksum, CArray **pp_array, bool assert) const;
	bool GetArray(const char *p_paramName, CArray **pp_array, bool assert) const;
	///////////////////////////////////////////////////////////////////////////////////
	
	// TODO: Remove at some point. Provided for back-compatibility.
	void AppendStructure(const CStruct *p_struct);

	void AppendComponentPointer(CComponent *p_comp);

	// This will copy the contents of this structure into p_dest, but in such a way that
	// p_dest will contain no unnamed-structure references, they will all get expanded.
	// This was added because sometimes one wants to step through each of the components of
	// a structure using GetNextComponent, but GetNextComponent itself cannot recurse into
	// unnamed structures, because when it reaches the end of one it won't know how to get back
	// to the parent.
	// So instead just create a new structure, copy the other structure into it using ExpandInto,
	// then step through that using GetNextComponent, then delete the new structure once finished.
	// It's a bit memory intensive, but saves having to write special code to resolve unnamed
	// structures & recursing whenever one wants to scan through the components.
	// Could be handy for other things later too.
	// recursionCount is just to catch infinite recursion, so no need to pass it a value.
	void ExpandInto(CStruct *p_dest, int recursionCount=0) const;
	
	// Returns true if the structure contains no components.
	bool IsEmpty() const;
    
	// Searches for a component with the given name, but will also recurse into substructures.
	// Used when resolving the  syntax.
	CComponent *FindNamedComponentRecurse(uint32 nameChecksum) const;
	
	// If passed NULL this will return the first (leftmost) component.
	// If passed non-NULL it return the next component (to the right) 
	// Returns NULL if the passed component is the last component.
	CComponent *GetNextComponent(CComponent *p_comp=NULL) const;

	// Returns true if this structure contains p_struct as a substructure anywhere.
	// This is used in CScript::set_script, to determine whether clearing a structure
	// will result in the clearing of another structure.
	bool References(CStruct *p_struct);
	
	#ifdef __NOPT_ASSERT__ 
	CScript *GetParentScript() const;
	bool CheckForDuplicateFlags() const;
	#endif
};

void CleanUpComponent(CComponent *p_comp);
void CopyComponent(CComponent *p_dest, const CComponent *p_source);
void CleanUpArray(CArray *p_array);
void CopyArray(CArray *p_dest, const CArray *p_source);

void DumpStructs();


} // namespace Script

#endif // #ifndef	__SCRIPTING_STRUCT_H


================================================
FILE: Code/Gel/Scripting/symboltable.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// symboltable.cpp		KSH 17 Oct 2001
//
// Symbol hash table stuff.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 

#include 

DefinePoolableClass(Script::CSymbolTableEntry);

namespace Script
{

CSymbolTableEntry::CSymbolTableEntry()
{
	// Initialise everything. CSymbolTableEntry is not derived from CClass so we don't get
	// the auro-zeroing.
	Reset();
}

// The destructor does not delete anything to avoid cyclic dependencies, so just assert
// if it look like there could be a non-NULL pointer left.
#ifdef __NOPT_ASSERT__
CSymbolTableEntry::~CSymbolTableEntry()
{
	Dbg_MsgAssert(mUnion==0,("CSymbolTableEntry still contains data, possibly an undeleted pointer"));
}
#endif

void CSymbolTableEntry::Reset()
{
    mNameChecksum=NO_NAME;
	mGotReloaded=false;
    mUsed=false;
    mType=ESYMBOLTYPE_NONE;
	mUnion=0;
    mpNext=NULL;
	mSourceFileNameChecksum=NO_NAME;
	#ifdef COUNT_USAGE
	mUsage=0;
	#endif
}

static CSymbolTableEntry *sp_hash_table=NULL;

void CreateSymbolHashTable()
{
	Dbg_MsgAssert(sp_hash_table==NULL,("sp_hash_table not NULL ?"));
	sp_hash_table=new CSymbolTableEntry[1<mUsed) 
//	{
//		//++NothingCount;
//		return NULL;
//	}	

#if 0	
	//MCozzini 4-30-03 optimization to avoid the p_sym test
	if(p_sym->mNameChecksum==checksum)
	{
		return p_sym;
	}
	p_sym=p_sym->mpNext;


    // Scan through the linked list until the complete checksum is found.
    while (p_sym && p_sym->mNameChecksum!=checksum) 
	{
		p_sym=p_sym->mpNext;
	}	
#else
	// it it's an unused slot, then both mNameChecksum and mpNext will be 0 (NULL)
	// so it will just drop through this test
	do 
	{
		if(p_sym->mNameChecksum == checksum)
		{
			return p_sym;	// Quicker to return here than to break
		}
		p_sym=p_sym->mpNext;
	} while (p_sym && p_sym->mNameChecksum!=checksum);
	
	// Here p_sym will be NULL	
	return p_sym;	

#endif
	
	// p_sym will be NULL if the checksum was not found.
	/*
	if (p_sym)
	{
		if (p_sym->mType==ESYMBOLTYPE_QSCRIPT)
		{
			++ScriptCount;
		}
		else if (p_sym->mType==ESYMBOLTYPE_CFUNCTION)
		{
			++CFuncCount;
		}
		else if (p_sym->mType==ESYMBOLTYPE_MEMBERFUNCTION)
		{
			++MemberFuncCount;
		}
		else if (p_sym->mType==ESYMBOLTYPE_FLOAT)
		{
			++FloatCount;
		}
		else if (p_sym->mType==ESYMBOLTYPE_INTEGER)
		{
			++IntegerCount;
		}
		else if (p_sym->mType==ESYMBOLTYPE_STRUCTURE)
		{
			++StructureCount;
		}
		else if (p_sym->mType==ESYMBOLTYPE_ARRAY)
		{
			++ArrayCount;
		}
		
	}
	else
	{
		++NothingCount;
	}*/
    return p_sym;
}

CSymbolTableEntry *LookUpSymbol(const char *p_name)
{
	return LookUpSymbol(Crc::GenerateCRCFromString(p_name));
}	

// Resolves a checksum into a symbol table entry. Does any stepping over of chains of symbols linked by
// name. Eg, Resolving a where a=b, b=c, c=d, d=6 will return the symbol d, with value 6.
// Returns NULL if the checksum cannot be resolved.
CSymbolTableEntry *Resolve(uint32 checksum)
{
    // Look up the checksum.
    CSymbolTableEntry *p_entry=LookUpSymbol(checksum);
	if (p_entry && p_entry->mType==ESYMBOLTYPE_NAME)
	{
		// If the symbol is of type name, then it may be that the name is the name of another symbol,
		// in which case we need to find that.
		// Eg we could be trying to resolve a, where a=b, b=c, c=7, in which case we need to find 7.
		// So loop until either a non-name symbol is reached, or no further symbol is reached.
		
		#ifdef __NOPT_ASSERT__ 
		// An infinite loop could occur, for example if we have a global a=b where b=a
		// This counter will make an assert go off if we try and loop a suspicious number of times.
		uint32 num_loops=0;
		#endif
	
		while (true)
		{
			CSymbolTableEntry *p_new=LookUpSymbol(p_entry->mChecksum);
			if (!p_new)
			{
				// Reached the end of the chain, so return the last entry, which will have type name.
				return p_entry;
			}
				
			if (p_new->mType!=ESYMBOLTYPE_NAME)
			{
				// Reached something not of type name, so return that.
				return p_new;
			}
			
			// Repeat.	
			p_entry=p_new;
			Dbg_MsgAssert(num_loops++<10,("Possible infinite recursion of the Resolve function when trying to resolve '%s', bailing out!",FindChecksumName(checksum)));
		}
	}
			
    return p_entry;
}

CSymbolTableEntry *Resolve(const char *p_name)
{
	return Resolve(Crc::GenerateCRCFromString(p_name));
}	

// Removes the symbol from the table.
// Used when reloading a .qb file. 
// Note: Will not delete any entities referred to by the symbol, and will assert if any of the
// pointers are not NULL.
// This is to avoid a cyclic dependency between symboltable and struct.
// It is up to the caller to have deleted these before calling this.
void RemoveSymbol(CSymbolTableEntry *p_sym)
{
	Dbg_MsgAssert(p_sym,("NULL p_sym"));
	Dbg_MsgAssert(p_sym->mUnion==0,("CSymbolTableEntry still contains data, possibly an undeleted pointer. Type='%s'",GetTypeName(p_sym->mType)));
	Dbg_MsgAssert(p_sym->mUsed,("Tried to call RemoveSymbol on an unused CSymbolTableEntry"));
	Dbg_MsgAssert(sp_hash_table!=NULL,("sp_hash_table is NULL ?"));

	// Get the head pointer of the list that p_sym is in (or should be in) 
    CSymbolTableEntry *p_head=&sp_hash_table[ p_sym->mNameChecksum & ((1<mpNext)
        {
			// There are later entries in the list, so copy the first of these down
			// to fill the hole.
			CSymbolTableEntry *p_source=p_sym->mpNext;
			
			// Copy the contents. Not using the assignement operator because that won't link.
			// It is declared but not defined so as to cause it not to link, to prevent any problems
			// with copying pointers if it gets used anywhere else.
			uint32 *p_source_word=(uint32*)p_source;
			uint32 *p_dest_word=(uint32*)p_sym;
			Dbg_MsgAssert((sizeof(CSymbolTableEntry)&3)==0,("sizeof(CSymbolTableEntry) not a multiple of 4 ?"));
			for (uint32 i=0; imUnion=0;  
			delete p_source;
        }
		else
		{
			// Just reset the contents of p_sym.
			p_sym->Reset();
		}
    }
    else
    {
        // p_sym is not in the hash table, so it must have been allocated off the pool.
		// In order to unlink it from the list we need to find the previous entry in the list.
		// So search through the list until p_sym is found.		
				
		// Start with mpNext, cos we know p_sym is not p_head		
		CSymbolTableEntry *p_previous=p_head;
		CSymbolTableEntry *p_search=p_head->mpNext;
		while (p_search)
		{
			if (p_search==p_sym)
			{
				break;
			}	
			p_previous=p_search;
			p_search=p_search->mpNext;
		}
		
		Dbg_MsgAssert(p_search,("p_sym not found in list ? (checksum='%s')",FindChecksumName(p_sym->mNameChecksum)));
		
		// Unlink p_sym and delete it.
		p_previous->mpNext=p_sym->mpNext;
		delete p_sym;
	}	
}

// Adds a new checksum to the directory and returns a pointer to the new entry.
CSymbolTableEntry *CreateNewSymbolEntry(uint32 checksum)
{
	#ifdef __NOPT_ASSERT__
	CSymbolTableEntry *p_entry=LookUpSymbol(checksum);
    Dbg_MsgAssert(p_entry==NULL,("Symbol '%s' defined twice.",FindChecksumName(checksum)));
	#endif
    
    // Get the head pointer of the list where the new symbol needs to go.
	Dbg_MsgAssert(sp_hash_table!=NULL,("sp_hash_table is NULL ?"));
    CSymbolTableEntry *p_sym=&sp_hash_table[ checksum & ((1<mUsed)
    {
		p_sym->Reset();
        // Set the checksum.
        p_sym->mNameChecksum=checksum;
        // Flag it as used.
        p_sym->mUsed=true;
		// Set the mGotReloaded flag so that other game-specific code can see 
		// that the symbol has changed in value.
		p_sym->mGotReloaded=true;
        return p_sym;
    }

    // Find the last element of the linked list, checking for any checksum
    // clashes on the way.
    CSymbolTableEntry *p_previous=NULL;
    while (p_sym)
    {
        Dbg_MsgAssert(p_sym->mNameChecksum!=checksum,("Checksum clash in symbol table: '%s'",FindChecksumName(checksum)));
        p_previous=p_sym;
        p_sym=p_sym->mpNext;
    }

	// Get a new entry from the pool.
    CSymbolTableEntry *p_new=new CSymbolTableEntry;
	
    // Set the checksum & next pointer.
    p_new->mNameChecksum=checksum;
    // Flag it as used.
    p_new->mUsed=true;
	// Set the mGotReloaded flag so that other game-specific code can see 
	// that the symbol has changed in value.
	p_new->mGotReloaded=true;
	
    p_new->mpNext=NULL;

    // This assert should never go off, but do it just in case.
    Dbg_MsgAssert(p_previous,("NULL p_previous ???"));
    // Stick pNew on the end of the list.
    p_previous->mpNext=p_new;

    return p_new;
}

// This function provides an easy way to loop through all the symbols in the symbol table by
// hiding all the messy logic for stepping through the hash table.
//
// If passed NULL, it will return the first entry in the symbol table (or NULL if there is nothing in it)
// If passed a pointer to a CSymbolTableEntry, it will return the next one, or NULL if none left.
//
// So if you want to loop through all the symbols just do something like:
// CSymbolTableEntry *p_sym=GetNextSymbolTableEntry(); // No need to pass NULL, it defaults to it.
// while (p_sym)
// {
// 		// ...
//		p_sym=GetNextSymbolTableEntry(p_sym);
// }
//
CSymbolTableEntry *GetNextSymbolTableEntry(CSymbolTableEntry *p_sym)
{
	// static's for keeping track of where we are in the hash table.
	static uint32 s_current_hash_index=0;
	static CSymbolTableEntry *sp_last=NULL;
	
	if (p_sym==NULL)
	{
		// They want the first symbol.
		s_current_hash_index=0;
		p_sym=sp_hash_table;
		
		// Step through the hash table entries until a used one is found.
		while (!p_sym->mUsed)
		{
			++s_current_hash_index;
			++p_sym;
			
			if (s_current_hash_index>=(1<mpNext;
	if (!p_sym)
	{
		// We've gone off the end of this linked list, so go onto the next entry in the hash table.
		++s_current_hash_index;
		p_sym=sp_hash_table+s_current_hash_index;
		
		if (s_current_hash_index>=(1<mUsed)
		{
			++s_current_hash_index;
			++p_sym;
			
			if (s_current_hash_index>=(1<mType)
    {
    case ESYMBOLTYPE_FLOAT:
        return p_entry->mFloatValue;
        break;
    case ESYMBOLTYPE_INTEGER:
        return (float)p_entry->mIntegerValue;
        break;    
    default:
        Dbg_MsgAssert(0,("Symbol '%s' of type '%s' cannot be converted to a float.",FindChecksumName(checksum),GetTypeName(p_entry->mType)));
        return 0.0f;
        break;
    }        
	
	return 0.0f;
}

float GetFloat(const char *p_name, EAssertType assert)
{
	return GetFloat(Crc::GenerateCRCFromString(p_name),assert);
}

int GetInteger(uint32 checksum, EAssertType assert)
{
    CSymbolTableEntry *p_entry=Resolve(checksum);
    if (!p_entry)
    {
		Dbg_MsgAssert(!assert,("Integer '%s' not found.",FindChecksumName(checksum)));
        return 0;
    }    

    switch (p_entry->mType)
    {
    case ESYMBOLTYPE_FLOAT:
        return (int)p_entry->mFloatValue;
        break;
    case ESYMBOLTYPE_INTEGER:
        return p_entry->mIntegerValue;
        break;    
    default:
        Dbg_MsgAssert(0,("Symbol '%s' of type '%s' cannot be converted to an integer.",FindChecksumName(checksum),GetTypeName(p_entry->mType)));
        return 0;
        break;
    }        

	return 0;
}

int GetInteger(const char *p_name, EAssertType assert)
{
	return GetInteger(Crc::GenerateCRCFromString(p_name),assert);
}

uint32 GetChecksum(uint32 checksum, EAssertType assert)
{
    CSymbolTableEntry *p_entry=Resolve(checksum);
    if (!p_entry)
    {
		Dbg_MsgAssert(!assert,("Checksum named '%s' not found.",FindChecksumName(checksum)));
        return 0;
    }    

    Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_NAME,("GetChecksum: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType)));
	return p_entry->mChecksum;	
}

uint32 GetChecksum(const char *p_name, EAssertType assert)
{
	return GetChecksum(Crc::GenerateCRCFromString(p_name),assert);
}

const char *GetString(uint32 checksum, EAssertType assert)
{
    CSymbolTableEntry *p_entry=Resolve(checksum);
    if (!p_entry)
    {
		Dbg_MsgAssert(!assert,("String named '%s' not found.",FindChecksumName(checksum)));
        return "";
    }    

    Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_STRING || p_entry->mType==ESYMBOLTYPE_LOCALSTRING,("GetString: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType)));
	Dbg_MsgAssert(p_entry->mpString,("NULL p_entry->mpString"));
	return p_entry->mpString;	
}

const char *GetString(const char *p_name, EAssertType assert)
{
	return GetString(Crc::GenerateCRCFromString(p_name),assert);
}

const char *GetLocalString(uint32 checksum, EAssertType assert)
{
    CSymbolTableEntry *p_entry=Resolve(checksum);
    if (!p_entry)
    {
		Dbg_MsgAssert(!assert,("Local string named '%s' not found.",FindChecksumName(checksum)));
        return "";
    }    

    Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_LOCALSTRING || p_entry->mType==ESYMBOLTYPE_STRING,("GetLocalString: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType)));
	Dbg_MsgAssert(p_entry->mpLocalString,("NULL p_entry->mpLocalString"));
	return p_entry->mpLocalString;	
}

const char *GetLocalString(const char *p_name, EAssertType assert)
{
	return GetLocalString(Crc::GenerateCRCFromString(p_name),assert);
}

CVector *GetVector(uint32 checksum, EAssertType assert)
{
    CSymbolTableEntry *p_entry=Resolve(checksum);
    if (!p_entry)
    {
		Dbg_MsgAssert(!assert,("Vector named '%s' not found.",FindChecksumName(checksum)));
        return NULL;
    }    

    Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_VECTOR,("GetVector: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType)));
	Dbg_MsgAssert(p_entry->mpVector,("NULL p_entry->mpVector"));
	return p_entry->mpVector;	
}

CVector *GetVector(const char *p_name, EAssertType assert)
{
	return GetVector(Crc::GenerateCRCFromString(p_name),assert);
}

CPair *GetPair(uint32 checksum, EAssertType assert)
{
    CSymbolTableEntry *p_entry=Resolve(checksum);
    if (!p_entry)
    {
		Dbg_MsgAssert(!assert,("Pair named '%s' not found.",FindChecksumName(checksum)));
        return NULL;
    }    

    Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_PAIR,("GetPair: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType)));
	Dbg_MsgAssert(p_entry->mpPair,("NULL p_entry->mpPair"));
	return p_entry->mpPair;	
}

CPair *GetPair(const char *p_name, EAssertType assert)
{
	return GetPair(Crc::GenerateCRCFromString(p_name),assert);
}

CStruct *GetStructure(uint32 checksum, EAssertType assert)
{
    CSymbolTableEntry *p_entry=Resolve(checksum);
    if (!p_entry)
    {
		Dbg_MsgAssert(!assert,("Structure named '%s' not found.",FindChecksumName(checksum)));
        return NULL;
    }    

	#ifdef	__NOPT_ASSERT__
	if (assert)
	{
		Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_STRUCTURE,("GetStructure: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType)));
	}
	else
	#endif
	{
		if (p_entry->mType!=ESYMBOLTYPE_STRUCTURE)
		{
			return NULL;
		}
	}		
	
	Dbg_MsgAssert(p_entry->mpStructure,("NULL p_entry->mpStructure"));
	return p_entry->mpStructure;	
}

CStruct *GetStructure(const char *p_name, EAssertType assert)
{
	return GetStructure(Crc::GenerateCRCFromString(p_name),assert);
}

CArray *GetArray(uint32 checksum, EAssertType assert)
{
    CSymbolTableEntry *p_entry=Resolve(checksum);
    if (!p_entry)
    {
		Dbg_MsgAssert(!assert,("Array named '%s' not found.",FindChecksumName(checksum)));
        return NULL;
    }    

	#ifdef	__NOPT_ASSERT__
	if (assert)
	{
		Dbg_MsgAssert(p_entry->mType==ESYMBOLTYPE_ARRAY,("GetArray: Symbol '%s' has the wrong type of '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType)));
	}
	else
	#endif
	{
		if (p_entry->mType!=ESYMBOLTYPE_ARRAY)
		{
			return NULL;
		}
	}
			
	Dbg_MsgAssert(p_entry->mpArray,("NULL p_entry->mpArray"));
	return p_entry->mpArray;	
}

CArray *GetArray(const char *p_name, EAssertType assert)
{
	return GetArray(Crc::GenerateCRCFromString(p_name),assert);
}

bool (*GetCFunc(uint32 checksum, EAssertType assert))(CStruct *, CScript *)
{
	CSymbolTableEntry *p_entry=Resolve(checksum);
	if (!p_entry)
	{
		Dbg_MsgAssert(!assert,("GetCFunc could not find the symbol '%s'. No symbol of any type exists with that name.",FindChecksumName(checksum)));
		return NULL;
	}
		
	switch (p_entry->mType)
	{
		case ESYMBOLTYPE_CFUNCTION:
			Dbg_MsgAssert(p_entry->mpCFunction,("NULL p_entry->pCFunction ?"));
			return p_entry->mpCFunction;
			break;
			
		case ESYMBOLTYPE_MEMBERFUNCTION:
			Dbg_MsgAssert(0,("'%s' is not a C-function, it is a member function.",FindChecksumName(checksum)));
			break;
			
		default:	
			Dbg_MsgAssert(0,("'%s' is not a C-function, but has type '%s'",FindChecksumName(checksum),GetTypeName(p_entry->mType)));
			break;
	}
	
	return NULL;			
}

bool (*GetCFunc(const char *p_name, EAssertType assert))(CStruct *, CScript *)
{
	return GetCFunc(Crc::GenerateCRCFromString(p_name),assert);
}

///////////////////////////////////////////////////////////////////////////////////
// TODO: Remove these next functions at some point.
// They are only included to provide back compatibility with the old code without
// having to change thousands of occurrences.
int GetInt(uint32 checksum, bool assert)
{
	return GetInteger(checksum,assert?ASSERT:NO_ASSERT);
}

int GetInt(const char *p_name, bool assert)
{
	return GetInteger(p_name,assert?ASSERT:NO_ASSERT);
}

bool (*GetCFuncPointer(uint32 checksum, EAssertType assert))(CStruct *, CScript *)
{
	return GetCFunc(checksum, assert);
}

bool (*GetCFuncPointer(const char *p_name, EAssertType assert))(CStruct *, CScript *)
{
	return GetCFunc(p_name, assert);
}
///////////////////////////////////////////////////////////////////////////////////

} // namespace Script



================================================
FILE: Code/Gel/Scripting/symboltable.h
================================================
#ifndef	__SCRIPTING_SYMBOLTABLE_H
#define	__SCRIPTING_SYMBOLTABLE_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

#ifndef	__SCRIPTING_SYMBOLTYPE_H
#include  // For ESymbolType
#endif

#ifndef	__SCRIPTING_SCRIPTDEFS_H
#include  // For EAssertType
#endif

namespace Script
{

// If defined then the usage of each cfunc and member func will be counted on startup
// by scanning through all the scripts.
// Uses a bit of memory so only define on debug builds.
#ifdef __NOPT_ASSERT__
//#define COUNT_USAGE
#endif

#define NUM_HASH_BITS 12

class CPair;
class CVector;
class CArray;
class CStruct;
class CScript;

#ifdef __PLAT_WN32__
class CSymbolTableEntry
#else
class CSymbolTableEntry : public Mem::CPoolable
#endif
{
public:
	// Note: The placement of these bitfielded members is important. The CPoolable class
	// has an overhead of 1 byte, so by putting the small members here they will use up the 3
	// byte padding, keeping the size of this class down. There are 4000 or so of these, so
	// the size needs to be kept as small as possible.
	
	// If a symbol is deleted and recreated by parse.cpp then this flag will get set.
	// This is so that other code can use this to detect whether a symbol has changed due to a reload.
	// For example, file.cpp checks the script mGotReloaded flags after loading a qb, and if reloaded
	// it will then restart all existing CScript's that refer to it so that  they do not crash due to 
	// invalid pointers.
	// Similarly, if a qb gets unloaded any existing CScript's that refer to a script in it must be stopped.
	// Also, in THPS, if the symbol NodeArray gets reloaded then name prefix information must be regenerated.
	//
	uint8 mGotReloaded:1;
	
	// The symbols in the contiguous array use this flag to indicate if they are occupied.
    bool mUsed:1;
	
    uint8 mType;
	
	#ifdef COUNT_USAGE
	uint32 mUsage;
	#endif
	
	// This used to just store the upper checksum bits, because the lower bits are implied
	// by the index of this entries list in the hash table.
	// This was just to save space, because of the fairly large (8000ish) number of symbols.
	// However, it makes the LookUpSymbol function a little bit slower because of the extra bit
	// operations needed to compare, and also it makes the RemoveSymbol function more complex
	// and slower because it has to search the whole symbol table in order to find the previous
	// entry, so that the current one can be unlinked.
	// In skate4, since we have the ability to unload qb's, this should reduce the requirement
	// for such a large symbol table, so I decided to store the whole checksum instead.
	// Simpler, faster and neater! Just uses a wee bit more space. Not a huge amount though,
	// probably only 25K ish.
    uint32 mNameChecksum;
	
	// Required so that all the symbols from a particular a qb can be unloaded.
	// Also handy for debugging.
	// Could perhaps make it a 16-bit index into an array of file names rather than a checksum,
	// then it would pack next to the above mUsed and mType and they would all 3 only use up 4 bytes
	// altogether rather than 8 ...
	uint32 mSourceFileNameChecksum;

    union
    {
        int mIntegerValue;
        float mFloatValue;
        char *mpString;
        char *mpLocalString;
        CPair *mpPair;
        CVector *mpVector;
        CStruct *mpStructure;
#ifdef __PLAT_NGC__
        uint32 mScriptOffset;
#else
        uint8 *mpScript;
#endif		// __PLAT_NGC__
        bool (*mpCFunction)(CStruct *pParams, CScript *pCScript);
        CArray *mpArray;
        uint32 mChecksum; // Used when Type is ESYMBOLTYPE_NAME
		uint32 mUnion; // For when all the above need to be zeroed 
    };

    CSymbolTableEntry *mpNext;

	CSymbolTableEntry();
	#ifdef __NOPT_ASSERT__
	~CSymbolTableEntry();
	#endif

	// These cannot be defined because it would cause a cyclic dependency, because
	// a CSymbolTableEntry member function can't create things. So declare them but leave them undefined
	// so that it will not link if they are attempted to be used.
	CSymbolTableEntry( const CSymbolTableEntry& rhs );
	CSymbolTableEntry& operator=( const CSymbolTableEntry& rhs );
	
	void Reset();
};

void CreateSymbolHashTable();
void DestroySymbolHashTable();
CSymbolTableEntry *LookUpSymbol(uint32 checksum);
CSymbolTableEntry *LookUpSymbol(const char *p_name);
CSymbolTableEntry *Resolve(uint32 checksum);
CSymbolTableEntry *Resolve(const char *p_name);
void RemoveSymbol(CSymbolTableEntry *p_sym);
CSymbolTableEntry *CreateNewSymbolEntry(uint32 checksum);
CSymbolTableEntry *GetNextSymbolTableEntry(CSymbolTableEntry *p_sym=NULL);

float GetFloat(uint32 checksum, EAssertType assert=NO_ASSERT);
float GetFloat(const char *p_name, EAssertType assert=NO_ASSERT);
int GetInteger(uint32 checksum, EAssertType assert=NO_ASSERT);
int GetInteger(const char *p_name, EAssertType assert=NO_ASSERT);
uint32 GetChecksum(uint32 checksum, EAssertType assert=NO_ASSERT);
uint32 GetChecksum(const char *p_name, EAssertType assert=NO_ASSERT);
const char *GetString(uint32 checksum, EAssertType assert=NO_ASSERT);
const char *GetString(const char *p_name, EAssertType assert=NO_ASSERT);
const char *GetLocalString(uint32 checksum, EAssertType assert=NO_ASSERT);
const char *GetLocalString(const char *p_name, EAssertType assert=NO_ASSERT);
CVector *GetVector(uint32 checksum, EAssertType assert=NO_ASSERT);
CVector *GetVector(const char *p_name, EAssertType assert=NO_ASSERT);
CPair *GetPair(uint32 checksum, EAssertType assert=NO_ASSERT);
CPair *GetPair(const char *p_name, EAssertType assert=NO_ASSERT);
CStruct *GetStructure(uint32 checksum, EAssertType assert=NO_ASSERT);
CStruct *GetStructure(const char *p_name, EAssertType assert=NO_ASSERT);
CArray *GetArray(uint32 checksum, EAssertType assert=NO_ASSERT);
CArray *GetArray(const char *p_name, EAssertType assert=NO_ASSERT);
bool (*GetCFunc(uint32 checksum, EAssertType assert=NO_ASSERT))(CStruct *, CScript *);
bool (*GetCFunc(const char *p_name, EAssertType assert=NO_ASSERT))(CStruct *, CScript *);

///////////////////////////////////////////////////////////////////////////////////
// TODO: Remove these next functions at some point.
// They are only included to provide back compatibility with the old code without
// having to change thousands of occurrences of calls to GetInt
int GetInt(uint32 checksum, bool assert=true);
int GetInt(const char *p_name, bool assert=true);
bool (*GetCFuncPointer(uint32 checksum, EAssertType assert=NO_ASSERT))(CStruct *, CScript *);
bool (*GetCFuncPointer(const char *p_name, EAssertType assert=NO_ASSERT))(CStruct *, CScript *);
///////////////////////////////////////////////////////////////////////////////////

} // namespace Script

#endif // #ifndef	__SCRIPTING_SYMBOLTABLE_H



================================================
FILE: Code/Gel/Scripting/symboltype.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// symboltype.cpp		KSH 22 Oct 2001
//
// Just a utility function for printing a type name.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 

namespace Script
{

#ifdef __NOPT_ASSERT__
const char *GetTypeName(uint8 type)
{
	switch (type)
	{
	case ESYMBOLTYPE_NONE:
		return "None";
		break;
	case ESYMBOLTYPE_INTEGER:
		return "Integer";
		break;
    case ESYMBOLTYPE_FLOAT:
		return "Float";
		break;
    case ESYMBOLTYPE_STRING:
		return "String";
		break;
    case ESYMBOLTYPE_LOCALSTRING:
		return "Local String";
		break;
    case ESYMBOLTYPE_PAIR:
		return "Pair";
		break;
    case ESYMBOLTYPE_VECTOR:
		return "Vector";
		break;
    case ESYMBOLTYPE_QSCRIPT:
		return "Script";
		break;
    case ESYMBOLTYPE_CFUNCTION:
		return "C-Function";
		break;
    case ESYMBOLTYPE_MEMBERFUNCTION:
		return "Member Function";
		break;
    case ESYMBOLTYPE_STRUCTURE:
		return "Structure";
		break;
    case ESYMBOLTYPE_STRUCTUREPOINTER:
		return "Structure Pointer";
		break;
    case ESYMBOLTYPE_ARRAY:
		return "Array";
		break;
    case ESYMBOLTYPE_NAME:
		return "Name";
		break;
	default:
		return "Unknown";
		break;		
	}
}
#else
const char *GetTypeName(uint8 type)
{
	return "(Hey, don't use GetTypeName on the CD!)";
}
#endif

} // namespace Script



================================================
FILE: Code/Gel/Scripting/symboltype.h
================================================
#ifndef	__SCRIPTING_SYMBOLTYPE_H
#define	__SCRIPTING_SYMBOLTYPE_H

namespace Script
{
// Values for CSymbolTableEntry::mType and CComponent::mType
enum ESymbolType
{
    ESYMBOLTYPE_NONE=0,
    ESYMBOLTYPE_INTEGER,
    ESYMBOLTYPE_FLOAT,
    ESYMBOLTYPE_STRING,
    ESYMBOLTYPE_LOCALSTRING,
    ESYMBOLTYPE_PAIR,
    ESYMBOLTYPE_VECTOR,
    ESYMBOLTYPE_QSCRIPT,
    ESYMBOLTYPE_CFUNCTION,
    ESYMBOLTYPE_MEMBERFUNCTION,
    ESYMBOLTYPE_STRUCTURE,
	// ESYMBOLTYPE_STRUCTUREPOINTER is not really used any more. It is only supported as a valid
	// type that can be sent to AddComponent, which is an old CStruct member function that is
	// still supported for back compatibility. mType will never be ESYMBOLTYPE_STRUCTUREPOINTER
    ESYMBOLTYPE_STRUCTUREPOINTER,
    ESYMBOLTYPE_ARRAY,
    ESYMBOLTYPE_NAME,
	
	// These symbols are just used for memory optimization by the
	// CScriptStructure WriteToBuffer and ReadFromBuffer functions.
	ESYMBOLTYPE_INTEGER_ONE_BYTE,
	ESYMBOLTYPE_INTEGER_TWO_BYTES,
	ESYMBOLTYPE_UNSIGNED_INTEGER_ONE_BYTE,
	ESYMBOLTYPE_UNSIGNED_INTEGER_TWO_BYTES,
	ESYMBOLTYPE_ZERO_INTEGER,
	ESYMBOLTYPE_ZERO_FLOAT,
	
	// Warning! Don't exceed 256 entries, since Type is a uint8 in SSymbolTableEntry
	// New warning! Don't exceed 64 entries, because the top two bits of the symbol
	// type are used to indicate whether the name checksum has been compressed to
	// a 8 or 16 bit index, when WriteToBuffer writes out parameter names.
};

// These get masked onto the symbol type in CScriptStructure::WriteToBuffer if
// the following name checksum matches one in the lookup table. (Defined in compress.q)
#define MASK_8_BIT_NAME_LOOKUP (1<<7)
#define MASK_16_BIT_NAME_LOOKUP (1<<6)

const char *GetTypeName(uint8 type);

// TODO: Remove these at some point, they are just to allow compilation of the existing game code
// without having to change all the occurrences.
#define ESYMBOLTYPE_NONE                       Script::ESYMBOLTYPE_NONE                      
#define ESYMBOLTYPE_INTEGER                    Script::ESYMBOLTYPE_INTEGER                   
#define ESYMBOLTYPE_FLOAT                      Script::ESYMBOLTYPE_FLOAT                     
#define ESYMBOLTYPE_STRING                     Script::ESYMBOLTYPE_STRING                    
#define ESYMBOLTYPE_LOCALSTRING                Script::ESYMBOLTYPE_LOCALSTRING               
#define ESYMBOLTYPE_PAIR                       Script::ESYMBOLTYPE_PAIR                      
#define ESYMBOLTYPE_VECTOR                     Script::ESYMBOLTYPE_VECTOR                    
#define ESYMBOLTYPE_QSCRIPT                    Script::ESYMBOLTYPE_QSCRIPT                   
#define ESYMBOLTYPE_CFUNCTION                  Script::ESYMBOLTYPE_CFUNCTION                 
#define ESYMBOLTYPE_MEMBERFUNCTION             Script::ESYMBOLTYPE_MEMBERFUNCTION            
#define ESYMBOLTYPE_STRUCTURE                  Script::ESYMBOLTYPE_STRUCTURE                 
#define ESYMBOLTYPE_STRUCTUREPOINTER           Script::ESYMBOLTYPE_STRUCTUREPOINTER          
#define ESYMBOLTYPE_ARRAY                      Script::ESYMBOLTYPE_ARRAY                     
#define ESYMBOLTYPE_NAME                       Script::ESYMBOLTYPE_NAME                      
#define ESYMBOLTYPE_INTEGER_ONE_BYTE           Script::ESYMBOLTYPE_INTEGER_ONE_BYTE          
#define ESYMBOLTYPE_INTEGER_TWO_BYTES          Script::ESYMBOLTYPE_INTEGER_TWO_BYTES         
#define ESYMBOLTYPE_UNSIGNED_INTEGER_ONE_BYTE  Script::ESYMBOLTYPE_UNSIGNED_INTEGER_ONE_BYTE 
#define ESYMBOLTYPE_UNSIGNED_INTEGER_TWO_BYTES Script::ESYMBOLTYPE_UNSIGNED_INTEGER_TWO_BYTES
#define ESYMBOLTYPE_ZERO_INTEGER               Script::ESYMBOLTYPE_ZERO_INTEGER              
#define ESYMBOLTYPE_ZERO_FLOAT                 Script::ESYMBOLTYPE_ZERO_FLOAT                



}

#endif // #ifndef	__SCRIPTING_SYMBOLTYPE_H


================================================
FILE: Code/Gel/Scripting/tokens.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// tokens.cpp		KSH 22 Oct 2001
//
// Just a function for getting a token name.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#ifndef __CORE_DEFINES_H
#include  // For __NOPT_ASSERT__
#endif

namespace Script
{

#ifdef __NOPT_ASSERT__
const char *GetTokenName(EScriptToken token)
{
	switch (token)
	{
	case ESCRIPTTOKEN_ENDOFFILE:
		return "ENDOFFILE";
		break;
	case ESCRIPTTOKEN_ENDOFLINE:
		return "ENDOFLINE";
		break;
	case ESCRIPTTOKEN_ENDOFLINENUMBER:
		return "ENDOFLINENUMBER";
		break;
	case ESCRIPTTOKEN_STARTSTRUCT:
		return "{";
		break;
	case ESCRIPTTOKEN_ENDSTRUCT:
		return "}";
		break;
	case ESCRIPTTOKEN_STARTARRAY:
		return "[";
		break;
	case ESCRIPTTOKEN_ENDARRAY:
		return "]";
		break;
	case ESCRIPTTOKEN_EQUALS:
		return "=";
		break;
	case ESCRIPTTOKEN_DOT:
		return ".";
		break;
	case ESCRIPTTOKEN_COMMA:
		return ",";
		break;
	case ESCRIPTTOKEN_MINUS:
		return "-";
		break;
	case ESCRIPTTOKEN_ADD:
		return "+";
		break;
	case ESCRIPTTOKEN_DIVIDE:
		return "/";
		break;
	case ESCRIPTTOKEN_MULTIPLY:
		return "*";
		break;
	case ESCRIPTTOKEN_OPENPARENTH:
		return "(";
		break;
	case ESCRIPTTOKEN_CLOSEPARENTH:
		return ")";
		break;
	case ESCRIPTTOKEN_DEBUGINFO:
		return "DEBUGINFO";
		break;
	case ESCRIPTTOKEN_SAMEAS:
		return "==";
		break;
	case ESCRIPTTOKEN_LESSTHAN:
		return "<";
		break;
	case ESCRIPTTOKEN_LESSTHANEQUAL:
		return "<=";
		break;
	case ESCRIPTTOKEN_GREATERTHAN:
		return ">";
		break;
	case ESCRIPTTOKEN_GREATERTHANEQUAL:
		return ">=";
		break;
	case ESCRIPTTOKEN_NAME:
		return "NAME";
		break;
	case ESCRIPTTOKEN_INTEGER:
		return "INTEGER";
		break;
	case ESCRIPTTOKEN_HEXINTEGER:
		return "HEXINTEGER";
		break;
    case ESCRIPTTOKEN_ENUM:
		return "ENUM";
		break;
	case ESCRIPTTOKEN_FLOAT:
		return "FLOAT";
		break;
	case ESCRIPTTOKEN_STRING:
		return "STRING";
		break;
	case ESCRIPTTOKEN_LOCALSTRING:
		return "LOCALSTRING";
		break;
	case ESCRIPTTOKEN_ARRAY:
		return "ARRAY";
		break;
	case ESCRIPTTOKEN_VECTOR:
		return "VECTOR";
		break;
	case ESCRIPTTOKEN_PAIR:
		return "PAIR";
		break;
	case ESCRIPTTOKEN_KEYWORD_BEGIN:
		return "BEGIN";
		break;
	case ESCRIPTTOKEN_KEYWORD_REPEAT:
		return "REPEAT";
		break;
	case ESCRIPTTOKEN_KEYWORD_BREAK:
		return "BREAK";
		break;
	case ESCRIPTTOKEN_KEYWORD_SCRIPT:
		return "SCRIPT";
		break;
	case ESCRIPTTOKEN_KEYWORD_ENDSCRIPT:
		return "ENDSCRIPT";
		break;
	case ESCRIPTTOKEN_KEYWORD_IF:
		return "IF";
		break;
	case ESCRIPTTOKEN_KEYWORD_ELSE:
		return "ELSE";
		break;
	case ESCRIPTTOKEN_KEYWORD_ELSEIF:
		return "ELSEIF";
		break;
	case ESCRIPTTOKEN_KEYWORD_ENDIF:
		return "ENDIF";
		break;
	case ESCRIPTTOKEN_KEYWORD_RETURN:
		return "RETURN";
		break;
	case ESCRIPTTOKEN_KEYWORD_NOT:
		return "NOT";
		break;
	case ESCRIPTTOKEN_KEYWORD_SWITCH:
		return "SWITCH";
		break;
	case ESCRIPTTOKEN_KEYWORD_ENDSWITCH:
		return "ENDSWITCH";
		break;
	case ESCRIPTTOKEN_KEYWORD_CASE:
		return "CASE";
		break;
	case ESCRIPTTOKEN_KEYWORD_DEFAULT:
		return "DEFAULT";
		break;
    case ESCRIPTTOKEN_UNDEFINED:
		return "UNDEFINED";
		break;
	case ESCRIPTTOKEN_CHECKSUM_NAME:
		return "CHECKSUM_NAME";
		break;
	case ESCRIPTTOKEN_KEYWORD_ALLARGS:
		return "<...>";
		break;
	case ESCRIPTTOKEN_ARG:	
		return "ARG";
		break;
		
	case ESCRIPTTOKEN_KEYWORD_RANDOM:
		return "RANDOM";
		break;

	case ESCRIPTTOKEN_KEYWORD_RANDOM2:
		return "RANDOM2";
		break;
		
	case ESCRIPTTOKEN_KEYWORD_RANDOM_NO_REPEAT:
		return "RANDOM_NO_REPEAT";
		break;

	case ESCRIPTTOKEN_KEYWORD_RANDOM_PERMUTE:
		return "RANDOM_PERMUTE";
		break;
		
	case ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE:
		return "RANDOM_RANGE";
		break;

	case ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE2:
		return "RANDOM_RANGE2";
		break;
			
	case ESCRIPTTOKEN_OR:
		return "OR";
		break;
	case ESCRIPTTOKEN_AND:
		return "AND";
		break;
	case ESCRIPTTOKEN_XOR:
		return "XOR";
		break;
	
	case ESCRIPTTOKEN_SHIFT_LEFT:
		return "SHIFT_LEFT";
		break;
	case ESCRIPTTOKEN_SHIFT_RIGHT:
		return "SHIFT_RIGHT";
		break;
		
	case ESCRIPTTOKEN_COLON:
		return "COLON";
		break;
		
	case ESCRIPTTOKEN_RUNTIME_CFUNCTION:
		return "RUNTIME-CFUNCTION";
		break;
		
	case ESCRIPTTOKEN_RUNTIME_MEMBERFUNCTION:
		return "RUNTIME-MEMBERFUNCTION";
		break;
			
	default:
		return "Unknown";
		break;
	}		
}
#endif // #ifdef __NOPT_ASSERT__

} // namespace Script



================================================
FILE: Code/Gel/Scripting/tokens.h
================================================

// This file gets included in the compiler tool code (qcomp.cpp) as well as
// in the PS2 code. So whenever this file is modified, qcomp must be recompiled.
// qcomp.exe is a PC executable that converts a .q file into a .qb file
// for loading into the PS2.

// The following are token values used to represent the various things in
// the .qb.

#ifndef __SCRIPTING_TOKENS_H
#define __SCRIPTING_TOKENS_H

namespace Script
{
// Note! Careful if inserting new token values here, because this file also gets
// included in the qcomp tool.
// If the numerical values of the symbols here change, all the .qb's must be
// recompiled. This won't automatically happen, so everyone will have to be
// told to delete their .qb's to force them to be recompiled.

// When adding a new token value, it would be best to add it to the bottom, to
// prevent having to tell everyone to recompile all the qb's.
enum EScriptToken
{
	// Misc
	ESCRIPTTOKEN_ENDOFFILE,			// 0
	ESCRIPTTOKEN_ENDOFLINE,			// 1
	ESCRIPTTOKEN_ENDOFLINENUMBER,   // 2
	ESCRIPTTOKEN_STARTSTRUCT,       // 3
	ESCRIPTTOKEN_ENDSTRUCT,         // 4
	ESCRIPTTOKEN_STARTARRAY,        // 5
	ESCRIPTTOKEN_ENDARRAY,          // 6
	ESCRIPTTOKEN_EQUALS,            // 7
	ESCRIPTTOKEN_DOT,               // 8
	ESCRIPTTOKEN_COMMA,             // 9
	ESCRIPTTOKEN_MINUS,             // 10
	ESCRIPTTOKEN_ADD,               // 11
	ESCRIPTTOKEN_DIVIDE,            // 12
	ESCRIPTTOKEN_MULTIPLY,          // 13
	ESCRIPTTOKEN_OPENPARENTH,       // 14
	ESCRIPTTOKEN_CLOSEPARENTH,      // 15

	// This is ignored by the interpreter.
	// Allows inclusion of source level debugging info, eg line number.
	ESCRIPTTOKEN_DEBUGINFO,			// 16

	// Comparisons
	ESCRIPTTOKEN_SAMEAS,			// 17
	ESCRIPTTOKEN_LESSTHAN,			// 18
	ESCRIPTTOKEN_LESSTHANEQUAL,     // 19
	ESCRIPTTOKEN_GREATERTHAN,       // 20
	ESCRIPTTOKEN_GREATERTHANEQUAL,  // 21

	// Types
	ESCRIPTTOKEN_NAME,				// 22
	ESCRIPTTOKEN_INTEGER,			// 23
	ESCRIPTTOKEN_HEXINTEGER,        // 24
    ESCRIPTTOKEN_ENUM,              // 25
	ESCRIPTTOKEN_FLOAT,             // 26
	ESCRIPTTOKEN_STRING,            // 27
	ESCRIPTTOKEN_LOCALSTRING,       // 28
	ESCRIPTTOKEN_ARRAY,             // 29
	ESCRIPTTOKEN_VECTOR,            // 30
	ESCRIPTTOKEN_PAIR,				// 31

	// Key words
	ESCRIPTTOKEN_KEYWORD_BEGIN,		// 32
	ESCRIPTTOKEN_KEYWORD_REPEAT,    // 33
	ESCRIPTTOKEN_KEYWORD_BREAK,     // 34
	ESCRIPTTOKEN_KEYWORD_SCRIPT,    // 35
	ESCRIPTTOKEN_KEYWORD_ENDSCRIPT, // 36
	ESCRIPTTOKEN_KEYWORD_IF,        // 37
	ESCRIPTTOKEN_KEYWORD_ELSE,      // 38
	ESCRIPTTOKEN_KEYWORD_ELSEIF,    // 39
	ESCRIPTTOKEN_KEYWORD_ENDIF,		// 40
	ESCRIPTTOKEN_KEYWORD_RETURN,	// 41
    
    ESCRIPTTOKEN_UNDEFINED,			// 42
	
	// For debugging					  
	ESCRIPTTOKEN_CHECKSUM_NAME,		// 43
	
	// Token for the <...> symbol					
	ESCRIPTTOKEN_KEYWORD_ALLARGS,	// 44
	// Token that preceds a name when the name is enclosed in < > in the source.
	ESCRIPTTOKEN_ARG,				// 45
	
	// A relative jump. Used to speed up if-else-endif and break statements, and
	// used to jump to the end of lists of items in the random operator.
	ESCRIPTTOKEN_JUMP,				// 46
	// Precedes a list of items that are to be randomly chosen from.
	ESCRIPTTOKEN_KEYWORD_RANDOM,    // 47

	// Precedes two integers enclosed in parentheses.
	ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE,	// 48
	
	// Only used internally by qcomp, never appears in a .qb
	ESCRIPTTOKEN_AT,				// 49
	
	// Logical operators
	ESCRIPTTOKEN_OR,				// 50
	ESCRIPTTOKEN_AND,				// 51
	ESCRIPTTOKEN_XOR,				// 52
									
	// Shift operators
	ESCRIPTTOKEN_SHIFT_LEFT,		// 53
	ESCRIPTTOKEN_SHIFT_RIGHT,		// 54
	
	// These versions use the Rnd2 function, for use in certain things so as not to mess up
	// the determinism of the regular Rnd function in replays.
	ESCRIPTTOKEN_KEYWORD_RANDOM2,		// 55
	ESCRIPTTOKEN_KEYWORD_RANDOM_RANGE2, // 56
	
	ESCRIPTTOKEN_KEYWORD_NOT,			// 57
	ESCRIPTTOKEN_KEYWORD_AND,			// 58
	ESCRIPTTOKEN_KEYWORD_OR,            // 59
	ESCRIPTTOKEN_KEYWORD_SWITCH,       	// 60
	ESCRIPTTOKEN_KEYWORD_ENDSWITCH,   	// 61
	ESCRIPTTOKEN_KEYWORD_CASE,          // 62
	ESCRIPTTOKEN_KEYWORD_DEFAULT,		// 63

	ESCRIPTTOKEN_KEYWORD_RANDOM_NO_REPEAT,	// 64
	ESCRIPTTOKEN_KEYWORD_RANDOM_PERMUTE,	// 65

	ESCRIPTTOKEN_COLON,		// 66
	
	// These are calculated at runtime in the game code by PreProcessScripts,
	// so they never appear in a qb file.
	ESCRIPTTOKEN_RUNTIME_CFUNCTION,	// 67
	ESCRIPTTOKEN_RUNTIME_MEMBERFUNCTION, // 68
	
	// Warning! Do not exceed 256 entries, since these are stored in bytes.
};

const char *GetTokenName(EScriptToken token);

} // namespace Script

#endif // #ifndef __SCRIPTING_TOKENS_H



================================================
FILE: Code/Gel/Scripting/utils.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// utils.cpp		KSH 22 Oct 2001
//
// Misc script utility functions.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// Some defines that affect how PrintContents works:

// If defined, then if a structure contains a reference to a global structure, PrintContents
// will print the contents of that structure too.
// (Added this define so that it can be disabled for viewing the mem card career-save structure,
// which has lots of references and takes ages to print if they are all expanded)
#define EXPAND_GLOBAL_STRUCTURE_REFERENCES

// Enables a for-loop to slow down PrintContents so that printf can catch up.
// (Needed for viewing big mem card structures)
//#define SLOW_DOWN_PRINTCONTENTS

namespace Script
{

static uint8 *sWriteCompressedName(uint8 *p_buffer, uint8 SymbolType, uint32 NameChecksum);
static uint32 sIntegerWriteToBuffer(uint32 Name, int val, uint8 *p_buffer, uint32 BufferSize);
static uint32 sFloatWriteToBuffer(uint32 Name, float val, uint8 *p_buffer, uint32 BufferSize);
static uint32 sChecksumWriteToBuffer(uint32 Name, uint32 Checksum, uint8 *p_buffer, uint32 BufferSize);
static uint32 sStringWriteToBuffer(uint32 Name, const char *pString, uint8 *p_buffer, uint32 BufferSize);
static uint32 sLocalStringWriteToBuffer(uint32 Name, const char *pString, uint8 *p_buffer, uint32 BufferSize);
static uint32 sPairWriteToBuffer(uint32 Name, CPair *pPair, uint8 *p_buffer, uint32 BufferSize);
static uint32 sVectorWriteToBuffer(uint32 Name, CVector *pVector, uint8 *p_buffer, uint32 BufferSize);
static uint32 sStructureWriteToBuffer(uint32 Name, CStruct *pStructure, uint8 *p_buffer, uint32 BufferSize, EAssertType assert);
static uint32 sArrayWriteToBuffer(uint32 Name, CArray *pArray, uint8 *p_buffer, uint32 BufferSize, EAssertType assert);

#ifdef __NOPT_ASSERT__
static void   sDoIndent(int indent);

static void sDoIndent(int indent)
{
	for (int i=0; iGetSize();
	ESymbolType type=p_array->GetType();
	
	if (!indent)
	{
		printf("Contents of Array=\n");
	}
	
	sDoIndent(indent);
	printf("[\n");
	indent+=3;
	
	int int_column_count=0;
	for (int i=0; iGetInteger(i));
			}
			else
			{
				printf("%d\n",p_array->GetInteger(i));
			}	
			++int_column_count;
			if (int_column_count==15)
			{
				printf("\n");
				int_column_count=0;
			}
            break;
        case ESYMBOLTYPE_FLOAT:
            printf("%f\n",p_array->GetFloat(i));
            break;
        case ESYMBOLTYPE_STRING:
		{
			const char *p_string=p_array->GetString(i);
			if (p_string)
			{
				printf("\"%s\"\n",p_string);
			}	
			else
			{
				printf("NULL string\n");
			}	
            break;
		}	
        case ESYMBOLTYPE_LOCALSTRING:
		{
			const char *p_string=p_array->GetLocalString(i);
			if (p_string)
			{
				printf("'%s'\n",p_string);
			}	
			else
			{
				printf("NULL local string\n");
			}	
            break;
		}	
        case ESYMBOLTYPE_PAIR:
		{
			CPair *p_pair=p_array->GetPair(i);
			if (p_pair)
			{
				printf("(%f,%f)\n",p_pair->mX,p_pair->mY);
			}	
			else
			{
				printf("NULL pair\n");
			}	
            break;
		}	
        case ESYMBOLTYPE_VECTOR:
		{
			CVector *p_vector=p_array->GetVector(i);
			if (p_vector)
			{
				printf("(%f,%f,%f)\n",p_vector->mX,p_vector->mY,p_vector->mZ);
			}	
			else
			{
				printf("NULL vector\n");
			}	
            break;
		}	
        case ESYMBOLTYPE_NAME:
            printf("%s\n",FindChecksumName(p_array->GetChecksum(i)));
            break;
        case ESYMBOLTYPE_STRUCTURE:
		{
			CStruct *p_structure=p_array->GetStructure(i);
			if (p_structure)
			{
				PrintContents(p_structure,indent);
			}	
			else
			{
				printf("NULL structure\n");
			}	
            break;
		}	
		case ESYMBOLTYPE_ARRAY:
		{
			CArray *p_a=p_array->GetArray(i);
			if (p_a)
			{
				PrintContents(p_a,indent);
			}	
			else
			{
				printf("NULL array\n");
			}	
            break;
		}	
			
        default:
            Dbg_MsgAssert(0,("Bad array type"));
            break;
        }

		#ifdef SLOW_DOWN_PRINTCONTENTS
		// A delay to let printf catch up so that it doesn't skip stuff when printing big arrays.		
		for (int i=0; i<10000; ++i);
		#endif
    }
    sDoIndent(indent-3);
	printf("]\n");
#endif	
}

void PrintContents(const CStruct *p_structure, int indent)
{
#ifdef __NOPT_ASSERT__
	Dbg_MsgAssert(p_structure,("NULL p_structure"));
	
	if (!indent)
	{
		printf("Contents of CStruct=\n");
	}
	
	sDoIndent(indent);
	printf("{\n");
	indent+=3;
		
    CComponent *p_comp=p_structure->GetNextComponent(NULL);
    while (p_comp)
    {
		sDoIndent(indent);
		
		if (p_comp->mNameChecksum)
		{
			printf("%s=",FindChecksumName(p_comp->mNameChecksum));
		}	
            
        switch (p_comp->mType)
        {
        case ESYMBOLTYPE_INTEGER:
            printf("%d\n",p_comp->mIntegerValue);
            break;
        case ESYMBOLTYPE_FLOAT:
            printf("%f\n",p_comp->mFloatValue);
            break;
        case ESYMBOLTYPE_STRING:
            printf("\"%s\"\n",p_comp->mpString);
            break;
        case ESYMBOLTYPE_LOCALSTRING:
            printf("'%s'\n",p_comp->mpLocalString);
            break;
        case ESYMBOLTYPE_PAIR:
            printf("(%f,%f)\n",p_comp->mpPair->mX,p_comp->mpPair->mY);
            break;
        case ESYMBOLTYPE_VECTOR:
            printf("(%f,%f,%f)\n",p_comp->mpVector->mX,p_comp->mpVector->mY,p_comp->mpVector->mZ);
            break;
        case ESYMBOLTYPE_STRUCTURE:
			printf("\n");
			PrintContents(p_comp->mpStructure,indent);
            break;
        case ESYMBOLTYPE_NAME:
            printf("%s\n",FindChecksumName(p_comp->mChecksum));
			
			#ifdef EXPAND_GLOBAL_STRUCTURE_REFERENCES
			if (p_comp->mNameChecksum==0)
			{
				// It's an un-named name. Maybe it's a global structure ... 
				// If so, print its contents too, which is handy for debugging.
			    CSymbolTableEntry *p_entry=Resolve(p_comp->mChecksum);
				if (p_entry && p_entry->mType==ESYMBOLTYPE_STRUCTURE)
				{
					sDoIndent(indent);
					printf("... Defined in %s ...\n",FindChecksumName(p_entry->mSourceFileNameChecksum));
					Dbg_MsgAssert(p_entry->mpStructure,("NULL p_entry->mpStructure"));
					PrintContents(p_entry->mpStructure,indent);
				}
			}		
			#endif
            break;
        case ESYMBOLTYPE_QSCRIPT:
			printf("(A script)\n"); // TODO
			break;
		case ESYMBOLTYPE_ARRAY:
			printf("\n");
			PrintContents(p_comp->mpArray,indent);
            break;
        default:
			printf("Component of type '%s', value 0x%08x\n",GetTypeName(p_comp->mType),p_comp->mUnion);
            //Dbg_MsgAssert(0,("Bad p_comp->Type"));
            break;
        }
        p_comp=p_structure->GetNextComponent(p_comp);
		
		#ifdef SLOW_DOWN_PRINTCONTENTS
		// A delay to let printf catch up so that it doesn't skip stuff when printing big arrays.		
		for (int i=0; i<1000000; ++i);
		#endif
    }
	
    sDoIndent(indent-3);
	printf("}\n");
#endif
}

static uint8 *sWriteCompressedName(uint8 *p_buffer, uint8 symbolType, uint32 nameChecksum)
{
	#ifdef __PLAT_WN32__
	// If compiling on PC, the lookup table will not exist, so do no compression.
	*p_buffer++=symbolType;
	return Write4Bytes(p_buffer,nameChecksum);
	#else
	
	// Check if the checksum is in the small array.
	CArray *p_table=GetArray(0x35115a20/*WriteToBuffer_CompressionLookupTable_8*/);
	int size=p_table->GetSize();
	Dbg_MsgAssert(size<256,("Size of WriteToBuffer_CompressionLookupTable_8 too big"));
	
	for (int i=0; iGetChecksum(i)==nameChecksum)
		{
			// It is in the array! So write out a 1 byte index.
			*p_buffer++=symbolType | MASK_8_BIT_NAME_LOOKUP;
			*p_buffer++=i;
			return p_buffer;
		}
	}		
	
	// Check if the checksum is in the big array.
	p_table=GetArray(0x25231f42/*WriteToBuffer_CompressionLookupTable_16*/);
	size=p_table->GetSize();
	Dbg_MsgAssert(size<65536,("Size of WriteToBuffer_CompressionLookupTable_16 too big"));
	
	for (int i=0; iGetChecksum(i)==nameChecksum)
		{
			// It is in the array! So write out a 2 byte index.
			*p_buffer++=symbolType | MASK_16_BIT_NAME_LOOKUP;
			return Write2Bytes(p_buffer,i);
		}
	}		
	
	// Oh well, it is not in either array, so write out the whole 4 byte checksum.
	*p_buffer++=symbolType;
	return Write4Bytes(p_buffer,nameChecksum);
	#endif
}

static uint32 sIntegerWriteToBuffer(uint32 name, int val, uint8 *p_buffer, uint32 bufferSize)
{
	uint8 *p_buffer_before=p_buffer;
		
	// Choose what type of symbol to use so as to minimize the space used.
	
	ESymbolType symbol_type=ESYMBOLTYPE_INTEGER; // Default to a full 4 bytes.
	
	if (val==0)
	{
		symbol_type=ESYMBOLTYPE_ZERO_INTEGER;
	}
	else if (val>=0 && val<=255)
	{
		symbol_type=ESYMBOLTYPE_UNSIGNED_INTEGER_ONE_BYTE;
	}
	else if (val>=0 && val<=65535)	
	{
		symbol_type=ESYMBOLTYPE_UNSIGNED_INTEGER_TWO_BYTES;
	}
	else if (val>=-128 && val<=127)
	{
		symbol_type=ESYMBOLTYPE_INTEGER_ONE_BYTE;
	}
	else if (val>=-32768 && val<=32767)
	{
		symbol_type=ESYMBOLTYPE_INTEGER_TWO_BYTES;
	}

	
	uint8 p_temp[20];
	uint8 *p_end=p_temp;
	 
	switch (symbol_type)
	{
		case ESYMBOLTYPE_INTEGER:
			// Write out the component into p_temp
			p_end=sWriteCompressedName(p_temp,symbol_type,name);
			p_end=Write4Bytes(p_end,(uint32)val);
			break;
		
		case ESYMBOLTYPE_INTEGER_ONE_BYTE:
		case ESYMBOLTYPE_UNSIGNED_INTEGER_ONE_BYTE:
			// Write out the component into p_temp
			p_end=sWriteCompressedName(p_temp,symbol_type,name);
			*p_end++=(uint8)val;
			break;
			
		case ESYMBOLTYPE_INTEGER_TWO_BYTES:
		case ESYMBOLTYPE_UNSIGNED_INTEGER_TWO_BYTES:
			// Write out the component.
			p_end=sWriteCompressedName(p_temp,symbol_type,name);
			p_end=Write2Bytes(p_end,(uint16)val);
			break;
			
		case ESYMBOLTYPE_ZERO_INTEGER:
			// Write out the component.
			p_end=sWriteCompressedName(p_temp,symbol_type,name);
			break;
			
		default:
			Dbg_MsgAssert(0,("Bad symbol_type"));
			break;
	}

	uint32 bytes_written_to_temp=p_end-p_temp;
	
	// Check that there is enough space before doing any writing.
	if (bufferSize < bytes_written_to_temp)
	{
		return 0;
	}

	uint8 *p_source=p_temp;
	for (uint32 i=0; imX);
	p_buffer=Write4Bytes(p_buffer,p_pair->mY);
	
	return p_buffer-p_buffer_before;
}

static uint32 sVectorWriteToBuffer(uint32 name, CVector *p_vector, uint8 *p_buffer, uint32 bufferSize)
{
	Dbg_MsgAssert(p_vector,("NULL p_vector sent to sVectorWriteToBuffer"));
	uint8 *p_buffer_before=p_buffer;
	
	// Check that there is enough space before doing any writing.
	if (bufferSize<17)
	{
		return 0;
	}
		
	// Write out the component.
	p_buffer=sWriteCompressedName(p_buffer,ESYMBOLTYPE_VECTOR,name);
	p_buffer=Write4Bytes(p_buffer,p_vector->mX);
	p_buffer=Write4Bytes(p_buffer,p_vector->mY);
	p_buffer=Write4Bytes(p_buffer,p_vector->mZ);
	
	return p_buffer-p_buffer_before;
}

static uint32 sStructureWriteToBuffer(uint32 name, CStruct *p_structure, uint8 *p_buffer, uint32 bufferSize, EAssertType assert)
{
	Dbg_MsgAssert(p_structure,("NULL p_structure sent to sStructureWriteToBuffer"));
	uint8 *p_buffer_before=p_buffer;
	
	// Check that there is enough space before doing any writing.
	if (bufferSize<5)
	{
		return 0;
	}
		
	// Write out the type and name
	p_buffer=sWriteCompressedName(p_buffer,ESYMBOLTYPE_STRUCTURE,name);
	int name_bytes_written=p_buffer-p_buffer_before;
	
	// That's name_bytes_written bytes written out successfully, but maybe the writing out of the structure will fail ...
	
	uint32 structure_bytes_written=WriteToBuffer(p_structure,p_buffer,bufferSize-name_bytes_written,assert);
	// If writing out the structure failed, return 0.
	if (!structure_bytes_written)
	{
		return 0;
	}
	
	// Otherwise return the total bytes written.
	return name_bytes_written+structure_bytes_written;
}

static uint32 sArrayWriteToBuffer(uint32 name, CArray *p_array, uint8 *p_buffer, uint32 bufferSize, EAssertType assert)
{
	Dbg_MsgAssert(p_array,("NULL p_array sent to sArrayWriteToBuffer"));
	uint8 *p_buffer_before=p_buffer;
	
	// Check that there is enough space before doing any writing.
	if (bufferSize<5)
	{
		return 0;
	}

	// Write out the type and name
	p_buffer=sWriteCompressedName(p_buffer,ESYMBOLTYPE_ARRAY,name);
	int name_bytes_written=p_buffer-p_buffer_before;
	
	// That's name_bytes_written bytes written out successfully, but maybe the writing out of the array will fail ...
	
	uint32 array_bytes_written=WriteToBuffer(p_array,p_buffer,bufferSize-name_bytes_written,assert);
	// If writing out the array failed, return 0.
	if (!array_bytes_written)
	{
		return 0;
	}
	
	// Otherwise return the total bytes written.
	return name_bytes_written+array_bytes_written;
}	

// Writes the contents of the structure to a buffer.
// The information is outputted in a byte-packed format, so p_buffer does
// not need to be aligned.
// The buffer can then be used to regenerate the original structure by
// passing it to ReadFromBuffer.
// Passed the size of the buffer so that it can check if there is enough space.
// Returns the number of bytes that it actually wrote.
//
// If there was not enough space, and assert is NO_ASSERT, it will return a count of 0.
//
uint32 WriteToBuffer(CStruct *p_structure, uint8 *p_buffer, uint32 bufferSize, EAssertType assert)
{
	Dbg_MsgAssert(p_buffer,("NULL p_buffer sent to WriteToBuffer"));
	uint32 bytes_left=bufferSize;

	// Scan through the components adding each to the buffer.	
    CComponent *p_comp=NULL;
	if (p_structure)
	{
		p_comp=p_structure->GetNextComponent(NULL);
	}
		
    while (p_comp)
    {
		uint32 component_bytes_written=0;
		
        switch (p_comp->mType)
        {
        case ESYMBOLTYPE_INTEGER:
			component_bytes_written=sIntegerWriteToBuffer(p_comp->mNameChecksum,p_comp->mIntegerValue,p_buffer,bytes_left);
			break;
			
        case ESYMBOLTYPE_FLOAT:
			component_bytes_written=sFloatWriteToBuffer(p_comp->mNameChecksum,p_comp->mFloatValue,p_buffer,bytes_left);
			break;

        case ESYMBOLTYPE_NAME:
			component_bytes_written=sChecksumWriteToBuffer(p_comp->mNameChecksum,p_comp->mChecksum,p_buffer,bytes_left);
			break;
			
        case ESYMBOLTYPE_STRING:
			component_bytes_written=sStringWriteToBuffer(p_comp->mNameChecksum,p_comp->mpString,p_buffer,bytes_left);
			break;

        case ESYMBOLTYPE_LOCALSTRING:
			component_bytes_written=sLocalStringWriteToBuffer(p_comp->mNameChecksum,p_comp->mpLocalString,p_buffer,bytes_left);
			break;
			
        case ESYMBOLTYPE_PAIR:
			component_bytes_written=sPairWriteToBuffer(p_comp->mNameChecksum,p_comp->mpPair,p_buffer,bytes_left);
			break;
			
        case ESYMBOLTYPE_VECTOR:
			component_bytes_written=sVectorWriteToBuffer(p_comp->mNameChecksum,p_comp->mpVector,p_buffer,bytes_left);
			break;

        case ESYMBOLTYPE_STRUCTURE:
			component_bytes_written=sStructureWriteToBuffer(p_comp->mNameChecksum,p_comp->mpStructure,p_buffer,bytes_left,assert);
			break;

        case ESYMBOLTYPE_ARRAY:
			component_bytes_written=sArrayWriteToBuffer(p_comp->mNameChecksum,p_comp->mpArray,p_buffer,bytes_left,assert);
			break;
			
		case ESYMBOLTYPE_QSCRIPT:	
			component_bytes_written=sChecksumWriteToBuffer(p_comp->mNameChecksum,CRCD(0xb9c4f664,"InlineScript"),p_buffer,bytes_left);
            break;
			
        default:
            Dbg_MsgAssert(0,("Component type of '%s' is not supported in WriteToBuffer",GetTypeName(p_comp->mType)));
            break;
        }
		
		// If any of the above writes failed due to lack of space, then bail out too.
		if (!component_bytes_written)
		{
			if (assert)
			{
				Dbg_MsgAssert(0,("WriteToBuffer: Buffer not big enough"));
			}	
			return 0;
		}
			
		bytes_left-=component_bytes_written;
		p_buffer+=component_bytes_written;
		
        p_comp=p_structure->GetNextComponent(p_comp);
    }

	// Add the terminating ESYMBOLTYPE_NONE
	if (!bytes_left)
	{
		// Aargh! Not enough room left for the final byte!
		if (assert)
		{
			Dbg_MsgAssert(0,("WriteToBuffer: Buffer not big enough"));
		}	
		return 0;
	}	
	*p_buffer++=ESYMBOLTYPE_NONE;
	--bytes_left;
	
	// Return how many bytes got written.
	return bufferSize-bytes_left;
}

// Calculates how big a buffer will need to be to hold a structure using WriteToBuffer.
uint32 CalculateBufferSize(CStruct *p_structure, uint32 tempBufferSize)
{
	Dbg_MsgAssert(p_structure,("NULL p_structure"));

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); // Use the temporary heap
	
	uint8 *p_temp=(uint8*)Mem::Malloc(tempBufferSize);
	Dbg_MsgAssert(p_temp,("Could not allocate temporary buffer"));
	uint32 space_required=WriteToBuffer(p_structure,p_temp,tempBufferSize);
	Mem::Free(p_temp);
	Mem::Manager::sHandle().PopContext();

	return space_required;
}

// Sets the structure's contents using the passed buffer, which was generated
// by WriteToBuffer.
// If the structure contained anything to begin with, it will get cleared.	
//
// Returns a pointer to after the terminating ESYMBOLTYPE_NONE, required
// when this function calls itself.
uint8 *ReadFromBuffer(CStruct *p_structure, uint8 *p_buffer)
{
	Dbg_MsgAssert(p_structure,("NULL p_structure"));
	Dbg_MsgAssert(p_buffer,("NULL p_buffer sent to ReadFromBuffer"));
	float zero_float=0.0f;

	// First clear anything currently in the structure.
	p_structure->Clear();

	// Make sure we're using the script heap.
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	
	// Scan through the buffer adding the components until ESYMBOLTYPE_NONE is reached.
	while (true)
    {
		float x,y,z;
		const char *p_string;
		CStruct *p_struct;
		CArray *p_array;
		
		// Get the type and the name checksum.
		ESymbolType type=(ESymbolType)*p_buffer++;
		if (type==ESYMBOLTYPE_NONE)
		{
			// All done.
			break;
		}

		// Get the name checksum, which may be stored as an index into a table of checksums
		// to save space.
		uint32 name=0;
		if (type&MASK_8_BIT_NAME_LOOKUP)			
		{
			Dbg_MsgAssert(!(type&MASK_16_BIT_NAME_LOOKUP),("Eh? Both lookup-table flags set ?"));
			
			#ifdef __PLAT_WN32__
			// The lookup table is not loaded when compiling on PC
			name=CRCD(0xef5f3f41,"CompressedName");
			#else
			CArray *p_table=GetArray(0x35115a20/*WriteToBuffer_CompressionLookupTable_8*/);
			name=p_table->GetChecksum(*p_buffer);
			#endif
			++p_buffer;
		}
		else if (type&MASK_16_BIT_NAME_LOOKUP)			
		{
			#ifdef __PLAT_WN32__
			name=CRCD(0xef5f3f41,"CompressedName");
			#else
			CArray *p_table=GetArray(0x25231f42/*WriteToBuffer_CompressionLookupTable_16*/);
			name=p_table->GetChecksum(Read2Bytes(p_buffer).mUInt);
			#endif
			p_buffer+=2;
		}
		else
		{
			// It's not stored as an index, but as the full 4 byte checksum.
			name=Read4Bytes(p_buffer).mChecksum;
			p_buffer+=4;
		}
		type=(ESymbolType)( ((uint32)type) & ~(MASK_8_BIT_NAME_LOOKUP | MASK_16_BIT_NAME_LOOKUP) );
			
		
        switch (type)
        {
        case ESYMBOLTYPE_INTEGER:
			p_structure->AddInteger(name,Read4Bytes(p_buffer).mInt);
			p_buffer+=4;
            break;
        case ESYMBOLTYPE_FLOAT:
			p_structure->AddFloat(name,Read4Bytes(p_buffer).mFloat);
			p_buffer+=4;
            break;
        case ESYMBOLTYPE_NAME:
			p_structure->AddChecksum(name,Read4Bytes(p_buffer).mChecksum);
			p_buffer+=4;
            break;
		case ESYMBOLTYPE_ZERO_FLOAT:
			p_structure->AddFloat(name,zero_float);
			break;	
		case ESYMBOLTYPE_ZERO_INTEGER:
			p_structure->AddInteger(name,0);
			break;	
		case ESYMBOLTYPE_INTEGER_ONE_BYTE:
			p_structure->AddInteger(name,*(sint8*)p_buffer++);
			break;
		case ESYMBOLTYPE_UNSIGNED_INTEGER_ONE_BYTE:
			p_structure->AddInteger(name,*p_buffer++);
			break;
		case ESYMBOLTYPE_INTEGER_TWO_BYTES:
			p_structure->AddInteger(name,Read2Bytes(p_buffer).mInt);
			p_buffer+=2;
			break;	
		case ESYMBOLTYPE_UNSIGNED_INTEGER_TWO_BYTES:
			p_structure->AddInteger(name,Read2Bytes(p_buffer).mUInt);
			p_buffer+=2;
			break;	
        case ESYMBOLTYPE_STRING:
			p_string=(const char *)p_buffer;
			p_structure->AddString(name,p_string);
			p_buffer+=strlen(p_string);
			++p_buffer; // Skip over the terminator too.
            break;
        case ESYMBOLTYPE_LOCALSTRING:
			p_string=(const char *)p_buffer;
			p_structure->AddLocalString(name,p_string);
			p_buffer+=strlen(p_string);
			++p_buffer; // Skip over the terminator too.
            break;
        case ESYMBOLTYPE_PAIR:
			x=Read4Bytes(p_buffer).mFloat;
			p_buffer+=4;
			y=Read4Bytes(p_buffer).mFloat;
			p_buffer+=4;
			p_structure->AddPair(name,x,y);
            break;
        case ESYMBOLTYPE_VECTOR:
			x=Read4Bytes(p_buffer).mFloat;
			p_buffer+=4;
			y=Read4Bytes(p_buffer).mFloat;
			p_buffer+=4;
			z=Read4Bytes(p_buffer).mFloat;
			p_buffer+=4;
			p_structure->AddVector(name,x,y,z);
            break;
		case ESYMBOLTYPE_STRUCTURE:
			// Create a new structure, and fill it in by calling this function recursively.
			p_struct=new CStruct;
			p_buffer=ReadFromBuffer(p_struct,p_buffer);
			// Add the new component.
			p_structure->AddStructurePointer(name,p_struct);
			break;	
		case ESYMBOLTYPE_ARRAY:
			// Create a new array, and fill it in using the array's ReadFromBuffer.
			p_array=new CArray;
			p_buffer=ReadFromBuffer(p_array,p_buffer);
			// Add the new component.
			p_structure->AddArrayPointer(name,p_array);
			break;	
        default:
            Dbg_MsgAssert(0,("Unsupported component type of '%s' encountered in ReadFromBuffer",GetTypeName(type)));
            break;
        }
    }

	Mem::Manager::sHandle().PopContext();
	
	return p_buffer;
}

// Writes the contents of the array to a buffer.
// The information is outputted in a byte-packed format, so p_buffer does
// not need to be aligned.
// The buffer can then be used to regenerate the original array by
// passing it to ReadFromBuffer.
// Passed the size of the buffer so that it can check if there is enough space.
// Returns the number of bytes that it actually wrote.
//
// If there was not enough space, and assert is false, it will return a count of 0.
//
uint32 WriteToBuffer(CArray *p_array, uint8 *p_buffer, uint32 bufferSize, EAssertType assert)
{
	Dbg_MsgAssert(p_array,("NULL p_array"));
	Dbg_MsgAssert(p_buffer,("NULL p_buffer sent to WriteToBuffer"));
	uint32 bytes_left=bufferSize;

	if (bytes_left<3)
	{
		if (assert)
		{
			Dbg_MsgAssert(0,("WriteToBuffer: Buffer not big enough"));
		}	
		return 0;
	}
	
	ESymbolType type=p_array->GetType();
	uint32 size=p_array->GetSize();
	
	*p_buffer++=type;

	// Easy to change WriteToBuffer to support 4 byte sizes, but keeping as 2 for the moment for
	// backwards compatibility.
	Dbg_MsgAssert(size<0x10000,("Size of array too big, currently only 2 bytes used to store size in WriteToBuffer ..."));
	p_buffer=Write2Bytes(p_buffer,size);
	bytes_left-=3;
	
	switch (type)
	{
        case ESYMBOLTYPE_INTEGER:
        case ESYMBOLTYPE_FLOAT:
        case ESYMBOLTYPE_NAME:
		{
			if (size*4>bytes_left)
			{
				if (assert)
				{
					Dbg_MsgAssert(0,("WriteToBuffer: Buffer not big enough"));
				}	
				return 0;
			}
			
			uint32 *p_data=p_array->GetArrayPointer();
			for (uint32 i=0; iGetArrayPointer();
			for (uint32 i=0; ibytes_left)
			{
				if (assert)
				{
					Dbg_MsgAssert(0,("WriteToBuffer: Buffer not big enough"));
				}	
				return 0;
			}
			
			CPair **pp_pairs=(CPair**)p_array->GetArrayPointer();
			Dbg_MsgAssert(pp_pairs,("NULL pp_pairs ?"));
			
			for (uint32 i=0; imX);
				p_buffer=Write4Bytes(p_buffer,p_pair->mY);
				++pp_pairs;
			}
			
			bytes_left-=size*8;
			break;
		}
			
        case ESYMBOLTYPE_VECTOR:
		{
			if (size*12>bytes_left)
			{
				if (assert)
				{
					Dbg_MsgAssert(0,("WriteToBuffer: Buffer not big enough"));
				}	
				return 0;
			}
			
			CVector **pp_vectors=(CVector**)p_array->GetArrayPointer();
			Dbg_MsgAssert(pp_vectors,("NULL pp_vectors ?"));
			
			for (uint32 i=0; imX);
				p_buffer=Write4Bytes(p_buffer,p_vector->mY);
				p_buffer=Write4Bytes(p_buffer,p_vector->mZ);
				++pp_vectors;
			}
			
			bytes_left-=size*12;
			break;
		}
		
        case ESYMBOLTYPE_STRUCTURE:
		{
			CStruct **pp_structures=(CStruct**)p_array->GetArrayPointer();
			Dbg_MsgAssert(pp_structures,("NULL pp_structures ?"));
			
			for (uint32 i=0; iGetArrayPointer();
			Dbg_MsgAssert(pp_arrays,("NULL pp_arrays ?"));
			
			for (uint32 i=0; iGetSize();
	// Easy to change WriteToBuffer to support 4 byte sizes, but keeping as 2 for the moment for
	// backwards compatibility.
	Dbg_MsgAssert(size<0x10000,("Size of array too big, currently only 2 bytes used to store size in WriteToBuffer ..."));
	ESymbolType type=p_array->GetType();
	
	switch (type)
	{
        case ESYMBOLTYPE_INTEGER:
        case ESYMBOLTYPE_FLOAT:
        case ESYMBOLTYPE_NAME:
			space_required+=size*4;
			break;
			
        case ESYMBOLTYPE_STRING:
			for (i=0; iGetString(i);
				Dbg_MsgAssert(p_string,("NULL GetString() for element %d when attempting to call CalculateBufferSize",i));			
				space_required+=strlen(p_string)+1;
			}
			break;

        case ESYMBOLTYPE_LOCALSTRING:
			for (i=0; iGetLocalString(i);
				Dbg_MsgAssert(p_string,("NULL GetLocalString() for element %d when attempting to call CalculateBufferSize",i));			
				space_required+=strlen(p_string)+1;
			}
			break;
			
        case ESYMBOLTYPE_PAIR:
			#ifdef __NOPT_ASSERT__
			for (i=0; iGetPair(i);
				Dbg_MsgAssert(p_pair,("NULL GetPair() for element %d when attempting to call CalculateBufferSize",i));
			}	
			#endif
			space_required+=size*8;
			break;
			
        case ESYMBOLTYPE_VECTOR:
			#ifdef __NOPT_ASSERT__
			for (i=0; iGetVector(i);
				Dbg_MsgAssert(p_vector,("NULL GetVector() for element %d when attempting to call CalculateBufferSize",i));
			}	
			#endif
			space_required+=size*12;
			break;
		
        case ESYMBOLTYPE_STRUCTURE:
			for (i=0; iGetStructure(i);
				Dbg_MsgAssert(p_structure,("NULL GetStructure() for element %d when attempting to call CalculateBufferSize",i));
				space_required+=CalculateBufferSize(p_structure);
			}
			break;
		
        case ESYMBOLTYPE_ARRAY:
			for (i=0; iGetArray(i);
				Dbg_MsgAssert(p_array_element,("NULL GetArray() for element %d when attempting to call CalculateBufferSize",i));
				space_required+=CalculateBufferSize(p_array_element);
			}
			break;

        case ESYMBOLTYPE_NONE:
			// Empty array
			break;
						
        default:
            Dbg_MsgAssert(0,("Array type of '%s' is not supported in CalculateBufferSize",GetTypeName(type)));
            break;
    }
	
	return space_required;
}

// Sets the array's contents using the passed buffer, which was generated
// by WriteToBuffer.
// If the array contained anything to begin with, it will get cleared.	

// Returns a pointer to after the read array data.
uint8 *ReadFromBuffer(CArray *p_array, uint8 *p_buffer)
{
	Dbg_MsgAssert(p_array,("NULL p_array sent to ReadFromBuffer"));
	Dbg_MsgAssert(p_buffer,("NULL p_buffer sent to ReadFromBuffer"));

	// First clear anything currently in the array.
	CleanUpArray(p_array);

	// Get the type and size
	ESymbolType type=(ESymbolType)*p_buffer++;
	uint32 size=*p_buffer++;
	size+=*p_buffer++<<8; // 2 byte size.

	// Make sure we're using the script heap.
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	p_array->SetSizeAndType(size,type);
	Mem::Manager::sHandle().PopContext();
	
    switch (type)
    {
		case ESYMBOLTYPE_INTEGER:
		{
			for (uint32 i=0; iSetInteger(i,Read4Bytes(p_buffer).mInt);
				p_buffer+=4;
			}
			break;
		}		
		case ESYMBOLTYPE_FLOAT:
		{
			for (uint32 i=0; iSetFloat(i,Read4Bytes(p_buffer).mFloat);
				p_buffer+=4;
			}
			break;
		}		
		case ESYMBOLTYPE_NAME:
		{
			for (uint32 i=0; iSetChecksum(i,Read4Bytes(p_buffer).mChecksum);
				p_buffer+=4;
			}
			break;
		}		
		case ESYMBOLTYPE_STRING:
		{
			for (uint32 i=0; iSetString(i,CreateString((const char*)p_buffer));
				p_buffer+=strlen((char*)p_buffer)+1;
			}
			break;
		}		
		case ESYMBOLTYPE_LOCALSTRING:
		{
			for (uint32 i=0; iSetLocalString(i,CreateString((const char*)p_buffer));
				p_buffer+=strlen((char*)p_buffer)+1;
			}
			break;
		}		
		case ESYMBOLTYPE_PAIR:
		{
			for (uint32 i=0; imX=Read4Bytes(p_buffer).mFloat;
				p_buffer+=4;
				p_pair->mY=Read4Bytes(p_buffer).mFloat;
				p_buffer+=4;

				p_array->SetPair(i,p_pair);
			}		
			break;
		}	
		case ESYMBOLTYPE_VECTOR:
		{
			for (uint32 i=0; imX=Read4Bytes(p_buffer).mFloat;
				p_buffer+=4;
				p_vector->mY=Read4Bytes(p_buffer).mFloat;
				p_buffer+=4;
				p_vector->mZ=Read4Bytes(p_buffer).mFloat;
				p_buffer+=4;

				p_array->SetVector(i,p_vector);
			}		
			break;
		}	
		case ESYMBOLTYPE_STRUCTURE:
		{
			for (uint32 i=0; iSetStructure(i,p_structure);
			}			
			break;
		}	
		case ESYMBOLTYPE_ARRAY:
		{
			for (uint32 i=0; iSetArray(i,p_new_array);
			}			
			break;
		}	
		case ESYMBOLTYPE_NONE:
			// Empty array
			break;
		default:
			Dbg_MsgAssert(0,("Unsupported type of '%s' encountered in ReadFromBuffer",GetTypeName(type)));
			break;
	}
	
	return p_buffer;
}


union UValue
{
	int mIntegerValue;
	float mFloatValue;
	char *mpString;
	char *mpLocalString;
	CPair *mpPair;
	CVector *mpVector;
	CStruct *mpStructure;
	CArray *mpArray;
	uint8 *mpScript;
	uint32 mChecksum;
	uint32 mUnion;
};

// This will load type and value from the passed component, resolving it if it refers to a global.
static void Resolve(const CComponent& comp, ESymbolType &type, UValue &value)
{
	value.mUnion=comp.mUnion;
	type=(ESymbolType)comp.mType;
	
	if (type==ESYMBOLTYPE_NAME)
	{
		CSymbolTableEntry *p_entry=Resolve(value.mChecksum);
		if (p_entry)
		{
			value.mUnion=p_entry->mUnion;
			type=(ESymbolType)p_entry->mType;
		}	
	}
}

// The == operator is used when interpreting switch statements and expressions in script
// This function is here rather than in component.cpp to avoid cyclic dependencies.
bool CComponent::operator==( const CComponent& v ) const
{
	UValue value_a,value_b;
	ESymbolType type_a,type_b;
	Resolve(*this,type_a,value_a);
	Resolve(v,type_b,value_b);
	
	switch (type_a)
	{
		case ESYMBOLTYPE_INTEGER:
			if (type_b==ESYMBOLTYPE_FLOAT)
			{
				return ((float)value_a.mIntegerValue)==value_b.mFloatValue;
			}
			else if (type_b==ESYMBOLTYPE_INTEGER)
			{
				return value_a.mIntegerValue==value_b.mIntegerValue;
			}
			break;
		case ESYMBOLTYPE_FLOAT:
			if (type_b==ESYMBOLTYPE_INTEGER)
			{
				return value_a.mFloatValue==((float)value_b.mIntegerValue);
			}
			else if (type_b==ESYMBOLTYPE_FLOAT)
			{
				return value_a.mFloatValue==value_b.mFloatValue;
			}
			break;
		case ESYMBOLTYPE_STRING:
			if (type_b==ESYMBOLTYPE_STRING)
			{
				Dbg_MsgAssert(value_a.mpString,("NULL value_a.mpString ?"));
				Dbg_MsgAssert(value_b.mpString,("NULL value_b.mpString ?"));
				return strcmp(value_a.mpString,value_b.mpString)==0;
			}
			break;
		case ESYMBOLTYPE_LOCALSTRING:
			if (type_b==ESYMBOLTYPE_LOCALSTRING)
			{
				Dbg_MsgAssert(value_a.mpLocalString,("NULL value_a.mpLocalString ?"));
				Dbg_MsgAssert(value_b.mpLocalString,("NULL value_b.mpLocalString ?"));
				return strcmp(value_a.mpLocalString,value_b.mpLocalString)==0;
			}
			break;
		case ESYMBOLTYPE_NAME:
			if (type_b==ESYMBOLTYPE_NAME)
			{
				return value_a.mChecksum==value_b.mChecksum;
			}	
			break;
		case ESYMBOLTYPE_PAIR:
			if (type_b==ESYMBOLTYPE_PAIR)
			{
				Dbg_MsgAssert(value_a.mpPair,("NULL value_a.mpPair ?"));
				Dbg_MsgAssert(value_b.mpPair,("NULL value_b.mpPair ?"));
				return value_a.mpPair->mX==value_b.mpPair->mX && 
					   value_a.mpPair->mY==value_b.mpPair->mY;
			}
			break;
		case ESYMBOLTYPE_VECTOR:
			if (type_b==ESYMBOLTYPE_VECTOR)
			{
				Dbg_MsgAssert(value_a.mpVector,("NULL value_a.mpVector ?"));
				Dbg_MsgAssert(value_b.mpVector,("NULL value_b.mpVector ?"));
				return value_a.mpVector->mX==value_b.mpVector->mX && 
					   value_a.mpVector->mY==value_b.mpVector->mY &&
					   value_a.mpVector->mZ==value_b.mpVector->mZ;
			}
			break;
		case ESYMBOLTYPE_QSCRIPT:
			if (type_b==ESYMBOLTYPE_QSCRIPT)
			{
				return value_a.mpScript==value_b.mpScript;
			}
			break;
		case ESYMBOLTYPE_STRUCTURE:
			if (type_b==ESYMBOLTYPE_STRUCTURE)
			{
				Dbg_MsgAssert(value_a.mpStructure,("NULL value_a.mpStructure ?"));
				Dbg_MsgAssert(value_b.mpStructure,("NULL value_b.mpStructure ?"));
				
				// The structures definitely match if the pointers match, cos its the 
				// same bloomin' structure.
				if (value_a.mpStructure==value_b.mpStructure)
				{
					return true;
				}	
				
				// If the pointers do not match, the structures may still have contents that match.
				// Structure comparison's ain't supported yet though. Implement when needed.
				// Note: If this ever get's implemented, you'll need to deal with un-named name 
				// components that resolve to a global structure.
				#ifdef __NOPT_ASSERT__
				printf("CStruct comparisons are not supported yet ... implement when needed\n");
				#endif
			}
			break;
		case ESYMBOLTYPE_ARRAY:
			if (type_b==ESYMBOLTYPE_ARRAY)
			{
				Dbg_MsgAssert(value_a.mpArray,("NULL value_a.mpArray ?"));
				Dbg_MsgAssert(value_b.mpArray,("NULL value_b.mpArray ?"));
				
				// The arrays definitely match if the pointers match.
				if (value_a.mpArray==value_b.mpArray)
				{
					return true;
				}	
				
				// If the pointers do not match, the arrays may still have contents that match.
				return *value_a.mpArray==*value_b.mpArray;
			}
			break;
		case ESYMBOLTYPE_CFUNCTION:
			// TODO ...
			#ifdef __NOPT_ASSERT__
			printf("C-Function comparisons are not supported yet ... implement when needed\n");
			#endif
			break;
		case ESYMBOLTYPE_MEMBERFUNCTION:
			// TODO ...
			#ifdef __NOPT_ASSERT__
			printf("Member function comparisons are not supported yet ... implement when needed\n");
			#endif
			break;
		case ESYMBOLTYPE_NONE:
			break;
		default:
			#ifdef __NOPT_ASSERT__
			printf("'%s' with '%s' comparisons are not supported\n",GetTypeName(type_a),GetTypeName(type_b));			
			#endif
			break;
	}		
	
	return false;
}

bool CComponent::operator!=( const CComponent& v ) const
{
	return !(*this==v);
}

bool CComponent::operator<( const CComponent& v ) const
{
	UValue value_a,value_b;
	ESymbolType type_a,type_b;
	Resolve(*this,type_a,value_a);
	Resolve(v,type_b,value_b);
	
	switch (type_a)
	{
		case ESYMBOLTYPE_INTEGER:
			if (type_b==ESYMBOLTYPE_FLOAT)
			{
				return ((float)value_a.mIntegerValue) < value_b.mFloatValue;
			}
			else if (type_b==ESYMBOLTYPE_INTEGER)
			{
				return value_a.mIntegerValue < value_b.mIntegerValue;
			}
			break;
		case ESYMBOLTYPE_FLOAT:
			if (type_b==ESYMBOLTYPE_INTEGER)
			{
				return value_a.mFloatValue < ((float)value_b.mIntegerValue);
			}
			else if (type_b==ESYMBOLTYPE_FLOAT)
			{
				return value_a.mFloatValue < value_b.mFloatValue;
			}
			break;
		case ESYMBOLTYPE_STRING:
			if (type_b==ESYMBOLTYPE_STRING)
			{
				Dbg_MsgAssert(value_a.mpString,("NULL value_a.mpString ?"));
				Dbg_MsgAssert(value_b.mpString,("NULL value_b.mpString ?"));
				// Using string length rather than position in the alphabet, cos it's
				// probably more useful.
				return strlen(value_a.mpString) < strlen(value_b.mpString);
			}
			break;
		case ESYMBOLTYPE_LOCALSTRING:
			if (type_b==ESYMBOLTYPE_LOCALSTRING)
			{
				Dbg_MsgAssert(value_a.mpLocalString,("NULL value_a.mpLocalString ?"));
				Dbg_MsgAssert(value_b.mpLocalString,("NULL value_b.mpLocalString ?"));
				return strlen(value_a.mpLocalString) < strlen(value_b.mpLocalString);
			}
			break;
		default:
			#ifdef __NOPT_ASSERT__
			printf("'%s' with '%s' less-than comparisons are not supported\n",GetTypeName(type_a),GetTypeName(type_b));			
			#endif
			break;
	}		
	
	return false;
}

bool CComponent::operator>( const CComponent& v ) const
{
	UValue value_a,value_b;
	ESymbolType type_a,type_b;
	Resolve(*this,type_a,value_a);
	Resolve(v,type_b,value_b);
	
	switch (type_a)
	{
		case ESYMBOLTYPE_INTEGER:
			if (type_b==ESYMBOLTYPE_FLOAT)
			{
				return ((float)value_a.mIntegerValue) > value_b.mFloatValue;
			}
			else if (type_b==ESYMBOLTYPE_INTEGER)
			{
				return value_a.mIntegerValue > value_b.mIntegerValue;
			}
			break;
		case ESYMBOLTYPE_FLOAT:
			if (type_b==ESYMBOLTYPE_INTEGER)
			{
				return value_a.mFloatValue > ((float)value_b.mIntegerValue);
			}
			else if (type_b==ESYMBOLTYPE_FLOAT)
			{
				return value_a.mFloatValue > value_b.mFloatValue;
			}
			break;
		case ESYMBOLTYPE_STRING:
			if (type_b==ESYMBOLTYPE_STRING)
			{
				Dbg_MsgAssert(value_a.mpString,("NULL value_a.mpString ?"));
				Dbg_MsgAssert(value_b.mpString,("NULL value_b.mpString ?"));
				// Using string length rather than position in the alphabet, cos it's
				// probably more useful.
				return strlen(value_a.mpString) > strlen(value_b.mpString);
			}
			break;
		case ESYMBOLTYPE_LOCALSTRING:
			if (type_b==ESYMBOLTYPE_LOCALSTRING)
			{
				Dbg_MsgAssert(value_a.mpLocalString,("NULL value_a.mpLocalString ?"));
				Dbg_MsgAssert(value_b.mpLocalString,("NULL value_b.mpLocalString ?"));
				return strlen(value_a.mpLocalString) > strlen(value_b.mpLocalString);
			}
			break;
		default:
			#ifdef __NOPT_ASSERT__
			printf("'%s' with '%s' greater-than comparisons are not supported\n",GetTypeName(type_a),GetTypeName(type_b));			
			#endif
			break;
	}		
	
	return false;
}

bool CComponent::operator<=( const CComponent& v ) const
{
	if (*this=( const CComponent& v ) const
{
	if (*this>v)
	{
		return true;
	}
	if (*this==v)
	{
		return true;
	}
	return false;	
}

// This is used in eval.cpp, when evaluating foo[3] say.
// Copies the array element indicated by index into the passed component.
// The type of p_comp may be ESYMBOLTYPE_NONE if the type is not supported yet by not being in
// the switch statement.
void CopyArrayElementIntoComponent(CArray *p_array, uint32 index, CComponent *p_comp)
{
	Dbg_MsgAssert(p_array,("NULL p_array"));
	Dbg_MsgAssert(indexGetSize(),("Array index %d out of range, array size is %d",index,p_array->GetSize()));
	Dbg_MsgAssert(p_comp,("NULL p_comp"));
	
	p_comp->mType=p_array->GetType();
	switch (p_comp->mType)
	{
	case ESYMBOLTYPE_INTEGER:
		p_comp->mIntegerValue=p_array->GetInteger(index);
		break;
	case ESYMBOLTYPE_FLOAT:
		p_comp->mFloatValue=p_array->GetFloat(index);
		break;
	case ESYMBOLTYPE_NAME:
		p_comp->mChecksum=p_array->GetChecksum(index);
		break;
	case ESYMBOLTYPE_STRUCTURE:
		p_comp->mpStructure=new CStruct;
		p_comp->mpStructure->AppendStructure(p_array->GetStructure(index));
		break;
	case ESYMBOLTYPE_ARRAY:
		p_comp->mpArray=new CArray;
		CopyArray(p_comp->mpArray,p_array->GetArray(index));
		break;
	case ESYMBOLTYPE_STRING:
		p_comp->mpString=CreateString(p_array->GetString(index));
		break;
	case ESYMBOLTYPE_VECTOR:
	{
		CVector *p_source_vector=p_array->GetVector(index);
		p_comp->mpVector=new CVector;
		p_comp->mpVector->mX=p_source_vector->mX;
		p_comp->mpVector->mY=p_source_vector->mY;
		p_comp->mpVector->mZ=p_source_vector->mZ;
		break;
	}	
	case ESYMBOLTYPE_PAIR:
	{
		CPair *p_source_pair=p_array->GetPair(index);
		p_comp->mpPair=new CPair;
		p_comp->mpPair->mX=p_source_pair->mX;
		p_comp->mpPair->mY=p_source_pair->mY;
		break;
	}	
	default:
		Dbg_MsgAssert(0,("The [] operator is not yet supported when the array element has  type '%s'",GetTypeName(p_comp->mType)));
		p_comp->mType=ESYMBOLTYPE_NONE;
		p_comp->mUnion=0;
		break;
	}	
}

// This is used in parse.cpp, and cfuncs.cpp in sFormatText
void ResolveNameComponent(CComponent *p_comp)
{
	Dbg_MsgAssert(p_comp,("NULL p_comp"));

	if (p_comp->mType!=ESYMBOLTYPE_NAME)
	{
		return;
	}
		
	CSymbolTableEntry *p_entry=Resolve(p_comp->mChecksum);
	if (!p_entry)
	{
		return;
	}
		
	switch (p_entry->mType)
	{
	case ESYMBOLTYPE_FLOAT:
		p_comp->mType=ESYMBOLTYPE_FLOAT;
		p_comp->mFloatValue=p_entry->mFloatValue;
		break;
	case ESYMBOLTYPE_INTEGER:
		p_comp->mType=ESYMBOLTYPE_INTEGER;
		p_comp->mIntegerValue=p_entry->mIntegerValue;
		break;
	case ESYMBOLTYPE_VECTOR:
		p_comp->mType=ESYMBOLTYPE_VECTOR;
		p_comp->mpVector=p_entry->mpVector;
		break;
	case ESYMBOLTYPE_PAIR:
		p_comp->mType=ESYMBOLTYPE_PAIR;
		p_comp->mpPair=p_entry->mpPair;
		break;
	case ESYMBOLTYPE_STRING:
		p_comp->mType=ESYMBOLTYPE_STRING;
		p_comp->mpString=p_entry->mpString;
		break;
	case ESYMBOLTYPE_LOCALSTRING:
		p_comp->mType=ESYMBOLTYPE_LOCALSTRING;
		p_comp->mpLocalString=p_entry->mpLocalString;
		break;
	case ESYMBOLTYPE_STRUCTURE:
		p_comp->mType=ESYMBOLTYPE_STRUCTURE;
		p_comp->mpStructure=p_entry->mpStructure;
		break;
	case ESYMBOLTYPE_ARRAY:
		p_comp->mType=ESYMBOLTYPE_ARRAY;
		p_comp->mpArray=p_entry->mpArray;
		break;
	default:
		break;
	}
}

} // namespace Script



================================================
FILE: Code/Gel/Scripting/utils.h
================================================
#ifndef	__SCRIPTING_UTILS_H
#define	__SCRIPTING_UTILS_H

#ifndef	__SCRIPTING_SCRIPTDEFS_H
#include  // For EAssertType
#endif

namespace Script
{

class CArray;
class CStruct;
class CComponent;

void PrintContents(const CArray *p_array, int indent=0);
void PrintContents(const CStruct *p_structure, int indent=0);

uint32 WriteToBuffer(CStruct *p_structure, uint8 *p_buffer, uint32 bufferSize, EAssertType assert=ASSERT);
uint32 CalculateBufferSize(CStruct *p_structure, uint32 tempBufferSize=100000);
uint8 *ReadFromBuffer(CStruct *p_structure, uint8 *p_buffer);

uint32 WriteToBuffer(CArray *p_array, uint8 *p_buffer, uint32 bufferSize, EAssertType assert=ASSERT);
uint32 CalculateBufferSize(CArray *p_array);
uint8 *ReadFromBuffer(CArray *p_array, uint8 *p_buffer);

// This is used in eval.cpp, when evaluating foo[3] say.
// Copies the array element indicated by index into the passed component.
// The type of p_comp may be ESYMBOLTYPE_NONE if the type is not supported yet by not being in
// the switch statement.
void CopyArrayElementIntoComponent(CArray *p_array, uint32 index, CComponent *p_comp);
void ResolveNameComponent(CComponent *p_comp);

}

#endif // #ifndef	__SCRIPTING_UTILS_H



================================================
FILE: Code/Gel/Scripting/vecpair.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// vecpair.cpp		KSH 17 Oct 2001
//
// Just defines the constructors and pool statics.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 

DefinePoolableClass(Script::CVector);
DefinePoolableClass(Script::CPair);

namespace Script
{

CVector::CVector()
{
	// Initialise everything. CVector is not derived from CClass so we don't get
	// the auro-zeroing.
	mX=mY=mZ=0.0f;
}

// Note: No destructor needed.


CPair::CPair()
{
	// Initialise everything. CPair is not derived from CClass so we don't get
	// the auro-zeroing.
	mX=mY=0.0f;
}

// Note: No destructor needed.

} // namespace Script



================================================
FILE: Code/Gel/Scripting/vecpair.h
================================================
#ifndef	__SCRIPTING_VECPAIR_H
#define	__SCRIPTING_VECPAIR_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

namespace Script
{

// Note: These are not derived from CClass to avoid the extra memory overhead due to the virtual destructor.

#ifdef __PLAT_WN32__
class CVector
#else
class CVector : public Mem::CPoolable
#endif
{
public:
	CVector();
	// No copy constructor or assignement operators needed, the defaults will work.

	union
	{
		CVector *mpNext;
		float mX;
	};	
	
	float mY;
	float mZ;
};

#ifdef __PLAT_WN32__
class CPair
#else
class CPair : public Mem::CPoolable
#endif
{
public:
	CPair();
	// No copy constructor or assignement operators needed, the defaults will work.

	union
	{
		CPair *mpNext;
		float mX;
	};	
	
	float mY;
};

} // namespace Script

#endif // #ifndef	__SCRIPTING_VECPAIR_H


================================================
FILE: Code/Gel/Scripting/win32functions.cpp
================================================
#ifdef __PLAT_WN32__

#ifndef __CORE_DEFINES_H
#include 
#endif

namespace Tmr
{
uint64 GetRenderFrame( void )
{
	return 0;
}

uint64 GetTimeInCPUCycles( void )
{
	return 0;
}
}

namespace Obj
{
// Needed for asserts to compile
bool DebugSkaterScripts=false;

class CObject;

CObject *ResolveToObject(uint32 id, bool ignoreScreenElements)
{
	return NULL;
}
}

namespace Pip
{
void *Load(const char *p_fileName)
{
	return NULL;
}

void Unload(const char *p_fileName)
{
}
}

namespace Mem
{

Manager *Manager::sp_instance = NULL;

Manager::Manager( void )
{
	memset(this,0,sizeof(Manager));
}

void Manager::sSetUp( void )
{
	if ( !sp_instance )
	{
		sp_instance = new Manager;
	}
	else
	{
		Dbg_Warning( "Already Initialized!" );
	}
}

void Manager::sCloseDown( void )
{
}

void Manager::PushContext( Allocator* alloc )
{
}

void Manager::PopContext( void )
{
}

char *Manager::GetContextName()
{
	return "";
}

Mem::Heap *Manager::FirstHeap()
{
	return NULL;
}

Heap* Manager::GetHeap( uint32 whichHeap )
{
	return NULL;
}

Mem::Heap *Manager::NextHeap(Mem::Heap * pHeap)
{
	return NULL;
}

void SetThreadSafe(bool safe)
{
}

void *Malloc( size_t size )
{
	return new char[size];
}

void Free( void *p_mem )
{
	delete [] p_mem;
}

void PopMemProfile()
{
}

void PushMemProfile(char *p_type)
{
}

}

#endif




================================================
FILE: Code/Gel/SoundFX/NGPS/p_sfx.cpp
================================================
//--------------------------------------------------------------------------------------------
// Playstation 2 sound effect functionality...
// Taken from samples on Sony website, modified by mjd.
//--------------------------------------------------------------------------------------------

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#define NO_SOUND	0

namespace Pcm
{
extern int	gNonAllignedIopBuffer;
};

namespace Sfx
{



// Macros and constants:

// This array maps the platform-independent reverb modes specified in reverb.q to the available Ps2 reverb modes.
int ReverbModes[] =
{
	// put the default first in this list, since the default param to SetReverb() is 0
	SD_REV_MODE_HALL,			// REV_MODE_DEFAULT
	SD_REV_MODE_HALL,			// REV_MODE_GENERIC
	SD_REV_MODE_ROOM,			// REV_MODE_PADDEDCELL
	SD_REV_MODE_ROOM,			// REV_MODE_ROOM
	SD_REV_MODE_STUDIO_A,		// REV_MODE_BATHROOM
	SD_REV_MODE_ROOM,			// REV_MODE_LIVINGROOM
	SD_REV_MODE_STUDIO_C,		// REV_MODE_STONEROOM
	SD_REV_MODE_HALL,			// REV_MODE_AUDITORIUM
	SD_REV_MODE_HALL,			// REV_MODE_CONCERTHALL
	SD_REV_MODE_ROOM,			// REV_MODE_CAVE
	SD_REV_MODE_HALL,			// REV_MODE_ARENA
	SD_REV_MODE_HALL,			// REV_MODE_HANGAR
	SD_REV_MODE_HALL,			// REV_MODE_CARPETEDHALLWAY
	SD_REV_MODE_HALL,			// REV_MODE_HALLWAY
	SD_REV_MODE_HALL,			// REV_MODE_STONECORRIDOR
	SD_REV_MODE_HALL,			// REV_MODE_ALLEY
	SD_REV_MODE_HALL,			// REV_MODE_FOREST
	SD_REV_MODE_HALL,			// REV_MODE_CITY
	SD_REV_MODE_HALL,			// REV_MODE_MOUNTAINS
	SD_REV_MODE_HALL,			// REV_MODE_QUARRY
	SD_REV_MODE_HALL,			// REV_MODE_PLAIN
	SD_REV_MODE_HALL,			// REV_MODE_PARKINGLOT
	SD_REV_MODE_PIPE,			// REV_MODE_SEWERPIPE
	SD_REV_MODE_PIPE,			// REV_MODE_UNDERWATER
};

#define NUM_REVERB_MODES ( ( int )( sizeof( ReverbModes ) / sizeof( int ) ) )

#if NUM_STREAMS > 3
#error tell matt to handle more than 3 streams in sfx.cpp
#endif

#if NUM_STREAMS == 3
#define VOICES_MINUS_STREAMS ( 0xFFFFFF & ~( ( 1 << STREAM_VOICE( 0 ) ) | ( 1 << STREAM_VOICE( 1 ) ) | ( 1 << STREAM_VOICE( 2 ) ) ) )
#else
#define VOICES_MINUS_STREAMS ( 0xFFFFFF & ~( ( 1 << MUSIC_L_VOICE ) | ( 1 << MUSIC_R_VOICE ) ) )
#endif
#define VOICES_MINUS_NON_REVERB ( ( ( 1 << NUM_NO_REVERB_VOICES ) - 1 ) << NUM_NO_REVERB_START_VOICE )

// convert sample rate to pitch setting
#define FREQ2PITCH( x ) ( ( ( float ) ( ( x ) * 100 ) / 48000.0f ) )

#define DMA_CH						0

#if ( ( CORE_0_EFFECTS_END_ADDRESS - CORE_0_EFFECTS_START_ADDRESS ) < ( RAM_NEEDED_FOR_EFFECTS ) )
#error "not enough room for effects"
#endif

int sTargetReverbDepth = 0;
int sCurrentReverbDepth = 0;

// allocated SPU RAM
int SpuRAMUsedTemp = 0;
int SpuRAMUsedPerm = 0;

int LoopingSounds[ NUM_CORES ];
// gotta keep track of voices used, to be able
// to tell whether a voice is free or not...
volatile int VoicesUsed[ NUM_CORES ];
volatile int VoicesCleared[ NUM_CORES ];

// avoid checking voice status more than once per frame:
//uint64	gLastVoiceAvailableUpdate[ NUM_CORES ];
//int		gAvailableVoices[ NUM_CORES ];
float	gSfxVolume = 100.0f;

#define MAX_VOLUME					0x3FFF  // max playstation 2 volume...
#define MAX_REVERB_DEPTH			0x3FFF
#define MAX_PITCH					0x3FFF
#define UNALTERED_PITCH				4096

#define BLOCKHEADER_LOOP_BIT	( 1 << 9 )

// Internal function prototypes:

float 	GetPitchValue( VagHeader *VagHdr );
int 	convertEnd( int inp );
bool	PS2Sfx_LoadVAG( PlatformWaveInfo *pInfo, const char *filename, bool loadPerm, float *samplePitch = NULL );
int		GetCore( int whichVoice );

int		PS2Sfx_PlaySound( PlatformWaveInfo *pInfo, int voiceNumber, float volL = 100.0f, float volR = 100.0f, float pitch = 100.0f );
void	PS2Sfx_StopSound( int voiceNumber );
int		PS2Sfx_GetVoiceStatus( int core );

#define ALLIGN_REQUIREMENT	64 // EE to IOP
#define SIZE_OF_LEADIN		16
#define USE_CLEVER_TRICK_FOR_CLOBBERED_DATA		1

// IOP_MIN_TRANSFER must be a power of 2!!!
#define IOP_MIN_TRANSFER	64	// iop will clobber memory if you're writing above a sound.  we'll restore the
								// potentially clobbered memory by keeping a buffer containing the beginning of
								// the previously loaded perm sound, and copying that to the end of our new
								// data...

#if USE_CLEVER_TRICK_FOR_CLOBBERED_DATA
#define		PAD_TO_PREVENT_CLOBBERING		0
#else
#define		PAD_TO_PREVENT_CLOBBERING		64
#endif

#define FULL_NAME_SIZE		256

int GetMemAvailable( void )
{
	
	return ( MAX_SPU_RAM_AVAILABLE - ( SpuRAMUsedTemp + SpuRAMUsedPerm ) );
}

//--------------------------------------------------------------------------------------------
// Function: int EELoadVAG(char *filename ); //, int *samplePitch)
// Description: Transfers specified filename into SPU2 RAM and returns an address value for
//		for use in the SetVoice() function
// Parameters:  filename to be transfered
//
// Returns: value referencing the vag address in SPU2 RAM
// Notes: This transfers all data in a blocking fashion, but quickly.
//-------------------------------------------------------------------------------------------
bool PS2Sfx_LoadVAG( PlatformWaveInfo *pInfo, const char *filename, bool loadPerm, float *samplePitch)
{
	
	
	uint32 size = 0;				// file size
	int vagAddr = 0;			// VAG address in SPU2 RAM, returned to calling function
	VagHeader vagHeader;
	char fullname[ FULL_NAME_SIZE ]; // sound names can be HUGE...
	unsigned int IOPbuffer = 0; // IOP memory buffer
	
	Dbg_MsgAssert( strlen( filename ) < FULL_NAME_SIZE - 30,( "Increase buffer for full filename..." ));
	sprintf(fullname, "sounds\\vag\\%s.vag", filename);
	//Matt("Loading sound %s\n", fullname);
    
	void *fp = File::Open( fullname, "rb" );
	if ( !fp )
	{
		Dbg_MsgAssert( 0,( "LoadSound: Couldn't find file %s", filename ));
		printf( "load sound failed to find %s\n", filename );
		return ( false );
	}
	File::Seek( fp, 0, SEEK_SET );
	if ( SIZE_OF_VAGHEADER != File::Read( &vagHeader, 1, SIZE_OF_VAGHEADER, fp ) )
	{
		// error reading header...
		Dbg_MsgAssert( 0,( "Sorry, young lady, but your VAG is too small" ));
//		Dbg_MsgAssert( 0,( "Not enough bytes to satisfy our VAG" ));
	}
	/*  this fucked things up...
	int endType = 0;
	if ( strncmp( vagHeader.ID, "VAGp", 4 ) == 0 )
		endType = 0;
	else
		endType = 1;
	
	size = ( ( endType == 0 ) ? convertEnd( vagHeader.dataSize ) : vagHeader.dataSize );
	*/
	size = convertEnd( vagHeader.dataSize );

	Dbg_MsgAssert( size > 0,( "There's nothing worse than an empty VAG." ));

	//Dbg_Message( "vag header size %d data size %d", SIZE_OF_VAGHEADER, size );

#if USE_CLEVER_TRICK_FOR_CLOBBERED_DATA
	uint8 *buffer = new uint8[ size + ALLIGN_REQUIREMENT + IOP_MIN_TRANSFER ];
#else
	uint8 *buffer = new uint8[ size + ALLIGN_REQUIREMENT ];
#endif
	File::Seek( fp, SIZE_OF_VAGHEADER, SEEK_SET );
	
	uint8 *allignedBuffer = ( uint8 * )( ( ( int ) buffer + ( ALLIGN_REQUIREMENT - 1 ) ) & ~( ALLIGN_REQUIREMENT - 1 ) );
	
	uint32 readsize = File::Read( allignedBuffer, 1, size, fp );
	if ( size !=  readsize)
	{
		Dbg_Message( "if non-converted size %d is the correct size of file %s, inconsistent VAG format", vagHeader.dataSize + SIZE_OF_VAGHEADER, filename );
		Dbg_MsgAssert( 0,( "VAG %s is %d bytes, not as big as she said it would be (%d bytes)", filename, readsize, size ));
	}
	File::Close( fp );
		
	if ( samplePitch )
	{
		*samplePitch = GetPitchValue( &vagHeader );
	}
//	Dbg_Message( "Pitch value %f", GetPitchValue( &vagHeader ) );

	// make sure we've got room for this sound in RAM:
	// sound data must be 16byte aligned in the SPU, so check for that
	// also, the IOP seems to transfer only in 128 byte chunks, so let's
	// pad the buffer if we're writing a permanent sound (which stacks from
	// the bottom if RAM up) and write the potentially clobbered bytes from
	// the previously loaded permanent sound into the end of the buffer to
	// be transfered (clever huh?)  NOTE: This didn't seem to work without
	// flushing the cache...  Gives everything in memory time to settle I
	// guess?
	
	if ( loadPerm )
	{
#if USE_CLEVER_TRICK_FOR_CLOBBERED_DATA
		int i;
		int overshoot = 0; // need to initialize to zero!
		static uint8 prevPermBuffer[ IOP_MIN_TRANSFER ];
		static int prevVagAddr;
        if ( !SpuRAMUsedPerm )
		{
			// don't store nuthin' first time through...
			prevVagAddr = 0;
		}
#endif
		// stack the permanent sounds up from the bottom:
		SpuRAMUsedPerm += ( size + PAD_TO_PREVENT_CLOBBERING );
		if ( ( END_WAVE_DATA_ADDR - SpuRAMUsedPerm ) & 0x0F )
		{
#ifdef __NOPT_ASSERT__
			int oldSpuRAM = SpuRAMUsedPerm;
#endif			
			SpuRAMUsedPerm += ( ( END_WAVE_DATA_ADDR - SpuRAMUsedPerm ) & 0x0F );
			Dbg_MsgAssert( SpuRAMUsedPerm > oldSpuRAM,( "Duh asshole." ));
		}
		vagAddr = END_WAVE_DATA_ADDR - SpuRAMUsedPerm;
		Dbg_MsgAssert( !( vagAddr & 0x0F ),( "Fire Matt please." ));
		
#if USE_CLEVER_TRICK_FOR_CLOBBERED_DATA
		if ( prevVagAddr )
		{
			// don't have to do this if this is the first loaded perm sound...
			// hence condition above...
			
			// find out how much of the previous perm data we'd be writing
			// over, add that to the end of our buffer...
			if ( size & ( IOP_MIN_TRANSFER - 1 ) )
			{
//				Dbg_Message( "size %d over 64-byte boundary", size & ( IOP_MIN_TRANSFER - 1 ) );
				overshoot = ( IOP_MIN_TRANSFER ) - ( size & ( IOP_MIN_TRANSFER - 1 ) );
//				Dbg_Message( "overshoot %d", overshoot );
				int spaceBetween = prevVagAddr - ( vagAddr + size );
//				Dbg_Message( "space between %d", spaceBetween );
				int numBytesClobbered = overshoot - spaceBetween;
				Dbg_MsgAssert( numBytesClobbered <= IOP_MIN_TRANSFER,( "how in the world?" ));
//				Dbg_Message( "numBytesClobbered = %d", numBytesClobbered );
				for ( i = 0; i < numBytesClobbered; i++ )
				{
					// store the previous buffer at the end of our buffer,
					allignedBuffer[ size + i + spaceBetween ] = prevPermBuffer[ i ];
				}
			}
			else
			{
				overshoot = 0;
			}	
			size += overshoot;
			// add on the size of the overshoot, which in the case of permanent sounds
			// has data from a previous sound (which will be clobbered), written in.
//			size += overshoot;
			Dbg_MsgAssert( !( size & ( IOP_MIN_TRANSFER - 1 ) ),( "Fire Matt." ));
			
		}
		for ( i = 0; i < IOP_MIN_TRANSFER; i++ )
		{
			// store the beginning of our new buffer into the static buffer
			// for when the next perm sound loads...
			prevPermBuffer[ i ] = allignedBuffer[ i ];
		}
		prevVagAddr = vagAddr;
#endif		
	} // if ( loadPerm )
	else
	{
		if ( ( SpuRAMUsedTemp + BASE_WAVE_DATA_ADDR ) & 0x0F )
		{
			SpuRAMUsedTemp += 0x10 - ( ( SpuRAMUsedTemp + BASE_WAVE_DATA_ADDR ) & 0x0F );
		}
		vagAddr = BASE_WAVE_DATA_ADDR + SpuRAMUsedTemp;
		SpuRAMUsedTemp += ( size );
	}
	
	// transfer buffer to IOP, it's a DMA operation so FlushCache() to ensure cache is flushed
	FlushCache( 0 );

	// Ken: Instead of borrowing a new chunk of memory from the IOP, (which causes it to often
	// run out cos we're so tight on memory) use the already existing
	// gNonAllignedIopBuffer, used by streams and music etc. They have no need of it at this point.
	//IOPbuffer = ( unsigned int )sceSifAllocIopHeap( size );
	Dbg_MsgAssert(size MAX_SPU_RAM_AVAILABLE )
	{
		Dbg_MsgAssert( 0,( "Our VAG space has been RAMed to the hilt" ));
	}

	if (!CSpuManager::sVoiceTrans(DMA_CH, SD_TRANS_MODE_WRITE | SD_TRANS_BY_DMA, (void *) IOPbuffer, (uint32 *) vagAddr, size))
	{
		Dbg_MsgAssert( 0,( "Failed to Transfer to SPU2 size %d vagAddr %x IOPBuffer addr %x\n", size, vagAddr, IOPbuffer ));
	}

	// Ken: Commented this out because now we're using Pcm::gNonAllignedIopBuffer instead.
	// Now free up IOP and EE memory, check this return value is zero, as documentation (iopservice.txt v1.5) seems to be wrong
	//if( sceSifFreeIopHeap( (void * ) IOPbuffer ) != 0 )
	//{
	//	Dbg_MsgAssert( 0,( "Failed to free IOP memory\n" ));
	//}

	pInfo->vagAddress = vagAddr;
	uint16 blockHeader = *( ( uint16 * ) allignedBuffer + SIZE_OF_LEADIN );
	pInfo->looping = ( blockHeader & BLOCKHEADER_LOOP_BIT ) ? true : false;
	
	delete buffer;
	return ( true );
}

//--------------------------------------------------------------------------------------------
// Function    : float GetPitchValue(char *buffer)
// Description : gets pitch value from VAG header
//
// Parameters  : pointer to VAG header
//
// Returns     : pitch value
// Notes       : 
//-------------------------------------------------------------------------------------------
float GetPitchValue( VagHeader *VagHdr )
{
	
/*	int endType = 0;

	if ( strncmp( VagHdr->ID, "VAGp", 4 ) == 0 )
		endType = 0;
	else
		endType = 1;

	if(endType == 0)
    {*/
		return ( ( FREQ2PITCH( convertEnd( VagHdr->sampleFreq ) ) ) );
/*	}
	else
	{
		return ( FREQ2PITCH( VagHdr->sampleFreq ) );
	}
	return 0;*/
}

#ifdef __USER_MATT__
//--------------------------------------------------------------------------------------------
// Function    : void PrintVAGDetails(char *buffer)
// Description : debug function to print out VAG details
//
// Parameters  : pointer to VAG header
//
// Returns     : 
// Notes       : 
//-------------------------------------------------------------------------------------------
void PrintVAGDetails(char *buffer)
{
	
	VagHeader *VagHdr;
	int i;
	int endType = 0;	// 0 for PC, 1 for MAC

	VagHdr = (VagHeader *)buffer;

	Dbg_Message("Checking for VAGp string ..'");
	for(i=0;i<4;i++)
	{
		Dbg_Message("%c",VagHdr->ID[i]);
	}
	Dbg_Message("' found.\n");

	Dbg_Message("VAG Details\n");
	Dbg_Message("===========\n");

	// check for either little or big endian format (PC or MAC)
	if(strncmp(VagHdr->ID,"VAGp",4) == 0)
		endType = 0;
	else
		endType = 1;

	Dbg_Message("ID         : ");
	for(i=0;i<4;i++)
	{
		Dbg_Message("%c",VagHdr->ID[i]);
	}
	Dbg_Message("\n");

	Dbg_Message("VERSION    : ");
	Dbg_Message("0x%x", (endType == 0) ? convertEnd(VagHdr->version) : VagHdr->version);
	Dbg_Message("\n");

	Dbg_Message("RESERVED   : ");
	for(i=0;i<4;i++)
	{
		Dbg_Message("%x",VagHdr->reserved1[i]);
	}
	Dbg_Message("\n");
		
	Dbg_Message("DATASIZE   : ");
	Dbg_Message("0x%x", (endType == 0) ? convertEnd(VagHdr->dataSize) : VagHdr->dataSize);
	Dbg_Message("\n");

	Dbg_Message("SAMPLEFREQ : ");
	Dbg_Message("0x%x (%d)", (endType == 0) ? convertEnd(VagHdr->sampleFreq) : VagHdr->sampleFreq, (endType == 0) ? convertEnd(VagHdr->sampleFreq) : VagHdr->sampleFreq);
	Dbg_Message("\n");

	Dbg_Message("RESERVED   : ");
	for(i=0;i<12;i++)
	{
		Dbg_Message("%x",VagHdr->reserved2[i]);
	}
	Dbg_Message("\n");

	Dbg_Message("NAME       : ");
	for(i=0;i<16;i++)
	{
		if(VagHdr->name[i] == '\0')
			break;
		Dbg_Message("%c",VagHdr->name[i]);
	}
	Dbg_Message("\n");

	return;
}
#endif

//--------------------------------------------------------------------------------------------
// Function    : int convertEnd(int inp)
// Description : needed for swopping endian values if VAG has been created by PC
//
// Parameters  : 
//
// Returns     : 
// Notes       : 
//-------------------------------------------------------------------------------------------
int convertEnd(int inp)
{
	
	// swap bytes 0 and 4, 1 and 2
	return((inp & 0xFF) << 24) | ((inp & 0xFF00) << 8) | ((inp & 0xFF0000) >> 8) | ((inp & 0xFF000000) >> 24);
}

// Master volumeLevel from 0 to 100 percent...
void SetVolumePlease( float volumeLevel )
{
	
	gSfxVolume = volumeLevel;

	Pcm::PCMAudio_SetStreamGlobalVolume( ( unsigned int ) volumeLevel );
/*
	int vol = ( int )PERCENT( volumeLevel, MAX_VOLUME );
	if ( vol > MAX_VOLUME )
		vol = MAX_VOLUME;
	
	Dbg_MsgAssert( vol >= 0.0f,( "Can't have negative main volume." ));

	int i;
	// set master volume for both cores, both blocking
	for ( i = 0; i < 2; i++ )
	{
		
		if ( !CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_MVOLL, vol ) )
		{
			Dbg_MsgAssert( 0,( "Error Setting Left Vol on core %d\n",i));
		}
		if ( !CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_MVOLR, vol ) )
		{
			Dbg_MsgAssert( 0,( "Error Setting Right Vol on core %d\n",i));
		}
	}*/
} // end of SetVolumePlease( )

void ReverbOff( void )
{
	
	int i;
	sceSdEffectAttr r_attr;

	sTargetReverbDepth = sCurrentReverbDepth = 0;
	// --- set reverb attribute
	r_attr.depth_L  = 0;
	r_attr.depth_R  = 0;
	r_attr.delay    = 0;
	r_attr.feedback = 0;
	r_attr.mode = SD_REV_MODE_OFF;
#if REVERB_ONLY_ON_CORE_0
	i = 0;  // just set reverb on core 0 (not the music core or any sfx on core 1)
#else
	for ( i = 0; i < 2; i++ )
#endif
	{
		// Shut volume first
		if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLL , sTargetReverbDepth ) )
			Dbg_Message("Error setting reverb2\n");
		if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLR , sTargetReverbDepth ) )
			Dbg_Message("Error setting reverb3\n");

		// just turning off reverb on all voices...
		if(!CSpuManager::sSendSdRemote( rSdSetSwitch, i | SD_S_VMIXEL , 0 ) )
			Dbg_Message("Error setting reverb4\n");
		if(!CSpuManager::sSendSdRemote( rSdSetSwitch, i | SD_S_VMIXER , 0 ) )
			Dbg_Message("Error setting reverb5\n");
		if( !CSpuManager::sSendSdRemote( rSdSetEffectAttr, i, (uint32) &r_attr ) )
			Dbg_Message( "Error setting reverb\n" );
	}
}

#define REVERB_STEP ( ( MAX_REVERB_DEPTH / 20 ) + 1 )

void DoReverbFade( void )
{
	int i;
	if ( sTargetReverbDepth != sCurrentReverbDepth )
	{
		if ( sCurrentReverbDepth < sTargetReverbDepth )
		{
			sCurrentReverbDepth += REVERB_STEP;
			if ( sCurrentReverbDepth > sTargetReverbDepth )
			{
				sCurrentReverbDepth = sTargetReverbDepth;
			}
		}
		else
		{
			sCurrentReverbDepth -= REVERB_STEP;
			if ( sCurrentReverbDepth < sTargetReverbDepth )
			{
				sCurrentReverbDepth = sTargetReverbDepth;
				if ( !sTargetReverbDepth )
				{
					ReverbOff( );
				}
			}
		}
		
#if REVERB_ONLY_ON_CORE_0
		i = 0;  // just set reverb on core 0 (not the music core or any sfx on core 1)
#else
		for ( i = 0; i < 2; i++ )
#endif
		{
			if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLL , sCurrentReverbDepth ) )
				Dbg_Message("Error setting reverb2\n");
			
			if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLR , sCurrentReverbDepth ) )
				Dbg_Message("Error setting reverb3\n");
		}
	}
}

void InitReverbAddr( void )
{
#if REVERB_ONLY_ON_CORE_0
	if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 0 | SD_A_ESA, CORE_0_EFFECTS_START_ADDRESS ) )
		Dbg_Message( "Error setting reverb\n" );
	if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 0 | SD_A_EEA, CORE_0_EFFECTS_END_ADDRESS ) )
		Dbg_Message( "Error setting reverb\n" );
#else
	if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 0 | SD_A_ESA, CORE_0_EFFECTS_START_ADDRESS ) )
		Dbg_Message( "Error setting reverb\n" );
	if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 0 | SD_A_EEA, CORE_0_EFFECTS_END_ADDRESS ) )
		Dbg_Message( "Error setting reverb\n" );
		
	if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 1 | SD_A_ESA, CORE_1_EFFECTS_START_ADDRESS ) )
		Dbg_Message( "Error setting reverb\n" );
	if ( !CSpuManager::sSendSdRemote( rSdSetAddr, 1 | SD_A_EEA, CORE_1_EFFECTS_END_ADDRESS ) )
		Dbg_Message( "Error setting reverb\n" );
#endif
}

//--------------------------------------------------------------------------------------------
// Function: void SetReverb(void)
// Description: Sets reverb level and type
// Parameters:  none
//
// Returns:
//-------------------------------------------------------------------------------------------
void SetReverbPlease( float reverbLevel, int reverbType, bool instant )
{
	
	int i;
	sceSdEffectAttr r_attr;

	Dbg_MsgAssert( reverbType >= 0,( "Bad reverb mode." ));
	Dbg_MsgAssert( reverbType < NUM_REVERB_MODES,( "Bad reverb mode..." ));
	
	if ( !reverbLevel )
	{
		sTargetReverbDepth = 0;
		if ( instant )
		{
			ReverbOff( );
		}
	}
	else
	{
		// Calling this in case it wasn't set before.  There is a bug with the SPU reg set code where some
		// messages are lost
		InitReverbAddr();

		sTargetReverbDepth = ( int )PERCENT( MAX_REVERB_DEPTH, reverbLevel );
		
		// --- set reverb attribute
		r_attr.depth_L  = 0x3fff;
		r_attr.depth_R  = 0x3fff;
		r_attr.delay    = 30;
		r_attr.feedback = 200;
		r_attr.mode = ReverbModes[ reverbType ];
#if REVERB_ONLY_ON_CORE_0
		i = 0;
#else		
		for( i = 0; i < 2; i++ )
#endif
		{
			if( !CSpuManager::sSendSdRemote( rSdSetEffectAttr, i, (uint32) &r_attr ) )
				Dbg_Message( "Error setting reverb\n" );
	
			if(!CSpuManager::sSendSdRemote( rSdSetCoreAttr, i | SD_C_EFFECT_ENABLE, 1 ) )
				Dbg_Message("Error setting reverb\n");
			if ( instant )
			{
				if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLL , sTargetReverbDepth ) )
					Dbg_Message("Error setting reverb2\n");
				if(!CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_EVOLR , sTargetReverbDepth ) )
					Dbg_Message("Error setting reverb3\n");
				sCurrentReverbDepth = sTargetReverbDepth;
			}

			uint32 reverb_voices = (i == NUM_NO_REVERB_CORE) ? VOICES_MINUS_NON_REVERB : VOICES_MINUS_STREAMS;
			if(!CSpuManager::sSendSdRemote( rSdSetSwitch, i | SD_S_VMIXEL , reverb_voices ) )
				Dbg_Message("Error setting reverb4\n");
			if(!CSpuManager::sSendSdRemote( rSdSetSwitch, i | SD_S_VMIXER , reverb_voices ) )
				Dbg_Message("Error setting reverb5\n");
			
		}
	}
} // end of SetReverb( )

//--------------------------------------------------------------------------------------------
// Function    : void StopSound(int voiceNumber)
// Description : turns off voice
// Parameters  : voiceNumber - voice to stop
//
// Returns:
// Notes: 
//-------------------------------------------------------------------------------------------
void PS2Sfx_StopSound(int voiceNumber)
{
	//Dbg_Message("Turning off voice number %d", voiceNumber);

	int coreUsed;

	if ( voiceNumber >= NUM_VOICES_PER_CORE )
	{
		voiceNumber -= NUM_VOICES_PER_CORE;
		coreUsed = SD_CORE_1;
	}
	else
	{
		coreUsed = SD_CORE_0;
	}

	if ( !CSpuManager::sSendSdRemote( rSdSetSwitch, coreUsed | SD_S_KOFF , 1 << voiceNumber ) )
		Dbg_MsgAssert( 0,( "Failed to turn off\n" ));

	// turn off looping sound flag (if it's even on...)
	// Garrett: Took out this line so that we don't possibly turn off and on a voice in the same SPU tick.
	//LoopingSounds[ coreUsed ] &= ~( 1 << voiceNumber );
//	SoundsThatWereSwitchedOff[ coreUsed ] |= ( 1 << voiceNumber );
	// might not want this... can't write too often to a voice...
	// Garrett: Took out this line so that we don't possibly turn off and on a voice in the same SPU tick.
	// The only bad thing is this won't be cleared until the next frame.
	//VoicesUsed[ coreUsed ] &= ~( 1 << voiceNumber );
	VoicesCleared[ coreUsed ] |= 1 << voiceNumber;
	return;
}

bool LoadSoundPlease( const char *sfxName, uint32 checksum, PlatformWaveInfo *pInfo, bool loadPerm )
{
	
	Dbg_MsgAssert( pInfo,( "Null pointer to PlatformWaveInfo." ));
	return PS2Sfx_LoadVAG( pInfo, sfxName, loadPerm, &pInfo->pitchAdjustmentForSamplingRate );
} // end of LoadSoundPlease( )

int	GetCore( int voiceNumber )
{
	
	if ( voiceNumber >= NUM_VOICES_PER_CORE )
	{
		voiceNumber -= NUM_VOICES_PER_CORE;
		Dbg_MsgAssert( voiceNumber < NUM_VOICES_PER_CORE,( "Bad voice number." ));
		return ( SD_CORE_1 );
	}
	Dbg_MsgAssert( voiceNumber >= 0,( "Bad voice number." ));
	return ( SD_CORE_0 );
	
} // end of GetCore( )

//void SetVoiceParameters( int voiceNumber, float volL, float volR, float fPitch )
void SetVoiceParameters( int voiceNumber, sVolume *p_vol, float fPitch )
{
	int coreUsed = GetCore( voiceNumber );

	if ( coreUsed == SD_CORE_1 )
	{
		voiceNumber -= NUM_VOICES_PER_CORE;
	}
	Dbg_MsgAssert( voiceNumber < NUM_VOICES_PER_CORE,( "What in tarnation?  Voice out of range." ));

	// adjust for SFX Volume level:
	p_vol->PercentageAdjustment( gSfxVolume );

	int lVol = (int)PERCENT( MAX_VOLUME, p_vol->GetChannelVolume( 0 ));	
	int rVol = (int)PERCENT( MAX_VOLUME, p_vol->GetChannelVolume( 1 ));	

	if ( lVol > MAX_VOLUME )
		lVol = MAX_VOLUME;
	else if ( lVol < -MAX_VOLUME )
		lVol = -MAX_VOLUME;

	if ( rVol > MAX_VOLUME )
		rVol = MAX_VOLUME;
	else if ( rVol < -MAX_VOLUME )
		rVol = -MAX_VOLUME;

	if ( fPitch )
	{
		int pitch = ( int )PERCENT( UNALTERED_PITCH, fPitch );
		if ( pitch > MAX_PITCH )
			pitch = MAX_PITCH;
		if ( pitch <= 0 )
			pitch = 1;
		if ( !CSpuManager::sSendSdRemote( rSdSetParam, coreUsed | ( voiceNumber << 1 ) | SD_VP_PITCH, pitch ) )
			Dbg_Message("Failed pitch setting\n");
	}

	// for phasing effects (of sounds behind you), set the volume
	// to 0x7fff (left channel will be negative if sound is behind
	// us)
	if ( lVol < 0 )
	{
		lVol = 0x7fff + lVol;
	}
	if ( rVol < 0 )		// Just in case we start phase shifting the right side instead
	{
		rVol = 0x7fff + rVol;
	}
	if ( !CSpuManager::sSendSdRemote( rSdSetParam, coreUsed | ( voiceNumber << 1 ) | SD_VP_VOLL, lVol ) )
		Dbg_Message( "Error1\n" );
	if ( !CSpuManager::sSendSdRemote( rSdSetParam, coreUsed | ( voiceNumber << 1 ) | SD_VP_VOLR, rVol ) )
		Dbg_Message("Error2\n");
} // end of SetVoiceParameters( )

int PlaySoundPlease( PlatformWaveInfo *pInfo, sVolume *p_vol, float pitch, bool no_reverb )
{
	#if NO_SOUND
	return ( -1 );
	#endif

	int voiceAvailableFlags;
	int i;

	if ( no_reverb )
	{
		voiceAvailableFlags = PS2Sfx_GetVoiceStatus( NUM_NO_REVERB_CORE );

		for ( i = NUM_NO_REVERB_START_VOICE; i < ( NUM_NO_REVERB_VOICES + NUM_NO_REVERB_START_VOICE ); i++ )
		{
			if ( voiceAvailableFlags & ( 1 << i ) )
			{
				return ( PS2Sfx_PlaySound( pInfo, i + (NUM_NO_REVERB_CORE * NUM_VOICES_PER_CORE), p_vol->GetChannelVolume( 0 ), p_vol->GetChannelVolume( 1 ), pitch ));
			}
		}

//		Dbg_MsgAssert(0, ("Ran out of NO_REVERB voices"));
		printf("WARNING: Ran out of NO_REVERB voices - using reverb voices\n");
	}

	voiceAvailableFlags = PS2Sfx_GetVoiceStatus( 0 );
	for ( i = 0; i < NUM_VOICES_PER_CORE; i++ )
	{
		if ( voiceAvailableFlags & ( 1 << i ) )
		{
			//gAvailableVoices[ 0 ] &= ~( 1 << i );
			return ( PS2Sfx_PlaySound( pInfo, i, p_vol->GetChannelVolume( 0 ), p_vol->GetChannelVolume( 1 ), pitch ));
		}
	}
	
	// 2nd core ( 1st core busy, all voices used )
	// For now, allow reverb sounds to use non-reverb voices.  They are at the end, anyway
	voiceAvailableFlags = PS2Sfx_GetVoiceStatus( 1 );
	for ( i = 0; i < NUM_VOICES_PER_CORE; i++ )
	{
		if ( voiceAvailableFlags & ( 1 << i ) )
		{
			//gAvailableVoices[ 1 ] &= ~( 1 << i );
			return ( PS2Sfx_PlaySound( pInfo, i + NUM_VOICES_PER_CORE, p_vol->GetChannelVolume( 0 ), p_vol->GetChannelVolume( 1 ), pitch ));
		}
	}

#ifdef	__NOPT_ASSERT__
	CSpuManager::sPrintStatus();
#endif
	Dbg_MsgAssert(0, ("Can't play sound.  Out of voices"));
	return ( -1 );
} // end of PlaySoundPlease( )

void StopSoundPlease( int whichVoice )
{

	
	if ( VoiceIsOn( whichVoice ) )
	{
		PS2Sfx_StopSound( whichVoice );
	}
} // end of StopSoundPlease( )

void PauseSoundsPlease( void )
{
	sVolume silent_volume;
	silent_volume.SetSilent();

	// just turn the volume down on all playing voices...
	for( int i = 0; i < NUM_VOICES; i++ )
	{
		if( VoiceIsOn( i ))
		{
			SetVoiceParameters( i, &silent_volume );
		}
	}
}// end of PauseSounds( )

//--------------------------------------------------------------------------------------------
// Function    : void StopAllSoundFX(void)
// Description : turns off all voices
// Parameters  : 
//
// Returns:
// Notes: 
//-------------------------------------------------------------------------------------------
void StopAllSoundFX( void )
{
	
	int core;
	Pcm::StopMusic( );
	Pcm::StopStreams( );
	for ( core = 0; core < NUM_CORES; core++ )
	{
//		SoundsThatWereSwitchedOff[ core ] = ( ( 1 << NUM_VOICES_PER_CORE ) - 1 );
		VoicesUsed[ core ] = 1;		// So that the KOFF check thinks there are sounds to kill
		VoicesCleared[ core ] = 0;
		LoopingSounds[ core ] = 0;
//		Dbg_Message( "Stopping all sfx -- remind Matt to fix what this is going to do to streams!!!" );
		CSpuManager::sClean();
		CSpuManager::sSendSdRemote( rSdSetSwitch, core | SD_S_KOFF, VOICES_MINUS_STREAMS );
		CSpuManager::sSendSdRemote( rSdSetSwitch, core | SD_S_ENDX, 0 );
		VoicesUsed[ core ] = 0;
	}
	return;
}

//--------------------------------------------------------------------------------------------
// Function    : void PlaySound(int voiceNumber,int pitch,int volume)
// Description : Plays voice
// Parameters  : voiceNumber - voice to play
//		 pitch - pitch to play at
//		 volume - volume to play at
//
// Returns: voice number
//-------------------------------------------------------------------------------------------
int PS2Sfx_PlaySound( PlatformWaveInfo *pInfo, int voiceNumber, float volL, float volR, float pitch )
{
	
	int coreUsed = GetCore( voiceNumber );
	int voiceNumOnCore;

	if ( coreUsed == SD_CORE_1 )
	{
		voiceNumOnCore = voiceNumber - NUM_VOICES_PER_CORE;
	}
	else
		voiceNumOnCore = voiceNumber;

	if ( !CSpuManager::sSendSdRemote( rSdSetAddr, coreUsed | ( voiceNumOnCore << 1 ) | SD_VA_SSA, pInfo->vagAddress ) )
		Dbg_Message( "Error4\n" );
	
	// adjust pitch to account for lower sampling rates:
	pitch = PERCENT( pitch, pInfo->pitchAdjustmentForSamplingRate );
	
	// should be voiceNumber, not voiceNumOnCore:
	sVolume vol;
	vol.SetChannelVolume( 0, volL );
	vol.SetChannelVolume( 1, volR );
	SetVoiceParameters( voiceNumber, &vol, pitch );
	
	if ( !CSpuManager::sSendSdRemote( rSdSetSwitch, coreUsed | SD_S_KON, 0x1 << voiceNumOnCore ) )
		Dbg_Message("Failed setswitch\n");
	
	if ( pInfo->looping )
	{
		LoopingSounds[ coreUsed ] |= ( 1 << voiceNumOnCore );
		//Dbg_Message("Setting looping sound on voice %d core %d", voiceNumOnCore, coreUsed);
		//Dbg_Assert(0);
	}

	// Set the VoicesUsed flag.
	VoicesUsed[ coreUsed ] |= 1 << voiceNumOnCore;
//	printf( "voice %d\n", voiceNumber );
	//Dbg_Message("Voices Used (%x %x) Cleared (%x %x) Looped (%x %x) Voice Status (%x %x)", VoicesUsed[0], VoicesUsed[1], VoicesCleared[0], VoicesCleared[1],
	//																  LoopingSounds[0], LoopingSounds[1], CSpuManager::sGetVoiceStatus(0), CSpuManager::sGetVoiceStatus(1));

	return ( voiceNumber );
} // end of PS2Sfx_PlaySound( )

void CleanUpSoundFX( void )
{
	
	StopAllSoundFX( );
	SpuRAMUsedTemp = 0;
	SetReverbPlease( 0 );
}// end of CleanUpSoundFX( )

#define NUM_VOICES_INCLUDING_STREAMS_PER_CORE 24

//--------------------------------------------------------------------------------------------
// Function    : void InitSound( void )
// Description : initialise sound system
// Parameters  : 
//	
// Returns:
// Notes: 
//--------------------------------------------------------------------------------------------
void InitSoundFX( CSfxManager *p_sfx_manager )
{
	// Set default volume type.
	if( p_sfx_manager )
	{
		p_sfx_manager->SetDefaultVolumeType( VOLUME_TYPE_2_CHANNEL_DOLBYII );
	}
	
	int i, j;
	// set master volume for both cores, both blocking
	for ( i = 0; i < 2; i++ )
	{
		
		if ( !CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_MVOLL, MAX_VOLUME ) )
		{
			Dbg_MsgAssert( 0,( "Error Setting Left Vol on core %d\n",i));
		}
		if ( !CSpuManager::sSendSdRemote( rSdSetParam, i | SD_P_MVOLR, MAX_VOLUME ) )
		{
			Dbg_MsgAssert( 0,( "Error Setting Right Vol on core %d\n",i));
		}
	}
	
//	SetVolumePlease( 100 );
	
	// Sets the reverb address registers
	InitReverbAddr();
			
	CleanUpSoundFX( );

	// Set the envelope info at the end because we sometimes lose messages
	for ( i = 0; i < 2; i++ )
	{
		for ( j = 0; j < NUM_VOICES_INCLUDING_STREAMS_PER_CORE; j++ )
		{
			CSpuManager::sSendSdRemote ( rSdSetParam, i | ( j << 1 ) | SD_VP_ADSR1, 0x00ff );
			CSpuManager::sSendSdRemote ( rSdSetParam, i | ( j << 1 ) | SD_VP_ADSR2, 0x1fc0 );
		}
	}

	return;
}

void PS2Sfx_InitCold( void )
{
	

	CSpuManager::sInit();

//	int i;
//	for ( i = 0; i < NUM_CORES; i++ )
//	{
//		gLastVoiceAvailableUpdate[ i ] = 0;
//		gAvailableVoices[ i ] = 0;
//	}

} // end of PS2Sfx_Init( )

//--------------------------------------------------------------------------------------------
// Function    : int GetVoiceStatus( int core )
// Description : returns bitmask showing which voices are available
// Parameters  : core - SPU core
//	
// Returns     : integer value which is the bitmask for voices
//--------------------------------------------------------------------------------------------
int PS2Sfx_GetVoiceStatus( int core )
{
	
#if 1
	int availableVoices = CSpuManager::sGetVoiceStatus(core);
#else
	int availableVoices;

	if ( gLastVoiceAvailableUpdate[ core ] != Tmr::GetRenderFrame( ) )
	{
		gLastVoiceAvailableUpdate[ core ] = Tmr::GetRenderFrame( );
		CSpuManager::sWaitForIO();
		gAvailableVoices[ core ] = sceSdRemote( 1, rSdGetSwitch, core | SD_S_ENDX );
	}
	// available voices at this point will show all voices that have reached the end...
	availableVoices = gAvailableVoices[ core ];
#endif

	// except we've got to remove looping sounds (they set the ENDX flag first time they reach LOOPEND)
	availableVoices &= ~LoopingSounds[ core ];
	// include the voices that have not ever been used yet (if never used,
	// a voice never has had a chance to set the ENDX register...)
	availableVoices |= ~VoicesUsed[ core ];
	
	return ( availableVoices );
}

bool VoiceIsOn( int whichVoice )
{
	
	int whichCore;
	int voiceAvailableFlags;
		
	if ( whichVoice < NUM_VOICES_PER_CORE )
	{
		whichCore = 0;
	}
	else
	{
		whichCore = 1;
		whichVoice -= NUM_VOICES_PER_CORE;
	}
	
	voiceAvailableFlags = PS2Sfx_GetVoiceStatus( whichCore );
	return ( ( voiceAvailableFlags & ( 1 << whichVoice ) ) ? false : true );
} // end of	VoiceIsOn( )


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PerFrameUpdate( void )
{
	
	DoReverbFade( );
}

/////////////////////////////////////////////////////////////////////////////////
/// CSpuManager

volatile bool					CSpuManager::s_sd_remote_busy;
volatile bool					CSpuManager::s_sd_transferring_voice;
volatile uint32					CSpuManager::s_sd_voice_status[NUM_CORES];
volatile uint32					CSpuManager::s_sd_voice_status_update_frame[NUM_CORES] = { 0, 0 };

uint32							CSpuManager::s_spu_reg_kon[NUM_CORES] = { 0, 0 };
uint32							CSpuManager::s_spu_reg_koff[NUM_CORES] = { 0, 0 };

SSifCmdSoundRequestPacket		CSpuManager::s_sd_remote_commands[CSpuManager::MAX_QUEUED_COMMANDS] __attribute__ ((aligned(16)));
volatile int					CSpuManager::s_sd_remote_command_free_index;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CSpuManager::sInit()
{
	// Init variables
	s_sd_remote_busy = false;
	s_sd_remote_command_free_index = 0;

	if ( sceSdRemoteInit( ) == -1 )
	{
		Dbg_MsgAssert( 0,( "Error Initialising sound\n"));
		return;
	}

	// Initialise IOP side, blocking call
	if ( sceSdRemote( 1, rSdInit, SD_INIT_COLD ) == -1 )
	{
		Dbg_MsgAssert( 0,( "Error Initialising IOP\n"));
		return;
	}

	// Make sure the DMA ID's are 0 so we don't wait for random ID's
	for (int i = 0; i < MAX_QUEUED_COMMANDS; i++)
	{
		s_sd_remote_commands[i].m_request.m_ee_dma_id = 0;
	}

	sceSifAddCmdHandler(SOUND_RESULT_COMMAND, s_result_callback, NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CSpuManager::sClean()
{
	for (int core = 0; core < NUM_CORES; core++)
	{
		s_spu_reg_kon[core] = 0;
		s_spu_reg_koff[core] = 0;
		s_sd_voice_status[core] = 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef	__NOPT_ASSERT__
void			CSpuManager::sPrintStatus()
{
	for (int core = 0; core < NUM_CORES; core++)
	{
		Dbg_Message("Core %d: Voices Used (%x) Cleared (%x) Looped (%x) Voice Status (%x)", core, VoicesUsed[core], VoicesCleared[core],
																		  LoopingSounds[core], sGetVoiceStatus(core));
		Dbg_Message("        Saved KON value (%x) Saved KOFF value (%x)", s_spu_reg_kon[core], s_spu_reg_koff[core]);
	}
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CSpuManager::sWaitForIO()
{
	while (s_sd_remote_busy)
		;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CSpuManager::sUpdateStatus()
{
	//scePrintf("******** Doing CSpuManager::sUpdateStatus() on frame %d\n", Tmr::GetRenderFrame( ));
	for (int core = 0; core < NUM_CORES; core++)
	{
		sSendSdRemote( rSdGetSwitch, core | SD_S_ENDX, 0 );

		// Clear out switches
		//s_spu_reg_kon[core] = 0;
		//s_spu_reg_koff[core] = 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32			CSpuManager::sGetVoiceStatus(int core)
{
	// For now, just go into a wait loop.  In the future, we can probably use the old value
	while (s_sd_voice_status_update_frame[core] != Tmr::GetRenderFrame( ))
		;

	if (s_sd_voice_status_update_frame[core] != Tmr::GetRenderFrame( ))
	{
		Dbg_MsgAssert(0, ("Sound voice status not current: need frame %d but have frame %d", Tmr::GetRenderFrame( ), s_sd_voice_status_update_frame[core]));
	}

	return s_sd_voice_status[core];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CSpuManager::sSendSdRemote(uint16 function, uint16 entry, uint32 value)
{
	return s_queue_new_command(function, entry, value);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CSpuManager::sVoiceTrans(uint16 channel, uint16 mode, void *p_iop_addr, uint32 *p_spu_addr, uint32 size)
{
	Dbg_Assert(!s_sd_transferring_voice);

#if 0
	sceSdRemote(1, rSdVoiceTrans, channel, mode, p_iop_addr, p_spu_addr, size);
	sceSdRemote(1, rSdVoiceTransStatus, channel, SD_TRANS_STATUS_WAIT);
#else
	s_sd_transferring_voice = true;

	// Send transfer command first
	if (!s_queue_new_command(rSdVoiceTrans, channel, mode, (uint32) p_iop_addr, (uint32) p_spu_addr, size))
	{
		return false;
	}

	if (!s_queue_new_command(rSdVoiceTransStatus, channel, SD_TRANS_STATUS_WAIT))
	{
		return false;
	}

	s_wait_for_voice_transfer();
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CSpuManager::s_queue_new_command(uint16 function, uint16 entry, uint32 value, uint32 arg0, uint32 arg1, uint32 arg2)
{
	static int s_num_sound_requests_sent = 0;

	if (function == rSdSetSwitch)
	{
#if 0
		// If we are sending a switch, combine with old settings to make sure we don't overwrite them.
		// Switches are only updated every 1/48000 a second on the SPU.
		value = s_get_switch_value(entry, value);
#endif
		s_update_local_status(entry, value);
	}

	int nextFreeIndex = (s_sd_remote_command_free_index + 1) % MAX_QUEUED_COMMANDS;
	SSifCmdSoundRequestPacket *p_command = &s_sd_remote_commands[s_sd_remote_command_free_index];

	// Wait for old send to complete
	if (p_command->m_request.m_ee_dma_id)
	{
		//Dbg_Assert(0);
		int wait_num = 0;
		while(sceSifDmaStat(p_command->m_request.m_ee_dma_id) >= 0)
		{
			if ((wait_num++ % 1000) == 0)
				scePrintf("Waiting for sound request buffer\n");
		}
	}

	if (function == rSdSetEffectAttr)
	{
		sceSdEffectAttr *p_attr = (sceSdEffectAttr *) value;

		p_command->m_request.m_function	= function;
		p_command->m_request.m_entry		= entry;
		p_command->m_request.m_value		= p_attr->mode;
		p_command->m_request.m_arg_0		= ((uint32) p_attr->depth_L << 16) | (uint32) p_attr->depth_R;
		p_command->m_request.m_arg_1		= p_attr->delay;
		p_command->m_request.m_arg_2		= p_attr->feedback;
	}
	else
	{
		p_command->m_request.m_function	= function;
		p_command->m_request.m_entry		= entry;
		p_command->m_request.m_value		= value;
		p_command->m_request.m_arg_0		= arg0;
		p_command->m_request.m_arg_1		= arg1;
		p_command->m_request.m_arg_2		= arg2;
	}
	p_command->m_request.m_pad[0] = ++s_num_sound_requests_sent;

	// Send the actual command
	//scePrintf("CSpuManager: sending command %x with entry %x and value %x\n", function, entry, value);
	p_command->m_request.m_ee_dma_id = sceSifSendCmd(SOUND_REQUEST_COMMAND, p_command, sizeof(*p_command), 0, 0, 0);

	s_sd_remote_command_free_index = nextFreeIndex;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32			CSpuManager::s_get_switch_value(uint16 entry, uint32 value)
{
	int core = entry & 0x1;
	int switch_reg = entry & 0xFF00;

	uint32 new_value = value;

	switch (switch_reg)
	{
	case SD_S_KON:
		if(s_spu_reg_koff[core] & value)
		{
			//Dbg_MsgAssert(0, ("Turning on a voice that was turned off this frame."));
			Dbg_Message("Turning on a voice that was turned off this frame.");
			s_spu_reg_koff[core] &= ~(s_spu_reg_koff[core] & value);	// turn off those bits
		}

		s_spu_reg_kon[core] |= value;
		new_value = s_spu_reg_kon[core];

		break;

	case SD_S_KOFF:
		if(s_spu_reg_kon[core] & value)
		{
			//Dbg_MsgAssert(0, ("Turning off a voice that was turned on this frame."));
			Dbg_Message("Turning off a voice that was turned on this frame.");
			s_spu_reg_kon[core] &= ~(s_spu_reg_kon[core] & value);		// turn off those bits
		}

		s_spu_reg_koff[core] |= value;
		new_value = s_spu_reg_koff[core];

		break;

	default:
		break;
	}

	return new_value;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CSpuManager::s_update_local_status(uint16 entry, uint32 value)
{
	int core = entry & 0x1;
	int switch_reg = entry & 0xFF00;

	switch (switch_reg)
	{
	case SD_S_KON:
		//Dbg_Message("************ ORing KON%d with %x", core, value); 
		if(s_spu_reg_koff[core] & value)
		{
#ifdef	__NOPT_ASSERT__
			sPrintStatus();
#endif
			Dbg_Message("Core %d: Set KON value (%x)", core, value);
			Dbg_MsgAssert(0, ("Turning on a voice that was turned off this frame."));
			Dbg_Message("Turning on a voice that was turned off this frame.");
			s_spu_reg_koff[core] &= ~(s_spu_reg_koff[core] & value);	// turn off those bits
		}

		s_spu_reg_kon[core] |= value;

		// Make sure the voice is not used again before SPU update is done
		s_sd_voice_status[core] &= ~value;
		break;

	case SD_S_KOFF:
		//Dbg_Message("************ ORing KOFF%d with %x", core, value); 
		if(!(value & ~PS2Sfx_GetVoiceStatus(core)))
		{
#ifdef	__NOPT_ASSERT__
			sPrintStatus();
#endif
			Dbg_MsgAssert(0, ("Can't set KOFF with %x: status %x raw status %x", value, sGetVoiceStatus(core), s_sd_voice_status[core]));
			Dbg_Message("Can't set KOFF with %x: status %x raw status %x", value, sGetVoiceStatus(core), s_sd_voice_status[core]);
		}
		if(s_spu_reg_kon[core] & value)
		{
#ifdef	__NOPT_ASSERT__
			//sPrintStatus();
#endif
			//Dbg_Message("Core %d: Set KOFF value (%x)", core, value);
			//Dbg_MsgAssert(0, ("Turning off a voice that was turned on this frame."));
			Dbg_Message("Turning off a voice that was turned on this frame.");
			s_spu_reg_kon[core] &= ~(s_spu_reg_kon[core] & value);		// turn off those bits
		}

		s_spu_reg_koff[core] |= value;

		// Lets not clear the voice now.  Wait until we get the SPU status break;
		break;

	default:
		break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CSpuManager::s_result_callback(void *p, void *q)
{
	SSifCmdSoundResultPacket *p_result = (SSifCmdSoundResultPacket *) p;

	if (p_result->m_function == rSdVoiceTransStatus)
	{
		s_sd_transferring_voice = false;
	}
	else if (p_result->m_function == rSdGetSwitch)
	{
		int core = p_result->m_entry & 0x1;

		//scePrintf("************ CSpuManager: received result from command %x with entry %x and result %x for core %d\n", p_result->m_function, p_result->m_entry, p_result->m_result, core);

		if ((p_result->m_entry & 0xFF00) == SD_S_ENDX)
		{
			s_sd_voice_status[core] = p_result->m_result;
			s_sd_voice_status_update_frame[core] = Tmr::GetRenderFrame( );

			// We can clear this out now since the status is up-to-date
			VoicesUsed[ core ] &= ~VoicesCleared[ core ];
			//if (LoopingSounds[ core ] & VoicesCleared[ core ])
		//	{
		//		scePrintf("Clearing looping sound voices %x on core %d\n", LoopingSounds[ core ] & VoicesCleared[ core ], core);
		//	}
			LoopingSounds[ core ] &= ~VoicesCleared[ core ];
			VoicesCleared[ core ] = 0;

			// Keep bits that haven't been updated yet
			s_spu_reg_kon[core] &= (s_sd_voice_status[core] | ~VoicesUsed[core]);
			s_spu_reg_koff[core] &= ~(s_sd_voice_status[core] | ~VoicesUsed[core]);

			// And remove any leftover KON voices
			s_sd_voice_status[core] &= ~s_spu_reg_kon[core];
		}
	}

	if (p_result->m_panic)
	{
		Dbg_MsgAssert(0, ("Error on IOP module while processing SPU request %x", p_result->m_function));
	}

	ExitHandler();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CSpuManager::s_wait_for_voice_transfer()
{
	while (s_sd_transferring_voice)
		;
}

} // namespace Sfx


================================================
FILE: Code/Gel/SoundFX/NGPS/p_sfx.h
================================================
/*	Playstation 2 sound support functions.
	mjd	jan 10, 2001
*/
#ifndef __MODULES_P_SFX_H
#define __MODULES_P_SFX_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 

namespace Sfx
{

struct sVolume;
class CSfxManager;

		  
#if NUM_STREAMS > 2
#define NUM_VOICES_PER_CORE		(24 - NUM_STREAMS)  // leave #stream voices per core for streaming audio off the CD and shit...
#else
#define NUM_VOICES_PER_CORE		22  // leave 2 voices per core for streaming audio off the CD and shit...
#endif

#define NUM_CORES				2
#define NUM_VOICES				( NUM_VOICES_PER_CORE * NUM_CORES )

// Non-reverb voices.  Make sure they are always the last voices on the last core, since we presently allow
// reverb sounds to use all voices.
#define NUM_NO_REVERB_VOICES		10
#define NUM_NO_REVERB_CORE			MUSIC_CORE
#define NUM_NO_REVERB_START_VOICE	( NUM_VOICES_PER_CORE - NUM_NO_REVERB_VOICES )

// platform specific info needed per wave (stored in the WaveTable):
struct PlatformWaveInfo{
	int		vagAddress; //PS2 waves played by setting a voice to the address of the wave on the sound chip...	
	bool	looping;
	float	pitchAdjustmentForSamplingRate; // figure out how much to alter the pitch due to lower sampling rates than 48000.
};

// on startup of the game only...
void PS2Sfx_InitCold( void );

void	InitSoundFX( CSfxManager *p_sfx_manager );
void	CleanUpSoundFX( void );
void	StopAllSoundFX( void );
bool	LoadSoundPlease( const char *sfxName, uint32 checksum, PlatformWaveInfo *pInfo, bool loadPerm = 0 );

int		GetMemAvailable( void );

// returns 0 - ( NUM_VOICES - 1 ), or -1 if no voices are free...
int		PlaySoundPlease( PlatformWaveInfo *pInfo, sVolume *p_vol, float pitch = 100.0f, bool no_reverb = false );

void	StopSoundPlease( int whichVoice );

// really just turning down the volume on all playing voices...
// Looping sounds will get the volume adjusted by the owner
// during the next logic loop.
// The non-looping sounds will play (with no volume) then stop.
// This is for when we go into the menus or whatnot...
void	PauseSoundsPlease( void );

// see reverb types in ReverbModes table (p_sfx.cpp), corresponding to reverb.q script file
void	SetReverbPlease( float reverbLevel = 0.0f, int reverbMode = 0, bool instant = false );

// volume from -100 to 100 ( negative if the sound is from behind us. ps2 alters the sound... )
void	SetVolumePlease( float volumeLevel );

// returns true if voice is being used currently.
bool	VoiceIsOn( int whichVoice );

// change parameters on an active voice:
// if pitch parameter is zero, pitch won't be changed (lowest pitch is 1)
void	SetVoiceParameters( int whichVoice, sVolume *p_vol, float pitch = 0.0f );

void	PerFrameUpdate( void );


///////////////////////////////////////////////////////////
// SPU Manager class
// All the SPU communication should go through here instead of sceSdRemote().
// It is asynchronous, so it doesn't stall the EE while waiting for the
// IOP (which can be milliseconds!)
class CSpuManager
{
public:
	static void				sInit();
	static void				sUpdateStatus();				// Called at the beginning of every frame to get the voice status

	static uint32			sGetVoiceStatus(int core);		// Gets the already requested status
	static void				sFlush();
	static volatile bool	sIsBusy();
	static void				sWaitForIO();

	static void				sClean();

#ifdef	__NOPT_ASSERT__
	static void				sPrintStatus();
#endif

	// Transfer functions
	static bool				sSendSdRemote(uint16 function, uint16 entry, uint32 value);
	static bool				sVoiceTrans(uint16 channel, uint16 mode, void *p_iop_addr, uint32 *p_spu_addr, uint32 size);

private:
	// Constants
	enum
	{
		MAX_QUEUED_COMMANDS	= 28,
	};

	static void				s_result_callback(void *p, void *q);	// SifCmd callback

	static bool				s_queue_new_command(uint16 function, uint16 entry, uint32 value, uint32 arg0 = 0, uint32 arg1 = 0, uint32 arg2 = 0);
	static uint32			s_get_switch_value(uint16 entry, uint32 value);
	static void				s_update_local_status(uint16 entry, uint32 value);

	static void				s_wait_for_voice_transfer();

	// Variables
	static volatile bool	s_sd_remote_busy;
	static volatile bool	s_sd_transferring_voice;
	static volatile uint32	s_sd_voice_status[NUM_CORES];
	static volatile uint32	s_sd_voice_status_update_frame[NUM_CORES];

	// Switch settings made in frame.  Because the SPU only reads registers every 1/48000 of a sec, we may have
	// to re-send a switch setting so we don't overwrite an old one.
	static uint32			s_spu_reg_kon[NUM_CORES];
	static uint32			s_spu_reg_koff[NUM_CORES];

	// Store multiple request packets, so we don't have to wait for the last DMA to finish before
	// starting a new one.
	static SSifCmdSoundRequestPacket	s_sd_remote_commands[MAX_QUEUED_COMMANDS] __attribute__ ((aligned(16)));
	static volatile int					s_sd_remote_command_free_index;
};


} // namespace Gel

#endif


================================================
FILE: Code/Gel/SoundFX/Xbox/p_sfx.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL Library												**
**																			**
**	Module:			Sound effects (Sfx)	 									**
**																			**
**	File name:		xbox/p_sfx.cpp											**
**																			**
**	Created by:		01/10/01	-	dc										**
**																			**
**	Description:	XBox Sound effects										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
//#include 

#include 
#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/

int num_buffers = 0;

namespace Sfx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define RIFFCHUNK_FLAGS_VALID   0x00000001

// FourCC definitions
const DWORD FOURCC_RIFF		= 'FFIR';
const DWORD FOURCC_WAVE		= 'EVAW';
const DWORD FOURCC_FORMAT	= ' tmf';
const DWORD FOURCC_DATA		= 'atad';
const DWORD FOURCC_SAMPLER	= 'lpms';

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

struct VoiceEntry
{
	IDirectSoundBuffer*	p_buffer;
	DWORD				default_frequency;
};

typedef struct
{
    WCHAR *         szName;
    DSI3DL2LISTENER ds3dl;
} I3DL2ENVIRONMENT;

/*****************************************************************************
 *
 * Name: RIFFHEADER
 * Desc: For parsing WAV files
 *
 ****************************************************************************/
struct RIFFHEADER
{
	FOURCC  fccChunkId;
	DWORD   dwDataSize;
};


struct SAMPLERCHUNK
{
	DWORD	manufacturer;
	DWORD	product;
	DWORD	sample_period;
	DWORD	MIDI_unity_note;
	DWORD	MIDI_pitch_fraction;
	DWORD	SMPTE_format;
	DWORD	SMPTE_offset;
	DWORD	num_sample_loops;
	DWORD	sampler_data;
};


/*****************************************************************************
 *
 * Name: class CRiffChunk
 * Desc: RIFF chunk utility class
 *
 ****************************************************************************/
class CRiffChunk
{
	FOURCC				m_fccChunkId;       // Chunk identifier
	const CRiffChunk*	m_pParentChunk;     // Parent chunk
	void*				m_hFile;
	DWORD				m_dwDataOffset;     // Chunk data offset
	DWORD				m_dwDataSize;       // Chunk data size
	DWORD				m_dwFlags;          // Chunk flags

	public:

    CRiffChunk();

    // Initialization.
    void	Initialize( FOURCC fccChunkId, const CRiffChunk* pParentChunk, void* hFile );
	bool	Open();
    bool    IsValid( void )		{ return !!( m_dwFlags & RIFFCHUNK_FLAGS_VALID ); }

    // Data
    bool	ReadData( LONG lOffset, VOID* pData, DWORD dwDataSize );

    // Chunk information
    FOURCC  GetChunkId()  { return m_fccChunkId; }
    DWORD   GetDataSize() { return m_dwDataSize; }
};




/*****************************************************************************
 *
 * Name: class CWaveFile
 * Desc: Wave file utility class
 *
 ****************************************************************************/
class CWaveFile
{
	void*		m_hFile;            // File handle
	CRiffChunk	m_RiffChunk;        // RIFF chunk
	CRiffChunk  m_FormatChunk;      // Format chunk
	CRiffChunk  m_DataChunk;        // Data chunk
	CRiffChunk  m_SamplerChunk;		// Sampler chunk, may or may not be present
    
	bool		m_ContainsSamplerChunk;

	public:

    CWaveFile( void );
    ~CWaveFile( void );

    // Initialization
    bool	Open( const char* strFileName );
    void    Close( void );

    // File format
    bool	GetFormat( WAVEFORMATEX* pwfxFormat, DWORD dwFormatSize );

    // File data
    bool	ReadSample( DWORD dwPosition, VOID* pBuffer, DWORD dwBufferSize, DWORD* pdwRead );

    // File properties
    void    GetDuration( DWORD* pdwDuration ) { *pdwDuration = m_DataChunk.GetDataSize(); }

	bool	ContainsLoop( void );
};





/*****************************************************************************
 *
 * Name: class CSound
 * Desc: Encapsulates functionality of a DirectSound buffer.
 *
 ****************************************************************************/
class CXBSound
{
	public:
	XBOXADPCMWAVEFORMAT		m_WaveFormat;		// This encapsulates WAVEFORMATEX.
    DSBUFFERDESC			m_dsbd;
    DWORD					m_dwBufferSize;
	void*					m_pRawData;
	bool					m_Loops;

    bool	Create( const CHAR* strFileName, DWORD dwFlags = 0L );
    bool	Create( const XBOXADPCMWAVEFORMAT* pwfxFormat, DWORD dwFlags, VOID* pBuffer, DWORD dwBytes );
    void    Destroy();

	DWORD	GetSampleRate( void ) const;

    CXBSound();
    ~CXBSound();
};



/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

// Because we don't use voices in the same way as on PS2, this array simulates
// the system. An entry with a NULL pointer means no sound is playing on that
// 'voice', a valid pointer indicates a sound is playing on that 'voice', and
// provides details of the buffer.
static VoiceEntry		VoiceSimulator[NUM_VOICES];
static IDirectSound8*	pDirectSound	= NULL;

static I3DL2ENVIRONMENT reverbEnvironments[] =
{
    { L"Default",           { DSI3DL2_ENVIRONMENT_PRESET_DEFAULT }        },
    { L"Generic",           { DSI3DL2_ENVIRONMENT_PRESET_GENERIC }        },
    { L"Padded Cell",       { DSI3DL2_ENVIRONMENT_PRESET_PADDEDCELL }     },
    { L"Room",              { DSI3DL2_ENVIRONMENT_PRESET_ROOM }           },
    { L"Bathroom",          { DSI3DL2_ENVIRONMENT_PRESET_BATHROOM }       },
    { L"Living Room",       { DSI3DL2_ENVIRONMENT_PRESET_LIVINGROOM }     },
    { L"Stone Room",        { DSI3DL2_ENVIRONMENT_PRESET_STONEROOM }      },
    { L"Auditorium",        { DSI3DL2_ENVIRONMENT_PRESET_AUDITORIUM }     },
    { L"Concert Hall",      { DSI3DL2_ENVIRONMENT_PRESET_CONCERTHALL }    },
    { L"Cave",              { DSI3DL2_ENVIRONMENT_PRESET_CAVE }           },
    { L"Arena",             { DSI3DL2_ENVIRONMENT_PRESET_ARENA }          },
    { L"Hangar",            { DSI3DL2_ENVIRONMENT_PRESET_HANGAR }         },
    { L"Carpeted Hallway",  { DSI3DL2_ENVIRONMENT_PRESET_CARPETEDHALLWAY }},
    { L"Hallway",           { DSI3DL2_ENVIRONMENT_PRESET_HALLWAY }        },
    { L"Stone Corridor",    { DSI3DL2_ENVIRONMENT_PRESET_STONECORRIDOR }  },
    { L"Alley",             { DSI3DL2_ENVIRONMENT_PRESET_ALLEY }          },
    { L"Forest",            { DSI3DL2_ENVIRONMENT_PRESET_FOREST }         },
    { L"City",              { DSI3DL2_ENVIRONMENT_PRESET_CITY }           },
    { L"Mountains",         { DSI3DL2_ENVIRONMENT_PRESET_MOUNTAINS }      },
    { L"Quarry",            { DSI3DL2_ENVIRONMENT_PRESET_QUARRY }         },
    { L"Plain",             { DSI3DL2_ENVIRONMENT_PRESET_PLAIN }          },
    { L"Parking Lot",       { DSI3DL2_ENVIRONMENT_PRESET_PARKINGLOT }     },
    { L"Sewer Pipe",        { DSI3DL2_ENVIRONMENT_PRESET_SEWERPIPE }      },
    { L"Underwater",        { DSI3DL2_ENVIRONMENT_PRESET_UNDERWATER }     },
};



/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

// Global sfx multiplier, percentage.
float					gSfxVolume		= 100.0f;

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


//-----------------------------------------------------------------------------
// Name: CRiffChunk()
// Desc: Object constructor.
//-----------------------------------------------------------------------------
CRiffChunk::CRiffChunk()
{
    // Initialize defaults
    m_fccChunkId   = 0;
    m_pParentChunk = NULL;
    m_hFile        = NULL;
    m_dwDataOffset = 0;
    m_dwDataSize   = 0;
    m_dwFlags      = 0;
}




//-----------------------------------------------------------------------------
// Name: Initialize()
// Desc: Initializes the object
//-----------------------------------------------------------------------------
VOID CRiffChunk::Initialize( FOURCC fccChunkId, const CRiffChunk* pParentChunk, void* hFile )
{
    m_fccChunkId   = fccChunkId;
    m_pParentChunk = pParentChunk;
    m_hFile        = hFile;
}




//-----------------------------------------------------------------------------
// Name: Open()
// Desc: Opens an existing chunk.
//-----------------------------------------------------------------------------
bool CRiffChunk::Open()
{
    RIFFHEADER rhRiffHeader;
    LONG       lOffset = 0;

    // Seek to the first byte of the parent chunk's data section
    if( m_pParentChunk )
    {
        lOffset = m_pParentChunk->m_dwDataOffset;

        // Special case the RIFF chunk
        if( FOURCC_RIFF == m_pParentChunk->m_fccChunkId )
		{
            lOffset += sizeof(FOURCC);
		}
    }
    
    // Read each child chunk header until we find the one we're looking for
//	RwFileFunctions* fs = RwOsGetFileInterface();
    for( ;; )
    {
//		if( fs->rwfseek( m_hFile, lOffset, SEEK_SET ) != 0 )
		File::Seek( m_hFile, lOffset, SEEK_SET );
//		{
//			return false;
//		}

		DWORD dwRead;
//		dwRead = fs->rwfread( &rhRiffHeader, sizeof( rhRiffHeader ), 1, m_hFile );
		dwRead = File::Read( &rhRiffHeader, sizeof( rhRiffHeader ), 1, m_hFile );
		if( dwRead != sizeof( rhRiffHeader ))
		{
            return false;
		}

		// Check if we found the one we're looking for.
		if( m_fccChunkId == rhRiffHeader.fccChunkId )
        {
			// Save the chunk size and data offset.
            m_dwDataOffset = lOffset + sizeof( rhRiffHeader );
            m_dwDataSize   = rhRiffHeader.dwDataSize;

            // Success.
            m_dwFlags |= RIFFCHUNK_FLAGS_VALID;

            return true;
		}
		lOffset += sizeof( rhRiffHeader ) + rhRiffHeader.dwDataSize;
    }
	return false;
}



//-----------------------------------------------------------------------------
// Name: Read()
// Desc: Reads from the file
//-----------------------------------------------------------------------------
bool CRiffChunk::ReadData( LONG lOffset, VOID* pData, DWORD dwDataSize )
{
	// Seek to the offset
//	RwFileFunctions* fs = RwOsGetFileInterface();
//	if( fs->rwfseek( m_hFile, m_dwDataOffset+lOffset, SEEK_SET ) != 0 )
//	if( File::Seek( m_hFile, m_dwDataOffset+lOffset, SEEK_SET ) != 0 )
	File::Seek( m_hFile, m_dwDataOffset + lOffset, SEEK_SET );
//	{
//		return false;
//	}

    // Read from the file
	DWORD dwRead;
//	dwRead = fs->rwfread( pData, dwDataSize, 1, m_hFile );
	dwRead = File::Read( pData, dwDataSize, 1, m_hFile );
	if( dwRead != dwDataSize )
	{
		return false;
	}
	return true;
}




//-----------------------------------------------------------------------------
// Name: CWaveFile()
// Desc: Object constructor.
//-----------------------------------------------------------------------------
CWaveFile::CWaveFile()
{
    m_hFile = NULL;
}




//-----------------------------------------------------------------------------
// Name: ~CWaveFile()
// Desc: Object destructor.
//-----------------------------------------------------------------------------
CWaveFile::~CWaveFile()
{
	Close();
}




//-----------------------------------------------------------------------------
// Name: Open()
// Desc: Initializes the object.
//-----------------------------------------------------------------------------
bool CWaveFile::Open( const char* strFileName )
{
    // If we're already open, close.
    Close();
    
    // Open the file
	m_hFile = File::Open( strFileName, "rb" );

//	RwFileFunctions* fs = RwOsGetFileInterface();
//	m_hFile = fs->rwfopen( strFileName, "rb" );

	if( m_hFile == NULL )
	{
        return false;
	}

	// Initialize the chunk objects.
    m_RiffChunk.Initialize( FOURCC_RIFF, NULL, m_hFile );
    m_FormatChunk.Initialize( FOURCC_FORMAT, &m_RiffChunk, m_hFile );
    m_DataChunk.Initialize( FOURCC_DATA, &m_RiffChunk, m_hFile );
    m_SamplerChunk.Initialize( FOURCC_SAMPLER, &m_RiffChunk, m_hFile );

    bool hr = m_RiffChunk.Open();
    if( !hr )
	{
        return hr;
	}

    hr = m_FormatChunk.Open();
    if( !hr )
	{
        return hr;
	}

    hr = m_DataChunk.Open();
    if( !hr )
	{
        return hr;
	}

	// Not a problem if it doesn't, not every file will contain the sampler chunk.
    m_ContainsSamplerChunk	= m_SamplerChunk.Open();

    // Validate the file type.
    FOURCC fccType;
    hr = m_RiffChunk.ReadData( 0, &fccType, sizeof( fccType ));
    if( !hr )
	{
        return hr;
	}

    if( FOURCC_WAVE != fccType )
        return false;

    return true;
}




//-----------------------------------------------------------------------------
// Name: GetFormat()
// Desc: Gets the wave file format
//-----------------------------------------------------------------------------
bool CWaveFile::GetFormat( WAVEFORMATEX* pwfxFormat, DWORD dwFormatSize )
{
    DWORD dwValidSize = m_FormatChunk.GetDataSize();

    if( NULL == pwfxFormat || 0 == dwFormatSize )
        return false;

    // Read the format chunk into the buffer
    bool hr = m_FormatChunk.ReadData( 0, pwfxFormat, min( dwFormatSize, dwValidSize ));

	if( hr )
	{
	    // Zero out remaining bytes, in case enough bytes were not read
		if( dwFormatSize > dwValidSize )
		{
	        ZeroMemory( (BYTE*)pwfxFormat + dwValidSize, dwFormatSize - dwValidSize );
		}
	}

    return hr;
}




//-----------------------------------------------------------------------------
// Name: ReadSample()
// Desc: Reads data from the audio file.
//-----------------------------------------------------------------------------
bool CWaveFile::ReadSample( DWORD dwPosition, VOID* pBuffer, DWORD dwBufferSize, DWORD* pdwRead )
{                                   
    // Don't read past the end of the data chunk
    DWORD dwDuration;
    GetDuration( &dwDuration );

    if( dwPosition + dwBufferSize > dwDuration )
        dwBufferSize = dwDuration - dwPosition;

    bool hr = true;
    if( dwBufferSize )
        hr = m_DataChunk.ReadData( (LONG)dwPosition, pBuffer, dwBufferSize );

    if( pdwRead )
        *pdwRead = dwBufferSize;

    return hr;
}



//-----------------------------------------------------------------------------
// Name: 
// Desc: 
//-----------------------------------------------------------------------------
bool CWaveFile::ContainsLoop( void )
{
	// Figure out if it is a looping sound.
	if( m_ContainsSamplerChunk )
	{
		SAMPLERCHUNK sc;
		m_SamplerChunk.ReadData( 0, &sc, sizeof( sc ));
		if( sc.num_sample_loops > 0 )
		{
			return true;
		}		
	}
	return false;
}



//-----------------------------------------------------------------------------
// Name: Close()
// Desc: Closes the object
//-----------------------------------------------------------------------------
void CWaveFile::Close( void )
{
    if( m_hFile != NULL )
    {
//		RwFileFunctions* fs = RwOsGetFileInterface();
//		fs->rwfclose( m_hFile );
//		m_hFile = NULL;
		File::Close( m_hFile );
    }
}




//-----------------------------------------------------------------------------
// Name: CXBSound()
// Desc: 
//-----------------------------------------------------------------------------
CXBSound::CXBSound( void )
{
    m_dwBufferSize  = 0L;
	m_pRawData		= NULL;
}




//-----------------------------------------------------------------------------
// Name: ~CXBSound()
// Desc: 
//-----------------------------------------------------------------------------
CXBSound::~CXBSound( void )
{
    Destroy();
}




//-----------------------------------------------------------------------------
// Name: Create()
// Desc: Creates the sound. Sound is buffered to memory allocated internally
//       by DirectSound.
//-----------------------------------------------------------------------------
bool CXBSound::Create( const CHAR* strFileName, DWORD dwFlags )
{
    bool	br;

    // Open the .wav file
    CWaveFile waveFile;
	br = waveFile.Open( strFileName );
    if( !br )
	{
        return false;
	}

    // Get the WAVEFORMAT structure for the .wav file
    br = waveFile.GetFormat( &m_WaveFormat.wfx, sizeof( WAVEFORMATEX ));
    if( !br )
	{
        return false;
	}

	// Required if the sound is Xbox ADPCM, will be ignored otherwise.
	m_WaveFormat.wSamplesPerBlock = 64;

    // Get the size of the .wav file
    waveFile.GetDuration( &m_dwBufferSize );

	// See if the sound is looping.
	m_Loops = waveFile.ContainsLoop();

    // Create the sound buffer
    br = Create( &m_WaveFormat, dwFlags, NULL, m_dwBufferSize );
    if( !br )
	{
        return false;
	}

    // Lock the buffer so it can be filled
    VOID* pLock1 = NULL;
    VOID* pLock2 = NULL;
    DWORD dwLockSize1 = 0L;
    DWORD dwLockSize2 = 0L;

	pLock1 = new char[m_dsbd.dwBufferBytes];

    // Read the wave file data into the buffer
    br = waveFile.ReadSample( 0L, pLock1, m_dsbd.dwBufferBytes, NULL );
    if( !br )
	{
        return false;
	}

	// Store pointer to raw sound data.
    m_pRawData = pLock1;

    return true;
}




//-----------------------------------------------------------------------------
// Name: Create()
// Desc: Creates the sound and tells DirectSound where the sound data will be
//       stored. If pBuffer is NULL, DirectSound handles buffer creation.
//-----------------------------------------------------------------------------
bool CXBSound::Create( const XBOXADPCMWAVEFORMAT* pwfxFormat, DWORD dwFlags, VOID* pBuffer, DWORD dwBytes )
{
	// Setup the sound buffer description.
	ZeroMemory( &m_dsbd, sizeof(DSBUFFERDESC) );
	m_dsbd.dwSize      = sizeof(DSBUFFERDESC);
	m_dsbd.dwFlags     = dwFlags;

	if( pwfxFormat->wfx.wFormatTag == WAVE_FORMAT_XBOX_ADPCM )
	{
		m_dsbd.lpwfxFormat	= (LPWAVEFORMATEX)&m_WaveFormat;
	}
	else
	{
		m_dsbd.lpwfxFormat	= (LPWAVEFORMATEX)&pwfxFormat->wfx;
	}

	// If pBuffer is non-NULL, dwBufferBytes will be zero, which informs
	// DirectSoundCreateBuffer that we will presently be using SetBufferData().
	// Otherwise, we set dwBufferBytes to the size of the WAV data, potentially
	// including alignment bytes.
	if( pBuffer == NULL )
	{
		m_dsbd.dwBufferBytes = ( 0 == m_WaveFormat.wfx.nBlockAlign ) ? dwBytes : 
								 dwBytes - ( dwBytes % m_WaveFormat.wfx.nBlockAlign );
	}

	// If buffer specified, tell DirectSound to use it
	if( pBuffer != NULL )
	{
		// Store pointer to raw sound data.
		m_pRawData = pBuffer;
	}

    return true;
}




//-----------------------------------------------------------------------------
// Name: Destroy()
// Desc: Destroys the resources used by the sound
//-----------------------------------------------------------------------------
VOID CXBSound::Destroy()
{
	if( m_pRawData )
	{
		delete [] m_pRawData;
	}
}



//-----------------------------------------------------------------------------
// Name: GetSampleRate
// Desc: Provides the sample rate
//-----------------------------------------------------------------------------
DWORD CXBSound::GetSampleRate( void ) const
{
	return m_WaveFormat.wfx.nSamplesPerSec;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int getFreeVoice( void )
{
	for( int i = 0; i < NUM_VOICES; ++i )
	{
		if( VoiceSimulator[i].p_buffer == NULL )
		{
			return i;
		}
	}
	return -1;
}



/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void InitSoundFX( CSfxManager *p_sfx_manager )
{
	HRESULT				hr;
    LPDSEFFECTIMAGEDESC	pDesc;
    DSEFFECTIMAGELOC	EffectLoc;

	Dbg_Assert( pDirectSound == NULL );

	// Set default volume type.
	if( p_sfx_manager )
	{
		p_sfx_manager->SetDefaultVolumeType( VOLUME_TYPE_5_CHANNEL_DOLBY5_1 );
	}

	// Initialize the 'voice' array.
	for( int i = 0; i < NUM_VOICES; ++i )
	{
		VoiceSimulator[i].p_buffer = NULL;
	}

	// Create the DirectSound interface.
	hr = DirectSoundCreate( NULL, &pDirectSound, NULL );

//	HANDLE hFile = CreateFile( "d:\\data\\sounds\\bin\\dsstdfx.bin", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL );
	HANDLE hFile = CreateFile( "d:\\data\\sounds\\bin\\skate5fx.bin", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL );
    if( hFile == INVALID_HANDLE_VALUE )
    {
		Dbg_Assert( 0 );
	}
	else
	{
		// Determine the size of the scratch image by seeking to the end of the file.
		int size = SetFilePointer( hFile, 0, NULL, FILE_END );
        SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
    
		// Allocate memory to read the scratch image from disk
        BYTE* p_buffer = new BYTE[size];

        // Read the image in
        DWORD dwBytesRead;
        BOOL bResult = ReadFile( hFile, p_buffer, size, &dwBytesRead, 0 );
		if( !bResult )
        {
			Dbg_Assert( 0 );
        }
		else
	    {
	        // Call DSound API to download the image.
//			EffectLoc.dwI3DL2ReverbIndex	= I3DL2_CHAIN_I3DL2_REVERB;
//			EffectLoc.dwCrosstalkIndex		= I3DL2_CHAIN_XTALK;
			EffectLoc.dwI3DL2ReverbIndex	= GraphI3DL2_I3DL2Reverb;
			EffectLoc.dwCrosstalkIndex		= GraphXTalk_XTalk;
			hr = pDirectSound->DownloadEffectsImage( p_buffer, size, &EffectLoc, &pDesc );

			delete[] p_buffer;
        }
    }

	if( hFile != INVALID_HANDLE_VALUE ) 
	{
		CloseHandle( hFile );
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CleanUpSoundFX( void )
{
	// This just resets the SPU RAM pointer on the PS2. However, on Xbox it needs to explicitly
	// delete any sounds that were not marked as permanent at load time.
	StopAllSoundFX();
	SetReverbPlease( 0 );

	for( int i = 0; i < NumWavesInTable; ++i )
	{
		PlatformWaveInfo* p_info = &( WaveTable[PERM_WAVE_TABLE_MAX_ENTRIES + i].platformWaveInfo );
		Dbg_Assert( p_info->p_sound_data );
		delete p_info->p_sound_data;
		p_info->p_sound_data = NULL;
	}
	NumWavesInTable = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void StopAllSoundFX( void )
{
	Pcm::StopMusic();
	Pcm::StopStreams();

	for( int i = 0; i < NUM_VOICES; ++i )
	{
		StopSoundPlease( i );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int GetMemAvailable( void )
{
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool LoadSoundPlease( const char *sfxName, uint32 checksum, PlatformWaveInfo *pInfo, bool loadPerm )
{
	Dbg_Assert( pInfo );

	// Need to append file type. Copy string into a buffer.
	static char name_buffer[256] = "sounds\\pcm\\";
	const int SOUND_PREPEND_START_POS = 11;

	// Check there is room to add the extension.
	int length = strlen( sfxName );
	Dbg_Assert( strlen( sfxName ) <= ( 256 - ( SOUND_PREPEND_START_POS + 4 )));
	strcpy( name_buffer + SOUND_PREPEND_START_POS, sfxName );
	strcpy( name_buffer + SOUND_PREPEND_START_POS + length, ".pcm" );

	CXBSound* p_sound = new CXBSound();

	// Pass the locdefer flag along to prevent this buffer taking up a voice because it will not be played directly.
	bool result = p_sound->Create( name_buffer, DSBCAPS_LOCDEFER );

	if( result )
	{
		pInfo->p_sound_data = p_sound;
		pInfo->looping		= p_sound->m_Loops;
		pInfo->permanent	= loadPerm;
	}
	else
	{
		OutputDebugString( name_buffer );
		OutputDebugString( "\n" );
		pInfo->p_sound_data = NULL;
		pInfo->looping		= false;
		pInfo->permanent	= false;
		delete p_sound;
	}
 	return result;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//int	PlaySoundPlease( PlatformWaveInfo *pInfo, float volL, float volR, float pitch )
int	PlaySoundPlease( PlatformWaveInfo *pInfo, sVolume *p_vol, float pitch )
{
	Dbg_Assert( pInfo );

	if( pInfo->p_sound_data )
	{
		// Obtain a free voice.
		int voice = getFreeVoice();
		if( voice >= 0 )
		{
			// Create the dynamic buffer. Need to set buffer bytes to 0, since we will be setting our own data below.
			DWORD temp_flags	= pInfo->p_sound_data->m_dsbd.dwFlags;
			DWORD buffer_bytes	= pInfo->p_sound_data->m_dsbd.dwBufferBytes;

//			pInfo->p_sound_data->m_dsbd.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY;			
			pInfo->p_sound_data->m_dsbd.dwBufferBytes	= 0;

			// Add in the MixBin setup data.			
			DSMIXBINS			dsmixbins;
			DSMIXBINVOLUMEPAIR	dsmbvp[7];
			pInfo->p_sound_data->m_dsbd.lpMixBins	= &dsmixbins;
			dsmixbins.dwMixBinCount					= 7;
			dsmixbins.lpMixBinVolumePairs			= dsmbvp;
			dsmbvp[0].dwMixBin						= DSMIXBIN_3D_FRONT_LEFT;
			dsmbvp[0].lVolume						= DSBVOLUME_EFFECTIVE_MIN;
			dsmbvp[1].dwMixBin						= DSMIXBIN_3D_FRONT_RIGHT;
			dsmbvp[1].lVolume						= DSBVOLUME_EFFECTIVE_MIN;
			dsmbvp[2].dwMixBin						= DSMIXBIN_FRONT_CENTER;
			dsmbvp[2].lVolume						= DSBVOLUME_EFFECTIVE_MIN;
			dsmbvp[3].dwMixBin						= DSMIXBIN_LOW_FREQUENCY;
			dsmbvp[3].lVolume						= DSBVOLUME_EFFECTIVE_MIN;
			dsmbvp[4].dwMixBin						= DSMIXBIN_3D_BACK_LEFT;
			dsmbvp[4].lVolume						= DSBVOLUME_EFFECTIVE_MIN;
			dsmbvp[5].dwMixBin						= DSMIXBIN_3D_BACK_RIGHT;
			dsmbvp[5].lVolume						= DSBVOLUME_EFFECTIVE_MIN;
			dsmbvp[6].dwMixBin						= DSMIXBIN_I3DL2;
			dsmbvp[6].lVolume						= DSBVOLUME_EFFECTIVE_MIN;

			HRESULT hr = DirectSoundCreateBuffer( &pInfo->p_sound_data->m_dsbd, &VoiceSimulator[voice].p_buffer );

			// Reset various buffer description values.
			pInfo->p_sound_data->m_dsbd.dwFlags			= temp_flags;
			pInfo->p_sound_data->m_dsbd.dwBufferBytes	= buffer_bytes;
			pInfo->p_sound_data->m_dsbd.lpMixBins		= NULL;

			++num_buffers;

			if( hr == DS_OK )
			{
				// Set the buffer to point to the raw sound data.
				hr = VoiceSimulator[voice].p_buffer->SetBufferData( pInfo->p_sound_data->m_pRawData, pInfo->p_sound_data->m_dsbd.dwBufferBytes );
				if( hr == DS_OK )
				{
					// Set the default sample rate.
					VoiceSimulator[voice].default_frequency = pInfo->p_sound_data->GetSampleRate();

					// Play the sound.
					hr = VoiceSimulator[voice].p_buffer->Play( 0, 0, ( pInfo->looping ) ? DSBPLAY_LOOPING : 0 );
					if( hr == DS_OK )
					{
//						SetVoiceParameters( voice, volL, volR, pitch );
						SetVoiceParameters( voice, p_vol, pitch );
						return voice;
					}
				}
			}
		}
	}

	return -1;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void StopSoundPlease( int voice )
{
	if( VoiceSimulator[voice].p_buffer )
	{
		HRESULT hr = VoiceSimulator[voice].p_buffer->Stop();
		Dbg_Assert( hr == DS_OK )
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SetVolumePlease( float volumeLevel )
{
	gSfxVolume = volumeLevel;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PauseSoundsPlease( void )
{
	sVolume vol;
	vol.SetSilent();
	
	// Just turn the volume down on all playing voices...
	for( int i = 0; i < NUM_VOICES; i++ )
	{
		if( VoiceIsOn( i ))
		{
//			SetVoiceParameters( i, 0.0f, 0.0f );
			SetVoiceParameters( i, &vol );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SetReverbPlease( float reverbLevel, int reverbMode, bool instant )
{
	if( reverbMode == 0 )
	{
		// Default to plain.
		reverbMode = 20;
	}
	reverbMode = 20;
	
	HRESULT hr = pDirectSound->SetI3DL2Listener( &reverbEnvironments[reverbMode].ds3dl, DS3D_IMMEDIATE );
	Dbg_Assert( hr == DS_OK );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool VoiceIsOn( int voice )
{
	return( VoiceSimulator[voice].p_buffer != NULL );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//void SetVoiceParameters( int voice, float volL, float volR, float pitch )
void SetVoiceParameters( int voice, sVolume *p_vol, float pitch )
{
	if( VoiceSimulator[voice].p_buffer )
	{
		// This array will hold individual volumes for the five speakers.
		// In order, they are: front left, center, front right, rear right, rear left.
		float	volumes[5];
		int		i_volumes[5], max_i_volume;

		memset( volumes, 0, sizeof( float ) * 5 );
		
		if( p_vol->IsSilent())
		{
			// Pointless doing any more work.
		}
		else
		{
			switch( p_vol->GetVolumeType())
			{
				case VOLUME_TYPE_5_CHANNEL_DOLBY5_1:
				{
					// All the work has been done already - just copy volumes over.
					volumes[0] = p_vol->GetChannelVolume( 0 );
					volumes[1] = p_vol->GetChannelVolume( 4 );
					volumes[2] = p_vol->GetChannelVolume( 1 );
					volumes[3] = p_vol->GetChannelVolume( 2 );
					volumes[4] = p_vol->GetChannelVolume( 3 );
					break;
				}

				case VOLUME_TYPE_BASIC_2_CHANNEL:
				{
					// Simple two channel sound, so just setup as such.
					volumes[0] = p_vol->GetChannelVolume( 0 );
					volumes[1] = ( p_vol->GetChannelVolume( 0 ) + p_vol->GetChannelVolume( 1 )) * 0.5f;
					volumes[2] = p_vol->GetChannelVolume( 1 );
					volumes[3] = p_vol->GetChannelVolume( 0 ) * 0.5f;
					volumes[4] = p_vol->GetChannelVolume( 1 ) * 0.5f;
					break;
				}

				case VOLUME_TYPE_2_CHANNEL_DOLBYII:
				default:
				{
					// Wes should not get any of these.
					Dbg_Assert( 0 );

#					if 0
					// Get the length of the vector here which will be used to multiply out the normalised speaker volumes.
					Mth::Vector test( fabsf( p_vol->GetChannelVolume( 0 )), fabsf( p_vol->GetChannelVolume( 1 )), 0.0f, 0.0f );
					float amplitude = test.Length();
				
					// Look just at the normalized right component to figure the sound angle from Matt's calculations.
					test.Normalize();

					float angle;
					angle	= asinf( test[Y] );
					angle	= ( angle * 2.0f ) - ( Mth::PI * 0.5f );
					angle	= ( p_vol->GetChannelVolume( 0 ) < 0.0f ) ? ( Mth::PI - angle ) : angle;
				
				
					// Now figure volumes based on speaker coverage.
					angle	= Mth::RadToDeg( angle );

					// Left front channel.
					if(( angle >= 225.0f ) || ( angle <= 45.0f ))
					{
						// Because of the discontinuity, shift this angle round to the [0,180] range.
						float shift_angle = angle + 135;
						shift_angle = ( shift_angle >= 360.0f ) ? ( shift_angle - 360.0f ) : shift_angle;
						volumes[0]	= ( shift_angle / 180.0f ) * Mth::PI;
						volumes[0]	= sqrtf( sinf( volumes[0] ));
					}
				
					// Center channel.
					if(( angle >= -60.0f ) && ( angle <= 60.0f ))
					{
						// Scale this into [0,PI] space so we can get smooth fadeout.
						volumes[1]	= (( angle + 60.0f ) / 120.0f ) * Mth::PI;
						volumes[1]	= sqrtf( sinf( volumes[1] ));
					}
		
					// Right front channel.
					if(( angle >= -45.0f ) && ( angle <= 135.0f ))
					{
						// Scale this into [0,PI] space so we can get smooth fadeout.
						volumes[2]	= (( angle + 45.0f ) / 180.0f ) * Mth::PI;
						volumes[2]	= sqrtf( sinf( volumes[2] ));
					}

					// Right rear channel.
					if(( angle >= 45.0f ) && ( angle <= 225.0f ))
					{
						// Scale this into [0,PI] space so we can get smooth fadeout.
						volumes[3]	= (( angle - 45.0f ) / 180.0f ) * Mth::PI;
						volumes[3]	= sqrtf( sinf( volumes[3] ));
					}

					// Left rear channel.
					if(( angle >= 135.0f ) || ( angle <= -45.0f ))
					{
						// Because of the discontinuity, shift this angle round to the [0,180] range.
						float shift_angle = angle + 225;
						shift_angle = ( shift_angle >= 360.0f ) ? ( shift_angle - 360.0f ) : shift_angle;
						volumes[4]	= ( shift_angle / 180.0f ) * Mth::PI;
						volumes[4]	= sqrtf( sinf( volumes[4] ));
					}

					// Scale back up to original amplitude.
					for( int v = 0; v < 5; ++v )
					{
						volumes[v] *= amplitude;
					}
#					endif
					break;
				}
			}
		}

		// Now readjust the relative values...
		for( int v = 0; v < 5; ++v )
		{
			// Adjust for SFX volume level, and scale into limits.
			volumes[v] = fabsf( PERCENT( gSfxVolume, volumes[v] ));

				if( volumes[v] > 100.0f )
					volumes[v] = 100.0f;
		}
		
		// Now figure the attenuation of the sound. To convert to a decibel value, figure the ratio of requested
		// volume versus max volume, then calculate the log10 and multiply by (10 * 2). (The 2 is because sound
		// power varies as square of pressure, and squaring doubles the log value).
		max_i_volume = DSBVOLUME_EFFECTIVE_MIN;
		for( int v = 0; v < 5; ++v )
		{
			if( volumes[v] > 0.0f )
			{
				float attenuation	= 20.0f * log10f( volumes[v] * 0.01f );
				i_volumes[v]		= DSBVOLUME_MAX + (int)( attenuation * 100.0f );
				if( i_volumes[v] < DSBVOLUME_EFFECTIVE_MIN )
					i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;

				if( i_volumes[v] > max_i_volume )
					max_i_volume = i_volumes[v];
			}
			else
			{
				i_volumes[v] = DSBVOLUME_EFFECTIVE_MIN;
			}
		}
		
		// Set individual mixbins for panning.
		DSMIXBINS			dsmixbins;
		DSMIXBINVOLUMEPAIR	dsmbvp[DSMIXBIN_ASSIGNMENT_MAX];

		dsmixbins.dwMixBinCount			= 0;
		dsmixbins.lpMixBinVolumePairs	= dsmbvp;

		if( i_volumes[0] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_FRONT_LEFT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[0];
            dsmixbins.dwMixBinCount++;
		}

		if( i_volumes[1] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_FRONT_CENTER;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[1];
            dsmixbins.dwMixBinCount++;
		}

		if( i_volumes[2] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_FRONT_RIGHT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[2];
            dsmixbins.dwMixBinCount++;
		}

		if( i_volumes[3] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_BACK_RIGHT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[3];
            dsmixbins.dwMixBinCount++;
		}

		if( i_volumes[4] > DSBVOLUME_EFFECTIVE_MIN )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_3D_BACK_LEFT;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= i_volumes[4];
            dsmixbins.dwMixBinCount++;
		}

		if( dsmixbins.dwMixBinCount > 0 )
		{
            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_LOW_FREQUENCY;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= max_i_volume;
            dsmixbins.dwMixBinCount++;

            dsmbvp[dsmixbins.dwMixBinCount].dwMixBin	= DSMIXBIN_I3DL2;
            dsmbvp[dsmixbins.dwMixBinCount].lVolume		= DSBVOLUME_MAX;
            dsmixbins.dwMixBinCount++;
		}

		// Set all speaker volumes.
		HRESULT hr = VoiceSimulator[voice].p_buffer->SetMixBinVolumes( &dsmixbins );

		// Set overall buffer volume.
		if( dsmixbins.dwMixBinCount > 0 )
		{
			hr = VoiceSimulator[voice].p_buffer->SetVolume( DSBVOLUME_MAX );
		}
		else
		{
			hr = VoiceSimulator[voice].p_buffer->SetVolume( DSBVOLUME_EFFECTIVE_MIN );

			// Pointless doing pitch calculation if volume is zero.
			return;
		}

		// Same kind of deal for pitch. Each consecutive doubling (or halving) of the pitch percentage
		// raises (or lowers) the pitch by 1 octave. So we need to figure x, where 2 ^ x = percentage / 100.
		// Can do this with logs...
		int ipitch;

		// Matt appears to be doing an extra adjustment to the pitch on the Ps2 to account for sample rates
		// not at 48,000Hz. Whilst this seems an unnecessary step, we need to do the same adjustment here.
		// Adjust pitch to account for lower sampling rates.
//		pitch = PERCENT( pitch, ((float)VoiceSimulator[voice].default_frequency / 48000.0f ) * 100.0f );
		
		if( pitch != 100.0f )
		{
			ipitch = (int)(( VoiceSimulator[voice].default_frequency * pitch ) * 0.01f );
			if( ipitch > DSBFREQUENCY_MAX )
			{
				ipitch = DSBFREQUENCY_MAX;
			}
			else if( ipitch < DSBFREQUENCY_MIN )
			{
				ipitch = DSBFREQUENCY_MIN;
			}
		}
		else
		{
			ipitch = VoiceSimulator[voice].default_frequency;
		}
		hr = VoiceSimulator[voice].p_buffer->SetFrequency( ipitch );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PerFrameUpdate( void )
{
	DirectSoundDoWork();

	int num_used_voices = 0;

	for( int i = 0; i < NUM_VOICES; ++i )
	{
		if( VoiceSimulator[i].p_buffer )
		{
			DWORD status;
			HRESULT hr = VoiceSimulator[i].p_buffer->GetStatus( &status );
			if(( hr == DS_OK ) && ( status == 0 ))
			{
				// This sound has stopped playing, so we can release the buffer.
				hr = VoiceSimulator[i].p_buffer->Release();

				--num_buffers;

				Dbg_Assert( hr == 0 );

				VoiceSimulator[i].p_buffer = NULL;
			}
			else
			{
				++num_used_voices;
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Sfx


================================================
FILE: Code/Gel/SoundFX/Xbox/p_sfx.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Sound effects  (Sfx)									**
**																			**
**	File name:		gel/													**
**																			**
**	Created: 		01/10/2001	-	dc										**
**																			**
*****************************************************************************/

#ifndef __MODULES_P_SFX_H
#define __MODULES_P_SFX_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
 
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Sfx
{

#define NUM_VOICES_PER_CORE				24
#define NUM_CORES						2
#define NUM_VOICES						( NUM_VOICES_PER_CORE * NUM_CORES )

#define WAVE_TABLE_MAX_ENTRIES			256	// non-permanent sounds
#define PERM_WAVE_TABLE_MAX_ENTRIES		256	// permanent sounds
#define MAX_POSITIONAL_SOUNDS			128

// (From Xbox docs) Even though DirectSound allows you to set the minimum volume to -100 dB,
// the XBox audio hardware clamps to silence at -64 dB. For efficiency, your game should avoid
// making volume calls and calculations where the volume is below -64 dB, particularly when
// keeping 3D sounds on that are intended to sound distant or far away.
#define DSBVOLUME_EFFECTIVE_MIN	-6400


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CXBSound;
class CSfxManager;

// Platform specific info needed per wave (stored in the WaveTable).
struct PlatformWaveInfo
{
	CXBSound*	p_sound_data;
	bool		looping;		// this flag is checked in soundfx.cpp.  might oughtta be added to WaveTableEntry struct...
	bool		permanent;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

extern float	gSfxVolume;
struct sVolume;

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

void	InitSoundFX( CSfxManager *p_sfx_manager );
void	CleanUpSoundFX( void );
void	StopAllSoundFX( void );
bool	LoadSoundPlease( const char *sfxName, uint32 checksum, PlatformWaveInfo *pInfo, bool loadPerm = 0 );

// returns 0 - ( NUM_VOICES - 1 ), or -1 if no voices are free...
//int		PlaySoundPlease( PlatformWaveInfo *pInfo, float volL = 100.0f, float volR = 100.0f, float pitch = 100.0f );
int		PlaySoundPlease( PlatformWaveInfo *pInfo, sVolume *p_vol, float pitch = 100.0f );

void	StopSoundPlease( int whichVoice );

int		GetMemAvailable( void );

// really just turning down the volume on all playing voices...
// Looping sounds will get the volume adjusted by the owner
// during the next logic loop.
// The non-looping sounds will play (with no volume) then stop.
// This is for when we go into the menus or whatnot...
void	PauseSoundsPlease( void );

// see reverb types above...
void	SetReverbPlease( float reverbLevel = 0.0f, int reverbMode = 0, bool instant = false );

// volume from -100 to 100 ( negative if the sound is from behind us. ps2 alters the sound... )
void	SetVolumePlease( float volumeLevel );

// returns true if voice is being used currently.
bool	VoiceIsOn( int whichVoice );

// change parameters on an active voice:
// if pitch parameter is zero, pitch won't be changed (lowest pitch is 1)
void	SetVoiceParameters( int whichVoice, sVolume *p_vol, float pitch = 0.0 );

void	PerFrameUpdate( void );


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Sfx

#endif	// __MODULES_P_SFX_H


================================================
FILE: Code/Gel/SoundFX/Xbox/skate5fx.h
================================================

#pragma once

typedef enum _DSP_IMAGE_skate5fx_FX_INDICES {
    UserStereoEcho_StereoEcho = 0,
    UserI3DL224KReverb_I3DL224KReverb = 1,
    GraphI3DL2_I3DL2Reverb = 2,
    GraphXTalk_XTalk = 3
} DSP_IMAGE_skate5fx_FX_INDICES;

#define DSI3DL2_ENVIRONMENT_UserI3DL224KReverb_I3DL224KReverb -1000, -270, 0.000000, 1.490000, 0.860000, -1204, 0.007000, -4, 0.011000, 100.000000, 100.000000, 5000.000000

#define DSI3DL2_ENVIRONMENT_GraphI3DL2_I3DL2Reverb -1000, -100, 0.000000, 1.490000, 0.830000, -2602, 0.007000, 200, 0.011000, 100.000000, 100.000000, 5000.000000

typedef struct _UserStereoEcho_FX0_StereoEcho_STATE {
    DWORD dwScratchOffset;        // Offset in bytes, of scratch area for this FX
    DWORD dwScratchLength;        // Length in DWORDS, of scratch area for this FX
    DWORD dwYMemoryOffset;        // Offset in DSP WORDS, of Y memory area for this FX
    DWORD dwYMemoryLength;        // Length in DSP WORDS, of Y memory area for this FX
    DWORD dwFlags;                // FX bitfield for various flags. See xgpimage documentation
    DWORD dwInMixbinPtrs[2];      // XRAM offsets in DSP WORDS, of input mixbins
    DWORD dwOutMixbinPtrs[2];     // XRAM offsets in DSP WORDS, of output mixbins
} UserStereoEcho_FX0_StereoEcho_STATE, *LPUserStereoEcho_FX0_StereoEcho_STATE;

typedef const UserStereoEcho_FX0_StereoEcho_STATE *LPCUserStereoEcho_FX0_StereoEcho_STATE;

typedef struct _UserI3DL224KReverb_FX0_I3DL224KReverb_STATE {
    DWORD dwScratchOffset;        // Offset in bytes, of scratch area for this FX
    DWORD dwScratchLength;        // Length in DWORDS, of scratch area for this FX
    DWORD dwYMemoryOffset;        // Offset in DSP WORDS, of Y memory area for this FX
    DWORD dwYMemoryLength;        // Length in DSP WORDS, of Y memory area for this FX
    DWORD dwFlags;                // FX bitfield for various flags. See xgpimage documentation
    DWORD dwInMixbinPtrs[2];      // XRAM offsets in DSP WORDS, of input mixbins
    DWORD dwOutMixbinPtrs[35];     // XRAM offsets in DSP WORDS, of output mixbins
} UserI3DL224KReverb_FX0_I3DL224KReverb_STATE, *LPUserI3DL224KReverb_FX0_I3DL224KReverb_STATE;

typedef const UserI3DL224KReverb_FX0_I3DL224KReverb_STATE *LPCUserI3DL224KReverb_FX0_I3DL224KReverb_STATE;

typedef struct _GraphI3DL2_FX0_I3DL2Reverb_STATE {
    DWORD dwScratchOffset;        // Offset in bytes, of scratch area for this FX
    DWORD dwScratchLength;        // Length in DWORDS, of scratch area for this FX
    DWORD dwYMemoryOffset;        // Offset in DSP WORDS, of Y memory area for this FX
    DWORD dwYMemoryLength;        // Length in DSP WORDS, of Y memory area for this FX
    DWORD dwFlags;                // FX bitfield for various flags. See xgpimage documentation
    DWORD dwInMixbinPtrs[2];      // XRAM offsets in DSP WORDS, of input mixbins
    DWORD dwOutMixbinPtrs[35];     // XRAM offsets in DSP WORDS, of output mixbins
} GraphI3DL2_FX0_I3DL2Reverb_STATE, *LPGraphI3DL2_FX0_I3DL2Reverb_STATE;

typedef const GraphI3DL2_FX0_I3DL2Reverb_STATE *LPCGraphI3DL2_FX0_I3DL2Reverb_STATE;

typedef struct _GraphXTalk_FX0_XTalk_STATE {
    DWORD dwScratchOffset;        // Offset in bytes, of scratch area for this FX
    DWORD dwScratchLength;        // Length in DWORDS, of scratch area for this FX
    DWORD dwYMemoryOffset;        // Offset in DSP WORDS, of Y memory area for this FX
    DWORD dwYMemoryLength;        // Length in DSP WORDS, of Y memory area for this FX
    DWORD dwFlags;                // FX bitfield for various flags. See xgpimage documentation
    DWORD dwInMixbinPtrs[4];      // XRAM offsets in DSP WORDS, of input mixbins
    DWORD dwOutMixbinPtrs[4];     // XRAM offsets in DSP WORDS, of output mixbins
} GraphXTalk_FX0_XTalk_STATE, *LPGraphXTalk_FX0_XTalk_STATE;

typedef const GraphXTalk_FX0_XTalk_STATE *LPCGraphXTalk_FX0_XTalk_STATE;


================================================
FILE: Code/Gel/SoundFX/ngc/p_sfx.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2001 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL Library												**
**																			**
**	Module:			Sound effects (Sfx)	 									**
**																			**
**	File name:		ngc/p_sfx.cpp											**
**																			**
**	Created by:		08/21/01	-	dc										**
**																			**
**	Description:	NGC Sound effects										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include	
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 	"gfx\ngc\nx\nx_init.h"


/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/

namespace Sfx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define	GAMECUBE_START_DSP_DATA_ADDR		( NxNgc::EngineGlobals.aram_dsp )
#define	GAMECUBE_END_DSP_DATA_ADDR			( NxNgc::EngineGlobals.aram_dsp + aram_dsp_size )

#define MAX_VOLUME						0x7FFF  // Max Gamecube volume.
#define MAX_CHANNEL_VOLUME				0x3FFF	// Half of the maximum, so sounds played at 100% will be half max volume.
#define MAX_REVERB_DEPTH				0x3FFF
#define MAX_PITCH						0xFFFFFL
#define UNALTERED_PITCH					0x10000L

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

struct sDSPADPCM
{
	// for WAV header generation during decode
	u32 num_samples;		// total number of RAW samples
	u32 num_adpcm_nibbles;	// number of ADPCM nibbles (including frame headers)
	u32 sample_rate;		// Sample rate, in Hz

	// DSP addressing and decode context
	u16 loop_flag; // 1=LOOPED, 0=NOT LOOPED
	u16 format; // Always 0x0000, for ADPCM
	u32 sa; // Start offset address for looped samples (zero for non-looped)
	u32 ea; // End offset address for looped samples
	u32 ca; // always zero
	u16 coef[16]; // decode coefficients (eight pairs of 16-bit words)

	// DSP decoder initial state
	u16 gain; // always zero for ADPCM
	u16 ps; // predictor/scale
	u16 yn1; // sample history
	u16 yn2; // sample history

	// DSP decoder loop context
	u16 lps; // predictor/scale for loop context
	u16 lyn1; // sample history (n-1) for loop context
	u16 lyn2; // sample history (n-2) for loop context
	u16 pad[10]; // reserved
};



struct VoiceEntry
{
	AXVPB*				p_axvpb;		// Pointer returned by AXAcquireVoice().
	struct sDSPADPCM*	p_dspadpcm;		// Pointer to sample header data in memory.
};



/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

// Because we don't use voices in the same way as on PS2, this array simulates
// the system. An entry with a NULL pointer means no sound is playing on that
// 'voice', a valid pointer indicates a sound is playing on that 'voice', and
// provides details of the playback.
static VoiceEntry		VoiceSimulator[NUM_VOICES];

// Global sfx multiplier, percentage.
static float			gSfxVolume		= 100.0f;

// allocated SPU RAM
int SpuRAMUsedTemp = 0;
int SpuRAMUsedPerm = 0;

// For maintaining pointers to the temporary sound headers, so they can be wiped
// when cleanup between levels is performed.
static void*			tempSoundHeaders[200];
static int				numTempSoundHeaders = 0;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

bool					isStereo = true;

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void AXUserCBack( void )
{
	for( int i = 0; i < NUM_VOICES; ++i )
	{
		if( VoiceSimulator[i].p_axvpb != NULL )
		{
			if( VoiceSimulator[i].p_axvpb->pb.state == AX_PB_STATE_STOP )
			{
				// Playback on this voice has ended, so free up the voice.
				AXFreeVoice( VoiceSimulator[i].p_axvpb );
				VoiceSimulator[i].p_axvpb = NULL;
//				OSReport( "voice %d stopped %d\n", i, OSGetTick() );
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void ax_voice_reacquisition_callback( void* p )
{
	OSReport( "Voice was reacquired\n" );

	for( int i = 0; i < NUM_VOICES; ++i )
	{
		if( VoiceSimulator[i].p_axvpb != NULL )
		{
			if( (void*)( VoiceSimulator[i].p_axvpb ) == p )
			{
				OSReport( "Voice was voice %d\n", i );
				VoiceSimulator[i].p_axvpb = NULL;
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int getFreeVoice( void )
{
	for( int i = 0; i < NUM_VOICES; ++i )
	{
		if( VoiceSimulator[i].p_axvpb == NULL )
		{
			return i;
		}
	}
	return -1;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static bool loadGamecubeDSP( PlatformWaveInfo *pInfo, const char *filename, bool loadPerm, float *samplePitch )
{
	int vagAddr	= 0;			// VAG address in SPU2 RAM, returned to calling function
	bool rv = false;

	void* p_file = File::Open( filename, "rb" );
	if( p_file )
	{
		int		size		= File::GetFileSize( p_file );
		if ( size )
		{
			int		align_size	= ( size + 31 ) & ~31;
			int		data_size	= align_size - 96;
	
			// Grab enough memory to ensure a multiple of 32 bytes data length. To avoid fragmentation, we grab
			// memory for the header copy first.
			char*	p_header_copy	= (char*)Mem::Malloc( 96 );
//			File::Read( p_header_copy, 1, 96, p_file );
			Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
			char*	p_data			= (char*)Mem::Malloc( align_size );
			Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
//	#ifdef	__NOPT_ASSERT__
			int		bytes_read		= File::Read( p_data, 1, size, p_file );
//	#else
//			File::Read( p_data, 1, size, p_file );
			DCFlushRange ( p_data, size );
//	#endif		//	__NOPT_ASSERT__
//			Dbg_Assert( bytes_read == size )
			if ( bytes_read != size )
			{
				File::Close( p_file );
				delete p_data;
				return false;
			}
//			file.close();
	
			// Copy the header to the reserved area.
			memcpy( p_header_copy, p_data, 96 );
	
			// Obtain information from the file header.
			sDSPADPCM*	p_header	= (sDSPADPCM*)p_header_copy;
	//		OSReport( "num samples: %d\n", p_header->num_samples );
	//		OSReport( "sample rate: %d\n", p_header->sample_rate );
	//		OSReport( "loop flag:	%d\n", p_header->loop_flag );
	
			if( loadPerm )
			{
				// Stack the permanent sounds up from the bottom.
				SpuRAMUsedPerm += data_size;
				if(( GAMECUBE_END_DSP_DATA_ADDR - SpuRAMUsedPerm ) & 0x0F )
				{
	#ifdef	__NOPT_ASSERT__
					int oldSpuRAM = SpuRAMUsedPerm;
	#endif		//	__NOPT_ASSERT__
					SpuRAMUsedPerm += (( END_WAVE_DATA_ADDR - SpuRAMUsedPerm ) & 0x0F );
					Dbg_MsgAssert( SpuRAMUsedPerm > oldSpuRAM, ( "Duh asshole." ));
				}
				vagAddr = GAMECUBE_END_DSP_DATA_ADDR - SpuRAMUsedPerm;
				Dbg_MsgAssert( !( vagAddr & 0x0F ), ( "Fire Matt please." ));
			}
			else
			{
				// If a temporary sound, add the details of the header to the cleanup array.
				tempSoundHeaders[numTempSoundHeaders++]	= p_header_copy;
	
				if(( SpuRAMUsedTemp + GAMECUBE_START_DSP_DATA_ADDR ) & 0x0F )
				{
					SpuRAMUsedTemp += 0x10 - ( ( SpuRAMUsedTemp + BASE_WAVE_DATA_ADDR ) & 0x0F );
				}
				vagAddr = GAMECUBE_START_DSP_DATA_ADDR + SpuRAMUsedTemp;
				SpuRAMUsedTemp += data_size;
			}
		
			// Start DMA transfer buffer to sound RAM...
			Dbg_Assert(((unsigned int)p_data & 31 ) == 0 );
			Dbg_Assert(((unsigned int)vagAddr & 31 ) == 0 );
	
			// Read data in chunks & DMA.
//			int toread = size - 96;
//			int offset = 0;
//			while ( toread )
//			{
//#define BUFFER_SIZE 16384
//				char readBuffer[BUFFER_SIZE];
//				if ( toread > BUFFER_SIZE )
//				{
//					// Read a full buffer.
//					File::Read( readBuffer, 1, BUFFER_SIZE, p_file );
//					toread -= BUFFER_SIZE;
//					offset += BUFFER_SIZE;
//					NsDMA::toARAM( vagAddr + offset, readBuffer, BUFFER_SIZE );
//				}
//				else
//				{
//					// Last chunk
//					File::Read( readBuffer, 1, toread, p_file );
//					NsDMA::toARAM( vagAddr + offset, readBuffer, ( toread + 31 ) & ~31 );
//					toread = 0;
//				}
//			}
//

			NsDMA::toARAM( vagAddr, p_data + 96, data_size );
	
			pInfo->headerAddress	= p_header_copy;
			pInfo->vagAddress		= vagAddr;
			pInfo->looping			= ( p_header->loop_flag > 0 );
	
			// Figure pitch adjustment coefficient.
			if( samplePitch )
			{
				*samplePitch = ( (float)p_header->sample_rate * 100.0f ) / 32000.0f;
			}
	
			// Release main RAM version of data.
			Mem::Free( p_data );
	
			rv = true;
		}
		File::Close( p_file );
	}
	else
	{
//		OSReport( 0, "************ failed to load sound: %s\n", filename );
//		while ( 1== 1 );
		//Dbg_MsgAssert( 0, ( "failed to load sound: %s\n", filename ));
	}
	return rv;
}



/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

void GetStreamBufferInfo( int* stream0_base, int* stream1_base, int* stream2_base,
						  int* stream0_size, int* stream1_size, int* stream2_size )
{
	*stream0_base	= NxNgc::EngineGlobals.aram_stream0;
	*stream0_size	= aram_stream0_size;
	*stream1_base	= NxNgc::EngineGlobals.aram_stream1;
	*stream1_size	= aram_stream1_size;
	*stream2_base	= NxNgc::EngineGlobals.aram_stream2;
	*stream2_size	= aram_stream2_size;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PerFrameUpdate( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void InitSoundFX( CSfxManager *p_manager )
{
	// Initialize the 'voice' array.
	for( int i = 0; i < NUM_VOICES; ++i )
	{
		VoiceSimulator[i].p_axvpb = NULL;
	}

	// See whether the unit is in mono or stereo mode.
	isStereo = !( OSGetSoundMode() == OS_SOUND_MODE_MONO );

	// Initialize Audio Interface API.
//	AIInit( NULL );

	// Initialize the AX library, and boot the DSP.
//	AXInit();

	// Set the callback responsible for freeing up voices.
	AXRegisterCallback( AXUserCBack );

	// Zero first 1024 bytes of ARAM - this is where one-shot sounds will end up when they finish.
#	if aram_zero_size != 1024
#	error aram zero buffer size has changed
#	endif
	
	char reset_buffer[1024];
	memset( reset_buffer, 0, 1024 );
	DCFlushRange( reset_buffer, 1024 );
	NsDMA::toARAM( NxNgc::EngineGlobals.aram_zero, reset_buffer, 1024 );

	CleanUpSoundFX();

	// Moved this from sioman.cpp since the pcm stuff for ngc needs the sfx stuff initialised first.
	Pcm::Init();
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void ReInitSoundFX( void )
{
	// Initialize the 'voice' array.
	for( int i = 0; i < NUM_VOICES; ++i )
	{
		VoiceSimulator[i].p_axvpb = NULL;
	}

	// Flush pending ARQ transfers and wait for DMA to finish.
	ARQFlushQueue();
	while( ARGetDMAStatus() != 0 );

	AXQuit();
	AIReset();
	ARQReset();
	ARReset();

	// Initialize Audio Interface API.
	AIInit( NULL );

	// Initialize the AX library, and boot the DSP.
	AXInit();

	// Set the callback responsible for freeing up voices.
	AXRegisterCallback( AXUserCBack );

#ifndef DVDETH
	DTKInit();
	DTKSetRepeatMode( DTK_MODE_NOREPEAT );
#endif		// DVDETH
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool VoiceIsOn( int whichVoice )
{
	if( VoiceSimulator[whichVoice].p_axvpb != NULL )
	{
		// Check it hasn't already been stopped since then it will be dealt with by the 5ms callback.
//		if( VoiceSimulator[whichVoice].p_axvpb->pb.state != AX_PB_STATE_STOP )
		{
			return true;
		}
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//void SetVoiceParameters( int voiceNumber, float volL, float volR, float fPitch )
void	SetVoiceParameters( int whichVoice, sVolume *p_vol, float pitch )
{
	unsigned int rpitch = UNALTERED_PITCH;
	if( pitch != 0.0f )
	{
		rpitch = (unsigned int)PERCENT( UNALTERED_PITCH, pitch );
		if ( rpitch > MAX_PITCH )
			rpitch = MAX_PITCH;
		if ( rpitch <= 0 )
			rpitch = 1;
	}
	
	int old = OSDisableInterrupts();
	if( VoiceSimulator[whichVoice].p_axvpb )
	{
		AXPB* p_pb = &( VoiceSimulator[whichVoice].p_axvpb->pb );
//		Pcm::setDolby( p_pb, VoiceSimulator[voiceNumber].p_axvpb, volL, volR, pitch, true, gSfxVolume );
		//Pcm::setDolby( p_pb, VoiceSimulator[whichVoice].p_axvpb, p_vol->GetChannelVolume( 0 ), p_vol->GetChannelVolume( 1 ), rpitch, true, gSfxVolume );
		if ( p_vol )
		{
			Pcm::setDolby( p_pb, VoiceSimulator[whichVoice].p_axvpb, p_vol->GetChannelVolume( 0 ), p_vol->GetChannelVolume( 1 ), rpitch, true, gSfxVolume );
		}
		else
		{
			Pcm::setDolby( p_pb, VoiceSimulator[whichVoice].p_axvpb, 0.0f, 0.0f, rpitch, true, gSfxVolume );
		}
	}
	OSRestoreInterrupts( old );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool LoadSoundPlease( const char *sfxName, uint32 checksum, PlatformWaveInfo *pInfo, bool loadPerm )
{
	Dbg_Assert( pInfo );

	// Need to append file type. Copy string into a buffer.
	static char name_buffer[256] = "sounds\\dsp\\";
	const int SOUND_PREPEND_START_POS = 11;

	// Check there is room to add the extension.
	int length = strlen( sfxName );
	Dbg_Assert( strlen( sfxName ) <= ( 256 - ( SOUND_PREPEND_START_POS + 4 )));
	strcpy( name_buffer + SOUND_PREPEND_START_POS, sfxName );
	strcpy( name_buffer + SOUND_PREPEND_START_POS + length, ".dsp" );

//	OSReport( "Loading sound: %s\n", name_buffer );

	loadGamecubeDSP( pInfo, name_buffer, loadPerm, &pInfo->pitchAdjustmentForSamplingRate );


	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//int PlaySoundPlease( PlatformWaveInfo *pInfo, float volL, float volR, float pitch )
int PlaySoundPlease( PlatformWaveInfo *pInfo, sVolume *p_vol, float pitch )
{
	//Dbg_Assert( pInfo->headerAddress != NULL );
	
//	Spt::SingletonPtr< Sfx::CSfxManager > sfx_manager;
//	if ( sfx_manager->GetMainVolume() < 1.0f ) return -1;

	int voice = getFreeVoice();
	if( voice == -1 )
	{
		// No voice in our array available.
		return -1;
	}

	// Attempt to grab a voice. Set the priority high if it is a looping sound.
	sDSPADPCM*	p_header	= (sDSPADPCM*)pInfo->headerAddress;
	// HACK!!!
	if( !p_header )
	{
		// No voice available.
		return -1;
	}
	AXVPB*		p_axvpb		= AXAcquireVoice( ( p_header->loop_flag ) ? AX_PRIORITY_NODROP : AX_PRIORITY_LOWEST, ax_voice_reacquisition_callback, 0 );
	if( p_axvpb == NULL )
	{
		// No voice available.
		return -1;
	}

	AXPBADDR		addr;
	AXPBSRC			src;
	AXPBADPCM		adpcm;
	AXPBADPCMLOOP	loop;        

	addr.loopFlag           = p_header->loop_flag ? AXPBADDR_LOOP_ON : AXPBADDR_LOOP_OFF;
	addr.format             = AX_PB_FORMAT_ADPCM;   

	// Convert from byte to nibble address, and split into low and high words.
	u32 start_address		= ( pInfo->vagAddress * 2 ) + 2;
	u32 end_address			= ( pInfo->vagAddress * 2 ) + ( p_header->num_adpcm_nibbles - 1 );

	addr.currentAddressHi   = (u16)( start_address >> 16 );
	addr.currentAddressLo   = (u16)( start_address & 0xFFFFU );
	addr.endAddressHi		= (u16)( end_address >> 16 );
	addr.endAddressLo		= (u16)( end_address & 0xFFFFU );

	if( p_header->loop_flag )
	{
		addr.loopAddressHi      = (u16)( start_address >> 16 );
		addr.loopAddressLo      = (u16)( start_address & 0xFFFFU );
	}
	else
	{
		u32 zero_buffer_address	= NxNgc::EngineGlobals.aram_zero * 2;
		addr.loopAddressHi      = (u16)( zero_buffer_address >> 16 );
		addr.loopAddressLo      = (u16)( zero_buffer_address & 0xFFFFU );
	}

	loop.loop_pred_scale    = p_header->lps;
	loop.loop_yn1           = p_header->lyn1;
	loop.loop_yn2           = p_header->lyn2;

//	uint32 srcBits			= (uint32)(0x00010000 * ((f32)p_header->sample_rate / AX_IN_SAMPLES_PER_SEC));
//	src.ratioHi				= (u16)(srcBits >> 16);
//	src.ratioLo				= (u16)(srcBits & 0xFFFF);
	src.ratioHi             = 1;
	src.ratioLo             = 0;
	src.currentAddressFrac  = 0;
	src.last_samples[0]     = 0;
	src.last_samples[1]     = 0;
	src.last_samples[2]     = 0;
	src.last_samples[3]     = 0;

	memcpy( &adpcm.a[0][0], p_header->coef, sizeof( u16 ) * 16 );
	adpcm.gain              = p_header->gain;
	adpcm.pred_scale        = p_header->ps;
	adpcm.yn1               = p_header->yn1;
	adpcm.yn2               = p_header->yn2;

	AXPBVE ve;
	ve.currentVolume		= 0x7FFF;					// This is max volume.
	ve.currentDelta			= 0x0000;

	AXSetVoiceType( p_axvpb, AX_PB_TYPE_NORMAL );
	
	AXSetVoiceAddr( p_axvpb, &addr );                   // input addressing
	AXSetVoiceAdpcm( p_axvpb, &adpcm );                 // ADPCM coefficients
	AXSetVoiceAdpcmLoop( p_axvpb, &loop );              // ADPCM loop context

	AXSetVoiceVe( p_axvpb, &ve );
	
	AXSetVoiceSrcType( p_axvpb, AX_SRC_TYPE_LINEAR );   // SRC type
	AXSetVoiceSrc( p_axvpb, &src );                     // initial SRC settings

	// Set the voice to run.
	AXSetVoiceState( p_axvpb, AX_PB_STATE_RUN );        
	
	// Fill in voice details.
	VoiceSimulator[voice].p_axvpb		=  p_axvpb;
	VoiceSimulator[voice].p_dspadpcm	=  p_header;
//	OSReport( "voice %d started %d\n", voice, OSGetTick() );

	// Adjust pitch to account for lower sampling rates:
	pitch = PERCENT( pitch, pInfo->pitchAdjustmentForSamplingRate );

	// This takes the place of AxSetVoiceMix().
	SetVoiceParameters( voice, p_vol, pitch );
	
	return voice;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void StopSoundPlease( int whichVoice )
{
	if( VoiceIsOn( whichVoice ))
	{
		AXSetVoiceState( VoiceSimulator[whichVoice].p_axvpb, AX_PB_STATE_STOP );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void StopAllSoundFX( void )
{
//	Pcm::StopMusic();
//	Pcm::StopStreams();

	for( int i = 0; i < NUM_VOICES; i++ )
	{
		StopSoundPlease( i );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SetVolumePlease( float volumeLevel )
{
	// Sets master volume level.
	gSfxVolume = volumeLevel;

//	Pcm::PCMAudio_SetStreamGlobalVolume( ( unsigned int ) volumeLevel );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void PauseSoundsPlease( void )
{
//	sVolume silent_vol;
//	silent_vol.SetSilent();

	// Just turn the volume down on all playing voices.
	for( int i = 0; i < NUM_VOICES; i++ )
	{
		if( VoiceIsOn( i ))
		{
//			SetVoiceParameters( i, 0.0f, 0.0f );
//			SetVoiceParameters( i, &silent_vol );
			SetVoiceParameters( i, NULL );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CleanUpSoundFX( void )
{
	StopAllSoundFX();
	SpuRAMUsedTemp = 0;
	SetReverbPlease( 0 );

	for( int i = 0; i < numTempSoundHeaders; ++i )
	{
		Dbg_Assert( tempSoundHeaders[i] );
		Mem::Free( tempSoundHeaders[i] );
		tempSoundHeaders[i] = NULL;
	}
	numTempSoundHeaders = 0;
}









// Macros and constants:

int ReverbModes[ ] =
{
	// put the default first in this list, since the default param to SetReverb() is 0
	0,
//	SD_REV_MODE_HALL,
//	SD_REV_MODE_ROOM,
//	SD_REV_MODE_STUDIO_A,
//	SD_REV_MODE_STUDIO_B,
//	SD_REV_MODE_STUDIO_C,
//	SD_REV_MODE_SPACE,
//	SD_REV_MODE_ECHO,
//	SD_REV_MODE_DELAY,
//	SD_REV_MODE_PIPE,
};

#define NUM_REVERB_MODES ( ( int )( sizeof( ReverbModes ) / sizeof( int ) ) )

#if NUM_STREAMS > 3
#error tell matt to handle more than 3 streams in sfx.cpp
#endif

#if NUM_STREAMS == 3
#define VOICES_MINUS_STREAMS ( 0xFFFFFF & ~( ( 1 << STREAM_VOICE( 0 ) | ( 1 << STREAM_VOICE( 1 ) ) | ( 1 << STREAM_VOICE( 2 ) ) ) )
#else
#define VOICES_MINUS_STREAMS ( 0xFFFFFF & ~( ( 1 << MUSIC_L_VOICE ) | ( 1 << MUSIC_R_VOICE ) ) )
#endif

// convert sample rate to pitch setting
#define FREQ2PITCH( x ) ( ( float )( ( ( x ) * 100 ) / 48000 ) )

#define DMA_CH						0

#if ( ( CORE_0_EFFECTS_END_ADDRESS - CORE_0_EFFECTS_START_ADDRESS ) < ( RAM_NEEDED_FOR_EFFECTS ) )
#error "not enough room for effects"
#endif

//int LoopingSounds[ NUM_CORES ];
// gotta keep track of voices used, to be able
// to tell whether a voice is free or not...
//int VoicesUsed[ NUM_CORES ];

// avoid checking voice status more than once per frame:
//uint64	gLastVoiceAvailableUpdate[ NUM_CORES ];
//int		gAvailableVoices[ NUM_CORES ];


#define BLOCKHEADER_LOOP_BIT	( 1 << 9 )

// Internal function prototypes:

static bool	loadGamecubeDSP( PlatformWaveInfo *pInfo, const char *filename, bool loadPerm, float *samplePitch = NULL );

int		PS2Sfx_PlaySound( PlatformWaveInfo *pInfo, int voiceNumber, float volL = 100.0f, float volR = 100.0f, float pitch = 100.0f );
void	PS2Sfx_StopSound( int voiceNumber );
int		PS2Sfx_GetVoiceStatus( int core );

#define ALLIGN_REQUIREMENT	64 // EE to IOP
#define SIZE_OF_LEADIN		16
#define USE_CLEVER_TRICK_FOR_CLOBBERED_DATA		1











// IOP_MIN_TRANSFER must be a power of 2!!!
#define IOP_MIN_TRANSFER	64	// iop will clobber memory if you're writing above a sound.  we'll restore the
								// potentially clobbered memory by keeping a buffer containing the beginning of
								// the previously loaded perm sound, and copying that to the end of our new
								// data...

#if USE_CLEVER_TRICK_FOR_CLOBBERED_DATA
#define		PAD_TO_PREVENT_CLOBBERING		0
#else
#define		PAD_TO_PREVENT_CLOBBERING		64
#endif

#define FULL_NAME_SIZE		256





int GetMemAvailable( void )
{
	return ( MAX_SPU_RAM_AVAILABLE - ( SpuRAMUsedTemp + SpuRAMUsedPerm ) );
}














//--------------------------------------------------------------------------------------------
// Function: void SetReverb(void)
// Description: Sets reverb level and type
// Parameters:  none
//
// Returns:
//-------------------------------------------------------------------------------------------
void SetReverbPlease( float reverbLevel, int reverbMode, bool instant = false )
{
//	int i;
//	sceSdEffectAttr r_attr;
//
//	Dbg_MsgAssert( reverbType >= 0,( "Bad reverb mode." ));
//	Dbg_MsgAssert( reverbType < NUM_REVERB_MODES,( "Bad reverb mode..." ));
//	
//	if ( !reverbLevel )
//	{
//		// --- set reverb attribute
//		r_attr.depth_L  = 0;
//		r_attr.depth_R  = 0;
//		r_attr.delay    = 0;
//		r_attr.feedback = 0;
//		r_attr.mode = SD_REV_MODE_OFF;
//#if REVERB_ONLY_ON_CORE_0
//		i = 0;  // just set reverb on core 0 (not the music core or any sfx on core 1)
//#else
//		for ( i = 0; i < 2; i++ )
//#endif
//		{
//			// just turning off reverb on all voices...
//			if(sceSdRemote( 1, rSdSetSwitch, i | SD_S_VMIXEL , 0 ) == -1 )
//				Dbg_Message("Error setting reverb4\n");
//			if(sceSdRemote( 1, rSdSetSwitch, i | SD_S_VMIXER , 0 ) == -1 )
//				Dbg_Message("Error setting reverb5\n");
//			if( sceSdRemote( 1, rSdSetEffectAttr, i, &r_attr ) == -1 )
//				Dbg_Message( "Error setting reverb\n" );
//		}
//	}
//	else
//	{
//		int reverbDepth = ( int )PERCENT( MAX_REVERB_DEPTH, reverbLevel );
//		
//		// --- set reverb attribute
//		r_attr.depth_L  = 0x3fff;
//		r_attr.depth_R  = 0x3fff;
//		r_attr.delay    = 30;
//		r_attr.feedback = 200;
//		r_attr.mode = ReverbModes[ reverbType ];
//#if REVERB_ONLY_ON_CORE_0
//		i = 0;
//#else		
//		for( i = 0; i < 2; i++ )
//#endif
//		{
//			if( sceSdRemote( 1, rSdSetEffectAttr, i, &r_attr ) == -1 )
//				Dbg_Message( "Error setting reverb\n" );
//	
//			if(sceSdRemote( 1, rSdSetCoreAttr, i | SD_C_EFFECT_ENABLE, 1 ) == -1 )
//				Dbg_Message("Error setting reverb\n");
//			if(sceSdRemote( 1, rSdSetParam, i | SD_P_EVOLL , reverbDepth ) == -1 )
//				Dbg_Message("Error setting reverb2\n");
//			if(sceSdRemote( 1, rSdSetParam, i | SD_P_EVOLR , reverbDepth ) == -1 )
//				Dbg_Message("Error setting reverb3\n");;
//			if(sceSdRemote( 1, rSdSetSwitch, i | SD_S_VMIXEL , VOICES_MINUS_STREAMS ) == -1 )
//				Dbg_Message("Error setting reverb4\n");
//			if(sceSdRemote( 1, rSdSetSwitch, i | SD_S_VMIXER , VOICES_MINUS_STREAMS ) == -1 )
//				Dbg_Message("Error setting reverb5\n");
//			
//		}
//	}
} // end of SetReverb( )


































} // namespace Sfx


================================================
FILE: Code/Gel/SoundFX/ngc/p_sfx.h
================================================
/*	Playstation 2 sound support functions.
	mjd	jan 10, 2001
*/
#ifndef __MODULES_P_SFX_H
#define __MODULES_P_SFX_H

#ifndef __CORE_DEFINES_H
#include 
#endif

// Need to include this to get NUM_STREAMS.
#include 

namespace Sfx
{
		  
#define NUM_MUSIC_VOICES		2
#define NUM_VOICES				( 64 - NUM_STREAMS - NUM_MUSIC_VOICES ) // leave some voices per core for streaming audio off the CD and shit...

struct sVolume;

// platform specific info needed per wave (stored in the WaveTable):
struct PlatformWaveInfo
{
	int		vagAddress;		// PS2 waves played by setting a voice to the address of the wave on the sound chip...	
	void*	headerAddress;	// Main memory location of header information.
	bool	looping;
	float	pitchAdjustmentForSamplingRate; // figure out how much to alter the pitch due to lower sampling rates than 48000.
};

void	GetStreamBufferInfo( int* stream0_base, int* stream1_base, int* stream2_base, int* stream0_size, int* stream1_size, int* stream2_size );
class	CSfxManager;

// on startup of the game only...
void PS2Sfx_InitCold( void );

void	AXUserCBack( void );


void	InitSoundFX( CSfxManager *p_sfx_manager );
void	ReInitSoundFX( void );
void	CleanUpSoundFX( void );
void	StopAllSoundFX( void );
bool	LoadSoundPlease( const char *sfxName, uint32 checksum, PlatformWaveInfo *pInfo, bool loadPerm = 0 );

int		GetMemAvailable( void );

// returns 0 - ( NUM_VOICES - 1 ), or -1 if no voices are free...
//int		PlaySoundPlease( PlatformWaveInfo *pInfo, float volL = 100.0f, float volR = 100.0f, float pitch = 100.0f );
int		PlaySoundPlease( PlatformWaveInfo *pInfo, sVolume *p_vol, float pitch = 100.0f );

void	StopSoundPlease( int whichVoice );

extern bool	isStereo;

// really just turning down the volume on all playing voices...
// Looping sounds will get the volume adjusted by the owner
// during the next logic loop.
// The non-looping sounds will play (with no volume) then stop.
// This is for when we go into the menus or whatnot...
void	PauseSoundsPlease( void );

// see reverb types in ReverbModes table (p_sfx.cpp), corresponding to reverb.q script file
void	SetReverbPlease( float reverbLevel = 0.0f, int reverbMode = 0, bool instant = false );

// volume from -100 to 100 ( negative if the sound is from behind us. ps2 alters the sound... )
void	SetVolumePlease( float volumeLevel );

// returns true if voice is being used currently.
bool	VoiceIsOn( int whichVoice );

// change parameters on an active voice:
// if pitch parameter is zero, pitch won't be changed (lowest pitch is 1)
//void	SetVoiceParameters( int whichVoice, float volL, float volR, float pitch = 0.0f );
void	SetVoiceParameters( int whichVoice, sVolume *p_vol, float pitch = 0.0f );

void	PerFrameUpdate( void );


} // namespace Gel

#endif


================================================
FILE: Code/Gel/SoundFX/soundfx.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL Library												**
**																			**
**	Module:			Sound effects (Sfx)	 									**
**																			**
**	File name:		soundfx.cpp												**
**																			**
**	Created by:		01/10/01	-	dc										**
**																			**
**	Description:	SoundFX for the game. These are the sounds that are		**
**					loaded into memory. Streaming SoundFX are in the Music	**
**					modules (music.cpp and p_music.cpp) which should		**
**					actually be called the Streaming module.				**
**																			**
*****************************************************************************/



/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include   // until gametimer is moved into lower level stuff..

#include 
#include 

#include 

#include 
#include 
#include 

// Used by the script debugger code to fill in a structure
// for transmitting to the monitor.exe utility running on the PC.
void ObjectSoundInfo::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to ObjectSoundInfo::GetDebugInfo"));
	
	p_info->AddChecksum("checksum",checksum);
	p_info->AddFloat("dropoffDist",dropoffDist);
	p_info->AddFloat("currentPitch",currentPitch);
	p_info->AddFloat("currentVolume",currentVolume);
	p_info->AddFloat("origPitch",origPitch);
	p_info->AddFloat("origVolume",origVolume);
	p_info->AddFloat("targetPitch",targetPitch);
	p_info->AddFloat("targetVolume",targetVolume);
	p_info->AddFloat("deltaPitch",deltaPitch);
	p_info->AddFloat("deltaVolume",deltaVolume);
	p_info->AddInteger("timeForNextDistCheck",timeForNextDistCheck);
#endif				 
}

namespace Sfx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool NoSoundPlease( void )
{
#	if NO_SOUND_PLEASE
	return true;
#	else
	// Cannot have sound if building for proview, because there is not enough IOP memory
	// (ProView uses up some IOP memory with it's monitor)
	if( Config::GetHardware() == Config::HARDWARE_PS2_PROVIEW )
	{
		return true;
	}
	return false;
#	endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EDropoffFunc	GetDropoffFunctionFromChecksum(uint32 checksum)
{
	switch (checksum)
	{
	case 0xef082878: // standard
		return DROPOFF_FUNC_STANDARD;

	case 0xf221474c: // linear
		return DROPOFF_FUNC_LINEAR;

	case 0x9706104b: // exponential
		return DROPOFF_FUNC_EXPONENTIAL;

	case 0xca64c41: // inv_exponential
		return DROPOFF_FUNC_INV_EXPONENTIAL;

	default:
		Dbg_MsgAssert(0, ("Invalid dropoff function type %s (%x)", Script::FindChecksumName(checksum), checksum));
		return DROPOFF_FUNC_STANDARD;
	}
}



DefineSingletonClass( CSfxManager, "Sound FX" );

static float	DefaultDropoffDist = DEFAULT_DROPOFF_DIST;


VoiceInfo		VoiceInfoTable[NUM_VOICES];

// The entries will be permanent entries, followed by temporary entries...
WaveTableEntry	WaveTable[PERM_WAVE_TABLE_MAX_ENTRIES + WAVE_TABLE_MAX_ENTRIES];

// This structure is used on sounds that aren't looping, but are long enough that they need to be updated for
// volume and pan (like the things pedestrians are saying to each other for example).
struct PositionalSoundEntry
{
	int							uniqueID;
	uint32						checksum;
	uint32						flags;
	Obj::CSoundComponent				*pObj;
	struct PositionalSoundEntry	*pNext;
	struct PositionalSoundEntry	*pPrev;
};

#define POSITIONAL_SOUND_FLAG_OCCUPIED	( 1 << 0 )
#define POSITIONAL_SOUND_FLAG_DOPPLER	( 1 << 1 )

static PositionalSoundEntry				*GpPositionalSounds = NULL;
static PositionalSoundEntry				PositionalSounds[MAX_POSITIONAL_SOUNDS];

int										NumWavesInTable		= 0;
int										NumWavesInPermTable	= 0;
int										NumPositionalSounds = 0;
float									gVolume				= 100.0f;



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVolume::sVolume( void )
{
	// Set the volume type to be the default volume type of the manager.
	Sfx::CSfxManager * p_manager = Sfx::CSfxManager::Instance();
	if( p_manager )
	{
		m_volume_type = p_manager->GetDefaultVolumeType();
	}
	else
	{
		m_volume_type = VOLUME_TYPE_BASIC_2_CHANNEL;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVolume::sVolume( EVolumeType type )
{
	m_volume_type = type;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVolume::sVolume( const sVolume& rhs )
{
	m_volume_type = rhs.m_volume_type;
	
	uint32 num_channels = ( m_volume_type == VOLUME_TYPE_5_CHANNEL_DOLBY5_1 ) ? 5 : 2;
	for( uint32 c = 1; c < num_channels; ++c )
	{
		m_channels[c] = rhs.m_channels[c];
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVolume::~sVolume( void )
{
}


	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sVolume::PercentageAdjustment( float percentage )
{
	uint32 num_channels = ( m_volume_type == VOLUME_TYPE_5_CHANNEL_DOLBY5_1 ) ? 5 : 2;
	for( uint32 c = 0; c < num_channels; ++c )
	{
		m_channels[c] = PERCENT( m_channels[c], percentage );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float sVolume::GetLoudestChannel( void )
{
	float loudest = m_channels[0];
	
	uint32 num_channels = ( m_volume_type == VOLUME_TYPE_5_CHANNEL_DOLBY5_1 ) ? 5 : 2;
	for( uint32 c = 1; c < num_channels; ++c )
	{
		// Allow for phase shifting, which would make a channel negative.
		if( fabsf( m_channels[c] ) > fabsf( loudest ))
			loudest = m_channels[c];
	}
	return loudest;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool sVolume::IsSilent( void )
{
	// All volume types have at least two channels.
	if(( m_channels[0] != 0.0f ) || ( m_channels[1] != 0.0f ))
		return false;
	
	// Check remaining channels for 5.1 type volumes.
	if( m_volume_type == VOLUME_TYPE_5_CHANNEL_DOLBY5_1 )
	{
		if(( m_channels[2] != 0.0f ) || ( m_channels[3] != 0.0f ) || ( m_channels[4] != 0.0f ))
			return false;
	}

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sVolume::SetSilent( void )
{
	// Strictly speaking, we only need to set as many channels silent as the type
	// of the volume requires, but it's probably no less efficient to do it this way.
	memset( m_channels, 0, sizeof( float ) * MAX_CHANNELS );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool sVolume::operator== ( const sVolume& rhs )
{
	if (m_volume_type != rhs.m_volume_type)
	{
		return false;
	}
	
	uint32 num_channels = ( m_volume_type == VOLUME_TYPE_5_CHANNEL_DOLBY5_1 ) ? 5 : 2;
	for( uint32 c = 1; c < num_channels; ++c )
	{
		if (m_channels[c] != rhs.m_channels[c])
		{
			return false;
		}
	}
	
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSfxManager::CSfxManager( void )
{
	if( NoSoundPlease())
		return;

	// Set the default volume type to the most basic available - platform specific code
	// may override this.
	SetDefaultVolumeType( VOLUME_TYPE_BASIC_2_CHANNEL );

	InitSoundFX( this );

	// Clear out the uniqueID table. Only has to be done once (even if module is reset).
	for( int i = 0; i < NUM_VOICES; i++ )
	{
		VoiceInfoTable[i].uniqueID = 0;
		VoiceInfoTable[i].controlID = 0;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSfxManager::~CSfxManager( void )
{
	if( NoSoundPlease())
		return;

	CleanUp();	
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSfxManager::CleanUp( void )
{
	// Call this at the end of a level (or the beginning of the next level before loading sounds, or both).
	if( NoSoundPlease())
		return;

	GpPositionalSounds	= NULL;
	NumPositionalSounds	= 0;
	for( int i = 0; i < MAX_POSITIONAL_SOUNDS; i++ )
	{
		PositionalSounds[i].flags = 0;
	}
	
	// Should stop all sounds and do whatever cleanup necessary so soundfx can be used in the next phase
	// (next level, frontend, whatever).
	CleanUpSoundFX();

	NumWavesInTable		= 0;
	DefaultDropoffDist	= DEFAULT_DROPOFF_DIST;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

bool CSfxManager::IDAvailable(uint32 id)
{
	return !SoundIsPlaying(id, NULL);
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

int CSfxManager::GetVoiceFromID(uint32 id)
{
	int i;

	// Check unique ID's first
	for( i = 0; i < NUM_VOICES; i++ )
	{
		if ((VoiceInfoTable[i].uniqueID == id) && VoiceIsOn(i))
		{
			return i;
		}
	}

	// Now the control ID's
	for( i = 0; i < NUM_VOICES; i++ )
	{
		if ((VoiceInfoTable[i].controlID == id) && VoiceIsOn(i))
		{
			if( Script::GetInteger( 0x6d2e270e /* DebugSoundFxUpdate */, Script::NO_ASSERT ) )
			{
				Dbg_MsgAssert(0, ("Trying to control duplicate sound %s by checksum", Script::FindChecksumName(id)));
			}
			else
			{
				Dbg_Message("ERROR: Trying to control duplicate sound %s by checksum", Script::FindChecksumName(id));
			}
			return i;
		}
	}

	return -1;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

uint32 CSfxManager::GenerateUniqueID(uint32 id)
{
	// Keep incrementing ID until one works.
	while (!IDAvailable(id))
		id++;

	return id;
}


/******************************************************************/
/*																  */
/* Different levels might want to have different dropoff		  */
/* distances.													  */
/*                                                                */
/******************************************************************/
void CSfxManager::SetDefaultDropoffDist( float dist )
{
	if( NoSoundPlease())
		return;

	Dbg_MsgAssert( dist > 0.0f, ( "Can't set dropoff dist to 0 or less." ));

	DefaultDropoffDist = dist;
}



/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/
void CSfxManager::StopAllSounds( void )
{
	if( NoSoundPlease())
		return;
	
	StopAllSoundFX();
	GpPositionalSounds	= NULL;
	NumPositionalSounds	= 0;
	for( int i = 0; i < MAX_POSITIONAL_SOUNDS; i++ )
	{
		PositionalSounds[i].flags = 0;
	}
}



/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/
void CSfxManager::PauseSounds( void )
{
	if( NoSoundPlease())
		return;

	PauseSoundsPlease();
}



/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/
WaveTableEntry * CSfxManager::GetWaveTableIndex( uint32 checksum )
{
	if( NoSoundPlease())
		return NULL;

	// Check through list of perm sounds.
	for( int i = 0; i < NumWavesInPermTable; i++ )
	{
		if ( checksum == WaveTable[i].checksum )
			return &WaveTable[i];
	}

	// Check through list of temp sounds.
	for( int i = 0; i < NumWavesInTable; i++ )
	{
		if( checksum == WaveTable[i + PERM_WAVE_TABLE_MAX_ENTRIES].checksum )
			return &WaveTable[i + PERM_WAVE_TABLE_MAX_ENTRIES];
	}

	// Not found.
	return NULL;	
}



/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/
int CSfxManager::MemAvailable( void )
{
	return GetMemAvailable();
}


/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

bool CSfxManager::PositionalSoundSilenceMode()
{
	// Add any conditions here where you want to set the positional sound
	// to 0.

	// Don't update if loading screen is active
	if( Nx::CLoadScreen::sIsActive() )
	{
		return true;
	}

	// Don't update if the game is paused
	if( Mdl::FrontEnd::Instance()->GamePaused() )
	{
		return true;
	}

#if 0	// TT2808: Seemed to cause more problems than it helped.
	// Don't update if the game is playing a CamAnim
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	if ( skate_mod->GetMovieManager()->IsRolling() )
	{
		return true;
	}
#endif

	//Dbg_Message("Updating sounds frame %d", Tmr::GetRenderFrame());
	return false;
}

/******************************************************************/
/*																  */
/* Returns true if sound is still playing.                        */
/* Adjust the pitch for doppler, and the volume and pan for		  */
/* position.													  */
/*																  */
/******************************************************************/
bool CSfxManager::AdjustObjectSound( Obj::CSoundComponent *pObj, VoiceInfo *pVoiceInfo, Tmr::Time gameTime )
{
	Dbg_Assert( pVoiceInfo );

	ObjectSoundInfo *pInfo = &pVoiceInfo->info;
	
	float dist;

	// Get dropoff distance if sent to Obj_PlaySound. If not, get default (set up in LoadSound).
	float dropoffDist = ( pInfo->dropoffDist == 0.0f ) ? DefaultDropoffDist : pInfo->dropoffDist;

	Gfx::Camera *pCamera = Nx::CViewportManager::sGetClosestCamera( pObj->GetPosition(), &dist );
	if( !pCamera )
	{
		Dbg_Message( "Warning: Positional sound, but no camera... Sound won't be updated." );

		// If there is no camera, just don't update the sound but keep it playing the same as before.
		// (If we return false, the object will delete its sound).
		// We might not be able to find the camera at the start of the level or perhaps when changing levels.
		return true;
	}

	Mth::Vector dropoff_pos;
	Mth::Vector *p_dropoff_pos = NULL;
	if (pObj->GetClosestDropoffPos(pCamera, dropoff_pos))
	{
		p_dropoff_pos = &dropoff_pos;
	}
	// See if the object sound could be turned off (far enough away from the camera).
	else if( pInfo->timeForNextDistCheck <= gameTime )
	{
//		Dbg_Message( "checking if we need to turn %s off", Script::FindChecksumName( pInfo->checksum ) );
		if( dist >= ( dropoffDist + DIST_FROM_DROPOFF_AT_WHICH_TO_STOP_SOUND ))
		{
//			Dbg_Message( "sound %s auto off", Script::FindChecksumName( pInfo->checksum ) );

			// If the sound is looping, let the scripts continue to think the
			// sound is still around and keep pitch/volume adjusted for the action.
			if( pVoiceInfo->waveIndex->platformWaveInfo.looping )
			{
				// Turn the sound off, monitor the sound from the object update.
				pObj->SoundOutOfRange( pInfo, gameTime );
			}

			// Should cause this sound to be stopped and voice cleared!
			return false;
		}
		// Still okay, sound still in range. Continue playing for x more milliseconds.
		pInfo->timeForNextDistCheck = gameTime + 2000;
	}
	
	float pitch	= 100.0f;
	sVolume vol;
	vol.SetSilent();

	// If dist is beyond dropoff dist, leave volume at zero.
	if( /*!PositionalSoundSilenceMode() &&*/ ( p_dropoff_pos || (dist < dropoffDist) ) )
	{
		Mth::Vector obj_pos(pObj->GetClosestEmitterPos(pCamera));

		if( pVoiceInfo->waveIndex->flags & SFX_FLAG_POSITIONAL_UPDATE_WITH_DOPPLER )
		{
			// Even if the object didn't move, the camera could have!
			AdjustPitchForDoppler( &pitch, obj_pos, pObj->GetClosestOldEmitterPos(pCamera), Tmr::FrameLength(), pCamera );
		}
		SetVolumeFromPos( &vol, obj_pos, dropoffDist, pInfo->dropoffFunction, pCamera, p_dropoff_pos );
	}
	
	if( !Config::CD())
	{
		if( Script::GetInteger( 0x6d2e270e /* DebugSoundFxUpdate */, Script::NO_ASSERT ) && !vol.IsSilent())
		{
			Dbg_Message( "Updating sound %s with volL = %f volR = %f", Script::FindChecksumName( pInfo->checksum ), vol.GetChannelVolume(0), vol.GetChannelVolume(1));
		}
	}	

	return UpdateLoopingSound( pVoiceInfo->uniqueID, &vol, pitch );
}




/******************************************************************/
/*																  */
/* Only updates one sound for frame (since it's slow to send	  */
/* commands between the EE and the IOP)                           */
/*																  */
/******************************************************************/
void CSfxManager::UpdatePositionalSounds( void )
{
	PositionalSoundEntry	*pEntry		= GpPositionalSounds;
	PositionalSoundEntry	*pTemp;
	Tmr::Time				gameTime	= Tmr::GetTime();

//	static int				active		= 0;	   // Mick: Only update this one this frame.
//	int						count		= 0;	   // Counter for all the sounds.

//	uint32 start_time = Tmr::GetTimeInUSeconds();	
	while ( pEntry )
	{
		pTemp	= pEntry;
		pEntry	= pEntry->pNext;
		Dbg_MsgAssert( pTemp->flags & POSITIONAL_SOUND_FLAG_OCCUPIED, ( "Sounds in the list should have 'occupied' turned on." ));
		
		// This just filters the current volume/pitch towards the target, and should be done every frame per sound:
		Dbg_MsgAssert( pTemp->uniqueID,( "UniqueID 0 means no sound was played.  Shouldn't have gotten this far." ));
	
		int voiceIndex;
		if( !SoundIsPlaying( pTemp->uniqueID, &voiceIndex ))
		{
			// Wasn't a looping sound, and it's done playing.
			RemovePositionalSoundFromList( pTemp, false );
		}
		else
		{
			VoiceInfo *pVoiceInfo = &VoiceInfoTable[voiceIndex];
			pTemp->pObj->UpdateObjectSound( &pVoiceInfo->info );
			
			// Only do the low-down nitty gritty positional stuff on one sound per frame.
			//if(active == count)
			{
				Dbg_MsgAssert( pTemp->pObj,( "Null object pointer in positional sound." ));

#				ifdef __USE_PROFILER__
				//Sys::CPUProfiler->PushContext( 0, 0, 128 );		// blue = this bit of sound code.
#				endif // __USE_PROFILER__
				
				if( !AdjustObjectSound( pTemp->pObj, pVoiceInfo, gameTime ))
				{
					RemovePositionalSoundFromList( pTemp, true );
				}

#				ifdef __USE_PROFILER__
				//Sys::CPUProfiler->PopContext();
#				endif // __USE_PROFILER__
			}
		}
		//count++;
	}

//	uint32 end_time = Tmr::GetTimeInUSeconds();	
//	if ((end_time - start_time) > 250)
//		Dbg_Message("CSfxManager::UpdatePositionalSounds Frame %d Time %d (%x - %x)", Tmr::GetVblanks(), (end_time - start_time), end_time, start_time);

#if 0		// We can update all sounds now
	// Increment the active sound and wrap based on current count.
	active++;

	// Wrap based on current count.
	if( active >= count )
	{
		active = 0;
	}
#endif
}



/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/
void CSfxManager::RemovePositionalSoundFromList( PositionalSoundEntry *pEntry, bool stopIfPlaying )
{
	pEntry->flags = 0;
	
	// Stop the sound from playing if it is playing.
	if( stopIfPlaying && SoundIsPlaying( pEntry->uniqueID ))
	{
		StopSound( pEntry->uniqueID );
	}

	if( pEntry->pNext )
	{
		pEntry->pNext->pPrev = pEntry->pPrev;
	}

	if ( pEntry->pPrev )
	{
		pEntry->pPrev->pNext = pEntry->pNext;
	}
	else
	{
		GpPositionalSounds = pEntry->pNext;
	}
	NumPositionalSounds--;
}



/******************************************************************/
/*																  */
/* Called from the object's destructor.                           */
/* Stops all soundfx and streams associated with the object.	  */
/*																  */
/******************************************************************/
void CSfxManager::ObjectBeingRemoved( Obj::CSoundComponent *pObj )
{
	PositionalSoundEntry *pEntry = GpPositionalSounds;
	PositionalSoundEntry *pTemp;
	while( pEntry )
	{
		pTemp	= pEntry;
		pEntry	= pEntry->pNext;
		
		if( pTemp->pObj == pObj )
		{
			RemovePositionalSoundFromList( pTemp, true );

			// Keep scanning the list, in case there's more than one...
		}
	}

}



/******************************************************************/
/*																  */
/* Stop a sound on an object.									  */
/*																  */
/******************************************************************/
void CSfxManager::StopObjectSound( Obj::CSoundComponent *pObj, uint32 checksum )
{
	PositionalSoundEntry *pEntry = GpPositionalSounds;
	PositionalSoundEntry *pTemp;
	while( pEntry )
	{
		pTemp	= pEntry;
		pEntry	= pEntry->pNext;
		if( pTemp->pObj == pObj )
		{
			if(( !checksum ) || ( pTemp->checksum == checksum ))
			{
				// This will stop the sound as well as removing it from the list.
				RemovePositionalSoundFromList( pTemp, true );

				// Keep scanning the list, in case there's more than one.
			}
		}
	}
}



/******************************************************************/
/*																  */
/*																  */
/******************************************************************/
void CSfxManager::AddPositionalSoundToUpdateList( uint32 uniqueID, uint32 soundChecksum, Obj::CSoundComponent *pObj )
{
	if( !uniqueID )
	{
		// Sound play was unsuccessful.
		return;
	}
	if( NumPositionalSounds == MAX_POSITIONAL_SOUNDS )
	{
		Dbg_Message( "WARNING: Ran out of positional sound slots.\nIncrease the number of slots, or decrease the number of requests." );
		return;
	}
	NumPositionalSounds++;
	int i;
	for( i = 0; i < MAX_POSITIONAL_SOUNDS; i++ )
	{
		PositionalSoundEntry *pEntry = &PositionalSounds[i];

		if( !( pEntry->flags & POSITIONAL_SOUND_FLAG_OCCUPIED ))
		{
			if( GpPositionalSounds )
			{
				GpPositionalSounds->pPrev = pEntry;
			}
			pEntry->pNext = GpPositionalSounds;
			pEntry->pPrev = NULL;
			GpPositionalSounds = pEntry;
			pEntry->uniqueID = uniqueID;
			pEntry->checksum = soundChecksum;
			pEntry->flags = POSITIONAL_SOUND_FLAG_OCCUPIED;
			pEntry->pObj = pObj;
			return;
		}
	}
	Dbg_MsgAssert( 0, ( "This is impossible." ));
}



/******************************************************************/
/*																  */
/* SFX Name should be the name of the sound, no extension or	  */
/* path. It will be the responsibility of the loader for each	  */
/* platform to add the path and append the extension.			  */
/* This module assumes the sounds are all loaded at the beginning */
/* of a level or module, and all removed at the end.			  */
/*																  */
/******************************************************************/
bool CSfxManager::LoadSound( const char *sfxName,  int flags, float dropoff, float pitch, float volume )
{
	if( NoSoundPlease())
		return false;

	const char	*pNameMinusPath	= sfxName;
	int			stringLength	= strlen( sfxName );
	for( int i = 0; i < stringLength; i++ )
	{
		if(( sfxName[i] == '\\' ) || ( sfxName[i] == '/' ))
			pNameMinusPath = &sfxName[i + 1];
	}
	
	uint32			checksum	= Script::GenerateCRC( pNameMinusPath );
	WaveTableEntry	*pWaveEntry	= NULL;
	
	// See if the sound is already loaded.
	if( NULL != GetWaveTableIndex( checksum ))
	{
		if( !Config::CD())
		{
			if( Script::GetInt( 0xd7bb618d /* DebugSoundFx */, false )) 
			{
				Dbg_Message( "Warning: Sound '%s' already loaded (maybe from a different directory)?", sfxName );
			}
		}
		return false;
	}
	
	if( flags & SFX_FLAG_LOAD_PERM )
	{
		if( NumWavesInPermTable >= PERM_WAVE_TABLE_MAX_ENTRIES )
		{
			Dbg_Message( "Increase PERM_WAVE_TABLE_MAX_ENTRIES, no room for %s", sfxName );
			Dbg_MsgAssert( 0,( "Too many permanent sound waves being loaded." ));
			return false;
		}
		pWaveEntry = &WaveTable[ NumWavesInPermTable ];
		if( !LoadSoundPlease( sfxName, checksum, &pWaveEntry->platformWaveInfo, 1 ))
		{
			Dbg_Message( "failed to load sound %s", sfxName );
			return false;
		}
		NumWavesInPermTable++;
	}
	else
	{
		if( NumWavesInTable >= WAVE_TABLE_MAX_ENTRIES )
		{
			Dbg_MsgAssert( 0, ( "Too many sound waves being loaded." ));
			return false;
		}
		pWaveEntry = &WaveTable[ NumWavesInTable + PERM_WAVE_TABLE_MAX_ENTRIES ];
		if( !LoadSoundPlease( sfxName, checksum, &pWaveEntry->platformWaveInfo ))
		{
			return false;
		}
		NumWavesInTable++;
	}

	Dbg_MsgAssert( volume >= 0.0f,( "Tweak volume less than zero." ));
	Dbg_MsgAssert( volume <= MAX_VOL_ALLOWED,( "Tweak volume greater than max allowed." ));	
	Dbg_Assert( pWaveEntry );

	pWaveEntry->dropoff		= dropoff;
	pWaveEntry->pitch		= pitch;
	pWaveEntry->volume		= volume;
	pWaveEntry->checksum	= checksum;
	pWaveEntry->flags		= flags;
	return true;
}



/******************************************************************/
/*																  */
/*																  */
/******************************************************************/
void CSfxManager::TweakVolumeAndPitch( sVolume *p_vol, float *pitch, WaveTableEntry* waveTableIndex )
{
	if( NoSoundPlease())
		return;

	Dbg_Assert( p_vol && pitch );
	
	// 100% volume would result in 1/2 the real MAX. This gives headroom for louder sounds!
	p_vol->PercentageAdjustment( 50.0f );
	
	// Apply tweaked volumes, pitches to sound (variables from LoadSound).
	if( waveTableIndex->volume != 100.0f )
	{
		p_vol->PercentageAdjustment( waveTableIndex->volume );
	}
	if( waveTableIndex->pitch != 100.0f )
	{
		*pitch = PERCENT( *pitch, waveTableIndex->pitch );
	}
}




/******************************************************************/
/*																  */
/* Returns a unique ID each time a sound is played, 0 on failure. */
/* The 'unique ID' is just a number that is incremented each	  */
/* time a sound is played.										  */
/*																  */
/* For each platform, keep a table that stores these unique ID's  */
/* for each voice. PS2 has 48 voices.							  */
/*																  */
/* If somebody calls "StopSound" I check each of the entries in	  */
/* the table for a match, and if a match is found and that voice  */
/* is playing, I stop the voice.								  */
/*																  */
/* If a match is not found, that means the voice stopped playing  */
/* earlier and somebody else may be using the voice now, so that  */
/* voice shouldn't be stopped									  */
/*																  */
/******************************************************************/
uint32 CSfxManager::PlaySound( uint32 checksum, sVolume *p_vol, float pitch, uint32 controlID, SoundUpdateInfo *pUpdateInfo, const char *pSoundName )
{
	if( NoSoundPlease())
		return 0;

	Dbg_Assert( p_vol );
	
	WaveTableEntry *waveTableIndex = GetWaveTableIndex( checksum );
	if( waveTableIndex == NULL )
	{
		//Dbg_MsgAssert( 0,( "Couldn't find sound %s", Script::FindChecksumName( checksum )));
		Dbg_Message("******** NON-FATAL ERROR: Couldn't find sound %s", Script::FindChecksumName( checksum ));
		return 0;
	}

	// Just use checksum if no controlID is set
	if (controlID == 0)
	{
		controlID = checksum;
	}

	if( !Config::CD())
	{
		if( Script::GetInteger( 0xd7bb618d /* DebugSoundFx */, Script::NO_ASSERT ))
		{
			if (!pUpdateInfo && !IDAvailable(controlID))
			{
				Dbg_Message("Playing sound %s with same checksum as one already being played, won't be able to control from script directly.", Script::FindChecksumName(controlID));
			}
		}
	}

	TweakVolumeAndPitch( p_vol, &pitch, waveTableIndex );
	
	// Continue to play the sound, even if the volume is zero. We might be starting a looping sound,
	// so don't second guess this.
	// For the moment, don't record looping sounds, due to problems with running out of voices due to
	// voices still being used by game when it is paused. They just have their volume turned down to zero.
	if( !waveTableIndex->platformWaveInfo.looping )
	{
		// Only record the sound in the replay if the game is not paused, otherwise the menu sounds in the
		// paused menu will get recorded.
		Mdl::FrontEnd* p_frontend = Mdl::FrontEnd::Instance();
		if( !p_frontend->GamePaused())
		{
//			Replay::WritePlaySound( checksum, volL, volR, pitch );
		}	
	}
	
	// In PlaySoundPlease(), clip sounds to the max allowable. If no volume modifiers are listed in the script
	// commands (LoadSound or PlaySound) and the sound is played without attenuation, volL and volR will be 1/2
	// the max (so 50 at this point). Find the platform specific volumes like this:
	// leftVolume = PERCENT( volL, PLATFORM_MAX ). Clip to PLATFORM_MAX (or -PLATFORM_MAX if phasing is supported).
#ifdef __PLAT_NGPS__
	int whichVoice = PlaySoundPlease( &waveTableIndex->platformWaveInfo, p_vol, pitch, waveTableIndex->flags & SFX_FLAG_NO_REVERB);
#else
	int whichVoice = PlaySoundPlease( &waveTableIndex->platformWaveInfo, p_vol, pitch );
#endif

	if( whichVoice == -1 )
	{
		Dbg_Message( "Couldn't find free voice." );
		return 0;
	}
	Dbg_MsgAssert( whichVoice < NUM_VOICES,( "Voice larger than max allowed." ));
	VoiceInfo *pVoiceInfo = &VoiceInfoTable[whichVoice];
	pVoiceInfo->controlID = controlID;
	pVoiceInfo->uniqueID = GenerateUniqueID(controlID);

	// So we don't have to look this up every time we query this module for the properties of a sound that's
	// playing (which we do often for looping sounds).
	pVoiceInfo->waveIndex		= waveTableIndex;
	ObjectSoundInfo *pInfo		= &pVoiceInfo->info;
	if ( pUpdateInfo )
	{
		pInfo->currentPitch		= pUpdateInfo->pitch;
		pInfo->currentVolume	= pUpdateInfo->volume;
		pInfo->dropoffDist		= pUpdateInfo->dropoffDist ? pUpdateInfo->dropoffDist : waveTableIndex->dropoff;
		pInfo->origPitch		= pInfo->targetPitch = pInfo->currentPitch;
		pInfo->origVolume		= pInfo->targetVolume = pInfo->currentVolume;
		pInfo->deltaPitch		= 0.0f;
		pInfo->deltaVolume		= 0.0f;
		pInfo->dropoffFunction	= pUpdateInfo->dropoffFunction;
	}
	else
	{
		pInfo->currentPitch		= 100.0f;
		pInfo->currentVolume	= 100.0f;
		pInfo->dropoffDist		= 0.0f;
		pInfo->dropoffFunction	= DROPOFF_FUNC_STANDARD;
	}
	pInfo->checksum = checksum;
	if( !Config::CD())
	{
		if( Script::GetInteger( 0xd7bb618d /* DebugSoundFx */, Script::NO_ASSERT ))
		{
			//Dbg_Message( "Playing sound %s\n", (pSoundName) ? pSoundName : Script::FindChecksumName( checksum ));
			Dbg_Message( "Playing sound %s with volL = %f volR = %f on voice %d", (pSoundName) ? pSoundName : Script::FindChecksumName( checksum ),
						 p_vol->GetChannelVolume(0), p_vol->GetChannelVolume(1), whichVoice);
		}
	}	
	return pVoiceInfo->uniqueID;
}



/******************************************************************/
/*																  */
/* Send in the uniqueID returned from PlaySound()				  */
/*																  */
/******************************************************************/
bool CSfxManager::StopSound( uint32 uniqueID )
{
	if( NoSoundPlease())
		return true;

	int whichVoice = GetVoiceFromID(uniqueID);

	if (whichVoice >= 0)
	{
		// Stop the sound if it's playing (platform specific).
		StopSoundPlease( whichVoice );
		return true;
	}

	return false;
}


bool CSfxManager::SetSoundParams( uint32 uniqueID, sVolume *p_vol, float pitch )
{
	if( NoSoundPlease())
		return true;

	int whichVoice = GetVoiceFromID(uniqueID);

	if (whichVoice >= 0)
	{
		WaveTableEntry *waveTableIndex = VoiceInfoTable[ whichVoice ].waveIndex;
	
		if( waveTableIndex == NULL )
		{
			Dbg_MsgAssert(0, ( "Can't find wave table entry for sound %s", Script::FindChecksumName( uniqueID ) ) );
			return false;
		}
		if( !p_vol->IsSilent())
		{
			// Tweak the values to designer specs (from LoadSound and PlaySound).
			TweakVolumeAndPitch( p_vol, &pitch, waveTableIndex );
		}

#		if defined( __PLAT_NGPS__ ) || defined( __PLAT_NGC__ )
		// Adjust pitch to account for lower sampling rates.
		pitch = PERCENT( pitch, waveTableIndex->platformWaveInfo.pitchAdjustmentForSamplingRate );
#		endif

		// And finally, set the parameters.
		SetVoiceParameters( whichVoice, p_vol, pitch );

		return true;
	}

	if( !Config::CD())
	{
		if( Script::GetInteger( 0x6d2e270e /* DebugSoundFxUpdate */, Script::NO_ASSERT ))
		{
			Dbg_Message( "SetSoundParams: couldn't find sound %s", Script::FindChecksumName( uniqueID ));
		}
	}

	return false;
}


/******************************************************************/
/*																  */
/*																  */
/******************************************************************/
void CSfxManager::SetMainVolume( float volume )
{
	if( NoSoundPlease())
		return;
	
	Dbg_MsgAssert( volume >= 0.0f, ( "Volume below 0%" ));
	Dbg_MsgAssert( volume <= 100.0f, ( "Volume above 100%" ));
	
	if( Script::GetInteger( 0xd7bb618d /* DebugSoundFx */, Script::NO_ASSERT ))
	{
		Dbg_Message( "Setting gVolume to %f\n",volume );
	}
	  
	gVolume	= volume;
	volume	= volume * Config::GetMasterVolume() / 100.0f;
	if( volume < 0.1f )
	{
		volume = 0.0f;
	}	
	SetVolumePlease( volume );
	Pcm::SetAmbientVolume( volume );
}



/******************************************************************/
/*																  */
/*																  */
/******************************************************************/
float CSfxManager::GetMainVolume( void )
{
	return gVolume;
}



/******************************************************************/
/*																  */
/*																  */
/******************************************************************/
void CSfxManager::SetReverb( float reverbLevel, int reverbMode, bool instant )
{
	if( NoSoundPlease())
		return;

	Dbg_MsgAssert( reverbLevel >= 0.0f, ( "Reverb below 0%" ));
	Dbg_MsgAssert( reverbLevel <= 100.0f, ( "Reverb above 100%" ));
	SetReverbPlease( reverbLevel, reverbMode, instant );
}



/******************************************************************/
/*																  */
/*																  */
/******************************************************************/
bool CSfxManager::SoundIsPlaying( uint32 uniqueID, int *pWhichVoice )
{
	if( NoSoundPlease())
		return 0;

	int whichVoice;

	// Zero is not a valid ID... quickly return.
	if ( !uniqueID )
		return false;
		
	for( whichVoice = 0; whichVoice < NUM_VOICES; whichVoice++ )
	{
		if ( VoiceInfoTable[ whichVoice ].uniqueID == uniqueID )
		{
			if( !VoiceIsOn( whichVoice ))
			{
				return false;
			}
			if( pWhichVoice )
			{
				*pWhichVoice = whichVoice;
			}
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*																  */
/*																  */
/******************************************************************/

int CSfxManager::GetNumSoundsPlaying()
{
	// This is not the most efficient way to figure this out, but it doesn't
	// require any changes below the p-line.  We should add p-line code if
	// it get used a lot.
	int voicesUsed = 0;

	for( int whichVoice = 0; whichVoice < NUM_VOICES; whichVoice++ )
	{
		if( VoiceIsOn( whichVoice ) )
		{
			voicesUsed++;
		}
	}

	return voicesUsed;
}

/******************************************************************/
/*																  */
/*																  */
/******************************************************************/
float CSfxManager::GetDropoffDist( uint32 soundChecksum )
{
	if( NoSoundPlease())
		return DefaultDropoffDist;

	WaveTableEntry *waveIndex = GetWaveTableIndex( soundChecksum );
	if( waveIndex == NULL )
	{
		return 0;
	}
	float dropoffDist = waveIndex->dropoff;
	return ( dropoffDist ? dropoffDist : DefaultDropoffDist );
}



/******************************************************************/
/*																  */
/*																  */
/******************************************************************/
bool CSfxManager::UpdateLoopingSound( uint32 soundID, sVolume *p_vol, float pitch )
{
	if( NoSoundPlease())
		return true;

//    uint32 start_time = Tmr::GetTimeInUSeconds();

	int whichVoice;
	for( whichVoice = 0; whichVoice < NUM_VOICES; whichVoice++ )
	{
		if( VoiceInfoTable[whichVoice].uniqueID == soundID )
		{
			if( !VoiceIsOn( whichVoice ))
			{
				if( !Config::CD())
				{
					if( Script::GetInteger( 0x6d2e270e /* DebugSoundFxUpdate */, Script::NO_ASSERT ))
					{
						Dbg_Message( "Sound %s is done", Script::FindChecksumName( VoiceInfoTable[whichVoice].info.checksum ));
					}
				}	

				return false;
			}
			
			// Tweak sounds for this particular instance of PlaySound (this allows a designer to vary the pitch,
			// for example, of a carloop sound on each car, by adding a pitch = parameter to Obj_PlaySound. This
			// will be maintained, as will the volume, thanks to this code.
			if( !p_vol->IsSilent())
			{
				float tweakVolume = VoiceInfoTable[whichVoice].info.currentVolume;
				if( tweakVolume != 100.0f )
				{
					p_vol->PercentageAdjustment( tweakVolume );
				}
				float tweakPitch = VoiceInfoTable[ whichVoice ].info.currentPitch;
				if( tweakPitch != 100.0f )
				{
					pitch = PERCENT( pitch, tweakPitch );
				}
			}
			
			WaveTableEntry *waveTableIndex = VoiceInfoTable[ whichVoice ].waveIndex;
		
			if( waveTableIndex == NULL )
			{
				Dbg_Message( "WARNING:  Positional/looping sound error." );
				return false;
			}
			if( !p_vol->IsSilent())
			{
				// Tweak the values to designer specs (from LoadSound and PlaySound).
				TweakVolumeAndPitch( p_vol, &pitch, waveTableIndex );
			}

#			if defined( __PLAT_NGPS__ ) || defined( __PLAT_NGC__ )
			// Adjust pitch to account for lower sampling rates.
			pitch = PERCENT( pitch, waveTableIndex->platformWaveInfo.pitchAdjustmentForSamplingRate );
#			endif

			// And finally, set the parameters.
			SetVoiceParameters( whichVoice, p_vol, pitch );

//            uint32 end_time = Tmr::GetTimeInUSeconds();	
//			if ((end_time - start_time) > 250)
//				Dbg_Message("CSfxManager::UpdateLoopingSound Frame %d Time %d", Tmr::GetVblanks(), (end_time - start_time));
			return true;
		}
	}

	if( !Config::CD())
	{
		if( Script::GetInteger( 0x6d2e270e /* DebugSoundFxUpdate */, Script::NO_ASSERT ))
		{
			Dbg_Message( "UpdateSound: couldn't find sound %s", Script::FindChecksumName( VoiceInfoTable[whichVoice].info.checksum ));
		}
	}	
	return false;
}



/******************************************************************/
/*																  */
/* Calculates multipliers for front left, front right, rear left, */
/* rear right and center channel speakers.						  */
/*																  */
/******************************************************************/
void CSfxManager::Get5ChannelMultipliers( const Mth::Vector &sound_source, float *p_multipliers )
{
	float angle	= Mth::RadToDeg( atan2f( sound_source[X], -sound_source[Z] ));
	Get5ChannelMultipliers( angle, p_multipliers );
}



/******************************************************************/
/*																  */
/* Calculates multipliers for front left, front right, rear left, */
/* rear right and center channel speakers.						  */
/*																  */
/******************************************************************/
void CSfxManager::Get5ChannelMultipliers( float angle, float *p_multipliers )
{
	static float speakers[5][3]	= {	{ 330.00f, 240.01f, 359.99f },		// Front left max angle, min angle0, min angle1
									{  30.00f,   0.01f,	119.99f },		// Front right
									{ 240.00f, 120.01f, 329.99f },		// Rear left
									{ 120.0f,   30.01f, 239.99f },		// Rear right
									{   0.0f,  330.01f,  29.99f }};		// Center

	// Ensure angle is in a suitable range.
	angle = ( angle < 0.0f ) ? ( 360.0f + angle ) : angle;

	// Go through and calculate the relative volumes for each speaker.
	for( int spkr = 0; spkr < 5; ++spkr )
	{
		float amin0	= speakers[spkr][1];
		float amin1	= speakers[spkr][2];
		float amax	= speakers[spkr][0];
		float mul	= 0.0f;

		if( amin0 < amax )
		{
			// Regular test.
			if(( angle > amin0 ) && ( angle <= amax ))
			{
				// Angle lies between amin0 and amax.
				mul = ( angle - amin0 ) / ( amax - amin0 );
			}
			else if(( angle > amax ) && ( angle < amin1 ))
			{
				// Angle lies between amax and amin1.
				mul = 1.0f - (( angle - amax ) / ( amin1 - amax ));
			}
		}
		else
		{
			// Non regular test (center channel). Assumes center at 0.0.
			if( angle > amin0 )
			{
				mul = ( angle - amin0 ) / ( 360.0f - amin0 );
			}
			else if( angle < amin1 )
			{
				mul = ( amin1 - angle ) / ( amin1 );
			}
		}
						
		// Angle is within scope of this speaker. Figure multiplier.
		Dbg_Assert( mul <= 1.0f );
		p_multipliers[spkr] = sinf( mul * Mth::PI * 0.5f );
	}
}



/******************************************************************/
/*																  */
/* Sets volume and pan considering all cameras and current		  */
/* position.													  */
/*																  */
/******************************************************************/
void CSfxManager::SetVolumeFromPos( sVolume *p_vol, const Mth::Vector &soundSource, float dropoffDist, EDropoffFunc dropoff_func,
									Gfx::Camera *p_camera,  const Mth::Vector *p_dropoff_pos)
{
	if( NoSoundPlease())
		return;

	Dbg_Assert( p_vol );

	// Set the volume to 0
	p_vol->SetSilent();

	// Return if in Silence Mode
	if( PositionalSoundSilenceMode() )
		return;

	// Find the camera if one wasn't already supplied
	if (!p_camera)
	{
		p_camera = Nx::CViewportManager::sGetClosestCamera(soundSource);

		// If we still can't find a camera, return with the volume set to 0
		if (!p_camera)
		{
			return;
		}
	}

	// Find the dist to the sound
	float dist = Mth::Distance( p_camera->GetPos(), soundSource );

	// Calculate the dropoff dist
	if (p_dropoff_pos)
	{
		dropoffDist = dist + Mth::Distance( *p_dropoff_pos, p_camera->GetPos());
	}
	else if( !dropoffDist )
	{
		dropoffDist = DefaultDropoffDist;
	}
	
	// If we are outside the dropoff dist, we are out of here
	if( dist >= dropoffDist )
		return;

	// Sound is within range of this camera.
	float dropOff	= dist / dropoffDist;
	float volume = 0.0f;
	switch (dropoff_func)
	{
	case DROPOFF_FUNC_STANDARD:
		volume	= ( 100.0f * 
				  ((( 1.0f - ( dropOff * dropOff )) + 			// Exponential and...
				  ( 3.0f * ( 1.0f - ( dropOff )))) / 4.0f ));	// ...linear averaged together.
		break;

	case DROPOFF_FUNC_LINEAR:
		volume	= ( 100.0f * ( 1.0f - dropOff ) );
		break;

	case DROPOFF_FUNC_EXPONENTIAL:
		volume	= ( 100.0f * ( ( 1.0f - dropOff ) * ( 1.0f - dropOff ) ) );
		break;

	case DROPOFF_FUNC_INV_EXPONENTIAL:
		volume	= ( 100.0f * ( sqrtf ( 1.0f - dropOff ) ) );
		break;
	}

	switch( p_vol->GetVolumeType() )
	{
		case VOLUME_TYPE_BASIC_2_CHANNEL:
		case VOLUME_TYPE_2_CHANNEL_DOLBYII:
			{
				Dbg_Assert( p_camera );		// We should have returned by now

				if( fabsf( volume ) > fabsf( p_vol->GetLoudestChannel()))
				{
					Mth::Vector sound_pos_from_camera = soundSource - p_camera->GetPos();
					sound_pos_from_camera.Normalize();

					Mth::Vector camRightVector = -p_camera->GetMatrix()[X];
					
					// Project the obj_pos vector onto the right vector.
					// For some reason right and left were switched (must be left instead of right!)
					float panVal = Mth::DotProduct( sound_pos_from_camera, camRightVector );

					// +1.0f is all the way on the right, -1.0f is all the way on the left.
					// Doing an exponential curve, so that if the object is right in the middle
					// the right and left volume (at closest dist) is 75%. 100% volume if all
					// the way to the right/left.
					panVal		+= 1.0f;		// Will now be from 0 to 2 (Lmax to Rmax)..
					panVal		/= 2.0f;		// ...now from 0 to 1.
					float rVol	= ( 1.0f - ( panVal * panVal )) * volume;
					panVal		= 1.0f - panVal;
					float lVol	= ( 1.0f - ( panVal * panVal )) * volume;

					if( p_vol->GetVolumeType() == VOLUME_TYPE_2_CHANNEL_DOLBYII )
					{
						bool bSetPhase = false;

						if( lVol > p_vol->GetChannelVolume( 0 ))
						{
							p_vol->SetChannelVolume( 0, lVol );
							bSetPhase = true;
						}
						if( rVol > p_vol->GetChannelVolume( 1 ))
						{
							p_vol->SetChannelVolume( 1, rVol );
							bSetPhase = true;
						}
						if( bSetPhase )
						{
							// If sound is behind the camera, set volume negative and it will sound out of phase.
							Mth::Vector camAtVector = -p_camera->GetMatrix()[Z];

							float behindCamera = Mth::DotProduct( sound_pos_from_camera, camAtVector );
							if ( behindCamera < 0.0f )
							{
								// Just one channel needs to be reverse phased to get the effect.
								p_vol->SetChannelVolume( 0, p_vol->GetChannelVolume( 0 ) * -1.0f );
							}
						}
					}
					else
					{
						if( lVol > p_vol->GetChannelVolume( 0 ))
						{
							p_vol->SetChannelVolume( 0, lVol );
						}
						if( rVol > p_vol->GetChannelVolume( 1 ))
						{
							p_vol->SetChannelVolume( 1, rVol );
						}
					}
				}
			}
			break;
	
		case VOLUME_TYPE_5_CHANNEL_DOLBY5_1:
			{
				Dbg_Assert( p_camera );		// We should have returned by now

				if( fabsf( volume ) > p_vol->GetLoudestChannel())
				{
					// Transform the sound source into the camera's coordinate space.
					Mth::Matrix inv_view	= p_camera->GetMatrix();
					inv_view.Invert();

					Mth::Vector sound_src	= soundSource - p_camera->GetPos();
					Mth::Vector sound_pos	= inv_view.Transform( sound_src );
					sound_pos.Normalize();

					float channel_multipliers[5];
                    Get5ChannelMultipliers( sound_pos, &channel_multipliers[0] );

					// Go through and calculate the relative volumes for each speaker.
					for( int spkr = 0; spkr < 5; ++spkr )
					{
						float mul = channel_multipliers[spkr];
						if( mul > 0.0f )
						{
							// Angle is within scope of this speaker. Figure multiplier.
							Dbg_Assert( mul <= 1.0f );
							float vol = volume * mul;
							if( vol > p_vol->GetChannelVolume( spkr ))
							{
								p_vol->SetChannelVolume( spkr, vol );
							}
						}
					}

					// Final step is to normalize the channels out and reset to volume level.
					if( !p_vol->IsSilent())
					{
						float norm = 0.0;
						for( int spkr = 0; spkr < 5; ++spkr )
						{
							norm += p_vol->GetChannelVolume( spkr ) * p_vol->GetChannelVolume( spkr );
						}
						norm = sqrtf( norm );
						for( int spkr = 0; spkr < 5; ++spkr )
						{
							p_vol->SetChannelVolume( spkr, ( volume * p_vol->GetChannelVolume( spkr )) / norm );
						}
					}

				}
			}
			break;
		
		default:
			break;
	}
}



/* Real speed of sound ~= 760 mph.								  */
/* Lower that for dramatic effect.								  */
#define	SPEED_OF_OUR_SOUND	MPH_TO_INCHES_PER_SECOND( 700.0f )

/******************************************************************/
/*																  */
/* This should happen after all other pitch adjustments.		  */
/*																  */
/******************************************************************/
void CSfxManager::AdjustPitchForDoppler( float *pitch, const Mth::Vector ¤tPos, const Mth::Vector &oldPos, float elapsedTime, Gfx::Camera *pCam )
{
#	ifndef __PLAT_NGC__
	const float cutoff_dist =  360.0f;	// in inches
#	endif		// __PLAT_NGC__

	if( NoSoundPlease())
		return;

#	ifndef __PLAT_NGC__
	if ( !pCam )
	{
		pCam = Nx::CViewportManager::sGetClosestCamera( currentPos );
		if( !pCam )
			return;
	}

	float prevDist	= Mth::Distance( pCam->m_old_pos, oldPos );
	float deltaDist	= Mth::Distance( pCam->GetPos(), currentPos ) - prevDist;

	if(( fabsf( deltaDist ) * Tmr::FrameRatio()) > Tmr::FrameRatio() * cutoff_dist )
	{
		// TT2794: Large movements in the camera are causing pitch glitches, so
		// it would be better to not change the pitch at all than to try to find
		// a "good" change.  Garrett
		return;

		// Clip so there aren't high pitched schreeches when camera is moved in net games and such:
		if ( deltaDist < 0.0f )
		{
			deltaDist = -( Tmr::FrameRatio( ) * cutoff_dist );
		}
		else
		{
			deltaDist = Tmr::FrameRatio( ) * cutoff_dist;
		}
	}
	float deltaPitch = SPEED_OF_OUR_SOUND * elapsedTime;
	Dbg_MsgAssert( deltaPitch,( "Divide by zero." ));
	deltaPitch = (( *pitch ) * deltaDist ) / deltaPitch;
	*pitch -= deltaPitch;
#	endif // __PLAT_NGC__
}



/******************************************************************/
/*																  */
/* This should happen after all other pitch adjustments.		  */
/*																  */
/******************************************************************/
ObjectSoundInfo *CSfxManager::GetObjectSoundProperties( Obj::CSoundComponent *pObj, uint32 checksum )
{
	PositionalSoundEntry *pEntry = GpPositionalSounds;
	while( pEntry )
	{
		if(( pEntry->pObj == pObj ) && ( pEntry->checksum == checksum ))
		{
			// Change the current volume, pitch.
			int voiceIndex;
			if( !SoundIsPlaying( pEntry->uniqueID, &voiceIndex ))
			{
				return NULL;
			}
			return &VoiceInfoTable[ voiceIndex ].info;
		}
		pEntry = pEntry->pNext;
	}
	return NULL;
}



/******************************************************************/
/*																  */
/* Plays sound, considering camera position(s) from sound source. */
/*																  */
/******************************************************************/
uint32 CSfxManager::PlaySoundWithPos( uint32 soundChecksum, SoundUpdateInfo *pUpdateInfo, Obj::CSoundComponent *pObj, bool noPosUpdate )
{
	if( NoSoundPlease())
		return 0;

	WaveTableEntry *waveTableIndex = GetWaveTableIndex( soundChecksum );
	if( waveTableIndex == NULL )
	{
		Dbg_MsgAssert( 0, ( "Asking to play sound that hasn't been loaded %s.", Script::FindChecksumName( soundChecksum )));
		return 0;
	}
	
	Dbg_MsgAssert( pObj,( "pObj should be non-NULL" ));

	sVolume vol;

	// Garrett: Shouldn't we be calling this here, too?  Or is it not necessary?
//	if( pVoiceInfo->waveIndex->flags & SFX_FLAG_POSITIONAL_UPDATE_WITH_DOPPLER )
//	{
//		// Even if the object didn't move, the camera could have!
//		AdjustPitchForDoppler( &pitch, pObj->m_pos, pObj->m_old_pos, Tmr::FrameLength(), pCamera );
//	}

	Gfx::Camera *pCamera = Nx::CViewportManager::sGetClosestCamera( pObj->GetPosition() );
	if( pCamera )
	{
		Mth::Vector dropoff_pos;
		Mth::Vector *p_dropoff_pos = NULL;
		if (pObj->GetClosestDropoffPos(pCamera, dropoff_pos))
		{
			p_dropoff_pos = &dropoff_pos;
		}

		SetVolumeFromPos( &vol, pObj->GetClosestEmitterPos(pCamera), pUpdateInfo->dropoffDist ? pUpdateInfo->dropoffDist : waveTableIndex->dropoff,
						  pUpdateInfo->dropoffFunction, pCamera, p_dropoff_pos );
	}
	else
	{
		vol.SetSilent();
	}
	
	Dbg_MsgAssert(!(noPosUpdate && ( waveTableIndex->flags & SFX_FLAG_POSITIONAL_UPDATE_WITH_DOPPLER )), ("Trying to play doppler sound with NoPosUpdate"));
	if( vol.IsSilent() && noPosUpdate )
	{
		return 0;
	}
	
	vol.PercentageAdjustment( pUpdateInfo->volume );
	
	uint32 uniqueID = PlaySound( soundChecksum, &vol, pUpdateInfo->pitch, 0, pUpdateInfo );
	
	if( !Config::CD())
	{
		if( Script::GetInteger( 0x6d2e270e /* DebugSoundFxUpdate */, Script::NO_ASSERT ))
		{
			Dbg_Message( "Starting sound %s with volL = %f volR = %f", Script::FindChecksumName( soundChecksum ), vol.GetChannelVolume(0), vol.GetChannelVolume(1));
		}
	}	

	// Should be able to have looping sounds, as long as the ones that are manually updated call PlaySound
	// instead of PlaySoundWithPos.
	if(!noPosUpdate)
	{
		AddPositionalSoundToUpdateList( uniqueID, soundChecksum, pObj );

		if( !Config::CD())
		{
			if( Script::GetInteger( 0x6d2e270e /* DebugSoundFxUpdate */, Script::NO_ASSERT ))
			{
				Dbg_Message( "Added sound %s to update list", Script::FindChecksumName( soundChecksum ));
			}
		}	
	}
	return uniqueID;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSfxManager::Update( void )
{
	PerFrameUpdate();
}

} // namespace Sfx



================================================
FILE: Code/Gel/SoundFX/soundfx.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			soundfx													**
**																			**
**	File name:		gel/soundfx/soundfx.h  									**
**																			**
**	Created: 		1/4/01	-	mjd											**
**																			**
*****************************************************************************/

#ifndef __GEL_SOUNDFX_H
#define __GEL_SOUNDFX_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

#ifdef __PLAT_NGPS__
#include 
#elif defined( __PLAT_XBOX__ )
#include 
#elif defined( __PLAT_NGC__ )
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// change the following line to compile out soundfx from the game:
#ifdef __PLAT_XBOX__
#define NO_SOUND_PLEASE	0
#else
#define NO_SOUND_PLEASE	0
#endif

// if we need more, NO PROBLEMA!  Change this:
#if defined( __PLAT_NGPS__ )
#define WAVE_TABLE_MAX_ENTRIES			256	// non-permanent sounds
#define PERM_WAVE_TABLE_MAX_ENTRIES		256	// permanent sounds
#define MAX_POSITIONAL_SOUNDS			128
#elif defined( __PLAT_XBOX__ )
#define WAVE_TABLE_MAX_ENTRIES			256	// non-permanent sounds
#define PERM_WAVE_TABLE_MAX_ENTRIES		256	// permanent sounds
#define MAX_POSITIONAL_SOUNDS			128
#else
#define WAVE_TABLE_MAX_ENTRIES			256	// non-permanent sounds
#define PERM_WAVE_TABLE_MAX_ENTRIES		256	// permanent sounds
#define MAX_POSITIONAL_SOUNDS			128
#endif

#define DIST_FROM_DROPOFF_AT_WHICH_TO_START_SOUND	FEET_TO_INCHES( 20.0f )
#define DFDAWTSS									DIST_FROM_DROPOFF_AT_WHICH_TO_START_SOUND

// This buffer prevents rapid switching on/off of voices that are right on the border.
#define SOUND_DIST_BUFFER							FEET_TO_INCHES( 20.0f )
#define DIST_FROM_DROPOFF_AT_WHICH_TO_STOP_SOUND	( SOUND_DIST_BUFFER + DFDAWTSS )

#define DEFAULT_DROPOFF_DIST						FEET_TO_INCHES( 85.0f )

// When a voice is set to negative volume on some platforms, it puts the sound 'out of phase' to sound like it's behind you...
#if defined( __PLAT_NGPS__ ) || defined( __PLAT_XBOX__ )
#define PLATFORM_SUPPORTS_VOLUME_PHASING			1
#endif

#define MAX_VOL_ALLOWED								1000 // 1000 percent (10 times)

namespace Script
{
	class CStruct;
}

namespace Gfx
{
	class Camera;
}

namespace Obj
{
    class CSoundComponent;
    class CEmitterObject;
}

enum
{
	SFX_FLAG_LOAD_PERM						= ( 1 << 0 ),
//	SFX_FLAG_POSITIONAL_UPDATE				= ( 1 << 1 ),
	SFX_FLAG_POSITIONAL_UPDATE_WITH_DOPPLER = ( 1 << 2 ),
	SFX_FLAG_NO_REVERB						= ( 1 << 4 ),
};


// Function used to calculate volume dropoff
enum EDropoffFunc
{
	DROPOFF_FUNC_STANDARD,				// Combination of linear and exponential
	DROPOFF_FUNC_LINEAR,
	DROPOFF_FUNC_EXPONENTIAL,
	DROPOFF_FUNC_INV_EXPONENTIAL,
};


struct ObjectSoundInfo
{
	// Needed for long sounds played on a per-object basis and positionally updated:
	uint32	checksum;
	float	dropoffDist;
	float	currentPitch;
	float	currentVolume;
	float	origPitch;
	float	origVolume;
	float	targetPitch;
	float	targetVolume;
	float	deltaPitch;
	float	deltaVolume;
	EDropoffFunc dropoffFunction;

	// Used to check to see if the sound should be turned off or on, if too far/close to the camera:
	Tmr::Time timeForNextDistCheck;
	
	// Used by the script debugger code to fill in a structure
	// for transmitting to the monitor.exe utility running on the PC.
	void GetDebugInfo(Script::CStruct *p_info);
};


#ifdef __PLAT_WN32__
struct PlatformWaveInfo
{
	// Stub to compile under win32
};
#endif

namespace Sfx
{

struct WaveTableEntry
{
	uint32		checksum;	// name of the sound...
	uint32		flags;		// see SFX_FLAG_ 's in soundfx.h

	// All the shit designers want to be able to set in a script file:
	float		pitch;		// tweakable by designers in script file
	float		volume;		// tweakable by designers in script file
	float		dropoff;	// tweakable by designers in script file

	// Platform dependent info:
	PlatformWaveInfo platformWaveInfo;
};


struct VoiceInfo
{
	uint32				uniqueID;
	uint32				controlID;
	WaveTableEntry		*waveIndex;
	ObjectSoundInfo		info;
};


enum EVolumeType
{
	VOLUME_TYPE_BASIC_2_CHANNEL,		// Basic 2 channel left/right sound with no volume phasing.
	VOLUME_TYPE_2_CHANNEL_DOLBYII,		// As above, but with DolbyII volume phasing.
	VOLUME_TYPE_5_CHANNEL_DOLBY5_1		// Five discreet volumes, as per Dolby 5.1.
};



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

struct sVolume
{
	enum
	{
		MAX_CHANNELS = 5
	};
	float				m_channels[MAX_CHANNELS];	// Can store up to five discreet channel volumes.
	EVolumeType			m_volume_type;				// The type of the volume.

						sVolume( void );
						sVolume( EVolumeType type );
						sVolume( const sVolume& rhs );
						~sVolume( void );
	bool				operator== ( const sVolume& rhs );
	bool				operator!= ( const sVolume& rhs )			{ return !(*this == rhs); }
	bool				IsSilent( void );
	void				SetSilent( void );
	EVolumeType			GetVolumeType( void )						{ return m_volume_type; }
	float				GetChannelVolume( uint32 chan )				{ return m_channels[chan]; }
	void				SetChannelVolume( uint32 chan, float vol )	{ m_channels[chan] = vol; }
	void				PercentageAdjustment( float percentage );
	float				GetLoudestChannel( void );
};

struct PositionalSoundEntry;

struct SoundUpdateInfo
{
	// So each instance of Obj_PlaySound maintains the values sent in by the designer:
	float pitch; 
	float volume;
	float dropoffDist;
	EDropoffFunc dropoffFunction;
};


class CSfxManager : public Spt::Class
{
	DeclareSingletonClass( CSfxManager );

public:
					CSfxManager( void );
				    ~CSfxManager( void );

	int				MemAvailable( void );

	bool			LoadSound( const char *sfxName,  int flags = 0, float dropoff = 0.0f, float pitch = 100.0f, float volume = 100.0f );
	uint32			PlaySound( uint32 checksum, sVolume *p_vol, float pitch = 100.0f, uint32 controlID = 0, SoundUpdateInfo *pUpdateInfo = NULL, const char *pSoundName = NULL );
	uint32			PlaySoundWithPos( uint32 soundChecksum, SoundUpdateInfo *pUpdateInfo, Obj::CSoundComponent *pObj, bool noPosUpdate );
	ObjectSoundInfo	*GetObjectSoundProperties( Obj::CSoundComponent *pObj, uint32 checksum );

	void			CleanUp( void );
	void			StopAllSounds( void );
	bool			StopSound( uint32 uniqueID );
	bool			SetSoundParams( uint32 uniqueID, sVolume *p_vol, float pitch = 100.0f );	// For non-object sounds
	void			StopObjectSound( Obj::CSoundComponent *pObj, uint32 checksum );
	void			PauseSounds( void );
	void			SetReverb( float reverbLevel, int reverbMode = 0, bool instant = false );
	bool			SoundIsPlaying( uint32 uniqueID, int *pWhichVoice = NULL );
	int				GetNumSoundsPlaying();
	void			SetMainVolume( float volume );
	void			SetDefaultDropoffDist( float dist );
	void			SetVolumeFromPos( sVolume *p_vol, const Mth::Vector &soundSource, float dropoffDist,
									  EDropoffFunc dropoff_func = DROPOFF_FUNC_STANDARD,
									  Gfx::Camera* pCamera = NULL, const Mth::Vector *p_dropoff_pos = NULL );
	void			AdjustPitchForDoppler( float *pitch, const Mth::Vector ¤tPos, const Mth::Vector &oldPos, float elapsedTime, Gfx::Camera* pCam = NULL );
	void			Get5ChannelMultipliers( const Mth::Vector &sound_source, float *p_multipliers );
	void			Get5ChannelMultipliers( float angle, float *p_multipliers );

	// A per-frame function. Does nothing on PS2, on Xbox checks for stop notification events.
	void			Update( void );
	void			UpdatePositionalSounds( void );
	
	// If, for some reason, the sound has stopped playing, this returns false, true otherwise.
	bool			UpdateLoopingSound( uint32 soundID, sVolume *p_vol, float pitch = 100.0f );

	float			GetMainVolume( void );
	float			GetDropoffDist( uint32 soundChecksum );

	// -1 on failure (sound not loaded)
	WaveTableEntry*	GetWaveTableIndex( uint32 checksum );

	void			ObjectBeingRemoved( Obj::CSoundComponent *pObj );
		
	void			SetDefaultVolumeType( EVolumeType type )	{ m_default_volume_type = type; }
	EVolumeType		GetDefaultVolumeType( void )				{ return m_default_volume_type; }

	bool			PositionalSoundSilenceMode();		// Returns true if in mode where environment sounds should be a 0 volume
	
private:

	EVolumeType		m_default_volume_type;

	bool			AdjustObjectSound( Obj::CSoundComponent *pObj, VoiceInfo *pVoiceInfo, Tmr::Time gameTime );
	void			TweakVolumeAndPitch( sVolume* p_vol, float *pitch, WaveTableEntry* waveTableIndex );
	void			RemovePositionalSoundFromList( PositionalSoundEntry *pEntry, bool stopIfPlaying );
	void			AddPositionalSoundToUpdateList( uint32 uniqueID, uint32 soundChecksum, Obj::CSoundComponent *pObj = NULL );

	// UniqueID functions
	bool			IDAvailable(uint32 id);
	int				GetVoiceFromID(uint32 id);
	uint32			GenerateUniqueID(uint32 id);
};



/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

extern int				NumWavesInTable;
extern int				NumWavesInPermTable;
extern int				NumPositionalSounds;
extern WaveTableEntry	WaveTable[PERM_WAVE_TABLE_MAX_ENTRIES + WAVE_TABLE_MAX_ENTRIES];


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/
bool			NoSoundPlease();
EDropoffFunc	GetDropoffFunctionFromChecksum(uint32 checksum);

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Sfx

#endif	// __GEL_SOUNDFX_H




================================================
FILE: Code/Gel/inpman.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Input  (Inp)											**
**																			**
**	File name:		gel/inpman.h											**
**																			**
**	Created: 		05/26/2000	-	spg										**
**																			**
*****************************************************************************/

#ifndef __GEL_INPMAN_H
#define __GEL_INPMAN_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#include 

#include 
#include 
 
/*****************************************************************************
**								   Defines									**
*****************************************************************************/


#define PAD_NONE 0
#define PAD_U 1
#define PAD_D 2
#define PAD_L 3
#define PAD_R 4
#define PAD_UL 5
#define PAD_UR 6
#define PAD_DL 7
#define PAD_DR 8
#define PAD_CIRCLE 9
#define PAD_SQUARE 10
#define PAD_X 11
#define PAD_TRIANGLE 12
#define PAD_L1 13
#define PAD_L2 14
#define PAD_L3 15
#define PAD_R1 16
#define PAD_R2 17
#define PAD_R3 18
#define PAD_BLACK 19
#define PAD_WHITE 20
#define PAD_Z 21
#define PAD_NUMBUTTONS 22

namespace Inp
{



static const int vANALOGUE_TOL      = 50;			// Mick, changed back from 32, as our controllers were wandering...
static const int vANALOGUE_CENTER   = 128;


extern uint32	gDebugButtons[];
extern uint32	gDebugBreaks[];
extern uint32	gDebugMakes[];


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

int GetButtonIndex(uint32 Checksum);
uint32 GetButtonChecksum( int whichButton );

class  Data  : public Spt::Class
{   
	

public:

        friend class Server;

						Data( void );

#define vMAX_ANALOG_VALUE   255

    enum DigitalButtonIndex
	{      
        vD_L2,
        vD_R2,
        vD_L1,
        vD_R1,
        vD_TRIANGLE,
        vD_CIRCLE,
        vD_X,
        vD_SQUARE,
        vD_SELECT,
        vD_L3,
        vD_R3,
        vD_START,
        vD_UP,
        vD_RIGHT,
        vD_DOWN,
		vD_LEFT,
		vD_BLACK,	// Only supported on XBox
		vD_WHITE,	// Only supported on XBox
		vD_Z,		// Only supported on NGC
        vMAX_DIGITAL_EVENTS
    };
       
    enum AnalogButtonIndex
    {
        vA_RIGHT_X,         // Right analog controller stick
		vA_RIGHT_Y,
		vA_LEFT_X,          // Left analog controller stick
		vA_LEFT_Y,
        vA_RIGHT,
        vA_LEFT,
        vA_UP,
        vA_DOWN,
        vA_TRIANGLE,
        vA_CIRCLE,
        vA_X,
        vA_SQUARE,
        vA_L1,
        vA_R1,
        vA_L2,
        vA_R2,
        vA_L3,
        vA_R3,
		// (Mick) Added the following to stop the analog values before they are clamped
		// these values only get clamped if BOTH X and Y are in the middle
		// the above values clamp X and Y independently
		// leading to dead zones when rotating the stick round in a circle
		// a significant loss in accuracy
        vA_RIGHT_X_UNCLAMPED,         // Right analog controller stick
		vA_RIGHT_Y_UNCLAMPED,
		vA_LEFT_X_UNCLAMPED,          // Left analog controller stick
		vA_LEFT_Y_UNCLAMPED,
		
		vA_BLACK,					// Only supported on XBox
		vA_WHITE,					// Only supported on XBox
		
		vA_Z,						// Only supported on XBox

//		vA_SELECT,		
					   
		vMAX_ANALOG_EVENTS
	};

	enum DigitalButtonMask
	{
        // These map directly to digital input in m_Buttons, m_Prev and m_New
        mD_L2           = nBit( vD_L2 ),
        mD_R2           = nBit( vD_R2 ),
        mD_L1           = nBit( vD_L1 ),
        mD_R1           = nBit( vD_R1 ),
        mD_TRIANGLE     = nBit( vD_TRIANGLE ),
		mD_CIRCLE       = nBit( vD_CIRCLE ),
        mD_X            = nBit( vD_X ),
        mD_SQUARE       = nBit( vD_SQUARE ),
        mD_SELECT       = nBit( vD_SELECT ),
        mD_L3           = nBit( vD_L3 ),
        mD_R3           = nBit( vD_R3 ),
        mD_START        = nBit( vD_START ),
        mD_UP           = nBit( vD_UP ),
        mD_RIGHT        = nBit( vD_RIGHT ),
        mD_DOWN         = nBit( vD_DOWN ),
        mD_LEFT         = nBit( vD_LEFT ),
		mD_BLACK		= nBit( vD_BLACK ),
		mD_WHITE		= nBit( vD_WHITE ),
		mD_Z			= nBit( vD_Z ),
        mD_ALL          = 0xffffffff
	};

    enum AnalogButtonMask
    {    
        // These map indirectly to analog input in m_Events
		mA_RIGHT_X      = nBit( vA_RIGHT_X ),         // Right analog controller stick
		mA_RIGHT_Y      = nBit( vA_RIGHT_Y ),
		mA_LEFT_X       = nBit( vA_LEFT_X ),          // Left analog controller stick
		mA_LEFT_Y       = nBit( vA_LEFT_Y ),
        mA_RIGHT        = nBit( vA_RIGHT ),
        mA_LEFT         = nBit( vA_LEFT ),
        mA_UP           = nBit( vA_UP ),
        mA_DOWN         = nBit( vA_DOWN ),
        mA_TRIANGLE     = nBit( vA_TRIANGLE ),
        mA_CIRCLE       = nBit( vA_CIRCLE ),
        mA_X            = nBit( vA_X ),
        mA_SQUARE       = nBit( vA_SQUARE ),
        mA_L1           = nBit( vA_L1 ),
        mA_R1           = nBit( vA_R1 ),
        mA_L2           = nBit( vA_L2 ),
        mA_R2           = nBit( vA_R2 ),
        mA_L3           = nBit( vA_L3 ),
        mA_R3           = nBit( vA_R3 ),
        mA_BLACK        = nBit( vA_BLACK ),
        mA_WHITE        = nBit( vA_WHITE ),
        mA_Z	        = nBit( vA_Z ),
//		mA_SELECT		= nBit( vA_SELECT ),
        mA_ALL          = 0xffffffff
	};
	

	void    MaskDigitalInput( int button_mask );
    void    MaskAnalogInput( int button_mask );
    void    ConvertDigitalToAnalog( void );
	void	OverrideAnalogPadWithStick( void );

	uint8   m_Event[vMAX_ANALOG_EVENTS];	// Analog info
	uint    m_Buttons;              // Current accessible button info (digital)   
    uint    m_Makes;                // Depressed this frame
    uint    m_Breaks;               // Released this frame

private:

	uint    m_new;                  // Edges (digital)
	uint    m_cur;              	// Current INTERNAL button info (digital)
	uint    m_prev;                 // last frame's trig
	
    void    handle_analog_tolerance( void );
};

class  RecordedData  : public Spt::Class
{
	
	
	friend class Server;

private:	
	sint8	m_valid;								// Was data valid on this frame?
	uint8   m_event[Data::vMAX_ANALOG_EVENTS];		// Analog info
	uint    m_cur;              					// Current INTERNAL button info (digital)     
	
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  BaseHandler : public  Tsk::BaseTask 
{
	

    friend class Manager;

public:
	Data*           m_Input;          // controller input  
    SIO::Device     *m_Device;        // the device from which this data was obtained
	int				m_Index;

protected:
                    BaseHandler ( int index, Tsk::BaseTask::Node::Priority pri )
					: Tsk::BaseTask( pri ), m_Index ( index ) 
					{
						Dbg_Printf( "Creating BaseHandler with Pri %d\n", pri );
					}
                    

    virtual         ~BaseHandler ( void ) {}
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

nTemplateSubClass( _T, Handler, BaseHandler )
{
	

public:

	typedef void	(Code)( const Handler< _T >& );

					Handler( int index, Code* const code, _T& data, 
                             Lst::Node< Tsk::BaseTask >::Priority pri = Lst::Node< Tsk::BaseTask >::vNORMAL_PRIORITY );
		
    virtual         ~Handler ( void );

	virtual void	vCall( void ) const;
	virtual void *	GetCode(void) const;
	_T&				GetData( void ) const;

private :

	Code* const		code;			// tasks entry point
	_T&				data;			// task defined data                    
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/                                                                   
                                                                   
//class  Server  : public Spt::Class // note - need compiler fix for delete[] bug!!!
class Server
{
//	 

	friend class Manager;

public:
				Server();
				
	void		RecordInput( RecordedData *data_buffer );
	void		PlaybackInput( RecordedData *recorded_input );
	
private:

    void        service_handlers( void );

	SIO::Device                 *m_device;
	Tsk::Stack	                m_handler_stack;
	Data                        m_data;
	
	RecordedData				*m_data_in;
	void						*m_data_in_end;
	RecordedData				*m_data_out;
	void						*m_data_out_end;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  Manager  : public Spt::Class
{
	                    

public :

	void                        AddHandler( BaseHandler &handler );
	void						ReassignHandler( BaseHandler &handler, int new_index );
	void                        AddPushHandler( BaseHandler &handler );
	Tsk::BaseTask&				GetProcessHandlersTask ( void ) const;
	
	void						RecordInput( int index, RecordedData *data_buffer, int byte_length );
	void						PlaybackInput( int index, RecordedData *recorded_input, int byte_length );
	void						PushInputLogicTasks( void );
	void						PopInputLogicTasks( void );
	bool						ActuatorEnabled( int i );
	
	void						DisableActuators( void );
	void						DisableActuator( int i );
	
	void						EnableActuators( void );
	void						EnableActuator( int i );
	void						ResetActuators( void );

	void						SetAnalogStickOverride( bool should_override_pad );
	bool						ShouldAnalogStickOverride( void );

private :
								~Manager ( void );
								Manager ( void );

	static Tsk::Task< Manager >::Code	process_handlers;
								
	Tsk::Task< Manager >*		m_process_handlers_task;
	Server                      m_server[SIO::vMAX_DEVICES];
	bool						m_override_pad_with_stick;
	
	DeclareSingletonClass( Manager );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline	Tsk::BaseTask&	Manager::GetProcessHandlersTask ( void ) const
{
	
	
	Dbg_AssertType ( m_process_handlers_task, Tsk::BaseTask );

	return	*m_process_handlers_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void Manager::AddHandler ( BaseHandler &handler )
{
	

    Dbg_MsgAssert( handler.m_Index < SIO::vMAX_DEVICES,( "Invalid controller index" ));
	
    m_server[handler.m_Index].m_handler_stack.AddTask( handler );
    handler.m_Input = &m_server[handler.m_Index].m_data;
    handler.m_Device = m_server[handler.m_Index].m_device;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void Manager::ReassignHandler( BaseHandler &handler, int new_index )
{
	Dbg_MsgAssert( new_index < SIO::vMAX_DEVICES,( "Invalid controller index" ));

	handler.Remove();
	m_server[new_index].m_handler_stack.AddTask( handler );
    handler.m_Input = &m_server[new_index].m_data;
    handler.m_Device = m_server[new_index].m_device;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void Manager::AddPushHandler ( BaseHandler &handler )
{
	

	Dbg_MsgAssert( handler.m_Index < SIO::vMAX_DEVICES,( "Invalid controller index" ));
	
    m_server[handler.m_Index].m_handler_stack.AddPushTask( handler );
    handler.m_Input = &m_server[handler.m_Index].m_data;
    handler.m_Device = m_server[handler.m_Index].m_device;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline 
Handler< _T >::Handler( int index, Code* const _code, _T& _data, Tsk::BaseTask::Node::Priority pri )
: BaseHandler( index, pri ), code( _code ), data( _data ) 
{
    
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline 
Handler< _T >::~Handler( void )
{
    
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline 
void		Handler< _T >::vCall( void ) const
{
	
	
	Dbg_AssertPtr( code );
	code( *this );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline 
_T&		Handler< _T >::GetData( void ) const
{
	
	
	Dbg_AssertType( &data, _T );
	return data;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline 
void *		Handler< _T >::GetCode( void ) const
{
	

	return NULL;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Inp

#endif	// __GEL_INPMAN_H


================================================
FILE: Code/Gel/mainloop.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Main Loop Module (ML)									**
**																			**
**	File name:		gel/mainloop.h											**
**																			**
**	Created: 		05/27/99	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __GEL_MAINLOOP_H
#define __GEL_MAINLOOP_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/
   
namespace Mlp
{



/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class  Manager  : public Spt::Class
{
	

public :

	enum ELogicMask
	{
		mNONE			= 0,
		mSKATE_MOD 		= (1<<1), // skate module
		mGAME_OBJECTS	= (1<<2), // game objects
		mGFX_MANAGER	= (1<<3), // gfx manager
	};

	void				RegisterRenderStartHook ( Tsk::BaseHook* start_hook );
	void				RegisterRenderEndHook ( Tsk::BaseHook* end_hook );

	Tsk::BaseHook*		GetRenderStartHook( void );
	Tsk::BaseHook*		GetRenderEndHook( void );
	
	void				MainLoop ( void );
	void				QuitLoop ( void );
	void				DoGameLogic( void );

	void				PauseDisplayTasks( bool pause = true );
	void				RemoveAllDisplayTasks ( void );
	void				RemoveAllLogicTasks ( void );
	void				RemoveAllSystemTasks ( void );
	
	void				PushLogicTasks ( void );
	void				PushDisplayTasks ( void );
	void				PushSystemTasks ( void );
	
	void				PopLogicTasks ( void );
	void				PopDisplayTasks ( void );
	void				PopSystemTasks ( void );

	void				AddLogicTask ( Tsk::BaseTask& task );
	void				AddDisplayTask ( Tsk::BaseTask& task );
	void				AddSystemTask ( Tsk::BaseTask& task );

	void				AddLogicPushTask ( Tsk::BaseTask& task );
	void				AddDisplayPushTask ( Tsk::BaseTask& task );
	void				AddSystemPushTask ( Tsk::BaseTask& task );

	void				ProfileTasks(int n=1);
	void				SetLogicMask ( uint mask );

	bool				IsProfiling(){return currently_profiling;}
	
private :
						Manager ( void );
						~Manager ( void );

	void				game_logic ( void );
	void				render_frame ( void );
	void				service_system ( void );

	bool				done;
	Tsk::Stack			logic_task_stack;
	Tsk::Stack			display_task_stack;
	Tsk::Stack			system_task_stack;
	Tsk::BaseHook*		start_render_hook;
	Tsk::BaseHook*		end_render_hook;

	bool				display_tasks_paused;
	
	int				trigger_profiling;
	int				currently_profiling;
	
	DeclareSingletonClass( Manager );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**									Macros									**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline	void		Manager::RegisterRenderStartHook ( Tsk::BaseHook* start_hook )
{
   	

    start_render_hook = start_hook;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::RegisterRenderEndHook ( Tsk::BaseHook* end_hook )
{
   	
	
	end_render_hook = end_hook;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Tsk::BaseHook*	Manager::GetRenderStartHook( void )
{
	

	return start_render_hook;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Tsk::BaseHook*	Manager::GetRenderEndHook( void )
{
	

	return end_render_hook;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::AddLogicTask ( Tsk::BaseTask& task )
{
   	
	
	Dbg_AssertType ( &task, Tsk::BaseTask );

	logic_task_stack.AddTask ( task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::SetLogicMask ( uint mask )
{
   	

	logic_task_stack.SetMask ( (uint) mask );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::AddDisplayTask ( Tsk::BaseTask& task )
{
   	
	
	Dbg_AssertType ( &task, Tsk::BaseTask );

	display_task_stack.AddTask ( task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::AddSystemTask ( Tsk::BaseTask& task )
{
   	
	
	Dbg_AssertType ( &task, Tsk::BaseTask );

	system_task_stack.AddTask ( task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::AddSystemPushTask ( Tsk::BaseTask& task )
{
   	
	
	Dbg_AssertType ( &task, Tsk::BaseTask );

	system_task_stack.AddPushTask ( task );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::AddLogicPushTask ( Tsk::BaseTask& task )
{
   	
	
	Dbg_AssertType ( &task, Tsk::BaseTask );

	logic_task_stack.AddPushTask ( task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::AddDisplayPushTask ( Tsk::BaseTask& task )
{
   	
	
	Dbg_AssertType ( &task, Tsk::BaseTask );

	display_task_stack.AddPushTask ( task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::RemoveAllDisplayTasks ( void )
{
   	

	display_task_stack.RemoveAllTasks ();
}

inline void		Manager::PauseDisplayTasks( bool pause )
{
   	
	display_tasks_paused = pause;

}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::RemoveAllLogicTasks ( void )
{
   	

	logic_task_stack.RemoveAllTasks ();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::RemoveAllSystemTasks ( void )
{
   	

	system_task_stack.RemoveAllTasks ();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::PushLogicTasks ( void )
{
   	

	logic_task_stack.Push ();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::PushDisplayTasks ( void )
{
   	

	display_task_stack.Push ();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::PushSystemTasks ( void )
{
   	

	system_task_stack.Push ();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::PopLogicTasks ( void )
{
   	

	logic_task_stack.Pop ();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::PopDisplayTasks ( void )
{
   	

	display_task_stack.Pop ();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		Manager::PopSystemTasks ( void )
{
   	

	system_task_stack.Pop ();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mlp

#endif	// __GEL_MAINLOOP_H




================================================
FILE: Code/Gel/modman.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Module  (MDL)											**
**																			**
**	File name:		gel/modman.h											**
**																			**
**	Created: 		05/27/99	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __GEL_MODMAN_H
#define __GEL_MODMAN_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
					  
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mdl
{



/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class  Manager  : public Spt::Class
{
	

public :
	
	void						RegisterModule ( Module &module );
	void						UnregisterModule ( Module &module );

	void						StartModule ( Module &module );
	void						RestartModule( Module &module );
	void						StopModule ( Module &module );
	void						StartAllModules ( void );
	void						StopAllModules ( void );

	void						LockAllModules ( void );
	void						UnlockAllModules ( void );

	Tsk::BaseTask&				GetProcessModulesTask ( void ) const;
	

private :
								Manager ( void );
								~Manager ( void );

	static Tsk::Task< Manager >::Code	process_modules;
								
	Tsk::Task< Manager >*		process_modules_task;
	Lst::Head< Module >			module_list;
	bool						control_change;
		
	DeclareSingletonClass( Manager );

};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline	Tsk::BaseTask&	Manager::GetProcessModulesTask ( void ) const
{
   	
	
	Dbg_AssertType ( process_modules_task, Tsk::BaseTask );

	return	*process_modules_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mdl

#endif	// __GEL_MODMAN_H


================================================
FILE: Code/Gel/module.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Module  (MDL)											**
**																			**
**	File name:		gel/module.h											**
**																			**
**	Created: 		05/27/99	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __GEL_MODULE_H
#define __GEL_MODULE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mdl
{



/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class  Module  : public Spt::Class
{
	

	friend class			Manager;

	friend Tsk::Task< Manager >::Code	MDL_process_modules;

public :

							Module ( void );
	virtual	 				~Module ( void );

	void					Lock ( void );
	void					Unlock ( void );

	bool					Running ( void ) const;
	bool					Locked ( void ) const;

private :

	enum State
	{
		vSTOPPED,
		vRUNNING
	};

	enum Command
	{
		vNONE,
		vSTART,
		vSTOP,
		vRESTART,
	};
	virtual	void			v_start_cb ( void ) = 0;
	virtual	void			v_stop_cb ( void ) = 0;

	State					state;
	Command					command;
	bool					locked;
	Lst::Node*		node;

};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline void			Module::Lock ( void )
{
   	

	locked = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void			Module::Unlock ( void )
{
   	

	locked = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	bool		Module::Locked ( void ) const
{
   	

	return locked;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool			Module::Running ( void ) const
{
   	

	return ( state == vRUNNING );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mdl

#endif	// __GEL_MODULE_H


================================================
FILE: Code/Gel/object.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Object (OBJ)											**
**																			**
**	File name:		gel/object.h											**
**																			**
**	Created: 		05/27/99	-	mjb										**
**																			**
**  Notes:
**
**  (Mick) This is the base type for all objects in the game.  It contains basic
**  mechanisms for maintaining a list of object associated with Object Servers
**  and procedures for deleting them
**  There are also hooks into the CScript class, in that an object has a 
**  reference to a script, and a virtual function CallMember, which allows 
**  the script to call a member function by name (actually by checksum of the name)  
**
**
*****************************************************************************/

#ifndef __GEL_OBJECT_H
#define __GEL_OBJECT_H

#define	__SCRIPT_EVENT_TABLE__		// define this is you want event tables attached to scripts, rather than objects

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

#include 

#include 
#include 

namespace Script
{
	class CStruct;
	class CScript;
}	

struct SBBox{
	Mth::Vector m_max;
	Mth::Vector m_min;
	Mth::Vector centerOfGravity;
	float m_radius;  // will give the max distance squared where there may still be a collision.
};

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define OBJECT_FLAG_INVISIBLE	1

namespace Front
{
	class CScreenElementManager;
}

namespace Script
{
	class CArray;
}

enum EReplaceEventHandlers
{
	DONT_REPLACE_HANDLERS = 0,
	REPLACE_HANDLER
};


namespace Obj			 
{

/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

class CEvent;
class CEventHandlerTable;
class CBaseManager;
class CGeneralManager;
class CObject;
typedef CSmtPtr CObjectPtr;


/*
	CObject is described in more detail in: Q:\sk4\Docs\programming\Object Scripting System.htm
	
	The base class for all scriptable objects. Every CObject has a global ID that
	is unique among all CObjects. It also has a type specifier.
	
	A script that is running can reference a CObject. The script can access special
	commands for that CObject through CallMemberFunction().	Tags, which are like
	scriptable member variables, can be attached from CObjects from script, too.
	
	Event handling is supported through the PassTargetedEvent() function.
*/
class  CObject  : public CRefCounted
{
  	
	friend class		CBaseManager;
	friend class		CGeneralManager;
	friend class		Front::CScreenElementManager;

public :						
						CObject();
	virtual				~CObject ( void );

	void				DestroyIfUnlocked ();				// Destroy immediately 

	void				AddReference ( void );
	void				RemoveReference ( void );
	bool				IsReferenced() {return (m_ref_count > 0);}

	bool				IsDead ( void ) const;				// Access functions for the above
	bool				IsLocked ( void ) const;
		
	uint32				GetID() const  {return m_id;}
	sint				GetType() const {return m_type;}
	void				SetID(uint32 id);
	void				SetType(sint type);
	
	void				ClearStampBit(uint32 stamp_mask) { m_stamp &= ~stamp_mask; }
	bool				CheckStampBit(uint32 stamp_mask) const { return m_stamp & stamp_mask; }
	void				SetStampBit(uint32 stamp_mask) { m_stamp |= stamp_mask; }

	uint32				GetFlags() const {return m_object_flags;}
	void				SetFlags(uint32 flags) {m_object_flags = flags;}
	void				SetLockOn() {m_object_flags |= vLOCKED;}
	void				SetLockOff() {m_object_flags &= ~vLOCKED;}
	
	#ifdef	__NOPT_ASSERT__		
	void				SetLockAssertOn() {m_object_flags |= vLOCKEDASSERT;}
	void				SetLockAssertOff() {m_object_flags &= ~vLOCKEDASSERT;}
	bool				GetLockAssert() {return m_object_flags & vLOCKEDASSERT;}
	#endif

	Script::CScript	*	GetScript() const {return mp_script;}
	void				SetScript(Script::CScript* p_script) {mp_script = p_script;}



	void  				SetEventHandlers(Script::CArray *pArray, EReplaceEventHandlers replace = DONT_REPLACE_HANDLERS);
	void				RemoveEventHandler(uint32 type);
	void				RemoveEventHandlerGroup(uint32 group);
	
	// See details on tags in object document
	void				SetIntegerTag(uint32 name, int value);
	void				SetChecksumTag(uint32 name, uint32 value);
	void				RemoveFlagTag(uint32 name);
	bool				GetIntegerTag(uint32 name, int *pResult) const ;
	bool				GetChecksumTag(uint32 name, uint32 *pResult) const;
	bool				ContainsFlagTag(uint32 name) const;
	void				SetTagsFromScript(Script::CStruct *pStruct);
	void 				RemoveTagsFromScript(Script::CArray *pNameArray);
	void				CopyTagsToScriptStruct(Script::CStruct *pStruct);
	void 				SetVectorTag(uint32 name, Mth::Vector	v);
	bool 				GetVectorTag(uint32 name, Mth::Vector *pResult) const;


	void 				SetOnExceptionScriptChecksum(uint32 OnExceptionScriptChecksum);
	uint32 				GetOnExceptionScriptChecksum() const; 

	
	// subclasses that extend next three functions should still call CObject versions
	virtual void		SetProperties(Script::CStruct *pProps);
	virtual bool 		CallMemberFunction (uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript);	// Call a member function based on the checksum of the function name
	virtual void		GetDebugInfo(Script::CStruct *p_info);
	
	// Called by CTracker singleton to pass events to this CObject
	virtual bool		PassTargetedEvent(CEvent *pEvent, bool broadcast = false);	// return true if object still valid

	virtual void		HideForReplayPlayback() {}
	virtual void		RestoreAfterReplayPlayback() {}
	
												  
	enum
	{
		vDEAD						= ( 1 << 0 ),
		// if locked, can't be deleted
		vLOCKED						= ( 1 << 1 ),
		vINVISIBLE					= ( 1 << 2 ),

		// this flag has been moved to the ExceptionComponent:
		//		vDISABLE_EXCEPTIONS			= ( 1 << 3 ),
	
	#ifdef	__NOPT_ASSERT__	
		vLOCKEDASSERT				= ( 1 << 4 ),
	#endif
		vCOMPOSITE				= ( 1 << 5 ),
		
		
		// RJM: flags from 8 on are reserved for children of CObject
		
	};

protected:

						CObject ( CBaseManager* );
	
	void				allocate_tags_if_needed();

public:	
	void 				AllocateScriptIfNeeded();
	
	void 				debug_validate_smart_pointers(CSmtPtr *pPtrToCheckForInclusion = NULL);
	void				MarkAsDead( void );			// Safe kill called from within the object's logic

	CBaseManager *			GetManager() const	{return mp_manager;}	// just for exception component stuff

protected:
	
	uint32				m_object_flags;
	
	CBaseManager *			mp_manager;
	
	// object script stuff:
	Script::CScript*	mp_script;
	
	// object ID, globally unique among CObjects
	uint32				m_id;
	sint				m_type;						// Object type game dependent
	uint32				m_stamp;

#ifndef	__SCRIPT_EVENT_TABLE__		
	CEventHandlerTable *	mp_event_handler_table;
	
	// Script to run when there is an exception	
	// (needed here, since we are getting rid of CExceptionComponent, to replace it with one 
	// single event system
	uint32 			mOnExceptionScriptChecksum;
#endif	 

	// Tags are basically variables that can be attached and queried by the scripting
	// system at runtime. Very convenient for providing state info.
	Script::CStruct	*		mp_tags;	 

public:
	// (Moved here from CMovingObject)	
	// A set of flags that can be set using the SendFlag script command. Used for sending messages to objects.
	uint32  mScriptFlags;
	uint32	GetFlags( Script::CStruct *pParams, Script::CScript *pScript ) const;

	// K: Used by CScript::TransmitInfoToDebugger to determine whether the CScript is the
	// main script of it's parent object.
	bool	MainScriptIs(const Script::CScript *p_script) const {return mp_script==p_script;}
	
	// standard objects can not be paused; thus, spawned (non-main) scripts running on them should never be paused
	virtual bool		ShouldUpdatePauseWithObjectScripts (   ) { return true; }
	
	// K: Also used by CScript::TransmitInfoToDebugger
	Script::CStruct *GetTags() const {return mp_tags;}

public:
	// GJ:  Made this public so that components can set their
	// parent objects' scripts
	void				SwitchScript( uint32 scriptChecksum, Script::CStruct *pParams );
	Script::CScript*	SpawnScriptPlease( uint32 scriptChecksum, Script::CStruct *pParams, int Id = 0, bool pause_with_object = false );
	void				SpawnAndRunScript( uint32 ScriptChecksum, int node = -1, bool net_script = false, bool permanent = false, Script::CStruct *p_params = NULL );
	void				SpawnAndRunScript( const char *pScriptName, int node = -1, bool net_script = false, bool permanent = false, Script::CStruct *p_params = NULL );

	void				CallScript( uint32 ScriptChecksum, Script::CStruct *pParams );

	void 				SelfEvent(uint32 event, Script::CStruct *pData = NULL);
	void				BroadcastEvent(uint32 event, Script::CStruct *pData = NULL, float radius = 0.0f);
	
protected:
	Lst::Node< CObject >	m_node;
	
private:
	int						m_ref_count;
};




/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

void		DestroyIfUnlocked ( CObject* obj, void* data = NULL );
void		SetLockOff ( CObject* obj, void* data = NULL );
CObject *	ResolveToObject(uint32 id);

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline	void			CObject::AddReference ( void )
{
	m_ref_count++;
}


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void			CObject::RemoveReference ( void )
{
	//Dbg_Assert(m_ref_count > 0);
	if (m_ref_count > 0)
	{
		m_ref_count--;   	
	}
	// XXX
	else
	{
		#ifdef	__NOPT_ASSERT__
		printf("Aaaah!! Removing more references than there are\n");
		#endif
		Dbg_Assert(( m_object_flags & ( 1 << 16 )) == 0 );
//		if (m_object_flags & (1<<16)) //Front::CScreenElement::vIS_SCREEN_ELEMENT
//			Dbg_Assert(0);
	
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	bool			CObject::IsLocked ( void ) const 
{
	return ( m_object_flags & ( vLOCKED ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	bool			CObject::IsDead ( void ) const 
{
   	
	return ( m_object_flags & ( vDEAD ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj

#endif	// __GEL_OBJECT_H


================================================
FILE: Code/Gel/objman.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Object (OBJ)											**
**																			**
**	File name:		gel/objman.h											**
**																			**
**	Created: 		05/27/99	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __GEL_OBJMAN_H
#define __GEL_OBJMAN_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#ifndef __GEL_OBJECT_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Lst
{
	template< class _V > class HashTable;
}



namespace Obj
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

	
	
	
/*
	This class manages usage of the bits of a uint32.  Use of CBitManager insures that no two sections of the greater code are using the same bit.  When
	a bit is requested from a CBitManager, no other requests for bits will give that bit until access to the bit has been returned to the manager.
	Bit requests and returns are done in the form of uint32 masks.
*/
class CBitManager
{
public:
								CBitManager (   );
	
	uint32						RequestBit (   );
	void						ReturnBit ( uint32 mask );
	
private:
	uint32						m_used_bit_mask;
};




/*
	See document for more info on this class: Q:\sk4\Docs\programming\Object Scripting System.htm
	
	The base class for all object managers. An object manager keeps track of a related
	set of CObjects (e.g. the screen element manager manages all CScreenElements).
	The manager might also be responsible for creation and destruction of its CObjects.
	
	An subclass of CBaseMananger must register its CObjects with the CTracker singleton.
	
	This is a virtual base class.
*/
class CBaseManager : public Spt::Class
{
public:	
	
	static const uint32			vNO_GROUP;
	static const uint32			vNO_OBJECT_ID;
								
	static const sint			vNO_OBJECT_TYPE;

								CBaseManager();
	virtual 					~CBaseManager() {;}
	
	virtual void				RegisterObject ( CObject& obj );
	virtual void				UnregisterObject ( CObject& obj );
	virtual void				ReregisterObject ( CObject& obj, uint32 newId );
	
	virtual void				SetObjectPriority ( CObject& obj, Lst::Node< CObject >::Priority priority );
	
	// Called by CObject::mark_as_dead()
	virtual void				KillObject ( CObject& obj ) = 0;
	
	virtual Lst::Head< CObject > &GetRefObjectList() = 0;

protected:

	Lst::Head< CObject >		m_object_list;	// list of created objects
	
	CBitManager					m_stamp_bit_manager;
	
	uint32						m_list_changed;

	// important in ReregisterObject(); if applicable, set to ID of new object,
	// otherwise, zero
	uint32						m_new_id_of_object_being_momentarily_removed;
	bool						m_momentary_removal;
};




/*
	See document for more info on this class: Q:\sk4\Docs\programming\Object Scripting System.htm
	
	The generic implementation of CBaseManager. Use when you want a very
	basic manager only.
*/
class  CGeneralManager  : public CBaseManager
{
	

public :
	
	typedef	void (Callback) ( CObject*, void *data );

								CGeneralManager ( void );
								~CGeneralManager ( void );

	void						ProcessAllObjects ( Callback* process, void* data = NULL );
	void						ProcessAllObjectsOfType ( sint type, Callback* process, 
														  void* data = NULL );

	void						DestroyAllObjects ( void );
	void						DestroyAllObjectsOfType ( sint type );

	void						UnlockAllObjects ( void );
	void						UnlockAllObjectsOfType ( sint type );
	

	void						KillObject ( CObject& obj );

	uint32						NewObjectID( void );
	sint						CountObjectsOfType ( sint type );
	void 						AssertIfObjectsRemainApartFrom( sint *pApartFromThisType );


	void 						FlushDeadObjects();

	CObject*					GetObjectByID( uint32 id );
	
	Lst::Head< CObject > &		GetRefObjectList() {return m_object_list;}

protected :
	
	static Tsk::Task< CGeneralManager >::Code	flush_dead_objects;

	Lst::Head< CObject >		m_kill_list;		// list of objects to be killed
	Tsk::Task< CGeneralManager >*		mp_kill_task;		// kill task that processes the kill list

//	Lst::HashTable *	mp_hash_table;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline				CBitManager::CBitManager (   )
	:	m_used_bit_mask(0x00000000)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	uint32		CBitManager::RequestBit (   )
{
	Dbg_MsgAssert(m_used_bit_mask != 0xFFFFFFFF, ("Out of available bits in CBitManager::RequestBit"));
	
	uint32 ready_mask = nBit(0);
	while (m_used_bit_mask & ready_mask)
	{
		ready_mask <<= 1;
	}
	
	m_used_bit_mask |= ready_mask;
	
	return ready_mask;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		CBitManager::ReturnBit ( uint32 returning_mask )
{
	Dbg_MsgAssert((returning_mask & (returning_mask - 1)) == 0, ("Calling CBitManager::ReturnBit with a mask contining more than a single set bit"));
	Dbg_MsgAssert(returning_mask & m_used_bit_mask, ("Returning an unused bit to CBitManager::ReturnBit"));
	
	m_used_bit_mask &= ~returning_mask;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		CGeneralManager::DestroyAllObjects ( void )
{
   	
	
	ProcessAllObjects ( DestroyIfUnlocked );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
inline	void		CGeneralManager::DestroyAllObjectsOfType ( sint type )
{
   	

	ProcessAllObjectsOfType ( type, DestroyIfUnlocked );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void		CGeneralManager::UnlockAllObjects ( void )
{
   	

	ProcessAllObjects ( SetLockOff, NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
inline	void		CGeneralManager::UnlockAllObjectsOfType ( sint type )
{
   	

	ProcessAllObjectsOfType ( type, SetLockOff );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj

#endif	// __GEL_OBJMAN_H


================================================
FILE: Code/Gel/objsearch.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Objects (OBJ) 											**
**																			**
**	File name:		gel/objsearch.h											**
**																			**
**	Created:		05/27/99	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __GEL_OBJSEARCH_H
#define __GEL_OBJSEARCH_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __GEL_OBJECT_H
#include 
#endif
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

class  Search : public  Lst::Search< CObject > 
{
	

public :

	CObject*			FindFirstObjectOfType  ( Lst::Head< CObject >& head, sint type );

	CObject*			FindNextObjectOfType  ( void );

private :

	sint			obj_type;
	
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __GEL_OBJSEARCH_H


================================================
FILE: Code/Gel/objserv.h
================================================

/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GEL (Game Engine Library)								**
**																			**
**	Module:			Base Object-Server (OS)							        **
**																			**
**	File name:		gel/server/os_cb.h										**
**																			**
**	Created: 		07/23/99	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __GEL_OBJSERV_H
#define __GEL_OBJSERV_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace ObjServ
{



/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class  Object  : public Spt::Class
{
	
    
public:

	virtual		~Object( void ) {}

protected:
				Object( void ) {}
	
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  Server  : public Spt::Class
{
	

public:
	virtual void ProcessAllObjects ( void ) = 0;
    virtual void RegisterObject( Object *object ) = 0;
    
    virtual     ~Server( void ) {}

protected:
				Server( void ) {}
    
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace ObjServ

#endif	// __GEL_OBJSERV_H



================================================
FILE: Code/Gel/objtrack.h
================================================
#ifndef __GEL_OBJTRACK_H
#define __GEL_OBJTRACK_H

#include 

namespace Script
{
	class CStruct;
	class CScript;
}


namespace Front
{
	class CScreenElementManager;
}


namespace Lst
{
	template< class _V > class HashTable;
}


namespace Obj
{

class CObject;
//class CEvent;
class CEventListener;
	

/*
	An instance of this class is attached to the CTracker singleton. It tracks the launching
	and handling of all events in the system, for debugging purposes.
*/
class CEventLog
{
public:
	
	enum EOccurenceType
	{
		vLAUNCHED,
		vHANDLED,
		vREAD,
		vUPDATE,
		vOBJECT_ADD,
		vOBJECT_REMOVE,
	};

	enum
	{
		MAX_LOG_ENTRIES			= 1024,
		MAX_EVENT_TYPES			= 256,
	};

								CEventLog();
								~CEventLog();

	void						AddEntry(uint32 type, uint32 target, uint32 source, uint32 script, uint32 receiverID, EOccurenceType lifePoint);
	void						Print(bool onlyPrintNewEntries = true, int maxEntriesToPrint = MAX_LOG_ENTRIES);
	void						PrintLatestEntry();
	void						Clear() {m_num_new_entries = 0;}

private:

	void						print_entry(int index);
	const char *				get_type_name(uint32 type);
	
	struct Entry
	{
		union
		{
			uint32				m_type;
			int					m_tick_count; // applies to update type entries
		};
		uint32 					m_target;
		uint32 					m_source;
		uint32					m_script;					
		uint32					m_receiver_id;
		EOccurenceType			m_occurence_type;
		int						m_depth;
	};

	Entry						m_table[MAX_LOG_ENTRIES];
	int							m_next_entry;
	bool						m_wrapped;

	struct EventType
	{
		//uint32					mCrc;
		char					mName[64];
	};
	EventType					m_event_type_table[MAX_EVENT_TYPES];
	Lst::HashTable *	mp_event_type_hash_table;

	int							m_event_depth;
	int							m_num_new_entries;
};


// CEventReceiverList comprises of an event type
// and a list of object that can receive this type of event
class	CEventReceiverList : public Spt::Class
{
public:
				CEventReceiverList();

#ifdef	__SCRIPT_EVENT_TABLE__		
	void		RegisterEventReceiverObject(Script::CScript *p_obj);
	void		UnregisterEventReceiverObject(Script::CScript *p_obj);
	bool		IsEmpty() {return m_objects.IsEmpty();};
	Lst::Node< Script::CScript >*	FirstItem() {return m_objects.FirstItem();}
	int			CountItems() {return m_objects.CountItems();}
private:
//	uint32						m_type;	   					// The type of event this is
	Lst::Head		m_objects;			// a list of objects (SCRIPTS) that are listening for it
#else
	void		RegisterEventReceiverObject(CObject *p_obj);
	void		UnregisterEventReceiverObject(CObject *p_obj);
	bool		IsEmpty() {return m_objects.IsEmpty();};
	Lst::Node< Obj::CObject >*	FirstItem() {return m_objects.FirstItem();}
	int			CountItems() {return m_objects.CountItems();}
private:
//	uint32						m_type;	   					// The type of event this is
	Lst::Head		m_objects;			// a list of objects that are listening for it
#endif
};


/*
	See document for more info on this class: Q:\sk4\Docs\programming\Object Scripting System.htm
	
	This class keeps track of all registered CObjects, and provides functions for retreiving
	them by ID. It also takes care of launching and distributing events. It also can reactivate
	scripts that are suspended while waiting for an event.
*/
class CTracker : public Spt::Class
{
	friend class CBaseManager;
	friend class CGeneralManager;
	friend class Front::CScreenElementManager;

	DeclareSingletonClass( CTracker );

public:								
								
	// In logging
	enum ELoggingSystem
	{
		vID_UNSPECIFIED_RECEIVER				= 0,
		vID_SCREEN_ELEMENT_MANAGER				= 1,
		vID_SUSPENDED_SCRIPT					= 2,
		vID_OBJECT_TRACKER						= 3,
	};

								CTracker();
	virtual						~CTracker();

	CObject *					GetObject(uint32 id);
	
	// See the menu document for a description of aliases
	CObject *					GetObjectByAlias(uint32 aliasId);
	void						AddAlias(uint32 alias, CObject *pObject);

	uint32						GetFreshId();

	#ifdef	__NOPT_ASSERT__
	void 						ValidateReceivers();
	#endif
	

	bool						LaunchEvent(uint32 type, uint32 target = CEvent::vSYSTEM_EVENT, uint32 source = CEvent::vSYSTEM_EVENT, Script::CStruct *pData = NULL, bool broadcast=false);
	void						BlockEventLaunching(bool block) {m_block_event_launching = block;}
	
	void 						LogEventScript(uint32 script = 0);
	void						LogEventHandled(CEvent *pEvent, uint32 receiverID = 0, uint32 script = 0);
	void						LogEventRead(CEvent *pEvent, uint32 receiverID = 0, uint32 script = 0);
	void						LogTick();
	void 						PrintEventLog(bool mostRecentOnly, int maxToPrint);
	void						EmptyLog() {m_event_log.Clear();}

// Event Listener are special objects that will get sent all types of events
// and it is up to them to 	
	void 						RegisterEventListener(CEventListener *pListener);
	void						UnregisterEventListener(CEventListener *pListener);

// Event Receivers are just CObject and they only get sent events that they tell the tracker they are listening for
	
#ifdef	__SCRIPT_EVENT_TABLE__		
	void						RegisterEventReceiver(uint32 type, Script::CScript *p_obj);
	void						UnregisterEventReceiver(uint32 type, Script::CScript *p_obj);
#else	
	void						RegisterEventReceiver(uint32 type, CObject *p_obj);
	void						UnregisterEventReceiver(uint32 type, CObject *p_obj);
#endif	
	// Note, removing an object should remove all event receivers.

	void						SuspendScriptUntilEvent(Script::CScript *pScript, uint32 event_type);

private:

	void						addObject(CObject *pObject);
	void 						removeObject(CObject *pObject, uint32 newIdOfObjectBeingMomentarilyRemoved, bool momentary_removal);

	void						forward_event_to_listeners(CEvent *pEvent, CObject *pObject);

	void						remove_aliases(CObject *pObject);
	
	int							m_id_seed;
	
	Lst::HashTable *	mp_hash_table;
	Lst::HashTable *	mp_alias_table;

// The hash table of event listeners is keyed off the "type" of the event
// (the "type" is the actual event name, as a uint32 CRC)	
	Lst::HashTable *	mp_event_receiver_table;
	
	CEventListener *			mp_event_listener_list;
	
	bool						m_block_event_launching;
	enum 
	{
		vMAX_EVENT_RECURSE_DEPTH = 32,
	};
	int							m_event_recurse_depth;

	CEventLog					m_event_log;
	uint32						m_next_event_script;
	
	// Used to keep track of of scripts that are suspended while waiting for an event.
	// No pointers to scripts are kept, in case scripts are destroyed.
	struct WaitingScriptEntry
	{
		// the unique ID of the script
		uint32					mScriptId;
		#ifdef __NOPT_ASSERT__
		uint32					mScriptName;
		#endif
		// 0 denotes a dead entry
		uint32				  	mEventType;
		// the object on which the script is running
		uint32					mObjectId;
	};

	enum
	{
		vMAX_SCRIPT_ENTRIES 	= 40,
		vDEAD_SCRIPT_ENTRY 		= 0,
	};

	WaitingScriptEntry			m_waiting_script_tab[vMAX_SCRIPT_ENTRIES];

	bool						m_debug;
};
	


bool ScriptLaunchEvent(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptWaitForEvent(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptObjectExists(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptTerminateObjectsScripts(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAssignAlias(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetObjectProperties(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPrintEventLog(Script::CStruct *pParams, Script::CScript *pScript);



} // end namespace

#endif



================================================
FILE: Code/Gfx/2D/BlurEffect.cpp
================================================
#include 
#include 

#include 
#include 

namespace Front
{



CBlurEffect::CBlurEffect()
{
}




CBlurEffect::~CBlurEffect()
{
}




void CBlurEffect::Update()
{
	Tmr::Time current_time = Tmr::GetTime();
	float motion_grad = (float) (current_time - m_last_time) / (float) m_key_time;
	if (current_time - m_last_time < m_key_time)
		m_key_time -= current_time - m_last_time;
	else
		m_key_time = 0;
	m_last_time = current_time;
	
	if (m_key_time)
	{
		m_current.topAlpha = m_current.topAlpha + (m_target.topAlpha - m_current.topAlpha) * motion_grad;
		m_current.bottomAlpha = m_current.bottomAlpha + (m_target.bottomAlpha - m_current.bottomAlpha) * motion_grad;
		m_current.bottomScaleX = m_current.bottomScaleX + (m_target.bottomScaleX - m_current.bottomScaleX) * motion_grad;
		m_current.bottomScaleY = m_current.bottomScaleY + (m_target.bottomScaleY - m_current.bottomScaleY) * motion_grad;
		m_current.maxDisplacementX = m_current.maxDisplacementX + (m_target.maxDisplacementX - m_current.maxDisplacementX) * motion_grad;
		m_current.maxDisplacementY = m_current.maxDisplacementY + (m_target.maxDisplacementY - m_current.maxDisplacementY) * motion_grad;
	}
	else
	{
		m_current = m_target;
	}
	
	for (int i = 0; i < 16; i++)
	{
		float depth_mult = (float) i / 16.0f;
		m_tab[i].SetScreenPos(m_current.maxDisplacementX * depth_mult,
							  m_current.maxDisplacementY * depth_mult);
		m_tab[i].SetScale(1.0f + (m_current.bottomScaleX - 1.0f) * depth_mult,
						  1.0f + (m_current.bottomScaleY - 1.0f) * depth_mult);
		m_tab[i].alpha = (m_current.topAlpha + (m_current.bottomAlpha - m_current.topAlpha) * depth_mult) / 16.0f;
	}
}




CScreenElement::ConcatProps &CBlurEffect::GetInfo(int index)
{
	return m_tab[index];
}




void CBlurEffect::SetAllTargetProps(const Props &newProps, Tmr::Time time) 
{
	m_target = newProps;
	SetAnimTime(time);
}




void CBlurEffect::SetAnimTime(Tmr::Time time)
{
	if (time == 0)
		m_current = m_target;
	
	m_key_time = time;
	m_last_time = Tmr::GetTime();
}




const CBlurEffect::Props &CBlurEffect::SetMorph(Script::CStruct *pProps, Tmr::Time *pRetTime)
{	
	Script::CPair alpha_pair;
	if (pProps->GetPair(CRCD(0x21de240c,"blur_alpha_pair"), &alpha_pair))
	{
		SetAlphas(alpha_pair.mX, alpha_pair.mY);
	}

	Script::CPair bottom_scales;
	if (pProps->GetPair(CRCD(0x3e10436d,"blur_bottom_scales"), &bottom_scales))
	{
		SetBottomScales(bottom_scales.mX, bottom_scales.mY);
	}

	Script::CPair max_displacement;
	if (pProps->GetPair(CRCD(0x4c2a3cde,"blur_max_displacement"), &max_displacement))
	{
		SetMaxDisplacements(max_displacement.mX, max_displacement.mY);
	}
	
	float desired_time;
	Tmr::Time anim_time;
	if (pProps->GetFloat(CRCD(0x906b67ba,"time"), &desired_time))
	{
		anim_time = (Tmr::Time) (desired_time * (float) Tmr::vRESOLUTION);
	}
	else
		anim_time = 0;
	SetAnimTime(anim_time);
	if (pRetTime)
		*pRetTime = anim_time;

	return m_target;
}




}


================================================
FILE: Code/Gfx/2D/BlurEffect.h
================================================
#ifndef __GFX_2D_BLUREFFECT_H__
#define __GFX_2D_BLUREFFECT_H__

#include 
#include 

namespace Script
{
	class CStruct;
}

namespace Front
{

class CBlurEffect
{
public:

	struct Props
	{
		float						topAlpha;
		float						bottomAlpha;
		float 						bottomScaleX;
		float						bottomScaleY;					
		float						maxDisplacementX;
		float						maxDisplacementY;
	};
		
									CBlurEffect();
									~CBlurEffect();

	void							Update();

	CScreenElement::ConcatProps &	GetInfo(int index);
	int								GetNumEntries() {return 16;}
	Image::RGBA						GetRGBA() {return m_rgba;}

	void							SetAlphas(float top, float bottom) {m_target.topAlpha = top; m_target.bottomAlpha = bottom;}
	void							SetBottomScales(float scaleX, float scaleY) {m_target.bottomScaleX = scaleX; m_target.bottomScaleY = scaleY;}
	void							SetMaxDisplacements(float dispX, float dispY) {m_target.maxDisplacementX = dispX; m_target.maxDisplacementY = dispY;}
	void							SetRGBA(Image::RGBA rgba) {m_rgba = rgba;}
	void							SetAllTargetProps(const Props &newProps, Tmr::Time time);
	void							SetAnimTime(Tmr::Time time);

	const Props &					SetMorph(Script::CStruct *pProps, Tmr::Time *pRetTime = NULL);

protected:

	CScreenElement::ConcatProps		m_tab[16];

	Props							m_target;
	Props							m_current;

	Tmr::Time						m_last_time;
	Tmr::Time						m_key_time;
	
	Image::RGBA						m_rgba;
};

}

#endif // __GFX_2D_BLUREFFECT_H__




================================================
FILE: Code/Gfx/2D/Element3d.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 		// <<<<<<<< NEEDS MOVING!!!!
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Front
{

// added by your mother
// returns the scale that needs to be applied to (modelWorldW,modelWorldH) to make it fit (screenW,screenH)
static float sGetScaleFromScreenDimensions(float screenW, float screenH, float zOffset, float modelWorldW, float modelWorldH)
{
	// Inverse project.
	float desired_world_w = screenW * zOffset / (640.0f * (1.0f + screenW / 1280.0f));
	float desired_world_h = screenH * zOffset / (448.0f * (1.0f + screenW / 896.0f));

	float scale_x = desired_world_w / modelWorldW;
	float scale_y = desired_world_h / modelWorldH;
	return (-scale_x < -scale_y) ? -scale_x : -scale_y;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// screenX, screenY indicate where the origin of the model will be located
static void sGetWorldMatrixFromScreenPosition(int camera_num, Mth::Matrix *p_world_matrix, float screenX, float screenY, float zOffset, Mth::Vector &model_offs)
{
	Dbg_MsgAssert(p_world_matrix,("NULL p_world_matrix"));
	Dbg_MsgAssert((camera_num >= 0) && (camera_num < Nx::CViewportManager::vMAX_NUM_ACTIVE_VIEWPORTS), ("Camera number is out of range: %d", camera_num));

	Mth::Vector camera_space_pos;

#ifdef __PLAT_XBOX__
	// Don't think Mick's assumption below is correct - for 3D objects going through the rendering pipeline, no screen correction is required.
	screenX = screenX - ( 640 / 2 );
	screenY = screenY - ( 448 / 2 );

	// Adjust X for aspect ratio that is different than 4/3 (1.3333333)
	screenX *= (Nx::CViewportManager::sGetScreenAspect() / 1.3333333f);

	// Also, adjust for 720p (horrible hack).
	if( NxXbox::EngineGlobals.backbuffer_width > 640 )
	{
		screenX *= 0.9f;
		screenY *= 0.9f;
	}

	camera_space_pos.Set( -screenX * zOffset / 448.0f, screenY * zOffset / 448.0f, zOffset );
#else
	// Mick:  Need to convert from logical to physical screen coordinates
	screenX = SCREEN_CONV_X(screenX);
	screenY = SCREEN_CONV_Y(screenY);

	// Make ScreenX and ScreenY relative to the centre of the screen.
	screenX -= SCREEN_CONV_X(640)/2;
	screenY -= SCREEN_CONV_Y(448)/2;

	// Adjust X for aspect ratio that is different than 4/3 (1.3333333)
	screenX *= (Nx::CViewportManager::sGetScreenAspect() / 1.3333333f);

	// Inverse project.
	camera_space_pos.Set(-screenX * zOffset / SCREEN_CONV_X(448),
						 screenY * zOffset / SCREEN_CONV_Y(448),
						 zOffset);
#endif // __PLAT_XBOX__

	camera_space_pos += model_offs;
						 
	// We now have a 3d vector in camera space, so rotate by the camera matrix
	// to get world coords.	

	p_world_matrix->Ident();

	// Get the camera.
	Gfx::Camera *p_camera = Nx::CViewportManager::sGetActiveCamera(camera_num);
	if (p_camera)
	{
		Mth::Matrix cam_matrix=p_camera->GetMatrix();
		
		// Camera matrix might have been incorrectly set up with a translation in W
		// so clear it out to be safe
		// Probably should be handled at a higher level.
		cam_matrix[W].Set();

		// Apply the camera matrix to the camera_space_pos
		Mth::Vector world_pos = camera_space_pos * cam_matrix;
		// and add the camera position to get the final world position.
		Mth::Vector cam_pos = p_camera->GetPos();
		cam_pos[W] = 0.0f;
		world_pos += cam_pos;
		world_pos[W] = 0.0f;
		
	
		// Make the rotation part of the passed matrix be the camera matrix so that the model 
		// always faces the camera.
		*p_world_matrix*=cam_matrix;
		// Then add in the calculated world position.
		p_world_matrix->Translate(world_pos);
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CElement3d::CElement3d()
{
	for (int i=0; iGetAsset( skeletonChecksum, false );

	if ( !pSkeletonData )
	{
		Dbg_MsgAssert( 0, ("Unrecognized skeleton %s. (Is skeleton.q up to date?)", Script::FindChecksumName(skeletonChecksum)) );
	}
    
	Gfx::CSkeleton* pSkeleton = new Gfx::CSkeleton( pSkeletonData );
    
    Dbg_Assert( pSkeleton );
    Dbg_Assert( pSkeleton->GetNumBones() > 0 );
	
    pModel->SetSkeleton( pSkeleton );

    return pSkeleton;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::CModel* CElement3d::CreateModel( uint32 skeleton_name, Mth::Vector offset )
{
	Dbg_MsgAssert(m_num_modelsCreateSkeleton( p_new_model, skeleton_name );
	}

	p_new_model->SetColor(m_rgba.r,m_rgba.g,m_rgba.b,m_rgba.a);

	Dbg_MsgAssert(mp_models[m_num_models].mpModel==NULL,("mp_models[m_num_models].mpModel not NULL ?"));
	mp_models[m_num_models].mpModel=p_new_model;
	
	mp_models[m_num_models].mOffset=offset;
	++m_num_models;

	return p_new_model;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CElement3d::AddModel(const char *p_model_name, uint32 skeleton_name, Mth::Vector offset, int texDictOffset)
{
	Dbg_MsgAssert(m_num_modelsCreateSkeleton( p_new_model, skeleton_name );
    }

	if ( p_model_name )
	{
		// loads a geom, if one was specified
		Str::String fullModelName;
		fullModelName = Gfx::GetModelFileName(p_model_name, ".mdl");
		p_new_model->AddGeom(fullModelName.getString(), 0, true, texDictOffset);
	}
	
	p_new_model->SetColor(m_rgba.r,m_rgba.g,m_rgba.b,m_rgba.a);
	
	Dbg_MsgAssert(mp_models[m_num_models].mpModel==NULL,("mp_models[m_num_models].mpModel not NULL ?"));
	mp_models[m_num_models].mpModel=p_new_model;
	
	mp_models[m_num_models].mOffset=offset;
	++m_num_models;

	update_visibility();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CElement3d::AddModelFromSector(uint32 sectorName, Mth::Vector offset)
{
	Dbg_MsgAssert(m_num_modelsGetGeom();
		Dbg_Assert(p_geom);
		if( p_geom )
		{
			Nx::CGeom* p_cloned_geom = p_geom->Clone( true );
			p_cloned_geom->SetActive(true);
			p_new_model->AddGeom( p_cloned_geom, 0 );
		}
	}
	
	p_new_model->SetColor(m_rgba.r,m_rgba.g,m_rgba.b,m_rgba.a);
	
	Dbg_MsgAssert(mp_models[m_num_models].mpModel==NULL,("mp_models[m_num_models].mpModel not NULL ?"));
	mp_models[m_num_models].mpModel=p_new_model;
	
	mp_models[m_num_models].mOffset=offset;
	++m_num_models;

	update_visibility();
}


void CElement3d::AutoComputeScale()
{	
	Mth::Vector element_world_min, element_world_max;
	for (int i = 0; i < m_num_models; i++)
	{
		Nx::CGeom *p_geom = mp_models[i].mpModel->GetGeom(0);
		Dbg_Assert(p_geom);
		Mth::CBBox b_box = p_geom->GetBoundingBox();
		Mth::Vector model_min(mp_models[i].mOffset.GetX() + b_box.GetMin().GetX(),
							  mp_models[i].mOffset.GetY() + b_box.GetMin().GetY(),
							  mp_models[i].mOffset.GetZ() + b_box.GetMin().GetZ());
		Mth::Vector model_max(mp_models[i].mOffset.GetX() + b_box.GetMax().GetX(),
							  mp_models[i].mOffset.GetY() + b_box.GetMax().GetY(),
							  mp_models[i].mOffset.GetZ() + b_box.GetMax().GetZ());

		if (i == 0)
		{
			element_world_min = model_min;
			element_world_max = model_max;
		}
		else
		{
			if (model_min[X] < element_world_min[X])
				element_world_min[X] = model_min[X];
			if (model_min[Y] < element_world_min[Y])
				element_world_min[Y] = model_min[Y];
			if (model_min[Z] < element_world_min[Z])
				element_world_min[Z] = model_min[Z];

			if (model_max[X] > element_world_max[X])
				element_world_max[X] = model_max[X];
			if (model_max[Y] > element_world_max[Y])
				element_world_max[Y] = model_max[Y];
			if (model_max[Z] > element_world_max[Z])
				element_world_max[Z] = model_max[Z];
		}

		/*
		Ryan("offset is (%.2f,%.2f,%.2f), min is (%.2f,%.2f,%.2f), max is (%.2f,%.2f,%.2f)\n",
			 mp_models[i].mOffset.GetX(), mp_models[i].mOffset.GetY(), mp_models[i].mOffset.GetZ(),
			 b_box.GetMin().GetX(), b_box.GetMin().GetY(), b_box.GetMin().GetZ(),
			 b_box.GetMax().GetX(), b_box.GetMax().GetY(), b_box.GetMax().GetZ());
		*/
	}

	float element_world_w = element_world_max[X] - element_world_min[X];
	float element_world_h = element_world_max[Y] - element_world_min[Y];
	float element_world_l = element_world_max[Z] - element_world_min[Z];
	m_scale3d = sGetScaleFromScreenDimensions(m_base_w, m_base_h, 
								  m_camera_z, 
								  (element_world_w > element_world_l) ? element_world_w : element_world_l,
									element_world_h);
	
	/*
	Ryan("yo homies, world dims are: (%.2f,%.2f,%.2f), desired scale is: (%.5f,%.5f), camera_z1 is %.4f, camera_z2 is %.4f\n", 
		 element_world_w, element_world_h, element_world_l,
		 scale, scale, m_camera_z, m_camera_z + element_world_max[Z] * scale);
	*/
	//CScreenElement::SetScale(scale, scale, false, CScreenElement::FORCE_INSTANT);

	m_offset_of_origin_from_center.Set(-(element_world_max[X] + element_world_min[X]) / 2.0f,
									   -(element_world_max[Y] + element_world_min[Y]) / 2.0f,
									   -(element_world_max[Z] + element_world_min[Z]) / 2.0f,
									   0.0f);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This gets called by the SetProps script command.
// Also gets called when the element is first created by CreateScreenElement, all the params sent
// to CreateScreenElement get dent to this function.
void CElement3d::SetProperties(Script::CStruct *p_props)
{
	CScreenElement::SetProperties(p_props);
	
	uint32 skeletonChecksum = 0;
	p_props->GetChecksum( CRCD(0x9794932,"skeletonName"), &skeletonChecksum, false );
	
	const char* p_model_name="";
	Script::CArray *p_model_array=NULL;
	uint32 clone_model_name;
	uint32 sector_name;
	Script::CArray *p_sector_array=NULL;
	bool skip_empty_model = false;
    
    int texDictOffset = 0;
    p_props->GetInteger( CRCD(0xf891ac27,"TexDictOffset"), &texDictOffset, false );
	
	if (p_props->GetChecksum( CRCD(0x73ec4c8,"CloneModel"), &clone_model_name ))
	{
		UnloadModels();
		
		Mth::Vector off;
		off.Set();

		// Clone the geometry off of an existing model
		Obj::CCompositeObject* pObject = static_cast(Obj::CCompositeObjectManager::Instance()->GetObjectByID(clone_model_name));
		Dbg_MsgAssert( pObject, ( "Couldn't find object id %d to clone", clone_model_name ) );

		Obj::CModelComponent *p_src_model_comp = GetModelComponentFromObject(pObject);
		Dbg_MsgAssert( p_src_model_comp, ( "Couldn't find model component in object id %d", clone_model_name ) );
		Dbg_MsgAssert( p_src_model_comp->GetModel(), ( "Couldn't find CModel in model component of object id %d", clone_model_name ) );

		Script::CArray *p_geom_array=NULL;
		if (p_props->GetArray(CRCD(0x3cc0910b,"CloneModelGeoms"), &p_geom_array))
		{
			// Create new model
			Nx::CModel *p_model = CreateModel(skeletonChecksum, off);

			uint32 geomName;
			for (uint i = 0; i < p_geom_array->GetSize(); i++)
			{
				geomName = p_geom_array->GetChecksum(i);
				if (geomName)
				{
					Nx::CGeom *p_orig_geom = p_src_model_comp->GetModel()->GetGeom(geomName);
					Dbg_MsgAssert(p_orig_geom, ("Couldn't find CGeom %s", Script::FindChecksumName(geomName)));

					Nx::CGeom *p_new_geom = p_orig_geom->Clone(true, p_model);
					Dbg_MsgAssert(p_new_geom, ("Couldn't clone CGeom %s", Script::FindChecksumName(geomName)));

					p_new_geom->SetActive(true);
					p_model->AddGeom(p_new_geom, geomName);
				}
			}
		}
		else
		{
			Dbg_MsgAssert( 0, ( "Must name geoms for object id %d to clone", clone_model_name ) );
		}

		update_visibility();
		skip_empty_model = true;
	}	
	else if (p_props->GetString( CRCD(0x286a8d26,"model"), &p_model_name ))
	{
		UnloadModels();
		
		Mth::Vector off;
		off.Set();
		AddModel(p_model_name,skeletonChecksum,off,texDictOffset);
	}	
	else if (p_props->GetArray(CRCD(0x1b29cff6,"models"),&p_model_array))
	{
		UnloadModels();
		for (uint32 i=0; iGetSize(); ++i)
		{
			Script::CStruct *p_struct=p_model_array->GetStructure(i);
			p_struct->GetString(NONAME,&p_model_name);
			Mth::Vector off;
			off.Set();
			p_struct->GetVector(NONAME,&off);
			AddModel(p_model_name,skeletonChecksum,off,texDictOffset);
		}
	}	 
	if (p_props->GetChecksum( CRCD(0xb45c2617,"sector"), §or_name ))
	{
		UnloadModels();
		
		Mth::Vector off;
		off.Set();
		AddModelFromSector(sector_name, off);
	}	
	else if (p_props->GetArray(CRCD(0x4a6bf967,"sectors"), &p_sector_array))
	{
		UnloadModels();
		for (uint32 i=0; i < p_sector_array->GetSize(); ++i)
		{
			Script::CStruct *p_struct = p_sector_array->GetStructure(i);
			p_struct->GetChecksum(NONAME, §or_name);
			Mth::Vector off;
			off.Set();
			p_struct->GetVector(NONAME, &off);
			AddModelFromSector(sector_name, off);
		}
	}	 
	else if ( (skeletonChecksum != 0) && !skip_empty_model )
	{
		// no model was specified, but we might need to create
		// a skeleton anyway (like for the preview models)
		Mth::Vector off;
		off.Set();
		AddModel(NULL, skeletonChecksum, off, 0);
	}
	
	p_props->GetFloat(CRCD(0xed7c6031,"CameraZ"),&m_camera_z);
	p_props->GetFloat(CRCD(0xaffd09d,"AngleX"),&m_angle_x);
	p_props->GetFloat(CRCD(0x7df8e00b,"AngleY"),&m_angle_y);
	p_props->GetFloat(CRCD(0xe4f1b1b1,"AngleZ"),&m_angle_z);
	p_props->GetFloat(CRCD(0xc1608989,"AngVelX"),&m_angvel_x);
	p_props->GetFloat(CRCD(0xb667b91f,"AngVelY"),&m_angvel_y);
	p_props->GetFloat(CRCD(0x2f6ee8a5,"AngVelZ"),&m_angvel_z);

	if (p_props->ContainsFlag(CRCD(0x233f6b18,"AngleOrderXYZ"))) m_angle_order = ANGLE_ORDER_XYZ;
	if (p_props->ContainsFlag(CRCD(0x911b6961,"AngleOrderXZY"))) m_angle_order = ANGLE_ORDER_XZY;
	if (p_props->ContainsFlag(CRCD(0x3be6306e,"AngleOrderYXZ"))) m_angle_order = ANGLE_ORDER_YXZ;
	if (p_props->ContainsFlag(CRCD(0xe7de33c0,"AngleOrderYZX"))) m_angle_order = ANGLE_ORDER_YZX;
	if (p_props->ContainsFlag(CRCD(0xa0a9df8d,"AngleOrderZXY"))) m_angle_order = ANGLE_ORDER_ZXY;
	if (p_props->ContainsFlag(CRCD(0xceb5de5a,"AngleOrderZYX"))) m_angle_order = ANGLE_ORDER_ZYX;

	if (p_props->ContainsFlag(CRCD(0xf01fdca4,"DisablePointing")))
	{
		m_point_type=POINT_TYPE_NONE;
	}
		
	uint32 node_checksum=0;
	if (p_props->GetChecksum(CRCD(0xb2a86eb5,"NodeToPointTo"),&node_checksum))
	{
		int node_to_point_to=SkateScript::FindNamedNode(node_checksum);
		Script::CStruct *p_node=SkateScript::GetNode(node_to_point_to);
		Dbg_MsgAssert(p_node,("NULL p_node"));
		SkateScript::GetPosition(p_node,&m_node_position_to_point_to);

		m_point_type=POINT_TYPE_NODE;
	}	

	m_id_of_object_to_point_to=0;
	if (p_props->GetChecksum(CRCD(0xdad39b88,"ObjectToPointTo"),&m_id_of_object_to_point_to))
	{
		m_point_type=POINT_TYPE_OBJECT;
	}	
	
	m_tilt=0.0f;
	p_props->GetFloat(CRCD(0xe3c07609,"Tilt"),&m_tilt);
	
	m_hover_amp=0.0f;
	m_hover_period=0;
	Script::CStruct *p_hover_params=NULL;
	if (p_props->GetStructure(CRCD(0xca1c41,"HoverParams"),&p_hover_params))
	{
		p_hover_params->GetFloat(CRCD(0xc9fde32c,"Amp"),&m_hover_amp);
		float f=1.0f;
		p_hover_params->GetFloat(CRCD(0xa80bea4a,"Freq"),&f);
		m_hover_period=(int)(1000.0f/f);
	}
	
	m_skater_number=0;
	m_parent_object_name=0;
	m_parent_node_name=0;
	m_parent_offset.Set();
	Script::CStruct *p_parent_params=NULL;
	if (p_props->GetStructure(CRCD(0x36388933,"ParentParams"),&p_parent_params))
	{
		p_parent_params->GetChecksum(CRCD(0xa1dc81f9,"Name"),&m_parent_object_name);
		p_parent_params->GetVector(NONAME,&m_parent_offset);
		if (p_parent_params->GetInteger(CRCD(0x5b8ab877,"Skater"),&m_skater_number))
		{
			m_parent_object_name=0x5b8ab877/*Skater*/;
		}	
		// Sometimes they want an arrow to be hovering near an actual node,
		// rather than the instance of some CObject associated with that node.
		// Eg, they may want an arrow hovering over some level geometry.
		p_parent_params->GetChecksum(CRCD(0x7a8017ba,"Node"),&m_parent_node_name);
		
		Dbg_MsgAssert(!(m_parent_node_name && m_parent_object_name),("Cannot specify both a parent object and a parent node for an element3d"));
	}

	if (p_props->ContainsFlag(CRCD(0x1b399065,"scale_to_screen_dims")))
	{
		AutoComputeScale();
	}
	else if (p_props->GetVector("PivotPoint", &m_pivot_point))
	{
		m_pivot_point[W] = 1.0f;		// force to be a point
	}

	m_scale_with_distance=p_props->ContainsFlag(CRCD(0x4564c8f2,"ScaleWithDistance"));
	m_scale_multiplier=0.002f;
	p_props->GetFloat(CRCD(0x571f3f3a,"ScaleMultiplier"),&m_scale_multiplier);
	m_max_scale=3.0f;
	p_props->GetFloat(CRCD(0x31e78f98,"MaxScale"),&m_max_scale);


	int viewport;
	if (p_props->GetInteger(CRCD(0x655ee08e,"Active_Viewport"), &viewport))
	{
		m_active_viewport_number = viewport;
		update_visibility();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CElement3d::update()
{
	if (m_waiting_to_die)
	{
		// Return straight away to prevent repeated spawnings of the KillElement3d script
		return;
	}
		
	if (m_num_models)
	{							
		if (m_parent_object_name)
		{
			// Get a pointer to the parent object.
			Obj::CMovingObject *p_pos_obj=NULL;

// Skate module 
//			if (m_parent_object_name==0x5b8ab877/*Skater*/)
//			{
//				// This is 
//				Mdl::Skate * skate_mod = Mdl::Skate::Instance();
//				p_pos_obj=skate_mod->GetSkater(m_skater_number);
//			}
//			else			
			{
//				p_pos_obj=Obj::CMovingObject::m_hash_table.GetItem(m_parent_object_name);
				p_pos_obj = (Obj::CMovingObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID(m_parent_object_name);
			}	
			
			if (p_pos_obj)
			{
				m_model_matrix.Ident();
				m_model_matrix.SetPos(p_pos_obj->m_pos+m_parent_offset*m_model_matrix);
			}
			else
			{
				// The parent object has disappeared!
				// So call the KillElement3d script to safely delete this arrow.
				
				// Tried various ways of killing directly from c-code, but they didn't work.
				// Instant deletion here will mess up the update loop, and calling
				// Die() to flag the element for deletion later also cause problems, because
				// the parent element needs to be unlocked at the time of deletion.
				
				// Running a script seems to be safe ...
				// The KillElement3d script will wait a gameframe, then call the usual Die
				Script::CScript *p_script=Script::SpawnScript(0xa66c1c31/*KillElement3d*/);
				#ifdef __NOPT_ASSERT__
				p_script->SetCommentString("Created by CElement3d::update()");
				#endif
				p_script->mpObject=this;
				m_waiting_to_die=true;
			}	
		}
		else if (m_parent_node_name)
		{
			// FindNamedNode will assert if it does not find the node.
			int node_index=SkateScript::FindNamedNode(m_parent_node_name);
			Script::CStruct *p_node=SkateScript::GetNode(node_index);
			Dbg_MsgAssert(p_node,("NULL p_node"));
			
			Mth::Vector pos;
			SkateScript::GetPosition(p_node,&pos);
			
			m_model_matrix.Ident();
			m_model_matrix.SetPos( pos + m_parent_offset * m_model_matrix );
			// m_model_matrix.SetPos(pos);
		}
		else
		{
			Mth::Vector pos_change(m_offset_of_origin_from_center[X] * m_scale3d,
								   m_offset_of_origin_from_center[Y] * m_scale3d,
								   m_offset_of_origin_from_center[Z] * m_scale3d);
			/*
			Mth::Vector pos_change(m_offset_of_origin_from_center[X] * m_summed_props.scalex * m_scale3d,
								   m_offset_of_origin_from_center[Y] * m_summed_props.scaley * m_scale3d,
								   m_offset_of_origin_from_center[Z] * m_summed_props.scalex * m_scale3d);
			*/
			sGetWorldMatrixFromScreenPosition(m_active_viewport_number, &m_model_matrix, 
											  m_summed_props.GetScreenUpperLeftX() + GetAbsW() / 2.0f, 
											  m_summed_props.GetScreenUpperLeftY() + GetAbsH() / 2.0f, 
											  m_camera_z, 
											  pos_change);
	
			// If the model were rendered using m_model_matrix as it is then it will be unscaled
			// and facing the camera.
			
			// If the object is required to point to somewhere in the world then change the rotation
			// the be the required one.
			Mth::Vector position_to_point_to;
			switch (m_point_type)
			{
			case POINT_TYPE_NODE:
				position_to_point_to=m_node_position_to_point_to;
				break;
			case POINT_TYPE_OBJECT:
			{
				// Get a pointer to the object.
				// Doing a naughty cast up to a CMovingObject, because it probably will be one, and
				// won't cause a crash if it isn't, it'll just get a bad position.
				Obj::CMovingObject *p_pos_obj=(Obj::CMovingObject*)Obj::ResolveToObject(m_id_of_object_to_point_to);
				if (p_pos_obj)
				{
					position_to_point_to=p_pos_obj->m_pos;
				}
				break;
			}	
			default:
				break;
			}
			
			if (m_point_type!=POINT_TYPE_NONE)
			{
				// Save the position
				Mth::Vector model_pos=m_model_matrix.GetPos();
				// Clear out the rotation calculated by sGetWorldMatrixFromScreenPosition
				m_model_matrix.Ident();
				
				Mth::Vector dir=position_to_point_to-model_pos;
				
				dir.Normalize();
				m_model_matrix[Z]=dir;
				
				m_model_matrix[X].Set(dir.GetZ(),0.0f,-dir.GetX());
				m_model_matrix[X].Normalize();
				
				m_model_matrix.OrthoNormalizeAbout(Z);
				
				// Tilt so that the arrow is not end on to the camera.
				// However, make the tilt drop linearly down to zero as the
				// arrow goes towards being vertical so that the arrow never
				// ends up pointing backwards.
				float r=m_tilt-(-m_model_matrix[Z].GetY()*m_tilt);
				m_model_matrix.RotateXLocal(Mth::DegToRad(r));

				// Put the position back in.
				m_model_matrix.Translate(model_pos);
			}
		}
				
		// Now create a rotation matrix containing any extra rotation that we want.
		Mth::Matrix rot_matrix;
		rot_matrix.Ident();

		rot_matrix.Translate(-m_pivot_point);

		// Update the angles using the angular velocities.
		m_angle_x+=m_angvel_x;
		m_angle_y+=m_angvel_y;
		m_angle_z+=m_angvel_z;
		
		// Reduce modulo 2pi so that the calculations don't go weird after a few days
		// due to the angles getting huge.
		m_angle_x=m_angle_x-((int)(m_angle_x/(2.0f*3.141592654f)))*(2.0f*3.141592654f);
		m_angle_y=m_angle_y-((int)(m_angle_y/(2.0f*3.141592654f)))*(2.0f*3.141592654f);
		m_angle_z=m_angle_z-((int)(m_angle_z/(2.0f*3.141592654f)))*(2.0f*3.141592654f);
		
		//rot_matrix.RotateY(m_angle_y);
		//rot_matrix.RotateX(m_angle_x);
		//rot_matrix.RotateZ(m_angle_z);
		s_apply_rotations(rot_matrix, m_angle_x, m_angle_y, m_angle_z, m_angle_order);

		rot_matrix.Translate(m_pivot_point);

		// Apply scaling.
		//Ryan("oogbat %.4f, %.4f, %.4f\n", m_summed_props.scalex, m_scale3d, m_summed_props.scalex * m_scale3d);
		Mth::Vector scale(m_summed_props.GetScaleX() * m_scale3d,
						  m_summed_props.GetScaleY() * m_scale3d,
						  m_summed_props.GetScaleX() * m_scale3d,
						  1.0f);
						  
		// The ped arrows (or stars) need to scale according to the distance from the camera so that
		// they are visible from a distance. (TT2006)
		if (m_scale_with_distance)
		{
			Gfx::Camera *p_camera = Nx::CViewportManager::sGetActiveCamera(m_active_viewport_number);
			if (p_camera)
			{
				Mth::Vector cam_offset=m_model_matrix.GetPos()-p_camera->GetPos();
				float multiplier=cam_offset.Length()*m_scale_multiplier;
				if (multiplier < 1.0f)
				{
					multiplier=1.0f;
				}
				if (multiplier > m_max_scale)
				{
					multiplier = m_max_scale;
				}
					
				scale[X]*=multiplier;
				scale[Y]*=multiplier;
				scale[Z]*=multiplier;
			}	
		}
		
		rot_matrix.Scale(scale);

		// Apply the rotation and scaling to the m_model_matrix	
		m_model_matrix=rot_matrix*m_model_matrix;


		// Calculate any hover offset that may need to be applied.		
		Mth::Vector hover_offset;
		if (m_hover_amp>0.0f)
		{
			int t=Tmr::ElapsedTime(0)%m_hover_period;
			float h=m_hover_amp*sinf(t*2*3.141592653f/m_hover_period);
			// Use m_model_matrix[Y] so that it hovers up and down in the model's
			// coordinate system.
			hover_offset=h*m_model_matrix[Y];
		}
		
		// Render the models
		bool show_models = (m_summed_props.alpha >= .0001f);
		
		// If running a replay, hide arrows that are pointing to something,
		// otherwise they go all spazzy. (TT8083 and TT8819)
		if ((m_point_type!=POINT_TYPE_NONE || m_parent_object_name) && Replay::RunningReplay())
		{
			show_models=false;
		}	
		

		for (int i=0; i0.0f)
			{
				p+=hover_offset;
			}
				
			display_matrix.SetPos(p);
			
			mp_models[i].mpModel->SetActive(show_models);
			mp_models[i].mpModel->Render(&display_matrix,false,mp_models[i].mpSkeleton);
		}
	}
}

void CElement3d::SetMorph(Script::CStruct *pProps)
{
	CScreenElement::SetMorph(pProps);
	if (pProps->ContainsFlag("scale_to_screen_dims"))
		AutoComputeScale();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CElement3d::update_visibility()
{
	// Make visible for only this viewport
	uint32 vis_mask = 1 << m_active_viewport_number;
	for (int i=0; iSetVisibility(vis_mask);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CElement3d::s_apply_rotations(Mth::Matrix & mat, float x_rot, float y_rot, float z_rot, EAngleOrder rot_order)
{
	switch (rot_order)
	{
	case ANGLE_ORDER_XYZ:
		mat.RotateX(x_rot);
		mat.RotateY(y_rot);
		mat.RotateZ(z_rot);
		break;

	case ANGLE_ORDER_XZY:
		mat.RotateX(x_rot);
		mat.RotateZ(z_rot);
		mat.RotateY(y_rot);
		break;

	case ANGLE_ORDER_YXZ:
		mat.RotateY(y_rot);
		mat.RotateX(x_rot);
		mat.RotateZ(z_rot);
		break;

	case ANGLE_ORDER_YZX:
		mat.RotateY(y_rot);
		mat.RotateZ(z_rot);
		mat.RotateX(x_rot);
		break;

	case ANGLE_ORDER_ZXY:
		mat.RotateZ(z_rot);
		mat.RotateX(x_rot);
		mat.RotateY(y_rot);
		break;

	case ANGLE_ORDER_ZYX:
		mat.RotateZ(z_rot);
		mat.RotateY(y_rot);
		mat.RotateX(x_rot);
		break;
	}
}

}


================================================
FILE: Code/Gfx/2D/Element3d.h
================================================
#ifndef __GFX_2D_ELEMENT3D_H__
#define __GFX_2D_ELEMENT3D_H__

#ifndef __GEL_OBJECT_H
#include 
#endif
#include 

namespace Gfx
{
    class CSkeleton;
};
                     
namespace Nx
{
    class CModel;
};

namespace Script
{
	class CScript;
	class CStruct;
};

namespace Front
{

#define MAX_MODELS_PER_ELEMENT 25

struct SSubModel
{
	Nx::CModel	*mpModel;
    Gfx::CSkeleton* mpSkeleton;
	Mth::Vector mOffset;
};
	
class CElement3d : public CScreenElement
{
	friend class CScreenElementManager;

	SSubModel mp_models[MAX_MODELS_PER_ELEMENT];
	int		m_num_models;
	
	float	m_camera_z;
	float	m_angle_x;
	float	m_angle_y;
	float	m_angle_z;
	float	m_angvel_x;
	float	m_angvel_y;
	float	m_angvel_z;

	Mth::Matrix m_model_matrix;
	
	enum EPointType
	{
		POINT_TYPE_NONE,
		POINT_TYPE_NODE,
		POINT_TYPE_OBJECT,
	};
	Mth::Vector m_node_position_to_point_to;
	EPointType m_point_type;
	
	enum EAngleOrder
	{
		ANGLE_ORDER_XYZ,
		ANGLE_ORDER_XZY,
		ANGLE_ORDER_YXZ,
		ANGLE_ORDER_YZX,
		ANGLE_ORDER_ZXY,
		ANGLE_ORDER_ZYX,
	};
	EAngleOrder m_angle_order;

	uint32 m_id_of_object_to_point_to;

	float m_hover_amp;
	int m_hover_period;

	Mth::Vector m_pivot_point;				// Point in local space for rotations

	// These following members are used when the element3d is associated with some 3d position.
	// The 3d poistion can be got from an instance of a CObject, or from a fixed node.

	// Offset from the parent position.
	Mth::Vector m_parent_offset;
		
	Mth::Vector m_offset_of_origin_from_center; // origin - center

	// different from 2D scale, is scale of 3D object
	float		m_scale3d;

	// These three are used for when the object needs to scale according to the distance from the camera.
	bool 	m_scale_with_distance;
	float 	m_scale_multiplier;
	float 	m_max_scale;

	int			m_active_viewport_number; // viewport to draw to
	
	uint32 m_parent_object_name;	// Non-zero if associated with an instance of a CObject
	int m_skater_number; 			// Valid if m_parent_object_name is skater
	uint32 m_parent_node_name;		// Non-zero if associated with a node.
	
	// Tilt value. Used for the arrow so that it does not go totally flat, which makes the
	// direction it is pointing in hard to see. Units are degrees.
	float m_tilt;
	
	// These is used to flag the element to be killed if its parent object dies.	
	bool m_waiting_to_die;
	
public:
				CElement3d();
	virtual		~CElement3d();

	void		SetProperties(Script::CStruct *p_props);
	void		SetMorph(Script::CStruct *pProps);

	void		UnloadModels();
	void		AddModel(const char *p_model_name, uint32 skeleton_name, Mth::Vector offset, int texDictOffset);
	void 		AddModelFromSector(uint32 sectorName, Mth::Vector offset);
    Nx::CModel* CreateModel( uint32 skeleton_name, Mth::Vector offset );
	void 		AutoComputeScale();
	
protected:

	void		update();
	void		update_visibility();
    Gfx::CSkeleton* CreateSkeleton( Nx::CModel* pModel, uint32 skeletonChecksum );

	static void	s_apply_rotations(Mth::Matrix & mat, float x_rot, float y_rot, float z_rot, EAngleOrder rot_order);
};


}

#endif



================================================
FILE: Code/Gfx/2D/Menu2.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Front
{




CBaseMenu::CBaseMenu()
{
    m_selected_index = -1;
    m_selected_id = 0;
	m_in_focus = false;
	m_focus_controller = 0;

	m_regular_space_val = 0.0f;
	m_padding_scale = 1.0f;
	m_spacing_between = 0.0f;

	m_internal_just_x = 0.0f;
	m_internal_just_y = 0.0f;
	
	m_pad_handling_enabled = true;
	m_allow_wrap = true;

	m_current_grid_index = -1;
}




CBaseMenu::~CBaseMenu()
{
}




void CBaseMenu::SetProperties(Script::CStruct *pProps)
{
	float just[2] = { 0.0f, 0.0f };
	if (resolve_just(pProps, CRCD(0x67e093e4,"internal_just"), just, just+1))
		SetInternalJust(just[0], just[1]);
	
	int regular_space_val = 0;
	if (pProps->GetInteger(CRCD(0xf24adedb,"regular_space_amount"), ®ular_space_val))
		m_regular_space_val = (float) regular_space_val;
	
	pProps->GetFloat(CRCD(0x6d853b88,"padding_scale"), &m_padding_scale);
	
	int spacing_between = 0;
	if (pProps->GetInteger(CRCD(0x4b4e14ab,"spacing_between"), &spacing_between))
		m_spacing_between = (float) spacing_between;
		
	if (pProps->ContainsFlag(CRCD(0x4bd08bfe,"enable_pad_handling")))
		m_pad_handling_enabled = true;
	else if (pProps->ContainsFlag(CRCD(0xc22541cc,"disable_pad_handling")))
		m_pad_handling_enabled = false;

	if (pProps->ContainsFlag(CRCD(0x43a7692d,"allow_wrap")))
		m_allow_wrap = true;
	else if (pProps->ContainsFlag(CRCD(0x5dea5c64,"dont_allow_wrap")))
		m_allow_wrap = false;
	
	CScreenElement::SetProperties(pProps);
		
	return;
}




bool CBaseMenu::PassTargetedEvent(Obj::CEvent *pEvent, bool broadcast)
{	
	
	#ifdef	__NOPT_ASSERT__												 
	Obj::CSmtPtr p = this;
	uint32	debug_id = m_id;
	#endif		
	if (!Obj::CObject::PassTargetedEvent(pEvent))
	{
		return false;
	}	
	Dbg_MsgAssert(p, ("BaseMenu (%s) deleted itself in call to PassTargetedEvent", Script::FindChecksumName(debug_id)));
		
	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
    
	//printf("VMenu receives event of type %s\n", Script::FindChecksumName(pEvent->GetType()));
	
    if (pEvent->GetType() == Obj::CEvent::TYPE_NOTIFY_CHILD_LOCK)
    {
        // The currently selected child might have been removed. Make sure we update
        // references and send new focus event if necessary
        bool need_new_focus_event = false;
        
        if (m_selected_id) 
        {
            int current_child_index;
            CScreenElementPtr p_current_child = GetChildById(m_selected_id, ¤t_child_index);
            
            // see if selected child has disappeared
            if (!p_current_child) 
            {
                // child disappeared, see if another one at same index
                CScreenElementPtr p_new_child = GetChildByIndex(m_selected_index);
                if (p_new_child) 
                {
                    m_selected_id = p_new_child->GetID();
					setup_tags();
					need_new_focus_event = true;
                }
                else
                {
                    // couldn't locate replacement
                    m_selected_index = -1;
					// will set m_selected_id to 0 if no focusable item can be found
					find_focusable_item(1, true, true);
					need_new_focus_event = true;
                }
            } // end if
            else
            {
                // selected child is still there, update indices
                m_selected_index = current_child_index;
				setup_tags();
            }
        } // end if (m_selected_id)
		else
		{
            // nothing selected, so find something
			find_focusable_item(1, true, true);
			need_new_focus_event = true;
		}
        
        if (need_new_focus_event && m_in_focus) 
        {
            find_focusable_item(1, true, true);
            if (m_selected_id)
			{
				Script::CStruct data;
				data.AddInteger(CRCD(0xb30d9965,"controller"), m_focus_controller);
				p_tracker->LaunchEvent(Obj::CEvent::TYPE_FOCUS, m_selected_id, Obj::CEvent::vSYSTEM_EVENT, &data);
			}
        }

		reposition();

		pEvent->MarkHandled(m_id);
    }
	if (pEvent->GetType() == Obj::CEvent::TYPE_FOCUS && !m_in_focus)
	{
		m_focus_controller = Obj::CEvent::sExtractControllerIndex(pEvent);
		
		//if (!m_is_vertical_menu)
		//	printf("Menu %s receives focus event, sending to 0x%x\n", Script::FindChecksumName(m_id), m_selected_id);

		// see if event has a child-to-select component
		int desired_grid_index = -1;
		if (pEvent->GetData() && pEvent->GetData()->GetInteger(CRCD(0xfacf9a8b,"grid_index"), &desired_grid_index))
		{
			// find the child that matches that grid index, if any
			int match_index = -1;

			m_current_grid_index = desired_grid_index;
			
			CScreenElementPtr p_child = mp_child_list;
			int current_index = 0;
			while(p_child)
			{
				int found_grid_index = -1;
				if (p_child->GetIntegerTag(CRCD(0x5b92f8dd, "tag_grid_x"), &found_grid_index))
				{
					if (found_grid_index <= desired_grid_index)
						match_index = current_index;
				}
				p_child = p_child->GetNextSibling();
				current_index++;
			} 
			
			if (match_index != -1)
			{
				m_selected_index = match_index;
				m_selected_id = 1;
			}
		}

		// see if the event has a child_id component 
		uint32 desired_child_id;
		if ( pEvent->GetData() && pEvent->GetData()->GetChecksum( CRCD(0x229d3de4,"child_id"), &desired_child_id ) )
		{
			// printf("got a desired child_id of %x\n", desired_child_id);			
			// CScreenElementPtr p_child = GetChildById( desired_child_id );
			CScreenElementPtr p_child = mp_child_list;

			int current_index = 0;
			while ( p_child )
			{
				if ( desired_child_id == p_child->GetID() )
				{
					m_selected_id = desired_child_id;
					m_selected_index = current_index;
					break;
				}
				current_index++;
				p_child = p_child->GetNextSibling();
			}
		}

		m_in_focus = true;
		if (m_selected_id) 
		{
			find_focusable_item(1, true, false);
            if (m_selected_id)
				p_tracker->LaunchEvent(Obj::CEvent::TYPE_FOCUS, m_selected_id, Obj::CEvent::vSYSTEM_EVENT, pEvent->GetData());
		}
		pEvent->MarkHandled(m_id);
	}
	if (pEvent->GetType() == Obj::CEvent::TYPE_UNFOCUS && m_in_focus)
	{
		//printf("VMenu %s receives unfocus event\n", Script::FindChecksumName(m_id));
		m_in_focus = false;
		if (m_selected_id) 
			p_tracker->LaunchEvent(Obj::CEvent::TYPE_UNFOCUS, m_selected_id);
		pEvent->MarkHandled(m_id);
	}
	if (((pEvent->GetType() == Obj::CEvent::TYPE_PAD_UP && m_is_vertical_menu) ||
		 (pEvent->GetType() == Obj::CEvent::TYPE_PAD_LEFT && !m_is_vertical_menu)) && 
		m_in_focus && m_pad_handling_enabled) 
	{
		change_selection(-1);
		pEvent->MarkHandled(m_id);
	}
	if (((pEvent->GetType() == Obj::CEvent::TYPE_PAD_DOWN && m_is_vertical_menu) ||
		 (pEvent->GetType() == Obj::CEvent::TYPE_PAD_RIGHT && !m_is_vertical_menu)) && 
		m_in_focus && m_pad_handling_enabled) 
	{
		change_selection(1);
		pEvent->MarkHandled(m_id);
	}
	return true;
}




bool CBaseMenu::UsingRegularSpacing(float &rRegularSpaceAmount)
{
	rRegularSpaceAmount = m_regular_space_val;
	return (m_regular_space_val > 0.0001f);
}




void CBaseMenu::find_focusable_item(int dir, bool include_current, bool updateGridIndex)
{
	int count = CountChildren() * 2;

	if (m_selected_index < 0)
		m_selected_index = 0;
	
	while(count)
	{
		if (!include_current)
		{
			m_selected_index += dir;
			if (m_selected_index >= CountChildren())
			{
				if (m_allow_wrap)
					m_selected_index = 0;
				else
				{
					// go back to previous item, and reverse direction
					dir = -dir;
					m_selected_index = CountChildren() - 1;
				}
			}
			else if (m_selected_index < 0)
			{
				if (m_allow_wrap)
					m_selected_index = CountChildren() - 1;
				else
				{
					// go back to previous item, and reverse direction
					dir = -dir;
					m_selected_index = 0;
				}
			}
		}
		include_current = false;

		CScreenElementPtr p_new_child = GetChildByIndex(m_selected_index);
		Dbg_Assert(p_new_child);
		m_selected_id = p_new_child->GetID(); 
		
		if (updateGridIndex)
		{
			// update grid index that we have stored for selected child
			m_current_grid_index = -1;
			p_new_child->GetIntegerTag(CRCD(0x5b92f8dd, "tag_grid_x"), &m_current_grid_index);
		}

		if ( !p_new_child->ContainsFlagTag(CRCD(0xf33a3321, "tag_not_focusable")) && 
			 !p_new_child->EventsBlocked() )
			break;
		
		count--;
	}
	
	if (count)
	{
		setup_tags();
	}
	else
	{
		// nothing selected
		m_selected_index = -1;
		m_selected_id = 0;
	}
}




void CBaseMenu::change_selection(int dir)
{
	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
	
	// if the child is also	a menu, it will have a selected index
	int grid_index_within_child	= -1;
	
	if (m_selected_id) 
	{
		CScreenElementManager* p_manager = CScreenElementManager::Instance();
		
		// figure out which of selected child's children is selected
		CScreenElement *p_child = p_manager->GetElement(m_selected_id);
		// This assert is causing an occasional crash. I can't figure out the problem, so 
		// here is the next best solution.
		//Dbg_MsgAssert(p_child, ("Couldn't find selected child %s", Script::FindChecksumName(m_selected_id)));
		if (p_child)
			p_child->GetIntegerTag(CRCD(0x8321dd71, "tag_selected_childs_grid_index"), &grid_index_within_child);

		p_tracker->LaunchEvent(Obj::CEvent::TYPE_UNFOCUS, m_selected_id);
	}

	find_focusable_item(dir, false, true);

	if (m_selected_id) 
	{
		Script::CStruct data;
		data.AddInteger("controller", m_focus_controller);

		if (grid_index_within_child != -1)
		{
			data.AddInteger("grid_index", grid_index_within_child);
			//printf("child index is %d\n", selected_index_of_child);
		}

		p_tracker->LaunchEvent(Obj::CEvent::TYPE_FOCUS, m_selected_id, Obj::CEvent::vSYSTEM_EVENT, &data);
	}
}




void CBaseMenu::setup_tags()
{
	SetChecksumTag(CRCD(0xac988ebe, "tag_selected_id"), m_selected_id);
	SetIntegerTag(CRCD(0xf2239871, "tag_selected_index"), m_selected_index);
	//printf("setup_tags(), %s\n", Script::FindChecksumName(m_id));

	SetIntegerTag(CRCD(0x8321dd71, "tag_selected_childs_grid_index"), m_current_grid_index);
}




void CBaseMenu::reposition() 
{
	float z = 0.0f; // functions as a virtual x or y
	float biggest = 0.0f; // functions as virtual 'widest' or 'highest'

	bool use_regular_space = (m_regular_space_val > 0.0001f);
	
	if (use_regular_space)
	{
		// do first pass to find out biggest element, total height of all elements
		CScreenElementPtr p_child = mp_child_list;
		while(p_child)
		{
			// virtual dimensions, 'k' in the direction that menu items are added (i.e. of 'z')
			float scaled_j;
					
			if (m_is_vertical_menu)
				scaled_j = p_child->GetBaseW() * p_child->GetScaleX();
			else
				scaled_j = p_child->GetBaseH() * p_child->GetScaleY();
			
			z += m_regular_space_val;
			
			if (scaled_j > biggest)
				biggest = scaled_j;
			
			p_child = p_child->GetNextSibling();
		}
	}
	else
	{
		// do first pass to find out biggest element, total height of all elements
		CScreenElementPtr p_child = mp_child_list;
		while(p_child)
		{
			// virtual dimensions, 'k' in the direction that menu items are added (i.e. of 'z')
			float scaled_j, scaled_k;
					
			if (m_is_vertical_menu)
			{
				scaled_j = p_child->GetBaseW() * m_padding_scale;
				scaled_k = p_child->GetBaseH() * m_padding_scale;
			}
			else
			{
				scaled_j = p_child->GetBaseH() * m_padding_scale;
				scaled_k = p_child->GetBaseW() * m_padding_scale;
			}
			
			z += scaled_k + m_spacing_between;
			
			if (scaled_j > biggest)
				biggest = scaled_j;
			
			p_child = p_child->GetNextSibling();
		} 
		z -= m_spacing_between;
	}
		
	if ((m_object_flags & vFORCED_DIMS)) 
	{
		// "grow" the menu in case of overflow (disobeying rule of above flag)
		if (m_is_vertical_menu && z > m_base_h) 
			m_base_h = z;
		else if (!m_is_vertical_menu && z > m_base_w) 
			m_base_w = z;
	}
	else
	{
		// this element is in control of own dimensions
		if (m_is_vertical_menu)
		{
			m_base_w = biggest;
			m_base_h = z;
		}
		else
		{
			m_base_w = z;
			m_base_h = biggest;
		}
	}
	compute_ul_pos(m_target_local_props);
	m_object_flags |= vNEEDS_LOCAL_POS_CALC;
	
		
	// use vertical	justification to figure out starting position
	if (m_is_vertical_menu)
		z = (m_internal_just_y + 1.0f) * (m_base_h - z) / 2.0f;
	else
		z = (m_internal_just_x + 1.0f) * (m_base_w - z) / 2.0f;
			
	if (use_regular_space)
	{
		z += m_regular_space_val / 2.0f;
		
		// now, set positions of everything
		CScreenElementPtr p_child = mp_child_list;
		while(p_child)
		{
			if (m_is_vertical_menu)
			{
				p_child->SetJust(m_internal_just_x, 0.0f);
				p_child->SetPos((m_internal_just_x + 1.0f) * m_base_w / 2.0f, z, FORCE_INSTANT);
			}
			else
			{
				p_child->SetJust(0.0f, m_internal_just_y);
				p_child->SetPos(z, (m_internal_just_y + 1.0f) * m_base_h / 2.0f, FORCE_INSTANT);
			}
			
			z += m_regular_space_val;
			p_child = p_child->GetNextSibling();
		}
	}
	else // if !use_regular_space
	{
		// now, set positions of everything
		CScreenElementPtr p_child = mp_child_list;
		while(p_child)
		{
			if (m_is_vertical_menu)
			{
				float inc_value = p_child->GetBaseH() * m_padding_scale / 2.0f;
				z += inc_value;
				
				p_child->SetJust(m_internal_just_x, 0.0f);
				p_child->SetPos((m_internal_just_x + 1.0f) * m_base_w / 2.0f, z, FORCE_INSTANT);
				
				z += inc_value;
			}
			else
			{
				float inc_value = p_child->GetBaseW() * m_padding_scale / 2.0f;
				z += inc_value;
				
				p_child->SetJust(0.0f, m_internal_just_y);
				p_child->SetPos(z, (m_internal_just_y + 1.0f) * m_base_h / 2.0f, FORCE_INSTANT);
				
				z += inc_value;
			}
			z += m_spacing_between;
			
			if (p_child->GetFlags() & vNEEDS_LOCAL_POS_CALC)
				m_object_flags |= vNEEDS_LOCAL_POS_CALC;
			
			p_child = p_child->GetNextSibling();
		}
	}
    
	if (!m_selected_id && mp_child_list && m_in_focus) 
    {
        m_selected_index = 0;
        find_focusable_item(1, true, true);

        if (m_selected_id)
		{
			Script::CStruct data;
			data.AddInteger("controller", m_focus_controller);
			Obj::CTracker* p_tracker = Obj::CTracker::Instance();
			p_tracker->LaunchEvent(Obj::CEvent::TYPE_FOCUS, m_selected_id, Obj::CEvent::vSYSTEM_EVENT, &data);
		}
    }
}




CVMenu::CVMenu()
{
	m_is_vertical_menu = true;
	SetType(CScreenElement::TYPE_VMENU);
}




CVMenu::~CVMenu()
{
}




CHMenu::CHMenu()
{
	m_is_vertical_menu = false;
	
	SetType(CScreenElement::TYPE_HMENU);
}




CHMenu::~CHMenu()
{
}




}


================================================
FILE: Code/Gfx/2D/Menu2.h
================================================
#ifndef __GFX_2D_MENU_H__
#define __GFX_2D_MENU_H__

#include 

namespace Front
{

/*
	A base menu is an.
*/
class CBaseMenu : public Front::CScreenElement
{
	friend class CVScrollingMenu;

public:

							CBaseMenu();
	virtual					~CBaseMenu();

	void					SetInternalJust(float h, float v) {m_internal_just_x = h; m_internal_just_y = v;}	
	
	virtual void			SetProperties(Script::CStruct *pProps);
	virtual bool			PassTargetedEvent(Obj::CEvent *pEvent, bool broadcast = false);

	int						GetSelectedIndex() { return m_selected_index; }

	bool					UsingRegularSpacing(float &rRegularSpaceAmount);

protected:

	void					find_focusable_item(int dir, bool include_current, bool updateGridIndex);
	void					change_selection(int dir);
	void					setup_tags();
	void					reposition();

	// both m_selected_index and m_selected_id will have matching, valid settings,
	// or both will be invalid (-1, 0)
	int                     m_selected_index;
    uint32                  m_selected_id;
	bool					m_in_focus;
	int						m_focus_controller; // 0 or 1

	float					m_internal_just_x, m_internal_just_y;
	bool 					m_is_vertical_menu;

	float					m_regular_space_val;
	float					m_padding_scale;
	float					m_spacing_between;

	bool					m_pad_handling_enabled;
	bool					m_allow_wrap;

	int						m_current_grid_index; // of selected item, set to -1 when not applicable
												  // not necessarily the same as the grid index stored
												  // in the child that's selected (for "remembering"
												  // the column)
};




class CVMenu : public CBaseMenu
{
public:

							CVMenu();
	virtual					~CVMenu();
};




class CHMenu : public CBaseMenu
{
public:

							CHMenu();
	virtual					~CHMenu();
};




}

#endif


================================================
FILE: Code/Gfx/2D/ScreenElemMan.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
                                          
#include 
#include 
#include 
#include 

#include 
#include 

// start autoduck documentation
// @DOC ScreenElemMan
// @module ScreenElemMan | None
// @subindex Scripting Database
// @index script | ScreenElemMan

namespace Front
{


	
DefineSingletonClass(CScreenElementManager, "Screen Element Manager");




CScreenElementManager::CScreenElementManager()
{
	mp_root_element = NULL;
	mp_resolve_temp = NULL;

	// register event listener
	RegisterWithTracker(NULL);

	for (int i = 0; i < NUM_FOCUS_LISTS; i++)
	{
		mp_focus_list[i] = NULL;
		m_focus_list_changed[i] = false;
	}
	for (int i = 0; i < NUM_FOCUS_NODES; i++)
	{
		m_focus_node_pool[i].mpElement = NULL;
		m_focus_node_pool[i].mpNextNode = NULL;
	}

	m_num_pad_event_types = 0;
	for (int i = 0; i < MAX_PAD_EVENT_TYPES; i++)
		m_pad_event_type_tab[i] = 0;
}




CScreenElementManager::~CScreenElementManager()
{
	Dbg_MsgAssert(!m_object_list.CountItems(), ("items still in CScreenElementManager"));
}




/*
	Set 'id' to Obj::CBaseManager::vNO_OBJECT_ID if object to receive automatic ID
*/
CScreenElementPtr CScreenElementManager::CreateElement(uint32 type, uint32 id, Script::CScriptStructure *pProps)
{
	CScreenElementPtr p_new_element = NULL;
	
	uint32 heap_crc;
	int heap_num = 0;
	bool switched = false;
	if (pProps->GetChecksum("heap", &heap_crc))
	{
		switch ( heap_crc )
		{
			case 0x477fc6de:		// topdown
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
				switched = true;
				break;
			case 0xc80bf12d:		// bottomup 
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
				switched = true;
				break;
			case 0xe37e78c5:		// script
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
				switched = true;
				break;
			case 0x9f7b7843:		// network
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());
				switched = true;
				break;
			case 0x03c84a59:		// profiler
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ProfilerHeap());
				switched = true;
				break;
			case 0x935ab858:		// debug
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
				switched = true;
				break;
			case 0x5b8ab877:		// skater
				pProps->GetInteger("heapnum", &heap_num);
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterHeap(heap_num));
				switched = true;
				break;
			case 0xeabd217b:		// skaterinfo
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());
				switched = true;
				break;
			case 0x39fb63cc:		// skatergeom
				pProps->GetInteger("heapnum", &heap_num);
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterGeomHeap(heap_num));
				switched = true;
				break;
			case 0xe3f81b18:		// internettopdown
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
				switched = true;
				break;
			case 0xbaa81175:		// internetbottomup
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
				switched = true;
				break;
			case 0x1ca1ff20:		// default (ie don't change context).
				break;
			default:	// Default = frontend
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
				switched = true;
				break;
		}
	}
	else
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		switched = true;
	}

	switch(type)
	{
		case CScreenElement::TYPE_CONTAINER_ELEMENT:
			p_new_element = new CContainerElement();
			break;
		case CScreenElement::TYPE_TEXT_ELEMENT:
			p_new_element = new CTextElement();
			break;
		case CScreenElement::TYPE_VMENU:
  			p_new_element = new CVMenu();
			break;
		case CScreenElement::TYPE_HMENU:
  			p_new_element = new CHMenu();
			break;
		case CScreenElement::TYPE_TEXT_BLOCK_ELEMENT:
  			p_new_element = new CTextBlockElement();
			break;
		case CScreenElement::TYPE_SPRITE_ELEMENT:
  			p_new_element = new CSpriteElement();
			break;
		case CScreenElement::TYPE_VSCROLLING_MENU:
  			p_new_element = new CVScrollingMenu();
			break;
		case CScreenElement::TYPE_HSCROLLING_MENU:
  			p_new_element = new CHScrollingMenu();
			break;
		case CScreenElement::TYPE_ELEMENT_3D:
			p_new_element = new CElement3d();
			break;
		case CScreenElement::TYPE_WINDOW_ELEMENT:
			p_new_element = new CWindowElement();
			break;
		default:
			Dbg_MsgAssert(0, ("unknown element type 0x%x", type));
			break;
	}

	p_new_element->SetID(id);
	RegisterObject(*p_new_element);
	
	p_new_element->SetProperties(pProps);
	p_new_element->SetMorph(pProps);
	
	if ( switched ) Mem::Manager::sHandle().PopContext();
	
	
	return p_new_element;
}




void CScreenElementManager::DestroyElement(uint32 id, ERecurse recurse, EPreserveParent preserveParent, Script::CScript *pCallingScript)
{

	
	CScreenElementPtr p_element = GetElement(id, CScreenElementManager::ASSERT);
	Dbg_Assert(p_element);
	if (recurse)
	{
		mark_element_out_of_focus(p_element);
		if (p_element->mp_parent)
			p_element->mp_parent->SetChildLockState(CScreenElement::UNLOCK);
		destroy_element_recursive(preserveParent, p_element, pCallingScript);
	}
	else
	{
		mark_element_out_of_focus(p_element);
		if (pCallingScript && p_element)
			// must disassociate script from element being destroyed
			pCallingScript->DisassociateWithObject(p_element);
		
		UnregisterObject(*p_element);
		#ifdef	__NOPT_ASSERT__	
		// Mick:  screen elements are deleted directly, so LockAssert is not applicable.
		p_element->SetLockAssertOff();
		#endif
		p_element.Kill();
	}
}




/*
	Passing in pParent = NULL will give the child no parent
*/
void CScreenElementManager::SetParent(const CScreenElementPtr &pParent, const CScreenElementPtr &pChild, CScreenElement::EPosRecalc recalculatePosition)
{
	Dbg_Assert(pChild);

	CScreenElementPtr p_current_parent = pChild->mp_parent;
	if (p_current_parent)	
		Dbg_MsgAssert(!(p_current_parent->m_object_flags & CScreenElement::vCHILD_LOCK), ("can't remove child -- locked"));
	
	// if child was root element, then seek out	a new root
	if (pParent && (mp_root_element == pChild || !mp_root_element))
	{
		mp_root_element = pParent;
		while(mp_root_element->mp_parent)
			mp_root_element = mp_root_element->mp_parent;
	}

	mark_element_out_of_focus(pChild);
	
	pChild->set_parent(pParent, recalculatePosition);
}




CScreenElementPtr CScreenElementManager::GetElement(uint32 id, EAssert assert)
{
	Dbg_MsgAssert(id > 0, ("can't use 0 as an ID"));
	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
	Obj::CObject *p_object = p_tracker->GetObject(id);
	if (assert)
		Dbg_MsgAssert(p_object, ("couldn't find screen element %s", Script::FindChecksumName(id)));
	if (p_object)
	{
		CScreenElementPtr p_element = static_cast(p_object);
		Dbg_MsgAssert(p_element, ("%s not a screen element", Script::FindChecksumName(id)));
		return p_element;
	}
	else
		return NULL;
}




CScreenElementPtr CScreenElementManager::GetElement(Script::CStruct *pStructContainingId, uint32 IdSubStructName, EAssert assert)
{
	uint32 id = ResolveComplexID(pStructContainingId, IdSubStructName);
	#ifdef __NOPT_ASSERT__
	if ( assert )
	{
		if ( !id )
			Script::PrintContents(pStructContainingId, 2);
		Dbg_MsgAssert(id, ("could not resolve ID %s", Script::FindChecksumName(IdSubStructName)));
	}
	#endif
	if ( id )
	{
		return GetElement(id, assert);
	}
	return NULL;
}


CScreenElementPtr CScreenElementManager::GetElement(Script::CStruct *pStructContainingId, char *pIdSubStructName, EAssert assert)
{
	return GetElement(pStructContainingId,Script::GenerateCRC(pIdSubStructName),ASSERT);
}



/*
	No screen element can be destroyed during this phase
*/
void CScreenElementManager::Update()
{
	if (mp_root_element)
	{
		set_tree_lock_state(CScreenElement::LOCK);
		
		//Ryan("in CScreenElementManager::Update(), timer is %d\n", Tmr::GetTime());
		mp_root_element->UpdateProperties();
		
		set_tree_lock_state(CScreenElement::UNLOCK);
	}
}




void CScreenElementManager::SetPausedState(bool pause)
{
	// find a new parentless element to be root element
	Lst::Node *p_node = m_object_list.FirstItem();
	while(p_node)
	{
		CScreenElementPtr p_element = static_cast(p_node->GetData());
		Dbg_Assert(p_element);
		p_element->SetMorphPausedState(pause);
		p_node = p_node->GetNext();
	}
}




// locks/unlocks all the screen elements in the tree so they can't/can be deleted without an assert
void CScreenElementManager::set_tree_lock_state(CScreenElement::ELockState state)
{
	if (mp_root_element)
	{
		CScreenElement* p_stack[32];
		p_stack[0] = mp_root_element;
		int depth = 1;
		
		while(depth)
		{
			// pop top value off stack
			CScreenElement* p_node = p_stack[--depth];

			#ifdef __NOPT_ASSERT__
			p_node->debug_verify_integrity();
			#endif
			
			// set its lock state
			if (state == CScreenElement::LOCK)
				p_node->AddReference();
			else
			{
				// this	element may have been ADDED since the call to set_tree_lock_state(LOCK),
				// so we can't depend on it being referenced
				if (p_node->IsReferenced())
					p_node->RemoveReference();
			}
			
			Dbg_Assert(depth <= 30);
			
			// put sibling and child on stack
			if (p_node->GetNextSibling())
				p_stack[depth++] = p_node->GetNextSibling();
			if (p_node->GetFirstChild())
				p_stack[depth++] = p_node->GetFirstChild();
		}
	}
}


// Optimization - as event handling become more widespread
// this should only be called on the type of events that 
// it actually handles, which is basically the pad_event_types

void CScreenElementManager::pass_event_to_listener(Obj::CEvent *pEvent)
{
	// Fill in the array of pad event types by copying it from the global "pad_event_types" script array
	if (!m_num_pad_event_types)
	{
		Script::CArray *p_event_type_array = Script::GetArray("pad_event_types", Script::ASSERT);
		m_num_pad_event_types = p_event_type_array->GetSize();
		Dbg_MsgAssert(m_num_pad_event_types <= MAX_PAD_EVENT_TYPES, ("increase size of MAX_PAD_EVENT_TYPES"));
		for (int i = 0; i < m_num_pad_event_types; i++)
		{
			m_pad_event_type_tab[i] = p_event_type_array->GetChecksum(i);
		}
	}

	// check that the controller is bound to a skater
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	if ( !skate_mod->IsMultiplayerGame() )
	{
		if ( skate_mod->m_requested_level != CRCD( 0x9f2bafb7, "load_skateshop" ) )
		{
			CScreenElementPtr p_element = GetElement( CRCD(0x21f1f4a,"startup_menu"), CScreenElementManager::DONT_ASSERT);
			if ( !p_element )
			{
				int device_num;
				Script::CStruct* pEventData = pEvent->GetData();
				if ( pEventData && pEventData->GetInteger( CRCD(0xc9428a08,"device_num"), &device_num, Script::NO_ASSERT ) )
				{
					if ( skate_mod->m_device_server_map[0] != device_num )
					{
						// this controller isn't bound to the skater!
						return;
					}
				}
			}
		}
	}
	
	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
		
	if (pEvent->GetType() == Obj::CEvent::TYPE_FOCUS) 
	{
		uint32 focus_id = pEvent->GetTarget();
		// HACK: this assert should be there, but was removed to force a last-minute fix
		//Dbg_MsgAssert(focus_id != Obj::CEvent::vSYSTEM_EVENT, ("focus event needs specific target"));
		if (focus_id != Obj::CEvent::vSYSTEM_EVENT)
		{		
			CScreenElementPtr p_focus_element = GetElement(focus_id);
			// HACK: see above
			//Dbg_MsgAssert(p_focus_element, ("focus screen element doesn't exist"));
			if ( p_focus_element && !p_focus_element->EventsBlocked() )
			{
				int controller = Obj::CEvent::sExtractControllerIndex(pEvent);
		
				mark_element_in_focus(p_focus_element, controller);
				
				pEvent->MarkRead(Obj::CTracker::vID_SCREEN_ELEMENT_MANAGER);
			}
		}
	}
	if (pEvent->GetType() == Obj::CEvent::TYPE_UNFOCUS)
	{
		uint32 unfocus_id = pEvent->GetTarget();
		// HACK: this assert should be there, but was removed to force a last-minute fix
		//Dbg_MsgAssert(unfocus_id != Obj::CEvent::vSYSTEM_EVENT, ("unfocus event needs specific target"));
		if (unfocus_id != Obj::CEvent::vSYSTEM_EVENT)
		{		
			CScreenElementPtr p_unfocus_element = GetElement(unfocus_id);
			// HACK: see above
			//Dbg_MsgAssert(p_unfocus_element, ("unfocus screen element %s doesn't exist", Script::FindChecksumName(unfocus_id)));
			if ( p_unfocus_element && !p_unfocus_element->EventsBlocked() )
			{
				mark_element_out_of_focus(p_unfocus_element);
				
				pEvent->MarkRead(Obj::CTracker::vID_SCREEN_ELEMENT_MANAGER);
			}
		}
	}
	if (pEvent->GetType() == Obj::CEvent::TYPE_NOTIFY_CHILD_UNLOCK)
	{
		uint32 unlock_id = pEvent->GetTarget();
		CScreenElementPtr p_unlocked_element = GetElement(unlock_id);
		Dbg_MsgAssert(p_unlocked_element, ("unlock screen element %s doesn't exist", Script::FindChecksumName(unlock_id)));
		
		// mark all descendants TEMPORARILY out of focus
		mark_element_out_of_focus(p_unlocked_element, true, true);
		
		pEvent->MarkRead(Obj::CTracker::vID_SCREEN_ELEMENT_MANAGER);
	}
	if (pEvent->GetType() == Obj::CEvent::TYPE_NOTIFY_CHILD_LOCK)
	{
		uint32 lock_id = pEvent->GetTarget();
		CScreenElementPtr p_last_focus_element = GetElement(lock_id);
		Dbg_MsgAssert(p_last_focus_element, ("unlock screen element %s doesn't exist", Script::FindChecksumName(lock_id)));
		
		// restore children of this element to focus
		remark_temporarily_out_of_focus_elements(p_last_focus_element);
		
		pEvent->MarkRead(Obj::CTracker::vID_SCREEN_ELEMENT_MANAGER);
	}
	if (is_pad_event(pEvent->GetType()) &&
		pEvent->GetTarget() == Obj::CEvent::vSYSTEM_EVENT) 
	{
		bool successful_handling = false;
		
		int controller = Obj::CEvent::sExtractControllerIndex(pEvent);
		
		// forward pad events (of global type) to elements in focus 		
		FocusNode *p_node = mp_focus_list[controller];
		while(p_node)
		{
			p_node->mProcessed = false;
			p_node = p_node->mpNextNode;
		}
		  
		m_focus_list_changed[controller] = true;
		while(m_focus_list_changed[controller])
		{
			m_focus_list_changed[controller] = false;
			p_node = mp_focus_list[controller];
			while(p_node)
			{
				test_focus_node(p_node);
				
				if ( !p_node->mProcessed && !p_node->mTempOutOfFocus && !p_node->mpElement->EventsBlocked() )
				{
					Dbg_MsgAssert(p_node->mpElement,("Node in focus list has NULL mpElement, entry = %d", (int) (p_node - m_focus_node_pool)));
					//Ryan("   sending pad event %d to %s\n", controller, Script::FindChecksumName(p_node->mpElement->GetID()));
					if (p_tracker->LaunchEvent(pEvent->GetType(), p_node->mpElement->GetID(), pEvent->GetSource(), pEvent->GetData()))
					{
						successful_handling = true;
						break;
					}
					p_node->mProcessed = true;
				}

				// the event just sent may have led to the clearing of this	node (and maybe others) -- if 
				// that happens, start again
				if (m_focus_list_changed[controller])
					break;
				
				p_node = p_node->mpNextNode;
			} // end while
			
			if (successful_handling)
				break;
		} // end while
		
		if (successful_handling)
			pEvent->MarkHandled(Obj::CTracker::vID_SCREEN_ELEMENT_MANAGER);
	}
}




bool CScreenElementManager::IsComplexID(Script::CStruct *pStructContainingId, char *pIdSubStructName)
{
	Script::CStruct *p_recurse_struct = NULL;	
	return pStructContainingId->GetStructure(pIdSubStructName, &p_recurse_struct);
}




/*
	This function takes a complex ID and reduces it to a regular checksum that designates a single screen
	element. The following are examples of complex ID's:
	
	id=blah1						<-- also a regular ID
	id={blah1 child=0}				<-- first child of blah1
	id=GetStructure(IdSubStructName, &p_recurse_struct))
	{
		// Expecting the form something={...}
		// May be recursively inside some other ID structure
		
		// grab the 'x' part of structure, where x is something={id ...}
		
		// grab the ID part of something={id ...}
		// we expect either this or a child index (see below)
		// 
		uint32 contained_id = 0;
		p_recurse_struct->GetChecksum(NONAME, &contained_id);

		// grab	the child index part of something={child_index ...}
		int child_index = -1;
		p_recurse_struct->GetInteger(NONAME, &child_index);
		
		if (contained_id != 0)
		{
			if (!mp_resolve_temp)
				// we're at the top level of the recursion chain
				mp_resolve_temp = GetElement(contained_id);
			else
			{
				// we're NOT at the top level of the recursion chain, so treat ID as
				// a LOCAL id
				mp_resolve_temp = get_element_by_local_id(mp_resolve_temp, contained_id);
			}
			if (!mp_resolve_temp)
				return 0;
		}
		else if (child_index != -1) 
		{
			Dbg_MsgAssert(mp_resolve_temp, ("can't map child %d of %s to anything, no parent", child_index, Script::FindChecksumName(IdSubStructName)));
			mp_resolve_temp = mp_resolve_temp->GetChildByIndex(child_index);
			if (!mp_resolve_temp)
				return 0;
		}
		else
		{
			#ifdef	__NOPT_ASSERT__
			Script::PrintContents(pStructContainingId);
			Dbg_MsgAssert(0, ("can't resolve complex ID %s, no ID or index given.  See struct above.", Script::FindChecksumName(IdSubStructName)));
			#endif
		}
		// we can expect to recurse further
		uint32 result_id = ResolveComplexID(p_recurse_struct, CRCD(0xdd4cabd6,"child"));
		mp_resolve_temp = NULL;

		return result_id;
	}
	else if (pStructContainingId->GetChecksum(IdSubStructName, &id))
	{
		// Expecting the form something=some_checksum
		// May be recursively inside some other ID structure
		
		if (mp_resolve_temp)
		{
			// we're NOT at the top level of the recursion chain, so treat ID as
			// a LOCAL id
			mp_resolve_temp = get_element_by_local_id(mp_resolve_temp, id);
			id = mp_resolve_temp->GetID();
		}
		else
		{
			// see if ID is really an alias
			Obj::CTracker* p_tracker = Obj::CTracker::Instance();
			Obj::CObject *p_aliased_obj = p_tracker->GetObjectByAlias(id);
			if (p_aliased_obj)
				// get real ID
				id = p_aliased_obj->GetID();
		}
		
		mp_resolve_temp = NULL;
		return id;
	}
	else if (pStructContainingId->GetInteger(IdSubStructName, &index))
	{
		// Expecting the form something=child_index
		// May be recursively inside some other ID structure
		
		Dbg_MsgAssert(mp_resolve_temp, ("can't map child %d to anything, no parent", index));
		CScreenElementPtr p_child = mp_resolve_temp->GetChildByIndex(index);
		mp_resolve_temp = NULL;
		if (p_child) 		
			return p_child->GetID();
		else
			return 0;
	}
	
	mp_resolve_temp = NULL;
	return 0;	
}


uint32 CScreenElementManager::ResolveComplexID(Script::CStruct *pStructContainingId, char *pIdSubStructName)
{

	return ResolveComplexID(pStructContainingId, Script::GenerateCRC(pIdSubStructName));	
}



void CScreenElementManager::RegisterObject ( Obj::CObject& obj )
{
	#ifdef	__NOPT_ASSERT__
	CScreenElementPtr p_element = static_cast(&obj);
	Dbg_MsgAssert(p_element, ("object registered with ScreenElement manager not ScreenElement"));
	#endif	
						   
	CBaseManager::RegisterObject(obj);
}




void CScreenElementManager::UnregisterObject ( Obj::CObject& obj )
{
	CScreenElementPtr p_unregister_element	= static_cast(&obj);
	Dbg_Assert(p_unregister_element);
	
	Dbg_MsgAssert(!p_unregister_element->mp_parent, ("can't unregister screen element from manager -- still has parent"));
	Dbg_MsgAssert(!p_unregister_element->GetFirstChild(), ("can't unregister screen element from manager -- still has children"));
	Dbg_MsgAssert(!p_unregister_element->GetNextSibling() && !p_unregister_element->GetPrevSibling(), 
				  ("can't unregister screen element from manager -- still has siblings"));
	
	// see if unregister element is root element
	if (p_unregister_element && mp_root_element == p_unregister_element)
	{
		mp_root_element = NULL;
		
		// find a new parentless element to be root element
		Lst::Node *p_node = m_object_list.FirstItem();
		while(p_node)
		{
			CScreenElementPtr p_element = static_cast(p_node->GetData());
			Dbg_Assert(p_element);
			// must have no parent, can't be element that we're unregistering
			if (!p_element->mp_parent && p_element != p_unregister_element)
			{
				mp_root_element = p_element;
				break;
			}
			p_node = p_node->GetNext();
		}
	}
	
	CBaseManager::UnregisterObject(obj);
}




void CScreenElementManager::KillObject ( Obj::CObject& obj )
{
	Dbg_MsgAssert(0, ("this virtual function not supported"));
}




Lst::Head< Obj::CObject > &CScreenElementManager::GetRefObjectList()
{
	Dbg_MsgAssert(0, ("this virtual function not supported"));
	return m_object_list;
}




void CScreenElementManager::destroy_element_recursive(EPreserveParent preserve_parent, const CScreenElementPtr &pElement, Script::CScript *pCallingScript)
{
	pElement->SetChildLockState(CScreenElement::UNLOCK);
	
	CScreenElementPtr p_child = pElement->GetFirstChild();
	while(p_child)
	{
		CScreenElementPtr p_next = p_child->GetNextSibling();
		destroy_element_recursive(DONT_PRESERVE_PARENT, p_child, pCallingScript);
		p_child = p_next;
	}

	if( !preserve_parent )
	{
		if (pCallingScript && pElement)
			// must disassociate script from element being destroyed
			pCallingScript->DisassociateWithObject(pElement);
			
		SetParent(NULL, pElement);
		UnregisterObject(*pElement);
		#ifdef	__NOPT_ASSERT__	
		// Mick:  screen elements are deleted directly, so LockAssert is not applicable.
		pElement->SetLockAssertOff();;
		#endif
		delete pElement;
	}
}




CScreenElementPtr CScreenElementManager::get_element_by_local_id(const CScreenElementPtr &pParent, uint32 desiredLocalID)
{
	uint32 crc_tag_local_id = CRCX("tag_local_id");

	CScreenElementPtr p_child = pParent->GetFirstChild();
	while(p_child)
	{
		uint32 local_id;
		if (p_child->GetChecksumTag(crc_tag_local_id, &local_id))
		{
			if (local_id == desiredLocalID)
				return p_child;
		}
		p_child = p_child->GetNextSibling();
	}
	return NULL;
}




void CScreenElementManager::mark_element_in_focus(const CScreenElementPtr &pElement, int controller)
{
	Dbg_Assert(controller >= 0 && controller <= 1);
	Dbg_Assert(pElement);
	
	FocusNode *p_prev_node = NULL;
	// If in the list for the same controller, then exit function.
	// If in the list for another controller, assert
	for (int c = 0; c < NUM_FOCUS_LISTS; c++)
	{
		FocusNode *p_node = mp_focus_list[c];
		while (p_node)
		{
			test_focus_node(p_node);
			if (p_node->mpElement == pElement)
			{
				if (controller == c) 
				{
					// already in focus, make sure in full focus
					p_node->mTempOutOfFocus = false;
					return;
				}
				else
				{
					Dbg_MsgAssert(0, ("can't change existing focus to another controller"));
				}
			}
			p_prev_node = p_node;
			p_node = p_node->mpNextNode;
		}
	}
	
	// Time to make a new focus node
	// p_prev_node will point to last element in list, or NULL if no list
	
	for (int i = 0; i < NUM_FOCUS_NODES; i++)
	{
		if (!m_focus_node_pool[i].mpElement)
		{
			//Ryan("TTT element %s marked in focus, %d\n", Script::FindChecksumName(pElement->GetID()), (int) controller);
			
			m_focus_node_pool[i].mpElement = pElement;
			m_focus_node_pool[i].mId = pElement->GetID();
			m_focus_node_pool[i].mpNextNode = NULL;
			m_focus_node_pool[i].mTempOutOfFocus = false;
			// put in list -- at end of list
			if (p_prev_node)
				p_prev_node->mpNextNode = &m_focus_node_pool[i];
			else
				mp_focus_list[controller] = &m_focus_node_pool[i];
			m_focus_list_changed[controller] = true;
			return;
		}
	}
	Dbg_Assert(0);
}




void CScreenElementManager::mark_element_out_of_focus(const CScreenElementPtr &pElement, bool onlyChildren, bool tempOnly)
{
	Dbg_Assert(pElement);
	
	for (int c = 0; c < NUM_FOCUS_LISTS; c++)
	{
		// remove from list all elements that are descendents of pElement, and possibly pElement itself

		FocusNode *p_prev_node = NULL;
		FocusNode *p_node = mp_focus_list[c];
		while(p_node)
		{
			test_focus_node(p_node);
			FocusNode *p_next_node = p_node->mpNextNode;

			bool am_descendent = false;
			CScreenElementPtr p_elem = p_node->mpElement;
			if (onlyChildren && p_elem)
				p_elem = p_elem->mp_parent;
			while(p_elem)
			{
				if (p_elem == pElement)
				{
					am_descendent = true;
					break;
				}
				p_elem = p_elem->mp_parent;
			}
			
			if (am_descendent)
			{
				if (tempOnly)
				{
					// just mark temporarily out of focus
					p_node->mTempOutOfFocus = true;
					//Ryan("TTT element %s marked TEMPORARILY out of focus, %d\n", Script::FindChecksumName(p_node->mpElement->GetID()), c);
				}
				else
				{
					// found a match -- remove from list
					if (p_prev_node)
						p_prev_node->mpNextNode = p_next_node;
					else
						mp_focus_list[c] = p_next_node;
					//Ryan("TTT element %s marked OUT OF focus, %d\n", Script::FindChecksumName(p_node->mpElement->GetID()), (int) c);
					// remove from pool
					p_node->mpElement = NULL;
					m_focus_list_changed[c] = true;

					p_node = p_prev_node;
				}
			}
			
			p_prev_node = p_node;
			p_node = p_next_node;
		} // end while p_node
	} // end for c
}




// Elements temporarily marked out-of-focus are put back in focus. Only elements that are descendents 
// of pElement (or pElement) itself are restored.
void CScreenElementManager::remark_temporarily_out_of_focus_elements(const CScreenElementPtr &pElement)
{
	for (int c = 0; c < NUM_FOCUS_LISTS; c++)
	{
		FocusNode *p_node = mp_focus_list[c];
		while(p_node)
		{
			if (p_node->mTempOutOfFocus)
			{				
				// element still exists, make sure it or its ancestor is pElement
				test_focus_node(p_node);
				CScreenElementPtr p_element = p_node->mpElement;
				while (p_element)
				{
					if (p_element == pElement)
					{
						p_node->mTempOutOfFocus = false;
						break;
					}
					p_element = p_element->mp_parent;
				}
			}			
			
			p_node = p_node->mpNextNode;
		}
	} // end for c
}




void CScreenElementManager::test_focus_node(FocusNode *pNode)
{
	#ifdef __NOPT_ASSERT__
	// if this element was removed, its node should have been, too
	Dbg_MsgAssert(pNode->mpElement, ("this node entry shouldn't be in list"));
	Dbg_MsgAssert(GetElement(pNode->mId), ("ID of node associated with non-existant element")); 
	Dbg_MsgAssert(GetElement(pNode->mId) == pNode->mpElement, ("ID of node associated with element other than one pointed to"));
	#endif
}




bool CScreenElementManager::is_pad_event(uint32 eventType)
{
	for (int i = 0; i < m_num_pad_event_types; i++)
	{
		if (eventType == m_pad_event_type_tab[i])
			return true;
	}
	return false;
}

void CScreenElementManager::SetRootScreenElement( uint32 id )
{
	CScreenElementPtr p_elem = GetElement( id, ASSERT );
	mp_root_element = p_elem;
}


bool ScriptCreateScreenElement(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 type;
	if (!pParams->GetChecksum("type", &type))
		Dbg_MsgAssert(0, ("can't create screen element without type"));

	uint32 id = Obj::CBaseManager::vNO_OBJECT_ID;    
    pParams->GetChecksum(CRCD(0x40c698af,"id"), &id);

    // get id of ScreenElement
    CScreenElementManager* pManager = CScreenElementManager::Instance();
    CScreenElementPtr p_Element = pManager->CreateElement(type, id, pParams);
    id = p_Element->GetID();

    // add id to params - will overwrite if id present
    pScript->GetParams()->AddChecksum(CRCD(0x40c698af,"id"), id);

	return true;
}




bool ScriptDestroyScreenElement(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	uint32 id = pManager->ResolveComplexID(pParams, CRCD(0x40c698af,"id"));
	Dbg_MsgAssert(id, ("\n%s\nelement not in manager",pScript->GetScriptInfo()));
	
	// Let's always destroy recursively -- is there any reason not to?
	CScreenElementManager::ERecurse recurse = 
		CScreenElementManager::ERecurse(!pParams->ContainsFlag("dont_recurse"));
	CScreenElementManager::EPreserveParent preserve_parent = 
		CScreenElementManager::EPreserveParent(pParams->ContainsFlag("preserve_parent"));
	
	// This combination doesn't really make sense
	// Warn the user and do nothing since, in effect, it is an empty operation
	if( !recurse && preserve_parent )
	{
		Dbg_Printf( "Warning: Destroying an element without recursion and with preserve_parent makes no sense\n" );
		return true;
	}

	pManager->DestroyElement(id, recurse, preserve_parent, pScript);
	
	return true;
}




bool ScriptRunScriptOnScreenElement(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement(pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT);
		
	uint32 script;
	pParams->GetChecksum(NONAME, &script, true);
	
	uint32 callback = 0;
	pParams->GetChecksum(CRCD(0x86068bd9,"callback"), &callback);
    
    Script::CStruct *p_ScriptParams = NULL;
    pParams->GetStructure(CRCD(0x7031f10c,"params"), &p_ScriptParams);

    Script::CStruct *p_CallbackParams = NULL;
    pParams->GetStructure(CRCD(0xe6cf88c4,"callback_params"), &p_CallbackParams);

	// K: If script is actually a member function, then call it, for Gary bless him.
	// G: God bless us, everyone!
    Script::CSymbolTableEntry *p_entry=Script::Resolve(script);
    if (p_entry && p_entry->mType==ESYMBOLTYPE_MEMBERFUNCTION)
    {
		Dbg_MsgAssert(p_elem,("NULL p_elem"));
		p_elem->CallMemberFunction(script,p_ScriptParams,pScript);
	}	
	else
	{
		Script::CScript *p_new_script = Script::SpawnScript(script,	p_ScriptParams, callback, p_CallbackParams);
		#ifdef __NOPT_ASSERT__
		p_new_script->SetCommentString("Spawned by script command RunScriptOnScreenElement");
		p_new_script->SetOriginatingScriptInfo(pScript->GetCurrentLineNumber(),pScript->mScriptChecksum);
		#endif
		
		// K: This 'if' is now required because if script is actually a cfunc, SpawnScript will have run it,
		// then returned NULL cos it did not need to create a script.
		if (p_new_script)
		{
			p_new_script->mpObject = p_elem;
			// normally, script won't be updated until next frame -- we want it NOW, motherfucker
			p_new_script->Update();
			//Script::RunScript(script, pParams, pElement);
		}	
	}
		
	return true;
}




bool ScriptSetScreenElementProps(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement(pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT);
			
	p_elem->SetProperties(pParams);
	
	return true;
}




/*
CScreenElementPtr KlaabuBaabu()
{
	return NULL;
}
*/

bool ScriptDoScreenElementMorph(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement(pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT);
			
	p_elem->SetMorph(pParams);
	
	//CScreenElementPtr arg_bat = KlaabuBaabu();
	//printf("KlaabuBaabu 0x%x\n", arg_bat.Convert());
	//printf("KlaabuBaabu 0x%x\n", KlaabuBaabu().Convert());
	//Dbg_Assert(0);
	
	return true;
}




bool ScriptSetScreenElementLock(Script::CStruct *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement(pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT);
	Dbg_MsgAssert(p_elem, ("element not in manager"));
			
	if (pParams->ContainsFlag("off"))
		p_elem->SetChildLockState(CScreenElement::UNLOCK);
	else
		p_elem->SetChildLockState(CScreenElement::LOCK);
	
	return true;
}




bool ScriptScreenElementSystemInit(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
	CWindowElement *p_window = new CWindowElement();
	Mem::Manager::sHandle().PopContext();

	p_window->SetID(Script::GenerateCRC("root_window"));

	CScreenElementManager* pManager = CScreenElementManager::Instance();
	pManager->RegisterObject(*p_window);

	return true;
}

/*
	Temporary hack functions for THPS4. Of course, you the reader are probably now
	using them for THPS8! :x
*/

void SetScoreTHPS4(char* score_text, int skater_num)
{
	Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
	CScreenElementPtr p_element = p_screen_elem_man->GetElement(Script::GenerateCRC("the_score") + skater_num );
	if (p_element)
	{
		Dbg_MsgAssert((uint32)p_element->GetType() == CRCD(0x5200dfb6, "TextElement"), ("type is 0x%x", p_element->GetType()));
		CTextElement *p_score_element = (CTextElement *) (p_element.Convert());
		
		p_score_element->SetText(score_text);
	}
}

bool ScriptGetScreenElementDims(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement(pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT);
	
	int width = (int)( p_elem->GetBaseW() * p_elem->GetScaleX() );
	int height = (int)( p_elem->GetBaseH() * p_elem->GetScaleY() );

	pScript->GetParams()->AddInteger( "width", width );
	pScript->GetParams()->AddInteger( "height", height );
	return true;
}


// @script | TextElementConcatenate | this will append the given string on the end of the 
// current text element's string
// @parm name | id | the id of the element
// @uparm "string" | the string to append
// @flag enforce_max_width | Don't allow concatenation on a text block
// element if there's no way to wrap the element and keep it within the
// maximum width.  This has no effect on TextElements.
bool ScriptTextElementConcatenate(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement( pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT );
	
	const char* p_concat_string;
	pParams->GetString( NONAME, &p_concat_string, Script::ASSERT );

	uint32 type = p_elem->GetType();
	if ( type == CRCD( 0x40d92263, "TextBlockElement" ) )
	{
		CTextBlockElement* pTextBlockElement = (CTextBlockElement*)p_elem.Convert();
		return pTextBlockElement->Concatenate( p_concat_string, pParams->ContainsFlag( CRCD( 0x27e7a420, "enforce_max_width" ) ), pParams->ContainsFlag( CRCD(0xb8c08f55,"last_line") ) );
	}
	else if ( type == CRCD( 0x5200dfb6, "TextElement" ) )
	{
		CTextElement* pTextElement = (CTextElement*)p_elem.Convert();
		return pTextElement->Concatenate( p_concat_string );
	}
	else
	{
		Dbg_MsgAssert( 0, ( "TextElementConcatenate called on type 0x%x", p_elem->GetType() ) );
		return false;
	}
}

// @script | TextElementBackspace | removes one character from the text element.  This will
// return false if the text element is already empty
// @parm name | id | the element id
bool ScriptTextElementBackspace(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement( pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT );

	uint32 type = p_elem->GetType();
	switch ( type )
	{
		case CRCC( 0x5200dfb6, "TextElement" ):
		{
			CTextElement* p_text_element = (CTextElement*)p_elem.Convert();
			return p_text_element->Backspace();
			break;
		}
		case CRCC( 0x40d92263, "TextBlockElement" ):
		{
			CTextBlockElement* p_text_block_element = (CTextBlockElement*)p_elem.Convert();
			return p_text_block_element->Backspace();
			break;
		}
		default:
			Dbg_MsgAssert( 0, ( "TextElementBackspace called on %s, which has type %x", Script::FindChecksumName( p_elem->GetID() ), p_elem->GetType() ) );
			return false;
			break;
	}
}

// @script | GetTextElementString | this returns the current string for the specified 
// text element.  The string is returned in the script's params (string).
// @parm name | id | the text element id
bool ScriptGetTextElementString(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement( pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT );
	
	bool found_text = false;
	switch ( (uint32)p_elem->GetType() )
	{
		case CRCC( 0x5200dfb6, "TextElement" ):
		{
			CTextElement* p_text_element = (CTextElement*)p_elem.Convert();
			char *p_text = p_text_element->GetText();
			if ( p_text )
			{
				pScript->GetParams()->AddString( "string", p_text );
				found_text = true;
			}
			break;
		}
		case CRCC( 0x40d92263, "TextBlockElement" ):
		{
			CTextBlockElement* p_text_block_element = (CTextBlockElement*)p_elem.Convert();
			char text[Front::MAX_EDITABLE_TEXT_BLOCK_LENGTH];
			if ( p_text_block_element->GetText( text, Front::MAX_EDITABLE_TEXT_BLOCK_LENGTH ) )
			{
				pScript->GetParams()->AddString( "string", text );
				found_text = true;
			}
			break;
		}
		default:
			Dbg_MsgAssert( 0, ( "GetScreenElementText called on type %x", p_elem->GetType() ) );
			break;
	}
	return false;
}

// @script | GetTextElementLength | returns the length of the specified text element
// in the scripts params (length)
// @parm name | id | the text element's id
bool ScriptGetTextElementLength(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement( pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT );

	uint32 type = (uint32)p_elem->GetType();
	int length = 0;
	switch ( type )
	{
		case CRCC( 0x5200dfb6, "TextElement" ):
		{
			CTextElement* p_text_element = (CTextElement*) p_elem.Convert();
			length = p_text_element->GetLength();
			break;
		}
		case CRCC( 0x40d92263, "TextBlockElement" ):
		{
			CTextBlockElement* p_text_block_element = (CTextBlockElement*) p_elem.Convert();
			length = p_text_block_element->GetLength();
			break;
		}
		default:
			Dbg_MsgAssert( 0, ("GetTextElementLength called on screen element with type 0x%x", type ) );
			return false;
			break;
	}
	pScript->GetParams()->AddInteger( "length", length );
	return true;
}

bool ScriptGetScreenElementPosition(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement(pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT);
	
	//float x = p_elem->GetAbsX();
	//float y = p_elem->GetAbsY();
	float x, y;
	p_elem->GetLocalULPos( &x, &y );

	pScript->GetParams()->AddPair( "ScreenElementPos", x, y );
	return true;
}

// @script | MenuSelectedIndexIs | returns true if the selected item in the vmenu is 
// the item specified.  Must be called with either an index, the "first" flag, or the "last" flag
// @uparmopt 1 | some index value (first item is index 0)
// @flag first | checks if the selected item is the first item
// @flag last | checks if the selected item is the last item
bool ScriptMenuSelectedIndexIs(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement(pParams, CRCD(0x40c698af,"id"), CScreenElementManager::ASSERT);

	Dbg_MsgAssert( (uint32) p_elem->GetType() == CRCD( 0x130ef802, "vmenu" ), ( "Screen element has wrong type." ) );

	CBaseMenu* p_menu = (CBaseMenu*)p_elem.Convert();

	int selected_index = p_menu->GetSelectedIndex();
	if ( selected_index == -1 )
		return false;
	
	bool rv = false;
	
	int testIndex;
	if ( pParams->GetInteger( NONAME, &testIndex, Script::NO_ASSERT ) )
		rv = ( testIndex == selected_index );
	else if ( pParams->ContainsFlag( "first" ) )
		rv = ( selected_index == 0 );
	else if ( pParams->ContainsFlag( "last" ) )
		rv = ( selected_index == ( p_menu->CountChildren() - 1 ) );
	else
		Dbg_MsgAssert( 0, ("MenuSelectedIndexIs must be called with a number, the \"first\" flag, or the \"last\" flag") );

	return rv;
}

// @script | ScreenElementExists | returns true if the given screen element exists
// @parm name | id | the id to look for...supports compound id's
bool ScriptScreenElementExists(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();
	CScreenElementPtr p_elem = pManager->GetElement( pParams, CRCD(0x40c698af,"id"), CScreenElementManager::DONT_ASSERT );
	if ( p_elem )
		return true;
	return false;
}

// @script | GetScreenElementProps | writes the props of the given screen element to the 
// calling script's params
// @parm name | id | the id of the screen element
// @flag | dont_assert
bool ScriptGetScreenElementProps( Script::CScriptStructure *pParams, Script::CScript *pScript )
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();	  
	CScreenElementPtr p_elem = NULL;
	uint32 id = pManager->ResolveComplexID(pParams, CRCD(0x40c698af,"id"));
	if ( id )
	{
		p_elem = pManager->GetElement(id, CScreenElementManager::DONT_ASSERT);
	}
	
	if ( p_elem )
	{
		p_elem->WritePropertiesToStruct( pScript->GetParams() );
		return true;
	}
	else
	{
		if ( pParams->ContainsFlag( CRCD(0x3d92465e,"dont_assert") ) )
			return false;
		Dbg_MsgAssert( 0, ( "%s\n SetScreenElementProps unable to find screen element %s",  pScript->GetScriptInfo(),  Script::FindChecksumName(id) ) );
	}
	return false;
}

bool ScriptSetRootScreenElement( Script::CStruct* pParams, Script::CScript* pScript )
{
	CScreenElementManager* pManager = CScreenElementManager::Instance();	  
	uint32 id = pManager->ResolveComplexID(pParams, CRCD(0x40c698af,"id"));
	if ( id )
	{
		pManager->SetRootScreenElement( id );
		return true;
	}
	return false;
}

void SetTimeTHPS4(int minutes, int seconds)
{
	Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
	CScreenElementPtr p_element = p_screen_elem_man->GetElement(CRCD(0xa6343cd4,"the_time"));
	if (p_element)
	{
		Dbg_MsgAssert((uint32) p_element->GetType() == CRCD(0x5200dfb6, "TextElement"), ("type is 0x%x", p_element->GetType()));
		CTextElement *p_time_element = (CTextElement *) p_element.Convert();
		
		char time_text[64];
		sprintf(time_text, "%2d:%.2d", minutes, seconds);
		
		p_time_element->SetText(time_text);
	}
}


void HideTimeTHPS4()
{
    Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
    CScreenElementPtr p_element = p_screen_elem_man->GetElement(CRCD(0xa6343cd4,"the_time"));
    if (p_element)
    {
        Dbg_MsgAssert((uint32) p_element->GetType() == CRCD(0x5200dfb6, "TextElement"), ("type is 0x%x", p_element->GetType()));
        CTextElement *p_time_element = (CTextElement *) p_element.Convert();
        p_time_element->SetText( "" );
    }
}


}



================================================
FILE: Code/Gfx/2D/ScreenElemMan.h
================================================
#ifndef __GFX_2D_SCREENELEMMAN_H__
#define __GFX_2D_SCREENELEMMAN_H__

#include 
#include 
#include 

#include 

namespace Script
{
	class CScriptStructure;
	class CScript;
}

namespace Front
{

/*
	Manages all screen elements. References the root element of the screen element
	parent/child tree.
	
	Keeps track of which elements are in focus, and makes sure that pad events are
	passed to these elements.
*/
class CScreenElementManager : public Obj::CBaseManager, public Obj::CEventListener
{

	DeclareSingletonClass( CScreenElementManager );

public:
								CScreenElementManager();
								~CScreenElementManager();

	CScreenElementPtr 			CreateElement(uint32 type, uint32 id, Script::CStruct *pProps);
	enum 						ERecurse { DONT_RECURSE = 0, RECURSE = 1};
	enum 						EPreserveParent { DONT_PRESERVE_PARENT = 0, PRESERVE_PARENT = 1};
	void						DestroyElement(uint32 id, ERecurse recurse = RECURSE, EPreserveParent preserveParent = DONT_PRESERVE_PARENT, Script::CScript *pCallingScript = NULL);

	void						SetParent(const CScreenElementPtr &pParent, const CScreenElementPtr &pChild, CScreenElement::EPosRecalc recalculatePosition = CScreenElement::vRECALC_POS);
	enum EAssert
	{
		DONT_ASSERT = 0,
		ASSERT,
	};
	CScreenElementPtr 			GetElement(uint32 id, EAssert assert = DONT_ASSERT);
	CScreenElementPtr 			GetElement(Script::CStruct *pStructContainingId, char *pIdSubStructName, EAssert assert = DONT_ASSERT);
	CScreenElementPtr 			GetElement(Script::CStruct *pStructContainingId, uint32 IdSubStructName, EAssert assert);

	void						Update();
	void						SetPausedState(bool pause);
	
	void 						set_tree_lock_state(CScreenElement::ELockState state);
	void						pass_event_to_listener(Obj::CEvent *pEvent);	
	
	bool						IsComplexID(Script::CStruct *pStructContainingId, char *pIdSubStructName);
	uint32 						ResolveComplexID(Script::CStruct *pStructContainingId, uint32 IdSubStructName);
	uint32						ResolveComplexID(Script::CStruct *pStructContainingId, char *pIdSubStructName);
	
	/*
		Virtual functions from CBaseManager
	*/
	
	void						RegisterObject ( Obj::CObject& obj );
	void						UnregisterObject ( Obj::CObject& obj );	
	void						KillObject ( Obj::CObject& obj );
	Lst::Head< Obj::CObject > &	GetRefObjectList();

	void						SetRootScreenElement( uint32 id );

private:

	struct FocusNode // in the focus tree
	{
		//FocusNode *				mpFirstChild;
		//FocusNode *				mpSibling;
		CScreenElementPtr 		mpElement;
		uint32					mId;
		FocusNode *				mpNextNode;
		bool					mProcessed;
		bool					mTempOutOfFocus;
	};
	
	void						destroy_element_recursive(EPreserveParent preserve_parent, const CScreenElementPtr &pElement, Script::CScript *pCallingScript);
	CScreenElementPtr 			get_element_by_local_id(const CScreenElementPtr &pParent, uint32 desiredLocalID);

	void						mark_element_in_focus(const CScreenElementPtr &pElement, int controller);
	void						mark_element_out_of_focus(const CScreenElementPtr &pElement, bool onlyChildren = false, bool temporaryOnly = false);
	void						remark_temporarily_out_of_focus_elements(const CScreenElementPtr &pElement);
	void 						test_focus_node(FocusNode *pNode);

	bool						is_pad_event(uint32 eventType);
	
	CScreenElementPtr			mp_root_element;

	// a temporary pointer used by ResolveComplexID() for recursion
	CScreenElementPtr 			mp_resolve_temp;
	
	enum
	{
		NUM_FOCUS_NODES	=		48,					// basically, a pool of these
		NUM_FOCUS_LISTS =		SIO::vMAX_DEVICES,	// one for each controller
	};
	
	FocusNode					m_focus_node_pool[NUM_FOCUS_NODES];	
	FocusNode *					mp_focus_list[NUM_FOCUS_LISTS];
	bool						m_focus_list_changed[NUM_FOCUS_LISTS];

	enum
	{
		MAX_PAD_EVENT_TYPES		= 48,
	};
	uint32						m_pad_event_type_tab[MAX_PAD_EVENT_TYPES];
	int							m_num_pad_event_types;
};



bool ScriptCreateScreenElement(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptDestroyScreenElement(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptRunScriptOnScreenElement(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetScreenElementProps(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptDoScreenElementMorph(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetScreenElementLock(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptScreenElementSystemInit(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptGetScreenElementDims(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptTextElementConcatenate(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptTextElementBackspace(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptGetTextElementString(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptGetTextElementLength(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptGetScreenElementPosition(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptMenuSelectedIndexIs(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptScreenElementExists(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptGetScreenElementProps( Script::CScriptStructure *pParams, Script::CScript *pScript );
bool ScriptSetRootScreenElement( Script::CStruct* pParams, Script::CScript* pScript );
}

#endif


================================================
FILE: Code/Gfx/2D/ScreenElement2.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#ifdef	__PLAT_NGPS__	
#include 
#endif

/*
	=========================================================
	RTFM!!!!
	
	Be sure to check out the user's guide for the menu system
	to get an idea of what all this code's for. Heurghh!!
	=========================================================
*/

namespace Front
{


const float CScreenElement::vJUST_LEFT		= -1.0f;
const float CScreenElement::vJUST_TOP		= -1.0f;
const float CScreenElement::vJUST_CENTER	= 0.0f;
const float CScreenElement::vJUST_RIGHT		= 1.0f;
const float CScreenElement::vJUST_BOTTOM	= 1.0f;

const float CScreenElement::AUTO_Z_SPACE	= 1.0f;

CScreenElement::CScreenElement()
	: CObject()
{
	m_key_time = 0;
	mp_parent = NULL;
	mp_child_list = NULL;
	mp_prev_sibling = NULL;
	mp_next_sibling = NULL;

	m_local_props.SetScale(1.0f, 1.0f);
	m_local_props.SetAbsoluteScale(1.0f, 1.0f);
	m_local_props.SetScreenPos(0.0f, 0.0f);
	m_local_props.SetRotate( 0.0f );
	m_local_props.SetWorldPos( Mth::Vector(0.0f, 0.0f, 0.0f) );
	
	Image::RGBA default_rgba;
	default_rgba.r = 128;
	default_rgba.g = 128;
	default_rgba.b = 128;
	default_rgba.a = 128;
	m_local_props.SetRGBA( default_rgba );

	m_local_props.alpha = 1.0f;
	m_target_local_props = m_local_props;
	m_summed_props = m_local_props;

	m_base_w = 0.0f;
	m_base_h = 0.0f;

	m_rgba.r = 128;
	m_rgba.g = 128;
	m_rgba.b = 128;
	m_rgba.a = 128;
	
	m_z_priority = 0.0f;

	m_originalAlpha = 1.0f;
	
	m_object_flags |= vNEEDS_LOCAL_POS_CALC;
	m_object_flags |= vIS_SCREEN_ELEMENT;
}




CScreenElement::~CScreenElement()
{
	set_parent(NULL, vDONT_RECALC_POS);
	m_id = 0xDEADBEEF;
}




// absolute upper-left, last frame's
float CScreenElement::GetAbsX()
{
	return m_summed_props.GetScreenUpperLeftX();
}




// absolute upper-left, last frame's
float CScreenElement::GetAbsY()
{
	return m_summed_props.GetScreenUpperLeftY();
}




// absolute width, includes scale, last frame's
float CScreenElement::GetAbsW()
{
	return m_base_w * m_summed_props.GetAbsoluteScaleX();
}




// absolute height, includes scale, last frame's
float CScreenElement::GetAbsH()
{
	return m_base_h * m_summed_props.GetAbsoluteScaleY();
}




/*
	Position of anchor point, in parent's space. 'Target' means where it's going,
	not necessarily where it is.
*/
void CScreenElement::GetLocalPos(float *pX, float *pY)
{
	Dbg_Assert(pX);
	Dbg_Assert(pY);
	
	*pX = m_target_local_props.GetScreenPosX();
	*pY = m_target_local_props.GetScreenPosY();
}




/*
	Position of upper left corner of element, in parent's space. 'Target' means where 
	it's going, not necessarily where it is.
*/
void CScreenElement::GetLocalULPos(float *pX, float *pY)
{
	Dbg_Assert(pX);
	Dbg_Assert(pY);
	
	*pX = m_target_local_props.GetScreenUpperLeftX();
	*pY = m_target_local_props.GetScreenUpperLeftY();
}




/*
	Justification is a fixed attribute (doesn't morph)
*/
void CScreenElement::GetJust(float *pJustX, float *pJustY)
{
	Dbg_Assert(pJustX);
	Dbg_Assert(pJustY);
	
	*pJustX = m_just_x;
	*pJustY = m_just_y;
}




/* 
	Sets desired local position, element will morph from current position to
	that position over set morph time (unless force_instant set).
*/
void CScreenElement::SetPos(float x, float y, EForceInstant forceInstant)
{
	Dbg_MsgAssert(!(m_object_flags & CScreenElement::v3D_POS), ("Illegal to set the 2D position of a 3D screen element"));

	if (m_target_local_props.GetScreenPosX() != x || m_target_local_props.GetScreenPosY() != y)
	{
		m_target_local_props.SetScreenPos(x, y);
		m_object_flags |= vNEEDS_LOCAL_POS_CALC;
		compute_ul_pos(m_target_local_props);
	}
	if (forceInstant) 
	{
		if (m_local_props.GetScreenPosX() != x || m_local_props.GetScreenPosY() != y)
		{
			// on the next update, this element will arrive at the target position
			m_local_props.SetScreenPos(x, y);
			m_object_flags |= vNEEDS_LOCAL_POS_CALC;
		}
	}
}


/* 
	Sets desired world position, element will morph from current position to
	that position over set morph time (unless force_instant set).
*/
void CScreenElement::SetPos3D(const Mth::Vector & pos3D, EForceInstant forceInstant)
{
	if (m_target_local_props.GetWorldPos() != pos3D )
	{
		m_target_local_props.SetWorldPos(pos3D);
		m_object_flags |= vNEEDS_LOCAL_POS_CALC;
		compute_ul_pos(m_target_local_props);
	}
	if (forceInstant) 
	{
		if (m_local_props.GetWorldPos() != pos3D )
		{
			// on the next update, this element will arrive at the target position
			m_local_props.SetWorldPos(pos3D);
			m_object_flags |= vNEEDS_LOCAL_POS_CALC;
		}
	}

	m_object_flags |= CScreenElement::v3D_POS;
}


/*
	Set 'force' if dims being set from outside element. When that happens, element cannot
	change own dims.
	
	Well, actually it can (VMenu), but 'force' is taken is a suggestion.
*/
void CScreenElement::SetDims(float w, float h, EForceDims force)
{
	if (m_base_w != w || m_base_h != h)
	{
		m_object_flags |= vNEEDS_LOCAL_POS_CALC;
		m_base_w = w;
		m_base_h = h;
		compute_ul_pos(m_target_local_props);
	}
	
	if (force) 
		m_object_flags |= vFORCED_DIMS;
	else
		m_object_flags &= ~vFORCED_DIMS;
}




/*
	Justification effects where the upper-left corner of an element is relative to 
	the element's assigned position (anchor point). 
	
	(-1.0, -1.0):	anchor is at upper-left of element
	(0.0, 0.0):		anchor is in center of element
	(1.0, 1.0):		anchor is at lower-right of element
	
	You can figure out the meaning of other numbers.
*/
void CScreenElement::SetJust(float justX, float justY)
{
	if (m_just_x != justX || m_just_y != justY)
	{
		// calculate justification differences and adjust new position so the item still appears to
		// have the same location as before
		float x_diff = m_local_props.GetScaleX() * m_base_w * (justX - m_just_x) / 2.0f;
		float y_diff = m_local_props.GetScaleY() * m_base_h * (justY - m_just_y) / 2.0f;
		m_local_props.SetScreenPos(m_local_props.GetScreenPosX() + x_diff, m_local_props.GetScreenPosY() + y_diff);
		m_target_local_props.SetScreenPos(m_target_local_props.GetScreenPosX() + x_diff, m_target_local_props.GetScreenPosY() + y_diff);
		//m_local_props.ulx += x_diff;
		//m_local_props.uly += y_diff;

		m_just_x = justX;
		m_just_y = justY;
		m_object_flags |= vNEEDS_LOCAL_POS_CALC;
		
		compute_ul_pos(m_target_local_props);
	}
}




/* 
	Sets desired local scale, element will morph from current scale to
	that scale over set morph time (unless force_instant set).
*/
void CScreenElement::SetScale(float scaleX, float scaleY, bool relative, EForceInstant forceInstant )
{
	if (m_target_local_props.GetScaleX() != scaleX || m_target_local_props.GetScaleY() != scaleY || relative)
	{
		if ( !relative )
		{
			m_target_local_props.SetAbsoluteScale(scaleX, scaleY);
		}
		else
		{
			scaleX *= m_target_local_props.GetAbsoluteScaleX();
			scaleY *= m_target_local_props.GetAbsoluteScaleY();
		}
		m_target_local_props.SetScale(scaleX, scaleY);
		compute_ul_pos(m_target_local_props);
		m_object_flags |= vNEEDS_LOCAL_POS_CALC;
	}
	if (forceInstant) 
	{
		if (m_local_props.GetScaleX() != scaleX || m_local_props.GetScaleY() != scaleY)
		{
			// on the next update, this element will arrive at the target scale
			m_local_props.SetScale(scaleX, scaleY);
			m_object_flags |= vNEEDS_LOCAL_POS_CALC;
		}
	}
}




/* 
	Sets desired local alpha, element will morph from current alpha to
	that alpha over set morph time (unless force_instant set).
*/
void CScreenElement::SetAlpha(float alpha, EForceInstant forceInstant)
{
	if (m_target_local_props.alpha != alpha)
	{
		m_target_local_props.alpha = alpha;
		m_object_flags |= vNEEDS_LOCAL_POS_CALC;
	}
	if (forceInstant) 
	{
		if (m_local_props.alpha != alpha)
		{
			// on the next update, this element will arrive at the target alpha
			m_local_props.alpha = alpha;
			m_object_flags |= vNEEDS_LOCAL_POS_CALC;
		}
	}
}




/*
	Specifies the amount of time over which this element will changes from its
	current morphable properties (pos, scale, alpha) to the target ones. If
	an animation already in progress, override previously set time.
*/
void CScreenElement::SetAnimTime(Tmr::Time animTime)
{
	m_base_time = Tmr::GetTime();
	m_key_time = animTime;
	m_last_motion_grad = 0.0f;
	m_last_pct_changed = 0.0f;
	
	if (!animTime)
	{
		m_local_props = m_target_local_props;
	}
	m_object_flags |= vNEEDS_LOCAL_POS_CALC;
	m_object_flags &= ~vMORPHING_PAUSED;
	m_object_flags &= ~vMORPHING_PAUSED2;
}




/*
	Note: The alpha component here is multiplied by the alpha from SetAlpha() to
	determine final alpha.
*/
void CScreenElement::SetRGBA( Image::RGBA rgba, EForceInstant forceInstant )
{
	Image::RGBA target_rgba = m_target_local_props.GetRGBA();
	if ( target_rgba.r != rgba.r || target_rgba.g != rgba.g || target_rgba.b != rgba.b || target_rgba.a != rgba.a )
	{
		m_target_local_props.SetRGBA( rgba );
		m_object_flags |= vNEEDS_LOCAL_POS_CALC;
	}
	if ( forceInstant )
	{
		m_local_props.SetRGBA( rgba );
		m_object_flags |= vNEEDS_LOCAL_POS_CALC;
	}
}


/*
	Used to force drawing order entry (instead of it being automatically assigned). Client code should be 
	written using knowledge of how these values work.
*/
void CScreenElement::SetZPriority(float zPriority)
{
	m_z_priority = zPriority;
	m_object_flags |= vCHANGED_STATIC_PROPS;
	m_object_flags |= vCUSTOM_Z_PRIORITY;
}




void CScreenElement::SetMorphPausedState(bool pause)
{
	if (pause)
		m_object_flags |= vMORPHING_PAUSED;
	else
	{
		m_object_flags &= ~vMORPHING_PAUSED;
		m_object_flags &= ~vMORPHING_PAUSED2;
	}
}




/*
	When element is unlocked, children can be added or removed, otherwise they can't. Really just an 
	optimization so that lots of children can be added en masse without slowdown.
	
	Note: The children you add to an element don't officially exist until you lock the element again,
	so don't forget to do this.
*/
void CScreenElement::SetChildLockState(CScreenElement::ELockState lockState)
{
	ELockState current_lock_state = UNLOCK;
	if (m_object_flags & CScreenElement::vCHILD_LOCK) current_lock_state = LOCK;
	if (lockState == current_lock_state)
		return;

	if (lockState == LOCK)
		m_object_flags |= CScreenElement::vCHILD_LOCK;
	else
		m_object_flags &= ~CScreenElement::vCHILD_LOCK;

	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
	
	if (lockState == LOCK) 
	{	
		//Ryan("Setting child lock on, item %s\n", Script::FindChecksumName(m_id));
		
		// send out notice that child lock has been turned on
		p_tracker->LaunchEvent(Obj::CEvent::TYPE_NOTIFY_CHILD_LOCK, m_id);
	}
	else
	{
		//Ryan("Setting child lock off, item %s\n", Script::FindChecksumName(m_id));
		
		// send out notice that child lock has been turned off
		p_tracker->LaunchEvent(Obj::CEvent::TYPE_NOTIFY_CHILD_UNLOCK, m_id);
	}
}




CScreenElement* CScreenElement::GetLastChild()
{
	CScreenElement* pChild = mp_child_list;
	while(pChild)
	{
		if (!pChild->GetNextSibling())
			break;
		
		pChild = pChild->GetNextSibling();
	}

	return pChild;
}




/*
	Returns a pointer to the child with that ID.
*/
CScreenElement* CScreenElement::GetChildById(uint32 id, int *pIndex)
{
	return get_child_by_id_or_index(id, pIndex);
}




/*
	Returns a pointer to the Nth child, where N = index.
	
	The children are indexed in the order they are added. If one is removed,
	then the indices of all subsequent children are shifted down a notch.
*/
CScreenElement* CScreenElement::GetChildByIndex(int index)
{
	return get_child_by_id_or_index(0, &index);
}




/*
	Returns number of children.
*/
int CScreenElement::CountChildren()
{
	CScreenElementPtr p_child = mp_child_list;
	int count = 0;
	while(p_child)
	{
		p_child = p_child->GetNextSibling();
		count++;
	}
	return count;
}




/*
	An important entry point to CScreenElement from script. Sets the elements static
	(as opposed to morphable) properties.
	
	Note: a virtual function, so can be extended by subclasses
	Note: SetProperties() in a subclass should call this function before doing its own logic
*/
void CScreenElement::SetProperties(Script::CStruct *pProps)
{
	CScreenElementManager *pManager = static_cast(mp_manager);
	Dbg_Assert(pManager);

	Script::CPair pos;
	if (pProps->GetPair(CRCD(0x7f261953,"pos"), &pos))
	{
		// in this case, position will be forced instantly to the specified position
		SetPos(pos.mX, pos.mY, FORCE_INSTANT);
	}

	Mth::Vector pos3D;
	if (pProps->GetVector(CRCD(0x4b491900,"pos3D"), &pos3D))
	{
		pos3D[W] = 1.0f;		// force to be a point
		//Dbg_Message("************ position (%f, %f, %f)", pos3D[X], pos3D[Y], pos3D[Z]);
		SetPos3D(pos3D, FORCE_INSTANT);
	}

	Script::CPair dims;
	if (pProps->GetPair(CRCD(0x34a68574,"dims"), &dims))
	{
		SetDims(dims.mX, dims.mY, FORCE_DIMS);
	}

	uint32 parent_id = pManager->ResolveComplexID(pProps, CRCD(0xc2719fb0,"parent"));
	if (parent_id)
	{
		CScreenElementPtr pParent = pManager->GetElement(parent_id);
		#ifdef __NOPT_ASSERT__
		
		if (pParent->GetType() == CScreenElement::TYPE_TEXT_BLOCK_ELEMENT)
		{
			if (GetType() != CScreenElement::TYPE_TEXT_ELEMENT)
			{
				Script::PrintContents(pProps);
				Dbg_MsgAssert(0,("TextBlockElement %s parenting child %s that is NOT a CTextElement (it's %s)\nIn CScreenElement::SetProperties\n^^^ SEE STRUCTURE ABOVE MEM DUMP & TELL BRAD^^^\n",
					Script::FindChecksumName(pParent->GetID()), Script::FindChecksumName(GetID()), Script::FindChecksumName(GetType())));
			}
		}
		
		if (!pParent)
		{
			Script::PrintContents(pProps, 2);
			Dbg_MsgAssert(0, ("could not resolve parent, see struct above"));
		}
		#endif
		pManager->SetParent(pParent, this);
	}

	float just[2] = { 0.0f, 0.0f };
	if (resolve_just(pProps, CRCD(0x8b60022f,"just"), just, just+1))
		SetJust(just[0], just[1]);
	
	
	Image::RGBA rgba;
	if (resolve_rgba(pProps, CRCD(0x3f6bcdba,"rgba"), &rgba))
	{
		SetRGBA(rgba);
	}
		
	float z_priority;
	if (pProps->GetFloat(CRCD(0x57710f31,"z_priority"), &z_priority))
	{
		SetZPriority(z_priority);
	}
	
	if (pProps->ContainsFlag(CRCD(0x1d944426,"not_focusable")))
	{
		// sets a flag
		SetChecksumTag(NONAME, CRCD(0xf33a3321,"tag_not_focusable"));
	}
	else if (pProps->ContainsFlag(CRCD(0x5cdecf29,"focusable")))
	{
		RemoveFlagTag(CRCD(0xf33a3321,"tag_not_focusable"));
	}
	
	uint32 local_id;
	if (pProps->GetChecksum(CRCD(0xa2a5defe,"local_id"), &local_id))
	{
		SetChecksumTag(CRCD(0x9494a525,"tag_local_id"), local_id); 
	}

	if (pProps->ContainsFlag(CRCD(0xff85d4d7,"unpause")))
	{
		SetMorphPausedState(false);
	}

	if ( pProps->ContainsFlag( CRCD(0xe5d6fe3e,"block_events") ) )
	{
		m_object_flags |= vEVENTS_BLOCKED;
	}
	else if ( pProps->ContainsFlag( CRCD(0xd5614a50,"unblock_events") ) )
	{
		m_object_flags &= ~vEVENTS_BLOCKED;
	}

	if ( pProps->ContainsFlag( CRCD(0x5b6634d4,"hide") ) )
	{
		m_object_flags |= vHIDDEN;
	}
	else if ( pProps->ContainsFlag( CRCD(0xb60d1f35,"unhide") ) )
	{
		m_object_flags &= ~vHIDDEN;
		m_object_flags |= vCHANGED_STATIC_PROPS;
	}
	
	CObject::SetProperties(pProps);
		
	return;
}


void CScreenElement::WritePropertiesToStruct( Script::CStruct *pStruct )
{
	// alpha value
	pStruct->AddFloat( CRCD(0x2f1fc695,"alpha"), m_local_props.alpha );
	
	// rgba array
	Image::RGBA current_rgba = m_local_props.GetRGBA();
	Script::CArray* p_rgba = new Script::CArray();
	p_rgba->SetSizeAndType( 4, ESYMBOLTYPE_INTEGER );
	p_rgba->SetInteger( 0, current_rgba.r );
	p_rgba->SetInteger( 1, current_rgba.g );
	p_rgba->SetInteger( 2, current_rgba.b );
	p_rgba->SetInteger( 3, current_rgba.a );
	pStruct->AddArray( CRCD(0x3f6bcdba,"rgba"), p_rgba );
	Script::CleanUpArray( p_rgba );
	delete p_rgba;

	// scale value
	float scaleX = m_local_props.GetScaleX();
	float scaleY = m_local_props.GetScaleY();
	// only add a pair if they're different
	if ( scaleX == scaleY )
		pStruct->AddFloat( CRCD(0x13b9da7b,"scale"), scaleX );
	else
		pStruct->AddPair( CRCD(0x13b9da7b,"scale"), scaleX, scaleY );
		
	// position
	pStruct->AddPair( CRCD(0x7f261953,"pos"), m_local_props.GetScreenPosX(), m_local_props.GetScreenPosY() );

	// are the events blocked?
	if ( EventsBlocked() )
		pStruct->AddInteger( CRCD(0x73cb6d65,"events_blocked"), 1 );
	else
		pStruct->AddInteger( CRCD(0x73cb6d65,"events_blocked"), 0 );
}


/*
	Another important entry point to CScreenElement from script. Sets the elements morphable
	(as opposed to static) properties.
	
	Note: a virtual function, so can be extended by subclasses.
	Note: SetMorph() in a subclass should call this function before doing its own logic
*/
void CScreenElement::SetMorph(Script::CStruct *pProps)
{	
	float pos_x, pos_y;
	if (resolve_pos(pProps, &pos_x, &pos_y))
	{
		SetPos(pos_x, pos_y);
	}

	// TODO: temp hack for restoring alpha values
	if ( pProps->ContainsFlag( CRCD(0x5d8bb535,"restore_alpha") ) )
		SetAlpha( m_originalAlpha );
	
	float alpha;
	if ( pProps->GetFloat(CRCD(0x2f1fc695,"alpha"), &alpha) )
	{
		// TODO: temp hack for remembering alpha values		
		if ( pProps->ContainsFlag( CRCD(0xff8df26b,"remember_alpha") ) )
			m_originalAlpha = m_local_props.alpha;
		
		SetAlpha( alpha );
	}

	float scale;
	int relative_scale = 0;
	if ( pProps->ContainsFlag( CRCD(0xe1f4711f,"relative_scale") ) )
		relative_scale = 1;
	if (pProps->GetFloat(CRCD(0x13b9da7b,"scale"), &scale))
	{
		SetScale(scale, scale, relative_scale );
	}
	Script::CPair scale_pair;
	if (pProps->GetPair(CRCD(0x13b9da7b,"scale"), &scale_pair))
	{
		SetScale(scale_pair.mX, scale_pair.mY, relative_scale);
	}

	float desired_time;
	Tmr::Time anim_time;
	if (pProps->GetFloat(CRCD(0x906b67ba,"time"), &desired_time))
	{
		anim_time = (Tmr::Time) (desired_time * (float) Tmr::vRESOLUTION);
	}
	else
		anim_time = 0;
	SetAnimTime(anim_time);

	uint32 anim_type = 0;
	// clear affected bits
	m_object_flags &= ~(vANIM_BIT_MASK << vANIM_FIRST_BIT);
	if (pProps->GetChecksum(CRCD(0x98549ba4,"anim"), &anim_type))
	{
		if (anim_type == CRCD(0xf5b95ce4, "fast_out"))
			m_object_flags |= vANIM_FAST_OUT << vANIM_FIRST_BIT;
		else if (anim_type == CRCD(0x6aa7dd49, "fast_in"))
			m_object_flags |= vANIM_FAST_IN << vANIM_FIRST_BIT;
		else if (anim_type == CRCD(0x768f6843, "gentle"))
			m_object_flags |= vANIM_SLOW_INOUT << vANIM_FIRST_BIT;
	}
	if (!anim_type)
	{
		m_object_flags |= (vANIM_LINEAR << vANIM_FIRST_BIT);
	}

	// rgba values
	Image::RGBA rgba;
	if ( resolve_rgba( pProps, CRCD(0x3f6bcdba,"rgba"), &rgba ) )
	{
		SetRGBA( rgba, DONT_FORCE_INSTANT );
	}

}




/*
	This function must be implemented for access to member commands from script.
*/
bool CScreenElement::CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript )
{
	if (Checksum == CRCD(0x6c63c7c5, "SetProps"))
	{
		SetProperties(pParams);
	}
    else if (Checksum == CRCD(0xdd0c8df3, "DoMorph"))
	{
		float time;
		if (pParams->GetFloat(CRCD(0x906b67ba,"time"), &time))
		{
			Tmr::Time anim_time = (Tmr::Time) (time * (float) Tmr::vRESOLUTION);
			pScript->WaitTime(anim_time);
		}

		SetMorph(pParams);
	}
	else if (Checksum == CRCD(0xc6870028, "Die"))
	{
		CScreenElementManager* p_manager = CScreenElementManager::Instance();		
		
		CScreenElementPtr p_parent = mp_parent;
		p_parent->SetChildLockState(UNLOCK);
		
		// will kill this screen element and all its children
		// THIS IS POTENTIALLY DANGEROUS, LEAVE THIS FUNCTION ASAP
		p_manager->DestroyElement(m_id, CScreenElementManager::RECURSE, CScreenElementManager::DONT_PRESERVE_PARENT, pScript);
		
		p_parent->SetChildLockState(LOCK);
		return true;
	}
	else if ( Checksum == CRCD(0x9492f814, "GetProps" ) )
	{
		WritePropertiesToStruct( pScript->GetParams() );
	}
	else
	{
		return CObject::CallMemberFunction(Checksum, pParams, pScript);
	}

	return true;
}




/*
	Update Procedure:
	
	(Recurse through all elements in tree, starting at root)
	
	For each element:
	1. Update local position, scale, alpha
	2. Update summed position, scale, alpha
	3. Update: (optional)
		-properties of children
		-underlying CSprite or CText object(s)
		-underlying 3D object(s)
		-own base_w, base_h dimensions
	4. Run these steps on all children

	Events may be launched at various points.
*/
void CScreenElement::UpdateProperties()
{
	#ifdef __NOPT_ASSERT__
	debug_verify_integrity();
	#endif
	//Dbg_MsgAssert(m_id && m_id != 0xDEADBEEF, ("this screen element seems to have been deleted"));
	
	Tmr::Time current_time = Tmr::GetTime();
	
	AddReference();
	
	// for convenience -- once element is being used, we can assume it's locked
	SetChildLockState(LOCK);
	
	// only do expensive calculation when change occurs
	bool needs_summed_pos_calc = false;

	// Update 3D position, if any
	if (m_object_flags & CScreenElement::v3D_POS)
	{
		Nx::CViewport *p_viewport = Nx::CViewportManager::sGetActiveViewport();
		Dbg_MsgAssert(p_viewport, ("Can't find an active viewport"));

		float screen_pos_x, screen_pos_y;
		Nx::ZBufferValue screen_pos_z;
		float scale_3d;
		scale_3d = p_viewport->TransformToScreenCoord(m_target_local_props.GetWorldPos(), screen_pos_x, screen_pos_y, screen_pos_z);
		if (scale_3d >= 0.0f)
		{
			m_target_local_props.SetScreenPos(screen_pos_x, screen_pos_y, screen_pos_z);
			SetScale(scale_3d, scale_3d, true);		// set to relative scale
			m_object_flags &= ~CScreenElement::v3D_CULLED;
		} else {
			m_target_local_props.SetScreenPos(-320.0f, -224.0f);
			m_object_flags |= CScreenElement::v3D_CULLED;
		}
		compute_ul_pos(m_target_local_props);

		if (mp_parent)
		{
			Dbg_MsgAssert(!(mp_parent->m_object_flags & CScreenElement::v3D_POS), ("Can't handle having 3D parent"));
		}

		m_object_flags |= vNEEDS_LOCAL_POS_CALC;
	}

	// continue animation of this element
	if ((m_object_flags & vNEEDS_LOCAL_POS_CALC) && !(m_object_flags & vMORPHING_PAUSED2))
	{
		if (m_key_time)
		{
			float pct_changed = (float) (current_time - m_base_time) / (float) m_key_time;
			// if element has reached its new destination, end animation
			if (current_time - m_base_time >= m_key_time)
			{
				pct_changed = 1.0f;
				m_key_time = 0;
			}
	
			float motion_grad;
			int anim_type = ((m_object_flags >> vANIM_FIRST_BIT) & vANIM_BIT_MASK);
			switch (anim_type)
			{
				case vANIM_FAST_OUT:
					motion_grad = 2.0f * pct_changed - pct_changed * pct_changed;
					break;
				case vANIM_FAST_IN:
					motion_grad = pct_changed * pct_changed;
					break;
				case vANIM_SLOW_INOUT:
					motion_grad = (3.0f - 2.0f * pct_changed) * pct_changed * pct_changed;
					break;
				default:
					motion_grad = pct_changed;
					break;
			}
			
			float motion_inc = (motion_grad - m_last_motion_grad) / (1.0f - m_last_motion_grad);
			float pct_changed_inc = (pct_changed - m_last_pct_changed) / (1.0f - m_last_pct_changed);
			m_local_props.SetScreenPos(m_local_props.GetScreenPosX() + (m_target_local_props.GetScreenPosX() - m_local_props.GetScreenPosX()) * motion_inc, 
									   m_local_props.GetScreenPosY() + (m_target_local_props.GetScreenPosY() - m_local_props.GetScreenPosY()) * motion_inc);
			m_local_props.SetScale(m_local_props.GetScaleX() + (m_target_local_props.GetScaleX() - m_local_props.GetScaleX()) * pct_changed_inc,
								   m_local_props.GetScaleY() + (m_target_local_props.GetScaleY() - m_local_props.GetScaleY()) * pct_changed_inc);
			
			m_local_props.SetRotate( m_local_props.GetRotate() + ( m_target_local_props.GetRotate() - m_local_props.GetRotate() ) * pct_changed_inc);
			
			m_local_props.alpha = m_local_props.alpha + (m_target_local_props.alpha - m_local_props.alpha) * pct_changed_inc;
			compute_ul_pos(m_local_props);

			// figure any new rgba values 
			Image::RGBA current_rgba = m_local_props.GetRGBA();
			Image::RGBA target_rgba = m_target_local_props.GetRGBA();
			Image::RGBA new_rgba;
			new_rgba.r = (int)( current_rgba.r + ( target_rgba.r - current_rgba.r ) * pct_changed_inc );
			new_rgba.g = (int)( current_rgba.g + ( target_rgba.g - current_rgba.g ) * pct_changed_inc );
			new_rgba.b = (int)( current_rgba.b + ( target_rgba.b - current_rgba.b ) * pct_changed_inc );
			new_rgba.a = (int)( current_rgba.a + ( target_rgba.a - current_rgba.a ) * pct_changed_inc );
			m_local_props.SetRGBA( new_rgba );
			
			m_last_motion_grad = motion_grad;
			m_last_pct_changed = pct_changed;
		
			/*
			if (m_key_time == 0)
			{
				//m_local_props = m_target_local_props;
				m_local_props.ulx = m_local_props.x - m_local_props.scalex * m_base_w * (m_just_x + 1.0f) / 2.0f;
				m_local_props.uly = m_local_props.y - m_local_props.scaley * m_base_h * (m_just_y + 1.0f) / 2.0f;
				printf("Hurk!! target UL=(%.2f,%.2f) local UL=(%.2f,%.2f)\n", 
					   m_target_local_props.ulx, m_target_local_props.uly,
					   m_local_props.ulx, m_local_props.uly);
			}
			*/
		}
		else
		{
			m_local_props = m_target_local_props;
			m_object_flags &= ~vNEEDS_LOCAL_POS_CALC;
		}
		needs_summed_pos_calc = true;
		
		/*
		if (GetID() == Script::GenerateCRC("test_h_menu"))
			printf("local_pos=(%.2f,%.2f), ul=(%.2f,%.2f), dims=(%.2f,%.2f)\n", 
				   m_local_props.x, m_local_props.y, 
				   m_local_props.ulx, m_local_props.uly, 
				   m_base_w, m_base_h);
		*/
	} // end if

	if (mp_parent && (mp_parent->m_object_flags & vDID_SUMMED_POS_CALC))
		needs_summed_pos_calc = true;
	m_object_flags &= ~vDID_SUMMED_POS_CALC;
	
	if (needs_summed_pos_calc)
	{
		if (mp_parent && !(m_object_flags & CScreenElement::v3D_POS))
		{
			m_summed_props.SetScale(m_local_props.GetScaleX() * mp_parent->m_summed_props.GetScaleX(),
									m_local_props.GetScaleY() * mp_parent->m_summed_props.GetScaleY());
			m_summed_props.SetScreenPos(mp_parent->m_summed_props.GetScreenUpperLeftX() + m_local_props.GetScreenPosX() * mp_parent->m_summed_props.GetScaleX(),
										mp_parent->m_summed_props.GetScreenUpperLeftY() + m_local_props.GetScreenPosY() * mp_parent->m_summed_props.GetScaleY());
			m_summed_props.alpha = m_local_props.alpha * mp_parent->m_summed_props.alpha;
			m_summed_props.SetScreenUpperLeft(mp_parent->m_summed_props.GetScreenUpperLeftX() + m_local_props.GetScreenUpperLeftX() * mp_parent->m_summed_props.GetScaleX(),
											  mp_parent->m_summed_props.GetScreenUpperLeftY() + m_local_props.GetScreenUpperLeftY() * mp_parent->m_summed_props.GetScaleY());
		}
		else
			m_summed_props = m_local_props;
		m_object_flags |= vDID_SUMMED_POS_CALC;
	} // end if

	update();
	
	m_object_flags &= ~vCHANGED_STATIC_PROPS;
	if (m_object_flags & vMORPHING_PAUSED)
		m_object_flags |= vMORPHING_PAUSED2;

	// update all children
	//CScreenElementPtr pChild = mp_child_list;
	CScreenElement * pChild = mp_child_list;
	while(pChild)
	{
		pChild->UpdateProperties();
		pChild = pChild->GetNextSibling();
	}

	RemoveReference();
}




CScreenElementPtr CScreenElement::get_child_by_id_or_index(uint32 id, int *pIndex)
{
	int count = 0;
	CScreenElementPtr pChild = mp_child_list;
	while(pChild)
	{
		// if we are getting by index
		if (id == 0) 
		{
			if (*pIndex == count)
				break;
		}
		// if we are getting by ID
		else if (pChild->GetID() == id) 
		{
			// save index
			if (pIndex)
				*pIndex = count;
			break;
		}

		pChild = pChild->GetNextSibling();
		count++;
	}

	return pChild;
}


CScreenElementPtr CScreenElement::get_parent()
{
	return mp_parent;
}


CWindowElement * CScreenElement::get_window()
{
	CScreenElement * p_parent = get_parent();
	while (p_parent)
	{
		if (p_parent->GetType() == (sint) TYPE_WINDOW_ELEMENT)
		{
			return static_cast(p_parent);
		}

		p_parent = p_parent->get_parent();
	}

	Dbg_MsgAssert(0, ("Could not find window for CScreenElement."));
	return NULL;
}

/*
	Private function, should not be called by anything except
	CScreenElementManager.
*/
void CScreenElement::set_parent(const CScreenElementPtr &pParent, EPosRecalc recalculatePosition)
{
	if (pParent)	
	{
		Dbg_MsgAssert(!(pParent->m_object_flags & CScreenElement::vCHILD_LOCK), ("can't add child %s -- locked parent - %s", Script::FindChecksumName(m_id), Script::FindChecksumName( pParent->GetID())));
		//Ryan("parent of screen element %s is now %s\n", Script::FindChecksumName(GetID()), Script::FindChecksumName(pParent->GetID()));
		#ifdef	__NOPT_ASSERT__
		if (pParent->GetType() == CScreenElement::TYPE_TEXT_BLOCK_ELEMENT)
		{
			Dbg_MsgAssert(GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s parenting child %s that is NOT a CTextElement (it's %s)\n",
			Script::FindChecksumName(pParent->GetID()), Script::FindChecksumName(GetID()), Script::FindChecksumName(GetType())));
		}
		#endif
	}



	if (mp_parent)
	{
		// remove from existing parent's child list before setting new
		// (there are max. 5 references to remove)
		
		
		CScreenElementPtr p_elem = mp_parent->mp_child_list;
		while(p_elem)
		{
			if (p_elem == this)
			{
				if (GetPrevSibling())
				{
					GetPrevSibling()->mp_next_sibling = GetNextSibling();
				}
				else
				{
					// ref count won't change for next sibling, since was ref'd by this
					mp_parent->mp_child_list = GetNextSibling();
				}
				
				if (GetNextSibling())
				{
					// Either original parent or original prev sibling is now pointing to original next sibling
					// Would both add and remove a reference on next, but they cancel each other, so do nothing

					GetNextSibling()->mp_prev_sibling = GetPrevSibling();
				}

				break;
			}
			
			p_elem = p_elem->GetNextSibling();
		}
	}

	// add to new parent's child list
	// (at end of list)
	mp_parent = pParent;
	if (mp_parent) 
	{
		CScreenElementPtr p_prev_sib = mp_parent->GetFirstChild();
		if (p_prev_sib)
		{
			while(p_prev_sib->GetNextSibling())
				p_prev_sib = p_prev_sib->GetNextSibling();
			p_prev_sib->mp_next_sibling = this;
		}
		else
			mp_parent->mp_child_list = this;
		mp_prev_sibling = p_prev_sib;
	}
	else
		// no parent, therefore no siblings
		mp_prev_sibling = NULL;
	mp_next_sibling = NULL;

	if (pParent)
		auto_set_z_priorities_recursive(pParent->m_z_priority + AUTO_Z_SPACE);
	else
		auto_set_z_priorities_recursive(0.0f);
}




/*
	...given element's dimensions, justification, scale, and target position.
*/
void CScreenElement::compute_ul_pos(ConcatProps &props)
{
	props.SetScreenUpperLeft(props.GetScreenPosX() - props.GetScaleX() * m_base_w * (m_just_x + 1.0f) / 2.0f,
							 props.GetScreenPosY() - props.GetScaleY() * m_base_h * (m_just_y + 1.0f) / 2.0f);
}




// called by set_parent()
void CScreenElement::auto_set_z_priorities_recursive(float topPriority)
{
	if (!(m_object_flags & vCUSTOM_Z_PRIORITY))
	{
		m_object_flags |= vCHANGED_STATIC_PROPS;
		m_z_priority = topPriority;
	}
	
	CScreenElementPtr pChild = mp_child_list;
	while(pChild)
	{
		pChild->auto_set_z_priorities_recursive(topPriority + AUTO_Z_SPACE);
		pChild = pChild->GetNextSibling();
	}	
}




/* 
	Resolves position info stored in a script structure:
	
	pos=(x y)
	pos={relative (x y)}
	pos={proportional (x y)}
	
	Returns false if no position info found.
*/
bool CScreenElement::resolve_pos(Script::CStruct *pProps, float *pX, float *pY)
{
	Script::CPair pos_pair;
	Script::CStruct *p_pos_struct;
	if (pProps->GetStructure(CRCD(0x7f261953,"pos"), &p_pos_struct))
	{
		p_pos_struct->GetPair(NONAME, &pos_pair, true);
		if (p_pos_struct->ContainsFlag(0x91a4c826)) // "relative"
		{
			*pX = m_target_local_props.GetScreenPosX() + pos_pair.mX;
			*pY = m_target_local_props.GetScreenPosY() + pos_pair.mY;
		}
		else if (p_pos_struct->ContainsFlag(0xb922906a)) // "proportional"
		{
			Dbg_Assert(mp_parent);
			*pX = pos_pair.mX * mp_parent->GetBaseW();
			*pY = pos_pair.mY * mp_parent->GetBaseH();
		}
		else
			Dbg_MsgAssert(0, ("position needs 'relative' or 'proportional' flag"));
		return true;
	}	
	else if (pProps->GetPair(CRCD(0x7f261953,"pos"), &pos_pair))
	{
		*pX = pos_pair.mX;
		*pY = pos_pair.mY;
		return true;
	}
	
	return false;
}




/* 
	Resolves justification info stored in a script structure, e.g.:
	
	just=[left top]
	just=[center center]
	just=[right bottom]
	just=(-1.0 1.0)
	just=(.65 3.75)
	
	Returns false if no justification info found.
*/
bool CScreenElement::resolve_just(Script::CStruct *pProps, const uint32 RefName, float *pHJust, float *pVJust)
{
	Script::CArray *p_just;
	if (pProps->GetArray(RefName, &p_just))
	{
		Script::ESymbolType type = (Script::ESymbolType) p_just->GetType();
		switch(type)
		{
			case ESYMBOLTYPE_FLOAT:
				*pHJust = p_just->GetFloat(0);
				*pVJust = p_just->GetFloat(1);
				break;
			case ESYMBOLTYPE_NAME:
			{
				float just[2];
				for (int i = 0; i < 2; i++)
				{
					uint32 crc = p_just->GetNameChecksum(i); 
					if (crc == CRCD(0x85981897, "left") || crc == CRCD(0xe126e035, "top"))
						just[i] = -1.0f;
					else if (crc == CRCD(0x4b358aeb, "right") || crc == CRCD(0x76a08d5b, "bottom"))
						just[i] = 1.0f;
					else
						just[i] = 0;
				}
				*pHJust = just[0];
				*pVJust = just[1];
				break;
			}
			default:
				Dbg_MsgAssert(0, ("wrong type for justification"));
				break;
		}
		return true;
	}
	return false;
}




/*
	Retrieves RGBA information from a script struct.
*/
bool CScreenElement::resolve_rgba(Script::CStruct *pProps, const uint32 RefName, Image::RGBA *pRGBA)
{
	Script::CArray *p_color;
	if (pProps->GetArray(RefName, &p_color))
	{
		uint32 rgba = 0;
		int size = p_color->GetSize();
		Dbg_MsgAssert(size >= 3 && size <= 4, ("wrong size %d for color array", size));
		for (int i = 0; i < size; i++) 
		{
			rgba |= (p_color->GetInteger(i) & 255) << (i*8);
		}
#ifdef __PLAT_NGC__
		rgba = ( ( rgba & 0xff000000 ) >> 24 ) | ( ( rgba & 0xff0000 ) >> 8 ) | ( ( rgba & 0xff00 ) << 8 ) | ( ( rgba & 0xff ) << 24 );
#endif		// __PLAT_NGC__
		*pRGBA = *((Image::RGBA *) &rgba);
		return true;
	}
	return false;
}




#ifdef __NOPT_ASSERT__
/*
	Used to verify that screen element has not been corrupted.
*/
void CScreenElement::debug_verify_integrity()
{
	
// Mick - Removed for now, as it's taking up rather a lot of time....	
	return;
	
	Dbg_MsgAssert(Mem::Valid(this), ("not a valid block!"));
	Dbg_MsgAssert(m_id, ("screen element has id of 0, that shouldn't happen"));
	Dbg_MsgAssert(m_id != 0xDEADBEEF, ("screen element has been deleted"));

	CScreenElementManager* p_manager = CScreenElementManager::Instance();
	CScreenElementPtr p_in_tracker = p_manager->GetElement(m_id);
	Dbg_MsgAssert(p_in_tracker == this, ("this element seems to have been lost from tracking"));
	
	if (mp_parent)
	{
		Dbg_MsgAssert(!((uint) mp_parent.Convert() & 3), ("parent has goofy address"));
		Dbg_MsgAssert(Mem::Valid(mp_parent), ("parent not a valid block!"));
		CScreenElementPtr p_sib = mp_parent->mp_child_list;
		while(p_sib)
		{
			if (p_sib == this)
				break;
			p_sib = p_sib->GetNextSibling();
		}
		Dbg_MsgAssert(p_sib, ("this element not in its parent's child list"));
	}

	CScreenElementPtr p_child = mp_child_list;
	while(p_child)
	{
		Dbg_MsgAssert(!((uint) (p_child.Convert()) & 3), ("child has goofy address"));
		Dbg_MsgAssert(Mem::Valid(p_child), ("child not a valid block!"));
		Dbg_MsgAssert(p_child->mp_parent == this, ("this element not parent of child"));
		p_child = p_child->GetNextSibling();
	}
}
#endif




CContainerElement::CContainerElement()
{
	//Ryan("I am a new CContainerElement\n");
	m_focusable_child = 0;

	SetType(CScreenElement::TYPE_CONTAINER_ELEMENT);
}




CContainerElement::~CContainerElement()
{
	//Ryan("Destroying CContainerElement\n");
}




void CContainerElement::SetProperties(Script::CStruct *pProps)
{
	pProps->GetChecksum("focusable_child", &m_focusable_child);
	
	CScreenElement::SetProperties(pProps);
}




bool CContainerElement::PassTargetedEvent(Obj::CEvent *pEvent, bool broadcast)
{
	if (!Obj::CObject::PassTargetedEvent(pEvent))
	{
		return false;
	}
	
	if (pEvent->GetType() == Obj::CEvent::TYPE_FOCUS || pEvent->GetType() == Obj::CEvent::TYPE_UNFOCUS)
	{
		// forward events of these types to specified focusable child
		if (m_focusable_child) 
		{
			int controller = Obj::CEvent::sExtractControllerIndex(pEvent);
			Script::CStruct data;
			data.AddInteger(CRCD(0xb30d9965,"controller"), controller);

			Obj::CTracker* p_tracker = Obj::CTracker::Instance();
			p_tracker->LaunchEvent(pEvent->GetType(), m_focusable_child, Obj::CEvent::vSYSTEM_EVENT, &data);
		}
		pEvent->MarkHandled(m_id);
	}
	return true;
}

bool CScreenElement::EventsBlocked()
{
	// recurse back up through the parents to find any parent that is hidden
	if ( ( m_object_flags & vEVENTS_BLOCKED) || ( mp_parent && mp_parent->EventsBlocked() ) )
		 return true;
	return false;
}

bool CScreenElement::IsHidden()
{
	// recurse back up through the parents to find any parent that is hidden
	if ( ( m_object_flags & vHIDDEN ) || ( mp_parent && mp_parent->IsHidden() ) )
		 return true;
	return false;
}

} // end namespace


================================================
FILE: Code/Gfx/2D/ScreenElement2.h
================================================
#ifndef __GFX_2D_SCREENELEMENT2_H__
#define __GFX_2D_SCREENELEMENT2_H__

#ifndef __GEL_OBJECT_H
#include 
#endif
#include 

#include 
#include 


namespace Front
{

class CScreenElement;
class CWindowElement;
typedef Obj::CSmtPtr CScreenElementPtr;




/*
	The base class for all screen elements. Screen elements include things like onscreen text, HUD sprites,
	items in menus, etc. Every screen element has 2D coordinates and (rectangular) dimensions. Screen elements
	can be "children" of other screen elements, whereby the child uses coordinates relative the upper left corner
	of the parent. If the parent is moved, all its children will be moved along with it.
	
	A screen element also has a pair of scale values, which are independent of its base width and height. A
	screen element will generally have a fixed base width and height, but the scale can be varied for animation
	purposes, making the element appear to grow or shrink in size. Scale changes to an element are also passed to
	children.
	
	The same idea applies to alpha and RGBA. RGBA is a static setting for the screen element, but alpha is a 
	separate morpheable multiplier that can be used for animation.  
*/
class CScreenElement : public Obj::CObject
{
	friend class CScreenElementManager;

public:

	enum EScreenElementType
	{
		TYPE_CONTAINER_ELEMENT						= 0x5b9da842,
		TYPE_TEXT_ELEMENT							= 0x5200dfb6,
		TYPE_VMENU									= 0x130ef802,
		TYPE_HMENU									= 0xccded1e1,
		TYPE_TEXT_BLOCK_ELEMENT						= 0x40d92263,
		TYPE_SPRITE_ELEMENT							= 0xb12b510a,
		TYPE_VSCROLLING_MENU						= 0x84298a3c,
		TYPE_HSCROLLING_MENU						= 0x2900e3d0,
		TYPE_ELEMENT_3D								= 0x82c59d78,
		TYPE_WINDOW_ELEMENT							= 0xa13adbb7,
	};

	enum EPosRecalc
	{
		vDONT_RECALC_POS	= 0,
		vRECALC_POS			= 1,
	};
	
	// flags 8-23 belong to CScreenElement, 24-31 can be used by subclasses
	enum 
	{
		// One of these values will fill bits 8-9
		// in m_object_flags
		vANIM_LINEAR		= 0,
		vANIM_FAST_OUT		= 1,
		vANIM_FAST_IN 		= 2,
		vANIM_SLOW_INOUT	= 3,
		vANIM_FIRST_BIT		= 8,
		vANIM_BIT_MASK		= 3,
		
		/* 
			Should be set whenever one of the following changes:
				-a morpheable property (pos, alpha, scale, etc...)
				-active morph time
				-justification
				-base width, height
				
			UpdateProperties() will respond by calculating current morph frame,
			Once element has finished morph, flag will be turned off.
		*/ 
		vNEEDS_LOCAL_POS_CALC 	= (1<<10),
		/*
			Set by UpdatePropertie
			s() after fresh absolute morph attributes (pos, scale, alpha,
			etc.) are computed. This happens a) after new morph frame calculated for element, or
			b) after new absolute morph attributes computed for parent.
		
			Cleared upon next call to UpdateProperties (if not set again). Can be used by update()
			to decide whether or not to do expensive calculations that are only necessary when
			element moves.
		*/
		vDID_SUMMED_POS_CALC 	= (1<<11),
		/* 
			Should be set when change made to a static property (i.e. a non-morpheable one).
			Virtual update() function can use to decide whether or not to do expensive
			calculation.
			
			Cleared by UpdateProperties() after call to virtual update() function
		*/
		vCHANGED_STATIC_PROPS 	= (1<<12),
		// if set dimensions have been set from outside, don't allow to resize self
		vFORCED_DIMS			= (1<<13),
		// can't add children if this flags is set
		vCHILD_LOCK				= (1<<14),
		// if set, this element (and all its children) will
		vCUSTOM_Z_PRIORITY		= (1<<15),
		vIS_SCREEN_ELEMENT		= (1<<16),
		vMORPHING_PAUSED		= (1<<17),
		vMORPHING_PAUSED2		= (1<<18), // a second stage is necessary to allow one update
		v3D_POS					= (1<<19), // Element is in 3D space
		v3D_CULLED				= (1<<20), // Element in 3D space is culled
		vEVENTS_BLOCKED			= (1<<21), // Element is not accepting any events
		vHIDDEN					= (1<<22), // Element is hiddden
	};
	
	/* 
		These are properties which can be concatenated down the parent-child tree.
		
		There are local properties (relative to parent), and absolute properties
		(concatenation of own local properties with all those of ancestors).
	*/
	struct ConcatProps
	{
	public:
		void				SetScreenPos(float x, float y);
		void				SetScreenPos(float x, float y, Nx::ZBufferValue z);
		void				SetScreenUpperLeft(float x, float y);
		void				SetWorldPos(const Mth::Vector & pos);
		void				SetScale(float scale_x, float scale_y);
		void				SetAbsoluteScale(float scale_x, float scale_y);

		void				SetRotate( float rot );
		void				SetRGBA( Image::RGBA rgba );

		float				GetScreenPosX() const;
		float				GetScreenPosY() const;
		Nx::ZBufferValue	GetScreenPosZ() const;
		float				GetScreenUpperLeftX() const;
		float				GetScreenUpperLeftY() const;
		const Mth::Vector &	GetWorldPos() const;
		float				GetScaleX() const;
		float				GetScaleY() const;
		float				GetAbsoluteScaleX() const;
		float				GetAbsoluteScaleY() const;
		
		float				GetRotate() const;
		Image::RGBA			GetRGBA() const;

		// These values do not need to be protected, since they aren't treated differently in 3D

		// Alpha value
		float				alpha;

	private:
		// 3D position of anchor point
		Mth::Vector			world_pos;

		// 2D position of anchor point
		float				screen_x, screen_y;
		Nx::ZBufferValue	screen_z;				// If using z-buffer sorting

		// position of upper-left hand corner (for quick lookup)
		float				ulx, uly;

		// final render scale
		float				scalex, scaley;
		
		// absolute scale used for calling DoMorph with a relative scale and now for 3D positions
		float				absoluteScalex;
		float				absoluteScaley;

		float				m_rot_angle;
		Image::RGBA			m_rgba;
	};

	static const float vJUST_LEFT;
	static const float vJUST_TOP;
	static const float vJUST_CENTER;
	static const float vJUST_RIGHT;
	static const float vJUST_BOTTOM;

							CScreenElement();
	virtual					~CScreenElement();

	// "absolute" means concatenated
	float					GetAbsX();
	float					GetAbsY();
	float					GetAbsW();
	float					GetAbsH();
	float 					GetBaseW() {return m_base_w;}
	float 					GetBaseH() {return m_base_h;}
	float					GetScaleX() {return m_local_props.GetScaleX();}
	float					GetScaleY() {return m_local_props.GetScaleY();}
	void					GetLocalPos(float *pX, float *pY);
	void					GetLocalULPos(float *pX, float *pY);
	void					GetJust(float *pJustX, float *pJustY);
	
	enum EForceInstant
	{
		DONT_FORCE_INSTANT	= 0,
		FORCE_INSTANT,
	};
	void					SetPos(float x, float y, EForceInstant forceInstant = DONT_FORCE_INSTANT);
	void					SetPos3D(const Mth::Vector &pos3D, EForceInstant forceInstant = DONT_FORCE_INSTANT);
	enum EForceDims
	{
		DONT_FORCE_DIMS		= 0,
		FORCE_DIMS,
	};
	void					SetDims(float w, float h, EForceDims force = DONT_FORCE_DIMS);
	void					SetJust(float justX, float justY);
	void					SetScale(float scaleX, float scaleY, bool relative = false, EForceInstant forceInstant = DONT_FORCE_INSTANT);
	void					SetAlpha(float alpha, EForceInstant forceInstant = DONT_FORCE_INSTANT);
	void					SetAnimTime(Tmr::Time animTime);
	void					SetRGBA( Image::RGBA rgba, EForceInstant forceInstant = FORCE_INSTANT );
	void					SetZPriority(float zPriority);
	void					SetMorphPausedState(bool pause);

	enum ELockState
	{
		UNLOCK,
		LOCK,
	};
	void					SetChildLockState(ELockState lockState);

	// These don't need to return smart pointers	
	CScreenElement*		GetFirstChild() {return mp_child_list;}
	CScreenElement*		GetLastChild();
	CScreenElement*		GetPrevSibling() {return mp_prev_sibling;}
	CScreenElement*		GetNextSibling() {return mp_next_sibling;}
	CScreenElement*		GetChildById(uint32 id, int *pIndex = NULL);
	CScreenElement*		GetChildByIndex(int index);
	
	int						CountChildren();
	
	// See notes before function definition
	void					UpdateProperties();
  	//void					Draw();

	/* 
		Versions of SetProperties(), SetMorph(), CallMemberFunction() in subclasses 
		should call versions in this class.
	*/
	virtual void			SetProperties(Script::CStruct *pProps);
	virtual void			WritePropertiesToStruct( Script::CStruct *pStruct );
	virtual void			SetMorph(Script::CStruct *pProps);
	virtual bool 			CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript );

	bool					EventsBlocked();
	bool					IsHidden();

protected:

	CScreenElementPtr		get_child_by_id_or_index(uint32 id, int *pIndex);
	CScreenElementPtr		get_parent();
	CWindowElement *		get_window();
	void					set_parent(const CScreenElementPtr &pParent, EPosRecalc recalculatePosition = vRECALC_POS);
	void					compute_ul_pos(ConcatProps &props);
	void					auto_set_z_priorities_recursive(float topPriority);
	
	bool					resolve_pos(Script::CStruct *pProps, float *pX, float *pY);
	bool					resolve_just(Script::CStruct *pProps, const uint32 RefName, float *pHJust, float *pVJust);
	bool					resolve_rgba(Script::CStruct *pProps, const uint32 RefName, Image::RGBA *pRGBA);
	
public:	
	#ifdef __NOPT_ASSERT__
	// a debugging function
	void					debug_verify_integrity();
	#endif
protected:
	
	/* 
		See notes on UpdateProperties() about what this function can do.
		
		What it *CAN'T* do:
		-use the summed pos values from children (these will be a frame out of date)
		-changed the properties of the parent (the changes won't be converted to
			local and summed positions until the next frame)
	*/
	virtual void			update() {;}
	
	CScreenElementPtr		mp_parent;
	CScreenElementPtr		mp_child_list;
	CScreenElementPtr		mp_next_sibling;
	CScreenElementPtr		mp_prev_sibling;
	
	ConcatProps				m_local_props;
	ConcatProps				m_target_local_props;

	Tmr::Time				m_base_time;
	// how much time must elapse since m_base_time for animation to end
	// set to 0 when animation complete
	Tmr::Time				m_key_time;
	float					m_last_motion_grad;
	float					m_last_pct_changed;
	
	// the concatenation of local props in ancestors and this
	ConcatProps				m_summed_props;

	// determines offset of upper-left corner of screen element
	// relative	to anchor point
	// -1.0	= left/top
	//	0	= center
	//  1.0	= bottom/right
	float					m_just_x, m_just_y;
	
	// the dimensions of this screen element before scaling is applied
	// (in pixels) (local)
	float					m_base_w, m_base_h;

	Image::RGBA				m_rgba;

	static const float		AUTO_Z_SPACE;
	
	float					m_z_priority;

	// TODO: temp hack to get preserve_original_alpha and restore_original_alpha working
	float					m_originalAlpha;
};




class CContainerElement : public CScreenElement
{
	friend class CScreenElementManager;

public:
							CContainerElement();
	virtual					~CContainerElement();

	void					SetProperties(Script::CStruct *pProps);
	virtual bool			PassTargetedEvent(Obj::CEvent *pEvent, bool broadcast = false);
	
protected:

	uint32					m_focusable_child;
};


inline void					CScreenElement::ConcatProps::SetScreenPos(float x, float y)
{
	screen_x = x;
	screen_y = y;
}

inline void					CScreenElement::ConcatProps::SetScreenPos(float x, float y, Nx::ZBufferValue z)
{
	SetScreenPos(x, y);
	screen_z = z;
}

inline void					CScreenElement::ConcatProps::SetScreenUpperLeft(float x, float y)
{
	ulx = x;
	uly = y;
}

inline void					CScreenElement::ConcatProps::SetWorldPos(const Mth::Vector & pos)
{
	world_pos = pos;
}

inline void					CScreenElement::ConcatProps::SetScale(float scale_x, float scale_y)
{
	scalex = scale_x;
	scaley = scale_y;
}

inline void					CScreenElement::ConcatProps::SetAbsoluteScale(float scale_x, float scale_y)
{
	absoluteScalex = scale_x;
	absoluteScaley = scale_y;
}

inline void					CScreenElement::ConcatProps::SetRotate( float rot_angle )
{
	m_rot_angle = rot_angle;
}

inline void					CScreenElement::ConcatProps::SetRGBA( Image::RGBA rgba )
{
	m_rgba.r = rgba.r;
	m_rgba.g = rgba.g;
	m_rgba.b = rgba.b;
	m_rgba.a = rgba.a;
}

inline float				CScreenElement::ConcatProps::GetScreenPosX() const
{
	return screen_x;
}

inline float				CScreenElement::ConcatProps::GetScreenPosY() const
{
	return screen_y;
}

inline Nx::ZBufferValue		CScreenElement::ConcatProps::GetScreenPosZ() const
{
	return screen_z;
}

inline float				CScreenElement::ConcatProps::GetScreenUpperLeftX() const
{
	return ulx;
}

inline float				CScreenElement::ConcatProps::GetScreenUpperLeftY() const
{
	return uly;
}

inline const Mth::Vector &	CScreenElement::ConcatProps::GetWorldPos() const
{
	return world_pos;
}

inline float				CScreenElement::ConcatProps::GetScaleX() const
{
	return scalex;
}

inline float				CScreenElement::ConcatProps::GetScaleY() const
{
	return scaley;
}

inline float				CScreenElement::ConcatProps::GetAbsoluteScaleX() const
{
	return absoluteScalex;
}

inline float				CScreenElement::ConcatProps::GetAbsoluteScaleY() const
{
	return absoluteScaley;
}

inline float				CScreenElement::ConcatProps::GetRotate() const
{
	return m_rot_angle;
}

inline Image::RGBA			CScreenElement::ConcatProps::GetRGBA() const
{
	return m_rgba;
}



} // end namespace

#endif


================================================
FILE: Code/Gfx/2D/ScrollingMenu.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Front
{




CBaseScrollingMenu::CBaseScrollingMenu()
{
	RegisterWithTracker(this);
	mp_the_menu = NULL;
	m_selected_element_id = 0;

	m_in_focus = false;
	m_needs_update = false;

	m_top_or_left_line = 0.0f;
	// a very large number
	m_scroll_speed = 10000.0f;

	m_num_items_to_display = -1;
	m_window_dimension = 0.0f;
	m_regular_space_amount = -1.0f;
	
	SetType(CScreenElement::TYPE_VSCROLLING_MENU);
}




CBaseScrollingMenu::~CBaseScrollingMenu()
{
}




void CBaseScrollingMenu::SetProperties(Script::CStruct *pProps)
{
	float just[2] = { 0.0f, 0.0f };
	if (resolve_just(pProps, CRCD(0x67e093e4,"internal_just"), just, just+1))
		SetInternalJust(just[0], just[1]);
	
	int scroll_speed;
	if (pProps->GetInteger(CRCD(0x9020a1d1,"scroll_speed"), &scroll_speed))
		m_scroll_speed = (float) scroll_speed;
	
	pProps->GetInteger(CRCD(0xc855511c,"num_items_to_show"), &m_num_items_to_display);

	if (pProps->ContainsFlag(CRCD(0xed889e1f,"reset_window_top")))
	{
		m_top_or_left_line = 0;
		m_needs_update = true;
	}
	else if (pProps->ContainsFlag(CRCD(0xc3a894f6,"reset_window_bottom")))
	{
		CScreenElementPtr p_child = mp_the_menu->GetLastChild();
		float element_x, element_z, element_w, element_l;
		compute_element_area(p_child, element_x, element_z, element_w, element_l);
		m_top_or_left_line = element_z + element_l - m_window_dimension;
		m_needs_update = true;
	}
	else if ( pProps->ContainsFlag( CRCD(0xa1f3b8bb,"reset_window") ) )
	{
		m_needs_update = true;
	}
	
	CScreenElement::SetProperties(pProps);
}




bool CBaseScrollingMenu::PassTargetedEvent(Obj::CEvent *pEvent, bool broadcast)
{
	
	if (!Obj::CObject::PassTargetedEvent(pEvent))
	{
		return false;
	}
	
	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
    
    if (pEvent->GetType() == Obj::CEvent::TYPE_NOTIFY_CHILD_LOCK)
    {
        // we can assume that VMenu child is in place now, find it
		get_the_menu();
		m_needs_update = true;
		update();

		pEvent->MarkHandled(m_id);
    }
	if (pEvent->GetType() == Obj::CEvent::TYPE_FOCUS)
	{
		if (!mp_the_menu)
			get_the_menu();
		
		// event is for this scrolling menu			
		if (!m_in_focus)
		{
			//printf("VMenu %s receives focus event, sending to 0x%x\n", Script::FindChecksumName(m_id), m_selected_id);
			m_in_focus = true;
			p_tracker->LaunchEvent(Obj::CEvent::TYPE_FOCUS, mp_the_menu->GetID(), Obj::CEvent::vSYSTEM_EVENT, pEvent->GetData());		
			pEvent->MarkHandled(m_id);
		}
	}
	if (pEvent->GetType() == Obj::CEvent::TYPE_UNFOCUS && m_in_focus)
	{
		get_the_menu();
		
		//printf("VMenu %s receives focus event, sending to 0x%x\n", Script::FindChecksumName(m_id), m_selected_id);
		m_in_focus = false;
		p_tracker->LaunchEvent(Obj::CEvent::TYPE_UNFOCUS, mp_the_menu->GetID());		
		pEvent->MarkHandled(m_id);
	}
	return true;
}




void CBaseScrollingMenu::pass_event_to_listener(Obj::CEvent *pEvent)
{
	//printf("VMenu receives event of type %s\n", Script::FindChecksumName(pEvent->GetType()));
	
    if (pEvent->GetType() == Obj::CEvent::TYPE_NOTIFY_CHILD_LOCK && mp_the_menu)
    {
        if (pEvent->GetTarget() == mp_the_menu->GetID())
		{
			m_needs_update = true;
			update();
		}
		
		//Obj::CTracker* p_tracker = Obj::CTracker::Instance();
		//p_tracker->PrintEventLog(true, 100);
		
		pEvent->MarkRead(m_id);
    }
	if (pEvent->GetType() == Obj::CEvent::TYPE_FOCUS)
	{
		if (!mp_the_menu)
			get_the_menu();
		
		// see if focus event is for one of contained menu's children
		CScreenElementPtr p_child = mp_the_menu->GetFirstChild();
		while(p_child)
		{
			if (p_child->GetID() == pEvent->GetTarget())
			{
				put_element_in_view(p_child);
				pEvent->MarkRead(m_id);
				break;
			}
			p_child = p_child->GetNextSibling();
		}
	}
}




void CBaseScrollingMenu::get_the_menu()
{
	CScreenElementPtr p_child = GetFirstChild();
	while(p_child)
	{
		if ((uint32) p_child->GetType() == CRCX("vmenu") && m_is_vertical)
			mp_the_menu = (CBaseMenu *) p_child.Convert();
		else if ((uint32) p_child->GetType() == CRCX("hmenu") && !m_is_vertical)
			mp_the_menu = (CBaseMenu *) p_child.Convert();
		p_child = p_child->GetNextSibling();
	}
	
	Dbg_MsgAssert(mp_the_menu || !m_is_vertical, ("could not find VMenu"));
	Dbg_MsgAssert(mp_the_menu || m_is_vertical, ("could not find HMenu"));

	float space_amount = -1.0f;
	if (mp_the_menu->UsingRegularSpacing(space_amount) && m_num_items_to_display >= 0)
	{
		m_window_dimension = space_amount * m_num_items_to_display;
		m_regular_space_amount = space_amount;
	}
	else
	{
		m_num_items_to_display = -1;
		m_window_dimension = (m_is_vertical) ? m_base_h : m_base_w;
	}
}




void CBaseScrollingMenu::put_element_in_view(const CScreenElementPtr &pElement)
{
	Dbg_Assert(mp_the_menu);
	m_selected_element_id = pElement->GetID();
	m_needs_update = true;
}




void CBaseScrollingMenu::update()
{
	CScreenElementPtr pElement = NULL;
	int element_index = 0;
	
	float center_menu_offset_up = 0.0f;
	float center_menu_offset_down = 0.0f;
	
	if ( mp_the_menu )
	{
		if ( m_selected_element_id )
			pElement = mp_the_menu->GetChildById(m_selected_element_id, &element_index);
		else
			pElement = mp_the_menu->GetChildByIndex( 0 );
	
		if ( pElement )
		{
			int num_children = mp_the_menu->CountChildren();
	
			// Good doggy. Now find out if the selection needs to be centered in the window
			if (m_regular_space_amount >= 0.0f)
			{
				if (element_index >= (m_num_items_to_display / 2))
				{
					center_menu_offset_up = m_regular_space_amount * ((m_num_items_to_display - 1) / 2);
				}
				if (element_index < num_children - (m_num_items_to_display / 2))
				{
					center_menu_offset_down = m_regular_space_amount * (m_num_items_to_display / 2);
				}
			}
		}
	}

	if (pElement)
	{	
		float menu_x, menu_y;
		mp_the_menu->SetJust(-1.0f, -1.0f);
		mp_the_menu->GetLocalPos(&menu_x, &menu_y);

		// get upper-left of element, local coordinates (in relation to VMenu)
		float element_x, element_y, element_w, element_h;
		float element_z, element_l;
		if (m_is_vertical)
		{
			compute_element_area(pElement, element_x, element_z, element_w, element_l);
		}
		else
		{
			compute_element_area(pElement, element_z, element_y, element_l, element_h);
		}
		if (m_regular_space_amount >= 0.0f) 
		{
			element_z = m_regular_space_amount * element_index;
			element_l = m_regular_space_amount;
		}
		
		// if element top is above window top
		if (element_z < m_top_or_left_line + center_menu_offset_up)
		{
			// scroll up
			m_top_or_left_line -= m_scroll_speed;
			if (m_top_or_left_line < element_z)
				m_top_or_left_line = element_z - center_menu_offset_up;
			m_needs_update = true;
		}
		// if element bottom is below window bottom
		else if (element_z + element_l > m_top_or_left_line + m_window_dimension - center_menu_offset_down)
		{
			// scroll down
			m_top_or_left_line += m_scroll_speed;
			if (m_top_or_left_line + m_window_dimension > element_z + element_l)
				m_top_or_left_line = element_z + element_l - m_window_dimension + center_menu_offset_down;
			m_needs_update = true;
		}
			
		if (m_needs_update)
		{
			if (m_is_vertical)
				mp_the_menu->SetPos(menu_x, -m_top_or_left_line, FORCE_INSTANT);
			else
				mp_the_menu->SetPos(-m_top_or_left_line, menu_y, FORCE_INSTANT);
		
			//printf("m_top_or_left_line is %.2f\n", m_top_or_left_line);
			
			// make elements visible or invisible
			element_index = 0;
			CScreenElementPtr p_child = mp_the_menu->GetFirstChild();
			while(p_child)
			{
				if (m_is_vertical)
				{
					compute_element_area(p_child, element_x, element_z, element_w, element_l);
				}
				else
				{
					compute_element_area(p_child, element_z, element_y, element_l, element_h);
				}
				if (m_regular_space_amount >= 0.0f) 
				{
					element_z = m_regular_space_amount * element_index;
					element_l = m_regular_space_amount;
					//Ryan("oog\n");
				}
				
				const float TOLERANCE = 0.0f;
				if (element_z >= m_top_or_left_line - TOLERANCE && element_z + element_l <= m_top_or_left_line + m_window_dimension + TOLERANCE)
				{
					// top is (mostly) in window, bottom is (mostly) in window
					p_child->SetAlpha(1.0f, FORCE_INSTANT);
				}
				else
				{
					p_child->SetAlpha(0.0f, FORCE_INSTANT);
				}
		
				p_child = p_child->GetNextSibling();
				element_index++;
			}
			m_needs_update = false;
		}
	} // end if pElement
}




// finds area of element in coordinates relative to this scrolling menu
void CBaseScrollingMenu::compute_element_area(const CScreenElementPtr &pElement, float &elementX, float &elementY, float &elementW, float &elementH)
{
	pElement->GetLocalPos(&elementX, &elementY);
	elementX *= mp_the_menu->GetScaleX();
	elementY *= mp_the_menu->GetScaleY();
	elementW = pElement->GetBaseW() * pElement->GetScaleX() * mp_the_menu->GetScaleX();
	elementH = pElement->GetBaseH() * pElement->GetScaleY() * mp_the_menu->GetScaleY();
	float element_just_x, element_just_y;
	pElement->GetJust(&element_just_x, &element_just_y);
	elementX -= (element_just_x + 1.0f) * elementW / 2.0f;
	elementY -= (element_just_y + 1.0f) * elementH / 2.0f;
}




CVScrollingMenu::CVScrollingMenu()
{
	m_is_vertical = true;
}




CVScrollingMenu::~CVScrollingMenu()
{
}




CHScrollingMenu::CHScrollingMenu()
{
	m_is_vertical = false;
}




CHScrollingMenu::~CHScrollingMenu()
{
}




}


================================================
FILE: Code/Gfx/2D/ScrollingMenu.h
================================================
#ifndef __GFX_2D_SCROLLINGMENU_H__
#define __GFX_2D_SCROLLINGMENU_H__

#include 

namespace Front
{

class CBaseMenu;
	
class CBaseScrollingMenu : public Front::CScreenElement, public Obj::CEventListener
{
public:

							CBaseScrollingMenu();
	virtual					~CBaseScrollingMenu();

	void					SetInternalJust(float h, float v) {m_internal_just_x = h; m_internal_just_y = v;}	
	
	virtual void			SetProperties(Script::CStruct *pProps);
	virtual bool			PassTargetedEvent(Obj::CEvent *pEvent, bool broadcast = false);

protected:

	void					pass_event_to_listener(Obj::CEvent *pEvent);
	virtual void 			update();

	void					get_the_menu();
	void					put_element_in_view(const CScreenElementPtr &pElement);
    	
	void					compute_element_area(const CScreenElementPtr &pElement, float &elementX, float &elementY, float &elementW, float &elementH);
	
	CBaseMenu *				mp_the_menu;
	uint32					m_selected_element_id;
	
	float					m_internal_just_x, m_internal_just_y;

	bool					m_in_focus;
	bool					m_needs_update;

	float					m_top_or_left_line;
	float					m_scroll_speed;

	// set to negative if we aren't limiting
	int						m_num_items_to_display;
	float					m_window_dimension;
	// set to negative if not used
	float					m_regular_space_amount;

	bool					m_is_vertical;
};




class CVScrollingMenu : public CBaseScrollingMenu
{
public:
							CVScrollingMenu();
	virtual					~CVScrollingMenu();
};




class CHScrollingMenu : public CBaseScrollingMenu
{
public:
							CHScrollingMenu();
	virtual					~CHScrollingMenu();
};




};

#endif


================================================
FILE: Code/Gfx/2D/SpriteElement.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Front
{




CSpriteElement::CSpriteElement()
{
	//m_rgba = 0x40909090;
	m_texture = 0;
	mp_sprite = Nx::CEngine::sCreateSprite(NULL);
	
	SetType(CScreenElement::TYPE_SPRITE_ELEMENT);
}




CSpriteElement::~CSpriteElement()
{
	if (mp_sprite)
	{
		Nx::CEngine::sDestroySprite(mp_sprite);
	}
	//Ryan("Destroying CSpriteElement\n");
}




void CSpriteElement::SetProperties(Script::CStruct *pProps)
{
	CScreenElement::SetProperties(pProps);

	uint32 texture_crc;
	if (pProps->GetChecksum("texture", &texture_crc))
		SetTexture(texture_crc);
	float rot_angle;
	if ( pProps->GetFloat( "rot_angle", &rot_angle ) )
		SetRotate( rot_angle );
	// Dbg_MsgAssert(m_texture, ("no texture loaded"));
}

void CSpriteElement::SetMorph( Script::CStruct *pProps )
{
	CScreenElement::SetMorph( pProps );

	float rot_angle;
	if ( pProps->GetFloat( "rot_angle", &rot_angle ) )
		SetRotate( rot_angle, DONT_FORCE_INSTANT );
}


void CSpriteElement::SetTexture(uint32 texture_checksum)
{
	Nx::CTexture *p_texture;

	m_texture = texture_checksum;

	p_texture = Nx::CTexDictManager::sp_sprite_tex_dict->GetTexture(texture_checksum);
	Dbg_MsgAssert(p_texture, ("no texture found for sprite 0x%x %s on element 0x%x %s",
					texture_checksum,Script::FindChecksumName(texture_checksum),
					m_id,Script::FindChecksumName(m_id)));
	mp_sprite->SetTexture(p_texture);
	
	Dbg_MsgAssert(!(m_object_flags & vFORCED_DIMS), ("Trying to override sprite texture size"));
	SetDims(p_texture->GetWidth(), p_texture->GetHeight());

	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;	
}



void CSpriteElement::SetRotate( float angle, EForceInstant forceInstant )
{
	float rot = angle * Mth::PI / 180.0f;
	rot += 0.001f;
	Dbg_Assert( mp_sprite );

	if ( m_target_local_props.GetRotate() != rot )
	{
		m_target_local_props.SetRotate( rot );
		m_object_flags |= vNEEDS_LOCAL_POS_CALC;
	}
	if ( forceInstant )
	{
		if ( m_local_props.GetRotate() != rot )
		{
			// on the next update, this element will arrive at the target alpha
			mp_sprite->SetRotation( rot );
			m_local_props.SetRotate( rot );
			m_object_flags |= vNEEDS_LOCAL_POS_CALC;
		}
	}
}


void CSpriteElement::update()
{
	// HACK
	bool offscreen = false;
	if (m_summed_props.GetScreenUpperLeftY() < -200.0f || m_summed_props.GetScreenUpperLeftY() > 648.0f)
		offscreen = true;
	
	// IsHidden can be relatively slow (it's recursive), so only call once
	bool hidden = IsHidden();

	mp_sprite->SetHidden( hidden );

	// only change state of underlying sprite if there's been a change to CScreenElement/CSpriteElement visual state
	if ( ( m_object_flags & CScreenElement::vDID_SUMMED_POS_CALC ) || ( m_object_flags & CScreenElement::vCHANGED_STATIC_PROPS ) )
	{
		Image::RGBA true_rgba = m_local_props.GetRGBA();
		if (m_summed_props.alpha >= .0001f)
			true_rgba.a = (uint8) ((float) m_local_props.GetRGBA().a * m_summed_props.alpha);
		else
			true_rgba.a = 0;
		
		// GARRETT: 
		// Do all drawing here
		//
		// -screen coordinates of upper-left of sprite: m_summed_props.ulx, m_summed_props.uly
		// -scale to apply: m_summed_props.scale
		// -screen W,H of sprite (if needed): m_summed_props.scale * m_base_w, m_summed_props.scale * m_base_h
		//mp_sprite->SetPos(m_summed_props.ulx, m_summed_props.uly);
		mp_sprite->SetPos(m_summed_props.GetScreenPosX(), m_summed_props.GetScreenPosY());
		mp_sprite->SetSize(m_base_w, m_base_h);
		mp_sprite->SetAnchor(m_just_x, m_just_y);
		mp_sprite->SetScale(m_summed_props.GetScaleX(), m_summed_props.GetScaleY());
		
		if ( m_object_flags & CScreenElement::v3D_POS )
		{
			mp_sprite->SetZValue(m_summed_props.GetScreenPosZ());
			mp_sprite->SetHidden( ( ( m_object_flags & CScreenElement::v3D_CULLED ) || hidden ) );
		}
		else
		{
			mp_sprite->SetPriority( m_z_priority );
			mp_sprite->SetHidden( offscreen || hidden );
		}
		
		mp_sprite->SetRGBA(true_rgba);
		// mp_sprite->SetHidden( ( offscreen || hidden ) );
	

#if 0  // Garrett: Can't find window
		// Update the clip window.  This should only be done at init time, instead.
		CWindowElement *p_window = get_window();
		mp_sprite->SetWindow(p_window->GetClipWindow());
#endif

		// update the rotation angle
		mp_sprite->SetRotation( m_local_props.GetRotate() );
		// mp_sprite->SetRotation(mp_sprite->GetRotation() + 0.001f);

	}
}

void CSpriteElement::WritePropertiesToStruct( Script::CStruct* pStruct )
{
	CScreenElement::WritePropertiesToStruct( pStruct );

	// rotation angle
	pStruct->AddFloat( "rot_angle", m_local_props.GetRotate() );

	// texture name
	pStruct->AddChecksum( "texture", this->m_texture );
}

}


================================================
FILE: Code/Gfx/2D/SpriteElement.h
================================================
#ifndef __GFX_2D_SPRITEELEMENT_H__
#define __GFX_2D_SPRITEELEMENT_H__

#ifndef __GEL_OBJECT_H
#include 
#endif
#include 

namespace Nx
{
	class CSprite;
}

namespace Front
{

class CSpriteElement : public CScreenElement
{
	friend class CScreenElementManager;

public:
							CSpriteElement();
	virtual					~CSpriteElement();

	void					SetProperties(Script::CStruct *pProps);
	void					SetMorph(Script::CStruct *pProps);
	
	void 					SetTexture(uint32 texture_checksum);
	void					SetRotate( float angle, EForceInstant forceInstant = FORCE_INSTANT );

	void					WritePropertiesToStruct( Script::CStruct *pStruct );
	
protected:

	void					update();

	uint32					m_texture;

	Nx::CSprite *			mp_sprite;
};


}

#endif


================================================
FILE: Code/Gfx/2D/TextElement.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BLUR_EFFECT_ON 1

namespace Front
{




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextElement::CTextElement() :
	m_shadow_rgba(0, 0, 0, 128)
{
	//Ryan("I am a new CTextElement\n");
	m_font_checksum = 0;
	mp_font = NULL;
	mp_text = NULL;
	mp_blur_effect = NULL;
	m_use_shadow = false;
	create_text_instances(1);

	m_override_encoded_rgba = false;
	m_previous_override_rgba_state = false;
	
	SetType(CScreenElement::TYPE_TEXT_ELEMENT);
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextElement::~CTextElement()
{
	//Ryan("Destroying CTextElement\n");
	DetachBlurEffect();
	destroy_text_instances();
	if (mp_text)
		delete mp_text;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetProperties(Script::CStruct *pProps)
{
	CScreenElement::SetProperties(pProps);

	uint32 font_crc;
	if (pProps->GetChecksum(CRCD(0x2f6bf72d,"font"), &font_crc))
		SetFont(font_crc);
	Dbg_MsgAssert(m_font_checksum, ("no font loaded"));
	
	const char *p_text;
	if (pProps->GetText(CRCD(0xc4745838,"text"), &p_text))
		SetText(p_text);

	Dbg_MsgAssert(m_id != Obj::CBaseManager::vNO_OBJECT_ID, ("what, no ID"));

	if (pProps->ContainsFlag(CRCD(0xd1653c6c,"blur_effect")))
		AttachBlurEffect();
	if (pProps->ContainsFlag(CRCD(0x4b73e4f8,"no_blur_effect")))
		DetachBlurEffect();

	Image::RGBA blur_rgba;
	if (resolve_rgba(pProps, CRCD(0x4f0e1182,"blur_rgba"), &blur_rgba) && mp_blur_effect)
		mp_blur_effect->SetRGBA(blur_rgba);

	Script::CPair shadow_offs;
	if (pProps->GetPair(CRCD(0x2a1fe0cc,"shadow_offs"), &shadow_offs))
	{
		SetShadowOff(shadow_offs.mX, shadow_offs.mY);
	}
	
	if (pProps->ContainsFlag(CRCD(0x8a897dd2,"shadow")))
		SetShadowState(true);
	if (pProps->ContainsFlag(CRCD(0x95b5b9a0,"no_shadow")))
		SetShadowState(false);
	
	resolve_rgba(pProps, CRCD(0x1e7bb1f5,"shadow_rgba"), &m_shadow_rgba);
	
	if ( pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ) )
	{
		Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
		m_previous_override_rgba_state = m_override_encoded_rgba;
	}
	if ( pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ) )
	{
		Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
		m_override_encoded_rgba = m_previous_override_rgba_state;
	}
	
	if (pProps->ContainsFlag(CRCD(0xb7686f92,"override_encoded_rgba")))
		m_override_encoded_rgba = true;
	if (pProps->ContainsFlag(CRCD(0xd25bc6cd,"dont_override_encoded_rgba")))
		m_override_encoded_rgba = false;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetMorph(Script::CStruct *pProps)
{	
	if (mp_blur_effect)
	{
		mp_blur_effect->SetMorph(pProps);
	}

	CScreenElement::SetMorph(pProps);
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetFont(uint32 font_checksum)
{
	m_font_checksum = font_checksum;
	mp_font = Nx::CFontManager::sGetFont(m_font_checksum);
	Dbg_MsgAssert(Nx::CFontManager::sTestFontLoaded(m_font_checksum), ("font %s isn't loaded", Script::FindChecksumName(m_font_checksum)));
	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;	
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetText(const char *pText)
{
	int new_length = strlen(pText) + 1;
	Dbg_MsgAssert(new_length < vMAX_TEXT_LENGTH, ("string too long %d", new_length));

	// if the old string is not big enough, then we need to allocate a new one	
	// otherwize, overwrite it, as allocations are slow
	if (!mp_text || (int)(strlen(mp_text)) < new_length-1)		  // note, does not account for actual allocated size, but will work is string are same size
	{
		if (mp_text)
			delete mp_text;	
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mp_text = new char[new_length];
		Mem::Manager::sHandle().PopContext();
	}
	// copy pText to mp_text, replacing '\m' tags with '\b' tags
	const char *p_in = pText;
	char *p_out = mp_text;
	while(*p_in)
	{
		if (*p_in == '\\' && *(p_in+1) == '\\')
		{
			// output "\\" tag unchanged
			*p_out++ = *p_in++;
			*p_out++ = *p_in++;
		}
		// ignore sticky space
		else if (*p_in == '\\' && *(p_in+1) == '_')
		{
			p_in++;
			p_in++;
			*p_out++ = ' ';
		}
		else if (*p_in == '\\' && *(p_in+1) == 'm')
		{
			*p_out++ = *p_in++;
			*p_out++ = 'b';
			p_in++;
			*p_out++ = Nx::CFontManager::sMapMetaCharacterToButton(p_in++);
		}
		else
		{
			*p_out++ = *p_in++;
		}
	}
	*p_out = '\0';
	
	if( mp_font )
	{
		float w, h;
		mp_font->QueryString(mp_text, w, h);	// width and height in pixels

		if (!(m_object_flags & vFORCED_DIMS)) 
		{
			SetDims(w, h);
		}
	}

	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;	
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CTextElement::GetLength()
{
	return strlen( mp_text );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTextElement::Concatenate( const char *pText )
{
	int old_length = 0;
	if ( mp_text )
		old_length += strlen( mp_text );
	int length  = old_length + strlen( pText );
	
	if ( length + 1 >= vMAX_TEXT_LENGTH )
	{
		// new string would be too long...
		return false;
	}

	if ( length > 0 )
	{
		char new_string[vMAX_TEXT_LENGTH];
		
		// initialize string
		strcpy( new_string, "" );
		if ( mp_text )
			strcpy( new_string, mp_text );

		// add new text
		strcat( new_string, pText );
	
		SetText( new_string );
		return true;
	}
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTextElement::Backspace()
{
	if ( !mp_text )
		return false;
	
	int length = strlen( mp_text );

	if ( length == 0 )
		return false;

	// copy string
	char new_string[vMAX_TEXT_LENGTH];
	strcpy( new_string, mp_text );
	
	// take care of escaped backslash char (ASCII code 92)
	if ( length > 1 && new_string[length - 1] == 92 && new_string[length - 2] == 92 )
		new_string[length - 2] = '\0';
	
	// terminate
	new_string[length - 1] = '\0';
	SetText( new_string );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::AttachBlurEffect()
{
	#if BLUR_EFFECT_ON
	if (!mp_blur_effect)
	{
		destroy_text_instances();
		
		// create blur effect according to specs
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mp_blur_effect = new CBlurEffect();
		// mp_blur_effect->SetRGBA(m_rgba);
		mp_blur_effect->SetRGBA( m_local_props.GetRGBA() );
		Mem::Manager::sHandle().PopContext();
	
		// get number of CText objects needed
		// create 'em
		create_text_instances(mp_blur_effect->GetNumEntries() + 1);
	}
	#endif
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::DetachBlurEffect()
{
	if (mp_blur_effect)
	{
		destroy_text_instances();
		delete mp_blur_effect;
		mp_blur_effect = NULL;
		create_text_instances(1);
	}
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetShadowState(bool shadowOn)
{
	if (shadowOn && !m_use_shadow)
	{
		m_use_shadow = true;
		create_text_instances(m_num_tab_entries, true);
	}
	else if (!shadowOn && m_use_shadow)
	{
		m_use_shadow = false;
		destroy_text_instances(true);
	}
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::SetShadowOff(float offX, float offY)
{
	m_shadow_off_x = offX;
	m_shadow_off_y = offY;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::update()
{
	//Dbg_Message("I am drawing text element at (%.1f,%.1f), scale %.1f\n", m_summed_props.ulx, m_summed_props.uly, m_summed_props.scalex);
#if 0
	if( mp_font )
	{
		mp_font->BeginText( *((uint32 *) &m_local_props.GetRGBA() ), m_summed_props.scalex);
		mp_font->DrawString(mp_text, m_summed_props.ulx, m_summed_props.uly);
		mp_font->EndText();
	}
#else

	if (!mp_text) return;
	
	bool offscreen = false;
	// HACK
	if (m_summed_props.GetScreenUpperLeftY() < -200.0f || m_summed_props.GetScreenUpperLeftY() > 648.0f)
		offscreen = true;

	if (m_num_tab_entries)
	{
		Image::RGBA true_rgba = m_local_props.GetRGBA();
		if (m_summed_props.alpha >= .0001f)
			true_rgba.a = (uint8) ((float) m_local_props.GetRGBA().a * m_summed_props.alpha);
		else
			true_rgba.a = 0;
		
		/* 
			-pass current pos, scale, alpha (local) to blur effect
			-for each entry in table
				-get pos, scale, alpa (in local, relative to current)
		*/
		
		mpp_text_req_tab[0]->SetFont(mp_font);
		mpp_text_req_tab[0]->SetPos(m_summed_props.GetScreenUpperLeftX(), m_summed_props.GetScreenUpperLeftY());
		mpp_text_req_tab[0]->SetRGBA(true_rgba, m_override_encoded_rgba);
		mpp_text_req_tab[0]->SetScale(m_summed_props.GetScaleX(), m_summed_props.GetScaleY());
		mpp_text_req_tab[0]->SetString(mp_text);
		if (m_object_flags & CScreenElement::v3D_POS)
		{
			mpp_text_req_tab[0]->SetZValue(m_summed_props.GetScreenPosZ());
			mpp_text_req_tab[0]->SetHidden( ( ( m_object_flags & CScreenElement::v3D_CULLED ) || IsHidden() ) );
		} else {
			mpp_text_req_tab[0]->SetPriority(m_z_priority);
			mpp_text_req_tab[0]->SetHidden( ( offscreen || IsHidden() ) );
		}

#if 0  // Garrett: Can't find window
		// Update the clip window.  This should only be done at init time, instead.
		CWindowElement *p_window = get_window();
		mpp_text_req_tab[0]->SetWindow(p_window->GetClipWindow());
#endif


		if (m_use_shadow)
		{
			Image::RGBA true_rgba = m_shadow_rgba;
			if (m_summed_props.alpha >= .0001f)
				true_rgba.a = (uint8) ((float) m_shadow_rgba.a * m_summed_props.alpha);
			else
				true_rgba.a = 0;
			mpp_shadow_req_tab[0]->SetFont(mp_font);
			mpp_shadow_req_tab[0]->SetPos(m_summed_props.GetScreenUpperLeftX() + m_shadow_off_x, m_summed_props.GetScreenUpperLeftY() + m_shadow_off_y);
			mpp_shadow_req_tab[0]->SetRGBA(true_rgba, m_override_encoded_rgba);
			mpp_shadow_req_tab[0]->SetScale(m_summed_props.GetScaleX(), m_summed_props.GetScaleY());
			mpp_shadow_req_tab[0]->SetString(mp_text);
			if (m_object_flags & CScreenElement::v3D_POS)
			{
				mpp_shadow_req_tab[0]->SetZValue(m_summed_props.GetScreenPosZ());
				mpp_shadow_req_tab[0]->SetHidden( ( ( m_object_flags & CScreenElement::v3D_CULLED ) || IsHidden() ) );
			} else {
				mpp_shadow_req_tab[0]->SetPriority(m_z_priority - .005f);
				mpp_shadow_req_tab[0]->SetHidden( ( offscreen || IsHidden() ) );
			}
		}
	}
	if(mp_blur_effect)
	{
		/* 
			-pass current center pos, scale, alpha (local) to blur effect
			-for each entry in table
				-get center pos, scale, alpa (in local, relative to current)
		*/

		Dbg_MsgAssert(!(m_object_flags & CScreenElement::v3D_POS), ("Can't use motion blur on 3D positioned text"));

		mp_blur_effect->Update();

		// work out absolute center of this element
		float center_x = m_summed_props.GetScreenUpperLeftX() + m_summed_props.GetScaleX() * m_base_w / 2.0f;
		float center_y = m_summed_props.GetScreenUpperLeftY() + m_summed_props.GetScaleY() * m_base_h / 2.0f;

		for (int i = 1; i < m_num_tab_entries; i++)
		{
			ConcatProps &blur_entry = mp_blur_effect->GetInfo(i-1);
			
			Image::RGBA true_rgba = mp_blur_effect->GetRGBA();
			true_rgba.a = (uint8) ((float) true_rgba.a * blur_entry.alpha);
			
			float draw_pos_x = center_x - (blur_entry.GetScreenPosX() + m_base_w * blur_entry.GetScaleX() / 2.0f) * m_summed_props.GetScaleX();
			float draw_pos_y = center_y - (blur_entry.GetScreenPosY() + m_base_h * blur_entry.GetScaleY() / 2.0f) * m_summed_props.GetScaleY();
			float draw_scale_x = m_summed_props.GetScaleX() * blur_entry.GetScaleX();
			float draw_scale_y = m_summed_props.GetScaleY() * blur_entry.GetScaleY();
			
			mpp_text_req_tab[i]->SetPos(draw_pos_x, draw_pos_y);
			mpp_text_req_tab[i]->SetRGBA(true_rgba, m_override_encoded_rgba);
			mpp_text_req_tab[i]->SetScale(draw_scale_x, draw_scale_y);
			mpp_text_req_tab[i]->SetPriority(m_z_priority - i * .01f);
			
			mpp_text_req_tab[i]->SetFont(mp_font);
			mpp_text_req_tab[i]->SetString(mp_text);
			mpp_text_req_tab[i]->SetHidden( ( offscreen || IsHidden() ) );

			if (m_use_shadow)
			{
				Image::RGBA true_rgba = m_shadow_rgba;
				if (m_summed_props.alpha >= .0001f)
					true_rgba.a = (uint8) ((float) m_shadow_rgba.a * m_summed_props.alpha);
				else
					true_rgba.a = 0;
				mpp_shadow_req_tab[i]->SetPos(draw_pos_x + m_shadow_off_x * draw_scale_x, 
											draw_pos_y + m_shadow_off_y * draw_scale_y);
				mpp_shadow_req_tab[i]->SetRGBA(true_rgba, m_override_encoded_rgba);
				mpp_shadow_req_tab[i]->SetScale(draw_scale_x, draw_scale_y);
				mpp_shadow_req_tab[i]->SetPriority(m_z_priority - i * .01f - .005f);

				mpp_shadow_req_tab[i]->SetFont(mp_font);
				mpp_shadow_req_tab[i]->SetString(mp_text);
				mpp_shadow_req_tab[i]->SetHidden( ( offscreen || IsHidden() ) );
			}
		}
	}	
#endif
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::create_text_instances(int numEntries, bool shadow_only)
{
	Dbg_Assert(!mpp_shadow_req_tab);

	if (m_use_shadow)
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mpp_shadow_req_tab = new Nx::CText*[numEntries];
		Mem::Manager::sHandle().PopContext();
		for (int i = 0; i < numEntries; i++)
		{
			mpp_shadow_req_tab[i] = Nx::CTextMan::sGetTextInstance();
		}
	}
	
	if (!shadow_only)
	{
		Dbg_Assert(!m_num_tab_entries);
	
		m_num_tab_entries = numEntries;
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mpp_text_req_tab = new Nx::CText*[numEntries];
		Mem::Manager::sHandle().PopContext();
		for (int i = 0; i < numEntries; i++)
		{
			mpp_text_req_tab[i] = Nx::CTextMan::sGetTextInstance();
		}
	}
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextElement::destroy_text_instances(bool shadow_only)
{
	if (mpp_shadow_req_tab)
	{
		for (int i = 0; i < m_num_tab_entries; i++)
		{
			Nx::CTextMan::sFreeTextInstance(mpp_shadow_req_tab[i]);
		}
		delete mpp_shadow_req_tab;
		mpp_shadow_req_tab = NULL;
	}
	
	if (!shadow_only)
	{
		Dbg_Assert(m_num_tab_entries);
		
		for (int i = 0; i < m_num_tab_entries; i++)
		{
			Nx::CTextMan::sFreeTextInstance(mpp_text_req_tab[i]);
		}
		delete mpp_text_req_tab;
		m_num_tab_entries = 0;
	}
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextBlockElement::CTextBlockElement() :
	m_shadow_rgba(0, 0, 0, 128)
{
	//Ryan("I am a new CTextBlockElement\n");
	m_font = 0;
	m_internal_scale = 1.0f;
    m_line_spacing_scale = 1.0f;
	m_total_height = 0.0f;
	m_total_out_lines = 0;

	mp_blur_effect = NULL;
	m_allow_expansion = false;

	m_override_encoded_rgba = false;
	m_previous_override_rgba_state = false;
	
	SetType(CScreenElement::TYPE_TEXT_BLOCK_ELEMENT);
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextBlockElement::~CTextBlockElement()
{	
	//Ryan("Destroying CTextBlockElement\n");
	if (mp_blur_effect)
		delete mp_blur_effect;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::SetProperties(Script::CStruct *pProps)
{
	CScreenElement::SetProperties(pProps);

	// must find this before setting font
	if (pProps->GetFloat(CRCD(0x1fe341d2,"internal_scale"), &m_internal_scale))
		m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;

    pProps->GetFloat(CRCD(0xe3fa22fc,"line_spacing"), &m_line_spacing_scale);
    
	uint32 font_crc;
	if (pProps->GetChecksum(CRCD(0x2f6bf72d,"font"), &font_crc))
		SetFont(font_crc);
	Dbg_MsgAssert(m_font, ("no font loaded"));
	

	if (resolve_just(pProps, CRCD(0x67e093e4,"internal_just"), &m_internal_just_x, &m_internal_just_y))
		m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
	

	const char *pp_line_tab[32];
	Script::CArray *p_text_array;
	
	if (pProps->ContainsFlag( CRCD(0x322839a2,"allow_expansion") ))
		m_allow_expansion = true;
	
	if (pProps->GetText(CRCD(0xc4745838,"text"), pp_line_tab))
	{
		SetText(pp_line_tab, 1);
	}
	else if (pProps->GetArray(CRCD(0xc4745838,"text"), &p_text_array))
	{
		int num_entries = p_text_array->GetSize();
		for (int i = 0; i < num_entries; i++)
			pp_line_tab[i] = p_text_array->GetString(i);

		SetText(pp_line_tab, num_entries);
	}

	/*
		Property changes to be forwared to children follow:	
	*/
	
	if (pProps->ContainsFlag(CRCD(0xd1653c6c,"blur_effect")) && !mp_blur_effect)
	{
		#if BLUR_EFFECT_ON
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		mp_blur_effect = new CBlurEffect();
		// mp_blur_effect->SetRGBA(m_rgba);
		mp_blur_effect->SetRGBA( m_local_props.GetRGBA() );
		Mem::Manager::sHandle().PopContext();
		#endif
	}
	else if (pProps->ContainsFlag(CRCD(0x4b73e4f8,"no_blur_effect")) && mp_blur_effect)
	{
		delete mp_blur_effect;
		mp_blur_effect = NULL;
	}
	
	Image::RGBA blur_rgba;
	if (resolve_rgba(pProps, CRCD(0x4f0e1182,"blur_rgba"), &blur_rgba) && mp_blur_effect)
	{
		mp_blur_effect->SetRGBA(blur_rgba);
	}

	Script::CPair shadow_offs;
	if (pProps->GetPair(CRCD(0x2a1fe0cc,"shadow_offs"), &shadow_offs))
	{
		m_shadow_off_x = shadow_offs.mX;
		m_shadow_off_y = shadow_offs.mY;
	}
	
	if (pProps->ContainsFlag(CRCD(0x8a897dd2,"shadow")))
		m_use_shadow = true;
	if (pProps->ContainsFlag(CRCD(0x95b5b9a0,"no_shadow")))
		m_use_shadow = false;

	resolve_rgba(pProps, CRCD(0x1e7bb1f5,"shadow_rgba"), &m_shadow_rgba);
	
	if ( pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ) )
	{
		Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
		m_previous_override_rgba_state = m_override_encoded_rgba;
	}
	if ( pProps->ContainsFlag( CRCD(0x61b7b58b,"restore_override_rgba_state") ) )
	{
		Dbg_MsgAssert( !pProps->ContainsFlag( CRCD(0xbf51a0c,"remember_override_rgba_state") ), ( "restore_override_rgba_state and remember_override_rgba_state?" ) );
		m_override_encoded_rgba = m_previous_override_rgba_state;
	}
	
	if (pProps->ContainsFlag(CRCD(0xb7686f92,"override_encoded_rgba")))
		m_override_encoded_rgba = true;
	if (pProps->ContainsFlag(CRCD(0xd25bc6cd,"dont_override_encoded_rgba")))
		m_override_encoded_rgba = false;
	
	forward_properties_to_children();
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::SetMorph(Script::CStruct *pProps)
{	
	if (mp_blur_effect)
	{
		// Distribute blur effect settings to children, account for different 
		// vertical positions of children relative to center of this
		
		// cheap hack, update self so that contained elements have proper position
		update();
		
		Tmr::Time target_time;
		const CBlurEffect::Props &blur_target = mp_blur_effect->SetMorph(pProps, &target_time);
		//printf("%f\n", blur_target.topAlpha);
		// hook up blur effect to children
		
		CScreenElementPtr p_child = CScreenElement::GetFirstChild();
		while(p_child)
		{
			
			Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
			Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));

			
			// "child blur target"
			CBlurEffect::Props cbt = blur_target;

			// find center of child relative to center of this
			// (we know we'll be getting top of child, since that's the just this class sets)
			float child_center_x, child_center_y;
			p_child->GetLocalPos(&child_center_x, &child_center_y);
			child_center_y += p_child->GetBaseH() / 2.0f;
			float disp_y = m_base_h / 2.0f - child_center_y;
			cbt.maxDisplacementY += blur_target.bottomScaleY * disp_y - disp_y;
			
			CBlurEffect *p_child_blur = ((CTextElement *) ((CScreenElement *) p_child))->GetBlurEffect();
			Dbg_Assert(p_child_blur);
			p_child_blur->SetAllTargetProps(cbt, target_time);
			p_child = p_child->GetNextSibling();
		}
	}

	CScreenElement::SetMorph(pProps);
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::SetFont(uint32 font_checksum)
{
	Dbg_MsgAssert(!m_font, ("font already set"));
	m_font = font_checksum;
	Dbg_MsgAssert(Nx::CFontManager::sTestFontLoaded(m_font), ("font %s isn't loaded", Script::FindChecksumName(m_font)));

	// how many lines will fit?
	
	Nx::CFont* p_font = Nx::CFontManager::sGetFont(m_font);
	Dbg_MsgAssert(p_font, ("no font found"));
	
	// see if word wraps past end of element
	float word_w, word_h;
	p_font->QueryString("AAA", word_w, word_h);	// width and height in pixels

	m_num_visible_lines = (int) m_base_h / (int) (word_h * m_internal_scale);
	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::SetText(const char **ppTextLines, int numLines)
{
	Dbg_MsgAssert(m_object_flags & vFORCED_DIMS, ("no dimensions have been set"));
	Dbg_MsgAssert(numLines < 256, ("large number of lines, probably not good"));

//	char parsed_lines[MAX_LINES][MAX_CHARS]; 
								  
	CScreenElementManager* p_manager = CScreenElementManager::Instance();
	
	// destroy any children that are present
	SetChildLockState( UNLOCK );
	CScreenElementPtr p_child = GetFirstChild();
	while ( p_child )
	{
		CScreenElementPtr p_next = p_child->GetNextSibling();
		p_manager->DestroyElement(p_child->GetID());
		p_child = p_next;
	}
	Dbg_Assert( !GetFirstChild() );

	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
	mpp_parsed_lines = new char*[MAX_LINES];
	// (Mick) in order to avoid doing multiple allocations, we just
	// allocate a single array big enough for all the lines
	mpp_parsed_lines[0] = new char[MAX_CHARS * MAX_LINES];
	Mem::Manager::sHandle().PopContext();
	// then calculate the pointers for all the other lines, as an offset to this
	for ( int l = 1; l < MAX_LINES; l++ )
	{
		mpp_parsed_lines[l] = mpp_parsed_lines[0] + MAX_CHARS*l;
	}
	for( int i = 0; i < MAX_LINES; i++ )
	{
		mpp_parsed_lines[i][0] = '\0';
	}

	m_out_char = 0;
	m_virtual_out_line = 0;
	m_out_line = 0;
	m_current_line_width = 0.0f;
	
	// reset number of visible lines
	if ( m_allow_expansion )
	{
		m_base_h = 0.0f;
		m_num_visible_lines = 0;
	}
	
	for ( int in_line = 0; in_line < numLines; in_line++ )
		read_in_text_line( ppTextLines[in_line] );
		
	// printf("line %d: %s\n", m_out_line, mpp_parsed_lines[m_out_line]);
	// want to handle the "no text" case
	if (numLines)
	{
		m_virtual_out_line++;
		m_out_line++;
	}

	// we only to want to use the N most recent lines,
	// where N is the number of lines that will fit
	int buf_use_line = 0;
	m_total_out_lines = m_out_line;
	if ( m_virtual_out_line >= m_num_visible_lines )
	{
		// expand the element if we're supposed to
		if ( m_allow_expansion )
		{
			Nx::CFont* p_font = Nx::CFontManager::sGetFont(m_font);
			Dbg_MsgAssert(p_font, ("no font found"));
			
			// change the height of the element
			float word_w, word_h;
			p_font->QueryString("AAA", word_w, word_h );	// width and height in pixels
			m_base_h = m_virtual_out_line * (word_h * m_internal_scale * m_line_spacing_scale );

			m_num_visible_lines = (int) m_base_h / (int) (word_h * m_internal_scale * m_line_spacing_scale );

			// re-parse the text now that m_num_visible_lines has changed
// Mick: no need for this, since they are fixed size, we just overwrite them
//			for (int l = 0; l < MAX_LINES; l++)
//			{
//				delete mpp_parsed_lines[l];
//				mpp_parsed_lines[l] = new char[MAX_CHARS];
//			}
			m_out_char = 0;
			m_virtual_out_line = 0;
			m_out_line = 0;
			m_current_line_width = 0.0f;
			for (int i = 0; i < numLines; i++)
				read_in_text_line(ppTextLines[i]);

			// printf("changing m_total_out_lines from %i to %i\n", m_total_out_lines, m_num_visible_lines);
			m_total_out_lines = m_num_visible_lines;
		}
		else 
		{
			Dbg_MsgAssert( m_num_visible_lines, ("Text block element too small for one line") );
            buf_use_line = m_virtual_out_line % m_num_visible_lines;
			m_total_out_lines = m_num_visible_lines;
		}
	}


	// create new children
	m_total_height = 0.0f;
	for (int i = 0; i < m_total_out_lines; i++)
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		CTextElement *p_new_elem = new CTextElement();
		Mem::Manager::sHandle().PopContext();
		p_manager->RegisterObject(*p_new_elem);
		p_manager->SetParent(this, p_new_elem);
		p_new_elem->SetFont(m_font);
		p_new_elem->SetText(&mpp_parsed_lines[buf_use_line][0]);
		p_new_elem->SetScale(m_internal_scale, m_internal_scale);
	
		
		m_total_height += p_new_elem->GetBaseH() * m_internal_scale * m_line_spacing_scale;
		buf_use_line++;
		if (buf_use_line >= m_num_visible_lines)
			buf_use_line = 0;
	}

	// delete the lines, now we've used them
// Mick: Now we just delete the array of chars, and the array of pointers
//	for (int l = 0; l < MAX_LINES; l++)
//	{
//		delete mpp_parsed_lines[l];
//		mpp_parsed_lines[l] = NULL;			
//	}
	delete mpp_parsed_lines[0];
	delete mpp_parsed_lines;

	
	SetChildLockState(LOCK);

	m_object_flags |= CScreenElement::vCHANGED_STATIC_PROPS;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::SetText(const char *pTextLine)
{
	SetText(&pTextLine, 1);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTextBlockElement::GetText( char* p_text, int size )
{
	strcpy( p_text, "" );
	int total_length = 0;
	
	CScreenElementPtr p_child = GetFirstChild();
	while( p_child )
	{
		Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
		Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));

	
		CScreenElementPtr p_next = p_child->GetNextSibling();
		CTextElement* p_text_element = (CTextElement*)p_child.Convert();

		char* p_text_element_string = p_text_element->GetText();
		total_length += strlen( p_text_element_string );
		
		if ( p_next )
			total_length += 1;
	
		if ( total_length < size )
		{
			strcat( p_text, p_text_element_string );
			
			// add a space between lines if this isn't the last line
			if ( p_next )
				strcat( p_text, " " );
		}
		else
		{
			Dbg_MsgAssert( 0, ( "TextBlockElement::GetText - text too long" ) );
			return false;
		}
		p_child = p_next;
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CTextBlockElement::GetLength()
{
	// printf( "CTextBlockElement::GetLength called on %s\n", Script::FindChecksumName( m_id ) );
	int total_length = 0;
	
	CScreenElementPtr p_child = GetFirstChild();
	while( p_child )
	{
		Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
		Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));
		
		CScreenElementPtr p_next = p_child->GetNextSibling();
		CTextElement* p_text_element = (CTextElement*)p_child.Convert();
		total_length += p_text_element->GetLength();
		
		if ( p_next )
			total_length += 1;
		
		p_child = p_next;
	}	
	return total_length;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTextBlockElement::Backspace()
{
	int length = GetLength();

	if ( length == 0 )
		return false;

	Dbg_MsgAssert( length < MAX_EDITABLE_TEXT_BLOCK_LENGTH, ( "Backspace failed - string too long" ) );
	char new_string[MAX_EDITABLE_TEXT_BLOCK_LENGTH];
	GetText( new_string, MAX_EDITABLE_TEXT_BLOCK_LENGTH );
	
	if ( !new_string )
		return false;
	
	// take care of escaped backslash char (ASCII code 92)
	if ( length > 1 && new_string[length - 1] == 92 && new_string[length - 2] == 92 )
		new_string[length - 2] = '\0';
	
	// terminate
	new_string[length - 1] = '\0';
	SetText( new_string );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
	Called from read_in_text_line()
*/
CTextBlockElement::EParseResult CTextBlockElement::parse_next_word(char *pWordBuf, const char **ppSource)
{
	bool keep_scanning = true;
	EParseResult result = NORMAL; 
	
	char *p_out = pWordBuf;
	
	while (keep_scanning)
	{
		switch(**ppSource)
		{
			case ' ':
				*p_out++ = **ppSource;
				(*ppSource)++;
				keep_scanning = false;
				break;
			case '\\':
			{
				(*ppSource)++;
				if (**ppSource == '_')
				{
					*p_out++ = ' ';
					(*ppSource)++;
				}
				else if (**ppSource == 'n')
				{
					(*ppSource)++;
					result = NEXT_LINE;
					keep_scanning = false;
				}
				else
				{
					// is \?, where ? is some character
					// output both
					*p_out++ = '\\';
					*p_out++ = **ppSource;
					(*ppSource)++;
				}
				break;
			} // end case
			case '\0':
				result = END_OF_BUFFER;
				keep_scanning = false;
				break;
			default:
			{
				*p_out++ = **ppSource;
				(*ppSource)++;
				break;
			}
		} // end switch

		Dbg_Assert((int) p_out - (int) pWordBuf < MAX_CHARS - 1);
	}

	*p_out++ = '\0';
	return result;
}




/*
	Called from SetText()
*/
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void CTextBlockElement::read_in_text_line(const char *pText)
{
	Nx::CFont* p_font = Nx::CFontManager::sGetFont(m_font);
	Dbg_MsgAssert(p_font, ("no font found"));
	
	const char *p_in = pText;

	char p_word[MAX_CHARS];

	EParseResult last_result = NORMAL;
	
	// printf("reading in: %s\n", pText);
	
	while ( last_result != END_OF_BUFFER )
	{
		EParseResult result = parse_next_word( p_word, &p_in );
		
		// see if word wraps past end of element
		float word_w, word_h;
		p_font->QueryString(p_word, word_w, word_h);	// width and height in pixels
		word_w *= m_internal_scale;
		
		// printf("result = %d, word = '%s', screen_w = %.2f\n", result, p_word, word_w);

		// if adding this word makes the line too long
		// or we had a newline char or similar
		// or this word will overflow the buffer (unlikely, but possible)
		if ( ( m_current_line_width > 0.0f && m_current_line_width + word_w > m_base_w )
			 ||  ( last_result == NEXT_LINE )
			 ||  ( m_out_char + strlen( p_word ) >= MAX_CHARS )
		   )
		{
			// printf("m_current_line_width = %f, word_w = %f, m_base_w = %f\n", m_current_line_width, word_w, m_base_w);
			// printf("line %d: %s\n", m_out_line, mpp_parsed_lines[m_out_line]);
			
			// if last character on line is a space, do away with it
			if ( mpp_parsed_lines[m_out_line][m_out_char-1] == ' ' )
				mpp_parsed_lines[m_out_line][m_out_char-1] = '\0';
			
			// Wrap to next	line.
			// Once we fill all visible lines, we start recycling the buffer,
			// keeping only the most recent lines, hence the virtual line stuff.
			m_virtual_out_line++;
			m_out_line++;
			if ( m_out_line >= m_num_visible_lines )
				m_out_line = 0;
			m_out_char = 0;
			m_current_line_width = 0.0f;
		}
		
		Dbg_Assert(m_out_line < MAX_LINES); // need to increase MAX_LINES if triggered
		Dbg_MsgAssert(m_out_char + strlen(p_word) < MAX_CHARS, ("overflow, max allowed = %d, needed = %d\n%s%s\n", MAX_CHARS, m_out_char + strlen(p_word),&mpp_parsed_lines[m_out_line][0],p_word));
		char *p_out = &mpp_parsed_lines[m_out_line][0] + m_out_char;
		strcpy(p_out, p_word);
		m_out_char += strlen(p_word);
		m_current_line_width += word_w;

		last_result = result;
	}
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::update()
{
	if (m_object_flags & CScreenElement::vCHANGED_STATIC_PROPS)
	{
		Dbg_Assert(m_total_height > .00001f || !m_total_out_lines);
		
		// position children
		float y = (m_internal_just_y + 1.0f) * (m_base_h - m_total_height) / 2.0f;
		CScreenElementPtr p_child = GetFirstChild();
		for (int i = 0; i < m_total_out_lines; i++)
		{
			Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
			Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));
			
			p_child->SetJust(m_internal_just_x, -1.0f);
			// p_child->SetRGBA(m_rgba);
			p_child->SetRGBA( m_local_props.GetRGBA() );
			p_child->SetPos((m_internal_just_x + 1.0f) * m_base_w / 2.0f, y);

			y += p_child->GetBaseH() * m_internal_scale * m_line_spacing_scale;
			p_child = p_child->GetNextSibling();
		}
		forward_properties_to_children();
	}
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTextBlockElement::forward_properties_to_children()
{
	CScreenElementPtr p_child = CScreenElement::GetFirstChild();
	while(p_child)
	{
		
		Dbg_MsgAssert(p_child->GetType() == CScreenElement::TYPE_TEXT_ELEMENT,("TextBlockElement %s has child %s that is NOT a CTextElement (it's %s)\n",
			Script::FindChecksumName(GetID()), Script::FindChecksumName(p_child->GetID()), Script::FindChecksumName(p_child->GetType())));
		
		if (mp_blur_effect)
			((CTextElement *) ((CScreenElement *) p_child))->AttachBlurEffect();
		else
			((CTextElement *) ((CScreenElement *) p_child))->DetachBlurEffect();
		if (m_use_shadow)
			((CTextElement *) ((CScreenElement *) p_child))->SetShadowState(true);
		else
			((CTextElement *) ((CScreenElement *) p_child))->SetShadowState(false);
		((CTextElement *) ((CScreenElement *) p_child))->SetShadowOff(m_shadow_off_x, m_shadow_off_y);
		((CTextElement *) ((CScreenElement *) p_child))->SetShadowRGBA(m_shadow_rgba);
		((CTextElement *) ((CScreenElement *) p_child))->SetEncodedRGBAOverride(m_override_encoded_rgba);
		p_child = p_child->GetNextSibling();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// if enforce_max_width is true, the function will fail if a single word is
// bigger than the width of a line.  That is, if the text would go outside
// the specified width and there's no way to wrap, concatenation will fail.
// Only the end of the string is checked.  The text already in the element
// is assumed to be safe.  Thus it's possible to have a textblock element go
// outside it's box by using SetText directly, rather than using concatenate
bool CTextBlockElement::Concatenate( const char* pText, bool enforce_max_width, bool last_line )
{
#ifdef __NOPT_ASSERT__
	int length = GetLength();
	Dbg_MsgAssert( length < (int)( MAX_EDITABLE_TEXT_BLOCK_LENGTH - strlen( pText ) ), ( "TextBlock too long to concatenate" ) );
#endif
	
	// setup new, unwrapped string
	char* p_new_string = new char[MAX_EDITABLE_TEXT_BLOCK_LENGTH];
	GetText( p_new_string, Front::MAX_EDITABLE_TEXT_BLOCK_LENGTH );
	strcat( p_new_string, pText );

	// enforce max width but allow them to add a space.
	if ( (enforce_max_width && strcmp( pText, " " ) != 0) || last_line)
	{
		Nx::CFont* p_font = Nx::CFontManager::sGetFont( m_font );
		Dbg_MsgAssert( p_font, ( "no font found" ) );

		// parse through the new text word by word
		const char *p_in = p_new_string;
		char p_word[MAX_CHARS];

		// skip up to the last word
		EParseResult last_result = NORMAL;
		while ( last_result != END_OF_BUFFER )
		{			
			last_result = parse_next_word( p_word, &p_in );
		}
			
		// check that each word is not longer than the max width
		float word_w, word_h;
		p_font->QueryString( p_word, word_w, word_h );	// width and height in pixels
		word_w *= m_internal_scale;
		if ( word_w > m_base_w )
		{
			// too long!
			delete p_new_string;
			return false;
		}
	}
	
	SetText( p_new_string );
	delete p_new_string;
	return true;
}


}


================================================
FILE: Code/Gfx/2D/TextElement.h
================================================
#ifndef __GFX_2D_TEXTELEMENT_H__
#define __GFX_2D_TEXTELEMENT_H__

#ifndef __GEL_OBJECT_H
#include 
#endif
#include 

#define TEXT_ELEMENT_MAX_LENGTH 96

namespace Nx
{
	class CFont;
	class CText;
}

namespace Front
{
	class CBlurEffect;

	enum
	{
		MAX_EDITABLE_TEXT_BLOCK_LENGTH = 512,
	};


class CTextElement : public CScreenElement
{
	friend class CScreenElementManager;
	friend class CTextBlockElement;

public:
							CTextElement();
	virtual					~CTextElement();

	void					SetProperties(Script::CStruct *pProps);
	void					SetMorph(Script::CStruct *pProps);
	
	void 					SetFont(uint32 font_checksum);
	void					SetText(const char *pText);
	char*					GetText() { return mp_text; }
	int						GetLength();

    bool					Concatenate( const char *pText );
	bool					Backspace();
	
	void					AttachBlurEffect();
	void					DetachBlurEffect();
	CBlurEffect	*			GetBlurEffect() {return mp_blur_effect;}

	void					SetShadowState(bool shadowOn);
	void					SetShadowOff(float offX, float offY);
	void					SetShadowRGBA(Image::RGBA shadowRGBA) {m_shadow_rgba = shadowRGBA;}

	void					SetEncodedRGBAOverride(bool override) {m_override_encoded_rgba = override;}

protected:

	void					create_text_instances(int numInstances, bool shadowOnly = false);
	void					destroy_text_instances(bool shadowOnly = false);
	
	enum
	{
		vMAX_TEXT_LENGTH	= TEXT_ELEMENT_MAX_LENGTH,
	};
	
	void					update();

	char *					mp_text;

	uint32					m_font_checksum;
	Nx::CFont *				mp_font;
	Nx::CText **			mpp_text_req_tab;
	Nx::CText **			mpp_shadow_req_tab;
	int						m_num_tab_entries;

	CBlurEffect	*			mp_blur_effect;

	bool					m_use_shadow;
	float					m_shadow_off_x;
	float					m_shadow_off_y;
	Image::RGBA				m_shadow_rgba;

	bool					m_override_encoded_rgba;
	bool					m_previous_override_rgba_state;
};




class CTextBlockElement : public CScreenElement
{
	friend class CScreenElementManager;

public:
							CTextBlockElement();
	virtual					~CTextBlockElement();

	void					SetProperties(Script::CStruct *pProps);
	void					SetMorph(Script::CStruct *pProps);
	
	void 					SetFont(uint32 font_checksum);
	void					SetText(const char **ppTextLines, int numLines);
	void					SetText(const char *pTextLine);

	bool					GetText( char* p_text, int size );
	int						GetLength();
	bool					Backspace();
	bool					Concatenate( const char* pText, bool enforce_max_width = false, bool last_line = false );

protected:

	enum EParseResult
	{ 
		NORMAL,
		NEXT_LINE,
		END_OF_BUFFER,
	};

	enum
	{
		MAX_LINES			= 20,
		MAX_CHARS			= TEXT_ELEMENT_MAX_LENGTH,
	};
	
	EParseResult 			parse_next_word(char *pWordBuf, const char **ppSource);
	void 					read_in_text_line(const char *pText);
	void					update();
	void 					forward_properties_to_children();

	uint32					m_font;

	char **					mpp_parsed_lines;
	// Line of text "outputted" so far. With a lot of text lines, only most recent ones are used.
	int						m_virtual_out_line;	
	int 					m_out_line;	// line of mpp_parsed_lines currently being written to
	int 					m_out_char; // on m_out_line
	float 					m_current_line_width;

	int						m_num_visible_lines;

	float					m_internal_just_x, m_internal_just_y;
	float					m_internal_scale;
    float					m_line_spacing_scale;
	float					m_total_height; // of contained TextElements
	int						m_total_out_lines; // number of contained TextElements
	
	CBlurEffect	*			mp_blur_effect;
	bool					m_use_shadow;
	bool					m_allow_expansion;
	float					m_shadow_off_x;
	float					m_shadow_off_y;
	Image::RGBA				m_shadow_rgba;
	
	bool					m_override_encoded_rgba;
	bool					m_previous_override_rgba_state;
};




}

#endif


================================================
FILE: Code/Gfx/2D/Window.cpp
================================================
#include 
#include 
#include 

#ifdef	__PLAT_NGPS__ 
#include 
#endif

namespace Front
{




CWindowElement::CWindowElement()
{
	SetType(CScreenElement::TYPE_WINDOW_ELEMENT);
	mp_clip_window = Nx::CWindow2DManager::sGetWindowInstance(0, 0, 640, 448);
}




CWindowElement::~CWindowElement()
{
	Dbg_Assert(mp_clip_window);
	Nx::CWindow2DManager::sFreeWindowInstance(mp_clip_window);
}


Nx::CWindow2D *	CWindowElement::GetClipWindow() const
{
	return mp_clip_window;
}


#if 0
void CWindowElement::drawMainPart()
{
#ifdef	__PLAT_NGPS__ 
	NxPs2::SetTextWindow(0,639,0,447);						// a full-screen clipping window
#else
	printf ("WARNING: drawMainPart not the same on this platform....\n");
#endif
}
#endif




}


================================================
FILE: Code/Gfx/2D/Window.h
================================================
#ifndef __GFX_2D_WINDOW_H__
#define __GFX_2D_WINDOW_H__

#include 

namespace Nx
{
	class CWindow2D;
}

namespace Front
{

class CWindowElement : public Front::CScreenElement
{
public:

							CWindowElement();
	virtual					~CWindowElement();

	void					SetClipWindow(Nx::CWindow2D *p_window);		// This should probably be protected or private
	Nx::CWindow2D *			GetClipWindow() const;

protected:
	Nx::CWindow2D *			mp_clip_window;

};

}

#endif


================================================
FILE: Code/Gfx/AnimController.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       AnimChannel.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/13/01
//****************************************************************************

// Current state of an animation.  There should be no
// references to skeletons or geometry;  this is purely 
// a time-keeping class).

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
                                  
#include 
#include 
#include 
								   
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Gfx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/
	
/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CAnimChannel::get_new_anim_time( void )
{
#ifdef __NOPT_ASSERT__	
	float old_time = m_currentTime;
#endif

	float new_time=0;
	float new_hold_time=0;
	float new_loop_time=0;

	m_animComplete = false;

	if ( m_loopingType == LOOPING_WOBBLE )
	{
		Dbg_MsgAssert( 0, ( "Was not expecting a wobble looping type in this controller" ) );
		return 0.0f;
	}
	
	if ( m_direction == ANIM_DIR_FORWARDS )
	{
		new_time = m_currentTime + ( m_animSpeed * Tmr::FrameRatio() );
		m_animComplete = new_time > m_endTime;
		
		if (m_startTime == m_endTime)
		{
			new_loop_time = m_endTime;
		}
		else
		{
			new_loop_time = m_startTime + ( new_time - m_endTime );
			while ( new_loop_time > m_endTime )
			{
				// when the FrameRatio() > 1.0f, it's possible that
				// we need to subtract more than once, esp. with short anims
				new_loop_time = m_startTime + ( new_loop_time - m_endTime );
			}
		}
		new_hold_time = m_endTime;
	}
	else if ( m_direction == ANIM_DIR_BACKWARDS )
	{
		new_time = m_currentTime - ( m_animSpeed * Tmr::FrameRatio() );
		m_animComplete = new_time < m_startTime;
		if (m_startTime == m_endTime)
		{
			new_loop_time = m_endTime;
		}
		else
		{
			new_loop_time = m_endTime + ( new_time - m_startTime );
			while ( new_loop_time < m_startTime )
			{
				// when the FrameRatio() > 1.0f, it's possible that
				// we need to subtract more than once, esp. with short anims
				new_loop_time = m_endTime + ( new_loop_time - m_startTime );
			}
		}
		new_hold_time = m_startTime;
	}
	else
	{
		Dbg_Assert( 0 );
	}

	if ( m_animComplete )
	{
		if ( m_loopingType == LOOPING_HOLD )
		{
			new_time = new_hold_time;
		}
		else if ( m_loopingType == LOOPING_PINGPONG )
		{
			m_direction = ( m_direction == ANIM_DIR_FORWARDS ) ? ANIM_DIR_BACKWARDS : ANIM_DIR_FORWARDS;
			new_time = new_hold_time;
		}
        else if ( m_loopingType == LOOPING_CYCLE )
        {
            new_time = new_loop_time;
		}
	}

	// Ken: This is temporary, to prevent the following assert going off when running a replay.
	// Eventually the replays will be fixed to always recreate the correct new_time
    if ( new_time < m_startTime )
	{
		new_time = m_startTime;
	}	
	if ( new_time > m_endTime )
	{
		new_time = m_endTime;
	}	

#ifdef __NOPT_ASSERT__
    if ( new_time < m_startTime || new_time > m_endTime )
    {
        Dbg_Message( "old_time = %f", old_time );
        Dbg_Message( "new_time = %f", new_time );
        Dbg_Message( "new_loop_time = %f", new_loop_time );
        Dbg_Message( "new_hold_time = %f", new_hold_time );
        Dbg_Message( "m_startTime = %f", m_startTime );
        Dbg_Message( "m_endTime = %f", m_endTime );
        Dbg_Message( "m_loopingType = %d", m_loopingType );
        Dbg_Message( "m_direction = %d", m_direction );
        Dbg_Message( "m_animComplete = %d", m_animComplete );
        Dbg_Message( "m_animName = %s", Script::FindChecksumName( m_animName ) );
		Dbg_Assert( 0 );
    }
#endif

	return new_time;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAnimChannel::CAnimChannel() : Lst::Node(this)
{
    Reset();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAnimChannel::~CAnimChannel()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimChannel::Reset()
{
	m_startTime = 0.0f;
    m_currentTime = 0.0f;
    m_endTime = 0.0f;
    m_animSpeed = 0.0f;
    m_direction = ANIM_DIR_FORWARDS;
    m_loopingType = LOOPING_CYCLE;
    m_animComplete = false;

	// Ken: Changed this to be a hard wired checksum rather than calling Script::GenerateCRC
	// The reason for this is that CAnimChannel::Reset() gets called from 
	// CSkaterTrackingInfo::Reset() in replay.cpp, which gets called from the CSkaterTrackingInfo
	// constructor. There is a static instance of a CSkaterTrackingInfo in replay.cpp, so the
	// constructor will get called at some point before main(). Since Script::GenerateCRC accesses
	// an array of data, there is a chance that it might get called before the array is initialised,
	// since the order of execution is not well defined.
	// It probably would be OK, I just want to be safe.
	m_animName = 0xf60c9090;		// unassigned <-- just giving it a non-zero value, so that
									// we can be sure it's getting initialized correctly...
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CAnimChannel::GetCurrentAnim( void )
{
    return m_animName;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CAnimChannel::GetCurrentAnimTime( void )
{
    return m_currentTime;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimChannel::GetAnimTimes(float *pStart, float *pCurrent, float *pEnd)
{
  	Dbg_MsgAssert( pStart, ("NULL pStart") );
	Dbg_MsgAssert( pCurrent, ("NULL pCurrent") );
	Dbg_MsgAssert( pEnd, ("NULL pEnd") );

	*pCurrent = m_currentTime;
	
    if ( m_direction == ANIM_DIR_FORWARDS )
	{
		*pStart = m_startTime;
		*pEnd = m_endTime;
	}	
	else
	{
		*pStart = m_endTime;
		*pEnd = m_startTime;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimChannel::AddTime( float incVal )
{
	// GJ:  a way to fool the animation controller
	// into incrementing its time (frame rate
	// independent) for the viewer object

	float oldAnimSpeed = m_animSpeed;

	m_animSpeed = 0.0f;

	m_currentTime += incVal;

	CAnimChannel::Update();

	m_animSpeed = oldAnimSpeed;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimChannel::Update()
{
	m_currentTime = get_new_anim_time();
}

/******************************************************************/
/*                                                                */
/* Returns whether the animation has completed.  This is only     */
/* valid with "hold on last frame" animations.				  */
/*                                                                */
/******************************************************************/

bool CAnimChannel::IsAnimComplete( void )
{
	if ( m_loopingType == LOOPING_HOLD )
	{
		if ( m_direction == ANIM_DIR_FORWARDS )
		{
			return ( m_currentTime == m_endTime );
		}	
		else
		{
			return ( m_currentTime == m_startTime );
		}	
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CAnimChannel::IsLoopingAnim( void )
{	
	// Returns whether the current animation will loop forever.
	// Used in skater.cpp, to determine whether or not it should
	// wait for an animation to finish.
	return ( m_loopingType == LOOPING_CYCLE || m_loopingType == LOOPING_PINGPONG );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimChannel::SetAnimSpeed( float speed )
{
	m_animSpeed = speed / 60.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CAnimChannel::GetAnimSpeed( void )
{
	return ( m_animSpeed * 60.0f );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimChannel::ReverseDirection( void )
{
	if ( m_direction == ANIM_DIR_FORWARDS )
	{
		m_direction = ANIM_DIR_BACKWARDS;
	}	
	else
	{
		m_direction = ANIM_DIR_FORWARDS;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimChannel::SetLoopingType( EAnimLoopingType type )
{
	m_loopingType = type;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimChannel::PlaySequence( uint32 anim_name, float start_time, float end_time, EAnimLoopingType loop_type, float blend_period, float speed )
{		   
// Don't reset any more, because this will call the CBlendChannel's virtual
// virtual function, which makes the animation status inactive...
//	Reset();

	m_startTime							    = (start_time < end_time) ? start_time : end_time;
	m_currentTime							= start_time;
	m_endTime								= (start_time < end_time) ? end_time : start_time;
	m_animSpeed							    = speed / 60.0f;
	m_direction								= (start_time < end_time) ? ANIM_DIR_FORWARDS : ANIM_DIR_BACKWARDS;
	m_loopingType							= loop_type;
	m_animComplete							= false;
	m_animName								= anim_name;

//	Dbg_MsgAssert(m_startTime != m_endTime,("m_startTime == m_endTime (%f) TELL MICK",m_startTime));
//	m_wobbleTargetTime					    = (start_time + end_time) / 2;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAnimChannel::GetDebugInfo( Script::CStruct* p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert( p_info, ( "NULL p_info sent to CAnimChannel::GetDebugInfo" ) );

	// put the name first
	p_info->AddChecksum( "m_animName", m_animName );
	
	uint32 looping_type_checksum = 0;
	switch ( m_loopingType )
	{
	case 0:
		looping_type_checksum = CRCD( 0x3f00df04, "LOOPING_HOLD" );
		break;
	case 1:
		looping_type_checksum = CRCD( 0xfbada576, "LOOPING_CYCLE" );
		break;
	case 2:
		looping_type_checksum = CRCD( 0x792b924b, "LOOPING_PINGPONG" );
		break;
	case 3:
		looping_type_checksum = CRCD( 0x90423ff2, "LOOPING_WOBBLE" );
		break;
	default:
		Dbg_MsgAssert( 0, ( "Unknown looping type found in CAnimChannel::GetDebugInfo.  Was the enum changed?" ) );
		break;
	}
	if ( looping_type_checksum )
		p_info->AddChecksum( CRCD(0x84b06b3,"m_loopingType"), looping_type_checksum );

	switch ( m_direction )
	{
	case 0:
		p_info->AddChecksum( CRCD(0x9f648df4,"m_direction"), CRCD( 0xdf8b2509, "ANIM_DIR_FORWARDS" ) );
		break;
	case -1:
		p_info->AddChecksum( CRCD(0x9f648df4,"m_direction"), CRCD( 0xfdf0c4a4, "ANIM_DIR_BACKWARDS" ) );
		break;
	default:
		Dbg_MsgAssert( 0, ( "Unknown direction found in CAnimChannel::GetDebugInfo.  Was the enum changed?" ) );
		break;
	}

	p_info->AddFloat( CRCD(0x8fbac25e,"m_startTime"), m_startTime );
	p_info->AddFloat( CRCD(0xb73f0945,"m_currentTime"), m_currentTime );
	p_info->AddFloat( CRCD(0xf1cef774,"m_endTime"), m_endTime );
	p_info->AddFloat( CRCD(0xaa1e73de,"m_animSpeed"), m_animSpeed );
	p_info->AddInteger( CRCD(0x2d574054,"m_animComplete"), m_animComplete );
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx


================================================
FILE: Code/Gfx/AnimController.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       AnimChannel.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/13/01
//****************************************************************************

// Current state of an animation.  There should be no
// references to skeletons or geometry;  this is purely 
// a time-keeping class).

#ifndef __GFX_ANIMCHANNEL_H
#define __GFX_ANIMCHANNEL_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Script
{
	class CStruct;
}
						
namespace Gfx
{

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class CAnimChannel : public Lst::Node
{
public:
    CAnimChannel();
    virtual ~CAnimChannel();

    virtual void        Reset();
    virtual void        Update();
	uint32				GetCurrentAnim();
	
    float               GetCurrentAnimTime( void );
    void                GetAnimTimes( float*, float*, float* );
    bool                IsAnimComplete( void );
    bool                IsLoopingAnim( void );
    void                SetAnimSpeed( float );
    float               GetAnimSpeed( void );
	void				ReverseDirection( void );
    void                SetLoopingType( EAnimLoopingType );
	EAnimLoopingType	GetLoopingType() const {
		return m_loopingType;
	}
    virtual void        PlaySequence( uint32 animName, float start_time, float end_time, EAnimLoopingType loop_type, float blend_period, float speed );
	void				AddTime( float incVal );

	virtual void		GetDebugInfo( Script::CStruct* p_info );

	// made this public while i'm changing over to new anim controllers

//protected:
public:
    float				get_new_anim_time( void );
    
    float				m_startTime;
    float				m_currentTime;
    float				m_endTime;
    float				m_animSpeed;
    EAnimDirection		m_direction;
    EAnimLoopingType	m_loopingType;
    bool				m_animComplete;
	uint32				m_animName;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // namespace Gfx

#endif	// __GFX_ANIMCHANNEL_H


================================================
FILE: Code/Gfx/BonedAnim.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       bonedanim.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/14/2001
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef __PLAT_NGC__
#include 
#include "sys/ngc/p_dma.h"
#include "sys\ngc\p_aram.h"
#endif		// __PLAT_NGC__

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

#ifdef __PLAT_NGC__
#define __ARAM__
#endif

#ifdef __PLAT_NGC__
#define _16(a) (((a>>8)&0x00ff)|((a<<8)&0xff00))
#define _32(a) (((a>>24)&0x000000ff)|((a>>8)&0x0000ff00)|((a<<8)&0x00ff0000)|((a<<24)&0xff000000)) 
#else
#define _16(a) a
#define _32(a) a
#endif		// __PLAT_NGC__

namespace Gfx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// In order to compress the data, we convert the animation quat into
// a unit quat and then discard the W value during export-time/load-time.  
// Given the X, Y, Z values, we can rebuild the W value at run-time.
// The code uses the following tolerance to when comparing the rebuilt W value
// to the original value.  I chose the number pretty arbitrarily, based
// on the existing (THPS3) skater and pedestrian animations.
//(0.0005 radians ~= 0.028 degrees)
const float nxUNITQUAT_TOLERANCE_RADIANS = 0.0005f;

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

// The following structures are only needed until new data
// is exported in the correct format

struct SBonedAnimFileHeader
{
	uint32 	    version;
	uint32    	flags;
	float	    duration;
};

struct SPlatformFileHeader
{
    uint32      numBones;
    uint32      numQKeys;
    uint32      numTKeys;
    uint32      numCustomAnimKeys;
};

struct SStandardAnimFramePointers
{
	unsigned char	numQKeys;
	unsigned char	numTKeys;
};

struct SHiResAnimFramePointers
{
	short			numQKeys;
	short			numTKeys;
};

//#define nxBONEDANIMFLAGS_UNUSED			(1<<31)
#define nxBONEDANIMFLAGS_INTERMEDIATE   	(1<<30)
#define nxBONEDANIMFLAGS_UNCOMPRESSED   	(1<<29)
#define nxBONEDANIMFLAGS_PLATFORM      		(1<<28)	
#define nxBONEDANIMFLAGS_CAMERADATA    		(1<<27)	
#define nxBONEDANIMFLAGS_COMPRESSEDTIME 	(1<<26)
#define nxBONEDANIMFLAGS_PREROTATEDROOT 	(1<<25)
#define nxBONEDANIMFLAGS_OBJECTANIMDATA		(1<<24)
#define nxBONEDANIMFLAGS_USECOMPRESSTABLE	(1<<23)
#define nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS	(1<<22)
#define nxBONEDANIMFLAGS_CUSTOMKEYSAT60FPS	(1<<21)
#define nxBONEDANIMFLAGS_CUTSCENEDATA		(1<<20)
#define nxBONEDANIMFLAGS_PARTIALANIM		(1<<19)

// to be phased out...
#define nxBONEDANIMFLAGS_OLDPARTIALANIM		(1<<18)

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

#ifdef __ARAM__
#define ARAM_CACHE_SIZE (64*1024)
#define MAX_BONE_COUNT 128

static char qqq[ARAM_CACHE_SIZE] ATTRIBUTE_ALIGN(32); 

static uint16				framecount[MAX_BONE_COUNT*2] ATTRIBUTE_ALIGN(32);
//static uint32				partial[8] ATTRIBUTE_ALIGN(32);

volatile uint8				framecount_active = 0;
static int					framecount_size = 0;
ARQRequest					framecount_request;
#endif

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

// compress tables take up 4K,
// which are a waste on Xbox and GC
// (should PLAT_NGPS it once it's working)

struct CBonedAnimCompressEntry
{
	short x48;
	short y48;
	short z48;
	short n8;
};

CBonedAnimCompressEntry sQTable[256];
CBonedAnimCompressEntry sTTable[256];


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool init_48_table( void* pStream, CBonedAnimCompressEntry* pEntry )
{
	for ( int i = 0; i < 256; i++ )
	{
		if ( !File::Read( (int*)&pEntry->x48, sizeof(short), 1, pStream ) )
		{
			return false;
		}
		
		if ( !File::Read( (int*)&pEntry->y48, sizeof(short), 1, pStream ) )
		{
			return false;
		}
		
		if ( !File::Read( (int*)&pEntry->z48, sizeof(short), 1, pStream ) )
		{
			return false;
		}

		if ( !File::Read( (int*)&pEntry->n8, sizeof(short), 1, pStream ) )
		{
			return false;
		}
		
		pEntry->x48 = _16( pEntry->x48 );
		pEntry->y48 = _16( pEntry->y48 );
		pEntry->z48 = _16( pEntry->z48 );
		pEntry->n8 = _16( pEntry->n8 );

		pEntry++;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
#ifdef __ARAM__
//static volatile bool	dmaComplete;

static void arqCallback( u32 pointerToARQRequest )
{
//	ARQRequest * p_arq = (ARQRequest *)pointerToARQRequest;

//	if ( p_arq->owner == 0x55555555 )
//	{
		framecount_active = 0;
//	}
}

void dma_count( uint32 p_source_base, int size )
{
	// DMA the count info.
	size = ( ( size + 31 ) & ~31 ); 
	DCFlushRange ( framecount, size );
	framecount_size = size;
	framecount_active = 1;
	ARQPostRequest	(	&framecount_request,
						0x55555555,											// Owner.
						ARQ_TYPE_ARAM_TO_MRAM,								// Type.
						ARQ_PRIORITY_HIGH,									// Priority.
						p_source_base,										// Source.
						(uint32)framecount,									// Dest.
						size,												// Length.
						arqCallback );										// Callback
}

//void dma_partial( uint32 p_source_base )
//{
//	while ( framecount_active );
//	// DMA the count info.
//	DCFlushRange ( partial, 32 );
//	framecount_size = 32;
//	framecount_active = 1;
//	ARQPostRequest	(	&framecount_request,
//						0x55555555,											// Owner.
//						ARQ_TYPE_ARAM_TO_MRAM,								// Type.
//						ARQ_PRIORITY_HIGH,									// Priority.
//						p_source_base,										// Source.
//						(uint32)partial,									// Dest.
//						32,													// Length.
//						arqCallback );										// Callback
//}

#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool InitQ48Table( const char* pFileName, bool assertOnFail )
{
	bool success = false;
	
	// open the file as a stream
	void* pStream = File::Open( pFileName, "rb" );
	
    // make sure the file is valid
	if ( !pStream )
	{
		Dbg_MsgAssert( !assertOnFail, ("Load of %s failed - file not found?", pFileName) );
		goto exit;
	}

	success = init_48_table( pStream, &sQTable[0] );

exit:
	Dbg_MsgAssert( success, ("Parse of %s failed", pFileName) );
	File::Close( pStream );
	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool InitT48Table( const char* pFileName, bool assertOnFail )
{
	bool success = false;

	// open the file as a stream
	void* pStream = File::Open( pFileName, "rb" );

	// make sure the file is valid
	if ( !pStream )
	{
		Dbg_MsgAssert( assertOnFail, ("Load of %s failed - file not found?", pFileName) );
		goto exit;
	}

	success = init_48_table( pStream, &sTTable[0] );

	//Dbg_Assert( 0 );

exit:
	Dbg_MsgAssert( success, ("Parse of %s failed", pFileName) );
	File::Close( pStream );	
	return success;
}

/******************************************************************/
/*                                                                 */
/*                                                                */
/******************************************************************/

inline float timeDown(short theSource)
{
    return (float)theSource;
}	  

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline short timeUp(float theSource)
{
	short retVal = (short)((theSource * 60.0f) + 0.5f);

    return retVal;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline short transUp(float theSource, float scaleFactor)
{
    return (short)(theSource * scaleFactor);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float transDown(short theSource, float scaleFactor)
{
    return (float)(theSource / scaleFactor);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline short quatUp(float theSource)
{
    return (short)(theSource * 16384.0f);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float quatDown(short theSource)
{
    return (float)(theSource / 16384.0f);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void get_rotation_from_key( CAnimQKey* p_in, Mth::Quat* pQuat, bool isHiRes )
{
	if ( isHiRes )
	{
		(*pQuat)[X] = ((CHiResAnimQKey*)p_in)->qx;
		(*pQuat)[Y] = ((CHiResAnimQKey*)p_in)->qy;
		(*pQuat)[Z] = ((CHiResAnimQKey*)p_in)->qz;
//  	(*pQuat)[W] = ((CHiResAnimQKey*)p_in)->qw;
	}
	else
	{
		(*pQuat)[X] = quatDown( ((CStandardAnimQKey*)p_in)->qx );
		(*pQuat)[Y] = quatDown( ((CStandardAnimQKey*)p_in)->qy );
		(*pQuat)[Z] = quatDown( ((CStandardAnimQKey*)p_in)->qz );
//		(*pQuat)[W] = quatDown( ((CStandardAnimQKey*)p_in)->qw );
	}
	
	float qx = (*pQuat)[X];
	float qy = (*pQuat)[Y];
	float qz = (*pQuat)[Z];

	// Dave note: added 09/12/02 - a simple check to ensure we don't try to take the square root of a negative
	// number, which will hose Nan-sensitive platforms later on...
	float sum	= 1.0f - qx * qx - qy * qy - qz * qz;
	(*pQuat)[W] = sqrtf(( sum >= 0.0f ) ? sum : 0.0f );
	
	if ( p_in->signBit )
	{
		(*pQuat)[W] = -(*pQuat)[W];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void set_key_from_rotation( CAnimQKey* p_in, float x, float y, float z, float w, bool isHiRes )
{
	if ( isHiRes )
	{
		((CHiResAnimQKey*)p_in)->qx = x;
		((CHiResAnimQKey*)p_in)->qy = y;
		((CHiResAnimQKey*)p_in)->qz = z;
//		((CHiResAnimQKey*)p_in)->qw = w;
		((CHiResAnimQKey*)p_in)->signBit = ( w < 0.0f ) ? 1 : 0;
	}
	else
	{
		((CStandardAnimQKey*)p_in)->qx = quatUp( x );
		((CStandardAnimQKey*)p_in)->qy = quatUp( y );
		((CStandardAnimQKey*)p_in)->qz = quatUp( z );
//		((CStandardAnimQKey*)p_in)->qw = quatUp( w );
		((CStandardAnimQKey*)p_in)->signBit = ( w < 0.0f ) ? 1 : 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void get_translation_from_key( CAnimTKey* p_in, Mth::Vector* pVector, bool isHiRes )
{
	if ( isHiRes )
	{
		(*pVector)[X] = ((CHiResAnimTKey*)p_in)->tx;
		(*pVector)[Y] = ((CHiResAnimTKey*)p_in)->ty;
		(*pVector)[Z] = ((CHiResAnimTKey*)p_in)->tz;
		(*pVector)[W] = 1.0f;
	}
	else
	{
		(*pVector)[X] = transDown( ((CStandardAnimTKey*)p_in)->tx, 32.0f );
		(*pVector)[Y] = transDown( ((CStandardAnimTKey*)p_in)->ty, 32.0f );
		(*pVector)[Z] = transDown( ((CStandardAnimTKey*)p_in)->tz, 32.0f );
		(*pVector)[W] = 1.0f;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline void get_rotation_from_standard_key( CStandardAnimQKey* p_in, Mth::Quat* pQuat )
{
	if ( p_in->qx == 0 && p_in->qy == 0 && p_in->qz == 0 )
	{
		// Optimization: this handles the identity rotation, which is so common that it's not
		// worth doing a square root...
		pQuat->SetVector( 0.0f, 0.0f, 0.0f );
		pQuat->SetScalar( 1.0f );
		return;
	}
		
	float qx = quatDown( p_in->qx );
	float qy = quatDown( p_in->qy );
	float qz = quatDown( p_in->qz );

	// Dave note: added 09/12/02 - a simple check to ensure we don't try
	// to take the square root of a negative number, which will hose 
	// Nan-sensitive platforms later on...
	float sum = 1.0f - qx * qx - qy * qy - qz * qz;
	
// This assert was firing off in one of the cutscenes...
// probably worth looking into later...
//	Dbg_Assert( sum >= 0.0f );
	if ( sum < 0.0f )
	{
		sum = 0.0f;
	}

	(*pQuat)[X] = qx;
	(*pQuat)[Y] = qy;
	(*pQuat)[Z] = qz;
	(*pQuat)[W] = ( p_in->signBit ) ? -sqrtf( sum ) : sqrtf( sum );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void get_translation_from_standard_key( CStandardAnimTKey* p_in, Mth::Vector* pVector )
{
	(*pVector)[X] = transDown( p_in->tx, 32.0f );
	(*pVector)[Y] = transDown( p_in->ty, 32.0f );
	(*pVector)[Z] = transDown( p_in->tz, 32.0f );
	(*pVector)[W] = 1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
						  
inline void set_key_from_translation( CAnimTKey* p_in, float x, float y, float z, bool isHiRes )
{
	if ( isHiRes )
	{
		((CHiResAnimTKey*)p_in)->tx = x;
		((CHiResAnimTKey*)p_in)->ty = y;
		((CHiResAnimTKey*)p_in)->tz = z;
	}
	else
	{
		((CStandardAnimTKey*)p_in)->tx = transUp( x, 32.0f );
		((CStandardAnimTKey*)p_in)->ty = transUp( y, 32.0f );
		((CStandardAnimTKey*)p_in)->tz = transUp( z, 32.0f );
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline void interpolate_q_frame(Mth::Quat* p_out, CAnimQKey* p_in1, CAnimQKey* p_in2, float alpha, bool isHiRes)
{
	if ( alpha == 0.0f )
	{
		// don't need to slerp, because it's the start time
		get_rotation_from_key( p_in1, p_out, isHiRes );
		return;
	}

	if ( alpha == 1.0f )
	{
		// don't need to slerp, because it's the end time
		get_rotation_from_key( p_in2, p_out, isHiRes );
		return;
	}
	
	// INTERPOLATE Q-COMPONENT

	Mth::Quat	qIn1;
	Mth::Quat   qIn2;
	
	get_rotation_from_key( p_in1, &qIn1, isHiRes );
	get_rotation_from_key( p_in2, &qIn2, isHiRes );
    
	// Faster slerp, stolen from game developer magazine.
	*p_out = Mth::FastSlerp( qIn1, qIn2, alpha );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void interpolate_t_frame(Mth::Vector* p_out, CAnimTKey* p_in1, CAnimTKey* p_in2, float alpha, bool isHiRes)
{
	if ( alpha == 0.0f )
	{
		// don't need to lerp, because it's the start time
		get_translation_from_key( p_in1, p_out, isHiRes );
		return;
	}

	if ( alpha == 1.0f )
	{
		// don't need to slerp, because it's the end time
		get_translation_from_key( p_in2, p_out, isHiRes );
		return;
	}

	// INTERPOLATE T-COMPONENT

    Mth::Vector   tIn1;					  
	Mth::Vector   tIn2;
	
	get_translation_from_key( p_in1, &tIn1, isHiRes );
	
	get_translation_from_key( p_in2, &tIn2, isHiRes );

    /* Linearly interpolate positions */
    *p_out = Mth::Lerp( tIn1, tIn2, alpha );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
						  
inline void interpolate_standard_q_frame(Mth::Quat* p_out, CStandardAnimQKey* p_in1, CStandardAnimQKey* p_in2, float alpha)
{
	if ( alpha == 0.0f )
	{
		// don't need to slerp, because it's the start time
		get_rotation_from_standard_key( p_in1, p_out );
		return;
	}

	if ( alpha == 1.0f )
	{
		// don't need to slerp, because it's the end time
		get_rotation_from_standard_key( p_in2, p_out );
		return;
	}
	
	// INTERPOLATE Q-COMPONENT
	Mth::Quat	qIn1;
	Mth::Quat   qIn2;
	get_rotation_from_standard_key( p_in1, &qIn1 );
	get_rotation_from_standard_key( p_in2, &qIn2 );

	// Faster slerp, stolen from game developer magazine.
	*p_out = Mth::FastSlerp( qIn1, qIn2, alpha );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void interpolate_standard_t_frame(Mth::Vector* p_out, CStandardAnimTKey* p_in1, CStandardAnimTKey* p_in2, float alpha)
{
	if ( alpha == 0.0f )
	{
		// don't need to lerp, because it's the start time
		get_translation_from_standard_key( p_in1, p_out );
		return;
	}

	if ( alpha == 1.0f )
	{
		// don't need to slerp, because it's the end time
		get_translation_from_standard_key( p_in2, p_out );
		return;
	}

	// INTERPOLATE T-COMPONENT

    Mth::Vector   tIn1;					  
	Mth::Vector   tIn2;
	
	get_translation_from_standard_key( p_in1, &tIn1 );
	
	get_translation_from_standard_key( p_in2, &tIn2 );

    /* Linearly interpolate positions */
    *p_out = Mth::Lerp( tIn1, tIn2, alpha );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// eventually, this will go in the plat-specific version of CBonedAnimFrameData

bool CBonedAnimFrameData::plat_dma_to_aram( int qbytes, int tbytes, uint32 flags )
{
	// GameCube: DMA to ARAM.
#ifdef __ARAM__

	if ( qbytes && tbytes )
	{
		uint32 address;
		uint size;
//   	uint32 header[8];

//		// Want this to happen even if asserts turned off.
//		if ( qbytes > (ARAM_CACHE_SIZE) )
//		{
//			OSReport( "Too many q keys (%d)!!!\n", qbytes );
//			while (1);
//		}
//
//		if ( tbytes > (ARAM_CACHE_SIZE) )
//		{
//			OSReport( "Too many t keys (%d)!!!\n", tbytes );
//			while (1);
//		}
//
//		if ( m_numBones > MAX_BONE_COUNT )
//		{
//			OSReport( "Too many bones (%d)!!!\n", m_numBones );
//			while (1);
//		}

//		// DMA partial animation data.
//		if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
//		{
//			address = NsARAM::alloc( 32 );
//			NsDMA::toARAM( address, mp_partialAnimData, 32 );
//			mp_partialAnimData = (uint32*)address;
//		}

		// DMA per-bone frame count data.
		size = m_numBones * sizeof(uint16) * 2;
		memcpy( qqq, mp_perBoneQFrameSize, size );
		int part_size = 0;
		int frame_size = size;
		if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
		{
			part_size = ( 1 + (( *mp_partialAnimData - 1 )/ 32) + 1 ) * sizeof( uint32 );
			memcpy( &qqq[size], mp_partialAnimData, part_size );
			size += part_size;
		}
		size = ( ( size + 31 ) & ~31 ); 
		address = NsARAM::alloc( size );
		NsDMA::toARAM( address, qqq, size );

		mp_perBoneQFrameSize = (uint16*)address;
		mp_perBoneTFrameSize = (uint16*)address;
		if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
		{
			mp_partialAnimData = (uint32*)( address + frame_size );
		}

//		size = m_numBones * sizeof(uint16) * 2;
//		size = ( ( size + 31 ) & ~31 ); 
//		address = NsARAM::alloc( size );
//		NsDMA::toARAM( address, mp_perBoneQFrameSize, size );
//
//		mp_perBoneQFrameSize = (uint16*)address;
//		mp_perBoneTFrameSize = (uint16*)address;

		// DMA Q frames to ARAM, delete RAM version & assign ARAM pointer.
		size = qbytes/* + 32*/;
		size = ( ( size + 31 ) & ~31 ); 
//		header[0] = size;
		address = NsARAM::alloc( size );
//		NsDMA::toARAM( address, header, 32 );
		NsDMA::toARAM( address/*+32*/, mp_qFrames, size );
//		delete mp_qFrames;
		mp_qFrames = (char*)address;

		// DMA T frames to ARAM, delete RAM version & assign ARAM pointer.
		size = tbytes/* + 32*/;
		size = ( ( size + 31 ) & ~31 ); 
//		header[0] = size;
		address = NsARAM::alloc( size );
//		NsDMA::toARAM( address, header, 32 );
		NsDMA::toARAM( address/*+32*/, mp_tFrames, size );
//		delete mp_tFrames;
		mp_tFrames = (char*)address;

		// hijack this pointer as it won't be used.
		mp_boneNames = (uint32*)( ( qbytes << 16 ) | tbytes );
	}
	else
	{
		if ( is_hires() )
		{
			uint32 address;
			uint size;

//			// Want this to happen even if asserts turned off.
//			if ( ( m_num_qFrames * sizeof( CHiResAnimQKey ) ) > (ARAM_CACHE_SIZE) )
//			{
//				OSReport( "Too many q keys (%d)!!!\n", m_num_qFrames );
//				while (1);
//			}
//
//			if ( ( m_num_tFrames * sizeof( CHiResAnimTKey ) ) > (ARAM_CACHE_SIZE) )
//			{
//				OSReport( "Too many t keys (%d)!!!\n", m_num_tFrames );
//				while (1);
//			}
//

//			// DMA partial animation data.
//			if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
//			{
//				uint32 * pData = mp_partialAnimData;
//				int numBones = *pData;
//
//				int numMasks = (( numBones - 1 )/ 32) + 1;		
//				size = sizeof( int ) + ( numMasks * sizeof( uint32 ) ); 
//
//				header[0] = size;
//				address = NsARAM::alloc( size );
//				NsDMA::toARAM( address, header, 32 );
//				NsDMA::toARAM( address+32, mp_partialAnimData, size );
//				mp_partialAnimData = (char*)address;
//			}
			mp_partialAnimData = NULL;

			// DMA per-bone frame count data.
			void * ptr;
			if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA )
			{
				size = ( m_numBones * sizeof(uint32) );
				ptr = mp_boneNames;
			}
			else
			{
				size = 0;
				ptr = mp_perBoneFrames;
			}

			if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS )
			{
				size += ( m_numBones * sizeof(SHiResAnimFramePointers) );
			}
			else
			{
				size += ( m_numBones * sizeof(SStandardAnimFramePointers) );
			}

			if ( size > ( MAX_BONE_COUNT * 2 * sizeof(uint16) ) )
			{
				OSReport( "Too many bones (%d)!!!\n", m_numBones );
				while (1);
			}

			size = ( ( size + 31 ) & ~31 ); 
			address = NsARAM::alloc( size );
			NsDMA::toARAM( address, ptr, size );

			mp_perBoneFrames = (uint16*)address;
			mp_perBoneQFrameSize = (uint16*)address;		// So that it can be deallocated.

			// DMA Q frames to ARAM, delete RAM version & assign ARAM pointer.
//			Dbg_MsgAssert( m_num_qFrames <= (ARAM_CACHE_SIZE), ( "Too many hires Q keys (%d) - maximum is %d", m_num_qFrames, (ARAM_CACHE_SIZE) ) );
			size = sizeof( CHiResAnimQKey ) * m_num_qFrames;
			size = ( ( size + 31 ) & ~31 ); 
			address = NsARAM::alloc( size );
			NsDMA::toARAM( address, mp_qFrames, size );
//			delete mp_qFrames;
			mp_qFrames = (char*)address;

			// DMA T frames to ARAM, delete RAM version & assign ARAM pointer.
//			Dbg_MsgAssert( m_num_tFrames <= (ARAM_CACHE_SIZE), ( "Too many hires T keys (%d) - maximum is %d", m_num_tFrames, (ARAM_CACHE_SIZE) ) );
			size = sizeof( CHiResAnimTKey ) * m_num_tFrames;
			size = ( ( size + 31 ) & ~31 ); 
			address = NsARAM::alloc( size );
			NsDMA::toARAM( address, mp_tFrames, size );
//			delete mp_tFrames;
			mp_tFrames = (char*)address;
		}
		else
		{
			uint32 address;
			uint size;

//			// Want this to happen even if asserts turned off.
//			if ( ( m_num_qFrames * sizeof( CStandardAnimQKey ) ) > (ARAM_CACHE_SIZE) )
//			{
//				OSReport( "Too many q keys (%d)!!!\n", m_num_qFrames );
//				while (1);
//			}
//
//			if ( ( m_num_tFrames * sizeof( CStandardAnimTKey ) ) > (ARAM_CACHE_SIZE) )
//			{
//				OSReport( "Too many t keys (%d)!!!\n", m_num_tFrames );
//				while (1);
//			}
//

//			// DMA partial animation data.
//			if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
//			{
//				uint32 * pData = mp_partialAnimData;
//				int numBones = *pData;
//
//				int numMasks = (( numBones - 1 )/ 32) + 1;		
//				size = sizeof( int ) + ( numMasks * sizeof( uint32 ) ); 
//
//				header[0] = size;
//				address = NsARAM::alloc( size );
//				NsDMA::toARAM( address, header, 32 );
//				NsDMA::toARAM( address+32, mp_partialAnimData, size );
//				mp_partialAnimData = (char*)address;
//			}
			mp_partialAnimData = NULL;

			// DMA per-bone frame count data.
			void * ptr;
			if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA )
			{
				size = ( m_numBones * sizeof(uint32) );
				ptr = mp_boneNames;
			}
			else
			{
				size = 0;
				ptr = mp_perBoneFrames;
			}

			if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS )
			{
				size += ( m_numBones * sizeof(SHiResAnimFramePointers) );
			}
			else
			{
				size += ( m_numBones * sizeof(SStandardAnimFramePointers) );
			}

			if ( size > ( MAX_BONE_COUNT * 2 * sizeof(uint16) ) )
			{
				OSReport( "Too many bones (%d)!!!\n", m_numBones );
				while (1);
			}

			size = ( ( size + 31 ) & ~31 ); 
			address = NsARAM::alloc( size );
			NsDMA::toARAM( address, ptr, size );

			mp_perBoneFrames = (uint16*)address;
			mp_perBoneQFrameSize = (uint16*)address;		// So that it can be deallocated.

			// DMA Q frames to ARAM, delete RAM version & assign ARAM pointer.
//			Dbg_MsgAssert( m_num_qFrames <= (short)MAX_STANDARD_Q, ( "Too many standard Q keys (%d) - maximum is %d", m_num_qFrames, MAX_STANDARD_Q ) );
			size = sizeof( CStandardAnimQKey ) * m_num_qFrames;
			size = ( ( size + 31 ) & ~31 ); 
			address = NsARAM::alloc( size );
			NsDMA::toARAM( address, mp_qFrames, size );
//			delete mp_qFrames;
			mp_qFrames = (char*)address;

			// DMA T frames to ARAM, delete RAM version & assign ARAM pointer.
//			Dbg_MsgAssert( m_num_tFrames <= (short)MAX_STANDARD_T, ( "Too many standard T keys (%d) - maximum is %d", m_num_tFrames, MAX_STANDARD_T ) );
			size = sizeof( CStandardAnimTKey ) * m_num_tFrames;
			size = ( ( size + 31 ) & ~31 ); 
			address = NsARAM::alloc( size );
			NsDMA::toARAM( address, mp_tFrames, size );
//			delete mp_tFrames;
			mp_tFrames = (char*)address;
		}
	}

#endif		// __ARAM__
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CBonedAnimFrameData::plat_read_compressed_stream(uint8* pData, bool delete_buffer)
{
	Dbg_Assert( pData );

	SPlatformFileHeader *pThePlatformHeader = (SPlatformFileHeader *) pData;
	pData += sizeof(SPlatformFileHeader);

	m_numBones = pThePlatformHeader->numBones;
	m_num_qFrames = pThePlatformHeader->numQKeys;
	m_num_tFrames = pThePlatformHeader->numTKeys;
	
	uint32 qAllocSize = *((uint32*)pData);
	pData += sizeof(uint32);

	uint32 tAllocSize = *((uint32*)pData);
	pData += sizeof(uint32);

	mp_perBoneQFrameSize = (uint16*)pData;
	pData += ( m_numBones * sizeof(uint16) );
	
	mp_perBoneTFrameSize = (uint16*)pData;
	pData += ( m_numBones * sizeof(uint16) );

	// long-align
	if ( (uint32)pData & 0x3 )
	{
		pData += ( 4 - ((uint32)pData & 0x3) );
	}
	
	// first read in object anim bone names, if any
	if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA )
	{
		Dbg_MsgAssert( 0, ( "Wasn't expecting object anims to be compressed" ) );
		mp_boneNames = NULL;
	}
	else
	{
		mp_boneNames = NULL;
	}
	
	if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
	{
		Dbg_Assert(!((uint32)pData & 0x3));
		
		mp_partialAnimData = (uint32*)pData;
		
		// skip original number of bones
		int numBones = *((uint32*)pData);
		pData += sizeof( int );
		
		int numMasks = (( numBones - 1 )/ 32) + 1;		
		pData += ( numMasks * sizeof( uint32 ) );
	}

	// long-align
	if ( (uint32)pData & 0x3 )
	{
		pData += ( 4 - ((uint32)pData & 0x3) );
	}
		
	if ( is_hires() )
	{
		Dbg_MsgAssert( !is_hires(), ( "Hi res format not supported for compressed anims" ) );
	}
	else
	{
		mp_qFrames = (char*)pData;
		pData += qAllocSize;
		
		mp_tFrames = (char*)pData;
		pData += tAllocSize;
	}
	
	Dbg_Assert( mp_perBoneFrames == NULL );
	Dbg_Assert( mp_qFrames );
	Dbg_Assert( mp_tFrames );
    
	// dma to aram here...
	plat_dma_to_aram( qAllocSize, tAllocSize );

	// GJ:  be able to PIP the custom keys in as well

	// long-align
	if ( (uint32)pData & 0x3 )
	{
		pData += ( 4 - ((uint32)pData & 0x3) );
	}
   
	// create an array of pointers to the custom keys
	if ( !pThePlatformHeader->numCustomAnimKeys )
	{
		mpp_customAnimKeyList = NULL;
	}
	else
	{
		mpp_customAnimKeyList = (CCustomAnimKey **)(Mem::Malloc(pThePlatformHeader->numCustomAnimKeys*sizeof(CCustomAnimKey *)));
		
		// read custom keys
		for ( uint32 i = 0; i < pThePlatformHeader->numCustomAnimKeys; i++ )
		{
			CCustomAnimKey* pKey = ReadCustomAnimKey( &pData );
			if ( !pKey )
			{
				Dbg_Message( "Failed while reading custom data" );
				return false;
			}		
			mpp_customAnimKeyList[i] = pKey;
		}
	}		

	m_num_customKeys = pThePlatformHeader->numCustomAnimKeys;

#ifdef __ARAM__
	if ( delete_buffer )
	{
		delete mp_fileBuffer;
	}
	mp_fileBuffer = NULL;
#endif
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBonedAnimFrameData::plat_read_stream(uint8* pData, bool delete_buffer)
{
	Dbg_Assert( pData );

	SPlatformFileHeader* pThePlatformHeader = (SPlatformFileHeader*)pData;
	pData += sizeof(SPlatformFileHeader);
	Dbg_Assert(!((uint) pData & 0x3));

	m_numBones = pThePlatformHeader->numBones;
	m_num_qFrames = pThePlatformHeader->numQKeys;
	m_num_tFrames = pThePlatformHeader->numTKeys;
	
	Dbg_Assert( !mp_perBoneFrames );
	Dbg_Assert( !mp_qFrames );
	Dbg_Assert( !mp_tFrames );
	
	// first read in object anim bone names, if any
	if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA )
	{
		Dbg_Assert(!((uint) pData & 0x3));
		mp_boneNames = (uint32*)pData;
		pData += ( m_numBones * sizeof(uint32) );
	}
	else
	{
		mp_boneNames = NULL;
	}

	if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
	{
		Dbg_Assert(!((uint32)pData & 0x3));
		
		mp_partialAnimData = (uint32*)pData;
		
		// skip original number of bones
		int numBones = *((uint32*)pData);
		pData += sizeof( int );
		
		int numMasks = (( numBones - 1 )/ 32) + 1;		
		pData += ( numMasks * sizeof( uint32 ) );
	}

	// now, read in the per-bone frames of the correct size
	if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS )
	{
		mp_perBoneFrames = pData;
		pData += ( m_numBones * sizeof(SHiResAnimFramePointers) );		
	}
	else
	{
		mp_perBoneFrames = pData;
		pData += ( m_numBones * sizeof(SStandardAnimFramePointers) );		
	}

	// long-align
	if ( (uint32)pData & 0x3 )
	{
		pData += ( 4 - ((uint32)pData & 0x3) );
	}
   
	// count to make sure the number of keys per bone didn't overflow
	int runningQCount = 0;
	int runningTCount = 0;

	for ( int i = 0; i < m_numBones; i++ )
	{
		runningQCount += get_num_qkeys( mp_perBoneFrames, i );
		runningTCount += get_num_tkeys( mp_perBoneFrames, i );
	}

	Dbg_MsgAssert( runningQCount == m_num_qFrames, ( "Wrong number of qframes in %x %d %d", m_fileNameCRC, runningQCount, m_num_qFrames ) );
	Dbg_MsgAssert( runningTCount == m_num_tFrames, ( "Wrong number of tframes in %x %d %d", m_fileNameCRC, runningTCount, m_num_tFrames ) );

	if ( is_hires() )
	{
		Dbg_Assert(!((uint) pData & 0x3));
		mp_qFrames = (char*)pData;
		pData += ( m_num_qFrames * sizeof(CHiResAnimQKey) );
		
		Dbg_Assert(!((uint) pData & 0x3));
		mp_tFrames = (char*)pData;
		pData += ( m_num_tFrames * sizeof(CHiResAnimTKey) );
	}
	else
	{
		Dbg_Assert(!((uint) pData & 0x3));
		mp_qFrames = (char*)pData;
		pData += ( m_num_qFrames * sizeof(CStandardAnimQKey) );
		
		Dbg_Assert(!((uint) pData & 0x3));
		mp_tFrames = (char*)pData;
		pData += ( m_num_tFrames * sizeof(CStandardAnimTKey) );
	}
	
	Dbg_Assert( mp_perBoneFrames );
	Dbg_Assert( mp_qFrames );
	Dbg_Assert( mp_tFrames );
    
	// dma to aram here...
	plat_dma_to_aram( 0, 0, m_flags );

	// GJ:  be able to PIP the custom keys in as well

	// long-align
	if ( (uint32)pData & 0x3 )
	{
		pData += ( 4 - ((uint32)pData & 0x3) );
	}
   
	// create an array of pointers to the custom keys
	if ( !pThePlatformHeader->numCustomAnimKeys )
	{
		mpp_customAnimKeyList = NULL;
	}
	else
	{
		mpp_customAnimKeyList = (CCustomAnimKey **)(Mem::Malloc(pThePlatformHeader->numCustomAnimKeys*sizeof(CCustomAnimKey *)));
		
		// read custom keys
		for ( uint32 i = 0; i < pThePlatformHeader->numCustomAnimKeys; i++ )
		{
			CCustomAnimKey* pKey = ReadCustomAnimKey( &pData );
			if ( !pKey )
			{
				Dbg_Message( "Failed while reading custom data" );
				return false;
			}		
			mpp_customAnimKeyList[i] =  pKey;
		}
	}		

	m_num_customKeys = pThePlatformHeader->numCustomAnimKeys;

#ifdef __ARAM__
	if ( delete_buffer )
	{
		delete mp_fileBuffer;
	}
	mp_fileBuffer = NULL;
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CBonedAnimFrameData::get_num_qkeys( void * p_frame_data, int boneIndex ) const
{
	if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS )
	{
		SHiResAnimFramePointers* pFramePointers = (SHiResAnimFramePointers*)p_frame_data;
		
		pFramePointers += boneIndex;

		return pFramePointers->numQKeys;
	}
	else
	{
		SStandardAnimFramePointers* pFramePointers = (SStandardAnimFramePointers*)p_frame_data;
		
		pFramePointers += boneIndex;

		return pFramePointers->numQKeys;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CBonedAnimFrameData::get_num_tkeys( void * p_frame_data, int boneIndex ) const
{
	if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS )
	{
		SHiResAnimFramePointers* pFramePointers = (SHiResAnimFramePointers*)p_frame_data;
		
		pFramePointers += boneIndex;

		return pFramePointers->numTKeys;
	}
	else
	{
		SStandardAnimFramePointers* pFramePointers = (SStandardAnimFramePointers*)p_frame_data;
		
		pFramePointers += boneIndex;

		return pFramePointers->numTKeys;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CBonedAnimFrameData::is_hires() const
{
	return ( m_flags & nxBONEDANIMFLAGS_CAMERADATA ) || ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA );
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBonedAnimFrameData::CBonedAnimFrameData()
{
	m_duration = 0.0f;
	m_numBones = 0;

	mp_fileBuffer = NULL;
	mp_fileHandle = NULL;
	m_dataLoaded = false;

	mp_qFrames = NULL;	
	mp_tFrames = NULL;

	mp_perBoneFrames = NULL;
	mp_boneNames = NULL;

	mp_perBoneQFrameSize = NULL;
	mp_perBoneTFrameSize = NULL;

	m_printDebugInfo = false;

	m_num_customKeys = 0;

	mpp_customAnimKeyList = NULL;
	m_fileNameCRC = 0;
	m_pipped = false;

	mp_partialAnimData = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBonedAnimFrameData::~CBonedAnimFrameData()
{

	for (int i=0;iGetFileSize();

		Dbg_MsgAssert(file_size, ("Anim file size is 0"));

		Dbg_Assert(!mp_fileBuffer);
//		Now that we're pipping it, we don't have to force this on the top-down heap
//		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
		mp_fileBuffer = (void *) Mem::Malloc( file_size );
//		Mem::Manager::sHandle().PopContext();

		// Set the callback
		mp_fileHandle->SetCallback(async_callback, (unsigned int) this, (unsigned int) assertOnFail);

		// read the file in
		mp_fileHandle->Read( mp_fileBuffer, 1, file_size );
		//Dbg_Message("Started read of %x", this);

		//mp_fileHandle->WaitForIO();
		//Dbg_Message("Done waiting for %x", this);

		// Should be the callback
		return true; //PostLoad(assertOnFail, file_size);
	}
	else
	{
		int file_size = 0;

		if ( use_pip )
		{
			mp_fileBuffer = Pip::Load( p_fileName );
			file_size = Pip::GetFileSize( p_fileName );
			Dbg_MsgAssert(file_size, ("Anim file size is 0"));
			m_pipped = true;
		}
		else
		{
			// open the file as a stream
			void* pStream = File::Open( p_fileName,"rb" );
			// make sure the file is valid
			if ( !pStream )
			{
				Dbg_MsgAssert( assertOnFail, ("Load of %s failed - file not found?", p_fileName) );
				return false;
			}

			file_size = File::GetFileSize(pStream);
			Dbg_MsgAssert(file_size, ("Anim file size is 0"));
			Dbg_Assert(!mp_fileBuffer);
	//		Now that we're pipping it, we don't have to force this on the top-down heap
	//		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
			mp_fileBuffer = (void *) Mem::Malloc( file_size );
	//		Mem::Manager::sHandle().PopContext();

			// read the file in
			if ( !File::Read( mp_fileBuffer, 1, file_size, pStream ) )
			{
				Mem::Free(mp_fileBuffer);
				mp_fileBuffer = NULL;
				File::Close( pStream );

				Dbg_MsgAssert( assertOnFail, ("Load of %s failed - bad format?", p_fileName) );
				return false;
			}
			
			File::Close(pStream);
		}

		return PostLoad(assertOnFail, file_size);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBonedAnimFrameData::async_callback(File::CAsyncFileHandle *, File::EAsyncFunctionType function,
										 int result, unsigned int arg0, unsigned int arg1)
{
	//Dbg_Message("Got callback from %x", arg0);
	if (function == File::FUNC_READ)
	{
		CBonedAnimFrameData *p_data = (CBonedAnimFrameData *) arg0;
		bool assert = (bool) arg1;

		p_data->PostLoad(assert, result);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBonedAnimFrameData::PostLoad(bool assertOnFail, int file_size, bool delete_buffer)
{
	bool success = false;

	//Dbg_Message("PostLoad of %x", this);

	// Handle end of async, if that was used
	if (mp_fileHandle)
	{
#if 0
		if (mp_fileHandle->WaitForIO() != file_size)
		{
			Mem::Free(mp_fileBuffer);
			mp_fileBuffer = NULL;
			File::CAsyncFileLoader::sClose( mp_fileHandle );
			mp_fileHandle = NULL;

			Dbg_MsgAssert( assertOnFail, ("PostLoad of anim file failed - bad format?") );
			return false;
		}
#endif
		File::CAsyncFileLoader::sClose( mp_fileHandle );
		mp_fileHandle = NULL;
	}

	uint8 *pFileData = (uint8 *) mp_fileBuffer;

	// read in header information
	SBonedAnimFileHeader* pTheHeader = (SBonedAnimFileHeader*)pFileData;
	pFileData += sizeof(SBonedAnimFileHeader);

	// store header information
	m_duration = pTheHeader->duration;

	m_flags = pTheHeader->flags;

	// trying to phase out the old partial anim format here...
	Dbg_MsgAssert( ( m_flags & nxBONEDANIMFLAGS_OLDPARTIALANIM ) == 0, ( "No longer supporting old partial anim format" ) ); 

	// the anim converter should automatically do this,
	// so that we don't have to do it at runtime...
	Dbg_MsgAssert( pTheHeader->flags & nxBONEDANIMFLAGS_COMPRESSEDTIME, ( "Expected the anim times to be in frames" ) );
	Dbg_MsgAssert( pTheHeader->flags & nxBONEDANIMFLAGS_PREROTATEDROOT, ( "Expected the root bones to be prerotated" ) );

	// some debugging information
//	Dbg_Message( "Loading animation %s", p_fileName );
//	Dbg_Message( "Duration = %f", m_duration );
//	Dbg_Message( "Flags = %08x", theHeader.flags );

	// read the stream differently based on whether it's compressed...
    if ( pTheHeader->flags & nxBONEDANIMFLAGS_PLATFORM )
    {
		Dbg_Assert( (pTheHeader->flags & nxBONEDANIMFLAGS_USECOMPRESSTABLE) == 0 );
		success = this->plat_read_stream( pFileData, delete_buffer );
    }
    else if ( pTheHeader->flags & nxBONEDANIMFLAGS_USECOMPRESSTABLE )
    {
		Dbg_Assert( (pTheHeader->flags & nxBONEDANIMFLAGS_PLATFORM) == 0 );
		success = this->plat_read_compressed_stream( pFileData, delete_buffer );
    }
    else
    {
        Dbg_MsgAssert( 0, ("Unrecognized file format (flags = %08x)...", pTheHeader->flags) );
    }

	// handle failure
	Dbg_MsgAssert( success || !assertOnFail, ("PostLoad of anim file failed - bad format?") );

	if ( !m_pipped )
	{
	// Done with temp buffer (since it's pipped, don't delete the file buffer)
//	Mem::Free(mp_fileBuffer);
//	mp_fileBuffer = NULL;
	}
	m_dataLoaded = success;

    return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBonedAnimFrameData::IsValidTime( float time )
{
	return ( time >= 0.0f && time < m_duration );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float get_alpha( float timeStamp1, float timeStamp2, float time )
{
	Dbg_MsgAssert(timeStamp1 <= time && timeStamp2 >= time, ( "%f should be within [%f %f]", time, timeStamp1, timeStamp2 ));
    
	return (( time - timeStamp1 ) / ( timeStamp2 - timeStamp1 ));
}

// THE FOLLOWING 2 FUNCTIONS SHOULD BE REFACTORED?

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBonedAnimFrameData::GetInterpolatedCameraFrames(Mth::Quat* pRotations, Mth::Vector* pTranslations, float time, Nx::CQuickAnim* pQuickAnim )
{
    Dbg_Assert( pRotations );
    Dbg_Assert( pTranslations );

	Dbg_Assert( is_hires() );
	
	float qAlpha = 0.0f;
	float tAlpha = 0.0f;

	// DMA the animation data here.
#ifdef __ARAM__
	int		size;
	if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA )
	{
		size = ( m_numBones * sizeof(uint32) );
	}
	else
	{
		size = 0;
	}

	if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS )
	{
		size += ( m_numBones * sizeof(SHiResAnimFramePointers) );
	}
	else
	{
		size += ( m_numBones * sizeof(SStandardAnimFramePointers) );
	}
	dma_count( (uint32)mp_perBoneFrames, size );
	while ( framecount_active );
//	if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
//	{
//		dma_partial( (uint32)mp_partialAnimData, ( m_numBones * sizeof( uint16 ) * 2 ) );
//	}
//	while ( framecount_active );

//	NsDMA::toMRAM( qqq, (uint32)mp_qFrames, m_num_qFrames * sizeof( CHiResAnimQKey ) );
//	NsDMA::toMRAM( ttt, (uint32)mp_tFrames, m_num_tFrames * sizeof( CHiResAnimTKey ) );

//	while( framecount_active );
#endif		// __ARAM__

	// find the nearest 2 keyframes
	float qTimeStamp = time * 60.0f;
	float tTimeStamp = time * 60.0f;
	
	CHiResAnimQKey* pStartQFrame = NULL;
	CHiResAnimQKey* pEndQFrame = NULL;

	CHiResAnimTKey* pStartTFrame = NULL;
	CHiResAnimTKey* pEndTFrame = NULL;

	CHiResAnimQKey* pCurrentQFrame = NULL;
	CHiResAnimTKey* pCurrentTFrame = NULL;
	
#ifdef __ARAM__
//	pStartQFrame = p_hqqq;
//	pEndQFrame = p_hqqq;
//
//	pStartTFrame = p_httt;
//	pEndTFrame = p_httt;
//	
//	pCurrentQFrame = p_hqqq;
//	pCurrentTFrame = p_httt;

	uint32 anim_q_offset = (uint32)mp_qFrames;
	uint32 anim_t_offset = (uint32)mp_tFrames;

	void * p_bone_frames;
	if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA )
	{
		p_bone_frames = &framecount[m_numBones*2];
	}
	else
	{
		p_bone_frames = framecount;
	}

#else
	pStartQFrame = (CHiResAnimQKey*)mp_qFrames;
	pEndQFrame = pStartQFrame;
	pCurrentQFrame = pStartQFrame;

	pStartTFrame = (CHiResAnimTKey*)mp_tFrames;
	pEndTFrame = pStartTFrame;
	pCurrentTFrame = pStartTFrame;

	void * p_bone_frames = mp_perBoneFrames;
#endif		// __aram__

	for ( int i = 0; i < m_numBones; i++ )
	{
		int numQKeys = get_num_qkeys( p_bone_frames, i );
		int numTKeys = get_num_tkeys( p_bone_frames, i );

#ifdef __ARAM__
		int q_off = 0;
		{
			// DMA this q track.
			int aligned_off = ( anim_q_offset & ~31 );
			int size = ( ( ( sizeof( CHiResAnimQKey ) * numQKeys ) - ( aligned_off - anim_q_offset ) ) + 31 ) & ~31;
			DCFlushRange ( qqq, size );
			framecount_size = size;
			framecount_active = 1;
			ARQPostRequest	(	&framecount_request,
								0x55555555,											// Owner.
								ARQ_TYPE_ARAM_TO_MRAM,								// Type.
								ARQ_PRIORITY_HIGH,									// Priority.
								(u32)aligned_off,									// Source.
								(uint32)qqq,										// Dest.
								size,												// Length.
								arqCallback );										// Callback
			while ( framecount_active );
			q_off = anim_q_offset - aligned_off;

			pStartQFrame = (CHiResAnimQKey*)&qqq[q_off];
			pEndQFrame = (CHiResAnimQKey*)&qqq[q_off];
			pCurrentQFrame = (CHiResAnimQKey*)&qqq[q_off];
		}
#endif		// __ARAM__

		for ( int j = 0; j < numQKeys; j++ )
		{
			if ( j == (numQKeys-1) )
			{
				// last frame
				pStartQFrame = pCurrentQFrame + j;
				pEndQFrame = pCurrentQFrame + j;
				qAlpha = 0.0f;
				break;
			}
			else if ( qTimeStamp >= timeDown(pCurrentQFrame[j].timestamp) 
				 && qTimeStamp <= timeDown(pCurrentQFrame[j+1].timestamp) )
			{
				pStartQFrame = pCurrentQFrame + j;
				pEndQFrame = pCurrentQFrame + j + 1;
				qAlpha = get_alpha( timeDown(pStartQFrame->timestamp), timeDown(pEndQFrame->timestamp), qTimeStamp );
				break;
			}
		}

		interpolate_q_frame( pRotations, 
						 pStartQFrame, 
						 pEndQFrame,
						 qAlpha,
						 is_hires() );

#ifdef __ARAM__
		int t_off = 0;
		{
			// DMA this t track.
			int aligned_off = ( anim_t_offset & ~31 );
			int size = ( ( ( sizeof( CHiResAnimTKey ) * numTKeys ) - ( aligned_off - anim_t_offset ) ) + 31 ) & ~31;
			DCFlushRange ( qqq, size );
			framecount_size = size;
			framecount_active = 1;
			ARQPostRequest	(	&framecount_request,
								0x55555555,											// Owner.
								ARQ_TYPE_ARAM_TO_MRAM,								// Type.
								ARQ_PRIORITY_HIGH,									// Priority.
								(u32)aligned_off,									// Source.
								(uint32)qqq,										// Dest.
								size,												// Length.
								arqCallback );										// Callback
			while ( framecount_active );
			t_off = anim_t_offset - aligned_off;

			pStartTFrame = (CHiResAnimTKey*)&qqq[t_off];
			pEndTFrame = (CHiResAnimTKey*)&qqq[t_off];
			pCurrentTFrame = (CHiResAnimTKey*)&qqq[t_off];
		}
#endif		// __ARAM__

		for ( int j = 0; j < numTKeys; j++ )
		{
			if ( j == (numTKeys-1) )
			{
				// last frame
				pStartTFrame = pCurrentTFrame + j;
				pEndTFrame = pCurrentTFrame + j;
				tAlpha = 0.0f;
				break;
			}
			else if ( tTimeStamp >= timeDown(pCurrentTFrame[j].timestamp) 
				 && tTimeStamp <= timeDown(pCurrentTFrame[j+1].timestamp) )
			{
				pStartTFrame = pCurrentTFrame + j;
				pEndTFrame = pCurrentTFrame + j + 1;
				tAlpha = get_alpha( timeDown(pStartTFrame->timestamp), timeDown(pEndTFrame->timestamp), tTimeStamp );
				break;
			}
		}
		
		// theStartFrame and theEndFrame should contain the
		// two closest keyframes here.  now interpolate between them
		// TODO:  we might be able to cache some of this data...
		interpolate_t_frame( pTranslations, 
							 pStartTFrame, 
							 pEndTFrame,
							 tAlpha,
							 is_hires() );

#if 0
		if ( m_printDebugInfo )
		{
			// for debug info, if necessary
		}
#endif
		
#ifdef __ARAM__
		anim_q_offset += ( sizeof( CHiResAnimQKey ) * numQKeys );
		anim_t_offset += ( sizeof( CHiResAnimTKey ) * numTKeys );
#else
		pCurrentQFrame += numQKeys;
		pCurrentTFrame += numTKeys;
#endif	// __ARAM__
		pRotations++;
		pTranslations++;
	}

    return true;
}

#ifdef __ARAM__
inline int get_compressed_q_frame( char* pData, CStandardAnimQKey* pReturnData )
#else
inline char * get_compressed_q_frame( char* pData, CStandardAnimQKey* pReturnData )
#endif		// __ARAM__
{
	unsigned char* p_data = (unsigned char*)pData;

	short data = (short)(( p_data[1]<<8 ) | p_data[0] );
	pReturnData->timestamp = data & 0x3fff;
	pReturnData->signBit = data & 0x8000 ? 1 : 0;
	
	// skip the timestamp
	p_data += sizeof( short );

	if ( data & 0x4000 )
	{
		if ( !(data & 0x3800) )
		{
			unsigned char d = *p_data;

			// a char* defaults to a signed char*,
			// which means you'll try
			// to access outside the array
			// boundaries...  brutal!
			//Dbg_Assert( *p_data == d );

			CBonedAnimCompressEntry* pEntry = &sQTable[d];
			pReturnData->qx = pEntry->x48;
			pReturnData->qy = pEntry->y48;
			pReturnData->qz = pEntry->z48;
			p_data += sizeof( char );

			// remove the bits
			pReturnData->timestamp &= 0x07ff;
		}
		else
		{
			if ( data & 0x2000 )
			{
				pReturnData->qx = p_data[0];
				p_data += sizeof( char );
			}
			else
			{
				pReturnData->qx = (p_data[1]<<8) | p_data[0];
				p_data += sizeof( short );
			}

			if ( data & 0x1000 )
			{
				pReturnData->qy = p_data[0];
				p_data += sizeof( char );
			}
			else
			{
				pReturnData->qy = (p_data[1]<<8) | p_data[0];
				p_data += sizeof( short );
			}

			if ( data & 0x0800 )
			{
				pReturnData->qz = p_data[0];
				p_data += sizeof( char );
			}
			else
			{
				pReturnData->qz = (p_data[1]<<8) | p_data[0];
				p_data += sizeof( short );
			}

			// remove the bits
			pReturnData->timestamp &= 0x07ff;
		}
	}
	else
	{
		// no compression
		pReturnData->qx = (p_data[1]<<8) | p_data[0];
		pReturnData->qy = (p_data[3]<<8) | p_data[2];
		pReturnData->qz = (p_data[5]<<8) | p_data[4];
		p_data += 6;
	}

#ifdef __ARAM__
	return (int)p_data - (int)pData;
#else
	return (char*)p_data;
#endif		// __ARAM__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef __ARAM__
inline int get_compressed_t_frame( char* pData, CStandardAnimTKey* pReturnData )
#else
inline char * get_compressed_t_frame( char* pData, CStandardAnimTKey* pReturnData )
#endif		// __ARAM__
{
	unsigned char* p_data = (unsigned char*)pData;
	
	bool useLookupTable = false;
	
	unsigned char flags = *p_data;
	p_data += sizeof( char );
	
	if ( flags & 0x80 )
	{					
		useLookupTable = true;
	}

	if ( flags & 0x40 )
	{
		// expand the timestamp
		pReturnData->timestamp = (unsigned short)(flags & 0x3f);
	}
	else
	{
		// read another short
		pReturnData->timestamp = (unsigned short)(p_data[1]<<8) | p_data[0];
		p_data += sizeof( short );
	}

	if ( useLookupTable )
	{
		unsigned char d = *p_data;

		// defaults to signed char,
		// which means you'll try
		// to access outside the array
		// boundaries...  brutal!
		//Dbg_Assert( *p_data == d );
		
		CBonedAnimCompressEntry* pEntry = &sTTable[d];
		pReturnData->tx = pEntry->x48;
		pReturnData->ty = pEntry->y48;
		pReturnData->tz = pEntry->z48;
		p_data += sizeof( char );
	}
	else
	{
		// no compression
		pReturnData->tx = (p_data[1]<<8) | p_data[0];
		pReturnData->ty = (p_data[3]<<8) | p_data[2];
		pReturnData->tz = (p_data[5]<<8) | p_data[4];
		p_data += 6;
	}

#ifdef __ARAM__
	return (int)p_data - (int)pData;
#else
	return (char*)p_data;
#endif		// __ARAM__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBonedAnimFrameData::GetCompressedInterpolatedFrames(Mth::Quat* pRotations, Mth::Vector* pTranslations, float time, Nx::CQuickAnim* pQuickAnim )
{
    Dbg_Assert( pRotations );
    Dbg_Assert( pTranslations );

	Dbg_Assert( !is_hires() );

	CStandardAnimQKey theStartQFrame;
	CStandardAnimQKey theEndQFrame;
	CStandardAnimQKey* pStartQFrame = &theStartQFrame;
	CStandardAnimQKey* pEndQFrame = &theEndQFrame;
	
	CStandardAnimTKey theStartTFrame;
	CStandardAnimTKey theEndTFrame;
	CStandardAnimTKey* pStartTFrame = &theStartTFrame;
	CStandardAnimTKey* pEndTFrame = &theEndTFrame;
	
#ifdef __ARAM__
	char* pCurrentQFrame = mp_qFrames;//&mp_qFrames[32];
	char* pCurrentTFrame = mp_tFrames;//&mp_tFrames[32];

	// Pre-dma framecount sizes.
	dma_count( (uint32)mp_perBoneQFrameSize, ( m_numBones * sizeof( uint16 ) * 2 ) + 16 );		// +16 should cover partial mask data.
//	if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
//	{
//		while ( framecount_active );
//		dma_partial( (uint32)mp_partialAnimData );
//	}
//	uint32 * partial = (uint32 *)&framecount[( (uint32)mp_partialAnimData - (uint32)mp_perBoneQFrameSize ) * 2];
	uint32 * partial = (uint32 *)&framecount[( (uint32)mp_partialAnimData - (uint32)mp_perBoneQFrameSize ) / 2];

	// Pre-DMA all data.
//	uint32 q_lines = ((uint32)mp_boneNames) >> 16;
//	uint32 t_lines = ((uint32)mp_boneNames) & 0xffff;
//	q_lines = ( ( q_lines + ( ARAM_CACHE_LINE_SIZE - 1 ) ) / ARAM_CACHE_LINE_SIZE ); 
//	t_lines = ( ( t_lines + ( ARAM_CACHE_LINE_SIZE - 1 ) ) / ARAM_CACHE_LINE_SIZE ); 

//	for ( uint32 lp = 0; lp < q_lines; lp++ ) dma_q_entry( ((uint32)mp_qFrames), lp );
//	for ( uint32 lp = 0; lp < t_lines; lp++ ) dma_t_entry( ((uint32)mp_tFrames), lp );

	uint16* pQSizes = &framecount[0];
	uint16* pTSizes = &framecount[m_numBones];

	// Make sure framecount sizes have been DMA'd.
	while ( framecount_active );
#else
	Dbg_Assert( mp_qFrames );
	Dbg_Assert( mp_tFrames );

	char* pCurrentQFrame = (char*)mp_qFrames;
	char* pCurrentTFrame = (char*)mp_tFrames;

	uint16* pQSizes = &mp_perBoneQFrameSize[0];
	uint16* pTSizes = &mp_perBoneTFrameSize[0];
#endif		// __aram__
	
	// find the nearest 2 keyframes
	float qTimeStamp = time * 60.0f;
	float tTimeStamp = time * 60.0f;
	
	// Precalculate the skip index mask for speed.
	uint32 skip_index_mask = ( pQuickAnim && pQuickAnim->m_quickAnimPointers.valid ) ? ( 1 << pQuickAnim->m_quickAnimPointers.skipIndex ) : 0;

	for ( int i = 0; i < m_numBones; i++ )
	{
		// See if the QuickAnim data indicates that this bone may be skipped.
		bool skip_this_bone = ( skip_index_mask ) ? (( pQuickAnim->m_quickAnimPointers.pSkipList[i] & skip_index_mask ) > 0 ) : false;

		if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
		{
#ifdef __ARAM__
			uint32* pBoneMask = ( partial + 1 ) + ( i / 32 );
#else
			uint32* pBoneMask = ( mp_partialAnimData + 1 ) + ( i / 32 );
#endif		// __ARAM__
			skip_this_bone = ( ( (*pBoneMask) & ( 1 << (i%32) ) ) == 0 );
//			skip_this_bone = ( ( (*pBoneMask) & ( (1<<31) >> (i%32) ) ) == 0 );
		}

		if( !skip_this_bone )
		{
			float qAlpha = 0.0f;
			float tAlpha = 0.0f;

			char* pNextQFrame = pCurrentQFrame;
			char* pNextQBone = pCurrentQFrame + *pQSizes;
		
			char* pNextTFrame = pCurrentTFrame;
    		char* pNextTBone = pCurrentTFrame + *pTSizes;

			if ( pQuickAnim && pQuickAnim->m_quickAnimPointers.valid )
			{
				pNextQFrame = pQuickAnim->m_quickAnimPointers.pQuickQKey[i];
				pNextTFrame = pQuickAnim->m_quickAnimPointers.pQuickTKey[i];
			}
	 
#ifdef __ARAM__
			int q_off = 0;
			{
				// DMA this q track.
				uint aligned_off = ( (int)pNextQFrame & ~31 );
				uint size = ( ( *pQSizes - ( aligned_off - (int)pCurrentQFrame ) ) + 31 ) & ~31;
				DCFlushRange ( qqq, size );
				framecount_size = size;
				framecount_active = 1;
				ARQPostRequest	(	&framecount_request,
									0x55555555,											// Owner.
									ARQ_TYPE_ARAM_TO_MRAM,								// Type.
									ARQ_PRIORITY_HIGH,									// Priority.
									(u32)aligned_off,									// Source.
									(uint32)qqq,										// Dest.
									size,												// Length.
									arqCallback );										// Callback
				q_off = (int)pNextQFrame - aligned_off;
			}
#endif		// __ARAM__

			while ( 1 )
			{
				if ( pQuickAnim )
				{
					pQuickAnim->m_quickAnimPointers.pQuickQKey[i] = (char*)pNextQFrame;
				}

#				ifdef __ARAM__
				while ( framecount_active );
#				endif		// __ARAM__

#ifdef __ARAM__
				int bytes = get_compressed_q_frame( &qqq[q_off], pStartQFrame );
				q_off += bytes;
				pNextQFrame = &pNextQFrame[bytes];
#else
				pNextQFrame = get_compressed_q_frame( pNextQFrame, pStartQFrame );
#endif		// __ARAM__
				if ( pNextQFrame >= pNextQBone )
				{
					// last frame
					*pEndQFrame = *pStartQFrame;
					qAlpha = 0.0f;
					break;
				}

#ifdef __ARAM__
				get_compressed_q_frame( &qqq[q_off], pEndQFrame );
#else
				get_compressed_q_frame( pNextQFrame, pEndQFrame );
#endif		// __ARAM__
				if ( qTimeStamp >= timeDown(pStartQFrame->timestamp) && qTimeStamp <= timeDown(pEndQFrame->timestamp) )
				{
					qAlpha = get_alpha( timeDown(pStartQFrame->timestamp), timeDown(pEndQFrame->timestamp), qTimeStamp );
					break;
				}
			}

			// theStartFrame and theEndFrame should contain the
			// two closest keyframes here.  now interpolate between them
			// TODO:  we might be able to cache some of this data...
			interpolate_standard_q_frame( pRotations, pStartQFrame, pEndQFrame, qAlpha );
			
#ifdef __ARAM__
			int t_off = 0;
			{
				// DMA this q track.
				int aligned_off = ( (int)pNextTFrame & ~31 );
				int size = ( ( *pTSizes - ( aligned_off - (int)pCurrentTFrame ) ) + 31 ) & ~31;
				DCFlushRange ( qqq, size );
				framecount_size = size;
				framecount_active = 1;
				ARQPostRequest	(	&framecount_request,
									0x55555555,											// Owner.
									ARQ_TYPE_ARAM_TO_MRAM,								// Type.
									ARQ_PRIORITY_HIGH,									// Priority.
									(u32)aligned_off,									// Source.
									(uint32)qqq,										// Dest.
									size,												// Length.
									arqCallback );										// Callback
				t_off = (int)pNextTFrame - aligned_off;
			}
#endif		// __ARAM__

			while ( 1 )
			{
				if ( pQuickAnim )
				{
					pQuickAnim->m_quickAnimPointers.pQuickTKey[i] = (char*)pNextTFrame;
				}

#				ifdef __ARAM__
				while ( framecount_active );
#				endif		// __ARAM__

#ifdef __ARAM__
				int bytes = get_compressed_t_frame( &qqq[t_off], pStartTFrame );
				t_off += bytes;
				pNextTFrame = &pNextTFrame[bytes];
#else
				pNextTFrame = get_compressed_t_frame( pNextTFrame, pStartTFrame );
#endif		// __ARAM__
				if ( pNextTFrame >= pNextTBone )
				{
					// last frame
					*pEndTFrame = *pStartTFrame;
					tAlpha = 0.0f;
					break;
				}

#ifdef __ARAM__
				get_compressed_t_frame( &qqq[t_off], pEndTFrame );
#else
				get_compressed_t_frame( pNextTFrame, pEndTFrame );
#endif		// __ARAM__
				if ( tTimeStamp >= timeDown(pStartTFrame->timestamp) && tTimeStamp <= timeDown(pEndTFrame->timestamp) )
				{
					tAlpha = get_alpha( timeDown(pStartTFrame->timestamp), timeDown(pEndTFrame->timestamp), tTimeStamp );
					break;
				}
			}

			interpolate_standard_t_frame( pTranslations, pStartTFrame, pEndTFrame, tAlpha );
		}
		
		pCurrentQFrame += *pQSizes;
		pCurrentTFrame += *pTSizes;
		pQSizes++;
		pTSizes++;
		pRotations++;
		pTranslations++;
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBonedAnimFrameData::GetInterpolatedFrames(Mth::Quat* pRotations, Mth::Vector* pTranslations, float time, Nx::CQuickAnim* pQuickAnim )
{
	if ( m_flags & nxBONEDANIMFLAGS_USECOMPRESSTABLE )
	{
		// new partial anims now use the standard GetCompressedInterpolatedFrames function
		return GetCompressedInterpolatedFrames(pRotations, pTranslations, time, pQuickAnim);
	}

	if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
	{
		Dbg_MsgAssert( 0, ( "Non-compressed partial animations are not supported yet" ) );
	}

    Dbg_Assert( pRotations );
    Dbg_Assert( pTranslations );

	Dbg_Assert( !is_hires() );
    
	float qAlpha = 0.0f;
	float tAlpha = 0.0f;

	// DMA the animation data here.
#ifdef __ARAM__
	int		size;
	if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA )
	{
		size = ( m_numBones * sizeof(uint32) );
	}
	else
	{
		size = 0;
	}

	if ( m_flags & nxBONEDANIMFLAGS_HIRESFRAMEPOINTERS )
	{
		size += ( m_numBones * sizeof(SHiResAnimFramePointers) );
	}
	else
	{
		size += ( m_numBones * sizeof(SStandardAnimFramePointers) );
	}
	dma_count( (uint32)mp_perBoneFrames, size );
	while ( framecount_active );
//	if ( m_flags & nxBONEDANIMFLAGS_PARTIALANIM )
//	{
//		dma_partial( (uint32)mp_partialAnimData, ( m_numBones * sizeof( uint16 ) * 2 ) );
//	}
//	while ( framecount_active );

//	NsDMA::toMRAM( p_sqqq, (uint32)mp_qFrames, m_num_qFrames * sizeof( CStandardAnimQKey ) );
//	NsDMA::toMRAM( p_sttt, (uint32)mp_tFrames, m_num_tFrames * sizeof( CStandardAnimTKey ) );

//	while( framecount_active );
#endif		// __ARAM__

	// find the nearest 2 keyframes
	float qTimeStamp = time * 60.0f;
	float tTimeStamp = time * 60.0f;
	
	CStandardAnimQKey* pStartQFrame = NULL;
	CStandardAnimQKey* pEndQFrame = NULL;

	CStandardAnimTKey* pStartTFrame = NULL;
	CStandardAnimTKey* pEndTFrame = NULL;

	CStandardAnimQKey* pCurrentQFrame = NULL;
	CStandardAnimTKey* pCurrentTFrame = NULL;
	
#ifdef __ARAM__
//	pStartQFrame = p_sqqq;
//	pEndQFrame = p_sqqq;
//
//	pStartTFrame = p_sttt;
//	pEndTFrame = p_sttt;
//	
//	pCurrentQFrame = p_sqqq;
//	pCurrentTFrame = p_sttt;

	uint32 anim_q_offset = (uint32)mp_qFrames;
	uint32 anim_t_offset = (uint32)mp_tFrames;

	void * p_bone_frames;
	if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA )
	{
		p_bone_frames = &framecount[m_numBones*2];
	}
	else
	{
		p_bone_frames = framecount;
	}
#else
	pStartQFrame = (CStandardAnimQKey*)mp_qFrames;
	pEndQFrame = (CStandardAnimQKey*)mp_qFrames;

	pStartTFrame = (CStandardAnimTKey*)mp_tFrames;
	pEndTFrame = (CStandardAnimTKey*)mp_tFrames;
	
	pCurrentQFrame = (CStandardAnimQKey*)mp_qFrames;
	pCurrentTFrame = (CStandardAnimTKey*)mp_tFrames;

	void * p_bone_frames = mp_perBoneFrames;
#endif		// __aram__

	for ( int i = 0; i < m_numBones; i++ )
	{
		int numQKeys = get_num_qkeys( p_bone_frames, i );
		int numTKeys = get_num_tkeys( p_bone_frames, i );

#ifdef __ARAM__
		int q_off = 0;
		{
			// DMA this q track.
			int aligned_off = ( anim_q_offset & ~31 );
			int size = ( ( ( sizeof( CStandardAnimQKey ) * numQKeys ) - ( aligned_off - anim_q_offset ) ) + 31 ) & ~31;
			DCFlushRange ( qqq, size );
			framecount_size = size;
			framecount_active = 1;
			ARQPostRequest	(	&framecount_request,
								0x55555555,											// Owner.
								ARQ_TYPE_ARAM_TO_MRAM,								// Type.
								ARQ_PRIORITY_HIGH,									// Priority.
								(u32)aligned_off,									// Source.
								(uint32)qqq,										// Dest.
								size,												// Length.
								arqCallback );										// Callback
			while ( framecount_active );
			q_off = anim_q_offset - aligned_off;

			pStartQFrame = (CStandardAnimQKey*)&qqq[q_off];
			pEndQFrame = (CStandardAnimQKey*)&qqq[q_off];
			pCurrentQFrame = (CStandardAnimQKey*)&qqq[q_off];
		}
#endif		// __ARAM__

		for ( int j = 0; j < numQKeys; j++ )
		{
			if ( j == (numQKeys-1) )
			{
				// last frame
				pStartQFrame = pCurrentQFrame + j;
				pEndQFrame = pCurrentQFrame + j;
				qAlpha = 0.0f;
				break;
			}
			else if ( qTimeStamp >= timeDown(pCurrentQFrame[j].timestamp) 
				 && qTimeStamp <= timeDown(pCurrentQFrame[j+1].timestamp) )
			{
				pStartQFrame = pCurrentQFrame + j;
				pEndQFrame = pCurrentQFrame + j + 1;
				qAlpha = get_alpha( timeDown(pStartQFrame->timestamp), timeDown(pEndQFrame->timestamp), qTimeStamp );
				break;
			}
		}

		interpolate_q_frame( pRotations, 
						 pStartQFrame, 
						 pEndQFrame,
						 qAlpha,
						 is_hires() );

#ifdef __ARAM__
		int t_off = 0;
		{
			// DMA this t track.
			int aligned_off = ( anim_t_offset & ~31 );
			int size = ( ( ( sizeof( CStandardAnimTKey ) * numTKeys ) - ( aligned_off - anim_t_offset ) ) + 31 ) & ~31;
			DCFlushRange ( qqq, size );
			framecount_size = size;
			framecount_active = 1;
			ARQPostRequest	(	&framecount_request,
								0x55555555,											// Owner.
								ARQ_TYPE_ARAM_TO_MRAM,								// Type.
								ARQ_PRIORITY_HIGH,									// Priority.
								(u32)aligned_off,									// Source.
								(uint32)qqq,										// Dest.
								size,												// Length.
								arqCallback );										// Callback
			while ( framecount_active );
			t_off = anim_t_offset - aligned_off;

			pStartTFrame = (CStandardAnimTKey*)&qqq[t_off];
			pEndTFrame = (CStandardAnimTKey*)&qqq[t_off];
			pCurrentTFrame = (CStandardAnimTKey*)&qqq[t_off];
		}
#endif		// __ARAM__

		for ( int j = 0; j < numTKeys; j++ )
		{
			if ( j == (numTKeys-1) )
			{
				// last frame
				pStartTFrame = pCurrentTFrame + j;
				pEndTFrame = pCurrentTFrame + j;
				tAlpha = 0.0f;
				break;
			}
			else if ( tTimeStamp >= timeDown(pCurrentTFrame[j].timestamp) 
				 && tTimeStamp <= timeDown(pCurrentTFrame[j+1].timestamp) )
			{
				pStartTFrame = pCurrentTFrame + j;
				pEndTFrame = pCurrentTFrame + j + 1;
				tAlpha = get_alpha( timeDown(pStartTFrame->timestamp), timeDown(pEndTFrame->timestamp), tTimeStamp );
				break;
			}
		}
		
		// theStartFrame and theEndFrame should contain the
		// two closest keyframes here.  now interpolate between them
		// TODO:  we might be able to cache some of this data...
		interpolate_t_frame( pTranslations, 
							 pStartTFrame, 
							 pEndTFrame,
							 tAlpha,
							 is_hires() );

#if 0
		if ( m_printDebugInfo )
		{
			// for debug info, if necessary
		}
#endif

#ifdef __ARAM__
		anim_q_offset += ( sizeof( CStandardAnimQKey ) * numQKeys );
		anim_t_offset += ( sizeof( CStandardAnimTKey ) * numTKeys );
#else
		pCurrentQFrame += numQKeys;
		pCurrentTFrame += numTKeys;
#endif	// __ARAM__
		pRotations++;
		pTranslations++;
	}

    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CBonedAnimFrameData::GetBoneName( int index )
{
	// only the object anim stores the name of the bones
	Dbg_MsgAssert( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA, ( "Bone names are only stored with object anims" ) );
	
	Dbg_MsgAssert( index >= 0 && index < m_numBones, ( "Bone name request out of range %d (0-%d)", index, m_numBones ) );

	Dbg_Assert( mp_boneNames );

#ifdef __ARAM__
	int		size;
	if ( m_flags & nxBONEDANIMFLAGS_OBJECTANIMDATA )
	{
		size = ( m_numBones * sizeof(uint32) );
	}
	else
	{
		size = 0;
	}
	dma_count( (uint32)mp_perBoneFrames, size );
	while ( framecount_active );

	uint32 * p32 = (uint32*)framecount;
	return p32[index];
#else
	return mp_boneNames[index];
#endif		// __ARAM__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CBonedAnimFrameData::get_num_customkeys()
{
	return m_num_customKeys;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCustomAnimKey* CBonedAnimFrameData::get_custom_key( int index )
{
	Dbg_Assert( index >= 0 && index < get_num_customkeys() );
	Dbg_MsgAssert( mpp_customAnimKeyList, ( "custom key list doesn't exist" ) );
	return mpp_customAnimKeyList[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBonedAnimFrameData::ResetCustomKeys()
{
	// this assumes that there's only going to be one 
	// animcontroller calling this frame data...
	// eventually, we might need to move this
	// into CReferencedFrameData...

	int customKeyCount = get_num_customkeys();
	for ( int i = 0; i < customKeyCount; i++ )
	{					 
		CCustomAnimKey* pKey = get_custom_key(i);
		pKey->SetActive( true );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBonedAnimFrameData::ProcessCustomKeys( float startTime, float endTime, Obj::CObject* pObject, bool inclusive )
{
	// for each key,
	// see if it's between the start and end time
	// start_time <= time < end_time

	// get it into frames...
	startTime *= 60.0f;
	endTime *= 60.0f;

	int customKeyCount = get_num_customkeys();
	for ( int i = 0; i < customKeyCount; i++ )
	{
		CCustomAnimKey* pKey = get_custom_key(i);
		if ( pKey->WithinRange( startTime, endTime, inclusive ) )
		{
//			printf( "Processing key at %f (%f %f)\n", custom_key_time, startTime, endTime );
			pKey->ProcessKey( pObject );
		}
		else
		{
//			printf( "Not processing key at %f (%f %f)\n", custom_key_time, startTime, endTime );
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx







================================================
FILE: Code/Gfx/BonedAnim.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       bonedanim.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/14/2001
//****************************************************************************

#ifndef __GFX_BONEDANIM_H
#define __GFX_BONEDANIM_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

#include 

#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

namespace Nx
{
	class CQuickAnim;
}

namespace Obj
{
	class CObject;
}

namespace File
{
	class CAsyncFileHandle;
}
			 
namespace Gfx
{
	class CAnimQKey;
	class CAnimTKey;
	struct SAnimCustomKey;
	struct SQuickAnimPointers;

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

// Raw Animation Data
class CBonedAnimFrameData
{
public:
    CBonedAnimFrameData();
    ~CBonedAnimFrameData();

public:
    bool    			   	Load(uint32* pData, int file_size, bool assertOnFail);
    bool    			   	Load(const char* p_fileName, bool assertOnFail, bool async, bool use_pip = false);
    bool    			   	PostLoad(bool assertOnFail, int file_size, bool delete_buffer = true);
	bool					LoadFinished();
	bool					IsValidTime(float time);
	float					GetDuration();
	int						GetNumBones();
	uint32					GetBoneName( int index );
    bool				    GetInterpolatedFrames(Mth::Quat* pRotations, Mth::Vector* pTranslations, float time, Nx::CQuickAnim* = NULL);
    bool				    GetCompressedInterpolatedFrames(Mth::Quat* pRotations, Mth::Vector* pTranslations, float time, Nx::CQuickAnim* = NULL);
    bool				    GetCompressedInterpolatedPartialFrames(Mth::Quat* pRotations, Mth::Vector* pTranslations, float time, Nx::CQuickAnim* = NULL);
    bool				    GetInterpolatedCameraFrames(Mth::Quat* pRotations, Mth::Vector* pTranslations, float time, Nx::CQuickAnim* = NULL);
	bool					ResetCustomKeys( void );
	bool					ProcessCustomKeys( float startTimeInclusive, float endTimeExclusive, Obj::CObject* pObject, bool endInclusive = false );

	CAnimQKey*				GetQFrames( void ) { return (CAnimQKey*)mp_qFrames; }
	CAnimTKey*				GetTFrames( void ) { return (CAnimTKey*)mp_tFrames; }

	void					SetQFrames( CAnimQKey * p_q ) { mp_qFrames = (char*)p_q; }
	void					SetTFrames( CAnimTKey * p_t ) { mp_tFrames = (char*)p_t; }

protected:	
	int						get_num_customkeys();
	CCustomAnimKey*			get_custom_key( int index );
	int						get_num_qkeys( void * p_frame_data, int boneIndex ) const;
	int						get_num_tkeys( void * p_frame_data, int boneIndex ) const;
	void					set_num_qkeys( int boneIndex, int numKeys );
	void					set_num_tkeys( int boneIndex, int numKeys );
	bool					is_hires() const;

	static void 			async_callback(File::CAsyncFileHandle *, File::EAsyncFunctionType function,
										   int result, unsigned int arg0, unsigned int arg1);

protected:
//	bool				   	intermediate_read_stream(void* pStream);
    bool                    plat_read_stream(uint8* pData, bool delete_buffer = true);
    bool                    plat_read_compressed_stream(uint8* pData, bool delete_buffer = true);
	bool					plat_dma_to_aram( int qbytes = 0, int tbytes = 0, uint32 flags = 0 );
	
protected:
	float				   	m_duration;		  // could be a short
	uint32					m_flags;
	int						m_numBones;

	// file buffer (the only malloc'ed pointer)
	void*					mp_fileBuffer;
	File::CAsyncFileHandle* mp_fileHandle;
	int						m_fileSize;

	// massive block of all Q-frames and T-frames
	char*		   			mp_qFrames;
	char*		   			mp_tFrames;

	// per-bone pointers into the massive block of Q-frames and T-frames
	void*					mp_perBoneFrames;

	// list of bone names (for object anims only)
	uint32*					mp_boneNames;
		
	// original number of bones + list of bone masks (for partial anims only)
	uint32*					mp_partialAnimData;

	uint32					m_fileNameCRC;

	// custom keys (for cameras, changing parent bones, etc.)
	CCustomAnimKey** 		mpp_customAnimKeyList;

	uint16*					mp_perBoneQFrameSize;
	uint16*					mp_perBoneTFrameSize;
	
	short					m_num_qFrames;
	short					m_num_tFrames;
	short					m_num_customKeys;
	
	short					m_printDebugInfo:1;
	short					m_dataLoaded:1;
	short					m_pipped:1;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

bool InitQ48Table( const char* pFileName, bool assertOnFail = true );
bool InitT48Table( const char* pFileName, bool assertOnFail = true );

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
inline float CBonedAnimFrameData::GetDuration()
{
	return m_duration;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
inline int CBonedAnimFrameData::GetNumBones()
{
	return m_numBones;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
inline bool CBonedAnimFrameData::LoadFinished()
{
	return m_dataLoaded;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // namespace Gfx

#endif	// __GFX_BONEDANIM_H


================================================
FILE: Code/Gfx/BonedAnimTypes.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       BonedAnimTypes.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  02/04/2003
//****************************************************************************

#ifndef __GFX_BONEDANIMTYPES_H
#define __GFX_BONEDANIMTYPES_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Gfx
{
 
/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CAnimQKey
{
public:
#ifdef __PLAT_NGC__
	short			signBit:1;	// 1 = negative
	short			timestamp:15;
#else
	short			timestamp:15;
	short			signBit:1;	// 1 = negative
#endif		// __PLAT_NGC__

protected:
	CAnimQKey() {}
};

class CStandardAnimQKey	: public CAnimQKey
{
public:
    short           qx;
    short           qy;
    short           qz;
};

class CHiResAnimQKey : public CAnimQKey
{
public:
    float           qx;
    float           qy;
    float           qz;
};

class CAnimTKey
{
public:
	short			timestamp;

protected:
	CAnimTKey() {}
};

class CStandardAnimTKey	: public CAnimTKey
{
public:
    short           tx;
    short           ty;
    short           tz;
};

class CHiResAnimTKey : public CAnimTKey
{
public:
    float           tx;
    float           ty;
    float           tz;
};

struct SQuickAnimPointers
{
	char* 				pQuickQKey[64];
	char*				pQuickTKey[64];
	CStandardAnimQKey	theStartQKey[64];
	CStandardAnimQKey	theEndQKey[64];
	CStandardAnimTKey	theStartTKey[64];
	CStandardAnimTKey	theEndTKey[64];
//	char				qSkip[64];
//	char				tSkip[64];
	bool				valid;
	uint32*				pSkipList;
	uint32				skipIndex;
};

// NOTE: if you change this enum, update the CAnimChannel::GetDebugInfo switch statement!	
enum EAnimLoopingType
{
	LOOPING_HOLD				= 0,	// holds on last frame
	LOOPING_CYCLE,						// cycles the animation forever
	LOOPING_PINGPONG,					// pingpongs the animation forever
	LOOPING_WOBBLE,						// Aims towards wobble_target_time whilst wobbling a bit. Used for manuals & grinds.

// these are samples of other possible looping types,
// although they have not yet been implemented
//	LOOPING_CYCLE_X_TIMES,				// cycles the same animation x times
//	LOOPING_PINGPONG_X_TIMES			// pingpongs the animation x times
};
	
// NOTE: if you change this enum, update the CAnimChannel::GetDebugInfo switch statement!	
enum EAnimDirection 
{
	ANIM_DIR_FORWARDS			= 0,
	ANIM_DIR_BACKWARDS			= -1
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**						Inline Functions									**
*****************************************************************************/
					
} // namespace Gfx

#endif // __GFX_BONEDANIMTYPES_H

================================================
FILE: Code/Gfx/CasUtils.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       CasUtils.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  ?/??/????
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

#include 
#include 
#include 
#include  
#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Cas
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 get_desc_id_from_structure( Script::CStruct* pStructure )
{
	Dbg_Assert( pStructure );

	uint32 descId;
	pStructure->GetChecksum( CRCD(0x4bb2084e,"desc_id"), &descId, true );
	return descId;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool IsValidOption( uint32 partChecksum, uint32 lookFor )
{
	return ( GetOptionStructure( partChecksum, lookFor ) != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* GetOptionStructure( uint32 partChecksum, uint32 lookFor, bool assertOnFail )
{
	Script::CArray* pArray;
	pArray = Script::GetArray( partChecksum, Script::ASSERT );

	Script::CStruct* pReturnStructure = NULL;

	if ( lookFor == 0 )
	{
		// special checksum, meaning to return NULL
		// (handy when preprocessing random descs)
		return NULL;
	}

	for ( uint32 i = 0; i < pArray->GetSize(); i++ )
	{
		Script::CStruct* pCurrDesc = pArray->GetStructure( i );
		uint32 desc_id = get_desc_id_from_structure( pCurrDesc );
		if ( desc_id == lookFor )
		{
			pReturnStructure = pCurrDesc;
			break;
		}
	}

	if ( assertOnFail )
	{
		Dbg_MsgAssert( pReturnStructure, ( "Couldn't find %s %s", Script::FindChecksumName(partChecksum), Script::FindChecksumName(lookFor) ) );
	}

	return pReturnStructure;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* GetFirstOptionStructure( uint32 partChecksum )
{
	Script::CArray* pArray;
	pArray = Script::GetArray( partChecksum, Script::ASSERT );

	return pArray->GetStructure( 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void BuildRandomSetList( uint32 partChecksum, uint32 random_set, Script::CStruct** ppReturnStructs, int* pReturnNumStructs )
{
	Script::CArray* pArray;
	pArray = Script::GetArray( partChecksum, Script::ASSERT );

	// reset the return value
	*pReturnNumStructs = 0;

	int part_array_size = pArray->GetSize();
	for ( int i = 0; i < part_array_size; i++ )
	{
		Script::CStruct* pStruct = pArray->GetStructure( i );
		int allowed_to_pick = 0;
		pStruct->GetInteger( CRCD(0x355f9467,"allowed_to_pick"), &allowed_to_pick, Script::ASSERT );
		
		if ( allowed_to_pick )
		{
			// if a random_set was specified, then
			// make sure that this item works with it
			// (used for matching skin tones)
			uint32 this_random_set = 0;
			pStruct->GetChecksum( CRCD(0x0d7260fd,"random_set"), &this_random_set, Script::NO_ASSERT );

			if ( this_random_set == random_set )
			{
				*ppReturnStructs = pStruct;
				ppReturnStructs++;

				(*pReturnNumStructs)++;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* GetRandomOptionStructure( uint32 partChecksum, uint32 random_set )
{
	Script::CArray* pArray;
	pArray = Script::GetArray( partChecksum, Script::ASSERT );

	const int vMAX_CAS_ITEMS = 32;
	Script::CStruct* pAllowedStructs[vMAX_CAS_ITEMS];
	bool alreadySelected[vMAX_CAS_ITEMS];
	int allowRandomCount = 0;
	
	int part_array_size = pArray->GetSize();
	for ( int i = 0; i < part_array_size; i++ )
	{
		Script::CStruct* pStruct = pArray->GetStructure( i );
		int allow_random = !pStruct->ContainsFlag( CRCD(0xf6f8e158,"no_random") );
		pStruct->GetInteger( CRCD(0xf1e3cd22,"allow_random"), &allow_random, Script::ASSERT );
		
		if ( random_set )
		{
			// if a random_set was specified, then
			// make sure that this item works with it
			// (used for matching skin tones)
			uint32 this_random_set = 0;
			if ( pStruct->GetChecksum( CRCD(0x0d7260fd,"random_set"), &this_random_set, Script::NO_ASSERT ) )
			{
				if ( this_random_set != random_set )
				{
					allow_random = false;
				}
			}
		}
		
		if ( allow_random )
		{
			Dbg_Assert( allowRandomCount < vMAX_CAS_ITEMS );
			pAllowedStructs[allowRandomCount] = pStruct;
			
			int already_selected;
			pStruct->GetInteger( CRCD(0x92bddfd9,"already_selected"), &already_selected, true );
			alreadySelected[allowRandomCount] = already_selected;
			
			allowRandomCount++;
		}
	}

	// make sure there's at least one allow_random,
	// or else we'll get into an infinite loop
	Dbg_MsgAssert( allowRandomCount > 0, ( "No allow_random was found in this array %s (Tell Gary!)", Script::FindChecksumName(partChecksum) ) );

	// count how many of them are already unselected
	int unselected_count = 0;
	for ( int i = 0; i < allowRandomCount; i++ )
	{
		if ( !alreadySelected[i] )
		{
			unselected_count++;
		}
	}

	// the following makes sure that all the "allow_random" parts
	// are used at least once, before duplicates begin.
	if ( unselected_count == 0 )
	{
		for ( int i = 0; i < allowRandomCount; i++ )
		{
			alreadySelected[i] = false;
		}
	}

	// now get rid of all the already selected items
	int currentCount = 0;
	for ( int i = 0; i < allowRandomCount; i++ )
	{
		if ( !alreadySelected[i] )
		{
			pAllowedStructs[currentCount] = pAllowedStructs[i];
			alreadySelected[currentCount] =	alreadySelected[i];
			currentCount++;
		}
	}
	allowRandomCount = currentCount;
	
	Dbg_MsgAssert( allowRandomCount >= 1, ( "No part to pick" ) );
	int index = Mth::Rnd( allowRandomCount );
	Dbg_Assert( index >= 0 && index < allowRandomCount );
	Script::CStruct* pReturnStruct = pAllowedStructs[index];

	Dbg_Assert( pReturnStruct );
	pReturnStruct->AddInteger( CRCD(0x92bddfd9,"already_selected"), 1 );

//	uint32 checksum;
//	pStruct->GetChecksum( "desc_id", &checksum, true );
//	Dbg_Message( "Choosing random part %s", Script::FindChecksumName( checksum ) );

	return pReturnStruct;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* GetRandomOptionStructureByIndex( uint32 partChecksum, int desired_index, uint32 random_set )
{
	Script::CArray* pArray;
	pArray = Script::GetArray( partChecksum, Script::ASSERT );

	int part_array_size = pArray->GetSize();

	if ( !random_set )
	{
		for ( int i = 0; i < part_array_size; i++ )
		{
			Script::CStruct* pStruct = pArray->GetStructure( i );

			int random_index = -1;
			if ( pStruct->GetInteger( CRCD(0x4b833e64,"random_index"), &random_index, Script::ASSERT ) )
			{
				if ( random_index == desired_index )
				{
					return pStruct;
				}
			}
		}
	}
	else
	{
		// GJ FIX FOR SK5:TT12579:  "NJ - Goal peds being created with mismached skin colors"
		// if a random_set (skintone) was specified, then find the n-th item that actually works with this
		// random set
		while ( 1 )
		{
			#ifdef	__NOPT_ASSERT__
			int current_random_index = desired_index;
			#endif

			for ( int i = 0; i < part_array_size; i++ )
			{
				Script::CStruct* pStruct = pArray->GetStructure( i );

				int allow_random = !pStruct->ContainsFlag( CRCD(0xf6f8e158,"no_random") );
				pStruct->GetInteger( CRCD(0xf1e3cd22,"allow_random"), &allow_random, Script::ASSERT );

				uint32 this_random_set = 0;
				pStruct->GetChecksum( CRCD(0x0d7260fd,"random_set"), &this_random_set, Script::NO_ASSERT );
				bool is_universal_item = ( this_random_set == 0 );

				if ( allow_random && ( ( this_random_set == random_set ) || is_universal_item ) )
				{
					if ( desired_index <= 0 )
					{
						return pStruct;
					}

					desired_index--;
				}
			}

			// once we get to the end of the array, we need to start over
			// (basically, we're doing a modulo of the desired_index)...
			// the following checks for an infinite loop in which there
			// were no items found that works with this random set
			Dbg_MsgAssert( current_random_index != desired_index, ( "Couldn't find 'random_index' %d for ped part %s (are there any that fit this skintone %s?)", desired_index, Script::FindChecksumName(partChecksum), Script::FindChecksumName(random_set) ) );
		}
	}

	Dbg_MsgAssert( 0, ( "Couldn't find 'random_index' %d for ped part %s", desired_index, Script::FindChecksumName(partChecksum) ) );
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // end namespace




================================================
FILE: Code/Gfx/CasUtils.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       CasUtils.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  ?/??/????
//****************************************************************************

#ifndef __GFX_CASUTILS_H
#define __GFX_CASUTILS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Script
{
	class CStruct;
}

namespace Cas
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

// for searching/traversing an actual optionset
bool				IsValidOption( uint32 partChecksum, uint32 descId );
Script::CStruct*	GetOptionStructure( uint32 partChecksum, uint32 descId, bool assertOnFail = true );
Script::CStruct*	GetFirstOptionStructure( uint32 partChecksum );
Script::CStruct*	GetNullOptionStructure( uint32 partChecksum );
void				BuildRandomSetList( uint32 partChecksum, uint32 random_set, Script::CStruct** ppReturnStructs, int* pReturnCount );
Script::CStruct*	GetRandomOptionStructure( uint32 partChecksum, uint32 random_set = 0 );
Script::CStruct*	GetRandomOptionStructureByIndex( uint32 partChecksum, int index, uint32 random_set = 0 );

} // end namespace

#endif


================================================
FILE: Code/Gfx/CustomAnimKey.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       CustomAnimKey.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  02/11/2002
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
								   
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Gfx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

// all custom keys must have 32-bit data, for the animconv's
// byte-swapping algorithm to work properly

struct SIntermediateCustomAnimKeyHeader
{
	uint32	timeStamp;
	uint32	keyType;
	uint32	size;
};

typedef enum
{
	vUNUSED1 = 0,
	vCHANGE_FOCAL_LENGTH,
	vCHANGE_CAMERA_RT,
	vCHANGE_CAMERA_RT_IGNORE,
	vRUN_SCRIPT,
	vCREATE_OBJECT_FROM_STRUCT,
	vKILL_OBJECT_FROM_STRUCT,
	vCHANGE_CAMERA_RT_ENDKEY,
};

// class CChangeFOVKey
class CChangeFOVKey : public CCustomAnimKey
{
public:
	CChangeFOVKey( int frame, float fov );

public:
	virtual	bool	process_key( Obj::CObject* pObject );

protected:
	float	m_fov;
};

// class CRunScriptKey
class CRunScriptKey : public CCustomAnimKey
{
public:
	CRunScriptKey( int frame, uint32 scriptName );

public:
	virtual	bool	process_key( Obj::CObject* pObject );

protected:
	uint32	m_scriptName;
};

// class CChangeCameraRTKey
class CChangeCameraRTKey : public CCustomAnimKey
{
public:
	CChangeCameraRTKey( int frame, const Mth::Quat& theQuat, const Mth::Vector& theVector, bool endKey );

public:
	virtual	bool	process_key( Obj::CObject* pObject );
	virtual bool	WithinRange( float startFrame, float endFrame, bool inclusive );

protected:
	Mth::Quat	m_rot;
	Mth::Vector	m_trans;
	bool		m_endKey;
};

// class CEmptyKey
class CEmptyKey : public CCustomAnimKey
{
public:
	CEmptyKey( int frame );

public:
	virtual	bool	process_key( Obj::CObject* pObject );
};

// class CCreateObjectFromStructKey
class CCreateObjectFromStructKey : public CCustomAnimKey
{
public:
	CCreateObjectFromStructKey( int frame, uint32 objectStructName );

public:
	virtual	bool	process_key( Obj::CObject* pObject );

protected:
	uint32	m_objectStructName;
};

// class CKillObjectFromStructKey
class CKillObjectFromStructKey : public CCustomAnimKey
{
public:
	CKillObjectFromStructKey( int frame, uint32 objectStructName );

public:
	virtual	bool	process_key( Obj::CObject* pObject );

protected:
	uint32	m_objectStructName;
};

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCustomAnimKey::CCustomAnimKey( int frame ) : Lst::Node( this ), m_frame( frame )
{
	// defaults to inactive
	m_active = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCustomAnimKey::WithinRange( float startFrame, float endFrame, bool inclusive )
{
	if ( inclusive )
	{
		if ( startFrame < endFrame )
		{
			// forwards
			return ( m_frame >= startFrame && m_frame <= endFrame );
		}
		else
		{
			// backwards
			return ( m_frame <= startFrame && m_frame >= endFrame );
		}
	}
	else
	{
		if ( startFrame < endFrame )
		{
			// forwards
			return ( m_frame >= startFrame && m_frame < endFrame );
		}
		else
		{
			// backwards
			return ( m_frame <= startFrame && m_frame > endFrame );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCustomAnimKey::SetActive( bool active )
{
	m_active = active;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCustomAnimKey::ProcessKey( Obj::CObject* pObject )
{
	if ( !m_active )
	{
		Dbg_Message( "Key at %d was not active...  ignored!", m_frame );

		return false;
	}

	// once it's run, don't run it again...
	// (this is in case you pause a movie camera
	// on the same frame that it's running a key)
	this->SetActive( false );

	return process_key( pObject );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CChangeFOVKey::CChangeFOVKey( int frame, float fov ) : CCustomAnimKey( frame ), m_fov( fov )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CChangeFOVKey::process_key( Obj::CObject* pObject )
{
	if ( Script::GetInt( CRCD(0x2f510f45,"moviecam_debug"), false ) )
	{
		Dbg_Message( "Processing Change FOV key (fov = %f, %f) (%d)!", m_fov, Mth::RadToDeg(m_fov), m_frame );
	}

	Script::CScriptStructure* pParams = new Script::CScriptStructure;
	pParams->AddFloat( NONAME, m_fov );
	Script::RunScript( "ChangeCameraFov", pParams, pObject );
	delete pParams;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/				

CRunScriptKey::CRunScriptKey( int frame, uint32 scriptName ) : CCustomAnimKey( frame ), m_scriptName( scriptName )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CRunScriptKey::process_key( Obj::CObject* pObject )
{
	if ( Script::GetInt( CRCD(0x2f510f45,"moviecam_debug"), false ) )
	{
		Dbg_Message( "Processing Run Script key (script = %s) @ %d!", Script::FindChecksumName( m_scriptName ), m_frame );
	}
	Script::RunScript( m_scriptName, NULL, pObject );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CChangeCameraRTKey::CChangeCameraRTKey( int frame, const Mth::Quat& theQuat, const Mth::Vector& theVector, bool isEndKey ) : CCustomAnimKey( frame )
{
	// rotate by 90 degrees,
	// eventually, we should do this in the build tools
	
	Mth::Quat rotQuat( Mth::Vector(1,0,0), Mth::DegToRad(90.0f) );
	m_rot = theQuat * rotQuat;

	m_trans[X] = theVector[X];
	m_trans[Y] = theVector[Z];
	m_trans[Z] = -theVector[Y];
	m_trans[W] = 1.0f;

	m_endKey = isEndKey;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CChangeCameraRTKey::process_key( Obj::CObject* pObject )
{
	// GJ:  This key is a kludge to fix the problem with camera
	// double-cuts in the cutscene (where we're trying to
	// interpolate from one camera to the next, rather
	// than just jumping)...  the way it works is that
	// 2 of these keys need to be inserted right before
	// a camera change, which will override the interpolated
	// camera values.  we need 2 because we're guaranteed
	// that the minimum time increment is 1/60th of a second
	// so we need to stick these keys in every 1/60th of
	// a second between the two cameras.  note that this
	// won't work properly if we do anim speed changes
	// or setslomo, because then the minimum time increment
	// is actually less than 1/60th of a second, then it's
	// possible that there will be a camera update, without
	// an accompanying CChangeCamera key found
	// i'd eventually like to look at how WithinRange() works, 
	// to see if there's a better way to do this.
	
	if ( m_frame == 0 )
	{
		// GJ KLUDGE:  there's an exporter bug that writes out extra camera RT
		// keys at the beginning of the cutscene.  this skips
		// those extra keys
		Dbg_Message( "Skipping Change Camera RT key @ %d!", m_frame );
		return false;
	}

	if ( Script::GetInt( CRCD(0x2f510f45,"moviecam_debug"), false ) )
	{
		Dbg_Message( "Processing Change Camera RT key @ %d!", m_frame );
		Dbg_Message( "Quat: %f %f %f %f", m_rot[X], m_rot[Y], m_rot[Z], m_rot[W] );
		Dbg_Message( "Trans: %f %f %f %f", m_trans[X], m_trans[Y], m_trans[Z], m_trans[W] );
	}

	Gfx::Camera* pCamera = (Gfx::Camera*)pObject;

	Mth::Vector& camPos = pCamera->GetPos();
	Mth::Matrix& camMatrix = pCamera->GetMatrix();
	
	// update the camera position
	camPos = m_trans;

	// update the camera orientation
	Mth::Vector nullTrans;
	nullTrans.Set(0.0f,0.0f,0.0f,1.0f);		// Mick: added separate initialization
	
	// QuatVecToMatrix destroys the input data, so need to pass in a temp variable
	Mth::Quat theQuat = m_rot;
	Mth::QuatVecToMatrix( &theQuat, &nullTrans, &camMatrix );

	// need this because of our 1-frame range check
	this->SetActive( true );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CChangeCameraRTKey::WithinRange( float startFrame, float endFrame, bool inclusive )
{
	// good for 1 frame...			  1/60th of a frame

	if ( inclusive )
	{
		if ( startFrame < endFrame )
		{
			// this end key business is used to fix a cutscene glitch
			// during camera cuts.  the problem is that the keys come
			// in sets of 3:
			// KEY[-2]    KEY[-1]    KEY[0]
			// old camera old camera new camera
			// we basically take the current frame (stored
			// in endFrame) and figure out which of the
			// three keys should handle it...  formerly,
			// all three keys handled it, effectively
			// causing the last key's camera data to be 
			// used.  to fix the glitch, we need to only
			// run the correct key...
			if ( m_endKey )
			{
				if ( endFrame > m_frame )
				{
					// let it interpolate
					return false;
				}
			}
			else
			{
				if ( m_frame < ( endFrame - 1.0f ) )
				{
					// there's an end key coming, so let someone else handle it
					return false;
				}
			}
  			
			// forwards
			return ( m_frame >= ( startFrame - 1.0f ) && m_frame <= endFrame );
		}
		else
		{
			// backwards
			return ( m_frame <= ( startFrame + 1.0f ) && m_frame >= endFrame );
		}
	}
	else
	{
		if ( startFrame < endFrame )
		{
			if ( m_endKey )
			{
				if ( endFrame > m_frame )
				{
					// let it interpolate
					return false;
				}
			}
			else
			{
				if ( m_frame < ( endFrame - 1.0f ) )
				{
					// there's an end key coming, so let someone else handle it
					return false;
				}
			}
  			
			// forwards
			return ( m_frame >= ( startFrame - 1.0f ) && m_frame < endFrame );
		}
		else
		{
			// backwards
			return ( m_frame <= ( startFrame + 1.0f ) && m_frame > endFrame );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CEmptyKey::CEmptyKey( int frame ) : CCustomAnimKey( frame )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CEmptyKey::process_key( Obj::CObject* pObject )
{
	// do nothing

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CEventKey::CEventKey( int frame, uint32 eventType, Script::CStruct* pEventParams ) : CCustomAnimKey( frame )
{
	m_eventType = eventType;
	mp_eventParams = new Script::CStruct;
	mp_eventParams->AppendStructure( pEventParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CEventKey::~CEventKey()
{
	if ( mp_eventParams )
	{
		delete mp_eventParams;
		mp_eventParams = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CEventKey::process_key( Obj::CObject* pObject )
{
	pObject->SelfEvent( m_eventType, mp_eventParams );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/				

CCreateObjectFromStructKey::CCreateObjectFromStructKey( int frame, uint32 objectStructName ) : CCustomAnimKey( frame ), m_objectStructName( objectStructName )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCreateObjectFromStructKey::process_key( Obj::CObject* pObject )
{
	if ( Script::GetInt( CRCD(0x2f510f45,"moviecam_debug"), false ) )
	{
		Dbg_Message( "Processing Create Object From Struct key (struct = %s) @ %d!", Script::FindChecksumName( m_objectStructName ), m_frame );
	}

	Script::CStruct* pObjectStruct = Script::GetStructure( m_objectStructName, Script::ASSERT );
	if ( pObjectStruct )
	{
		Script::RunScript( CRCD(0xbdf7f843,"CreateFromStructure"), pObjectStruct );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/				

CKillObjectFromStructKey::CKillObjectFromStructKey( int frame, uint32 objectStructName ) : CCustomAnimKey( frame ), m_objectStructName( objectStructName )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CKillObjectFromStructKey::process_key( Obj::CObject* pObject )
{
	if ( Script::GetInt( CRCD(0x2f510f45,"moviecam_debug"), false ) )
	{
		Dbg_Message( "Processing Kill Object From Struct key (script = %s) @ %d!", Script::FindChecksumName( m_objectStructName ), m_frame );
	}
	
	Script::CStruct* pObjectStruct = Script::GetStructure( m_objectStructName, Script::ASSERT );
	if ( pObjectStruct )
	{
		uint32 objectName;
		pObjectStruct->GetChecksum( CRCD(0xa1dc81f9,"name"), &objectName, Script::ASSERT );
		
		Obj::CObject* pObject = (Obj::CObject*)Obj::ResolveToObject( objectName );
		pObject->MarkAsDead();
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCustomAnimKey* ReadCustomAnimKey( uint8** pData )
{
	// factory function for generating a custom key...

	// TODO:  might need to push the heap context

	SIntermediateCustomAnimKeyHeader theIntermediateHeader;
	memcpy(&theIntermediateHeader, *pData, sizeof(SIntermediateCustomAnimKeyHeader));
	*pData += sizeof(SIntermediateCustomAnimKeyHeader);

	switch ( theIntermediateHeader.keyType )
	{
		case vCHANGE_FOCAL_LENGTH:
			{
				float fov;
				memcpy(&fov, *pData, sizeof(float));				// May not be word-aligned in memory
				*pData += sizeof(float);
				return new CChangeFOVKey( theIntermediateHeader.timeStamp, fov );
			}
			break;
		case vRUN_SCRIPT:
			{
				uint32 script_name;
				memcpy(&script_name, *pData, sizeof(uint32));		// May not be word-aligned in memory
				*pData += sizeof(uint32);
				return new CRunScriptKey( theIntermediateHeader.timeStamp, script_name );
			}
			break;
		case vCHANGE_CAMERA_RT:
			{
				Mth::Quat theQuat;
				Mth::Vector theVector;
				memcpy(&theVector, *pData, sizeof(Mth::Vector));
				*pData += sizeof(Mth::Vector);
				memcpy(&theQuat, *pData, sizeof(Mth::Quat));
				*pData += sizeof(Mth::Quat);
				return new CChangeCameraRTKey( theIntermediateHeader.timeStamp, theQuat, theVector, false );
			}
			break;
		case vCHANGE_CAMERA_RT_ENDKEY:
			{
				Mth::Quat theQuat;
				Mth::Vector theVector;
				memcpy(&theVector, *pData, sizeof(Mth::Vector));
				*pData += sizeof(Mth::Vector);
				memcpy(&theQuat, *pData, sizeof(Mth::Quat));
				*pData += sizeof(Mth::Quat);
				return new CChangeCameraRTKey( theIntermediateHeader.timeStamp, theQuat, theVector, true );
			}
			break;
		case vCHANGE_CAMERA_RT_IGNORE:
			{
           		// GJ:  There's a bug in the exporter where
				// 2 camera RT keys with different positions
				// are listed for the same time...  this causes
				// a glitch during certain camera transitions.
				// To fix, I will replace the second key with one
				// that does nothing...
				*pData += sizeof(Mth::Vector);
				*pData += sizeof(Mth::Quat);

				return new CEmptyKey( theIntermediateHeader.timeStamp );
			}
			break;
		case vCREATE_OBJECT_FROM_STRUCT:
			{
				uint32 object_struct_name;
				memcpy(&object_struct_name, *pData, sizeof(uint32));		// May not be word-aligned in memory
				*pData += sizeof(uint32);
				return new CCreateObjectFromStructKey( theIntermediateHeader.timeStamp, object_struct_name );
			}
			break;
		case vKILL_OBJECT_FROM_STRUCT:
			{
				uint32 object_struct_name;
				memcpy(&object_struct_name, *pData, sizeof(uint32));		// May not be word-aligned in memory
				*pData += sizeof(uint32);
				return new CKillObjectFromStructKey( theIntermediateHeader.timeStamp, object_struct_name );
			}
			break;
		default:
			{
				Dbg_Message( "Warning:  Ignoring custom anim key (type %08x is currently unsupported).", theIntermediateHeader.keyType );

				// just skip past the size
				Dbg_Assert( theIntermediateHeader.size - sizeof(SIntermediateCustomAnimKeyHeader) < 512 );
				*pData += theIntermediateHeader.size;
				Dbg_Assert(!((uint) *pData & 0x3));

				return NULL;
			}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx


================================================
FILE: Code/Gfx/CustomAnimKey.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       CustomAnimKey.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  02/08/2002
//****************************************************************************

#ifndef __GFX_CUSTOMANIMKEY_H
#define __GFX_CUSTOMANIMKEY_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{
	class CObject;
}

namespace Script
{
	class CStruct;
}

namespace Gfx
{
	enum RangeFlags
	{
		mSTART_INCLUSIVE = ( 1 << 0 ),
		mSTART_EXCLUSIVE = ( 1 << 1 ),
		mEND_INCLUSIVE = ( 1 << 2 ),
		mEND_EXCLUSIVE = ( 1 << 3 ),
	};

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

// interface to all the custom keys
// (the custom keys are defined in the CPP file)
// there should be no allocation going on in these keys

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// class CCustomAnimKey
class CCustomAnimKey : public Lst::Node
{
public:
	CCustomAnimKey( int time );
	virtual bool	WithinRange( float startFrame, float endFrame, bool inclusive = false );
	void			SetActive( bool active );

public:
	bool			ProcessKey( Obj::CObject* pObject );

protected:
	virtual bool	process_key( Obj::CObject* pObject ) = 0;

protected:
	int				m_frame;
	bool			m_active;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// class CEventKey
class CEventKey : public CCustomAnimKey
{
public:
	CEventKey( int time, uint32 eventType, Script::CStruct* pParams );
	virtual ~CEventKey();

public:
	virtual	bool		process_key( Obj::CObject* pObject );

protected:
	uint32				m_eventType;
	Script::CStruct*	mp_eventParams;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

CCustomAnimKey* ReadCustomAnimKey( uint8** pData );

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // namespace Gfx

#endif	// __GFX_CUSTOMANIMKEY_H





================================================
FILE: Code/Gfx/FaceMassage.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// FaceMassage.cpp

#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

namespace	Nx
{

#ifdef __NOPT_ASSERT__
void			SFacePoints::PrintData()
{
	Dbg_Message("FacePoints left_eye (%d, %d)", m_left_eye[X], m_left_eye[Y]);
	Dbg_Message("FacePoints right_eye (%d, %d)", m_right_eye[X], m_right_eye[Y]);
	Dbg_Message("FacePoints nose (%d, %d)", m_nose[X], m_nose[Y]);
	Dbg_Message("FacePoints lips (%d, %d)", m_lips[X], m_lips[Y]);
	Dbg_Message("FacePoints adjust HSV (%s)", m_adjust_hsv ? "true" : "false");
	if (m_adjust_hsv)
	{
		Dbg_Message("FacePoints H offset (%f)", m_h_offset);
		Dbg_Message("FacePoints S scale (%f)", m_s_scale);
		Dbg_Message("FacePoints V scale (%f)", m_v_scale);
	}
	Dbg_Message("FacePoints texture size (%d, %d)", m_texture_width, m_texture_height);
}
#endif // __NOPT_ASSERT__

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SFacePoints s_default_face_points = {
	{  49,  54 },		// Left eye
	{  76,  54 },		// Right eye
	{  64,  75 },		// Nose
	{  64,  89 },		// Lips
	false,				// Adjust HSV
	0.0f,				// H offset
	1.0f,				// S scale
	1.0f,				// V scale
	128,				// Texture width
	128					// Texture height
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Utility functions
void SetDefaultFacePoints(SFacePoints* pFacePoints)
{
	Dbg_MsgAssert( pFacePoints, ( "No face points" ) );

	*pFacePoints = s_default_face_points;
}  

///////////////////////////////////////////////////////////////////////////////
// CFaceTexMassager

// copy over default face points
SFacePoints		CFaceTexMassager::s_model_face_points = s_default_face_points;

CTexture *		CFaceTexMassager::sp_face_texture_overlay = NULL;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CFaceTexMassager::sSetModelFacePoints(const SFacePoints &f_points)
{
	s_model_face_points = f_points;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CFaceTexMassager::sSetFaceTextureOverlay(CTexture *p_texture)
{
	if (p_texture)
	{
		Dbg_MsgAssert(p_texture->GetWidth() == s_model_face_points.m_texture_width, ("Face texture overlay width is not equal to the face points width"));
		Dbg_MsgAssert(p_texture->GetHeight() == s_model_face_points.m_texture_height, ("Face texture overlay height is not equal to the face points height"));
	}

	sp_face_texture_overlay = p_texture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#define PRINT_TIMES 0

bool			CFaceTexMassager::sMassageTexture(CTexture *p_face_texture, const SFacePoints &c_texture_points, bool palette_gen,
												  bool use_fill_color, Image::RGBA fill_color)
{
	Dbg_Assert(p_face_texture);

#if PRINT_TIMES
	uint32 start_time = Tmr::GetTimeInUSeconds();
#endif

	// Convert texture to 32-bit, if not already (speeds conversion)
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	p_face_texture->Generate32BitImage();
	Mem::Manager::sHandle().PopContext();

	// Adjust the texture based on the model face points
	sAdjustTextureToModel(p_face_texture, c_texture_points, use_fill_color, fill_color);

	// Adjust the texture colors
	sAdjustTextureColors(p_face_texture, c_texture_points);

	// And put overlay on top, if any
	if (sp_face_texture_overlay)
	{
		p_face_texture->CombineTextures(sp_face_texture_overlay, palette_gen);
	}

	// Put 32-bit image into texture
	p_face_texture->Put32BitImageIntoTexture(palette_gen);

#if PRINT_TIMES
	uint32 end_time = Tmr::GetTimeInUSeconds();
	Dbg_Message("sMassageTexture Update time %d us", end_time - start_time);
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CFaceTexMassager::sAdjustTextureToModel(CTexture *p_face_texture, const SFacePoints &c_texture_points,
														bool use_fill_color, Image::RGBA fill_color)
{
	Dbg_Assert(p_face_texture);
	Dbg_MsgAssert(p_face_texture->GetWidth() == s_model_face_points.m_texture_width, ("Face texture width is not equal to the face points width"));
	Dbg_MsgAssert(p_face_texture->GetHeight() == s_model_face_points.m_texture_height, ("Face texture height is not equal to the face points height"));

	// Make a local copy of the texture points, as we might need to modify it to make it safe											 
	SFacePoints texture_points = c_texture_points;
											 
// Make the values "safe" (should also do this at the user interface stage)


// if eyes are flipped, then flip them back
	int t;
	if (texture_points.m_left_eye[X] > texture_points.m_right_eye[X])
	{
		t = texture_points.m_left_eye[X];
		texture_points.m_left_eye[X] = texture_points.m_right_eye[X];
		texture_points.m_right_eye[X] = t;
	}
	
	
// If Left eye is below the nose then put it above it.	
	if (texture_points.m_left_eye[Y] > texture_points.m_nose[Y])
	{
		texture_points.m_left_eye[Y] = texture_points.m_nose[Y]-1;
	}


// If Right eye is below the nose then put it above it.	
	if (texture_points.m_right_eye[Y] >= texture_points.m_nose[Y])
	{
		texture_points.m_right_eye[Y] = texture_points.m_nose[Y]-2;
	}

// if Lips is above the nose, then put it below
	if (texture_points.m_lips[Y] <= texture_points.m_nose[Y])
	{
		texture_points.m_lips[Y] = texture_points.m_nose[Y]+2;
	}

	// All stretching and shrinking will be done at the nose point
	int x_axis_point = s_model_face_points.m_nose[X];
	int y_axis_point = s_model_face_points.m_nose[Y];

	// First, offset the texture by the difference in nose points
	int x_offset = s_model_face_points.m_nose[X] - texture_points.m_nose[X];
	int y_offset = s_model_face_points.m_nose[Y] - texture_points.m_nose[Y];
	p_face_texture->Offset(x_offset, y_offset);

	int model_eyeline_y = (s_model_face_points.m_left_eye[Y] + s_model_face_points.m_right_eye[Y]) >> 1;
	int texture_eyeline_y = ((texture_points.m_left_eye[Y] + texture_points.m_right_eye[Y]) >> 1) + y_offset;

	// Calculate the ratios to find how much we need to pull/push a texture section
	float left_eye_to_nose_width = texture_points.m_nose[X] - texture_points.m_left_eye[X];
	float left_nose_width = texture_points.m_nose[X] + x_offset;
	float left_eye_ratio = left_nose_width / left_eye_to_nose_width;

	float right_eye_to_nose_width = texture_points.m_right_eye[X] - texture_points.m_nose[X];
	float right_nose_width = p_face_texture->GetWidth() - (texture_points.m_nose[X] + x_offset);
	float right_eye_ratio = right_nose_width / right_eye_to_nose_width;

	float eyeline_to_nose_height = (texture_points.m_nose[Y] + y_offset) - texture_eyeline_y;
	float top_nose_height = texture_points.m_nose[Y] + y_offset;
	float eyeline_ratio = top_nose_height / eyeline_to_nose_height;

	float lips_to_nose_height = texture_points.m_lips[Y] - texture_points.m_nose[Y];
	float bottom_nose_height = p_face_texture->GetHeight() - (texture_points.m_nose[Y] + y_offset);
	float lips_ratio = bottom_nose_height / lips_to_nose_height;

	// Move the left eye along the X axis
	int left_eye_pixels = s_model_face_points.m_left_eye[X] - (texture_points.m_left_eye[X] + x_offset);
	//Dbg_Message("Left eye pixels before %d", left_eye_pixels);
	//Dbg_Message("Left eye to_nose %f width %f", left_eye_to_nose_width, left_nose_width);
	left_eye_pixels = (int) ((left_eye_pixels * left_eye_ratio) + 0.5f);
	//Dbg_Message("Left eye pixels after %d", left_eye_pixels);
	if (left_eye_pixels < 0)
	{
		// Stretch out
		p_face_texture->PullToEdge(x_axis_point, X, left_eye_pixels);
	}
	else
	{
		// Shrink
		p_face_texture->PushToPoint(x_axis_point, X, left_eye_pixels, use_fill_color, fill_color);
	}

	// Move the right eye along the X axis
	int right_eye_pixels = s_model_face_points.m_right_eye[X] - (texture_points.m_right_eye[X] + x_offset);
	//Dbg_Message("Right eye pixels before %d", right_eye_pixels);
	//Dbg_Message("Right eye to_nose %f width %f", right_eye_to_nose_width, right_nose_width);
	right_eye_pixels = (int) ((right_eye_pixels * right_eye_ratio) + 0.5f);
	//Dbg_Message("Right eye pixels after %d", right_eye_pixels);
	if (right_eye_pixels >= 0)
	{
		// Stretch out
		p_face_texture->PullToEdge(x_axis_point, X, right_eye_pixels);
	}
	else
	{
		// Shrink
		p_face_texture->PushToPoint(x_axis_point, X, right_eye_pixels, use_fill_color, fill_color);
	}

	// Move the eyeline along the Y axis
	int eyeline_pixels = model_eyeline_y - texture_eyeline_y;
	eyeline_pixels = (int) ((eyeline_pixels * eyeline_ratio) + 0.5f);
	if (eyeline_pixels < 0)
	{
		// Stretch out
		p_face_texture->PullToEdge(y_axis_point, Y, eyeline_pixels);
	}
	else
	{
		// Shrink
		p_face_texture->PushToPoint(y_axis_point, Y, eyeline_pixels, use_fill_color, fill_color);
	}

	// Move the lips along the Y axis
	int lips_pixels = s_model_face_points.m_lips[Y] - (texture_points.m_lips[Y] + y_offset);
	lips_pixels = (int) ((lips_pixels * lips_ratio) + 0.5f);
	if (lips_pixels >= 0)
	{
		// Stretch out
		p_face_texture->PullToEdge(y_axis_point, Y, lips_pixels);
	}
	else
	{
		// Shrink
		p_face_texture->PushToPoint(y_axis_point, Y, lips_pixels, use_fill_color, fill_color);
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CFaceTexMassager::sAdjustTextureColors(CTexture *p_face_texture, const SFacePoints &texture_points)
{
	Dbg_Assert(p_face_texture);

	if (texture_points.m_adjust_hsv)
	{
		p_face_texture->AdjustHSV(texture_points.m_h_offset, texture_points.m_s_scale, texture_points.m_v_scale, false);
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CFaceTexMassager::sCombineTextureWithOverlay(CTexture *p_face_texture)
{
	Dbg_Assert(p_face_texture);

	if (sp_face_texture_overlay)
	{
		p_face_texture->CombineTextures(sp_face_texture_overlay, false);
		return true;
	}

	return false;
}

////////////////////////////////////////////////////////////////////////////////////////
// Script functions

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool SetFacePointsStruct(const SFacePoints& face_points, Script::CStruct *p_struct)
{
	Dbg_Assert(p_struct);

	Script::CArray* p_left_eye = new Script::CArray;
	p_left_eye->SetSizeAndType( 2, ESYMBOLTYPE_INTEGER );
	p_left_eye->SetInteger( 0, face_points.m_left_eye[X] );
	p_left_eye->SetInteger( 1, face_points.m_left_eye[Y] );
	
	Script::CArray* p_right_eye = new Script::CArray;
	p_right_eye->SetSizeAndType( 2, ESYMBOLTYPE_INTEGER );
	p_right_eye->SetInteger( 0, face_points.m_right_eye[X] );
	p_right_eye->SetInteger( 1, face_points.m_right_eye[Y] );
	
	Script::CArray* p_nose = new Script::CArray;
	p_nose->SetSizeAndType( 2, ESYMBOLTYPE_INTEGER );
	p_nose->SetInteger( 0, face_points.m_nose[X] );
	p_nose->SetInteger( 1, face_points.m_nose[Y] );
	
	Script::CArray* p_lips = new Script::CArray;
	p_lips->SetSizeAndType( 2, ESYMBOLTYPE_INTEGER );
	p_lips->SetInteger( 0, face_points.m_lips[X] );
	p_lips->SetInteger( 1, face_points.m_lips[Y] );

	p_struct->AddArrayPointer( CRCD(0x08bf1d3f,"left_eye"), p_left_eye );
	p_struct->AddArrayPointer( CRCD(0xb0b44396,"right_eye"), p_right_eye );
	p_struct->AddArrayPointer( CRCD(0x7f03932c,"nose"), p_nose );
	p_struct->AddArrayPointer( CRCD(0x0e7ec187,"lips"), p_lips );

	if (face_points.m_adjust_hsv)
	{
		p_struct->AddFloat(CRCD(0x6e94f918,"h"), face_points.m_h_offset);
		p_struct->AddFloat(CRCD(0xe4f130f4,"s"), face_points.m_s_scale);
		p_struct->AddFloat(CRCD(0x949bc47b,"v"), face_points.m_v_scale);
	}

	p_struct->AddInteger( CRCD(0x73e5bad0,"width"), face_points.m_texture_width );
	p_struct->AddInteger( CRCD(0x0ab21af0,"height"), face_points.m_texture_height );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool GetFacePointsStruct(SFacePoints &face_points, Script::CStruct *p_struct)
{
	Dbg_Assert(p_struct);

	Script::CArray *p_left_eye = NULL;
	Script::CArray *p_right_eye = NULL;
	Script::CArray *p_nose = NULL;
	Script::CArray *p_lips = NULL;

	// Left eye
	p_struct->GetArray( CRCD(0x08bf1d3f,"left_eye"), &p_left_eye );
	if (p_left_eye)
	{
		face_points.m_left_eye[X] = p_left_eye->GetInteger(0);
		face_points.m_left_eye[Y] = p_left_eye->GetInteger(1);
	}
	else
	{
		Dbg_MsgAssert(0, ("Couldn't find member left_eye"))
		return false;	
	}

	// Right eye
	p_struct->GetArray( CRCD(0xb0b44396,"right_eye"), &p_right_eye );
	if (p_right_eye)
	{
		face_points.m_right_eye[X] = p_right_eye->GetInteger(0);
		face_points.m_right_eye[Y] = p_right_eye->GetInteger(1);
	}
	else
	{
		Dbg_MsgAssert(0, ("Couldn't find member right_eye"))
		return false;	
	}

	// Nose
	p_struct->GetArray( CRCD(0x7f03932c,"nose"), &p_nose );
	if (p_nose)
	{
		face_points.m_nose[X] = p_nose->GetInteger(0);
		face_points.m_nose[Y] = p_nose->GetInteger(1);
	}
	else
	{
		Dbg_MsgAssert(0, ("Couldn't find member nose"))
		return false;	
	}

	// Lips
	p_struct->GetArray( CRCD(0x0e7ec187,"lips"), &p_lips );
	if (p_lips)
	{
		face_points.m_lips[X] = p_lips->GetInteger(0);
		face_points.m_lips[Y] = p_lips->GetInteger(1);
	}
	else
	{
		Dbg_MsgAssert(0, ("Couldn't find member lips"))
		return false;	
	}

	// Grab color adjustment values, if any
	face_points.m_adjust_hsv = p_struct->GetFloat(CRCD(0x6e94f918,"h"), &face_points.m_h_offset);
	face_points.m_adjust_hsv = p_struct->GetFloat(CRCD(0xe4f130f4,"s"), &face_points.m_s_scale) && face_points.m_adjust_hsv;
	face_points.m_adjust_hsv = p_struct->GetFloat(CRCD(0x949bc47b,"v"), &face_points.m_v_scale) && face_points.m_adjust_hsv;

	if (face_points.m_adjust_hsv)
	{
		Dbg_MsgAssert((face_points.m_h_offset >= 0.0f) && (face_points.m_h_offset <= 360.0f), ("h must be in the range of 0-360"));
		Dbg_MsgAssert(face_points.m_s_scale >= 0.0f, ("s cannot be negative"));
		Dbg_MsgAssert(face_points.m_v_scale >= 0.0f, ("v cannot be negative"));
	}

	// Width and height (some functions may not care, so init them to 0)
	face_points.m_texture_width = 0;
	face_points.m_texture_height = 0;
	p_struct->GetInteger( CRCD(0x73e5bad0,"width"), &face_points.m_texture_width );
	p_struct->GetInteger( CRCD(0x0ab21af0,"height"), &face_points.m_texture_height );

	return true;
}

// @script | SetModelFaceTexturePoints | Sets the face points for the face texture of the model
// @parm struct | face_points | face points structure
bool ScriptSetModelFaceTexturePoints(Script::CStruct *pParams, Script::CScript *pScript)
{
	SFacePoints face_points;

	Script::CStruct *pFacePointsStruct = NULL;
	pParams->GetStructure("face_points", &pFacePointsStruct);

	if (pFacePointsStruct)
	{
		GetFacePointsStruct(face_points, pFacePointsStruct);

		Dbg_MsgAssert(face_points.m_texture_width != 0, ("Must set texture width"));
		Dbg_MsgAssert(face_points.m_texture_height != 0, ("Must set texture height"));

		CFaceTexMassager::sSetModelFacePoints(face_points);
		//face_points.PrintData();
	}
	else
	{
		Dbg_MsgAssert(0, ("The face_points structure needs to be supplied"));
	}

	return true;
}

// @script | SetFaceMassageTextureOverlay | Sets the texture that is used as an overlay to the massaged face texture 
// @parm name |  | name of texture
bool ScriptSetFaceMassageTextureOverlay(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(NONAME, &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		CFaceTexMassager::sSetFaceTextureOverlay(p_texture);
	} else {
		Dbg_MsgAssert(0, ("Can't find texture %s to adjust", Script::FindChecksumName(checksum)));
	}

	return true;
}

// @script | MassageFaceTexture | Transform the supplied face texture to one that can be used with a model
// @parm name | texture | name of texture
// @parm struct | face_points | face points structure
// @parmopt flag | no_palette_gen | | use original palette (faster)
bool ScriptMassageFaceTexture(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(CRCD(0x7d99f28d,"texture"), &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		bool palette_gen = true;
		SFacePoints face_points;

		Script::CStruct *pFacePointsStruct = NULL;
		pParams->GetStructure(CRCD(0xac3cd84c,"face_points"), &pFacePointsStruct);

		if (pParams->ContainsFlag(CRCD(0x5905256b,"no_palette_gen")))
		{
			palette_gen = false;
		}

		if (pFacePointsStruct)
		{
			GetFacePointsStruct(face_points, pFacePointsStruct);

			CFaceTexMassager::sMassageTexture(p_texture, face_points, palette_gen);
			//CFaceTexMassager::sMassageTexture(p_texture, face_points, palette_gen, true, Image::RGBA(255, 0, 0, 128));
		}
		else
		{
			Dbg_MsgAssert(0, ("The face_points structure needs to be supplied"));
		}

	} else {
		Dbg_MsgAssert(0, ("Can't find texture %s to massage", Script::FindChecksumName(checksum)));
	}

	return true;
}

// @script | AdjustFaceTextureToModel | Pulls portions of the face texture from the supplied points to the model points
// @parm name | texture | name of texture
// @parm struct | face_points | face points structure
bool ScriptAdjustFaceTextureToModel(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(CRCD(0x7d99f28d,"texture"), &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));

	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		SFacePoints face_points;

		Script::CStruct *pFacePointsStruct = NULL;
		pParams->GetStructure(CRCD(0xac3cd84c,"face_points"), &pFacePointsStruct);

		if (pFacePointsStruct)
		{
			GetFacePointsStruct(face_points, pFacePointsStruct);

			CFaceTexMassager::sAdjustTextureToModel(p_texture, face_points);
		}
		else
		{
			Dbg_MsgAssert(0, ("The face_points structure needs to be supplied"));
		}
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't find texture %s to adjust", Script::FindChecksumName(checksum)));
	}

	return true;
}

// @script | AdjustFaceTextureColors | Does all the requested color adjustments to the face texture.
// @parm name | texture | name of texture
// @parm struct | face_points | face points structure
bool ScriptAdjustFaceTextureColors(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(CRCD(0x7d99f28d,"texture"), &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));

	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		SFacePoints face_points;

		Script::CStruct *pFacePointsStruct = NULL;
		pParams->GetStructure(CRCD(0xac3cd84c,"face_points"), &pFacePointsStruct);

		if (pFacePointsStruct)
		{
			GetFacePointsStruct(face_points, pFacePointsStruct);

			CFaceTexMassager::sAdjustTextureColors(p_texture, face_points);
		}
		else
		{
			Dbg_MsgAssert(0, ("The face_points structure needs to be supplied"));
		}
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't find texture %s to adjust", Script::FindChecksumName(checksum)));
	}

	return true;
}

// @script | CombineFaceTextureWithOverlay | Puts overlay texture on top of face texture and combines them.
// @parm name | texture | name of texture
bool ScriptCombineFaceTextureWithOverlay(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(CRCD(0x7d99f28d,"texture"), &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));

	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		CFaceTexMassager::sCombineTextureWithOverlay(p_texture);
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't find texture %s to combine", Script::FindChecksumName(checksum)));
	}

	return true;
}

} // namespace Nx




================================================
FILE: Code/Gfx/FaceMassage.h
================================================
///////////////////////////////////////////////////////////////////////////////
// FaceMassage.h

#ifndef	__GFX_FACE_MASSAGE_H__
#define	__GFX_FACE_MASSAGE_H__

#include 
#include 

#include 

namespace Script
{
	class CStruct;
	class CScript;
}

namespace Nx
{

// Forward declarations
class CTexture;

////////////////////////////////////////////////////////////////////////////
// Holds all the face coordinates
struct SFacePoints
{
	// Constants
	enum
	{
		NUM_AXES	= 2,			// Just two axes for 2D coordinates
	};

	// Eye points
	int				m_left_eye[NUM_AXES];
	int				m_right_eye[NUM_AXES];

	// Nose point (between the nostrils)
	int				m_nose[NUM_AXES];

	// Lips point (center)
	int				m_lips[NUM_AXES];

	// Color HSV adjustments
	bool			m_adjust_hsv;
	float			m_h_offset;				// Added to each pixel H value (0 - 360)
	float			m_s_scale;				// Scale applied to each pixel S value (1=no change)
	float			m_v_scale;				// Scale applied to each pixel V value (1=no change)

	// Texture size
	int				m_texture_width;
	int				m_texture_height;

#ifdef __NOPT_ASSERT__
	void			PrintData();
#endif // __NOPT_ASSERT__
};

//////////////////////////////////////////////////////////////////////////////
// Transforms a face texture into one that can be used by a model.
class CFaceTexMassager
{
public:
	// Set data
	static void				sSetModelFacePoints(const SFacePoints &f_points);
	static void				sSetFaceTextureOverlay(CTexture *p_texture);

	// Massage the face texture into a usable face texture for a model (overwrites old texture pixel data)
	static bool				sMassageTexture(CTexture *p_face_texture, const SFacePoints &texture_points, bool palette_gen = true,
											bool use_fill_color = false, Image::RGBA fill_color = Image::RGBA(128, 128, 128, 128));

	// Individual massage steps (mainly for editor).  Note that these functions do not convert to 32-bit or back to
	// 8-bit at the end.  These functions need to be called separately.
	static bool				sAdjustTextureToModel(CTexture *p_face_texture, const SFacePoints &texture_points,
												  bool use_fill_color = false, Image::RGBA fill_color = Image::RGBA(128, 128, 128, 128));
	static bool				sAdjustTextureColors(CTexture *p_face_texture, const SFacePoints &texture_points);
	static bool				sCombineTextureWithOverlay(CTexture *p_face_texture);

protected:

	static SFacePoints		s_model_face_points;		// Face points needed for a model

	static CTexture *		sp_face_texture_overlay;  	// The texture that needs to go over the top of a supplied face texture

};

// Script functions
bool ScriptSetModelFaceTexturePoints(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetFaceMassageTextureOverlay(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptMassageFaceTexture(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAdjustFaceTextureToModel(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAdjustFaceTextureColors(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCombineFaceTextureWithOverlay(Script::CStruct *pParams, Script::CScript *pScript);

// Utility functions
void SetDefaultFacePoints(SFacePoints* pFacePoints);
bool SetFacePointsStruct(const SFacePoints &face_points, Script::CStruct *p_struct);
bool GetFacePointsStruct(SFacePoints &face_points, Script::CStruct *p_struct);

}

#endif //	__GFX_FACE_MASSAGE_H__



================================================
FILE: Code/Gfx/FaceTexture.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       FaceTexture.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  4/29/2003
//****************************************************************************

#include 

#include 
#include 
#include 

// for temporary texture
#include 

namespace Gfx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CFaceTexture::CFaceTexture()
{
	m_isValid = false;

	SetDefaultFacePoints( &m_points );
	
	SetOverlayTextureName( "faces\\CS_NSN_Head_wht_alpha" );

/*
	// should already be cleared out
	memset( m_rawData, 0, vFACE_TEXTURE_SIZE );
*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFaceTexture::LoadFace( const char* pFaceName, bool fullPathIncluded )
{
	// As a test, set the freak image
	// (otherwise, we'd need to go through the face download interface)
	char extended_filename[512];
	int file_size;

	if ( fullPathIncluded )
	{
		sprintf( extended_filename, "%s.img.ps2", pFaceName );
	}
	else
	{
		sprintf( extended_filename, "images\\%s.img.ps2", pFaceName );	
	}

	char* pData = (char*)File::LoadAlloc( extended_filename, &file_size );
	memcpy( m_rawData, pData, file_size );
	Dbg_MsgAssert( file_size <= vRAW_TEXTURE_SIZE, ( "File too big (file is %d, buffer is %d)", file_size, vRAW_TEXTURE_SIZE ) );
	Mem::Free( pData );
	
	m_isValid = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8* CFaceTexture::ReadTextureDataFromBuffer(uint8* pBuffer, int bufferSize)
{
	// READ RAW IMG.PS2 DATA ONLY, NOT FACE POINT OR OVERLAY INFO

	// if a safety-check was specified...
	#ifdef	__NOPT_ASSERT__
	if ( bufferSize )
	{
		int totalSize = vRAW_TEXTURE_SIZE;
		Dbg_MsgAssert( bufferSize >= totalSize, ( "Buffer size (%d bytes) must <= %d bytes", bufferSize, totalSize ) );
	}
	#endif
	
	memcpy( &m_rawData[0], pBuffer, vRAW_TEXTURE_SIZE );

	pBuffer += vRAW_TEXTURE_SIZE;
	
	return pBuffer;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CFaceTexture::WriteToBuffer( uint8* pBuffer, int bufferSize )
{
	int totalSize = 0;
		
	totalSize += vRAW_TEXTURE_SIZE;
	Dbg_MsgAssert( bufferSize >= totalSize, ( "Buffer size (%d bytes) must >= %d bytes", bufferSize, totalSize ) );
	memcpy( pBuffer, &m_rawData[0], vRAW_TEXTURE_SIZE );
	pBuffer += vRAW_TEXTURE_SIZE;
	
	totalSize += sizeof(Nx::SFacePoints);
	Dbg_MsgAssert( bufferSize >= totalSize, ( "Buffer size (%d bytes) must >= %d bytes", bufferSize, totalSize ) );
	memcpy( pBuffer, &m_points, sizeof(Nx::SFacePoints) );
	pBuffer += sizeof(Nx::SFacePoints);

	totalSize += ( strlen( m_overlayTextureName ) + 1 );
	Dbg_MsgAssert( bufferSize >= totalSize, ( "Buffer size (%d bytes) must >= %d bytes", bufferSize, totalSize ) );
	memcpy( pBuffer, m_overlayTextureName, strlen( m_overlayTextureName ) + 1 );
	pBuffer += ( strlen( m_overlayTextureName ) + 1 );

	return totalSize;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8* CFaceTexture::ReadFromBuffer( uint8* pBuffer, int bufferSize )
{
	int totalSize = 0;
	
	totalSize += vRAW_TEXTURE_SIZE;
	if ( bufferSize )
	{
		// if a safety-check was specified...
		Dbg_MsgAssert( bufferSize >= totalSize, ( "Buffer size (%d bytes) must <= %d bytes", bufferSize, totalSize ) );
	}
	memcpy( &m_rawData[0], pBuffer, vRAW_TEXTURE_SIZE );
	pBuffer += vRAW_TEXTURE_SIZE;
	
	totalSize += sizeof(Nx::SFacePoints);
	if ( bufferSize )
	{
		// if a safety-check was specified...
		Dbg_MsgAssert( bufferSize >= totalSize, ( "Buffer size (%d bytes) must <= %d bytes", bufferSize, totalSize ) );
	}
	memcpy( &m_points, pBuffer, sizeof(Nx::SFacePoints) );
	pBuffer += sizeof(Nx::SFacePoints);

	int overlayNameLength = strlen( (char*)pBuffer );
	if ( overlayNameLength >= vMAX_OVERLAY_NAME_SIZE )
	{
		Dbg_MsgAssert( 0, ( "Unusual length for overlay name" ) );
	}
	
	totalSize += ( overlayNameLength + 1 );
	if ( bufferSize )
	{
		// if a safety-check was specified...
		Dbg_MsgAssert( bufferSize >= totalSize, ( "Buffer size (%d bytes) must <= %d bytes", bufferSize, totalSize ) );
	}
	memcpy( m_overlayTextureName, pBuffer, overlayNameLength + 1 );
	pBuffer += ( overlayNameLength + 1 );

	return pBuffer;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CFaceTexture::WriteIntoStructure( Script::CStruct* pSubStruct )
{
	Dbg_Assert( pSubStruct );        
	Dbg_MsgAssert( IsValid(), ( "Face texture is not valid!" ) );

	Script::CArray* pRawTextureArray = new Script::CArray;
	Dbg_MsgAssert( ( vRAW_TEXTURE_SIZE ) % 4 == 0, ( "Was expecting raw texture size to be a multiple of 4", vRAW_TEXTURE_SIZE ) ); 
	pRawTextureArray->SetSizeAndType( vRAW_TEXTURE_SIZE / 4, ESYMBOLTYPE_NAME );

	uint32* pFaceData = (uint32*)&m_rawData[0];

	for ( int i = 0; i < (vRAW_TEXTURE_SIZE / 4); i++ )
	{
		
		pRawTextureArray->SetChecksum( i, *pFaceData );
		pFaceData++;
	}

	pSubStruct->AddArrayPointer( CRCD(0x99790d96,"rawTextureData"), pRawTextureArray );
	pSubStruct->AddString( CRCD(0xa7e7a264,"overlayTextureName"), m_overlayTextureName );
	Nx::SetFacePointsStruct( m_points, pSubStruct );

	// readfromstructure should also set the valid flag
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CFaceTexture::ReadFromStructure( Script::CStruct* pSubStruct )
{
	Dbg_Assert( pSubStruct );

	Script::CArray* pRawTextureArray;
	pSubStruct->GetArray( CRCD(0x99790d96,"rawTextureData"), &pRawTextureArray, Script::ASSERT );
	
	uint32* pFaceData = (uint32*)&m_rawData[0];
	Dbg_Assert( pRawTextureArray->GetSize() * sizeof(uint32) == vRAW_TEXTURE_SIZE );
	for ( uint32 i = 0; i < pRawTextureArray->GetSize(); i++ )
	{
		*pFaceData = pRawTextureArray->GetChecksum( i );
		pFaceData++;
	}
	
	const char* pOverlayTextureName;
	pSubStruct->GetString( CRCD(0xa7e7a264,"overlayTextureName"), &pOverlayTextureName, Script::ASSERT );
	strcpy( m_overlayTextureName, pOverlayTextureName );

	Nx::GetFacePointsStruct( m_points, pSubStruct );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CFaceTexture::PrintContents()
{
#ifdef __NOPT_ASSERT__
	m_points.PrintData();
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CFaceTexture::IsValid() const
{
	return m_isValid;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFaceTexture::SetValid( bool valid )
{
	m_isValid = valid;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
uint8* CFaceTexture::GetTextureData()
{
	return m_rawData;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CFaceTexture::GetTextureSize() const
{
	return vRAW_TEXTURE_SIZE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::SFacePoints CFaceTexture::GetFacePoints() const
{
	return m_points;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFaceTexture::SetFacePoints( const Nx::SFacePoints& facePoints )
{
	m_points = facePoints;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFaceTexture::SetOverlayTextureName(const char* pTextureName)
{
	Dbg_MsgAssert( pTextureName, ( "No texture name specified" ) );
	Dbg_MsgAssert( strlen(pTextureName) < vMAX_OVERLAY_NAME_SIZE, ( "Overlay name is too long %s (max=%d)", pTextureName, vMAX_OVERLAY_NAME_SIZE ) );

	strcpy( m_overlayTextureName, pTextureName );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* CFaceTexture::GetOverlayTextureName()
{
	return m_overlayTextureName;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx




================================================
FILE: Code/Gfx/FaceTexture.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       FaceTexture.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  4/29/2003
//****************************************************************************

#ifndef	__GFX_FACE_TEXTURE_H__
#define	__GFX_FACE_TEXTURE_H__

#include 
#include 

// for SFacePoints
#include 

namespace Gfx
{

//////////////////////////////////////////////////////////////////////////////
// This encapsulates all the face texture-related data
// (raw texture data, and any tweaks needed for correct
// mapping).  The "WriteToBuffer/ReadFromBuffer" is
// used to store this data to a memory card, to a skater
// profile, or for use in network games.
class CFaceTexture : public Spt::Class
{
public:
	enum
	{
		// TODO:  Find out how big the data really needs to be...
		// (right now, it's 16K for the texture data, and 1K for the palette data)
		vRAW_TEXTURE_SIZE = ( 17440 ),
		vMAX_OVERLAY_NAME_SIZE = 64,
		vTOTAL_CFACETEXTURE_SIZE = vRAW_TEXTURE_SIZE + sizeof(Nx::SFacePoints) + vMAX_OVERLAY_NAME_SIZE,
	};

	CFaceTexture();

public:
	// general accessors
	bool				IsValid() const;
	void				SetValid( bool valid );
	uint8*				GetTextureData();
	int					GetTextureSize() const;
	Nx::SFacePoints		GetFacePoints() const;
	void				SetFacePoints( const Nx::SFacePoints& facePoints );
	void				SetOverlayTextureName(const char* pTexture);
	const char*			GetOverlayTextureName();

	// debug face
	void				LoadFace( const char* pFaceName, bool fullPathIncluded );
	void				PrintContents();

public:
	// for downloading faces
	// (handles raw IMG.PS2 data only)
	uint8*				ReadTextureDataFromBuffer(uint8* pBuffer, int bufferSize);

public:
	// for network message handling
	// (handles raw IMG.PS2 data, face points, overlay information)
	int					WriteToBuffer(uint8* pBuffer, int bufferSize );
	uint8*				ReadFromBuffer(uint8* pBuffer, int bufferSize );
	
public:
	// for memory card loading/saving
	// (handles raw IMG.PS2 data, face points, overlay information)
	void 				WriteIntoStructure( Script::CStruct* pStuff );
	void 				ReadFromStructure( Script::CStruct* pStuff );

protected:
	bool				m_isValid;
	Nx::SFacePoints		m_points;
	uint8				m_rawData[vRAW_TEXTURE_SIZE];
	char				m_overlayTextureName[vMAX_OVERLAY_NAME_SIZE];
};

}

#endif //	__GFX_FACE_TEXTURE_H__



================================================
FILE: Code/Gfx/Image/ImageBasic.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		            											**
**																			**
**	Module:			              			 								**
**																			**
**	File name:		                    									**
**																			**
**	Created by:     rjm        				    			                **
**																			**
**	Description:					                                        **
**																			**
*****************************************************************************/

#ifndef __GFX_IMAGE_IMAGEBASIC_H
#define __GFX_IMAGE_IMAGEBASIC_H


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Image
{

enum JustX
{
	vJUST_LEFT 		= 0,
	vJUST_CENTER_X,
	vJUST_RIGHT,
};

enum JustY
{
	vJUST_TOP		= 0,
	vJUST_CENTER_Y,	
	vJUST_BOTTOM,
};


struct RGBA
{
	RGBA() { }
	RGBA(uint8 red, uint green, uint blue, uint alpha) :
		r(red),
		g(green),
		b(blue),
		a(alpha) { }

	void	Blend128(const RGBA & rgba);
	void	Blend255(const RGBA & rgba);

	uint8 r;
	uint8 g;
	uint8 b;
	uint8 a;
};

////////////////////////
// Inlines

inline void RGBA::Blend128(const RGBA & rgba)
{
	int blend_val;

	blend_val = r * rgba.r;
	r = (uint8) (blend_val >> 7);
	blend_val = g * rgba.g;
	g = (uint8) (blend_val >> 7);
	blend_val = b * rgba.b;
	b = (uint8) (blend_val >> 7);
	blend_val = a * rgba.a;
	a = (uint8) (blend_val >> 7);
}

inline void RGBA::Blend255(const RGBA & rgba)
{
	int blend_val;

	blend_val = r * rgba.r;
	r = (uint8) (blend_val / 255);
	blend_val = g * rgba.g;
	g = (uint8) (blend_val / 255);
	blend_val = b * rgba.b;
	b = (uint8) (blend_val / 255);
	blend_val = a * rgba.a;
	a = (uint8) (blend_val >> 7);		// Alpha is always 128 == 1.0
}

}

#endif //__GFX_IMAGE_IMAGEBASIC_H



================================================
FILE: Code/Gfx/ModelAppearance.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       ModelAppearance.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  4/2/2000
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Gfx
{

// NOTES:

// A CModelAppearance contains one script structure, whose
// contents look something like this:
//	{
//		head			= { desc_id=#"Andrew Reynolds" h = 0 s = 50 v = 100 use_default_hsv = 1 }
//		torso			= { desc_id=#"Long Sleeve - Collar" h = 0 s = 50 v = 100 use_default_hsv = 1 }
//		legs			= { desc_id=#"Reynolds's Pants" h = 0 s = 50 v = 100 use_default_hsv = 1 }
//		shoes			= { desc_id=#"Reynolds's" h = 0 s = 50 v = 100 use_default_hsv = 1 }
//		boardup			= { desc_id=#"Solid" }	
//		boarddown		= { desc_id=#"Green Brand Logo" h = 0 s = 50 v = 100 use_default_hsv = 1 }
//		scale			= 1.02
//		weight_scale	= 0.97
//	}

// Basically, it's a bunch of "virtual structures", each 
// assigned to a "part checksum" (head, torso, etc.).  
// Each part checksum corresponds to the name of a global
// array containing a list of read-only "actual structures".
// Given a part checksum and a desc_id in the CModelAppearance, 
// we can lookup the appropriate global array and find the 
// appropriate actual structure.

// CModelAppearance is really just a wrapper around the
// CStruct class, but after some consideration,
// I decided not to subclass from it.  This is so that
// I'd be able to replace the underlying implementation
// while keeping the same interface.

// There shouldn't be anything skater- (or even player-)
// specific hardcoded here.  The intent is that we use
// this same class for any kind of customizable model
// (such as Create-a-Peds, Create-a-Cars, or even
// Create-a-Spaceships for future games).
	
// The CModelAppearance should have no knowledge of the full
// list of possible part checksums;  this list should be
// completely in script.  This implies that the code should
// never have to iterate through a list of part checksums,
// (unlike THPS3, which had many explicit references to
// "editable_cas_options", Cas::GetBodyPartCount(), and
// Cas::GetBodyPartName()).

// One of the reset types used to be "randomized", but
// that is something that is more appropriate at the skater
// profile level (or, even better, in script), which has
// a better understanding of what parts should be disqualified 
// from working with a particular skater instance.  If 
// randomization were to be implemented at this level, it should
// be a purely naive randomization (no weighting, no part
// disqualification).
	
/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 get_desc_id_from_structure( Script::CStruct* pStructure )
{
	// This function is purely for convenience.  It grabs
	// the "desc_id" field from the supplied structure.
	
	uint32 descId = 0;
	pStructure->GetChecksum( CRCD(0x4bb2084e,"desc_id"), &descId, Script::NO_ASSERT );
	return descId;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelAppearance::resolve_randomized_desc_ids()
{
	// for each structure component, see whether
	// it's got any randomized desc ids.  if so,
	// select one to be used every time this
	// appearance instance is used

	// sometimes the skin tone must be consistent 
	// among all the body parts...  if any structure
	// contains a "random_set" checksum, then
	// make sure all future random parts get
	// this selected as well
	uint32 random_set = 0;

	Script::CComponent* p_comp = m_appearance.GetNextComponent( NULL );
	while ( p_comp )
	{
		Script::CComponent* p_next = m_appearance.GetNextComponent( p_comp );		
		
		if ( p_comp->mType == ESYMBOLTYPE_STRUCTURE )
		{
			uint32 partChecksum = p_comp->mNameChecksum;
			int randomIndex;

			if ( p_comp->mpStructure->ContainsFlag( CRCD(0xd81c03b0,"randomized_desc_id") ) )
			{
				Script::CStruct* pActualStruct = Cas::GetRandomOptionStructure( partChecksum, random_set );
				Dbg_MsgAssert( pActualStruct, ( "Unrecognized part checksum to randomize %s", Script::FindChecksumName(partChecksum) ) );
				
				// remember the random_set, if any
				pActualStruct->GetChecksum( CRCD(0x0d7260fd,"random_set"), &random_set, Script::NO_ASSERT );
				
				p_comp->mpStructure->Clear();
				p_comp->mpStructure->AddChecksum( CRCD(0x4bb2084e,"desc_id"), get_desc_id_from_structure(pActualStruct) );
			}
			else if ( p_comp->mpStructure->GetInteger( CRCD(0x4b833e64,"random_index"), &randomIndex, Script::NO_ASSERT ) )
			{
				Script::CStruct* pActualStruct = Cas::GetRandomOptionStructureByIndex( partChecksum, randomIndex, random_set );
				Dbg_MsgAssert( pActualStruct, ( "Unrecognized part checksum to randomize %s", Script::FindChecksumName(partChecksum) ) );
				
				// remember the random_set, if any
				pActualStruct->GetChecksum( CRCD(0x0d7260fd,"random_set"), &random_set, Script::NO_ASSERT );
				
				p_comp->mpStructure->Clear();
				p_comp->mpStructure->AddChecksum( CRCD(0x4bb2084e,"desc_id"), get_desc_id_from_structure(pActualStruct) );
			}
		}

		p_comp = p_next;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelAppearance::clear_part(uint32 partChecksum)
{
	m_appearance.RemoveComponent(partChecksum);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelAppearance::set_part(uint32 partChecksum, uint32 descID, Script::CStruct* pParams)
{
	Script::CStruct* pStruct;
	
	// if the structure doesn't already exist, then add it...
	if ( !m_appearance.GetStructure(partChecksum, &pStruct) )
	{
		pStruct = new Script::CStruct;
		m_appearance.AddComponent(partChecksum, ESYMBOLTYPE_STRUCTUREPOINTER, (int)pStruct);
	}

	pStruct->Clear();

	// check for extra color parameters
	int use_default_hsv = 1;
	pParams->GetInteger( CRCD(0x97dbdde6,"use_default_hsv"), &use_default_hsv, Script::NO_ASSERT );
	
	if ( !use_default_hsv )
	{
		int h, s, v;
		pParams->GetInteger( CRCD(0x6e94f918,"h"), &h, Script::ASSERT );
		pParams->GetInteger( CRCD(0xe4f130f4,"s"), &s, Script::ASSERT );
		pParams->GetInteger( CRCD(0x949bc47b,"v"), &v, Script::ASSERT );
		
		pStruct->AddInteger( CRCD(0x97dbdde6,"use_default_hsv"), 0 );
		pStruct->AddInteger( CRCD(0x6e94f918,"h"), h );
		pStruct->AddInteger( CRCD(0xe4f130f4,"s"), s );
		pStruct->AddInteger( CRCD(0x949bc47b,"v"), v );
	}

	pStruct->AddChecksum( CRCD(0x4bb2084e,"desc_id"), descID );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelAppearance::set_checksum(uint32 fieldChecksum, uint32 valueChecksum)
{
	m_appearance.AddChecksum( fieldChecksum, valueChecksum );
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CModelAppearance::CModelAppearance( void )
{
	mp_faceTexture = NULL;

	m_willEventuallyHaveFaceTexture = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModelAppearance::CModelAppearance( const CModelAppearance& rhs )
{
	mp_faceTexture = NULL;

	// use the overridden assignment operator
	*this = rhs;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CModelAppearance::~CModelAppearance()
{
	if ( mp_faceTexture )
	{
		delete mp_faceTexture;
		mp_faceTexture = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModelAppearance& CModelAppearance::operator=( const CModelAppearance& rhs )
{
	if ( &rhs == this )
	{
		return *this;
	}

	// it shouldn't be necessary to define this function, as
	// this is what the default assignment operator is supposed
	// to do.  However, the compiler gives me warnings if I
	// don't define it ("statement with no effect").
	m_appearance = rhs.m_appearance;

	// get rid of old face texture
	if ( mp_faceTexture )
	{
		delete mp_faceTexture;
		mp_faceTexture = NULL;

	}

	if ( rhs.mp_faceTexture )
	{
		CreateFaceTexture();
		Dbg_Assert( mp_faceTexture );
		*mp_faceTexture = *rhs.mp_faceTexture;
	}

	m_willEventuallyHaveFaceTexture = rhs.m_willEventuallyHaveFaceTexture;

	return *this;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelAppearance::Init()
{
	m_appearance.Clear();

	if ( mp_faceTexture )
	{
		// reset the face texture
		mp_faceTexture->SetValid( false );
	}

	// everyone should have certain items defined, 
	// such as sleeves for doing sleeve colors.
	Script::CStruct* pResetStructure = Script::GetStructure( CRCD(0xfe54486d,"appearance_init_structure"), Script::NO_ASSERT );
	if ( pResetStructure )
	{
		m_appearance.AppendStructure( pResetStructure );
	}

	m_willEventuallyHaveFaceTexture = false;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelAppearance::Load( Script::CStruct* pStructure, bool resolve_randoms )
{
	Dbg_Assert( pStructure );

	// In THPS3, we didn't clear the m_appearance first when
	// loading from a memory card, so that if the saved data is 
	// missing anything, it won't matter because the default 
	// will already be in m_appearance.  (This prevents asserts
	// on autoloading when a new component has been added to 
	// m_appearance which is not present on the memory card)
	// However, for THPS4, the appearances should be a lot more
	// flexible, and should no longer fail when there are missing items.
	Init();

#if 1
	// in case there are any global structure names,
	// resolve them
	pStructure->ExpandInto(	&m_appearance, 0 );
#else
	// add the new desired data
	m_appearance.AppendStructure( pStructure );
#endif
	
   // at this point, all the randomized_desc_ids
	// should be resolved
	if ( resolve_randoms )
	{
		resolve_randomized_desc_ids();
	}
		
#ifdef	__NOPT_ASSERT__
	// Make sure that the m_appearance does not contain any flag members,
	// which would potentially cause leaks as we append any structures.
	uint32 dummy = 0;
	Dbg_MsgAssert( !m_appearance.GetChecksum( NONAME, &dummy ), ( "m_appearance contains a flag '%s'", Script::FindChecksumName(dummy) ) );
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelAppearance::Load( uint32 structure_name, bool resolve_randoms )
{
	Script::CStruct* pStructure;

	pStructure = Script::GetStructure( structure_name, Script::ASSERT );

	return Load( pStructure, resolve_randoms );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifndef __PLAT_NGC__
void compress_model_appearance( Script::CStruct* pStruct )
{
	// the worst-case is too big to fit into a network packet
	// so this will remove some of the more unnecessary items
	// from the structure.
	pStruct->RemoveComponent( CRCD(0xb1d19000,"deck_layer1") );
	pStruct->RemoveComponent( CRCD(0x28d8c1ba,"deck_layer2") );
	pStruct->RemoveComponent( CRCD(0x5fdff12c,"deck_layer3") );
	pStruct->RemoveComponent( CRCD(0xc1bb648f,"deck_layer4") );
	pStruct->RemoveComponent( CRCD(0xb6bc5419,"deck_layer5") );

	pStruct->RemoveComponent( CRCD(0x3fa9b96e,"head_tattoo") );
	pStruct->RemoveComponent( CRCD(0x283dea37,"left_bicep_tattoo") );
	pStruct->RemoveComponent( CRCD(0xde55864b,"left_forearm_tattoo") );
	pStruct->RemoveComponent( CRCD(0xd408fa96,"right_bicep_tattoo") );
	pStruct->RemoveComponent( CRCD(0x74fee7b2,"right_forearm_tattoo") );
	pStruct->RemoveComponent( CRCD(0x8eba2bc9,"chest_tattoo") );
	pStruct->RemoveComponent( CRCD(0x233b7bba,"back_tattoo") );
	pStruct->RemoveComponent( CRCD(0x2b359a94,"left_leg_tattoo") );
	pStruct->RemoveComponent( CRCD(0x609432ca,"right_leg_tattoo") );
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CModelAppearance::WriteToBuffer(uint8 *pBuffer, uint32 BufferSize, bool ignoreFaceData )
{
	// for network message building
	// (note that the face texture is not included in this buffer
	// because the buffer is too small...  face textures will
	// be sent in a separate net packet)
	Script::CStruct* pTempStructure = new Script::CStruct;
	pTempStructure->AppendStructure( &m_appearance );
	uint32 size = Script::WriteToBuffer(pTempStructure, pBuffer, BufferSize);

#ifndef __PLAT_NGC__
	// GJ:  Need to compress the model appearances for Xbox and PS2
	// versions, or else the worst-case model appearance will crash
	// the server if he quits his own game (because it overflows the
	// max net packet size).  MAX_MODEL_APPEARANCE_SIZE is a guess 
	// based on the existing model appearance size, but it really
	// depends on how big the info/tricks get...  700 bytes seems safe
	const uint32 MAX_MODEL_APPEARANCE_SIZE = 700;
	if ( size > MAX_MODEL_APPEARANCE_SIZE )
	{
		// if the model appearance is too big, then	strip out
		// some of the cosmetic stuff...
		compress_model_appearance( pTempStructure );
		size = Script::WriteToBuffer(pTempStructure, pBuffer, BufferSize);
	}
#endif

	delete pTempStructure;

	// skip to the next chunk
	pBuffer += size;
	BufferSize -= size;

	// we do, however, still need to send a flag that says it
	// will eventually have a face texture...  so that the model 
	// builder knows to use the face-mapped head rather than
	// the non-facemapped head
	if( ignoreFaceData )
	{
		
		*pBuffer = 0;
	}
	else
	{
		*pBuffer = ( ( GetFaceTexture() && GetFaceTexture()->IsValid() ) || m_willEventuallyHaveFaceTexture ) ? 1 : 0;		
	}
	
	pBuffer += 1;
	BufferSize -= 1;
	size += 1;
	
	return size;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8* CModelAppearance::ReadFromBuffer(uint8 *pBuffer)
{
	Script::CStruct* pTempStructure = new Script::CStruct;
	pBuffer = Script::ReadFromBuffer( pTempStructure, pBuffer );
	m_appearance.Clear();
	m_appearance.AppendStructure( pTempStructure );
	delete pTempStructure;
	
	// we do, however, still need to send a flag that says it
	// will eventually have a face texture...  so that the model 
	// builder knows to use the face-mapped head rather than
	// the non-facemapped head
	m_willEventuallyHaveFaceTexture = *pBuffer;
	pBuffer++;
	
	return pBuffer;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelAppearance::PrintContents( const Script::CStruct* p_structure )
{
#ifdef __NOPT_ASSERT__
	//Script::PrintContents( &m_appearance );

	if(!p_structure)
	{
		p_structure = &m_appearance;
	}
	//const Script::CStruct* p_structure= &m_appearance;

	Dbg_MsgAssert(p_structure,("NULL p_structure"));
	  
	//printf(" ");
	
	
	printf("{");
			
    Script::CComponent *p_comp=p_structure->GetNextComponent(NULL);

    while (p_comp)
    {
		
		if (p_comp->mNameChecksum)
		{
			printf(" %s=",Script::FindChecksumName(p_comp->mNameChecksum));
		}	
            
        switch (p_comp->mType)
        {
        case ESYMBOLTYPE_INTEGER:
            printf("%d",p_comp->mIntegerValue);
            break;
        case ESYMBOLTYPE_FLOAT:
            printf("%f",p_comp->mFloatValue);
            break;
        case ESYMBOLTYPE_STRING:
            printf("#\"%s\"",p_comp->mpString);
            break;
        case ESYMBOLTYPE_LOCALSTRING:
            printf("'%s'",p_comp->mpLocalString);
            break;
		/*case ESYMBOLTYPE_PAIR:
            printf("(%f,%f) ",p_comp->mpPair->mX,p_comp->mpPair->mY);
            break;
        case ESYMBOLTYPE_VECTOR:
            printf("(%f,%f,%f) ",p_comp->mpVector->mX,p_comp->mpVector->mY,p_comp->mpVector->mZ);
            break;*/
        case ESYMBOLTYPE_STRUCTURE:
			//printf(" ");
			CModelAppearance::PrintContents(p_comp->mpStructure);
            break;
        case ESYMBOLTYPE_NAME:
            printf("#\"%s\"",Script::FindChecksumName(p_comp->mChecksum));
			
			#ifdef EXPAND_GLOBAL_STRUCTURE_REFERENCES
			if (p_comp->mNameChecksum==0)
			{
				// It's an un-named name. Maybe it's a global structure ... 
				// If so, print its contents too, which is handy for debugging.
			    CSymbolTableEntry *p_entry=Resolve(p_comp->mChecksum);
				if (p_entry && p_entry->mType==ESYMBOLTYPE_STRUCTURE)
				{
					printf("... Defined in %s ...\n",Script::FindChecksumName(p_entry->mSourceFileNameChecksum));
					Dbg_MsgAssert(p_entry->mpStructure,("NULL p_entry->mpStructure"));
					CModelAppearance::PrintContents(p_entry->mpStructure);
				}
			}		
			#endif
            break;
        case ESYMBOLTYPE_QSCRIPT:
			printf("(A script) "); // TODO
			break;
		case ESYMBOLTYPE_ARRAY:
			//printf(" ");
			Script::PrintContents(p_comp->mpArray,0);
            break;
        default:
			printf("Component of type '%s', value 0x%08x\n",Script::GetTypeName(p_comp->mType),p_comp->mUnion);
            //Dbg_MsgAssert(0,("Bad p_comp->Type"));
            break;
        }
        p_comp=p_structure->GetNextComponent(p_comp);
		
		#ifdef SLOW_DOWN_PRINTCONTENTS
		// A delay to let printf catch up so that it doesn't skip stuff when printing big arrays.		
		for (int i=0; i<1000000; ++i);
		#endif
    }
	
	printf("}\n");


	/*if ( mp_faceTexture && mp_faceTexture->IsValid() )
	{
		mp_faceTexture->PrintContents();
	}*/
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CModelAppearance::GetActualDescStructure( uint32 partChecksum )
{
	Script::CStruct* pStructure;
	if ( m_appearance.GetStructure( partChecksum, &pStructure, Script::NO_ASSERT ) )
	{
		return Cas::GetOptionStructure( partChecksum, get_desc_id_from_structure( pStructure ), false );
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CModelAppearance::GetVirtualDescStructure( uint32 partChecksum )
{
	Script::CStruct* pStructure = NULL;
	m_appearance.GetStructure( partChecksum, &pStructure, Script::NO_ASSERT );
	return pStructure;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelAppearance::CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( checksum )
	{
		case 0x0a23400c:		// ClearPart
		{
			Dbg_MsgAssert(pParams, ("No params supplied to model appearance"));

			uint32 partChecksum;
			pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &partChecksum, Script::ASSERT );
			clear_part(partChecksum);
		}
		return true;
		
		case 0x83339d0b:		// SetPart
		{
			Dbg_MsgAssert(pParams, ("No params supplied to model appearance"));

			uint32 partChecksum;
			pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &partChecksum, Script::ASSERT );
	
			uint32 descChecksum;
			pParams->GetChecksum( CRCD(0x4bb2084e,"desc_id"), &descChecksum, Script::ASSERT );
			
			set_part(partChecksum, descChecksum, pParams);
		}
		return true;

		case 0x10a225d6:		// GetPart
		{
			Dbg_MsgAssert(pParams, ("No params supplied to model appearance"));
		
			uint32 partChecksum;
			pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &partChecksum, Script::ASSERT );
	
			Script::CStruct* pVirtualDescStructure = GetVirtualDescStructure( partChecksum );

			if ( pVirtualDescStructure )
			{
				// return all the parameters, including desc_id, use_default_hsv, h, s, v
				pScript->GetParams()->AppendStructure( pVirtualDescStructure );
				return true;
			}

			return false;
		}
		return true;

		case 0xd27427ff:		// SetChecksum
		{
			Dbg_MsgAssert(pParams, ("No params supplied to model appearance"));

			uint32 fieldChecksum;
			pParams->GetChecksum( CRCD(0xa40abaa7,"field"), &fieldChecksum, Script::ASSERT );
	
			uint32 valueChecksum;
			pParams->GetChecksum( CRCD(0xe288a7cb,"value"), &valueChecksum, Script::ASSERT );
			
			set_checksum(fieldChecksum, valueChecksum);
		}
		return true;

		case 0xb13906b0:		// GotPart
		{
			uint32 partChecksum;
			pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &partChecksum, Script::ASSERT );
			return ( GetActualDescStructure( partChecksum ) );
		}

		case 0xe961bafa:		// PartGotFlag
		{
			uint32 partChecksum;
			pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &partChecksum, Script::ASSERT );
			uint32 flagChecksum;
			pParams->GetChecksum( CRCD(0x2e0b1465,"flag"), &flagChecksum, Script::ASSERT );

			Script::CStruct* pActualDescStructure = GetActualDescStructure( partChecksum );

			if ( pActualDescStructure )
			{
				return pActualDescStructure->ContainsFlag( flagChecksum );
			}

			Dbg_MsgAssert( 0, ( "part %s was not defined (need it for disqualification script)", Script::FindChecksumName(partChecksum) ) );

			return false;
		}
		return true;
	}

	return Obj::CObject::CallMemberFunction(checksum, pParams, pScript);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CFaceTexture* CModelAppearance::GetFaceTexture()
{
	return mp_faceTexture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
											  
void CModelAppearance::CreateFaceTexture()
{
	Dbg_MsgAssert( !mp_faceTexture, ( "Model appearance already has a face texture" ) );
	
	// the face texture should always go on the skater info heap
	// these may be permanent, or they might be temporary...
	mp_faceTexture = new CFaceTexture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
											  
void CModelAppearance::DestroyFaceTexture()
{
	if ( mp_faceTexture )
	{
		delete mp_faceTexture;
		mp_faceTexture = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelAppearance::WillEventuallyHaveFaceTexture()
{
	return m_willEventuallyHaveFaceTexture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx


================================================
FILE: Code/Gfx/ModelAppearance.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       ModelAppearance.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  4/2/2000
//****************************************************************************

#ifndef __GFX_MODELAPPEARANCE_H
#define __GFX_MODELAPPEARANCE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Gfx
{

/*****************************************************************************
**							Forward Declarations						**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

	class CFaceTexture;

// A CModelAppearance contains all the data associated with
// generating a rendered model (NxModel).
			
class CModelAppearance : public Obj::CObject
{
public:
	CModelAppearance();
	CModelAppearance( const CModelAppearance& rhs );
	CModelAppearance&	operator=( const CModelAppearance& rhs );
	virtual 			~CModelAppearance();

public:
	virtual bool		CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript );

public:
	// Init: Clears it out to nothing
	bool				Init();

	// Load: Appends a structure
	bool				Load( Script::CStruct* pStructure, bool resolve_randoms = true );
	bool				Load( uint32 structure_name, bool resolve_randoms = true );

public:
	// for network message building
	// (note that the face texture is not included in this buffer
	// because the buffer is too small...  face textures need to
	// be sent through a separate pathway)
	uint32				WriteToBuffer(uint8* pBuffer, uint32 bufferSize, bool ignoreFaceData = false);
	uint8*				ReadFromBuffer(uint8* pBuffer);

public:
	// for debugging
	void				PrintContents(const Script::CStruct* p_structure = 0);

public:
	// used by builder, to apply the desired appearance to a particular SkinModel
	Script::CStruct*	GetActualDescStructure( uint32 partChecksum );
	Script::CStruct*	GetVirtualDescStructure( uint32 partChecksum );
	Script::CStruct*	GetStructure() { return &m_appearance; }
	Gfx::CFaceTexture*	GetFaceTexture();
	void				CreateFaceTexture();
	void				DestroyFaceTexture();
		
protected:
	void				resolve_randomized_desc_ids();
	void				set_part(uint32 partChecksum, uint32 descID, Script::CStruct* pParams);
	void				clear_part(uint32 partChecksum);
	void				set_checksum(uint32 fieldChecksum, uint32 valueChecksum);

protected:
	Script::CStruct		m_appearance;
	Gfx::CFaceTexture*	mp_faceTexture;
	
public:
	// the following is needed because skaters created in net games 
	// don't have a face texture during initial model building (the
	// face texture will eventually come in a later net packet),
	// but the model builder still needs to know if there will eventually
	// be a face texture so that it can load up the correct head geometry
	bool				WillEventuallyHaveFaceTexture();

protected:
	bool				m_willEventuallyHaveFaceTexture;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Gfx

#endif	// __OBJECTS_MODELAPPEARANCE_H


================================================
FILE: Code/Gfx/ModelBuilder.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       ModelBuilder.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/16/2001
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Gfx
{

// NOTES:

// Given a CModelAppearance, the CModelBuilder generates the 
// appropriate Nx::CModel (creating its Nx::CGeoms, modulating
// its color, replacing its textures, etc.)

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// some handy checksums
const uint32 vCHECKSUM_PART = 0xb6f08f39;							// part
const uint32 vCHECKSUM_USE_DEFAULT_HSV = 0x97dbdde6;				// use_default_hsv
const uint32 vCHECKSUM_HUE = 0x6e94f918;							// h
const uint32 vCHECKSUM_SATURATION = 0xe4f130f4;						// s
const uint32 vCHECKSUM_VALUE = 0x949bc47b;							// v
const uint32 vCHECKSUM_USE_DEFAULT_SCALE = 0x5a96985d;				// use_default_scale
const uint32 vCHECKSUM_X = 0x7323e97c;								// x
const uint32 vCHECKSUM_Y = 0x0424d9ea;								// y
const uint32 vCHECKSUM_Z = 0x9d2d8850;								// z
const uint32 vCHECKSUM_REPLACE = 0xf362dbba;						// replace
const uint32 vCHECKSUM_WITH = 0x676f1df1;							// with
const uint32 vCHECKSUM_IN = 0xa01371b1;								// in
const uint32 vCHECKSUM_REPLACE1 = 0x7a993873;						// replace1
const uint32 vCHECKSUM_WITH1 = 0x9b03adad;							// with1
const uint32 vCHECKSUM_IN1 = 0xed189051;							// in1
const uint32 vCHECKSUM_REPLACE2 = 0xe39069c9;						// replace2
const uint32 vCHECKSUM_WITH2 = 0x020afc17;							// with2
const uint32 vCHECKSUM_IN2 = 0x7411c1eb;							// in2
const uint32 vCHECKSUM_REPLACE3 = 0x9497595f;						// replace3
const uint32 vCHECKSUM_WITH3 = 0x750dcc81;							// with3
const uint32 vCHECKSUM_IN3 = 0x0316f17d;							// in3
const uint32 vCHECKSUM_REPLACE4 = 0x0af3ccfc;						// replace4
const uint32 vCHECKSUM_WITH4 = 0xeb695922;							// with4
const uint32 vCHECKSUM_IN4 = 0x9d7264de;							// in4
const uint32 vCHECKSUM_REPLACE5 = 0x7df4fc6a;						// replace5
const uint32 vCHECKSUM_WITH5 = 0x9c6e69b4;							// with5
const uint32 vCHECKSUM_IN5 = 0xea755448;							// in5
const uint32 vCHECKSUM_REPLACE6 = 0xe4fdadd0;						// replace6
const uint32 vCHECKSUM_WITH6 = 0x0567380e;							// with6
const uint32 vCHECKSUM_IN6 = 0x737c05f2;							// in6
const uint32 vCHECKSUM_MESH = 0x1e90c5a9;							// mesh
const uint32 vCHECKSUM_MESH1 = 0xfeca8bb3;							// mesh1
const uint32 vCHECKSUM_MESH2 = 0x67c3da09;							// mesh2
const uint32 vCHECKSUM_MESH3 = 0x10c4ea9f;							// mesh3
const uint32 vCHECKSUM_MESH4 = 0x8ea07f3c;							// mesh4
const uint32 vCHECKSUM_REMOVE = 0x977fe2cf;							// remove
const uint32 vCHECKSUM_TARGET = 0xb990d003;							// target
const uint32 vCHECKSUM_BODY_SHAPE = 0x812684ef;						// body_shape
const uint32 vCHECKSUM_APPLY_NORMAL_MODE = 0x2700a288;				// apply_normal_mode
const uint32 vCHECKSUM_BODY = 0x2457f44d;							// body
const uint32 vCHECKSUM_NO_SCALING_ALLOWED = 0xb25e39fd;				// no_scaling_allowed

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

Nx::CModel* CModelBuilder::mp_model = NULL;
Gfx::CSkeleton* CModelBuilder::mp_skeleton = NULL;
CModelAppearance* CModelBuilder::mp_appearance = NULL;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModelBuilder::CModelBuilder( bool useAssetManager, uint32 texDictOffset )
{
	// parameters that will be used for the duration of
	// the model-building:

	// whether the asset manager should be used
	// (it should NOT, for skaters and other
	// things that do texture replacement/poly
	// removal)
	m_useAssetManager = useAssetManager;

	// used for unique-ifying the texture dictionaries
	// when you've got multiple skaters
	m_texDictOffset = texDictOffset;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModelBuilder::BuildModel( CModelAppearance* pAppearance, Nx::CModel* pModel, Gfx::CSkeleton* pSkeleton, uint32 buildScriptName )
{
//	Tmr::Time baseTime = Tmr::ElapsedTime(0);

	Dbg_Assert( pAppearance );
	Dbg_Assert( pModel );

	// don't destroy the existing model...
	// leave it up to the particular buildscript
//	pModel->ClearGeoms();

	Dbg_MsgAssert( !mp_model, ( "Static temporary model already exists" ) );
	mp_model = pModel;
	
	Dbg_MsgAssert( !mp_skeleton, ( "Static skeleton already exists" ) );
	mp_skeleton = pSkeleton;
	
	Dbg_MsgAssert( !mp_appearance, ( "Static temporary appearance already exists" ) );
	mp_appearance = new CModelAppearance;
	
	// clear it out from the previous use
	mp_appearance->Init();

	// GJ 9/8/03:  since it's a temporary variable, we don't want 
	// it on the skater geom heap (which would fragment it on the
	// PS2 with 17K worth of face texture data)
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

  	// copy over the desired appearance
	// (must make a copy, so that we can
	// make changes to it)
	*mp_appearance = *pAppearance;
	
   	Mem::Manager::sHandle().PopContext();

	// this script is responsible for looping
	// through all the supported partChecksums
	// and running all the supported operations
	// on them.  Which partChecksums and operations
	// are "supported" should be completely in
	// script.
//    Dbg_Message( "BuildModel - %s", Script::FindChecksumName(buildScriptName) );
    Script::RunScript( buildScriptName, NULL, this );
    
	mp_model = NULL;
	mp_skeleton = NULL;

	delete mp_appearance;
	mp_appearance = NULL;

//	Dbg_Message( "BuildModel took %d ms", Tmr::ElapsedTime( baseTime ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( checksum )
	{
		case 0x30e2951d:		// DebugPrintAppearance
		{
			// print out the appearance to make sure
			// it's coming through correctly in net games
			mp_appearance->PrintContents();
		}
		return true;

		case 0x53da94f1:		// ModelAddGeom
		{
			uint32 partChecksum;
			pParams->GetChecksum( vCHECKSUM_PART, &partChecksum, Script::ASSERT );
			model_add_geom(partChecksum);
		}
		return true;

		case 0x681a03af:		// ModelHideGeom
		{
			uint32 partChecksum;
			pParams->GetChecksum( vCHECKSUM_PART, &partChecksum, Script::ASSERT );
		
			int hidden;
			pParams->GetInteger( NONAME, &hidden, Script::ASSERT );

			model_hide_geom(partChecksum, hidden);
		}
		return true;

		case 0xaee6e915:		// GeomModulateColor
		{
			geom_modulate_color( pParams );
		}
		return true;

		case 0x18924da1:		// GeomSetUVOffset
		{
			uint32 partChecksum;
			pParams->GetChecksum( vCHECKSUM_PART, &partChecksum, Script::ASSERT );
			
			uint32 material;
			pParams->GetChecksum( CRCD(0x83418a6a,"material"), &material, Script::ASSERT );

			int pass;
			pParams->GetInteger( CRCD(0x318f2bdb,"pass"), &pass, Script::ASSERT );

			geom_set_uv_offset(partChecksum, material, pass);
		}
		return true;

		case 0x3bf41299:		// GeomAllocateUVMatrixParams
		{
			uint32 material;
			pParams->GetChecksum( CRCD(0x83418a6a,"material"), &material, Script::ASSERT );

			int pass;
			pParams->GetInteger( CRCD(0x318f2bdb,"pass"), &pass, Script::ASSERT );

			geom_allocate_uv_matrix_params(material, pass);
		}
		return true;

		case 0xaa42a104:		// GeomReplaceTexture
		{
			uint32 partChecksum;
			pParams->GetChecksum( vCHECKSUM_PART, &partChecksum, Script::ASSERT );
			geom_replace_texture(partChecksum);
		}
		return true;
		
		case 0x9041e901:		// ModelRemovePolys
		{
			model_remove_polys();
		}
		return true;

		case 0x50f1285b:		// ModelApplyObjectScale
		{
			Script::CStruct* pBodyShapeStructure = NULL;
			if ( pParams->GetStructure( vCHECKSUM_BODY_SHAPE, &pBodyShapeStructure, false ) )
			{
				Mth::Vector vec = mp_model->GetScale();
				
				Mth::Vector theScale( 1.0f, 1.0f, 1.0f, 1.0f );
				if ( Gfx::GetScaleFromParams( &theScale, pBodyShapeStructure ) )
				{
					vec[X] *= theScale[X];
					vec[Y] *= theScale[Y];
					vec[Z] *= theScale[Z];
					vec[W] *= theScale[W];

					// if the body shape has a scale
					mp_model->SetScale( vec );
				}
			}
			else
			{
				model_apply_object_scale();
			}
		}
		return true;
		
		case 0x21aec583:		// ModelApplyBoneScale
		{
			if ( pParams->ContainsComponentNamed( CRCD(0x3f5f5bc2,"bone_scaling") ) )
			{
				uint32 partChecksum;
				pParams->GetChecksum( vCHECKSUM_PART, &partChecksum, Script::ASSERT );
				model_apply_bone_scale( partChecksum );
			}
		}
		return true;
		
		case 0xc0bc8271:		// ModelApplyBodyShape
		{
			Script::CStruct* pBodyShapeStructure = NULL;
			if ( pParams->GetStructure( vCHECKSUM_BODY_SHAPE, &pBodyShapeStructure, false ) )
			{
				// GJ:  PATCH TO FIX STEVE CABALLERO'S INCORRECT WEIGHTING IN BIGHEAD MODE
				Script::CStruct* pActualStruct = mp_appearance->GetActualDescStructure( CRCD(0x650fab6d,"skater_m_head") );
				if ( pActualStruct && pParams->ContainsFlag( CRCD(0x3c4d849d,"is_bighead_cheat") ) ) 
				{
				   pActualStruct->GetStructure( CRCD(0x1ade0dd8,"bighead_scale_info"), &pBodyShapeStructure );
				}

				// if a body shape was explicitly specified
				// (for kid and gorilla modes)
				if ( mp_skeleton )
				{
					return mp_skeleton->ApplyBoneScale( pBodyShapeStructure );
				}
	        }
			else
			{
				model_apply_body_shape();
			}
		}
		return true;

		case 0x73ab02bc:		// ModelApplyFaceTexture
		{
#ifdef __PLAT_NGPS__
			bool success = true;

			Gfx::CFaceTexture* pFaceTexture = mp_appearance->GetFaceTexture() ;
												   
			if ( pFaceTexture && pFaceTexture->IsValid() )
			{
				if ( mp_model )
				{
					const char* pSrcTexture;
					pParams->GetText( CRCD(0x9fbbdb72,"src"), &pSrcTexture, Script::ASSERT );

					// by default, it searches globally in the model for the correct texture
					uint32 partChecksumToReplace = Nx::CModel::vREPLACE_GLOBALLY;
					pParams->GetChecksum( CRCD(0xa01371b1,"in"), &partChecksumToReplace, Script::NO_ASSERT );
                    
					success = mp_model->ApplyFaceTexture( pFaceTexture, pSrcTexture, partChecksumToReplace );
				}
			}
			
			return success;
#else
			// X-box and NGC don't have to implement these
			// because they don't do face textures...
			return true;
#endif
		}

		case 0x6516b484:		// AppearanceAllowScalingCheat
		{
			Script::CStruct* pActualStruct = mp_appearance->GetActualDescStructure( vCHECKSUM_BODY );
			if ( pActualStruct ) 
			{
				return !pActualStruct->ContainsFlag( vCHECKSUM_NO_SCALING_ALLOWED );
			}

			return true;
		}
		break;

		case 0xcdeea636:		// ModelResetScale
		{
			model_reset_scale();
		}
		return true;

		case 0xbff957a9:		// ModelClearGeom
		{
			uint32 partChecksum;
			pParams->GetChecksum( vCHECKSUM_PART, &partChecksum, Script::ASSERT );
			model_clear_geom( partChecksum );
		}
		return true;

		case 0x0ecf0248:		// ModelClearAllGeoms
		{
			model_clear_all_geoms();
		}
		return true;

		case 0xb1c39a54:		// ModelRunScript
		{
			uint32 partChecksum;
			pParams->GetChecksum( vCHECKSUM_PART, &partChecksum, Script::ASSERT );
			
			uint32 targetChecksum;
			pParams->GetChecksum( vCHECKSUM_TARGET, &targetChecksum, Script::ASSERT );

			// runs an embedded script
			model_run_script(partChecksum, targetChecksum);
		}
		return true;

		case 0xcefe8478:		// ModelFinalize
		{
			model_finalize();
		}
		return true;
	}

	return Obj::CObject::CallMemberFunction( checksum, pParams, pScript );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_hide_geom( uint32 partChecksum, bool hidden )
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_appearance );

/*
	if ( !Script::GetArray( partChecksum, Script::NO_ASSERT ) )
	{
		// no array involved
		return false;
	}
*/

	mp_model->HideGeom( partChecksum, hidden );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_add_geom( uint32 partChecksum )
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_appearance );

	Script::CStruct* pActualStruct = mp_appearance->GetActualDescStructure( partChecksum );
	if ( pActualStruct ) 
	{
		int supports_multiple_colors = 0;
		pActualStruct->GetInteger( CRCD(0x92b43e79,"multicolor"), &supports_multiple_colors, Script::NO_ASSERT );

#ifdef __PLAT_NGPS__
		Gfx::CFaceTexture* pFaceTexture = mp_appearance->GetFaceTexture() ;

		// GJ:  THPS5 KLUDGE
		// WillEventuallyHaveFaceTexture is a flag used in net games...
		// this is needed because there is no face texture pointer
		// in net games (because it's handled in a separate net packet)...

		if ( ( pFaceTexture && pFaceTexture->IsValid() ) || ( mp_appearance->WillEventuallyHaveFaceTexture() ) )
		{
			// if the special parameter doesn't exist, then
			// fall through to the normal mesh-loading code...
			
			const char* pMeshName;
			if ( pActualStruct->GetString( CRCD(0xc9aed80f,"mesh_if_facemapped"), &pMeshName, Script::NO_ASSERT ) )
			{
				mp_model->AddGeom( pMeshName, partChecksum, m_useAssetManager, m_texDictOffset, false, supports_multiple_colors );
				return true;
			}
		}
#endif
		{
			const char* pMeshName;
			if ( pActualStruct->GetString( vCHECKSUM_MESH, &pMeshName, Script::NO_ASSERT ) )
			{
				mp_model->AddGeom( pMeshName, partChecksum, m_useAssetManager, m_texDictOffset, false, supports_multiple_colors );
			}
			if ( pActualStruct->GetString( vCHECKSUM_MESH1, &pMeshName, Script::NO_ASSERT ) )
			{
				mp_model->AddGeom( pMeshName, partChecksum, m_useAssetManager, m_texDictOffset, false, supports_multiple_colors );
			}
			if ( pActualStruct->GetString( vCHECKSUM_MESH2, &pMeshName, Script::NO_ASSERT ) )
			{
				mp_model->AddGeom( pMeshName, partChecksum, m_useAssetManager, m_texDictOffset, false, supports_multiple_colors );
			}
			if ( pActualStruct->GetString( vCHECKSUM_MESH3, &pMeshName, Script::NO_ASSERT ) )
			{
				mp_model->AddGeom( pMeshName, partChecksum, m_useAssetManager, m_texDictOffset, false, supports_multiple_colors );
			}
			if ( pActualStruct->GetString( vCHECKSUM_MESH4, &pMeshName, Script::NO_ASSERT ) )
			{
				mp_model->AddGeom( pMeshName, partChecksum, m_useAssetManager, m_texDictOffset, false, supports_multiple_colors );
			}
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_clear_geom( uint32 partChecksum )
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_appearance );

	// it's destructive, so make sure you're not 
	// doing it to the "real" appearance...
	mp_appearance->GetStructure()->RemoveComponent( partChecksum );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_clear_all_geoms( void )
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_appearance );

	mp_model->ClearGeoms();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_reset_scale()
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_skeleton );
	Dbg_Assert( mp_appearance );

	Mth::Vector theBoneScaleVector( 1.0f, 1.0f, 1.0f, 1.0f );
	mp_model->SetScale( theBoneScaleVector );
	
	if ( mp_skeleton )
	{
		mp_skeleton->ResetScale();
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_apply_body_shape()
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_skeleton );
	Dbg_Assert( mp_appearance );

	Script::CStruct* pStructure = mp_appearance->GetStructure();
	Dbg_Assert( pStructure );
	
	Script::CStruct* pBodyShapeStructure = NULL;
	if ( pStructure->GetStructure( vCHECKSUM_BODY_SHAPE, &pBodyShapeStructure, false ) )
	{
		return mp_skeleton->ApplyBoneScale( pBodyShapeStructure );
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_apply_bone_scale( uint32 partChecksum )
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_skeleton );
	Dbg_Assert( mp_appearance );
	
	Script::CStruct* pVirtualStruct = mp_appearance->GetVirtualDescStructure( partChecksum );
	if ( pVirtualStruct ) 
	{
		if ( !pVirtualStruct->ContainsComponentNamed( vCHECKSUM_USE_DEFAULT_SCALE ) )
		{
			return false;
		}

		int use_default_scale;
		pVirtualStruct->GetInteger( vCHECKSUM_USE_DEFAULT_SCALE, &use_default_scale );
	
		if ( use_default_scale )
		{
			return false;
		}

		int x, y, z;
		if ( pVirtualStruct->GetInteger( vCHECKSUM_X, &x, false )
			 && pVirtualStruct->GetInteger( vCHECKSUM_Y, &y, false )
			 && pVirtualStruct->GetInteger( vCHECKSUM_Z, &z, false ) )
		{
			Script::CArray* pBoneGroupArray = Script::GetArray( partChecksum, Script::NO_ASSERT );
			if ( pBoneGroupArray )
			{
				Mth::Vector theBoneScaleVector;
//				theBoneScaleVector[X] = (float)x / 100.0f;
//				theBoneScaleVector[Y] = (float)y / 100.0f;
//				theBoneScaleVector[Z] = (float)z / 100.0f;
				theBoneScaleVector[W] = 1.0f;

				for ( uint32 i = 0; i < pBoneGroupArray->GetSize(); i++ )
				{
					uint32 boneChecksum = pBoneGroupArray->GetChecksum(i);

					// get the existing scale...
					mp_skeleton->GetBoneScale( boneChecksum, &theBoneScaleVector );

					// then combine it with the new desired bone scale...
					theBoneScaleVector[X] *= ( (float)x / 100.0f );
					theBoneScaleVector[Y] *= ( (float)y / 100.0f );
					theBoneScaleVector[Z] *= ( (float)z / 100.0f );

					bool isLocalScale = true;

					// GJ:  The shoulder bones are handled slightly differently
					// so that the female skaters aren't so broad-shouldered.
					Script::CArray* pShoulderScalingArray = Script::GetArray( CRCD(0x20d9ac2f,"nonlocal_bones"), Script::NO_ASSERT );
					if ( pShoulderScalingArray )
					{
						for ( uint32 i = 0; i < pShoulderScalingArray->GetSize(); i++ )
						{
							if ( pShoulderScalingArray->GetChecksum(i) == boneChecksum )
							{
								isLocalScale = false;
							}
						}
					}

					mp_skeleton->SetBoneScale( boneChecksum, theBoneScaleVector, isLocalScale );
				}
			}
		}
	}
		
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_apply_object_scale()
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_appearance );

	Mth::Vector theScale( 1.0f, 1.0f, 1.0f );
	if ( Gfx::GetScaleFromParams( &theScale, mp_appearance->GetStructure() ) )
	{
		mp_model->SetScale( theScale );
		return true;
	}

	Script::CStruct* pStructure = mp_appearance->GetStructure();
	Dbg_Assert( pStructure );
	
	// sometimes the object scale can be found in the bone params
	Script::CStruct* pBodyShapeStructure = NULL;
	if ( pStructure->GetStructure( vCHECKSUM_BODY_SHAPE, &pBodyShapeStructure, false ) )
	{
		if ( Gfx::GetScaleFromParams( &theScale, pBodyShapeStructure ) )
		{
			mp_model->SetScale( theScale );
			return true;
		}
	}

	Script::CStruct* pVirtualStruct = mp_appearance->GetVirtualDescStructure( CRCD(0x8b314358,"object_scaling") );
	if ( pVirtualStruct ) 
	{
		if ( !pVirtualStruct->ContainsComponentNamed( vCHECKSUM_USE_DEFAULT_SCALE ) )
		{
			return false;
		}  	

		int use_default_scale;
		pVirtualStruct->GetInteger( vCHECKSUM_USE_DEFAULT_SCALE, &use_default_scale );
		
		if ( use_default_scale )
		{
			return false;
		}

		int x, y, z;
		if ( pVirtualStruct->GetInteger( vCHECKSUM_X, &x, false )
			 && pVirtualStruct->GetInteger( vCHECKSUM_Y, &y, false )
			 && pVirtualStruct->GetInteger( vCHECKSUM_Z, &z, false ) )
		{
			Mth::Vector theBoneScaleVector;
			theBoneScaleVector[X] = (float)x / 100.0f;
			theBoneScaleVector[Y] = (float)y / 100.0f;
			theBoneScaleVector[Z] = (float)z / 100.0f;
			theBoneScaleVector[W] = 1.0f;

			mp_model->SetScale( theBoneScaleVector );
		}

		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_remove_polys()
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_appearance );

	mp_model->RemovePolys();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::geom_modulate_color( Script::CStruct* pParams )
{
	uint32 partChecksum;
	pParams->GetChecksum( vCHECKSUM_PART, &partChecksum, Script::ASSERT );
			
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_appearance );

	Script::CStruct* pVirtualStruct = mp_appearance->GetVirtualDescStructure( partChecksum );
	if ( pVirtualStruct ) 
	{
		int use_default_hsv = 0;

		if ( !pVirtualStruct->ContainsComponentNamed( vCHECKSUM_USE_DEFAULT_HSV ) )
		{
			use_default_hsv = 1;
		}
		else
		{
			pVirtualStruct->GetInteger( vCHECKSUM_USE_DEFAULT_HSV, &use_default_hsv );
		}
	
		if ( use_default_hsv )
		{
			Script::CStruct* pActualStruct = mp_appearance->GetActualDescStructure( partChecksum );
			int h, s, v;
			if ( pActualStruct
				 && pActualStruct->GetInteger( CRCD(0xc6cc7f7f,"default_h"), &h, Script::NO_ASSERT )
				 && pActualStruct->GetInteger( CRCD(0x4ca9b693,"default_s"), &s, Script::NO_ASSERT )
				 && pActualStruct->GetInteger( CRCD(0x3cc3421c,"default_v"), &v, Script::NO_ASSERT ) )
			{
				// set the default color that's specified in the actual structure
				mp_model->SetColor( pParams, h, (float)s / 100.0f, (float)v / 100.0f );
			}
			else
			{
				// otherwise, clear out the color   
				mp_model->ClearColor( pParams );
			}
			
			return false;
		}
		else
		{
			int h, s, v;
			if ( pVirtualStruct->GetInteger( vCHECKSUM_HUE, &h, false )
				 && pVirtualStruct->GetInteger( vCHECKSUM_SATURATION, &s, false )
				 && pVirtualStruct->GetInteger( vCHECKSUM_VALUE, &v, false ) )
			{
				// set the non-default color that's specified in the virtual structure
				mp_model->SetColor( pParams, h, (float)s / 100.0f, (float)v / 100.0f );
			}
		}
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::geom_allocate_uv_matrix_params( uint32 matChecksum, int pass )
{
	Dbg_Assert(mp_model);
	mp_model->AllocateUVMatrixParams(matChecksum, pass);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::geom_set_uv_offset( uint32 partChecksum, uint32 matChecksum, int pass )
{
#if 0
// debugging
	if ( mp_appearance->GetActualDescStructure( Script::GenerateCRC("skater_m_head") ) )
	{
		mp_model->SetMaterialColor(matChecksum, pass, Image::RGBA(0,0,128,128));
	}
#endif	
	Script::CStruct* pVirtualStruct = mp_appearance->GetVirtualDescStructure( partChecksum );
	if ( pVirtualStruct ) 
	{
		if ( !pVirtualStruct->ContainsComponentNamed( CRCD(0x8602f6ee,"use_default_uv") ) )
		{
			return false;
		}
		
		int use_default_uv;
		pVirtualStruct->GetInteger( CRCD(0x8602f6ee,"use_default_uv"), &use_default_uv );
	
		if ( use_default_uv )
		{
// 			(do nothing;  keep the default UVs)
//			(assumes that we're starting from an unmodified model)
			return false;
		}

		float u_offset;
        pVirtualStruct->GetFloat(CRCD(0xcf6aa087,"uv_u"), &u_offset, Script::ASSERT);
		
		float v_offset;
		pVirtualStruct->GetFloat(CRCD(0x5663f13d,"uv_v"), &v_offset, Script::ASSERT);
		
		float uv_scale;
		pVirtualStruct->GetFloat(CRCD(0x266932c8,"uv_scale"), &uv_scale, Script::ASSERT);
		
		float uv_rot;
		pVirtualStruct->GetFloat(CRCD(0x1256b6c6,"uv_rot"), &uv_rot, Script::ASSERT);
		
		// GJ:  seems if you want a bigger image, you need smaller UV values...
		// is that right?

		Mth::Matrix theMat;
		theMat.Ident();
		theMat.Translate( Mth::Vector( -0.5f, -0.5f, 0.0f, 0.0f ) );
		theMat.Scale( Mth::Vector(uv_scale / 100.0f, uv_scale / 100.0f, uv_scale / 100.0f, 1.0f ) );
		theMat.Rotate( Mth::Vector( 0.0f, 0.0f, 1.0f, 1.0f ), Mth::DegToRad(uv_rot) );  // in degrees
		theMat.Translate( Mth::Vector( 0.5f, 0.5f, 0.0f, 0.0f ) );
		
		theMat.TranslateLocal( Mth::Vector( u_offset / 100.0f, v_offset / 100.0f, 0.0f, 0.0f ) );
		
		Dbg_Assert(mp_model);
		mp_model->SetUVMatrix(matChecksum, pass, theMat);
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::geom_replace_texture( uint32 partChecksum )
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_appearance );

	bool success = false;

/*
	if ( !Script::GetArray( partChecksum, Script::NO_ASSERT ) )
	{
		// no array involved
		return false;
	}
*/

	Script::CStruct* pActualStruct = mp_appearance->GetActualDescStructure( partChecksum );
	if ( pActualStruct ) 
	{
		const char* pSrcTexture;
		const char* pDestTexture;
		
		if ( pActualStruct->GetText( vCHECKSUM_REPLACE, &pSrcTexture, Script::NO_ASSERT ) )
		{
			pActualStruct->GetText( vCHECKSUM_WITH, &pDestTexture, Script::ASSERT );

			uint32 partChecksumToReplace = partChecksum;
			pActualStruct->GetChecksum( vCHECKSUM_IN, &partChecksumToReplace, Script::NO_ASSERT );
			success = mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDestTexture );
		}
		if ( pActualStruct->GetText( vCHECKSUM_REPLACE1, &pSrcTexture, Script::NO_ASSERT ) )
		{
			pActualStruct->GetText( vCHECKSUM_WITH1, &pDestTexture, Script::ASSERT );

			uint32 partChecksumToReplace = partChecksum;
			pActualStruct->GetChecksum( vCHECKSUM_IN1, &partChecksumToReplace, Script::NO_ASSERT );
			success = mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDestTexture );
		}
		if ( pActualStruct->GetText( vCHECKSUM_REPLACE2, &pSrcTexture, Script::NO_ASSERT ) )
		{
			pActualStruct->GetText( vCHECKSUM_WITH2, &pDestTexture, Script::ASSERT );

			uint32 partChecksumToReplace = partChecksum;
			pActualStruct->GetChecksum( vCHECKSUM_IN2, &partChecksumToReplace, Script::NO_ASSERT );
			success = mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDestTexture );
		}
		if ( pActualStruct->GetText( vCHECKSUM_REPLACE3, &pSrcTexture, Script::NO_ASSERT ) )
		{
			pActualStruct->GetText( vCHECKSUM_WITH3, &pDestTexture, Script::ASSERT );

			uint32 partChecksumToReplace = partChecksum;
			pActualStruct->GetChecksum( vCHECKSUM_IN3, &partChecksumToReplace, Script::NO_ASSERT );
			success = mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDestTexture );
		}
		if ( pActualStruct->GetText( vCHECKSUM_REPLACE4, &pSrcTexture, Script::NO_ASSERT ) )
		{
			pActualStruct->GetText( vCHECKSUM_WITH4, &pDestTexture, Script::ASSERT );

			uint32 partChecksumToReplace = partChecksum;
			pActualStruct->GetChecksum( vCHECKSUM_IN4, &partChecksumToReplace, Script::NO_ASSERT );
			success = mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDestTexture );
		}
		if ( pActualStruct->GetText( vCHECKSUM_REPLACE5, &pSrcTexture, Script::NO_ASSERT ) )
		{
			pActualStruct->GetText( vCHECKSUM_WITH5, &pDestTexture, Script::ASSERT );

			uint32 partChecksumToReplace = partChecksum;
			pActualStruct->GetChecksum( vCHECKSUM_IN5, &partChecksumToReplace, Script::NO_ASSERT );
			success = mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDestTexture );
		}
		if ( pActualStruct->GetText( vCHECKSUM_REPLACE6, &pSrcTexture, Script::NO_ASSERT ) )
		{
			pActualStruct->GetText( vCHECKSUM_WITH6, &pDestTexture, Script::ASSERT );

			uint32 partChecksumToReplace = partChecksum;
			pActualStruct->GetChecksum( vCHECKSUM_IN6, &partChecksumToReplace, Script::NO_ASSERT );
			success = mp_model->ReplaceTexture( partChecksumToReplace, pSrcTexture, pDestTexture );
		}
	}
	
	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_finalize()
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_appearance );

	mp_model->Finalize();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelBuilder::model_run_script( uint32 partChecksum, uint32 targetChecksum )
{
	Dbg_Assert( mp_model );
	Dbg_Assert( mp_appearance );

/*
	if ( !Script::GetArray( partChecksum, Script::NO_ASSERT ) )
	{
		// no array involved
		return false;
	}
*/

	Script::CStruct* pActualStruct = mp_appearance->GetActualDescStructure( partChecksum );
	if ( pActualStruct ) 
	{
		Script::SStructScript* pStructScript = new Script::SStructScript;
		if ( pActualStruct->GetScript( targetChecksum, pStructScript, Script::NO_ASSERT ) )
		{
			// construct a script and run it
			Script::CScript* pScript = new Script::CScript;
			pScript->SetScript( pStructScript, NULL, mp_appearance );	
			#ifdef __NOPT_ASSERT__
			pScript->SetCommentString("Created in CModelBuilder::model_run_script(...)");
			#endif
			int ret_val;
			while ((ret_val = pScript->Update()) != Script::ESCRIPTRETURNVAL_FINISHED)
			{
				// Script must not get blocked, otherwise it'll hang in this loop forever.
				Dbg_MsgAssert(ret_val != Script::ESCRIPTRETURNVAL_BLOCKED,("\n%s\nScript got blocked when being run by RunScript.",pScript->GetScriptInfo()));
			}
			delete pScript;
		}
		delete pStructScript;
	}
	
	return true;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx

#if 0

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelScalingModifier::ApplyModifier( Model* pModel, CModelAppearance* pAppearance )
{
	Dbg_Assert( pModel );
	Dbg_Assert( pAppearance );

	// Enforce that it's a skin model, for now...
	// TODO:  Move some skin model functionality into model class...
	Model* pModel = static_cast ( pModel );
	if ( !pModel )
	{
		return false;
	}

	Mth::Vector vec = pModel->GetScale();

	// always reset the guy when scaling
	vec[X] = 1.0f;
	vec[Y] = 1.0f;
	vec[Z] = 1.0f;
	
	float heightScale = pAppearance->GetFloat( vCHECKSUM_HEIGHT_SCALE );
	
	vec.Scale( heightScale );

	pModel->SetScale( vec );

	// reset scaling mode...  for the most part,
	// it will be apply_normal_mode
	// unless someone overrides it in script
	uint32 scalingMode = vCHECKSUM_APPLY_NORMAL_MODE;

	pAppearance->GetChecksum( vCHECKSUM_SCALING_MODE, &scalingMode );

	// apply the desired scaling script here...
//	Dbg_Message( "Applying %s scaling mode\n", Script::FindChecksumName( scalingMode ) );
	Script::RunScript( scalingMode, NULL, pModel );

	pModel->ApplyScaling();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModelWeightModifier::ApplyModifier( Model* pModel, CModelAppearance* pAppearance )
{
	Dbg_Assert( pModel );
	Dbg_Assert( pAppearance );

	// Enforce that it's a skin model, for now...
	// TODO:  Move some skin model functionality into model class...
	Model* pModel = static_cast ( pModel );
	if ( !pModel )
	{
		return false;
	}

	// should automatically be reset to identity...
	CScalingTable scaling_table;

	Script::CScriptStructure* pTempStructure = new Script::CScriptStructure;
	float weight = pAppearance->GetFloat( "weight_scale" );
	scaling_table.SetWeightScale( weight );
	delete pTempStructure;

	pModel->ApplyScaling( &scaling_table );
	
	return true;
}
#endif


================================================
FILE: Code/Gfx/ModelBuilder.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       ModelBuilder.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/16/2001
//****************************************************************************

#ifndef __GFX_MODELBUILDER_H
#define __GFX_MODELBUILDER_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

namespace Nx
{
    class CModel;
};
                
namespace Gfx
{
	class CModelAppearance;
	class CSkeleton;
	
	const uint32 vCHECKSUM_CREATE_MODEL_FROM_APPEARANCE = 0x91c1ec93;	// create_model_from_appearance
	const uint32 vCHECKSUM_COLOR_MODEL_FROM_APPEARANCE = 0xd2c041c2;	// color_model_from_appearance
	const uint32 vCHECKSUM_SCALE_MODEL_FROM_APPEARANCE = 0xaee1af7d;	// scale_model_from_appearance
	const uint32 vCHECKSUM_HIDE_MODEL_FROM_APPEARANCE = 0xb1a55798;		// hide_model_from_appearance


/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class CModelBuilder	: public Obj::CObject
{
	public:
		CModelBuilder( bool useAssetManager, uint32 texDictOffset );

	public:
		virtual void	BuildModel( CModelAppearance* pAppearance, Nx::CModel* pModel, Gfx::CSkeleton* pSkeleton, uint32 scriptName = vCHECKSUM_CREATE_MODEL_FROM_APPEARANCE );
		virtual bool	CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript );
	
	protected:
		virtual bool	model_add_geom( uint32 partChecksum );
		virtual bool	model_clear_geom( uint32 partChecksum );
		virtual bool	model_hide_geom( uint32 partChecksum, bool hidden );
		virtual bool	geom_modulate_color( Script::CStruct* pParams );
		virtual bool	geom_set_uv_offset( uint32 partChecksum, uint32 matChecksum, int pass );
		virtual bool	geom_allocate_uv_matrix_params( uint32 matChecksum, int pass );
		virtual bool	geom_replace_texture( uint32 partChecksum );
		virtual bool	model_clear_all_geoms();
		virtual bool	model_remove_polys();
		virtual bool	model_reset_scale();
		virtual bool	model_apply_body_shape();
		virtual bool	model_apply_bone_scale( uint32 partChecksum );
		virtual bool	model_apply_object_scale();
		virtual bool	model_finalize();
		virtual bool	model_run_script( uint32 partChecksum, uint32 targetChecksum );
		
	protected:
		// temporary pointer to the model, so that CallMemberFunctions
		// can operate on the Nx::CModel.  This only exists for the
		// duration of the BuildModel() function call.
		static Nx::CModel*			mp_model;
		static Gfx::CSkeleton*		mp_skeleton;
		static CModelAppearance*	mp_appearance;
		bool						m_useAssetManager;
		uint32						m_texDictOffset;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __GFX_MODELBUILDER_H


================================================
FILE: Code/Gfx/NGC/NX/anim.cpp
================================================
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include "nx_init.h"
#include "mesh.h"
#include "scene.h"
#include "anim.h"

namespace NxNgc
{
//	// Material color attenuation.
//	static DWORD WeightedMeshVertexShader0Decl[] = {
//	D3DVSD_STREAM( 0 ),
//	D3DVSD_REG( VSD_REG_POS,		D3DVSDT_FLOAT3 ),	// Position.
//	D3DVSD_REG( VSD_REG_WEIGHTS,	D3DVSDT_FLOAT4 ),	// Weights.
//	D3DVSD_REG( VSD_REG_INDICES,	D3DVSDT_SHORT4 ),	// Indices.
//	D3DVSD_REG( VSD_REG_NORMAL,		D3DVSDT_FLOAT3 ),	// Normals.
//	D3DVSD_REG( VSD_REG_TEXCOORDS,	D3DVSDT_FLOAT2 ),	// Texture coordinates.
//	D3DVSD_END() };
//
//	// Vertex color attenuation.
//	static DWORD WeightedMeshVertexShader1Decl[] = {
//	D3DVSD_STREAM( 0 ),
//	D3DVSD_REG( VSD_REG_POS,		D3DVSDT_FLOAT3 ),	// Position.
//	D3DVSD_REG( VSD_REG_WEIGHTS,	D3DVSDT_FLOAT4 ),	// Weights.
//	D3DVSD_REG( VSD_REG_INDICES,	D3DVSDT_SHORT4 ),	// Indices.
//	D3DVSD_REG( VSD_REG_NORMAL,		D3DVSDT_FLOAT3 ),	// Normals.
//	D3DVSD_REG( VSD_REG_COLOR,		D3DVSDT_D3DCOLOR ),	// Diffuse color.
//	D3DVSD_REG( VSD_REG_TEXCOORDS,	D3DVSDT_FLOAT2 ),	// Texture coordinates.
//	D3DVSD_END() };
//
//
//
//DWORD WeightedMeshVertexShader0;
//DWORD WeightedMeshVertexShader1;
//
//
////DWORD GetVertexShader( bool vertex_colors )
////{
////	if( vertex_colors )
////	{
////		return WeightedMeshVertexShader1;
////	}
////
////	return WeightedMeshVertexShader0;
////}
//
//

//void CreateWeightedMeshVertexShaders( void )
//{
//	static bool created_shaders = false;
//
//	if( !created_shaders )
//	{
//		created_shaders = true;
//
//		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShader0Decl,
//													dwWeightedmeshvertexshader0VertexShader,	// Defined in the header file from xsasm.
//													&WeightedMeshVertexShader0,
//													0 ))
//		{
//			exit( 0 );
//		}
//
//		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShader1Decl,
//													dwWeightedmeshvertexshader1VertexShader,	// Defined in the header file from xsasm.
//													&WeightedMeshVertexShader1,
//													0 ))
//		{
//			exit( 0 );
//		}
//	}
//}




//static void MatrixInvertOrthoNormalized( D3DXMATRIX *matrix, const D3DXMATRIX *matrixIn )
//{
//    /*
//     * Inverse of upper left 3x3 sub matrix
//     * is a simple transpose
//     */
////    matrix->right.x = matrixIn->right.x;
////    matrix->right.y = matrixIn->up.x;
////    matrix->right.z = matrixIn->at.x;
////    matrix->up.x = matrixIn->right.y;
////    matrix->up.y = matrixIn->up.y;
////    matrix->up.z = matrixIn->at.y;
////    matrix->at.x = matrixIn->right.z;
////    matrix->at.y = matrixIn->up.z;
////    matrix->at.z = matrixIn->at.z;
//	matrix->m[0][0] = matrixIn->m[0][0];
//	matrix->m[0][1] = matrixIn->m[1][0];
//	matrix->m[0][2] = matrixIn->m[2][0];
//	matrix->m[0][3] = 0.0f;
//	matrix->m[1][0] = matrixIn->m[0][1];
//	matrix->m[1][1] = matrixIn->m[1][1];
//	matrix->m[1][2] = matrixIn->m[2][1];
//	matrix->m[1][3] = 0.0f;
//	matrix->m[2][0] = matrixIn->m[0][2];
//	matrix->m[2][1] = matrixIn->m[1][2];
//	matrix->m[2][2] = matrixIn->m[2][2];
//	matrix->m[2][3] = 0.0f;
//
//    /*
//     * calculate translation componennt of inverse
//     */
////    matrix->pos.x = -RwV3dDotProductMacro(&matrixIn->pos, &matrixIn->right);
////    matrix->pos.y = -RwV3dDotProductMacro(&matrixIn->pos, &matrixIn->up);
////    matrix->pos.z = -RwV3dDotProductMacro(&matrixIn->pos, &matrixIn->at);
//    matrix->m[3][0] = -(( matrixIn->m[3][0] * matrixIn->m[0][0] ) + ( matrixIn->m[3][1] * matrixIn->m[0][1] ) + ( matrixIn->m[3][2] * matrixIn->m[0][2] ));
//    matrix->m[3][1] = -(( matrixIn->m[3][0] * matrixIn->m[1][0] ) + ( matrixIn->m[3][1] * matrixIn->m[1][1] ) + ( matrixIn->m[3][2] * matrixIn->m[1][2] ));
//    matrix->m[3][2] = -(( matrixIn->m[3][0] * matrixIn->m[2][0] ) + ( matrixIn->m[3][1] * matrixIn->m[2][1] ) + ( matrixIn->m[3][2] * matrixIn->m[2][2] ));
//    matrix->m[3][3] = 1.0f;
//}





//void setup_weighted_mesh_vertex_shader( void *p_root_matrix, void *p_bone_matrices, int num_bone_matrices )
//{
//	D3DXMATRIX	dest_matrix;
//	D3DXMATRIX	inverse_view_matrix;
//	D3DXMATRIX	temp_matrix;
//	D3DXMATRIX	projMatrix;
//	D3DXMATRIX	viewMatrix;
//	D3DXMATRIX	worldMatrix;
//
//	// Projection matrix.
//	projMatrix.m[0][0] = EngineGlobals.projection_matrix.m[0][0];
//    projMatrix.m[0][1] = EngineGlobals.projection_matrix.m[1][0];
//    projMatrix.m[0][2] = EngineGlobals.projection_matrix.m[2][0];
//    projMatrix.m[0][3] = EngineGlobals.projection_matrix.m[3][0];
//    projMatrix.m[1][0] = EngineGlobals.projection_matrix.m[0][1];
//	projMatrix.m[1][1] = EngineGlobals.projection_matrix.m[1][1];
//    projMatrix.m[1][2] = EngineGlobals.projection_matrix.m[2][1];
//    projMatrix.m[1][3] = EngineGlobals.projection_matrix.m[3][1];
//
//    projMatrix.m[2][0] = 0.0f;
//    projMatrix.m[2][1] = 0.0f;
////    projMatrix.m[2][2] = camera->farPlane / ( camera->farPlane - camera->nearPlane );
//    projMatrix.m[2][2] = 20000.0f / ( 20000.0f - 2.0f );
////	projMatrix.m[2][3] = -projMatrix.m[2][2] * camera->nearPlane;
//	projMatrix.m[2][3] = -projMatrix.m[2][2] * 2.0f;
//
//    projMatrix.m[3][0] = 0.0f;
//    projMatrix.m[3][1] = 0.0f;
//    projMatrix.m[3][2] = 1.0f;
//    projMatrix.m[3][3] = 0.0f;
//
//	// View matrix.
//	D3DXMATRIX	view_matrix = EngineGlobals.view_matrix;
//	
//	viewMatrix.m[0][0] = -view_matrix.m[0][0];
//    viewMatrix.m[0][1] = view_matrix.m[1][0];
//    viewMatrix.m[0][2] = -view_matrix.m[2][0];
//    viewMatrix.m[0][3] = view_matrix.m[3][0];
//    viewMatrix.m[1][0] = -view_matrix.m[0][1];
//    viewMatrix.m[1][1] = view_matrix.m[1][1];
//    viewMatrix.m[1][2] = -view_matrix.m[2][1];
//    viewMatrix.m[1][3] = view_matrix.m[3][1];
//    viewMatrix.m[2][0] = view_matrix.m[0][2];
//    viewMatrix.m[2][1] = -view_matrix.m[1][2];
//    viewMatrix.m[2][2] = view_matrix.m[2][2];
//    viewMatrix.m[2][3] = -view_matrix.m[3][2];
//    viewMatrix.m[3][0] = 0.0f;
//    viewMatrix.m[3][1] = 0.0f;
//    viewMatrix.m[3][2] = 0.0f;
//    viewMatrix.m[3][3] = 1.0f;
//	
//	// World space transformation matrix.
//	worldMatrix.m[0][0] = ((float*)p_root_matrix )[0];
//	worldMatrix.m[0][1] = ((float*)p_root_matrix )[1];
//	worldMatrix.m[0][2] = ((float*)p_root_matrix )[2];
//	worldMatrix.m[0][3] = ((float*)p_root_matrix )[3];
//    worldMatrix.m[1][0] = ((float*)p_root_matrix )[4];
//    worldMatrix.m[1][1] = ((float*)p_root_matrix )[5];
//    worldMatrix.m[1][2] = ((float*)p_root_matrix )[6];
//    worldMatrix.m[1][3] = ((float*)p_root_matrix )[7];
//    worldMatrix.m[2][0] = ((float*)p_root_matrix )[8];
//    worldMatrix.m[2][1] = ((float*)p_root_matrix )[9];
//    worldMatrix.m[2][2] = ((float*)p_root_matrix )[10];
//    worldMatrix.m[2][3] = ((float*)p_root_matrix )[11];
//    worldMatrix.m[3][0] = 0.0f;
//    worldMatrix.m[3][1] = 0.0f;
//    worldMatrix.m[3][2] = 0.0f;
//    worldMatrix.m[3][3] = 1.0f;
//
//	// Calculate composite world->view->projection matrix.
//	D3DXMatrixMultiply( &temp_matrix, &viewMatrix, &worldMatrix );
//	D3DXMatrixMultiply( &dest_matrix, &projMatrix, &temp_matrix );
//
//	// Switch to 192 constant mode, removing the lock on the reserved constants c-38 and c-37.
//	D3DDevice_SetShaderConstantMode( D3DSCM_192CONSTANTS | D3DSCM_NORESERVEDCONSTANTS );
//
//	// Load up the combined world, camera & projection matrix.
//	for( int i = 0; i < 16; ++i )
//	{
//		if( fabsf(((float*)dest_matrix.m )[i] ) < 0.001f )
//		{
//			((float*)dest_matrix.m )[i] = 0.0f;
//		}
//	}
//	
//	D3DDevice_SetVertexShaderConstant( VSCONST_REG_TRANSFORM_OFFSET, (void*)&dest_matrix, VSCONST_REG_TRANSFORM_SIZE );
//	D3DDevice_SetVertexShaderConstant( VSCONST_REG_WORLD_TRANSFORM_OFFSET,	(void*)&worldMatrix, VSCONST_REG_WORLD_TRANSFORM_SIZE );
//
//	// Load up the directional light data.
//	static float directional_light_color[24] = {
//	0.0f, -1.0f, 0.0f, 0.0f,	// Dir 0
//	0.7f, 0.6f, 0.5f, 1.0f,		// Col 0
//	1.0f, 0.0f, 0.0f, 0.0f,		// Dir 1
//	0.35f, 0.4f, 0.4f, 1.0f,		// Col 1
//	0.0f, 0.0f, 1.0f, 0.0f,		// Dir 2
//	0.3f, 0.3f, 0.2f, 1.0f };	// Col 2
//	
//	D3DDevice_SetVertexShaderConstant( VSCONST_REG_DIR_LIGHT_OFFSET,		(void*)directional_light_color, VSCONST_REG_DIR_LIGHT_SIZE );
//
//	// Load up the ambient light color.
//	static float ambient_light_color[4] = { 0.4f, 0.4f, 0.4f, 1.0f };
//	D3DDevice_SetVertexShaderConstant( VSCONST_REG_AMB_LIGHT_OFFSET, (void*)ambient_light_color, 1 );
//	
//	// Load up the material color.
//	static float material_color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
//	D3DDevice_SetVertexShaderConstant( VSCONST_REG_MATERIAL_OFFSET, (void*)material_color, 1 );
//
//	// Load up the bone transforms, in sets of 40.
//	int offset = 0;
//	while( num_bone_matrices > 0 )
//	{
//		if( num_bone_matrices >= 10 )
//		{
//			D3DDevice_SetVertexShaderConstant( VSCONST_REG_MATRIX_OFFSET + offset, (float*)p_bone_matrices + ( offset * 4 ), 10 * 3 );
//			offset += 10 * 3;
//			num_bone_matrices -= 10;
//		}
//		else
//		{
//			D3DDevice_SetVertexShaderConstant( VSCONST_REG_MATRIX_OFFSET + offset, (float*)p_bone_matrices + ( offset * 4 ), num_bone_matrices * 3 );
//			num_bone_matrices = 0;
//		}
//	}
//
//	// Load up the replacement registers for c-38 and c-37.
//	static float homogenous_to_screen_reg[8] = { 320.0f, -240.0f, 1.67772e7f, 0.0f, 320.03125f, 240.03125f, 0.0f, 0.0f };
//	D3DDevice_SetVertexShaderConstant( 94, (void*)homogenous_to_screen_reg, 2 );
//}



//void shutdown_weighted_mesh_vertex_shader( void )
//{
//	// Switch back to 96 constant mode.
//	D3DDevice_SetShaderConstantMode( D3DSCM_96CONSTANTS );
//}


} // namespace NxNgc

================================================
FILE: Code/Gfx/NGC/NX/anim.h
================================================
#ifndef __ANIM_H
#define __ANIM_H

namespace NxNgc
{

//DWORD	GetVertexShader( bool vertex_colors );
//void	CreateWeightedMeshVertexShaders( void );
//void	setup_weighted_mesh_vertex_shader( void *p_root_matrix, void *p_bone_matrices, int num_bone_matrices );
//void	shutdown_weighted_mesh_vertex_shader( void );


} // namespace NxNgc

#endif // __ANIM_H



================================================
FILE: Code/Gfx/NGC/NX/chars.cpp
================================================
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include "nx_init.h"
#include "chars.h"

#include 
#include 
#include 
#include 
#include 
#include 

#include 

/*


	
.fnt file format (by Ryan)
--------------------------

	4	File size in bytes
	4	Number of characters
	4	Default height
	4	Default base
	
	?	Character table (see below)
	?	Texture (see below)

	Character
	2	Baseline (how many pixels down relative to top of image)
	2	Ascii value

	Texture
	4	Size of texture
	2	Width
	2	Height
	2	Bit depth
	6	Padding
	W*H	Raw data
	0-3	Padding for uint32 alignment
	1K	Palette data
	4	Number of subtextures
	?	Subtexture table (see below)

	Subtexture
	2	X
	2	Y
	2	W
	2	H
	
*/

// This is the function generated by the makefile when building permanent font files.
extern void * get_font_file_address( const char * p_filename );

namespace NxNgc
{


float CurrentScale;
uint32 FontVramBase;
SFont *pFontList;
SFont *pButtonsFont = NULL;

float wx0 = 0.0f;
float wy0 = 0.0f;
float wx1 = 640.0f;
float wy1 = 448.0f;

SFont *SText::spOverrideFont = NULL;

SFont *LoadFont( const char *Filename )
{
	SFont	*pFont;
	SChar	*pChar;
	uint8	*pData;
//	int		i,Len,NumChars,Width,Height,Depth,NumBytes;
	int		i,NumChars,Width,Height,Depth,NumBytes;

	char * p8 = (char *)Filename;
	while ( *p8 != '\0' )
	{
		if ( ( *p8 >= 'A' ) && ( *p8 <= 'Z' ) )
		{
			*p8 = *p8 - ( 'A' - 'a' );
		}
		p8++;
	}
	unsigned char * p_fnt = (unsigned char *)get_font_file_address( Filename );
	Dbg_MsgAssert( p_fnt, ( "Font file '%s' not found in permanent data.", Filename ));

#define get_bytes(p,n) { memcpy( p, p_fnt, n ); p_fnt += n; }

	// Allocate memory for the font structure.
	pFont = new SFont();

	// Allocate a temporary buffer.
	uint8 FontBuf[2048];

	// Load file header.
	get_bytes( FontBuf, 16 );
//	Len = File::Read( FontBuf, 16, 1, p_FH );
//	Dbg_MsgAssert( Len==16, ( "couldn't read file header from font file %s", Filename ));
	NumChars			 = ((uint32 *)FontBuf)[1];
	pFont->DefaultHeight = ((uint32 *)FontBuf)[2];
	pFont->DefaultBase	 = ((uint32 *)FontBuf)[3];

#ifdef __PLAT_NGC__
	// PJR - This needs to disappear once we start converting .ska to .ska.ngc etc.
	NumChars				= nReverse32( NumChars );
	pFont->DefaultHeight	= nReverse32( pFont->DefaultHeight );
	pFont->DefaultBase		= nReverse32( pFont->DefaultBase );
#endif		// __PLAT_NGC__

	// Clear character map to zero.
	memset( pFont->Map, 0, 256 );
	memset(pFont->SpecialMap, 0, 32);

	// Allocate memory for character table.
	pFont->pChars = new SChar[NumChars];

	// Load character map and character table.
	get_bytes( FontBuf, NumChars << 2 );
//	Len = File::Read( FontBuf, NumChars << 2, 1, p_FH );
//	Dbg_MsgAssert( Len == ( NumChars << 2 ), ( "couldn't read character table in font file %s", Filename ));

	for( i = 0, pChar = pFont->pChars, pData = FontBuf; i < NumChars; i++,pChar++,pData += 4 )
	{
#ifdef __PLAT_NGC__
		// PJR - This needs to disappear once we start converting .ska to .ska.ngc etc.
		pChar->Baseline = nReverse32( pChar->Baseline );
		pChar->Baseline = nReverse16( ((uint16 *)pData)[0] );
		sint16 ascii_val = nReverse16(((sint16*) pData)[1]);
		if (ascii_val >= 0)
			pFont->Map[(uint8) ascii_val] = i;
		else
		{
			Dbg_Assert(ascii_val >= -32)
			pFont->SpecialMap[(uint8) (-ascii_val - 1)] = i;
		}
#else
		pChar->Baseline							= ((uint16 *)pData)[0];
		pFont->Map[(uint8)((uint16*)pData)[1]]	= i;
#endif		// __PLAT_NGC__
	}

	// now, if there is a null character in the font, make characters that could not be found
	// in the font display that instead of 'A'
	if (pFont->SpecialMap[31] != 0)
	{
		for (i = 0; i < 256; i++) 
		{
			if (pFont->Map[i] == 0 && i != 'A' && i != 'a') pFont->Map[i] = pFont->SpecialMap[31];
			if (i < 31 && pFont->SpecialMap[i] == 0) pFont->SpecialMap[i] = pFont->SpecialMap[31];
		}
	}	
	
	// Load texture header.
	get_bytes( FontBuf, 16 );
//	Len = File::Read( FontBuf, 16, 1, p_FH );
//	Dbg_MsgAssert( Len == 16, ( "couldn't read texture header from font file %s", Filename ));
	Width	= ((uint16 *)FontBuf)[2];
	Height	= ((uint16 *)FontBuf)[3];
	Depth	= ((uint16 *)FontBuf)[4];

#ifdef __PLAT_NGC__
	// PJR - This needs to disappear once we start converting .ska to .ska.ngc etc.
	Width	= nReverse16( Width );
	Height	= nReverse16( Height );
	Depth	= nReverse16( Depth );
#endif		// __PLAT_NGC__

	// Create texture.
	Dbg_Assert( Depth == 8 );

	NumBytes = ( Width * Height + 3 ) & 0xFFFFFFFC;
	
	// Align texture to 32 bytes & point up.
	int size = ( Width * Height ) + ( 256 * 2 );
	unsigned char * p_align = (unsigned char*)OSRoundUp32B( p_fnt );
	for ( int lp = ( size - 1 ); lp >= 0; lp-- ) p_align[lp] = p_fnt[lp];
	DCFlushRange( p_align, size );
	pFont->mp_texture = p_align;
	pFont->mp_palette = &p_align[( Width * Height )];
	p_fnt += ( Width * Height );		// Texture
	p_fnt += ( 256 * 2 );				// Palette
	p_fnt += 32;						// Padding

//	pFont->mp_texture = new char[ ( Width * Height ) ];
//	Len = File::Read( pFont->mp_texture, ( Width * Height ), 1, p_FH );
//	Dbg_MsgAssert( Len == ( Width * Height ), ( "couldn't read texture data from font file %s", Filename ));
//
//	pFont->mp_palette = new char[ ( 256 * 4 ) ];
//	Len = File::Read( pFont->mp_palette, ( 256 * 4 ), 1, p_FH );
//	Dbg_MsgAssert( Len == ( 256 * 4 ), ( "couldn't read palette data from font file %s", Filename ));

	pFont->m_width = Width;
	pFont->m_height = Height;

	// Skip numsubtextures, and load subtextures.
	get_bytes( FontBuf, ( NumChars << 3 ) + 4 );
//	Len = File::Read( FontBuf, ( NumChars << 3 ) + 4, 1, p_FH );
//	Dbg_MsgAssert( Len == ( NumChars << 3 ) + 4, ( "couldn't read subtexture table from font file %s", Filename ));
	for( i = 0, pChar = pFont->pChars, pData = FontBuf + 4; i < NumChars; i++, pChar++, pData += 8 )
	{
		uint16 x = ((uint16 *)pData )[0];
		uint16 y = ((uint16 *)pData )[1];
		uint16 w = ((uint16 *)pData )[2];
		uint16 h = ((uint16 *)pData )[3];
		
#ifdef __PLAT_NGC__
		x = nReverse16( x );
		y = nReverse16( y );
		w = nReverse16( w );
		h = nReverse16( h );
#endif		// __PLAT_NGC__

		pChar->w	= w;
		pChar->h	= h;
		pChar->u0	= ( ((float)x + 0.5f) / (float)Width );
		pChar->v0	= ( ((float)y + 0.5f) / (float)Height );
		pChar->u1	= pChar->u0 + ((float)w / (float)Width );
		pChar->v1	= pChar->v0 + ((float)h / (float)Height );
	}

	// Add font to font list.
	pFont->pNext	= pFontList;
	pFontList		= pFont;

	// We're done with the font file now.
//	File::Close( p_FH );
	
	// this will serve as the default spacing
	pFont->mSpaceSpacing = pFont->pChars[pFont->Map['I']].w;
	
	return pFont;
}



void UnloadFont(SFont *pFont)
{
	SFont *pPrevFont;
	int found=0;

	// find font and unchain from list
	if (pFontList == pFont)
	{
		found=1;
		pFontList = pFontList->pNext;
	}
	else
	{
		for (pPrevFont=pFontList; pPrevFont->pNext; pPrevFont=pPrevFont->pNext)
		{
			if (pPrevFont->pNext == pFont)
			{
				found=1;
				pPrevFont->pNext = pFont->pNext;
				break;
			}
		}
	}

	Dbg_MsgAssert(found, ("attempt to unload font which has not been loaded"));

	// free memory
	free( pFont->pChars );
	free( pFont );
}

uint32 SFont::GetDefaultHeight() const
{
	return DefaultHeight << 4;
}

uint32 SFont::GetDefaultBase() const
{
	return DefaultBase << 4;
}

void SFont::QueryString(char *String, float &width, float &height)
{
	SChar *pChar;
	char *pLetter;
	float u0,v0,x0,u1,v1,x1;

	x0 = 0;

	//sint16 char_spacing = 16 + (mCharSpacing<<4);
	float char_spacing = mCharSpacing;
	float space_spacing = mSpaceSpacing;
	
	for (pLetter=String;; pLetter++)
	{
		pChar = NULL;
		// may be overridden by the '\b' tag
		SFont *p_font = this;
		
		// acount for tags (might be multiple ones in a row)
		bool got_char_tag = false; // tag resulting in output of character
		while (*pLetter == '\\' && !got_char_tag)
		{
			pLetter++;
			if (*pLetter == '\\')
				break;

			switch(*pLetter)
			{
				case '\\':
					got_char_tag = true;
					break;
				case 'c':
				case 'C':
					pLetter += 2; // skip over "c#"
					break;
				case 's':
				case 'S':
				{
					pLetter++; // skip "s"
					uint digit = Str::DehexifyDigit(pLetter);
					pChar = pChars + SpecialMap[digit];
					got_char_tag = true;
					break;
				}
				case 'b':
				case 'B':
				{
					pLetter++; // skip "b"
					uint digit = Str::DehexifyDigit(pLetter);
					
					// switch over to buttons font, the regular font will be used again on the next character

					p_font = pButtonsFont;
					Dbg_Assert(p_font);
					pChar = p_font->pChars + p_font->SpecialMap[digit];
					got_char_tag = true;
					break;
				}
				case 'm':
				case 'M':
				{
					pLetter++; // skip "m"
					char button_char = Nx::CFontManager::sMapMetaCharacterToButton(pLetter);
					uint digit = Str::DehexifyDigit(&button_char);
					
					p_font = pButtonsFont;
					Dbg_Assert(p_font);
					pChar = p_font->pChars + p_font->SpecialMap[digit];
					got_char_tag = true;
					break;
				}
				default:
					Dbg_MsgAssert(0, ("unknown tag %c", *pLetter));
					break;
			}
		} // end while
		
		if (*pLetter == '\0') break;

		if (*pLetter != ' ' || pChar)
		{
			if (!pChar)
				pChar = p_font->pChars + p_font->Map[(uint8)*pLetter];
			u0 = pChar->u0;
			v0 = pChar->v0;
			u1 = pChar->u1;
			v1 = pChar->v1;
			x1 = x0+((u1-u0)*m_width);
		}
		else
		{
			x1 = x0 + space_spacing;
		}
		
		x0 = x1+char_spacing;
	}

	width  = (float)x0;
	height = (float)DefaultHeight;
}



/////////////////////////////////////////////////////////////////////////////
//

SText::SText( float pri ) : SDraw2D( pri, true )
{
}


SText::~SText( void )
{
}


void SText::BeginDraw( void )
{
}

/////////////////////////////////////////////////////////////////////////////
//

void SText::Draw( void )
{
	bool uploaded = false;
	bool new_color = true;
	SChar *pChar = NULL;
	char *pLetter;
	float u0,v0,x0,y0,u1,v1,x1,y1,yt;

	x0 = m_xpos + wx0;
	y0 = m_ypos + wy0;

	//sint16 char_spacing = 16 + (mp_font->mCharSpacing<<4);
	float char_spacing = (float) mp_font->mCharSpacing * m_xscale;
	float space_spacing = (float) mp_font->mSpaceSpacing * m_xscale;
	
	GXColor current_color;
	current_color.a = (m_rgba&0xff);
	current_color.b = ((m_rgba&0xff00)>>8);
	current_color.g = ((m_rgba&0xff0000)>>16);
	current_color.r = ((m_rgba&0xff000000)>>24);
	
	for (pLetter=mp_string;; pLetter++)
	{
		pChar = NULL;
		SFont *p_font = (spOverrideFont) ? spOverrideFont : mp_font;
		
		// acount for tags (might be multiple ones in a row)
		bool got_char_tag = false; // tag resulting in output of character
		while (*pLetter == '\\' && !got_char_tag)
		{
			pLetter++;

			switch(*pLetter)
			{
				case '\\':
					got_char_tag = true;
					break;
				case 'c':
				case 'C':
				{
					pLetter++;	// skip "c"					
					uint digit = Str::DehexifyDigit(pLetter);
					pLetter++; // skip "#"

					// set active color from font
					uint32 trgba;
					if (digit != 0 && !m_color_override)
					{
						trgba = mp_font->mRGBATab[digit-1];
						current_color.r = (trgba&0xff);
						current_color.g = ((trgba&0xff00)>>8);
						current_color.b = ((trgba&0xff0000)>>16);
						current_color.a = ((trgba&0xff000000)>>24);
					}
					else
					{
						trgba = m_rgba;
						current_color.a = (trgba&0xff);
						current_color.b = ((trgba&0xff00)>>8);
						current_color.g = ((trgba&0xff0000)>>16);
						current_color.r = ((trgba&0xff000000)>>24);
					}

					//GX::SetChanAmbColor( GX_COLOR0A0, current_color );
					new_color = true;

					break;
				}
				case 's':
				case 'S':
				{
					pLetter++;	// skip "s"
					uint digit = Str::DehexifyDigit(pLetter);
					
					pChar = mp_font->pChars + mp_font->SpecialMap[digit];
					got_char_tag = true;
					
					break;
				}
				case 'b':
				case 'B':
				{
					// 'B' stands for button, accesses the button font

					pLetter++; // skip "b"
					uint digit = Str::DehexifyDigit(pLetter);
					
					// switch to the buttons font!
					
					p_font = pButtonsFont;
					Dbg_Assert(p_font);
					pChar = p_font->pChars + p_font->SpecialMap[digit];
					got_char_tag = true;
					
					spOverrideFont = p_font;
					//EndDraw();
					//BeginDraw();
					uploaded = false;
					// we can now return to using the regular font (no override)
					spOverrideFont = NULL;
					
					break;
				}
				default:
					Dbg_MsgAssert(0, ("unknown tag %c", *pLetter));
					break;
			}
		} // end while

		if (*pLetter == '\0') break;
		
		if (*pLetter != ' ' || pChar)
		{
			if (!pChar)
				pChar = p_font->pChars + p_font->Map[(uint8) *pLetter];
			yt = y0 + (float)(p_font->DefaultBase - pChar->Baseline) * m_yscale;
			u0 = pChar->u0;
			v0 = pChar->v0;
			u1 = pChar->u1;
			v1 = pChar->v1;
			x1 = x0+(((u1-u0)*p_font->m_width)*m_xscale);
			y1 = yt+(((v1-v0)*p_font->m_height)*m_yscale);
		}
		else
		{
			x0 += (space_spacing + char_spacing);
			continue;
		}

		if ( !uploaded && current_color.a )
		{
			uploaded = true;
			// Upload the texture & alpha map.
			GX::UploadTexture( p_font->mp_texture,
							   p_font->m_width,
							   p_font->m_height,
							   GX_TF_C8,
							   GX_CLAMP,
							   GX_CLAMP,
							   GX_FALSE,
							   GX_LINEAR,
							   GX_LINEAR,
							   0.0f,
							   0.0f,
							   0.0f,
							   GX_FALSE,
							   GX_FALSE,
							   GX_ANISO_1,
							   GX_TEXMAP0 );

			GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, p_font->m_width, p_font->m_height );
	
			GX::UploadPalette( p_font->mp_palette,
							   GX_TL_RGB5A3,
							   GX_TLUT_256,
							   GX_TEXMAP0 );

//			GXTexObj	texObj;
//			GXInitTexObjCI(
//				&texObj,
//				p_font->mp_texture,
//				p_font->m_width,
//				p_font->m_height,
//				GX_TF_C8,
//				GX_CLAMP,
//				GX_CLAMP,
//				GX_FALSE,
//				GX_TLUT0 );
//			GXInitTexObjLOD(&texObj, GX_LINEAR, GX_LINEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
//			GXLoadTexObj( &texObj, GX_TEXMAP0 );
//	
//			GXTlutObj	palObj;
//			GXInitTlutObj( &palObj, p_font->mp_palette, GX_TL_RGB5A3, 16 );
//			GXLoadTlut ( &palObj, GX_TLUT0 );
	
	
		//	GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP1 );       // Alpha map is in Green channel, so use this to swap it to the alpha channel.
		//	GX::SetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR0A0 );
		//	GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
		//	GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
		//	GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO );
		//	GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
	
	
			GX::SetTexChanTevIndCull( 1, 1, 1, 0, GX_CULL_NONE );
			GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
			GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
			GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

			GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
			GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_RASA, GX_CA_TEXA, GX_CA_ZERO,
									 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV,
									 GX_TEV_SWAP0, GX_TEV_SWAP0 );
			GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_RASC, GX_CC_TEXC, GX_CC_ZERO,
								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );


		//		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
	
			// Set current vertex descriptor to enable position and color0.
			// Both use 8b index to access their data arrays.
			GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
	
			// Set material color.
			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
	
			GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
		}

		if ( current_color.a )
		{
			if ( new_color )
			{
				GX::SetChanAmbColor( GX_COLOR0A0, current_color );
				new_color = false;
			}
			// Send coordinates.
			GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
				GX::Position3f32(x0, yt, -1.0f);
				GX::TexCoord2f32(u0, v0);
				GX::Position3f32(x1, yt, -1.0f);
				GX::TexCoord2f32(u1, v0);
				GX::Position3f32(x1, y1, -1.0f);
				GX::TexCoord2f32(u1, v1);
				GX::Position3f32(x0, y1, -1.0f);
				GX::TexCoord2f32(u0, v1);
			GX::End();
		}

		x0 = x1 + char_spacing;

		if (p_font != mp_font)
		{
			// we just used the button font, so return to the regular one
			uploaded = false;
			//EndDraw();
			//BeginDraw();
		}
	} // end for
}




/////////////////////////////////////////////////////////////////////////////
//

void SText::EndDraw( void )
{
}









void SetTextWindow( uint16 x0, uint16 x1, uint16 y0, uint16 y1 )
{
	wx0 = (float)x0;
	wy0 = (float)y0;
	wx1 = (float)x1;
	wy1 = (float)y1;
//	TextRegSCISSOR = (uint64)x0 | (uint64)x1<<16 | (uint64)y0<<32 | (uint64)y1<<48;
}

void SText::DrawSingle( void )
{
	BeginDraw();
	Draw();
	EndDraw();
}

} // namespace Ngc



================================================
FILE: Code/Gfx/NGC/NX/chars.h
================================================
#ifndef __CHARS_H
#define __CHARS_H

#include 

namespace NxNgc
{


typedef struct
{
	float	u0, v0, u1, v1;
	uint16	w, h;
	uint16	Baseline;
}
SChar;


struct SFont
{
public:
	uint32		GetDefaultHeight() const;
	uint32		GetDefaultBase() const;

//	void		BeginText(uint32 rgba, float Scale);
//	void		DrawString(char *String, float x0, float y0);
//	void		EndText(void);
	void		QueryString(char *String, float &width, float &height);

	//char Name[16];
	uint32		DefaultHeight, DefaultBase;
	SChar		*pChars;
	uint8		Map[256];
	uint8		SpecialMap[32];
//	uint8		*pVifData;
//	uint32		VifSize;
//	uint64		RegTEX0, RegTEX1;
	SFont		*pNext;

	void*		mp_texture;
	void*		mp_palette;
	int			m_width;
	int			m_height;

	float		mCharSpacing;
	float		mSpaceSpacing;
	uint32		mRGBATab[16];

//	IDirect3DTexture8*	pD3DTexture;		// To do - these should probably be replaced with an sTexture.
//	IDirect3DPalette8*	pD3DPalette;
};



SFont *		LoadFont( const char *Filename );
void		UnloadFont( SFont * );
void		SetTextWindow( uint16 x0, uint16 x1, uint16 y0, uint16 y1 );


struct SText : public SDraw2D
{
	public:
					SText( float pri = 0.0f );
	virtual			~SText();

	void			DrawSingle( void );

	SFont			*mp_font;

	char			*mp_string;
	float			m_xpos;
	float			m_ypos;
	float			m_xscale;
	float			m_yscale;
	uint32			m_rgba;
	bool			m_color_override;
	
	// used in conjunction with BeginDraw()
	// if set, use specified font instead of mp_font
	// if not, use mp_font
	static SFont *	spOverrideFont;

private:

	void			BeginDraw( void );
	void			Draw( void );
	void			EndDraw( void );
};

void SwizzleTexture( void *dstBuffer, void *srcBuffer, int width, int height, int32 depth, int32 stride );

extern uint32 FontVramBase;
extern SFont *pFontList;
extern SFont *pButtonsFont;


} // namespace NxNgc


#endif // __CHARS_H



================================================
FILE: Code/Gfx/NGC/NX/geomnode.cpp
================================================
///*****************************************************************************
//**																			**
//**			              Neversoft Entertainment.			                **
//**																		   	**
//**				   Copyright (C) 2000 - All Rights Reserved				   	**
//**																			**
//******************************************************************************
//**																			**
//**	Project:													**
//**																			**
//**	Module:						 								**
//**																			**
//**	File name:		.cpp											**
//**																			**
//**	Created by:		00/00/00	-	initials								**
//**																			**
//**	Description:			 									**
//**																			**
//*****************************************************************************/
//
///*****************************************************************************
//**							  	  Includes									**
//*****************************************************************************/
//
//#include 
//#include 
//#include "render.h"
//#include "geomnode.h"
//#include 
//
///*****************************************************************************
//**								DBG Information								**
//*****************************************************************************/
//
//namespace NxNgc
//{
//
//
//
///*****************************************************************************
//**								  Externals									**
//*****************************************************************************/
//
///*****************************************************************************
//**								   Defines									**
//*****************************************************************************/
//
///*****************************************************************************
//**								Private Types								**
//*****************************************************************************/
//
///*****************************************************************************
//**								 Private Data								**
//*****************************************************************************/
//
///*****************************************************************************
//**								 Public Data								**
//*****************************************************************************/
//
//CGeomNode CGeomNode::sWorld;
//CGeomNode CGeomNode::sDatabase;
//
///*****************************************************************************
//**							  Private Prototypes							**
//*****************************************************************************/
//
///*****************************************************************************
//**							  Private Functions								**
//*****************************************************************************/
//
///*****************************************************************************
//**							  Public Functions								**
//*****************************************************************************/
//
//
//CGeomNode::CGeomNode()
//{
//	m_flags = 0;
//
//	m_bounding_sphere[0] = 0.0f;
//	m_bounding_sphere[1] = 0.0f;
//	m_bounding_sphere[2] = 0.0f;
//	m_bounding_sphere[3] = (float)1.0e+30;
//
//	mp_properties	= NULL;
//	mp_child		= NULL;
//	mp_sibling		= NULL;
//	mp_dma			= NULL;
//	mp_group		= NULL;
//}
//
//
//void CGeomNode::Init()
//{
//	m_flags = 0;
//
//	m_bounding_sphere[0] = 0.0f;
//	m_bounding_sphere[1] = 0.0f;
//	m_bounding_sphere[2] = 0.0f;
//	m_bounding_sphere[3] = (float)1.0e+30;
//
//	mp_properties	= NULL;
//	mp_child		= NULL;
//	mp_sibling		= NULL;
//	mp_dma			= NULL;
//	mp_group		= NULL;
//}
//
//
//CGeomNode::CGeomNode(const CGeomNode &node)
//{
//	m_flags				= node.m_flags;
//	m_bounding_sphere	= node.m_bounding_sphere;
//	mp_properties		= node.mp_properties;
//	mp_child			= node.mp_child;
//	mp_sibling			= node.mp_sibling;
//	mp_dma				= node.mp_dma;
//	mp_group			= node.mp_group;
//}
//
//
//
//
//#define GET_FLAG(FLAG) (m_flags & (FLAG) ? true : false)
//#define SET_FLAG(FLAG) m_flags = yes ? m_flags|FLAG : m_flags&~FLAG
//
//bool CGeomNode::IsLeaf()
//{
//	return GET_FLAG(NODEFLAG_LEAF);
//}
//
//
//bool CGeomNode::IsObject()
//{
//	return GET_FLAG(NODEFLAG_OBJECT);
//}
//
//
//bool CGeomNode::IsActive()
//{
//	return GET_FLAG(NODEFLAG_ACTIVE);
//}
//
//
//bool CGeomNode::IsTransformed()
//{
//	return GET_FLAG(NODEFLAG_TRANSFORMED);
//}
//
//
//void CGeomNode::SetLeaf(bool yes)
//{
//	SET_FLAG(NODEFLAG_LEAF);
//}
//
//
//void CGeomNode::SetObject(bool yes)
//{
//	SET_FLAG(NODEFLAG_OBJECT);
//}
//
//
//void CGeomNode::SetActive(bool yes)
//{
//	SET_FLAG(NODEFLAG_ACTIVE);
//}
//
//
//void CGeomNode::SetTransformed(bool yes)
//{
//	SET_FLAG(NODEFLAG_TRANSFORMED);
//}
//
//
//uint32 CGeomNode::GetChecksum()
//{
//	return m_checksum;
//}
//
//
//void CGeomNode::SetChecksum(uint32 checksum)
//{
//	m_checksum = checksum;
//}
//
//
//void CGeomNode::SetBoundingSphere(float x, float y, float z, float R)
//{
//	m_bounding_sphere[0] = x;
//	m_bounding_sphere[1] = y;
//	m_bounding_sphere[2] = z;
//	m_bounding_sphere[3] = R;
//}
//
//
//uint8 *CGeomNode::GetDma()
//{
//	return mp_dma;
//}
//
//
//void CGeomNode::SetDma(uint8 *pDma)
//{
//	mp_dma = pDma;
//}
//
//
//CGeomNode *CGeomNode::GetChild()
//{
//	return mp_child;
//}
//
//
//void CGeomNode::SetChild(CGeomNode *pChild)
//{
//	mp_child = pChild;
//}
//
//
//CGeomNode *CGeomNode::GetSibling()
//{
//	return mp_sibling;
//}
//
//
//void CGeomNode::SetSibling(CGeomNode *pSibling)
//{
//	mp_sibling = pSibling;
//}
//
//
//sGroup *CGeomNode::GetGroup()
//{
//	return mp_group;
//}
//
//
//void CGeomNode::SetGroup(sGroup *pGroup)
//{
//	mp_group = pGroup;
//}
//
//
//Mth::Matrix& CGeomNode::GetMatrix()
//{
//	Dbg_MsgAssert(IsTransformed(), ("trying to access matrix of a non-transformed CGeomNode"));
//	Dbg_MsgAssert(mp_properties, ("trying to access matrix of a CGeomNode which has no properties"));
//	return *((Mth::Matrix *)mp_properties);
//}
//
//
//void CGeomNode::SetMatrix(Mth::Matrix *p_mat)
//{
//	mp_properties = (void *)p_mat;
//	SetTransformed(p_mat ? true : false);
//}
//
//
//void *CGeomNode::GetProperties()
//{
//	return mp_properties;
//}
//
//
//void CGeomNode::SetProperties(void *pProperties)
//{
//	mp_properties = pProperties;
//}
//
//
//#ifdef __PLAT_WN32__
//
//void CGeomNode::Preprocess(uint8 *p_baseAddress)
//{
//	// recursively process any children
//	CGeomNode *p_child, *p_sibling;
//	for (p_child=mp_child; p_child; p_child=p_sibling)
//	{
//		p_sibling = p_child->mp_sibling;
//		p_child->Preprocess(p_baseAddress);
//	}
//
//	// convert properties pointer to offset
//	if (mp_properties)
//		mp_properties = (void *) ( (uint8 *)mp_properties - p_baseAddress );
//	else
//		mp_properties = (void *)(-1);
//
//	// convert dma pointer to offset
//	if (mp_dma)
//		mp_dma = (uint8 *)( mp_dma - p_baseAddress );
//	else
//		mp_dma = (uint8 *)(-1);
//
//	// leave group ID alone
//
//	// convert child pointer to offset
//	if (mp_child)
//	{
//		mp_child = (CGeomNode *) ( (uint8 *)mp_child - p_baseAddress );
//	}
//	else
//	{
//		mp_child = (CGeomNode *)(-1);
//	}
//
//	// convert sibling pointer to offset
//	if (mp_sibling)
//	{
//		mp_sibling = (CGeomNode *) ( (uint8 *)mp_sibling - p_baseAddress );
//	}
//	else
//	{
//		mp_sibling = (CGeomNode *)(-1);
//	}
//}
//
//#endif
//
//
//
//CGeomNode* CGeomNode::CreateInstance(Mth::Matrix *p_mat)
//{
//	// copy node
//	CGeomNode *p_node = new CGeomNode(*this);
//
//	// set matrix
//	p_node->SetMatrix(p_mat);
//
////	// add to world
////	p_node->BecomesChildOf(&sWorld);
////
////	// activate!
////	p_node->SetActive(true);
//
//	// return new node
//	return p_node;
//}
//
//
//void CGeomNode::DeleteInstance()
//{
////	RemoveFrom(&sWorld);
//	delete this;
//}
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//
//} // namespace NxNgc







================================================
FILE: Code/Gfx/NGC/NX/geomnode.h
================================================
///*****************************************************************************
//**																			**
//**			              Neversoft Entertainment.			                **
//**																		   	**
//**				   Copyright (C) 2000 - All Rights Reserved				   	**
//**																			**
//******************************************************************************
//**																			**
//**	Project:		THPS4													**
//**																			**
//**	Module:			NxNgc					 								**
//**																			**
//**	File name:		geometry.h												**
//**																			**
//**	Created by:		02/08/02	-	mrd										**
//**																			**
//**	Description:	classes for Ngc geometry								**
//**																			**
//*****************************************************************************/
//
//#ifndef __GFX_NGC_NX_GEOMETRY_H
//#define __GFX_NGC_NX_GEOMETRY_H
//
///*****************************************************************************
//**							  	  Includes									**
//*****************************************************************************/
//
///*****************************************************************************
//**								   Defines									**
//*****************************************************************************/
//
//
//namespace NxNgc
//{
//	// Forward declarations
//	struct	sGroup;
//
//
//#define NODEFLAG_ACTIVE			(1<<0)
//#define NODEFLAG_LEAF			(1<<1)
//#define NODEFLAG_OBJECT			(1<<2)
//#define NODEFLAG_TRANSFORMED	(1<<3)
//#define NODEFLAG_COLOURED		(1<<4)
//
//#define LOADFLAG_RENDERNOW		(1<<0)
//
//// put these here temporarily
//#define RENDERFLAG_CULL			(1<<0)
//#define RENDERFLAG_CLIP			(1<<1)
//
//
///*****************************************************************************
//**							Class Definitions								**
//*****************************************************************************/
//
//struct STransformContext
//{
//	Mth::Matrix	matrix;
//	Mth::Matrix	adjustedMatrix;
//	Mth::Vector	translation;
//};
//
//
//class CGeomNode
//{
//public:
//
//
//
//	#ifdef __PLAT_NGC__
//	void				Render(STransformContext& transformContext, uint32 renderFlags);
//	#endif
//
//	bool				IsLeaf();
//	bool				IsActive();
//	bool				IsTransformed();
//	bool				IsObject();
//
//	void				SetLeaf(bool yes);
//	void				SetActive(bool yes);
//	void				SetTransformed(bool yes);
//	void				SetObject(bool yes);
//
//	void				SetChecksum(uint32 checksum);
//	void				SetBoundingSphere(float x, float y, float z, float R);	// change args to const Mth::Vector sphere
//	void				SetDma(uint8 *pDma);
//	void				SetChild(CGeomNode *pChild);
//	void				SetSibling(CGeomNode *pSibling);
//	void				SetGroup(sGroup *pGroup);
//	void				SetMatrix(Mth::Matrix *p_mat);
//	void				SetProperties(void *pProperties);
//
//	uint32				GetChecksum();
//	Mth::Matrix&		GetMatrix();
//	void*				GetProperties();
//	CGeomNode*			GetChild();
//	CGeomNode*			GetSibling();
//	uint8*				GetDma();
//	sGroup*				GetGroup();
//
//	#ifdef __PLAT_WN32__
//	void 				Preprocess(uint8 *p_baseAddress);
//	#endif
//
//	#ifdef __PLAT_NGC__
//	static CGeomNode*	sProcessInPlace(uint8 *pPipData, uint32 loadFlags);
//	void				Cleanup();
//	
//	CGeomNode*			CreateInstance(Mth::Matrix *p_mat);
//	void				DeleteInstance();
//	CGeomNode*			CreateCopy();
//	#endif
//	
//	void				Init();
//
//						CGeomNode();
//						CGeomNode(const CGeomNode &node);
//						~CGeomNode() {}
//
//
//						static CGeomNode	sWorld;
//						static CGeomNode	sDatabase;
//
//
//
//private:
//
//	#ifdef __PLAT_NGC__
//	bool				IsOutsideViewVolume(Mth::Vector &s);
//	bool				IsInsideOuterVolume(Mth::Vector &s);
//	bool				NeedsClipping(Mth::Vector &s);
//
//	void				SendVu1Context(STransformContext& tc);
//	void				LinkDma(uint8 *p_newTail);
//
//	void 				ProcessNodeInPlace(uint8 *p_baseAddress);
//
//	void 				BecomesChildOf(CGeomNode *p_parent);
//	void				RemoveFrom(CGeomNode *p_parent);
//
//	#endif
//
//	Mth::Vector			m_bounding_sphere;
//	uint32				m_flags;
//	void *				mp_properties;
//	CGeomNode *			mp_child;
//	CGeomNode *			mp_sibling;
//	uint8 *				mp_dma;
//	sGroup *			mp_group;
//	uint32				m_checksum;
//	uint32				pad[1];
//};
//
//
//
///*****************************************************************************
//**							 Private Declarations							**
//*****************************************************************************/
//
///*****************************************************************************
//**							  Private Prototypes							**
//*****************************************************************************/
//
///*****************************************************************************
//**							  Public Declarations							**
//*****************************************************************************/
//
///*****************************************************************************
//**							   Public Prototypes							**
//*****************************************************************************/
//
///*****************************************************************************
//**								Inline Functions							**
//*****************************************************************************/
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//
//} // namespace NxNgc
//
//#endif	// __GFX_NGC_NX_GEOMETRY_H




================================================
FILE: Code/Gfx/NGC/NX/grass.cpp
================================================
//-----------------------------------------------------------------------------
// File: XBFur.cpp
//
// Desc: routines for generating and displaying a patch of fur,
//       which is a series of layers that give the appearance of
//       hair, fur, grass, or other fuzzy things.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#include 
#include 
#include 

#include 
#include 
#include "nx_init.h"
#include "render.h"
#include "grass.h"
//#include "mipmap.h"

////extern LPDIRECT3DDEVICE8 NxNgc::EngineGlobals.p_Device;
//
//#define irand(a) ((rand()*(a))>>15)
//#define frand(a) ((float)rand()*(a)/32768.0f)
//
//#define SAFE_RELEASE(p)      { if(p) { (p)->Release(); (p)=NULL; } }
//
//
//float g_fOneInch = 0.01f;
//
//
//extern CXBFur *p_fur;
//
//
//
//
////-----------------------------------------------------------------------------
//// Name: Constructor
//// Desc: 
////-----------------------------------------------------------------------------
//CXBFur::CXBFur()
//{
//    DWORD i;
//    ZeroMemory(this, sizeof(CXBFur));
//
//    // init default patch
//    m_fXSize = 0.1f;
//    m_fZSize = 0.1f;
//
//    // init default fuzzlib
//    m_dwNumFuzzLib = 0;
//    m_pFuzzLib = NULL;
//    m_fuzzCenter.colorBase = D3DXCOLOR(1.f, 1.f, 1.f, 1.f);
//    m_fuzzCenter.colorTip = D3DXCOLOR(1.f, 1.f, 1.f, 0.f);
//    m_fuzzCenter.dp.y = 1.0f;
//
//    // init default fuzz
//    m_dwNumSegments = 4;
//    m_pFuzz = NULL;
//
//    // init default volume
//    m_dwNumSlices = 0;
//    m_dwSliceXSize = 0;
//    m_dwSliceZSize = 0;
//    for(i=0; im_fYSize)
//            m_fYSize = y;
//    }
//
//     // initialize the fuzz locations & pick a random fuzz from the library
//    srand(m_dwSeed*54795);
//    for(i=0; i 0)
//    {
//        D3DSURFACE_DESC desc;
//        m_apSliceTexture[0]->GetLevelDesc(0, &desc);
//        if (desc.Format != D3DFMT_A8R8G8B8)
//            bFormatOK = false;
//    }
//
//    // make sure volume info is up to date
//    if(m_dwSliceXSize!=slicexsize 
//       || m_dwSliceZSize!=slicezsize
//       || !bFormatOK)
//    {
//        m_dwNumSlices = 0;
//        m_dwNumSlicesLOD = 0;
//        for(UINT i=0; i> i;
//        m_dwLODMax = i - 1;
//
//        // create new layers and level-of-detail layers
//        for(i=0; iCreateTexture(slicexsize, slicezsize, 0, 0, D3DFMT_A8R8G8B8, 0, &m_apSliceTexture[i]);
//            
//        // release unused layers
//        for(i=nslices+nLOD; iGetRenderTarget(&save.pBackBuffer);
//    NxNgc::EngineGlobals.p_Device->GetDepthStencilSurface(&save.pZBuffer);
//    NxNgc::EngineGlobals.p_Device->GetTransform( D3DTS_WORLD, &save.matWorld);
//    NxNgc::EngineGlobals.p_Device->GetTransform( D3DTS_VIEW, &save.matView);
//    NxNgc::EngineGlobals.p_Device->GetTransform( D3DTS_PROJECTION, &save.matProjection);
//        
//    // make a new depth buffer
//    IDirect3DSurface8 *pZBuffer = NULL;
//    NxNgc::EngineGlobals.p_Device->CreateDepthStencilSurface(slicexsize, slicezsize, D3DFMT_LIN_D24S8, D3DMULTISAMPLE_NONE, &pZBuffer);
//    D3DXVECTOR3 va(-0.5f*m_fXSize, 0.f, -0.5f*m_fZSize), vb(0.5f*m_fXSize, m_fYSize, 0.5f*m_fZSize); // bounds of fuzz patch
//    D3DXVECTOR3 vwidth(vb-va), vcenter(0.5f*(vb+va)); // width and center of fuzz patch
//        
//    // set world transformation to scale model to unit cube [-1,1] in all dimensions
//    D3DXMATRIX matWorld, matTranslate, matScale;
//    D3DXMatrixTranslation(&matTranslate, -vcenter.x, -vcenter.y, -vcenter.z);
//    D3DXMatrixScaling(&matScale, 2.f/vwidth.x, 2.f/vwidth.y, 2.f/vwidth.z);
//    matWorld = matTranslate * matScale;
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_WORLD, &matWorld);
//        
//    // set view transformation to flip z and look down y axis, with bottom-most slice scaled and translated to map to [0,1]
//    D3DMATRIX matView;
//    matView.m[0][0] = 1.f;    matView.m[0][1] =  0.f;    matView.m[0][2] = 0.f;                matView.m[0][3] = 0.f;
//    matView.m[1][0] = 0.f;    matView.m[1][1] =  0.f;    matView.m[1][2] = 0.5f * nslices;    matView.m[1][3] = 0.f;
//    matView.m[2][0] = 0.f;    matView.m[2][1] = -1.f;    matView.m[2][2] = 0.f;                matView.m[2][3] = 0.f;
//    matView.m[3][0] = 0.f;    matView.m[3][1] =  0.f;    matView.m[3][2] = 0.5f * nslices;    matView.m[3][3] = 1.f;
//        
//    // set projection matrix to orthographic
//    D3DXMATRIX matProjection;
//    D3DXMatrixIdentity(&matProjection);
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_PROJECTION, &matProjection);
//    UINT numLines = m_dwNumFuzz*m_dwNumSegments;
//    IDirect3DVertexBuffer8 *pVB;
//    GetLinesVertexBuffer(&pVB);
//    NxNgc::EngineGlobals.p_Device->SetTexture(0, NULL);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); // premultiplied alpha
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_LIGHTING, FALSE);
//
//    NxNgc::EngineGlobals.p_Device->SetPixelShader( 0 );
//    NxNgc::EngineGlobals.p_Device->SetVertexShader(FVF_XYZDIFF);
//    NxNgc::EngineGlobals.p_Device->SetStreamSource(0, pVB, sizeof(FVFT_XYZDIFF));
//        
//    // draw each slice
//    for (int i = 0; i < (int)nslices; i++)
//    {
//        // get destination surface & set as render target, then draw the fuzz slice
//        LPDIRECT3DTEXTURE8 pTexture = m_apSliceTexture[i];
//        IDirect3DSurface8 *pSurface;
//        pTexture->GetSurfaceLevel(0, &pSurface);
//        NxNgc::EngineGlobals.p_Device->SetRenderTarget(pSurface, pZBuffer);
//        NxNgc::EngineGlobals.p_Device->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, D3DCOLOR_RGBA(0x10,0x20,0x10,0), 1.0f, 0 );
//        matView.m[1][2] = 0.5f * nslices;
//        matView.m[3][2] = 0.5f * nslices - (float)i;    // offset to next slice
//        
//        // We want the texture to wrap, so draw multiple times with offsets in the plane so that 
//        // the boundaries will be filled in by the overlapping geometry.
//        for (int iX = -1; iX <= 1; iX++)
//        {
//            for (int iY = -1; iY <= 1; iY++)
//            {
//                matView.m[3][0] = 2.f * iX;
//                matView.m[3][1] = 2.f * iY;
//                NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_VIEW, &matView);
//                NxNgc::EngineGlobals.p_Device->DrawPrimitive(D3DPT_LINELIST, 0, numLines);
//            }
//        }
//        pSurface->Release();
//        GenerateMipmaps(pTexture, 0);
//    }
//    
//    // clean up
//    pVB->Release();
//    pZBuffer->Release();
//    NxNgc::EngineGlobals.p_Device->SetRenderTarget(save.pBackBuffer, save.pZBuffer);
//    save.pBackBuffer->Release();
//    save.pZBuffer->Release();
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_WORLD, &save.matWorld);
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_VIEW, &save.matView);
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_PROJECTION, &save.matProjection);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_LIGHTING, TRUE);
//
//
//	// Compress the textures.
//	for( int i = 0; i < (int)nslices; i++ )
//    {
//		LPDIRECT3DTEXTURE8 pTextureSrc = m_apSliceTexture[i];
//        LPDIRECT3DTEXTURE8 pTextureDst = NULL;
//		CompressTexture( &pTextureDst, D3DFMT_DXT4, pTextureSrc );
//		m_apSliceTexture[i] = pTextureDst;
//		pTextureSrc->Release();
//	}
//}
//
////-----------------------------------------------------------------------------
//// Name: GenFin
//// Desc: Generate the fin texture in a similar way as GenSlices.
////-----------------------------------------------------------------------------
//void CXBFur::GenFin(DWORD finWidth, DWORD finHeight, float fFinXFraction, float fFinZFraction)
//{
//    m_fFinXFraction = fFinXFraction;
//    m_fFinZFraction = fFinZFraction;
//
//    // Check the format of the textures
//    bool bFormatOK = true;
//    if (m_pFinTexture != NULL)
//    {
//        D3DSURFACE_DESC desc;
//        m_pFinTexture->GetLevelDesc(0, &desc);
//        if (desc.Format != D3DFMT_A8R8G8B8)
//            bFormatOK = false;
//    }
//
//    // make sure fin info is up to date
//    if(m_finWidth != finWidth
//       || m_finHeight != finHeight
//       || !bFormatOK)
//    {
//        SAFE_RELEASE(m_pFinTexture);
//    }
//    m_finWidth = finWidth;
//    m_finHeight = finHeight;
//
//    // create fin texture if needed
//    if (m_pFinTexture == NULL)
//        NxNgc::EngineGlobals.p_Device->CreateTexture(finWidth, finHeight, 0, 0, D3DFMT_A8R8G8B8, 0, &m_pFinTexture);
//
//    // save current back buffer, z buffer, and transforms
//    struct {
//        IDirect3DSurface8 *pBackBuffer, *pZBuffer;
//        D3DMATRIX matWorld, matView, matProjection;
//    } save;
//    NxNgc::EngineGlobals.p_Device->GetRenderTarget(&save.pBackBuffer);
//    NxNgc::EngineGlobals.p_Device->GetDepthStencilSurface(&save.pZBuffer);
//    NxNgc::EngineGlobals.p_Device->GetTransform( D3DTS_WORLD, &save.matWorld);
//    NxNgc::EngineGlobals.p_Device->GetTransform( D3DTS_VIEW, &save.matView);
//    NxNgc::EngineGlobals.p_Device->GetTransform( D3DTS_PROJECTION, &save.matProjection);
//
//    // make a new depth buffer
//    IDirect3DSurface8 *pZBuffer = NULL;
//    NxNgc::EngineGlobals.p_Device->CreateDepthStencilSurface(finWidth, finHeight, D3DFMT_LIN_D24S8, D3DMULTISAMPLE_NONE, &pZBuffer);
//    D3DXVECTOR3 va(-0.5f*m_fXSize, 0.f, -0.5f*m_fZSize), vb(0.5f*m_fXSize, m_fYSize, 0.5f*m_fZSize); // bounds of fuzz patch
//    D3DXVECTOR3 vwidth(vb-va), vcenter(0.5f*(vb+va)); // width and center of fuzz patch
//
//    // set world transformation to scale model to unit cube [-1,1] in all dimensions
//    D3DXMATRIX matWorld, matTranslate, matScale;
//    D3DXMatrixTranslation(&matTranslate, -vcenter.x, -vcenter.y, -vcenter.z);
//    D3DXMatrixScaling(&matScale, 2.f/vwidth.x, 2.f/vwidth.y, 2.f/vwidth.z);
//    matWorld = matTranslate * matScale;
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_WORLD, &matWorld);
//
//    // set view transformation to look down z axis, with z range [0,1]
//    float fXFraction = m_fFinXFraction;
//    float fYFraction = 1.f;
//    float fZFraction = m_fFinZFraction; 
//    float fXScale = 1.f / fXFraction;
//    float fYScale = 1.f / fYFraction;
//    float fZScale = 1.f / fZFraction;
//    D3DMATRIX matView;
//    matView.m[0][0] = fXScale;    matView.m[0][1] =  0.f;        matView.m[0][2] = 0.f;                matView.m[0][3] = 0.f;
//    matView.m[1][0] = 0.f;        matView.m[1][1] = -fYScale;    matView.m[1][2] = 0.f;                matView.m[1][3] = 0.f;
//    matView.m[2][0] = 0.f;        matView.m[2][1] =  0.f;        matView.m[2][2] = 0.5f * fZScale;    matView.m[2][3] = 0.f;
//    matView.m[3][0] = 0.f;        matView.m[3][1] =  0.f;     matView.m[3][2] = 0.5f * fZScale;    matView.m[3][3] = 1.f;
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_VIEW, &matView);
//    // set projection matrix to orthographic
//
//    D3DXMATRIX matProjection;
//    D3DXMatrixIdentity(&matProjection);
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_PROJECTION, &matProjection);
//    UINT numLines = m_dwNumFuzz*m_dwNumSegments;
//    IDirect3DVertexBuffer8 *pVB;
//    GetLinesVertexBuffer(&pVB);
//    NxNgc::EngineGlobals.p_Device->SetTexture(0, NULL);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); // premultiplied alpha
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_LIGHTING, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetVertexShader(FVF_XYZDIFF);
//    NxNgc::EngineGlobals.p_Device->SetStreamSource(0, pVB, sizeof(FVFT_XYZDIFF));
//    
//    // draw fuzz into the fin texture, looking from the side
//    LPDIRECT3DTEXTURE8 pTexture = m_pFinTexture;
//    IDirect3DSurface8 *pSurface;
//    pTexture->GetSurfaceLevel(0, &pSurface);
//    NxNgc::EngineGlobals.p_Device->SetRenderTarget(pSurface, pZBuffer);
//    NxNgc::EngineGlobals.p_Device->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, 
//                         D3DCOLOR_RGBA(0,0,0,0), 1.0f, 0 );
//    NxNgc::EngineGlobals.p_Device->DrawPrimitive(D3DPT_LINELIST, 0, numLines);
//    pSurface->Release();
//    GenerateMipmaps(pTexture, 0, D3DTADDRESS_WRAP, D3DTADDRESS_CLAMP);
//
//    // clean up
//    pVB->Release();
//    pZBuffer->Release();
//    NxNgc::EngineGlobals.p_Device->SetRenderTarget(save.pBackBuffer, save.pZBuffer);
//    save.pBackBuffer->Release();
//    save.pZBuffer->Release();
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_WORLD, &save.matWorld);
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_VIEW, &save.matView);
//    NxNgc::EngineGlobals.p_Device->SetTransform( D3DTS_PROJECTION, &save.matProjection);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_LIGHTING, TRUE);
//}
//
////-----------------------------------------------------------------------------
//// Name: GetLinesVertexBuffer
//// Desc: Create and fill in a vertex buffer as a series of individual lines from the fuzz library.
////-----------------------------------------------------------------------------
//void CXBFur::GetLinesVertexBuffer(IDirect3DVertexBuffer8 **ppVB)
//{
//    IDirect3DVertexBuffer8 *pVB;
//    UINT numVertices = m_dwNumFuzz*m_dwNumSegments*2;
//    NxNgc::EngineGlobals.p_Device->CreateVertexBuffer(numVertices*sizeof(FVFT_XYZDIFF), 0, FVF_XYZDIFF, 0, &pVB);
//    assert(pVB!=NULL);
//    DWORD i, j, vidx, lidx;
//    FVFT_XYZDIFF *verts;
//    float x0, y0, z0;
//    float dx, dy, dz;
//    float ddx, ddy, ddz;
//    float step, cp;
//    UINT startseg = 0;
//    step = 1.0f/(float)m_dwNumSegments;
//    pVB->Lock(0, numVertices*sizeof(FVFT_XYZDIFF), (BYTE **)&verts, 0);
//    vidx = 0;
//    D3DXCOLOR colorBase, colorDelta;
//    for(i=0; iUnlock();
//    *ppVB = pVB;
//}
//
////-----------------------------------------------------------------------------
//// Name: RenderLines
//// Desc: Draw the fuzz patch as a series of individual lines.
////-----------------------------------------------------------------------------
//void CXBFur::RenderLines()
//{
//    UINT numLines = m_dwNumFuzz*m_dwNumSegments;
//    IDirect3DVertexBuffer8 *pVB;
//    GetLinesVertexBuffer(&pVB);
//    NxNgc::EngineGlobals.p_Device->SetTexture(0, NULL);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
//    NxNgc::EngineGlobals.p_Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); // premultiplied alpha
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_LIGHTING, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetVertexShader(FVF_XYZDIFF);
//    NxNgc::EngineGlobals.p_Device->SetStreamSource(0, pVB, sizeof(FVFT_XYZDIFF));
//    NxNgc::EngineGlobals.p_Device->DrawPrimitive(D3DPT_LINELIST, 0, numLines);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_LIGHTING, TRUE);
//    pVB->Release();
//}
//
//
//
//#if 0
//
////-----------------------------------------------------------------------------
//// Desc: File routines for saving and loading patch files.
////       Saving is only relevant if used on a Windows platform.
////-----------------------------------------------------------------------------
//struct _fphdr
//{
//    char sig[5];
//    int flags;
//};
//
//void CXBFur::Save(char *fname, int flags)
//{
//    HANDLE hFile;
//    struct _fphdr hdr;
//
//    DWORD dwNumBytesWritten;
//    hFile = CreateFile(fname, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
//                       OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
//    if(hFile == INVALID_HANDLE_VALUE)
//        return;
//
//    // write file header
//    strcpy(hdr.sig, "FUZ2");
//    hdr.flags = flags;
//
//    WriteFile(hFile, &hdr, sizeof(struct _fphdr), &dwNumBytesWritten, NULL);
//    
//
//    // write fpatch data
//    WriteFile(hFile, &m_dwSeed, sizeof(DWORD), &dwNumBytesWritten, NULL);
//    
//    WriteFile(hFile, &m_fXSize, sizeof(float), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fYSize, sizeof(float), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fZSize, sizeof(float), &dwNumBytesWritten, NULL);
//
//    WriteFile(hFile, &m_dwNumSegments, sizeof(DWORD), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fFinXFraction, sizeof(float), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fFinZFraction, sizeof(float), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fuzzCenter.colorBase, sizeof(D3DXCOLOR), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fuzzRandom.colorBase, sizeof(D3DXCOLOR), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fuzzCenter.colorTip, sizeof(D3DXCOLOR), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fuzzRandom.colorTip, sizeof(D3DXCOLOR), &dwNumBytesWritten, NULL);
//    
//    WriteFile(hFile, &m_fuzzCenter.dp, sizeof(D3DVECTOR), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fuzzRandom.dp, sizeof(D3DVECTOR), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fuzzCenter.ddp, sizeof(D3DVECTOR), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_fuzzRandom.ddp, sizeof(D3DVECTOR), &dwNumBytesWritten, 0);
//
//    WriteFile(hFile, &m_dwNumFuzzLib, sizeof(DWORD), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_dwNumFuzz, sizeof(DWORD), &dwNumBytesWritten, NULL);
//
//    WriteFile(hFile, &m_dwNumSlices, sizeof(DWORD), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_dwSliceXSize, sizeof(DWORD), &dwNumBytesWritten, NULL);
//    WriteFile(hFile, &m_dwSliceZSize, sizeof(DWORD), &dwNumBytesWritten, NULL);
//    
//    return;
//
//
//
//    // write volume data if available and flag is set
//    {
//    }
//
//    // cleanup
//    CloseHandle(hFile);
//}
//
//void CXBFur::Load(char *fname)
//{
//    HANDLE hFile;
//    struct _fphdr hdr;
//    DWORD numfuzzlib, numfuzz;
//    DWORD numslices, slicexsize, slicezsize;
//
//    DWORD dwNumBytesRead;
//    hFile = CreateFile(fname, GENERIC_READ, FILE_SHARE_READ, NULL,
//                       OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
//    if(hFile == INVALID_HANDLE_VALUE)
//        return;
//    
//    // read file header
//    ReadFile(hFile, &hdr, sizeof(struct _fphdr), &dwNumBytesRead, 0);
//
//    // verify signature
//    bool bFUZ1 = !strcmp(hdr.sig, "FUZ1");
//    bool bFUZ2 = !strcmp(hdr.sig, "FUZ2");
//    if (!bFUZ1 && !bFUZ2)
//        return;    // signature not understood
//
//    // read patch data
//
//    ReadFile(hFile, &m_dwSeed, sizeof(DWORD), &dwNumBytesRead, NULL);
//    ReadFile(hFile, &m_fXSize, sizeof(float), &dwNumBytesRead, NULL);
//    ReadFile(hFile, &m_fYSize, sizeof(float), &dwNumBytesRead, NULL);
//    ReadFile(hFile, &m_fZSize, sizeof(float), &dwNumBytesRead, NULL);
//    
//
//    ReadFile(hFile, &m_dwNumSegments, sizeof(DWORD), &dwNumBytesRead, NULL);
//    
//    if (bFUZ1)
//    {
//        m_fFinXFraction = 1.f;
//        m_fFinZFraction = 0.05f;
//
//        ReadFile(hFile, &m_fuzzCenter.colorBase, sizeof(D3DXCOLOR), &dwNumBytesRead, NULL);
//        m_fuzzCenter.colorTip = m_fuzzCenter.colorBase;
//        m_fuzzRandom.colorBase = m_fuzzRandom.colorTip = 0ul;
//    }
//    else
//    {
//
//        ReadFile(hFile, &m_fFinXFraction, sizeof(float), &dwNumBytesRead, NULL);
//        ReadFile(hFile, &m_fFinZFraction, sizeof(float), &dwNumBytesRead, NULL);
//        ReadFile(hFile, &m_fuzzCenter.colorBase, sizeof(D3DXCOLOR), &dwNumBytesRead, NULL);
//        ReadFile(hFile, &m_fuzzRandom.colorBase, sizeof(D3DXCOLOR), &dwNumBytesRead, NULL);
//        ReadFile(hFile, &m_fuzzCenter.colorTip, sizeof(D3DXCOLOR), &dwNumBytesRead, NULL);
//        ReadFile(hFile, &m_fuzzRandom.colorTip, sizeof(D3DXCOLOR), &dwNumBytesRead, NULL);
//    }
//
//    ReadFile(hFile, &m_fuzzCenter.dp, sizeof(D3DVECTOR), &dwNumBytesRead, NULL);  // velocity center
//    ReadFile(hFile, &m_fuzzRandom.dp, sizeof(D3DVECTOR), &dwNumBytesRead, NULL);  // velocity random
//    ReadFile(hFile, &m_fuzzCenter.ddp, sizeof(D3DVECTOR), &dwNumBytesRead, NULL); // acceleration center
//    ReadFile(hFile, &m_fuzzRandom.ddp, sizeof(D3DVECTOR), &dwNumBytesRead, NULL); // acceleration random
//    
//
//    ReadFile(hFile, &numfuzzlib, sizeof(DWORD), &dwNumBytesRead, NULL);
//    ReadFile(hFile, &numfuzz, sizeof(DWORD), &dwNumBytesRead, NULL);
//        
//    ReadFile(hFile, &numslices, sizeof(DWORD), &dwNumBytesRead, NULL);
//    ReadFile(hFile, &slicexsize, sizeof(DWORD), &dwNumBytesRead, NULL);
//    ReadFile(hFile, &slicezsize, sizeof(DWORD), &dwNumBytesRead, NULL);
//       
//    // read volume data if available and flag is set
//    {
//    }
//
//    CloseHandle(hFile);
//
//    InitFuzz(numfuzz, numfuzzlib);
//    GenSlices(numslices, slicexsize, slicezsize);
//    ComputeLevelOfDetailTextures();
//    SetLevelOfDetail(0.f);
//}
//
////////////////////////////////////////////////////////////////////////
//// Lighting Model
////////////////////////////////////////////////////////////////////////
//
//inline float
//Luminance(const D3DXCOLOR &c)
//{
//    return (0.229f * c.r) + (0.587f * c.g) + (0.114f * c.b);
//}
//
//inline float
//MaxChannel(const D3DXCOLOR &c)
//{
//    if (c.r > c.g)
//    {
//        if (c.r > c.b)
//            return c.r;
//        else
//            return c.b;
//    }
//    else
//    {
//        if (c.g > c.b)
//            return c.g;
//        else
//            return c.b;
//    }
//}
//
//inline D3DXCOLOR 
//Lerp(const D3DXCOLOR &c1, const D3DXCOLOR &c2, float s)
//{
//    return c1 + s * (c2 - c1);
//}
//
//inline D3DXCOLOR
//Desaturate(const D3DXCOLOR &rgba)
//{
//    float alpha = rgba.a;
//    if (alpha > 1.f)
//        alpha = 1.f;
//    float fMaxChan = MaxChannel(rgba);
//    if (fMaxChan > alpha) 
//    {
//        D3DXCOLOR rgbGray(alpha, alpha, alpha, alpha);
//        float fYOld = Luminance(rgba);
//        if (fYOld >= alpha)
//            return rgbGray;
//        // scale color to preserve hue
//        D3DXCOLOR rgbNew;
//        float fInvMaxChan = 1.f / fMaxChan;
//        rgbNew.r = rgba.r * fInvMaxChan;
//        rgbNew.g = rgba.g * fInvMaxChan;
//        rgbNew.b = rgba.b * fInvMaxChan;
//        rgbNew.a = alpha;
//        float fYNew = Luminance(rgbNew);
//        // add gray to preserve luminance
//        return Lerp(rgbNew, rgbGray, (fYOld - fYNew) / (alpha - fYNew));
//    }
//    return rgba;
//}
//
//inline D3DXCOLOR &operator *=(D3DXCOLOR &p, const D3DXCOLOR &q)
//{
//    p.r *= q.r;
//    p.g *= q.g;
//    p.b *= q.b;
//    p.a *= q.a;
//    return p;
//}
//
//inline D3DXCOLOR operator *(const D3DXCOLOR &p, const D3DXCOLOR &q)
//{
//    D3DXCOLOR r;
//    r.r = p.r * q.r;
//    r.g = p.g * q.g;
//    r.b = p.b * q.b;
//    r.a = p.a * q.a;
//    return r;
//}
//
////-----------------------------------------------------------------------------
////  HairLighting
////-----------------------------------------------------------------------------
//struct HairLighting {
//    // like D3DMATERIAL8, except populated with D3DXCOLOR's so that color arithmetic works
//    D3DXCOLOR m_colorDiffuse;
//    D3DXCOLOR m_colorSpecular;
//    float m_fSpecularExponent;
//    D3DXCOLOR m_colorAmbient;
//    D3DXCOLOR m_colorEmissive;
//
//    HRESULT Initialize(LPDIRECT3DDEVICE8 pDevice, D3DMATERIAL8 *pMaterial);
//    HRESULT CalculateColor(D3DXCOLOR *pColor, float LT, float HT);
//    HRESULT CalculateClampedColor(D3DXCOLOR *pColor, float LT, float HT);
//};
//
//HRESULT HairLighting::Initialize(LPDIRECT3DDEVICE8 pDevice, D3DMATERIAL8 *pMaterial)
//{
//    // Get current colors modulated by light 0 and global ambient
//    // ignore current alphas
//    m_colorDiffuse = D3DXCOLOR(pMaterial->Diffuse.r, pMaterial->Diffuse.g, pMaterial->Diffuse.b, 1.f);
//    m_colorSpecular = D3DXCOLOR(pMaterial->Specular.r, pMaterial->Specular.g, pMaterial->Specular.b, 1.f);
//    m_fSpecularExponent = pMaterial->Power;
//    m_colorAmbient = D3DXCOLOR(pMaterial->Ambient.r, pMaterial->Ambient.g, pMaterial->Ambient.b, 1.f);
//    m_colorEmissive = D3DXCOLOR(pMaterial->Emissive.r, pMaterial->Emissive.g, pMaterial->Emissive.b, 1.f);
//    return S_OK;
//}
//
///* Zockler et al's technique worked with streamlines, and used dot(V,T) instead of the simpler dot(H,T) */
//#define ZOCKLER 0 
//#if ZOCKLER
//
//HRESULT HairLighting::CalculateColor(D3DXCOLOR *pColor, float LT, float VT)
//{
//    // Zockler et al 1996
//    float fDiffuseExponent = 2.f;    // Banks used 4.8
//    float fDiffuse = powf(sqrtf(1.f - LT*LT), fDiffuseExponent);
//    float VR = LT*VT - sqrtf(1.f - LT*LT) * sqrtf(1.f - VT*VT);
//    float fSpecular = powf(VR, m_fSpecularExponent);
//    *pColor = m_colorEmissive + m_colorAmbient + fSpecular * m_colorSpecular + fDiffuse * m_colorDiffuse;
//    return S_OK;
//}
//
//#else
//
//HRESULT HairLighting::CalculateColor(D3DXCOLOR *pColor, float LT, float HT)
//{
//    float fDiffuseExponent = 2.f;    // Banks used 4.8
//    float fDiffuse = powf(sqrtf(1.f - LT*LT), fDiffuseExponent);
//    float fSpecular = powf(sqrtf(1.f - HT*HT), m_fSpecularExponent);
//    *pColor = m_colorEmissive + m_colorAmbient + fSpecular * m_colorSpecular + fDiffuse * m_colorDiffuse;
//    return S_OK;
//}
//
//#endif
//
//HRESULT HairLighting::CalculateClampedColor(D3DXCOLOR *pColor, float LT, float HT)
//{
//    D3DXCOLOR color;
//    HRESULT hr = CalculateColor(&color, LT, HT);
//    *pColor = Desaturate(color);    // bring back to 0,1 range
//    return hr;
//}
//
////////////////////////////////////////////////////////////////////////
//// 
//// Create a hair lighting lookup-table texture
//// 
////////////////////////////////////////////////////////////////////////
//HRESULT FillHairLightingTexture(D3DMATERIAL8 *pMaterial, LPDIRECT3DTEXTURE8 pTexture)
//{
//    /* 
//    The hair lighting texture maps U as the dot product of the hair
//    tangent T with the light direction L and maps V as the dot product
//    of the tangent with the half vector H.  Since the lighting is a
//    maximum when the tangent is perpendicular to L (or H), the maximum
//    is at zero.  The minimum is at 0.5, 0.5 (or -0.5, -0.5, since
//    wrapping is turned on.)  For the mapped T.H, we raise the map
//    value to a specular power.
//    */
//    HRESULT hr;
//    D3DSURFACE_DESC desc;
//    pTexture->GetLevelDesc(0, &desc);
//    if (desc.Format != D3DFMT_A8R8G8B8)
//        return E_NOTIMPL;
//    DWORD dwPixelStride = 4;
//    D3DLOCKED_RECT lockedRect;
//    hr = pTexture->LockRect(0, &lockedRect, NULL, 0l);
//    if (FAILED(hr))
//        return hr;
//    HairLighting lighting;
//    lighting.Initialize(NxNgc::EngineGlobals.p_Device, pMaterial);
//    Swizzler s(desc.Width, desc.Height, 0);
//    s.SetV(s.SwizzleV(0));
//    s.SetU(s.SwizzleU(0));
//    for (UINT v = 0; v < desc.Height; v++)
//    {
//        for (UINT u = 0; u < desc.Width; u++)
//        {
//            BYTE *p = (BYTE *)lockedRect.pBits + dwPixelStride * s.Get2D();
//            // vertical is specular lighting
//            // horizontal is diffuse lighting
//            D3DXCOLOR color;
//#if ZOCKLER
//            // Zockler et al 1996
//            float LT = 2.f * u / desc.Width - 1.0f;
//            float VT = 2.f * v / desc.Height - 1.0f;
//            lighting.CalculateClampedColor(&color, LT, VT);
//#else
//            float LT = 2.f * u / desc.Width;
//            if (LT > 1.f) LT -= 2.f;
//            float HT = 2.f * v / desc.Height;
//            if (HT > 1.f) HT -= 2.f;
//            lighting.CalculateClampedColor(&color, LT, HT);
//#endif
//            int r, g, b;
//            r = (BYTE)(255 * color.r);
//            g = (BYTE)(255 * color.g);
//            b = (BYTE)(255 * color.b);
//            *p++ = (BYTE)b;
//            *p++ = (BYTE)g;
//            *p++ = (BYTE)r;
//            *p++ = 255;    // alpha
//            s.IncU();
//        }
//        s.IncV();
//    }
//    pTexture->UnlockRect(0);
//    return S_OK;
//}
//
////-----------------------------------------------------------------------------
//// Name: SetHairLightingMaterial
//// Desc: Create and fill the hair lighting texture
////-----------------------------------------------------------------------------
//HRESULT CXBFur::SetHairLightingMaterial(D3DMATERIAL8 *pMaterial)
//{
//    // Create and fill the hair lighting texture
//    HRESULT hr;
//    m_HairLightingMaterial = *pMaterial;
//    if (!m_pHairLightingTexture)
//    {
//        DWORD dwWidth = 8; // an extremely small texture suffices when the exponents are low
//        DWORD dwHeight = 8;
//        D3DFORMAT surfaceFormat = D3DFMT_A8R8G8B8;
//        DWORD nMipMap = 1;
//        hr = D3DXCreateTexture(NxNgc::EngineGlobals.p_Device, dwWidth, dwHeight, nMipMap, 0, surfaceFormat, 0, &m_pHairLightingTexture);
//        if (FAILED(hr)) goto e_Exit;
//    }
//    hr = FillHairLightingTexture(pMaterial, m_pHairLightingTexture);
//    if (FAILED(hr)) goto e_Exit;
//e_Exit:
//    if (FAILED(hr))
//        SAFE_RELEASE(m_pHairLightingTexture);
//    return hr;
//}
//
////-----------------------------------------------------------------------------
//// Choose level of detail 
////
//// 0       = finest detail, all slices of original source textures
//// ...
//// i       = reduced number of slices, N / (1 << i)
//// i + f   = odd slices fade to clear, texLOD[2*j+1] = tex[2*j+1] * (1-f) + clear * f
////           even slices compensate,   texLOD[2*j] = (tex[2*j+1] * f) OVER tex[2*j]
//// i + 1   = reduced number of slices, N / (1 << (i+1))
//// ...
//// log2(N) = coarsest, one slice with composite of all source textures
////-----------------------------------------------------------------------------
//HRESULT CXBFur::SetLevelOfDetail(float fLevelOfDetail)
//{
//    // Choose number of LOD slices
//    if (fLevelOfDetail < 0.f)
//    {
//        m_fLevelOfDetail = 0.f;
//        m_iLOD = 0;
//        m_fLODFraction = 0.f;
//    }
//    else if (fLevelOfDetail > (float)m_dwLODMax)
//    {
//        m_fLevelOfDetail = (float)m_dwLODMax;
//        m_iLOD = m_dwLODMax;
//        m_fLODFraction = 0.f;
//    }
//    else
//    {
//        m_fLevelOfDetail = fLevelOfDetail;
//        m_iLOD = (UINT)floorf(fLevelOfDetail);
//        m_fLODFraction = fLevelOfDetail - (float)m_iLOD;
//    }
//    m_dwNumSlicesLOD = LevelOfDetailCount(m_iLOD);
//    UINT index = LevelOfDetailIndex(m_iLOD);
//    m_pSliceTextureLOD = m_apSliceTexture + index;
//    return S_OK;
//}
//
////-----------------------------------------------------------------------------
////
//// Generate level-of-detail textures by compositing together alternating layers.
////
////-----------------------------------------------------------------------------
//HRESULT CXBFur::ComputeLevelOfDetailTextures()
//{
//    // All the textures must have the same number of mip levels.
//    DWORD nMip = m_apSliceTexture[0]->GetLevelCount();
//
//    // save current back buffer and z buffer
//    struct {
//        IDirect3DSurface8 *pBackBuffer, *pZBuffer;
//    } save;
//    NxNgc::EngineGlobals.p_Device->GetRenderTarget(&save.pBackBuffer);
//    NxNgc::EngineGlobals.p_Device->GetDepthStencilSurface(&save.pZBuffer);
//
//    // set render state for compositing textures
//    NxNgc::EngineGlobals.p_Device->SetVertexShader(D3DFVF_XYZRHW|D3DFVF_TEX1);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_LIGHTING, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_STENCILENABLE, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_FOGENABLE, FALSE);
//
//    // use pixel shaders to composite two or three layers at a time
//#pragma warning(push)
//#pragma warning(disable: 4245)    // conversion from int to DWORD
//    DWORD dwPS2 = 0;
//    {
//#include "comp2.inl"
//        NxNgc::EngineGlobals.p_Device->CreatePixelShader(&psd, &dwPS2);
//    }
//    DWORD dwPS3 = 0;
//    {
//#include "comp3.inl"
//        NxNgc::EngineGlobals.p_Device->CreatePixelShader(&psd, &dwPS3);
//    }
//#pragma warning(pop)
//
//    // set default texture stage states
//    UINT xx; // texture stage index
//    for (xx = 0; xx < 4; xx++)
//    {
//        NxNgc::EngineGlobals.p_Device->SetTexture(xx, NULL);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_COLOROP, D3DTOP_DISABLE);    // Are the COLOROP and ALPHAOP needed since we're using a pixel shader?
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);    // pass texture coords without transformation
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_TEXCOORDINDEX, 0);            // all the textures use the same tex coords
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_MAGFILTER, D3DTEXF_POINT);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_MINFILTER, D3DTEXF_POINT);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_MIPFILTER, D3DTEXF_POINT);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_MIPMAPLODBIAS, 0);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_MAXMIPLEVEL, 0);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_COLORKEYOP, D3DTCOLORKEYOP_DISABLE);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_COLORSIGN, 0);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_ALPHAKILL, D3DTALPHAKILL_DISABLE);
//    }
//    
//    // Compute all the level-of-detail textures
//    for (UINT iLOD = 1; m_dwNumSlices >> iLOD; iLOD++)
//    {
//        UINT nSliceSrc = LevelOfDetailCount(iLOD-1);
//        LPDIRECT3DTEXTURE8 *apTextureSrc = m_apSliceTexture + LevelOfDetailIndex(iLOD - 1);
//        UINT nSliceDst = LevelOfDetailCount(iLOD);
//        LPDIRECT3DTEXTURE8 *apTextureDst = m_apSliceTexture + LevelOfDetailIndex(iLOD);
//        
//        // Composite source textures into LOD textures
//        UINT iMipNotHandled = (UINT)-1;
//        for (UINT iSliceDst = 0; iSliceDst < nSliceDst; iSliceDst++)
//        {
//            LPDIRECT3DTEXTURE8 pTextureDst = apTextureDst[iSliceDst];
//            UINT nComp;
//            if (iSliceDst == nSliceDst-1 && nSliceSrc > nSliceDst * 2)
//            {
//                // composite 3 textures into the top-most level when number of source textures is odd
//                nComp = 3;
//                NxNgc::EngineGlobals.p_Device->SetPixelShader(dwPS3);
//            }
//            else
//            {
//                // composite 2 textures (this is the default)
//                nComp = 2;
//                NxNgc::EngineGlobals.p_Device->SetPixelShader(dwPS2);
//            }
//            for (xx = 0; xx < nComp; xx++)
//            {
//                NxNgc::EngineGlobals.p_Device->SetTexture(xx, apTextureSrc[ iSliceDst * 2 + xx]);
//                NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
//                NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
//            }
//            for (; xx<4; xx++)
//            {
//                NxNgc::EngineGlobals.p_Device->SetTexture(xx, NULL);
//                NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_COLOROP, D3DTOP_DISABLE);
//                NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
//            }
//            for (UINT iMip = 0; iMip < nMip; iMip++)
//            {
//                DWORD width = m_dwSliceXSize / (1 << iMip);
//                if (width == 0) width = 1;
//                DWORD height = m_dwSliceZSize / (1 << iMip);
//                if (height == 0) height = 1;
//
//                // Ngc render target must of be at least 16x16
//                if (width*4 < 64 || width * height < 64)            
//                {
//                    iMipNotHandled = iMip;
//                    break; // skip rest of coarser mipmaps and go to next slice
//                }
//
//                // Use a screen space quad to do the compositing.
//                struct quad {
//                    float x, y, z, w;
//                    float u, v;
//                } aQuad[4] =
//                {
//                    {-0.5f,        -0.5f,         1.0f, 1.0f, 0.0f, 0.0f},
//                    {width - 0.5f, -0.5f,         1.0f, 1.0f, 1.0f, 0.0f},
//                    {-0.5f,        height - 0.5f, 1.0f, 1.0f, 0.0f, 1.0f},
//                    {width - 0.5f, height - 0.5f, 1.0f, 1.0f, 1.0f, 1.0f}
//                };
//
//                // get destination surface and set as render target
//                IDirect3DSurface8 *pSurface;
//                pTextureDst->GetSurfaceLevel(iMip, &pSurface);
//                NxNgc::EngineGlobals.p_Device->SetRenderTarget(pSurface, NULL); // no depth buffering
//                NxNgc::EngineGlobals.p_Device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, aQuad, sizeof(quad)); // one quad blends 2 or 3 textures
//                pSurface->Release();
//            }
//        }
//        if (iMipNotHandled > 0 && iMipNotHandled != -1)
//        {
//            // fill in the small mips with filtered versions of the previous levels
//            for (UINT iSliceDst = 0; iSliceDst < nSliceDst; iSliceDst++)
//            {
//                LPDIRECT3DTEXTURE8 pTextureDst = apTextureDst[iSliceDst];
//                GenerateMipmaps(pTextureDst, iMipNotHandled - 1, D3DTADDRESS_MIRROR, D3DTADDRESS_MIRROR);
//            }
//        }
//    }
//
//    // clean up pixel shaders
//    NxNgc::EngineGlobals.p_Device->SetPixelShader(0);
//    NxNgc::EngineGlobals.p_Device->DeletePixelShader(dwPS2);
//    NxNgc::EngineGlobals.p_Device->DeletePixelShader(dwPS3);
//    
//    // restore render states
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_LIGHTING, TRUE);
//    NxNgc::EngineGlobals.p_Device->SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
//
//    // restore texture stage states
//    for (xx=0; xx<4; xx++)
//    {
//        NxNgc::EngineGlobals.p_Device->SetTexture(xx, NULL);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_COLOROP, D3DTOP_DISABLE);
//        NxNgc::EngineGlobals.p_Device->SetTextureStageState(xx, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
//    }
//
//    // restore back buffer and z buffer
//    NxNgc::EngineGlobals.p_Device->SetRenderTarget(save.pBackBuffer, save.pZBuffer);
//    save.pBackBuffer->Release();
//    save.pZBuffer->Release();
//    return S_OK;
//}
//
////-----------------------------------------------------------------------------
////  Divide alpha channel into color channel.  Leave alpha unchanged.
////-----------------------------------------------------------------------------
//HRESULT AlphaDivide(LPDIRECT3DTEXTURE8 pTexture)
//{
//    HRESULT hr;
//    DWORD nMip = pTexture->GetLevelCount();
//    for (UINT iMip = 0; iMip < nMip; iMip++)
//    {
//        D3DSURFACE_DESC desc;
//        hr = pTexture->GetLevelDesc(iMip, &desc);
//        if (FAILED(hr))
//            return hr;
//        if (desc.Format != D3DFMT_A8R8G8B8)
//            return E_NOTIMPL;
//        D3DLOCKED_RECT lockedRect;
//        hr = pTexture->LockRect(iMip, &lockedRect, NULL, 0l);
//        if (FAILED(hr))
//            return hr;
//        UINT dwPixelSize = 4;
//        UINT nPixel = desc.Size / dwPixelSize;
//        DWORD *pPixel = (DWORD *)lockedRect.pBits;
//        while (nPixel--)
//        {
//            D3DXCOLOR c(*pPixel);
//            if (c.a > 0.f)
//            {
//                D3DXCOLOR d;
//                d.r = c.r / c.a;
//                d.g = c.g / c.a;
//                d.b = c.b / c.a;
//                d.a = c.a;
//                *pPixel = d;
//            }
//            pPixel++;
//        }
//        pTexture->UnlockRect(iMip);
//    }
//    return S_OK;
//}
//
////-----------------------------------------------------------------------------
////  Multiply alpha channel into color channel.  Leave alpha unchanged.
////-----------------------------------------------------------------------------
//HRESULT AlphaMultiply(LPDIRECT3DTEXTURE8 pTexture)
//{
//    HRESULT hr;
//    DWORD nMip = pTexture->GetLevelCount();
//    for (UINT iMip = 0; iMip < nMip; iMip++)
//    {
//        D3DSURFACE_DESC desc;
//        hr = pTexture->GetLevelDesc(iMip, &desc);
//        if (FAILED(hr))
//            return hr;
//        if (desc.Format != D3DFMT_A8R8G8B8)
//            return E_NOTIMPL;
//        D3DLOCKED_RECT lockedRect;
//        hr = pTexture->LockRect(iMip, &lockedRect, NULL, 0l);
//        if (FAILED(hr))
//            return hr;
//        UINT dwPixelSize = 4;
//        UINT nPixel = desc.Size / dwPixelSize;
//        DWORD *pPixel = (DWORD *)lockedRect.pBits;
//        while (nPixel--)
//        {
//            D3DXCOLOR c(*pPixel);
//            D3DXCOLOR d;
//            d.r = c.r * c.a;
//            d.g = c.g * c.a;
//            d.b = c.b * c.a;
//            d.a = c.a;
//            *pPixel = d;
//            pPixel++;
//        }
//        pTexture->UnlockRect(iMip);
//    }
//    return S_OK;
//}
//
////-----------------------------------------------------------------------------
////
//// Copy textures to new texture format
////
////-----------------------------------------------------------------------------
//HRESULT CXBFur::CompressNextTexture(D3DFORMAT fmtNew, UINT *pIndex)
//{
//    HRESULT hr;
//    for (UINT iTexture = 0; iTexture < XBFUR_MAXSLICE * 2 - 1; iTexture++)    // convert all the slice textures
//    {
//        LPDIRECT3DTEXTURE8 pTextureDst = NULL;
//        LPDIRECT3DTEXTURE8 pTextureSrc = m_apSliceTexture[iTexture];
//        if (pTextureSrc == NULL)
//            break;    // we're done
//
//        // See if this texture needs to be compressed
//        D3DSURFACE_DESC desc0;
//        pTextureSrc->GetLevelDesc(0, &desc0);
//        if (desc0.Format == fmtNew)
//            continue;    // we've already compressed this one, go to the next
//
//        // We've found a texture that needs to be compressed
//        hr = CompressTexture(&pTextureDst, fmtNew, pTextureSrc);
//        if (FAILED(hr))
//            return hr;
//        m_apSliceTexture[iTexture] = pTextureDst; // already addref'd
//        pTextureSrc->Release();    // we're done with the old texture
//        if (pIndex != NULL)
//            *pIndex = iTexture;    // return index of the texture we just compressed
//        return S_FALSE;    // more textures are pending
//    }
//    return S_OK;
//}
//
//
//#endif
//
//
//static bool grass_mats = false;
//static NxNgc::sMaterial*	p_grass_mats[16];
//
//
//static void createGrassMats( void )
//{
//	if( !grass_mats )
//	{
//		grass_mats = true;
//
//		for( int layer = 0; layer < 8; ++layer )
//		{
//			NxNgc::sTexture *p_tex		= new NxNgc::sTexture();
//			p_tex->pD3DTexture			= p_fur->m_apSliceTexture[layer];
//			p_tex->pD3DPalette			= NULL;
//
//			
//			NxNgc::sMaterial *p_mat	= new NxNgc::sMaterial();
//
//			p_mat->m_no_bfc				= false;
//			p_mat->Passes				= 1;
//			p_mat->pTex[0]				= p_tex;
//
//			if( layer < 3 )
//			{
//				p_mat->Flags[0]				= 0x00;
//				p_mat->RegALPHA[0]			= NxNgc::vBLEND_MODE_DIFFUSE;
//			}
//			else
//			{
//				p_mat->Flags[0]				= 0x40;	// Transparent.
//				p_mat->RegALPHA[0]			= NxNgc::vBLEND_MODE_BLEND;
//			}
//
//			p_mat->UVAddressing[0]		= 0x00000000UL;
//			p_mat->AlphaCutoff			= 1;
//			p_mat->Color[0][0]			= 1.0f;
//			p_mat->Color[0][1]			= 1.0f;
//			p_mat->Color[0][2]			= 1.0f;
//			p_mat->Color[0][3]			= 1.0f;
//			p_mat->m_sorted				= false;
//			p_mat->m_draw_order			= 0;
//			p_mat->mp_wibble_vc_colors	= NULL;
//
//			p_grass_mats[layer]			= p_mat;
//		}
//	}
//}
//



bool AddGrass( Nx::CNgcGeom *p_geom, NxNgc::sMesh *p_mesh )
{
#if 0
	bool add_grass = false;
	
	if( p_mesh->mp_material && p_mesh->mp_material->m_grassify )
	{
		add_grass = true;
	}
	else
	{
		return false;
	}

	// At this stage we know we want to add grass.
	Dbg_Assert( p_mesh->mp_material->m_grass_layers > 0 );
//	float height_per_layer = p_mesh->mp_material->m_grass_height / p_mesh->mp_material->m_grass_layers;
	
	for( int layer = 0; layer < p_mesh->mp_material->m_grass_layers; ++layer )
	{
		Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().BottomUpHeap());
		NxNgc::sMesh *p_grass_mesh = p_mesh->Clone( false );
		Mem::Manager::sHandle().PopContext();

		// No longer any shadow cast on the base mesh.
//		p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_NO_SKATER_SHADOW;
		
		if( layer != 2 )
			p_grass_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_NO_SKATER_SHADOW;
		
		// Now point the mesh to a different material, first build the material checksum from the name of the parent
		// material (Grass) and the name of the sub material (Grass_LayerX)...
		char material_name[64];
		sprintf( material_name, "Grass-Grass_Layer%d", layer );
		uint32 material_checksum	= Crc::GenerateCRCCaseSensitive( material_name, strlen( material_name ));

		// ...then use the checksum to lookup the material.
		p_grass_mesh->mp_material	= p_geom->mp_scene->GetEngineScene()->GetMaterial( material_checksum );

		// We need also to override the mesh pixel shader, which may have been setup for a multipass material.
//		NxNgc::GetPixelShader( p_grass_mesh->mp_material, &p_grass_mesh->m_pixel_shader, &p_grass_mesh->m_pixel_shader_no_mcm );
		
		// Add transparent flag to the material.
		p_grass_mesh->mp_material->Flags[0]		|= 0x40;

		// Set the draw order to ensure meshes are drawn from the bottom up.
		p_grass_mesh->mp_material->m_sorted		 = false;
		p_grass_mesh->mp_material->m_draw_order	 = layer * 0.1f;
		
		// Go through and move all the vertices up a little, and calculate new texture coordinates based on the x-z space of the vertex positions.
//		char *p_byte_dest;
//		p_grass_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_byte_dest, 0 );
//
//		for( uint32 vert = 0; vert < p_grass_mesh->m_num_vertices; ++vert )
//		{
//			D3DXVECTOR3 *p_pos = (D3DXVECTOR3*)( p_byte_dest + ( vert * p_grass_mesh->m_vertex_stride ));
//			p_pos->y += ( height_per_layer * ( layer + 1 ));
//
//			float u			= fabsf( p_pos->x ) / 48.0f;
//			float v			= fabsf( p_pos->z ) / 48.0f;
//			float *p_tex	= (float*)( p_byte_dest + p_grass_mesh->m_uv0_offset + ( vert * p_grass_mesh->m_vertex_stride ));
//			p_tex[0]		= u;		
//			p_tex[1]		= v;
//		}

		p_geom->AddMesh( p_grass_mesh );
	}
	
/*	
	
	if( p_mesh->mp_material && p_mesh->mp_material->pTex[0] )
	{
		if( p_mesh->mp_material->Passes == 1 )
		{
			if( p_mesh->mp_material->pTex[0]->Checksum == 0x8d9b6a64 )
				add_grass = true;
		}
	}

	if( !add_grass )
		return false;

	createGrassMats();

	// At this stage we know we want to add grass.
	for( int layer = 0; layer < 6; ++layer )
	{
		Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().BottomUpHeap());
		NxNgc::sMesh *p_grass_mesh = p_mesh->Clone( false );
		Mem::Manager::sHandle().PopContext();

		// No longer any shadow cast on the base mesh.
//		p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_NO_SKATER_SHADOW;
		
		if( layer != 2 )
			p_grass_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_NO_SKATER_SHADOW;
		
		// Now point the mesh to a different material.
		p_grass_mesh->mp_material = p_grass_mats[layer];

		// Go through and move all the vertices up a little, and calculate new texture coordinates based on the x-z space of the vertex positions.
		BYTE *p_byte_dest;
		p_grass_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_byte_dest, 0 );

		for( uint32 vert = 0; vert < p_grass_mesh->m_num_vertices; ++vert )
		{
			D3DXVECTOR3 *p_pos = (D3DXVECTOR3*)( p_byte_dest + ( vert * p_grass_mesh->m_vertex_stride ));
			p_pos->y += ( 1.0f * ( layer + 1 ));

			float u			= fabsf( p_pos->x ) / 48.0f;
			float v			= fabsf( p_pos->z ) / 48.0f;
			float *p_tex	= (float*)( p_byte_dest + p_grass_mesh->m_uv0_offset + ( vert * p_grass_mesh->m_vertex_stride ));
			p_tex[0]		= u;		
			p_tex[1]		= v;
		}

		p_geom->AddMesh( p_grass_mesh );
	}
*/
#endif
	return true;
}


================================================
FILE: Code/Gfx/NGC/NX/grass.h
================================================
//-----------------------------------------------------------------------------
// File: XBFur.h
//
// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
//#include "xfvf.h"
#include 

//#define XBFUR_MAXSLICE_LOG2 5
//#define XBFUR_MAXSLICE (1 << XBFUR_MAXSLICE_LOG2)
//
//extern float g_fOneInch;
//
//#define FVF_XYZDIFF (D3DFVF_XYZ|D3DFVF_DIFFUSE)
//
//typedef struct sFVFT_XYZDIFF
//{
//    D3DVECTOR v;
//    DWORD diff;
//} FVFT_XYZDIFF;
//
//
//// patch generation
//
//// a fuzz is a single hair follicle, blade of grass, etc.
//struct Fuzz
//{
//    D3DVECTOR dp;            // velocity
//    D3DVECTOR ddp;            // acceleration
//    D3DXCOLOR colorBase;
//    D3DXCOLOR colorTip;
//};
//
//// a fuzz instance is a single instance of a fuzz
//// located at x, z on the patch
//// we create only a limited number of unique fuzzes
//// and index the library with lidx.
//struct FuzzInst
//{
//    float x, z;                // fuzz location
//    int lidx;                // library index
//};
//
//// a fur patch is a volume that holds fuzzes.
//// xsize and zsize are chosen by the user
//// ysize is calculated using the height of the 
//// tallest fuzz
//class CXBFur
//{
//    friend class CXBFurMesh;
//public:
//    DWORD m_dwSeed;            // patch seed
//    
//    float m_fXSize;            // patch size in world coords
//    float m_fYSize;
//    float m_fZSize;
//
//    // fuzz library
//    DWORD m_dwNumSegments;    // # of segments in highest LOD
//    Fuzz m_fuzzCenter;        // fuzz constant
//    Fuzz m_fuzzRandom;        // random offset around center
//    DWORD m_dwNumFuzzLib;    // # of fuzz in the library
//    Fuzz *m_pFuzzLib;        // fuzz library
//
//    // fuzz instances
//    DWORD m_dwNumFuzz;        // # of fuzz in this patch
//    FuzzInst *m_pFuzz;
//
//    // patch volume
//    DWORD m_dwNumSlices;    // # of layers in the volume
//    DWORD m_dwSliceSize;        // width*height
//    DWORD m_dwSliceXSize;        // width of volume texture slice
//    DWORD m_dwSliceZSize;        // height of volume texture slice
//    LPDIRECT3DTEXTURE8 m_apSliceTexture[XBFUR_MAXSLICE * 2 - 1];    // slices of volume texture
//                    // ... followed by level-of-detail textures  N/2, N/4, N/8, ... 1
//
//    // LOD textures
//    DWORD m_dwNumSlicesLOD; // number of slices in current level of detail
//    float m_fLevelOfDetail;    // current LOD value
//    DWORD m_iLOD;            // current integer LOD value
//    float m_fLODFraction;    // fraction towards next coarser level-of-detail
//    DWORD m_dwLODMax;        // maximum LOD index
//    LPDIRECT3DTEXTURE8 *m_pSliceTextureLOD; // current level of detail pointer into m_apSliceTexture array
//
//    // hair lighting texture
//    D3DMATERIAL8 m_HairLightingMaterial;
//    LPDIRECT3DTEXTURE8 m_pHairLightingTexture;
//
//    // fin texture
//    DWORD m_finWidth, m_finHeight;        // size of fin texture
//    float m_fFinXFraction, m_fFinZFraction;    // portion of hair texture to put into fin
//    LPDIRECT3DTEXTURE8 m_pFinTexture;    // texture projected from the side
//
//    CXBFur();
//    ~CXBFur();
//    void InitFuzz(DWORD nfuzz, DWORD nfuzzlib);
//    void GenSlices(DWORD nslices, DWORD slicexsize, DWORD slicezsize);
//    void GenFin(DWORD finWidth, DWORD finHeight, float fFinXFraction, float fFinZFraction);
//    void GetLinesVertexBuffer(IDirect3DVertexBuffer8 **ppVB);
//    void RenderLines();
//    void Save(char *fname, int flags);
//    void Load(char *fname);
//    HRESULT SetHairLightingMaterial(D3DMATERIAL8 *pMaterial);
//    void SetPatchSize(float x, float z)
//    {
//        m_fXSize = x;
//        m_fZSize = z;
//        InitFuzz(m_dwNumFuzz, m_dwNumFuzzLib);    // re-init the fuzz. automatically sets ysize
//    };
//    void SetFVel(float cx, float cy, float cz, float rx, float ry, float rz)
//    {
//        m_fuzzCenter.dp.x = cx; m_fuzzCenter.dp.y = cy; m_fuzzCenter.dp.z = cz;
//        m_fuzzRandom.dp.x = rx; m_fuzzRandom.dp.y = ry; m_fuzzRandom.dp.z = rz;
//    };
//    void SetFAcc(float cx, float cy, float cz, float rx, float ry, float rz)
//    {
//        m_fuzzCenter.ddp.x = cx; m_fuzzCenter.ddp.y = cy; m_fuzzCenter.ddp.z = cz;
//        m_fuzzRandom.ddp.x = rx; m_fuzzRandom.ddp.y = ry; m_fuzzRandom.ddp.z = rz;
//    };
//
//    // fLevelOfDetail can range from 0 to log2(NumSlices)
//    HRESULT SetLevelOfDetail(float fLevelOfDetail);
//    HRESULT ComputeLevelOfDetailTextures();
//    inline UINT LevelOfDetailCount(UINT iLOD)
//    {
//        return m_dwNumSlices >> iLOD;
//    }
//    inline UINT LevelOfDetailIndex(UINT iLOD)
//    {
//        UINT offset = 0;
//        for (UINT i = 1; i <= iLOD; i++)
//            offset += LevelOfDetailCount(i-1);
//        return offset;
//    }
//    inline UINT TotalTextureCount()
//    {
//        UINT TextureCount = 0;
//        for (UINT iLOD = 0; m_dwNumSlices >> iLOD; iLOD++)
//            TextureCount += LevelOfDetailCount(iLOD);
//        return TextureCount;
//    }
//
//    // Compress textures one at a time until all are done.
//    // Returns S_OK when all the textures are in fmtNew format.
//    // Returns S_FALSE if there are textures still to be done.
//    HRESULT CompressNextTexture(D3DFORMAT fmtNew, UINT *pTextureIndex);
//};
//
//HRESULT FillHairLightingTexture(D3DMATERIAL8 *pMaterial, LPDIRECT3DTEXTURE8 pTexture);
bool AddGrass( Nx::CNgcGeom *p_geom, NxNgc::sMesh *p_mesh );



================================================
FILE: Code/Gfx/NGC/NX/import.cpp
================================================
#include 
#include 

#include 
#include 
#include 
#include "texture.h"
#include "gfx/ngc/p_nxtexture.h"
#include "mesh.h"
#include "import.h"
	
namespace NxNgc
{

uint8 *pData, *TextureLoadPos;

//----------------------------------------------------------------------------------------
//		L O A D   T E X T U R E   F I L E
//----------------------------------------------------------------------------------------

void LoadTextureFile( const char* Filename, Lst::HashTable * p_texture_table )
{
	uint32 temp;
	
	Dbg_Message( "Loading texture file %s\n", Filename );

	// Open the texture file.
	void *p_FH = File::Open( Filename, "rb" );
	if( !p_FH )
	{
		Dbg_Message( "Couldn't open texture file %s\n", Filename );
//		return 0;
		return;
	}

	// Read the texture file version.
	int version;
	File::Read( &version, sizeof( int ), 1, p_FH );

	// Read the number of textures.	
	int num_textures;
	File::Read( &num_textures, sizeof( int ), 1, p_FH );
	
//	NumTextures				+= num_textures;

	NxNgc::sTexture ** p_tex = new (Mem::Manager::sHandle().TopDownHeap()) NxNgc::sTexture *[num_textures];
	unsigned short * p_resolvem = new (Mem::Manager::sHandle().TopDownHeap()) unsigned short[num_textures];
	unsigned short * p_resolvec = new (Mem::Manager::sHandle().TopDownHeap()) unsigned short[num_textures];
	int num_resolve = 0;

	for( int t = 0; t < num_textures; ++t )
	{
		sTexture *p_texture = new sTexture;
		
		p_tex[t] = p_texture;

		File::Read( &p_texture->Checksum,		sizeof( uint32 ), 1, p_FH );
		File::Read( &temp,		sizeof( uint32 ), 1, p_FH );
		p_texture->BaseWidth = (uint16)temp;
		File::Read( &temp,		sizeof( uint32 ), 1, p_FH );
		p_texture->BaseHeight = (uint16)temp;
		File::Read( &temp,		sizeof( uint32 ), 1, p_FH );
		p_texture->Levels = (uint16)temp;

		p_texture->ActualWidth = ( p_texture->BaseWidth + 3 ) & ~3;
		p_texture->ActualHeight = ( p_texture->BaseHeight + 3 ) & ~3;

		int tex_format;
		int channel;
		int index;
		File::Read( &tex_format, sizeof( uint32 ), 1, p_FH );
		channel = ( tex_format >> 8 ) & 0xff;
		index = ( tex_format >> 16 ) & 0xffff;
		tex_format = tex_format & 0xff;

		switch ( tex_format ) {
			case 0:
				p_texture->format = GX_TF_CMPR;
				break;
			case 1:
				p_texture->format = GX_TF_CMPR;
				break;
			case 2:
				p_texture->format = GX_TF_RGBA8;
				break;
			default:
				Dbg_MsgAssert( false, ("Illegal texture format: %d\n", tex_format ));
				break;
		}

		p_texture->flags = 0;

		int has_holes;
		File::Read( &has_holes, sizeof( uint32 ), 1, p_FH );
		p_texture->flags |= has_holes ? NxNgc::sTexture::TEXTURE_FLAG_HAS_HOLES : 0;

		uint8 * p8[8];
		int bytes;
		int mipbytes[8];

		// Read color maps.
		bytes = 0;
		for( uint32 mip_level = 0; mip_level < p_texture->Levels; ++mip_level )
		{
			uint32 texture_level_data_size;
			File::Read( &texture_level_data_size,			sizeof( uint32 ), 1, p_FH );

			p8[mip_level] = new (Mem::Manager::sHandle().TopDownHeap()) uint8[texture_level_data_size];
			mipbytes[mip_level] = texture_level_data_size;
			bytes += texture_level_data_size;

			File::Read( p8[mip_level], texture_level_data_size, 1, p_FH );
		}
		// Copy all textures & delete the originals.
		Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
		p_texture->pTexelData = new uint8[bytes];
		p_texture->byte_size = bytes;
		Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
		uint8 * pTex = p_texture->pTexelData;
		for( uint32 mip_level = 0; mip_level < p_texture->Levels; ++mip_level )
			{
			memcpy( pTex, p8[mip_level], mipbytes[mip_level] );
			delete p8[mip_level];
			pTex += mipbytes[mip_level];
		}

		// Read alpha maps.
		if ( tex_format == 1 )
		{
			if ( channel == 0 )
			{
				bytes = 0;
				for( uint32 mip_level = 0; mip_level < p_texture->Levels; ++mip_level )
				{
					uint32 texture_level_data_size;
					File::Read( &texture_level_data_size,			sizeof( uint32 ), 1, p_FH );

					p8[mip_level] = new (Mem::Manager::sHandle().TopDownHeap()) uint8[texture_level_data_size];
					mipbytes[mip_level] = texture_level_data_size;
					bytes += texture_level_data_size;

					File::Read( p8[mip_level], texture_level_data_size, 1, p_FH );
				}
				// Copy all textures & delete the originals.
				Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
				p_texture->pAlphaData = new uint8[bytes];
				Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
				uint8 * pTex = p_texture->pAlphaData;
				for( uint32 mip_level = 0; mip_level < p_texture->Levels; ++mip_level )
				{
					memcpy( pTex, p8[mip_level], mipbytes[mip_level] );
					delete p8[mip_level];
					pTex += mipbytes[mip_level];
				}
				p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA;
			}
			else
			{
				p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA;

				p_resolvem[num_resolve] = t;
				p_resolvec[num_resolve] = index;
				num_resolve++;
			}
			switch ( channel )
			{
				case 0:
				default:
					p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_GREEN;
					break;
				case 1:
					p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_RED;
					break;
				case 2:
					p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_BLUE;
					break;
			}
			p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_SINGLE_OWNER;
		}
		else
		{
			// No unique alpha map.
			p_texture->pAlphaData = NULL;
			p_texture->flags &= ~NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA;
			p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_GREEN;
		}

		// Add this texture to the table.
//		pTextureTable->PutItem( p_texture->Checksum, p_texture );
		Nx::CNgcTexture *p_Ngc_texture = new Nx::CNgcTexture();
		p_Ngc_texture->SetEngineTexture( p_texture );
		if ( p_texture_table )
		{
			p_texture_table->PutItem( p_texture->Checksum, p_Ngc_texture );
		}
	}

	// Resolve alpha maps.
	for ( int r = 0; r < num_resolve; r++ )
	{
		p_tex[p_resolvem[r]]->pAlphaData = p_tex[p_resolvec[r]]->pAlphaData;
		p_tex[p_resolvec[r]]->flags &= ~NxNgc::sTexture::TEXTURE_FLAG_SINGLE_OWNER;
	}

	File::Close( p_FH );

	delete p_tex;
	delete p_resolvem;
	delete p_resolvec;

//	return pTextureTable;
}




//void Error(int err)
//{
//	printf("Error = %d\n", err);
//	exit(1);
//}

} // namespace NxNgc



================================================
FILE: Code/Gfx/NGC/NX/import.h
================================================
#ifndef __IMPORT_H
#define __IMPORT_H

#include 
#include "texture.h"
#include "gfx/nxtexture.h"

namespace NxNgc
{

void LoadTextureFile( const char* Filename, Lst::HashTable * p_texture_table );

} // namespace NxNgc


#endif // __IMPORT_H



================================================
FILE: Code/Gfx/NGC/NX/instance.cpp
================================================
#include "nx_init.h"
#include "instance.h"
#include "anim.h"
#include "render.h"
#include "gfx\camera.h"
#include "gfx\nx.h"
#include "occlude.h"
#include "charpipeline\skinning.h"
#include "sys/ngc/p_gx.h"
#include "sys/ngc/p_aram.h"
#include "sys/ngc/p_dma.h"
#include 
#include "sys/ngc/p_buffer.h"
#include 
#include "sys/ngc/p_display.h"

#include "dolphin/base/ppcwgpipe.h"
#include "dolphin/gx/gxvert.h"

bool g_in_cutscene = false;

int g_twiddle = 900;

int g_hash_items = 0;

int g_count = 0;

float g_tweak = 0.0f;

float NEAR_Z = 50.0f;
float FAR_Z = 150.0f;

float MIN_LEN = 10.0f;
float MAX_LEN = 80.0f;

extern int gNewRender;
extern int g_object;

extern bool g_mc_hack;

//#define DEBUG_SPHERES

extern "C" {

extern void TransformSingle	( ROMtx m, s16 * srcBase, s16 * dstBase, u32 count );
extern void TransformDouble	( ROMtx m0, ROMtx m1, s16 * wtBase, s16 * srcBase, s16 * dstBase, u32 count );
extern void TransformAcc	( ROMtx m, u16 count, s16 * srcBase, s16 * dstBase, u16 * indices, s16 * weights );
extern void ConvertFixed196ToFloat	( s16 * p_source, float * p_dest, int count );
extern void CalculateDotProducts	( s16 * p_xyz, u16 * p_index_list, float * p_dot, int count, float px, float py, float pz );
extern void RenderShadows			( s16 * p_xyz, u16 * p_index_list, NxNgc::sShadowEdge * p_neighbor_list, float * p_dot, int count, float px, float py, float pz, float tx, float ty, float tz );

}

//static char doubleBuffer0[(100*1024)+32];
//static char doubleBuffer1[(100*1024)+32];
//static uint32 *p_double[2] = { (uint32 *)OSRoundUp32B(doubleBuffer0), (uint32 *)OSRoundUp32B(doubleBuffer1) };
//
//static volatile bool	dmaComplete;
//
//static void arqCallback( u32 pointerToARQRequest )
//{
//	dmaComplete = true;
//}

namespace NxNgc
{


//Lst::HashTable< CInstance > *pInstanceTable	= NULL;

static Mth::Matrix	*pLastBoneTransforms	= NULL;
CInstance			*pFirstInstance			= NULL;
#if 0
static CInstance	*pAnimInstance[256];
static int			instanceCount = 0;
#endif

#define INSTANCE_ARRAY_SIZE	2048
static CInstance *p_instances[INSTANCE_ARRAY_SIZE];
int current_index = 0;

void InitialiseInstanceTable( void )
{
//	if( pInstanceTable == NULL )
//	{
//		pInstanceTable = new Lst::HashTable( 8 );
//	}
}


//bool frustum_check_sphere( NsVector * p_center, float radius )
//{
//	bool visible = true;
//
//	NsVector out;
//
//	EngineGlobals.local_to_camera.multiply( p_center, &out );
//
//	float x = out.x;
//	float y = out.y;
//	float z = out.z;
//	float R = radius;
//	
//	if (RGetBoneTransforms();
//	Mth::Matrix *p_mat2			= p_mesh2->GetBoneTransforms();
	
	// Setup some required pointers for both transforms.
	uint32 p_mat1 = (uint32)p_mesh1->GetScene()->mp_dl->mp_object_header->m_num_skin_verts;
	uint32 p_mat2 = (uint32)p_mesh2->GetScene()->mp_dl->mp_object_header->m_num_skin_verts;

	if(( p_mat1 == 0 ) || ( p_mesh1->GetScene()->m_numHierarchyObjects > 0 ))
	{
		if(( p_mat2 == NULL ) || ( p_mesh2->GetScene()->m_numHierarchyObjects > 0 ))
		{
			int num_st1 = p_mesh1->GetScene()->m_num_filled_meshes - p_mesh1->GetScene()->m_num_opaque_meshes;
			int num_st2 = p_mesh2->GetScene()->m_num_filled_meshes - p_mesh2->GetScene()->m_num_opaque_meshes;

			if( num_st1 == num_st2 )
			{
				// Try sorting on the material draw order of the first semitransparent mesh.
				if( num_st1 > 0 )
				{
					NxNgc::sMaterialHeader	*p_material1 = p_mesh1->GetScene()->mpp_mesh_list[p_mesh1->GetScene()->m_num_opaque_meshes]->mp_dl->m_material.p_header;
					NxNgc::sMaterialHeader	*p_material2 = p_mesh2->GetScene()->mpp_mesh_list[p_mesh2->GetScene()->m_num_opaque_meshes]->mp_dl->m_material.p_header; 

					if( p_material1 && p_material2 )
					{
						return ( p_material1->m_draw_order > p_material2->m_draw_order ) ? 1 : (( p_material1->m_draw_order < p_material2->m_draw_order ) ? -1 : 0 );
					}
				}
				return 0;
			}
	
			return ( num_st1 > num_st2 ) ? 1 : -1;
		}
		return 1;
	}
	else if(( p_mat2 == 0 ) || ( p_mesh2->GetScene()->m_numHierarchyObjects > 0 ))
	{
		return -1;
	}

	// At this stage we know both instances have bone transforms.
	if( p_mat1 == p_mat2 )
	{
		int num_st1 = p_mesh1->GetScene()->m_num_filled_meshes - p_mesh1->GetScene()->m_num_opaque_meshes;
		int num_st2 = p_mesh2->GetScene()->m_num_filled_meshes - p_mesh2->GetScene()->m_num_opaque_meshes;
	
		if( num_st1 == num_st2 )
			return 0;
	
		return ( num_st1 > num_st2 ) ? 1 : -1;
	}

	return((uint32)p_mat1 > (uint32)p_mat2 ) ? 1 : -1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_instance( CInstance *p_instance, uint32 flags )
{
//	// Seed the static pointer off to NULL, otherwise if there is only one object with bone transforms, it will never update.
//	pLastBoneTransforms = NULL;
//
//	if( p_instance->GetActive())
//	{
//		// Check whether this instance is visible.
//		set_frustum_bbox_transform( p_instance->GetTransform());
//		float sphere[4];
//		sphere[0] = p_instance->GetScene()->m_sphere_center.x;
//		sphere[1] = p_instance->GetScene()->m_sphere_center.y;
//		sphere[2] = p_instance->GetScene()->m_sphere_center.z;
//		sphere[3] = p_instance->GetScene()->m_sphere_radius;
//		if( frustum_check_sphere( sphere ))
//		{
//			if( !TestSphereAgainstOccluders( (Mth::Vector*)&p_instance->GetScene()->m_sphere_center, p_instance->GetScene()->m_sphere_radius ))
//			{
//				p_instance->Render( flags | vRENDER_OPAQUE | vRENDER_SEMITRANSPARENT );
//
////				// Restore world transform to identity.
////				D3DDevice_SetTransform( D3DTS_WORLD, &EngineGlobals.world_matrix );
////				set_frustum_bbox_transform( NULL );
//			}
//		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_instances( uint32 flags )
{
	int save_g_object = g_object;

	if( current_index > 0 )
	{
		int i = 0;

//		Mth::Matrix *pLast = NULL;
//		ROMtx bone_mtx[80];

		for( ; i < current_index; ++i )
		{
#ifndef __NOPT_FINAL__
			g_object = p_instances[i]->m_object_num;
#endif		// __NOPT_FINAL__
//			if ( ( flags & NxNgc::vRENDER_TRANSFORM ) && ( p_instances[i]->GetFlags() & NxNgc::CInstance::INSTANCE_FLAG_TRANSFORM_ME ) )
//			{
////				p_instances[i]->Transform( 0 /*flags*/ | vRENDER_OPAQUE | vRENDER_SEMITRANSPARENT, bone_mtx, pLast );
////				pLast = p_instances[i]->GetBoneTransforms();
//				p_instances[i]->ClearFlag( NxNgc::CInstance::INSTANCE_FLAG_TRANSFORM_ME );
//			}

			if(( p_instances[i]->GetBoneTransforms() == NULL ) || ( p_instances[i]->GetScene()->m_numHierarchyObjects > 0 ))
			{
				break;
			}

			if(( flags & vRENDER_OPAQUE ) || (( flags & vRENDER_SEMITRANSPARENT ) && ( flags & vRENDER_INSTANCE_PRE_WORLD_SEMITRANSPARENT )))
			{
				p_instances[i]->Render( flags );
//				p_instances[i]->SetFlag( NxNgc::CInstance::INSTANCE_FLAG_TRANSFORM_ME );
			}
		}

		for( ; i < current_index; ++i )
		{
#ifndef __NOPT_FINAL__
			g_object = p_instances[i]->m_object_num;
#endif		// __NOPT_FINAL__

//			if(( p_instances[i]->GetBoneTransforms()) && ( p_instances[i]->GetScene()->m_numHierarchyObjects == 0 ))
//				break;

			if(( flags & vRENDER_OPAQUE ) || (( flags & vRENDER_SEMITRANSPARENT ) && ( flags & vRENDER_INSTANCE_POST_WORLD_SEMITRANSPARENT )))
			{
				p_instances[i]->Render( flags );
			}
		}
	}
	g_object = save_g_object;
}

void CInstance::Transform( uint32 flags, ROMtx * p_mtx_buffer, Mth::Matrix *p_mtx_last )
{
//#	ifdef __USE_PROFILER__
////	Sys::VUProfiler->PushContext( 128,0,0 );
//#	endif		// __USE_PROFILER__

//	if ( gNewRender ) render_flags |= NxNgc::vRENDER_NEW_TEST;

	if( GetBoneTransforms() && ( GetScene()->m_numHierarchyObjects == 0 ) )
	{
		// Has bone transforms, is therefore an animated object such as a skater, pedestrian etc.
		mp_posNormRenderBuffer = GetPosNormalBuffer( 0 );

		if ( !p_mtx_last || ( GetBoneTransforms() != p_mtx_last ) )
		{
			float * ps;
			float * pd;
			for( int i = 0; i < GetNumBones(); ++i )
			{
				ps = (float*)&GetBoneTransforms()[i];
				pd = (float*)&p_mtx_buffer[i][0][0];

				*pd++ = *ps++;
				*pd++ = *ps++;
				*pd++ = *ps++;
				ps++;
				*pd++ = *ps++;
				*pd++ = *ps++;
				*pd++ = *ps++;
				ps++;
				*pd++ = *ps++;
				*pd++ = *ps++;
				*pd++ = *ps++;
				ps++;
				*pd++ = *ps++;
				*pd++ = *ps++;
				*pd++ = *ps++;
			}
		}

		// Setup some required pointers for both transforms.
//		sMesh*	p_mesh = GetScene()->mpp_mesh_list[0];
//		if ( GetScene()->m_num_opaque_entries )
//		{
//			p_mesh = GetScene()->m_opaque_meshes[0];
//		}
//		else if ( GetScene()->m_num_semitransparent_entries )
//		{
//			p_mesh = GetScene()->m_semitransparent_meshes[0];
//		}
//		else
//		{
//			Dbg_MsgAssert(0, ("No meshes in scene."));
//			p_mesh = NULL;
//		}

		// Transform the single lists.
		uint32*	p32 = (uint32 *)GetScene()->mp_dl->mp_object_header->m_skin.p_data;
		s16*	p_pos = mp_posNormRenderBuffer;
		for ( uint32 lp = 0; lp < GetScene()->mp_dl->mp_object_header->m_num_single_lists; lp++ ) {
			uint32		pairs = *p32++;
			uint32		mtx = *p32++;
			s16*		p_pn = (s16 *)p32;
			ROMtxPtr	p_bone = p_mtx_buffer[mtx];
	
			TransformSingle( p_bone, p_pn, p_pos, pairs == 1 ? 2 : pairs ); 
			p_pos += (6 * pairs);
			p32 = (uint32 *)&p_pn[pairs*6];
		}

		// Transform the double lists.
		for ( uint32 lp = 0; lp < GetScene()->mp_dl->mp_object_header->m_num_double_lists; lp++ ) {
			uint32		pairs = *p32++;
			uint32		mtx = *p32++;
			s16*		p_pn = (s16 *)p32;
			ROMtxPtr	p_bone0 = p_mtx_buffer[mtx&255];
			ROMtxPtr	p_bone1 = p_mtx_buffer[(mtx>>8)&255];
			s16*		p_weight = (s16 *)&p_pn[(6*pairs)];
	
			TransformDouble( p_bone0, p_bone1, p_weight, p_pn, p_pos, pairs == 1 ? 2 : pairs ); 
			p_pos += (6 * pairs);
			p32 = (uint32 *)&p_weight[pairs*2];
		}

		// Transform the acc lists.
		for ( uint32 lp = 0; lp < GetScene()->mp_dl->mp_object_header->m_num_add_lists; lp++ ) {
			uint32		singles = *p32++;
			uint32		mtx = *p32++;
			s16*		p_pn = (s16 *)p32;
			ROMtxPtr	p_bone = p_mtx_buffer[mtx];
			s16*		p_weight = (s16 *)&p_pn[(6*singles)];
			uint16*		p_index = (uint16 *)&p_weight[singles];
			
			TransformAcc( p_bone, singles, p_pn, mp_posNormRenderBuffer, p_index, p_weight ); 
//			p32 = (uint32 *)&p_index[singles+(singles&1)];
			p32 = (uint32 *)&p_index[singles];
		}



//		int num_vertex;
//		if ( mp_scene->m_num_opaque_entries )
//		{
//			num_vertex = mp_scene->m_opaque_meshes[0]->m_num_vertex;
//		}
//		else if ( mp_scene->m_num_semitransparent_entries )
//		{
//			num_vertex = mp_scene->m_semitransparent_meshes[0]->m_num_vertex;
//		}
//		else
//		{
//			Dbg_MsgAssert(0, ("No meshes in scene."));
//			num_vertex = 0;
//		}
//
		int num_vertex = GetScene()->mp_dl->mp_object_header->m_num_skin_verts;
		DCFlushRange ( mp_posNormRenderBuffer, sizeof ( s16 ) * 6 * ( num_vertex + 1 ) );
	}
//#	ifdef __USE_PROFILER__
////	Sys::VUProfiler->PopContext(  );
//#	endif		// __USE_PROFILER__
}



void CInstance::Render( uint32 flags )
{
	uint32 render_flags = flags;
//	if ( gNewRender ) render_flags |= NxNgc::vRENDER_NEW_TEST;

	NsMatrix	root;

	root.setRightX( GetTransform()->GetRight()[X] );
	root.setRightY( GetTransform()->GetUp()[X] );
	root.setRightZ( GetTransform()->GetAt()[X] );

	root.setUpX( GetTransform()->GetRight()[Y] ); 
	root.setUpY( GetTransform()->GetUp()[Y] );    
	root.setUpZ( GetTransform()->GetAt()[Y] );    

	root.setAtX( GetTransform()->GetRight()[Z] ); 
	root.setAtY( GetTransform()->GetUp()[Z] );    
	root.setAtZ( GetTransform()->GetAt()[Z] );    

	root.setPosX( GetTransform()->GetPos()[X] );
	root.setPosY( GetTransform()->GetPos()[Y] );
	root.setPosZ( GetTransform()->GetPos()[Z] );

//	Mth::Vector world_sphere(GetScene()->m_sphere_center.x,GetScene()->m_sphere_center.y,GetScene()->m_sphere_center.z);
//	world_sphere[W] = 1.0f;
//	world_sphere *= *GetTransform();							// => use mp_transform
//	if (TestSphereAgainstOccluders( &world_sphere, GetScene()->m_sphere_radius ))
//	{
//		return;
//	}

	// Create & upload current view matrix.
	NsMatrix	tr;
	tr.cat( EngineGlobals.world_to_camera, root );
	GX::LoadPosMtxImm( (MtxPtr)&tr, GX_PNMTX0 );

	// Need to force the current matrix to be just incase hierarchical models change it to 1.
	if ( render_flags & vRENDER_SHADOW_1ST_PASS )
	{
		GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );
	}

//	if ( !( render_flags & vRENDER_SHADOW_1ST_PASS ) )
	{
		//NsMatrix inv;
		//inv.invert( root );
		//GX::LoadNrmMtxImm((MtxPtr)&inv, GX_PNMTX0);
//		tr.cat( EngineGlobals.camera, root );
//		tr.invert( root/*EngineGlobals.world_to_camera*/ );
	
		Nx::CModelLights *p_lights = GetModel() ? GetModel()->GetModelLights() : NULL;
		if( p_lights )
		{
			p_lights->UpdateEngine( GetTransform()->GetPos(), true );
		}
		else
		{
			Nx::CLightManager::sUpdateEngine();
		}
	}
	MTXCopy ( (MtxPtr)&tr, EngineGlobals.current_uploaded );

	EngineGlobals.local_to_camera.copy( tr );

	EngineGlobals.object_pos.x = tr.getPosX();
	EngineGlobals.object_pos.y = tr.getPosY();
	EngineGlobals.object_pos.z = tr.getPosZ();




#ifdef DEBUG_SPHERES
	if( GetBoneTransforms() && ( GetScene()->m_numHierarchyObjects == 0 ) )
	{
		Mth::Vector sphere = GetScene()->mpp_mesh_list[0]->mp_dl->mp_object_header->m_sphere;
		debug_render_sphere( sphere, (GXColor){255,0,0,255} );
	}
	else
	{
		if ( GetScene()->m_numHierarchyObjects && GetBoneTransforms() )
		{
			// Car or something...
			Mth::Vector sphere = GetScene()->m_sphere;	//mpp_mesh_list[0]->mp_dl->mp_object_header->m_sphere;
			debug_render_sphere( sphere, (GXColor){0,255,0,255} );
		}
		else
		{
			// No bones, just render normally. Must be a trashcan or something.
			Mth::Vector sphere = GetScene()->mpp_mesh_list[0]->mp_dl->mp_object_header->m_sphere;
			debug_render_sphere( sphere, (GXColor){0,0,255,255} );
		}
	}
#endif		// DEBUG_SPHERES





	GXLightObj light_obj[3];

	// Always set ambient & diffuse colors.
	GX::SetChanAmbColor( GX_COLOR0A0, NxNgc::EngineGlobals.ambient_light_color );
//		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){128,128,128,255} );
//				GX::SetChanAmbColor( GX_COLOR1A1, NxNgc::EngineGlobals.ambient_light_color );

	GX::InitLightColor( &light_obj[0], NxNgc::EngineGlobals.diffuse_light_color[0] );
	GX::InitLightPos( &light_obj[0], NxNgc::EngineGlobals.light_x[0], NxNgc::EngineGlobals.light_y[0], NxNgc::EngineGlobals.light_z[0] );
	GX::LoadLightObjImm( &light_obj[0], GX_LIGHT0 );

	GX::InitLightColor( &light_obj[1], NxNgc::EngineGlobals.diffuse_light_color[1] );
	GX::InitLightPos( &light_obj[1], NxNgc::EngineGlobals.light_x[1], NxNgc::EngineGlobals.light_y[1], NxNgc::EngineGlobals.light_z[1] );
	GX::LoadLightObjImm( &light_obj[1], GX_LIGHT1 );

	GX::InitLightColor( &light_obj[2], NxNgc::EngineGlobals.diffuse_light_color[2] );
	GX::InitLightPos( &light_obj[2], NxNgc::EngineGlobals.light_x[2], NxNgc::EngineGlobals.light_y[2], NxNgc::EngineGlobals.light_z[2] );
	GX::LoadLightObjImm( &light_obj[2], GX_LIGHT2 );

	// Set channel control for diffuse.
	GX::SetChanCtrl( GX_COLOR0, GX_ENABLE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT0|GX_LIGHT1|GX_LIGHT2, GX_DF_CLAMP, GX_AF_NONE );
	GX::SetChanCtrl( GX_ALPHA0, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 







	if( GetBoneTransforms() && ( GetScene()->m_numHierarchyObjects == 0 ) )
	{
		// Has bone transforms, is therefore an animated object such as a skater, pedestrian etc.

//		if ( ( flags & vRENDER_SHADOW_1ST_PASS ) && GetScene()->mp_scene_data && GetScene()->mp_scene_data->m_num_shadow_faces && !g_in_cutscene )
//		{
//			f32 p_pm[GX_PROJECTION_SZ];
//			GX::GetProjectionv( p_pm );
//			float value = p_pm[6] + ( (float)(-g_twiddle) ) * p_pm[5];
//
//			GXWGFifo.u8 = GX_LOAD_XF_REG;
//			GXWGFifo.u16 = 0;
//			GXWGFifo.u16 = 0x1025;
//			GXWGFifo.f32 = value;
//
//
//			GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_FRONT ); 
//			GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );
//			GX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0 );
//
//			GX::SetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0,
//										   GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0 );
//
//			GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0,	GX_CA_KONST, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO,
//													GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV,
//													GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetTevColorInOp( GX_TEVSTAGE0,		GX_CC_KONST, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
//													GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//
//			GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
//
//			GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//
////			GX::SetZMode( GX_TRUE, GX_GREATER, GX_FALSE );
////			GX::SetZMode( GX_TRUE, GX_GREATER, GX_FALSE );
//			GX::SetZMode(GX_TRUE, GX_LEQUAL, GX_FALSE);
//
//			GX::SetTevKColor( GX_KCOLOR0, (GXColor){4,4,4,4} );
//			GX::SetTevKSel( GX_TEVSTAGE0, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A );
//
////			if ( !g_in_cutscene ) GX::SetClipMode( GX_CLIP_DISABLE );
//			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//
//
//			float px = 0.0f;	//NxNgc::EngineGlobals.skater_shadow_dir.x;
//			float py = -1.0f;    //NxNgc::EngineGlobals.skater_shadow_dir.y * 3.0f;
//			float pz = 0.0f;    //NxNgc::EngineGlobals.skater_shadow_dir.z;
//
//			float l = sqrtf( ( px * px ) + ( py * py ) + ( pz * pz ) );
//
//			px /= l;
//			py /= l;
//			pz /= l;
//
////#define NEAR_Z 50.0f
////#define FAR_Z 120.0f
////
////#define MIN_LEN 10.0f
////#define MAX_LEN 100.0f
//
//			// Shorten length if:
//			// 1. light vector & object vector are opposing.
//			// AND
//			// 2. camera is not above player.
//			// AND
//			// 3. camera is close to the object.
//			float base_length = MAX_LEN;
//
////			float cx = NxNgc::EngineGlobals.camera.getPosX() - GetTransform()->GetPos()[X];
////			float cy = NxNgc::EngineGlobals.camera.getPosY() - GetTransform()->GetPos()[Y];
////			float cz = NxNgc::EngineGlobals.camera.getPosZ() - GetTransform()->GetPos()[Z];
//
////			float obj_len = sqrtf( ( cx * cx ) + /*( cy * cy ) +*/ ( cz * cz ) );
//
////
////			// Fabricate a worst-case vector.
////			// 1st, pick a point at the head of the skater.
////			float wx = 0.0f;
////			float wy = 12.0f * 7.0f;		// Assuming 7ft skater.
////			float wz = 0.0f;
////
////			// 2nd, move towards camera by average value.
////			wx += ( cx / obj_len ) * ( 12.0f * 3.0f );
////			wy += ( cy / obj_len ) * ( 12.0f * 3.0f );
////			wz += ( cz / obj_len ) * ( 12.0f * 3.0f );
////
//			
////			float cam_dir_dot = ( px * cx ) + /*( py * cy ) +*/ ( pz * cz );
////
//////			OSReport( "Object len: %8.3f\n", obj_len );
////
////			if ( ( ( cam_dir_dot > -0.0f ) || ( obj_len <= NEAR_Z ) ) && ( cy < 100.0f ) )
////			{
////				// Opposing, get length.
////				if ( obj_len < FAR_Z )
////				{
////					if ( obj_len > NEAR_Z )
////					{
////						// Close enough, adjust base length.
////						base_length = MIN_LEN + ( ( ( obj_len - NEAR_Z ) * ( MAX_LEN - MIN_LEN ) ) / ( FAR_Z - NEAR_Z ) );
////					}
////					else
////					{
////						// Close enough, adjust base length.
////						base_length = MIN_LEN;
////					}
////				}
////			}
////
//			float length = NxNgc::EngineGlobals.skater_height + base_length;
//
//			root.setRightX( GetTransform()->GetRight()[X] );
//			root.setRightY( GetTransform()->GetRight()[Y] );
//			root.setRightZ( GetTransform()->GetRight()[Z] );
//
//			root.setUpX( GetTransform()->GetUp()[X] );
//			root.setUpY( GetTransform()->GetUp()[Y] );
//			root.setUpZ( GetTransform()->GetUp()[Z] );
//
//			root.setAtX( GetTransform()->GetAt()[X] );
//			root.setAtY( GetTransform()->GetAt()[Y] );
//			root.setAtZ( GetTransform()->GetAt()[Z] );
//
//			root.setPosX( 0.0f );
//			root.setPosY( 0.0f );
//			root.setPosZ( 0.0f );
//
//			NsVector in, out;
//
//			in.x = px;
//			in.y = py;
//			in.z = pz;
//			root.multiply( &in, &out );
//			px = out.x;
//			py = out.y;
//			pz = out.z;
//
//			float tx = px * g_tweak;
//			float ty = py * g_tweak;
//			float tz = pz * g_tweak;
//
//			px *= length;
//			py *= length;
//			pz *= length;
//
//			int num_shadow_faces = GetScene()->mp_scene_data->m_num_shadow_faces;
//
//			float p_dot[4096];
//			CalculateDotProducts( mp_posNormRenderBuffer, GetScene()->mp_shadow_volume_mesh, p_dot, num_shadow_faces, px, py, pz );
//#if 1
//			RenderShadows( mp_posNormRenderBuffer, GetScene()->mp_shadow_volume_mesh, GetScene()->mp_shadow_edge, p_dot, num_shadow_faces, px, py, pz, tx, ty, tz );
//#else
//			float * p_xyz = new float[mp_scene->mp_dl->mp_object_header->m_num_skin_verts*3];
//			ConvertFixed196ToFloat( mp_posNormRenderBuffer, p_xyz, mp_scene->mp_dl->mp_object_header->m_num_skin_verts );
//
//			for ( uint lp = 0; lp < GetScene()->mp_scene_data->m_num_shadow_faces; lp++ )
//			{
//				float dot = p_dot[lp];
//
//				if ( dot > 0 )
//				{
////					// See if we have to draw all sides.
////					// Check 01.
//					int shape = 0;
//					if ( ( GetScene()->mp_shadow_edge[lp].neighbor[0] == -1 ) || ( p_dot[ GetScene()->mp_shadow_edge[lp].neighbor[0] ] <= 0 ) )
//					{
//						shape |= (1<<0);
//					}
//					// Check 12.
//					if ( ( GetScene()->mp_shadow_edge[lp].neighbor[1] == -1 ) || ( p_dot[ GetScene()->mp_shadow_edge[lp].neighbor[1] ] <= 0 ) )
//					{
//						shape |= (1<<1);
//					}
//					// Check 23.
//					if ( ( GetScene()->mp_shadow_edge[lp].neighbor[2] == -1 ) || ( p_dot[ GetScene()->mp_shadow_edge[lp].neighbor[2] ] <= 0 ) )
//					{
//						shape |= (1<<2);
//					}
//
//					unsigned short idx0 = GetScene()->mp_shadow_volume_mesh[(lp*3)+0];
//					unsigned short idx1 = GetScene()->mp_shadow_volume_mesh[(lp*3)+1];
//					unsigned short idx2 = GetScene()->mp_shadow_volume_mesh[(lp*3)+2];
//
//					float x0 = p_xyz[(idx0*3)+0];
//					float y0 = p_xyz[(idx0*3)+1];
//					float z0 = p_xyz[(idx0*3)+2];
//
//					float x1 = p_xyz[(idx1*3)+0];
//					float y1 = p_xyz[(idx1*3)+1];
//					float z1 = p_xyz[(idx1*3)+2];
//
//					float x2 = p_xyz[(idx2*3)+0];
//					float y2 = p_xyz[(idx2*3)+1];
//					float z2 = p_xyz[(idx2*3)+2];
//
//					float x3 = x0 + px;
//					float y3 = y0 + py;
//					float z3 = z0 + pz;
//	
//					float x4 = x1 + px;
//					float y4 = y1 + py;
//					float z4 = z1 + pz;
//	
//					float x5 = x2 + px;
//					float y5 = y2 + py;
//					float z5 = z2 + pz;
//
//					GX::SetBlendMode ( GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_FALSE, GX_TRUE, GX_FALSE );
//
//					switch ( shape )
//					{
//						case 0:
//							GX::Begin( GX_TRIANGLES, GX_VTXFMT0, 3 ); 
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::End();
//							GX::Begin( GX_TRIANGLES, GX_VTXFMT0, 3 ); 
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::End();
//							break;
//						case 1:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 6 ); 
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::End();
//							break;
//						case 2:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 6 ); 
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::End();
//							break;
//						case 3:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 8 ); 
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::End();
//							break;
//						case 4:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 6 ); 
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::End();
//							break;
//						case 5:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 8 ); 
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::End();
//							break;
//						case 6:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 8 ); 
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::End();
//							break;
//						case 7:
//						default:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 10 ); 
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::End();
//							break;
//					}
//					GX::SetBlendMode ( GX_BM_SUBTRACT, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_FALSE, GX_TRUE, GX_FALSE );
//
//					switch ( shape )
//					{
//						case 0:
//							GX::Begin( GX_TRIANGLES, GX_VTXFMT0, 3 ); 
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::End();
//							GX::Begin( GX_TRIANGLES, GX_VTXFMT0, 3 ); 
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::End();
//							break;
//						case 1:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 6 ); 
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::End();
//							break;
//						case 2:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 6 ); 
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::End();
//							break;
//						case 3:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 8 ); 
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::End();
//							break;
//						case 4:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 6 ); 
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::End();
//							break;
//						case 5:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 8 ); 
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::End();
//							break;
//						case 6:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 8 ); 
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::End();
//							break;
//						case 7:
//						default:
//							GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 10 ); 
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::Position3f32( x1, y1, z1 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x4, y4, z4 );
//							GX::Position3f32( x3, y3, z3 );
//							GX::Position3f32( x5, y5, z5 );
//							GX::Position3f32( x0, y0, z0 );
//							GX::Position3f32( x2, y2, z2 );
//							GX::End();
//							break;
//					}
//				}
//			}
//			delete p_xyz;
//#endif
//			GX::SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
////			if ( !g_in_cutscene ) GX::SetClipMode( GX_CLIP_ENABLE );
//			GX::SetProjectionv( p_pm );
//		}
//		else
		{
			GX::LoadNrmMtxImm((MtxPtr)&root, GX_PNMTX0);
			// Render the scene & see if any meshes were drawn. If so, transform the
			// vertex buffer for use when the GPU comes to actually render it..
			NxNgc::EngineGlobals.poly_culling = false;

			make_scene_visible( GetScene() );
			GX::SetVtxAttrFmt( GX_VTXFMT2, GX_VA_POS, GX_POS_XYZ, GX_S16, 6 );
			GX::SetVtxAttrFmt( GX_VTXFMT3, GX_VA_POS, GX_POS_XYZ, GX_S16, 6 );
			GX::SetVtxAttrFmt( GX_VTXFMT4, GX_VA_POS, GX_POS_XYZ, GX_S16, 6 );
			GX::SetVtxAttrFmt( GX_VTXFMT5, GX_VA_POS, GX_POS_XYZ, GX_S16, 6 );
			GX::SetVtxAttrFmt( GX_VTXFMT6, GX_VA_POS, GX_POS_XYZ, GX_S16, 6 );
			GX::SetVtxAttrFmt( GX_VTXFMT7, GX_VA_POS, GX_POS_XYZ, GX_S16, 6 );
			render_scene( GetScene(), mp_posNormRenderBuffer, render_flags | vRENDER_NO_CULLING );
			GX::SetVtxAttrFmt( GX_VTXFMT2, GX_VA_POS, GX_POS_XYZ, GX_F32, 0 );
			GX::SetVtxAttrFmt( GX_VTXFMT3, GX_VA_POS, GX_POS_XYZ, GX_F32, 0 );
			GX::SetVtxAttrFmt( GX_VTXFMT4, GX_VA_POS, GX_POS_XYZ, GX_F32, 0 );
			GX::SetVtxAttrFmt( GX_VTXFMT5, GX_VA_POS, GX_POS_XYZ, GX_F32, 0 );
			GX::SetVtxAttrFmt( GX_VTXFMT6, GX_VA_POS, GX_POS_XYZ, GX_F32, 0 );
			GX::SetVtxAttrFmt( GX_VTXFMT7, GX_VA_POS, GX_POS_XYZ, GX_F32, 0 );
		}
	}
	else
	{
		if( ( m_flags & CInstance::INSTANCE_FLAG_LIGHTING_ALLOWED ) == 0 ) render_flags |= NxNgc::vRENDER_LIT;
		GX::LoadNrmMtxImm((MtxPtr)&tr, GX_PNMTX0);
		if ( GetScene()->m_numHierarchyObjects && GetBoneTransforms() )
		{
			// Car or something...
			NxNgc::EngineGlobals.poly_culling = true;
			make_scene_visible( GetScene() );
			render_scene( GetScene(), NULL, render_flags | vRENDER_NO_CULLING, GetBoneTransforms(), GetTransform(), GetNumBones() );
		}
		else
		{
			if( ( m_flags & CInstance::INSTANCE_FLAG_LIGHTING_ALLOWED ) == 0 ) render_flags |= NxNgc::vRENDER_LIT;
			// No bones, just render normally. Must be a trashcan or something.
			NxNgc::EngineGlobals.poly_culling = false;
			make_scene_visible( GetScene() );
//			int meshes_rendered = cull_scene( GetScene(), vRENDER_OCCLUDED, GetTransform() ); 
			render_scene( GetScene(), NULL, render_flags );
//			SetMeshesRendered( meshes_rendered );
		}
	}
}



void process_instances( void )
{
	current_index = 0;
	
	if ( g_mc_hack ) return;

	// Seed the static pointer off to NULL, otherwise if there is only one object with bone transforms, it will never update.
	pLastBoneTransforms = NULL;
	
	// First go through and build a list of the visible instances.
	CInstance *p_instance = pFirstInstance;
	while( p_instance )
	{
#ifndef __NOPT_FINAL__
		p_instance->m_object_num = g_object;
		g_object++;
#endif		// __NOPT_FINAL__
		if( p_instance->GetActive() && p_instance->GetScene()->mp_scene_data )
		{
			p_instances[current_index++] = p_instance;
		}
		p_instance = p_instance->GetNextInstance();
	}

	int save_g_object = g_object;

	// Now sort the list based on bone transform and number of semitransparent objects in scene.
	if( current_index > 0 )
	{
		if( current_index > 1 ) qsort( p_instances, current_index, sizeof( CInstance* ), sort_by_bone_transform );
	
		int i = 0;
		Mth::Matrix *pLast = NULL;
		ROMtx bone_mtx[60];

		for( ; i < current_index; ++i )
		{
#ifndef __NOPT_FINAL__
			g_object = p_instances[i]->m_object_num;
#endif		// __NOPT_FINAL__

			if(( p_instances[i]->GetBoneTransforms() == NULL ) || ( p_instances[i]->GetScene()->m_numHierarchyObjects > 0 ))
				break;

			p_instances[i]->Transform( 0 /*flags*/ | vRENDER_OPAQUE | vRENDER_SEMITRANSPARENT, bone_mtx, pLast );
			pLast = p_instances[i]->GetBoneTransforms();
		}

//		for( ; i < current_index; ++i )
//		{
//#ifndef __NOPT_FINAL__
//			g_object = p_instances[i]->m_object_num;
//#endif		// __NOPT_FINAL__
//
//			p_instances[i]->Transform( 0 /*flags*/ | vRENDER_OPAQUE | vRENDER_SEMITRANSPARENT, NULL, NULL );
//		}
	}
	g_object = save_g_object;
}



s16	*CInstance::GetPosNormalBuffer( int buffer )
{
	int num_vertex = mp_scene->mp_dl->mp_object_header->m_num_skin_verts;
	Dbg_MsgAssert(num_vertex, ("No verts in scene."));

	s16 * rv = (s16*)NsBuffer::alloc( sizeof(s16) * 6 * (num_vertex+1) ); 
	return rv;
}

CInstance::CInstance( sScene *pScene, Mth::Matrix &transform, int numBones, Mth::Matrix *pBoneTransforms )
{
	SetTransform( transform );
	mp_bone_transforms	= pBoneTransforms;
	m_num_bones			= numBones;
	mp_scene			= pScene;
	m_active			= true;
	m_flags				= 0;
	mp_model			= NULL;

	mp_next_instance	= pFirstInstance;
	pFirstInstance		= this;

	mp_posNormBuffer = NULL;
	m_visibility_mask = 0xff;

	// Check to see whether this instance is allowed to be lit or not (for non-skinned instances).
	// Currently, we assume that instances with a model containing valid normals requires lighting, except in the
	// situation where that model contains a mesh specifically flagged not to be lit.
	if(( pBoneTransforms == NULL ) || ( GetScene()->m_numHierarchyObjects > 0 ))
	{
		for( int m = 0; m < pScene->m_num_filled_meshes; ++m )
		{
			NxNgc::sMesh *p_mesh = pScene->mpp_mesh_list[m];
			if( !pScene->mp_scene_data->m_num_nrm )
				return;
			if( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<7) )
				return;
		}
		m_flags |= CInstance::INSTANCE_FLAG_LIGHTING_ALLOWED;
	}
}




CInstance::~CInstance()
{
	if( m_flags & INSTANCE_FLAG_DELETE_ATTACHED_SCENE )
	{
		if( mp_scene )
		{
			delete mp_scene;
			mp_scene = NULL;
		}
	}
	
//	if ( mp_posNormBuffer )
//	{
//		delete  mp_posNormBuffer;
//		mp_posNormBuffer = NULL;
//	}

	// Remove this instance from the list.
	CInstance **pp_instance = &pFirstInstance;
	while( *pp_instance )
	{
		if( *pp_instance == this )
		{
			*pp_instance = mp_next_instance;
			break;
		}
		else
		{
			pp_instance = &(( *pp_instance )->mp_next_instance );
		}
	}
}

} // namespace NxNgc









================================================
FILE: Code/Gfx/NGC/NX/instance.h
================================================
#ifndef __INSTANCE_H
#define __INSTANCE_H


#include 
#include 
#include "scene.h"

namespace NxNgc
{

class CInstance
{
public:
	enum EInstanceFlag
	{
		INSTANCE_FLAG_DELETE_ATTACHED_SCENE	= 0x01,
		INSTANCE_FLAG_TRANSFORM_ME			= 0x02,
		INSTANCE_FLAG_LIGHTING_ALLOWED		= 0x04

	};
	
	void		SetTransform( Mth::Matrix &transform )	{ m_transform = transform; }
	Mth::Matrix	*GetTransform(void)						{ return &m_transform; }
	int			GetNumBones( void )						{ return m_num_bones; }
	Mth::Matrix	*GetBoneTransforms( void )				{ return mp_bone_transforms; }
	void		SetBoneTransforms( Mth::Matrix * p )	{ mp_bone_transforms = p; }
	sScene		*GetScene( void )						{ return mp_scene; }
	void		SetActive( bool active )				{ m_active = (uint16)active; }
	bool		GetActive( void )						{ return (bool)m_active; }
	void		SetFlag( EInstanceFlag flag )			{ m_flags |= flag; }
	uint32		GetFlags( void )						{ return m_flags; }
	void		ClearFlag( EInstanceFlag flag )			{ m_flags &= ~flag; }
	s16			*GetPosNormalBuffer( int buffer );
	void		SetVisibility( uint32 mask )			{ m_visibility_mask	= mask; }
	uint32		GetVisibility( void )					{ return m_visibility_mask; }
	CInstance	*GetNextInstance( void )				{ return mp_next_instance; }

	void		SetModel( Nx::CModel *p_model )			{ mp_model = p_model; }
	Nx::CModel	*GetModel( void )						{ return mp_model; }

	int			GetMeshesRendered( void )				{ return m_meshes_rendered; }
	void		SetMeshesRendered( int n )				{ m_meshes_rendered = n; }

	CInstance( sScene *pScene, Mth::Matrix &transform, int numBones, Mth::Matrix *pBoneTransforms );
	~CInstance();

	void		Transform( uint32 flags, ROMtx * p_mtx_buffer, Mth::Matrix *p_mtx_last );
	void		Render( uint32 flags );

	s16*		GetPosNormRenderBuffer() { return mp_posNormRenderBuffer; }
	void		SetPosNormRenderBuffer( s16* p ) { mp_posNormRenderBuffer = p; }

	#ifndef __NOPT_FINAL__
	int			m_object_num;
	#endif		// __NOPT_FINAL__

private:
	uint32		m_flags;
	Mth::Matrix	m_transform;
	Mth::Matrix	*mp_bone_transforms;
	int			m_num_bones;
	uint16		m_active;
	uint16		m_meshes_rendered;
	sScene		*mp_scene;

	uint32		m_visibility_mask;

	Nx::CModel	*mp_model;		// Required in order to get pointer to CXboxLights structure at render time.

	s16*		mp_posNormBuffer;
	s16*		mp_posNormRenderBuffer;

	CInstance	*mp_next_instance;
};


void	InitialiseInstanceTable( void );
void	render_instance( CInstance *p_instance, uint32 flags );
void	render_instances( uint32 flags );
void	process_instances( void );


} // namespace NxNgc


#endif // __INSTANCE_H



================================================
FILE: Code/Gfx/NGC/NX/light.cpp
================================================
#include 
#include "light.h"

namespace NxNgc
{


// Statics
CLightGroup		CLightGroup::s_default_lights;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CLightGroup::CLightGroup()
{
	// Don't use new lights unless someone sets them
	m_flags = 0;

	// Init to default lights
	m_light_vector = s_default_lights.m_light_vector;

	m_diffuse_base_color = s_default_lights.m_diffuse_base_color;
	m_diffuse_mod_color = s_default_lights.m_diffuse_base_color;

	m_ambient_base_color = s_default_lights.m_ambient_base_color;
	m_ambient_mod_color = s_default_lights.m_ambient_base_color;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CLightGroup::~CLightGroup()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &		CLightGroup::GetDirection(int idx) const
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	if (m_flags & (mUSE_DIFFUSE_0 << idx))
	{
		return m_light_vector[idx];
	} else {
		return s_default_lights.m_light_vector[idx];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &		CLightGroup::GetBaseAmbientColor() const
{
	if (m_flags & mUSE_AMBIENT)
	{
		return m_ambient_base_color;
	} else {
		return s_default_lights.m_ambient_base_color;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &		CLightGroup::GetBaseDiffuseColor(int idx) const
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	if (m_flags & (mUSE_DIFFUSE_0 << idx))
	{
		return m_diffuse_base_color[idx];
	} else {
		return s_default_lights.m_diffuse_base_color[idx];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &		CLightGroup::GetAmbientColor() const
{
	if (m_flags & (mUSE_AMBIENT | mMODULATING_AMBIENT))
	{
		return m_ambient_mod_color;
	} else {
		return s_default_lights.m_ambient_base_color;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &		CLightGroup::GetDiffuseColor(int idx) const
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	if (m_flags & ((mUSE_DIFFUSE_0 | mMODULATING_DIFFUSE_0) << idx))
	{
		return m_diffuse_mod_color[idx];
	} else {
		return s_default_lights.m_diffuse_base_color[idx];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CLightGroup::SetDirection(int idx, const Mth::Vector & direction)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);
	//Dbg_Assert(m_flags & (mUSE_DIFFUSE_0 << idx));
	m_flags |= (mUSE_DIFFUSE_0 << idx);  // turn on light if it isn't already on

	m_light_vector[idx] = direction;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CLightGroup::SetBaseAmbientColor(const Mth::Vector & color)
{
	//Dbg_Assert(m_flags & mUSE_AMBIENT);
	m_flags |= mUSE_AMBIENT;		// turn on light if it isn't already on

	m_ambient_base_color = color;

	if (!(m_flags & mMODULATING_AMBIENT))
	{
		m_ambient_mod_color = color;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CLightGroup::SetBaseDiffuseColor(int idx, const Mth::Vector & color)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);
	//Dbg_Assert(m_flags & (mUSE_DIFFUSE_0 << idx));
	m_flags |= (mUSE_DIFFUSE_0 << idx);  // turn on light if it isn't already on

	m_diffuse_base_color[idx] = color;

	if (!(m_flags & (mMODULATING_DIFFUSE_0 << idx)))
	{
		m_diffuse_mod_color[idx] = color;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CLightGroup::EnableAmbientLight(bool enable)
{
	if (enable)
	{
		m_flags |= mUSE_AMBIENT;
	} else {
		m_flags &= ~mUSE_AMBIENT;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CLightGroup::EnableDiffuseLight(int idx, bool enable)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	if (enable)
	{
		m_flags |= (mUSE_DIFFUSE_0 << idx);
	} else {
		m_flags &= ~(mUSE_DIFFUSE_0 << idx);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool					CLightGroup::IsAmbientLightEnabled() const
{
	return (m_flags & mUSE_AMBIENT);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool					CLightGroup::IsDiffuseLightEnabled(int idx) const
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	return (m_flags & (mUSE_DIFFUSE_0 << idx));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool					CLightGroup::SetAmbientBrightness(float brightness)
{
	m_ambient_mod_color = GetBaseAmbientColor() * brightness;

	m_flags |= mMODULATING_AMBIENT;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool					CLightGroup::SetDiffuseBrightness(int idx, float brightness)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	m_diffuse_mod_color[idx] = GetBaseDiffuseColor(idx) * brightness;

	m_flags |= (mMODULATING_DIFFUSE_0 << idx);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &		CLightGroup::sGetDefaultDirection(int idx)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	return s_default_lights.m_light_vector[idx];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &		CLightGroup::sGetDefaultAmbientColor()
{
	return s_default_lights.m_ambient_base_color;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &		CLightGroup::sGetDefaultDiffuseColor(int idx)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	return s_default_lights.m_diffuse_base_color[idx];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CLightGroup::sSetDefaultDirection(int idx, const Mth::Vector & direction)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	s_default_lights.m_light_vector[idx] = direction;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CLightGroup::sSetDefaultAmbientColor(const Mth::Vector & color)
{
	s_default_lights.m_ambient_base_color = color;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void					CLightGroup::sSetDefaultDiffuseColor(int idx, const Mth::Vector & color)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	s_default_lights.m_diffuse_base_color[idx] = color;
}

} // namespace NxNgc




================================================
FILE: Code/Gfx/NGC/NX/light.h
================================================
#ifndef __LIGHT_H
#define __LIGHT_H

#include	
#include	

namespace NxNgc
{

// Forward declarations
class CLightGroup;

class CBaseLightGroup
{
public:
								CBaseLightGroup();
	virtual						~CBaseLightGroup();

	virtual const Mth::Vector &	GetDirection(int idx) const;

	virtual const Mth::Vector &	GetAmbientColor() const;
	virtual const Mth::Vector &	GetDiffuseColor(int idx) const;

protected:
	// Directional light vectors
	Mth::Vector					m_light_vector[2];

	// Light base colors
	Mth::Vector 				m_diffuse_base_color[2];
	Mth::Vector 				m_ambient_base_color;

	friend CLightGroup;
};

class CLightGroup/* : public CBaseLightGroup*/
{

public:
	// Constants
	enum
	{
		MAX_DIFFUSE_LIGHTS = 2,
	};

								CLightGroup();
								~CLightGroup();
	//virtual						~CLightGroup();

	const Mth::Vector &			GetDirection(int idx) const;
	//virtual const Mth::Vector &	GetDirection(int idx) const;

	const Mth::Vector &			GetBaseAmbientColor() const;
	const Mth::Vector &			GetBaseDiffuseColor(int idx) const;

	const Mth::Vector &			GetAmbientColor() const;
	const Mth::Vector &			GetDiffuseColor(int idx) const;
	//virtual const Mth::Vector &	GetAmbientColor() const;
	//virtual const Mth::Vector &	GetDiffuseColor(int idx) const;

	void						SetDirection(int idx, const Mth::Vector & direction);
	void						SetBaseAmbientColor(const Mth::Vector & color);
	void						SetBaseDiffuseColor(int idx, const Mth::Vector & color);

	// Enable lights
	void						EnableAmbientLight(bool enable);
	void						EnableDiffuseLight(int idx, bool enable);
	bool						IsAmbientLightEnabled() const;
	bool						IsDiffuseLightEnabled(int idx) const;

	// Set brightness of lights
	bool						SetAmbientBrightness(float brightness);
	bool						SetDiffuseBrightness(int idx, float brightness);

	// Default lights
	static const Mth::Vector &	sGetDefaultDirection(int idx);
	static const Mth::Vector &	sGetDefaultAmbientColor();
	static const Mth::Vector &	sGetDefaultDiffuseColor(int idx);

	static void					sSetDefaultDirection(int idx, const Mth::Vector & direction);
	static void					sSetDefaultAmbientColor(const Mth::Vector & color);
	static void					sSetDefaultDiffuseColor(int idx, const Mth::Vector & color);

protected:
	// Internal flags.
	enum {
		mUSE_AMBIENT				= 0x0001,					// Use ambient light
		mUSE_DIFFUSE_0				= 0x0002,					// Use diffuse light 0
		mUSE_DIFFUSE_1				= 0x0004,					// rest of diffuse must follow 0
		mMODULATING_AMBIENT			= 0x0008,					// setting ambient brightness
		mMODULATING_DIFFUSE_0		= 0x0010,					// setting diffuse brightness 0
		mMODULATING_DIFFUSE_1		= 0x0020,					// rest of diffuse must follow 0
	};

	uint32					m_flags;

	// Directional light vectors
	Mth::Vector				m_light_vector[2];

	// Light base colors
	Mth::Vector 			m_diffuse_base_color[2];
	Mth::Vector 			m_ambient_base_color;

	// Light modulated colors (modified base colors)
	Mth::Vector 			m_diffuse_mod_color[2];
	Mth::Vector 			m_ambient_mod_color;

	// Default lights
//	static Mth::Vector		s_default_directions[2];
//	static CBaseLightGroup	s_default_colors;
	static CLightGroup		s_default_lights;

}; // class CLightGroup



} // namespace NxNgc



#endif // __LIGHT_H




================================================
FILE: Code/Gfx/NGC/NX/line.cpp
================================================
#include "line.h"
#include "render.h"


namespace NxPs2
{


void BeginLines2D(uint32 rgba)
{
//	// dma tag
//	dma::BeginTag(dma::cnt, 0);
//
//	vu1::Buffer = vu1::Loc;
//
//	// GS context
//	gs::BeginPrim(ABS,0,0);
//	gs::Reg1(gs::ALPHA_1, PackALPHA(0,1,0,1,0));
//	gs::Reg1(gs::RGBAQ,   (uint64)rgba);
//	gs::EndPrim(0);
//
//	// unpack giftag
//	vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
//	gif::BeginTagImmediate(gs::XYZ2, 1, PACKED, LINE|ABE, 1, VU1_ADDR(GSPrim));
//
//	// begin unpack for coordinates
//	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
}



void DrawLine2D(float x0, float y0, float z0, float x1, float y1, float z1)
{
//	vif::StoreV4_32(0x6C00+(sint32)(x0*16),0x7200+(sint32)(y0*16),0xFFFFFF,0);
//	vif::StoreV4_32(0x6C00+(sint32)(x1*16),0x7200+(sint32)(y1*16),0xFFFFFF,0);
//
//	if (((dma::pLoc - gif::pTag)>>4) >= 250)
//	{
//		vif::EndUNPACK();
//		gif::EndTagImmediate(1,0);
//		vif::MSCAL(VU1_ADDR(Parser));
//		vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
//		gif::BeginTagImmediate(gs::XYZ2, 1, PACKED, LINE|ABE, 1, VU1_ADDR(GSPrim));
//		vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
//	}
//
}


void EndLines2D(void)
{
//	vif::EndUNPACK();
//	gif::EndTagImmediate(1,0);
//	vif::MSCAL(VU1_ADDR(Parser));
//	dma::EndTag();
}



void BeginLines3D(uint32 rgba)
{
//	// dma tag
//	dma::BeginTag(dma::cnt, 0);
//
//	vu1::Buffer = vu1::Loc;
//
//	// VU context
//	vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));
//	vu1::StoreVec(ViewportScale);		// VF10
//	vu1::StoreVec(ViewportOffset);		// VF11
//	vu1::StoreMat(WorldToFrustum);		// VF12-15
//	vu1::EndPrim(0);
//
//	// GS context
//	gs::BeginPrim(ABS,0,0);
//	gs::Reg1(gs::ALPHA_1, PackALPHA(0,1,0,1,0));
//	gs::Reg1(gs::RGBAQ,   (uint64)rgba);
//	gs::EndPrim(0);
//
//	// all lines will be rendered with simple culling
//	vif::ITOP(CULL);
//
//	// unpack giftag
//	vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
//	gif::BeginTagImmediate(gs::XYZ2, 1, PACKED, LINE|ABE, 1, VU1_ADDR(ProjLine));
//
//	// offset mode
//	vif::STMOD(1);
//
//	// begin unpack for coordinates
//	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
}


void DrawLine3D(float x0, float y0, float z0, float x1, float y1, float z1)
{
//	vif::StoreV4_32((sint32)(x0*256.0f), (sint32)(y0*256.0f), (sint32)(z0*256.0f), 0);
//	vif::StoreV4_32((sint32)(x1*256.0f), (sint32)(y1*256.0f), (sint32)(z1*256.0f), 0);
//
//	if (((dma::pLoc - gif::pTag)>>4) >= 250)
//	{
//		vif::EndUNPACK();
//		gif::EndTagImmediate(1,1);
//		vif::STMOD(0);
//		vif::MSCAL(VU1_ADDR(Parser));
//		vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
//		gif::BeginTagImmediate(gs::XYZ2, 1, PACKED, LINE|ABE, 1, VU1_ADDR(ProjLine));
//		vif::STMOD(1);
//		vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
//	}
//
}


void EndLines3D(void)
{
//	vif::EndUNPACK();
//
//	if (vif::UnpackSize)
//	{
//		gif::EndTagImmediate(1,1);
//		vif::STMOD(0);
//		vif::MSCAL(VU1_ADDR(Parser));
//	}
//	else
//		dma::pLoc -= 24;
//
//	dma::EndTag();
}

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGC/NX/line.h
================================================
#ifndef __LINE_H
#define __LINE_H

#include "types.h"

namespace NxNgc
{


void BeginLines2D(uint32 rgba);
void DrawLine2D(float x0, float y0, float z0, float x1, float y1, float z1);
void EndLines2D(void);
void BeginLines3D(uint32 rgba);
void DrawLine3D(float x0, float y0, float z0, float x1, float y1, float z1);
void EndLines3D(void);

} // namespace NxNgc


#endif // __LINE_H



================================================
FILE: Code/Gfx/NGC/NX/material.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include "import.h"
#include "material.h"
#include "scene.h"
#include "render.h"
#include "gfx/ngc/p_nxtexture.h"
#include "nx_init.h"

#include 
#include 

//int gMatBytes = 0;

extern u16 colorMap[];

bool gCorrectColor = false;

extern bool gMeshUseCorrection;
extern bool gMeshUseAniso;

int g_mat = 0;

namespace NxNgc
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//sMaterial::sMaterial( void )
//{
//	m_num_wibble_vc_anims	= 0;
//	mp_wibble_vc_params		= NULL;
//	mp_wibble_vc_colors		= NULL;
//	m_uv_wibble				= false;
//	for( int p = 0; p < MAX_PASSES; ++p )
//	{
//		Flags[p]			 = 0;
//		mp_UVWibbleParams[p] = NULL;
//	}
//}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//sMaterial::~sMaterial( void )
//{
////	if( mp_wibble_vc_params	)
////	{
////		for( uint32 i = 0; i < m_num_wibble_vc_anims; ++i )
////		{
////			delete [] mp_wibble_vc_params[i].mp_keyframes;
////		}
////		delete [] mp_wibble_vc_params;
////	}
////	if( mp_wibble_vc_colors	)
////	{
////		delete [] mp_wibble_vc_colors;
////	}
//}



///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//void sMaterial::figure_wibble_uv( void )
//{
//	float uoff, voff, t;
//
//	m_wibble_uv_time	= (float)Tmr::GetTime();
//	t					= m_wibble_uv_time * 0.001f;
//	m_wibble_uv_update	= 0;
//
//	for( uint32 p = 0; p < Passes; ++p )
//	{
//		// Figure out UV offsets for wibbling if required.
//		if( Flags[p] & MATFLAG_UV_WIBBLE )
//		{
//			m_wibble_uv_update |= ( 1 << p );
//		
//			uoff	= ( t * m_pUVControl->UVWibbleParams[p].m_UVel ) + ( m_pUVControl->UVWibbleParams[p].m_UAmplitude * sinf( m_pUVControl->UVWibbleParams[p].m_UFrequency * t + m_pUVControl->UVWibbleParams[p].m_UPhase ));
////			uoff	-= (float)(int)uoff;
//	
//			voff	= ( t * m_pUVControl->UVWibbleParams[p].m_VVel ) + ( m_pUVControl->UVWibbleParams[p].m_VAmplitude * sinf( m_pUVControl->UVWibbleParams[p].m_VFrequency * t + m_pUVControl->UVWibbleParams[p].m_VPhase ));
////			voff	-= (float)(int)voff;
//
//			m_pUVControl->UVWibbleOffset[p][0]	= uoff;
//			m_pUVControl->UVWibbleOffset[p][1]	= voff;
//		}
//	}
//}
//
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//void sMaterial::figure_wibble_vc( void )
//{
//	// The vertex color wibble flag is placed in pass 0.
//	if( Flags[0] & MATFLAG_VC_WIBBLE )
//	{
//		int current_time = (int)Tmr::GetTime();
//
//		for( uint32 i = 0; i < m_num_wibble_vc_anims; ++i )
//		{
//			struct sVCWibbleParams	*p_sequence		= mp_wibble_vc_params + i;
//			
//			// Get phase-shift.
//			int						phase_shift		= p_sequence->m_phase;
//
//			// Time parameters.
//			int						num_keys		= p_sequence->m_num_keyframes;
//			int						start_time		= p_sequence->mp_keyframes[0].m_time;
//			int						end_time		= p_sequence->mp_keyframes[num_keys - 1].m_time;
//			int						period			= end_time - start_time;
//			int						time			= start_time + (( current_time + phase_shift ) % period );
//
//			// Locate the keyframe.
//			int key;
//			for( key = num_keys - 1; key >= 0; --key )
//			{
//				if( time >= p_sequence->mp_keyframes[key].m_time )
//				{
//					break;
//				}
//			}
//			
//			// Parameter expressing how far we are between between this keyframe and the next.
//			float					t				= (float)( time - start_time ) / (float)period;
//
//			// Interpolate the color.
//			GXColor	rgba;
//			rgba.r = (uint8)((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.r ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.r ));
//			rgba.g = (uint8)((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.g ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.g ));
//			rgba.b = (uint8)((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.b ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.b ));
//			rgba.a = (uint8)((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.a ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.a ));
//
//			mp_wibble_vc_colors[i] = *((uint32*)&rgba);
//		}
//	}
//}
//	
//static int	stage_id = 0;	// 0-15
//static int	tev_id = 0;					// 0-3
//static int	uv_id = 0;					// 0-7
//static int	map_id = 0;					// 0-3
//static int	layer_id = 1;					// 1-3
//static bool correct_color = false;
//
//static void multi_start ( int passes )
//{
//	// Set everything to 0.
//	stage_id	= 0;
//	tev_id		= 0;
//	uv_id		= 0;
//	map_id		= 0;
//	layer_id	= 1;
//
//	// Don't forget these!
////	GXSetNumChans(2);
////	GX::SetNumTexGens(2);
////	GX::SetNumTevStages(2);
//
//	if ( gCorrectColor && gMeshUseCorrection )
//	{
//		GXTexObj	texObj;
//		GXInitTexObj( &texObj, colorMap, 256, 256, GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE );
//		GXLoadTexObj( &texObj, GX_TEXMAP7 );
//
//		// Tev 0: Use rasterization lookup table (RG)
//		// Note: Blue = 0 and Alpha = 0;
//		GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//		GX::SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC);
//		GX::SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO);
//		GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG1);
//		GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG1);
//		stage_id++;
//
//		// Tev 1: Use rasterization lookup table (BA),
//		// then add previous tev (RG + BA)
//		GX::SetTevSwapMode( GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP3 );
//		GX::SetTevColorIn(GX_TEVSTAGE1, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_C1);
//		GX::SetTevAlphaIn(GX_TEVSTAGE1, GX_CA_TEXA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A1);
//		GX::SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG2);
//		GX::SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG2);
//		stage_id++;
//		correct_color = true;
//	}
//	else
//	{
//		correct_color = false;
//	}
//
////	// Create 2 dummy stages.
////	GXTevStageID s_id = (GXTevStageID)(((int)GX_TEVSTAGE0)+stage_id);
////
////	GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR_NULL );
////	GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
////	GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
////	GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
////	GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
////	GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
////	stage_id++;
////
////	s_id = (GXTevStageID)(((int)GX_TEVSTAGE0)+stage_id);
////	
////	GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR0A0 );
////	GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
////	GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG2 );
////	GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG2 );
////	GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
////	GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
////	stage_id++;
//
////	if ( !gCorrectColor || !gMeshUseCorrection )
////	{
//////		// Create 2 dummy stages.
//////		GXTevStageID s_id = (GXTevStageID)(((int)GX_TEVSTAGE0)+stage_id);
//////
//////		GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR0A0 );
//////		GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//////		GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG2 );
//////		GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG2 );
//////		GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
//////		GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
//////		stage_id++;
////		correct_color = false;
////	}
//}
//
//static void multi_add_layer ( BlendModes blend, int raw_fix )
//{
//	// Set inputs.
//	GXTevStageID s_id = (GXTevStageID)(((int)GX_TEVSTAGE0)+stage_id);
//	
//	int reg_id = layer_id;	//( layer_id > 1 ) ? 1 : layer_id;	//( tev_id == 3 ) ? 2 : tev_id;		// CPREV, C0, C1, C0.
//  //  int reg_id = layer_id;	//( layer_id == 3 ) ? 2 : layer_id;		// CPREV, C0, C1, C0.
//
////	GXTevAlphaArg newa = (GXTevAlphaArg)(((int)GX_CA_APREV)+layer_id);
//	GXTevColorArg newc = (GXTevColorArg)(((int)GX_CC_CPREV)+(reg_id*2));
//	GXTevColorArg newca= (GXTevColorArg)(((int)GX_CC_APREV)+(reg_id*2));
//
//
//    // out_reg = (d (op) ((1.0 - c)*a + c*b) + bias) * scale;
//
//	int fix = ( raw_fix >= 128 ) ? 255 : raw_fix * 2;
//
//	switch ( blend ) {
//		case vBLEND_MODE_ADD:
//		case vBLEND_MODE_ADD_FIXED:
//			// Add using texture or fixed alpha.
//			GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR_NULL );
//            GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV );
//			GX::SetTevColorIn ( s_id, GX_CC_ZERO, newc, newca, GX_CC_CPREV );
//			stage_id++;
//			break;
//		case vBLEND_MODE_SUBTRACT:
//		case vBLEND_MODE_SUB_FIXED:
//			// Subtract using texture or fixed alpha.
//			GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR_NULL );
//            GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetTevAlphaOp(s_id, GX_TEV_SUB, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevColorOp(s_id, GX_TEV_SUB, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV );
//			GX::SetTevColorIn ( s_id, GX_CC_ZERO, newc, newca, GX_CC_CPREV );
//			stage_id++;
//			break;
//		case vBLEND_MODE_BLEND:
//		case vBLEND_MODE_BLEND_FIXED:
//			// Blend using texture or fixed alpha.
//			GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR_NULL );
//            GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV );
//			GX::SetTevColorIn ( s_id, GX_CC_CPREV, newc, newca, GX_CC_ZERO );
//			stage_id++;
//			break;
//		case vBLEND_MODE_MODULATE:
//			// Modulate current layer with previous layer.
//			GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR_NULL );
//            GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV );
//			GX::SetTevColorIn ( s_id, GX_CC_ZERO, newca, GX_CC_CPREV, GX_CC_ZERO );
//			stage_id++;
//			break;
//		case vBLEND_MODE_MODULATE_FIXED:
//			// Modulate current layer with fixed alpha.
//			GX::SetTevKAlphaSel( s_id, (GXTevKAlphaSel)(((int)GX_TEV_KASEL_K0_A)+layer_id) );
//			GX::SetTevKColorSel( s_id, (GXTevKColorSel)(((int)GX_TEV_KCSEL_K0_A)+layer_id) );
//			GX::SetTevKColor( (GXTevKColorID)(((int)GX_KCOLOR0)+layer_id), (GXColor){fix,fix,fix,fix} );
//			GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR_NULL );
//            GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV );
//			GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_CPREV, GX_CC_KONST, GX_CC_ZERO );
//			stage_id++;
//			break;
//		case vBLEND_MODE_BRIGHTEN:
//			// Modulate current layer with previous layer, & add current layer.
//			GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR_NULL );
//            GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV );
//			GX::SetTevColorIn ( s_id, GX_CC_ZERO, newca, GX_CC_CPREV, GX_CC_CPREV );
//			stage_id++;
//			break;
//		case vBLEND_MODE_BRIGHTEN_FIXED:
//			// Modulate current layer with fixed alpha, & add current layer.
//			GX::SetTevKAlphaSel( s_id, (GXTevKAlphaSel)(((int)GX_TEV_KASEL_K0_A)+layer_id) );
//			GX::SetTevKColorSel( s_id, (GXTevKColorSel)(((int)GX_TEV_KCSEL_K0_A)+layer_id) );
//			GX::SetTevKColor( (GXTevKColorID)(((int)GX_KCOLOR0)+layer_id), (GXColor){fix,fix,fix,fix} );
//			GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR_NULL );
//            GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV );
//			GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_CPREV, GX_CC_KONST, GX_CC_CPREV );
//			stage_id++;
//			break;
//		case vBLEND_MODE_DIFFUSE:
//		default:
//			// Replace with this texture. Shouldn't ever be used, but here for compatibility.
//			GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR_NULL );
//            GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV );
//			GX::SetTevColorIn ( s_id, newc, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//			stage_id++;
//			break;
//	}
////	layer_id++;
//}
//
//static void multi_add_texture ( sTexture * p_texture, GXColor matcol, GXTexWrapMode umode, GXTexWrapMode vmode, BlendModes blend, int fix, uint32 flags, char * p_uv_slot, float k )
//{
//	if ( map_id >= 8 ) return;
//	
//	void * p_tex_data = p_texture->pTexelData;
//	void * p_alpha_data = p_texture->has_alpha ? p_texture->pAlphaData : NULL;
//	int width = p_texture->ActualWidth;
//	int height = p_texture->ActualHeight;
//	int levels = p_texture->Levels;
//	int reg_id = ( tev_id > 1 ) ? 1 : tev_id;	//( tev_id == 3 ) ? 2 : tev_id;		// CPREV, C0, C1, C0.
//	
//	GXTevAlphaArg rasa;
//	GXTevColorArg rasc;
//	if ( correct_color )
//	{
//		rasa = GX_CA_A2;
//		rasc = GX_CC_C2;
//	}
//	else
//	{
//		rasa = GX_CA_RASA;
//		rasc = GX_CC_RASC;
//	}
//
//	int cur_map = map_id + 1;
//	int alpha_map = -1;
//	bool uniqueMaterialColor = false;
//
//	if ( ( matcol.r == 128 ) && ( matcol.g == 128 ) && ( matcol.b == 128 ) && ( matcol.a == 255 ) )
//	{
//		uniqueMaterialColor = false;
//	}
//	else
//	{
//		uniqueMaterialColor = true;
//	}
//
//	if ( p_alpha_data )
//	{
//		switch ( blend )
//		{
//			case vBLEND_MODE_ADD:
//			case vBLEND_MODE_SUBTRACT:
//			case vBLEND_MODE_BLEND:
//			case vBLEND_MODE_BRIGHTEN:
//			case vBLEND_MODE_MODULATE:
//			case vBLEND_MODE_DIFFUSE:
//				if ( cur_map < 8 )
//				{
//					alpha_map = cur_map;
//					cur_map++;
//				}
//				break;
//			default:
//				break;
//		}
//	}
//
//	GXTexObj	texObj;
//	GXInitTexObj( &texObj, p_tex_data, width, height, ((GXTexFmt)p_texture->format), umode, vmode, levels > 1 ? GX_TRUE : GX_FALSE );
//	if ( levels > 1 )
//	{
//		if ( gMeshUseAniso && ( tev_id == 0 ) )
//		{
//			// If we're correcting the color, we also want ANISO_4.
//			GXInitTexObjLOD( &texObj, GX_LIN_MIP_LIN, GX_LINEAR, 0.0f, levels - 1, k, GX_FALSE, GX_TRUE, GX_ANISO_4 );
//		}
//		else
//		{
//			GXInitTexObjLOD( &texObj, GX_LIN_MIP_LIN, GX_LINEAR, 0.0f, levels - 1, k, GX_FALSE, GX_TRUE, GX_ANISO_1 );
//		}
//	}
//	else
//	{
//		GXInitTexObjLOD( &texObj, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, k, GX_FALSE, GX_TRUE, GX_ANISO_1 );
//	}
//	GXLoadTexObj( &texObj, (GXTexMapID)(((int)GX_TEXMAP0)+map_id) );
//
//	if ( alpha_map >= 0 )
//	{
//		switch ( blend )
//		{
//			case vBLEND_MODE_ADD:
//			case vBLEND_MODE_SUBTRACT:
//			case vBLEND_MODE_BLEND:
//			case vBLEND_MODE_BRIGHTEN:
//			case vBLEND_MODE_MODULATE:
//			case vBLEND_MODE_DIFFUSE:
//				GXTexObj	alphaObj;
//				GXInitTexObj( &alphaObj, p_alpha_data, width, height, ((GXTexFmt)p_texture->format), umode, vmode, levels > 1 ? GX_TRUE : GX_FALSE );
//				if ( levels > 1 )
//				{
//					if ( gMeshUseAniso && ( tev_id == 0 ) )
//					{
//						// If we're correcting the color, we also want ANISO_4.
//						GXInitTexObjLOD( &alphaObj, GX_LIN_MIP_LIN, GX_LINEAR, 0.0f, levels - 1, k, GX_FALSE, GX_TRUE, GX_ANISO_4 );
//					}
//					else
//					{
//						GXInitTexObjLOD( &alphaObj, GX_LIN_MIP_LIN, GX_LINEAR, 0.0f, levels - 1, k, GX_FALSE, GX_TRUE, GX_ANISO_1 );
//					}
//				}
//				else
//				{
//					GXInitTexObjLOD( &alphaObj, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, k, GX_FALSE, GX_TRUE, GX_ANISO_1 );
//				}
//				GXLoadTexObj( &alphaObj, (GXTexMapID)(((int)GX_TEXMAP0)+alpha_map) );
//				break;
//			default:
//				p_alpha_data = NULL;
//				break;
//		}
//	}
//
//	GXTexMapID t_id = (GXTexMapID)(((int)GX_TEXMAP0)+map_id);
//	GXTexCoordID u_id = (GXTexCoordID)(((int)GX_TEXCOORD0)+uv_id);
//	GXTevStageID s_id = (GXTevStageID)(((int)GX_TEVSTAGE0)+stage_id);
//
//	// Modulate material color if required.
//	if ( uniqueMaterialColor )
//	{
//		GX::SetTevKColor( (GXTevKColorID)(((int)GX_KCOLOR0)+tev_id), (GXColor){matcol.r,matcol.g,matcol.b,fix} );
//		GX::SetTevKColorSel( s_id, (GXTevKColorSel)(((int)GX_TEV_KCSEL_K0)+tev_id) );
//
//		GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR0A0 );
//		GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//		GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+reg_id) );
//		GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+reg_id) );
//		GX::SetTevAlphaIn ( s_id, rasa, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//		GX::SetTevColorIn ( s_id, GX_CC_ZERO, rasc, GX_CC_KONST, GX_CC_ZERO );
//		stage_id++;
//		s_id = (GXTevStageID)(((int)GX_TEVSTAGE0)+stage_id);
//	}
//	
//	// Set texture coordinates. Note: If p_tex is NULL, it means this layer is environment mapped.
////	if ( !(flags & MATFLAG_ENVIRONMENT) )
////	{
//		*p_uv_slot = uv_id;
//		uv_id++;
////	} else {
////		*p_uv_slot = -1;
////	}
//
//	// Load this texture up into a temporary register for use later.
//	// Note: conveniently, the 1st texture ends up in TEVPREV which means it will be fine
//	// if no blends are performed.
//	switch ( blend )
//	{
//		case vBLEND_MODE_ADD_FIXED:
//		case vBLEND_MODE_SUB_FIXED:
//		case vBLEND_MODE_BLEND_FIXED:
//		case vBLEND_MODE_BRIGHTEN_FIXED:
//		case vBLEND_MODE_MODULATE_FIXED:
//			GX::SetTevKAlphaSel( s_id, (GXTevKAlphaSel)(((int)GX_TEV_KASEL_K0_A)+tev_id) );
//			if ( !uniqueMaterialColor ) GX::SetTevKColor( (GXTevKColorID)(((int)GX_KCOLOR0)+tev_id), (GXColor){fix,fix,fix,fix} );
//			GX::SetTevOrder( s_id, u_id, t_id, GX_COLOR0A0 );
//            GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+reg_id) );
//			GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+reg_id) );
//			if ( uniqueMaterialColor )
//			{
//				GX::SetTevAlphaIn ( s_id, GX_CA_KONST, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//				GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_TEXC, (GXTevColorArg)(((int)GX_CC_CPREV)+(reg_id*2)), GX_CC_ZERO );
//			}
//			else
//			{
//				GX::SetTevAlphaIn ( s_id, GX_CA_KONST, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//				GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_TEXC, rasc, GX_CC_ZERO );
//			}
//			stage_id++;
//			break;
//		case vBLEND_MODE_ADD:
//		case vBLEND_MODE_SUBTRACT:
//		case vBLEND_MODE_BLEND:
//		case vBLEND_MODE_BRIGHTEN:
//		case vBLEND_MODE_MODULATE:
//		case vBLEND_MODE_DIFFUSE:
//		default:
//			// We need to add a stage, if we have an alpha map.
//			if ( alpha_map >= 0 )
//			{
//				// Set inputs.
//				GXTexMapID a_id = (GXTexMapID)(((int)GX_TEXMAP0)+alpha_map);
//
//				GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP1 );       // Alpha map is in Green channel, so use this to swap it to the alpha channel.
//				GX::SetTevOrder( s_id, u_id, a_id, GX_COLOR0A0 );
//				GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+reg_id) );
//				GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+reg_id) );
//				if ( uniqueMaterialColor )
//				{
//					GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_TEXA, (GXTevAlphaArg)(((int)GX_CA_APREV)+reg_id), GX_CA_ZERO );
//					GX::SetTevColorIn ( s_id, (GXTevColorArg)(((int)GX_CC_CPREV)+(reg_id*2)), GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//				}
//				else
//				{
//					GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_TEXA, rasa, GX_CA_ZERO );
//					GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//				}
//				stage_id++;
//				s_id = (GXTevStageID)(((int)GX_TEVSTAGE0)+stage_id);
//				GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//				GX::SetTevOrder( s_id, u_id, t_id, GX_COLOR0A0 );
//				GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+reg_id) );
//				GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+reg_id) );
//				GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, (GXTevAlphaArg)(((int)GX_CA_APREV)+reg_id), GX_CA_TEXA, GX_CA_ZERO );
//				if ( uniqueMaterialColor )
//				{
//					GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_TEXC, (GXTevColorArg)(((int)GX_CC_CPREV)+(reg_id*2)), GX_CC_ZERO );
//				}
//				else
//				{
//					GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_TEXC, rasc, GX_CC_ZERO );
//				}
//				stage_id++;
//			} else {
//				GX::SetTevOrder( s_id, u_id, t_id, GX_COLOR0A0 );
//				GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//				GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+reg_id) );
//				GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+reg_id) );
//				if ( uniqueMaterialColor )
//				{
//					GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_TEXA, (GXTevAlphaArg)(((int)GX_CA_APREV)+reg_id), GX_CA_ZERO );
//					GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_TEXC, (GXTevColorArg)(((int)GX_CC_CPREV)+(reg_id*2)), GX_CC_ZERO );
//				}
//				else
//				{
//					GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_TEXA, rasa, GX_CA_ZERO );
//					GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_TEXC, rasc, GX_CC_ZERO );
//				}
//				stage_id++;
//			}
//
//			break;
//	}
//
//	if ( tev_id > 0 ) {
//		multi_add_layer ( blend, fix );
//	} else {
//		// Set blend mode for base layer.
//		switch ( blend )
//		{
//			case vBLEND_MODE_ADD:
//			case vBLEND_MODE_ADD_FIXED:
//				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR );
//				break;
//			case vBLEND_MODE_SUBTRACT:
//			case vBLEND_MODE_SUB_FIXED:
//				GX::SetBlendMode ( GX_BM_SUBTRACT, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR );
//				break;
//			case vBLEND_MODE_BLEND:
//			case vBLEND_MODE_BLEND_FIXED:
//				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR );
//				break;
//			case vBLEND_MODE_BRIGHTEN:
//			case vBLEND_MODE_BRIGHTEN_FIXED:
////				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ONE, GX_LO_CLEAR );
//				GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR );
//				break;
//			case vBLEND_MODE_MODULATE_FIXED:
//			case vBLEND_MODE_MODULATE:
////				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR );
//				GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR );
//				break;
//			case vBLEND_MODE_DIFFUSE:
//			default:
//				GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR );
//				break;
//		}
//	}
//
//	tev_id++;
//	map_id = cur_map;
//}
//
//static void multi_end ( BlendModes blend, uint32 alphacutoff )
//{
//	// Special case. If the base layer uses subtractive mode, we need to add a stage to
//	// modulate the color & alpha.
//	switch ( blend )
//	{
////		case vBLEND_MODE_DIFFUSE:
////			GX::SetAlphaCompare(GX_GEQUAL, 0, GX_AOP_AND, GX_GEQUAL, 0 );
////			if ( alphacutoff == 0 )
////			{
////				GXSetZCompLoc( GX_TRUE );
////			}
////			else
////			{
////				GXSetZCompLoc( GX_FALSE );		// Textures with holes will not write to the z buffer when a<=cutoff.
////			}
////			break;
//		case vBLEND_MODE_SUB_FIXED:
//		case vBLEND_MODE_SUBTRACT:
//			{
//				GXTevStageID s_id = (GXTevStageID)(((int)GX_TEVSTAGE0)+stage_id);
//
//				GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR_NULL );
//				GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//				GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//				GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//				GX::SetTevAlphaIn ( s_id, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_APREV );
//				GX::SetTevColorIn ( s_id, GX_CC_ZERO, GX_CC_CPREV, GX_CC_APREV, GX_CC_ZERO );
//				stage_id++;
//				//GX::SetAlphaCompare(GX_GEQUAL, 0, GX_AOP_AND, GX_GEQUAL, 0 );
////				GXSetZCompLoc( GX_FALSE );
//			}
//			break;
//		default:
//			//GX::SetAlphaCompare(GX_GEQUAL, alphacutoff, GX_AOP_AND, GX_GEQUAL, alphacutoff );
////			if ( alphacutoff == 0 )
////			{
////				GXSetZCompLoc( GX_TRUE );
////			}
////			else
////			{
////				GXSetZCompLoc( GX_FALSE );		// Textures with holes will not write to the z buffer when a<=cutoff.
////			}
//			break;
//	}
//
////	if ( blend == vBLEND_MODE_DIFFUSE )
////	{
////		//Always pass pixels with multi-texturing as the base must be opaque.
////		GX::SetAlphaCompare(GX_GEQUAL, 0, GX_AOP_AND, GX_GEQUAL, 0 );
////		GXSetZCompLoc( GX_TRUE );
////	}
////	else
////	{
////	}
//
//	if ( correct_color )
//	{
//		GXTexCoordID u_id;
//		// Convert color0 to s,t for 2-D texture lookup (RG)
//		u_id = (GXTexCoordID)(((int)GX_TEXCOORD0)+uv_id);
//		GX::SetTexCoordGen(u_id, GX_TG_SRTG, GX_TG_COLOR0, GX_IDENTITY);
//		GX::SetTevOrder(GX_TEVSTAGE0, u_id, GX_TEXMAP7, GX_COLOR_NULL);
//		uv_id++;
//	
//		// Convert color1 to s,t for 2-D texture lookup (BA)
//		u_id = (GXTexCoordID)(((int)GX_TEXCOORD0)+uv_id);
//		GX::SetTexCoordGen(u_id, GX_TG_SRTG, GX_TG_COLOR1, GX_IDENTITY);
//		GX::SetTevOrder(GX_TEVSTAGE1, u_id, GX_TEXMAP7, GX_COLOR_NULL);
//		uv_id++;
//		GX::SetNumChans( 2 );
//	}
//	else
//	{
//		GX::SetNumChans( 1 );
//	}
//
//	// Set final number of textures/stages.
//	GX::SetNumTexGens( uv_id );
//    GX::SetNumTevStages( stage_id );
//}
//
////sMaterial					*Materials;
//uint32						NumMaterials;
////Lst::HashTable< sMaterial > *pMaterialTable	= NULL;
//
//void sMaterial::Submit( uint32 mesh_flags, GXColor mesh_color )
//{
//	// Figure uv and vc wibble updates if required.
//	figure_wibble_uv();
//	figure_wibble_vc();
//
//	
//
//	for ( uint layer = 0; layer < Passes; layer++ )
//	{
//		if( Flags[layer] & MATFLAG_UV_WIBBLE )
//		{
//			// Set up the texture generation matrix here.
//			Mtx	m;
//			MTXTrans( m, m_pUVControl->UVWibbleOffset[layer][0], m_pUVControl->UVWibbleOffset[layer][1], 0.0f );
//			switch ( layer )
//			{
//				case 0:
//					GX::LoadTexMtxImm( m, GX_TEXMTX0, GX_MTX2x4 );
//					break;
//				case 1:
//					GX::LoadTexMtxImm( m, GX_TEXMTX1, GX_MTX2x4 );
//					break;
//				case 2:
//					GX::LoadTexMtxImm( m, GX_TEXMTX2, GX_MTX2x4 );
//					break;
//				case 3:
//					GX::LoadTexMtxImm( m, GX_TEXMTX3, GX_MTX2x4 );
//					break;
//				default:
//					break;
//				
//			}
//
////			// Handle UV wibbling.
////			D3DXMATRIX mat;
////			mat._11 = 1.0f;					mat._12 = 0.0f; 
////			mat._21 = 0.0f;					mat._22 = 1.0f;
////			mat._31 = UVWibbleOffset[p][0];	mat._32 = UVWibbleOffset[p][1];
////			mat._41 = 0.0f;					mat._42 = 0.0f;
////			D3DDevice_SetTransform((D3DTRANSFORMSTATETYPE)( D3DTS_TEXTURE0 + p ), &mat );
////			D3DDevice_SetTextureStageState( p, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );
////			D3DDevice_SetTextureStageState( p, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU | p );
//		}
//	}
//
////	// Generate display list.
////
////	multi_start ( Passes );
////	for ( uint layer = 0; layer < Passes ; layer++ )
////	{
////		GXTexWrapMode u_mode;
////		GXTexWrapMode v_mode;
////		if ( Flags[layer] & MATFLAG_ENVIRONMENT )
////		{
////			u_mode = GX_REPEAT;
////			v_mode = GX_REPEAT;
////		}
////		else
////		{
////			u_mode = UVAddressing[layer] & (1<<0)  ? GX_CLAMP : GX_REPEAT;
////			v_mode = UVAddressing[layer] & (1<<1) ? GX_CLAMP : GX_REPEAT;
////		}
////
////		if ( pTex[layer] )
////		{
////			multi_add_texture ( pTex[layer],
////								matcol[layer],
////								u_mode,
////								v_mode,
////								(BlendModes)blendMode[layer],
////								fixAlpha[layer],
////								Flags[layer],
////								&uv_slot[layer],
////								K[layer] );
////		}
////		else
////		{
////			// Untextured.
////			GXTevStageID s_id = (GXTevStageID)(((int)GX_TEVSTAGE0)+stage_id);
////			BlendModes blend = (BlendModes)blendMode[layer];
////			uint32 fix = fixAlpha[layer];
////
////			uv_slot[layer] = -1;
////
////			switch ( blend )
////			{
////				case vBLEND_MODE_ADD_FIXED:
////				case vBLEND_MODE_SUB_FIXED:
////				case vBLEND_MODE_BLEND_FIXED:
////				case vBLEND_MODE_BRIGHTEN_FIXED:
////				case vBLEND_MODE_MODULATE_FIXED:
////					GX::SetTevKAlphaSel( s_id, (GXTevKAlphaSel)(((int)GX_TEV_KASEL_K0_A)+tev_id) );
////					GX::SetTevKColor( (GXTevKColorID)(((int)GX_KCOLOR0)+tev_id), (GXColor){fix,fix,fix,fix} );
////					GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR0A0 );
////					GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
////					GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+tev_id) );
////					GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+tev_id) );
////					GX::SetTevAlphaIn ( s_id, GX_CA_KONST, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
////					GX::SetTevColorIn ( s_id, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
////					stage_id++;
////					break;
////				case vBLEND_MODE_ADD:
////				case vBLEND_MODE_SUBTRACT:
////				case vBLEND_MODE_BLEND:
////				case vBLEND_MODE_BRIGHTEN:
////				case vBLEND_MODE_MODULATE:
////				case vBLEND_MODE_DIFFUSE:
////				default:
////					GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR0A0 );
////					GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
////					GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+tev_id) );
////					GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+tev_id) );
////					GX::SetTevAlphaIn ( s_id, GX_CA_RASA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
////					GX::SetTevColorIn ( s_id, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
////					stage_id++;
////					break;
////			}
////
////			if ( tev_id > 0 ) {
////				multi_add_layer ( blend, fix );
////			} else {
////				// Set blend mode for base layer.
////				switch ( blend )
////				{
////					case vBLEND_MODE_ADD:
////					case vBLEND_MODE_ADD_FIXED:
////						GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR );
////						break;
////					case vBLEND_MODE_SUBTRACT:
////					case vBLEND_MODE_SUB_FIXED:
////						GX::SetBlendMode ( GX_BM_SUBTRACT, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR );
////						break;
////					case vBLEND_MODE_BLEND:
////					case vBLEND_MODE_BLEND_FIXED:
////						GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR );
////						break;
////					case vBLEND_MODE_BRIGHTEN:
////					case vBLEND_MODE_BRIGHTEN_FIXED:
////						GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ONE, GX_LO_CLEAR );
////						break;
////					case vBLEND_MODE_MODULATE_FIXED:
////					case vBLEND_MODE_MODULATE:
////						GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR );
////						break;
////					case vBLEND_MODE_DIFFUSE:
////					default:
////						GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR );
////						break;
////				}
////			}
////			tev_id++;
////		}
////	}
////
////	multi_end( (BlendModes)blendMode[0], AlphaCutoff );
//
//
//	// Set the cull mode, but only if the calling code didn't override this by setting CULL_NONE.
//	if ( EngineGlobals.poly_culling )
//	{
//		GX::SetCullMode ( m_no_bfc ? GX_CULL_NONE : GX_CULL_FRONT );
//	}
//	else
//	{
//		GX::SetCullMode ( GX_CULL_NONE );
//	}
//
//#ifdef __USE_MAT_DL__
//	if ( mp_display_list )
//	{
//		GXCallDisplayList ( mp_display_list, m_display_list_size ); 
//	}
//#else
//	Initialize( mesh_flags, mesh_color );
//#endif		// __USE_MAT_DL__
//
//	// See if we need to set the comp location (just check the base layer).
//	if ( pTex[0] )
//	{
//		if ( pTex[0]->HasHoles )
//		{
//			GX::SetZCompLoc( GX_FALSE );
//		}
//		else
//		{
////			GX::SetZCompLoc( GX_FALSE );
//			GX::SetZCompLoc( GX_TRUE );
////			GX::SetAlphaCompare(GX_GEQUAL, 0, GX_AOP_AND, GX_GEQUAL, 0 );
//		}
//	}
//	GX::SetAlphaCompare(GX_GEQUAL, AlphaCutoff, GX_AOP_AND, GX_GEQUAL, AlphaCutoff );
//
////	if ( mp_display_list )
////	{
////		GXCallDisplayList ( mp_display_list, m_display_list_size ); 
////	}
////	else
////	{
////		// If no material, default to purple, untextured.
////		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
////		GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
////		GX::SetNumTexGens( 0 );
////		GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
////		GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
////		GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
////		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR );
////		GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
////		GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
////		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,0,255,255} );
////		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){128,128,128,255} );
////		GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
////	}
//}
//
//void sMaterial::Initialize( uint32 mesh_flags, GXColor mesh_color )
//{
//#ifdef __USE_MAT_DL__
//	// Generate display list.
//	Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
//#define DL_BUILD_SIZE (32*1024)
//	uint8 * p_build_dl = new (Mem::Manager::sHandle().TopDownHeap()) uint8[DL_BUILD_SIZE];
//	DCFlushRange ( p_build_dl, DL_BUILD_SIZE );
//
//	GXBeginDisplayList ( p_build_dl, DL_BUILD_SIZE );
//	GXResetWriteGatherPipe();
//#endif		// __USE_MAT_DL__
//
//	multi_start ( Passes );
//	for ( uint layer = 0; layer < Passes ; layer++ )
//	{
//		GXTexWrapMode u_mode;
//		GXTexWrapMode v_mode;
//		if ( Flags[layer] & MATFLAG_ENVIRONMENT )
//		{
//			u_mode = GX_REPEAT;
//			v_mode = GX_REPEAT;
//		}
//		else
//		{
//			u_mode = UVAddressing[layer] & (1<<0)  ? GX_CLAMP : GX_REPEAT;
//			v_mode = UVAddressing[layer] & (1<<1) ? GX_CLAMP : GX_REPEAT;
//		}
//
//		GXColor col;
//
//		if ( mesh_flags & sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE && !( Flags[layer] & 0x80 ) )
//		{
//			col = mesh_color;
//		}
//		else
//		{
//			col = matcol[layer];
//		}
//
//		if ( pTex[layer] )
//		{
//			multi_add_texture ( pTex[layer],
//								col,
//								u_mode,
//								v_mode,
//								(BlendModes)blendMode[layer],
//								fixAlpha[layer],
//								Flags[layer],
//								&uv_slot[layer],
//								K[layer] );
//		}
//		else
//		{
//			// Untextured.
//			GXTevStageID s_id = (GXTevStageID)(((int)GX_TEVSTAGE0)+stage_id);
//			BlendModes blend = (BlendModes)blendMode[layer];
//			uint32 fix = fixAlpha[layer];
//
//			uv_slot[layer] = -1;
//
//			switch ( blend )
//			{
//				case vBLEND_MODE_ADD_FIXED:
//				case vBLEND_MODE_SUB_FIXED:
//				case vBLEND_MODE_BLEND_FIXED:
//				case vBLEND_MODE_BRIGHTEN_FIXED:
//				case vBLEND_MODE_MODULATE_FIXED:
//					GX::SetTevKAlphaSel( s_id, (GXTevKAlphaSel)(((int)GX_TEV_KASEL_K0_A)+tev_id) );
//					GX::SetTevKColor( (GXTevKColorID)(((int)GX_KCOLOR0)+tev_id), (GXColor){fix,fix,fix,fix} );
//					GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR0A0 );
//					GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//					GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+tev_id) );
//					GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+tev_id) );
//					GX::SetTevAlphaIn ( s_id, GX_CA_KONST, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//					GX::SetTevColorIn ( s_id, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//					stage_id++;
//					break;
//				case vBLEND_MODE_ADD:
//				case vBLEND_MODE_SUBTRACT:
//				case vBLEND_MODE_BLEND:
//				case vBLEND_MODE_BRIGHTEN:
//				case vBLEND_MODE_MODULATE:
//				case vBLEND_MODE_DIFFUSE:
//				default:
//					GX::SetTevOrder( s_id, GX_TEXCOORD_NULL, GX_TEX_DISABLE, GX_COLOR0A0 );
//					GX::SetTevSwapMode( s_id, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//					GX::SetTevAlphaOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+tev_id) );
//					GX::SetTevColorOp(s_id, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, (GXTevRegID)(((int)GX_TEVPREV)+tev_id) );
//					GX::SetTevAlphaIn ( s_id, GX_CA_RASA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//					GX::SetTevColorIn ( s_id, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//					stage_id++;
//					break;
//			}
//
//			if ( tev_id > 0 ) {
//				multi_add_layer ( blend, fix );
//			} else {
//				// Set blend mode for base layer.
//				switch ( blend )
//				{
//					case vBLEND_MODE_ADD:
//					case vBLEND_MODE_ADD_FIXED:
//						GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR );
//						break;
//					case vBLEND_MODE_SUBTRACT:
//					case vBLEND_MODE_SUB_FIXED:
//						GX::SetBlendMode ( GX_BM_SUBTRACT, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR );
//						break;
//					case vBLEND_MODE_BLEND:
//					case vBLEND_MODE_BLEND_FIXED:
//						GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR );
//						break;
//					case vBLEND_MODE_BRIGHTEN:
//					case vBLEND_MODE_BRIGHTEN_FIXED:
//						GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ONE, GX_LO_CLEAR );
//						break;
//					case vBLEND_MODE_MODULATE_FIXED:
//					case vBLEND_MODE_MODULATE:
//						GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR );
//						break;
//					case vBLEND_MODE_DIFFUSE:
//					default:
//						GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR );
//						break;
//				}
//			}
//			tev_id++;
//		}
//	}
//
//	multi_end( (BlendModes)blendMode[0], AlphaCutoff );
//
//#ifdef __USE_MAT_DL__
//	int size = GXEndDisplayList();
//
//	// If we already have a display list & this one will fit, just use the existing piece of memory.
//	uint8 * p_dl;
//	if ( mp_display_list && ( size <= m_display_list_size ) )
//	{
//		p_dl = mp_display_list;
//	}
//	else
//	{
//		if ( mp_display_list )
//		{
//			//gMatBytes -= m_display_list_size;
//			delete mp_display_list;
//		}
//		p_dl = new uint8[size];
//		//gMatBytes += size;
//	}
//
//	memcpy ( p_dl, p_build_dl, size );
//	DCFlushRange ( p_dl, size );
//
//	delete p_build_dl;
//
//	mp_display_list = p_dl;
//	m_display_list_size = size;
//	Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
//#endif		// __USE_MAT_DL__
//}




sMaterial* GetMaterial( uint32 checksum, sScene *p_scene )
{
//	for ( int lp = 0; lp < p_scene->m_num_materials; lp++ )
//	{
//		if( p_scene->mp_material_array[lp].Checksum == checksum )
//		{
//			return &p_scene->mp_material_array[lp];
//		}
//	}
	return NULL;
}

void InitializeMaterial( sMaterial* p_material )
{
//	p_material->Initialize();
	p_material->uv_slot[0] = 0;
	p_material->uv_slot[1] = 1;
	p_material->uv_slot[2] = 2;
	p_material->uv_slot[3] = 3;
}

#define MemoryRead( dst, size, num, src )	memcpy(( dst ), ( src ), (( num ) * ( size )));	\
											( src ) += (( num ) * ( size ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMaterial * LoadMaterialsFromMemory( void **pp_mem, Lst::HashTable< Nx::CTexture > *p_texture_table, int * p_num_materials )
{
	uint8	*p_data = (uint8*)( *pp_mem );
	uint32 MMAG, MMIN, K, L, NumSeqs, seq, NumKeys;
	uint32 i;

	// Get number of materials.
	uint32 new_materials;
	MemoryRead( &new_materials, sizeof( uint32 ), 1, p_data );
	
	if ( p_num_materials ) *p_num_materials = new_materials;

	// Create array of materials.
	sMaterial * p_mat_array = new sMaterial[new_materials];
	
	// Loop over materials.
	for( i = 0; i < new_materials; ++i )
	{
		// Create new material.
		sMaterial *pMat = &p_mat_array[i];
		
		pMat->m_num_wibble_vc_anims	= 0;
		pMat->mp_wibble_vc_params	= NULL;
		pMat->mp_wibble_vc_colors	= NULL;

		// Get material checksum.
		MemoryRead( &pMat->Checksum, sizeof( uint32 ), 1, p_data );

		// Get number of passes.
		uint32 pass;
		MemoryRead( &pass, sizeof( uint32 ), 1, p_data );
		pMat->Passes = pass;

		// Get alpha cutoff value.
		uint32 ac;
		MemoryRead( &ac, sizeof( uint32 ), 1, p_data );
		pMat->AlphaCutoff = ac;		//( ac >= 128 ) ? 255 : ( ac * 2 );

		// Get sorted flag.
		bool sorted;
		MemoryRead( &sorted, sizeof( bool ), 1, p_data );
		pMat->m_sorted = sorted;
		
		// Get draw order.
		MemoryRead( &pMat->m_draw_order, sizeof( float ), 1, p_data );
		
		// Get backface cull flag.
		bool bfc;
		MemoryRead( &bfc, sizeof( bool ), 1, p_data );
		pMat->m_no_bfc = bfc;

//		// Get grassify flag and (optionally) grassify data.
//		bool grassify;
//		MemoryRead( &grassify, sizeof( bool ), 1, p_data );
//		pMat->m_grassify = (uint8)grassify;
//		if( grassify )
//		{
//			MemoryRead( &pMat->m_grass_height, sizeof( float ), 1, p_data );
//			int layers;
//			MemoryRead( &layers, sizeof( int ), 1, p_data );
//			pMat->m_grass_layers = (uint8)layers;
//		}

		GXColor mat_col[MAX_PASSES];

		pMat->m_pUVControl = NULL;
		for( uint32 pass = 0; pass < pMat->Passes; ++pass )
		{
			// Get texture checksum.
			uint32 TextureChecksum;
			MemoryRead( &TextureChecksum, sizeof( uint32 ), 1, p_data );

			// Get material flags.
			MemoryRead( &pMat->Flags[pass], sizeof( uint32 ), 1, p_data );

			// Get ALPHA register value.
			uint64 ra;
			MemoryRead( &ra, sizeof( uint64 ), 1, p_data );
			pMat->blendMode[pass] = ra & 0xff;
			pMat->fixAlpha[pass] = ( ra >> 32 ) & 0xff;

			// Backface cull test - if this is an alpha blended material, turn off backface culling.
			if(( pass == 0 ) && (( ra & 0xFF ) != 0x00 ))
			{
				pMat->m_no_bfc = true;
			}

			// Get UV addressing types.
			uint32 u_addressing, v_addressing;
			MemoryRead( &u_addressing, sizeof( uint32 ), 1, p_data );
			MemoryRead( &v_addressing, sizeof( uint32 ), 1, p_data );
			pMat->UVAddressing[pass] = (( v_addressing << 1 ) | u_addressing );
			
			// Step past material colors, currently unsupported.
			float col[9];
			MemoryRead( col, sizeof( float ) * 9, 1, p_data );

			mat_col[pass].r = (u8)((col[0] * 255.0f) + 0.5f);
			mat_col[pass].g = (u8)((col[1] * 255.0f) + 0.5f);
			mat_col[pass].b = (u8)((col[2] * 255.0f) + 0.5f);
			mat_col[pass].a = 255;

			// Read uv wibble data if present.
			if( pMat->Flags[pass] & MATFLAG_UV_WIBBLE )
			{
				if ( !pMat->m_pUVControl ) pMat->m_pUVControl = new sUVControl;
				MemoryRead( &( pMat->m_pUVControl->UVWibbleParams[pass] ), sizeof( sUVWibbleParams ), 1, p_data );
			}

			// Step past vc wibble data, currently unsupported.
			if(( pass == 0 ) && ( pMat->Flags[0] & MATFLAG_VC_WIBBLE ))
			{
				MemoryRead( &NumSeqs, sizeof( uint32 ), 1, p_data );
				pMat->m_num_wibble_vc_anims = NumSeqs;

				// Create sequence data array.
				pMat->mp_wibble_vc_params = new sVCWibbleParams[NumSeqs];
				
				// Create resultant color array.
				pMat->mp_wibble_vc_colors = new uint32[NumSeqs];

				for( seq = 0; seq < NumSeqs; ++seq )
				{ 
					MemoryRead( &NumKeys, sizeof( uint32 ), 1, p_data );

					int phase;
					MemoryRead( &phase, sizeof( int ), 1, p_data );

					// Create array for keyframes.
					pMat->mp_wibble_vc_params[seq].m_num_keyframes	= NumKeys;
					pMat->mp_wibble_vc_params[seq].m_phase			= phase;
					pMat->mp_wibble_vc_params[seq].mp_keyframes		= new sVCWibbleKeyframe[NumKeys];

					// Read keyframes into array.
					MemoryRead( pMat->mp_wibble_vc_params[seq].mp_keyframes, sizeof( sVCWibbleKeyframe ), NumKeys, p_data );

					for ( uint lp = 0; lp < NumKeys; lp++ )
					{
						uint8 r = pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.r;
						uint8 g = pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.g;
						uint8 b = pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.b;
						uint8 a = pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.a;
						pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.r = b;
						pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.g = r;
						pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.b = g;
						pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.a = a;
					}
				}
			}

			if( TextureChecksum )
			{
				// If textured, resolve texture checksum...
				Nx::CNgcTexture *p_ngc_texture = static_cast( p_texture_table->GetItem( TextureChecksum ) );
				sTexture *pTex = ( p_ngc_texture ) ? p_ngc_texture->GetEngineTexture() : NULL;

				// Bail if checksum not found.
				if( pTex == NULL )
				{
					Dbg_Message( "error: couldn't find texture checksum %08X\n", TextureChecksum );
//					exit( 1 );
					pMat->pTex[pass] = NULL;
				}
				else
				{
					// Set texture pointer.
					pMat->pTex[pass] = pTex;
				}

				// Get mipmap info.
				MemoryRead( &MMAG, sizeof( uint32 ), 1, p_data );
				MemoryRead( &MMIN, sizeof( uint32 ), 1, p_data );

				MemoryRead( &K, sizeof( uint32 ), 1, p_data );
				MemoryRead( &L, sizeof( uint32 ), 1, p_data );
				
				// Default PS2 value for K appears to be -8.0f - we are interested in deviations from this value.
				pMat->K[pass]	= ( *(float*)&K ) + 8.0f;
			
				// Dave note 08/03/02 - having MIPs selected earlier than normal seems to cause some problems, since Xbox
				// MIP selection is so different to Ps2. Limit the k value such that Xbox can never select smaller MIPs
				// earlier than it would do by default.
				if( pMat->K[pass] > 0.0f )
				{
					pMat->K[pass] = 0.0f;
				}
			}
			else
			{
				// ...otherwise just step past mipmap info.
				pMat->pTex[pass] = NULL;
				uint32 mip[4];
				MemoryRead( mip, sizeof( uint32 ) * 4, 1, p_data );
			}
		}

		// Generate material colors.
		for( uint32 pass = 0; pass < pMat->Passes; ++pass )
		{
			if ( ( mat_col[0].r == mat_col[pass].r ) &&
				 ( mat_col[0].g == mat_col[pass].g ) && 
				 ( mat_col[0].b == mat_col[pass].b ) && 
				 ( mat_col[0].a == mat_col[pass].a ) )
			{
				// Do nothing.
				pMat->matcol[pass] = (GXColor){128,128,128,255};
			}
			else
			{
				// Work out the texture color.
				GXColor tc;
				float base_r = (float)(mat_col[0].r > 0 ? mat_col[0].r : 1 );
				float base_g = (float)(mat_col[0].g > 0 ? mat_col[0].g : 1 );
				float base_b = (float)(mat_col[0].b > 0 ? mat_col[0].b : 1 );
				float base_a = (float)(mat_col[0].a > 0 ? mat_col[0].a : 1 );
				float r = ( ((float)mat_col[pass].r) / base_r ) * 128.0f;
				float g = ( ((float)mat_col[pass].g) / base_g ) * 128.0f;
				float b = ( ((float)mat_col[pass].b) / base_b ) * 128.0f;
				float a = ( ((float)mat_col[pass].a) / base_a ) * 255.0f;
				int _r = (int)r;
				int _g = (int)g;
				int _b = (int)b;
				int _a = (int)a;
				if ( _r > 255 ) _r = 255;
				if ( _g > 255 ) _g = 255;
				if ( _b > 255 ) _b = 255;
				if ( _a > 255 ) _a = 255;
				tc.r = (u8)( _r );
				tc.g = (u8)( _g );
				tc.b = (u8)( _b );
				tc.a = (u8)( _a );
				pMat->matcol[pass] = tc;
			}
		}

//		pMat->mp_display_list = NULL;
//		pMat->Initialize();
		pMat->uv_slot[0] = 0;
		pMat->uv_slot[1] = 1;
		pMat->uv_slot[2] = 2;
		pMat->uv_slot[3] = 3;
	}
	*pp_mem = p_data;

	return p_mat_array;
}

sMaterial *LoadMaterials( void *p_FH, Lst::HashTable< Nx::CTexture > *p_texture_table, int * p_num_materials )
{
	uint32 MMAG, MMIN, K, L, NumSeqs, seq, NumKeys;
	uint32 i;

	// Get number of materials.
	uint32 new_materials;
	File::Read( &new_materials, sizeof( uint32 ), 1, p_FH );
	
	if ( p_num_materials ) *p_num_materials = new_materials;

	// Create array of materials.
	sMaterial * p_mat_array = new sMaterial[new_materials];
	
	// Loop over materials.
	for( i = 0; i < new_materials; ++i )
	{
		// Create new material.
		sMaterial *pMat = &p_mat_array[i];
		
		pMat->m_num_wibble_vc_anims	= 0;
		pMat->mp_wibble_vc_params	= NULL;
		pMat->mp_wibble_vc_colors	= NULL;

		// Get material checksum.
		File::Read( &pMat->Checksum, sizeof( uint32 ), 1, p_FH );

		// Get number of passes.
		uint32 pass;
		File::Read( &pass, sizeof( uint32 ), 1, p_FH );
		pMat->Passes = pass;

		// Get alpha cutoff value.
		uint32 ac;
		File::Read( &ac, sizeof( uint32 ), 1, p_FH );
		pMat->AlphaCutoff = ac;		//( ac >= 128 ) ? 255 : ( ac * 2 );

		// Get sorted flag.
		bool sorted;
		File::Read( &sorted, sizeof( bool ), 1, p_FH );
		pMat->m_sorted = sorted;
		
		// Get draw order.
		File::Read( &pMat->m_draw_order, sizeof( float ), 1, p_FH );
		
		// Get backface cull flag.
		bool bfc;
		File::Read( &bfc, sizeof( bool ), 1, p_FH );
		pMat->m_no_bfc = bfc;

//		// Get grassify flag and (optionally) grassify data.
//		bool grassify;
//		File::Read( &grassify, sizeof( bool ), 1, p_FH );
//		pMat->m_grassify = (uint8)grassify;
//		if( grassify )
//		{
//			File::Read( &pMat->m_grass_height, sizeof( float ), 1, p_FH );
//			int layers;
//			File::Read( &layers, sizeof( int ), 1, p_FH );
//			pMat->m_grass_layers = (uint8)layers;
//		}

		GXColor mat_col[MAX_PASSES];

		pMat->m_pUVControl = NULL;
		for( uint32 pass = 0; pass < pMat->Passes; ++pass )
		{
			// Get texture checksum.
			uint32 TextureChecksum;
			File::Read( &TextureChecksum, sizeof( uint32 ), 1, p_FH );

			// Get material flags.
			File::Read( &pMat->Flags[pass], sizeof( uint32 ), 1, p_FH );

			// Get ALPHA register value.
			uint64 ra;
			File::Read( &ra, sizeof( uint64 ), 1, p_FH );
			pMat->blendMode[pass] = ra & 0xff;
			pMat->fixAlpha[pass] = ( ra >> 32 ) & 0xff;

			// Backface cull test - if this is an alpha blended material, turn off backface culling.
			if(( pass == 0 ) && (( ra & 0xFF ) != 0x00 ))
			{
				pMat->m_no_bfc = true;
			}

			// Get UV addressing types.
			uint32 u_addressing, v_addressing;
			File::Read( &u_addressing, sizeof( uint32 ), 1, p_FH );
			File::Read( &v_addressing, sizeof( uint32 ), 1, p_FH );
			pMat->UVAddressing[pass] = (( v_addressing << 1 ) | u_addressing );
			
			// Step past material colors, currently unsupported.
			float col[9];
			File::Read( col, sizeof( float ) * 9, 1, p_FH );

			mat_col[pass].r = (u8)((col[0] * 255.0f) + 0.5f);
			mat_col[pass].g = (u8)((col[1] * 255.0f) + 0.5f);
			mat_col[pass].b = (u8)((col[2] * 255.0f) + 0.5f);
			mat_col[pass].a = 255;

			// Read uv wibble data if present.
			if( pMat->Flags[pass] & MATFLAG_UV_WIBBLE )
			{
				if ( !pMat->m_pUVControl ) pMat->m_pUVControl = new sUVControl;
				File::Read( &( pMat->m_pUVControl->UVWibbleParams[pass] ), sizeof( sUVWibbleParams ), 1, p_FH );
			}

			// Step past vc wibble data, currently unsupported.
			if(( pass == 0 ) && ( pMat->Flags[0] & MATFLAG_VC_WIBBLE ))
			{
				File::Read( &NumSeqs, sizeof( uint32 ), 1, p_FH );
				pMat->m_num_wibble_vc_anims = NumSeqs;

				// Create sequence data array.
				pMat->mp_wibble_vc_params = new sVCWibbleParams[NumSeqs];
				
				// Create resultant color array.
				pMat->mp_wibble_vc_colors = new uint32[NumSeqs];

				for( seq = 0; seq < NumSeqs; ++seq )
				{ 
					File::Read( &NumKeys, sizeof( uint32 ), 1, p_FH );

					int phase;
					File::Read( &phase, sizeof( int ), 1, p_FH );

					// Create array for keyframes.
					pMat->mp_wibble_vc_params[seq].m_num_keyframes	= NumKeys;
					pMat->mp_wibble_vc_params[seq].m_phase			= phase;
					pMat->mp_wibble_vc_params[seq].mp_keyframes		= new sVCWibbleKeyframe[NumKeys];

					// Read keyframes into array.
					File::Read( pMat->mp_wibble_vc_params[seq].mp_keyframes, sizeof( sVCWibbleKeyframe ), NumKeys, p_FH );

					for ( uint lp = 0; lp < NumKeys; lp++ )
					{
						uint8 r = pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.r;
						uint8 g = pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.g;
						uint8 b = pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.b;
						uint8 a = pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.a;
						pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.r = b;
						pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.g = r;
						pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.b = g;
						pMat->mp_wibble_vc_params[seq].mp_keyframes[lp].m_color.a = a;
					}
				}
			}

			if( TextureChecksum )
			{
				// If textured, resolve texture checksum...
				Nx::CNgcTexture *p_ngc_texture = static_cast( p_texture_table->GetItem( TextureChecksum ) );
				sTexture *pTex = ( p_ngc_texture ) ? p_ngc_texture->GetEngineTexture() : NULL;

				// Bail if checksum not found.
				if( pTex == NULL )
				{
					Dbg_Message( "error: couldn't find texture checksum %08X\n", TextureChecksum );
//					exit( 1 );
					pMat->pTex[pass] = NULL;
				}
				else
				{
					// Set texture pointer.
					pMat->pTex[pass] = pTex;
				}

				// Get mipmap info.
				File::Read( &MMAG, sizeof( uint32 ), 1, p_FH );
				File::Read( &MMIN, sizeof( uint32 ), 1, p_FH );

				File::Read( &K, sizeof( uint32 ), 1, p_FH );
				File::Read( &L, sizeof( uint32 ), 1, p_FH );
				
				// Default PS2 value for K appears to be -8.0f - we are interested in deviations from this value.
				pMat->K[pass]	= ( *(float*)&K ) + 8.0f;
			
				// Dave note 08/03/02 - having MIPs selected earlier than normal seems to cause some problems, since Xbox
				// MIP selection is so different to Ps2. Limit the k value such that Xbox can never select smaller MIPs
				// earlier than it would do by default.
				if( pMat->K[pass] > 0.0f )
				{
					pMat->K[pass] = 0.0f;
				}
			}
			else
			{
				// ...otherwise just step past mipmap info.
				pMat->pTex[pass] = NULL;
				uint32 mip[4];
				File::Read( mip, sizeof( uint32 ) * 4, 1, p_FH );
			}
		}

		// Generate material colors.
		for( uint32 pass = 0; pass < pMat->Passes; ++pass )
		{
			if ( ( mat_col[0].r == mat_col[pass].r ) &&
				 ( mat_col[0].g == mat_col[pass].g ) && 
				 ( mat_col[0].b == mat_col[pass].b ) && 
				 ( mat_col[0].a == mat_col[pass].a ) )
			{
				// Do nothing.
				pMat->matcol[pass] = (GXColor){128,128,128,255};
			}
			else
			{
				// Work out the texture color.
				GXColor tc;
				float base_r = (float)(mat_col[0].r > 0 ? mat_col[0].r : 1 );
				float base_g = (float)(mat_col[0].g > 0 ? mat_col[0].g : 1 );
				float base_b = (float)(mat_col[0].b > 0 ? mat_col[0].b : 1 );
				float base_a = (float)(mat_col[0].a > 0 ? mat_col[0].a : 1 );
				float r = ( ((float)mat_col[pass].r) / base_r ) * 128.0f;
				float g = ( ((float)mat_col[pass].g) / base_g ) * 128.0f;
				float b = ( ((float)mat_col[pass].b) / base_b ) * 128.0f;
				float a = ( ((float)mat_col[pass].a) / base_a ) * 255.0f;
				int _r = (int)r;
				int _g = (int)g;
				int _b = (int)b;
				int _a = (int)a;
				if ( _r > 255 ) _r = 255;
				if ( _g > 255 ) _g = 255;
				if ( _b > 255 ) _b = 255;
				if ( _a > 255 ) _a = 255;
				tc.r = (u8)( _r );
				tc.g = (u8)( _g );
				tc.b = (u8)( _b );
				tc.a = (u8)( _a );
				pMat->matcol[pass] = tc;
			}
		}

//		pMat->mp_display_list = NULL;
//		pMat->Initialize();
		pMat->uv_slot[0] = 0;
		pMat->uv_slot[1] = 1;
		pMat->uv_slot[2] = 2;
		pMat->uv_slot[3] = 3;
	}

	return p_mat_array;
}

} // namespace NxNgc



================================================
FILE: Code/Gfx/NGC/NX/material.h
================================================
#ifndef __MATERIAL_H
#define __MATERIAL_H

#include 
#include "texture.h"
#include 

//#define __USE_MAT_DL__		// Comment out to save memory.

namespace NxNgc
{

// Material Flags
#define MATFLAG_UV_WIBBLE   (1<<0)
#define MATFLAG_VC_WIBBLE   (1<<1)
#define MATFLAG_TEXTURED    (1<<2)
#define MATFLAG_ENVIRONMENT (1<<3)
#define MATFLAG_DECAL       (1<<4)
#define MATFLAG_SMOOTH      (1<<5)
#define MATFLAG_TRANSPARENT (1<<6)


const uint32 MAX_PASSES = 4;

	
struct sUVWibbleParams
{
	float	m_UVel;
	float	m_VVel;
	float	m_UFrequency;
	float	m_VFrequency;
	float	m_UAmplitude;
	float	m_VAmplitude;
	float	m_UPhase;
	float	m_VPhase;
};

struct sUVControl
{
	sUVWibbleParams	UVWibbleParams[MAX_PASSES];         // 128
	float			UVWibbleOffset[MAX_PASSES][2];		// 32
};

struct sVCWibbleKeyframe
{
	int			m_time;
	Image::RGBA	m_color;
};



struct sVCWibbleParams
{
	uint32				m_num_keyframes;
	int					m_phase;
	sVCWibbleKeyframe	*mp_keyframes;
};




struct sMaterial
{
//	sMaterial( void );
//	~sMaterial( void );

	void			Submit( uint32 mesh_flags, GXColor mesh_color );
	void			Initialize( uint32 mesh_flags, GXColor mesh_color );
	void			figure_wibble_uv( void );
	void			figure_wibble_vc( void );
		
	uint32			Checksum;							// 4
	uint8			Passes;                             // 1
	uint8			m_sorted;                           // 1
	uint8			m_no_bfc;                           // 1
	uint8			AlphaCutoff;                        // 1
	float			m_draw_order;                       // 4
	uint32			Flags[MAX_PASSES];                  // 16
	sTexture*		pTex[MAX_PASSES];                   // 16
	uint8			blendMode[MAX_PASSES];              // 4
	uint8			fixAlpha[MAX_PASSES];               // 4
	uint8			UVAddressing[MAX_PASSES];           // 4
	float			K[MAX_PASSES];						// 16

	char			uv_slot[MAX_PASSES];				// 4
	GXColor			matcol[4];							// 16

#ifdef __USE_MAT_DL__
	uint8*			mp_display_list;					// 4
	uint16			m_display_list_size;				// 2
#endif		// __USE_MAT_DL__
	uint8			m_wibble_uv_update;					// 1
	uint8			m_num_wibble_vc_anims;				// 1
	uint16			pad;
//	uint8			m_grassify;
//	uint8			m_grass_layers;
//	float			m_grass_height;

	sUVControl*		m_pUVControl;						// 4

	float			m_wibble_uv_time;					// 4
	sVCWibbleParams	*mp_wibble_vc_params;				// 4
	uint32			*mp_wibble_vc_colors;				// 4 (116) Max of eight banks of vertex color wibble infortation.
};


sMaterial * LoadMaterials( void *p_FH, Lst::HashTable< Nx::CTexture > *p_texture_table, int * p_num_materials );
sMaterial * LoadMaterialsFromMemory( void **pp_mem, Lst::HashTable< Nx::CTexture > *p_texture_table, int * p_num_materials );
void InitializeMaterial( sMaterial* p_material );

//extern Lst::HashTable< sMaterial > *pMaterialTable;
extern uint32 NumMaterials;

} // namespace NxNgc

#endif // __MATERIAL_H




================================================
FILE: Code/Gfx/NGC/NX/mesh.cpp
================================================
#include 
#include 

#include 
#include 
#include "import.h"
#include "scene.h"
#include "mesh.h"
#include "nx_init.h"
#include "sys/ngc/p_aram.h"
#include "sys/ngc/p_dma.h"
#include 

#include 

extern bool gCorrectColor;

namespace NxNgc
{

extern sint32 *pVertex;
int TotalNumVertices;

uint NumMeshes;


// Globals used for cutscene head scaling.
bool			s_meshScalingEnabled	= false;
char*			s_pWeightIndices		= NULL;
float*			s_pWeights				= NULL;
Mth::Vector*	s_pBonePositions		= NULL;
Mth::Vector*	s_pBoneScales			= NULL;
int				s_currentVertIndex		= 0;


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SetMeshScalingParameters( Nx::SMeshScalingParameters* pParams )
{
	Dbg_Assert( pParams );

	s_meshScalingEnabled	= true;
	s_pWeights				= pParams->pWeights;
	s_pWeightIndices		= pParams->pWeightIndices;
	s_pBoneScales			= pParams->pBoneScales;
	s_pBonePositions		= pParams->pBonePositions;
	s_currentVertIndex		= 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void DisableMeshScaling( void )
{
	s_meshScalingEnabled	= false;
	s_pWeights				= NULL;
	s_pWeightIndices		= NULL;
	s_pBoneScales			= NULL;
	s_pBonePositions		= NULL;
	s_currentVertIndex		= 0;
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline Mth::Vector get_bone_scale( int bone_index )
{
	Mth::Vector returnVec( 1.0f, 1.0f, 1.0f, 1.0f );

	if( bone_index >= 29 && bone_index <= 33 )
	{
		// this only works with the thps5 skeleton, whose
		// head bones are between 29 and 33...
		// (eventually, we can remove the subtract 29
		// once the exporter is massaging the data correctly)
		returnVec = s_pBoneScales[ bone_index - 29 ];
		
		// Y & Z are reversed...  odd!
		Mth::Vector tempVec = returnVec;
		returnVec[Y] = tempVec[Z];
		returnVec[Z] = tempVec[Y];
	}
	else if( bone_index == -1 )
	{
		// implies that it's not weighted to a bone
		return returnVec;
	}
	else
	{
		// implies that it's weighted to the wrong bone
		return returnVec;
	}
	return returnVec;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline Mth::Vector get_bone_pos( int bone_index )
{
	Mth::Vector returnVec( 0.0f, 0.0f, 0.0f, 1.0f );
	
	if( bone_index >= 29 && bone_index <= 33 )
	{
		// this only works with the thps5 skeleton, whose
		// head bones are between 29 and 33...
		// (eventually, we can remove the subtract 29
		// once the exporter is massaging the data correctly)
		returnVec = s_pBonePositions[ bone_index - 29 ];
	}
	else if( bone_index == -1 )
	{
		// implies that it's not weighted to a bone
		return returnVec;
	}
	else
	{
		// implies that it's weighted to the wrong bone
		return returnVec;
	}
	returnVec[W] = 1.0f;

	return returnVec;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void ApplyMeshScaling( NxNgc::sObjectHeader * p_object )
{
	if( s_meshScalingEnabled )
	{
		float orig_pool[3*1500];
		int pool_size = 0;
	
		// First, build an array from the skin data.
	
		// Singles.
		uint32*	p32 = (uint32 *)p_object->m_skin.p_data;
		for ( uint32 lp = 0; lp < p_object->m_num_single_lists; lp++ ) {
			uint32		pairs = *p32++;
			/*uint32		mtx = **/p32++;
			s16*		p_pn = (s16 *)p32;
	
			for ( uint32 lp2 = 0; lp2 < pairs; lp2++ )
			{
				orig_pool[(pool_size*3)+0] = (float)p_pn[(lp2*6)+0] / (float)(1 << 6);
				orig_pool[(pool_size*3)+1] = (float)p_pn[(lp2*6)+1] / (float)(1 << 6);
				orig_pool[(pool_size*3)+2] = (float)p_pn[(lp2*6)+2] / (float)(1 << 6);
				pool_size++;
			}
	
			p32 = (uint32 *)&p_pn[pairs*6];
		}
	
		// Doubles.
		for ( uint32 lp = 0; lp < p_object->m_num_double_lists; lp++ ) {
			uint32		pairs = *p32++;
			/*uint32		mtx = **/p32++;
			s16*		p_pn = (s16 *)p32;
			s16*		p_weight = (s16 *)&p_pn[(6*pairs)];
	
			for ( uint32 lp2 = 0; lp2 < pairs; lp2++ )
			{
				orig_pool[(pool_size*3)+0] = (float)p_pn[(lp2*6)+0] / (float)(1 << 6);
				orig_pool[(pool_size*3)+1] = (float)p_pn[(lp2*6)+1] / (float)(1 << 6);
				orig_pool[(pool_size*3)+2] = (float)p_pn[(lp2*6)+2] / (float)(1 << 6);
				pool_size++;
			}
	
			p32 = (uint32 *)&p_weight[pairs*2];
		}
	
		// Now scale the verts.
		float * p_vertices = orig_pool;
		for( int v = 0; v < pool_size; ++v, p_vertices += 3 )
		{
			float x = p_vertices[0];
			float y = p_vertices[1];
			float z = p_vertices[2];

    		Mth::Vector origPos( x, y, z, 1.0f );

			Mth::Vector bonePos0 = get_bone_pos( s_pWeightIndices[v * 3] );
			Mth::Vector bonePos1 = get_bone_pos( s_pWeightIndices[v * 3 + 1] );
			Mth::Vector bonePos2 = get_bone_pos( s_pWeightIndices[v * 3 + 2] );

			// Need to scale each vert relative to its parent bone.
			Mth::Vector localPos0 = origPos - bonePos0;
			Mth::Vector localPos1 = origPos - bonePos1;
			Mth::Vector localPos2 = origPos - bonePos2;

			localPos0.Scale( get_bone_scale( s_pWeightIndices[v * 3] ) );
			localPos1.Scale( get_bone_scale( s_pWeightIndices[v * 3 + 1] ) );
			localPos2.Scale( get_bone_scale( s_pWeightIndices[v * 3 + 2] ) );

			localPos0 += bonePos0;
			localPos1 += bonePos1;
			localPos2 += bonePos2;
			
			Mth::Vector scaledPos = ( localPos0 * s_pWeights[v * 3] ) +
									( localPos1 * s_pWeights[v * 3 + 1] ) +
									( localPos2 * s_pWeights[v * 3 + 2] );

			p_vertices[0] = scaledPos[X];
			p_vertices[1] = scaledPos[Y];
			p_vertices[2] = scaledPos[Z];
		} 	

		// Now copy back.
		pool_size = 0;
	
		// Singles.
		p32 = (uint32 *)p_object->m_skin.p_data;
		for ( uint32 lp = 0; lp < p_object->m_num_single_lists; lp++ ) {
			uint32		pairs = *p32++;
			/*uint32		mtx = **/p32++;
			s16*		p_pn = (s16 *)p32;
	
			for ( uint32 lp2 = 0; lp2 < pairs; lp2++ )
			{
				p_pn[(lp2*6)+0] = (s16)(orig_pool[(pool_size*3)+0] * (float)(1 << 6 ));
				p_pn[(lp2*6)+1] = (s16)(orig_pool[(pool_size*3)+1] * (float)(1 << 6 ));
				p_pn[(lp2*6)+2] = (s16)(orig_pool[(pool_size*3)+2] * (float)(1 << 6 ));
				pool_size++;
			}
	
			p32 = (uint32 *)&p_pn[pairs*6];
		}
	
		// Doubles.
		for ( uint32 lp = 0; lp < p_object->m_num_double_lists; lp++ ) {
			uint32		pairs = *p32++;
			/*uint32		mtx = **/p32++;
			s16*		p_pn = (s16 *)p32;
			s16*		p_weight = (s16 *)&p_pn[(6*pairs)];
	
			for ( uint32 lp2 = 0; lp2 < pairs; lp2++ )
			{
				p_pn[(lp2*6)+0] = (s16)(orig_pool[(pool_size*3)+0] * (float)(1 << 6 ));
				p_pn[(lp2*6)+1] = (s16)(orig_pool[(pool_size*3)+1] * (float)(1 << 6 ));
				p_pn[(lp2*6)+2] = (s16)(orig_pool[(pool_size*3)+2] * (float)(1 << 6 ));
				pool_size++;
			}
	
			p32 = (uint32 *)&p_weight[pairs*2];
		}
	
		// Accumulation.
		for ( uint32 lp = 0; lp < p_object->m_num_add_lists; lp++ ) {
			uint32		singles = *p32++;
			/*uint32		mtx = **/p32++;
			s16*		p_pn = (s16 *)p32;
			s16*		p_weight = (s16 *)&p_pn[(6*singles)];
			uint16*		p_index = (uint16 *)&p_weight[singles];
	
			for ( uint32 lp2 = 0; lp2 < singles; lp2++ )
			{
				int index = p_index[lp2];
				p_pn[(lp2*6)+0] = (s16)(orig_pool[(index*3)+0] * (float)(1 << 6 ));
				p_pn[(lp2*6)+1] = (s16)(orig_pool[(index*3)+1] * (float)(1 << 6 ));
				p_pn[(lp2*6)+2] = (s16)(orig_pool[(index*3)+2] * (float)(1 << 6 ));
			}
	
			p32 = (uint32 *)&p_index[singles];
		}
	}
}








sMesh::sMesh( void )
{
	m_flags				= 0;
	SetActive( true );
//	mp_vc_wibble_data	= NULL;
//
//	m_offset_x = 0.0f;
//	m_offset_y = 0.0f;
//	m_offset_z = 0.0f;

	m_visibility_mask = 0xff;

	mp_dl = NULL;

	// It's the end of teh project - time for a hack!!!
	Mdl::Skate * p_skate_mod =  Mdl::Skate::Instance();
	if (p_skate_mod->m_requested_level == CRCD(0x9f2bafb7,"load_skateshop"))
	{
		m_base_color.r = 64;
		m_base_color.g = 64;
		m_base_color.b = 64;
		m_base_color.a = 128;
	}
	else
	{
		m_base_color.r = 128;
		m_base_color.g = 128;
		m_base_color.b = 128;
		m_base_color.a = 128;
	}
}



sMesh::~sMesh( void )
{
	if( !( m_flags & MESH_FLAG_IS_INSTANCE ))
	{
		if( m_flags & MESH_FLAG_IS_CLONED )
		{
//			if ( m_localMeshIndex == 0 )
//			{
//				if( mp_posBuffer )
//				{
//					delete [] mp_posBuffer;
//					mp_posBuffer = NULL;
//				}
//				if( mp_normBuffer )
//				{
//					delete [] mp_normBuffer;
//					mp_normBuffer = NULL;
//				}
//				if( mp_colBuffer )
//				{
//					delete [] mp_colBuffer;
//					mp_colBuffer = NULL;
//				}
//			}
		}
		else
		{
//			if ( mp_dl->mp_texture_dl )
//			{
//				delete [] mp_dl->mp_texture_dl;
//			}

//			if( mp_display_list	)
//			{
//				delete mp_display_list;
//				mp_display_list = NULL;
//			}

//			if ( mp_dl && mp_dl->dl_owner )
//			{
//				if ( mp_dl->mp_material_dl )
//				delete [] mp_dl->mp_material_dl;
//			}

//			// If we're the 0th mesh, we can delete the shared pools.
//			if ( m_localMeshIndex == 0 )
//			{
//				if( mp_posBuffer )
//				{
//					delete [] mp_posBuffer;
//					mp_posBuffer = NULL;
//				}
//				if( mp_normBuffer )
//				{
//					delete [] mp_normBuffer;
//					mp_normBuffer = NULL;
//				}
//				if( mp_uvBuffer )
//				{
//					delete [] mp_uvBuffer;
//					mp_uvBuffer = NULL;
//				}
//				if( mp_envBuffer )
//				{
//					delete [] mp_envBuffer;
//					mp_envBuffer = NULL;
//				}
//				if( mp_colBuffer )
//				{
//					delete [] mp_colBuffer;
//					mp_colBuffer = NULL;
//				}
//				if( mp_doubleWeight )
//				{
//					delete [] mp_doubleWeight;
//					mp_doubleWeight = NULL;
//				}
//				if( mp_vc_wibble_data )
//				{
//					delete mp_vc_wibble_data;
//					mp_vc_wibble_data = NULL;
//				}
//			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::wibble_normals( void )
{
//	// Angle in the range [-PI/16, PI/16], period is 1 second.
//	float time = (float)Tmr::GetTime() * 0.0005f;
//
//	if( m_flags & MESH_FLAG_WATER_HACK )
//	{
//		BYTE		*p_byte;
//		float		*p_normal;
//		float		*p_pos;
//		mp_vertex_buffer2->Lock( 0, 0, &p_byte, 0 );
//		p_pos		= (float*)( p_byte + 0 );
//		p_normal	= (float*)( p_byte + m_normal_offset );
//
//		for( uint32 i = 0; i < m_num_vertices; ++i )
//		{
//			float x				= p_pos[0] - m_sphere[0];
//			float z				= p_pos[2] - m_sphere[2];
//			
//			float time_offset_x	= time + (( x / m_sphere_radius ) * 0.5f );
//			float time_offset_z	= time + (( z / m_sphere_radius ) * 0.5f );
//
//			float angle_x		= ( Mth::PI * ( 1.0f / 64.0f ) * (float)fabs( sinf( time_offset_x * Mth::PI ))) - ( Mth::PI * ( 1.0f / 128.0f ));
//			float angle_z		= ( Mth::PI * ( 1.0f / 64.0f ) * (float)fabs( sinf( time_offset_z * Mth::PI ))) - ( Mth::PI * ( 1.0f / 129.0f ));
//			
//			Mth::Vector	n( sinf( angle_x ), cosf(( angle_x + angle_z ) * 0.5f ), sinf( angle_z ));
//			n.Normalize();
//			
//			p_normal[0]			= n[X];
//			p_normal[1]			= n[Y];
//			p_normal[2]			= n[Z];
//			
//			p_pos				= (float*)((BYTE*)p_pos + m_vertex_stride );
//			p_normal			= (float*)((BYTE*)p_normal + m_vertex_stride );
//		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::wibble_vc( void )
{
//	if( mp_vc_wibble_data )
//	{
//		// Grab byte pointer to current 'write' vertex buffer.
////		GXColor		*p_byte = (char *)mp_colBuffer;
//		uint32		*p_color = mp_colBuffer;
////		p_color = (GXColor*)( p_byte + m_diffuse_offset );
//
//		// Scan through each vertex, setting the new color.
//		for( uint32 i = 0; i < m_num_vertex; ++i )
//		{
//			// An index of zero means no update for this vert.
//			uint32 index	= mp_vc_wibble_data[i];
//			if( ( index > 0 ) && ( m_flags & MESH_FLAG_VERTEX_COLOR_WIBBLE ) && mp_material->mp_wibble_vc_colors )
//			{
//				*p_color	= mp_material->mp_wibble_vc_colors[index - 1];
//			}
//			p_color++;
//		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::Submit( void )
{
//	wibble_vc();
////	wibble_normals();
//
//	if ( mp_display_list )
//	{
//		GXCallDisplayList ( mp_display_list, m_display_list_size ); 
//	}
////	// Horribly inefficient, but will have to do for now until we get bucket rendering going.
////	if( mp_material )
////	{
////		mp_material->Submit();
////	}
////	
////	// Set pixel and vertex shader.
////	D3DDevice_SetPixelShader( m_pixel_shader );
////	D3DDevice_SetVertexShader( m_fvf_flags );
////
////	// Set the stream source.
////	D3DDevice_SetStreamSource( 0, mp_vertex_buffer, m_vertex_stride );
////
////	// Submit.
////	D3DDevice_DrawIndexedVertices( m_primitive_type, m_num_indices, mp_index_buffer );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMesh *sMesh::Clone( bool instance )
{
	sMesh *p_clone = new sMesh();

	// Copy over basic details.
	memcpy( p_clone, this, sizeof( sMesh ));
	p_clone->m_flags &= ~(MESH_FLAG_CLONED_POS|MESH_FLAG_CLONED_COL);

	if( instance )
	{
		p_clone->m_flags |= MESH_FLAG_IS_INSTANCE;
	}
	else
	{
		p_clone->m_flags |= MESH_FLAG_IS_CLONED;

		// Clone the display list.
		Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
		p_clone->mp_dl = (NxNgc::sDLHeader*)new char[sizeof(NxNgc::sDLHeader)+mp_dl->m_size];
		Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
		memcpy( p_clone->mp_dl, mp_dl, sizeof(NxNgc::sDLHeader)+mp_dl->m_size );
		p_clone->m_flags |= NxNgc::sMesh::MESH_FLAG_CLONED_DL;



//		p_clone->m_flags |= MESH_FLAG_IS_INSTANCE;

//		if ( m_localMeshIndex == 0 )
//		{
//			if ( mp_posBuffer )
//			{
//#ifdef SHORT_VERT
//				p_clone->mp_posBuffer = new s16[3*m_num_vertex];
//				memcpy( p_clone->mp_posBuffer, mp_posBuffer, 3 * m_num_vertex * sizeof( s16 ) );
//				DCFlushRange( p_clone->mp_posBuffer, 3 * m_num_vertex * sizeof( s16 ) );
//#else
//				p_clone->mp_posBuffer = new float[3*m_num_vertex];
//				memcpy( p_clone->mp_posBuffer, mp_posBuffer, 3 * m_num_vertex * sizeof( float ) );
//				DCFlushRange( p_clone->mp_posBuffer, 3 * m_num_vertex * sizeof( float ) );
//#endif		// SHORT_VERT 
//			}
//			if ( mp_normBuffer )
//			{
//				p_clone->mp_normBuffer = new s16[3*m_num_vertex];
//				memcpy( p_clone->mp_normBuffer, mp_normBuffer, 3 * m_num_vertex * sizeof( s16 ) );
//				DCFlushRange( p_clone->mp_normBuffer, 3 * m_num_vertex * sizeof( s16 ) );
//			}
//
//			if ( mp_colBuffer )
//			{
//				p_clone->mp_colBuffer = new uint32[m_num_vertex];
//				memcpy( p_clone->mp_colBuffer, mp_colBuffer, m_num_vertex * sizeof( uint32 ) );
//				DCFlushRange( p_clone->mp_colBuffer, m_num_vertex * sizeof( uint32 ) );
//			}
//		}
	}
	return p_clone;

//	Dbg_MsgAssert( false, ( "Not yet implmented!!!!!!!!" ) );
//	return NULL;
}



///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//void sMesh::Initialize( int num_vertices,
//						float* p_positions,
//#ifdef SHORT_VERT
//						s16* p_positions16,
//						int shift,
//						float off_x,
//						float off_y,
//						float off_z,
//#endif		// SHORT_VERT
//						s16* p_normals,
//						float* p_tex_coords,
//						float* p_env_buffer,
//						int num_tc_sets,
//						uint32* p_colors,
//						int num_indices,
//						uint16* p_pos_indices,
//						uint16* p_col_indices,
//						uint16* p_uv_indices,
//						unsigned long material_checksum,
//						void *p_scene,
//						int num_double,
//						uint32 * p_double,
//						int num_single,
//						int localMeshIndex,
//						GXPrimitive primitive_type,
//						int bone_idx,
//						char *p_vc_wibble_anims )
//
//{
//	m_num_indices		= 0;
//	m_vertex_stride		= 0;
//	m_num_vertex		= num_vertices;
//
//	m_bone_idx			= bone_idx;
//
////	m_primitive_type	= D3DPT_TRIANGLESTRIP;
////	m_fvf_flags			= 0;
////	m_pixel_shader		= 0;
//	
////	mp_posBuffer = NULL;	//p_positions;
////	mp_normBuffer = p_normals;
////	mp_uvBuffer = NULL;	//p_tex_coords;
////	mp_colBuffer = NULL;	//p_colors;
////	mp_index_buffer = NULL;	//p_indices;
//
//	mp_normBuffer = p_normals;
//	mp_uvBuffer = p_tex_coords;
//	mp_colBuffer = p_colors;
//	m_num_uv_sets = num_tc_sets;
//
//	m_numDouble = num_double;
//	m_numSingle = num_single;
//	mp_doubleWeight = p_double;
//
//	m_localMeshIndex = localMeshIndex;
//
//	// First thing to do is grab the material pointer for this mesh.
//	mp_material			= ((sScene*)p_scene )->GetMaterial( material_checksum );
////	if( mp_material == NULL )
////	{
////		exit( 0 );
////
////		// Try with the dummy material for now.
////		// mp_material = GetMaterial( 0 );
////	}
//	
////	if( num_indices == 0 )
////	{
////		return;
////	}
////	
////	// Figure the min and max indices for this mesh.
////	uint16 min_index	= p_indices[0];
////	uint16 max_index	= p_indices[0];
////	for( int i = 1; i < num_indices; ++i )
////	{
////		if( p_indices[i] > max_index )
////		{
////			max_index = p_indices[i];
////		}
////		else if( p_indices[i] < min_index )
////		{
////			min_index = p_indices[i];
////		}
////	}
//	
//	// See if any vertex color alpha valus are less than any of the texture's cutoff values.
//	// If so, make sure that the texture is specified as having holes.
//	for ( int lp = 0; lp < num_vertices; lp++ )
//	{
//		if ( ( p_colors[lp] & 255 ) < mp_material->AlphaCutoff )
//		{
//			for ( int pass = 0; pass < mp_material->Passes; pass++ )
//			{
////				if ( mp_material->pTex[pass] ) mp_material->pTex[pass]->HasHoles = 1;
//				if ( mp_material->pTex[pass] )
//				{
//					if ( !mp_material->pTex[pass]->HasHoles )
//					{
//						mp_material->pTex[pass]->HasHoles = 1;
//						OSReport( "Material found that was incorrectly flagged.\n" );
//					}
//				}
//			}
//			break;
//		}
//	}
//
//	// See if any of the material layers require environment mapping.
//	mp_envBuffer = NULL;
//	for ( int pass = 0; pass < mp_material->Passes; pass++ )
//	{
//		if ( mp_material->Flags[pass] & MATFLAG_ENVIRONMENT )
//		{
//			if ( p_env_buffer )
//			{
//				// Already allocated one, so just assign it.
//				mp_envBuffer = p_env_buffer;
//			}
//			else
//			{
//				// Has environment mapping, so allocate an environment map uv buffer.
//				mp_envBuffer = new float[2*num_vertices];
//			}
//			// Break so that we only allocate once.
//			break;
//		}
//	}
//
//	// Set the bounding box.
//	if ( p_positions )
//	{
//		// Non-skinned.
//		for( int v = 0; v < num_vertices; ++v )
//		{
//			// Do bounding box setting.
//			Mth::Vector p( p_positions[(v*3)+0], p_positions[(v*3)+1], p_positions[(v*3)+2] );
//			m_bbox.AddPoint( p );
//		}
//	}
//	else
//	{
//		// Skinned.
//		uint32*	p32 = p_double;
//		for ( int lp = 0; lp < num_double; lp++ ) {
//			uint32		pairs = *p32++;
//			/*uint32		mtx = **/p32++;
//			float*		p_pn = (float *)p32;
//			float*			p_weight = (float *)&p_pn[(6*pairs)];
//
//			for( uint v = 0; v < pairs; ++v )
//			{
//				// Do bounding box setting.
//				Mth::Vector p( p_pn[(v*6)+0], p_pn[(v*6)+1], p_pn[(v*6)+2] );
//				m_bbox.AddPoint( p );
//			}
//
//			p32 = (uint32 *)&p_weight[pairs*2];
//		}
//	}
//	float x = ( m_bbox.GetMax().GetX() + m_bbox.GetMin().GetX() ) / 2;
//	float y = ( m_bbox.GetMax().GetY() + m_bbox.GetMin().GetY() ) / 2;
//	float z = ( m_bbox.GetMax().GetZ() + m_bbox.GetMin().GetZ() ) / 2;
//	float r = ( m_bbox.GetMax().GetX() - m_bbox.GetMin().GetX() ) * ( m_bbox.GetMax().GetX() - m_bbox.GetMin().GetX() );
//	r += ( m_bbox.GetMax().GetY() - m_bbox.GetMin().GetY() ) * ( m_bbox.GetMax().GetY() - m_bbox.GetMin().GetY() );
//	r += ( m_bbox.GetMax().GetZ() - m_bbox.GetMin().GetZ() ) * ( m_bbox.GetMax().GetZ() - m_bbox.GetMin().GetZ() );
//	r = sqrtf( r );
//	r = r / 2.0f;
//	m_sphere[0] = x;
//	m_sphere[1] = y;
//	m_sphere[2] = z;
//	m_sphere[3] = r;
//
//	// Set the position buffer & offset.
//#ifdef SHORT_VERT
//	mp_posBuffer = p_positions16;
//#else
//	mp_posBuffer = p_positions;
//#endif		// SHORT_VERT
//
//	if ( p_positions )
//	{
//#ifdef SHORT_VERT
//		// Non-skinned.
//		switch ( shift )
//		{
//			case 1:
//				m_vertex_format = GX_VTXFMT2;
//				break;
//			case 2:
//				m_vertex_format = GX_VTXFMT3;
//				break;
//			case 3:
//				m_vertex_format = GX_VTXFMT4;
//				break;
//			case 4:
//				m_vertex_format = GX_VTXFMT5;
//				break;
//			case 5:
//				m_vertex_format = GX_VTXFMT6;
//				break;
//			case 6:
//				m_vertex_format = GX_VTXFMT7;
//				break;
//			case 0:
//			default:
//				m_vertex_format = GX_VTXFMT1;
//				break;
//		}
//#else
//		m_vertex_format = GX_VTXFMT6;
//#endif		// SHORT_VERT
//	}
//	else
//	{
//		// Skinned.
//		m_vertex_format = GX_VTXFMT7;
//	}
//
//#ifdef SHORT_VERT
//	m_offset_x = off_x;
//	m_offset_y = off_y;
//	m_offset_z = off_z;
//#endif		// SHORT_VERT
//
////	if( max_index >= num_vertices )
////	{
////		// Error!
////		exit( 0 );
////	}
//	
//	// Now figure the total number of vertices required for this mesh, to span the min->max indices.
////	uint16 vertices_for_this_mesh = max_index - min_index + 1;
//
//	// Create the index buffer (should be 16byte aligned for best performance).
////	mp_index_buffer	= new uint16[num_indices];
//
//	// Set our count of the number of indices.
//	m_num_indices = num_indices - 2;		// Only used for skinned - subtract count & trailing 0.
//
//	// Copy in index data, normaling the indices for this vertex buffer (i.e. so the lowest index will reference
//	// vertex 0 in the buffer built specifically for this mesh).
////	for( int i = 0; i < num_indices; ++i )
////	{
////		mp_index_buffer[i] = p_indices[i];	// - min_index;
////	}
//
//	// Use the material flags to figure the vertex format.
//	int vertex_size			= 3 * sizeof( float );
//
//	// Include weights (for weighted animation) if present.
////	if( p_weights )
////	{
////		Dbg_Assert( p_matrix_indices );
////		vertex_size	+= sizeof( float ) * 4;
////	}
//
//	// Include indices (for weighted animation) if present.
////	if( p_matrix_indices )
////	{
////		Dbg_Assert( p_weights );
////		vertex_size	+= sizeof( uint16 ) * 4;
////	}
//	
//	// Texture coordinates.
//	uint32	tex_coord_pass	= 0;
////	bool	env_mapped		= false;
//	if( p_tex_coords )
//	{
//		for( uint32 pass = 0; pass < mp_material->Passes; ++pass )
//		{
////			if( mp_material->Flags[pass] & MATFLAG_ENVIRONMENT )
////			{
////				env_mapped		= true;
////			}
//
//			// Only need UV's for this stage if it is *not* environment mapped.
//			if(( mp_material->pTex[pass] ) /*&& ( !( mp_material->Flags[pass] & MATFLAG_ENVIRONMENT ))*/)
//			{
//				// Will need uv for this pass and all before it.
//				tex_coord_pass	= pass + 1; 
//			}
//		}
//	}
//	else
//	{
//		for( uint32 pass = 0; pass < mp_material->Passes; ++pass )
//		{
////			if( mp_material->Flags[pass] & MATFLAG_ENVIRONMENT )
////			{
////				env_mapped		= true;
////			}
//		}
//	}
//
//	if( tex_coord_pass > 0 )
//	{
//		vertex_size += 2 * sizeof( float ) * tex_coord_pass;
//	}
//
//	// Assume no normals for now.
//	if( 0 )
//	{
//		// We want vertex colors in this mesh,
//		if( 0 )
//		{
//			// The raw vertex data does not contain vertex colors.
//			exit( 0 );
//		}
//		else
//		{
//			// The raw vertex data does contain vertex colors.
//		}
//	}
//
//	bool use_colors = false;
//	if( p_colors )
//	{
//		// We want vertex colors in this mesh,
//		if( 0 )
//		{
//			// The raw vertex data does not contain vertex colors.
//			exit( 0 );
//		}
//		else
//		{
//			// The raw vertex data does contain vertex colors.
//			vertex_size	+= sizeof( uint32 );
//			use_colors	= true;
//		}
//	}
//	
//	// Create the vertex color wibble array if data is present.
//	if( p_vc_wibble_anims )
//	{
//		mp_vc_wibble_data = p_vc_wibble_anims;
//	}
//
////	if ( p_double )
////	{
////		mp_doubleWeight = 1;
////	}
////	else
////	{
////		mp_doubleWeight = 0;
////	}
//
//	m_primitive_type = primitive_type;
//
//	Build( num_indices, p_pos_indices, p_col_indices, p_uv_indices );
//
//	// Find the index list in the display list data.
//	mp_index_buffer = NULL;
//	{
//		unsigned char * p8;
//		unsigned char search[3];
//
//		p8 = (unsigned char *)mp_display_list;
//
//		search[0] = *p8++;
//		search[1] = *p8++;
//		for ( int ss = 2; ss < m_display_list_size; ss++ ) {
//			search[2] = *p8++;
//
//			if (	( ( search[0] & 0xf8 ) == m_primitive_type ) &&
//					( ( ( search[1] << 8 ) | search[2] ) == (int)*p_pos_indices ) ) {
//				// Found it!!!
//				if ( num_double )
//				{
//					// Skinned mesh.
//					mp_index_buffer = (unsigned short *)p8;
//				}
//				else
//				{
//					// Non-skinned mesh.
//					mp_index_buffer = (unsigned short *)&p8[-2];
//				}
//				break;
//			}
//			search[0] = search[1];
//			search[1] = search[2];
//		}
//	}
//
////	if ( p_double )
////	{
////		double_bytes = dbytes;
////		single_bytes = sbytes;
////		
////		if ( m_localMeshIndex == 0 )
////		{
////			NsARAM::HEAPTYPE heap = NsARAM::BOTTOMUP;
////
////			if ( Mem::Manager::sHandle().GetContextAllocator() == Mem::Manager::sHandle().SkaterGeomHeap(0) )
////			{
////				heap = NsARAM::SKATER0;
////			}
////			if ( Mem::Manager::sHandle().GetContextAllocator() == Mem::Manager::sHandle().SkaterGeomHeap(1) )
////			{
////				heap = NsARAM::SKATER1;
////			}
////
////			//OSReport( "Allocating %d ARAM bytes %s\n", dbytes + sbytes, Mem::Manager::sHandle().GetContextName() );
////			mp_doubleWeight = NsARAM::alloc( (dbytes + sbytes) * 4, heap );
////			NsDMA::toARAM( mp_doubleWeight, p_double, ( dbytes + sbytes ) * 4 );
////		}
////	}
////	else
////	{
////		mp_doubleWeight = 0;
////		double_bytes = dbytes;
////		single_bytes = sbytes;
////	}
//
//	delete p_pos_indices;
//}

//void sMesh::Build( int num_indices, uint16* p_pos_indices, uint16* p_col_indices, uint16* p_uv_indices, bool rebuild )
//{
//	Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
//	EngineGlobals.gpuBusy = true;
//	// Turn the index list into a display list.
//	mp_display_list = NULL;
//#define DL_BUILD_SIZE (32*1024)
//	uint8 * p_build_dl = new (Mem::Manager::sHandle().TopDownHeap()) uint8[DL_BUILD_SIZE];
//	DCFlushRange ( p_build_dl, DL_BUILD_SIZE );
//
//	GXBeginDisplayList ( p_build_dl, DL_BUILD_SIZE );
//	GXResetWriteGatherPipe();
//
//	GXClearVtxDesc();
//	GXSetVtxDesc( GX_VA_POS, GX_INDEX16 );
//	m_vertex_stride = 1;
//	if ( mp_normBuffer || mp_doubleWeight )
//	{
//		GXSetVtxDesc( GX_VA_NRM, GX_INDEX16 );
//		m_vertex_stride++;
//	}
//	if ( mp_colBuffer )
//	{
//		GXSetVtxDesc( GX_VA_CLR0, GX_INDEX16 );
//		m_vertex_stride++;
//		if ( !mp_doubleWeight && gCorrectColor )
//		{
//			// Before we go wading in and allow a 2nd color index list, see if it's necessary.
//			// Check every poly in the mesh and see if the color difference is greater than a threshold.
//			bool needs_fixing = false;
//			uint32 color0;
//			uint32 color1 = mp_colBuffer[p_col_indices[p_pos_indices[0]]];
//			uint32 color2 = mp_colBuffer[p_col_indices[p_pos_indices[1]]];
//			for ( int lp = 2; lp < num_indices; lp++ )
//			{
//				// Grab new color to form triangle.
//				color0 = color1;
//				color1 = color2;
//				color2 = mp_colBuffer[p_col_indices[p_pos_indices[lp]]];
//
//				int diff0_a = abs( ( ( color1 >> 24 ) & 255 ) - ( ( color0 >> 24 ) & 255 ) );
//				int diff1_a = abs( ( ( color2 >> 24 ) & 255 ) - ( ( color1 >> 24 ) & 255 ) );
//				int diff2_a = abs( ( ( color0 >> 24 ) & 255 ) - ( ( color2 >> 24 ) & 255 ) );
//				int diff0_r = abs( ( ( color1 >> 16 ) & 255 ) - ( ( color0 >> 16 ) & 255 ) );
//				int diff1_r = abs( ( ( color2 >> 16 ) & 255 ) - ( ( color1 >> 16 ) & 255 ) );
//				int diff2_r = abs( ( ( color0 >> 16 ) & 255 ) - ( ( color2 >> 16 ) & 255 ) );
//				int diff0_g = abs( ( ( color1 >>  8 ) & 255 ) - ( ( color0 >>  8 ) & 255 ) );
//				int diff1_g = abs( ( ( color2 >>  8 ) & 255 ) - ( ( color1 >>  8 ) & 255 ) );
//				int diff2_g = abs( ( ( color0 >>  8 ) & 255 ) - ( ( color2 >>  8 ) & 255 ) );
//				int diff0_b = abs( ( ( color1 >>  0 ) & 255 ) - ( ( color0 >>  0 ) & 255 ) );
//				int diff1_b = abs( ( ( color2 >>  0 ) & 255 ) - ( ( color1 >>  0 ) & 255 ) );
//				int diff2_b = abs( ( ( color0 >>  0 ) & 255 ) - ( ( color2 >>  0 ) & 255 ) );
//
//				int wdiff = diff0_a;
//				if ( diff1_a > wdiff ) wdiff = diff1_a;
//				if ( diff2_a > wdiff ) wdiff = diff2_a;
//				if ( diff0_r > wdiff ) wdiff = diff0_r;
//				if ( diff1_r > wdiff ) wdiff = diff1_r;
//				if ( diff2_r > wdiff ) wdiff = diff2_r;
//				if ( diff0_g > wdiff ) wdiff = diff0_g;
//				if ( diff1_g > wdiff ) wdiff = diff1_g;
//				if ( diff2_g > wdiff ) wdiff = diff2_g;
//				if ( diff0_b > wdiff ) wdiff = diff0_b;
//				if ( diff1_b > wdiff ) wdiff = diff1_b;
//				if ( diff2_b > wdiff ) wdiff = diff2_b;
//
//				if ( wdiff > 8 ) needs_fixing = true;
//			}
//
//			if ( needs_fixing )
//			{
//				m_flags |= MESH_FLAG_DOUBLE_COLOR_IDX;
//				GXSetVtxDesc( GX_VA_CLR1, GX_INDEX16 );
//				m_vertex_stride++;
//			}
//		}
//	}
//
//	int coord = 0;
//	for ( uint layer = 0; layer < mp_material->Passes; layer++ )
//	{
//		GXSetVtxDesc( (GXAttr)(((int)GX_VA_TEX0)+coord), GX_INDEX16 );
//		m_vertex_stride++;
//		coord++;
//	}
//
//	if ( rebuild )
//	{
//		// Send coordinates.
//		GXBegin( (GXPrimitive)m_primitive_type, (GXVtxFmt)m_vertex_format, num_indices ); 
//		for ( int lp = 0; lp < num_indices; lp++ )
//		{
//			// Send coordinates.
//			int pindex = p_pos_indices[lp];
//			int cindex = p_col_indices[pindex];
//			GXPosition1x16(pindex);
//			if ( mp_normBuffer || mp_doubleWeight ) GXNormal1x16(pindex);
//			if ( mp_colBuffer ) GXColor1x16(cindex);
//			if ( m_flags & MESH_FLAG_DOUBLE_COLOR_IDX ) GXColor1x16(cindex);
//			for ( uint layer = 0; layer < mp_material->Passes; layer++ )
//			{
//				int uindex = pindex;
//				if ( p_uv_indices ) uindex = p_uv_indices[pindex+(layer*m_num_vertex)];
//				GXTexCoord1x16(uindex);
//			}
//		}
//		GXEnd();
//	}
//	else
//	{
//		// Parse index list & turn into gxbegin/end pairs.
//		int v = 0;
//		while ( p_pos_indices[v] )
//		{
//			uint16 num = p_pos_indices[v];
//			v++;
//
//			GXBegin( (GXPrimitive)m_primitive_type, (GXVtxFmt)m_vertex_format, num ); 
//
//			for ( int vv = 0; vv < num; vv++ )
//			{
//				// Send coordinates.
//				int pindex = p_pos_indices[v+vv];
//				int cindex = p_col_indices[pindex];
//				GXPosition1x16(pindex);
//				if ( mp_normBuffer || mp_doubleWeight ) GXNormal1x16(pindex);
//				if ( mp_colBuffer ) GXColor1x16(cindex);
//				if ( m_flags & MESH_FLAG_DOUBLE_COLOR_IDX ) GXColor1x16(cindex);
//				for ( uint layer = 0; layer < mp_material->Passes; layer++ )
//				{
//					int uindex = pindex;
//					if ( p_uv_indices ) uindex = p_uv_indices[pindex+(layer*m_num_vertex)];
//					GXTexCoord1x16(uindex);
//				}
//			}
//			v += num;
//			GXEnd();
//		}			
//	}
//
//	int size = GXEndDisplayList();
//	
//	mp_index_buffer = NULL;
//
//	uint8 * p_dl = new uint8[size];
//	memcpy ( p_dl, p_build_dl, size );
//	DCFlushRange ( p_dl, size );
//
//	delete p_build_dl;
//
//	mp_display_list = p_dl;
//	m_display_list_size = size;
//	EngineGlobals.gpuBusy = false;
//
//	Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
//}
//
//void sMesh::Rebuild( int num_indices, uint16* p_indices )
//{
//	uint16 * p_temp_col_remap = NULL;
//	uint16 * p_temp_uv_remap = NULL;
//	p_temp_uv_remap = m_num_uv_sets ? new (Mem::Manager::sHandle().TopDownHeap()) uint16[m_num_vertex*m_num_uv_sets] : NULL;
//	p_temp_col_remap = mp_colBuffer ? new (Mem::Manager::sHandle().TopDownHeap()) uint16[m_num_vertex] : NULL;
//
//	for ( int c = 0; c < m_num_vertex; c++ )
//	{
//		for ( int u = 0; u < m_num_uv_sets; u++ )
//		{
//			p_temp_uv_remap[(u*m_num_vertex)+c] = (u*m_num_vertex)+c;
//		}
//	}
//
//	for ( int c = 0; c < m_num_vertex; c++ )
//	{
//		p_temp_col_remap[c] = c;
//	}
//
//	delete [] mp_display_list;
//
////	Build( num_indices, p_indices, p_indices, p_indices, true );
//	Build( num_indices, p_indices, p_temp_col_remap, p_temp_uv_remap, true );
//	// Update to new number of indices.
//	m_num_indices = num_indices;
//
//	delete [] p_temp_col_remap;
//	delete [] p_temp_uv_remap;
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::SetPosition( Mth::Vector &pos )
{


//	// Figure what we need to add to each vertex, based on current position.
//	Mth::Vector offset(	pos[X] - m_offset_x,
//						pos[Y] - m_offset_y,
//						pos[Z] - m_offset_z );
//	m_offset_x = pos[X];
//	m_offset_y = pos[Y];
//	m_offset_z = pos[Z];
//
//	// We also need to adjust the bounding box and sphere information for this mesh.
//	m_sphere[0] += offset[X];
//	m_sphere[1] += offset[Y];
//	m_sphere[2] += offset[Z];
//
//	m_bbox.SetMin( m_bbox.GetMin() + offset );
//	m_bbox.SetMax( m_bbox.GetMax() + offset );
//
//#ifndef SHORT_VERT
//	if ( m_localMeshIndex != 0 ) return;
//
//	for( uint32 bv = 0; bv < m_num_vertex; ++bv )
//	{
//		mp_posBuffer[(bv*3)+0] += offset[X];
//		mp_posBuffer[(bv*3)+1] += offset[Y];
//		mp_posBuffer[(bv*3)+2] += offset[Z];
//	}
//	DCFlushRange( mp_posBuffer, 3 * sizeof( float ) * m_num_vertex );
//#endif		// SHORT_VERT
//
}
	

	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::GetPosition( Mth::Vector *p_pos )
{
//	p_pos->Set( m_offset_x, m_offset_y, m_offset_z );
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::SetYRotation( Mth::ERot90 rot )
{
//	if( ( rot > Mth::ROT_0 ) && ( m_localMeshIndex == 0 ) )
//	{
//		s16 nx, nz;
//#ifdef SHORT_VERT
//		s16 xo = 0;
//		s16 zo = 0;
//		s16 x, z;
//#else
//		float x, z;
//		float xo = m_offset_x;
//		float zo = m_offset_z;
//#endif		// SHORT_VERT 
//		switch( rot )
//		{
//			case Mth::ROT_90:
//			{
//				if ( m_localMeshIndex == 0 )
//				{
//					for( uint32 v = 0; v < m_num_vertex; ++v )
//					{
//						x = mp_posBuffer[(v*3)+0] - xo;
//						z = mp_posBuffer[(v*3)+2] - zo;
//						mp_posBuffer[(v*3)+0] = z + xo;
//						mp_posBuffer[(v*3)+2] = -x + zo;
//						if ( mp_normBuffer )
//						{
//							nx = mp_normBuffer[(v*3)+0];
//							nz = mp_normBuffer[(v*3)+2];
//							mp_normBuffer[(v*3)+0] = nz;
//							mp_normBuffer[(v*3)+2] = -nx;
//						}
//					}
//				}
//				// Adjust the bounding sphere information for this mesh.
//				m_sphere[0]	-= m_offset_x;
//				m_sphere[2]	-= m_offset_z;
//				float t				= m_sphere[0];
//				m_sphere[0]	= m_sphere[2] + m_offset_x;
//				m_sphere[2]	= -t + m_offset_z;
//
//				// Adjust the bounding box information for this mesh.
//				Mth::Vector m[4];
//
//				m[0].Set( m_bbox.GetMin()[X], 0.0f, m_bbox.GetMin()[Z] );
//				m[1].Set( m_bbox.GetMin()[X], 0.0f, m_bbox.GetMax()[Z] );
//				m[2].Set( m_bbox.GetMax()[X], 0.0f, m_bbox.GetMin()[Z] );
//				m[3].Set( m_bbox.GetMax()[X], 0.0f, m_bbox.GetMax()[Z] );
//
//				for( int i = 0; i < 4; ++i )
//				{
//					m[i][X] -= m_offset_x;
//					m[i][Z] -= m_offset_z;
//					float t = m[i][X];
//					m[i][X] = m[i][Z];
//					m[i][Z] = -t;
//					m[i][X] += m_offset_x;
//					m[i][Z] += m_offset_z;
//				}
//
//				Mth::Vector mmin( m[0][X], m_bbox.GetMin()[Y], m[0][Z] );
//				Mth::Vector mmax( m[0][X], m_bbox.GetMax()[Y], m[0][Z] );
//				for( int i = 1; i < 4; ++i )
//				{
//					if( m[i][X] < mmin[X] )
//						mmin[X] = m[i][X];
//					if( m[i][X] > mmax[X] )
//						mmax[X] = m[i][X];
//
//					if( m[i][Z] < mmin[Z] )
//						mmin[Z] = m[i][Z];
//					if( m[i][Z] > mmax[Z] )
//						mmax[Z] = m[i][Z];
//				}
//
//				m_bbox.SetMin( mmin );
//				m_bbox.SetMax( mmax );
//				break;
//			}
//			case Mth::ROT_180:
//			{
//				if ( m_localMeshIndex == 0 )
//				{
//					for( uint32 v = 0; v < m_num_vertex; ++v )
//					{
//						x = mp_posBuffer[(v*3)+0] - xo;
//						z = mp_posBuffer[(v*3)+2] - zo;
//						mp_posBuffer[(v*3)+0] = -x + xo;
//						mp_posBuffer[(v*3)+2] = -z + zo;
//						if ( mp_normBuffer )
//						{
//							nx = mp_normBuffer[(v*3)+0];
//							nz = mp_normBuffer[(v*3)+2];
//							mp_normBuffer[(v*3)+0] = -nx;
//							mp_normBuffer[(v*3)+2] = -nz;
//						}
//					}
//				}
//				// Adjust the bounding sphere information for this mesh.
//				m_sphere[0]	-= m_offset_x;
//				m_sphere[2]	-= m_offset_z;
//				m_sphere[0]	= -m_sphere[0] + m_offset_x;
//				m_sphere[2]	= -m_sphere[2] + m_offset_z;
//
//				// Adjust the bounding box information for this mesh.
//				Mth::Vector m[4];
//
//				m[0].Set( m_bbox.GetMin()[X], 0.0f, m_bbox.GetMin()[Z] );
//				m[1].Set( m_bbox.GetMin()[X], 0.0f, m_bbox.GetMax()[Z] );
//				m[2].Set( m_bbox.GetMax()[X], 0.0f, m_bbox.GetMin()[Z] );
//				m[3].Set( m_bbox.GetMax()[X], 0.0f, m_bbox.GetMax()[Z] );
//
//				for( int i = 0; i < 4; ++i )
//				{
//					m[i][X] -= m_offset_x;
//					m[i][Z] -= m_offset_z;
//					m[i][X] = -m[i][X];
//					m[i][Z] = -m[i][Z];
//					m[i][X] += m_offset_x;
//					m[i][Z] += m_offset_z;
//				}
//
//				Mth::Vector mmin( m[0][X], m_bbox.GetMin()[Y], m[0][Z] );
//				Mth::Vector mmax( m[0][X], m_bbox.GetMax()[Y], m[0][Z] );
//				for( int i = 1; i < 4; ++i )
//				{
//					if( m[i][X] < mmin[X] )
//						mmin[X] = m[i][X];
//					if( m[i][X] > mmax[X] )
//						mmax[X] = m[i][X];
//
//					if( m[i][Z] < mmin[Z] )
//						mmin[Z] = m[i][Z];
//					if( m[i][Z] > mmax[Z] )
//						mmax[Z] = m[i][Z];
//				}
//
//				m_bbox.SetMin( mmin );
//				m_bbox.SetMax( mmax );
//				break;
//			}
//			case Mth::ROT_270:
//			{
//				if ( m_localMeshIndex == 0 )
//				{
//					for( uint32 v = 0; v < m_num_vertex; ++v )
//					{
//						x = mp_posBuffer[(v*3)+0] - xo;
//						z = mp_posBuffer[(v*3)+2] - zo;
//						mp_posBuffer[(v*3)+0] = -z + xo;
//						mp_posBuffer[(v*3)+2] = x + zo;
//						if ( mp_normBuffer )
//						{
//							nx = mp_normBuffer[(v*3)+0];
//							nz = mp_normBuffer[(v*3)+2];
//							mp_normBuffer[(v*3)+0] = -nz;
//							mp_normBuffer[(v*3)+2] = nx;
//						}
//					}
//				}
//				// Adjust the bounding sphere information for this mesh.
//				m_sphere[0]	-= m_offset_x;
//				m_sphere[2]	-= m_offset_z;
//				float t				= m_sphere[0];
//				m_sphere[0]	= -m_sphere[2] + m_offset_x;
//				m_sphere[2]	= t + m_offset_z;
//
//				// Adjust the bounding box information for this mesh.
//				Mth::Vector m[4];
//
//				m[0].Set( m_bbox.GetMin()[X], 0.0f, m_bbox.GetMin()[Z] );
//				m[1].Set( m_bbox.GetMin()[X], 0.0f, m_bbox.GetMax()[Z] );
//				m[2].Set( m_bbox.GetMax()[X], 0.0f, m_bbox.GetMin()[Z] );
//				m[3].Set( m_bbox.GetMax()[X], 0.0f, m_bbox.GetMax()[Z] );
//
//				for( int i = 0; i < 4; ++i )
//				{
//					m[i][X] -= m_offset_x;
//					m[i][Z] -= m_offset_z;
//					float t = m[i][X];
//					m[i][X] = -m[i][Z];
//					m[i][Z] = t;
//					m[i][X] += m_offset_x;
//					m[i][Z] += m_offset_z;
//				}
//
//				Mth::Vector mmin( m[0][X], m_bbox.GetMin()[Y], m[0][Z] );
//				Mth::Vector mmax( m[0][X], m_bbox.GetMax()[Y], m[0][Z] );
//				for( int i = 1; i < 4; ++i )
//				{
//					if( m[i][X] < mmin[X] )
//						mmin[X] = m[i][X];
//					if( m[i][X] > mmax[X] )
//						mmax[X] = m[i][X];
//
//					if( m[i][Z] < mmin[Z] )
//						mmin[Z] = m[i][Z];
//					if( m[i][Z] > mmax[Z] )
//						mmax[Z] = m[i][Z];
//				}
//
//				m_bbox.SetMin( mmin );
//				m_bbox.SetMax( mmax );
//				break;
//			}
//			default:
//				// Do nothing.
//				break;
//		}
//#ifdef SHORT_VERT
//		DCFlushRange( mp_posBuffer, 3 * m_num_vertex * sizeof( s16 ) );
//#else
//		DCFlushRange( mp_posBuffer, 3 * m_num_vertex * sizeof( float ) );
//#endif		// SHORT_VERT
//		if ( mp_normBuffer ) DCFlushRange( mp_normBuffer, 3 * m_num_vertex * sizeof( s16 ) );
//	}
}
	
} // namespace NxNgc






================================================
FILE: Code/Gfx/NGC/NX/mesh.h
================================================
#ifndef __MESH_H
#define __MESH_H

#include 
#include 
#include 
#include "material.h"
#include 

// Comment in for 16 bit vertices.
//#define SHORT_VERT

namespace NxNgc
{

struct sDLHeader;	// Forward reference.

//#define MESHFLAG_TEXTURE     (1<<0)
//#define MESHFLAG_COLOURS     (1<<1)
//#define MESHFLAG_NORMALS     (1<<2)

//#define OBJFLAG_TEXTURE      (1<<0)
//#define OBJFLAG_COLOURS      (1<<1)
//#define OBJFLAG_NORMALS      (1<<2)
//#define OBJFLAG_TRANSPARENT	 (1<<3)

struct sCASData
{
	uint32	mask;
	uint32	data0;
	uint32	data1;
};
	
// Starting to turn this into a class, but since engine is still
// C-style, I'm keeping everything public.  Garrett
struct sMesh
{
public:
	enum EMeshFlags
	{
		MESH_FLAG_IS_INSTANCE				= (1<<0),
		MESH_FLAG_ACTIVE					= (1<<1),
		MESH_FLAG_IS_CLONED					= (1<<2),
		MESH_FLAG_VISIBLE					= (1<<3),		// Used for batch culling.
		MESH_FLAG_CLONED_POS				= (1<<4),
		MESH_FLAG_CLONED_COL				= (1<<5),
		MESH_FLAG_INDEX_COUNT_SET			= (1<<6),
		MESH_FLAG_NO_SKATER_SHADOW			= (1<<7),
		MESH_FLAG_CLONED_DL					= (1<<8),
	};

					sMesh( void );
					~sMesh( void );

	// Functions
	void			wibble_vc( void );
	void			wibble_normals( void );
	uint32			GetChecksum()	const { return Checksum; }
	uint32			GetFlags()		const			{ return m_flags; }
	
	void			SetActive( bool active )		{ m_flags = ( m_flags & ~MESH_FLAG_ACTIVE ) | ( active ? MESH_FLAG_ACTIVE : 0 ); }
	void			SetPosition( Mth::Vector &pos );
	void			GetPosition( Mth::Vector *p_pos );
	void			SetYRotation( Mth::ERot90 rot );
	void			SetVisibility( uint32 mask )	{ m_visibility_mask	= mask; }
	uint32			GetVisibility( void )			{ return m_visibility_mask; }
	sMesh			*Clone( bool instance = false );
//	void			Initialize( int num_vertices,
//								float* p_positions,
//#ifdef SHORT_VERT
//								s16* p_positions16,
//								int shift,
//								float off_x,
//								float off_y,
//								float off_z,
//#endif		// SHORT_VERT
//								s16* p_normals,
//								float* p_tex_coords,
//								float* p_env_buffer,
//								int num_tc_sets,
//								uint32* p_colors,
//								int num_indices,
//								uint16* p_pos_indices,
//								uint16* p_col_indices,
//								uint16* p_uv_indices,
//								unsigned long material_checksum,
//								void *p_scene,
//								int num_double,
//								uint32* p_double,
//								int num_single,
//								int localMeshIndex,
//								GXPrimitive primitive_type,
//								int bone_idx,
//								char *p_vc_wibble_anims  = NULL );

	void			Build( int num_indices, uint16* p_pos_indices, uint16* p_col_indices, uint16* p_uv_indices, bool rebuild = false );
	void			Rebuild( int num_indices, uint16* p_indices );

	// Note: localMeshIndex is the nth instance of a mesh in a sector.
	void			Submit( void );

	// Members
	uint32					Checksum;
	uint16					m_flags;
	int16					m_bone_idx;
	uint32					m_visibility_mask;
	float					m_bottom_y;	// Use this to cull meshes above the skater for shadows.

//	sMaterial*				mp_material;
//	
//	uint16					m_num_indices;
//	uint16					m_num_vertex;
//	uint8					m_vertex_stride;
//	uint8					m_num_uv_sets;
//
//	uint16					m_display_list_size;
//	uint8					m_primitive_type;
//	uint8					m_vertex_format;
//	uint16					m_localMeshIndex;
//
//	uint16*					mp_index_buffer;
//#ifdef SHORT_VERT
//	s16*					mp_posBuffer;
//#else
//	float*					mp_posBuffer;
//#endif		// SHORT_VERT
//	float					m_offset_x;
//	float					m_offset_y;
//	float					m_offset_z;
//	s16*					mp_normBuffer;
//	float*					mp_uvBuffer;
//	float*					mp_envBuffer;
//	uint32*					mp_colBuffer;
//
//	uint8*					mp_display_list;
//
//	Mth::CBBox				m_bbox;
//	float					m_sphere[4];
//
//	uint16					m_numDouble;
//	uint16					m_numSingle;
//	uint32*					mp_doubleWeight;
//
//	char					*mp_vc_wibble_data;
//
//	GXColor					m_material_color_override;

//	Mth::Vector				m_sphere;

	sDLHeader*				mp_dl;
//	void *					mp_texture_dl;
//	uint32					m_texture_dl_size;
//
//	bool					dl_owner;

	GXColor					m_base_color;
};

/*
// Starting to turn this into a class, but since engine is still
// C-style, I'm keeping everything public.  Garrett
struct sGroup
{
public:
	// Functions
	sMesh*			GetMeshArray(); 
	uint			GetNumMeshes() const { return NumMeshes; }

	// Members
	uint32 ID;
	uint8 *pUpload[2];
	uint8 *pRender[2];
	uint FirstMeshIndex;
	uint NumMeshes;
};


sGroup*			NewMeshGroup(uint32 group_ID, uint32 num_meshes);
sGroup*			LoadMeshGroup(void *p_FH);
void			LoadVertices(void *p_FH, sMesh *pMesh, sMaterial *pMat);

extern sMesh *Meshes;
extern uint NumMeshes, NumMeshGroups;
extern uint NumMeshesInGroup[32];
extern int TotalNumVertices;
*/

void SetMeshScalingParameters( Nx::SMeshScalingParameters* pParams );
void DisableMeshScaling( void );
void ApplyMeshScaling( NxNgc::sObjectHeader * p_object );

} // namespace NxNgc

#endif // __MESH_H



================================================
FILE: Code/Gfx/NGC/NX/nx_init.cpp
================================================
#include "nx_init.h"
#include "instance.h"
#include "render.h"
#include "sys/ngc/p_buffer.h"


namespace NxNgc
{

sEngineGlobals	EngineGlobals;
	
	


void InitialiseRenderstates( void )
{
//	EngineGlobals.lighting_enabled		= false;
//	D3DDevice_SetRenderState( D3DRS_LIGHTING, FALSE );
//
//	EngineGlobals.cull_mode				= D3DCULL_NONE;
//	D3DDevice_SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
//
//	EngineGlobals.dither_enable			= TRUE;
//	D3DDevice_SetRenderState( D3DRS_DITHERENABLE, TRUE );
//
//	EngineGlobals.z_test_enabled		= TRUE;
//	D3DDevice_SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL );
//
//	EngineGlobals.z_write_enabled		= TRUE;
//	D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
//	
//	EngineGlobals.alpha_blend_enable	= TRUE;
//	D3DDevice_SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
//
//	EngineGlobals.alpha_test_enable		= TRUE;
//	D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
//
//	EngineGlobals.alpha_ref				= 0;
//	D3DDevice_SetRenderState( D3DRS_ALPHAREF, 0x00 );
//
//	D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
//    D3DDevice_SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
//    D3DDevice_SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR );
//
//    D3DDevice_SetTextureStageState( 0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP );
//    D3DDevice_SetTextureStageState( 0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP );
//
//    D3DDevice_SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL );
//    D3DDevice_SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL );
//    D3DDevice_SetRenderState( D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL );
//
//	// Set these up so they will get reset first time through.
//	EngineGlobals.blend_mode_value			= 0xDEADBABE;
//	EngineGlobals.blend_mode_fixed_alpha	= 0xDEADBABE;

	init_render_system();
}


void InitialiseEngine( void )
{

//	D3DPRESENT_PARAMETERS   params;
//	DWORD					video_flags = XGetVideoFlags();
//
//	ZeroMemory( ¶ms, sizeof( D3DPRESENT_PARAMETERS ));
//
//	// This setting required for any multisample presentation.
//	params.SwapEffect						= D3DSWAPEFFECT_DISCARD;
//
//	// Let D3D create the depth-stencil buffer for us.
//	params.EnableAutoDepthStencil			= TRUE;
//
//	// Select default refresh rate and presentation interval. Note: When we switch to the December SDK
//	// we can use the ONE_OR_IMMEDIATE value (if the tearing looks okay).
//	params.FullScreen_RefreshRateInHz		= D3DPRESENT_RATE_DEFAULT;
//	params.FullScreen_PresentationInterval	= D3DPRESENT_INTERVAL_ONE;
//
//	// Set up the back buffer format.
//	params.BackBufferCount					= 1;
//	params.BackBufferWidth					= 640;
//	params.BackBufferHeight					= 480;
////	params.BackBufferFormat					= D3DFMT_LIN_R5G6B5;
//	params.BackBufferFormat					= D3DFMT_X8R8G8B8;
//
//	// Set up the Z-stencil buffer format and multisample format.
//	params.AutoDepthStencilFormat			= D3DFMT_D24S8;
//    params.MultiSampleType					= D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR;
//
//	// Set flag for progrssive scan where appropriate.
//	if( video_flags & XC_VIDEO_FLAGS_HDTV_480p )
//	{
//		params.Flags						= D3DPRESENTFLAG_PROGRESSIVE;
//	}
//	else
//	{
//		params.Flags						= D3DPRESENTFLAG_INTERLACED;
//	}
//	
//	if( D3D_OK != Direct3D_CreateDevice(	D3DADAPTER_DEFAULT,
//											D3DDEVTYPE_HAL,
//											NULL,
//											D3DCREATE_HARDWARE_VERTEXPROCESSING,	// Note: may want to consider adding the PUREDEVICE flag here also.
//											¶ms,
//											&EngineGlobals.p_Device ))
//	{
//		// Failed to start up engine. Bad!
//		return;
//	}
//
//	// Obtain pointers to the render and Z-stencil surfaces. Doing this increases their reference counts, so release
//	// them following the operation.
//	D3DDevice_GetRenderTarget( &EngineGlobals.p_RenderSurface );
//	D3DDevice_GetDepthStencilSurface( &EngineGlobals.p_ZStencilSurface );
//
//    D3DResource_Release((D3DResource*)&EngineGlobals.p_RenderSurface );
//    D3DResource_Release((D3DResource*)&EngineGlobals.p_ZStencilSurface );
//
	// Set default directional lights
	EngineGlobals.light_x[0] = -0.5f;		// Dir0
	EngineGlobals.light_y[0] = -0.8660254f;
	EngineGlobals.light_z[0] = 0.0f;
	EngineGlobals.diffuse_light_color[0] = (GXColor){143,143,143,255};
	
	EngineGlobals.light_x[1] = 1.0f;		// Dir1 
	EngineGlobals.light_y[1] = 0.0f;
	EngineGlobals.light_z[1] = 0.0f;
	EngineGlobals.diffuse_light_color[1] = (GXColor){0,0,0,255};

	// Set default ambient light.
	EngineGlobals.ambient_light_color = (GXColor){150,150,150,255};

	EngineGlobals.gpuBusy = false;

	EngineGlobals.screen_brightness = 1.0f;

	EngineGlobals.frameCount = 0;

	EngineGlobals.use_480p			= false;
	EngineGlobals.use_60hz			= false;
	EngineGlobals.letterbox_active	= false;

	EngineGlobals.reduceColors = true;

	// Set our renderstate to a known state.
	InitialiseRenderstates();

	// Create our instance table.
	InitialiseInstanceTable();

	EngineGlobals.disableReset = false;
	EngineGlobals.resetToIPL = false; 

	NsBuffer::init( ( 1024 * 512 ) - ( 50 * 1024 ) );
}


} // namespace NxNgc



================================================
FILE: Code/Gfx/NGC/NX/nx_init.h
================================================
#ifndef __NX_INIT_H
#define __NX_INIT_H

#include 
#include 

namespace NxNgc
{

void InitialiseEngine( void );

typedef struct
{
//	IDirect3DDevice8*	p_Device;
//	IDirect3DSurface8*	p_RenderSurface;
//	IDirect3DSurface8*	p_ZStencilSurface;

	uint32				blend_mode_value;
	uint32				blend_mode_fixed_alpha;

	// These renderstates should go in their own structure.
	bool				alpha_blend_enable;
	bool				alpha_test_enable;
	uint32				alpha_ref;
	bool				lighting_enabled;
	bool				dither_enable;
	bool				z_write_enabled;
	bool				z_test_enabled;
	int					cull_mode;
	bool				poly_culling;

	NsMatrix			local_to_camera;
	NsMatrix			world_to_camera;
	NsMatrix			camera;
	NsMatrix			shadow_camera;
	Vec					object_pos;
	Mtx					current_uploaded;

	float				tx;
	float				ty;
	float				sx;
	float				sy;
	float				cx;
	float				cy;
	float				near;
	float				far;

	GXColor				ambient_light_color;
	GXColor				diffuse_light_color[3];
	float				light_x[3];
	float				light_y[3];
	float				light_z[3];

	float				screen_brightness;	// 0 = black, 1 = normal, 2 = saturated.

	uint32				frameCount;

	volatile bool		gpuBusy;

	// ARAM defines.
	#define aram_zero_size		1024
	#define	aram_dsp_size		1024 * 1900
	#define	aram_stream0_size	1024 * 32
	#define aram_stream1_size	aram_stream0_size
	#define aram_stream2_size	aram_stream1_size
	#define aram_music_size		1024 * 32
	
	uint32				aram_zero;
	uint32				aram_dsp;
	uint32				aram_stream0;
	uint32				aram_stream1;
	uint32				aram_stream2;
	uint32				aram_music;

	bool				use_480p;
	bool				use_60hz;
	bool				use_widescreen;
	bool				letterbox_active;

	bool				reduceColors;

	bool				disableReset;
	bool				resetToIPL;

	uint32				viewport;		// Expressed as a bit ( 1 << v ).

	NsVector			skater_shadow_dir;
	float				skater_height;
	NsVector			ped_shadow_dir;
}
sEngineGlobals;

extern sEngineGlobals EngineGlobals;



} // namespace NxNgc

#endif // __NX_INIT_H


================================================
FILE: Code/Gfx/NGC/NX/occlude.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		occlude.cpp												**
**																			**
**	Created:		04/02/02	-	dc										**
**																			**
**	Description:	Occlusion testing code									**
**																			**
*****************************************************************************/


//#define	OCCLUDER_USES_SCRATCHPAD
//#define	OCCLUDER_USES_VU0_MACROMODE
//#define	OCCLUDER_USES_VU0_MICROMODE

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/
		
#include 
#include 
#include "occlude.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

//extern D3DXMATRIX *p_bbox_transform;

namespace NxNgc
{


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

const uint32 MAX_NEW_OCCLUSION_POLYS_TO_CHECK_PER_FRAME = 4;

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

// Structure used to store details of a single poly. A list of these will be built at geometry load time.
struct sOcclusionPoly
{
	bool		in_use;		// Whether the poly is currently being used for occlusion.
	bool		available;	// Whether the poly is available for selection for occlusion.
	uint32		checksum;	// Name checksum of the occlusion poly.
	Mth::Vector	verts[4];
	Mth::Vector	normal;
};
	
const uint32	MAX_OCCLUDERS				= 8;
const uint32	MAX_VIEWS_PER_OCCLUDER		= 2;

struct sOccluder
{
	static uint32		NumOccluders;
	static sOccluder	Occluders[MAX_OCCLUDERS];

	static void			add_to_stack( sOcclusionPoly *p_poly );
	static void			sort_stack( void );
	static void			tidy_stack( void );

	sOcclusionPoly	*p_poly;
	Mth::Vector		planes[5];
	int				score[MAX_VIEWS_PER_OCCLUDER];	// Current rating on quality of occlusion - based on number of meshes occluded last frame.
};

const uint32	MAX_OCCLUSION_POLYS			= 512;
uint32			NumOcclusionPolys			= 0;
uint32			NextOcclusionPolyToCheck	= 0;
int				CurrentView					= 0;
sOcclusionPoly	OcclusionPolys[MAX_OCCLUSION_POLYS];

uint32		sOccluder::NumOccluders				= 0;
sOccluder	sOccluder::Occluders[MAX_OCCLUDERS];

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sOccluder::add_to_stack( sOcclusionPoly *p_poly )
{
	if( NumOccluders < MAX_OCCLUDERS )
	{
		Dbg_Assert( p_poly->available );

		Occluders[NumOccluders].p_poly	= p_poly;
		p_poly->in_use					= true;

		// Reset scores for all views.
		memset( Occluders[NumOccluders].score, 0, sizeof( int ) * MAX_VIEWS_PER_OCCLUDER );

		++NumOccluders;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int cmp( const void *p1, const void *p2 )
{
	// Sort based on the sum total of scores in all views.
	int score1 = 0;
	int score2 = 0;
	for( uint v = 0; v < MAX_VIEWS_PER_OCCLUDER; ++v )
	{
		// Zero the score for any occlusion poly that is no longer available. This will force it out of the stack.
		if(((sOccluder*)p1)->p_poly->available == false )
			((sOccluder*)p1)->score[v] = 0;

		if(((sOccluder*)p2)->p_poly->available == false )
			((sOccluder*)p2)->score[v] = 0;

		score1 += ((sOccluder*)p1)->score[v];
		score2 += ((sOccluder*)p2)->score[v];
	}

	return score1 < score2 ? 1 : score1 > score2 ? -1 : 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sOccluder::sort_stack( void )
{
	qsort( Occluders, NumOccluders, sizeof( sOccluder ), cmp );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sOccluder::tidy_stack( void )
{
	if( NumOccluders > 0 )
	{
		// Sort in descending score order.
		sort_stack();

		// Count backwards so we know we get all the bad occluders.
		for( int i = NumOccluders - 1; i >= 0; --i )
		{
			// If we have hit an occluder with zero meshes culled, cut off the stack at this point.
			int total_score = 0;
			for( uint v = 0; v < MAX_VIEWS_PER_OCCLUDER; ++v )
			{
				total_score += Occluders[i].score[v];
			}
			
			if( total_score == 0 )
			{
				// No longer using this poly.
				Occluders[i].p_poly->in_use = false;

				// One less occluder to worry about.
				--NumOccluders;
			}
			else
			{
				// Reset the good occluders.
				Occluders[i].score[CurrentView] = 0;
			}
		}
	}
	
}



/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/* Used to add an occlusion poly to the list. Likely to be called */
/* as geometry is spooled in.                                     */
/*                                                                */
/******************************************************************/
void AddOcclusionPoly( Mth::Vector &v0, Mth::Vector &v1, Mth::Vector &v2, Mth::Vector &v3, uint32 checksum )
{
	Dbg_Assert( NumOcclusionPolys < MAX_OCCLUSION_POLYS );	
	
	OcclusionPolys[NumOcclusionPolys].in_use	= false;
	OcclusionPolys[NumOcclusionPolys].available	= true;
	OcclusionPolys[NumOcclusionPolys].checksum	= checksum;
	OcclusionPolys[NumOcclusionPolys].verts[0]	= v0;
	OcclusionPolys[NumOcclusionPolys].verts[1]	= v1;
	OcclusionPolys[NumOcclusionPolys].verts[2]	= v2;
	OcclusionPolys[NumOcclusionPolys].verts[3]	= v3;
	OcclusionPolys[NumOcclusionPolys].normal	= Mth::CrossProduct( v1 - v0, v3 - v0 );
	OcclusionPolys[NumOcclusionPolys].normal.Normalize();
	
	++NumOcclusionPolys;
}



/******************************************************************/
/*                                                                */
/* Used to toggle whether an occlusion poly can be used or not	  */
/*                                                                */
/******************************************************************/
void EnableOcclusionPoly( uint32 checksum, bool available )
{
	for( uint32 i = 0; i < NumOcclusionPolys; ++i )
	{
		if( OcclusionPolys[i].checksum == checksum )
		{
			OcclusionPolys[i].available	= available;
		}
	}
}



/******************************************************************/
/*                                                                */
/* Used to clear all occlusion polys (when a level is unloaded)   */
/*                                                                */
/******************************************************************/
void RemoveAllOcclusionPolys( void )
{
	Dbg_Assert( NumOcclusionPolys < MAX_OCCLUSION_POLYS );	
	
 	sOccluder::NumOccluders		= 0;
	NumOcclusionPolys			= 0;
	NextOcclusionPolyToCheck	= 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CheckForOptimalOccluders( Mth::Vector &cam_pos, Mth::Vector &view_direction )
{
	if( NumOcclusionPolys > 0 )
	{
		uint32 added	= 0;
		uint32 checked	= 0;

		while( added < MAX_NEW_OCCLUSION_POLYS_TO_CHECK_PER_FRAME )
		{
			// Given the current position of the camera, check through the unused, available occlusion polys to see if one scores higher
			// than the lowest scoring occlusion poly in use.
			sOcclusionPoly *poly_to_check = &OcclusionPolys[NextOcclusionPolyToCheck++];
			if(( !poly_to_check->in_use ) && ( poly_to_check->available ))
			{
				sOccluder::add_to_stack( poly_to_check );
				++added;
			}
			++checked;

			// Ensure we are always checking within bounds.
			if( NextOcclusionPolyToCheck >= NumOcclusionPolys )
			{
				NextOcclusionPolyToCheck = 0;
			}

			// Quit out if we have less available occluders than spaces to fill.
			if( checked >= NumOcclusionPolys )
			{
				break;
			}
		}
	}
}


//char scratch[16384];


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void BuildOccluders( Mth::Vector *p_cam_pos, int view )
{
//	for( uint32 i = 0; i < NumOcclusionPolys; ++i )
//	{
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[0], OcclusionPolys[i].verts[1], 0, 0, 1 );
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[1], OcclusionPolys[i].verts[2], 0, 0, 1 );
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[2], OcclusionPolys[i].verts[3], 0, 0, 1 );
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[3], OcclusionPolys[i].verts[0], 0, 0, 1 );
//	}
	
	// Set the current view - this will remain active until another call to this function.
	Dbg_Assert( (uint32)view < MAX_VIEWS_PER_OCCLUDER );
	CurrentView	= view;
	
	// Tidy up from last frame.
	sOccluder::tidy_stack();
	
	// Cyclically add more occluders for checking.
	CheckForOptimalOccluders( *p_cam_pos, *p_cam_pos );
	
	// Build all 5 planes for each occluder.
	Mth::Vector u0, u1, p;

	// The order in which the verts are used to build tha planes depends upon where the camera is in relation to the occlusion
	// poly. We use the default order when the viewpoint is on the side of the poly on which the default poly normal faces.
	for( uint32 i = 0; i < sOccluder::NumOccluders; ++i )
	{
		sOcclusionPoly *p_poly = sOccluder::Occluders[i].p_poly;

//		Gfx::AddDebugLine( p_poly->verts[0], p_poly->verts[1], 0, 0, 1 );
//		Gfx::AddDebugLine( p_poly->verts[1], p_poly->verts[2], 0, 0, 1 );
//		Gfx::AddDebugLine( p_poly->verts[2], p_poly->verts[3], 0, 0, 1 );
//		Gfx::AddDebugLine( p_poly->verts[3], p_poly->verts[0], 0, 0, 1 );
		
		if( Mth::DotProduct( *p_cam_pos - p_poly->verts[0], p_poly->normal ) >= 0.0f )
		{
			// Start with the front. We want to reverse the order of the front plane to ensure that objects *behind* the plane
			// are considered occluded. (1->3->4)...
			u0						= p_poly->verts[2] - p_poly->verts[0];
			u1						= p_poly->verts[3] - p_poly->verts[0];
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, p_poly->verts[0] );
			sOccluder::Occluders[i].planes[0]	= p;

			// ...then left (0->1->2)...
			u0						= p_poly->verts[0] - *p_cam_pos;
			u1						= p_poly->verts[1] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[1]	= p;

			// ...then right (0->3->4)...
			u0						= p_poly->verts[2] - *p_cam_pos;
			u1						= p_poly->verts[3] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[2]	= p;

			// ...then top (0->2->3)...
			u0						= p_poly->verts[1] - *p_cam_pos;
			u1						= p_poly->verts[2] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[3]	= p;

			// ...then bottom (0->4->1)...
			u0						= p_poly->verts[3] - *p_cam_pos;
			u1						= p_poly->verts[0] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[4]	= p;
		}
		else
		{
			// Start with the front. We want to reverse the order of the front plane to ensure that objects *behind* the plane
			// are considered occluded. (1->4->3)...
			u0						= p_poly->verts[3] - p_poly->verts[0];
			u1						= p_poly->verts[2] - p_poly->verts[0];
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, p_poly->verts[0] );
			sOccluder::Occluders[i].planes[0]	= p;

			// ...then left (0->2->1)...
			u0						= p_poly->verts[1] - *p_cam_pos;
			u1						= p_poly->verts[0] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[1]	= p;

			// ...then right (0->4->3)...
			u0						= p_poly->verts[3] - *p_cam_pos;
			u1						= p_poly->verts[2] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[2]	= p;

			// ...then top (0->3->2)...
			u0						= p_poly->verts[2] - *p_cam_pos;
			u1						= p_poly->verts[1] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[3]	= p;

			// ...then bottom (0->1->4)...
			u0						= p_poly->verts[0] - *p_cam_pos;
			u1						= p_poly->verts[3] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[4]	= p;
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool TestSphereAgainstOccluders( Mth::Vector *p_sphere, uint32 meshes )
{
	// Test against each occluder.
	for( uint32 o = 0; o < sOccluder::NumOccluders; ++o )
	{
		bool occluded = true;

		// Test against each plane in the occluder.
		for( uint32 p = 0; p < 5; ++p )
		{
			float result =	( sOccluder::Occluders[o].planes[p][X] * p_sphere->GetX() ) +
							( sOccluder::Occluders[o].planes[p][Y] * p_sphere->GetY() ) +
							( sOccluder::Occluders[o].planes[p][Z] * p_sphere->GetZ() ) -
							( sOccluder::Occluders[o].planes[p][W] );
			if( result >= -p_sphere->GetW() )
			{
				// Outside of this plane, therefore not occluded by this occluder.
				occluded = false;
				break;
			}
		}

		if( occluded )
		{
			// Inside all planes, therefore occluded. Increase score for this occluder.
			sOccluder::Occluders[o].score[CurrentView] += meshes;
			return true;
		}
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


} // namespace NxXbox



================================================
FILE: Code/Gfx/NGC/NX/occlude.h
================================================
#ifndef	__OCCLUDE_H__
#define	__OCCLUDE_H__

namespace NxNgc
{
	void AddOcclusionPoly( Mth::Vector &v0, Mth::Vector &v1, Mth::Vector &v2, Mth::Vector &v3, uint32 checksum = 0 );
	void EnableOcclusionPoly( uint32 checksum, bool available );
	void RemoveAllOcclusionPolys( void );
	void BuildOccluders( Mth::Vector *p_cam_pos, int view );
	bool TestSphereAgainstOccluders( Mth::Vector *p_sphere, uint32 meshes = 1 );
}

#endif




================================================
FILE: Code/Gfx/NGC/NX/particles.cpp
================================================
//
///*****************************************************************************
//**																			**
//**			              Neversoft Entertainment			                **
//**																		   	**
//**				   Copyright (C) 1999 - All Rights Reserved				   	**
//**																			**
//******************************************************************************
//**																			**
//**	Project:		GFX (Graphics Library)									**
//**																			**
//**	Module:			Graphics (GFX)		 									**
//**																			**
//**	File name:		particle.cpp											**
//**																			**
//**	Created:		03/27/02	-	dc										**
//**																			**
//**	Description:	Low level particle rendering code						**
//**																			**
//*****************************************************************************/
//
//int gParticleMem = 0;
//
///*****************************************************************************
//**							  	  Includes									**
//*****************************************************************************/
//		
//#include 
//#include 
//#include "render.h"
//#include "particles.h"
//#include "scene.h"
//#include 
//#include 
//#include 
//#include 
//
///*****************************************************************************
//**								DBG Information								**
//*****************************************************************************/
//
//
///*****************************************************************************
//**								  Externals									**
//*****************************************************************************/
//
//namespace NxNgc
//{
//
//
///*****************************************************************************
//**								   Defines									**
//*****************************************************************************/
//
///*****************************************************************************
//**								Private Types								**
//*****************************************************************************/
//
///*****************************************************************************
//**								 Private Data								**
//*****************************************************************************/
//
///*****************************************************************************
//**								 Public Data								**
//*****************************************************************************/
//
///*****************************************************************************
//**							  Private Prototypes							**
//*****************************************************************************/
//
///*****************************************************************************
//**							   Private Functions							**
//*****************************************************************************/
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//
///*****************************************************************************
//**							   Public Functions								**
//*****************************************************************************/
//
//static BlendModes get_texture_blend( uint32 blend_checksum )
//{
//	BlendModes rv = NxNgc::vBLEND_MODE_DIFFUSE;
//	switch ( blend_checksum )
//	{
//		case 0x54628ed7:		// Blend
//			rv = NxNgc::vBLEND_MODE_BLEND;
//			break;
//		case 0x02e58c18:		// Add
//			rv = NxNgc::vBLEND_MODE_ADD;
//			break;
//		case 0xa7fd7d23:		// Sub
//		case 0xdea7e576:		// Subtract
//			rv = NxNgc::vBLEND_MODE_SUBTRACT;
//			break;
//		case 0x40f44b8a:		// Modulate
//			rv = NxNgc::vBLEND_MODE_MODULATE;
//			break;
//		case 0x68e77f40:		// Brighten
//			rv = NxNgc::vBLEND_MODE_BRIGHTEN;
//			break;
//		case 0x18b98905:		// FixBlend
//			rv = NxNgc::vBLEND_MODE_BLEND_FIXED;
//			break;
//		case 0xa86285a1:		// FixAdd
//			rv = NxNgc::vBLEND_MODE_ADD_FIXED;
//			break;
//		case 0x0d7a749a:		// FixSub
//		case 0x0eea99ff:		// FixSubtract
//			rv = NxNgc::vBLEND_MODE_SUB_FIXED;
//			break;
//		case 0x90b93703:		// FixModulate
//			rv = NxNgc::vBLEND_MODE_MODULATE_FIXED;
//			break;
//		case 0xb8aa03c9:		// FixBrighten
//			rv = NxNgc::vBLEND_MODE_BRIGHTEN_FIXED;
//			break;
//		case 0x515e298e:		// Diffuse
//		case 0x806fff30:		// None
//			rv = NxNgc::vBLEND_MODE_DIFFUSE;
//			break;
//		default:
//			Dbg_MsgAssert(0,("Illegal blend mode specified. Please use (fix)blend/add/sub/modulate/brighten or diffuse/none."));
//			break;
//	}
//	return rv;
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//sParticleSystem::sParticleSystem( uint32 max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix )
//{
//	// Create a scene to store the mesh.
//	mp_scene = new sScene();
//
//	// Get the texture.
//
//	uint32 flags = MATFLAG_TRANSPARENT;
//	Nx::CTexture *p_texture;
//	Nx::CNgcTexture *p_ngc_texture;
//	NxNgc::sTexture * p_engine_texture = NULL;
//
//	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
//	p_ngc_texture = static_cast( p_texture );
//	if ( p_ngc_texture )
//	{
//		p_engine_texture = p_ngc_texture->GetEngineTexture(); 
//		flags |=MATFLAG_TEXTURED;
//	}
//
//	// Create a material used to render the mesh.
//	sMaterial *p_material				= new sMaterial();
//	p_material->m_sorted				= 0;
//	p_material->m_no_bfc				= 1;
//	p_material->Flags[0]				= flags;		// 0x40 makes it semitransparent.
//	p_material->Checksum				= 0x5c41d978;	// particle_system	//(uint32)rand() * (uint32)rand();
//	p_material->Passes					= 1;
//	p_material->pTex[0]					= p_engine_texture;
//	p_material->blendMode[0]			= (uint8)get_texture_blend( blendmode_checksum );
//	p_material->fixAlpha[0]				= fix & 255;
//	p_material->UVAddressing[0]			= 0;
//	p_material->matcol[0]				= (GXColor){128,128,128,255};
//	p_material->AlphaCutoff				= 1;
//	p_material->m_num_wibble_vc_anims	= 0;
//	p_material->mp_wibble_vc_params		= NULL;
//	p_material->mp_wibble_vc_colors		= NULL;
//	p_material->m_pUVControl			= NULL;
//
//	// Create a hash table to store the material...
//	mp_scene->mp_material_array = p_material;
//	mp_scene->m_num_materials = 1;
//
//	// Initialize the material (builds the display list).
//	InitializeMaterial( p_material );
//
//	// Create a mesh which will be used to render the system.
//	mp_mesh = new sMesh();
//
//	// Create a position buffer.
//#ifdef SHORT_VERT
//	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
//	float *p_pos		= new float[max_particles * 3 * 4];
//	Mem::Manager::sHandle().PopContext();
//	s16 *p_pos16		= new s16[max_particles * 3 * 4];
//	gParticleMem += max_particles * 3 * 4 * 2;
//#else
//	float *p_pos		= new float[max_particles * 3 * 4];
//#endif		// SHORT_VERT
//
//	for( uint32 lp = 0; lp < ( max_particles * 3 * 4 ); ++lp )
//	{
//		p_pos[lp] = 1024.0f;
//#ifdef SHORT_VERT
//		p_pos16[lp] = (s16)(1024.0f*(1 << 0));
//#endif		// SHORT_VERT
//	}
//	
//	// Save pointer to bounding sphere so that the particle system can update it.
//	mp_sphere = mp_mesh->m_sphere;
//
//	// Create an index buffer.
//	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
//	uint16 *p_indices	= new uint16[(max_particles * 4)+2];
//	gParticleMem += ( (max_particles * 4)+2 ) * 2;
//	Mem::Manager::sHandle().PopContext();
//
//	// Fill the index buffer.
//	uint16 *p16 = p_indices;
//	*p16++ = max_particles * 4;
//	for( uint32 i = 0; i < max_particles; ++i )
//	{
//		*p16++ = (uint16)i * 4 + 0;
//		*p16++ = (uint16)i * 4 + 1;
//		*p16++ = (uint16)i * 4 + 2;
//		*p16++ = (uint16)i * 4 + 3;
//	}
//	*p16++ = 0;
//	
//	// Create texture coordinates.
//	s16 *p_uv = NULL;
//	if ( p_engine_texture )
//	{
//		p_uv = new s16[max_particles * 2 * 4];
//		gParticleMem += max_particles * 2 * 4 * 2;
//
//		for( uint32 lp = 0; lp < max_particles; ++lp )
//		{
//			p_uv[(lp*2*4)+0] = (s16)(0.0f * ((float)(1<<10)));
//			p_uv[(lp*2*4)+1] = (s16)(0.0f * ((float)(1<<10)));
//			p_uv[(lp*2*4)+2] = (s16)(1.0f * ((float)(1<<10)));
//			p_uv[(lp*2*4)+3] = (s16)(0.0f * ((float)(1<<10)));
//			p_uv[(lp*2*4)+4] = (s16)(1.0f * ((float)(1<<10)));
//			p_uv[(lp*2*4)+5] = (s16)(1.0f * ((float)(1<<10)));
//			p_uv[(lp*2*4)+6] = (s16)(0.0f * ((float)(1<<10)));
//			p_uv[(lp*2*4)+7] = (s16)(1.0f * ((float)(1<<10)));
//		}
//	}
//
//	// Create Color list.
//	GXColor* p_color = new GXColor[max_particles * 4];
//	gParticleMem += max_particles * 4 * 2;
//
//	for( uint32 cc = 0; cc < max_particles; ++cc )
//	{
//		p_color[(cc*4)+0] = (GXColor){128,128,128,255};
//		p_color[(cc*4)+1] = (GXColor){128,128,128,255};
//		p_color[(cc*4)+2] = (GXColor){128,128,128,255};
//		p_color[(cc*4)+3] = (GXColor){128,128,128,255};
//	}
//	
//	// Set up the mesh.
//	mp_mesh->Initialize( max_particles * 4,		// Num vertices
//						p_pos,					// Positions
//#ifdef SHORT_VERT
//						p_pos16,
//						0,
//						0.0f,
//						0.0f,
//						0.0f,
//#endif		// SHORT_VERT
//						NULL,					// Normals
//						p_uv,					// Texture coordinates
//						1,						// Number of tc sets
//						(uint32*)p_color,		// Colors
//						max_particles * 4,		// Num indices (4 per particle)
//						p_indices,
//						p_material->Checksum,	// Material checksum
//						mp_scene,
//						0,						// Double lists.
//						NULL,
//						0,						// Single lists.
//						0,						// local index of 0 means data will be deleted.
//						GX_QUADS,				// Type of mesh. 
//						0,						// flags
//						NULL );					// Wibble.
//
//#ifdef SHORT_VERT
//	delete p_pos;
//#endif		// SHORT_VERT
//
////	// Also adjust the bounding sphere and bounding box of the mesh.
////	p_mesh->m_sphere_center = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
////	p_mesh->m_sphere_radius = 360.0f;
////	p_mesh->m_bbox.SetMin( Mth::Vector( -360.0f, -360.0f, -360.0f ));
////	p_mesh->m_bbox.SetMax( Mth::Vector(  360.0f,  360.0f,  360.0f ));
//	
//	// Now add the mesh to the scene.
//	mp_scene->CountMeshes( 1, &mp_mesh );
//	mp_scene->CreateMeshArrays();
//	mp_scene->AddMeshes( 1, &mp_mesh );
//	mp_scene->SortMeshes();
//
//	// Create an instance and attach the mesh to it.
//	Mth::Matrix temp;
//	temp.GetRight().Set( 1.0f, 0.0f, 0.0f );
//	temp.GetUp().Set( 0.0f, 1.0f, 0.0f );
//	temp.GetAt().Set( 0.0f, 0.0f, 1.0f );
//	temp.GetPos().Set( 0.0f, 0.0f, 0.0f );
//	mp_instance = new NxNgc::CInstance( mp_scene, temp, 1, NULL );
//
//	mp_material = p_material;
//
//	mp_engine_texture = p_engine_texture;
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//sParticleSystem::~sParticleSystem()
//{
// 	// Delete the single sMesh from the scene.
//	Dbg_Assert( mp_scene->m_num_opaque_entries == 0 );
//	Dbg_Assert( mp_scene->m_num_semitransparent_entries == 1 );
//	delete mp_scene->m_semitransparent_meshes[0];
//	
//	// Deleting the sScene will also remove the material table (which removes the material).
//	DeleteScene( mp_scene );
//
//	if ( mp_instance ) delete mp_instance;
////	mp_scene->RemoveMeshes( &mp_mesh );
////	delete mp_mesh;
////	delete mp_scene;
////	delete mp_instance;
//}
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//void sParticleSystem::Render( void )
//{
////	render_scene( mp_scene, NULL, vRENDER_OPAQUE );
//
////	// Switch vertex buffers on the mesh.
////	mp_scene->m_opaque_meshes[0]->SwapVertexBuffers();
//}
//
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//#ifdef SHORT_VERT
//s16 *sParticleSystem::GetVertexWriteBuffer( void )
//#else
//float *sParticleSystem::GetVertexWriteBuffer( void )
//#endif		// SHORT_VERT
//{
//	return mp_scene->m_semitransparent_meshes[0]->mp_posBuffer;
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//GXColor *sParticleSystem::GetColorWriteBuffer( void )
//{
//	return (GXColor*)mp_scene->m_semitransparent_meshes[0]->mp_colBuffer;
//}
//
//} // namespace NxNgc
//
//



================================================
FILE: Code/Gfx/NGC/NX/particles.h
================================================
#ifndef	__PARTICLE_H__
#define	__PARTICLE_H__

#include "gfx\Ngc\nx\scene.h"
#include "gfx\Ngc\nx\instance.h"
#include "gfx/ngc/nx/mesh.h"

namespace NxNgc
{


struct sParticleSystem
{
public:
				sParticleSystem( uint32 max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix );
				~sParticleSystem( void );

	void		Render();
#ifdef SHORT_VERT
	s16			*GetVertexWriteBuffer( void );
#else
	float		*GetVertexWriteBuffer( void );
#endif		// SHORT_VERT
	GXColor		*GetColorWriteBuffer( void );
	sMaterial*	GetMaterial( void )			{ return mp_material; }
	NxNgc::sTexture * GetTexture( void )	{ return mp_engine_texture; }

	sScene*		mp_scene;

	float* 		mp_sphere;

	CInstance*	mp_instance;

	sMaterial*	mp_material;

	sMesh*		mp_mesh;

	NxNgc::sTexture * mp_engine_texture;
};

}

#endif




================================================
FILE: Code/Gfx/NGC/NX/render.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "nx_init.h"
#include "scene.h"
#include "render.h"
#include 
#include 
#include "occlude.h"
#include 	"gfx/ngc/nx/mesh.h"
#include "charpipeline\skinning.h"
#include	"gfx\NxMiscFX.h"

#include	
#include	
#include	
#include	

#include "dolphin/base/ppcwgpipe.h"
#include "dolphin/gx/gxvert.h"

extern int g_object;
extern int g_view_object;
extern int g_material;
extern NxNgc::sScene * g_view_scene;

//extern u16 colorMap[];

bool g_skip_correctable = 0;

#define SHADOW_TEXTURE_SIZE 256
extern uint8 * shadowTextureData;
extern uint16 shadowPalette[16];
extern uint8 * zTextureData;
extern uint8 * blurTextureData;
NxNgc::sMaterial * p_last_material = NULL;
uint32 last_color_override = 0;
uint16	last_mesh_flags = 0;
bool last_correct = true;
bool reload_camera = false;

Mtx g_matrix0;
Mtx g_matrix90;
Mtx g_matrix180;
Mtx g_matrix270;

Mth::Vector g_shadow_object_pos;

int g_kill_layer = -1;

extern "C"
{
extern void FakeEnvMap( ROMtx m, s16 * srcPos, s16 * srcNorm, float * dstBase, u32 count );
extern void FakeEnvMapSkin( ROMtx m, s16 * srcPosNorm, float * dstBase, u32 count );
extern void FakeEnvMapFloat( ROMtx m, float * srcPos, s16 * srcNorm, float * dstBase, u32 count );
}

static float * sLastUV = NULL;

static bool first_mat = false;
//static bool one_mat = false;

extern uint16 shadowPalette[16];

//bool gOverDraw = false;

bool gMeshUseCorrection = false;
bool gMeshUseAniso = false;

const float CORRECTION_CUTOFF = ( 25.0f * 12.0f );
const float ANISO_CUTOFF = ( 100.0f * 12.0f );

int meshes_considered = 0;

namespace NxNgc
{

Lst::HashTable< sTextureProjectionDetails >	*pTextureProjectionDetailsTable = NULL;

NsCamera	current_cam;

#ifdef SHORT_VERT
static void cam_offset( float x, float y, float z )
{
	Mtx m;

	MTXTrans( m, x, y, z );
	MTXConcat( EngineGlobals.current_uploaded, m, m );

    GX::LoadPosMtxImm( m, GX_PNMTX0 );
}
#endif		// SHORT_VERT

//static void reflect_uvs( sMesh * p_mesh, float * p_tex, s16 * p_posBuffer, s16 * p_normBuffer, int step )
//{
//	if ( sLastUV == p_tex ) return;
//
//	// Inverse transform the camera position by the local to world matrix.
//	NsMatrix mp;
////	NsMatrix mi;
//
//	mp.copy( *((NsMatrix*)EngineGlobals.current_uploaded) );
////	mp.copy( EngineGlobals.camera );
//#ifdef SHORT_VERT
//	NsVector v( p_mesh->m_offset_x, p_mesh->m_offset_y, p_mesh->m_offset_z );
//	mp.translate( &v, NsMatrix_Combine_Post );
//#endif		// SHORT_VERT
////	mi.invert( mp );
////	mp.setRight( mi.getRight() );
////	mp.setUp( mi.getUp() );
////	mp.setAt( mi.getAt() );
//
////	mp.invert();
//
////	mp.setPosX( mi.getPosX() );
////	mp.setPosY( mi.getPosY() );
////	mp.setPosZ( mi.getPosZ() );
//	
//	ROMtx sm;
//
//	sm[0][0] = mp.getRightX();
//	sm[1][0] = mp.getRightY();
//	sm[2][0] = mp.getRightZ();
//					   
//	sm[0][1] = mp.getUpX();
//	sm[1][1] = mp.getUpY();
//	sm[2][1] = mp.getUpZ();
//					   
//	sm[0][2] = mp.getAtX();
//	sm[1][2] = mp.getAtY();
//	sm[2][2] = mp.getAtZ();
//			     
//	sm[3][0] = mp.getPosX();
//	sm[3][1] = mp.getPosY();
//	sm[3][2] = mp.getPosZ();
//
//	if ( step == 6 )
//	{
//		// Skinned
//		FakeEnvMapSkin( sm, p_posBuffer, p_tex, p_mesh->m_num_vertex );
//	}
//	else
//	{
//		// Not Skinned
//		GQRSetup6( p_mesh->m_vertex_format - 1,		// Pos
//				   GQR_TYPE_S16,
//				   0,					// UV
//				   GQR_TYPE_F32 );
//
//		GQRSetup7( 14,		// Normal
//				   GQR_TYPE_S16,
//				   0,					// UV
//				   GQR_TYPE_F32 );
//
////		NsMatrix mn;
////		mn.copy( mp );
//////		mn.invert(;
////		mn.setPos( 0.0f, 0.0f, 0.0f );
////
////		float shift = (float)(1<<(p_mesh->m_vertex_format-1));
////		for ( int lp = 0; lp < p_mesh->m_num_vertex; lp++ )
////		{
////			NsVector in, tpos, tnorm;
////			in.x = (((float)p_posBuffer[(lp*3)+0])/shift);
////			in.y = (((float)p_posBuffer[(lp*3)+1])/shift);
////			in.z = (((float)p_posBuffer[(lp*3)+2])/shift);
////			mp.multiply( &in, &tpos );
////			in.x = (((float)p_normBuffer[(lp*3)+0])/(float)(1<<14));
////			in.y = (((float)p_normBuffer[(lp*3)+1])/(float)(1<<14));
////			in.z = (((float)p_normBuffer[(lp*3)+2])/(float)(1<<14));
////			mn.multiply( &in, &tnorm );
//////			float z1 = 1.0f / 2000.0f;
//////			p_tex[(lp*2)+0] = ( ( ( ( ( tpos.x / (tpos.z+(float)gAdd) ) * (float)gScale ) + tnorm.x ) + 1.0f ) * 0.5f ) * (float)gRepeat;
//////			p_tex[(lp*2)+1] = ( ( ( ( ( tpos.y / (tpos.z+(float)gAdd) ) * (float)gScale )  + tnorm.y ) + 1.0f ) * 0.5f ) * (float)gRepeat;
////			
////			p_tex[(lp*2)+0] = ( ( ( ( ( tpos.x / ( tpos.z > 1 ? 1 : tpos.z ) ) + tnorm.x ) * 1.0f ) * 0.5f ) + 0.5f );
////			p_tex[(lp*2)+1] = ( ( ( ( ( tpos.y / ( tpos.z > 1 ? 1 : tpos.z ) ) + tnorm.y ) * 1.0f ) * 0.5f ) + 0.5f );
////
//////			p_tex[(lp*2)+0] = ( ( ( ( tpos.x + ( tnorm.x * tpos.z ) ) * ( 0.5f * tpos.z ) ) * ( 3.0f * tpos.z ) ) + ( 0.5f * tpos.z ) );
//////			p_tex[(lp*2)+1] = ( ( ( ( tpos.y + ( tnorm.y * tpos.z ) ) * ( 0.5f * tpos.z ) ) * ( 3.0f * tpos.z ) ) + ( 0.5f * tpos.z ) );
////			
//////			p_tex[(lp*2)+0] = ( ( tnorm.x ) + 1.0f ) * 0.5f;
//////			p_tex[(lp*2)+1] = ( ( tnorm.y ) + 1.0f ) * 0.5f;
////		}
//
//		FakeEnvMap( sm, p_posBuffer, p_normBuffer, p_tex, p_mesh->m_num_vertex );
//
//		// GQR back to normal.
//		GQRSetup7( GQR_SCALE_128,		// Pos/Normal
//				   GQR_TYPE_S16,
//				   GQR_SCALE_128,
//				   GQR_TYPE_S16 );
//		GQRSetup6( GQR_SCALE_256,		// Weights
//				   GQR_TYPE_U8,
//				   GQR_SCALE_256,
//				   GQR_TYPE_U8 );
//	}
//	sLastUV = p_tex;
//}

#ifndef SHORT_VERT
//static void float_reflect_uvs( sMesh * p_mesh, float * p_tex, float * p_posBuffer, s16 * p_normBuffer, int step )
//{
//	if ( sLastUV == p_tex ) return;
//
//	// Inverse transform the camera position by the local to world matrix.
//	NsMatrix mp;
//
//	mp.copy( *((NsMatrix*)EngineGlobals.current_uploaded) );
//	
//	ROMtx sm;
//
//	sm[0][0] = mp.getRightX();
//	sm[1][0] = mp.getRightY();
//	sm[2][0] = mp.getRightZ();
//					   
//	sm[0][1] = mp.getUpX();
//	sm[1][1] = mp.getUpY();
//	sm[2][1] = mp.getUpZ();
//					   
//	sm[0][2] = mp.getAtX();
//	sm[1][2] = mp.getAtY();
//	sm[2][2] = mp.getAtZ();
//			     
//	sm[3][0] = mp.getPosX();
//	sm[3][1] = mp.getPosY();
//	sm[3][2] = mp.getPosZ();
//
////	GQRSetup6( 0,					// Pos
////			   GQR_TYPE_F32,
////			   0,					// UV
////			   GQR_TYPE_F32 );
////
////	GQRSetup7( 14,					// Normal
////			   GQR_TYPE_S16,
////			   0,					// UV
////			   GQR_TYPE_F32 );
//
//	FakeEnvMapFloat( sm, p_posBuffer, p_normBuffer, p_tex, p_mesh->m_num_vertex );
//
////	// GQR back to normal.
////	GQRSetup7( GQR_SCALE_128,		// Pos/Normal
////			   GQR_TYPE_S16,
////			   GQR_SCALE_128,
////			   GQR_TYPE_S16 );
////	GQRSetup6( GQR_SCALE_256,		// Weights
////			   GQR_TYPE_U8,
////			   GQR_SCALE_256,
////			   GQR_TYPE_U8 );
//	sLastUV = p_tex;
//}
#endif	// SHORT_VERT

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void init_render_system( void )
{
	pTextureProjectionDetailsTable = new Lst::HashTable< sTextureProjectionDetails >( 8 );

	MTXRotDeg( g_matrix0, 'y', 0.0f );
	MTXRotDeg( g_matrix90, 'y', 90.0f );
	MTXRotDeg( g_matrix180, 'y', 180.0f );
	MTXRotDeg( g_matrix270, 'y', 270.0f );
}

void set_blend_mode( uint64 mode )
{
	// Low 32 bits contain the mode, high 32 bits contain the fixed alpha value.
	uint32 actual_mode	= (uint32)mode;
//	uint32 fixed_alpha	= (uint32)( mode >> 32 );

	// Only do something if the blend mode is changing.
//	if( actual_mode != EngineGlobals.blend_mode_value )
	{
		switch( actual_mode )
		{
			case vBLEND_MODE_ADD:
			case vBLEND_MODE_ADD_FIXED:
				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
			case vBLEND_MODE_SUBTRACT:
			case vBLEND_MODE_SUB_FIXED:
				GX::SetBlendMode ( GX_BM_SUBTRACT, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
			case vBLEND_MODE_BLEND:
			case vBLEND_MODE_BLEND_FIXED:
				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
			case vBLEND_MODE_BRIGHTEN:
			case vBLEND_MODE_BRIGHTEN_FIXED:
				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
			case vBLEND_MODE_MODULATE_FIXED:
			case vBLEND_MODE_MODULATE:
				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
			case vBLEND_MODE_DIFFUSE:
			default:
				GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
		}
	}
}



void set_render_state( uint32 type, uint32 state )
{
	switch( type )
	{
		case RS_ZWRITEENABLE:
		{
			if( state > 0 )
			{
				if( EngineGlobals.z_write_enabled == FALSE )
				{
					GX::SetZMode ( EngineGlobals.z_test_enabled ? GX_TRUE : GX_FALSE, EngineGlobals.z_test_enabled ? GX_LEQUAL : GX_ALWAYS, GX_TRUE );
					EngineGlobals.z_write_enabled = TRUE;
				}
			}
			else
			{
				if( EngineGlobals.z_write_enabled == TRUE )
				{
					GX::SetZMode ( EngineGlobals.z_test_enabled ? GX_TRUE : GX_FALSE, EngineGlobals.z_test_enabled ? GX_LEQUAL : GX_ALWAYS, GX_FALSE );
					EngineGlobals.z_write_enabled = FALSE;
				}
			}
			break;
		}

		case RS_ZTESTENABLE:
		{
			if( state > 0 )
			{
				if( EngineGlobals.z_test_enabled == FALSE )
				{
					GX::SetZMode ( GX_TRUE, GX_LEQUAL, EngineGlobals.z_write_enabled ? GX_TRUE : GX_FALSE );
					EngineGlobals.z_test_enabled = TRUE;
				}
			}
			else
			{
				if( EngineGlobals.z_test_enabled == TRUE )
				{
					GX::SetZMode ( GX_FALSE, GX_ALWAYS, EngineGlobals.z_write_enabled ? GX_TRUE : GX_FALSE );
					EngineGlobals.z_test_enabled = FALSE;
				}
			}
			break;
		}

		case RS_ALPHACUTOFF:
		{
//			GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//			// Convert from state (where 1 means "render all pixels with alpha 1 or higher") to the D3D
//			if( state != EngineGlobals.alpha_ref )
//			{
//				EngineGlobals.alpha_ref = state;
//				if( state > 0 )
//				{
//					D3DDevice_SetRenderState( D3DRS_ALPHAFUNC,			D3DCMP_GREATEREQUAL );
//					D3DDevice_SetRenderState( D3DRS_ALPHAREF,			state );
//					D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE,	TRUE );
//				}
//				else
//				{
//					D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE,	FALSE );
//				}
//			}
			break;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float frustum_sort_mid( Mth::Vector& p_sphere )
{
	NsVector	test_in;
	NsVector	test_mid;
	
//	D3DXMATRIX *p_world_transform;
//	
//	// Build the composite transform if required.
//	if( p_bbox_transform )
//	{
//		p_world_transform = p_bbox_transform;
//	}
//	else
//	{
//		p_world_transform = NULL;
//	}
	
	test_in.x	= p_sphere[X];
	test_in.y	= p_sphere[Y];
	test_in.z	= p_sphere[Z];

	EngineGlobals.local_to_camera.multiply( &test_in, &test_mid );
	
//	if( p_world_transform )
//	{
//		D3DXVec3Transform( &test_mid, &test_in, p_world_transform );
//		test_in.x = test_mid.x;
//		test_in.y = test_mid.y;
//		test_in.z = test_mid.z;
//	}
//	D3DXVec3Transform( &test_mid, &test_in, &EngineGlobals.view_matrix );
	return test_mid.z;
}		



struct sSortedMeshEntry
{
	sMesh				*p_mesh;
	float				sort;
	sSortedMeshEntry	*pNext;
};


//static sSortedMeshEntry	sortedMeshArray[1000];

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int cmp( const void *p1, const void *p2 )
{
	return((sSortedMeshEntry*)p1)->sort < ((sSortedMeshEntry*)p2)->sort ? -1 : ((sSortedMeshEntry*)p1)->sort > ((sSortedMeshEntry*)p2)->sort ? 1 : 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void create_texture_projection_details( sTexture *p_texture, Nx::CNgcModel *p_model, sScene *p_scene )
{
	sTextureProjectionDetails *p_details = new sTextureProjectionDetails;

	p_details->p_model		= p_model;
	p_details->p_scene		= p_scene;
	p_details->p_texture	= p_texture;
	
	// Flag this scene as receiving shadows.
//	p_scene->m_flags		|= SCENE_FLAG_RECEIVE_SHADOWS;
	
	pTextureProjectionDetailsTable->PutItem((uint32)p_texture, p_details );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void destroy_texture_projection_details( sTexture *p_texture )
{
	sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->GetItem((uint32)p_texture );
	if( p_details )
	{
		pTextureProjectionDetailsTable->FlushItem((uint32)p_texture );
		delete p_details;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void set_texture_projection_camera( sTexture *p_texture, NsVector * p_pos, NsVector * p_at )
{
//	NsMatrix view_matrix;

	sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->GetItem((uint32)p_texture );
	if( p_details )
	{
		NsVector	up;
		// Check for 'straight down' vector.
		if(( p_pos->x == p_at->x ) && ( p_pos->z == p_at->z ))
		{
//			D3DXMatrixLookAtRH( &p_details->view_matrix, p_pos, p_at, &D3DXVECTOR3( 0.0f, 0.0f, 1.0f ));


			up.set( 0.0f, 0.0f, 1.0f );

		}
		else
		{
			up.set( 0.0f, 1.0f, 0.0f );
//			D3DXMatrixLookAtRH( &p_details->view_matrix, p_pos, p_at, &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ));
		}

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
//		NsVector screen_right;
//		NsVector screen_up;
//		screen_right.cross( *p_at, up );
//		screen_up.cross( screen_right, *p_at );
//
//		screen_right.normalize();
//		screen_up.normalize();
//
//		p_details->view_matrix.setRight( &screen_right );
//		p_details->view_matrix.setUp( &screen_up );
//		p_details->view_matrix.setAt( p_at );
//		p_details->view_matrix.setPos( p_pos );
		p_details->view_matrix.lookAt( p_pos, &up, p_at );

	

//		D3DXMatrixOrthoRH( &p_details->projection_matrix, 96.0f, 96.0f, 1.0f, 512.0f );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

//static D3DXMATRIX	world;
//static D3DXMATRIX	view;
//static D3DXMATRIX	projection;

void set_camera( Mth::Matrix *p_matrix, Mth::Vector *p_position, float screen_angle, float aspect_ratio, float scale )
{
	NsMatrix	m;
	NsFrame		f;

	EngineGlobals.tx = 2.0f * tanf(screen_angle*8.72664626e-03f);		// tan of half (angle in radians)
	EngineGlobals.ty = -(EngineGlobals.tx / aspect_ratio);
	EngineGlobals.near	= -2.0;
	EngineGlobals.far	= -40000.0;

	current_cam.perspective( EngineGlobals.tx, -EngineGlobals.ty, -EngineGlobals.near, -EngineGlobals.far * 2 );

//	if ( scale > 1.0f )
//	{
//		current_cam.SetViewMatrix( 2, 2, -0.999999f );
//		current_cam.SetViewMatrix( 3, 2, 0.0f );
//	}

	EngineGlobals.tx = tanf( Mth::DegToRad( screen_angle * 0.5f )); ;		// tan of half (angle in radians)
	EngineGlobals.ty = -(EngineGlobals.tx / aspect_ratio);
	EngineGlobals.sx	= 1.0f/sqrtf(1.0f+1.0f/(EngineGlobals.tx*EngineGlobals.tx));
	EngineGlobals.sy	= 1.0f/sqrtf(1.0f+1.0f/(EngineGlobals.ty*EngineGlobals.ty));
	EngineGlobals.cx	= 1.0f/sqrtf(1.0f+EngineGlobals.tx*EngineGlobals.tx);
	EngineGlobals.cy	= 1.0f/sqrtf(1.0f+EngineGlobals.ty*EngineGlobals.ty);
//	EngineGlobals.near	= -2.0;
//	EngineGlobals.far	= -40000.0;

	m.setRight( -p_matrix->GetRight().GetX(), -p_matrix->GetRight().GetY(), -p_matrix->GetRight().GetZ() );
	m.setUp( p_matrix->GetUp().GetX(), p_matrix->GetUp().GetY(), p_matrix->GetUp().GetZ() );
	m.setAt( -p_matrix->GetAt().GetX(), -p_matrix->GetAt().GetY(), -p_matrix->GetAt().GetZ() );
	m.setPos( p_position->GetX(), p_position->GetY(), p_position->GetZ() );

	f.setModelMatrix( &m );
	current_cam.setFrame( &f );
	current_cam.begin();		// No need to end...

	EngineGlobals.world_to_camera = *current_cam.getCurrent();
	EngineGlobals.local_to_camera = *current_cam.getCurrent();
	EngineGlobals.camera = m;

	EngineGlobals.object_pos.x = 0.0f;
	EngineGlobals.object_pos.y = 0.0f;
	EngineGlobals.object_pos.z = 0.0f;

	MTXCopy ( (MtxPtr)current_cam.getCurrent(), EngineGlobals.current_uploaded );


//	NsVector wscale( 1.0f, 1.0f, 1.0f );	//scale, scale, scale );
	NsVector wscale( scale, scale, scale );
	NsMatrix ms;
	ms.copy( m );
	ms.scale( &wscale, NsMatrix_Combine_Pre );

	f.setModelMatrix( &ms );
	current_cam.setFrame( &f );
	current_cam.end();
	current_cam.begin();		// No need to end...

//	NsMatrix ms;
//	ms.setRight( m.getRightX() * scale, m.getRightY() * scale, m.getRightZ() * scale );
//	ms.setUp( m.getUpX() * scale, m.getUpY() * scale, m.getUpZ() * scale );
//	ms.setAt( m.getAtX() * scale, m.getAtY() * scale, m.getAtZ() * scale );
//	ms.setPos( m.getPosX(), m.getPosY(), m.getPosZ() );


}

/******************************************************************/
/*                                                                */
/* Checks a bounding box against the current view frustum		  */
/* (iBgnoring far clipping),										  */
/* returns true if any part is visible.							  */
/* Horribly inefficient at present.								  */
/*                                                                */
/******************************************************************/
bool frustum_check_box( Mtx m, Mth::CBBox& box )
{
	Vec	test_in, test_out;
	
	uint32	cumulative_projection_space_outcode	= 0xFF;
	float	min_x = box.GetMin().GetX();
	float	min_y = box.GetMin().GetY();
	float	min_z = box.GetMin().GetZ();
	float	max_x = box.GetMax().GetX();
	float	max_y = box.GetMax().GetY();
	float	max_z = box.GetMax().GetZ();

	for( uint32 v = 0; v < 8; ++v )
	{
		uint32 projection_space_outcode = 0;

		test_in.x = ( v & 0x04 ) ? max_x : min_x;
		test_in.y = ( v & 0x02 ) ? max_y : min_y;
		test_in.z = ( v & 0x01 ) ? max_z : min_z;

		// At this point it's important to check to see whether the point is in postive or negative z-space, since
		// after the projection transform, both very large camera space z values and camera space z values where z < 0
		// will give results with z > 1. (Camera space values in the range [0,near] give negative projection space z values).
		MTXMultVec( m, &test_in, &test_out );

//		if(( -test_mid.z < 0.0f ) && ( !EngineGlobals.is_orthographic ))
//		{
//			test_out.x = -test_out.x;
//			test_out.y = -test_out.y;
//		}

		if( test_out.x > 1.0f )
		{
			projection_space_outcode |= 0x01;
		}
		else if( test_out.x < -1.0f )
		{
			projection_space_outcode |= 0x02;
		}

		if( test_out.y > 1.0f )
		{
			projection_space_outcode |= 0x04;
		}
		else if( test_out.y < -1.0f )
		{
			projection_space_outcode |= 0x08;
		}

		cumulative_projection_space_outcode	&= projection_space_outcode;

		if( cumulative_projection_space_outcode == 0 )
		{
			// Early out.
			return true;
		}
	}
	return false;
}

bool frustum_check_sphere( Mtx m, Mth::Vector& p_sphere )
{
//	Vec in;
//	Vec out;
//
//	in.x = p_sphere[X];
//	in.y = p_sphere[Y];
//	in.z = p_sphere[Z];
//
////	NsMatrix mat;
////	mat.copy( EngineGlobals.local_to_camera );
////	NsVector s( 0.5f, 0.5f, 0.5f );
////	mat.scale( &s, NsMatrix_Combine_Post );
////
////
////	mat.multiply( &in, &out );
////	EngineGlobals.local_to_camera.multiply( &in, &out );
//
//	MTXMultVec( m, &in, &out );
//
//	float x = out.x;
//	float y = out.y;
//	float z = out.z;
//	float R = p_sphere[W];
//
//	if ( R < ( z - EngineGlobals.near ) ) return false;
//	if ( R < ( EngineGlobals.far - z ) ) return false;
//
//	float sx_z = EngineGlobals.sx * z;
//	float cx_x = EngineGlobals.cx * x;
//	if ( R < ( sx_z - cx_x ) ) return false;
//	if ( R < ( sx_z + cx_x ) ) return false; 
//
//	float sy_z = EngineGlobals.sy * z;
//	float cy_y = EngineGlobals.cy * y;
//	if ( R < ( sy_z - cy_y ) ) return false;
//	if ( R < ( sy_z + cy_y ) ) return false; 
//
//	return true;

	Mth::CBBox bbox;
	Mth::Vector p;
	
	p.Set( p_sphere[X] + p_sphere[W], p_sphere[Y], p_sphere[Z] ); bbox.AddPoint( p );
	p.Set( p_sphere[X] - p_sphere[W], p_sphere[Y], p_sphere[Z] ); bbox.AddPoint( p );
	p.Set( p_sphere[X] , p_sphere[Y]+ p_sphere[W], p_sphere[Z] ); bbox.AddPoint( p );
	p.Set( p_sphere[X] , p_sphere[Y]- p_sphere[W], p_sphere[Z] ); bbox.AddPoint( p );
	p.Set( p_sphere[X] , p_sphere[Y], p_sphere[Z]+ p_sphere[W] ); bbox.AddPoint( p );
	p.Set( p_sphere[X] , p_sphere[Y], p_sphere[Z]- p_sphere[W] ); bbox.AddPoint( p );

	return frustum_check_box( m, bbox );
}

/******************************************************************/
/*                                                                */
/* Checks a bounding box against the current view frustum		  */
/* (iBgnoring far clipping),										  */
/* returns true if any part is visible.							  */
/* Horribly inefficient at present.								  */
/*                                                                */
/******************************************************************/
bool frustum_check_box( Mth::CBBox& box )
{
	int				lp;
	unsigned int	code;
	unsigned int	codeAND;
	f32				rx[8], ry[8], rz[8];
	f32				p[GX_PROJECTION_SZ];
	f32				vp[GX_VIEWPORT_SZ];
	u32				clip_x;
	u32				clip_y;
	u32				clip_w;
	u32				clip_h;
	float			clip_l;
	float			clip_t;
	float			clip_r;
	float			clip_b;
	MtxPtr			view;
	float			minx, miny, minz;
	float			maxx, maxy, maxz;

	GX::GetProjectionv( p );
	GX::GetViewportv( vp );
	GX::GetScissor ( &clip_x, &clip_y, &clip_w, &clip_h );
	clip_l = (float)clip_x;
	clip_t = (float)clip_y;
	clip_r = (float)(clip_x + clip_w);
	clip_b = (float)(clip_y + clip_h);

	view = (MtxPtr)&EngineGlobals.local_to_camera;

	minx = box.GetMin().GetX();
	miny = box.GetMin().GetY();
	minz = box.GetMin().GetZ();
	maxx = box.GetMax().GetX();
	maxy = box.GetMax().GetY();
	maxz = box.GetMax().GetZ();
	GX::Project ( minx, miny, minz, view, p, vp, &rx[0], &ry[0], &rz[0] );
	GX::Project ( minx, maxy, minz, view, p, vp, &rx[1], &ry[1], &rz[1] );
	GX::Project ( maxx, miny, minz, view, p, vp, &rx[2], &ry[2], &rz[2] );
	GX::Project ( maxx, maxy, minz, view, p, vp, &rx[3], &ry[3], &rz[3] );
	GX::Project ( minx, miny, maxz, view, p, vp, &rx[4], &ry[4], &rz[4] );
	GX::Project ( minx, maxy, maxz, view, p, vp, &rx[5], &ry[5], &rz[5] );
	GX::Project ( maxx, miny, maxz, view, p, vp, &rx[6], &ry[6], &rz[6] );
	GX::Project ( maxx, maxy, maxz, view, p, vp, &rx[7], &ry[7], &rz[7] );

	// Generate clip code. {page 178, Procedural Elements for Computer Graphics}
	// 1001|1000|1010
	//     |    |
	// ----+----+----
	// 0001|0000|0010
	//     |    |
	// ----+----+----
	// 0101|0100|0110
	//     |    |
	//
	// Addition: Bit 4 is used for z behind.

	codeAND	= 0x001f;
	for ( lp = 0; lp < 8; lp++ ) {
		// Only check x/y if z is valid (if z is invalid, the x/y values will be garbage).
		if ( rz[lp] > 1.0f   ) {
			code = (1<<4);
		} else {
			code = 0;
			if ( rx[lp] < clip_l ) code |= (1<<0);
			if ( rx[lp] > clip_r ) code |= (1<<1);
			if ( ry[lp] > clip_b ) code |= (1<<2);
			if ( ry[lp] < clip_t ) code |= (1<<3);
		}
		codeAND	&= code;
		if ( !codeAND ) return true;
	}
	if ( !codeAND ) return true;
	return false;
//	m_cull.clipCodeAND = codeAND;
//	// If any bits are set in the AND code, the object is invisible.
//	if ( codeAND ) {
//		m_cull.visible = 0;
//	}
//	
//	return !m_cull.visible;		// 0 = not culled, 1 = culled.


//
//	NsVector test_in, test_out;
//	
//	uint32	cumulative_projection_space_outcode	= 0xFF;
//	float	min_x = box.GetMin().GetX();
//	float	min_y = box.GetMin().GetY();
//	float	min_z = box.GetMin().GetZ();
//	float	max_x = box.GetMax().GetX();
//	float	max_y = box.GetMax().GetY();
//	float	max_z = box.GetMax().GetZ();
//
//	for( uint32 v = 0; v < 8; ++v )
//	{
//		uint32 projection_space_outcode = 0;
//
//		test_in.x = ( v & 0x04 ) ? max_x : min_x;
//		test_in.y = ( v & 0x02 ) ? max_y : min_y;
//		test_in.z = ( v & 0x01 ) ? max_z : min_z;
//
//		// At this point it's important to check to see whether the point is in postive or negative z-space, since
//		// after the projection transform, both very large camera space z values and camera space z values where z < 0
//		// will give results with z > 1. (Camera space values in the range [0,near] give negative projection space z values).
//		EngineGlobals.local_to_camera.multiply( &test_in, &test_out );
////		MTXMultVec( m, &test_in, &test_out );
//
////		if(( -test_mid.z < 0.0f ) && ( !EngineGlobals.is_orthographic ))
////		{
////			test_out.x = -test_out.x;
////			test_out.y = -test_out.y;
////		}
//
//		if( test_out.x > 1.0f )
//		{
//			projection_space_outcode |= 0x01;
//		}
//		else if( test_out.x < -1.0f )
//		{
//			projection_space_outcode |= 0x02;
//		}
//
//		if( test_out.y > 1.0f )
//		{
//			projection_space_outcode |= 0x04;
//		}
//		else if( test_out.y < -1.0f )
//		{
//			projection_space_outcode |= 0x08;
//		}
//
//		cumulative_projection_space_outcode	&= projection_space_outcode;
//
//		if( cumulative_projection_space_outcode == 0 )
//		{
//			// Early out.
//			return true;
//		}
//	}
//	return false;
}

/******************************************************************/
/* Quick determination of if something is visible or not, uses	  */
/* the previously calculated s and c vectors and the			  */
/* WorldToCamera transform (note, no attempt is made to ensure	  */
/* this is the same camera that the object will eventually be	  */
/* rendered with.												  */
/******************************************************************/
bool IsVisible( Mth::Vector &sphere )
{
	NsVector in;
	NsVector out;

	in.x = sphere.GetX();
	in.y = sphere.GetY();
	in.z = sphere.GetZ();

	EngineGlobals.world_to_camera.multiply( &in, &out );

	float x = out.x;
	float y = out.y;
	float z = out.z;
	float R = sphere[W];
	
	if(	//( -z + R < EngineGlobals.near ) ||
//	   if ( R < ( -10000.0f/*EngineGlobals.far*/ - z ) ) return false;
	   ( R < ( z - EngineGlobals.near ) ) ||
	   ( R < ( EngineGlobals.far - z ) ) ||

	   ( R < EngineGlobals.sy * z + EngineGlobals.cy * y ) ||
	   ( R < EngineGlobals.sy * z - EngineGlobals.cy * y ) ||
	   ( R < EngineGlobals.sx * z - EngineGlobals.cx * x ) ||
	   ( R < EngineGlobals.sx * z + EngineGlobals.cx * x ))
	{
		return false;
	}
	else
	{
		if( TestSphereAgainstOccluders( &sphere ))
		{
			// Occluded.
			return false;
		}
		else
		{
			return true;
		}
	}
//	if (Rcat( m_matTexProj, matBiasScale );

	// This is just for displaying the frustum lines.
/*
    // Convert from homogeneous texmap coords to worldspace
    D3DXMATRIX matInvTexView, matInvTexProj;
    D3DXMatrixInverse( &matInvTexView, NULL, &matTexView );
    D3DXMatrixInverse( &matInvTexProj, NULL, &matTexProj );          

    for( int i = 0; i < 8; i++ )
    {
        D3DXVECTOR4 vT( 0.0f, 0.0f, 0.0f, 1.0f );
        vT.x = (i%2) * ( i&0x2 ? -1.0f : 1.0f );
        vT.y = (i%2) * ( i&0x4 ? -1.0f : 1.0f );
        vT.z = (i%2) * ( 1.0f );

        D3DXVec4Transform( &vT, &vT, &matInvTexProj );
        D3DXVec4Transform( &vT, &vT, &matInvTexView );

        g_FrustumLines[i].x = vT.x / vT.w;
        g_FrustumLines[i].y = vT.y / vT.w;
        g_FrustumLines[i].z = vT.z / vT.w;
    }
*/
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_shadow_targets( void )
{
	GX::SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE);
//	D3DXMATRIX stored_view_matrix		= EngineGlobals.view_matrix;
//	D3DXMATRIX stored_projection_matrix	= EngineGlobals.projection_matrix;
	
	// Goes through the list of render target textures, rendering to each one in turn.
	pTextureProjectionDetailsTable->IterateStart();
	sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->IterateNext();
//
//	// Set the basic pixel shader that just copies a single color (in C0) over.
//	EngineGlobals.pixel_shader_constants[0]		= 1.0f;
//	EngineGlobals.pixel_shader_constants[1]		= 1.0f;
//	EngineGlobals.pixel_shader_constants[2]		= 1.0f;
//	EngineGlobals.pixel_shader_constants[3]		= 1.0f;
//	EngineGlobals.upload_pixel_shader_constants	= true;
//	set_pixel_shader( PixelShader3 );
//	EngineGlobals.pixel_shader_override			= PixelShader3;
//	
	// Clear background of shadow.
//	NsCamera cam;
//	cam.orthographic( 0, 0, 640, 448 );
//	cam.begin();
//
//	GX::SetZMode ( GX_FALSE, GX_ALWAYS, GX_TRUE );
//	//NsPrim::box( 0, 0, 256, 256, (GXColor){128,128,128,255} );
//	float zf = -( EngineGlobals.far + 0.5f );
//	NsPrim::quad( 0, 0, zf, 640, 0, zf, 640, 448, zf, 0, 448, zf, (GXColor){128,128,128,255} );
//	cam.end();

	while( p_details )
	{
		if( p_details->p_model )
		{
			// Set the new render target.
///			D3DDevice_SetRenderTarget( p_details->p_texture->pD3DSurface, NULL );
//
//			D3DDevice_Clear( 0, 0, D3DCLEAR_TARGET, 0, 1.0f, 0 );

			// Set the view and projection transforms.
//			EngineGlobals.view_matrix		= p_details->view_matrix;
//			EngineGlobals.projection_matrix	= p_details->projection_matrix;
//			EngineGlobals.is_orthographic	= true;

			// Render all instances for the CGeom's contained in this model.
			int num_geoms = p_details->p_model->GetNumGeoms();
			for( int i = 0; i < num_geoms; ++i )
			{
				Nx::CNgcGeom *p_ngc_geom	= static_cast( p_details->p_model->GetGeomByIndex( i ));
				CInstance *p_instance		= p_ngc_geom->GetInstance();
				
				if( p_instance->GetActive())
				{
					// Create the camera and attach a frame.
					NsCamera cam;
					//NsFrame frame;
					//cam.setFrame( &frame );
	
					// Assume parallel projection in this case.
					cam.orthographic();
					cam.orthographic( 0, 0, 256, 256 );
					cam.setViewWindow( 128, 128 );
					
					// Position the camera somewhere near the object, pointing directly at it.
					float	at[3];
					float	pos[3];
	
					at[0]	= p_instance->GetTransform()->GetPos()[X];
					at[1]	= p_instance->GetTransform()->GetPos()[Y];
					at[2]	= p_instance->GetTransform()->GetPos()[Z];
	
					pos[0]	= p_instance->GetTransform()->GetPos()[X];
					pos[1]	= p_instance->GetTransform()->GetPos()[Y] + 64.0f;
					pos[2]	= p_instance->GetTransform()->GetPos()[Z];
	
					g_shadow_object_pos = p_instance->GetTransform()->GetPos();
					g_shadow_object_pos[Y] += ( 12.0f * 3.0f );		// Amount of leeway...

					// Also need to take account of the view offset.
					Mth::Vector object_up	= p_instance->GetTransform()->GetUp();
					at[0]	+= object_up.GetX() * 36.0f;
					at[1]	+= object_up.GetY() * 36.0f;
					at[2]	+= object_up.GetZ() * 36.0f;
					pos[0]	+= object_up.GetX() * 36.0f;
					pos[1]	+= object_up.GetY() * 36.0f;
					pos[2]	+= object_up.GetZ() * 36.0f;
	
					cam.pos( pos[0], pos[1], pos[2] );
					cam.up( 1.0f, 0.0f, 0.0f );
					cam.lookAt( at[0], at[1], at[2] );
	
//					EngineGlobals.world_to_camera.identity();
//					EngineGlobals.world_to_camera.setPos( pos[0], pos[1], pos[2] );
//					EngineGlobals.world_to_camera.setRight( 1.0f, 0.0f, 0.0f );
//					EngineGlobals.world_to_camera.setUp( 0.0f, 0.0f, 1.0f );
//					EngineGlobals.world_to_camera.setAt( 0.0f, -1.0f, 0.0f );
	
					cam.begin();
					MTXCopy ( (MtxPtr)cam.getCurrent(), EngineGlobals.current_uploaded );
	
					EngineGlobals.world_to_camera.copy( *cam.getCurrent() );
					EngineGlobals.shadow_camera.copy( *cam.getCurrent() );
	
					// Flag the scene as having the shadow version rendered.
					p_instance->GetScene()->m_flags |= SCENE_FLAG_RENDERING_SHADOW;
	
					// Render the model.
//					ROMtx bone_mtx[60];
//					p_instance->Transform( vRENDER_SHADOW_1ST_PASS, bone_mtx, NULL );
//					p_instance->ClearFlag( NxNgc::CInstance::INSTANCE_FLAG_TRANSFORM_ME );
					p_instance->Render( vRENDER_SHADOW_1ST_PASS );
	
					// Clear the flag the scene as having the shadow version rendered.
					p_instance->GetScene()->m_flags &= ~SCENE_FLAG_RENDERING_SHADOW;
				
					// Flag the scene as self shadowing.
					p_instance->GetScene()->m_flags |= SCENE_FLAG_SELF_SHADOWS;
				}
			}
		}
		p_details = pTextureProjectionDetailsTable->IterateNext();
	}
//
//	// Pixel shader override no longer required.
//	EngineGlobals.pixel_shader_override = 0;
//	
//	// Restore the view and projection transforms.
//	EngineGlobals.view_matrix		= stored_view_matrix;
//	EngineGlobals.projection_matrix	= stored_projection_matrix;
//	EngineGlobals.is_orthographic	= false;
//
//	// Restore the world transform.
//	D3DDevice_SetTransform( D3DTS_WORLD, &EngineGlobals.world_matrix );
//	
//	// Restore the default render target.
//	D3DDevice_SetRenderTarget( EngineGlobals.p_RenderSurface, EngineGlobals.p_ZStencilSurface );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_shadow_meshes( sScene *p_scene, int *p_mesh_indices, int num_meshes )
{
}


void figure_wibble_vc( sScene *p_scene )
{
	if ( !p_scene->mp_scene_data ) return;

	// The vertex color wibble flag is placed in pass 0.
	NxNgc::sMaterialVCWibbleKeyHeader *	p_key_header	= p_scene->mp_vc_wibble; 
	NxNgc::sMaterialVCWibbleKey *		p_key			= (NxNgc::sMaterialVCWibbleKey *)&p_key_header[1]; 

	int current_time = (int)Tmr::GetTime();

	uint32 * p_col = p_scene->mp_col_pool;

	for ( int wibble = 0; wibble < p_scene->mp_scene_data->m_num_vc_wibbles; wibble++ )
	{
		int						num_keys		= p_key_header->m_num_frames;
		int						phase_shift		= p_key_header->m_phase;

		// Time parameters.
		int						start_time		= p_key[0].m_time;
		int						end_time		= p_key[(num_keys - 1)].m_time;
		int						period			= end_time - start_time;
		int						time			= start_time + (( current_time + phase_shift ) % period );

		// Locate the keyframe.
		int key;
		for( key = num_keys - 1; key >= 0; --key )
		{
			if( time >= p_key[key].m_time )
			{
				break;
			}
		}
		
		// Parameter expressing how far we are between between this keyframe and the next.
		float t = (float)( time - p_key[key].m_time ) / (float)( p_key[key+1].m_time - p_key[key].m_time );

		// Interpolate the color.
		GXColor	rgba;
		rgba.r = (uint8)((( 1.0f - t ) * p_key[key].m_color.r ) + ( t * p_key[key + 1].m_color.r ));
		rgba.g = (uint8)((( 1.0f - t ) * p_key[key].m_color.g ) + ( t * p_key[key + 1].m_color.g ));
		rgba.b = (uint8)((( 1.0f - t ) * p_key[key].m_color.b ) + ( t * p_key[key + 1].m_color.b ));
		rgba.a = (uint8)((( 1.0f - t ) * p_key[key].m_color.a ) + ( t * p_key[key + 1].m_color.a ));

		p_col[wibble] = *((uint32*)&rgba);

		// Next wibble sequence.
		p_key_header	= (NxNgc::sMaterialVCWibbleKeyHeader *)&p_key[num_keys];
		p_key			= (NxNgc::sMaterialVCWibbleKey *)&p_key_header[1]; 
	}
}

			//sMaterialHeader * p_mat = p_mesh->mp_dl->m_material.p_header;
			//sMaterialPassHeader * p_pass = (sMaterialPassHeader *)&p_mat[1];

// Cached GX commands.

float *	g_p_pos		= NULL;
char *	g_p_col0	= NULL;
//char *	g_p_col1	= NULL;
GXColor g_col0		= (GXColor){0,0,0,0};
//GXColor g_col1		= (GXColor){0,0,0,0};

static void _reset( void )
{
	g_p_pos		= NULL;
	g_p_col0	= NULL;
//	g_p_col1	= NULL;
	g_col0		= (GXColor){0,0,0,0};
//	g_col1		= (GXColor){0,0,0,0};
	GX::SetChanMatColor( GX_COLOR0A0, g_col0 );
//	GX::SetChanMatColor( GX_COLOR0A0, g_col1 );
}

static void _set_pos( float * p_pos )
{
	if ( g_p_pos != p_pos )
	{
		GX::SetArray( GX_VA_POS, p_pos, sizeof( float ) * 3 );
		g_p_pos = p_pos;
	}
}

static void _set_col0( char * p_col0 )
{
	if ( g_p_col0 != p_col0 )
	{
		GX::SetArray( GX_VA_CLR0, p_col0, sizeof( uint32 ) );
		g_p_col0 = p_col0;
	}
}

//static void _set_col1( char * p_col1 )
//{
//	if ( g_p_col1 != p_col1 )
//	{
//		GX::SetArray( GX_VA_CLR1, p_col1, sizeof( uint32 ) );
//		g_p_col1 = p_col1;
//	}
//}

static void _set_mat0( GXColor col )
{
	if ( ( col.r != g_col0.r ) || ( col.g != g_col0.g ) || ( col.b != g_col0.b ) || ( col.a != g_col0.a ) )
	{
		g_col0 = col;
		GX::SetChanMatColor( GX_COLOR0A0, col );
	}
}

//static void _set_mat1( GXColor col )
//{
//	if ( ( col.r != g_col1.r ) || ( col.g != g_col1.g ) || ( col.b != g_col1.b ) || ( col.a != g_col1.a ) )
//	{
//		g_col1 = col;
//		GX::SetChanMatColor( GX_COLOR1A1, col );
//	}
//}

static void submit_mesh( sScene *p_scene, sMesh * p_mesh, s16* p_posNormBuffer, Mth::Matrix * p_bone_xform, Mth::Matrix * p_instance_xform, int * p_array_base, int * p_col_base, int * p_layer, float * p_pm, GXColor * p_base_color, bool * set_base_color, bool * p_world_fog, uint32 flags )
{
//	if ( g_skip_correctable && p_mesh->mp_dl->m_material.p_header->m_flags & (1<<6) ) return;
	if ( (int)p_mesh->mp_dl->m_material.p_header->m_layer_id == g_kill_layer ) return;

	if ( !p_posNormBuffer )
	{
		// Deal with cloned pos pools.
		if ( p_mesh->mp_dl->mp_pos_pool )
		{
			_set_pos( p_mesh->mp_dl->mp_pos_pool );
		}
		else
		{
			// Deal with verts outside of 65535.
			_set_pos( &p_scene->mp_pos_pool[p_mesh->mp_dl->m_array_base*3] );
		}
	}

	// Deal with cloned color pools.
	if ( p_mesh->mp_dl->mp_col_pool )
	{
		_set_col0( &((char*)p_mesh->mp_dl->mp_col_pool)[0] );
//		if ( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<6) )
//		{
//			_set_col1( &((char*)p_mesh->mp_dl->mp_col_pool)[2] );
//		}
	}
	else
	{
		// Deal with resetting cloned color pools.
		// Assumes that this isn't a skinned object.
		_set_col0( &((char*)p_scene->mp_col_pool)[0] );
//		if ( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<6) )
//		{
//			_set_col1( &((char*)p_scene->mp_col_pool)[2] );
//		}
	}

	// Deal with zbias.
	if ( (int)p_mesh->mp_dl->m_material.p_header->m_layer_id != *p_layer )
	{
		*p_layer = p_mesh->mp_dl->m_material.p_header->m_layer_id;

		float layer = p_mesh->mp_dl->m_material.p_header->m_layer_id;

		float value = p_pm[6] + ( 150.0f * layer ) * p_pm[5];

		GXWGFifo.u8 = GX_LOAD_XF_REG;
		GXWGFifo.u16 = 0;
		GXWGFifo.u16 = 0x1025;
		GXWGFifo.f32 = value;
	}



	// Billboard tweaking.
//	enum BillboardType
//	{
//		BBT_NOT_BILLBOARD,
//		BBT_SCREEN_ALIGNED,
//		BBT_AXIAL_ALIGNED,
//	};
	
//	if ( p_mesh->mp_mod )
//	{
//		Mtx t;
//		Mtx m;
//
////		MTXTrans( m, p_mesh->mp_mod.offset->GetX(), p_mesh->mp_mod.offset->GetY(), p_mesh->mp_mod.offset->GetZ() );
////
////		MTXRotAxisDeg( 
//
//		float w = p_mesh->mp_mod->offset[W];
//		Mth::ERot90 rot = *((Mth::ERot90*)&w);
//
//		if ( p_mesh->mp_mod->scale[X] > 1.0f ) OSReport( "X Scale > 1.0f: %8.3f\n", p_mesh->mp_mod->scale[X] );
//		if ( p_mesh->mp_mod->scale[Y] > 1.0f ) OSReport( "Y Scale > 1.0f: %8.3f\n", p_mesh->mp_mod->scale[Y] );
//		if ( p_mesh->mp_mod->scale[Z] > 1.0f ) OSReport( "Z Scale > 1.0f: %8.3f\n", p_mesh->mp_mod->scale[Z] );
//
//		switch ( rot )
//		{
//			default:
//			case Mth::ROT_0:
//				MTXScaleApply( g_matrix0, t, p_mesh->mp_mod->scale[X], p_mesh->mp_mod->scale[Y], p_mesh->mp_mod->scale[Z] );
//				break;
//			case Mth::ROT_90:
//				MTXScaleApply( g_matrix90, t, p_mesh->mp_mod->scale[X], p_mesh->mp_mod->scale[Y], p_mesh->mp_mod->scale[Z] );
//				break;
//			case Mth::ROT_180:
//				MTXScaleApply( g_matrix180, t, p_mesh->mp_mod->scale[X], p_mesh->mp_mod->scale[Y], p_mesh->mp_mod->scale[Z] );
//				break;
//			case Mth::ROT_270:
//				MTXScaleApply( g_matrix270, t, p_mesh->mp_mod->scale[X], p_mesh->mp_mod->scale[Y], p_mesh->mp_mod->scale[Z] );
//				break;
//		}
//		t[0][3] = p_mesh->mp_mod->offset[X];
//		t[1][3] = p_mesh->mp_mod->offset[Y];
//		t[2][3] = p_mesh->mp_mod->offset[Z];
//		MTXConcat( EngineGlobals.current_uploaded, t, m );
//
//		GX::LoadPosMtxImm( m, GX_PNMTX0 );
//		reload_camera = true;
//	}

	// Set Fog color if necessary.
	bool mesh_world_fog = true;
	if ( p_mesh->mp_dl->m_material.p_header )
	{
		switch ( p_mesh->mp_dl->m_material.p_header->m_base_blend )
		{
			case vBLEND_MODE_ADD:
			case vBLEND_MODE_ADD_FIXED:
			case vBLEND_MODE_SUBTRACT:
			case vBLEND_MODE_SUB_FIXED:
				mesh_world_fog = false;
				break;
			default:
				break;
		}
	}
	if ( mesh_world_fog != *p_world_fog )
	{
		*p_world_fog = mesh_world_fog;
		if ( mesh_world_fog )
		{
			Nx::CFog::sSetFogColor();
		}
		else
		{
			GX::SetFogColor( (GXColor){0,0,0,0} );
		}
	}


	MaterialSubmit( p_mesh, p_scene );

	switch ( p_mesh->mp_dl->mp_object_header->m_billboard_type )
	{
		case 2:
			{
				NsMatrix*	p_matrix	= &EngineGlobals.world_to_camera;
				NsVector	at = *p_matrix->getAt();
//				at.x = -at.x;
//				at.y = -at.y;
//				at.z = -at.z;

				NsVector screen_right;
				NsVector screen_up;
				NsVector screen_at;

				NsMatrix	root;

				// Create coordinate system based on camera at & pivot axis.
				screen_up.set( p_mesh->mp_dl->mp_object_header->m_axis[X], p_mesh->mp_dl->mp_object_header->m_axis[Y], p_mesh->mp_dl->mp_object_header->m_axis[Z] );
//				screen_right.cross( *p_matrix->getAt(), screen_up );
				screen_right.cross( at, screen_up );
				screen_at.cross( screen_up, screen_right );

				screen_right.normalize();
				screen_at.normalize();

				root.setRightX( screen_right.x );
				root.setRightY( screen_up.x    );
				root.setRightZ( screen_at.x    );

				root.setUpX( screen_right.y );
				root.setUpY( screen_up.y    );
				root.setUpZ( screen_at.y    );

				root.setAtX( screen_right.z );
				root.setAtY( screen_up.z    );
				root.setAtZ( screen_at.z    );

//				root.setRightX( screen_right.x );
//				root.setRightY( screen_right.y );
//				root.setRightZ( screen_right.z );
//
//				root.setUpX( screen_up.x );
//				root.setUpY( screen_up.y );
//				root.setUpZ( screen_up.z );
//
//				root.setAtX( screen_at.x );
//				root.setAtY( screen_at.y );
//				root.setAtZ( screen_at.z );

				root.setPosX( p_mesh->mp_dl->mp_object_header->m_origin[X] );
				root.setPosY( p_mesh->mp_dl->mp_object_header->m_origin[Y] );
				root.setPosZ( p_mesh->mp_dl->mp_object_header->m_origin[Z] );

				root.cat( EngineGlobals.world_to_camera, root );

				GX::LoadPosMtxImm( (MtxPtr)&root, GX_PNMTX0 );
				//GXSetCurrentMtx( GX_PNMTX1 );
				reload_camera = true;
			}
			break;
		case 1:
			{
				NsMatrix*	p_matrix	= &EngineGlobals.world_to_camera;

				NsMatrix	root;

				// Just transpose the matrix for screen-facing.
				root.setRightX( p_matrix->getRightX() );
				root.setRightY( p_matrix->getUpX()    );
				root.setRightZ( p_matrix->getAtX()    );

				root.setUpX( p_matrix->getRightY() );
				root.setUpY( p_matrix->getUpY()    );
				root.setUpZ( p_matrix->getAtY()    );

				root.setAtX( p_matrix->getRightZ() );
				root.setAtY( p_matrix->getUpZ()    );
				root.setAtZ( p_matrix->getAtZ()    );

				root.setPosX( p_mesh->mp_dl->mp_object_header->m_origin[X] );
				root.setPosY( p_mesh->mp_dl->mp_object_header->m_origin[Y] );
				root.setPosZ( p_mesh->mp_dl->mp_object_header->m_origin[Z] );

				root.cat( EngineGlobals.world_to_camera, root );

				GX::LoadPosMtxImm( (MtxPtr)&root, GX_PNMTX0 );
				//GXSetCurrentMtx( GX_PNMTX1 );
				reload_camera = true;
			}
			break;
		default:
			// Don't do anything - not a billboard.
			//GXSetCurrentMtx( GX_PNMTX0 );
			break;
	}

//	// Create array of bone matrices if required.
//	NsMatrix	bone_xform[80];
//	if ( p_scene->m_numHierarchyObjects && p_bone_xform )
//	{
//		for ( int bone = 0; bone < num_bones; bone++ )
//		{
//			Mth::Matrix temp = p_bone_xform[bone] * *p_instance_xform;
//			NsMatrix	local;
//
//			local.setRightX( temp.GetRight()[X] );
//			local.setRightY( temp.GetUp()[X] );
//			local.setRightZ( temp.GetAt()[X] );
//
//			local.setUpX( temp.GetRight()[Y] ); 
//			local.setUpY( temp.GetUp()[Y] );    
//			local.setUpZ( temp.GetAt()[Y] );    
//
//			local.setAtX( temp.GetRight()[Z] ); 
//			local.setAtY( temp.GetUp()[Z] );    
//			local.setAtZ( temp.GetAt()[Z] );    
//
//			local.setPosX( temp.GetPos()[X] );
//			local.setPosY( temp.GetPos()[Y] );
//			local.setPosZ( temp.GetPos()[Z] );
//
//			bone_xform[bone].cat( EngineGlobals.world_to_camera, local );
//		}
//	}


	if ( p_scene->m_numHierarchyObjects && p_bone_xform )
	{
		NsMatrix	bone_xform;
		Mth::Matrix temp = p_bone_xform[p_mesh->m_bone_idx] * *p_instance_xform;
		NsMatrix	local;

		local.setRightX( temp.GetRight()[X] );
		local.setRightY( temp.GetUp()[X] );
		local.setRightZ( temp.GetAt()[X] );

		local.setUpX( temp.GetRight()[Y] ); 
		local.setUpY( temp.GetUp()[Y] );    
		local.setUpZ( temp.GetAt()[Y] );    

		local.setAtX( temp.GetRight()[Z] ); 
		local.setAtY( temp.GetUp()[Z] );    
		local.setAtZ( temp.GetAt()[Z] );    

		local.setPosX( temp.GetPos()[X] );
		local.setPosY( temp.GetPos()[Y] );
		local.setPosZ( temp.GetPos()[Z] );

		bone_xform.cat( EngineGlobals.world_to_camera, local );

		GX::LoadPosMtxImm( (MtxPtr)&bone_xform, GX_PNMTX0 );
		reload_camera = true;

		GX::LoadNrmMtxImm((MtxPtr)&bone_xform, GX_PNMTX0);

	}

	if ( /*p_scene->mp_scene_data->m_num_nrm ||*/ p_posNormBuffer || ( p_scene->m_numHierarchyObjects && p_bone_xform ) )
	{
//		GXLightObj light_obj[3];
//
//		// Always set ambient & diffuse colors.
//		GX::SetChanAmbColor( GX_COLOR0A0, NxNgc::EngineGlobals.ambient_light_color );
////		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){128,128,128,255} );
////				GX::SetChanAmbColor( GX_COLOR1A1, NxNgc::EngineGlobals.ambient_light_color );
//	
//		GX::InitLightColor( &light_obj[0], NxNgc::EngineGlobals.diffuse_light_color[0] );
//		GX::InitLightPos( &light_obj[0], NxNgc::EngineGlobals.light_x[0], NxNgc::EngineGlobals.light_y[0], NxNgc::EngineGlobals.light_z[0] );
//		GX::LoadLightObjImm( &light_obj[0], GX_LIGHT0 );
//	
//		GX::InitLightColor( &light_obj[1], NxNgc::EngineGlobals.diffuse_light_color[1] );
//		GX::InitLightPos( &light_obj[1], NxNgc::EngineGlobals.light_x[1], NxNgc::EngineGlobals.light_y[1], NxNgc::EngineGlobals.light_z[1] );
//		GX::LoadLightObjImm( &light_obj[1], GX_LIGHT1 );
//
//		// Set channel control for diffuse.
//		GX::SetChanCtrl( GX_COLOR0, GX_ENABLE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT0|GX_LIGHT1, GX_DF_CLAMP, GX_AF_NONE );
//		GX::SetChanCtrl( GX_ALPHA0, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 
//
////		if ( p_mesh->mp_dl->m_material.p_header->m_shininess > 0.0f )
////		{
////			// Set channel control for specular.
////			Vec  ldir;
////
////			ldir.x = 0.0f;	//NxNgc::EngineGlobals.light_x[0] * ( 1.0f / 200000.0f );
////			ldir.y = 0.0f;  //NxNgc::EngineGlobals.light_y[0] * ( 1.0f / 200000.0f );
////			ldir.z = -1.0f;  //NxNgc::EngineGlobals.light_z[0] * ( 1.0f / 200000.0f );
////
////			GX::InitSpecularDirv( &light_obj[2], &ldir );
////			GX::InitLightShininess( &light_obj[2], 8.0f );
////			GX::InitLightColor( &light_obj[2], (GXColor){255,255,255,255} );
////			GX::LoadLightObjImm( &light_obj[2], GX_LIGHT2 );
////
////			GX::SetChanCtrl( GX_COLOR1, GX_ENABLE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT2, GX_DF_NONE, GX_AF_SPEC );
////			GX::SetChanCtrl( GX_ALPHA1, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 
////		}
//
	}
	else
	{
		if ( !*set_base_color )
		{
			GX::SetChanCtrl( GX_COLOR0, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
			GX::SetChanCtrl( GX_ALPHA0, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 
////			if ( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<6) )
//			{
//				GX::SetChanCtrl( GX_COLOR1, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//				GX::SetChanCtrl( GX_ALPHA1, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 
//			}
			*set_base_color = true;
		}
//		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){128,128,128,255} );
//		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){32,32,32,255} );
		GXColor col;
		col.r = p_mesh->m_base_color.r;
		col.g = p_mesh->m_base_color.g;
		col.b = p_mesh->m_base_color.b;
		col.a = 255;

////		if ( !(p_mesh->mp_dl->m_material.p_header->m_flags & (1<<7)) )
//		if ( !(flags & vRENDER_LIT) )
//		{
//			// Not sure why we need to force this to be set...
////			_set_mat0( col );
////			GX::SetChanMatColor( GX_COLOR0A0, col );
////			if ( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<6) )
//			{
////				_set_mat1( col );
////				GX::SetChanMatColor( GX_COLOR1A1, col );
//			}
////			*((uint32*)p_base_color) = *((uint32*)&col);
//
//			col.r = col.r >> 1;
//			col.g = col.g >> 1;
//			col.b = col.b >> 1;
//		}

//		if ( *((uint32*)p_base_color) !=  *((uint32*)&col) )
		{
//			p_mesh->m_base_color.a = 255;
//			GX::SetChanMatColor( GX_COLOR0A0, col );

			_set_mat0( col );
//			if ( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<6) )
//			{
////				GX::SetChanMatColor( GX_COLOR1A1, col );
//				_set_mat1( (GXColor){col.b,col.a,0,0} );
//			}
//			*((uint32*)p_base_color) = *((uint32*)&col);
		}

//		GX::SetChanCtrl( GX_COLOR1A1, GX_DISABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//		GX::SetChanMatColor( GX_COLOR1A1, (GXColor){128,128,128,255} );
//		GX::SetChanAmbColor( GX_COLOR1A1, (GXColor){128,128,128,255} );
//		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){64,64,64,128} );
//		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){64,64,64,128} );
	}

	// Major hack!!!
//   if ( p_posNormBuffer )
//	{
//		unsigned char * p8 = (unsigned char *)&p_mesh->mp_dl[1];
//		for ( uint lp = 0; lp < p_mesh->mp_dl->m_size; lp++ ) GXWGFifo.u8 = *p8++;
//	}
//	else
//	{
	/*if ( !p_mesh->mp_dl->mp_pos_pool )*/ GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size );
//	}

	if ( reload_camera )
	{
		GX::LoadPosMtxImm( EngineGlobals.current_uploaded, GX_PNMTX0 );
//		GX::LoadPosMtxImm( (Mtx)current_cam.getCurrent(), GX_PNMTX0 );
		reload_camera = false;
	}

//	// Reload color texture if necessary.
//	if ( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<5) )
//	{
//		// Make sure color map is uploaded.
//		GX::UploadTexture(	colorMap,
//							64,	//COLOR_MAP_SIZE,
//							64, //COLOR_MAP_SIZE,
//							GX_TF_RGBA8,
//							GX_CLAMP,
//							GX_CLAMP,
//							GX_FALSE,
//							GX_LINEAR,
//							GX_LINEAR,
//							0.0f,
//							0.0f,
//							0.0f,
//							GX_FALSE,
//							GX_TRUE,
//							GX_ANISO_1,
//							GX_TEXMAP7 ); 
//	}
}

void render_scene( sScene *p_scene, s16* p_posNormBuffer, uint32 flags, Mth::Matrix * p_bone_xform, Mth::Matrix * p_instance_xform, int num_bones )
{
	// Don't render dictionary scenes.
	if( p_scene->m_is_dictionary )
	{
		return;
	}
	
//	// This forces the texture to go into the unusable region.
//	GX::UploadTexture(	colorMap,
//						64,
//						64,
//						GX_TF_RGBA8,
//						GX_CLAMP,
//						GX_CLAMP,
//						GX_FALSE,
//						GX_LINEAR,
//						GX_LINEAR,
//						0.0f,
//						0.0f,
//						0.0f,
//						GX_FALSE,
//						GX_TRUE,
//						GX_ANISO_1,
//						GX_TEXMAP7 ); 

	sLastUV = NULL;

	first_mat = true;

	p_last_material = NULL;
	last_correct = false;
//	if ( p_scene->m_numHierarchyObjects == 0 )
//	{
//		// Not hierarchical, set to matrix 0.
//		GXSetCurrentMtx( GX_PNMTX0 );
//	}

	ResetMaterialChange();
//	int meshes_rendered		= 0;

	figure_wibble_vc( p_scene );

#define MAX_SHADOW_MESHES 512

//	int shadow_meshes = 0;
//	sMesh *pShadow[MAX_SHADOW_MESHES];

//	sMesh	*visible_mesh_array[2000];
//	int		visible_mesh_array_index = 0;

	// Increment the scene framestamp.
//_scene->m_framestamp++;

	// Set up the texture generation matrix here.
//	Mtx light;
//	MTXLightOrtho( light, -128.0f, 128.0f, -128.0f, 128.0f, 0.5f, 0.5f, 0.5f, 0.5f );
//	MTXConcat( light, (Mtx)&EngineGlobals.shadow_camera, light );
//#ifndef SHORT_VERT
//	GX::LoadTexMtxImm( light, GX_TEXMTX7, GX_MTX3x4 );
//#endif		// SHORT_VERT

	if ( p_posNormBuffer )
	{
		GX::SetArray( GX_VA_POS,  p_posNormBuffer, sizeof( uint16 ) * 6 );
		GX::SetArray( GX_VA_NRM,  &p_posNormBuffer[3], sizeof( uint16 ) * 6 );
	}
	else
	{
		GX::SetArray( GX_VA_POS,  p_scene->mp_pos_pool, sizeof( float ) * 3 );
		GX::SetArray( GX_VA_NRM,  p_scene->mp_nrm_pool, sizeof( uint16 ) * 3 );
	}
	int array_base = 0;
	int col_base = 2;
	int layer = -1;
	bool set_chan = false;
	GXColor base_color = { 0, 0, 0, 0 };
	bool world_fog = true;
	Nx::CFog::sSetFogColor();
	GX::SetChanMatColor( GX_COLOR0A0, base_color );
	GX::SetChanMatColor( GX_COLOR1A1, base_color );
	f32 pm[GX_PROJECTION_SZ];
	GX::GetProjectionv( pm );


//	if ( flags & vRENDER_NEW_TEST )
	{
		// Pool setup.
		GX::SetArray( GX_VA_CLR0, &((char*)p_scene->mp_col_pool)[0], sizeof( uint32 ) );
		GX::SetArray( GX_VA_CLR1, &((char*)p_scene->mp_col_pool)[2], sizeof( uint32 ) );

//		GX::SetArray( GX_VA_CLR1, p_scene->mp_col_pool, sizeof( uint32 ) );
//		GX::SetArray( GX_VA_TEX0, p_scene->mp_tex_pool, sizeof( float ) * 2 );
//
//		GX::SetArray( GX_VA_TEX1, p_scene->mp_tex_pool, sizeof( float ) * 2 );
//		GX::SetArray( GX_VA_TEX2, p_scene->mp_tex_pool, sizeof( float ) * 2 );
//		GX::SetArray( GX_VA_TEX3, p_scene->mp_tex_pool, sizeof( float ) * 2 );

		GX::SetArray( GX_VA_TEX0, p_scene->mp_tex_pool, sizeof( uint16 ) * 2 );
		GX::SetArray( GX_VA_TEX1, p_scene->mp_tex_pool, sizeof( uint16 ) * 2 );
		GX::SetArray( GX_VA_TEX2, p_scene->mp_tex_pool, sizeof( uint16 ) * 2 );
		GX::SetArray( GX_VA_TEX3, p_scene->mp_tex_pool, sizeof( uint16 ) * 2 );

//		GXSetArray( GX_VA_TEX4, p_scene->mp_tex_pool, sizeof( float ) * 2 );
//		GXSetArray( GX_VA_TEX5, p_scene->mp_tex_pool, sizeof( float ) * 2 );
//		GXSetArray( GX_VA_TEX6, p_scene->mp_tex_pool, sizeof( float ) * 2 );
//		GXSetArray( GX_VA_TEX7, p_scene->mp_tex_pool, sizeof( float ) * 2 );
		//GXSetVtxAttrFmt( GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0 );
	}





	if ( g_object == g_view_object ) g_view_scene = p_scene;

	// Render all meshes.
#	ifdef __USE_PROFILER__
						Sys::VUProfiler->PushContext( 128,0,0 );
#	endif		// __USE_PROFILER__

//						{
//							GX::SetFog( GX_FOG_NONE, 0.0f, 0.0f, 0.0f, 0.0f, (GXColor){0,0,0,0} );
//							GX::SetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0 );
//
//							GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_KONST,
//												 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//
//							GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST,
//													 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV,
//													 GX_TEV_SWAP0, GX_TEV_SWAP0 );
//
//							GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
//
//							GX::SetTevKSel( GX_TEVSTAGE0, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A );
//							GX::SetTevKColor( GX_KCOLOR0, (GXColor){8,8,8,255} );
//							GX::SetChanCtrl( GX_ALPHA0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 
//							GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255});
//							GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255});
//
//							GX::SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE);
//							GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE ); 
//						}

	///////////////////////////////////////////////////////////////////////////////////////////
	// Shadow Target.
	///////////////////////////////////////////////////////////////////////////////////////////

	if ( flags & vRENDER_SHADOW_1ST_PASS )
	{
//		GX::SetFog( GX_FOG_NONE, 0.0f, 0.0f, 0.0f, 0.0f, (GXColor){0,0,0,0} );
		GX::SetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0 );

		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_KONST,
							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );

		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST,
								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV,
								 GX_TEV_SWAP0, GX_TEV_SWAP0 );

		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );		// Replace

		GX::SetTevKSel( GX_TEVSTAGE0, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A );
		GX::SetTevKColor( GX_KCOLOR0, (GXColor){64,64,64,255} );
		GX::SetChanCtrl( GX_ALPHA0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 
		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255});
		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255});
		GX::SetChanMatColor( GX_COLOR1A1, (GXColor){255,255,255,255});
		GX::SetChanAmbColor( GX_COLOR1A1, (GXColor){255,255,255,255});
		base_color = (GXColor){ 255, 255, 255, 255 };

//		GX::SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE);
		GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE ); 

		for ( int mesh = 0; mesh < p_scene->m_num_filled_meshes; mesh++ )
		{
			sMesh * p_mesh = p_scene->mpp_mesh_list[mesh];

			if ( ( p_mesh->m_flags & sMesh::MESH_FLAG_VISIBLE ) &&
				 ( ( g_object != g_view_object ) || ( &p_scene->mp_material_header[g_material] != p_mesh->mp_dl->m_material.p_header ) ) )
			{
				GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size );


//				submit_mesh( p_scene, p_mesh, p_posNormBuffer, p_bone_xform, p_instance_xform, &array_base, &col_base, &layer, pm, &world_fog, flags );
			}
		}
	}

	///////////////////////////////////////////////////////////////////////////////////////////
	// Opaque meshes.
	///////////////////////////////////////////////////////////////////////////////////////////

	_reset();
	if ( flags & vRENDER_OPAQUE )
	{
//		GX::SetZMode( GX_TRUE, GX_LEQUAL, GX_TRUE );
		for ( int mesh = 0; mesh < p_scene->m_num_opaque_meshes; mesh++ )
		{
			sMesh * p_mesh = p_scene->mpp_mesh_list[mesh];

			if ( ( p_mesh->m_flags & sMesh::MESH_FLAG_VISIBLE ) &&
				 ( ( g_object != g_view_object ) || ( &p_scene->mp_material_header[g_material] != p_mesh->mp_dl->m_material.p_header ) ) )
			{
				submit_mesh( p_scene, p_mesh, p_posNormBuffer, p_bone_xform, p_instance_xform, &array_base, &col_base, &layer, pm, &base_color, &set_chan, &world_fog, flags );
			}
		}
	}

	///////////////////////////////////////////////////////////////////////////////////////////
	// Semitransparent meshes.
	///////////////////////////////////////////////////////////////////////////////////////////

	if ( flags & vRENDER_SEMITRANSPARENT )
	{
//		GX::SetZMode( GX_TRUE, GX_LEQUAL, GX_TRUE );
		int opaque		= p_scene->m_num_opaque_meshes;
		int presemi		= opaque + p_scene->m_num_pre_semitrans_meshes;
		int dynsemi		= presemi + p_scene->m_num_dynamic_semitrans_meshes;
		int postsemi	= dynsemi + p_scene->m_num_post_semitrans_meshes;

		// Pre meshes.
		_reset();
		for ( int mesh = opaque; mesh < presemi; mesh++ )
		{
			sMesh * p_mesh = p_scene->mpp_mesh_list[mesh];

			if ( ( p_mesh->m_flags & sMesh::MESH_FLAG_VISIBLE ) &&
				 ( ( g_object != g_view_object ) || ( &p_scene->mp_material_header[g_material] != p_mesh->mp_dl->m_material.p_header ) ) )
			{
				submit_mesh( p_scene, p_mesh, p_posNormBuffer, p_bone_xform, p_instance_xform, &array_base, &col_base, &layer, pm, &base_color, &set_chan, &world_fog, flags );
			}
		}

		// Dynamic meshes.
		sSortedMeshEntry sortedMeshArray[1000];
		int next_sorted_mesh_entry	= 0;

		for ( int mesh = presemi; mesh < dynsemi; mesh++ )
		{
			sMesh * p_mesh = p_scene->mpp_mesh_list[mesh];

			if ( ( p_mesh->m_flags & sMesh::MESH_FLAG_VISIBLE ) &&
				 ( ( g_object != g_view_object ) || ( &p_scene->mp_material_header[g_material] != p_mesh->mp_dl->m_material.p_header ) ) )
			{
				sortedMeshArray[next_sorted_mesh_entry].p_mesh	= p_mesh;
				sortedMeshArray[next_sorted_mesh_entry].sort	= frustum_sort_mid( p_mesh->mp_dl->m_sphere );
				++next_sorted_mesh_entry;
			}
		}
		qsort( sortedMeshArray, next_sorted_mesh_entry, sizeof( sSortedMeshEntry ), cmp );

		_reset();
		for( int m = 0; m < next_sorted_mesh_entry; ++m )
		{
			submit_mesh( p_scene, sortedMeshArray[m].p_mesh, p_posNormBuffer, p_bone_xform, p_instance_xform, &array_base, &col_base, &layer, pm, &base_color, &set_chan, &world_fog, flags );
		}

		// Post meshes.
		_reset();
		for ( int mesh = dynsemi; mesh < postsemi; mesh++ )
		{
			sMesh * p_mesh = p_scene->mpp_mesh_list[mesh];

			if ( ( p_mesh->m_flags & sMesh::MESH_FLAG_VISIBLE ) &&
				 ( ( g_object != g_view_object ) || ( &p_scene->mp_material_header[g_material] != p_mesh->mp_dl->m_material.p_header ) ) )
			{
				submit_mesh( p_scene, p_mesh, p_posNormBuffer, p_bone_xform, p_instance_xform, &array_base, &col_base, &layer, pm, &base_color, &set_chan, &world_fog, flags );
			}
		}
	}



	///////////////////////////////////////////////////////////////////////////////////////////
	// Shadow Mesh.
	///////////////////////////////////////////////////////////////////////////////////////////

	if ( flags & vRENDER_SHADOW_2ND_PASS )
	{
		Nx::CFog::sEnableFog( false );
		Mtx light;
		MTXLightOrtho( light, -128.0f, 128.0f, -128.0f, 128.0f, 0.5f, 0.5f, 0.5f, 0.5f );
		MTXConcat( light, (Mtx)&EngineGlobals.shadow_camera, light );
		GX::LoadTexMtxImm( light, GX_TEXMTX7, GX_MTX3x4 );
		GX::SetZMode( GX_TRUE, GX_LEQUAL, GX_FALSE );

		GX::SetZCompLoc( GX_TRUE );
		GX::UploadTexture(  shadowTextureData,
							SHADOW_TEXTURE_SIZE,
							SHADOW_TEXTURE_SIZE,
							GX_TF_I4,
							GX_CLAMP,
							GX_CLAMP,
							GX_FALSE,
							GX_LINEAR,
							GX_LINEAR,
							0.0f,
							0.0f,
							0.0f,
							GX_FALSE,
							GX_TRUE,
							GX_ANISO_1,
							GX_TEXMAP0 ); 
		GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, SHADOW_TEXTURE_SIZE, SHADOW_TEXTURE_SIZE );

		GX::UploadPalette( shadowPalette,
						   GX_TL_RGB5A3,
						   GX_TLUT_16,
						   GX_TEXMAP0 );

		GX::SetTexChanTevIndCull( 1, 0, 1, 0, GX_CULL_NONE );
		GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX3x4, GX_TG_POS, GX_FALSE, GX_PTIDENTITY );

		GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

		GX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0 );
//		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );		// Replace
		GX::SetBlendMode( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
		GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_TEXMTX7, GX_TEXMTX7, GX_TEXMTX7, GX_TEXMTX7 );

		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO,
								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV,
								 GX_TEV_SWAP0, GX_TEV_SWAP0 );
		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ONE, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ZERO,
							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );


//out_reg = (d (op) ((1.0 - c)*a + c*b) + bias) * scale;

		GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );

//		visible_mesh_array[visible_mesh_array_index++] = p_scene->m_semitransparent_meshes[e];
//		for ( int mesh = 0; mesh < p_scene->m_num_filled_meshes; mesh++ )

		layer = 5;		// Force subsequent materials to reset the layer.

//		float value = pm[6] + ( 80.0f * p_mesh->mp_dl->m_material.p_header->m_layer_id ) * pm[5];
		float value = pm[6] + ( 150.0f * 5 ) * pm[5];

		GXWGFifo.u8 = GX_LOAD_XF_REG;
		GXWGFifo.u16 = 0;
		GXWGFifo.u16 = 0x1025;
		GXWGFifo.f32 = value;

//		for ( int mesh = 0; mesh < ( p_scene->m_num_opaque_meshes + p_scene->m_num_pre_semitrans_meshes ); mesh++ )
		for ( int mesh = 0; mesh < ( p_scene->m_num_filled_meshes ); mesh++ )

		{
			sMesh * p_mesh = p_scene->mpp_mesh_list[mesh];

//			if( frustum_check_sphere( light, p_scene->m_opaque_meshes[e]->m_sphere ))
			float by;

//			if ( p_mesh->mp_mod )
//			{
////				by = ( p_mesh->m_bottom_y * p_mesh->mp_mod->scale[Y] ) + p_mesh->mp_mod->offset[Y];
//				by = p_mesh->mp_mod->offset[Y];
//			}
//			else
//			{
				by = p_mesh->m_bottom_y;
//			}

			if ( ( p_mesh->m_flags & sMesh::MESH_FLAG_VISIBLE ) &&
				 frustum_check_sphere( light, p_mesh->mp_dl->m_sphere ) &&
				 ( g_shadow_object_pos[Y] > by ) &&
				 !( p_mesh->m_flags & sMesh::MESH_FLAG_NO_SKATER_SHADOW ) )
			{

//				if ( (int)p_mesh->mp_dl->m_material.p_header->m_layer_id == g_kill_layer ) return;

				// Deal with cloned pos pools.
				if ( p_mesh->mp_dl->mp_pos_pool )
				{
					array_base = -1;
					GX::SetArray( GX_VA_POS, p_mesh->mp_dl->mp_pos_pool, sizeof( float ) * 3 );
				}
				else
				{
					// Deal with verts outside of 65535.
					if ( p_mesh->mp_dl->m_array_base != (uint32)array_base )
					{
						array_base = p_mesh->mp_dl->m_array_base;

						// Assumes that this isn't a skinned object.
						GX::SetArray( GX_VA_POS, &p_scene->mp_pos_pool[p_mesh->mp_dl->m_array_base*3], sizeof( float ) * 3 );
					}
				}

				GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size );


//				submit_mesh( p_scene, p_mesh, p_posNormBuffer, p_bone_xform, p_instance_xform, &array_base, &col_base, &layer, pm, &world_fog, flags );
			}
		}
		Nx::CFog::sEnableFog( true );
		world_fog = true;
	}








//	// Render opaque meshes.
//	if( (flags & vRENDER_OPAQUE) && !(flags & vRENDER_SHADOW_1ST_PASS) )
//	{
//		for( int e = 0; e < p_scene->m_num_opaque_entries; ++e )
//		{
//			if ( p_scene->m_opaque_meshes[e]->m_flags & sMesh::MESH_FLAG_VISIBLE )
//			{
//				sMesh * p_mesh = p_scene->m_opaque_meshes[e];
//		
////				set_render_state( RS_ZWRITEENABLE, p_mesh->m_zwrite );
//
//				if ( flags & vRENDER_NEW_TEST )
//				{
//					// Textured.
//					// First pass material setup.
//					sMaterialHeader * p_mat = p_mesh->mp_dl->m_material.p_header;
//					sMaterialPassHeader * p_pass = (sMaterialPassHeader *)&p_mat[1];
//
//					if ( p_pass[0].m_texture.p_data )
//					{
//						if ( first_mat || !one_mat )
//						{
//							first_mat = false;
//
//							MaterialSubmit( p_mesh );
//						}
//						GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size ); 
//					}
//					else
//					{
//						// Untextured.
//						if ( !(p_mesh->mp_dl->m_flags & 0x00200000) )
//						{
//							GXSetNumTevStages( 1 );
//							GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
//							GXSetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//							GXSetNumTexGens( 0 );
//							GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//							GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
////							GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//							GXSetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//							GXSetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
//							GXSetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
//							GXSetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//							GXSetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
////							GXSetChanMatColor( GX_COLOR0A0, p_pass->m_color );
//
//							GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size ); 
//						}
//					}
//				}
//				else
//				{
//					multi_mesh( p_mesh, p_posNormBuffer );
//				}
//
//				// Now draw the opaque meshes with shadow mapped on them.
//				if( p_scene->m_flags & SCENE_FLAG_RECEIVE_SHADOWS )
//				{
//					if( !( p_mesh->m_flags & sMesh::MESH_FLAG_NO_SKATER_SHADOW ) )
//					{
//						if( frustum_check_sphere( light, p_scene->m_opaque_meshes[e]->m_sphere ))
//						{
//							//if( frustum_check_box( light, p_scene->m_opaque_meshes[e]->m_bbox ))
//							{
//								if ( shadow_meshes <  MAX_SHADOW_MESHES )
//								{
//									pShadow[shadow_meshes] = p_mesh;
//									shadow_meshes++;
//								}
//							}
//						}
//					}
//				}
//			}
//		}			
//	}			
//
//// Render semi-transparent meshes.
//	if( (flags & vRENDER_SEMITRANSPARENT) && !(flags & vRENDER_SHADOW_1ST_PASS) )
//	{
//		int e = 0;
//		int next_sorted_mesh_entry = 0;
//		
//		// Semitransparent rendering is done in three stages.
//		// The first stage is meshes in the list up to the point where dynamic sorting starts.
//		// The second stage is meshes in the list which use dynamic sorting.
//		// The third stage is meshes in the list beyond the point where dynamic sorting ends.
//		for( ; e < p_scene->m_first_dynamic_sort_entry; ++e )
//		{
//			if ( p_scene->m_semitransparent_meshes[e]->m_flags & sMesh::MESH_FLAG_VISIBLE )
//			{
//				sMesh * p_mesh = p_scene->m_semitransparent_meshes[e];
//		
////				set_render_state( RS_ZWRITEENABLE, p_mesh->m_zwrite );
//
//				++meshes_rendered;
//
//				if ( flags & vRENDER_NEW_TEST )
//				{
//					// Textured.
//					// First pass material setup.
//					sMaterialHeader * p_mat = p_mesh->mp_dl->m_material.p_header;
//					sMaterialPassHeader * p_pass = (sMaterialPassHeader *)&p_mat[1];
//
//					if ( p_pass[0].m_texture.p_data )
//					{
//						if ( first_mat || !one_mat )
//						{
//							first_mat = false;
//
//							MaterialSubmit( p_mesh );
//						}
//
//						GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size ); 
//					}
//					else
//					{
//						// Untextured.
//						if ( !(p_mesh->mp_dl->m_flags & 0x00200000) )
//						{
//							GXSetNumTevStages( 1 );
//							GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
//							GXSetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//							GXSetNumTexGens( 0 );
//							GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//							GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//							GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//							GXSetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//							GXSetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
//							GXSetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
//							GXSetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//							GXSetChanMatColor( GX_COLOR0A0, p_pass->m_color );
//
//							GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size ); 
//						}
//					}
//				}
//				else
//				{
//					multi_mesh( p_mesh, p_posNormBuffer );
//				}
//			}
//		}
//
//		if( p_scene->m_num_dynamic_sort_entries > 0 )
//		{
//			// Second stage - dynamically sorted meshes.
//			int last_dynamic_sort_entry = p_scene->m_first_dynamic_sort_entry + p_scene->m_num_dynamic_sort_entries;
//			for( ; e < last_dynamic_sort_entry; ++e )
//			{
//				if ( p_scene->m_semitransparent_meshes[e]->m_flags & sMesh::MESH_FLAG_VISIBLE )
//				{
//					++meshes_rendered;
//					// Figure the midpoint of this mesh, and sort it into the render list.
//					float midpoint = frustum_sort_mid( p_scene->m_semitransparent_meshes[e]->m_sphere );
//
////					// Add this mesh to the visible list.
////					if( !( p_scene->m_semitransparent_meshes[e]->m_flags & sMesh::MESH_FLAG_NO_SKATER_SHADOW ))
////					{
////						visible_mesh_array[visible_mesh_array_index++] = p_scene->m_semitransparent_meshes[e];
////					}
//
//					sortedMeshArray[next_sorted_mesh_entry].p_mesh	= p_scene->m_semitransparent_meshes[e];
//					sortedMeshArray[next_sorted_mesh_entry].sort	= midpoint;
//					++next_sorted_mesh_entry;
//				}
//			}
//			if( next_sorted_mesh_entry > 0 )
//			{
//				// Sort the array into ascending sort order.
//				qsort( sortedMeshArray, next_sorted_mesh_entry, sizeof( sSortedMeshEntry ), cmp );
//		
//				for( int m = 0; m < next_sorted_mesh_entry; ++m )
//				{
//					sMesh * p_mesh = sortedMeshArray[m].p_mesh;
//
////					set_render_state( RS_ZWRITEENABLE, p_mesh->m_zwrite );
//
//					if ( flags & vRENDER_NEW_TEST )
//					{
//						// Textured.
//						// First pass material setup.
//						sMaterialHeader * p_mat = p_mesh->mp_dl->m_material.p_header;
//						sMaterialPassHeader * p_pass = (sMaterialPassHeader *)&p_mat[1];
//
//						if ( p_pass[0].m_texture.p_data )
//						{
//							if ( first_mat || !one_mat )
//							{
//								first_mat = false;
//
//								MaterialSubmit( p_mesh );
//							}
//
//							GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size ); 
//						}
//						else
//						{
//							// Untextured.
//							if ( !(p_mesh->mp_dl->m_flags & 0x00200000) )
//							{
//	    						GXSetNumTevStages( 1 );
//								GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
//								GXSetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//								GXSetNumTexGens( 0 );
//								GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//								GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//								GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//								GXSetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//								GXSetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
//								GXSetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
//								GXSetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//								GXSetChanMatColor( GX_COLOR0A0, p_pass->m_color );
//
//								GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size ); 
//							}
//						}
//					}
//					else
//					{
//						multi_mesh( p_mesh, p_posNormBuffer );
//					}
//				}
//			}
//		
//			// Third stage - meshes after the dynamically sorted set.
//			for( ; e < p_scene->m_num_semitransparent_entries; ++e )
//			{
//				if ( p_scene->m_semitransparent_meshes[e]->m_flags & sMesh::MESH_FLAG_VISIBLE )
//				{
//					sMesh * p_mesh = p_scene->m_semitransparent_meshes[e];
//
////					set_render_state( RS_ZWRITEENABLE, p_mesh->m_zwrite );
//
//					++meshes_rendered;
//
//					if ( flags & vRENDER_NEW_TEST )
//					{
//						// Textured.
//						// First pass material setup.
//						sMaterialHeader * p_mat = p_mesh->mp_dl->m_material.p_header;
//						sMaterialPassHeader * p_pass = (sMaterialPassHeader *)&p_mat[1];
//
//						if ( p_pass[0].m_texture.p_data )
//						{
//							if ( first_mat || !one_mat )
//							{
//								first_mat = false;
//
//								MaterialSubmit( p_mesh );
//							}
//
//							GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size ); 
//						}
//						else
//						{
//							// Untextured.
//							if ( !(p_mesh->mp_dl->m_flags & 0x00200000) )
//							{
//								GXSetNumTevStages( 1 );
//								GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
//								GXSetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//								GXSetNumTexGens( 0 );
//								GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//								GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//								GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//								GXSetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//								GXSetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
//								GXSetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
//								GXSetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//								GXSetChanMatColor( GX_COLOR0A0, p_pass->m_color );
//
//								GX::CallDisplayList ( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size ); 
//							}
//						}
//					}
//					else
//					{
//						multi_mesh( p_mesh, p_posNormBuffer );
//					}
//				}
//			}
//		}
//
////		// Now draw the semitransparent meshes with shadow mapped on them.
////		if( p_scene->m_flags & SCENE_FLAG_RECEIVE_SHADOWS )
////		{
////			render_shadow_meshes( p_scene, visible_mesh_array, visible_mesh_array_index );
////		}
//	}
//
////	if ( 0 )
//	{
//	// Render shadow meshes.
//	if( flags & vRENDER_SHADOW_1ST_PASS )
//	{
//		int e;
//		for( e = 0; e < p_scene->m_num_opaque_entries; ++e )
//		{
//			if ( p_scene->m_opaque_meshes[e]->m_flags & sMesh::MESH_FLAG_VISIBLE )
//			{
//				sMesh * p_mesh = p_scene->m_opaque_meshes[e];
//		
////				set_render_state( RS_ZWRITEENABLE, p_mesh->m_zwrite );
//
//				if ( p_posNormBuffer ) {
//					// Skinned.
//					multi_start_16 ( &p_posNormBuffer[0], &p_posNormBuffer[3], 6, 6 );
//				} else {
//					// Not skinned.
//#ifdef SHORT_VERT
//					cam_offset( p_mesh->m_offset_x, p_mesh->m_offset_y, p_mesh->m_offset_z );
//					multi_start_16 ( p_mesh->mp_posBuffer, p_mesh->mp_normBuffer, 3, 3 );
//#else
//					multi_start ( p_mesh->mp_posBuffer, p_mesh->mp_normBuffer, 3, 3 );
//#endif		// SHORT_VERT
//				}
//				
//				multi_end( p_mesh, vRENDER_SHADOW_1ST_PASS );
//			}
//		}			
//
//		for( e = 0; e < p_scene->m_num_semitransparent_entries; ++e )
//		{
//			if ( p_scene->m_semitransparent_meshes[e]->m_flags & sMesh::MESH_FLAG_VISIBLE )
//			{
//				sMesh * p_mesh = p_scene->m_semitransparent_meshes[e];
//
////				set_render_state( RS_ZWRITEENABLE, p_mesh->m_zwrite );
//
//				if ( p_posNormBuffer ) {
//					// Skinned.
//					multi_start_16 ( &p_posNormBuffer[0], &p_posNormBuffer[3], 6, 6 );
//				} else {
//					// Not skinned.
//#ifdef SHORT_VERT
//					cam_offset( p_mesh->m_offset_x, p_mesh->m_offset_y, p_mesh->m_offset_z );
//					multi_start_16 ( p_mesh->mp_posBuffer, p_mesh->mp_normBuffer, 3, 3 );
//#else
//					multi_start ( p_mesh->mp_posBuffer, p_mesh->mp_normBuffer, 3, 3 );
//#endif		// SHORT_VERT
//				}
//
//				multi_end( p_mesh, vRENDER_SHADOW_1ST_PASS );
//			}
//		}			
//	}			
//
//	// Render shadows from list.
//#if 1
//	for ( int lp = 0; lp < shadow_meshes; lp++ )
//	{
//		GXTlutObj	palObj;
//		GXInitTlutObj( &palObj, &shadowPalette, GX_TL_RGB5A3, 16 );
//		GXLoadTlut ( &palObj, GX_TLUT0 );
//
//		// Set up shadow map texture
//#define SHADOW_TEXTURE_SIZE 256
//#define BLUR_TEXTURE_SIZE 64
//		GXTexObj blurTexture;
//		GXTexObj shadowTexture;
//		GXInitTexObjCI(
//			&blurTexture,
//			blurTextureData,		//shadowTextureData,
//			BLUR_TEXTURE_SIZE,		//SHADOW_TEXTURE_SIZE,
//			BLUR_TEXTURE_SIZE,        //SHADOW_TEXTURE_SIZE,
//			GX_TF_C4,
//			GX_CLAMP,
//			GX_CLAMP,
//			GX_FALSE,
//			GX_TLUT0 );
////	GXInitTexObjLOD(&blurTexture, GX_NEAR, GX_NEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
//		GXLoadTexObj( &blurTexture, GX_TEXMAP5 );
//		GXInitTexObjCI(
//			&shadowTexture,
//			shadowTextureData,
//			SHADOW_TEXTURE_SIZE,
//			SHADOW_TEXTURE_SIZE,
//			GX_TF_C4,
//			GX_CLAMP,
//			GX_CLAMP,
//			GX_FALSE,
//			GX_TLUT0 );
////	GXInitTexObjLOD(&shadowTexture, GX_NEAR, GX_NEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
//		GXLoadTexObj( &shadowTexture, GX_TEXMAP6 );
//	
//		// Offset the texture matrix.
//#ifdef SHORT_VERT
//		Mtx m;
//		MTXTrans( m, pShadow[lp]->m_offset_x, pShadow[lp]->m_offset_y, pShadow[lp]->m_offset_z );
//		MTXConcat( light, m, m );
//		GXLoadTexMtxImm( m, GX_TEXMTX7, GX_MTX3x4 );
//#endif		// SHORT_VERT
//
//		if ( p_posNormBuffer ) {
//			// Skinned.
//			multi_start_16 ( &p_posNormBuffer[0], &p_posNormBuffer[3], 6, 6 );
//		} else {
//			// Not skinned.
//#ifdef SHORT_VERT
//			cam_offset( pShadow[lp]->m_offset_x, pShadow[lp]->m_offset_y, pShadow[lp]->m_offset_z );
//			multi_start_16 ( pShadow[lp]->mp_posBuffer, pShadow[lp]->mp_normBuffer, 3, 3 );
//#else
//			multi_start ( pShadow[lp]->mp_posBuffer, pShadow[lp]->mp_normBuffer, 3, 3 );
//#endif		// SHORT_VERT
//		}
//
//		multi_end( pShadow[lp], vRENDER_SHADOW_2ND_PASS );
//	}
//#endif
//	}
//
////	if( flags & vRENDER_SEMITRANSPARENT )
////	{
////		// Sort semi-transparent meshes.
////		int bucket_size = p_scene->m_num_semitransparent_entries * 2;
////		sMesh * sorted_meshes[bucket_size];
////		memset( sorted_meshes, 0, bucket_size * 4 );
////		for( int e = 0; e < p_scene->m_num_semitransparent_entries; ++e )
////		{
////			if( p_scene->m_semitransparent_meshes[e]->m_flags & sMesh::MESH_FLAG_ACTIVE )
////			{
////				// Frustum cull this set of meshes, using the associated bounding box.
////				float z;
////				if( frustum_check_sphere( p_scene->m_semitransparent_meshes[e]->m_sphere, &z ))
////				{
////					int index = (int)( z / ( 20000.0f / bucket_size ) );
////					if ( index < 0 ) index = 0;
////					if ( index > (bucket_size - 1) ) index = (bucket_size - 1);
////	
////					if ( index >= (bucket_size / 2) )
////					{
////						// Search backwards to find an empty slot.
////						while ( sorted_meshes[index] ) index--;
////						if ( index >= 0 )
////						{
////							sorted_meshes[index] = p_scene->m_semitransparent_meshes[e];
////						}
////					}
////					else
////					{
////						// Search forwards to find an empty slot.
////						while ( sorted_meshes[index] ) index++;
////						if ( index < bucket_size )
////						{
////							sorted_meshes[index] = p_scene->m_semitransparent_meshes[e];
////						}
////					}
////				}
////			}
////		}
////	
////		// Render semi-transparent meshes.
////		for( int e = 0; e < bucket_size; ++e )
////		{
////			if( sorted_meshes[e] )
////			{
////				++meshes_rendered;
////	
////				sMesh * p_mesh = sorted_meshes[e];
////				
////				set_render_state( RS_ZWRITEENABLE, p_mesh->m_zwrite );
////
//////				if ( p_mesh->mp_uvBuffer && p_mesh->mp_material->pTex[0] )
////				{
////					uint32 layer;
////
////					if ( p_posNormBuffer ) {
////						// Skinned.
////						multi_start ( &p_posNormBuffer[0], &p_posNormBuffer[3], 6, 6 );
////					} else {
////						// Not skinned.
////						multi_start ( p_mesh->mp_posBuffer, p_mesh->mp_normBuffer, 3, 3 );
////					}
////					multi_add_color ( p_mesh->mp_colBuffer );
////					int uv_set = 0;
////					float * p_tex;
////					for ( layer = 0; layer < p_mesh->mp_material->Passes ; layer++ )
////					{
////						GXTexWrapMode u_mode;
////						GXTexWrapMode v_mode;
////						if ( p_mesh->mp_material->Flags[layer] & MATFLAG_ENVIRONMENT )
////						{
////							p_tex = NULL;
////							u_mode = GX_REPEAT;
////							v_mode = GX_REPEAT;
////						}
////						else
////						{
////							p_tex = &p_mesh->mp_uvBuffer[2*uv_set];
////							u_mode = p_mesh->mp_material->UVAddressing[layer] & (1<<0)  ? GX_CLAMP : GX_REPEAT;
////							v_mode = p_mesh->mp_material->UVAddressing[layer] & (1<<1) ? GX_CLAMP : GX_REPEAT;
////						}
////						uv_set++;
////
////						multi_add_texture ( p_tex, p_mesh->m_num_uv_sets, p_mesh->mp_material->uv_slot[layer], p_mesh->mp_material->Flags[layer], layer );
////					}
////
////					multi_end( p_mesh, 0 );
////				}
////			}
////		}			
////	}			
//	
//	if ( p_scene->m_numHierarchyObjects != 0 )
//	{
//		// Hierarchical, set to matrix 0.
//		GXSetCurrentMtx( GX_PNMTX0 );
//	}
	GX::SetProjectionv( pm );
	g_object++;
}

/******************************************************************/
/*                                                                */
/* Sets MESH_FLAG_VISIBLE if visible for all meshes in this scene */
/*                                                                */
/******************************************************************/
int cull_scene( sScene *p_scene, uint32 flags )
{
	int meshes_visible = 0;

	// Cull all meshes.
	sMesh ** pp_mesh = p_scene->mpp_mesh_list;

	for( int e = 0; e < p_scene->m_num_filled_meshes; ++e )
	{
		sMesh * p_mesh = pp_mesh[e];
		if( ( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && ( p_mesh->GetVisibility() & EngineGlobals.viewport ) )
		{
			Mth::Vector sphere = p_mesh->mp_dl->m_sphere;

			// Frustum cull this set of meshes, using the associated bounding box.
			if( frustum_check_sphere( sphere ))
			{
				// Sphere is visible.
				if( ( flags & vRENDER_OCCLUDED ) )
				{
					if ( TestSphereAgainstOccluders( &sphere ) )
					{
						// Occluded.
						p_mesh->m_flags &= ~sMesh::MESH_FLAG_VISIBLE;
					}
					else
					{
						// Not occluded.
						p_mesh->m_flags |= sMesh::MESH_FLAG_VISIBLE;
						++meshes_visible;
					}

				}
				else
				{
					// Sphere being visible is enough.
					p_mesh->m_flags |= sMesh::MESH_FLAG_VISIBLE;
					++meshes_visible;
				}
			}
			else
			{
				// Sphere not visible.
				p_mesh->m_flags &= ~sMesh::MESH_FLAG_VISIBLE;
			}
		}
		else
		{
			// Force to be not visible.
			p_mesh->m_flags &= ~sMesh::MESH_FLAG_VISIBLE;
		}
	}
	return meshes_visible;

//	make_scene_visible( p_scene );
//	return p_scene->m_num_filled_meshes;
}

/******************************************************************/
/*                                                                */
/* Sets MESH_FLAG_VISIBLE for all meshes in this scene.			  */
/*                                                                */
/******************************************************************/
void make_scene_visible( sScene *p_scene )
{
	// Set all meshes to be visible.
	sMesh ** pp_mesh = p_scene->mpp_mesh_list;

	for( int e = 0; e < p_scene->m_num_filled_meshes; ++e )
	{
		if( ( pp_mesh[e]->m_flags & sMesh::MESH_FLAG_ACTIVE ) && ( pp_mesh[e]->GetVisibility() & EngineGlobals.viewport ) )
		{
			pp_mesh[e]->m_flags |= sMesh::MESH_FLAG_VISIBLE;
		}
	}
}

void render_begin( void )
{

}

void render_end( void )
{

}

} // namespace NxNgc





================================================
FILE: Code/Gfx/NGC/NX/render.h
================================================
#ifndef __RENDER_H
#define __RENDER_H

#include 
#include 
#include 
#include "mesh.h"
#include "scene.h"
#include 
#include 

#define		RS_ZWRITEENABLE			1
#define		RS_ZTESTENABLE			2
#define		RS_ALPHACUTOFF			3
#define		RS_UVADDRESSMODE0		4
#define		RS_UVADDRESSMODE1		5
#define		RS_UVADDRESSMODE2		6
#define		RS_UVADDRESSMODE3		7

namespace NxNgc
{
	struct sTextureProjectionDetails
	{
		sTexture		*p_texture;
		Nx::CNgcModel	*p_model;
		sScene			*p_scene;
		NsMatrix		view_matrix;
		NsMatrix		projection_matrix;
		NsMatrix		texture_projection_matrix;
	};

	extern Lst::HashTable< sTextureProjectionDetails > *pTextureProjectionDetailsTable;
	
	typedef enum
	{
		vBLEND_MODE_DIFFUSE,						// ( 0 - 0 ) * 0 + Src
		vBLEND_MODE_ADD,							// ( Src - 0 ) * Src + Dst
		vBLEND_MODE_ADD_FIXED,						// ( Src - 0 ) * Fixed + Dst
		vBLEND_MODE_SUBTRACT,						// ( 0 - Src ) * Src + Dst
		vBLEND_MODE_SUB_FIXED,						// ( 0 - Src ) * Fixed + Dst
		vBLEND_MODE_BLEND,							// ( Src * Dst ) * Src + Dst	
		vBLEND_MODE_BLEND_FIXED,					// ( Src * Dst ) * Fixed + Dst	
		vBLEND_MODE_MODULATE,						// ( Dst - 0 ) * Src + 0
		vBLEND_MODE_MODULATE_FIXED,					// ( Dst - 0 ) * Fixed + 0	
		vBLEND_MODE_BRIGHTEN,						// ( Dst - 0 ) * Src + Dst
		vBLEND_MODE_BRIGHTEN_FIXED,					// ( Dst - 0 ) * Fixed + Dst	
		vBLEND_MODE_GLOSS_MAP,						// Specular = Specular * Src	- special mode for gloss mapping
		vBLEND_MODE_BLEND_PREVIOUS_MASK,			// ( Src - Dst ) * Dst + Dst
		vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK,	// ( Dst - Src ) * Dst + Src

		vNUM_BLEND_MODES
	} BlendModes; 

	typedef enum
	{
		vRENDER_OPAQUE			= 1,						
		vRENDER_SEMITRANSPARENT	= 2,
		vRENDER_OCCLUDED		= 4,
		vRENDER_NO_CULLING		= 8,		// Used for instances which have already been culled at a higher level
		vRENDER_SHADOW_1ST_PASS = 16,
		vRENDER_SHADOW_2ND_PASS = 32,
		vRENDER_NEW_TEST		= 64,
		vRENDER_INSTANCE_PRE_WORLD_SEMITRANSPARENT	= 128,		// Used to indicate that this instance rendering is happening prior to semitransparent world rendering
		vRENDER_INSTANCE_POST_WORLD_SEMITRANSPARENT	= 256,		// Used to indicate that this instance rendering is happening after semitransparent world rendering
		vRENDER_TRANSFORM		= 512,
		vRENDER_LIT				= 1024,
		vNUM_SCENE_RENDER_FLAGS
	} SceneRenderFlags; 

	void	init_render_system();
	void	set_render_state( uint32 type, uint32 state );
	void	set_blend_mode( uint64 mode );

	void	create_texture_projection_details( sTexture *p_texture, Nx::CNgcModel *p_model, sScene *p_scene );
	void	destroy_texture_projection_details( sTexture *p_texture );
	void	set_texture_projection_camera( sTexture *p_texture, NsVector * p_pos, NsVector * p_at );
	void	calculate_tex_proj_matrix( NsMatrix * p_tex_view_matrix, NsMatrix * p_tex_proj_matrix, NsMatrix * p_tex_transform_matrix, NsMatrix * p_world_matrix = NULL );
	
	void	set_camera( Mth::Matrix *p_matrix, Mth::Vector *p_position, float screen_angle, float aspect_ratio, float scale = 1.0f );
	void	set_frustum_bbox_transform( Mth::Matrix *p_transform );
	bool	frustum_check_box( Mth::CBBox& box );
	bool	frustum_check_sphere( Mth::Vector& p_sphere );
	bool	frustum_check_sphere( Mth::Vector& p_sphere, float * p_z );
	void	render_scene( sScene *p_scene, s16* p_posNormBuffer, uint32 flags = ( vRENDER_OPAQUE | vRENDER_SEMITRANSPARENT ), Mth::Matrix * p_bone_xform = NULL, Mth::Matrix * p_instance_xform = NULL, int num_bones = 0 );
	bool	IsVisible( Mth::Vector &sphere );
	void	render_shadow_targets();

	int		cull_scene( sScene *p_scene, uint32 flags = 0 );
	void	make_scene_visible( sScene *p_scene );

	void	render_begin( void );
	void	render_end( void );
} // namespace NxNgc

#endif // __RENDER_H



================================================
FILE: Code/Gfx/NGC/NX/scene.cpp
================================================
#include 

#include 
#include 
#include 
#include "texture.h"
#include "mesh.h"
#include "scene.h"
#include "render.h"
#include 
#include "nx_init.h"
#include 
#include 
#include 

#define ENV_MAP_SCROLL_SCALE ( 1.0f / 32768.0f )

int g_dl = 1;

bool gOverDraw = false;

namespace NxNgc
{

static s32	last_audio_update = 0;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void do_audio_update( void )
{
	s32 t = OSGetTick();
	if(( t < last_audio_update ) || ( OSDiffTick( t, last_audio_update ) > (s32)( OS_TIMER_CLOCK / 60 )))
	{
		last_audio_update = t;
		Pcm::PCMAudio_Update();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int sort_by_material_draw_order( const void *p1, const void *p2 )
{
	sMesh			*p_mesh1		= *((sMesh**)p1 );
	sMesh			*p_mesh2		= *((sMesh**)p2 );
	sMaterialHeader	*p_material1	= p_mesh1->mp_dl->m_material.p_header;
	sMaterialHeader	*p_material2	= p_mesh2->mp_dl->m_material.p_header;
	
	Dbg_Assert( p_material1 != NULL );
	Dbg_Assert( p_material2 != NULL );

	if( p_material1->m_draw_order == p_material2->m_draw_order )
	{
		// Have to do some special case processing for the situation where two or more meshes have the same draw order, but only some are
		// marked as being dynamically sorted. In such a situation the non dynamically sorted ones should come first.
		if( ( p_material1->m_flags & (1<<0) ) == ( p_material2->m_flags & (1<<0) ) )
		{
			if( p_material1 == p_material2 )
			{
				// Same material, no further sorting required.
				return 0;
			}
			else
			{
				// If the blend mode is the same, sort by material address.
				if( p_material1->m_base_blend == p_material2->m_base_blend )
				{
					// If the pixel shaders are the same, sort by material address, otherwise sort by pixel shader value.
					if( p_material1->m_texture_dl_id == p_material2->m_texture_dl_id )
					{
						return ((uint32)p_material1 > (uint32)p_material2 ) ? 1 : -1;
					}
					else
					{
						return ((uint32)p_material1->m_texture_dl_id < (uint32)p_material2->m_texture_dl_id ) ? 1 : -1;
					}
				}
				else
				{
					return ((uint32)p_material1->m_base_blend > (uint32)p_material2->m_base_blend ) ? 1 : -1;
				}
			}
		}
		else if( p_material1->m_flags & (1<<0) )
		{
			return 1;
		}
		return -1;
	}
	return ( p_material1->m_draw_order > p_material2->m_draw_order ) ? 1 : -1;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#define INDEX_WORKBUFFER_SIZE	1700
#define MAX_STRIPS				1024
#define MAX_INDEX_WIDTH			7

// Maximum index width:
// 1 position
// 1 normal
// 2 colors
// 4 tex coord
	
static void hide_mesh( uint32 mask, sCASData *p_cas_data, uint32 num_entries, sMesh * p_mesh )
{
	uint16	index_workbuffer[INDEX_WORKBUFFER_SIZE][MAX_INDEX_WIDTH];
	uint32	new_indices_index = 0;

	int		index_strip_len[MAX_STRIPS];
	int		num_strips = 0;

	// Clear strips.
	for ( int lp = 0; lp < MAX_STRIPS; lp++ )
	{
		index_strip_len[lp] = 0;
	}

	unsigned char * p_start = (unsigned char *)&p_mesh->mp_dl[1];
	unsigned char * p_end = &p_start[p_mesh->mp_dl->m_size];
	p_start = &p_start[p_mesh->mp_dl->m_index_offset];		// Skip to actual 1st GDBegin.
	unsigned char * p8 = p_start;

	uint8 begin_token = p8[0];		// Save token for use when rebuilding mesh.

	int stride = p_mesh->mp_dl->m_index_stride;

	int w;

	while ( p8 < p_end )
	{
		if ( ( p8[0] & 0xf8 ) == GX_TRIANGLESTRIP )
		{
			// Found a triangle strip - parse it.
			int num_verts = ( p8[1] << 8 ) | p8[2];
			p8 += 3;		// Skip GDBegin

			uint16 idx0[MAX_INDEX_WIDTH];
			uint16 idx1[MAX_INDEX_WIDTH];
			uint16 idx2[MAX_INDEX_WIDTH];

			for ( w = 0; w < stride; w++ ) { idx1[w] = ( p8[0] << 8 ) | p8[1]; p8 += 2; }
			for ( w = 0; w < stride; w++ ) { idx2[w] = ( p8[0] << 8 ) | p8[1]; p8 += 2; }

			for ( int v = 2; v < num_verts; v++ )
			{
				// Read next index to form triangle.
				for ( w = 0; w < stride; w++ ) idx0[w] = idx1[w];
				for ( w = 0; w < stride; w++ ) idx1[w] = idx2[w];
				for ( w = 0; w < stride; w++ ) { idx2[w] = ( p8[0] << 8 ) | p8[1]; p8 += 2; } 

				// There shuold be no degenerate triangles.
				// ...check against every CAS entry.
				bool keep = true;
				for( uint32 entry = 0; entry < num_entries; ++entry )
				{
					// Check this CAS entry has the correct mask...
					if( p_cas_data[entry].mask & mask )
					{
						// ...and the right mesh...
						uint32 mesh = p_cas_data[entry].data0 >> 16;
	
						// The CAS data is ordered first in increasing mesh size - so we can early out here if applicable.
						if ( mesh > p_mesh->mp_dl->m_mesh_index )
						{
							break;
						}
	
						if ( mesh == p_mesh->mp_dl->m_mesh_index )
						{
							// ...and the correct index.
							uint32 i0 = ( p_cas_data[entry].data0 & 0xFFFF );
	
							// The CAS data is next ordered in increasing size of i0 - so we can early out here if applicable.
							if ( i0 > idx0[0] )
							{
								break;
							}
	
							uint32 i1 = ( p_cas_data[entry].data1 >> 16 );
							uint32 i2 = ( p_cas_data[entry].data1 & 0xFFFF );
							if ( ( i0 == idx0[0] ) && ( i1 == idx1[0] ) && ( i2 == idx2[0] ) )
							{
								keep = false;

								break;
							}
						}
					}
				}

				// Copy index.
				if ( keep )
				{
					if ( index_strip_len[num_strips] )
					{
						// Already put 1st triangle in, just add index.
						for ( w = 0; w < stride; w++ ) index_workbuffer[new_indices_index][w] = idx2[w];
						new_indices_index++;
						index_strip_len[num_strips]++;
					}
					else
					{
						// 1st triangle, so add all 3 verts.
						for ( w = 0; w < stride; w++ ) index_workbuffer[new_indices_index][w] = idx0[w];
						new_indices_index ++;
						for ( w = 0; w < stride; w++ ) index_workbuffer[new_indices_index][w] = idx1[w];
						new_indices_index ++;
						for ( w = 0; w < stride; w++ ) index_workbuffer[new_indices_index][w] = idx2[w];
						new_indices_index ++;
						index_strip_len[num_strips] += 3;
					}
				}
				else
				{
					// If we don't keep this tri & we already started a strip, we need
					// to start a new strip.
					if ( index_strip_len[num_strips] )
					{
						// Need to start a new strip.
						num_strips++;
					}
				}
			}
			// Start a new strip if we put data in this one.
			if ( index_strip_len[num_strips] )
			{
				// Need to start a new strip.
				num_strips++;
			}
		}
		else
		{
			break;
		}
	}
	do_audio_update();

	Dbg_MsgAssert( new_indices_index <= INDEX_WORKBUFFER_SIZE, ( "Too many indices in new mesh: %d\n", new_indices_index ) );

	// See if we'll fit in the existing buffer.
	uint32 new_size = ( 3 * num_strips ) + ( sizeof( uint16 ) * stride * new_indices_index ) + p_mesh->mp_dl->m_index_offset;

//	OSReport( "New: %d Old: %d Diff: %d\n", new_size, p_mesh->mp_dl->m_size, p_mesh->mp_dl->m_size - new_size );

//	if ( new_size > p_mesh->mp_dl->m_original_dl_size )
//	{
//		// Need to allocate a new buffer for this.
//	}

	if ( new_size <= p_mesh->mp_dl->m_original_dl_size )
	{
		p8 = p_start;
		int buffer_index = 0;

		for ( int strip = 0; strip < num_strips; strip++ )
		{
			*p8++ = begin_token;
			*p8++ = (uint8)(index_strip_len[strip] >> 8 );
			*p8++ = (uint8)(index_strip_len[strip] & 0xff );
			for ( int index = 0; index < index_strip_len[strip]; index++ )
			{
				for ( w = 0; w < stride; w++ )
				{
					*p8++ = (uint8)(index_workbuffer[buffer_index][w] >> 8 );
					*p8++ = (uint8)(index_workbuffer[buffer_index][w] & 0xff );
				}
				buffer_index++;
			}
		}
		// Set new size of display list.
		int new_rounded_size = ( new_size + 31 ) & ~31;
		p_mesh->mp_dl->m_size = new_rounded_size;

		// Pad the DL with 0s to avoid spurious crap at the end & flush cache.
		int pad_size = new_rounded_size - new_size;
		for ( int pad = 0; pad < pad_size; pad++ ) *p8++ = 0;

		DCFlushRange( &p_mesh->mp_dl[1], new_rounded_size );
	}
	else
	{
#ifdef __NOPT_FINAL__
		OSReport( "Warning: DL too big after hide_mesh - New: %d Old: %d Diff: %d\n", new_size, p_mesh->mp_dl->m_size, p_mesh->mp_dl->m_size - new_size );
#else
		Dbg_MsgAssert( false, ( "Error: DL too big after hide_mesh - New: %d Old: %d Diff: %d\n", new_size, p_mesh->mp_dl->m_size, p_mesh->mp_dl->m_size - new_size ) );
#endif		// __NOPT_FINAL__
	}
	do_audio_update();
}

sScene::sScene( void )
{
	m_flags					= 0;

	m_num_meshes			= 0;		// No meshes as yet.
	m_num_filled_meshes		= 0;
	mpp_mesh_list			= NULL;

	mp_hierarchyObjects		= NULL;
	m_numHierarchyObjects	= 0;

	mp_scene_data			= NULL;
	mp_dl					= NULL;

	mp_blend_dl				= NULL;
	mp_material_header		= NULL;

	mp_hierarchyObjects		= NULL;
	m_numHierarchyObjects	= 0;

	m_is_dictionary			= false;
}

sScene::~sScene( void )
{
	// Remove the material table.
//	if( mp_material_array )
//	{
//		delete mp_material_array;
//	}
//
//	if( m_opaque_meshes != NULL )
//	{
//		delete [] m_opaque_meshes;
//	}
//	if( m_semitransparent_meshes != NULL )
//	{
//		delete [] m_semitransparent_meshes;
//	}
//
//	if ( mp_hierarchyObjects )
//	{
//		delete [] mp_hierarchyObjects;
//	}
//

//	// Go through, and see if any DLs got allocated.
//	int dl = 0;
//	for( uint s = 0; s < mp_scene_data->m_num_objects; ++s )
//	{
//		int num_mesh = mp_dl[dl].mp_object_header->m_num_meshes;
//		for ( int m = 0; m < num_mesh; m++ )
//		{
//			if ( mp_dl[dl].mp_pos_pool )
//			{
//			}
//		}
//	}


	if ( mp_scene_data && !( m_flags & SCENE_FLAG_CLONED_GEOM ) )
	{
		delete [] mp_scene_data;
	}

	if ( mpp_mesh_list )
	{
		delete [] mpp_mesh_list;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::AddMeshes( int num_meshes, sMesh **pp_meshes )
{
	// Add each mesh.
	for( int m = 0; m < num_meshes; ++m )
	{
		if ( m_num_filled_meshes < m_num_meshes )
		{
			mpp_mesh_list[m_num_filled_meshes] = pp_meshes[m];
			++m_num_filled_meshes;
		}
		else
		{
			Dbg_MsgAssert( false, ( "Too many meshes being added." ) );
		}
	}

//	for( int m = 0; m < num_meshes; ++m )
//	{
//		sMaterialHeader *		p_material = pp_meshes[m]->mp_dl->m_material.p_header;
//		sMaterialPassHeader *	p_pass = &mp_material_pass[p_material->m_pass_item];
//
//		bool transparent = (( p_material ) && ( p_pass->m_flags & (1<<3) ));
//
//		if ( transparent )
//		{
//			if ( m_num_filled_meshes < m_num_meshes )
//			{
//				mpp_mesh_list[m_num_filled_meshes] = pp_meshes[m];
//				++m_num_filled_meshes;
//			}
//			else
//			{
//				Dbg_MsgAssert( false, ( "Too many meshes being added." ) );
//			}
//		}
//	}
//
//	for( int m = 0; m < num_meshes; ++m )
//	{
//		sMaterialHeader *		p_material = pp_meshes[m]->mp_dl->m_material.p_header;
//		sMaterialPassHeader *	p_pass = &mp_material_pass[p_material->m_pass_item];
//
//		bool transparent = (( p_material ) && ( p_pass->m_flags & (1<<3) ));
//
//		if ( !transparent )
//		{
//			if ( m_num_filled_meshes < m_num_meshes )
//			{
//				mpp_mesh_list[m_num_filled_meshes] = pp_meshes[m];
//				++m_num_filled_meshes;
//			}
//			else
//			{
//				Dbg_MsgAssert( false, ( "Too many meshes being added." ) );
//			}
//		}
//	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::CountMeshes( int num_meshes, sMesh **pp_meshes )
{
	m_num_meshes += (uint16)num_meshes;


//	// Count each mesh.
//	for( int m = 0; m < num_meshes; ++m )
//	{
//		++m_num_meshes;
////		bool transparent = (( pp_meshes[m]->mp_material ) && ( pp_meshes[m]->mp_material->Flags[0] & 0x40 ));
////		if( transparent )
////		{
////			++m_num_semitransparent_entries;
////		}
////		else
////		{
////			++m_num_opaque_entries;
////		}
//	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::CreateMeshArrays( void )
{
	if ( m_num_meshes > 0 )
	{
		mpp_mesh_list = new sMesh*[m_num_meshes];
	}

//	if( m_num_semitransparent_entries > 0 )
//	{
//		m_semitransparent_meshes = new sMesh*[m_num_semitransparent_entries];
//	}
//	
//	if( m_num_opaque_entries > 0 )
//	{
//		m_opaque_meshes = new sMesh*[m_num_opaque_entries];
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::FigureBoundingVolumes( void )
{
//	// Figure bounding sphere assuming bounding box has already been set up (during individual mesh initialisation).
//	Mth::Vector radial	= ( m_bbox.GetMax() - m_bbox.GetMin() ) * 0.5f;
//	Mth::Vector center	= m_bbox.GetMin() + radial;
//	m_sphere_center.set( center[X], center[Y], center[Z] );
//	m_sphere_radius		= sqrtf(( radial[X] * radial[X] ) +	( radial[Y] * radial[Y] ) + ( radial[Z] * radial[Z] ));
}








void sScene::RemoveMeshes( int num_meshes, sMesh **pp_meshes )
{
	int num_opaque_entries_removed					= 0;
	int num_pre_semitransparent_entries_removed		= 0;
	int num_dynamic_semitransparent_entries_removed	= 0;
	int num_post_semitransparent_entries_removed	= 0;
	
	int opaque		= m_num_opaque_meshes;
	int presemi		= opaque + m_num_pre_semitrans_meshes;
	int dynsemi		= presemi + m_num_dynamic_semitrans_meshes;
	int postsemi	= dynsemi + m_num_post_semitrans_meshes;

	for( int m = 0; m < num_meshes; ++m )
	{
		sMesh *p_mesh = pp_meshes[m];

		bool found = false;
		// Search Opaque
		for ( int i = 0; i < opaque; i++ )
		{
			if( mpp_mesh_list[i] == p_mesh )
			{
				found = true;
				mpp_mesh_list[i] = NULL;
				++num_opaque_entries_removed;
				--m_num_opaque_meshes;
				break;
			}
		}
		if( found ) continue;

		// Search Pre Semitransparent
		for ( int i = opaque; i < presemi; i++ )
		{
			if( mpp_mesh_list[i] == p_mesh )
			{
				found = true;
				mpp_mesh_list[i] = NULL;
				++num_pre_semitransparent_entries_removed;
				--m_num_pre_semitrans_meshes;
				break;
			}
		}

		if( found ) continue;

		// Search Dynamic Semitransparent
		for ( int i = presemi; i < dynsemi; i++ )
		{
			if( mpp_mesh_list[i] == p_mesh )
			{
				found = true;
				mpp_mesh_list[i] = NULL;
				++num_dynamic_semitransparent_entries_removed;
				--m_num_dynamic_semitrans_meshes;
				break;
			}
		}

		if( found ) continue;

		// Search Post Semitransparent
		for ( int i = dynsemi; i < postsemi; i++ )
		{
			if( mpp_mesh_list[i] == p_mesh )
			{
				found = true;
				mpp_mesh_list[i] = NULL;
				++num_post_semitransparent_entries_removed;
				--m_num_post_semitrans_meshes;
				break;
			}
		}

		Dbg_Assert( found );	
	}
	
	// Now go through and compact the arrays.

//	int total = m_num_opaque_meshes + m_num_pre_semitrans_meshes + m_num_dynamic_semitrans_meshes + m_num_post_semitrans_meshes;
	int total_removed = num_opaque_entries_removed +
						num_pre_semitransparent_entries_removed +
						num_dynamic_semitransparent_entries_removed	+
						num_post_semitransparent_entries_removed;

	for ( int lp = 0; lp < total_removed; lp++ )
	{
		for ( int i = 0; i < m_num_filled_meshes; i++ )
		{
			if( !mpp_mesh_list[i] )
			{
//				m_num_filled_meshes--;
//				mpp_mesh_list[i] = mpp_mesh_list[m_num_filled_meshes];


				// Only worth copying if there is anything beyond this mesh.
				if( i < ( m_num_filled_meshes - 1 ))
				{
					memcpy( &mpp_mesh_list[i], &mpp_mesh_list[i + 1], sizeof( sMesh* ) * ( m_num_filled_meshes - ( i + 1 )));
				}
				m_num_filled_meshes--;
				break;
			}
		}
	}
//	SortMeshes();
//	m_num_filled_meshes -= total_removed;
}



void sScene::SortMeshes( void )
{
	// Sort the list of meshes.
	qsort( mpp_mesh_list, m_num_filled_meshes, sizeof( sMesh* ), sort_by_material_draw_order );

	if ( mp_scene_data )
	{
		m_num_opaque_meshes				= 0;
		m_num_pre_semitrans_meshes		= 0;
		m_num_dynamic_semitrans_meshes	= 0;
		m_num_post_semitrans_meshes		= 0;

		// Force opaque to be before semitrans.
		sMesh *p_mesh[8192];
		int mesh_entry = 0;
		Dbg_MsgAssert( m_num_filled_meshes < 8192, ( "Too many meshes for temporary sort buffer." ) );

		// Find 1st dynamic entry.
		int first_dynamic = -1;
		for( int i = 0; i < m_num_filled_meshes; ++i )
		{
			if ( mpp_mesh_list[i] )
			{
				bool sorted = mpp_mesh_list[i]->mp_dl->m_material.p_header->m_flags & (1<<0) ? true : false;

				if ( ( first_dynamic == -1 ) && sorted ) first_dynamic = i;
			}
		}


		for ( int pass = 0; pass < 4; pass++ )
		{
			for( int i = 0; i < m_num_filled_meshes; ++i )
			{
				if ( mpp_mesh_list[i] )
				{
					bool transparent = ( mp_material_pass[mpp_mesh_list[i]->mp_dl->m_material.p_header->m_pass_item].m_flags & (1<<3) );
//					bool transparent = ( mpp_mesh_list[i]->mp_dl->m_material.p_header->m_flags & (1<<3) ) ? true : false;

					bool sorted = mpp_mesh_list[i]->mp_dl->m_material.p_header->m_flags & (1<<0) ? true : false;

					if ( ( first_dynamic == -1 ) && sorted ) first_dynamic = i;

					switch ( pass )
					{
						case 0:		// Opaque
							if ( !transparent )
							{
								p_mesh[mesh_entry] = mpp_mesh_list[i];
								mpp_mesh_list[i] = NULL;
								mesh_entry++;
								m_num_opaque_meshes++;
							}
							break;
						case 1:		// Pre semi
							if ( transparent && !sorted && ( i < first_dynamic ) )
							{
//								if ( mpp_mesh_list[i]->mp_dl ) printf( "Pre Semi: %d, %8.3f\n", i, mpp_mesh_list[i]->mp_dl->m_material.p_header->m_draw_order );
								p_mesh[mesh_entry] = mpp_mesh_list[i];
								mpp_mesh_list[i] = NULL;
								mesh_entry++;
								m_num_pre_semitrans_meshes++;
							}
							break;
						case 2:		// Dynamic semi
							if ( transparent && sorted )
							{
//								if ( mpp_mesh_list[i]->mp_dl ) printf( "Dyn Semi: %d, %8.3f\n", i, mpp_mesh_list[i]->mp_dl->m_material.p_header->m_draw_order );
								p_mesh[mesh_entry] = mpp_mesh_list[i];
								mpp_mesh_list[i] = NULL;
								mesh_entry++;
								m_num_dynamic_semitrans_meshes++;
							}
							break;
						case 3:		// Post semi (everything else)...
//							if ( transparent && !sorted && ( ( i >= first_dynamic ) && ( first_dynamic != -1 ) ) )
							{
//								if ( mpp_mesh_list[i]->mp_dl ) printf( "Pst Semi: %d, %8.3f\n", i, mpp_mesh_list[i]->mp_dl->m_material.p_header->m_draw_order );
								p_mesh[mesh_entry] = mpp_mesh_list[i];
								mpp_mesh_list[i] = NULL;
								mesh_entry++;
								m_num_post_semitrans_meshes++;
							}
							break;
						default:
							Dbg_MsgAssert( false, ( "This should never happen." ) );
							break;
					}
				}
			}
		}
		Dbg_MsgAssert( m_num_filled_meshes == mesh_entry, ( "Sorted meshes differs from actual meshes." ) );

//		float order = p_mesh[0]->mp_dl->m_material.p_header->m_draw_order;
		for( int i = 0; i < m_num_filled_meshes; ++i )
		{
//			Dbg_MsgAssert( p_mesh[i]->mp_dl->m_material.p_header->m_draw_order >= order, ( "Out of order on entry %d: last=%f, current = %f", i, order, p_mesh[i]->mp_dl->m_material.p_header->m_draw_order ) );
//			order = p_mesh[i]->mp_dl->m_material.p_header->m_draw_order;
			mpp_mesh_list[i] = p_mesh[i];
		}
	}
	else
	{
		m_num_opaque_meshes				= m_num_filled_meshes;
		m_num_pre_semitrans_meshes		= 0;
		m_num_dynamic_semitrans_meshes	= 0;
		m_num_post_semitrans_meshes		= 0;
	}
}






sMaterial *sScene::GetMaterial( uint32 checksum )
{
//	for ( int lp = 0; lp < m_num_materials; lp++ )
//	{
//		if( mp_material_array[lp].Checksum == checksum )
//		{
//			return &mp_material_array[lp];
//		}
//	}
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void sScene::HidePolys( uint32 mask, sCASData *p_cas_data, uint32 num_entries )
{
	if(( num_entries == 0 ) || ( mask == 0 ))
	{
		return;
	}

	// For each mesh, need to find all cas data which references verts in that mesh.
	for( int m = 0; m < this->m_num_filled_meshes; ++m )
	{
		sMesh *p_mesh = mpp_mesh_list[m];

		hide_mesh( mask, p_cas_data, num_entries, p_mesh );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sScene *LoadScene( const char *Filename, sScene *pScene )
{
	return NULL;
}



void DeleteScene( sScene *pScene )
{
//	// Iterate through the table of materials, deleting them.
//	for ( int lp = 0; lp < pScene->m_num_materials; lp++ )
//	{
//		if( pScene->mp_material_array[lp].mp_wibble_vc_params	)
//		{
//			for( uint32 i = 0; i < pScene->mp_material_array[lp].m_num_wibble_vc_anims; ++i )
//			{
//				delete [] pScene->mp_material_array[lp].mp_wibble_vc_params[i].mp_keyframes;
//			}
//			delete [] pScene->mp_material_array[lp].mp_wibble_vc_params;
//		}
//		if( pScene->mp_material_array[lp].mp_wibble_vc_colors	)
//		{
//			delete [] pScene->mp_material_array[lp].mp_wibble_vc_colors;
//		}
//		if( pScene->mp_material_array[lp].m_pUVControl	)
//		{
//			delete [] pScene->mp_material_array[lp].m_pUVControl;
//		}
//	}
//	delete pScene->mp_material_array;
//	pScene->mp_material_array = NULL;
//
//	// Delete the scene itself.
	delete pScene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void MaterialBuild(  sMesh * p_mesh, sScene * p_scene, bool bl, bool tx )
{
	if ( !p_mesh->mp_dl->m_material.p_header ) return;

	// Construct the DLs.
	uint32 size;

	Mem::Manager::sHandle().TopDownHeap()->PushAlign( 32 );
#define DL_BUILD_SIZE (8*1024)
	uint8 * p_build_dl = new (Mem::Manager::sHandle().TopDownHeap()) uint8[DL_BUILD_SIZE];

	// Build the texture upload DL.
	DCFlushRange ( p_build_dl, DL_BUILD_SIZE );

	GX::begin( p_build_dl, DL_BUILD_SIZE );

//	multi_mesh( p_mesh, p_scene );

	multi_mesh( p_mesh->mp_dl->m_material.p_header,
				&p_scene->mp_material_pass[p_mesh->mp_dl->m_material.p_header->m_pass_item],
				bl,
				tx );

	size = GX::end();

	DCFlushRange ( p_build_dl, DL_BUILD_SIZE );

	if ( size && ( size <= p_scene->mp_texture_dl[p_mesh->mp_dl->m_material.p_header->m_texture_dl_id].m_dl_size ) )
	{
//		p_mesh->mp_dl->mp_texture_dl = new uint8[size];

		memcpy ( p_scene->mp_texture_dl[p_mesh->mp_dl->m_material.p_header->m_texture_dl_id].mp_dl, p_build_dl, size );
		DCFlushRange ( p_scene->mp_texture_dl[p_mesh->mp_dl->m_material.p_header->m_texture_dl_id].mp_dl, size );

		p_scene->mp_texture_dl[p_mesh->mp_dl->m_material.p_header->m_texture_dl_id].m_dl_size = (uint16)size;
	}
	else
	{
//		p_mesh->mp_dl->mp_texture_dl = NULL;
//		p_mesh->mp_dl->m_texture_dl_size = 0;
	}

	Mem::Manager::sHandle().TopDownHeap()->PopAlign();

	// Done constructing DL.
	delete p_build_dl;
}

int g_material_id = -1;
GXBool g_comploc = (GXBool)2;

void ResetMaterialChange( void )
{
	g_material_id = -1; 
	g_comploc = (GXBool)2; 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

//static void MyGXProject ( 
//    f32  x,          // model coordinates
//    f32  y,
//    f32  z,
//    f32  mtx[3][4],  // model-view matrix
//    f32* pm,         // projection matrix, as returned by GXGetProjectionv
//    f32* vp,         // viewport, as returned by GXGetViewportv
//    f32* sx,         // screen coordinates
//    f32* sy,
//    f32* sz ) 
//{
//    Vec   peye;
//    f32   xc, yc, zc, wc;
//
//    ASSERTMSG(pm && vp && sx && sy && sz, GXERR_GET_NULL_PTR);
//
//    // transform to eye space
//    peye.x = mtx[0][0]*x + mtx[0][1]*y + mtx[0][2]*z + mtx[0][3];
//    peye.y = mtx[1][0]*x + mtx[1][1]*y + mtx[1][2]*z + mtx[1][3];
//    peye.z = mtx[2][0]*x + mtx[2][1]*y + mtx[2][2]*z + mtx[2][3];
//
//	// My addition: Just a frig to stop stuff messing up as it gets close to the near plane.
//	peye.z -= 512.0f;
//	if ( peye.z > -1.0f ) peye.z = -1.0f;
//
//    // transform to clip space
//    if (pm[0] == (f32)GX_PERSPECTIVE) { // perspective
//        xc = peye.x * pm[1] + peye.z * pm[2];
//        yc = peye.y * pm[3] + peye.z * pm[4];
//        zc = peye.z * pm[5] + pm[6];
//        wc = 1.0f / -peye.z;
//    } else { // ortho
//        xc = peye.x * pm[1] + pm[2];
//        yc = peye.y * pm[3] + pm[4];
//        zc = peye.z * pm[5] + pm[6];
//        wc = 1.0f;
//    }
//
//    // compute screen scale and offset
//    *sx = xc * vp[2]/2 * wc + vp[0] + vp[2]/2;
//    *sy = -yc * vp[3]/2 * wc + vp[1] + vp[3]/2;
//    *sz = zc * (vp[5] - vp[4]) * wc + vp[5];
//}
//

void MaterialSubmit( sMesh * p_mesh, sScene *pScene = NULL )
{
	if ( !pScene->mp_scene_data ) return;
	if ( !p_mesh->mp_dl->m_material.p_header ) return;
//	if ( !p_mesh->mp_dl->mp_texture_dl ) return;

	if ( gOverDraw )
	{
		GX::SetFog( GX_FOG_NONE, 0.0f, 0.0f, 0.0f, 0.0f, (GXColor){0,0,0,0} );
		GX::SetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0 );

		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_KONST,
							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
		
		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST,
								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV,
								 GX_TEV_SWAP0, GX_TEV_SWAP0 );

		GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );

		GX::SetTevKSel( GX_TEVSTAGE0, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A );
		GX::SetTevKColor( GX_KCOLOR0, (GXColor){8,8,8,255} );
		GX::SetChanCtrl( GX_ALPHA0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 
		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255});
		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255});

		GX::SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE);
		GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE ); 
		return;
	}







//	if ( g_dl /*&& !( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<3) )*/ )
//	if ( g_dl/* && !( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<3) )*/ )
	if ( g_dl && !( p_mesh->mp_dl->m_material.p_header->m_flags & ((1<<3)|(1<<4)) ) )
	{
////		if ( p_mesh->mp_dl->m_material.p_header->m_material_dl_id != g_material_id )
//		{
//			g_material_id = p_mesh->mp_dl->m_material.p_header->m_material_dl_id;
//
//			if ( pScene->mp_blend_dl[g_material_id].mp_dl && pScene->mp_blend_dl[g_material_id].m_dl_size )
//			{
////				GX::CallDisplayList( pScene->mp_blend_dl[g_material_id].mp_dl, pScene->mp_blend_dl[g_material_id].m_dl_size );
//				multi_mesh( p_mesh->mp_dl->m_material.p_header,
//							&pScene->mp_material_pass[p_mesh->mp_dl->m_material.p_header->m_pass_item],
//							true,
//							false );
//			}
//		}

		int tex_id = (int)p_mesh->mp_dl->m_material.p_header->m_texture_dl_id;
		GX::CallDisplayList( pScene->mp_texture_dl[tex_id].mp_dl, pScene->mp_texture_dl[tex_id].m_dl_size );
	}
	else
	{
		multi_mesh( p_mesh->mp_dl->m_material.p_header,
					&pScene->mp_material_pass[p_mesh->mp_dl->m_material.p_header->m_pass_item],
					true,
					true,
					p_mesh->mp_dl->m_material.p_header->m_flags & (1<<6) ? true : false,
					p_mesh->mp_dl->mp_object_header->m_num_skin_verts ? false : true );
	}

	// See if we need to upload env mapping matrices.
	sMaterialHeader * p_mat = p_mesh->mp_dl->m_material.p_header;
	sMaterialPassHeader *p_pass = &pScene->mp_material_pass[p_mat->m_pass_item];

	// Set Comploc
	u8 alphacutoff = p_mesh->mp_dl->m_material.p_header->m_alpha_cutoff;
	GXBool comploc;
//	if ( p_pass->m_texture.p_data && ( p_pass->m_texture.p_data->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_HOLES ) )
	if ( alphacutoff > 0 )
	{
		comploc = GX_FALSE;
	}
	else
	{
		comploc = GX_TRUE;
	}
	comploc = GX_FALSE;

	if ( g_comploc != comploc )
	{
		g_comploc = comploc;
		GX::SetZCompLoc( comploc );
	}

//	GX::SetAlphaCompare(GX_GEQUAL, alphacutoff, GX_AOP_AND, GX_GEQUAL, alphacutoff );
//	GX::SetZCompLoc( GX_FALSE );
	GX::SetAlphaCompare(GX_GEQUAL, alphacutoff, GX_AOP_AND, GX_GEQUAL, alphacutoff );

	for ( int lp = 0; lp < p_mat->m_passes; lp++, p_pass++ )
	{
		if ( p_pass->m_flags & ( (1<<1) | (1<<2) ) )
		{
			// UV Wibbled or Environment mapped.
			GXTexMtx mtx;
			switch ( lp )
			{
				case 0:
					mtx = GX_TEXMTX0;
					break;
				case 1:
					mtx = GX_TEXMTX1;
					break;
				case 2:
					mtx = GX_TEXMTX2;
					break;
				case 3:
					mtx = GX_TEXMTX3;
					break;
				default:
					mtx = GX_IDENTITY;
					break;
			}

			if( p_pass->m_flags & (1<<1) )
			{
				// Env mapping.
				Mtx s, t, e, mv;

				MTXInvXpose((Mtx)&EngineGlobals.local_to_camera, mv );

				// Project bounding sphere into screen space to get a metric to scroll the environment map.
				f32				p[GX_PROJECTION_SZ];
				f32				vp[GX_VIEWPORT_SZ];
				float			rx, ry;	//, rz;

				GX::GetProjectionv( p );
				GX::GetViewportv( vp );

				float x = EngineGlobals.local_to_camera.getPosX();
				float y = EngineGlobals.local_to_camera.getPosY();
//				float z = EngineGlobals.local_to_camera.getPosZ();

				rx = -x;	//EngineGlobals.local_to_camera.getRightX()*x + EngineGlobals.local_to_camera.getRightY()*y + EngineGlobals.local_to_camera.getRightZ()*z;
				ry = -y;	//EngineGlobals.local_to_camera.getUpX()*x + EngineGlobals.local_to_camera.getUpY()*y + EngineGlobals.local_to_camera.getUpZ()*z;


//				MyGXProject( p_mesh->mp_dl->m_sphere[X],
//						   p_mesh->mp_dl->m_sphere[Y],
//						   p_mesh->mp_dl->m_sphere[Z],
//						   (Mtx)&EngineGlobals.local_to_camera,
//						   p, vp, &rx, &ry, &rz );

				float u;
				float v;

				float ut = (float)p_pass->m_u_tile;
				float vt = (float)p_pass->m_v_tile;
				ut = ( ut * (1.0f / (float)(1<<12)) );
				vt = ( vt * (1.0f / (float)(1<<12)) );

				u = ( rx * ( ENV_MAP_SCROLL_SCALE * ut ) );
				v = ( ry * ( ENV_MAP_SCROLL_SCALE * vt ) );

				u -= (float)(int)u;		// Keep within +/-1.
				v -= (float)(int)v;

				// Create the rotational component.
				MTXScale( s, 0.5f * ut, -0.5f * vt, 0.0f );
				MTXTrans( t, 0.5f, 0.5f, 1.0f );
				MTXConcat( t, s, e );

				MTXConcat(e, mv, e);

				e[0][3] += u;
				e[1][3] += v;

				GX::LoadTexMtxImm(e, mtx, GX_MTX2x4);
			}
			else
			{
				if ( p_pass->m_uv_enabled )
				{
					// Explicit matrix.
					Mtx real;
//					MTXCopy( p_pass->mp_explicit_wibble->m_matrix, real );

//					real[0][3] = ( p_pass->mp_explicit_wibble->m_matrix[0][3] * -real[0][0] ) + ( p_pass->mp_explicit_wibble->m_matrix[1][3] * -real[0][1] );
//					real[1][3] = ( p_pass->mp_explicit_wibble->m_matrix[0][3] * -real[1][0] ) + ( p_pass->mp_explicit_wibble->m_matrix[1][3] * -real[1][1] );

					real[0][0] = ( (float)p_pass->m_uv_mat[0] ) * ( 1.0f / ((float)(1<<12)) );
					real[0][1] = -( (float)p_pass->m_uv_mat[1] ) * ( 1.0f / ((float)(1<<12)) );
					real[0][2] = 1.0f;
					real[0][3] = ( (float)p_pass->m_uv_mat[2] ) * ( 1.0f / ((float)(1<<12)) ) - 1.0f;

					real[1][0] = ( (float)p_pass->m_uv_mat[1] ) * ( 1.0f / ((float)(1<<12)) );
					real[1][1] = ( (float)p_pass->m_uv_mat[0] ) * ( 1.0f / ((float)(1<<12)) );
					real[1][2] = 1.0f;
					real[1][3] = ( (float)p_pass->m_uv_mat[3] ) * ( 1.0f / ((float)(1<<12)) ) - 1.0f;
					GX::LoadTexMtxImm( real, mtx, GX_MTX2x4 );
				}
				else
				{
					// Wibbled.
					float uoff, voff, t;

					t = (float)Tmr::GetTime() * 0.001f;

					Mtx	m;

					sMaterialUVWibble * p_uv = &pScene->mp_uv_wibble[p_pass->m_uv_wibble_index];

					uoff	= ( t * p_uv->m_u_vel ) + ( p_uv->m_u_amp * sinf( p_uv->m_u_freq * t + p_uv->m_u_phase ));
					voff	= ( t * p_uv->m_v_vel ) + ( p_uv->m_v_amp * sinf( p_uv->m_v_freq * t + p_uv->m_v_phase ));

					// Reduce offset mod 16 and put it in the range -8 to +8.
					uoff	+= 8.0f;
					uoff	-= (float)(( (int)uoff >> 4 ) << 4 );
					voff	+= 8.0f;
					voff	-= (float)(( (int)voff >> 4 ) << 4 );

					uoff = ( uoff < 0.0f ) ? ( uoff + 8.0f ) : ( uoff - 8.0f ); 
					voff = ( voff < 0.0f ) ? ( voff + 8.0f ) : ( voff - 8.0f ); 

					MTXTrans( m, uoff, voff, 0.0f );

					GX::LoadTexMtxImm(m, mtx, GX_MTX2x4);
				}
			}
		}
	}
}

} // namespace NxNgc



================================================
FILE: Code/Gfx/NGC/NX/scene.h
================================================
#ifndef __SCENE_H
#define __SCENE_H


#include 
#include 
#include 
#include "texture.h"
#include "mesh.h"
#include "material.h"
#include "anim.h"
#include 
#include 
#include 

namespace NxNgc
{

struct sMeshEntry
{
	sMesh*					mp_mesh;			// Pointer to mesh.
	int						m_bbox;				// Bounding box index.
};

#define SCENE_FLAG_RENDERING_SHADOW		( 1 << 7 )
#define SCENE_FLAG_RECEIVE_SHADOWS		( 1 << 8 )
#define SCENE_FLAG_SELF_SHADOWS			( 1 << 9 )
#define SCENE_FLAG_CLONED_GEOM			( 1 << 10 )

struct sScene
{
								sScene( void );
								~sScene( void );

	sMaterial *					sScene::GetMaterial( uint32 checksum );
	void						AddMeshes( int num_meshes, sMesh **pp_meshes );
	void						CountMeshes( int num_meshes, sMesh **pp_meshes );
	void						CreateMeshArrays( void );
	void						RemoveMeshes( int num_meshes, sMesh **pp_meshes );
	void						SortMeshes( void );
	void						FigureBoundingVolumes( void );
	void						HidePolys( uint32 mask, sCASData *p_cas_data, uint32 num_entries );

//	uint32						m_flags;
//	int							NumTextures;
//	uint8						*pTexBuffer;
//	uint8						*pTexDma;
//	sTexture					*pTextures;
//
//	int							m_num_materials;
//	sMaterial *					mp_material_array;
//	
//	// New style, with separate opaque and semitransparent mesh lists.
//	sMesh						**m_opaque_meshes;
//	int							m_num_opaque_entries;
//	int							m_num_filled_opaque_entries;
//	sMesh						**m_semitransparent_meshes;
//	int							m_num_semitransparent_entries;
//	int							m_num_filled_semitransparent_entries;
//	int							m_first_dynamic_sort_entry;
//	int							m_num_dynamic_sort_entries;
////	int							m_num_bboxes;
//	
////	class CInstance				*pInstances;
////
////	sScene						*pNext;
////
////	static sScene				*pHead;
//
//	
	bool						m_is_dictionary;
//
//	Mth::CBBox					m_bbox;	


	// New stuff.
	uint32						m_flags;

	sSceneHeader *				mp_scene_data;		// This is all data for pointers below.
	float *						mp_pos_pool;
	s16 *						mp_nrm_pool;
	uint32 *					mp_col_pool;
	s16 *						mp_tex_pool;
	sDLHeader *					mp_dl;
	sMaterialDL *				mp_blend_dl;
	sTextureDL *				mp_texture_dl;
	sMaterialVCWibbleKeyHeader *mp_vc_wibble;
	sMaterialHeader *			mp_material_header;
	sMaterialUVWibble *			mp_uv_wibble;
	sMaterialPassHeader *		mp_material_pass;
	uint16 *					mp_shadow_volume_mesh;
	sShadowEdge *				mp_shadow_edge;

	Mth::Vector					m_sphere;

	sMesh						**mpp_mesh_list;
	uint16						m_num_meshes;
	uint16						m_num_filled_meshes;

	uint16						m_num_opaque_meshes;
	uint16						m_num_pre_semitrans_meshes;
	uint16						m_num_dynamic_semitrans_meshes;
	uint16						m_num_post_semitrans_meshes;

//	uint16						m_num_opaque_entries;
//	uint16						m_num_semitrans_entries;

	// For mesh heirarchies
	Nx::CHierarchyObject*		mp_hierarchyObjects;						// array of hierarchy objects
	int							m_numHierarchyObjects;						// number of hierarchy objects
};


sScene	*LoadScene( const char *Filename, sScene *pScene );
void	DeleteScene( sScene *pScene );

#define MATERIAL_GROUP_SU	(1<<0)
#define MATERIAL_GROUP_CP	(1<<1)
#define MATERIAL_GROUP_A	(1<<2)
#define MATERIAL_GROUP_B	(1<<3)
#define MATERIAL_GROUP_C	(1<<4)

void ResetMaterialChange( void );
void MaterialSubmit( sMesh * p_mesh, sScene *pScene = NULL );

void MaterialBuild(  sMesh * p_mesh, sScene * p_scene, bool bl, bool tx ); 

} // namespace NxNgc


#endif // __SCENE_H




================================================
FILE: Code/Gfx/NGC/NX/sprite.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include "nx_init.h"
#include "scene.h"
#include "render.h"
#include "sprite.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


namespace NxNgc
{


/******************************************************************/
/*                                                                */
/* SDraw2D														  */
/*                                                                */
/******************************************************************/

SDraw2D *SDraw2D::sp_2D_draw_list = NULL;


SDraw2D::SDraw2D( float pri, bool hide )
{
	m_hidden = hide;
	m_pri = pri;

	mp_next = NULL;

	// add to draw list
	if( !m_hidden )
	{
		InsertDrawList();
	}
}



SDraw2D::~SDraw2D()
{
	// Try removing from draw list
	RemoveDrawList();
}



void SDraw2D::SetPriority( float pri )
{
	if (m_pri != pri)
	{
		m_pri = pri;

		// By removing and re-inserting, we re-sort the list
		if (!m_hidden)
		{
			RemoveDrawList();
			InsertDrawList();
		}
	}
}



void SDraw2D::SetHidden( bool hide )
{
	if (m_hidden != hide)
	{
		m_hidden = hide;
		if (hide)
		{
			RemoveDrawList();
		} else {
			InsertDrawList();
		}
	}
}



void SDraw2D::DrawAll()
{
	set_blend_mode( vBLEND_MODE_BLEND );

	SDraw2D *pDraw = sp_2D_draw_list;
	while (pDraw)
	{
		if (!pDraw->m_hidden)
		{
			pDraw->BeginDraw();
			pDraw->Draw();
			pDraw->EndDraw();
		}
		pDraw = pDraw->mp_next;
	}
}



void SDraw2D::InsertDrawList()
{
	if (!sp_2D_draw_list ||							// Empty List
		(m_pri <= sp_2D_draw_list->m_pri))	// Start List
	{
		mp_next = sp_2D_draw_list;
		sp_2D_draw_list = this;
	} else {				// Insert
		SDraw2D *p_cur = sp_2D_draw_list;
	
		// Find where to insert
		while(p_cur->mp_next)
		{
			if (m_pri <= p_cur->mp_next->m_pri)
				break;

			p_cur = p_cur->mp_next;
		}

		// Insert at this point
		mp_next = p_cur->mp_next;
		p_cur->mp_next = this;
	}
}



void SDraw2D::RemoveDrawList()
{
	// Take out from draw list
	if (sp_2D_draw_list == this)
	{
		sp_2D_draw_list = mp_next;
	} 
	else if (sp_2D_draw_list)
	{
		SDraw2D *p_cur = sp_2D_draw_list;

		while(p_cur->mp_next)
		{
			if (p_cur->mp_next == this)
			{
				p_cur->mp_next = mp_next;
				break;
			}

			p_cur = p_cur->mp_next;
		}
	}
}

	

//typedef struct
//{
//	float		x, y, z;
//	float		rhw;
//	D3DCOLOR	col;
//	float		u, v;
//}
//sSpriteVert;



sSprite::sSprite( float pri ) : SDraw2D( pri, true )
{
	mp_texture = NULL;
	
//	if( D3D_OK != D3DDevice_CreateVertexBuffer(	sizeof( sSpriteVert ) * 6,
//												0,										// Usage - ignored.
//												0,										// FVF - ignored.
//												0,										// Pool - ignored.
//												&p_vertex_buffer ))
//	{
//		exit( 0 );
//	}
//
//
}

sSprite::~sSprite()
{
//	p_vertex_buffer->Release();
}





void sSprite::BeginDraw( void )
{
	// Nothing required here right now.
}

//static inline void _GDWriteXFCmd(u16 addr, u32 val)
//{
//	GXWGFifo.u8 = GX_LOAD_XF_REG;
//	GXWGFifo.u16 = 0; // 0 means one value follows
//	GXWGFifo.u16 = addr;
//	GXWGFifo.u32 = val;
//}
//
//static inline void _GDWriteBPCmd(u32 regval)
//{
//	GXWGFifo.u8 = GX_LOAD_BP_REG;
//	GXWGFifo.u32 = regval;
//}
//
//void _SetGenMode( u8			nTexGens,
//				 u8			nChans,
//				 u8			nTevs,
//				 u8			nInds,
//				 GXCullMode	cm )
//{
//	static u8 cm2hw[] = { 0, 2, 1, 3 };
//	_GDWriteXFCmd( XF_NUMTEX_ID, XF_NUMTEX( nTexGens ));
//	_GDWriteXFCmd( XF_NUMCOLORS_ID, XF_NUMCOLORS( nChans ));
//	_GDWriteBPCmd( GEN_MODE( nTexGens, nChans, 0, (nTevs-1),
//							cm2hw[cm], nInds, 0, GEN_MODE_ID ));
//}


void sSprite::Draw( void )
{
	GXColor current_color;
	current_color.a = (m_rgba&0xff);
	if ( current_color.a == 0 ) return;
	current_color.b = ((m_rgba&0xff00)>>8);
	current_color.g = ((m_rgba&0xff0000)>>16);
	current_color.r = ((m_rgba&0xff000000)>>24);

	float start_screen_x = m_xpos;
	float start_screen_y = m_ypos;

	float x0,y0,x1,y1;

	float u0 = 0.0f;
	float v0 = 0.0f;
	float u1 = 0.0f;
	float v1 = 0.0f;

	if ( mp_texture )
	{
		u0 = 0.0f;
		v0 = 1.0f;
		u1 = (float)mp_texture->BaseWidth / (float)mp_texture->ActualWidth;       
		v1 = 1.0f - ( (float)mp_texture->BaseHeight / (float)mp_texture->ActualHeight ); 
	}

	// Check for flip
	float abs_scale_x = m_scale_x;
	float abs_scale_y = m_scale_y;
	if (abs_scale_x < 0.0f)
	{
		float temp = u0;
		u0 = u1;
		u1 = temp;
		abs_scale_x = -abs_scale_x;
	}
	if (abs_scale_y < 0.0f)
	{
		float temp = v0;
		v0 = v1;
		v1 = temp;
		abs_scale_y = -abs_scale_y;
	}

	x0 = (-m_xhot * abs_scale_x);
	y0 = (-m_yhot * abs_scale_y);

	if ( mp_texture )
	{
		x1 = x0 + ( /*m_width*/mp_texture->BaseWidth * abs_scale_x);
		y1 = y0 + ( /*m_height*/mp_texture->BaseHeight * abs_scale_y);
	//	x1 = x0 + ( m_width * abs_scale_x);
	//	y1 = y0 + ( m_height * abs_scale_y);
	}
	else
	{
		x1 = x0 + ( m_width * abs_scale_x);
		y1 = y0 + ( m_height * abs_scale_y);
	}
	
	Mth::Vector p0(x0, y0, 0.0f, 0.0f);
	Mth::Vector p1(x1, y0, 0.0f, 0.0f);
	Mth::Vector p2(x0, y1, 0.0f, 0.0f);
	Mth::Vector p3(x1, y1, 0.0f, 0.0f);

	if (m_rot != 0.0f)
	{
		p0.RotateZ(m_rot);
		p1.RotateZ(m_rot);
		p2.RotateZ(m_rot);
		p3.RotateZ(m_rot);
	}

	p0[X] += start_screen_x;
	p1[X] += start_screen_x;
	p2[X] += start_screen_x;
	p3[X] += start_screen_x;
	
	p0[Y] += start_screen_y;
	p1[Y] += start_screen_y;
	p2[Y] += start_screen_y;
	p3[Y] += start_screen_y;

	if( mp_texture == NULL )
	{
		// No alpha map.
		GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE ); 

		GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
		GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );
		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV,
								 GX_TEV_SWAP0, GX_TEV_SWAP0 );
		GX::SetTevColorInOp(	 GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC,
								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
		GX::SetTevOrder( 		 GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL );

//		GX::SetNumTexGens( 0 );
//		GX::SetNumTevStages( 1 );
		//GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//		GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY );
//		GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );

//		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
//		GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//		GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//		GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
//		GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
	
		// Set current vertex descriptor to enable position and color0.
		// Both use 8b index to access their data arrays.
		GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
	
		// Set material color.
		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
		GX::SetChanAmbColor( GX_COLOR0A0, current_color );
	
		GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
	
		// Send coordinates.
		GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
			GX::Position3f32(p0[X], p0[Y], -1.0f);
			GX::Position3f32(p1[X], p1[Y], -1.0f);
			GX::Position3f32(p3[X], p3[Y], -1.0f);
			GX::Position3f32(p2[X], p2[Y], -1.0f);
		GX::End();
	
		return;
	}
	
//	// Upload the texture.
//	GXTexObj	texObj;
//	GXInitTexObj(	&texObj,
//					mp_texture->pTexelData,
//					mp_texture->ActualWidth,
//					mp_texture->ActualHeight,
//					(GXTexFmt)mp_texture->format,
//					GX_CLAMP,
//					GX_CLAMP,
//					GX_FALSE );
//	GXLoadTexObj(	&texObj,
//					GX_TEXMAP0 );

	GX::UploadTexture( mp_texture->pTexelData,
					   mp_texture->ActualWidth,
					   mp_texture->ActualHeight,
					   (GXTexFmt)mp_texture->format,
					   GX_CLAMP,
					   GX_CLAMP,
					   GX_FALSE,
					   GX_LINEAR,
					   GX_LINEAR,
					   0.0f,
					   0.0f,
					   0.0f,
					   GX_FALSE,
					   GX_FALSE,
					   GX_ANISO_1,
					   GX_TEXMAP0 );

	if ( ( mp_texture->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA ) && mp_texture->pAlphaData )
	{
//		// Upload alpha map.
//		GXTexObj	alphaObj;
//		GXInitTexObj(	&alphaObj,
//						mp_texture->pAlphaData,
//						mp_texture->ActualWidth,
//						mp_texture->ActualHeight,
//						(GXTexFmt)mp_texture->format,
//						GX_CLAMP,
//						GX_CLAMP,
//						GX_FALSE );
//		GXLoadTexObj(	&alphaObj,
//						GX_TEXMAP1 );

		GX::UploadTexture( mp_texture->pAlphaData,
						   mp_texture->ActualWidth,
						   mp_texture->ActualHeight,
						   (GXTexFmt)mp_texture->format,
						   GX_CLAMP,
						   GX_CLAMP,
						   GX_FALSE,
						   GX_LINEAR,
						   GX_LINEAR,
						   0.0f,
						   0.0f,
						   0.0f,
						   GX_FALSE,
						   GX_FALSE,
						   GX_ANISO_1,
						   GX_TEXMAP1 );

		// Has alpha map.
		GX::SetTexChanTevIndCull( 2, 1, 2, 0, GX_CULL_NONE ); 
		GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
		GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, mp_texture->ActualWidth, mp_texture->ActualHeight );
		GX::SetTexCoordScale( GX_TEXCOORD1, GX_TRUE, mp_texture->ActualWidth, mp_texture->ActualHeight );
		GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
		GX::SetTexCoordGen( GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
		GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR0A0);
		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO,
								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVREG0,
								 GX_TEV_SWAP0, GX_TEV_SWAP0 );
		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_RASC, GX_CC_TEXC, GX_CC_ZERO,
							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVREG0 );

		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO,
								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV,
								 GX_TEV_SWAP0, GX_TEV_SWAP1 );
		GX::SetTevColorInOp( GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_C0,
							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
	}
	else
	{
		// No alpha map.
		GX::SetTexChanTevIndCull( 1, 1, 1, 0, GX_CULL_NONE ); 
		GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
		GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, mp_texture->ActualWidth, mp_texture->ActualHeight );
		GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
		GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO,
								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV,
								 GX_TEV_SWAP0, GX_TEV_SWAP0 );
		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_RASC, GX_CC_TEXC, GX_CC_ZERO,
							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
	}

	// Set current vertex descriptor to enable position and color0.
	// Both use 8b index to access their data arrays.
	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );

	// Set material color.
	GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
	GX::SetChanAmbColor( GX_COLOR0A0, current_color );

	GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//	GX::SetNumChans( 1 );

//	float uone = u1;		//(float)mp_texture->BaseWidth / (float)mp_texture->ActualWidth;
//	float vzro = v1;		//( (float)mp_texture->BaseHeight / (float)mp_texture->ActualHeight );

	// Send coordinates.
	GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
		GX::Position3f32(p0[X], p0[Y], -1.0f);
		GX::TexCoord2f32(u0, v0);
		GX::Position3f32(p1[X], p1[Y], -1.0f);
		GX::TexCoord2f32(u1, v0);
		GX::Position3f32(p3[X], p3[Y], -1.0f);
		GX::TexCoord2f32(u1, v1);
		GX::Position3f32(p2[X], p2[Y], -1.0f);
		GX::TexCoord2f32(u0, v1);
	GX::End();
}



void sSprite::EndDraw( void )
{
//	if( mp_texture == NULL )
//	{
//		return;
//	}
//
//	D3DDevice_SetPixelShader( 0 );
//	set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
//	D3DDevice_SetRenderState( D3DRS_LIGHTING, FALSE );
//
//	D3DDevice_SetTexture( 0, (LPDIRECT3DBASETEXTURE8)mp_texture->pD3DTexture );
//	if( mp_texture->pD3DPalette )
//	{
//		D3DDevice_SetPalette( 0, mp_texture->pD3DPalette );
//	}
//	
//	D3DDevice_SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE2X );
//	D3DDevice_SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_DIFFUSE );
//	D3DDevice_SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_TEXTURE );
//	D3DDevice_SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE2X );
//	D3DDevice_SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );
//	D3DDevice_SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE );
//
//	D3DDevice_SetStreamSource( 0, p_vertex_buffer, sizeof( sSpriteVert ));
//	EngineGlobals.p_Device->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 2 );
}






} // namespace NxNgc




================================================
FILE: Code/Gfx/NGC/NX/sprite.h
================================================
#ifndef __SPRITE_H
#define __SPRITE_H

#include "texture.h"

namespace NxNgc
{

struct SDraw2D
{
					SDraw2D( float pri = 0.0f, bool hide = true );
	virtual			~SDraw2D( void );

	void			SetPriority( float pri );
	float			GetPriority( void ) const;

	void			SetHidden( bool hide );
	bool			IsHidden( void ) const;

	// members
	SDraw2D			*mp_next;

	// Statics
	static void		DrawAll( void );

private:
	void			InsertDrawList( void );
	void			RemoveDrawList( void );

	virtual void	BeginDraw( void ) = 0;
	virtual void	Draw( void ) = 0;
	virtual void	EndDraw( void ) = 0;

	// Not even the derived classes should have direct access
	bool			m_hidden;
	float			m_pri;

	// 2D draw list (sorted by priority);
	static SDraw2D	*sp_2D_draw_list;
};


struct sSprite : public SDraw2D
{
	public:
					sSprite( float pri = 0.0f );
					~sSprite();

	sTexture		*mp_texture;

	float			m_xpos;
	float			m_ypos;
	uint16			m_width;
	uint16			m_height;
	float			m_scale_x;
	float			m_scale_y;
	float			m_xhot;
	float			m_yhot;
	float			m_rot;
	uint32			m_rgba;

private:

//	IDirect3DVertexBuffer8	*p_vertex_buffer;
	
	void					BeginDraw();
	void					Draw();
	void					EndDraw(void);
};


} // namespace NxNgc


#endif // __SPRITE_H



================================================
FILE: Code/Gfx/NGC/NX/texture.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "chars.h"
#include "texture.h"
#include "sys/ngc/p_gx.h"

namespace NxNgc
{

uint8*					TextureBuffer;
//sTexture* Textures		= NULL;
uint32 NumTextures		= 0;
uint32 NumTextureGroups	= 0;
Lst::HashTable< sTexture > *pTextureTable	= NULL;




sTexture::sTexture()
{
	pTexelData = NULL;
	pAlphaData = NULL;
	pOldAlphaData = NULL;
	flags = 0;
}

sTexture::~sTexture()
{
	if ( pTexelData )
	{
		delete pTexelData;
	}
	if ( pAlphaData && ( ( flags & TEXTURE_FLAG_CHANNEL_MASK ) == TEXTURE_FLAG_CHANNEL_GREEN ) )
	{
		delete pAlphaData;
	}
	if ( ( flags & TEXTURE_FLAG_OLD_DATA ) && pOldAlphaData )
	{
		delete pOldAlphaData;
	}
}



// Eeeek - the .img contains PS2 specific register values for bit depth.
// Use these values to convert them.
#define PSMCT32		0x00
#define PSMCT24		0x01
#define PSMCT16		0x02
#define PSMCT16S	0x0A
#define PS_GPU24	0x12
#define PSMT8		0x13
#define PSMT4		0x14
#define PSMT8H		0x1B
#define PSMT4HL		0x24
#define PSMT4HH		0x2C
#define PSMZ32		0x30
#define PSMZ24		0x31
#define PSMZ16		0x32
#define PSMZ16S		0x3A

const static uint32 TEXTURE_VERSION = 2;

sTexture *LoadTexture( const char *p_filename )
{
	sTexture *p_texture = NULL;

	void *p_FH = File::Open( p_filename, "rb" );

	if( p_FH )
	{
		uint32 version;
		uint32 checksum;
		uint32 width;
		uint32 height;
		uint32 depth;
		uint32 levels;
		uint32 rwidth;
		uint32 rheight;
		uint16 alphamap;
		uint16 hasholes;

		uint32 FileSize = File::GetFileSize(p_FH);

		// Load the actual texture

		p_texture = new sTexture();

		File::Read( &version,  4, 1, p_FH );
		File::Read( &checksum, 4, 1, p_FH );
		File::Read( &width,    4, 1, p_FH );
		File::Read( &height,   4, 1, p_FH );
		File::Read( &depth,    4, 1, p_FH );
		File::Read( &levels,   4, 1, p_FH );
		File::Read( &rwidth,   4, 1, p_FH );
		File::Read( &rheight,  4, 1, p_FH );
		File::Read( &alphamap, 2, 1, p_FH );
		File::Read( &hasholes, 2, 1, p_FH );

		Dbg_MsgAssert( version <= TEXTURE_VERSION, ("Illegal texture version number: %d (The newest version we deal with is: %d)\n", version, TEXTURE_VERSION ));
		Dbg_MsgAssert( depth == 32, ("We only deal with 32-bit textures.\n"));
	
		uint tsize = FileSize-(9*4);

		p_texture->flags = 0;
		p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_GREEN;

		Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
		if ( tsize == ( width * height * 4 ) )
		{
			p_texture->format		= (uint8)GX_TF_RGBA8;
		
			p_texture->pTexelData = new uint8[tsize];
			p_texture->byte_size = tsize;
			Dbg_MsgAssert(p_texture->pTexelData, ("couldn't allocate buffer for texture file %s\n", p_filename));
			File::Read( p_texture->pTexelData, 1, tsize, p_FH );
		}
		else
		{
			p_texture->format		= (uint8)GX_TF_CMPR;
		
			if ( alphamap )
			{
				tsize = tsize / 2;
			}

			p_texture->pTexelData = new uint8[tsize];
			p_texture->byte_size = tsize;
			Dbg_MsgAssert(p_texture->pTexelData, ("couldn't allocate buffer for texture file %s\n", p_filename));
			File::Read( p_texture->pTexelData, 1, tsize, p_FH );

			if ( alphamap )
			{
				p_texture->pAlphaData = new uint8[tsize];
				Dbg_MsgAssert(p_texture->pAlphaData, ("couldn't allocate buffer for alpha file %s\n", p_filename));
				File::Read( p_texture->pAlphaData, 1, tsize, p_FH );
				p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA;
			}
		}
		Mem::Manager::sHandle().BottomUpHeap()->PopAlign();

		p_texture->Checksum		= checksum;
		p_texture->BaseWidth	= width;
		p_texture->BaseHeight	= height;
		p_texture->Levels		= levels;
		p_texture->ActualWidth	= rwidth;
		p_texture->ActualHeight	= rheight;
		p_texture->flags |= hasholes ? NxNgc::sTexture::TEXTURE_FLAG_HAS_HOLES : 0;

		File::Close( p_FH );
	}
	
	return p_texture;
}





sTexture* GetTexture( uint32 checksum )
{
	if( pTextureTable )
	{
		pTextureTable->IterateStart();
		sTexture *p_tex = pTextureTable->IterateNext();
		while( p_tex )
		{
			if( p_tex->Checksum == checksum )
			{
				return p_tex;
			}
			p_tex = pTextureTable->IterateNext();
		}
	}
	return NULL;
}





bool sTexture::SetRenderTarget( int width, int height, int depth, int z_depth )
{
//	Checksum		= 0;;
//	BaseWidth		= width;
//	BaseHeight		= height;
//	ActualWidth		= width;
//	ActualHeight	= height;
//	Levels			= 0;
//	pAlphaData		= NULL;
//	format			= GX_TF_RGBA8;
//	
//	pTexelData		= new uint8[width*height*4];
//
	return true;
}

extern int g_id;

GXTexMapID sTexture::Upload( GXTexMapID id, uint8 flags, float k )
{
	GXTexMapID	rv = id;

	// Texels.
	GX::UploadTexture(  pTexelData,
						ActualWidth,
						ActualHeight,
						((GXTexFmt)format),
						flags & (1<<5) ? GX_CLAMP : GX_REPEAT,
						flags & (1<<6) ? GX_CLAMP : GX_REPEAT,
						Levels > 1 ? GX_TRUE : GX_FALSE,
						Levels > 1 ? GX_LIN_MIP_LIN : GX_LINEAR,
						GX_LINEAR,
						0.0f,
						Levels > 1 ? Levels - 1 : 0.0f,
						Levels > 1 ? k : 0.0f,
						GX_FALSE,
						GX_TRUE,
						GX_ANISO_1,
						rv ); 
	rv = (GXTexMapID)(((int)rv)+1);

	// Alpha.
	if ( flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA )
	{
		GX::UploadTexture(  pAlphaData,
							ActualWidth,
							ActualHeight,
							((GXTexFmt)format),
							flags & (1<<5) ? GX_CLAMP : GX_REPEAT,
							flags & (1<<6) ? GX_CLAMP : GX_REPEAT,
							Levels > 1 ? GX_TRUE : GX_FALSE,
							Levels > 1 ? GX_LIN_MIP_LIN : GX_LINEAR,
							GX_LINEAR,
							0.0f,
							Levels > 1 ? Levels - 1 : 0.0f,
							Levels > 1 ? k : 0.0f,
							GX_FALSE,
							GX_TRUE,
							GX_ANISO_1,
							rv ); 
		rv = (GXTexMapID)(((int)rv)+1);
	}

	return rv;
}


} // namespace NxNgc




================================================
FILE: Code/Gfx/NGC/NX/texture.h
================================================
#ifndef __TEXTURE_H
#define __TEXTURE_H

#include 
#include 

namespace NxNgc
{

struct sTexture
{
	enum ETextureFlags
	{
		TEXTURE_FLAG_HAS_HOLES				= (1<<0),
		TEXTURE_FLAG_HAS_ALPHA				= (1<<1),
		TEXTURE_FLAG_CHANNEL_GREEN			= (1<<2),
		TEXTURE_FLAG_CHANNEL_RED			= (1<<3),
		TEXTURE_FLAG_CHANNEL_BLUE			= (1<<4),
		TEXTURE_FLAG_SINGLE_OWNER			= (1<<5),
		TEXTURE_FLAG_OLD_DATA				= (1<<6),
		TEXTURE_FLAG_REPLACED				= (1<<7),

		TEXTURE_FLAG_CHANNEL_MASK			= ( TEXTURE_FLAG_CHANNEL_GREEN | TEXTURE_FLAG_CHANNEL_RED | TEXTURE_FLAG_CHANNEL_BLUE )
	};

						sTexture();
						~sTexture();
	
	bool				SetRenderTarget( int width, int height, int depth, int z_depth );

	GXTexMapID			Upload( GXTexMapID id, uint8 flags, float k );

	uint32				Checksum;
	uint16				BaseWidth, BaseHeight;		// The size of the D3D texture (will be power of 2).
	uint16				ActualWidth, ActualHeight;	// The size of the texture itself (may not be power of 2).
	uint8				Levels;
//	uint8				HasHoles;
	uint8				format;
//	uint8				has_alpha;
//	uint16				channel;
//	uint16				single_owner;			// 1 means single owner.
	uint16				flags;
	uint32				byte_size;
	uint8*				pTexelData;
	uint8*				pAlphaData;
	uint8*				pOldAlphaData;
};


sTexture* GetTexture( uint32 checksum );

sTexture	*LoadTexture( const char *p_filename );
void LoadTextureGroup(int Group);

extern uint8 *TextureBuffer;
//extern sTexture *Textures;
extern Lst::HashTable< sTexture > *pTextureTable;

extern uint32 NumTextures, NumTextureGroups;
extern uint NextTBP, LastCBP;

} // namespace NxNgc

#endif // __TEXTURE_H




================================================
FILE: Code/Gfx/NGC/NX/types.h
================================================
#ifndef __TYPES_H
#define __TYPES_H

typedef unsigned int 		uint;
typedef unsigned long long	uint64;
typedef unsigned int		uint32;
typedef unsigned short		uint16;
typedef unsigned char		uint8;

typedef signed int 			sint;
typedef signed long long	sint64;
typedef signed int			sint32;
typedef signed short		sint16;
typedef signed char			sint8;

#endif // __TYPES_H


================================================
FILE: Code/Gfx/NGC/blur.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		blur.cpp												**
**																			**
**	Created:		06/21/00	-	mjb										**
**																			**
**	Description:	Motion-Blur effect										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

//#include 
//#include 
//#include 
//#include 
//				 
//#include 
//
///*****************************************************************************
//**								DBG Information								**
//*****************************************************************************/
//
//
///*****************************************************************************
//**								  Externals									**
//*****************************************************************************/
//
//extern int skyFrameBit;
//
//namespace Gfx
//{
//
///*****************************************************************************
//**								   Defines									**
//*****************************************************************************/
//
///*****************************************************************************
//**								Private Types								**
//*****************************************************************************/
//
//typedef struct
//{
//	sceGifTag       giftag;
//	u_int       	data_buf[0x80];
//} GsClearData __attribute__((aligned(16)));
//
///*****************************************************************************
//**								 Private Data								**
//*****************************************************************************/
//
///*****************************************************************************
//**								 Public Data								**
//*****************************************************************************/
//
///*****************************************************************************
//**							  Private Prototypes							**
//*****************************************************************************/
//
///*****************************************************************************
//**							   Private Functions							**
//*****************************************************************************/
//
//void copy_frame(void)
//{
//	int i,nloop;
//	GsClearData gcd;
//
//	nloop = 25;
//
//	SCE_GIF_CLEAR_TAG(&gcd.giftag);
//	gcd.giftag.NLOOP = nloop;
//	gcd.giftag.EOP = 1;
//	gcd.giftag.NREG = 1;
//	gcd.giftag.REGS0 = 0xe;	// A_D
//
//	i=0;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x0000001a; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000001; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x0000004c; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x000a0046; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000047; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000040; gcd.data_buf[i+1] = (u_int)0x00df0000; gcd.data_buf[i] = (u_int)0x027f0000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000042; gcd.data_buf[i+1] = (u_int)0x00000040; gcd.data_buf[i] = (u_int)0x00000064; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000008; gcd.data_buf[i+1] = (u_int)0x0000037c; gcd.data_buf[i] = (u_int)0x009fc00a; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000018; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000046; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000001; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000049; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000006; gcd.data_buf[i+1] = (u_int)0x00000006; gcd.data_buf[i] = (u_int)0xa8028000; i+=4;
//
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000014; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000060; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x0000003b; gcd.data_buf[i+1] = (u_int)0x00000080; gcd.data_buf[i] = (u_int)0x00000080; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000001; gcd.data_buf[i+1] = (u_int)0x3f800000; gcd.data_buf[i] = (u_int)0x80808080; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000000; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x0000015e; i+=4;
//
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000003; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000004; gcd.data_buf[i+1] = (u_int)0x00000001; gcd.data_buf[i] = (u_int)0x00100000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000003; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x0e002800; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000004; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x0e102800; i+=4;
//
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x0000003f; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000003; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00100000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000004; gcd.data_buf[i+1] = (u_int)0x00000001; gcd.data_buf[i] = (u_int)0x00100000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000003; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x0e102800; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000004; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x0e102800; i+=4;
//
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x0000003f; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000008; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000005; i+=4;
//
//	FlushCache(0);
//
//	*D2_QWC = gcd.giftag.NLOOP+1;
//
//	*D2_MADR = (u_int)&gcd.giftag & 0x0fffffff;
//	FlushCache(0);
//	*D2_CHCR = (1 << 8) | 1;
//
//	while( (*D2_CHCR & 0x0100) || (*GIF_STAT & 0x0c00) || ((*GS_CSR & 0x4000) == 0) );
//
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//
//void copy_frame1(void)
//{
//	int i,nloop;
//	GsClearData gcd;
//
//	nloop = 25;
//
//	SCE_GIF_CLEAR_TAG(&gcd.giftag);
//	gcd.giftag.NLOOP = nloop;
//	gcd.giftag.EOP = 1;
//	gcd.giftag.NREG = 1;
//	gcd.giftag.REGS0 = 0xe;	// A_D
//
//	i=0;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x0000001a; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000001; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x0000004c; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x000a0000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000047; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000040; gcd.data_buf[i+1] = (u_int)0x00df0000; gcd.data_buf[i] = (u_int)0x027f0000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000042; gcd.data_buf[i+1] = (u_int)0x00000040; gcd.data_buf[i] = (u_int)0x00000064; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000008; gcd.data_buf[i+1] = (u_int)0x0000037c; gcd.data_buf[i] = (u_int)0x009fc00a; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000018; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00080008; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000046; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000001; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000049; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000006; gcd.data_buf[i+1] = (u_int)0x00000006; gcd.data_buf[i] = (u_int)0xa80288c0; i+=4;
//
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000014; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000060; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x0000003b; gcd.data_buf[i+1] = (u_int)0x00000080; gcd.data_buf[i] = (u_int)0x00000080; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000001; gcd.data_buf[i+1] = (u_int)0x3f800000; gcd.data_buf[i] = (u_int)0x80808080; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000000; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x0000015e; i+=4;
//
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000003; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000004; gcd.data_buf[i+1] = (u_int)0x00000001; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000003; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x0e002800; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000004; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x0e002800; i+=4;
//
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x0000003f; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000003; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00100000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000004; gcd.data_buf[i+1] = (u_int)0x00000001; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000003; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x0e102800; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000004; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x0e002800; i+=4;
//
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x0000003f; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000000; i+=4;
//	gcd.data_buf[i+3] = (u_int)0x0; gcd.data_buf[i+2] = (u_int)0x00000008; gcd.data_buf[i+1] = (u_int)0x00000000; gcd.data_buf[i] = (u_int)0x00000005; i+=4;
//
//	FlushCache(0);
//
//	*D2_QWC = gcd.giftag.NLOOP+1;
//
//	*D2_MADR = (u_int)&gcd.giftag & 0x0fffffff;
//	FlushCache(0);
//	*D2_CHCR = (1 << 8) | 1;
//
//	while( (*D2_CHCR & 0x0100) || (*GIF_STAT & 0x0c00) || ((*GS_CSR & 0x4000) == 0) );
//
//}
//
///*****************************************************************************
//**							   Public Functions								**
//*****************************************************************************/
//
//void	Blur( void )
//{
//	if (skyFrameBit & 0x1)
//	{
//		copy_frame1();
//	}
//	else
//	{
//		copy_frame();
//	}
//
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//
//
//} // namespace Gfx
//
//

================================================
FILE: Code/Gfx/NGC/p_NxGeom.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxGeom.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  3/4/2002
//****************************************************************************

#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
static s32	last_audio_update = 0;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void do_audio_update( void )
{
	s32 t = OSGetTick();
	if(( t < last_audio_update ) || ( OSDiffTick( t, last_audio_update ) > (s32)( OS_TIMER_CLOCK / 60 )))
	{
		last_audio_update = t;
		Pcm::PCMAudio_Update();
	}
}
	
	

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcGeom::CNgcGeom() : mp_init_mesh_list( NULL ), m_mesh_array( NULL ), m_visible( 0x55 )
{
	mp_instance = NULL;
//	mp_sector	= NULL;
	m_active	= true;
	m_mod.offset[X] = 0.0f;
	m_mod.offset[Y] = 0.0f;
	m_mod.offset[Z] = 0.0f;
	m_mod.offset[W] = 0.0f;
	m_mod.scale[X] = 1.0f;
	m_mod.scale[Y] = 1.0f;
	m_mod.scale[Z] = 1.0f;
	m_mod.scale[W] = 1.0f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcGeom::~CNgcGeom( void )
{
	DestroyMeshArray();

	if( mp_instance != NULL )
	{
		delete mp_instance;
		mp_instance = NULL;
	}
}



/////////////////////////////////////////////////////////////////////////////////////
// Mesh List Functions
//
// The mesh list is only for initialization of the CGeom.  As we find all the
// meshes for the CGeoms, we add them to the temporary lists.  When we are
// done, the list is copied into a permanent array.
//
// All the list functions use the TopDownHeap for memory allocation.

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::InitMeshList( void )
{
	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
	mp_init_mesh_list = new Lst::Head< NxNgc::sMesh >;
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::AddMesh( NxNgc::sMesh *mesh )
{
	Dbg_Assert( mp_init_mesh_list );

	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
	Lst::Node< NxNgc::sMesh > *node = new Lst::Node< NxNgc::sMesh > (mesh);
	mp_init_mesh_list->AddToTail( node );
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::CreateMeshArray( void )
{
	if( !m_mesh_array )
	{
		Dbg_Assert( mp_init_mesh_list );

		m_num_mesh = mp_init_mesh_list->CountItems();
		if (m_num_mesh)
		{
			Lst::Node< NxNgc::sMesh > *mesh, *next;

			m_mesh_array = new NxNgc::sMesh*[m_num_mesh];
			int k = 0;
			for( mesh = mp_init_mesh_list->GetNext(); mesh; mesh = next )
			{
				next			= mesh->GetNext();
				m_mesh_array[k]	= mesh->GetData();
				delete mesh;
				k++;
			}
		}

		// Delete temporary list.
		delete mp_init_mesh_list;
		mp_init_mesh_list = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Lst::Head< NxNgc::sMesh >	*CNgcGeom::GetMeshList( void )
{
	return mp_init_mesh_list;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcGeom::RegisterMeshArray( bool just_count )
{
	// Tells the engine to count or add the meshes for this sector to the rendering list for supplied scene id.
	if( just_count )
	{
		mp_scene->GetEngineScene()->CountMeshes( m_num_mesh, m_mesh_array );
	}
	else
	{
		mp_scene->GetEngineScene()->AddMeshes( m_num_mesh, m_mesh_array );
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::DestroyMeshArray( void )
{
	if( m_mesh_array )
	{
		// Tells the engine to remove the meshes for this sector from the rendering list for supplied scene id.
		if( mp_scene && mp_scene->GetEngineScene())
		{
			mp_scene->GetEngineScene()->RemoveMeshes( m_num_mesh, m_mesh_array );
		}

		// Now actually go through and delete each mesh.
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxNgc::sMesh *p_mesh = m_mesh_array[m];

			// Delete pools.
			if ( p_mesh->m_flags & NxNgc::sMesh::MESH_FLAG_CLONED_POS )
			{
				delete p_mesh->mp_dl->mp_pos_pool;
			}

			if ( p_mesh->m_flags & NxNgc::sMesh::MESH_FLAG_CLONED_COL )
			{
				delete p_mesh->mp_dl->mp_col_pool;
			}

			// Delete copied dl if it's there.
			if ( p_mesh->m_flags & NxNgc::sMesh::MESH_FLAG_CLONED_DL )
			{
				delete p_mesh->mp_dl;
			}

			delete p_mesh;
		}

		delete [] m_mesh_array;
		m_mesh_array = NULL;
	}
}



/******************************************************************/
/*                                                                */
/* Generates an entirely new engine scene.                        */
/* Used when instance-cloning CGeoms.                             */
/*                                                                */
/******************************************************************/
NxNgc::sScene* CNgcGeom::GenerateScene( void )
{
	NxNgc::sScene *p_scene = new NxNgc::sScene();

	memcpy( p_scene, mp_scene->GetEngineScene(), sizeof( NxNgc::sScene ) );

	p_scene->m_flags				= SCENE_FLAG_CLONED_GEOM;
	p_scene->m_is_dictionary		= false;

	p_scene->m_num_meshes			= 0;		// No meshes as yet.
	p_scene->m_num_filled_meshes	= 0;
	p_scene->mpp_mesh_list			= NULL;

	p_scene->mp_hierarchyObjects	= NULL;
	p_scene->m_numHierarchyObjects	= 0;

	p_scene->CountMeshes( m_num_mesh, m_mesh_array );
	p_scene->CreateMeshArrays();
	p_scene->AddMeshes( m_num_mesh, m_mesh_array );
	p_scene->SortMeshes();



	// Set the bounding box and bounding sphere.
//	p_scene->m_bbox = m_bbox;
//	p_scene->FigureBoundingVolumes();
	
	return p_scene;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CNgcGeom::plat_load_geom_data(CMesh* pMesh, CModel* pModel, bool color_per_material)
{
//	// The skeleton must exist by this point (unless it's a hacked up car).
//	int numBones;
//	numBones = pModel->GetSkeleton() ? pModel->GetSkeleton()->GetNumBones() : 1;
//	
//	Mth::Matrix temp;
//	CNgcMesh *p_Ngc_mesh = static_cast( pMesh );
//	mp_instance = new NxNgc::CInstance( p_Ngc_mesh->GetScene()->GetEngineScene(), temp, numBones, pModel->GetBoneTransforms() );
//
//    return true;
	
	
	// The skeleton must exist by this point (unless it's a hacked up car).
	int numBones;
	numBones = pModel->GetNumBones();
	
	Mth::Matrix temp;
	CNgcMesh *p_Ngc_mesh = static_cast( pMesh );
	
	// Store a pointer to the CNgcMesh, used for obtaining CAS poly removal data.
	mp_mesh = p_Ngc_mesh;

//	CNgcModel *p_Ngc_model = static_cast( pModel );
	NxNgc::CInstance *p_instance = new NxNgc::CInstance( p_Ngc_mesh->GetScene()->GetEngineScene(), temp, numBones, pModel->GetBoneTransforms());
	SetInstance( p_instance );
	p_instance->SetModel( pModel );

    return true;


//	int numBones;
//	numBones = pModel->GetSkeleton() ? pModel->GetSkeleton()->GetNumBones() : 0;
//
//	NxNgc::Mat temp;
//	NxNgc::MatIdentity(temp);
//	Dbg_Assert( mp_instance == NULL );
//
//	Dbg_Assert( pMesh );
//	Dbg_Assert( pMesh->GetTextureDictionary() );
//	NxNgc::sScene* pScene = ((Nx::CNgcTexDict*)pMesh->GetTextureDictionary())->GetEngineTextureDictionary();
//	Dbg_Assert( pScene );
//
//	if ( numBones )
//	{
//		mp_matrices = ((CNgcModel*)pModel)->GetMatrices();
//	}
//	mp_instance = new NxNgc::CInstance(pScene, temp, numBones, mp_matrices ); 
//
//	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_active( bool active )
{
	if( mp_instance )
	{
		mp_instance->SetActive( active );
	}	
	else
	{
		m_active = active;
		if( m_mesh_array )
		{
			for( uint m = 0; m < m_num_mesh; ++m )
			{
				NxNgc::sMesh *p_mesh = m_mesh_array[m];
				p_mesh->SetActive( active );
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcGeom::plat_is_active( void ) const
{
	if( mp_instance )
	{
		return mp_instance->GetActive();
	}	
	return m_active;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_world_position( const Mth::Vector& pos )
{
//	return;
	// Ensure w component is correct.

	if( mp_instance )
	{
		mp_instance->GetTransform()->SetPos( pos );
	}
	else
	{
		// Go through and adjust the individual meshes.
//		for( uint32 i = 0; i < m_num_mesh; ++i )
		{
//			NxNgc::sMesh *p_mesh = m_mesh_array[i];
//			p_mesh->mp_mod = &m_mod;

			Mth::Vector delta_pos = pos - m_mod.offset;

			m_mod.offset[X] = pos[X];
			m_mod.offset[Y] = pos[Y];
			m_mod.offset[Z] = pos[Z];

			int num = plat_get_num_render_verts();
			Mth::Vector * p_verts = new Mth::Vector[num];
			plat_get_render_verts( p_verts );
			for ( int lp = 0; lp < num; lp++ )
			{
				p_verts[lp] += delta_pos;
			}
			plat_set_render_verts( p_verts );
			delete p_verts;


//			Mth::Vector delta_pos( pos[X] - m_offset[X], pos[Y] - m_offset[Y], pos[Z] - m_offset[Z], 1.0f );
//			p_mesh->mp_dl->m_sphere[X] += delta_pos[X];
//			p_mesh->mp_dl->m_sphere[Y] += delta_pos[Y];
//			p_mesh->mp_dl->m_sphere[Z] += delta_pos[Z];
//			p_mesh->SetPosition( proper_pos );
		}

//		if ( mp_scene->GetEngineScene()->mp_scene_data )
//		{
//			Mth::Vector proper_pos( pos[X] - m_offset[X], pos[Y] - m_offset[Y], pos[Z] - m_offset[Z], 1.0f );
//	
//			float * pos = mp_scene->GetEngineScene()->mp_pos_pool;
//			for ( int lp = 0; lp += mp_scene->GetEngineScene()->mp_scene_data->m_num_pos; lp++ )
//			{
//				pos[0] += proper_pos[X];
//				pos[1] += proper_pos[Y];
//				pos[2] += proper_pos[Z];
//				pos += 3;
//			}
//			m_offset[X] = proper_pos[X];
//			m_offset[Y] = proper_pos[Y];
//			m_offset[Z] = proper_pos[Z];
//		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &	CNgcGeom::plat_get_world_position() const
{
	static Mth::Vector pos;
	pos.Set( m_mod.offset[X], m_mod.offset[Y], m_mod.offset[Z], 1.0f );
	return pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::CBBox & CNgcGeom::plat_get_bounding_box( void ) const
{
	Dbg_Assert(mp_coll_tri_data);
	return mp_coll_tri_data->GetBBox();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_bounding_sphere( const Mth::Vector& boundingSphere )
{
	if( mp_instance )
	{
		NxNgc::sScene *p_scene = mp_instance->GetScene();
		if( p_scene )
		{
			if ( mp_instance->GetBoneTransforms() && p_scene->m_numHierarchyObjects )
			{
				// Car or something...
				p_scene->m_sphere = boundingSphere;
			}
			else
			{
				if ( p_scene->mpp_mesh_list[0] )
				{
					p_scene->mpp_mesh_list[0]->mp_dl->mp_object_header->m_sphere = boundingSphere;
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector CNgcGeom::plat_get_bounding_sphere( void ) const
{
	if( mp_instance )
	{
		NxNgc::sScene *p_scene = mp_instance->GetScene();
		if( p_scene )
		{
			if ( mp_instance->GetBoneTransforms() && p_scene->m_numHierarchyObjects )
			{
				// Car or something...
				return p_scene->m_sphere;
			}
			else
			{
				if ( p_scene->mpp_mesh_list[0] )
				{
					return p_scene->mpp_mesh_list[0]->mp_dl->mp_object_header->m_sphere;
				}
			}
		}
	}
	
	return Mth::Vector( 0.0f, 0.0f, 0.0f, 10000.0f );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcGeom::plat_render(Mth::Matrix* pRootMatrix, Mth::Matrix* pBoneMatrices, int numBones)
{
	if( mp_instance )
	{
		mp_instance->SetTransform( *pRootMatrix );
	}
    return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_bone_matrix_data( Mth::Matrix* pBoneMatrices, int numBones )
{
	if( mp_instance )
	{
		Mth::Matrix* p_bone_matrices	= mp_instance->GetBoneTransforms();
		memcpy( p_bone_matrices, pBoneMatrices, numBones * sizeof( Mth::Matrix ));
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcGeom::plat_hide_polys( uint32 mask )
{
	if( mp_mesh )
	{
		// Obtain a pointer to the Ngc scene.
		NxNgc::sScene *p_engine_scene = GetInstance()->GetScene();

		// Request the scene to hide the relevant polys.
		p_engine_scene->HidePolys( mask, mp_mesh->GetCASData(), mp_mesh->GetNumCASData());
	}
	
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_visibility( uint32 mask )
{
	// Set values
	m_visible = mask & 0xff;

	if( mp_instance )
	{
		mp_instance->SetVisibility( mask & 0xff );
	}	
//	else
//	{
		if( m_mesh_array )
		{
			for( uint m = 0; m < m_num_mesh; ++m )
			{
				NxNgc::sMesh *p_mesh = m_mesh_array[m];
				p_mesh->SetVisibility( mask & 0xff );
			}
		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 CNgcGeom::plat_get_visibility( void ) const
{
	return m_visible | 0xFFFFFF00;
}






/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_color( Image::RGBA rgba )
{
	if( mp_instance && mp_instance->GetBoneTransforms() )
	{
		if( mp_instance && mp_instance->GetScene())
		{
			for( int m = 0; m < mp_instance->GetScene()->m_num_filled_meshes; ++m )
			{
				NxNgc::sMesh			*p_mesh	= mp_instance->GetScene()->mpp_mesh_list[m];
				NxNgc::sMaterialHeader	*p_mat	= p_mesh->mp_dl->m_material.p_header;
				if( p_mat )
				{
					NxNgc::sMaterialPassHeader * p_pass = &mp_instance->GetScene()->mp_material_pass[p_mat->m_pass_item];
					for ( int pass = 0; pass < p_mat->m_passes; pass++, p_pass++ )
					{
						p_pass->m_color.r = rgba.r;
						p_pass->m_color.g = rgba.g;
						p_pass->m_color.b = rgba.b;

//						NxNgc::sTextureDL* p_dl = &mp_instance->GetScene()->mp_texture_dl[p_mat->m_texture_dl_id];
//						GX::begin( p_dl->mp_dl, p_dl->m_dl_size );
//						multi_mesh( p_mat, p_pass, true, true );
//						p_dl->m_dl_size = GX::end();

//						int size = mp_instance->GetScene()->mp_texture_dl[p_mat->m_texture_dl_id].m_dl_size;
//						void * p_dl = mp_instance->GetScene()->mp_texture_dl[p_mat->m_texture_dl_id].mp_dl;
//						GX::ChangeMaterialColor( (unsigned char *)p_dl, size, p_pass->m_color, pass );

						p_mat->m_flags |= (1<<4);
					}
				}
			}
		}
	}
	else if( m_mesh_array != NULL )
	{
		for( uint32 i = 0; i < m_num_mesh; ++i )
		{
			NxNgc::sMesh *p_mesh = m_mesh_array[i];
			p_mesh->m_base_color.r = rgba.r;
			p_mesh->m_base_color.g = rgba.g;
			p_mesh->m_base_color.b = rgba.b;
			p_mesh->m_base_color.a = rgba.a;
		}
	}

//	// Oofa, nasty hack.
//	if( mp_instance )
//	{
//		// Grab the engine scene from the geom, and set all meshes to the color.
//		NxNgc::sScene *p_scene = mp_instance->GetScene();
//		for( int i = 0; i < p_scene->m_num_opaque_entries; ++i )
//		{
//			NxNgc::sMesh *p_mesh = p_scene->m_opaque_meshes[i];
//			p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE;
//			p_mesh->m_material_color_override.r = rgba.r;
//			p_mesh->m_material_color_override.g = rgba.g;
//			p_mesh->m_material_color_override.b = rgba.b;
//			p_mesh->m_material_color_override.a = rgba.a;
//		}
//		for( int i = 0; i < p_scene->m_num_semitransparent_entries; ++i )
//		{
//			NxNgc::sMesh *p_mesh = p_scene->m_semitransparent_meshes[i];
//			p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE;
//			p_mesh->m_material_color_override.r = rgba.r;
//			p_mesh->m_material_color_override.g = rgba.g;
//			p_mesh->m_material_color_override.b = rgba.b;
//			p_mesh->m_material_color_override.a = rgba.a;
//		}
//	}
//	else if( m_mesh_array != NULL )
//	{
//		for( uint32 i = 0; i < m_num_mesh; ++i )
//		{
//			NxNgc::sMesh *p_mesh = m_mesh_array[i];
//			p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE;
//			p_mesh->m_material_color_override.r = rgba.r;
//			p_mesh->m_material_color_override.g = rgba.g;
//			p_mesh->m_material_color_override.b = rgba.b;
//			p_mesh->m_material_color_override.a = rgba.a;
//		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_clear_color( void )
{
	if( mp_instance && mp_instance->GetBoneTransforms() )
	{
		if( mp_instance && mp_instance->GetScene())
		{
			for( int m = 0; m < mp_instance->GetScene()->m_num_filled_meshes; ++m )
			{
				NxNgc::sMesh			*p_mesh	= mp_instance->GetScene()->mpp_mesh_list[m];
				NxNgc::sMaterialHeader	*p_mat	= p_mesh->mp_dl->m_material.p_header;
				if( p_mat )
				{
					NxNgc::sMaterialPassHeader * p_pass = &mp_instance->GetScene()->mp_material_pass[p_mat->m_pass_item];
					for ( int pass = 0; pass < p_mat->m_passes; pass++, p_pass++ )
					{
						p_pass->m_color.r = 128;
						p_pass->m_color.g = 128;
						p_pass->m_color.b = 128;
//						NxNgc::sTextureDL* p_dl = &mp_instance->GetScene()->mp_texture_dl[p_mat->m_texture_dl_id];
//						GX::begin( p_dl->mp_dl, p_dl->m_dl_size );
//						multi_mesh( p_mat, p_pass, true, true );
//						p_dl->m_dl_size = GX::end();

//						int size = mp_instance->GetScene()->mp_texture_dl[p_mat->m_texture_dl_id].m_dl_size;
//						void * p_dl = mp_instance->GetScene()->mp_texture_dl[p_mat->m_texture_dl_id].mp_dl;
//						GX::ChangeMaterialColor( (unsigned char *)p_dl, size, p_pass->m_color, pass );
						p_mat->m_flags |= (1<<4);
					}
				}
			}
		}
	}
	else if( m_mesh_array != NULL )
	{
		for( uint32 i = 0; i < m_num_mesh; ++i )
		{
			NxNgc::sMesh *p_mesh = m_mesh_array[i];
			p_mesh->m_base_color.r = 128;
			p_mesh->m_base_color.g = 128;
			p_mesh->m_base_color.b = 128;
			p_mesh->m_base_color.a = 255;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA	CNgcGeom::plat_get_color( void ) const
{
	Image::RGBA rgba( 0, 0, 0, 0 );

	if( mp_instance && mp_instance->GetBoneTransforms() )
	{
		if( mp_instance && mp_instance->GetScene())
		{
			if ( mp_instance->GetScene()->m_num_filled_meshes > 0 )
			{
				NxNgc::sMesh			*p_mesh	= mp_instance->GetScene()->mpp_mesh_list[0];
				NxNgc::sMaterialHeader	*p_mat	= p_mesh->mp_dl->m_material.p_header;
				if( p_mat )
				{
					NxNgc::sMaterialPassHeader * p_pass = &mp_instance->GetScene()->mp_material_pass[p_mat->m_pass_item];

					rgba.r = p_pass->m_color.r;
					rgba.g = p_pass->m_color.g;
					rgba.b = p_pass->m_color.b;
					rgba.a = p_pass->m_color.a;

				}
			}
		}
	}
	else if( m_mesh_array != NULL )
	{
		NxNgc::sMesh *p_mesh = m_mesh_array[0];
		rgba.r = p_mesh->m_base_color.r;
		rgba.g = p_mesh->m_base_color.g;
		rgba.b = p_mesh->m_base_color.b;
		rgba.a = p_mesh->m_base_color.a;
	}

	return rgba;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcGeom::plat_set_material_color( uint32 mat_name_checksum, int pass, Image::RGBA rgba )
{
	bool something_changed = false;

	if( mp_instance && mp_instance->GetScene())
	{
		for( int m = 0; m < mp_instance->GetScene()->m_num_filled_meshes; ++m )
		{
			NxNgc::sMesh			*p_mesh	= mp_instance->GetScene()->mpp_mesh_list[m];
			NxNgc::sMaterialHeader	*p_mat	= p_mesh->mp_dl->m_material.p_header;
			if( p_mat )
			{
				bool	want_this_material	= false;
				int		adjusted_pass		= pass;

				// We are searching for materials with a matching name. However there is a caveat in that the
				// conversion process sometimes creates new materials for geometry flagged as 'render as separate', or for
				// geometry which is mapped with only certain passes of a multipass material (or both cases).
				// In such a case, the new material name checksum will differ from the original material name checksum,
				// with the following bits having significance (i.e. consider bitflags = new_namechecksum - old_namechecksum ):
				//
				// Bits 3->6	Pass flags indicating which passes of the original material this material uses
				// Bits 0->2	Absolute number ('render as separate' flagged geometry) indicating which single pass of the material this material represents.
				if( p_mat->m_materialNameChecksum == mat_name_checksum )
					want_this_material = true;
				else if(( p_mat->m_materialNameChecksum > mat_name_checksum ) && (( p_mat->m_materialNameChecksum - mat_name_checksum ) <= 0x7F ))
				{
					uint32 checksum_diff		= p_mat->m_materialNameChecksum - mat_name_checksum;
					int render_separate_pass	= checksum_diff & 0x07;
					uint32 pass_flags			= checksum_diff >> 3;

					if( render_separate_pass )
					{
						if( render_separate_pass == ( pass + 1 ))
						{
							want_this_material = true;

							// Readjust the pass to zero, since this material was formed as a single pass of a multipass material.
							adjusted_pass = 0;
						}
					}
					else if( pass_flags )
					{
						// This material was created during scene conversion from another material with more passes.
						if( pass_flags & ( 1 << pass ))
						{
							want_this_material = true;
							for( int p = 0; p < pass; ++p )
							{
								if(( pass_flags & ( 1 << p )) == 0 )
								{
									// Readjust the pass down by 1, since this material was created as a subset of another material.
									--adjusted_pass;
								}
							}
						}
					}
				}

				if( want_this_material )
				{
//					Dbg_Assert((uint32)adjusted_pass < p_mat->m_passes );
		
					if ( adjusted_pass < p_mat->m_passes )
					{
						NxNgc::sMaterialPassHeader * p_pass = &mp_instance->GetScene()->mp_material_pass[p_mat->m_pass_item+adjusted_pass];

						if( !( p_pass->m_flags & (1<<4) ))		// COLOR_LOCKED
						{
							p_pass->m_color.r = rgba.r;
							p_pass->m_color.g = rgba.g;
							p_pass->m_color.b = rgba.b;

//							void * p_dl = mp_instance->GetScene()->mp_texture_dl[p_mat->m_texture_dl_id].mp_dl;
//							int size = mp_instance->GetScene()->mp_texture_dl[p_mat->m_texture_dl_id].m_dl_size;
//
//							GX::ChangeMaterialColor( (unsigned char *)p_dl, size, p_pass->m_color, adjusted_pass );

							// Flag as being direct for ever more.
							p_mat->m_flags |= (1<<4);

							something_changed = true;
						}
					}
				}
			}
		}
	}
	return something_changed;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcGeom *CNgcGeom::plat_clone( bool instance, CScene *p_dest_scene )
{
//	instance = true;

	CNgcGeom *p_clone = NULL;

	// This is a CNgcGeom 'hanging' from a sector. Create a new CNgcGeom which will store the CInstance.
	p_clone = new CNgcGeom();
	p_clone->mp_scene = mp_scene;

	// Create new meshes for the clone.
	p_clone->m_mesh_array	= m_num_mesh ? new NxNgc::sMesh*[m_num_mesh] : NULL;
	p_clone->m_num_mesh		= m_num_mesh;
//	NxNgc::sMesh * p_mesh0 = NULL;
	for( uint32 m = 0; m < p_clone->m_num_mesh; ++m )
	{
//		if ( mp_instance )
//		{
//			// Instances don't need display lists cloning...
//			p_clone->m_mesh_array[m] = m_mesh_array[m]->Clone( false );
//		}
//		else
//		{
			p_clone->m_mesh_array[m] = m_mesh_array[m]->Clone( instance );
//		}

//		if ( m_mesh_array[m]->m_localMeshIndex == 0 )
//		{
//			p_mesh0 = p_clone->m_mesh_array[m];
//		}
	}
//	if ( p_mesh0 )
//	{
//		for( uint32 m = 0; m < p_clone->m_num_mesh; ++m )
//		{
//			if ( p_clone->m_mesh_array[m]->m_localMeshIndex != 0 )
//			{
//				p_clone->m_mesh_array[m]->mp_posBuffer = p_mesh0->mp_posBuffer;
//				p_clone->m_mesh_array[m]->mp_normBuffer = p_mesh0->mp_normBuffer;
//				p_clone->m_mesh_array[m]->mp_colBuffer = p_mesh0->mp_colBuffer;
//			}
//		}
//	}

	if( instance == false )
	{
		p_clone->SetActive( true );

		// In this situation, we need to add the individual meshes to the scene.
		// Grab a temporary workspace buffer.
		Nx::CNgcScene *p_Ngc_scene						= static_cast( p_dest_scene );
		NxNgc::sScene *p_scene								= p_Ngc_scene->GetEngineScene();
		NxNgc::sMesh **p_temp_mesh_buffer				= ( p_scene->m_num_meshes > 0 ) ? new NxNgc::sMesh*[p_scene->m_num_meshes] : NULL;

		// Set the scene pointer for the clone.
		p_clone->SetScene( p_Ngc_scene );
		
		// Copy meshes over into the temporary workspace buffer.
		for( int i = 0; i < p_scene->m_num_meshes; ++i )
		{
			p_temp_mesh_buffer[i] = p_scene->mpp_mesh_list[i];
		}

		// Store how many meshes were present.
		int old_num_entries			= p_scene->m_num_meshes;
		
		// Delete current mesh arrays.
		delete [] p_scene->mpp_mesh_list;

		// Important to set these to NULL.
		p_scene->mpp_mesh_list			= NULL;
		
		// Include new meshes in count.
		p_scene->CountMeshes( m_num_mesh, m_mesh_array );

		// Allocate new mesh arrays.
		p_scene->CreateMeshArrays();

		// Copy old mesh data back in.
		for( int i = 0; i < old_num_entries; ++i )
		{
			p_scene->mpp_mesh_list[i] = p_temp_mesh_buffer[i];
		}

		// Remove temporary arrays.
		delete [] p_temp_mesh_buffer;
		
		// Add new meshes.
		p_scene->AddMeshes( p_clone->m_num_mesh, p_clone->m_mesh_array );

		// Sort the meshes.
		p_scene->SortMeshes();
	}
	else
	{
		// Create a new scene which will be attached via the instance.
		p_clone->m_bbox			= m_bbox;
		NxNgc::sScene *p_scene = p_clone->GenerateScene();

		p_clone->SetActive( true );
	
		// Create the instance.
		Mth::Matrix temp;
		temp.Identity();
		NxNgc::CInstance *p_instance = new NxNgc::CInstance( p_scene, temp, 1, NULL );
		
		// This instance will be the only object maintaining a reference to the attached scene, so we want to delete
		// the scene when the instance gets removed.
		p_instance->SetFlag( NxNgc::CInstance::INSTANCE_FLAG_DELETE_ATTACHED_SCENE );

		// Hook the clone up to the instance.		
		p_clone->SetInstance( p_instance );

		p_clone->mp_scene = NULL;
	}

	return p_clone;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_model_lights( CModelLights* p_model_lights )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcGeom *CNgcGeom::plat_clone( bool instance, CModel* pDestModel )
{
	CNgcGeom *p_clone = NULL;

	// This is a CNgcGeom 'hanging' from a sector. Create a new CNgcGeom which will store the CInstance.
	Nx::CNgcScene *p_Ngc_scene = new CNgcScene;
	p_Ngc_scene->SetEngineScene( mp_instance->GetScene() );

	p_clone = new CNgcGeom();
	p_clone->mp_scene = p_Ngc_scene;

	int num_mesh = mp_instance->GetScene()->m_num_filled_meshes;

	// Create new meshes for the clone.
	p_clone->m_mesh_array	= num_mesh ? new NxNgc::sMesh*[num_mesh] : NULL;
	p_clone->m_num_mesh		= num_mesh;
	for( uint32 m = 0; m < p_clone->m_num_mesh; ++m )
	{
		p_clone->m_mesh_array[m] = mp_instance->GetScene()->mpp_mesh_list[m]->Clone( instance );
	}
	// Create a new scene which will be attached via the instance.
	p_clone->m_bbox			= m_bbox;
	NxNgc::sScene *p_scene = p_clone->GenerateScene();

	// Kill the temp scene.
	p_Ngc_scene->SetEngineScene( NULL );
	delete p_Ngc_scene;
	p_clone->mp_scene = NULL;

	p_clone->SetActive( true );

	int numBones;
	numBones = pDestModel->GetNumBones();

	Mth::Matrix * p_bone = new Mth::Matrix[numBones];

	for ( int i = 0; i < numBones; i++ )
	{
		p_bone[i].Identity();
	}

//	((CNgcModel*)pDestModel)->GetInstance()->SetBoneTransforms( p_bone );

	// Create the instance.
	Mth::Matrix temp;
	temp.Identity();
	NxNgc::CInstance *p_instance = new NxNgc::CInstance( p_scene, temp, numBones, p_bone );
	
	((CNgcModel*)pDestModel)->SetInstance( p_instance );

	// This instance will be the only object maintaining a reference to the attached scene, so we want to delete
	// the scene when the instance gets removed.
	p_instance->SetFlag( NxNgc::CInstance::INSTANCE_FLAG_DELETE_ATTACHED_SCENE );

	// Hook the clone up to the instance.		
	p_clone->SetInstance( p_instance );

	return p_clone;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcGeom::plat_set_orientation(const Mth::Matrix& orient)
{
	if( mp_instance )
	{
		// Set values
		mp_instance->GetTransform()->GetRight() = orient[X];
		mp_instance->GetTransform()->GetUp() = orient[Y];
		mp_instance->GetTransform()->GetAt() = orient[Z];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcGeom::plat_rotate_y(Mth::ERot90 rot)
{
	if( rot != Mth::ROT_0 )
	{
		if( mp_instance )
		{
			Mth::Matrix *p_matrix = mp_instance->GetTransform();

			// Zero out translation component.
			Mth::Matrix	instance_transform = *p_matrix;
			instance_transform[W] = Mth::Vector(0.0f, 0.0f, 0.0f, 1.0f);

			// Build rotation matrix.
			Mth::Matrix	rot_mat;
			float		rad = (float)((int)rot) * ( Mth::PI * 0.5f );
			Mth::CreateRotateYMatrix( rot_mat, rad );

			// Rotate matrix.
			rot_mat	= instance_transform * rot_mat;

			// Replace translation component.
			rot_mat[W] = p_matrix->GetPos();

			mp_instance->SetTransform( rot_mat );
		}
		else
		{
			*((Mth::ERot90*)&m_mod.offset[W]) = rot;

			int num = plat_get_num_render_verts();
			Mth::Vector * p_verts = new Mth::Vector[num];
			plat_get_render_verts( p_verts );
			for ( int lp = 0; lp < num; lp++ )
			{
				float x;
				float z;
				switch ( rot )
				{
					case Mth::ROT_90:
						x = p_verts[lp][X] - m_mod.offset[X];
						z = p_verts[lp][Z] - m_mod.offset[Z];
						p_verts[lp][X] =  z + m_mod.offset[X];
						p_verts[lp][Z] = -x + m_mod.offset[Z];
						break;
					case Mth::ROT_180:
						x = p_verts[lp][X] - m_mod.offset[X];
						z = p_verts[lp][Z] - m_mod.offset[Z];
						p_verts[lp][X] = -x + m_mod.offset[X];
						p_verts[lp][Z] = -z + m_mod.offset[Z];
						break;
					case Mth::ROT_270:
						x = p_verts[lp][X] - m_mod.offset[X];
						z = p_verts[lp][Z] - m_mod.offset[Z];
						p_verts[lp][X] = -z + m_mod.offset[X];
						p_verts[lp][Z] = x + m_mod.offset[Z];
						break;
					default:
					case Mth::ROT_0:
						// Do nothing.
						break;
				}
			}
			plat_set_render_verts( p_verts );
			delete p_verts;
		}
	}
	*((Mth::ERot90*)&m_mod.offset[W]) = rot;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcGeom::plat_set_transform(const Mth::Matrix& transform)
{
	// Set values
	*mp_instance->GetTransform() = transform;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Matrix &	CNgcGeom::plat_get_transform() const
{
	return *mp_instance->GetTransform();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CNgcGeom::build_index_map( int * p_pos_index, int * p_col_index )
{
	int num_used = 0;
	// Need to go through every index and find unique pos/color pairs.
	if ( mp_scene->GetEngineScene()->mp_scene_data )
	{
		for ( uint mesh = 0; mesh < m_num_mesh; mesh++ )
		{
			NxNgc::sMesh * p_mesh = m_mesh_array[mesh];

			// Parse display list looking for this pos index, then get the color index.
			unsigned char * p_start = (unsigned char *)&p_mesh->mp_dl[1];
			unsigned char * p_end = &p_start[p_mesh->mp_dl->m_size];
			p_start = &p_start[p_mesh->mp_dl->m_index_offset];		// Skip to actual 1st GDBegin.
			unsigned char * p8 = p_start;

			int stride = p_mesh->mp_dl->m_index_stride * 2;
			int offset = p_mesh->mp_dl->m_color_offset * 2;

			while ( p8 < p_end )
			{
				if ( ( p8[0] & 0xf8 ) == GX_TRIANGLESTRIP )
				{
					// Found a triangle strip - parse it.
					int num_idx = ( p8[1] << 8 ) | p8[2];
					p8 += 3;		// Skip GDBegin

					for ( int v = 0; v < num_idx; v++ )
					{
						int index = ( ( p8[0] << 8 ) | p8[1] ) + p_mesh->mp_dl->m_array_base;
						int cindex = ( p8[offset+0] << 8 ) | p8[offset+1];

						// See if this is a unique pair.
						bool found = false;
						for ( int lp = 0; lp < num_used; lp++ )
						{
							if ( ( p_pos_index[lp] == index ) && ( p_col_index[lp] == cindex ) )
							{
								found = true;
								break;
							}
						}
						if ( !found )
						{
							p_pos_index[num_used] = index;
							p_col_index[num_used] = cindex;
							num_used++;
						}
						p8 += stride;
					}
				}
				else
				{
					break;
				}
			}
		}
	}
	return num_used;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#define MAX_INDEX 2500

int CNgcGeom::plat_get_num_render_verts( void )
{
	if ( mp_scene->GetEngineScene()->mp_scene_data )
	{
		NxNgc::sMesh * p_mesh = m_mesh_array[0];

		if ( p_mesh->m_flags & NxNgc::sMesh::MESH_FLAG_INDEX_COUNT_SET )
		{
			return p_mesh->mp_dl->m_num_indices;
		}
	}

	int pos_index[MAX_INDEX];
	int col_index[MAX_INDEX];

	int num_verts = build_index_map( pos_index, col_index );
//	Pcm::PCMAudio_Update();
	do_audio_update();

	return num_verts;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcGeom::plat_get_render_verts( Mth::Vector *p_verts )
{
	// See if pool has been copied already.
	if ( mp_scene->GetEngineScene()->mp_scene_data )
	{
		NxNgc::sMesh * p_mesh = m_mesh_array[0];

		if ( p_mesh->m_flags & NxNgc::sMesh::MESH_FLAG_CLONED_POS )
		{
			for ( int lp = 0; lp < p_mesh->mp_dl->m_num_indices; lp++ )
			{
				p_verts[lp][X] = p_mesh->mp_dl->mp_pos_pool[(lp*3)+0];
				p_verts[lp][Y] = p_mesh->mp_dl->mp_pos_pool[(lp*3)+1];
				p_verts[lp][Z] = p_mesh->mp_dl->mp_pos_pool[(lp*3)+2];
				p_verts[lp][W] = 1.0f;
			}
//			Pcm::PCMAudio_Update();
			do_audio_update();
			return;
		}
	}

	int pos_index[MAX_INDEX];
	int col_index[MAX_INDEX];

	int num_verts = build_index_map( pos_index, col_index );
//	Pcm::PCMAudio_Update();
	do_audio_update();

	// Copy from original pool.
	for ( int lp = 0; lp < num_verts; lp++ )
	{
		p_verts[lp][X] = mp_scene->GetEngineScene()->mp_pos_pool[(pos_index[lp]*3)+0];
		p_verts[lp][Y] = mp_scene->GetEngineScene()->mp_pos_pool[(pos_index[lp]*3)+1];
		p_verts[lp][Z] = mp_scene->GetEngineScene()->mp_pos_pool[(pos_index[lp]*3)+2];
		p_verts[lp][W] = 1.0f;

	}
//	Pcm::PCMAudio_Update();
	do_audio_update();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_get_render_colors( Image::RGBA *p_colors )
{
	// See if pool has been copied already.
	if ( mp_scene->GetEngineScene()->mp_scene_data )
	{
		NxNgc::sMesh * p_mesh = m_mesh_array[0];

		if ( p_mesh->m_flags & NxNgc::sMesh::MESH_FLAG_CLONED_COL )
		{
			for ( int lp = 0; lp < p_mesh->mp_dl->m_num_indices; lp++ )
			{
				p_colors[lp].r = ( p_mesh->mp_dl->mp_col_pool[lp] >> 24 ) & 0xff;
				p_colors[lp].g = ( p_mesh->mp_dl->mp_col_pool[lp] >> 16 ) & 0xff;
				p_colors[lp].b = ( p_mesh->mp_dl->mp_col_pool[lp] >>  8 ) & 0xff;
				p_colors[lp].a = ( p_mesh->mp_dl->mp_col_pool[lp] >>  0 ) & 0xff;
			}
//			Pcm::PCMAudio_Update();
			do_audio_update();
			return;
		}
	}

	int pos_index[MAX_INDEX];
	int col_index[MAX_INDEX];

	int num_verts = build_index_map( pos_index, col_index );
//	Pcm::PCMAudio_Update();
	do_audio_update();

	// Copy from original pool.
	for ( int lp = 0; lp < num_verts; lp++ )
	{
		p_colors[lp].r = ( mp_scene->GetEngineScene()->mp_col_pool[col_index[lp]] >> 24 ) & 0xff;
		p_colors[lp].g = ( mp_scene->GetEngineScene()->mp_col_pool[col_index[lp]] >> 16 ) & 0xff;
		p_colors[lp].b = ( mp_scene->GetEngineScene()->mp_col_pool[col_index[lp]] >>  8 ) & 0xff;
		p_colors[lp].a = ( mp_scene->GetEngineScene()->mp_col_pool[col_index[lp]] >>  0 ) & 0xff;
	}
//	Pcm::PCMAudio_Update();
	do_audio_update();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_render_verts( Mth::Vector *p_verts )
{
	// See if we need to create a new pool.
	bool need_to_create = false;
	if ( mp_scene->GetEngineScene()->mp_scene_data )
	{
		NxNgc::sMesh * p_mesh = m_mesh_array[0];
		if ( ( p_mesh->m_flags & NxNgc::sMesh::MESH_FLAG_CLONED_POS ) == 0 )
		{
			need_to_create = true;
		}
	}

	// Create new pos pool if necessary.
	float * p_new_pos;
	int num_verts;
	if ( need_to_create && mp_scene->GetEngineScene()->mp_scene_data )
	{
		int pos_index[MAX_INDEX];
		int col_index[MAX_INDEX];

		num_verts = build_index_map( pos_index, col_index );
//		Pcm::PCMAudio_Update();
		do_audio_update();

		// Allocate space for new pool.
		p_new_pos = new float[3*num_verts];

		for ( uint mesh = 0; mesh < m_num_mesh; mesh++ )
		{
			NxNgc::sMesh * p_mesh = m_mesh_array[mesh];

			if ( !p_mesh->mp_dl->mp_pos_pool )
			{
				p_mesh->mp_dl->mp_pos_pool = p_new_pos;

				if ( mesh == 0 )
				{
					p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_CLONED_POS;
					p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_INDEX_COUNT_SET;
					p_mesh->mp_dl->m_num_indices = num_verts;
				}

				// Remap the DL.
				// Parse display list looking for this pos index, then get the color index.
				unsigned char * p_start = (unsigned char *)&p_mesh->mp_dl[1];
				unsigned char * p_end = &p_start[p_mesh->mp_dl->m_size];
				p_start = &p_start[p_mesh->mp_dl->m_index_offset];		// Skip to actual 1st GDBegin.
				unsigned char * p8 = p_start;

				int stride = p_mesh->mp_dl->m_index_stride * 2;
				int offset = p_mesh->mp_dl->m_color_offset * 2;

				while ( p8 < p_end )
				{
					if ( ( p8[0] & 0xf8 ) == GX_TRIANGLESTRIP )
					{
						// Found a triangle strip - parse it.
						int num_idx = ( p8[1] << 8 ) | p8[2];
						p8 += 3;		// Skip GDBegin

						for ( int v = 0; v < num_idx; v++ )
						{
							// Find the remapped index.
							int index = ( ( p8[0] << 8 ) | p8[1] ) + p_mesh->mp_dl->m_array_base;
							int cindex = ( p8[offset+0] << 8 ) | p8[offset+1];

							for ( int lp = 0; lp < num_verts; lp++ )
							{
								if ( ( pos_index[lp] == index ) && ( col_index[lp] == cindex ) )
								{
									p8[0] = ( lp >> 8 ) & 0xff;
									p8[1] = lp & 0xff;
									break;
								}
							}
							p8 += stride;
						}
					}
					else
					{
						break;
					}
				}
				DCFlushRange( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size );
			}
			p_mesh->mp_dl->m_array_base = 0;

//			Pcm::PCMAudio_Update();
			do_audio_update();
		}
	}
	else
	{
		p_new_pos = m_mesh_array[0]->mp_dl->mp_pos_pool;
		num_verts = m_mesh_array[0]->mp_dl->m_num_indices;
	}

	// Copy actual data over.
	Mth::CBBox bbox;
	for ( int v = 0; v < num_verts; v++ )
	{
		p_new_pos[(v*3)+0] = p_verts[v][X];
		p_new_pos[(v*3)+1] = p_verts[v][Y];
		p_new_pos[(v*3)+2] = p_verts[v][Z];
		bbox.AddPoint( p_verts[v] );
	}
	DCFlushRange( p_new_pos, sizeof( float ) * 3 * num_verts );

	if ( mp_scene->GetEngineScene()->mp_scene_data )
	{
		for ( uint mesh = 0; mesh < m_num_mesh; mesh++ )
		{
			NxNgc::sMesh * p_mesh = m_mesh_array[mesh];

			// Now refigure the bounding sphere.
			Mth::Vector sphere_center	= bbox.GetMin() + ( 0.5f * ( bbox.GetMax() - bbox.GetMin()));
			p_mesh->mp_dl->m_sphere[X] = sphere_center[X];
			p_mesh->mp_dl->m_sphere[Y] = sphere_center[Y];
			p_mesh->mp_dl->m_sphere[Z] = sphere_center[Z];
			p_mesh->mp_dl->m_sphere[W] = ( bbox.GetMax() - sphere_center ).Length(); 

			p_mesh->m_bottom_y = bbox.GetMin()[Y];
		}
	}
//	Pcm::PCMAudio_Update();
	do_audio_update();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_render_colors( Image::RGBA *p_colors )
{
	// See if we need to create a new pool.
	bool need_to_create = false;
	if ( mp_scene->GetEngineScene()->mp_scene_data )
	{
		NxNgc::sMesh * p_mesh = m_mesh_array[0];
		if ( !( p_mesh->m_flags & NxNgc::sMesh::MESH_FLAG_CLONED_COL ) )
		{
			need_to_create = true;
		}
	}

	// Create new col pool if necessary.
	uint32 * p_new_col;
	int num_verts;
	if ( need_to_create && mp_scene->GetEngineScene()->mp_scene_data )
	{
		int pos_index[MAX_INDEX];
		int col_index[MAX_INDEX];

		num_verts = build_index_map( pos_index, col_index );
//		Pcm::PCMAudio_Update();
		do_audio_update();

		// Allocate space for new pool.
		p_new_col = new uint32[num_verts];

		for ( uint mesh = 0; mesh < m_num_mesh; mesh++ )
		{
			NxNgc::sMesh * p_mesh = m_mesh_array[mesh];

			if ( !p_mesh->mp_dl->mp_col_pool )
			{
				p_mesh->mp_dl->mp_col_pool = p_new_col;

				if ( mesh == 0 )
				{
					p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_CLONED_COL;
					p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_INDEX_COUNT_SET;
					p_mesh->mp_dl->m_num_indices = num_verts;
				}

				// Remap the DL.
				// Parse display list looking for this pos index, then get the color index.
				unsigned char * p_start = (unsigned char *)&p_mesh->mp_dl[1];
				unsigned char * p_end = &p_start[p_mesh->mp_dl->m_size];
				p_start = &p_start[p_mesh->mp_dl->m_index_offset];		// Skip to actual 1st GDBegin.
				unsigned char * p8 = p_start;

				int stride = p_mesh->mp_dl->m_index_stride * 2;
				int offset = p_mesh->mp_dl->m_color_offset * 2;

				while ( p8 < p_end )
				{
					if ( ( p8[0] & 0xf8 ) == GX_TRIANGLESTRIP )
					{
						// Found a triangle strip - parse it.
						int num_idx = ( p8[1] << 8 ) | p8[2];
						p8 += 3;		// Skip GDBegin

						for ( int v = 0; v < num_idx; v++ )
						{
							// Find the remapped index.
							int index = ( ( p8[0] << 8 ) | p8[1] ) + p_mesh->mp_dl->m_array_base;
							int cindex = ( p8[offset+0] << 8 ) | p8[offset+1];

							for ( int lp = 0; lp < num_verts; lp++ )
							{
								if ( ( pos_index[lp] == index ) && ( col_index[lp] == cindex ) )
								{
									p8[offset+0] = ( lp >> 8 ) & 0xff;
									p8[offset+1] = lp & 0xff;
									if ( p_mesh->mp_dl->m_material.p_header->m_flags & (1<<6)/*m_correctable*/ )
									{
										p8[offset+2] = ( lp >> 8 ) & 0xff;
										p8[offset+3] = lp & 0xff;
									}
									break;
								}
							}
							p8 += stride;
						}
					}
					else
					{
						break;
					}
				}
				DCFlushRange( &p_mesh->mp_dl[1], p_mesh->mp_dl->m_size );
			}
//			p_mesh->mp_dl->m_array_base = 0;

//			Pcm::PCMAudio_Update();
			do_audio_update();
		}
	}
	else
	{
		p_new_col = m_mesh_array[0]->mp_dl->mp_col_pool;
		num_verts = m_mesh_array[0]->mp_dl->m_num_indices;
	}

	// Copy actual data over.
	for ( int v = 0; v < num_verts; v++ )
	{
		p_new_col[v] = ( p_colors[v].r << 24 ) | ( p_colors[v].g << 16 ) | ( p_colors[v].b << 8 ) | p_colors[v].a;
	}
	DCFlushRange( p_new_col, sizeof( uint32 ) * num_verts );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_scale(const Mth::Vector & scale)
{
	// Ensure w component is correct.

	if( mp_instance )
	{
//		mp_instance->GetTransform()->SetScale( scale );
	}
	else
	{
		int num = plat_get_num_render_verts();
		Mth::Vector * p_verts = new Mth::Vector[num];
		plat_get_render_verts( p_verts );
		for ( int lp = 0; lp < num; lp++ )
		{
			p_verts[lp][X] = ( ( p_verts[lp][X] - m_mod.offset[X] ) * scale[X] ) + m_mod.offset[X];
			p_verts[lp][Y] = ( ( p_verts[lp][Y] - m_mod.offset[Y] ) * scale[Y] ) + m_mod.offset[Y];
			p_verts[lp][Z] = ( ( p_verts[lp][Z] - m_mod.offset[Z] ) * scale[Z] ) + m_mod.offset[Z];
		}
		plat_set_render_verts( p_verts );
		delete p_verts;

//		// Go through and adjust the individual meshes.
//		for( uint32 i = 0; i < m_num_mesh; ++i )
//		{
//			NxNgc::sMesh *p_mesh = m_mesh_array[i];
//			p_mesh->mp_mod = &m_mod;
//
////			Mth::Vector delta_pos( pos[X] - m_offset[X], pos[Y] - m_offset[Y], pos[Z] - m_offset[Z], 1.0f );
////			p_mesh->mp_dl->m_sphere[X] += delta_pos[X];
////			p_mesh->mp_dl->m_sphere[Y] += delta_pos[Y];
////			p_mesh->mp_dl->m_sphere[Z] += delta_pos[Z];
////			p_mesh->SetPosition( proper_pos );
//		}
//
////		if ( mp_scene->GetEngineScene()->mp_scene_data )
////		{
////			Mth::Vector proper_pos( pos[X] - m_offset[X], pos[Y] - m_offset[Y], pos[Z] - m_offset[Z], 1.0f );
////	
////			float * pos = mp_scene->GetEngineScene()->mp_pos_pool;
////			for ( int lp = 0; lp += mp_scene->GetEngineScene()->mp_scene_data->m_num_pos; lp++ )
////			{
////				pos[0] += proper_pos[X];
////				pos[1] += proper_pos[Y];
////				pos[2] += proper_pos[Z];
////				pos += 3;
////			}
////			m_offset[X] = proper_pos[X];
////			m_offset[Y] = proper_pos[Y];
////			m_offset[Z] = proper_pos[Z];
////		}
	}
	m_mod.scale = scale;



//	Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" ));
//
//	m_scale = scale;
//
//	if( m_mesh_array )
//	{
//		NxNgc::sMesh *p_mesh = m_mesh_array[0];
//
//		// Scale each vertex.
//#ifdef SHORT_VERT
//		for( uint32 v = 0; v < p_mesh->m_num_vertex; ++v )
//		{
//			p_mesh->mp_posBuffer[(v*3)+0] = (s16)( (float)p_mesh->mp_posBuffer[(v*3)+0] * scale[X] );
//			p_mesh->mp_posBuffer[(v*3)+1] = (s16)( (float)p_mesh->mp_posBuffer[(v*3)+1] * scale[Y] );
//			p_mesh->mp_posBuffer[(v*3)+2] = (s16)( (float)p_mesh->mp_posBuffer[(v*3)+2] * scale[Z] );
//		}
//		DCFlushRange( p_mesh->mp_posBuffer, 3 * sizeof( s16 ) * p_mesh->m_num_vertex );
//#else
//		for( uint32 v = 0; v < p_mesh->m_num_vertex; ++v )
//		{
//			p_mesh->mp_posBuffer[(v*3)+0] = ( ( p_mesh->mp_posBuffer[(v*3)+0] - p_mesh->m_offset_x ) * scale[X] ) + p_mesh->m_offset_x;
//			p_mesh->mp_posBuffer[(v*3)+1] = ( ( p_mesh->mp_posBuffer[(v*3)+1] - p_mesh->m_offset_y ) * scale[Y] ) + p_mesh->m_offset_y;
//			p_mesh->mp_posBuffer[(v*3)+2] = ( ( p_mesh->mp_posBuffer[(v*3)+2] - p_mesh->m_offset_z ) * scale[Z] ) + p_mesh->m_offset_z;
//		}
//		DCFlushRange( p_mesh->mp_posBuffer, 3 * sizeof( float ) * p_mesh->m_num_vertex );
//#endif		// SHORT_VERT 
//	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Mth::Vector CNgcGeom::plat_get_scale() const
{
	return m_mod.scale;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_uv_wibble_params( float u_vel, float u_amp, float u_freq, float u_phase,
										   float v_vel, float v_amp, float v_freq, float v_phase )
{
//	if( m_mesh_array )
//	{
//		for( uint32 m = 0; m < m_num_mesh; ++m )
//		{
//			NxNgc::sMesh		*p_mesh	= m_mesh_array[m];
//			NxNgc::sMaterial	*p_mat	= p_mesh->mp_material;
//			if( p_mat )
//			{
//				// Find the first pass that wibbles.
//				for( uint32 p = 0; p < p_mat->m_passes; ++p )
//				{
//					if( p_mat->mp_UVWibbleParams[p] )
//					{
//						p_mat->mp_UVWibbleParams[p]->m_UVel			= u_vel;
//						p_mat->mp_UVWibbleParams[p]->m_VVel			= v_vel;
//						p_mat->mp_UVWibbleParams[p]->m_UAmplitude	= u_amp;
//						p_mat->mp_UVWibbleParams[p]->m_VAmplitude	= v_amp;
//						p_mat->mp_UVWibbleParams[p]->m_UFrequency	= u_freq;
//						p_mat->mp_UVWibbleParams[p]->m_VFrequency	= v_freq;
//						p_mat->mp_UVWibbleParams[p]->m_UPhase		= u_phase;
//						p_mat->mp_UVWibbleParams[p]->m_VPhase		= v_phase;
//						break;
//					}
//				}
//			}
//		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_use_explicit_uv_wibble( bool yes )
{
//	if( mp_instance && mp_instance->GetScene())
//	{
//		for( int m = 0; m < mp_instance->GetScene()->m_num_mesh_entries; ++m )
//		{
//			NxNgc::sMesh		*p_mesh	= mp_instance->GetScene()->m_meshes[m];
//			NxNgc::sMaterial	*p_mat	= p_mesh->mp_material;
//			if( p_mat )
//			{
//				for( uint32 p = 0; p < p_mat->m_passes; ++p )
//				{
//					if( p_mat->mp_UVWibbleParams[p] )
//					{
//						p_mat->m_flags[p] |= MATFLAG_EXPLICIT_UV_WIBBLE;
//					}
//				}
//			}
//		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcGeom::plat_set_uv_wibble_offsets( float u_offset, float v_offset )
{
//	if( m_mesh_array )
//	{
//		for( uint32 m = 0; m < m_num_mesh; ++m )
//		{
//			NxNgc::sMesh		*p_mesh	= m_mesh_array[m];
//			NxNgc::sMaterial	*p_mat	= p_mesh->mp_material;
//			if( p_mat )
//			{
//				// Find the first pass that wibbles.
//				for( uint32 p = 0; p < p_mat->m_passes; ++p )
//				{
//					if( p_mat->mp_UVWibbleParams[p] )
//					{
//						p_mat->mp_UVWibbleParams[p]->m_UVMatrix[2] = u_offset;
//						p_mat->mp_UVWibbleParams[p]->m_UVMatrix[3] = v_offset;
//						plat_use_explicit_uv_wibble( true );
//						break;
//					}
//				}
//			}
//		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcGeom::plat_set_uv_wibble_offsets( uint32 mat_name_checksum, int pass, float u_offset, float v_offset )
{
//	if( m_mesh_array )
//	{
//		for( uint32 m = 0; m < m_num_mesh; ++m )
//		{
//			NxNgc::sMesh		*p_mesh	= m_mesh_array[m];
//			NxNgc::sMaterial	*p_mat	= p_mesh->mp_material;
//			if( p_mat && ( p_mat->m_name_checksum == mat_name_checksum ))
//			{
//				// Find the first pass that wibbles.
////				for( uint32 p = 0; p < p_mat->m_passes; ++p )
////				{
////					if( p_mat->mp_UVWibbleParams[p] )
////					{
////						p_mat->mp_UVWibbleParams[p]->m_UWibbleOffset = u_offset;
////						p_mat->mp_UVWibbleParams[p]->m_VWibbleOffset = v_offset;
////						plat_use_explicit_uv_wibble( true );
////						break;
////					}
////				}
//			}
//		}
//	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcGeom::plat_set_uv_matrix( uint32 mat_name_checksum, int pass, const Mth::Matrix& mat )
{
	if( mp_instance && mp_instance->GetScene())
	{
		for( int m = 0; m < mp_instance->GetScene()->m_num_filled_meshes; ++m )
		{
			NxNgc::sMesh			*p_mesh	= mp_instance->GetScene()->mpp_mesh_list[m];
			NxNgc::sMaterialHeader	*p_mat	= p_mesh->mp_dl->m_material.p_header;
			if( p_mat )
			{
				bool	want_this_material	= false;
				int		adjusted_pass		= pass;

				// We are searching for materials with a matching name. However there is a caveat in that the
				// conversion process sometimes creates new materials for geometry flagged as 'render as separate', or for
				// geometry which is mapped with only certain passes of a multipass material (or both cases).
				// In such a case, the new material name checksum will differ from the original material name checksum,
				// with the following bits having significance (i.e. consider bitflags = new_namechecksum - old_namechecksum ):
				//
				// Bits 3->6	Pass flags indicating which passes of the original material this material uses
				// Bits 0->2	Absolute number ('render as separate' flagged geometry) indicating which single pass of the material this material represents.
				if( p_mat->m_materialNameChecksum == mat_name_checksum )
				{
					want_this_material = true;
				}
				else if(( p_mat->m_materialNameChecksum > mat_name_checksum ) && (( p_mat->m_materialNameChecksum - mat_name_checksum ) <= 0x7F ))
				{
					uint32 checksum_diff		= p_mat->m_materialNameChecksum - mat_name_checksum;
					int render_separate_pass	= checksum_diff & 0x07;
					uint32 pass_flags			= checksum_diff >> 3;

					if( render_separate_pass )
					{
						if( render_separate_pass == ( pass + 1 ))
						{
							want_this_material = true;

							// Readjust the pass to zero, since this material was formed as a single pass of a multipass material.
							adjusted_pass = 0;
						}
					}
					else if( pass_flags )
					{
						// This material was created during scene conversion from another material with more passes.
						if( pass_flags & ( 1 << pass ))
						{
							want_this_material = true;
							for( int p = 0; p < pass; ++p )
							{
								if(( pass_flags & ( 1 << p )) == 0 )
								{
									// Readjust the pass down by 1, since this material was created as a subset of another material.
									--adjusted_pass;
								}
							}
						}
					}
				}

				if( want_this_material )
				{
					if ( adjusted_pass < p_mat->m_passes )
					{
						// Create the wibble params if they don't exist already.
						NxNgc::sMaterialPassHeader * p_pass = &mp_instance->GetScene()->mp_material_pass[p_mat->m_pass_item+adjusted_pass];

						// Force this to be used.
						p_pass->m_flags |= (1<<2);
						p_pass->m_uv_enabled = 1;
						p_mat->m_flags |= (1<<4);
	//
	//
	//
	////					// Set the matrix values.
	//					p_pass->mp_explicit_wibble->m_matrix[0][0] = mat[0][0];
	//					p_pass->mp_explicit_wibble->m_matrix[0][1] = mat[0][1];
	//					p_pass->mp_explicit_wibble->m_matrix[1][0] = mat[1][0];
	//					p_pass->mp_explicit_wibble->m_matrix[1][1] = mat[1][1];
	//					p_pass->mp_explicit_wibble->m_matrix[2][0] = 1.0f;
	//					p_pass->mp_explicit_wibble->m_matrix[2][1] = 1.0f;
	//					p_pass->mp_explicit_wibble->m_matrix[0][3] = mat[3][0];
	//					p_pass->mp_explicit_wibble->m_matrix[1][3] = mat[3][1];

						p_pass->m_uv_mat[0] = (short)(mat[0][0] * ((float)(1<<12)));
						p_pass->m_uv_mat[1] = (short)(mat[0][1] * ((float)(1<<12)));
						p_pass->m_uv_mat[2] = (short)(mat[3][0] * ((float)(1<<12)));
						p_pass->m_uv_mat[3] = (short)(mat[3][1] * ((float)(1<<12)));
					}
				}
			}
		}
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcGeom::plat_allocate_uv_matrix_params( uint32 mat_name_checksum, int pass )
{
	if( mp_instance && mp_instance->GetScene())
	{
		for( int m = 0; m < mp_instance->GetScene()->m_num_filled_meshes; ++m )
		{
			NxNgc::sMesh			*p_mesh	= mp_instance->GetScene()->mpp_mesh_list[m];
			NxNgc::sMaterialHeader	*p_mat	= p_mesh->mp_dl->m_material.p_header;
			if( p_mat )
			{
				bool	want_this_material	= false;
				int		adjusted_pass		= pass;

				// We are searching for materials with a matching name. However there is a caveat in that the
				// conversion process sometimes creates new materials for geometry flagged as 'render as separate', or for
				// geometry which is mapped with only certain passes of a multipass material (or both cases).
				// In such a case, the new material name checksum will differ from the original material name checksum,
				// with the following bits having significance (i.e. consider bitflags = new_namechecksum - old_namechecksum ):
				//
				// Bits 3->6	Pass flags indicating which passes of the original material this material uses
				// Bits 0->2	Absolute number ('render as separate' flagged geometry) indicating which single pass of the material this material represents.
				if( p_mat->m_materialNameChecksum == mat_name_checksum )
					want_this_material = true;
				else if(( p_mat->m_materialNameChecksum > mat_name_checksum ) && (( p_mat->m_materialNameChecksum - mat_name_checksum ) <= 0x7F ))
				{
					uint32 checksum_diff		= p_mat->m_materialNameChecksum - mat_name_checksum;
					int render_separate_pass	= checksum_diff & 0x07;
					uint32 pass_flags			= checksum_diff >> 3;

					if( render_separate_pass )
					{
						if( render_separate_pass == ( pass + 1 ))
						{
							want_this_material = true;

							// Readjust the pass to zero, since this material was formed as a single pass of a multipass material.
							adjusted_pass = 0;
						}
					}
					else if( pass_flags )
					{
						// This material was created during scene conversion from another material with more passes.
						if( pass_flags & ( 1 << pass ))
						{
							want_this_material = true;
							for( int p = 0; p < pass; ++p )
							{
								if(( pass_flags & ( 1 << p )) == 0 )
								{
									// Readjust the pass down by 1, since this material was created as a subset of another material.
									--adjusted_pass;
								}
							}
						}
					}
				}

				if( want_this_material )
				{
					if ( adjusted_pass < p_mat->m_passes )
					{
						NxNgc::sMaterialPassHeader * p_pass = &mp_instance->GetScene()->mp_material_pass[p_mat->m_pass_item+adjusted_pass];
	
						p_pass->m_flags |= (1<<2);
						p_pass->m_uv_enabled = 1;
						p_pass->m_uv_mat[0] = (1<<12);
						p_pass->m_uv_mat[1] = 0;
						p_pass->m_uv_mat[2] = 0;
						p_pass->m_uv_mat[3] = 0;
					}

//					if ( p_pass->mp_explicit_wibble == NULL )
//					{
//						p_pass->mp_explicit_wibble = new NxNgc::sExplicitMaterialUVWibble;
//						p_pass->mp_explicit_wibble->m_matrix[0][0] = 1.0f;
//						p_pass->mp_explicit_wibble->m_matrix[0][1] = 0.0f;
//						p_pass->mp_explicit_wibble->m_matrix[0][2] = 1.0f;
//						p_pass->mp_explicit_wibble->m_matrix[0][3] = 0.0f;
//						p_pass->mp_explicit_wibble->m_matrix[1][0] = 0.0f;
//						p_pass->mp_explicit_wibble->m_matrix[1][1] = 1.0f;
//						p_pass->mp_explicit_wibble->m_matrix[1][2] = 1.0f;
//						p_pass->mp_explicit_wibble->m_matrix[1][3] = 0.0f;
//						p_pass->m_flags |= (1<<2);
//						return true;
//					}
//					// Create the wibble params if they don't exist already.
//					if( p_mat->mp_UVWibbleParams[pass] == NULL )
//					{
//						p_mat->mp_UVWibbleParams[pass]	= new NxNgc::sUVWibbleParams;
//
//						// Need to set flags to indicate that uv wibble is now in effect.
//						p_mat->m_uv_wibble				= true;
//						p_mat->m_flags[pass]		   |= MATFLAG_UV_WIBBLE | MATFLAG_EXPLICIT_UV_WIBBLE;
//						return true;
//					}
				}
			}
		}
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#if 0
	if( mp_instance && mp_instance->GetScene())
	{
		for( int m = 0; m < mp_instance->GetScene()->m_num_filled_meshes; ++m )
		{
			NxNgc::sMesh			*p_mesh	= mp_instance->GetScene()->mpp_mesh_list[m];
			NxNgc::sMaterialHeader	*p_mat	= p_mesh->mp_dl->m_material.p_header;
			if( p_mat )
			{
				bool	want_this_material	= false;

				// We are searching for materials with a matching name. However there is one caveat in that the
				// conversion process sometimes creates new materials for geometry flagged as 'render as separate.
				// In such a case, the new material name checksum = original name checksum + pass + 1.
				if( p_mat->m_materialNameChecksum == mat_name_checksum )
					want_this_material = true;
				else if( p_mat->m_materialNameChecksum == ( mat_name_checksum + pass + 1 ) )
				{
					want_this_material = true;

					pass = 0;
				}

				if( want_this_material )
				{
//					// Create the wibble params if they don't exist already.
//					if( p_mat->mp_UVWibbleParams[pass] == NULL )
//					{
//						p_mat->mp_UVWibbleParams[pass]	= new NxNgc::sUVWibbleParams;
//
//						// Need to set flags to indicate that uv wibble is now in effect.
//						p_mat->m_uv_wibble				= true;
//						p_mat->m_flags[pass]		   |= MATFLAG_UV_WIBBLE | MATFLAG_EXPLICIT_UV_WIBBLE;
//						return true;
//					}
				}
			}
		}
	}
#endif
} // Nx
				



================================================
FILE: Code/Gfx/NGC/p_NxGeom.h
================================================
	//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxGeom.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  3/5/2002
//****************************************************************************

#ifndef	__GFX_P_NX_GEOM_H__
#define	__GFX_P_NX_GEOM_H__
    
#include "gfx/nxgeom.h"
#include "gfx/Ngc/p_nxsector.h"
#include "gfx/Ngc/p_nxmesh.h"

namespace Mth
{
	class Matrix;
}

namespace NxNgc
{
	class CInstance;
}
						   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CGeom
class CNgcGeom : public CGeom
{
                                      
public:
								CNgcGeom();
	virtual 					~CNgcGeom();
	void						SetInstance( NxNgc::CInstance *p_instance )		{ mp_instance = p_instance; }
	NxNgc::CInstance			*GetInstance( void )								{ return mp_instance; }
	void						InitMeshList();
	void						ClearMeshList();
	void						AddMesh( NxNgc::sMesh * );
	Lst::Head< NxNgc::sMesh >	*GetMeshList();

	void						CreateMeshArray();
	bool						RegisterMeshArray( bool just_count );
	void						DestroyMeshArray( void );

	const Mth::CBBox &			GetBoundingBox( void )			{ return m_bbox; }
	void						SetScene( CNgcScene *p_scene )	{ mp_scene = p_scene; }
	NxNgc::sScene				*GenerateScene( void );

	Mth::Vector *				GetOffset( void ) { return &m_mod.offset; }
	Mth::Vector *				GetScale( void ) { return &m_mod.scale; }

private:						// It's all private, as it is machine specific
	virtual	bool					plat_load_geom_data( CMesh *pMesh, CModel *pModel, bool color_per_material );
	virtual	void					plat_set_active( bool active );
	virtual bool					plat_is_active( void ) const;
	const Mth::CBBox &				plat_get_bounding_box( void ) const;
	virtual void					plat_set_bounding_sphere( const Mth::Vector& boundingSphere );
	virtual const Mth::Vector		plat_get_bounding_sphere( void ) const;
	virtual void					plat_set_world_position( const Mth::Vector& pos );
	virtual const Mth::Vector &		plat_get_world_position() const;
	virtual bool					plat_render( Mth::Matrix* pRootMatrix, Mth::Matrix* ppBoneMatrices, int numBones );
	virtual void					plat_set_bone_matrix_data( Mth::Matrix* pBoneMatrices, int numBones );
	virtual bool					plat_hide_polys( uint32 mask );
	virtual uint32					plat_get_visibility( void ) const;
	virtual	void					plat_set_visibility( uint32 mask );
	virtual void					plat_set_color( Image::RGBA rgba );
	virtual void					plat_clear_color( void );
	virtual Image::RGBA				plat_get_color( void ) const;
	virtual CNgcGeom				*CNgcGeom::plat_clone( bool instance, CScene *p_dest_scene = NULL );
	virtual CNgcGeom				*CNgcGeom::plat_clone( bool instance, CModel* pDestModel );
	virtual void					plat_set_orientation(const Mth::Matrix& orient);
	virtual void 					plat_rotate_y(Mth::ERot90 rot);
	virtual void					plat_set_transform(const Mth::Matrix& transform);
	virtual const 					Mth::Matrix &	plat_get_transform() const;
	virtual	int						plat_get_num_render_verts( void );
	virtual	void					plat_get_render_verts( Mth::Vector *p_verts );
	virtual	void					plat_get_render_colors( Image::RGBA *p_colors );
	virtual void					plat_set_render_verts( Mth::Vector *p_verts );
	virtual	void					plat_set_render_colors( Image::RGBA *p_colors );
	virtual void					plat_set_model_lights( CModelLights* p_model_lights );
	virtual void					plat_set_scale(const Mth::Vector& scale);
	virtual Mth::Vector 			plat_get_scale() const;
	virtual bool					plat_set_material_color( uint32 mat_name_checksum, int pass, Image::RGBA rgba );
	virtual void					plat_set_uv_wibble_params( float u_vel, float u_amp, float u_freq, float u_phase, float v_vel, float v_amp, float v_freq, float v_phase );
	virtual void					plat_use_explicit_uv_wibble( bool yes );
	virtual void					plat_set_uv_wibble_offsets( float u_offset, float v_offset );
	virtual bool					plat_set_uv_wibble_offsets( uint32 mat_name_checksum, int pass, float u_offset, float v_offset );
	virtual bool					plat_set_uv_matrix( uint32 mat_name_checksum, int pass, const Mth::Matrix& mat );
	virtual bool					plat_allocate_uv_matrix_params( uint32 mat_checksum, int pass );

public:
	Mth::CBBox						m_bbox;	
	
	CNgcScene *					mp_scene;
	Lst::Head< NxNgc::sMesh >		*mp_init_mesh_list;   
	NxNgc::sMesh **				m_mesh_array;
	uint							m_num_mesh;
	uint32							m_visible;
	bool							m_active;

private:
	NxNgc::CInstance				*mp_instance;
	CNgcMesh						*mp_mesh;		// Used for obtaining CAS poly removal data.
	NxNgc::sMeshMod					m_mod;

	int								build_index_map( int * p_pos_index, int * p_col_index );
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx

#endif 



================================================
FILE: Code/Gfx/NGC/p_NxImposter.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxImposter.cpp

#include 	"gfx/ngc/p_NxGeom.h"
#include 	"gfx/ngc/p_NxImposter.h"

namespace Nx
{
	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CImposterGroup* CImposterManager::plat_create_imposter_group( void )
{
	return new CNgcImposterGroup;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::plat_pre_render_imposters( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::plat_post_render_imposters( void )
{
}



/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CImposterGroup

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcImposterGroup::CNgcImposterGroup()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcImposterGroup::~CNgcImposterGroup()
{
}



} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/NGC/p_NxImposter.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxFont.h

#ifndef	__GFX_P_NX_IMPOSTER_H__
#define	__GFX_P_NX_IMPOSTER_H__

#include 	"gfx/NxImposter.h"

namespace Nx
{
	
/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CImposterGroup
class CNgcImposterGroup : public CImposterGroup
{
	public:
								CNgcImposterGroup();
	virtual						~CNgcImposterGroup();


	private:					// It's all private, as it is machine specific

	// Machine specific members
};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/NGC/p_NxLightMan.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxLightMan.cpp - Ngc platform specific interface to CLightManager

#include 
#include "gfx/NxLightMan.h"
#include "gfx/Ngc/p_NxLight.h"
#include "gfx/Ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

namespace Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::s_plat_update_engine( void )
{
	int difr = (int)((float)s_world_lights.m_light_ambient_rgba.r * s_ambient_brightness);
	int difg = (int)((float)s_world_lights.m_light_ambient_rgba.g * s_ambient_brightness);
	int difb = (int)((float)s_world_lights.m_light_ambient_rgba.b * s_ambient_brightness);
	NxNgc::EngineGlobals.ambient_light_color.r = difr > 255 ? 255 : (u8)difr;  
	NxNgc::EngineGlobals.ambient_light_color.g = difg > 255 ? 255 : (u8)difg;  
	NxNgc::EngineGlobals.ambient_light_color.b = difb > 255 ? 255 : (u8)difb;  

//	NxNgc::EngineGlobals.ambient_light_color.r = NxNgc::EngineGlobals.ambient_light_color.r < 128 ? NxNgc::EngineGlobals.ambient_light_color.r * 2 : 255;
//	NxNgc::EngineGlobals.ambient_light_color.g = NxNgc::EngineGlobals.ambient_light_color.g < 128 ? NxNgc::EngineGlobals.ambient_light_color.g * 2 : 255;
//	NxNgc::EngineGlobals.ambient_light_color.b = NxNgc::EngineGlobals.ambient_light_color.b < 128 ? NxNgc::EngineGlobals.ambient_light_color.b * 2 : 255;

	for( int i = 0; i < 2; ++i )
	{
		Image::RGBA	dif = CLightManager::sGetLightDiffuseColor( i );
		int difr = (int)((float)dif.r * s_diffuse_brightness[i]);
		int difg = (int)((float)dif.g * s_diffuse_brightness[i]);
		int difb = (int)((float)dif.b * s_diffuse_brightness[i]);
		NxNgc::EngineGlobals.diffuse_light_color[i].r = difr > 255 ? 255 : (u8)difr;
		NxNgc::EngineGlobals.diffuse_light_color[i].g = difg > 255 ? 255 : (u8)difg;
		NxNgc::EngineGlobals.diffuse_light_color[i].b = difb > 255 ? 255 : (u8)difb;

//		NxNgc::EngineGlobals.diffuse_light_color[i].r = NxNgc::EngineGlobals.diffuse_light_color[i].r < 128 ? NxNgc::EngineGlobals.diffuse_light_color[i].r * 2 : 255;
//		NxNgc::EngineGlobals.diffuse_light_color[i].g = NxNgc::EngineGlobals.diffuse_light_color[i].g < 128 ? NxNgc::EngineGlobals.diffuse_light_color[i].g * 2 : 255;
//		NxNgc::EngineGlobals.diffuse_light_color[i].b = NxNgc::EngineGlobals.diffuse_light_color[i].b < 128 ? NxNgc::EngineGlobals.diffuse_light_color[i].b * 2 : 255;

		Mth::Vector dir = CLightManager::sGetLightDirection( i );
		dir.Normalize();
		NxNgc::EngineGlobals.light_x[i] = dir[X] * 200000.0f;
		NxNgc::EngineGlobals.light_y[i] = dir[Y] * 200000.0f;
		NxNgc::EngineGlobals.light_z[i] = dir[Z] * 200000.0f;
	}
	// Set lighting.
//	GXLightObj light_obj[2];
//
////	GX::SetChanAmbColor( GX_COLOR0A0, NxNgc::EngineGlobals.ambient_light_color );
//
////	GXInitLightColor( &light_obj[0], NxNgc::EngineGlobals.diffuse_light_color[0] );
////	GXInitLightPos( &light_obj[0], NxNgc::EngineGlobals.light_x[0], NxNgc::EngineGlobals.light_y[0], NxNgc::EngineGlobals.light_z[0] );
////	GXLoadLightObjImm( &light_obj[0], GX_LIGHT0 );
////	GXInitSpecularDir( &light_obj[2], -NxNgc::EngineGlobals.light_x[0] / 200000.0f, -NxNgc::EngineGlobals.light_y[0] / 200000.0f, -NxNgc::EngineGlobals.light_z[0] / 200000.0f );
////	GXInitLightShininess( &light_obj[2], 5.0f );
////
////	GXInitLightColor( &light_obj[1], NxNgc::EngineGlobals.diffuse_light_color[1] );
////	GXInitLightPos( &light_obj[1], NxNgc::EngineGlobals.light_x[1], NxNgc::EngineGlobals.light_y[1], NxNgc::EngineGlobals.light_z[1] );
////	GXLoadLightObjImm( &light_obj[1], GX_LIGHT1 );
////	GXInitSpecularDir( &light_obj[2], -NxNgc::EngineGlobals.light_x[1] / 200000.0f, -NxNgc::EngineGlobals.light_y[1] / 200000.0f, -NxNgc::EngineGlobals.light_z[1] / 200000.0f );
////	GXInitLightShininess( &light_obj[2], 5.0f );
//
//    Vec  ldir;
//    f32  theta, phi;
//
//    // Light position
//    theta = (f32)90 * Mth::PI / 180.0F;
//    phi   = (f32)0   * Mth::PI / 180.0F;
//    ldir.x = - 1.0F * cosf(phi) * sinf(theta);
//    ldir.y = - 1.0F * sinf(phi);
//    ldir.z = - 1.0F * cosf(phi) * cosf(theta);
//
//    // Convert light position into view space
//    Vec  dir0;
//    Vec  dir1;
//    MTXMultVecSR(NxNgc::EngineGlobals.current_uploaded, &ldir, &dir0);
//    MTXMultVecSR(NxNgc::EngineGlobals.current_uploaded, &ldir, &dir1);
//
//////	GXInitSpecularDir( &light_obj[0], NxNgc::EngineGlobals.light_x[0] / 200000.0f, NxNgc::EngineGlobals.light_y[0] / 200000.0f, NxNgc::EngineGlobals.light_z[0] / 200000.0f );
////	GXInitSpecularDirv( &light_obj[0], &dir0 );
////	GXInitLightShininess( &light_obj[0], 16.0f );
////	GXInitLightColor( &light_obj[0], (GXColor){0,0,0,0}/*NxNgc::EngineGlobals.diffuse_light_color[0]*/ );
////	GXLoadLightObjImm( &light_obj[0], GX_LIGHT0 );
//
////	GXInitSpecularDir( &light_obj[1], NxNgc::EngineGlobals.light_x[1] / 200000.0f, NxNgc::EngineGlobals.light_y[1] / 200000.0f, NxNgc::EngineGlobals.light_z[1] / 200000.0f );
//	GXInitSpecularDirv( &light_obj[0], &dir0 );
//	GXInitLightShininess( &light_obj[0], 5.0f );
//	GXInitLightColor( &light_obj[0], NxNgc::EngineGlobals.diffuse_light_color[1] );
//	GXLoadLightObjImm( &light_obj[0], GX_LIGHT0 );
//
//	GXSetChanAmbColor( GX_COLOR0A0, (GXColor){0,0,0,255} );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::s_plat_update_lights( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CLightManager::s_plat_set_light_ambient_color()
{
	int difr = (int)((float)s_world_lights.m_light_ambient_rgba.r * s_ambient_brightness);
	int difg = (int)((float)s_world_lights.m_light_ambient_rgba.g * s_ambient_brightness);
	int difb = (int)((float)s_world_lights.m_light_ambient_rgba.b * s_ambient_brightness);
	NxNgc::EngineGlobals.ambient_light_color.r = difr > 255 ? 255 : (u8)difr;
	NxNgc::EngineGlobals.ambient_light_color.g = difg > 255 ? 255 : (u8)difg;
	NxNgc::EngineGlobals.ambient_light_color.b = difb > 255 ? 255 : (u8)difb;
	
	NxNgc::EngineGlobals.ambient_light_color.r = NxNgc::EngineGlobals.ambient_light_color.r < 128 ? NxNgc::EngineGlobals.ambient_light_color.r * 2 : 255;
	NxNgc::EngineGlobals.ambient_light_color.g = NxNgc::EngineGlobals.ambient_light_color.g < 128 ? NxNgc::EngineGlobals.ambient_light_color.g * 2 : 255;
	NxNgc::EngineGlobals.ambient_light_color.b = NxNgc::EngineGlobals.ambient_light_color.b < 128 ? NxNgc::EngineGlobals.ambient_light_color.b * 2 : 255;
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA	CLightManager::s_plat_get_light_ambient_color()
{
	return s_world_lights.m_light_ambient_rgba;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CLightManager::s_plat_set_light_direction( int light_index )
{
	NxNgc::EngineGlobals.light_x[light_index] = -s_world_lights.m_light_direction[light_index][X];
	NxNgc::EngineGlobals.light_y[light_index] = -s_world_lights.m_light_direction[light_index][Y];
	NxNgc::EngineGlobals.light_z[light_index] = -s_world_lights.m_light_direction[light_index][Z];

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector & CLightManager::s_plat_get_light_direction( int light_index )
{
	static Mth::Vector dir;
	dir.Set( s_world_lights.m_light_direction[light_index][X], s_world_lights.m_light_direction[light_index][Y], s_world_lights.m_light_direction[light_index][Z] );
	return dir;
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CLightManager::s_plat_set_light_diffuse_color( int light_index )
{
	NxNgc::EngineGlobals.diffuse_light_color[light_index].r = (u8)((float)s_world_lights.m_light_diffuse_rgba[light_index].r * s_diffuse_brightness[light_index]);
	NxNgc::EngineGlobals.diffuse_light_color[light_index].g = (u8)((float)s_world_lights.m_light_diffuse_rgba[light_index].g * s_diffuse_brightness[light_index]);
	NxNgc::EngineGlobals.diffuse_light_color[light_index].b = (u8)((float)s_world_lights.m_light_diffuse_rgba[light_index].b * s_diffuse_brightness[light_index]);
	
	NxNgc::EngineGlobals.diffuse_light_color[light_index].r = NxNgc::EngineGlobals.diffuse_light_color[light_index].r < 128 ? NxNgc::EngineGlobals.diffuse_light_color[light_index].r * 2 : 255;
	NxNgc::EngineGlobals.diffuse_light_color[light_index].g = NxNgc::EngineGlobals.diffuse_light_color[light_index].g < 128 ? NxNgc::EngineGlobals.diffuse_light_color[light_index].g * 2 : 255;
	NxNgc::EngineGlobals.diffuse_light_color[light_index].b = NxNgc::EngineGlobals.diffuse_light_color[light_index].b < 128 ? NxNgc::EngineGlobals.diffuse_light_color[light_index].b * 2 : 255;

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA	CLightManager::s_plat_get_light_diffuse_color( int light_index )
{
	return s_world_lights.m_light_diffuse_rgba[light_index];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::s_plat_update_colors( void )
{
	s_plat_set_light_ambient_color();
	for( int i = 0; i < MAX_LIGHTS; ++i )
	{
		s_plat_set_light_diffuse_color( i );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CModelLights *CLightManager::s_plat_create_model_lights()
{
	return new CNgcModelLights;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CLightManager::s_plat_free_model_lights( CModelLights *p_model_lights )
{
	Dbg_Assert( p_model_lights );

	delete p_model_lights;

	return true;
}



} 
 



================================================
FILE: Code/Gfx/NGC/p_NxMesh.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxMesh.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/15/2002
//****************************************************************************

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//extern Nx::CNgcScene *p_skater;

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int cmp( const void *p1, const void *p2 )
{
	NxNgc::sCASData *p_casdata1 = (NxNgc::sCASData*)p1;
	NxNgc::sCASData *p_casdata2 = (NxNgc::sCASData*)p2;
	
	uint32 mesh1 = p_casdata1->data0 >> 16;
	uint32 mesh2 = p_casdata2->data0 >> 16;
	
	if( mesh1 > mesh2 )
	{
		return 1;
	}
	else if( mesh1 < mesh2 )
	{
		return -1;
	}
	else
	{
		uint32 indexzero1 = p_casdata1->data0 & 0xFFFF;
		uint32 indexzero2 = p_casdata2->data0 & 0xFFFF;
		if( indexzero1 > indexzero2 )
		{
			return 1;
		}
		else if( indexzero1 < indexzero2 )
		{
			return -1;
		}
	}
	return 0;
}


	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcMesh::build_casdata_table( const char* pFileName )
{
	void *pFile = File::Open( pFileName, "rb" );
	Dbg_MsgAssert( pFile, ( "Couldn't open CAS data file %s\n", pFileName ));

	uint32 version;
	File::Read( &version, sizeof( uint32 ), 1, pFile );

	if( version >= 2 )
	{
		File::Read( &m_CASRemovalMask, sizeof( uint32 ), 1, pFile );
	}
	
	File::Read( &m_numCASData, sizeof( int ), 1, pFile );

	if( m_numCASData > 0 )
	{
		// CAS flags.
		mp_CASData = new NxNgc::sCASData[m_numCASData];
		File::Read( &mp_CASData[0].mask,	sizeof( uint32 ) * 3 * m_numCASData, 1, pFile );
	
		// Sort the CAS data based first on mesh, then on the first tri index, lowest to highest. This allows some efficient early-out checking
		// during the poly removal.
		qsort( mp_CASData, m_numCASData, sizeof( NxNgc::sCASData ), cmp );
	}

	File::Close( pFile );
	return ( m_numCASData > 0 );
}

#define MemoryRead( dst, size, num, src )	memcpy(( dst ), ( src ), (( num ) * ( size )));	\
											( src ) += (( num ) * ( size ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcMesh::build_casdata_table_from_memory( void **pp_mem )
{
	uint8 *p_data = (uint8*)( *pp_mem );

	uint32 version;
	MemoryRead( &version, sizeof( uint32 ), 1, p_data );

	if( version >= 2 )
	{
		MemoryRead( &m_CASRemovalMask, sizeof( uint32 ), 1, p_data );
	}
	
	MemoryRead( &m_numCASData, sizeof( int ), 1, p_data );

	if( m_numCASData > 0 )
	{
		// CAS flags.
		mp_CASData = new NxNgc::sCASData[m_numCASData];

		for( uint32 i = 0; i < m_numCASData; ++i )
		{
			MemoryRead( &mp_CASData[i].mask, sizeof( uint32 ) * 3, 1, p_data );
		}

		// Sort the CAS data based first on mesh, then on the first tri index, lowest to highest. This allows some efficient early-out checking
		// during the poly removal.
		qsort( mp_CASData, m_numCASData, sizeof( NxNgc::sCASData ), cmp );
	}
	
	// Set the data pointer to the new position on return.
	*pp_mem = p_data;

	return ( m_numCASData > 0 );
}


/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcMesh::CNgcMesh( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcMesh::CNgcMesh( const char *pMeshFileName )
{
	// Only do the CAS flag building for skinned objects.
	if( strstr( pMeshFileName, "skin" ))
	{
		char CASFileName[256];
		strcpy( CASFileName, pMeshFileName );
		Str::LowerCase( CASFileName );
		char *pExt = strstr( CASFileName, "skin.ngc" );
		if( pExt )
		{
			Dbg_MsgAssert( pExt, ( "Couldn't find skin.ngc extension in %s", CASFileName ));
			strcpy( pExt, "cas.ngc" );
			build_casdata_table( CASFileName );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcMesh::~CNgcMesh( void )
{
	// unload textures
//	NxNgc::DeleteTextures( mp_scene );

	if( mp_CASData )
	{
		delete [] mp_CASData;
	}
	
	if( mp_scene )
	{
		CEngine::sUnloadScene( mp_scene );
//		NxNgc::DeleteScene( mp_engine_scene );
	}
}


void CNgcMesh::SetScene( CNgcScene *p_scene )
{
	mp_scene = p_scene;
	// Copy the hierarchy info from the scene so that above the p-line stuff can access it.
	mp_hierarchyObjects = mp_scene->GetEngineScene()->mp_hierarchyObjects;
	m_numHierarchyObjects = mp_scene->GetEngineScene()->m_numHierarchyObjects;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcMesh::SetCASData( uint8 *p_cas_data )
{
	if( p_cas_data )
	{
		build_casdata_table_from_memory((void**)&p_cas_data );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx
				



================================================
FILE: Code/Gfx/NGC/p_NxMesh.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxMesh.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/15/2002
//****************************************************************************

#ifndef	__GFX_P_NX_MESH_H__
#define	__GFX_P_NX_MESH_H__
    
#include "gfx/nxmesh.h"
#include "p_nxscene.h"

namespace NxNgc
{
	struct sScene;
}
			 
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CMesh
    
class CNgcMesh : public CMesh
{
                                      
public:
						CNgcMesh( void );
						CNgcMesh( const char *pMeshFileName );
	virtual 			~CNgcMesh();
	void				SetScene( CNgcScene *p_scene );

	void				SetTexDict( Nx::CTexDict *p_tex_dict )	{ mp_texDict = p_tex_dict; }
	void				SetCASData( uint8 *p_cas_data );
	CNgcScene			*GetScene( void )						{ return mp_scene; }

	NxNgc::sCASData		*GetCASData( void )						{ return mp_CASData; }
	uint32				GetNumCASData( void )					{ return m_numCASData; }

protected:
	bool				build_casdata_table(const char* pFileName);
	bool				build_casdata_table_from_memory( void **pp_mem );
	
	NxNgc::sCASData		*mp_CASData;
	uint32				m_numCASData;

private:
	CNgcScene			*mp_scene;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx

#endif 



================================================
FILE: Code/Gfx/NGC/p_NxSprite.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxSprite.cpp

#include 	"Gfx/Ngc/p_NxSprite.h"
#include 	"Gfx/Ngc/p_NxTexture.h"

namespace Nx
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CSprite

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcSprite::CNgcSprite()
{
	mp_plat_sprite = new NxNgc::sSprite();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcSprite::~CNgcSprite()
{
	delete mp_plat_sprite;
}

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcSprite::plat_initialize()
{
	plat_update_engine();
	plat_update_priority();
	plat_update_hidden();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcSprite::plat_update_hidden()
{
	// Take sprite on or off draw list
	mp_plat_sprite->SetHidden( m_hidden );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcSprite::plat_update_engine()
{
	CNgcTexture *p_Ngc_texture = static_cast( mp_texture );

	// Rebuild sprite primitives

	if ( p_Ngc_texture )
	{
		mp_plat_sprite->mp_texture	= p_Ngc_texture->GetEngineTexture();
	}
	else
	{
		mp_plat_sprite->mp_texture	= NULL;
	}

	mp_plat_sprite->m_xpos		= m_pos_x;
	mp_plat_sprite->m_ypos		= m_pos_y;
	mp_plat_sprite->m_width		= m_width;
	mp_plat_sprite->m_height	= m_height;
	mp_plat_sprite->m_scale_x	= m_scale_x;
	mp_plat_sprite->m_scale_y	= m_scale_y;

	mp_plat_sprite->m_xhot		= (( m_anchor_x + 1.0f ) * 0.5f) * ( m_width );
	mp_plat_sprite->m_yhot		= (( m_anchor_y + 1.0f ) * 0.5f) * ( m_height );

	mp_plat_sprite->m_rot		= m_rotation;
	mp_plat_sprite->m_rgba		= *((uint32 *) &m_rgba);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcSprite::plat_update_priority()
{
	if( mp_plat_sprite )
	{
		mp_plat_sprite->SetPriority( m_priority );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSprite::plat_enable_constant_z_value(bool enable)
{
	//NxPs2::SSprite::EnableConstantZValue(enable);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSprite::plat_set_constant_z_value(Nx::ZBufferValue z)
{
	//NxPs2::SSprite::SetConstantZValue(z);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::ZBufferValue	CSprite::plat_get_constant_z_value()
{
	//return NxPs2::SSprite::GetConstantZValue();
	return 0;
}


} // Namespace Nx  			
				
				



================================================
FILE: Code/Gfx/NGC/p_NxSprite.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.h

#ifndef	__GFX_P_NX_SPRITE_H__
#define	__GFX_P_NX_SPRITE_H__

#include 	"Gfx/NxSprite.h"
#include 	"Gfx/Ngc/NX/texture.h"
#include 	"Gfx/Ngc/NX/sprite.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Machine specific implementation of the CSprite
class	CNgcSprite : public CSprite
{
public:
								CNgcSprite();
	virtual						~CNgcSprite();

private:		// It's all private, as it is machine specific
	virtual void				plat_initialize();

	virtual void				plat_update_hidden();		// Tell engine of update
	virtual void				plat_update_engine();		// Update engine primitives
	virtual void				plat_update_priority();

	NxNgc::sSprite *			mp_plat_sprite;
};

} // Namespace Nx  			

#endif



================================================
FILE: Code/Gfx/NGC/p_NxTextured3dPoly.cpp
================================================
#include 	"Gfx/ngc/p_NxTextured3dPoly.h"

namespace NxNgc
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CTextured3dPoly

CNgcTextured3dPoly::CNgcTextured3dPoly()
{
}

CNgcTextured3dPoly::~CNgcTextured3dPoly()
{
}

void CNgcTextured3dPoly::plat_set_texture(uint32 texture_checksum)
{
}	

void CNgcTextured3dPoly::plat_render()
{
}

} // Namespace NxNgc  			
				
				


================================================
FILE: Code/Gfx/NGC/p_NxTextured3dPoly.h
================================================
#ifndef	__GFX_P_NX_TEXTURED_3D_POLY_H__
#define	__GFX_P_NX_TEXTURED_3D_POLY_H__

#include 	

namespace NxNgc
{

// Machine specific implementation of CTextured3dPoly
class	CNgcTextured3dPoly : public Nx::CTextured3dPoly
{
public:
		   					CNgcTextured3dPoly();
	virtual					~CNgcTextured3dPoly();
private:
	void					plat_render();
	void					plat_set_texture(uint32 texture_checksum);
};

}	// namespace NxNgc

#endif
				   


================================================
FILE: Code/Gfx/NGC/p_NxViewport.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxViewport.cpp

#include 	"Gfx/NGC/p_NxViewport.h"

namespace Nx
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CViewport

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcViewport::CNgcViewport()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcViewport::CNgcViewport( const Mth::Rect* rect, Gfx::Camera* cam) :
	CViewport(rect, cam)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcViewport::~CNgcViewport()
{
}

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

} // Namespace Nx  			
				
				



================================================
FILE: Code/Gfx/NGC/p_NxViewport.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxViewport.h

#ifndef	__GFX_P_NX_VIEWPORT_H__
#define	__GFX_P_NX_VIEWPORT_H__

#include 	"Gfx/NxViewport.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Machine specific implementation of the CViewport
class	CNgcViewport : public CViewport
{
public:
								CNgcViewport();
								CNgcViewport(const Mth::Rect* rect, Gfx::Camera* cam = NULL);
	virtual						~CNgcViewport();

private:		// It's all private, as it is machine specific
	//virtual void				plat_initialize();

};

} // Namespace Nx  			

#endif



================================================
FILE: Code/Gfx/NGC/p_NxWin2D.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxWin2D.cpp

#include 	"Gfx/Ngc/p_NxWin2D.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CText

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcWindow2D::CNgcWindow2D(int x, int y, int width, int height) : CWindow2D(x, y, width, height)
{
//	mp_plat_window = new NxNgc::SScissorWindow();

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcWindow2D::~CNgcWindow2D()
{
//	if (mp_plat_window)
//	{
//		delete mp_plat_window;
//	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CNgcWindow2D::plat_update_engine()
{
	Dbg_Assert(m_xpos >= 0);
	Dbg_Assert(m_ypos >= 0);

	int x1 = m_xpos + m_width - 1;
	int y1 = m_ypos + m_height - 1;

	Dbg_Assert(x1 >= 0);
	Dbg_Assert(y1 >= 0);

//	mp_plat_window->SetScissor(m_xpos, m_ypos, x1, y1);
	m_left		= m_xpos;
	m_top		= m_ypos;
	m_right 	= x1;
	m_bottom	= y1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CWindow2DManager::s_plat_alloc_window2d_pool()
{
	for (int i = 0; i < vMAX_WINDOW_INSTANCES; i++)
	{
	   	CNgcWindow2D *p_window = new CNgcWindow2D;
		p_window->mp_next = sp_window_list;
		sp_window_list = p_window;
	}
}

} // Namespace Nx  			
				
				



================================================
FILE: Code/Gfx/NGC/p_gfxman.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		p_gfxman.cpp											**
**																			**
**	Created:		07/26/99	-	mjb										**
**																			**
**	Description:	Graphics device manager									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/
		
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

namespace Gfx
{


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

//#define SELECT_VIDEO_MODE
// Some defines for gamma settings.
#define gammaDefault	1.0f
#define gammaDomain		256
#define gammaRange		256.0f
#define MIN_GAMMA		0.0f
#define MAX_GAMMA		2.0f



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

//static D3DGAMMARAMP	gammaTable;
//static float		gammaValueR = gammaDefault;	// Current red gamma value.
//static float		gammaValueG = gammaDefault;	// Current green gamma value.
//static float		gammaValueB = gammaDefault;	// Current blue gamma value.



/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//static void gammaInitTable( void )
//{
//	// Create the gamma intensity lookup table.
//	// It works like this: given a colour value r [0,255] do r' = gammaTable.red[r] etc.
//	for( int i = 0; i < gammaDomain; ++i )
//	{
//		gammaTable.red[i]	= (BYTE)((float)gammaRange * pow(((float)i / (float)gammaDomain ), 1.0f / gammaValueR ));
//		gammaTable.green[i]	= (BYTE)((float)gammaRange * pow(((float)i / (float)gammaDomain ), 1.0f / gammaValueG ));
//		gammaTable.blue[i]	= (BYTE)((float)gammaRange * pow(((float)i / (float)gammaDomain ), 1.0f / gammaValueB ));
//	}
//}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void gammaSetValue( float gr, float gg, float gb )
{
//	gammaValueR = gr;
//	gammaValueG = gg;
//	gammaValueB = gb;
//
//	// Create the table.
//	gammaInitTable();
//
//	// Pass the table along to the hardware.
//	D3DDevice_SetGammaRamp( D3DSGR_NO_CALIBRATION, &gammaTable );
}



/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//void Manager::SetGammaNormalized( float fr, float fg, float fb )
//{
//	// Cap it....
//	fr = ( fr < 0.0f ) ? 0.0f : (( fr > 1.0f ) ? 1.0f : fr );
//	fg = ( fg < 0.0f ) ? 0.0f : (( fg > 1.0f ) ? 1.0f : fg );
//	fb = ( fb < 0.0f ) ? 0.0f : (( fb > 1.0f ) ? 1.0f : fb );
//
//	// Scale it....
//	fr *= MAX_GAMMA;
//	fg *= MAX_GAMMA;
//	fb *= MAX_GAMMA;
//
//	// Offset it.
//	fr += 0.5f;
//	fg += 0.5f;
//	fb += 0.5f;
//
//	// f now goes from 0.5 to 2.5.
//	gammaSetValue( fr, fg, fb );
//}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::DumpVRAMUsage( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::ScreenShot( const char *fileroot )
{
//	char fileName[32];
//	char fullFileName[64];
//
////	Skate3 code, disabled for now.
//#	if 0
//	
//	// Try to find a good filename of the format filebasexxx.bmp. "Good" is defined here as one that isn't already used.
//	RwFileFunctions* fileFunctions = RwOsGetFileInterface();
//	for( int i = 0;; ++i )
//	{
//		sprintf( fileName, "screens\\%s%03d.bmp", fileroot, i );
//
//		// Found an unused one! Yay!
//		if( !fileFunctions->rwfexist( fileName ))
//		{
//			sprintf( fullFileName, "d:\\data\\screens\\%s%03d.bmp", fileroot, i );
//			break;
//		}
//	}
//
//	// Obtain the render surface.
//	IDirect3DSurface8* p_render_target;
//	D3DDevice_GetRenderTarget( &p_render_target );
//
//	// Get the surface description, just for s and g.
//	D3DSURFACE_DESC surface_desc;
//	p_render_target->GetDesc( &surface_desc );
//
//	// This is great - this function spits surfaces straight out into a file.
//	HRESULT hr = XGWriteSurfaceToFile( p_render_target, fullFileName );
//	Dbg_Assert( hr == S_OK );
//
//#	endif
}






/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


} // namespace Gfx


================================================
FILE: Code/Gfx/NGC/p_loadscreen.cpp
================================================
///*****************************************************************************
//**																			**
//**			              Neversoft Entertainment.			                **
//**																		   	**
//**				   Copyright (C) 2000 - All Rights Reserved				   	**
//**																			**
//******************************************************************************
//**																			**
//**	Project:		Gfx														**
//**																			**
//**	Module:			LoadScreen			 									**
//**																			**
//**	File name:		p_loadscreen.cpp										**
//**																			**
//**	Created by:		05/08/02	-	SPG										**
//**																			**
//**	Description:	PS2-specific loading screen calls						**
//**																			**
//*****************************************************************************/
//
///*****************************************************************************
//**							  	  Includes									**
//*****************************************************************************/
//
//#include 
//
//#include 
//#include 
//
//#include 
//
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//
//#include "sys/ngc/p_profile.h"
//
//#include 
//
//NsProfile _gpu( "GPU", 256 );
//NsProfile _cpu( "CPU", 256 );
//static volatile int _open = 0;
//
//NsCamera			camera2D;
//int heaperror = 0;
//
///*****************************************************************************
//**								DBG Information								**
//*****************************************************************************/
//
//extern void	(*pIconCallback)( void );
//
////extern void* copyXFB;
////extern void* dispXFB;
////extern void* myXFB1;
////extern void* myXFB2;
//
//namespace LoadScreen
//{
//
///*****************************************************************************
//**								  Externals									**
//*****************************************************************************/
//
///*****************************************************************************
//**								   Defines									**
//*****************************************************************************/
//
//#define vMAX_PIXEL_DATA_SIZE	( 32767 * 16 )
//
//#define	ENABLE_LOADING_ICON		1
//
//
///*****************************************************************************
//**								Private Types								**
//*****************************************************************************/
//
//class LoadingIconData
//{
//public:
//	int			m_X;
//	int			m_Y;
//	int			m_FrameDelay;
//	NsTexture**	m_Rasters;
//	int			m_NumFrames;
//};
//
///*****************************************************************************
//**								 Private Data								**
//*****************************************************************************/
//
//static bool				s_loading_screen_on		= false;
//static OSThread			s_load_icon_thread;
//static OSAlarm			s_alarm;
//static LoadingIconData	s_load_icon_data		= { 0 };
////static char				s_load_icon_thread_stack[4096]	__attribute__ (( aligned( 16 )));
//static volatile bool	s_terminate_thread		= false;
//static volatile bool	s_terminate_thread_done	= false;
//static bool				s_thread_exists			= false;
//
///*****************************************************************************
//**								 Public Data								**
//*****************************************************************************/
//
///*****************************************************************************
//**							  Private Prototypes							**
//*****************************************************************************/
//
///*****************************************************************************
//**							  Private Functions								**
//*****************************************************************************/
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//void s_thread_loading_icon_alarm_handler( OSAlarm* alarm, OSContext* context )
//{
//	// Check the thread has not been resumed yet...
//	if( OSIsThreadSuspended( &s_load_icon_thread ))
//	{
//		OSResumeThread( &s_load_icon_thread );
//	}
//}
//
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//void s_non_threaded_loading_icon( void )
//{
//	LoadingIconData* p_data = (LoadingIconData*)&s_load_icon_data;
//
//	static int			current_image = 0;
//	static Tmr::Time	time = 0;
//
//	Tmr::Time			time_now = Tmr::GetVblanks();
//
//	if(( time_now < time ) || ( time_now > ( time +	p_data->m_FrameDelay )))
//	{
//		time = time_now;
//
//		NsTexture* p_texture = p_data->m_Rasters[current_image];
//
//		NsDisplay::begin();
//
//		NsRender::begin();
//
////		NsPrim::begin();
//	    GXSetVtxAttrFmt( GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0 );		// 3 = positions & uvs.
//		GXSetVtxAttrFmt( GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0 );
//
//		camera2D.orthographic( 0, 0, 640, 448 );
//
//		NsRender::setBlendMode( NsBlendMode_None, p_texture, (unsigned char)0 );
//
//		GXClearVtxDesc();
//		GXSetVtxDesc( GX_VA_POS, GX_DIRECT );
//		GXSetVtxDesc( GX_VA_TEX0, GX_DIRECT );
//		GX::SetChanMatColor( GX_COLOR0, (GXColor){ 128,128,128,128 });
//		GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//
//		p_texture->upload( NsTexture_Wrap_Clamp );
//
//		GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
//		GXPosition3f32((float)p_data->m_X, (float)p_data->m_Y, -1.0f );
//		GXTexCoord2f32( 0.0f, 0.0f );
//		GXPosition3f32((float)p_data->m_X + (float)p_texture->m_width, (float)p_data->m_Y, -1.0f );
//		GXTexCoord2f32( 1.0f, 0.0f );
//		GXPosition3f32((float)p_data->m_X + (float)p_texture->m_width, (float)p_data->m_Y + (float)p_texture->m_height, -1.0f );
//		GXTexCoord2f32( 1.0f, 1.0f );
//		GXPosition3f32((float)p_data->m_X, (float)p_data->m_Y + (float)p_texture->m_height, -1.0f );
//		GXTexCoord2f32( 0.0f, 1.0f );
//		GXEnd();
//
////		NsPrim::end();
//
//		camera2D.end();
//
//		NsRender::end();
//
//		NsDisplay::end( false );
//
//		current_image = ( current_image + 1 ) % p_data->m_NumFrames ;
//	}
//}
//
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//void* s_threaded_loading_icon( void* data )
//{
//	LoadingIconData* p_data = (LoadingIconData*)data;
//
//	int		current_image = 0;
//
//	while( 1 )
//	{
//		if( s_terminate_thread )
//		{
//			// This thread is done...
//			s_terminate_thread_done	= true;
//			return NULL;
//		}
//
//		bool busy = /*NsModel::busy() | DVDError() |*/ heaperror;
//
//		// Don't want to do draw anything whilst display lists are being built. Come back in a bit.
//		if( !busy )
//		{
//			NsTexture* p_texture = p_data->m_Rasters[current_image];
//
//			NsDisplay::begin();
//			NsRender::begin();
//
//		    GXSetVtxAttrFmt( GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0 );		// 3 = positions & uvs.
//			GXSetVtxAttrFmt( GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0 );
//
//			camera2D.orthographic( 0, 0, 640, 448 );
//
//			NsRender::setBlendMode( NsBlendMode_None, p_texture, (unsigned char)0 );
//
//			GXClearVtxDesc();
//			GXSetVtxDesc( GX_VA_POS, GX_DIRECT );
//			GXSetVtxDesc( GX_VA_TEX0, GX_DIRECT );
//			GX::SetChanMatColor( GX_COLOR0, (GXColor){ 128,128,128,128 });
//			GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//
//			p_texture->upload( NsTexture_Wrap_Clamp );
//
//			GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
//			GXPosition3f32((float)p_data->m_X, (float)p_data->m_Y, -1.0f );
//			GXTexCoord2f32( 0.0f, 0.0f );
//			GXPosition3f32((float)p_data->m_X + (float)p_texture->m_width, (float)p_data->m_Y, -1.0f );
//			GXTexCoord2f32( 1.0f, 0.0f );
//			GXPosition3f32((float)p_data->m_X + (float)p_texture->m_width, (float)p_data->m_Y + (float)p_texture->m_height, -1.0f );
//			GXTexCoord2f32( 1.0f, 1.0f );
//			GXPosition3f32((float)p_data->m_X, (float)p_data->m_Y + (float)p_texture->m_height, -1.0f );
//			GXTexCoord2f32( 0.0f, 1.0f );
//			GXEnd();
//
//			camera2D.end();
//
//			NsRender::end();
//			NsDisplay::end( false );
//
//			current_image = ( current_image + 1 ) % p_data->m_NumFrames ;
//		}
//
//		// Go to sleep.
//		if( busy )
//		{
//			// Come back shortly.
//			OSSetAlarm( &s_alarm, OSMillisecondsToTicks( 50 ), s_thread_loading_icon_alarm_handler );
//		}
//		else
//		{
//			// Come back in the proper time.
//			OSSetAlarm( &s_alarm, OSMillisecondsToTicks( s_load_icon_data.m_FrameDelay ), s_thread_loading_icon_alarm_handler );
//		}
//
//		OSSuspendThread( &s_load_icon_thread );
//	}
//}
//
//
///*****************************************************************************
//**							  Public Functions								**
//*****************************************************************************/
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//void SetLoadingIconProperties( int x, int y, int frame_delay, int num_frames, char* basename, char* ext )
//{
////	int i;
////
////	if( s_load_icon_data.m_NumFrames > 0  )
////	{   
////		for( i = 0; i < s_load_icon_data.m_NumFrames; i++ )
////		{
////			delete s_load_icon_data.m_Rasters[i];
////		}
////		delete [] s_load_icon_data.m_Rasters;
////	}
////
////	s_load_icon_data.m_X			= x;
////	s_load_icon_data.m_Y			= y;
////	s_load_icon_data.m_FrameDelay	= ( frame_delay * 1000 ) / 60;		// Convert from frames to milliseconds.
//////	s_load_icon_data.m_FrameDelay	= frame_delay;
////	s_load_icon_data.m_NumFrames	= num_frames;
////	s_load_icon_data.m_Rasters		= new NsTexture*[s_load_icon_data.m_NumFrames];
////
////	for( i = 0; i < s_load_icon_data.m_NumFrames; i++ )
////	{
////		NsFile file;
////
////		char image_name[64];
////		sprintf( image_name, "%s%d.%s", basename, i+1, ext );
////
//// 		file.open( image_name );
////		int filesize		= file.size();
////		uint8 *pData		= (uint8*)new uint8[filesize + 31];
////		pData				= (uint8*)OSRoundUp32B( pData );
////		file.read( pData, filesize );
////		file.close();
////
////		NsTexture *pTex		= (NsTexture*)pData;
////		pData				= (uint8*)&pTex[1];
////		pTex->m_pImage		= pData;
////		pData			   += ( pTex->m_width * pTex->m_height * pTex->m_depth ) / 8;
////		pTex->m_pPalette	= pData;
////
////		s_load_icon_data.m_Rasters[i] = pTex;
////	}
//}
//
//void _start( void )
//{
//	_gpu.start();
//	_open = 1;
//}
//
//void _end( void )
//{
//	_gpu.stop();
//	_open = 0;
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//void Display( char* filename, bool just_freeze, bool blank )
//{
////	float y = 0;
////
////	while ( 1 )
////	{
////		Nx::CEngine::sPreRender();		
////					 
//////		Nx::CEngine::sRenderWorld();		
////
//////--------------------------------------------------
////
////		NsCamera cam;
////		cam.orthographic( 0, 0, 640, 448 );
////
////		// Draw the screen.
////		NsPrim::begin();
////
////		cam.begin();
////
////		NsRender::setBlendMode( NsBlendMode_None, NULL, (unsigned char)0 );
////
////		NsPrim::box( 64, 64 + y, 640-64, 64+80 + y, (GXColor){0,32,64,128} );
////
////		y += 1.0f;
////		if ( y > 256.0f ) y -= 256.0f;
////
////		cam.end();
////
////		NsPrim::end();
////
//////--------------------------------------------------
////
////		Nx::CEngine::sPostRender();		
////	}
//	
////	Spt::SingletonPtr< Mlp::Manager > mlp_man;
////	Tmr::Time start_time;
////
////#if 0
////	NsDisplay::setRenderStartCallback( _start );
////	NsDisplay::setRenderEndCallback( _end );
////
////	camera2D.orthographic ( 0, 0, 640, 448 );
////	int col = 0;
////
////	NsDisplay::setRenderStartCallback( _start );
////	NsDisplay::setRenderEndCallback( _end );
////
////	while ( 1 ) {
////		NsDisplay::setBackgroundColor ( (GXColor){0,0,255,0} );
////		col+=2;
////		if (col == 256) col = 0;
////
////		// Draw the screen.
////		NsDisplay::begin();
////		NsRender::begin();
////		NsPrim::begin();
////
////		camera2D.begin();
////
////		_cpu.start();
////
////		NsRender::setBlendMode( NsBlendMode_None, NULL, (unsigned char)128 );
////
////		
////		GXClearVtxDesc();
////		GXSetVtxDesc( GX_VA_POS, GX_DIRECT );
////
////		// Set material color.
////		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,col,128} );
////		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
////
////		GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
////
////		
////		for (int lp = 0; lp < 3000; lp++) {
////			float l = 64+col;
////			float t = 64;
////			float r = 64+2+col;
////			float b = 64+2;
//////			NsPrim::box ( 64+col, 64, 80+col, 80, (GXColor){255,255,col,128} );
////		
////			// Send coordinates.
////			GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
////				GXPosition3f32(l, t, -1.0f);
////				GXPosition3f32(r, t, -1.0f);
////				GXPosition3f32(r, b, -1.0f);
////				GXPosition3f32(l, b, -1.0f);
////			GXEnd();
////		}
////
////		_cpu.stop();
////
//////		while ( _open );
////		_gpu.histogram( 576, 32, 608, 416, (GXColor){255,0,0,128} );
////		_cpu.histogram( 544, 32, 576, 416, (GXColor){0,255,0,128} );
////
////		camera2D.end();
////
////		NsPrim::end();
////		NsRender::end();
////		NsDisplay::end( true );
////	}
////#endif
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////
////	if( s_loading_screen_on )
////	{
////		return;
////	}
////
////	start_time = Tmr::GetTime();
////
////	if( !just_freeze && !blank )
////	{
////		// Load in the screen.
////		NsFile	file;
////		NsTexture * pTex = (NsTexture *)file.load( filename );
////
////		// Skip data.
////		uint8 * pData = (uint8 *)&pTex[1];
////		pTex->m_pImage = pData;
////		pData += ( pTex->m_width * pTex->m_height * pTex->m_depth ) / 8;
////		pTex->m_pPalette = pData;
////
////		camera2D.orthographic ( 0, 0, 640, 448 );
////		NsDisplay::setBackgroundColor ( (GXColor){0,0,0,0} );
////
////		for ( int lp = 0; lp < 2; lp++ )
////		{
////			// Draw the screen.
////			NsDisplay::begin();
////			NsRender::begin();
////			NsPrim::begin();
////
////			camera2D.begin();
////
////			NsRender::setBlendMode( NsBlendMode_None, pTex, (unsigned char)0 );
////			NsPrim::sprite ( pTex, 0.0f, 0.0f, -1.0f );
////
////			camera2D.end();
////
////			NsPrim::end();
////			NsRender::end();
////			NsDisplay::end( false );
////		}
////
////		// Wait for it to finish.
////		NsDisplay::flush();
////
////		// We're done.
////		delete pTex;
////
////		// Start the thread to display the animated loading icon.
////#		if ENABLE_LOADING_ICON
//////		pIconCallback = s_non_threaded_loading_icon;
////		BOOL rv = true;
////
////		s_terminate_thread = false;
////
////		memset( &s_load_icon_thread, 0, sizeof( OSThread ));
////		rv = OSCreateThread(	&s_load_icon_thread,
////								s_threaded_loading_icon,										// Entry function.
////								&s_load_icon_data,												// Argument for start function.
////								s_load_icon_thread_stack + sizeof( s_load_icon_thread_stack ),	// Stack base (stack grows down).
////								sizeof( s_load_icon_thread_stack ),								// Stack size in bytes.
////								1,																// Thread priority.
////								OS_THREAD_ATTR_DETACH  );										// Thread attributes.
////		Dbg_MsgAssert( rv, ( "Failed to create thread" ));
////
////		if( rv )
////		{
////			s_thread_exists = true;
//////			OSResumeThread( &s_load_icon_thread );
////			OSSetAlarm( &s_alarm, OSMillisecondsToTicks( s_load_icon_data.m_FrameDelay ), s_thread_loading_icon_alarm_handler );
////		}
////#		endif // ENABLE_LOADING_ICON
////	}
////	else
////	{
////		// Wait a couple of VBlanks for RW stuff to disappear
////		// Mick: so there is no pending page flip
////		while( Tmr::GetVblanks() - start_time < 2 );		
////		
////		// not loading, just freezing, maybe should copy current buffer over second buffer?
////		if( blank )
////		{
////			// Need to clear the screen here.
//////			RpSkySuspend( );	 // Turn or Renderwware inputs and stuff
//////		    Flx::clearGsMem(0x00, 0x00, 0x00, 640, 448*2);
//////			RpSkyResume( );		 // and trun em back on, with the screen nice and blank...
////		}
////	}
////	s_loading_screen_on = true;
////
////	mlp_man->PauseDisplayTasks( true ); 
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//
//void Hide( void )
//{
//#	if ENABLE_LOADING_ICON
//	if( s_thread_exists )
//	{
//		s_terminate_thread_done	= false;
//		s_terminate_thread = true;
//
//		// Cancel the alarm and resume the thread.
////		OSCancelAlarm( &s_alarm );
////		OSResumeThread( &s_load_icon_thread );
//
//		while( !s_terminate_thread_done );
//
//		s_thread_exists = false;
////		pIconCallback = NULL;
//
//		VISetBlack( TRUE );
//		VIFlush();
//		VIWaitForRetrace();
//		NsDisplay::flush();
//		VISetBlack( FALSE );
//		VIFlush();
//		VIWaitForRetrace();
//	}
//#	endif // ENABLE_LOADING_ICON
//
//	Spt::SingletonPtr< Mlp::Manager >	mlp_man;
//	mlp_man->PauseDisplayTasks( false ); 
//
//	if( !s_loading_screen_on )
//	{
//		return;
//	}
//
//	s_loading_screen_on = false;
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//
//void	StartProgressBar( char* tick_image, int num_ticks, int x, int y )
//{
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//
//void	ProgressTick( int num_ticks )
//{
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//
//void	EndProgressBar( void )
//{
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//
//} // namespace LoadScreen







================================================
FILE: Code/Gfx/NGC/p_memview.cpp
================================================
//////////////////////////////////////////////////////
// p_memview.cpp
//
// code for tracking memory usage, and displaying it in a graphical manner
// keeps extra info about allocated blocks
// including the call stack, so we can print out information
// about specific allocated blocks
// which we will select using the graphical memory browser
//
//
// tried to use a little of the task system as possible
// so we can run the inspector
// without it messing with the heap it inspects
//
//


extern char _mem_dump_start[];
extern char _map_file_start[];
extern char _symbols_start[];
extern char _callstack_start[];
extern char _code_end[];
extern char	_std_mem_end[];
extern char	_stack_size[];

extern char __text_org[];
extern char __data_org[];
extern char __rodata_org[];
extern char __bss_org[];

extern char __rodata_orgend[];
extern char __bss_objend[];
extern char __text_objend[];

//extern char _rwheapdebug_start[];

			 
#define	STACKDEPTH  30			 


			 
extern "C"				 
{
extern void* ENTRYPOINT;		
}

#include 
#include 
#include 					// needed for buttons
#include 
#include  			// needed for loading map file
#include 

#include 
#include 


#include 
#include 
#include 
#include 

// needed for some VerticalMenu specific debugging							
#include 
#include 
#include 


extern volatile int	test_vblanks;


class CCallStack
{
public:
	void Append(CCallStack *p);
	void Remove();
	void InitHead();
	int	used;
	int	size;
	CCallStack *pNext;
	CCallStack *pPrev;
	int	addr[STACKDEPTH];
	uint32	flags;
	Mem::Allocator::BlockHeader * pBlock;	// pointer to block that has this callstack

};

CCallStack		free_list;	// list of created objects
CCallStack		used_list;	// list of created objects

// init a node, so it can act as the head			 
inline void CCallStack::InitHead()
{
	pPrev = this;
	pNext = this;
}
	
// append node p to this node (after it)									 
inline void CCallStack::Append(CCallStack *p)
{

	p->pNext = this->pNext;
	p->pPrev = this;
	this->pNext = p;
	p->pNext->pPrev = p;
}

// simply unlink it from the list			   
inline void CCallStack::Remove()
{
	pPrev->pNext = pNext;
	pNext->pPrev = pPrev;
}


						
//CCallStack * CallStack_FirstFree;
//CCallStack * CallStack_FirstUsed; 

static int MemView_Active = 0;



#define	MAX_CALLSTACK (8192 * 8)		// we got 8 mb, woo woo.


static float step = 128.0f;


//static char HexByte(char a)
//{
//	if (a >= '0' && a <='9')
//	{
//		return a-'0';
//	}
//	if (a >= 'A' && a <='F')
//	{
//		return 10 + a-'A';
//	}
//	if (a >= 'a' && a <='f')
//	{
//		return 10 + a-'a';
//	}
//
//	// should really be an error, but just ignore it and return 0
//	// as this is only used for parsing the map file	
//	return 0;
//	
//	
//}
//
//
//static int doneonce = 0;


char *MemView_GetFunctionName(int pc, int *p_size)
{
//
//	if (!pc)
//	{
//		return "NULL";
//	}
//				   
//	// given an address, return the name of the function
//	// does this by intially loading and buuilding a list of
//	// all the start points, and names, of all the functions
//	// by loading the skate3.map
//
//	
//	static int symbols = 0;
//	
//	if (!doneonce)
//	{
//	
////		mdl.m_fd = sceOpen( "host:ctrl_out.dat", SCE_RDWR );
/////		sceRead( mdl.m_fd, mdl.m_recorded_data, 72000 * sizeof( Inp::RecordedData ));
////		sceClose( mdl.m_fd );
//
//
//		#ifdef	__NOPT_CDROM__OLD
//		int pFP= sceOpen("host:..\\build\\ngpsgnu\\skate4c.map", SCE_RDONLY);		
//		#else
//		int pFP= sceOpen("host:..\\build\\ngpsgnu\\skate4.map", SCE_RDONLY);		
//		#endif
//
//		if (!pFP)
//		{
//			return "(skate4.map not loaded yet)";
//		}
//
//
//		doneonce = 1;
//	
//		// Open the qb file and load it into memory.
//		//int FileSize = ((skyFile*)pFP)->SOF		
////		int FileSize = File::GetFileSize(pFP);
//		char *pQB= _map_file_start ;
//		sceRead(pFP,pQB,4000000);
//		sceClose(pFP);
//		
//		// Now the file is loaded, we need to extract all the functions
//		// so, search for the text 
//		
//		char *p = strstr(pQB,"address order");
//		int	 *d = (int*)_symbols_start; 
//		while (*p!=0x0a) p++;		// skip to start of next line
//		p++;						// skip over 0a
//		while (*p)
//		{
//			p++;					// skip over the space
//			// the next 8 characters are the address in upper case hex
//			int addr = 0;
//			for (int i=0;i<8;i++)
//			{
//				addr <<= 4;
//				addr += HexByte(*p++);
//			}
//			p+= 2;			// skip two spaces
//			
//			// the next 8 characters are the size in upper case hex
//			int size = 0;
//			for (int i=0;i<8;i++)
//			{
//				size <<= 4;
//				size += HexByte(*p++);
//			}
//			
//			p+= 2;			// skip two spaces
//			
//
//			// only store symbols of non-zero size
//			// otherwise, we get confused by having things like _bss_size in there
//			// as they are not addresses, they just look like them, being so big...
//			if (size || (addr >(int) __text_objend))
//			{
//				*d++ = addr;		// store the address of the symbol
//				*d++ = (int)p;			// store the start of the symbol name
//				symbols++;		 	// one more symbol
//			}
//			
//			// search for first space, or CF, and replace with a 0
//			// that way we ignore the "unmangled" version of the function
//			while (*p && /**p!=' ' &&*/ *p!=0x0a && *p!='(' && *p != 0x0d) p++;	
//			*p++ = 0;
//			
//
//			// skip to LF, and replace the 			
//			while (*p && *p!=0x0a) p++;		// skip to start of next line
//			p++;						// skip over 0a, will now be at the space on next line
//		}
//	}
//
//	
//	int	 *s = (int*)_symbols_start; 
//
//// just serach the table
//// (might want a binary search, but no real need for speed)
//	for (int i=0;i pc)	 		// if this one is above the pc
//		{
//			*p_size = addr-s[-2];		// calculate the size of the function
//			return (char*) (s[-1]);		// then the previous one is the function
//		}
//		s += 2;	
//	}
		 
	return "UNKNOWN";
	
}

// Modifed version of Jamie's unwind stack function
// ignores the fp, and just goes directly off the sp
// seems to work much better (and faster too)
int DumpUnwindStack( int iMaxDepth, int *pDest )
{
//	uint32* ra;
//	uint64* sp;											// frame pointer
//	ra = ((uint32*)DumpUnwindStack)+64;				// fake point in function to unwind from (
//														// after the sd ra,0(sp), but before getting it back
////	asm ( "daddu %0, $29, $0" : "=r" (sp) );			// get current sp
//	sp = (uint64*)OSGetStackPointer();
//
//	if (!pDest)
//	{
//		printf("\n");
//	}
//		
//	int icd = iMaxDepth;	   							// depth counter
//	uint32* last_ra = NULL;
//	while ( icd-- )
//	{
//			/* scan instruction*/
//		uint32* pc = ra;								// current pc, somewehre in middle of function
//		uint32 count = 4096;							// enought to cover large functions	(16k)
//		while ( count-- )
//		{
//			uint32 ins = *pc;		 					// get 32 bit instruction
//			if (((ins >> 16) & 0x7fff) == 0x7fbf)		// sd ra,offset(sp)  (or sq, for .C files)
//			{
//				uint32 offset = *(short*)pc;			// get offset (bottom 16 bits)
//				ra = (uint32*)(sp[offset>>3]);					// >>3 as it's at 64 bit word pointer
//				break;
//			}
//			pc--;
//		}
//		while ( count--)
//		{
//			uint32 ins = *pc;		 					// get 32 bit instruction
//			if ((ins >> 16) == 0x27bd)					// addiu sp,sp,offset
//			{
//				int offset = *(short*)pc;				// get offset (bottom 16 bits)
//				if (offset & 0x8000)
//				{
//					offset |= 0xffff0000;
//				}
//				sp = (uint64*)( (int)(sp) - (offset));	   
//				break; 	
//			}			
//			pc--;
//		}
//
////		if (last_ra == ra)
////		{
////			icd++;			// one more please....
////		}
////		else
//		{
//			last_ra = ra;
//			if (pDest)
//			{
//				*pDest++ = (int)ra;
//				*pDest = 0;
//			}
//			else
//			{
//				int size;
//	//			printf ("sp = %p, ra = %p %s\n",sp,ra,MemView_GetFunctionName((int)ra));
//				printf ("%p: %s\n",ra,MemView_GetFunctionName((int)ra,&size));
//			}
//		}
//	    
//		// test to see if we have recursed up all the way...
//		if (abs(int((int)ra - (int)&ENTRYPOINT)) < 1024
//		   || (int)ra &3 
//		   || (int)ra < 0x100000
//		   || (int)ra > (int)_code_end		// and check it's not totally crazy....
//		   )
//		{
//			return 0;
//		}
//	
//	}
//	return iMaxDepth - icd;
	return 0;
}
				   
				   
//        mD_L2           = nBit( vD_L2 ),
//        mD_R2           = nBit( vD_R2 ),
//        mD_L1           = nBit( vD_L1 ),
//        mD_R1           = nBit( vD_R1 ),
//        mD_TRIANGLE     = nBit( vD_TRIANGLE ),
//	      mD_CIRCLE       = nBit( vD_CIRCLE ),
//        mD_X            = nBit( vD_X ),
//        mD_SQUARE       = nBit( vD_SQUARE ),
//        mD_SELECT       = nBit( vD_SELECT ),
//        mD_L3           = nBit( vD_L3 ),
//        mD_R3           = nBit( vD_R3 ),
//        mD_START        = nBit( vD_START ),
//        mD_UP           = nBit( vD_UP ),
//        mD_RIGHT        = nBit( vD_RIGHT ),
//        mD_DOWN         = nBit( vD_DOWN ),
//        mD_LEFT         = nBit( vD_LEFT ),


void MemViewToggle()
{
	MemView_Active ^=1;
}


	 

void MemView_Alloc( void *v)
{
#ifdef	__LINKED_LIST_HEAP__


#ifdef	__NOPT_CDROM__OLD
   return;
#endif 	

#endif
}

void MemView_Free( void *v)
{
#ifdef	__LINKED_LIST_HEAP__
	// Need to remove it from the used list
	// and add it back to the full list
	
	Mem::Allocator::BlockHeader *p = (Mem::Allocator::BlockHeader *)v;						  
						  
#ifdef	__NOPT_CDROM__OLD
//   return;
#endif 	

	CCallStack *c = (CCallStack*)p->mp_debug_data;	
	
	if (!c)
	{
		// no debug data, so probably a re-alloc
		// should probably handle those later
		return;
	}

	// we clear it, in case this header is re-used later 
	// I'm not entirely sure how well this will work
	p->mp_debug_data = NULL;	
	c->Remove();
	free_list.Append(c);
	
#endif	
}


Mem::Allocator::BlockHeader *MemView_FindBlock( int addr)
{
#ifdef	__LINKED_LIST_HEAP__

	
	Mem::Allocator::BlockHeader *pSmallestBlock	= NULL;
	uint32 smallest_block_size = 100000000;
	Mem::Manager& mem_man = Mem::Manager::sHandle();
	for (Mem::Heap* heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
	{
		Mem::Allocator::BlockHeader *pBlock = (Mem::Allocator::BlockHeader *) heap->find_block((void*)addr);	
		if (pBlock)
		{
			if (pBlock->mSize < smallest_block_size)
			{
				smallest_block_size = pBlock->mSize;
				pSmallestBlock = pBlock;
			}
		}
	}
	return pSmallestBlock;
#else
	return NULL;
#endif
}

char * MemView_GetClassName(CCallStack *c)
{
#ifdef	__LINKED_LIST_HEAP__
	int *ra = (int*)(c->addr[4]);
	if (!ra) return NULL;
	int count = STACKDEPTH-4;
	while (count--)
	{
		int instruction = *ra++;
		if (instruction >> 24 == 0x0c)
		{
			int code = (instruction & 0xffffff)<<2;
			int size;
			char *p = MemView_GetFunctionName(code,&size); 
			// to tell if this is class or not
			// we see if the text is of the form  
			//    classname::classname (teminated by a 0)
			// as that indicates that it is a constructor
			// dude... this is where we need a regular expression....
			char *end = p;
			while (*end) end++;	   						// scan to end
			while (end[-1] != ':' && end > p)	end--;	// skip to char after the last :
			char *other = strstr(p,end);				// find fist occurance of end of string
			if (other != end)							// if different, then this is it!!
			{
				return MemView_GetFunctionName(code,&size);
				break;
			}
		}
	}
#endif
	return NULL;
}


void MemView_DumpBlockInfo(int cursor)
{
#ifdef	__LINKED_LIST_HEAP__
	
	Mem::Allocator::BlockHeader *pBlock = MemView_FindBlock(cursor);
	if (!pBlock)
	{
		// should search free blocks here???
	}
	// find this in the allocators used list
	// and say if it is free, or not	
	if (pBlock == NULL)
	{
//		if (cursor > (int)__text_org && cursor < (int)__bss_objend)		// check to see if in code/data
//		{
//			
//			if (cursor < (int)__data_org)
//				printf("Code: ");
//			else if (cursor < (int)__rodata_org)
//				printf("Data: ");
//			else if (cursor < (int)__bss_org)
//				printf("RO-Data: ");
//			else 
//				printf("BSS: ");
//			
//		
//			int size;
//			char *p_name = MemView_GetFunctionName(cursor,&size);
//			printf ( "%s, size %d\n",p_name,size);
//		}
//		else
		{
			printf ("Block Not Found\n");
		}
	}
	else
	{
		void * p_start = (void*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize);
		printf ("Block found, addr = %p, size = %d (Header = %d)\n",p_start,pBlock->mSize,Mem::Allocator::BlockHeader::sSize);
		CCallStack *c = (CCallStack*)pBlock->mp_debug_data;
		if (!c)
		{
			//printf ("Block with No Debug Info!!\n");
		}
		else
		{
			// assume this is a "new", then the fourth callstack ra will point to the 
			// jal xxxxxx  instruction, where xxxxx is the constructor for the 
			// or it might be sortly thereafter, so check 16 instructions
			
			char * classname = MemView_GetClassName(c);
						
			if (classname)
			{
				printf ("CLASS: %s\n",classname);		
			}

			// then list out the call stack (skipping the MemView_Alloc, as that's a given, and irrelevant);			
		
			int *p = c->addr + 1;
			while (p[1])			// also skip the ENTRYPOINT, just go back to main()
			{
				int size;
				printf ("%p: %s\n",(void*)*p,MemView_GetFunctionName(*p,&size));
				p++;
			}
		}
	}
#endif
}

static	int	blockstart;

static float cursor;

void 		MemView_Display()
{

//#ifdef	__NOPT_CDROM__OLD
//   return;
//#endif 	
//
//
//	if (!MemView_Active)
//	{
//		return;
//	}
//
//	FlushCache( 0 );
//	sceGsSyncPath( 0, 0 );
//	
//	//perfrom the copying
//	// there are 512x256 words in the rectangle
//	// and 32768*1024 bytes in memory
//	// giving us a step of 256 (i.e, sample every 256th bytes)
//	
//	
//	// The start of the middle line will be at 
//	//       start + 512 * 2 * 128 * step;
//	// then  start1 + 512 * 2 * 128 * step1
//	// for them to be the same, start + 512 * 2 * 128 * step = start1 + 512 * 2 * 128 * step1
//	// so start1 =  start + 512 * 2 * 128 * (step - step1)
//	
//	
//
//	blockstart = 0;
//	int blockend = 0;
//	
//	static float last_start;				 
//	
//	float start = cursor - (512.0f * 2.0f * 128.0f * step);
//	
//	int i_cursor = (int)cursor;
//	
//	Mem::Allocator::BlockHeader *pBlock = MemView_FindBlock(i_cursor);
//
//	if (pBlock)
//	{
//		blockstart = (int)((uint)pBlock + Mem::Allocator::BlockHeader::sSize);
//		int size = pBlock->mSize;
//		blockend = (int)((int)blockstart + size);
//	}
//	
//	if (start != last_start)
//	{
//		last_start = start;
//		printf ("\nCursor Addr = %p\n",(void*)i_cursor);   	
//		MemView_DumpBlockInfo(i_cursor);
//	}
//	
//	
//	static int color = 10 + (10<<5) ;
////	color ^= 5 << 10;
//	
//	float f_source = start;
//	float f_off = 0.0f;
////	uint16 *source = (uint16*)(intstart&~1);   		// converting from a float to a pointer... yowza!!!
//	uint16 *dest  =  (uint16*)_mem_dump_start;
//	for (int i=0;i<512*256-4096;i++)
//	{
//		uint16 *source = (uint16*)((int)(f_source + f_off) &~1);		
//	
//		uint32 word;
//		if (source < (uint16*)0x00100000 || source >= (uint16*)(0x08000000))
//		{
//			word = (3<<10)+(3<<5)+(3);			// grey for outside of memory		
//		}
//		else
//		{
//			if (blockstart && (int)(source)>=blockstart && (int)(source)  (uint32)_code_end /*&& x < (uint32)_std_mem_end*/) // don't check for end now, as we have some debug heaps up there we want to include
//		{
//			// check to see if it points to one of the heap members
//			
//			uint32 *p_refs = (uint32*)_mem_dump_start;
//			
//		#if 0
//			for (int i=0;i x)
//					{
//						high = mid;
//					}
//					else
//					{
//						// if the low point is already the same as the mid point
//						// then the only way to go is up!
//						// as this will only occur when low + 1 == high
//						if (low == mid)
//						{
//							low = high;
//						}
//						else
//						{
//							low = mid;
//						}
//					}				
//				}
//				x -= 16;
//			}
//			x = oldx;
//		#endif						  		
//		}
//	}
//	
//	
//}
#endif

#ifdef	__LINKED_LIST_HEAP__
static uint32 *p_used;		
#endif

int MemView_CountBlocks(Mem::Allocator::BlockHeader *p_header)
{
#ifdef	__LINKED_LIST_HEAP__
	int num_used = 0;
	while ( p_header )
	{
		void * p_start = (void*)((uint)p_header + Mem::Allocator::BlockHeader::sSize);
		
		*p_used++ = (uint32)p_start;	 		// store the start of the block
		*p_used++ = 0;							// store a count
		p_header = p_header->mp_next_used;
		num_used++;
	}
	return num_used;
#else
	return 0;
#endif
}


int blockCompFunc( const void *arg1, const void *arg2 )
{
	uint32 addr1 = (*(uint32*)arg1);
	uint32 addr2 = (*(uint32*)arg2);

	if ( addr1 == addr2 )
	{
		return 0;
	}
	else if ( addr1 < addr2 )
	{
		return 1;
	}
	else
	{
		return -1;
	}

}


// Find memory leaks
// the algorithm is quite simple:
// 1) Make a list of all "used" memory blocks, and set their usage count to 0
// 2) Scan all of the heap, and the stack, for each word that looks like a pointer, 
//    check to see if it is in the list of "used", and increment the usage count if so
// 3) Scan the list of used pointers, and check for any with usage == 0

// NEED OT EXTEND FOR TOP_DOWN heap.....		
		
void MemView_FindLeaks()
{
//#ifdef	__LINKED_LIST_HEAP__
//		p_used  =  (uint32*)_mem_dump_start;		
//		num_used = 0;
//		printf ("Counting blocks....");		
//		num_used += MemView_CountBlocks(Mem::Manager::sHandle().BottomUpHeap()->first_block()); 
//		num_used += MemView_CountBlocks(Mem::Manager::sHandle().TopDownHeap()->first_block()); 
//		num_used += MemView_CountBlocks(Mem::Manager::sHandle().FrontEndHeap()->first_block()); 
//		num_used += MemView_CountBlocks(Mem::Manager::sHandle().NetworkHeap()->first_block()); 
//		num_used += MemView_CountBlocks(Mem::Manager::sHandle().ScriptHeap()->first_block()); 
//		num_used += MemView_CountBlocks(Mem::Manager::sHandle().SkaterHeap(0)->first_block()); 
////		num_used += MemView_CountBlocks(Mem::Manager::sHandle().DebugHeap()->first_block()); 
//		printf (" %d\n",num_used);
//		printf ("Sorting .....\n");			
//		// Now we've done that, let's sort the list, so we can use a binary search later
//		
//		
//		#if 1
//		uint32 *p_top  =  (uint32*)_mem_dump_start;		
//		for	(int i = 0;i= 10)
		{
			printf ("Stopping after %d refs\n",count);
			return;
		}
		if (p_start >= p_end)
		{
			printf ("No more References Found in heap \n");
			return;
		}
	}
#endif
}

// Find the first block in the free list
// if no free blocks, then return
// scan all used blocks, and print out the info for all the blocks
// that have an address above the first free block
void MemView_DumpFragments(Mem::Heap *pHeap)
{
	#ifdef	__LINKED_LIST_HEAP__    

	if (!pHeap->mFreeBlocks.m_count)
	{
		printf ("NO Fragmentation\n");
		return;
	}
	
	if (!pHeap->mp_context->mp_free_list)
	{
		printf ("!!!!!! No free list, but there are %d free blocks???\n",pHeap->mFreeBlocks.m_count);
		return;
	}

	Mem::Allocator::BlockHeader *p_free = pHeap->mp_context->mp_free_list;
	
	while (p_free->mSize < 10000)
	{
		Mem::Allocator::BlockHeader *p_next = p_free->mpNext;
		if (!p_next)
		{
			printf ("Did not find a free block >10K ?????\n");
		}		
		p_free = p_next;
	}
	
	Mem::Allocator::BlockHeader *p_full = pHeap->mp_context->mp_used_list;
	
	printf ("!!!!!! Free list starts at %p\n",p_free);
	

	// The first p_free will be the start of fragmentations
	while (p_full)
	{
		if (p_full > p_free)
		{
			//printf ("\nFramgented Block\n\n");
			void * p_start = (void*)((uint)p_full + Mem::Allocator::BlockHeader::sSize);
			MemView_DumpBlockInfo((int)p_start);
			for (int xx=0;xx<1000000;xx++);		// little delay, to allow printfs to work
		}
		p_full = p_full->mp_next_used;
	}
	#endif
}

void MemView_DumpHeap(Mem::Heap *pHeap, uint32 mask)
{
	#ifdef	__LINKED_LIST_HEAP__    

//	Mem::Allocator::BlockHeader *p_free = pHeap->mp_context->mp_free_list;
	Mem::Allocator::BlockHeader *p_full = pHeap->mp_context->mp_used_list;
	
	// The first p_free will be the start of fragmentations
	while (p_full)
	{
//		if (p_full > p_free)
//		CCallStack *c = (CCallStack*)p_full->mp_debug_data;	
//		if (!mask || !c || !(c->flags && mask))
		{
			printf ("\n");
			void * p_start = (void*)((uint)p_full + Mem::Allocator::BlockHeader::sSize);
			MemView_DumpBlockInfo((int)p_start);
		}
		p_full = p_full->mp_next_used;
	}
	#endif
}



void MemView_DumpBottomFragments()
{

	MemView_DumpFragments(Mem::Manager::sHandle().BottomUpHeap());
}

void MemView_DumpTopFragments()
{

	MemView_DumpFragments(Mem::Manager::sHandle().TopDownHeap());
}



/*
class CCallStack
{
public:
	void Append(CCallStack *p);
	void Remove();
	void InitHead();
	int	used;
	int	size;
	CCallStack *pNext;
	CCallStack *pPrev;
	int	addr[STACKDEPTH];

};
*/

struct SBlockType
{
	int	return_addr; 			// first meaningful return addr 
	
	int	size;					// size of block (if we want to sort by it
	int	total;					// total size of this type
	int	actual;					// actual total size, including headers
	char *p_class;				// points to class node 
	
	int	count;
};

// scan throught the list of "used" blocks
// and sort them into a list, organized by "type"
// the "type" is determined by the first return address after
// a callstack entry that is either "Malloc" or "Spt::Class::operator new"
// the "type" is furthur sorted by either "size" or "Class"
// where "size" is the size of the block (for a Malloc)
// and "Class" is the type of class that constructed this block 

#define	MAX_TYPES 10000


void MemView_DumpAnalysis( SBlockType* blocks, int numBlocksToPrint )
{
#ifdef	__LINKED_LIST_HEAP__
	// Sorts the types, and print out totals

	int temp;

	for (int i = 0; i < numBlocksToPrint; i++)
	{
		for (int j = i+1;jsize;	  	// size is the only thing we know for sure
	int	return_addr = 0;				// default unknown return address
	char *p_class = "not a class";
	int latest = 1;
	int i = 0;
	
	for ( i = 1; i < 8; i++ )
	{
		int xsize;

		/*
		// the types of call stack we may encounter:
		// need to 
			0x10be48: Mem::Heap::allocate                                                   
			0x109914: Mem::Manager::New                                                     
			0x1035b0: Spt::Class::operator new                                              
			0x161094: Front::KeyboardControl::sCreateInstance   

			0x10be48: Mem::Heap::allocate                                                   
			0x109914: Mem::Manager::New                                                     
			0x10a150: Malloc                                                                
			0x222df8: _SkyBuildPktForUpLoadAlignedContiguousRectangle  

			0x10be48: Mem::Heap::allocate                                                   
			0x109914: Mem::Manager::New                                                     
			0x10a210: Malloc_FreeList                                                       
			0x257034: _rwFreeListAllocReal
		*/

		char *p_name = MemView_GetFunctionName(pCallStack->addr[i],&xsize);
		if (!strcmp("Malloc",p_name) 
			|| !strcmp("Spt::Class::operator new",p_name)
			|| !strcmp("Mem::Manager::New",p_name)
			|| !strcmp("_rwFreeListAllocReal",p_name))
		{
			latest = i;
		}
	}

	if (latest != 1)
	{
		return_addr = pCallStack->addr[latest+1];
	}

	p_class = MemView_GetClassName(pCallStack);				// get class
	// right, now we have all the info on this block
	// let's see if we've got one just like it

	//		if (!p_class && !MemView_GetFunctionName(return_addr,&temp))
	/*		
			if (!return_addr)
			{
				for (int i = 0;i>%s<<\n",i,MemView_GetFunctionName(p->addr[i],&temp));

				}
				return;

			}
	*/
	
									  
	// check if it is a string, and print it out, if so					
/*	
	int temp;				  
	if (!strcmp("Str::String::copy",MemView_GetFunctionName(return_addr,&temp)))
	{
		printf ("String <%s>\n",(char*)((char*)(pCallStack->pBlock)+32)); 
	}
	
	
	if (!strcmp("Front::VerticalMenu::sCreateInstance",MemView_GetFunctionName(return_addr,&temp)))
	{
		void *p_start  =  (void*)((char*)(pCallStack->pBlock)+32);
		printf ("\nVertical Menu "); 
		
		Front::ScreenElement *pV =  (Front::ScreenElement *)p_start;
		printf (" id = %s\n", Script::FindChecksumName(pV->GetID()));
//		MemView_DumpBlockInfo((int)p_start);
		
	}	
*/	

	// check to see if this block is already included
	for ( i = 0; i < num; i++ )
	{
		if ( pBlocks[i].p_class == p_class
			/*&& pBlocks[i].size == size */
			 && pBlocks[i].return_addr == return_addr )
		{
			pBlocks[i].count++;
			pBlocks[i].total += size;
			pBlocks[i].actual += size + Mem::Allocator::BlockHeader::sSize;
			break;				
		}
	}

	// if not, then add the block
	if ( i == num )
	{
		pBlocks[i].p_class = p_class;
		pBlocks[i].size = size;
		pBlocks[i].total = size;
		pBlocks[i].actual = size + Mem::Allocator::BlockHeader::sSize;
		pBlocks[i].return_addr = return_addr;
		pBlocks[i].count = 1;
		num++;
	}
}

#ifdef	__LINKED_LIST_HEAP__
//static int bbb = 0;	   	// compiler patch var, see below
#endif

void MemView_AnalyzeBlocks(uint32 mask)
{
//#ifdef	__LINKED_LIST_HEAP__
//	SBlockType  *pBlocks = (SBlockType  *)_mem_dump_start;	// temp memory
//	int	num_blocks = 0;
//	int num = 0;
//
//	printf ("\nAnalyzing blocks....\n");
//	
//	CCallStack *p = used_list.pNext;  
//	while (p != &used_list)
//	{
//		// Get the actualy block we referred to
////		Mem::Allocator::BlockHeader * pBlock = p->pBlock;
////		void * p_start = (void*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize);
//		// Otionally check to see if it on the front end heap
////		if (Mem::SameContext(p_start,Mem::Manager::sHandle().FrontEndHeap()))
//		{
//			if (!mask || !(p->flags & mask))
//			{
//				MemView_AnalyzeCallStack( p, pBlocks, num );			
//				num_blocks++;
//			}
//		}
//		p = p->pNext; 
//	}
//	
//	printf ("%d types, in %d total blocks\n", num, num_blocks); 
//
//	MemView_DumpAnalysis( pBlocks, num );
//	if (bbb)
//	{
//		MemView_DumpBottomFragments();			// just to get it compiling 
//		MemView_DumpTopFragments();			// just to get it compiling 
//	}		
//#endif
}


void MemView_MarkBlocks(uint32 mask)
{
#ifdef	__LINKED_LIST_HEAP__

	CCallStack *p = used_list.pNext;  
	while (p != &used_list)
	{
		p->flags |= mask;
		
		p = p->pNext;
	}
#endif
}



void MemView_Input(uint buttons, uint makes, uint breaks)
{

	if (Config::CD())
	{
		return;
	}

//	if (makes & Inp::Data::mD_TRIANGLE)
//	{
//		MemView_Active = !MemView_Active;
//	}


	if (!MemView_Active)
	{
		return;
	}

					  
	float step1 = step;
	
	float zoom = 1.1f;
	
	float scroll = 4.0f;

	



	if (buttons & Inp::Data::mD_LEFT)
	{
		step1 = step * zoom;
	}
	if (buttons & Inp::Data::mD_RIGHT)
	{
		step1 = step / zoom;
	}
	
	if (buttons & Inp::Data::mD_UP)
	{
//		start = start - scroll * 512.0f * 2.0f * step;
		cursor = cursor - scroll * 512.0f * 2.0f * step;
	}
	if (buttons & Inp::Data::mD_DOWN)
	{
//		start = start + scroll * 512.0f * 2.0f * step;
		cursor = cursor + scroll * 512.0f * 2.0f * step;
	}

	if (buttons & Inp::Data::mD_L1)
	{
//		start = start - scroll * 512.0f * 2.0f * step / 256.0f;
		cursor = cursor - scroll * 2.0f * 2.0f * step;
	}
	if (buttons & Inp::Data::mD_L2)
	{
//		start = start + scroll * 512.0f * 2.0f * step / 256.0f;
		cursor = cursor + scroll * 2.0f * 2.0f * step;
	}

#define 	MMMIN 	(0.0078125f)
	 				
	if (step1 1024.0f)
	{
		step1 = 1024.0f;
	}

//	start = start + (512.0f * 2.0f * 128.0f * (step - step1));

	step = step1;

	if (makes & Inp::Data::mD_CIRCLE)
	{
		if (blockstart)
		{
			MemView_DumpRefs(blockstart);
		}
//		MemView_MarkBlocks(1);
	}

	// We don't look for leaks automatically now, so I'v put it on "SQUARE"	
	if (makes & Inp::Data::mD_SQUARE)
	{
		MemView_FindLeaks();
//	Mem::Manager& mem_man = Mem::Manager::sHandle();		MemView_DumpHeap(1);
//	heap	= mem_man.TopDownHeap();
//	MemView_DumpFragments(heap);
//	MemView_DumpHeap(heap,1);

	}

	if (makes & Inp::Data::mD_X)
	{
		MemView_AnalyzeBlocks();
	}

	// Triangle = Dump Fragmentation
/*	if (makes & Inp::Data::mD_TRIANGLE)
	{
		Mem::Manager& mem_man = Mem::Manager::sHandle();
		Mem::Heap* heap = mem_man.BottomUpHeap();
		Mem::Region* region = heap->ParentRegion();
		printf ("BottomUp Frag %dK, %d Blocks\n",heap->mFreeMem.m_count / 1024, heap->mFreeBlocks.m_count);
		printf ("Region %d/%d K", region->MemAvailable() / 1024, region->TotalSize() / 1024 );
		MemView_DumpFragments(heap);
	}
*/	

}

void MemView_AnalyzeHeap(Mem::Heap *pHeap)
{
	if ( !pHeap )
		return;
	
//#ifdef	__LINKED_LIST_HEAP__
//	SBlockType  *pBlocks = (SBlockType  *)_mem_dump_start;	// temp memory
//	int	num_blocks = 0;
//	int num = 0;
//
//	Mem::Allocator::BlockHeader *p_full = pHeap->mp_context->mp_used_list;
//
//	while (p_full)
//	{
//		CCallStack* pCallStack = (CCallStack*)p_full->mp_debug_data;
//
//		if ( pCallStack )
//		{
//			MemView_AnalyzeCallStack( pCallStack, pBlocks, num );
//		}
//		else
//		{
//			printf ("Block with No Debug Info!!\n");
//		}
//
//		p_full = p_full->mp_next_used;
//	}
//
//	printf ("%d types, in %d total blocks\n", num, num_blocks); 
//
//	MemView_DumpAnalysis( pBlocks, num );
//#endif
}
  
  




================================================
FILE: Code/Gfx/NGC/p_memview.h
================================================
////

#ifndef	__P_MEMVIEW_H__
#define	__P_MEMVIEW_H__

void MemView_Display();
void MemView_Input(uint buttons, uint makes, uint breaks);
void MemView_Alloc( void *v);
void MemView_Free( void *v);
void MemViewToggle();
void MemView_FindLeaks();
int DumpUnwindStack( int iMaxDepth, int *pDest );
char *MemView_GetFunctionName(int pc, int *p_size);
void MemView_DumpFragments(Mem::Heap *pHeap);
void MemView_AnalyzeBlocks(uint32 mask = 0);
void MemView_MarkBlocks(uint32 flags = 1 );

#endif



================================================
FILE: Code/Gfx/NGC/p_nx.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:						 		 									**
**																			**
**	File name:		gfx/ngc/p_nx.cpp										**
**																			**
**	Created:		01/16/2002	-	dc										**
**																			**
**	Description:	ngc platform specific interface to the engine			**
**					This is ngc SPECIFIC!!!!!! If there is anything in		**
**					here that is not ngc specific, then it needs to be		**
**					in nx.cpp												**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include	

#include 
#include 

#include	"gfx\camera.h"
#include	"gfx\nx.h"
#include	"gfx\nxquickanim.h"
#include	"gfx\nxviewman.h"
#include	"gfx\ngc\p_NxGeom.h"
#include	"gfx\NGc\p_NxMesh.h"
#include	"gfx\NxParticleMgr.h"
#include	"gfx\NxWeather.h"
#include	"gfx\ngc\p_nxWeather.h"
#include	"gfx\NxMiscFX.h"
#include	"gfx\NGc\p_NxModel.h"
#include	"gfx\ngc\p_NxSector.h"
#include	"gfx\ngc\p_NxScene.h"
#include	"gfx\ngc\p_NxSprite.h"
#include	"gfx\ngc\p_NxTexture.h"
#include	"gfx\ngc\p_NxTextured3dPoly.h"
#include	"gfx\ngc\p_NxNewParticleMgr.h"
#include	"core\math.h"
#include 	"sk\engine\SuperSector.h"					
#include 	"gel\scripting\script.h"
#include	"gfx\debuggfx.h"

#include 	"gfx\ngc\nx\nx_init.h"
#include 	"gfx\ngc\nx\import.h"
#include 	"gfx\ngc\nx\material.h"
#include 	"gfx\ngc\nx\mesh.h"
#include 	"gfx\ngc\nx\render.h"
#include 	"gfx\ngc\nx\occlude.h"
#include 	"gfx\ngc\nx\scene.h"
#include 	"gfx\ngc\nx\chars.h"
#include	"gfx\ngc\nx\nx_init.h"

#include	
#include	
#include	
#include	
#include	
#include	

#include	"sys/ngc/p_gx.h"

extern bool gCorrectColor;

static bool s_correctable = true;

int gOffy = 0;

int g_object = 0;
int g_view_object = 0;
NxNgc::sScene * g_view_scene = NULL;
int g_material = -1;
int g_scroll_material = 0;

int g_mip = 0;
int g_passes = -1;

extern unsigned char uv_col_map[][2];

extern u8 g_blur;
u8 sBlur = 0;
extern bool		gLoadingScreenActive;

extern PADStatus padData[PAD_MAX_CONTROLLERS]; // game pad state

extern int g_dl;

extern bool g_in_cutscene;

#define COLOR_MAP_SIZE 64
	
//u16 colorMap[(COLOR_MAP_SIZE*COLOR_MAP_SIZE)*2] __attribute__ (( aligned( 32 )));
//bool color_map_created = false;

//extern NxNgc::sMaterialHeader *	p_u_mat[2048];
//extern int					u_mat_count[2048];
//extern int					num_u_mat;
//bool gPrintMatStats = false;

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#if 0
static u8 sample_pattern[12][2] =
{
//	{1,1},{3,3},{5,5},
//	{11,1},{9,3},{7,5},
//	{5,7},{3,9},{1,11},
//	{7,7},{9,9},{11,11}
	{3,2},{9,2},{3,10},
	{3,2},{9,2},{9,10},
	{3,2},{3,10},{9,10},
	{9,2},{3,10},{9,10},
};
#endif

extern int meshes_considered;

#ifndef __NOPT_FINAL__
bool gDumpHeap = false;
#endif		// __NOPT_FINAL__

#define SHADOW_TEXTURE_SIZE 256
//#define BLUR_TEXTURE_SIZE 64
//#define BLUR_BORDER 2
//
//#define BLUR_1 ( ( (float)BLUR_TEXTURE_SIZE - (float)BLUR_BORDER ) / (float)BLUR_TEXTURE_SIZE )
//#define BLUR_0 ( (float)BLUR_BORDER / (float)BLUR_TEXTURE_SIZE )

uint8 * shadowTextureData = NULL;
//uint8 * volumeTextureData = NULL;
//uint8 * zTextureDataH = NULL;
//uint8 * zTextureDataL = NULL;
//uint8 * screenTextureData = NULL;
//uint8 * focusTextureData = NULL;
//uint8 * blurTextureData = NULL;

//int gFocus = 1;
//
// Set global palette for shadow & blur.

uint16 shadowPalette[16] __attribute__ (( aligned( 32 ))) = {
	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0x00 ),		// 0
	GXPackedRGB5A3( 0x11, 0x11, 0x11, 0xff ),       // 1
	GXPackedRGB5A3( 0x22, 0x22, 0x22, 0xff ),       // 2
	GXPackedRGB5A3( 0x33, 0x33, 0x33, 0xff ),       // 3
	GXPackedRGB5A3( 0x44, 0x44, 0x44, 0xff ),       // 4
	GXPackedRGB5A3( 0x55, 0x55, 0x55, 0xff ),       // 5
	GXPackedRGB5A3( 0x66, 0x66, 0x66, 0xff ),       // 6
	GXPackedRGB5A3( 0x77, 0x77, 0x77, 0xff ),       // 7
	GXPackedRGB5A3( 0x88, 0x88, 0x88, 0xff ),       // 8
	GXPackedRGB5A3( 0x99, 0x99, 0x99, 0xff ),       // 9
	GXPackedRGB5A3( 0xaa, 0xaa, 0xaa, 0xff ),       // 10
	GXPackedRGB5A3( 0xbb, 0xbb, 0xbb, 0xff ),       // 11
	GXPackedRGB5A3( 0xcc, 0xcc, 0xcc, 0xff ),       // 12
	GXPackedRGB5A3( 0xdd, 0xdd, 0xdd, 0xff ),       // 13
	GXPackedRGB5A3( 0xee, 0xee, 0xee, 0xff ),       // 14
	GXPackedRGB5A3( 0xff, 0xff, 0xff, 0xff ),       // 15
};

//uint16 shadowPalette[16] __attribute__ (( aligned( 32 ))) = {
//	GXPackedRGB5A3( 0xff, 0xff, 0xff, 0xff ),       // 15
//	GXPackedRGB5A3( 0xee, 0xee, 0xee, 0xff ),       // 14
//	GXPackedRGB5A3( 0xdd, 0xdd, 0xdd, 0xff ),       // 13
//	GXPackedRGB5A3( 0xcc, 0xcc, 0xcc, 0xff ),       // 12
//	GXPackedRGB5A3( 0xbb, 0xbb, 0xbb, 0xff ),       // 11
//	GXPackedRGB5A3( 0xaa, 0xaa, 0xaa, 0xff ),       // 10
//	GXPackedRGB5A3( 0x99, 0x99, 0x99, 0xff ),       // 9
//	GXPackedRGB5A3( 0x88, 0x88, 0x88, 0xff ),       // 8
//	GXPackedRGB5A3( 0x77, 0x77, 0x77, 0xff ),       // 7
//	GXPackedRGB5A3( 0x66, 0x66, 0x66, 0xff ),       // 6
//	GXPackedRGB5A3( 0x55, 0x55, 0x55, 0xff ),       // 5
//	GXPackedRGB5A3( 0x44, 0x44, 0x44, 0xff ),       // 4
//	GXPackedRGB5A3( 0x33, 0x33, 0x33, 0xff ),       // 3
//	GXPackedRGB5A3( 0x22, 0x22, 0x22, 0xff ),       // 2
//	GXPackedRGB5A3( 0x11, 0x11, 0x11, 0xff ),       // 1
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0x00 ),		// 0
//};

//uint16 zPalette[256] __attribute__ (( aligned( 32 ))) = {
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),		// 0x00
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0x01
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0x02
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0x03
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0x04
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0x05
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x06
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x07
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x08
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x09
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x0a
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x0b
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x0c
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x0d
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x0e
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x0f
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),		// 0x10
//	GXPackedRGB5A3( 0x11, 0x11, 0x11, 0xff ),       // 0x11
//	GXPackedRGB5A3( 0x12, 0x12, 0x12, 0xff ),       // 0x12
//	GXPackedRGB5A3( 0x13, 0x13, 0x13, 0xff ),       // 0x13
//	GXPackedRGB5A3( 0x14, 0x14, 0x14, 0xff ),       // 0x14
//	GXPackedRGB5A3( 0x15, 0x15, 0x15, 0xff ),       // 0x15
//	GXPackedRGB5A3( 0x16, 0x16, 0x16, 0xff ),       // 0x16
//	GXPackedRGB5A3( 0x17, 0x17, 0x17, 0xff ),       // 0x17
//	GXPackedRGB5A3( 0x18, 0x18, 0x18, 0xff ),       // 0x18
//	GXPackedRGB5A3( 0x19, 0x19, 0x19, 0xff ),       // 0x19
//	GXPackedRGB5A3( 0x1a, 0x1a, 0x1a, 0xff ),       // 0x1a
//	GXPackedRGB5A3( 0x1b, 0x1b, 0x1b, 0xff ),       // 0x1b
//	GXPackedRGB5A3( 0x1c, 0x1c, 0x1c, 0xff ),       // 0x1c
//	GXPackedRGB5A3( 0x1d, 0x1d, 0x1d, 0xff ),       // 0x1d
//	GXPackedRGB5A3( 0x1e, 0x1e, 0x1e, 0xff ),       // 0x1e
//	GXPackedRGB5A3( 0x1f, 0x1f, 0x1f, 0xff ),       // 0x1f
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),		// 0x20
//	GXPackedRGB5A3( 0x21, 0x21, 0x21, 0xff ),       // 0x21
//	GXPackedRGB5A3( 0x22, 0x22, 0x22, 0xff ),       // 0x22
//	GXPackedRGB5A3( 0x23, 0x23, 0x23, 0xff ),       // 0x23
//	GXPackedRGB5A3( 0x24, 0x24, 0x24, 0xff ),       // 0x24
//	GXPackedRGB5A3( 0x25, 0x25, 0x25, 0xff ),       // 0x25
//	GXPackedRGB5A3( 0x26, 0x26, 0x26, 0xff ),       // 0x26
//	GXPackedRGB5A3( 0x27, 0x27, 0x27, 0xff ),       // 0x27
//	GXPackedRGB5A3( 0x28, 0x28, 0x28, 0xff ),       // 0x28
//	GXPackedRGB5A3( 0x29, 0x29, 0x29, 0xff ),       // 0x29
//	GXPackedRGB5A3( 0x2a, 0x2a, 0x2a, 0xff ),       // 0x2a
//	GXPackedRGB5A3( 0x2b, 0x2b, 0x2b, 0xff ),       // 0x2b
//	GXPackedRGB5A3( 0x2c, 0x2c, 0x2c, 0xff ),       // 0x2c
//	GXPackedRGB5A3( 0x2d, 0x2d, 0x2d, 0xff ),       // 0x2d
//	GXPackedRGB5A3( 0x2e, 0x2e, 0x2e, 0xff ),       // 0x2e
//	GXPackedRGB5A3( 0x2f, 0x2f, 0x2f, 0xff ),       // 0x2f
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),		// 0x30
//	GXPackedRGB5A3( 0x31, 0x31, 0x31, 0xff ),       // 0x31
//	GXPackedRGB5A3( 0x32, 0x32, 0x32, 0xff ),       // 0x32
//	GXPackedRGB5A3( 0x33, 0x33, 0x33, 0xff ),       // 0x33
//	GXPackedRGB5A3( 0x34, 0x34, 0x34, 0xff ),       // 0x34
//	GXPackedRGB5A3( 0x35, 0x35, 0x35, 0xff ),       // 0x35
//	GXPackedRGB5A3( 0x36, 0x36, 0x36, 0xff ),       // 0x36
//	GXPackedRGB5A3( 0x37, 0x37, 0x37, 0xff ),       // 0x37
//	GXPackedRGB5A3( 0x38, 0x38, 0x38, 0xff ),       // 0x38
//	GXPackedRGB5A3( 0x39, 0x39, 0x39, 0xff ),       // 0x39
//	GXPackedRGB5A3( 0x3a, 0x3a, 0x3a, 0xff ),       // 0x3a
//	GXPackedRGB5A3( 0x3b, 0x3b, 0x3b, 0xff ),       // 0x3b
//	GXPackedRGB5A3( 0x3c, 0x3c, 0x3c, 0xff ),       // 0x3c
//	GXPackedRGB5A3( 0x3d, 0x3d, 0x3d, 0xff ),       // 0x3d
//	GXPackedRGB5A3( 0x3e, 0x3e, 0x3e, 0xff ),       // 0x3e
//	GXPackedRGB5A3( 0x3f, 0x3f, 0x3f, 0xff ),       // 0x3f
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),		// 0x40
//	GXPackedRGB5A3( 0x41, 0x41, 0x41, 0xff ),       // 0x41
//	GXPackedRGB5A3( 0x42, 0x42, 0x42, 0xff ),       // 0x42
//	GXPackedRGB5A3( 0x43, 0x43, 0x43, 0xff ),       // 0x43
//	GXPackedRGB5A3( 0x44, 0x44, 0x44, 0xff ),       // 0x44
//	GXPackedRGB5A3( 0x45, 0x45, 0x45, 0xff ),       // 0x45
//	GXPackedRGB5A3( 0x46, 0x46, 0x46, 0xff ),       // 0x46
//	GXPackedRGB5A3( 0x47, 0x47, 0x47, 0xff ),       // 0x47
//	GXPackedRGB5A3( 0x48, 0x48, 0x48, 0xff ),       // 0x48
//	GXPackedRGB5A3( 0x49, 0x49, 0x49, 0xff ),       // 0x49
//	GXPackedRGB5A3( 0x4a, 0x4a, 0x4a, 0xff ),       // 0x4a
//	GXPackedRGB5A3( 0x4b, 0x4b, 0x4b, 0xff ),       // 0x4b
//	GXPackedRGB5A3( 0x4c, 0x4c, 0x4c, 0xff ),       // 0x4c
//	GXPackedRGB5A3( 0x4d, 0x4d, 0x4d, 0xff ),       // 0x4d
//	GXPackedRGB5A3( 0x4e, 0x4e, 0x4e, 0xff ),       // 0x4e
//	GXPackedRGB5A3( 0x4f, 0x4f, 0x4f, 0xff ),       // 0x4f
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),		// 0x50
//	GXPackedRGB5A3( 0x51, 0x51, 0x51, 0xff ),       // 0x51
//	GXPackedRGB5A3( 0x52, 0x52, 0x52, 0xff ),       // 0x52
//	GXPackedRGB5A3( 0x53, 0x53, 0x53, 0xff ),       // 0x53
//	GXPackedRGB5A3( 0x54, 0x54, 0x54, 0xff ),       // 0x54
//	GXPackedRGB5A3( 0x55, 0x55, 0x55, 0xff ),       // 0x55
//	GXPackedRGB5A3( 0x56, 0x56, 0x56, 0xff ),       // 0x56
//	GXPackedRGB5A3( 0x57, 0x57, 0x57, 0xff ),       // 0x57
//	GXPackedRGB5A3( 0x58, 0x58, 0x58, 0xff ),       // 0x58
//	GXPackedRGB5A3( 0x59, 0x59, 0x59, 0xff ),       // 0x59
//	GXPackedRGB5A3( 0x5a, 0x5a, 0x5a, 0xff ),       // 0x5a
//	GXPackedRGB5A3( 0x5b, 0x5b, 0x5b, 0xff ),       // 0x5b
//	GXPackedRGB5A3( 0x5c, 0x5c, 0x5c, 0xff ),       // 0x5c
//	GXPackedRGB5A3( 0x5d, 0x5d, 0x5d, 0xff ),       // 0x5d
//	GXPackedRGB5A3( 0x5e, 0x5e, 0x5e, 0xff ),       // 0x5e
//	GXPackedRGB5A3( 0x5f, 0x5f, 0x5f, 0xff ),       // 0x5f
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),		// 0x60
//	GXPackedRGB5A3( 0x61, 0x61, 0x61, 0xff ),       // 0x61
//	GXPackedRGB5A3( 0x62, 0x62, 0x62, 0xff ),       // 0x62
//	GXPackedRGB5A3( 0x63, 0x63, 0x63, 0xff ),       // 0x63
//	GXPackedRGB5A3( 0x64, 0x64, 0x64, 0xff ),       // 0x64
//	GXPackedRGB5A3( 0x65, 0x65, 0x65, 0xff ),       // 0x65
//	GXPackedRGB5A3( 0x66, 0x66, 0x66, 0xff ),       // 0x66
//	GXPackedRGB5A3( 0x67, 0x67, 0x67, 0xff ),       // 0x67
//	GXPackedRGB5A3( 0x68, 0x68, 0x68, 0xff ),       // 0x68
//	GXPackedRGB5A3( 0x69, 0x69, 0x69, 0xff ),       // 0x69
//	GXPackedRGB5A3( 0x6a, 0x6a, 0x6a, 0xff ),       // 0x6a
//	GXPackedRGB5A3( 0x6b, 0x6b, 0x6b, 0xff ),       // 0x6b
//	GXPackedRGB5A3( 0x6c, 0x6c, 0x6c, 0xff ),       // 0x6c
//	GXPackedRGB5A3( 0x6d, 0x6d, 0x6d, 0xff ),       // 0x6d
//	GXPackedRGB5A3( 0x6e, 0x6e, 0x6e, 0xff ),       // 0x6e
//	GXPackedRGB5A3( 0x6f, 0x6f, 0x6f, 0xff ),       // 0x6f
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),		// 0x70
//	GXPackedRGB5A3( 0x71, 0x71, 0x71, 0xff ),       // 0x71
//	GXPackedRGB5A3( 0x72, 0x72, 0x72, 0xff ),       // 0x72
//	GXPackedRGB5A3( 0x73, 0x73, 0x73, 0xff ),       // 0x73
//	GXPackedRGB5A3( 0x74, 0x74, 0x74, 0xff ),       // 0x74
//	GXPackedRGB5A3( 0x75, 0x75, 0x75, 0xff ),       // 0x75
//	GXPackedRGB5A3( 0x76, 0x76, 0x76, 0xff ),       // 0x76
//	GXPackedRGB5A3( 0x77, 0x77, 0x77, 0xff ),       // 0x77
//	GXPackedRGB5A3( 0x78, 0x78, 0x78, 0xff ),       // 0x78
//	GXPackedRGB5A3( 0x79, 0x79, 0x79, 0xff ),       // 0x79
//	GXPackedRGB5A3( 0x7a, 0x7a, 0x7a, 0xff ),       // 0x7a
//	GXPackedRGB5A3( 0x7b, 0x7b, 0x7b, 0xff ),       // 0x7b
//	GXPackedRGB5A3( 0x7c, 0x7c, 0x7c, 0xff ),       // 0x7c
//	GXPackedRGB5A3( 0x7d, 0x7d, 0x7d, 0xff ),       // 0x7d
//	GXPackedRGB5A3( 0x7e, 0x7e, 0x7e, 0xff ),       // 0x7e
//	GXPackedRGB5A3( 0x7f, 0x7f, 0x7f, 0xff ),       // 0x7f
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),		// 0x80
//	GXPackedRGB5A3( 0x81, 0x81, 0x81, 0xff ),       // 0x81
//	GXPackedRGB5A3( 0x82, 0x82, 0x82, 0xff ),       // 0x82
//	GXPackedRGB5A3( 0x83, 0x83, 0x83, 0xff ),       // 0x83
//	GXPackedRGB5A3( 0x84, 0x84, 0x84, 0xff ),       // 0x84
//	GXPackedRGB5A3( 0x85, 0x85, 0x85, 0xff ),       // 0x85
//	GXPackedRGB5A3( 0x86, 0x86, 0x86, 0xff ),       // 0x86
//	GXPackedRGB5A3( 0x87, 0x87, 0x87, 0xff ),       // 0x87
//	GXPackedRGB5A3( 0x88, 0x88, 0x88, 0xff ),       // 0x88
//	GXPackedRGB5A3( 0x89, 0x89, 0x89, 0xff ),       // 0x89
//	GXPackedRGB5A3( 0x8a, 0x8a, 0x8a, 0xff ),       // 0x8a
//	GXPackedRGB5A3( 0x8b, 0x8b, 0x8b, 0xff ),       // 0x8b
//	GXPackedRGB5A3( 0x8c, 0x8c, 0x8c, 0xff ),       // 0x8c
//	GXPackedRGB5A3( 0x8d, 0x8d, 0x8d, 0xff ),       // 0x8d
//	GXPackedRGB5A3( 0x8e, 0x8e, 0x8e, 0xff ),       // 0x8e
//	GXPackedRGB5A3( 0x8f, 0x8f, 0x8f, 0xff ),       // 0x8f
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),		// 0x90
//	GXPackedRGB5A3( 0x91, 0x91, 0x91, 0xff ),       // 0x91
//	GXPackedRGB5A3( 0x92, 0x92, 0x92, 0xff ),       // 0x92
//	GXPackedRGB5A3( 0x93, 0x93, 0x93, 0xff ),       // 0x93
//	GXPackedRGB5A3( 0x94, 0x94, 0x94, 0xff ),       // 0x94
//	GXPackedRGB5A3( 0x95, 0x95, 0x95, 0xff ),       // 0x95
//	GXPackedRGB5A3( 0x96, 0x96, 0x96, 0xff ),       // 0x96
//	GXPackedRGB5A3( 0x97, 0x97, 0x97, 0xff ),       // 0x97
//	GXPackedRGB5A3( 0x98, 0x98, 0x98, 0xff ),       // 0x98
//	GXPackedRGB5A3( 0x99, 0x99, 0x99, 0xff ),       // 0x99
//	GXPackedRGB5A3( 0x9a, 0x9a, 0x9a, 0xff ),       // 0x9a
//	GXPackedRGB5A3( 0x9b, 0x9b, 0x9b, 0xff ),       // 0x9b
//	GXPackedRGB5A3( 0x9c, 0x9c, 0x9c, 0xff ),       // 0x9c
//	GXPackedRGB5A3( 0x9d, 0x9d, 0x9d, 0xff ),       // 0x9d
//	GXPackedRGB5A3( 0x9e, 0x9e, 0x9e, 0xff ),       // 0x9e
//	GXPackedRGB5A3( 0x9f, 0x9f, 0x9f, 0xff ),       // 0x9f
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),		// 0xa0
//	GXPackedRGB5A3( 0xa1, 0xa1, 0xa1, 0xff ),       // 0xa1
//	GXPackedRGB5A3( 0xa2, 0xa2, 0xa2, 0xff ),       // 0xa2
//	GXPackedRGB5A3( 0xa3, 0xa3, 0xa3, 0xff ),       // 0xa3
//	GXPackedRGB5A3( 0xa4, 0xa4, 0xa4, 0xff ),       // 0xa4
//	GXPackedRGB5A3( 0xa5, 0xa5, 0xa5, 0xff ),       // 0xa5
//	GXPackedRGB5A3( 0xa6, 0xa6, 0xa6, 0xff ),       // 0xa6
//	GXPackedRGB5A3( 0xa7, 0xa7, 0xa7, 0xff ),       // 0xa7
//	GXPackedRGB5A3( 0xa8, 0xa8, 0xa8, 0xff ),       // 0xa8
//	GXPackedRGB5A3( 0xa9, 0xa9, 0xa9, 0xff ),       // 0xa9
//	GXPackedRGB5A3( 0xaa, 0xaa, 0xaa, 0xff ),       // 0xaa
//	GXPackedRGB5A3( 0xab, 0xab, 0xab, 0xff ),       // 0xab
//	GXPackedRGB5A3( 0xac, 0xac, 0xac, 0xff ),       // 0xac
//	GXPackedRGB5A3( 0xad, 0xad, 0xad, 0xff ),       // 0xad
//	GXPackedRGB5A3( 0xae, 0xae, 0xae, 0xff ),       // 0xae
//	GXPackedRGB5A3( 0xaf, 0xaf, 0xaf, 0xff ),       // 0xaf
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),		// 0xb0
//	GXPackedRGB5A3( 0xb1, 0xb1, 0xb1, 0xff ),       // 0xb1
//	GXPackedRGB5A3( 0xb2, 0xb2, 0xb2, 0xff ),       // 0xb2
//	GXPackedRGB5A3( 0xb3, 0xb3, 0xb3, 0xff ),       // 0xb3
//	GXPackedRGB5A3( 0xb4, 0xb4, 0xb4, 0xff ),       // 0xb4
//	GXPackedRGB5A3( 0xb5, 0xb5, 0xb5, 0xff ),       // 0xb5
//	GXPackedRGB5A3( 0xb6, 0xb6, 0xb6, 0xff ),       // 0xb6
//	GXPackedRGB5A3( 0xb7, 0xb7, 0xb7, 0xff ),       // 0xb7
//	GXPackedRGB5A3( 0xb8, 0xb8, 0xb8, 0xff ),       // 0xb8
//	GXPackedRGB5A3( 0xb9, 0xb9, 0xb9, 0xff ),       // 0xb9
//	GXPackedRGB5A3( 0xba, 0xba, 0xba, 0xff ),       // 0xba
//	GXPackedRGB5A3( 0xbb, 0xbb, 0xbb, 0xff ),       // 0xbb
//	GXPackedRGB5A3( 0xbc, 0xbc, 0xbc, 0xff ),       // 0xbc
//	GXPackedRGB5A3( 0xbd, 0xbd, 0xbd, 0xff ),       // 0xbd
//	GXPackedRGB5A3( 0xbe, 0xbe, 0xbe, 0xff ),       // 0xbe
//	GXPackedRGB5A3( 0xbf, 0xbf, 0xbf, 0xff ),       // 0xbf
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),		// 0xc0
//	GXPackedRGB5A3( 0xc1, 0xc1, 0xc1, 0xff ),       // 0xc1
//	GXPackedRGB5A3( 0xc2, 0xc2, 0xc2, 0xff ),       // 0xc2
//	GXPackedRGB5A3( 0xc3, 0xc3, 0xc3, 0xff ),       // 0xc3
//	GXPackedRGB5A3( 0xc4, 0xc4, 0xc4, 0xff ),       // 0xc4
//	GXPackedRGB5A3( 0xc5, 0xc5, 0xc5, 0xff ),       // 0xc5
//	GXPackedRGB5A3( 0xc6, 0xc6, 0xc6, 0xff ),       // 0xc6
//	GXPackedRGB5A3( 0xc7, 0xc7, 0xc7, 0xff ),       // 0xc7
//	GXPackedRGB5A3( 0xc8, 0xc8, 0xc8, 0xff ),       // 0xc8
//	GXPackedRGB5A3( 0xc9, 0xc9, 0xc9, 0xff ),       // 0xc9
//	GXPackedRGB5A3( 0xca, 0xca, 0xca, 0xff ),       // 0xca
//	GXPackedRGB5A3( 0xcb, 0xcb, 0xcb, 0xff ),       // 0xcb
//	GXPackedRGB5A3( 0xcc, 0xcc, 0xcc, 0xff ),       // 0xcc
//	GXPackedRGB5A3( 0xcd, 0xcd, 0xcd, 0xff ),       // 0xcd
//	GXPackedRGB5A3( 0xce, 0xce, 0xce, 0xff ),       // 0xce
//	GXPackedRGB5A3( 0xcf, 0xcf, 0xcf, 0xff ),       // 0xcf
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),		// 0xd0
//	GXPackedRGB5A3( 0xd1, 0xd1, 0xd1, 0xff ),       // 0xd1
//	GXPackedRGB5A3( 0xd2, 0xd2, 0xd2, 0xff ),       // 0xd2
//	GXPackedRGB5A3( 0xd3, 0xd3, 0xd3, 0xff ),       // 0xd3
//	GXPackedRGB5A3( 0xd4, 0xd4, 0xd4, 0xff ),       // 0xd4
//	GXPackedRGB5A3( 0xd5, 0xd5, 0xd5, 0xff ),       // 0xd5
//	GXPackedRGB5A3( 0xd6, 0xd6, 0xd6, 0xff ),       // 0xd6
//	GXPackedRGB5A3( 0xd7, 0xd7, 0xd7, 0xff ),       // 0xd7
//	GXPackedRGB5A3( 0xd8, 0xd8, 0xd8, 0xff ),       // 0xd8
//	GXPackedRGB5A3( 0xd9, 0xd9, 0xd9, 0xff ),       // 0xd9
//	GXPackedRGB5A3( 0xda, 0xda, 0xda, 0xff ),       // 0xda
//	GXPackedRGB5A3( 0xdb, 0xdb, 0xdb, 0xff ),       // 0xdb
//	GXPackedRGB5A3( 0xdc, 0xdc, 0xdc, 0xff ),       // 0xdc
//	GXPackedRGB5A3( 0xdd, 0xdd, 0xdd, 0xff ),       // 0xdd
//	GXPackedRGB5A3( 0xde, 0xde, 0xde, 0xff ),       // 0xde
//	GXPackedRGB5A3( 0xdf, 0xdf, 0xdf, 0xff ),       // 0xdf
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),		// 0xe0
//	GXPackedRGB5A3( 0xe1, 0xe1, 0xe1, 0xff ),       // 0xe1
//	GXPackedRGB5A3( 0xe2, 0xe2, 0xe2, 0xff ),       // 0xe2
//	GXPackedRGB5A3( 0xe3, 0xe3, 0xe3, 0xff ),       // 0xe3
//	GXPackedRGB5A3( 0xe4, 0xe4, 0xe4, 0xff ),       // 0xe4
//	GXPackedRGB5A3( 0xe5, 0xe5, 0xe5, 0xff ),       // 0xe5
//	GXPackedRGB5A3( 0xe6, 0xe6, 0xe6, 0xff ),       // 0xe6
//	GXPackedRGB5A3( 0xe7, 0xe7, 0xe7, 0xff ),       // 0xe7
//	GXPackedRGB5A3( 0xe8, 0xe8, 0xe8, 0xff ),       // 0xe8
//	GXPackedRGB5A3( 0xe9, 0xe9, 0xe9, 0xff ),       // 0xe9
//	GXPackedRGB5A3( 0xea, 0xea, 0xea, 0xff ),       // 0xea
//	GXPackedRGB5A3( 0xeb, 0xeb, 0xeb, 0xff ),       // 0xeb
//	GXPackedRGB5A3( 0xec, 0xec, 0xec, 0xff ),       // 0xec
//	GXPackedRGB5A3( 0xed, 0xed, 0xed, 0xff ),       // 0xed
//	GXPackedRGB5A3( 0xee, 0xee, 0xee, 0xff ),       // 0xee
//	GXPackedRGB5A3( 0xef, 0xef, 0xef, 0xff ),       // 0xef
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),		// 0xf0
//	GXPackedRGB5A3( 0xf1, 0xf1, 0xf1, 0xff ),       // 0xf1
//	GXPackedRGB5A3( 0xf2, 0xf2, 0xf2, 0xff ),       // 0xf2
//	GXPackedRGB5A3( 0xf3, 0xf3, 0xf3, 0xff ),       // 0xf3
//	GXPackedRGB5A3( 0xf4, 0xf4, 0xf4, 0xff ),       // 0xf4
//	GXPackedRGB5A3( 0xf5, 0xf5, 0xf5, 0xff ),       // 0xf5
//	GXPackedRGB5A3( 0xf6, 0xf6, 0xf6, 0xff ),       // 0xf6
//	GXPackedRGB5A3( 0xf7, 0xf7, 0xf7, 0xff ),       // 0xf7
//	GXPackedRGB5A3( 0xf8, 0xf8, 0xf8, 0xff ),       // 0xf8
//	GXPackedRGB5A3( 0xf9, 0xf9, 0xf9, 0xff ),       // 0xf9
//	GXPackedRGB5A3( 0xfa, 0xfa, 0xfa, 0xff ),       // 0xfa
//	GXPackedRGB5A3( 0xfb, 0xfb, 0xfb, 0xff ),       // 0xfb
//	GXPackedRGB5A3( 0xfc, 0xfc, 0xfc, 0xff ),       // 0xfc
//	GXPackedRGB5A3( 0xfd, 0xfd, 0xfd, 0xff ),       // 0xfd
//	GXPackedRGB5A3( 0xfe, 0xfe, 0xfe, 0xff ),       // 0xfe
//	GXPackedRGB5A3( 0xff, 0xff, 0xff, 0xff ),       // 0xff
//};

//uint16 zPaletteH[256] __attribute__ (( aligned( 32 ))) = {
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),		// 0x00
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x01
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x02
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x03
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x04
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x05
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x06
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x07
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x08
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x09
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x0a
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x0b
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x0c
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x0d
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x0e
//	GXPackedRGB5A3( 0xf0, 0xf0, 0xf0, 0xff ),       // 0x0f
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),		// 0x10
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x11
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x12
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x13
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x14
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x15
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x16
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x17
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x18
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x19
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x1a
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x1b
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x1c
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x1d
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x1e
//	GXPackedRGB5A3( 0xe0, 0xe0, 0xe0, 0xff ),       // 0x1f
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),		// 0x20
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x21
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x22
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x23
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x24
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x25
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x26
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x27
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x28
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x29
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x2a
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x2b
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x2c
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x2d
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x2e
//	GXPackedRGB5A3( 0xd0, 0xd0, 0xd0, 0xff ),       // 0x2f
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),		// 0x30
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x31
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x32
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x33
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x34
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x35
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x36
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x37
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x38
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x39
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x3a
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x3b
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x3c
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x3d
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x3e
//	GXPackedRGB5A3( 0xc0, 0xc0, 0xc0, 0xff ),       // 0x3f
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),		// 0x40
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x41
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x42
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x43
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x44
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x45
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x46
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x47
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x48
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x49
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x4a
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x4b
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x4c
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x4d
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x4e
//	GXPackedRGB5A3( 0xb0, 0xb0, 0xb0, 0xff ),       // 0x4f
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),		// 0x50
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x51
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x52
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x53
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x54
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x55
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x56
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x57
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x58
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x59
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x5a
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x5b
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x5c
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x5d
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x5e
//	GXPackedRGB5A3( 0xa0, 0xa0, 0xa0, 0xff ),       // 0x5f
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),		// 0x60
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x61
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x62
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x63
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x64
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x65
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x66
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x67
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x68
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x69
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x6a
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x6b
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x6c
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x6d
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x6e
//	GXPackedRGB5A3( 0x90, 0x90, 0x90, 0xff ),       // 0x6f
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),		// 0x70
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x71
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x72
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x73
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x74
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x75
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x76
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x77
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x78
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x79
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x7a
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x7b
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x7c
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x7d
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x7e
//	GXPackedRGB5A3( 0x80, 0x80, 0x80, 0xff ),       // 0x7f
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),		// 0x80
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x81
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x82
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x83
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x84
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x85
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x86
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x87
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x88
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x89
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x8a
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x8b
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x8c
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x8d
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x8e
//	GXPackedRGB5A3( 0x70, 0x70, 0x70, 0xff ),       // 0x8f
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),		// 0x90
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x91
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x92
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x93
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x94
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x95
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x96
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x97
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x98
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x99
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x9a
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x9b
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x9c
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x9d
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x9e
//	GXPackedRGB5A3( 0x60, 0x60, 0x60, 0xff ),       // 0x9f
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),		// 0xa0
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xa1
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xa2
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xa3
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xa4
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xa5
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xa6
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xa7
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xa8
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xa9
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xaa
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xab
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xac
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xad
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xae
//	GXPackedRGB5A3( 0x50, 0x50, 0x50, 0xff ),       // 0xaf
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),		// 0xb0
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xb1
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xb2
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xb3
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xb4
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xb5
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xb6
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xb7
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xb8
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xb9
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xba
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xbb
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xbc
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xbd
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xbe
//	GXPackedRGB5A3( 0x40, 0x40, 0x40, 0xff ),       // 0xbf
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),		// 0xc0
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xc1
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xc2
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xc3
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xc4
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xc5
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xc6
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xc7
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xc8
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xc9
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xca
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xcb
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xcc
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xcd
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xce
//	GXPackedRGB5A3( 0x30, 0x30, 0x30, 0xff ),       // 0xcf
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),		// 0xd0
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xd1
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xd2
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xd3
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xd4
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xd5
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xd6
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xd7
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xd8
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xd9
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xda
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xdb
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xdc
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xdd
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xde
//	GXPackedRGB5A3( 0x20, 0x20, 0x20, 0xff ),       // 0xdf
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),		// 0xe0
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xe1
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xe2
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xe3
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xe4
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xe5
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xe6
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xe7
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xe8
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xe9
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xea
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xeb
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xec
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xed
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xee
//	GXPackedRGB5A3( 0x10, 0x10, 0x10, 0xff ),       // 0xef
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),		// 0xf0
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf1
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf2
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf3
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf4
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf5
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf6
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf7
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf8
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf9
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xfa
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xfb
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xfc
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xfd
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xfe
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xff
//};
//
//uint16 zPaletteL[256] __attribute__ (( aligned( 32 ))) = {
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),		// 0x00
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x01
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x02
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x03
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x04
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x05
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x06
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x07
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x08
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x09
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x0a
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x0b
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x0c
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x0d
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x0e
//	GXPackedRGB5A3( 0x0f, 0x0f, 0x0f, 0xff ),       // 0x0f
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),		// 0x10
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x11
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x12
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x13
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x14
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x15
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x16
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x17
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x18
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x19
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x1a
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x1b
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x1c
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x1d
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x1e
//	GXPackedRGB5A3( 0x0e, 0x0e, 0x0e, 0xff ),       // 0x1f
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),		// 0x20
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x21
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x22
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x23
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x24
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x25
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x26
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x27
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x28
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x29
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x2a
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x2b
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x2c
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x2d
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x2e
//	GXPackedRGB5A3( 0x0d, 0x0d, 0x0d, 0xff ),       // 0x2f
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),		// 0x30
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x31
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x32
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x33
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x34
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x35
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x36
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x37
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x38
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x39
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x3a
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x3b
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x3c
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x3d
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x3e
//	GXPackedRGB5A3( 0x0c, 0x0c, 0x0c, 0xff ),       // 0x3f
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),		// 0x40
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x41
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x42
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x43
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x44
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x45
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x46
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x47
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x48
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x49
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x4a
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x4b
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x4c
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x4d
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x4e
//	GXPackedRGB5A3( 0x0b, 0x0b, 0x0b, 0xff ),       // 0x4f
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),		// 0x50
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x51
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x52
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x53
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x54
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x55
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x56
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x57
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x58
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x59
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x5a
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x5b
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x5c
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x5d
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x5e
//	GXPackedRGB5A3( 0x0a, 0x0a, 0x0a, 0xff ),       // 0x5f
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),		// 0x60
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x61
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x62
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x63
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x64
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x65
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x66
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x67
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x68
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x69
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x6a
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x6b
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x6c
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x6d
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x6e
//	GXPackedRGB5A3( 0x09, 0x09, 0x09, 0xff ),       // 0x6f
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),		// 0x70
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x71
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x72
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x73
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x74
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x75
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x76
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x77
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x78
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x79
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x7a
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x7b
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x7c
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x7d
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x7e
//	GXPackedRGB5A3( 0x08, 0x08, 0x08, 0xff ),       // 0x7f
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),		// 0x80
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x81
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x82
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x83
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x84
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x85
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x86
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x87
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x88
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x89
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x8a
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x8b
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x8c
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x8d
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x8e
//	GXPackedRGB5A3( 0x07, 0x07, 0x07, 0xff ),       // 0x8f
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),		// 0x90
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x91
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x92
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x93
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x94
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x95
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x96
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x97
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x98
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x99
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x9a
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x9b
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x9c
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x9d
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x9e
//	GXPackedRGB5A3( 0x06, 0x06, 0x06, 0xff ),       // 0x9f
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),		// 0xa0
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xa1
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xa2
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xa3
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xa4
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xa5
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xa6
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xa7
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xa8
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xa9
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xaa
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xab
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xac
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xad
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xae
//	GXPackedRGB5A3( 0x05, 0x05, 0x05, 0xff ),       // 0xaf
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),		// 0xb0
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xb1
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xb2
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xb3
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xb4
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xb5
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xb6
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xb7
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xb8
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xb9
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xba
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xbb
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xbc
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xbd
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xbe
//	GXPackedRGB5A3( 0x04, 0x04, 0x04, 0xff ),       // 0xbf
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),		// 0xc0
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xc1
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xc2
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xc3
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xc4
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xc5
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xc6
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xc7
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xc8
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xc9
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xca
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xcb
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xcc
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xcd
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xce
//	GXPackedRGB5A3( 0x03, 0x03, 0x03, 0xff ),       // 0xcf
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),		// 0xd0
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xd1
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xd2
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xd3
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xd4
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xd5
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xd6
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xd7
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xd8
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xd9
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xda
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xdb
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xdc
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xdd
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xde
//	GXPackedRGB5A3( 0x02, 0x02, 0x02, 0xff ),       // 0xdf
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),		// 0xe0
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xe1
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xe2
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xe3
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xe4
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xe5
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xe6
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xe7
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xe8
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xe9
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xea
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xeb
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xec
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xed
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xee
//	GXPackedRGB5A3( 0x01, 0x01, 0x01, 0xff ),       // 0xef
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),		// 0xf0
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf1
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf2
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf3
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf4
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf5
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf6
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf7
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf8
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xf9
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xfa
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xfb
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xfc
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xfd
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xfe
//	GXPackedRGB5A3( 0x00, 0x00, 0x00, 0xff ),       // 0xff
//};
//
//uint8 zPalette8[256] __attribute__ (( aligned( 32 ))) = {
//	0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
//	0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
//	0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
//	0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
//	0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
//	0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
//	0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
//	0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
//	0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
//	0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
//	0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
//	0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
//	0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
//	0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
//	0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
//	0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
//};
//
//#define _H(a)  (((a<<(8+6))&0xc000)|((a<<6)&0x00c0))
//
//uint16 zPalette8H[256] __attribute__ (( aligned( 32 ))) = {
//	_H(0x00),_H(0x01),_H(0x02),_H(0x03),_H(0x04),_H(0x05),_H(0x06),_H(0x07),_H(0x08),_H(0x09),_H(0x0a),_H(0x0b),_H(0x0c),_H(0x0d),_H(0x0e),_H(0x0f),
//	_H(0x10),_H(0x11),_H(0x12),_H(0x13),_H(0x14),_H(0x15),_H(0x16),_H(0x17),_H(0x18),_H(0x19),_H(0x1a),_H(0x1b),_H(0x1c),_H(0x1d),_H(0x1e),_H(0x1f),
//	_H(0x20),_H(0x21),_H(0x22),_H(0x23),_H(0x24),_H(0x25),_H(0x26),_H(0x27),_H(0x28),_H(0x29),_H(0x2a),_H(0x2b),_H(0x2c),_H(0x2d),_H(0x2e),_H(0x2f),
//	_H(0x30),_H(0x31),_H(0x32),_H(0x33),_H(0x34),_H(0x35),_H(0x36),_H(0x37),_H(0x38),_H(0x39),_H(0x3a),_H(0x3b),_H(0x3c),_H(0x3d),_H(0x3e),_H(0x3f),
//	_H(0x40),_H(0x41),_H(0x42),_H(0x43),_H(0x44),_H(0x45),_H(0x46),_H(0x47),_H(0x48),_H(0x49),_H(0x4a),_H(0x4b),_H(0x4c),_H(0x4d),_H(0x4e),_H(0x4f),
//	_H(0x50),_H(0x51),_H(0x52),_H(0x53),_H(0x54),_H(0x55),_H(0x56),_H(0x57),_H(0x58),_H(0x59),_H(0x5a),_H(0x5b),_H(0x5c),_H(0x5d),_H(0x5e),_H(0x5f),
//	_H(0x60),_H(0x61),_H(0x62),_H(0x63),_H(0x64),_H(0x65),_H(0x66),_H(0x67),_H(0x68),_H(0x69),_H(0x6a),_H(0x6b),_H(0x6c),_H(0x6d),_H(0x6e),_H(0x6f),
//	_H(0x70),_H(0x71),_H(0x72),_H(0x73),_H(0x74),_H(0x75),_H(0x76),_H(0x77),_H(0x78),_H(0x79),_H(0x7a),_H(0x7b),_H(0x7c),_H(0x7d),_H(0x7e),_H(0x7f),
//	_H(0x80),_H(0x81),_H(0x82),_H(0x83),_H(0x84),_H(0x85),_H(0x86),_H(0x87),_H(0x88),_H(0x89),_H(0x8a),_H(0x8b),_H(0x8c),_H(0x8d),_H(0x8e),_H(0x8f),
//	_H(0x90),_H(0x91),_H(0x92),_H(0x93),_H(0x94),_H(0x95),_H(0x96),_H(0x97),_H(0x98),_H(0x99),_H(0x9a),_H(0x9b),_H(0x9c),_H(0x9d),_H(0x9e),_H(0x9f),
//	_H(0xa0),_H(0xa1),_H(0xa2),_H(0xa3),_H(0xa4),_H(0xa5),_H(0xa6),_H(0xa7),_H(0xa8),_H(0xa9),_H(0xaa),_H(0xab),_H(0xac),_H(0xad),_H(0xae),_H(0xaf),
//	_H(0xb0),_H(0xb1),_H(0xb2),_H(0xb3),_H(0xb4),_H(0xb5),_H(0xb6),_H(0xb7),_H(0xb8),_H(0xb9),_H(0xba),_H(0xbb),_H(0xbc),_H(0xbd),_H(0xbe),_H(0xbf),
//	_H(0xc0),_H(0xc1),_H(0xc2),_H(0xc3),_H(0xc4),_H(0xc5),_H(0xc6),_H(0xc7),_H(0xc8),_H(0xc9),_H(0xca),_H(0xcb),_H(0xcc),_H(0xcd),_H(0xce),_H(0xcf),
//	_H(0xd0),_H(0xd1),_H(0xd2),_H(0xd3),_H(0xd4),_H(0xd5),_H(0xd6),_H(0xd7),_H(0xd8),_H(0xd9),_H(0xda),_H(0xdb),_H(0xdc),_H(0xdd),_H(0xde),_H(0xdf),
//	_H(0xe0),_H(0xe1),_H(0xe2),_H(0xe3),_H(0xe4),_H(0xe5),_H(0xe6),_H(0xe7),_H(0xe8),_H(0xe9),_H(0xea),_H(0xeb),_H(0xec),_H(0xed),_H(0xee),_H(0xef),
//	_H(0xf0),_H(0xf1),_H(0xf2),_H(0xf3),_H(0xf4),_H(0xf5),_H(0xf6),_H(0xf7),_H(0xf8),_H(0xf9),_H(0xfa),_H(0xfb),_H(0xfc),_H(0xfd),_H(0xfe),_H(0xff)
//};
//
//#define _L(a)  (((a<<(8-2))&0x3f00)|((a>>2)&0x003f))
////#define _L(a)  ((a<<8)|a)
//
//uint16 zPalette8L[256] __attribute__ (( aligned( 32 ))) = {
//	_L(0x00),_L(0x01),_L(0x02),_L(0x03),_L(0x04),_L(0x05),_L(0x06),_L(0x07),_L(0x08),_L(0x09),_L(0x0a),_L(0x0b),_L(0x0c),_L(0x0d),_L(0x0e),_L(0x0f),
//	_L(0x10),_L(0x11),_L(0x12),_L(0x13),_L(0x14),_L(0x15),_L(0x16),_L(0x17),_L(0x18),_L(0x19),_L(0x1a),_L(0x1b),_L(0x1c),_L(0x1d),_L(0x1e),_L(0x1f),
//	_L(0x20),_L(0x21),_L(0x22),_L(0x23),_L(0x24),_L(0x25),_L(0x26),_L(0x27),_L(0x28),_L(0x29),_L(0x2a),_L(0x2b),_L(0x2c),_L(0x2d),_L(0x2e),_L(0x2f),
//	_L(0x30),_L(0x31),_L(0x32),_L(0x33),_L(0x34),_L(0x35),_L(0x36),_L(0x37),_L(0x38),_L(0x39),_L(0x3a),_L(0x3b),_L(0x3c),_L(0x3d),_L(0x3e),_L(0x3f),
//	_L(0x40),_L(0x41),_L(0x42),_L(0x43),_L(0x44),_L(0x45),_L(0x46),_L(0x47),_L(0x48),_L(0x49),_L(0x4a),_L(0x4b),_L(0x4c),_L(0x4d),_L(0x4e),_L(0x4f),
//	_L(0x50),_L(0x51),_L(0x52),_L(0x53),_L(0x54),_L(0x55),_L(0x56),_L(0x57),_L(0x58),_L(0x59),_L(0x5a),_L(0x5b),_L(0x5c),_L(0x5d),_L(0x5e),_L(0x5f),
//	_L(0x60),_L(0x61),_L(0x62),_L(0x63),_L(0x64),_L(0x65),_L(0x66),_L(0x67),_L(0x68),_L(0x69),_L(0x6a),_L(0x6b),_L(0x6c),_L(0x6d),_L(0x6e),_L(0x6f),
//	_L(0x70),_L(0x71),_L(0x72),_L(0x73),_L(0x74),_L(0x75),_L(0x76),_L(0x77),_L(0x78),_L(0x79),_L(0x7a),_L(0x7b),_L(0x7c),_L(0x7d),_L(0x7e),_L(0x7f),
//	_L(0x80),_L(0x81),_L(0x82),_L(0x83),_L(0x84),_L(0x85),_L(0x86),_L(0x87),_L(0x88),_L(0x89),_L(0x8a),_L(0x8b),_L(0x8c),_L(0x8d),_L(0x8e),_L(0x8f),
//	_L(0x90),_L(0x91),_L(0x92),_L(0x93),_L(0x94),_L(0x95),_L(0x96),_L(0x97),_L(0x98),_L(0x99),_L(0x9a),_L(0x9b),_L(0x9c),_L(0x9d),_L(0x9e),_L(0x9f),
//	_L(0xa0),_L(0xa1),_L(0xa2),_L(0xa3),_L(0xa4),_L(0xa5),_L(0xa6),_L(0xa7),_L(0xa8),_L(0xa9),_L(0xaa),_L(0xab),_L(0xac),_L(0xad),_L(0xae),_L(0xaf),
//	_L(0xb0),_L(0xb1),_L(0xb2),_L(0xb3),_L(0xb4),_L(0xb5),_L(0xb6),_L(0xb7),_L(0xb8),_L(0xb9),_L(0xba),_L(0xbb),_L(0xbc),_L(0xbd),_L(0xbe),_L(0xbf),
//	_L(0xc0),_L(0xc1),_L(0xc2),_L(0xc3),_L(0xc4),_L(0xc5),_L(0xc6),_L(0xc7),_L(0xc8),_L(0xc9),_L(0xca),_L(0xcb),_L(0xcc),_L(0xcd),_L(0xce),_L(0xcf),
//	_L(0xd0),_L(0xd1),_L(0xd2),_L(0xd3),_L(0xd4),_L(0xd5),_L(0xd6),_L(0xd7),_L(0xd8),_L(0xd9),_L(0xda),_L(0xdb),_L(0xdc),_L(0xdd),_L(0xde),_L(0xdf),
//	_L(0xe0),_L(0xe1),_L(0xe2),_L(0xe3),_L(0xe4),_L(0xe5),_L(0xe6),_L(0xe7),_L(0xe8),_L(0xe9),_L(0xea),_L(0xeb),_L(0xec),_L(0xed),_L(0xee),_L(0xef),
//	_L(0xf0),_L(0xf1),_L(0xf2),_L(0xf3),_L(0xf4),_L(0xf5),_L(0xf6),_L(0xf7),_L(0xf8),_L(0xf9),_L(0xfa),_L(0xfb),_L(0xfc),_L(0xfd),_L(0xfe),_L(0xff)
//};
//
//#define _64(a)	((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),\
//				((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),\
//				((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),\
//				((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),\
//				((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),\
//				((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),\
//				((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),\
//				((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a),((a<<8)|a)
//
//#define _I(a)	((a<<8)|a)
//
//uint16 zPalette16[65536] __attribute__ (( aligned( 32 ))) = {
//
//	_64(0x00),_64(0x01),_64(0x02),_64(0x03),_64(0x04),_64(0x05),_64(0x06),_64(0x07),_64(0x08),_64(0x09),_64(0x0a),_64(0x0b),_64(0x0c),_64(0x0d),_64(0x0e),_64(0x0f),
//	_64(0x10),_64(0x11),_64(0x12),_64(0x13),_64(0x14),_64(0x15),_64(0x16),_64(0x17),_64(0x18),_64(0x19),_64(0x1a),_64(0x1b),_64(0x1c),_64(0x1d),_64(0x1e),_64(0x1f),
//	_64(0x20),_64(0x21),_64(0x22),_64(0x23),_64(0x24),_64(0x25),_64(0x26),_64(0x27),_64(0x28),_64(0x29),_64(0x2a),_64(0x2b),_64(0x2c),_64(0x2d),_64(0x2e),_64(0x2f),
//	_64(0x30),_64(0x31),_64(0x32),_64(0x33),_64(0x34),_64(0x35),_64(0x36),_64(0x37),_64(0x38),_64(0x39),_64(0x3a),_64(0x3b),_64(0x3c),_64(0x3d),_64(0x3e),_64(0x3f),
//	_64(0x40),_64(0x41),_64(0x42),_64(0x43),_64(0x44),_64(0x45),_64(0x46),_64(0x47),_64(0x48),_64(0x49),_64(0x4a),_64(0x4b),_64(0x4c),_64(0x4d),_64(0x4e),_64(0x4f),
//	_64(0x50),_64(0x51),_64(0x52),_64(0x53),_64(0x54),_64(0x55),_64(0x56),_64(0x57),_64(0x58),_64(0x59),_64(0x5a),_64(0x5b),_64(0x5c),_64(0x5d),_64(0x5e),_64(0x5f),
//	_64(0x60),_64(0x61),_64(0x62),_64(0x63),_64(0x64),_64(0x65),_64(0x66),_64(0x67),_64(0x68),_64(0x69),_64(0x6a),_64(0x6b),_64(0x6c),_64(0x6d),_64(0x6e),_64(0x6f),
//	_64(0x70),_64(0x71),_64(0x72),_64(0x73),_64(0x74),_64(0x75),_64(0x76),_64(0x77),_64(0x78),_64(0x79),_64(0x7a),_64(0x7b),_64(0x7c),_64(0x7d),_64(0x7e),_64(0x7f),
//	_64(0x80),_64(0x81),_64(0x82),_64(0x83),_64(0x84),_64(0x85),_64(0x86),_64(0x87),_64(0x88),_64(0x89),_64(0x8a),_64(0x8b),_64(0x8c),_64(0x8d),_64(0x8e),_64(0x8f),
//	_64(0x90),_64(0x91),_64(0x92),_64(0x93),_64(0x94),_64(0x95),_64(0x96),_64(0x97),_64(0x98),_64(0x99),_64(0x9a),_64(0x9b),_64(0x9c),_64(0x9d),_64(0x9e),_64(0x9f),
//	_64(0xa0),_64(0xa1),_64(0xa2),_64(0xa3),_64(0xa4),_64(0xa5),_64(0xa6),_64(0xa7),_64(0xa8),_64(0xa9),_64(0xaa),_64(0xab),_64(0xac),_64(0xad),_64(0xae),_64(0xaf),
//	_64(0xb0),_64(0xb1),_64(0xb2),_64(0xb3),_64(0xb4),_64(0xb5),_64(0xb6),_64(0xb7),_64(0xb8),_64(0xb9),_64(0xba),_64(0xbb),_64(0xbc),_64(0xbd),_64(0xbe),_64(0xbf),
//	_64(0xc0),_64(0xc1),_64(0xc2),_64(0xc3),_64(0xc4),_64(0xc5),_64(0xc6),_64(0xc7),_64(0xc8),_64(0xc9),_64(0xca),_64(0xcb),_64(0xcc),_64(0xcd),_64(0xce),_64(0xcf),
//	_64(0xd0),_64(0xd1),_64(0xd2),_64(0xd3),_64(0xd4),_64(0xd5),_64(0xd6),_64(0xd7),_64(0xd8),_64(0xd9),_64(0xda),_64(0xdb),_64(0xdc),_64(0xdd),_64(0xde),_64(0xdf),
//	_64(0xe0),_64(0xe1),_64(0xe2),_64(0xe3),_64(0xe4),_64(0xe5),_64(0xe6),_64(0xe7),_64(0xe8),_64(0xe9),_64(0xea),_64(0xeb),_64(0xec),_64(0xed),_64(0xee),_64(0xef),
//	_64(0xf0),_64(0xf1),_64(0xf2),_64(0xf3),_64(0xf4),_64(0xf5),_64(0xf6),_64(0xf7),_64(0xf8),_64(0xf9),_64(0xfa),_64(0xfb),_64(0xfc),_64(0xfd),_64(0xfe),_64(0xff),
//
//	_64(0x00),_64(0x01),_64(0x02),_64(0x03),_64(0x04),_64(0x05),_64(0x06),_64(0x07),_64(0x08),_64(0x09),_64(0x0a),_64(0x0b),_64(0x0c),_64(0x0d),_64(0x0e),_64(0x0f),
//	_64(0x10),_64(0x11),_64(0x12),_64(0x13),_64(0x14),_64(0x15),_64(0x16),_64(0x17),_64(0x18),_64(0x19),_64(0x1a),_64(0x1b),_64(0x1c),_64(0x1d),_64(0x1e),_64(0x1f),
//	_64(0x20),_64(0x21),_64(0x22),_64(0x23),_64(0x24),_64(0x25),_64(0x26),_64(0x27),_64(0x28),_64(0x29),_64(0x2a),_64(0x2b),_64(0x2c),_64(0x2d),_64(0x2e),_64(0x2f),
//	_64(0x30),_64(0x31),_64(0x32),_64(0x33),_64(0x34),_64(0x35),_64(0x36),_64(0x37),_64(0x38),_64(0x39),_64(0x3a),_64(0x3b),_64(0x3c),_64(0x3d),_64(0x3e),_64(0x3f),
//	_64(0x40),_64(0x41),_64(0x42),_64(0x43),_64(0x44),_64(0x45),_64(0x46),_64(0x47),_64(0x48),_64(0x49),_64(0x4a),_64(0x4b),_64(0x4c),_64(0x4d),_64(0x4e),_64(0x4f),
//	_64(0x50),_64(0x51),_64(0x52),_64(0x53),_64(0x54),_64(0x55),_64(0x56),_64(0x57),_64(0x58),_64(0x59),_64(0x5a),_64(0x5b),_64(0x5c),_64(0x5d),_64(0x5e),_64(0x5f),
//	_64(0x60),_64(0x61),_64(0x62),_64(0x63),_64(0x64),_64(0x65),_64(0x66),_64(0x67),_64(0x68),_64(0x69),_64(0x6a),_64(0x6b),_64(0x6c),_64(0x6d),_64(0x6e),_64(0x6f),
//	_64(0x70),_64(0x71),_64(0x72),_64(0x73),_64(0x74),_64(0x75),_64(0x76),_64(0x77),_64(0x78),_64(0x79),_64(0x7a),_64(0x7b),_64(0x7c),_64(0x7d),_64(0x7e),_64(0x7f),
//	_64(0x80),_64(0x81),_64(0x82),_64(0x83),_64(0x84),_64(0x85),_64(0x86),_64(0x87),_64(0x88),_64(0x89),_64(0x8a),_64(0x8b),_64(0x8c),_64(0x8d),_64(0x8e),_64(0x8f),
//	_64(0x90),_64(0x91),_64(0x92),_64(0x93),_64(0x94),_64(0x95),_64(0x96),_64(0x97),_64(0x98),_64(0x99),_64(0x9a),_64(0x9b),_64(0x9c),_64(0x9d),_64(0x9e),_64(0x9f),
//	_64(0xa0),_64(0xa1),_64(0xa2),_64(0xa3),_64(0xa4),_64(0xa5),_64(0xa6),_64(0xa7),_64(0xa8),_64(0xa9),_64(0xaa),_64(0xab),_64(0xac),_64(0xad),_64(0xae),_64(0xaf),
//	_64(0xb0),_64(0xb1),_64(0xb2),_64(0xb3),_64(0xb4),_64(0xb5),_64(0xb6),_64(0xb7),_64(0xb8),_64(0xb9),_64(0xba),_64(0xbb),_64(0xbc),_64(0xbd),_64(0xbe),_64(0xbf),
//	_64(0xc0),_64(0xc1),_64(0xc2),_64(0xc3),_64(0xc4),_64(0xc5),_64(0xc6),_64(0xc7),_64(0xc8),_64(0xc9),_64(0xca),_64(0xcb),_64(0xcc),_64(0xcd),_64(0xce),_64(0xcf),
//	_64(0xd0),_64(0xd1),_64(0xd2),_64(0xd3),_64(0xd4),_64(0xd5),_64(0xd6),_64(0xd7),_64(0xd8),_64(0xd9),_64(0xda),_64(0xdb),_64(0xdc),_64(0xdd),_64(0xde),_64(0xdf),
//	_64(0xe0),_64(0xe1),_64(0xe2),_64(0xe3),_64(0xe4),_64(0xe5),_64(0xe6),_64(0xe7),_64(0xe8),_64(0xe9),_64(0xea),_64(0xeb),_64(0xec),_64(0xed),_64(0xee),_64(0xef),
//	_64(0xf0),_64(0xf1),_64(0xf2),_64(0xf3),_64(0xf4),_64(0xf5),_64(0xf6),_64(0xf7),_64(0xf8),_64(0xf9),_64(0xfa),_64(0xfb),_64(0xfc),_64(0xfd),_64(0xfe),_64(0xff),
//
//	_64(0x00),_64(0x01),_64(0x02),_64(0x03),_64(0x04),_64(0x05),_64(0x06),_64(0x07),_64(0x08),_64(0x09),_64(0x0a),_64(0x0b),_64(0x0c),_64(0x0d),_64(0x0e),_64(0x0f),
//	_64(0x10),_64(0x11),_64(0x12),_64(0x13),_64(0x14),_64(0x15),_64(0x16),_64(0x17),_64(0x18),_64(0x19),_64(0x1a),_64(0x1b),_64(0x1c),_64(0x1d),_64(0x1e),_64(0x1f),
//	_64(0x20),_64(0x21),_64(0x22),_64(0x23),_64(0x24),_64(0x25),_64(0x26),_64(0x27),_64(0x28),_64(0x29),_64(0x2a),_64(0x2b),_64(0x2c),_64(0x2d),_64(0x2e),_64(0x2f),
//	_64(0x30),_64(0x31),_64(0x32),_64(0x33),_64(0x34),_64(0x35),_64(0x36),_64(0x37),_64(0x38),_64(0x39),_64(0x3a),_64(0x3b),_64(0x3c),_64(0x3d),_64(0x3e),_64(0x3f),
//	_64(0x40),_64(0x41),_64(0x42),_64(0x43),_64(0x44),_64(0x45),_64(0x46),_64(0x47),_64(0x48),_64(0x49),_64(0x4a),_64(0x4b),_64(0x4c),_64(0x4d),_64(0x4e),_64(0x4f),
//	_64(0x50),_64(0x51),_64(0x52),_64(0x53),_64(0x54),_64(0x55),_64(0x56),_64(0x57),_64(0x58),_64(0x59),_64(0x5a),_64(0x5b),_64(0x5c),_64(0x5d),_64(0x5e),_64(0x5f),
//	_64(0x60),_64(0x61),_64(0x62),_64(0x63),_64(0x64),_64(0x65),_64(0x66),_64(0x67),_64(0x68),_64(0x69),_64(0x6a),_64(0x6b),_64(0x6c),_64(0x6d),_64(0x6e),_64(0x6f),
//	_64(0x70),_64(0x71),_64(0x72),_64(0x73),_64(0x74),_64(0x75),_64(0x76),_64(0x77),_64(0x78),_64(0x79),_64(0x7a),_64(0x7b),_64(0x7c),_64(0x7d),_64(0x7e),_64(0x7f),
//	_64(0x80),_64(0x81),_64(0x82),_64(0x83),_64(0x84),_64(0x85),_64(0x86),_64(0x87),_64(0x88),_64(0x89),_64(0x8a),_64(0x8b),_64(0x8c),_64(0x8d),_64(0x8e),_64(0x8f),
//	_64(0x90),_64(0x91),_64(0x92),_64(0x93),_64(0x94),_64(0x95),_64(0x96),_64(0x97),_64(0x98),_64(0x99),_64(0x9a),_64(0x9b),_64(0x9c),_64(0x9d),_64(0x9e),_64(0x9f),
//	_64(0xa0),_64(0xa1),_64(0xa2),_64(0xa3),_64(0xa4),_64(0xa5),_64(0xa6),_64(0xa7),_64(0xa8),_64(0xa9),_64(0xaa),_64(0xab),_64(0xac),_64(0xad),_64(0xae),_64(0xaf),
//	_64(0xb0),_64(0xb1),_64(0xb2),_64(0xb3),_64(0xb4),_64(0xb5),_64(0xb6),_64(0xb7),_64(0xb8),_64(0xb9),_64(0xba),_64(0xbb),_64(0xbc),_64(0xbd),_64(0xbe),_64(0xbf),
//	_64(0xc0),_64(0xc1),_64(0xc2),_64(0xc3),_64(0xc4),_64(0xc5),_64(0xc6),_64(0xc7),_64(0xc8),_64(0xc9),_64(0xca),_64(0xcb),_64(0xcc),_64(0xcd),_64(0xce),_64(0xcf),
//	_64(0xd0),_64(0xd1),_64(0xd2),_64(0xd3),_64(0xd4),_64(0xd5),_64(0xd6),_64(0xd7),_64(0xd8),_64(0xd9),_64(0xda),_64(0xdb),_64(0xdc),_64(0xdd),_64(0xde),_64(0xdf),
//	_64(0xe0),_64(0xe1),_64(0xe2),_64(0xe3),_64(0xe4),_64(0xe5),_64(0xe6),_64(0xe7),_64(0xe8),_64(0xe9),_64(0xea),_64(0xeb),_64(0xec),_64(0xed),_64(0xee),_64(0xef),
//	_64(0xf0),_64(0xf1),_64(0xf2),_64(0xf3),_64(0xf4),_64(0xf5),_64(0xf6),_64(0xf7),_64(0xf8),_64(0xf9),_64(0xfa),_64(0xfb),_64(0xfc),_64(0xfd),_64(0xfe),_64(0xff),
//
//	_64(0x00),_64(0x01),_64(0x02),_64(0x03),_64(0x04),_64(0x05),_64(0x06),_64(0x07),_64(0x08),_64(0x09),_64(0x0a),_64(0x0b),_64(0x0c),_64(0x0d),_64(0x0e),_64(0x0f),
//	_64(0x10),_64(0x11),_64(0x12),_64(0x13),_64(0x14),_64(0x15),_64(0x16),_64(0x17),_64(0x18),_64(0x19),_64(0x1a),_64(0x1b),_64(0x1c),_64(0x1d),_64(0x1e),_64(0x1f),
//	_64(0x20),_64(0x21),_64(0x22),_64(0x23),_64(0x24),_64(0x25),_64(0x26),_64(0x27),_64(0x28),_64(0x29),_64(0x2a),_64(0x2b),_64(0x2c),_64(0x2d),_64(0x2e),_64(0x2f),
//	_64(0x30),_64(0x31),_64(0x32),_64(0x33),_64(0x34),_64(0x35),_64(0x36),_64(0x37),_64(0x38),_64(0x39),_64(0x3a),_64(0x3b),_64(0x3c),_64(0x3d),_64(0x3e),_64(0x3f),
//	_64(0x40),_64(0x41),_64(0x42),_64(0x43),_64(0x44),_64(0x45),_64(0x46),_64(0x47),_64(0x48),_64(0x49),_64(0x4a),_64(0x4b),_64(0x4c),_64(0x4d),_64(0x4e),_64(0x4f),
//	_64(0x50),_64(0x51),_64(0x52),_64(0x53),_64(0x54),_64(0x55),_64(0x56),_64(0x57),_64(0x58),_64(0x59),_64(0x5a),_64(0x5b),_64(0x5c),_64(0x5d),_64(0x5e),_64(0x5f),
//	_64(0x60),_64(0x61),_64(0x62),_64(0x63),_64(0x64),_64(0x65),_64(0x66),_64(0x67),_64(0x68),_64(0x69),_64(0x6a),_64(0x6b),_64(0x6c),_64(0x6d),_64(0x6e),_64(0x6f),
//	_64(0x70),_64(0x71),_64(0x72),_64(0x73),_64(0x74),_64(0x75),_64(0x76),_64(0x77),_64(0x78),_64(0x79),_64(0x7a),_64(0x7b),_64(0x7c),_64(0x7d),_64(0x7e),_64(0x7f),
//	_64(0x80),_64(0x81),_64(0x82),_64(0x83),_64(0x84),_64(0x85),_64(0x86),_64(0x87),_64(0x88),_64(0x89),_64(0x8a),_64(0x8b),_64(0x8c),_64(0x8d),_64(0x8e),_64(0x8f),
//	_64(0x90),_64(0x91),_64(0x92),_64(0x93),_64(0x94),_64(0x95),_64(0x96),_64(0x97),_64(0x98),_64(0x99),_64(0x9a),_64(0x9b),_64(0x9c),_64(0x9d),_64(0x9e),_64(0x9f),
//	_64(0xa0),_64(0xa1),_64(0xa2),_64(0xa3),_64(0xa4),_64(0xa5),_64(0xa6),_64(0xa7),_64(0xa8),_64(0xa9),_64(0xaa),_64(0xab),_64(0xac),_64(0xad),_64(0xae),_64(0xaf),
//	_64(0xb0),_64(0xb1),_64(0xb2),_64(0xb3),_64(0xb4),_64(0xb5),_64(0xb6),_64(0xb7),_64(0xb8),_64(0xb9),_64(0xba),_64(0xbb),_64(0xbc),_64(0xbd),_64(0xbe),_64(0xbf),
//	_64(0xc0),_64(0xc1),_64(0xc2),_64(0xc3),_64(0xc4),_64(0xc5),_64(0xc6),_64(0xc7),_64(0xc8),_64(0xc9),_64(0xca),_64(0xcb),_64(0xcc),_64(0xcd),_64(0xce),_64(0xcf),
//	_64(0xd0),_64(0xd1),_64(0xd2),_64(0xd3),_64(0xd4),_64(0xd5),_64(0xd6),_64(0xd7),_64(0xd8),_64(0xd9),_64(0xda),_64(0xdb),_64(0xdc),_64(0xdd),_64(0xde),_64(0xdf),
//	_64(0xe0),_64(0xe1),_64(0xe2),_64(0xe3),_64(0xe4),_64(0xe5),_64(0xe6),_64(0xe7),_64(0xe8),_64(0xe9),_64(0xea),_64(0xeb),_64(0xec),_64(0xed),_64(0xee),_64(0xef),
//	_64(0xf0),_64(0xf1),_64(0xf2),_64(0xf3),_64(0xf4),_64(0xf5),_64(0xf6),_64(0xf7),_64(0xf8),_64(0xf9),_64(0xfa),_64(0xfb),/*_64(0xfc),_64(0xfd),_64(0xfe),_64(0xff)*/
//
//	_I(0x00),
//	_I(0x01),
//	_I(0x02),
//	_I(0x03),
//	_I(0x04),
//	_I(0x05),
//	_I(0x06),
//	_I(0x07),
//	_I(0x08),
//	_I(0x09),
//	_I(0x0a),
//	_I(0x0b),
//	_I(0x0c),
//	_I(0x0d),
//	_I(0x0e),
//	_I(0x0f),
//	_I(0x10),
//	_I(0x11),
//	_I(0x12),
//	_I(0x13),
//	_I(0x14),
//	_I(0x15),
//	_I(0x16),
//	_I(0x17),
//	_I(0x18),
//	_I(0x19),
//	_I(0x1a),
//	_I(0x1b),
//	_I(0x1c),
//	_I(0x1d),
//	_I(0x1e),
//	_I(0x1f),
//	_I(0x20),
//	_I(0x21),
//	_I(0x22),
//	_I(0x23),
//	_I(0x24),
//	_I(0x25),
//	_I(0x26),
//	_I(0x27),
//	_I(0x28),
//	_I(0x29),
//	_I(0x2a),
//	_I(0x2b),
//	_I(0x2c),
//	_I(0x2d),
//	_I(0x2e),
//	_I(0x2f),
//	_I(0x30),
//	_I(0x31),
//	_I(0x32),
//	_I(0x33),
//	_I(0x34),
//	_I(0x35),
//	_I(0x36),
//	_I(0x37),
//	_I(0x38),
//	_I(0x39),
//	_I(0x3a),
//	_I(0x3b),
//	_I(0x3c),
//	_I(0x3d),
//	_I(0x3e),
//	_I(0x3f),
//	_I(0x40),
//	_I(0x41),
//	_I(0x42),
//	_I(0x43),
//	_I(0x44),
//	_I(0x45),
//	_I(0x46),
//	_I(0x47),
//	_I(0x48),
//	_I(0x49),
//	_I(0x4a),
//	_I(0x4b),
//	_I(0x4c),
//	_I(0x4d),
//	_I(0x4e),
//	_I(0x4f),
//	_I(0x50),
//	_I(0x51),
//	_I(0x52),
//	_I(0x53),
//	_I(0x54),
//	_I(0x55),
//	_I(0x56),
//	_I(0x57),
//	_I(0x58),
//	_I(0x59),
//	_I(0x5a),
//	_I(0x5b),
//	_I(0x5c),
//	_I(0x5d),
//	_I(0x5e),
//	_I(0x5f),
//	_I(0x60),
//	_I(0x61),
//	_I(0x62),
//	_I(0x63),
//	_I(0x64),
//	_I(0x65),
//	_I(0x66),
//	_I(0x67),
//	_I(0x68),
//	_I(0x69),
//	_I(0x6a),
//	_I(0x6b),
//	_I(0x6c),
//	_I(0x6d),
//	_I(0x6e),
//	_I(0x6f),
//	_I(0x70),
//	_I(0x71),
//	_I(0x72),
//	_I(0x73),
//	_I(0x74),
//	_I(0x75),
//	_I(0x76),
//	_I(0x77),
//	_I(0x78),
//	_I(0x79),
//	_I(0x7a),
//	_I(0x7b),
//	_I(0x7c),
//	_I(0x7d),
//	_I(0x7e),
//	_I(0x7f),
//	_I(0x80),
//	_I(0x81),
//	_I(0x82),
//	_I(0x83),
//	_I(0x84),
//	_I(0x85),
//	_I(0x86),
//	_I(0x87),
//	_I(0x88),
//	_I(0x89),
//	_I(0x8a),
//	_I(0x8b),
//	_I(0x8c),
//	_I(0x8d),
//	_I(0x8e),
//	_I(0x8f),
//	_I(0x90),
//	_I(0x91),
//	_I(0x92),
//	_I(0x93),
//	_I(0x94),
//	_I(0x95),
//	_I(0x96),
//	_I(0x97),
//	_I(0x98),
//	_I(0x99),
//	_I(0x9a),
//	_I(0x9b),
//	_I(0x9c),
//	_I(0x9d),
//	_I(0x9e),
//	_I(0x9f),
//	_I(0xa0),
//	_I(0xa1),
//	_I(0xa2),
//	_I(0xa3),
//	_I(0xa4),
//	_I(0xa5),
//	_I(0xa6),
//	_I(0xa7),
//	_I(0xa8),
//	_I(0xa9),
//	_I(0xaa),
//	_I(0xab),
//	_I(0xac),
//	_I(0xad),
//	_I(0xae),
//	_I(0xaf),
//	_I(0xb0),
//	_I(0xb1),
//	_I(0xb2),
//	_I(0xb3),
//	_I(0xb4),
//	_I(0xb5),
//	_I(0xb6),
//	_I(0xb7),
//	_I(0xb8),
//	_I(0xb9),
//	_I(0xba),
//	_I(0xbb),
//	_I(0xbc),
//	_I(0xbd),
//	_I(0xbe),
//	_I(0xbf),
//	_I(0xc0),
//	_I(0xc1),
//	_I(0xc2),
//	_I(0xc3),
//	_I(0xc4),
//	_I(0xc5),
//	_I(0xc6),
//	_I(0xc7),
//	_I(0xc8),
//	_I(0xc9),
//	_I(0xca),
//	_I(0xcb),
//	_I(0xcc),
//	_I(0xcd),
//	_I(0xce),
//	_I(0xcf),
//	_I(0xd0),
//	_I(0xd1),
//	_I(0xd2),
//	_I(0xd3),
//	_I(0xd4),
//	_I(0xd5),
//	_I(0xd6),
//	_I(0xd7),
//	_I(0xd8),
//	_I(0xd9),
//	_I(0xda),
//	_I(0xdb),
//	_I(0xdc),
//	_I(0xdd),
//	_I(0xde),
//	_I(0xdf),
//	_I(0xe0),
//	_I(0xe1),
//	_I(0xe2),
//	_I(0xe3),
//	_I(0xe4),
//	_I(0xe5),
//	_I(0xe6),
//	_I(0xe7),
//	_I(0xe8),
//	_I(0xe9),
//	_I(0xea),
//	_I(0xeb),
//	_I(0xec),
//	_I(0xed),
//	_I(0xee),
//	_I(0xef),
//	_I(0xf0),
//	_I(0xf1),
//	_I(0xf2),
//	_I(0xf3),
//	_I(0xf4),
//	_I(0xf5),
//	_I(0xf6),
//	_I(0xf7),
//	_I(0xf8),
//	_I(0xf9),
//	_I(0xfa),
//	_I(0xfb),
//	_I(0xfc),
//	_I(0xfd),
//	_I(0xfe),
//	_I(0xff),
//};

//static uint16 sBlurBuffer[640*448];

namespace NxNgc
{

	// extern void	test_render(Mth::Matrix* camera_orient, Mth::Vector* camera_pos);
	// extern void	test_init();			

} // namespace NxNgc


namespace Nx
{

//	    // Set camera configuration
//	    sc->light.cam.cfg = ( sc->projMode ) ?
//	                        DefaultLightCamera1 : DefaultLightCamera0;
//	    SetCamera(&sc->light.cam);
//	    
//	    // Light camera
//	    sc->projMode           = 0;
//	    sc->light.cam.theta    = 0;
//	    sc->light.cam.phi      = 60;
//	    sc->light.cam.distance = 3000.0F;
//	
//	
//		static void SetCamera( MyCameraObj* cam )
//		{
//			f32  r_theta, r_phi;
//			
//			r_theta = (f32)cam->theta * PI / 180.0F;
//			r_phi   = (f32)cam->phi   * PI / 180.0F;
//		
//			cam->cfg.location.x =
//				cam->distance * cosf(r_theta) * cosf(r_phi);
//			cam->cfg.location.y =
//				cam->distance * sinf(r_theta) * cosf(r_phi);
//			cam->cfg.location.z =
//				cam->distance * sinf(r_phi);
//		
//			MTXLookAt(
//				cam->view,
//				&cam->cfg.location,
//				&cam->cfg.up,
//				&cam->cfg.target );    
//		
//			if ( cam->cfg.type == GX_PERSPECTIVE )
//			{
//				MTXFrustum(
//					cam->proj,
//					cam->cfg.top,
//					- (cam->cfg.top),
//					cam->cfg.left,
//					- (cam->cfg.left),
//					cam->cfg.znear,
//					cam->cfg.zfar );
//			}
//			else // ( cam->cfg.type == GX_ORTHOGRAPHIC )
//			{
//				MTXOrtho(
//					cam->proj,
//					cam->cfg.top,
//					- (cam->cfg.top),
//					cam->cfg.left,
//					- (cam->cfg.left),
//					cam->cfg.znear,
//					cam->cfg.zfar );
//			}
//			
//			GXSetProjection(cam->proj, cam->cfg.type);
//		}
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_start_engine( void )
{
    uint32	size;

	NxNgc::InitialiseEngine();

	
	mp_particle_manager = new CNgcNewParticleManager;

//	Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
//
//	// Allocate memory for shadow map
	size = GX::GetTexBufferSize( SHADOW_TEXTURE_SIZE, SHADOW_TEXTURE_SIZE, GX_TF_I4, GX_FALSE, 0 );
	Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
    shadowTextureData = new uint8[size];
	Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
//
//	size = GXGetTexBufferSize( 320, 224, GX_TF_Z8, GX_FALSE, 0 );
//	zTextureDataH = new uint8[size];
//	zTextureDataL = new uint8[size];
//
//	size = GXGetTexBufferSize( 320, 224, GX_TF_RGBA8, GX_FALSE, 0 );
//	screenTextureData = new uint8[size];
//
//	size = GXGetTexBufferSize( 320, 224, GX_TF_RGBA8, GX_FALSE, 0 );
//	focusTextureData = new uint8[size];
//
//	size = GXGetTexBufferSize( BLUR_TEXTURE_SIZE, BLUR_TEXTURE_SIZE, GX_TF_I4, GX_FALSE, 0 );
//    blurTextureData = new uint8[size];
//
//	size = GXGetTexBufferSize( 640, 448, GX_TF_A8, GX_FALSE, 0 );
//	volumeTextureData = new uint8[size];
//
//
//	Mem::Manager::sHandle().BottomUpHeap()->PopAlign();

	if( NxNgc::EngineGlobals.use_widescreen )
	{
		Script::RunScript( "screen_setup_widescreen" );
	}
	mp_weather = new CNgcWeather;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_pre_render( void )
{
	//NsDisplay::begin();
	//NsRender::begin();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_post_render( void )
{
	//NsRender::end();
	//NsDisplay::end();
//	D3DDevice_Swap( D3DSWAP_DEFAULT );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_render_world( void )
{
	if ( gLoadingScreenActive ) return;

	NsBuffer::begin();

	g_object = 0;
	g_view_scene = NULL;
//	if ( gPrintMatStats )
//	{
//		gPrintMatStats = false;
//
//		char *blend_text[] =
//		{
//			"DIFFUSE        ",
//			"ADD            ",
//			"ADD_FIXED      ",
//			"SUBTRACT       ",
//			"SUB_FIXED      ",
//			"BLEND          ",
//			"BLEND_FIXED    ",
//			"MODULATE       ",
//			"MODULATE_FIXED ",
//			"BRIGHTEN       ",
//			"BRIGHTEN_FIXED ",
//		};
//
//		OSReport( "Unique blend modes: %d\n\n", num_u_mat );
//
//		for ( int um = 0; um < num_u_mat; um++ )
//		{
//			OSReport( "%d: (%4d) ", p_u_mat[um]->m_passes, u_mat_count[um] );
//
//			NxNgc::sMaterialPassHeader * p_u_pass = (NxNgc::sMaterialPassHeader *)&p_u_mat[um][1];
//			for ( int p = 0; p < p_u_mat[um]->m_passes; p++ )
//			{
//				if ( p_u_pass[p].m_blend_mode <= 10 )
//				{
//					OSReport( "%s", blend_text[p_u_pass[p].m_blend_mode] );
//				}
//				else
//				{
//					OSReport( "      " );
//				}
//			}
//			OSReport( "\n" );
//		}
//		
//	}
	
	NsCamera cam;
	Mtx light;
	MTXIdentity( light );

	NxNgc::EngineGlobals.gpuBusy = true;

#ifndef __NOPT_FINAL__
	if ( gDumpHeap )
	{
		Mem::Manager& mem_man = Mem::Manager::sHandle();
	
		OSReport ("MEM CONTEXT: %s\n",Mem::Manager::sHandle().GetContextName());
		OSReport("Name            Used  Frag  Free   Blocks\n");
		OSReport("--------------- ----- ----- ------ ------\n");
		Mem::Heap* heap;
		for (heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
		{		
				Mem::Region* region = heap->ParentRegion();			
				OSReport( "%12s: %6dK %6dK %6dK   %6d \n",
						heap->GetName(),
						heap->mUsedMem.m_count / 1024,
						heap->mFreeMem.m_count / 1024,
						region->MemAvailable() / 1024,
						heap->mUsedBlocks.m_count
						);										
		}
		gDumpHeap = false;
	}
#endif		// __NOPT_FINAL__

//	NsPrim::end();
//	NsRender::end();
//
////	if ( movies )
////	{
////		NsDisplay::end( false );
////	}
////	else
////	{
//		NsDisplay::end( true );
////	}


	NxNgc::process_instances();

	NsDisplay::begin();
//	NsDisplay::end();
//	NsDisplay::begin();


//	// Create color map if necessary.
//	if ( !color_map_created )
//	{
//		color_map_created = true;
//
//		for ( int y = 0; y < COLOR_MAP_SIZE; y++ )
//		{
//			for ( int x = 0; x < COLOR_MAP_SIZE; x++ )
//			{
//				int r;
//				int g;
//
//				r = 0;
//				r += ( x / 4 ) * 32;
//				r += ( x & 3 );
//				r += ( ( y / 4 ) * ( COLOR_MAP_SIZE * 4 * 2 ) );
//				r += ( ( y & 3 ) * 4 );
//
//				g = 16;
//				g += ( x / 4 ) * 32;
//				g += ( x & 3 );
//				g += ( ( y / 4 ) * ( COLOR_MAP_SIZE * 4 * 2 ) );
//				g += ( ( y & 3 ) * 4 );
//
//				// Red across.
//				colorMap[r] = ( x * 256 ) / COLOR_MAP_SIZE;
//				// Green down.
//				colorMap[g] = ( ( ( y * 256 ) / COLOR_MAP_SIZE ) << 8 );
//			}
//		}
//	}




	// Clear to black with alpha of 124.
	// This is important as the shadow volume stuff uses the alpha channel as a stencil buffer.
	// With destination alpha, the RGBA channels each have 6 bits, so they are only accurate to multiples of 4.
	// To save having to copy the alpha map to a texture, we do a 2-pass full-screen polygon.
	// 1. And the alpha channel with 128. As it was set to 124, anything positive will have bit (128) set.
	// 2. Blend alpha with framebuffer.
//	NsDisplay::setBackgroundColor( (GXColor){0,0,0,124} );
	NsDisplay::setBackgroundColor( (GXColor){0,0,0,255} );
	NsRender::begin();
	NsPrim::begin();

//	cam.orthographic( 0, 0, 640, 448 );
//	cam.begin();
//
//	GX::SetNumTevStages(1);
//	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
//	GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetNumTexGens( 0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//	GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//	GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//	GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE, GX_TRUE, GX_FALSE, GX_FALSE );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
//	GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
//	GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//	GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//	GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//
//	NsPrim::quad(   0.0f,   0.0f, -9999.0f,
//				  640.0f,   0.0f, -9999.0f,
//				  640.0f, 448.0f, -9999.0f,
//				    0.0f, 448.0f, -9999.0f,
//				  (GXColor){128,128,128,0} );
//
//	cam.end();

//	bool movies = Flx::Movie_Render();

	//--------------------------------------------------------------
	//--------------------------------------------------------------
	//--------------------------------------------------------------

#	ifdef __USE_PROFILER__
	Sys::VUProfiler->PushContext( 128,128,0 );
#	endif		// __USE_PROFILER__
	if( sp_loaded_scenes[0] != NULL )
	{
		GX::SetZMode ( GX_FALSE, GX_ALWAYS, GX_FALSE );
	



	//	// 8bit mode
	//	shCpFmt = GX_TF_Z8;
	//	shFmt   = GX_TF_I8;
	//
		//-------------------------------------------
		//  1st. pass
		//  Make an image viewed from the light
		//-------------------------------------------
	
		// Color update is disabled. Only Z will be updated.
	//		GXSetColorUpdate(GX_DISABLE);
	
		// To draw "second" surfaces from the light
		//GX::SetCullMode(GX_CULL_FRONT);
	
		// Set viewport for making shadow texture
	//		GXSetViewport(0, 0, SHADOW_TEXTURE_SIZE, SHADOW_TEXTURE_SIZE, 0.0F, 1.0F);
	//		GXSetScissor(0, 0, (u32)SHADOW_TEXTURE_SIZE, (u32)SHADOW_TEXTURE_SIZE);
	
		// Set up the camera..
		//NxNgc::set_camera( &( cur_camera->GetMatrix()), &( cur_camera->GetPos()), cur_camera->GetAdjustedHFOV(), p_cur_viewport->GetAspectRatio());
	
		// Set render mode to use only constant color
		// because we need only depth buffer
	//		GXSetNumChans(1);
		GX::SetChanCtrl(
			GX_COLOR0A0,
			GX_DISABLE,    // disable channel
			GX_SRC_REG,    // amb source
			GX_SRC_REG,    // mat source
			GX_LIGHT_NULL, // light mask
			GX_DF_CLAMP,   // diffuse function
			GX_AF_NONE );
	
		GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE );
		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV,
											   GX_TEV_SWAP0, GX_TEV_SWAP0 );
		GX::SetTevColorInOp( GX_TEVSTAGE0,	   GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC,
											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, 1, GX_TEVPREV );
		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
	
	
		// Draw models
	//		MTXCopy(sc->light.cam.view, tmo.view);
	//		DrawModels(&tmo, sc->modelRot);
	//	
//		NxNgc::render_instances( true );
		NxNgc::EngineGlobals.poly_culling = false;
		NxNgc::render_shadow_targets();
	
	






		// Draw line around border.
		cam.orthographic( 0, 0, 640, 448 );
		cam.begin();

		GX::SetPointSize( 6, GX_TO_ONE );
		GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE );
		GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
		GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );
		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);

		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV,
											   GX_TEV_SWAP0, GX_TEV_SWAP0 );

		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
										   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
		GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){0,0,0,255} );

		GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );

		for ( int vvv = 0; vvv < 8; vvv++ )
		{
			float min = -1.0f + ( (float)vvv / 4.0f );
			float max = (float)SHADOW_TEXTURE_SIZE - ( (float)vvv / 4.0f );
			GX::Begin( GX_LINESTRIP, GX_VTXFMT0, 5 ); 
				GX::Position3f32(min, min, -1.0f);
				GX::Position3f32(max, min, -1.0f);
				GX::Position3f32(max, max, -1.0f);
				GX::Position3f32(min, max, -1.0f);
				GX::Position3f32(min, min, -1.0f);
			GX::End();
		}
		cam.end();









	//		// Copy shadow image into texture
	//		GXSetTexCopySrc(0, 0, SHADOW_TEXTURE_SIZE, SHADOW_TEXTURE_SIZE);
	//		GXSetTexCopyDst(SHADOW_TEXTURE_SIZE, SHADOW_TEXTURE_SIZE, GX_TF_Z16, GX_FALSE);
	//		GXCopyTex(zTextureData, GX_FALSE);
	//
	//		// Wait for finishing the copy task in the graphics pipeline
	//		GXPixModeSync();
	
//		// Draw line around border.
//
//		cam.orthographic( 0, 0, 640, 448 );
//		cam.begin();
//
//		GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE );
//		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
//											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV,
//											   GX_TEV_SWAP0, GX_TEV_SWAP0 );
//		GX::SetTevColorInOp( GX_TEVSTAGE0,	   GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC,
//											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//		GX::SetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL );
//
//		GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){128,128,128,255} );
//		GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//	
//		GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
//	
//		for ( int vvv = 0; vvv < 8; vvv++ )
//		{
//			float min = -1.0f + ( (float)vvv / 4.0f );
//			float max = (float)SHADOW_TEXTURE_SIZE - ( (float)vvv / 4.0f );
//			GX::Begin( GX_LINESTRIP, GX_VTXFMT0, 5 ); 
//				GX::Position3f32(min, min, -1.0f);
//				GX::Position3f32(max, min, -1.0f);
//				GX::Position3f32(max, max, -1.0f);
//				GX::Position3f32(min, max, -1.0f);
//				GX::Position3f32(min, min, -1.0f);
//			GX::End();
//		}


		// Copy shadow image into texture
		GX::SetTexCopySrc(0, 0, SHADOW_TEXTURE_SIZE, SHADOW_TEXTURE_SIZE);
		GX::SetTexCopyDst(SHADOW_TEXTURE_SIZE, SHADOW_TEXTURE_SIZE, GX_CTF_R4, GX_FALSE);
		GX::CopyTex(shadowTextureData, GX_FALSE);
	
		// Wait for finishing the copy task in the graphics pipeline
		GX::PixModeSync();
	
		// Enable color update
	//		GXSetColorUpdate(GX_ENABLE);
	
		// Restore culling mode to normal
		//GX::SetCullMode(GX_CULL_BACK);
	
	
	
	
	
	
	
	
	
//		//--------------------------------------------------------------
//		//--------------------------------------------------------------
//		//--------------------------------------------------------------
//	
//		// Horrible hack - this should be somewhere else ASAP.
//	
//	
//		GX::UploadTexture( shadowTextureData,
//						   SHADOW_TEXTURE_SIZE,
//						   SHADOW_TEXTURE_SIZE,
//						   GX_TF_C4,
//						   GX_CLAMP,
//						   GX_CLAMP,
//						   GX_FALSE,
//						   GX_LINEAR,
//						   GX_LINEAR,
//						   0.0f,
//						   0.0f,
//						   0.0f,
//						   GX_FALSE,
//						   GX_FALSE,
//						   GX_ANISO_1,
//						   GX_TEXMAP0 );
//
//		GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, SHADOW_TEXTURE_SIZE, SHADOW_TEXTURE_SIZE );
//
//		GX::UploadPalette( &shadowPalette,
//						   GX_TL_RGB5A3,
//						   GX_TLUT_16,
//						   GX_TEXMAP0 ); 
//
//
//		GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
//		GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );
//	
//		GX::SetTexChanTevIndCull( 1, 1, 1, 0, GX_CULL_NONE );
//		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
//
//		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO,
//								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV,
//								 GX_TEV_SWAP0, GX_TEV_SWAP0 );
//		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_RASC, GX_CC_ZERO,
//							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//
//		GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){128,128,128,255} );
//		GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//	
//		Mtx mv;
//		MTXIdentity( mv );
//		GX::LoadTexMtxImm(mv, GX_TEXMTX0, GX_MTX3x4);
//	
//		// Set current vertex descriptor to enable position and color0.
//		// Both use 8b index to access their data arrays.
//		GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
//	
//		// Send coordinates.
//	//		GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
//	//			GXPosition3f32(-BLUR_BORDER, -BLUR_BORDER, -1.0f);
//	//			GXTexCoord2f32(0.0f, 0.0f);
//	//			GXPosition3f32(BLUR_TEXTURE_SIZE+BLUR_BORDER, -BLUR_BORDER, -1.0f);
//	//			GXTexCoord2f32(1.0f, 0.0f);
//	//			GXPosition3f32(BLUR_TEXTURE_SIZE+BLUR_BORDER, BLUR_TEXTURE_SIZE+BLUR_BORDER, -1.0f);
//	//			GXTexCoord2f32(1.0f, 1.0f);
//	//			GXPosition3f32(-BLUR_BORDER, BLUR_TEXTURE_SIZE+BLUR_BORDER, -1.0f);
//	//			GXTexCoord2f32(0.0f, 1.0f);
//	//		GXEnd();
//	
//	
//	
//		GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
//			GX::Position3f32(0.0f, 0.0f, -1.0f);
//			GX::TexCoord2f32(BLUR_0, BLUR_0);
//			GX::Position3f32(BLUR_TEXTURE_SIZE, 0.0f, -1.0f);
//			GX::TexCoord2f32(BLUR_1, BLUR_0);
//			GX::Position3f32(BLUR_TEXTURE_SIZE, BLUR_TEXTURE_SIZE, -1.0f);
//			GX::TexCoord2f32(BLUR_1, BLUR_1);
//			GX::Position3f32(0.0f, BLUR_TEXTURE_SIZE, -1.0f);
//			GX::TexCoord2f32(BLUR_0, BLUR_1);
//		GX::End();
//	
//		// Draw line around blur texture.
//		GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE );
//		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
//
//		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
//								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV,
//								 GX_TEV_SWAP0, GX_TEV_SWAP0 );
//
//		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC,
//							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//
//		GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){128,128,128,255} );
//		GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//	
//		GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
//	
//		GX::Begin( GX_LINESTRIP, GX_VTXFMT0, 5 ); 
//			GX::Position3f32(0.0f, 0.0f, -1.0f);
//			GX::Position3f32(BLUR_TEXTURE_SIZE-1, 0.0f, -1.0f);
//			GX::Position3f32(BLUR_TEXTURE_SIZE-1, BLUR_TEXTURE_SIZE-1, -1.0f);
//			GX::Position3f32(0.0f, BLUR_TEXTURE_SIZE-1, -1.0f);
//			GX::Position3f32(0.0f, 0.0f, -1.0f);
//		GX::End();
//
//		// Copy blur image into texture
//		GX::SetTexCopySrc(0, 0, BLUR_TEXTURE_SIZE, BLUR_TEXTURE_SIZE);
//		GX::SetTexCopyDst(BLUR_TEXTURE_SIZE, BLUR_TEXTURE_SIZE, GX_CTF_R4, GX_FALSE);
//		GX::CopyTex(blurTextureData, GX_TRUE);
//	
//		// Wait for finishing the copy task in the graphics pipeline
//		GX::PixModeSync();
//	
//	
//	//		// Clear screen to black.
//	//		GX::SetNumTevStages(1);
//	//		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
//	//		GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	//		GX::SetNumTexGens( 0 );
//	//		GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//	//		GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//	//		GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//	//		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//	//		GX::SetTevKColorSel( GX_TEVSTAGE0, GX_TEV_KCSEL_K0 );
//	//		GX::SetTevKAlphaSel( GX_TEVSTAGE0, GX_TEV_KASEL_K0_A );
//	//		GX::SetTevKColor( GX_KCOLOR0, (GXColor){0,0,0,255} );
//	//		GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_KONST );
//	//		GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_KONST );
//	//
//	//		GXSetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
//	//
//	//		// Send coordinates.
//	//		GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
//	//			GXPosition3f32(0.0f,   0.0f,   -50000.0f);
//	//			GXPosition3f32(640.0f, 0.0f,   -50000.0f);
//	//			GXPosition3f32(640.0f, 480.0f, -50000.0f);
//	//			GXPosition3f32(0.0f,   480.0f, -50000.0f);
//	//		GXEnd();
//	
//		cam.end();

		// Clear to bg color.
		cam.orthographic( 0, 0, 640, 448 );
		cam.begin();

		GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE );
		GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
		GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );
		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);

		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV,
											   GX_TEV_SWAP0, GX_TEV_SWAP0 );

		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
										   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
		GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255} );

		GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );

		if ( g_in_cutscene )
		{
			// Bars must be black.
			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){0,0,0,255} );
			GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
				GX::Position3f32( -4.0f, -4.0f, -1.0f );
				GX::Position3f32( -4.0f+648.0f, -4.0f, -1.0f );
				GX::Position3f32( -4.0f+648.0f, -4.0f+488.0f, -1.0f );
				GX::Position3f32( -4.0f, -4.0f+488.0f, -1.0f );
			GX::End();

			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){0x50,0x60,0x70,255} );
			GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
				GX::Position3f32( -4.0f, 56.0f, -1.0f );
				GX::Position3f32( -4.0f+648.0f, 56.0f, -1.0f );
				GX::Position3f32( -4.0f+648.0f, 336.0f, -1.0f );
				GX::Position3f32( -4.0f, 336.0f, -1.0f );
			GX::End();
		}
		else
		{
			// Set to sky color.
			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){0x50,0x60,0x70,255} );
			GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
				GX::Position3f32( -4.0f, -4.0f, -1.0f );
				GX::Position3f32( -4.0f+648.0f, -4.0f, -1.0f );
				GX::Position3f32( -4.0f+648.0f, -4.0f+488.0f, -1.0f );
				GX::Position3f32( -4.0f, -4.0f+488.0f, -1.0f );
			GX::End();
		}
		cam.end();
	}
#	ifdef __USE_PROFILER__
	Sys::VUProfiler->PopContext();
#	endif		// __USE_PROFILER__

	//--------------------------------------------------------------
	//--------------------------------------------------------------
	//--------------------------------------------------------------
	int rendered = 0;
	int considered = 0;
	meshes_considered = 0;

	Nx::CFog::sFogUpdate();

	// Note: this method of setting up the camera must change
	// so that the p_nx module does not reference things higher up the hierarchy.
	int num_viewports = CViewportManager::sGetNumActiveViewports();
	for( int v = 0; v < num_viewports; ++v )
	{
		NxNgc::render_begin();

		NxNgc::EngineGlobals.viewport = ( 1 << v );
		CViewport *p_cur_viewport = CViewportManager::sGetActiveViewport( v );
		Gfx::Camera *cur_camera = NULL;
		if ( p_cur_viewport ) cur_camera = p_cur_viewport->GetCamera();

#	ifdef __USE_PROFILER__
		Sys::VUProfiler->PushContext( 0,128,0 );
#	endif		// __USE_PROFILER__
		if( cur_camera && ( sp_loaded_scenes[0] != NULL ))
		{
//			NxNgc::EngineGlobals.viewport.X		= (DWORD)( p_cur_viewport->GetOriginX() * NxNgc::EngineGlobals.backbuffer_width );
//			NxNgc::EngineGlobals.viewport.Y		= (DWORD)( p_cur_viewport->GetOriginY() * NxNgc::EngineGlobals.backbuffer_height );
//			NxNgc::EngineGlobals.viewport.Width	= (DWORD)( p_cur_viewport->GetWidth() * NxNgc::EngineGlobals.backbuffer_width );
//			NxNgc::EngineGlobals.viewport.Height	= (DWORD)( p_cur_viewport->GetHeight() * NxNgc::EngineGlobals.backbuffer_height );
//			NxNgc::EngineGlobals.viewport.MinZ		= 0.0f;
//			NxNgc::EngineGlobals.viewport.MaxZ		= 1.0f;
//			D3DDevice_SetViewport( &NxNgc::EngineGlobals.viewport );

			float vx = p_cur_viewport->GetOriginX() * 640.0f;
			float vy = p_cur_viewport->GetOriginY() * 448.0f;
			float vw = p_cur_viewport->GetWidth() * 640.0f;
			float vh = p_cur_viewport->GetHeight() * 448.0f;
			
			if( NxNgc::EngineGlobals.letterbox_active )
			{
				vy += 448.0f / 8.0f;
				vh -= 448.0f / 4.0f;
			}

            GX::SetViewport( vx, vy, vw, vh, 0.0f, 1.0f );
            GX::SetScissor( (u32)vx, (u32)vy, (u32)vw, (u32)vh );

			NxNgc::set_camera( &( cur_camera->GetMatrix()), &( cur_camera->GetPos()), cur_camera->GetAdjustedHFOV(), p_cur_viewport->GetAspectRatio());

			// There is no bounding box transform for rendering the world.
	//		NxNgc::set_frustum_bbox_transform( NULL );

			// Render objects of interest for the render target (shadow objects).
	//		NxNgc::render_shadow_targets();

			// Render the world. At this stage, non-sky scenes are rendered using just the opaque sections.

			GX::LoadNrmMtxImm( light, GX_PNMTX0);
			for( int i = 0; i < MAX_LOADED_SCENES; i++ )
			{
				if( sp_loaded_scenes[i] )
				{
					CNgcScene *pNgcScene = static_cast( sp_loaded_scenes[i] );

					// If this is a sky scene, disable z buffer reads and writes, otherwise enable them.
					if( pNgcScene->IsSky())
					{
						NxNgc::EngineGlobals.poly_culling = false;
						// Set up the camera.
						Mth::Vector	centre_pos( 0.0f, 0.0f, 0.0f );
						NxNgc::set_camera( &( cur_camera->GetMatrix()), ¢re_pos, cur_camera->GetAdjustedHFOV(), p_cur_viewport->GetAspectRatio(), ( 30000.0f / pNgcScene->GetEngineScene()->m_sphere[W] ) );
	
						GX::SetZMode ( GX_FALSE, GX_ALWAYS, GX_FALSE );
	
						uint32 flags = ( NxNgc::vRENDER_OPAQUE | NxNgc::vRENDER_SEMITRANSPARENT | NxNgc::vRENDER_LIT );

						NxNgc::make_scene_visible( pNgcScene->GetEngineScene() );
						NxNgc::render_scene( pNgcScene->GetEngineScene(), NULL, flags );
					}
					else
					{
						NxNgc::EngineGlobals.poly_culling = true;
						// Set up the camera..
						NxNgc::set_camera( &( cur_camera->GetMatrix()), &( cur_camera->GetPos()), cur_camera->GetAdjustedHFOV(), p_cur_viewport->GetAspectRatio());

						// Build relevant occlusion poly list, now that the camera is set.
						NxNgc::BuildOccluders( &( cur_camera->GetPos()), v);

						// Build relevant occlusion poly list, now that the camera is set.
						GX::SetZMode ( GX_TRUE, GX_LEQUAL, GX_TRUE );

						// Flag this scene as receiving shadows.
						pNgcScene->GetEngineScene()->m_flags |= SCENE_FLAG_RECEIVE_SHADOWS;

						uint32 flags = ( NxNgc::vRENDER_OPAQUE | NxNgc::vRENDER_OCCLUDED | NxNgc::vRENDER_LIT );

						gCorrectColor = true;
						rendered = NxNgc::cull_scene( pNgcScene->GetEngineScene(), NxNgc::vRENDER_OCCLUDED );
						NxNgc::render_scene( pNgcScene->GetEngineScene(), NULL, flags );
						considered = meshes_considered;
						gCorrectColor = false;
					}
				}
			}
		}

#	ifdef __USE_PROFILER__
		Sys::VUProfiler->PopContext();
#	endif		// __USE_PROFILER__

//		// Process data for instances. Specifically, transform the verts.
//		Sys::VUProfiler->PushContext( 0,128,128 );
//		NxNgc::process_instances();
//		Sys::VUProfiler->PopContext();

		// Render all opaque instances.
#	ifdef __USE_PROFILER__
		Sys::VUProfiler->PushContext( 128,0,0 );
#	endif		// __USE_PROFILER__
		GX::SetZMode ( GX_TRUE, GX_LEQUAL, GX_TRUE );
		NxNgc::render_instances( NxNgc::vRENDER_OPAQUE | NxNgc::vRENDER_TRANSFORM );
		NxNgc::render_instances( NxNgc::vRENDER_SEMITRANSPARENT | NxNgc::vRENDER_INSTANCE_PRE_WORLD_SEMITRANSPARENT );
#	ifdef __USE_PROFILER__
		Sys::VUProfiler->PopContext();
#	endif		// __USE_PROFILER__

#	ifdef __USE_PROFILER__
		Sys::VUProfiler->PushContext( 0,128,0 );
#	endif		// __USE_PROFILER__

		// Render all the non-sky semitransparent scene geometry.
		NxNgc::EngineGlobals.poly_culling = true;
		if( cur_camera && ( sp_loaded_scenes[0] != NULL ))
		{
			// There is no bounding box transform for rendering the world.
	//		NxNgc::set_frustum_bbox_transform( NULL );

			GX::LoadNrmMtxImm( light, GX_PNMTX0);
			for( int i = 0; i < MAX_LOADED_SCENES; i++ )
			{
				if( sp_loaded_scenes[i] )
				{
					CNgcScene *pNgcScene = static_cast( sp_loaded_scenes[i] );

					// Only interested in non-sky scenes, this time round.
					if( !pNgcScene->IsSky())
					{
						// Set up the camera..
						NxNgc::set_camera( &( cur_camera->GetMatrix()), &( cur_camera->GetPos()), cur_camera->GetAdjustedHFOV(), p_cur_viewport->GetAspectRatio());

						// Build relevant occlusion poly list, now that the camera is set.
						GX::SetZMode ( GX_TRUE, GX_LEQUAL, GX_TRUE );


						gCorrectColor = true;
						//rendered += NxNgc::cull_scene( pNgcScene->GetEngineScene(), NxNgc::vRENDER_OCCLUDED );
						uint32 flags;
						flags = ( NxNgc::vRENDER_SEMITRANSPARENT | NxNgc::vRENDER_OCCLUDED | NxNgc::vRENDER_LIT );
						NxNgc::render_scene( pNgcScene->GetEngineScene(), NULL, flags );
						flags = ( NxNgc::vRENDER_SHADOW_2ND_PASS );
						NxNgc::render_scene( pNgcScene->GetEngineScene(), NULL, flags );
						//considered += meshes_considered;
						gCorrectColor = false;
						//OSReport( "Meshes rendered: %d of %d\n", rendered, considered );
//					}
//					else
//					{
//						// Draw sky after opaque world polys.
//						// Set up the camera.
//						Mth::Vector	centre_pos( 0.0f, 0.0f, 0.0f );
//						NxNgc::set_camera( &( cur_camera->GetMatrix()), ¢re_pos, cur_camera->GetAdjustedHFOV(), p_cur_viewport->GetAspectRatio(), 30.0f);
//
//						GX::SetZMode ( GX_TRUE, GX_LEQUAL, GX_FALSE );
//
//						gCorrectColor = true;
//						NxNgc::render_scene( pNgcScene->GetEngineScene(), NULL );
//						gCorrectColor = false;
					}
				}
			}
		}

		GX::SetZMode ( GX_TRUE, GX_LEQUAL, GX_TRUE );
		NxNgc::render_instances( NxNgc::vRENDER_SEMITRANSPARENT | NxNgc::vRENDER_INSTANCE_POST_WORLD_SEMITRANSPARENT );

#	ifdef __USE_PROFILER__
		Sys::VUProfiler->PopContext();
#	endif		// __USE_PROFILER__

		NxNgc::render_end();

		// Render other stuff.
		if( cur_camera && ( sp_loaded_scenes[0] != NULL ))
		{
			GX::SetFog( GX_FOG_NONE, 0.0f, 0.0f, 0.0f, 0.0f, (GXColor){0,0,0,0} );		// Turn fog off.
			NxNgc::set_camera( &( cur_camera->GetMatrix()), &( cur_camera->GetPos()), cur_camera->GetAdjustedHFOV(), p_cur_viewport->GetAspectRatio());
		
			GX::SetZMode ( GX_TRUE, GX_LEQUAL, GX_FALSE );
			GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
			GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
			render_particles();
			mp_weather->Process( Tmr::FrameLength() );
			mp_weather->Render();

			// New style particles. Update should probably be somewhere else.
			Nx::CFog::sFogUpdate();		// Restore fog to its' former glory.
			NxNgc::set_camera( &( cur_camera->GetMatrix()), &( cur_camera->GetPos()), cur_camera->GetAdjustedHFOV(), p_cur_viewport->GetAspectRatio());
			mp_particle_manager->UpdateParticles();
			mp_particle_manager->RenderParticles();

			GX::SetZMode ( GX_TRUE, GX_LEQUAL, GX_TRUE );
			Nx::ShatterRender();
			Nx::TextureSplatRender();
			GX::SetZMode ( GX_TRUE, GX_LEQUAL, GX_FALSE );
		}

//		Gfx::DebugGfx_Draw();

//		// Render all semi-transparent instances.
//		GX::SetZMode ( GX_TRUE, GX_LEQUAL, GX_TRUE );
//#	ifdef __USE_PROFILER__
//		Sys::VUProfiler->PushContext( 128,0,0 );
//#	endif		// __USE_PROFILER__
//		NxNgc::render_instances( NxNgc::vRENDER_SHADOW_1ST_PASS );
//#	ifdef __USE_PROFILER__
//		Sys::VUProfiler->PopContext();
//#	endif		// __USE_PROFILER__
	
	}

	GX::SetViewport( 0.0f, 0.0f, 640.0f, 448.0f, 0.0f, 1.0f );
	GX::SetScissor( 0, 0, 640, 448 );

	// Horrible hack - this should be somewhere else ASAP.
	GX::SetZMode ( GX_FALSE, GX_ALWAYS, GX_FALSE );

	cam.orthographic( 0, 0, 640, 448 );
	cam.begin();










#ifndef __NOPT_FINAL__

	// 2nd pad controls texture display.
	static int tick;
	static unsigned short last;
	unsigned short current = padData[1].button;
	unsigned short press = ( current ^ last ) & current;
	last = current;

	if ( press & ( PAD_BUTTON_UP | PAD_BUTTON_DOWN | PAD_BUTTON_X | PAD_BUTTON_Y ) )
	{
		tick = 0;
	}


	if ( current & ( PAD_BUTTON_UP | PAD_BUTTON_DOWN | PAD_BUTTON_X | PAD_BUTTON_Y ) )
	{
		if ( tick == 20 )
		{
			press |= current;
		}
		if ( ( ( ( tick - 20 ) % 2 ) == 0 ) && ( tick > 20 ) )
		{
			press |= current;
		}

		tick++;
	}

	if ( press & PAD_BUTTON_UP ) g_scroll_material++;
	if ( press & PAD_BUTTON_DOWN ) g_scroll_material--;
	if ( ( press & PAD_BUTTON_UP ) && ( current & PAD_BUTTON_B ) ) g_scroll_material+=99;
	if ( ( press & PAD_BUTTON_DOWN ) && ( current & PAD_BUTTON_B ) ) g_scroll_material-=99;
	if ( g_view_scene )
	{
		if ( g_scroll_material < 0 ) g_scroll_material = 0;
		if ( g_scroll_material > ( g_view_scene->mp_scene_data->m_num_materials - 1 ) ) g_scroll_material = g_view_scene->mp_scene_data->m_num_materials - 1;
	}

	if ( press & PAD_BUTTON_X ) g_view_object++;
	if ( press & PAD_BUTTON_Y ) g_view_object--;
	if ( g_view_object < 0 ) g_view_object = 0;
	if ( g_view_object > g_object ) g_view_object = g_object;

	if ( press & PAD_BUTTON_A )
	{
		if ( g_material == -1 )
		{
			g_material = g_scroll_material;
		}
		else
		{
			g_material = -1;
		}
	}
	if ( g_material != -1 )
	{
		g_material = g_scroll_material;
	}

	if ( press & PAD_TRIGGER_Z )
	{
		g_dl ^= 1;
	}

	if ( press & PAD_TRIGGER_R ) g_mip++;
	if ( g_mip > 5 ) g_mip = 0;

	if ( press & PAD_TRIGGER_L ) g_passes++;
	if ( g_passes > 3 ) g_passes = -1;

	if ( !g_view_scene )
	{
		// Draw Object number & "NO SCENE" info.
		{
			NxNgc::SText message;
			Nx::CFont * p_cfont;
			const char * p_font_name = "testtitle";
			char p_text[128];

			// We can only draw a text string if we have a font & a string.
			p_cfont = Nx::CFontManager::sGetFont( p_font_name );
			if ( !p_cfont )
			{
				Nx::CFontManager::sLoadFont( p_font_name );
				p_cfont = Nx::CFontManager::sGetFont( p_font_name );
			}
			message.mp_string = p_text;
			message.m_rgba = 0x808080ff;

			Nx::CNgcFont * p_nfont = static_cast( p_cfont );
			NxNgc::SFont * p_font = p_nfont->GetEngineFont();
			message.mp_font = p_font;
			message.m_xscale = 0.75f;
			message.m_yscale = 1.2f;

			message.m_color_override = false;

			if ( p_font )
			{
				sprintf( p_text, "OBJ %d / %d - NOT VISIBLE", g_view_object, g_object + 1 );
				message.m_xpos = 28.0f;
				message.m_ypos = 72.0f;
				message.DrawSingle();
			}
		}
	}
	else
	{
		if ( ( g_material >= 0 ) && ( g_material < g_view_scene->mp_scene_data->m_num_materials ) )
		{
			// Draw material.

			NxNgc::sMaterialHeader * p_mat = &g_view_scene->mp_material_header[g_material];

			uint8 save_passes = p_mat->m_passes;
			p_mat->m_passes = ( g_passes == -1 ) ? p_mat->m_passes : ( g_passes + 1 );

			// Adjust mip to render.
			int mip_off[4] = { 0, 0, 0, 0 };
			{
				NxNgc::sMaterialPassHeader * p_pass = &g_view_scene->mp_material_pass[p_mat->m_pass_item];
				for ( int tex = 0; tex < p_mat->m_passes; tex++, p_pass++ )
				{
					if ( p_pass->m_texture.p_data )
					{
						for ( int mm = 0; mm < g_mip; mm++ )
						{
							int mw = p_pass->m_texture.p_data->ActualWidth >> mm;
							int mh = p_pass->m_texture.p_data->ActualHeight >> mm;
							mip_off[tex] += ( mw * mh ) >> 1;
						}
						p_pass->m_texture.p_data->pTexelData += mip_off[tex];
						p_pass->m_texture.p_data->pAlphaData += mip_off[tex];
						p_pass->m_texture.p_data->ActualWidth >>= g_mip;
						p_pass->m_texture.p_data->ActualHeight >>= g_mip;
					}
				}
			}

			multi_mesh( p_mat,
						&g_view_scene->mp_material_pass[p_mat->m_pass_item],
						true,
						true );
			p_mat->m_passes = save_passes;
			// Adjust mip back.
			{
				NxNgc::sMaterialPassHeader * p_pass = &g_view_scene->mp_material_pass[p_mat->m_pass_item];
				for ( int tex = 0; tex < p_mat->m_passes; tex++, p_pass++ )
				{
					if ( p_pass->m_texture.p_data )
					{
						p_pass->m_texture.p_data->pTexelData -= mip_off[tex];
						p_pass->m_texture.p_data->pAlphaData -= mip_off[tex];
						p_pass->m_texture.p_data->ActualWidth <<= g_mip;
						p_pass->m_texture.p_data->ActualHeight <<= g_mip;
					}
				}
			}

			GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){64,64,64,128} );
			GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){64,64,64,128} );

			GX::SetVtxDesc( 5, GX_VA_POS,  GX_DIRECT,
							   GX_VA_TEX0, GX_DIRECT,
							   GX_VA_TEX1, GX_DIRECT,
							   GX_VA_TEX2, GX_DIRECT,
							   GX_VA_TEX3, GX_DIRECT );

			GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
				GX::Position3f32(32.0f, 96.0f, -1.0f);
				GX::TexCoord2f32(0.0f, 1.0f);
				GX::TexCoord2f32(0.0f, 1.0f);
				GX::TexCoord2f32(0.0f, 1.0f);
				GX::TexCoord2f32(0.0f, 1.0f);
				GX::Position3f32(160.0f, 96.0f, -1.0f);
				GX::TexCoord2f32(1.0f, 1.0f);
				GX::TexCoord2f32(1.0f, 1.0f);
				GX::TexCoord2f32(1.0f, 1.0f);
				GX::TexCoord2f32(1.0f, 1.0f);
				GX::Position3f32(160.0f, 222.0f, -1.0f);
				GX::TexCoord2f32(1.0f, 0.0f);
				GX::TexCoord2f32(1.0f, 0.0f);
				GX::TexCoord2f32(1.0f, 0.0f);
				GX::TexCoord2f32(1.0f, 0.0f);
				GX::Position3f32(32.0f, 222.0f, -1.0f);
				GX::TexCoord2f32(0.0f, 0.0f);
				GX::TexCoord2f32(0.0f, 0.0f);
				GX::TexCoord2f32(0.0f, 0.0f);
				GX::TexCoord2f32(0.0f, 0.0f);
			GX::End();

			float	tx[8] = { 32.0f, 98.0f, 164.0f, 230.0f, 296.0f, 362.0f, 428.0f, 494.0f };
			float	ty[8] = { 230.0f, 230.0f, 230.0f, 230.0f, 230.0f, 230.0f, 230.0f, 230.0f };
			int		tr = 0;

			// Draw Object & material number.
			{
				NxNgc::SText message;
				Nx::CFont * p_cfont;
				const char * p_font_name = "testtitle";
				char p_text[128];

				// We can only draw a text string if we have a font & a string.
				p_cfont = Nx::CFontManager::sGetFont( p_font_name );
				if ( !p_cfont )
				{
					Nx::CFontManager::sLoadFont( p_font_name );
					p_cfont = Nx::CFontManager::sGetFont( p_font_name );
				}
				message.mp_string = p_text;
				message.m_rgba = 0x808080ff;

				Nx::CNgcFont * p_nfont = static_cast( p_cfont );
				NxNgc::SFont * p_font = p_nfont->GetEngineFont();
				message.mp_font = p_font;
				message.m_xscale = 0.75f;
				message.m_yscale = 1.2f;

				message.m_color_override = false;

				if ( p_font )
				{
					char * p_pass_string = "***";
					if ( g_passes == -1 ) p_pass_string = "ALL";
					if ( g_passes == 0 ) p_pass_string = "P1";
					if ( g_passes == 1 ) p_pass_string = "P2";
					if ( g_passes == 2 ) p_pass_string = "P3";
					if ( g_passes == 3 ) p_pass_string = "P4";
					sprintf( p_text, "OBJ %d / %d - MAT %d / %d DO: %6.3f AC: %3d M: %d %s", g_view_object, g_object + 1, g_material, g_view_scene->mp_scene_data->m_num_materials, p_mat->m_draw_order, p_mat->m_alpha_cutoff, g_mip, p_pass_string );
					message.m_xpos = 28.0f;
					message.m_ypos = 72.0f;
					message.DrawSingle();
				}
			}

			NxNgc::sMaterialPassHeader * p_pass = &g_view_scene->mp_material_pass[p_mat->m_pass_item];
			for ( int tex = 0; tex < p_mat->m_passes; tex++, p_pass++ )
			{
				if ( p_pass->m_texture.p_data )
				{
					// Draw texture.
					int w = p_pass->m_texture.p_data->ActualWidth >> g_mip;
					int h = p_pass->m_texture.p_data->ActualHeight >> g_mip;
					int mip_off = 0;
					for ( int mm = 0; mm < g_mip; mm++ )
					{
						int mw = p_pass->m_texture.p_data->ActualWidth >> mm;
						int mh = p_pass->m_texture.p_data->ActualHeight >> mm;
						mip_off += ( mw * mh ) >> 1;
					}
					GX::UploadTexture(  &p_pass->m_texture.p_data->pTexelData[mip_off],
										w,
										h,
										GX_TF_CMPR,
										GX_CLAMP,
										GX_CLAMP,
										GX_FALSE,
										GX_LINEAR,
										GX_LINEAR,
										0.0f,
										0.0f,
										0.0f,
										GX_FALSE,
										GX_TRUE,
										GX_ANISO_1,
										GX_TEXMAP0 ); 
					GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, w, h );

					GX::SetTexChanTevIndCull( 1, 0, 1, 0, GX_CULL_NONE );
					GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
					GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

					GX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0 );
					GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );		// Replace

					GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
					GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO,
											 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV,
											 GX_TEV_SWAP0, GX_TEV_SWAP0 );
					GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
										 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );

					GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );

					GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
						GX::Position3f32(tx[tr], ty[tr], -1.0f);
						GX::TexCoord2f32(0.0f, 1.0f);
						GX::Position3f32(tx[tr] + 62.0f, ty[tr], -1.0f);
						GX::TexCoord2f32(1.0f, 1.0f);
						GX::Position3f32(tx[tr] + 62.0f, ty[tr] + 62.0f, -1.0f);
						GX::TexCoord2f32(1.0f, 0.0f);
						GX::Position3f32(tx[tr], ty[tr] + 62.0f, -1.0f);
						GX::TexCoord2f32(0.0f, 0.0f);
					GX::End();
					if ( p_pass->m_texture.p_data->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA )
					{
						// Draw alpha.
						int w = p_pass->m_texture.p_data->ActualWidth >> g_mip;
						int h = p_pass->m_texture.p_data->ActualHeight >> g_mip;
						int mip_off = 0;
						for ( int mm = 0; mm < g_mip; mm++ )
						{
							int mw = p_pass->m_texture.p_data->ActualWidth >> mm;
							int mh = p_pass->m_texture.p_data->ActualHeight >> mm;
							mip_off += ( mw * mh ) >> 1;
						}
						GX::UploadTexture(  &p_pass->m_texture.p_data->pAlphaData[mip_off],
											w,
											h,
											GX_TF_CMPR,
											GX_CLAMP,
											GX_CLAMP,
											GX_FALSE,
											GX_LINEAR,
											GX_LINEAR,
											0.0f,
											0.0f,
											0.0f,
											GX_FALSE,
											GX_TRUE,
											GX_ANISO_1,
											GX_TEXMAP0 ); 
						GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, w, h );

						GX::SetTexChanTevIndCull( 1, 0, 1, 0, GX_CULL_NONE );
						GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
						GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

						GX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0 );
						GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );		// Replace

						GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);

						GXTevSwapSel alpha_swap = GX_TEV_SWAP1;
						switch ( ( p_pass->m_texture.p_data->flags & NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_MASK ) )
						{
							case NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_GREEN:
							default:
								alpha_swap = GX_TEV_SWAP1;		// Green
								break;
							case NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_RED:
								alpha_swap = GX_TEV_SWAP2;		// Red
								break;
							case NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_BLUE:
								alpha_swap = GX_TEV_SWAP3;		// Blue
								break;
						}

						GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO,
												 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV,
												 GX_TEV_SWAP0, alpha_swap );
						GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
											 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );

						GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );

						GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
							GX::Position3f32(tx[tr+1], ty[tr+1], -1.0f);
							GX::TexCoord2f32(0.0f, 1.0f);
							GX::Position3f32(tx[tr+1] + 62.0f, ty[tr+1], -1.0f);
							GX::TexCoord2f32(1.0f, 1.0f);
							GX::Position3f32(tx[tr+1] + 62.0f, ty[tr+1] + 62.0f, -1.0f);
							GX::TexCoord2f32(1.0f, 0.0f);
							GX::Position3f32(tx[tr+1], ty[tr+1] + 62.0f, -1.0f);
							GX::TexCoord2f32(0.0f, 0.0f);
						GX::End();
					}
				}

				// Draw pass info.
				char * blendmode[14] = {
					"DIFF",
					"ADD",
					"ADDF",
					"SUB",
					"SUBF",
					"BLND",
					"BLNDF",
					"MOD",
					"MODF",
					"BRT",
					"BRTF",
					"GLOSS",
					"BLPV",
					"BLPVI"
				};

				NxNgc::SText message;
				Nx::CFont * p_cfont;
				const char * p_font_name = "testtitle";
				char p_text[128];

				// We can only draw a text string if we have a font & a string.
				p_cfont = Nx::CFontManager::sGetFont( p_font_name );
				if ( !p_cfont )
				{
					Nx::CFontManager::sLoadFont( p_font_name );
					p_cfont = Nx::CFontManager::sGetFont( p_font_name );
				}
				message.mp_string = p_text;
				message.m_rgba = 0x808080ff;

				Nx::CNgcFont * p_nfont = static_cast( p_cfont );
				NxNgc::SFont * p_font = p_nfont->GetEngineFont();
				message.mp_font = p_font;
				message.m_xscale = 0.75f;
				message.m_yscale = 1.2f;

				message.m_color_override = false;

# define toupper(c) ( ( (c) >= 'a' ) && ( (c) <= 'z' ) ) ? (c) += ( 'A' - 'a' ) : (c)

				if ( p_font )
				{
					if ( p_pass->m_texture.p_data )
					{
						sprintf( p_text, "%d,%d", p_pass->m_texture.p_data->ActualWidth, p_pass->m_texture.p_data->ActualHeight );
						message.m_xpos = tx[tr];
						message.m_ypos = ty[tr] + 66.0f + ( 16.0 * 0.0f );
						message.DrawSingle();
					}

					sprintf( p_text, "%s", blendmode[p_pass->m_blend_mode] );
					message.m_xpos = tx[tr];
					message.m_ypos = ty[tr] + 66.0f + ( 16.0 * 1.0f );
					message.DrawSingle();

					sprintf( p_text, "%02x %02x", p_pass->m_color.r, p_pass->m_color.g );
					p_text[0] = toupper( p_text[0] );
					p_text[1] = toupper( p_text[1] );
					p_text[3] = toupper( p_text[3] );
					p_text[4] = toupper( p_text[4] );
					message.m_xpos = tx[tr];
					message.m_ypos = ty[tr] + 66.0f + ( 16.0 * 2.0f );
					message.DrawSingle();

					sprintf( p_text, "%02x %02x", p_pass->m_color.b, p_pass->m_color.a );
					p_text[0] = toupper( p_text[0] );
					p_text[1] = toupper( p_text[1] );
					p_text[3] = toupper( p_text[3] );
					p_text[4] = toupper( p_text[4] );
					message.m_xpos = tx[tr];
					message.m_ypos = ty[tr] + 66.0f + ( 16.0 * 3.0f );
					message.DrawSingle();

					sprintf( p_text, "%02x %02x", p_pass->m_alpha_fix, p_pass->m_flags );
					p_text[0] = toupper( p_text[0] );
					p_text[1] = toupper( p_text[1] );
					p_text[3] = toupper( p_text[3] );
					p_text[4] = toupper( p_text[4] );
					message.m_xpos = tx[tr];
					message.m_ypos = ty[tr] + 66.0f + ( 16.0 * 4.0f );
					message.DrawSingle();

					if ( p_pass->m_texture.p_data )
					{
						sprintf( p_text, "%3.1f %d", (float)(p_pass->m_k) * (1.0f / (float)(1<<8)), p_pass->m_texture.p_data->Levels );
						message.m_xpos = tx[tr];
						message.m_ypos = ty[tr] + 66.0f + ( 16.0 * 5.0f );
						message.DrawSingle();
					}
				}
				tr++;
				if ( p_pass->m_texture.p_data && p_pass->m_texture.p_data->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA ) tr++;
			}
		}
	}
#endif

//	// Draw texture.
//	GX::UploadTexture(  shadowTextureData,
//						SHADOW_TEXTURE_SIZE,
//						SHADOW_TEXTURE_SIZE,
//						GX_TF_I4,
//						GX_CLAMP,
//						GX_CLAMP,
//						GX_FALSE,
//						GX_LINEAR,
//						GX_LINEAR,
//						0.0f,
//						0.0f,
//						0.0f,
//						GX_FALSE,
//						GX_TRUE,
//						GX_ANISO_1,
//						GX_TEXMAP0 ); 
//	GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, SHADOW_TEXTURE_SIZE, SHADOW_TEXTURE_SIZE );
//
//	GX::UploadPalette( shadowPalette,
//					   GX_TL_RGB5A3,
//					   GX_TLUT_16,
//					   GX_TEXMAP0 );
//
//	GX::SetTexChanTevIndCull( 1, 0, 1, 0, GX_CULL_NONE );
//	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
//	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );
//
//	GX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0 );
//	GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );		// Replace
//
//	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
//	GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO,
//							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV,
//							 GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
//						 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
//
//	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
//
//	GX::Begin( GX_QUADS, GX_VTXFMT0, 4 );
//		GX::Position3f32( 32.0f,  32.0f, -1.9f);
//		GX::TexCoord2f32(0.0f, 0.0f);
//		GX::Position3f32(288.0f,  32.0f, -1.9f);
//		GX::TexCoord2f32(1.0f, 0.0f);
//		GX::Position3f32(288.0f, 288.0f, -1.9f);
//		GX::TexCoord2f32(1.0f, 1.0f);
//		GX::Position3f32( 32.0f, 288.0f, -1.9f);
//		GX::TexCoord2f32(0.0f, 1.0f);
//	GX::End();



/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////













//	// Shadow volume stage 1.
//	// And the alpha channel with 128. Pixels in shadow will have alpha of 128, not will be 0.
//	GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE );
//	GX::SetBlendMode ( GX_BM_LOGIC, GX_BL_SRCALPHA, GX_BL_DSTALPHA, GX_LO_AND, GX_FALSE, GX_TRUE, GX_FALSE );
//	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
//	GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_KONST, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO,
//							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV,
//							 GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
//						 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
//	GX::SetTevKColor( GX_KCOLOR0, (GXColor){0,0,0,128} );
//	GX::SetTevKSel( GX_TEVSTAGE0, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A );
//
//	GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
//
//	GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
//		GX::Position3f32(0.0f, 0.0f, -1.0f);
//		GX::Position3f32(640.0f, 0.0f, -1.0f);
//		GX::Position3f32(640.0f, 448.0f, -1.0f);
//		GX::Position3f32(0.0f, 448.0f, -1.0f);
//	GX::End();
//
//	// Shadow volume stage 2.
//	// Blend alpha with shadow color.
//	GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE );
//	GX::SetBlendMode( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_INVDSTALPHA, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
//	GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_KONST, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO,
//							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV,
//							 GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_KONST, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
//						 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
//	GX::SetTevKColor( GX_KCOLOR0, (GXColor){0,0,0,128} );
//	GX::SetTevKSel( GX_TEVSTAGE0, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A, GX_TEV_KCSEL_K0, GX_TEV_KASEL_K0_A );
//
//	GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
//
//	GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
//		GX::Position3f32(0.0f, 0.0f, -1.0f);
//		GX::Position3f32(640.0f, 0.0f, -1.0f);
//		GX::Position3f32(640.0f, 448.0f, -1.0f);
//		GX::Position3f32(0.0f, 448.0f, -1.0f);
//	GX::End();








































#if 0
	// Copy framebuffer to 320x224 texture.
	GXSetCopyFilter( GX_FALSE, NULL, GX_FALSE, NULL );
	GXSetTexCopySrc(0, 0, 640, 448);
	GXSetTexCopyDst(320, 224, GX_TF_Z8, GX_TRUE);
	GXCopyTex(zTextureDataH, GX_FALSE);
	GXPixModeSync();

	GXSetCopyFilter( GX_FALSE, NULL, GX_FALSE, NULL );
	GXSetTexCopySrc(0, 0, 640, 448);
	GXSetTexCopyDst(320, 224, GX_CTF_Z8M, GX_TRUE);
	GXCopyTex(zTextureDataL, GX_FALSE);
	GXPixModeSync();

	// Copy top-corner of screen.
	GXSetCopyFilter( GX_FALSE, NULL, GX_FALSE, NULL );
	GXSetTexCopySrc(0, 0, 320, 224);
	GXSetTexCopyDst(320, 224, GX_TF_RGBA8, GX_FALSE);
	GXCopyTex(screenTextureData, GX_FALSE);
	GXPixModeSync();

	// Copy screen and filter down.
	GXSetCopyFilter( GX_TRUE, sample_pattern, GX_TRUE, GXNtsc480IntAa.vfilter );
	GXSetTexCopySrc(0, 0, 640, 448);
	GXSetTexCopyDst(320, 224, GX_TF_RGBA8, GX_TRUE);
	GXCopyTex(focusTextureData, GX_FALSE);
	GXPixModeSync();

	// Shrink down the focus texture.
	GXTexObj	focusTexture;
	GXInitTexObj(
		&focusTexture,
		focusTextureData,
		320,
		224,
		GX_TF_RGBA8,
		GX_CLAMP,
		GX_CLAMP,
		GX_FALSE );
	GXInitTexObjLOD(&focusTexture, GX_NEAR, GX_NEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
	GXLoadTexObj( &focusTexture, GX_TEXMAP0 );

	GX::SetNumChans(0);
	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	GX::SetNumTevStages( 1 );
	GX::SetNumTexGens( 1 );
	GX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0 );
	GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );		// Replace

	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
	GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
	GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
	GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
	GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
	GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );

	GXSetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );

	GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
		GXPosition3f32(0.0f, 0.0f, -1.0f);
		GXTexCoord2f32(0.0f, 0.0f);
		GXPosition3f32(320.0f, 0.0f, -1.0f);
		GXTexCoord2f32(1.0f, 0.0f);
		GXPosition3f32(320.0f, 224.0f, -1.0f);
		GXTexCoord2f32(1.0f, 1.0f);
		GXPosition3f32(0.0f, 224.0f, -1.0f);
		GXTexCoord2f32(0.0f, 1.0f);
	GXEnd();

	GXSetCopyFilter( GX_TRUE, sample_pattern, GX_TRUE, GXNtsc480IntAa.vfilter );
	GXSetTexCopySrc(0, 0, 320, 224);
	GXSetTexCopyDst(160, 112, GX_TF_RGBA8, GX_TRUE);
	GXCopyTex(focusTextureData, GX_FALSE);
	GXPixModeSync();

	// Draw top-corner screen area we wrote over.
	GXTexObj	screenTexture;
	GXInitTexObj(
		&screenTexture,
		screenTextureData,
		320,
		224,
		GX_TF_RGBA8,
		GX_CLAMP,
		GX_CLAMP,
		GX_FALSE );
	GXInitTexObjLOD(&screenTexture, GX_NEAR, GX_NEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
	GXLoadTexObj( &screenTexture, GX_TEXMAP0 );

	GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
		GXPosition3f32(0.0f, 0.0f, -1.0f);
		GXTexCoord2f32(0.0f, 0.0f);
		GXPosition3f32(320.0f, 0.0f, -1.0f);
		GXTexCoord2f32(1.0f, 0.0f);
		GXPosition3f32(320.0f, 224.0f, -1.0f);
		GXTexCoord2f32(1.0f, 1.0f);
		GXPosition3f32(0.0f, 224.0f, -1.0f);
		GXTexCoord2f32(0.0f, 1.0f);
	GXEnd();

	// Setup z textures
	GXTexObj	zTextureH;
	GXTexObj	zTextureL;
	GXTlutObj	zPalH;
	GXTlutObj	zPalL;

	GXInitTlutObj( &zPalH, &zPalette8H, GX_TL_IA8, 256 );
	GXLoadTlut ( &zPalH, GX_TLUT0 );

	GXInitTlutObj( &zPalL, &zPalette8L, GX_TL_IA8, 256 );
	GXLoadTlut ( &zPalL, GX_TLUT1 );

	GXInitTexObjCI(
		&zTextureH,
		zTextureDataH,
		320,
		224,
		GX_TF_C8,
		GX_CLAMP,
		GX_CLAMP,
		GX_FALSE,
		GX_TLUT0 );
	GXInitTexObjLOD(&zTextureH, GX_NEAR, GX_NEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
	GXLoadTexObj( &zTextureH, GX_TEXMAP0 );

	GXInitTexObjCI(
		&zTextureL,
		zTextureDataL,
		320,
		224,
		GX_TF_C8,
		GX_CLAMP,
		GX_CLAMP,
		GX_FALSE,
		GX_TLUT1 );
	GXInitTexObjLOD(&zTextureL, GX_NEAR, GX_NEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
	GXLoadTexObj( &zTextureL, GX_TEXMAP1 );

	GXInitTexObj(
		&focusTexture,
		focusTextureData,
		160,
		112,
		GX_TF_RGBA8,
		GX_CLAMP,
		GX_CLAMP,
		GX_FALSE );
	GXInitTexObjLOD(&focusTexture, GX_LINEAR, GX_LINEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
	GXLoadTexObj( &focusTexture, GX_TEXMAP2 );









//	GXTexObj	zTexture;
//	GXTlutObj	zPal;
//
//	GXInitTlutObj( &zPal, &zPalette16, GX_TL_IA8, 65535 );
//	GXLoadTlut ( &zPal, GX_TLUT0 );
//
//	GXInitTexObjCI(
//		&zTexture,
//		zTextureData,
//		320,
//		224,
//		GX_TF_C14X2,
//		GX_CLAMP,
//		GX_CLAMP,
//		GX_FALSE,
//		GX_TLUT0 );
//	GXInitTexObjLOD(&zTexture, GX_NEAR, GX_NEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
//	GXLoadTexObj( &zTexture, GX_TEXMAP0 );

	GX::SetTexChanTevIndCull( 3, 0, 3, 0, GX_CULL_NONE ); 
	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetTexCoordGen( GX_TEXCOORD1, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetTexCoordGen( GX_TEXCOORD2, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	GX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0 );

//	GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ZERO, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE ); // Replace alpha
//	GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR );		// Replace
	GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );		// Blend

//	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_4, GX_DISABLE, GX_TEVPREV );
//	GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_4, GX_DISABLE, GX_TEVPREV );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_TEXA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//
//	GX::SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_4, GX_DISABLE, GX_TEVREG1 );
//	GX::SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_4, GX_DISABLE, GX_TEVREG1 );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE1, GX_CA_APREV, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE1, GX_CC_CPREV, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//
//	GX::SetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE2, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVREG0 );
//	GX::SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVREG0 );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE2, GX_CA_A1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE2, GX_CC_C1, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//
//	GX::SetTevKColorSel( GX_TEVSTAGE3, GX_TEV_KCSEL_K0_A );
//	GX::SetTevKColor( GX_KCOLOR0, (GXColor){16,16,16,16} );
//
//	GX::SetTevOrder(GX_TEVSTAGE3, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE3, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE3, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG1 );
//	GX::SetTevColorOp(GX_TEVSTAGE3, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG1 );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE3, GX_CA_ZERO, GX_CA_KONST, GX_CA_TEXA, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE3, GX_CC_ZERO, GX_CC_KONST, GX_CC_TEXC, GX_CC_ZERO );
//
//	GX::SetTevOrder(GX_TEVSTAGE4, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE4, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE4, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//	GX::SetTevColorOp(GX_TEVSTAGE4, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE4, GX_CA_A0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A1 );
//	GX::SetTevColorIn ( GX_TEVSTAGE4, GX_CC_C0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_C1 );



//	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG0 );
//	GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVREG0 );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA );
//	GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC );
//
//	GX::SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//	GX::SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
////	GX::SetTevAlphaIn ( GX_TEVSTAGE1, GX_CA_A0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA );
////	GX::SetTevColorIn ( GX_TEVSTAGE1, GX_CC_C0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC );
////	GX::SetTevAlphaIn ( GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_TEXA );
////	GX::SetTevColorIn ( GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_TEXC );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE1, GX_CA_A0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE1, GX_CC_C0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );




//	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_4, GX_DISABLE, GX_TEVPREV );
//	GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_4, GX_DISABLE, GX_TEVPREV );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ONE, GX_CC_ZERO, GX_CC_TEXC, GX_CC_ZERO );
//
//	GX::SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_4, GX_DISABLE, GX_TEVREG1 );
//	GX::SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_4, GX_DISABLE, GX_TEVREG1 );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE1, GX_CC_CPREV, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//
//	GX::SetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE2, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVREG0 );
//	GX::SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVREG0 );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE2, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE2, GX_CC_C1, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//
//	GX::SetTevKColorSel( GX_TEVSTAGE3, GX_TEV_KCSEL_K0_A );
//	GX::SetTevKColor( GX_KCOLOR0, (GXColor){16,16,16,16} );
//
//	GX::SetTevOrder(GX_TEVSTAGE3, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE3, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE3, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
//	GX::SetTevColorOp(GX_TEVSTAGE3, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE3, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE3, GX_CC_ONE, GX_CC_ZERO, GX_CC_TEXC, GX_CC_C0 );




	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL, GX_TEXCOORD1, GX_TEXMAP1, GX_COLOR_NULL);
	GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
	GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVREG0 );
	GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVREG0 );
	GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_TEXA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
	GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );

	GX::SetTevSwapMode( GX_TEVSTAGE1, GX_TEV_SWAP0, GX_TEV_SWAP0 );
	GX::SetTevAlphaOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVREG1 );
	GX::SetTevColorOp(GX_TEVSTAGE1, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVREG1 );
	GX::SetTevAlphaIn ( GX_TEVSTAGE1, GX_CA_TEXA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_A0 );
	GX::SetTevColorIn ( GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );

	GX::SetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD2, GX_TEXMAP2, GX_COLOR_NULL, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
	GX::SetTevSwapMode( GX_TEVSTAGE2, GX_TEV_SWAP0, GX_TEV_SWAP0 );
	GX::SetTevAlphaOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
	GX::SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
	GX::SetTevAlphaIn ( GX_TEVSTAGE2, GX_CA_A1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
	GX::SetTevColorIn ( GX_TEVSTAGE2, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE2, GX_CA_A1, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE2, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );



//    GXSetTevSwapModeTable( GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_RED );
//    GXSetTevSwapModeTable( GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN );
//    GXSetTevSwapModeTable( GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE );

//	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
//	GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
//	GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );



//	if ( gFocus )
//	{
//		// Set current vertex descriptor to enable position and color0.
//		// Both use 8b index to access their data arrays.
//		GXSetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
//
//		GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
//			GXPosition3f32(0.0f, 0.0f, -1.0f);
//			GXTexCoord2f32(0.0f, 0.0f);
//			GXPosition3f32(640.0f, 0.0f, -1.0f);
//			GXTexCoord2f32(1.0f, 0.0f);
//			GXPosition3f32(640.0f, 448.0f, -1.0f);
//			GXTexCoord2f32(1.0f, 1.0f);
//			GXPosition3f32(0.0f, 448.0f, -1.0f);
//			GXTexCoord2f32(0.0f, 1.0f);
//		GXEnd();
//		
//	}
#endif




































/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////



	GX::SetZMode ( GX_FALSE, GX_ALWAYS, GX_FALSE );
	NxNgc::set_blend_mode( NxNgc::vBLEND_MODE_ADD );
	NxNgc::SDraw2D::DrawAll();

//	if ( NsDisplay::shouldReset() && NxNgc::EngineGlobals.disableReset )
//	{
//		GX::SetNumTevStages(1);
//		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
//		GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//		GX::SetNumTexGens( 0 );
//		GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//		GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//		GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//		GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
//		GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
//		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//		GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){0,0,0,255} );
//		GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//
//		GXSetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
//
//		GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
//			GXPosition3f32(128.0f, 224.0f - 64.0f, -1.0f);
//			GXPosition3f32(640.0f - 128.0f, 224.0f - 64.0f, -1.0f);
//			GXPosition3f32(640.0f - 128.0f, 224.0f + 64.0f, -1.0f);
//			GXPosition3f32(128.0f, 224.0f + 64.0f, -1.0f);
//		GXEnd();
//
//		Script::RunScript( "ngc_reset" );
//	}

//	// Set up shadow map texture
//	GXTexObj shadowTexture;
//	GXInitTexObj(
//		&shadowTexture,
//		shadowTextureData,
//		SHADOW_TEXTURE_SIZE,
//		SHADOW_TEXTURE_SIZE,
//		GX_TF_RGBA8,
//		GX_CLAMP,
//		GX_CLAMP,
//		GX_FALSE );
//	GXInitTexObjLOD(&shadowTexture, GX_NEAR, GX_NEAR, 0, 0, 0, 0, 0, GX_ANISO_1);
//	GXLoadTexObj( &shadowTexture, GX_TEXMAP0 );
//
//    GXSetNumChans(1);
//	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX3x4, GX_TG_TEX0, GX_IDENTITY );
//
//    GX::SetNumTevStages(1);
//	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
//	GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetNumTexGens( 1 );
//	GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//	GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//	GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//	GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
//	GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO );
//	GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_RASC, GX_CC_ZERO );
//	GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//	GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){128,128,128,255} );
//	GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//
//    Mtx mv;
//	MTXIdentity( mv );
//    GX::LoadTexMtxImm(mv, GX_TEXMTX0, GX_MTX3x4);
//    GX::LoadTexMtxImm(mv, GX_TEXMTX1, GX_MTX3x4);
//
//	// Set current vertex descriptor to enable position and color0.
//	// Both use 8b index to access their data arrays.
//	GXSetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
//
//	// Send coordinates.
//	GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
//		GXPosition3f32(320, 32, -1.0f);
//		GXTexCoord2f32(0.0f, 0.0f);
//		GXPosition3f32(576, 32, -1.0f);
//		GXTexCoord2f32(1.0f, 0.0f);
//		GXPosition3f32(576, 288, -1.0f);
//		GXTexCoord2f32(1.0f, 1.0f);
//		GXPosition3f32(320, 288, -1.0f);
//		GXTexCoord2f32(0.0f, 1.0f);
//	GXEnd();


	// Blur.
//	if ( sBlur > 0 )
//	{
//		
//	}
//
//sBlurBuffer

	
	
	
	
	
	
	
//	GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, 64, 64 );
//	
//	GX::SetTexChanTevIndCull( 1, 0, 1, 0, GX_CULL_NONE );
//	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
//	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );
//	
//	GX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0 );
//	GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );		// Replace
//	
//	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP7, GX_COLOR_NULL, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
//	GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO,
//							 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV,
//							 GX_TEV_SWAP0, GX_TEV_SWAP0 );
//	GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
//						 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );
//	
//	GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//	GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//	GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//	
//	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
//	
//	GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
//		GX::Position3f32(64.0f, 64.0f, -1.0f);
//		GX::TexCoord2f32(0.0f, 0.0f);
//		GX::Position3f32(64.0f + 64.0f, 64.0f, -1.0f);
//		GX::TexCoord2f32(1.0f, 0.0f);
//		GX::Position3f32(64.0f + 64.0f, 64.0f + 64.0f, -1.0f);
//		GX::TexCoord2f32(1.0f, 1.0f);
//		GX::Position3f32(64.0f, 64.0f + 64.0f, -1.0f);
//		GX::TexCoord2f32(0.0f, 1.0f);
//	GX::End();
//	
	
	
	
	
	
	
	
	cam.end();
	NsPrim::end();
	NsRender::end();

//	if ( movies )
//	{
//		NsDisplay::end( false );
//	}
//	else
//	{
		//NsDisplay::end( true );
//	}
	NsBuffer::end();

	NxNgc::EngineGlobals.gpuBusy = false;

	NxNgc::EngineGlobals.frameCount = ( NxNgc::EngineGlobals.frameCount + 1 ) & 1;

	NsDisplay::end(true);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CScene	*	CEngine::s_plat_create_scene(const char *p_name, CTexDict *p_tex_dict, bool add_super_sectors)
{
	// Create scene class instance
	CNgcScene	*p_Ngc_scene	= new CNgcScene;
	CScene		*p_new_scene	= p_Ngc_scene;
	p_new_scene->SetInSuperSectors( add_super_sectors );
	p_new_scene->SetIsSky( false );

	// Create a new sScene so the engine can track assets for this scene.
	NxNgc::sScene *p_engine_scene = new NxNgc::sScene();
	p_Ngc_scene->SetEngineScene( p_engine_scene );

	return p_new_scene;
}

#define MemoryRead( dst, size, num, src )	memcpy(( dst ), ( src ), (( num ) * ( size )));	\
											( src ) += (( num ) * ( size ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int s_plat_scene_size( void *p_mem, CTexDict *p_tex_dict, bool add_super_sectors, bool is_sky, bool is_dictionary )
{
	void * p_start = p_mem;

	// Setup pointer to actual data.
	NxNgc::sSceneHeader * p_scene_header = (NxNgc::sSceneHeader *)p_mem;
	unsigned int * p32 = (unsigned int *)&p_scene_header[1];

	// Setup material DL pointer.
	NxNgc::sMaterialDL * blend_dl = (NxNgc::sMaterialDL *)p32; 
	NxNgc::sTextureDL * tex_dl = (NxNgc::sTextureDL *)&blend_dl[p_scene_header->m_num_blend_dls];
	unsigned char * p8 = (unsigned char *)&tex_dl[p_scene_header->m_num_materials];

	int bytes = ( p_scene_header->m_num_blend_dls * sizeof( NxNgc::sMaterialDL ) ) + ( p_scene_header->m_num_materials * sizeof( NxNgc::sTextureDL ) );
	int rounded_bytes = ( bytes + 31 ) & ~31;
	int pad_bytes = rounded_bytes - bytes;

	p8 = &p8[pad_bytes];

	// Assign blend dl pointers.
	for ( uint bdl = 0; bdl < p_scene_header->m_num_blend_dls; bdl++ )
	{
		p8 += blend_dl[bdl].m_dl_size;
	}

	// Assign texture dl pointers.
	for ( uint tdl = 0; tdl < p_scene_header->m_num_materials; tdl++ )
	{
		p8 += tex_dl[tdl].m_dl_size;
	}
	p32 = (unsigned int *)p8;

	// Point past pool data.
	NxNgc::sObjectHeader* p_data = (NxNgc::sObjectHeader*)&((char*)p32)[p_scene_header->m_num_pool_bytes];

	// Setup VC wibble data.
	NxNgc::sMaterialVCWibbleKeyHeader *	p_key_header	= (NxNgc::sMaterialVCWibbleKeyHeader *)p32; 
	NxNgc::sMaterialVCWibbleKey *		p_key			= (NxNgc::sMaterialVCWibbleKey *)&p_key_header[1]; 
	for ( int vc = 0; vc < p_scene_header->m_num_vc_wibbles; vc++ )
	{
		p_key_header = (NxNgc::sMaterialVCWibbleKeyHeader *)&p_key[p_key_header->m_num_frames]; 
		p_key = (NxNgc::sMaterialVCWibbleKey *)&p_key_header[1];  
	}
	p32 = (uint32 *)p_key_header;

	// Setup material header data.
	NxNgc::sMaterialHeader *material_header = (NxNgc::sMaterialHeader *)p32;
	p32 = (uint32 *)&material_header[p_scene_header->m_num_materials];

	// Setup UV wibble data.
	NxNgc::sMaterialUVWibble *uv_wibble = (NxNgc::sMaterialUVWibble *)p32;
	p32 = (uint32 *)&uv_wibble[p_scene_header->m_num_uv_wibbles];

	// Setup material pass data.
	NxNgc::sMaterialPassHeader *material_pass = (NxNgc::sMaterialPassHeader *)p32;
	p32 = (uint32 *)&material_pass[p_scene_header->m_num_pass_items];

	if ( p_scene_header->m_num_pos )
	{
		p32 = &((unsigned int *)p32)[p_scene_header->m_num_pos*3];
	}

	if ( p_scene_header->m_num_nrm )
	{
		p32 = &((unsigned int *)p32)[p_scene_header->m_num_nrm*3];
	}

	if ( p_scene_header->m_num_col )
	{
		p32 = &((unsigned int *)p32)[p_scene_header->m_num_col];
	}

	if ( p_scene_header->m_num_tex )
	{
		p32 = &((unsigned int *)p32)[p_scene_header->m_num_tex*2];
	}

	if ( p_scene_header->m_num_shadow_faces )
	{
		p32 = (unsigned int *)(&((unsigned short *)p32)[p_scene_header->m_num_shadow_faces*3]);
		p32 = (unsigned int *)(&((NxNgc::sShadowEdge *)p32)[p_scene_header->m_num_shadow_faces]);
	}

	for( uint s = 0; s < p_scene_header->m_num_objects; ++s )
	{
		int num_mesh = p_data->m_num_meshes;

		char * p_skin = (char *)&p_data[1];
		int nbytes = p_data->m_skin.num_bytes;
		NxNgc::sDLHeader* p_dl = (NxNgc::sDLHeader*)&p_skin[nbytes];

		for( int m = 0; m < num_mesh; ++m )
		{
			if ( p_dl->m_size )
			{
				p_dl = (NxNgc::sDLHeader*)&(((char *)&p_dl[1])[p_dl->m_size]);
			}
		}

		p_data = (NxNgc::sObjectHeader*)p_dl;
	}

	// Point up hierarchy.
	uint32 * p_h = (uint32 *)p_data;
	uint32 num_hobj = *p_h++;

	CHierarchyObject * p_end = (CHierarchyObject *)p_h;

	p_end = &p_end[num_hobj];

	return (int)p_end - (int)p_start;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static CScene * s_plat_load_scene_guts( void *p_mem, CTexDict *p_tex_dict, bool add_super_sectors, bool is_sky, bool is_dictionary )
{
	if ( s_correctable ) gCorrectColor = true;

	CSector*		pSector;
	CNgcSector*	pNgcSector;

	// Create scene class instance.
	CScene	*new_scene;
	if( add_super_sectors )
	{
		// Use default size sector table.
		new_scene = new CNgcScene();
	}
	else
	{
		// Use minimum size sector table.
		new_scene = new CNgcScene( 1 );
	}

	new_scene->SetInSuperSectors( add_super_sectors );
	new_scene->SetIsSky( is_sky );

	// Get a scene id from the engine.
	CNgcScene *p_new_ngc_scene = static_cast( new_scene );

	// Create a new sScene so the engine can track assets for this scene.
	NxNgc::sScene *p_engine_scene = new NxNgc::sScene();
	p_new_ngc_scene->SetEngineScene( p_engine_scene );

	// Set the dictionary flag.
	p_engine_scene->m_is_dictionary	= is_dictionary;



//------------------------------------------------------------------------------------

	// Setup pointer to actual data.
	p_engine_scene->mp_scene_data = (NxNgc::sSceneHeader *)p_mem;
	unsigned int * p32 = (unsigned int *)&p_engine_scene->mp_scene_data[1];

	// Setup material DL pointer.
	Dbg_MsgAssert( p_engine_scene->mp_scene_data->m_num_blend_dls < 256, ( "Too many (>255) unique material DLs (%d)\n", p_engine_scene->mp_scene_data->m_num_blend_dls ) );
	p_engine_scene->mp_blend_dl = (NxNgc::sMaterialDL *)p32; 
	p_engine_scene->mp_texture_dl = (NxNgc::sTextureDL *)&p_engine_scene->mp_blend_dl[p_engine_scene->mp_scene_data->m_num_blend_dls];
	unsigned char * p8 = (unsigned char *)&p_engine_scene->mp_texture_dl[p_engine_scene->mp_scene_data->m_num_materials];

	int bytes = ( p_engine_scene->mp_scene_data->m_num_blend_dls * sizeof( NxNgc::sMaterialDL ) ) + ( p_engine_scene->mp_scene_data->m_num_materials * sizeof( NxNgc::sTextureDL ) );
	int rounded_bytes = ( bytes + 31 ) & ~31;
	int pad_bytes = rounded_bytes - bytes;

	p8 = &p8[pad_bytes];

	// Assign blend dl pointers.
	for ( uint bdl = 0; bdl < p_engine_scene->mp_scene_data->m_num_blend_dls; bdl++ )
	{
		p_engine_scene->mp_blend_dl[bdl].mp_dl = p8;
		p8 += p_engine_scene->mp_blend_dl[bdl].m_dl_size;
	}

	// Assign texture dl pointers.
	for ( uint tdl = 0; tdl < p_engine_scene->mp_scene_data->m_num_materials; tdl++ )
	{
		p_engine_scene->mp_texture_dl[tdl].mp_dl = p8;
		p8 += p_engine_scene->mp_texture_dl[tdl].m_dl_size;
	}
	p32 = (unsigned int *)p8;

	// Point past pool data.
	NxNgc::sObjectHeader* p_data = (NxNgc::sObjectHeader*)&((char*)p32)[p_engine_scene->mp_scene_data->m_num_pool_bytes];

	// Setup VC wibble data.
	p_engine_scene->mp_vc_wibble = (NxNgc::sMaterialVCWibbleKeyHeader *)p32;
	NxNgc::sMaterialVCWibbleKeyHeader *	p_key_header	= p_engine_scene->mp_vc_wibble;
	NxNgc::sMaterialVCWibbleKey *		p_key			= (NxNgc::sMaterialVCWibbleKey *)&p_key_header[1]; 
	for ( int vc = 0; vc < p_engine_scene->mp_scene_data->m_num_vc_wibbles; vc++ )
	{
		p_key_header = (NxNgc::sMaterialVCWibbleKeyHeader *)&p_key[p_key_header->m_num_frames]; 
		p_key = (NxNgc::sMaterialVCWibbleKey *)&p_key_header[1];  
	}
	p32 = (uint32 *)p_key_header;

	// Setup material header data.
	p_engine_scene->mp_material_header = (NxNgc::sMaterialHeader *)p32;
	p32 = (uint32 *)&p_engine_scene->mp_material_header[p_engine_scene->mp_scene_data->m_num_materials];

	// Setup UV wibble data.
	p_engine_scene->mp_uv_wibble = (NxNgc::sMaterialUVWibble *)p32;
	p32 = (uint32 *)&p_engine_scene->mp_uv_wibble[p_engine_scene->mp_scene_data->m_num_uv_wibbles];

	// Setup material pass data.
	p_engine_scene->mp_material_pass = (NxNgc::sMaterialPassHeader *)p32;
	p32 = (uint32 *)&p_engine_scene->mp_material_pass[p_engine_scene->mp_scene_data->m_num_pass_items];

	// Setup material data texture pointers.
	NxNgc::sMaterialHeader * p_mat = p_engine_scene->mp_material_header;
	for ( unsigned int lp = 0; lp < p_engine_scene->mp_scene_data->m_num_materials; lp++ )
	{
		p_mat->m_alpha_cutoff = p_mat->m_alpha_cutoff >= 128 ? 255 : p_mat->m_alpha_cutoff * 2;
		// Need to compensate for possible errors in alpha map.
//		int cut = (int)p_mat->m_alpha_cutoff - 16;
//		if ( cut < 0 ) cut = 0;
//		p_mat->m_alpha_cutoff = cut;

		// Setup texture pointer.
		NxNgc::sMaterialPassHeader * p_pass = &p_engine_scene->mp_material_pass[p_mat->m_pass_item];

		// Copy base pass blend mode to material.
		p_mat->m_base_blend = p_pass->m_blend_mode;

		for ( int pass = 0; pass < p_mat->m_passes; pass++ )
		{
			// If textured, resolve texture checksum...
			Nx::CNgcTexture *p_ngc_texture = static_cast( p_tex_dict->GetTexLookup()->GetItem( p_pass->m_texture.checksum ) );
			p_pass->m_texture.p_data = ( p_ngc_texture ) ? p_ngc_texture->GetEngineTexture() : NULL;

			// Temp HACK
//			p_pass->m_k = (1<<8);
//			p_pass->m_u_tile = (1<<12);
//			p_pass->m_v_tile = (1<<12);
//			p_pass->m_uv_enabled = 0;
//			p_pass->m_uv_mat[0] = (1<<12);
//			p_pass->m_uv_mat[1] = 0;
//			p_pass->m_uv_mat[2] = 0;
//			p_pass->m_uv_mat[3] = 0;







//			if ( p_pass->m_texture.checksum && !p_ngc_texture )
//			{
//				OSReport( "Failed to hook up texture!!!!!!!!!!! %08x\n", p_pass->m_texture.checksum );
//			}

//			// Adjust K value.
//			p_pass->m_mip_k += 8;

			// Switch color values.
//			u8 r;
//			u8 g;
//			u8 b;
//			u8 a;
//			r = p_pass->m_color.r;
//			g = p_pass->m_color.g;
//			b = p_pass->m_color.b;
//			a = p_pass->m_color.a;
//
//			p_pass->m_color.r = b;
//			p_pass->m_color.g = g;
//			p_pass->m_color.b = r;
//			p_pass->m_color.a = a;

//			p_pass->m_color.a = 255;

//			p_pass->m_alpha_fix = ( p_pass->m_alpha_fix ) >= 128 ? 255 : p_pass->m_alpha_fix * 2;

			// Next pass structure.
			++p_pass;
		}

		// Scan texture DL and patch texture address pointers.
		// Search for SetMode0/1 SetImage0/1/2/3.
		NxNgc::sTextureDL * p_dl = &p_engine_scene->mp_texture_dl[lp]; 
		p_pass = &p_engine_scene->mp_material_pass[p_mat->m_pass_item];

//		int search_size = ( p_dl->m_dl_size > ((5*5)+4) ) ? p_dl->m_dl_size - ((5*5)+4) : 0;

//		for ( int s = 0; s < search_size ; s++ )
		//for ( int s = 0; s < (int)p_dl->m_dl_size ; s++ )

		GX::ResolveDLTexAddr( p_dl, p_pass, p_mat->m_passes );

//		GX::begin( p_dl->mp_dl, p_dl->m_dl_size );
//		multi_mesh( p_mat, p_pass, true, true );
//		p_dl->m_dl_size = GX::end();

		++p_mat;
	}

	// Setup vertex data.
	p_engine_scene->mp_pos_pool	= NULL;
	p_engine_scene->mp_nrm_pool	= NULL;
	p_engine_scene->mp_col_pool	= NULL;
	p_engine_scene->mp_tex_pool	= NULL;

	if ( p_engine_scene->mp_scene_data->m_num_pos )
	{
		p_engine_scene->mp_pos_pool = (float *)p32;
		p32 = &((unsigned int *)p32)[p_engine_scene->mp_scene_data->m_num_pos*3];
	}

	if ( p_engine_scene->mp_scene_data->m_num_col )
	{
		p_engine_scene->mp_col_pool = (unsigned int *)p32;
		p32 = &((unsigned int *)p32)[p_engine_scene->mp_scene_data->m_num_col];
	}

	if ( p_engine_scene->mp_scene_data->m_num_tex )
	{
		p_engine_scene->mp_tex_pool = (s16 *)p32;
		p32 = (unsigned int *)&p_engine_scene->mp_tex_pool[p_engine_scene->mp_scene_data->m_num_tex*2];
	}

	if ( p_engine_scene->mp_scene_data->m_num_nrm )
	{
		p_engine_scene->mp_nrm_pool = (s16 *)p32;
		p32 = (unsigned int*)&p_engine_scene->mp_nrm_pool[p_engine_scene->mp_scene_data->m_num_nrm*3];
	}

	if ( p_engine_scene->mp_scene_data->m_num_shadow_faces )
	{
		p_engine_scene->mp_shadow_volume_mesh = (uint16*)p32;
		p32 = (unsigned int *)(&((unsigned short *)p32)[p_engine_scene->mp_scene_data->m_num_shadow_faces*3]);
		p_engine_scene->mp_shadow_edge = (NxNgc::sShadowEdge *)p32; 
		p32 = (unsigned int *)(&((NxNgc::sShadowEdge *)p32)[p_engine_scene->mp_scene_data->m_num_shadow_faces]);
	}

	// Calculate radius.
	p_engine_scene->m_sphere[X] = 0.0f;
	p_engine_scene->m_sphere[Y] = 0.0f;
	p_engine_scene->m_sphere[Z] = 0.0f;
	p_engine_scene->m_sphere[W] = 0.0f;

	for ( uint32 lp = 0; lp < p_engine_scene->mp_scene_data->m_num_pos; lp++ )
	{
		NsVector v;
		v.x = p_engine_scene->mp_pos_pool[(lp*3)+0];
		v.y = p_engine_scene->mp_pos_pool[(lp*3)+1];
		v.z = p_engine_scene->mp_pos_pool[(lp*3)+2];
		float l = v.length();

		if ( l > p_engine_scene->m_sphere[W] ) p_engine_scene->m_sphere[W] = l;
	}

	// Setup display list pointer.
	p_engine_scene->mp_dl = (NxNgc::sDLHeader *)&(((char*)&p_data[1])[p_data->m_skin.num_bytes]);

	for( uint s = 0; s < p_engine_scene->mp_scene_data->m_num_objects; ++s )
	{
		// Create a new sector to hold the incoming details.
		pSector						= p_new_ngc_scene->CreateSector();
		pNgcSector					= static_cast( pSector );

		// Generate a hanging geom for the sector, used for creating level objects etc.
		CNgcGeom	*p_Ngc_geom	= new CNgcGeom();
		p_Ngc_geom->SetScene( p_new_ngc_scene );
		pNgcSector->SetGeom( p_Ngc_geom );

		// Prepare CNgcGeom for receiving data.
		p_Ngc_geom->InitMeshList();

		// Load sector data.
		p_data = pNgcSector->LoadFromFile( p_data );
		new_scene->AddSector( pSector );

//		if( ( p_data = pNgcSector->LoadFromFile( p_file, p_data ) ) )
//		{
//			new_scene->AddSector( pSector );
//		}
	}

	// At this point we can process any scaling that may need to be applied to the positions.
	if ( p_engine_scene->mp_dl->mp_object_header->m_skin.p_data )
	{
		NxNgc::ApplyMeshScaling( p_engine_scene->mp_dl->mp_object_header );
	}

	// Point up hierarchy.
	uint32 * p_h = (uint32 *)p_data;
	uint32 num_hobj = *p_h++;

	if ( num_hobj )
	{
		p_engine_scene->mp_hierarchyObjects = (CHierarchyObject *)p_h;
		p_engine_scene->m_numHierarchyObjects = num_hobj;
		//p32 = (uint32 *)&p_engine_scene->mp_hierarchyObjects[num_hobj];

//		p_engine_scene->mp_hierarchyObjects = new CHierarchyObject[num_hobj];
//
//		File::Read( p_engine_scene->mp_hierarchyObjects, sizeof( CHierarchyObject ), num_hobj, p_file );
//
//		p_engine_scene->m_numHierarchyObjects = num_hobj;

		// Fix up hierarchical object sphere.
		Lst::HashTable< Nx::CSector > *p_sector_list = new_scene->GetSectorList();
		if( p_sector_list )
		{
			p_sector_list->IterateStart();	
			Nx::CSector *p_sector = p_sector_list->IterateNext();
			while( p_sector )
			{
				pNgcSector = static_cast( p_sector );
				CNgcGeom *p_Ngc_geom = static_cast( pNgcSector->GetGeom() );

				Lst::Head< NxNgc::sMesh >	*p_mesh_list = p_Ngc_geom->GetMeshList();
				int num_mesh = p_mesh_list->CountItems();
				if (num_mesh)
				{
					Lst::Node< NxNgc::sMesh > *mesh = p_mesh_list->GetNext();
					mesh->GetData()->mp_dl->mp_object_header->m_sphere[W] *= 2.0f;  
				}
				p_sector = p_sector_list->IterateNext();
			}
		}
	}

	if ( s_correctable ) gCorrectColor = false;

	return new_scene;

//------------------------------------------------------------------------------------





























//	// Open the scene file.
//	void* p_file = File::Open( p_name, "rb" );
//	if( !p_file )
//	{
//		Dbg_MsgAssert( p_file, ( "Couldn't open scene file %s\n", p_name ));
//		if ( s_correctable ) gCorrectColor = false;
//		return NULL;
//	}
//	
//	// Version numbers.
//	uint32 mat_version, mesh_version, vert_version;
//	File::Read( &mat_version, sizeof( uint32 ), 1, p_file );
//	File::Read( &mesh_version, sizeof( uint32 ), 1, p_file );
//	File::Read( &vert_version, sizeof( uint32 ), 1, p_file );
//	Dbg_Message( "material version %d\n", mat_version );
//	Dbg_Message( "mesh version %d\n", mesh_version );
//	Dbg_Message( "vertex version %d\n", vert_version );
//
//	// Import materials (they will now be associated at the engine-level with this scene).
//	p_engine_scene->mp_material_array = NxNgc::LoadMaterials( p_file, p_tex_dict->GetTexLookup(), &p_engine_scene->m_num_materials );
//
//	// Read number of sectors.
//	int num_sectors;
//	File::Read( &num_sectors, sizeof( int ), 1, p_file );
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//	NxNgc::sDLHeader* p_data = (NxNgc::sDLHeader*)p_engine_scene->mp_dl;
//
//	for( int s = 0; s < num_sectors; ++s )
//	{
//		// Create a new sector to hold the incoming details.
//		pSector						= p_new_ngc_scene->CreateSector();
//		pNgcSector					= static_cast( pSector );
//		
//		// Generate a hanging geom for the sector, used for creating level objects etc.
//		CNgcGeom	*p_Ngc_geom	= new CNgcGeom();
//		p_Ngc_geom->SetScene( p_new_ngc_scene );
//		pNgcSector->SetGeom( p_Ngc_geom );
//		
//		// Prepare CNgcGeom for receiving data.
//		p_Ngc_geom->InitMeshList();
//		
//		// Load sector data.
//		p_data = pNgcSector->LoadFromFile( p_file, p_data );
//		new_scene->AddSector( pSector );
//
////		if( ( p_data = pNgcSector->LoadFromFile( p_file, p_data ) ) )
////		{
////			new_scene->AddSector( pSector );
////		}
//	}
//
//	// Load hierarchy.
//	uint32 num_hobj;
//	File::Read( &num_hobj, sizeof( uint32 ), 1, p_file );
//
//	if ( num_hobj )
//	{
//		p_engine_scene->mp_hierarchyObjects = new CHierarchyObject[num_hobj];
//
//		File::Read( p_engine_scene->mp_hierarchyObjects, sizeof( CHierarchyObject ), num_hobj, p_file );
//
//		p_engine_scene->m_numHierarchyObjects = num_hobj;
//	}
//
//	// At this point get the engine scene to figure it's bounding volumes.
//	p_engine_scene->FigureBoundingVolumes();
//	
//	File::Close( p_file );
//
//	if ( s_correctable ) gCorrectColor = false;
//
//	return new_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CScene *CEngine::s_plat_load_scene_from_memory( void *p_mem, CTexDict *p_tex_dict, bool add_super_sectors, bool is_sky, bool is_dictionary )
{
	int size = s_plat_scene_size( p_mem, p_tex_dict, add_super_sectors, is_sky, is_dictionary );

	int mem_available;
	bool need_to_pop = false;
	if ( g_in_cutscene )
	{
		Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
		mem_available = Mem::Manager::sHandle().Available();
		if ( size < ( mem_available - ( 40 * 1024 ) ) )
		{
			need_to_pop = true;
		}
		else
		{
			Mem::Manager::sHandle().PopContext();
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
			mem_available = Mem::Manager::sHandle().Available();
			if ( size < ( mem_available - ( 5 * 1024 ) ) )
			{
				need_to_pop = true;
			}
			else
			{
				Mem::Manager::sHandle().PopContext();
				Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ScriptHeap() );
				mem_available = Mem::Manager::sHandle().Available();
				if ( size < ( mem_available - ( 40 * 1024 ) ) )
				{
					need_to_pop = true;
				}
				else
				{
					Mem::Manager::sHandle().PopContext();
				}
			}
		}
	}

	Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
	char * p_scene_data = new char[size]; 
	Mem::Manager::sHandle().BottomUpHeap()->PopAlign();

	if ( need_to_pop )
	{
		Mem::Manager::sHandle().PopContext();
	}

	memcpy( p_scene_data, p_mem, size );
	DCFlushRange( p_scene_data, size );

	return s_plat_load_scene_guts( p_scene_data, p_tex_dict, add_super_sectors, is_sky, is_dictionary );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CScene*	CEngine::s_plat_load_scene( const char *p_name, CTexDict *p_tex_dict, bool add_super_sectors, bool is_sky, bool is_dictionary )
{
	Dbg_Message( "loading scene from file %s\n", p_name );

	// Open the scene file.
	char gdname[128];
	sprintf( gdname, "%s", p_name );
	void* p_gd_file = File::Open( gdname, "rb" );
	if( !p_gd_file )
	{
		Dbg_MsgAssert( p_gd_file, ( "Couldn't open scene file %s\n", gdname ));
		if ( s_correctable ) gCorrectColor = false;
		return NULL;
	}

	// Read all data.
	int size = File::GetFileSize( p_gd_file );
	Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
	char * p_scene_data = new char[size]; 
	Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
	File::Read( p_scene_data, size, 1, p_gd_file );
	
	File::Close( p_gd_file );

	DCFlushRange( p_scene_data, size );
	return s_plat_load_scene_guts( p_scene_data, p_tex_dict, add_super_sectors, is_sky, is_dictionary );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CEngine::s_plat_add_scene( CScene *p_scene, const char *p_filename )
{
	// Function to incrementally add geometry to a scene - should NOT be getting called on GameCube.
	Dbg_Assert( 0 );
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CEngine::s_plat_unload_scene( CScene *p_scene )
{
	Dbg_MsgAssert( p_scene,( "Trying to delete a NULL scene" ));

	CNgcScene *p_ngc_scene = (CNgcScene*)p_scene;

	// Ask the engine to remove the associated meshes for each sector in the scene.
	p_ngc_scene->DestroySectorMeshes();

	// Get the engine specific scene data and pass it to the engine to delete.
	NxNgc::DeleteScene( p_ngc_scene->GetEngineScene());
	p_ngc_scene->SetEngineScene( NULL );
	
	return true;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModel*		CEngine::s_plat_init_model( void )
{
	CNgcModel* pModel = new CNgcModel;

	return pModel;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CEngine::s_plat_uninit_model(CModel* pModel)
{
	Dbg_Assert( pModel );

	delete pModel;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMesh* CEngine::s_plat_load_mesh( const char* pMeshFileName, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume )
{
	s_correctable = false;
	// Load the scene.
	Nx::CScene *p_scene = Nx::CEngine::s_plat_load_scene( pMeshFileName, pTexDict, false, false, false );
	p_scene->SetID(Script::GenerateCRC( pMeshFileName )); 	// store the checksum of the scene name
	p_scene->SetTexDict( pTexDict );
	p_scene->PostLoad( pMeshFileName );
	
	// Disable any scaling.
	NxNgc::DisableMeshScaling();
	
	CNgcMesh *pMesh = new CNgcMesh( pMeshFileName );
	Nx::CNgcScene *p_Ngc_scene = static_cast( p_scene );
	pMesh->SetScene( p_Ngc_scene );
	pMesh->SetTexDict( pTexDict );
	s_correctable = true;
	return pMesh;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CMesh* CEngine::s_plat_load_mesh( uint32 id, uint32 *p_model_data, int model_data_size, uint8 *p_cas_data, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume )
{
	// Convert the id into a usable string.
	Dbg_Assert( id > 0 );
	char id_as_string[16];
	sprintf( id_as_string, "%d\n", id );

	// Load the scene.
	Nx::CScene *p_scene = Nx::CEngine::s_plat_load_scene_from_memory( p_model_data, pTexDict, false, false, false );

	// Store the checksum of the scene name.
	p_scene->SetID( Script::GenerateCRC( id_as_string ));

	p_scene->SetTexDict( pTexDict );
	p_scene->PostLoad( id_as_string );
	
	// Disable any scaling.
	NxNgc::DisableMeshScaling();
	
	CNgcMesh *pMesh = new CNgcMesh();

	// Set CAS data for mesh.
	pMesh->SetCASData( p_cas_data );

	Nx::CNgcScene *p_Ngc_scene = static_cast( p_scene );
	pMesh->SetScene( p_Ngc_scene );
	pMesh->SetTexDict( pTexDict );
	return pMesh;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CEngine::s_plat_unload_mesh(CMesh* pMesh)
{
	Dbg_Assert( pMesh );

	delete pMesh;

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_mesh_scaling_parameters( SMeshScalingParameters* pParams )
{
	NxNgc::SetMeshScalingParameters( pParams );
}



///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//CTexDict* CEngine::s_plat_load_textures( const char* p_name )
//{
////	NxNgc::LoadTextureFile( p_name );
//	return NULL;
//}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSprite *	CEngine::s_plat_create_sprite(CWindow2D *p_window)
{
	return new CNgcSprite;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CEngine::s_plat_destroy_sprite( CSprite *p_sprite )
{
	delete p_sprite;
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextured3dPoly *	CEngine::s_plat_create_textured_3d_poly()
{
	return new NxNgc::CNgcTextured3dPoly;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CEngine::s_plat_destroy_textured_3d_poly(CTextured3dPoly *p_poly)
{
	delete p_poly;
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::CTexture *CEngine::s_plat_create_render_target_texture( int width, int height, int depth, int z_depth )
{
	// Create the CNgcTexture (just a container class for the NxNgc::sTexture).
	CNgcTexture *p_texture = new CNgcTexture();

	// Create the NxNgc::sTexture.
	NxNgc::sTexture *p_engine_texture = new NxNgc::sTexture;
	p_texture->SetEngineTexture( p_engine_texture );
	
	// Set the texture as a render target.
	p_engine_texture->SetRenderTarget( width, height, depth, z_depth );
	
	return p_texture;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEngine::s_plat_project_texture_into_scene( Nx::CTexture *p_texture, Nx::CModel *p_model, Nx::CScene *p_scene )
{
	Nx::CNgcTexture		*p_ngc_texture	= static_cast( p_texture );
	Nx::CNgcModel		*p_ngc_model	= static_cast( p_model );
//	Nx::CNgcScene		*p_ngc_scene	= static_cast( p_scene );
//	NxNgc::create_texture_projection_details( p_ngc_texture->GetEngineTexture(), p_ngc_model, p_ngc_scene->GetEngineScene());
	NxNgc::create_texture_projection_details( p_ngc_texture->GetEngineTexture(), p_ngc_model, NULL);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEngine::s_plat_set_projection_texture_camera( Nx::CTexture *p_texture, Gfx::Camera *p_camera )
{
	Nx::CNgcTexture		*p_ngc_texture	= static_cast( p_texture );
	NsVector			pos( p_camera->GetPos()[X], p_camera->GetPos()[Y], p_camera->GetPos()[Z] );
	NsVector			at( pos.x + p_camera->GetMatrix()[Z][X], pos.y + p_camera->GetMatrix()[Z][Y], pos.z + p_camera->GetMatrix()[Z][Z] );
	
	NxNgc::set_texture_projection_camera( p_ngc_texture->GetEngineTexture(), &pos, &at );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEngine::s_plat_stop_projection_texture( Nx::CTexture *p_texture )
{
	Nx::CNgcTexture *p_ngc_texture = static_cast( p_texture );
	NxNgc::destroy_texture_projection_details( p_ngc_texture->GetEngineTexture());
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_add_occlusion_poly( uint32 num_verts, Mth::Vector *p_vert_array, uint32 checksum )
{
	if( num_verts == 4 )
	{
		NxNgc::AddOcclusionPoly( p_vert_array[0], p_vert_array[1], p_vert_array[2], p_vert_array[3], checksum );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_enable_occlusion_poly( uint32 checksum, bool enable )
{
	NxNgc::EnableOcclusionPoly( checksum, enable );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_remove_all_occlusion_polys( void )
{
	NxNgc::RemoveAllOcclusionPolys();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// returns true if the sphere at "center", with the "radius"
// is visible to the current camera
// (note, currently this is the last frame's camera on PS2)

bool CEngine::s_plat_is_visible( Mth::Vector&	center, float radius  )
{
	Mth::Vector v;
	v[X] = center[X];
	v[Y] = center[Y];
	v[Z] = center[Z];
	v[W] = radius;

	return NxNgc::IsVisible( v );
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const char* CEngine::s_plat_get_platform_extension( void )
{
	// String literals are statically allocated so can be returned safely, (Bjarne, p90)
	return "ngc";
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom* CEngine::s_plat_init_geom( void )
{
	CNgcGeom* pGeom = new CNgcGeom;

	return pGeom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CEngine::s_plat_uninit_geom(CGeom* pGeom)
{
	Dbg_Assert( pGeom );

	delete pGeom;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CQuickAnim* CEngine::s_plat_init_quick_anim()
{
	CQuickAnim* pQuickAnim = new CQuickAnim;

	return pQuickAnim;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEngine::s_plat_uninit_quick_anim(CQuickAnim* pQuickAnim)
{
	Dbg_Assert( pQuickAnim );

	delete pQuickAnim;

	return;
}

/******************************************************************/
// Wait for any pending asyncronous rendering to finish, so rendering
// data can be unloaded
/******************************************************************/

void CEngine::s_plat_finish_rendering()
{
	// TODO:  Flush pending rendering, so data can be unloaded
	NsDisplay::flush();
} 

/******************************************************************/
// Set the amount that the previous frame is blended with this frame
// 0 = none	  	(just see current frame) 	
// 128 = 50/50
// 255 = 100% 	(so you only see the previous frame)												  
/******************************************************************/

void CEngine::s_plat_set_screen_blur(uint32 amount )
{
//	g_blur = ( amount * 8 ) / 255;
//	sBlur = amount;
} 


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	s_plat_get_num_soundtracks()
{
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* s_plat_get_soundtrack_name( int soundtrack_number )
{
	return NULL;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int	CEngine::s_plat_get_num_soundtracks( void )
{
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* CEngine::s_plat_get_soundtrack_name( int soundtrack_number )
{
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_max_multipass_distance(float dist)
{
//	NxPs2::render::sMultipassMaxDist = dist;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_letterbox( bool letterbox )
{
	// Letterbox mode is designed for use on a regular 4:3 screen.
	// It should use the same, wider viewing angle as for widescreen mode, but shrink the resultant image down
	// vertically by 25%.
	if( letterbox )
	{
		if( NxNgc::EngineGlobals.letterbox_active == false )
		{
			// Need to adjust the screen y offset and multiplier to ensure sprites are scaled properly for this mode.
//			NxNgc::EngineGlobals.screen_conv_y_offset		+= ( NxXbox::EngineGlobals.backbuffer_height / 4 ) / 2;
//			NxNgc::EngineGlobals.screen_conv_y_multiplier	= 0.75f;
			NxNgc::EngineGlobals.letterbox_active			= true;
		}
	}
	else
	{
		if( NxNgc::EngineGlobals.letterbox_active == true )
		{
			// Restore the screen y offset and multiplier.
//			NxNgc::EngineGlobals.screen_conv_y_offset		-= ( NxXbox::EngineGlobals.backbuffer_height / 4 ) / 2;
//			NxNgc::EngineGlobals.screen_conv_y_multiplier	= 1.0f;
			NxNgc::EngineGlobals.letterbox_active			= false;
		}
	}
} 
 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_color_buffer_clear( bool clear )
{
}



} // namespace Nx




================================================
FILE: Code/Gfx/NGC/p_nxanimcache.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_NxAnimCache.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  5/06/2002
//****************************************************************************

#include 

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcAnimCache::CNgcAnimCache( int lookupTableSize ) : CAnimCache( lookupTableSize )
{
	// Machine specific code here ............
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcAnimCache::~CNgcAnimCache()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx
				



================================================
FILE: Code/Gfx/NGC/p_nxanimcache.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_NxAnimCache.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  5/06/2002
//****************************************************************************

#ifndef	__GFX_P_NXANIMCACHE_H__
#define	__GFX_P_NXANIMCACHE_H__
    
#include 

namespace Nx
{
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CGeom
    
class CNgcAnimCache : public CAnimCache
{
                                      
public:
						CNgcAnimCache( int lookupTableSize );
	virtual 			~CNgcAnimCache();

private:				// It's all private, as it is machine specific
};

} // Nx

#endif 



================================================
FILE: Code/Gfx/NGC/p_nxfont.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxFont.cpp

#include 	"gfx/Ngc/p_nxfont.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CFont

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcFont::CNgcFont() : mp_plat_font(NULL)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcFont::~CNgcFont()
{
	if (mp_plat_font)
	{
		plat_unload();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcFont::plat_load(const char *filename)
{
	mp_plat_font = NxNgc::LoadFont(filename);

	return (mp_plat_font != NULL);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcFont::plat_set_spacings(int charSpacing, int spaceSpacing)
{
	mp_plat_font->mCharSpacing = charSpacing;
	if (spaceSpacing > 0)
		mp_plat_font->mSpaceSpacing = spaceSpacing;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcFont::plat_set_rgba_table(Image::RGBA *pTab)
{
	for (int i = 0; i < 16; i++)
		mp_plat_font->mRGBATab[i] = *((uint32 *) &pTab[i]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcFont::plat_mark_as_button_font(bool isButton)
{
	NxNgc::pButtonsFont = (isButton) ? mp_plat_font : NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcFont::plat_unload()
{
	NxNgc::UnloadFont(mp_plat_font);
	mp_plat_font = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 CNgcFont::plat_get_default_height() const
{
	Dbg_Assert(mp_plat_font);

	return mp_plat_font->GetDefaultHeight();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 CNgcFont::plat_get_default_base() const
{
	Dbg_Assert(mp_plat_font);

	return mp_plat_font->GetDefaultBase();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//void CNgcFont::plat_begin_text(uint32 rgba, float Scale)
//{
//	Dbg_Assert(mp_plat_font);
//
//	mp_plat_font->BeginText(rgba, Scale);
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//void CNgcFont::plat_draw_string(char *String, float x0, float y0)
//{
//	Dbg_Assert(mp_plat_font);
//
//	mp_plat_font->DrawString(String, x0, y0);
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//void CNgcFont::plat_end_text(void)
//{
//	Dbg_Assert(mp_plat_font);
//
//	mp_plat_font->EndText();
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcFont::plat_query_string(char *String, float &width, float &height) const
{
	Dbg_Assert(mp_plat_font);

	mp_plat_font->QueryString(String, width, height);
}




/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CText

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcText::CNgcText()
{
	mp_plat_text = new NxNgc::SText();

	plat_initialize();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcText::~CNgcText()
{
	if( mp_plat_text )
	{
		delete mp_plat_text;
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcText::plat_initialize()
{
	plat_update_engine();
	plat_update_priority();
	plat_update_hidden();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcText::plat_update_hidden()
{
	mp_plat_text->SetHidden(m_hidden);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcText::plat_update_engine()
{
	CNgcFont *p_Ngc_font = static_cast( mp_font );

	mp_plat_text->mp_string	= m_string;
	if( p_Ngc_font)
	{
		mp_plat_text->mp_font = p_Ngc_font->GetEngineFont();
	}

	mp_plat_text->m_xpos	= m_xpos;
	mp_plat_text->m_ypos	= m_ypos;
	mp_plat_text->m_xscale	= m_xscale;
	mp_plat_text->m_yscale	= m_yscale;
	mp_plat_text->m_rgba	= *((uint32 *) &m_rgba);
	mp_plat_text->m_color_override = m_color_override;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcText::plat_update_priority( void )
{
	// Update draw list
	mp_plat_text->SetPriority( m_priority );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CTextMan::s_plat_alloc_text_pool( void )
{
	for( int i = 0; i < vMAX_TEXT_INSTANCES; ++i )
	{
	   	CNgcText *p_text		= new CNgcText;
		p_text->mp_next			= sp_dynamic_text_list;
		sp_dynamic_text_list	= p_text;
	}
}






} // Namespace Nx  			
				
				



================================================
FILE: Code/Gfx/NGC/p_nxfont.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxFont.h

#ifndef	__GFX_P_NX_FONT_H__
#define	__GFX_P_NX_FONT_H__

#include 	"gfx/nxfont.h"
#include 	"gfx/Ngc/nx/chars.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CFont
class CNgcFont : public CFont
{
public:
								CNgcFont();
	virtual						~CNgcFont();
	NxNgc::SFont				*GetEngineFont() const;

private:		// It's all private, as it is machine specific
	virtual	bool				plat_load(const char *filename);
	virtual void				plat_set_spacings(int charSpacing, int spaceSpacing);
	virtual void				plat_set_rgba_table(Image::RGBA *pTab);
	virtual void 				plat_mark_as_button_font(bool isButton);
	virtual void				plat_unload();

	virtual	uint32				plat_get_default_height() const;
	virtual	uint32				plat_get_default_base() const;
//	virtual void				plat_begin_text(uint32 rgba, float Scale);
//	virtual void				plat_draw_string(char *String, float x0, float y0);
//	virtual void				plat_end_text(void);
	virtual void				plat_query_string(char *String, float &width, float &height) const;

	// Machine specific members
	NxNgc::SFont *				mp_plat_font;		// Pointer to engine font
};


/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CText
class	CNgcText : public CText
{
public:
								CNgcText();
	virtual						~CNgcText();

private:
	//
	virtual void				plat_initialize();

	virtual void				plat_update_hidden();		// Tell engine of update
	virtual void				plat_update_engine();		// Update engine primitives
	virtual void				plat_update_priority();

	// Machine specific members
	NxNgc::SText				*mp_plat_text;		// Pointer to engine text
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline NxNgc::SFont *CNgcFont::GetEngineFont() const
{
	return mp_plat_font;
}


} // Namespace Nx  			

#endif



================================================
FILE: Code/Gfx/NGC/p_nxfontman.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:						 		 									**
**																			**
**	File name:		gfx/Ngc/p_nxfontman.cpp								**
**																			**
**	Created:		01/16/2002	-	dc										**
**																			**
**	Description:	Ngc platform specific interface to the Font Manager	**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include	"gfx\nx.h"
#include	"gfx\NxFontMan.h"
#include	"gfx\Ngc\p_NxFont.h"
#include 	"gfx\Ngc\nx\chars.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

namespace Nx
{
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CFont* CFontManager::s_plat_load_font( const char* pName )
{
	CNgcFont *p_new_font = new CNgcFont;
	p_new_font->Load( pName );
	return p_new_font;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFontManager::s_plat_unload_font( CFont* pFont )
{
	pFont->Unload();
}

} // namespace Nx
 



================================================
FILE: Code/Gfx/NGC/p_nxlight.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxLight.cpp - Ngc platform specific interface to CModelLights

#include 

#include 
#include 
#include "sys/ngc/p_gx.h"


namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcModelLights::CNgcModelLights()
{
	m_flags = 0;

	EnableAmbientLight( false );
	for( int i = 0; i < CLightManager::MAX_LIGHTS; ++i )
	{
		EnableDiffuseLight( i, false );
	}
	
	// Set valid default direction.
	m_diffuse_direction[0].Set( 0.0f, 1.0f, 0.0f );
	m_diffuse_direction[1].Set( 0.0f, 1.0f, 0.0f );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcModelLights::~CNgcModelLights()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcModelLights::plat_update_brightness()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcModelLights::plat_update_engine( Mth::Vector & pos, bool add_scene_light )
{
	if( m_flags & mUSE_MODEL_AMBIENT )
	{
		// Use the local ambient color, modulate it with the local ambient brightness.
		int difr = (int)((float)m_ambient_color.r * m_ambient_brightness);
		int difg = (int)((float)m_ambient_color.g * m_ambient_brightness);
		int difb = (int)((float)m_ambient_color.b * m_ambient_brightness);
		NxNgc::EngineGlobals.ambient_light_color.r = difr > 255 ? 255 : (u8)difr; 
		NxNgc::EngineGlobals.ambient_light_color.g = difg > 255 ? 255 : (u8)difg; 
		NxNgc::EngineGlobals.ambient_light_color.b = difb > 255 ? 255 : (u8)difb; 
	}
	else
	{
		// Use the default ambient color, but modulate it with the local ambient brightness.
		Image::RGBA	amb = CLightManager::sGetLightAmbientColor();
		int difr = (int)((float)amb.r * m_ambient_brightness);
		int difg = (int)((float)amb.g * m_ambient_brightness);
		int difb = (int)((float)amb.b * m_ambient_brightness);
		NxNgc::EngineGlobals.ambient_light_color.r = difr > 255 ? 255 : (u8)difr;  
		NxNgc::EngineGlobals.ambient_light_color.g = difg > 255 ? 255 : (u8)difg;  
		NxNgc::EngineGlobals.ambient_light_color.b = difb > 255 ? 255 : (u8)difb;  
	}

//	NxNgc::EngineGlobals.ambient_light_color.r = NxNgc::EngineGlobals.ambient_light_color.r < 128 ? NxNgc::EngineGlobals.ambient_light_color.r * 2 : 255;
//	NxNgc::EngineGlobals.ambient_light_color.g = NxNgc::EngineGlobals.ambient_light_color.g < 128 ? NxNgc::EngineGlobals.ambient_light_color.g * 2 : 255;
//	NxNgc::EngineGlobals.ambient_light_color.b = NxNgc::EngineGlobals.ambient_light_color.b < 128 ? NxNgc::EngineGlobals.ambient_light_color.b * 2 : 255;

	for( int i = 0; i < 2; ++i )
	{
		if( m_flags & (( i == 0 ) ? mUSE_MODEL_DIFFUSE_0 : mUSE_MODEL_DIFFUSE_1 ))
		{
			// Use the local directional color, modulate it with the local directional brightness.
			int difr = (int)((float)m_diffuse_color[i].r * m_diffuse_brightness[i] * 0.5f);
			int difg = (int)((float)m_diffuse_color[i].g * m_diffuse_brightness[i] * 0.5f);
			int difb = (int)((float)m_diffuse_color[i].b * m_diffuse_brightness[i] * 0.5f);
			NxNgc::EngineGlobals.diffuse_light_color[i].r = difr > 255 ? 255 : (u8)difr;
			NxNgc::EngineGlobals.diffuse_light_color[i].g = difg > 255 ? 255 : (u8)difg;
			NxNgc::EngineGlobals.diffuse_light_color[i].b = difb > 255 ? 255 : (u8)difb;

			// Use the local direction.
			m_diffuse_direction[i].Normalize();
			NxNgc::EngineGlobals.light_x[i] = m_diffuse_direction[i][X] * 200000.0f;
			NxNgc::EngineGlobals.light_y[i] = m_diffuse_direction[i][Y] * 200000.0f;
			NxNgc::EngineGlobals.light_z[i] = m_diffuse_direction[i][Z] * 200000.0f;
		}
		else
		{
			// Use the default directional color, but modulate it with the local directional brightness.
			Image::RGBA	dif = CLightManager::sGetLightDiffuseColor( i );
			int difr = (int)((float)dif.r * m_diffuse_brightness[i] * 0.5f);
			int difg = (int)((float)dif.g * m_diffuse_brightness[i] * 0.5f);
			int difb = (int)((float)dif.b * m_diffuse_brightness[i] * 0.5f);
			NxNgc::EngineGlobals.diffuse_light_color[i].r = difr > 255 ? 255 : (u8)difr;
			NxNgc::EngineGlobals.diffuse_light_color[i].g = difg > 255 ? 255 : (u8)difg;
			NxNgc::EngineGlobals.diffuse_light_color[i].b = difb > 255 ? 255 : (u8)difb;

			// Use the default direction.
			Mth::Vector dir = CLightManager::sGetLightDirection( i );
			dir.Normalize();
			NxNgc::EngineGlobals.light_x[i] = dir[X] * 200000.0f;
			NxNgc::EngineGlobals.light_y[i] = dir[Y] * 200000.0f;
			NxNgc::EngineGlobals.light_z[i] = dir[Z] * 200000.0f;
		}
//		NxNgc::EngineGlobals.diffuse_light_color[i].r = NxNgc::EngineGlobals.diffuse_light_color[i].r < 128 ? NxNgc::EngineGlobals.diffuse_light_color[i].r * 2 : 255;
//		NxNgc::EngineGlobals.diffuse_light_color[i].g = NxNgc::EngineGlobals.diffuse_light_color[i].g < 128 ? NxNgc::EngineGlobals.diffuse_light_color[i].g * 2 : 255;
//		NxNgc::EngineGlobals.diffuse_light_color[i].b = NxNgc::EngineGlobals.diffuse_light_color[i].b < 128 ? NxNgc::EngineGlobals.diffuse_light_color[i].b * 2 : 255;
	}

	if( add_scene_light )
	{
		Nx::CSceneLight *p_scene_light = CLightManager::sGetOptimumSceneLight( pos );
		if( p_scene_light )
		{
			Mth::Vector light_pos = p_scene_light->GetLightPosition();

			float dist	= Mth::Distance( pos, light_pos );
			float ratio	= dist * p_scene_light->GetLightReciprocalRadius();

			light_pos = ( pos - light_pos ).Normalize();

			// Figure the direction...
			NxNgc::EngineGlobals.light_x[2] = -light_pos[X] * 200000.0f;
			NxNgc::EngineGlobals.light_y[2] = -light_pos[Y] * 200000.0f;
			NxNgc::EngineGlobals.light_z[2] = -light_pos[Z] * 200000.0f;

			// ...and the color.
			ratio = sqrtf( 1.0f - ratio ) * p_scene_light->GetLightIntensity();
			int difr = (int)((float)p_scene_light->GetLightColor().r * ratio * 0.5f);
			int difg = (int)((float)p_scene_light->GetLightColor().g * ratio * 0.5f);
			int difb = (int)((float)p_scene_light->GetLightColor().b * ratio * 0.5f);
			NxNgc::EngineGlobals.diffuse_light_color[2].r	= difr > 255 ? 255 : (u8)difr;
			NxNgc::EngineGlobals.diffuse_light_color[2].g	= difg > 255 ? 255 : (u8)difg;
			NxNgc::EngineGlobals.diffuse_light_color[2].b	= difb > 255 ? 255 : (u8)difb;
		}
		else
		{
			// Disbale this light by setting zero color.
			NxNgc::EngineGlobals.diffuse_light_color[2].r = 0;
			NxNgc::EngineGlobals.diffuse_light_color[2].g = 0;
			NxNgc::EngineGlobals.diffuse_light_color[2].b = 0;
		}
	}

//	// Set lighting.
//	GXLightObj light_obj[2];
//
//	GX::SetChanAmbColor( GX_COLOR0A0, NxNgc::EngineGlobals.ambient_light_color );
//	GX::SetChanAmbColor( GX_COLOR1A1, NxNgc::EngineGlobals.ambient_light_color );
//
//	GXInitLightColor( &light_obj[0], NxNgc::EngineGlobals.diffuse_light_color[0] );
//	GXInitLightPos( &light_obj[0], NxNgc::EngineGlobals.light_x[0], NxNgc::EngineGlobals.light_y[0], NxNgc::EngineGlobals.light_z[0] );
//	GXLoadLightObjImm( &light_obj[0], GX_LIGHT0 );
//
//	GXInitLightColor( &light_obj[1], NxNgc::EngineGlobals.diffuse_light_color[1] );
//	GXInitLightPos( &light_obj[1], NxNgc::EngineGlobals.light_x[1], NxNgc::EngineGlobals.light_y[1], NxNgc::EngineGlobals.light_z[1] );
//	GXLoadLightObjImm( &light_obj[1], GX_LIGHT1 );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcModelLights::plat_set_light_ambient_color( const Image::RGBA &rgba )
{
	m_ambient_color = rgba;
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA	CNgcModelLights::plat_get_light_ambient_color() const
{
	return m_ambient_color;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcModelLights::plat_set_light_direction( int light_index, const Mth::Vector &direction )
{
	m_diffuse_direction[light_index] = direction;
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector &	CNgcModelLights::plat_get_light_direction( int light_index ) const
{
	if( plat_is_diffuse_light_enabled( light_index ))
		return m_diffuse_direction[light_index];
	else
		return Nx::CLightManager::sGetLightDirection( light_index );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcModelLights::plat_set_light_diffuse_color( int light_index, const Image::RGBA &rgba )
{
	m_diffuse_color[light_index] = rgba;
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA	CNgcModelLights::plat_get_light_diffuse_color( int light_index ) const
{
	return m_diffuse_color[light_index];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcModelLights::plat_enable_ambient_light( bool enable )
{
	if( enable )
		m_flags |= mUSE_MODEL_AMBIENT;
	else
		m_flags &= ~mUSE_MODEL_AMBIENT;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcModelLights::plat_enable_diffuse_light( int light_index, bool enable )
{
	if( enable )
		m_flags |= ( light_index == 0 ) ? mUSE_MODEL_DIFFUSE_0 : mUSE_MODEL_DIFFUSE_1;
	else
		m_flags &= ~(( light_index == 0 ) ? mUSE_MODEL_DIFFUSE_0 : mUSE_MODEL_DIFFUSE_1 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcModelLights::plat_is_ambient_light_enabled() const
{
	return ( m_flags & mUSE_MODEL_AMBIENT ) > 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcModelLights::plat_is_diffuse_light_enabled( int light_index ) const
{
	return (( light_index == 0 ) ? (( m_flags & mUSE_MODEL_DIFFUSE_0 ) > 0 ) : (( m_flags & mUSE_MODEL_DIFFUSE_1 ) > 0 ));
}

} 
 



================================================
FILE: Code/Gfx/NGC/p_nxlight.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// p_NxLight.H - Neversoft Engine, Rendering portion, Platform dependent interface

#ifndef	__GFX_P_NX_LIGHT_H__
#define	__GFX_P_NX_LIGHT_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
#include 
#include 

namespace Nx
{

///////////////////////////////////////////////////////////////////////////////////
// Nx::CNgcModelLights
class	CNgcModelLights : public CModelLights
{
public:
								CNgcModelLights();
	virtual						~CNgcModelLights();

private:	

	// Platform-specific calls
	virtual void				plat_update_engine( Mth::Vector & pos, bool add_scene_light );
	virtual bool				plat_set_light_ambient_color(const Image::RGBA &rgba);
	virtual bool				plat_set_light_direction(int light_index, const Mth::Vector &direction);
	virtual bool				plat_set_light_diffuse_color(int light_index, const Image::RGBA &rgba);
	virtual Image::RGBA			plat_get_light_ambient_color() const;
	virtual const Mth::Vector &	plat_get_light_direction(int light_index) const;
	virtual Image::RGBA			plat_get_light_diffuse_color(int light_index) const;

	virtual void				plat_update_brightness();

	virtual void				plat_enable_ambient_light(bool enable);
	virtual void				plat_enable_diffuse_light(int light_index, bool enable);
	virtual bool				plat_is_ambient_light_enabled() const;
	virtual bool				plat_is_diffuse_light_enabled(int light_index) const;

	uint32						m_flags;
	Image::RGBA					m_ambient_color;
	Image::RGBA					m_diffuse_color[CLightManager::MAX_LIGHTS];
	Mth::Vector					m_diffuse_direction[CLightManager::MAX_LIGHTS];
};


}


#endif




================================================
FILE: Code/Gfx/NGC/p_nxloadscreen.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxLoadScreen.cpp - PS2 platform specific interface for the load screen
//
// This is PS2 SPECIFIC!!!!!!  So might get a bit messy
//

#include	"gfx\Nx.h"
#include	"gfx\NxLoadScreen.h"
#include	"gfx\ngc\p_NxTexture.h"
#include	"gfx\ngc\p_NxSprite.h"
#include	"gfx\ngc\NX\sprite.h"
#include	"gfx\ngc\NX\render.h"
#include 

#include	"core\macros.h"
#include	"sys/config/config.h"

#include 
#include 
#include 
#include 
#include "gfx/ngc/nx/nx_init.h"
#include	
#include 

bool		gReload = false;
bool		gLoadingLoadScreen = false;
bool		gLoadingBarActive = false;
bool		gLoadingScreenActive = false;
int			gLoadBarTotalFrames;		// Number of frames it takes for loading bar to go to 100%
int			gLoadBarNumFrames;			// Number of frames so far
int			gLoadBarX = 150;			// Bar position
int			gLoadBarY = 400;
int			gLoadBarWidth = 340;		// Bar size
int			gLoadBarHeight = 20;
int			gLoadBarStartColor[4] = {  255,    0,   0,  128 };
int			gLoadBarDeltaColor[4] = { -255,  255,   0,  128 };
int			gLoadBarBorderWidth = 5;	// Border width
int			gLoadBarBorderHeight = 5;	// Border height
int			gLoadBarBorderColor[4] = {  40, 40, 40, 128 };

//static bool				s_loading_screen_on		= false;
static OSThread			s_load_icon_thread;
static OSAlarm			s_alarm;
//static LoadingIconData	s_load_icon_data		= { 0 };
//static char				s_load_icon_thread_stack[4096]	__attribute__ (( aligned( 16 )));
static volatile bool	s_terminate_thread		= false;
static volatile bool	s_terminate_thread_done	= false;
bool				s_thread_exists			= false;

extern char * g_p_buffer;

namespace	Nx
{


Nx::CNgcTexture	*sp_load_screen_texture;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void s_thread_loading_icon_alarm_handler( OSAlarm* alarm, OSContext* context )
{
	// Check the thread has not been resumed yet...
	if( OSIsThreadSuspended( &s_load_icon_thread ))
	{
		OSResumeThread( &s_load_icon_thread );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void* s_threaded_loading_icon( void* data )
{
	while( 1 )
	{
		if( s_terminate_thread )
		{
			// This thread is done...
			s_terminate_thread_done	= true;
			return NULL;
		}

		gLoadBarNumFrames++;

		bool busy = NxNgc::EngineGlobals.gpuBusy;

//		// Don't want to do draw anything whilst display lists are being built. Come back in a bit.
//		if( !busy )
		{
//			NsTexture* p_texture = p_data->m_Rasters[current_image];

//			NsDisplay::begin();
//			NsRender::begin();
//			NsPrim::begin();
//
////		    GXSetVtxAttrFmt( GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0 );		// 3 = positions & uvs.
////			GXSetVtxAttrFmt( GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0 );
////
//			NsCamera camera2D;
//			camera2D.orthographic( 0, 0, 640, 448 );
//
//			camera2D.begin();
//
////			NsRender::setBlendMode( NsBlendMode_None, NULL, (unsigned char)0 );
////
////			GXClearVtxDesc();
////			GXSetVtxDesc( GX_VA_POS, GX_DIRECT );
////			GXSetVtxDesc( GX_VA_TEX0, GX_DIRECT );
////			GX::SetChanMatColor( GX_COLOR0, (GXColor){ 128,128,128,128 });
////			GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
////
////			p_texture->upload( NsTexture_Wrap_Clamp );
////
////			GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
////			GXPosition3f32((float)p_data->m_X, (float)p_data->m_Y, -1.0f );
////			GXTexCoord2f32( 0.0f, 0.0f );
////			GXPosition3f32((float)p_data->m_X + (float)p_texture->m_width, (float)p_data->m_Y, -1.0f );
////			GXTexCoord2f32( 1.0f, 0.0f );
////			GXPosition3f32((float)p_data->m_X + (float)p_texture->m_width, (float)p_data->m_Y + (float)p_texture->m_height, -1.0f );
////			GXTexCoord2f32( 1.0f, 1.0f );
////			GXPosition3f32((float)p_data->m_X, (float)p_data->m_Y + (float)p_texture->m_height, -1.0f );
////			GXTexCoord2f32( 0.0f, 1.0f );
////			GXEnd();
//
//			GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
//			GX::SetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//			GX::SetNumTexGens( 0 );
//			GX::SetNumTevStages( 1 );
//			GX::SetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
//			GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR );
//			GX::SetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );
//			GX::SetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_RASA, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO );
//			GX::SetTevColorIn ( GX_TEVSTAGE0, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO );
//
//			// Set current vertex descriptor to enable position and color0.
//			// Both use 8b index to access their data arrays.
//			GXClearVtxDesc();
//			GXSetVtxDesc( GX_VA_POS, GX_DIRECT );
//			GXSetVtxDesc( GX_VA_CLR0, GX_DIRECT );
//
//			// Set material color.
//			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//
//			GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//
//			GX::SetCullMode ( GX_CULL_NONE );
//
//			int cur_width = (gLoadBarWidth - 1);
//			if (gLoadBarNumFrames < gLoadBarTotalFrames)
//			{
//				cur_width = (cur_width * gLoadBarNumFrames) / gLoadBarTotalFrames;
//			}
//
//			int x1 = gLoadBarX;
//			int y1 = gLoadBarY;
//			int x2 = x1 + cur_width;
//			int y2 = y1 + (gLoadBarHeight - 1);
//
//			int end_color[4];
//			if (gLoadBarNumFrames < gLoadBarTotalFrames)
//			{
//				end_color[0] = gLoadBarStartColor[0] + ((gLoadBarDeltaColor[0] * gLoadBarNumFrames) / gLoadBarTotalFrames);
//				end_color[1] = gLoadBarStartColor[1] + ((gLoadBarDeltaColor[1] * gLoadBarNumFrames) / gLoadBarTotalFrames);
//				end_color[2] = gLoadBarStartColor[2] + ((gLoadBarDeltaColor[2] * gLoadBarNumFrames) / gLoadBarTotalFrames);
//				end_color[3] = gLoadBarStartColor[3] + ((gLoadBarDeltaColor[3] * gLoadBarNumFrames) / gLoadBarTotalFrames);
//			} else {
//				end_color[0] = gLoadBarStartColor[0] + gLoadBarDeltaColor[0];
//				end_color[1] = gLoadBarStartColor[1] + gLoadBarDeltaColor[1];
//				end_color[2] = gLoadBarStartColor[2] + gLoadBarDeltaColor[2];
//				end_color[3] = gLoadBarStartColor[3] + gLoadBarDeltaColor[3];
//			}
//
//			int border_x1 = x1 - gLoadBarBorderWidth;
//			int border_y1 = y1 - gLoadBarBorderHeight;
//			int border_x2 = x1 + (gLoadBarWidth - 1) + gLoadBarBorderWidth;
//			int border_y2 = y2 + gLoadBarBorderHeight;
//
//			u32 bc = gLoadBarBorderColor[3]|(gLoadBarBorderColor[2]<<8)|(gLoadBarBorderColor[1]<<16)|(gLoadBarBorderColor[0]<<24);
//			u32 sc = gLoadBarStartColor[3]|(gLoadBarStartColor[2]<<8)|(gLoadBarStartColor[1]<<16)|(gLoadBarStartColor[0]<<24); 
//			u32 ec = end_color[3]|(end_color[2]<<8)|(end_color[1]<<16)|(end_color[0]<<24);
//
//			// Border
//			GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
//				GXPosition3f32(border_x1, border_y1, -1.0f);
//				GXColor1u32( bc );
//				GXPosition3f32(border_x1, border_y2, -1.0f);
//				GXColor1u32( bc );
//				GXPosition3f32(border_x2, border_y2, -1.0f);
//				GXColor1u32( bc );
//				GXPosition3f32(border_x2, border_y1, -1.0f);
//				GXColor1u32( bc );
//			GXEnd();
//			
//			// Bar
//			GXBegin( GX_QUADS, GX_VTXFMT0, 4 ); 
//				GXPosition3f32(x1, y1, -1.0f);
//				GXColor1u32( sc );
//				GXPosition3f32(x1, y2, -1.0f);
//				GXColor1u32( sc );
//				GXPosition3f32(x2, y2, -1.0f);
//				GXColor1u32( ec );
//				GXPosition3f32(x2, y1, -1.0f);
//				GXColor1u32( ec );
//			GXEnd();
//
//			camera2D.end();
//
//			NsPrim::end();
//			NsRender::end();
//			NsDisplay::end( false );
//
////			current_image = ( current_image + 1 ) % p_data->m_NumFrames ;
		}

		// Go to sleep.
		if( busy )
		{
			// Come back shortly.
			OSSetAlarm( &s_alarm, OSMillisecondsToTicks( 500 ), s_thread_loading_icon_alarm_handler );
		}
		else
		{
			// Come back in the proper time.
			OSSetAlarm( &s_alarm, OSMillisecondsToTicks( (long long int)( 1000.0f / 60.0f ) ), s_thread_loading_icon_alarm_handler );
		}

		OSSuspendThread( &s_load_icon_thread );
	}
}


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions

#define USE_SPRITES 0

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_display(const char* filename, bool just_freeze, bool blank)
{
	// See if we need to just flag that a loading screen is active to disable draws.
	if ( just_freeze )
	{
		gLoadingScreenActive = true;
		return;
	}

	// See if we need to clear the screen to black.
	if ( blank )
	{
		for ( int lp = 0; lp < 2; lp++ )
		{
			NsDisplay::begin();
			NsRender::begin();
			NsRender::end();
			NsDisplay::end();
		}

		gLoadingScreenActive = true;
		return;
	}

	NxNgc::EngineGlobals.gpuBusy = true;

	char name[256];
	char pre[256];

	File::PreMgr* pre_mgr = File::PreMgr::Instance();

	// Hmm seems we need to prepend 'images\\' to the start of this string...
	strcpy( name, "images\\" );
	switch( Config::GetLanguage())
	{
		case Config::LANGUAGE_GERMAN:
			strcpy( pre, filename );
			strcat( pre, ".prd" );
			pre_mgr->LoadPre( pre, false );
			strcat( name, "grimages_ngc\\" );
			break;
		case Config::LANGUAGE_FRENCH:
			strcpy( pre, filename );
			strcat( pre, ".prf" );
			pre_mgr->LoadPre( pre, false );
			strcat( name, "frimages_ngc\\" );
			break;
		default:
			break;
	}
	strcat( name, filename );
	
	// ...and append '.img.ngc' to the end.
	strcat( name, ".img.ngc" );
	
	// Load the texture a few lines at a time & render to the display.
	gLoadingLoadScreen = true;
	void *p_FH = File::Open( name, "rb" );

	if( p_FH )
	{
		uint32 version;
		uint32 checksum;
		uint32 width;
		uint32 height;
		uint32 depth;
		uint32 levels;
		uint32 rwidth;
		uint32 rheight;
		uint16 alphamap;
		uint16 hasholes;

		// Load the actual texture

		File::Read( &version,  4, 1, p_FH );
		File::Read( &checksum, 4, 1, p_FH );
		File::Read( &width,    4, 1, p_FH );
		File::Read( &height,   4, 1, p_FH );
		File::Read( &depth,    4, 1, p_FH );
		File::Read( &levels,   4, 1, p_FH );
		File::Read( &rwidth,   4, 1, p_FH );
		File::Read( &rheight,  4, 1, p_FH );
		File::Read( &alphamap, 2, 1, p_FH );
		File::Read( &hasholes, 2, 1, p_FH );

//		Dbg_MsgAssert( version <= TEXTURE_VERSION, ("Illegal texture version number: %d (The newest version we deal with is: %d)\n", version, TEXTURE_VERSION ));
		Dbg_MsgAssert( depth == 32, ("We only deal with 32-bit textures.\n"));

#define LINES 16

		// Load in 8 line chunks.
//		uint16 temp_buffer[width*LINES*2];
//		uint32 tex_buffer[width*LINES];

		uint16 * temp_buffer = (uint16 *)g_p_buffer;
		uint32 * tex_buffer = (uint32 *)&temp_buffer[width*LINES*2];

		NsDisplay::begin();
		NsRender::begin();

		GX::PokeColorUpdate( GX_TRUE );
		GX::PokeAlphaUpdate( GX_FALSE );
		uint yy = 0;
		while ( yy < height )
		{
			int lines = ( height - yy ) > LINES ? LINES : ( height - yy );

			File::Read( temp_buffer, width * 4 * lines, 1, p_FH );

			if ( gReload )
			{
				gReload = false;
				yy = 0;
				File::Close( p_FH );
				void *p_FH = File::Open( name, "rb" );
				File::Read( &version,  4, 1, p_FH );
				File::Read( &checksum, 4, 1, p_FH );
				File::Read( &width,    4, 1, p_FH );
				File::Read( &height,   4, 1, p_FH );
				File::Read( &depth,    4, 1, p_FH );
				File::Read( &levels,   4, 1, p_FH );
				File::Read( &rwidth,   4, 1, p_FH );
				File::Read( &rheight,  4, 1, p_FH );
				File::Read( &alphamap, 2, 1, p_FH );
				File::Read( &hasholes, 2, 1, p_FH );
				NsDisplay::begin();
				NsRender::begin();
				continue;
			}

			// Need to swizzle data we just read.
			for ( int lp = 0; lp < lines; lp += 4 )
			{
				int xx = lp * width;
				for ( uint lp2 = 0; lp2 < width; lp2 += 4 )
				{
					for ( int lp3 = 0; lp3 < 16; lp3++ )
					{
						uint16 p0 = temp_buffer[lp3+(lp2*8)+(xx*2)];
						uint16 p1 = temp_buffer[16+lp3+(lp2*8)+(xx*2)];
						tex_buffer[(lp3&3)+(width*(lp3>>2))+lp2+xx] =	( ( p0 << 16 ) & 0xff000000 ) |
																		( ( p0 << 16 ) & 0x00ff0000 ) | 
																		( ( p1 <<  0 ) & 0x0000ff00 ) | 
																		( ( p1 <<  0 ) & 0x000000ff );
					}
				}
			}

			for ( int yp = 0; yp < lines; yp++ )
			{
				uint y_pos = ( ( height - 1 ) - ( yy + yp ) );
				for ( uint xp = 0; xp < width; xp++ )
				{
					GX::PokeARGB( xp, y_pos, tex_buffer[(width*yp)+xp] );
				}
			}

			yy += lines;
		}
		NsRender::end();
		NsDisplay::end(false);

		File::Close( p_FH );
	}
	gLoadingLoadScreen = false;

	switch( Config::GetLanguage())
	{
		case Config::LANGUAGE_GERMAN:
		case Config::LANGUAGE_FRENCH:
			pre_mgr->UnloadPre( pre );
			break;
		default:
			break;
	}
//	NsDisplay::flush();

	NxNgc::EngineGlobals.gpuBusy = false;

	gLoadingScreenActive = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_hide()
{
	if( s_thread_exists )
	{
		s_terminate_thread_done	= false;
		s_terminate_thread = true;

		// Cancel the alarm and resume the thread.
//		OSCancelAlarm( &s_alarm );
//		OSResumeThread( &s_load_icon_thread );

		while( !s_terminate_thread_done );

		s_thread_exists = false;
//		pIconCallback = NULL;

		//VISetBlack( TRUE );
		//VIFlush();
		//VIWaitForRetrace();
		//NsDisplay::flush();
		//VISetBlack( FALSE );
		//VIFlush();
		//VIWaitForRetrace();
	}

//#if !USE_SPRITES
//	NxPs2::EnableFlipCopy(true);
//#endif // USE_SPRITES
	gLoadingBarActive = false;
	gLoadingScreenActive = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_clear()
{
//	if (sp_load_screen_sprite)
//	{
//		CEngine::sDestroySprite(sp_load_screen_sprite);
//		sp_load_screen_sprite = NULL;
//	}
//
//	if (sp_load_screen_texture)
//	{
//		delete sp_load_screen_texture;
//		sp_load_screen_texture = NULL;
//	}
	gLoadingBarActive = false;
	gLoadingScreenActive = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_start_loading_bar(float seconds)
{
//	NsDisplay::flush();

	gLoadBarTotalFrames = (int) (seconds * 1.15f * Config::FPS());
	gLoadBarNumFrames = 0;

//
////	NxPs2::StartLoadingBar((int) (seconds * Config::FPS()));
//
//	// Start the thread to display the animated loading icon.
////	pIconCallback = s_non_threaded_loading_icon;
//	BOOL rv = true;
//
//	s_terminate_thread = false;
//
//	memset( &s_load_icon_thread, 0, sizeof( OSThread ));
//	rv = OSCreateThread(	&s_load_icon_thread,
//							s_threaded_loading_icon,										// Entry function.
//							NULL,															// Argument for start function.
//							s_load_icon_thread_stack + sizeof( s_load_icon_thread_stack ),	// Stack base (stack grows down).
//							sizeof( s_load_icon_thread_stack ),								// Stack size in bytes.
//							1,																// Thread priority.
//							OS_THREAD_ATTR_DETACH  );										// Thread attributes.
//	Dbg_MsgAssert( rv, ( "Failed to create thread" ));
//
//	if( rv )
//	{
//		s_thread_exists = true;
////		OSResumeThread( &s_load_icon_thread );
//		OSSetAlarm( &s_alarm, OSMillisecondsToTicks( (long long int)( 1000.0f / 60.0f ) ), s_thread_loading_icon_alarm_handler );
//	}
	gLoadingBarActive = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_update_bar_properties()
{
	// Bar size and position
	gLoadBarX = s_bar_x;
	gLoadBarY = s_bar_y;
	gLoadBarWidth = s_bar_width;
	gLoadBarHeight = s_bar_height;

	// Bar colors
	gLoadBarStartColor[0] = s_bar_start_color.r;
	gLoadBarStartColor[1] = s_bar_start_color.g;
	gLoadBarStartColor[2] = s_bar_start_color.b;
	gLoadBarStartColor[3] = s_bar_start_color.a;
	gLoadBarDeltaColor[0] = (int) s_bar_end_color.r - gLoadBarStartColor[0];
	gLoadBarDeltaColor[1] = (int) s_bar_end_color.g - gLoadBarStartColor[1];
	gLoadBarDeltaColor[2] = (int) s_bar_end_color.b - gLoadBarStartColor[2];
	gLoadBarDeltaColor[3] = (int) s_bar_end_color.a - gLoadBarStartColor[3];

	// Border size
	gLoadBarBorderWidth = s_bar_border_width;
	gLoadBarBorderHeight = s_bar_border_height;

	// Border color
	gLoadBarBorderColor[0] = s_bar_border_color.r;
	gLoadBarBorderColor[1] = s_bar_border_color.g;
	gLoadBarBorderColor[2] = s_bar_border_color.b;
	gLoadBarBorderColor[3] = s_bar_border_color.a;
}

} 
 





================================================
FILE: Code/Gfx/NGC/p_nxmiscfx.cpp
================================================
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 

#include 
#include 
#include "dolphin/base/ppcwgpipe.h"
#include "dolphin/gx/gxvert.h"

namespace Nx
{

#define	DRAW_DEBUG_LINES		0

#	if DRAW_DEBUG_LINES
static float debugpos[10240*6];
static GXColor debugcol[10240];
static int nLines = 0;
#	endif		// DRAW_DEBUG_LINES

#define ADD_LINE(_s,_e,_r,_g,_b) {	debugpos[(nLines*6)+0] = _s[X];	\
									debugpos[(nLines*6)+1] = _s[Y];    \
									debugpos[(nLines*6)+2] = _s[Z];    \
									debugpos[(nLines*6)+3] = _e[X];    \
									debugpos[(nLines*6)+4] = _e[Y];    \
									debugpos[(nLines*6)+5] = _e[Z];    \
									debugcol[nLines] = (GXColor){_r,_g,_b,255};			\
									nLines++; }							\

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sNgcScreenFlashVert
{
	float		x, y, z;
	GXColor		col;
	float		u, v;
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sNgcVert
{
	Mth::Vector		pos;
	GXColor			col;
	s16				u, v;
};


	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#if 0
struct sNgcSplatInstanceDetails : public sSplatInstanceDetails
{
	// Platform specific part.
	NxNgc::CInstance	*mp_instance;
//	NxNgc::sMaterialHeader		m_mat;
//	NxNgc::sMaterialPassHeader	m_pass;
	NxNgc::sTexture *	mp_texture;
	sNgcVert			m_verts[SPLAT_POLYS_PER_MESH * 3];
};
#endif		// 0


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sNgcShatterInstanceDetails : public sShatterInstanceDetails
{
	// Platform specific part.

						sNgcShatterInstanceDetails( int num_tris, NxNgc::sMesh *p_mesh, NxNgc::sScene *p_scene, GXVtxFmt format );
						~sNgcShatterInstanceDetails( void );
	
	NxNgc::sMesh		*mp_mesh;
	NxNgc::sScene		*mp_scene;
	sNgcVert			*mp_vertex_buffer;
	GXVtxFmt			m_vertex_format;
};


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sNgcShatterInstanceDetails::sNgcShatterInstanceDetails( int num_tris, NxNgc::sMesh *p_mesh, NxNgc::sScene *p_scene, GXVtxFmt format ) : sShatterInstanceDetails( num_tris )
{
	mp_mesh				= p_mesh;
	mp_scene			= p_scene;
	mp_vertex_buffer	= new sNgcVert[num_tris * 3];
	m_vertex_format		= format;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sNgcShatterInstanceDetails::~sNgcShatterInstanceDetails( void )
{
	delete [] mp_vertex_buffer;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#if 0
sNgcSplatInstanceDetails * getDetailsForTextureSplat( NxNgc::sTexture *p_texture )
{

	sNgcSplatInstanceDetails *p_Ngc_details;

	Dbg_Assert( p_splat_details_table );
	
	// Check to see whether we have a scene already created for this type of texture splat.
	p_splat_details_table->IterateStart();
	sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
	while( p_details )
	{
		p_Ngc_details					= static_cast( p_details );
//		if( p_Ngc_details->m_pass.m_texture.p_data == p_texture )
		if( p_Ngc_details->mp_texture == p_texture )
		{
			// This scene contains a material with the required texture, so use this scene.
			return p_Ngc_details;
		}
		p_details = p_splat_details_table->IterateNext();
	}
	
	// Create an (opaque) material used to render the mesh.
	p_Ngc_details = new sNgcSplatInstanceDetails;

	p_Ngc_details->mp_texture				= p_texture;

	p_Ngc_details->m_highest_active_splat	= 0;
	memset( p_Ngc_details->m_lifetimes, 0, sizeof( int ) * SPLAT_POLYS_PER_MESH );

	for( int v = 0; v < SPLAT_POLYS_PER_MESH * 3; ++v )
	{
		p_Ngc_details->m_verts[v].pos = Mth::Vector(0.0f, 0.0f, 0.0f, 1.0f);
		p_Ngc_details->m_verts[v].col = (GXColor){ 0x80, 0x80, 0x80, 0xff };
	}
	
	p_splat_details_table->PutItem((uint32)p_Ngc_details, p_Ngc_details );

	return p_Ngc_details;
}
#endif		// 0



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool subdivide_tri_stack( sNgcVert **p_write, NxNgc::sMesh *p_mesh )
{
	// If there are elements on the stack, pop off the top three vertices and subdivide if necessary.
	if( triSubdivideStack.IsEmpty())
	{
		return false;
	}

	// Three temporary buffers.
	sNgcVert v0;
	sNgcVert v1;
	sNgcVert v2;
	
	sNgcVert	*p_v0 = &v0;
	sNgcVert	*p_v1 = &v1;
	sNgcVert	*p_v2 = &v2;
	
	// Stack is LIFO, so Pop() off in reverse order.
	triSubdivideStack.Pop( p_v2 );
	triSubdivideStack.Pop( p_v1 );
	triSubdivideStack.Pop( p_v0 );
	
	// Calculate the area of this tri.
	Mth::Vector p(	p_v1->pos[X] - p_v0->pos[X], p_v1->pos[Y] - p_v0->pos[Y], p_v1->pos[Z] - p_v0->pos[Z] );
	Mth::Vector q(	p_v2->pos[X] - p_v0->pos[X], p_v2->pos[Y] - p_v0->pos[Y], p_v2->pos[Z] - p_v0->pos[Z] );
	Mth::Vector r(( p[Y] * q[Z] ) - ( q[Y] * p[Z] ), ( p[Z] * q[X] ) - ( q[Z] * p[X] ), ( p[X] * q[Y] ) - ( q[X] * p[Y] ));
	float area_squared = r.LengthSqr();

	if( area_squared > shatterAreaTest )
	{
		// Three temporary buffers.
		sNgcVert i01 = v0;
		sNgcVert i12 = v0;
		sNgcVert i20 = v0;

		// Deal with positions (always present).
		i01.pos[X] = p_v0->pos[X] + (( p_v1->pos[X] - p_v0->pos[X] ) * 0.5f );
		i01.pos[Y] = p_v0->pos[Y] + (( p_v1->pos[Y] - p_v0->pos[Y] ) * 0.5f );
		i01.pos[Z] = p_v0->pos[Z] + (( p_v1->pos[Z] - p_v0->pos[Z] ) * 0.5f );
		      
		i12.pos[X] = p_v1->pos[X] + (( p_v2->pos[X] - p_v1->pos[X] ) * 0.5f );
		i12.pos[Y] = p_v1->pos[Y] + (( p_v2->pos[Y] - p_v1->pos[Y] ) * 0.5f );
		i12.pos[Z] = p_v1->pos[Z] + (( p_v2->pos[Z] - p_v1->pos[Z] ) * 0.5f );
		      
		i20.pos[X] = p_v2->pos[X] + (( p_v0->pos[X] - p_v2->pos[X] ) * 0.5f );
		i20.pos[Y] = p_v2->pos[Y] + (( p_v0->pos[Y] - p_v2->pos[Y] ) * 0.5f );
		i20.pos[Z] = p_v2->pos[Z] + (( p_v0->pos[Z] - p_v2->pos[Z] ) * 0.5f );

		// Deal with colors (not always present).
//		if ( p_mesh->mp_colBuffer )
		{
			i01.col.r		= v0.col.r + (u8)(( v1.col.r - v0.col.r ) >> 1 );
			i12.col.r		= v1.col.r + (u8)(( v2.col.r - v1.col.r ) >> 1 );
			i20.col.r		= v2.col.r + (u8)(( v0.col.r - v2.col.r ) >> 1 );
									   						
			i01.col.g		= v0.col.g + (u8)(( v1.col.g - v0.col.g ) >> 1 );
			i12.col.g		= v1.col.g + (u8)(( v2.col.g - v1.col.g ) >> 1 );
			i20.col.g		= v2.col.g + (u8)(( v0.col.g - v2.col.g ) >> 1 );
									   						
			i01.col.b		= v0.col.b + (u8)(( v1.col.b - v0.col.b ) >> 1 );
			i12.col.b		= v1.col.b + (u8)(( v2.col.b - v1.col.b ) >> 1 );
			i20.col.b		= v2.col.b + (u8)(( v0.col.b - v2.col.b ) >> 1 );
									   						
			i01.col.a		= v0.col.a + (u8)(( v1.col.a - v0.col.a ) >> 1 );
			i12.col.a		= v1.col.a + (u8)(( v2.col.a - v1.col.a ) >> 1 );
			i20.col.a		= v2.col.a + (u8)(( v0.col.a - v2.col.a ) >> 1 );
		}

		// Deal with uv0 (not always present).
//		if ( p_mesh->mp_uvBuffer )
		{
			i01.u		= v0.u + (( v1.u - v0.u ) >> 1 );
			i01.v		= v0.v + (( v1.v - v0.v ) >> 1 );
			i12.u		= v1.u + (( v2.u - v1.u ) >> 1 );
			i12.v		= v1.v + (( v2.v - v1.v ) >> 1 );
			i20.u		= v2.u + (( v0.u - v2.u ) >> 1 );
			i20.v		= v2.v + (( v0.v - v2.v ) >> 1 );
		}
		
		// Push the four new tris onto the stack.
		triSubdivideStack.Push( &v0 );
		triSubdivideStack.Push( &i01 );
		triSubdivideStack.Push( &i20 );

		triSubdivideStack.Push( &i01 );
		triSubdivideStack.Push( &v1 );
		triSubdivideStack.Push( &i12 );

		triSubdivideStack.Push( &i01 );
		triSubdivideStack.Push( &i12 );
		triSubdivideStack.Push( &i20 );

		triSubdivideStack.Push( &i20 );
		triSubdivideStack.Push( &i12 );
		triSubdivideStack.Push( &v2 );
	}
	else
	{
		// Just copy the tri into the next available slot.
		**p_write = v0;
		(*p_write)++;
		**p_write = v1;
		(*p_write)++;
		**p_write = v2;
		(*p_write)++;
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static bool same_side( Mth::Vector &p1, Mth::Vector &p2, Mth::Vector &a, Mth::Vector &b )
{
	Mth::Vector cp1 = Mth::CrossProduct( b - a, p1 - a );
	Mth::Vector cp2 = Mth::CrossProduct( b - a, p2 - a );
	if( Mth::DotProduct( cp1, cp2 ) >= 0.0f )
		return true;
    else
		return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static bool point_in_triangle( Mth::Vector &p, Mth::Vector &a, Mth::Vector &b, Mth::Vector &c )
{
	if( same_side( p, a, b, c ) && same_side( p, b, a, c ) && same_side( p, c, a, b ))
		return true;
    else
		return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline bool line_segment_intersection( float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4 )
{
	float den = (( y4 - y3 ) * ( x2 - x1 )) - (( x4 - x3 ) * ( y2 - y1 ));

	if( den == 0.0f )
	{
		// Parallel lines.
		return false;
	}
	
	float num_a = (( x4 - x3 ) * ( y1 - y3 )) - (( y4 - y3 ) * ( x1 - x3 ));
	float num_b = (( x2 - x1 ) * ( y1 - y3 )) - (( y2 - y1 ) * ( x1 - x3 ));

	num_a /= den;
	num_b /= den;

	if(( num_a <= 1.0f ) && ( num_b <= 1.0f ))
		return true;

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline bool tri_texture_intersect( float u0, float v0, float u1, float v1, float u2, float v2 )
{
	// Trivial check to see if all three points are outside range of texture.
	if(( u0 < -1.0f ) && ( u1 < -1.0f ) && ( u2 < -1.0f ))
		return false;
	if(( u0 > 1.0f ) && ( u1 > 1.0f ) && ( u2 > 1.0f ))
		return false;
	if(( v0 < -1.0f ) && ( v1 < -1.0f ) && ( v2 < -1.0f ))
		return false;
	if(( v0 > 1.0f ) && ( v1 > 1.0f ) && ( v2 > 1.0f ))
		return false;
	
	// Check that at least one corner of the texture falls within the tri.
	Mth::Vector texture_square[4] = {	Mth::Vector( -1.0f, -1.0f, 0.0f, 0.0f ),
										Mth::Vector(  1.0f, -1.0f, 0.0f, 0.0f ),
										Mth::Vector(  1.0f,  1.0f, 0.0f, 0.0f ),
										Mth::Vector( -1.0f,  1.0f, 0.0f, 0.0f )};

	Mth::Vector a( u0, v0, 0.0f );
	Mth::Vector b( u1, v1, 0.0f );
	Mth::Vector c( u2, v2, 0.0f );

	for( int p = 0; p < 4; ++p )
	{
		if( point_in_triangle( texture_square[p], a, b, c ))
		{
			return true;
		}
	}

	// No corners of the texture fall within the tri. There are 3 possible explanations:
	// 1) The tri intersects the texture, but does not contain a texture corner.
	// 2) The texture lies entirely outside of the tri.
	// 3) The tri falls entirely within the texture.
	// Given the relatively small size of the textures, case (3) is extremely unlikely.

	// Perform a trivial check to see whether a corner of the tri lies within the texture. This will catch (3) and sometimes (1).
	if(( u0 >= -1.0f ) && ( u0 <= 1.0f ) && ( v0 >= -1.0f ) && ( v0 <= 1.0f ))
		return true;
	if(( u1 >= -1.0f ) && ( u1 <= 1.0f ) && ( v1 >= -1.0f ) && ( v1 <= 1.0f ))
		return true;
	if(( u2 >= -1.0f ) && ( u2 <= 1.0f ) && ( v2 >= -1.0f ) && ( v2 <= 1.0f ))
		return true;

	// Perform the complete check to see if any line segment forming the tri intersects any line segment forming the texture.
	for( int p = 0; p < 4; ++p )
	{
		int q = ( p + 1 ) % 4;
		if( line_segment_intersection( u0, v0, u1, v1, texture_square[p][X], texture_square[p][Y], texture_square[q][X], texture_square[q][Y] ))
			return true;
	}
	
	return false;
}

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_screen_flash_render( sScreenFlashDetails *p_details )
{
	// Create an (opaque) material used to render the mesh.
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= NULL;
	pass.m_flags			= (1<<5) | (1<<6);		// clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)NxNgc::vBLEND_MODE_BLEND;
	pass.m_alpha_fix		= (unsigned char)0; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	// Get viewport details.
	CViewport *p_vp = CViewportManager::sGetActiveViewport( p_details->m_viewport );

	sNgcScreenFlashVert verts[4];

	verts[0].x	= p_vp->GetOriginX() * 640;
	verts[0].y	= p_vp->GetOriginY() * 448;
	verts[0].z	= p_details->m_z;
	
	verts[1].x	= verts[0].x + ( p_vp->GetWidth() * 640 );
	verts[1].y	= verts[0].y;
	verts[1].z	= verts[0].z;

	verts[2].x	= verts[0].x + ( p_vp->GetWidth() * 640 );
	verts[2].y	= verts[0].y + ( p_vp->GetHeight() * 448 );
	verts[2].z	= verts[0].z;

	verts[3].x	= verts[0].x;
	verts[3].y	= verts[0].y + ( p_vp->GetHeight() * 448 );
	verts[3].z	= verts[0].z;

	for( int v = 0; v < 4; ++v )
	{
		verts[v].col = (GXColor){ p_details->m_current.r, p_details->m_current.g, p_details->m_current.b, p_details->m_current.a };
	}

	if( p_details->mp_texture )
	{
		verts[0].u	= 0.0f;
		verts[0].v	= 0.0f;
		verts[1].u	= 1.0f;
		verts[1].v	= 0.0f;
		verts[2].u	= 1.0f;
		verts[2].v	= 1.0f;
		verts[3].u	= 0.0f;
		verts[3].v	= 1.0f;

		Nx::CNgcTexture *p_Ngc_texture = static_cast( p_details->mp_texture );
		pass.m_texture.p_data = p_Ngc_texture->GetEngineTexture();
		pass.m_flags |= (1<<0);
	}
	GX::SetZMode ( GX_FALSE, GX_ALWAYS, GX_FALSE );

	NxNgc::multi_mesh( &mat, &pass, true, false );
	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Render the triangles
	if( p_details->mp_texture )
	{
		GX::SetVtxDesc( 3, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
	}
	else
	{
		GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );
	}
	GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
	for (int i = 0; i < 4; i++ )
	{
		GX::Position3f32( verts[i].x, verts[i].y, verts[i].z );
		GX::Color1u32(*((u32*)&(verts[i].col)));
		if( p_details->mp_texture ) GX::TexCoord2f32( verts[i].u, verts[i].v );
	}
	GX::End();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_initialize( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_cleanup( void )
{
#if 0
	sNgcSplatInstanceDetails *p_Ngc_details;

	Dbg_Assert( p_splat_details_table );
	
	p_splat_details_table->IterateStart();
	sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
	while( p_details )
	{
		p_Ngc_details = static_cast( p_details );

		//delete p_Ngc_details->mp_material;
		
		p_details = p_splat_details_table->IterateNext();

		p_splat_details_table->FlushItem((uint32)p_Ngc_details );
		delete p_Ngc_details;
	}
#endif		// 0
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_reset_poly( sSplatInstanceDetails *p_details, int index )
{
#if 0
	// Cast the details to Ngc details.
	sNgcSplatInstanceDetails *p_Ngc_details = static_cast( p_details );
	
	// Force this poly to be degenerate.
	p_Ngc_details->m_verts[index * 3 + 1]	= p_Ngc_details->m_verts[index * 3];
	p_Ngc_details->m_verts[index * 3 + 2]	= p_Ngc_details->m_verts[index * 3];
#endif		// 0
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool plat_texture_splat( Nx::CSector **pp_sectors, Nx::CCollStatic **pp_collision, Mth::Vector& start, Mth::Vector& end, float size, float lifetime, Nx::CTexture *p_texture, Nx::sSplatTrailInstanceDetails *p_trail_details )
{
#if 0
	Mth::Matrix view_matrix, ortho_matrix, projection_matrix;

#	if DRAW_DEBUG_LINES
//	Gfx::AddDebugLine( start, end, MAKE_RGB( 200, 200, 0 ), MAKE_RGB( 200, 200, 0 ), 1 );
	ADD_LINE( start, end, 200, 200, 0 );
#	endif // DRAW_DEBUG_LINES
	
	// The length of the start->end line defines the view depth of the frustum.
	Mth::Vector	splat_vector	= end - start;
	float		view_depth		= splat_vector.Length();
	splat_vector.Normalize();
	
	// Calculate the parallel projection matrix. Generally the projection vector will tend to point downwards, so we use a
	// random vector in the x-z plane to define the up vector for the projection. However if this splat is part of a trail,
	// use the previous point to generate the up vector.
	if( p_trail_details )
	{
		Mth::Vector up( start[X] - p_trail_details->m_last_pos[X], start[Y] - p_trail_details->m_last_pos[Y], start[Z] - p_trail_details->m_last_pos[Z], 0.0f );

		// The height of the viewport is defined by the distance between the two points.
		float height = up.Length() * 0.5f;

		start	-= up * 0.5f;
		end		-= up * 0.5f;

		up.Normalize();

		Mth::CreateMatrixLookAt( view_matrix, start, end, up );
		Mth::CreateMatrixOrtho( ortho_matrix, size, height, 0.1f, view_depth );
	}
	else if( fabsf( splat_vector[Y] ) > 0.5f )
	{
		float angle = ((float)rand() * 2.0f * Mth::PI ) / (float)RAND_MAX;
		Mth::CreateMatrixLookAt( view_matrix, start, end, Mth::Vector( sinf( angle ), 0.0f, cosf( angle ), 0.0f ));
		Mth::CreateMatrixOrtho( ortho_matrix , size, size, 0.1f, view_depth );
	}
	else
	{
		Mth::CreateMatrixLookAt( view_matrix, start, end, Mth::Vector( 0.0f, 1.0f, 0.0f, 0.0f ));
		Mth::CreateMatrixOrtho( ortho_matrix , size, size, 0.1f, view_depth );
	}

	projection_matrix = view_matrix * ortho_matrix;

	// Pointer to the mesh we will be modifying. (Don't want to set the pointer up until we know for
	// sure that we will be adding some polys).
	sNgcSplatInstanceDetails	*p_details		= NULL;
	sNgcVert					*p_target_verts	= NULL;

	Nx::CSector *p_sector;
	while( (p_sector = *pp_sectors) )
	{
		Nx::CNgcGeom *p_Ngc_geom = static_cast( p_sector->GetGeom());

		if( p_Ngc_geom )
		{
#			if DRAW_DEBUG_LINES
			Mth::Vector min = p_Ngc_geom->GetBoundingBox().GetMin();
			Mth::Vector max = p_Ngc_geom->GetBoundingBox().GetMax();

			Mth::Vector box[8];
			box[0] = box[1] = box[2] = box[3] = max;
			box[1][X] = min[X];
			box[2][Y] = min[Y];
			box[3][Z] = min[Z];
			box[5] = box[6] = box[7] = box[4] = min;;
			box[5][X] = max[X];
			box[6][Y] = max[Y];
			box[7][Z] = max[Z];

			for ( int i = 1; i < 4; i++ )
			{
//				Gfx::AddDebugLine( box[0], box[i], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
				ADD_LINE( box[0], box[i], 200, 0, 0 );
			}
			for ( int i = 5; i < 8; i++ )
			{
//				Gfx::AddDebugLine( box[4], box[i], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
				ADD_LINE( box[4], box[i], 200, 0, 0 );
			}
//			Gfx::AddDebugLine( box[1], box[6], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
//			Gfx::AddDebugLine( box[1], box[7], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
//			Gfx::AddDebugLine( box[2], box[5], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
//			Gfx::AddDebugLine( box[2], box[7], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
//			Gfx::AddDebugLine( box[3], box[5], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
//			Gfx::AddDebugLine( box[3], box[6], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			ADD_LINE( box[1], box[6], 200, 0, 0 );
			ADD_LINE( box[1], box[7], 200, 0, 0 );
			ADD_LINE( box[2], box[5], 200, 0, 0 );
			ADD_LINE( box[2], box[7], 200, 0, 0 );
			ADD_LINE( box[3], box[5], 200, 0, 0 );
			ADD_LINE( box[3], box[6], 200, 0, 0 );
#			endif // DRAW_DEBUG_LINES
			
			// For each mesh in the geom...
			for( uint32 m = 0; m < p_Ngc_geom->m_num_mesh; ++m )
			{
				NxNgc::sMesh *p_mesh = p_Ngc_geom->m_mesh_array[m];
				
				NxNgc::sScene *p_scene = p_Ngc_geom->mp_scene->GetEngineScene();

				// Not allowed on meshes which are flagged not to shadow.
				if( p_mesh->m_flags & NxNgc::sMesh::MESH_FLAG_NO_SKATER_SHADOW )
					continue;
				
				// Count indices.
				int num_indices = 0;
				unsigned char * p_start = (unsigned char *)&p_mesh->mp_dl[1];
//				uint32 cp = (p_start[2]<<24)|(p_start[3]<<16)|(p_start[4]<<8)|p_start[5];
				unsigned char * p_end = &p_start[p_mesh->mp_dl->m_size];
				p_start = &p_start[p_mesh->mp_dl->m_index_offset];		// Skip to actual 1st GDBegin.
				unsigned char * p8 = p_start;

				int stride = p_mesh->mp_dl->m_index_stride;

				unsigned char * p_index_buffer  = &p8[1];

				while ( p8 < p_end )
				{
					if ( ( p8[0] & 0xf8 ) == GX_TRIANGLESTRIP )
					{
						// Found a triangle strip - parse it.
						int num_verts = ( p8[1] << 8 ) | p8[2];
						p8 += 3;		// Skip GDBegin

						num_indices += num_verts;

						p8 += stride * 2 * num_verts;
					}
					else
					{
						break;
					}
				}

				if ( p_index_buffer )
				{
					// Create the index buffer.
					uint polys = 0;
					uint16 idxbuf[2048*3];		// 12k.
					uint16 * p_dest = idxbuf;
					uint8 * p_source = (uint8*)p_index_buffer;
					uint16 total = 0;
					while ( total < num_indices )
					{
						uint16 num = ( p_source[0] << 8 ) | p_source[1];
						p_source += 2;
						uint16 i0;
						uint16 i1 = ( p_source[0] << 8 ) | p_source[1];
						p_source += stride * 2;
						uint16 i2 = ( p_source[0] << 8 ) | p_source[1];
						p_source += stride * 2;
						for ( int vv = 2; vv < num; vv++ )
						{
							i0 = i1;
							i1 = i2;
							i2 = ( p_source[0] << 8 ) | p_source[1];
							p_source += stride * 2;
							*p_dest++ = i0;
							*p_dest++ = i1;
							*p_dest++ = i2;
							polys++;
						}
						p_source += 1;
						total += num + 1;		// Count each index + the count.
					}

					// Now scan through each non-degenerate tri, checking the verts to see if they are within scope.
					for( uint32 i = 0; i < polys; ++i )
					{
						// Wrap the indices round.
						uint16 index0 = idxbuf[(i*3)+0];
						uint16 index1 = idxbuf[(i*3)+1];
						uint16 index2 = idxbuf[(i*3)+2];

						if(( index0 != index1 ) && ( index0 != index2 ) && ( index1 != index2 ))
						{
							Mth::Vector v0( p_scene->mp_pos_pool[(index0*3)+0],
											p_scene->mp_pos_pool[(index0*3)+1],
											p_scene->mp_pos_pool[(index0*3)+2] );
							Mth::Vector v1( p_scene->mp_pos_pool[(index1*3)+0],
											p_scene->mp_pos_pool[(index1*3)+1],
											p_scene->mp_pos_pool[(index1*3)+2] );
							Mth::Vector v2( p_scene->mp_pos_pool[(index2*3)+0],
											p_scene->mp_pos_pool[(index2*3)+1],
											p_scene->mp_pos_pool[(index2*3)+2] );

							v0[W] = v1[W] = v2[W] = 1.0f;		// Make sure they are points, not vectors

	//						OSReport( "Looking at: (%6.3f, %6.3f, %6.3f) (%6.3f, %6.3f, %6.3f) (%6.3f, %6.3f, %6.3f)\n", v0[X], v0[Y], v0[Z], v1[X], v1[Y], v1[Z], v2[X], v2[Y], v2[Z] );

							Mth::Vector uvprojections[3];
							uvprojections[0] = v0 * projection_matrix;
							uvprojections[1] = v1 * projection_matrix;
							uvprojections[2] = v2 * projection_matrix;

							// Check that they are in the frustum
//							if(( uvprojections[0][X] < -1.0f ) && ( uvprojections[1][X] < -1.0f ) && ( uvprojections[2][X] < -1.0f ))
//								continue;
//							if(( uvprojections[0][Y] < -1.0f ) && ( uvprojections[1][Y] < -1.0f ) && ( uvprojections[2][Y] < -1.0f ))
//								continue;
//							if(( uvprojections[0][X] > 1.0f ) && ( uvprojections[1][X] > 1.0f ) && ( uvprojections[2][X] > 1.0f ))
//								continue;
//							if(( uvprojections[0][Y] > 1.0f ) && ( uvprojections[1][Y] > 1.0f ) && ( uvprojections[2][Y] > 1.0f ))
//								continue;
							if(( uvprojections[0][Z] < -1.0f ) && ( uvprojections[1][Z] < -1.0f ) && ( uvprojections[2][Z] < -1.0f ))
								continue;
							if(( uvprojections[0][Z] > 1.0f ) && ( uvprojections[1][Z] > 1.0f ) && ( uvprojections[2][Z] > 1.0f ))
								continue;

							// Okay, this tri lies within the projection frustum. Now check that it intersects the texture
							if( !tri_texture_intersect( uvprojections[0][X], uvprojections[0][Y],
														uvprojections[1][X], uvprojections[1][Y],
														uvprojections[2][X], uvprojections[2][Y] ))
							{
								continue;
							}

//#ifdef SHORT_VERT
							Mth::Vector	s = v1 - v0;
							Mth::Vector	t = v2 - v0;
							Mth::Vector normal = Mth::CrossProduct( s, t );
							normal.Normalize();

							v0 -= normal;
							v1 -= normal;
							v2 -= normal;
//#endif		// SHORT_VERT 

	//						OSReport( "Added: (%5.1f, %5.1f, %5.1f) (%5.1f, %5.1f, %5.1f) (%5.1f, %5.1f, %5.1f)\n", v0[X], v0[Y], v0[Z], v1[X], v1[Y], v1[Z], v2[X], v2[Y], v2[Z] );

							// Okay, this tri lies within the projection frustum. Get a pointer to the mesh used for rendering texture splats
							// with the given texture. (Note this will create a new instance to handle texture splats of this texture if one
							// does not already exist).
							if( p_target_verts == NULL )
							{
								CNgcTexture *p_Ngc_texture	= static_cast( p_texture );
								p_details						= getDetailsForTextureSplat( p_Ngc_texture->GetEngineTexture());
								p_target_verts					= p_details->m_verts;
								Dbg_Assert( p_target_verts );
							}

							// If we have trails, scale up the mapping by one pixel in all directions.
							// (effectively 2 pixels in u and 2 pixels in v ).
							float up_one_u_pixel;
							float up_one_v_pixel;
							if( p_trail_details )
							{
								up_one_u_pixel = 1.0f - ( 1.0f / ( p_texture->GetWidth() / 2.0f ) );
								up_one_v_pixel = 1.0f - ( 1.0f / ( p_texture->GetHeight() / 2.0f ) );
							}
							else
							{
								up_one_u_pixel = 1.0f;
								up_one_v_pixel = 1.0f;
							}

							// Scan through the lifetimes, finding a 'dead' poly (lifetime == 0), or the oldest.
							uint32 idx						= p_details->GetOldestSplat();

							// Convert lifetime from seconds to milliseconds.
							p_details->m_lifetimes[idx]		= (int)( lifetime * 1000.0f );

							// Set up the corresponding vertices. First write the positions.
							uint32 index					= idx * 3;
							p_target_verts[index + 0].pos	= v0;
							p_target_verts[index + 1].pos	= v1;
							p_target_verts[index + 2].pos	= v2;

							// Then the uv's.
							p_target_verts[index + 0].u		= (s16)( ( ( ( uvprojections[0][X] * 0.5f ) + 0.5f ) * up_one_u_pixel ) * 256.0f );
							p_target_verts[index + 0].v		= (s16)( ( ( ( uvprojections[0][Y] * 0.5f ) + 0.5f ) * up_one_v_pixel ) * 256.0f );
							p_target_verts[index + 1].u		= (s16)( ( ( ( uvprojections[1][X] * 0.5f ) + 0.5f ) * up_one_u_pixel ) * 256.0f );
							p_target_verts[index + 1].v		= (s16)( ( ( ( uvprojections[1][Y] * 0.5f ) + 0.5f ) * up_one_v_pixel ) * 256.0f );
							p_target_verts[index + 2].u		= (s16)( ( ( ( uvprojections[2][X] * 0.5f ) + 0.5f ) * up_one_u_pixel ) * 256.0f );
							p_target_verts[index + 2].v		= (s16)( ( ( ( uvprojections[2][Y] * 0.5f ) + 0.5f ) * up_one_v_pixel ) * 256.0f );

							// Now the colors
							p_target_verts[index + 0].col = (GXColor){128,128,128,255};		// p_coll_geom->GetVertexRGBA(p_coll_geom->GetFaceVertIndex(*p_face_indexes, 0));
							p_target_verts[index + 1].col = (GXColor){128,128,128,255};     // p_coll_geom->GetVertexRGBA(p_coll_geom->GetFaceVertIndex(*p_face_indexes, 1));
							p_target_verts[index + 2].col = (GXColor){128,128,128,255};     // p_coll_geom->GetVertexRGBA(p_coll_geom->GetFaceVertIndex(*p_face_indexes, 2));
//.col

							Mth::Vector	*p_v0 = &( p_target_verts[index + 0].pos );
							Mth::Vector	*p_v1 = &( p_target_verts[index + 1].pos );
							Mth::Vector	*p_v2 = &( p_target_verts[index + 2].pos );
							Mth::Vector pv(	p_v1->GetX() - p_v0->GetX(), p_v1->GetY() - p_v0->GetY(), p_v1->GetZ() - p_v0->GetZ() );
							Mth::Vector qv(	p_v2->GetX() - p_v0->GetX(), p_v2->GetY() - p_v0->GetY(), p_v2->GetZ() - p_v0->GetZ() );
							Mth::Vector r(( pv[Y] * qv[Z] ) - ( qv[Y] * pv[Z] ), ( pv[Z] * qv[X] ) - ( qv[Z] * pv[X] ), ( pv[X] * qv[Y] ) - ( qv[X] * pv[Y] ));
							float area_squared = r.LengthSqr();
				
							// Set the shatter test to ensure that we don't subdivide too far. Note that each successive subdivision will quarter
							// the area of each triangle, which means the area *squared* of each triangle will become 1/16th of the previous value.
							shatterAreaTest = area_squared / 128.0f;
				
							triSubdivideStack.Reset();
							triSubdivideStack.SetBlockSize( sizeof(sNgcVert) );
							triSubdivideStack.Push( &p_target_verts[index + 0] );
							triSubdivideStack.Push( &p_target_verts[index + 1] );
							triSubdivideStack.Push( &p_target_verts[index + 2] );
				
							// Allocate a block of memory into which the subdivision stack will write the results.
							Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
							sNgcVert		*p_array		= new sNgcVert[1024];
							sNgcVert		*p_array_start	= p_array;
							sNgcVert		*p_array_loop	= p_array;
							//memset( p_array, 0, sizeof(sNgcVert) * 1024 );
							Mem::Manager::sHandle().PopContext();
				
							while( subdivide_tri_stack( &p_array, NULL ));
				
							// Ensure we haven't overrun the buffer.
							Dbg_Assert((uint32)p_array - (uint32)p_array_loop < ( sizeof(sNgcVert) * 1024 ));
				
							float oo_up_one_u_pixel = 1.0f / up_one_u_pixel;
							float oo_up_one_v_pixel = 1.0f / up_one_v_pixel;
				
				//			int vert_idx = 0;
							bool no_polys = true;
				
							while( p_array_loop != p_array )
							{
								Dbg_Assert(((uint32) p_array_loop) < ((uint32) p_array));
				//				Dbg_Message("Looking at vert %d", vert_idx);
								// Add this triangle, *if* it is valid.
								if( tri_texture_intersect(((p_array_loop[0].u * oo_up_one_u_pixel) - 0.5f) * 2.0f,
														  ((p_array_loop[0].v * oo_up_one_v_pixel) - 0.5f) * 2.0f,
														  ((p_array_loop[1].u * oo_up_one_u_pixel) - 0.5f) * 2.0f,
														  ((p_array_loop[1].v * oo_up_one_v_pixel) - 0.5f) * 2.0f,
														  ((p_array_loop[2].u * oo_up_one_u_pixel) - 0.5f) * 2.0f,
														  ((p_array_loop[2].v * oo_up_one_v_pixel) - 0.5f) * 2.0f ))
				//				if( tri_texture_intersect((p_array_loop[0].u * 2.0f * oo_up_one_u_pixel) - 1.0f,
				//										  (p_array_loop[0].v * 2.0f * oo_up_one_v_pixel) - 1.0f,
				//										  (p_array_loop[1].u * 2.0f * oo_up_one_u_pixel) - 1.0f,
				//										  (p_array_loop[1].v * 2.0f * oo_up_one_v_pixel) - 1.0f,
				//										  (p_array_loop[2].u * 2.0f * oo_up_one_u_pixel) - 1.0f,
				//										  (p_array_loop[2].v * 2.0f * oo_up_one_v_pixel) - 1.0f ))
								{
				//					Dbg_Message("Accepted vert %d", vert_idx);
									// Convert lifetime from seconds to milliseconds.
									p_details->m_lifetimes[idx]		= (int)( lifetime * 1000.0f );
				
									p_target_verts[index + 0].pos	= p_array_loop[0].pos;
									p_target_verts[index + 1].pos	= p_array_loop[1].pos;
									p_target_verts[index + 2].pos	= p_array_loop[2].pos;
				
									p_target_verts[index + 0].u		= p_array_loop[0].u;
									p_target_verts[index + 0].v		= p_array_loop[0].v;
									p_target_verts[index + 1].u		= p_array_loop[1].u;
									p_target_verts[index + 1].v		= p_array_loop[1].v;
									p_target_verts[index + 2].u		= p_array_loop[2].u;
									p_target_verts[index + 2].v		= p_array_loop[2].v;
				
									p_target_verts[index + 0].col	= p_array_loop[0].col;
									p_target_verts[index + 1].col	= p_array_loop[1].col;
									p_target_verts[index + 2].col	= p_array_loop[2].col;
				
									idx								= p_details->GetOldestSplat();
									index							= idx * 3;
									Dbg_Assert((index + 2) < (SPLAT_POLYS_PER_MESH * 3));
									no_polys 						= false;
								}
				
								p_array_loop					+= 3;
				//				vert_idx++;
							}
				
							delete [] p_array_start;
				
							// Check if all the new polys were rejected.  Don't know why this happens, but we want
							// to get rid of the original texture.
							if (no_polys)
							{
								plat_texture_splat_reset_poly( p_details, idx );
							}
				
				

	//						OSReport( "Add U0: %6.1f %6.1f\n", p_target_verts[index + 0].u, p_target_verts[index + 0].v );
	//						OSReport( "Add U1: %6.1f %6.1f\n", p_target_verts[index + 1].u, p_target_verts[index + 1].v );
	//						OSReport( "Add U2: %6.1f %6.1f\n", p_target_verts[index + 2].u, p_target_verts[index + 2].v );

	#						if DRAW_DEBUG_LINES
	//						D3DXVECTOR3* p_d3dvert;
	//						p_d3dvert = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 ));
	//						Mth::Vector v0( p_d3dvert->x, p_d3dvert->y, p_d3dvert->z );
	//						p_d3dvert = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 ));
	//						Mth::Vector v1( p_d3dvert->x, p_d3dvert->y, p_d3dvert->z );
	//						p_d3dvert = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 ));
	//						Mth::Vector v2( p_d3dvert->x, p_d3dvert->y, p_d3dvert->z );
	//						Gfx::AddDebugLine( v0, v1, MAKE_RGB( 0, 200, 200 ), MAKE_RGB( 0, 200, 200 ), 1 );
	//						Gfx::AddDebugLine( v1, v2, MAKE_RGB( 0, 200, 200 ), MAKE_RGB( 0, 200, 200 ), 1 );
	//						Gfx::AddDebugLine( v2, v0, MAKE_RGB( 0, 200, 200 ), MAKE_RGB( 0, 200, 200 ), 1 );

	//						v0[Y] += 10;
	//						v1[Y] += 10;
	//						v2[Y] += 10;

							ADD_LINE( v0, v1, 0, 200, 200 );
							ADD_LINE( v1, v2, 0, 200, 200 );
							ADD_LINE( v2, v0, 0, 200, 200 );
	#						endif // DRAW_DEBUG_LINES
						}
					}
				}
			}
		}
		++pp_sectors;
	}
#endif		// 0
	
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_render( void )
{
#if 0
//#	if DRAW_DEBUG_LINES
//#	endif 		//DRAW_DEBUG_LINES
//


	sNgcSplatInstanceDetails *p_Ngc_details;

	Dbg_Assert( p_splat_details_table );

	p_splat_details_table->IterateStart();
	sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
	while( p_details )
	{
		p_Ngc_details = static_cast( p_details );

		if( p_Ngc_details->m_highest_active_splat >= 0 )
		{
//			NxNgc::multi_mesh( &p_Ngc_details->m_mat, &p_Ngc_details->m_pass, true, true );

			GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
		
//			NxNgc::sMaterialHeader mat;
//			NxNgc::sMaterialPassHeader pass;
//		
//			mat.m_checksum			= 0;
//			mat.m_passes			= 1;
//			mat.m_alpha_cutoff		= 0;
//			mat.m_flags				= 0;
//			mat.m_material_dl_id	= 0;
//			mat.m_draw_order		= 0;
//			mat.m_pass_item			= 0;
//			mat.m_texture_dl_id		= 0;
//		
//			pass.m_texture.p_data	= p_Ngc_details->mp_texture;
//			pass.m_flags			= (1<<0)|(1<<5)|(1<<6);
//			pass.m_filter			= 0;
//			pass.m_blend_mode		= (unsigned char)NxNgc::vBLEND_MODE_BLEND;
//			pass.m_alpha_fix		= 128;
//			pass.m_uv_wibble_index	= 0;
//			pass.m_color			= (GXColor){128,128,128,255};
//			pass.m_k				= 0;
//			pass.m_u_tile			= 0;
//			pass.m_v_tile			= 0;
//		
//			multi_mesh( &mat, &pass, true, true );





//			GX::SetPointSize( 6, GX_TO_ONE );
//
//			GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE );
//			GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
//			GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );
//			GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
//
//			GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
//												   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV,
//												   GX_TEV_SWAP0, GX_TEV_SWAP0 );
//
//			GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_RASC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
//											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
//			GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
//			GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//			GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,0,255,255} );
//
//			GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
//
//			sNgcVert *p_vert_array = p_Ngc_details->m_verts;
//			for (int i = 0; i < ( p_details->m_highest_active_splat + 1 ); i++, p_vert_array += 3)
//			{
////				printf( "Splat Tri:\n%8.3f %8.3f %8.3f\n", p_vert_array[0].pos[X], p_vert_array[0].pos[Y], p_vert_array[0].pos[Z] );
////				printf( "%8.3f %8.3f %8.3f\n", p_vert_array[1].pos[X], p_vert_array[1].pos[Y], p_vert_array[1].pos[Z] );
////				printf( "%8.3f %8.3f %8.3f\n", p_vert_array[2].pos[X], p_vert_array[2].pos[Y], p_vert_array[2].pos[Z] );
//				GX::Begin( GX_LINES, GX_VTXFMT0, 2 ); 
//				GX::Position3f32( p_vert_array[0].pos[X], p_vert_array[0].pos[Y], p_vert_array[0].pos[Z] );
//				GX::Position3f32( p_vert_array[1].pos[X], p_vert_array[1].pos[Y], p_vert_array[1].pos[Z] );
//				GX::End(); 
//				GX::Begin( GX_LINES, GX_VTXFMT0, 2 ); 
//				GX::Position3f32( p_vert_array[1].pos[X], p_vert_array[1].pos[Y], p_vert_array[1].pos[Z] );
//				GX::Position3f32( p_vert_array[2].pos[X], p_vert_array[2].pos[Y], p_vert_array[2].pos[Z] );
//				GX::End(); 
//				GX::Begin( GX_LINES, GX_VTXFMT0, 2 ); 
//				GX::Position3f32( p_vert_array[2].pos[X], p_vert_array[2].pos[Y], p_vert_array[2].pos[Z] );
//				GX::Position3f32( p_vert_array[0].pos[X], p_vert_array[0].pos[Y], p_vert_array[0].pos[Z] );
//				GX::End(); 
//			}
////			GX::End();
//
//





			GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

			GX::UploadTexture(  p_Ngc_details->mp_texture->pTexelData,
								p_Ngc_details->mp_texture->ActualWidth,
								p_Ngc_details->mp_texture->ActualHeight,
								GX_TF_CMPR,
								GX_CLAMP,
								GX_CLAMP,
								GX_FALSE,
								GX_LINEAR,
								GX_LINEAR,
								0.0f,
								0.0f,
								0.0f,
								GX_FALSE,
								GX_TRUE,
								GX_ANISO_1,
								GX_TEXMAP0 ); 
			GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, p_Ngc_details->mp_texture->ActualWidth, p_Ngc_details->mp_texture->ActualHeight );
		
			GX::SetTexChanTevIndCull( 1, 0, 1, 0, GX_CULL_NONE );
			GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
			GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );
		
			GX::SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0 );
			GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );		// Replace
//			GX::SetBlendMode( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
		
			GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);
			GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO,
									 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV,
									 GX_TEV_SWAP0, GX_TEV_SWAP0 );
			GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_TEXC, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO,
								 GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_DISABLE, GX_TEVPREV );


			f32 pm[GX_PROJECTION_SZ];
			GX::GetProjectionv( pm );
			float value = pm[6] + ( 150.0f * 5 ) * pm[5];

			GXWGFifo.u8 = GX_LOAD_XF_REG;
			GXWGFifo.u16 = 0;
			GXWGFifo.u16 = 0x1025;
			GXWGFifo.f32 = value;


//			GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
//			GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

			// Render the triangles
			GX::SetVtxDesc( 3, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
//			GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
			GX::Begin( GX_TRIANGLES, GX_VTXFMT6, ( p_details->m_highest_active_splat + 1 ) * 3 ); 
			sNgcVert *p_vert_array = p_Ngc_details->m_verts;
			for (int i = 0; i < ( p_details->m_highest_active_splat + 1 ); i++, p_vert_array += 3)
			{
				GX::Position3f32( p_vert_array[0].pos[X], p_vert_array[0].pos[Y], p_vert_array[0].pos[Z] );
				GX::Color1u32(*((u32*)&(p_vert_array[0].col)));
				GX::TexCoord2s16( p_vert_array[0].u, p_vert_array[0].v );

				GX::Position3f32( p_vert_array[1].pos[X], p_vert_array[1].pos[Y], p_vert_array[1].pos[Z] );
				GX::Color1u32(*((u32*)&(p_vert_array[1].col)));
				GX::TexCoord2s16( p_vert_array[1].u, p_vert_array[1].v );

				GX::Position3f32( p_vert_array[2].pos[X], p_vert_array[2].pos[Y], p_vert_array[2].pos[Z] );
				GX::Color1u32(*((u32*)&(p_vert_array[2].col)));
				GX::TexCoord2s16( p_vert_array[2].u, p_vert_array[2].v );

//				GX::Position3f32( 0.0f, 0.0f, 0.0f );
//				GX::Position3f32( 0.0f, 0.0f, 0.0f );
//				GX::Position3f32( 0.0f, 0.0f, 0.0f );
//				GX::Position3f32( 0.0f, 0.0f, 0.0f );
//  			GX::Position3f32( 0.0f, 0.0f, 0.0f );
//				GX::Position3f32( 0.0f, 0.0f, 0.0f );
			}
			GX::End();
		}
		
		p_details = p_splat_details_table->IterateNext();
	}
#endif		// 0
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_shatter_initialize( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_shatter_cleanup( void )
{
	sNgcShatterInstanceDetails *p_Ngc_details;

	Dbg_Assert( p_shatter_details_table );
	
	p_shatter_details_table->IterateStart();
	sShatterInstanceDetails *p_details = p_shatter_details_table->IterateNext();
	while( p_details )
	{
		p_Ngc_details = static_cast( p_details );
		
		p_details = p_shatter_details_table->IterateNext();

		p_shatter_details_table->FlushItem((uint32)p_Ngc_details );
		delete p_Ngc_details;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_shatter( CGeom *p_geom )
{
#if 1
	CNgcGeom *p_Ngc_geom = static_cast( p_geom );

	// For each mesh in the geom...
	for( uint32 m = 0; m < p_Ngc_geom->m_num_mesh; ++m )
	{
		NxNgc::sMesh *p_mesh = p_Ngc_geom->m_mesh_array[m];

		NxNgc::sScene *p_scene = p_Ngc_geom->mp_scene->GetEngineScene();

		// Count indices.
		int num_indices = 0;
		unsigned char * p_start = (unsigned char *)&p_mesh->mp_dl[1];
		uint32 cp = (p_start[2]<<24)|(p_start[3]<<16)|(p_start[4]<<8)|p_start[5];
		unsigned char * p_end = &p_start[p_mesh->mp_dl->m_size];
		p_start = &p_start[p_mesh->mp_dl->m_index_offset];		// Skip to actual 1st GDBegin.
		unsigned char * p8 = p_start;

		int stride = p_mesh->mp_dl->m_index_stride;

		unsigned char * p_index_buffer  = &p8[1];

		int off = 2 + ( ( cp & ((1<<11)|(1<<12)) ) ? 2 : 0 );

		GXVtxFmt format = GX_VTXFMT2;

		while ( p8 < p_end )
		{
			if ( ( p8[0] & 0xf8 ) == GX_TRIANGLESTRIP )
			{
				format = (GXVtxFmt)(p8[0] & 7);
				// Found a triangle strip - parse it.
				int num_verts = ( p8[1] << 8 ) | p8[2];
				p8 += 3;		// Skip GDBegin

				num_indices += num_verts;

				p8 += stride * 2 * num_verts;
			}
			else
			{
				break;
			}
		}

		if( num_indices >= 3 )
		{
			triSubdivideStack.SetBlockSize( sizeof( sNgcVert ) );

			// Create the index buffer.
			uint polys = 0;
			uint16 idxbuf[1024*3];		// 6k.
			uint16 cidxbuf[1024*3];		// 6k.
			uint16 tidxbuf[1024*3];		// 6k.
			uint16 * p_dest = idxbuf;
			uint16 * p_cdest = cidxbuf;
			uint16 * p_tdest = tidxbuf;
			uint8 * p_source = (uint8*)p_index_buffer;
			uint16 total = 0;

			while ( total < num_indices )
			{
				uint16 num = ( p_source[0] << 8 ) | p_source[1];
				p_source += 2;
				uint16 i0, ci0, ti0;
				uint16 i1 = ( p_source[0] << 8 ) | p_source[1];
				uint16 ci1 = ( p_source[off+0] << 8 ) | p_source[off+1];
				uint16 ti1 = ( p_source[off+2] << 8 ) | p_source[off+3];
				p_source += stride * 2;
				uint16 i2 = ( p_source[0] << 8 ) | p_source[1];
				uint16 ci2 = ( p_source[off+0] << 8 ) | p_source[off+1];
				uint16 ti2 = ( p_source[off+2] << 8 ) | p_source[off+3];
				p_source += stride * 2;
				for ( int vv = 2; vv < num; vv++ )
				{
					i0 = i1;
					i1 = i2;
					i2 = ( p_source[0] << 8 ) | p_source[1];
					ci0 = ci1;
					ci1 = ci2;
					ci2 = ( p_source[off+0] << 8 ) | p_source[off+1];
					ti0 = ti1;
					ti1 = ti2;
					ti2 = ( p_source[off+2] << 8 ) | p_source[off+3];
					p_source += stride * 2;
					*p_dest++ = i0;
					*p_dest++ = i1;
					*p_dest++ = i2;
					*p_cdest++ = ci0;
					*p_cdest++ = ci1;
					*p_cdest++ = ci2;
					*p_tdest++ = ti0;
					*p_tdest++ = ti1;
					*p_tdest++ = ti2;
					polys++;
				}
				p_source += 1;
				total += num + 1;		// Count each index + the count.
			}

			// First scan through each non-degenerate tri, counting them to see how many verts we'll need.
			// We also have to figure the area of the tris here, since we need to calculate the worst case given the requirements for subdivision.
			uint32 valid_tris	= 0;
			for( uint32 i = 0; i < polys; ++i )
			{
				// Wrap the indices round.
				uint16 index0 = idxbuf[(i*3)+0];
				uint16 index1 = idxbuf[(i*3)+1];
				uint16 index2 = idxbuf[(i*3)+2];

				uint16 cindex0 = cidxbuf[(i*3)+0];
				uint16 cindex1 = cidxbuf[(i*3)+1];
				uint16 cindex2 = cidxbuf[(i*3)+2];

				uint16 tindex0 = tidxbuf[(i*3)+0];
				uint16 tindex1 = tidxbuf[(i*3)+1];
				uint16 tindex2 = tidxbuf[(i*3)+2];

				if(( index0 != index1 ) && ( index0 != index2 ) && ( index1 != index2 ))
				{
					++valid_tris;

					sNgcVert v0;
					sNgcVert v1;
					sNgcVert v2;

					if ( p_mesh->mp_dl->mp_pos_pool )
					{
						v0.pos.Set( p_mesh->mp_dl->mp_pos_pool[(index0*3)+0],
									p_mesh->mp_dl->mp_pos_pool[(index0*3)+1],
									p_mesh->mp_dl->mp_pos_pool[(index0*3)+2] );
						v1.pos.Set( p_mesh->mp_dl->mp_pos_pool[(index1*3)+0],
									p_mesh->mp_dl->mp_pos_pool[(index1*3)+1],
									p_mesh->mp_dl->mp_pos_pool[(index1*3)+2] );
						v2.pos.Set( p_mesh->mp_dl->mp_pos_pool[(index2*3)+0],
									p_mesh->mp_dl->mp_pos_pool[(index2*3)+1],
									p_mesh->mp_dl->mp_pos_pool[(index2*3)+2] );
					}
					else
					{
						v0.pos.Set( p_scene->mp_pos_pool[(index0*3)+0],
									p_scene->mp_pos_pool[(index0*3)+1],
									p_scene->mp_pos_pool[(index0*3)+2] );
						v1.pos.Set( p_scene->mp_pos_pool[(index1*3)+0],
									p_scene->mp_pos_pool[(index1*3)+1],
									p_scene->mp_pos_pool[(index1*3)+2] );
						v2.pos.Set( p_scene->mp_pos_pool[(index2*3)+0],
									p_scene->mp_pos_pool[(index2*3)+1],
									p_scene->mp_pos_pool[(index2*3)+2] );
					}

					if ( p_scene->mp_col_pool )
					{
						if ( p_mesh->mp_dl->mp_col_pool )
						{
							v0.col = *((GXColor*)&p_mesh->mp_dl->mp_col_pool[cindex0]);
							v1.col = *((GXColor*)&p_mesh->mp_dl->mp_col_pool[cindex1]);
							v2.col = *((GXColor*)&p_mesh->mp_dl->mp_col_pool[cindex2]);
						}
						else
						{
							v0.col = *((GXColor*)&p_scene->mp_col_pool[cindex0]);
							v1.col = *((GXColor*)&p_scene->mp_col_pool[cindex1]);
							v2.col = *((GXColor*)&p_scene->mp_col_pool[cindex2]);
						}
					}

					if ( p_scene->mp_tex_pool )
					{
						v0.u = p_scene->mp_tex_pool[(tindex0*2)+0];
						v0.v = p_scene->mp_tex_pool[(tindex0*2)+1];
						v1.u = p_scene->mp_tex_pool[(tindex1*2)+0];
						v1.v = p_scene->mp_tex_pool[(tindex1*2)+1];
						v2.u = p_scene->mp_tex_pool[(tindex2*2)+0];
						v2.v = p_scene->mp_tex_pool[(tindex2*2)+1];
					}

					v0.pos[W] = v1.pos[W] = v2.pos[W] = 1.0f;		// Make sure they are points, not vectors
					
					// Push this tri onto the stack.
					triSubdivideStack.Push( &v0 );
					triSubdivideStack.Push( &v1 );
					triSubdivideStack.Push( &v2 );

					// Figure the area of this tri.
					Mth::Vector p( v1.pos[X] - v0.pos[X], v1.pos[Y] - v0.pos[Y], v1.pos[Z] - v0.pos[Z], 0.0f );
					Mth::Vector q( v2.pos[X] - v0.pos[X], v2.pos[Y] - v0.pos[Y], v2.pos[Z] - v0.pos[Z], 0.0f );
					Mth::Vector r(( p[Y] * q[Z] ) - ( q[Y] * p[Z] ), ( p[Z] * q[X] ) - ( q[Z] * p[X] ), ( p[X] * q[Y] ) - ( q[X] * p[Y] ), 0.0f);
					float area_squared = r.LengthSqr();
					if( area_squared > shatterAreaTest )
					{
						// We will need to subdivide - each subdivision will result in an area one quarter the previous area
						// (and thusly the square of the area will be one sixteenth the previous area).
						int num_extra_tris = 1;
						while( area_squared > shatterAreaTest )
						{
							num_extra_tris *= 4;
							area_squared *= ( 1.0f / 16.0f );
						}
					
						// This original tri will not be added...
						--valid_tris;

						// ...however, the subdivided versions will.
						valid_tris += num_extra_tris;
					}
				}
			}

			if( valid_tris == 0 )
			{
				continue;
			}
			
			// Create a tracking structure for this mesh.
			sNgcShatterInstanceDetails *p_details		= new sNgcShatterInstanceDetails( valid_tris, p_mesh, p_scene, format );
			sNgcVert				   *p_write_vertex	= p_details->mp_vertex_buffer;
			uint32						details_index	= 0;

			Mth::Vector					spread_center	= shatterVelocity * -shatterSpreadFactor;
			float						base_speed		= shatterVelocity.Length();

			spread_center[X] += p_mesh->mp_dl->m_sphere[0];
			spread_center[Y] += p_mesh->mp_dl->m_sphere[1];
			spread_center[Z] += p_mesh->mp_dl->m_sphere[2];
			
			// Add the tracking structure to the table.
			p_shatter_details_table->PutItem((uint32)p_details, p_details );
			
			// Process-subdivide the entire stack.
			sNgcVert *p_copy_vertex = p_write_vertex;
			while( subdivide_tri_stack( &p_write_vertex, p_mesh ));
					
			// Copy the (possibly subdivided) vertex data over.
			while( p_copy_vertex < p_write_vertex )
			{
				Mth::Vector *p_vert0 = &p_copy_vertex[0].pos;
				Mth::Vector *p_vert1 = &p_copy_vertex[1].pos;
				Mth::Vector *p_vert2 = &p_copy_vertex[2].pos;
				
				// Calculate position as the midpoint of the three vertices per poly.
				p_details->mp_positions[details_index][X] = ( p_vert0->GetX() + p_vert1->GetX() + p_vert2->GetX() ) * ( 1.0f / 3.0f );
				p_details->mp_positions[details_index][Y] = ( p_vert0->GetY() + p_vert1->GetY() + p_vert2->GetY() ) * ( 1.0f / 3.0f );
				p_details->mp_positions[details_index][Z] = ( p_vert0->GetZ() + p_vert1->GetZ() + p_vert2->GetZ() ) * ( 1.0f / 3.0f );

				// Calculate the vector  back from the bounding box of the object. Then use this to figure the 'spread' of the
				// shards by calculating the vector from this position to the center of each shard.
				float speed = base_speed + ( base_speed * (( shatterVelocityVariance * rand() ) / RAND_MAX ));
				p_details->mp_velocities[details_index] = ( p_details->mp_positions[details_index] - spread_center ).Normalize( speed );

				Mth::Vector axis( -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), -1.0f + ( 2.0f * (float)rand() / RAND_MAX ));
				axis.Normalize();
				p_details->mp_matrices[details_index].Ident();
				p_details->mp_matrices[details_index].Rotate( axis, 0.1f * ((float)rand() / RAND_MAX ));

				p_copy_vertex += 3;
						
				++details_index;
			}
		}
	}
#endif
}



/******************************************************************************
 *
 * 
 *****************************************************************************/
void plat_shatter_update( sShatterInstanceDetails *p_details, float framelength )
{
	sNgcShatterInstanceDetails *p_Ngc_details = static_cast( p_details );
	
	sNgcVert *p_vert_data = p_Ngc_details->mp_vertex_buffer;
	
	for( int i = 0; i < p_details->m_num_triangles; ++i )
	{
		Mth::Vector *p_v0	= &p_vert_data[(i*3)+0].pos;
		Mth::Vector *p_v1	= &p_vert_data[(i*3)+1].pos;
		Mth::Vector *p_v2	= &p_vert_data[(i*3)+2].pos;
		// To move the shatter pieces:
		// 1) subtract position from each vertex
		// 2) rotate
		// 3) update position with velocity
		// 4) add new position to each vertex

		// The matrix holds 3 vectors at once.
		Mth::Matrix m;
		m[X].Set( p_v0->GetX() - p_details->mp_positions[i][X], p_v0->GetY() - p_details->mp_positions[i][Y], p_v0->GetZ() - p_details->mp_positions[i][Z] );
		m[Y].Set( p_v1->GetX() - p_details->mp_positions[i][X], p_v1->GetY() - p_details->mp_positions[i][Y], p_v1->GetZ() - p_details->mp_positions[i][Z] );
		m[Z].Set( p_v2->GetX() - p_details->mp_positions[i][X], p_v2->GetY() - p_details->mp_positions[i][Y], p_v2->GetZ() - p_details->mp_positions[i][Z] );
         
		m[X].Rotate( p_details->mp_matrices[i] );
		m[Y].Rotate( p_details->mp_matrices[i] );
		m[Z].Rotate( p_details->mp_matrices[i] );

		// Update the position and velocity of the shatter piece, dealing with bouncing if necessary.
		p_details->UpdateParameters( i, framelength );
      
		m[X] += p_details->mp_positions[i]; 
		m[Y] += p_details->mp_positions[i]; 
		m[Z] += p_details->mp_positions[i];

		p_v0->Set( m[X][X], m[X][Y], m[X][Z] );
		p_v1->Set( m[Y][X], m[Y][Y], m[Y][Z] );
		p_v2->Set( m[Z][X], m[Z][Y], m[Z][Z] );
	}
}



/******************************************************************************
 *
 * 
 *****************************************************************************/
void plat_shatter_render( sShatterInstanceDetails *p_details )
{
	sNgcShatterInstanceDetails *p_Ngc_details = static_cast( p_details );

	Dbg_Assert( p_Ngc_details );

//	p_Ngc_details->mp_mesh->mp_material->Submit( 0, (GXColor){0,0,0,0} );

	NxNgc::MaterialSubmit( p_Ngc_details->mp_mesh, p_Ngc_details->mp_scene );
	GX::SetChanCtrl( GX_COLOR0, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
	GX::SetChanCtrl( GX_ALPHA0, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 
//	GX::SetChanMatColor( GX_COLOR0A0, p_Ngc_details->mp_mesh->m_base_color );
	GX::SetChanMatColor( GX_COLOR0A0, (GXColor){128,128,128,255} );

//	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_IDENTITY );

	// Render the triangles
	GX::SetVtxDesc( 3, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
	GX::Begin( GX_TRIANGLES, p_Ngc_details->m_vertex_format, p_Ngc_details->m_num_triangles * 3 ); 
	sNgcVert *p_vert_array = p_Ngc_details->mp_vertex_buffer;
	for (int i = 0; i < p_Ngc_details->m_num_triangles; i++, p_vert_array += 3)
	{
		GX::Position3f32( p_vert_array[0].pos[X], p_vert_array[0].pos[Y], p_vert_array[0].pos[Z] );
		GX::Color1u32(*((u32*)&(p_vert_array[0].col)));
		GX::TexCoord2s16( p_vert_array[0].u, p_vert_array[0].v );

		GX::Position3f32( p_vert_array[1].pos[X], p_vert_array[1].pos[Y], p_vert_array[1].pos[Z] );
		GX::Color1u32(*((u32*)&(p_vert_array[1].col)));
		GX::TexCoord2s16( p_vert_array[1].u, p_vert_array[1].v );

		GX::Position3f32( p_vert_array[2].pos[X], p_vert_array[2].pos[Y], p_vert_array[2].pos[Z] );
		GX::Color1u32(*((u32*)&(p_vert_array[2].col)));
		GX::TexCoord2s16( p_vert_array[2].u, p_vert_array[2].v );
	}
	GX::End();
}

	
	
///////////////////////////////////////////////////////////////////
//
// FOG
//
///////////////////////////////////////////////////////////////////


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static bool		fogEnabled			= false;
static GXColor	fogColor			= (GXColor){ 0, 0, 0, 0 };
static float	fogNear				= 0.0f;
static float	fogFar				= 0.0f;

void		CFog::s_plat_enable_fog(bool enable)
{
	if( enable != fogEnabled )
	{
		// If we're disabling, reset to default values.
		if ( !enable )
		{
			fogColor	= (GXColor){ 0, 0, 0, 0 };
			fogNear		= 0.0f;
			fogFar		= 0.0f;
		}

		fogEnabled = enable;
	}
}

void		CFog::s_plat_set_fog_exponent(float exponent)
{
//	Dbg_Message("Stub: CFog::SetFogExponent()");
}

void		CFog::s_plat_set_fog_rgba(Image::RGBA rgba)
{
	fogColor.r = rgba.r;
	fogColor.g = rgba.g;
	fogColor.b = rgba.b;
	fogColor.a = rgba.a;
//	fogFar = 1000.0f * ( 128.0f / (float)fogColor.a );
	fogFar = 20000.0f * ( 128.0f / (float)fogColor.a ); 
}

void		CFog::s_plat_set_fog_near_distance(float distance)
{
	fogNear = distance;
//	fogFar = 1000.0f * ( 128.0f / (float)fogColor.a );
	fogFar = 20000.0f * ( 128.0f / (float)fogColor.a ); 
}

void		CFog::s_plat_set_fog_color( void )
{
	GX::SetFogColor( fogColor );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFog::s_plat_fog_update( void )
{
	if( fogEnabled )
	{
		GX::SetFog( GX_FOG_EXP, fogNear, fogFar, 2.0f, 20000.0f, fogColor );
	}
	else
	{
		GX::SetFog( GX_FOG_NONE, 0.0f, 0.0f, 0.0f, 0.0f, fogColor );
	}
}
	


} // Nx





================================================
FILE: Code/Gfx/NGC/p_nxmodel.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxModel.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/21/2002
//****************************************************************************

#include 

#include "gfx/nxmodel.h"
#include "gfx/skeleton.h"
#include 
#include "gfx/Ngc/p_nxmodel.h"
#include "gfx/Ngc/p_nxscene.h"
#include "gfx/Ngc/nx/import.h"
#include "gfx/Ngc/nx/render.h"
#include "gfx/Ngc/p_nxgeom.h"

#include 

			   
#include 
#include 

int			test_num_bones			= 0;
Mth::Matrix	*p_test_bone_matrices	= NULL;
Mth::Matrix	test_root_matrix;

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CNgcModel::plat_init_skeleton( int numBones )
{
//	if ( !mp_instance ) return false;
//	Mth::Matrix * p_bone = new Mth::Matrix[numBones];
//
//	mp_instance->SetBoneTransforms( p_bone );
//	for ( int i = 0; i < numBones; i++ )
//	{
//		p_bone[i].Identity();
//	}
    
	return true;
}


///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//	
///*
//	bool CNgcModel::plat_load_file(const char* p_fileName)
//{
//	// Machine specific code here ............
//	
//	// TODO:  Make this more generalized
//
//	// Load in the texture dictionary for the model.
//	Lst::HashTable< NxNgc::sTexture > *p_texture_table = NxNgc::LoadTextureFile( "models/testskin/testskin.tex.xbx" );
//
//    return true;
//}
//*/
//
//
//bool CNgcModel::plat_load_mesh( CMesh* pMesh )
//{
//	// The skeleton must exist by this point (unless it's a hacked up car).
//	int numBones;
//	numBones = mp_skeleton ? mp_skeleton->GetNumBones() : 1;
//	
//	Mth::Matrix temp;
//	CNgcMesh *p_Ngc_mesh = static_cast( pMesh );
//	mp_instance = new NxNgc::CInstance( p_Ngc_mesh->GetScene()->GetEngineScene(), temp, numBones, mp_boneTransforms );
//
//    return true;
//}
//	
//	
//	
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//bool CNgcModel::plat_unload_mesh( void )
//{
//	if ( mp_instance != NULL )
//	{
//		delete mp_instance;
//		mp_instance = NULL;
//	}
//
//	return true;
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//	
//bool CNgcModel::plat_set_render_mode(ERenderMode mode)
//{
//	// Machine specific code here ............
//    return true;
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//	
//bool CNgcModel::plat_set_color(uint8 r, uint8 g, uint8 b, uint8 a)
//{
//	// Machine specific code here ............
//    return true;
//}
//
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//bool CNgcModel::plat_set_visibility(uint32 mask)
//{
//    return true;
//}
//
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//bool CNgcModel::plat_set_active( bool active )
//{
//	if( mp_instance )
//	{
//		mp_instance->SetActive( active );
//	}
//	return true;
//}
//
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//bool CNgcModel::plat_set_scale(float scaleFactor)
//{
//	// Machine specific code here ............
//    return true;
//}
//
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//bool CNgcModel::plat_replace_texture(char* p_srcFileName, char* p_dstFileName)
//{
//	// Machine specific code here ............
//    return true;
//}
//
//
//
//void ConvertMatrix(Mth::Matrix* pMatrix, void *pMat)
//{
//}
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//bool CNgcModel::plat_render( Mth::Matrix *pRootMatrix, Mth::Matrix *pBoneMatrices, int numBones )
//{
//	if( mp_instance )
//	{
//		mp_instance->SetTransform( *pRootMatrix );
//	}
//    return true;
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CNgcModel::plat_prepare_materials( void )
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CNgcModel::plat_refresh_materials( void )
{
	int numGeoms = GetNumGeoms();
	for ( int i = 0; i < numGeoms; i++ )
	{
		NxNgc::CInstance *p_instance = NULL;
		CNgcGeom * p_ngc_geom = static_cast( mp_geom[i] ); 
		p_instance = p_ngc_geom->GetInstance();
		NxNgc::sScene *p_scene = p_instance->GetScene();

		// Setup material data texture pointers.
		for ( unsigned int lp = 0; lp < p_scene->mp_scene_data->m_num_materials; lp++ )
		{
			NxNgc::sMaterialHeader * p_mat = &p_scene->mp_material_header[lp];
			NxNgc::sTextureDL * p_dl = &p_scene->mp_texture_dl[lp]; 
			NxNgc::sMaterialPassHeader * p_pass = &p_scene->mp_material_pass[p_mat->m_pass_item];

			GX::ResolveDLTexAddr( p_dl, p_pass, p_mat->m_passes );

//			GX::begin( p_dl->mp_dl, p_dl->m_dl_size );
//			multi_mesh( p_mat, p_pass, true, true );
//			p_dl->m_dl_size = GX::end();

			// See if any textures have alpha, but the texture DL doesn't set it up. If so, flag material as
			// not using display list.
			bool direct = false;
			for ( int p = 0; p < p_mat->m_passes; p++, p_pass++ )
			{
				if ( p_pass->m_texture.p_data )
				{
//					bool current_alpha = ( p_pass->m_texture.p_data->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA ) ? true : false;
//					bool dl_alpha = p_dl->m_alpha_offset[p] ? true : false;
//					if ( current_alpha != dl_alpha )
//					{
//						// Must use direct setup mode.
//						direct = true;
//					}
					if ( p_pass->m_texture.p_data->flags & NxNgc::sTexture::TEXTURE_FLAG_REPLACED )
					{
						direct = true;
					}

				}
			}
			if ( direct )
			{
				p_mat->m_flags |= (1<<3);
			}
			else
			{
				p_mat->m_flags &= ~(1<<3);
			}
		}
	}

	return true;
}

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcModel::CNgcModel()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcModel::~CNgcModel()
{
	if ( mp_instance && mp_instance->GetBoneTransforms() )
	{
		delete mp_instance->GetBoneTransforms();
		mp_instance->SetBoneTransforms( NULL );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CNgcModel::plat_get_bounding_sphere()
{
	Mth::Vector sphere, sphere1, sum, diff;
	float dist;

	// this should probably never happen
	if (m_numGeoms == 0)
		return Mth::Vector(0.0f, 0.0f, 0.0f, 0.0f);

	// combine the spheres of all geoms
	// (this should really be done once at load time)

	// start with first sphere
	sphere = mp_geom[0]->GetBoundingSphere();

	// loop over remaining spheres, expanding as necessary
	for (int i=1; iGetBoundingSphere();

		// centre-to-centre vector, and distance
		diff = sphere1-sphere;
		dist = diff.Length();

		// test for sphere1 inside sphere
		if (dist+sphere1[3] <= sphere[3])
			continue;			// keep sphere

		// test for sphere inside sphere1
		if (dist+sphere[3] <= sphere1[3])
		{
			sphere = sphere1;	// replace sphere
			continue;
		}

		// otherwise make a larger sphere that contains both
		sum       = sphere+sphere1;
		sphere    = 0.5f * (sum + (diff[3]/dist) * diff);
		sphere[3] = 0.5f * (dist + sum[3]);
	}

	return sphere;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcModel::plat_set_bounding_sphere( const Mth::Vector& boundingSphere )
{
	// loop over all spheres
	for ( int i = 0; i < m_numGeoms; i++ )
	{
		mp_geom[i]->SetBoundingSphere( boundingSphere );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx

				



================================================
FILE: Code/Gfx/NGC/p_nxmodel.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxModel.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  1/8/2002
//****************************************************************************

#ifndef	__GFX_P_NX_MODEL_H__
#define	__GFX_P_NX_MODEL_H__
    
#include "gfx/nxmodel.h"
#include "gfx/Ngc/nx/instance.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CModel
    
class CNgcModel : public CModel
{
public:
						CNgcModel();
	virtual 			~CNgcModel();

	NxNgc::CInstance    *GetInstance() { return mp_instance; }
	void				SetInstance( NxNgc::CInstance * p ) { mp_instance = p; }
private:				// It's all private, as it is machine specific
//	bool				plat_load_mesh( CMesh* pMesh );
//	bool				plat_unload_mesh();
//	bool				plat_set_render_mode(ERenderMode mode);
//	bool				plat_set_color(uint8 r, uint8 g, uint8 b, uint8 a);
//	bool				plat_set_visibility(uint32 mask);
//	bool				plat_set_active( bool active );
//    bool				plat_set_scale( float scaleFactor );
//    bool				plat_replace_texture( char* p_srcFileName, char* p_dstFileName );
//	bool				plat_render(Mth::Matrix* pRootMatrix, Mth::Matrix* ppBoneMatrices, int numBones);

	bool				plat_init_skeleton( int numBones );

	bool				plat_prepare_materials( void );
	bool				plat_refresh_materials( void );

	Mth::Vector 		plat_get_bounding_sphere();
	void				plat_set_bounding_sphere( const Mth::Vector& boundingSphere );

	NxNgc::CInstance	*mp_instance;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx

#endif 



================================================
FILE: Code/Gfx/NGC/p_nxnewparticle.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_NxNewParticle.cpp										**
**																			**
**	Created by:		3/25/03	-	SPG											**
**																			**
**	Description:	Ngc new parametric particle system						**
*****************************************************************************/

#include 

#include 

#include 
#include 
#include 
#include 

#include "gfx/ngc/p_nxparticle.h"

#include "dolphin/base/ppcwgpipe.h"
#include "dolphin/gx/gxvert.h"
#include 


extern "C"
{

extern float ReciprocalEstimate_ASM( float f );

extern void RenderNewParticles( Nx::CParticleStream * p_stream, float lifetime, float midpercent, bool use_mid_color, Image::RGBA * p_color0, Mth::Vector * p0, Mth::Vector * sr, Mth::Vector * su, float * p_params, float nearz );

}

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

//static int rand_seed;
//static int rand_a	= 314159265;
//static int rand_b	= 178453311;


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//static void seed_particle_rnd( int s, int a, int b )
//{
//	rand_seed		= s;
//	rand_a			= a;
//	rand_b			= b;
//}
//
//
//
///******************************************************************/
///*                                                                */
///*                                                                */
///******************************************************************/
//static int particle_rnd( int n )
//{
//	rand_seed	= rand_seed * rand_a + rand_b;
//	rand_a		= ( rand_a ^ rand_seed ) + ( rand_seed >> 4 );
//	rand_b		+= ( rand_seed >> 3 ) - 0x10101010L;
//	return (int)(( rand_seed & 0xffff ) * n ) >> 16;
//}
//
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CParticleStream::AdvanceSeed( int num_places )
{
//	// Seed the random number generator back to the current seed.
//	seed_particle_rnd( m_rand_seed, m_rand_a, m_rand_b );
//
//	// Each particle will call the random function four times.
//	for( int i = 0; i < ( num_places * 4 ); i++ )
//	{
//		particle_rnd( 1 );
//	}
//
//	m_rand_seed = rand_seed;
//	m_rand_a	= rand_a;
//	m_rand_b	= rand_b;

	// Seed the random number generator back to the current seed.
	uint32 rand_current = m_rand_current;

	// Each particle will call the random function four times.
	for( int i = 0; i < ( num_places * 4 ); i++ )
	{
		rand_current = ( rand_current & 1 ) ? ( rand_current >> 1 ) ^ 0x3500 : ( rand_current >> 1 );
	}

	m_rand_current = rand_current;
}
	
	
	
//inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcNewParticle::plat_render( void )
{
	CParticleStream* p_stream;
	int i;

	// Process the streams.
	if( m_params.m_EmitRate && ( !m_emitting || ( m_params.m_EmitRate != mp_newest_stream->m_rate )))
	{	
		if( m_num_streams < m_max_streams )
		{
			// Add new stream to cyclic buffer
			m_num_streams++;
			mp_newest_stream++;
			if( mp_newest_stream == mp_stream + m_max_streams )
			{
				mp_newest_stream = mp_stream;
			}

			// Initialise new stream.
			mp_newest_stream->m_rate			= m_params.m_EmitRate;
			mp_newest_stream->m_interval		= 1.0f / m_params.m_EmitRate;
			mp_newest_stream->m_oldest_age		= 0.0f;
			mp_newest_stream->m_num_particles	= 0;
//			mp_newest_stream->m_rand_seed		= rand();
//			mp_newest_stream->m_rand_a			= 314159265;
//			mp_newest_stream->m_rand_b			= 178453311;
			mp_newest_stream->m_rand_current	= rand() & ( 16384 - 1 );
			if ( mp_newest_stream->m_rand_current == 0 ) mp_newest_stream->m_rand_current = 1;
			m_emitting = true;
		}
		else
		{
			m_emitting = false;
		}
	}
	else
	{
		m_emitting = m_params.m_EmitRate;
	}

	if( !m_num_streams )
		return;

	// Age all streams.
	for( i = 0, p_stream = mp_oldest_stream; i < m_num_streams; ++i )
	{
		// Increase age of oldest particle.
		p_stream->m_oldest_age += 1.0f / 60.0f;

		// Step pointer within cyclic buffer.
		p_stream++;
		if( p_stream == mp_stream + m_max_streams )
		{
			p_stream = mp_stream;
		}
	}

	// Births into newest stream.
	if( m_emitting )
	{
		// How many particles so far emitted?
		mp_newest_stream->m_num_particles = (int)( mp_newest_stream->m_oldest_age * mp_newest_stream->m_rate + 1.0f );
	}

	// Deaths from oldest stream.
	if( mp_oldest_stream->m_oldest_age > m_params.m_Lifetime )
	{
		// Work out number dead.
		int particles_dead = (int)(( mp_oldest_stream->m_oldest_age - m_params.m_Lifetime ) * mp_oldest_stream->m_rate + 1.0f );

		// Remove dead particles.
		mp_oldest_stream->m_num_particles -= particles_dead;

		// Should we keep processing the oldest stream?
		if( mp_oldest_stream->m_num_particles > 0 || ( m_num_streams == 1 && m_emitting ))
		{
			// Adjust age of oldest particle.
			mp_oldest_stream->m_oldest_age -= (float)particles_dead * mp_oldest_stream->m_interval;

			// Advance seed.
			mp_oldest_stream->AdvanceSeed( particles_dead );
		}
		else
		{
			// Remove oldest stream and wrap in cyclic buffer if necessary.
			m_num_streams--;
			mp_oldest_stream++;
			if( mp_oldest_stream == mp_stream + m_max_streams )
			{
				mp_oldest_stream = mp_stream;
			}
			if( !m_num_streams )
				return;
		}
	}

//	// Now render the streams. after checking the bounding sphere is visible.
//	D3DXVECTOR3	center( m_bsphere[X], m_bsphere[Y], m_bsphere[Z] );
//	if( !NxNgc::frustum_check_sphere( ¢er, m_bsphere[W] ))
//	{
//		return;
//	}
	if( !m_params.m_LocalCoord )
	{
		if ( !NxNgc::frustum_check_sphere( m_bsphere ) ) return;
	}
 //   if ( NxNgc::TestSphereAgainstOccluders( &sphere ) )

	GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );

	NxNgc::sMaterialHeader mat;
	NxNgc::sMaterialPassHeader pass;

	mat.m_checksum			= 0;
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 0;
	mat.m_flags				= 0;
//	mat.m_material_dl_id	= 0;
	mat.m_draw_order		= 0;
	mat.m_pass_item			= 0;
	mat.m_texture_dl_id		= 0;
//	mat.m_shininess			= 0;
//	mat.m_specular_color	= (GXColor){255,255,255,255};

	pass.m_texture.p_data	= mp_texture;
	pass.m_flags			= (1<<0)|(1<<5)|(1<<6);
	pass.m_filter			= 0;
	pass.m_blend_mode		= m_blend;
	pass.m_alpha_fix		= m_fix;
	pass.m_uv_wibble_index	= 0;
	pass.m_color			= (GXColor){64,64,64,255};
	pass.m_k				= 0;
	pass.m_u_tile			= 0;
	pass.m_v_tile			= 0;
	pass.m_uv_enabled		= false;

	multi_mesh( &mat, &pass, true, true );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;

	NsVector	up( 0.0f, 1.0f, 0.0f );
	NsVector screen_right;
	NsVector screen_up;
	screen_right.cross( *p_matrix->getAt(), up );
	screen_up.cross( screen_right, *p_matrix->getAt());

	screen_right.normalize();
	screen_up.normalize();
	
	Mth::Vector sr;
	Mth::Vector su;
	sr[X] = screen_right.x;
	sr[Y] = screen_right.y;
	sr[Z] = screen_right.z;
	sr[W] = 1.0f;

	su[X] = screen_up.x;
	su[Y] = screen_up.y;
	su[Z] = screen_up.z;
	su[W] = 1.0f;

	GQRSetup6( GQR_SCALE_64,		// Pos
			   GQR_TYPE_S16,
			   GQR_SCALE_64,
			   GQR_TYPE_S16 );
	GQRSetup7( 14,		// Normal
			   GQR_TYPE_S16,
			   14,
			   GQR_TYPE_S16 );

	f32				pm[GX_PROJECTION_SZ];
	f32				vp[GX_VIEWPORT_SZ];

	GX::GetProjectionv( pm );
	GX::GetViewportv( vp );
	NsMatrix * p_mtx = &NxNgc::EngineGlobals.local_to_camera;

	float params[6];
	params[0] = p_mtx->getAtX();
	params[1] = p_mtx->getAtY();
	params[2] = p_mtx->getPosZ();
	params[3] = p_mtx->getAtZ();
	params[4] = pm[1];
	params[5] = vp[2];

	// Construct a packet with data for each stream.
	for( i = 0, p_stream = mp_oldest_stream; i < m_num_streams; i++, p_stream++ )
	{
		Dbg_MsgAssert( p_stream->m_num_particles < 65536, ( "particle limit reached" ));

		// Wrap at end of cyclic buffer.
		if( p_stream == mp_stream + m_max_streams )
		{
			p_stream = mp_stream;
		}

#if 1
		RenderNewParticles( p_stream,
							m_params.m_Lifetime,
							m_params.m_ColorMidpointPct,
							m_params.m_UseMidcolor,
							&m_params.m_Color[0],
							&m_s0,
							&sr,
							&su,
							params,
							256.0f );

#else
		float t				= p_stream->m_oldest_age;
		float midpoint_time = m_params.m_Lifetime * ( m_params.m_ColorMidpointPct * 0.01f );

		// Seed the random number generators for this stream.
//		seed_particle_rnd( p_stream->m_rand_seed, p_stream->m_rand_a, p_stream->m_rand_b );
		uint32 rand_current = p_stream->m_rand_current;

		for( int p = 0; p < p_stream->m_num_particles; ++p )
		{
			// Generate random vector. Each component in the range [1.0, 2.0].
//			Mth::Vector r( 1.0f + ((float)particle_rnd( 16384 ) / 16384 ),
//						   1.0f + ((float)particle_rnd( 16384 ) / 16384 ),
//						   1.0f + ((float)particle_rnd( 16384 ) / 16384 ),
//						   1.0f + ((float)particle_rnd( 16384 ) / 16384 ));

			Mth::Vector r;
			r[X] = 1.0f + ((float)( rand_current = ( ( rand_current & 1 ) ? ( rand_current >> 1 ) ^ 0x3500 : ( rand_current >> 1 ) ) ) / 16384.0f );
			r[Y] = 1.0f + ((float)( rand_current = ( ( rand_current & 1 ) ? ( rand_current >> 1 ) ^ 0x3500 : ( rand_current >> 1 ) ) ) / 16384.0f );
			r[Z] = 1.0f + ((float)( rand_current = ( ( rand_current & 1 ) ? ( rand_current >> 1 ) ^ 0x3500 : ( rand_current >> 1 ) ) ) / 16384.0f );
			r[W] = 1.0f + ((float)( rand_current = ( ( rand_current & 1 ) ? ( rand_current >> 1 ) ^ 0x3500 : ( rand_current >> 1 ) ) ) / 16384.0f );

			float color_interpolator;
			Image::RGBA	col0, col1;

			if( m_params.m_UseMidcolor )
			{
				if( t > midpoint_time )
				{
					color_interpolator	= ( t - midpoint_time ) * ReciprocalEstimate_ASM( m_params.m_Lifetime - midpoint_time );
					col0				= m_params.m_Color[1];
					col1				= m_params.m_Color[2];
				}
				else
				{
					color_interpolator	= t * ReciprocalEstimate_ASM( midpoint_time );
					col0				= m_params.m_Color[0];
					col1				= m_params.m_Color[1];
				}
			}
			else 
			{
				color_interpolator		= t * ReciprocalEstimate_ASM( m_params.m_Lifetime );
				col0					= m_params.m_Color[0];
				col1					= m_params.m_Color[2];
			}


//; Calculate the position of the particle, from:
// pos = ( m_p0 + ( t * m_p1 ) + (( t * t ) * m_p2 )) + ( m_s0 + ( t * m_s1 ) + (( t * t ) * m_s2 )).Scale( r );
			Mth::Vector pos = ( m_p0 + ( t * m_p1 ) + (( t * t ) * m_p2 )) + ( m_s0 + ( t * m_s1 ) + (( t * t ) * m_s2 )).Scale( r );

			GXColor matcol;
			matcol.r = col0.r + (uint8)( ( (float)col1.r - (float)col0.r ) * color_interpolator );
			matcol.g = col0.g + (uint8)( ( (float)col1.g - (float)col0.g ) * color_interpolator );
			matcol.b = col0.b + (uint8)( ( (float)col1.b - (float)col0.b ) * color_interpolator );
			matcol.a = col0.a + (uint8)( ( (float)col1.a - (float)col0.a ) * color_interpolator );

			GX::SetChanMatColor( GX_COLOR0A0, matcol );
			GX::SetChanAmbColor( GX_COLOR0A0, matcol );

//			NsVector sr;
//			sr.x = screen_right.x * pos[W];
//			sr.y = screen_right.y * pos[W];
//			sr.z = screen_right.z * pos[W];
//
//			float sx, sy, sz;
//			float tx, ty, tz;
//			float sc;
//			GX::Project( pos[X], pos[Y], pos[Z], (Mtx)p_mtx, pm, vp, &sx, &sy, &sz );
//			GX::Project( pos[X]+sr.x, pos[Y]+sr.y, pos[Z]+sr.z, (Mtx)p_mtx, pm, vp, &tx, &ty, &tz );
//			sc = tx - sx;	//sqrtf( ( ( tx - sx ) * ( tx - sx ) ) + ( ( ty - sy ) * ( ty - sy ) ) + ( ( tz - sz ) * ( tz - sz ) ) );

			// Calculate size.
//			float x = p_mtx->getRightX()*pos[W];
			float z = p_mtx->getAtX()*pos[X] + p_mtx->getAtY()*pos[Y] + p_mtx->getAtZ()*pos[Z] + p_mtx->getPosZ();
			float xc = pos[W] * pm[1];	// + z * pm[2];
			float wc = ( 1.0f / z );
			float sc = xc * vp[2] * wc;	// + vp[0] + vp[2]/2;

			sc = fabsf( sc );

			int size = (int)(sc * 6.0f);
			if ( size <= 255 )
			{
				GX::SetPointSize( size, GX_TO_ONE );
				GX::Begin( GX_POINTS, GX_VTXFMT0, 1 ); 
				GXWGFifo.f32 = pos[X];
				GXWGFifo.f32 = pos[Y];
				GXWGFifo.f32 = pos[Z];
				GXWGFifo.f32 = 0.0f;
				GXWGFifo.f32 = 0.0f;
				GX::End();
			}
			else
			{
				if ( sc > 256.0f )
				{
					pos[W] = ( pos[W] * 256.0f ) / sc;
				}

				NsVector sr;
				sr.x = screen_right.x * pos[W];
				sr.y = screen_right.y * pos[W];
				sr.z = screen_right.z * pos[W];
				NsVector su;
				su.x = screen_up.x * pos[W];
				su.y = screen_up.y * pos[W];
				su.z = screen_up.z * pos[W];
		
				float v0x = pos[X] - su.x;
				float v0y = pos[Y] - su.y;
				float v0z = pos[Z] - su.z;
	
				float v1x = pos[X] + su.x;
				float v1y = pos[Y] + su.y;
				float v1z = pos[Z] + su.z;

				// Send coordinates.
				GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
					GXWGFifo.f32 = v1x - sr.x;
					GXWGFifo.f32 = v1y - sr.y;
					GXWGFifo.f32 = v1z - sr.z;
					GXWGFifo.f32 = 0.0f;
					GXWGFifo.f32 = 0.0f;

					GXWGFifo.f32 = v1x + sr.x;
					GXWGFifo.f32 = v1y + sr.y;
					GXWGFifo.f32 = v1z + sr.z;
					GXWGFifo.f32 = 1.0f;
					GXWGFifo.f32 = 0.0f;

					GXWGFifo.f32 = v0x + sr.x;
					GXWGFifo.f32 = v0y + sr.y;
					GXWGFifo.f32 = v0z + sr.z;
					GXWGFifo.f32 = 1.0f;
					GXWGFifo.f32 = 1.0f;

					GXWGFifo.f32 = v0x - sr.x;
					GXWGFifo.f32 = v0y - sr.y;
					GXWGFifo.f32 = v0z - sr.z;
					GXWGFifo.f32 = 0.0f;
					GXWGFifo.f32 = 1.0f;
				GX::End();
			}



			// Reduce t by particle interval.
			t -= p_stream->m_interval;
		}
#endif
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcNewParticle::update_position( void )
{
	// Convert 3-point -> PVA format
	float t1 = m_params.m_Lifetime * m_params.m_MidpointPct * 0.01f;
	float t2 = m_params.m_Lifetime;
	Mth::Vector u, a_;

	Mth::Vector x0	= m_params.m_BoxPos[0];
	x0[3]			= m_params.m_Radius[0];
	Mth::Vector x1	= m_params.m_BoxPos[1];
	x1[3]			= m_params.m_Radius[1];
	Mth::Vector x2	= m_params.m_BoxPos[2];
	x2[3]			= m_params.m_Radius[2];

	if( m_params.m_UseMidpoint )
	{
		u  = ( t2 * t2 * ( x1 - x0 ) - t1 * t1 * ( x2 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
		a_ = ( t1 * ( x2 - x0 ) - t2 * ( x1 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
	}
	else
	{
		u  = ( x2 - x0 ) / t2;
		a_.Set( 0, 0, 0, 0 );
	}

	m_p0 = x0 - 1.5f * m_s0;
	m_p1 = u  - 1.5f * m_s1;
	m_p2 = a_ - 1.5f * m_s2;
	m_p0[3] = x0[3] - 1.5f * m_s0[3];
	m_p1[3] = u[3]  - 1.5f * m_s1[3];
	m_p2[3] = a_[3] - 1.5f * m_s2[3];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcNewParticle::plat_update( void )
{

	if (m_params.m_LocalCoord)
	{
		update_position();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcNewParticle::plat_build( void )
{
	// Adjust some of the stream params to improve performance.
	m_params.m_EmitRate	= m_params.m_EmitRate * 0.25f;
	
	// Initialise streams.
	m_max_streams		= 5;
	m_num_streams		= 0;

	mp_stream			= new CParticleStream[m_max_streams]; 
	mp_newest_stream	= mp_stream + m_max_streams - 1;
	mp_oldest_stream	= mp_stream;
	m_emitting			= false;

	// Create a (semi-transparent) material used to render the mesh.
//	mp_material			= new NxNgc::sMaterial;
//	memset( mp_material, 0, sizeof( NxNgc::sMaterial ));

//	mp_material->m_flags[0]		= MATFLAG_TRANSPARENT | MATFLAG_TEXTURED;
//	mp_material->m_passes		= 1;
//	mp_material->m_alpha_cutoff	= 1;
//	mp_material->m_no_bfc		= true;
//	mp_material->m_color[0][0]	= 0.5f;
//	mp_material->m_color[0][1]	= 0.5f;
//	mp_material->m_color[0][2]	= 0.5f;
//	mp_material->m_color[0][3]	= m_params.m_FixedAlpha * ( 1.0f /  128.0f );
//	mp_material->m_reg_alpha[0]	= NxNgc::GetBlendMode( m_params.m_BlendMode );
//
//	// Get texture.
//	Nx::CTexture*		p_texture;
//	Nx::CNgcTexture*	p_Ngc_texture;
//	mp_material->mp_tex[0]	= NULL;
//	p_texture				= Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( m_params.m_Texture );
//	p_Ngc_texture			= static_cast( p_texture );
//	if( p_Ngc_texture )
//	{
//		mp_material->mp_tex[0] = p_Ngc_texture->GetEngineTexture();
//	}

	// Have already set m_Lifetime, which is called m_life in newflat.

	// x-component will be overwritten by vu1 code, so can store midtime there
//	*(float *)&m_systemDmaData.m_tagx = m_params.m_UseMidcolor ?
//										m_params.m_Lifetime * m_params.m_ColorMidpointPct * 0.01f :
//										0.0f;


	// Set Texture & blend mode.
	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( m_params.m_Texture );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_texture = p_Ngc_texture->GetEngineTexture(); 
	}
	m_blend = get_texture_blend( m_params.m_BlendMode ); 
	m_fix = m_params.m_FixedAlpha;


	// and now a load of redundant duplication of data, which should later be removed...

	// Convert 3-point -> PVA format.
	float t1 = m_params.m_Lifetime * m_params.m_MidpointPct * 0.01f;
	float t2 = m_params.m_Lifetime;
	Mth::Vector x0,x1,x2,u,a_;

	x0    = m_params.m_BoxDims[0];
	x0[3] = m_params.m_RadiusSpread[0];
	x1    = m_params.m_BoxDims[1];
	x1[3] = m_params.m_RadiusSpread[1];
	x2    = m_params.m_BoxDims[2];
	x2[3] = m_params.m_RadiusSpread[2];

	if( m_params.m_UseMidpoint )
	{
		u  = ( t2 * t2 * ( x1 - x0 ) - t1 * t1 * ( x2 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
		a_ = ( t1 * ( x2 - x0 ) - t2 * ( x1 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
	}
	else
	{
		u  = ( x2 - x0 ) / t2;
		a_.Set(0,0,0,0);
	}

	m_s0 = x0;
	m_s1 = u;
	m_s2 = a_;

	x0    = m_params.m_BoxPos[0];
	x0[3] = m_params.m_Radius[0];
	x1    = m_params.m_BoxPos[1];
	x1[3] = m_params.m_Radius[1];
	x2    = m_params.m_BoxPos[2];
	x2[3] = m_params.m_Radius[2];

	if( m_params.m_UseMidpoint )
	{
		u  =  ( t2 * t2 * ( x1 - x0 ) - t1 * t1 * ( x2 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
		a_ =  ( t1 * ( x2 - x0 ) - t2 * ( x1 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
	}
	else
	{
		u  = ( x2 - x0 ) / t2;
		a_.Set(0,0,0,0);
	}

	m_p0	= x0 - 1.5f * m_s0;
	m_p1	= u  - 1.5f * m_s1;
	m_p2	= a_ - 1.5f * m_s2;
	m_p0[3]	= x0[3] - 1.5f * m_s0[3];
	m_p1[3]	= u[3]  - 1.5f * m_s1[3];
	m_p2[3]	= a_[3] - 1.5f * m_s2[3];

	update_position();

	// Color.
	if( m_params.m_UseMidcolor )
	{
//		float q0 = 100.0f / ( m_params.m_Lifetime * m_params.m_ColorMidpointPct );
//		float q1 = 100.0f / ( m_params.m_Lifetime * ( 100.0f - m_params.m_ColorMidpointPct ));

//		m_systemDmaData.m_c0[0] = ((float)m_params.m_Color[1].r - (float)m_params.m_Color[0].r) * q0;
//		m_systemDmaData.m_c0[1] = ((float)m_params.m_Color[1].g - (float)m_params.m_Color[0].g) * q0;
//		m_systemDmaData.m_c0[2] = ((float)m_params.m_Color[1].b - (float)m_params.m_Color[0].b) * q0;
//		m_systemDmaData.m_c0[3] = ((float)m_params.m_Color[1].a - (float)m_params.m_Color[0].a) * q0;

//		m_systemDmaData.m_c1[0] = (float)m_params.m_Color[1].r;
//		m_systemDmaData.m_c1[1] = (float)m_params.m_Color[1].g;
//		m_systemDmaData.m_c1[2] = (float)m_params.m_Color[1].b;
//		m_systemDmaData.m_c1[3] = (float)m_params.m_Color[1].a;

//		m_systemDmaData.m_c2[0] = ((float)m_params.m_Color[2].r - (float)m_params.m_Color[1].r) * q1;
//		m_systemDmaData.m_c2[1] = ((float)m_params.m_Color[2].g - (float)m_params.m_Color[1].g) * q1;
//		m_systemDmaData.m_c2[2] = ((float)m_params.m_Color[2].b - (float)m_params.m_Color[1].b) * q1;
//		m_systemDmaData.m_c2[3] = ((float)m_params.m_Color[2].a - (float)m_params.m_Color[1].a) * q1;
	}
	else // else suppress mid-colour
	{
//		float q = 1.0f / m_params.m_Lifetime;

//		m_systemDmaData.m_c1[0] = (float)m_params.m_Color[0].r;
//		m_systemDmaData.m_c1[1] = (float)m_params.m_Color[0].g;
//		m_systemDmaData.m_c1[2] = (float)m_params.m_Color[0].b;
//		m_systemDmaData.m_c1[3] = (float)m_params.m_Color[0].a;

//		m_systemDmaData.m_c2[0] = ((float)m_params.m_Color[2].r - (float)m_params.m_Color[0].r) * q;
//		m_systemDmaData.m_c2[1] = ((float)m_params.m_Color[2].g - (float)m_params.m_Color[0].g) * q;
//		m_systemDmaData.m_c2[2] = ((float)m_params.m_Color[2].b - (float)m_params.m_Color[0].b) * q;
//		m_systemDmaData.m_c2[3] = ((float)m_params.m_Color[2].a - (float)m_params.m_Color[0].a) * q;
	}

	// Rotation matrix.
//	m_rotation = m_params.m_RotMatrix;
	m_rotation.Identity();

	#if 0
	// invert rotation and apply to spatial params
	// leaving this code a bit shoddy and slow until full transition to new-style params
	Mth::Matrix mat;
	mat=m_rotation;
	mat.Transpose();
	Mth::Vector vec;

	vec = m_systemDmaData.m_p0 + 1.5f * m_systemDmaData.m_s0;
	vec *= mat;
	m_systemDmaData.m_p0 = vec - 1.5f * m_systemDmaData.m_s0;

	vec = m_systemDmaData.m_p1 + 1.5f * m_systemDmaData.m_s1;
	vec *= mat;
	m_systemDmaData.m_p1 = vec - 1.5f * m_systemDmaData.m_s1;

	vec = m_systemDmaData.m_p2 + 1.5f * m_systemDmaData.m_s2;
	vec *= mat;
	m_systemDmaData.m_p2 = vec - 1.5f * m_systemDmaData.m_s2;
	#endif
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcNewParticle::plat_destroy( void )
{
	if( mp_stream )
	{
		delete [] mp_stream;
	}

//	if( mp_material )
//	{
//		delete mp_material;
//	}
}



/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx







================================================
FILE: Code/Gfx/NGC/p_nxnewparticle.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_NxNewParticle.h										**
**																			**
**	Created by:		3/24/03	-	SPG											**
**																			**
**	Description:	Ngc implementation of new parametric particle system	**
**																			**
*****************************************************************************/

#ifndef __GFX_Ngc_P_NXNEWPARTICLE_H__
#define __GFX_Ngc_P_NXNEWPARTICLE_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Nx
{

                        
/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CParticleStream
{
public:
	int						m_num_particles;
	float					m_rate;
	float					m_interval;
	float					m_oldest_age;
//	uint32					m_rand_seed;
//	uint32					m_rand_a;
//	uint32					m_rand_b;
	uint32					m_rand_current;
	void					AdvanceSeed( int num_places );
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CNgcNewParticle : public CNewParticle
{
	bool				m_emitting;
	int					m_max_streams;
	int					m_num_streams;
	CParticleStream*	mp_stream;
	CParticleStream*	mp_newest_stream;
	CParticleStream*	mp_oldest_stream;
	Mth::Matrix 		m_rotation;
	Mth::Matrix			m_new_matrix;
//	NxNgc::sMaterial*	mp_material;
	NxNgc::sTexture *	mp_texture;
	uint8				m_blend;
	uint8				m_fix;

	Mth::Vector			m_s0;
	Mth::Vector			m_s1;
	Mth::Vector			m_s2;
	Mth::Vector			m_p0;
	Mth::Vector			m_p1;
	Mth::Vector			m_p2;

protected:
	void	plat_build( void );
	void	plat_destroy( void );
	void	plat_render( void );
	void	plat_update( void );
	void	update_position( void );
};



/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx

#endif	// __GFX_Ngc_P_NXNEWPARTICLE_H__





================================================
FILE: Code/Gfx/NGC/p_nxnewparticlemgr.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_NxNewParticleMgr.cpp									**
**																			**
**	Created by:		3/25/03	-	SPG											**
**																			**
**	Description:	Ngc-specific parametric particle system manager			**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

CNewParticle*	CNgcNewParticleManager::plat_create_particle( void )
{
	return new CNgcNewParticle;
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx






================================================
FILE: Code/Gfx/NGC/p_nxnewparticlemgr.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5 													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_nxnewparticlemgr.h									**
**																			**
**	Created by:		3/24/03	-	SPG											**
**																			**
**	Description:	Ngc-specific new parametric particle system manager		**
**																			**
*****************************************************************************/

#ifndef __GFX_NGC_P_NXNEWPARTICLEMGR_H__
#define __GFX_NGC_P_NXNEWPARTICLEMGR_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Nx
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CNgcNewParticleManager : public CNewParticleManager
{
protected:
	virtual	CNewParticle*	plat_create_particle( void );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx

#endif	// __GFX_NGC_P_NXNEWPARTICLEMGR_H__




================================================
FILE: Code/Gfx/NGC/p_nxparticle.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include 
#include 

#include "gfx/Ngc/nx/mesh.h"
#include "gfx/Ngc/p_nxparticleflat.h"
#include "gfx/Ngc/p_nxparticleshaded.h"
#include "gfx/Ngc/p_nxparticlesmooth.h"
#include "gfx/Ngc/p_nxparticleglow.h"
#include "gfx/Ngc/p_nxparticlestar.h"
#include "gfx/Ngc/p_nxparticlesmoothstar.h"
#include "gfx/Ngc/p_nxparticleline.h"
#include "gfx/Ngc/p_nxparticleribbon.h"
#include "gfx/Ngc/p_nxparticleribbontrail.h"
#include "gfx/Ngc/p_nxparticlesmoothribbon.h"
#include "gfx/Ngc/p_nxparticleglowribbontrail.h"


namespace Nx
{

NxNgc::BlendModes get_texture_blend( uint32 blend_checksum )
{
	NxNgc::BlendModes rv = NxNgc::vBLEND_MODE_DIFFUSE;
	switch ( blend_checksum )
	{
		case 0x54628ed7:		// Blend
			rv = NxNgc::vBLEND_MODE_BLEND;
			break;
		case 0x02e58c18:		// Add
			rv = NxNgc::vBLEND_MODE_ADD;
			break;
		case 0xa7fd7d23:		// Sub
		case 0xdea7e576:		// Subtract
			rv = NxNgc::vBLEND_MODE_SUBTRACT;
			break;
		case 0x40f44b8a:		// Modulate
			rv = NxNgc::vBLEND_MODE_MODULATE;
			break;
		case 0x68e77f40:		// Brighten
			rv = NxNgc::vBLEND_MODE_BRIGHTEN;
			break;
		case 0x18b98905:		// FixBlend
			rv = NxNgc::vBLEND_MODE_BLEND_FIXED;
			break;
		case 0xa86285a1:		// FixAdd
			rv = NxNgc::vBLEND_MODE_ADD_FIXED;
			break;
		case 0x0d7a749a:		// FixSub
		case 0x0eea99ff:		// FixSubtract
			rv = NxNgc::vBLEND_MODE_SUB_FIXED;
			break;
		case 0x90b93703:		// FixModulate
			rv = NxNgc::vBLEND_MODE_MODULATE_FIXED;
			break;
		case 0xb8aa03c9:		// FixBrighten
			rv = NxNgc::vBLEND_MODE_BRIGHTEN_FIXED;
			break;
		case 0x515e298e:		// Diffuse
		case 0x806fff30:		// None
			rv = NxNgc::vBLEND_MODE_DIFFUSE;
			break;
		default:
			Dbg_MsgAssert(0,("Illegal blend mode specified. Please use (fix)blend/add/sub/modulate/brighten or diffuse/none."));
			break;
	}
	return rv;
}

CParticle* plat_create_particle( uint32 checksum, uint32 type_checksum, int max_particles, int max_streams, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	// Types to add:
	// 1. x Flat color quad
	// 2. x Gouraud quad
	// 3. x Quad with center point & outer color
	// 4. x n sided glow (center color + outer color)
	// 5. x n sided 2 layer glow ( center color + mid color + outer color)
	// 6. x n spiked star (center color + mid color + spike color)
	// 7. Lines (current color + previous color)
	// 8. Ribbons - volumetric lines made from quads (current color + previous color)

	switch ( type_checksum )
	{
		case 0xf4d8d486:		// Shaded
			{
				CNgcParticleShaded* p_particle = new CNgcParticleShaded( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x8addac1f:		// Smooth
			{
				CNgcParticleSmooth* p_particle = new CNgcParticleSmooth( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x15834eea:		// Glow
			{
				CNgcParticleGlow* p_particle = new CNgcParticleGlow( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x3624a5eb:		// Star
			{
				CNgcParticleStar* p_particle = new CNgcParticleStar( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x97cb7a9:		// SmoothStar
			{
				CNgcParticleSmoothStar* p_particle = new CNgcParticleSmoothStar( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x2eeb4b09:		// Line
			{
				CNgcParticleLine* p_particle = new CNgcParticleLine( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0xee6fc5b:		// Ribbon
			{
				CNgcParticleRibbon* p_particle = new CNgcParticleRibbon( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0xc4d5a4cb:		// RibbonTrail
			{
				CNgcParticleRibbonTrail* p_particle = new CNgcParticleRibbonTrail( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x3f109fcc:		// SmoothRibbon
			{
				CNgcParticleSmoothRibbon* p_particle = new CNgcParticleSmoothRibbon( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x7ec7252d:		// GlowRibbonTrail
			{
				CNgcParticleGlowRibbonTrail* p_particle = new CNgcParticleGlowRibbonTrail( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0xaab555bb:		// Flat
		default:
			{
				CNgcParticleFlat* p_particle = new CNgcParticleFlat( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
	}
}

} // Nx

				







================================================
FILE: Code/Gfx/NGC/p_nxparticle.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLE_H__
#define	__GFX_P_NX_PARTICLE_H__
    
#include "gfx/nxparticle.h"
#include "gfx/ngc/nx/render.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
NxNgc::BlendModes get_texture_blend( uint32 blend_checksum );
	
} // Nx

#endif 







================================================
FILE: Code/Gfx/NGC/p_nxparticleflat.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleflat.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleFlat::CNgcParticleFlat()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleFlat::CNgcParticleFlat( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	m_start_color.r = 128;
	m_start_color.g = 128;
	m_start_color.b = 128;
	m_start_color.a = 128;
	m_mid_color.r = 128;
	m_mid_color.g = 128;
	m_mid_color.b = 128;
	m_mid_color.a = 128;
	m_end_color.r = 128;
	m_end_color.g = 128;
	m_end_color.b = 128;
	m_end_color.a = 128;

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleFlat::~CNgcParticleFlat()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleFlat::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleFlat::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleFlat::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleFlat::plat_get_num_particle_colors( void ) { return 1; }
int CNgcParticleFlat::plat_get_num_vertex_lists( void ) { return 1; }
void CNgcParticleFlat::plat_set_sr( int entry, uint8 value ) { m_start_color.r = value; }
void CNgcParticleFlat::plat_set_sg( int entry, uint8 value ) { m_start_color.g = value; }
void CNgcParticleFlat::plat_set_sb( int entry, uint8 value ) { m_start_color.b = value; }
void CNgcParticleFlat::plat_set_sa( int entry, uint8 value ) { m_start_color.a = value; }
void CNgcParticleFlat::plat_set_mr( int entry, uint8 value ) { m_mid_color.r = value; }
void CNgcParticleFlat::plat_set_mg( int entry, uint8 value ) { m_mid_color.g = value; }
void CNgcParticleFlat::plat_set_mb( int entry, uint8 value ) { m_mid_color.b = value; }
void CNgcParticleFlat::plat_set_ma( int entry, uint8 value ) { m_mid_color.a = value; }
void CNgcParticleFlat::plat_set_er( int entry, uint8 value ) { m_end_color.r = value; }
void CNgcParticleFlat::plat_set_eg( int entry, uint8 value ) { m_end_color.g = value; }
void CNgcParticleFlat::plat_set_eb( int entry, uint8 value ) { m_end_color.b = value; }
void CNgcParticleFlat::plat_set_ea( int entry, uint8 value ) { m_end_color.a = value; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleFlat::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;

	// Concatenate p_matrix with the emmission angle to create the direction.

	NsVector	up( 0.0f, 1.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	NsVector screen_right;
	NsVector screen_up;
	screen_right.cross( *p_matrix->getAt(), up );
	screen_up.cross( screen_right, *p_matrix->getAt());

	screen_right.normalize();
	screen_up.normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	if ( mp_engine_texture )
	{
		GX::SetVtxDesc( 3, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
	}
	else
	{
		GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );
	}
	if ( m_num_particles > 0 ) GX::Begin( GX_QUADS, GX_VTXFMT0, m_num_particles * 4 );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );
		
		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
	
		VECScale((Vec*)&screen_right, (Vec*)&ss_right, w );
		VECScale((Vec*)&screen_up, (Vec*)&ss_up, h );
	
		tmp[0]		= pos - ss_right + ss_up;
		tmp[1]		= pos + ss_right + ss_up;		
		tmp[2]		= pos + ss_right - ss_up;		
		tmp[3]		= pos - ss_right - ss_up;		
	
		GXColor color;
		GXColor *p_col0;
		GXColor *p_col1;
		
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = &m_start_color;
				p_col1 = &m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = &m_mid_color;
				p_col1 = &m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = &m_start_color;
			p_col1 = &m_end_color;
		}

		GXColor start = *p_col0;
		GXColor end = *p_col1;

		color.r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
		color.g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
		color.b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
		color.a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
	
		GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
		GX::Color1u32( *((uint32*)&color) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 0.0f, 0.0f );

		GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
		GX::Color1u32( *((uint32*)&color) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 1.0f, 0.0f );

		GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
		GX::Color1u32( *((uint32*)&color) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 1.0f, 1.0f );

		GX::Position3f32( tmp[3][X], tmp[3][Y], tmp[3][Z] );
		GX::Color1u32( *((uint32*)&color) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 0.0f, 1.0f );
	}

	if ( m_num_particles > 0 ) GX::End();
}

} // Nx



================================================
FILE: Code/Gfx/NGC/p_nxparticleflat.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLEFLAT_H__
#define	__GFX_P_NX_PARTICLEFLAT_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleFlat : public CParticle
{
public:
							CNgcParticleFlat();
							CNgcParticleFlat( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleFlat();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

	GXColor					m_start_color;				// Start color for each corner.
	GXColor					m_mid_color;				// Mid color for each corner.
	GXColor					m_end_color;				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 







================================================
FILE: Code/Gfx/NGC/p_nxparticleglow.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleGlow.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleGlow::CNgcParticleGlow()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleGlow::CNgcParticleGlow( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	for ( int lp = 0; lp < 3; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;

	m_num_segments = num_segments;
	m_split = split;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleGlow::~CNgcParticleGlow()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleGlow::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleGlow::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleGlow::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleGlow::plat_get_num_particle_colors( void ) { return 3; }
int CNgcParticleGlow::plat_get_num_vertex_lists( void ) { return 1; }
void CNgcParticleGlow::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CNgcParticleGlow::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CNgcParticleGlow::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CNgcParticleGlow::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CNgcParticleGlow::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CNgcParticleGlow::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CNgcParticleGlow::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CNgcParticleGlow::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CNgcParticleGlow::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CNgcParticleGlow::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CNgcParticleGlow::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CNgcParticleGlow::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleGlow::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;

	// Concatenate p_matrix with the emmission angle to create the direction.

	NsVector	up( 0.0f, 1.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	NsVector screen_right;
	NsVector screen_up;
	screen_right.cross( *p_matrix->getAt(), up );
	screen_up.cross( screen_right, *p_matrix->getAt());

	screen_right.normalize();
	screen_up.normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
	
		VECScale((Vec*)&screen_right, (Vec*)&ss_right, w );
		VECScale((Vec*)&screen_up, (Vec*)&ss_up, h );
	
		GXColor color[3];
		GXColor *p_col0;
		GXColor *p_col1;

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 3; c++ )
		{
			GXColor start = *p_col0++;
			GXColor end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		tmp[0]  = pos;
		tmp[0] += ss_right * sinf( Mth::DegToRad( 0.0f ) ) * m_split;
		tmp[0] += ss_up    * cosf( Mth::DegToRad( 0.0f ) ) * m_split;

		tmp[2]  = pos;
		tmp[2] += ss_right * sinf( Mth::DegToRad( 0.0f ) );
		tmp[2] += ss_up    * cosf( Mth::DegToRad( 0.0f ) );

		for ( int lp2 = 0; lp2 < m_num_segments; lp2++ )
		{
			tmp[1]  = pos;
			tmp[1] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;
			tmp[1] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;

			tmp[3]  = pos;
			tmp[3] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) );
			tmp[3] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) );

			GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 5 );

			GX::Position3f32( pos[X], pos[Y], pos[Z] );
			GX::Color1u32( *((uint32*)&color[0]) );

			GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
			GX::Color1u32( *((uint32*)&color[2]) );

			GX::Position3f32( tmp[3][X], tmp[3][Y], tmp[3][Z] );
			GX::Color1u32( *((uint32*)&color[2]) );

			GX::End();

			tmp[0] = tmp[1];
			tmp[2] = tmp[3];
		}
	}
}

} // Nx





================================================
FILE: Code/Gfx/NGC/p_nxparticleglow.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLEGlow_H__
#define	__GFX_P_NX_PARTICLEGlow_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleGlow : public CParticle
{
public:
							CNgcParticleGlow();
							CNgcParticleGlow( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleGlow();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

//	NxNgc::sParticleSystem*	mp_engine_particle;
	int						m_num_segments;
	float					m_split;
	GXColor					m_start_color[3];			// Start color for each corner.
	GXColor					m_mid_color[3];				// Mid color for each corner.
	GXColor					m_end_color[3];				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/NGC/p_nxparticleglowribbontrail.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleGlowRibbonTrail.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleGlowRibbonTrail::CNgcParticleGlowRibbonTrail()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleGlowRibbonTrail::CNgcParticleGlowRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	m_start_color = new GXColor[m_num_vertex_buffers+3];
	m_mid_color = new GXColor[m_num_vertex_buffers+3];
	m_end_color = new GXColor[m_num_vertex_buffers+3];
	for ( int lp = 0; lp < (m_num_vertex_buffers+3); lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;

	m_num_segments = num_segments;
	m_split = split;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleGlowRibbonTrail::~CNgcParticleGlowRibbonTrail()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
	delete [] m_start_color;
	delete [] m_mid_color;
	delete [] m_end_color;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleGlowRibbonTrail::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleGlowRibbonTrail::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleGlowRibbonTrail::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleGlowRibbonTrail::plat_get_num_particle_colors( void ) { return m_num_vertex_buffers + 3; }
int CNgcParticleGlowRibbonTrail::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CNgcParticleGlowRibbonTrail::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CNgcParticleGlowRibbonTrail::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CNgcParticleGlowRibbonTrail::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CNgcParticleGlowRibbonTrail::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CNgcParticleGlowRibbonTrail::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CNgcParticleGlowRibbonTrail::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CNgcParticleGlowRibbonTrail::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CNgcParticleGlowRibbonTrail::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CNgcParticleGlowRibbonTrail::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CNgcParticleGlowRibbonTrail::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CNgcParticleGlowRibbonTrail::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CNgcParticleGlowRibbonTrail::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleGlowRibbonTrail::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the ribbontrail.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;

	// Concatenate p_matrix with the emmission angle to create the direction.
	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	Mth::Vector at( p_matrix->getAtX(), p_matrix->getAtY(), p_matrix->getAtZ(), 0.0f );
	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

	screen_right.Normalize();
	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	GXColor color[3];
	GXColor *p_col0;
	GXColor *p_col1;

	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );

	for ( lp = 0, p_particle = mp_particle_array; lp < m_num_particles; lp++, p_particle++ )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		Mth::Vector	pos[2];
		p_v = &mp_vertices[0][lp*3];
		pos[0].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		p_v = &mp_vertices[1][lp*3];
		pos[1].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );

		Mth::Vector	part_vec = pos[1] - pos[0];
		Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
		perp_vec.Normalize();

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );
		
		Mth::Vector tmp[4];

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = &m_start_color[3];
				p_col1 = &m_mid_color[3];
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = &m_mid_color[3];
				p_col1 = &m_end_color[3];
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = &m_start_color[3];
			p_col1 = &m_end_color[3];
		}

		GXColor start = *p_col0++;
		GXColor end = *p_col1++;

		color[0].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
		color[0].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
		color[0].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
		color[0].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));

		tmp[0]		= pos[0] + ( perp_vec * w * m_split );
		tmp[1]		= pos[0] - ( perp_vec * w * m_split );

		GX::Begin( GX_QUADS, GX_VTXFMT0, 4 * ( m_num_vertex_buffers - 1 ) );

		for ( int c = 1; c < m_num_vertex_buffers; c++ )
		{
			start = *p_col0++;
			end = *p_col1++;

			color[1].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[1].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[1].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[1].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));

			if ( c > 1 )
			{
				p_v = &mp_vertices[c][lp*3];
				pos[1].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				part_vec = pos[1] - pos[0];
				perp_vec = Mth::CrossProduct( part_vec, at );
				perp_vec.Normalize();
			}

			tmp[2]		= pos[1] - ( perp_vec * w * m_split );
			tmp[3]		= pos[1] + ( perp_vec * w * m_split );

			GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
			GX::Color1u32( *((uint32*)&color[0]) );

			GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
			GX::Color1u32( *((uint32*)&color[0]) );

			GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			GX::Position3f32( tmp[3][X], tmp[3][Y], tmp[3][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			color[0] = color[1];
			pos[0] = pos[1];
			tmp[0] = tmp[3];
			tmp[1] = tmp[2];
		}

		GX::End();

		// Draw the glow.

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		p_v = &mp_vertices[0][lp*3];
		pos[0].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
	
		ss_right	= screen_right * w;
		ss_up		= screen_up * h;
	
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 3; c++ )
		{
			GXColor start = *p_col0++;
			GXColor end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		tmp[0]  = pos[0];
		tmp[0] += ss_right * sinf( Mth::DegToRad( 0.0f ) ) * m_split;
		tmp[0] += ss_up    * cosf( Mth::DegToRad( 0.0f ) ) * m_split;

		tmp[2]  = pos[0];
		tmp[2] += ss_right * sinf( Mth::DegToRad( 0.0f ) );
		tmp[2] += ss_up    * cosf( Mth::DegToRad( 0.0f ) );

		for ( int lp2 = 0; lp2 < m_num_segments; lp2++ )
		{
			tmp[1]  = pos[0];
			tmp[1] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;
			tmp[1] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;

			tmp[3]  = pos[0];
			tmp[3] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) );
			tmp[3] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) );

			GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 5 );

			GX::Position3f32( pos[0][X], pos[0][Y], pos[0][Z] );
			GX::Color1u32( *((uint32*)&color[0]) );

			GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
			GX::Color1u32( *((uint32*)&color[2]) );

			GX::Position3f32( tmp[3][X], tmp[3][Y], tmp[3][Z] );
			GX::Color1u32( *((uint32*)&color[2]) );

			GX::End();

			tmp[0] = tmp[1];
			tmp[2] = tmp[3];
		}
	}
}

} // Nx




================================================
FILE: Code/Gfx/NGC/p_nxparticleglowribbontrail.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLEGlowRibbonTrail_H__
#define	__GFX_P_NX_PARTICLEGlowRibbonTrail_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleGlowRibbonTrail : public CParticle
{
public:
							CNgcParticleGlowRibbonTrail();
							CNgcParticleGlowRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleGlowRibbonTrail();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

	int						m_num_segments;
	float					m_split;
	GXColor*				m_start_color;				// Start color for each corner.
	GXColor*				m_mid_color;				// Mid color for each corner.
	GXColor*				m_end_color;				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGC/p_nxparticleline.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleLine.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleLine::CNgcParticleLine()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleLine::CNgcParticleLine( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffers.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	m_start_color = new GXColor[m_num_vertex_buffers];
	m_mid_color = new GXColor[m_num_vertex_buffers];
	m_end_color = new GXColor[m_num_vertex_buffers];
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleLine::~CNgcParticleLine()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleLine::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleLine::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleLine::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleLine::plat_get_num_particle_colors( void ) { return 2; }
int CNgcParticleLine::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CNgcParticleLine::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CNgcParticleLine::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CNgcParticleLine::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CNgcParticleLine::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CNgcParticleLine::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CNgcParticleLine::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CNgcParticleLine::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CNgcParticleLine::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CNgcParticleLine::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CNgcParticleLine::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CNgcParticleLine::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CNgcParticleLine::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleLine::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the particles.
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v0;
	float			*p_v1;

	GXColor color[2];
	GXColor *p_col0;
	GXColor *p_col1;

	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );

	if ( m_num_particles > 0 ) GX::Begin( GX_LINES, GX_VTXFMT0, m_num_particles * 2 );
	for ( lp = 0, p_particle = mp_particle_array, p_v0 = mp_vertices[0], p_v1 = mp_vertices[(m_num_vertex_buffers-1)]; lp < m_num_particles; lp++, p_particle++, p_v0 += 3, p_v1 += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 2; c++ )
		{
			GXColor start = *p_col0++;
			GXColor end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos0( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
		Mth::Vector	pos1( p_v1[0] + m_pos[X], p_v1[1] + m_pos[Y], p_v1[2] + m_pos[Z] );
	
		GX::Position3f32( pos0[X], pos0[Y], pos0[Z] );
		GX::Color1u32( *((uint32*)&color[0]) );
	
		GX::Position3f32( pos1[X], pos1[Y], pos1[Z] );
		GX::Color1u32( *((uint32*)&color[1]) );
	}
	if ( m_num_particles > 0 ) GX::End();
}

} // Nx




================================================
FILE: Code/Gfx/NGC/p_nxparticleline.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLELine_H__
#define	__GFX_P_NX_PARTICLELine_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleLine : public CParticle
{
public:
							CNgcParticleLine();
							CNgcParticleLine( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleLine();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

	GXColor*				m_start_color;				// Start color for each corner.
	GXColor*				m_mid_color;				// Mid color for each corner.
	GXColor*				m_end_color;				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGC/p_nxparticleribbon.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleRibbon.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleRibbon::CNgcParticleRibbon()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleRibbon::CNgcParticleRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	for ( int lp = 0; lp < 2; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleRibbon::~CNgcParticleRibbon()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleRibbon::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleRibbon::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleRibbon::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleRibbon::plat_get_num_particle_colors( void ) { return 2; }
int CNgcParticleRibbon::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CNgcParticleRibbon::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CNgcParticleRibbon::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CNgcParticleRibbon::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CNgcParticleRibbon::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CNgcParticleRibbon::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CNgcParticleRibbon::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CNgcParticleRibbon::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CNgcParticleRibbon::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CNgcParticleRibbon::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CNgcParticleRibbon::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CNgcParticleRibbon::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CNgcParticleRibbon::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleRibbon::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the particles.
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v0;
	float			*p_v1;

	GXColor color[2];
	GXColor *p_col0;
	GXColor *p_col1;

	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;
	Mth::Vector at( p_matrix->getAtX(), p_matrix->getAtY(), p_matrix->getAtZ(), 0.0f );

	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );

	if ( m_num_particles > 0 ) GX::Begin( GX_QUADS, GX_VTXFMT0, m_num_particles * 4 );
	
	for ( lp = 0, p_particle = mp_particle_array, p_v0 = mp_vertices[0], p_v1 = mp_vertices[(m_num_vertex_buffers-1)]; lp < m_num_particles; lp++, p_particle++, p_v0 += 3, p_v1 += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;
		
		Mth::Vector	pos0( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
		Mth::Vector	pos1( p_v1[0] + m_pos[X], p_v1[1] + m_pos[Y], p_v1[2] + m_pos[Z] );

		Mth::Vector	part_vec = pos1 - pos0;
		Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
		perp_vec.Normalize();

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector tmp[4];
	
		tmp[0]		= pos0 + ( perp_vec * w );
		tmp[1]		= pos0 - ( perp_vec * w );
		tmp[2]		= pos1 - ( perp_vec * w );
		tmp[3]		= pos1 + ( perp_vec * w );
	
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 2; c++ )
		{
			GXColor start = *p_col0++;
			GXColor end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
		GX::Color1u32( *((uint32*)&color[0]) );

		GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
		GX::Color1u32( *((uint32*)&color[0]) );

		GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
		GX::Color1u32( *((uint32*)&color[1]) );

		GX::Position3f32( tmp[3][X], tmp[3][Y], tmp[3][Z] );
		GX::Color1u32( *((uint32*)&color[1]) );
	}
	
	if ( m_num_particles > 0 ) GX::End();
}

} // Nx




================================================
FILE: Code/Gfx/NGC/p_nxparticleribbon.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLERibbon_H__
#define	__GFX_P_NX_PARTICLERibbon_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleRibbon : public CParticle
{
public:
							CNgcParticleRibbon();
							CNgcParticleRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleRibbon();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

	GXColor					m_start_color[2];			// Start color for each corner.
	GXColor					m_mid_color[2];				// Mid color for each corner.
	GXColor					m_end_color[2];				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGC/p_nxparticleribbontrail.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleRibbonTrail.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleRibbonTrail::CNgcParticleRibbonTrail()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleRibbonTrail::CNgcParticleRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	m_start_color = new GXColor[m_num_vertex_buffers];
	m_mid_color = new GXColor[m_num_vertex_buffers];
	m_end_color = new GXColor[m_num_vertex_buffers];
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleRibbonTrail::~CNgcParticleRibbonTrail()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
	delete [] m_start_color;
	delete [] m_mid_color;
	delete [] m_end_color;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleRibbonTrail::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleRibbonTrail::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleRibbonTrail::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleRibbonTrail::plat_get_num_particle_colors( void ) { return m_num_vertex_buffers; }
int CNgcParticleRibbonTrail::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CNgcParticleRibbonTrail::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CNgcParticleRibbonTrail::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CNgcParticleRibbonTrail::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CNgcParticleRibbonTrail::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CNgcParticleRibbonTrail::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CNgcParticleRibbonTrail::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CNgcParticleRibbonTrail::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CNgcParticleRibbonTrail::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CNgcParticleRibbonTrail::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CNgcParticleRibbonTrail::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CNgcParticleRibbonTrail::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CNgcParticleRibbonTrail::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleRibbonTrail::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the particles.
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	GXColor color[2];
	GXColor *p_col0;
	GXColor *p_col1;

	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;
	Mth::Vector at( p_matrix->getAtX(), p_matrix->getAtY(), p_matrix->getAtZ(), 0.0f );

	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );

	for ( lp = 0, p_particle = mp_particle_array; lp < m_num_particles; lp++, p_particle++ )
	{
		float terp = p_particle->m_time / p_particle->m_life;
		
		Mth::Vector	pos[2];
		p_v = &mp_vertices[0][lp*3];
		pos[0].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		p_v = &mp_vertices[1][lp*3];
		pos[1].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );

		Mth::Vector	part_vec = pos[1] - pos[0];
		Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
		perp_vec.Normalize();

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector tmp[4];
	
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		GXColor start = *p_col0++;
		GXColor end = *p_col1++;

		color[0].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
		color[0].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
		color[0].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
		color[0].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));

		tmp[0]		= pos[0] + ( perp_vec * w );
		tmp[1]		= pos[0] - ( perp_vec * w );

		GX::Begin( GX_QUADS, GX_VTXFMT0, 4 * ( m_num_vertex_buffers - 1 ) );

		for ( int c = 1; c < m_num_vertex_buffers; c++ )
		{
			start = *p_col0++;
			end = *p_col1++;

			color[1].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[1].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[1].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[1].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));

			if ( c > 1 )
			{
				p_v = &mp_vertices[c][lp*3];
				pos[1].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				part_vec = pos[1] - pos[0];
				perp_vec = Mth::CrossProduct( part_vec, at );
				perp_vec.Normalize();
			}

			tmp[2]		= pos[1] - ( perp_vec * w );
			tmp[3]		= pos[1] + ( perp_vec * w );

			GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
			GX::Color1u32( *((uint32*)&color[0]) );

			GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
			GX::Color1u32( *((uint32*)&color[0]) );

			GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			GX::Position3f32( tmp[3][X], tmp[3][Y], tmp[3][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			color[0] = color[1];
			pos[0] = pos[1];
			tmp[0] = tmp[3];
			tmp[1] = tmp[2];
		}
		GX::End();
	}
}

} // Nx




================================================
FILE: Code/Gfx/NGC/p_nxparticleribbontrail.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLERibbonTrail_H__
#define	__GFX_P_NX_PARTICLERibbonTrail_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleRibbonTrail : public CParticle
{
public:
							CNgcParticleRibbonTrail();
							CNgcParticleRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleRibbonTrail();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

	GXColor*				m_start_color;				// Start color for each corner.
	GXColor*				m_mid_color;				// Mid color for each corner.
	GXColor*				m_end_color;				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGC/p_nxparticleshaded.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleShaded.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleShaded::CNgcParticleShaded()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleShaded::CNgcParticleShaded( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	for ( int lp = 0; lp < 4; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleShaded::~CNgcParticleShaded()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleShaded::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleShaded::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleShaded::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleShaded::plat_get_num_particle_colors( void ) { return 4; }
int CNgcParticleShaded::plat_get_num_vertex_lists( void ) { return 1; }
void CNgcParticleShaded::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CNgcParticleShaded::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CNgcParticleShaded::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CNgcParticleShaded::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CNgcParticleShaded::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CNgcParticleShaded::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CNgcParticleShaded::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CNgcParticleShaded::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CNgcParticleShaded::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CNgcParticleShaded::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CNgcParticleShaded::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CNgcParticleShaded::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleShaded::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;

	// Concatenate p_matrix with the emmission angle to create the direction.

	NsVector	up( 0.0f, 1.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	NsVector screen_right;
	NsVector screen_up;
	screen_right.cross( *p_matrix->getAt(), up );
	screen_up.cross( screen_right, *p_matrix->getAt());

	screen_right.normalize();
	screen_up.normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	if ( mp_engine_texture )
	{
		GX::SetVtxDesc( 3, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
	}
	else
	{
		GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );
	}
	if ( m_num_particles > 0 ) GX::Begin( GX_QUADS, GX_VTXFMT0, m_num_particles * 4 );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );
		
		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
	
		VECScale((Vec*)&screen_right, (Vec*)&ss_right, w );
		VECScale((Vec*)&screen_up, (Vec*)&ss_up, h );
	
		tmp[0]		= pos - ss_right + ss_up;
		tmp[1]		= pos + ss_right + ss_up;		
		tmp[2]		= pos + ss_right - ss_up;		
		tmp[3]		= pos - ss_right - ss_up;		
	
		GXColor color[4];
		GXColor *p_col0;
		GXColor *p_col1;
		
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 4; c++ )
		{
			GXColor start = *p_col0++;
			GXColor end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}
	
		GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
		GX::Color1u32( *((uint32*)&color[0]) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 0.0f, 0.0f );

		GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
		GX::Color1u32( *((uint32*)&color[1]) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 1.0f, 0.0f );

		GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
		GX::Color1u32( *((uint32*)&color[2]) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 1.0f, 1.0f );

		GX::Position3f32( tmp[3][X], tmp[3][Y], tmp[3][Z] );
		GX::Color1u32( *((uint32*)&color[3]) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 0.0f, 1.0f );
	}

	if ( m_num_particles > 0 ) GX::End();
}

} // Nx




================================================
FILE: Code/Gfx/NGC/p_nxparticleshaded.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLEShaded_H__
#define	__GFX_P_NX_PARTICLEShaded_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleShaded : public CParticle
{
public:
							CNgcParticleShaded();
							CNgcParticleShaded( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleShaded();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

	GXColor					m_start_color[4];			// Start color for each corner.
	GXColor					m_mid_color[4];				// Mid color for each corner.
	GXColor					m_end_color[4];				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGC/p_nxparticlesmooth.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleSmooth.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleSmooth::CNgcParticleSmooth()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleSmooth::CNgcParticleSmooth( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	for ( int lp = 0; lp < 2; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleSmooth::~CNgcParticleSmooth()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmooth::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmooth::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmooth::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleSmooth::plat_get_num_particle_colors( void ) { return 2; }
int CNgcParticleSmooth::plat_get_num_vertex_lists( void ) { return 1; }
void CNgcParticleSmooth::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CNgcParticleSmooth::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CNgcParticleSmooth::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CNgcParticleSmooth::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CNgcParticleSmooth::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CNgcParticleSmooth::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CNgcParticleSmooth::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CNgcParticleSmooth::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CNgcParticleSmooth::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CNgcParticleSmooth::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CNgcParticleSmooth::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CNgcParticleSmooth::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmooth::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;

	// Concatenate p_matrix with the emmission angle to create the direction.

	NsVector	up( 0.0f, 1.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	NsVector screen_right;
	NsVector screen_up;
	screen_right.cross( *p_matrix->getAt(), up );
	screen_up.cross( screen_right, *p_matrix->getAt());

	screen_right.normalize();
	screen_up.normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	if ( mp_engine_texture )
	{
		GX::SetVtxDesc( 3, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );
	}
	else
	{
		GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );
	}

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );
		
		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
	
		VECScale((Vec*)&screen_right, (Vec*)&ss_right, w );
		VECScale((Vec*)&screen_up, (Vec*)&ss_up, h );
	
		tmp[0]		= pos - ss_right + ss_up;
		tmp[1]		= pos + ss_right + ss_up;		
		tmp[2]		= pos + ss_right - ss_up;		
		tmp[3]		= pos - ss_right - ss_up;		
	
		GXColor color[2];
		GXColor *p_col0;
		GXColor *p_col1;
		
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 2; c++ )
		{
			GXColor start = *p_col0++;
			GXColor end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}
		GX::Begin( GX_TRIANGLEFAN, GX_VTXFMT0, 6 );
	
		GX::Position3f32( pos[X], pos[Y], pos[Z] );
		GX::Color1u32( *((uint32*)&color[0]) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 0.5f, 0.5f );

		GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
		GX::Color1u32( *((uint32*)&color[1]) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 0.0f, 0.0f );

		GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
		GX::Color1u32( *((uint32*)&color[1]) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 1.0f, 0.0f );

		GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
		GX::Color1u32( *((uint32*)&color[1]) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 1.0f, 1.0f );

		GX::Position3f32( tmp[3][X], tmp[3][Y], tmp[3][Z] );
		GX::Color1u32( *((uint32*)&color[1]) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 0.0f, 1.0f );

		GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
		GX::Color1u32( *((uint32*)&color[1]) );
		if ( mp_engine_texture ) GX::TexCoord2f32( 0.0f, 0.0f );

		GX::End();
	}
}

} // Nx





================================================
FILE: Code/Gfx/NGC/p_nxparticlesmooth.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLESmooth_H__
#define	__GFX_P_NX_PARTICLESmooth_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleSmooth : public CParticle
{
public:
							CNgcParticleSmooth();
							CNgcParticleSmooth( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleSmooth();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

	GXColor					m_start_color[2];			// Start color for each corner.
	GXColor					m_mid_color[2];				// Mid color for each corner.
	GXColor					m_end_color[2];				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/NGC/p_nxparticlesmoothribbon.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleSmoothRibbon.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleSmoothRibbon::CNgcParticleSmoothRibbon()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleSmoothRibbon::CNgcParticleSmoothRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	for ( int lp = 0; lp < 4; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleSmoothRibbon::~CNgcParticleSmoothRibbon()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmoothRibbon::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmoothRibbon::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmoothRibbon::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleSmoothRibbon::plat_get_num_particle_colors( void ) { return 4; }
int CNgcParticleSmoothRibbon::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CNgcParticleSmoothRibbon::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CNgcParticleSmoothRibbon::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CNgcParticleSmoothRibbon::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CNgcParticleSmoothRibbon::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CNgcParticleSmoothRibbon::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CNgcParticleSmoothRibbon::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CNgcParticleSmoothRibbon::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CNgcParticleSmoothRibbon::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CNgcParticleSmoothRibbon::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CNgcParticleSmoothRibbon::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CNgcParticleSmoothRibbon::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CNgcParticleSmoothRibbon::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmoothRibbon::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the particles.
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v0;
	float			*p_v1;

	GXColor color[4];
	GXColor *p_col0;
	GXColor *p_col1;

	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;
	Mth::Vector at( p_matrix->getAtX(), p_matrix->getAtY(), p_matrix->getAtZ(), 0.0f );

	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );
	
	for ( lp = 0, p_particle = mp_particle_array, p_v0 = mp_vertices[0], p_v1 = mp_vertices[(m_num_vertex_buffers-1)]; lp < m_num_particles; lp++, p_particle++, p_v0 += 3, p_v1 += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;
		
		Mth::Vector	pos0( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
		Mth::Vector	pos1( p_v1[0] + m_pos[X], p_v1[1] + m_pos[Y], p_v1[2] + m_pos[Z] );

		Mth::Vector	part_vec = pos1 - pos0;
		Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
		perp_vec.Normalize();

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector tmp[4];
	
		tmp[0]		= pos0 + ( perp_vec * w );
		tmp[1]		= pos0 - ( perp_vec * w );
		tmp[2]		= pos1 - ( perp_vec * w );
		tmp[3]		= pos1 + ( perp_vec * w );
	
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 4; c++ )
		{
			GXColor start = *p_col0++;
			GXColor end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 6 );

		GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
		GX::Color1u32( *((uint32*)&color[1]) );

		GX::Position3f32( tmp[3][X], tmp[3][Y], tmp[3][Z] );
		GX::Color1u32( *((uint32*)&color[2]) );

		GX::Position3f32( pos0[X], pos0[Y], pos0[Z] );
		GX::Color1u32( *((uint32*)&color[0]) );

		GX::Position3f32( pos1[X], pos1[Y], pos1[Z] );
		GX::Color1u32( *((uint32*)&color[3]) );
		
		GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
		GX::Color1u32( *((uint32*)&color[1]) );

		GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
		GX::Color1u32( *((uint32*)&color[2]) );

		GX::End();
	}
}

} // Nx




================================================
FILE: Code/Gfx/NGC/p_nxparticlesmoothribbon.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLESmoothRibbon_H__
#define	__GFX_P_NX_PARTICLESmoothRibbon_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleSmoothRibbon : public CParticle
{
public:
							CNgcParticleSmoothRibbon();
							CNgcParticleSmoothRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleSmoothRibbon();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

	GXColor					m_start_color[4];			// Start color for each corner.
	GXColor					m_mid_color[4];				// Mid color for each corner.
	GXColor					m_end_color[4];				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGC/p_nxparticlesmoothstar.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleSmoothStar.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleSmoothStar::CNgcParticleSmoothStar()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleSmoothStar::CNgcParticleSmoothStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	for ( int lp = 0; lp < 3; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;

	m_num_segments = num_segments;
	m_split = split;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleSmoothStar::~CNgcParticleSmoothStar()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmoothStar::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmoothStar::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmoothStar::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleSmoothStar::plat_get_num_particle_colors( void ) { return 3; }
int CNgcParticleSmoothStar::plat_get_num_vertex_lists( void ) { return 1; }
void CNgcParticleSmoothStar::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CNgcParticleSmoothStar::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CNgcParticleSmoothStar::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CNgcParticleSmoothStar::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CNgcParticleSmoothStar::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CNgcParticleSmoothStar::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CNgcParticleSmoothStar::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CNgcParticleSmoothStar::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CNgcParticleSmoothStar::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CNgcParticleSmoothStar::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CNgcParticleSmoothStar::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CNgcParticleSmoothStar::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleSmoothStar::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;

	// Concatenate p_matrix with the emmission angle to create the direction.

	NsVector	up( 0.0f, 1.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	NsVector screen_right;
	NsVector screen_up;
	screen_right.cross( *p_matrix->getAt(), up );
	screen_up.cross( screen_right, *p_matrix->getAt());

	screen_right.normalize();
	screen_up.normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
	
		VECScale((Vec*)&screen_right, (Vec*)&ss_right, w );
		VECScale((Vec*)&screen_up, (Vec*)&ss_up, h );
	
		GXColor color[3];
		GXColor *p_col0;
		GXColor *p_col1;

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 3; c++ )
		{
			GXColor start = *p_col0++;
			GXColor end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		tmp[1]  = pos;
		tmp[1] += ss_right * sinf( Mth::DegToRad( 0.0f ) ) * m_split;
		tmp[1] += ss_up    * cosf( Mth::DegToRad( 0.0f ) ) * m_split;

		for ( int lp2 = 0; lp2 < m_num_segments; lp2++ )
		{
			tmp[0]  = pos;
			tmp[0] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) ) * m_split;
			tmp[0] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) ) * m_split;
			
			tmp[3]  = pos;
			tmp[3] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 2 ) ) ) ) * m_split;
			tmp[3] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 2 ) ) ) ) * m_split;
			
			tmp[2]  = pos;
			tmp[2] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );
			tmp[2] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );

			GX::Begin( GX_TRIANGLEFAN, GX_VTXFMT0, 6 );

			GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
			GX::Color1u32( *((uint32*)&color[2]) );

			GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
			GX::Color1u32( *((uint32*)&color[2]) );

			GX::Position3f32( tmp[3][X], tmp[3][Y], tmp[3][Z] );
			GX::Color1u32( *((uint32*)&color[2]) );

			GX::Position3f32( pos[X], pos[Y], pos[Z] );
			GX::Color1u32( *((uint32*)&color[0]) );

			GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
			GX::Color1u32( *((uint32*)&color[2]) );

			GX::End();

			tmp[1] = tmp[3];
		}
	}
}

} // Nx





================================================
FILE: Code/Gfx/NGC/p_nxparticlesmoothstar.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLESmoothStar_H__
#define	__GFX_P_NX_PARTICLESmoothStar_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleSmoothStar : public CParticle
{
public:
							CNgcParticleSmoothStar();
							CNgcParticleSmoothStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleSmoothStar();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

	int						m_num_segments;
	float					m_split;
	GXColor					m_start_color[3];			// SmoothStart color for each corner.
	GXColor					m_mid_color[3];				// Mid color for each corner.
	GXColor					m_end_color[3];				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/NGC/p_nxparticlestar.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/Ngc/nx/render.h"
#include 
#include 
#include "gfx/Ngc/nx/mesh.h"
#include "gfx/ngc/nx/nx_init.h"
#include "sys/ngc/p_gx.h"

#include "gfx/Ngc/p_nxparticleStar.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleStar::CNgcParticleStar()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleStar::CNgcParticleStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxNgc::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_engine_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set blendmode.
	m_blend = (uint8)get_texture_blend( blendmode_checksum );
	m_fix = fix;

	// Default color.
	for ( int lp = 0; lp < 3; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;

	m_num_segments = num_segments;
	m_split = split;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcParticleStar::~CNgcParticleStar()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleStar::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleStar::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleStar::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CNgcParticleStar::plat_get_num_particle_colors( void ) { return 3; }
int CNgcParticleStar::plat_get_num_vertex_lists( void ) { return 1; }
void CNgcParticleStar::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CNgcParticleStar::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CNgcParticleStar::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CNgcParticleStar::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CNgcParticleStar::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CNgcParticleStar::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CNgcParticleStar::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CNgcParticleStar::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CNgcParticleStar::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CNgcParticleStar::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CNgcParticleStar::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CNgcParticleStar::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcParticleStar::plat_render( void )
{
	NxNgc::sMaterialHeader		mat;
	NxNgc::sMaterialPassHeader	pass;

	// Header.
	mat.m_checksum			= 0xa9db601e;   // particle 
	mat.m_passes			= 1;
	mat.m_alpha_cutoff		= 1;
	mat.m_flags				= (1<<1);		// 2 sided.
//	mat.m_shininess			= 0.0f;

	// Pass 0.
	pass.m_texture.p_data	= mp_engine_texture;
	pass.m_flags			= ( mp_engine_texture ? (1<<0) : 0 ) | (1<<5) | (1<<6);		// textured, clamped.
	pass.m_filter			= 0;
	pass.m_blend_mode		= (unsigned char)m_blend;
	pass.m_alpha_fix		= (unsigned char)m_fix; 
	pass.m_k				= 0;
	pass.m_color.r			= 128;
	pass.m_color.g			= 128;
	pass.m_color.b			= 128;
	pass.m_color.a			= 255;

	NxNgc::multi_mesh( &mat, &pass, true, true );

	GX::SetTexCoordGen( GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_FALSE, GX_PTIDENTITY );
	GX::SetCurrMtxPosTex03( GX_PNMTX0, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY, GX_IDENTITY );

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;

	// Concatenate p_matrix with the emmission angle to create the direction.

	NsVector	up( 0.0f, 1.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	NsVector screen_right;
	NsVector screen_up;
	screen_right.cross( *p_matrix->getAt(), up );
	screen_up.cross( screen_right, *p_matrix->getAt());

	screen_right.normalize();
	screen_up.normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[3];
	
		VECScale((Vec*)&screen_right, (Vec*)&ss_right, w );
		VECScale((Vec*)&screen_up, (Vec*)&ss_up, h );
	
		GXColor color[3];
		GXColor *p_col0;
		GXColor *p_col1;

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 3; c++ )
		{
			GXColor start = *p_col0++;
			GXColor end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		tmp[0]  = pos;
		tmp[0] += ss_right * sinf( Mth::DegToRad( 0.0f ) ) * m_split;
		tmp[0] += ss_up    * cosf( Mth::DegToRad( 0.0f ) ) * m_split;

		for ( int lp2 = 0; lp2 < m_num_segments; lp2++ )
		{
			tmp[1]  = pos;
			tmp[1] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;
			tmp[1] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;

			tmp[2]  = pos;
			tmp[2] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );
			tmp[2] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );

			GX::Begin( GX_TRIANGLESTRIP, GX_VTXFMT0, 4 );

			GX::Position3f32( pos[X], pos[Y], pos[Z] );
			GX::Color1u32( *((uint32*)&color[0]) );

			GX::Position3f32( tmp[0][X], tmp[0][Y], tmp[0][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			GX::Position3f32( tmp[1][X], tmp[1][Y], tmp[1][Z] );
			GX::Color1u32( *((uint32*)&color[1]) );

			GX::Position3f32( tmp[2][X], tmp[2][Y], tmp[2][Z] );
			GX::Color1u32( *((uint32*)&color[2]) );

			GX::End();

			tmp[0] = tmp[1];
		}
	}
}

} // Nx





================================================
FILE: Code/Gfx/NGC/p_nxparticlestar.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLEStar_H__
#define	__GFX_P_NX_PARTICLEStar_H__
    
#include "gfx/Ngc/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CNgcParticleStar : public CParticle
{
public:
							CNgcParticleStar();
							CNgcParticleStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CNgcParticleStar();

//	NxNgc::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint8					m_blend;
	uint8					m_fix;
	uint8					m_pad0;
	uint8					m_pad1;

	int						m_num_segments;
	float					m_split;
	GXColor					m_start_color[3];			// Start color for each corner.
	GXColor					m_mid_color[3];				// Mid color for each corner.
	GXColor					m_end_color[3];				// End color for each corner.
	NxNgc::sTexture *		mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/NGC/p_nxscene.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.cpp

#include 	"gfx/Ngc/p_NxScene.h"
#include 	"gfx/Ngc/p_NxSector.h"
#include 	"gfx/Ngc/p_NxGeom.h"

namespace Nx
{


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcScene::CNgcScene( int sector_table_size ) : CScene( sector_table_size )
{
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcScene::DestroySectorMeshes( void )
{
	// Iterate through the list of sectors, removing each one in turn, and requesting the engine to
	// remove the meshes.
	mp_sector_table->IterateStart();
	CSector* pSector = mp_sector_table->IterateNext();
	while( pSector )
	{
		// Access platform dependent data.
//		CNgcSector* pNgcSector = static_cast(pSector);

		// Remove this mesh array from the engine.
//		pNgcSector->DestroyMeshArray();

		pSector = mp_sector_table->IterateNext();
	}
}



/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CScene

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcScene::plat_post_load()
{
	// Now turn the temporary mesh lists into mesh arrays.
	mp_sector_table->IterateStart();
	CSector* pSector = mp_sector_table->IterateNext();
	while( pSector )
	{
		CNgcGeom *p_Ngc_geom = static_cast(pSector->GetGeom());

		p_Ngc_geom->CreateMeshArray();

		// First time through we just want to count the meshes,
		p_Ngc_geom->RegisterMeshArray( true );

		pSector = mp_sector_table->IterateNext();
	}

	// Now we have counted all the meshes, tell the engine to create the arrays to hold them.
	GetEngineScene()->CreateMeshArrays();
	
	// Now go through and actually add the meshes.
	mp_sector_table->IterateStart();
	pSector = mp_sector_table->IterateNext();
	while( pSector )
	{
		// Access platform dependent data.
		CNgcGeom *p_Ngc_geom = static_cast(pSector->GetGeom());

		p_Ngc_geom->RegisterMeshArray( false );

		pSector = mp_sector_table->IterateNext();
	}

	// Now all meshes are registered, tell the engine to sort them.
	GetEngineScene()->SortMeshes();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcScene::plat_load_textures(const char* p_name)
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcScene::plat_load_collision(const char* p_name)
{
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcScene::plat_unload_add_scene( void )
{
	// Not sure what this is supposed to do, but added for now to remove annoying stub output.
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Create an empty sector
CSector	*			CNgcScene::plat_create_sector()
{
	CNgcSector *p_Ngc_sector	= new CNgcSector();
	return p_Ngc_sector;
}



} // Namespace Nx  			
				
				



================================================
FILE: Code/Gfx/NGC/p_nxscene.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.h

#ifndef	__GFX_P_NX_SCENE_H__
#define	__GFX_P_NX_SCENE_H__

#include "Gfx/Nx.h"
#include "Gfx/Ngc/nx/scene.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CScene
class	CNgcScene : public CScene
{
public:

								CNgcScene( int sector_table_size = 10 );
	NxNgc::sScene *			GetEngineScene() const						{ return mp_engine_scene; }
	void						SetEngineScene( NxNgc::sScene *p_scene )	{ mp_engine_scene = p_scene; }
	void						DestroySectorMeshes( void );

private:		// It's all private, as it is machine specific
	virtual void				plat_post_load();	
	virtual bool				plat_load_textures( const char *p_name );	// load textures 
	virtual bool				plat_load_collision( const char *p_name );	// load collision data
	virtual bool				plat_unload_add_scene( void );
	virtual	CSector	*			plat_create_sector();	 					// empty sector


	NxNgc::sScene				*mp_engine_scene;

};

} // Namespace Nx  			

#endif



================================================
FILE: Code/Gfx/NGC/p_nxsector.cpp
================================================

///////////////////////////////////////////////////////////////////////////////
// p_NxSector.cpp

#include	

#include 	"gfx/ngc/p_NxSector.h"
#include 	"gfx/ngc/p_NxGeom.h"
#include 	"gfx/Image/ImageBasic.h"
#include 	"gfx/ngc/nx/render.h"
#include 	"gfx/ngc/nx/mesh.h"
#include 	"gfx/NxMiscFX.h"
#include 	"gfx\ngc\nx\nx_init.h"
#include	

//NxNgc::sMesh *	p_u_mat[256];
//int				u_mat_count[256];
//int				num_u_mat = 0;

namespace Nx
{

#ifdef SHORT_VERT

static int round_float( float f, int shift, float off, float cen )
{
	float mul = ((float)( 1 << shift ));
	int i_f = (int)( ( f - cen ) * mul );

	int adjust = 0;
	if ( i_f > 0 )
	{
		adjust = 1;
	}
//	if ( i_f < 0 )
//	{
//		adjust = -1;
//	}

	i_f = (int)( ( f - off ) * mul );
	i_f += adjust;
	return i_f;

//	float mul = ((float)( 1 << shift ));
//	int i_f = (int)( f * mul );
//	float d_f = ( f * mul ) - ((float)i_f);
//
//	if ( d_f >= 0.0f )
//	{
//		if( d_f < 0.5f )
//		{
//			return i_f;
//		}
//		else
//		{
//			return i_f + 1;
//		}
//	}
//	else
//	{
//		if( d_f < -0.5f )
//		{
//			return i_f;
//		}
//		else
//		{
//			return i_f - 1;
//		}
//	}
}
#endif		// SHORT_VERT

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

//CNgcSector::CNgcSector() : mp_init_mesh_list( NULL ), m_mesh_array( NULL ), m_visible( 0xDEADBEEF )
CNgcSector::CNgcSector()
{
	m_pos_offset.Set( 0.0f, 0.0f, 0.0f );
//	m_active = true;						// default to be active....
//	mp_scene = NULL;
}




#define MemoryRead( dst, size, num, src )	memcpy(( dst ), ( src ), (( num ) * ( size )));	\
											( src ) += (( num ) * ( size ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
NxNgc::sObjectHeader* CNgcSector::LoadFromMemory( NxNgc::sObjectHeader* p_data )
{
//	uint8		*p_file_data	= (uint8*)( *pp_mem );
//
//	Dbg_Assert( mp_geom );
//
//	CNgcGeom *p_geom = static_cast( mp_geom );
//	
//	// Read sector checksum.
//	uint32 sector_checksum;
//	MemoryRead( §or_checksum, sizeof( uint32 ), 1, p_file_data );
//	
//	SetChecksum( sector_checksum );
//
//	// Read bone index.
//	int bone_idx;
//	MemoryRead( &bone_idx, sizeof( int ), 1, p_file_data );
//
//	// Read sector flags.
//	uint32 flags;
//	MemoryRead( &flags, sizeof( int ), 1, p_file_data );
//	m_flags = flags;
//
//	// Read number of meshes.
//	int num_mesh;
//	MemoryRead( &num_mesh, sizeof( uint ), 1, p_file_data );
//	p_geom->m_num_mesh = num_mesh;
//
//	// Read bounding box.
//	float bbox[6];
//	MemoryRead( &bbox[0], sizeof( float ), 6, p_file_data );
//	Mth::Vector	inf( bbox[0], bbox[1], bbox[2] );
//	Mth::Vector	sup( bbox[3], bbox[4], bbox[5] );
//	p_geom->m_bbox.Set( inf, sup );
//		
//	// Read bounding sphere.
//	float bsphere[4];
//	MemoryRead( &bsphere[0], sizeof( float ), 4, p_file_data );
//		
//	// Read num vertices.
//	int num_vertices;
//	MemoryRead( &num_vertices, sizeof( int ), 1, p_file_data );
//	
//	// Read vertex data stride.
//	int vertex_data_stride;
//	MemoryRead( &vertex_data_stride, sizeof( int ), 1, p_file_data );
//	
//	// Only read vertex/normal pools if skinned.
//	int dbytes = 0;
//	int sbytes = 0;
//	float* p_vertex_positions = NULL;
//	s16* p_vertex_normals = NULL;
//	uint32* p_double = NULL;
//	uint32* p_temp = NULL;
//	int num_double = 0;
//	int num_single = 0;
//#ifdef SHORT_VERT
//	s16* p_vertex_positions16 = NULL;
//	int shift = 0;
//	float off_x = 0.0f;
//	float off_y = 0.0f;
//	float off_z = 0.0f;
//#endif		// SHORT_VERT
//	if ( !(m_flags & 0x10) )
//	{
//		// Grab a buffer for the raw vertex data position stream, and read it.
//#ifdef SHORT_VERT
//		p_vertex_positions = new (Mem::Manager::sHandle().TopDownHeap()) float[num_vertices * 3];
//#else
//		p_vertex_positions = new float[num_vertices * 3];
//#endif		// SHORT_VERT
//		MemoryRead( p_vertex_positions, sizeof( float ) * 3, num_vertices, p_file_data );
//
//#ifdef SHORT_VERT
//		int lp;
//
//		// Calculate the largest dimension.
//		float smallest_x = p_vertex_positions[0];
//		float smallest_y = p_vertex_positions[1];
//		float smallest_z = p_vertex_positions[2];
//		float largest_x = p_vertex_positions[0];
//		float largest_y = p_vertex_positions[1];
//		float largest_z = p_vertex_positions[2];
//		for ( lp = 1; lp < num_vertices; lp++ )
//		{
//			if ( p_vertex_positions[(lp*3)+0] > largest_x ) largest_x = p_vertex_positions[(lp*3)+0];
//			if ( p_vertex_positions[(lp*3)+1] > largest_y ) largest_y = p_vertex_positions[(lp*3)+1];
//			if ( p_vertex_positions[(lp*3)+2] > largest_z ) largest_z = p_vertex_positions[(lp*3)+2];
//			if ( p_vertex_positions[(lp*3)+0] < smallest_x ) smallest_x = p_vertex_positions[(lp*3)+0];
//			if ( p_vertex_positions[(lp*3)+1] < smallest_y ) smallest_y = p_vertex_positions[(lp*3)+1];
//			if ( p_vertex_positions[(lp*3)+2] < smallest_z ) smallest_z = p_vertex_positions[(lp*3)+2];
//		}
//		float biggest = largest_x;
//		if ( largest_y > biggest ) biggest = largest_y;
//		if ( largest_z > biggest ) biggest = largest_z;
//		if ( -smallest_x > biggest ) biggest = -smallest_x;
//		if ( -smallest_y > biggest ) biggest = -smallest_y;
//		if ( -smallest_z > biggest ) biggest = -smallest_z;
//
//		float c_x = ( smallest_x + largest_x ) / 2.0f;
//		float c_y = ( smallest_y + largest_y ) / 2.0f;
//		float c_z = ( smallest_z + largest_z ) / 2.0f;
//
//		// Work out the shift amount.
//		if ( biggest > ( ( 1 << 15 ) - 1 ) )
//		{
//			// Work out how to offset it.
//			off_x = ( smallest_x + largest_x ) / 2.0f;
//			off_y = ( smallest_y + largest_y ) / 2.0f;
//			off_z = ( smallest_z + largest_z ) / 2.0f;
//			biggest = ( largest_x - smallest_x ) / 2.0f;
//			if ( ( ( largest_y - smallest_y ) / 2.0f ) > biggest ) biggest = ( ( largest_y - smallest_y ) / 2.0f );
//			if ( ( ( largest_z - smallest_z ) / 2.0f ) > biggest ) biggest = ( ( largest_z - smallest_z ) / 2.0f );
//
//			if ( ( largest_x - smallest_x ) > 65535 )
//			{
//				OSReport( "Cannot deal with meshes larger than 65535 X units, this mesh is %6.3f\n", ( largest_x - smallest_x ) );
//				while ( 1 == 1 );
//			}
//
//			if ( ( largest_y - smallest_y ) > 65535 )
//			{
//				OSReport( "Cannot deal with meshes larger than 65535 Y units, this mesh is %6.3f\n", ( largest_y - smallest_y ) );
//				while ( 1 == 1 );
//			}
//
//			if ( ( largest_z - smallest_z ) > 65535 )
//			{
//				OSReport( "Cannot deal with meshes larger than 65535 Z units, this mesh is %6.3f\n", ( largest_z - smallest_z ) );
//				while ( 1 == 1 );
//			}
//			shift = 0;
//		}
//
//		if ( biggest > ( ( 1 << 14 ) - 1 ) )
//		{
//			shift = 0;
//		}
//		else
//		if ( biggest > ( ( 1 << 13 ) - 1 ) )
//		{
//			shift = 1;
//		}
//		else
//		if ( biggest > ( ( 1 << 12 ) - 1 ) )
//		{
//			shift = 2;
//		}
//		else
//		if ( biggest > ( ( 1 << 11 ) - 1 ) )
//		{
//			shift = 3;
//		}
//		else
//		if ( biggest > ( ( 1 << 10 ) - 1 ) )
//		{
//			shift = 4;
//		}
//		else
//		{
//			shift = 5;
//		}
////		if ( biggest > ( ( 1 << 9 ) - 1 ) )
////		{
////			shift = 5;
////		}
////		else
////		{
////			shift = 6;
////		}
//
//		// Now, we need to squeeze the vertex positions down to s16s (1:9:6)
//		p_vertex_positions16 = new s16[num_vertices * 3];
//		for ( lp = 0; lp < num_vertices; lp++ )
//		{
//			p_vertex_positions16[(lp*3)+0] = (s16)( round_float( p_vertex_positions[(lp*3)+0], shift, off_x, c_x ) );
//			p_vertex_positions16[(lp*3)+1] = (s16)( round_float( p_vertex_positions[(lp*3)+1], shift, off_y, c_y ) );
//			p_vertex_positions16[(lp*3)+2] = (s16)( round_float( p_vertex_positions[(lp*3)+2], shift, off_z, c_z ) );
//		}
//#endif		// SHORT_VERT
//
//		// Grab a buffer for the raw vertex data normal stream (if present), and read it.
//		p_vertex_normals = ( m_flags & 0x04 ) ? new s16[num_vertices * 3] : NULL;
//		if( p_vertex_normals )
//		{
//			MemoryRead( p_vertex_normals, sizeof( s16 ) * 3, num_vertices, p_file_data );
//		}
//	} else {
//		int bytes = 0;
//		// This is a skinned model, read in the double data.
//		MemoryRead( &bytes, sizeof( int ), 1, p_file_data );
//		MemoryRead( &num_double, sizeof( int ), 1, p_file_data );
//
//		dbytes = ( bytes + 3 ) / 4;
//		p_temp = new uint32[dbytes];
//		MemoryRead( p_temp, bytes, 1, p_file_data );
//
//		// Read the single data.
//		MemoryRead( &bytes, sizeof( int ), 1, p_file_data );
//		MemoryRead( &num_single, sizeof( int ), 1, p_file_data );
//
//		sbytes = ( bytes + 3 ) / 4;
//		p_double = new /*(Mem::Manager::sHandle().TopDownHeap())*/ uint32[dbytes + sbytes];
//		memcpy( p_double, p_temp, dbytes * 4 );
//		MemoryRead( &p_double[dbytes], bytes, 1, p_file_data );
//
//		delete p_temp;
//	}
//
////	// Grab a buffer for the raw vertex data weights stream (if present), and read it.
////	float* p_vertex_weights = ( m_flags & 0x10 ) ? new float[num_vertices * 3] : NULL;
////	if( p_vertex_weights )
////	{
////		MemoryRead( p_vertex_weights, sizeof( float ) * 3, num_vertices, p_file_data );
////	}
////	
////	// Grab a buffer for the raw vertex data bone indices stream (if present), and read it.
////	uint16* p_vertex_bone_indices = ( m_flags & 0x10 ) ? new uint16[num_vertices * 3] : NULL;
////	if( p_vertex_bone_indices )
////	{
////		MemoryRead( p_vertex_bone_indices, sizeof( uint16 ) * 3, num_vertices, p_file_data );
////	}
//
//	uint16 * p_temp_col_remap = NULL;
//	uint16 * p_temp_uv_remap = NULL;
//
//	// Grab a buffer for the raw vertex texture coordinate stream (if present), and read it.
//	int		num_tc_sets			= 0;
//	float*	p_vertex_tex_coords	= NULL;
//	if( m_flags & 0x01 )
//	{
//		MemoryRead( &num_tc_sets, sizeof( int ), 1, p_file_data );
//		
//		float * p_temp_tex_coords = new (Mem::Manager::sHandle().TopDownHeap()) float[num_vertices * 2 * num_tc_sets];
//		p_vertex_tex_coords = new float[num_vertices * 2 * num_tc_sets];
//		MemoryRead( p_temp_tex_coords, sizeof( float ) * 2 * num_tc_sets, num_vertices, p_file_data );
//
//		// Convert to friendlier non-interleaved format.
//		for ( int lp = 0; lp < num_vertices; lp++ )
//		{
//			for ( int lp2 = 0; lp2 < num_tc_sets; lp2++ )
//			{
//				p_vertex_tex_coords[(lp2*2*num_vertices)+(lp*2)+0] = p_temp_tex_coords[(lp2*2)+(lp*2*num_tc_sets)+0];
//				p_vertex_tex_coords[(lp2*2*num_vertices)+(lp*2)+1] = p_temp_tex_coords[(lp2*2)+(lp*2*num_tc_sets)+1];
//			}
//		}
//
//		// Create temp uv mapping buffer.
//		p_temp_uv_remap = num_tc_sets ? new (Mem::Manager::sHandle().TopDownHeap()) uint16[num_vertices*num_tc_sets] : NULL;
//
//		for ( int c = 0; c < num_vertices; c++ )
//		{
//			for ( int u = 0; u < num_tc_sets; u++ )
//			{
//				p_temp_uv_remap[(u*num_vertices)+c] = (u*num_vertices)+c;
//			}
//		}
//
//		bool reduce = true;
//		if ( m_flags & 0x04 ) reduce = false;	// No uv reduction if normals are present.
//
//		if ( reduce )
//		{
//			// Copy back to temp coords.
//			memcpy( p_temp_tex_coords, p_vertex_tex_coords, num_vertices * num_tc_sets * 2 * sizeof( float ) );
//			delete [] p_vertex_tex_coords;
//			float * p_unique_tex_coords = new (Mem::Manager::sHandle().TopDownHeap()) float[num_vertices * 2 * num_tc_sets];
//
//			// Now, squeeze down to only unique uvs.
//			int numUnique = 0;
//			for ( int lp = 0; lp < num_vertices; lp++ )
//			{
//				for ( int lp2 = 0; lp2 < num_tc_sets; lp2++ )
//				{
//					bool found = false;
//					for ( int lp3 = 0; lp3 < numUnique; lp3++ )
//					{
//						if ( ( p_unique_tex_coords[(lp3*2)+0] == p_temp_tex_coords[(lp2*2*num_vertices)+(lp*2)+0] ) &&
//							 ( p_unique_tex_coords[(lp3*2)+1] == p_temp_tex_coords[(lp2*2*num_vertices)+(lp*2)+1] ) )
//						{
//							// Found a match.
//							p_temp_uv_remap[(lp2*num_vertices)+lp] = lp3;
//							found = true;
//							break;
//						}
//					}
//					if ( !found )
//					{
//						p_unique_tex_coords[(numUnique*2)+0] = p_temp_tex_coords[(lp2*2*num_vertices)+(lp*2)+0];
//						p_unique_tex_coords[(numUnique*2)+1] = p_temp_tex_coords[(lp2*2*num_vertices)+(lp*2)+1];
//						p_temp_uv_remap[(lp2*num_vertices)+lp] = numUnique;
//						numUnique++;
//					}
//				}
//			}
//			// Allocate only the space we need.
//			p_vertex_tex_coords = new float[numUnique * 2];
//			memcpy( p_vertex_tex_coords, p_unique_tex_coords, numUnique * 2 * sizeof( float ) );
//			delete [] p_unique_tex_coords;
//		}
//
//		delete p_temp_tex_coords;
//	}
//	
//	// Grab a buffer for the raw vertex colors stream (if present), and read it.
//	uint32* p_vertex_colors = ( m_flags & 0x02 ) ? new uint32[num_vertices] : NULL;
//	if( p_vertex_colors )
//	{
//		MemoryRead( p_vertex_colors, sizeof( uint32 ), num_vertices, p_file_data );
//	
//		// Create temp col mapping buffer.
//		p_temp_col_remap = p_vertex_colors ? new (Mem::Manager::sHandle().TopDownHeap()) uint16[num_vertices] : NULL;
//
//		for ( int c = 0; c < num_vertices; c++ )
//		{
//			p_temp_col_remap[c] = c;
//		}
//
//		bool reduce = true;
//		if ( m_flags & 0x04 ) reduce = false;	// No color reduction if normals are present.
//		if ( m_flags & 0x800 ) reduce = false;	// No color reduction if vc wibble data is present.
//		if ( !NxNgc::EngineGlobals.reduceColors ) reduce = false;	// No color reduction if vc wibble data is present.
//
//		if ( reduce )
//		{
//			uint32 * p_temp_colors = new (Mem::Manager::sHandle().TopDownHeap()) uint32[num_vertices];
//			memcpy( p_temp_colors, p_vertex_colors, num_vertices * 4 );
//			delete [] p_vertex_colors;
//			uint32* p_unique_colors = new (Mem::Manager::sHandle().TopDownHeap()) uint32[num_vertices];
//
//			// Now, squeeze down to only unique uvs.
//			int numUnique = 0;
//			for ( int lp = 0; lp < num_vertices; lp++ )
//			{
//				bool found = false;
//				for ( int lp2 = 0; lp2 < numUnique; lp2++ )
//				{
//					if ( p_unique_colors[lp2] == p_temp_colors[lp] )
//					{
//						// Found a match.
//						p_temp_col_remap[lp] = lp2;
//						found = true;
//						break;
//					}
//				}
//				if ( !found )
//				{
//					p_unique_colors[numUnique] = p_temp_colors[lp];
//					p_temp_col_remap[lp] = numUnique;
//					numUnique++;
//				}
//			}
//			// Allocate only the space we need.
//			p_vertex_colors = new uint32[numUnique];
//			memcpy( p_vertex_colors, p_unique_colors, numUnique * 4 );
//			delete [] p_unique_colors;
//			delete p_temp_colors;
//		}
//	}
//
//	// Grab a buffer for the raw vertex colors stream (if present), and read it.
//	char* p_vc_wibble_indices = ( m_flags & 0x800 ) ? new char[num_vertices] : NULL;
//	if( p_vc_wibble_indices )
//	{
//		MemoryRead( p_vc_wibble_indices, sizeof( char ), num_vertices, p_file_data );
//	}
//	
//	NxNgc::sMesh*	p_mesh0 = NULL;
//
//	for( uint m = 0; m < p_geom->m_num_mesh; ++m )
//	{
//		unsigned long	material_checksum;
//		uint32			flags;
//		int				num_indices;
//		float			sphere[4];
//
//		NxNgc::sMesh*	p_mesh = new NxNgc::sMesh;
//		if ( m == 0 )
//		{
//			p_mesh0 = p_mesh;
//			p_mesh0->mp_envBuffer = NULL;
//		}
//		
//		MemoryRead( &material_checksum,	sizeof( unsigned long ), 1, p_file_data );
//		MemoryRead( &flags,	sizeof( uint32 ), 1, p_file_data );
//		MemoryRead( sphere, sizeof( float ), 4, p_file_data );
//		MemoryRead( &num_indices, sizeof( int ), 1, p_file_data );
//
//		uint16* p_indices = new uint16[num_indices];
//		MemoryRead( p_indices, sizeof( uint16 ), num_indices, p_file_data );
//
//		// Deal with skater shadow flag.
//		if( flags & (0x4000|0x400) )
//		{
//			p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_NO_SKATER_SHADOW;
//		}
//		// Deal with vertex color wibble flag.
//		if( flags & (0x800) )
//		{
//			p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_VERTEX_COLOR_WIBBLE;
//		}
//		
//		if( m_flags & (0x80000) )
//		{
//			p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_GRASS;
//		}
//		
//		// Create the mesh from the raw data.
//		p_mesh->Initialize( num_vertices,
//							p_vertex_positions,
//#ifdef SHORT_VERT
//							p_vertex_positions16,
//							shift,
//							off_x,
//							off_y,
//							off_z,
//#endif		// SHORT_VERT
//							p_vertex_normals,
//							p_vertex_tex_coords,
//							p_mesh0->mp_envBuffer,
//							num_tc_sets,
//							p_vertex_colors,
//							num_indices,
//							p_indices,		// pos
//							p_temp_col_remap,		// color
//							p_temp_uv_remap,		// uv
//							material_checksum,
//							p_geom->mp_scene->GetEngineScene(),
//							num_double,
//							p_double,
//							num_single,
//							m,
//							GX_TRIANGLESTRIP,
//							bone_idx,
//							p_vc_wibble_indices );
//
//		// Assign env buffer to mesh 0 if assigned.
//		if ( ( m > 0 ) && p_mesh->mp_envBuffer ) p_mesh0->mp_envBuffer = p_mesh->mp_envBuffer;
//		
//		// Add the mesh to the sector.
//		p_geom->AddMesh( p_mesh );
//	
//		// Done with the raw index data.
////		delete[] p_indices;

//		if ( p_data->m_size )
//		{
//			// Setup the DL.
//			p_mesh->mp_dl = p_data;
//			p_data = (NxNgc::sDLHeader*)(&((char*)&p_data[1])[p_data->m_size]);
//
//			p_mesh->mp_dl->mp_material_dl = NULL;
//			p_mesh->mp_dl->m_material_dl_size = 0;
//
//			// Find the material.
//			NxNgc::sMaterialHeader * p_mat_list = p_geom->mp_scene->GetEngineScene()->mp_material_list;
//			bool found = false;
//			for ( unsigned int lp = 0; lp < p_geom->mp_scene->GetEngineScene()->mp_scene_data->m_num_materials; lp++ )
//			{
//				if ( p_mat_list->m_checksum == p_mesh->mp_dl->m_material.checksum )
//				{
//					// Found it!
//					p_mesh->mp_dl->m_material.p_header = p_mat_list;
//
//					// Construct the DL.
//					Mem::Manager::sHandle().TopDownHeap()->PushAlign( 32 );
//#define DL_BUILD_SIZE (4*1024)
//					uint8 * p_build_dl = new (Mem::Manager::sHandle().TopDownHeap()) uint8[DL_BUILD_SIZE];
//					DCFlushRange ( p_build_dl, DL_BUILD_SIZE );
//
//					GXBeginDisplayList ( p_build_dl, DL_BUILD_SIZE );
//					GXResetWriteGatherPipe();
//
////					// ---------------- Begin Generate display list.
//////					GXSetNumTevStages( 1 );
////					GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD_NULL, GX_TEXMAP_NULL, GX_COLOR0A0);
////					GXSetTevSwapMode( GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0 );
//////					GXSetNumTexGens( 0 );
////					GXSetTevAlphaOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
////					GXSetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );
////////					GXSetAlphaCompare(GX_GREATER, 0, GX_AOP_AND, GX_GREATER, 0 );
////					GXSetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR );
////					GXSetTevAlphaIn ( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA );
////					GXSetTevColorIn ( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC );
////					GXSetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
////					GXSetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//
//					NxNgc::sMaterialHeader * p_mat = p_mesh->mp_dl->m_material.p_header;
//					NxNgc::sMaterialPassHeader * p_pass = (NxNgc::sMaterialPassHeader *)&p_mat[1];
//
//					int lp;
//
//					NxNgc::MaterialBegin(); for ( lp = 0; lp < p_mat->m_passes; lp++ ) NxNgc::MaterialLayer( p_pass, lp, MATERIAL_GROUP_CP );	NxNgc::MaterialEnd( &p_mesh->mp_dl->m_tev_stages, &p_mesh->mp_dl->m_tex_gens );
//					NxNgc::MaterialBegin(); for ( lp = 0; lp < p_mat->m_passes; lp++ ) NxNgc::MaterialLayer( p_pass, lp, MATERIAL_GROUP_SU );   NxNgc::MaterialEnd( &p_mesh->mp_dl->m_tev_stages, &p_mesh->mp_dl->m_tex_gens );
//					NxNgc::MaterialBegin(); for ( lp = 0; lp < p_mat->m_passes; lp++ ) NxNgc::MaterialLayer( p_pass, lp, MATERIAL_GROUP_B );    NxNgc::MaterialEnd( &p_mesh->mp_dl->m_tev_stages, &p_mesh->mp_dl->m_tex_gens );
//					NxNgc::MaterialBegin(); for ( lp = 0; lp < p_mat->m_passes; lp++ ) NxNgc::MaterialLayer( p_pass, lp, MATERIAL_GROUP_C );    NxNgc::MaterialEnd( &p_mesh->mp_dl->m_tev_stages, &p_mesh->mp_dl->m_tex_gens );
//					NxNgc::MaterialBegin(); for ( lp = 0; lp < p_mat->m_passes; lp++ ) NxNgc::MaterialLayer( p_pass, lp, MATERIAL_GROUP_A );    NxNgc::MaterialEnd( &p_mesh->mp_dl->m_tev_stages, &p_mesh->mp_dl->m_tex_gens );
//
//					GXSetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
//					GXSetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
//					GXSetAlphaCompare(GX_GREATER, p_mat->m_alpha_cutoff, GX_AOP_AND, GX_GREATER, p_mat->m_alpha_cutoff );
//					// ---------------- End Generate display list.
//
//					uint32 size = GXEndDisplayList();
//
////					// If we already have a display list & this one will fit, just use the existing piece of memory.
////					uint8 * p_dl;
////					if ( mp_display_list && ( size <= m_display_list_size ) )
////					{
////						p_dl = mp_display_list;
////					}
////					else
////					{
////						if ( mp_display_list )
////						{
////							//gMatBytes -= m_display_list_size;
////							delete mp_display_list;
////						}
//						p_mesh->mp_dl->mp_material_dl = new uint8[size];
//						//gMatBytes += size;
////					}
//
//					memcpy ( p_mesh->mp_dl->mp_material_dl, p_build_dl, size );
//					DCFlushRange ( p_mesh->mp_dl->mp_material_dl, size );
//
//					delete p_build_dl;
//
//					p_mesh->mp_dl->m_material_dl_size = size;
//					Mem::Manager::sHandle().TopDownHeap()->PopAlign();
//
//
//
//
//
//					found = true;
//					break;
//				}
//				p_mat_list = (NxNgc::sMaterialHeader *)&(((char*)(&p_mat_list[1]))[p_mat_list->m_skip_bytes]);
//			}
//			if ( !found )
//			{
//				Dbg_MsgAssert( 0, ( "Unable to find material checksum 0x%08x\n", p_mesh->mp_dl->m_material.checksum ));
//				p_mesh->mp_dl->m_material.p_header = NULL;
//			}
//		}
//	}

//	if ( p_temp_col_remap ) delete [] p_temp_col_remap;
//	if ( p_temp_uv_remap ) delete [] p_temp_uv_remap;
////	if ( p_double ) delete p_double;
//
//	// Done with the raw vertex data.
////	delete[] p_vertex_colors;
////	delete[] p_vertex_tex_coords;
////	delete[] p_vertex_normals;
//#ifdef SHORT_VERT
//	delete[] p_vertex_positions;
//#endif		// SHORT_VERT
//
//	// Set the data pointer to the new position on return.
//	*pp_mem = p_file_data;
//
//	return p_data;

	Dbg_MsgAssert( false, ( "Not yet implented!!!!!!!" ) );
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
NxNgc::sObjectHeader* CNgcSector::LoadFromFile( NxNgc::sObjectHeader* p_data )
{
	Dbg_Assert( mp_geom );

	CNgcGeom *p_geom = static_cast( mp_geom );
	
	// Hook up vars.
	p_geom->m_num_mesh = p_data->m_num_meshes;
//  	p_geom->mp_scene->GetEngineScene()->mp_dl->mp_object_header = p_data;

	char * p_skin = (char *)&p_data[1];
	int nbytes = p_data->m_skin.num_bytes;
	p_data->m_skin.p_data = p_skin;
	NxNgc::sDLHeader* p_dl = (NxNgc::sDLHeader*)&p_skin[nbytes];

	m_flags = p_dl->m_flags;
	SetChecksum( p_dl->m_checksum );

	for( uint m = 0; m < p_geom->m_num_mesh; ++m )
	{
		NxNgc::sMesh*	p_mesh = new NxNgc::sMesh;

		p_geom->AddMesh( p_mesh );

		// Setup mesh flags.
		if ( *((uint32*)(&p_dl->mp_col_pool)) & ( 0x00000100 | 0x00000400 ) )
		{
			p_mesh->m_flags |= NxNgc::sMesh::MESH_FLAG_NO_SKATER_SHADOW;
		}
		p_dl->mp_col_pool = NULL;

		// Setup bottom y.
		p_mesh->m_bottom_y = *((float*)(&p_dl->mp_pos_pool));
		p_dl->mp_pos_pool = NULL;

		if ( p_dl->m_size )
		{
			// Setup the DL.
			p_mesh->mp_dl = p_dl;
			p_mesh->mp_dl->mp_object_header = p_data;
			p_mesh->m_bone_idx = p_mesh->mp_dl->mp_object_header->m_bone_index;

			// Fix up the bounding sphere for billboards.
			switch ( p_mesh->mp_dl->mp_object_header->m_billboard_type )
			{
				case 1:
				case 2:
					p_mesh->mp_dl->m_sphere[X] += p_mesh->mp_dl->mp_object_header->m_origin[X];
					p_mesh->mp_dl->m_sphere[Y] += p_mesh->mp_dl->mp_object_header->m_origin[Y];
					p_mesh->mp_dl->m_sphere[Z] += p_mesh->mp_dl->mp_object_header->m_origin[Z];
					break;
				default:
					break;
			}

			p_dl = (NxNgc::sDLHeader*)&(((char *)&p_dl[1])[p_dl->m_size]);

			// Find the material.
			NxNgc::sMaterialHeader * p_mat_list = p_geom->mp_scene->GetEngineScene()->mp_material_header;
			bool found = false;
			for ( unsigned int lp = 0; lp < p_geom->mp_scene->GetEngineScene()->mp_scene_data->m_num_materials; lp++ )
			{
				if ( p_mat_list->m_checksum == p_mesh->mp_dl->m_material.checksum )
				{
					// Found it!
					p_mesh->mp_dl->m_material.p_header = p_mat_list;

//					NxNgc::MaterialBuild( p_mesh, p_geom->mp_scene->GetEngineScene(), false, true ); 

					found = true;
					break;
				}
				++p_mat_list;
			}
			if ( !found )
			{
				Dbg_MsgAssert( 0, ( "Unable to find material checksum 0x%08x\n", p_mesh->mp_dl->m_material.checksum ));
				p_mesh->mp_dl->m_material.p_header = NULL;
			}
		}
	}

	return (NxNgc::sObjectHeader*)p_dl;
}



/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CSector
// and we will also have a CNgcSector, CNgcSector, even a CPcSector
// maybe in the future we will have a CPS3Sector?

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcSector::plat_set_color(Image::RGBA rgba)
{
	// Set values
	m_rgba = rgba;

	// Engine call here
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcSector::plat_clear_color()
{
	// Set to white
	plat_set_color(Image::RGBA(128, 128, 128, 128));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcSector::plat_set_visibility(uint32 mask)
{
//	// Set values
//	m_visible = mask;
//
//	// Engine call here
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcSector::plat_set_active( bool on )
{
//	// Set values
//	m_active = on;
//
//	if( m_mesh_array )
//	{
//		for( uint m = 0; m < m_num_mesh; ++m )
//		{
//			NxNgc::sMesh *p_mesh = m_mesh_array[m];
//			p_mesh->SetActive( on );
//		}
//	}
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

//bool CNgcSector::plat_is_active() const
//{
//	return m_active;
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

 void CNgcSector::plat_set_world_position(const Mth::Vector& pos)
 {
/*
	Mth::Vector new_offset = pos - m_pos_offset;

	// Go through and adjust the individual meshes.
	for( uint32 i = 0; i < m_num_mesh; ++i )
	{
		NxNgc::sMesh *p_mesh = m_mesh_array[i];
		p_mesh->Move( new_offset );
	}
*/
 }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::CBBox &CNgcSector::plat_get_bounding_box( void ) const
{
	static Mth::CBBox dummy;
	
	//	return m_bbox;
	return dummy;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &	CNgcSector::plat_get_world_position() const
{
	return m_pos_offset;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

//void CNgcSector::plat_set_y_rotation(Mth::ERot90 rot)
//{
//	// Engine call here
//	// Garrett: TEMP just set the world matrix
//	Mth::Matrix rot_mat;
//	CreateRotateYMatrix(rot_mat, (float) rot * (Mth::PI * 0.5f));
//
//	m_world_matrix[X] = rot_mat[X];
//	m_world_matrix[Y] = rot_mat[Y];
//	m_world_matrix[Z] = rot_mat[Z];
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CNgcSector::plat_set_shatter(bool on)	
{
	if( on && mp_geom )
	{
		Shatter( mp_geom );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSector * CNgcSector::plat_clone(bool instance, CScene *p_dest_scene)
{
	CNgcSector *p_Ngc_sector = new CNgcSector();

	/*


	// Copies over much of the standard stuff, individual stuff will be overwritten later.
	CopyMemory( p_Ngc_sector, this, sizeof( CNgcSector ));

	if( instance )
	{
		Dbg_Assert( 0 );
	}
	else
	{
		// Need to create a new set of meshes. First create the mesh pointer array...
		p_Ngc_sector->m_mesh_array = new NxNgc::sMesh*[m_num_mesh];

		// ...then clone the meshes themselves.
		for( uint32 i = 0; i < m_num_mesh; ++i )
		{
			p_Ngc_sector->m_mesh_array[i] = m_mesh_array[i]->Clone();
		}

		// Grab a temporary workspace buffer.
		NxNgc::sScene *p_scene								= ( static_cast( p_dest_scene ))->GetEngineScene();
		NxNgc::sMesh **p_temp_opaque_mesh_buffer			= new NxNgc::sMesh*[p_scene->m_num_opaque_entries];
		NxNgc::sMesh **p_temp_semitransparent_mesh_buffer	= new NxNgc::sMesh*[p_scene->m_num_semitransparent_entries];

		// Copy meshes over into the temporary workspace buffer.
		for( int i = 0; i < p_scene->m_num_opaque_entries; ++i )
		{
			p_temp_opaque_mesh_buffer[i] = p_scene->m_opaque_meshes[i];
		}
		for( int i = 0; i < p_scene->m_num_semitransparent_entries; ++i )
		{
			p_temp_semitransparent_mesh_buffer[i] = p_scene->m_semitransparent_meshes[i];
		}

		// Delete current mesh arrays.
		delete [] p_scene->m_opaque_meshes;
		delete [] p_scene->m_semitransparent_meshes;

		// Include new meshes in count.
		p_scene->CountMeshes( p_Ngc_sector->m_num_mesh, p_Ngc_sector->m_mesh_array );

		// Allocate new mesh arrays.
		p_scene->CreateMeshArrays();

		// Copy old mesh data back in.
		for( int i = 0; i < p_scene->m_num_opaque_entries; ++i )
		{
			p_scene->m_opaque_meshes[i] = p_temp_opaque_mesh_buffer[i];
		}
		for( int i = 0; i < p_scene->m_num_semitransparent_entries; ++i )
		{
			p_scene->m_semitransparent_meshes[i] = p_temp_semitransparent_mesh_buffer[i];
		}

		// Add new meshes.
		p_scene->AddMeshes( p_Ngc_sector->m_num_mesh, p_Ngc_sector->m_mesh_array );

		// Sort the meshes.
		p_scene->SortMeshes();
	}
*/
	return p_Ngc_sector;
}




} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/NGC/p_nxsector.h
================================================
#ifndef	__GFX_P_NX_SECTOR_H__
#define	__GFX_P_NX_SECTOR_H__

#include 	
#include 	
#include 	"gfx\NxSector.h"
#include 	"gfx\Image\ImageBasic.h"

#include 	"gfx\ngc\p_nxscene.h"
#include 	"gfx\ngc\nx\mesh.h"
#include 	"gfx\ngc\nx\geomnode.h"

#include 	

namespace NxNgc
{
	class CGeomNode;
}

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CSector
class CNgcSector : public CSector
{
	public:
								CNgcSector();

	NxNgc::sObjectHeader*		LoadFromFile( NxNgc::sObjectHeader* p_data );
	NxNgc::sObjectHeader*		LoadFromMemory( NxNgc::sObjectHeader* p_data );
	
private:		// It's all private, as it is machine specific
	virtual void				plat_set_color(Image::RGBA rgba);
	virtual void				plat_clear_color();
	virtual void				plat_set_visibility(uint32 mask);
	virtual void				plat_set_active(bool on);	
	virtual void				plat_set_world_position(const Mth::Vector& pos);
	virtual const Mth::CBBox	&plat_get_bounding_box( void ) const;
	virtual const Mth::Vector &	plat_get_world_position() const;
	virtual void				plat_set_shatter( bool on );
	virtual CSector *			plat_clone(bool instance, CScene *p_dest_scene);

	int								m_flags;

	Mth::Vector						m_pos_offset;

	Image::RGBA						m_rgba;
};

} // Namespace Nx  			

#endif

================================================
FILE: Code/Gfx/NGC/p_nxtexman.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxTexMan.cpp - Ngc platform specific interface to CTexMan
//

#include 

#include "gfx/NxTexMan.h"
#include "gfx/Ngc/p_NxTexture.h"
#include "gfx/Ngc/nx/import.h"

namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CTexDict* CTexDictManager::s_plat_load_texture_dictionary( const char *p_tex_dict_name, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup )
{
//	NxNgc::LoadTextureFile( p_tex_dict_name );
	return new CNgcTexDict( p_tex_dict_name, forceTexDictLookup );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CTexDict* CTexDictManager::s_plat_load_texture_dictionary( uint32 checksum, uint32 *p_data, int data_size, bool is_level_data, uint32 texDictOffset, bool is_skin, bool forceTexDictLookup )
{
	CNgcTexDict *p_dict = new CNgcTexDict( checksum );
	p_dict->LoadTextureDictionaryFromMemory( p_data );
	return p_dict;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CTexDict* CTexDictManager::s_plat_create_texture_dictionary( uint32 checksum )
{
	return new CNgcTexDict( checksum );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CTexDictManager::s_plat_unload_texture_dictionary( CTexDict* p_tex_dict )
{
	delete p_tex_dict;
	return true;
}


} 
 



================================================
FILE: Code/Gfx/NGC/p_nxtexture.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxTexture.cpp

#include 	"Gfx/Nx.h"
#include 	"Gfx/Ngc/p_NxTexture.h"
#include 	"Gfx/Ngc/NX/import.h"

extern bool g_in_cutscene;

namespace Nx
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CTexture

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcTexture::CNgcTexture() :  mp_texture( NULL )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcTexture::~CNgcTexture()
{
	if( mp_texture )
	{
		delete mp_texture;
	}
}


/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcTexture::plat_load_texture( const char *p_texture_name, bool sprite, bool alloc_vram )
{
	char filename[256];

	strcpy( filename, p_texture_name );
	
	// append '.img.ngc' to the end.
	strcat( filename, ".img.ngc" );
	
	mp_texture = NxNgc::LoadTexture( filename );
	
	return mp_texture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcTexture::plat_replace_texture( CTexture *p_texture )
{						  
	Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );

	Dbg_Assert(p_texture);
	
	CNgcTexture *p_Ngc_texture = static_cast( p_texture );

	// Go through and copy the texture.
	NxNgc::sTexture *p_src	= p_Ngc_texture->GetEngineTexture();
	NxNgc::sTexture *p_dst	= GetEngineTexture();

	// Couple of problem cases.
//	Dbg_MsgAssert( p_src->format == p_dst->format, ( "Cannot replace textures of different formats.\n" ));
//	if ( p_src->pAlphaData && !p_dst->pAlphaData )
//	{
//		Dbg_MsgAssert( false, ( "Cannot assign a texture with alpha to a texture without alpha.\n" ));
//	}
//	if ( !p_src->pAlphaData && p_dst->pAlphaData )
//	{
//		Dbg_MsgAssert( false, ( "Cannot assign a texture without alpha to a texture with alpha.\n" ));
//	}

	// Delete & re-allocate alpha space if new image is bigger, or not single owner alpha, or not channel 0.
	if ( p_src->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA )
	{
		// See if this texture needs to be stored in pOldAlpha.
		if ( !( p_dst->flags & NxNgc::sTexture::TEXTURE_FLAG_SINGLE_OWNER ) &&
			 ( ( p_dst->flags & NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_MASK ) == NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_GREEN ) )
		{
			p_dst->pOldAlphaData = p_dst->pAlphaData;
			p_dst->flags |= NxNgc::sTexture::TEXTURE_FLAG_OLD_DATA;
		}

		if ( ( p_dst->byte_size < p_src->byte_size ) ||
			 !( p_dst->flags & NxNgc::sTexture::TEXTURE_FLAG_SINGLE_OWNER ) ||
			 ( ( p_dst->flags & NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_MASK ) != NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_GREEN ) )
		{
			if( p_dst->pAlphaData &&
				( p_dst->byte_size < p_src->byte_size ) &&
				( p_dst->flags & NxNgc::sTexture::TEXTURE_FLAG_SINGLE_OWNER ) &&
				( ( p_dst->flags & NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_MASK ) == NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_GREEN ) )
			{
				delete [] p_dst->pAlphaData;
			}
			p_dst->pAlphaData = new uint8[p_src->byte_size];
		}
	}

	// Delete & re-allocate texture space if new image is bigger.
	if ( p_dst->byte_size < p_src->byte_size )
	{
		if( p_dst->pTexelData )
		{
			delete [] p_dst->pTexelData;
		}
		p_dst->pTexelData = new uint8[p_src->byte_size];
		p_dst->byte_size = p_src->byte_size;
	}

	p_dst->BaseWidth	= p_src->BaseWidth;
	p_dst->BaseHeight	= p_src->BaseHeight;
	p_dst->ActualWidth	= p_src->ActualWidth;
	p_dst->ActualHeight	= p_src->ActualHeight;

	p_dst->flags		= ( p_dst->flags & ~NxNgc::sTexture::TEXTURE_FLAG_HAS_HOLES ) | ( p_src->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_HOLES );

	// Copy the pixel data.
	memcpy( p_dst->pTexelData, p_src->pTexelData, p_src->byte_size );
	DCFlushRange ( p_dst->pTexelData, p_src->byte_size );

	// Copy the alpha data if necessary.
	if ( p_src->pAlphaData && p_dst->pAlphaData )
	{
		memcpy( p_dst->pAlphaData, p_src->pAlphaData, p_src->byte_size );
		DCFlushRange ( p_dst->pAlphaData, p_src->byte_size );
		p_dst->flags		= ( p_dst->flags & ~NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA ) | ( p_src->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA );
		p_dst->flags		= ( p_dst->flags & ~NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_MASK ) | NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_GREEN;
		p_dst->flags		= p_dst->flags | NxNgc::sTexture::TEXTURE_FLAG_SINGLE_OWNER;
	}

	// If the replacement texture doesn't have alpha, but the original does, twiddle the flag.
	if ( p_dst->pAlphaData && !p_src->pAlphaData )
	{
		p_dst->flags		= ( p_dst->flags & ~NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA );
	}
	
	// Replacement texture does not mip-map.
	p_dst->Levels = 0;

	// So that if the regular texture is 32-bit, it can have a CMPR texture copied over it.
	p_dst->format		= p_src->format;

	// Flag this texture as having been replaced.
	p_dst->flags |= NxNgc::sTexture::TEXTURE_FLAG_REPLACED;

	Mem::Manager::sHandle().BottomUpHeap()->PopAlign();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcTexture::plat_add_to_vram( void )
{								 	
	// Meaningless on Ngc, added to remove annoying debug stub output.
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcTexture::plat_remove_from_vram( void )
{								 	
	// Meaningless on Ngc, added to remove annoying debug stub output.
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint16	CNgcTexture::plat_get_width() const
{
	if( mp_texture )
	{
		return mp_texture->BaseWidth;
	}
	else
	{
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint16	CNgcTexture::plat_get_height() const
{
	if( mp_texture )
	{
		return mp_texture->BaseHeight;
	}
	else
	{
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CNgcTexture::plat_get_bitdepth() const
{
	switch ( ((GXTexFmt)mp_texture->format) )
	{
		case GX_TF_I4:
		case GX_TF_IA4:
		case GX_TF_CMPR:
			return 4;
			break;

		case GX_TF_I8:
		case GX_TF_IA8:
		case GX_TF_A8:
		case GX_TF_Z8:
			return 8;
			break;
		
		case GX_TF_RGB565:
		case GX_TF_RGB5A3:
		case GX_TF_Z16:
			return 16;
			break;

		default:
		case GX_TF_RGBA8:
		case GX_TF_Z24X8:
			return 32;
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CNgcTexture::plat_get_num_mipmaps() const
{
	return mp_texture->Levels;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CNgcTexture::plat_is_transparent() const
{
	return false;
}

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CTexDict

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcTexDict::CNgcTexDict( uint32 checksum ) : CTexDict( checksum )
{
	// Load nothing
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNgcTexDict::CNgcTexDict( const char *p_tex_dict_name, bool forceTexDictLookup ) : CTexDict( p_tex_dict_name, true )
{
	 LoadTextureDictionary( p_tex_dict_name, forceTexDictLookup );	// the derived class will does this
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNgcTexDict::~CNgcTexDict()
{
	UnloadTextureDictionary();				// the derived class does this
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcTexDict::LoadTextureDictionary( const char *p_tex_dict_name, bool forceTexDictLookup )
{
	NxNgc::LoadTextureFile( p_tex_dict_name, mp_texture_lookup );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcTexDict::LoadTextureDictionaryFromMemory( void *p_mem )
{
	Nx::LoadTextureFileFromMemory( &p_mem, mp_texture_lookup );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcTexDict::UnloadTextureDictionary()
{
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CTexture *CNgcTexDict::plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram )
{
	CNgcTexture *p_texture = new CNgcTexture;
	if( !p_texture->LoadTexture( p_texture_name, sprite ))
	{
		Dbg_Error("Can't load texture %s", p_texture_name);
	}
	return p_texture;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CTexture *CNgcTexDict::plat_reload_texture( const char *p_texture_name )
{
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcTexDict::plat_unload_texture( CTexture *p_texture )
{
	delete p_texture;

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcTexDict::plat_add_texture( CTexture *p_texture )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CNgcTexDict::plat_remove_texture( CTexture *p_texture )
{
	return false;
}



#define MemoryRead( dst, size, num, src )	memcpy(( dst ), ( src ), (( num ) * ( size )));	\
											( src ) += (( num ) * ( size ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void LoadTextureFileFromMemory( void **pp_mem, Lst::HashTable *p_texture_table )
{
	uint8 *p_data = (uint8*)( *pp_mem );

	uint32 temp;
	
	// Open the texture file.
//	void *p_FH = File::Open( Filename, "rb" );
//	if( !p_FH )
//	{
//		Dbg_Message( "Couldn't open texture file %s\n", Filename );
////		return 0;
//		return;
//	}

	// Read the texture file version.
	int version;
	MemoryRead( &version, sizeof( int ), 1, p_data );

	// Read the number of textures.	
	int num_textures;
	MemoryRead( &num_textures, sizeof( int ), 1, p_data );
	
//	NumTextures				+= num_textures;

	int bytes;
	bool need_to_pop;

	NxNgc::sTexture ** p_tex = new (Mem::Manager::sHandle().TopDownHeap()) NxNgc::sTexture *[num_textures];
	unsigned short * p_resolvem = new (Mem::Manager::sHandle().TopDownHeap()) unsigned short[num_textures];
	unsigned short * p_resolvec = new (Mem::Manager::sHandle().TopDownHeap()) unsigned short[num_textures];
	int num_resolve = 0;

	int mem_available;
	for( int t = 0; t < num_textures; ++t )
	{
		need_to_pop = false;
		bytes = sizeof( NxNgc::sTexture );
		if ( g_in_cutscene )
		{
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
			mem_available = Mem::Manager::sHandle().Available();
			if ( bytes < ( mem_available - ( 40 * 1024 ) ) )
			{
				need_to_pop = true;
			}
			else
			{
				Mem::Manager::sHandle().PopContext();
				Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
				mem_available = Mem::Manager::sHandle().Available();
				if ( bytes < ( mem_available - ( 5 * 1024 ) ) )
				{
					need_to_pop = true;
				}
				else
				{
					Mem::Manager::sHandle().PopContext();
					Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ScriptHeap() );
					mem_available = Mem::Manager::sHandle().Available();
					if ( bytes < ( mem_available - ( 40 * 1024 ) ) )
					{
						need_to_pop = true;
					}
					else
					{
						Mem::Manager::sHandle().PopContext();
					}
				}
			}
		}

		NxNgc::sTexture *p_texture = new NxNgc::sTexture;
		
		if ( need_to_pop )
		{
			Mem::Manager::sHandle().PopContext();
		}

		p_tex[t] = p_texture;

		MemoryRead( &p_texture->Checksum,		sizeof( uint32 ), 1, p_data );
		MemoryRead( &temp,		sizeof( uint32 ), 1, p_data );
		p_texture->BaseWidth = (uint16)temp;
		MemoryRead( &temp,		sizeof( uint32 ), 1, p_data );
		p_texture->BaseHeight = (uint16)temp;
		MemoryRead( &temp,		sizeof( uint32 ), 1, p_data );
		p_texture->Levels = (uint16)temp;

		p_texture->ActualWidth = ( p_texture->BaseWidth + 3 ) & ~3;
		p_texture->ActualHeight = ( p_texture->BaseHeight + 3 ) & ~3;

		int tex_format;
		int channel;
		int index;
		MemoryRead( &tex_format, sizeof( uint32 ), 1, p_data );
		channel = ( tex_format >> 8 ) & 0xff;
		index = ( tex_format >> 16 ) & 0xffff;
		tex_format = tex_format & 0xff;

		switch ( tex_format ) {
			case 0:
				p_texture->format = GX_TF_CMPR;
				break;
			case 1:
				p_texture->format = GX_TF_CMPR;
				break;
			case 2:
				p_texture->format = GX_TF_RGBA8;
				break;
			default:
				Dbg_MsgAssert( false, ("Illegal texture format: %d\n", tex_format ));
				break;
		}

		p_texture->flags = 0;

		int has_holes;
		MemoryRead( &has_holes, sizeof( uint32 ), 1, p_data );
		p_texture->flags |= has_holes ? NxNgc::sTexture::TEXTURE_FLAG_HAS_HOLES : 0;

		uint8 * p8[8];
		int mipbytes[8];

		// Read color maps.
		bytes = 0;
		for( uint32 mip_level = 0; mip_level < p_texture->Levels; ++mip_level )
		{
			uint32 texture_level_data_size;
			MemoryRead( &texture_level_data_size,			sizeof( uint32 ), 1, p_data );

			p8[mip_level] = new (Mem::Manager::sHandle().TopDownHeap()) uint8[texture_level_data_size];
			mipbytes[mip_level] = texture_level_data_size;
			bytes += texture_level_data_size;

			MemoryRead( p8[mip_level], texture_level_data_size, 1, p_data );
		}
		// Copy all textures & delete the originals.

		need_to_pop = false;
		if ( g_in_cutscene )
		{
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
			mem_available = Mem::Manager::sHandle().Available();
			if ( bytes < ( mem_available - ( 40 * 1024 ) ) )
			{
				need_to_pop = true;
			}
			else
			{
				Mem::Manager::sHandle().PopContext();
				Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
				mem_available = Mem::Manager::sHandle().Available();
				if ( bytes < ( mem_available - ( 5 * 1024 ) ) )
				{
					need_to_pop = true;
				}
				else
				{
					Mem::Manager::sHandle().PopContext();
					Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ScriptHeap() );
					mem_available = Mem::Manager::sHandle().Available();
					if ( bytes < ( mem_available - ( 40 * 1024 ) ) )
					{
						need_to_pop = true;
					}
					else
					{
						Mem::Manager::sHandle().PopContext();
					}
				}
			}
		}

		Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
		p_texture->pTexelData = new uint8[bytes];
		p_texture->byte_size = bytes;
		Mem::Manager::sHandle().BottomUpHeap()->PopAlign();

		if ( need_to_pop )
		{
			Mem::Manager::sHandle().PopContext();
		}

		uint8 * pTex = p_texture->pTexelData;
		for( uint32 mip_level = 0; mip_level < p_texture->Levels; ++mip_level )
		{
			memcpy( pTex, p8[mip_level], mipbytes[mip_level] );
			delete p8[mip_level];
			pTex += mipbytes[mip_level];
		}

		// Read alpha maps.
		if ( tex_format == 1 )
		{
			if ( channel == 0 )
			{
				bytes = 0;
				for( uint32 mip_level = 0; mip_level < p_texture->Levels; ++mip_level )
				{
					uint32 texture_level_data_size;
					MemoryRead( &texture_level_data_size,			sizeof( uint32 ), 1, p_data );

					p8[mip_level] = new (Mem::Manager::sHandle().TopDownHeap()) uint8[texture_level_data_size];
					mipbytes[mip_level] = texture_level_data_size;
					bytes += texture_level_data_size;

					MemoryRead( p8[mip_level], texture_level_data_size, 1, p_data );
				}
				// Copy all textures & delete the originals.

				need_to_pop = false;
				if ( g_in_cutscene )
				{
					Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
					mem_available = Mem::Manager::sHandle().Available();
					if ( bytes < ( mem_available - ( 40 * 1024 ) ) )
					{
						need_to_pop = true;
					}
					else
					{
						Mem::Manager::sHandle().PopContext();
						Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
						mem_available = Mem::Manager::sHandle().Available();
						if ( bytes < ( mem_available - ( 5 * 1024 ) ) )
						{
							need_to_pop = true;
						}
						else
						{
							Mem::Manager::sHandle().PopContext();
							Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ScriptHeap() );
							mem_available = Mem::Manager::sHandle().Available();
							if ( bytes < ( mem_available - ( 40 * 1024 ) ) )
							{
								need_to_pop = true;
							}
							else
							{
								Mem::Manager::sHandle().PopContext();
							}
						}
					}
				}

				Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
				p_texture->pAlphaData = new uint8[bytes];
				Mem::Manager::sHandle().BottomUpHeap()->PopAlign();

				if ( need_to_pop )
				{
					Mem::Manager::sHandle().PopContext();
				}

				uint8 * pTex = p_texture->pAlphaData;
				for( uint32 mip_level = 0; mip_level < p_texture->Levels; ++mip_level )
				{
					memcpy( pTex, p8[mip_level], mipbytes[mip_level] );
					delete p8[mip_level];
					pTex += mipbytes[mip_level];
				}
				p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA;
			}
			else
			{
				p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA;

				p_resolvem[num_resolve] = t;
				p_resolvec[num_resolve] = index;
				num_resolve++;
			}
			switch ( channel )
			{
				case 0:
				default:
					p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_GREEN;
					break;
				case 1:
					p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_RED;
					break;
				case 2:
					p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_BLUE;
					break;
			}
			p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_SINGLE_OWNER;
		}
		else
		{
			// No unique alpha map.
			p_texture->pAlphaData = NULL;
			p_texture->flags &= ~NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA;
			p_texture->flags |= NxNgc::sTexture::TEXTURE_FLAG_CHANNEL_GREEN;
		}

		// Add this texture to the table.
//		pTextureTable->PutItem( p_texture->Checksum, p_texture );
		need_to_pop = false;

		bytes = sizeof( Nx::CNgcTexture );
		if ( g_in_cutscene )
		{
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
			mem_available = Mem::Manager::sHandle().Available();
			if ( bytes < ( mem_available - ( 40 * 1024 ) ) )
			{
				need_to_pop = true;
			}
			else
			{
				Mem::Manager::sHandle().PopContext();
				Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
				mem_available = Mem::Manager::sHandle().Available();
				if ( bytes < ( mem_available - ( 5 * 1024 ) ) )
				{
					need_to_pop = true;
				}
				else
				{
					Mem::Manager::sHandle().PopContext();
					Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ScriptHeap() );
					mem_available = Mem::Manager::sHandle().Available();
					if ( bytes < ( mem_available - ( 40 * 1024 ) ) )
					{
						need_to_pop = true;
					}
					else
					{
						Mem::Manager::sHandle().PopContext();
					}
				}
			}
		}

		Nx::CNgcTexture *p_Ngc_texture = new Nx::CNgcTexture();

		if ( need_to_pop )
		{
			Mem::Manager::sHandle().PopContext();
		}

		p_Ngc_texture->SetEngineTexture( p_texture );
		if ( p_texture_table )
		{
			p_texture_table->PutItem( p_texture->Checksum, p_Ngc_texture );
		}
	}

	// Resolve alpha maps.
	for ( int r = 0; r < num_resolve; r++ )
	{
		p_tex[p_resolvem[r]]->pAlphaData = p_tex[p_resolvec[r]]->pAlphaData;
		p_tex[p_resolvec[r]]->flags &= ~NxNgc::sTexture::TEXTURE_FLAG_SINGLE_OWNER;
	}

	delete p_tex;
	delete p_resolvem;
	delete p_resolvec;
//	return pTextureTable;
}

} // Namespace Nx  			
				
				



================================================
FILE: Code/Gfx/NGC/p_nxtexture.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.h

#ifndef	__GFX_P_NX_TEXTURE_H__
#define	__GFX_P_NX_TEXTURE_H__

#include 	"Gfx/NxTexture.h"
#include 	"Gfx/Ngc/nx/texture.h"
#include 	"Gfx/Ngc/nx/material.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Machine specific implementation of the CTexture
class	CNgcTexture : public CTexture
{
public:
								CNgcTexture();
	virtual						~CNgcTexture();

	NxNgc::sTexture			*GetEngineTexture() const;
	void						SetEngineTexture( NxNgc::sTexture *p_texture )	{ mp_texture = p_texture; }


private:		// It's all private, as it is machine specific
	virtual bool				plat_load_texture( const char *p_texture_name, bool sprite, bool alloc_vram );
	virtual bool				plat_replace_texture( CTexture *p_texture );
	virtual bool				plat_add_to_vram( void );
	virtual bool				plat_remove_from_vram( void );

	virtual uint16				plat_get_width() const;
	virtual uint16				plat_get_height() const;
	virtual uint8				plat_get_bitdepth() const;
	virtual uint8				plat_get_num_mipmaps() const;
	virtual bool				plat_is_transparent() const;

	// The actual data in the engine
	NxNgc::sTexture *			mp_texture;

};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline NxNgc::sTexture	*CNgcTexture::GetEngineTexture() const
{
	return mp_texture;
}



//////////////////////////////////////////////////////////////////////////////////
// Machine specific implementation of the CMaterial
class	CNgcMaterial : public CMaterial
{
public:
								CNgcMaterial();
	virtual						~CNgcMaterial();

private:
	Image::RGBA					plat_get_rgba() const;
	void						plat_set_rgba(Image::RGBA rgba);
	void						plat_set_texture();

	Image::RGBA					m_rgba;

	// The actual data in the engine
	NxNgc::sMaterial *			mp_material;
};

//////////////////////////////////////////////////////////////////////////////////
// Machine specific implementation of the CTexDict
class	CNgcTexDict : public CTexDict
{
public:
								CNgcTexDict( uint32 checksum );		// loads nothing
								CNgcTexDict(const char *p_tex_dict_name, bool forceTexDictLookup = false);
	virtual						~CNgcTexDict();

	bool						LoadTextureDictionary(const char *p_tex_dict_name, bool forceTexDictLookup = false);
	bool						LoadTextureDictionaryFromMemory( void *p_mem );
	bool						UnloadTextureDictionary();

private:
	// Platform-specific calls
	virtual CTexture *			plat_load_texture( const char *p_texture_name, bool sprite, bool alloc_vram );
	virtual CTexture *			plat_reload_texture(const char *p_texture_name);
	virtual bool				plat_unload_texture(CTexture *p_texture);
	virtual void				plat_add_texture(CTexture *p_texture);
	virtual bool				plat_remove_texture(CTexture *p_texture);

//	NxNgc::sScene *			mp_tex_dict;		// Platform-dependent data

};

void LoadTextureFile( const char *Filename, Lst::HashTable *p_texture_table );
void LoadTextureFileFromMemory( void **pp_mem, Lst::HashTable *p_texture_table );

} // Namespace Nx  			

#endif



================================================
FILE: Code/Gfx/NGC/p_nxviewman.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxViewMan.cpp - Ngc platform specific interface to CViewportManager
//
// This is Ngc SPECIFIC!!!!!!  So might get a bit messy

#include 

#include "gfx/NxViewMan.h"
#include "gfx/NGC/p_NxViewport.h"

namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CViewport *		CViewportManager::s_plat_create_viewport(const Mth::Rect* rect, Gfx::Camera* cam)
{
	return new CNgcViewport(rect, cam);
}

} 
 



================================================
FILE: Code/Gfx/NGC/p_nxweather.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxWeather.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  6/2/2003
//****************************************************************************

#include 
#include "gfx/ngc/nx/render.h"
#include "gfx/ngc/nx/nx_init.h"

#include "gfx/ngc/nx/line.h"
#include 
#include 

#include "gfx/ngc/nx/mesh.h"

#include "gfx/ngc/p_nxweather.h"
#include "gfx/ngc/p_nxparticle.h"

#include 
#include 

#include 
#include 
#include "gfx/nx.h"
#include 

#include "dolphin/base/ppcwgpipe.h"
#include "dolphin/gx/gxvert.h"

#define FEELER_START 50000.0f

unsigned char grid_bytes[45*1024];

#define RENDER_DIST 16
//#define RAIN_HEIGHT 2000
//#define RAIN_FRAMES 40
//#define RAIN_LENGTH 100.0f
#define SEQ_START 411		// Between 1 and 4095.
#define SEQ_MASK 0x0240
//#define SEQ_MASK 0x0ca0
//#define NUM_DROPS_PER_FRAME 25

//1-3: 0x03
//1-7: 0x06
//1-15: 0x0C
//1-31: 0x14
//1-63: 0x30
//1-127: 0x60
//1-255: 0xB8
//1-511: 0x0110
//1-1023: 0x0240
//1-2047: 0x0500
//1-4095: 0x0CA0
//1-8191: 0x1B00
//1-16383: 0x3500
//1-32767: 0x6000
//1-65535: 0xB400
//0x00012000
//0x00020400
//0x00072000
//0x00090000
//0x00140000
//0x00300000
//0x00400000
//0x00D80000
//0x01200000
//0x03880000
//0x07200000
//0x09000000
//0x14000000
//0x32800000
//0x48000000
//0xA3000000


//#define DEBUG_LINES

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcWeather::CNgcWeather()
{
	mp_roof_height_index = NULL;
	mp_rain_texture = NULL;
	mp_snow_texture = NULL;

	m_rain_blend = get_texture_blend( CRCD(0xa86285a1,"fixadd") );		// FixAdd / 64
	m_splash_blend = get_texture_blend( CRCD(0xa86285a1,"fixadd") );		// FixAdd / 64
	m_snow_blend = get_texture_blend( CRCD(0xa86285a1,"fixadd") );		// FixAdd / 64

	m_rain_blend_fix = 64;
	m_splash_blend_fix = 64;
	m_snow_blend_fix = 64;

	m_rain_rate = 0.0f;
	m_splash_rate = 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CNgcWeather::~CNgcWeather()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcWeather::plat_update_grid( void )
{
	m_system_active = false;
	// Get super sector manager.
	SSec::Manager *ss_man;
	Mth::Line line;
	ss_man = Nx::CEngine::sGetNearestSuperSectorManager( line );		// Line is ignored, 1st manager is returned.
	if ( !ss_man ) return;

	// Calculate the size of the world in cels.
	int min_x = ( ((int)ss_man->GetWorldBBox()->GetMin()[X]) / WEATHER_CEL_SIZE ) - 1;
	int max_x = ( ((int)ss_man->GetWorldBBox()->GetMax()[X]) / WEATHER_CEL_SIZE ) + 1;
	int min_z = ( ((int)ss_man->GetWorldBBox()->GetMin()[Z]) / WEATHER_CEL_SIZE ) - 1;
	int max_z = ( ((int)ss_man->GetWorldBBox()->GetMax()[Z]) / WEATHER_CEL_SIZE ) + 1;

	// Define a maximum...
	if ( ( max_x - min_x ) > 350 )
	{
		int wid = ( max_x - min_x );
		int excess = ( wid - 350 );
		min_x += ( excess / 2 );
		max_x -= ( excess / 2 );
	}

	if ( ( max_z - min_z ) > 300 )
	{
		int wid = ( max_z - min_z );
		int excess = ( wid - 300 );
		min_z += ( excess / 2 );
		max_z -= ( excess / 2 );
	}

	// This is the actual width;
	m_width = ( max_x - min_x ) + 1;
	m_height = ( max_z - min_z ) + 1;

	// Allocate a new piece of memory for the grid.
//	if ( mp_roof_height_index ) delete mp_roof_height_index;
//	mp_roof_height_index = new unsigned char[m_width * m_height];

//	mp_roof_height_index = grid_bytes;

	m_min_x = ( min_x * WEATHER_CEL_SIZE );
	m_min_z = ( min_z * WEATHER_CEL_SIZE );

	// Temporary buffer for the raw array.
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	unsigned char * p8 = new unsigned char[m_width*m_height];
	Mem::Manager::sHandle().PopContext();

	// Go through and get the height for each cel corner.
	CFeeler feeler;
	int num_heights = 0;
	int xx, zz;
	for ( zz = min_z; zz <= max_z; zz++ )
	{
		for ( xx = min_x; xx <= max_x; xx++ )
		{
			// The cel to fill.
			int cel = ( ( zz - min_z ) * m_width ) + ( xx - min_x );

			// The position to check.
			Mth::Vector pos;
			pos[X] = (float)( xx * WEATHER_CEL_SIZE );
			pos[Y] = FEELER_START;
			pos[Z] = (float)( zz * WEATHER_CEL_SIZE );
			pos[W] = 1.0f;

			feeler.SetStart( pos );
			pos[Y] = -FEELER_START;
			feeler.SetEnd( pos );

			// Get the y position.
			float y;
			if ( feeler.GetCollision( false, false ) )		// No movables, nearest collision.
			{
				y = feeler.GetPoint()[Y];
			}
			else
			{
				y = FEELER_START;
			}

			// See if a close enough y pos already exists.
			int found = -1;
			for ( int lp = 0; lp < num_heights; lp++ )
			{
				if ( fabsf( ( m_roof_height[lp] - y ) ) < HEIGHT_TOLERANCE )
				{
					found = lp;
					break;
				}
			}

			// Fill in the cel.
			if ( found != -1 )
			{
				// Existing height.
				p8[cel] = found;
			}
			else
			{
				// New height.
				p8[cel] = num_heights;
				m_roof_height[num_heights] = y;
				num_heights++;
			}
		}
	}

	// Work out highest height for each cel.
	for ( zz = min_z; zz <= max_z; zz++ )
	{
		for ( xx = min_x; xx <= max_x; xx++ )
		{
			// The cel to fill.
			int cel = ( ( zz - min_z ) * m_width ) + ( xx - min_x );
			int celx = ( ( zz - min_z ) * m_width ) + ( ( xx + 1 ) - min_x );
			int celz = ( ( ( zz + 1 ) - min_z ) * m_width ) + ( xx - min_x );
			int celxz = ( ( ( zz + 1 ) - min_z ) * m_width ) + ( ( xx + 1 ) - min_x );

			if ( m_roof_height[p8[celx]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celx];
			if ( m_roof_height[p8[celz]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celz];
			if ( m_roof_height[p8[celxz]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celxz];
		}
	}

	// Create a sparse array.
	mp_roof_row = (sRowEntry *)grid_bytes;
	mp_roof_height_index = (unsigned char *)&mp_roof_row[m_height];

	// 0 = offset
	// 1 = width
	// 2 = index

	unsigned short index = 0;
	for ( zz = 0; zz <= m_height; zz++ )
	{
		unsigned short start = 0;
		unsigned short end = m_width - 1;

		// Scan to find the start.
		bool start_set = false;
		for ( xx = 0; xx < m_width; xx++ )
		{
			int cel = ( zz * m_width ) + xx;

			if ( m_roof_height[p8[cel]] != FEELER_START )
			{
				if ( !start_set )
				{
					// Set start value.
					start = xx;
					start_set = true;
				}
				else
				{
					// Set end value.
					end = xx;
				}

			}
		}

		// Copy data & set row entry.
		if ( start < end )
		{
			mp_roof_row[zz].start = start;
			mp_roof_row[zz].end = end;
			mp_roof_row[zz].index = index;
			for ( xx = start; xx <= end ; xx++ )
			{
				int cel = ( zz * m_width ) + xx;

				mp_roof_height_index[index] = p8[cel];
				index++;
			}
		}
		else
		{
			// Row doesn't exist.
			mp_roof_row[zz].start = 16384;
			mp_roof_row[zz].end = 0;
			mp_roof_row[zz].index = 0;
		}
	}

	delete p8;

#ifndef __NOPT_FINAL__
	int new_size = ( m_height * 6 ) + index;

	printf( "Grid Size: Old: %d New: %d Num Heights: %d\n", ( m_width * m_height ), new_size, num_heights );
#endif		// __NOPT_FINAL__
//	printf( "Num Weather Grid Heights: %d (%d x %d)\n", num_heights, m_width, m_height );

	// Set all drip time counters to 255.
	// 255 means the drop won't be rendered.
	// Setting to anything other than 255 will mean that it will increment to 255 and stop.
	for ( int lp = 0; lp < ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ); lp++ )
	{
		m_drop_time[lp] = 255;
		m_x_offset[lp] = Mth::Rnd( 255 );
		m_z_offset[lp] = Mth::Rnd( 255 );
	}
	m_active_drops = 0;

	m_seq = SEQ_START;

	Nx::CTexture *p_texture;
	Nx::CNgcTexture *p_Ngc_texture;

	// Set Rain Texture.
	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( CRCD(0x45c7eb0f,"splash") );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_rain_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	// Set Snow Texture.
	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( CRCD(0xc97c5a4c,"snow") );
	p_Ngc_texture = static_cast( p_texture );
	if ( p_Ngc_texture )
	{
		mp_snow_texture = p_Ngc_texture->GetEngineTexture(); 
	}

	m_system_active = true;

	// Zero out the splashes.
	for ( int sp = 0; sp < NUM_SPLASH_ACTIVE; sp++ )
	{
		m_splash_current_life[sp] = 0;
	}
	m_current_splash = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcWeather::plat_process( float delta_time )
{
	if ( !m_system_active ) return;

	if ( m_raining )
	{
		// It's raining.
		float rate = m_rain_drops_per_frame;
		if ( rate > (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_rain_frames ) ) rate = (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_rain_frames );

		float last = m_rain_rate;
		m_rain_rate += rate;
		int new_drops = (int)m_rain_rate - (int)last;

		for ( int lp = 0; lp < new_drops; lp++ )
		{
			// If this cel is not inactive, we caught up with ourselves.
			if ( m_drop_time[m_seq] != 255 ) break;

			// Setup the drop.
			m_drop_time[m_seq] = ( 254 - m_rain_frames );
			m_x_offset[m_seq] = Mth::Rnd( 255 );
			m_z_offset[m_seq] = Mth::Rnd( 255 );
			m_active_drops++;

			// Calculate next sequence value. Notice hack to get entry 0 into the sequence.
			switch ( m_seq )
			{
				case SEQ_START:
					m_seq = 0;
					break;
				case 0:
					m_seq = SEQ_START;
					// Fall through to default case...
				default:
					if ( m_seq & 1 )
					{
						m_seq = ( m_seq >> 1 ) ^ SEQ_MASK;
					}
					else
					{
						m_seq = ( m_seq >> 1 );
					}
					break;
			}
		}
	}
	else
	{
		// It's snowing.
		float rate = m_snow_flakes_per_frame;
		if ( rate > (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_snow_frames ) ) rate = (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_snow_frames );

		float last = m_snow_rate;
		m_snow_rate += rate;
		int new_drops = (int)m_snow_rate - (int)last;

		for ( int lp = 0; lp < new_drops; lp++ )
		{
			// If this cel is not inactive, we caught up with ourselves.
			if ( m_drop_time[m_seq] != 255 ) break;

			// Setup the drop.
			m_drop_time[m_seq] = ( 254 - m_snow_frames );
			m_x_offset[m_seq] = Mth::Rnd( 255 );
			m_z_offset[m_seq] = Mth::Rnd( 255 );
			m_active_drops++;

			// Calculate next sequence value. Notice hack to get entry 0 into the sequence.
			switch ( m_seq )
			{
				case SEQ_START:
					m_seq = 0;
					break;
				case 0:
					m_seq = SEQ_START;
					// Fall through to default case...
				default:
					if ( m_seq & 1 )
					{
						m_seq = ( m_seq >> 1 ) ^ SEQ_MASK;
					}
					else
					{
						m_seq = ( m_seq >> 1 );
					}
					break;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcWeather::plat_render_snow( float skx, float skz )
{
	// Early out if no drops to draw.
	if ( !m_active_drops ) return;

	// Get skater position.
	int x = (int)( ( skx - m_min_x ) / WEATHER_CEL_SIZE );
	int z = (int)( ( skz - m_min_z ) / WEATHER_CEL_SIZE );

	// Calculate area to render.
	int sx = x - RENDER_DIST;
	int ex = x + DROP_SIZE;	//RENDER_DIST;
	int sz = z - RENDER_DIST;
	int ez = z + DROP_SIZE;	//RENDER_DIST;

	// Clip z values.
	if ( ez < 0 ) return;
	if ( sz > ( m_height - 1 ) ) return;

//	if ( sz < 0 ) sz = 0;
//	if ( ez > ( m_height - 1 ) ) ez = ( m_height - 1 );

	GX::UploadTexture(  mp_snow_texture->pTexelData,
						mp_snow_texture->ActualWidth,
						mp_snow_texture->ActualHeight,
						GX_TF_CMPR,
						GX_CLAMP,
						GX_CLAMP,
						GX_FALSE,
						GX_LINEAR,
						GX_LINEAR,
						0.0f,
						0.0f,
						0.0f,
						GX_FALSE,
						GX_TRUE,
						GX_ANISO_1,
						GX_TEXMAP0 ); 
	GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, mp_snow_texture->ActualWidth, mp_snow_texture->ActualHeight );

	if ( mp_snow_texture->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA )
	{
		GX::UploadTexture(  mp_snow_texture->pAlphaData,
							mp_snow_texture->ActualWidth,
							mp_snow_texture->ActualHeight,
							GX_TF_CMPR,
							GX_CLAMP,
							GX_CLAMP,
							GX_FALSE,
							GX_LINEAR,
							GX_LINEAR,
							0.0f,
							0.0f,
							0.0f,
							GX_FALSE,
							GX_TRUE,
							GX_ANISO_1,
							GX_TEXMAP1 ); 
		GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, mp_snow_texture->ActualWidth, mp_snow_texture->ActualHeight );
		GX::SetTexChanTevIndCull( 2, 1, 2, 0, GX_CULL_NONE );
	}
	else
	{
		GX::SetTexChanTevIndCull( 1, 1, 1, 0, GX_CULL_NONE );
	}

	GX::SetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR0A0 );

	GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
										   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV,
										   GX_TEV_SWAP0, GX_TEV_SWAP0 );

	GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_RASC, GX_CC_ZERO, 
									   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );

	if ( mp_snow_texture->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA )
	{
		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO,
											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV,
											   GX_TEV_SWAP0, GX_TEV_SWAP1 );

		GX::SetTevColorInOp( GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_CPREV,
										   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );

	}

	switch ( m_snow_blend )
	{
		case NxNgc::vBLEND_MODE_ADD:
		case NxNgc::vBLEND_MODE_ADD_FIXED:
			GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
		case NxNgc::vBLEND_MODE_SUBTRACT:
		case NxNgc::vBLEND_MODE_SUB_FIXED:
			GX::SetBlendMode ( GX_BM_SUBTRACT, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
		case NxNgc::vBLEND_MODE_BLEND:
		case NxNgc::vBLEND_MODE_BLEND_FIXED:
			GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
		case NxNgc::vBLEND_MODE_BRIGHTEN:
		case NxNgc::vBLEND_MODE_BRIGHTEN_FIXED:
			GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
		case NxNgc::vBLEND_MODE_MODULATE_FIXED:
		case NxNgc::vBLEND_MODE_MODULATE:
			GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
		case NxNgc::vBLEND_MODE_BLEND_PREVIOUS_MASK:
		case NxNgc::vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
		case NxNgc::vBLEND_MODE_DIFFUSE:
		default:
			GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
	}

	GX::SetChanCtrl( GX_COLOR0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
	GX::SetChanCtrl( GX_ALPHA0, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 
	
	switch ( m_snow_blend )
	{
		case NxNgc::vBLEND_MODE_ADD_FIXED:
		case NxNgc::vBLEND_MODE_SUB_FIXED:
		case NxNgc::vBLEND_MODE_BLEND_FIXED:
		case NxNgc::vBLEND_MODE_BRIGHTEN_FIXED:
		case NxNgc::vBLEND_MODE_MODULATE_FIXED:
			{
				uint8 fix = m_snow_blend_fix >= 128 ? 255 : m_snow_blend_fix * 2;
				GX::SetChanMatColor( GX_COLOR0A0, (GXColor){fix,fix,fix,fix} );
			}
			break;
		default:
			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
			break;
	}
	
	GXColor col = (GXColor){ m_snow_color.r, m_snow_color.g, m_snow_color.b, m_snow_color.a };
	GX::SetChanAmbColor( GX_COLOR0A0, col );





	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );

	float minx = m_min_x;
	float minz = m_min_z;
//	float rlength = m_rain_length;
	
	// Calculate drop height list.
	int lp;
	float y_off[256];

	for ( lp = ( 254 - m_snow_frames ); lp < 256; lp++ )
	{
		y_off[lp] = m_snow_height - ( ( (float)( ( lp - 254 ) + m_snow_frames ) / (float)m_snow_frames ) * m_snow_height );
	}

	// Calculate xz offset list.
	float xz_off[256];

	for ( lp = 0; lp < 256; lp++ )
	{
		xz_off[lp] = ( (float)lp * (float)( WEATHER_CEL_SIZE / 2 ) ) / 255.0f;
	}

	// Calculate sine wave.
	float sin_off[256];

	for ( lp = 0; lp < 256; lp++ )
	{
		sin_off[lp] = sinf( Mth::PI * 2.0f * ( (float)lp / 256.0f ) ) * ( WEATHER_CEL_SIZE / 2 );
//		sin_off[lp] = sinf( Mth::PI * 2.0f * (float)lp ) * 128.0f;
	}



	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;

	NsVector	up( 0.0f, 1.0f, 0.0f );
	NsVector screen_right;
	NsVector screen_up;
	screen_right.cross( *p_matrix->getAt(), up );
	screen_up.cross( screen_right, *p_matrix->getAt());

	screen_right.normalize();
	screen_up.normalize();
	
	screen_right.x *= m_snow_size;
	screen_right.y *= m_snow_size;
	screen_right.z *= m_snow_size;

	screen_up.x *= m_snow_size;
	screen_up.y *= m_snow_size;
	screen_up.z *= m_snow_size;

	for ( int lzz = sz; lzz <= ez; lzz++ )
	{
		int zz = ( lzz < 0 ) ? 0 : ( lzz > ( m_height - 1 ) ) ? ( m_height - 1 ) : lzz;

		if ( mp_roof_row[zz].start == 16384 ) continue;

//		// Calculate actual span to scan.
//		int rsx = sx > mp_roof_row[zz].start ? sx : mp_roof_row[zz].start;
//		int rex = ex < mp_roof_row[zz].end ? ex : mp_roof_row[zz].end;
//
//		// Start position.
//		float vx = ( rsx * WEATHER_CEL_SIZE ) + minx;
//		float vz = ( zz * WEATHER_CEL_SIZE ) + minz;

		float vx = ( (float)sx * (float)WEATHER_CEL_SIZE ) + minx;
		float vz = ( (float)zz * (float)WEATHER_CEL_SIZE ) + minz;

		int cel = mp_roof_row[zz].index + ( sx - mp_roof_row[zz].start );

		int drop_cel_z = ( ( zz & ( DROP_SIZE - 1 ) ) << DROP_SIZE_SHIFT );

//		for ( int xx = rsx; xx <= rex; xx++, cel++ )
		for ( int lxx = sx; lxx <= ex; lxx++, cel++ )
		{
			int xx = ( lxx > mp_roof_row[zz].start ) ? ( ( lxx < mp_roof_row[zz].end ) ? lxx : mp_roof_row[zz].end ) : mp_roof_row[zz].start;

			// Get the current drop value. Skip this one if it's inactive.
			int drop_cel = drop_cel_z + ( xx & ( DROP_SIZE - 1 ) );

			vx += (float)WEATHER_CEL_SIZE;
			float vy = m_roof_height[mp_roof_height_index[cel]];

			for ( int d = 0; d < DROP_LAYERS; d++, drop_cel += ( DROP_SIZE * DROP_SIZE ) )
			{
				if ( m_drop_time[drop_cel] == 255 ) continue;

				// Create the position for rendering.
				float v0x = vx + xz_off[m_x_offset[drop_cel]] + sin_off[(m_z_offset[drop_cel]+m_drop_time[drop_cel])&255];
				float v0y = vy + y_off[m_drop_time[drop_cel]] + m_snow_size;
				float v0z = vz + xz_off[m_z_offset[drop_cel]] + sin_off[(m_x_offset[drop_cel]+m_drop_time[drop_cel])&255];

				float v1x = v0x + screen_up.x;
				float v1y = v0y + screen_up.y;
				float v1z = v0z + screen_up.z;

				v0x -= screen_up.x;
				v0y -= screen_up.y;
				v0z -= screen_up.z;

				// Send coordinates.
				GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
					GXWGFifo.f32 = v1x - screen_right.x;
					GXWGFifo.f32 = v1y - screen_right.y;
					GXWGFifo.f32 = v1z - screen_right.z;
					GXWGFifo.f32 = 0.0f;
					GXWGFifo.f32 = 0.0f;

					GXWGFifo.f32 = v1x + screen_right.x;
					GXWGFifo.f32 = v1y + screen_right.y;
					GXWGFifo.f32 = v1z + screen_right.z;
					GXWGFifo.f32 = 1.0f;
					GXWGFifo.f32 = 0.0f;

					GXWGFifo.f32 = v0x + screen_right.x;
					GXWGFifo.f32 = v0y + screen_right.y;
					GXWGFifo.f32 = v0z + screen_right.z;
					GXWGFifo.f32 = 1.0f;
					GXWGFifo.f32 = 1.0f;

					GXWGFifo.f32 = v0x - screen_right.x;
					GXWGFifo.f32 = v0y - screen_right.y;
					GXWGFifo.f32 = v0z - screen_right.z;
					GXWGFifo.f32 = 0.0f;
					GXWGFifo.f32 = 1.0f;
//					GX::Position3f32( v1x - screen_right.x, v1y - screen_right.y, v1z - screen_right.z );
//					GX::Position3f32( v1x + screen_right.x, v1y + screen_right.y, v1z + screen_right.z );
//					GX::Position3f32( v0x + screen_right.x, v0y + screen_right.y, v0z + screen_right.z );
//					GX::Position3f32( v0x - screen_right.x, v0y - screen_right.y, v0z - screen_right.z );
				GX::End();
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcWeather::plat_render_rain( float skx, float skz )
{
	// Early out if no drops to draw.
	if ( !m_active_drops ) return;

	// Get skater position.
	int x = (int)( ( skx - m_min_x ) / WEATHER_CEL_SIZE );
	int z = (int)( ( skz - m_min_z ) / WEATHER_CEL_SIZE );

	// Calculate area to render.
	int sx = x - RENDER_DIST;
	int ex = x + DROP_SIZE;	//RENDER_DIST;
	int sz = z - RENDER_DIST;
	int ez = z + DROP_SIZE;	//RENDER_DIST;

	// Clip z values.
	if ( ez < 0 ) return;
	if ( sz > ( m_height - 1 ) ) return;

//	if ( sz < 0 ) sz = 0;
//	if ( ez > ( m_height - 1 ) ) ez = ( m_height - 1 );

	GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE );
	GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);

	GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
										   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_4, GX_ENABLE, GX_TEVPREV,
										   GX_TEV_SWAP0, GX_TEV_SWAP0 );

	GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC,
									   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_4, GX_ENABLE, GX_TEVPREV );

	// Set blend mode for base layer.
	switch ( m_rain_blend )
	{
		case NxNgc::vBLEND_MODE_ADD:
		case NxNgc::vBLEND_MODE_ADD_FIXED:
			GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
		case NxNgc::vBLEND_MODE_SUBTRACT:
		case NxNgc::vBLEND_MODE_SUB_FIXED:
			GX::SetBlendMode ( GX_BM_SUBTRACT, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
		case NxNgc::vBLEND_MODE_BLEND:
		case NxNgc::vBLEND_MODE_BLEND_FIXED:
			GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
		case NxNgc::vBLEND_MODE_BRIGHTEN:
		case NxNgc::vBLEND_MODE_BRIGHTEN_FIXED:
			GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
		case NxNgc::vBLEND_MODE_MODULATE_FIXED:
		case NxNgc::vBLEND_MODE_MODULATE:
			GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
		case NxNgc::vBLEND_MODE_BLEND_PREVIOUS_MASK:
		case NxNgc::vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
		case NxNgc::vBLEND_MODE_DIFFUSE:
		default:
			GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
			break;
	}

	GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
	
	switch ( m_rain_blend )
	{
		case NxNgc::vBLEND_MODE_ADD_FIXED:
		case NxNgc::vBLEND_MODE_SUB_FIXED:
		case NxNgc::vBLEND_MODE_BLEND_FIXED:
		case NxNgc::vBLEND_MODE_BRIGHTEN_FIXED:
		case NxNgc::vBLEND_MODE_MODULATE_FIXED:
			{
				uint8 fix = m_rain_blend_fix >= 128 ? 255 : m_rain_blend_fix * 2;
				GX::SetChanMatColor( GX_COLOR0A0, (GXColor){fix,fix,fix,fix} );
			}
			break;
		default:
			GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
			break;
	}

	GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_CLR0, GX_DIRECT );

	float minx = m_min_x;
	float minz = m_min_z;
	float rlength = m_rain_length;
	
	// Calculate drop height list.
	int lp;
	float y_off[256];

	for ( lp = ( 254 - m_rain_frames ); lp < 256; lp++ )
	{
		y_off[lp] = ( m_rain_height - ( (float)( ( lp - 254 ) + m_rain_frames ) / (float)m_rain_frames ) * m_rain_height );
	}

	// Calculate xz offset list.
	float xz_off[256];

	for ( lp = 0; lp < 256; lp++ )
	{
		xz_off[lp] = ( (float)lp * (float)WEATHER_CEL_SIZE ) / 255.0f;
	}

	GXColor colb = (GXColor){ m_rain_bottom_color.r, m_rain_bottom_color.g, m_rain_bottom_color.b, m_rain_bottom_color.a };
	GXColor colt = (GXColor){ m_rain_top_color.r, m_rain_top_color.g, m_rain_top_color.b, m_rain_top_color.a };

	for ( int lzz = sz; lzz <= ez; lzz++ )
	{
		int zz = ( lzz < 0 ) ? 0 : ( lzz > ( m_height - 1 ) ) ? ( m_height - 1 ) : lzz;

		if ( mp_roof_row[zz].start == 16384 ) continue;

//		// Calculate actual span to scan.
//		int rsx = sx > mp_roof_row[zz].start ? sx : mp_roof_row[zz].start;
//		int rex = ex < mp_roof_row[zz].end ? ex : mp_roof_row[zz].end;
//
//		// Start position.
//		float vx = ( rsx * WEATHER_CEL_SIZE ) + minx;
//		float vz = ( zz * WEATHER_CEL_SIZE ) + minz;

		float vx = (float)( sx << WEATHER_CEL_SIZE_SHIFT ) + minx;
		float vz = (float)( zz << WEATHER_CEL_SIZE_SHIFT ) + minz;

		int cel = mp_roof_row[zz].index + ( sx - mp_roof_row[zz].start );

		int drop_cel_z = ( ( zz & ( DROP_SIZE - 1 ) ) << DROP_SIZE_SHIFT );

//		for ( int xx = rsx; xx <= rex; xx++, cel++ )
		for ( int lxx = sx; lxx <= ex; lxx++, cel++ )
		{
			int xx = ( lxx > mp_roof_row[zz].start ) ? ( ( lxx < mp_roof_row[zz].end ) ? lxx : mp_roof_row[zz].end ) : mp_roof_row[zz].start;

			// Get the current drop value. Skip this one if it's inactive.
			int drop_cel = drop_cel_z + ( xx & ( DROP_SIZE - 1 ) );

			vx += (float)WEATHER_CEL_SIZE;
			float vy = m_roof_height[mp_roof_height_index[cel]];

			for ( int d = 0; d < DROP_LAYERS; d++, drop_cel += ( DROP_SIZE * DROP_SIZE ) )
			{
				if ( m_drop_time[drop_cel] == 255 ) continue;

				// Create the position for rendering.
				float v0x = vx + xz_off[m_x_offset[drop_cel]];
				float v0y = vy + y_off[m_drop_time[drop_cel]]; 
				float v0z = vz + xz_off[m_z_offset[drop_cel]];

				float v1y = v0y + rlength;

				// Send coordinates.
				GX::Begin( GX_LINES, GX_VTXFMT0, 2 ); 
					GX::Position3f32( v0x, v0y, v0z );
					GX::Color1u32( *((uint32*)&colb) );
					GX::Position3f32( v0x, v1y, v0z );
					GX::Color1u32( *((uint32*)&colt) );
				GX::End();
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcWeather::plat_render_splashes( float skx, float skz )
{
	// Create a new splash if required.
	float last = m_splash_rate;
	m_splash_rate += m_splash_per_frame;
	int new_splashes = (int)m_splash_rate - (int)last;

	if ( new_splashes )
	{
		CFeeler feeler;
		Mth::Vector pos;

		pos[X] = NxNgc::EngineGlobals.camera.getPosX();	// + ( Mth::Rnd( ( WEATHER_CEL_SIZE * RENDER_DIST * 2 ) ) ) - ( WEATHER_CEL_SIZE * RENDER_DIST );
		pos[Y] = FEELER_START;
		pos[Z] = NxNgc::EngineGlobals.camera.getPosZ();	// + ( Mth::Rnd( ( WEATHER_CEL_SIZE * RENDER_DIST * 2 ) ) ) - ( WEATHER_CEL_SIZE * RENDER_DIST ); 
		pos[W] = 1.0f;

		Mth::Vector dir;
		dir[X] = skx - NxNgc::EngineGlobals.camera.getPosX();
		dir[Y] = 0.0f;
		dir[Z] = skz - NxNgc::EngineGlobals.camera.getPosZ();
		dir[W] = 1.0f;
		dir.Normalize();

		// Add distance.
		float r1 = (float)Mth::Rnd( 32768 ) / 32768.0f;
		pos += ( dir * ( (float)WEATHER_CEL_SIZE * RENDER_DIST * r1 ) );

		// Add lateral.
		Mth::Vector lat;
		lat[X] = dir[Z];
		lat[Y] = 0.0f;
		lat[Z] = -dir[X];
		lat[W] = 1.0f;

		float r2 = 1.0f - ( (float)Mth::Rnd( 32768 ) / 32768.0f );
		if ( m_current_splash & 1 )
		{
			pos += ( lat * ( (float)WEATHER_CEL_SIZE * RENDER_DIST * r1 * r2 ) );
		}
		else
		{
			pos += ( lat * ( (float)WEATHER_CEL_SIZE * RENDER_DIST * r1 * -r2 ) );
		}

		feeler.SetStart( pos );
		pos[Y] = -FEELER_START;
		feeler.SetEnd( pos );

		// Get the y position.
		float y;
		if ( feeler.GetCollision( false, false ) )		// No movables, nearest collision.
		{
			y = feeler.GetPoint()[Y];
		}
		else
		{
			y = FEELER_START;
		}

		m_splash_x[m_current_splash] = pos[X];
		m_splash_y[m_current_splash] = y + m_splash_size;
		m_splash_z[m_current_splash] = pos[Z];
		m_splash_current_life[m_current_splash] = m_splash_life;

		m_current_splash++;
		m_current_splash %= NUM_SPLASH_ACTIVE;
	}
	
	// Count active splashes.
	int splashes = 0;
	for ( int spl = 0; spl < NUM_SPLASH_ACTIVE; spl++ )
	{
		if ( m_splash_current_life[spl] != 0 ) splashes++;
	}

	if ( splashes )
	{
		GX::UploadTexture(  mp_rain_texture->pTexelData,
							mp_rain_texture->ActualWidth,
							mp_rain_texture->ActualHeight,
							GX_TF_CMPR,
							GX_CLAMP,
							GX_CLAMP,
							GX_FALSE,
							GX_LINEAR,
							GX_LINEAR,
							0.0f,
							0.0f,
							0.0f,
							GX_FALSE,
							GX_TRUE,
							GX_ANISO_1,
							GX_TEXMAP0 ); 
		GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, mp_rain_texture->ActualWidth, mp_rain_texture->ActualHeight );

		if ( mp_rain_texture->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA )
		{
			GX::UploadTexture(  mp_rain_texture->pAlphaData,
								mp_rain_texture->ActualWidth,
								mp_rain_texture->ActualHeight,
								GX_TF_CMPR,
								GX_CLAMP,
								GX_CLAMP,
								GX_FALSE,
								GX_LINEAR,
								GX_LINEAR,
								0.0f,
								0.0f,
								0.0f,
								GX_FALSE,
								GX_TRUE,
								GX_ANISO_1,
								GX_TEXMAP1 ); 
			GX::SetTexCoordScale( GX_TEXCOORD0, GX_TRUE, mp_rain_texture->ActualWidth, mp_rain_texture->ActualHeight );
			GX::SetTexChanTevIndCull( 2, 1, 2, 0, GX_CULL_NONE );
		}
		else
		{
			GX::SetTexChanTevIndCull( 1, 1, 1, 0, GX_CULL_NONE );
		}

		GX::SetTevOrder( GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP1, GX_COLOR0A0 );

		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV,
											   GX_TEV_SWAP0, GX_TEV_SWAP0 );

		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_RASC, GX_CC_ZERO, 
										   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );

		if ( mp_rain_texture->flags & NxNgc::sTexture::TEXTURE_FLAG_HAS_ALPHA )
		{
			GX::SetTevAlphaInOpSwap( GX_TEVSTAGE1, GX_CA_ZERO, GX_CA_TEXA, GX_CA_RASA, GX_CA_ZERO,
												   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV,
												   GX_TEV_SWAP0, GX_TEV_SWAP1 );

			GX::SetTevColorInOp( GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_CPREV,
											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_2, GX_ENABLE, GX_TEVPREV );

		}

		switch ( m_splash_blend )
		{
			case NxNgc::vBLEND_MODE_ADD:
			case NxNgc::vBLEND_MODE_ADD_FIXED:
				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
			case NxNgc::vBLEND_MODE_SUBTRACT:
			case NxNgc::vBLEND_MODE_SUB_FIXED:
				GX::SetBlendMode ( GX_BM_SUBTRACT, GX_BL_ZERO, GX_BL_ZERO, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
			case NxNgc::vBLEND_MODE_BLEND:
			case NxNgc::vBLEND_MODE_BLEND_FIXED:
				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
			case NxNgc::vBLEND_MODE_BRIGHTEN:
			case NxNgc::vBLEND_MODE_BRIGHTEN_FIXED:
				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
			case NxNgc::vBLEND_MODE_MODULATE_FIXED:
			case NxNgc::vBLEND_MODE_MODULATE:
				GX::SetBlendMode ( GX_BM_BLEND, GX_BL_DSTCLR, GX_BL_ZERO, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
			case NxNgc::vBLEND_MODE_BLEND_PREVIOUS_MASK:
			case NxNgc::vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
			case NxNgc::vBLEND_MODE_DIFFUSE:
			default:
				GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_TRUE );
				break;
		}

//		GX::SetChanCtrl( GX_COLOR0A0, GX_DISABLE, GX_SRC_VTX, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
		GX::SetChanCtrl( GX_COLOR0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
		GX::SetChanCtrl( GX_ALPHA0, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE ); 

		switch ( m_splash_blend )
		{
			case NxNgc::vBLEND_MODE_ADD_FIXED:
			case NxNgc::vBLEND_MODE_SUB_FIXED:
			case NxNgc::vBLEND_MODE_BLEND_FIXED:
			case NxNgc::vBLEND_MODE_BRIGHTEN_FIXED:
			case NxNgc::vBLEND_MODE_MODULATE_FIXED:
				{
					uint8 fix = m_splash_blend_fix >= 128 ? 255 : m_splash_blend_fix * 2;
					GX::SetChanMatColor( GX_COLOR0A0, (GXColor){fix,fix,fix,fix} );
				}
				break;
			default:
				GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );
				break;
		}

		GXColor col = (GXColor){ m_splash_color.r, m_splash_color.g, m_splash_color.b, m_splash_color.a };
		GX::SetChanAmbColor( GX_COLOR0A0, col );





		GX::SetVtxDesc( 2, GX_VA_POS, GX_DIRECT, GX_VA_TEX0, GX_DIRECT );

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		NsMatrix*	p_matrix	= &NxNgc::EngineGlobals.camera;

		NsVector	up( 0.0f, 1.0f, 0.0f );
		NsVector screen_right;
		NsVector screen_up;
		screen_right.cross( *p_matrix->getAt(), up );
		screen_up.cross( screen_right, *p_matrix->getAt());

		screen_right.normalize();
		screen_up.normalize();

		screen_right.x *= m_splash_size;
		screen_right.y *= m_splash_size;
		screen_right.z *= m_splash_size;

		screen_up.x *= m_splash_size;
		screen_up.y *= m_splash_size;
		screen_up.z *= m_splash_size;

		// Render the splashes.
	//	int u = mp_rain_texture->GetWidth() << 4;
	//	int v = mp_rain_texture->GetHeight() << 4;
	//	uint32 col = *((uint32 *)&m_splash_color); 

		for ( int spl = 0; spl < NUM_SPLASH_ACTIVE; spl++ )
		{
			if ( m_splash_current_life[spl] == 0 ) continue;

			// Interpolate color.
			GXColor	icol;
			icol.r = (uint8)( ( (int)m_splash_color.r * m_splash_current_life[spl] ) / m_splash_life );
			icol.g = (uint8)( ( (int)m_splash_color.g * m_splash_current_life[spl] ) / m_splash_life );
			icol.b = (uint8)( ( (int)m_splash_color.b * m_splash_current_life[spl] ) / m_splash_life );
			icol.a = (uint8)( ( (int)m_splash_color.a * m_splash_current_life[spl] ) / m_splash_life );

			float v0x = m_splash_x[spl] - screen_up.x;
			float v0y = m_splash_y[spl] - screen_up.y;
			float v0z = m_splash_z[spl] - screen_up.z;

			float v1x = m_splash_x[spl] + screen_up.x; 
			float v1y = m_splash_y[spl] + screen_up.y; 
			float v1z = m_splash_z[spl] + screen_up.z; 

			// Draw splashes...
			GX::SetChanMatColor( GX_COLOR0A0, icol );
			GX::Begin( GX_QUADS, GX_VTXFMT0, 4 ); 
				GXWGFifo.f32 = v1x - screen_right.x;
				GXWGFifo.f32 = v1y - screen_right.y;
				GXWGFifo.f32 = v1z - screen_right.z;
				GXWGFifo.f32 = 0.0f;
				GXWGFifo.f32 = 0.0f;
	
				GXWGFifo.f32 = v1x + screen_right.x;
				GXWGFifo.f32 = v1y + screen_right.y;
				GXWGFifo.f32 = v1z + screen_right.z;
				GXWGFifo.f32 = 1.0f;
				GXWGFifo.f32 = 0.0f;
	
				GXWGFifo.f32 = v0x + screen_right.x;
				GXWGFifo.f32 = v0y + screen_right.y;
				GXWGFifo.f32 = v0z + screen_right.z;
				GXWGFifo.f32 = 1.0f;
				GXWGFifo.f32 = 1.0f;
	
				GXWGFifo.f32 = v0x - screen_right.x;
				GXWGFifo.f32 = v0y - screen_right.y;
				GXWGFifo.f32 = v0z - screen_right.z;
				GXWGFifo.f32 = 0.0f;
				GXWGFifo.f32 = 1.0f;
			GX::End();

			m_splash_current_life[spl]--;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CNgcWeather::plat_render( void )
{
	if ( !m_system_active ) return;

	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = pSkate->GetSkater(0);

	float skx = pSkater->m_pos[X];
	float skz = pSkater->m_pos[Z];

	GX::LoadPosMtxImm( (MtxPtr)&NxNgc::EngineGlobals.world_to_camera, GX_PNMTX0 );
	GX::SetZMode( GX_TRUE, GX_LEQUAL, GX_FALSE );

	if ( m_raining )
	{
		plat_render_splashes( skx, skz );
		plat_render_rain( skx, skz );
	}
	else
	{
		plat_render_snow( skx, skz );
	}

	// Increment rain drop/snow flake positions.
	if ( m_active_drops )
	{
		for ( int lp = 0; lp < ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ); lp++ )
		{
			switch ( m_drop_time[lp] )
			{
				case 255:
					// Inactive.
					break;
				case 254:
					// About to become inactive, so decrement active drops.
					m_active_drops--;
					// Deliberately falls through...
				default:
					// Increment time.
					m_drop_time[lp]++;
					break;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcWeather::plat_set_rain_blend_mode( uint32 blendmode_checksum, int fix )
{
	m_rain_blend = get_texture_blend( blendmode_checksum );
	m_rain_blend_fix = fix;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcWeather::plat_set_splash_blend_mode( uint32 blendmode_checksum, int fix )
{
	m_splash_blend = get_texture_blend( blendmode_checksum );
	m_splash_blend_fix = fix;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNgcWeather::plat_set_snow_blend_mode( uint32 blendmode_checksum, int fix )
{
	m_snow_blend = get_texture_blend( blendmode_checksum );
	m_snow_blend_fix = fix;
}

} // Nx







================================================
FILE: Code/Gfx/NGC/p_nxweather.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_WEATHER_H__
#define	__GFX_P_NX_WEATHER_H__
    
#include "gfx/nxweather.h"

namespace Nx
{

//#define WEATHER_GRID_W 65
//#define WEATHER_GRID_H 65
//
//#define WEATHER_CEL_W 32
//#define WEATHER_CEL_H 32

#define WEATHER_CEL_SIZE 128
#define WEATHER_CEL_SIZE_SHIFT 7
#define HEIGHT_TOLERANCE 30.0f
#define DROP_SIZE 16
#define DROP_SIZE_SHIFT 4
#define DROP_LAYERS 4

#define NUM_SPLASH_ACTIVE 32

typedef struct
{
	unsigned short start;
	unsigned short end;
	unsigned short index;
} sRowEntry;

class CNgcWeather : public CWeather
{
public:
							CNgcWeather();
	virtual 				~CNgcWeather();

private:					// It's all private, as it is machine specific
	void					plat_update_grid( void );
	void					plat_process( float delta_time );
	void					plat_render( void );
	void					plat_set_rain_blend_mode( uint32 blendmode_checksum, int fix );
	void					plat_set_splash_blend_mode( uint32 blendmode_checksum, int fix );
	void					plat_set_snow_blend_mode( uint32 blendmode_checksum, int fix );

	void					plat_render_splashes( float skx, float skz );
	void					plat_render_rain( float skx, float skz );
	void					plat_render_snow( float skx, float skz );

	NxNgc::sTexture *	mp_rain_texture;
	NxNgc::sTexture *	mp_snow_texture;

	unsigned char *		mp_roof_height_index;
	sRowEntry *			mp_roof_row;
	float				m_roof_height[256];
	int					m_width;
	int					m_height;

	float				m_min_x;
	float				m_min_z;

	float				m_rain_rate;
	float				m_splash_rate;
	float				m_snow_rate;

	Mth::Vector			m_grid_base;

	unsigned char		m_drop_time[DROP_SIZE*DROP_SIZE*DROP_LAYERS];
	unsigned char		m_x_offset[DROP_SIZE*DROP_SIZE*DROP_LAYERS];
	unsigned char		m_z_offset[DROP_SIZE*DROP_SIZE*DROP_LAYERS];

	uint32				m_seq;

	uint8				m_rain_blend;
	uint8				m_splash_blend;
	uint8				m_snow_blend;

	uint8				m_rain_blend_fix;
	uint8				m_splash_blend_fix;
	uint8				m_snow_blend_fix;

	float				m_splash_x[NUM_SPLASH_ACTIVE];
	float				m_splash_y[NUM_SPLASH_ACTIVE];
	float				m_splash_z[NUM_SPLASH_ACTIVE];
	int					m_splash_current_life[NUM_SPLASH_ACTIVE]; 
	int					m_current_splash;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif




================================================
FILE: Code/Gfx/NGC/p_nxwin2D.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxWin2D.h

#ifndef	__GFX_P_NX_WIN2D_H__
#define	__GFX_P_NX_WIN2D_H__

#include 	"Gfx/NxWin2D.h"
#include 	"Gfx/Ngc/NX/sprite.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CWindow2D
class	CNgcWindow2D : public CWindow2D
{
public:
								CNgcWindow2D(int x = 0, int y = 0, int width = 640, int height = 448);
								CNgcWindow2D(const Mth::Rect & win_rect);
	virtual						~CNgcWindow2D();

//	NxNgc::SScissorWindow *		GetEngineWindow() const;

private:
	//
	virtual void				plat_update_engine();		// Update engine primitives

	// Machine specific members
//	NxNgc::SScissorWindow *		mp_plat_window;				// Pointer to engine window
	short						m_left, m_right, m_top, m_bottom;
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

//inline NxNgc::SScissorWindow *	CNgcWindow2D::GetEngineWindow() const
//{
//	return mp_plat_window;
//}

} // Namespace Nx  			

#endif



================================================
FILE: Code/Gfx/NGPS/NX/Makefile
================================================

TOP         = /usr/local/sce/ee
LIBDIR      = $(TOP)/lib
INCDIR      = $(TOP)/include

TARGET      = main
OBJS        = crt0.o        \
              $(TARGET).o   \
              dma.o         \
              vif.o         \
              vu.o          \
              gif.o         \
              gs.o          \
              pcrtc.o       \
              dmacalls.o    \
              mikemath.o    \
              render.o      \
              import.o      \
              texture.o     \
              material.o    \
              mesh.o        \
              nx_init.o     \
              line.o        \
              chars.o       \
              microcode.o   \
              /skate4/code/sys/file/ngps/p_filesys.o    \
              /skate4/code/core/debug/Debug.o           \
              /skate4/code/core/debug/Assert.o          \

LCFILE      = app.cmd

LIBS        = $(LIBDIR)/libsn.a    \
              $(LIBDIR)/libgraph.a \
              $(LIBDIR)/libdma.a   \
              $(LIBDIR)/libdev.a   \
              $(LIBDIR)/libpad.a   \
              $(LIBDIR)/libpkt.a   \
              $(LIBDIR)/libvu0.a

PREFIX      = ee
AS          = $(PREFIX)-gcc
CC          = $(PREFIX)-gcc
LD          = $(PREFIX)-gcc
DVPASM      = ps2dvpas
OBJDUMP     = $(PREFIX)-objdump
RUN         = ps2run -r
RM          = del

CFLAGS      = -O0 -g  -Wall -Werror -fno-common
CXXFLAGS    = -O0 -Wall -Werror -fno-exceptions -fno-common
ASFLAGS     = -c -xassembler-with-cpp
DVPASMFLAGS = -g -W
LDFLAGS     = -Wl,-Map,$(TARGET).map -nostartfiles -L$(LIBDIR) -lm
TMPFLAGS    =

.SUFFIXES: .c .s .cc .dsm .vsm

all: $(TARGET).elf

main.elf: $(OBJS) $(LIBS)
	$(LD) -o $@ -T $(LCFILE) $(OBJS) $(LIBS) $(LDFLAGS)

crt0.o: crt0.s
	$(AS) $(ASFLAGS) $(TMPFLAGS) -o $@ $<

.s.o:
	$(AS) $(ASFLAGS) $(TMPFLAGS) -I$(INCDIR) -o $@ $<

.dsm.o:
	$(DVPASM) $(DVPASMFLAGS) -I$(INCDIR) -o $@ $<

.vsm.o:
	$(DVPASM) $(DVPASMFLAGS) -I$(INCDIR) -o $@ $<

.cpp.o:
	$(CC) $(CFLAGS) $(TMPFLAGS) -I$(INCDIR) -I/skate4/code -I/skate4/code/core/STL \
    -I/usr/local/sce/common/include -D__PLAT_NGPS__ -c $< -o $*.o \


.cc.o:
	$(CC) $(CXXFLAGS) $(TMPFLAGS) -I$(INCDIR) -c $< -o $*.o

run: $(TARGET).elf
	$(RUN) $(TARGET).elf

clean:
	$(RM) *.o
	$(RM) *.map
	$(RM) *.dis
	$(RM) *.elf



================================================
FILE: Code/Gfx/NGPS/NX/asmdma.dsm
================================================


;------------------------------------------------------------------------------------------------------------------------------------
; post-process fog effect
; (this will be replaced by a vu1 routine)

.global FogDma
.global FogPalette

.align 7	; align to multiple of 8 qwords (2^7 bytes) to give fastest dma transfer
FogDma:


		; preswizzled 256-colour palette, 32-bit entries


DMAcnt	*

	DIRECT *
	
		; set registers for palette transfer to vram
		.quad	0x00000000_0000000E_10000000_00000004		; GifTag	(GS_A_D,1,PACKED,0,0,0,4)
		.quad	0x00000000_00000050_00013000_00000000		; BITBLTBUF	(0,0,0,0x3000,1,PSMCT32)
		.quad	0x00000000_00000051_00000000_00000000		; TRXPOS	(0,0,0,0,0)
		.quad	0x00000000_00000052_00000010_00000010		; TRXREG	(16,16)
		.quad   0x00000000_00000053_00000000_00000000		; TRXDIR	(0)
		
		; image mode giftag for palette
		.quad	0x00000000_00000000_08000000_00000040		; GifTag	(0,0,IMAGE,0,0,0,64)

FogPalette:

		; the palette itself (which is filled in at run-time)
		.rept	256
		.int	0
		.endr



		.quad	0x00000000_0000000E_10AB4000_00008008		; GifTag	(GS_A_D,1,PACKED,SPRITE|TME|ABE|FST,1,1,8)
		.quad	0x00000000_0000003F_00000000_00000000		; TEXFLUSH	(0)
		.quad	0x00000000_00000014_00000000_00000001		; TEX1_1	(1,0,NEAREST,NEAREST,0,0,0)
		.quad	0x00000000_00000008_00000000_00003F73		; CLAMP_1	(REGION_REPEAT,REPEAT,0x3F7,0,0,0)
		.quad	0x00000000_00000040_01BF0000_027F0000		; SCISSOR_1	(0,447,0,639)
		.quad	0x00000000_00000042_00000000_00000044		; ALPHA_1	(Cs,Cd,As,Cd,0)
		.quad	0x00000000_00000047_00000000_00050000		; TEST_1	(ZGEQUAL,1,0,0,0,0,0,0)
		.quad	0x00000000_0000004C_00000000_000A0000		; FRAME_1	(0,10,PSMCT32,0)
		.quad	0x00000000_0000004E_00000001_010000D2		; ZBUF_1	(0xD2,PSMZ24,1)
		
	.EndDirect
	
.EndDmaData



; break screen into halves, otherwise source texture > 1024 in width
; left half
DMAcall *,HalfScreen

	DIRECT *
	
		; set up source and dest coordinate origins
		.quad	0x00000000_0000000E_10AB4000_00008002		; GifTag	(GS_A_D,1,PACKED,SPRITE|TME|ABE|FST,1,1,1)
		.quad	0x00000000_00000006_2006000E_A9351A40		; TEX0_1	(0x1A40,20,PSMT8,10,10,RGBA,DECAL,0x3000,PSMCT32,CSM1,0,1)
		.quad	0x00000000_00000018_00000000_00001400		; XYOFFSET_1(0x1400,0)
		
	.EndDirect

.EndDmaData



; right half
DMAcall *,HalfScreen

	DIRECT *
	
		; set up source and dest coordinate origins
		.quad	0x00000000_0000000E_10AB4000_00008002		; GifTag	(GS_A_D,1,PACKED,SPRITE|TME|ABE|FST,1,1,1)
		.quad	0x00000000_00000006_2006000E_A9351AE0		; TEX0_1	(0x1AE0,20,PSMT8,10,10,RGBA,DECAL,0x3000,PSMCT32,CSM1,0,1)
		.quad	0x00000000_00000018_00000000_00000000		; XYOFFSET_1(0,0)
		
	.EndDirect

.EndDmaData



; restore and return
DMAret *

	DIRECT *
	
		; restore any GS regs that we shouldn't have knackered
		.quad	0x00000000_0000000E_10000000_00008001		; GifTag	(GS_A_D,1,PACKED,0,0,1,1)
		.quad	0x00000000_00000008_00000000_00000000		; CLAMP_1	(REPEAT,REPEAT,0,0,0,0)

	.EndDirect

.EndDmaData



; apply fogging effect to half of the screen (left or right)

.align 7		; align to 8 qwords
HalfScreen:

; This innocent-looking piece of assembler generates 280K of dma data!
; (It will eventually be replaced with a small VU1 progam that generates it on the fly, saving memory and dma bandwidth.)

DMAret *

	DIRECT *

.if 0	 ; Change to 1 to get the full-screen fog back, and lose 280K of memory
		.quad	0x00000000_00005353_44000000_0000A300		; GifTag	(UV|XYZ2<<4|UV<<8|XYZ2<<12,4,REGLIST,0,0,1,8960)

		; loop over quarter-pages		
		qpageX = 0
		.rept 10

			; use diagonally facing quarter-page as the source		
			qpageU = (qpageX^0x200)*2+8
		
			qpageY = 0
			.rept 28
			
				; use diagonally facing quarter-page as the source		
				qpageV = (qpageY^0x100)*2+8
				
				X = qpageX+0x1400
				U = qpageU
				.rept 4
				
					Y = qpageY
					V = qpageV+0x20
					.rept 4
					
						; even column
						.short U+0x40,V,0,0
						.short X,Y,0xFFFF,0x0080
						.short U+0xC0,V+0x20,0,0
						.short X+0x80,Y+0x20,0xFFFF,0x0080
						
						; odd column
						.short U,V+0x40,0,0
						.short X,Y+0x20,0xFFFF,0x0080
						.short U+0x80,V+0x60,0,0
						.short X+0x80,Y+0x40,0xFFFF,0x0080
						
						Y = Y+0x40
						V = V+0x80
					.endr
				
					X = X+0x80
					U = U+0x100
				.endr
		
				qpageY = qpageY+0x100
			.endr
		
			qpageX = qpageX+0x200
		.endr
.else
; Mick:  Just bunged in something to make it not crash
		.quad	0x00000000_0000000E_10000000_00008001		; GifTag	(GS_A_D,1,PACKED,0,0,1,1)
		.quad	0x00000000_00000008_00000000_00000000		; CLAMP_1	(REPEAT,REPEAT,0,0,0,0)
.endif
	
	.EndDirect

.EndDmaData

;------------------------------------------------------------------------------------------------------------------------------------

.if 0

; BILLBOARD TEST

.global BillboardDma
.global BillboardContext
.global BillboardMats

.align 4
BillboardDma:
DMAret *

	UNPACK	1, 1, V4_32, 0, *
	
	; VU context... 32
	.quad	0x0000000C_0000000F_10000020_3400000C
BillboardContext:
	.quad	0,0,0,0
BillboardMats:
	.quad	0,0,0,0									; world to camera matrix
	.quad	0,0,0,0									; camera to screen matrix
	
	; axial billboards... 86
	.quad	0x00000024_00000512_300E4056_35C0000C
	
	.float	0,0,1,0									; stq0
	.int	90,45,15,128							; rgba0
	.float	0,100,0,0								; xyz
	.float	1,0,1,0									; stq1
	.int	30,15,5,128								; rgba1
	.float	10,100,0,0								; w/2,h/2
	.float	0,1,1,0									; stq2
	.int	90,45,15,128							; rgba2
	.float	0,0,0,0									; tx,ty,tz
	.float	1,1,1,0									; stq3
	.int	30,15,5,128								; rgba3
	.float	0,1,0,0									; axis
	
	.float	0,0,1,0									; stq0
	.int	90,45,15,128							; rgba0
	.float	70.71,270.71,0,0						; xyz
	.float	1,0,1,0									; stq1
	.int	30,15,5,128								; rgba1
	.float	10,100,0,0								; w/2,h/2
	.float	0,1,1,0									; stq2
	.int	90,45,15,128							; rgba2
	.float	0,0,0,0									; tx,ty,tz
	.float	1,1,1,0									; stq3
	.int	30,15,5,128								; rgba3
	.float	0.7071,0.7071,0,0						; axis
	
	.float	0,0,1,0									; stq0
	.int	90,45,15,128							; rgba0
	.float	-70.71,270.71,0,0						; xyz
	.float	1,0,1,0									; stq1
	.int	30,15,5,128								; rgba1
	.float	10,100,0,0								; w/2,h/2
	.float	0,1,1,0									; stq2
	.int	90,45,15,128							; rgba2
	.float	0,0,0,0									; tx,ty,tz
	.float	1,1,1,0									; stq3
	.int	30,15,5,128								; rgba3
	.float	-0.7071,0.7071,0,0						; axis
	
	; screen-aligned billboards... 84
	.quad	0x00000018_00000512_302E4054_35C08008
	
	.float	0,0,1,0									; stq0
	.int	0,100,0,64								; rgba0
	.float	141.42,341.42,0,0						; xyz
	.float	1,0,1,0									; stq1
	.int	0,100,0,64								; rgba1
	.float	60,60,0,0								; w/2,h/2
	.float	0,1,1,0									; stq2
	.int	0,100,0,64								; rgba2
	.float	0,0,10,0								; tx,ty,tz
	.float	1,1,1,0									; stq3
	.int	0,100,0,64								; rgba3
	.float	0,0,0,0									; axis
	
	.float	0,0,1,0									; stq0
	.int	0,100,0,64								; rgba0
	.float	-141.42,341.42,0,0						; xyz
	.float	1,0,1,0									; stq1
	.int	0,100,0,64								; rgba1
	.float	60,60,0,0								; w/2,h/2
	.float	0,1,1,0									; stq2
	.int	0,100,0,64								; rgba2
	.float	0,0,10,0								; tx,ty,tz
	.float	1,1,1,0									; stq3
	.int	0,100,0,64								; rgba3
	.float	0,0,0,0									; axis
	.EndUnpack

.EndDmaData

.endif

;------------------------------------------------------------------------------------------------------------------------------------



================================================
FILE: Code/Gfx/NGPS/NX/asmdma.h
================================================
#ifndef __ASMDMA_H
#define __ASMDMA_H

extern uint32 FogPalette[256];
extern uint   FogDma;

extern uint BillboardDma;
extern uint BillboardContext;
extern uint BillboardMats;

#endif //__ASMDMA_H



================================================
FILE: Code/Gfx/NGPS/NX/chars.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "chars.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gif.h"
#include "gs.h"
#include "vu1code.h"
#include "texturemem.h"
#include "render.h"
#include "switches.h"

/*


	
.fnt file format (by Ryan)
--------------------------

	4	File size in bytes
	4	Number of characters
	4	Default height
	4	Default base
	
	?	Character table (see below)
	?	Texture (see below)

	Character
	2	Baseline (how many pixels down relative to top of image)
	2	Ascii value

	Texture
	4	Size of texture
	2	Width
	2	Height
	2	Bit depth
	6	Padding
	W*H	Raw data
	0-3	Padding for uint32 alignment
	1K	Palette data
	4	Number of subtextures
	?	Subtexture table (see below)

	Subtexture
	2	X
	2	Y
	2	W
	2	H

	
	
	
font->vram upload format
------------------------


	GIFtag PACKED 4
	BITBLTBUF ?
	TRXPOS 0
	TRXREG w,h
	TRXDIR 0
	
	GIFtag IMAGE (w*h+15)/16
	... (tex data)
	
	GIFtag PACKED 4
	BITBLTBUF ?
	TRXPOS 0
	TRXREG w,h
	TRXDIR 0
	
	GIFtag IMAGE 64
	... (clut data)
	
	
	
	total size in bytes = 12*16+((w*h+15)/16)*16+1024 = 1216+((w*h+15)/16)*16
	
	this will be referenced as follows:
	
	dma ref
	NOP
	DIRECT 76+(w*h+15)/16
	
	
*/


namespace NxPs2
{


float CurrentScale;
uint32 FontVramStart;
uint32 FontVramBase;
SFont *pFontList;
SFont *pButtonsFont = NULL;


// Garrett: Now uses both 648K buffers at once, so we'd need to undo this if we want to
// implement double buffering.
const uint32 FontVramSize = 0xA20 * 2;	// 648K * 2

extern TextureMemoryLayout TexMemLayout;

SFont *SText::spOverrideFont = NULL;

void * OpenExtended(const char *Filename)
{
	char *ExtendedFilename;

	// allocate memory for extended name
	ExtendedFilename = (char *)Mem::Malloc(strlen(Filename)+17);	// 12 for "fonts/" plus 4 for ".fnt" plus 1 for \0
	Dbg_MsgAssert(ExtendedFilename, ("couldn't malloc memory for extended filename fonts/%s", Filename));

	// copy and extend name
	strcpy(ExtendedFilename, "fonts/");
	strcat(ExtendedFilename, Filename);
	strcat(ExtendedFilename, ".fnt.ps2");

	// open file
	void *pFile = File::Open(ExtendedFilename, "rb");
	Dbg_MsgAssert(pFile>=0, ("couldn't open file %s", Filename));

	// free memory
	Mem::Free(ExtendedFilename);

	return pFile;
}





SFont * LoadFont(const char *Filename)
{
	SFont *pFont;
	SChar *pChar;
	uint8 *pData;
	void *pFile;
	int i,j,Len,NumChars,Width,Height,NumQWords,NumBytes;
	uint32 TW,TH,TBW;
	uint32 temp;

		  
	uint8*	old_dma_loc = dma::pLoc;		  

	// open the font file
	pFile = OpenExtended(Filename);


	// allocate memory for the font structure
	pFont = (SFont *)Mem::Malloc(sizeof(SFont));
	Dbg_MsgAssert(pFont, ("couldn't malloc memory for SFont"));


	// allocate a temporary buffer
	uint8 FontBuf[2048];
	Dbg_MsgAssert(FontBuf, ("couldn't malloc memory for FontBuf"));


	// load file header
	Len = File::Read(FontBuf, 1, 16, pFile);
	Dbg_MsgAssert(Len==16, ("couldn't read file header from font file %s", Filename));
	NumChars			 = ((uint32 *)FontBuf)[1];
	pFont->DefaultHeight = ((uint32 *)FontBuf)[2] << 4;		// *16 for texel coords
	pFont->DefaultBase	 = ((uint32 *)FontBuf)[3] << 4;		// *16 for texel coords


	// clear character map to zero
	memset(pFont->Map, 0, 256);
	memset(pFont->SpecialMap, 0, 32);


	// allocate memory for character table
	pFont->pChars = (SChar *)Mem::Malloc(NumChars*sizeof(SChar));
	Dbg_MsgAssert(pFont->pChars, ("couldn't malloc memory for font character table"));


	// load character map and character table
	Len = File::Read(FontBuf, 1, NumChars<<2, pFile);
	Dbg_MsgAssert(Len==(NumChars<<2), ("couldn't read character table in font file %s", Filename));

	for (i=0,pChar=pFont->pChars,pData=FontBuf; iBaseline = ((uint16 *)pData)[0] << 4;		// *16 for texel coords
		sint16 ascii_val = ((sint16 *)pData)[1];
		if (ascii_val >= 0)
			pFont->Map[(uint8) ascii_val] = i;
		else
		{
			Dbg_Assert(ascii_val >= -32)
			pFont->SpecialMap[(uint8) (-ascii_val - 1)] = i;
		}
	}

	// now, if there is a null character in the font, make characters that could not be found
	// in the font display that instead of 'A'
	if (pFont->SpecialMap[31] != 0)
	{
		for (i = 0; i < 256; i++) 
		{
			if (pFont->Map[i] == 0 && i != 'A' && i != 'a') pFont->Map[i] = pFont->SpecialMap[31];
			if (i < 31 && pFont->SpecialMap[i] == 0) pFont->SpecialMap[i] = pFont->SpecialMap[31];
		}
	}	
	
	// load texture header
	Len = File::Read(FontBuf, 1, 16, pFile);
	Dbg_MsgAssert(Len==16, ("couldn't read texture header from font file %s", Filename));
	Width  = ((uint16 *)FontBuf)[2];
	Height = ((uint16 *)FontBuf)[3];


	// allocate memory for vif stream
	pFont->VifSize = 1216 + (((Width*Height+15)>>4)<<4);
	pFont->pVifData = (uint8 *)Mem::Malloc(pFont->VifSize);
	Dbg_MsgAssert(pFont->pVifData, ("couldn't malloc memory for vif data"));
	Dbg_MsgAssert(((uint32)pFont->pVifData & 15) == 0, ("vif data not qword aligned"));



	// build dma data in this buffer
	dma::pLoc = pFont->pVifData;

	// Calculate memory needed
	int min_width = Width, min_height = Height;
	TexMemLayout.getMinSize(min_width, min_height, PSMT8);
	pFont->m_NumTexVramBlocks = TexMemLayout.getImageSize(min_width, min_height, PSMT8);
	pFont->m_NumClutVramBlocks = 4;


	// set base pointers and texture buffer width
	pFont->m_CBP = FontVramBase;
	pFont->m_TBP = FontVramBase + pFont->m_NumClutVramBlocks;
	TBW = (Width+63) >> 6;
	if (TBW<2)
		TBW=2;


	// texture upload format:
	//
	// GIFtag PACKED 4
	// BITBLTBUF ?
	// TRXPOS 0
	// TRXREG w,h
	// TRXDIR 0
	//
	// GIFtag IMAGE (w*h+15)/16
	// ... (tex data)

	// texture upload header
	NumQWords = (Width*Height+15)>>4;
	gif::Tag2(gs::A_D,1,PACKED,0,0,0,4);
	pFont->mp_TexBitBltBuf = gs::Reg2_pData(gs::BITBLTBUF,PackBITBLTBUF(0,0,0,pFont->m_TBP,TBW,PSMT8));
	gs::Reg2(gs::TRXPOS,	PackTRXPOS(0,0,0,0,0));
	gs::Reg2(gs::TRXREG,	PackTRXREG(Width,Height));
	gs::Reg2(gs::TRXDIR,	0);
	gif::Tag2(0, 0, IMAGE, 0, 0, 0, NumQWords);

	// read texture bitmap data
	NumBytes = (Width*Height+3)&0xFFFFFFFC;
	Len = File::Read(dma::pLoc, 1, NumBytes, pFile);
	Dbg_MsgAssert(Len==NumBytes, ("couldn't read texture bitmap from font file %s", Filename));
	dma::pLoc += NumQWords<<4;


	// clut upload format:
	//
	// GIFtag PACKED 4
	// BITBLTBUF ?
	// TRXPOS 0
	// TRXREG w,h
	// TRXDIR 0
	//
	// GIFtag IMAGE 64
	// ... (clut data)

	// clut upload header
	gif::Tag2(gs::A_D,1,PACKED,0,0,0,4);
	pFont->mp_ClutBitBltBuf = gs::Reg2_pData(gs::BITBLTBUF,PackBITBLTBUF(0,0,0,pFont->m_CBP,1,PSMCT32));
	gs::Reg2(gs::TRXPOS,	PackTRXPOS(0,0,0,0,0));
	gs::Reg2(gs::TRXREG,	PackTRXREG(16,16));
	gs::Reg2(gs::TRXDIR,	0);
	gif::Tag2(0,0,IMAGE,0,0,0,64);

	// read clut bitmap data
	Len = File::Read(dma::pLoc, 1, 1024, pFile);
	Dbg_MsgAssert(Len==1024, ("couldn't read clut bitmap from font file %s", Filename));
	
	// swizzle the clut data
	for (i=0; i<256; i+=32)
		for (j=0; j<8; j++)
		{
			temp = ((uint32 *)dma::pLoc)[i+j+8];
			((uint32 *)dma::pLoc)[i+j+8] = ((uint32 *)dma::pLoc)[i+j+16];
			((uint32 *)dma::pLoc)[i+j+16] = temp;
		}
	
	// step dma location
	dma::pLoc += 1024;


	// skip numsubtextures, and load subtextures
	Len = File::Read(FontBuf, 1, (NumChars<<3)+4, pFile);
	Dbg_MsgAssert(Len==(NumChars<<3)+4, ("couldn't read subtexture table from font file %s", Filename));
	for (i=0,pChar=pFont->pChars,pData=FontBuf+4; iu0 = (((uint16 *)pData)[0] << 4) + 8;
		pChar->v0 = (((uint16 *)pData)[1] << 4) + 8;
		pChar->u1 = pChar->u0 + (((uint16 *)pData)[2] << 4);
		pChar->v1 = pChar->v0 + (((uint16 *)pData)[3] << 4);
	}

	// set font GS context
	for (TW=0; (1<RegTEX0 = PackTEX0(pFont->m_TBP,TBW,PSMT8,TW,TH,1,MODULATE,pFont->m_CBP,PSMCT32,0,0,1);
	pFont->RegTEX1 = PackTEX1(1,0,LINEAR,LINEAR,0,0,0);


	// update vram base pointer
	//FontVramBase = (((pFont->TBP<<8) + (TBW << 6)*Height + 0x1FFF) & 0xFFFFE000) >> 8;
	FontVramBase +=	(pFont->m_NumTexVramBlocks + pFont->m_NumClutVramBlocks);
	Dbg_MsgAssert(FontVramBase < (FontVramStart + FontVramSize), ("Font: 2D VRAM Buffer Full: %x.", FontVramBase));


	// add font to font list
	pFont->pNext = pFontList;
	pFontList = pFont;

	// we're done with the font file now
	File::Close(pFile);
	
	// And restore the CurrentDMALoc, which we were just using temporarily.
	dma::pLoc = old_dma_loc;	  

	// this will serve as the default spacing
	pFont->mSpaceSpacing = (pFont->pChars[pFont->Map['I']].u1 - pFont->pChars[pFont->Map['I']].u0) >> 4;
	
	return pFont;
}



void UnloadFont(SFont *pFont)
{
	SFont *pPrevFont;
	int found=0;

	// find font and unchain from list
	if (pFontList == pFont)
	{
		found=1;
		pFontList = pFontList->pNext;
	}
	else
	{
		for (pPrevFont=pFontList; pPrevFont->pNext; pPrevFont=pPrevFont->pNext)
		{
			if (pPrevFont->pNext == pFont)
			{
				found=1;
				pPrevFont->pNext = pFont->pNext;
				break;
			}
		}
	}

	Dbg_MsgAssert(found, ("attempt to unload font which has not been loaded"));

	// free memory
	Mem::Free(pFont->pChars);
	Mem::Free(pFont->pVifData);
	Mem::Free(pFont);

	// and re-do VRAM allocation
	VRAMNeedsUpdating();
}

/////////////////////////////////////////////////////////////////////////////
//

void SFont::ReallocateVRAM()
{
	// calculate TBP
	m_TBP = FontVramBase;
	FontVramBase += m_NumTexVramBlocks;

	// calculate CBP
	m_CBP = FontVramBase;
	FontVramBase += m_NumClutVramBlocks;

	// bail if VRAM would become over-packed
	if (FontVramBase > (FontVramStart + FontVramSize))
	{
		Dbg_MsgAssert(0, ("SingleTexture Realloc: 2D VRAM Buffer Full: %x.", FontVramBase));
	}

	// Modify the data
	gs::ModifyTBP(gs::TEX0_1, (uint32 *) &RegTEX0, m_TBP, m_CBP);
}

/////////////////////////////////////////////////////////////////////////////
//

void SFont::UpdateDMA()
{
	// Modify the packets
	gs::ModifyTBP(gs::BITBLTBUF, mp_TexBitBltBuf, m_TBP);
	if (mp_ClutBitBltBuf)
	{
		gs::ModifyTBP(gs::BITBLTBUF, mp_ClutBitBltBuf, m_CBP);
	}
}

uint32 SFont::GetDefaultHeight() const
{
	return DefaultHeight;
}

uint32 SFont::GetDefaultBase() const
{
	return DefaultBase;
}

void SFont::QueryString(char *String, float &width, float &height)
{
	SChar *pChar;
	char *pLetter;
	uint16 u0,v0,x0,u1,v1,x1;

	x0 = 0;

	//sint16 char_spacing = 16 + (mCharSpacing<<4);
	sint16 char_spacing = mCharSpacing<<4;
	sint16 space_spacing = mSpaceSpacing<<4;
	
	for (pLetter=String;; pLetter++)
	{
		pChar = NULL;
		// may be overridden by the '\b' tag
		SFont *p_font = this;
		
		// acount for tags (might be multiple ones in a row)
		bool got_char_tag = false; // tag resulting in output of character
		while (*pLetter == '\\' && !got_char_tag)
		{
			pLetter++;
			if (*pLetter == '\\')
				break;

			switch(*pLetter)
			{
				case '\\':
					got_char_tag = true;
					break;
				case 'c':
				case 'C':
					pLetter += 2; // skip over "c#"
					break;
				case 's':
				case 'S':
				{
					pLetter++; // skip "s"
					uint digit = Str::DehexifyDigit(pLetter);
					pChar = pChars + SpecialMap[digit];
					got_char_tag = true;
					break;
				}
				case 'b':
				case 'B':
				{
					pLetter++; // skip "b"
					uint digit = Str::DehexifyDigit(pLetter);
					
					// switch over to buttons font, the regular font will be used again on the next character

					p_font = pButtonsFont;
					Dbg_Assert(p_font);
					pChar = p_font->pChars + p_font->SpecialMap[digit];
					got_char_tag = true;
					break;
				}
				case 'm':
				case 'M':
				{
					pLetter++; // skip "m"
					char button_char = Nx::CFontManager::sMapMetaCharacterToButton(pLetter);
					uint digit = Str::DehexifyDigit(&button_char);
					
					p_font = pButtonsFont;
					Dbg_Assert(p_font);
					pChar = p_font->pChars + p_font->SpecialMap[digit];
					got_char_tag = true;
					break;
				}
				default:
					Dbg_MsgAssert(0, ("unknown tag %c", *pLetter));
					break;
			}
		} // end while
		
		if (*pLetter == '\0') break;

		if (*pLetter != ' ' || pChar)
		{
			if (!pChar)
				pChar = p_font->pChars + p_font->Map[(uint8)*pLetter];
			u0 = pChar->u0;
			v0 = pChar->v0;
			u1 = pChar->u1;
			v1 = pChar->v1;
			x1 = x0+u1-u0;
		}
		else
		{
			x1 = x0 + space_spacing;
		}
		
		x0 = x1+char_spacing;
	}

	width  = (float)x0/16.0f;
	height = (float)DefaultHeight/16.0f;
}


/////////////////////////////////////////////////////////////////////////////
//

SText::SText(float pri) : SDraw2D(pri, true)
{
}

/////////////////////////////////////////////////////////////////////////////
//

SText::~SText()
{
}

/////////////////////////////////////////////////////////////////////////////
//

void SText::BeginDraw()
{
	//printf("Trying to draw: %s with %x at (%f, %f) of scale %f\n", mp_string, m_rgba, m_xpos, m_ypos, m_scale);

	// Subsequent processing within Draw() will use this font
	// Draw() may call this function to temporarily switch fonts
	SFont *p_font = (spOverrideFont) ? spOverrideFont : mp_font;
	
	// dma tag
	dma::BeginTag(dma::cnt, 0);

	vu1::Buffer = vu1::Loc;

	Dbg_Assert(GetScissorWindow());

	// GS context
	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::SCISSOR_1, GetScissorWindow()->GetScissorReg());
	gs::Reg1(gs::TEX0_1,	 p_font->RegTEX0);
	gs::Reg1(gs::TEX1_1,	 p_font->RegTEX1);
	gs::Reg1(gs::ALPHA_1,	 PackALPHA(0,1,0,1,0));
	gs::Reg1(gs::COLCLAMP,	 PackCOLCLAMP(1));
	gs::Reg1(gs::RGBAQ,	 (uint64)m_rgba);
	gs::EndPrim(0);

	// unpack giftag
	vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
	gif::BeginTagImmediate(gs::UV|gs::XYZ2<<4, 2, PACKED, SPRITE|TME|FST|ABE, 1, VU1_ADDR(GSPrim));

	// begin unpack for uv/xyz's
	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
}

/////////////////////////////////////////////////////////////////////////////
//

void SText::Draw()
{
	SChar *pChar = NULL;
	char *pLetter;
	uint16 u0,v0,u1,v1;
	uint32 x0,y0,x1,y1,yt,z;

	Dbg_Assert(GetScissorWindow());

	x0 = (int)(m_xpos * s_screen_scale_x * 16.0f) + XOFFSET + ((GetScissorWindow()->GetScissorReg() & 0x7FF) << 4);
	y0 = (int)(m_ypos * s_screen_scale_y * 16.0f) + YOFFSET + ((GetScissorWindow()->GetScissorReg() >> 32 & 0x7FF) << 4);
	z = s_constant_z_enabled ? s_constant_z : GetZValue();

	//sint16 char_spacing = 16 + (mp_font->mCharSpacing<<4);
	float char_spacing = (float) (mp_font->mCharSpacing<<4) * m_xscale * s_screen_scale_x;
	float space_spacing = (float) (mp_font->mSpaceSpacing<<4) * m_xscale * s_screen_scale_x;
	
	// XXX
	//printf("   spacing=%f, %d\n", char_spacing, mp_font->mCharSpacing);

	for (pLetter=mp_string;; pLetter++)
	{
		pChar = NULL;
		SFont *p_font = mp_font;
		
		// acount for tags (might be multiple ones in a row)
		bool got_char_tag = false; // tag resulting in output of character
		while (*pLetter == '\\' && !got_char_tag)
		{
			pLetter++;

			switch(*pLetter)
			{
				case '\\':
					got_char_tag = true;
					break;
				case 'c':
				case 'C':
				{
					pLetter++;	// skip "c"					
					uint digit = Str::DehexifyDigit(pLetter);
					pLetter++; // skip "#"

					// BIG MIKE	(remove #if below, replace active_color_gs_register_thingy with GS code)
					#if 1
					// set active color from font
					uint32	temp_rgba = m_rgba;
					if (digit != 0 && !m_color_override)
					{
						m_rgba = mp_font->mRGBATab[digit-1];
					}
					// This method is not optimal, but it's not done very often
					// we just turn off text, and turn it on with new colors
					EndDraw();
					BeginDraw();
					m_rgba = temp_rgba;
					#endif

					break;
				}
				case 's':
				case 'S':
				{
					pLetter++;	// skip "s"
					uint digit = Str::DehexifyDigit(pLetter);
					
					pChar = mp_font->pChars + mp_font->SpecialMap[digit];
					got_char_tag = true;
					
					break;
				}
				case 'b':
				case 'B':
				{
					// 'B' stands for button, accesses the button font

					pLetter++; // skip "b"
					uint digit = Str::DehexifyDigit(pLetter);
					
					// switch to the buttons font!
					
					p_font = pButtonsFont;
					Dbg_Assert(p_font);
					pChar = p_font->pChars + p_font->SpecialMap[digit];
					got_char_tag = true;
					
					spOverrideFont = p_font;
					EndDraw();
					BeginDraw();
					// we can now return to using the regular font (no override)
					spOverrideFont = NULL;
					
					break;
				}
				default:
					Dbg_MsgAssert(0, ("unknown tag %c", *pLetter));
					break;
			}
		} // end while

		if (*pLetter == '\0') break;
		
		if (*pLetter != ' ' || pChar)
		{
			if (!pChar)
				pChar = p_font->pChars + p_font->Map[(uint8) *pLetter];
			yt = y0 + (int)((float)(p_font->DefaultBase - pChar->Baseline) * m_yscale * s_screen_scale_y);
			u0 = pChar->u0;
			v0 = pChar->v0;
			u1 = pChar->u1;
			v1 = pChar->v1;
			x1 = x0+(int)((float)(u1-u0)*m_xscale * s_screen_scale_x);
			y1 = yt+(int)((float)(v1-v0)*m_yscale * s_screen_scale_y);
		}
		else
		{
			x0 += (int) (space_spacing + char_spacing);
			continue;
		}

		if (!NeedsCulling() || !((x0 | x1 | y0 | y1) & 0xFFFF0000))
		{
			vif::StoreV4_32(u0,v0,0,0);
			vif::StoreV4_32(x0,yt,z,0);
			vif::StoreV4_32(u1,v1,0,0);
			vif::StoreV4_32(x1,y1,z,0);
		}

		x0 = x1 + (int) char_spacing;

		// kick and start a new buffer if this one is full
		if (((dma::pLoc - gif::pTag)>>4) >= 250)
		{
			vif::EndUNPACK();
			gif::EndTagImmediate(1);
			vif::MSCAL(VU1_ADDR(Parser));
			vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
			gif::BeginTagImmediate(gs::UV|gs::XYZ2<<4, 2, PACKED, SPRITE|TME|FST|ABE, 1, VU1_ADDR(GSPrim));
			vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
		}
		
		if (p_font != mp_font)
		{
			// we just used the button font, so return to the regular one
			EndDraw();
			BeginDraw();
		}
	} // end for
}

/////////////////////////////////////////////////////////////////////////////
//

void SText::EndDraw(void)
{
	vif::EndUNPACK();
	gif::EndTagImmediate(1);
	vif::MSCAL(VU1_ADDR(Parser));
	dma::EndTag();
}



} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/chars.h
================================================
#ifndef __CHARS_H
#define __CHARS_H

#include 

namespace NxPs2
{


typedef struct
{
	uint16 u0,v0,u1,v1;
	uint16 Baseline;
}
SChar;


struct SFont
{
public:
	uint32		GetDefaultHeight() const;
	uint32		GetDefaultBase() const;

	void		QueryString(char *String, float &width, float &height);

	void		ReallocateVRAM();
	void		UpdateDMA();

	//char Name[16];
	uint32		DefaultHeight, DefaultBase;
	SChar		*pChars;
	uint8		Map[256];
	uint8		SpecialMap[32];
	uint8		*pVifData;
	uint32		VifSize;
	uint64		RegTEX0, RegTEX1;
	uint32 		*mp_TexBitBltBuf;		// Note these register pointers point to data in the DMA buffer,
	uint32 		*mp_ClutBitBltBuf;		// so they should only be used for writing, and only after drawing is done.
	uint16		m_NumTexVramBlocks;
	uint16		m_NumClutVramBlocks;
	uint16		m_TBP;
	uint16		m_CBP;
	sint16		mCharSpacing;
	sint16		mSpaceSpacing;
	uint32		mRGBATab[16];
	SFont		*pNext;
};



SFont *		LoadFont(const char *Filename);
void		UnloadFont(SFont *);
void		SetTextWindow(uint16 x0, uint16 x1, uint16 y0, uint16 y1);


struct SText : public SDraw2D
{
public:
					SText(float pri = 0.0f);
	virtual			~SText();

	SFont			*mp_font;

	char			*mp_string;
	float			m_xpos;
	float			m_ypos;
	float			m_xscale;
	float			m_yscale;
	uint32			m_rgba;
	bool			m_color_override;
	
	// used in conjunction with BeginDraw()
	// if set, use specified font instead of mp_font
	// if not, use mp_font
	static SFont *	spOverrideFont;

private:

	void			BeginDraw();
	void			Draw(void);
	void			EndDraw(void);
};

extern uint32		FontVramStart;
extern uint32		FontVramBase;
extern const uint32	FontVramSize;
extern SFont		*pFontList;
extern SFont *		pButtonsFont;


} // namespace NxPs2


#endif // __CHARS_H


================================================
FILE: Code/Gfx/NGPS/NX/dma.cpp
================================================
#include 
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gs.h"
#include "group.h"
#include "render.h"
#include "vu1code.h"
#include "switches.h"
#include "geomnode.h"

/*

 Mick Notes:

 The calls the the DMA functions suggest that you are immediately executing something.
 You are not.  You are simply building a list of DMA commands that get executed the next frame
 
 The DMA list is the primary mechanism for initiating rendering on the PS2.
 
 To render something (like a line), you "simply" need to generate the appropiate DMA
 packets that contain GIF packets, containing GS primitives, and then link this into the 
 main DMA list.
 
 Things such as world sectors have pre-build DMA lists that contain commands
 to upload lists of vertices to VU1 micro-memory, and then upload lists of
 triangles, and then trigger the appropiate VU1 microcode to transform and render them.
 
 These pre-built lists are linked in each frame with the appropiate transformation 
 matricies that are needed based on the current camera position and the current
 object position (if it is a moving object)
 
 Thus, each object is mostly pre-build, and so rendering it requires very little CPU time.

 Each frame, we build a series of dynamic DMA lists.
 
 There are two lists for each group (see group.cpp).
 
 The first list is the texture upload.  This is pretty simple, just uploading a few textures.
 Texture uploading happens asyncrnously with rendering, so we can be executing a DMA list
 of rendering stuff at the same time as we are uploading the textures for the next group.   

 A lot of the DMA code is in DMA.H, as it is small inline functions.  

 The Runtime buffer is dynamically allocated in InitializeEngine().  Last time I looked it
 was 512K in size (note though, this is increased to 16MB for the debugging wireframe mode)
 
 The Runtime DMA lists are double buffered. You are executing one whilst building the next one.
 
 The pRunTimeBuffer is split in two, and pointers to the base of each half are stored in 
 pList[0] and pList[1]   

 The integer varaible "Field" is 0 or 1, and indicates which field (odd or even) we are rendering to.  
 
 The DMA lists are build using a global variable char * pLoc.  
 
 At the start of a frame, pLoc is initilized like:
 
 	dma::pLoc = dma::pList[Field];

 So it points to the start of one of our 256K DMA buffers (remember the other is being executed)
 
 DMA packets are now built using calls to the functions here and in DMA.H, for example: 
 
	dma::Tag(dma::ref, ((uint8 *)&MPGEnd-(uint8 *)&MPGStart+15)/16, (uint)&MPGStart);
	vif::NOP();
	vif::NOP();
	
 (the above simply transfers the microcode from RAM to VU1 micromem)	
																					   
 Now, it's important to note that DMA execution does NOT simply start at pList[Field] and
 then run all the way through a frame's worth of data.  There are actually several DMA lists,
 which are fired off individually.  Some of these (the groups) are logically connected so that 
 the end of one group's DMA (and the GS/VU activity it triggers) causes an interrupt,
 which the CPU handles by starting the DMA for another group (see interrupt.cpp)

 After the groups, there is the "immediate mode" DMA list, triggered by pEpilogue->pRender[Field]
 this is simply a raw DMA list that you can put anything you want into.  It starts at the value
 of pLoc after RenderWorld has been called (a terrible hack, as noted in the code :)
 
 All of the "immediate mode" rendering is considered to be part of pEpilogue.  It is terminated 
 by the RenderEpilogue() function, which just links in a final interrupt trigger, which will 
 get picked up by GsHandler(), and sGroup::pRenderGroup will be set to NULL, which is what
 WaitForRendering() uses to determine if rendering has finished.
 
 						  
						  

*/



namespace NxPs2
{



// begin a subroutine

void dma::BeginSub(eTag ID)
{
	Align(0,16);
	pSub = pLoc;
	dma::ID = ID;
	vu1::Loc = vu1::Buffer = 0;
}


// end a subroutine and return its address

uint64 dma::EndSub(void)
{
	uint64 GosubTag;
	Align(0,16);
	GosubTag = ((uint64)pSub<<32) | ((uint64)ID<<28) | (vu1::Loc & 0x3FF) << 16;
	Dbg_MsgAssert(ID!=refe && ID!=refs, ("refe and refs not supported in dma tag!"));
	if (ID==ref)
	{
		GosubTag |= (pLoc - pSub) >> 4;
	}
	return GosubTag;
}


// call a subroutine (using a dma::call or a dma::ref)

void dma::Gosub(uint Num, uint Path)
{
	register uint64 Tag = Gosubs[Num];

	Store64(Tag);

	switch (Path)
	{
	case 1:
		vif::BASE(vu1::Loc);
		vif::OFFSET(0);
		vu1::Loc += (Tag>>16) & 0x3FF;			// VUMem size
		break;

	case 2:
		vif::NOP();
		vif::NOP();
		break;

	case 3:
		pLoc += 8;								// no need to write anything since TTE=0
		break;

	#ifdef __PLAT_NGPS__
	default:
		printf("error: dma::Gosub() called with unrecognised path number\n");
		exit(1);
	#endif
	}

}



// begin a 3D subroutine

void dma::BeginSub3D(void)
{
	Align(0,16);
	pSub = pLoc;
	vu1::Loc = vu1::Buffer = 0;
}


// end a 3D subroutine and return its address

uint8 *dma::EndSub3D(void)
{
	((uint16 *)pSub)[1] |= vu1::Loc&0x3FF;
	return pSub;
}


// call a 3D subroutine (always using a dma::call)

void dma::Gosub3D(uint8 *pSub, uint RenderFlags)
{
	BeginTag(call,(uint)pSub);
	vif::BASE(vu1::Loc);
	vif::OFFSET(0);
	vif::ITOP(RenderFlags);
	EndTag();
	vu1::Loc += ((uint16 *)pSub)[1];
}



// dma list traversal function
uint8 *dma::NextTag(uint8 *pTag, bool stepInto)
{
	Dbg_MsgAssert((*(uint32 *)pTag&0x80000000)==0, ("IRQ bit set in dma tag"));
	Dbg_MsgAssert(*(uint32 *)pTag!=refe<<28 && *(uint32 *)pTag!=refs<<28, ("refe and refs not supported in dma tag!"));
	switch (*(uint32 *)pTag>>28)
	{
	case cnt:
		return pTag + 16 + ((uint)((uint16 *)pTag)[0] << 4);

	case next:
		return ((uint8 **)pTag)[1];

	case ref:
		return pTag + 16;

	case call:
		Dbg_MsgAssert(sp<2, ("dma call stack overflow"));
		if (stepInto)
		{
			Stack[sp++] = pTag + 16 + ((uint)((uint16 *)pTag)[0] << 4);
			return ((uint8 **)pTag)[1];
		}
		else
		{
			return pTag + 16 + ((uint)((uint16 *)pTag)[0] << 4);
		}

	case ret:
		Dbg_MsgAssert(sp>0, ("dma call stack underflow"));
		return Stack[--sp];

	case end:
	default:
		return NULL;
	}
}


#ifdef __PLAT_NGPS__

// auxilliary comparison function for dma sort
int dma::Cmp(const void *p1, const void *p2)
{
	return ((SSortElement *)p1)->z < ((SSortElement *)p2)->z ? -1 :
		   ((SSortElement *)p1)->z > ((SSortElement *)p2)->z ? +1 :
																0 ;
}


#if 0

// original version

// sort a dma list of mesh packets on z
// the z is stored in the unused ADDR word of the first cnt tag of the mesh packet

uint8 *dma::SortGroup(uint8 *pList)
{
	int num_elements;
	SSortElement *p_element;
	uint8 *p_tag, *p_end_tag, *p_prev_tag=NULL;
	eTag ID;

	// set array base at start of scratchpad
	SSortElement *p_array = (SSortElement *)0x70000000;

	// copy the addresses and z-values into array
	p_element = p_array;
	p_tag = pList;
	sp = 0;				// starting at top level of dma list
    while ((ID = (eTag)(*(uint32 *)p_tag>>28)) != end)
	{
		if (ID == cnt)
		{
			p_element->address = p_tag;
			p_element->z = ((float *)p_tag)[1];
			p_element++;
		}
		p_tag = NextTag(p_tag, false);
	}

	// check array fits within scratchpad
	num_elements = p_element-p_array;
	Dbg_MsgAssert(num_elements*sizeof(SSortElement)<=16384, ("Can't fit array in scratchpad"));

	// record address of end tag
	p_end_tag = p_tag;

	// sort the array
	qsort(p_array, num_elements, sizeof(SSortElement), Cmp);

	// reorder the dma list according to the sorted array
	for (p_element=p_array; p_elementaddress;

		do
		{
			p_prev_tag = p_tag;
			p_tag = NextTag(p_tag, false);
			ID = (eTag)(*(uint32 *)p_tag >> 28);
		} while (ID!=cnt && ID!=end);

		((uint8 **)p_prev_tag)[1] = (p_element+1)->address;
	}

	// patch up the final dma::next tag to point to the dma::end tag
	((uint8 **)p_prev_tag)[1] = p_end_tag;

	// chain through the whole list to adjust vu1 base pointers
	vu1::Loc = 0;
	for (p_tag = p_array->address; *(uint32 *)p_tag>>28 != end; p_tag = NextTag(p_tag, false))
	{
		//if (*(uint32 *)p_tag & 0x03FF0000)
		if (((uint8 *)p_tag)[11] == 0x03)
		{
			((uint16 *)p_tag)[4] &= ~0x3FF;
			((uint16 *)p_tag)[4] |= vu1::Loc;
			vu1::Loc += ((uint16 *)p_tag)[1];
		}
	}

	// return the address of the head tag
	return p_array->address;
}

#else

// new version:
// the list is sorted in segments corresponding to viewports

// sort a dma list of mesh packets on z
// the z is stored in the unused ADDR word of the first cnt tag of the mesh packet

uint8 *dma::SortGroup(uint8 *pList)
{
	int num_elements, i, num_segments;
	SSortElement *p_element, *p_segment[4];
	uint8 *p_tag, *p_end_tag, *p_prev_tag=NULL;
	eTag ID;

	// set array base at start of scratchpad
	SSortElement *p_array = (SSortElement *)0x70000000;

	// copy the addresses and z-values into array
	p_element = p_array;
	p_tag = pList;
	sp = 0;				// starting at top level of dma list
	num_segments = 0;
    while ((ID = (eTag)(*(uint32 *)p_tag>>28)) != end)
	{
		for (i=0; iaddress = p_tag;
			p_element->z = ((float *)p_tag)[1];
			p_element++;
		}
		p_tag = NextTag(p_tag, false);
	}

	// check array fits within scratchpad
	num_elements = p_element-p_array;
	Dbg_MsgAssert(num_elements*sizeof(SSortElement)<=16384, ("Can't fit array in scratchpad"));

	// record address of end tag
	p_end_tag = p_tag;

	// sort the array in segments
	if (num_segments)
	{
		for (i=0; iaddress;

		do
		{
			p_prev_tag = p_tag;
			p_tag = NextTag(p_tag, false);
			ID = (eTag)(*(uint32 *)p_tag >> 28);
		} while (ID!=cnt && ID!=end);

		((uint8 **)p_prev_tag)[1] = (p_element+1)->address;
	}

	// patch up the final dma::next tag to point to the dma::end tag
	((uint8 **)p_prev_tag)[1] = p_end_tag;

	// chain through the whole list to adjust vu1 base pointers
	vu1::Loc = 0;
	for (p_tag = p_array->address; *(uint32 *)p_tag>>28 != end; p_tag = NextTag(p_tag, false))
	{
		if (*(uint32 *)p_tag & 0x3FF0000)
		{
			((uint16 *)p_tag)[4] &= ~0x3FF;
			((uint16 *)p_tag)[4] |= vu1::Loc;
			vu1::Loc += ((uint16 *)p_tag)[1];
		}
	}

	// return the address of the head tag
	return p_array->address;
}

#endif


#endif


void dma::BeginList(void *pGroup)
{
#ifdef __PLAT_NGPS__
	// assume group isn't used
	((sGroup *)pGroup)->Used[render::Field] = false;

	// set the dma list pointer
	((sGroup *)pGroup)->pRender[render::Field] = pLoc;

	// VIF1 and VU1 setup
	BeginTag(cnt, 0xFF000000);		// bit of a cheat, so it will stay at the start of any sorted list
	vif::FLUSH();
	vif::STMASK(0);
	vif::STMOD(0);
	vif::STCYCL(1,1);
	vif::BASE(0);
	vif::OFFSET(0);
	vif::MSCAL(VU1_ADDR(Setup));
	EndTag();

	dma::Tag(dma::next, 0, 0);
	vif::NOP();
	vif::NOP();

	((sGroup *)pGroup)->vu1_loc = 0;
	((sGroup *)pGroup)->p_tag = pTag;
#endif
}


void dma::EndList(void *pGroup)
{

	SetList(pGroup);

	// end dma list for this group
	BeginTag(end, 0);
	#if USE_INTERRUPTS
	//vif::BASE(((sGroup *)pGroup)->vu1_loc);
	vif::BASE(vu1::Loc);
	vif::OFFSET(0);
	vu1::Loc = 0;   							// must do this as a relative prim for a sortable list...
	gs::BeginPrim(REL,0,0);
	gs::Reg1(gs::SIGNAL, PackSIGNAL(1,1));		// signal the end of rendering this group
	gs::EndPrim(1);
	vif::MSCAL(VU1_ADDR(Parser));
	#endif
	EndTag();
	((uint16 *)pTag)[1] |= vu1::Loc & 0x3FF;	// must write some code for doing this automatically
}


void dma::ReallySetList(void *pGroup)
{

	// finish with the previous dma context
	if (sp_group)
	{
		// ensure the last tag was a 'next'...

		// get the tag ID
		uint ID = *(uint32 *)pTag>>28;

		// take care of 'refe' and 'refs'
		Dbg_MsgAssert(ID!=refe && ID!=refs, ("refe and refs not supported in dma tag!"));

		// take care of 'call' and 'ref'
		if (ID==call || ID==ref)
		{
			Tag(next, 0, 0);
			vif::NOP();
			vif::NOP();
		}

		// take care of 'cnt'
		else if (ID==cnt)
		{
			pTag[3] = next<<4;
		}

		// 'end' and 'ret' won't have anything after them in the same context
		// and 'next' is fine as it is

		// save the vu1 location and dma tag location
		((sGroup *)sp_group)->vu1_loc = vu1::Loc;
		((sGroup *)sp_group)->p_tag = pTag;
	}

	// change bucket
	sp_group = pGroup;

	// set up the new bucket
	if (pGroup)
	{
		// restore the vu1 location and dma tag location
		vu1::Loc = ((sGroup *)pGroup)->vu1_loc;
		pTag = ((sGroup *)pGroup)->p_tag;

		// patch the pointer of the dangling 'next' tag
		((uint32 *)pTag)[1] = (uint32)pLoc;
	}
}




int dma::GetDmaSize(uint8 *pTag)
{
	return (*(uint16 *)pTag + 1) << 4;	// (QWC+1)*16 bytes
}



int dma::GetNumVertices(uint8 *pTag)
{
	// get start and end of dma packet
	uint8 *p_start = pTag + 8;
	uint8 *p_end   = pTag + 16 + (*(uint16 *)pTag << 4);

	// parse vifcodes
	uint8 *p_code = p_start;
	int   num_verts = 0;
	do
	{
		if (((p_code[3] & 0x7F) == 0x05) && (p_code[0] == 1))		// look for STMOD(1)
		{
			p_code = vif::NextCode(p_code);
			Dbg_MsgAssert((p_code[3] & 0x7E)==0x6C, ("0x%08X: expected UNPACK V4_16 or V4_32", *(uint32 *)p_code));
			num_verts += p_code[2];
		}

		p_code=vif::NextCode(p_code);
	}
	while (p_code < p_end);

	return num_verts;
}



int dma::GetNumTris(uint8 *pTag)
{
	// get start and end of dma packet
	uint8 *p_start = pTag + 8;
	uint8 *p_end   = pTag + 16 + (*(uint16 *)pTag << 4);

	// parse vifcodes
	uint8 *p_code = p_start;
	int   num_tris = 0;
	int   num_verts;
	do
	{
		if (((p_code[3] & 0x7F) == 0x05) && (p_code[0] == 1))		// look for STMOD(1)
		{
			p_code = vif::NextCode(p_code);
			Dbg_MsgAssert((p_code[3] & 0x7E)==0x6C, ("0x%08X: expected UNPACK V4_16 or V4_32", *(uint32 *)p_code));
			num_verts = p_code[2];

			// loop over verts, counting the adc bits which are zero
			if (p_code[3] & 0x01)	// V4_16
			{
				uint16 *p_adc = ((uint16 *)p_code)+5;
				for (int i=0; i> 2;
			for (i=0; i> (p_code[3] & 0x03);
			break;
		}

		p_code=vif::NextCode(p_code);
	}
	while (p_code < p_end);

	return bit_length;
}


void dma::ExtractXYZs(uint8 *pTag, uint8 *pArray)
{
	// get start and end of dma packet
	uint8 *p_start = pTag + 8;
	uint8 *p_end   = pTag + 16 + (*(uint16 *)pTag << 4);

	// parse vifcodes
	uint8 *p_code = p_start;
	int i, num_words;
	sint32 *p_dest = (sint32 *)pArray;

	do
	{
		if ((*(uint32 *)p_code & 0x7F000001) == 0x05000001)
		{
			p_code = vif::NextCode(p_code);
			Dbg_MsgAssert((p_code[3] & 0x7E)==0x6C, ("0x%08X: expected UNPACK V4_16 or V4_32", *(uint32 *)p_code));

			num_words = (int)((((uint32)p_code[2]-1)&0xFF)+1) << 2;

			if ((p_code[3] & 0x7F) == 0x6C)
			{
				// 32 bit
				sint32 *p_source = (sint32 *)(p_code+4);
				for (i=0; i1 && seenMSCAL)
			{
				// look for giftag
				if (unpack_num==-1)
				{
					p_giftag = (uint32 *)p_code + 1;
					nloop = p_giftag[0] & 0x7FFF;
					nreg  = p_giftag[1] >> 28;
				}

				// look for vertex elements
				else
				{
					p_unpack[unpack_num] = p_code;
				}

				// next element
				unpack_num++;

			}

		}
		else
		{
			if ((p_code[3] & 0x7F) == 0x14)		// mscal
			{
				seenMSCAL = true;
			}
		}

		// step to next vifcode
		p_code = vif::NextCode(p_code);

		// have we found all 5 unpacks of a vertex packet?
		if (unpack_num==5)
		{
			// perform compression...

			// set element pointers
			p_texcrds_source = p_texcrds_dest = (uint32 *)(p_unpack[0]+4);
			p_weights_source = p_weights_dest = (uint8  *)(p_unpack[1]+4);
			p_normal_source	 = p_normal_dest  = (uint16 *)(p_unpack[2]+4);
			p_colour_source	 = p_colour_dest  = (uint32 *)(p_unpack[3]+4);
			p_coords_source	 = p_coords_dest  = (uint32 *)(p_unpack[4]+4);

			// set datasize for tex coords
			texcrds_size = ((p_unpack[0][3] & 0x07) == 0x04) ? 2 : 1;

			// loop over source vertices
			num_squeezed = 0;
			for (i=0; i> 4;
}




//---------------------------------
//		S T A T I C   D A T A
//---------------------------------

uint8 *		dma::pBase;			// base of dynamic DMA buffer for this frame
uint8 *		dma::pLoc;			// current position in it that we are building DMA packets
uint8 *		dma::pTag;
uint8 *		dma::pPrebuiltBuffer;
uint8 *		dma::pDummyBuffer;		// (Mick) used to simulate memory usage
uint8 *		dma::pRuntimeBuffer;
uint8 *		dma::pList[2];
uint64 *	dma::Gosubs;
uint8 *		dma::pSub;
dma::eTag	dma::ID;
int    		dma::sp;
uint8 *		dma::Stack[2];
void *		dma::sp_group;
int			dma::size;

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/dma.h
================================================
#ifndef __DMA_H
#define __DMA_H

#include "render.h"


namespace NxPs2
{


class CGeomNode;



#define PREBUILT_DMA_BUFFER_SIZE 32768		    		

// Random crashes in the engine can frequntly be traced to DMA buffer overflow
// We probably need to add some cheking for this.
//#define SPLIT_SCREEN_DMA_BUFFER_SIZE  	(675000 * 2)			// split screen needs more
//#define NON_DEBUG_DMA_BUFFER_SIZE  		(500000 * 2)			// non split screen needs less
#define NON_DEBUG_DMA_BUFFER_SIZE  		((int)((675000 * 2)&0xffffffe0))			// non split screen needs less
#define DEBUG_DMA_BUFFER_SIZE  			((int)(16256000 & 0xffffffe0))				// Mick, 12 MB for debugging....

class dma
{

public:

	enum eTag
	{	refe = 0,
		cnt  = 1,
		next = 2,
		ref  = 3,
		refs = 4,
		call = 5,
		ret  = 6,
		end  = 7
	};

	struct SSortElement
	{
		float z;
		uint8 *address;
	};

	//-------------------------------------------
	//		S T A T I C   F U N C T I O N S
	//-------------------------------------------

	static void   Align(uint Offset, uint Boundary);
	static void   Align();
	static void   Store32(uint32 Data);
	static void   Store32(uint32 Data1, uint32 Data2 );
	static void   Store32(uint32 Data1, uint32 Data2, uint32 Data3 );
	static void   Store32(uint32 Data1, uint32 Data2, uint32 Data3, uint32 Data4 );
	static void   Store64(uint64 Data);
	static void   Tag(eTag ID, uint QWC, uint ADDR);
	static void   BeginTag(eTag ID, uint ADDR);
	static void   EndTag(void);
	static void   BeginSub(eTag ID);
	static uint64 EndSub(void);
	static void   Gosub(uint Num, uint Path);
	static void   BeginSub3D(void);
	static uint8 *EndSub3D(void);
	static void   Gosub3D(uint8 *pSub, uint RenderFlags);
	static uint8 *NextTag(uint8 *pTag, bool stepInto);
	static int    Cmp(const void *p1, const void *p2);
	static uint8 *SortGroup(uint8 *pList);
	static void   BeginList(void *pGroup);
	static void   EndList(void *pGroup);
	static void   SetList(void *pGroup);
	static void   ReallySetList(void *pGroup);
	static int    GetDmaSize(uint8 *pTag);
	static int    GetNumVertices(uint8 *pTag);
	static int    GetNumTris(uint8 *pTag);
	static void   Copy(uint8 *pTag, uint8 *pDest);
	static int    GetBitLengthXYZ(uint8 *pTag);
	static void   TransferValues(uint8 *pTag, uint8 *pArray, int size, int dir, uint32 vifcodeMask, uint32 vifcodePattern);
	static void   TransferColours(uint8 *pTag, uint8 *pArray, int dir);
	static void   ExtractXYZs(uint8 *pTag, uint8 *pArray);
	static void   ReplaceXYZs(uint8 *pTag, uint8 *pArray, bool skipW = false);
	static void   ExtractRGBAs(uint8 *pTag, uint8 *pArray);
	static void   ReplaceRGBAs(uint8 *pTag, uint8 *pArray);
	static void   ExtractSTs(uint8 *pTag, uint8 *pArray);
	static void   ReplaceSTs(uint8 *pTag, uint8 *pArray);
	static void   TransformSTs(uint8 *pTag, const Mth::Matrix &mat);
	static void	  ConvertXYZToFloat(Mth::Vector &vec, sint32 *p_xyz);
	static void	  ConvertXYZToFloat(Mth::Vector &vec, sint32 *p_xyz, const Mth::Vector ¢er);
	static void	  ConvertXYZToFloat(Mth::Vector &vec, sint32 *p_xyz, const sint32 *p_center);
	static void	  ConvertSTToFloat(float & s, float & t, sint32 *p_st);
	static void	  ConvertFloatToXYZ(sint32 *p_xyz, Mth::Vector &vec);
	static void	  ConvertFloatToXYZ(sint32 *p_xyz, Mth::Vector &vec, const Mth::Vector ¢er);
	static void	  ConvertFloatToXYZ(sint32 *p_xyz, Mth::Vector &vec, const sint32 *p_center);
	static void	  ConvertFloatToST(sint32 *p_st, float & s, float & t);
	static void   SqueezeADC(uint8 *pTag);
	static void   SqueezeNOP(uint8 *pTag);


	//---------------------------------
	//		S T A T I C   D A T A
	//---------------------------------

	static uint8 *		pBase;
	static uint8 *		pLoc;
	static uint8 *		pTag;
	static uint8 *		pPrebuiltBuffer;
	static uint8 *		pDummyBuffer;
	static uint8 *		pRuntimeBuffer;
	static uint8 *		pList[2];
	static uint64 *		Gosubs;
	static uint8 *		pSub;
	static eTag			ID;
	static int			sp;
	static uint8 *		Stack[2];
	static void *		sp_group;
	static int			size;

}; // class dma


// -------------------------------------------------
//        INLINE FUNCTIONS
// -------------------------------------------------



// align to Boundary, then add Offset
// Boundary must be a power of 2

inline void dma::Align(uint Offset, uint Boundary)
{
	uint8 *NewDmaLoc = (uint8 *)((((uint)pLoc - Offset + Boundary - 1) & ((uint)(-(int)Boundary))) + Offset);
	while (pLoc < NewDmaLoc)
		*pLoc++ = 0;
}


// quick version for dma list building; assumes pLoc already word-aligned and you want it quadword-aligned

inline void dma::Align()
{
	while ((uint)pLoc & 0xF)
	{
		*(uint32 *)pLoc = 0;
		pLoc += 4;
	}
}





// store a word

inline void dma::Store32(uint32 Data)
{
	#if 0
	*(uint32 *)pLoc = Data;
	pLoc += 4;
	#else
	uint32 *p_loc = (uint32*)pLoc;
	p_loc[0] = Data;
	pLoc = (uint8*) (p_loc + 1);
	#endif	
}

// Store two words, quicker this way, as we only have to update pLoc once
inline void dma::Store32(uint32 Data1, uint32 Data2)
{
	uint32 *p_loc = (uint32*)pLoc;
	p_loc[0] = Data1;
	p_loc[1] = Data2;
	pLoc = (uint8*) (p_loc + 2);
}

// Store three words, quicker this way, as we only have to update pLoc once
inline void dma::Store32(uint32 Data1, uint32 Data2, uint32 Data3)
{
	uint32 *p_loc = (uint32*)pLoc;
	p_loc[0] = Data1;
	p_loc[1] = Data2;
	p_loc[2] = Data3;
	pLoc = (uint8*) (p_loc + 3);
}

// Store four words, quicker this way, as we only have to update pLoc once
inline void dma::Store32(uint32 Data1, uint32 Data2, uint32 Data3, uint32 Data4)
{
	uint32 *p_loc = (uint32*)pLoc;
	p_loc[0] = Data1;
	p_loc[1] = Data2;
	p_loc[2] = Data3;
	p_loc[3] = Data4;
	pLoc = (uint8*) (p_loc + 4);
}


// store a dword

inline void dma::Store64(uint64 Data)
{
	((uint32 *)pLoc)[0] = (uint32)Data;		// break into 2 words
	((uint32 *)pLoc)[1] = (uint32)(Data>>32);	// because pLoc is only word-aligned
	pLoc += 8;
}



//--------------------------
//		D M A   T A G S
//--------------------------



// generic source chain tag
//
//	 63  62                        32 31  30  28 27 26 25     16 15            0
//	Ŀ
//	SPR          ADDR          0000IRQ  ID   PCE     -          QWC      
//	

inline void dma::Tag(eTag ID, uint QWC, uint ADDR)
{
	// assumes pLoc already qword aligned
	pTag = pLoc;
	Store32(ID<<28 | QWC, ADDR);
}


// Begin-End style

inline void dma::BeginTag(eTag ID, uint ADDR)
{
	// assumes pLoc already qword aligned
	pTag = pLoc;									// record tag location for patching later
	Store32(ID<<28, ADDR);
}


// Begin-End style

inline void dma::EndTag(void)
{
	uint ID = (*(uint32 *)pTag >> 28) & 7;				// get the tag ID field
	Align();											// align to qword boundary
	if (ID!=ref && ID!=refe && ID!=refs)				// check that the QWC is patchable
	{
		((uint16 *)pTag)[0] = (pLoc - pTag - 8) >> 4;
	}
}


inline void	 dma::ConvertXYZToFloat(Mth::Vector &vec, sint32 *p_xyz)
{
	vec[X] = ((float) *(p_xyz++)) * RECIPROCAL_SUB_INCH_PRECISION;
	vec[Y] = ((float) *(p_xyz++)) * RECIPROCAL_SUB_INCH_PRECISION;
	vec[Z] = ((float) *(p_xyz)  ) * RECIPROCAL_SUB_INCH_PRECISION;
	vec[W] = 1.0f;
}

inline void	 dma::ConvertXYZToFloat(Mth::Vector &vec, sint32 *p_xyz, const Mth::Vector ¢er)
{
	vec[X] = (((float) *(p_xyz++)) * RECIPROCAL_SUB_INCH_PRECISION) + center[X];
	vec[Y] = (((float) *(p_xyz++)) * RECIPROCAL_SUB_INCH_PRECISION) + center[Y];
	vec[Z] = (((float) *(p_xyz)  ) * RECIPROCAL_SUB_INCH_PRECISION) + center[Z];
	vec[W] = 1.0f;
}

inline void	 dma::ConvertXYZToFloat(Mth::Vector &vec, sint32 *p_xyz, const sint32 *p_center)
{
	vec[X] = ((float) (*(p_xyz++) + *(p_center++))) * RECIPROCAL_SUB_INCH_PRECISION;
	vec[Y] = ((float) (*(p_xyz++) + *(p_center++))) * RECIPROCAL_SUB_INCH_PRECISION;
	vec[Z] = ((float) (*(p_xyz)   + *(p_center)  )) * RECIPROCAL_SUB_INCH_PRECISION;
	vec[W] = 1.0f;
}

inline void	 dma::ConvertSTToFloat(float & s, float & t, sint32 *p_st)
{
	s = ((float) *(p_st++)) * (1.0f / 4096.0f);
	t = ((float) *(p_st)  ) * (1.0f / 4096.0f);
}

inline void	 dma::ConvertFloatToXYZ(sint32 *p_xyz, Mth::Vector &vec)
{
	*(p_xyz++) = (sint32) (vec[X] * SUB_INCH_PRECISION);
	*(p_xyz++) = (sint32) (vec[Y] * SUB_INCH_PRECISION);
	*(p_xyz)   = (sint32) (vec[Z] * SUB_INCH_PRECISION);
}

inline void	 dma::ConvertFloatToXYZ(sint32 *p_xyz, Mth::Vector &vec, const sint32 *p_center)
{
	*(p_xyz++) = ((sint32) (vec[X] * SUB_INCH_PRECISION)) - *(p_center++);
	*(p_xyz++) = ((sint32) (vec[Y] * SUB_INCH_PRECISION)) - *(p_center++);
	*(p_xyz)   = ((sint32) (vec[Z] * SUB_INCH_PRECISION)) - *(p_center);
}

inline void	 dma::ConvertFloatToST(sint32 *p_st, float & s, float & t)
{
	*(p_st++) = (sint32) (s * 4096.0f);
	*(p_st)   = (sint32) (t * 4096.0f);
}

inline void dma::SetList(void *pGroup)
{
	// do nothing if we're already in the right dma context
	// (Mick) This part has been moved to an inline function 
	// since the overhead of setting up the stack frame 
	// is large, this is a more efficient way of taking advantage of
	// group coherency.   This represents a 25% speed improvmeent for this function
	// just 0.25% of a frame.  But every little helps.
	if (sp_group != pGroup)
	{
		ReallySetList(pGroup);
	}
}

} // namespace NxPs2


#endif // __DMA_H



================================================
FILE: Code/Gfx/NGPS/NX/dmacalls.cpp
================================================
#include 
#include 
#include 
#include 
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gif.h"
#include "gs.h"
#include "material.h"
#include "dmacalls.h"
#include "vu1code.h"
#include "switches.h"
#include "render.h"

namespace NxPs2
{


uint8 *pSubroutine;


uint32	*p_patch_PRMODE;
uint32	*p_patch_ALPHA;
uint32	*p_patch_ALPHA2;

//-----------------------------------------------------------
//		H A N D L I N G   D M A   S U B R O U T I N E S
//-----------------------------------------------------------


// begin a subroutine

uint8 *BeginDmaSubroutine(void)
{
	dma::Align(0,16);
	pSubroutine = dma::pLoc;
	vu1::Loc = vu1::Buffer = 0;
	return pSubroutine;
}


// end a subroutine and return its address

void EndDmaSubroutine(void)
{
	((uint16 *)pSubroutine)[1] |= vu1::Loc&0x3FF;
}

#if 0

// call a subroutine, Path1

void Gosub1(uint Num)
{
	uint8 *pSub = Subroutines[Num];
	dma::Tag(dma::call, 0, (uint)pSub);
	vif::BASE(vu1::Loc);
	vif::OFFSET(0);
	vu1::Loc += ((uint16 *)pSub)[1];
}


// call a subroutine, Path2

void Gosub2(uint Num)
{
	dma::Tag(dma::call, 0, (uint)Subroutines[Num]);
	vif::NOP();
	vif::NOP();
}

#endif



//-----------------------------------------
//		D M A   S U B R O U T I N E S
//-----------------------------------------



void BuildDmaSubroutines(void)
{
	//-------------------------------
	//		C L E A R   V R A M
	//-------------------------------

	dma::BeginSub(dma::next);
	dma::BeginTag(dma::end,0);
	vif::BeginDIRECT();
	gif::BeginTag2(gs::A_D, 1, PACKED, SPRITE, 1);
	gs::Reg2(gs::FRAME_1,		PackFRAME(0,16,PSMCT32,0));
	gs::Reg2(gs::ZBUF_1,		PackZBUF(0,0,1));
	gs::Reg2(gs::TEST_1,		PackTEST(0,0,0,0,0,0,1,ZALWAYS));
	gs::Reg2(gs::XYOFFSET_1,	PackXYOFFSET(0,0));
	gs::Reg2(gs::SCISSOR_1,		PackSCISSOR(0,0x7FF,0,0x7FF));
	gs::Reg2(gs::PRMODECONT,	PackPRMODECONT(1));
	gs::Reg2(gs::SCANMSK,		PackSCANMSK(0));
	gs::Reg2(gs::FBA_1,			PackFBA(0));
	gs::Reg2(gs::DTHE,			PackDTHE(0));
	gs::Reg2(gs::TEXA,			PackTEXA(0x00,0,0x80));
	gs::Reg2(gs::RGBAQ,			PackRGBAQ(0,0,0,0,0));
	gs::Reg2(gs::XYZ2,			PackXYZ(0,0,0));
	gs::Reg2(gs::XYZ2,			PackXYZ(0x3FFF,0x3FFF,0));
	gs::Reg2(gs::TEXFLUSH,		0);
	gif::EndTag2(1);
	vif::EndDIRECT();
	vif::FLUSH();
	dma::EndTag();
	dma::Gosubs[CLEAR_VRAM] = dma::EndSub();



	//---------------------------------------
	//		F L I P   A N D   C L E A R
	//---------------------------------------

	#define CLEAR_COLOUR 0x00706050
	
	uint32 screen_w = HRES << 4;
	uint32 screen_h = VRES << 4;

	// begin Path2 subroutine
	dma::BeginSub(dma::ref);

	// wait in case something's still drawing from last frame
	vif::FLUSHA();

	// send everything PATH2
	vif::BeginDIRECT();

	// register setup
	gif::BeginTag2(gs::A_D, 1, PACKED, 0, 0);
	p_patch_ALPHA = (uint32*) (dma::pLoc + 4);
	gs::Reg2(gs::ALPHA_1,		PackALPHA(0,1,2,1,128));		// tweak this alpha for motion blur!
	gs::Reg2(gs::DIMX,			PackDIMX(-4,0,-3,1,2,-2,3,-1,-3,1,-4,0,3,-1,2,-2));
	gs::Reg2(gs::DTHE,			PackDTHE(1));
	gs::Reg2(gs::FBA_1,			PackFBA(0));
	gs::Reg2(gs::FRAME_1,		PackFRAME(DISPLAY_START,HRES/64,PSMCT16S,0x00000000));
	gs::Reg2(gs::PRMODECONT,	PackPRMODECONT(1));
	gs::Reg2(gs::SCANMSK,		PackSCANMSK(0));
	gs::Reg2(gs::SCISSOR_1,		PackSCISSOR(0,HRES - 1,0,VRES - 1));
	gs::Reg2(gs::TEST_1,		PackTEST(0,0,0,0,0,0,1,ZALWAYS));
	gs::Reg2(gs::TEX0_1,		PackTEX0(0,HRES/64,PSMCT32,10,10,1,DECAL,0,0,0,0,0));
	gs::Reg2(gs::TEX1_1,		PackTEX1(1,0,NEAREST,NEAREST,0,0,0));
	gs::Reg2(gs::TEXFLUSH,		0);
	gs::Reg2(gs::XYOFFSET_1,	PackXYOFFSET(0,0));
	gs::Reg2(gs::ZBUF_1,		PackZBUF(ZBUFFER_START,PSMZ24,0));
	gif::EndTag2(0);

	// using a set of screen-high textured sprites, copy draw buffer to disp buffer and clear z-buffer
	gif::BeginTag2(gs::UV|gs::XYZ2<<4, 2, PACKED, SPRITE|TME|FST|ABE, 1);
	for (uint32 x=0x0000; x
#include "math.h"
#include "fx.h"
#include "asmdma.h"


namespace NxPs2
{



void Fx::SetupFogPalette(uint32 FogRGBA, float Exponent)
{
	int i,j;
	float t, alpha = (float)(int)(FogRGBA>>24);

	for (i=255,t=1.0f/256.0f; i>=0; i--,t+=1.0f/256.0f)
	{
		j = (i&0xE7) | (i&0x10)>>1 | (i&0x08)<<1;
		FogPalette[j] = (FogRGBA & 0x00FFFFFF) | (uint32)(int)(alpha * powf(t,Exponent) + 0.5f)<<24;
	}
}



} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/fx.h
================================================

#ifndef __NX_NGPS_FX_H
#define __NX_NGPS_FX_H

namespace NxPs2
{

namespace Fx
{

void SetupFogPalette(uint32 FogRGBA, float Exponent);

} // namespace Fx

} // namespace NxPs2

#endif // __NX_NGPS_FX_H


================================================
FILE: Code/Gfx/NGPS/NX/geomnode.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		THPS4													**
**																			**
**	Module:			geomnode				 								**
**																			**
**	File name:		geomnode.cpp											**
**																			**
**	Created by:		00/00/00	-	mrd										**
**																			**
**	Description:	process-in-place geometry nodes for PS2					**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
//#include 
#include "render.h"
#include "group.h"
#include "geomnode.h"
#include "vu1context.h"
#include "occlude.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gs.h"
#include "vu1code.h"
#include "light.h"
#include "line.h"
#include "dmacalls.h"
#include "scene.h"
#include "texture.h"
#include "switches.h"


// Mick - Defining this __GEOM_STATS__ will enable bunch of counters in CGeomNode::Render
// 
#define	__GEOM_STATS__

#define	__ASSERT_ON_ZERO_RADIUS__  0

#define	OCCLUSION_CACHE 0

// Just in case we forgot to take it out, this does it automatically on final burns   
#ifndef	__NOPT_ASSERT__
#undef	__GEOM_STATS__
#endif   
   
#ifdef	__GEOM_STATS__
#define	GEOMSTATINC(x) {x++;}
#else
#define	GEOMSTATINC(x) {}
#endif


//float	gClipDist = 0.1f;

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace NxPs2
{




int geom_stats_total=0;
int geom_stats_inactive =0;
int geom_stats_sky=0;
int geom_stats_transformed=0;
int geom_stats_skeletal=0;
int geom_stats_camera_sphere=0;
int geom_stats_clipcull=0;
int geom_stats_culled=0;
int geom_stats_leaf_culled=0;
int geom_stats_boxcheck=0;
int geom_stats_occludecheck=0;
int geom_stats_occluded=0;
int geom_stats_colored=0;
int geom_stats_leaf=0;
int geom_stats_wibbleUV=0;
int geom_stats_wibbleVC=0;
int geom_stats_sendcontext=0;
int geom_stats_sorted=0;
int geom_stats_shadow=0;



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

//#define	__LOD_MULTIPASS__

#define SHADOW_CAM_FRUSTUM_HALF_WIDTH	64.0f
#define SHADOW_CAM_FRUSTUM_HALF_HEIGHT	64.0f

#define GET_FLAG(FLAG) (m_flags & (FLAG) ? true : false)
#define SET_FLAG(FLAG) m_flags = yes ? m_flags|FLAG : m_flags&~FLAG

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

CGeomNode CGeomNode::sWorld;
CGeomNode CGeomNode::sDatabase;

CGeomNode *GpInstanceNode=NULL;

#ifdef	__NOPT_ASSERT__
// Mick: patch variables for debugging toggle of multi-passes
uint32		gPassMask1 = 0; 		// 1<<6 | 1<<7  (0x40, 0x80)
uint32		gPassMask0 = 0; 		// 1<<6 | 1<<7  (0x40, 0x80)
#endif 

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

#ifdef __PLAT_NGPS__

// This is only used in a couple of places, and it's 2% of CPU time in a release build
// so I'm making it inline for now to improve cache coherency 
// TODO: would be a good candidate for VU0 optimization
// assuming the ViewVolume could be stored in VU mem or regs?
inline bool CGeomNode::SphereIsOutsideViewVolume(Mth::Vector& s)
{
	return
		s[3] < s[2] - render::Near						||
		s[3] < render::sx * s[2] - render::cx * s[0]	||
		s[3] < render::sx * s[2] + render::cx * s[0]	||
		s[3] < render::sy * s[2] + render::cy * s[1] 	||
		s[3] < render::sy * s[2] - render::cy * s[1]	||
		s[3] < render::Far - s[2];
}


// Same with this
inline bool CGeomNode::SphereIsInsideOuterVolume(Mth::Vector& s)
{
	return
		s[3] < render::Near - s[2]						&&
		s[3] < -render::Sx * s[2] + render::Cx * s[0]	&&
		s[3] < -render::Sx * s[2] - render::Cx * s[0]	&&
		s[3] < -render::Sy * s[2] - render::Cy * s[1]	&&
		s[3] < -render::Sy * s[2] + render::Cy * s[1]	&&
		s[3] < s[2] - render::Far;
}


bool CGeomNode::BoxIsOutsideViewVolume(Mth::Vector& b, Mth::Vector& s)
{
	return ((render::FrustumFlagsPlus[0]  && (s[0]+b[0] <= render::CameraToWorld[3][0])) ||
			(render::FrustumFlagsPlus[1]  && (s[1]+b[1] <= render::CameraToWorld[3][1])) ||
			(render::FrustumFlagsPlus[2]  && (s[2]+b[2] <= render::CameraToWorld[3][2])) ||
			(render::FrustumFlagsMinus[0] && (s[0]-b[0] >= render::CameraToWorld[3][0])) ||
			(render::FrustumFlagsMinus[1] && (s[1]-b[1] >= render::CameraToWorld[3][1])) ||
			(render::FrustumFlagsMinus[2] && (s[2]-b[2] >= render::CameraToWorld[3][2])));
}


inline bool CGeomNode::NeedsClipping(Mth::Vector& s)
{
	return true;
}


void CGeomNode::LinkDma(uint8 *p_newTail)
{
	dma::Tag(dma::next, 0, 0);	// will be patched up by next call to LinkDma()
	vif::NOP();
	vif::NOP();
	((uint *)u3.mp_group->pListEnd[render::Field])[1] = (uint)p_newTail;
	u3.mp_group->pListEnd[render::Field] = dma::pLoc - 16;
}

// returns the number of leaf nodes
int CGeomNode::ProcessNodeInPlace(uint8 *p_baseAddress)
{
	int leaves = 0;
	
	if (!(m_flags & NODEFLAG_LEAF))
	{
		leaves++;
		// convert transform offset to pointer
		if ((int)u2.mp_transform != -1)
		{
			u2.mp_transform = (Mth::Matrix *)( (int)u2.mp_transform + p_baseAddress );
		}
		else
		{
			u2.mp_transform = (Mth::Matrix *)0;
		}
	}

	uint32	tex = GetTextureChecksum();
	SetTextureChecksum(0);  			// clear it in case we don't find it

	// convert group checksum to pointer
	if ((m_flags & NODEFLAG_LEAF) && u1.mp_dma)
	{
		sGroup *pGroup;
		for (pGroup=sGroup::pHead; pGroup; pGroup=pGroup->pNext)
			if (pGroup->Checksum == (uint)u3.mp_group)
				break;
		Dbg_MsgAssert(pGroup, ("Couldn't group checksum #%d\n", (uint)u3.mp_group));
		u3.mp_group = pGroup;
		
		// Resolve the group+texture checksum into a texture pointer		
//		printf ("%x\n",GetTextureChecksum());
		if (tex)
		{
			sGroup * p_group = GetGroup();
			sScene * p_scene = p_group->pScene;
			sTexture *p_textures = p_scene->pTextures;
			int num_textures = p_scene->NumTextures;
			for (int i=0;iChecksum)
				{
					//printf ("Found %dx%dx%d %x mips at 0x%x (%d)\n",p_textures[i].GetWidth(),p_textures[i].GetHeight(),p_textures[i].GetClutBitdepth(),p_textures[i].GetNumMipmaps(),*(p_textures+i),i);
					SetTextureChecksum((uint32)(p_textures+i));
					break;  
				}
			}
//			printf ("-> 0x%x\n",GetTextureChecksum());
		}
		
	}

	if (m_flags & NODEFLAG_LEAF)
	{
		// convert dma offset to pointer
		if ((int)u1.mp_dma != -1)
		{
			u1.mp_dma = (uint)u1.mp_dma + p_baseAddress;
		}
		else
		{
			u1.mp_dma = (uint8 *)0;
		}
	}
	else
	{
		// convert child offset to pointer
		if ((int)u1.mp_child != -1)
		{
			u1.mp_child = (CGeomNode *)( (int)u1.mp_child + p_baseAddress );
		}
		else
		{
			u1.mp_child = (CGeomNode *)0;
		}
	}

	// convert sibling offset to pointer
	if ((int)mp_sibling != -1)
	{
		mp_sibling = (CGeomNode *)( (int)mp_sibling + p_baseAddress );
	}
	else
	{
		mp_sibling = (CGeomNode *)0;
	}

	// convert LOD offset to pointer
	if ((int)mp_next_LOD != -1)
	{
		mp_next_LOD = (CGeomNode *)( (int)mp_next_LOD + p_baseAddress );
	}
	else
	{
		mp_next_LOD = (CGeomNode *)0;
	}

	// convert uv wibble offset to pointer
	if (m_flags & NODEFLAG_UVWIBBLE)
	{
		if (!(m_flags & NODEFLAG_ENVMAPPED) || !(m_flags & NODEFLAG_LEAF))
		{
			Dbg_MsgAssert ((int)mp_uv_wibble!=-1, ("Null uv wibble pointer on a uv wibbled node"));
			mp_uv_wibble = (float *)( (int)mp_uv_wibble + p_baseAddress );
			Dbg_MsgAssert (!(((uint32)mp_uv_wibble) & 0xf0000003),("Corrupt UV wibble offset (0x%x) in node %s\n",((int)mp_uv_wibble - (int)p_baseAddress), Script::FindChecksumName(m_checksum)));
		}
		else
		{
			mp_uv_wibble = (float *)0;
			m_flags &= ~NODEFLAG_UVWIBBLE;
		}
	}
	
	// convert vc wibble offset to pointer
	if ((int)mp_vc_wibble != -1)
	{
		// first convert the wibble offset itself
		mp_vc_wibble = (uint32 *)( (int)mp_vc_wibble + p_baseAddress );

		// now the mesh info
		uint32 *p_meshdata = mp_vc_wibble;
		int seq, num_seqs, vert, num_verts;

		num_seqs = *p_meshdata++;
		for (seq=0; seqmp_sibling)
		{
			leaves += p_child->ProcessNodeInPlace(p_baseAddress);
		}
	}

	// recursively process any LODs
	if (mp_next_LOD)
	{
		(void) mp_next_LOD->ProcessNodeInPlace(p_baseAddress);
	}
	
	return leaves;
}



void CGeomNode::BecomesChildOf(CGeomNode *p_parent)
{
	if (m_flags & NODEFLAG_LEAF)
	{
		mp_sibling = NULL;
		m_flags &= ~NODEFLAG_LEAF;
	}
	else
	{
		mp_sibling = p_parent->u1.mp_child;
	}
	p_parent->u1.mp_child = this;
}


bool CGeomNode::RemoveFrom(CGeomNode *p_parent)
{
	if ((m_flags & NODEFLAG_LEAF) || !p_parent->u1.mp_child)
	{
		return false;
	}
		
	if (p_parent->u1.mp_child == this)
	{
		p_parent->u1.mp_child = mp_sibling;
		return true;
	}
	else
	{
		for (CGeomNode *p_node=p_parent->u1.mp_child; p_node->mp_sibling; p_node=p_node->mp_sibling)
		{
			if (p_node->mp_sibling == this)
			{
				p_node->mp_sibling = mp_sibling;
				return true;
			}
		}
	}

	return false;
}


bool CGeomNode::RemoveFromChildrenOf(CGeomNode *p_parent)
{
	if ((m_flags & NODEFLAG_LEAF) || !p_parent->u1.mp_child)
	{
		return false;
	}

	for (CGeomNode *p_node=p_parent->u1.mp_child; p_node; p_node=p_node->mp_sibling)
	{
		if (RemoveFrom(p_node))
		{
			return true;
		}
	}

	return false;
}


#endif // #ifdef __PLAT_NGPS__


/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/


#if OCCLUSION_CACHE
static bool set_valid_flag = false;
#endif


CGeomNode::CGeomNode()
{
	m_flags = ALL_VISIBILITY_BITS;

	
#if OCCLUSION_CACHE
	if( set_valid_flag )
	{
		set_valid_flag = false;
		m_flags |= NODEFLAG_LAST_OCCLUSION_VALID;
	}
	else
	{
		set_valid_flag = true;
	}
#endif
	m_bounding_sphere[0] = 0.0f;
	m_bounding_sphere[1] = 0.0f;
	m_bounding_sphere[2] = 0.0f;
	m_bounding_sphere[3] = 1.0e+30f;

	m_bounding_box[0] = 1.0e+30f;
	m_bounding_box[1] = 1.0e+30f;
	m_bounding_box[2] = 1.0e+30f;

	m_LOD_far_dist	= MAX_LOD_DIST;

	u2.mp_transform	= NULL;
	u1.mp_child		= NULL;
	mp_sibling		= NULL;
	mp_next_LOD		= NULL;
	//u1.mp_dma			= NULL;
	u3.mp_group		= NULL;
	mp_uv_wibble	= NULL;
	mp_vc_wibble	= NULL;

	m_num_bones		   = 0;
	m_bone_index	   = -1;
	u4.mp_bone_transforms = NULL;

	m_colour = 0x80808080;
}


void CGeomNode::Init()
{
	m_flags = ALL_VISIBILITY_BITS;

	m_bounding_sphere[0] = 0.0f;
	m_bounding_sphere[1] = 0.0f;
	m_bounding_sphere[2] = 0.0f;
	m_bounding_sphere[3] = 1.0e+30f;

	m_bounding_box[0] = 1.0e+30f;
	m_bounding_box[1] = 1.0e+30f;
	m_bounding_box[2] = 1.0e+30f;

	m_LOD_far_dist	= MAX_LOD_DIST;

	u2.mp_transform	= NULL;
	u1.mp_child		= NULL;
	mp_sibling		= NULL;
	mp_next_LOD		= NULL;
	//u1.mp_dma			= NULL;
	u3.mp_group		= NULL;
	mp_uv_wibble	= NULL;
	mp_vc_wibble	= NULL;

	m_num_bones		   = 0;
	m_bone_index	   = -1;
	u4.mp_bone_transforms = NULL;

	m_colour = 0x80808080;
}


CGeomNode::CGeomNode(const CGeomNode &node)
{
	#if 1
	memcpy(this,&node,sizeof(CGeomNode));
	#else
	m_flags				= node.m_flags;
	m_bounding_sphere	= node.m_bounding_sphere;
	u2.mp_transform		= node.u2.mp_transform;
	u1.mp_child			= node.u1.mp_child;
	mp_sibling			= node.mp_sibling;
	mp_next_LOD			= node.mp_next_LOD;
	m_LOD_far_dist		= node.m_LOD_far_dist;
	//u1.mp_dma				= node.u1.mp_dma;
	u3.mp_group			= node.u3.mp_group;
	mp_uv_wibble		= node.mp_uv_wibble;
	mp_vc_wibble		= node.mp_vc_wibble;
	m_num_bones			= node.m_num_bones;
	m_bone_index		= node.m_bone_index;
	u4.mp_bone_transforms	= node.u4.mp_bone_transforms;
	m_colour			= node.m_colour;
	#endif
}



#ifdef __PLAT_NGPS__



//#define	DUMP_NODE_STRUCTURE


#ifdef	DUMP_NODE_STRUCTURE
static int node_level = 0;
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeomNode::RenderWorld(CVu1Context& ctxt, uint32 renderFlags)
{
	GEOMSTATINC(geom_stats_total);

	
// Don't render anything if it's not going to be displayed	
	if (!FlipCopyEnabled())
	{
		return;
	}
	
	// render children
	CGeomNode *p_child;
	for (p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
	{
		p_child->RenderScene(ctxt, renderFlags);
	}
	
#ifdef	__NOPT_ASSERT__
	m_flags |= NODEFLAG_WAS_RENDERED;		// Set flag saying it was rendered
#endif

#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeomNode::RenderScene(CVu1Context& ctxt, uint32 renderFlags)
{
	CVu1Context *p_ctxt;

#ifdef	__NOPT_ASSERT__
	m_flags &= ~NODEFLAG_WAS_RENDERED;		// Clear flag saying it was rendered
#endif

	GEOMSTATINC(geom_stats_total);


	// cull the node if not active
	if (!((m_flags & NODEFLAG_ACTIVE) && (m_flags & render::ViewportMask)))
	{
		GEOMSTATINC(geom_stats_inactive);
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
		return;
	}

	// is it the root node of a sky?
	if (m_flags & NODEFLAG_SKY)
	{
		GEOMSTATINC(geom_stats_sky);
		// reserve space from dma list for new trans context
		p_ctxt = ctxt.Localise();

		p_ctxt->SetupAsSky();
		RenderAsSky(*p_ctxt, renderFlags);
		dma::SetList(NULL);			// not quite sure why we need this, but we do
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
		return;
	}

	// is it really an object node in disguise?
	if (m_flags & NODEFLAG_OBJECT)
	{
		RenderObject(ctxt, renderFlags);
		return;
	}

	// get this node's transform context
	if (m_flags & NODEFLAG_TRANSFORMED)
	{
		// copy the parent vu1 context
		p_ctxt = ctxt.LocaliseExtend();

		// set up the parts of the context that will depend on the new matrix
		p_ctxt->StandardSetup(*u2.mp_transform);   

		// Propogate the 'transformed' flag when recursing.
		renderFlags |= RENDERFLAG_TRANSFORMED;
	}
	else
	{
		p_ctxt = &ctxt;
	}

	// make a note of the root node for a skeletal model
	if (m_flags & NODEFLAG_SKELETAL)
	{
		GpInstanceNode = this;
		renderFlags |= RENDERFLAG_SKELETAL;
	}


	// applying a colour to a node
	if (m_flags & NODEFLAG_COLOURED)
	{
		GEOMSTATINC(geom_stats_colored);
		// if there's no local context, copy the parent's
		if (p_ctxt == &ctxt)
		{
			p_ctxt = ctxt.Localise();
		}
	
		// copy node's colour to vu1 context
		p_ctxt->SetColour(this);
		renderFlags |= RENDERFLAG_COLOURED;
	}

	// Check for light group
	if (u3.mp_light_group && u3.mp_light_group != ctxt.GetLights())
	{
		// if there's no local context, copy the global one
		if (p_ctxt == &ctxt || !p_ctxt->IsExtended())
		{
			p_ctxt = ctxt.LocaliseExtend();
		}

		p_ctxt->SetLights(u3.mp_light_group);
	}


// If we got here, then it's been rendered, so set the flag
#ifdef	__NOPT_ASSERT__
	m_flags |= NODEFLAG_WAS_RENDERED;		// Set flag saying it was rendered
#endif

	// render any children
	for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
	{
		p_child->RenderObject(*p_ctxt, renderFlags);
	}

#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeomNode::RenderObject(CVu1Context& ctxt, uint32 renderFlags)
{
	CVu1Context *p_ctxt;

#ifdef	__NOPT_ASSERT__
	m_flags &= ~NODEFLAG_WAS_RENDERED;		// Clear flag saying it was rendered
#endif

	GEOMSTATINC(geom_stats_total);




	#if	__ASSERT_ON_ZERO_RADIUS__
	Dbg_MsgAssert( m_bounding_sphere[W] != 0.0f, ( "Wasn't expecting a radius of 0 for geomnode %s or a sub-mesh of it. \n perhaps it has isolated verts that need removing?",Script::FindChecksumName(GetChecksum()) ) );
	#endif

	// cull the node if not active
	if (!((m_flags & NODEFLAG_ACTIVE) && (m_flags & render::ViewportMask)))
	{
		GEOMSTATINC(geom_stats_inactive);
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
	    return;
	}

#if OCCLUSION_CACHE
	bool last_occlusion_check_valid = ( m_flags & NODEFLAG_LAST_OCCLUSION_VALID );
	if( last_occlusion_check_valid )
	{
		// Clear the valid flag.
		m_flags &= ~NODEFLAG_LAST_OCCLUSION_VALID;

		// Was this object occluded last frame? If so, occlude this frame also.
		if( m_flags & NODEFLAG_LAST_OCCLUSION_TRUE )
		{
			m_flags &= ~NODEFLAG_LAST_OCCLUSION_TRUE;
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
		return;
		}
	}
#endif

	// get this node's transform context
	if (m_flags & NODEFLAG_TRANSFORMED)
	{
		// copy the parent vu1 context
		p_ctxt = ctxt.LocaliseExtend();

		// set up the parts of the context that will depend on the new matrix
		p_ctxt->StandardSetup(*u2.mp_transform);   

		// Propogate the 'transformed' flag when recursing.
		renderFlags |= RENDERFLAG_TRANSFORMED;
	}
	else
	{
		p_ctxt = &ctxt;
	}

	// check for skeletal mesh
	if ((renderFlags & RENDERFLAG_SKELETAL) && (m_bone_index >= 0))
	{
		Dbg_MsgAssert(GpInstanceNode, ("GpInstanceNode is NULL"));

		GEOMSTATINC(geom_stats_skeletal);
		if (p_ctxt == &ctxt)
		{
			p_ctxt = ctxt.Localise();
		}
	
		p_ctxt->HierarchicalSetup(GpInstanceNode->u4.mp_bone_transforms[m_bone_index]);
	}

	Mth::Vector camera_sphere;
	GEOMSTATINC(geom_stats_camera_sphere);
	camera_sphere = m_bounding_sphere;
	camera_sphere[W] = 1.0f;
	camera_sphere *= *p_ctxt->GetMatrix();
	camera_sphere[W] = m_bounding_sphere[W];


	// view-volume bounding sphere tests, based on parent render flags
	// MickNote:  I experimented with only clipping the leaf nodes, but this was slightly
	// slower, as the higher level culling must cull enough to justify the overhead)
	if ( renderFlags & (RENDERFLAG_CLIP | RENDERFLAG_CULL))
	{
		GEOMSTATINC(geom_stats_clipcull);
		if (SphereIsOutsideViewVolume(camera_sphere))
		{
			GEOMSTATINC(geom_stats_culled);
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
			return;
		}

		if (SphereIsInsideOuterVolume(camera_sphere))
		{
			renderFlags &= ~(RENDERFLAG_CLIP|RENDERFLAG_CULL);
		}
	}

	// view-volume bounding box test
	if ((renderFlags & (RENDERFLAG_CLIP | RENDERFLAG_CULL)) &&
		!((renderFlags & RENDERFLAG_TRANSFORMED) || m_flags & NODEFLAG_TRANSFORMED))
	{
		GEOMSTATINC(geom_stats_boxcheck);
		if (BoxIsOutsideViewVolume(*(Mth::Vector *)&m_bounding_box, m_bounding_sphere))
		{
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
			return;
		}

	}

	// Occlusion test.
	if (m_bounding_sphere[W] > 1.0e20f)
	{
		// Don't bother if sphere is stupidly large.
	}
#if OCCLUSION_CACHE
	else if( !last_occlusion_check_valid )
#else
	else
#endif	
	{
		
#if OCCLUSION_CACHE
		m_flags |= NODEFLAG_LAST_OCCLUSION_VALID;
#endif
		if (m_flags & NODEFLAG_TRANSFORMED)					// root of a transformed branch
		{
			GEOMSTATINC(geom_stats_occludecheck);
			Mth::Vector world_sphere(m_bounding_sphere);
			world_sphere[W] = 1.0f;
			world_sphere *= *u2.mp_transform;							// => use u2.mp_transform
			if (TestSphereAgainstOccluders( &world_sphere, m_bounding_sphere[W] ))
			{
				GEOMSTATINC(geom_stats_occluded);
#if OCCLUSION_CACHE
				m_flags |= NODEFLAG_LAST_OCCLUSION_TRUE;
#endif
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
				return;
			}
		}
		else if (renderFlags & RENDERFLAG_TRANSFORMED)				// deeper within a transformed branch
		{
		}
		else														// not transformed => use world position as-is
		{
			GEOMSTATINC(geom_stats_occludecheck);
			if (TestSphereAgainstOccluders( &m_bounding_sphere, m_bounding_sphere[W] ))
			{
				GEOMSTATINC(geom_stats_occluded);
#if OCCLUSION_CACHE
				m_flags |= NODEFLAG_LAST_OCCLUSION_TRUE;
#endif				
				#ifdef	DUMP_NODE_STRUCTURE
					node_level--;
				#endif
				return;
			}
		}
	}


	#if !STENCIL_SHADOW
	// shadow test
	if (renderFlags & RENDERFLAG_SHADOW)
	{
		if (m_flags & (NODEFLAG_TRANSFORMED))
		{
			renderFlags &= ~RENDERFLAG_SHADOW;
		}
		else if (( m_bounding_sphere[X] + m_bounding_box[X] ) < ( render::ShadowCameraPosition[X] - SHADOW_CAM_FRUSTUM_HALF_WIDTH  )	||
				 ( m_bounding_sphere[X] - m_bounding_box[X] ) > ( render::ShadowCameraPosition[X] + SHADOW_CAM_FRUSTUM_HALF_WIDTH  )	||
				 ( m_bounding_sphere[Z] + m_bounding_box[Z] ) < ( render::ShadowCameraPosition[Z] - SHADOW_CAM_FRUSTUM_HALF_HEIGHT )	||
				 ( m_bounding_sphere[Z] - m_bounding_box[Z] ) > ( render::ShadowCameraPosition[Z] + SHADOW_CAM_FRUSTUM_HALF_HEIGHT ))
		{
			renderFlags &= ~RENDERFLAG_SHADOW;
		}
	}
	#endif

	// applying a colour to a node
	if (m_flags & NODEFLAG_COLOURED)
	{
		GEOMSTATINC(geom_stats_colored);
		// if there's no local context, copy the parent's
		if (p_ctxt == &ctxt)
		{
			p_ctxt = ctxt.Localise();
		}

		// copy node's colour to vu1 context
		p_ctxt->SetColour(this);
		renderFlags |= RENDERFLAG_COLOURED;
	}

	// Check for light group
	if (u3.mp_light_group && u3.mp_light_group != ctxt.GetLights())
	{
		// if there's no local context, copy the global one
		if (p_ctxt == &ctxt || !p_ctxt->IsExtended())
		{
			p_ctxt = ctxt.LocaliseExtend();
		}

		p_ctxt->SetLights(u3.mp_light_group);
	}


	// Check if we should use another LOD (Garrett: Logically, this should be right, but I still want to streamline it)
	float camera_dist = -camera_sphere[Z];
	CGeomNode *p_child = NULL;
	CGeomNode *p_lod;
	for (p_lod=this; p_lod; p_lod = p_lod->mp_next_LOD)
	{
		if (p_lod->m_LOD_far_dist > camera_dist)
		{
			p_child = p_lod->u1.mp_child;
			break;
		}
	}

// If we got here, then it's been rendered, so set the flag
#ifdef	__NOPT_ASSERT__
	m_flags |= NODEFLAG_WAS_RENDERED;		// Set flag saying it was rendered
#endif

	// render any children
	if (renderFlags & RENDERFLAG_TRANSFORMED)
	{
		for (; p_child; p_child=p_child->mp_sibling)
		{
			p_child->RenderTransformedLeaf(*p_ctxt, renderFlags);
		}
	}
	else
	{
		for (; p_child; p_child=p_child->mp_sibling)
		{
			p_child->RenderNonTransformedLeaf(*p_ctxt, renderFlags);
		}
	}

#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeomNode::RenderTransformedLeaf(CVu1Context& ctxt, uint32 renderFlags)
{
	CVu1Context *p_ctxt;

	#if	__ASSERT_ON_ZERO_RADIUS__
	Dbg_MsgAssert( m_bounding_sphere[W] != 0.0f, ( "Wasn't expecting a radius of 0 for geomnode %s or a sub-mesh of it. \n perhaps it has isolated verts that need removing?",Script::FindChecksumName(GetChecksum()) ) );
	#endif

#ifdef	__NOPT_ASSERT__
	m_flags &= ~NODEFLAG_WAS_RENDERED;		// Clear flag saying it was rendered
#endif

	GEOMSTATINC(geom_stats_total);


#if OCCLUSION_CACHE
	bool last_occlusion_check_valid = ( m_flags & NODEFLAG_LAST_OCCLUSION_VALID );
	if( last_occlusion_check_valid )
	{
		// Clear the valid flag.
		m_flags &= ~NODEFLAG_LAST_OCCLUSION_VALID;

		// Was this object occluded last frame? If so, occlude this frame also.
		if( m_flags & NODEFLAG_LAST_OCCLUSION_TRUE )
		{
			m_flags &= ~NODEFLAG_LAST_OCCLUSION_TRUE;
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
		return;
		}
	}
#endif

	Mth::Vector camera_sphere;
	GEOMSTATINC(geom_stats_camera_sphere);
	camera_sphere = m_bounding_sphere;
	camera_sphere[W] = 1.0f;
	camera_sphere *= *ctxt.GetMatrix();
	camera_sphere[W] = m_bounding_sphere[W];


	// view-volume bounding sphere tests, based on parent render flags
	// MickNote:  I experimented with only clipping the leaf nodes, but this was slightly
	// slower, as the higher level culling must cull enough to justify the overhead)
	if ( renderFlags & (RENDERFLAG_CLIP | RENDERFLAG_CULL))
	{
		GEOMSTATINC(geom_stats_clipcull);
		if (SphereIsOutsideViewVolume(camera_sphere))
		{
			GEOMSTATINC(geom_stats_culled);
			GEOMSTATINC(geom_stats_leaf_culled);
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
			return;
		}

		if (SphereIsInsideOuterVolume(camera_sphere))
		{
			renderFlags &= ~(RENDERFLAG_CLIP|RENDERFLAG_CULL);
		}
	}


	// Occlusion test.
#if OCCLUSION_CACHE
	if( !last_occlusion_check_valid )
#endif	
	{
#if OCCLUSION_CACHE
		m_flags |= NODEFLAG_LAST_OCCLUSION_VALID;
#endif
	}


	#if !STENCIL_SHADOW
	// shadow test
	if (renderFlags & RENDERFLAG_SHADOW)
	{
		if (m_flags & (NODEFLAG_BILLBOARD | NODEFLAG_ENVMAPPED))
		{
			renderFlags &= ~RENDERFLAG_SHADOW;
		}
		else if (( m_bounding_sphere[X] + m_bounding_box[X] ) < ( render::ShadowCameraPosition[X] - SHADOW_CAM_FRUSTUM_HALF_WIDTH  )	||
				 ( m_bounding_sphere[X] - m_bounding_box[X] ) > ( render::ShadowCameraPosition[X] + SHADOW_CAM_FRUSTUM_HALF_WIDTH  )	||
				 ( m_bounding_sphere[Z] + m_bounding_box[Z] ) < ( render::ShadowCameraPosition[Z] - SHADOW_CAM_FRUSTUM_HALF_HEIGHT )	||
				 ( m_bounding_sphere[Z] - m_bounding_box[Z] ) > ( render::ShadowCameraPosition[Z] + SHADOW_CAM_FRUSTUM_HALF_HEIGHT ))
		{
			renderFlags &= ~RENDERFLAG_SHADOW;
		}
	}
	#endif

	// get this node's transform context
	p_ctxt = &ctxt;

	// applying a colour to a node
	if (m_flags & NODEFLAG_COLOURED)
	{
		GEOMSTATINC(geom_stats_colored);
		p_ctxt = ctxt.Localise();

		// copy node's colour to vu1 context
		p_ctxt->SetColour(this);
		renderFlags |= RENDERFLAG_COLOURED;
	}

	GEOMSTATINC(geom_stats_leaf);
	#ifdef	__NOPT_ASSERT__
	// Mick: check patch variables for debugging toggle of multi-passes
	if ((m_flags & gPassMask1) != gPassMask0)
	{	
#ifdef	DUMP_NODE_STRUCTURE
node_level--;
#endif
		return;
	}
	#endif

	#ifdef	__LOD_MULTIPASS__
	// if 2nd+ pass, check to see if distance from camera is greater than allowed for multipass
	if ((m_flags & NODEFLAG_ZPUSH) && (-camera_sphere[Z] > render::sMultipassMaxDist))
	{
#ifdef	DUMP_NODE_STRUCTURE
node_level--;
#endif
		return;
	}
	#endif

	if (m_flags & (NODEFLAG_UVWIBBLE | NODEFLAG_ENVMAPPED | NODEFLAG_VCWIBBLE))
	{
		if (m_flags & (NODEFLAG_UVWIBBLE | NODEFLAG_ENVMAPPED))
		{
			// if there's no local context, copy the global one
			if (p_ctxt == &ctxt)
			{
				p_ctxt = ctxt.LocaliseExtend();
			}

			// uv wibble
			if (m_flags & NODEFLAG_UVWIBBLE)
			{
				GEOMSTATINC(geom_stats_wibbleUV);
				p_ctxt->WibbleUVs(mp_uv_wibble, m_flags&NODEFLAG_EXPLICIT_UVWIBBLE);
			}

			// environment mapping
			if (m_flags & NODEFLAG_ENVMAPPED)
			{
				// reflection map vectors
				p_ctxt->SetReflectionVecs((sint16)(uint)mp_uv_wibble, (sint16)(((uint)mp_uv_wibble)>>16));
			}
		}

		// vc wibble
		if (m_flags & NODEFLAG_VCWIBBLE)
		{
			GEOMSTATINC(geom_stats_wibbleVC);
			WibbleVCs();
		}
	}

	// set the dma context
	dma::SetList(u3.mp_group);
	u3.mp_group->Used[render::Field] = true;

	// flag the texture as used
	if (u4.mp_texture)
	{
		u4.mp_texture->m_render_count++;
	}


	Mth::Vector *p_trans = p_ctxt->GetTranslation();

	// initial cnt tag, for sending row reg and z-sort key
	dma::Tag(dma::cnt, 1, *(uint32 *)&camera_sphere[Z]);
	vif::NOP();
	vif::STROW((int)(*p_trans)[0] + (int)(m_bounding_sphere[0]*SUB_INCH_PRECISION),
			   (int)(*p_trans)[1] + (int)(m_bounding_sphere[1]*SUB_INCH_PRECISION),
			   (int)(*p_trans)[2] + (int)(m_bounding_sphere[2]*SUB_INCH_PRECISION), 0);


	// special case for billboards
	if (m_flags & NODEFLAG_BILLBOARD)
	{
		// compute billboard basis vecs
		Mth::Vector udir,vdir;
		vdir = (*p_ctxt->GetMatrix())[1];
		vdir[2] =  0.0f;
		vdir[3] =  0.0f;
		vdir.Normalize();
		udir[0] =  vdir[1];
		udir[1] = -vdir[0];
		udir[2] =  0.0f;
		udir[3] =  0.0f;

		udir *= render::CameraToFrustum;
		vdir *= render::CameraToFrustum;
		udir[2] =  0.0f;
		udir[3] =  0.0f;
		vdir[2] =  0.0f;
		vdir[3] =  0.0f;

		// send vu1 context
		dma::Tag(dma::next, 16, (uint)dma::pLoc+272);
		vif::BASE(vu1::Loc);
		vif::OFFSET(0);
		uint vu1_loc = vu1::Loc;
		vu1::Loc = 0;   							// must do this as a relative prim for a sortable list...
		vu1::BeginPrim(REL, VU1_ADDR(L_VF20));
		vu1::StoreVec(*(Vec *)&udir);
		vu1::StoreVec(*(Vec *)&vdir);
		vu1::StoreVec(*(Vec *)&render::IntViewportScale);
		vif::StoreV4_32(0,0,0,0);
		vif::StoreV4_32(0,0,0,0);
		vu1::StoreVec(*(Vec *)&render::CameraToWorld[3]);
		vu1::StoreVec(*(Vec *)ctxt.GetColour());
		vu1::StoreVec(*(Vec *)&render::IntViewportOffset);
		Mth::Matrix LocalToFrustum = (*p_ctxt->GetMatrix()) * render::CameraToFrustum;
		vu1::CopyMat((float*)&LocalToFrustum);
		vu1::EndPrim(0);

		// gs context data, specifically the fog coefficient
		float w,f;
		if (renderFlags & RENDERFLAG_TRANSFORMED)
		{
			w = -(*p_ctxt->GetMatrix())[3][2];
		}
		else
		{
			// Context only contains WorldToCamera matrix, so convert world position to camera relative position
			Mth::Vector camera_rel_pos = render::WorldToCamera.TransformAsPos(m_bounding_sphere);
			w = -camera_rel_pos[2];
		}
		if ((w > render::FogNear) && render::EnableFog)	// Garrett: We have to check for EnableFog here because the VU1 code isn't
		{
			f = 1.0 + render::EffectiveFogAlpha * (render::FogNear/w - 1.0f);
		}
		else
		{
			f = 1.0f;
		}
		gs::BeginPrim(REL,0,0);
		gs::Reg1(gs::FOG, PackFOG((int)(f*255.99f)));
		gs::EndPrim(0);

		dma::EndTag();
		((uint16 *)dma::pTag)[1] |= vu1::Loc & 0x3FF;	// must write some code for doing this automatically
		vu1::Loc += vu1_loc;

		u3.mp_group->pVu1Context = NULL;
	}

	// send transform context if necessary
	else if ((p_ctxt != u3.mp_group->pVu1Context) || (u3.mp_group->flags & GROUPFLAG_SORT))
	{
		GEOMSTATINC(geom_stats_sendcontext);
		int qwc = p_ctxt->IsExtended() ? EXT_CTXT_SIZE+2 : STD_CTXT_SIZE+2;
		dma::Tag(dma::ref, qwc|(qwc-1)<<16, p_ctxt->GetDma());
		vif::BASE(vu1::Loc);
		vif::OFFSET(0);
		vu1::Loc += qwc-1;
		u3.mp_group->pVu1Context = p_ctxt;
	}

	// render this node by adding to the chosen dma list
	uint vu1_flags = (renderFlags & RENDERFLAG_CLIP) ? CLIP :
					 (renderFlags & RENDERFLAG_CULL) ? CULL : PROJ;
	if (renderFlags & RENDERFLAG_COLOURED)
		vu1_flags |= COLR;
	if (render::EnableFog && (camera_sphere[3]-camera_sphere[2] > render::FogNear))
		vu1_flags |= FOGE;
	dma::pTag = dma::pLoc;		// must set this manually if we bypass the dma tag functions
	dma::Store32(u2.m_dma_tag_lo32,(uint32)u1.mp_dma);
	vif::BASE(vu1::Loc);
	vif::ITOP(vu1_flags);
	vu1::Loc += u2.m_dma_tag_lo32>>16;

	if (m_flags & NODEFLAG_BLACKFOG)
	{
		dma::Gosub(SET_FOGCOL,2);
		dma::pLoc -= 8;
		vif::FLUSHA();
		vif::NOP();
	}

	// must add a 'next' tag for sorted groups
	if (u3.mp_group->flags & GROUPFLAG_SORT)
	{
		GEOMSTATINC(geom_stats_sorted);
		dma::SetList(NULL);
	}

	#if !STENCIL_SHADOW
	// render shadow
	if ((renderFlags & RENDERFLAG_SHADOW) && !(m_flags & NODEFLAG_NOSHADOW))
	{
		GEOMSTATINC(geom_stats_shadow);
		// set the dma context
		dma::SetList(sGroup::pShadow);

		// set row reg
		dma::Tag(dma::cnt, 1, *(uint32 *)&camera_sphere[Z]);
		vif::NOP();
		vif::STROW((int)(*p_trans)[0] + (int)(m_bounding_sphere[0]*SUB_INCH_PRECISION),
				   (int)(*p_trans)[1] + (int)(m_bounding_sphere[1]*SUB_INCH_PRECISION),
				   (int)(*p_trans)[2] + (int)(m_bounding_sphere[2]*SUB_INCH_PRECISION), 0);

		// send transform context
		dma::Tag(dma::ref, 18|17<<16, sGroup::pShadow->pVu1Context->GetDma());
		vif::BASE(vu1::Loc);
		vif::OFFSET(0);
		vu1::Loc += 17;

		// render this node
		vu1_flags = (renderFlags & RENDERFLAG_CLIP) ? CLIP|SHDW :
					(renderFlags & RENDERFLAG_CULL) ? CULL|SHDW : PROJ|SHDW;
		if (render::EnableFog && (camera_sphere[3]-camera_sphere[2] > render::FogNear))
			vu1_flags |= FOGE;
		dma::pTag = dma::pLoc;		// must set this manually if we bypass the dma tag functions
		dma::Store32(u2.m_dma_tag_lo32,(uint32)u1.mp_dma);
		vif::BASE(vu1::Loc);
		vif::ITOP(vu1_flags);
		vu1::Loc += u2.m_dma_tag_lo32>>16;

		if (m_flags & NODEFLAG_BLACKFOG)
		{
			dma::Gosub(SET_FOGCOL,2);
			dma::pLoc -= 8;
			vif::FLUSHA();
			vif::NOP();
		}
	}
	#endif

#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeomNode::RenderNonTransformedLeaf(CVu1Context& ctxt, uint32 renderFlags)
{
	float camera_sphere_z;

	#if	__ASSERT_ON_ZERO_RADIUS__
	Dbg_MsgAssert( m_bounding_sphere[W] != 0.0f, ( "Wasn't expecting a radius of 0 for geomnode %s or a sub-mesh of it. \n perhaps it has isolated verts that need removing?",Script::FindChecksumName(GetChecksum()) ) );
	#endif	

	// kick off the frustum culling tests in VU0
	asm __volatile__("

		lqc2		vf08,(%0)					# sphere (x,y,z,R)
		lqc2		vf09,(%1)					# box (bx,by,bz,?)
		vcallms		BothCullTests				# call microsubroutine

	": : "r" (&m_bounding_sphere), "r" (&m_bounding_box) );


#ifdef	__NOPT_ASSERT__
	m_flags &= ~NODEFLAG_WAS_RENDERED;		// Clear flag saying it was rendered
#endif

	GEOMSTATINC(geom_stats_total);


//Mick:  Minimal leaf rendering makes no real difference 
// so I've removed this call while refactoring	

	// check for minimal leaf node for a quicker render
	if (!(m_flags & (NODEFLAG_COLOURED		|
					 NODEFLAG_BILLBOARD		|
					 NODEFLAG_UVWIBBLE		|
					 NODEFLAG_VCWIBBLE		|
					 NODEFLAG_ENVMAPPED))

		&& !(renderFlags & (RENDERFLAG_COLOURED		|
							#if !STENCIL_SHADOW
							RENDERFLAG_SHADOW		|
							#endif
							RENDERFLAG_CLIP			|
							RENDERFLAG_CULL)))
	{
		RenderMinimalLeaf(ctxt);
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
		return;
	}


#if OCCLUSION_CACHE
	bool last_occlusion_check_valid = ( m_flags & NODEFLAG_LAST_OCCLUSION_VALID );
	if( last_occlusion_check_valid )
	{
		// Clear the valid flag.
		m_flags &= ~NODEFLAG_LAST_OCCLUSION_VALID;

		// Was this object occluded last frame? If so, occlude this frame also.
		if( m_flags & NODEFLAG_LAST_OCCLUSION_TRUE )
		{
			m_flags &= ~NODEFLAG_LAST_OCCLUSION_TRUE;
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
		return;
		}
	}
#endif


	// view-volume bounding sphere tests, based on parent render flags
	// MickNote:  I experimented with only clipping the leaf nodes, but this was slightly
	// slower, as the higher level culling must cull enough to justify the overhead)
	if ( renderFlags & (RENDERFLAG_CLIP | RENDERFLAG_CULL))
	{
		GEOMSTATINC(geom_stats_clipcull);

		if (CullAgainstViewFrustum())
		{
			GEOMSTATINC(geom_stats_culled);
			GEOMSTATINC(geom_stats_leaf_culled);
#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
			return;
		}

		if (!CullAgainstOuterFrustum())
		{
			renderFlags &= ~(RENDERFLAG_CLIP|RENDERFLAG_CULL);
		}
	}


	// Occlusion test.
#if OCCLUSION_CACHE
	if( !last_occlusion_check_valid )
#endif	
	{
		
#if OCCLUSION_CACHE
		m_flags |= NODEFLAG_LAST_OCCLUSION_VALID;
#endif
		GEOMSTATINC(geom_stats_occludecheck);
		if (TestSphereAgainstOccluders( &m_bounding_sphere, m_bounding_sphere[W] ))
		{
			GEOMSTATINC(geom_stats_occluded);
#if OCCLUSION_CACHE
			m_flags |= NODEFLAG_LAST_OCCLUSION_TRUE;
#endif				
			#ifdef	DUMP_NODE_STRUCTURE
				node_level--;
			#endif
			return;
		}
	}


	#if !STENCIL_SHADOW
	// shadow test
	if (renderFlags & RENDERFLAG_SHADOW)
	{
		if (m_flags & (NODEFLAG_BILLBOARD | NODEFLAG_ENVMAPPED))
		{
			renderFlags &= ~RENDERFLAG_SHADOW;
		}
		else if (( m_bounding_sphere[X] + m_bounding_box[X] ) < ( render::ShadowCameraPosition[X] - SHADOW_CAM_FRUSTUM_HALF_WIDTH  )	||
				 ( m_bounding_sphere[X] - m_bounding_box[X] ) > ( render::ShadowCameraPosition[X] + SHADOW_CAM_FRUSTUM_HALF_WIDTH  )	||
				 ( m_bounding_sphere[Z] + m_bounding_box[Z] ) < ( render::ShadowCameraPosition[Z] - SHADOW_CAM_FRUSTUM_HALF_HEIGHT )	||
				 ( m_bounding_sphere[Z] - m_bounding_box[Z] ) > ( render::ShadowCameraPosition[Z] + SHADOW_CAM_FRUSTUM_HALF_HEIGHT ))
		{
			renderFlags &= ~RENDERFLAG_SHADOW;
		}
	}
	#endif

	// get this node's transform context
	CVu1Context *p_ctxt = &ctxt;

	// applying a colour to a node
	if (m_flags & NODEFLAG_COLOURED)
	{
		GEOMSTATINC(geom_stats_colored);
		p_ctxt = ctxt.Localise();

		// copy node's colour to vu1 context
		p_ctxt->SetColour(this);
		renderFlags |= RENDERFLAG_COLOURED;
	}

	GEOMSTATINC(geom_stats_leaf);
	#ifdef	__NOPT_ASSERT__
	// Mick: check patch variables for debugging toggle of multi-passes
	if ((m_flags & gPassMask1) != gPassMask0)
	{	
#ifdef	DUMP_NODE_STRUCTURE
node_level--;
#endif
		return;
	}
	#endif


	asm __volatile__("
		qmfc2		$8,$vf05
		sw			$8,(%0)
	": : "r" (&camera_sphere_z) : "$8" );

	#ifdef	__LOD_MULTIPASS__
	// if 2nd+ pass, check to see if distance from camera is greater than allowed for multipass
	if ((m_flags & NODEFLAG_ZPUSH) && (-camera_sphere_z > render::sMultipassMaxDist))
	{
#ifdef	DUMP_NODE_STRUCTURE
node_level--;
#endif
		return;
	}
	#endif

	if (m_flags & (NODEFLAG_UVWIBBLE | NODEFLAG_ENVMAPPED | NODEFLAG_VCWIBBLE))
	{
		if (m_flags & (NODEFLAG_UVWIBBLE | NODEFLAG_ENVMAPPED))
		{
			// if there's no local context, copy the global one
			if (p_ctxt == &ctxt)
			{
				p_ctxt = ctxt.LocaliseExtend();
			}

			// uv wibble
			if (m_flags & NODEFLAG_UVWIBBLE)
			{
				GEOMSTATINC(geom_stats_wibbleUV);
				p_ctxt->WibbleUVs(mp_uv_wibble, m_flags&NODEFLAG_EXPLICIT_UVWIBBLE);
			}

			// environment mapping
			if (m_flags & NODEFLAG_ENVMAPPED)
			{
				// reflection map vectors
				p_ctxt->SetReflectionVecs((sint16)(uint)mp_uv_wibble, (sint16)(((uint)mp_uv_wibble)>>16));
			}
		}

		// vc wibble
		if (m_flags & NODEFLAG_VCWIBBLE)
		{
			GEOMSTATINC(geom_stats_wibbleVC);
			WibbleVCs();
		}
	}

	// set the dma context
	dma::SetList(u3.mp_group);
	u3.mp_group->Used[render::Field] = true;

	// flag the texture as used
	if (u4.mp_texture)
	{
		u4.mp_texture->m_render_count++;
	}


	Mth::Vector *p_trans = p_ctxt->GetTranslation();

	// initial cnt tag, for sending row reg and z-sort key
	dma::Tag(dma::cnt, 1, *(uint32 *)&camera_sphere_z);
	vif::NOP();
	vif::STROW((int)(*p_trans)[0] + (int)(m_bounding_sphere[0]*SUB_INCH_PRECISION),
			   (int)(*p_trans)[1] + (int)(m_bounding_sphere[1]*SUB_INCH_PRECISION),
			   (int)(*p_trans)[2] + (int)(m_bounding_sphere[2]*SUB_INCH_PRECISION), 0);


	// special case for billboards
	if (m_flags & NODEFLAG_BILLBOARD)
	{
		// compute billboard basis vecs
		Mth::Vector udir,vdir;
		vdir = (*p_ctxt->GetMatrix())[1];
		vdir[2] =  0.0f;
		vdir[3] =  0.0f;
		vdir.Normalize();
		udir[0] =  vdir[1];
		udir[1] = -vdir[0];
		udir[2] =  0.0f;
		udir[3] =  0.0f;

		udir *= render::CameraToFrustum;
		vdir *= render::CameraToFrustum;
		udir[2] =  0.0f;
		udir[3] =  0.0f;
		vdir[2] =  0.0f;
		vdir[3] =  0.0f;

		// send vu1 context
		dma::Tag(dma::next, 16, (uint)dma::pLoc+272);
		vif::BASE(vu1::Loc);
		vif::OFFSET(0);
		uint vu1_loc = vu1::Loc;
		vu1::Loc = 0;   							// must do this as a relative prim for a sortable list...
		vu1::BeginPrim(REL, VU1_ADDR(L_VF20));
		vu1::StoreVec(*(Vec *)&udir);
		vu1::StoreVec(*(Vec *)&vdir);
		vu1::StoreVec(*(Vec *)&render::IntViewportScale);
		vif::StoreV4_32(0,0,0,0);
		vif::StoreV4_32(0,0,0,0);
		vu1::StoreVec(*(Vec *)&render::CameraToWorld[3]);
		vu1::StoreVec(*(Vec *)ctxt.GetColour());
		vu1::StoreVec(*(Vec *)&render::IntViewportOffset);
		Mth::Matrix LocalToFrustum = (*p_ctxt->GetMatrix()) * render::CameraToFrustum;
		vu1::CopyMat((float*)&LocalToFrustum);
		vu1::EndPrim(0);

		// gs context data, specifically the fog coefficient
		float w,f;
		if (renderFlags & RENDERFLAG_TRANSFORMED)
		{
			w = -(*p_ctxt->GetMatrix())[3][2];
		}
		else
		{
			// Context only contains WorldToCamera matrix, so convert world position to camera relative position
			Mth::Vector camera_rel_pos = render::WorldToCamera.TransformAsPos(m_bounding_sphere);
			w = -camera_rel_pos[2];
		}
		if ((w > render::FogNear) && render::EnableFog)	// Garrett: We have to check for EnableFog here because the VU1 code isn't
		{
			f = 1.0 + render::EffectiveFogAlpha * (render::FogNear/w - 1.0f);
		}
		else
		{
			f = 1.0f;
		}
		gs::BeginPrim(REL,0,0);
		gs::Reg1(gs::FOG, PackFOG((int)(f*255.99f)));
		gs::EndPrim(0);

		dma::EndTag();
		((uint16 *)dma::pTag)[1] |= vu1::Loc & 0x3FF;	// must write some code for doing this automatically
		vu1::Loc += vu1_loc;

		u3.mp_group->pVu1Context = NULL;
	}

	// send transform context if necessary
	else if ((p_ctxt != u3.mp_group->pVu1Context) || (u3.mp_group->flags & GROUPFLAG_SORT))
	{
		GEOMSTATINC(geom_stats_sendcontext);
		int qwc = p_ctxt->IsExtended() ? EXT_CTXT_SIZE+2 : STD_CTXT_SIZE+2;
		dma::Tag(dma::ref, qwc|(qwc-1)<<16, p_ctxt->GetDma());
		vif::BASE(vu1::Loc);
		vif::OFFSET(0);
		vu1::Loc += qwc-1;
		u3.mp_group->pVu1Context = p_ctxt;
	}

	// render this node by adding to the chosen dma list
	uint vu1_flags = (renderFlags & RENDERFLAG_CLIP) ? CLIP :
					 (renderFlags & RENDERFLAG_CULL) ? CULL : PROJ;
	if (renderFlags & RENDERFLAG_COLOURED)
		vu1_flags |= COLR;
	if (render::EnableFog && (m_bounding_sphere[W]-camera_sphere_z > render::FogNear))
		vu1_flags |= FOGE;
	dma::pTag = dma::pLoc;		// must set this manually if we bypass the dma tag functions
	dma::Store32(u2.m_dma_tag_lo32,(uint32)u1.mp_dma);
	vif::BASE(vu1::Loc);
	vif::ITOP(vu1_flags);
	vu1::Loc += u2.m_dma_tag_lo32>>16;

	if (m_flags & NODEFLAG_BLACKFOG)
	{
		dma::Gosub(SET_FOGCOL,2);
		dma::pLoc -= 8;
		vif::FLUSHA();
		vif::NOP();
	}

	// must add a 'next' tag for sorted groups
	if (u3.mp_group->flags & GROUPFLAG_SORT)
	{
		GEOMSTATINC(geom_stats_sorted);
		dma::SetList(NULL);
	}

	#if !STENCIL_SHADOW
	// render shadow
	if ((renderFlags & RENDERFLAG_SHADOW) && !(m_flags & NODEFLAG_NOSHADOW))
	{
		GEOMSTATINC(geom_stats_shadow);
		// set the dma context
		dma::SetList(sGroup::pShadow);

		// set row reg
		dma::Tag(dma::cnt, 1, *(uint32 *)&camera_sphere_z);
		vif::NOP();
		vif::STROW((int)(*p_trans)[0] + (int)(m_bounding_sphere[0]*SUB_INCH_PRECISION),
				   (int)(*p_trans)[1] + (int)(m_bounding_sphere[1]*SUB_INCH_PRECISION),
				   (int)(*p_trans)[2] + (int)(m_bounding_sphere[2]*SUB_INCH_PRECISION), 0);

		// send transform context
		dma::Tag(dma::ref, 18|17<<16, sGroup::pShadow->pVu1Context->GetDma());
		vif::BASE(vu1::Loc);
		vif::OFFSET(0);
		vu1::Loc += 17;

		// render this node
		vu1_flags = (renderFlags & RENDERFLAG_CLIP) ? CLIP|SHDW :
					(renderFlags & RENDERFLAG_CULL) ? CULL|SHDW : PROJ|SHDW;
		if (render::EnableFog && (m_bounding_sphere[W]-camera_sphere_z > render::FogNear))
			vu1_flags |= FOGE;
		dma::pTag = dma::pLoc;		// must set this manually if we bypass the dma tag functions
		dma::Store32(u2.m_dma_tag_lo32,(uint32)u1.mp_dma);
		vif::BASE(vu1::Loc);
		vif::ITOP(vu1_flags);
		vu1::Loc += u2.m_dma_tag_lo32>>16;

		if (m_flags & NODEFLAG_BLACKFOG)
		{
			dma::Gosub(SET_FOGCOL,2);
			dma::pLoc -= 8;
			vif::FLUSHA();
			vif::NOP();
		}
	}
	#endif

#ifdef	DUMP_NODE_STRUCTURE
	node_level--;
#endif
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Mick:  I've moved this function here to improved I-Cache usage while rendering

bool TestSphereAgainstOccluders( Mth::Vector *p_center, float radius, uint32 meshes )
{
//	return false;
	// GJ:  This will catch bugs where a model's bounding sphere
	// is uninitialized, return inconsistent values depending on
	// the level you were in (this came up with the create-a-trick
	// skater, which uses "ref models", which don't have valid bounding
	// spheres)
	#if	__ASSERT_ON_ZERO_RADIUS__
	Dbg_MsgAssert( radius != 0.0f, ( "Wasn't expecting a radius of 0" ) );
	#endif

#if defined(OCCLUDER_USES_VU0_MACROMODE) || defined(OCCLUDER_USES_VU0_MICROMODE)

	if (sOccluder::sUseVU0)
	{
#ifdef	OCCLUDER_USES_VU0_MACROMODE
		bool result;

		asm __volatile__(
		"
		.set noreorder
		mfc1      $8, %3			 # radius
		qmtc2     $8, vf9

		vilwr.x   vi01, (vi00)		 # load number of occluders

		lqc2      vf10, 0x0(%1)		 # p_center
		ctc2      %2, $vi15			 # meshes

		vaddw.x   vf31, vf00, vf00w  # Load 1.0f to VF31x

		# Init loop
		cfc2      $10,  $vi01		 # move number of occluders to $10
		beq       $10,  $0, no_occlusions
		viaddi    vi02, vi00, 1		 # load address of first occluder

occ_loop:
		viaddi    vi03, vi02, 0		 # and put here, too
		vlqi.xyzw vf01, (vi03++)	 # load planes
		li        $9, 5				 # number of planes

plane_loop:
		# dot product
		vmul.xyz  vf11,vf01,vf10     # result = plane * center + plane_w [start]
		vmulax.x  ACC, vf01,vf10x
		vmaddaw.x ACC, vf31,vf01w	 # plane_w
		vmaddax.x ACC, vf31,vf09x    # also add radius in
		vmadday.x ACC, vf31,vf11y
		vmaddz.x  vf11,vf31,vf11z    # result = plane * center + plane_w + radius [ready]

		addi      $9, $9, -1         # decrement plane counter
		viaddi    vi04, vi02, 5		 # calc score address (since we are waiting anyway)
		vilwr.x   vi14, (vi04)		 # load score
		vlqi.xyzw vf01, (vi03++)	 # load next planes
		vnop

		cfc2      $8, $vi17			 # transfer if result (MAC register)
		andi	  $8, $8, 0x80		 # check result (v >= 0) not_occluded
		beq		  $8, $0, done_occluder
		nop

		bne       $9, $0, plane_loop
		nop

		# got through all planes - found occluder
found_occluder:
		li		  %0, 1				 # store true for return value
		viadd     vi14, vi14, vi15   # add meshes
		b         done
		viswr.x   vi14, (vi04)

done_occluder:
		addi      $10, $10, -1       # decrement occluder counter
		bne       $10, $0, occ_loop
		viaddi    vi02, vi02, 6		 # increment occluder address

no_occlusions:
		li		  %0, 0				 # store false for return value

done:
		.set reorder
		": "=r" (result) : "r" (p_center), "r" (meshes), "f" (radius) : "$8", "$9", "$10");

		return result;

#endif	// OCCLUDER_USES_VU0_MACROMODE
	
#ifdef OCCLUDER_USES_VU0_MICROMODE

		uint32 result, index;
		Mth::Vector sphere(*p_center);
		sphere[W] = radius;

		// call vu0 microsubroutine
		asm __volatile__("

			lqc2		vf07,(%0)					# load sphere (x,y,z,R) into vf07 of vu0
			vcallms		TestSphereAgainstOccluders	# call microsubroutine
			vnop									# interlocking instruction, waits for vu0 completion
			cfc2		$8,$vi01					# get boolean result from vu0
			cfc2		$9,$vi06					# get index of first successful occluder
			sw			$8,(%1)						# store boolean result
			sw			$9,(%2)						# store index
		
		": : "r" (&sphere), "r" (&result), "r" (&index) : "$8","$9");

		// keeping score
		if (result)
		{
			sOccluder::Occluders[index].score += meshes;
		}

		return (bool)result;

#endif	// OCCLUDER_USES_VU0_MICROMODE
	}
	else
#endif // defined(OCCLUDER_USES_VU0_MACROMODE) || defined(OCCLUDER_USES_VU0_MICROMODE)

	{
		float center_x = p_center->GetX();
		float center_y = p_center->GetY();
		float center_z = p_center->GetZ();

		sOccluder * p_occluder;
		if (sOccluder::sUseScratchPad)
		{
			p_occluder = (sOccluder*)0x70000000;
		} else {
			p_occluder = &sOccluder::Occluders[0];
		}

		// Test against each occluder.
		for( uint32 o = sOccluder::NumOccluders; o > 0 ; --o )
		{
			Mth::Vector * p_plane = &(p_occluder->planes[0]);
			// Test against each plane in the occluder.
			for( uint32 p = 5; p > 0; --p )
			{

				float result =	( p_plane->GetX() * center_x ) +
								( p_plane->GetY() * center_y ) +
								( p_plane->GetZ() * center_z ) +
								( p_plane->GetW() );

				if( result >= -radius )
				{
					// Outside of this plane, therefore not occluded by this occluder.
					goto	NOT_OCCLUDED;
				}
				p_plane++;
			}

			// Inside all five planes, therefore occluded. Increase score for this occluder.
			p_occluder->score += meshes;
			return true;

	NOT_OCCLUDED:		
			p_occluder++;
		}

		return false;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CGeomNode::CullAgainstViewFrustum()
{

	uint32 result;

	#if 0

	// test version... perform test and get results
	asm __volatile__("

		lqc2		vf08,(%0)					# sphere (x,y,z,R)
		lqc2		vf09,(%1)					# box (bx,by,bz,?)
		vcallms		ViewCullTest				# call microsubroutine
		vnop									# interlocking instruction, waits for vu0 completion
		cfc2		$8,$vi01					# get result
		sw			$8,(%2)						# store result

	": : "r" (&m_bounding_sphere), "r" (&m_bounding_box), "r" (&result) : "$8");


	#else
	
	// faster version... pick up results from test which kicked off earlier
	asm __volatile__("

		vnop									# interlocking instruction, waits for vu0 completion
		cfc2		$8,$vi01					# get result
		sw			$8,(%0)						# store result

	": : "r" (&result) : "$8");

	#endif

	return (bool)result;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CGeomNode::CullAgainstOuterFrustum()
{

	uint32 result;

	#if 0
	
	// test version... perform test and get results
	asm __volatile__("

		lqc2		vf08,(%0)					# sphere (x,y,z,R)
		lqc2		vf09,(%1)					# box (bx,by,bz,?)
		vcallms		OuterCullTest				# call microsubroutine
		vnop									# interlocking instruction, waits for vu0 completion
		cfc2		$8,$vi01					# get result
		sw			$8,(%2)						# store result

	": : "r" (&m_bounding_sphere), "r" (&m_bounding_box), "r" (&result) : "$8");

	#else
	
	// faster version... pick up results from test which kicked off earlier
	asm __volatile__("

		cfc2		$8,$vi11					# get result
		sw			$8,(%0)						# store result

	": : "r" (&result) : "$8");

	#endif

	return (bool)result;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeomNode::RenderMinimalLeaf(CVu1Context& ctxt)
{
	float camera_sphere_z;

	#if	__ASSERT_ON_ZERO_RADIUS__
	Dbg_MsgAssert( m_bounding_sphere[W] != 0.0f, ( "Wasn't expecting a radius of 0 for geomnode %s or a sub-mesh of it. \n perhaps it has isolated verts that need removing?",Script::FindChecksumName(GetChecksum()) ) );
	#endif
	
	GEOMSTATINC(geom_stats_total);

	GEOMSTATINC(geom_stats_occludecheck);
	if (TestSphereAgainstOccluders( &m_bounding_sphere, m_bounding_sphere[W] ))
	{
		GEOMSTATINC(geom_stats_occluded);
		return;
	}

	GEOMSTATINC(geom_stats_leaf);
	#ifdef	__NOPT_ASSERT__
	// Mick: check patch variables for debugging toggle of multi-passes
	if ((m_flags & gPassMask1) != gPassMask0)
	{	
		return;
	}
	#endif


	// set the dma context
	dma::SetList(u3.mp_group);
	u3.mp_group->Used[render::Field] = true;

	// flag the texture as used
	if (u4.mp_texture)
	{
		u4.mp_texture->m_render_count++;
	}


	Mth::Vector *p_trans = ctxt.GetTranslation();
	

	asm __volatile__("
		qmfc2		$8,$vf05
		sw			$8,(%0)
	": : "r" (&camera_sphere_z) : "$8" );


	#ifdef	__LOD_MULTIPASS__
	// if 2nd+ pass, check to see if distance from camera is greater than allowed for multipass
//		if ((m_flags & NODEFLAG_ZPUSH) && (-camera_sphere[Z] > render::sMultipassMaxDist))
	if ((m_flags & NODEFLAG_ZPUSH) && (-camera_sphere_z > render::sMultipassMaxDist))
		return;
	#endif

	// initial cnt tag, for sending row reg and z-sort key
	dma::Tag(dma::cnt, 1, *(uint32 *)&camera_sphere_z);
	vif::NOP();
	vif::STROW((int)(*p_trans)[0] + (int)(m_bounding_sphere[0]*SUB_INCH_PRECISION),
			   (int)(*p_trans)[1] + (int)(m_bounding_sphere[1]*SUB_INCH_PRECISION),
			   (int)(*p_trans)[2] + (int)(m_bounding_sphere[2]*SUB_INCH_PRECISION), 0);

	// send transform context if necessary
	if ((&ctxt != u3.mp_group->pVu1Context) || (u3.mp_group->flags & GROUPFLAG_SORT))
	{
		GEOMSTATINC(geom_stats_sendcontext);
		//Dbg_MsgAssert(!ctxt.IsExtended(), ("oh bugger"));
		int qwc = ctxt.IsExtended() ? EXT_CTXT_SIZE+2 : STD_CTXT_SIZE+2;
		dma::Tag(dma::ref, qwc|(qwc-1)<<16, ctxt.GetDma());
		vif::BASE(vu1::Loc);
		vif::OFFSET(0);
		vu1::Loc += qwc-1;
		u3.mp_group->pVu1Context = &ctxt;
	}

	// render this node by adding to the chosen dma list
	dma::pTag = dma::pLoc;		// must set this manually if we bypass the dma tag functions
	dma::Store32(u2.m_dma_tag_lo32,(uint32)u1.mp_dma);
	vif::BASE(vu1::Loc);
	if (render::EnableFog && (m_bounding_sphere[W]-camera_sphere_z > render::FogNear))
		vif::ITOP(PROJ|FOGE);
	else
		vif::ITOP(PROJ);
	vu1::Loc += u2.m_dma_tag_lo32>>16;

	if (m_flags & NODEFLAG_BLACKFOG)
	{
		dma::Gosub(SET_FOGCOL,2);
		dma::pLoc -= 8;
		vif::FLUSHA();
		vif::NOP();
	}

	// must add a 'next' tag for sorted groups
	if (u3.mp_group->flags & GROUPFLAG_SORT)
	{
		GEOMSTATINC(geom_stats_sorted);
		dma::SetList(NULL);
	}
	
	
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeomNode::RenderAsSky(CVu1Context& ctxt, uint32 renderFlags)
{
	CVu1Context *p_ctxt;

	// cull the node if not active
	if (!(m_flags & NODEFLAG_ACTIVE))
	{
		return;
	}

	// no support for transformed nodes yet...
	p_ctxt = &ctxt;

	Mth::Vector sphere;

	// bounding sphere tests, based on parent render flags
	if (renderFlags & (RENDERFLAG_CLIP | RENDERFLAG_CULL))
	{
		// get and transform centre of bounding sphere
		sphere = m_bounding_sphere;
		sphere[W] = 1.0f;
		sphere *= *p_ctxt->GetMatrix();
		sphere[W] = m_bounding_sphere[W];
					  
		if (SphereIsOutsideViewVolume(sphere))
		{
			return;
		}

		if (SphereIsInsideOuterVolume(sphere))
		{
			renderFlags &= ~(RENDERFLAG_CLIP|RENDERFLAG_CULL);
		}

		else if ((renderFlags & RENDERFLAG_CLIP) && !NeedsClipping(sphere))
		{
			renderFlags = renderFlags & ~RENDERFLAG_CLIP | RENDERFLAG_CULL;
		}
    }
	
	// applying a colour to a node (copied from Geomnode::Render)
	if (m_flags & NODEFLAG_COLOURED)
	{
		GEOMSTATINC(geom_stats_colored);
		// if there's no local context, copy the parent's
		if (p_ctxt == &ctxt)
		{
			p_ctxt = ctxt.Localise();
		}

		// copy node's colour to vu1 context
		p_ctxt->SetColour(this);
		renderFlags |= RENDERFLAG_COLOURED;
	}


	// is it a leaf node?
	if (m_flags & NODEFLAG_LEAF)
	{
		#ifdef	__NOPT_ASSERT__
		// Mick: check patch variables for debugging toggle of multi-passes
		if ((m_flags & gPassMask1) != gPassMask0)
		{	
			return;
		}
		#endif
		
		// uv wibble
		if (m_flags & NODEFLAG_UVWIBBLE)
		{
			// if there's no local context, copy the global one
			if (p_ctxt == &ctxt)
			{
				p_ctxt = ctxt.LocaliseExtend();
			}

			GEOMSTATINC(geom_stats_wibbleUV);
			p_ctxt->WibbleUVs(mp_uv_wibble, m_flags&NODEFLAG_EXPLICIT_UVWIBBLE);
		}

		// set the dma context
		dma::SetList(u3.mp_group);
		u3.mp_group->Used[render::Field] = true;

		// initial cnt tag, for sending row reg and z-sort key
		dma::Tag(dma::cnt, 1, 0);
		vif::NOP();
		vif::STROW((int)(m_bounding_sphere[0]*SUB_INCH_PRECISION),
				   (int)(m_bounding_sphere[1]*SUB_INCH_PRECISION),
				   (int)(m_bounding_sphere[2]*SUB_INCH_PRECISION), 0);

		// send transform context if necessary
		if (p_ctxt != u3.mp_group->pVu1Context)
		{
			int qwc = p_ctxt->IsExtended() ? EXT_CTXT_SIZE+2 : STD_CTXT_SIZE+2;
			dma::Tag(dma::ref, qwc|(qwc-1)<<16, p_ctxt->GetDma());
			vif::BASE(vu1::Loc);
			vif::OFFSET(0);
			vu1::Loc += qwc-1;
			u3.mp_group->pVu1Context = p_ctxt;
		}

		// render this node by adding to the chosen dma list
		uint vu1_flags = (renderFlags & RENDERFLAG_CLIP) ? CLIP :
						 (renderFlags & RENDERFLAG_CULL) ? CULL : PROJ;
		if (renderFlags & RENDERFLAG_COLOURED)
			vu1_flags |= COLR;
		if (render::EnableFog)
			vu1_flags |= FOGE;
		dma::pTag = dma::pLoc;		// must set this manually if we bypass the dma tag functions
		dma::Store32(u2.m_dma_tag_lo32, (uint32)u1.mp_dma);
		vif::BASE(vu1::Loc);
		vif::ITOP(vu1_flags);
		vu1::Loc += u2.m_dma_tag_lo32>>16;

		if (m_flags & NODEFLAG_BLACKFOG)
		{
			dma::Gosub(SET_FOGCOL,2);
			dma::pLoc -= 8;
			vif::FLUSHA();
			vif::NOP();
		}
	}
	else
	{
		// render any children
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_child->RenderAsSky(*p_ctxt, renderFlags);
		}
	}

}



void CGeomNode::SetBoneTransforms(Mth::Matrix *pMat)
{

	#if 0	// single-buffered
	
	// copy the given matrices into the matrix buffer
	for (int i = 0; i < m_num_bones; i++)
	{
		u4.mp_bone_transforms[i] = pMat[i];
	}

	#else	// double-buffered
	
	int startIndex = m_field ? m_num_bones : 0;

	// copy the given matrices into the correct half of the matrix buffer
	for (int i = 0; i < m_num_bones; i++)
	{
		u4.mp_bone_transforms[startIndex+i] = pMat[i];
	}

	// toggle field
	m_field = !m_field;

	#endif
	
}



#endif


bool CGeomNode::IsLeaf()
{
	return GET_FLAG(NODEFLAG_LEAF);
}


bool CGeomNode::IsObject()
{
	return GET_FLAG(NODEFLAG_OBJECT);
}


bool CGeomNode::IsActive()
{
	return GET_FLAG(NODEFLAG_ACTIVE);
}


bool CGeomNode::IsTransformed()
{
	return GET_FLAG(NODEFLAG_TRANSFORMED);
}


bool CGeomNode::IsSky()
{
	return GET_FLAG(NODEFLAG_SKY);
}


bool CGeomNode::IsColored()
{
	return GET_FLAG(NODEFLAG_COLOURED);
}


bool CGeomNode::IsSkeletal()
{
	return GET_FLAG(NODEFLAG_SKELETAL);
}


bool CGeomNode::IsEnvMapped()
{
	return GET_FLAG(NODEFLAG_ENVMAPPED);
}


bool CGeomNode::IsUVWibbled()
{
	return GET_FLAG(NODEFLAG_UVWIBBLE);
}

bool CGeomNode::IsBillboard()
{
	return GET_FLAG(NODEFLAG_BILLBOARD);
}


#ifdef	__NOPT_ASSERT__
bool CGeomNode::WasRendered()
{
	return GET_FLAG(NODEFLAG_WAS_RENDERED);
}
#endif

void CGeomNode::SetLeaf(bool yes)
{
	SET_FLAG(NODEFLAG_LEAF);
}


void CGeomNode::SetObject(bool yes)
{
	SET_FLAG(NODEFLAG_OBJECT);
}


void CGeomNode::SetActive(bool yes)
{
	SET_FLAG(NODEFLAG_ACTIVE);
}


void CGeomNode::SetTransformed(bool yes)
{
	SET_FLAG(NODEFLAG_TRANSFORMED);
}


void CGeomNode::SetSky(bool yes)
{
	SET_FLAG(NODEFLAG_SKY);
}


void CGeomNode::SetZPush0(bool yes)
{
	SET_FLAG(NODEFLAG_ZPUSH0);
}


void CGeomNode::SetZPush1(bool yes)
{
	SET_FLAG(NODEFLAG_ZPUSH1);
}


void CGeomNode::SetZPush2(bool yes)
{
	SET_FLAG(NODEFLAG_ZPUSH2);
}


void CGeomNode::SetZPush3(bool yes)
{
	SET_FLAG(NODEFLAG_ZPUSH3);
}


void CGeomNode::SetNoShadow(bool yes)
{
	SET_FLAG(NODEFLAG_NOSHADOW);
}


void CGeomNode::SetUVWibbled(bool yes)
{
	SET_FLAG(NODEFLAG_UVWIBBLE);
}


void CGeomNode::SetVCWibbled(bool yes)
{
	SET_FLAG(NODEFLAG_VCWIBBLE);
}


void CGeomNode::SetColored(bool yes)
{
	SET_FLAG(NODEFLAG_COLOURED);
}


void CGeomNode::SetSkinned(bool yes)
{
	SET_FLAG(NODEFLAG_SKINNED);
}


void CGeomNode::SetBillboard(bool yes)
{
	SET_FLAG(NODEFLAG_BILLBOARD);
}


void CGeomNode::SetSkeletal(bool yes)
{
	SET_FLAG(NODEFLAG_SKELETAL);
}


void CGeomNode::SetEnvMapped(bool yes)
{
	SET_FLAG(NODEFLAG_ENVMAPPED);
}


void CGeomNode::SetBlackFog(bool yes)
{
	SET_FLAG(NODEFLAG_BLACKFOG);
}


uint32 CGeomNode::GetChecksum()
{
	return m_checksum;
}


void CGeomNode::SetChecksum(uint32 checksum)
{
	m_checksum = checksum;
}


void CGeomNode::SetBoundingSphere(float x, float y, float z, float R)
{
	m_bounding_sphere[0] = x;
	m_bounding_sphere[1] = y;
	m_bounding_sphere[2] = z;
	m_bounding_sphere[3] = R;
}


void CGeomNode::SetBoundingBox(float x, float y, float z)
{
	m_bounding_box[0] = x;
	m_bounding_box[1] = y;
	m_bounding_box[2] = z;
}


uint8 *CGeomNode::GetDma()
{
	return (m_flags & NODEFLAG_LEAF) ? u1.mp_dma : NULL;
}


void CGeomNode::SetDma(uint8 *pDma)
{
	m_flags |= NODEFLAG_LEAF;
	u1.mp_dma = pDma;
}


void CGeomNode::SetDmaTag(uint8 *pDma)
{
	m_flags |= NODEFLAG_LEAF;
	u2.m_dma_tag_lo32 = dma::call<<28 | (((uint16 *)pDma)[1]&0x3FF)<<16 | 0;
	u1.mp_dma = pDma;
}


CGeomNode *CGeomNode::GetChild()
{
	return (m_flags & NODEFLAG_LEAF) ? NULL : u1.mp_child;
}


void CGeomNode::SetChild(CGeomNode *pChild)
{
	m_flags &= ~NODEFLAG_LEAF;
	u1.mp_child = pChild;
}

void CGeomNode::AddChild(CGeomNode *pChild)
{
	if (!(m_flags & NODEFLAG_LEAF) && u1.mp_child)
	{
		Dbg_Assert(!pChild->mp_sibling);

		CGeomNode *p_last_child = u1.mp_child;
		while (p_last_child->mp_sibling)
		{
			p_last_child = p_last_child->mp_sibling;
		}

		p_last_child->mp_sibling = pChild;
	} else {
		u1.mp_child = pChild;
	}

	m_flags &= ~NODEFLAG_LEAF;
}


CGeomNode *CGeomNode::GetSibling()
{
	return mp_sibling;
}


void CGeomNode::SetSibling(CGeomNode *pSibling)
{
	mp_sibling = pSibling;
}

void CGeomNode::SetSlaveLOD(CGeomNode *pLOD)
{
    CGeomNode *pLOD_list = this;

	while (pLOD_list->mp_next_LOD) {
		if (pLOD_list->mp_next_LOD->m_LOD_far_dist > pLOD->m_LOD_far_dist) {
			pLOD->mp_next_LOD = pLOD_list->mp_next_LOD;
			pLOD_list->mp_next_LOD = pLOD;
			return;
		}

		pLOD_list = pLOD_list->mp_next_LOD;
	}

	// If we get here, then we are at the end of the list
	pLOD_list->mp_next_LOD = pLOD; 
}
	
void CGeomNode::SetLODFarDist(float LOD_far_dist)
{
	m_LOD_far_dist = LOD_far_dist;
}

float CGeomNode::GetLODFarDist()
{
	return m_LOD_far_dist;
}

sGroup *CGeomNode::GetGroup()
{
	return u3.mp_group;
}


void CGeomNode::SetGroup(sGroup *pGroup)
{
	u3.mp_group = pGroup;
}


Mth::Matrix& CGeomNode::GetMatrix()
{
	Dbg_MsgAssert(IsTransformed(), ("trying to access matrix of a non-transformed CGeomNode"));
	Dbg_MsgAssert(u2.mp_transform, ("null matrix pointer"));
	Dbg_MsgAssert(!(m_flags&NODEFLAG_LEAF), ("Transformed leaf node\n"));
	return *u2.mp_transform;
}


void CGeomNode::SetMatrix(Mth::Matrix *p_mat)
{
	Dbg_MsgAssert(!(m_flags&NODEFLAG_LEAF), ("Transformed leaf node\n"));
	u2.mp_transform = (Mth::Matrix *)p_mat;
	SetTransformed(p_mat ? true : false);
}


void CGeomNode::SetUVWibblePointer(float *pData)
{
	mp_uv_wibble = pData;
}


void CGeomNode::SetVCWibblePointer(uint32 *pData)
{
	mp_vc_wibble = pData;
}


void CGeomNode::SetColor(uint32 rgba)
{
	if (rgba == 0x80808080)
	{
		// clear the colored falg if set to the neutral color
		// as things render a lot quicker that way
		// also, applying the color gives a slightly different
		// value from not applying any color
		SetColored(false);
	}
	
	m_colour = rgba;
}


uint32 CGeomNode::GetColor()
{
	if (IsColored())
	{
		return m_colour;
	} else {
		return 0x80808080;
	}
}


void CGeomNode::SetLightGroup(CLightGroup *p_light_group)
{
	if (!IsLeaf())
	{
		u3.mp_light_group = p_light_group;
	}
}


CLightGroup * CGeomNode::GetLightGroup()
{
	if (!IsLeaf())
	{
		return u3.mp_light_group;
	} else {
		return NULL;
	}
}


void CGeomNode::SetVisibility(uint8 mask)
{
	m_flags &= ~ALL_VISIBILITY_BITS;		// Take out old visibility bits
	m_flags |= (mask << VISIBILITY_FLAG_BIT);
}


uint8 CGeomNode::GetVisibility()
{
	return (m_flags & ALL_VISIBILITY_BITS) >> VISIBILITY_FLAG_BIT;
}


void CGeomNode::SetBoneIndex(sint8 index)
{
	m_bone_index = index;
}


sint8 CGeomNode::GetBoneIndex()
{
	return m_bone_index;
}

void	CGeomNode::CountMetrics(CGeomMetrics *p_metrics)
{
	p_metrics->m_total++;
	if (IsObject()) 
	{
		p_metrics->m_object++;				
	}
	if (IsLeaf())
	{
		p_metrics->m_leaf++;
		p_metrics->m_verts += dma::GetNumVertices(u1.mp_dma);
		p_metrics->m_polys += dma::GetNumTris(u1.mp_dma);
	}
	else
	{
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_child->CountMetrics(p_metrics);
		}
	}
	
}


static Mth::Vector	*p_verts = NULL; 

void	CGeomNode::RenderWireframe(int mode)
{
#ifdef	__PLAT_NGPS__
	int lines = 0;
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
	p_verts = new Mth::Vector[100000];
	Mem::Manager::sHandle().PopContext();
	RenderWireframeRecurse(mode, lines);
	delete p_verts;
	p_verts = NULL;
#endif
}

void	CGeomNode::RenderWireframeRecurse(int mode, int &lines)
{

#ifdef	__PLAT_NGPS__
	int sectors = 0;
	int vert_count=0;

	if (IsLeaf())
	{
		if (lines < 95000)  // clmap number of lines, otherwise we crash
		{
			sectors++;
		
			//
			// Do renderable geometry
			int verts = GetNumVerts();
			if (verts>2)
			{
				vert_count += verts;  
				  
				#define	peak 500
				int n=GetNumPolys();
				int r,g,b;
				r=g=b=0;
				if (n <= peak )
				{
					r = (255 * (n)) / peak;  	// r ramps up (black to red)	
				}
				else if (n <= peak * 2)
				{
					r = 255;
					b = (255 * (n - peak) / (peak));	// b&g ramps up (to white)
					g = b;
					
				}
				else
				{
					r = g = b = 255;
				}
				uint32 rgb = (b<<16)|(g<<8)|r;	
	
				
				if (mode == 4)
				{
					// generate a random looking number that's basically a hash of some
					// values in the node, so it stays the same
					uint32 xxx = ((uint32)(this) * verts  * n);
					NxPs2::BeginLines3D(0x80000000 + (0x00ffffff & xxx));		
				}
				else
				{
					NxPs2::BeginLines3D(0x80000000 + (0x00ffffff & rgb));		
				}
				
				GetVerts(p_verts);
				Mth::Vector *p_vert = p_verts+1;
				for (int i = 1; i < verts; i++)
				{
					NxPs2::DrawLine3D((*p_vert)[X],(*p_vert)[Y],(*p_vert)[Z],p_vert[-1][X],p_vert[-1][Y],p_vert[-1][Z]);								
					p_vert++;
					lines++;
				} // end for
		
				NxPs2::EndLines3D();
			} // end if
		}				
	}
	else
	{
		if (WasRendered())
		{
			for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
			{
				p_child->RenderWireframeRecurse(mode, lines);
			}
		}
	}
#endif
}


int	CGeomNode::GetNumVerts()
{
	int num_verts = 0;

	if (IsLeaf())
	{
		num_verts = dma::GetNumVertices(u1.mp_dma);

	}
	else
	{
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				num_verts += mp_next_LOD->GetNumVerts();
			}
		}

		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			num_verts += p_child->GetNumVerts();
		}
	}

	return num_verts;
}


int	CGeomNode::GetNumPolys()
{
	int num_polys = 0;

	if (IsLeaf())
	{
		num_polys = dma::GetNumTris(u1.mp_dma);

	}
	else
	{
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				num_polys += mp_next_LOD->GetNumPolys();
			}
		}

		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			num_polys += p_child->GetNumPolys();
		}
	}

	return num_polys;
}


int	CGeomNode::GetNumBasePolys()
{
	int num_base_polys = 0;

	if (IsLeaf())
	{
		if ((m_flags & NODEFLAG_ZPUSH) == 0)		// check it's the base layer
		{
			num_base_polys = dma::GetNumTris(u1.mp_dma);
		}
	}
	else
	{
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				num_base_polys += mp_next_LOD->GetNumBasePolys();
			}
		}

		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			num_base_polys += p_child->GetNumBasePolys();
		}
	}

	return num_base_polys;
}


int	CGeomNode::GetNumObjects()
{
	int num_objects = 0;

	// Check self
	if (IsObject())
	{
		num_objects++;
	}

	// Recursively check children
	if (!IsLeaf())
	{
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			num_objects += p_child->GetNumObjects();
		}
	}

	return num_objects;
}


CGeomNode*	CGeomNode::GetObject(int num, bool root)
{
	static int found_so_far;

	if (root)
	{
		found_so_far = -1;
		//Dbg_Message("Start looking for #%d", num);
	}

	// Check self
	if (IsObject())
	{
		if (++found_so_far == num)
		{
			//Dbg_Message("Found object #%d", num);
			return this;
		}

		//Dbg_Message("Found object #%d; looking for #%d", found_so_far, num);
	}

	CGeomNode *p_found = NULL;

	// Recursively check children
	if (!IsLeaf())
	{
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_found = p_child->GetObject(num, false);
			if (p_found)
			{
				break;
			}
		}

	}

	if (root)
	{
		Dbg_Assert(p_found);
	}

	return p_found;
}


#ifdef __PLAT_WN32__

void CGeomNode::Preprocess(uint8 *p_baseAddress)
{
	// recursively process any children
	CGeomNode *p_child, *p_sibling;
	if (!(m_flags & NODEFLAG_LEAF))
	{
		for (p_child=u1.mp_child; p_child; p_child=p_sibling)
		{
			p_sibling = p_child->mp_sibling;
			p_child->Preprocess(p_baseAddress);
		}
	}

	// recursively process any LODs
	if (mp_next_LOD)
	{
		mp_next_LOD->Preprocess(p_baseAddress);
	}

	if (!(m_flags & NODEFLAG_LEAF))
	{
		// convert transform pointer to offset
		if (u2.mp_transform)
		{
			u2.mp_transform = (Mth::Matrix *) ( (uint8 *)u2.mp_transform - p_baseAddress );
		}
		else
		{
			u2.mp_transform = (Mth::Matrix *)(-1);
		}
	}

	if (m_flags & NODEFLAG_LEAF)
	{
		// convert dma pointer to offset
		if (u1.mp_dma)
		{
			u1.mp_dma = (uint8 *)( u1.mp_dma - p_baseAddress );
		}
		else
		{
			u1.mp_dma = (uint8 *)(-1);
		}
	}
	else
	{
		// convert child pointer to offset
		if (u1.mp_child)
		{
			u1.mp_child = (CGeomNode *) ( (uint8 *)u1.mp_child - p_baseAddress );
		}
		else
		{
			u1.mp_child = (CGeomNode *)(-1);
		}
	}

	// leave group checksum alone

	// convert sibling pointer to offset
	if (mp_sibling)
	{
		mp_sibling = (CGeomNode *) ( (uint8 *)mp_sibling - p_baseAddress );
	}
	else
	{
		mp_sibling = (CGeomNode *)(-1);
	}

	// convert LOD pointer to offset
	if (mp_next_LOD)
	{
		mp_next_LOD = (CGeomNode *) ( (uint8 *)mp_next_LOD - p_baseAddress );
	}
	else
	{
		mp_next_LOD = (CGeomNode *)(-1);
	}

	// convert uv wibble pointer to offset
	if (m_flags & NODEFLAG_UVWIBBLE)
	{
		if (!(m_flags & NODEFLAG_ENVMAPPED) || !(m_flags & NODEFLAG_LEAF))		// can't wibble environment-mapped leaves
		{
			mp_uv_wibble = (float *) ( (uint8 *)mp_uv_wibble - p_baseAddress );
		}
		else
		{
			mp_uv_wibble = (float *)(-1);
		}
	}

	// convert vc wibble pointer to offset
	if (m_flags & NODEFLAG_VCWIBBLE)
	{
		// first convert mesh info
		uint32 *p_meshdata = mp_vc_wibble;
		int seq, num_seqs, vert, num_verts;

		num_seqs = *p_meshdata++;
		for (seq=0; seqGetChild();
	// iterate over siblings without recursion
	while (p_child)	
	{
		
		printf ("%2d:",SNL_Depth);
		for (int i=i;iGetFlags());
		
		CGeomNode *p_next_child = p_child->GetSibling();
		
		// if child not a leaf, then just recurse down
		if (! (p_child->GetFlags() & NODEFLAG_LEAF))
		{
			StripNiceLeaves(p_child);
		}
		else
		{
		
			// it's a leaf node, so maybe strip it, and then carry on to the siblings
			if (((p_child->GetFlags() & (NODEFLAG_ACTIVE		|
				 NODEFLAG_SKY			|
				 NODEFLAG_TRANSFORMED	|
				 NODEFLAG_LEAF			|
				 NODEFLAG_OBJECT		|
				 NODEFLAG_COLOURED		|
				 NODEFLAG_SKELETAL		|
				 NODEFLAG_BILLBOARD		|
				 NODEFLAG_UVWIBBLE		|
				 NODEFLAG_VCWIBBLE		|
				 NODEFLAG_ENVMAPPED))

			==  (NODEFLAG_ACTIVE		|
				 NODEFLAG_LEAF)))
			
			{
				// strip it out (eventually add it to a quick-render list)
				if (p_parent->GetChild() == p_child)
				{
					// just need to fix up the parent
					p_parent->SetChild(p_next_child);
				}
				else
				{
					// find the elder sibling and fix up his sibling
					CGeomNode *p_elder_sibling = p_parent->GetChild();
					while (p_elder_sibling->GetSibling() != p_child)
					{
						p_elder_sibling = p_elder_sibling->GetSibling();
						Dbg_Assert(p_elder_sibling);	// catch infinite loop
					}
					p_elder_sibling->SetSibling(p_next_child);							
				}
				// p_child is now an orphan
				// TODO - add to quick render list ...
			}
		}
		p_child = p_next_child;
	}	
	SNL_Depth--;
}



CGeomNode* CGeomNode::sProcessInPlace(uint8* pPipData, uint32 loadFlags)
{
	Dbg_MsgAssert(((int)(pPipData) & 0xf) == 0,("pPipData (0x%x) not multiple of 16 \n"
													,(int)pPipData));

	// Jump to beginning of CGeomNode data
	pPipData += *(uint32 *)pPipData;
	
	// from header, get address of root node
	CGeomNode *p_root_node = (CGeomNode *) (pPipData + *(uint32 *)pPipData);

	Dbg_MsgAssert(((int)(p_root_node) & 0xf) == 0,("p_root_node (0x%x) not multiple of 16, .SCN.PS2 file corrupt... \n"
													,(int)p_root_node));



	// recursively process the tree
	#ifdef	__NOPT_ASSERT__
//	int leaves = 
	#endif
	p_root_node->ProcessNodeInPlace(pPipData);
	#ifdef	__NOPT_ASSERT__
//	printf (">>>>> Number of leaves in geom PIP file = %d\n",leaves);
	#endif


	//StripNiceLeaves(p_root_node);	

	// add the root node to the world or to the database
	p_root_node->BecomesChildOf((loadFlags & LOADFLAG_RENDERNOW) ? &sWorld : &sDatabase);

	// set the root node's sky flag if loaded as a sky
	if (loadFlags & LOADFLAG_SKY)
	{
		p_root_node->SetSky(true);
	}

	// return the root node pointer
	return p_root_node;
}

void * CGeomNode::sGetHierarchyArray(uint8 *pPipData, int& size)
{
	Dbg_MsgAssert(((int)(pPipData) & 0xf) == 0,("pPipData (0x%x) not multiple of 16 \n"
													,(int)pPipData));

	uint32 *pPip32Data = (uint32 *) pPipData;

	pPip32Data++;		// skip CGeomNode offset

	// Get array pointer and size
	void *p_array = (pPipData + *(pPip32Data++));
	size = *pPip32Data;

	if (size)
	{
		return p_array;
	} else {
		return NULL;
	}
}

void CGeomNode::AddToTree(uint32 loadFlags)
{
	BecomesChildOf((loadFlags & LOADFLAG_RENDERNOW) ? &sWorld : &sDatabase);

	// set the root node's sky flag if loaded as a sky
	if (loadFlags & LOADFLAG_SKY)
	{
		SetSky(true);
	}
}

void CGeomNode::Cleanup()
{
	// remove from any node it belongs to
	RemoveFrom(&sWorld);
	RemoveFrom(&sDatabase);
}


CGeomNode* CGeomNode::CreateInstance(Mth::Matrix *pMat, CGeomNode *p_parent)
{
	// copy node
	CGeomNode *p_node = new CGeomNode(*this);

	// flag as an instance
	p_node->m_flags |= NODEFLAG_INSTANCE;

	// set matrix
	p_node->SetMatrix(pMat);

	// add to world
	if (p_parent)
	{
		p_node->BecomesChildOf(p_parent);
	}
	else
	{
		p_node->BecomesChildOf(&sWorld);
	}

	// activate!
	p_node->SetActive(true);

	// return new node
	return p_node;
}


// skeletal version
CGeomNode* CGeomNode::CreateInstance(Mth::Matrix *pMat, int numBones, Mth::Matrix *pBoneTransforms, CGeomNode *p_parent)
{
	// copy node
	CGeomNode *p_node = new CGeomNode(*this);

	// flag as a skeletal instance
	p_node->m_flags |= NODEFLAG_SKELETAL|NODEFLAG_INSTANCE;

	// set matrix and bone data
	p_node->SetMatrix(pMat);
	p_node->m_num_bones = numBones;
	p_node->u4.mp_bone_transforms = pBoneTransforms;
	p_node->m_field = 0;

	// add to world
	if (p_parent)
	{
		p_node->BecomesChildOf(p_parent);
	}
	else
	{
		p_node->BecomesChildOf(&sWorld);
	}

	// activate!
	p_node->SetActive(true);

	// return new node
	return p_node;
}


void CGeomNode::DeleteInstance()
{
	Dbg_MsgAssert(m_flags&NODEFLAG_INSTANCE, ("attempt to delete a non-instance"));

	bool removed;
	removed = RemoveFrom(&sWorld);
	removed = removed || RemoveFromChildrenOf(&sWorld);		// Check all cloned scenes, too

	Dbg_MsgAssert(removed, ("Couldn't remove instanced CGeomNode from CGeomNode tree"));

	delete this;
}

CGeomNode* CGeomNode::CreateCopy(CGeomNode *p_parent, bool root)
{
	// copy node
	CGeomNode *p_node = new CGeomNode(*this);

	// clear some pointers
	p_node->mp_sibling = NULL;

	// add to world if this is the top node
	if (root)
	{
		if (p_parent)
		{
			p_node->BecomesChildOf(p_parent);
		}
		else
		{
			p_node->BecomesChildOf(&sWorld);
		}
	}

	// activate!
	p_node->SetActive(true);

	// Also copy data and tree underneath
	if (IsLeaf())
	{
		// Copy the dma data
		Dbg_Assert(u1.mp_dma);
		p_node->u1.mp_dma = new uint8[dma::GetDmaSize(u1.mp_dma)];
		Dbg_Assert(!((uint32) p_node->u1.mp_dma & 0xf))
		dma::Copy(u1.mp_dma, p_node->u1.mp_dma);
	} else {
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				p_node->mp_next_LOD = mp_next_LOD->CreateCopy(NULL, false);
			}
		}

		// Recursively copy children
		p_node->u1.mp_child = NULL;
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_node->AddChild(p_child->CreateCopy(NULL, false));
		}
	}

	// return new node
	return p_node;
}

void WaitForRendering();

// Recursivly deletes an object and all its children
void CGeomNode::DeleteCopy(bool root)
{
	// Garrett: Kinda hacky, but gets the job done.  Make sure we aren't rendering before
	// we release DMA buffers
	WaitForRendering();  

	Dbg_MsgAssert(!(m_flags & NODEFLAG_INSTANCE), ("attempt to delete an instance"));

	if (root)
	{
		bool removed;
		removed = RemoveFrom(&sWorld);
		removed = removed || RemoveFromChildrenOf(&sWorld);		// Check all cloned scenes, too

		Dbg_MsgAssert(removed, ("Couldn't remove copied CGeomNode from CGeomNode tree"));
	}

	// Also delete data and tree underneath
	if (IsLeaf())
	{
		// Delete the dma data
		Dbg_Assert(u1.mp_dma);
		delete [] u1.mp_dma;
	} else {
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				mp_next_LOD->DeleteCopy(false);
			}
		}

		// Recursively delete children
		// (Mick) restructured this because DeleteCopy will delete the object it is run on
		CGeomNode *p_child=u1.mp_child;
		while ( p_child)
		{
			CGeomNode *p_next=p_child->mp_sibling;
			p_child->DeleteCopy(false);
			p_child = p_next;
		}
	}

	delete this;	   // <<<< Warning:  deletes 'this'
}

void CGeomNode::Translate(const Mth::Vector & delta_pos)
{
	Dbg_MsgAssert(!(m_flags & NODEFLAG_INSTANCE), ("attempt to translate an instance"));

	// Update bounding sphere
	m_bounding_sphere[X] += delta_pos[X];
	m_bounding_sphere[Y] += delta_pos[Y];
	m_bounding_sphere[Z] += delta_pos[Z];

#if 0
	// Test for Park Editor
	Dbg_Message("Translating by (%.8f, %.8f, %.8f)", delta_pos[X], delta_pos[Y], delta_pos[Z]);
	int delta_x = (int) (delta_pos[X] * SUB_INCH_PRECISION);
	int delta_y = (int) (delta_pos[Y] * SUB_INCH_PRECISION);
	int delta_z = (int) (delta_pos[Z] * SUB_INCH_PRECISION);
	Dbg_MsgAssert(!(delta_x & 0xF), ("Fraction in X: (%x, %x, %x)", delta_x, delta_y, delta_z));
	Dbg_MsgAssert(!(delta_y & 0xF), ("Fraction in Y: (%x, %x, %x)", delta_x, delta_y, delta_z));
	Dbg_MsgAssert(!(delta_z & 0xF), ("Fraction in Z: (%x, %x, %x)", delta_x, delta_y, delta_z));
#endif

	// Update whole tree
	if (IsLeaf())
	{
		// Get the dma data
		Dbg_Assert(u1.mp_dma);

		if (dma::GetBitLengthXYZ(u1.mp_dma) == 32)
		{
			// Calculate int versions
			int delta_x = (int) (delta_pos[X] * SUB_INCH_PRECISION);
			int delta_y = (int) (delta_pos[Y] * SUB_INCH_PRECISION);
			int delta_z = (int) (delta_pos[Z] * SUB_INCH_PRECISION);

			int num_verts = dma::GetNumVertices(u1.mp_dma);

			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
			int32 *p_verts = new int32[num_verts * 4];
			dma::ExtractXYZs(u1.mp_dma, (uint8 *) p_verts);		

			for (int i = 0; i < num_verts; i++) {
				p_verts[(i * 4) + 0] += delta_x;
				p_verts[(i * 4) + 1] += delta_y;
				p_verts[(i * 4) + 2] += delta_z;
			}

			dma::ReplaceXYZs(u1.mp_dma, (uint8 *) p_verts);		

			delete [] p_verts;

			Mem::Manager::sHandle().PopContext();
		}
		else
		{
			Dbg_MsgAssert(dma::GetBitLengthXYZ(u1.mp_dma)==16, ("oh dear, should've been either 16 or 32..."));
		}
	} else {
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				mp_next_LOD->Translate(delta_pos);
			}
		}

		// Recursively update children
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_child->Translate(delta_pos);
		}
	}
}

void CGeomNode::RotateY(const Mth::Vector & world_origin, Mth::ERot90 rot_y, bool root)
{
	Dbg_MsgAssert(!(m_flags & NODEFLAG_INSTANCE), ("attempt to translate an instance"));

	if (root)
	{
		Translate(-world_origin);
	}

	// Update bounding box
	// Since they are relative to width and height, we just need to possibly swap X and Z
	if ((int) rot_y & 1)
	{
		float temp = m_bounding_box[X];
		m_bounding_box[X] = m_bounding_box[Z];
		m_bounding_box[Z] = temp;
	}

	// Also rotate bounding sphere position
	m_bounding_sphere.RotateY90(rot_y);

	// Update whole tree
	if (IsLeaf())
	{
		// Delete the dma data
		Dbg_Assert(u1.mp_dma);

		int num_verts = dma::GetNumVertices(u1.mp_dma);

		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
		int32 *p_verts = new int32[num_verts * 4];
		dma::ExtractXYZs(u1.mp_dma, (uint8 *) p_verts);		

		for (int i = 0; i < num_verts; i++) {
			Mth::RotateY90(rot_y,
						   p_verts[(i * 4) + 0],
						   p_verts[(i * 4) + 1],
						   p_verts[(i * 4) + 2]);
		}

		dma::ReplaceXYZs(u1.mp_dma, (uint8 *) p_verts);		

		delete [] p_verts;

		Mem::Manager::sHandle().PopContext();
	} else {
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				mp_next_LOD->RotateY(world_origin, rot_y, false);
			}
		}

		// Recursively update children
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_child->RotateY(world_origin, rot_y, false);
		}
	}

	if (root)
	{
		Translate(world_origin);
	}
}

void CGeomNode::Scale(const Mth::Vector & world_origin, const Mth::Vector & scale, bool root)
{
	Dbg_MsgAssert(!(m_flags & NODEFLAG_INSTANCE), ("attempt to scale an instance"));

	if (root)
	{
		Translate(-world_origin);
	}

	// Update bounding box
	m_bounding_box[X] *= scale[X];
	m_bounding_box[Y] *= scale[Y];
	m_bounding_box[Z] *= scale[Z];

#if 0	// Don't do this since it is a local scale
	// Also scale bounding sphere position
	// Update bounding sphere
	m_bounding_sphere[X] *= scale[X];
	m_bounding_sphere[Y] *= scale[Y];
	m_bounding_sphere[Z] *= scale[Z];
#endif

	// Garrett: The radius calculation is not accurate, but so far, it works.  It will always round up.
	// We have to assume the largest radius
	float radius_scale = sqrtf((scale[X] * scale[X]) + (scale[Y] * scale[Y]) + (scale[Z] + scale[Z]));
	m_bounding_sphere[W] *= radius_scale;
	// But see if the bounding box radius is smaller
	float box_radius = sqrtf((m_bounding_box[X] * m_bounding_box[X]) + (m_bounding_box[Y] * m_bounding_box[Y]) + (m_bounding_box[Z] * m_bounding_box[Z]));
	//Dbg_Message("Calculated sphere radius %f; Bounding box radius %f", m_bounding_sphere[W] , box_radius);
	m_bounding_sphere[W] = Mth::Min(box_radius, m_bounding_sphere[W]);

	// Update whole tree
	if (IsLeaf())
	{
		// Get the dma data
		Dbg_Assert(u1.mp_dma);

		// Calculate int versions
		int scale_x = (int) (scale[X] * SUB_INCH_PRECISION);
		int scale_y = (int) (scale[Y] * SUB_INCH_PRECISION);
		int scale_z = (int) (scale[Z] * SUB_INCH_PRECISION);

		// Check to see that we didn't go to zero
		Dbg_MsgAssert(scale_x, ("CGeomNode::Scale(): X scale went to 0 in integer conversion. Original value %f", scale[X]));
		Dbg_MsgAssert(scale_y, ("CGeomNode::Scale(): Y scale went to 0 in integer conversion. Original value %f", scale[Y]));
		Dbg_MsgAssert(scale_z, ("CGeomNode::Scale(): Z scale went to 0 in integer conversion. Original value %f", scale[Z]));

		int num_verts = dma::GetNumVertices(u1.mp_dma);

		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
		int32 *p_verts = new int32[num_verts * 4];
		dma::ExtractXYZs(u1.mp_dma, (uint8 *) p_verts);		

		// SUB_INCH_PRECISION must be a whole number
		Dbg_Assert( ((int) (SUB_INCH_PRECISION * 1000.0f)) == (((int) SUB_INCH_PRECISION) * 1000) );

		for (int i = 0; i < num_verts; i++) {
			p_verts[(i * 4) + 0] = (p_verts[(i * 4) + 0] * scale_x) / ((int) SUB_INCH_PRECISION);
			p_verts[(i * 4) + 1] = (p_verts[(i * 4) + 1] * scale_y) / ((int) SUB_INCH_PRECISION);
			p_verts[(i * 4) + 2] = (p_verts[(i * 4) + 2] * scale_z) / ((int) SUB_INCH_PRECISION);
		}

		dma::ReplaceXYZs(u1.mp_dma, (uint8 *) p_verts);		

		delete [] p_verts;

		Mem::Manager::sHandle().PopContext();
	} else {
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				mp_next_LOD->Scale(world_origin, scale, false);
			}
		}

		// Recursively update children
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_child->Scale(world_origin, scale, false);
		}
	}

	if (root)
	{
		Translate(world_origin);
	}
}

void 		CGeomNode::GetVerts(Mth::Vector *p_verts, bool root)
{
	// Not thread safe
	static int vert_index;
	if (root)
	{
		vert_index = 0;
	}

	if (IsLeaf())
	{
		// Get the dma data
		Dbg_Assert(u1.mp_dma);
		int num_verts = dma::GetNumVertices(u1.mp_dma);

		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

		int32 *p_raw_verts = (int32 *) &p_verts[vert_index];
		//int32 *p_raw_verts = new int32[num_verts * 4];
		dma::ExtractXYZs(u1.mp_dma, (uint8 *) p_raw_verts);		

		// Convert center to integer
		// (Mick:  Changed to always have a center offset, as even 32 bit coordinates have one
		// previous, the code assumed that 32 bit coordinate were absolute world coords, and so
		// did not add in the "center" offset
		int32 center[4];
		dma::ConvertFloatToXYZ(center,  m_bounding_sphere);

		for (int i = 0; i < num_verts; i++, vert_index++) 
		{
			dma::ConvertXYZToFloat(p_verts[vert_index], &(p_raw_verts[i * 4]), center);
		}

//		delete [] p_raw_verts;

		Mem::Manager::sHandle().PopContext();
	} else {
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				mp_next_LOD->GetVerts(p_verts, false);
			}
		}

		// Get children verts
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_child->GetVerts(p_verts, false);
		}
	}
}

void 		CGeomNode::SetVerts(Mth::Vector *p_verts, bool root)
{
	// Not thread safe
	static int vert_index;
	if (root)
	{
		vert_index = 0;
	}

	if (IsLeaf())
	{
		// Get the dma data
		Dbg_Assert(u1.mp_dma);
		int num_verts = dma::GetNumVertices(u1.mp_dma);

		// Recalculate the bounding data BEFORE setting the DMA data, since it will change the
		// center of the bounding sphere.
		recalculate_bounding_data(&p_verts[vert_index], num_verts);

		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

		int32 *p_raw_verts = new int32[num_verts * 4];

		// Convert center to integer
		int32 center[4];
		if (dma::GetBitLengthXYZ(u1.mp_dma) < 32)
		{
			dma::ConvertFloatToXYZ(center,  m_bounding_sphere);
		}
		

		for (int i = 0; i < num_verts; i++, vert_index++) {
			if (dma::GetBitLengthXYZ(u1.mp_dma) == 32)
			{
				dma::ConvertFloatToXYZ(&(p_raw_verts[i * 4]), p_verts[vert_index]);
			} else {
				dma::ConvertFloatToXYZ(&(p_raw_verts[i * 4]), p_verts[vert_index], center);
			}
		}

		// Replaces only XYZs (not W)
		dma::ReplaceXYZs(u1.mp_dma, (uint8 *) p_raw_verts, true);		

		delete [] p_raw_verts;

		Mem::Manager::sHandle().PopContext();
	} else {
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				mp_next_LOD->SetVerts(p_verts, false);
			}
		}

		// Set children verts
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_child->SetVerts(p_verts, false);
		}

		// Recalculate the bounding data at this level using all the verts (assuming this is
		// going to be the object node).
		Dbg_MsgAssert(IsObject(), ("Don't know how to recalculate the bounding data for this node: Not an object"));
		int num_verts = GetNumVerts();
		recalculate_bounding_data(p_verts, num_verts);
	}
}

void 		CGeomNode::recalculate_bounding_data(Mth::Vector *p_verts, int num_verts)
{
	Mth::Vector min, max, center, half_vec;
	float radius;
	int i;

	// calculate bounding box for the mesh
	min[0] = min[1] = min[2] = 1e30f;
	max[0] = max[1] = max[2] = -1e30f;

	for (i=0; i max[X])
			max[X] = p_verts[i][X];
		if (p_verts[i][Y] > max[Y])
			max[Y] = p_verts[i][Y];
		if (p_verts[i][Z] > max[Z])
			max[Z] = p_verts[i][Z];
	}

	// calculate bounding sphere for the mesh
	center = (max+min)*0.5f;
	radius = 0.0f;
	for (i=0; i radius)
			radius = len;
	}

	// Update bounding box data
	half_vec = 0.5f * (max - min);
	m_bounding_box[X] = half_vec[X];
	m_bounding_box[Y] = half_vec[Y];
	m_bounding_box[Z] = half_vec[Z];

	// Update bounding sphere data
	m_bounding_sphere = center;
	m_bounding_sphere[W] = radius;
}

void 		CGeomNode::GetColors(uint32 *p_colors, bool root)
{
	// Not thread safe
	static int vert_index;
	if (root)
	{
		vert_index = 0;
	}

	if (IsLeaf())
	{
		// Get the dma data
		Dbg_Assert(u1.mp_dma);
		int num_verts = dma::GetNumVertices(u1.mp_dma);

		dma::ExtractRGBAs(u1.mp_dma, (uint8 *) &(p_colors[vert_index]));		
		vert_index += num_verts;
	} else {
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				mp_next_LOD->GetColors(p_colors, false);
			}
		}

		// Get children colors
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_child->GetColors(p_colors, false);
		}
	}
}

void 		CGeomNode::SetColors(uint32 *p_colors, bool root)
{
	// Not thread safe
	static int vert_index;
	if (root)
	{
		vert_index = 0;
	}

	if (IsLeaf())
	{
		// Set the dma data
		Dbg_Assert(u1.mp_dma);
		int num_verts = dma::GetNumVertices(u1.mp_dma);

		dma::ReplaceRGBAs(u1.mp_dma, (uint8 *) &(p_colors[vert_index]));		
		vert_index += num_verts;
	} else {
		// Check if we have LODs
		if (IsObject())
		{
			if (mp_next_LOD)
			{
				mp_next_LOD->SetColors(p_colors, false);
			}
		}

		// Set children colors
		for (CGeomNode *p_child=u1.mp_child; p_child; p_child=p_child->mp_sibling)
		{
			p_child->SetColors(p_colors, false);
		}
	}
}

/*
	vc wibble material data block format
	------------------------------------

	int num_seqs;
	for (seq=0; seq end_time) time = end_time;

		// locate the keyframe
		for (key=num_keys-1,p_key=(float *)p_matdata+5*num_keys-5; key>=0; key--,p_key-=5)
		{
			if (time >= *(int *)p_key)
			{
				break;
			}
		}

		// parameter expressing how far we are between between this keyframe and the next
		t = (float)(time - ((int *)p_key)[0]) / (float)(((int *)p_key)[5] - ((int *)p_key)[0]);

		#if 0
		// for debugging a TT bug...
		for (int i=1; i<=9; i++)
			Dbg_MsgAssert(p_key[i]>=0 && p_key[i]<=255, ("sTime=%d, num_keys=%d, key=%d, p_key[1]=%g, p_key[2]=%g, p_key[3]=%g, p_key[4]=%g, p_key[5]=%g, p_key[6]=%g, p_key[7]=%g, p_key[8]=%g, p_key[9]=%g, time=%d, start_time=%d, end_time=%d, period=%d, t=%g", \
														  render::sTime,	\
														  num_keys,			\
														  key,				\
														  p_key[1],			\
														  p_key[2],			\
														  p_key[3],			\
														  p_key[4],			\
														  p_key[5],			\
														  p_key[6],			\
														  p_key[7],			\
														  p_key[8],			\
														  p_key[9],			\
														  time,				\
														  start_time,		\
														  end_time,			\
														  period,			\
														  t));
		#endif

		// interpolate between colours
		r = (1.0f-t) * p_key[1] + t * p_key[6];
		g = (1.0f-t) * p_key[2] + t * p_key[7];
		b = (1.0f-t) * p_key[3] + t * p_key[8];
		a = (1.0f-t) * p_key[4] + t * p_key[9];

		Dbg_MsgAssert(r>=0 && r<=255 && g>=0 && g<=255 && b>=0 && b<=255 && a>=0 && a<=255,
					  ("wibbled colour r=%g, g=%g, b=%g, a=%g", r,g,b,a));

		rgba  =	(uint32)(sint32)r     & 0x000000FF |
				(uint32)(sint32)g<<8  & 0x0000FF00 |
				(uint32)(sint32)b<<16 & 0x00FF0000 |
				(uint32)(sint32)a<<24;

		// get # vertices wibbled by this sequence
		num_verts = *p_meshdata++;

		// loop over wibbled vertices, overwriting colour in dma data
		for (vert=0; vertm_flags & NODEFLAG_LEAF) && p_node->m_bounding_sphere[3] >= 1.0e+10f)
	{
		p_node = p_node->u1.mp_child;
	}

	sphere = p_node->m_bounding_sphere;

	// then account for any siblings
	while (p_node->mp_sibling)
	{
		p_node = p_node->mp_sibling;

		Mth::Vector x0, x1;
		if (p_node->m_bounding_sphere[3] > sphere[3])
		{
			x0 = p_node->m_bounding_sphere; 
			x1 = sphere;
		}
		else
		{
			x0 = sphere;
			x1 = p_node->m_bounding_sphere;
		}
		float r0=x0[3], r1=x1[3];

		// (x0,r0) is the larger sphere, (x1,r1) the smaller
		// d is the vector between centres
		Mth::Vector d = x1 - x0;

		// square of distance between centres
		float D2 = DotProduct(d,d);

		// (r0-r1)^2
		float R2 = (r0-r1)*(r0-r1);

		// m = max { (r1-r0+|d|)/2, 0 }
		float m = 0.0f;
		float D = sqrtf(D2);
		if (R2-D2 < 0)
		{
			m = (r1-r0 + sqrtf(D2)) * 0.5f;
		}

		// normalise d
		if (D>0)
			d *= 1.0f/D;

		sphere    = x0 + m * d;
		sphere[3] = r0 + m;
	}

	return sphere;
}

Mth::CBBox CGeomNode::GetBoundingBox()
{
	CGeomNode *p_node = this;

	while (!(p_node->m_flags & NODEFLAG_LEAF) && p_node->m_bounding_sphere[3] >= 1.0e+10f)
	{
		p_node = p_node->u1.mp_child;
	}

	Mth::Vector min_p(p_node->m_bounding_sphere);
	Mth::Vector max_p(p_node->m_bounding_sphere);

	//Dbg_Message("Bounding sphere (%.8f, %.8f, %.8f, %.8f)", p_node->m_bounding_sphere[X], p_node->m_bounding_sphere[Y], p_node->m_bounding_sphere[Z], p_node->m_bounding_sphere[W]);
	//Dbg_Message("Bounding box (%.8f, %.8f, %.8f)", p_node->m_bounding_box[X], p_node->m_bounding_box[Y], p_node->m_bounding_box[Z]);

	min_p[X] -= p_node->m_bounding_box[0];
	min_p[Y] -= p_node->m_bounding_box[1];
	min_p[Z] -= p_node->m_bounding_box[2];
	min_p[W] = 1.0f;

	max_p[X] += p_node->m_bounding_box[0];
	max_p[Y] += p_node->m_bounding_box[1];
	max_p[Z] += p_node->m_bounding_box[2];
	min_p[W] = 1.0f;

	return Mth::CBBox(min_p, max_p);
}



// interface to uv wibble control


void CGeomNode::SetUVWibbleParams(float u_vel, float u_amp, float u_freq, float u_phase,
								  float v_vel, float v_amp, float v_freq, float v_phase)
{
	Dbg_Assert(mp_uv_wibble);

	mp_uv_wibble[0] = u_vel;
	mp_uv_wibble[1] = v_vel;
	mp_uv_wibble[2] = u_freq;
	mp_uv_wibble[3] = v_freq;
	mp_uv_wibble[4] = u_amp;
	mp_uv_wibble[5] = v_amp;
	mp_uv_wibble[6] = u_phase;
	mp_uv_wibble[7] = v_phase;
}



void CGeomNode::UseExplicitUVWibble(bool yes)
{
	SET_FLAG(NODEFLAG_EXPLICIT_UVWIBBLE);
}



void CGeomNode::SetUVWibbleOffsets(float u_offset, float v_offset)
{
	Dbg_Assert(mp_uv_wibble);

	mp_uv_wibble[8] = u_offset;
	mp_uv_wibble[9] = v_offset;
}



void CGeomNode::SetUVOffset(uint32 material_name, int pass, float u_offset, float v_offset)
{
	Dbg_MsgAssert(0, ("SetUVOffset not supported for CGeomNodes"));
}


void CGeomNode::SetUVMatrix(uint32 material_name, int pass, Mth::Matrix &mat)
{
	Dbg_MsgAssert(0, ("SetUVOffset not supported for CGeomNodes"));
}




#endif









} // namespace NxPs2






================================================
FILE: Code/Gfx/NGPS/NX/geomnode.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		THPS4													**
**																			**
**	Module:			NxPs2					 								**
**																			**
**	File name:		geometry.h												**
**																			**
**	Created by:		02/08/02	-	mrd										**
**																			**
**	Description:	classes for PS2 geometry								**
**																			**
*****************************************************************************/

#ifndef __GFX_NGPS_NX_GEOMETRY_H
#define __GFX_NGPS_NX_GEOMETRY_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

//#include "vu1context.h"

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define MAX_LOD_DIST			(10000000.0f)

namespace Mth
{
	class CBBox;
}

namespace NxPs2
{
	// Forward declarations
	struct	sGroup;
	struct  sTexture;
	class	CLightGroup;
	class	CVu1Context;

// For the flags, note the visibility bits that are reserved below
#define VISIBILITY_FLAG_BIT		(24)
#define NUM_VISIBILITY_BITS		(8)
//#define ALL_VISIBILITY_BITS		(((1 << NUM_VISIBILITY_BITS) - 1) << VISIBILITY_FLAG_BIT)
#define ALL_VISIBILITY_BITS		((0xFFFFFFFF >> (32 - NUM_VISIBILITY_BITS)) << VISIBILITY_FLAG_BIT)

#define NODEFLAG_ACTIVE			(1<<0)
#define NODEFLAG_LEAF			(1<<1)
#define NODEFLAG_OBJECT			(1<<2)
#define NODEFLAG_TRANSFORMED	(1<<3)
#define NODEFLAG_COLOURED		(1<<4)
#define NODEFLAG_SKY			(1<<5)
#define NODEFLAG_ZPUSH0			(1<<6)
#define NODEFLAG_ZPUSH1			(1<<7)
#define NODEFLAG_NOSHADOW		(1<<8)
#define NODEFLAG_UVWIBBLE		(1<<9)
#define NODEFLAG_VCWIBBLE		(1<<10)
#define NODEFLAG_SKINNED		(1<<11)
#define NODEFLAG_SKELETAL		(1<<11)
#define NODEFLAG_INSTANCE		(1<<12)
#define NODEFLAG_OBJECT_LIGHTS	(1<<13)
#define NODEFLAG_ENVMAPPED		(1<<14)
#define NODEFLAG_BILLBOARD		(1<<15)
#define NODEFLAG_EXPLICIT_UVWIBBLE		(1<<16)
#define NODEFLAG_ZPUSH2			(1<<17)
#define NODEFLAG_ZPUSH3			(1<<18)
#define NODEFLAG_BLACKFOG		(1<<19)

#define NODEFLAG_LAST_OCCLUSION_VALID	(1 << 20)
#define NODEFLAG_LAST_OCCLUSION_TRUE	(1 << 21)

#ifdef	__NOPT_ASSERT__
#define NODEFLAG_WAS_RENDERED	(1 << 22) // True if rendered last frame.  Not always valid for leaf nodes
#endif

#if 0
#define NODEFLAG_VISIBILITY_TEST_PERFORMED		( 1 << 22 )
#define NODEFLAG_VISIBILITY_TEST_NOT_VISIBLE	( 1 << 23 )
#endif

#define NODEFLAG_VISIBILITY_0	(1<<(VISIBILITY_FLAG_BIT + 0))
#define NODEFLAG_VISIBILITY_1	(1<<(VISIBILITY_FLAG_BIT + 1))
#define NODEFLAG_VISIBILITY_2	(1<<(VISIBILITY_FLAG_BIT + 2))
#define NODEFLAG_VISIBILITY_3	(1<<(VISIBILITY_FLAG_BIT + 3))
#define NODEFLAG_VISIBILITY_4	(1<<(VISIBILITY_FLAG_BIT + 4))
#define NODEFLAG_VISIBILITY_5	(1<<(VISIBILITY_FLAG_BIT + 5))
#define NODEFLAG_VISIBILITY_6	(1<<(VISIBILITY_FLAG_BIT + 6))
#define NODEFLAG_VISIBILITY_7	(1<<(VISIBILITY_FLAG_BIT + 7))

#define NODEFLAG_ZPUSH			(NODEFLAG_ZPUSH0 | NODEFLAG_ZPUSH1 | NODEFLAG_ZPUSH2 | NODEFLAG_ZPUSH3)

#define LOADFLAG_RENDERNOW		(1<<0)
#define LOADFLAG_SKY			(1<<1)

// put these here temporarily
#define RENDERFLAG_CULL			(1<<0)
#define RENDERFLAG_CLIP			(1<<1)
#define RENDERFLAG_SHADOW		(1<<2)
#define RENDERFLAG_COLOURED		(1<<3)
#define RENDERFLAG_TRANSFORMED	(1<<4)
#define RENDERFLAG_SKELETAL		(1<<5)


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

// from CClass, so they get zeroed automatically 
class CGeomMetrics : public Spt::Class
{
public:
	int 	m_total;
	int		m_leaf;
	int		m_object;
	int		m_verts;
	int		m_polys;
};


class CGeomNode
{
public:

	#ifdef __PLAT_NGPS__
#	if 0
	void				RecursiveVisibilityTest( CVu1Context& ctxt, uint32 renderFlags );
#	endif
	void				RenderWorld(CVu1Context& ctxt, uint32 renderFlags);
	void				RenderScene(CVu1Context& ctxt, uint32 renderFlags);
	void				RenderObject(CVu1Context& ctxt, uint32 renderFlags);
	void				RenderTransformedLeaf(CVu1Context& ctxt, uint32 renderFlags);
	void				RenderNonTransformedLeaf(CVu1Context& ctxt, uint32 renderFlags);
	void				RenderMinimalLeaf(CVu1Context& ctxt);
	void				RenderAsSky(CVu1Context& ctxt, uint32 renderFlags);
	void 				SetBoneTransforms(Mth::Matrix *pMat);

	// These modify the actual DMA data
	void				Translate(const Mth::Vector & delta_trans);		// delta since we don't store the original pos
	void				RotateY(const Mth::Vector & world_origin, Mth::ERot90 rot_y, bool root = true);	// don't set root
	void				Scale(const Mth::Vector & world_origin, const Mth::Vector & delta_scale, bool root = true); // delta since we don't store the original scale

	void 				GetVerts(Mth::Vector *p_verts, bool root = true);		// don't set root on any of these
	void 				GetColors(uint32 *p_colors, bool root = true);
	void 				SetVerts(Mth::Vector *p_verts, bool root = true);
	void 				SetColors(uint32 *p_colors, bool root = true);
	#endif

	bool				IsLeaf();
	bool				IsActive();
	bool				IsObject();
	bool				IsTransformed();
	bool				IsSky();
	bool				IsColored();
	bool				IsSkeletal();
	bool				IsEnvMapped();
	bool				IsUVWibbled();
	bool				IsBillboard();

#ifdef	__NOPT_ASSERT__
	bool				WasRendered();
#else
	bool				WasRendered() {return false;};	
#endif
	

	void				SetLeaf(bool yes);
	void				SetActive(bool yes);
	void				SetObject(bool yes);
	void				SetTransformed(bool yes);
	void				SetSky(bool yes);
	void				SetZPush0(bool yes);
	void				SetZPush1(bool yes);
	void				SetZPush2(bool yes);
	void				SetZPush3(bool yes);
	void				SetNoShadow(bool yes);
	void				SetUVWibbled(bool yes);
	void				SetVCWibbled(bool yes);
	void				SetColored(bool yes);
	void				SetSkinned(bool yes);
	void				SetBillboard(bool yes);
	void				SetSkeletal(bool yes);
	void				SetEnvMapped(bool yes);
	void				SetBlackFog(bool yes);

	void				SetChecksum(uint32 checksum);
	void				SetBoundingSphere(float x, float y, float z, float R);	// change args to const Mth::Vector sphere
	void				SetBoundingBox(float x, float y, float z);				// change args to const Mth::Vector box
	void				SetDma(uint8 *pDma);
	void				SetDmaTag(uint8 *pDma);
	void				SetChild(CGeomNode *pChild);
	void				AddChild(CGeomNode *pChild);
	void				SetSibling(CGeomNode *pSibling);
	void				SetSlaveLOD(CGeomNode *pLOD);
	void				SetLODFarDist(float LOD_far_dist);
	void				SetGroup(sGroup *pGroup);
	void				SetMatrix(Mth::Matrix *p_mat);
	void				SetUVWibblePointer(float *pData);
	void				SetVCWibblePointer(uint32 *pData);
	void				SetColor(uint32 rgba);
	void				SetLightGroup(CLightGroup *p_light_group);
	void				SetVisibility(uint8 mask);
	void				SetBoneIndex(sint8 index);

	// Texture pointer starts off as a texture checksum in SceneConv
	void				SetTextureChecksum(uint32 checksum) {u4.mp_texture = (sTexture*)checksum;}
	uint32				GetTextureChecksum() {return (uint32)u4.mp_texture;}
	#ifdef __PLAT_NGPS__
	void				SetTexture(sTexture *p_texture) {u4.mp_texture = p_texture;}
	sTexture *			GetTexture() {return u4.mp_texture;}
	#endif

	void				SetUVWibbleParams(float u_vel, float u_amp, float u_freq, float u_phase,
										  float v_vel, float v_amp, float v_freq, float v_phase);
	void				UseExplicitUVWibble(bool yes);
	void				SetUVWibbleOffsets(float u_offset, float v_offset);

	void				SetUVOffset(uint32 material_name, int pass, float u_offset, float v_offset);
	void 				SetUVMatrix(uint32 material_name, int pass, Mth::Matrix &mat);
	
	uint32				GetChecksum();
	Mth::Matrix&		GetMatrix();
	CGeomNode*			GetChild();
	CGeomNode*			GetSibling();
	uint8*				GetDma();
	sGroup*				GetGroup();
	uint32				GetColor();
	float				GetLODFarDist();
	CLightGroup *		GetLightGroup();
	uint8				GetVisibility();
	Mth::Vector			GetBoundingSphere();
	Mth::CBBox			GetBoundingBox();
	sint8				GetBoneIndex();

	uint32				GetFlags(){return m_flags;}

	void				CountMetrics(CGeomMetrics *p_metrics);
	void				RenderWireframe(int mode);
	void				RenderWireframeRecurse(int mode, int &lines);
	
								   
	int					GetNumBones() { return m_num_bones; }
	int					GetNumVerts();
	int					GetNumPolys();
	int					GetNumBasePolys();

	// To get at the different objects in a heirarchy
	int					GetNumObjects();
	CGeomNode*			GetObject(int num, bool root = true);		// Don't set root


	#ifdef __PLAT_WN32__
	void 				Preprocess(uint8 *p_baseAddress);
	#endif

	#ifdef __PLAT_NGPS__
	static CGeomNode*	sProcessInPlace(uint8 *pPipData, uint32 loadFlags);
	static void *		sGetHierarchyArray(uint8 *pPipData, int& size);
	void				AddToTree(uint32 loadFlags);
	void				Cleanup();
	
	CGeomNode*			CreateInstance(Mth::Matrix *pMat, CGeomNode *p_parent = NULL);
	CGeomNode*			CreateInstance(Mth::Matrix *pMat, int numBones, Mth::Matrix *pBoneTransforms, CGeomNode *p_parent = NULL);
	void				DeleteInstance();
	CGeomNode*			CreateCopy(CGeomNode *p_parent = NULL, bool root = true);				// Don't set root
	void				DeleteCopy(bool root = true);											// Don't set root
	#endif
	
	bool				CullAgainstViewFrustum();
	bool				CullAgainstOuterFrustum();


	void				Init();

						CGeomNode();
						CGeomNode(const CGeomNode &node);
						~CGeomNode() {}


						static CGeomNode	sWorld;
						static CGeomNode	sDatabase;


private:

	#ifdef __PLAT_NGPS__
	bool				SphereIsOutsideViewVolume(Mth::Vector &s);
	bool				SphereIsInsideOuterVolume(Mth::Vector &s);
	bool				BoxIsOutsideViewVolume(Mth::Vector& b, Mth::Vector& s);
	bool				NeedsClipping(Mth::Vector &s);
	void				LinkDma(uint8 *p_newTail);
	int	 				ProcessNodeInPlace(uint8 *p_baseAddress);
	void 				BecomesChildOf(CGeomNode *p_parent);
	bool				RemoveFrom(CGeomNode *p_parent);
	bool				RemoveFromChildrenOf(CGeomNode *p_parent);
	void 				WibbleVCs();

	void 				recalculate_bounding_data(Mth::Vector *p_verts, int num_verts);
	#endif

	Mth::Vector			m_bounding_sphere;
	float				m_bounding_box[3];
	uint32				m_flags;
	union
	{
		CGeomNode *		mp_child;			// internal
		uint8 *			mp_dma;				// leaf
	} u1;
	union
	{
		Mth::Matrix *	mp_transform;		// internal
		uint32			m_dma_tag_lo32;		// leaf
	} u2;
	CGeomNode *			mp_sibling;
	union
	{
		sGroup *		mp_group;			// leaf
		CLightGroup *	mp_light_group;		// optional group of lights
	} u3;
	uint32				m_checksum;
	float *				mp_uv_wibble;		// leaf
	uint32 *			mp_vc_wibble;		// leaf
	uint32				m_colour;
	// used for skeletal models...
	sint8				m_num_bones;		// skeletal, internal, instance
	sint8				m_bone_index;		// skeletal, internal, source
	uint16				m_field;			// skeletal, internal, instance
	union 
	{
		Mth::Matrix *		mp_bone_transforms;	// skeletal, internal, instance
		sTexture	*		mp_texture;			// Level Geometry leaf- pointer to the texture we use
	} u4;
	float				m_LOD_far_dist;
	CGeomNode *			mp_next_LOD;
											// stay quadword-aligned with padding if necessary
};


// Hierarchy information available to game.  It is found at the beginning of a geom.ps2 file,
// although it is not used by the CGeomNodes.
class CHierarchyObject
{
public:
						CHierarchyObject();
						~CHierarchyObject();

	void				SetChecksum(uint32 checksum);
	uint32				GetChecksum() const;
	void				SetParentChecksum(uint32 checksum);
	uint32				GetParentChecksum() const;
	void				SetParentIndex(int16 index);
	int16				GetParentIndex() const;
	void				SetBoneIndex(sint8 index);
	sint8				GetBoneIndex() const;

	void				SetSetupMatrix(const Mth::Matrix& mat);
	const Mth::Matrix &	GetSetupMatrix() const;

	
protected:
	uint32				m_checksum;			// Object checksum
	uint32				m_parent_checksum;	// Checksum of parent, or 0 if root object
	int16				m_parent_index;		// Index of parent in the hierarchy array (or -1 if root object)
	sint8				m_bone_index;		// The index of the bone matrix used on this object
	uint8				m_pad_8;
	uint32				m_pad_32;
	Mth::Matrix			m_setup_matrix;		// Initial local to parent matrix
};



/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


//extern CGeomNode	*pShadowList[SHADOW_LIST_SIZE];
extern uint			NumShadowListEntries;

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace NxPs2

#endif	// __GFX_NGPS_NX_GEOMETRY_H



================================================
FILE: Code/Gfx/NGPS/NX/gif.cpp
================================================
#include 
#include 
#include "dma.h"
#include "gif.h"
#include "vif.h"
#include "vu1.h"

namespace NxPs2
{



/*	ͻ
	 GIFtag format 
	ͼ


	 31                                           16 15 14                                         0
	Ŀ
	                                               EOP                   NLOOP                    
	


	 63       60 59 58 57                            47 46 45                                     32
	Ŀ
	   NREG     FLG               PRIM              PRE                                         
	


	 95                                                       76 75       72 71       68 67       64
	Ŀ
	                                                     ...     [REG2]     [REG1]      REG0    
	


	 127                                                                                          96
	Ŀ
	                                                                                               
	



	ͻ
	 PATH1 GIFtag format 
	ͼ


	 31 30                   23 22                16 15 14                                         0
	Ŀ
	0      NREG exponent      << NREG            EOP                   NLOOP                    
	


	 63       60 59 58 57                            47 46 45    43 42                            32
	Ŀ
	   NREG     FLG               PRIM              PRE                      ADDR              
	


	 95                                                       76 75       72 71       68 67       64
	Ŀ
	                                                     ...     [REG2]     [REG1]      REG0    
	


	 127                                         112 111           106 105                        96
	Ŀ
	                                                     flags                  SIZE             
	


	flags:
	106 -
	107 -
	108 -
	109 -
	110 - no ITOP	}
	111 - no ITOP	} uses 2 flags because the parsing loop is quicker
	


*/



//-------------------------------------------------
//		G I F T A G   C O N S T R U C T I O N
//-------------------------------------------------


// PATH1


void gif::Tag1(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre, uint Eop, uint NLoop, uint Addr)
{
	uint32 size = NReg * NLoop;
	float NRegFloat  = (float)NReg * 1.1920928955e-07f;	// 2^-23
	dma::Store32(*(uint32 *)&NRegFloat | Eop<<15 | NLoop,
							NReg<<28 | Flg<<26 | Prim<<15 | Pre<<14 | Addr,
							Regs,
							size);
}


void gif::BeginTag1(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre, uint Addr)
{
	float NRegFloat = (float)NReg * 1.1920928955e-07f;	// 2^-23
	pTag = dma::pLoc;
	dma::Store32(*(uint32 *)&NRegFloat,
							NReg<<28 | Flg<<26 | Prim<<15 | Pre<<14 | Addr,
							Regs,
							0);
}


void gif::BeginTag1_extended(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre, uint Addr, uint Step)
{
	float StepFloat = (float)Step * 1.1920928955e-07f;	// 2^-23
	pTag = dma::pLoc;
	dma::Store32(*(uint32 *)&StepFloat,
					NReg<<28 | Flg<<26 | Prim<<15 | Pre<<14 | Addr,
					Regs,
					0);
}


void gif::EndTag1(uint Eop)
{
	uint32 size = vif::UnpackSize * vif::CycleLength;
	((uint32 *)pTag)[0] |= Eop<<15 | vif::UnpackSize;
	((uint32 *)pTag)[3] = size;
	vu1::Loc += vif::UnpackSize * vif::CycleLength + 1;
}


void gif::BeginTagImmediate(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre, uint Addr)
{
	float NRegFloat = (float)NReg * 1.1920928955e-07f;	// 2^-23
	pTag = dma::pLoc;
	dma::Store32(*(uint32 *)&NRegFloat,
							NReg<<28 | Flg<<26 | Prim<<15 | Pre<<14 | Addr,
							Regs,
							0);
}


void gif::EndTagImmediate(uint Eop)
{
	uint32 size = vif::UnpackSize;
	uint NREG = pTag[7]>>4;
	((uint32 *)pTag)[0] |= Eop<<15 | (vif::UnpackSize/NREG);
	((uint32 *)pTag)[3] = size;
	vu1::Loc += vif::UnpackSize+1;
}




// PATH2 or 3

void gif::Tag2(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre, uint Eop, uint NLoop)
{
	dma::Store32(	Eop<<15 | NLoop,									
					NReg<<28 | Flg<<26 | Prim<<15 | Pre<<14,
					Regs,
					0);
}


void gif::BeginTag2(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre)
{
	pTag = dma::pLoc;
	dma::Store32(	0,
					NReg<<28 | Flg<<26 | Prim<<15 | Pre<<14,
					Regs,
					0);
}


void gif::EndTag2(uint Eop)
{
	uint FLG  = pTag[7]>>2 & 3;
	uint NREG = pTag[7]>>4;
	uint NLOOP;
	if (FLG==IMAGE)
		NLOOP = (dma::pLoc - pTag - 16) / 16;
	else
		NLOOP = (dma::pLoc - pTag - 16) / (16*NREG);
	*(uint32 *)pTag |= Eop<<15 | NLOOP;
}


//--------------------------------
//		S T A T I C   D A T A
//--------------------------------

uint8 *gif::pTag;



} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/gif.h
================================================
#ifndef __GIF_H
#define __GIF_H


namespace NxPs2
{


// GIFtag FLG definitions
#define PACKED	0
#define REGLIST	1
#define IMAGE	2

class gif
{

public:

	//------------------------------------------
	//		S T A T I C   F U N C T I O N S
	//------------------------------------------

	static void Tag1(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre, uint Eop, uint NLoop, uint Addr);
	static void EndTag1(uint Eop);
	static void BeginTag1(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre, uint Addr);
	static void BeginTag1_extended(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre, uint Addr, uint Step);
	static void EndTagImmediate(uint Eop);
	static void BeginTagImmediate(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre, uint Addr);
	static void Tag2(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre, uint Eop, uint NLoop);
	static void EndTag2(uint Eop);
	static void BeginTag2(uint32 Regs, uint NReg, uint Flg, uint Prim, uint Pre);

	//--------------------------------
	//		S T A T I C   D A T A
	//--------------------------------

	static uint8 *pTag;

}; // class gif


} // namespace NxPs2


#endif // __GIF_H


================================================
FILE: Code/Gfx/NGPS/NX/group.cpp
================================================
#include 
#include "group.h"
	
/*

 (Notes by Mick)
 
 A Group is a set of textures, and a set of meshes or primitives that use those textures.
 
 Rendering is performed one group at a time. 
 
 The textures for a group are uploaded, and then it is rendered.
 
 Whilst a group is being rendered, the uploading of the next group's textures
 can start to be uploaded.
 
 This all happens asyncronously, whilst the CPU is doing the calculations for the next frame.
 
 The uploading/rendering cycle is syncronized by interrupts.
 
 The groups are rendered in this order:
 
 1) PrologueGroup - set up in render.cpp - flips the display buffers and initializes VU1 microcode 
 2) The groups linked by PrologueGroup->pNext, which represent groups of textures and polygons
 3) pEpilogue, (the last in this list) - uploads fonts and sprites, allowing them to be rendered next

 after the groups are rendered, we can render the 2D elements, namely the sprites and fonts (see render.cpp)

*/
				
				
namespace NxPs2
{

sGroup *sGroup::pHead;
sGroup *sGroup::pTail;
sGroup *sGroup::pShadow;
sGroup *sGroup::pFog;
sGroup *sGroup::pParticles;
sGroup *sGroup::pEpilogue;
volatile sGroup *sGroup::pUploadGroup;    // Group that is currently uploading
volatile sGroup *sGroup::pRenderGroup;	  // Group that is currently rendering
uint32 sGroup::VramBufferBase;

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/group.h
================================================
#ifndef __GROUP_H
#define __GROUP_H


namespace NxPs2
{


#define GROUPFLAG_TRANSPARENT	(1<<0)
#define GROUPFLAG_SKY			(1<<1)
#define GROUPFLAG_SORT			(1<<2)


class CVu1Context;
struct STransformContext;


struct sGroup
{
public:
	sGroup *pNext;

	float  Priority;
	uint32 Checksum;
	uint32 flags;
	uint8  *pUpload[2];
	uint8  *pRender[2];
	uint8  *pListEnd[2];
	bool	Used[2];			// flag saying if this group is actually used this frame (has something in the pRender data)
	struct sMesh  *pMeshes;
	int    NumMeshes;
	uint   vu1_loc;
	uint8  *p_tag;

	uint32 VramStart;
	uint32 VramEnd;

	struct sScene *pScene;

	sGroup *pPrev;

	static sGroup *pHead;
	static sGroup *pTail;
	static sGroup *pShadow;
	static sGroup *pFog;
	static sGroup *pParticles;
	static sGroup *pEpilogue;
	volatile static sGroup *pUploadGroup;
	volatile static sGroup *pRenderGroup;
	static uint32 VramBufferBase;
	//STransformContext *pTransformContext;
	CVu1Context *pVu1Context;
	uint32	profile_color;					// color we display it as in the profiler
};

} // namespace NxPs2

#endif // __GROUP_H


================================================
FILE: Code/Gfx/NGPS/NX/gs.cpp
================================================
#include 
#include "vu1code.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gif.h"
#include "gs.h"


namespace NxPs2
{


//--------------------------------------------------
//		G S   P R I M   C O N S T R U C T I O N
//--------------------------------------------------


// begin
void gs::BeginPrim(uint FLG, uint Prim, uint Pre)
{
	vif::STCYCL(1,1);
	vif::UNPACK(0, V4_32, 1, FLG, UNSIGNED, 0);
	gif::BeginTag1(gs::A_D, 1, PACKED, Prim, Pre, VU1_ADDR(GSPrim));
	vif::BeginUNPACK(0, V3_32, FLG, UNSIGNED, 1);
}


// end
void gs::EndPrim(uint Eop)
{
	vif::EndUNPACK();
	gif::EndTag1(Eop);
}




// output 1 GS register DIRECT via PATH2, V4_32 format
void gs::Reg2(eReg Reg, uint64 Data)
{
	dma::Store64(Data);
	dma::Store64((uint64)Reg);
}


// version returning address of data
uint32 *gs::Reg2_pData(eReg Reg, uint64 Data)
{
	uint32 *p_ret = (uint32 *) dma::pLoc;

	dma::Store64(Data);
	dma::Store64((uint64)Reg);

	return p_ret;
}


// Modify the TBPs of GS registers
void gs::ModifyTBP(eReg Reg, uint32 *p_reg_ptr, sint16 tbp1, sint16 tbp2, sint16 tbp3)
{
	switch (Reg)
	{
	case BITBLTBUF:		// only modifies DBP
		*(++p_reg_ptr) &= ~(M14);				// DBP
		*(  p_reg_ptr) |= (tbp1 & M14);
		break;

	case TEX0_1:
	case TEX0_2:
		*(  p_reg_ptr) &= ~(M14);				// TBP
		*(  p_reg_ptr) |= (tbp1 & M14);
		if (tbp2 >= 0)
		{
			*(++p_reg_ptr) &= ~(M14 << 5);		// CBP
			*(  p_reg_ptr) |= (tbp2 & M14) << 5;
		}
		break;

	case TEX2_1:
	case TEX2_2:
		*(++p_reg_ptr) &= ~(M14 << 5);			// CBP
		*(  p_reg_ptr) |= (tbp1 & M14) << 5;
		break;

	case MIPTBP1_1:
	case MIPTBP1_2:
	case MIPTBP2_1:
	case MIPTBP2_2:
	default:
		#ifdef __PLAT_NGPS__
		Dbg_MsgAssert(0, ("gs::ModifyTBP(): Can't modify register %x", Reg));
		#endif
		break;
	}
}

} // namespace NxPs2





================================================
FILE: Code/Gfx/NGPS/NX/gs.h
================================================
#ifndef __GS_H
#define __GS_H

#include "dma.h"

//---------------------------------------------------
//		R E G I S T E R   D E F I N I T I O N S
//---------------------------------------------------



// mask definitions for packing macros
#define M01	0x0000000000000001
#define M02	0x0000000000000003
#define M03	0x0000000000000007
#define M04	0x000000000000000F
#define M05	0x000000000000001F
#define M06	0x000000000000003F
#define M08	0x00000000000000FF
#define M09	0x00000000000001FF
#define M10	0x00000000000003FF
#define M11	0x00000000000007FF
#define M12	0x0000000000000FFF
#define M14	0x0000000000003FFF
#define M16	0x000000000000FFFF
#define M24	0x0000000000FFFFFF
#define M32	0x00000000FFFFFFFF
#define M64	0xFFFFFFFFFFFFFFFF


//----------------------------------------------------------------------------------------
//		G E N E R A L   P U R P O S E   R E G I S T E R   P A C K I N G   M A C R O S
//----------------------------------------------------------------------------------------

#define PackALPHA(A,B,C,D,FIX)				\
(											\
	( (uint64) (A)		& M02 )	<< 0	|	\
	( (uint64) (B)		& M02 )	<< 2	|	\
	( (uint64) (C)		& M02 )	<< 4	|	\
	( (uint64) (D)		& M02 )	<< 6	|	\
	( (uint64) (FIX)	& M08 )	<< 32		\
)

#define PackBITBLTBUF(SBP,SBW,SPSM,DBP,DBW,DPSM)\
(											\
	( (uint64) (SBP)	& M14 )	<< 0	|	\
	( (uint64) (SBW)	& M06 )	<< 16	|	\
	( (uint64) (SPSM)	& M06 )	<< 24	|	\
	( (uint64) (DBP)	& M14 )	<< 32	|	\
	( (uint64) (DBW)	& M06 )	<< 48	|	\
	( (uint64) (DPSM)	& M06 )	<< 56		\
)

#define PackCLAMP(WMS,WMT,MINU,MAXU,MINV,MAXV)\
(											\
	( (uint64) (WMS)	& M02 )	<< 0	|	\
	( (uint64) (WMT)	& M02 )	<< 2	|	\
	( (uint64) (MINU)	& M10 )	<< 4	|	\
	( (uint64) (MAXU)	& M10 )	<< 14	|	\
	( (uint64) (MINV)	& M10 )	<< 24	|	\
	( (uint64) (MAXV)	& M10 )	<< 34		\
)

#define PackCOLCLAMP(CLAMP)					\
(											\
	( (uint64) (CLAMP)	& M01 )	<< 0		\
)

#define PackDIMX(DM00,DM01,DM02,DM03,DM10,DM11,DM12,DM13,DM20,DM21,DM22,DM23,DM30,DM31,DM32,DM33)\
(											\
	( (uint64) (DM00)	& M03 )	<< 0	|	\
	( (uint64) (DM01)	& M03 )	<< 4	|	\
	( (uint64) (DM02)	& M03 )	<< 8	|	\
	( (uint64) (DM03)	& M03 )	<< 12	|	\
	( (uint64) (DM10)	& M03 )	<< 16	|	\
	( (uint64) (DM11)	& M03 )	<< 20	|	\
	( (uint64) (DM12)	& M03 )	<< 24	|	\
	( (uint64) (DM13)	& M03 )	<< 28	|	\
	( (uint64) (DM20)	& M03 )	<< 32	|	\
	( (uint64) (DM21)	& M03 )	<< 36	|	\
	( (uint64) (DM22)	& M03 )	<< 40	|	\
	( (uint64) (DM23)	& M03 )	<< 44	|	\
	( (uint64) (DM30)	& M03 )	<< 48	|	\
	( (uint64) (DM31)	& M03 )	<< 52	|	\
	( (uint64) (DM32)	& M03 )	<< 56	|	\
	( (uint64) (DM33)	& M03 )	<< 60		\
)

#define PackDTHE(DTHE)						\
(											\
	( (uint64) (DTHE)	& M01 )	<< 0		\
)

#define PackFBA(FBA)						\
(											\
	( (uint64) (FBA)	& M01 )	<< 0		\
)

#define PackFOG(F)							\
(											\
	( (uint64) (F)		& M08 )	<< 56		\
)

#define PackFOGCOL(FCR,FCG,FCB)				\
(											\
	( (uint64) (FCR)	& M08 )	<< 0	|	\
	( (uint64) (FCG)	& M08 )	<< 8	|	\
	( (uint64) (FCB)	& M08 )	<< 16		\
)

#define PackFRAME(FBP,FBW,PSM,FBMSK)		\
(											\
	( (uint64) (FBP)	& M09 )	<< 0	|	\
	( (uint64) (FBW)	& M06 )	<< 16	|	\
	( (uint64) (PSM)	& M06 )	<< 24	|	\
	( (uint64) (FBMSK)	& M32 )	<< 32		\
)

#define PackHWREG(DATA)						\
(											\
	( (uint64) (DATA)	& M64 )	<< 0		\
)

#define PackLABEL(ID,IDMSK)					\
(											\
	( (uint64) (ID)		& M32 )	<< 0	|	\
	( (uint64) (IDMSK)	& M32 )	<< 32		\
)

#define PackMIPTBP1(TBP1,TBW1,TBP2,TBW2,TBP3,TBW3)\
(											\
	( (uint64) (TBP1)	& M14 )	<< 0	|	\
	( (uint64) (TBW1)	& M06 )	<< 14	|	\
	( (uint64) (TBP2)	& M14 )	<< 20	|	\
	( (uint64) (TBW2)	& M06 )	<< 34	|	\
	( (uint64) (TBP3)	& M14 )	<< 40	|	\
	( (uint64) (TBW3)	& M06 )	<< 54		\
)

#define PackMIPTBP2(TBP4,TBW4,TBP5,TBW5,TBP6,TBW6)\
(											\
	( (uint64) (TBP4)	& M14 )	<< 0	|	\
	( (uint64) (TBW4)	& M06 )	<< 14	|	\
	( (uint64) (TBP5)	& M14 )	<< 20	|	\
	( (uint64) (TBW5)	& M06 )	<< 34	|	\
	( (uint64) (TBP6)	& M14 )	<< 40	|	\
	( (uint64) (TBW6)	& M06 )	<< 54		\
)

#define PackPABE(PABE)						\
(											\
	( (uint64) (PABE)	& M01 )	<< 0		\
)

#define PackPRIM(PRIM,IIP,TME,FGE,ABE,AA1,FST,CTXT,FIX)\
(											\
	( (uint64) (PRIM)	& M03 )	<< 0	|	\
	( (uint64) (IIP)	& M01 )	<< 3	|	\
	( (uint64) (TME)	& M01 )	<< 4	|	\
	( (uint64) (FGE)	& M01 )	<< 5	|	\
	( (uint64) (ABE)	& M01 )	<< 6	|	\
	( (uint64) (AA1)	& M01 )	<< 7	|	\
	( (uint64) (FST)	& M01 )	<< 8	|	\
	( (uint64) (CTXT)	& M01 )	<< 9	|	\
	( (uint64) (FIX)	& M01 )	<< 10		\
)

#define PackPRMODE(IIP,TME,FGE,ABE,AA1,FST,CTXT,FIX)\
(											\
	( (uint64) (IIP)	& M01 )	<< 3	|	\
	( (uint64) (TME)	& M01 )	<< 4	|	\
	( (uint64) (FGE)	& M01 )	<< 5	|	\
	( (uint64) (ABE)	& M01 )	<< 6	|	\
	( (uint64) (AA1)	& M01 )	<< 7	|	\
	( (uint64) (FST)	& M01 )	<< 8	|	\
	( (uint64) (CTXT)	& M01 )	<< 9	|	\
	( (uint64) (FIX)	& M01 )	<< 10		\
)

#define PackPRMODECONT(AC)					\
(											\
	( (uint64) (AC)		& M01 )	<< 0		\
)

#define PackRGBAQ(R,G,B,A,Q)				\
(											\
	( (uint64) (R)		& M08 )	<< 0	|	\
	( (uint64) (G)		& M08 )	<< 8	|	\
	( (uint64) (B)		& M08 )	<< 16	|	\
	( (uint64) (A)		& M08 )	<< 24	|	\
	( (uint64) (Q)		& M32 )	<< 32		\
)

#define PackSCANMSK(MSK)					\
(											\
	( (uint64) (MSK)	& M01 )	<< 0		\
)

#define PackSCISSOR(SCAX0,SCAX1,SCAY0,SCAY1)\
(											\
	( (uint64) (SCAX0)	& M11 )	<< 0	|	\
	( (uint64) (SCAX1)	& M11 )	<< 16	|	\
	( (uint64) (SCAY0)	& M11 )	<< 32	|	\
	( (uint64) (SCAY1)	& M11 )	<< 48		\
)

#define PackSIGNAL(ID,IDMSK)				\
(											\
	( (uint64) (ID)		& M32 )	<< 0	|	\
	( (uint64) (IDMSK)	& M32 )	<< 32		\
)

#define PackST(S,T)							\
(											\
	( (uint64) (S)		& M32 )	<< 0	|	\
	( (uint64) (T)		& M32 )	<< 32		\
)

#define PackTEST(ATE,ATST,AREF,AFAIL,DATE,DATM,ZTE,ZTST)\
(											\
	( (uint64) (ATE)	& M01 )	<< 0	|	\
	( (uint64) (ATST)	& M03 )	<< 1	|	\
	( (uint64) (AREF)	& M08 )	<< 4	|	\
	( (uint64) (AFAIL)	& M02 )	<< 12	|	\
	( (uint64) (DATE)	& M01 )	<< 14	|	\
	( (uint64) (DATM)	& M01 )	<< 15	|	\
	( (uint64) (ZTE)	& M01 )	<< 16	|	\
	( (uint64) (ZTST)	& M02 )	<< 17		\
)

#define PackTEX0(TBP0,TBW,PSM,TW,TH,TCC,TFX,CBP,CPSM,CSM,CSA,CLD)\
(											\
	( (uint64) (TBP0)	& M14 )	<< 0	|	\
	( (uint64) (TBW)	& M06 )	<< 14	|	\
	( (uint64) (PSM)	& M06 )	<< 20	|	\
	( (uint64) (TW)		& M04 )	<< 26	|	\
	( (uint64) (TH)		& M04 )	<< 30	|	\
	( (uint64) (TCC)	& M01 )	<< 34	|	\
	( (uint64) (TFX)	& M02 )	<< 35	|	\
	( (uint64) (CBP)	& M14 )	<< 37	|	\
	( (uint64) (CPSM)	& M04 )	<< 51	|	\
	( (uint64) (CSM)	& M01 )	<< 55	|	\
	( (uint64) (CSA)	& M05 )	<< 56	|	\
	( (uint64) (CLD)	& M03 )	<< 61		\
)

#define PackTEX1(LCM,MXL,MMAG,MMIN,MTBA,L,K)\
(											\
	( (uint64) (LCM)	& M01 )	<< 0	|	\
	( (uint64) (MXL)	& M03 )	<< 2	|	\
	( (uint64) (MMAG)	& M01 )	<< 5	|	\
	( (uint64) (MMIN)	& M03 )	<< 6	|	\
	( (uint64) (MTBA)	& M01 )	<< 9	|	\
	( (uint64) (L)		& M02 )	<< 19	|	\
	( (uint64) (K)		& M12 )	<< 32		\
)

#define PackTEX2(PSM,CBP,CPSM,CSM,CSA,CLD)	\
(											\
	( (uint64) (PSM)	& M06 )	<< 20	|	\
	( (uint64) (CBP)	& M14 )	<< 37	|	\
	( (uint64) (CPSM)	& M04 )	<< 51	|	\
	( (uint64) (CSM)	& M01 )	<< 55	|	\
	( (uint64) (CSA)	& M05 )	<< 56	|	\
	( (uint64) (CLD)	& M03 )	<< 61		\
)

#define PackTEXFLUSH(VAL)					\
(											\
	( (uint64) (VAL)	& M64 )	<< 0		\
)

#define PackTEXA(TA0,AEM,TA1)				\
(											\
	( (uint64) (TA0)	& M08 )	<< 0	|	\
	( (uint64) (AEM)	& M01 )	<< 15	|	\
	( (uint64) (TA1)	& M08 )	<< 32		\
)

#define PackTEXCLUT(CBW,COU,COV)			\
(											\
	( (uint64) (CBW)	& M06 )	<< 0	|	\
	( (uint64) (COU)	& M06 )	<< 6	|	\
	( (uint64) (COV)	& M10 )	<< 12		\
)

#define PackTRXDIR(XDIR)					\
(											\
	( (uint64) (XDIR)	& M02 )	<< 0		\
)

#define PackTRXPOS(SSAX,SSAY,DSAX,DSAY,DIR)	\
(											\
	( (uint64) (SSAX)	& M11 )	<< 0	|	\
	( (uint64) (SSAY)	& M11 )	<< 16	|	\
	( (uint64) (DSAX)	& M11 )	<< 32	|	\
	( (uint64) (DSAY)	& M11 )	<< 48	|	\
	( (uint64) (DIR)	& M02 )	<< 59		\
)

#define PackTRXREG(RRW,RRH)					\
(											\
	( (uint64) (RRW)	& M12 )	<< 0	|	\
	( (uint64) (RRH)	& M12 )	<< 32		\
)

#define PackUV(U,V)							\
(											\
	( (uint64) (U)		& M14 )	<< 0	|	\
	( (uint64) (V)		& M14 )	<< 16		\
)

#define PackXYOFFSET(OFX,OFY)				\
(											\
	( (uint64) (OFX)	& M16 )	<< 0	|	\
	( (uint64) (OFY)	& M16 )	<< 32		\
)

#define PackXYZ(X,Y,Z)						\
(											\
	( (uint64) (X)		& M16 )	<< 0	|	\
	( (uint64) (Y)		& M16 )	<< 16	|	\
	( (uint64) (Z)		& M32 )	<< 32		\
)

#define PackXYZF(X,Y,Z,F)					\
(											\
	( (uint64) (X)		& M16 )	<< 0	|	\
	( (uint64) (Y)		& M16 )	<< 16	|	\
	( (uint64) (Z)		& M24 )	<< 32	|	\
	( (uint64) (F)		& M08 )	<< 56		\
)

#define PackZBUF(ZBP,PSM,ZMSK)				\
(											\
	( (uint64) (ZBP)	& M09 )	<< 0	|	\
	( (uint64) (PSM)	& M04 )	<< 24	|	\
	( (uint64) (ZMSK)	& M01 )	<< 32		\
)


//----------------------------------------------------------------------------------------
//		P R I V I L E G E D   R E G I S T E R   P A C K I N G   M A C R O S
//----------------------------------------------------------------------------------------


#define PackBGCOLOR(R,G,B)					\
(											\
	( (uint64) (R)		& M08 )	<< 0	|	\
	( (uint64) (G)		& M08 )	<< 8	|	\
	( (uint64) (B)		& M08 )	<< 16		\
)

#define PackBUSDIR(DIR)						\
(											\
	( (uint64) (DIR)	& M01 )	<< 0		\
)

#define PackCSR(SIGNAL,FINISH,HSINT,VSINT,FLUSH,RESET,NFIELD,FIELD,FIFO,REV,ID)\
(											\
	( (uint64) (SIGNAL)	& M01 )	<< 0	|	\
	( (uint64) (FINISH)	& M01 )	<< 1	|	\
	( (uint64) (HSINT)	& M01 )	<< 2	|	\
	( (uint64) (VSINT)	& M01 )	<< 3	|	\
	( (uint64) (FLUSH)	& M01 )	<< 8	|	\
	( (uint64) (RESET)	& M01 )	<< 9	|	\
	( (uint64) (NFIELD)	& M01 )	<< 12	|	\
	( (uint64) (FIELD)	& M01 )	<< 13	|	\
	( (uint64) (FIFO)	& M02 )	<< 14	|	\
	( (uint64) (REV)	& M08 )	<< 16	|	\
	( (uint64) (ID)		& M08 )	<< 24		\
)

#define PackDISPFB(FBP,FBW,PSM,DBX,DBY)		\
(											\
	( (uint64) (FBP)	& M09 )	<< 0	|	\
	( (uint64) (FBW)	& M06 )	<< 9	|	\
	( (uint64) (PSM)	& M05 )	<< 15	|	\
	( (uint64) (DBX)	& M11 )	<< 32	|	\
	( (uint64) (DBY)	& M11 )	<< 43		\
)

#define PackDISPLAY(DX,DY,MAGH,MAGV,DW,DH)	\
(											\
	( (uint64) (DX)		& M12 )	<< 0	|	\
	( (uint64) (DY)		& M11 )	<< 12	|	\
	( (uint64) (MAGH)	& M04 )	<< 23	|	\
	( (uint64) (MAGV)	& M02 )	<< 27	|	\
	( (uint64) (DW)		& M12 )	<< 32	|	\
	( (uint64) (DH)		& M11 )	<< 44		\
)

#define PackEXTBUF(EXBP,EXBW,FBIN,WFFMD,EMODA,EMODC,WDX,WDY)\
(											\
	( (uint64) (EXBP)	& M14 )	<< 0	|	\
	( (uint64) (EXBW)	& M06 )	<< 14	|	\
	( (uint64) (FBIN)	& M02 )	<< 20	|	\
	( (uint64) (WFFMD)	& M01 )	<< 22	|	\
	( (uint64) (EMODA)	& M02 )	<< 23	|	\
	( (uint64) (EMODC)	& M02 )	<< 25	|	\
	( (uint64) (WDX)	& M11 )	<< 32	|	\
	( (uint64) (WDY)	& M11 )	<< 43		\
)

#define PackEXTDATA(SX,SY,SMPH,SMPV,WW,WH)	\
(											\
	( (uint64) (SX)		& M12 )	<< 0	|	\
	( (uint64) (SY)		& M11 )	<< 12	|	\
	( (uint64) (SMPH)	& M04 )	<< 23	|	\
	( (uint64) (SMPV)	& M02 )	<< 27	|	\
	( (uint64) (WW)		& M12 )	<< 32	|	\
	( (uint64) (WH)		& M11 )	<< 44		\
)

#define PackEXTWRITE(WRITE)					\
(											\
	( (uint64) (WRITE)	& M01 )	<< 0		\
)

#define PackIMR(SIGMSK,FINISHMSK,HSMSK,VSMSK)\
(											\
	( (uint64) (SIGMSK)	& M01 )	<< 8	|	\
	( (uint64) (FINISHMSK)& M01)<< 9	|	\
	( (uint64) (HSMSK)	& M01 )	<< 10	|	\
	( (uint64) (VSMSK)	& M01 )	<< 11	|	\
	( (uint64)            M04 ) << 12		\
)

#define PackPMODE(EN1,EN2,CRTMD,MMOD,AMOD,SLBG,ALP)\
(											\
	( (uint64) (EN1)	& M01 )	<< 0	|	\
	( (uint64) (EN2)	& M01 )	<< 1	|	\
	( (uint64) (CRTMOD)	& M03 )	<< 2	|	\
	( (uint64) (MMOD)	& M01 )	<< 5	|	\
	( (uint64) (AMOD)	& M01 )	<< 6	|	\
	( (uint64) (SLBG)	& M01 )	<< 7	|	\
	( (uint64) (ALP)	& M08 )	<< 8		\
)

#define PackSIGLBLID(SIGID,LBLID)			\
(											\
	( (uint64) (SIGID)	& M32 )	<< 0	|	\
	( (uint64) (LBLID)	& M32 )	<< 32		\
)

#define PackSMODE2(INT,FFMD,DPMS)			\
(											\
	( (uint64) (INT)	& M01 )	<< 0	|	\
	( (uint64) (FFMD)	& M01 )	<< 1	|	\
	( (uint64) (DPMS)	& M02 )	<< 2		\
)




//------------------------------------------------------
//		R E G I S T E R   F I E L D   V A L U E S
//------------------------------------------------------


// drawing primitives
#define	POINT		0
#define	LINE		1
#define	LINESTRIP	2
#define	TRIANGLE	3
#define	TRISTRIP	4
#define	TRIFAN		5
#define	SPRITE		6

// PRIM bit masks   (GS Manual p. 116)
#define	IIP			(1<<3)		// Gouraud Shading
#define	TME			(1<<4)		// Texture Mapping
#define	FGE			(1<<5)		// Fogging
#define	ABE			(1<<6)		// Alpha Blending
#define	AA1			(1<<7)		// Pass 1 Antialiasing
#define	FST			(1<<8)		// UV values used (UV Register)
#define	CTXT		(1<<9)		// Context 2 used
#define	FIX			(1<<10)		// Fixed Fragment Value Control

// pixel storage modes
#define PSMCT32		0x00
#define PSMCT24		0x01
#define PSMCT16		0x02
#define PSMCT16S	0x0A
#define PS_GPU24	0x12
#define PSMT8		0x13
#define PSMT4		0x14
#define PSMT8H		0x1B
#define PSMT4HL		0x24
#define PSMT4HH		0x2C
#define PSMZ32		0x30
#define PSMZ24		0x31
#define PSMZ16		0x32
#define PSMZ16S		0x3A

// depth test methods
#define ZNEVER		0
#define ZALWAYS		1
#define ZGEQUAL		2
#define ZGREATER	3

// alpha test methods
#define	ANEVER		0
#define	AALWAYS		1
#define ALESS		2
#define ALEQUAL		3
#define	AEQUAL		4
#define	AGEQUAL		5
#define	AGREATER	6
#define	ANOTEQUAL	7

// alpha fail processing
#define	KEEP		0
#define	FB_ONLY		1
#define	ZB_ONLY		2
#define	RGB_ONLY	3

// texture functions
#define MODULATE	0
#define DECAL		1
#define HIGHLIGHT	2
#define HIGHLIGHT2	3

// texture filters
#define NEAREST					0
#define LINEAR					1
#define NEAREST_MIPMAP_NEAREST	2
#define NEAREST_MIPMAP_LINEAR	3
#define LINEAR_MIPMAP_NEAREST	4
#define LINEAR_MIPMAP_LINEAR	5

// texture wrap modes
#define REPEAT			0
#define CLAMP			1
#define REGION_CLAMP	2
#define REGION_REPEAT	3

namespace NxPs2
{

class gs
{

public:

	enum eReg
	{
		PRIM		= 0x00,		// GS p. 116  Drawing Primitive Setting (AC == 1)
		RGBAQ		= 0x01,		// GS p. 119  Vertex Color Setting
		ST			= 0x02,		// GS p. 123  Specification of Vertex Texture Coordinates (sets S and T)  FST == 0
		UV			= 0x03,		// GS p. 135  Specification of Vertex Texture Coordinates (sets U and V)  FST == 1
		XYZF2		= 0x04,		// GS p. 139  Setting for Vertex Coordinate Values (Fog coefficient)
		XYZ2		= 0x05,		// GS p. 137  Setting for Vertex Coordinate Values
		TEX0_1		= 0x06,		// GS p. 125  Texture Information Setting (type of textures to be used)
		TEX0_2		= 0x07,		//            (mode, dimensions, blendfunc, CLUT)
		CLAMP_1		= 0x08,		// GS p. 102  Texture wrap mode
		CLAMP_2		= 0x09,
		FOG			= 0x0A,		// GS p. 108  Vertex Fog Value Setting (per vert)
		XYZF3		= 0x0C,		// GS p. 140  Setting for Vertex Coordinate Values (without Drawing Kick) (Fog coefficient)
		XYZ3		= 0x0D,		// GS p. 138  Setting for Vertex Coordinate Values (without Drawing Kick)
		A_D			= 0x0E,		// Undocumented???
		NOP			= 0x0F,
		TEX1_1		= 0x14,		// GS p. 127  Texture Information Setting (LOD info)
		TEX1_2		= 0x15,
		TEX2_1		= 0x16,		// GS p. 128  Texture Information Setting (Format/CLUT)
		TEX2_2		= 0x17,
		XYOFFSET_1	= 0x18,		// GS p. 136  Offset Value setting (prim to window coords)
		XYOFFSET_2	= 0x19,
		PRMODECONT	= 0x1A,		// GS p. 118  Specification of Primitive Attribute Setting Method
		PRMODE		= 0x1B,		// GS p. 117  Setting for Attributes of Drawing Primitives (AC == 0)
		TEXCLUT		= 0x1C,		// GS p. 130  CLUT Position Specification (CLUT offset in buffer)
		SCANMSK		= 0x22,		// GS p. 120  Raster Address Mask Setting
		MIPTBP1_1	= 0x34,		// GS p. 113  MIPMAP Information Setting (Level 1 to 3)
		MIPTBP1_2	= 0x35,
		MIPTBP2_1	= 0x36,		// GS p. 114  MIPMAP Information Setting (Level 4 to 6)
		MIPTBP2_2	= 0x37,
		TEXA		= 0x3B,		// GS p. 129  Texture Alpha Value Setting (sets alpha when alpha not 8-bit)
		FOGCOL		= 0x3D,		// GS p. 109  Distant Fog Color Setting
		TEXFLUSH	= 0x3F,		// GS p. 131  Texture Page Buffer Disabling (wait for drawing to complete)
		SCISSOR_1	= 0x40,		// GS p. 121  Setting for Scissoring Area
		SCISSOR_2	= 0x41,
		ALPHA_1		= 0x42,		// GS p. 100  Alpha Blending Setting (Context 1)
		ALPHA_2		= 0x43,
		DIMX		= 0x44,		// GS p. 104  Dither Matrix Setting
		DTHE		= 0x45,		// GS p. 105  Dither Control (0 off / 1 on)
		COLCLAMP	= 0x46,		// GS p. 103  Color Clamp Control  (0 mask / 1 clamp)
		TEST_1		= 0x47,		// GS p. 124  Pixel Test Control (Alpha test/Depth Test method and enable/disable, etc.)
		TEST_2		= 0x48,
		PABE		= 0x49,		// GS p. 115  Alpha Blending Control in Units of Pixels
		FBA_1		= 0x4A,		// GS p. 106  Alpha Correction Value  (Sets fixed alpha value)
		FBA_2		= 0x4B,
		FRAME_1		= 0x4C,		// GS p. 110  Frame Buffer Setting
		FRAME_2		= 0x4D,
		ZBUF_1		= 0x4E,		// GS p. 141  Z Buffer Setting
		ZBUF_2		= 0x4F,
		BITBLTBUF	= 0x50,		// GS p. 101  Setting for transmission between buffers
		TRXPOS		= 0x51,		// GS p. 133  Specification of Transmission Areas in Buffers (direction)
		TRXREG		= 0x52,		// GS p. 134  Specification of Transmission Areas in Buffers
		TRXDIR		= 0x53,		// GS p. 132  Activation of Transmission between Buffers
		HWREG		= 0x54,		// GS p. 111  Data Port for Transmission between Buffers
		SIGNAL		= 0x60,		// GS p. 122  SIGNAL Event Occurrence Request
		FINISH		= 0x61,		// GS p. 107  Finish Event Occurance Request
		LABEL		= 0x62,		// GS p. 112  LABEL Event Occurrence Request
		A_D_NOP		= 0x7F		// Undocumented???
	};

	static void BeginPrim(uint FLG, uint Prim, uint Pre);
	static void EndPrim(uint Eop);
	static void Reg1(eReg Reg, uint64 Data);
	static void Reg2(eReg Reg, uint64 Data);
	static uint32 *gs::Reg2_pData(eReg Reg, uint64 Data);
	static void ModifyTBP(eReg Reg, uint32 *p_reg_ptr, sint16 tbp1, sint16 tbp2 = -1, sint16 tbp3 = -1);

}; // class gs



// output 1 GS register via PATH1, V3_32 format
inline void gs::Reg1(eReg Reg, uint64 Data)
{
//	dma::Store64(Data);
//	dma::Store32((uint64)Reg);
	uint32 *p_loc = (uint32*)dma::pLoc;
	p_loc[0] = (uint32)Data;
	p_loc[1] = Data>>32;
	p_loc[2] = Reg;
	dma::pLoc = (uint8*) (p_loc + 3);

}


} // namespace NxPs2





#endif // __GS_H



================================================
FILE: Code/Gfx/NGPS/NX/immediate.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       immediate.cpp
//* OWNER:          Garrett Jost
//* CREATION DATE:  7/19/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include "gfx/ngps/nx/vu1code.h"
#include "gfx/ngps/nx/mesh.h"
#include "gfx/ngps/nx/sprite.h"
#include "gfx/ngps/nx/switches.h"

#include "gfx/ngps/nx/immediate.h"

namespace NxPs2
{

uint64 CImmediateMode::sGetTextureBlend( uint32 blend_checksum, int fix )
{
	uint64 rv = PackALPHA(0,0,0,0,0);
	switch ( blend_checksum )
	{
		case 0x54628ed7:		// Blend
			rv = PackALPHA(0,1,0,1,0);
			break;
		case 0x02e58c18:		// Add
			rv = PackALPHA(0,2,0,1,0);
			break;
		case 0xa7fd7d23:		// Sub
		case 0xdea7e576:		// Subtract
			rv = PackALPHA(2,0,0,1,0);
			break;
		case 0x40f44b8a:		// Modulate
			rv = PackALPHA(1,2,0,2,0);
			break;
		case 0x68e77f40:		// Brighten
			rv = PackALPHA(1,2,0,1,0);
			break;
		case 0x18b98905:		// FixBlend
			rv = PackALPHA(0,1,2,1,fix);
			break;
		case 0xa86285a1:		// FixAdd
			rv = PackALPHA(0,2,2,1,fix);
			break;
		case 0x0d7a749a:		// FixSub
		case 0x0eea99ff:		// FixSubtract
			rv = PackALPHA(2,0,2,1,fix);
			break;
		case 0x90b93703:		// FixModulate
			rv = PackALPHA(1,2,2,2,fix);
			break;
		case 0xb8aa03c9:		// FixBrighten
			rv = PackALPHA(1,2,2,1,fix);
			break;
		case 0x515e298e:		// Diffuse
		case 0x806fff30:		// None
			rv = PackALPHA(0,0,0,0,0);
			break;
		default:
			Dbg_MsgAssert(0,("Illegal blend mode specified. Please use (fix)blend/add/sub/modulate/brighten or diffuse/none."));
			break;
	}
	return rv;
}

void CImmediateMode::sViewportInit()
{
	// start a cnt tag
	dma::BeginTag(dma::cnt, 0);
	vif::STROW((int)render::RowRegI[0], (int)render::RowRegI[1], (int)render::RowRegI[2], (int)render::RowRegI[3]);

	// VU context, uploading the view transform data
	vu1::BeginPrim(ABS, VU1_ADDR(L_VF09));	  				// Begin packed register upload to VF10 
	vu1::StoreVec(*(Vec *)&render::AltFrustum);				// VF09
	vu1::StoreVec(*(Vec *)&render::InverseViewportScale);	// VF10
	vu1::StoreVec(*(Vec *)&render::InverseViewportOffset);	// VF11
//	vu1::StoreMat(*(Mat *)&render::AdjustedWorldToFrustum);	// VF12-15
	vu1::StoreMat(*(Mat *)&render::AdjustedWorldToViewport);// VF12-15
	vu1::EndPrim(0);										// End upload

	// GS context
	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::CLAMP_1,		PackCLAMP(CLAMP,CLAMP,0,0,0,0));		// may want to make REPEAT an option
	gs::Reg1(gs::FRAME_1,		PackFRAME(FRAME_START,HRES/64,PSMCT32,0xFF000000));
	gs::Reg1(gs::SCISSOR_1,		render::reg_SCISSOR);
	gs::Reg1(gs::XYOFFSET_1,	render::reg_XYOFFSET);
	gs::Reg1(gs::TEST_1, 		PackTEST(0,0,0,0,0,0,1,ZGEQUAL));
	gs::Reg1(gs::COLCLAMP,		PackCOLCLAMP(1));
	gs::Reg1(gs::ZBUF_1,		PackZBUF(ZBUFFER_START,PSMZ24,1));
	gs::EndPrim(0);

	// end the dma tag
	dma::EndTag();
}

void CImmediateMode::sTextureGroupInit(uint unpackFLG)
{
	vif::STROW((int)render::RowRegI[0], (int)render::RowRegI[1], (int)render::RowRegI[2], (int)render::RowRegI[3]);

	// VU context, uploading the view transform data
	vu1::BeginPrim(unpackFLG, VU1_ADDR(L_VF10));	  				// Begin packed register upload to VF10 
	vu1::StoreVec(*(Vec *)&render::InverseViewportScale);	// VF10
	vu1::StoreVec(*(Vec *)&render::InverseViewportOffset);	// VF11
//	vu1::StoreMat(*(Mat *)&render::AdjustedWorldToFrustum);	// VF12-15
	vu1::StoreMat(*(Mat *)&render::AdjustedWorldToViewport);// VF12-15
	vu1::EndPrim(0);										// End upload
}

void CImmediateMode::sSetZPush(float zpush)
{
	Mth::Matrix localToViewport(render::AdjustedWorldToViewport);
	localToViewport[0][2] += localToViewport[0][3] * zpush * 64.0f;
	localToViewport[1][2] += localToViewport[1][3] * zpush * 64.0f;
	localToViewport[2][2] += localToViewport[2][3] * zpush * 64.0f;
	localToViewport[3][2] += localToViewport[3][3] * zpush * 64.0f;

	// VU context, uploading the view transform data
	vu1::BeginPrim(ABS, VU1_ADDR(L_VF12));	  				// Begin packed register upload to VF12 
	vu1::StoreMat(*(Mat *)&localToViewport);				// VF12-15
	vu1::EndPrim(0);										// End upload
}

void CImmediateMode::sClearZPush()
{
	// VU context, uploading the view transform data
	vu1::BeginPrim(ABS, VU1_ADDR(L_VF12));	  				// Begin packed register upload to VF12 
	vu1::StoreMat(*(Mat *)&render::AdjustedWorldToViewport);// VF12-15
	vu1::EndPrim(0);										// End upload
}

void CImmediateMode::sStartPolyDraw( SSingleTexture * p_engine_texture, uint64 blend, uint unpackFLG, bool clip )
{
	// GS context
	gs::BeginPrim(unpackFLG,0,0);
	gs::Reg1(gs::ALPHA_1,		blend);

	if ( p_engine_texture )
	{
		gs::Reg1(gs::TEX0_1,	p_engine_texture->m_RegTEX0);
		gs::Reg1(gs::TEX1_1,	p_engine_texture->m_RegTEX1);
	}
	gs::EndPrim(0);

	// Set render flags.
	vif::ITOP(clip ? CLIP : CULL);
}

//
// Starts a poly draw with pre-packed texture registers (probably stolen from some other dma list)
//
void CImmediateMode::sStartPolyDraw( uint32 * p_packed_texture_regs, int num_texture_regs, uint unpackFLG, bool clip )
{
	// GS context
	if (p_packed_texture_regs)
	{
		gs::BeginPrim(unpackFLG,0,0);
		for (int i = 0; i < num_texture_regs; i++)
		{
			NxPs2::dma::Store32(*(p_packed_texture_regs++));
			NxPs2::dma::Store32(*(p_packed_texture_regs++));
			NxPs2::dma::Store32(*(p_packed_texture_regs++));
		}
		gs::EndPrim(0);
	}

	// Set render flags.
	vif::ITOP(clip ? CLIP : CULL);
}

void CImmediateMode::sDrawQuadTexture( SSingleTexture * p_engine_texture, const Mth::Vector& vert0, const Mth::Vector& vert1,
									   const Mth::Vector& vert2, const Mth::Vector& vert3,
									   uint32 col0, uint32 col1, uint32 col2, uint32 col3 )
{
	// begin a batch of vertices
	// Note: UV must always be 1st, Pos must always be last.
	if ( p_engine_texture )
	{
		//BeginModelPrim(gs::XYZ2<<8|gs::RGBAQ<<4|gs::UV, 3, TRISTRIP|IIP|ABE|TME|FST, 1, VU1_ADDR(Proj));
		vif::STCYCL(1,3);
		vif::UNPACK(0,V4_32,1,ABS,UNSIGNED,0);
		gif::BeginTag1(gs::XYZF2<<8|gs::RGBAQ<<4|gs::UV, 3, PACKED, TRISTRIP|IIP|ABE|TME|FST, 1, VU1_ADDR(Proj));
	}
	else
	{
		//BeginModelPrim(gs::XYZ2<<4|gs::RGBAQ, 2, TRISTRIP|IIP|ABE, 1, VU1_ADDR(Proj));
		vif::STCYCL(1,2);
		vif::UNPACK(0,V4_32,1,ABS,UNSIGNED,0);
		gif::BeginTag1(gs::XYZF2<<4|gs::RGBAQ, 2, PACKED, TRISTRIP|IIP|ABE, 1, VU1_ADDR(Proj));
	}

	int loc = 1;

	if ( p_engine_texture )
	{
		int u = p_engine_texture->GetWidth() << 4;
		int v = p_engine_texture->GetHeight() << 4;

		vif::BeginUNPACK(0, V2_16, ABS, UNSIGNED, loc);
		vif::StoreV2_16(0,0);
		vif::StoreV2_16(u,0);
		vif::StoreV2_16(0,v);
		vif::StoreV2_16(u,v);
		vif::EndUNPACK();
		loc++;
	}

	vif::BeginUNPACK(0, V4_8, ABS, UNSIGNED, loc);
	vif::StoreS_32(col0);
	vif::StoreS_32(col1);
	vif::StoreS_32(col3);
	vif::StoreS_32(col2);
	vif::EndUNPACK();
	loc++;

	vif::STMOD(OFFSET_MODE);
	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, loc);
	vif::StoreV4_32((sint32)(vert0[X]*SUB_INCH_PRECISION), (sint32)(vert0[Y]*SUB_INCH_PRECISION), (sint32)(vert0[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert1[X]*SUB_INCH_PRECISION), (sint32)(vert1[Y]*SUB_INCH_PRECISION), (sint32)(vert1[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert3[X]*SUB_INCH_PRECISION), (sint32)(vert3[Y]*SUB_INCH_PRECISION), (sint32)(vert3[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert2[X]*SUB_INCH_PRECISION), (sint32)(vert2[Y]*SUB_INCH_PRECISION), (sint32)(vert2[Z]*SUB_INCH_PRECISION), 0);
	vif::EndUNPACK();
	loc++;
	vif::STMOD(NORMAL_MODE);

	// finish batch of vertices
	//EndModelMultiPrim();
	gif::EndTag1(1);
	vif::MSCAL(VU1_ADDR(Parser));
}

void CImmediateMode::sDrawTri( const Mth::Vector& vert0, const Mth::Vector& vert1, const Mth::Vector& vert2,
							   uint32 col0, uint32 col1, uint32 col2, uint unpackFLG )
{
	// begin a batch of vertices
	// Note: ST must always be 1st, Pos must always be last.
	//BeginModelPrim(gs::XYZ2<<4|gs::RGBAQ, 2, TRISTRIP|IIP|ABE, 1, VU1_ADDR(Proj));
	vif::STCYCL(1,2);
	vif::UNPACK(0,V4_32,1,unpackFLG,UNSIGNED,0);
	gif::BeginTag1(gs::XYZF2<<4|gs::RGBAQ, 2, PACKED, TRISTRIP|IIP|ABE, 1, VU1_ADDR(Proj));

	int loc = 1;

	vif::BeginUNPACK(0, V4_8, unpackFLG, UNSIGNED, loc);
	vif::StoreS_32(col0);
	vif::StoreS_32(col1);
	vif::StoreS_32(col2);
	vif::EndUNPACK();
	loc++;

	vif::STMOD(OFFSET_MODE);
	vif::BeginUNPACK(0, V4_32, unpackFLG, SIGNED, loc);
	vif::StoreV4_32((sint32)(vert0[X]*SUB_INCH_PRECISION), (sint32)(vert0[Y]*SUB_INCH_PRECISION), (sint32)(vert0[Z]*SUB_INCH_PRECISION), 0xC000);
	vif::StoreV4_32((sint32)(vert1[X]*SUB_INCH_PRECISION), (sint32)(vert1[Y]*SUB_INCH_PRECISION), (sint32)(vert1[Z]*SUB_INCH_PRECISION), 0xC000);
	vif::StoreV4_32((sint32)(vert2[X]*SUB_INCH_PRECISION), (sint32)(vert2[Y]*SUB_INCH_PRECISION), (sint32)(vert2[Z]*SUB_INCH_PRECISION), 0);
	vif::EndUNPACK();
	vif::STMOD(NORMAL_MODE);
	loc++;

	// finish batch of vertices
	//EndModelMultiPrim();
	//EndModelPrim(1);
	gif::EndTag1(1);
	vif::MSCAL(VU1_ADDR(Parser));
}

void CImmediateMode::sDrawTriUV( const Mth::Vector& vert0, const Mth::Vector& vert1, const Mth::Vector& vert2,
								 float u0, float v0, float u1, float v1, float u2, float v2,
								 uint32 col0, uint32 col1, uint32 col2, uint unpackFLG )
{
	// dummy gs context
	gs::BeginPrim(unpackFLG,0,0);
	gs::Reg1(gs::A_D_NOP, 0);
	gs::EndPrim(0);

	// gif tag
	vif::UNPACK(0,V4_32,1,unpackFLG,UNSIGNED,0);
	gif::BeginTag1(gs::XYZF2<<8|gs::RGBAQ<<4|gs::ST, 3, PACKED, TRISTRIP|IIP|ABE|TME, 1, VU1_ADDR(PTex) | (0x6 << 11) /* turns off UV correction */);

	// each vertex has 3 elements
	vif::STCYCL(1,3);

	// ST
	vif::UNPACK(0, V2_32, 3, unpackFLG, UNSIGNED, 1);
	vif::StoreV2_32((sint32) (u0 * 4096.0f), (sint32) (v0 * 4096.0f));
	vif::StoreV2_32((sint32) (u1 * 4096.0f), (sint32) (v1 * 4096.0f));
	vif::StoreV2_32((sint32) (u2 * 4096.0f), (sint32) (v2 * 4096.0f));

	// RGBA
	vif::UNPACK(0, V4_8, 3, unpackFLG, UNSIGNED, 2);
	vif::StoreS_32(col0);
	vif::StoreS_32(col1);
	vif::StoreS_32(col2);

	// XYZ
	vif::STMOD(OFFSET_MODE);
	vif::UNPACK(0, V4_32, 3, unpackFLG, SIGNED, 3);
	vif::StoreV4_32((sint32)(vert0[X]*SUB_INCH_PRECISION), (sint32)(vert0[Y]*SUB_INCH_PRECISION), (sint32)(vert0[Z]*SUB_INCH_PRECISION), 0xC000);
	vif::StoreV4_32((sint32)(vert1[X]*SUB_INCH_PRECISION), (sint32)(vert1[Y]*SUB_INCH_PRECISION), (sint32)(vert1[Z]*SUB_INCH_PRECISION), 0xC000);
	vif::StoreV4_32((sint32)(vert2[X]*SUB_INCH_PRECISION), (sint32)(vert2[Y]*SUB_INCH_PRECISION), (sint32)(vert2[Z]*SUB_INCH_PRECISION), 0);
	vif::STMOD(NORMAL_MODE);

	// finish batch of vertices
	gif::EndTag1(1);
	vif::MSCAL(VU1_ADDR(Parser));
}

void CImmediateMode::sDraw5QuadTexture( SSingleTexture * p_engine_texture, const Mth::Vector& vert0, const Mth::Vector& vert1,
									    const Mth::Vector& vert2, const Mth::Vector& vert3, const Mth::Vector& vert4,
									    uint32 col0, uint32 col1 )
{
	// begin a batch of vertices
	// Note: UV must always be 1st, Pos must always be last.
	if ( p_engine_texture )
	{
		//BeginModelPrim(gs::XYZ2<<8|gs::RGBAQ<<4|gs::UV, 3, TRISTRIP|IIP|ABE|TME|FST, 1, VU1_ADDR(Proj));
		vif::STCYCL(1,3);
		vif::UNPACK(0,V4_32,1,ABS,UNSIGNED,0);
		gif::BeginTag1(gs::XYZF2<<8|gs::RGBAQ<<4|gs::ST, 3, PACKED, TRISTRIP|IIP|ABE|TME|FST, 1, VU1_ADDR(Proj));
	}
	else
	{
		//BeginModelPrim(gs::XYZ2<<4|gs::RGBAQ, 2, TRIFAN|IIP|ABE, 1, VU1_ADDR(Proj));
		vif::STCYCL(1,2);
		vif::UNPACK(0,V4_32,1,ABS,UNSIGNED,0);
		gif::BeginTag1(gs::XYZF2<<4|gs::RGBAQ, 2, PACKED, TRIFAN|IIP|ABE, 1, VU1_ADDR(Proj));
	}

	int loc = 1;

	if ( p_engine_texture )
	{
		int u = p_engine_texture->GetWidth() << 4;
		int v = p_engine_texture->GetHeight() << 4;

		vif::BeginUNPACK(0, V2_16, ABS, UNSIGNED, loc);
		vif::StoreV2_16(u>>1,v>>1);
		vif::StoreV2_16(0,0);
		vif::StoreV2_16(u,0);
		vif::StoreV2_16(0,v);
		vif::StoreV2_16(u,v);
		vif::StoreV2_16(0,0);
		vif::EndUNPACK();
		loc++;
	}

	vif::BeginUNPACK(0, V4_8, ABS, UNSIGNED, loc);
	vif::StoreS_32(col0);
	vif::StoreS_32(col1);
	vif::StoreS_32(col1);
	vif::StoreS_32(col1);
	vif::StoreS_32(col1);
	vif::StoreS_32(col1);
	vif::EndUNPACK();
	loc++;

	vif::STMOD(OFFSET_MODE);
	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, loc);
	vif::StoreV4_32((sint32)(vert0[X]*SUB_INCH_PRECISION), (sint32)(vert0[Y]*SUB_INCH_PRECISION), (sint32)(vert0[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert1[X]*SUB_INCH_PRECISION), (sint32)(vert1[Y]*SUB_INCH_PRECISION), (sint32)(vert1[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert2[X]*SUB_INCH_PRECISION), (sint32)(vert2[Y]*SUB_INCH_PRECISION), (sint32)(vert2[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert3[X]*SUB_INCH_PRECISION), (sint32)(vert3[Y]*SUB_INCH_PRECISION), (sint32)(vert3[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert4[X]*SUB_INCH_PRECISION), (sint32)(vert4[Y]*SUB_INCH_PRECISION), (sint32)(vert4[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert1[X]*SUB_INCH_PRECISION), (sint32)(vert1[Y]*SUB_INCH_PRECISION), (sint32)(vert1[Z]*SUB_INCH_PRECISION), 0);
	vif::EndUNPACK();
	loc++;
	vif::STMOD(NORMAL_MODE);

	// finish batch of vertices
	//EndModelMultiPrim();
	gif::EndTag1(1);
	vif::MSCAL(VU1_ADDR(Parser));
}

void CImmediateMode::sDrawGlowSegment( const Mth::Vector& vert0, const Mth::Vector& vert1,
									   const Mth::Vector& vert2, const Mth::Vector& vert3, const Mth::Vector& vert4,
									   uint32 col0, uint32 col1, uint32 col2 )
{
	// begin a batch of vertices
	// Note: UV must always be 1st, Pos must always be last.
	//BeginModelPrim(gs::XYZ2<<4|gs::RGBAQ, 2, TRISTRIP|IIP|ABE, 1, VU1_ADDR(Proj));
	vif::STCYCL(1,2);
	vif::UNPACK(0,V4_32,1,ABS,UNSIGNED,0);
	gif::BeginTag1(gs::XYZF2<<4|gs::RGBAQ, 2, PACKED, TRISTRIP|IIP|ABE, 1, VU1_ADDR(Proj));

	int loc = 1;

	vif::BeginUNPACK(0, V4_8, ABS, UNSIGNED, loc);
	vif::StoreS_32(col0);
	vif::StoreS_32(col1);
	vif::StoreS_32(col1);
	vif::StoreS_32(col2);
	vif::StoreS_32(col2);
	vif::EndUNPACK();
	loc++;

	vif::STMOD(OFFSET_MODE);
	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, loc);
	vif::StoreV4_32((sint32)(vert0[X]*SUB_INCH_PRECISION), (sint32)(vert0[Y]*SUB_INCH_PRECISION), (sint32)(vert0[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert1[X]*SUB_INCH_PRECISION), (sint32)(vert1[Y]*SUB_INCH_PRECISION), (sint32)(vert1[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert2[X]*SUB_INCH_PRECISION), (sint32)(vert2[Y]*SUB_INCH_PRECISION), (sint32)(vert2[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert3[X]*SUB_INCH_PRECISION), (sint32)(vert3[Y]*SUB_INCH_PRECISION), (sint32)(vert3[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert4[X]*SUB_INCH_PRECISION), (sint32)(vert4[Y]*SUB_INCH_PRECISION), (sint32)(vert4[Z]*SUB_INCH_PRECISION), 0);
	vif::EndUNPACK();
	loc++;
	vif::STMOD(NORMAL_MODE);

	// finish batch of vertices
	//EndModelMultiPrim();
	gif::EndTag1(1);
	vif::MSCAL(VU1_ADDR(Parser));
}

void CImmediateMode::sDrawStarSegment( const Mth::Vector& vert0, const Mth::Vector& vert1,
									   const Mth::Vector& vert2, const Mth::Vector& vert3,
									   uint32 col0, uint32 col1, uint32 col2 )
{
	// begin a batch of vertices
	// Note: UV must always be 1st, Pos must always be last.
	//BeginModelPrim(gs::XYZ2<<4|gs::RGBAQ, 2, TRISTRIP|IIP|ABE, 1, VU1_ADDR(Proj));
	vif::STCYCL(1,2);
	vif::UNPACK(0,V4_32,1,ABS,UNSIGNED,0);
	gif::BeginTag1(gs::XYZF2<<4|gs::RGBAQ, 2, PACKED, TRISTRIP|IIP|ABE, 1, VU1_ADDR(Proj));

	int loc = 1;

	vif::BeginUNPACK(0, V4_8, ABS, UNSIGNED, loc);
	vif::StoreS_32(col0);
	vif::StoreS_32(col1);
	vif::StoreS_32(col1);
	vif::StoreS_32(col2);
	vif::EndUNPACK();
	loc++;

	vif::STMOD(OFFSET_MODE);
	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, loc);
	vif::StoreV4_32((sint32)(vert0[X]*SUB_INCH_PRECISION), (sint32)(vert0[Y]*SUB_INCH_PRECISION), (sint32)(vert0[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert1[X]*SUB_INCH_PRECISION), (sint32)(vert1[Y]*SUB_INCH_PRECISION), (sint32)(vert1[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert2[X]*SUB_INCH_PRECISION), (sint32)(vert2[Y]*SUB_INCH_PRECISION), (sint32)(vert2[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert3[X]*SUB_INCH_PRECISION), (sint32)(vert3[Y]*SUB_INCH_PRECISION), (sint32)(vert3[Z]*SUB_INCH_PRECISION), 0);
	vif::EndUNPACK();
	loc++;
	vif::STMOD(NORMAL_MODE);

	// finish batch of vertices
	//EndModelMultiPrim();
	gif::EndTag1(1);
	vif::MSCAL(VU1_ADDR(Parser));
}

void CImmediateMode::sDrawSmoothStarSegment( const Mth::Vector& vert0, const Mth::Vector& vert1,
											 const Mth::Vector& vert2, const Mth::Vector& vert3, const Mth::Vector& vert4,
											 uint32 col0, uint32 col1, uint32 col2 )
{
	// begin a batch of vertices
	// Note: UV must always be 1st, Pos must always be last.
	//BeginModelPrim(gs::XYZ2<<4|gs::RGBAQ, 2, TRIFAN|IIP|ABE, 1, VU1_ADDR(Proj));
	vif::STCYCL(1,2);
	vif::UNPACK(0,V4_32,1,ABS,UNSIGNED,0);
	gif::BeginTag1(gs::XYZF2<<4|gs::RGBAQ, 2, PACKED, TRIFAN|IIP|ABE, 1, VU1_ADDR(Proj));

	int loc = 1;

	vif::BeginUNPACK(0, V4_8, ABS, UNSIGNED, loc);
	vif::StoreS_32(col1);
	vif::StoreS_32(col0);
	vif::StoreS_32(col2);
	vif::StoreS_32(col2);
	vif::StoreS_32(col2);
	vif::StoreS_32(col0);
	vif::EndUNPACK();
	loc++;

	vif::STMOD(OFFSET_MODE);
	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, loc);
	vif::StoreV4_32((sint32)(vert0[X]*SUB_INCH_PRECISION), (sint32)(vert0[Y]*SUB_INCH_PRECISION), (sint32)(vert0[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert1[X]*SUB_INCH_PRECISION), (sint32)(vert1[Y]*SUB_INCH_PRECISION), (sint32)(vert1[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert2[X]*SUB_INCH_PRECISION), (sint32)(vert2[Y]*SUB_INCH_PRECISION), (sint32)(vert2[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert3[X]*SUB_INCH_PRECISION), (sint32)(vert3[Y]*SUB_INCH_PRECISION), (sint32)(vert3[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert4[X]*SUB_INCH_PRECISION), (sint32)(vert4[Y]*SUB_INCH_PRECISION), (sint32)(vert4[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert1[X]*SUB_INCH_PRECISION), (sint32)(vert1[Y]*SUB_INCH_PRECISION), (sint32)(vert1[Z]*SUB_INCH_PRECISION), 0);
	vif::EndUNPACK();
	loc++;
	vif::STMOD(NORMAL_MODE);

	// finish batch of vertices
	//EndModelMultiPrim();
	gif::EndTag1(1);
	vif::MSCAL(VU1_ADDR(Parser));
}

void CImmediateMode::sDrawLine( const Mth::Vector& vert0, const Mth::Vector& vert1, uint32 col0, uint32 col1 )
{
	// begin a batch of vertices
	// Note: UV must always be 1st, Pos must always be last.
	//BeginModelPrim(gs::XYZ2<<4|gs::RGBAQ, 2, LINE|IIP|ABE, 1, VU1_ADDR(Proj));
	vif::STCYCL(1,2);
	vif::UNPACK(0,V4_32,1,ABS,UNSIGNED,0);
	gif::BeginTag1(gs::XYZ2<<4|gs::RGBAQ, 2, PACKED, LINE|IIP|ABE, 1, VU1_ADDR(Line));

	int loc = 1;

	vif::BeginUNPACK(0, V4_8, ABS, UNSIGNED, loc);
	vif::StoreS_32(col0);
	vif::StoreS_32(col1);
	vif::EndUNPACK();
	loc++;

	vif::STMOD(OFFSET_MODE);
	vif::BeginUNPACK(1, V4_32, ABS, SIGNED, loc);
	vif::StoreV4_32((sint32)(vert0[X]*SUB_INCH_PRECISION), (sint32)(vert0[Y]*SUB_INCH_PRECISION), (sint32)(vert0[Z]*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(vert1[X]*SUB_INCH_PRECISION), (sint32)(vert1[Y]*SUB_INCH_PRECISION), (sint32)(vert1[Z]*SUB_INCH_PRECISION), 0);
	vif::EndUNPACK();
	loc++;
	vif::STMOD(NORMAL_MODE);

	// finish batch of vertices
	//EndModelMultiPrim();
	gif::EndTag1(1);
	vif::MSCAL(VU1_ADDR(Parser));
}

}





================================================
FILE: Code/Gfx/NGPS/NX/immediate.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       immediate.h
//* OWNER:          Garrett Jost
//* CREATION DATE:  7/19/2002
//****************************************************************************

#ifndef	__IMMEDIATE_H__
#define	__IMMEDIATE_H__
    
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
namespace NxPs2
{
// Forward declarations
struct SSingleTexture;

class CImmediateMode
{
public:
	// Init functions
	static void sViewportInit();
	static void sTextureGroupInit(uint unpackFLG);

	static void sSetZPush(float zpush = 1.0e-33f);
	static void sClearZPush();

	// Draw start functions
	static void sStartPolyDraw( SSingleTexture * p_engine_texture, uint64 blend, uint unpackFLG, bool clip = false );
	static void sStartPolyDraw( uint32 * p_packed_texture_regs, int num_texture_regs, uint unpackFLG, bool clip = false );

	// Draw functions
	static void sDrawQuadTexture( SSingleTexture * p_engine_texture, const Mth::Vector& vert0, const Mth::Vector& vert1,
								  const Mth::Vector& vert2, const Mth::Vector& vert3,
								  uint32 col0, uint32 col1, uint32 col2, uint32 col3 );
	static void sDrawTri( const Mth::Vector& vert0, const Mth::Vector& vert1, const Mth::Vector& vert2,
						  uint32 col0, uint32 col1, uint32 col2, uint unpackFLG );
	static void sDrawTriUV( const Mth::Vector& vert0, const Mth::Vector& vert1, const Mth::Vector& vert2,
							float u0, float v0, float u1, float v1, float u2, float v2,
							uint32 col0, uint32 col1, uint32 col2, uint unpackFLG );
	static void sDraw5QuadTexture( SSingleTexture * p_engine_texture, const Mth::Vector& vert0, const Mth::Vector& vert1,
								   const Mth::Vector& vert2, const Mth::Vector& vert3, const Mth::Vector& vert4,
								   uint32 col0, uint32 col1 );
	static void sDrawGlowSegment( const Mth::Vector& vert0, const Mth::Vector& vert1,
								  const Mth::Vector& vert2, const Mth::Vector& vert3, const Mth::Vector& vert4,
								  uint32 col0, uint32 col1, uint32 col2 );
	static void sDrawStarSegment( const Mth::Vector& vert0, const Mth::Vector& vert1,
								  const Mth::Vector& vert2, const Mth::Vector& vert3,
								  uint32 col0, uint32 col1, uint32 col2 );
	static void sDrawSmoothStarSegment( const Mth::Vector& vert0, const Mth::Vector& vert1,
										const Mth::Vector& vert2, const Mth::Vector& vert3, const Mth::Vector& vert4,
										uint32 col0, uint32 col1, uint32 col2 );
	static void sDrawLine( const Mth::Vector& vert0, const Mth::Vector& vert1, uint32 col0, uint32 col1 );

	static uint64 sGetTextureBlend( uint32 blend_checksum, int fix );

protected:
};

}

#endif 






================================================
FILE: Code/Gfx/NGPS/NX/instance.cpp
================================================
#include "instance.h"
#include "scene.h"
#include "dma.h"
#include "mesh.h"
#include 

namespace NxPs2
{


CInstance::CInstance(sScene *pScene, Mth::Matrix transform, bool color_per_material)
{
	SetTransform(transform);
	mp_bone_transforms = NULL;
	m_num_bones = 0;
	m_flags = INSTANCEFLAG_ACTIVE;
	mp_next = pScene->pInstances;
	pScene->pInstances = this;
	m_lastFrame = 0;
	m_field = 0;
	mp_scene = pScene;
	mp_light_group = NULL;
	if (color_per_material)
	{
		m_flags |= INSTANCEFLAG_COLORPERMATERIAL;
		mp_color_array = new uint32[mp_scene->NumMeshes];
		for (int i = 0; i < mp_scene->NumMeshes; i++)
		{
			mp_color_array[i] = 0x80808080;
		}
	}
	else
	{
		m_colour = 0x80808080;
	}
}


CInstance::CInstance(sScene *pScene, Mth::Matrix transform, bool color_per_material, int numBones, Mth::Matrix *pBoneTransforms)
{
	SetTransform(transform);
	mp_bone_transforms = pBoneTransforms;
	m_num_bones = numBones;
	m_flags = INSTANCEFLAG_ACTIVE;
	mp_next = pScene->pInstances;
	pScene->pInstances = this;
	m_lastFrame = 0;
	m_field = 0;
	mp_scene = pScene;
	mp_light_group = NULL;
	if (color_per_material)
	{
		m_flags |= INSTANCEFLAG_COLORPERMATERIAL;
		mp_color_array = new uint32[mp_scene->NumMeshes];
		for (int i = 0; i < mp_scene->NumMeshes; i++)
		{
			mp_color_array[i] = 0x80808080;
		}
	}
	else
	{
		m_colour = 0x80808080;
	}
}


CInstance::~CInstance()
{
	if (HasColorPerMaterial())
	{
		delete [] mp_color_array;
	}

	// remove from list structure...
	// head of instance list
	if (mp_scene->pInstances == this)
	{
		mp_scene->pInstances = mp_next;
	}
	// body of instance list
	else
	{
		CInstance *pInstance;
		for (pInstance=mp_scene->pInstances; pInstance; pInstance=pInstance->mp_next)
			if (pInstance->mp_next == this)
				break;
		Dbg_MsgAssert(pInstance, ("couldn't locate CInstance for deletion"));
		pInstance->mp_next = mp_next;
	}
}


void CInstance::SetActive(bool Active)
{
	if (Active)
	{
		m_flags |= INSTANCEFLAG_ACTIVE;
	}
	else
	{
		m_flags &= ~INSTANCEFLAG_ACTIVE;
	}
}

void CInstance::EnableShadow(bool Enabled)
{
	if (Enabled)
	{
		m_flags |= INSTANCEFLAG_CASTSSHADOW;
	}
	else
	{
		m_flags &= ~INSTANCEFLAG_CASTSSHADOW;
	}
}

void CInstance::SetWireframe(bool Wireframe)
{
	if (Wireframe)
	{
		m_flags |= INSTANCEFLAG_WIREFRAME;
	}
	else
	{
		m_flags &= ~INSTANCEFLAG_WIREFRAME;
	}
}

Mth::Matrix* CInstance::GetBoneTransforms(void)
{
	return m_field ? mp_bone_transforms : ( mp_bone_transforms + m_num_bones );
}

void CInstance::SetBoneTransforms(Mth::Matrix* pMat)
{
	// GJ:  toggle field, but only the first time
	// (otherwise, the double-buffer gets messed
	// up if SetBoneTransforms() is called more
	// than once per frame...)
	uint8 thisFrame = ( render::Frame & 0xff );
	if ( m_lastFrame != thisFrame )
	{
		m_field = !m_field;
		m_lastFrame = thisFrame;
	}

	int startIndex = m_field ? 0 : m_num_bones;

	Mth::Matrix* pSrc = pMat;
	Mth::Matrix* pDst = mp_bone_transforms + startIndex;

	// copy the given matrices into the correct half of the matrix double-buffer
	for ( int i = 0; i < m_num_bones; i++ )
	{
		*pDst++ = *pSrc++;
	}
}


void CInstance::SetColor(uint32 rgba)
{
	Dbg_Assert(!HasColorPerMaterial());

	m_colour = rgba;
}

uint32 CInstance::GetColor()
{
	Dbg_Assert(!HasColorPerMaterial());

	return m_colour;
}

void CInstance::SetMaterialColor(uint32 material_name, int pass, uint32 rgba)
{
	Dbg_Assert(HasColorPerMaterial());

	int i;
	sMesh *pMesh;

	// Array is in mesh order
	for (pMesh = mp_scene->pMeshes, i = 0; i < mp_scene->NumMeshes; pMesh++, i++)
	{
		if ((pMesh->MaterialName==material_name) && (pMesh->Pass == pass))
		{
			mp_color_array[i] = rgba;
		}
	}
}

void CInstance::SetMaterialColorByIndex(int mesh_idx, uint32 rgba)
{
	Dbg_Assert(HasColorPerMaterial());
	Dbg_MsgAssert(mesh_idx < mp_scene->NumMeshes, ("Mesh index %d is out of range", mesh_idx));

	mp_color_array[mesh_idx] = rgba;
}

uint32 CInstance::GetMaterialColor(uint32 material_name, int pass)
{
	Dbg_Assert(HasColorPerMaterial());

	int i;
	sMesh *pMesh;

	// Array is in mesh order
	for (pMesh = mp_scene->pMeshes, i = 0; i < mp_scene->NumMeshes; pMesh++, i++)
	{
		if ((pMesh->MaterialName==material_name) && (pMesh->Pass == pass))
		{
			return mp_color_array[i];
		}
	}

	return 0x80808080;
}

uint32 CInstance::GetMaterialColorByIndex(int mesh_idx)
{
	Dbg_Assert(HasColorPerMaterial());

	return mp_color_array[mesh_idx];
}

Mth::Vector CInstance::GetBoundingSphere()
{

	if (m_flags & INSTANCEFLAG_EXPLICITSPHERE)
	{
		return m_bounding_sphere;
	}
	else
	{
		Mth::Vector sphere(mp_scene->Sphere);
		sphere[3] += 24.0f;
		return sphere;
	}
}

void CInstance::SetBoundingSphere(float x, float y, float z, float r)
{
	//printf( "Stub:  SetBoundingSphere %f %f %f %f\n", x, y, z, r );
	m_bounding_sphere.Set(x,y,z,r);
	m_flags |= INSTANCEFLAG_EXPLICITSPHERE;
}


void CInstance::SqueezeADC()
{
	int i, num_meshes = mp_scene->NumMeshes;
	sMesh *p_mesh = mp_scene->pMeshes;

	for (i=0; ipSubroutine);
		dma::SqueezeNOP(p_mesh->pSubroutine);
	}
}



// uv manipulation for skins

void CInstance::SetUVOffset(uint32 material_name, int pass, float u_offset, float v_offset)
{
	mp_scene->SetUVOffset(material_name, pass, u_offset, v_offset);
}


void CInstance::SetUVMatrix(uint32 material_name, int pass, const Mth::Matrix &mat)
{
	mp_scene->SetUVMatrix(material_name, pass, mat);
}


} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/instance.h
================================================
#ifndef __INSTANCE_H
#define __INSTANCE_H


#ifndef __CORE_DEFINES_H
#include 
#endif
#include "scene.h"
#include 


namespace Script
{
	extern int GetInt(uint32, bool);
}

namespace NxPs2
{

// Forward declarations
class	CLightGroup;

#define INSTANCEFLAG_ACTIVE				(1<<0)
#define INSTANCEFLAG_CASTSSHADOW		(1<<1)
#define INSTANCEFLAG_EXPLICITSPHERE		(1<<2)
#define INSTANCEFLAG_WIREFRAME			(1<<3)
#define INSTANCEFLAG_COLORPERMATERIAL	(1<<4)

class CInstance
{
public:
	void SetTransform(Mth::Matrix transform) { m_transform = transform; }
	Mth::Matrix *GetTransform(void) { return &m_transform; }
	int GetNumBones(void) { return m_num_bones; }
	Mth::Matrix *GetBoneTransforms(void);
	void SetBoneTransforms(Mth::Matrix *pMat);
	CInstance *GetNext() { return mp_next; }
	sScene *GetScene() { return mp_scene; }
	void SetActive(bool Active);
	void EnableShadow(bool Enabled);
	void SetWireframe(bool Wireframe);
	bool IsActive(void) const; 
	bool CastsShadow(void) const; 
	bool IsWireframe(void) const; 
	bool HasColorPerMaterial(void) const; 
	void SetColor(uint32 rgba);
	void SetMaterialColor(uint32 material_name, int pass, uint32 rgba);
	void SetMaterialColorByIndex(int mesh_idx, uint32 rgba);
	uint32 GetColor();
	uint32 GetMaterialColor(uint32 material_name, int pass);
	uint32 GetMaterialColorByIndex(int mesh_idx);
	void SetLightGroup(CLightGroup *p_light_group);
	CLightGroup *GetLightGroup();
	Mth::Vector GetBoundingSphere();
	void SetBoundingSphere(float x, float y, float z, float r);
	void SqueezeADC();
	void SetUVOffset(uint32 material_name, int pass, float u_offset, float v_offset);
	void SetUVMatrix(uint32 material_name, int pass, const Mth::Matrix &mat);
	CInstance(sScene *pScene, Mth::Matrix transform, bool color_per_material);
	CInstance(sScene *pScene, Mth::Matrix transform, bool color_per_material, int numBones, Mth::Matrix *pBoneTransforms);
	~CInstance();

private:
	Mth::Matrix m_transform;
	Mth::Matrix *mp_bone_transforms;
	Mth::Vector m_bounding_sphere;
	uint32 m_flags;
	union
	{
		uint32 m_colour;
		uint32 *mp_color_array;			// Use this only when INSTANCEFLAG_COLORPERMATERIAL is set; in Mesh order
	};
	int m_num_bones;
	CInstance *mp_next;
	sScene *mp_scene;
	CLightGroup *mp_light_group;		// optional group of lights
	uint8 m_lastFrame;
	char m_field;
};


//  IsActive needs to be inline, otherwise it takes 1% of rendering cpu time
inline bool CInstance::IsActive(void) const
{
	return (m_flags & INSTANCEFLAG_ACTIVE) ? true : false;
}

inline bool CInstance::CastsShadow(void) const
{
	return (m_flags & INSTANCEFLAG_CASTSSHADOW) ? true : false;
}

inline bool CInstance::IsWireframe(void) const
{
	#ifdef	__NOPT_ASSERT__
	if (Script::GetInt(CRCD(0xb4ff073e,"wireframe_skins"),false))
	{
		return true;
	}
	#endif
	return (m_flags & INSTANCEFLAG_WIREFRAME) ? true : false;
}

inline bool CInstance::HasColorPerMaterial(void) const
{
	return m_flags & INSTANCEFLAG_COLORPERMATERIAL;
}

inline void CInstance::SetLightGroup(CLightGroup *p_light_group)
{
	mp_light_group = p_light_group;
}

inline CLightGroup * CInstance::GetLightGroup()
{
	return mp_light_group;
}

} // namespace NxPs2


#endif // __INSTANCE_H



================================================
FILE: Code/Gfx/NGPS/NX/interrupts.cpp
================================================
#include 
#include 
#include 
#include "texture.h"
#include "group.h"
#include "render.h"
#include "gs.h"
#include "switches.h"
#include 

//#undef	__USE_PROFILER__


/*

 Mick Notes:

 All interrupts are set up in the "InitialiseEngine" function of nx_init.cpp
 
 There are three types of interrupt covered here
 
 1) GIF Interrupt - Used to signal the completion of texture upload	to VRAM
 2) GS  Interrupt - Used to signal the completion of GS rendering (drawing polygons)
 3) VIF Interrupt - NOT USED, could be used like the GS interrupt, but unreliable.

 There are two types of activity going on in parallel: Upload and Render
 
 Whenever one of these finishes, it causes an interrupt. 
 
 There is no way of knowing in advance what order the tasks will finish in
 so we must handle all possible cases. 

 At any time, either upload or render might be "Stalled".  In this case we are waiting
 for something before we can start rendering or uploading a new group.  Stalls are not
 good, as the relevent processor is sat idle.  We want to minimize stalls as much as
 possible.  
 
 Stalls happen when:
 
  - The initial render group is waiting for its textures to upload.  
    (?) The renderer is always stalled on the first group 
  - An upload group want to use some VRAM that is used by a group 
    that is currently rendering. 
  - A render group is waiting for it's textures to upload. This could
    happen if a group renders quicker than the next groups uploads.

*/
   
   
   
namespace NxPs2
{


#define START_UPLOAD()																\
{																					\
	*D2_QWC = 0;																	\
	*D2_TADR = (uint)sGroup::pUploadGroup->pUpload[!render::Field];							\
	*D2_CHCR = 0x105;																\
	UploadStalled = 0;																\
}

#if GS_INTERRUPT
#define START_RENDER()																\
{																					\
	*D1_QWC = 0;																	\
	*D1_TADR = (uint)sGroup::pRenderGroup->pRender[!render::Field];							\
	*D1_CHCR = 0x145;								   								\
	RenderStalled = 0;																\
}
#endif

#if VIF1_INTERRUPT
#define START_RENDER()																\
{																					\
if (sGroup::pRenderGroup != sGroup::pHead)											\
		*VIF1_FBRST = 1<<3;															\
	else																			\
	{																				\
		*D1_QWC = 0;																\
		*D1_TADR = (uint)sGroup::pRenderGroup->pRender[!render::Field];						\
		*D1_CHCR = 0x145;								   							\
	}																				\
}
#endif

#define NO_VRAM_CONFLICT()															\
(																					\
	sGroup::pRenderGroup->VramStart >= sGroup::pUploadGroup->VramEnd ||				\
	sGroup::pUploadGroup->VramStart >= sGroup::pRenderGroup->VramEnd				\
)


volatile uint32 UploadStalled, RenderStalled;



// handler called when an upload finishes

int GifHandler(int Cause)
{
	if (!sGroup::pUploadGroup)		// Mick:  Might not actually be anything to upload, if p_memview was uploading
		goto EXIT;
	
	// we've finished uploading a group
	sGroup::pUploadGroup = sGroup::pUploadGroup->pNext;
	while (sGroup::pUploadGroup && !sGroup::pUploadGroup->Used[!render::Field])		// if exists, but not used
	{
		sGroup::pUploadGroup = sGroup::pUploadGroup->pNext;	// then skip groups until reach end, or find one that is used
	}


#	ifdef __USE_PROFILER__
	Sys::DMAProfiler->PopContext();	 
#	endif // __USE_PROFILER__

	// is there another group to upload?
	if (sGroup::pUploadGroup)
	{

		
		// was there a stalled render?
		if (RenderStalled)
		{
			// kick off the stalled render
#	ifdef __USE_PROFILER__
				Sys::VUProfiler->PushContext( sGroup::pRenderGroup->profile_color );	   									
#	endif // __USE_PROFILER__
			START_RENDER();

			// is the next upload safe?
			if (NO_VRAM_CONFLICT())
			{
				// kick off the next upload
#	ifdef __USE_PROFILER__
				Sys::DMAProfiler->PushContext( sGroup::pUploadGroup->profile_color );	   									
#	endif // __USE_PROFILER__
				START_UPLOAD();
			}
			else
			{
				// stall the next upload
				UploadStalled = 1;
			}
		}

		else// there's a render in progress
		{
			// since we just finished an upload, then the render in progress MUST
			// be using the previously uploaded group
			// so there is no way that we can start a new upload until that render finishes.
			// so, stall the next upload
			UploadStalled = 1;
		}
	}

	else// no more groups to upload
	{
		// was the final render stalled?
		if (RenderStalled)
		{
			// kick it off
#	ifdef __USE_PROFILER__
				Sys::VUProfiler->PushContext(  sGroup::pRenderGroup->profile_color );	   									
#	endif // __USE_PROFILER__
			START_RENDER();
		}
	}

EXIT:
	ExitHandler();
	return 0;
}




// handler called when a render finishes (gs version)

int GsHandler(int Cause)
{
	// mask GS interrupts
	*GS_IMR = PackIMR(1,1,1,1);

	// reset SIGNAL event bit
	*GS_CSR = *GS_CSR & ~(uint64)0x30F | 1;

	// reset signal bit
	*GS_SIGLBLID &= ~(uint64)1;

	// maybe it was a false alarm...
	if (!sGroup::pRenderGroup)	// if nothing currently rendering, then we've finished!!
		goto EXIT;
	
	// we've finished rendering a group, so find the next group to render
	sGroup::pRenderGroup = sGroup::pRenderGroup->pNext;
	while (sGroup::pRenderGroup && !sGroup::pRenderGroup->Used[!render::Field])		// if exists, but not used
	{
		sGroup::pRenderGroup = sGroup::pRenderGroup->pNext;	// then skip groups until reach end, or find one that is used
	}

	#	ifdef __USE_PROFILER__
	Sys::VUProfiler->PopContext();	 
	#	endif // __USE_PROFILER__

	
	// is there another group to render?
	if (sGroup::pRenderGroup)
	{
		// was there a stalled upload?
		if (UploadStalled)
		{
			// is the next render waiting for the stalled upload?
			// (i.e., they both are the same group)
			if (sGroup::pRenderGroup == sGroup::pUploadGroup)
			{
				// kick off the stalled upload
#	ifdef __USE_PROFILER__
				Sys::DMAProfiler->PushContext(  sGroup::pUploadGroup->profile_color );	   									
#	endif // __USE_PROFILER__
				START_UPLOAD();

				// but stall the next render
				// as this upload group is needed for this render 
				RenderStalled = 2;
			}
			else
			{
				// kick off the next render, as it's from a previous group
#	ifdef __USE_PROFILER__
				Sys::VUProfiler->PushContext(  sGroup::pRenderGroup->profile_color );	   									
#	endif // __USE_PROFILER__
				START_RENDER();

				// is the next upload safe?
				if (NO_VRAM_CONFLICT())
				{
					// kick off the next upload
#	ifdef __USE_PROFILER__
					Sys::DMAProfiler->PushContext(  sGroup::pUploadGroup->profile_color );	   									
#	endif // __USE_PROFILER__
					START_UPLOAD();
				}
			}
		}

		// is there an upload in progress?
		// (Mick: Does it make sense to also check to see if the upload group is the same as the render group?
		// or is that always the case, and that's why Mike is not checking it?)
		else if (sGroup::pUploadGroup)
		{
			// stall the next render
			RenderStalled = 1;
		}

		// no uploads left
		else
		{
			// kick off the final render
#	ifdef __USE_PROFILER__
				Sys::VUProfiler->PushContext(  sGroup::pRenderGroup->profile_color );	   									
#	endif // __USE_PROFILER__
			START_RENDER();
		}
	}

EXIT:
	// re-enable GS interrupts
	*GS_IMR = PackIMR(0,1,1,1);

	ExitHandler();
	return 0;
}





// handler called when a render finishes (vif version)
// (Mick:  not currently used, ignore for now)
int Vif1Handler(int Cause)
{
	// make sure the mark register gives the go-ahead
	if (*VIF1_MARK != 1)
		goto EXIT;

	// maybe it was a false alarm...
	if (!sGroup::pRenderGroup)
		goto EXIT;
	
	// we've finished rendering a group
	sGroup::pRenderGroup = sGroup::pRenderGroup->pNext;		// get next group
	
	// is there another group to render?
	if (sGroup::pRenderGroup)
	{
		// was there a stalled upload?
		if (UploadStalled)
		{
			// is the next render waiting for the stalled upload?
			if (sGroup::pRenderGroup == sGroup::pUploadGroup)
			{
				// kick off the stalled upload
#	ifdef __USE_PROFILER__
				Sys::DMAProfiler->PushContext(  sGroup::pUploadGroup->profile_color );	   									
#	endif // __USE_PROFILER__
				START_UPLOAD();
				UploadStalled = 0;

				// but stall the next render
				RenderStalled = 2;
			}
			else
			{
				// kick off the next render
#	ifdef __USE_PROFILER__
				Sys::VUProfiler->PushContext(  sGroup::pRenderGroup->profile_color );	   									
#	endif // __USE_PROFILER__
				START_RENDER();

				// is the next upload safe?
				if (NO_VRAM_CONFLICT())
				{
					// kick off the next upload
#	ifdef __USE_PROFILER__
					Sys::DMAProfiler->PushContext(  sGroup::pUploadGroup->profile_color );	   									
#	endif // __USE_PROFILER__
					START_UPLOAD();
					UploadStalled = 0;
				}
			}
		}

		// is there an upload in progress?
		else if (sGroup::pUploadGroup)
		{
			// stall the next render
			RenderStalled = 1;
		}

		// no uploads left
		else
		{
			// kick off the final render
#	ifdef __USE_PROFILER__
				Sys::VUProfiler->PushContext(  sGroup::pRenderGroup->profile_color );	   									
#	endif // __USE_PROFILER__
			START_RENDER();
		}
	}

EXIT:
	ExitHandler();
	return 0;
}




} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/interrupts.h
================================================
#ifndef __INTERRUPTS_H
#define __INTERRUPTS_H

namespace NxPs2
{

int TextureSyncHandler(int Cause);
int DMATextureSyncHandler(int Cause);
int GifHandler(int Cause);
int GsHandler(int Cause);
int Vif1Handler(int Cause);

extern volatile uint32 UploadStalled, RenderStalled;

// Inlines

// If you need to use floating point in an interrupt, you MUST use the functions saveFloatRegs()
// and restoreFloatRegs() or some equivalent.

//----------------------------------------------------------------------------
//
inline void saveFloatRegs(unsigned int *buffer)
{
    asm volatile ("
      swc1    $f00,0x00(%0)
      swc1    $f01,0x04(%0)
      swc1    $f02,0x08(%0)
      swc1    $f03,0x0c(%0)
      swc1    $f04,0x10(%0)
      swc1    $f05,0x14(%0)
      swc1    $f06,0x18(%0)
      swc1    $f07,0x1c(%0)

      swc1    $f08,0x20(%0)
      swc1    $f09,0x24(%0)
      swc1    $f10,0x28(%0)
      swc1    $f11,0x2c(%0)
      swc1    $f12,0x30(%0)
      swc1    $f13,0x34(%0)
      swc1    $f14,0x38(%0)
      swc1    $f15,0x3c(%0)

      swc1    $f16,0x40(%0)
      swc1    $f17,0x44(%0)
      swc1    $f18,0x48(%0)
      swc1    $f19,0x4c(%0)
      swc1    $f20,0x50(%0)
      swc1    $f21,0x54(%0)
      swc1    $f22,0x58(%0)
      swc1    $f23,0x5c(%0)

      swc1    $f24,0x60(%0)
      swc1    $f25,0x64(%0)
      swc1    $f26,0x68(%0)
      swc1    $f27,0x6c(%0)
      swc1    $f28,0x70(%0)
      swc1    $f29,0x74(%0)
      swc1    $f30,0x78(%0)
      swc1    $f31,0x7c(%0)

    ": : "r" (buffer));
}

//----------------------------------------------------------------------------
//
inline void restoreFloatRegs(unsigned int *buffer)
{
    asm volatile ("
      lwc1    $f00,0x00(%0)
      lwc1    $f01,0x04(%0)
      lwc1    $f02,0x08(%0)
      lwc1    $f03,0x0c(%0)
      lwc1    $f04,0x10(%0)
      lwc1    $f05,0x14(%0)
      lwc1    $f06,0x18(%0)
      lwc1    $f07,0x1c(%0)

      lwc1    $f08,0x20(%0)
      lwc1    $f09,0x24(%0)
      lwc1    $f10,0x28(%0)
      lwc1    $f11,0x2c(%0)
      lwc1    $f12,0x30(%0)
      lwc1    $f13,0x34(%0)
      lwc1    $f14,0x38(%0)
      lwc1    $f15,0x3c(%0)

      lwc1    $f16,0x40(%0)
      lwc1    $f17,0x44(%0)
      lwc1    $f18,0x48(%0)
      lwc1    $f19,0x4c(%0)
      lwc1    $f20,0x50(%0)
      lwc1    $f21,0x54(%0)
      lwc1    $f22,0x58(%0)
      lwc1    $f23,0x5c(%0)

      lwc1    $f24,0x60(%0)
      lwc1    $f25,0x64(%0)
      lwc1    $f26,0x68(%0)
      lwc1    $f27,0x6c(%0)
      lwc1    $f28,0x70(%0)
      lwc1    $f29,0x74(%0)
      lwc1    $f30,0x78(%0)
      lwc1    $f31,0x7c(%0)

    ": : "r" (buffer));
}

} // namespace NxPs2


#endif // __INTERRUPTS_H



================================================
FILE: Code/Gfx/NGPS/NX/light.cpp
================================================
#include 
#include "light.h"

namespace NxPs2
{


// Statics
CLightGroup		CLightGroup::s_default_lights;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CLightGroup::CLightGroup()
{
	if (this != &s_default_lights)	   // can't initialize from itself
	{
		// Don't use new lights unless someone sets them
		m_flags = 0;
	
		// Init to default lights
		m_light_vector = s_default_lights.m_light_vector;
	
		m_diffuse_base_color = s_default_lights.m_diffuse_base_color;
		m_diffuse_mod_color = s_default_lights.m_diffuse_base_color;
	
		m_ambient_base_color = s_default_lights.m_ambient_base_color;
		m_ambient_mod_color = s_default_lights.m_ambient_base_color;
	}
	else
	{
		m_light_vector[0].Set(0,-1,0);
		m_light_vector[1].Set(0,-1,0);
		m_light_vector[2].Set(0,-1,0);
		m_diffuse_base_color[0].Set() ;
		m_diffuse_base_color[1].Set() ;
		m_diffuse_base_color[2].Set() ;
		m_diffuse_mod_color[0].Set()  ;
		m_diffuse_mod_color[1].Set()  ;
		m_diffuse_mod_color[2].Set()  ;
		m_ambient_base_color.Set() ;
		m_ambient_mod_color.Set()  ;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CLightGroup::~CLightGroup()
{
}


} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/light.h
================================================
#ifndef __LIGHT_H
#define __LIGHT_H

#include	
#include	

namespace NxPs2
{

// Forward declarations
class CLightGroup;

#if 0
class CBaseLightGroup
{
public:
								CBaseLightGroup();
	virtual						~CBaseLightGroup();

	virtual const Mth::Vector &	GetDirection(int idx) const;

	virtual const Mth::Vector &	GetAmbientColor() const;
	virtual const Mth::Vector &	GetDiffuseColor(int idx) const;

protected:
	// Directional light vectors
	Mth::Vector					m_light_vector[MAX_DIFFUSE_LIGHTS];

	// Light base colors
	Mth::Vector 				m_diffuse_base_color[MAX_DIFFUSE_LIGHTS];
	Mth::Vector 				m_ambient_base_color;

	friend CLightGroup;
};
#endif

class CLightGroup/* : public CBaseLightGroup*/
{

	friend class CVu1Context;
public:
	// Constants
	enum
	{
		MAX_DIFFUSE_LIGHTS = 3,
	};

								CLightGroup();
								~CLightGroup();
	//virtual						~CLightGroup();

	const Mth::Vector &			GetDirection(int idx) const;
	//virtual const Mth::Vector &	GetDirection(int idx) const;

	const Mth::Vector &			GetBaseAmbientColor() const;
	const Mth::Vector &			GetBaseDiffuseColor(int idx) const;

	const Mth::Vector &			GetAmbientColor() const;
	const Mth::Vector &			GetDiffuseColor(int idx) const;
	//virtual const Mth::Vector &	GetAmbientColor() const;
	//virtual const Mth::Vector &	GetDiffuseColor(int idx) const;

	void						SetDirection(int idx, const Mth::Vector & direction);
	void						SetBaseAmbientColor(const Mth::Vector & color);
	void						SetBaseDiffuseColor(int idx, const Mth::Vector & color);

	// Enable lights
	void						EnableAmbientLight(bool enable);
	void						EnableDiffuseLight(int idx, bool enable);
	bool						IsAmbientLightEnabled() const;
	bool						IsDiffuseLightEnabled(int idx) const;

	// Set brightness of lights
	bool						SetAmbientBrightness(float brightness);
	bool						SetDiffuseBrightness(int idx, float brightness);

	// Default lights
	static const Mth::Vector &	sGetDefaultDirection(int idx);
	static const Mth::Vector &	sGetDefaultAmbientColor();
	static const Mth::Vector &	sGetDefaultDiffuseColor(int idx);

	static void					sSetDefaultDirection(int idx, const Mth::Vector & direction);
	static void					sSetDefaultAmbientColor(const Mth::Vector & color);
	static void					sSetDefaultDiffuseColor(int idx, const Mth::Vector & color);

protected:
	// Internal flags.
	enum {
		mUSE_AMBIENT				= 0x0001,					// Use ambient light
		mUSE_DIFFUSE_0				= 0x0002,					// Use diffuse light 0
		mUSE_DIFFUSE_1				= 0x0004,					// rest of diffuse must follow 0
		mUSE_DIFFUSE_2				= 0x0008,					// rest of diffuse must follow 0
		mMODULATING_AMBIENT			= 0x0010,					// setting ambient brightness
		mMODULATING_DIFFUSE_0		= 0x0020,					// setting diffuse brightness 0
		mMODULATING_DIFFUSE_1		= 0x0040,					// rest of diffuse must follow 0
		mMODULATING_DIFFUSE_2		= 0x0040,					// rest of diffuse must follow 0
	};

	uint32					m_flags;

	// Directional light vectors
	Mth::Vector				m_light_vector[MAX_DIFFUSE_LIGHTS];

	// Light base colors
	Mth::Vector 			m_diffuse_base_color[MAX_DIFFUSE_LIGHTS];
	Mth::Vector 			m_ambient_base_color;

	// Light modulated colors (modified base colors)
	Mth::Vector 			m_diffuse_mod_color[MAX_DIFFUSE_LIGHTS];
	Mth::Vector 			m_ambient_mod_color;

	// Default lights
//	static Mth::Vector		s_default_directions[MAX_DIFFUSE_LIGHTS];
//	static CBaseLightGroup	s_default_colors;
	static CLightGroup		s_default_lights;

}; // class CLightGroup

// Inline functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector &		CLightGroup::GetDirection(int idx) const
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	if (m_flags & (mUSE_DIFFUSE_0 << idx))
	{
		return m_light_vector[idx];
	} else {
		return s_default_lights.m_light_vector[idx];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector &		CLightGroup::GetBaseAmbientColor() const
{
	if (m_flags & mUSE_AMBIENT)
	{
		return m_ambient_base_color;
	} else {
		return s_default_lights.m_ambient_base_color;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector &		CLightGroup::GetBaseDiffuseColor(int idx) const
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	if (m_flags & (mUSE_DIFFUSE_0 << idx))
	{
		return m_diffuse_base_color[idx];
	} else {
		return s_default_lights.m_diffuse_base_color[idx];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector &		CLightGroup::GetAmbientColor() const
{
	if (m_flags & (mUSE_AMBIENT | mMODULATING_AMBIENT))
	{
		return m_ambient_mod_color;
	} else {
		return s_default_lights.m_ambient_base_color;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector &		CLightGroup::GetDiffuseColor(int idx) const
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	if (m_flags & ((mUSE_DIFFUSE_0 | mMODULATING_DIFFUSE_0) << idx))
	{
		return m_diffuse_mod_color[idx];
	} else {
		return s_default_lights.m_diffuse_base_color[idx];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CLightGroup::SetDirection(int idx, const Mth::Vector & direction)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);
	//Dbg_Assert(m_flags & (mUSE_DIFFUSE_0 << idx));
	m_flags |= (mUSE_DIFFUSE_0 << idx);  // turn on light if it isn't already on

	m_light_vector[idx] = direction;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CLightGroup::SetBaseAmbientColor(const Mth::Vector & color)
{
	//Dbg_Assert(m_flags & mUSE_AMBIENT);
// Mick:  turning on and off lights should be done ABOVE the p_line
// Mick:	m_flags |= mUSE_AMBIENT;		// turn on light if it isn't already on

	m_ambient_base_color = color;

	if (!(m_flags & mMODULATING_AMBIENT))
	{
		m_ambient_mod_color = color;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CLightGroup::SetBaseDiffuseColor(int idx, const Mth::Vector & color)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);
	//Dbg_Assert(m_flags & (mUSE_DIFFUSE_0 << idx));
// Mick:  turning on and off lights should be done ABOVE the p_line
// Mick:	m_flags |= (mUSE_DIFFUSE_0 << idx);  // turn on light if it isn't already on

	m_diffuse_base_color[idx] = color;

	if (!(m_flags & (mMODULATING_DIFFUSE_0 << idx)))
	{
		m_diffuse_mod_color[idx] = color;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CLightGroup::EnableAmbientLight(bool enable)
{
	if (enable)
	{
		m_flags |= mUSE_AMBIENT;
	} else {
		m_flags &= ~mUSE_AMBIENT;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CLightGroup::EnableDiffuseLight(int idx, bool enable)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	if (enable)
	{
		m_flags |= (mUSE_DIFFUSE_0 << idx);
	} else {
		m_flags &= ~(mUSE_DIFFUSE_0 << idx);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool					CLightGroup::IsAmbientLightEnabled() const
{
	return (m_flags & mUSE_AMBIENT);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool					CLightGroup::IsDiffuseLightEnabled(int idx) const
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	return (m_flags & (mUSE_DIFFUSE_0 << idx));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool					CLightGroup::SetAmbientBrightness(float brightness)
{
	m_ambient_mod_color = GetBaseAmbientColor() * brightness;

	m_flags |= mMODULATING_AMBIENT;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool					CLightGroup::SetDiffuseBrightness(int idx, float brightness)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	m_diffuse_mod_color[idx] = GetBaseDiffuseColor(idx) * brightness;

	m_flags |= (mMODULATING_DIFFUSE_0 << idx);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector &		CLightGroup::sGetDefaultDirection(int idx)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	return s_default_lights.m_light_vector[idx];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector &		CLightGroup::sGetDefaultAmbientColor()
{
	return s_default_lights.m_ambient_base_color;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Vector &		CLightGroup::sGetDefaultDiffuseColor(int idx)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	return s_default_lights.m_diffuse_base_color[idx];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CLightGroup::sSetDefaultDirection(int idx, const Mth::Vector & direction)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	s_default_lights.m_light_vector[idx] = direction;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CLightGroup::sSetDefaultAmbientColor(const Mth::Vector & color)
{
	s_default_lights.m_ambient_base_color = color;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CLightGroup::sSetDefaultDiffuseColor(int idx, const Mth::Vector & color)
{
	Dbg_Assert(idx < MAX_DIFFUSE_LIGHTS);

	s_default_lights.m_diffuse_base_color[idx] = color;
}





} // namespace NxPs2



#endif // __LIGHT_H



================================================
FILE: Code/Gfx/NGPS/NX/line.cpp
================================================
#include 
#include "line.h"
#include "render.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gif.h"
#include "gs.h"
#include "sprite.h"
#include "switches.h"
#include "vu1code.h"


namespace NxPs2
{


void BeginLines2D(uint32 rgba)
{
	// dma tag
	dma::BeginTag(dma::cnt, 0);

	vu1::Buffer = vu1::Loc;

	// GS context
	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::ALPHA_1, PackALPHA(0,1,0,1,0));
	gs::Reg1(gs::RGBAQ,   (uint64)rgba);
	gs::EndPrim(0);

	// unpack giftag
	vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
	gif::BeginTagImmediate(gs::XYZ2, 1, PACKED, LINE|ABE, 1, VU1_ADDR(GSPrim));

	// begin unpack for coordinates
	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
}



void DrawLine2D(float x0, float y0, float z0, float x1, float y1, float z1)
{
	vif::StoreV4_32(XOFFSET+(sint32)((x0 * SDraw2D::GetScreenScaleX() * 16.0f)),
					YOFFSET+(sint32)((y0 * SDraw2D::GetScreenScaleY() * 16.0f)),0xFFFFFF,0);
	vif::StoreV4_32(XOFFSET+(sint32)((x1 * SDraw2D::GetScreenScaleX() * 16.0f)),
					YOFFSET+(sint32)((y1 * SDraw2D::GetScreenScaleY() * 16.0f)),0xFFFFFF,0);

	if (((dma::pLoc - gif::pTag)>>4) >= 250)
	{
		vif::EndUNPACK();
		gif::EndTagImmediate(1);
		vif::MSCAL(VU1_ADDR(Parser));
		vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
		gif::BeginTagImmediate(gs::XYZ2, 1, PACKED, LINE|ABE, 1, VU1_ADDR(GSPrim));
		vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
	}

}


void EndLines2D(void)
{
	vif::EndUNPACK();
	gif::EndTagImmediate(1);
	vif::MSCAL(VU1_ADDR(Parser));
	dma::EndTag();
}



void BeginLines3D(uint32 rgba)
{
	
	// dma tag  - this will encompass ALL the lines up to the next EndLines3D
	dma::BeginTag(dma::cnt, 0);

	vu1::Buffer = vu1::Loc;

	// VU context, uploading the view transform data
	vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));	  	// Begin packed register upload to VF10 
	//vu1::StoreVec(*(Vec *)&render::ViewportScale);			// VF10
	vu1::StoreVec(*(Vec *)&render::InverseIntViewportScale);			// VF10

//	vu1::StoreVec(ViewportOffset);							// VF11

//	float z_push = 2.40741243e-34;							// was e-35
//	vif::StoreV4_32F(render::ViewportOffset[0],
//					 render::ViewportOffset[1],
//					 render::ViewportOffset[2]+z_push,
//					 render::ViewportOffset[3]);			// VF11
	
	Mth::Vector temp_inv_viewport_offset;
	temp_inv_viewport_offset[0] = -render::InverseIntViewportScale[0] * render::IntViewportOffset[0] * render::InverseIntViewportScale[3];
	temp_inv_viewport_offset[1] = -render::InverseIntViewportScale[1] * render::IntViewportOffset[1] * render::InverseIntViewportScale[3];
	temp_inv_viewport_offset[2] = -render::InverseIntViewportScale[2] * (render::IntViewportOffset[2]+1.0e-34f) * render::InverseIntViewportScale[3];
	temp_inv_viewport_offset[3] = 0.0f;
	vu1::StoreVec(*(Vec *)&temp_inv_viewport_offset);

	vu1::StoreMat(*(Mat *)&render::AdjustedWorldToIntViewport);	// VF12-15
	vu1::EndPrim(0);										// End upload

	// GS context, setting color
	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::ALPHA_1, PackALPHA(0,1,0,1,0));
	gs::Reg1(gs::RGBAQ,   (uint64)rgba);
	gs::EndPrim(0);

	// all lines will be rendered with simple culling
	vif::ITOP(CULL);

	// unpack giftag
	vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
	gif::BeginTagImmediate(gs::XYZ2, 1, PACKED, LINE|ABE, 1, VU1_ADDR(Line));

	// offset mode
	vif::STMOD(1);

	// begin unpack for coordinates
	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
}


void	ChangeLineColor(uint32 rgba)
{

// End the previous batch of lines
		EndLines3D();
		
// Like starting a new batch, but witout the VU context		
		dma::BeginTag(dma::cnt, 0);

		// GS context
		// Sets up the GS registers for color and alpha
		gs::BeginPrim(ABS,0,0);
		gs::Reg1(gs::ALPHA_1, PackALPHA(0,1,0,1,0));
		gs::Reg1(gs::RGBAQ,   (uint64)rgba);
		gs::EndPrim(0);
		
		vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
		gif::BeginTagImmediate(gs::XYZ2, 1, PACKED, LINE|ABE, 1, VU1_ADDR(Line));
		vif::STMOD(1);
		vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
	
}


void DrawLine3D(float x0, float y0, float z0, float x1, float y1, float z1)
{
	vif::StoreV4_32((sint32)(x0*SUB_INCH_PRECISION), (sint32)(y0*SUB_INCH_PRECISION), (sint32)(z0*SUB_INCH_PRECISION), 0);
	vif::StoreV4_32((sint32)(x1*SUB_INCH_PRECISION), (sint32)(y1*SUB_INCH_PRECISION), (sint32)(z1*SUB_INCH_PRECISION), 0);

	if (((dma::pLoc - gif::pTag)>>4) >= 250)
	{
		vif::EndUNPACK();
		gif::EndTagImmediate(1);
		vif::STMOD(0);
		vif::MSCAL(VU1_ADDR(Parser));
		vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
		gif::BeginTagImmediate(gs::XYZ2, 1, PACKED, LINE|ABE, 1, VU1_ADDR(Line));
		vif::STMOD(1);
		vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
	}

}


void EndLines3D(void)
{
	vif::EndUNPACK();

	if (vif::UnpackSize)
	{
		gif::EndTagImmediate(1);
		vif::STMOD(0);
		vif::MSCAL(VU1_ADDR(Parser));
	}
	else
		dma::pLoc -= 24;

	dma::EndTag();
}

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/line.h
================================================
#ifndef __LINE_H
#define __LINE_H


namespace NxPs2
{


void BeginLines2D(uint32 rgba);
void DrawLine2D(float x0, float y0, float z0, float x1, float y1, float z1);
void EndLines2D(void);
void BeginLines3D(uint32 rgba);
void ChangeLineColor(uint32 rgba);
void DrawLine3D(float x0, float y0, float z0, float x1, float y1, float z1);
void EndLines3D(void);

} // namespace NxPs2


#endif // __LINE_H



================================================
FILE: Code/Gfx/NGPS/NX/loadscreen.cpp
================================================
#include 
#include 
#include 
#include 
#include "dma.h"
#include "vif.h"
#include "gif.h"
#include "gs.h"
#include "dmacalls.h"
#include "group.h"
#include "nx_init.h"
#include "render.h"
#include "interrupts.h"
#include "switches.h"
#include "sprite.h"


namespace NxPs2
{


// Callback handler for loading bar
static int s_loading_bar_handler_id = -1;

/////////////////////////////////////////////////////////////////////////////
// Loading bar

static uint128 p_VifData[64];

int			gLoadBarTotalFrames;		// Number of frames it takes for loading bar to go to 100%
int			gLoadBarNumFrames;			// Number of frames so far
float		gLoadBarX = 150.0f;			// Bar position
float		gLoadBarY = 400.0f;
float		gLoadBarWidth = 340.0f;		// Bar size
float		gLoadBarHeight = 20.0f;
int			gLoadBarStartColor[4] = {  255,    0,   0,  128 };
int			gLoadBarDeltaColor[4] = { -255,  255,   0,  128 };
float		gLoadBarBorderWidth = 5.0f;	// Border width
float		gLoadBarBorderHeight = 5.0f;	// Border height
int			gLoadBarBorderColor[4] = {  40, 40, 40, 128 };


static int s_loading_bar_handler(int ca)
{
	// If writing to a variable, make sure it is volatile (or game will crash)
	
	// inc frame count    
	gLoadBarNumFrames++;

	//-----------------------------------------
	//		DRAW BAR
	//-----------------------------------------
	sceGsSyncPath(0, 0);
	if (sGroup::pRenderGroup)	// Can't handle this condition
	{
		ExitHandler();
		return 0;
	}

	// Save floats
    unsigned int floatBuffer[32];
    saveFloatRegs(floatBuffer);

	// Save all these because we could already be in the middle of building packets
	uint8*	old_dma_loc = dma::pLoc;		  
	uint8*	old_dma_tag = dma::pTag;
	uint8*	old_gif_tag = gif::pTag;
	uint8*	old_vif_code = vif::pVifCode;

	dma::pLoc = (uint8 *) p_VifData;

	// begin Path3 subroutine
	dma::BeginTag(dma::cnt, 0);

	// send everything PATH3
	//vif::BeginDIRECT();
	vif::NOP();
	vif::NOP();

	// GS prim sets up all the registers
	gif::BeginTag2(gs::A_D, 1, PACKED, 0, 0);
	gs::Reg2(gs::ALPHA_1,		PackALPHA(0,1,0,1,128));
	gs::Reg2(gs::CLAMP_1,		PackCLAMP(REPEAT,REPEAT,0,0,0,0));
	gs::Reg2(gs::COLCLAMP,		PackCOLCLAMP(1));
	//gs::Reg2(gs::DTHE,			PackDTHE(0));
	gs::Reg2(gs::DIMX,			PackDIMX(-4,0,-3,1,2,-2,3,-1,-3,1,-4,0,3,-1,2,-2));
	gs::Reg2(gs::DTHE,			PackDTHE(1));
	//gs::Reg2(gs::FBA_1,			PackFBA(0));
	gs::Reg2(gs::FRAME_1,		PackFRAME(DISPLAY_START,HRES/64,PSMCT16S,0xFF000000));	// draw directly to display buffer
	gs::Reg2(gs::PRMODECONT,	PackPRMODECONT(1));
	gs::Reg2(gs::SCANMSK,		PackSCANMSK(0));
	gs::Reg2(gs::SCISSOR_1,	 	PackSCISSOR(0,HRES - 1,0,VRES - 1));
	gs::Reg2(gs::TEST_1,		PackTEST(0,0,0,0,0,0,1,ZALWAYS));
	gs::Reg2(gs::XYOFFSET_1,	PackXYOFFSET(0x0, 0x0));
	gs::Reg2(gs::ZBUF_1,		PackZBUF(ZBUFFER_START,PSMZ24,1));
	gif::EndTag2(1);

	// Find scaled values (mainly for PAL)
	NxPs2::SDraw2D::CalcScreenScale();		// May not have been calculated yet
	float scaledBarX = gLoadBarX * NxPs2::SDraw2D::GetScreenScaleX();
	float scaledBarY = gLoadBarY * NxPs2::SDraw2D::GetScreenScaleY();
	float scaledBarWidth = gLoadBarWidth * NxPs2::SDraw2D::GetScreenScaleX();
	float scaledBarHeight = gLoadBarHeight * NxPs2::SDraw2D::GetScreenScaleY();
	float scaledBarBorderWidth = gLoadBarBorderWidth * NxPs2::SDraw2D::GetScreenScaleX();
	float scaledBarBorderHeight = gLoadBarBorderHeight * NxPs2::SDraw2D::GetScreenScaleY();

	int cur_width = (int) ((scaledBarWidth - 1.0f) * 16.0f);
	if (gLoadBarNumFrames < gLoadBarTotalFrames)
	{
		cur_width = (cur_width * gLoadBarNumFrames) / gLoadBarTotalFrames;
	}

	int x1 = (int) (scaledBarX * 16.0f);
	int y1 = (int) (scaledBarY * 16.0f);
	int x2 = x1 + cur_width;
	int y2 = y1 + (int) ((scaledBarHeight - 1.0f) * 16.0f);

	int end_color[4];
	if (gLoadBarNumFrames < gLoadBarTotalFrames)
	{
		end_color[0] = gLoadBarStartColor[0] + ((gLoadBarDeltaColor[0] * gLoadBarNumFrames) / gLoadBarTotalFrames);
		end_color[1] = gLoadBarStartColor[1] + ((gLoadBarDeltaColor[1] * gLoadBarNumFrames) / gLoadBarTotalFrames);
		end_color[2] = gLoadBarStartColor[2] + ((gLoadBarDeltaColor[2] * gLoadBarNumFrames) / gLoadBarTotalFrames);
		end_color[3] = gLoadBarStartColor[3] + ((gLoadBarDeltaColor[3] * gLoadBarNumFrames) / gLoadBarTotalFrames);
	} else {
		end_color[0] = gLoadBarStartColor[0] + gLoadBarDeltaColor[0];
		end_color[1] = gLoadBarStartColor[1] + gLoadBarDeltaColor[1];
		end_color[2] = gLoadBarStartColor[2] + gLoadBarDeltaColor[2];
		end_color[3] = gLoadBarStartColor[3] + gLoadBarDeltaColor[3];
	}

	int border_x1 = x1 - (int) (scaledBarBorderWidth * 16.0f);
	int border_y1 = y1 - (int) (scaledBarBorderHeight * 16.0f);
	int border_x2 = x1 + (int) (((scaledBarWidth - 1) * 16.0f) + (scaledBarBorderWidth * 16.0f));
	int border_y2 = y2 + (int) (scaledBarBorderHeight * 16.0f);

	gif::BeginTag2(gs::XYZ2<<4|gs::RGBAQ, 2, PACKED, TRISTRIP|IIP|FST|AA1/*|ABE*/, 1);

	// Border
	vif::StoreV4_32(gLoadBarBorderColor[0], gLoadBarBorderColor[1],
					gLoadBarBorderColor[2], gLoadBarBorderColor[3]);	// RGBAQ
	vif::StoreV4_32(border_x1, border_y1, 0, 0x8000);					// XYZ2
	vif::StoreV4_32(gLoadBarBorderColor[0], gLoadBarBorderColor[1],
					gLoadBarBorderColor[2], gLoadBarBorderColor[3]);	// RGBAQ
	vif::StoreV4_32(border_x1, border_y2, 0, 0x8000);					// XYZ2
	vif::StoreV4_32(gLoadBarBorderColor[0], gLoadBarBorderColor[1],
					gLoadBarBorderColor[2], gLoadBarBorderColor[3]);	// RGBAQ
	vif::StoreV4_32(border_x2, border_y1, 0, 0);						// XYZ2
	vif::StoreV4_32(gLoadBarBorderColor[0], gLoadBarBorderColor[1],
					gLoadBarBorderColor[2], gLoadBarBorderColor[3]);	// RGBAQ
	vif::StoreV4_32(border_x2, border_y2, 0, 0);						// XYZ2

	// Bar
	vif::StoreV4_32(gLoadBarStartColor[0], gLoadBarStartColor[1],
					gLoadBarStartColor[2], gLoadBarStartColor[3]);			// RGBAQ
	vif::StoreV4_32( x1,  y1, 0, 0x8000);									// XYZ2
	vif::StoreV4_32(gLoadBarStartColor[0], gLoadBarStartColor[1],
					gLoadBarStartColor[2], gLoadBarStartColor[3]);			// RGBAQ
	vif::StoreV4_32( x1,  y2, 0, 0x8000);									// XYZ2
	vif::StoreV4_32(end_color[0], end_color[1], end_color[2], end_color[3]);// RGBAQ
	vif::StoreV4_32( x2,  y1, 0, 0);										// XYZ2
	vif::StoreV4_32(end_color[0], end_color[1], end_color[2], end_color[3]);// RGBAQ
	vif::StoreV4_32( x2,  y2, 0, 0);										// XYZ2

	gif::EndTag2(1);

	//vif::EndDIRECT();

	dma::EndTag();

	// done
	iFlushCache(WRITEBACK_DCACHE);

	// kick dma to upload textures from this group
	*D2_QWC = 0;						// must zero QWC because the first action will be to use current MADR & QWC
	*D2_TADR = (uint)p_VifData;			// address of 1st tag
	*D2_CHCR = 0x145;					// start transfer, tte=1, chain mode, from memory
	sceGsSyncPath(0, 0);

	// Restore used variables
	dma::pLoc = old_dma_loc;		  
	dma::pTag = old_dma_tag;
	gif::pTag = old_gif_tag;
	vif::pVifCode = old_vif_code;

    // restore floats
    restoreFloatRegs(floatBuffer);

	ExitHandler();			// Mick: Sony requires interrupts to end with an ExitHandler() call
							// this enables interrupts	
	return 0;
}

void StartLoadingBar(int frames)
{
	//Dbg_Assert(s_loading_bar_handler_id < 0);

	if (s_loading_bar_handler_id < 0)
	{
		gLoadBarTotalFrames = frames;
		gLoadBarNumFrames = 0;

		s_loading_bar_handler_id = AddIntcHandler(INTC_VBLANK_S, s_loading_bar_handler, -1);
		Dbg_MsgAssert (( s_loading_bar_handler_id >= 0 ),( "Failed to add loading bar handler" )); 
	}
}

void RemoveLoadingBar()
{
	if (s_loading_bar_handler_id >= 0)
	{
		RemoveIntcHandler(INTC_VBLANK_S, s_loading_bar_handler_id);
		s_loading_bar_handler_id = -1;
	}
}


} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/loadscreen.h
================================================
#ifndef __NXPS2_LOADSCREEN_H
#define __NXPS2_LOADSCREEN_H


namespace NxPs2
{

// Enable and disable loading bar interrupt
void StartLoadingBar(int frames);
void RemoveLoadingBar();


// Loading bar data
extern int		gLoadBarTotalFrames;	// Number of frames it takes for loading bar to go to 100%
extern int		gLoadBarNumFrames;		// Number of frames so far
extern float	gLoadBarX;				// Bar position
extern float	gLoadBarY;
extern float	gLoadBarWidth;			// Bar size
extern float	gLoadBarHeight;
extern int		gLoadBarStartColor[4];	// 0 = red; 1 = green; 2 = blue; 3 = alpha
extern int		gLoadBarDeltaColor[4];	// 0 = delta red; 1 = delta green; 2 = delta blue; 3 = delta alpha
extern float	gLoadBarBorderWidth;	// Border width
extern float	gLoadBarBorderHeight;	// Border height
extern int		gLoadBarBorderColor[4];	// 0 = red; 1 = green; 2 = blue; 3 = alpha



} // namespace NxPs2

#endif // __NXPS2_LOADSCREEN_H


================================================
FILE: Code/Gfx/NGPS/NX/maintest.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "render.h"
#include "line.h"
#include "nx_init.h"
#include "chars.h"
#include "group.h"
#include "sprite.h"
#include "switches.h"
#include	"gfx/nx.h"
#include	"gfx/NxParticleMgr.h"
#include	"gfx/NxTextured3dPoly.h"
#include	"gfx/ngps/p_NxLightMan.h"


namespace Sys
{
	extern void Render_Profiler();		
}


namespace Gfx
{
	void DebugGfx_Draw( void );
}

namespace Tmr
{
	void VSync();	
}

namespace NxPs2
{

/*
void LineTest(void);
void CharTest(void);
*/

SFont *pMyFont1, *pMyFont2;



int maintest(int argc, char *argv[])
{
	return 0;
}




void test_init()
{
	InitialiseEngine();
}

// I'm forward declaring this, so the compiler does not try to inline it
volatile sGroup *get_render_group();
void	WaitForRendering();
void patch_texture_dma();


void	test_render_start_frame()
{

//------------------------------------------------------------

#if 0
// MICK: Note, this is done in the main loop now, for profiling reasons
	// wait for rendering completion
	NxPs2::WaitForRendering();
	// wait for vsync
	Tmr::VSync();
#endif
  
	// now the dma list has finished, we can swap dma buffers
	render::Field ^= 1;

	// kick off dma from previous frame (if there was one)
	if (render::Frame > 0)
		SendDisplayList();

}



// wait for pRenderGroup to be null
// note curius use of a seperate function to read pRenderGroup
// due to compler getting confused if we just read it directly
// even though it is volatile, it does not re-read it			   
void	WaitForRendering()
{
	while (get_render_group())
		;

	// Update the 2D DMA lists
	Update2DDMA();
	
}

void StuffAfterGSFinished()
{

	Nx::CPs2LightManager::sUpdateEngine();

	// particle systems effectively have their transform double-buffered for dma purposes
	// so now is the time to do the buffer flip by updating the transforms
	Nx::CEngine::sGetParticleManager()->UpdateParticles();

	// patch up texture DMA
	patch_texture_dma();	


}

					
volatile sGroup *get_render_group()
{
	return sGroup::pRenderGroup;
}



void	test_render(Mth::Matrix* camera_orient, Mth::Vector* camera_pos, float view_angle, float screen_aspect)
{
	//RenderPrologue();	 	// should this be in test_render_start_frame? (Garrett: Answer is no, it won't work)

	// render everything
	RenderWorld(camera_orient, camera_pos, view_angle, screen_aspect);
	//LineTest();
	//CharTest();
}

void	test_render_end_frame()
{
	SetTextWindow(0,HRES-1,0,VRES-1);						// a full-screen clipping window
	Gfx::DebugGfx_Draw( );

	// Make sure 2D VRAM is up-to-date
// Mick: Moved to CEngine::s_plat_render_world, which is earlier in the frame
// so it happens before we draw the texture splats.
//	Reallocate2DVRAM();

	SDraw2D::DrawAll();
	#ifdef		__USE_PROFILER__
	Sys::Render_Profiler();		
	#endif
	RenderEpilogue();
	
	// set frame for next render
	render::Frame++;
}



void LineTest(void)
{
/*	
	int xo,yo,zo;

	// do some 3D lines with r=0xFF, g=0x00, b=0x00, a=0x80
	BeginLines3D(0x800000FF);

	for (xo=-130; xo<=110; xo+=80)
		for (yo=-90; yo<=70; yo+=80)
			for (zo=-130; zo<=110; zo+=80)
			{
				DrawLine3D(-10+xo, 10+yo, 10+zo, 10+xo, 10+yo, 10+zo);
				DrawLine3D(-10+xo,-10+yo, 10+zo, 10+xo,-10+yo, 10+zo);
				DrawLine3D(-10+xo, 10+yo,-10+zo, 10+xo, 10+yo,-10+zo);
				DrawLine3D(-10+xo,-10+yo,-10+zo, 10+xo,-10+yo,-10+zo);
				
				DrawLine3D(-10+xo, 10+yo, 10+zo,-10+xo,-10+yo, 10+zo);
				DrawLine3D( 10+xo, 10+yo, 10+zo, 10+xo,-10+yo, 10+zo);
				DrawLine3D(-10+xo, 10+yo,-10+zo,-10+xo,-10+yo,-10+zo);
				DrawLine3D( 10+xo, 10+yo,-10+zo, 10+xo,-10+yo,-10+zo);
				
				DrawLine3D(-10+xo, 10+yo, 10+zo,-10+xo, 10+yo,-10+zo);
				DrawLine3D( 10+xo, 10+yo, 10+zo, 10+xo, 10+yo,-10+zo);
				DrawLine3D(-10+xo,-10+yo, 10+zo,-10+xo,-10+yo,-10+zo);
				DrawLine3D( 10+xo,-10+yo, 10+zo, 10+xo,-10+yo,-10+zo);
			}

	EndLines3D();

	// do some 2D lines with r=0xFF, g=0xFF, b=0xFF, a=0x80
	BeginLines2D(0x80FFFFFF);

	// "LINE"
	DrawLine2D(100,100,0, 100,150,0);
	DrawLine2D(100,150,0, 150,150,0);
	DrawLine2D(160,100,0, 160,150,0);
	DrawLine2D(170,100,0, 170,150,0);
	DrawLine2D(170,100,0, 220,150,0);
	DrawLine2D(220,100,0, 220,150,0);
	DrawLine2D(230,100,0, 230,150,0);
	DrawLine2D(230,100,0, 280,100,0);
	DrawLine2D(230,125,0, 260,125,0);
	DrawLine2D(230,150,0, 280,150,0);

	// "TEST"
	DrawLine2D(320,100,0, 370,100,0);
	DrawLine2D(345,100,0, 345,150,0);
	DrawLine2D(380,100,0, 380,150,0);
	DrawLine2D(380,100,0, 430,100,0);
	DrawLine2D(380,125,0, 410,125,0);
	DrawLine2D(380,150,0, 430,150,0);
	DrawLine2D(440,100,0, 490,100,0);
	DrawLine2D(440,125,0, 490,125,0);
	DrawLine2D(440,150,0, 490,150,0);
	DrawLine2D(440,100,0, 440,125,0);
	DrawLine2D(490,125,0, 490,150,0);
	DrawLine2D(500,100,0, 550,100,0);
	DrawLine2D(525,100,0, 525,150,0);

	EndLines2D();
*/
}




void CharTest(void)
{
#if 0
	float Width,Height;

	SetFont("small");
	QueryString("Hello World", &Width, &Height);	// width and height in pixels

	SetTextWindow(0,639,0,447);						// a full-screen clipping window

	BeginText(0x403070A0, 1.5);						// r=0xA0, g=0x70, b=0x30, a=0x40, scale=1.5
	DrawString("Score",  30, 30);
	DrawString("000000", 100, 30);
	EndText();

	BeginText(0x40A06080, 1.5);
	DrawString("Skate4", 520, 400);
	EndText();
#endif
}



} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/material.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "texture.h"
#include "material.h"
#include "dma.h"
#include "vif.h"
#include "gs.h"
#include "scene.h"
#include "switches.h"



namespace NxPs2
{


//----------------------------------------------------------------------------------------
//		L O A D   M A T E R I A L S
//----------------------------------------------------------------------------------------

void * LoadMaterials(void *pFile, sScene *pScene, uint32 texDictOffset)
{
	sMaterial *pMat;
	sTexture *pTex;
	uint32 TextureChecksum, GroupChecksum;
	uint32 MXL, MMAG, MMIN, K, L, NumSeqs, seq, NumKeys;
	int i;

	// get number of materials
	MEM_Read(&pScene->NumMaterials, sizeof(uint32), 1, pFile);

	// allocate storage for materials
	pScene->pMaterials = (sMaterial *)Mem::Malloc(pScene->NumMaterials*sizeof(sMaterial));
	Dbg_MsgAssert(pScene->pMaterials, ("couldn't allocate memory for materials"));

	// loop over materials
	for (i=0,pMat=pScene->pMaterials; iNumMaterials; i++,pMat++)
	{
		// get material checksum
		MEM_Read(&pMat->Checksum, sizeof(uint32), 1, pFile);

		if (sMaterial::Version >= 5)
		{
			// get material flags (moved up here in version 5)
			MEM_Read(&pMat->Flags, 1, sizeof(uint32), pFile);
		}

		// get Aref
		if (sMaterial::Version >= 2)
		{
			if (sMaterial::Version >= 3)
			{
				MEM_Read(&pMat->Aref, 1, 4, pFile);
			}
			else
			{
				MEM_Read(&pMat->Aref, 1, 1, pFile);
			}

		}
		else
		{
			pMat->Aref = 1;
		}

		if (sMaterial::Version >= 5)
		{
			if (pMat->Flags & MATFLAG_ANIMATED_TEX)
			{
				Dbg_Assert( 0 );
				/*
				NxAnimatedTexture* anim_tex;
	
				anim_tex = &material->m_Passes[0].m_AnimatedTexture;
				pOutFile->Write((const char*) &anim_tex->m_NumKeyframes, sizeof( int ), false);
				pOutFile->Write((const char*) &anim_tex->m_Period, sizeof( int ), false);
				pOutFile->Write((const char*) &anim_tex->m_Iterations, sizeof( int ), false);
				pOutFile->Write((const char*) &anim_tex->m_Phase, sizeof( int ), false);
				for( j = 0; j < anim_tex->m_NumKeyframes; j++ )
				{
					NxAnimatedTextureKeyframe* frame;
	
					frame = &anim_tex->m_Keyframes[j];
					pOutFile->Write((const char*) &frame->m_Time, sizeof( unsigned int ), false);
	
					tex_checksum = material->m_Passes[0].GetTextureChecksum( j, Utils::vPLATFORM_PS2 );
					pOutFile->Write((const char*) &tex_checksum, sizeof( unsigned long ), false);
				}*/
				
			}
			else
			{
				// get texture checksum
				MEM_Read(&TextureChecksum, 1, sizeof(uint32), pFile);
			}
		}
		else
		{
			// get texture checksum
			MEM_Read(&TextureChecksum, 1, sizeof(uint32), pFile);
		}
		

		if (sMaterial::Version >= 3)
		{
			// get group checksum
			MEM_Read(&GroupChecksum, 1, sizeof(uint32), pFile);

			GroupChecksum += texDictOffset;
		}

		if (sMaterial::Version < 5)
		{
			// get material flags (moved above in version 5 because the parser needs the flags
			// to correctly parse subsequent data
			MEM_Read(&pMat->Flags, 1, sizeof(uint32), pFile);
		}

		// get ALPHA register value
		MEM_Read(&pMat->RegALPHA, 1, sizeof(uint64), pFile);

		// get CLAMP register value
		if (sMaterial::Version >= 2)
		{
			uint32 ClampU,ClampV;
			MEM_Read(&ClampU, 1, sizeof(uint32), pFile);
			MEM_Read(&ClampV, 1, sizeof(uint32), pFile);
			pMat->RegCLAMP = PackCLAMP(ClampU,ClampV,0,0,0,0);
		}
		else
			pMat->RegCLAMP = PackCLAMP(REPEAT,REPEAT,0,0,0,0);

		// step past material colours, currently unsupported
		MEM_Seek(pFile, 36, SEEK_CUR);

		// step past uv wibble data, currently unsupported
		if (pMat->Flags & MATFLAG_UV_WIBBLE)
		{
			MEM_Seek(pFile, 40, SEEK_CUR);
		}

		// step past vc wibble data, currently unsupported
		if (pMat->Flags & MATFLAG_VC_WIBBLE)
		{
			MEM_Read(&NumSeqs, sizeof(uint32), 1, pFile);
			for (seq=0; seq= 3)
			{
				pTex = ResolveTextureChecksum(TextureChecksum, GroupChecksum);
				Dbg_MsgAssert(pTex, ("error: couldn't find texture checksum %08X with group checksum %08X\n",
																			TextureChecksum, GroupChecksum));
			}
			else
			{
				pTex = ResolveTextureChecksum(TextureChecksum);
				Dbg_MsgAssert(pTex, ("error: couldn't find texture checksum %08X\n", TextureChecksum));
			}

			// set texture pointer
			pMat->pTex = pTex;

			// get mipmap info
			MXL  = pTex->MXL;
			MEM_Read(&MMAG, sizeof(uint32), 1, pFile);
			MEM_Read(&MMIN, sizeof(uint32), 1, pFile);
			MEM_Read(&K, sizeof(uint32), 1, pFile);
			K = (int) ((*(float *)&K) * 16.0f) & 0xFFF;
			if (sMaterial::Version > 1)
				pMat->RegTEX1 = PackTEX1(0,MXL,MMAG,MMIN,0,0,K);
			else
			{
				MEM_Read(&L, sizeof(uint32), 1, pFile);
				pMat->RegTEX1 = PackTEX1(0,MXL,MMAG,MMIN,0,L,K);
			}
		}
		// otherwise just step past mipmap info
		else
		{
			pMat->pTex = 0;
			MEM_Seek(pFile, 12+4*(sMaterial::Version==1), SEEK_CUR);
		}

		// get reflection map scale values
		if (sMaterial::Version >= 4)
		{
			float temp;
			MEM_Read(&temp, sizeof(float), 1, pFile);
			pMat->RefMapScaleU = (sint16)(int)(temp * 256.0f);
			MEM_Read(&temp, sizeof(float), 1, pFile);
			pMat->RefMapScaleV = (sint16)(int)(temp * 256.0f);
		}

	}
	return pFile;
}


uint32 sMaterial::Version;


} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/material.h
================================================
#ifndef __MATERIAL_H
#define __MATERIAL_H


namespace NxPs2
{

// Material Flags
#define MATFLAG_UV_WIBBLE   	(1<<0)
#define MATFLAG_VC_WIBBLE   	(1<<1)
#define MATFLAG_TEXTURED    	(1<<2)
#define MATFLAG_ENVIRONMENT 	(1<<3)
#define MATFLAG_DECAL       	(1<<4)
#define MATFLAG_SMOOTH      	(1<<5)
#define MATFLAG_TRANSPARENT		(1<<6)
#define MATFLAG_ONE_SIDED 		(1<<7)
#define MATFLAG_INVISIBLE 		(1<<8)
#define MATFLAG_TWO_SIDED 		(1<<9)
#define MATFLAG_SPECULAR 		(1<<10)
#define MATFLAG_ANIMATED_TEX	(1<<11)
#define MATFLAG_FORCE_ALPHA		(1<<13)


struct sMaterial
{
	uint32	Flags;
	uint32	Checksum;
	struct	sTexture *pTex;
	uint64	RegALPHA, RegTEX1, RegCLAMP;
	uint8	Aref;
	float*	pUVWibbleInfo;
	uint32*	pVCWibbleInfo;
	sint16	RefMapScaleU, RefMapScaleV;

	static uint32 Version;
};


void * LoadMaterials(void *pFile, struct sScene *pScene, uint32 texDictOffset);


} // namespace NxPs2

#endif // __MATERIAL_H



================================================
FILE: Code/Gfx/NGPS/NX/mesh.cpp
================================================
#include 
#include 
#include 
#include 
#include "group.h"
#include "scene.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gif.h"
#include "gs.h"
#include "texture.h"
#include "material.h"
#include "mesh.h"
#include "vu1code.h"
#include "dmacalls.h"
#include "render.h"
#include 
#include 
#include 
#include 
										  
namespace NxPs2
{


#ifdef	__NOPT_ASSERT__
extern uint32		gPassMask1; 		// 1<<6 | 1<<7  (0x40, 0x80)
extern uint32		gPassMask0; 		// 1<<6 | 1<<7  (0x40, 0x80)
#endif 


#if 0

//----------------------------------------------------------------------------------------
//		Mesh Group Functions
//----------------------------------------------------------------------------------------

sMesh *sGroup::GetMeshArray()
{
	return &(Meshes[FirstMeshIndex]);
}


sGroup* NewMeshGroup(uint32 group_ID, uint32 num_meshes)
{
	uint32 i;
	int Group = -1;

	// resolve ID
	for (i=0; ipWeights;
	s_pWeightIndices = pParams->pWeightIndices;
	s_pBoneScales = pParams->pBoneScales;
	s_pBonePositions = pParams->pBonePositions;
	s_currentVertIndex = 0;
}

void DisableMeshScaling()
{
	s_meshScalingEnabled = false;
	s_pWeights = NULL;
	s_pWeightIndices = NULL;
	s_pBoneScales = NULL;
	s_pBonePositions = NULL;
	s_currentVertIndex = 0;
}

//----------------------------------------------------------------------------------------
//		L O A D   M E S H E S
//----------------------------------------------------------------------------------------

void * LoadMeshes(void *pFile, sScene *pScene, bool IsSkin, bool IsInstanceable, uint32 texDictOffset)
{
	int i;

	// initialise sScene bounding sphere to avoid rewriting it multiple times
	pScene->Sphere[3] = -1.0f;

	int skinnedVertexCount=0;

	// iterate over mesh groups
	for (i=0; iNumGroups; i++)
	{
		pFile = LoadMeshGroup(pFile, pScene, IsSkin, IsInstanceable, texDictOffset, skinnedVertexCount);
	}
	// Check hierarchy data
	int num_hier = -1;
	MEM_Read(&num_hier, sizeof(int), 1, pFile);
	Dbg_MsgAssert(num_hier == 0, ("num_hier = %d", num_hier));
	
	// clear it out for future use
	DisableMeshScaling();
	
	return pFile;
}




void *LoadMeshGroup(void *pFile, sScene *pScene, bool IsSkin, bool IsInstanceable, uint32 texDictOffset, int& skinnedVertexCount)
{
	sMesh *pMesh;
	sMaterial *pMat;
	sGroup *pGroup;
	uint32 MaterialChecksum, GroupChecksum;
	int NumMeshesThisGroup,i,j;
	Mth::Vector sphere;

	// get group checksum and number of meshes
	MEM_Read(&GroupChecksum, sizeof(uint32), 1, pFile);
	MEM_Read(&NumMeshesThisGroup, sizeof(uint32), 1, pFile);

	GroupChecksum += texDictOffset;

	// resolve checksum
	for (pGroup=sGroup::pHead; pGroup; pGroup=pGroup->pNext)
		if (pGroup->Checksum == GroupChecksum)
			break;
	Dbg_MsgAssert(pGroup, ("Couldn't find group checksum #%0X\n", GroupChecksum));

	// set mesh info in the group
	pGroup->pMeshes = pScene->pMeshes+pScene->NumMeshes;
	pGroup->NumMeshes = NumMeshesThisGroup;

	// loop over meshes
	for (i=0,pMesh=pGroup->pMeshes; iChecksum, sizeof(uint32), 1, pFile);

		// get object sphere for mesh version 2 onwards
		if (sMesh::Version >= 2)
		{
			unsigned int lod_stuff_not_used_here;
			MEM_Read(&lod_stuff_not_used_here, sizeof(unsigned int), 1, pFile);
			MEM_Read(&lod_stuff_not_used_here, sizeof(unsigned int), 1, pFile);

			// Just process parent/child stuff
			int num_child, hier_data;
			MEM_Read(&hier_data, sizeof(unsigned int), 1, pFile);
			MEM_Read(&num_child, sizeof(unsigned int), 1, pFile);
			for (int j = 0; j < num_child; j++)
			{
				MEM_Read(&hier_data, sizeof(unsigned int), 1, pFile);
			}

			MEM_Read(&sphere, sizeof(Mth::Vector), 1, pFile);

			// write sphere into sScene
			if (pScene->Sphere[3] < 0.0f)
				pScene->Sphere = sphere;

			#if 0
			// GJ:  the following assertion is no longer valid...
			// there are occasionally multiple NxObjects inside a
			// single MDL file, and each mesh will have a different
			// bounding sphere depending on which NxObject is belongs
			// to...  Mike agreed to rewrite the bounding sphere
			// calculation code so that all meshes will have a
			// new bounding sphere that encompasses all the individual
			// meshes' bounding spheres...

			// check object spheres from different meshes match ok
			Dbg_MsgAssert((pScene->Sphere[0]==sphere[0] && pScene->Sphere[1]==sphere[1] &&
						   pScene->Sphere[2]==sphere[2] && pScene->Sphere[3]==sphere[3]),
						  ("object bounding sphere differs between meshes"));
			#else
			// ...so, if the object spheres differ, just combine them...
			if (pScene->Sphere[0]!=sphere[0] || pScene->Sphere[1]!=sphere[1] ||
				pScene->Sphere[2]!=sphere[2] || pScene->Sphere[3]!=sphere[3])
			{
				//printf("\n\n");
				//printf("**********************************************\n");
				//printf("*                                            *\n");
				//printf("*  Tell Mike if you see this line printing!  *\n");
				//printf("*                                            *\n");
				//printf("**********************************************\n");
				//printf("\n\n");
				//printf("combining spheres (%g,%g,%g,%g) and (%g,%g,%g,%g)\n",
				//	   pScene->Sphere[0], pScene->Sphere[1], pScene->Sphere[2], pScene->Sphere[3],
				//	   sphere[0], sphere[1], sphere[2], sphere[3]);

				Mth::Vector x0, x1, d;
				if (pScene->Sphere[3] > sphere[3])
				{
					x0 = pScene->Sphere; 
					x1 = sphere;
				}
				else
				{
					x0 = sphere;
					x1 = pScene->Sphere;
				}
				float r0=x0[3], r1=x1[3];

				// (x0,r0) is the larger sphere, (x1,r1) the smaller
				// d is the vector between centres
				d = x1 - x0;

				// square of distance between centres
				float D2 = DotProduct(d,d);

				// (r0-r1)^2
				float R2 = (r0-r1)*(r0-r1);

				// m = max { (r1-r0+|d|)/2, 0 }
				float m = 0.0f;
				if (R2-D2 < 0)
				{
					m = (r1-r0 + sqrtf(D2)) * 0.5f;
				}

				pScene->Sphere    = x0 + m * d;
				pScene->Sphere[3] = r0 + m;

				//printf("result is (%g,%g,%g,%g)\n",
				//	   pScene->Sphere[0], pScene->Sphere[1], pScene->Sphere[2], pScene->Sphere[3]);
			
				//Dbg_MsgAssert( 0, ( "Please tell Mike if you see this assert firing off!" ) );
			}
			#endif
		}

		// get material checksum and look it up
		MEM_Read(&MaterialChecksum, sizeof(uint32), 1, pFile);
		for (j=0,pMat=pScene->pMaterials; jNumMaterials; j++,pMat++)
			if (pMat->Checksum == MaterialChecksum)
				break;
		Dbg_MsgAssert(pMat->Checksum==MaterialChecksum, ("couldn't find material with checksum %08X\n", MaterialChecksum));
		pMesh->pMaterial = pMat;

		// get mesh flags
		MEM_Read(&pMesh->Flags, sizeof(uint32), 1, pFile);

		// make it active
		pMesh->SetActive(true);

		// get bounding volume data
		MEM_Read(&pMesh->ObjBox, sizeof(float) * 3, 1, pFile);
		MEM_Read(&pMesh->Box,    sizeof(float) * 3, 1, pFile);
		MEM_Read(&pMesh->Sphere, sizeof(float) * 4, 1, pFile);

		// pass number
		if (sMesh::Version >= 5)
		{
			MEM_Read(&pMesh->Pass, sizeof(int), 1, pFile);
		}

		// material name
		if (sMesh::Version >= 6)
		{
			MEM_Read(&pMesh->MaterialName, sizeof(int), 1, pFile);
		}
		
		// import vertices of this mesh
		pFile = LoadVertices(pFile, pMesh, pMat, pGroup, skinnedVertexCount);

		// end the dma tag
		dma::EndTag();

		// end the model subroutine
		pMesh->pSubroutine = dma::EndSub3D();
	}

	// add to total number for scene
	pScene->NumMeshes += NumMeshesThisGroup;
	return pFile;
}



//----------------------------------------------------------------------------------------
//		L O A D   V E R T I C E S
//----------------------------------------------------------------------------------------

int UnpackOffset;

void * LoadVertices(void *pFile, sMesh *pMesh, sMaterial *pMat, sGroup *pGroup, int& skinnedIndexCount)
{
	uint32 NumVertices;
	uint REGS, NREG, PRIM, ADDR;
	sTexture *pTex;
	uint32 i;
	uint8  VertexData[64];
	int    STOffset, ColourOffset, NormalOffset, SkinOffset, XYZOffset, VertexSize;
	int    texture_width=0, texture_height=0;

	// get number of vertices for this mesh
	MEM_Read(&NumVertices, sizeof(uint32), 1, pFile);

	// skip mesh if it has no vertices
	if (NumVertices==0)
		return pFile;

	// work out giftag fields
	REGS = gs::XYZF2;
	NREG = 1;
	PRIM = TRISTRIP|ABE|FGE;
	ADDR = VU1_ADDR(Proj);
	if (pMesh->Flags & MESHFLAG_COLOURS)
	{
		REGS = REGS<<4 | gs::RGBAQ;
		NREG++;
	}
	if (pMesh->Flags & MESHFLAG_NORMALS)
	{
		if (pMat->Flags & MATFLAG_ENVIRONMENT)
		{
			ADDR = VU1_ADDR(Refl);
			REGS = REGS<<4 | gs::ST;
			PRIM |= TME;
		}
		else
		{
			REGS = REGS<<4 | gs::NOP;
		}
		NREG++;
	}
	if ((pMesh->Flags & MESHFLAG_TEXTURE) && !(pMat->Flags & MATFLAG_ENVIRONMENT))
	{
		REGS = REGS<<4 | gs::ST;
		NREG++;
		PRIM |= TME;
		ADDR = VU1_ADDR(PTex);
	}
	if (pMat->Flags & MATFLAG_SMOOTH)
		PRIM |= IIP;

	// override everything for skinned models!
	if (pMesh->Flags & MESHFLAG_SKINNED)
	{
		if (pMat->RegCLAMP & PackCLAMP(3,3,0,0,0,0))
		{
			// clamped case, send texture coords as 32 bits floating point STs
			REGS = gs::XYZ2<<16 | gs::RGBAQ<<12 | gs::NOP<<8 | gs::NOP<<4 | gs::ST;
			NREG = 5;
			PRIM |= TME;
			ADDR = VU1_ADDR(Skin);
		}
		else
		{
			// standard case, non-clamped, compress texture coords to 16 bit fixed point UVs
			REGS = gs::XYZ2<<16 | gs::RGBAQ<<12 | gs::NOP<<8 | gs::NOP<<4 | gs::UV;
			NREG = 5;
			PRIM |= TME|FST;
			ADDR = VU1_ADDR(Skin);
		}
	}

	// output GS context for material
	gs::BeginPrim(REL,0,0);
	gs::Reg1(gs::ALPHA_1,	pMat->RegALPHA);
	gs::Reg1(gs::TEST_1, PackTEST(1,AGEQUAL,pMat->Aref,KEEP,0,0,1,ZGEQUAL));

	// texture registers if necessary
	if (pMat->Flags & MATFLAG_TEXTURED)
	{
		pTex = pMat->pTex;
		gs::Reg1(gs::TEX0_1,	pTex->RegTEX0);
		gs::Reg1(gs::TEX1_1,	pMat->RegTEX1);
		gs::Reg1(gs::CLAMP_1,	pMat->RegCLAMP);
		if (pTex->MXL > 0)
		{
			gs::Reg1(gs::MIPTBP1_1,pTex->RegMIPTBP1);
			if (pTex->MXL > 3)
			{
				gs::Reg1(gs::MIPTBP2_1,pTex->RegMIPTBP2);
			}
		}

		// extract texture dimensions
		texture_width  = 1 << ((pTex->RegTEX0 >> 26) & 0xF);
		texture_height = 1 << ((pTex->RegTEX0 >> 30) & 0xF);
	}
	gs::EndPrim(0);

	// set maximum vu buffer size
	vu1::MaxBuffer = pMesh->Flags&MESHFLAG_SKINNED ? 240 : 240;

	// begin a batch of vertices
	BeginModelMultiPrim(REGS, NREG, PRIM, NumVertices, ADDR);

	// work out vertex size and data offsets
	VertexSize = (pMesh->Flags & MESHFLAG_SKINNED) ? 8 : 16;
	XYZOffset = SkinOffset = NormalOffset = ColourOffset = STOffset = 0;
	if (pMesh->Flags & MESHFLAG_SKINNED)
	{
		VertexSize   += 8;
		XYZOffset    += 8;
	}
	if (pMesh->Flags & MESHFLAG_NORMALS)
	{
		VertexSize   += 4;
		XYZOffset    += 4;
		SkinOffset   += 4;
	}
	if (pMesh->Flags & MESHFLAG_COLOURS)
	{
		VertexSize   += 4;
		XYZOffset    += 4;
		SkinOffset   += 4;
		NormalOffset += 4;
	}
	if (pMesh->Flags & MESHFLAG_TEXTURE)
	{
		int size = (pMesh->Flags & MESHFLAG_SKINNED) ? 4 : 8;
		VertexSize   += size;
		XYZOffset    += size;
		SkinOffset   += size;
		NormalOffset += size;
		ColourOffset += size;
	}
	// loop over vertices
	for (i=0; iFlags & MESHFLAG_SKINNED)
		{
			// texture coords
			UnpackOffset = 1;
			if ((pMesh->Flags & MESHFLAG_TEXTURE) && !(pMat->Flags & MATFLAG_ENVIRONMENT))
			{
				if (pMat->RegCLAMP & PackCLAMP(3,3,0,0,0,0))
				{
					VertexSTFloat(VertexData+STOffset);
				}
				else
				{
					VertexUV(VertexData+STOffset, texture_width<<4, texture_height<<4);
				}
			}

			// weights
			UnpackOffset = 2;
			VertexWeights(VertexData+SkinOffset);
			
			// normal and transform offsets
			UnpackOffset = 3;
			VertexSkinNormal(VertexData+NormalOffset, VertexData+SkinOffset+4);

			// colour
			UnpackOffset = 4;
			if (pMesh->Flags & MESHFLAG_COLOURS)
			{
				VertexRGBA(VertexData+ColourOffset);
			}

			// position
			UnpackOffset = 5;
			VertexXYZ(VertexData+XYZOffset, true, skinnedIndexCount);
		}
		else
		{
			// texture coords
			if ((pMesh->Flags & MESHFLAG_TEXTURE) && !(pMat->Flags & MATFLAG_ENVIRONMENT))
			{
				VertexST16(VertexData+STOffset);
			}

			// normal normals
			if (pMesh->Flags & MESHFLAG_NORMALS)
			{
				VertexNormal(VertexData+NormalOffset);
			}

			// colour
			if (pMesh->Flags & MESHFLAG_COLOURS)
			{
				VertexRGBA(VertexData+ColourOffset);
			}

			// position
			VertexXYZ(VertexData+XYZOffset, false, skinnedIndexCount);
		}

		EndVertex();
		
		// current vert being processed
		s_currentVertIndex++;
	}

	// accumulate number of vertices
	sMesh::TotalNumVertices += NumVertices;

	// finish batch of vertices
	EndModelMultiPrim();
	return pFile;
}






//---------------------------------------------------------
//		M O D E L   P R I M   C O N S T R U C T I O N
//---------------------------------------------------------


uint   MeshRegs, MeshNReg, MeshPrim, MeshNLoop, MeshAddr;
uint   NumVerticesThisBuffer, NumOutputVertices;
uint32 *pColour;
sint32 *pST32;
sint16 *pST16, *pVertex, *pNormal, *pSkinData;
uint8  *pWeights;
uint16 *pUV;



// begin model prim (i.e. begin a vertex packet)
void BeginModelPrim(uint32 Regs, uint NReg, uint Prim, uint Pre, uint Addr)
{
	vif::STCYCL(1,NReg);
	vif::UNPACK(0,V4_32,1,REL,UNSIGNED,0);
	gif::BeginTag1(Regs, NReg, PACKED, Prim, Pre, Addr);
}


// end model prim (i.e. end a vertex packet)
void EndModelPrim(uint Eop)
{
	gif::EndTag1(Eop);
}


// begin model prim (i.e. begin a vertex packet)
void BeginModelPrimImmediate(uint32 Regs, uint NReg, uint Prim, uint Pre, uint Addr)
{
	vif::STCYCL(1,NReg);
	vif::UNPACK(0,V4_32,1,ABS,UNSIGNED,0);
	gif::BeginTag1(Regs, NReg, PACKED, Prim, Pre, Addr);
}


// end model prim (i.e. end a vertex packet)
void EndModelPrimImmediate(uint Eop)
{
	gif::EndTag1(Eop);
}


// begin a model prim which may span multiple VU1 buffers
void BeginModelMultiPrim(uint32 Regs, uint NReg, uint Prim, uint NLoop, uint Addr)
{
	// record tag info
	MeshRegs  = Regs;
	MeshNReg  = NReg;
	MeshPrim  = Prim;
	MeshNLoop = NLoop;
	MeshAddr  = Addr;

	// work out number of vertices to unpack first
	// (and go ahead with it for now, even if there's only room for 1 vertex)
	NumVerticesThisBuffer = (vu1::MaxBuffer - ((vu1::Loc-vu1::Buffer)&0x3FF) - 1) / NReg;
	if (NumVerticesThisBuffer > MeshNLoop)
		NumVerticesThisBuffer = MeshNLoop;

	BeginModelPrim(Regs, NReg, Prim, 1, Addr);
	NumOutputVertices = 0;
}


void EndModelMultiPrim(void)
{
	EndModelPrim(1);
	vif::MSCAL(VU1_ADDR(Parser));
	vif::FLUSH();
}


int RestartTristrip=0;

void BeginVertex(sMesh *pMesh, sMaterial *pMat)
{
	if (NumOutputVertices == NumVerticesThisBuffer)
	{
		// end the model prim
		if ((pMesh->Flags & MESHFLAG_SKINNED) && (768-((vu1::Buffer+vif::UnpackSize*vif::CycleLength+1)&1023) < vu1::MaxBuffer))
		{
			EndModelPrim(0);
			vif::UNPACK(0, V4_32, 1, REL, UNSIGNED, 0);
			gif::Tag1(0, 0, PACKED, 0, 0, 1, 0, VU1_ADDR(Jump));		// reset address to 0
			vu1::Loc = 0;

			// start VU1 execution
			vif::MSCAL(VU1_ADDR(Parser));
			vif::FLUSH();
		}
		else
		{
			EndModelPrim(1);

			// start VU1 execution
			vif::MSCAL(VU1_ADDR(Parser));
			vif::FLUSH();
		}

		// reduce number still to go
		MeshNLoop -= NumOutputVertices;

		// work out number of vertices to unpack next
		NumVerticesThisBuffer = vu1::MaxBuffer / MeshNReg;
		if (NumVerticesThisBuffer > MeshNLoop)
			NumVerticesThisBuffer = MeshNLoop;

		// start a new buffalo'd

		// add a dummy gs context
		gs::BeginPrim(REL,0,0);
		gs::Reg1(gs::A_D_NOP,0);
		gs::EndPrim(0);

		BeginModelPrim(MeshRegs, MeshNReg, MeshPrim, 1, MeshAddr);
		NumOutputVertices = 0;

		// signal tristrip restart
		RestartTristrip=1;
	}
}


void VertexST16(uint8 *pData)
{
	static sint16 s0,t0,s1,t1;

	if (NumOutputVertices==0)
	{
		vif::BeginUNPACK(0, V2_16, REL, SIGNED, UnpackOffset);
		pST16 = (sint16 *)dma::pLoc;
		if (RestartTristrip)
		{
			*pST16++ = s0;
			*pST16++ = t0;
			*pST16++ = s1;
			*pST16++ = t1;
		}
		dma::pLoc = (uint8 *)pST16 + NumVerticesThisBuffer * 4;
		vif::EndUNPACK();
	}
	*pST16++ = (sint16) (int) (((float *)pData)[0] * 4096.0f);
	*pST16++ = (sint16) (int) (((float *)pData)[1] * 4096.0f);
	s0=s1, s1=pST16[-2];
	t0=t1, t1=pST16[-1];

	UnpackOffset++;
}


void VertexSTFloat(uint8 *pData)
{
	static sint32 s0,t0,s1,t1;

	if (NumOutputVertices==0)
	{
		vif::STMASK(0xE0);
		vif::BeginUNPACK(1, V2_32, REL, SIGNED, UnpackOffset);
		pST32 = (sint32 *)dma::pLoc;
		if (RestartTristrip)
		{
			*pST32++ = s0;
			*pST32++ = t0;
			*pST32++ = s1;
			*pST32++ = t1;
		}
		dma::pLoc = (uint8 *)pST32 + NumVerticesThisBuffer * 8;
		vif::EndUNPACK();
	}
	*(float *)pST32++ = (float)((sint16 *)pData)[0] * 0.000244140625f;
	*(float *)pST32++ = (float)((sint16 *)pData)[1] * 0.000244140625f;
	s0=s1, s1=pST32[-2];
	t0=t1, t1=pST32[-1];

	UnpackOffset++;
}


void VertexUV(uint8 *pData, int textureWidth16, int textureHeight16)
{
	static uint16 u0,v0,u1,v1;

	if (NumOutputVertices==0)
	{
		vif::BeginUNPACK(0, V2_16, REL, UNSIGNED, UnpackOffset);
		pUV = (uint16 *)dma::pLoc;
		if (RestartTristrip)
		{
			*pUV++ = u0;
			*pUV++ = v0;
			*pUV++ = u1;
			*pUV++ = v1;
		}
		dma::pLoc = (uint8 *)pUV + NumVerticesThisBuffer * 4;
		vif::EndUNPACK();
	}
	*pUV++ = (uint16)((int)((float)((sint16 *)pData)[0] * (float)textureWidth16  * 0.000244140625f) + 0x00002000);
	*pUV++ = (uint16)((int)((float)((sint16 *)pData)[1] * (float)textureHeight16 * 0.000244140625f) + 0x00002000);
	u0=u1, u1=pUV[-2];
	v0=v1, v1=pUV[-1];

	UnpackOffset++;
}


void VertexNormal(uint8 *pData)
{
	static sint16 nx0,ny0,nz0,nx1,ny1,nz1;

	if (NumOutputVertices==0)
	{
		vif::BeginUNPACK(0, V3_16, REL, SIGNED, UnpackOffset);
		pNormal = (sint16 *)dma::pLoc;
		if (RestartTristrip)
		{
			*pNormal++ = nx0;
			*pNormal++ = ny0;
			*pNormal++ = nz0;
			*pNormal++ = nx1;
			*pNormal++ = ny1;
			*pNormal++ = nz1;
		}
		dma::pLoc = (uint8 *)pNormal + NumVerticesThisBuffer * 6;
		vif::EndUNPACK();
	}
	*pNormal++ = ((sint16 *)pData)[0];
	*pNormal++ = ((sint16 *)pData)[1];
	//*pNormal++ = ((sint16 *)pData)[2];
	sint16 coord = (sint16)sqrtf(32767.0f*32767.0f - (float)((sint16 *)pData)[0] * (float)((sint16 *)pData)[0]
												   - (float)((sint16 *)pData)[1] * (float)((sint16 *)pData)[1]);
	if (((sint16 *)pData)[0] & 0x0001)
	{
		coord = -coord;
	}
	*pNormal++ = coord;
	nx0=nx1, nx1=pNormal[-3];
	ny0=ny1, ny1=pNormal[-2];
	nz0=nz1, nz1=pNormal[-1];

	UnpackOffset++;
}


#if 0

void VertexWeights(uint8 *pData)
{
	static sint16 wa0,wb0,wc0,wa1,wb1,wc1;

	if (NumOutputVertices==0)
	{
		vif::BeginUNPACK(0, V3_16, REL, SIGNED, UnpackOffset);
		pWeights = (sint16 *)dma::pLoc;
		if (RestartTristrip)
		{
			*pWeights++ = wa0;
			*pWeights++ = wb0;
			*pWeights++ = wc0;
			*pWeights++ = wa1;
			*pWeights++ = wb1;
			*pWeights++ = wc1;
		}
		dma::pLoc = (uint8 *)pWeights + NumVerticesThisBuffer * 6;
		vif::EndUNPACK();
	}
	*pWeights++ = ((sint16 *)pData)[0];
	*pWeights++ = ((sint16 *)pData)[1];
	*pWeights++ = 0x7FFF - pWeights[-1] - pWeights[-2];
	wa0=wa1, wa1=pWeights[-3];
	wb0=wb1, wb1=pWeights[-2];
	wc0=wc1, wc1=pWeights[-1];

	UnpackOffset++;
}

#else

void VertexWeights(uint8 *pData)
{
	static uint8 wa0,wb0,wc0,wa1,wb1,wc1;

	if (NumOutputVertices==0)
	{
		vif::BeginUNPACK(0, V3_8, REL, UNSIGNED, UnpackOffset);
		pWeights = dma::pLoc;
		if (RestartTristrip)
		{
			*pWeights++ = wa0;
			*pWeights++ = wb0;
			*pWeights++ = wc0;
			*pWeights++ = wa1;
			*pWeights++ = wb1;
			*pWeights++ = wc1;
		}
		dma::pLoc = pWeights + NumVerticesThisBuffer * 3;
		vif::EndUNPACK();
	}
	wa0=wa1;
	wb0=wb1;
	wc0=wc1;
	wa1 = (uint8)(sint8)(((float)((sint16 *)pData)[0] + 0.5f) * 0.007818608f);
	wb1 = (uint8)(sint8)(((float)((sint16 *)pData)[1] + 0.5f) * 0.007818608f);
	if (wb1==0)
	{
		wa1=255, wb1=1;
	}
	wc1 = 256 - wa1 - wb1;
	*pWeights++ = wa1;
	*pWeights++ = wb1;
	*pWeights++ = wc1;

	UnpackOffset++;
}

#endif



void VertexSkinNormal(uint8 *pNormData, uint8 *pTOData)
{
	static sint16 nx0,ny0,nz0,nx1,ny1,nz1;

	if (NumOutputVertices==0)
	{
		vif::BeginUNPACK(0, V3_16, REL, SIGNED, UnpackOffset);
		pSkinData = (sint16 *)dma::pLoc;
		if (RestartTristrip)
		{
			*pSkinData++ = nx0;
			*pSkinData++ = ny0;
			*pSkinData++ = nz0;
			*pSkinData++ = nx1;
			*pSkinData++ = ny1;
			*pSkinData++ = nz1;
		}
		dma::pLoc = (uint8 *)pSkinData + NumVerticesThisBuffer * 6;
		vif::EndUNPACK();
	}

	*pSkinData++ = (sint16) (((sint32) ((sint16 *)pNormData)[0]) & 0xFFFFFC00 | (((uint32)((uint8 *)pTOData)[0]) << 2));
	*pSkinData++ = (sint16) (((sint32) ((sint16 *)pNormData)[1]) & 0xFFFFFC00 | (((uint32)((uint8 *)pTOData)[1]) << 2));
	//*pSkinData++ = (sint16) (((sint32) ((sint16 *)pNormData)[2]) & 0xFFFFFC00 | (((uint32)((uint8 *)pTOData)[2]) << 2));
	sint16 coord = (sint16)sqrtf(32767.0f*32767.0f - (float)((sint16 *)pNormData)[0] * (float)((sint16 *)pNormData)[0]
												   - (float)((sint16 *)pNormData)[1] * (float)((sint16 *)pNormData)[1]);
	if (((sint16 *)pNormData)[0] & 0x0001)
	{
		coord = -coord;
	}
	*pSkinData++ = (sint16) ((sint32)coord & 0xFFFFFC00) | (((uint8 *)pTOData)[2] << 2);

	nx0=nx1, nx1=pSkinData[-3];
	ny0=ny1, ny1=pSkinData[-2];
	nz0=nz1, nz1=pSkinData[-1];

	UnpackOffset++;
}



void VertexRGBA(uint8 *pData)
{
	static uint32 rgba0,rgba1;

	if (NumOutputVertices==0)
	{
		vif::BeginUNPACK(0, V4_8, REL, UNSIGNED, UnpackOffset);
		pColour = (uint32 *)dma::pLoc;
		if (RestartTristrip)
		{
			*pColour++ = rgba0;
			*pColour++ = rgba1;
		}
		dma::pLoc = (uint8 *)pColour + NumVerticesThisBuffer * 4;
		vif::EndUNPACK();
	}
	*pColour++ = ((uint32 *)pData)[0];
	rgba0=rgba1, rgba1=pColour[-1];

	UnpackOffset++;
}

Mth::Vector get_bone_scale( int bone_index )
{
	Mth::Vector returnVec( 1.0f, 1.0f, 1.0f, 1.0f );

	if ( bone_index >= 29 && bone_index <= 33 )
	{
		// this only works with the thps5 skeleton, whose
		// head bones are between 29 and 33...
		// (eventually, we can remove the subtract 29
		// once the exporter is massaging the data correctly)
		returnVec = s_pBoneScales[ bone_index - 29 ];
		
		// Y & Z are reversed...  odd!
		Mth::Vector tempVec = returnVec;
		returnVec[Y] = tempVec[Z];
		returnVec[Z] = tempVec[Y];
	}
	else if ( bone_index == -1 )
	{
		// implies that it's not weighted to a bone
		return returnVec;
	}
	else
	{
		// implies that it's weighted to the wrong bone
		return returnVec;
	}

	return returnVec;
}

Mth::Vector get_bone_pos( int bone_index )
{
	Mth::Vector returnVec( 0.0f, 0.0f, 0.0f, 1.0f );
	
	if ( bone_index >= 29 && bone_index <= 33 )
	{
		// this only works with the thps5 skeleton, whose
		// head bones are between 29 and 33...
		// (eventually, we can remove the subtract 29
		// once the exporter is massaging the data correctly)
		returnVec = s_pBonePositions[ bone_index - 29 ];
	}
	else if ( bone_index == -1 )
	{
		// implies that it's not weighted to a bone
		return returnVec;
	}
	else
	{
		// implies that it's weighted to the wrong bone
		return returnVec;
	}

	// need to get into fixed point
	returnVec.Scale( 16.0f );
	returnVec[W] = 1.0f;

	return returnVec;
}

void VertexXYZ(uint8 *pData, bool IsSkin, int& skinnedVertexCount)
{
	static sint64 x0,y0,z0,x1,y1,z1,x2,y2,z2;
	sint64 det0,det1,det2;

	if (NumOutputVertices==0)
	{
		vif::STMOD(1);
		vif::BeginUNPACK(0, V4_16, REL, SIGNED, UnpackOffset);
		pVertex = (sint16 *)dma::pLoc;
		if (RestartTristrip)
		{
			*pVertex++ = x1;
			*pVertex++ = y1;
			*pVertex++ = z1;
			*pVertex++ = 0x8000;
			*pVertex++ = x2;
			*pVertex++ = y2;
			*pVertex++ = z2;
			*pVertex++ = 0x8000;
		}
		dma::pLoc = (uint8 *)pVertex + NumVerticesThisBuffer * 8;
		vif::EndUNPACK();
		vif::STMOD(0);
	}
	
	if (IsSkin)
	{
		if ( s_meshScalingEnabled )
		{
			float x = (float)((sint16 *)pData)[0];
			float y = (float)((sint16 *)pData)[1];
			float z = (float)((sint16 *)pData)[2];

    		Mth::Vector origPos( x, y, z, 1.0f );

			Mth::Vector bonePos0 = get_bone_pos( s_pWeightIndices[s_currentVertIndex * 3] );
			Mth::Vector bonePos1 = get_bone_pos( s_pWeightIndices[s_currentVertIndex * 3 + 1] );
			Mth::Vector bonePos2 = get_bone_pos( s_pWeightIndices[s_currentVertIndex * 3 + 2] );

			// need to scale each vert relative to its parent bone
			Mth::Vector localPos0 = origPos - bonePos0;
			Mth::Vector localPos1 = origPos - bonePos1;
			Mth::Vector localPos2 = origPos - bonePos2;
			localPos0.Scale( get_bone_scale( s_pWeightIndices[s_currentVertIndex * 3] ) );
			localPos1.Scale( get_bone_scale( s_pWeightIndices[s_currentVertIndex * 3 + 1] ) );
			localPos2.Scale( get_bone_scale( s_pWeightIndices[s_currentVertIndex * 3 + 2] ) );
			localPos0 += bonePos0;
			localPos1 += bonePos1;
			localPos2 += bonePos2;
			
			Mth::Vector scaledPos = ( localPos0 * s_pWeights[s_currentVertIndex * 3] )
				+ ( localPos1 * s_pWeights[s_currentVertIndex * 3 + 1] )
				+ ( localPos2 * s_pWeights[s_currentVertIndex * 3 + 2] );

			x = scaledPos[X];
			y = scaledPos[Y];
			z = scaledPos[Z];

			*pVertex++ = (sint16)x;
			*pVertex++ = (sint16)y;
			*pVertex++ = (sint16)z;
		}
		else
		{
			*pVertex++ = (((sint16 *)pData)[0] * 1);
			*pVertex++ = (((sint16 *)pData)[1] * 1);
			*pVertex++ = (((sint16 *)pData)[2] * 1);
		}
	}
	else
	{
		*pVertex++ = (sint16) (((float *)pData)[0] * SUB_INCH_PRECISION);
		*pVertex++ = (sint16) (((float *)pData)[1] * SUB_INCH_PRECISION);
		*pVertex++ = (sint16) (((float *)pData)[2] * SUB_INCH_PRECISION);
	}
	
	if (IsSkin)
	{
		// GJ:  if it's a skinned model, then check to see
		// if the skinnedVertexCount matches one of the ones
		// that requires a pointer to the ADC bit
		AddCASFlag( skinnedVertexCount, (uint16*)pVertex );
	}
	
	skinnedVertexCount++;

	if (IsSkin)
	{
		*pVertex++ = ((uint16 *)pData)[3] ? 0x8000 : 0x0000;
	}
	else
	{
		*pVertex++ = ((uint32 *)pData)[3] ? 0x8000 : 0x0000;
	}
	
	// advance vertex queue and cull triangle if zero area
	x0=x1, y0=y1, z0=z1;
	x1=x2, y1=y2, z1=z2;
	x2=pVertex[-4], y2=pVertex[-3], z2=pVertex[-2];
	det0 = y1*z2+y2*z0+y0*z1-y2*z1-y0*z2-y1*z0;
	det1 = z1*x2+z2*x0+z0*x1-z2*x1-z0*x2-z1*x0;
	det2 = x1*y2+x2*y0+x0*y1-x2*y1-x0*y2-x1*y0;
	if (det0*det0 + det1*det1 + det2*det2 == 0)
		pVertex[-1] = 0x8000;

	UnpackOffset++;
}


void EndVertex(void)
{
	if (NumOutputVertices==0 && RestartTristrip)
	{
		RestartTristrip = 0;
	}

	NumOutputVertices++;
}




void sMesh::SetActive(bool active)
{
	if (active)
	{
		Flags |= MESHFLAG_ACTIVE;
	}
	else
	{
		Flags &= ~MESHFLAG_ACTIVE;
	}
}


// Note, Meshflags and passmasks are off by 2 bits
//#define MESHFLAG_PASS_BIT_0	(1<<8)
//#define MESHFLAG_PASS_BIT_1	(1<<9)

//uint32		gPassMask1 = 0; 		// 1<<6 | 1<<7  (0x40, 0x80)
//uint32		gPassMask0 = 0; 		// 1<<6 | 1<<7  (0x40, 0x80)

bool sMesh::IsActive()
{
	#ifdef	__NOPT_ASSERT__
	// Mick: debug check for global pass on/off
	// Note, shifting flags by 2, so mesh flags match geomnode flags
	// Eventually, meshes will be replaced by leaf nodes in CGeomNode tree
//	if (((Flags>>2) & gPassMask1) != gPassMask0)
//	{	
//		return false;
//	}
	#endif
	

	
	return (Flags&MESHFLAG_ACTIVE) ? true : false;
}

const int MAX_CAS_DATA_LOOKUP = 10000;

// array of pointers to sCASData*
sCASData** sCASDataLookupTable = NULL;

void CreateCASDataLookupTable()
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

	Dbg_MsgAssert( sCASDataLookupTable == NULL, ( "CAS data lookup table already exists" ) );

	sCASDataLookupTable = (sCASData**)Mem::Malloc( MAX_CAS_DATA_LOOKUP * sizeof(sCASData*) );

	for ( int i = 0; i < MAX_CAS_DATA_LOOKUP; i++ )
	{
		sCASDataLookupTable[i] = NULL;
	}

	Mem::Manager::sHandle().PopContext();
}

void DestroyCASDataLookupTable()
{
	if ( sCASDataLookupTable )
	{
		Mem::Free( sCASDataLookupTable );
		sCASDataLookupTable = NULL;
	}
}

void SetCASDataLookupData( int index, sCASData* pData )
{
	Dbg_MsgAssert( sCASDataLookupTable != NULL, ( "CAS data lookup table doesn't exist" ) );

	Dbg_MsgAssert( index >= 0 && index < MAX_CAS_DATA_LOOKUP, ( "Out of range lookup index %d (must be between 0 and %d)", index, MAX_CAS_DATA_LOOKUP ) );

	Dbg_MsgAssert( sCASDataLookupTable[index] == NULL, ( "Lookup index %d already used", index, MAX_CAS_DATA_LOOKUP ) );

	sCASDataLookupTable[index] = pData;
}

void AddCASFlag( int skinnedVertexIndex, uint16* pADCBit )
{
	if ( !sCASDataLookupTable )
	{
		return;
	}

	Dbg_MsgAssert( skinnedVertexIndex >= 0 && skinnedVertexIndex < MAX_CAS_DATA_LOOKUP, ( "Out of range lookup index %d (must be between 0 and %d", skinnedVertexIndex, MAX_CAS_DATA_LOOKUP ) );

	if ( sCASDataLookupTable[skinnedVertexIndex] != NULL )
	{
		sCASDataLookupTable[skinnedVertexIndex]->pADCBit = pADCBit;
	}
}

int sMesh::TotalNumVertices;
uint32 sMesh::Version;



} // namespace NxPs2




================================================
FILE: Code/Gfx/NGPS/NX/mesh.h
================================================
#ifndef __MESH_H
#define __MESH_H


#include 

namespace Nx
{
	struct SMeshScalingParameters;
}
					  
namespace NxPs2
{


#define MESHFLAG_TEXTURE		(1<<0)
#define MESHFLAG_COLOURS		(1<<1)
#define MESHFLAG_NORMALS		(1<<2)
#define MESHFLAG_ST16			(1<<3)
#define MESHFLAG_SKINNED		(1<<4)
#define MESHFLAG_AXIAL			(1<<6)
#define MESHFLAG_SHORTAXIS		(1<<7)
#define MESHFLAG_PASS_BIT_0		(1<<8)
#define MESHFLAG_PASS_BIT_1		(1<<9)
#define MESHFLAG_NO_SHADOW		(1<<10)
#define MESHFLAG_ACTIVE			(1<<16)
#define MESHFLAG_UNLIT			(1<<17)
#define MESHFLAG_COLOR_LOCKED	(1<<18)
#define MESHFLAG_BILLBOARD		(1<<23)
#define MESHFLAG_SINGLESIDED	(1<<24)
#define MESHFLAG_PASS_BIT_2		(1<<25)
#define MESHFLAG_PASS_BIT_3		(1<<26)

#define MESHFLAG_PASS_BITS		(MESHFLAG_PASS_BIT_0 | MESHFLAG_PASS_BIT_1 | MESHFLAG_PASS_BIT_2 | MESHFLAG_PASS_BIT_3)


#define OBJFLAG_TEXTURE			(1<<0)
#define OBJFLAG_COLOURS			(1<<1)
#define OBJFLAG_NORMALS			(1<<2)
#define OBJFLAG_TRANSPARENT		(1<<3)


struct sGroup;
struct sMaterial;

struct sCASData
{
	uint32	mask;
//	int		vertIndex;	
	uint16* pADCBit;
};

void CreateCASDataLookupTable();
void DestroyCASDataLookupTable();
void SetCASDataLookupData( int index, sCASData* pData );

void AddCASFlag(int skinnedVertexIndex, uint16* pADCBit);

struct sMesh
{
public:
	uint32			GetChecksum() const { return Checksum; }
	uint32			GetFlags() const { return Flags; }
	void			SetActive(bool active);
	bool			IsActive(void);

	uint32 Checksum, Flags;
	sMaterial *pMaterial;
	uint8 *pSubroutine;
	Mth::Vector ObjBox,Box,Sphere;
	uint32*	pVCWibbleInfo;
	int Pass;
	uint32 MaterialName;

	static int TotalNumVertices;
	static uint32 Version;
};

// GJ:  for doing cutscene head scaling
void SetMeshScalingParameters( Nx::SMeshScalingParameters* pParams );

void * LoadMeshes(void *pFile, struct sScene *pScene, bool IsSkin, bool IsInstanceable, uint32 texDictOffset);
void * LoadMeshGroup(void *pFile, struct sScene *pScene, bool IsSkin, bool IsInstanceable, uint32 texDictOffset, int& skinnedVertexIndex );
void * LoadVertices(void *pFile, sMesh *pMesh, struct sMaterial *pMat, sGroup *pGroup, int& skinnedVertexIndex);
void BeginModelPrim(uint32 Regs, uint NReg, uint Prim, uint Pre, uint Addr);
void EndModelPrim(uint Eop);
void BeginModelPrimImmediate(uint32 Regs, uint NReg, uint Prim, uint Pre, uint Addr);
void EndModelPrimImmediate(uint Eop);
void BeginModelMultiPrim(uint32 Regs, uint NReg, uint Prim, uint NLoop, uint Addr);
void EndModelMultiPrim(void);
void BeginVertex(sMesh *pMesh, struct sMaterial *pMat);
void VertexST16(uint8 *pData);
void VertexSTFloat(uint8 *pData);
void VertexUV(uint8 *pData, int textureWidth16, int textureHeight16);
void VertexNormal(uint8 *pData);
void VertexWeights(uint8 *pData);
void VertexSkinNormal(uint8 *pNormData, uint8 *pTOData);
void VertexRGBA(uint8 *pData);
void VertexXYZ(uint8 *pData, bool IsSkin, int& skinnedVertexIndex);
void EndVertex(void);


extern uint NumMeshesInGroup[32];


} // namespace NxPs2

#endif // __MESH_H



================================================
FILE: Code/Gfx/NGPS/NX/mikemath.cpp
================================================


//--------------------------------------------------------------
//		V E C T O R   A N D   M A T R I X   R O U T I N E S
//--------------------------------------------------------------

					 
#include 
#include 
#include "mikemath.h"

namespace NxPs2
{


// copy a vector
// vd = vs
void VecCopy(Vec vd, Vec vs)
{
	vd[0]=vs[0], vd[1]=vs[1], vd[2]=vs[2], vd[3]=vs[3];
}


// set a vector
// v = (x,y,z,w)
void VecSet(Vec v, float x, float y, float z, float w)
{
	v[0]=x, v[1]=y, v[2]=z, v[3]=w;
}


// add two vectors to make another
// vd = v0+v1
void VecSum(Vec vd, Vec v0, Vec v1)
{
	int i;
	for (i=0; i<4; i++)
		vd[i] = v0[i]+v1[i];
}


// add one vector onto another
// vd += vs
void VecAdd(Vec vd, Vec vs)
{
	int i;
	for (i=0; i<4; i++)
		vd[i] += vs[i];
}


// subtract two vectors to make another
// vd = v0-v1
void VecDifference(Vec vd, Vec v0, Vec v1)
{
	int i;
	for (i=0; i<4; i++)
		vd[i] = v0[i]-v1[i];
}


// subtract one vector from another
// vd -= vs
void VecSubtract(Vec vd, Vec vs)
{
	int i;
	for (i=0; i<4; i++)
		vd[i] -= vs[i];
}


// negate a vector
// v = -v
void VecNegate(Vec v)
{
	int i;
	for (i=0; i<4; i++)
		v[i] = -v[i];
}


// multiply a vector by a matrix
// v = v * M
void VecTransform(Vec v, Mat M)
{
	int i,j;
	float sum;
	Vec TempVec;
	for (i=0; i<4; i++)
	{
		sum = 0.0;
		for (j=0; j<4; j++)
			sum += v[j]*M[j][i];
		TempVec[i] = sum;
	}
	VecCopy(v,TempVec);
}


// product of a vector by a matrix
// vd = vs * M
void VecTransformed(Vec vd, Vec vs, Mat M)
{
	int i,j;
	float sum;
	for (i=0; i<4; i++)
	{
		sum = 0.0;
		for (j=0; j<4; j++)
			sum += vs[j]*M[j][i];
		vd[i] = sum;
	}
}


// rotate a vector through an angle about an axis
void VecRotate(Vec v, float Angle, char Axis)
{
	Mat M;
	MatRotation(M, Angle, Axis);
	VecTransform(v, M);
}


// normalise a vector
// v = v / |v|
void VecNormalise(Vec v)
{
	int i;
	float RecipLength = 1.0 / sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2] + v[3]*v[3]);
	for (i=0; i<4; i++)
		v[i] *= RecipLength;
}


// scale a vector
// v *= k
void VecScale(Vec v, float k)
{
	int i;
	for (i=0; i<4; i++)
		v[i] *= k;
}


// scaled vector
// vd = k*vs
void VecScaled(Vec vd, float k, Vec vs)
{
	int i;
	for (i=0; i<4; i++)
		vd[i] = k*vs[i];
}


// add scaled vector
// vd += k*vs
void VecAddScaled(Vec vd, float k, Vec vs)
{
	int i;
	for (i=0; i<4; i++)
		vd[i] += k*vs[i];
}


// weighted mean of 2 vectors
// v = (k0*v0 + k1*v1)/(k0+k1)
void VecWeightedMean2(Vec v, float k0, Vec v0, float k1, Vec v1)
{
	Vec Temp;
	VecScaled(v,k0,v0);
	VecScaled(Temp,k1,v1);
	VecAdd(v,Temp);
	VecScale(v,1.0f/(k0+k1));
}


// weighted mean of 3 vectors
// v = (k0*v0 + k1*v1 + k2*v2)/(k0+k1+k2)
void VecWeightedMean3(Vec v, float k0, Vec v0, float k1, Vec v1, float k2, Vec v2)
{
	Vec Temp;
	VecScaled(v,k0,v0);
	VecScaled(Temp,k1,v1);
	VecAdd(v,Temp);
	VecScaled(Temp,k2,v2);
	VecAdd(v,Temp);
	VecScale(v,1.0f/(k0+k1+k2));
}


// cross product
void CrossProduct(Vec vd, Vec v0, Vec v1)
{
	vd[0] = v0[1]*v1[2] - v0[2]*v1[1];
	vd[1] = v0[2]*v1[0] - v0[0]*v1[2];
	vd[2] = v0[0]*v1[1] - v0[1]*v1[0];
}


// dot product (3 components)
float DotProduct3(Vec v0, Vec v1)
{
	return v0[0]*v1[0] + v0[1]*v1[1] + v0[2]*v1[2];
}


// dot product (4 components)
float DotProduct4(Vec v0, Vec v1)
{
	return v0[0]*v1[0] + v0[1]*v1[1] + v0[2]*v1[2] + v0[3]*v1[3];
}


// length of a vector (3 components)
float VecLength3(Vec v)
{
	return sqrtf(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
}


// copy a matrix
// Md = Ms
//void MatCopy(Mat Md, Mat Ms)
//{
//    int i,j;
//    for (i=0; i<4; i++)
//        for (j=0; j<4; j++)
//            Md[i][j] = Ms[i][j];
//	*(Mth::Matrix*)Md = *(Mth::Matrix*)Ms;
//}


// set a matrix
// M = ((m00,m01,m02,m03), (m10,m11,m12,m13), (m20,m21,m22,m23), (m30,m31,m32,m33))
void MatSet(Mat M, float m00, float m01, float m02, float m03,
				   float m10, float m11, float m12, float m13,
				   float m20, float m21, float m22, float m23,
				   float m30, float m31, float m32, float m33)
{
	M[0][0]=m00, M[0][1]=m01, M[0][2]=m02, M[0][3]=m03;
	M[1][0]=m10, M[1][1]=m11, M[1][2]=m12, M[1][3]=m13;
	M[2][0]=m20, M[2][1]=m21, M[2][2]=m22, M[2][3]=m23;
	M[3][0]=m30, M[3][1]=m31, M[3][2]=m32, M[3][3]=m33;
}




// zero a matrix
// set M to 4x4 zero matrix
void MatZero(Mat M)
{
    int i,j;
    for (i=0; i<4; i++)
        for (j=0; j<4; j++)
            M[i][j] = 0.0;
}


// set identity matrix
// set M to 4x4 identity
void MatIdentity(Mat M)
{
    int i,j;
    for (i=0; i<4; i++)
        for (j=0; j<4; j++)
            M[i][j] = (i==j) ? 1.0 : 0.0;
}


// transpose a matrix
// M = MT
void MatTranspose(Mat M)
{
	float temp;
	int i,j;
	for (i=0; i<4; i++)
		for (j=i+1; j<4; j++)
			temp=M[i][j], M[i][j]=M[j][i], M[j][i]=temp;
}


// transpose a matrix
// Md = MsT
void MatTransposed(Mat Md, Mat Ms)
{
	int i,j;
	for (i=0; i<4; i++)
		for (j=0; j<4; j++)
			Md[i][j]=Ms[j][i];
}


// make a diagonal matrix from the elements of v
void MatDiagonal(Mat M, Vec v)
{
	int i;
	MatZero(M);
	for (i=0; i<4; i++)
		M[i][i] = v[i];
}


// scale all elements of a matrix
void MatScale(Mat M, float k)
{
	int i,j;
	for (i=0; i<4; i++)
		for (j=0; j<4; j++)
			M[i][j] *= k;
}


// set M to matrix for rotation through Angle (radians) about Axis ('x','y' or 'z')
void MatRotation(Mat M, float Angle, char Axis)
{
    float s,c;
    int i0=(int)((Axis+1-'x'))%3, i1=(int)((Axis+2-'x'))%3;
    s=sinf(Angle), c=cosf(Angle);
    MatIdentity(M);
    M[i0][i0] = M[i1][i1] = c;
    M[i1][i0] = -(M[i0][i1] = s);
}


// set M to matrix for translation by v
void MatTranslation(Mat M, Vec v)
{
    MatIdentity(M);
    VecCopy(M[3],v);
}


// multiply two matrices to make another
// set AB = A * B
void MatProduct(Mat AB, Mat A, Mat B)
{
	
	#if 1	
	sceVu0MulMatrix((sceVu0FVECTOR*)AB,(sceVu0FVECTOR*)B,(sceVu0FVECTOR*)A);   
	return;
	#else 
	
	float sum;
    int i,j,k;
    for (i=0; i<4; i++)
        for (j=0; j<4; j++)
        {
            sum=0.0;
            for (k=0; k<4; k++)
                sum += A[i][k]*B[k][j];
            AB[i][j] = sum;
        }
	#endif		
}


// premulitplication of one matrix by another
// A = B * A
void MatPremultiply(Mat A, Mat B)
{
    Mat TempMat;
    MatProduct(TempMat,B,A);
    MatCopy(A,TempMat);
}


// postmultiplication of one matrix by another
// A = A * B
void MatPostmultiply(Mat A, Mat B)
{
    Mat TempMat;
    MatProduct(TempMat,A,B);
    MatCopy(A,TempMat);
}


// invert a 4x4 matrix
// Md = inv(Ms)
// Note: this is not a general matrix invert: it only works for matrices which incorporate rotation and translation.
void MatInverse(Mat Md, Mat Ms)
{
	int i,j;
	Mat InvT, InvR;
	// set up negative of original translation
	MatIdentity(InvT);
	InvT[3][0] = -Ms[3][0];
	InvT[3][1] = -Ms[3][1];
	InvT[3][2] = -Ms[3][2];
	// set up transpose of original rotation
	MatIdentity(InvR);
	for (i=0; i<3; i++)
		for (j=0; j<3; j++)
			InvR[i][j] = Ms[j][i];
	// take product
	MatProduct(Md,InvT,InvR);
}


// build a matrix from 3 Euler angles
// M = Rz(Angles[2]) * Rx(Angles[0]) * Ry(Angles[1])
void MatEuler(Mat M, Vec Angles)
{
	Mat Temp;
	MatRotation(M, Angles[2], 'z');
	MatRotation(Temp, Angles[0], 'x');
	MatPostmultiply(M, Temp);
	MatRotation(Temp, Angles[1], 'y');
	MatPostmultiply(M, Temp);
}

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/mikemath.h
================================================
#ifndef __MIKEMATH_H
#define __MIKEMATH_H

#include 
#include 

namespace NxPs2
{

// typedefs
typedef float Vec[4] nAlign(128);		// Structurally equivalent to Mth::Vector
typedef float Mat[4][4] nAlign(128);	// Structurally equivalent to Mth::Matrix

// function prototypes
void VecCopy(Vec vd, Vec vs);
void VecSet(Vec v, float x, float y, float z, float w);
void VecSum(Vec vd, Vec v0, Vec v1);
void VecAdd(Vec vd, Vec vs);
void VecDifference(Vec vd, Vec v0, Vec v1);
void VecSubtract(Vec vd, Vec vs);
void VecNegate(Vec v);
void VecTransform(Vec v, Mat M);
void VecTransformed(Vec vd, Vec vs, Mat M);
void VecRotate(Vec v, float Angle, char Axis);
void VecNormalise(Vec v);
void VecScale(Vec v, float k);
void VecScaled(Vec vd, float k, Vec vs);
void VecAddScaled(Vec vd, float k, Vec vs);
void VecWeightedMean2(Vec v, float k0, Vec v0, float k1, Vec v1);
void VecWeightedMean3(Vec v, float k0, Vec v0, float k1, Vec v1, float k2, Vec v2);
void CrossProduct(Vec vd, Vec v0, Vec v1);
float DotProduct3(Vec v0, Vec v1);
float DotProduct4(Vec v0, Vec v1);
float VecLength3(Vec v);
void MatCopy(Mat Md, Mat Ms);
void MatSet(Mat M, float m00, float m01, float m02, float m03,
				   float m10, float m11, float m12, float m13,
				   float m20, float m21, float m22, float m23,
				   float m30, float m31, float m32, float m33);
void MatZero(Mat M);
void MatIdentity(Mat M);
void MatTranspose(Mat M);
void MatTransposed(Mat Md, Mat Ms);
void MatDiagonal(Mat M, Vec v);
void MatScale(Mat M, float k);
void MatRotation(Mat M, float Angle, char Axis);
void MatTranslation(Mat M, Vec v);
void MatProduct(Mat AB, Mat A, Mat B);
void MatPremultiply(Mat A, Mat B);
void MatPostmultiply(Mat A, Mat B);
void MatInverse(Mat Md, Mat Ms);
void MatEuler(Mat M, Vec Angles);


inline void MatCopy(Mat Md, Mat Ms)
{
	*(Mth::Matrix*)Md = *(Mth::Matrix*)Ms;
}


} // namespace NxPs2
					  
					  
#endif // __MIKEMATH_H


================================================
FILE: Code/Gfx/NGPS/NX/nx_init.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "nx_init.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "vu1code.h"
#include "vu0code.h"
#include "gif.h"
#include "gs.h"
#include "dmacalls.h"
#include "pcrtc.h"
#include "render.h"
#include "chars.h"
#include "scene.h"
#include "group.h"
#include "interrupts.h"
#include "switches.h"
#include "instance.h"
#include "geomnode.h"
#include "texture.h"
#include "resource.h"
#include "fx.h"

namespace NxPs2
{

extern	void	WaitForRendering();

// Interrupts
static	void	StartInterrupts();
static	void	StopInterrupts();

Mth::Matrix GmyMat[4];

// Interrupt Handler IDs
#if USE_INTERRUPTS
	
#if GIF_INTERRUPT
static int sGifHandlerId = -1;
#endif

#if GS_INTERRUPT
static int sGsHandlerId = -1;
#endif

#if VIF1_INTERRUPT
static int sVif1HandlerId = -1;
#endif

#endif

//////////////////////////////////////////////////////////////////
// void InitialiseEngine(void)
//
// Sets up the PS2 engine, down to the hardware level
//  - Resets the hardware, using sce library calls, setting video mode
//  - sets up the PCRTC registers (display buffer conficuration, and read circuit anti-aliasing)
//  - Initilizes memory for DMA buffers
//  - Builds the standard DMA subroutines (clear VRAM, page flip, etc)
//  - adds GS, VIF1 and GIF interrupts
//  - creates an empty "scene" and an empty group as the default things to render


void InitialiseEngine(void)
{
    // Mick:  Make it safe to call more than once
    // Needed for a last minute loading screen hack TT#12480
	static bool done = false;
	if (done)
	{
		return;
	}
	done = true;
	
	// Initialize devices
	sceDmaReset(1);
	sceGsResetPath();
    sceDevVu1PutDBit(1);	// sceGsResetPath will clear the D-bit, so need to switch it on
    //sceDevVu0PutTBit(1);

	// setup PCRTC registers - switch off display until vram has been cleared
	SetupPCRTC(0, SCE_GS_FIELD);

	// Clear resoruces
	CSystemResources::sInitResources();

	// allocate dma list memory (prebuilt & run-time) and subroutine table
	dma::pPrebuiltBuffer = (uint8 *)Mem::Calloc(PREBUILT_DMA_BUFFER_SIZE/16,16);
	Dbg_MsgAssert(dma::pPrebuiltBuffer, ("couldn't malloc memory for prebuilt dma buffer"));
	
	if (Config::GotExtraMemory())
	{
		// Allocate a dummy buffer so that the current heap's usage is the same
		// as for when there is not extra memory.
		dma::pDummyBuffer = (uint8 *)Mem::Calloc(NON_DEBUG_DMA_BUFFER_SIZE/16,16);
		
		// But for the actual runtime buffer allocate a massive buffer off the debug heap.
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
		dma::pRuntimeBuffer = (uint8 *)Mem::Calloc(DEBUG_DMA_BUFFER_SIZE/16,16);
		Mem::Manager::sHandle().PopContext();
		
		Dbg_MsgAssert(dma::pRuntimeBuffer, ("couldn't malloc memory for runtime dma buffer"));
		// set runtime dma list pointers (they are double buffered, using half the buffer each)
		dma::pList[0] = dma::pRuntimeBuffer;
		dma::pList[1] = dma::pRuntimeBuffer + DEBUG_DMA_BUFFER_SIZE/2;
	}
	else
	{
		dma::pRuntimeBuffer = (uint8 *)Mem::Calloc(NON_DEBUG_DMA_BUFFER_SIZE/16,16);
		Dbg_MsgAssert(dma::pRuntimeBuffer, ("couldn't malloc memory for runtime dma buffer"));
		// set runtime dma list pointers (they are double buffered, using half the buffer each)
		dma::pList[0] = dma::pRuntimeBuffer;
		dma::pList[1] = dma::pRuntimeBuffer + NON_DEBUG_DMA_BUFFER_SIZE/2;
	}
	dma::size = NON_DEBUG_DMA_BUFFER_SIZE/2;  // normal sized (half the buffer)
	
		
	dma::Gosubs = (uint64 *)Mem::Calloc(NUM_SUBROUTINES, sizeof(uint64));
	Dbg_MsgAssert(dma::Gosubs, ("couldn't malloc memory for dma gosub table"));
	//Subroutines = (uint8 **)Mem::Calloc(256, sizeof(uint8 *));
	//Dbg_MsgAssert(Subroutines, ("couldn't malloc memory for dma gosub table"));

	// initialise dma pointer for use by BuildDmaSubroutines
	dma::pLoc = dma::pPrebuiltBuffer;

	// prebuild dma subroutines (into dma::pPrebuiltBuffer)
	BuildDmaSubroutines();

	// Enable interrupts
	StartInterrupts();

	// wait for v-sync
	sceGsSyncV(0);

	// clear vram
	FlushCache(WRITEBACK_DCACHE);
	*D1_QWC  = 0;						// must zero QWC because the first action will be to use current MADR & QWC
	*D1_TADR = dma::Gosubs[CLEAR_VRAM] >> 32;
	*D1_CHCR = 0x145;					// start transfer, tte=1, chain mode, from memory
	sceGsSyncPath(0, 0);

	// setup PCRTC registers - vram is cleared so switch on display
	SetupPCRTC(1, SCE_GS_FIELD);

	// upload vu0 code
	*D0_QWC  = ((uint8 *)MPGEnd0-(uint8 *)MPGStart0+15)/16;
	*D0_MADR = (uint)MPGStart0;
	*D0_CHCR = 0x100;					// start transfer, normal mode
	sceGsSyncPath(0, 0);

	// empty scene list
	sScene::pHead = NULL;

	// create the shadow group
	sGroup::pShadow = (sGroup *)Mem::Malloc(sizeof(sGroup));
	memset(sGroup::pShadow,0,sizeof(sGroup));
	Dbg_MsgAssert(sGroup::pShadow, ("couldn't allocate shadow group\n"));
	sGroup::pShadow->Priority = 1500.001f;
	sGroup::pShadow->pMeshes = NULL;
	sGroup::pShadow->NumMeshes = 0;
	#if STENCIL_SHADOW
	sGroup::pShadow->VramStart = 0x3740;
	#else
	sGroup::pShadow->VramStart = 0x3E00;
	#endif
	sGroup::pShadow->VramEnd   = 0x4000;
	sGroup::pShadow->flags = 0;
	sGroup::pShadow->profile_color = 0xFFFFFF;		// bright white = shadow group

	// create the fog group
	sGroup::pFog = (sGroup *)Mem::Malloc(sizeof(sGroup));
	memset(sGroup::pFog,0,sizeof(sGroup));
	Dbg_MsgAssert(sGroup::pFog, ("couldn't allocate fog group\n"));
	sGroup::pFog->Priority = 9999.0f;
	sGroup::pFog->pMeshes = NULL;
	sGroup::pFog->NumMeshes = 0;
	sGroup::pFog->VramStart = 0x3000;
	sGroup::pFog->VramEnd   = 0x4000;
	sGroup::pFog->flags = 0;
	sGroup::pFog->pScene = NULL;
	sGroup::pFog->profile_color = 0x303030;			// dark grey = fog group
	
	// create the particle group
	sGroup::pParticles = (sGroup *)Mem::Malloc(sizeof(sGroup));
	memset(sGroup::pParticles,0,sizeof(sGroup));
	Dbg_MsgAssert(sGroup::pParticles, ("couldn't allocate particle group\n"));
	sGroup::pParticles->Priority = 10000.0f;
	sGroup::pParticles->pMeshes = NULL;
	sGroup::pParticles->NumMeshes = 0;
	sGroup::pParticles->VramStart = 0x3000;
	sGroup::pParticles->VramEnd   = 0x4000;
	sGroup::pParticles->flags = 0;
	sGroup::pParticles->pScene = NULL;
	sGroup::pParticles->profile_color = 0x0;		// black = particle group

	// create epilogue group
	sGroup::pEpilogue = (sGroup *)Mem::Malloc(sizeof(sGroup));
	memset(sGroup::pEpilogue,0,sizeof(sGroup));
	Dbg_MsgAssert(sGroup::pEpilogue, ("couldn't allocate epilogue group\n"));
	sGroup::pEpilogue->Priority = 1000000;
	sGroup::pEpilogue->pMeshes = NULL;
	sGroup::pEpilogue->NumMeshes = 0;
	sGroup::pEpilogue->VramStart = 0x0000;	// pretend it uses all of vram
	sGroup::pEpilogue->VramEnd   = 0x4000;	// so there's no buffer address worries
	sGroup::pEpilogue->profile_color = 0x800000;		// blue = epilogue group

	// link predefined groups together
	sGroup::pShadow->pNext = sGroup::pFog;
	sGroup::pFog->pNext = sGroup::pParticles;
	sGroup::pParticles->pNext = sGroup::pEpilogue;
	sGroup::pEpilogue->pNext = NULL;
	sGroup::pShadow->pPrev = NULL;
	sGroup::pFog->pPrev = sGroup::pShadow;
	sGroup::pParticles->pPrev = sGroup::pFog;
	sGroup::pEpilogue->pPrev = sGroup::pParticles;
	sGroup::pHead = sGroup::pShadow;
	sGroup::pTail = sGroup::pEpilogue;

	// initialise vram group buffering scheme
	sGroup::VramBufferBase = 0x2BC0;

	// initialise font vram base pointer
	FontVramStart = FontVramBase = 0x2BC0;	// a cheat

	// initialise empty font list
	pFontList = NULL;



	// activate the world!
	CGeomNode::sWorld.SetActive(true);

	// set up fog palette
//	Fx::SetupFogPalette(0x40806060, 10.0f);
}

void 	AllocateExtraDMA(bool allocate)
{
	static void * s_extra = NULL;
	static void * old_pList1;
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());

	if (allocate)
	{
		Dbg_MsgAssert(!s_extra,("Tried to allocate extra DMA when already allocated"));
		old_pList1 = dma::pList[1];
		s_extra = (uint8 *)Mem::Calloc(NON_DEBUG_DMA_BUFFER_SIZE/16,16);	  // same size as the entire double buffer
		dma::pList[1] = (uint8 *)s_extra;
		dma::size = NON_DEBUG_DMA_BUFFER_SIZE;  // double sized
	}
	else
	{
		if (s_extra)
		{
			dma::pList[1] = (uint8 *)old_pList1;
			Mem::Free(s_extra);
			s_extra = NULL;
			dma::size = NON_DEBUG_DMA_BUFFER_SIZE/2;  // normal sized
		}
	}
	
	dma::pLoc = 0 ;  	// reset pLoc as it might not be valid.  This will catch this function being called in the wrong place
	
	Mem::Manager::sHandle().PopContext();	
}


//////////////////////////////////////////////////////////////////
// void ResetEngine(void)
//
// Sets up the PS2 engine, down to the hardware level
//  - Resets the hardware, using sce library calls, setting video mode
//  - sets up the PCRTC registers (display buffer conficuration, and read circuit anti-aliasing)
//  - clears VRAM, which also sets the TEXA register
//  - resets GS, VIF1 and GIF interrupts

void ResetEngine(void)
{
	// initialize devices
	sceDmaReset(1);
	sceGsResetPath();
    sceDevVu1PutDBit(1);	// sceGsResetPath will clear the D-bit, so need to switch it on
    //sceDevVu0PutTBit(1);

	// Don't call sceGsResetGraph() anymore.  It interferes with SetupPCRTC().
	//// re-initialize the GS
	//if (Config::NTSC())
	//{
	//	sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FIELD);
	//} else {
	//	sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_PAL, SCE_GS_FIELD);
	//}

	// setup PCRTC registers (don't need this call anymore; the last call is all that is necessary)
	//SetupPCRTC(0, SCE_GS_FIELD);

	// restart interrupts
	StartInterrupts();

	// wait for v-sync
	sceGsSyncV(0);

	// clear vram (Garrett: This is the ONLY place that TEXA is ever set!  Caused many weird bugs if not called.)
	FlushCache(WRITEBACK_DCACHE);
	*D1_QWC  = 0;						// must zero QWC because the first action will be to use current MADR & QWC
	*D1_TADR = dma::Gosubs[CLEAR_VRAM] >> 32;
	*D1_CHCR = 0x145;					// start transfer, tte=1, chain mode, from memory
	sceGsSyncPath(0, 0);

	// setup PCRTC registers - vram is cleared so switch on display
	SetupPCRTC(1, SCE_GS_FIELD);

}

//////////////////////////////////////////////////////////////////
// void SuspendEngine(void)
//
// Suspends the PS2 engine by waiting for any rendering to finish
// and then shuts down the interrupts.

void SuspendEngine(void)
{
	// Let the engine finish what it was doing first
	WaitForRendering();

	// stop interrupts
	StopInterrupts();
}


//////////////////////////////////////////////////////////////////
// void StartInterrupts(void)
//
// Starts all the interrupts for the PS2 engine.

static void StartInterrupts(void)
{
	#if USE_INTERRUPTS
	
	#if GIF_INTERRUPT
    // install interrupt handler for texture upload completion
	if ((sGifHandlerId = AddDmacHandler(DMAC_GIF, GifHandler, 0)) == -1)
	{
		printf("Couldn't register dmac handler\n");
		exit(1);
	}
	*D_STAT = 1 << DMAC_GIF;
	EnableDmac(DMAC_GIF);
	#endif

	#if GS_INTERRUPT
    // install interrupt handler for render completion
	if ((sGsHandlerId = AddIntcHandler(INTC_GS, GsHandler, 0)) == -1)
	{
		printf("Couldn't register GS handler\n");
		exit(1);
	}
	*GS_IMR = PackIMR(0,1,1,1);		// SIGNAL, FINISH, HSync, VSync
	EnableIntc(INTC_GS);
	#endif
	
	#if VIF1_INTERRUPT
    // install interrupt handler for render completion
	if ((sVif1HandlerId = AddIntcHandler(INTC_VIF1, Vif1Handler, 0)) == -1)
	{
		printf("Couldn't register vif1 handler\n");
		exit(1);
	}
	*VIF1_ERR = 0;
	EnableIntc(INTC_VIF1);
	#endif

	// set intermittent mode transfer
	*GIF_MODE = 0x0;
	#endif

}



//////////////////////////////////////////////////////////////////
// void StopInterrupts(void)
//
// Stops all the interrupts for the PS2 engine.

static void StopInterrupts(void)
{
	#if USE_INTERRUPTS
	
	#if GIF_INTERRUPT
    // remove interrupt handler for texture upload completion
	DisableDmac(DMAC_GIF);
	if (sGifHandlerId >= 0)
	{
		RemoveDmacHandler(DMAC_GIF, sGifHandlerId);
		sGifHandlerId = -1;
	}
	#endif

	#if GS_INTERRUPT
    // remove interrupt handler for render completion
	DisableIntc(INTC_GS);
	if (sGsHandlerId >= 0)
	{
		RemoveIntcHandler(INTC_GS, sGsHandlerId);
		sGsHandlerId = -1;
	}
	#endif
	
	#if VIF1_INTERRUPT
    // remove interrupt handler for render completion
	DisableIntc(INTC_VIF1);
	if (sVif1HandlerId >= 0)
	{
		RemoveIntcHandler(INTC_VIF1, sVif1HandlerId);
		sVif1HandlerId = -1;
	}
	#endif

	#endif
}

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/nx_init.h
================================================
#ifndef __NX_INIT_H
#define __NX_INIT_H

namespace NxPs2
{

void InitialiseEngine(void);
void ResetEngine(void);
void SuspendEngine(void);
void AllocateExtraDMA(bool allocate);

} // namespace NxPs2


#endif // __NX_INIT_H



================================================
FILE: Code/Gfx/NGPS/NX/occlude.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		occlude.cpp												**
**																			**
**	Created:		04/02/02	-	dc										**
**																			**
**	Description:	Occlusion testing code									**
**																			**
*****************************************************************************/



/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/
		
#include 
#include 
//#include 
#include "occlude.h"
#include "resource.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

//extern D3DXMATRIX *p_bbox_transform;

namespace NxPs2
{


/*****************************************************************************
**								   Defines									**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static bool sCopiedOccludersOffline = false;		// tells that occluders have been copied to a fast ram and the

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

uint32			NumOcclusionPolys			= 0;
uint32			NextOcclusionPolyToCheck	= 0;
sOcclusionPoly	OcclusionPolys[MAX_OCCLUSION_POLYS];

uint32		sOccluder::NumOccluders				= 0;
sOccluder	sOccluder::Occluders[MAX_OCCLUDERS];
bool		sOccluder::sUseVU0 = false;
bool		sOccluder::sUseScratchPad = false;


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sOccluder::add_to_stack( sOcclusionPoly *p_poly )
{
	if( NumOccluders < MAX_OCCLUDERS )
	{
		Occluders[NumOccluders].p_poly	= p_poly;
		p_poly->in_use					= true;
		++NumOccluders;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int cmp( const void *p1, const void *p2 )
{
	// Zero the score for any occlusion poly that is no longer available. This will force it out of the stack.
	if(((sOccluder*)p1)->p_poly->available == false )
		((sOccluder*)p1)->score = 0;

	if(((sOccluder*)p2)->p_poly->available == false )
		((sOccluder*)p2)->score = 0;

	return((sOccluder*)p1)->score < ((sOccluder*)p2)->score ? 1 : ((sOccluder*)p1)->score > ((sOccluder*)p2)->score ? -1 : 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sOccluder::sort_stack( void )
{
	qsort( Occluders, NumOccluders, sizeof( sOccluder ), cmp );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sOccluder::tidy_stack( void )
{
	if( NumOccluders > 0 )
	{
		// Sort in descending score order.
		sort_stack();
		

		int total = 0;
		int demoted = 0;
		// Count backwards so we know we get all the bad occluders.
		for( int i = NumOccluders - 1; i >= 0; --i )
		{
			// If we have hit an occluder with zero meshes culled, cut off the stack at this point.
			// also, if the stack is full, then demote the lowest scoring 4
			if( Occluders[i].score == 0  || i > (int)(MAX_OCCLUDERS-MIN_NEW_OCCLUSION_POLYS_TO_CHECK_PER_FRAME))
			{
				// No longer using this poly.
				Occluders[i].p_poly->in_use = false;

				// One less occluder to worry about.
				--NumOccluders;
				demoted++;
			}
			else
			{
				total += Occluders[i].score;
				// Reset the good occluders.
				Occluders[i].score = 0;
			}
		}
//		printf ("\nOccluded %d objects with %d Occluders (demoted %d)\n",total,NumOccluders,demoted);
	}
	
}



/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/* Used to add an occlusion poly to the list. Likely to be called */
/* as geometry is spooled in.                                     */
/*                                                                */
/******************************************************************/
void AddOcclusionPoly( Mth::Vector &v0, Mth::Vector &v1, Mth::Vector &v2, Mth::Vector &v3, uint32 checksum )
{
//	printf (" Adding occlusion polygons, currently %d\n",NumOcclusionPolys);
	Dbg_MsgAssert( NumOcclusionPolys < MAX_OCCLUSION_POLYS,("Too many (%d) occlusion polygons",NumOcclusionPolys) );	
	
	OcclusionPolys[NumOcclusionPolys].in_use	= false;
	OcclusionPolys[NumOcclusionPolys].available	= true;
	OcclusionPolys[NumOcclusionPolys].checksum	= checksum;
	OcclusionPolys[NumOcclusionPolys].verts[0]	= v0;
	OcclusionPolys[NumOcclusionPolys].verts[1]	= v1;
	OcclusionPolys[NumOcclusionPolys].verts[2]	= v2;
	OcclusionPolys[NumOcclusionPolys].verts[3]	= v3;
	OcclusionPolys[NumOcclusionPolys].normal	= Mth::CrossProduct( v1 - v0, v3 - v0 );
	OcclusionPolys[NumOcclusionPolys].normal.Normalize();
	
	++NumOcclusionPolys;

    sCopiedOccludersOffline = false;		// current scores now invalid
}



/******************************************************************/
/*                                                                */
/* Used to toggle whether an occlusion poly can be used or not	  */
/*                                                                */
/******************************************************************/
void EnableOcclusionPoly( uint32 checksum, bool available )
{
	for( uint32 i = 0; i < NumOcclusionPolys; ++i )
	{
		if( OcclusionPolys[i].checksum == checksum )
		{
			OcclusionPolys[i].available	= available;
		}
	}
}



/******************************************************************/
/*                                                                */
/* Used to clear all occlusion polys (when a level is unloaded)   */
/*                                                                */
/******************************************************************/
void RemoveAllOcclusionPolys( void )
{
	Dbg_Assert( NumOcclusionPolys < MAX_OCCLUSION_POLYS );	
	
 	sOccluder::NumOccluders		= 0;
	NumOcclusionPolys			= 0;
	NextOcclusionPolyToCheck	= 0;

    sCopiedOccludersOffline = false;		// current scores now invalid
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CheckForOptimalOccluders( Mth::Vector &cam_pos, Mth::Vector &view_direction )
{
	if( NumOcclusionPolys > 0 )
	{
		uint32 added	= 0;
		uint32 checked	= 0;

		while( added < MAX_NEW_OCCLUSION_POLYS_TO_CHECK_PER_FRAME )
		{
			// Given the current position of the camera, check through the unused occlusion polys to see if one scores higher
			// than the lowest scoring occlusion poly in use.
			sOcclusionPoly *poly_to_check = &OcclusionPolys[NextOcclusionPolyToCheck++];
			if(( !poly_to_check->in_use ) && ( poly_to_check->available ))
			{
				sOccluder::add_to_stack( poly_to_check );
				++added;
			}
			++checked;

			// Ensure we are always checking within bounds.
			if( NextOcclusionPolyToCheck >= NumOcclusionPolys )
			{
				NextOcclusionPolyToCheck = 0;
			}

			// Quit out if we have less available occluders than spaces to fill.
			if( checked >= NumOcclusionPolys )
			{
				break;
			}
		}
	}
}


//char scratch[16384];


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void BuildOccluders( Mth::Vector *p_cam_pos )
{
#ifdef	OCCLUDER_USES_VU0_MACROMODE
	if (sCopiedOccludersOffline)
	{
		sOccluder * p_occluder = &sOccluder::Occluders[0];
		uint128 * p_vu0_mem = (uint128*)0x11004060;     // Start of first score

		for( uint32 o = sOccluder::NumOccluders; o > 0 ; --o )
		{
			p_occluder->score = *((uint32*) (p_vu0_mem));
			p_occluder++;
			p_vu0_mem += 6;
		}

		sCopiedOccludersOffline = false;	// we are now up-to-date
	}
#endif
	for( uint32 i = 0; i < NumOcclusionPolys; ++i )
	{
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[0], OcclusionPolys[i].verts[1], 0xFFFFFFFFUL, 0, 1 );
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[1], OcclusionPolys[i].verts[2], 0xFFFFFFFFUL, 0, 1 );
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[2], OcclusionPolys[i].verts[3], 0xFFFFFFFFUL, 0, 1 );
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[3], OcclusionPolys[i].verts[0], 0xFFFFFFFFUL, 0, 1 );
	}
	
	// Tidy up from last frame.
	sOccluder::tidy_stack();
	
	// Cyclically add more occluders for checking.
	CheckForOptimalOccluders( *p_cam_pos, *p_cam_pos );
	
	// Build all 5 planes for each occluder.
	Mth::Vector u0, u1, p;

	// The order in which the verts are used to build tha planes depends upon where the camera is in relation to the occlusion
	// poly. We use the default order when the viewpoint is on the side of the poly on which the default poly normal faces.
	for( uint32 i = 0; i < sOccluder::NumOccluders; ++i )
	{
		sOcclusionPoly *p_poly = sOccluder::Occluders[i].p_poly;
		if( Mth::DotProduct( *p_cam_pos - p_poly->verts[0], p_poly->normal ) >= 0.0f )
		{
			// Start with left (0->1->2)...
			u0						= p_poly->verts[0] - *p_cam_pos;
			u1						= p_poly->verts[1] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= -Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[0]	= p;

			// ...then right (0->3->4)...
			u0						= p_poly->verts[2] - *p_cam_pos;
			u1						= p_poly->verts[3] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= -Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[1]	= p;

			// ...then top (0->2->3)...
			u0						= p_poly->verts[1] - *p_cam_pos;
			u1						= p_poly->verts[2] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= -Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[2]	= p;

			// ...then bottom (0->4->1)...
			u0						= p_poly->verts[3] - *p_cam_pos;
			u1						= p_poly->verts[0] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= -Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[3]	= p;

			// ...then front. We want to reverse the order of the front plane to ensure that objects *behind* the plane
			// are considered occluded. (1->3->4)...
			u0						= p_poly->verts[2] - p_poly->verts[0];
			u1						= p_poly->verts[3] - p_poly->verts[0];
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= -Mth::DotProduct( p, p_poly->verts[0] );
			sOccluder::Occluders[i].planes[4]	= p;
		}
		else
		{
			// Start with left (0->2->1)...
			u0						= p_poly->verts[1] - *p_cam_pos;
			u1						= p_poly->verts[0] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= -Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[0]	= p;

			// ...then right (0->4->3)...
			u0						= p_poly->verts[3] - *p_cam_pos;
			u1						= p_poly->verts[2] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= -Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[1]	= p;

			// ...then top (0->3->2)...
			u0						= p_poly->verts[2] - *p_cam_pos;
			u1						= p_poly->verts[1] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= -Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[2]	= p;

			// ...then bottom (0->1->4)...
			u0						= p_poly->verts[0] - *p_cam_pos;
			u1						= p_poly->verts[3] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= -Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[3]	= p;

			// ...then front. We want to reverse the order of the front plane to ensure that objects *behind* the plane
			// are considered occluded. (1->4->3)...
			u0						= p_poly->verts[3] - p_poly->verts[0];
			u1						= p_poly->verts[2] - p_poly->verts[0];
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= -Mth::DotProduct( p, p_poly->verts[0] );
			sOccluder::Occluders[i].planes[4]	= p;
		}
	}


	if (sOccluder::sUseScratchPad)
	{
		// Copy the occlusion polygons to the scratchpad
		// as we will read them a zillion times during rendering
		sOccluder * p_occluder = &sOccluder::Occluders[0];
		sOccluder * p_scratch = (sOccluder*)0x70000000;
	//	sOccluder * p_scratch = (sOccluder*)scratch;
		for( uint32 o = sOccluder::NumOccluders; o > 0 ; --o )
		{
			*p_scratch = *p_occluder;
			p_occluder++;
			p_scratch++;
		}
		Dbg_MsgAssert((void*)p_scratch < (void*)0x70004000,("Scratchpad overflow"));  // 16k scratchpad.  Nice.
	}

#ifdef	OCCLUDER_USES_VU0_MACROMODE
	sOccluder * p_occluder = &sOccluder::Occluders[0];
	uint128 * p_vu0_mem = (uint128*)0x11004000;		// VU0 data memory

	// Copies the number of occulders into X
	*((uint32*) (p_vu0_mem++)) = sOccluder::NumOccluders;

	for( uint32 o = sOccluder::NumOccluders; o > 0 ; --o )
	{
		*(p_vu0_mem++) = *((uint128*) &(p_occluder->planes[0]));
		*(p_vu0_mem++) = *((uint128*) &(p_occluder->planes[1]));
		*(p_vu0_mem++) = *((uint128*) &(p_occluder->planes[2]));
		*(p_vu0_mem++) = *((uint128*) &(p_occluder->planes[3]));
		*(p_vu0_mem++) = *((uint128*) &(p_occluder->planes[4]));
		*((uint32*) (p_vu0_mem++)) = p_occluder->score;
		p_occluder++;
	}

	FlushCache(WRITEBACK_DCACHE);

	Dbg_MsgAssert((void*)p_vu0_mem < (void*)0x11005000,("VU0 memory overflow"));  // 4k vu0 mem.  Nice.

    sCopiedOccludersOffline = true;			// so we know to copy the scores back (technically, this should be in
											// TestSphereAgainstOccluders(), but we only want to write this var once
#endif




#ifdef	OCCLUDER_USES_VU0_MICROMODE

	// copy occlusion planes to VUMem0
	sOccluder * p_occluder = &sOccluder::Occluders[0];
// Mick: removed this to fix a warning in final build.
//	float * p_vumem0 = (float *)0x11004000;		// VUMem0
	Mth::Vector * p_plane;
	asm ("viaddi vi01,vi00,0": : : "$8");
	for( uint32 o = sOccluder::NumOccluders; o > 0 ; --o )
	{
		p_plane = &(p_occluder->planes[0]);
		for( uint32 p = 5; p > 0; --p )
		{
			#if 0
			*p_vumem0++ = p_plane->GetX();
			*p_vumem0++ = p_plane->GetY();
			*p_vumem0++ = p_plane->GetZ();
			*p_vumem0++ = p_plane->GetW();
			#else
			// this way we don't need the FlushCache()
			asm __volatile__("

				lq		$8,0(%0)
				qmtc2	$8,vf01
				vsqi	vf01,(vi01++)

			": : "r" (p_plane) : "$8");
			#endif
			p_plane++;
		}
		p_occluder++;
	}
// Mick - removed this assertions as it is no longer valid
//	Dbg_MsgAssert((void*)p_vumem0 < (void*)0x11004FA0,("VUMem0 overflow"));		// 50 occluders max

	// flush the cache so VU0 can work on the data now.... not needed anymore
	//FlushCache(WRITEBACK_DCACHE);

	// call vu0 microsubroutine to rearrange data into a special format
	asm __volatile__("

		#sync									# wait for write-back of the mem we just wrote to
		lw			$8,(%0)						# t0 = num occluders
		ctc2		$8,$vi01					# move it to vi01 in vu0
		vcallms		InitialiseOccluders			# call microsubroutine to rearrange vu0 memory
		vnop									# interlocking instruction, waits for vu0 completion
	
	": : "r" (&sOccluder::NumOccluders) : "$8");


#endif


}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool OccludeUseVU0()
{
	return sOccluder::sUseVU0 = CSystemResources::sRequestResource(CSystemResources::vVU0_MEMORY);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void OccludeDisableVU0()
{
	if (sOccluder::sUseVU0)
	{
		CSystemResources::sFreeResource(CSystemResources::vVU0_MEMORY);
		sOccluder::sUseVU0 = false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool OccludeUseScratchPad()
{
	return sOccluder::sUseScratchPad = CSystemResources::sRequestResource(CSystemResources::vSCRATCHPAD_MEMORY);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void OccludeDisableScratchPad()
{
	if (sOccluder::sUseScratchPad)
	{
		CSystemResources::sFreeResource(CSystemResources::vSCRATCHPAD_MEMORY);
		sOccluder::sUseScratchPad = false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


} // namespace NxXbox


================================================
FILE: Code/Gfx/NGPS/NX/occlude.h
================================================
#ifndef	__OCCLUDE_H__
#define	__OCCLUDE_H__

//#define	OCCLUDER_USES_VU0_MACROMODE
#define	OCCLUDER_USES_VU0_MICROMODE


namespace NxPs2
{
	void AddOcclusionPoly( Mth::Vector &v0, Mth::Vector &v1, Mth::Vector &v2, Mth::Vector &v3, uint32 checksum = 0 );
	void EnableOcclusionPoly( uint32 checksum, bool available );
	void RemoveAllOcclusionPolys( void );
	void BuildOccluders( Mth::Vector *p_cam_pos );
	bool TestSphereAgainstOccluders( Mth::Vector *p_center, float radius, uint32 meshes = 1 );

	bool OccludeUseVU0();
	void OccludeDisableVU0();

	bool OccludeUseScratchPad();
	void OccludeDisableScratchPad();


const uint32 MAX_NEW_OCCLUSION_POLYS_TO_CHECK_PER_FRAME = 4;
const uint32 MIN_NEW_OCCLUSION_POLYS_TO_CHECK_PER_FRAME = 4;

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

// Structure used to store details of a single poly. A list of these will be built at geometry load time.
struct sOcclusionPoly
{
	bool		in_use;
	bool		available;	// Whether the poly is available for selection for occlusion.
	uint32		checksum;	// Name checksum of the occlusion poly.
	Mth::Vector	verts[4];
	Mth::Vector	normal;
};
	
const uint32	MAX_OCCLUDERS				= 12;

struct sOccluder
{
	static uint32		NumOccluders;
	static sOccluder	Occluders[MAX_OCCLUDERS];
	static bool			sUseVU0;
	static bool			sUseScratchPad;

	static void			add_to_stack( sOcclusionPoly *p_poly );
	static void			sort_stack( void );
	static void			tidy_stack( void );

	sOcclusionPoly	*p_poly;
	Mth::Vector		planes[5];
	int				score;			// Current rating on quality of occlusion - based on number of meshes occluded last frame.
};

const uint32	MAX_OCCLUSION_POLYS			= 128; 		// not currently useing that many
extern uint32			NumOcclusionPolys;
extern uint32			NextOcclusionPolyToCheck;
extern sOcclusionPoly	OcclusionPolys[];



}

#endif



================================================
FILE: Code/Gfx/NGPS/NX/pcrtc.cpp
================================================
#include 
#include 
#include 
#include "pcrtc.h"
#include "gs.h"
#include "switches.h"
#include "render.h"

namespace NxPs2
{

///////////////////////////////////////////////////////////////////////////////
// void SetupPCRTC(void)
//
// Sets up the read circuit blending that provides anti-aliasing
// also sets the background color (to 0,0,0)
//
// Note, this is dependent on HRES and VRES for horizontal and vertical resolution

void SetupPCRTC(int Enable, int FFMode, int HRes, int VRes)		// HRes and VRes default to 0
{
	// initialize the GS once with sceGsResetGraph().  It is the only way to set NTSC or PAL mode.
	static bool inited = false;
	if (!inited)
	{
		sceGsSyncV(0);
		if (Config::NTSC())
		{
			sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FIELD);
		} else {
			sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_PAL, SCE_GS_FIELD);
		}

		inited = true;
	}

	// zero resolution => use default res
	if (!HRes)
		HRes = HRES;
	if (!VRes)
		VRes = VRES;

	tGS_SMODE2   smode2;
	tGS_PMODE    pmode;
	tGS_DISPFB1  dispfb1;
	tGS_DISPLAY1 display1;
	tGS_DISPFB2  dispfb2;
	tGS_DISPLAY2 display2;
	tGS_BGCOLOR  bgcolor;

	// sync mode (SMODE2 register)
	smode2.INT  = SCE_GS_INTERLACE;
	smode2.FFMD = FFMode;
	smode2.DPMS = 0;

	pmode.EN1   = 1;				// enable Read Circuit 1
	pmode.EN2   = 1;				// enable Read Circuit 2
	pmode.CRTMD = 1;				// always 1
	pmode.MMOD  = 1;				// use value in ALP for blending
	pmode.AMOD  = 0;				// OUT1 alpha output selection (irrelevant for now)
	pmode.SLBG  = Enable?0:1;		// blend with the output of Read Circuit 2 if Enable=1, or with BGCOLOR if Enable=0
	pmode.ALP 	= Enable?128:0;		// alpha=0.5 if Enable=1, or 0.0 if Enable=0
	pmode.p0	= 0;				// GS manual states that unused bits of PMODE should be set to zero
	pmode.p1	= 0;

	// Read Circuit 2 (DISPFB2 and DISPLAY2 registers)
	dispfb2.FBP = (HRes*VRes/2048);
	dispfb2.FBW = HRes/64;    //   *2 this to also display 1/2 of next frame
 	dispfb2.PSM = PSMCT16S;
	dispfb2.DBX = 240-240/1;		// 1 is the scale
	dispfb2.DBY = 224-224/1;		// 1 is the scale

	if (Config::NTSC())
	{
		display2.DX   = 0x27C;		// horizontal offset }
		display2.DY   = 0x32;		// vertical offset   }
	} else {						//					 } see SCE_GS_SET_DISPLAY_ macros in eestruct.h
		display2.DX   = 0x290;		// horizontal offset }
		display2.DY   = 0x48;		// vertical offset	 }
	}
	display2.MAGH = 2560/HRes-1;	// see GS manual V5.0, p86
	display2.MAGV = 0;				// *1 vertical magnification (reg value plus 1)
	display2.DW   = 2560-1;
	display2.DH	  = VRes-1;
	
	// Read Circuit 1 (DISPFB1 and DISPLAY1 registers)
	//... copy Read Circuit 2 and then shift by 1 row to blend the fields
	dispfb1  = *(tGS_DISPFB1 *)&dispfb2;
	display1 = *(tGS_DISPLAY1 *)&display2;
	display1.DY -= 1;				// offset by 1 scanline, to blend the fields together

	// background colour (Sony says should always be black, unfortunately)
	bgcolor.R = 0;
	bgcolor.G = 0;
	bgcolor.B = 0;

	// write values to GS
	sceGsSyncV(0);
	*GS_SMODE2	 = *(volatile u_long *)&smode2;
	*GS_DISPFB1	 = *(volatile u_long *)&dispfb1;
	*GS_DISPLAY1 = *(volatile u_long *)&display1;
	*GS_DISPFB2	 = *(volatile u_long *)&dispfb2;
	*GS_DISPLAY2 = *(volatile u_long *)&display2;
	*GS_BGCOLOR  = *(volatile u_long *)&bgcolor;
	*GS_PMODE	 = *(volatile u_long *)&pmode;
}

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/pcrtc.h
================================================
#ifndef __PCRTC_H
#define __PCRTC_H

namespace NxPs2
{

void SetupPCRTC(int Enable, int FFMode, int HRes=0, int VRes=0);

} // namespace NxPs2

#endif // __PCRTC_H



================================================
FILE: Code/Gfx/NGPS/NX/render.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "mikemath.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gif.h"
#include "gs.h"
#include "vu1code.h"
#include "dmacalls.h"
#include "line.h"
#include "chars.h"
#include "texture.h"
#include "material.h"
#include "group.h"
#include "scene.h"
#include "mesh.h"
#include "nx_init.h"
#include "render.h"
#include "interrupts.h"
#include "switches.h"
#include "sprite.h"
#include "instance.h"
#include "geomnode.h"
#include "occlude.h"
#include "resource.h"
#include "vu1context.h"
#include "light.h"
#include "fx.h"
#include "asmdma.h"
#include "vu1\newvu1code.h"

#include "gfx\nx.h"
#include "gfx\ngps\p_nxscene.h"
#include "gfx\ngps\p_nxtexture.h"


//#undef	__USE_PROFILER__


namespace Inp
{
	extern uint32	gDebugButtons[];
	extern uint32	gDebugMakes[];

}

namespace NxPs2
{

extern uint32	*p_patch_PRMODE;
extern uint32 gPassMask1;


extern int geom_stats_total;
extern int geom_stats_inactive ;
extern int geom_stats_sky;
extern int geom_stats_transformed;
extern int geom_stats_skeletal;
extern int geom_stats_camera_sphere;
extern int geom_stats_clipcull;
extern int geom_stats_culled;
extern int geom_stats_leaf_culled;
extern int geom_stats_boxcheck;
extern int geom_stats_occludecheck;
extern int geom_stats_occluded;
extern int geom_stats_occludecheck;
extern int geom_stats_occluded;
extern int geom_stats_occludecheck;
extern int geom_stats_occluded;
extern int geom_stats_colored;
extern int geom_stats_leaf;
extern int geom_stats_wibbleUV;
extern int geom_stats_wibbleVC;
extern int geom_stats_sendcontext;
extern int geom_stats_sorted;
extern int geom_stats_shadow;

// Mick:  Letterboxing on the ps2 is a post-processing
// step one frame ahead in the rendering pipeline (as
// compared to things like setting the FOV), so 
// it must be delayed by 1 frame to synchronize it
// with such events.
bool ActualDoLetterbox = false;
bool DoLetterbox = false;

int	DMAOverflowOK  = false;

bool DoFlipCopy = true;
sGroup PrologueGroup;
float SkaterY;

int	SingleRender = 0;

// skinning globals
Mat SkaterTransform;
Mat BoneTransforms[64];
int NumBones=0;


#if STENCIL_SHADOW
void CastStencilShadow(CInstance *pInstance, sGroup *p_group, Mth::Vector &ShadowVec, Mth::Vector &TweakVec);
#else
void CastShadow(CInstance *pInstance, sGroup *p_group);
#endif

void EnableFlipCopy(bool flip)
{
	DoFlipCopy = flip;
}

bool FlipCopyEnabled()
{
	return DoFlipCopy;
}

//////////////////////////////////////////////////////////////////
// void RenderPrologue(void)
//
// called at the start of a frame
// sets up the initial DMA list
// by setting up a prologue "group"
// consisting of an empty upload portion, and 
// a render portion that flips the visible buffers,
// and sets up various vif settings
// finally we initilize 	vu1::Loc = vu1::Buffer=0;
//
// Note, Field will be 0 or 1, depending on which buffer we are drawing into

void RenderPrologue(void)
{
	#ifdef	__NOPT_ASSERT__
	// a nasty patch by Mick to allow us to see the extra passes in flat shaded mode
	if (gPassMask1)
	{
		if (Inp::gDebugMakes[0] & (1<<5))			  // check for Circle pressed
		{
			*p_patch_PRMODE ^= 1;
		}
	}
	else
	{
		*p_patch_PRMODE = 1;
	}
	#endif
	
	
	
	// record the time
	render::sTime = (int)Tmr::GetTime();
					 
	// set dma pointer to the start of the dma buffer
	dma::pLoc = dma::pList[render::Field];
	dma::pBase = dma::pLoc;

	PrologueGroup.profile_color = 0x008080;		// yellow = prologue group

	// prologue render begins here
	PrologueGroup.pRender[render::Field] = dma::pLoc;
	PrologueGroup.Used[render::Field] = true; 			// prologue always used

	// call the DMA routine to flip frame buffers and clear the draw buffer
	// currently the flip is accomplished by rendering a full screen sprite from the
	// draw buffer to the display buffer.  This converts it from 32-bit to 16-bit (dithered)
	// and means there is no actual buffer switching going on
	// the draw buffer is always at the same address.
	// When the game is running at 60fps, this copying takes place during the vblank.
	
	//static bool last_flip_copy = true;
	if (DoFlipCopy /*&& last_flip_copy*/)	  // DoFlipCopy is set/cleared by the loading screen code (p_NxLoadScreen.cpp)
	{
		if (ActualDoLetterbox)
			dma::Gosub(FLIP_N_CLEAR_LETTERBOX,2);
		else
			dma::Gosub(FLIP_N_CLEAR,2);
		ActualDoLetterbox=DoLetterbox;
	}
	else
	{
		dma::Gosub(CLEAR_ZBUFFER,2);
	}
	//last_flip_copy = DoFlipCopy;

	// set the GS to the default rendering state
	dma::Gosub(SET_RENDERSTATE,2);
	dma::Gosub(SET_FOGCOL,2);

	// upload VU1 microcode	(the code in microcode.dsm)
	// This is done every frame, at the start of the frame
	// it actually only needs doing once, but it does not really take
	// any time, and doing it every frame gives us the flexibility to uploaded new
	// microcode at a later stage in rendering.
	// for now, all the microcode fits in the 16K code area of VU1
	dma::Tag(dma::ref, ((uint8 *)&MPGEnd-(uint8 *)&MPGStart+15)/16, (uint)&MPGStart);
	vif::NOP();
	vif::NOP();
	
	// VIF1 and VU1 setup
	dma::BeginTag(dma::end, 0);
	vif::FLUSH();
	vif::STMASK(0);
	vif::STMOD(0);
	vif::STCYCL(1,1);
	vif::BASE(0);
	vif::OFFSET(0);
	vif::MSCAL(VU1_ADDR(Setup));
	dma::EndTag();
	
	// prepare all groups for rendering
	for (sGroup *p_group=sGroup::pHead; p_group; p_group=p_group->pNext)
	{
		// initialise the dma list
		dma::BeginList(p_group);

		// assume textures aren't used until something actually gets rendered
		p_group->Used[render::Field] = false;
	}
	dma::sp_group = NULL;


	// while the dma context is null, set up an empty upload for the particles group and fog group
	sGroup::pParticles->pUpload[render::Field] = dma::pLoc;
	sGroup::pFog->pUpload[render::Field] = dma::pLoc;
	dma::Tag(dma::end, 0, 0);
	vif::NOP();
	vif::NOP();
	
	// signal that the particle group is used
	sGroup::pParticles->Used[render::Field] = true;

	// reset current marker number
	render::sMarkerIndex = 0;

	// count particles
	render::sTotalNewParticles = 0;
}



void RenderWorld(Mth::Matrix* camera_orient, Mth::Vector* camera_pos, float view_angle, float screen_aspect)
{
	Mth::Matrix transform(*camera_orient);
	transform[3] = *camera_pos;

	// need the transform to be well-formed as a 4x4 matrix
	transform[0][3] = 0.0f;
	transform[1][3] = 0.0f;
	transform[2][3] = 0.0f;
	transform[3][3] = 1.0f;

	Mth::Rect viewport1(0.0f, 0.0f, 1.0f, 1.0f);
	RenderViewport(0, transform, viewport1, view_angle, screen_aspect, -1.0f, -100000.0f);
}

void RenderInitImmediateMode()
{
	dma::SetList(sGroup::pEpilogue);

	// epilogue render (a terrible hack to put this here!)
	//sGroup::pEpilogue->pRender[render::Field] = dma::pLoc;
	sGroup::pEpilogue->Used[render::Field] = true;			// epilogue always rendered

	// VIF1 and VU1 setup
	dma::BeginTag(dma::cnt, 0);
	vif::FLUSH();
	vif::STROW((int)render::RowRegI[0], (int)render::RowRegI[1], (int)render::RowRegI[2], (int)render::RowRegI[3]);
	vif::STMASK(0);
	vif::STMOD(0);
	vif::STCYCL(1,1);
	vif::BASE(0);
	vif::OFFSET(0);
	vif::MSCAL(VU1_ADDR(Setup));
	vu1::Loc = vu1::Buffer=0;
	//vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));
	//vu1::StoreVec(*(Vec *)&render::ViewportScale);				// VF10
	//vu1::StoreVec(*(Vec *)&render::ViewportOffset);				// VF11
	//vu1::StoreMat(*(Mat *)&render::AdjustedWorldToFrustum);		// VF12-15
	//vu1::StoreMat(*(Mat *)&render::AdjustedWorldToViewport);	// VF16-19
	//vu1::EndPrim(0);
	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::XYOFFSET_1, PackXYOFFSET(XOFFSET, YOFFSET));
	gs::Reg1(gs::SCISSOR_1,	 PackSCISSOR(0,HRES - 1,0,VRES - 1));
	gs::Reg1(gs::TEST_1, PackTEST(0,0,0,0,0,0,1,ZGEQUAL));
	gs::Reg1(gs::CLAMP_1,PackCLAMP(CLAMP,CLAMP,0,0,0,0));
	gs::Reg1(gs::PRMODECONT,	PackPRMODECONT(1));
	gs::EndPrim(1);
	vif::MSCAL(VU1_ADDR(Parser));
	dma::EndTag();
}

void RenderSwitchImmediateMode()
{
	dma::SetList(sGroup::pEpilogue);
}


// camera_transform must be a well-formed 4x4 matrix
void RenderViewport(uint viewport_num, const Mth::Matrix& camera_transform, const Mth::Rect& viewport_rec, float view_angle,
					float viewport_aspect, float near, float far, bool just_setup)
{
	// set up a load of persistent data for current viewport
	render::SetupVars(viewport_num, camera_transform, viewport_rec, view_angle, viewport_aspect, near, far);

	// Perform the DMA seperately, as the actual VARS might be re-set by update_render_vars called from plat_transform_to_screen
	render::SetupVarsDMA(viewport_num, near, far);

		// old and new style renders
		#	ifdef __USE_PROFILER__
			Sys::CPUProfiler->PushContext( 255, 0, 0 );	 	// Red (Under Yellow) = OldStyle Rendering	(Skins and stuff)
		#	endif // __USE_PROFILER__
			render::OldStyle();
		#	ifdef __USE_PROFILER__
			Sys::CPUProfiler->PopContext( );
		#	endif // __USE_PROFILER__
		
		
		#	ifdef __USE_PROFILER__
			Sys::CPUProfiler->PushContext( 0, 255, 0 );		// Green (Under Yellow) = NewStyle rendering (Environment)
		#	endif // __USE_PROFILER__
			
			render::NewStyle(just_setup);
		#	ifdef __USE_PROFILER__
			Sys::CPUProfiler->PopContext(  );
		#	endif // __USE_PROFILER__
}

// Uncomment the next line to use the color-per-mesh feature
#define USE_COLOR_PER_MESH

void render::OldStyle()
{
	// Don't render anything if it's not going to be displayed	
	if (!FlipCopyEnabled())
	{
		return;
	}
	
	float x,y,z,R;
	sMesh *pMesh;
	sGroup *p_group;
	int j;
	uint32 vu1_flags;
	uint32 colour;
	float colourComponent;

	Mth::Matrix LocalToWorld;
	Mth::Matrix LocalToCamera;
	Mth::Matrix LocalToFrustum;
	Mth::Matrix LocalToViewport;
	Mth::Matrix RootToWorld;
	Mth::Matrix RootToCamera;
	Mth::Matrix RootToFrustum;
	Mth::Matrix RootToViewport;
	Mth::Matrix LightMatrix;
	Mth::Matrix ColourMatrix;

	Mth::Vector ambientColour;
	Mth::Vector diffuseColour0;
	Mth::Vector diffuseColour1;
	Mth::Vector diffuseColour2;
	Mth::Vector colouredAmbientColour;
	Mth::Vector colouredDiffuseColour0;
	Mth::Vector colouredDiffuseColour1;
	Mth::Vector colouredDiffuseColour2;
	Mth::Vector v;

// Mick: Initialize these local matricies to known values....
	LocalToWorld.Zero();
	LocalToCamera.Zero();
	LocalToFrustum.Zero();
	LocalToViewport.Zero();
	RootToWorld.Zero();
	RootToCamera.Zero();
	RootToFrustum.Zero();
	RootToViewport.Zero();
	LightMatrix.Zero();
// And the vectors...	
	ambientColour.Set(0.0f,0.0f,0.0f,1.0f);
	diffuseColour0.Set(0.0f,0.0f,0.0f,1.0f);
	diffuseColour1.Set(0.0f,0.0f,0.0f,1.0f);
	diffuseColour2.Set(0.0f,0.0f,0.0f,1.0f);
	colouredAmbientColour.Set(0.0f,0.0f,0.0f,1.0f);
	colouredDiffuseColour0.Set(0.0f,0.0f,0.0f,1.0f);
	colouredDiffuseColour1.Set(0.0f,0.0f,0.0f,1.0f);
	colouredDiffuseColour2.Set(0.0f,0.0f,0.0f,1.0f);
	v.Set(0.0f,0.0f,0.0f,1.0f);
	

	
	#if STENCIL_SHADOW
	// prepare stencil shadow dma list for generating the stencil buffer
	dma::SetList(sGroup::pShadow);

	// send the GS setup
	dma::BeginTag(dma::cnt, 0);
	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::XYOFFSET_2,	PackXYOFFSET(XOFFSET, YOFFSET));
	gs::Reg1(gs::ZBUF_2,		PackZBUF(ZBUFFER_START,PSMZ24,1));
	gs::Reg1(gs::SCISSOR_2,		PackSCISSOR(0,HRES - 1,0,VRES - 1));
	gs::Reg1(gs::FRAME_2,		PackFRAME(0x1BA,HRES/64,PSMCT16S,0x00000000));
	//gs::Reg1(gs::FRAME_2,		PackFRAME(FRAME_START,HRES/64,PSMCT32,0x00000000));
	gs::Reg1(gs::ALPHA_2,		PackALPHA(0,2,2,1,128));
	gs::Reg1(gs::FBA_2,			PackFBA(0));
	gs::Reg1(gs::PABE,			PackPABE(0));
	gs::Reg1(gs::COLCLAMP,		PackCOLCLAMP(0));
	gs::Reg1(gs::PRMODECONT,	PackPRMODECONT(1));
	gs::Reg1(gs::DTHE,			PackDTHE(0));
	gs::Reg1(gs::TEST_2,		PackTEST(0,0,0,0,0,0,1,ZALWAYS));
	gs::Reg1(gs::PRIM,			PackPRIM(SPRITE,0,0,0,0,0,0,1,0));			// clear the stencil buffer
	gs::Reg1(gs::RGBAQ,			PackRGBAQ(0,0,0,0,0));
	gs::Reg1(gs::XYZ2,			PackXYZ(XOFFSET,YOFFSET,0));
	gs::Reg1(gs::XYZ2,			PackXYZ(0x10000-XOFFSET,0x10000-YOFFSET,0));
	gs::Reg1(gs::TEST_2,		PackTEST(0,0,0,0,0,0,1,ZGREATER));
	gs::Reg1(gs::FRAME_2,		PackFRAME(0x1BA,HRES/64,PSMCT16S,0xFF000000));
	gs::EndPrim(1);
	vif::MSCAL(VU1_ADDR(Parser));
	dma::EndTag();
	#else
	// prepare shadow dma list for generating the shadow texture
	dma::SetList(sGroup::pShadow);

	// send the GS setup
	dma::BeginTag(dma::cnt, 0);
	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::XYOFFSET_2,	PackXYOFFSET(0x7800,0x7800));
	gs::Reg1(gs::ZBUF_2,		PackZBUF(0,PSMZ16,1));
	gs::Reg1(gs::TEST_2,		PackTEST(0,0,0,0,0,0,1,ZALWAYS));
	gs::Reg1(gs::SCISSOR_2,		PackSCISSOR(0,255,0,255));
	gs::Reg1(gs::FRAME_2,		PackFRAME(0x1F0,4,PSMCT16,0x00000000));
	//gs::Reg1(gs::FRAME_2,		PackFRAME(0x000,HRES/64,PSMCT32,0x00000000));
	gs::Reg1(gs::FBA_2,			PackFBA(0));
	gs::Reg1(gs::PRIM,			PackPRIM(SPRITE,0,0,0,0,0,0,1,0));			// clear the texture
	gs::Reg1(gs::RGBAQ,			PackRGBAQ(0,0,0,0,0));
	gs::Reg1(gs::XYZ2,			PackXYZ(0x7800,0x7800,0));
	gs::Reg1(gs::XYZ2,			PackXYZ(0x8800,0x8800,0));
	gs::Reg1(gs::FBA_2,			PackFBA(1));								// set A on all pixels we touch
	gs::Reg1(gs::SCISSOR_2,		PackSCISSOR(1,254,1,254));					// don't touch edge of texture to avoid streaks
	gs::Reg1(gs::PRMODECONT,	PackPRMODECONT(0));
	gs::Reg1(gs::PRMODE,		PackPRMODE(0,0,0,0,0,0,1,0));
	gs::EndPrim(1);
	vif::MSCAL(VU1_ADDR(Parser));
	dma::EndTag();
	#endif


	// assume no dma context
	dma::SetList(NULL);

	// render old-style groups
	for (p_group=sGroup::pHead; p_group!=sGroup::pEpilogue; p_group=p_group->pNext)
	{
		// skip the old render mechanism if the group belongs to a pip-style scene
		if (p_group==sGroup::pShadow || !p_group->pScene || p_group->pScene->Flags & SCENEFLAG_USESPIP)
		{
			continue;
		}

		dma::SetList(p_group);

		dma::BeginTag(dma::cnt, 0);

			// VIF1 and VU1 setup
			vif::FLUSH();
			vif::STMASK(0);
			vif::STMOD(0);
			vif::STCYCL(1,1);
			vif::STCOL(0x3F800000,0,0,0);
			vif::BASE(0);
			vif::OFFSET(0);
			vif::MSCAL(VU1_ADDR(Setup));
			vu1::Loc = vu1::Buffer=0;

			// constant part of vu1 context data
			vu1::BeginPrim(ABS, VU1_ADDR(L_VF09));
//			vu1::StoreVec(*(Vec *)&render::AltFrustum);				// VF09
//			vu1::StoreVec(*(Vec *)&render::InverseViewportScale);	// VF10
//			vu1::StoreVec(*(Vec *)&render::InverseViewportOffset);	// VF11
			vu1::CopyQuads((uint32*)&render::AltFrustum, 
							(uint32*)&render::InverseViewportScale,
							(uint32*)&render::InverseViewportOffset
							);
			vu1::EndPrim(0);

			// send the GS viewport context
			gs::BeginPrim(ABS, 0, 0);
			gs::Reg1(gs::XYOFFSET_1, render::reg_XYOFFSET);
			gs::Reg1(gs::SCISSOR_1,  render::reg_SCISSOR);
			gs::EndPrim(1);
			vif::MSCAL(VU1_ADDR(Parser));

		dma::EndTag();


		Mth::Matrix refl_temp;
		Mth::Matrix  	ReflVecs;
		refl_temp[0] = Mth::Vector(1.0f, 0.0f, 0.0f, 0.0f);
		refl_temp[1] = Mth::Vector(0.0f, 1.0f, 0.0f, 0.0f);
		refl_temp[2] = Mth::Vector(0.0f, 0.0f, 1.4f, 0.0f);
		refl_temp[3] = Mth::Vector(0.0f, 0.0f, 0.0f, 0.0f);

		// this part handles the instanced non-skinned models
		if (p_group->pScene && (p_group->pScene->Flags & SCENEFLAG_INSTANCEABLE) &&
			p_group->pMeshes && !(p_group->pMeshes->Flags & MESHFLAG_SKINNED))
		{
			for (CInstance *pInstance=p_group->pScene->pInstances; pInstance; pInstance=pInstance->GetNext())
			{
				// make sure the instance is active
				if (!pInstance->IsActive())
				{
					continue;
				}

				LocalToWorld = *(Mth::Matrix *)pInstance->GetTransform();
				LocalToCamera = LocalToWorld * render::WorldToCamera;
				LocalToFrustum = LocalToWorld * render::WorldToFrustum;
				LocalToViewport = LocalToFrustum * render::FrustumToViewport;

				// set up reflection map transform
				sceVu0MulMatrix((sceVu0FVECTOR*)&ReflVecs,
								(sceVu0FVECTOR*)&refl_temp,
								(sceVu0FVECTOR*)&LocalToCamera);

				// send VIF/VU context
				dma::BeginTag(dma::cnt, 0);
				vif::STROW(0,0,0,0);
				vu1::BeginPrim(ABS, VU1_ADDR(L_VF12));
				vu1::StoreMat(*(Mat *)&LocalToViewport);	// VF12-15
				vu1::StoreMat(*(Mat *)&ReflVecs);			// VF16-19
				vu1::EndPrim(0);

				//gs::BeginPrim(ABS,0,0);
				//gs::Reg1(gs::FOGCOL, PackFOGCOL(0x60,0x40,0xC0));
				//gs::EndPrim(0);

				dma::EndTag();

				// traverse array of meshes in this group
				for (j=0,pMesh=p_group->pMeshes; jNumMeshes; j++,pMesh++)
				{
					// skip if invisible
					if (!pMesh->IsActive())
					{
						continue;
					}

					// do nothing if mesh doesn't have a dma subroutine
					if (!pMesh->pSubroutine)
					{
						continue;
					}

					v = pMesh->Sphere;
					v[3] = 1.0f;
					v *= LocalToCamera;
					x = v[0];
					y = v[1];
					z = v[2];
					R = pMesh->Sphere[3];

					// if all outside view volume then cull
					if (RpSubroutine, PROJ);
					}

					// otherwise cull at the vertex level
					else
					{
						dma::Gosub3D(pMesh->pSubroutine, CLIP);
					}
					
					p_group->Used[render::Field] = true;	  	// Note, should have culling before here
					

				}

			}

		}


		// this part handles the instanced skinned models
		if (p_group->pScene && (p_group->pScene->Flags & SCENEFLAG_INSTANCEABLE) &&
			p_group->pMeshes && (p_group->pMeshes->Flags & MESHFLAG_SKINNED))
		{
			for (CInstance *pInstance=p_group->pScene->pInstances; pInstance; pInstance=pInstance->GetNext())
			{
				// make sure the instance is active
				if (!pInstance->IsActive())
				{
					continue;
				}

				// shadow
				if (pInstance->CastsShadow())
				{
					#if STENCIL_SHADOW
					Mth::Vector shadow_vec(20.0f, -100.0f, 0.0f, 0.0f);
					Mth::Vector tweak_vec(0.4f, -2.0f, 0.0f, 0.0f);
					CastStencilShadow(pInstance, p_group, shadow_vec, tweak_vec);
					#else
					CastShadow(pInstance, p_group);
					#endif
				}

				// mat for bounding sphere test
				RootToWorld = *(Mth::Matrix *)pInstance->GetTransform();
				RootToCamera = RootToWorld * render::WorldToCamera;

				// get bounding sphere
				v = pInstance->GetBoundingSphere();
				R = v[3];
				v[3] = 1.0f;
				v *= RootToCamera;
				x = v[0];
				y = v[1];
				z = v[2];

				// if all outside view volume then cull
				if (RIsWireframe())
				{
					vu1_flags |= WIRE;
				}

				p_group->Used[render::Field] = true;	  	// Note, should have culling before here					

				dma::BeginTag(dma::cnt, 0);


				// adjust matrix for 0:8 fixed point weights format (used to be 1:15)
				RootToCamera[0] *= 128.0f;
				RootToCamera[1] *= 128.0f;
				RootToCamera[2] *= 128.0f;

				// send vu1 context
				RootToFrustum = RootToCamera * render::CameraToFrustum;
				RootToViewport = RootToFrustum * render::FrustumToIntViewport;

				CLightGroup *pLightGroup = pInstance->GetLightGroup();

				if (pLightGroup)
				{
					const Mth::Vector &light_dir0 = pLightGroup->GetDirection(0);
					const Mth::Vector &light_dir1 = pLightGroup->GetDirection(1);
					const Mth::Vector &light_dir2 = pLightGroup->GetDirection(2);

					LightMatrix[0][0] = light_dir0[0];
					LightMatrix[1][0] = light_dir0[1];
					LightMatrix[2][0] = light_dir0[2];
					LightMatrix[0][1] = light_dir1[0];
					LightMatrix[1][1] = light_dir1[1];
					LightMatrix[2][1] = light_dir1[2];
					LightMatrix[0][2] = light_dir2[0];
					LightMatrix[1][2] = light_dir2[1];
					LightMatrix[2][2] = light_dir2[2];
				} else {
					const Mth::Vector &light_dir0 = CLightGroup::sGetDefaultDirection(0);
					const Mth::Vector &light_dir1 = CLightGroup::sGetDefaultDirection(1);
					const Mth::Vector &light_dir2 = CLightGroup::sGetDefaultDirection(2);

					LightMatrix[0][0] = light_dir0[0];
					LightMatrix[1][0] = light_dir0[1];
					LightMatrix[2][0] = light_dir0[2];
					LightMatrix[0][1] = light_dir1[0];
					LightMatrix[1][1] = light_dir1[1];
					LightMatrix[2][1] = light_dir1[2];
					LightMatrix[0][2] = light_dir2[0];
					LightMatrix[1][2] = light_dir2[1];
					LightMatrix[2][2] = light_dir2[2];
				}

				Mth::Matrix temp = *(Mth::Matrix *)pInstance->GetTransform();
				temp[0].Normalize();							// do something sensible with a scaled matrix
				temp[1].Normalize();
				temp[2].Normalize();
				LightMatrix = temp * LightMatrix;

				if (pLightGroup)
				{
					ambientColour  = pLightGroup->GetAmbientColor();
					diffuseColour0 = pLightGroup->GetDiffuseColor(0);
					diffuseColour1 = pLightGroup->GetDiffuseColor(1);
					diffuseColour2 = pLightGroup->GetDiffuseColor(2);
				}
				else
				{
					ambientColour  = CLightGroup::sGetDefaultAmbientColor();
					diffuseColour0 = CLightGroup::sGetDefaultDiffuseColor(0);
					diffuseColour1 = CLightGroup::sGetDefaultDiffuseColor(1);
					diffuseColour2 = CLightGroup::sGetDefaultDiffuseColor(2);
				}

				// apply instance colour

#ifdef USE_COLOR_PER_MESH
				if (pInstance->HasColorPerMaterial())
				{
					// Get first color for now
					colour = pInstance->GetMaterialColorByIndex(0);
				}
				else
				{
					colour = pInstance->GetColor();
				}
#else
				colour = pInstance->GetColor();
#endif
				colourComponent = (float)(colour & 0xFF) * 0.0078125f;
				colouredAmbientColour[0]  = ambientColour[0]  * colourComponent;
				colouredDiffuseColour0[0] = diffuseColour0[0] * colourComponent;
				colouredDiffuseColour1[0] = diffuseColour1[0] * colourComponent;
				colouredDiffuseColour2[0] = diffuseColour2[0] * colourComponent;
				colourComponent = (float)(colour>>8 & 0xFF) * 0.0078125f;
				colouredAmbientColour[1]  = ambientColour[1]  * colourComponent;
				colouredDiffuseColour0[1] = diffuseColour0[1] * colourComponent;
				colouredDiffuseColour1[1] = diffuseColour1[1] * colourComponent;
				colouredDiffuseColour2[1] = diffuseColour2[1] * colourComponent;
				colourComponent = (float)(colour>>16 & 0xFF) * 0.0078125f;
				colouredAmbientColour[2]  = ambientColour[2]  * colourComponent;
				colouredDiffuseColour0[2] = diffuseColour0[2] * colourComponent;
				colouredDiffuseColour1[2] = diffuseColour1[2] * colourComponent;
				colouredDiffuseColour2[2] = diffuseColour2[2] * colourComponent;

				LightMatrix[3]  = colouredAmbientColour  * 1.0f/(128.0f*8388608.0f);
				ColourMatrix[0] = colouredDiffuseColour0 * 1.0f/(128.0f*8388608.0f);
				ColourMatrix[1] = colouredDiffuseColour1 * 1.0f/(128.0f*8388608.0f);
				ColourMatrix[2] = colouredDiffuseColour2 * 1.0f/(128.0f*8388608.0f);

				// vu context data
				vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));
				
//				vu1::StoreVec(*(Vec *)&render::InverseIntViewportScale);	// VF10
//				vu1::StoreVec(*(Vec *)&render::InverseIntViewportOffset);	// VF11
				vu1::CopyQuads((uint32*)&render::InverseIntViewportScale,
							   (uint32*)&render::InverseIntViewportOffset);
					
				vu1::StoreMat(*(Mat *)&RootToFrustum);			// VF12-15
				vu1::StoreMat(*(Mat *)&RootToViewport);			// VF16-19
				vu1::StoreMat(*(Mat *)&LightMatrix);			// VF20-23
				vu1::StoreMat(*(Mat *)&ColourMatrix);			// VF24-27
				vu1::EndPrim(0);

				// gs context data, specifically the fog coefficient
				float w,f;
				w = -RootToCamera[3][2];
				if ((w > FogNear) && EnableFog)	// Garrett: We have to check for EnableFog here because the VU1 code isn't
				{
					f = 1.0 + EffectiveFogAlpha * (FogNear/w - 1.0f);
				}
				else
				{
					f = 1.0f;
				}
				gs::BeginPrim(ABS,0,0);
				gs::Reg1(gs::FOG, PackFOG((int)(f*255.99f)));
				//gs::Reg1(gs::FOGCOL, PackFOGCOL(0x60,0x40,0xC0));
				gs::EndPrim(1);

				vif::MSCAL(VU1_ADDR(Parser));

				// get free run of the VUMem
				vif::FLUSHA();

				dma::EndTag();

				// send transforms
				vu1::Loc = 0;
				dma::Tag(dma::ref, pInstance->GetNumBones()<<2, (uint)pInstance->GetBoneTransforms());
				vif::NOP();
				vif::UNPACK(0,V4_32,pInstance->GetNumBones()<<2,ABS,SIGNED,0);

				dma::BeginTag(dma::cnt, 0);

				vif::ITOP(pInstance->GetNumBones()<<2);
				vif::MSCAL(VU1_ADDR(ReformatXforms));

				// not using pre-translate
				// don't forget to switch off the offset mode on xyz unpack if using the row reg for something else
				vif::STROW(0,0,0,0);

				dma::EndTag();

				// traverse colour-unlocked meshes
				for (j=0,pMesh=p_group->pMeshes; jNumMeshes; j++,pMesh++)
				{
					// skip if invisible, no subroutine, or not affected by dynamic colouring
					if (!pMesh->IsActive() || !pMesh->pSubroutine || (pMesh->Flags & MESHFLAG_COLOR_LOCKED))
					{
						continue;
					}

#ifdef USE_COLOR_PER_MESH
					uint32 mesh_color;
					// Garrett: Thought I had the material color array in the right order, but the render order
					// is different.  Right now I'm just searching out each color, but I'll need to go back
					// and see if it is possible to put the color array in render order (i.e. is the render order
					// static).
					if (pInstance->HasColorPerMaterial() && (colour != (mesh_color = pInstance->GetMaterialColor(pMesh->MaterialName, pMesh->Pass))))
					{
						colour = mesh_color;
						colourComponent = (float)(colour & 0xFF) * 0.0078125f;
						colouredAmbientColour[0]  = ambientColour[0]  * colourComponent;
						colouredDiffuseColour0[0] = diffuseColour0[0] * colourComponent;
						colouredDiffuseColour1[0] = diffuseColour1[0] * colourComponent;
						colouredDiffuseColour2[0] = diffuseColour2[0] * colourComponent;
						colourComponent = (float)(colour>>8 & 0xFF) * 0.0078125f;
						colouredAmbientColour[1]  = ambientColour[1]  * colourComponent;
						colouredDiffuseColour0[1] = diffuseColour0[1] * colourComponent;
						colouredDiffuseColour1[1] = diffuseColour1[1] * colourComponent;
						colouredDiffuseColour2[1] = diffuseColour2[1] * colourComponent;
						colourComponent = (float)(colour>>16 & 0xFF) * 0.0078125f;
						colouredAmbientColour[2]  = ambientColour[2]  * colourComponent;
						colouredDiffuseColour0[2] = diffuseColour0[2] * colourComponent;
						colouredDiffuseColour1[2] = diffuseColour1[2] * colourComponent;
						colouredDiffuseColour2[2] = diffuseColour2[2] * colourComponent;

						LightMatrix[3]  = colouredAmbientColour  * 1.0f/(128.0f*8388608.0f);
						ColourMatrix[0] = colouredDiffuseColour0 * 1.0f/(128.0f*8388608.0f);
						ColourMatrix[1] = colouredDiffuseColour1 * 1.0f/(128.0f*8388608.0f);
						ColourMatrix[2] = colouredDiffuseColour2 * 1.0f/(128.0f*8388608.0f);

						// change lighting context
						dma::BeginTag(dma::cnt, 0);
						vif::BASE(256);
						vif::OFFSET(0);
						vu1::Loc = 256;
						vu1::BeginPrim(ABS, VU1_ADDR(L_VF20));
						vu1::StoreMat(*(Mat *)&LightMatrix);			// VF20-23
						vu1::StoreMat(*(Mat *)&ColourMatrix);			// VF24-27
						vu1::EndPrim(1);
						vif::MSCAL(VU1_ADDR(ParseInit));
						vif::FLUSH();
						dma::EndTag();
					}
#endif // USE_COLOR_PER_MESH

					// render the mesh
					vu1::Loc = vu1::Buffer = 256;
					dma::BeginTag(dma::call,(uint)pMesh->pSubroutine);
					vif::BASE(256);
					vif::OFFSET(0);
					vif::MSCAL(VU1_ADDR(Setup));
					vif::ITOP(vu1_flags);
					vif::FLUSH();
					dma::EndTag();
					vu1::Loc += ((uint16 *)pMesh->pSubroutine)[1];
				}


				// change lighting context
				dma::BeginTag(dma::cnt, 0);
				vif::BASE(256);
				vif::OFFSET(0);
				vu1::Loc = 256;
				LightMatrix[3]  = ambientColour  * 1.0f/(128.0f*8388608.0f);
				ColourMatrix[0] = diffuseColour0 * 1.0f/(128.0f*8388608.0f);
				ColourMatrix[1] = diffuseColour1 * 1.0f/(128.0f*8388608.0f);
				ColourMatrix[2] = diffuseColour2 * 1.0f/(128.0f*8388608.0f);
				vu1::BeginPrim(ABS, VU1_ADDR(L_VF20));
				vu1::StoreMat(*(Mat *)&LightMatrix);			// VF20-23
				vu1::StoreMat(*(Mat *)&ColourMatrix);			// VF24-27
				vu1::EndPrim(1);
				vif::MSCAL(VU1_ADDR(ParseInit));
				vif::FLUSH();
				dma::EndTag();

				// traverse colour-locked meshes
				for (j=0,pMesh=p_group->pMeshes; jNumMeshes; j++,pMesh++)
				{
					// skip if invisible, no subroutine, or affected by dynamic colouring
					if (!pMesh->IsActive() || !pMesh->pSubroutine || !(pMesh->Flags & MESHFLAG_COLOR_LOCKED))
					{
						continue;
					}

					// render the mesh
					vu1::Loc = vu1::Buffer = 256;
					dma::BeginTag(dma::call,(uint)pMesh->pSubroutine);
					vif::BASE(256);
					vif::OFFSET(0);
					vif::MSCAL(VU1_ADDR(Setup));
					vif::ITOP(vu1_flags);
					vif::FLUSH();
					dma::EndTag();
					vu1::Loc += ((uint16 *)pMesh->pSubroutine)[1];
				}


			}

		}

	}


	#if STENCIL_SHADOW
	// postprocess stencil buffer
	dma::SetList(sGroup::pShadow);

	// send the GS setup
	dma::BeginTag(dma::cnt, 0);
	gs::BeginPrim(ABS,0,0);

	gs::Reg1(gs::TEXFLUSH,	0);

	gs::Reg1(gs::COLCLAMP,	PackCOLCLAMP(1));
	gs::Reg1(gs::TEST_1,	PackTEST(0,0,0,0,0,0,1,ZALWAYS));
	gs::Reg1(gs::TEX0_1,	PackTEX0(0x3740,HRES/64,PSMCT16S,10,10,1,DECAL,0,0,0,0,0));
	gs::Reg1(gs::TEX1_1,	PackTEX1(1,0,0,0,0,0,0));
	gs::Reg1(gs::ALPHA_1,	PackALPHA(2,1,0,1,0));
	gs::Reg1(gs::TEXA,		PackTEXA(ShadowDensity, 1, ShadowDensity));
	gs::Reg1(gs::ZBUF_1,	PackZBUF(ZBUFFER_START,PSMZ24,1));

	gs::Reg1(gs::PRIM,		PackPRIM(SPRITE,0,1,0,1,0,1,0,0));
	gs::Reg1(gs::UV,		PackUV(0x0008,0x0008));
	gs::Reg1(gs::XYZ2,		PackXYZ(XOFFSET,YOFFSET,0));
	gs::Reg1(gs::UV,		PackUV((HRES<<4)+8, (VRES<<4)+8));
	gs::Reg1(gs::XYZ2,		PackXYZ(XOFFSET+(HRES<<4), YOFFSET+(VRES<<4), 0));//0x10000-XOFFSET,0x10000-YOFFSET,0));

	gs::Reg1(gs::TEXA,		PackTEXA(0x00,0,0x80));
	gs::Reg1(gs::ZBUF_1,	PackZBUF(ZBUFFER_START,PSMZ24,0));

	gs::EndPrim(1);
	vif::MSCAL(VU1_ADDR(Parser));
	dma::EndTag();
	#endif



	// assume no dma context
	dma::SetList(NULL);

}

void patch_texture_dma()
{
	Nx::CScene *p_nxscene =  Nx::CEngine::sGetMainScene();
	if (p_nxscene)
	{
		Nx::CPs2TexDict *p_tex_dict = (Nx::CPs2TexDict *)p_nxscene->GetTexDict();
		if (p_tex_dict)
		{
			sScene *p_scene = p_tex_dict->GetEngineTextureDictionary();
		
		
			sTexture *p_textures = p_scene->pTextures;
			int num_textures = p_scene->NumTextures;
		
			
		//	int total_referenced = 0;
		//	int num_unique = 0;
			int i;
			for (i=0;ipNext)
	{
		if ((p_group!=sGroup::pShadow) && p_group->pScene && (p_group->pScene->Flags & SCENEFLAG_USESPIP))
		{
			// send the GS viewport context
			dma::SetList(p_group);

			// mark our place if it's a (the?) sorted group
			// remember to assert there's no overflow
			if (p_group->flags & GROUPFLAG_SORT)
			{
				//printf("Marking location %08X, p_group==%08X, flags=%08X\n", (int)dma::pLoc, (int)p_group, p_group->flags);
				Dbg_MsgAssert(sMarkerIndex < MAX_SORTED_LIST_MARKERS, ("too many sorted list markers - tell Mike"));
				render::sSortedListMarker[render::sMarkerIndex++] = (int)dma::pLoc;
			}

			dma::BeginTag(dma::cnt, 0xFE000000);
			vif::BASE(vu1::Loc);
			vif::OFFSET(0);
			uint vu1_loc = vu1::Loc;
			vu1::Loc = 0;   							// must do this as a relative prim for a sortable list...

			// constant part of vu1 context data
			vu1::BeginPrim(REL, VU1_ADDR(L_VF09));
			vu1::StoreVec(*(Vec *)&render::AltFrustum);				// VF09
			if (p_group->flags & GROUPFLAG_SKY)
			{
				vu1::StoreVec(*(Vec *)&render::SkyInverseViewportScale);	// VF10
				vu1::StoreVec(*(Vec *)&render::SkyInverseViewportOffset);	// VF11
			}
			else
			{
				vu1::StoreVec(*(Vec *)&render::InverseViewportScale);	// VF10
				vu1::StoreVec(*(Vec *)&render::InverseViewportOffset);	// VF11
			}
			vu1::EndPrim(0);

			gs::BeginPrim(REL, 0, 0);
			gs::Reg1(gs::XYOFFSET_1, render::reg_XYOFFSET);
			gs::Reg1(gs::SCISSOR_1,  render::reg_SCISSOR);
			gs::EndPrim(1);
			vif::MSCAL(VU1_ADDR(Parser));
			dma::EndTag();
			((uint16 *)dma::pTag)[1] |= vu1::Loc & 0x3FF;	// must write some code for doing this automatically
			vu1::Loc += vu1_loc;

			if (p_group->flags & GROUPFLAG_SORT)
			{
				dma::Tag(dma::cnt,0,0);
				vif::NOP();
				vif::NOP();
			}

			// assume no vu1 context for each group
			p_group->pVu1Context = NULL;
		}

	}

	// reserve space from dma list for new trans context
	dma::SetList(NULL);
	//CVu1Context *p_ctxt = new(dma::pLoc) CVu1Context;
	CVu1Context *p_ctxt = (CVu1Context *)dma::pLoc;
	//dma::pLoc += sizeof(CVu1Context);
	dma::pLoc += (STD_CTXT_SIZE+7)*16;
	p_ctxt->Init();
	Mth::Matrix I;
	I.Ident();
	p_ctxt->StandardSetup(I);

	// shadow stuff
	sGroup::pShadow->Used[render::Field] = true;

	#if !STENCIL_SHADOW

	// while the dma context is null, set up an empty upload for the shadow group
	sGroup::pShadow->pUpload[render::Field] = dma::pLoc;
	dma::Tag(dma::end, 0, 0);
	vif::NOP();
	vif::NOP();
	
	// ...and also a vu1 context for the shadow group
	CVu1Context *p_shdw_ctxt = new(dma::pLoc) CVu1Context(*p_ctxt);
	dma::pLoc += sizeof(CVu1Context);
	p_shdw_ctxt->InitExtended();
	p_shdw_ctxt->SetShadowVecs(SkaterY);

	// add a z-push
	p_shdw_ctxt->AddZPush(16.0f);


	dma::SetList(sGroup::pShadow);

	// initial cnt tag, for sending row reg and z-sort key
	Mth::Vector *p_trans = p_shdw_ctxt->GetTranslation();
	dma::Tag(dma::cnt, 1, 0);
	vif::NOP();
	vif::STROW((int)(*p_trans)[0], (int)(*p_trans)[1], (int)(*p_trans)[2], 0);

	// send transform context
	dma::Tag(dma::ref, (EXT_CTXT_SIZE+2)|(EXT_CTXT_SIZE+1)<<16, p_shdw_ctxt->GetDma());
	vif::BASE(vu1::Loc);
	vif::OFFSET(0);
	vu1::Loc += EXT_CTXT_SIZE+1;
	sGroup::pShadow->pVu1Context = p_shdw_ctxt;

	// send the GS context for the shadow list
	dma::BeginTag(dma::cnt, 0);

	vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));
	vu1::StoreVec(*(Vec *)&render::InverseViewportScale);	// VF10
	vu1::StoreVec(*(Vec *)&render::InverseViewportOffset);	// VF11
	vu1::EndPrim(0);

	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::TEXFLUSH,		PackTEXFLUSH(0));
	gs::Reg1(gs::FBA_2,			PackFBA(0));
	gs::Reg1(gs::TEX0_2,		PackTEX0(0x3E00,4,PSMCT16,8,8,1,DECAL,0,0,0,0,0));
	gs::Reg1(gs::TEX1_2,		PackTEX1(1,0,NEAREST,NEAREST,0,0,0));
	gs::Reg1(gs::CLAMP_2,		PackCLAMP(CLAMP,CLAMP,0,0,0,0));
	gs::Reg1(gs::FRAME_2,		PackFRAME(FRAME_START,HRES/64,PSMCT32,0xFF000000));
	gs::Reg1(gs::ZBUF_2,		PackZBUF(ZBUFFER_START,PSMZ24,1));
	gs::Reg1(gs::XYOFFSET_2,	reg_XYOFFSET);
	gs::Reg1(gs::SCISSOR_2,		reg_SCISSOR);
	gs::Reg1(gs::ALPHA_2,		PackALPHA(2,1,2,1,ShadowDensity));
	gs::Reg1(gs::TEST_2,		PackTEST(1,AGEQUAL,0x80,KEEP,0,0,1,ZGEQUAL));
	gs::Reg1(gs::PRMODECONT,	PackPRMODECONT(0));
	gs::Reg1(gs::PRMODE,		PackPRMODE(0,1,0,1,0,0,1,0));
	gs::EndPrim(0);
	dma::EndTag();
	#endif


	geom_stats_total=0;
	geom_stats_inactive =0;
	geom_stats_sky=0;
	geom_stats_transformed=0;
	geom_stats_skeletal=0;
	geom_stats_camera_sphere=0;
	geom_stats_clipcull=0;
	geom_stats_culled=0;
	geom_stats_leaf_culled=0;
	geom_stats_clipcull=0;
	geom_stats_boxcheck=0;
	geom_stats_occludecheck=0;
	geom_stats_occluded=0;
	geom_stats_colored=0;
	geom_stats_leaf=0;
	geom_stats_wibbleUV=0;
	geom_stats_wibbleVC=0;
	geom_stats_sendcontext=0;
	geom_stats_sorted=0;
	geom_stats_shadow=0;

	// render the world
	if (!just_setup)
	{
		
		#ifdef __NOPT_ASSERT__
		const int span = 60;
		static int avg[span];
		static int slot = 0;
		Tmr::CPUCycles	start = Tmr::GetTimeInCPUCycles();
		#endif

		#if STENCIL_SHADOW
		CGeomNode::sWorld.RenderWorld(*p_ctxt, RENDERFLAG_CLIP);
		#else
		CGeomNode::sWorld.RenderWorld(*p_ctxt, RENDERFLAG_CLIP | RENDERFLAG_SHADOW);
		#endif
		
		
		#ifdef __NOPT_ASSERT__		
		Tmr::CPUCycles len = Tmr::GetTimeInCPUCycles() - start;
		int ticks = len;
		avg[slot++] = ticks;
		if (slot == span) slot = 0;
		int tot = 0;
		for (int a=0;aGetItem((uint32)p_texture );
//	if( p_details )
	{
		render::ShadowCameraPosition[0] = p_pos->GetX();
		render::ShadowCameraPosition[1] = p_pos->GetY();
		render::ShadowCameraPosition[2] = p_pos->GetZ();
	}
}
#endif







// Added by Mick
// quick determination of if something is visible or not
// uses the previously calculated s and c vectors
// and the WorldToCamera transform
// (note, no attempt is made to ensure this is the same camera that the object
// will eventually be rendered with)
bool	IsVisible(Mth::Vector ¢er, float radius)
{


	float x,y,z;
	Mth::Vector v(center);

	v[3] = 1.0f;		// needed?
	v *= render::WorldToCamera;
	x = v[0];
	y = v[1];
	z = v[2];

	// if all outside view volume then cull
	if (radiuspSubroutine);
	vif::BASE(vu1::Loc);
	vif::OFFSET(0);
	vif::ITOP(RenderFlags);
	dma::EndTag();
	vu1::Loc += ((uint16 *)(pMesh->pSubroutine))[1];
}


void RenderEpilogue(void)
{
	sGroup *p_group;

	// ---------------------------------------------
	// tag onto end of particle group
	dma::SetList(sGroup::pParticles);

	// restore vu1 code (and buffering scheme) at end of particles group
	dma::Tag(dma::ref, ((uint8 *)&MPGEnd-(uint8 *)&MPGStart+15)/16, (uint)&MPGStart);
	vif::NOP();
	vif::NOP();
	dma::BeginTag(dma::cnt, 0);
	vif::FLUSH();
	vif::STMASK(0);
	vif::STMOD(0);
	vif::STCYCL(1,1);
	vif::BASE(0);
	vif::OFFSET(0);
	vif::MSCAL(VU1_ADDR(Setup));
	vu1::Loc = vu1::Buffer = 0;
	dma::EndTag();

	dma::SetList(NULL);
	// ---------------------------------------------
	
	// add a GS SIGNAL primitive to each group
	for (p_group=sGroup::pHead; p_group; p_group=p_group->pNext)
	{
		// end the dma list
		dma::EndList(p_group);
	}

	dma::SetList(NULL);

	// epilogue VRAM upload (fonts and sprites)

	// Upload the fonts, so we can draw 2D text on screen	
	sGroup::pEpilogue->pUpload[render::Field] = dma::pLoc;
	uint32 *p = (uint32*)(dma::pLoc);
	for (SFont *pFont=pFontList; pFont; pFont=pFont->pNext)
	{
	   	// Mick: we build this upload list directly, to cut down on the number of writes
		*p++ = (dma::ref << 28) + ((pFont->VifSize+15)>>4); //		dma::Tag(dma::ref, (pFont->VifSize+15)>>4, (uint)pFont->pVifData);
		*p++ = (uint32) pFont->pVifData;
		*p++ = 0;    //		vif::NOP();
		*p++ = 0;    //		vif::NOP();
	}

	// Upload the "Single Textures", the 2D screen elements (sprites)
	for (SSingleTexture *pTexture=SSingleTexture::sp_stexture_list; pTexture; pTexture=pTexture->mp_next)
	{
		if (pTexture->IsInVRAM())
		{
			*p++ = (dma::call << 28) + (0); //	dma::Tag(dma::call, 0, (uint)pTexture->mp_VifData);
			*p++ = (uint32) pTexture->mp_VifData;
			*p++ = 0;    //		vif::NOP();
			*p++ = 0;    //		vif::NOP();
		}
	}
	dma::pLoc = (uint8*)p;

	
	// (?) writing to TEXFLUSH will stall the gs until all the fonts and sprites
	// have been uploaded.
	// since we are not double buffering anything now, we have to wait until
	// everything is in VRAM before we can start rendering any 2D
	// this is a potential GS hiccup, but removing it would be tricky
	// and possibly not worth the effort
	// Garrett: Now the 2D uses both buffers at once.
		
	dma::BeginTag(dma::end, 0);
	vif::NOP();
	vif::NOP();
	gif::Tag2(gs::A_D,1,PACKED,0,0,1,1);
	gs::Reg2(gs::TEXFLUSH,	0);
	dma::EndTag();


	// We need the scratchpad in SortGroup()
	bool got_scratch = CSystemResources::sRequestResource(CSystemResources::vSCRATCHPAD_MEMORY);
	Dbg_Assert(got_scratch);

	// sort the dma lists of transparent groups
	for (p_group=sGroup::pHead; p_group!=sGroup::pEpilogue; p_group=p_group->pNext)
	{
		if ((p_group->flags & GROUPFLAG_SORT) 	   // Mick, most groups are not SORT, so this test first is the fastest
			&& p_group!=sGroup::pShadow
			&& p_group->pScene 
			&& (p_group->pScene->Flags & SCENEFLAG_USESPIP)
			)
		{
			p_group->pRender[render::Field] = dma::SortGroup(p_group->pRender[render::Field]);
		}
	}

	if (got_scratch)
	{
		CSystemResources::sFreeResource(CSystemResources::vSCRATCHPAD_MEMORY);
	}

	// swap epilogue and particle texture upload pointers (a cheat!)
	uint8 *temp_ptr = sGroup::pParticles->pUpload[render::Field];
	sGroup::pParticles->pUpload[render::Field] = sGroup::pEpilogue->pUpload[render::Field];
	sGroup::pEpilogue->pUpload[render::Field] = temp_ptr;
	
	int size = (int)dma::pLoc - (int)dma::pBase;
	static int max = 0;
	static int max2 = 0;
	if (size > max) max = size;
	if (size > max2) max2 = size;
	static int tick = 0;
	tick++; 
	tick &= 0x1f;
	if (!tick)
	{
	  //  printf ("%6d, %6d, %6d (of %6d)\n",size,max2,max, dma::size);
		max2 -= 50000;
	}

	Dbg_MsgAssert(DMAOverflowOK || size < (dma::size*98/100), ("DMA Buffer Overflow used %d\nShow screenshot to Mick (see .bmp saved, above)",size)); 
	
	if (DMAOverflowOK)
	{
		DMAOverflowOK--;
	}
	
}


void SendDisplayList(void)
{

// Mick:  Mike added this just before he left, but did not check it in
//	sceGsSyncPath(0, 0);

	// patch up the DMA list
// Moved to StuffAfterGSFinished
//	patch_texture_dma();	
	
	// Writeback data from D-Cache to Memory
	FlushCache(WRITEBACK_DCACHE);

#	ifdef __USE_PROFILER__
// Initial render group does not cause an interrupt
// so don't push context
//			Sys::VUProfiler->PushContext( 80,80, 228 );	   									
#	endif // __USE_PROFILER__
	// kick prologue render
	*D1_QWC  = 0;						// must zero QWC because the first action will be to use current MADR & QWC
	*D1_TADR = (uint)PrologueGroup.pRender[!render::Field];	// address of 1st tag
	*D1_CHCR = 0x145;					// start transfer, tte=1, chain mode, from memory
	sceGsSyncPath(0, 0);

	#if USE_INTERRUPTS
	
	// use interrupts
	sGroup::pUploadGroup = sGroup::pHead;
	sGroup::pRenderGroup = sGroup::pUploadGroup;
	

//	SingleRender = 0;

	#if 0  // debugging code to force all gorups to be executed, even if empty
	sGroup * p_group = (sGroup*)sGroup::pUploadGroup; // Skip prologue and sky
	while (p_group->pNext)		// all except the last one
	{
		p_group->Used[!render::Field] = 1;
		p_group = p_group->pNext;
	}
	#endif


	// Some debugging code to only render/upload a single selectable group
	// so we can see what is taking all the time
	if (SingleRender)
	{
		sGroup * p_group = (sGroup*)sGroup::pUploadGroup; // Skip prologue and sky
		int g = 0;
		while (p_group->pNext)		// all except the last one
		{
			g++;
			if (g > 2 && g != SingleRender)
			{
					p_group->Used[!render::Field] = 0;			
			}
			p_group = p_group->pNext;
		}
	}
	
	UploadStalled  = 0;
	RenderStalled  = 1;
	FlushCache(WRITEBACK_DCACHE);

#	ifdef __USE_PROFILER__
	Sys::DMAProfiler->PushContext(  sGroup::pUploadGroup->profile_color );	   									
#	endif // __USE_PROFILER__


	// kick off first upload, should trigger all the rest by interrupt
	*D2_QWC = 0;						// must zero QWC because the first action will be to use current MADR & QWC
	*D2_TADR = (uint)sGroup::pUploadGroup->pUpload[!render::Field];	// address of 1st tag
	*D2_CHCR = 0x105;					// start transfer, tte=0, chain mode, from memory



	#else
	
	// iterate over groups, uploading textures then rendering meshes in each
	// last group will be epilogue group
	for (sGroup *p_group=sGroup::pHead; p_group!=sGroup::pEpilogue; p_group=p_group->pNext)
	{
		if (p_group->Used[!render::Field])
		{
			// kick dma to upload textures from this group
			*D2_QWC = 0;						// must zero QWC because the first action will be to use current MADR & QWC
			*D2_TADR = (uint)p_group->pUpload[!render::Field];	// address of 1st tag
			*D2_CHCR = 0x105;					// start transfer, tte=0, chain mode, from memory
			sceGsSyncPath(0, 0);

			// kick dma to render scene
			*D1_QWC = 0;						// must zero QWC because the first action will be to use current MADR & QWC
			*D1_TADR = (uint)p_group->pRender[!render::Field];	// address of 1st tag
			*D1_CHCR = 0x145;					// start transfer, tte=1, chain mode, from memory
			sceGsSyncPath(0, 0);
		}
	}

	// kick dma to upload textures from this group
	*D2_QWC = 0;						// must zero QWC because the first action will be to use current MADR & QWC
	*D2_TADR = (uint)sGroup::pEpilogue->pUpload[!render::Field];	// address of 1st tag
	*D2_CHCR = 0x105;					// start transfer, tte=0, chain mode, from memory
	sceGsSyncPath(0, 0);

	// kick dma to render scene
	*D1_QWC = 0;						// must zero QWC because the first action will be to use current MADR & QWC
	*D1_TADR = (uint)sGroup::pEpilogue->pRender[!render::Field];	// address of 1st tag
	*D1_CHCR = 0x145;					// start transfer, tte=1, chain mode, from memory
	sceGsSyncPath(0, 0);
	#endif
}


/////////////////////////////////////////////////////////////////////////////
//
static bool sUpdate2DDma = false;
static bool	sVRAMNeedsUpdating = false;


void	Clear2DVRAM()
{
	// Reset VRAM pointer
	FontVramBase = FontVramStart;

	// And set flag
	sUpdate2DDma = true;
}

void	VRAMNeedsUpdating()
{
	sVRAMNeedsUpdating = true;
}

void	Reallocate2DVRAM()
{
	if (!sVRAMNeedsUpdating)
	{
		return;
	}

	// Reset VRAM pointer
	Clear2DVRAM();

	for (SFont *pFont=pFontList; pFont; pFont=pFont->pNext)
	{
		pFont->ReallocateVRAM();
	}

	for (SSingleTexture *pTexture=SSingleTexture::sp_stexture_list; pTexture; pTexture=pTexture->mp_next)
	{
		pTexture->ReallocateVRAM();
	}

	// And set flags
	sUpdate2DDma = true;
	sVRAMNeedsUpdating = false;
}

/////////////////////////////////////////////////////////////////////////////
//

void	Update2DDMA()
{
	// Only go through the lists if it was requested
	if (!sUpdate2DDma)
		return;

	// Go through each item
	for (SFont *pFont=pFontList; pFont; pFont=pFont->pNext)
	{
		pFont->UpdateDMA();
	}

	for (SSingleTexture *pTexture=SSingleTexture::sp_stexture_list; pTexture; pTexture=pTexture->mp_next)
	{
		pTexture->UpdateDMA();
	}

	// Clear flag
	sUpdate2DDma = false;
}






#if STENCIL_SHADOW

void CastStencilShadow(CInstance *pInstance, sGroup *p_group, Mth::Vector &ShadowVec, Mth::Vector &TweakVec)
{
	sShadowVolumeHeader * p_shadow = pInstance->GetScene()->mp_shadow_volume_header;
	if (!p_shadow)
		return;

	Mth::Matrix RootToFrustum,RootToViewport, WorldToRoot;

	RootToFrustum = *(Mth::Matrix *)pInstance->GetTransform() * render::WorldToFrustum;
	RootToViewport = RootToFrustum * render::FrustumToIntViewport;

	WorldToRoot = *(Mth::Matrix *)pInstance->GetTransform();
	WorldToRoot.Transpose();
	ShadowVec *= WorldToRoot;
	TweakVec *= WorldToRoot;

	dma::SetList(sGroup::pShadow);

	dma::BeginTag(dma::cnt, 0);
	vu1::BeginPrim(ABS, VU1_ADDR(L_VF14));
	vu1::StoreVec(*(Vec *)&ShadowVec);			// VF14, shadow vec
	vu1::StoreVec(*(Vec *)&TweakVec);			// VF15, tweak vec
	vu1::StoreMat(*(Mat *)&RootToViewport);		// VF16-19
	vu1::EndPrim(1);
	vif::MSCAL(VU1_ADDR(Parser));
	vif::FLUSH();
	dma::EndTag();

	// send transforms
	vu1::Loc = 0;
	dma::Tag(dma::ref, pInstance->GetNumBones()<<2, (uint)pInstance->GetBoneTransforms());
	vif::STCYCL(1,1);
	vif::UNPACK(0,V4_32,pInstance->GetNumBones()<<2,ABS,SIGNED,0);

	// send data
	dma::BeginTag(dma::cnt, 0);
	vif::STCYCL(7,21);

	sShadowVertex * p_vertex = p_shadow->p_vertex;
	sShadowConnect * p_connect = p_shadow->p_connect;
	sShadowNeighbor * p_neighbor = p_shadow->p_neighbor;

	int vert, num_faces;
	for (uint32 i=0; inum_faces; i++)
	{
		if (i%36==0)
		{
			vu1::Loc = 256;
			vif::BASE(256);
			vif::OFFSET(0);
			vif::MSCAL(VU1_ADDR(Setup));
		}

		if (i%12==0)
		{
			num_faces = p_shadow->num_faces-i;
			if (num_faces>12)
				num_faces=12;


			vif::UNPACK(0, V4_32, 7*num_faces, ABS, UNSIGNED, 0);
		}

		if ((i%12==11) || (i==p_shadow->num_faces-1))
		{
			vif::StoreV4_32(0x3480800A, 0x21224000, 0x00000051, 0x8000);
		}
		else
		{
			vif::StoreV4_32(0x3480800A, 0x21224000, 0x00000051, 0x0000);
		}

		uint32 * p32 = (uint32 *)dma::pLoc;

		vert = p_connect[i].corner[0];
		*p32++ = *((uint32 *)(&p_vertex[vert].x));
		*p32++ = *((uint32 *)(&p_vertex[vert].y));
		*p32++ = *((uint32 *)(&p_vertex[vert].z));
		*p32++ = *((uint32 *)(&p_vertex[vert].idx));

		vert = p_connect[i].corner[1];
		*p32++ = *((uint32 *)(&p_vertex[vert].x));
		*p32++ = *((uint32 *)(&p_vertex[vert].y));
		*p32++ = *((uint32 *)(&p_vertex[vert].z));
		*p32++ = *((uint32 *)(&p_vertex[vert].idx));

		vert = p_connect[i].corner[2];
		*p32++ = *((uint32 *)(&p_vertex[vert].x));
		*p32++ = *((uint32 *)(&p_vertex[vert].y));
		*p32++ = *((uint32 *)(&p_vertex[vert].z));
		*p32++ = *((uint32 *)(&p_vertex[vert].idx));

		vert = p_neighbor[i].edge[0];
		*p32++ = *((uint32 *)(&p_vertex[vert].x));
		*p32++ = *((uint32 *)(&p_vertex[vert].y));
		*p32++ = *((uint32 *)(&p_vertex[vert].z));
		*p32++ = *((uint32 *)(&p_vertex[vert].idx));

		vert = p_neighbor[i].edge[1];
		*p32++ = *((uint32 *)(&p_vertex[vert].x));
		*p32++ = *((uint32 *)(&p_vertex[vert].y));
		*p32++ = *((uint32 *)(&p_vertex[vert].z));
		*p32++ = *((uint32 *)(&p_vertex[vert].idx));

		vert = p_neighbor[i].edge[2];
		*p32++ = *((uint32 *)(&p_vertex[vert].x));
		*p32++ = *((uint32 *)(&p_vertex[vert].y));
		*p32++ = *((uint32 *)(&p_vertex[vert].z));
		*p32++ = *((uint32 *)(&p_vertex[vert].idx));

		dma::pLoc = (uint8 *)p32;

		vu1::Loc += 21;

		if ((i%12 == 11) || (i==p_shadow->num_faces-1))
		{
			vif::MSCAL(VU1_ADDR(ShadowVolumeSkin));
		}
	}

	dma::EndTag();

	dma::SetList(p_group);
}

#else

void CastShadow(CInstance *pInstance, sGroup *p_group)
{
	int j;
	sMesh *pMesh;
	Mth::Matrix ProjMat, temp;

	// set up projection matrix
	ProjMat = *pInstance->GetTransform();
	SkaterY = ProjMat[3][1];
	ProjMat[3].Set(-36.0f/**SUB_INCH_PRECISION*/*ProjMat[1][0],
				   -36.0f/**SUB_INCH_PRECISION*/*ProjMat[1][1],
				   -36.0f/**SUB_INCH_PRECISION*/*ProjMat[1][2], 1.0f);
	temp[0].Set(32.0f/**RECIPROCAL_SUB_INCH_PRECISION*/, 0.0f, 0.0f, 0.0f);
	temp[1].Set(0.0f, 0.0f, 0.0f, 0.0f);
	temp[2].Set(0.0f, 32.0f/**RECIPROCAL_SUB_INCH_PRECISION*/, 0.0f, 0.0f);
	temp[3].Set(8421376.0f, 8421376.0f, 0.0f, 1.0f);
	ProjMat *= temp;

	dma::SetList(sGroup::pShadow);
	dma::BeginTag(dma::cnt, 0);
	vu1::BeginPrim(ABS, VU1_ADDR(L_VF12));
	vu1::StoreMat(*(Mat *)&ProjMat);		// VF16-19
	vu1::EndPrim(1);

	vif::MSCAL(VU1_ADDR(Parser));

	// not using pre-translate
	// don't forget to switch off the offset mode on xyz unpack if using the row reg for somthing else
	vif::STROW(0,0,0,0);

	// get free run of the VUMem
	vif::FLUSH();

	dma::EndTag();

	// send transforms
	vu1::Loc = 0;
	dma::Tag(dma::ref, pInstance->GetNumBones()<<2, (uint)pInstance->GetBoneTransforms());
	vif::NOP();
	vif::UNPACK(0,V4_32,pInstance->GetNumBones()<<2,ABS,SIGNED,0);

	dma::Tag(dma::cnt, 0, 0);
	vif::ITOP(pInstance->GetNumBones()<<2);
	vif::MSCAL(VU1_ADDR(ReformatXforms));

	// traverse array of meshes in the model
	for (j=0,pMesh=p_group->pMeshes; jNumMeshes; j++,pMesh++)
	{
		// skip if invisible
		if (!pMesh->IsActive())
		{
			continue;
		}

		// do nothing if mesh doesn't have a dma subroutine
		if (!pMesh->pSubroutine)
		{
			continue;
		}

		// render the mesh
		vu1::Loc = vu1::Buffer = 256;
		dma::BeginTag(dma::call,(uint)pMesh->pSubroutine);
		vif::BASE(256);
		vif::OFFSET(0);
		vif::MSCAL(VU1_ADDR(Setup));
		vif::ITOP(SHDW);
		vif::FLUSH();
		dma::EndTag();
		vu1::Loc += ((uint16 *)pMesh->pSubroutine)[1];
	}

	dma::Tag(dma::cnt, 0, 0);
	vif::FLUSH();
	vif::NOP();

	dma::SetList(p_group);
}

#endif




namespace render
{
	Mth::Matrix WorldToCamera;
	Mth::Matrix WorldToCameraRotation;		// shouldn't need this
	Mth::Matrix WorldToFrustum;
	Mth::Matrix WorldToViewport;
	Mth::Matrix WorldToIntViewport;
	Mth::Matrix CameraToWorld;
	Mth::Matrix CameraToWorldRotation;
	Mth::Matrix CameraToFrustum;
	Mth::Matrix CameraToViewport;
	Mth::Matrix FrustumToViewport;
	Mth::Matrix FrustumToIntViewport;
	Mth::Matrix AdjustedWorldToCamera;
	Mth::Matrix AdjustedWorldToFrustum;
	Mth::Matrix AdjustedWorldToViewport;
	Mth::Matrix AdjustedWorldToIntViewport;
	Mth::Matrix SkyWorldToCamera;
	Mth::Matrix SkyWorldToFrustum;
	Mth::Matrix SkyWorldToViewport;
	Mth::Matrix SkyFrustumToViewport;

	Mth::Vector ViewportScale;
	Mth::Vector ViewportOffset;
	Mth::Vector IntViewportScale;
	Mth::Vector IntViewportOffset;
	Mth::Vector InverseViewportScale;
	Mth::Vector InverseViewportOffset;
	Mth::Vector InverseIntViewportScale;
	Mth::Vector InverseIntViewportOffset;
	Mth::Vector SkyViewportScale;
	Mth::Vector SkyViewportOffset;
	Mth::Vector SkyInverseViewportScale;
	Mth::Vector SkyInverseViewportOffset;
	Mth::Vector RowReg;
	Mth::Vector RowRegI;
	Mth::Vector RowRegF;
	#if !STENCIL_SHADOW
	Mth::Vector ShadowCameraPosition;
	#endif
	Mth::Vector AltFrustum;
	Mth::Vector CameraPosition;

	uint64 reg_XYOFFSET;
	uint64 reg_SCISSOR;

	float sx,sy,cx,cy,tx,ty,Sx,Sy,Cx,Cy,Tx,Ty,Near,Far;
	//float sMultipassMaxDist = 1000000000.0f;
	float sMultipassMaxDist = 4000.0f;

	uint Frame, Field;
	uint ViewportMask;

	uint RenderVarFrame = 0xFFFFFFFF; 
	uint RenderVarViewport = 0xFFFFFFFF;

	bool FrustumFlagsPlus[3], FrustumFlagsMinus[3];

	int sSortedListMarker[MAX_SORTED_LIST_MARKERS];
	int sMarkerIndex;
	int sTime;
	int sTotalNewParticles;

	float FogNear=3000.0f, FogAlpha=0.80f;
	uint32 FogCol=0xFF4070;
	bool EnableFog=false;
	float EffectiveFogAlpha;

	uint32 *p_patch_FOGCOL;

	uint8 ShadowDensity = 48;
}



void render::SetupVars(uint viewport_num, const Mth::Matrix& camera_transform, const Mth::Rect& viewport_rec, float view_angle,
					   float viewport_aspect, float near, float far)
{

	// fog variables
	EffectiveFogAlpha = FogAlpha;
	if (FogAlpha < 0.003f)
	{
		EffectiveFogAlpha = 0.003f;
	}
	if (FogAlpha > 0.997f)
	{
		EffectiveFogAlpha = 0.997f;
	}
	if (!EnableFog)		// TT5158: ZPush is affected by FogAlpha, so having a very low number causes z-fighting.  Garrett
	{
		EffectiveFogAlpha = 0.75f;
	}
	*p_patch_FOGCOL = FogCol;


	// Check if we already set up the current variables
	if (VarsUpToDate(viewport_num))
	{
		return;
	}

	// camera transform
	CameraToWorld = camera_transform;

	Dbg_MsgAssert(CameraToWorld[0][3] == 0.0f, ("CameraToWorld[0][3] is %f, not 0.0", CameraToWorld[0][3]));
	Dbg_MsgAssert(CameraToWorld[1][3] == 0.0f, ("CameraToWorld[1][3] is %f, not 0.0", CameraToWorld[1][3]));
	Dbg_MsgAssert(CameraToWorld[2][3] == 0.0f, ("CameraToWorld[2][3] is %f, not 0.0", CameraToWorld[2][3]));
	Dbg_MsgAssert(CameraToWorld[3][3] == 1.0f, ("CameraToWorld[3][3] is %f, not 1.0", CameraToWorld[3][3]));

	// need the transform to be well-formed as a 4x4 matrix
	//CameraToWorld[0][3] = 0.0f;
	//CameraToWorld[1][3] = 0.0f;
	//CameraToWorld[2][3] = 0.0f;
	//CameraToWorld[3][3] = 1.0f;

	CameraToWorldRotation = CameraToWorld;
	CameraToWorldRotation[3].Set(0.0f, 0.0f, 0.0f, 1.0f);

	// world view transform
	WorldToCameraRotation.Transpose(CameraToWorldRotation);
	WorldToCamera = CameraToWorld;
	WorldToCamera.InvertUniform();

	// massage to get ROW register value
	RowReg = SUB_INCH_PRECISION * WorldToCamera[3] * CameraToWorldRotation;
	RowRegI[0] = (float)(int)RowReg[0];
	RowRegI[1] = (float)(int)RowReg[1];
	RowRegI[2] = (float)(int)RowReg[2];
	RowRegI[3] = 0.0f;
	RowRegF = RowReg - RowRegI;
	RowRegF[3] = 0.0f;

	// massage to get modified matrix
	AdjustedWorldToCamera = WorldToCamera;
	AdjustedWorldToCamera[3] = RowRegF * WorldToCameraRotation;
	AdjustedWorldToCamera[3] *= RECIPROCAL_SUB_INCH_PRECISION;
	AdjustedWorldToCamera[3][3] = 1.0f;

	// view frustum
	tx = tanf(view_angle*8.72664626e-03f);		// tan of half (angle in radians)
	ty = -(tx / viewport_aspect);
	sx	= 1.0f/sqrtf(1.0f+1.0f/(tx*tx));
	sy	= 1.0f/sqrtf(1.0f+1.0f/(ty*ty));
	cx	= 1.0f/sqrtf(1.0f+tx*tx);
	cy	= 1.0f/sqrtf(1.0f+ty*ty);
	Near = near;
	Far  = far;

	// outer frustum
	Tx = tx * (620.0f/(((float) HRES) * 0.5f * viewport_rec.GetWidth()));
	Ty = ty * (524.0f/(((float) VRES) * 0.5f * viewport_rec.GetHeight()));
	Sx	= 1.0f/sqrtf(1.0f+1.0f/(Tx*Tx));
	Sy	= 1.0f/sqrtf(1.0f+1.0f/(Ty*Ty));
	Cx	= 1.0f/sqrtf(1.0f+Tx*Tx);
	Cy	= 1.0f/sqrtf(1.0f+Ty*Ty);

	// frustum transform
	CameraToFrustum.Zero();
	CameraToFrustum[0][0] = 1.0f/Tx;
	CameraToFrustum[1][1] = 1.0f/Ty;
	CameraToFrustum[2][2] = (near+far) / (far-near);
	CameraToFrustum[3][2] = 2.0f * near * far / (near-far);
	CameraToFrustum[2][3] = -1.0f;
	AdjustedWorldToFrustum = AdjustedWorldToCamera * CameraToFrustum;
	WorldToFrustum = WorldToCamera * CameraToFrustum;

	// viewport (scale and offset from outer frustum)
	ViewportScale.Set( 620.0f, 524.0f, 0.999f*powf(2,22), EffectiveFogAlpha);
	ViewportOffset.Set(2048.0f, 2048.0f, 1.5f*powf(2,23), 0);
	IntViewportScale.Set(620.0f, 524.0f, 0.999f*powf(2,-95), powf(2,32));
	IntViewportOffset.Set(2048.0f+powf(2,19), 2048.0f+powf(2,19), 1.5f*powf(2,-94), powf(2,-32));

	// viewport transform
	FrustumToViewport.Zero();
	FrustumToViewport[0][0] = ViewportScale[0];
	FrustumToViewport[1][1] = ViewportScale[1];
	FrustumToViewport[2][2] = ViewportScale[2];
	FrustumToViewport[3][3] = ViewportScale[3];
	FrustumToViewport[3][0] = ViewportOffset[0];
	FrustumToViewport[3][1] = ViewportOffset[1];
	FrustumToViewport[3][2] = ViewportOffset[2];
	AdjustedWorldToViewport = AdjustedWorldToFrustum * FrustumToViewport;
	WorldToViewport = WorldToFrustum * FrustumToViewport;
	CameraToViewport = CameraToFrustum * FrustumToViewport;

	FrustumToIntViewport.Zero();
	FrustumToIntViewport[0][0] = IntViewportScale[0];
	FrustumToIntViewport[1][1] = IntViewportScale[1];
	FrustumToIntViewport[2][2] = IntViewportScale[2];
	FrustumToIntViewport[3][3] = IntViewportScale[3];
	FrustumToIntViewport[3][0] = IntViewportOffset[0];
	FrustumToIntViewport[3][1] = IntViewportOffset[1];
	FrustumToIntViewport[3][2] = IntViewportOffset[2];
	AdjustedWorldToIntViewport = AdjustedWorldToFrustum * FrustumToIntViewport;
	WorldToIntViewport = WorldToFrustum * FrustumToIntViewport;


	// sky transforms
	//SkyViewportScale.Set(1900.0f, 1900.0f, powf(2,-102), powf(2,32));		// convert these to constants
	//SkyViewportOffset.Set(2048.0f+powf(2,19), 2048.0f+powf(2,19), 1.5f*powf(2,-101), powf(2,-32));	// convert these to constants
	SkyViewportScale.Set( 620.0f, 524.0f, 0.01f, EffectiveFogAlpha);
	SkyViewportOffset.Set(2048.0f, 2048.0f, 1.0f, 0);

	SkyWorldToCamera = AdjustedWorldToCamera;
	SkyWorldToCamera[3][0] = 0;
	SkyWorldToCamera[3][1] = 0;
	SkyWorldToCamera[3][2] = 0;
	SkyWorldToFrustum = SkyWorldToCamera * CameraToFrustum;
	SkyFrustumToViewport = FrustumToViewport;
	SkyFrustumToViewport[2][2] = SkyViewportScale[2];
	SkyFrustumToViewport[3][2] = SkyViewportOffset[2];
	SkyWorldToViewport = SkyWorldToFrustum * SkyFrustumToViewport;

	// used in 3D clipping
	AltFrustum[0] = Near;
	AltFrustum[1] = Far;
	AltFrustum[2] = 620.0f/(((float) HRES) * 0.5f * viewport_rec.GetWidth());
	AltFrustum[3] = 524.0f/(((float) VRES) * 0.5f * viewport_rec.GetHeight());

	// invert the viewport scale and offset
	InverseViewportScale[0]  = 1.0f / ViewportScale[0];
	InverseViewportScale[1]  = 1.0f / ViewportScale[1];
	InverseViewportScale[2]  = 1.0f / ViewportScale[2];
	InverseViewportScale[3]  = 1.0f / ViewportScale[3];

	InverseViewportOffset[0] = -InverseViewportScale[0] * ViewportOffset[0] * InverseViewportScale[3];
	InverseViewportOffset[1] = -InverseViewportScale[1] * ViewportOffset[1] * InverseViewportScale[3];
	InverseViewportOffset[2] = -InverseViewportScale[2] * ViewportOffset[2] * InverseViewportScale[3];
	InverseViewportOffset[3] = -InverseViewportScale[3] * ViewportOffset[3] * InverseViewportScale[3];
	InverseViewportOffset[3] = FogNear;
	
	InverseIntViewportScale[0]  = 1.0f / IntViewportScale[0];
	InverseIntViewportScale[1]  = 1.0f / IntViewportScale[1];
	InverseIntViewportScale[2]  = 1.0f / IntViewportScale[2];
	InverseIntViewportScale[3]  = 1.0f / IntViewportScale[3];

	InverseIntViewportOffset[0] = -InverseIntViewportScale[0] * IntViewportOffset[0] * InverseIntViewportScale[3];
	InverseIntViewportOffset[1] = -InverseIntViewportScale[1] * IntViewportOffset[1] * InverseIntViewportScale[3];
	InverseIntViewportOffset[2] = -InverseIntViewportScale[2] * IntViewportOffset[2] * InverseIntViewportScale[3];
	InverseIntViewportOffset[3] = 0.0f;
	
	SkyInverseViewportScale[0]  = 1.0f / SkyViewportScale[0];
	SkyInverseViewportScale[1]  = 1.0f / SkyViewportScale[1];
	SkyInverseViewportScale[2]  = 1.0f / SkyViewportScale[2];
	SkyInverseViewportScale[3]  = 1.0f / SkyViewportScale[3];

	SkyInverseViewportOffset[0] = -SkyInverseViewportScale[0] * SkyViewportOffset[0] * SkyInverseViewportScale[3];
	SkyInverseViewportOffset[1] = -SkyInverseViewportScale[1] * SkyViewportOffset[1] * SkyInverseViewportScale[3];
	SkyInverseViewportOffset[2] = -SkyInverseViewportScale[2] * SkyViewportOffset[2] * SkyInverseViewportScale[3];
	SkyInverseViewportOffset[3] = 0.0f;
	


	// generate the GS register values we'll need for the viewport
	reg_XYOFFSET = PackXYOFFSET((int)(16.0f * (2048.0f - ((float) HRES) * (viewport_rec.GetOriginX() + 0.5f * viewport_rec.GetWidth()))),
								(int)(16.0f * (2048.0f - ((float) VRES) * (viewport_rec.GetOriginY() + 0.5f * viewport_rec.GetHeight()))));
	reg_SCISSOR  = PackSCISSOR( (int)(((float) HRES) * viewport_rec.GetOriginX()),
								(int)(((float) HRES) * (viewport_rec.GetOriginX() + viewport_rec.GetWidth())) - 1,
								(int)(((float) VRES) * viewport_rec.GetOriginY()),
								(int)(((float) VRES) * (viewport_rec.GetOriginY() + viewport_rec.GetHeight())) - 1);


	// inverse frustum transform
	//ViewFrustumToCamera.Zero();
	//ViewFrustumToCamera[0][0] = tx;
	//ViewFrustumToCamera[1][1] = ty;
	//ViewFrustumToCamera[2][3] = (near - far) / (2.0f * near * far);
	//ViewFrustumToCamera[3][2] = -1.0f;
	//ViewFrustumToCamera[3][3] = -(near + far) / (2.0f * near * far);

	// get world coords of the far frustum vertices
	Mth::Vector vFTL, vFTR, vFBL, vFBR;
	vFTL.Set( tx*far, ty*far, far, 1.0f);
	vFTL *= CameraToWorld;
	vFTR.Set(-tx*far, ty*far, far, 1.0f);
	vFTR *= CameraToWorld;
	vFBL.Set( tx*far,-ty*far, far, 1.0f);
	vFBL *= CameraToWorld;
	vFBR.Set(-tx*far,-ty*far, far, 1.0f);
	vFBR *= CameraToWorld;

	Mth::Vector pos(WorldToCamera[3]);
	for (int i=0; i<3; i++)
	{
		FrustumFlagsPlus[i]  = (vFTL[i]>=pos[i] && vFTR[i]>=pos[i] && vFBL[i]>=pos[i] && vFBR[i]>=pos[i]);
		FrustumFlagsMinus[i] = (vFTL[i]<=pos[i] && vFTR[i]<=pos[i] && vFBL[i]<=pos[i] && vFBR[i]<=pos[i]);
	}


    // Set ViewportMask.  Will only draw CGeomNodes that have a non-zero result with (ViewportMask & Visibility)
	ViewportMask = (1 << (VISIBILITY_FLAG_BIT + viewport_num));

	// Mark current viewport and frame
	RenderVarFrame = Frame;
	RenderVarViewport = viewport_num;

}

void	render::SetupVarsDMA(uint viewport_num, float near, float far)
{


	// initialisation for new bounding volume tests
	Mth::Matrix Normals;

	asm ("vmaxw vf31,vf00,vf00w": :);

	// view frustum	 R,  L,  B,  T
	Normals[0].Set(-cx, cx,  0,  0);
	Normals[1].Set(  0,  0, cy,-cy);
	Normals[2].Set(-sx,-sx,-sy,-sy);
	Normals[3].Set(  0,  0,  0,  0);

	Normals = WorldToCamera * Normals;

	asm __volatile__("

		lqc2		vf10,(%0)
		lqc2		vf11,(%1)
		lqc2		vf12,(%2)
		lqc2		vf16,(%3)
		vabs		vf13,vf10
		vabs		vf14,vf11
		vabs		vf15,vf12

	": : "r" (&Normals[0]), "r" (&Normals[1]), "r" (&Normals[2]), "r" (&Normals[3]));

	// both frusta    ?,   ?    N,   F,
	Normals[0].Set(   0,   0,   0,   0);
	Normals[1].Set(   0,   0,   0,   0);
	Normals[2].Set(   0,   0,  -1,   1);
	Normals[3].Set(   0,   0,near,-far);

	Normals = WorldToCamera * Normals;

	asm __volatile__("

		lqc2		vf17,(%0)
		lqc2		vf18,(%1)
		lqc2		vf19,(%2)
		lqc2		vf23,(%3)
		vabs		vf20,vf17
		vabs		vf21,vf18
		vabs		vf22,vf19

	": : "r" (&Normals[0]), "r" (&Normals[1]), "r" (&Normals[2]), "r" (&Normals[3]));


	// outer frustum R,  L,  B,  T
	Normals[0].Set(-Cx, Cx,  0,  0);
	Normals[1].Set(  0,  0, Cy,-Cy);
	Normals[2].Set(-Sx,-Sx,-Sy,-Sy);
	Normals[3].Set(  0,  0,  0,  0);

	Normals = WorldToCamera * Normals;

	asm __volatile__("

		lqc2		vf24,(%0)
		lqc2		vf25,(%1)
		lqc2		vf26,(%2)
		lqc2		vf30,(%3)
		vabs		vf27,vf24
		vabs		vf28,vf25
		vabs		vf29,vf26

	": : "r" (&Normals[0]), "r" (&Normals[1]), "r" (&Normals[2]), "r" (&Normals[3]));




	#if OLD_FOG
	// link in the dma list that does fogging postprocessing
	dma::SetList(sGroup::pFog);
	dma::Tag(dma::call, 0, (uint)&FogDma);
	vif::NOP();
	vif::NOP();
	sGroup::pFog->Used[render::Field] = true;

	dma::Gosub(SET_RENDERSTATE,2);
	dma::Gosub(SET_FOGCOL,2);
	#endif


	// upload new vu1 code and set up double-buffering
	dma::SetList(sGroup::pParticles);
	dma::Tag(dma::ref, ((uint8 *)&NewMPGEnd-(uint8 *)&NewMPGStart+15)/16, (uint)&NewMPGStart);
	vif::BASE(0);
	vif::OFFSET(32);
	
	// send vu1 data for particle setup
	dma::BeginTag(dma::cnt, 0);
	vif::FLUSH();
	vif::BeginDIRECT();
	gif::Tag2(gs::A_D,1,PACKED,0,0,1,3);
	gs::Reg2(gs::SCISSOR_1, render::reg_SCISSOR);
	gs::Reg2(gs::XYOFFSET_1, render::reg_XYOFFSET);
	gs::Reg2(gs::ZBUF_1, PackZBUF(ZBUFFER_START,PSMZ24,1));
	vif::EndDIRECT();
	vif::UNPACK(0, V4_32, 7, REL, SIGNED, 0);
	vu1::StoreMat(*(Mat *)&render::WorldToIntViewport);			// view transform
	vif::StoreV4_32F(320.0f, 320.0f, 0.0f, 0.0f);				// kvec
	vif::StoreV4_32(0x39005000, 0x39006000, 0, 0x3F800000);		// NTL cull test vec - w-component limits tan of apparent radius
	vif::StoreV4_32(0x3900B000, 0x3900A000, 0, 0);				// BR cull test vec
	vif::MSCAL(8);		// init sprites
	dma::EndTag();
	dma::SetList(NULL);
}



// Checks to see if the render variables are current
bool render::VarsUpToDate(uint viewport_num)
{
	return (RenderVarFrame == Frame) && (RenderVarViewport == viewport_num);
}

// Invalidates the render variables
void render::InvalidateVars()
{
	RenderVarFrame = 0xFFFFFFFF; 
	RenderVarViewport = 0xFFFFFFFF;
}

// a quick hack for Dave...
void DrawRectangle(int x0, int y0, int x1, int y1, uint32 rgba)
{
	dma::SetList(sGroup::pEpilogue);
	dma::BeginTag(dma::cnt, 0);
	vif::FLUSH();
	vif::BeginDIRECT();
	gif::BeginTag2(gs::A_D, 1, PACKED, SPRITE|ABE, 1);
	gs::Reg2(gs::ALPHA_1,	PackALPHA(0,1,0,1,0));
	gs::Reg2(gs::RGBAQ,		(uint64)rgba);
	gs::Reg2(gs::XYZ2,		PackXYZ(x0,y0,0xFFFFFF));
	gs::Reg2(gs::XYZ2,		PackXYZ(x1,y1,0xFFFFFF));
	gif::EndTag2(1);
	vif::EndDIRECT();
	dma::EndTag();
}



} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/render.h
================================================
#ifndef __RENDER_H
#define __RENDER_H


#include "mikemath.h"
#include 

namespace NxPs2
{


#define SUB_INCH_PRECISION 16.0f
#define RECIPROCAL_SUB_INCH_PRECISION 0.0625f


typedef struct
{
	float tx,ty;
	float n,f;
}
sFrustum;


void RenderPrologue(void);
void RenderInitImmediateMode();

void RenderWorld(Mth::Matrix* camera_orient, Mth::Vector* camera_pos, float view_angle, float screen_aspect);
void RenderEpilogue(void);

void RenderSwitchImmediateMode();

void RenderViewport(uint viewport_num, const Mth::Matrix& camera_transform, const Mth::Rect & viewport_rec, float view_angle,
					float viewport_aspect, float nearz, float farz, bool just_setup=false);

bool IsVisible(Mth::Vector ¢er, float radius);
void UpdateCamera(Mat *camera_orient, Vec *camera_pos);
void SendDisplayList(void);
void RenderMesh(struct sMesh *pMesh, uint RenderFlags);
void SkinTest(void);
void SetTextureProjectionCamera( Mth::Vector *p_pos, Mth::Vector *p_at );
void DrawRectangle(int x0, int y0, int x1, int y1, uint32 rgba);

void Clear2DVRAM();
void VRAMNeedsUpdating();
void Reallocate2DVRAM();
void Update2DDMA();

void EnableFlipCopy(bool flip);
bool FlipCopyEnabled();


extern uint  Frame, Field;
extern Mat SkaterTransform;
extern Mat BoneTransforms[64];
extern int NumBones;

extern int	SingleRender;
extern bool DoLetterbox;
extern int	DMAOverflowOK;




namespace render
{
	void SetupVars(uint viewport_num, const Mth::Matrix& camera_transform, const Mth::Rect& viewport_rec, float view_angle,
				   float viewport_aspect, float near_z, float far_z);
#ifndef __PLAT_WN32__
	void SetupVarsDMA(uint viewport_num, float near, float far);	
#endif		// __PLAT_WN32__
	bool VarsUpToDate(uint viewport_num);
	void InvalidateVars();

	void OldStyle();
	void NewStyle(bool just_setup = false);

	extern Mth::Matrix WorldToCamera;
	extern Mth::Matrix WorldToCameraRotation;
	extern Mth::Matrix WorldToFrustum;
	extern Mth::Matrix WorldToViewport;
	extern Mth::Matrix WorldToIntViewport;
	extern Mth::Matrix CameraToWorld;
	extern Mth::Matrix CameraToWorldRotation;
	extern Mth::Matrix CameraToFrustum;
	extern Mth::Matrix CameraToViewport;
	extern Mth::Matrix FrustumToViewport;
	extern Mth::Matrix FrustumToIntViewport;
	extern Mth::Matrix AdjustedWorldToCamera;
	extern Mth::Matrix AdjustedWorldToFrustum;
	extern Mth::Matrix AdjustedWorldToViewport;
	extern Mth::Matrix AdjustedWorldToIntViewport;
	extern Mth::Matrix SkyWorldToCamera;
	extern Mth::Matrix SkyWorldToFrustum;
	extern Mth::Matrix SkyWorldToViewport;
	extern Mth::Matrix SkyFrustumToViewport;

	extern Mth::Vector ViewportScale;
	extern Mth::Vector ViewportOffset;
	extern Mth::Vector IntViewportScale;
	extern Mth::Vector IntViewportOffset;
	extern Mth::Vector InverseViewportScale;
	extern Mth::Vector InverseViewportOffset;
	extern Mth::Vector InverseIntViewportScale;
	extern Mth::Vector InverseIntViewportOffset;
	extern Mth::Vector SkyViewportScale;
	extern Mth::Vector SkyViewportOffset;
	extern Mth::Vector SkyInverseViewportScale;
	extern Mth::Vector SkyInverseViewportOffset;
	extern Mth::Vector RowReg;
	extern Mth::Vector RowRegI;
	extern Mth::Vector RowRegF;
	extern Mth::Vector ShadowCameraPosition;
	extern Mth::Vector AltFrustum;
	extern Mth::Vector CameraPosition;

	extern uint64 reg_XYOFFSET;
	extern uint64 reg_SCISSOR;

	extern float sx,sy,cx,cy,tx,ty,Sx,Sy,Cx,Cy,Tx,Ty,Near,Far;
	extern float sMultipassMaxDist;

	extern uint Frame, Field;
	extern uint ViewportMask;

	extern bool FrustumFlagsPlus[3], FrustumFlagsMinus[3];

	#define MAX_SORTED_LIST_MARKERS 32
	extern int sSortedListMarker[MAX_SORTED_LIST_MARKERS];
	extern int sMarkerIndex;
	extern int sTime;
	extern int sTotalNewParticles;

	extern float FogNear, FogAlpha;
	extern uint32 FogCol;
	extern uint32 *p_patch_FOGCOL;
	extern bool EnableFog;
	extern float EffectiveFogAlpha;
	extern uint8 ShadowDensity;
}


} // namespace NxPs2

#endif // __RENDER_H


================================================
FILE: Code/Gfx/NGPS/NX/resource.cpp
================================================
#include 
#include 
#include "resource.h"

namespace NxPs2
{


bool			CSystemResources::s_resource_used[vNUM_RESOURCES];

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 			CSystemResources::sInitResources()
{
	for (int i = 0; i < vNUM_RESOURCES; i++)
	{
		s_resource_used[i] = false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool 			CSystemResources::sRequestResource(EResourceType resource)
{
	Dbg_Assert(resource < vNUM_RESOURCES);
	
	bool old_result;

	// We need to do this with interrupts off, just in case
	DI();
	old_result = s_resource_used[resource];
	s_resource_used[resource] = true;			// We know it is reserved now
	EI();

	return !old_result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 			CSystemResources::sFreeResource(EResourceType resource)
{
	Dbg_Assert(resource < vNUM_RESOURCES);
	Dbg_Assert(s_resource_used[resource]);

	// We need to do this with interrupts off, just in case
	DI();
	s_resource_used[resource] = false;
	EI();
}

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/resource.h
================================================
#ifndef __RESOURCE_H
#define __RESOURCE_H

#include	
#include	

namespace NxPs2
{

// This static class locks certain system resources (like scratchpad and VU0 memory)
// for code to use exclusively.  Right now, there is no mechanism to lock out just
// parts of memory, but it may be added in the future if needed.
class CSystemResources
{

public:
	// Resource types
	enum EResourceType {
		vVU0_MEMORY = 0,
		vSCRATCHPAD_MEMORY,
		vNUM_RESOURCES
	};

	//-------------------------------------------

	static void sInitResources();								// Call only at the beginning!

	static bool sRequestResource(EResourceType resource); 		// Returns true if it was able to lock it
	static void sFreeResource(EResourceType resource);

private:
	static bool s_resource_used[vNUM_RESOURCES];

};


} // namespace NxPs2



#endif // __RESOURCE_H



================================================
FILE: Code/Gfx/NGPS/NX/scene.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "texture.h"
#include "material.h"
#include "mesh.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gif.h"
#include "gs.h"
#include "scene.h"
#include "mikemath.h"
#include "vu1code.h"
#include "switches.h"
#include 
#include 

#include 

namespace NxPs2
{

bool load_scene(uint8* pData, int dataSize, sScene *pScene, bool IsSkin, bool IsInstanceable, uint32 texDictOffset, bool doShadowVolume)
{
	pScene->mp_shadow_volume_header = NULL;

	// make sure there's no dma list running
	sceGsSyncPath(0,0);

	// Load the scene file onto the dma buffer so we can use it as a memory mapped file
	void *pMemory = pData;
	void *pFile = pMemory;
	
	int	mesh_size = dataSize * 10/100 + 2000;		// adding the 2000 to cope with very small files						
										
	// version numbers
	uint32 vert_version;
	MEM_Read(&sMaterial::Version, sizeof(uint32), 1, pFile);
	MEM_Read(&sMesh::Version, sizeof(uint32), 1, pFile);
	MEM_Read(&vert_version, sizeof(uint32), 1, pFile);

	// allocate and import materials
	pFile = LoadMaterials(pFile, pScene, texDictOffset);

	// get number of mesh groups
	int NumGroups;
	MEM_Read(&NumGroups, sizeof(uint32), 1, pFile);
	if ( NumGroups != pScene->NumGroups )
	{
		Dbg_Message( "Scene mismatch in number of groups" );
		return false;
	}

	int total_num_meshes = 0;
	// get number of meshes and allocate storage for meshes
	if (sMesh::Version >= 4)
	{
		MEM_Read(&total_num_meshes, sizeof(uint32), 1, pFile);
		mesh_size = total_num_meshes * sizeof(sMesh);
	}
	
	if ( pScene->NumMeshes != 0 )
	{
		Dbg_Message( "pScene->NumMeshes is %d when loading scene, texDictOffset = %d", pScene->NumMeshes, texDictOffset );
		return false;
	}
										  
	pScene->pMeshes = (sMesh *)Mem::Malloc(mesh_size);

	if ( pScene->pMeshes == NULL )
	{
		Dbg_Message( "couldn't allocate memory for meshes" );
		return false;
	}

	// allocate prebuilt dma buffer
//	int	dma_size = Mem::Available()-128;

	int	dma_size = dataSize;  // Really should be around 80%, based on observations, but we know it's no bigger
	
	if (dma_size < 300000)
	{
		dma_size = dma_size *110/100;	 	// Patch for ironman problem
	}
			 
	pScene->pMeshDma = (uint8 *)Mem::Malloc(dma_size);
	if ( pScene->pMeshDma == NULL )
	{
		Dbg_Message( "couldn't allocate memory for mesh dma" );
		return false;
	}
	
	// set dma buffer
	dma::pLoc = pScene->pMeshDma;

	// allocate and import meshes
	sMesh::TotalNumVertices=0;
	pFile = LoadMeshes(pFile, pScene, IsSkin, IsInstanceable, texDictOffset);

//	printf ("DMA Mesh (used %d,  file size = %d, NumMeshes = %d, total_num_meshes = %d, available %d)\n",
//	(int)(dma::pLoc-pScene->pMeshDma), dataSize, pScene->NumMeshes, total_num_meshes, dma_size);
	
	Dbg_MsgAssert( (int)(dma::pLoc-pScene->pMeshDma) < dma_size,("DMA overflow all available memory (used %d, available %d)\n",(int)(dma::pLoc-pScene->pMeshDma),dma_size));
	
	
	if ( (int)(pScene->NumMeshes*sizeof(sMesh)) > mesh_size )
	{
		Dbg_Message( "Mesh overflow file = %s, file size = %d, guessed = %d, actually %d", dataSize, mesh_size, pScene->NumMeshes * sizeof(sMesh) );
		return false;
	}
										
	// reduce dma memory to fit
	Mem::Manager::sHandle().ReallocateShrink( dma::pLoc-pScene->pMeshDma, pScene->pMeshDma);
	
	// reduce mesh memory to fit
	if (sMesh::Version < 4)
	{
		Mem::Manager::sHandle().ReallocateShrink( pScene->NumMeshes*sizeof(sMesh), pScene->pMeshes);
	}
	
	// Hook up shadow volume data.
	pScene->mp_shadow_volume_header = (sShadowVolumeHeader *)pFile;

	#if STENCIL_SHADOW
	if ( pScene->mp_shadow_volume_header && pScene->mp_shadow_volume_header->byte_size )
	{
		int size = pScene->mp_shadow_volume_header->byte_size + sizeof ( sShadowVolumeHeader );
		char * p8 = new char[size];
		memcpy( p8, pScene->mp_shadow_volume_header, size );
		pScene->mp_shadow_volume_header = (sShadowVolumeHeader *)p8;
		pScene->mp_shadow_volume_header->p_vertex = (sShadowVertex *)&pScene->mp_shadow_volume_header[1];
		pScene->mp_shadow_volume_header->p_connect = (sShadowConnect *)&pScene->mp_shadow_volume_header->p_vertex[pScene->mp_shadow_volume_header->num_verts];
		pScene->mp_shadow_volume_header->p_neighbor = (sShadowNeighbor *)&pScene->mp_shadow_volume_header->p_connect[pScene->mp_shadow_volume_header->num_faces];
	}
	else
	{
		pScene->mp_shadow_volume_header = NULL;
	}
	#else
	pScene->mp_shadow_volume_header = NULL;
	#endif

	// deal with instances
	pScene->pInstances = NULL;
	if (IsInstanceable)
	{
		pScene->Flags |= SCENEFLAG_INSTANCEABLE;
	}

	// success
	return true;
}

// load scene from a file
sScene* LoadScene(const char* Filename, sScene *pScene, bool IsSkin, bool IsInstanceable, uint32 texDictOffset, bool doShadowVolume)
{
	int data_size;
	uint8* pData = (uint8*)File::LoadAlloc(Filename,&data_size,dma::pRuntimeBuffer,NON_DEBUG_DMA_BUFFER_SIZE);	
	
	// theoretically, the data pointer returned is
	// exactly the same as the dma::pRuntimeBuffer
	Dbg_MsgAssert( pData == dma::pRuntimeBuffer, ( "Unexpected data pointer was found" ) );

	bool success = load_scene(pData, data_size, pScene, IsSkin, IsInstanceable, texDictOffset, doShadowVolume);
	if ( !success )
	{
		Dbg_MsgAssert( 0, ( "Load scene from file %s failed", Filename ) );
	}

	return pScene;
}

// load scene from a data buffer, rather than opening a file
sScene* LoadScene(uint32* pData, int dataSize, sScene *pScene, bool IsSkin, bool IsInstanceable, uint32 texDictOffset, bool doShadowVolume)
{
	Dbg_MsgAssert( dataSize <= NON_DEBUG_DMA_BUFFER_SIZE, ( "Data to copy is too large to fit in buffer" ) ); 

	// copy over the data
	memcpy( dma::pRuntimeBuffer, pData, dataSize );
	
	bool success = load_scene((uint8*)pData, dataSize, pScene, IsSkin, IsInstanceable, texDictOffset, doShadowVolume);
	if ( !success )
	{
		Dbg_MsgAssert( 0, ( "Load scene from data buffer failed" ) );
	}

	return pScene;
}

void DeleteScene(sScene *pScene)
{
	// deallocate assets
	Mem::Free(pScene->pMeshDma);
	Mem::Free(pScene->pMaterials);
	Mem::Free(pScene->pMeshes);
	Mem::Free(pScene->mp_shadow_volume_header);
	// and what of groups?

	// don't unlink or free, because the textures still exist

	// GJ:  a little pre-emptive debugging, since scenes
	// and textures aren't necessarily destroyed at the same time
	pScene->pMeshDma = NULL;
	pScene->pMaterials = NULL;
	pScene->pMeshes = NULL;

	Dbg_MsgAssert(!pScene->pInstances, ("Trying to delete sScene that still has CInstances attached to it.  Must delete CInstances first."));
}



void sScene::SetUVOffset(uint32 material_name, int pass, float u_offset, float v_offset)
{
	#if 1
	sMesh *pMesh;
	Mth::Matrix mat;
	int i;

	mat.Identity();
	mat[3][0] = u_offset;
	mat[3][1] = v_offset;

	for (pMesh=this->pMeshes, i=0; iNumMeshes; pMesh++, i++)
	{
		if (pMesh->MaterialName==material_name && pMesh->Pass==pass)
		{
			dma::TransformSTs(pMesh->pSubroutine, mat);
		}
	}
	#else
	
	// just some test code for me to play around with...

	sMesh *pMesh;
	Mth::Matrix mat;
	int i;

	mat.Identity();

	//mat[0][0] = cosf(2.0f * 3.14159265f * u_offset);
	//mat[0][1] = sinf(2.0f * 3.14159265f * u_offset);
	//mat[1][0] = -mat[0][1];
	//mat[1][1] = mat[0][0];

	if (u_offset>0.0f)
		mat[0][0] = 1.01f;
	else if (u_offset<0.0f)
		mat[0][0] = 1.0f/1.01f;
	if (v_offset>0.0f)
		mat[1][1] = 1.01f;
	else if (v_offset<0.0f)
		mat[1][1] = 1.0f/1.01f;

	for (pMesh=this->pMeshes, i=0; iNumMeshes; pMesh++, i++)
	{
		if (pMesh->MaterialName==material_name && pMesh->Pass==pass)
		{
			dma::TransformSTs(pMesh->pSubroutine, mat);
		}
	}
	#endif
}




void sScene::SetUVMatrix(uint32 material_name, int pass, const Mth::Matrix &mat)
{
	sMesh *pMesh;
	int i;

	for (pMesh=this->pMeshes, i=0; iNumMeshes; pMesh++, i++)
	{
		if (pMesh->MaterialName==material_name && pMesh->Pass==pass)
		{
			dma::TransformSTs(pMesh->pSubroutine, mat);
		}
	}
}




sScene *sScene::pHead;
uint8  *sScene::pData;


} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/scene.h
================================================
#ifndef __SCENE_H
#define __SCENE_H


#ifndef __CORE_DEFINES_H
#include 
#endif

#include 

namespace NxPs2
{


#define SCENEFLAG_INSTANCEABLE (1<<0)
#define SCENEFLAG_USESPIP      (1<<1)


struct sShadowVertex
{
	float	x;
	float	y;
	float	z;
	int		idx;
};

struct sShadowConnect
{
	uint16	corner[3];
};

struct sShadowNeighbor
{
	sint16	edge[3];
};

struct sShadowVolumeHeader
{
	uint32				version;
	uint32				num_faces;
	uint32				num_verts;
	uint32				byte_size;
	sShadowVertex *		p_vertex;
	sShadowConnect *	p_connect;
	sShadowNeighbor *	p_neighbor;
	uint32				pad;
};

struct sScene
{
	Mth::Vector			Sphere;

	uint32				Flags;
	int					NumGroups;

	int					NumTextures;
	uint8				*pTexBuffer;
	uint8				*pTexDma;
	struct sTexture		*pTextures;

	int					NumMaterials;
	struct sMaterial	*pMaterials;

	int 				NumMeshes;
	uint8 				*pMeshDma;
	struct sMesh		*pMeshes;
	class  CInstance	*pInstances;

	sScene				*pNext;

	static sScene		*pHead;
	static uint8		*pData;

	sShadowVolumeHeader	*mp_shadow_volume_header;
public:
	void SetUVOffset(uint32 material_name, int pass, float u_offset, float v_offset);
	void SetUVMatrix(uint32 material_name, int pass, const Mth::Matrix &mat);
};

struct sCASData;

sScene *LoadScene(uint32 *pData, int dataSize, sScene *pScene, bool IsSkin, bool Instanced, uint32 texDictOffset, bool doShadowVolume);
sScene *LoadScene(const char *Filename, sScene *pScene, bool IsSkin, bool Instanced, uint32 texDictOffset, bool doShadowVolume);
void DeleteScene(sScene *pScene);


} // namespace NxPs2


#endif // __SCENE_H



================================================
FILE: Code/Gfx/NGPS/NX/sprite.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "sprite.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gif.h"
#include "gs.h"
#include "vu1code.h"
#include "texturemem.h"
#include "render.h"
#include "switches.h"
#include 

/*


	
.img file format
--------------------------
	
Offset		Size (Bytes)	Field
---------------------------------------------------------------
0x00			4			Version (currently 1)
0x04			4			Basename of file in CRC format
0x08			4			PS2 TW (log2 Width)
0x0c			4			PS2 TH (log2 Height)
0x10			4			PS2 PSM (Bit depth)
0x14			4			PS2 CPSM (CLUT bit depth)
0x18			4			PS2 MXL (Number of mipmaps)
0x1c			2			Original image width
0x1e			2			Original image height
0x20			Upto 1K		CLUT data
0x20+CLUT_size	W*H*depth		Texture mipmaps

	
	
	
font->vram upload format
------------------------


	GIFtag PACKED 4
	BITBLTBUF ?
	TRXPOS 0
	TRXREG w,h
	TRXDIR 0
	
	GIFtag IMAGE (w*h+15)/16
	... (tex data)
	
	GIFtag PACKED 4
	BITBLTBUF ?
	TRXPOS 0
	TRXREG w,h
	TRXDIR 0
	
	GIFtag IMAGE 64
	... (clut data)
	
	
	
	total size in bytes = 12*16+((w*h+15)/16)*16+1024 = 1216+((w*h+15)/16)*16
	
	this will be referenced as follows:
	
	dma ref
	NOP
	DIRECT 76+(w*h+15)/16
	
	
*/


namespace NxPs2
{

/******************************************************************/
/*                                                                */
/* SSingleTexture												  */
/*                                                                */
/******************************************************************/

SSingleTexture *	SSingleTexture::sp_stexture_list = NULL;

/////////////////////////////////////////////////////////////////////////////
//

SSingleTexture::SSingleTexture()
{
	Dbg_Error("Must supply a filename when creating a SSingleTexture");
}

/////////////////////////////////////////////////////////////////////////////
//
// .img.ps2 buffer

SSingleTexture::SSingleTexture(uint8* p_buffer, int buffer_size, bool sprite, bool alloc_vram, bool load_screen) :
	m_flags(0),
	m_usage_count(0)
{
	#ifdef			__NOPT_ASSERT__
	strcpy(m_name,"texture from buffer");
	#endif

	mp_TexBitBltBuf = mp_ClutBitBltBuf = NULL;

	// Set flags
	if (load_screen)
	{
		m_flags |= mLOAD_SCREEN;
	}
	if (sprite)
	{
		m_flags |= mSPRITE;
	}
	if ((sprite && alloc_vram) && !load_screen)
	{
		m_flags |= mALLOC_VRAM;
	}

	// open the font file
	if (!sprite)
	{
		Dbg_Assert(!alloc_vram);
	}
	
	mp_PixelData = mp_ClutData = NULL;
	m_PixelDataSize = m_ClutDataSize = 0;
	
	mp_TexBuffer = (uint8*)Mem::Malloc( buffer_size );
	Dbg_Assert( mp_TexBuffer );
	memcpy( mp_TexBuffer, p_buffer, buffer_size );
	m_TexBufferSize = buffer_size;
	
	// Initialize all the data
	InitTexture(false);

	// add texture to texture list
	if (sprite && !load_screen)
	{
		mp_next = sp_stexture_list;
		sp_stexture_list = this;
	}

	if (load_screen) {
		FlipTextureVertical();
	}
}

/////////////////////////////////////////////////////////////////////////////
//
// .img.ps2 file

SSingleTexture::SSingleTexture(const char *Filename, bool sprite, bool alloc_vram, bool load_screen) :
	m_flags(0),
	m_usage_count(0)
{
	char ExtendedFilename[128];
		  
	#ifdef			__NOPT_ASSERT__
	strcpy(m_name,Filename);
	#endif

	mp_TexBitBltBuf = mp_ClutBitBltBuf = NULL;

	// Set flags
	if (load_screen)
	{
		m_flags |= mLOAD_SCREEN;
	}
	if (sprite)
	{
		m_flags |= mSPRITE;
	}
	if ((sprite && alloc_vram) && !load_screen)
	{
		m_flags |= mALLOC_VRAM;
	}

	// open the font file
	if (sprite)
	{
//		strcpy(ExtendedFilename, "images/");
//		strcat(ExtendedFilename, Filename);
		strcpy(ExtendedFilename, Filename);
	} else {
		Dbg_Assert(!alloc_vram);

		strcpy(ExtendedFilename, Filename);
	}
	strcat(ExtendedFilename, ".img.ps2");
	
	mp_PixelData = mp_ClutData = NULL;
	m_PixelDataSize = m_ClutDataSize = 0;
	
	#if 0
	void *pFile = File::Open(ExtendedFilename, "rb");
	// get file size and allocate texture buffer
	m_TexBufferSize = File::GetFileSize(pFile);
	mp_TexBuffer = (uint8 *)Mem::Malloc(m_TexBufferSize);
	File::Read(mp_TexBuffer, 1, m_TexBufferSize, pFile);
	File::Close(pFile);
	#else
	mp_TexBuffer = (uint8*)File::LoadAlloc(ExtendedFilename, &m_TexBufferSize);
	#endif
	
	// Initialize all the data
	InitTexture(false);

	// add texture to texture list
	if (sprite && !load_screen)
	{
		mp_next = sp_stexture_list;
		sp_stexture_list = this;
	}

	if (load_screen) {
		FlipTextureVertical();
	}
}

/////////////////////////////////////////////////////////////////////////////
//
// raw buffer

SSingleTexture::SSingleTexture(uint8* p_buffer, int width, int height, int bitdepth, int clut_bitdepth, int mipmaps,
							   bool sprite, bool alloc_vram, bool load_screen):
	m_flags(0),
	m_usage_count(0)
{
	#ifdef			__NOPT_ASSERT__
	strcpy(m_name,"texture from raw data");
	#endif

	mp_TexBitBltBuf = mp_ClutBitBltBuf = NULL;

	// Set flags
	if (load_screen)
	{
		m_flags |= mLOAD_SCREEN;
	}
	if (sprite)
	{
		m_flags |= mSPRITE;
	}
	if ((sprite && alloc_vram) && !load_screen)
	{
		m_flags |= mALLOC_VRAM;
	}

	// open the font file
	if (!sprite)
	{
		Dbg_Assert(!alloc_vram);
	}
	
	mp_PixelData = mp_ClutData = NULL;
	m_PixelDataSize = m_ClutDataSize = 0;
	mp_TexBuffer = NULL;		// Since we aren't allocating any memory
	m_TexBufferSize = 0;
	
	m_checksum = 0;
	m_orig_width = width;
	m_orig_height = height;

	// Initialize all the data
	//InitTexture(false);
	uint32 TW, TH, PSM, CPSM, MXL;

	TW = TextureMemoryLayout::numBits(width - 1);		// Rounds up to the nearest 2^N
	TH = TextureMemoryLayout::numBits(height - 1);

	PSM = bitdepth_to_psm(bitdepth);
	CPSM = bitdepth_to_psm(clut_bitdepth);

	MXL = mipmaps - 1;

	// Raw data is inverted vertically from the way we expect textures.
	m_flags |= mINVERTED;

	setup_reg_and_dma(p_buffer, TW, TH, PSM, CPSM, MXL, false);

	// add texture to texture list
	if (sprite && !load_screen)
	{
		mp_next = sp_stexture_list;
		sp_stexture_list = this;
	}

	if (load_screen) {
		FlipTextureVertical();
	}
}

/////////////////////////////////////////////////////////////////////////////
//
// copy constructor

SSingleTexture::SSingleTexture(const SSingleTexture & src_texture)
{
	// Copy checksum and flags
	m_checksum = src_texture.m_checksum;
	m_flags = src_texture.m_flags;

	#ifdef			__NOPT_ASSERT__
	strcpy(m_name,src_texture.m_name);
	#endif

	// Init some of the data before we call InitTexture()
	m_usage_count = 0;
	mp_TexBitBltBuf = mp_ClutBitBltBuf = NULL;
	mp_PixelData = mp_ClutData = NULL;
	m_PixelDataSize = m_ClutDataSize = 0;

	// Copy file buffer
	m_TexBufferSize = src_texture.m_TexBufferSize;
	if (m_TexBufferSize && src_texture.mp_TexBuffer)
	{
		mp_TexBuffer = (uint8 *)Mem::Malloc(m_TexBufferSize);
		memcpy(mp_TexBuffer, src_texture.mp_TexBuffer, m_TexBufferSize);

		// This routine will initialize and allocate the rest
		InitTexture(true);
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't copy SSingleTexture that was created with a raw buffer"));
		//InitTexture(true);
	}

	// add texture to texture list
	if ((m_flags & mSPRITE) && !(m_flags & mLOAD_SCREEN))
	{
		mp_next = sp_stexture_list;
		sp_stexture_list = this;
	}
}

/////////////////////////////////////////////////////////////////////////////
//

SSingleTexture::~SSingleTexture()
{
	// find texture and unchain from list
	if (sp_stexture_list == this)
	{
		sp_stexture_list = mp_next;
	} else if (sp_stexture_list) {
		SSingleTexture *p_texture = sp_stexture_list;

		while(p_texture->mp_next)
		{
			if (p_texture->mp_next == this)
			{
				p_texture->mp_next = mp_next;
				break;
			}

			p_texture = p_texture->mp_next;
		}
	}

	// free memory
	if (mp_VifData) Mem::Free(mp_VifData);
	if (mp_TexBuffer) Mem::Free(mp_TexBuffer);

	Dbg_MsgAssert(m_usage_count == 0, ("Trying to remove SSingleTexture %x that is still being used", m_checksum));

	// and re-do VRAM allocation
	VRAMNeedsUpdating();
}

/////////////////////////////////////////////////////////////////////////////
//
#define STEX_USE_CALL 1

const static uint32 TEXTURE_VERSION = 1;

TextureMemoryLayout TexMemLayout;

bool SSingleTexture::InitTexture(bool copy)
{
	uint32 TW, TH, PSM, CPSM, MXL = 0;

	uint8 *pData = mp_TexBuffer;

	uint32 version;
	version = *(uint32 *)pData, pData+=4;
	Dbg_MsgAssert(version >= TEXTURE_VERSION, ("Wrong version in .img file: %d. Game uses: %d", version, TEXTURE_VERSION));
	m_checksum = *(uint32 *)pData, pData+=4;

	// get dimensions
	TW = *(uint32 *)pData, pData+=4;
	TH = *(uint32 *)pData, pData+=4;

	// get pixel storage modes
	PSM  = *(uint32 *)pData, pData+=4;	// for texels
	CPSM = *(uint32 *)pData, pData+=4;	// for clut

	// get maximum mipmap level
	if (version >= 2)
	{
		MXL  = *(uint32 *)pData, pData+=4;
	}
	Dbg_Assert(MXL == 0);
	m_orig_width = *(uint16 *)pData, pData+=2;
	m_orig_height = *(uint16 *)pData, pData+=2;

	// bail if it's a non-texture
	Dbg_MsgAssert((TW != 0xFFFFFFFF), ("No texture in .img file\n"));

	// quadword-align
	pData = (uint8 *)(((uint)(pData+15)) & 0xFFFFFFF0);

	return setup_reg_and_dma(pData, TW, TH, PSM, CPSM, MXL, copy);
}

bool SSingleTexture::setup_reg_and_dma(uint8 *pData, uint32 TW, uint32 TH, uint32 PSM, uint32 CPSM, uint32 MXL, bool copy)
{
	uint TBW;
	uint Width, Height, NumTexBytes, NumTexQWords, NumClutBytes, NumClutQWords, NumVramBytes;
	uint BitsPerTexel, BitsPerClutEntry, PaletteSize;
	uint NextTBP;
	int j,k;

	uint8 *old_dma_loc = dma::pLoc;		  

	// initialise base pointers
	if (IsInVRAM())
	{
		NextTBP = FontVramBase;
	} else {
		NextTBP = FontVramStart;
	}

	// bits per texel and palette size
	switch (PSM)
	{
	case PSMCT32:
		BitsPerTexel = 32;
		PaletteSize  = 0;
		break;
	case PSMCT24:
		BitsPerTexel = 24;
		PaletteSize  = 0;
		break;
	case PSMCT16:
		BitsPerTexel = 16;
		PaletteSize  = 0;
		break;
	case PSMT8:
		BitsPerTexel = 8;
		PaletteSize  = 256;
		break;
	case PSMT4:
		BitsPerTexel = 4;
		PaletteSize  = 16;
		break;
	default:
		printf("Unknown PSM %d in texture file\n", PSM);
		exit(1);
	}

	// bits per clut entry
	if (BitsPerTexel < 16)
		switch (CPSM)
		{
		case PSMCT32:
			BitsPerClutEntry = 32;
			break;
		case PSMCT16:
			BitsPerClutEntry = 16;
			break;
		default:
			printf("Unknown CPSM %d in texture file\n", PSM);
			exit(1);
		}
	else
		BitsPerClutEntry = 0;

	// rearrange 256-colour cluts according to requirements of CSM1
	if (!copy && PSM==PSMT8)
	{
		uint32 temp32;
		uint16 temp16;
		for (j=0; j<256; j+=32)
			for (k=0; k<8; k++)
				if (CPSM==PSMCT32)
				{
					temp32 = ((uint32 *)pData)[j+k+8];
					((uint32 *)pData)[j+k+8] = ((uint32 *)pData)[j+k+16];
					((uint32 *)pData)[j+k+16] = temp32;
				}
				else
				{
					temp16 = ((uint16 *)pData)[j+k+8];
					((uint16 *)pData)[j+k+8] = ((uint16 *)pData)[j+k+16];
					((uint16 *)pData)[j+k+16] = temp16;
				}
	}

	// calculate texture dimensions
	Width = (TW>=0 ? 1<=0 ? 1<> 6;
	if (BitsPerTexel<16 && TBW<2)
		TBW = 2;

	// Calculate memory needed
	int min_width = Width, min_height = Height;
	TexMemLayout.getMinSize(min_width, min_height, PSM);
	m_NumTexVramBlocks = TexMemLayout.getImageSize(min_width, min_height, PSM);
	NumVramBytes = m_NumTexVramBlocks * 256;
	//NumVramBytes = (TBW << 6) * Height * BitsPerTexel >> 3;

	// calculate sizes within file
	NumClutBytes  = PaletteSize * BitsPerClutEntry >> 3;
	NumClutQWords = NumClutBytes >> 4;
	NumTexBytes  = Width * Height * BitsPerTexel >> 3;
	NumTexQWords = NumTexBytes >> 4;

	//Dbg_Message("Texture size (%d, %d) of bitdepth %d and BufferWidth %d", Width, Height, BitsPerTexel, TBW);
	//Dbg_MsgAssert(calcVramBytes <= NumVramBytes, ("Need more memory: %d vs. needed %d", NumVramBytes, calcVramBytes));

	// allocate memory for vif stream
#if STEX_USE_CALL
	m_VifSize = 272;
#else
	m_VifSize = 1024 + NumClutBytes + NumTexBytes; //256; //1216 + (((Width*Height+15)>>4)<<4);
#endif // STEX_USE_CALL
	mp_VifData = (uint8 *)Mem::Malloc(m_VifSize);
	Dbg_MsgAssert(mp_VifData, ("couldn't malloc memory for vif data"));
	Dbg_MsgAssert(((uint32)mp_VifData & 15) == 0, ("vif data not qword aligned"));

	// build dma data in this buffer
	dma::pLoc = mp_VifData;

	// calculate TBP
	m_TBP = NextTBP;

	// calculate next TBP
	//NextTBP = (((TBP<<8)+NumVramBytes+0x1FFF) & 0xFFFFE000) >> 8;
	NextTBP = m_TBP + m_NumTexVramBlocks;

	// calculate CBP
	m_CBP = NextTBP;
	if (BitsPerTexel >= 16)
		m_NumClutVramBlocks = 0;
	else if (BitsPerTexel == 4)
		m_NumClutVramBlocks = 4;
	else
		m_NumClutVramBlocks = (CPSM==PSMCT32 ? 4 : 4);

	NextTBP += m_NumClutVramBlocks;

	if (IsInVRAM() && Script::GetInt( "vram_debug", false ))
	{
		Dbg_Message("VRAM Used = %d blocks, texture Size = %d blocks, blocks left = %d out of %d", 
				NextTBP - FontVramStart, m_NumTexVramBlocks + m_NumClutVramBlocks, FontVramSize - (NextTBP - FontVramStart),FontVramSize);  										
	}
							  
	// bail if VRAM would become over-packed
	if (IsInVRAM() && (NextTBP > (FontVramStart + FontVramSize)))
	{
		Dbg_MsgAssert(0, ("SingleTexture: 2D VRAM Buffer Full: %x.", NextTBP));
		//pData += NumClutBytes;
		//pData = (uint8 *)(((uint)(pData+NumTexBytes+15)) & 0xFFFFFFF0);
		// And restore the CurrentDMALoc, which we were just using temporarily.
		dma::pLoc = old_dma_loc;		  
		return false;
	}

	// add clut upload to dma list if necessary
	if (BitsPerTexel < 16)
	{
#if STEX_USE_CALL
		dma::BeginTag(dma::cnt,0);
		vif::NOP();
		vif::DIRECT(6);
		gif::Tag2(gs::A_D,1,PACKED,0,0,0,4);
		mp_ClutBitBltBuf = gs::Reg2_pData(gs::BITBLTBUF,PackBITBLTBUF(0,0,0,m_CBP,1,CPSM));
		gs::Reg2(gs::TRXPOS,	PackTRXPOS(0,0,0,0,0));
		if (PSM==PSMT4)
			gs::Reg2(gs::TRXREG,PackTRXREG(8,2));
		else
			gs::Reg2(gs::TRXREG,PackTRXREG(16,16));
		gs::Reg2(gs::TRXDIR,	0);
		gif::Tag2(0,0,IMAGE,0,0,0,NumClutQWords);
		dma::EndTag();
		dma::Tag(dma::ref, NumClutQWords, (uint)pData);
		vif::NOP();
		vif::DIRECT(NumClutQWords);
#else
		// clut upload header
		gif::Tag2(gs::A_D,1,PACKED,0,0,0,4);
		mp_ClutBitBltBuf = gs::Reg2_pData(gs::BITBLTBUF,PackBITBLTBUF(0,0,0,m_CBP,1,CPSM));
		gs::Reg2(gs::TRXPOS,	PackTRXPOS(0,0,0,0,0));
		if (PSM==PSMT4)
			gs::Reg2(gs::TRXREG,PackTRXREG(8,2));
		else
			gs::Reg2(gs::TRXREG,PackTRXREG(16,16));
		gs::Reg2(gs::TRXDIR,	0);
		gif::Tag2(0,0,IMAGE,0,0,0,NumClutQWords);

		// read clut bitmap data
		memcpy(dma::pLoc, pData, NumClutBytes);

		// step dma location
		dma::pLoc += NumClutBytes;
#endif // STEX_USE_CALL

		// Save pointer
		mp_ClutData = pData;
		m_ClutDataSize = NumClutBytes;

		// step past clut data, and quadword-align
		pData = (uint8 *)(((uint)(pData+NumClutBytes+15)) & 0xFFFFFFF0);
	}

#if STEX_USE_CALL
	// add texture upload to dma list
	dma::BeginTag(dma::cnt,0);
	vif::NOP();
	vif::DIRECT(6);
	gif::Tag2(gs::A_D,1,PACKED,0,0,0,4);
	mp_TexBitBltBuf = gs::Reg2_pData(gs::BITBLTBUF,PackBITBLTBUF(0,0,0,m_TBP,TBW,PSM));
	gs::Reg2(gs::TRXPOS,	PackTRXPOS(0,0,0,0,0));
	gs::Reg2(gs::TRXREG,	PackTRXREG(Width,Height));
	gs::Reg2(gs::TRXDIR,	0);
	gif::Tag2(0, 0, IMAGE, 0, 0, 0, NumTexQWords);
	dma::EndTag();
	dma::Tag(dma::ref, NumTexQWords, (uint)pData);
	vif::NOP();
	vif::DIRECT(NumTexQWords);
#else
	// texture upload header
	gif::Tag2(gs::A_D,1,PACKED,0,0,0,4);
	mp_TexBitBltBuf = gs::Reg2_pData(gs::BITBLTBUF,PackBITBLTBUF(0,0,0,m_TBP,TBW,PSM));
	gs::Reg2(gs::TRXPOS,	PackTRXPOS(0,0,0,0,0));
	gs::Reg2(gs::TRXREG,	PackTRXREG(Width,Height));
	gs::Reg2(gs::TRXDIR,	0);
	gif::Tag2(0, 0, IMAGE, 0, 0, 0, NumTexQWords);

	// read texture bitmap data
	memcpy(dma::pLoc, pData, NumTexBytes);
	dma::pLoc += NumTexQWords<<4;
#endif // STEX_USE_CALL

	// Handle mipmaps, if any (but don't make any DMA packets for them)
	if (!(m_flags & mSPRITE) && (MXL > 0))
	{
		for (j = 1; j < (int) MXL; j++)
		{
			int mip_width = (TW>=0 ? 1<> j;
			int mip_height = (TH>=0 ? 1<> j;

			// Add in mip sizes
			NumTexBytes += mip_width * mip_height * BitsPerTexel >> 3;
		}
	}

	// Save pointer
	mp_PixelData = pData;
	m_PixelDataSize = NumTexBytes;

	// make entry in texture table
	m_RegTEX0    = PackTEX0(m_TBP,TBW,PSM,TW,TH,1,MODULATE,m_CBP,CPSM,0,0,1);
	m_RegTEX1	 = PackTEX1(1,0,LINEAR,LINEAR,0,0,0);

	// update vram base pointer
	if (IsInVRAM())
	{
		FontVramBase = NextTBP;
	}

#if STEX_USE_CALL
	// Since this data is called, do a return
	dma::Tag(dma::ret, 0, 0);
	vif::NOP();
	vif::NOP();
#endif // STEX_USE_CALL

	#ifdef	__NOPT_ASSERT__
	uint ActualSize = (uint) dma::pLoc - (uint) mp_VifData;
	Dbg_MsgAssert(ActualSize <= m_VifSize, ("Did not allocate enough DMA space for textures: allocated=%d actual=%d", m_VifSize, ActualSize));
	//Dbg_Message("Texture packet size = %d", (int) dma::pLoc - (int) pVifData);
	#endif

	// And restore the CurrentDMALoc, which we were just using temporarily.
	dma::pLoc = old_dma_loc;		  

	return true;
}

/////////////////////////////////////////////////////////////////////////////
// Need to do this to textures that are copied directly into the frame buffer
//

void	SSingleTexture::FlipTextureVertical()
{
	int startrow = 0;
	int endrow = m_orig_height - 1;
	int rowsize = (m_orig_width * GetBitdepth()) / 8;

	uint8 *p_temp_buffer = new uint8[rowsize];
	while (startrow < endrow)
	{
		// Swap rows
		memcpy(p_temp_buffer,						&(mp_PixelData[startrow * rowsize]),rowsize);
		memcpy(&(mp_PixelData[startrow * rowsize]),	&(mp_PixelData[endrow * rowsize]),	rowsize);
		memcpy(&(mp_PixelData[endrow * rowsize]),	p_temp_buffer,						rowsize);

		startrow++;
		endrow--;
	}

	delete p_temp_buffer;
}

/////////////////////////////////////////////////////////////////////////////
//
bool SSingleTexture::AddToVRAM()
{
	Dbg_MsgAssert(!(m_flags & mAUTO_ALLOC), ("Can't use AddToVRAM() on a auto allocated texture"));

	#ifdef			__NOPT_ASSERT__
//	printf ("++++ Adding %s ++++\n",m_name);
	#endif 
	m_flags |= mALLOC_VRAM;

	VRAMNeedsUpdating();

	return true;
}

/////////////////////////////////////////////////////////////////////////////
//
bool SSingleTexture::RemoveFromVRAM()
{
	Dbg_MsgAssert(!(m_flags & mAUTO_ALLOC), ("Can't use RemoveFromVRAM() on a auto allocated texture"));

	#ifdef			__NOPT_ASSERT__
//	printf ("---- removing %s ----\n",m_name);
	#endif 
	m_flags &= ~mALLOC_VRAM;

	VRAMNeedsUpdating();

	return true;
}

/////////////////////////////////////////////////////////////////////////////
//

void SSingleTexture::SetAutoAllocate()
{
	m_flags |= mAUTO_ALLOC;
}

/////////////////////////////////////////////////////////////////////////////
//

void SSingleTexture::inc_usage_count()
{
	m_usage_count++;
	m_flags |= mALLOC_VRAM;
}

/////////////////////////////////////////////////////////////////////////////
//

void SSingleTexture::dec_usage_count()
{
	if (--m_usage_count == 0)
	{
		m_flags &= ~mALLOC_VRAM;
	}

	Dbg_MsgAssert(m_usage_count >= 0, ("Usage Count negative on  SSingleTexture %x", m_checksum));
}

/////////////////////////////////////////////////////////////////////////////
//

void SSingleTexture::UseTexture(bool use)
{
	Dbg_MsgAssert(m_flags & mAUTO_ALLOC, ("Can't use UseTexture() on a non-auto allocated texture"));

	bool old_alloc = IsInVRAM();

	// Update usage
	if (use)
	{
		inc_usage_count();
	}
	else
	{
		dec_usage_count();
	}

	// See if we need to update DMA
	if (old_alloc != use)
	{
		VRAMNeedsUpdating();
	}
}

/////////////////////////////////////////////////////////////////////////////
//

uint16	SSingleTexture::GetOrigWidth() const
{
	return m_orig_width;
}

/////////////////////////////////////////////////////////////////////////////
//

uint16	SSingleTexture::GetOrigHeight() const
{
	return m_orig_height;
}

/////////////////////////////////////////////////////////////////////////////
//

void	SSingleTexture::ReallocateVRAM()
{
	// Don't realloc load screens
	if (!IsInVRAM()) return;

	// calculate TBP
	m_TBP = FontVramBase;
	FontVramBase += m_NumTexVramBlocks;

	// calculate CBP
	m_CBP = FontVramBase;
	FontVramBase += m_NumClutVramBlocks;

	// bail if VRAM would become over-packed
	if (FontVramBase > (FontVramStart + FontVramSize))
	{
		Dbg_MsgAssert(0, ("SingleTexture Realloc: 2D VRAM Buffer Full: %x.", FontVramBase));
	}

	// Modify the data
	gs::ModifyTBP(gs::TEX0_1, (uint32 *) &m_RegTEX0, m_TBP, m_CBP);
}

/////////////////////////////////////////////////////////////////////////////
//

void	SSingleTexture::UpdateDMA()
{
	// Modify the packets
	gs::ModifyTBP(gs::BITBLTBUF, mp_TexBitBltBuf, m_TBP);
	if (mp_ClutBitBltBuf)
	{
		gs::ModifyTBP(gs::BITBLTBUF, mp_ClutBitBltBuf, m_CBP);
	}
}

/////////////////////////////////////////////////////////////////////////////
//
// The following Get() calls are very inefficient.  They should only be
// used when necessary (i.e. initialization).
//
uint16	SSingleTexture::GetWidth() const
{
	sceGsTex0 tex0 = *((sceGsTex0 *) &m_RegTEX0);
	uint16 TW = tex0.TW;
	//uint16 TW = (uint16) ((RegTEX0 >> 26) & M04);

	return 1 << TW;
}

uint16	SSingleTexture::GetHeight() const
{
	sceGsTex0 tex0 = *((sceGsTex0 *) &m_RegTEX0);
	uint16 TH = tex0.TH;
	//uint16 TH = (uint16) ((RegTEX0 >> 30) & M04);

	return 1 << TH;
}

uint8	SSingleTexture::GetBitdepth() const
{
	sceGsTex0 tex0 = *((sceGsTex0 *) &m_RegTEX0);
	uint PSM = tex0.PSM;

	switch (PSM)
	{
	case PSMCT32:
		return 32;
		break;
	case PSMCT24:
		return 24;
		break;
	case PSMCT16:
		return 16;
		break;
	case PSMT8:
		return 8;
		break;
	case PSMT4:
		return 4;
		break;
	default:
		return 0;
	}
}

uint8	SSingleTexture::GetClutBitdepth() const
{
	sceGsTex0 tex0 = *((sceGsTex0 *) &m_RegTEX0);
	uint CPSM = tex0.CPSM;

	switch (CPSM)
	{
	case PSMCT32:
		return 32;
		break;
	case PSMCT24:
		return 24;
		break;
	case PSMCT16:
		return 16;
		break;
	default:
		return 0;
	}
}

uint8	SSingleTexture::GetNumMipmaps() const
{
	return 1;		// By definition for this class
}

bool	SSingleTexture::IsTransparent() const
{
	//Dbg_Error("Not working yet");
	return false;
}

uint32	SSingleTexture::bitdepth_to_psm(int bitdepth)
{
	switch (bitdepth)
	{
	case 32:
		return PSMCT32;
		break;
	case 24:
		return PSMCT24;
		break;
	case 16:
		return PSMCT16;
		break;
	case 8:
		return PSMT8;
		break;
	case 4:
		return PSMT4;
		break;
	default:
		Dbg_MsgAssert(0, ("Bitdepth %d is not supported on PS2", bitdepth));
		return PSMCT32;
		break;
	}
}

#ifdef			__NOPT_ASSERT__
void	SSingleTexture::sPrintAllTextureInfo()
{
	for (SSingleTexture *pTexture=SSingleTexture::sp_stexture_list; pTexture; pTexture=pTexture->mp_next)
	{
		Dbg_Message("Texture name %s: using vram: %d, blocks %d", pTexture->m_name, pTexture->IsInVRAM(),
					pTexture->m_NumTexVramBlocks + pTexture->m_NumClutVramBlocks);
	}
}

void	SSingleTexture::sPrintAllocatedTextureInfo()
{
	for (SSingleTexture *pTexture=SSingleTexture::sp_stexture_list; pTexture; pTexture=pTexture->mp_next)
	{
		if (!pTexture->IsInVRAM())
		{
			continue;
		}

		Dbg_Message("Texture name %s: using vram: , blocks %d", pTexture->m_name,
					pTexture->m_NumTexVramBlocks + pTexture->m_NumClutVramBlocks);
	}
}

#endif

/******************************************************************/
/*                                                                */
/* SScissorWindow												  */
/*                                                                */
/******************************************************************/

SScissorWindow DefaultScissorWindow;

void SetTextWindow(uint16 x0, uint16 x1, uint16 y0, uint16 y1)
{
	DefaultScissorWindow.SetScissor(x0, y0, x1, y1);
}

void SScissorWindow::SetScissor(uint16 x0, uint16 y0, uint16 x1, uint16 y1)
{
	m_RegSCISSOR = (uint64)x0 | (uint64)x1<<16 | (uint64)y0<<32 | (uint64)y1<<48;
}

/******************************************************************/
/*                                                                */
/* SDraw2D														  */
/*                                                                */
/******************************************************************/

float		SDraw2D::s_screen_scale_x = 1.0f;
float		SDraw2D::s_screen_scale_y = 1.0f;

bool		SDraw2D::s_constant_z_enabled = false;
uint32		SDraw2D::s_constant_z = 0x7FFFFF;

SDraw2D *	SDraw2D::sp_2D_sorted_draw_list = NULL;
SDraw2D *	SDraw2D::sp_2D_zbuffer_draw_list = NULL;


/////////////////////////////////////////////////////////////////////////////
//

SDraw2D::SDraw2D(float pri, bool hide) :
	m_hidden(hide),
	m_use_zbuffer(false),
	m_pri(pri),
	m_z(0xFFFFFF),
	mp_scissor_window(&DefaultScissorWindow)
{
	mp_next = NULL;

	// add to sorted draw list
	if (!m_hidden)
	{
		InsertSortedDrawList(&sp_2D_sorted_draw_list);
	}
}

/////////////////////////////////////////////////////////////////////////////
//

SDraw2D::~SDraw2D()
{
	// Try removing from draw list
	if (m_use_zbuffer)
	{
		RemoveDrawList(&sp_2D_zbuffer_draw_list);
	} else {
		RemoveDrawList(&sp_2D_sorted_draw_list);
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::SetPriority(float pri)
{
	if (m_pri != pri)
	{
		m_pri = pri;

		// By removing and re-inserting, we re-sort the list
		if (!m_hidden)
		{
			if (m_use_zbuffer)
			{
				RemoveDrawList(&sp_2D_zbuffer_draw_list);
			} else {
				RemoveDrawList(&sp_2D_sorted_draw_list);
			}
			InsertSortedDrawList(&sp_2D_sorted_draw_list);
		}

		m_use_zbuffer = false;
		m_z = 0xFFFFFF;
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::SetZValue(uint32 z)
{
	if (m_z != z)
	{
		m_z = z;

		// Switch lists, if necessary
		if (!m_use_zbuffer && !m_hidden)
		{
			RemoveDrawList(&sp_2D_sorted_draw_list);
			InsertDrawList(&sp_2D_zbuffer_draw_list);
		}

		m_use_zbuffer = true;
		m_pri = 0.0f;
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::SetHidden(bool hide)
{
	if (m_hidden != hide)
	{
		m_hidden = hide;
		if (m_use_zbuffer)
		{
			if (hide)
			{
				RemoveDrawList(&sp_2D_zbuffer_draw_list);
			} else {
				InsertDrawList(&sp_2D_zbuffer_draw_list);
			}
		} else {
			if (hide)
			{
				RemoveDrawList(&sp_2D_sorted_draw_list);
			} else {
				InsertSortedDrawList(&sp_2D_sorted_draw_list);
			}
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::SetScissorWindow(SScissorWindow *p_scissor)
{
	if (p_scissor)
	{
		mp_scissor_window = p_scissor;
	} else {
		mp_scissor_window = &DefaultScissorWindow;
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::DrawAll()
{
	// Figure out screen scale (for PAL mode)
	CalcScreenScale();

	// Init certain GS registers for 2D drawing
	dma::BeginTag(dma::cnt, 0);
	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::CLAMP_1,PackCLAMP(CLAMP,CLAMP,0,0,0,0));		// Man, I hope this is the last time I fix the CLAMP bug
	gs::Reg1(gs::XYOFFSET_1, PackXYOFFSET(XOFFSET, YOFFSET));
	gs::Reg1(gs::TEST_1, PackTEST(1,AGREATER,0,KEEP,0,0,1,ZGEQUAL));
	gs::EndPrim(0);
	dma::EndTag();

	// Draw items needing ZBuffer first
	DrawList(sp_2D_zbuffer_draw_list);
	if (s_constant_z_enabled)
	{
		DrawList(sp_2D_sorted_draw_list);
	}

	// Turn off ZBuffer
	dma::BeginTag(dma::cnt, 0);
	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::TEST_1, PackTEST(0,0,0,0,0,0,1,ZGEQUAL));
	gs::EndPrim(0);
	dma::EndTag();

	// Draw the sorted list now if we aren't using constant Z
	if (!s_constant_z_enabled)
	{
		DrawList(sp_2D_sorted_draw_list);
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::DrawList(SDraw2D *pDraw)
{
	while (pDraw)
	{
		if (!pDraw->m_hidden)
		{
			pDraw->BeginDraw();
			pDraw->Draw();
			pDraw->EndDraw();
		}
		pDraw = pDraw->mp_next;
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::EnableConstantZValue(bool enable)
{
	s_constant_z_enabled = enable;
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::SetConstantZValue(uint32 z)
{
	s_constant_z = z;
}

/////////////////////////////////////////////////////////////////////////////
//

uint32 SDraw2D::GetConstantZValue()
{
	return s_constant_z;
}


/////////////////////////////////////////////////////////////////////////////
// Figure out screen scale (mainly for PAL mode)
//

void SDraw2D::CalcScreenScale()
{
	if (Config::NTSC())
	{
		s_screen_scale_x = 1.0f;
		s_screen_scale_y = 1.0f;
	} else {
		s_screen_scale_x = ((float) HRES_PAL) / ((float) HRES_NTSC);
		s_screen_scale_y = ((float) VRES_PAL) / ((float) VRES_NTSC);
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::InsertSortedDrawList(SDraw2D **p_list)
{
	Dbg_Assert(!m_use_zbuffer);

	if (!*p_list ||							// Empty List
		(m_pri <= (*p_list)->m_pri))		// Start List
	{
		mp_next = *p_list;
		*p_list = this;
	} else {				// Insert
		SDraw2D *p_cur = *p_list;
	
		// Find where to insert
		while(p_cur->mp_next)
		{
			if (m_pri <= p_cur->mp_next->m_pri)
				break;

			p_cur = p_cur->mp_next;
		}

		// Insert at this point
		mp_next = p_cur->mp_next;
		p_cur->mp_next = this;
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::InsertDrawList(SDraw2D **p_list)
{
	if (!*p_list)							// Empty List
	{
		mp_next = *p_list;
		*p_list = this;
	} else {								// Insert at end of list
		SDraw2D *p_cur = *p_list;

		while(p_cur->mp_next)
		{
			p_cur = p_cur->mp_next;
		}

		mp_next = p_cur->mp_next;
		p_cur->mp_next = this;
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SDraw2D::RemoveDrawList(SDraw2D **p_list)
{
	// Take out from draw list
	if (*p_list == this)
	{
		*p_list = mp_next;
	} 
	else if (*p_list) 
	{
		SDraw2D *p_cur = *p_list;

		while(p_cur->mp_next)
		{
			if (p_cur->mp_next == this)
			{
				p_cur->mp_next = mp_next;
				break;
			}

			p_cur = p_cur->mp_next;
		}
	}
}


/******************************************************************/
/*                                                                */
/* SSprite														  */
/*                                                                */
/******************************************************************/

/////////////////////////////////////////////////////////////////////////////
//

SSprite::SSprite(float pri) : SDraw2D(pri, true)
{
}

/////////////////////////////////////////////////////////////////////////////
//

SSprite::~SSprite()
{
	// Deallocates the underlying texture, if there was one
	SetHidden(true);
}

/////////////////////////////////////////////////////////////////////////////
//

void SSprite::SetHidden(bool hide)
{
	bool old_hide = IsHidden();

	// Do the actual hide call
	SDraw2D::SetHidden(hide);

	// Make sure vram status is correct if we changed hidden states
	if (mp_texture && (old_hide != hide))
	{
		mp_texture->UseTexture(!hide);
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SSprite::SetTexture(SSingleTexture *p_texture)
{
	// Set auto VRAM allocate
	if (p_texture && (mp_texture != p_texture))
	{
		p_texture->SetAutoAllocate();

		// Change usage counts if we aren't hidden
		if (!IsHidden())
		{
			p_texture->UseTexture(true);
			if (mp_texture)
			{
				mp_texture->UseTexture(false);
			}
		}
	}

	mp_texture = p_texture;
}

/////////////////////////////////////////////////////////////////////////////
//

void SSprite::BeginDraw()
{

	// dma tag
	dma::BeginTag(dma::cnt, 0);

	vu1::Buffer = vu1::Loc;

	int tme_flag = (mp_texture) ? TME : 0;

	Dbg_Assert(GetScissorWindow());

	// GS context
	gs::BeginPrim(ABS,0,0);
	gs::Reg1(gs::SCISSOR_1, GetScissorWindow()->GetScissorReg());
	if (mp_texture)
	{
		Dbg_MsgAssert(mp_texture->IsInVRAM(), ("Can't draw sprite when its texture 0x%x, (%s) is not in VRAM.",mp_texture->m_checksum,mp_texture->m_name));

		gs::Reg1(gs::TEX0_1,	 mp_texture->m_RegTEX0);
		gs::Reg1(gs::TEX1_1,	 mp_texture->m_RegTEX1);
	}
	gs::Reg1(gs::ALPHA_1,	 PackALPHA(0,1,0,1,0));
	gs::Reg1(gs::COLCLAMP,	 PackCOLCLAMP(1));
	gs::Reg1(gs::RGBAQ,	 (uint64)m_rgba);
	gs::EndPrim(0);

	// unpack giftag
	vif::UNPACK(0, V4_32, 1, ABS, UNSIGNED, 0);
	if (m_rot == 0.0f)
	{
		gif::BeginTagImmediate(gs::UV|gs::XYZ2<<4, 2, PACKED, SPRITE|tme_flag|FST|ABE, 1, VU1_ADDR(GSPrim));
	} else {
		gif::BeginTagImmediate(gs::UV|gs::XYZ2<<4, 2, PACKED, TRISTRIP|tme_flag|FST|ABE, 1, VU1_ADDR(GSPrim));
	}

	// begin unpack for uv/xyz's
	vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 1);
}


/////////////////////////////////////////////////////////////////////////////
//

void SSprite::Draw()
{
	Dbg_Assert(GetScissorWindow());

	const int start_screen_x = XOFFSET + ((GetScissorWindow()->GetScissorReg() & 0x7FF) << 4) + ((int) (m_xpos * s_screen_scale_x * 16.0f));
	const int start_screen_y = YOFFSET + ((GetScissorWindow()->GetScissorReg() >> 32 & 0x7FF) << 4) + ((int) (m_ypos * s_screen_scale_y * 16.0f));

	uint16 u0,v0,u1,v1;
	float x0,y0,x1,y1;
	uint32 z;

	uint16 tex_height = (mp_texture) ? mp_texture->GetHeight() : m_height;

	u0 = /* 8 +*/ (0);
	v0 = /*-8 +*/ (tex_height << 4);
	u1 = /* 8 +*/ (m_width << 4);
	v1 = /*-8 +*/ ((tex_height - m_height) << 4);

	// Check for flip
	float abs_scale_x = m_scale_x;// * s_screen_scale_x;
	float abs_scale_y = m_scale_y;// * s_screen_scale_y;
	if (abs_scale_x < 0.0f)
	{
		uint16 temp = u0;
		u0 = u1;
		u1 = temp;
		abs_scale_x = -abs_scale_x;
	}
	if (abs_scale_y < 0.0f)
	{
		uint16 temp = v0;
		v0 = v1;
		v1 = temp;
		abs_scale_y = -abs_scale_y;
	}

	x0 = -m_xhot * 16.0f * abs_scale_x;
	y0 = -m_yhot * 16.0f * abs_scale_y;

	x1 = x0 + ((float) (u1 - u0) * abs_scale_x);
	y1 = y0 + ((float) (v0 - v1) * abs_scale_y);

	z = s_constant_z_enabled ? s_constant_z : GetZValue();

	// Flip V if texture is inverted in VRAM
	if (mp_texture && mp_texture->IsInverted())
	{
		uint16 temp = v0;
		v0 = v1;
		v1 = temp;
	}

	// Check for rotation
	if (m_rot != 0.0f)
	{
		Mth::Vector p0(x0, y0, 0.0f, 0.0f);
		Mth::Vector p1(x1, y0, 0.0f, 0.0f);
		Mth::Vector p2(x0, y1, 0.0f, 0.0f);
		Mth::Vector p3(x1, y1, 0.0f, 0.0f);

		p0.RotateZ(m_rot);
		p1.RotateZ(m_rot);
		p2.RotateZ(m_rot);
		p3.RotateZ(m_rot);

//printf("Before (%d, %d) - (%d, %d)\n", x0, y0, x1, y1);
//printf("After (%d, %d) - (%d, %d)\n", (int) p0.GetX(), (int) p0.GetY(), (int) p3.GetX(), (int) p3.GetY());

		int xi0 = ((int) (p0.GetX() * s_screen_scale_x)) + start_screen_x;
		int xi1 = ((int) (p1.GetX() * s_screen_scale_x)) + start_screen_x;
		int xi2 = ((int) (p2.GetX() * s_screen_scale_x)) + start_screen_x;
		int xi3 = ((int) (p3.GetX() * s_screen_scale_x)) + start_screen_x;
		int yi0 = ((int) (p0.GetY() * s_screen_scale_y)) + start_screen_y;
		int yi1 = ((int) (p1.GetY() * s_screen_scale_y)) + start_screen_y;
		int yi2 = ((int) (p2.GetY() * s_screen_scale_y)) + start_screen_y;
		int yi3 = ((int) (p3.GetY() * s_screen_scale_y)) + start_screen_y;

		// Cull, if necessary
		if (NeedsCulling() && ((xi0 | xi1 | xi2 | xi3 | yi0 | yi1 | yi2 | yi3) & 0xFFFF0000))
		{
			return;
		}

		// Do a TRISTRIP
		vif::StoreV4_32(u0,v0,0,0);
		vif::StoreV4_32(xi0, yi0, z, 0);
		vif::StoreV4_32(u1,v0,0,0);
		vif::StoreV4_32(xi1, yi1, z, 0);
		vif::StoreV4_32(u0,v1,0,0);
		vif::StoreV4_32(xi2, yi2, z, 0);
		vif::StoreV4_32(u1,v1,0,0);
		vif::StoreV4_32(xi3, yi3, z, 0);
	} else {
		// Just outputting two points for a SPRITE
		int xi0 = ((int) (x0 * s_screen_scale_x)) + start_screen_x;
		int xi1 = ((int) (x1 * s_screen_scale_x)) + start_screen_x;
		int yi0 = ((int) (y0 * s_screen_scale_y)) + start_screen_y;
		int yi1 = ((int) (y1 * s_screen_scale_y)) + start_screen_y;

		// Cull, if necessary
		if (NeedsCulling() && ((xi0 | xi1 | yi0 | yi1) & 0xFFFF0000))
		{
			return;
		}

		vif::StoreV4_32(u0,v0,0,0);
		vif::StoreV4_32(xi0,yi0,z,0);
		vif::StoreV4_32(u1,v1,0,0);
		vif::StoreV4_32(xi1,yi1,z,0);
	}
}

/////////////////////////////////////////////////////////////////////////////
//

void SSprite::EndDraw(void)
{
	vif::EndUNPACK();
	gif::EndTagImmediate(1);
	vif::MSCAL(VU1_ADDR(Parser));
	dma::EndTag();
}

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/sprite.h
================================================
#ifndef __SPRITE_H
#define __SPRITE_H

#ifndef __CORE_DEFINES_H
#include 
#endif
namespace NxPs2
{


// This is a temporary structure until we can merge the 2D textures
// with texture groups
struct SSingleTexture
{
public:
	// Flags
	enum
	{
		mLOAD_SCREEN		= 0x0001,
		mALLOC_VRAM			= 0x0002,
		mAUTO_ALLOC			= 0x0004,
		mSPRITE				= 0x0008,
		mINVERTED			= 0x0010,
	};
					SSingleTexture();
					SSingleTexture(uint8* p_buffer, int buffer_size, bool sprite, bool alloc_vram, bool load_screen = false);	// .img.ps2 buffer
					SSingleTexture(const char *Filename, bool sprite, bool alloc_vram, bool load_screen = false);				// .img.ps2 file
					SSingleTexture(uint8* p_buffer, int width, int height, int bitdepth, int clut_bitdepth, int mipmaps,
								   bool sprite, bool alloc_vram, bool load_screen = false);		// raw buffer
					SSingleTexture(const SSingleTexture & src_texture);		// Copy constructor
					~SSingleTexture();

	bool			InitTexture(bool copy);
	void			FlipTextureVertical();

	bool			IsInVRAM() const;

	// Manual allocate
	bool			AddToVRAM();
	bool			RemoveFromVRAM();

	// Auto allocate
	void			SetAutoAllocate();
	void			UseTexture(bool use);

	uint16			GetOrigWidth() const;
	uint16			GetOrigHeight() const;

	uint16			GetWidth() const;
	uint16			GetHeight() const;
	uint8			GetBitdepth() const;
	uint8			GetClutBitdepth() const;
	uint8			GetNumMipmaps() const;
	bool			IsTransparent() const;
	bool			IsInverted() const;

	uint8 *			GetTextureBuffer() const { return mp_PixelData; }
	uint32			GetTextureBufferSize() const { return m_PixelDataSize; }
	uint8 *			GetClutBuffer() const { return mp_ClutData; }
	uint32			GetClutBufferSize() const { return m_ClutDataSize; }

	void			ReallocateVRAM();
	void			UpdateDMA();

	uint32			m_checksum;
	uint32			m_flags;
	uint8 *			mp_VifData;
	uint8 *			mp_TexBuffer;
	uint8 *			mp_PixelData;
	uint8 *			mp_ClutData;
	uint32			m_PixelDataSize;
	uint32			m_ClutDataSize;
	uint32			m_VifSize;
	int				m_TexBufferSize;
	uint64			m_RegTEX0, m_RegTEX1;
	uint32 *		mp_TexBitBltBuf;		// Note these register pointers point to data in the DMA buffer,
	uint32 *		mp_ClutBitBltBuf;		// so they should only be used for writing, and only after drawing is done.

	uint16			m_orig_width, m_orig_height;
	uint16			m_NumTexVramBlocks;
	uint16			m_NumClutVramBlocks;
	uint16			m_TBP;
	uint16			m_CBP;

	SSingleTexture	*mp_next;

	#ifdef			__NOPT_ASSERT__
	char			m_name[128];
	#endif

	#ifdef			__NOPT_ASSERT__
	static void		sPrintAllTextureInfo();
	static void		sPrintAllocatedTextureInfo();
	#endif

	static SSingleTexture	*sp_stexture_list;

protected:

	bool			setup_reg_and_dma(uint8 *pData, uint32 TW, uint32 TH, uint32 PSM, uint32 CPSM, uint32 MXL, bool copy);

	// Update usage for auto VRAM allocate
	void			inc_usage_count();
	void			dec_usage_count();

	static uint32	bitdepth_to_psm(int bitdepth);

	int				m_usage_count;
};

struct SScissorWindow
{
	void			SetScissorReg(uint64 reg);
	void			SetScissor(uint16 x0, uint16 y0, uint16 x1, uint16 y1);
	uint64			GetScissorReg() const { return m_RegSCISSOR; }

private:
	uint64			m_RegSCISSOR;
};

struct SDraw2D
{
					SDraw2D(float pri = 0.0f, bool hide = true);
	virtual			~SDraw2D();

	void			SetPriority(float pri);
	float			GetPriority(void) const;

	void			SetZValue(uint32 z);
	uint32			GetZValue(void) const;

	virtual void	SetHidden(bool hide);		// Virtual because we need to check any vram issues
	bool			IsHidden(void) const;

	bool			NeedsCulling() const;

	void			SetScissorWindow(SScissorWindow *p_scissor);
	SScissorWindow *GetScissorWindow() const { return mp_scissor_window; }

	// Statics
	static void		DrawAll(void);

	static void		EnableConstantZValue(bool enable);
	static void		SetConstantZValue(uint32 z);
	static uint32	GetConstantZValue();

	static void		CalcScreenScale();
	static float	GetScreenScaleX();
	static float	GetScreenScaleY();

protected:
	// Scale for PAL conversion
	static float	s_screen_scale_x;
	static float	s_screen_scale_y;

	// Constant Z
	static bool		s_constant_z_enabled;
	static uint32	s_constant_z;

private:
	void			InsertSortedDrawList(SDraw2D **p_list);
	void			InsertDrawList(SDraw2D **p_list);
	void			RemoveDrawList(SDraw2D **p_list);

	static void		DrawList(SDraw2D *p_list);

	virtual void	BeginDraw(void) = 0;
	virtual void	Draw(void) = 0;
	virtual void	EndDraw(void) = 0;

	// Not even the derived classes should have direct access
	bool			m_hidden;
	bool			m_use_zbuffer;
	float			m_pri;
	uint32			m_z;
	SScissorWindow *mp_scissor_window;

	// members
	SDraw2D			*mp_next;

	// 2D draw list (sorted by priority)
	static SDraw2D	*sp_2D_sorted_draw_list;
	// 2D zbuffer draw list
	static SDraw2D	*sp_2D_zbuffer_draw_list;
};


struct SSprite : public SDraw2D
{
public:
					SSprite(float pri = 0.0f);
	virtual			~SSprite();

	virtual void	SetHidden(bool hide);		// Virtual because we need to check any vram issues

	void			SetTexture(SSingleTexture *p_texture);

	float			m_xpos;
	float			m_ypos;
	uint16			m_width;
	uint16			m_height;
	float			m_scale_x;
	float			m_scale_y;
	float			m_xhot;
	float			m_yhot;
	float			m_rot;
	uint32			m_rgba;

protected:

	SSingleTexture	*mp_texture;

private:
	void			BeginDraw(void);
	void			Draw(void);
	void			EndDraw(void);
};


//
// Inlines
//

inline bool		SSingleTexture::IsInVRAM() const
{
	return m_flags & mALLOC_VRAM;
}

inline bool		SSingleTexture::IsInverted() const
{
	return m_flags & mINVERTED;
}

inline float	SDraw2D::GetScreenScaleX()
{
	return s_screen_scale_x;
}

inline float	SDraw2D::GetScreenScaleY()
{
	return s_screen_scale_y;
}

inline uint32	SDraw2D::GetZValue() const
{
	return m_z;
}

inline bool		SDraw2D::IsHidden(void) const
{
	return m_hidden;
}

inline bool		SDraw2D::NeedsCulling() const
{
	return m_use_zbuffer;		// Assuming that only items using the ZBuffer need it
}

extern uint32		FontVramStart;
extern uint32		FontVramBase;
extern const uint32	FontVramSize;


} // namespace NxPs2


#endif // __CHARS_H


================================================
FILE: Code/Gfx/NGPS/NX/switches.h
================================================
#ifndef __SWITCHES_H
#define __SWITCHES_H

#ifdef __PLAT_NGPS__
#include 
#endif // __PLAT_NGPS__

#define	malloc(x) error Old Memory Function
#define	calloc(x) error Old Memory Function
#define	realloc(x) error Old Memory Function
#define	free(x) error Old Memory Function


namespace NxPs2
{

#define USE_INTERRUPTS 1
#define	GIF_INTERRUPT  1
#define VIF1_INTERRUPT 0
#define GS_INTERRUPT   1

#define TIMING_BARS 0

#define C32 1
#define C16 0
#define Z24 1
#define Z16 0

#ifdef __PLAT_NGPS__

#define HRES_NTSC	(640)
#define VRES_NTSC	(448)
#define HRES_PAL	(512)
#define VRES_PAL	(512)

#define HRES (Config::NTSC() ? HRES_NTSC : HRES_PAL)
#define VRES (Config::NTSC() ? VRES_NTSC : VRES_PAL)

#define XOFFSET ((2048 - (HRES >> 1)) << 4)
#define YOFFSET ((2048 - (VRES >> 1)) << 4)

// Start and size of buffers in 2K Word size blocks
#define FRAME_START		(0)
#define FRAME_SIZE		(((HRES * VRES) * 4) / (2048 * 4))		// 32-bit frame buffer
#define DISPLAY_START	(FRAME_START + FRAME_SIZE)
#define DISPLAY_SIZE	(((HRES * VRES) * 2) / (2048 * 4))		// 16-bit display buffer
#define ZBUFFER_START	(DISPLAY_START + DISPLAY_SIZE)
#define ZBUFFER_SIZE	(((HRES * VRES) * 4) / (2048 * 4))		// 32-bit aligned ZBuffer

#endif // __PLAT_NGPS__

#define FTOI_TRICK 1

#define STENCIL_SHADOW 0
#define OLD_FOG 0

} // namespace NxPs2

#endif // __SWITCHES_H



================================================
FILE: Code/Gfx/NGPS/NX/texture.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "texture.h"
#include "texturemem.h"
#include "dma.h"
#include "vif.h"
#include "gif.h"
#include "gs.h"
#include "group.h"
#include "scene.h"
#include "switches.h"
#include 
#include 


// K: I put this back to 0 for the moment because Conv4to32 is corrupting memory
// by writing past the end of the temporary swizzle buffer, which causes a hang 
// when switching themes in the park editor.
// Garrett: Should be fixed now.
#define SWIZZLE_IN_LOAD 1

namespace NxPs2
{

// static array of pointers to sGroup
// (so that we can pre-allocate our sGroups
// to avoid memory fragmentation)
const int vMAX_TEX_GROUPS = 32;
static sGroup* sp_temp_groups[vMAX_TEX_GROUPS];

extern TextureMemoryLayout TexMemLayout;

//------------------------------------------------------------------------------------
//		L O A D   T E X T U R E S
//------------------------------------------------------------------------------------
//
// Performs 2 tasks - it adds texture register settings to the array Textures for all
// textures in the texture file buffer (increasing NumTextures accordingly), and it
// creates a dma subroutine to upload all the textures in the file buffer to vram.
// Currently it handles all the packing (poorly!), but this will eventually be done by
// a preprocess in the tools pipeline.

bool load_textures(sScene* pScene, uint8* pTexBuffer, bool IsSkin, bool IsInstanceable, bool UsesPip, uint32 texDictOffset)
{
	Dbg_MsgAssert( pScene, ( "No scene pointer" ) );
	Dbg_MsgAssert( pTexBuffer, ( "No tex buffer pointer" ) );

	int total_num_textures=0;

#if 0
	// make sure there's no dma list running
	sceGsSyncPath(0,0);
#else
	// GJ:  Calling "sceGsSyncPath()" instead
	// of "WaitForRendering()" will cause the boardshop
	// to crash when going into the "Change Deck" menu.
	// Neither Mick nor Mike seem to know why this
	// would be the case, so it's probably worth 
	// investigating at some point
	void WaitForRendering();
	WaitForRendering();
#endif

//	Dbg_Message("loading textures from file %s\n", Filename);

	// initialise members
	pScene->NumTextures	= 0;
	pScene->NumMeshes	= 0;
	pScene->pMeshes		= NULL;
	pScene->Flags		= 0;
	pScene->pInstances	= NULL;

	// link it in
	pScene->pNext = sScene::pHead;
	sScene::pHead = pScene;

	// set flags
	if (UsesPip)
	{
		pScene->Flags |= SCENEFLAG_USESPIP;
	}
	
	// set the tex buffer pointer
	pScene->pTexBuffer = pTexBuffer;

	// set data pointer
	sScene::pData = pScene->pTexBuffer;

	// version number
	sTexture::sVersion = *(uint32 *)sScene::pData, sScene::pData+=4;
//	printf("Texture version %d\n", sTexture::sVersion);

	// get number of texture groups
	pScene->NumGroups = *(uint32 *)sScene::pData, sScene::pData+=4;

	// get total number of textures, version >= 3
	if (sTexture::sVersion >= 3)
	{
		total_num_textures = *(int *)sScene::pData, sScene::pData+=4;
	}


	// allocate storage for textures
	if (sTexture::sVersion >= 3)
	{
		// if there are no textures, it will assert in Mem::Malloc
		// so catch this assert earlier here.....
		// really there should be some more elegant handling of scenes with no textures, but they are sufficently rare.
		//Dbg_MsgAssert(total_num_textures>0,("Need at least one texture in %s (it has none)\n",Filename));
		if( total_num_textures > 0 )
		{
			pScene->pTextures = (sTexture *)Mem::Malloc(total_num_textures*sizeof(sTexture));
		}
		else
		{
			pScene->pTextures = NULL;
		}
	}
	else
	{
		pScene->pTextures = (sTexture *)Mem::Malloc(2000*sizeof(sTexture));
	}

	if ( !((total_num_textures == 0 ) || ( pScene->pTextures )) )
	{
		Dbg_Message( "couldn't allocate memory for textures" );
		return false;
	}
	
	Dbg_MsgAssert( pScene->NumGroups < vMAX_TEX_GROUPS, ( "Too many tex groups for static array of sGroup pointers (increase vMAX_TEX_GROUPS from %d)", vMAX_TEX_GROUPS ) );
	for (int i=0; iNumGroups; i++)
	{
		// pre-allocate space for texture groups...
		sp_temp_groups[i] = (sGroup *)Mem::Malloc(sizeof(sGroup));
		memset(sp_temp_groups[i],0,sizeof(sGroup));
	}

	// allocate prebuilt dma buffer for textures
	#if 0
	int		available = Mem::Available()-128;			  // -128 is needed otherwise it fails trying to allocate all free mem in a region	
	if (available > 500000)
	{
		available = 500000;	 							  // clamp to 500k if we have loads of memory.  No sure why, maybe just to speed up loading when in debug mode
	}
	#else
	// We don't want to allocate all memory as it messes with memory debugging
	// so 
	// only seem to need T*256 + G*48
	// hmm, somethings seems to be T*512 for some reason....
	// but upped it a bit to be safe
	// These only get used for peds and stuff now
	int available = total_num_textures * 512 + pScene->NumGroups * 128  + 512;  // bumped up multipliers, and added 512 byte extra
	#endif
	
	
	pScene->pTexDma = (uint8 *)Mem::Malloc(available);	  // Allocate all available memeory on current context 

	// set dma pointer to new buffer
	dma::pLoc = pScene->pTexDma;

	// test
	sGroup::VramBufferBase = 0x2BC0;

	// iterate over groups
	for (int i=0; iNumGroups; i++)
	{
		LoadTextureGroup(NULL, pScene, sp_temp_groups[i], IsSkin, IsInstanceable, UsesPip, texDictOffset);
		sp_temp_groups[i] = NULL;
	}

	//Dbg_Message( "Dma usage = %d Textures = %d, Groups = %d", (int)(dma::pLoc-pScene->pTexDma), total_num_textures, pScene->NumGroups );

	if ( (int)(dma::pLoc-pScene->pTexDma) >= available )
	{
		Dbg_Message( "Texture dictionary used more memory (%d) than available (%d)", (int)(dma::pLoc-pScene->pTexDma), available );
		return false;
	}




	// resize dma buffer so it just fits
	pScene->pTexDma = (uint8 *)Mem::ReallocateShrink(dma::pLoc-pScene->pTexDma, pScene->pTexDma);

	// success!
	return true;
}

// load textures from a file
sScene *LoadTextures(const char *Filename, bool IsSkin, bool IsInstanceable, bool UsesPip, uint32 texDictOffset, uint32 *p_size)
{
//	Dbg_Message( "LoadTextures: file = %s", Filename );
	
	sScene* pScene = (sScene*)Mem::Malloc(sizeof(sScene));
	Dbg_MsgAssert(pScene, ("couldn't allocate scene"));

	uint8* pTexBuffer = (uint8*)File::LoadAlloc( Filename, (int*) p_size );

	bool success = load_textures(pScene, pTexBuffer, IsSkin, IsInstanceable, UsesPip, texDictOffset);
	if ( !success )
	{
		Dbg_MsgAssert( 0, ( "Load textures from file %s failed", Filename ) );
	}

	return pScene;
}

// load textures from a data buffer, rather than opening a file
sScene *LoadTextures(uint32* pData, int dataSize, bool IsSkin, bool IsInstanceable, bool UsesPip, uint32 texDictOffset)
{
//	Dbg_Message( "LoadTextures: data = %p size = %d", pData, dataSize );
	
	sScene* pScene = (sScene*)Mem::Malloc(sizeof(sScene));
	Dbg_MsgAssert(pScene, ("couldn't allocate scene"));

	uint8* pTexBuffer = (uint8*)Mem::Malloc(dataSize);
	Dbg_MsgAssert(pTexBuffer, ("couldn't allocate tex buffer"));
	
	// copy over the data
	memcpy( pTexBuffer, pData, dataSize );

	bool success = load_textures(pScene, pTexBuffer, IsSkin, IsInstanceable, UsesPip, texDictOffset);
	if ( !success )
	{
		Dbg_MsgAssert( 0, ( "Load textures from data buffer failed" ) );
	}

	return pScene;
}

void LoadTextureGroup(void *pFile, sScene *pScene, sGroup *pGroup, bool IsSkin, bool IsInstanceable, bool UsesPip, uint32 texDictOffset)
{
	sTexture *pTex, *pOriginalTexture=NULL;
	uint32 TW, TH, PSM, CPSM;
	uint TBP[7], TBW[7], CBP;
	uint Width[7], Height[7], NumTexBytes[7], NumTexQWords[7], NumClutBytes, NumClutQWords, NumVramBytes[7];
	uint BitsPerTexel, BitsPerClutEntry, PaletteSize, AdjustedWidth[7], AdjustedHeight[7];
	uint PageWidth, PageHeight, AdjustedBitsPerTexel, TexCount=0;
	int i,j,k,NumTexturesThisGroup,MXL;
	uint NextTBP, LastCBP;
	uint8 *pTextureSource;
	static int Shit=0;

	Dbg_MsgAssert( pScene, ( "No pScene" ) );
	Dbg_MsgAssert( pGroup, ( "No pre-allocated pGroup was specified" ) );

	// set the scene it belongs to
	pGroup->pScene = pScene;

	pGroup->profile_color = 0x00c000;		// green = static group	 (world, cars)
	if (IsInstanceable)
	{
		pGroup->profile_color = 0x808000;		// cyan = instancable static group (cars, gameobjs)
	}
	if (IsSkin)
	{
		pGroup->profile_color = 0x000080;		// red = skin group
		if (IsInstanceable)
		{
			pGroup->profile_color = 0x800080;		// magenta = instancable skinned group 
		}
	}


	// zero the mesh pointer in case no meshes get loaded
	pGroup->pMeshes = NULL;
	pGroup->NumMeshes = 0;

	// prepare to build dma info for this group
	dma::Align(0,16);
	pGroup->pUpload[0] = pGroup->pUpload[1] = dma::pLoc;

	// get group checksum
	pGroup->Checksum = *(uint32 *)sScene::pData;
	pGroup->Checksum += texDictOffset;
	sScene::pData+=4;

	// get group flags if present
	if (sTexture::sVersion >= 2)
	{
		pGroup->flags = *(uint32 *)sScene::pData, sScene::pData+=4;
	}
	else if (pGroup->Checksum>=1000 & pGroup->Checksum<2000)
	{
		pGroup->flags = GROUPFLAG_SKY;
	}
	else
	{
		pGroup->flags = 0;
	}

	// get group priority if present
	if (sTexture::sVersion >= 4)
	{
		pGroup->Priority = *(float *)sScene::pData, sScene::pData+=4;
	}

	else
	{
		// set its priority manually
		if (UsesPip)
		{
			switch (pGroup->flags & (GROUPFLAG_SKY|GROUPFLAG_TRANSPARENT))
			{
			case 0:
				pGroup->Priority = 0;
				break;
			case GROUPFLAG_SKY:
				pGroup->Priority = -100;
				break;
			case GROUPFLAG_TRANSPARENT:
				pGroup->Priority = 7500;
				break;
			case GROUPFLAG_TRANSPARENT|GROUPFLAG_SKY:
				pGroup->Priority = -99;
				break;
			}
		}
		if (IsSkin)
		{
			pGroup->Priority = 5000+Shit;
		}
		if (IsInstanceable)
		{
			pGroup->Priority = 2500+Shit;
		}
	}

	// if it has the lowest priority, make a new head
	if (sGroup::pHead==NULL || pGroup->Priority < sGroup::pHead->Priority)
	{
		pGroup->pNext = sGroup::pHead;
		pGroup->pPrev = NULL;
		sGroup::pHead = pGroup;

		if (pGroup->pNext)
			pGroup->pNext->pPrev = pGroup;
		else
			sGroup::pTail = pGroup;
	}

	// othwerwise if it has the highest priority, make a new tail
	else if (pGroup->Priority >= sGroup::pTail->Priority)
	{
		pGroup->pPrev = sGroup::pTail;
		pGroup->pNext = NULL;
		sGroup::pTail->pNext = pGroup;
		sGroup::pTail = pGroup;
	}

	// otherwise find its immediate superior, priority-wise, in the group list
	else
	{
		sGroup *pSup;
		for (pSup=sGroup::pHead; pSup; pSup=pSup->pNext)
		{
			if (pGroup->Priority < pSup->Priority)
				break;
		}

		Dbg_MsgAssert(pSup, ("error inserting into group list\n"));

		pGroup->pNext = pSup;
		pGroup->pPrev = pSup->pPrev;
		pSup->pPrev->pNext = pGroup;
		pSup->pPrev = pGroup;
	}

	// get number of textures
	NumTexturesThisGroup = *(uint32 *)sScene::pData, sScene::pData+=4;

	// set vram usage for this group
	pGroup->VramStart = sGroup::VramBufferBase;
	pGroup->VramEnd   = sGroup::VramBufferBase+0x0A20;

	// advance buffer for next group
	sGroup::VramBufferBase ^= 0x1E20;

	// initialise base pointers
	NextTBP = pGroup->VramStart;
	LastCBP = pGroup->VramEnd;

	// loop over textures
	for (i=0,pTex=pScene->pTextures+pScene->NumTextures; i= 5)
		{
			pTex->Flags = *(uint32 *)sScene::pData, sScene::pData+=4;
		}

		// get texture checksum
		pTex->Checksum = *(uint32 *)sScene::pData, sScene::pData+=4;

		// set group checksum
		pTex->GroupChecksum = pGroup->Checksum;

		// get dimensions
		TW = *(uint32 *)sScene::pData, sScene::pData+=4;
		TH = *(uint32 *)sScene::pData, sScene::pData+=4;

		// get pixel storage modes
		PSM  = *(uint32 *)sScene::pData, sScene::pData+=4;	// for texels
		CPSM = *(uint32 *)sScene::pData, sScene::pData+=4;	// for clut

		// get maximum mipmap level
		MXL  = *(uint32 *)sScene::pData, sScene::pData+=4;

		// clear buffer pointers
		pTex->mp_texture_buffer = NULL;
		pTex->mp_clut_buffer = NULL;
		pTex->m_texture_buffer_size = 0;
		pTex->m_clut_buffer_size = 0;

		// bail if it's a non-texture
		if (TW == 0xFFFFFFFF)
		{
			printf("non-texture at number %d\n", i);
			// there won't be any more data for this texture
			continue;
		}

		// detect duplicated texture, signalled by MXL of -1
		if (MXL<0)
		{
			// find the original texture
			pOriginalTexture = ResolveTextureChecksum(pTex->Checksum);

			// adjust MXL
			MXL &= 0x7FFFFFFF;

			// signal duplicate
			pTex->Flags &= ~TEXFLAG_ORIGINAL;
		}
		else
		{
			// not a duplicate, must be the original
			pTex->Flags |= TEXFLAG_ORIGINAL;
		}

		// quadword-align (original only)
		if (pTex->Flags & TEXFLAG_ORIGINAL)
		{
			sScene::pData = (uint8 *)(((uint)(sScene::pData+15)) & 0xFFFFFFF0);
		}

		// bits per texel and palette size
		switch (PSM)
		{
		case PSMCT32:
			BitsPerTexel = 32;
			PaletteSize  = 0;
			break;
		case PSMCT24:
			BitsPerTexel = 24;
			PaletteSize  = 0;
			break;
		case PSMCT16:
			BitsPerTexel = 16;
			PaletteSize  = 0;
			break;
		case PSMT8:
			BitsPerTexel = 8;
			PaletteSize  = 256;
			break;
		case PSMT4:
			BitsPerTexel = 4;
			PaletteSize  = 16;
			break;
		default:
			printf("Unknown PSM %d at index %d in texture file\n", PSM, i);
			exit(1);
		}

		// adjust bits per texel for 24-bit
		AdjustedBitsPerTexel = BitsPerTexel;
		if (BitsPerTexel == 24)
		{
			AdjustedBitsPerTexel = 32;
		}

		// bits per clut entry
		if (BitsPerTexel < 16)
			switch (CPSM)
			{
			case PSMCT32:
				BitsPerClutEntry = 32;
				break;
			case PSMCT16:
				BitsPerClutEntry = 16;
				break;
			default:
				printf("Unknown CPSM %d in texture file\n", PSM);
				exit(1);
			}
		else
			BitsPerClutEntry = 0;

		// rearrange original 256-colour cluts according to requirements of CSM1
		if (PSM==PSMT8 && (pTex->Flags & TEXFLAG_ORIGINAL))
		{
			uint32 temp32;
			uint16 temp16;
			for (j=0; j<256; j+=32)
				for (k=0; k<8; k++)
					if (CPSM==PSMCT32)
					{
						temp32 = ((uint32 *)sScene::pData)[j+k+8];
						((uint32 *)sScene::pData)[j+k+8] = ((uint32 *)sScene::pData)[j+k+16];
						((uint32 *)sScene::pData)[j+k+16] = temp32;
					}
					else
					{
						temp16 = ((uint16 *)sScene::pData)[j+k+8];
						((uint16 *)sScene::pData)[j+k+8] = ((uint16 *)sScene::pData)[j+k+16];
						((uint16 *)sScene::pData)[j+k+16] = temp16;
					}
		}

		// calculate texture dimensions
		for (j=0; j<=MXL; j++)
		{
			Width[j]  = (TW>=0 ? 1<> j;
			Height[j] = (TH>=0 ? 1<> j;
			TBW[j] = (Width[j]+63) >> 6;
			if (BitsPerTexel<16 && TBW[j]<2)
				TBW[j] = 2;
		}

		// get page size
		switch (PSM)
		{
		case PSMCT32:
		case PSMCT24:
			PageWidth  = 64;
			PageHeight = 32;
			break;
		case PSMCT16:
			PageWidth  = 64;
			PageHeight = 64;
			break;
		case PSMT8:
			PageWidth  = 128;
			PageHeight = 64;
			break;
		case PSMT4:
			PageWidth  = 128;
			PageHeight = 128;
			break;
		default:
			printf("Unknown PSM %d at index %d in texture file\n", PSM, i);
			exit(1);
		}

		// calculate adjusted dimensions based on vram page size
		for (j=0; j<=MXL; j++)
		{
			AdjustedWidth[j]  = Width[j];
			AdjustedHeight[j] = Height[j];

			if (AdjustedWidth[j] < PageWidth && AdjustedHeight[j] > PageHeight)
			{
				AdjustedWidth[j] = PageWidth;
			}

			if (AdjustedWidth[j] > PageWidth && AdjustedHeight[j] < PageHeight)
			{
				AdjustedHeight[j] = PageHeight;
			}

			if (TBW[j]<<6 > AdjustedWidth[j])
			{
				AdjustedWidth[j] = TBW[j]<<6;
			}
		}

		// calculate sizes within file
		NumClutBytes  = PaletteSize * BitsPerClutEntry >> 3;
		NumClutQWords = NumClutBytes >> 4;
		for (j=0; j<=MXL; j++)
		{
			NumTexBytes[j]  = Width[j] * Height[j] * BitsPerTexel >> 3;
			NumVramBytes[j] = AdjustedWidth[j] * AdjustedHeight[j] * AdjustedBitsPerTexel >> 3;
			NumTexQWords[j] = (NumTexBytes[j]+15) >> 4;
		}

		// calculate TBP
		TBP[0] = NextTBP;
		for (j=1; j<=MXL; j++)
			TBP[j] = (((TBP[j-1]<<8)+NumVramBytes[j-1]+0x1FFF) & 0xFFFFE000) >> 8;

		// calculate CBP
		if (BitsPerTexel >= 16)
			CBP = LastCBP;
		else if (BitsPerTexel == 4)
			CBP = LastCBP - 1;
		else
			CBP = LastCBP - (CPSM==PSMCT32 ? 4 : 2);

		// calculate next TBP
		NextTBP = (((TBP[MXL]<<8)+NumVramBytes[MXL]+0x1FFF) & 0xFFFFE000) >> 8;

		// bail if VRAM would become over-packed
		if (NextTBP > CBP)
		{
			printf("no room for texture %d\n", i);
			if (pTex->Flags & TEXFLAG_ORIGINAL)
			{
				sScene::pData += NumClutBytes;
				for (j=0; j<=MXL; j++)
				{
					sScene::pData = (uint8 *)(((uint)(sScene::pData+NumTexBytes[j]+15)) & 0xFFFFFFF0);
				}
			}
			continue;
		}

		// add clut upload to dma list if necessary
		if (BitsPerTexel < 16)
		{
			// source
			if (pTex->Flags & TEXFLAG_ORIGINAL)
			{
				pTextureSource = sScene::pData;
			}
			else
			{
				pTextureSource = pOriginalTexture->mp_clut_buffer;
			}

			// save buffer pointer
			pTex->mp_clut_buffer = pTextureSource;
			pTex->m_clut_buffer_size = NumClutBytes;

			// add dma
			dma::BeginTag(dma::cnt,0);
			vif::NOP();
			vif::NOP();
			gif::Tag2(gs::A_D,1,PACKED,0,0,0,4);
			gs::Reg2(gs::BITBLTBUF,PackBITBLTBUF(0,0,0,CBP,1,CPSM));
			gs::Reg2(gs::TRXPOS,	PackTRXPOS(0,0,0,0,0));
			if (PSM==PSMT4)
				gs::Reg2(gs::TRXREG,PackTRXREG(8,2));
			else
				gs::Reg2(gs::TRXREG,PackTRXREG(16,16));
			gs::Reg2(gs::TRXDIR,	0);
			gif::Tag2(0,0,IMAGE,0,0,0,NumClutQWords);
			dma::EndTag();
			dma::Tag(dma::ref, NumClutQWords, (uint)pTextureSource);
			vif::NOP();
			vif::NOP();
		}

		if (pTex->Flags & TEXFLAG_ORIGINAL)
		{
			// step past clut data, and quadword-align
			sScene::pData = (uint8 *)(((uint)(sScene::pData+NumClutBytes+15)) & 0xFFFFFFF0);

			// save buffer pointer
			pTex->mp_texture_buffer = sScene::pData;
		}
		else
		{
			pTex->mp_texture_buffer = pOriginalTexture->mp_texture_buffer;
		}
		pTextureSource = pTex->mp_texture_buffer;


		// offset to improve caching
		for (j=0; j<=MXL; j++)
		{
			if (TexCount & 1)
			{
				if ((BitsPerTexel==32 || BitsPerTexel==8)
						&& (Width[j]  <= (PageWidth>>1))
						&& (Height[j] <= PageHeight)
					||
					(BitsPerTexel==16 || BitsPerTexel==4)
						&& (Width[j]  <=  PageWidth)
						&& (Height[j] <= (PageHeight>>1)))
				{
					TBP[j] += 16;
					//printf("better caching!\n");
				}
			}
			TexCount++;
		}

#if SWIZZLE_IN_LOAD
		bool swizzleTex = BitsPerTexel <= 8;
#endif // SWIZZLE_IN_LOAD

		// loop over mipmaps
		for (j=0; j<=MXL; j++)
		{
#if SWIZZLE_IN_LOAD
			int swizzle_tbw = TBW[j];
			int swizzle_width = Width[j];
			int swizzle_height = Height[j];
			uint32 swizzlePSM = PSM;
			bool swizzleMip = false;

			if (swizzleTex)
			{
				switch (PSM)
				{
				case PSMT8:
					{
						if (TexMemLayout.CanConv8to32(Width[j], Height[j]))
						{
							if (pTex->Flags & TEXFLAG_ORIGINAL)
							{
								Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
								uint8 *pTextureSwizzle = (uint8 *) Mem::Malloc(Width[j] * Height[j]);
								Mem::Manager::sHandle().PopContext();

								TexMemLayout.Conv8to32(Width[j], Height[j], pTextureSource, pTextureSwizzle);
								memcpy(pTextureSource, pTextureSwizzle, Width[j] * Height[j]);

								Mem::Free(pTextureSwizzle);
							}

							// Change size to 32-bit version
							swizzle_width /= 2;
							swizzle_height /= 2;

							// Recalculate Buffer Width since 8-bit version can be artificially high
							swizzle_tbw = swizzle_width / 64;
							if (swizzle_tbw == 0)
								swizzle_tbw = 1;

							swizzleMip = true;
							swizzlePSM=PSMCT32;	// swizzle
							pTex->Flags |= TEXFLAG_MIP_SWIZZLE_32BIT(j);
						}
						break;
					}
				case PSMT4:
					{
						if (TexMemLayout.CanConv4to32(Width[j], Height[j]))
						{
							//Dbg_Message("Swizzling texture checksum %x", pTex->Checksum);

							if (pTex->Flags & TEXFLAG_ORIGINAL)
							{
								Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
								uint8 *pTextureSwizzle = (uint8 *) Mem::Malloc(Width[j] * Height[j] / 2);
								Mem::Manager::sHandle().PopContext();

								TexMemLayout.Conv4to32(Width[j], Height[j], pTextureSource, pTextureSwizzle);
								memcpy(pTextureSource, pTextureSwizzle, Width[j] * Height[j] / 2);

								Mem::Free(pTextureSwizzle);
							}

							// Change size to 32-bit version
							swizzle_width /= 2;
							swizzle_height /= 4;

							// Recalculate Buffer Width since 4-bit version can be artificially high
							swizzle_tbw = swizzle_width / 64;
							if (swizzle_tbw == 0)
								swizzle_tbw = 1;

							swizzleMip = true;
							swizzlePSM=PSMCT32;	// swizzle
							pTex->Flags |= TEXFLAG_MIP_SWIZZLE_32BIT(j);
						}
						else if (TexMemLayout.CanConv4to16(Width[j], Height[j]))
						{
							if (pTex->Flags & TEXFLAG_ORIGINAL)
							{
								Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
								uint8 *pTextureSwizzle = (uint8 *) Mem::Malloc(Width[j] * Height[j] / 2);
								Mem::Manager::sHandle().PopContext();

								TexMemLayout.Conv4to16(Width[j], Height[j], pTextureSource, pTextureSwizzle);
								memcpy(pTextureSource, pTextureSwizzle, Width[j] * Height[j] / 2);

								Mem::Free(pTextureSwizzle);
							}

							// Change size to 16-bit version
							swizzle_width /= 2;
							swizzle_height /= 2;

							// Recalculate Buffer Width since 4-bit version can be artificially high
							swizzle_tbw = swizzle_width / 64;
							if (swizzle_tbw == 0)
								swizzle_tbw = 1;

							swizzleMip = true;
							swizzlePSM=PSMCT16;	// swizzle
							pTex->Flags |= TEXFLAG_MIP_SWIZZLE_16BIT(j);
						}
						break;
					}

				default:
					break;
				}
			}
#endif // SWIZZLE_IN_LOAD


			// add texture upload to dma list for this mipmap level
			dma::BeginTag(dma::cnt,0);
			vif::NOP();
			vif::NOP();
			gif::Tag2(gs::A_D,1,PACKED,0,0,0,4);
#if SWIZZLE_IN_LOAD
			gs::Reg2(gs::BITBLTBUF,PackBITBLTBUF(0,0,0,TBP[j],swizzle_tbw,swizzlePSM));
			gs::Reg2(gs::TRXPOS,	PackTRXPOS(0,0,0,0,0));
			gs::Reg2(gs::TRXREG,	PackTRXREG(swizzle_width,swizzle_height));
#else
			gs::Reg2(gs::BITBLTBUF,PackBITBLTBUF(0,0,0,TBP[j],TBW[j],PSM));
			gs::Reg2(gs::TRXPOS,	PackTRXPOS(0,0,0,0,0));
			gs::Reg2(gs::TRXREG,	PackTRXREG(Width[j],Height[j]));
#endif // SWIZZLE_IN_LOAD
			gs::Reg2(gs::TRXDIR,	0);
			
			if (j==0)
			{
				pTex->mp_dma = dma::pLoc;
				pTex->m_quad_words = NumTexQWords[j];
				pTex->m_render_count = 0;
			}
			
			
			gif::Tag2(0, 0, IMAGE, 0, 0, 0, NumTexQWords[j]);  			// 0..3 words, NumTexQWords is in the lower 15 bits of the first word [0]
			dma::EndTag();									   			// Just Aligns upo to QW boundry
			dma::Tag(dma::ref, NumTexQWords[j], (uint)pTextureSource);  // 0..1 (after alignment) NumTexQWords goes in word 1
			vif::NOP();
			vif::NOP();

			// step past tex data for this level
			pTextureSource += NumTexBytes[j];
		}

		pTex->m_texture_buffer_size = (uint32) pTextureSource - (uint32) pTex->mp_texture_buffer;

		if (pTex->Flags & TEXFLAG_ORIGINAL)
		{
			sScene::pData = pTextureSource;
		}

		// make entry in texture table
		pTex->MXL        = MXL;
		pTex->RegTEX0    = PackTEX0(TBP[0],TBW[0],PSM,TW,TH,(PSM!=PSMCT24),MODULATE,CBP,CPSM,0,0,1);
		pTex->RegMIPTBP1 = PackMIPTBP1(TBP[1],TBW[1],TBP[2],TBW[2],TBP[3],TBW[3]);
		pTex->RegMIPTBP2 = PackMIPTBP2(TBP[4],TBW[4],TBP[5],TBW[5],TBP[6],TBW[6]);

		// advance texture base pointer to 1st page after texture
		// and reduce clut base pointer to 1st page before clut (if there was one)
		TBP[0] = NextTBP;
		LastCBP = CBP;

	}

	// add to total for the scene
	pScene->NumTextures += NumTexturesThisGroup;

	// texflush so we can use textures
	dma::BeginTag(dma::end, 0);
	vif::NOP();
	vif::NOP();
	gif::Tag2(gs::A_D,1,PACKED,0,0,1,1);
	gs::Reg2(gs::TEXFLUSH,	0);
	dma::EndTag();

	if (UsesPip==false)
	{
		Shit++;
	}

}




void DeleteTextures(sScene *pScene)
{
	sScene *pPrev;
	sGroup *pGroup, *pDead;

	// deallocate assets
	Mem::Free(pScene->pTexBuffer);
	Mem::Free(pScene->pTexDma);
	if( pScene->pTextures != NULL )
	{
		Mem::Free(pScene->pTextures);
	}

	// deallocate groups
	pGroup = sGroup::pHead;
	while (pGroup)
	{
		if (pGroup->pScene == pScene)
		{
			if (pGroup == sGroup::pHead)
			{
				pGroup->pNext->pPrev = NULL;
				sGroup::pHead = pGroup->pNext;
			}
			else if (pGroup == sGroup::pTail)
			{
				pGroup->pPrev->pNext = NULL;
				sGroup::pTail = pGroup->pPrev;
			}
			else
			{
				pGroup->pNext->pPrev = pGroup->pPrev;
				pGroup->pPrev->pNext = pGroup->pNext;
			}
			pDead = pGroup;
			pGroup = pGroup->pNext;
			Mem::Free(pDead);
		}
		else
			pGroup = pGroup->pNext;
	}

	// unlink
	if (pScene==sScene::pHead)
		sScene::pHead = pScene->pNext;
	else
	{
		for (pPrev=sScene::pHead; pPrev; pPrev=pPrev->pNext)
			if (pPrev->pNext == pScene)
				break;
		Dbg_MsgAssert(pPrev, ("couldn't find scene to delete\n"));

		pPrev->pNext = pScene->pNext;
	}

	// free
	Mem::Free(pScene);
}





sTexture *ResolveTextureChecksum(uint32 TextureChecksum)
{
	sScene *pScene;
	sTexture *pTex=NULL;
	int i;

	for (pScene=sScene::pHead; pScene; pScene=pScene->pNext)
	{
		for (i=0,pTex=pScene->pTextures; iNumTextures; i++,pTex++)
			if ((pTex->Checksum == TextureChecksum) && (pTex->Flags & TEXFLAG_ORIGINAL))
				return pTex;
	}

	return NULL;
}



sTexture *ResolveTextureChecksum(uint32 TextureChecksum, uint32 GroupChecksum)
{
	sScene *pScene;
	sTexture *pTex=NULL;
	int i;

	for (pScene=sScene::pHead; pScene; pScene=pScene->pNext)
	{
		for (i=0,pTex=pScene->pTextures; iNumTextures; i++,pTex++)
			if ((pTex->Checksum == TextureChecksum) && (pTex->GroupChecksum == GroupChecksum))
				return pTex;
	}

	return NULL;
}



uint32 sTexture::sVersion;


/////////////////////////////////////////////////////////////////////////////
//
// The following Get() calls are very inefficient.  They should only be
// used when necessary (i.e. initialization).
//
uint16	sTexture::GetWidth() const
{
	sceGsTex0 tex0 = *((sceGsTex0 *) &RegTEX0);
	uint16 TW = tex0.TW;
	//uint16 TW = (uint16) ((RegTEX0 >> 26) & M04);

	return 1 << TW;
}

uint16	sTexture::GetHeight() const
{
	sceGsTex0 tex0 = *((sceGsTex0 *) &RegTEX0);
	uint16 TH = tex0.TH;
	//uint16 TH = (uint16) ((RegTEX0 >> 30) & M04);

	return 1 << TH;
}

uint8	sTexture::GetBitdepth() const
{
	sceGsTex0 tex0 = *((sceGsTex0 *) &RegTEX0);
	uint PSM = tex0.PSM;

	switch (PSM)
	{
	case PSMCT32:
		return 32;
		break;
	case PSMCT24:
		return 24;
		break;
	case PSMCT16:
		return 16;
		break;
	case PSMT8:
		return 8;
		break;
	case PSMT4:
		return 4;
		break;
	default:
		return 0;
	}
}

uint8	sTexture::GetClutBitdepth() const
{
	sceGsTex0 tex0 = *((sceGsTex0 *) &RegTEX0);
	uint CPSM = tex0.CPSM;

	switch (CPSM)
	{
	case PSMCT32:
		return 32;
		break;
	case PSMCT24:
		return 24;
		break;
	case PSMCT16:
		return 16;
		break;
	default:
		return 0;
	}
}

uint8	sTexture::GetNumMipmaps() const
{
	return MXL + 1;
}

bool	sTexture::HasSwizzleMip() const
{
	for (uint mip_idx = 0; mip_idx <= MXL; mip_idx++)
	{
		if (Flags & TEXFLAG_MIP_SWIZZLE(mip_idx))
		{
			return true;
		}
	}

	return false;
}

void	sTexture::ReplaceTextureData(uint8 *p_texture_data)
{
	uint mip_width, mip_height, mip_NumTexBytes;

	int bitdepth = GetBitdepth();
	int width = GetWidth();
	int height = GetHeight();

	uint8 *pTextureSource = mp_texture_buffer;

	// calculate texture dimensions
	for (uint j=0; j<=MXL; j++)
	{
		mip_width  = width >> j;
		mip_height = height >> j;
		mip_NumTexBytes  = mip_width * mip_height * bitdepth >> 3;

		// Do the actual copies, swizzling if necessary
		if (Flags & TEXFLAG_MIP_SWIZZLE_16BIT(j))
		{
			switch (bitdepth)
			{
			case 4:
				TexMemLayout.Conv4to16(mip_width, mip_height, p_texture_data, pTextureSource);
				break;
			case 8:
			default:
				Dbg_MsgAssert(0, ("Can't swizzle a texture of bitdepth %d to 16-bit", bitdepth));
				break;
			}
		}
		else if (Flags & TEXFLAG_MIP_SWIZZLE_32BIT(j))
		{
			switch (bitdepth)
			{
			case 8:
				TexMemLayout.Conv8to32(mip_width, mip_height, p_texture_data, pTextureSource);
				break;
			case 4:
				TexMemLayout.Conv4to32(mip_width, mip_height, p_texture_data, pTextureSource);
				break;
			default:
				Dbg_MsgAssert(0, ("Can't swizzle a texture of bitdepth %d to 32-bit", bitdepth));
				break;
			}
		}
		else
		{
			memcpy(pTextureSource, p_texture_data, mip_NumTexBytes);
		}

		// step past tex data for this level
		pTextureSource += mip_NumTexBytes;
		p_texture_data += mip_NumTexBytes;
	}

}

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/texture.h
================================================
#ifndef __TEXTURE_H
#define __TEXTURE_H


namespace NxPs2
{


#define TEXTURE_BUFFER_SIZE 4194304

////////////////////////
// Flags
#define TEXFLAG_ORIGINAL			(1<<0)
#define TEXFLAG_ANIMATED			(1<<1)
#define TEXFLAG_MIP0_SWIZZLE_16BIT	(1<<18)			// The swizzle bit flags go all the way to the end of the Flag variable
#define TEXFLAG_MIP1_SWIZZLE_16BIT	(1<<19)
#define TEXFLAG_MIP2_SWIZZLE_16BIT	(1<<20)
#define TEXFLAG_MIP3_SWIZZLE_16BIT	(1<<21)
#define TEXFLAG_MIP4_SWIZZLE_16BIT	(1<<22)
#define TEXFLAG_MIP5_SWIZZLE_16BIT	(1<<23)
#define TEXFLAG_MIP6_SWIZZLE_16BIT	(1<<24)
#define TEXFLAG_MIP0_SWIZZLE_32BIT	(1<<25)
#define TEXFLAG_MIP1_SWIZZLE_32BIT	(1<<26)
#define TEXFLAG_MIP2_SWIZZLE_32BIT	(1<<27)
#define TEXFLAG_MIP3_SWIZZLE_32BIT	(1<<28)
#define TEXFLAG_MIP4_SWIZZLE_32BIT	(1<<29)
#define TEXFLAG_MIP5_SWIZZLE_32BIT	(1<<30)
#define TEXFLAG_MIP6_SWIZZLE_32BIT	(1<<31)

#define TEXFLAG_MIP0_SWIZZLE		(TEXFLAG_MIP0_SWIZZLE_16BIT | TEXFLAG_MIP0_SWIZZLE_32BIT)

#define TEXFLAG_MIP_SWIZZLE_16BIT(m)	(TEXFLAG_MIP0_SWIZZLE_16BIT << m)
#define TEXFLAG_MIP_SWIZZLE_32BIT(m)	(TEXFLAG_MIP0_SWIZZLE_32BIT << m)
#define TEXFLAG_MIP_SWIZZLE(m)			(TEXFLAG_MIP0_SWIZZLE << m)

struct sGroup;

struct sTexture
{
	uint16			GetWidth() const;
	uint16			GetHeight() const;
	uint8			GetBitdepth() const;
	uint8			GetClutBitdepth() const;
	uint8			GetNumMipmaps() const;

	uint8 *			GetTextureBuffer() const { return mp_texture_buffer; }
	uint32			GetTextureBufferSize() const { return m_texture_buffer_size; }
	uint8 *			GetClutBuffer() const { return mp_clut_buffer; }
	uint32			GetClutBufferSize() const { return m_clut_buffer_size; }

	bool			HasSwizzleMip() const;
	void			ReplaceTextureData(uint8 *p_texture_data);

	uint32 Checksum, GroupChecksum, MXL;
	uint64 RegTEX0, RegMIPTBP1, RegMIPTBP2;

	uint8  *mp_texture_buffer;
	uint32 m_texture_buffer_size;
	uint8  *mp_clut_buffer;
	uint32 m_clut_buffer_size;

	//bool   Original;
	uint32 Flags;

	static uint32 sVersion;
	
	
	uint8		*	mp_dma;	 			// pointer to DMA list if we want to patch up this texture
	uint32		m_render_count;			// count of how many times this has been rendered this frame
	uint32 		m_quad_words;			// number of quad words to upload in first pass
};



struct sScene *LoadTextures(uint32* pData, int dataSize, bool IsSkin=false, bool IsInstanceable=false, bool UsesPip=false, uint32 texDictOffset=0);
struct sScene *LoadTextures(const char *Filename, bool IsSkin=false, bool IsInstanceable=false, bool UsesPip=false, uint32 texDictOffset=0, uint32 *p_size = NULL);
void LoadTextureGroup(void *pFile, sScene *pScene, sGroup *pGroup, bool IsSkin, bool IsInstanceable, bool UsesPip, uint32 texDictOffset);
void DeleteTextures(sScene *pScene);
sTexture *ResolveTextureChecksum(uint32 TextureChecksum);
sTexture *ResolveTextureChecksum(uint32 TextureChecksum, uint32 GroupChecksum);


} // namespace NxPs2

#endif // __TEXTURE_H



================================================
FILE: Code/Gfx/NGPS/NX/texturemem.cpp
================================================
#include 
#include "texture.h"
#include "texturemem.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gif.h"
#include "gs.h"

#include 

#define min(x, y) (((x) > (y))? (y): (x))
#define max(x, y) (((x) < (y))? (y): (x))

namespace NxPs2
{

const TextureMemoryLayout::PixelFormatLayout TextureMemoryLayout::pixelFormat[NUM_PIXEL_TYPES] = {
    // PIXEL_32
    {   { 64, 32 },     // page size
        { 8, 8 },       // block size
        true       },   // adjacentBlockWidth
    // PIXEL_16
    {   { 64, 64 },     // page size
        { 16, 8 },      // block size
        false      },   // adjacentBlockWidth
    // PIXEL_8
    {   { 128, 64 },    // page size
        { 16, 16 },     // block size
        true       },   // adjacentBlockWidth
    // PIXEL_4
    {   { 128, 128 },   // page size
        { 32, 16 },     // block size
        false      },   // adjacentBlockWidth
};

const int TextureMemoryLayout::blockArrangement[4][8] = {
    {  0,  1,  4,  5, 16, 17, 20, 21 },
    {  2,  3,  6,  7, 18, 19, 22, 23 },
    {  8,  9, 12, 13, 24, 25, 28, 29 },
    { 10, 11, 14, 15, 26, 27, 30, 31 }
};

//----------------------------------------------------------------------------
//
TextureMemoryLayout::TextureMemoryLayout()
{
    for (int i = PIXEL_32; i < NUM_PIXEL_TYPES; i++)
        initMinSize(minSize[i], PixelType(i));
}

//----------------------------------------------------------------------------
//
void
TextureMemoryLayout::initMinSize(MinSizeArray& mArray, PixelType pType)
{
    int tw, th;

    for (th = 0; th < MAX_PIXEL_SIZE_BITS; th++) {
        for (tw = 0; tw < MAX_PIXEL_SIZE_BITS; tw++) {
            mArray[tw][th].width = 1 << tw;
            mArray[tw][th].height = 1 << th;

            setMinSize(mArray[tw][th].width, mArray[tw][th].height, pType);
        }
    }
}

//----------------------------------------------------------------------------
//
void
TextureMemoryLayout::setMinSize(int& width, int& height, PixelType pType)
{
    const PixelFormatLayout &pf = pixelFormat[pType];

    // All widths and heights are powers of 2

    // If texture dimension is bigger than a page dimension, then
    // make sure whole pages are allocated and exit.
    if (width > pf.pageSize.width) {
        height = Mth::Max(height, pf.pageSize.height);
        return;
    } else if (height > pf.pageSize.height) {
        width = Mth::Max(width, pf.pageSize.width);
        return;
    }

    // Make sure width and height are at least block size
    if (width < pf.blockSize.width)
        width = pf.blockSize.width;
    if (height < pf.blockSize.height)
        height = pf.blockSize.height;

#if 0
    // Now we are only dealing with sub-pages.  Make sure
    // sizes don't go beyond allowable aspect ratios
    
    TexSize twoBlock;

    if (pf.adjacentBlockWidth) {    // width
        twoBlock.width = pf.blockSize.width * 2;
        twoBlock.height = pf.blockSize.height;
    } else {                        // height
        twoBlock.width = pf.blockSize.width;
        twoBlock.height = pf.blockSize.height * 2;
    }
#endif

    // It just happens that, given a sub-page,
    // the height cannot be bigger than the
    // width and the width cannot be more than
    // double the height
    if (height > width)
        width = height;
    if (width > (height * 2))
        height = width / 2;
}

//----------------------------------------------------------------------------
//
TextureMemoryLayout::PixelType
TextureMemoryLayout::modeToPixelType(int pixMode)
{
    switch(pixMode) {
    case PSMCT32:
    case PSMCT24:
        return PIXEL_32;

    case PSMCT16:
        return PIXEL_16;

    case PSMT8:
        return PIXEL_8;

    case PSMT4:
        return PIXEL_4;

    default:
        Dbg_MsgAssert(0, ("TextureMemoryLayout: PixelMode %d not supported.", pixMode));
        return PIXEL_INVALID;
    }
}

//----------------------------------------------------------------------------
//
void
TextureMemoryLayout::getMinSize(int& width, int& height, int pixMode)
{
    PixelType pType = modeToPixelType(pixMode);

    // Now get the minimum size
    int tw = numBits(width) - 1;
    int th = numBits(height) - 1;

    width = minSize[pType][tw][th].width;
    height = minSize[pType][tw][th].height;
}

//----------------------------------------------------------------------------
// Returns size in blocks.  Must calculate the minimum width and height first.
int
TextureMemoryLayout::getImageSize(int width, int height, int pixMode)
{
    PixelType pType = modeToPixelType(pixMode);
    const PixelFormatLayout &pf = pixelFormat[pType];
    int bw, bh;

    bw = width / pf.blockSize.width;
    bh = height / pf.blockSize.height;

    return bw * bh;
}

//----------------------------------------------------------------------------
//
int
TextureMemoryLayout::getBlockNumber(int blockX, int blockY, int pixMode)
{

    switch (pixMode) {
    case PSMCT32:
    case PSMCT24:
    case PSMT8:
        return blockArrangement[blockY][blockX];

    case PSMCT16:
    case PSMT4:
        return blockArrangement[blockX][blockY];

    default:
        Dbg_MsgAssert(0, ("TextureMemoryLayout: getBlockNumber %d not supported.", pixMode));
        return -1;
    }
}

//----------------------------------------------------------------------------
//
void
TextureMemoryLayout::getPosFromBlockNumber(int& posX, int& posY, int blockNumber, int pixMode)
{
    int blockRow = -1, blockCol = -1;

    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 8; j++)
            if (blockArrangement[i][j] == blockNumber) {
                blockRow = i;
                blockCol = j;
                break;
            }

    if (blockRow < 0)
        Dbg_MsgAssert(0, ("TextureMemoryLayout: getPosFromBlockNumber"));

    PixelType pType = modeToPixelType(pixMode);
    const PixelFormatLayout &pf = pixelFormat[pType];

    switch (pixMode) {
    case PSMCT32:
    case PSMCT24:
    case PSMT8:
        posX = blockCol * pf.blockSize.width;
        posY = blockRow * pf.blockSize.height;
        break;

    case PSMCT16:
    case PSMT4:
        posX = blockRow * pf.blockSize.width;
        posY = blockCol * pf.blockSize.height;
        break;

    default:
        Dbg_MsgAssert(0, ("TextureMemoryLayout: getPosFromBlockNumber %d not supported.", pixMode));
    }
}

//----------------------------------------------------------------------------
//
int
TextureMemoryLayout::getBlockOffset(int bufferWidth, int posX, int posY, int pixMode)
{
    int horizPages, vertPages;
    int numPages = 0;
    int blockNumber = 0;
    
    PixelType pType = modeToPixelType(pixMode);
    const PixelFormatLayout &pf = pixelFormat[pType];

    // First figure out how many pages are above
    vertPages = posY / pf.pageSize.height;
    if (vertPages) {
        horizPages = bufferWidth / pf.pageSize.width;
        posY -= vertPages * pf.pageSize.height;
        numPages += vertPages * horizPages;
    }

    // Now how many are to the left
    horizPages = posX / pf.pageSize.width;
    if (horizPages) {
        posX -= horizPages * pf.pageSize.width;
        numPages += horizPages;
    }

    // Now figure out where we are within the page
    int horizBlocks, vertBlocks;
    horizBlocks = posX / pf.blockSize.width;
    vertBlocks = posY / pf.blockSize.height;
    blockNumber = getBlockNumber(horizBlocks, vertBlocks, pixMode);

    return (numPages * 32) + blockNumber;
}

//----------------------------------------------------------------------------
//
void
TextureMemoryLayout::getPosFromBlockOffset(int& posX, int& posY, int blockOffset, int bufferWidth, int pixMode)
{
    int horizPages, vertPages;
    
    PixelType pType = modeToPixelType(pixMode);
    const PixelFormatLayout &pf = pixelFormat[pType];

    posX = posY = 0;

    // First figure out how many pages are above
    int horizPageWidth = bufferWidth / pf.pageSize.width;
    if (horizPageWidth) {
        vertPages = blockOffset / (horizPageWidth * 32);
        if (vertPages) {
            posY += vertPages * pf.pageSize.height;
            blockOffset -= vertPages * horizPageWidth * 32;
        }
    }

    // Now how many are to the left
    horizPages = blockOffset / 32;
    if (horizPages) {
        posX += horizPages * pf.pageSize.width;
        blockOffset -= horizPages * 32;
    }

    // Now figure out where we are within the page
    int blockPosX, blockPosY;
    getPosFromBlockNumber(blockPosX, blockPosY, blockOffset, pixMode);
    
    posX += blockPosX;
    posY += blockPosY;
}

//----------------------------------------------------------------------------
//
bool
TextureMemoryLayout::isPageSize(int width, int height, int pixMode)
{
    PixelType pType = modeToPixelType(pixMode);
    const PixelFormatLayout &pf = pixelFormat[pType];

    return !(width % pf.pageSize.width) && !(height % pf.pageSize.height);
}

//----------------------------------------------------------------------------
//
bool
TextureMemoryLayout::isBlockSize(int width, int height, int pixMode)
{
    PixelType pType = modeToPixelType(pixMode);
    const PixelFormatLayout &pf = pixelFormat[pType];

    return !(width % pf.blockSize.width) && !(height % pf.blockSize.height);
}

//----------------------------------------------------------------------------
//
bool
TextureMemoryLayout::isMinPageSize(int width, int height, int pixMode)
{
    PixelType pType = modeToPixelType(pixMode);
    const PixelFormatLayout &pf = pixelFormat[pType];

    getMinSize(width, height, pixMode);

    return (width >= pf.pageSize.width) && (height >= pf.pageSize.height);
}

//----------------------------------------------------------------------------
//
bool
TextureMemoryLayout::isMinBlockSize(int width, int height, int pixMode)
{
    PixelType pType = modeToPixelType(pixMode);
    const PixelFormatLayout &pf = pixelFormat[pType];

    getMinSize(width, height, pixMode);

    return (width >= pf.blockSize.width) && (height >= pf.blockSize.height);
}

//----------------------------------------------------------------------------
//
int
TextureMemoryLayout::numBits(unsigned int size)
{
    int bits = 0;

    while (size > 0) {
        size >>= 1;
        bits++;
    }

    return bits;
}


/////////////////////////////////////////////////////////////
// GS Simulator code
//

uint32 *p_gsmem;
int gsmem_size = 0;

int block16[32] =
{
	 0,  2,  8, 10,
	 1,  3,  9, 11,
	 4,  6, 12, 14,
	 5,  7, 13, 15,
	16, 18, 24, 26,
	17, 19, 25, 27,
	20, 22, 28, 30,
	21, 23, 29, 31
};

int columnWord16[32] = 
{
	 0,  1,  4,  5,  8,  9, 12, 13,   0,  1,  4,  5,  8,  9, 12, 13,
	 2,  3,  6,  7, 10, 11, 14, 15,   2,  3,  6,  7, 10, 11, 14, 15
};

int columnHalf16[32] =
{
	0, 0, 0, 0, 0, 0, 0, 0,  1, 1, 1, 1, 1, 1, 1, 1,
	0, 0, 0, 0, 0, 0, 0, 0,  1, 1, 1, 1, 1, 1, 1, 1
};


void readTexPSMCT16(int dbp, int dbw, int dsax, int dsay, int rrw, int rrh, void *data)
{
	//dbw >>= 1;
	unsigned short *src = (unsigned short *)data;
	int startBlockPos = dbp * 64;

	for(int y = dsay; y < dsay + rrh; y++)
	{
		int pageY = y / 64;
		int py = y - (pageY * 64);
		int blockY = py / 8;
		int by = py - blockY * 8;

		int column = by / 2;
		int cy = by - column * 2;

		for(int x = dsax; x < dsax + rrw; x++)
		{
			int pageX = x / 64;
			int page  = pageX + pageY * dbw;

			int px = x - (pageX * 64);

			int blockX = px / 16;
			int block  = block16[blockX + blockY * 4];

			int bx = px - blockX * 16;

			int cx = bx;
			int cw = columnWord16[cx + cy * 16];
			int ch = columnHalf16[cx + cy * 16];

			int gs_index = startBlockPos + page * 2048 + block * 64 + column * 16 + cw;
			Dbg_MsgAssert(gs_index < gsmem_size, ("GS simulator memory size: %d; addressing: %d", gsmem_size, gs_index));
			unsigned short *dst = (unsigned short *)&p_gsmem[gs_index];
			*src = dst[ch];
			src++;
		}
	}
}

int block4[32] =
{
	 0,  2,  8, 10,
	 1,  3,  9, 11,
	 4,  6, 12, 14,
	 5,  7, 13, 15,
	16, 18, 24, 26,
	17, 19, 25, 27,
	20, 22, 28, 30,
	21, 23, 29, 31
};

int columnWord4[2][128] =
{
	{
		 0,  1,  4,  5,  8,  9, 12, 13,   0,  1,  4,  5,  8,  9, 12, 13,   0,  1,  4,  5,  8,  9, 12, 13,   0,  1,  4,  5,  8,  9, 12, 13,
		 2,  3,  6,  7, 10, 11, 14, 15,   2,  3,  6,  7, 10, 11, 14, 15,   2,  3,  6,  7, 10, 11, 14, 15,   2,  3,  6,  7, 10, 11, 14, 15,

		 8,  9, 12, 13,  0,  1,  4,  5,   8,  9, 12, 13,  0,  1,  4,  5,   8,  9, 12, 13,  0,  1,  4,  5,   8,  9, 12, 13,  0,  1,  4,  5,
		10, 11, 14, 15,  2,  3,  6,  7,  10, 11, 14, 15,  2,  3,  6,  7,  10, 11, 14, 15,  2,  3,  6,  7,  10, 11, 14, 15,  2,  3,  6,  7
	},
	{
		 8,  9, 12, 13,  0,  1,  4,  5,   8,  9, 12, 13,  0,  1,  4,  5,   8,  9, 12, 13,  0,  1,  4,  5,   8,  9, 12, 13,  0,  1,  4,  5,
		10, 11, 14, 15,  2,  3,  6,  7,  10, 11, 14, 15,  2,  3,  6,  7,  10, 11, 14, 15,  2,  3,  6,  7,  10, 11, 14, 15,  2,  3,  6,  7,

		 0,  1,  4,  5,  8,  9, 12, 13,   0,  1,  4,  5,  8,  9, 12, 13,   0,  1,  4,  5,  8,  9, 12, 13,   0,  1,  4,  5,  8,  9, 12, 13,
		 2,  3,  6,  7, 10, 11, 14, 15,   2,  3,  6,  7, 10, 11, 14, 15,   2,  3,  6,  7, 10, 11, 14, 15,   2,  3,  6,  7, 10, 11, 14, 15
	}
};

int columnByte4[128] =
{
	0, 0, 0, 0, 0, 0, 0, 0,  2, 2, 2, 2, 2, 2, 2, 2,  4, 4, 4, 4, 4, 4, 4, 4,  6, 6, 6, 6, 6, 6, 6, 6,
	0, 0, 0, 0, 0, 0, 0, 0,  2, 2, 2, 2, 2, 2, 2, 2,  4, 4, 4, 4, 4, 4, 4, 4,  6, 6, 6, 6, 6, 6, 6, 6,

	1, 1, 1, 1, 1, 1, 1, 1,  3, 3, 3, 3, 3, 3, 3, 3,  5, 5, 5, 5, 5, 5, 5, 5,  7, 7, 7, 7, 7, 7, 7, 7,
	1, 1, 1, 1, 1, 1, 1, 1,  3, 3, 3, 3, 3, 3, 3, 3,  5, 5, 5, 5, 5, 5, 5, 5,  7, 7, 7, 7, 7, 7, 7, 7
};

void writeTexPSMT4(int dbp, int dbw, int dsax, int dsay, int rrw, int rrh, void *data)
{
	dbw >>= 1;
	unsigned char *src = (unsigned char *)data;
	int startBlockPos = dbp * 64;

	bool odd = false;

	for(int y = dsay; y < dsay + rrh; y++)
	{
		int pageY = y / 128;
		int py = y - (pageY * 128);
		int blockY = py / 16;
		int by = py - blockY * 16;

		int column = by / 4;
		int cy = by - column * 4;

		for(int x = dsax; x < dsax + rrw; x++)
		{
			int pageX = x / 128;
			int page  = pageX + pageY * dbw;

			int px = x - (pageX * 128);

			int blockX = px / 32;
			int block  = block4[blockX + blockY * 4];

			int bx = px - blockX * 32;

			int cx = bx;
			int cw = columnWord4[column & 1][cx + cy * 32];
			int cb = columnByte4[cx + cy * 32];

			int gs_index = startBlockPos + page * 2048 + block * 64 + column * 16 + cw;
			Dbg_MsgAssert(gs_index < gsmem_size, ("GS simulator memory size: %d; addressing: %d", gsmem_size, gs_index));
			unsigned char *dst = (unsigned char *)&p_gsmem[gs_index];

			if(cb & 1)
			{
				if(odd)
					dst[cb >> 1] = (dst[cb >> 1] & 0x0f) | ((*src) & 0xf0);
				else
					dst[cb >> 1] = (dst[cb >> 1] & 0x0f) | (((*src) << 4) & 0xf0);
			}
			else
			{
				if(odd)
					dst[cb >> 1] = (dst[cb >> 1] & 0xf0) | (((*src) >> 4) & 0x0f);
				else
					dst[cb >> 1] = (dst[cb >> 1] & 0xf0) | ((*src) & 0x0f);
			}

			if(odd)
				src++;

			odd = !odd;
		}
	}
}

// -------------------------------------------------------------------------------------------
//
//
int TextureMemoryLayout::BlockConv4to32(u_char *p_input, u_char *p_output) {

	static int lut[] = {
		// even column
		0, 68, 8,  76, 16, 84, 24, 92,
		1, 69, 9,  77, 17, 85, 25, 93,
		2, 70, 10, 78, 18, 86, 26, 94,
		3, 71, 11, 79, 19, 87, 27, 95,
		4, 64, 12, 72, 20, 80, 28, 88,
		5, 65, 13, 73, 21, 81, 29, 89,
		6, 66, 14, 74, 22, 82, 30, 90,
		7, 67, 15, 75, 23, 83, 31, 91,

		32, 100, 40, 108, 48, 116, 56, 124,
		33, 101, 41, 109, 49, 117, 57, 125,
		34, 102, 42, 110, 50, 118, 58, 126,
		35, 103, 43, 111, 51, 119, 59, 127,
		36, 96,  44, 104, 52, 112, 60, 120,
		37, 97,  45, 105, 53, 113, 61, 121,
		38, 98,  46, 106, 54, 114, 62, 122,
		39, 99,  47, 107, 55, 115, 63, 123,


		// odd column
		4, 64, 12, 72, 20, 80, 28, 88,
		5, 65, 13, 73, 21, 81, 29, 89,
		6, 66, 14, 74, 22, 82, 30, 90,
		7, 67, 15, 75, 23, 83, 31, 91,
		0, 68, 8,  76, 16, 84, 24, 92,
		1, 69, 9,  77, 17, 85, 25, 93,
		2, 70, 10, 78, 18, 86, 26, 94,
		3, 71, 11, 79, 19, 87, 27, 95,

		36, 96,  44, 104, 52, 112, 60, 120,
		37, 97,  45, 105, 53, 113, 61, 121,
		38, 98,  46, 106, 54, 114, 62, 122,
		39, 99,  47, 107, 55, 115, 63, 123,
		32, 100, 40, 108, 48, 116, 56, 124,
		33, 101, 41, 109, 49, 117, 57, 125,
		34, 102, 42, 110, 50, 118, 58, 126,
		35, 103, 43, 111, 51, 119, 59, 127
	};

	unsigned int i, j, k, i0, i1, i2;
	unsigned int index0, index1;
	unsigned char c_in, c_out, *pIn;

	pIn = p_input;

	// for first step, we only think for a single block. (4bits, 32x16)
	index1 = 0;

	for(k = 0; k < 4; k++) {
		index0 = (k % 2) * 128;

		for(i = 0; i < 16; i++) {

			for(j = 0; j < 4; j++) {

				c_out = 0x00;

				// lower 4bit.
				i0 = lut[index0++];
				i1 = i0 / 2;
				i2 = (i0 & 0x1) * 4;
				c_in = (pIn[i1] & (0x0f << i2)) >> i2;
				c_out = c_out | c_in;

				// uppper 4bit
				i0 = lut[index0++];
				i1 = i0 / 2;
				i2 = (i0 & 0x1) * 4;
				c_in = (pIn[i1] & (0x0f << i2)) >> i2;
				c_out = c_out | ((c_in << 4) & 0xf0);

				p_output[index1++] = c_out;
			}
		}
		pIn += 64;
	}

	return 0;
}


// -------------------------------------------------------------------------------------------
//
//
int TextureMemoryLayout::BlockConv8to32(u_char *p_input, u_char *p_output) {

	static int lut[] = {
		// even column
		0, 36, 8,  44,
		1, 37, 9,  45,
		2, 38, 10, 46,
		3, 39, 11, 47,
		4, 32, 12, 40,
		5, 33, 13, 41,
		6, 34, 14, 42,
		7, 35, 15, 43,

		16, 52, 24, 60,
		17, 53, 25, 61,
		18, 54, 26, 62,
		19, 55, 27, 63, 
		20, 48, 28, 56,
		21, 49, 29, 57,
		22, 50, 30, 58,
		23, 51, 31, 59,

		// odd column
		4, 32, 12, 40,
		5, 33, 13, 41,
		6, 34, 14, 42,
		7, 35, 15, 43,
		0, 36, 8,  44,
		1, 37, 9,  45,
		2, 38, 10, 46,
		3, 39, 11, 47,

		20, 48, 28, 56,
		21, 49, 29, 57,
		22, 50, 30, 58,
		23, 51, 31, 59,
		16, 52, 24, 60,
		17, 53, 25, 61,
		18, 54, 26, 62,
		19, 55, 27, 63
	};

	unsigned int i, j, k, i0;
	unsigned int index0, index1;
	unsigned char *pIn;

	pIn = p_input;

	// for first step, we only think for a single block. (4bits, 32x16)
	index1 = 0;

	for(k = 0; k < 4; k++) {

		index0 = (k % 2) * 64;

		for(i = 0; i < 16; i++) {
			for(j = 0; j < 4; j++) {
				i0 = lut[index0++];
				p_output[index1++] = pIn[i0];
			}
		}
		pIn += 64;
	}

	return 0;
}


// -------------------------------------------------------------------------------
// send page size 4bit texture and get each block
// 
//
//
#define PSMT4_BLOCK_WIDTH 32
#define PSMT4_BLOCK_HEIGHT 16

#define PSMCT32_BLOCK_WIDTH 8
#define PSMCT32_BLOCK_HEIGHT 8

int TextureMemoryLayout::PageConv4to32(int width, int height, u_char *p_input, u_char *p_output) {

	static u_int block_table4[] = {
		0,  2,  8, 10,
		1,  3,  9, 11,
		4,  6, 12, 14,
		5,  7, 13, 15,
		16, 18, 24, 26,
		17, 19, 25, 27,
		20, 22, 28, 30,
		21, 23, 29, 31
	};

	static u_int block_table32[] = {
		0,  1,  4,  5, 16, 17, 20, 21,
		2,  3,  6,  7, 18, 19, 22, 23,
		8,  9, 12, 13, 24, 25, 28, 29,
		10, 11, 14, 15, 26, 27, 30, 31
	};

	u_int *index32_h, *index32_v, in_block_nb;

	u_char input_block[16 * 16], output_block[16 * 16];
	u_char *pi0, *pi1, *po0, *po1;
	int index0, index1, i, j, k;
	int n_width, n_height, input_page_line_size;
	int output_page_line_size;





	// --- create table for output 32bit buffer ---
	index32_h = (u_int*) malloc(8 * 4 * sizeof(u_int));
	index32_v = (u_int*) malloc(8 * 4 * sizeof(u_int));
	index0 = 0;
	for(i = 0; i < 4; i++) {
		for(j = 0; j < 8; j++) {
			index1 = block_table32[index0];
			index32_h[index1] = j;
			index32_v[index1] = i;
			index0++;
		}
	}




	n_width = width / 32;
	n_height = height / 16;

	memset(input_block, 0, 16 *16);
	memset(output_block, 0, 16 * 16);

	input_page_line_size = 128 / 2;    // PSMT4 page width (byte)
	output_page_line_size = 256;       // PSMCT32 page width (byte)

	// now assume copying from page top. 
	for(i = 0; i < n_height; i++) {

		for(j = 0; j < n_width; j++) {

			pi0 = input_block;
			pi1 = p_input + 16 * i * input_page_line_size + j * 16;

			in_block_nb = block_table4[i * n_width + j];

			for(k = 0; k < PSMT4_BLOCK_HEIGHT; k++) {
				memcpy(pi0, pi1, PSMT4_BLOCK_WIDTH / 2); // copy full 1 line of 1 block.
				pi0 += PSMT4_BLOCK_WIDTH / 2;
				pi1 += input_page_line_size;
			}

			BlockConv4to32(input_block, output_block);

			po0 = output_block;
			po1 = p_output + 8 * index32_v[in_block_nb] * output_page_line_size + index32_h[in_block_nb] * 32;
			for(k = 0; k < PSMCT32_BLOCK_HEIGHT; k++) {
				memcpy(po1, po0, PSMCT32_BLOCK_WIDTH * 4);
				po0 += PSMCT32_BLOCK_WIDTH * 4;
				po1 += output_page_line_size;   
			}

		}
	}

	free(index32_h);
	free(index32_v);

	return 0;
}



// -------------------------------------------------------------------------------
// send page size 8bit texture and get each block
// 
//
//
#define PSMT8_BLOCK_WIDTH  16
#define PSMT8_BLOCK_HEIGHT 16



int TextureMemoryLayout::PageConv8to32(int width, int height, u_char *p_input, u_char *p_output) {

	static u_int block_table8[] = {
		0,  1,  4,  5, 16, 17, 20, 21,
		2,  3,  6,  7, 18, 19, 22, 23,
		8,  9, 12, 13, 24, 25, 28, 29,
		10, 11, 14, 15, 26, 27, 30, 31
	};

	static u_int block_table32[] = {
		0,  1,  4,  5, 16, 17, 20, 21,
		2,  3,  6,  7, 18, 19, 22, 23,
		8,  9, 12, 13, 24, 25, 28, 29,
		10, 11, 14, 15, 26, 27, 30, 31
	};

	u_int *index32_h, *index32_v, in_block_nb;

	u_char input_block[16 * 16], output_block[16 * 16];
	u_char *pi0, *pi1, *po0, *po1;
	int index0, index1, i, j, k;
	int n_width, n_height, input_page_line_size;
	int output_page_line_size;



	// --- create table for output 32bit buffer ---
	index32_h = (u_int*) malloc(8 * 4 * sizeof(u_int));
	index32_v = (u_int*) malloc(8 * 4 * sizeof(u_int));
	index0 = 0;
	for(i = 0; i < 4; i++) {
		for(j = 0; j < 8; j++) {
			index1 = block_table32[index0];
			index32_h[index1] = j;
			index32_v[index1] = i;
			index0++;
		}
	}


	// how many blocks we should calc (width/height)
	n_width = width / PSMT8_BLOCK_WIDTH;
	n_height = height / PSMT8_BLOCK_HEIGHT;

	memset(input_block, 0, 16 *16);
	memset(output_block, 0, 16 * 16);

	input_page_line_size  = 128;    // PSMT8 page width (byte)
	output_page_line_size = 256;    // PSMCT32 page width (byte)

	// now assume copying from page top. 
	for(i = 0; i < n_height; i++) {

		for(j = 0; j < n_width; j++) {

			pi0 = input_block;
			pi1 = p_input + PSMT8_BLOCK_HEIGHT * i * input_page_line_size + j * PSMT8_BLOCK_WIDTH; // byte

			in_block_nb = block_table8[i * n_width + j];

			for(k = 0; k < PSMT8_BLOCK_HEIGHT; k++) {
				memcpy(pi0, pi1, PSMT8_BLOCK_WIDTH); // copy full 1 line of 1 block.
				pi0 += PSMT8_BLOCK_WIDTH;  
				pi1 += input_page_line_size;
			}

			BlockConv8to32(input_block, output_block);

			po0 = output_block;
			po1 = p_output + 8 * index32_v[in_block_nb] * output_page_line_size + index32_h[in_block_nb] * 32;
			for(k = 0; k < PSMCT32_BLOCK_HEIGHT; k++) {
				memcpy(po1, po0, PSMCT32_BLOCK_WIDTH * 4);
				po0 += PSMCT32_BLOCK_WIDTH * 4;
				po1 += output_page_line_size;   
			}

		}
	}

	free(index32_h);
	free(index32_v);

	return 0;
}


// -------------------------------------------------------------
//
//

#define PSMT4_PAGE_WIDTH    128
#define PSMT4_PAGE_HEIGHT   128
#define PSMCT32_PAGE_WIDTH  64
#define PSMCT32_PAGE_HEIGHT 32

static bool sCanConvert4to32[10][10] =
{
#if 1	// This one only allows for dimensions that are easy to calculate the 32-bit dimensions
	//  1      2      4      8     16     32     64     128    256    512
	{ false, false, false, false, false, false, false, false, false, false },	// 1
	{ false, false, false, false, false, false, false, false, false, false },	// 2
	{ false, false, false, false, false, false, false, false, false, false },	// 4
	{ false, false, false, false, false, false , false, false, false, false },	// 8
	{ false, false, false, false, false, false , false, false, false, false },	// 16
	{ false, false, false, false, false, true , false, false, false, false },	// 32
	{ false, false, false, false, false, false, true , false, false, false },	// 64
	{ false, false, false, false, false, false, false, true , true , true  },	// 128
	{ false, false, false, false, false, false, false, true , true , true  },	// 256
	{ false, false, false, false, false, false, false, true , true , true  },	// 512
#else
	//  1      2      4      8     16     32     64     128    256    512
	{ false, false, false, false, false, false, false, false, false, false },	// 1
	{ false, false, false, false, false, false, false, false, false, false },	// 2
	{ false, false, false, false, false, false, false, false, false, false },	// 4
	{ false, false, false, false, false, true , false, false, false, false },	// 8
	{ false, false, false, false, false, true , false, false, false, false },	// 16
	{ false, false, false, false, false, true , true , false, false, false },	// 32
	{ false, false, false, false, false, true , true , true , false, false },	// 64
	{ false, false, false, false, false, false, true , true , true , true  },	// 128
	{ false, false, false, false, false, false, false, true , true , true  },	// 256
	{ false, false, false, false, false, false, false, true , true , true  },	// 512
#endif
};

bool TextureMemoryLayout::CanConv4to32(int width, int height)
{
    int tw = numBits(width) - 1;
    int th = numBits(height) - 1;

	return sCanConvert4to32[th][tw];
}

bool TextureMemoryLayout::Conv4to32(int width, int height, u_char *p_input, u_char *p_output) {


	int i, j, k;
	int n_page_h, n_page_w, n_page4_width_byte, n_page32_width_byte;
	u_char *pi0, *pi1, *po0, *po1;
	int n_input_width_byte, n_output_width_byte, n_input_height, n_output_height;
	u_char input_page[PSMT4_PAGE_WIDTH / 2 * PSMT4_PAGE_HEIGHT];
	u_char output_page[PSMCT32_PAGE_WIDTH * 4 * PSMCT32_PAGE_HEIGHT];

	#ifdef __NOPT_ASSERT__
	int output_buffer_size = width * height / 2;
	#endif

	// ----- check width -----
	for(i = 0; i < 11; i++) {
		if(width == (0x400 >> i)) break;
	}
	if(i == 11) {
		fprintf(stderr, "Error : width is not 2^n\n");
		return false;
	}

	//printf("input_page: %d\n", PSMT4_PAGE_WIDTH / 2 * PSMT4_PAGE_HEIGHT);
	//printf("output_page: %d\n", PSMCT32_PAGE_WIDTH * 4 * PSMCT32_PAGE_HEIGHT);

	memset(input_page, 0, PSMT4_PAGE_WIDTH / 2 * PSMT4_PAGE_HEIGHT);
	memset(output_page, 0, PSMCT32_PAGE_WIDTH * 4 * PSMCT32_PAGE_HEIGHT);

	// ----- check height -----
	for(i = 0; i < 11; i++) {
		if(height == (0x400 >> i)) break;
	}
	if(i == 11) {
		fprintf(stderr, "Error : width is not 2^n\n");
		return false;
	}


	n_page_w = (width - 1) / PSMT4_PAGE_WIDTH + 1;
	n_page_h = (height - 1) / PSMT4_PAGE_HEIGHT + 1;

	n_page4_width_byte = PSMT4_PAGE_WIDTH / 2;
	n_page32_width_byte = PSMCT32_PAGE_WIDTH * 4;

	//printf("n_page_w : %d\n", n_page_w );
	//printf("n_page_h : %d\n", n_page_h );

	//printf("n_page4_width_byte : %d\n", n_page4_width_byte );
	//printf("n_page32_width_byte : %d\n", n_page32_width_byte );


	// --- set in/out buffer size (for image smaller than one page) ---
	if(n_page_w == 1) {
		n_input_width_byte = width / 2;
		n_output_height = width / 4;
	} else {
		n_input_width_byte = n_page4_width_byte;
		n_output_height = PSMCT32_PAGE_HEIGHT;
	}

	if(n_page_h == 1) {
		n_input_height = height;
		n_output_width_byte = height * 2;
	} else {
		n_input_height = PSMT4_PAGE_HEIGHT;
		n_output_width_byte = n_page32_width_byte;
	}


	for(i = 0; i < n_page_h; i++) {
		for(j = 0; j < n_page_w; j++) {
			pi0 = p_input + (n_input_width_byte * PSMT4_PAGE_HEIGHT) * n_page_w * i 
				+ n_input_width_byte * j;
			pi1 = input_page;

			for(k = 0; k < n_input_height; k++) {
				memcpy(pi1, pi0, n_input_width_byte);
				pi0 += n_input_width_byte * n_page_w;
				pi1 += n_page4_width_byte;
			}

			PageConv4to32(PSMT4_PAGE_WIDTH, PSMT4_PAGE_HEIGHT, input_page, output_page);

			po0 = p_output + (n_output_width_byte * PSMCT32_PAGE_HEIGHT) * n_page_w * i
				+ n_output_width_byte * j;
			po1 = output_page;
			for(k = 0; k < n_output_height; k++) {
				Dbg_MsgAssert(((int) (po0 - p_output) + n_output_width_byte) <= output_buffer_size, ("Went outside output buffer for texture of size (%d, %d)", width, height));
				memcpy(po0, po1, n_output_width_byte);
				po0 += n_output_width_byte * n_page_w;
				po1 += n_page32_width_byte;
			}          
		}
	}

	return CanConv4to32(width, height);
}


// -------------------------------------------------------------
//
//

#define PSMT8_PAGE_WIDTH    128
#define PSMT8_PAGE_HEIGHT   64

static bool sCanConvert8to32[10][10] =
{
	//  1      2      4      8     16     32     64     128    256    512
	{ false, false, false, false, false, false, false, false, false, false },	// 1
	{ false, false, false, false, false, false, false, false, false, false },	// 2
	{ false, false, false, false, true , true , true , true , true , false },	// 4
	{ false, false, false, false, true , true , true , true , true , false },	// 8
	{ false, false, false, false, true , true , true , true , true , false },	// 16
	{ false, false, false, false, true , true , true , true , true , false },	// 32
	{ false, false, false, false, true , true , true , true , true , true  },	// 64
	{ false, false, false, false, false, false, false, true , true , true  },	// 128
	{ false, false, false, false, false, false, false, true , true , true  },	// 256
	{ false, false, false, false, false, false, false, true , true , false },	// 512
};

bool TextureMemoryLayout::CanConv8to32(int width, int height)
{
    int tw = numBits(width) - 1;
    int th = numBits(height) - 1;

	return sCanConvert8to32[th][tw];
}

bool TextureMemoryLayout::Conv8to32(int width, int height, u_char *p_input, u_char *p_output) {

	int i, j, k;
	int n_page_h, n_page_w, n_page8_width_byte, n_page32_width_byte;
	int n_input_width_byte, n_output_width_byte, n_input_height, n_output_height;
	u_char *pi0, *pi1, *po0, *po1;
	u_char input_page[PSMT8_PAGE_WIDTH * PSMT8_PAGE_HEIGHT];
	u_char output_page[PSMCT32_PAGE_WIDTH * 4 * PSMCT32_PAGE_HEIGHT];

	#ifdef __NOPT_ASSERT__
	int output_buffer_size = width * height;
	#endif
	
	// ----- check width -----
	for(i = 0; i < 11; i++) {
		if(width == (0x400 >> i)) break;
	}
	if(i == 11) {
		fprintf(stderr, "Error : width is not 2^n\n");
		return false;
	}

	// ----- check height -----
	for(i = 0; i < 11; i++) {
		if(height == (0x400 >> i)) break;
	}
	if(i == 11) {
		fprintf(stderr, "Error : width is not 2^n\n");
		return false;
	}

	memset(input_page, 0, PSMT8_PAGE_WIDTH * PSMT8_PAGE_HEIGHT);
	memset(output_page, 0, PSMCT32_PAGE_WIDTH * 4 * PSMCT32_PAGE_HEIGHT);

	n_page_w = (width - 1) / PSMT8_PAGE_WIDTH + 1;
	n_page_h = (height - 1) / PSMT8_PAGE_HEIGHT + 1;

	n_page8_width_byte = PSMT8_PAGE_WIDTH;
	n_page32_width_byte = PSMCT32_PAGE_WIDTH * 4;

	// --- set in/out buffer size (for image smaller than one page) ---
	if(n_page_w == 1) {
		n_input_width_byte = width;
		n_output_width_byte = width * 2;
	} else {
		n_input_width_byte = n_page8_width_byte;
		n_output_width_byte = n_page32_width_byte;
	}

	if(n_page_h == 1) {
		n_input_height = height;
		n_output_height = height / 2;
	} else {
		n_input_height = PSMT8_PAGE_HEIGHT;
		n_output_height = PSMCT32_PAGE_HEIGHT;
	}

	// --- conversion ---
	for(i = 0; i < n_page_h; i++) {
		for(j = 0; j < n_page_w; j++) {
			pi0 = p_input + (n_input_width_byte * PSMT8_PAGE_HEIGHT) * n_page_w * i 
				+ n_input_width_byte * j;
			pi1 = input_page;

			for(k = 0; k < n_input_height; k++) {
				memcpy(pi1, pi0, n_input_width_byte);
				pi0 += n_input_width_byte * n_page_w;
				pi1 += n_page8_width_byte;
			}

			// --- convert a page ---
			PageConv8to32(PSMT8_PAGE_WIDTH, PSMT8_PAGE_HEIGHT, input_page, output_page);

			po0 = p_output + (n_output_width_byte * n_output_height) * n_page_w * i
				+ n_output_width_byte * j;
			po1 = output_page;
			for(k = 0; k < n_output_height; k++) {
				Dbg_MsgAssert(((int) (po0 - p_output) + n_output_width_byte) <= output_buffer_size, ("Went outside output buffer for texture of size (%d, %d)", width, height));
				memcpy(po0, po1, n_output_width_byte);
				po0 += n_output_width_byte * n_page_w;
				po1 += n_page32_width_byte;
			}          
		}
	}

	return CanConv4to16(width, height);
}

// -------------------------------------------------------------
//
//

static bool sCanConvert4to16[10][10] =
{
	//  1      2      4      8     16     32     64     128    256    512
	{ false, false, false, false, false, false, false, false, false, false },	// 1
	{ false, false, false, false, false, false, false, false, false, false },	// 2
	{ false, false, false, false, false, false, false, false, false, false },	// 4
	{ false, false, false, false, true , true , true , true , true , false },	// 8
	{ false, false, false, false, true , true , true , true , true , false },	// 16
	{ false, false, false, false, true , true , true , true , true , false },	// 32
	{ false, false, false, false, true , true , true , true , true , false },	// 64
	{ false, false, false, false, true , true , true , true , true , true  },	// 128
	{ false, false, false, false, false, false, false, true , true , true  },	// 256
	{ false, false, false, false, false, false, false, true , true , true  },	// 512
};

bool TextureMemoryLayout::CanConv4to16(int width, int height)
{
    int tw = numBits(width) - 1;
    int th = numBits(height) - 1;

	return sCanConvert4to16[th][tw];
}

bool TextureMemoryLayout::Conv4to16(int width, int height, u_char *p_input, u_char *p_output) {


	int i;

	// ----- check width -----
	for(i = 0; i < 11; i++) {
		if(width == (0x400 >> i)) break;
	}
	if(i == 11) {
		fprintf(stderr, "Error : width is not 2^n\n");
		return false;
	}

	// ----- check height -----
	for(i = 0; i < 11; i++) {
		if(height == (0x400 >> i)) break;
	}
	if(i == 11) {
		fprintf(stderr, "Error : width is not 2^n\n");
		return false;
	}
	
	// Find size needed for buffers
	int min_4_width = width;
	int min_4_height = height;
	int min_16_width = width / 2;
	int min_16_height = height / 2;
	getMinSize(min_4_width, min_4_height, PSMT4);
	getMinSize(min_16_width, min_16_height, PSMCT16);

	int blocks_needed = max(getImageSize(min_4_width, min_4_height, PSMT4), getImageSize(min_16_width, min_16_height, PSMCT16));
	int words_needed = blocks_needed * 64;

	// Allocate memory
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	p_gsmem = new uint32[words_needed];
	gsmem_size = words_needed;
	Mem::Manager::sHandle().PopContext();

	// Swizzle through GS simulator code
	writeTexPSMT4(0, max(width / 128, 1) * 2, 0, 0, width, height, p_input);
	readTexPSMCT16(0, max(width / 2 / 128, 1) * 2, 0, 0, width / 2, height / 2, p_output);

	// Free memory
	delete [] p_gsmem;
	p_gsmem = NULL;
	gsmem_size = 0;

	return CanConv4to16(width, height);
}

} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/texturemem.h
================================================
#ifndef __TEXTUREMEM_H
#define __TEXTUREMEM_H


namespace NxPs2
{

class TextureMemoryLayout {
public:

    ///
    enum {
        MAX_PIXEL_SIZE_BITS = 10
    };

    
    /// PIXEL_16S won't work with the algorithms here because
    /// of it's wierd layout.  Hopefully we won't need it.
    
    /// Pixel type.  Will add more types later when needed.
    enum PixelType {
        PIXEL_INVALID = -1,
        PIXEL_32 = 0,       // Covers 32 and 24 bit
        PIXEL_16,
        PIXEL_8,
        PIXEL_4,
        NUM_PIXEL_TYPES
    };
    
    ///
    struct TexSize {
        int width;
        int height;
    };

    ///
    struct PixelFormatLayout {
        TexSize pageSize;
        TexSize blockSize;
        
        bool adjacentBlockWidth;  // if not width, then height
    };

    ///
    typedef TexSize MinSizeArray[MAX_PIXEL_SIZE_BITS][MAX_PIXEL_SIZE_BITS];
    
    ///
    TextureMemoryLayout();

    ///
    void getMinSize(int& width, int& height, int pixMode);
    ///
    int getImageSize(int width, int height, int pixMode);
    ///
    int getBlockNumber(int blockX, int blockY, int pixMode);
    ///
    int getBlockOffset(int bufferWidth, int posX, int posY, int pixMode);
    ///
    void getPosFromBlockNumber(int& posX, int& posY, int blockNumber, int pixMode);
    ///
    void getPosFromBlockOffset(int& posX, int& posY, int blockOffset, int bufferWidth, int pixMode);
    ///
    bool isPageSize(int width, int height, int pixMode);
    ///
    bool isBlockSize(int width, int height, int pixMode);
    ///
    bool isMinPageSize(int width, int height, int pixMode);
    ///
    bool isMinBlockSize(int width, int height, int pixMode);

	// Converts memory layout (not format)
    int BlockConv4to32(uint8 *p_input, uint8 *p_output);
	int BlockConv8to32(uint8 *p_input, uint8 *p_output);
	int PageConv4to32(int width, int height, uint8 *p_input, uint8 *p_output);
	int PageConv8to32(int width, int height, uint8 *p_input, uint8 *p_output);
	bool Conv4to32(int width, int height, uint8 *p_input, uint8 *p_output);
	bool Conv8to32(int width, int height, uint8 *p_input, uint8 *p_output);
	bool Conv4to16(int width, int height, uint8 *p_input, uint8 *p_output);

	bool CanConv4to32(int width, int height);
	bool CanConv8to32(int width, int height);
	bool CanConv4to16(int width, int height);

    ///
    static int numBits(unsigned int size);
    ///
    static PixelType modeToPixelType(int pMode);

private:
    ///
    MinSizeArray minSize[NUM_PIXEL_TYPES];

    ///
    static const PixelFormatLayout pixelFormat[NUM_PIXEL_TYPES];
    ///
    static const int blockArrangement[4][8];

    ///
    void initMinSize(MinSizeArray& mArray, PixelType pType);
    ///
    void setMinSize(int& width, int& height, PixelType pType);

};

} // namespace NxPs2


#endif // __TEXTUREMEM_H



================================================
FILE: Code/Gfx/NGPS/NX/types.h
================================================
#ifndef __TYPES_H
#define __TYPES_H

typedef unsigned int 	uint;
typedef unsigned long	uint64;
typedef unsigned int	uint32;
typedef unsigned short	uint16;
typedef unsigned char	uint8;

typedef signed int 		sint;
typedef signed long		sint64;
typedef signed int		sint32;
typedef signed short	sint16;
typedef signed char		sint8;

#endif // __TYPES_H


================================================
FILE: Code/Gfx/NGPS/NX/vif.cpp
================================================
#include 
#include 
#include "dma.h"
#include "vif.h"
#include "vu1.h"


namespace NxPs2
{


uint8 *vif::NextCode(uint8 *pVifcode)
{
	if ((pVifcode[3] & 0x60) != 0x60)
	{
		switch (pVifcode[3] & 0x7F)
		{
		case 0x00:	// NOP
		case 0x02:	// OFFSET
		case 0x03:	// BASE
		case 0x04:	// ITOP
		case 0x05:	// STMOD
		case 0x06:	// MSKPATH3
		case 0x07:	// MARK
		case 0x10:	// FLUSHE
		case 0x11:	// FLUSH
		case 0x13:	// FLUSHA
		case 0x14:	// MSCAL
		case 0x15:	// MSCALF
		case 0x17:	// MSCNT
			return pVifcode + 4;

		case 0x01:	// STCYCL
			vif::CL = pVifcode[0];
			vif::WL = pVifcode[1];
			return pVifcode + 4;

		case 0x20:	// STMASK
			return pVifcode + 8;

		case 0x30:	// STROW
		case 0x31:	// STCOL
			return pVifcode + 20;

		case 0x4A:	// MPG
			return pVifcode + (pVifcode[2] << 3) + 4;

		case 0x50:	// DIRECT
		case 0x51:	// DIRECTHL
			return pVifcode + (((uint16 *)pVifcode)[0] << 4) + 4;

		default:	// undefined vifcode
			return pVifcode;
		}
	}
	else			// UNPACK
	{
		uint vn = (pVifcode[3] >> 2) & 3;
		uint vl = pVifcode[3] & 3;
		uint num = pVifcode[2];
		uint dimension = vn+1;
		uint bitlength = 32>>vl;
		if (vif::WL <= vif::CL)
		{
			return pVifcode + 4 + (((bitlength * dimension * num + 31) >> 5) << 2);
		}
		else
		{
			uint rem = num % vif::WL;
			uint n = vif::CL * (num/vif::WL) + (rem > vif::CL ? vif::CL : rem);
			return pVifcode + 4 + (((bitlength * dimension * n + 31) >> 5) << 2);
		}
	}
}




//--------------------------------
//		S T A T I C   D A T A
//--------------------------------

uint  vif::UnpackSize;
uint  vif::CycleLength;
uint  vif::BitLength;
uint  vif::Dimension;
uint8 *vif::pVifCode;
uint  vif::WL;
uint  vif::CL;


} // namespace NxPs2





================================================
FILE: Code/Gfx/NGPS/NX/vif.h
================================================
#ifndef __VIF_H
#define __VIF_H

#include "dma.h"
#include "vu1.h"

namespace NxPs2
{

// VIF1_MODE settings
#define NORMAL_MODE		0
#define OFFSET_MODE		1
#define DIFFERENCE_MODE	2

// unpack formats
#define	S_32	0x0
#define	S_16	0x1
#define	S_8		0x2
#define	V2_32	0x4
#define	V2_16	0x5
#define	V2_8	0x6
#define	V3_32	0x8
#define	V3_16	0x9
#define	V3_8	0xA
#define	V4_32	0xC
#define	V4_16	0xD
#define	V4_8	0xE
#define	V4_5	0xF

// unpack FLG values
#define	ABS	0
#define REL 1

// unpack USN values
#define SIGNED		0
#define UNSIGNED	1



class vif
{

public:

	//------------------------------------------
	//		S T A T I C   F U N C T I O N S
	//------------------------------------------

	// vifcodes
	static void Code(uint8 CMD, uint8 NUM, uint16 IMMEDIATE);
	static void BASE(uint16 BASE);
	static void DIRECT(uint16 SIZE);
	static void DIRECTHL(uint16 SIZE);
	static void FLUSH(void);
	static void FLUSHA(void);
	static void FLUSHE(void);
	static void ITOP(uint16 ADDR);
	static void MARK(uint16 MARK);
	static void MPG(uint8 SIZE, uint16 LOADADDR);
	static void MSCAL(uint16 EXECADDR);
	static void MSCALF(uint16 EXECADDR);
	static void MSCNT(void);
	static void MSKPATH3(uint16 MASK);
	static void NOP(void);
	static void NOPi(void);
	static void OFFSET(uint16 OFFSET);
	static void STCOL(uint32 C0, uint32 C1, uint32 C2, uint32 C3);
	static void STCYCL(uint16 WL, uint16 CL);
	static void STMASK(uint32 MASK);
	static void STMOD(uint16 MODE);
	static void STROW(uint32 R0, uint32 R1, uint32 R2, uint32 R3);
	static void UNPACK(uint8 m, uint8 vnvl, uint8 SIZE, uint16 FLG, uint16 USN, uint16 ADDR);

	// vifcodes, begin-end style
	static void BeginDIRECT(void);
	static void EndDIRECT(void);
	static void BeginDIRECTHL(void);
	static void EndDIRECTHL(void);
	static void BeginMPG(uint16 LOADADDR);
	static void EndMPG(void);
	static void BeginUNPACK(uint8 m, uint8 vnvl, uint16 FLG, uint16 USN, uint16 ADDR);
	static void EndUNPACK(void);

	// storing data for various unpack formats
	static void StoreS_32(uint32 x);
	static void StoreS_16(uint16 x);
	static void StoreS_8(uint8 x);
	static void StoreV2_32(uint32 x, uint32 y);
	static void StoreV2_16(uint16 x, uint16 y);
	static void StoreV2_8(uint8 x, uint8 y);
	static void StoreV3_32(uint32 x, uint32 y, uint32 z);
	static void StoreV3_16(uint16 x, uint16 y, uint16 z);
	static void StoreV3_8(uint8 x, uint8 y, uint8 z);
	static void StoreV4_32F(float x, float y, float z, float w);
	static void StoreV4_32(sint32 x, sint32 y, sint32 z, sint32 w);
	static void StoreV4_16(sint16 x, sint16 y, sint16 z, sint16 w);
	static void StoreV4_8(sint8 x, sint8 y, sint8 z, sint8 w);
	static void StoreV4_5(uint8 R, uint8 G, uint8 B, uint8 A);

	// vifcode parsing
	static uint8 *vif::NextCode(uint8 *pVifcode);


	//--------------------------------
	//		S T A T I C   D A T A
	//--------------------------------

	static uint  UnpackSize;
	static uint  CycleLength;
	static uint  BitLength;
	static uint  Dimension;
	static uint8 *pVifCode;
	static uint  WL;
	static uint  CL;


}; // class vif



//--------------------------
//		V I F C O D E S
//--------------------------



// generic vifcode
//
//	 31   24 23   16 15            0
//	Ŀ
//	  CMD    NUM     IMMEDIATE   
//	


inline void vif::Code(uint8 CMD, uint8 NUM, uint16 IMMEDIATE)
{
	dma::Store32((uint32)CMD<<24 | (uint32)NUM<<16 | (uint32)IMMEDIATE);
}



// Sets VIF1_BASE to lower 10 bits of BASE.

inline void vif::BASE(uint16 BASE)
{
	vif::Code(0x03, 0, BASE);
}



// Transfers SIZE 128-bit data via PATH2.

inline void vif::DIRECT(uint16 SIZE)
{
	vif::Code(0x50, 0, SIZE);
}



// Transfers SIZE 128-bit data via PATH2, stalling till PATH3 IMAGE mode transfer is complete.

inline void vif::DIRECTHL(uint16 SIZE)
{
	vif::Code(0x51, 0, SIZE);
}



// Waits for PATH1/PATH2 transfers to end, after VU1 microprogram has ended.

inline void vif::FLUSH(void)
{
	vif::Code(0x11, 0, 0);
}



// Waits till no PATH3 transfer request, after the end of VU1 microprogram and PATH1/PATH2 transfer.

inline void vif::FLUSHA(void)
{
	vif::Code(0x13, 0, 0);
}



// Waits for VU0/VU1 microprogram to end.

inline void vif::FLUSHE(void)
{
	vif::Code(0x10, 0, 0);
}



// Sets VIFn_ITOPS to lower to bits of ADDR.

inline void vif::ITOP(uint16 ADDR)
{
	vif::Code(0x04, 0, ADDR);
}



// Sets VIFn_MARK to MARK.

inline void vif::MARK(uint16 MARK)
{
	vif::Code(0x07, 0, MARK);
}



// Waits for end of microprogram and transfers SIZE 64-bit data to MicroMem address LOADADDR (low 11 bits).

inline void vif::MPG(uint8 SIZE, uint16 LOADADDR)
{
	vif::Code(0x4A, SIZE, LOADADDR);
}



// Waits for end of microprogram, and activates microprogram at address EXECADDR (low 11 bits).

inline void vif::MSCAL(uint16 EXECADDR)
{
	vif::Code(0x14, 0, EXECADDR);
	vu1::Buffer = vu1::Loc;
}



// Waits for end of bot microprogram and PATH1/PATH2 transfers,
// and activates microprogram at address EXECADDR (low 11 bits).

inline void vif::MSCALF(uint16 EXECADDR)
{
	vif::Code(0x15, 0, EXECADDR);
}



// Waits for end of microprogram, and activates microprogram at address held in PC.

inline void vif::MSCNT(void)
{
	vif::Code(0x17, 0, 0);
}



// MASK==1 => disables transfer via PATH3.

inline void vif::MSKPATH3(uint16 MASK)
{
	vif::Code(0x06, 0, MASK<<15);
}



// Does nothing.

inline void vif::NOP(void)
{
	vif::Code(0x00, 0, 0);
}



// Interrupt version.

inline void vif::NOPi(void)
{
	vif::Code(0x80, 0, 0);
}



// Sets VIF1_OFST to lower 10 bits of OFFSET.
// DBF flag of VIF1_STAT is cleared and VIF1_BASE is copied to VIF1_TOPS.

inline void vif::OFFSET(uint16 OFFSET)
{
	vif::Code(0x02, 0, OFFSET);
}



// Sets VIFn_C0-VIFn_C3 to C0-C3.

inline void vif::STCOL(uint32 C0, uint32 C1, uint32 C2, uint32 C3)
{
	vif::Code(0x31, 0, 0);
	dma::Store32(C0, C1, C2, C3);
}



// Sets VIFn_CYCLE.
// CL>=WL gives skipping write;
// CLCL?WL:CL;
}



// Sets VIFn_MASK to MASK = (m15,...,m0) with m0 at the low end of the word.
//
//					 W	 Z	 Y	 X
//					---------------
// write cycle=1	m0	m1	m2	m3
// write cycle=2	m4	m5	m6	m7
// write cycle=3	m8	m9	m10	m11
// write cycle>=4	m15	m14	m13	m12
//
// m[n]=0 => decompressed data written as-is;
// m[n]=1 => row register written;
// m[n]=2 => column register written;
// m[n]=3 => write masked.

inline void vif::STMASK(uint32 MASK)
{
	vif::Code(0x20, 0, 0);
	dma::Store32(MASK);
}



// Sets VIFn_MODE to lower 2 bits of MODE.

inline void vif::STMOD(uint16 MODE)
{
	vif::Code(0x05, 0, MODE);
}



// Sets VIFn_R0-VIFn_R3 to R0-R3.

inline void vif::STROW(uint32 R0, uint32 R1, uint32 R2, uint32 R3)
{
	vif::Code(0x30, 0, 0);
	dma::Store32(R0,R1,R2,R3);
}



// Decompresses data to VUMem+ADDR.
// SIZE is the number of decompressed data;
// vnvl is the format of compressed data (S_8, V4_32, etc);
// destination in VUMem is ADDR (if FLG==0 on VU1) or VIF1_TOPS+ADDR (if FLG==1 on VU1);
// USN==0 => sign extension, USN==1 => padding with 0's;
// m==1 => masking write using VIFn_MASK.

inline void vif::UNPACK(uint8 m, uint8 vnvl, uint8 SIZE, uint16 FLG, uint16 USN, uint16 ADDR)
{
	vif::Code(0x60|m<<4|vnvl, SIZE, FLG<<15 | USN<<14 | ((vu1::Loc+ADDR)&0x03FF));
	UnpackSize = SIZE;
}





//--------------------------------------------------------------
//		V I F C O D E S   ( B E G I N - E N D   S T Y L E )
//--------------------------------------------------------------


// Auto-size version of DIRECT.
// Alignment necessary because data must align to 128-bit boundary.

inline void vif::BeginDIRECT(void)
{
	dma::Align(12,16);
	pVifCode = dma::pLoc;
	vif::Code(0x50, 0, 0);
}



// Patch up the IMMEDIATE field of a DIRECT generated using BeginDIRECT().
// Alignment necessary because vif will interpret till 16-byte boundary as data.

inline void vif::EndDIRECT(void)
{
	dma::Align(0,16);
	((uint16 *)pVifCode)[0] = (dma::pLoc - pVifCode - 4)/16;
}



// Auto-size version of DIRECTHL.
// Alignment necessary because data must align to 16-byte boundary.

inline void vif::BeginDIRECTHL(void)
{
	dma::Align(12,16);
	pVifCode = dma::pLoc;
	vif::Code(0x51, 0, 0);
}



// Patch up the IMMEDIATE field of a DIRECTHL generated using BeginDIRECTHL().
// Alignment necessary because vif will interpret till 16-byte boundary as data.

inline void vif::EndDIRECTHL(void)
{
	dma::Align(0,16);
	((uint16 *)pVifCode)[0] = (dma::pLoc - pVifCode - 4)/16;
}



// Auto-size version of MPG.
// Alignment necessary because data must align to 8-byte boundary.

inline void vif::BeginMPG(uint16 LOADADDR)
{
	dma::Align(4,8);
	pVifCode = dma::pLoc;
	vif::Code(0x4A, 0, LOADADDR);
}



// Patch up the NUM field of an MPG generated using BeginMPG().
// Alignment necessary because vif will interpret till 8-byte boundary as data.

inline void vif::EndMPG(void)
{
	dma::Align(0,8);
	pVifCode[2] = (dma::pLoc - pVifCode - 4)/8;
}



// Auto-size version of UNPACK.

inline void vif::BeginUNPACK(uint8 m, uint8 vnvl, uint16 FLG, uint16 USN, uint16 ADDR)
{
	pVifCode = dma::pLoc;
	BitLength = 32 >> (vnvl & 3);
	Dimension = (vnvl>>2 & 3) + 1;
	vif::Code(0x60 | m<<4 | vnvl, 0, FLG<<15 | USN<<14 | ((vu1::Loc+ADDR)&0x03FF) );
}



// Patch up the NUM field of an UNPACK generated using BeginUNPACK().
// Alignment is necessary because some data formats won't fill out to a 4-byte boundary.

inline void vif::EndUNPACK(void)
{
	UnpackSize = ((dma::pLoc - pVifCode - 4) << 3) / (Dimension * BitLength);
	dma::Align(0,4);
	if (UnpackSize==0)
		dma::pLoc -= 4;				// no data, rewind over vifcode
	else if (UnpackSize < 256)
		pVifCode[2] = UnpackSize;	// normal usage
	else if (UnpackSize == 256)
		pVifCode[2] = 0;				// 0 represents 256
	else
	{
		printf("unpack size greater than 256\n");
		#ifdef __PLAT_NGPS__
		exit(1);
		#endif
	}
}






//-----------------------------------------------------------------
//		S T O R I N G   C O M P R E S S E D   V I F   D A T A
//-----------------------------------------------------------------


// scalars

inline void vif::StoreS_32(uint32 x)
{
	*(uint32 *)dma::pLoc = x;
	dma::pLoc += 4;
}


inline void vif::StoreS_16(uint16 x)
{
	*(uint16 *)dma::pLoc = x;
	dma::pLoc += 2;
}


inline void vif::StoreS_8(uint8 x)
{
	*dma::pLoc++ = x;
}



// 2-vectors

inline void vif::StoreV2_32(uint32 x, uint32 y)
{
	((uint32 *)dma::pLoc)[0] = x;
	((uint32 *)dma::pLoc)[1] = y;
	dma::pLoc += 8;
}


inline void vif::StoreV2_16(uint16 x, uint16 y)
{
	((uint16 *)dma::pLoc)[0] = x;
	((uint16 *)dma::pLoc)[1] = y;
	dma::pLoc += 4;
}


inline void vif::StoreV2_8(uint8 x, uint8 y)
{
	((uint8 *)dma::pLoc)[0] = x;
	((uint8 *)dma::pLoc)[1] = y;
	dma::pLoc += 2;
}



// 3-vectors

inline void vif::StoreV3_32(uint32 x, uint32 y, uint32 z)
{
	((uint32 *)dma::pLoc)[0] = x;
	((uint32 *)dma::pLoc)[1] = y;
	((uint32 *)dma::pLoc)[2] = z;
	dma::pLoc += 12;
}


inline void vif::StoreV3_16(uint16 x, uint16 y, uint16 z)
{
	((uint16 *)dma::pLoc)[0] = x;
	((uint16 *)dma::pLoc)[1] = y;
	((uint16 *)dma::pLoc)[2] = z;
	dma::pLoc += 6;
}


inline void vif::StoreV3_8(uint8 x, uint8 y, uint8 z)
{
	((uint8 *)dma::pLoc)[0] = x;
	((uint8 *)dma::pLoc)[1] = y;
	((uint8 *)dma::pLoc)[2] = z;
	dma::pLoc[2] = z;
	dma::pLoc += 3;
}



// 4-vectors

inline void vif::StoreV4_32F(float x, float y, float z, float w)
{
	((float *)dma::pLoc)[0] = x;
	((float *)dma::pLoc)[1] = y;
	((float *)dma::pLoc)[2] = z;
	((float *)dma::pLoc)[3] = w;
	dma::pLoc += 16;
}


inline void vif::StoreV4_32(sint32 x, sint32 y, sint32 z, sint32 w)
{
	((sint32 *)dma::pLoc)[0] = x;
	((sint32 *)dma::pLoc)[1] = y;
	((sint32 *)dma::pLoc)[2] = z;
	((sint32 *)dma::pLoc)[3] = w;
	dma::pLoc += 16;
}


inline void vif::StoreV4_16(sint16 x, sint16 y, sint16 z, sint16 w)
{
	((sint16 *)dma::pLoc)[0] = x;
	((sint16 *)dma::pLoc)[1] = y;
	((sint16 *)dma::pLoc)[2] = z;
	((sint16 *)dma::pLoc)[3] = w;
	dma::pLoc += 8;
}


inline void vif::StoreV4_8(sint8 x, sint8 y, sint8 z, sint8 w)
{
	((sint8 *)dma::pLoc)[0] = x;
	((sint8 *)dma::pLoc)[1] = y;
	((sint8 *)dma::pLoc)[2] = z;
	((sint8 *)dma::pLoc)[3] = w;
	dma::pLoc += 4;
}


inline void vif::StoreV4_5(uint8 R, uint8 G, uint8 B, uint8 A)
{
	*(uint16 *)dma::pLoc = (A&0x80)<<8 | (B&0xF8)<<7 | (G&0xF8)<<2 | (R&0xF8)>>3;
	dma::pLoc += 2;
}




} // namespace NxPs2


#endif // __VIF_H



================================================
FILE: Code/Gfx/NGPS/NX/vu0code.dsm
================================================

.global MPGStart0
.global MPGEnd0

.global	TestFunc
.global	InitialiseOccluders
.global TestSphereAgainstOccluders
.global RayTriangleCollision
.global BatchRayTriangleCollision
.global ViewCullTest
.global OuterCullTest
.global BothCullTests

; align to a 2^4=16 byte boundary, so it can be the target of a dma::ref
.align	4

MPGStart0:

MPG 0, *


;-----------------------------------------------------------------------------------------------------------------------------


TestFunc:

		; just a little test func to make sure I understand how to communicate with a vu0 microprogram

		; integer register test
		NOP								IADD		VI02,VI01,VI01
		
		; float register test
		ADDw		VF02,VF01,VF00w		NOP
		
		; memory test
		NOP								LQ			VF03,0(VI00)
		ADDw		VF03,VF03,VF00w		NOP
		
		; quit
		NOP[E]							NOP
		NOP								NOP



InitialiseOccluders:

		; this function will be optimised later... not a priority now since only called once per frame (per camera)

		; assumes an initial state where the 5 planes of occluder 0 are at addresses 0,1,2,3,4
		; occluder 1 at 5,6,7,8,9, etc, and the number of occluders is in VI01

		
		; get address of 1st spare occluder
		NOP								IADD		VI05,VI01,VI01
		NOP								IADD		VI05,VI05,VI05
		NOP								IADD		VI05,VI05,VI01
		
		; set up a sentinel plane that's colossally far away
		NOP								LOI			-1.0e+35
		MULi		VF01,VF00,I			NOP
		
		; add 1 sentinel occluder (5 sentinel planes)
		NOP								SQI			VF01,(VI05++)
		NOP								SQI			VF01,(VI05++)
		NOP								SQI			VF01,(VI05++)
		NOP								SQI			VF01,(VI05++)
		NOP								SQI			VF01,(VI05++)
		
		; rearrange memory
		NOP								IADDIU		VI02,VI00,0
		NOP								MFIR.xyzw	VF31,VI00
		
MemLoop:
		; load 4 occluders, 5 planes each

		NOP								LQ			VF01,0(VI02)
		NOP								LQ			VF02,5(VI02)
		NOP								LQ			VF03,10(VI02)
		NOP								LQ			VF04,15(VI02)
		NOP								LQ			VF05,1(VI02)
		NOP								LQ			VF06,6(VI02)
		NOP								LQ			VF07,11(VI02)
		NOP								LQ			VF08,16(VI02)
		NOP								LQ			VF09,2(VI02)
		NOP								LQ			VF10,7(VI02)
		NOP								LQ			VF11,12(VI02)
		NOP								LQ			VF12,17(VI02)
		NOP								LQ			VF13,3(VI02)
		NOP								LQ			VF14,8(VI02)
		NOP								LQ			VF15,13(VI02)
		NOP								LQ			VF16,18(VI02)
		NOP								LQ			VF17,4(VI02)
		NOP								LQ			VF18,9(VI02)
		NOP								LQ			VF19,14(VI02)
		NOP								LQ			VF20,19(VI02)
		
		
		; transpose VF01,VF02,VF03,VF04
		
		NOP								MOVE		VF21,VF01
		NOP								MOVE		VF22,VF02
		NOP								MOVE		VF23,VF03
		
		ADDx.y		VF01,VF31,VF02x		NOP
		ADDx.z		VF01,VF31,VF03x		NOP
		ADDx.w		VF01,VF31,VF04x		NOP
		
		ADDy.x		VF02,VF31,VF21y		NOP
		ADDy.z		VF02,VF31,VF03y		NOP
		ADDy.w		VF02,VF31,VF04y		NOP
		
		ADDz.x		VF03,VF31,VF21z		NOP
		ADDz.y		VF03,VF31,VF22z		NOP
		ADDz.w		VF03,VF31,VF04z		NOP
		
		ADDw.x		VF04,VF31,VF21w		NOP
		ADDw.y		VF04,VF31,VF22w		NOP
		ADDw.z		VF04,VF31,VF23w		NOP
		
		
		; transpose VF05,VF06,VF07,VF08
		
		NOP								MOVE		VF21,VF05
		NOP								MOVE		VF22,VF06
		NOP								MOVE		VF23,VF07
		
		ADDx.y		VF05,VF31,VF06x		NOP
		ADDx.z		VF05,VF31,VF07x		NOP
		ADDx.w		VF05,VF31,VF08x		NOP
		
		ADDy.x		VF06,VF31,VF21y		NOP
		ADDy.z		VF06,VF31,VF07y		NOP
		ADDy.w		VF06,VF31,VF08y		NOP
		
		ADDz.x		VF07,VF31,VF21z		NOP
		ADDz.y		VF07,VF31,VF22z		NOP
		ADDz.w		VF07,VF31,VF08z		NOP
		
		ADDw.x		VF08,VF31,VF21w		NOP
		ADDw.y		VF08,VF31,VF22w		NOP
		ADDw.z		VF08,VF31,VF23w		NOP
		
		
		; transpose VF09,VF10,VF11,VF12
		
		NOP								MOVE		VF21,VF09
		NOP								MOVE		VF22,VF10
		NOP								MOVE		VF23,VF11
		
		ADDx.y		VF09,VF31,VF10x		NOP
		ADDx.z		VF09,VF31,VF11x		NOP
		ADDx.w		VF09,VF31,VF12x		NOP
		
		ADDy.x		VF10,VF31,VF21y		NOP
		ADDy.z		VF10,VF31,VF11y		NOP
		ADDy.w		VF10,VF31,VF12y		NOP
		
		ADDz.x		VF11,VF31,VF21z		NOP
		ADDz.y		VF11,VF31,VF22z		NOP
		ADDz.w		VF11,VF31,VF12z		NOP
		
		ADDw.x		VF12,VF31,VF21w		NOP
		ADDw.y		VF12,VF31,VF22w		NOP
		ADDw.z		VF12,VF31,VF23w		NOP
		
		
		; transpose VF13,VF14,VF15,VF16
		
		NOP								MOVE		VF21,VF13
		NOP								MOVE		VF22,VF14
		NOP								MOVE		VF23,VF15
		
		ADDx.y		VF13,VF31,VF14x		NOP
		ADDx.z		VF13,VF31,VF15x		NOP
		ADDx.w		VF13,VF31,VF16x		NOP
		
		ADDy.x		VF14,VF31,VF21y		NOP
		ADDy.z		VF14,VF31,VF15y		NOP
		ADDy.w		VF14,VF31,VF16y		NOP
		
		ADDz.x		VF15,VF31,VF21z		NOP
		ADDz.y		VF15,VF31,VF22z		NOP
		ADDz.w		VF15,VF31,VF16z		NOP
		
		ADDw.x		VF16,VF31,VF21w		NOP
		ADDw.y		VF16,VF31,VF22w		NOP
		ADDw.z		VF16,VF31,VF23w		NOP
		
		
		; transpose VF17,VF18,VF19,VF20
		
		NOP								MOVE		VF21,VF17
		NOP								MOVE		VF22,VF18
		NOP								MOVE		VF23,VF19
		
		ADDx.y		VF17,VF31,VF18x		NOP
		ADDx.z		VF17,VF31,VF19x		NOP
		ADDx.w		VF17,VF31,VF20x		NOP
		
		ADDy.x		VF18,VF31,VF21y		NOP
		ADDy.z		VF18,VF31,VF19y		NOP
		ADDy.w		VF18,VF31,VF20y		NOP
		
		ADDz.x		VF19,VF31,VF21z		NOP
		ADDz.y		VF19,VF31,VF22z		NOP
		ADDz.w		VF19,VF31,VF20z		NOP
		
		ADDw.x		VF20,VF31,VF21w		NOP
		ADDw.y		VF20,VF31,VF22w		NOP
		ADDw.z		VF20,VF31,VF23w		NOP
		
		
		; store the rearranged occluders
		
		NOP								SQI			VF04,(VI02++)
		NOP								SQI			VF01,(VI02++)
		NOP								SQI			VF02,(VI02++)
		NOP								SQI			VF03,(VI02++)
		NOP								SQI			VF08,(VI02++)
		NOP								SQI			VF05,(VI02++)
		NOP								SQI			VF06,(VI02++)
		NOP								SQI			VF07,(VI02++)
		NOP								SQI			VF12,(VI02++)
		NOP								SQI			VF09,(VI02++)
		NOP								SQI			VF10,(VI02++)
		NOP								SQI			VF11,(VI02++)
		NOP								SQI			VF16,(VI02++)
		NOP								SQI			VF13,(VI02++)
		NOP								SQI			VF14,(VI02++)
		NOP								SQI			VF15,(VI02++)
		NOP								SQI			VF20,(VI02++)
		NOP								SQI			VF17,(VI02++)
		NOP								SQI			VF18,(VI02++)
		NOP								SQI			VF19,(VI02++)
		
		
		; loop if we haven't just processed the sentinel planes
		
		NOP								ISUB		VI01,VI02,VI05
		NOP								NOP
		NOP								IBLTZ		VI01,MemLoop
		NOP								NOP
		
		; quit
		NOP[E]							NOP
		NOP								NOP
		
		
;-----------------------------------------------------------------------------------------------------------------------------


TestSphereAgainstOccluders:		

		; sphere is in VF07 as (x,y,z,R)
		; for each plane p, we must compute px*x + py*w + pz*z + pw + R
		; uses a sentinel occluder to terminate

		NOP								IADDIU		VI02,VI00,0			; initialise pointer

		; loop prologue
		ADD.y		VF05,VF00,VF00		LQI			VF04,(VI02++)
		NOP								LQI			VF01,(VI02++)
		NOP								LQI			VF02,(VI02++)
		NOP								LQI			VF03,(VI02++)
		ADDAw		ACC,VF04,VF07w		LQI			VF04,(VI02++)
		MADDAx		ACC,VF01,VF07x		LQI			VF01,(VI02++)
		MADDAy		ACC,VF02,VF07y		LQI			VF02,(VI02++)
		MADDz		VF00,VF03,VF07z		IADDIU		VI10,VI00,0xF0		; VI10 = mask for Sxyzw FMAC flags
		
		; main loop, tests 4 occluders (20 planes) per pass
Loop:	ADDw.y		VF05,VF05,VF00w		LQI			VF03,(VI02++)
		ADDAw		ACC,VF04,VF07w		LQI			VF04,(VI02++)
		MADDAx		ACC,VF01,VF07x		LQI			VF01,(VI02++)
		MADDAy		ACC,VF02,VF07y		FMAND		VI01,VI10
		MADDz		VF00,VF03,VF07z		LQI			VF02,(VI02++)
		ADDw.y		VF05,VF05,VF00w		LQI			VF03,(VI02++)
		ADDAw		ACC,VF04,VF07w		LQI			VF04,(VI02++)
		MADDAx		ACC,VF01,VF07x		LQI			VF01,(VI02++)
		MADDAy		ACC,VF02,VF07y		FMAND		VI01,VI01
		MADDz		VF00,VF03,VF07z		LQI			VF02,(VI02++)
		ADDw.y		VF05,VF05,VF00w		LQI			VF03,(VI02++)
		ADDAw		ACC,VF04,VF07w		LQI			VF04,(VI02++)
		MADDAx		ACC,VF01,VF07x		LQI			VF01,(VI02++)
		MADDAy		ACC,VF02,VF07y		FMAND		VI01,VI01
		MADDz		VF00,VF03,VF07z		LQI			VF02,(VI02++)
		ADDw.y		VF05,VF05,VF00w		LQI			VF03,(VI02++)
		ADDAw		ACC,VF04,VF07w		LQI			VF04,(VI02++)
		MADDAx		ACC,VF01,VF07x		LQI			VF01,(VI02++)
		MADDAy		ACC,VF02,VF07y		FMAND		VI01,VI01
		MADDz		VF00,VF03,VF07z		LQI			VF02,(VI02++)
		FTOI0.y		VF06,VF05			LQI			VF03,(VI02++)
		ADDAw		ACC,VF04,VF07w		LQI			VF04,(VI02++)
		MADDAx		ACC,VF01,VF07x		LQI			VF01,(VI02++)
		MADDAy		ACC,VF02,VF07y		FMAND		VI01,VI01
		NOP								IBEQ		VI01,VI00,Loop
		MADDz		VF00,VF03,VF07z		LQI			VF02,(VI02++)
		
		; (macflags & 0x80) => possible occluder in 1st slot of group of 4
		NOP								ISUBIU		VI10,VI01,0x80
		NOP								MTIR		VI06,VF06y
		NOP								IBGEZ		VI10,TestForSentinel
		NOP								ISUBIU		VI03,VI02,22
		
		; (macflags & 0x40) => possible occluder in 2nd slot of group of 4
		NOP								ISUBIU		VI10,VI01,0x40
		NOP								IADDIU		VI06,VI06,1
		NOP								IBGEZ		VI10,TestForSentinel
		NOP								ISUBIU		VI03,VI02,17
		
		; (macflags & 0x20) => possible occluder in 3rd slot of group of 4
		NOP								ISUBIU		VI10,VI01,0x20
		NOP								IADDIU		VI06,VI06,1
		NOP								IBGEZ		VI10,TestForSentinel
		NOP								ISUBIU		VI03,VI02,12
		
		; (macflags & 0x10) => possible occluder in 4th slot of group of 4
		NOP								IADDIU		VI06,VI06,1
		NOP								ISUBIU		VI03,VI02,7
		
TestForSentinel:
		; sentinel if VI03==VI05, else occluded
		NOP[E]							ISUB		VI01,VI03,VI05
		NOP								ISUBIU		VI06,VI06,4
		
;-----------------------------------------------------------------------------------------------------------------------------

BatchRayTriangleCollision:
		NOP								ILW.y		VI04, 0(VI00)		; total number of collision tests
		NOP								ILW.x		VI05, 0(VI00)		; starting index

		NOP                 			IADDIU 		VI01, VI00, 0		; init number of found collisions
		NOP                 			IADDIU 		VI03, VI00, 1		; init address
		NOP                 			IADD 		VI06, VI00, VI03	; init output address (writes over input)

		NOP								ISUB		VI04, VI04, VI05	; decrement the start index from the array size

		NOP								LQ			VF06, 2(VI03)		; v0
		NOP								LQ			VF08, 4(VI03)		; v2
		NOP								LQ			VF07, 3(VI03)		; v1
		NOP								LQ			VF04, 0(VI03)		; rayStart
		NOP								LQ			VF05, 1(VI03)		; rayDir
   
NextCollision:
		SUB.xyz     VF10, VF08, VF06	NOP							   	; edge2 = v2 - v0
		SUB.xyz     VF09, VF07, VF06	MFIR.w		VF17w, VI05   		; edge1 = v1 - v0  ; store index in VF17w (output)
		SUB.xyz     VF13, VF04, VF06	FSSET 0							; tvec = rayStart - v0
		ADDW.x      VF02, VF00, VF00w	LOI 1.00001						; Load 1.0f to VF02x ; load 1 + EPSILON into I

		OPMULA.xyz  ACC, VF05, VF10		NOP								; Cross product of ray normal and edge2 (pvec)
		OPMSUB.xyz  VF12,VF10, VF05		NOP								; Second part of cross product
		OPMULA.xyz  ACC, VF13, VF09     IADDIU      VI14, VI00, 0x80    ; qvec = crossProd(tvec, edge1) ; Set the mask X sign flag
		OPMSUB.xyz  VF14,VF09, VF13     IADDIU      VI13, VI00, 0x10	; Set the mask W sign flag
		SUBi.x		VF03,VF00, I		NOP								; put -(1 + EPSILON) in VF03x

		MUL.xyz     VF11,VF09, VF12		NOP								; det = edge1 * pvec [start]
		MUL.xyz     VF15,VF13, VF12		NOP								; u = tvec * pvec [start]
		MULAx.x     ACC, VF09, VF12x	IADDI		VI04, VI04, -1		; decrement number of collision tests
		MADDAy.x    ACC, VF02, VF11y	IADDIU 		VI03, VI03, 5		; increment address
		MADDz.x     VF11,VF02, VF11z	NOP								; det = edge1 * pvec [ready]
		MULAx.x     ACC, VF02, VF15x	LOI 0.00001						; load 1 + EPSILON into I
		MADDAy.x    ACC, VF02, VF15y	LQ			VF04, 0(VI03)		; next rayStart
		MUL.xyz		VF17,VF10, VF14		NOP								; t = edge2 * qvec [start]
		SUBi.x      VF01,VF11, I  		DIV 	    Q,   VF00w,VF11x	; If det < EPSILON ; Q = 1.0f / det

		MADDz.x     VF15,VF02, VF15z	LQ			VF06, 2(VI03)		; u = tvec * pvec [ready] ; next v0
		MULAx.x		ACC, VF10, VF14x	LQ			VF08, 4(VI03)		; next v2
		MADDAy.x	ACC, VF02, VF17		LQ			VF07, 3(VI03)		; next v1
        MUL.xyz		VF16,VF05, VF14     FMAND 		VI15,VI14 			; v = rayDir * qvec [start] ; check if det result (MAC register)
        MADDz.x		VF17,VF02, VF17z 	IBNE 		VI15,VI00,BatchRayTriDone	; t = edge2 * qvec [ready]
		MULAx.x		ACC, VF05, VF14x	LQ			VF05, 1(VI03)		; next rayDir
		MULQ.x		VF15,VF15, Q        NOP								; u = (tvec * pvec) / det
		MADDAy.x	ACC, VF02, VF16		NOP
		MADDz.x		VF16,VF02, VF16z    NOP								; v = rayDir * qvec [ready]
		NOP								NOP								

		ADDx.x		VF00,VF15, VF03x	FMAND 		VI15,VI14			; if u > 1 + EPSILON ; check if u result (MAC register)  
		NOP								IBNE		VI15,VI00,BatchRayTriDone
		MULQ.x		VF16,VF16, Q		NOP								; v = (rayDir * qvec) / det;
		MULQ.x		VF17,VF17, Q        NOP								; t = (edge2 * qvec) / det
        NOP								FMAND 		VI15,VI14      		; check if u > 1 + EPSILON result (MAC register)
        NOP							    IBEQ		VI15,VI00,BatchRayTriDone

        ADDA.x		ACC ,VF15, VF16   	FMAND 		VI15,VI14      		; check if v < 0 result (MAC register)
        MADDw.x     VF31,VF03, VF00w    IBNE 		VI15,VI00,BatchRayTriDone ; if (u+v) > 1 + EPSILON [start]

		NOP								NOP
		SUB.x		VF00,VF17, VF00		NOP
		NOP								NOP
        SUBx.w		VF00,VF00, VF17 	FMAND 		VI15,VI14     		; if t > 1 [start] ; check if u+v > 1 + EPSILON result (MAC register)
		NOP								IBEQ 		VI15,VI00,BatchRayTriDone	

		NOP							    FMAND 		VI15,VI14			; check if t < 0 result (MAC register) 	  	
        NOP                             IBNE 		VI15,VI00,BatchRayTriDone

        NOP								FMAND 		VI15,VI13          	; check if t > 1 result (MAC register)
        NOP                             IBNE 		VI15,VI00,BatchRayTriDone
		NOP								NOP

		NOP								SQI.xw		VF17, (VI06++)		; store distance (x) and index (w)
		NOP                 			IADDIU 		VI01, VI01, 1		; inc number of found collisions

BatchRayTriDone:
        NOP                             IBNE 		VI04, VI00,NextCollision
		NOP								IADDIU		VI05, VI05, 1		; increment index for output

		NOP[T]							NOP
		NOP								NOP

RayTriangleCollision:

		SUB.xyz     VF10, VF08, VF06	NOP								; edge2 = v2 - v0
		SUB.xyz     VF09, VF07, VF06	FSSET 0							; edge1 = v1 - v0 
		SUB.xyz     VF13, VF04, VF06	NOP								; tvec = rayStart - v0
		ADDW.x      VF02, VF00, VF00w	LOI 1.00001						; Load 1.0f to VF02x ; load 1 + EPSILON into I

		OPMULA.xyz  ACC, VF05, VF10		NOP								; Cross product of ray normal and edge2 (pvec)
		OPMSUB.xyz  VF12,VF10, VF05		NOP								; Second part of cross product
		OPMULA.xyz  ACC, VF13, VF09     IADDIU      VI14, VI00, 0x80    ; qvec = crossProd(tvec, edge1) ; Set the mask X sign flag
		OPMSUB.xyz  VF14,VF09, VF13     IADDIU      VI13, VI00, 0x10	; Set the mask W sign flag
		SUBi.x		VF03,VF00, I		IADDIU 		VI02, VI00, 0		; put -(1 + EPSILON) in VF03x ; store 0 for return value

		MUL.xyz     VF11,VF09, VF12		NOP								; det = edge1 * pvec [start]
		MUL.xyz     VF15,VF13, VF12		NOP								; u = tvec * pvec [start]
		MULAx.x     ACC, VF09, VF12x	NOP
		MADDAy.x    ACC, VF02, VF11y	NOP
		MADDz.x     VF11,VF02, VF11z	NOP								; det = edge1 * pvec [ready]
		MULAx.x     ACC, VF02, VF15x	LOI 0.00001						; load 1 + EPSILON into I
		MADDAy.x    ACC, VF02, VF15y	NOP
		MUL.xyz		VF17,VF10, VF14		NOP								; t = edge2 * qvec [start]
		SUBi.x      VF01,VF11, I  		DIV 	    Q,   VF00w,VF11x	; If det < EPSILON ; Q = 1.0f / det

		MADDz.x     VF15,VF02, VF15z	NOP								; u = tvec * pvec [ready]
		MULAx.x		ACC, VF10, VF14x	NOP
		MADDAy.x	ACC, VF02, VF17		NOP
        MUL.xyz		VF16,VF05, VF14     FMAND 		VI15,VI14 			; v = rayDir * qvec [start] ; check if det result (MAC register)
        MADDz.x		VF17,VF02, VF17z 	IBNE 		VI15,VI00,vu0RayTriDone	; t = edge2 * qvec [ready]
		MULAx.x		ACC, VF05, VF14x	NOP
		MULQ.x		VF15,VF15, Q        NOP								; u = (tvec * pvec) / det
		MADDAy.x	ACC, VF02, VF16		NOP
		MADDz.x		VF16,VF02, VF16z    NOP								; v = rayDir * qvec [ready]
		NOP								NOP								

		ADDx.x		VF00,VF15, VF03x	FMAND 		VI15,VI14			; if u > 1 + EPSILON ; check if u result (MAC register)  
		NOP								IBNE		VI15,VI00,vu0RayTriDone
		MULQ.x		VF16,VF16, Q		NOP								; v = (rayDir * qvec) / det;
		MULQ.x		VF17,VF17, Q        NOP								; t = (edge2 * qvec) / det
        NOP								FMAND 		VI15,VI14      		; check if u > 1 + EPSILON result (MAC register)
        NOP							    IBEQ		VI15,VI00,vu0RayTriDone

        ADDA.x		ACC ,VF15, VF16   	FMAND 		VI15,VI14      		; check if v < 0 result (MAC register)
        MADDw.x     VF31,VF03, VF00w    IBNE 		VI15,VI00,vu0RayTriDone ; if (u+v) > 1 + EPSILON [start]

		NOP								NOP
		SUB.x		VF00,VF17, VF00		NOP
		NOP								NOP
        SUBx.w		VF00,VF00, VF17 	FMAND 		VI15,VI14     		; if t > 1 [start] ; check if u+v > 1 + EPSILON result (MAC register)
		NOP								IBEQ 		VI15,VI00,vu0RayTriDone	

		NOP							    FMAND 		VI15,VI14			; check if t < 0 result (MAC register) 	  	
        NOP                             IBNE 		VI15,VI00,vu0RayTriDone

        NOP								FMAND 		VI15,VI13          	; check if t > 1 result (MAC register)
        NOP                             IBNE 		VI15,VI00,vu0RayTriDone
		NOP								NOP

	;qmfc2	$9, $vf17			 # move t to $9
	;sw		$9, 0(%1)			 # and write out
		NOP								IADDIU 		VI02, VI00, 1		; store 1 for return value

vu0RayTriDone:

		NOP[E]							NOP
		NOP								NOP


;-----------------------------------------------------------------------------------------------------------------------------
; new bounding volume / frustum intersection tests


		; VF08 = (x0,y0,z0,r)
		; VF09 = (bx,by,bz,?)
		
		; VF10 = (nx)
		; VF11 = (ny)
		; VF12 = (nz)
		; VF13 = (|nx|)
		; VF14 = (|ny|)
		; VF15 = (|nz|)
		; VF16 = (nw)
		
		; VF17 = (nx)
		; VF18 = (ny)
		; VF19 = (nz)
		; VF20 = (|nx|)
		; VF21 = (|ny|)
		; VF22 = (|nz|)
		; VF23 = (nw)
		
		; VF24 = (nx)
		; VF25 = (ny)
		; VF26 = (nz)
		; VF27 = (|nx|)
		; VF28 = (|ny|)
		; VF29 = (|nz|)
		; VF30 = (nw)
		
		; VF31 = (1)

		
ViewCullTest:
		
		ADDAx		ACC,VF16,VF00x		NOP
		MADDAx		ACC,VF10,VF08x		NOP
		MADDAy		ACC,VF11,VF08y		NOP
		MADDAz		ACC,VF12,VF08z		NOP
		MADDw		VF00,VF31,VF08w		NOP
		MADDAx		ACC,VF13,VF09x		NOP
		MADDAy		ACC,VF14,VF09y		NOP
		MADDz		VF00,VF15,VF09z		NOP
		
		ADDAx.xy	ACC,VF23,VF00x		FMOR		VI01,VI00
		MADDAx.xy	ACC,VF17,VF08x		NOP
		MADDAy.xy	ACC,VF18,VF08y		NOP
		MADDAz.xy	ACC,VF19,VF08z		FMOR		VI01,VI01
		MADDw.xy	VF00,VF31,VF08w		NOP
		MADDAx.xy	ACC,VF20,VF09x		NOP
		MADDAy.xy	ACC,VF21,VF09y		NOP
		MADDz.xy	VF00,VF22,VF09z		NOP
		
		NOP								FMOR		VI01,VI01
		NOP								NOP
		NOP								NOP
		NOP[E]							FMOR		VI01,VI01
		NOP								NOP
		

OuterCullTest:

		ADDAx.zw	ACC,VF23,VF00x		NOP
		MADDAx.zw	ACC,VF17,VF08x		NOP
		MADDAy.zw	ACC,VF18,VF08y		NOP
		MADDAz.zw	ACC,VF19,VF08z		NOP
		MSUBw.zw	VF00,VF31,VF08w		NOP
		MSUBAx.zw	ACC,VF20,VF09x		NOP
		MSUBAy.zw	ACC,VF21,VF09y		NOP
		MSUBz.zw	VF00,VF22,VF09z		NOP
		
		ADDAx		ACC,VF30,VF00x		FMOR		VI01,VI00
		MADDAx		ACC,VF24,VF08x		NOP
		MADDAy		ACC,VF25,VF08y		NOP
		MADDAz		ACC,VF26,VF08z		FMOR		VI01,VI01
		MSUBw		VF00,VF31,VF08w		NOP
		MSUBAx		ACC,VF27,VF09x		NOP
		MSUBAy		ACC,VF28,VF09y		NOP
		MSUBz		VF00,VF29,VF09z		NOP
		
		NOP								FMOR		VI01,VI01
		NOP								NOP
		NOP								NOP
		NOP[E]							FMOR		VI01,VI01
		NOP								NOP


BothCullTests:
		
		MULAx.zw	ACC,VF20,VF09x		NOP
		MADDAy.zw	ACC,VF21,VF09y		NOP
		MADDz.zw	VF01,VF22,VF09z		NOP
		
		ADDAx		ACC,VF16,VF00x		NOP
		MADDAx		ACC,VF10,VF08x		NOP
		MADDAy		ACC,VF11,VF08y		NOP
		MADDAz		ACC,VF12,VF08z		NOP
		MADDw		VF00,VF31,VF08w		NOP
		MADDAx		ACC,VF13,VF09x		NOP
		MADDAy		ACC,VF14,VF09y		NOP
		MADDz		VF00,VF15,VF09z		NOP
		
		ADDAx.zw	ACC,VF23,VF00x		FMOR		VI01,VI00
		MADDAx.zw	ACC,VF17,VF08x		NOP
		MADDAy.zw	ACC,VF18,VF08y		NOP
		MADDAz.zw	ACC,VF19,VF08z		FMOR		VI01,VI01
		MADDx.z		VF05,VF00,VF00x		NOP
		MADDw.zw	VF00,VF31,VF08w		NOP
		MADDw.zw	VF00,VF01,VF00w		NOP
		MSUBw.zw	VF00,VF31,VF08w		NOP
		MSUBw.zw	VF00,VF01,VF00w		NOP
		
		ADDAx		ACC,VF30,VF00x		FMOR		VI01,VI01
		MADDAx		ACC,VF24,VF08x		FMOR		VI01,VI01
		MADDAy		ACC,VF25,VF08y		FMOR		VI11,VI00
		MADDAz		ACC,VF26,VF08z		FMOR		VI11,VI11
		MSUBw		VF00,VF31,VF08w		NOP
		MSUBAx		ACC,VF27,VF09x		NOP
		MSUBAy		ACC,VF28,VF09y		NOP
		MSUBz		VF00,VF29,VF09z		NOP
		
		SUBz.x		VF05,VF00,VF05z		FMOR		VI11,VI11
		NOP								NOP
		NOP								NOP
		NOP								FMOR		VI11,VI11
		
		NOP[E]							NOP
		NOP								NOP



;-----------------------------------------------------------------------------------------------------------------------------


.EndMPG

MPGEnd0:


================================================
FILE: Code/Gfx/NGPS/NX/vu0code.h
================================================
#ifndef __VU0CODE_H
#define __VU0CODE_H

extern uint MPGStart0[8]; 		// Mick:  The [8] is a patch to make the compiler not assume it is in ldata
extern uint MPGEnd0[8];

extern uint TestFunc;
extern uint InitialiseOccluders;
extern uint TestSphereAgainstOccluders;
extern uint RayTriangleCollision;
extern uint BatchRayTriangleCollision;
extern uint ViewCullTest;
extern uint OuterCullTest;
extern uint BothCullTests;

#endif //__VU0CODE_H



================================================
FILE: Code/Gfx/NGPS/NX/vu1/defs.vsm
================================================

		.equr	zero,	VF00x
		.equr	one,	VF00w




================================================
FILE: Code/Gfx/NGPS/NX/vu1/jumptab.vsm
================================================

JumpTable:
; load at micromem address 0

		; initially the branch addresses will be static
		; but they can later be written dynamically by a vu code caching system
		NOP								B			Init
		NOP								NOP
		NOP								B			ProcessPacket
		NOP								NOP
		NOP								B			VU1Context
		NOP								NOP
		NOP								B			GSContext
		NOP								NOP
		NOP								B			SpritesInit
		NOP								NOP
		NOP								B			Sprites
		NOP								NOP
		;NOP								B			ScreenAlignedBillboards
		;NOP								NOP
		;NOP								B			AxialBillboards
		;NOP								NOP



================================================
FILE: Code/Gfx/NGPS/NX/vu1/main.vsm
================================================

;	ͻ
;	 tag format 
;	ͼ
;
;	This follows GIFtag format (EE User's Manual section 7.2.2), but with some added fields.
;
;
;
;	 31 30                   23 22                16 15 14                                         0
;	Ŀ
;	0      STEP exponent      STEP mantissa      EOP                   NLOOP                    
;	
;
;
;	 63       60 59 58 57                            47 46 45    43 42                            32
;	Ŀ
;	   NREG     FLG               PRIM              PRE                      ADDR              
;	
;
;
;	 95                                                       76 75       72 71       68 67       64
;	Ŀ
;	                                                     ...     [REG2]     [REG1]      REG0    
;	
;
;
;	 127                                         112 111                                          96
;	Ŀ
;	                                                                     SIZE                     
;	
;
;
;


Init:	NOP[E]							IADDIU		VI02,VI00,0			; initialise VI02
		NOP								NOP

ProcessPacket:
		NOP								LQI			VF01,(VI02++)		; get 1st tag
		NOP								ISUBIU		VI13,VI02,1			; VI13 = start address of current packet
		NOP								IADDIU		VI15,VI00,0			; set EOP=0
		NOP								XITOP		VI14				; get run-time render flags from VIF1_ITOP

MainLoop:
		NOP								IBLTZ		VI15,KickPacket		; kick if EOP==1
		ADDw		VF02,VF01,VF00w		MTIR		VI01,VF01y			; VI01 = ADDR, jumptable address. 'ADDw' is for extracting STEP
		NOP								MTIR		VI15,VF01x			; VI15 = EOP:NLOOP
		NOP								MTIR		VI06,VF01w			; VI06 = SIZE = NREG*NLOOP, size of prim (excl. tag)
		NOP								IADD		VI05,VI02,VI06		; VI05 = end pointer for prim
		NOP								JR			VI01				; jump via jumptable
		NOP								MTIR		VI04,VF02x			; VI04 = STEP (branch delay slot)


KickPacket:
		; kick and stop
		NOP[E]							XGKICK		VI13				; kick the processed packet to the GS
		NOP								ISUBIU		VI02,VI02,1			; undo last postincrement (so VI02 points to next packet)


NextPrim:
		NOP								LQI			VF01,(VI02++)		; get next tag
		NOP								B			MainLoop			; loop
		NOP								NOP




; SIZE field must be NREG*NLOOP - 1
; STEP mantissa must generate the value n*3-27 where VFn is the first reg in the context


VU1Context:

		NOP								IADDIU		VI01,VI04,VU1Context; eventually optimise this away and just do...
		NOP								JR			VI01				; JR VI04
		SUBA.w		ACC,VF00,VF00		LOI			0x44800000			; 2^10


		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF10,(VI02++)
		NOP								LOI			0x45000000			; 2^11
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF11,(VI02++)
		NOP								LOI			0x45800000			; 2^12
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF12,(VI02++)
		NOP								LOI			0x46000000			; 2^13
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF13,(VI02++)
		NOP								LOI			0x46800000			; 2^14
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF14,(VI02++)
		NOP								LOI			0x47000000			; 2^15
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF15,(VI02++)
		NOP								LOI			0x47800000			; 2^16
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF16,(VI02++)
		NOP								LOI			0x48000000			; 2^17
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF17,(VI02++)
		NOP								LOI			0x48800000			; 2^18
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF18,(VI02++)
		NOP								LOI			0x49000000			; 2^19
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF19,(VI02++)
		NOP								LOI			0x49800000			; 2^20
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF20,(VI02++)
		NOP								LOI			0x4A000000			; 2^21
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF21,(VI02++)
		NOP								LOI			0x4A800000			; 2^22
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF22,(VI02++)
		NOP								LOI			0x4B000000			; 2^23
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF23,(VI02++)
		NOP								LOI			0x4B800000			; 2^24
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF24,(VI02++)
		NOP								LOI			0x4C000000			; 2^25
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF25,(VI02++)
		NOP								LOI			0x4C800000			; 2^26
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF26,(VI02++)
		NOP								LOI			0x4D000000			; 2^27
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF27,(VI02++)
		NOP								LOI			0x4D800000			; 2^28
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF28,(VI02++)
		NOP								LOI			0x4E000000			; 2^29
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF29,(VI02++)
		NOP								LOI			0x4E800000			; 2^30
		NOP								IBEQ		VI02,VI05,NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF30,(VI02++)
		NOP								LOI			0x4F000000			; 2^31
		NOP								B			NextPrim
		MADDAi.w	ACC,VF00,I			LQI			VF31,(VI02++)
		





GSContext:
		NOP								LQ			VF01,0(VI05)
		NOP								B			MainLoop
		NOP								IADDIU		VI02,VI05,1



================================================
FILE: Code/Gfx/NGPS/NX/vu1/newvu1code.dsm
================================================

.global NewMPGStart
.global NewMPGEnd

.include "defs.vsm"

; align to a 2^7=128 byte boundary for faster dma transfer
.align	7

NewMPGStart:

.DmaData ParticleVU1CodeStart

	MPG 0, *
	
		.include "jumptab.vsm"
		.include "main.vsm"
		.include "particle.vsm"
		;.include "billboard.vsm"

	.EndMPG
	
.EndDmaData
NewMPGEnd:

; force rebuild



================================================
FILE: Code/Gfx/NGPS/NX/vu1/newvu1code.h
================================================
#ifndef __NEWVU1CODE_H
#define __NEWVU1CODE_H

#ifdef __PLAT_NGPS__

extern uint NewMPGStart       __attribute__((section(".vudata")));
extern uint NewMPGEnd         __attribute__((section(".vudata")));

#endif

#endif //__NEWVU1CODE_H



================================================
FILE: Code/Gfx/NGPS/NX/vu1/particle.vsm
================================================

; -----------------
; VU1 PARTICLE CODE
; -----------------


; to do:
; - lift restriction on number of particles per system (ie allow one system to span multiple VU1 'buffers')
; - cyclic paths?


; notes:
; the arrangement of VU1 memory accesses should be done in consideration of the low amount of VIF1 activity per system


; VU1's (crap) random number generator uses this algorithm:
;
; R = 0x3F800000		|
;     R<<1 & 0x007FFFFF	|
;     (R>>4 ^ R>>22) & 1
;
; This is augmented by a few xor's and a history of 4 seeds (which we sort of need anyway, to do random vectors),
; to give fairly decent random numbers. If VF13 holds the 4-vector of seeds, the following code steps to the next
; set of 4 values:
;
;		RNEXT.x		VF13,R
;		RXOR		R,VF13y
;		RGET.y		VF13,R
;		RXOR		R,VF13z
;		RGET.z		VF13,R
;		RXOR		R,VF13w
;		RGET.w		VF13,R
;
; But this code is also efficiently emulated on the cpu, which is crucial.



GS_BUF0 = 512
GS_BUF1 = 768
OUT_MAXNUM = 42




; output sprite format is (STQ,RGBA,XYZ2,UV,XYZ2)


		; per stream
		.equr	t,		VF09			; (t,dt,num_particles,?,)
		.equr	seed,	VF10			; (Rx,Ry,Rz,Rw)
		
		; per system
		.equr	uv1,	VF11			; (u1,v1,?,?)
		.equr	uv0,	VF12			; (u0,v0,?,?)
		.equr	p0,		VF13			; t^0 position coefficient
		.equr	p1,		VF14			; t^1 position coefficient
		.equr	p2,		VF15			; t^2 position coefficient
		.equr	s0,		VF17			; t^0 spread coefficient
		.equr	s1,		VF18			; t^1 spread coefficient
		.equr	s2,		VF19			; t^2 spread coefficient
		.equr	c0,		VF21			; t^0 colour coefficient
		.equr	c1,		VF22			; t^1 colour coefficient
		.equr	c2,		VF23			; t^1 colour coefficient
		.equr	tag,	VF24			; GIFtag for particle GS packets

		; constant
		.equr	kvec,	VF25			; scale vec (kx,ky,0,0)
		.equr	NTLvec,	VF26			; near-top-left cull testing vec
		.equr	BRvec,	VF27			; far-bottom-right cull testing vec
		.equr	mat0,	VF28			; view matrix row 0
		.equr	mat1,	VF29			; view matrix row 1
		.equr	mat2,	VF30			; view matrix row 2
		.equr	mat3,	VF31			; view matrix row 3

		; integer regs
		.equr	Input,	VI02
		.equr	Output,	VI03
		.equr	Flip,	VI12
		.equr	Packet,	VI13
		


		.scope


SpritesInit:

		; get input buffer address
		NOP								XTOP		Input
		
		; load constant data
		NOP								LQI			mat0,  (Input++)	; view matrix row 0
		NOP								LQI			mat1,  (Input++)	; view matrix row 1
		NOP								LQI			mat2,  (Input++)	; view matrix row 2
		NOP								LQI			mat3,  (Input++)	; view matrix row 3
		NOP								LQI			kvec,  (Input++)	; scale vec (kx,ky,0,0)
		NOP								LQI			NTLvec,(Input++)	; near-top-left cull testing vec
		NOP								LQI			BRvec,(Input++)		; bottom-right cull testing vec

		NOP[E]							IADDIU		Packet,VI00,GS_BUF0
		NOP								IADDIU		Flip,Packet,GS_BUF1



Sprites:

		NOP								NOP
		NOP								NOP

		; get input buffer address
		NOP								XTOP		Input
		
		; kick GS context and step over it
		NOP								ILW.x		VI01,0(Input)
		NOP								XGKICK		Input
		NOP								ISUBIU		Input,Input,0x7FFF
		NOP								IADD		Input,Input,VI01

		; load the context data for the system
		NOP								LQI			uv0,(Input++)		; (u0,v0,u1,v1)
		NOP								LQI			p0, (Input++)		; t^0 position coefficient
		NOP								LQI			p1, (Input++)		; t^1 position coefficient
		NOP								LQI			p2, (Input++)		; t^2 position coefficient
		NOP								LQI			s0, (Input++)		; t^0 spread coefficient
		NOP								LQI			s1, (Input++)		; t^1 spread coefficient
		NOP								LQI			s2, (Input++)		; t^2 spread coefficient
		NOP								LQI			c0, (Input++)		; 
		NOP								LQI			c1, (Input++)		; 
		NOP								LQI			c2, (Input++)		; 
		NOP								LQI			tag,(Input++)		; GIFtag for particle GS packets


		; a little bit of reformatting
		NOP								MR32		uv1,uv0
		NOP								MR32		uv1,uv1

		; copy tag to packet buffers
		NOP								SQ			tag,GS_BUF0(VI00)
		NOP								SQ			tag,GS_BUF1(VI00)

@StreamLoop:

		; load stream data
		NOP								LQI			t,(Input++)			; t_oldest, dt, num_particles, terminator
		NOP								LQI			seed,(Input++)

		; seed initialisation
		NOP								RINIT		R,seed.w

		; To start with, just pack each buffer full until we run out. This will usually end with a half-full buffer.
		; Later we can work out the ideal packet size to even out the cost.
		
		; get num particles
		NOP								MTIR		VI06,t.z

@PacketLoop:

		; set output pointer
		NOP								IADDIU		Output,Packet,1
		
		; see if they'll all fit
		NOP								ISUBIU		VI01,VI06,OUT_MAXNUM
		NOP								NOP
		NOP								IBGTZ		VI01,@WontFit
		NOP								NOP

@WillFit:
		NOP								B			@Reduce
		NOP								IADDIU		VI05,VI06,0

@WontFit:
		NOP								IADDIU		VI05,VI00,OUT_MAXNUM

@Reduce:
		NOP								ISUB		VI06,VI06,VI05
		NOP								IBEQ		VI05,VI00,@NextStream
		NOP								IADDIU		VI01,VI05,0x4000
		NOP								IADDIU		VI01,VI01,0x4000
		NOP								ISW.x		VI01,0(Packet)

@ParticleLoop:

		; generate new seed vector
		NOP								RNEXT.x		seed,R
		NOP								RXOR		R,seed.y
		NOP								RGET.y		seed,R
		NOP								RXOR		R,seed.z
		NOP								RGET.z		seed,R
		NOP								RXOR		R,seed.w
		NOP								RGET.w		seed,R

		; compute p0+s0*seed + t(p1+s1*seed) + t^2(p2+s2*seed) + t^3(p3+s3*seed)
		; = (((p3+s3*seed)t + p2+s2*seed)t + p1+s1*seed)t + p0+s0*seed

		ADDAx		ACC,p2,zero			NOP
		MADD		VF02,s2,seed		NOP
		NOP								NOP
		ADDAx		ACC,p1,zero			NOP
		MADDA		ACC,s1,seed			NOP
		MADDx		VF02,VF02,t.x		NOP
		NOP								NOP
		ADDAx		ACC,p0,zero			NOP
		MADDA		ACC,s0,seed			NOP
		MADDx		VF02,VF02,t.x		NOP
		
		; VF02 = (x,y,z,r)
		
		; transform and add corner offsets
		ADDAx		ACC,mat3,zero		NOP
		MADDAx		ACC,mat0,VF02x		NOP
		MADDAy		ACC,mat1,VF02y		NOP
		MADDAz		ACC,mat2,VF02z		NOP
		MSUBw		VF01,kvec,VF02w		NOP
		MADDw.xyz	VF02,kvec,VF02w		NOP
		
		; VF01 = (x'-kx*r, y'-ky*r, z', w)
		; VF02 = (x'+kx*r, y'+ky*r, z', r)
		
		; calc 1/w
		NOP								DIV			Q,one,VF01w
		NOP								WAITQ
		
		; prepare for cull tests, move r to NTL vec
		NOP								MOVE.w		NTLvec,VF02
		
		; generate screen coords of corners
		MULq.xyz	VF01,VF01,Q			NOP
		MULq		VF02,VF02,Q			NOP

		; VF01 = (X0,Y0,Z0,w)
		; VF02 = (X1,Y1,Z1,r/w) - r/w is only used for mipmapping

		; get (GS) Q
		NOP								MR32.z		VF03,VF02

		; compute colour
		SUB.x		VF06,t,tag			NOP			; subtract mid time
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								FMOR		VI01,VI00
		NOP								IBEQ		VI01,VI00,tPos
		NOP								NOP
		
tNeg:	ADDAx		ACC,c1,zero			NOP
		MADDx		VF06,c0,VF06x		NOP
		NOP								B			tDone
		NOP								NOP
		
tPos:	ADDAx		ACC,c1,zero			NOP
		MADDx		VF06,c2,VF06x		NOP

tDone:	FTOI0		VF06,VF06			NOP

		; decrement t (remember we started with the oldest particle!)
		SUBy.x		t,t,t.y				NOP
		
		; cull tests
		SUB.xyw		VF00,VF01,NTLvec	NOP
		SUB.xy		VF00,BRvec,VF02		NOP
		NOP								NOP
		NOP								NOP
		NOP								FMOR		VI01,VI00
		NOP								FMOR		VI01,VI01
		NOP								IADDIU		VI01,VI01,0x7FFF
		NOP								MFIR.w		VF02,VI01
		
		; store stuff
		NOP								SQI.z		VF03,(Output++)
		NOP								SQI			VF06,(Output++)
		NOP								SQI.xy		uv0, (Output++)
		NOP								SQI.xyz		VF01,(Output++)
		NOP								SQI			uv1, (Output++)
		NOP								SQI			VF02,(Output++)
		
		; dec counter and loop inner
		NOP								ISUBIU		VI05,VI05,1
		NOP								NOP
		NOP								IBNE		VI05,VI00,@ParticleLoop
		NOP								NOP

		; kick and flip
		NOP								XGKICK		Packet
		NOP								ISUB		Packet,Flip,Packet

		; loop outer
		NOP								IBGTZ		VI06,@PacketLoop
		NOP								NOP
		
@NextStream:
		NOP								MTIR		VI01,t.w
		NOP								NOP
		NOP								IBGEZ		VI01,@StreamLoop
		NOP								NOP

@Done:	NOP[E]							NOP
		NOP								NOP

		.endscope


================================================
FILE: Code/Gfx/NGPS/NX/vu1.cpp
================================================
#include 
#include 
#include "mikemath.h"
#include "dma.h"
#include "vif.h"
#include "vu1.h"
#include "gif.h"
#include "gs.h"
#include "vu1code.h"

namespace NxPs2
{


//---------------------------------------------------
//		V U   P R I M   C O N S T R U C T I O N
//---------------------------------------------------


// begin

#if 0
// original version
void vu1::BeginPrim(uint FLG, uint Addr)
{
	vif::STCYCL(1,1);
	vif::BeginUNPACK(0, V4_32, FLG, SIGNED, 0);
	gif::BeginTag1(gs::NOP, 1, PACKED, 0, 0, Addr);
}
#else
// a quicker version
void vu1::BeginPrim(uint FLG, uint Addr)
{
	register uint32 *p_dest = (uint32 *)dma::pLoc;
	*p_dest++ = 0x01000101;
	vif::pVifCode = (uint8 *)p_dest;
	*p_dest++ = 0x6C000000 | FLG<<15 | vu1::Loc&0x03FF;
	gif::pTag = (uint8 *)p_dest;
	*p_dest++ = 0x34000000;
	*p_dest++ = 0x10000000 | Addr;
	*p_dest   = 0x0000000F;
	dma::pLoc = (uint8 *)(p_dest+2);
}
#endif

// end

#if 0
// original version
void vu1::EndPrim(uint Eop)
{
	vif::EndUNPACK();
	vif::UnpackSize--;			// a fudge to ignore the giftag which was inside the unpack
	gif::EndTag1(Eop);
}
#else
// a quicker version
void vu1::EndPrim(uint Eop)
{
	register uint unpack_size = (dma::pLoc - vif::pVifCode - 4) >> 4;
	Dbg_MsgAssert(unpack_size>0, ("Error: unpack size 0 not supported for vu prims\n"));
	Dbg_MsgAssert(unpack_size<=256, ("Error: unpack size greater than 256\n"));
	vif::pVifCode[2] = unpack_size;
	((uint32 *)gif::pTag)[0] |= Eop<<15 | (unpack_size-1);
	((uint32 *)gif::pTag)[3] = unpack_size - 1;
	vu1::Loc += unpack_size;
}
#endif


// output vector

void vu1::StoreVec(Vec v)
{
	vif::StoreV4_32F(v[0], v[1], v[2], v[3]);
}


// output matrix

void vu1::StoreMat(Mat m)
{
	#if 1
	// we basically need to copy 16 32 bit words
	register uint32 * source = (uint32*)&m[0][0];
	register uint32 * dest = (uint32*)dma::pLoc;
	// 16 copies
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	*dest++ = *source++;
	
	dma::pLoc = (uint8*) dest;
	
	#else
	vif::StoreV4_32F(m[0][0], m[0][1], m[0][2], m[0][3]);
	vif::StoreV4_32F(m[1][0], m[1][1], m[1][2], m[1][3]);
	vif::StoreV4_32F(m[2][0], m[2][1], m[2][2], m[2][3]);
	vif::StoreV4_32F(m[3][0], m[3][1], m[3][2], m[3][3]);
	#endif
}


// routine has a breakpoint for debugging

void vu1::BreakpointPrim(uint FLG, uint Eop)
{
	vif::UNPACK(0, V4_32, 1, FLG, SIGNED, 0);
	gif::Tag1(0, 0, 0, 0, 0, Eop, 0, VU1_ADDR(Breakpoint));
	Loc += 1;
}


//---------------------------------
//		S T A T I C   D A T A
//---------------------------------

uint vu1::Loc;
uint vu1::Buffer;
uint vu1::MaxBuffer;


} // namespace NxPs2



================================================
FILE: Code/Gfx/NGPS/NX/vu1.h
================================================
#ifndef __VU_H
#define __VU_H

#include	
#include	

// simplify VU1 call addresses
#ifdef __PLAT_NGPS__
#define VU1_ADDR(Label) ((uint)&(Label)/8)
#endif
#ifdef __PLAT_WN32__
#define VU1_ADDR(Label) (Label)
#endif

namespace NxPs2
{

class vu1
{

public:

	//-------------------------------------------
	//		S T A T I C   F U N C T I O N S
	//-------------------------------------------

	static void BeginPrim(uint FLG, uint Addr);
	static void EndPrim(uint Eop);
	static void BreakpointPrim(uint FLG, uint Eop);
	static void StoreVec(float v[4]);
	static void CopyQuads(uint32 *p0);
	static void CopyQuads(uint32 *p0,uint32 *p1);
	static void CopyQuads(uint32 *p0,uint32 *p1,uint32 *p2);
	static void CopyQuads(uint32 *p0,uint32 *p1,uint32 *p2, uint32 *p3);
	static void StoreVecAligned(float v[4]);
	static void CopyMat(float *p_mat);
	static void CopyMatAligned(float *p_mat);
	static void StoreMat(float m[4][4]);
	static void StoreMatAligned(float m[4][4]);

	//---------------------------------
	//		S T A T I C   D A T A
	//---------------------------------

	static uint Loc;
	static uint Buffer;
	static uint MaxBuffer;

}; // class vu1


#ifdef __PLAT_NGPS__

inline void asm_copy_vec(void* m0, void* m1)
{
	asm __volatile__("
	lq    $6,0x0(%1)
	sq    $6,0x0(%0)
	": : "r" (m0) , "r" (m1):"$6");
}

inline void vu1::StoreVecAligned(float v[4])
{
	asm_copy_vec(dma::pLoc,v);
	dma::pLoc += 16;
}

inline void vu1::StoreMatAligned(float m[4][4])
{
	*(Mth::Matrix*)dma::pLoc = *(Mth::Matrix*)(&m[0][0]);
	dma::pLoc += 64;
}

inline void vu1::CopyMatAligned(float *p_mat)
{
	*(Mth::Matrix*)dma::pLoc = *((Mth::Matrix*)(p_mat));
	dma::pLoc += 64;
}

inline void vu1::CopyMat(float *p_mat)
{
	uint32 *p = (uint32*)dma::pLoc;
	uint32 *p2 = (uint32*)p_mat;
	for (int i=0;i<16;i++)
		*p++ = *p2++;
	dma::pLoc = (uint8*)p;
}



inline void vu1::CopyQuads(uint32 *p0)
{
	uint32	* p = (uint32*)dma::pLoc;
	p[0] = p0[0];
	p[1] = p0[1];
	p[2] = p0[2];
	p[3] = p0[3];
	dma::pLoc = (uint8*)(p+4);
}

inline void vu1::CopyQuads(uint32 *p0, uint32 *p1)
{
	uint32	* p = (uint32*)dma::pLoc;
	p[0] = p0[0];
	p[1] = p0[1];
	p[2] = p0[2];
	p[3] = p0[3];
	p[4] = p1[0];
	p[5] = p1[1];
	p[6] = p1[2];
	p[7] = p1[3];
	dma::pLoc = (uint8*)(p+8);
}

inline void vu1::CopyQuads(uint32 *p0, uint32 *p1, uint32*p2)
{
	uint32	* p = (uint32*)dma::pLoc;
	p[0] = p0[0];
	p[1] = p0[1];
	p[2] = p0[2];
	p[3] = p0[3];
	p[4] = p1[0];
	p[5] = p1[1];
	p[6] = p1[2];
	p[7] = p1[3];
	p[8] = p2[0];
	p[9] = p2[1];
	p[10] = p2[2];
	p[11] = p2[3];
	dma::pLoc = (uint8*)(p+12);
}

inline void vu1::CopyQuads(uint32 *p0, uint32 *p1, uint32 *p2, uint32 *p3)
{
	uint32	* p = (uint32*)dma::pLoc;
	p[0] = p0[0];
	p[1] = p0[1];
	p[2] = p0[2];
	p[3] = p0[3];
	p[4] = p1[0];
	p[5] = p1[1];
	p[6] = p1[2];
	p[7] = p1[3];
	p[8] = p2[0];
	p[9] = p2[1];
	p[10] = p2[2];
	p[11] = p2[3];
	p[12] = p3[0];
	p[13] = p3[1];
	p[14] = p3[2];
	p[15] = p3[3];
	dma::pLoc = (uint8*)(p+16);
}

	
//	static void StoreQuads(uint32 *p0,uint32 *p1);
//	static void StoreQuads(uint32 *p0,uint32 *p1,uint32 *p2);
//	static void StoreQuads(uint32 *p0,uint32 *p1,uint32 *p2, uint32 *p0);



#endif



} // namespace NxPs2



#endif // __VU_H



================================================
FILE: Code/Gfx/NGPS/NX/vu1code.dsm
================================================
;
; set tab stops to 4 to read this file
;
;-----------------------------------------------------------------------------------------------------------------------------
; The result of assembling this file is a set of vif packets with vifcode MPG, used to upload
; microcode to MicroMem1, the 16K program memory of VU1. There is only one MPG assembler
; directive, but this is clever enough to split the assembled code into multiple packets when
; the maximum MPG packet size (256 64-bit instruction slots, 2K of program) is exceeded.
; There will be the need to manage multiple sets of microcode, once there's more than 16K
; total.
;
;-----------------------------------------------------------------------------------------------------------------------------
;
; Very brief overview of rendering scheme
; ---------------------------------------
;
; I decided where possible to take a 'process-in-place' approach to basic triangle rendering.
; e.g. a pair of texture coordinates loaded to address 100 in VU memory will end up being
; output as a pair of texture coordinates from the same address, on its way to the GS. This
; is efficient because it reduces the amount of data copying, as sometimes the VU doesn't
; need to touch the data - e.g. vertex colours can sometimes pass straight through without
; further processing.
;
; This means the data should be at least triple-buffered for maximum efficiency, since there
; are 3 processes that can run in parallel - sending data into memory via VIF1, processing
; the data using VU1, and sending data out through the GIF to the GS. For maximum flexibility,
; I have chosen to use VUMem1 (the VU1 data memory) as a cyclic buffer, letting the data
; packets themselves ensure that the triple-buffering rule is not violated. (This is an
; original approach - generally people use fixed buffers.)
;
; The result of processing renderable data in VU1 will be a GS packet (see EE User's Manual,
; section 7.2, "Data Format"). Each GS packet is composed of GS primitives which begin with
; a GIFtag. Following through to the max on the 'process-in-place' philosophy, I consider the
; incoming geometry data to also consist of packets made up of primitives, each with a tag.
; The tag is a superset of the GIFtag, appearing to the GIF as a valid GIFtag but making use
; of the many unused bits of the GIFtag to signal additional info to VU1, eg the size of the
; packet, the address of the microcode that should be used to process the packet, etc.
; (This is also a pretty nifty idea, although using the spare bits is common practice.)
;
; There are 3 varieties of primitive - a VU prim, containing contextual VU data that will be
; loaded into floating point VU1 registers (which doesn't need kicking to the GS), a GS prim,
; containing contextual GS data that won't be touched by the VU but will be kicked directly
; to the GS where it will set some of the GS registers, and finally a geometry prim which
; contains renderable geometry and will be processed by the rendering code into a GS prim
; that outputs something on the screen. But all varieties of prim use a common tag format so
; they can be freely mixed in the data and parsed by a common piece of code.
;
;-----------------------------------------------------------------------------------------------------------------------------
; Registers used in the main parsing loop and in the renderers (all except for the clipping code)
;
; Integer registers:
; VI00 - the constant zero register, always zero (doesn't change if you write something else to it)
; VI01 - mainly temporary values, also hardwired as the result of the FCAND, FCEQ, FCOR instructions
; VI02 - the data pointer as the prims are parsed
; VI03 - an auxilliary data pointer, so that data can be read from one vertex while writing to another
; VI04 - number of quadwords in each vertex of the current prim
; VI05 - end address for the current prim
; VI06 - size of the current prim
; VI07 - \
; VI08 - \\
; VI09 - -- temporaries
; VI10 - //
; VI11 - /
; VI12 - address of current tag
; VI13 - address of packet start
; VI14 - render flags
; VI15 - EOP:NLOOP from current tag

; VF00 - the constant register (0,0,0,1) (doesn't change if you write something else to it)
; VF01 - temporary value
; VF02 - temporary value
; VF03 - temporary value
; VF04 - temporary value
; VF05 - temporary value
; VF06 - temporary value
; VF07 - temporary value
; VF08 - temporary value
; VF09 - (Near, Far, k/(xRes/2), k/(yRes/2)) where k=viewport_scale_x, should be 2048 but is 1900 because of clipper problem
; VF10 - inverse viewport scale vector
; VF11 - inverse viewport offset vector
; VF12 - row 0, local to viewport transform
; VF13 - row 1, local to viewport transform
; VF14 - row 2, local to viewport transform
; VF15 - row 3, local to viewport transform
; VF26 - lightsource 2 colour (r,g,b,?)
; VF17 - row 0, reflection map transform
; VF18 - row 1, reflection map transform
; VF19 - row 2, reflection map transform
; VF20 - light vectors, x components
; VF21 - light vectors, y components
; VF22 - light vectors, z components
; VF23 - ambient colour (r,g,b,?)
; VF24 - lightsource 0 colour (r,g,b,?)
; VF25 - lightsource 1 colour (r,g,b,?)
; VF16 - texture projection scale vector
; VF27 - texture projection offset vector
; VF28 - saves the z-components of the view matrix during a z-push
; VF29 - \
; VF30 - - temporaries used in skinning code
; VF31 - /

;-----------------------------------------------------------------------------------------------------------------------------

CULL=0x01	; per-triangle view culling
CLIP=0x02	; full 3D clipping of triangles
SHDW=0x04	; skinned=>cast shadow into texture; non-skinned=>render mesh with projected shadow texture on it
COLR=0x08	; apply colour at vertices
FOGE=0x10	; calculate per-vertex fog coefficient
WIRE=0x20	; render skinned as wireframe (but doesn't render all edges)

;-----------------------------------------------------------------------------------------------------------------------------

; Make the very start and end of the file available to the linker, so the dma packet that
; sends the code (using a dma ref tag) can be constructed.
.global MPGStart
.global MPGEnd

; here's a list of all the entry points into the microcode so they're available to the engine
.global	Setup
.global	Jump
.global Breakpoint
.global	ParseInit
.global	Parser
.global	L_VF09
.global	L_VF10
.global	L_VF11
.global	L_VF12
.global	L_VF13
.global	L_VF14
.global	L_VF15
.global	L_VF16
.global	L_VF17
.global	L_VF18
.global	L_VF19
.global	L_VF20
.global	L_VF21
.global	L_VF22
.global	L_VF23
.global	L_VF24
.global	L_VF25
.global	L_VF26
.global	L_VF27
.global	L_VF28
.global	L_VF29
.global	L_VF30
.global	L_VF31
.global	GSPrim
.global Sprites
.global SpriteCull
.global ReformatXforms
.global ShadowVolumeSkin

; These entry points are currently not used, because the entry points are being generated in
; the scene converter which doesn't have the linker information available for the microcode.
; Instead there is a temporary jump table at the top of program memory, branching to each
; routine via fixed known locations.
.global Proj
.global PTex
.global Refl
.global Line
.global Skin

; align to a 2^4=16 byte boundary, so it can be the target of a dma::ref
.align	4

; label so the engine knows where to start dma'ing the microcode from
MPGStart:

; The MPG directive (ended by .EndMPG at the bottom of the file) is the assembler mechanism
; for constructing a vif packet using the MPG vifcode, which tells the vif to upload the
; subsequent data as vu microcode. But it's cleverer than that, because (a) it will split
; the data into multiple MPG vif packets if the maximum size for MPG is exceeded (2K), and
; (b) all labels between the MPG and the .EndMPG will be reduced so that not only are they
; relative to the start of the MPG block, but also act as if any extra MPG vifcodes inserted
; into the data didn't really exist... as if the assembler output really contained just the
; microcode and not the extra vifcodes, just like it will be when it reaches MicroMem1 (the
; program memory of VU1).

MPG 0, *

;-----------------------------------------------------------------------------------------------------------------------------
; Jump table. (This can later be eliminated with a mechanism for supplying vu1 label
; addresses to the scene converter.)

		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								B			Breakpoint
		NOP								NOP
		NOP								B			ParseInit
		NOP								NOP
		NOP								B			Parser
		NOP								NOP
		NOP								B			L_VF09
		NOP								NOP
		NOP								B			L_VF10
		NOP								NOP
		NOP								B			L_VF11
		NOP								NOP
		NOP								B			L_VF12
		NOP								NOP
		NOP								B			L_VF13
		NOP								NOP
		NOP								B			L_VF14
		NOP								NOP
		NOP								B			L_VF15
		NOP								NOP
		NOP								B			L_VF16
		NOP								NOP
		NOP								B			L_VF17
		NOP								NOP
		NOP								B			L_VF18
		NOP								NOP
		NOP								B			L_VF19
		NOP								NOP
		NOP								B			L_VF20
		NOP								NOP
		NOP								B			L_VF21
		NOP								NOP
		NOP								B			L_VF22
		NOP								NOP
		NOP								B			L_VF23
		NOP								NOP
		NOP								B			L_VF24
		NOP								NOP
		NOP								B			L_VF25
		NOP								NOP
		NOP								B			L_VF26
		NOP								NOP
		NOP								B			L_VF27
		NOP								NOP
		NOP								B			L_VF28
		NOP								NOP
		NOP								B			L_VF29
		NOP								NOP
		NOP								B			L_VF30
		NOP								NOP
		NOP								B			L_VF31
		NOP								NOP
		NOP								B			GSPrim
		NOP								NOP
Proj:	NOP								B			Proj1
		NOP								NOP
PTex:	NOP								B			PTex1
		NOP								NOP
Refl:	NOP								B			Refl1
		NOP								NOP
Line:	NOP								B			Line1
		NOP								NOP
Skin:	NOP								B			Skin1
		NOP								NOP
Light:	NOP								B			Light1
		NOP								IADDIU		VI10,VI00,0
LightT:	NOP								B			Light1
		NOP								IADDIU		VI10,VI00,1
WibbleT:NOP								B			WibbleT1
		NOP								NOP
LWibT:	NOP								B			LWibT1
		NOP								IADDIU		VI10,VI00,1
AddZPush:NOP							B			ZPush
		NOP								LOI			16
SubZPush:NOP							B			RestoreZPush
		NOP								NOP
Setup:	NOP[E]							XTOP		VI02				; initialise input pointer and halt
		NOP								NOP
Jump:	NOP								B			JumpToIt
		NOP								NOP
SCAB:	NOP								B			ScreenAlignedBillboards
		NOP								NOP
LAB:	NOP								B			LongAxisBillboards
		NOP								NOP
SHAB:	NOP								B			ShortAxisBillboards
		NOP								NOP

;-----------------------------------------------------------------------------------------------------------------------------

JumpToIt:	; set new value for data pointer

		NOP								MTIR		VI02,VF01z
		NOP								XTOP		VI01
		NOP								IADD		VI02,VI02,VI01
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

;-----------------------------------------------------------------------------------------------------------------------------

Breakpoint:	; for debugging purposes

		NOP[D]							B			NextPrim
		NOP								LQI			VF01,(VI02++)

;-----------------------------------------------------------------------------------------------------------------------------
;
;
;
;	ͻ
;	 tag format 
;	ͼ
;
;	This follows GIFtag format (EE User's Manual section 7.2.2), but with some added fields.
;
;
;
;	 31 30                   23 22                16 15 14                                         0
;	Ŀ
;	0      NREG exponent      NREG mantissa      EOP                   NLOOP                    
;	
;
;
;	 63       60 59 58 57                            47 46 45    43 42                            32
;	Ŀ
;	   NREG     FLG               PRIM              PRE  FLAGS               ADDR              
;	
;
;
;	 95                                                       76 75       72 71       68 67       64
;	Ŀ
;	                                                     ...     [REG2]     [REG1]      REG0    
;	
;
;
;	 127                                         112 111                                          96
;	Ŀ
;	                                                                     SIZE                     
;	
;
;
;
; Added fields:
;
; bits 22-16:	the mantissa of ((float)NREG*powf(2,-23))
; bits 23-30:	the exponent of ((float)NREG*powf(2,-23))
; bit  31:		zero, the a sign bit for treating the x-component as a float
;
; bits 32-42:	ADDR, the vu1 code address for processing the current primitive
; bits 43-45:	FLAGS, 43=unused, 44=u-clamp, 45=v-clamp
;
; bits 96-111:	SIZE=NREG*NLOOP
;
; bits 112-127:	unused
;
;-----------------------------------------------------------------------------------------------------------------------------
; This is the main loop that parses the incoming packet data. Each iteration of PrimLoop will do this:
; - set VI04 to NREG, the vertex step size
; - set VI05 to the end address for the current prim
; - set VI06 to NREG*NLOOP, the size of the current prim
; - set VI15 to hold EOP in its sign bit
; - jump the the address of the renderer that will process the prim
; The two entry points are Parser, which maintains the previous value of the data pointer VI02,
; and ParseInit, which first initialises VI02 to the value in VIF1_TOP.


ParseInit:
		NOP								XTOP		VI02				; initialise VI02

Parser: NOP								LQI			VF01,(VI02++)		; get 1st tag
		NOP								ISUBIU		VI13,VI02,1			; VI13 = start address of current packet
		NOP								XITOP		VI14				; get run-time render flags from VIF1_ITOP

PrimLoop:
		ADDw.x		VF02,VF01,VF00w		MTIR		VI15,VF01x			; VI15=EOP:NLOOP, 'ADDw' is for extracting NREG
		FTOI0.y		VF02,VF01			MTIR		VI01,VF01y			; VI01 = ADDR, renderer address
		NOP								MTIR		VI06,VF01w			; VI06 = SIZE = NREG*NLOOP, size of prim (excl. tag)
		NOP								IADD		VI05,VI02,VI06		; VI05 = end pointer for prim
		NOP								JR			VI01				; jump to renderer
		NOP								MTIR		VI04,VF02x			; VI04 = NREG (branch delay slot)

NextPrim:
		NOP								IBGEZ		VI15,PrimLoop		; loop if EOP==0
		NOP								NOP

KickPacket:

		; kick and stop
		NOP[E]							XGKICK		VI13				; kick the processed packet to the GS
		NOP								ISUBIU		VI02,VI02,1			; undo last postincrement (VI02 points to next packet)


;-----------------------------------------------------------------------------------------------------------------------------
; Process a VU1 prim
; ------------------
; by loading the designated floating point registers.
; It loads registers consecutively, starting from any register (VF09 or higher) and ending on
; any of VF11, VF15, VF19, VF23, VF27 or VF31. The decision to load in batches of 4 was because
; one often wants to load matrices as contextual data, and also to save having to put a test
; (plus the necessary delay slots) after each individual register load... it just means having
; to sometimes pad out the VU1 context to a 4-register boundary.

L_VF09:	NOP								LQI			VF09,(VI02++)		; entry point for loading VF09, etc
L_VF10:	NOP								LQI			VF10,(VI02++)
L_VF11:	NOP								LQI			VF11,(VI02++)

		MULw.w		VF24,VF11,VF00w		DIV			Q,VF00w,VF10w
		SUB.w		VF11,VF00,VF00		IBEQ		VI02,VI05,VUPrimEnd
		MULq.w		VF23,VF00,Q			WAITQ							; VF23w = f0
		
L_VF12:	NOP								LQI			VF12,(VI02++)
L_VF13:	NOP								LQI			VF13,(VI02++)
L_VF14:	NOP								LQI			VF14,(VI02++)
L_VF15:	NOP								LQI			VF15,(VI02++)

		NOP								IADDIU		VI01,VI02,1
		NOP								IBEQ		VI02,VI05,VUPrimEnd
		NOP								NOP

		NOP								IBEQ		VI01,VI05,VUPrimEnd
L_VF16:	NOP								LQI			VF16,(VI02++)
L_VF17:	NOP								LQI			VF17,(VI02++)
L_VF18:	NOP								LQI			VF18,(VI02++)
L_VF19:	NOP								LQI			VF19,(VI02++)

		NOP								NOP
		NOP								IBEQ		VI02,VI05,VUPrimEnd
		NOP								NOP

L_VF20:	NOP								LQI			VF20,(VI02++)
L_VF21:	NOP								LQI			VF21,(VI02++)
L_VF22:	NOP								LQI			VF22,(VI02++)
L_VF23:	NOP								LQI.xyz		VF23,(VI02++)

		NOP								NOP
		NOP								IBEQ		VI02,VI05,VUPrimEnd
		NOP								NOP

L_VF24:	NOP								LQI.xyz		VF24,(VI02++)
L_VF25:	NOP								LQI			VF25,(VI02++)
L_VF26:	NOP								LQI			VF26,(VI02++)
L_VF27:	NOP								LQI			VF27,(VI02++)

		NOP								NOP
		NOP								IBEQ		VI02,VI05,VUPrimEnd
		NOP								NOP

L_VF28:	NOP								LQI			VF28,(VI02++)
L_VF29:	NOP								LQI			VF29,(VI02++)
L_VF30:	NOP								LQI			VF30,(VI02++)
L_VF31:	NOP								LQI			VF31,(VI02++)

VUPrimEnd:
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

;-----------------------------------------------------------------------------------------------------------------------------
; process a GS prim
; -----------------
; by simply stepping the data pointer over it.

GSPrim:	NOP								LQ			VF01,0(VI05)		; prefetch next tag
		NOP								ISUBIU		VI12,VI02,1			; save the current tag address (see clipping code)
		NOP								B			NextPrim			; go back for next prim
		NOP								IADDIU		VI02,VI05,1			; step data pointer past next tag

;-----------------------------------------------------------------------------------------------------------------------------

CullPrim:
		NOP								ISUBIU		VI01,VI00,1
		NOP								MFIR.z		VF01,VI01
		NOP								SQ.z		VF01,-1(VI02)
		NOP								IADD		VI02,VI02,VI06
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

;-----------------------------------------------------------------------------------------------------------------------------
; zpush
; -----
; m_localToViewport[0][2] += m_localToViewport[0][3] * zPush * I / FogAlpha;
; m_localToViewport[1][2] += m_localToViewport[1][3] * zPush * I / FogAlpha;
; m_localToViewport[2][2] += m_localToViewport[2][3] * zPush * I / FogAlpha;
; m_localToViewport[3][2] += m_localToViewport[3][3] * zPush * I / FogAlpha;
; on entry, zPush is held in VF01w (from the tag)

		; new version, frees up VF12-15
		; still needs optimising

ZPush:
		MULi.w		VF02,VF10,I			NOP

		SUB.w		VF28,VF00,VF00		NOP
		ADDz.x		VF28,VF00,VF12z		NOP
		ADDz.y		VF28,VF00,VF13z		NOP
		ADDz.z		VF28,VF00,VF14z		NOP
		ADDz.w		VF28,VF28,VF15z		NOP
		
		MULw.w		VF04,VF12,VF02w		MR32.z		VF02,VF01
		MULw.w		VF05,VF13,VF02w		NOP
		MULw.w		VF06,VF14,VF02w		NOP
		MULw.w		VF07,VF15,VF02w		NOP
		ADDA.z		ACC,VF00,VF12		NOP
		MADDw.z		VF12,VF02,VF04		NOP
		ADDA.z		ACC,VF00,VF13		NOP
		MADDw.z		VF13,VF02,VF05		NOP
		ADDA.z		ACC,VF00,VF14		LQI			VF01,(VI02++)
		MADDw.z		VF14,VF02,VF06		NOP
		ADDA.z		ACC,VF00,VF15		B			NextPrim
		MADDw.z		VF15,VF02,VF07		NOP

RestoreZPush:

		ADDx.z		VF12,VF00,VF28x		NOP
		ADDy.z		VF13,VF00,VF28y		NOP
		ADDz.z		VF14,VF00,VF28z		NOP
		ADDw.z		VF15,VF00,VF28w		NOP

		NOP								LQI			VF01,(VI02++)
		NOP								B			NextPrim
		NOP								NOP
		
;-----------------------------------------------------------------------------------------------------------------------------
; vertex projection
; -----------------
; just transforms and projects the vertex coords, used for non-textured meshes

Proj1:
		; first some hackery added in as a bit of an afterthought, to support single-sided and colouring of meshes

		NOP								IADDIU		VI11,VI00,0x0800
		NOP								IAND		VI01,VI01,VI11
		NOP								ISUBIU		VI10,VI00,FOGE+1
		NOP								IAND		VI14,VI14,VI10
		NOP								IBNE		VI01,VI00,SingleSided
		NOP								IADDIU		VI01,VI00,Label00

Label00:NOP								IADDIU		VI01,VI00,COLR
		NOP								IAND		VI01,VI14,VI01
		NOP								ISUB		VI14,VI14,VI01
		NOP								IBNE		VI01,VI00,ApplyColour
		NOP								IADDIU		VI01,VI00,Label0
		
		; now branch to the appropriate rendering code

Label0:	NOP								IBEQ		VI14,VI00,Proj2
		NOP								IADDIU		VI01,VI00,CULL
		
		NOP								IADDIU		VI11,VI00,CLIP
		NOP								IBEQ		VI14,VI01,Cull
		NOP								IADDIU		VI01,VI00,Cull
		
		NOP								IBEQ		VI14,VI11,Clip
		NOP								ISW.w		VI01,-1(VI02)
		
		NOP								B			CullPrim
		NOP								NOP
		
	

Proj2:

		.if 1
		
		; fog version
		
		; f = min(f0+k/w,1) = f0+(1/w)(1-f0)min(w0,w)
		; where k=w0(1-f0)
		
		NOP								LOI			0x45000FFF
		MULi.w		VF25,VF00,I			NOP								; VF25w = 2^11 + 1 - 2^-12
		SUBw.w		VF25,VF25,VF23w		NOP								; VF25w = 2^11 + 1-f0 - 2^-12
		NOP								LOI			8
		ADDi.w		VF26,VF25,I			NOP								; VF26w = VF25w + ADC
		



		.if 0

		NOP								IADDIU		VI03,VI02,0
		
LoopP:
		NOP								IADD		VI03,VI03,VI04
		NOP								LQ.xyzw		VF01,-1(VI03)
		NOP								NOP
		NOP								NOP
		NOP								NOP
		ITOF4.xyz	VF02,VF01			MTIR		VI07,VF01w
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF02x		NOP
		MADDAy		ACC,VF13,VF02y		NOP
		MADDz		VF03,VF14,VF02z		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								DIV			Q,VF23w,VF03w
		NOP								NOP
		NOP								NOP
		MINI.w		VF03,VF03,VF24w		NOP
		NOP								IBLTZ		VI07,CullP
		NOP								NOP
		MULAw		ACC,VF00,VF25w		NOP
		MADDq.xyzw	VF04,VF03,Q			NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		FTOI4.xyz	VF04,VF04			NOP
		NOP								NOP
		NOP								NOP
		NOP								IADD		VI02,VI02,VI04
		NOP								SQ.xyzw		VF04,-1(VI02)
		NOP								IBNE		VI02,VI05,LoopP
		NOP								NOP

		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
CullP:	MULAw		ACC,VF00,VF26w		NOP
		MADDq.xyzw	VF04,VF03,Q			NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		FTOI4.xyz	VF04,VF04			NOP
		NOP								NOP
		NOP								NOP
		NOP								IADD		VI02,VI02,VI04
		NOP								SQ.xyzw		VF04,-1(VI02)
		NOP								IBNE		VI02,VI05,LoopP
		NOP								NOP

		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
		
		.else
		
		NOP								IADD		VI03,VI02,VI04
		ADDAx		ACC,VF15,VF00x		LQ.xyzw		VF01,-1(VI03)
		ITOF4.xyz	VF02,VF01			MTIR		VI07,VF01w
		MADDAx		ACC,VF12,VF02x		IADD		VI03,VI03,VI04
		MADDAy		ACC,VF13,VF02y		LQ.xyzw		VF01,-1(VI03)
		MADDz		VF04,VF14,VF02z		NOP
		ITOF4.xyz	VF02,VF01			DIV			Q,VF23w,VF04w
		MINI.w		VF04,VF04,VF24w		IADD		VI03,VI03,VI04
		ADDAx		ACC,VF15,VF00x		IBLTZ		VI07,CullP0
		MADDAx		ACC,VF12,VF02x		MTIR		VI07,VF01w
		MADDAy		ACC,VF13,VF02y		LQ.xyzw		VF01,-1(VI03)
		MADDz		VF03,VF14,VF02z		NOP
		MULAw		ACC,VF00,VF25w		NOP
		
LoopP:	MADDq.xyzw	VF05,VF04,Q			MOVE.xyz	VF04,VF03
		ITOF4.xyz	VF02,VF01			IADD		VI02,VI02,VI04
		MINI.w		VF04,VF03,VF24w		DIV			Q,VF23w,VF03w
		ADDAx		ACC,VF15,VF00x		IADD		VI03,VI03,VI04
		FTOI4.xyz	VF05,VF05			IBLTZ		VI07,CullP
		MADDAx		ACC,VF12,VF02x		MTIR		VI07,VF01w
		MADDAy		ACC,VF13,VF02y		LQ.xyzw		VF01,-1(VI03)
		MADDz		VF03,VF14,VF02z		IBNE		VI02,VI05,LoopP
		MULAw		ACC,VF00,VF25w		SQ.xyzw		VF05,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

CullP0:	MADDAy		ACC,VF13,VF02y		LQ.xyzw		VF01,-1(VI03)
		MADDz		VF03,VF14,VF02z		B			LoopP
		MULAw		ACC,VF00,VF26w		NOP

CullP:	MADDAy		ACC,VF13,VF02y		LQ.xyzw		VF01,-1(VI03)
		MADDz		VF03,VF14,VF02z		IBNE		VI02,VI05,LoopP
		MULAw		ACC,VF00,VF26w		SQ.xyzw		VF05,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)


		.endif




		.if 0
		
		; unoptimised version
		
		NOP								IADDIU		VI03,VI02,0			; source ptr = dest ptr

LoopP:
		NOP								IADD		VI03,VI03,VI04		; step source ptr
		NOP								LQ.xyz		VF01,-1(VI03)		; get vertex coords
		ITOF4.xyz	VF01,VF01			NOP								; vertex coords to float
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF01x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF01y		NOP								; row 1 view transform
		MADDz		VF02,VF14,VF01z		NOP								; row 2 view transform
		NOP								DIV			Q,VF00w,VF02w		; calc 1/w
		NOP								WAITQ
		MULq.xyz	VF03,VF02,Q			NOP								; homogeneous divide
		NOP								IADD		VI02,VI02,VI04		; step dest ptr
		NOP								SQ.xyz		VF03,-1(VI02)		; store screen coords
		NOP								IBNE		VI02,VI05,LoopP		; loop
		NOP								NOP
		
		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag

		.endif
		.else
		.if 1
		
		; fairly optimised version
		; 7 cycles per vertex
		
		; loop prologue
		NOP								IADD		VI03,VI02,VI04									; init/step source ptr
		ADDAx		ACC,VF15,VF00x		LQ.xyz		VF01,-1(VI03)		; row 3 view transform		; get 1st vertex
		ITOF4.xyz	VF02,VF01			IADD		VI03,VI03,VI04		; 1st vertex to float		; step source ptr
		MADDAx		ACC,VF12,VF02x		LQ.xyz		VF01,-1(VI03)		; row 0 view transform		; get 2nd vertex
		MADDAy		ACC,VF13,VF02y		IADD		VI03,VI03,VI04		; row 1 view transform		; step source ptr
		MADDz		VF04,VF14,VF02z		NOP								; row 2 view transform
		ITOF4.xyz	VF02,VF01			LQ.xyz		VF01,-1(VI03)		; 2nd vertex to float		; get 3rd vertex
		ADDAx		ACC,VF15,VF00x		DIV			Q,VF00w,VF04w		; row 3 view transform		; calc 1/w
		MADDAx		ACC,VF12,VF02x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF02y		NOP                             ; row 1 view transform
		MADDz		VF03,VF14,VF02z		NOP                             ; row 2 view transform
		ITOF4.xyz	VF02,VF01			NOP								; 3rd vertex to float

		; projection loop		
LoopP:	NOP								IADD		VI03,VI03,VI04									; step source ptr
		ADDAx		ACC,VF15,VF00x		LQ.xyz		VF01,-1(VI03)		; row 3 view transform		; get vertex coords
		MULq.xyz	VF05,VF04,Q			DIV			Q,VF00w,VF03w		; homogeneous div (xyz)/w	; calc 1/w
		MADDAx		ACC,VF12,VF02x		IADD		VI02,VI02,VI04		; row 0 view transform		; step destination ptr
		MADDAy		ACC,VF13,VF02y		MOVE.xyz	VF04,VF03			; row 1 view transform		; advance vertex queue
		MADDz		VF03,VF14,VF02z		IBNE		VI02,VI05,LoopP		; row 2 view transform		; loop
		ITOF4.xyz	VF02,VF01			SQ.xyz		VF05,-1(VI02)		; vertex coord to float		; store screen coords
		
		NOP								B			NextPrim										; go back for next prim
		NOP								LQI			VF01,(VI02++)									; prefetch next tag

		.else
		
		; very optimised version
		; 6 cycles per vertex, but uses a lot of code space
		
		; loop prologue
		NOP								IADD		VI01,VI02,VI04
		NOP								IADD		VI03,VI01,VI04
		NOP								LQ.xyz		VF01,-1(VI03)									; get vertex 1
		NOP								LQ.xyz		VF05,-1(VI01)									; get vertex 0
		ITOF4.xyz	VF02,VF01			IADD		VI01,VI03,VI04		; vertex 1 to float
		ITOF4.xyz	VF06,VF05			IADD		VI03,VI01,VI04		; vertex 0 to float
		ADDAx		ACC,VF15,VF00x		LQ.xyz		VF01,-1(VI03)									; get vertex 3
		MADDAx		ACC,VF12,VF02x		LQ.xyz		VF05,-1(VI01)									; get vertex 2
		MADDAy		ACC,VF13,VF02y		IADD		VI03,VI03,VI04
		MADDz		VF02,VF14,VF02z		IADDIU		VI01,VI00,1
		ADDAx		ACC,VF15,VF00x		IAND		VI07,VI15,VI01		; test for NLOOP odd
		MADDAx		ACC,VF12,VF06x		NOP
		MADDAy		ACC,VF13,VF06y		NOP
		MADDz		VF07,VF14,VF06z		ERCPR		P,VF02w
		ITOF4.xyz	VF01,VF01			NOP								; vertex 3 to float
		ITOF4.xyz	VF06,VF05			LQ.xyz		VF05,-1(VI03)		; vertex 2 to float			; get vertex 4
		ADDAx		ACC,VF15,VF00x		DIV			Q,VF00w,VF07w
		MADDAx		ACC,VF12,VF01x		NOP
		MADDAy		ACC,VF13,VF01y		IADD		VI03,VI03,VI04
		MADDz		VF03,VF14,VF01z		IBEQ		VI07,VI00,LoopP
		ADDAx		ACC,VF15,VF00x		NOP
		NOP								ISUB		VI05,VI05,VI04		; finish 1 vertex early if NLOOP odd
	
LoopP:	MADDAx		ACC,VF12,VF06x		LQ.xyz		VF01,-1(VI03)		; row 0 vertex transform B	; get vertex A
		MADDAy		ACC,VF13,VF06y		IADD		VI03,VI03,VI04		; row 1 vertex transform B	; step source ptr
		MULq.xyz	VF04,VF07,Q			ERCPR		P,VF03w				; homogeneous div (xyz)/wB'	; calculate 1/wA'
		MADDz		VF07,VF14,VF06z		MFP.w		VF02,P				; row 2 vertex transform B	; get 1/wA
		ITOF4.xyz	VF01,VF01			IADD		VI02,VI02,VI04		; vertex A to float			; step destination ptr
		ITOF4.xyz	VF06,VF05			LQ.xyz		VF05,-1(VI03)		; vertex B' to float		; get vertex B
		ADDAx		ACC,VF15,VF00x		SQ.xyz		VF04,-1(VI02)		; row 3 vertex transform A	; store screen coords B'

		MULw.xyz	VF04,VF02,VF02w		DIV			Q,VF00w,VF07w		; homogeneous div (xyz)/wA	; calc 1/wB
		MADDAx		ACC,VF12,VF01x		IADD		VI02,VI02,VI04		; row 0 vertex transform A	; step destination ptr
		MADDAy		ACC,VF13,VF01y		IADD		VI03,VI03,VI04		; row 1 vertex transform A	; step source ptr
		MADDz		VF02,VF14,VF01z		IBEQ		VI02,VI05,QuitP		; row 2 vertex transform A	; continue or quit
		ADDAx		ACC,VF15,VF00x		SQ.xyz		VF04,-1(VI02)		; row 3 vertex transform B'	; store screen coords A

		MADDAx		ACC,VF12,VF06x		LQ.xyz		VF01,-1(VI03)		; row 0 vertex transform B'	; get vertex A'
		MADDAy		ACC,VF13,VF06y		IADD		VI03,VI03,VI04		; row 1 vertex transform B' ; step source ptr
		MULq.xyz	VF04,VF07,Q			ERCPR		P,VF02w				; homogeneous div (xyz)/wB	; calc 1/wA
		MADDz		VF07,VF14,VF06z		MFP.w		VF03,P				; row 2 vertex transform B'	; get 1/wA'
		ITOF4.xyz	VF01,VF01			IADD		VI02,VI02,VI04		; vertex A' to float		; step destination ptr
		ITOF4.xyz	VF06,VF05			LQ.xyz		VF05,-1(VI03)		; vertex B to float			; get vertex B'
		ADDAx		ACC,VF15,VF00x		SQ.xyz		VF04,-1(VI02)		; row 3 vertex transform A'	; store screen coords B

		MULw.xyz	VF04,VF03,VF03w		DIV			Q,VF00w,VF07w		; homogeneous div (xyz)/wA'	; calc 1/wB'
		MADDAx		ACC,VF12,VF01x		IADD		VI02,VI02,VI04		; row 0 vertex transform A'	; step destination ptr
		MADDAy		ACC,VF13,VF01y		IADD		VI03,VI03,VI04		; row 1 vertex transform A'	; step source ptr
		MADDz		VF03,VF14,VF01z		IBNE		VI02,VI05,LoopP		; row 2 vertex transform A'	; loop or quit
		ADDAx		ACC,VF15,VF00x		SQ.xyz		VF04,-1(VI02)		; row 3 vertex transform B	; store screen coords A'

QuitP:	NOP								IBEQ		VI07,VI00,EndP									; finish if NLOOP was even
		NOP								NOP
		MULq.xyz	VF04,VF07,Q			IADD		VI02,VI02,VI04		; homogeneous div (xyz)/wB	; step source ptr
		NOP								SQ.xyz		VF04,-1(VI02)									; store screen coords B

EndP:	NOP								B			NextPrim										; back for next prim
		NOP								LQI			VF01,(VI02++)									; prefetch next tag

		.endif
		.endif

;-----------------------------------------------------------------------------------------------------------------------------
; triangle culling
; ----------------
; the per-triangle culling version of Proj

Cull:


		.if 1
		
		; fog version
		
		NOP								LOI			0x45000FFF
		MULi.w		VF25,VF00,I			NOP								; VF25w = 2^11 + 1 - 2^-12
		SUBw.w		VF25,VF25,VF23w		NOP								; VF25w = 2^11 + 1-f0 - 2^-12
		
		NOP								IADDIU		VI10,VI00,0x4000
		NOP								IADDIU		VI10,VI10,0x4000


		.if 0
		
		NOP								IADDIU		VI03,VI02,0

LoopK:	NOP								IADD		VI03,VI03,VI04
		NOP								LQ.xyzw		VF01,-1(VI03)
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								NOP
		ITOF4.xyz	VF02,VF01			MTIR		VI07,VF01w
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF02x		NOP
		MADDAy		ACC,VF13,VF02y		NOP
		MADDz		VF03,VF14,VF02z		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULA		ACC,VF10,VF03		DIV			Q,VF23w,VF03w
		MADDw		VF04,VF11,VF03w		NOP
		MINI.w		VF03,VF03,VF24		NOP
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF04xyz,VF04w		NOP
		MULAw		ACC,VF00,VF25w		NOP
		MADDq.xyzw	VF05,VF03,Q			NOP
		NOP								NOP
		NOP								FCAND		VI01,0x03FFFF
		NOP								IBNE		VI01,VI00,CullK
		FTOI4.xyz	VF06,VF05			MTIR		VI01,VF05w
		NOP								IOR			VI01,VI01,VI07
		NOP								MFIR.w		VF06,VI01
		NOP								NOP
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopK
		NOP								SQ.xyzw		VF06,-1(VI02)

		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

CullK:	NOP								IOR			VI01,VI01,VI10
		NOP								MFIR.w		VF06,VI01
		NOP								NOP
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopK
		NOP								SQ.xyzw		VF06,-1(VI02)

		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)


		.else
		
		NOP								IADDIU		VI03,VI02,0
		
		
		NOP								IADD		VI03,VI03,VI04
		NOP								LQ			VF01,-1(VI03)
		NOP								NOP
		NOP								NOP
		NOP								NOP
		ITOF4.xyz	VF01,VF01			NOP
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF01x		NOP
		MADDAy		ACC,VF13,VF01y		MTIR		VI06,VF01w
		MADDz		VF02,VF14,VF01z		NOP
		NOP								NOP
		
		NOP								IADD		VI03,VI03,VI04
		NOP								LQ			VF01,-1(VI03)
		ADDx.xyz	VF04,VF02,VF00x		DIV			Q,VF23w,VF02w
		MULA		ACC,VF10,VF02		NOP
		MADDw		VF03,VF11,VF02w		NOP
		ITOF4.xyz	VF01,VF01			NOP
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF01x		IADDIU		VI07,VI06,0
		MADDAy		ACC,VF13,VF01y		MTIR		VI06,VF01w
		MADDz		VF02,VF14,VF01z		NOP
		CLIPw.xyz	VF03xyz,VF03w		NOP
		
LoopK:	MULAw		ACC,VF00,VF25w		IADD		VI03,VI03,VI04
		MADDq		VF05,VF04,Q			LQ			VF01,-1(VI03)
		ADDx.xyz	VF04,VF02,VF00x		DIV			Q,VF23w,VF02w
		MULA		ACC,VF10,VF02		IADD		VI02,VI02,VI04
		MADDw		VF03,VF11,VF02w		FCAND		VI01,0x03FFFF
		ITOF4.xyz	VF01,VF01			IBNE		VI01,VI00,CullK
		MINIw.w		VF04,VF02,VF24w		MTIR		VI01,VF05w
		FTOI4.xyz	VF06,VF05			IOR			VI01,VI01,VI07
		ADDAx		ACC,VF15,VF00x		MFIR.w		VF06,VI01
		MADDAx		ACC,VF12,VF01x		IADDIU		VI07,VI06,0
		MADDAy		ACC,VF13,VF01y		MTIR		VI06,VF01w
		MADDz		VF02,VF14,VF01z		IBNE		VI02,VI05,LoopK
		CLIPw.xyz	VF03xyz,VF03w		SQ			VF06,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

CullK:	FTOI4.xyz	VF06,VF05			IOR			VI01,VI01,VI10
		ADDAx		ACC,VF15,VF00x		MFIR.w		VF06,VI01
		MADDAx		ACC,VF12,VF01x		IADDIU		VI07,VI06,0
		MADDAy		ACC,VF13,VF01y		MTIR		VI06,VF01w
		MADDz		VF02,VF14,VF01z		IBNE		VI02,VI05,LoopK
		CLIPw.xyz	VF03xyz,VF03w		SQ			VF06,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

		
		
		
		.endif


		.else

		.if 0
		; unoptimised
		
		NOP								IADDIU		VI03,VI02,0			; source ptr = dest ptr
		FTOI15.w	VF05,VF00			NOP								; set VF05w=0x8000 (for ADC bit)

LoopK:	NOP								IADD		VI03,VI03,VI04		; step source ptr
		NOP								LQ.xyz		VF01,-1(VI03)		; get vertex coords
		NOP								NOP
		NOP								NOP
		NOP								NOP
		ITOF4.xyz	VF02,VF01			NOP								; vertex coords to float
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF02x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF02y		NOP								; row 1 view transform
		MADDz		VF03,VF14,VF02z		NOP								; row 2 view transform
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULA		ACC,VF10,VF03		DIV			Q,VF00w,VF03w		; inv viewport scale	; calc 1/w
		MADDw		VF06,VF11,VF03w		NOP								; inv viewport offset
		NOP								NOP
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF06xyz,VF06w		NOP								; generate outcodes
		NOP								NOP
		MULq.xyz	VF05,VF03,Q			NOP								; homogeneous divide
		NOP								IADD		VI02,VI02,VI04		; step dest ptr
		NOP								FCAND		VI01,0x03FFFF		; test last 3 outcodes
		NOP								IBNE		VI01,VI00,CullK		; cull if all out
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopK		; loop
		NOP								SQ.xyz		VF05,-1(VI02)		; store screen coords
		
		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag

CullK:	NOP								IBNE		VI02,VI05,LoopK		; loop
		NOP								SQ			VF05,-1(VI02)		; store screen coords
		
		.else
		; optimised
		
		FTOI15.w	VF05,VF00			IADD		VI03,VI02,VI04
		ADDAx		ACC,VF15,VF00x		LQ.xyz		VF01,-1(VI03)
		ITOF4.xyz	VF02,VF01			IADD		VI03,VI03,VI04
		MADDAx		ACC,VF12,VF02x		LQ.xyz		VF01,-1(VI03)
		MADDAy		ACC,VF13,VF02y		IADD		VI03,VI03,VI04
		MADDz		VF04,VF14,VF02z		NOP
		ITOF4.xyz	VF02,VF01			LQ.xyz		VF01,-1(VI03)
		MULA		ACC,VF10,VF04		DIV			Q,VF00w,VF04w
		MADDw		VF06,VF11,VF04w		NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF02x		NOP
		MADDAy		ACC,VF13,VF02y		NOP
		MADDz		VF03,VF14,VF02z		NOP
		
LoopK:	CLIPw.xyz	VF06xyz,VF06w		IADD		VI03,VI03,VI04
		MULq.xyz	VF05,VF04,Q			IADD		VI02,VI02,VI04
		ITOF4.xyz	VF02,VF01			LQ.xyz		VF01,-1(VI03)
		MULA		ACC,VF10,VF03		DIV			Q,VF00w,VF03w
		MADDw		VF06,VF11,VF03w		FCAND		VI01,0x03FFFF
		ADDAx		ACC,VF15,VF00x		IBNE		VI01,VI00,CullK
		MADDAx		ACC,VF12,VF02x		MOVE.xyz	VF04,VF03
		MADDAy		ACC,VF13,VF02y		IBNE		VI02,VI05,LoopK
		MADDz		VF03,VF14,VF02z		SQ.xyz		VF05,-1(VI02)
		
		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag

CullK:	MADDAy		ACC,VF13,VF02y		IBNE		VI02,VI05,LoopK
		MADDz		VF03,VF14,VF02z		SQ			VF05,-1(VI02)
		
		.endif
		
		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag

		
		.endif

;-----------------------------------------------------------------------------------------------------------------------------
; vertex projection with perspective texturing
; --------------------------------------------
; transforms and projects vertex coords, and applies perspective to texture coords



		
PTex1:

		; first some hackery added in as a bit of an afterthought, to support single-sided and colouring of meshes
		; and switch between VU1-fogging and standard version of rendering code
		
		NOP								IADDIU		VI10,VI00,0x0800
		NOP								IAND		VI10,VI01,VI10
		NOP								IADDIU		VI11,VI00,0x3000
		NOP								IAND		VI11,VI01,VI11
		NOP								IBNE		VI10,VI00,SingleSided
		NOP								IADDIU		VI01,VI00,Label1
Label1:
		NOP								IADDIU		VI01,VI00,COLR
		NOP								IAND		VI01,VI14,VI01
		NOP								ISUB		VI14,VI14,VI01
		NOP								IBNE		VI01,VI00,ApplyColour
		NOP								IADDIU		VI01,VI00,Label2
Label2:

		; test for fog enable
		NOP								MFIR.y		VF02,VI11
		NOP								IADDIU		VI01,VI00,FOGE
		NOP								IAND		VI01,VI14,VI01
		NOP								ISUB		VI14,VI14,VI01		; clear FOGE flag
		ITOF12.y	VF02,VF02			IBNE		VI01,VI00,PTexFog
		NOP								IADDIU		VI01,VI00,3
		NOP								LOI			0x302E4000
		ADDAi.y		ACC,VF00,I			LOI			0x2A800000
		MADDi.y		VF01,VF02,I			IBEQ		VI04,VI01,FGE0		; keep uv-clamp flags
		NOP								LOI			0x4F800000
		MULi.y		VF01,VF01,I			NOP
FGE0:	NOP								SQ.y		VF01,-1(VI02)		; clear FGE bit


		NOP								IBEQ		VI14,VI00,PTex2
		NOP								IADDIU		VI01,VI00,CULL
		
		NOP								IADDIU		VI11,VI00,CLIP
		NOP								IBEQ		VI14,VI01,CullPTex
		NOP								IADDIU		VI01,VI00,CullPTex
		
		NOP								IBEQ		VI14,VI11,Clip
		NOP								ISW.w		VI01,-1(VI02)
		
		NOP								B			Shadow
		NOP								NOP


PTexFog:
		
		NOP								IBEQ		VI14,VI00,PTex2F
		NOP								IADDIU		VI01,VI00,CULL
		
		NOP								IADDIU		VI11,VI00,CLIP
		NOP								IBEQ		VI14,VI01,CullPTexF
		NOP								IADDIU		VI01,VI00,CullPTexF
		
		NOP								IBEQ		VI14,VI11,Clip
		NOP								ISW.w		VI01,-1(VI02)
		
		NOP								B			Shadow
		NOP								NOP
		
	

		
PTex2F:
		; fog version
		
		NOP								LOI			0x45000FFF
		MULi.w		VF25,VF00,I			NOP								; VF25w = 2^11 + 1 - 2^-12
		SUBw.w		VF25,VF25,VF23w		NOP								; VF25w = 2^11 + 1-f0 - 2^-12
		NOP								LOI			8
		ADDi.w		VF26,VF25,I			NOP								; VF26w = VF25w + ADC



		.if 0
		
		; unoptimised
		
		NOP								IADDIU		VI03,VI02,0
		NOP								MR32.z		VF07,VF00

		
LoopPTF:NOP								LQ.xy		VF06,0(VI03)
		NOP								IADD		VI03,VI03,VI04
		NOP								LQ.xyzw		VF01,-1(VI03)
		NOP								NOP
		NOP								NOP
		ITOF12.xy	VF07,VF06			NOP
		ITOF4.xyz	VF02,VF01			MTIR		VI07,VF01w
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF02x		NOP
		MADDAy		ACC,VF13,VF02y		NOP
		MADDz		VF03,VF14,VF02z		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								DIV			Q,VF23w,VF03w
		MINI.w		VF03,VF03,VF24		NOP
		NOP								NOP
		NOP								NOP
		NOP								IBLTZ		VI07,PTCullF
		NOP								NOP
		MULAw		ACC,VF00,VF25w		NOP
		MULq.xyz	VF08,VF07,Q			NOP
		MADDq		VF04,VF03,Q			NOP
		NOP								NOP
		NOP								NOP
		NOP								SQ.xyz		VF08,0(VI02)
		FTOI4.xyz	VF04,VF04			NOP
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopPTF
		NOP								SQ.xyzw		VF04,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
PTCullF:MULAw		ACC,VF00,VF26w		NOP
		MULq.xyz	VF08,VF07,Q			NOP
		MADDq		VF04,VF03,Q			NOP
		NOP								NOP
		NOP								NOP
		NOP								SQ.xyz		VF08,0(VI02)
		FTOI4.xyz	VF04,VF04			NOP
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopPTF
		NOP								SQ.xyzw		VF04,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
		
		.else
		
		; optimised
		
		NOP								IADD		VI03,VI02,VI04
		ADDAx		ACC,VF15,VF00x		LQ			VF01,-1(VI03)
		ITOF4.xyz	VF01,VF01			LQ.xy		VF07,0(VI02)
		MADDAx		ACC,VF12,VF01x		NOP
		MADDAy		ACC,VF13,VF01y		LQ.xy		VF05,0(VI03)
		MADDz		VF03,VF14,VF01z		IADD		VI03,VI03,VI04
		MINI.w		VF03,VF03,VF24		DIV			Q,VF23w,VF03w
		ITOF12.xy	VF07,VF07			MTIR		VI07,VF01w
		ITOF12.xy	VF06,VF05			LQ			VF01,-1(VI03)
		ITOF4.xyz	VF01,VF01			NOP
		ADDAx		ACC,VF15,VF00x		MR32.z		VF07,VF00
		
LoopPTF:MADDAx		ACC,VF12,VF01x		LQ.xy		VF05,0(VI03)
		MADDAy		ACC,VF13,VF01y		IADD		VI03,VI03,VI04
		MULq		VF04,VF03,Q			IBLTZ		VI07,PTCullF
		MADDz		VF03,VF14,VF01z		MTIR		VI07,VF01w
		MULq.xyz	VF08,VF07,Q			LQ			VF01,-1(VI03)
		ADDx.xy		VF07,VF06,VF00x		IADDIU		VI01,VI02,0
		FTOI4.xyz	VF04,VF04			IADD		VI02,VI02,VI04
		ADD.w		VF04,VF04,VF25		DIV			Q,VF23w,VF03w
		ITOF4.xyz	VF01,VF01			SQ.xyz		VF08,0(VI01)
		MINI.w		VF03,VF03,VF24		NOP
		ITOF12.xy	VF06,VF05			IBNE		VI02,VI05,LoopPTF
		ADDAx		ACC,VF15,VF00x		SQ			VF04,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
PTCullF:MULq.xyz	VF08,VF07,Q			LQ			VF01,-1(VI03)
		ADDx.xy		VF07,VF06,VF00x		IADDIU		VI01,VI02,0
		FTOI4.xyz	VF04,VF04			IADD		VI02,VI02,VI04
		ADD.w		VF04,VF04,VF26		DIV			Q,VF23w,VF03w
		ITOF4.xyz	VF01,VF01			SQ.xyz		VF08,0(VI01)
		MINI.w		VF03,VF03,VF24		NOP
		ITOF12.xy	VF06,VF05			IBNE		VI02,VI05,LoopPTF
		ADDAx		ACC,VF15,VF00x		SQ			VF04,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
		
		
		
		.endif
		
		
		
		
		; non-fogged version
		
PTex2:


		.if 0
		; unoptimised version
		
		NOP								IADDIU		VI03,VI02,0			; source ptr = dest ptr
		NOP								MR32.z		VF04,VF00			; set 1 in (s,t,1)

LoopPT:	NOP								LQ.xy		VF04,0(VI03)		; get texture coords
		NOP								IADD		VI03,VI03,VI04		; step source ptr
		NOP								LQ.xyz		VF01,-1(VI03)		; get vertex coords
		NOP								NOP
		NOP								NOP
		ITOF12.xy	VF04,VF04			NOP								; texture coords to float
		ITOF4.xyz	VF01,VF01			NOP								; vertex coords to float
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF01x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF01y		NOP								; row 1 view transform		
		MADDz		VF02,VF14,VF01z		NOP								; row 2 view transform
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								DIV			Q,VF23w,VF02w		; calc 1/w
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULq.xyz	VF05,VF04,Q			NOP								; homogeneous div (st1)/w
		MULq.xyz	VF03,VF02,Q			NOP								; homogeneous div (xyz)/w
		NOP								NOP
		NOP								NOP
		NOP								NOP
		FTOI4.xyz	VF03,VF03			NOP
		NOP								NOP
		NOP								SQ.xyz		VF05,0(VI02)		; store texture coords
		NOP								IADD		VI02,VI02,VI04		; step dest ptr
		NOP								SQ.xyz		VF03,-1(VI02)		; store screen coords
		NOP								IBNE		VI02,VI05,LoopPT	; loop
		NOP								NOP
		
		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag
		
		.else
		
		; optimised
		
		NOP								IADD		VI03,VI02,VI04
		ADDAx		ACC,VF15,VF00x		LQ.xyz		VF01,-1(VI03)
		ITOF4.xyz	VF01,VF01			LQ.xy		VF05,0(VI02)
		MADDAx		ACC,VF12,VF01x		LQ.xy		VF06,0(VI03)
		MADDAy		ACC,VF13,VF01y		IADD		VI03,VI03,VI04
		MADDz		VF03,VF14,VF01z		LQ.xyz		VF01,-1(VI03)
		ITOF12.xy	VF07,VF05			NOP
		ITOF4.xyz	VF01,VF01			MR32.z		VF07,VF00
		ADDAx		ACC,VF15,VF00x		DIV			Q,VF23w,VF03w
		MADDAx		ACC,VF12,VF01x		LQ.xy		VF05,0(VI03)
		MADDAy		ACC,VF13,VF01y		IADD		VI03,VI03,VI04
		MADDz		VF02,VF14,VF01z		LQ.xyz		VF01,-1(VI03)
		MULq.xyz	VF08,VF07,Q			WAITQ
		MULq.xyz	VF04,VF03,Q			NOP
		
LoopPT:	ITOF12.xy	VF07,VF06			MOVE.xy		VF06,VF05
		ITOF4.xyz	VF01,VF01			DIV			Q,VF23w,VF02w
		ADDx.xyz	VF03,VF02,VF00x		SQ.xyz		VF08,0(VI02)
		FTOI4.xyz	VF04,VF04			IADD		VI02,VI02,VI04
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF01x		LQ.xy		VF05,0(VI03)
		MADDAy		ACC,VF13,VF01y		IADD		VI03,VI03,VI04
		MADDz		VF02,VF14,VF01z		LQ.xyz		VF01,-1(VI03)
		MULq.xyz	VF08,VF07,Q			IBNE		VI02,VI05,LoopPT
		MULq.xyz	VF04,VF03,Q			SQ.xyz		VF04,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
		
		.endif
		

;-----------------------------------------------------------------------------------------------------------------------------
; triangle culling and perspective texturing


CullPTexF:

		; fog version
		
		NOP								LOI			0x45000FFF
		MULi.w		VF25,VF00,I			NOP								; VF25w = 2^11 + 1 - 2^-12
		SUBw.w		VF25,VF25,VF23w		NOP								; VF25w = 2^11 + 1-f0 - 2^-12
		
		NOP								IADDIU		VI10,VI00,0x4000
		NOP								IADDIU		VI10,VI10,0x4000
		
		
		.if 0
		; unoptimised
		
		NOP								IADDIU		VI03,VI02,0
		NOP								MR32.z		VF07,VF00

		
LoopKPTF:
		NOP								LQ.xy		VF07,0(VI03)
		NOP								IADD		VI03,VI03,VI04
		NOP								LQ.xyzw		VF01,-1(VI03)
		NOP								NOP
		NOP								NOP
		ITOF12.xy	VF07,VF07			NOP
		ITOF4.xyz	VF02,VF01			MTIR		VI07,VF01w
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF02x		NOP
		MADDAy		ACC,VF13,VF02y		NOP
		MADDz		VF03,VF14,VF02z		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULA		ACC,VF10,VF03		DIV			Q,VF23w,VF03w
		MADDw		VF04,VF11,VF03w		NOP
		MINI.w		VF03,VF03,VF24		NOP
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF04xyz,VF04w		NOP
		MULAw		ACC,VF00,VF25w		NOP
		MULq.xyz	VF08,VF07,Q			NOP
		MADDq		VF05,VF03,Q			NOP
		NOP								NOP
		NOP								FCAND		VI01,0x03FFFF
		NOP								IBNE		VI01,VI00,CullKPTF
		FTOI4.xyz	VF06,VF05			SQ.xyz		VF08,0(VI02)
		NOP								MTIR		VI11,VF05w
		NOP								IOR			VI11,VI11,VI07
		NOP								MFIR.w		VF06,VI11
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopKPTF
		NOP								SQ.xyzw		VF06,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

CullKPTF:
		NOP								MTIR		VI11,VF05w
		NOP								IOR			VI11,VI11,VI10
		NOP								MFIR.w		VF06,VI11
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopKPTF
		NOP								SQ.xyzw		VF06,-1(VI02)

		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)


		.else
		; optimised

		NOP								IADD		VI03,VI02,VI04
		ADDAx		ACC,VF15,VF00x		LQ			VF04,-1(VI03)
		ITOF4.xyz	VF04,VF04			LQ.xy		VF07,0(VI02)
		MADDAx		ACC,VF12,VF04x		MTIR		VI07,VF04w
		MADDAy		ACC,VF13,VF04y		LQ.xy		VF06,0(VI03)
		MADDz		VF04,VF14,VF04z		IADD		VI03,VI03,VI04
		ITOF12.xy	VF07,VF07			LQ			VF01,-1(VI03)
		MULA		ACC,VF10,VF04		DIV			Q,VF23w,VF04w
		MADDw		VF03,VF11,VF04w		MR32.z		VF07,VF00
		ITOF4.xyz	VF01,VF01			NOP
		MINI.w		VF04,VF04,VF24		NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF01x		NOP
		MADDAy		ACC,VF13,VF01y		NOP
		MADDz		VF02,VF14,VF01z		NOP
		CLIPw.xyz	VF03xyz,VF03w		NOP
		
LoopKPTF:
		MULq.xyz	VF08,VF07,Q			MTIR		VI06,VF01w
		ITOF12.xy	VF07,VF06			LQ.xy		VF06,0(VI03)
		MULAw		ACC,VF00,VF25w		IADD		VI03,VI03,VI04
		MADDq		VF05,VF04,Q			LQ			VF01,-1(VI03)
		ADDx.xyz	VF04,VF02,VF00x		FCAND		VI01,0x03FFFF
		MULA		ACC,VF10,VF02		DIV			Q,VF23w,VF02w
		MADDw		VF03,VF11,VF02w		SQ.xyz		VF08,0(VI02)
		ITOF4.xyz	VF01,VF01			IBNE		VI01,VI00,CullKPTF
		MINI.w		VF04,VF02,VF24		MTIR		VI11,VF05w
		FTOI4.xyz	VF05,VF05			IOR			VI11,VI11,VI07
		ADDAx		ACC,VF15,VF00x		MFIR.w		VF05,VI11
		MADDAx		ACC,VF12,VF01x		IADD		VI02,VI02,VI04
		MADDAy		ACC,VF13,VF01y		IADDIU		VI07,VI06,0
		MADDz		VF02,VF14,VF01z		IBNE		VI02,VI05,LoopKPTF
		CLIPw.xyz	VF03xyz,VF03w		SQ.xyzw		VF05,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

CullKPTF:
		FTOI4.xyz	VF05,VF05			IOR			VI11,VI11,VI10
		ADDAx		ACC,VF15,VF00x		MFIR.w		VF05,VI11
		MADDAx		ACC,VF12,VF01x		IADD		VI02,VI02,VI04
		MADDAy		ACC,VF13,VF01y		IADDIU		VI07,VI06,0
		MADDz		VF02,VF14,VF01z		IBNE		VI02,VI05,LoopKPTF
		CLIPw.xyz	VF03xyz,VF03w		SQ.xyzw		VF05,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

		
		.endif
		
		


		
		; non-fogged version

CullPTex:

		.if 0
		; unoptimised
		
		NOP								IADDIU		VI03,VI02,0			; source ptr = dest ptr
		FTOI15.w	VF05,VF00			NOP								; set VF05w=0x8000 (for ADC bit)
		NOP								MR32.z		VF07,VF00			; set 1 in (s,t,1)

LoopKPT:NOP								LQ.xy		VF07,0(VI03)		; get tex coords
		NOP								IADD		VI03,VI03,VI04		; step source ptr
		NOP								LQ.xyz		VF01,-1(VI03)		; get vertex coords
		NOP								NOP
		NOP								NOP
		ITOF12.xy	VF07,VF07			NOP								; tex coords to float
		ITOF4.xyz	VF02,VF01			NOP								; vertex coords to float
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF02x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF02y		NOP								; row 1 view transform
		MADDz		VF03,VF14,VF02z		NOP								; row 2 view transform
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULA		ACC,VF10,VF03		DIV			Q,VF23w,VF03w		; inv viewport scale	; calc 1/w
		MADDw		VF06,VF11,VF03w		NOP								; inv viewport offset
		NOP								NOP
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF06xyz,VF06w		NOP								; generate outcodes
		NOP								NOP
		MULq.xyz	VF08,VF07,Q			NOP								; homogeneous divide (st1)/w
		MULq.xyz	VF05,VF03,Q			NOP								; homogeneous divide (xyz)/w
		NOP								NOP
		NOP								FCAND		VI01,0x03FFFF		; test last 3 outcodes
		NOP								SQ.xyz		VF08,0(VI02)
		FTOI4.xyz	VF05,VF05			IADD		VI02,VI02,VI04		; step dest ptr
		NOP								IBNE		VI01,VI00,CullKPT	; cull if all out
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopKPT	; loop
		NOP								SQ.xyz		VF05,-1(VI02)		; store screen coords
		
		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag

CullKPT:NOP								IBNE		VI02,VI05,LoopKPT	; loop
		NOP								SQ			VF05,-1(VI02)		; store screen coords
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
		.else
		
		; optimised
		
		NOP								IADD		VI03,VI02,VI04
		ADDAx		ACC,VF15,VF00x		LQ.xyz		VF01,-1(VI03)
		ITOF4.xyz	VF01,VF01			LQ.xy		VF05,0(VI02)
		MADDAx		ACC,VF12,VF01x		LQ.xy		VF06,0(VI03)
		MADDAy		ACC,VF13,VF01y		IADD		VI03,VI03,VI04
		MADDz		VF03,VF14,VF01z		LQ.xyz		VF01,-1(VI03)
		ITOF12.xy	VF07,VF05			NOP
		MULA		ACC,VF10,VF03		NOP
		MADDw		VF02,VF11,VF03w		DIV			Q,VF23w,VF03w
		ITOF4.xyz	VF01,VF01			MR32.z		VF07,VF00
		ADDAx		ACC,VF15,VF00x		ISUBIU		VI01,VI00,1
		CLIPw.xyz	VF02xyz,VF02w		MFIR.w		VF04,VI01
		MADDAx		ACC,VF12,VF01x		NOP
		MADDAy		ACC,VF13,VF01y		NOP
		
LoopKPT:MADDz		VF02,VF14,VF01z		LQ.xy		VF05,0(VI03)
		MULq.xyz	VF08,VF07,Q			IADD		VI03,VI03,VI04
		MULq.xyz	VF04,VF03,Q			LQ.xyz		VF01,-1(VI03)
		ITOF12.xy	VF07,VF06			FCAND		VI01,0x03FFFF
		MULA		ACC,VF10,VF02		DIV			Q,VF23w,VF02w
		MADDw		VF03,VF11,VF02w		MOVE.xy		VF06,VF05
		ITOF4.xyz	VF01,VF01			SQ.xyz		VF08,0(VI02)
		FTOI4.xyz	VF04,VF04			IADD		VI02,VI02,VI04
		ADDAx		ACC,VF15,VF00x		IBNE		VI01,VI00,CullKPT
		CLIPw.xyz	VF03xyz,VF03w		MOVE.xyz	VF03,VF02
		MADDAx		ACC,VF12,VF01x		IBNE		VI02,VI05,LoopKPT
		MADDAy		ACC,VF13,VF01y		SQ.xyz		VF04,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
CullKPT:MADDAx		ACC,VF12,VF01x		IBNE		VI02,VI05,LoopKPT
		MADDAy		ACC,VF13,VF01y		SQ			VF04,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
		.endif
		
;-----------------------------------------------------------------------------------------------------------------------------

WibbleT1:

		.if 0
		; unoptimised version
		
		NOP								IADDIU		VI03,VI02,0
		
LoopW:	NOP								LQ.xy		VF01,0(VI03)
		NOP								IADD		VI03,VI03,VI04
		NOP								NOP
		NOP								NOP
		ITOF12.xy	VF02,VF01			NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		ADD.xy		VF03,VF02,VF27		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		FTOI12.xy	VF04,VF03			NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								SQ.xy		VF04,0(VI02)
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopW
		NOP								NOP
		
		.else
		; optimised version
		
		
		NOP								LQ.xy		VF04,0(VI02)
		ITOF12.xy	VF04,VF04			IADD		VI03,VI02,VI04
		ADD.xy		VF04,VF04,VF27		LQ.xy		VF03,0(VI03)
		ITOF12.xy	VF03,VF03			IADD		VI03,VI03,VI04
		FTOI12.xy	VF04,VF04			LQ.xy		VF02,0(VI03)
		ADD.xy		VF03,VF03,VF27		IADD		VI03,VI03,VI04
		ITOF12.xy	VF02,VF02			ISUB		VI05,VI05,VI04
		
LoopW:	NOP								LQ.xy		VF01,0(VI03)
		NOP								SQ.xy		VF04,0(VI02)
		FTOI12.xy	VF04,VF03			IADD		VI03,VI03,VI04
		ADD.xy		VF03,VF02,VF27      IBNE		VI02,VI05,LoopW
		ITOF12.xy	VF02,VF01			IADD		VI02,VI02,VI04
		
		NOP								IADD		VI05,VI05,VI04
		
		.endif
		
		NOP								B			PTex1
		NOP								ISUB		VI02,VI02,VI06

;-----------------------------------------------------------------------------------------------------------------------------

LWibT1:

		; optimised version
		
		NOP								LQ.xy		VF04,0(VI02)
		ITOF12.xy	VF04,VF04			IADD		VI03,VI02,VI04
		ADD.xy		VF04,VF04,VF27		LQ.xy		VF03,0(VI03)
		ITOF12.xy	VF03,VF03			IADD		VI03,VI03,VI04
		FTOI12.xy	VF04,VF04			LQ.xy		VF02,0(VI03)
		ADD.xy		VF03,VF03,VF27		IADD		VI03,VI03,VI04
		ITOF12.xy	VF02,VF02			ISUB		VI05,VI05,VI04
		
LoopLW:	NOP								LQ.xy		VF01,0(VI03)
		NOP								SQ.xy		VF04,0(VI02)
		FTOI12.xy	VF04,VF03			IADD		VI03,VI03,VI04
		ADD.xy		VF03,VF02,VF27      IBNE		VI02,VI05,LoopLW
		ITOF12.xy	VF02,VF01			IADD		VI02,VI02,VI04
		
		NOP								IADD		VI05,VI05,VI04
		
		NOP								B			Light1
		NOP								ISUB		VI02,VI02,VI06

;-----------------------------------------------------------------------------------------------------------------------------
; reflection mapping

Refl1:

		NOP								IADDIU		VI11,VI00,0x0800
		NOP								IAND		VI01,VI01,VI11
		NOP								NOP
		NOP								IBNE		VI01,VI00,SingleSided
		NOP								IADDIU		VI01,VI00,LabelR0

LabelR0:
		NOP								IADDIU		VI01,VI00,COLR
		NOP								IAND		VI01,VI14,VI01
		NOP								ISUB		VI14,VI14,VI01
		NOP								IBNE		VI01,VI00,ApplyColour
		NOP								IADDIU		VI01,VI00,LabelR1

LabelR1:
		NOP								IADDIU		VI01,VI00,CLIP
		NOP								IAND		VI01,VI14,VI01
		NOP								NOP
		NOP								IBNE		VI01,VI00,ReflClip
		NOP								NOP


		.if 0
		; unoptimised

		NOP								IADDIU		VI03,VI02,0
		NOP								MR32.z		VF04,VF00
		NOP								LOI			0.5

LoopR: 	NOP								LQ.xyz		VF05,0(VI03)		; get normal
		NOP								IADD		VI03,VI03,VI04		; step source pointer
		NOP								LQ.xyz		VF01,-1(VI03)		; get vertex
		NOP								NOP
		ITOF15.xyz	VF05,VF05			NOP								; VF05 = n (unit, in model space)
		NOP								NOP
		ITOF4.xyz	VF02,VF01			NOP
		
		; compute 0.5*(nx+vx/vz+1, ny+vy/vz+1)
		MULAx.xyz	ACC,VF17,VF02x		NOP								; transform v
		MADDAy.xyz	ACC,VF18,VF02y		NOP
		MADDz.xyz	VF02,VF19,VF02z		NOP
		MULAx.xy	ACC,VF17,VF05x		NOP								; transform n
		MADDAy.xy	ACC,VF18,VF05y		NOP
		MADDz.xy	VF03,VF19,VF05z		NOP
		NOP								DIV			Q,VF00w,VF02z		; calc 1/vz'
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		ADDAi.xy	ACC,VF03,I			NOP
		MSUBq.xy	VF04,VF02,Q			NOP

		NOP								SQ.xyz		VF04,0(VI02)
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopR
		NOP								NOP

		NOP								B			Proj1
		NOP								ISUB		VI02,VI02,VI06

		.else
		; optimised
		NOP								LQ.xyz		VF02,2(VI02)
		NOP								LQ.xyz		VF04,0(VI02)
		ITOF4.xyz	VF02,VF02			MR32.z		VF03,VF00
		ITOF15.xyz	VF05,VF04			LOI			0.5
		MULAx.xyz	ACC,VF17,VF02x		LQ.xyz		VF01,5(VI02)
		MADDAy.xyz	ACC,VF18,VF02y		LQ.xyz		VF04,3(VI02)
		MADDz.xyz	VF02,VF19,VF02z		NOP
		MULAx.xy	ACC,VF17,VF05x		NOP
		ITOF4.xyz	VF01,VF01			DIV			Q,VF00w,VF02z
		MADDAy.xy	ACC,VF18,VF05y		NOP
		MADDz.xy    VF27,VF19,VF05z		NOP
		ITOF15.xyz	VF05,VF04			NOP
		
LoopR:	MULAx.xyz	ACC,VF17,VF01x		MOVE.xy		VF03,VF02
		MADDAy.xyz	ACC,VF18,VF01y		NOP
		MADDz.xyz	VF02,VF19,VF01z		LQ.xyz		VF01,8(VI02)
		ADDAi.xy	ACC,VF27,I			LQ.xyz		VF04,6(VI02)
		MSUBq.xy	VF03,VF03,Q			IADD		VI02,VI02,VI04
		MULAx.xy	ACC,VF17,VF05x		NOP
		ITOF4.xyz	VF01,VF01			DIV			Q,VF00w,VF02z
		MADDAy.xy	ACC,VF18,VF05y		NOP
		MADDz.xy    VF27,VF19,VF05z		IBNE		VI02,VI05,LoopR
		ITOF15.xyz	VF05,VF04			SQ.xyz		VF03,-3(VI02)
		
		NOP								B			Proj2
		NOP								ISUB		VI02,VI02,VI06

		.endif
		
		
		
		
ReflClip:

		.if 0
		; unoptimised
		
		NOP								IADDIU		VI03,VI02,0
		NOP								LOI			0.5


LoopRC:	NOP								LQ.xyz		VF04,0(VI03)		; get normal
		NOP								IADD		VI03,VI03,VI04		; step source pointer
		NOP								LQ.xyz		VF01,-1(VI03)		; get vertex
		NOP								NOP
		ITOF15.xyz	VF04,VF04			NOP								; VF01 = n (unit, in model space)
		NOP								NOP
		ITOF4.xyz	VF01,VF01			NOP
		
		; compute ((nx'+0.5)*vz'-vx', (ny'+0.5)*vz'-vy', vz', 1/vz')
		MULAx.xyz	ACC,VF17,VF01x		NOP								; transform v
		MADDAy.xyz	ACC,VF18,VF01y		NOP
		MADDz.xyz	VF02,VF19,VF01z		NOP
		ADDAi.xy	ACC,VF00,I			NOP
		MADDAx.xy	ACC,VF17,VF04x		NOP								; transform n
		MADDAy.xy	ACC,VF18,VF04y		NOP
		MADDz.xy	VF03,VF19,VF04z		NOP
		NOP								DIV			Q,VF00w,VF02z
		SUBA.xy		ACC,VF00,VF02		MOVE.z		VF04,VF02
		MADDz.xy	VF04,VF03,VF02z		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULq.w		VF04,VF00,Q			NOP
		
		NOP								SQ			VF04,0(VI02)
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopRC
		NOP								NOP

		NOP								ISUB		VI02,VI02,VI06

		NOP								IADDIU		VI01,VI00,ReflPostClip
		
		NOP								B			Clip
		NOP								ISW.w		VI01,-1(VI02)
		
		.else
		; optimised
		
		NOP								LQ.xyz		VF02,2(VI02)
		NOP								LQ.xyz		VF03,0(VI02)
		ITOF4.xyz	VF02,VF02			LOI			0.5
		ITOF15.xyz	VF04,VF03			NOP
		MULAx.xyz	ACC,VF17,VF02x		LQ.xyz		VF03,3(VI02)
		MADDAy.xyz	ACC,VF18,VF02y		LQ.xyz		VF01,5(VI02)
		MADDz.xyz	VF02,VF19,VF02z		NOP
		ADDAi.xy	ACC,VF00,I			NOP
		MADDAx.xy	ACC,VF17,VF04x		NOP
		MADDAy.xy	ACC,VF18,VF04y		NOP
		
LoopRC:	MADDz.xy	VF05,VF19,VF04z		DIV			Q,VF00w,VF02z
		ITOF4.xyz	VF01,VF01			IADD		VI02,VI02,VI04
		ITOF15.xyz	VF04,VF03			NOP
		SUBA.xy		ACC,VF00,VF02		MOVE.z		VF05,VF02
		MADDz.xy	VF05,VF05,VF02z		NOP
		MULAx.xyz	ACC,VF17,VF01x		NOP
		MADDAy.xyz	ACC,VF18,VF01y		NOP
		MULq.w		VF05,VF00,Q			LQ.xyz		VF03,3(VI02)
		MADDz.xyz	VF02,VF19,VF01z		LQ.xyz		VF01,5(VI02)
		ADDAi.xy	ACC,VF00,I			NOP
		MADDAx.xy	ACC,VF17,VF04x		IBNE		VI02,VI05,LoopRC
		MADDAy.xy	ACC,VF18,VF04y		SQ			VF05,-3(VI02)
		
		NOP								ISUB		VI02,VI02,VI06

		NOP								IADDIU		VI01,VI00,ReflPostClip
		
		NOP								B			Clip
		NOP								ISW.w		VI01,-1(VI02)
		
		
		.endif




ReflPostClip:

		.if 1
		
		; fog version

		
		
		NOP								LOI			0x45000FFF
		MULi.w		VF25,VF00,I			NOP								; VF25w = 2^11 + 1 - 2^-12
		SUBw.w		VF25,VF25,VF23w		NOP								; VF25w = 2^11 + 1-f0 - 2^-12
		
		NOP								IADDIU		VI10,VI00,0x4000
		NOP								IADDIU		VI10,VI10,0x4000
		
		
		.if 0
		; unoptimised
		
		NOP								IADDIU		VI03,VI02,0

		
LoopRPC:NOP								LQ			VF07,0(VI03)
		NOP								IADD		VI03,VI03,VI04
		NOP								LQ			VF01,-1(VI03)
		NOP								NOP
		NOP								NOP
		MULw.xyz	VF08,VF07,VF07w		NOP								; homogeneous divide (str)/r
		ITOF4.xyz	VF02,VF01			MTIR		VI07,VF01w
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF02x		NOP
		MADDAy		ACC,VF13,VF02y		NOP
		MADDz		VF03,VF14,VF02z		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULA		ACC,VF10,VF03		DIV			Q,VF23w,VF03w
		MADDw		VF04,VF11,VF03w		NOP
		MINI.w		VF03,VF03,VF24		NOP
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF04xyz,VF04w		NOP
		MULAw		ACC,VF00,VF25w		NOP
		MADDq		VF05,VF03,Q			NOP
		NOP								NOP
		NOP								FCAND		VI01,0x03FFFF
		NOP								IBNE		VI01,VI00,CullRPC
		FTOI4.xyz	VF06,VF05			SQ.xyz		VF08,0(VI02)
		NOP								MTIR		VI11,VF05w
		NOP								IOR			VI11,VI11,VI07
		NOP								MFIR.w		VF06,VI11
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopRPC
		NOP								SQ.xyzw		VF06,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

CullRPC:NOP								MTIR		VI11,VF05w
		NOP								IOR			VI11,VI11,VI10
		NOP								MFIR.w		VF06,VI11
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopRPC
		NOP								SQ.xyzw		VF06,-1(VI02)

		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

		.else
		; optimised
		
		NOP								IADD		VI03,VI02,VI04
		ADDAx		ACC,VF15,VF00x		LQ			VF04,-1(VI03)
		ITOF4.xyz	VF04,VF04			LQ			VF07,0(VI02)
		MADDAx		ACC,VF12,VF04x		MTIR		VI07,VF04w
		MADDAy		ACC,VF13,VF04y		LQ			VF06,0(VI03)
		MADDz		VF04,VF14,VF04z		IADD		VI03,VI03,VI04
		ADDx		VF07,VF07,VF00x		LQ			VF01,-1(VI03)
		MULA		ACC,VF10,VF04		DIV			Q,VF23w,VF04w
		MADDw		VF03,VF11,VF04w		NOP
		ITOF4.xyz	VF01,VF01			NOP
		MINI.w		VF04,VF04,VF24		NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF01x		NOP
		MADDAy		ACC,VF13,VF01y		NOP
		MADDz		VF02,VF14,VF01z		NOP
		CLIPw.xyz	VF03xyz,VF03w		NOP
		
LoopRPC:MULw.xyz	VF08,VF07,VF07w		MTIR		VI06,VF01w
		ADDx		VF07,VF06,VF00x		LQ			VF06,0(VI03)
		MULAw		ACC,VF00,VF25w		IADD		VI03,VI03,VI04
		MADDq		VF05,VF04,Q			LQ			VF01,-1(VI03)
		ADDx.xyz	VF04,VF02,VF00x		FCAND		VI01,0x03FFFF
		MULA		ACC,VF10,VF02		DIV			Q,VF23w,VF02w
		MADDw		VF03,VF11,VF02w		SQ.xyz		VF08,0(VI02)
		ITOF4.xyz	VF01,VF01			IBNE		VI01,VI00,CullRPC
		MINI.w		VF04,VF02,VF24		MTIR		VI11,VF05w
		FTOI4.xyz	VF05,VF05			IOR			VI11,VI11,VI07
		ADDAx		ACC,VF15,VF00x		MFIR.w		VF05,VI11
		MADDAx		ACC,VF12,VF01x		IADD		VI02,VI02,VI04
		MADDAy		ACC,VF13,VF01y		IADDIU		VI07,VI06,0
		MADDz		VF02,VF14,VF01z		IBNE		VI02,VI05,LoopRPC
		CLIPw.xyz	VF03xyz,VF03w		SQ.xyzw		VF05,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

CullRPC:FTOI4.xyz	VF05,VF05			IOR			VI11,VI11,VI10
		ADDAx		ACC,VF15,VF00x		MFIR.w		VF05,VI11
		MADDAx		ACC,VF12,VF01x		IADD		VI02,VI02,VI04
		MADDAy		ACC,VF13,VF01y		IADDIU		VI07,VI06,0
		MADDz		VF02,VF14,VF01z		IBNE		VI02,VI05,LoopRPC
		CLIPw.xyz	VF03xyz,VF03w		SQ.xyzw		VF05,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

		
		
		.endif
		
		
		
		.else
		
		; original version


		.if 1
		; unoptimised

		FTOI15.w	VF05,VF00			NOP								; set VF05w=0x8000 (for ADC bit)

LoopRPC:NOP								LQ			VF07,0(VI02)		; get tex coords
		NOP								LQ.xyz		VF01,2(VI02)		; get vertex coords
		NOP								NOP
		NOP								NOP
		MULw.xyz	VF08,VF07,VF07w		NOP								; homogeneous divide (str)/r
		ITOF4.xyz	VF02,VF01			NOP								; vertex coords to float
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF02x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF02y		NOP								; row 1 view transform
		MADDz		VF03,VF14,VF02z		NOP								; row 2 view transform
		NOP								SQ.xyz		VF08,0(VI02)
		NOP								IADD		VI02,VI02,VI04		; step dest ptr
		NOP								NOP
		MULA		ACC,VF10,VF03		DIV			Q,VF00w,VF03w		; inv viewport scale	; calc 1/w
		MADDw		VF06,VF11,VF03w		NOP								; inv viewport offset
		NOP								NOP
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF06xyz,VF06w		NOP								; generate outcodes
		NOP								NOP
		MULq.xyz	VF05,VF03,Q			NOP								; homogeneous divide (xyz)/w
		NOP								NOP
		NOP								FCAND		VI01,0x03FFFF		; test last 3 outcodes
		NOP								IBNE		VI01,VI00,CullRPC	; cull if one was out
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopRPC	; loop
		NOP								SQ.xyz		VF05,-1(VI02)		; store screen coords
		
		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag

CullRPC:NOP								IBNE		VI02,VI05,LoopRPC	; loop
		NOP								SQ			VF05,-1(VI02)		; store screen coords
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
		.else
		; optimised
		
		ADDAx		ACC,VF15,VF00x		LQ.xyz		VF01,2(VI02)
		ITOF4.xyz	VF02,VF01			LQ.xyz		VF01,5(VI02)
		MADDAx		ACC,VF12,VF02x		LQ			VF07,0(VI02)
		MADDAy		ACC,VF13,VF02y		IADDIU		VI01,VI00,0x4000
		MADDz		VF03,VF14,VF02z		IADDIU		VI01,VI01,0x4000
		MULw.xyz	VF08,VF07,VF07w		MFIR.w		VF05,VI01
		ITOF4.xyz	VF02,VF01			LQ			VF07,3(VI02)
		MULA		ACC,VF10,VF03		NOP
		MADDw		VF06,VF11,VF03w		DIV			Q,VF00w,VF03w
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF02x		NOP
		
LoopRPC:MADDAy		ACC,VF13,VF02y		MOVE.xyz	VF04,VF03
		CLIPw.xyz	VF06xyz,VF06w		LQ.xyz		VF01,8(VI02)
		MADDz		VF03,VF14,VF02z		IADD		VI02,VI02,VI04
		MULw.xyz	VF08,VF07,VF07w		SQ.xyz		VF08,-3(VI02)
		MULq.xyz	VF05,VF04,Q			LQ			VF07,3(VI02)
		ITOF4.xyz	VF02,VF01			FCAND		VI01,0x03FFFF
		MULA		ACC,VF10,VF03		IBNE		VI01,VI00,CullRPC
		MADDw		VF06,VF11,VF03w		DIV			Q,VF00w,VF03w
		ADDAx		ACC,VF15,VF00x		IBNE		VI02,VI05,LoopRPC
		MADDAx		ACC,VF12,VF02x		SQ.xyz		VF05,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
CullRPC:ADDAx		ACC,VF15,VF00x		IBNE		VI02,VI05,LoopRPC
		MADDAx		ACC,VF12,VF02x		SQ			VF05,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
		.endif
		
		.endif




;-----------------------------------------------------------------------------------------------------------------------------
; lighting (2 diffuse + ambient)

Light1:
		.if 0
		; unoptimised version
		
		NOP								IADDIU		VI03,VI02,0
		
LoopL:	NOP								IADD		VI03,VI03,VI04
		NOP								LQ.xyz		VF01,-3(VI03)
		NOP								LQ.xyz		VF08,-2(VI03)
		NOP								NOP
		NOP								NOP
		ITOF15.xyz	VF02,VF01			NOP
		ITOF0.xyz	VF08,VF08			NOP
		NOP								NOP
		NOP								NOP
		MULAx.xyz	ACC,VF20,VF02x		NOP
		MADDAy.xyz	ACC,VF21,VF02y		NOP
		MADDz.xyz	VF03,VF22,VF02z		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MAXx.xyz	VF04,VF03,VF00x		NOP
		NOP								NOP
		NOP								NOP
		ADDAx.xyz	ACC,VF23,VF00x		NOP
		MADDAx.xyz	ACC,VF24,VF04x		NOP
		MADDAy.xyz	ACC,VF25,VF04y		NOP
		MADDz.xyz	VF05,VF26,VF04z		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MUL.xyz		VF05,VF05,VF08		NOP
		NOP								NOP
		NOP								NOP
		NOP								LOI			255
		MINIi.xyz	VF06,VF05,I			NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		FTOI0.xyz	VF07,VF06			NOP
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopL
		NOP								SQ.xyz		VF07,-2(VI02)
		

		.else
		; optimised version
		
		NOP								IADD		VI03,VI02,VI04
		NOP								LQ.xyz		VF02,-3(VI03)
		ITOF15.xyz	VF02,VF02			LQ.xyz		VF06,-2(VI03)
		MULAx.xyz	ACC,VF20,VF02x		IADD		VI03,VI03,VI04
		MADDAy.xyz	ACC,VF21,VF02y		LQ.xyz		VF01,-3(VI03)
		MADDz.xyz	VF02,VF22,VF02z		LQ.xyz		VF05,-2(VI03)
		ITOF0.xyz	VF06,VF06			NOP
		MAXx.xyz	VF02,VF02,VF00x		NOP
		ADDAx.xyz	ACC,VF23,VF00x		NOP
		ITOF15.xyz	VF01,VF01			NOP
		MADDAx.xyz	ACC,VF24,VF02x		NOP
		MADDAy.xyz	ACC,VF25,VF02y		NOP
		MADDz.xyz	VF03,VF26,VF02z		NOP
		MULAx.xyz	ACC,VF20,VF01x		NOP
		MADDAy.xyz	ACC,VF21,VF01y		NOP
		MADDz.xyz	VF02,VF22,VF01z		IADD		VI03,VI03,VI04
		MUL.xyz		VF03,VF03,VF06		LQ.xyz		VF01,-3(VI03)
		ITOF0.xyz	VF06,VF05			LQ.xyz		VF05,-2(VI03)
		MAXx.xyz	VF02,VF02,VF00x		NOP
		ADDAx.xyz	ACC,VF23,VF00x      LOI			255
		
LoopL:	ITOF15.xyz	VF01,VF01			NOP
		MINIi.xyz	VF04,VF03,I			NOP
		MADDAx.xyz	ACC,VF24,VF02x		NOP
		MADDAy.xyz	ACC,VF25,VF02y		NOP
		MADDz.xyz	VF03,VF26,VF02z		NOP
		MULAx.xyz	ACC,VF20,VF01x		NOP
		MADDAy.xyz	ACC,VF21,VF01y		NOP
		MADDz.xyz	VF02,VF22,VF01z		IADD		VI03,VI03,VI04
		FTOI0.xyz	VF04,VF04			IADD		VI02,VI02,VI04
		MUL.xyz		VF03,VF03,VF06		LQ.xyz		VF01,-3(VI03)
		ITOF0.xyz	VF06,VF05			LQ.xyz		VF05,-2(VI03)
		MAXx.xyz	VF02,VF02,VF00x		IBNE		VI02,VI05,LoopL
		ADDAx.xyz	ACC,VF23,VF00x		SQ.xyz		VF04,-2(VI02)
		
		.endif
		
		NOP								IADDIU		VI11,VI00,0x3000
		NOP								IAND		VI11,VI01,VI11
		
		NOP								ISUBIU		VI08,VI00,COLR+1
		NOP								IAND		VI14,VI14,VI08
		
		NOP								IBNE		VI10,VI00,Label2
		NOP								ISUB		VI02,VI05,VI06
		NOP								B			Label0
		NOP								NOP

;-----------------------------------------------------------------------------------------------------------------------------
; applying material colour

ApplyColour:

		.if 0
		; unoptimised version
		
		NOP								IADDIU		VI03,VI02,0
		
LoopAC:	NOP								IADD		VI03,VI03,VI04
		NOP								LQ.xyz		VF01,-2(VI03)
		NOP								NOP
		NOP								NOP
		NOP								NOP
		ITOF0.xyz	VF02,VF01			NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MUL.xyz		VF03,VF02,VF16		NOP
		NOP								NOP
		NOP								NOP
		NOP								LOI			255
		MINIi.xyz	VF04,VF03,I			NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		FTOI0.xyz	VF05,VF04			NOP
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopAC
		NOP								SQ.xyz		VF05,-2(VI02)
		
		.else
		; optimised version
		
		NOP								IADD		VI03,VI02,VI04
		NOP								LQ.xyz		VF01,-2(VI03)
		ITOF0.xyz	VF02,VF01			IADD		VI03,VI03,VI04
		MUL.xyz		VF03,VF02,VF16		LQ.xyz		VF01,-2(VI03)
		ITOF0.xyz	VF02,VF01			LOI			255
		MINIi.xyz	VF04,VF03,I			IADD		VI03,VI03,VI04
		MUL.xyz		VF03,VF02,VF16		LQ.xyz		VF01,-2(VI03)
		ITOF0.xyz	VF02,VF01			IADD		VI03,VI03,VI04
		FTOI0.xyz	VF05,VF04			LQ.xyz		VF01,-2(VI03)
		
LoopAC:	MINIi.xyz	VF04,VF03,I			IADD		VI03,VI03,VI04
		MUL.xyz		VF03,VF02,VF16		IADD		VI02,VI02,VI04
		ITOF0.xyz	VF02,VF01			LQ.xyz		VF01,-2(VI03)
		NOP								IBNE		VI02,VI05,LoopAC
		FTOI0.xyz	VF05,VF04			SQ.xyz		VF05,-2(VI02)
		
		.endif

		NOP								JR			VI01
		NOP								ISUB		VI02,VI02,VI06
		
;-----------------------------------------------------------------------------------------------------------------------------

		
SingleSided:

		.if 0

		NOP								IADDIU		VI03,VI02,0
		ADDw.x		VF08,VF00,VF00w		NOP
		FTOI15.w	VF08,VF00			NOP
		
Loop_SS:OPMULA.xyz	ACC,VF02,VF03		NOP
		OPMSUB.xyz	VF04,VF03,VF02		NOP
		ADDx		VF02,VF03,VF00x		NOP
		NOP								IADD		VI03,VI03,VI04
		NOP								LQ			VF01,-1(VI03)
		NOP								NOP
		NOP								NOP
		NOP								NOP
		ITOF4.xyz	VF03,VF01			MTIR		VI10,VF01w
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MUL.xyz		VF05,VF04,VF03		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		ADDAy.x		ACC,VF05,VF05y		NOP
		MADDz.x		VF00,VF08,VF05z		NOP
		NOP								NOP
		NOP								NOP
		NOP								IADD		VI02,VI02,VI04
		NOP								FMEQ		VI10,VI10
		NOP								IBEQ		VI10,VI00,Cull_SS
		NOP								NOP
		NOP								IBNE		VI02,VI05,Loop_SS
		NOP								NOP
		
		NOP								JR			VI01
		NOP								ISUB		VI02,VI02,VI06
		
Cull_SS:NOP								IBNE		VI02,VI05,Loop_SS
		NOP								SQ.w		VF08,-1(VI02)
		
		NOP								JR			VI01
		NOP								ISUB		VI02,VI02,VI06
		
		.else
		
		ADDw.x		VF08,VF00,VF00w		IADD		VI03,VI02,VI04
		FTOI15.w	VF08,VF00			LQ			VF02,-1(VI03)
		NOP								IADD		VI03,VI03,VI04
		ITOF4.xyz	VF02,VF02			LQ			VF01,-1(VI03)
		ITOF4		VF03,VF01			MTIR		VI10,VF02w
		
Loop_SS:FTOI4.w		VF03,VF03			IADD		VI03,VI03,VI04
		MUL.xyz		VF05,VF04,VF03		LQ			VF01,-1(VI03)
		OPMULA.xyz	ACC,VF02,VF03		IADD		VI02,VI02,VI04
		OPMSUB.xyz	VF04,VF03,VF02		FMEQ		VI10,VI10
		ADDx		VF02,VF03,VF00x		IBEQ		VI10,VI00,Cull_SS
		ITOF4		VF03,VF01			MTIR		VI10,VF03w
		ADDAy.x		ACC,VF05,VF05y		IBNE		VI02,VI05,Loop_SS
		MADDz.x		VF00,VF08,VF05z		NOP
		
		NOP								JR			VI01
		NOP								ISUB		VI02,VI02,VI06
		
Cull_SS:ADDAy.x		ACC,VF05,VF05y		IBNE		VI02,VI05,Loop_SS
		MADDz.x		VF00,VF08,VF05z		SQ.w		VF08,-1(VI02)
		
		NOP								JR			VI01
		NOP								ISUB		VI02,VI02,VI06
		
		.endif
		
;-----------------------------------------------------------------------------------------------------------------------------
; lines

Line1:	NOP								IBEQ		VI03,VI00,Proj1+16
		NOP								NOP

;-----------------------------------------------------------------------------------------------------------------------------
; line culling

CullLine:

		.if 0
		; unoptimised
		
		NOP								IADDIU		VI03,VI02,0			; source ptr = dest ptr
		FTOI15.w	VF05,VF00			NOP								; set VF05w=0x8000 (for ADC bit)

LoopKL:	NOP								IADD		VI03,VI03,VI04		; step source ptr
		NOP								LQ.xyz		VF01,-1(VI03)		; get vertex coords
		NOP								NOP
		NOP								NOP
		NOP								NOP
		ITOF4.xyz	VF02,VF01			NOP								; vertex coords to float
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF02x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF02y		NOP								; row 1 view transform
		MADDz		VF03,VF14,VF02z		NOP								; row 2 view transform
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULA		ACC,VF10,VF03		DIV			Q,VF00w,VF03w		; inv viewport scale	; calc 1/w
		MADDw		VF06,VF11,VF03w		NOP								; inv viewport offset
		NOP								NOP
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF06xyz,VF06w		NOP								; generate outcodes
		NOP								NOP
		MULq.xyz	VF05,VF03,Q			NOP								; homogeneous divide
		NOP								IADD		VI02,VI02,VI04		; step dest ptr
		NOP								FCAND		VI01,0x000FFF		; test last 3 outcodes
		NOP								IBNE		VI01,VI00,CullKL		; cull if all out
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopKL	; loop
		NOP								SQ.xyz		VF05,-1(VI02)		; store screen coords
		
		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag

CullKL:	NOP								IBNE		VI02,VI05,LoopKL	; loop
		NOP								SQ			VF05,-1(VI02)		; store screen coords
		
		.else
		; optimised
		
		FTOI15.w	VF05,VF00			IADD		VI03,VI02,VI04
		ADDAx		ACC,VF15,VF00x		LQ.xyz		VF01,-1(VI03)
		ITOF4.xyz	VF02,VF01			IADD		VI03,VI03,VI04
		MADDAx		ACC,VF12,VF02x		LQ.xyz		VF01,-1(VI03)
		MADDAy		ACC,VF13,VF02y		IADD		VI03,VI03,VI04
		MADDz		VF04,VF14,VF02z		NOP
		ITOF4.xyz	VF02,VF01			LQ.xyz		VF01,-1(VI03)
		MULA		ACC,VF10,VF04		DIV			Q,VF00w,VF04w
		MADDw		VF06,VF11,VF04w		NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF02x		NOP
		MADDAy		ACC,VF13,VF02y		NOP
		MADDz		VF03,VF14,VF02z		NOP
		
LoopKL:	CLIPw.xyz	VF06xyz,VF06w		IADD		VI03,VI03,VI04
		MULq.xyz	VF05,VF04,Q			IADD		VI02,VI02,VI04
		ITOF4.xyz	VF02,VF01			LQ.xyz		VF01,-1(VI03)
		MULA		ACC,VF10,VF03		DIV			Q,VF00w,VF03w
		MADDw		VF06,VF11,VF03w		FCAND		VI01,0x000FFF
		ADDAx		ACC,VF15,VF00x		IBNE		VI01,VI00,CullKL
		MADDAx		ACC,VF12,VF02x		MOVE.xyz	VF04,VF03
		MADDAy		ACC,VF13,VF02y		IBNE		VI02,VI05,LoopKL
		MADDz		VF03,VF14,VF02z		SQ.xyz		VF05,-1(VI02)
		
		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag

CullKL:	MADDAy		ACC,VF13,VF02y		IBNE		VI02,VI05,LoopKL
		MADDz		VF03,VF14,VF02z		SQ			VF05,-1(VI02)
		
		.endif
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

;-----------------------------------------------------------------------------------------------------------------------------

Shadow:
		.if 0
		; unoptimised version

		NOP								IADDIU		VI03,VI02,0
		NOP								LOI			0.5
		MULi.w		VF04,VF00,I			NOP
		NOP								MOVE.z		VF04,VF00
		FTOI15.w	VF01,VF00			NOP
		NOP								IADDIU		VI09,VI00,0x1F

LoopSH:	NOP                             IADD		VI03,VI03,VI04
		NOP								LQ.xyz		VF01,-1(VI03)
		NOP								NOP
		NOP								IAND		VI08,VI07,VI09
		NOP								NOP
		ITOF4.xyz	VF02,VF01			NOP
		NOP								NOP
		NOP								NOP
		ADDAx.xy	ACC,VF27,VF00x		NOP
		MADDx.x		VF03,VF16,VF02x		NOP
		MADDz.y		VF03,VF16,VF02z		NOP
		NOP								NOP
		NOP								NOP
		ADDy.z		VF04,VF27,VF02y		NOP
		SUBw.xy		VF04,VF03,VF04w		NOP
		FTOI12.xy	VF03,VF03			NOP
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF04xyz,VF04w		NOP
		NOP								NOP
		NOP								NOP
		NOP								SQ.xy		VF03,0(VI02)
		NOP								FCGET		VI07
		NOP								IAND		VI01,VI01,VI07
		NOP								IADD		VI02,VI02,VI04
		NOP								IBNE		VI01,VI00,CullSH
		NOP								IAND		VI01,VI07,VI08
		NOP								IBNE		VI02,VI05,LoopSH
		NOP								NOP
		
		NOP								B			EndSH
		NOP								NOP
		
CullSH:	NOP								IBNE		VI02,VI05,LoopSH
		NOP								SQ.w		VF01,-1(VI02)
		
		.else
		; optimised version
		
		FTOI15.w	VF01,VF00			IADD		VI03,VI02,VI04
		ADDAx.xy	ACC,VF27,VF00x		LQ.xyz		VF01,-1(VI03)
		ITOF4.xyz	VF02,VF01			IADD		VI03,VI03,VI04
		MADDx.x		VF03,VF16,VF02x		LQ.xyz		VF01,-1(VI03)
		MADDz.y		VF03,VF16,VF02z		LOI			0.5
		MULi.w		VF04,VF00,I			IADDIU		VI09,VI00,0x1F
		ADDy.z		VF04,VF27,VF02y		NOP
		SUBw.xy		VF04,VF03,VF04w		NOP
		FTOI12.xy	VF03,VF03			NOP
		ITOF4.xyz	VF02,VF01			NOP
		CLIPw.xyz	VF04xyz,VF04w       MOVE.z		VF04,VF00
		
LoopSH:	ADDAx.xy	ACC,VF27,VF00x		SQ.xy		VF03,0(VI02)
		MADDx.x		VF03,VF16,VF02x		IAND		VI08,VI07,VI09
		MADDz.y		VF03,VF16,VF02z		IADD		VI03,VI03,VI04
		NOP								FCGET		VI07
		NOP								LQ.xyz		VF01,-1(VI03)
		ADDy.z		VF04,VF27,VF02y		IAND		VI01,VI01,VI07
		SUBw.xy		VF04,VF03,VF04w		IADD		VI02,VI02,VI04
		FTOI12.xy	VF03,VF03			IBEQ		VI01,VI00,KeepSH
		ITOF4.xyz	VF02,VF01			IAND		VI01,VI07,VI08
		NOP								IBNE		VI02,VI05,LoopSH
		CLIPw.xyz	VF04xyz,VF04w		SQ.w		VF01,-1(VI02)
		
		NOP								B			EndSH
		NOP								NOP
		
KeepSH:	CLIPw.xyz	VF00xyz,VF00w		IBNE		VI02,VI05,LoopSH
		CLIPw.xyz	VF04xyz,VF04w		NOP
		
		.endif
		
EndSH:	NOP                             ISUBIU		VI14,VI14,SHDW
		NOP								B			PTex1
		NOP								ISUB		VI02,VI02,VI06
		
;-----------------------------------------------------------------------------------------------------------------------------

ReformatXforms:

		NOP								LQ.xyz		VF01,3(VI00)
		MULx.w		VF02,VF00,VF01x		XITOP		VI06
		MULy.w		VF03,VF00,VF01y		IADDIU		VI01,VI00,0
		
LoopRT:	MULz.w		VF04,VF00,VF01z		LQ.xyz		VF01,7(VI01)
		NOP								IADDIU		VI01,VI01,4
		NOP								SQ.w		VF02,-4(VI01)
		NOP								SQ.w		VF03,-3(VI01)
		MULx.w		VF02,VF00,VF01x		IBNE		VI01,VI06,LoopRT
		MULy.w		VF03,VF00,VF01y		SQ.w		VF04,-2(VI01)
		
		NOP[E]							NOP
		NOP								NOP
		
		
;-----------------------------------------------------------------------------------------------------------------------------

; vertex format
; -------------
; (s,t,1,?)
; (w0,w1,w2,?)
; (nx:to0, ny:to1, nz:to2, ?)
; (r,g,b,a)
; (x,y,z,adc)



Skin1:
		;---------------------------------------------------------------
		; wireframe
		
		; 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 58 47
		; s  x  x  x  x  x  x  x  x  m  m  m  m  m  m  m  m
		;                   <-------------PRIM------------->
		;                         1  0  0  0  0  0  0  1  0
		
		NOP								IADDIU		VI01,VI00,WIRE
		NOP								IAND		VI01,VI14,VI01
		NOP								NOP
		NOP								IBEQ		VI01,VI00,SkipWireframe
		NOP								NOP
		
		NOP								ISUBIU		VI14,VI14,WIRE
		
		NOP								LQ.y		VF01,-1(VI02)
		NOP								RINIT		R,VF01y
		NOP								RGET.y		VF02,R
		NOP								DIV			Q,VF00w,VF02y
		MULq.y		VF03,VF01,Q			WAITQ
		ADDAx.y		ACC,VF01,VF00x		LOI			0.0078125
		MSUBAi.y	ACC,VF03,I			LOI			0.0625
		MSUBi.y		VF04,VF03,I			NOP
		NOP								SQ.y		VF04,-1(VI02)

SkipWireframe:
		;---------------------------------------------------------------
	
		NOP								IBNE		VI14,VI00,CullSkin
		NOP								IADDIU		VI01,VI00,SHDW
		
		
		.if 0
		; unoptimised

LoopS:	NOP								LQ.xyz		VF02,2(VI02)		; get normal & offsets
		NOP								LQ.xyz		VF03,1(VI02)		; get weights
		NOP								NOP
		NOP								NOP
		NOP								MTIR		VI08,VF02x										; offset of M0
		NOP								MTIR		VI09,VF02y										; offset of M1
		ITOF15.xyz	VF02,VF02			MTIR		VI10,VF02z			; normal to float			; offset of M2
		ITOF15.xyz	VF03,VF03			LQ			VF05,0(VI08)		; weights to float			; get M0 row 0
		NOP								LQ			VF06,1(VI08)									; get M0 row 1
		NOP								LQ			VF07,2(VI08)									; get M0 row 2
		NOP								NOP
		MULAx.xyz	ACC,VF05,VF02x		LQ			VF08,0(VI09)		; nx*(M0 row 0)				; get M1 row 0
		MADDAy.xyz	ACC,VF06,VF02y		LQ			VF30,0(VI10)		; ny*(M0 row 1)				; get M2 row 0
		MADDz.xyz	VF02,VF07,VF02z		NOP								; nz*(M0 row 2)
		MULAx		ACC,VF05,VF03x		NOP								; row 0 w0*M0
		MADDAy		ACC,VF08,VF03y		NOP								; add row 0 w1*M1
		MADDz		VF05,VF30,VF03z		NOP								; add row 0 w2*M2
		MULAx.xyz	ACC,VF20,VF02x		LQ			VF08,1(VI09)		; lighting dot prods x part	; get M1 row 1
		MADDAy.xyz	ACC,VF21,VF02y		LQ			VF30,1(VI10)		; lighting dot prods y part	; get M2 row 1
		MADDz.xyz	VF02,VF22,VF02z		NOP								; lighting dot prods z part
		MULAx		ACC,VF06,VF03x		MR32.z		VF31,VF05			; row 1 w0*M0
		MADDAy		ACC,VF08,VF03y		LQ.xyz		VF01,4(VI02)		; add row 1 w1*M1			; get xyz
		MADDz		VF06,VF30,VF03z		LQ.xyz		VF29,3(VI02)		; add row 1 w2*M2			; get rgb
		MAXx.xyz	VF02,VF02,VF00x		LQ			VF08,2(VI09)		; clamp dot prods at 0		; get M1 row 2
		NOP								LQ			VF30,2(VI10)									; get M2 row 2
		NOP								MR32.y		VF31,VF31
		MULAx		ACC,VF07,VF03x		MR32.z		VF31,VF06			; row 2 w0*M0
		MADDAy		ACC,VF08,VF03y		NOP								; add row 2 w1*M1
		MADDz		VF07,VF30,VF03z		NOP								; add row 2 w2*M2
		ITOF4.xyz	VF01,VF01			NOP
		ITOF0.xyz	VF29,VF29			NOP
		ADDAx.xyz	ACC,VF23,VF00x		NOP								; ambient colour
		MADDAx.xyz	ACC,VF24,VF02x		MR32.xy		VF31,VF31			; add diffuse 0
		MADDAy.xyz	ACC,VF25,VF02y		MR32.z		VF31,VF07			; add diffuse 1
		MADDz.xyz	VF02,VF26,VF02z		NOP								; add diffuse 2
		MULAx.xyz	ACC,VF05,VF01x		NOP								; add x*(M row 0)
		MADDAy.xyz	ACC,VF06,VF01y		NOP								; add y*(M row 1)
		MADDAz.xyz	ACC,VF07,VF01z		NOP								; add z*(M row 2)
		MADDw.xyz	VF01,VF31,VF00w		NOP								; M row 3
		ADDAw.xyz	ACC,VF00,VF00w		NOP
		MADD.xyz	VF02,VF02,VF29		NOP
		ADDAx		ACC,VF19,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF16,VF01x		NOP								; row 0 view transform
		MADDAy		ACC,VF17,VF01y		NOP								; row 1 view transform
		MADDz		VF01,VF18,VF01z		LOI			1.00003039837		; row 2 view transform
		MINIi.xyz	VF02,VF02,I			NOP
		NOP								NOP
		NOP								NOP
		NOP								DIV			Q,VF00w,VF01w
		NOP								SQ.xyz		VF02,3(VI02)
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULq.xyz	VF01,VF01,Q			NOP
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopS
		NOP								SQ.xyz		VF01,-1(VI02)
		
		.else
		; optimised
		
		NOP								LQ.xyz		VF01,2(VI02)
		NOP								LQ.xyz		VF03,1(VI02)
		NOP								MTIR		VI08,VF01x
		ITOF15.xyz	VF02,VF01			LQ			VF05,0(VI08)
		ITOF15.xyz	VF03,VF03			LQ			VF06,1(VI08)
		MULAx.xyz	ACC,VF05,VF02x		LQ			VF07,2(VI08)
		MADDAy.xyz	ACC,VF06,VF02y		MTIR		VI09,VF01y
		MADDz.xyz	VF02,VF07,VF02z		LQ			VF08,0(VI09)
		MULAx		ACC,VF05,VF03x		MTIR		VI10,VF01z
		MADDAy		ACC,VF08,VF03y		LQ			VF30,0(VI10)
		MADDz		VF05,VF30,VF03z		LQ			VF08,1(VI09)
		MULAx.xyz	ACC,VF20,VF02x		LQ			VF30,1(VI10)
		MADDAy.xyz	ACC,VF21,VF02y		NOP
		MADDz.xyz	VF02,VF22,VF02z		MR32.z		VF31,VF05
		MULAx		ACC,VF06,VF03x		LQ			VF04,2(VI09)
		MADDAy		ACC,VF08,VF03y		LQ			VF29,2(VI10)
		MADDz		VF06,VF30,VF03z		LOI			0x3F8000FF										; I = 1+255/(2^23)


LoopS:	MAXx.xyz	VF02,VF02,VF00x		LQ.xyz		VF01,4(VI02)		; clamp dot prods at 0		; get vertex
		MULAx		ACC,VF07,VF03x		NOP								;  row 2 M0*w0
		MADDAy		ACC,VF04,VF03y		MR32.y		VF31,VF31			; +row 2 M1*w1				; VF31 = (?,(M)30,?)
		MADDz		VF07,VF29,VF03z		MR32.z		VF31,VF06			; +row 2 M2*w2				; VF31 = (?,(M)30,(M)31)
		ITOF4.xyz	VF01,VF01			LQ.xyz		VF29,3(VI02)		; vertex to float			; get rgb
		ADDAx.xyz	ACC,VF23,VF00x		NOP								;  ambient
		MADDAx.xyz	ACC,VF24,VF02x		NOP								; +diffuse 0
		MADDAy.xyz	ACC,VF25,VF02y		MR32.xy		VF31,VF31			; +diffuse 1				; VF31 = ((M)30,(M)31,?)
		MADDz.xyz	VF04,VF26,VF02z		NOP								; +diffuse 2
		ITOF0.xyz	VF29,VF29			MR32.z		VF31,VF07			; rgb to float				; VF31 = M row 3
		MULAx.xyz	ACC,VF05,VF01x		LQ.xyz		VF02,7(VI02)		;  x * (M row 0)			; get normal and offsets
		MADDAy.xyz	ACC,VF06,VF01y		LQ.xyz		VF03,6(VI02)		; +y * (M row 1)			; get weights
		MADDAz.xyz	ACC,VF07,VF01z		NOP								; +z * (M row 2)
		MADDw.xyz	VF01,VF31,VF00w		NOP								; +1 * (M row 3)
		ADDAw.xyz	ACC,VF00,VF00w		MTIR		VI08,VF02x			;  (1,1,1)					; offset of M0
		MADD.xyz	VF04,VF04,VF29		LQ			VF05,0(VI08)		; +illum * rgb				; get M0 row 0
		ADDAx		ACC,VF19,VF00x		LQ			VF06,1(VI08)		; row 3 view transform		; get M0 row 1
		MADDAx		ACC,VF16,VF01x		MTIR		VI09,VF02y			; row 0 view transform		; offset of M1
		ITOF15.xyz	VF02,VF02			MTIR		VI10,VF02z			; normal to float			; offset of M2
		MADDAy		ACC,VF17,VF01y		LQ			VF07,2(VI08)		; row 1 view transform		; get M0 row 2
		MADDz		VF01,VF18,VF01z		LQ			VF08,0(VI09)		; row 2 view transform		; get M1 row 0
		MINIi.xyz	VF04,VF04,I			NOP								; clamp rgb at 255
		ITOF15.xyz	VF03,VF03			NOP								; weights to float
		MULAx.xyz	ACC,VF05,VF02x		LQ			VF30,0(VI10)		;  nx * (M0 row 0)			; get M2 row 0
		MADDAy.xyz	ACC,VF06,VF02y		DIV			Q,VF00w,VF01w		; +ny * (M0 row 1)			; calc 1/w
		MADDz.xyz	VF02,VF07,VF02z		SQ.xyz		VF04,3(VI02)		; +nz * (M0 row 2)			; store colour
		MULAx		ACC,VF05,VF03x		NOP								;  row 0 M0*w0
		MADDAy		ACC,VF08,VF03y		LQ			VF08,1(VI09)		; +row 0 M1*w1				; get M1 row 1
		MADDz		VF05,VF30,VF03z		LQ			VF30,1(VI10)		; +row 0 M2*w2				; get M2 row 1
		MULAx.xyz	ACC,VF20,VF02x		NOP								; lighting dot prods x part
		MADDAy.xyz	ACC,VF21,VF02y		IADD		VI02,VI02,VI04		; lighting dot prods y part	; step pointer
		MULq.xyz	VF01,VF01,Q			LQ			VF04,2(VI09)		; homogeneous div (xyz)/w	; get M1 row 2
		MADDz.xyz	VF02,VF22,VF02z		LQ			VF29,2(VI10)		; lighting dot prods z part	; get M2 row 2
		MULAx		ACC,VF06,VF03x		MR32.z		VF31,VF05			;  row 1 M0*w0				; VF31 = (?,?,(M)30)
		MADDAy		ACC,VF08,VF03y		IBNE		VI02,VI05,LoopS		; +row 1 M1*w1				; loop
		MADDz		VF06,VF30,VF03z		SQ.xyz		VF01,-1(VI02)		; +row 1 M2*w2				; store screen coords
		
		.endif
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)
		
		
		
;-----------------------------------------------------------------------------------------------------------------------------

CullSkin:
		NOP								NOP								; need this cycle here
		NOP								IBEQ		VI14,VI01,ShadowSkin
		NOP								NOP
		
		; new version
		
		.if 0
		; unoptimised
		
		FTOI15.w	VF03,VF00			NOP
		
		
LoopKS:	NOP								LQ.xyz		VF02,2(VI02)		; get normal & offsets
		NOP								LQ.xyz		VF03,1(VI02)		; get weights
		NOP								NOP
		NOP								NOP
		NOP								MTIR		VI08,VF02x										; offset of M0
		NOP								MTIR		VI09,VF02y										; offset of M1
		ITOF15.xyz	VF02,VF02			MTIR		VI10,VF02z			; normal to float			; offset of M2
		ITOF15.xyz	VF03,VF03			LQ			VF05,0(VI08)		; weights to float			; get M0 row 0
		NOP								LQ			VF06,1(VI08)									; get M0 row 1
		NOP								LQ			VF07,2(VI08)									; get M0 row 2
		NOP								NOP
		MULAx.xyz	ACC,VF05,VF02x		LQ			VF08,0(VI09)		; nx*(M0 row 0)				; get M1 row 0
		MADDAy.xyz	ACC,VF06,VF02y		LQ			VF30,0(VI10)		; ny*(M0 row 1)				; get M2 row 0
		MADDz.xyz	VF02,VF07,VF02z		NOP								; nz*(M0 row 2)
		MULAx		ACC,VF05,VF03x		NOP								; row 0 w0*M0
		MADDAy		ACC,VF08,VF03y		NOP								; add row 0 w1*M1
		MADDz		VF05,VF30,VF03z		NOP								; add row 0 w2*M2
		MULAx.xyz	ACC,VF20,VF02x		LQ			VF08,1(VI09)		; lighting dot prods x part	; get M1 row 1
		MADDAy.xyz	ACC,VF21,VF02y		LQ			VF30,1(VI10)		; lighting dot prods y part	; get M2 row 1
		MADDz.xyz	VF02,VF22,VF02z		NOP								; lighting dot prods z part
		MULAx		ACC,VF06,VF03x		MR32.z		VF31,VF05			; row 1 w0*M0
		MADDAy		ACC,VF08,VF03y		LQ.xyz		VF01,4(VI02)									; add row 1 w1*M1
		MADDz		VF06,VF30,VF03z		LQ.xyz		VF29,3(VI02)									; add row 1 w2*M2
		MAXx.xyz	VF02,VF02,VF00x		LQ			VF08,2(VI09)		; clamp dot prods at 0		; get M1 row 2
		NOP								LQ			VF30,2(VI10)									; get M2 row 2
		NOP								NOP
		MULAx		ACC,VF07,VF03x		NOP
		MADDAy		ACC,VF08,VF03y		MR32.y		VF31,VF31
		MADDz		VF07,VF30,VF03z		MR32.z		VF31,VF06
		ITOF4.xyz	VF01,VF01			NOP
		ITOF0.xyz	VF29,VF29			NOP
		ADDAx.xyz	ACC,VF23,VF00x		NOP								; ambient colour
		MADDAx.xyz	ACC,VF24,VF02x		MR32.xy		VF31,VF31			; add diffuse 0
		MADDAy.xyz	ACC,VF25,VF02y		MR32.z		VF31,VF07			; add diffuse 1
		MADDz.xyz	VF02,VF26,VF02z		NOP								; add diffuse 2
		MULAx.xyz	ACC,VF05,VF01x		NOP								; add x*(M row 0)
		MADDAy.xyz	ACC,VF06,VF01y		NOP								; add y*(M row 1)
		MADDAz.xyz	ACC,VF07,VF01z		NOP								; add z*(M row 2)
		MADDw.xyz	VF01,VF31,VF00w		NOP								; M row 3
		ADDAw.xyz	ACC,VF00,VF00w		NOP
		MADD.xyz	VF02,VF02,VF29		NOP
		ADDAx		ACC,VF19,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF16,VF01x		NOP								; row 0 view transform
		MADDAy		ACC,VF17,VF01y		NOP								; row 1 view transform
		MADDz		VF01,VF18,VF01z		LOI			1.00003039837		; row 2 view transform
		MINIi.xyz	VF02,VF02,I			NOP
		NOP								NOP
		NOP								NOP
		MULA		ACC,VF10,VF01		DIV			Q,VF00w,VF01w
		MADDw		VF05,VF11,VF01w		SQ.xyz		VF02,3(VI02)
		NOP								NOP
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF05xyz,VF05w		NOP
		NOP								NOP
		MULq.xyz	VF03,VF01,Q			NOP
		NOP								IADD		VI02,VI02,VI04
		NOP								FCAND		VI01,0x03FFFF
		NOP								IBNE		VI01,VI00,CullKS
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopKS
		NOP								SQ.xyz		VF03,-1(VI02)
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

CullKS:	NOP								IBNE		VI02,VI05,LoopKS
		NOP								SQ			VF03,-1(VI02)
		
		.else
		; optimised

		FTOI15.w	VF04,VF00			LQ.xyz		VF01,2(VI02)		; set ADC bit
		NOP								LQ.xyz		VF03,1(VI02)
		NOP								MTIR		VI08,VF01x
		NOP								LQ			VF05,0(VI08)
		ITOF15.xyz	VF02,VF01			LQ			VF06,1(VI08)
		ITOF15.xyz	VF03,VF03			LQ			VF07,2(VI08)
		MULAx.xyz	ACC,VF05,VF02x		MTIR		VI09,VF01y
		MADDAy.xyz	ACC,VF06,VF02y		MTIR		VI10,VF01z
		MADDz.xyz	VF02,VF07,VF02z		LQ			VF08,0(VI09)
		MULAx		ACC,VF05,VF03x		LQ			VF30,0(VI10)
		MADDAy		ACC,VF08,VF03y		NOP
		MADDz		VF05,VF30,VF03z		LQ			VF31,1(VI09)
		MULAx.xyz	ACC,VF20,VF02x		LQ			VF29,1(VI10)
		MADDAy.xyz	ACC,VF21,VF02y		LOI			0x3F8000FF										; I = 1+255/(2^23)
		MADDz.xyz	VF02,VF22,VF02z		LQ			VF08,2(VI09)
		MULAx		ACC,VF06,VF03x		LQ			VF30,2(VI10)
		
		
LoopKS:	MADDAy		ACC,VF31,VF03y		MR32.z		VF31,VF05			; +row 1 M1*w1				; VF31 = (?,?,(M)30)
		MADDz		VF06,VF29,VF03z		NOP								; +row 1 M2*w2
		MAXx.xyz	VF02,VF02,VF00x		LQ.xyz		VF01,4(VI02)		; clamp dot prods at 0		; get vertex
		MULAx		ACC,VF07,VF03x		LQ.xyz		VF29,3(VI02)		;  row 2 M0*w0				; get rgb
		MADDAy		ACC,VF08,VF03y		MR32.y		VF31,VF31			; +row 2 M1*w1				; VF31 = (?,(M)30,?)
		MADDz		VF07,VF30,VF03z		MR32.z		VF31,VF06			; +row 2 M2*w2				; VF31 = (?,(M)30,(M)31)
		ITOF4.xyz	VF01,VF01			NOP								; vertex to float
		ITOF0.xyz	VF29,VF29			NOP								; rgb to float
		ADDAx.xyz	ACC,VF23,VF00x		NOP								; ambient
		MADDAx.xyz	ACC,VF24,VF02x		MR32.xy		VF31,VF31			; +diffuse 0				; VF31 = ((M)30,(M)31,?)
		MADDAy.xyz	ACC,VF25,VF02y		MR32.z		VF31,VF07			; +diffuse 1				; VF31 = M row 3
		MADDz.xyz	VF04,VF26,VF02z		NOP								; +diffuse 2
		MULAx.xyz	ACC,VF05,VF01x		LQ.xyz		VF02,7(VI02)		;  x * (M row 0)			; get normal and offsets
		MADDAy.xyz	ACC,VF06,VF01y		NOP								; +y * (M row 1)
		MADDAz.xyz	ACC,VF07,VF01z		NOP								; +z * (M row 2)
		MADDw.xyz	VF01,VF31,VF00w		LQ.xyz		VF03,6(VI02)		; +1 * (M row 3)			; get weights
		ADDAw.xyz	ACC,VF00,VF00w		NOP								;  (1,1,1)
		MADD.xyz	VF04,VF04,VF29		MTIR		VI08,VF02x			; +illum * rgb				; offset of M0
		ADDAx		ACC,VF19,VF00x		LQ			VF05,0(VI08)		; row 3 view transform		; get M0 row 0
		MADDAx		ACC,VF16,VF01x		LQ			VF06,1(VI08)		; row 0 view transform		; get M0 row 1
		MADDAy		ACC,VF17,VF01y		MTIR		VI09,VF02y			; row 1 view transform		; offset of M1
		MADDz		VF01,VF18,VF01z		MTIR		VI10,VF02z			; row 2 view transform		; offset of M2
		MINIi.xyz	VF04,VF04,I			LQ			VF07,2(VI08)		; clamp rgb at 255			; get M0 row 2
		ITOF15.xyz	VF02,VF02			LQ			VF08,0(VI09)		; normal to float			; get M1 row 0
		ITOF15.xyz	VF03,VF03			NOP								; weights to float
		MULA		ACC,VF10,VF01		DIV			Q,VF00w,VF01w		; inverse viewport scale	; calc 1/w
		MADDw		VF31,VF11,VF01w		SQ.xyz		VF04,3(VI02)		; inverse viewport offset	; store colour
		MULAx.xyz	ACC,VF05,VF02x		LQ			VF30,0(VI10)		;  nx * (M0 row 0)			; get M2 row 0
		MADDAy.xyz	ACC,VF06,VF02y		NOP								; +ny * (M0 row 1)
		MADDz.xyz	VF02,VF07,VF02z		NOP								; +nz * (M0 row 2)
		CLIPw.xyz	VF31xyz,VF31w		LQ			VF31,1(VI09)		; generate clip codes		; get M1 row 1
		MULAx		ACC,VF05,VF03x		LQ			VF29,1(VI10)		;  row 0 M0*w0				; get M2 row 1
		MADDAy		ACC,VF08,VF03y		IADD		VI02,VI02,VI04		; +row 0 M1*w1				; step pointer
		MADDz		VF05,VF30,VF03z		LQ			VF08,2(VI09)		; +row 0 M2*w2				; get M1 row 2
		MULq.xyz	VF04,VF01,Q			FCAND		VI01,0x03FFFF		; homogeneous div (xyz)/w	; last 3 clip codes
		MULAx.xyz	ACC,VF20,VF02x		IBNE		VI01,VI00,CullKS	; lighting dot prods x part	; cull if any non-zero
		MADDAy.xyz	ACC,VF21,VF02y		LQ			VF30,2(VI10)		; lighting dot prods y part	; get M2 row 2
		MADDz.xyz	VF02,VF22,VF02z		IBNE		VI02,VI05,LoopKS	; lighting dot prods z part	; loop
		MULAx		ACC,VF06,VF03x		SQ.xyz		VF04,-1(VI02)		;  row 1 M0*w0				; store screen coords
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

CullKS:	MADDz.xyz	VF02,VF22,VF02z		IBNE		VI02,VI05,LoopKS	; lighting dot prods z part ; loop
		MULAx		ACC,VF06,VF03x		SQ			VF04,-1(VI02)       ;  row 1 M0*w0				; store screen coords
		
		.endif
		
		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)

;-----------------------------------------------------------------------------------------------------------------------------
; shadow version

ShadowSkin:

		.if 0
		; unoptimised
		
LoopSS:
		NOP								ILW.x		VI08,2(VI02)		; get primary transform offset
		NOP								LQ.xyz		VF01,4(VI02)		; get vertex coords
		ITOF4.xyz	VF01,VF01			NOP								; vertex coords to float
		NOP								LQ			VF04,0(VI08)		; get M0 row 0
		NOP								LQ			VF05,1(VI08)        ; get M0 row 1
		NOP								LQ			VF06,2(VI08)		; get M0 row 2
		ADDAw.x		ACC,VF00,VF04w		NOP								; ACCx = (M0)30
		ADDAw.y		ACC,VF00,VF05w		NOP								; ACCy = (M0)31
		ADDAw.z		ACC,VF00,VF06w		NOP								; ACCz = (M0)32
		MADDAx.xyz	ACC,VF04,VF01x		NOP								; +x * M0 row 0
		MADDAy.xyz	ACC,VF05,VF01y		NOP								; +y * M0 row 1
		MADDz.xyz	VF01,VF06,VF01z		NOP								; +z * M0 row 2
		NOP								IADD		VI02,VI02,VI04		; step pointer
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF01x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF01y		NOP								; row 1 view transform
		MADDz		VF01,VF14,VF01z		NOP								; row 2 view transform
		NOP								DIV			Q,VF00w,VF01w		; calc 1/w
		MULq.xyz	VF01,VF01,Q			WAITQ							; homogeneous div (xyz)/w
		NOP								IBNE		VI02,VI05,LoopSS	; loop
		NOP								SQ.xyz		VF01,-1(VI02)		; store screen coords
		
		.else
		; optimised
		
		NOP								ILW.x		VI08,2(VI02)
		NOP								LQ			VF01,4(VI02)
		ITOF4.xyz	VF02,VF01			LQ			VF08,3(VI08)
		ADDAx.xyz	ACC,VF08,VF00		LQ			VF05,0(VI08)
		MADDAx.xyz	ACC,VF05,VF02x		LQ			VF06,1(VI08)
		MADDAy.xyz	ACC,VF06,VF02y		LQ			VF07,2(VI08)
		MADDz.xyz	VF02,VF07,VF02z		ILW.x		VI08,7(VI02)
		ADDAx		ACC,VF15,VF00x		LQ			VF01,9(VI02)
		MADDAx		ACC,VF12,VF02x		LQ			VF05,0(VI08)
		MADDAy		ACC,VF13,VF02y		LQ			VF06,1(VI08)
		MADDz		VF03,VF14,VF02z		LQ			VF07,2(VI08)
		
LoopSS:	ITOF4.xyz	VF02,VF01			ILW.x		VI08,12(VI02)
		ADDAw.x		ACC,VF00,VF05w		IADD		VI02,VI02,VI04
		ADDAw.y		ACC,VF00,VF06w		DIV			Q,VF00w,VF03w
		ADDAw.z		ACC,VF00,VF07w		NOP
		MADDAx.xyz	ACC,VF05,VF02x		NOP
		MADDAy.xyz	ACC,VF06,VF02y		LQ			VF01,9(VI02)
		MADDz.xyz	VF02,VF07,VF02z		LQ			VF05,0(VI08)
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP
		MULq.xyz	VF04,VF03,Q			LQ			VF06,1(VI08)
		MADDAx		ACC,VF12,VF02x		LQ			VF07,2(VI08)
		MADDAy		ACC,VF13,VF02y		NOP
		MADDz		VF03,VF14,VF02z		IBNE		VI02,VI05,LoopSS
		NOP								SQ.xyz		VF04,-1(VI02)
		
		.endif

		NOP								B			NextPrim
		NOP								LQI			VF01,(VI02++)













;-----------------------------------------------------------------------------------------------------------------------------


		; context data:
		
		; VF14 = shadow vec (magnitude defines length of shadow polys), in body coords
		; VF15 = tweak vec (small, parallel to shadow vec), in body coords
		; VF16-19 = body to frustum transform


		
		;      3-----0-----5
		;		\   / \   /
		;        \ /   \ /
		;         1-----2
		;          \   /
		;			\ /
		;            4


ShadowVolumeSkin:

		; set up VF07 = VF14x*VF16 + VF14y*VF17 + VF14z*VF18 + VF19
		MULAx		ACC,VF16,VF14x		NOP
		MADDAy		ACC,VF17,VF14y		NOP
		MADDAz		ACC,VF18,VF14z		NOP
		MADDw		VF07,VF19,VF00w		NOP
		
		; set up VF08 = VF15x*VF16 + VF15y*VF17 + VF15z*VF18 + VF19
		MULAx		ACC,VF16,VF15x		NOP
		MADDAy		ACC,VF17,VF15y		NOP
		MADDAz		ACC,VF18,VF15z		NOP
		MADDw		VF08,VF19,VF00w		IADDIU		VI09,VI00,0x10		; mask for Sw FMAC flag

		NOP								IADDIU		VI08, VI00, 0

LoopSVS:

		NOP								LQ			VF20,1(VI02)		; | Load vector 0
		NOP								LQ			VF21,2(VI02)        ; | Load vector 1 
		NOP								LQ			VF22,3(VI02)        ; | Load vector 2 
		NOP								LQ			VF23,4(VI02)        ; | Load vector 3 
		NOP								LQ			VF24,5(VI02)        ; | Load vector 4 
		
		NOP								MTIR		VI01,VF20w			; | Load up matrix 0 index
		NOP								LQ			VF04,3(VI01)
		NOP								LQ			VF01,0(VI01)
		NOP								LQ			VF02,1(VI01)
		NOP								LQ			VF03,2(VI01)

		; v0
		ADDAx.xyz	ACC,VF04,VF00x		MTIR		VI01,VF21w          ; | Load up matrix 1 index 
		MADDAx.xyz	ACC,VF01,VF20x		LQ			VF04,3(VI01)
		MADDAy.xyz	ACC,VF02,VF20y		LQ			VF01,0(VI01)
		MADDz.xyz	VF20,VF03,VF20z		LQ			VF02,1(VI01)
		NOP								LQ			VF03,2(VI01)

		; v1
		ADDAx.xyz	ACC,VF04,VF00x		MTIR		VI01,VF22w          ; | Load up matrix 2 index 
		MADDAx.xyz	ACC,VF01,VF21x		LQ			VF04,3(VI01)
		MADDAy.xyz	ACC,VF02,VF21y		LQ			VF01,0(VI01)
		MADDz.xyz	VF21,VF03,VF21z		LQ			VF02,1(VI01)
		NOP								LQ			VF03,2(VI01)

		; v2
		ADDAx.xyz	ACC,VF04,VF00x		MTIR		VI01,VF23w          ; | Load up matrix 3 index 
		MADDAx.xyz	ACC,VF01,VF22x		LQ			VF04,3(VI01)
		MADDAy.xyz	ACC,VF02,VF22y		LQ			VF01,0(VI01)
		MADDz.xyz	VF22,VF03,VF22z		LQ			VF02,1(VI01)
		SUB.xyz		VF26,VF21,VF20      LQ			VF03,2(VI01)

		; v3
		ADDAx.xyz	ACC,VF04,VF00x		LQ			VF25,6(VI02)        ; | Load vector 5
		MADDAx.xyz	ACC,VF01,VF23x		MTIR		VI01,VF24w          ; | Load up matrix 4 index 
		SUB.xyz		VF27,VF22,VF20		LQ			VF04,3(VI01)
		MADDAy.xyz	ACC,VF02,VF23y		LQ			VF01,0(VI01)
		MADDz.xyz	VF23,VF03,VF23z		LQ			VF02,1(VI01)
		NOP								LQ			VF03,2(VI01)

		; v4
		OPMULA.xyz	ACC,VF26,VF27		NOP
		OPMSUB.xyz	VF05,VF27,VF26		NOP
		ADDAx.xyz	ACC,VF04,VF00x		MTIR		VI01,VF25w          ; | Load up matrix 5 index 
		MADDAx.xyz	ACC,VF01,VF24x		LQ			VF04,3(VI01)
		MADDAy.xyz	ACC,VF02,VF24y		LQ			VF01,0(VI01)
		MUL.xyz		VF05,VF05,VF15		LQ			VF02,1(VI01)
		MADDz.xyz	VF24,VF03,VF24z		LQ			VF03,2(VI01)
		
		; v5
		ADDAx.xyz	ACC,VF04,VF00x		NOP
		MADDAx.xyz	ACC,VF01,VF25x		NOP
		ADDy.x		VF05,VF05,VF05y		NOP
		MADDAy.xyz	ACC,VF02,VF25y		NOP
		MADDz.xyz	VF25,VF03,VF25z		NOP
		
											   
		SUB.xyz		VF02,VF21,VF23		NOP
		ADDz.x		VF00,VF05,VF05z		NOP
		SUB.xyz		VF06,VF22,VF24		NOP
		SUB.xyz		VF05,VF21,VF24		NOP
		OPMULA.xyz	ACC,VF02,VF26		ILW.w		VI15,0(VI02)
		SUB.xyz		VF04,VF25,VF20		FSAND		VI01,2
		OPMSUB.xyz	VF02,VF26,VF02		IBEQ		VI01,VI00,CullPrism
		OPMULA.xyz	ACC,VF05,VF06		NOP
		OPMSUB.xyz	VF03,VF06,VF05		NOP
		OPMULA.xyz	ACC,VF04,VF27		NOP
		OPMSUB.xyz	VF04,VF27,VF04		NOP

		; compute and project v0,v1,v2 and their translates
		MULAx		ACC,VF16,VF22x		NOP
		MADDAy		ACC,VF17,VF22y		NOP
		MADDAz		ACC,VF18,VF22z		NOP
		MADDw		VF22,VF08,VF00w		NOP
		MADDw		VF25,VF07,VF00w		NOP
		
		
		MULA		ACC,VF10,VF22		NOP
		MADDw		VF12,VF11,VF22w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF12xyz,VF12w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		NOP								FCAND		VI01,0x00003F
		NOP								IBNE		VI01,VI00,CullPrism
		NOP								NOP
		
		
		MULAx		ACC,VF16,VF21x		NOP
		MADDAy		ACC,VF17,VF21y		NOP
		MADDAz		ACC,VF18,VF21z		ERCPR		P,VF22w
		MADDw		VF21,VF08,VF00w		DIV			Q,VF00w,VF25w
		MADDw		VF24,VF07,VF00w		NOP
		
		
		
		MULAx		ACC,VF16,VF20x		MFIR.w      VF22,VI00
		MADDAy		ACC,VF17,VF20y		NOP
		MADDAz		ACC,VF18,VF20z		NOP
		MADDw		VF20,VF08,VF00w		NOP
		MADDw		VF23,VF07,VF00w		NOP

		MULA		ACC,VF10,VF24		NOP
		MADDw		VF01,VF11,VF24w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF01xyz,VF01w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		NOP								FCAND		VI01,0x00003F
		NOP								IBNE		VI01,VI00,CullPrism
		NOP								NOP

		
		MULA		ACC,VF10,VF25		NOP
		MADDw		VF12,VF11,VF25w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF12xyz,VF12w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		NOP								FCAND		VI01,0x00003F
		NOP								IBNE		VI01,VI00,CullPrism
		NOP								NOP


		MULA		ACC,VF10,VF21		NOP
		MADDw		VF12,VF11,VF21w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF12xyz,VF12w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		NOP								FCAND		VI01,0x00003F
		NOP								IBNE		VI01,VI00,CullPrism
		NOP								NOP
		
		
		MULq.xyz	VF25,VF25,Q			DIV			Q,VF00w,VF21w
		MUL.xyz		VF02,VF02,VF15		NOP
		MUL.xyz		VF03,VF03,VF15		NOP
		MUL.xyz		VF04,VF04,VF15		NOP
		FTOI15.w	VF21,VF00			MFP.w		VF01,P
		ADDy.x		VF02,VF02,VF02y		ERCPR		P,VF24w
		ADDx.y		VF03,VF03,VF03x		SQ.xyz		VF25,6(VI02)
		
		MULA		ACC,VF10,VF23		NOP
		MADDw		VF12,VF11,VF23w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF12xyz,VF12w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		NOP								FCAND		VI01,0x00003F
		NOP								IBNE		VI01,VI00,CullPrism
		NOP								NOP
		
		
		
		; vf20-25 = verts.	 
		
		MULA		ACC,VF10,VF20		NOP
		MADDw		VF12,VF11,VF20w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF12xyz,VF12w		NOP
		NOP								NOP			; needs to be 3 nops
		NOP								NOP
		NOP								NOP
		NOP								FCAND		VI01,0x00003F
		NOP								IBNE		VI01,VI00,CullPrism
		NOP								NOP
		

		
		MULq.xyz	VF21,VF21,Q			DIV			Q,VF00w,VF20w
		
		
		
		
		
		
		
		MULw.xyz	VF22,VF22,VF01w		SQ.w		VF21,2(VI02)
		ADDx.z		VF04,VF04,VF04x		NOP
		MULx.w		VF05,VF00,VF25x		NOP
		ADDz.x		VF02,VF02,VF02z		SQ			VF21,4(VI02)
		ADDz.y		VF03,VF03,VF03z		SQ			VF22,16(VI02)
		ADDy.z		VF04,VF04,VF04y		SQ.xyz		VF22,2(VI02)
		MULq.xyz	VF20,VF20,Q			DIV			Q,VF00w,VF23w
		SUB.w		VF23,VF00,VF00		IADDIU      VI10, VI00, 0	;FSAND		VI10,2			; get adc results (4 cycles after mac)
		ADDy.x		VF05,VF00,VF25y		IADDIU      VI11, VI00, 0  ;FSAND		VI11,2
		MULx.w		VF22,VF00,VF22x		IADDIU      VI12, VI00, 0  ;FSAND		VI12,2
		ADDy.x		VF22,VF00,VF22y		MFP.w		VF01,P
		MULx.w		VF26,VF00,VF21x		NOP	;ISUBIU		VI10,VI10,1
		ADDy.x		VF26,VF00,VF21y		NOP ;ISUBIU		VI11,VI11,1

		MULq.xyz	VF23,VF23,Q			NOP ;ISUBIU		VI12,VI12,1
		
;		SUB.w		VF23,VF00,VF00		FSAND		VI10,2			; get adc results (4 cycles after mac)
;		ADDy.x		VF05,VF00,VF25y		FSAND		VI11,2
;		MULx.w		VF22,VF00,VF22x		FSAND		VI12,2
;		ADDy.x		VF22,VF00,VF22y		MFP.w		VF01,P
;		MULx.w		VF26,VF00,VF21x		ISUBIU		VI10,VI10,1
;		ADDy.x		VF26,VF00,VF21y		ISUBIU		VI11,VI11,1
;		
;		MULq.xyz	VF23,VF23,Q			ISUBIU		VI12,VI12,1
		MULw.xyz	VF24,VF24,VF01w		SQ.xyz		VF21,12(VI02)
		
		NOP								SQ			VF23,10(VI02)
		NOP								MFIR.w		VF23,VI12
		NOP								ISW.w		VI10,12(VI02)
		NOP								ISW.w		VI11,6(VI02)

		; backface testing and colours (8 tests)
		SUB.xw		VF01,VF22,VF26		MR32.xw		VF04,VF24
		SUB.xw		VF02,VF05,VF26		MFIR.w		VF20,VI10
		NOP								MFIR.w		VF24,VI11
		NOP								MR32.xw		VF06,VF23
		
		
		
		
		SUB.xw		VF03,VF05,VF04		MFIR.w		VF25,VI12
		MULAx.w		ACC,VF01,VF02x		SQ			VF23,18(VI02)
		MSUBx.w		VF00,VF02,VF01x		SQ			VF20,14(VI02)

		SUB.xw		VF01,VF06,VF04		MR32.xw		VF20,VF20
		MULAx.w		ACC,VF03,VF02x		SQ			VF24,8(VI02)
		MSUBx.w		VF00,VF02,VF03x		SQ			VF25,20(VI02)

		SUB.xw		VF02,VF06,VF26		FMAND		VI01,VI09
		MULAx.w		ACC,VF03,VF01x		ISUBIU		VI01,VI01,8
		MSUBx.w		VF00,VF01,VF03x		ISW.xyz		VI01,5(VI02)

		SUB.xw		VF03,VF20,VF26		FMAND		VI01,VI09
		MULAx.w		ACC,VF02,VF01x		ISUBIU		VI01,VI01,8
		MSUBx.w		VF00,VF01,VF02x		ISW.xyz		VI01,7(VI02)

		SUB.xw		VF01,VF20,VF22		FMAND		VI01,VI09
		MULAx.w		ACC,VF02,VF03x		ISUBIU		VI01,VI01,8
		MSUBx.w		VF00,VF03,VF02x		ISW.xyz		VI01,9(VI02)

		SUB.xw		VF02,VF06,VF22		FMAND		VI01,VI09
		MULAx.w		ACC,VF01,VF03x		ISUBIU		VI01,VI01,8
		MSUBx.w		VF00,VF03,VF01x		ISW.xyz		VI01,11(VI02)

		SUB.xw		VF03,VF06,VF05		FMAND		VI01,VI09
		MULAx.w		ACC,VF01,VF02x		ISUBIU		VI01,VI01,8
		MSUBx.w		VF00,VF02,VF01x		ISW.xyz		VI01,13(VI02)

		NOP								FMAND		VI01,VI09
		MULAx.w		ACC,VF03,VF02x		ISUBIU		VI01,VI01,8
		MSUBx.w		VF00,VF02,VF03x		ISW.xyz		VI01,15(VI02)

		NOP								FMAND		VI01,VI09
		NOP								ISUBIU		VI01,VI01,8
		NOP								ISW.xyz		VI01,17(VI02)

		NOP								FMAND		VI01,VI09
		NOP								ISUBIU		VI01,VI01,8
		NOP								ISW.xyz		VI01,19(VI02)

		NOP								IADDIU		VI08, VI00, 1
		NOP								XGKICK		VI02
		
CullPrism:

		NOP								IBGEZ		VI15,LoopSVS
		NOP								IADDIU		VI02,VI02,21

		NOP								IBEQ		VI08, VI00, DummyKick
		NOP								NOP
		
		NOP[E]							NOP
		NOP								NOP

DummyKick:
		NOP								ISUBIU		VI01, VI02, 1
		NOP								IADDIU		VI08, VI00, 0x4000
		NOP								IADDIU      VI08, VI08, 0x4000
		NOP								ISW.x		VI08, 0(VI01)
		NOP								XGKICK		VI01

		NOP[E]							NOP
		NOP								NOP






		.if 0
		; original unoptimised version
 
LoopSVS:
		; v0
		NOP								LQ			VF20,0(VI02)
		NOP								MTIR		VI01,VF20w
		NOP								LQ			VF04,3(VI01)
		NOP								LQ			VF01,0(VI01)
		NOP								LQ			VF02,1(VI01)
		NOP								LQ			VF03,2(VI01)

		ADDAx.xyz	ACC,VF04,VF00x		NOP
		MADDAx.xyz	ACC,VF01,VF20x		NOP
		MADDAy.xyz	ACC,VF02,VF20y		NOP
		MADDz.xyz	VF20,VF03,VF20z		NOP

		; v1
		NOP								LQ			VF21,1(VI02)
		NOP								MTIR		VI01,VF21w
		NOP								LQ			VF04,3(VI01)
		NOP								LQ			VF01,0(VI01)
		NOP								LQ			VF02,1(VI01)
		NOP								LQ			VF03,2(VI01)

		ADDAx.xyz	ACC,VF04,VF00x		NOP
		MADDAx.xyz	ACC,VF01,VF21x		NOP
		MADDAy.xyz	ACC,VF02,VF21y		NOP
		MADDz.xyz	VF21,VF03,VF21z		NOP

		; v2
		NOP								LQ			VF22,2(VI02)
		NOP								MTIR		VI01,VF22w
		NOP								LQ			VF04,3(VI01)
		NOP								LQ			VF01,0(VI01)
		NOP								LQ			VF02,1(VI01)
		NOP								LQ			VF03,2(VI01)

		ADDAx.xyz	ACC,VF04,VF00x		NOP
		MADDAx.xyz	ACC,VF01,VF22x		NOP
		MADDAy.xyz	ACC,VF02,VF22y		NOP
		MADDz.xyz	VF22,VF03,VF22z		NOP


		; generate n021
		SUB.xyz		VF05,VF20,VF22		NOP
		SUB.xyz		VF06,VF21,VF22		NOP
		OPMULA.xyz	ACC,VF05,VF06		NOP
		OPMSUB.xyz	VF01,VF06,VF05		NOP

		; dot with light vector
		MUL.xyz		VF01,VF01,VF15		NOP
		ADDy.x		VF01,VF01,VF01y		NOP
		ADDz.x		VF01,VF01,VF01z		MOVE		VF02,VF01
		
		; cull if dot product negative
		;NOP								NOP
		;NOP								NOP
		;NOP								NOP
		FTOI0		VF02,VF02			FSAND		VI01,2
		NOP								IBEQ		VI01,VI00,CullPrism
		NOP								NOP


		; v3
		NOP								LQ			VF23,3(VI02)
		NOP								MTIR		VI01,VF23w
		NOP								LQ			VF04,3(VI01)
		NOP								LQ			VF01,0(VI01)
		NOP								LQ			VF02,1(VI01)
		NOP								LQ			VF03,2(VI01)

		ADDAx.xyz	ACC,VF04,VF00x		NOP
		MADDAx.xyz	ACC,VF01,VF23x		NOP
		MADDAy.xyz	ACC,VF02,VF23y		NOP
		MADDz.xyz	VF23,VF03,VF23z		NOP

		; v4
		NOP								LQ			VF24,4(VI02)
		NOP								MTIR		VI01,VF24w
		NOP								LQ			VF04,3(VI01)
		NOP								LQ			VF01,0(VI01)
		NOP								LQ			VF02,1(VI01)
		NOP								LQ			VF03,2(VI01)

		ADDAx.xyz	ACC,VF04,VF00x		NOP
		MADDAx.xyz	ACC,VF01,VF24x		NOP
		MADDAy.xyz	ACC,VF02,VF24y		NOP
		MADDz.xyz	VF24,VF03,VF24z		NOP

		; v5
		NOP								LQ			VF25,5(VI02)
		NOP								MTIR		VI01,VF25w
		NOP								LQ			VF04,3(VI01)
		NOP								LQ			VF01,0(VI01)
		NOP								LQ			VF02,1(VI01)
		NOP								LQ			VF03,2(VI01)

		ADDAx.xyz	ACC,VF04,VF00x		NOP
		MADDAx.xyz	ACC,VF01,VF25x		NOP
		MADDAy.xyz	ACC,VF02,VF25y		NOP
		MADDz.xyz	VF25,VF03,VF25z		NOP
		
		
		

		; generate n013
		SUB.xyz		VF06,VF20,VF21		NOP
		SUB.xyz		VF05,VF23,VF21		NOP
		OPMULA.xyz	ACC,VF05,VF06		NOP
		OPMSUB.xyz	VF02,VF06,VF05		NOP

		; generate n241
		SUB.xyz		VF06,VF22,VF24		NOP
		SUB.xyz		VF05,VF21,VF24		NOP
		OPMULA.xyz	ACC,VF05,VF06		NOP
		OPMSUB.xyz	VF03,VF06,VF05		NOP

		; generate n052
		SUB.xyz		VF06,VF20,VF25		NOP
		SUB.xyz		VF05,VF22,VF25		NOP
		OPMULA.xyz	ACC,VF05,VF06		NOP
		OPMSUB.xyz	VF04,VF06,VF05		NOP


		; take dot products with light vec
		MUL.xyz		VF02,VF02,VF15		NOP
		MUL.xyz		VF03,VF03,VF15		NOP
		MUL.xyz		VF04,VF04,VF15		NOP
		
		NOP								NOP
		ADDy.x		VF02,VF02,VF02y		NOP
		ADDx.y		VF03,VF03,VF03x		NOP
		ADDx.z		VF04,VF04,VF04x		NOP

		NOP								NOP
		ADDz.x		VF02,VF02,VF02z		IADDIU		VI10,VI00,0x80
		ADDz.y		VF03,VF03,VF03z		IADDIU		VI11,VI00,0x40
		ADDy.z		VF04,VF04,VF04y		IADDIU		VI12,VI00,0x20
		
		; get ADC results
		NOP								NOP
		NOP								FMAND		VI10,VI10
		NOP								FMAND		VI11,VI11
		NOP								FMAND		VI12,VI12
		
		NOP								ISUBIU		VI10,VI10,1
		NOP								ISUBIU		VI11,VI11,1
		NOP								ISUBIU		VI12,VI12,1


		; compute and project v0,v1,v2 and their translates
		MULAx		ACC,VF16,VF20x		NOP
		MADDAy		ACC,VF17,VF20y		NOP
		MADDAz		ACC,VF18,VF20z		NOP
		MADDw		VF20,VF08,VF00w		NOP
		MADDw		VF23,VF07,VF00w		NOP
		NOP								DIV			Q,VF00w,VF20w
		MULq.xyz	VF20,VF20,Q			WAITQ
		NOP								DIV			Q,VF00w,VF23w
		MULq.xyz	VF23,VF23,Q			WAITQ

		MULAx		ACC,VF16,VF21x		NOP
		MADDAy		ACC,VF17,VF21y		NOP
		MADDAz		ACC,VF18,VF21z		NOP
		MADDw		VF21,VF08,VF00w		NOP
		MADDw		VF24,VF07,VF00w		NOP
		NOP								DIV			Q,VF00w,VF21w
		MULq.xyz	VF21,VF21,Q			WAITQ
		NOP								DIV			Q,VF00w,VF24w
		MULq.xyz	VF24,VF24,Q			WAITQ

		MULAx		ACC,VF16,VF22x		NOP
		MADDAy		ACC,VF17,VF22y		NOP
		MADDAz		ACC,VF18,VF22z		NOP
		MADDw		VF22,VF08,VF00w		NOP
		MADDw		VF25,VF07,VF00w		NOP
		NOP								DIV			Q,VF00w,VF22w
		MULq.xyz	VF22,VF22,Q			WAITQ
		NOP								DIV			Q,VF00w,VF25w
		MULq.xyz	VF25,VF25,Q			WAITQ


		;   0
		;  /|\
		; 2---1
		; | | |
		; | 3 |
		; |/ \|
		; 5---4
		;
		; adc's a(01), b(12), c(20)
		;
		; 2, 1, 5, 4, 3, 1, 0, 2, 3, 5
		; 1, 1, b, b, 0, a, a, 0, c, c

		; store positions and adc's
		FTOI15.w	VF22,VF00			NOP
		NOP								SQ			VF22,1(VI02)
		FTOI15.w	VF21,VF00			NOP
		NOP								SQ			VF21,3(VI02)
		
		FTOI15.w	VF25,VF00			NOP
;		NOP								MFIR.w		VF25,VI11
		NOP								SQ			VF25,5(VI02)
		FTOI15.w	VF24,VF00			NOP
;		NOP								MFIR.w		VF24,VI11
		NOP								SQ			VF24,7(VI02)
		
		;FTOI15.w	VF23,VF00			NOP
		NOP								MFIR.w		VF23,VI00
		NOP								SQ			VF23,9(VI02)
		
;		NOP								MFIR.w		VF21,VI10
		FTOI15.w	VF21,VF00			NOP
		NOP								SQ			VF21,11(VI02)
		FTOI15.w	VF20,VF00			NOP
;		NOP								MFIR.w		VF20,VI10
		NOP								SQ			VF20,13(VI02)
		
		;FTOI15.w	VF22,VF00			NOP
		NOP								MFIR.w		VF22,VI00
		NOP								SQ			VF22,15(VI02)
		
;		NOP								MFIR.w		VF23,VI12
		FTOI15.w	VF23,VF00			NOP
		NOP								SQ			VF23,17(VI02)
;		NOP								MFIR.w		VF25,VI12
		FTOI15.w	VF25,VF00			NOP
		NOP								SQ			VF25,19(VI02)


		
		; backface testing and colours (8 tests)
		NOP								IADDIU		VI10,VI00,0x10
		NOP								IADDIU		VI11,VI00,0x08
		
		; 215
		SUB.xyz		VF01,VF22,VF21		NOP
		SUB.xyz		VF02,VF25,VF21		NOP
		OPMULA.xyz	ACC,VF01,VF02		NOP
		OPMSUB.xyz	VF03,VF02,VF01		MOVE		VF01,VF02
		MULz.w		VF00,VF00,VF03z		MOVE		VF01,VF02
		;NOP								NOP
		;NOP								NOP
		;NOP								NOP
		FTOI0		VF02,VF01			FMAND		VI01,VI10
		NOP								ISUB		VI01,VI01,VI11
		NOP								ISW.xyzw	VI01,4(VI02)
		
		; 145
		SUB.xyz		VF01,VF21,VF24		NOP
		SUB.xyz		VF02,VF25,VF24		NOP
		OPMULA.xyz	ACC,VF01,VF02		NOP
		OPMSUB.xyz	VF03,VF02,VF01		MOVE		VF01,VF02
		MULz.w		VF00,VF00,VF03z		MOVE		VF01,VF02
		;NOP								NOP
		;NOP								NOP
		;NOP								NOP
		FTOI0		VF02,VF01			FMAND		VI01,VI10
		NOP								ISUB		VI01,VI01,VI11
		NOP								ISW.xyzw	VI01,6(VI02)
		
		; 543
		SUB.xyz		VF01,VF25,VF24		NOP
		SUB.xyz		VF02,VF23,VF24		NOP
		OPMULA.xyz	ACC,VF01,VF02		NOP
		OPMSUB.xyz	VF03,VF02,VF01		MOVE		VF01,VF02
		MULz.w		VF00,VF00,VF03z		MOVE		VF01,VF02
		;NOP								NOP
		;NOP								NOP
		;NOP								NOP
		FTOI0		VF02,VF01			FMAND		VI01,VI10
		NOP								ISUB		VI01,VI01,VI11
		NOP								ISW.xyzw	VI01,8(VI02)
		
		; 134
		SUB.xyz		VF01,VF21,VF23		NOP
		SUB.xyz		VF02,VF24,VF23		NOP
		OPMULA.xyz	ACC,VF01,VF02		NOP
		OPMSUB.xyz	VF03,VF02,VF01		MOVE		VF01,VF02
		MULz.w		VF00,VF00,VF03z		MOVE		VF01,VF02
		;NOP								NOP
		;NOP								NOP
		;NOP								NOP
		FTOI0		VF02,VF01			FMAND		VI01,VI10
		NOP								ISUB		VI01,VI01,VI11
		NOP								ISW.xyzw	VI01,10(VI02)
		
		; 103
		SUB.xyz		VF01,VF21,VF20		NOP
		SUB.xyz		VF02,VF23,VF20		NOP
		OPMULA.xyz	ACC,VF01,VF02		NOP
		OPMSUB.xyz	VF03,VF02,VF01		MOVE		VF01,VF02
		MULz.w		VF00,VF00,VF03z		MOVE		VF01,VF02
		;NOP								NOP
		;NOP								NOP
		;NOP								NOP
		FTOI0		VF02,VF01			FMAND		VI01,VI10
		NOP								ISUB		VI01,VI01,VI11
		NOP								ISW.xyzw	VI01,12(VI02)
		
		; 012
		SUB.xyz		VF01,VF20,VF21		NOP
		SUB.xyz		VF02,VF22,VF21		NOP
		OPMULA.xyz	ACC,VF01,VF02		NOP
		OPMSUB.xyz	VF03,VF02,VF01		MOVE		VF01,VF02
		MULz.w		VF00,VF00,VF03z		MOVE		VF01,VF02
		;NOP								NOP
		;NOP								NOP
		;NOP								NOP
		FTOI0		VF02,VF01			FMAND		VI01,VI10
		NOP								ISUB		VI01,VI01,VI11
		NOP								ISW.xyzw	VI01,14(VI02)
		
		; 023
		SUB.xyz		VF01,VF20,VF22		NOP
		SUB.xyz		VF02,VF23,VF22		NOP
		OPMULA.xyz	ACC,VF01,VF02		NOP
		OPMSUB.xyz	VF03,VF02,VF01		MOVE		VF01,VF02
		MULz.w		VF00,VF00,VF03z		MOVE		VF01,VF02
		;NOP								NOP
		;NOP								NOP
		;NOP								NOP
		FTOI0		VF02,VF01			FMAND		VI01,VI10
		NOP								ISUB		VI01,VI01,VI11
		NOP								ISW.xyzw	VI01,16(VI02)

		; 253
		SUB.xyz		VF01,VF22,VF25		NOP
		SUB.xyz		VF02,VF23,VF25		NOP
		OPMULA.xyz	ACC,VF01,VF02		NOP
		OPMSUB.xyz	VF03,VF02,VF01		MOVE		VF01,VF02
		MULz.w		VF00,VF00,VF03z		MOVE		VF01,VF02
		;NOP								NOP
		;NOP								NOP
		;NOP								NOP
		FTOI0		VF02,VF01			FMAND		VI01,VI10
		NOP								ISUB		VI01,VI01,VI11
		NOP								ISW.xyz		VI01,18(VI02)

NextPrism:		
		; loop control
		NOP								IADDIU		VI02,VI02,20
		NOP								NOP


		.endif



;-----------------------------------------------------------------------------------------------------------------------------
; clip triangle if not already culled and if part of it may be in the view frustum and another part in the outer frustum
; i.e. if clip if (ADC==0 && viewAND==0 && outerOR!=0)

; ADC bit should be set for any of the following:
;  - ADC set already
;  - outerOR!=0 (i.e. if culling renderer would cull it)
;  - viewAND!=0 (trivial rejection)

Clip:

		; reconstruct world-to-frustum transform
		MULA		ACC,VF10,VF12		NOP
		MADDw		VF04,VF11,VF12w		NOP
		MULA		ACC,VF10,VF13		NOP
		MADDw		VF05,VF11,VF13w		NOP
		MULA		ACC,VF10,VF14		NOP
		MADDw		VF06,VF11,VF14w		NOP
		MULA		ACC,VF10,VF15		NOP
		MADDw		VF07,VF11,VF15w		NOP
		
		.if 0
		;---------------------------------------------------------
		.if 0
		; optimised version
		
		; loop prologue
		NOP								IADD		VI03,VI02,VI04
		ADDAx		ACC,VF07,VF00x		LQ			VF01,-1(VI03)
		ITOF4.xyz	VF02,VF01			NOP
		MADDAx		ACC,VF04,VF02x		NOP
		MADDAy		ACC,VF05,VF02y		NOP
		MADDz		VF03,VF06,VF02z		NOP
		CLIPw.xyz	VF03xyz,VF03w		IADDIU		VI10,VI00,0
		
		; main clip-testing loop
LoopC:	ADDAw.xyz	ACC,VF00,VF03w		MTIR		VI07,VF01w
		MULAw.w		ACC,VF03,VF00w		IADD		VI03,VI03,VI04
		MADDAz.x	ACC,VF03,VF09z		LQ			VF01,-1(VI03)
		MADDAw.y	ACC,VF03,VF09w		FCOR		VI01,0xFEFBEF
		MSUBAx.z	ACC,VF09,VF03x		ISUB		VI07,VI07,VI01
		MSUBAy.w	ACC,VF09,VF03y		FCOR		VI01,0xFDF7DF
		ITOF4.xyz	VF02,VF01			ISUB		VI07,VI07,VI01
		MADDx		VF00,VF00,VF00x		FCAND		VI01,0x03FFFF
		NOP								ISUB		VI07,VI07,VI01
		ADDAx		ACC,VF07,VF00x		IADD		VI02,VI02,VI04
		MADDAx		ACC,VF04,VF02x		IAND		VI08,VI10,VI11
		MADDAy		ACC,VF05,VF02y		IADDIU		VI11,VI10,0
		MADDz		VF03,VF06,VF02z		FMOR		VI10,VI00
		NOP								IAND		VI01,VI08,VI10
		NOP								ISUB		VI07,VI07,VI01
		NOP								IBNE		VI02,VI05,LoopC
		CLIPw.xyz	VF03xyz,VF03w		ISW.w		VI07,-1(VI02)
		
		;---------------------------------------------------------
		.else
		; unoptimised version

		; initialise source pointer
		NOP								IADDIU		VI03,VI02,0
		
LoopC:
		; step source pointer
		NOP								IADD		VI03,VI03,VI04
		
		; load vertex
		NOP								LQ			VF01,-1(VI03)
		NOP								NOP
		NOP								NOP
		NOP								NOP
		
		; convert to float
		ITOF4.xyz	VF02,VF01			NOP
		
		; get ADC field
		NOP								MTIR		VI07,VF01w
		
		; transform to outer volume
		ADDAx		ACC,VF07,VF00x		NOP
		MADDAx		ACC,VF04,VF02x		NOP
		MADDAy		ACC,VF05,VF02y		NOP
		MADDz		VF03,VF06,VF02z		NOP
		
		; step destination pointer
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								NOP
		
		; generate clip codes
		CLIPw.xyz	VF03xyz,VF03w		NOP

		; generate pre-AND and advance outcode queue
		NOP								IAND		VI08,VI10,VI11
		NOP								IADDIU		VI11,VI10,0
		NOP								NOP
		
		; generate view-AND.z, combine with ADC
		NOP								FCOR		VI01,0xFEFBEF		; near
		NOP								ISUB		VI07,VI07,VI01
		NOP								FCOR		VI01,0xFDF7DF		; far
		NOP								ISUB		VI07,VI07,VI01
		
		; get outer-OR.xyz, combine with ADC
		NOP								FCAND		VI01,0x03FFFF
		NOP								ISUB		VI07,VI07,VI01

		; generate flags for view-AND.xy
		ADDAw.xyz	ACC,VF00,VF03w		NOP
		MULAw.w		ACC,VF03,VF00w		NOP
		MADDAz.x	ACC,VF03,VF09z		NOP
		MADDAw.y	ACC,VF03,VF09w		NOP
		MSUBAx.z	ACC,VF09,VF03x		NOP
		MSUBAy.w	ACC,VF09,VF03y		NOP
		MADDx		VF00,VF00,VF00x		NOP			; result is (w+Sx*x,w+Sy*y,w-Sx*x,w-Sy*y)
		
		; get flags for view-AND.xy
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								FMOR		VI10,VI00
		
		; generate view-AND.xy, combine with ADC
		NOP								IAND		VI01,VI08,VI10
		NOP								ISUB		VI07,VI07,VI01
		
		; store computed w component
		NOP								ISW.w		VI07,-1(VI02)
		
		; loop control
		NOP								IBNE		VI02,VI05,LoopC
		NOP								NOP
		
		.endif
		;---------------------------------------------------------
		.else
		; backface cull version
		
		.if 0
		; unoptimised version
		
		; initialise source pointer
		NOP								IADDIU		VI03,VI02,0
		
		; set ADC mask
		NOP								IADDIU		VI09,VI00,0x4000
		NOP								IADDIU		VI09,VI09,0x4000
		
LoopC:
		; step source pointer
		NOP								IADD		VI03,VI03,VI04
		
		; load vertex
		NOP								LQ			VF01,-1(VI03)
		NOP								NOP
		NOP								NOP
		NOP								NOP
		
		; convert to float
		ITOF4.xyz	VF02,VF01			NOP
		
		; get ADC field
		NOP								MTIR		VI14,VF01w
		NOP								IAND		VI07,VI14,VI09
		
		; transform to outer volume
		ADDAx		ACC,VF07,VF00x		NOP
		MADDAx		ACC,VF04,VF02x		NOP
		MADDAy		ACC,VF05,VF02y		NOP
		MADDz		VF03,VF06,VF02z		NOP
		
		; step destination pointer
		NOP								IADD		VI02,VI02,VI04
		NOP								NOP
		NOP								NOP
		
		; generate clip codes
		CLIPw.xyz	VF03xyz,VF03w		NOP

		; generate pre-AND and advance outcode queue
		NOP								IAND		VI08,VI10,VI11
		NOP								IADDIU		VI11,VI10,0
		NOP								NOP
		
		; generate view-AND.z, combine with ADC
		NOP								FCOR		VI01,0xFEFBEF		; near
		NOP								ISUB		VI07,VI07,VI01
		NOP								FCOR		VI01,0xFDF7DF		; far
		NOP								ISUB		VI07,VI07,VI01
		
		; get outer-OR.xyz, combine with ADC
		NOP								FCAND		VI01,0x03FFFF
		NOP								ISUB		VI07,VI07,VI01

		; generate flags for view-AND.xy
		ADDAw.xyz	ACC,VF00,VF03w		NOP
		MULAw.w		ACC,VF03,VF00w		NOP
		MADDAz.x	ACC,VF03,VF09z		NOP
		MADDAw.y	ACC,VF03,VF09w		NOP
		MSUBAx.z	ACC,VF09,VF03x		NOP
		MSUBAy.w	ACC,VF09,VF03y		NOP
		MADDx		VF00,VF00,VF00x		NOP			; result is (w+Sx*x,w+Sy*y,w-Sx*x,w-Sy*y)
		
		; get flags for view-AND.xy
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								FMOR		VI10,VI00
		
		; inc by 1
		NOP								IADDIU		VI07,VI07,1
		
		; generate view-AND.xy, combine with ADC
		NOP								IAND		VI01,VI08,VI10
		NOP								ISUB		VI07,VI07,VI01
		
		; will it be clipped?
		NOP								NOP
		NOP								IBNE		VI07,VI00,WontBeClipped
		NOP								NOP
		
		; set both adc and clip-bit (0x4000)
		NOP								ISUBIU		VI01,VI14,0x4000
		NOP								ISW.w		VI01,-1(VI02)
WontBeClipped:
		
		; loop control
		NOP								IBNE		VI02,VI05,LoopC
		NOP								NOP
		
		.else
		; optimised version
		;
		
		; loop prologue
		NOP								IADD		VI03,VI02,VI04
		ADDAx		ACC,VF07,VF00x		LQ			VF01,-1(VI03)
		ITOF4.xyz	VF02,VF01			IADDIU		VI09,VI00,0x4000
		MADDAx		ACC,VF04,VF02x		IADDIU		VI09,VI09,0x4000
		MADDAy		ACC,VF05,VF02y		NOP
		MADDz		VF03,VF06,VF02z		NOP
		
		; main clip-testing loop
LoopC:	NOP								IBEQ		VI02,VI05,AllClipped
		CLIPw.xyz	VF03xyz,VF03w		MTIR		VI14,VF01w
		ADDAw.xyz	ACC,VF00,VF03w		IAND		VI07,VI14,VI09
		MULAw.w		ACC,VF03,VF00w		IADD		VI03,VI03,VI04
		MADDAz.x	ACC,VF03,VF09z		LQ			VF01,-1(VI03)
		MADDAw.y	ACC,VF03,VF09w		FCOR		VI01,0xFEFBEF
		MSUBAx.z	ACC,VF09,VF03x		ISUB		VI07,VI07,VI01
		MSUBAy.w	ACC,VF09,VF03y		FCOR		VI01,0xFDF7DF
		ITOF4.xyz	VF02,VF01			ISUB		VI07,VI07,VI01
		MADDx		VF00,VF00,VF00x		FCAND		VI01,0x03FFFF
		NOP								ISUB		VI07,VI07,VI01
		ADDAx		ACC,VF07,VF00x		IADD		VI02,VI02,VI04
		MADDAx		ACC,VF04,VF02x		IAND		VI08,VI10,VI11
		MADDAy		ACC,VF05,VF02y		IADDIU		VI11,VI10,0
		MADDz		VF03,VF06,VF02z		FMOR		VI10,VI00
		NOP								IAND		VI01,VI08,VI10
		NOP								ISUB		VI07,VI07,VI01
		NOP								IADDIU		VI07,VI07,1
		NOP								NOP
		NOP								IBNE		VI07,VI00,LoopC
		NOP								ISUBIU		VI01,VI14,0x4000
		NOP								IBNE		VI02,VI05,LoopC
		NOP								ISW.w		VI01,-1(VI02)
		
		.endif
		.endif
		;---------------------------------------------------------
		
AllClipped:
		; reset pointer
		NOP								ISUB		VI02,VI02,VI06
		
		; set the EOP bit of the previous tag
		NOP								ILW.x		VI01,0(VI12)
		NOP								IADDIU		VI01,VI01,0x4000
		NOP								IADDIU		VI01,VI01,0x4000
		NOP								ISW.x		VI01,0(VI12)
		
		; set fan buffer base
		NOP								ISUBIU		VI12,VI13,288		; MAX_VU1_BUFFER - # regs to save
		
		; kick the context (which might be just a dummy giftag)
		; this stalls until the GS has finished with the memory we want to use as the fan buffer
		NOP								XGKICK		VI13
		
		; the fan buffer is now guaranteed not to be in use by the GS or DMAC
		
		; save some registers
		NOP								SQ			VF20,-12(VI12)
		NOP								SQ			VF21,-11(VI12)
		NOP								SQ			VF22,-10(VI12)
		NOP								SQ			VF23,-9(VI12)
		NOP								SQ			VF24,-8(VI12)
		NOP								SQ			VF25,-7(VI12)
		NOP								SQ			VF26,-6(VI12)
		NOP								SQ			VF27,-5(VI12)
		NOP								SQ			VF28,-4(VI12)
		NOP								SQ			VF29,-3(VI12)
		NOP								SQ			VF30,-2(VI12)
		NOP								SQ			VF31,-1(VI12)
		
		; set new giftag pointer (the preclipped tristrip)
		NOP								ISUBIU		VI13,VI02,1
		
		; output pointer = fan buffer base
		NOP								IADDIU		VI03,VI12,0
		

		; frustum planes:
		;
		; 0x0020 far
		; 0x0010 near
		; 0x0008 top
		; 0x0004 bottom
		; 0x0002 left
		; 0x0001 right
		;
		;   1---------0
		;   |\   3   /|
		;   | *-----* |
		;   | | (5) | |
		;   |1|  4  |0|
		;   | |     | |
		;   | *-----* |
		;   |/   2   \|
		;   *---------*


		; registers used:
		;
		; VF20: p[j0], m[j0]
		; VF21: p[j1], m[j1]
		; VF22: p[j2], m[j2]
		; VF23: p[j3], m[j3]
		; VF24: p[j4], m[j4]
		; VF25: p[j5], m[j5]
		;
		; VF26: x[i0], o[i0]
		; VF27: x[i1], o[i1]
		; VF28: x[i2], o[i2]
		;
		; VF30: e0, flags(e0), just the w-component
		; VF31: e1, flags(e1)
		;
		;
		; VI12: fan buffer base


		; skip the 1st 2 vertices
		NOP								IADD		VI02,VI02,VI04
		NOP								IADD		VI02,VI02,VI04
		NOP								ISUB		VI01,VI02,VI05
		NOP								IADD		VI08,VI02,VI04
		NOP								IBGEZ		VI01,PostClip
		NOP								ILW.w		VI01,-1(VI08)

		; loop over strip, clipping the triangles that are marked with clip bit 0x4000
ClipLoop:
		NOP								IBEQ		VI02,VI05,KickFans
		NOP								IADDIU		VI08,VI00,0x4000
		NOP								IADD		VI02,VI02,VI04
		NOP								IAND		VI08,VI01,VI08
		NOP								IADD		VI01,VI02,VI04
		NOP								IBEQ		VI08,VI00,ClipLoop
		NOP								ILW.w		VI01,-1(VI01)


		; go ahead with clipping...
		

		;-------------------------------
		; load vertex coords from memory
		;-------------------------------

		NOP								LQ			VF07,-1(VI02)
		NOP								ISUB		VI01,VI02,VI04
		NOP								LQ			VF06,-1(VI01)
		NOP								ISUB		VI01,VI01,VI04
		NOP								LQ			VF05,-1(VI01)
		
		
		;-----------------
		; convert to float
		;-----------------
		
		ITOF4.xyz	VF07,VF07			NOP
		ITOF4.xyz	VF06,VF06			NOP
		ITOF4.xyz	VF05,VF05			NOP

		;------------------------
		; apply frustum transform
		;------------------------

		; reconstruct world-to-frustum transform
		MULA		ACC,VF10,VF15		NOP
		MADDw		VF04,VF11,VF15w		NOP
		MULA		ACC,VF10,VF12		NOP
		MADDw		VF01,VF11,VF12w		NOP
		MULA		ACC,VF10,VF13		NOP
		MADDw		VF02,VF11,VF13w		NOP
		MULA		ACC,VF10,VF14		NOP
		MADDw		VF03,VF11,VF14w		NOP
		
		ADDAx		ACC,VF04,VF00x		NOP
		MADDAx		ACC,VF01,VF05x		NOP
		MADDAy		ACC,VF02,VF05y		NOP
		MADDz		VF26,VF03,VF05z		NOP

		ADDAx		ACC,VF04,VF00x		NOP
		MADDAx		ACC,VF01,VF06x		NOP
		MADDAy		ACC,VF02,VF06y		NOP
		MADDz		VF27,VF03,VF06z		NOP

		ADDAx		ACC,VF04,VF00x		NOP
		MADDAx		ACC,VF01,VF07x		NOP
		MADDAy		ACC,VF02,VF07y		NOP
		MADDz		VF28,VF03,VF07z		NOP


		; reorder the vertices
		;
		.if 0
		
		; zero the swap flags
		NOP								IADDIU		VI10,VI00,0
		
		; set up mask 0x0E
		NOP								IADDIU		VI07,VI00,0x0E
		
		; compare VF05 to VF06
		MAX.xyz		VF01,VF05,VF06		NOP
		SUB.xyz		VF00,VF05,VF06		NOP
		NOP								NOP
		NOP								NOP
		SUB.xyz		VF00,VF01,VF06		NOP
		
		; Z
		NOP								FMAND 		VI01,VI07
		
		; ~Z
		NOP								ISUB		VI01,VI07,VI01
		
		; ~Z & -(~Z)
		NOP								ISUB		VI08,VI00,VI01
		NOP								IAND		VI01,VI01,VI08
		
		; (~Z & -(~Z)) & S
		NOP								FMAND		VI01,VI01
		NOP								IBNE		VI01,VI00,NoSwap0
		NOP								NOP
		
		ADDx.xyz	VF05,VF06,VF00x		MOVE.xyz	VF06,VF05
		ADDx		VF26,VF27,VF00x		MOVE		VF27,VF26
		NOP								IADDIU		VI10,VI10,1
NoSwap0:
		
		; compare VF05 to VF07
		MAX.xyz		VF02,VF05,VF07		NOP
		SUB.xyz		VF00,VF05,VF07		NOP
		NOP								NOP
		NOP								NOP
		SUB.xyz		VF00,VF02,VF07		NOP
		
		; Z
		NOP								FMAND 		VI01,VI07
		
		; ~Z
		NOP								ISUB		VI01,VI07,VI01
		
		; ~Z & -(~Z)
		NOP								ISUB		VI08,VI00,VI01
		NOP								IAND		VI01,VI01,VI08
		
		; (~Z & -(~Z)) & S
		NOP								FMAND		VI01,VI01
		NOP								IBNE		VI01,VI00,NoSwap1
		NOP								NOP
		
		ADDx.xyz	VF05,VF07,VF00x		MOVE.xyz	VF07,VF05
		ADDx		VF26,VF28,VF00x		MOVE		VF28,VF26
		NOP								IADDIU		VI10,VI10,2
NoSwap1:
		
		; compare VF06 to VF07
		MAX.xyz		VF03,VF06,VF07		NOP
		SUB.xyz		VF00,VF06,VF07		NOP
		NOP								NOP
		NOP								NOP
		SUB.xyz		VF00,VF03,VF07		NOP
		
		; Z
		NOP								FMAND 		VI01,VI07
		
		; ~Z
		NOP								ISUB		VI01,VI07,VI01
		
		; ~Z & -(~Z)
		NOP								ISUB		VI08,VI00,VI01
		NOP								IAND		VI01,VI01,VI08
		
		; (~Z & -(~Z)) & S
		NOP								FMAND		VI01,VI01
		NOP								IBNE		VI01,VI00,NoSwap2
		NOP								NOP
		
		ADDx.xyz	VF06,VF07,VF00x		MOVE.xyz	VF07,VF06
		ADDx		VF27,VF28,VF00x		MOVE		VF28,VF27
		NOP								IADDIU		VI10,VI10,4
NoSwap2:

		; save the swap flags
		NOP								MFIR.x		VF30,VI10
		
		.else
		
		MAX.xyz		VF01,VF05,VF06		IADDIU		VI09,VI00,0x0E		; set up mask 0x0E
		SUB.xyz		VF00,VF05,VF06		NOP
		MAX.xyz		VF02,VF05,VF07		NOP
		MAX.xyz		VF03,VF06,VF07		NOP
		NOP								NOP
		SUB.xyz		VF00,VF01,VF06		FMAND 		VI01,VI09			; Z
		SUB.xyz		VF00,VF05,VF07		ISUB		VI07,VI09,VI01		; ~Z
		NOP								ISUB		VI01,VI00,VI07
		NOP								IAND		VI07,VI07,VI01		; ~Z & -(~Z)
		NOP								FMAND		VI07,VI07			; (~Z & -(~Z)) & S
		SUB.xyz		VF00,VF02,VF07		FMAND 		VI01,VI09			; Z
		SUB.xyz		VF00,VF06,VF07		ISUB		VI08,VI09,VI01		; ~Z
		NOP								ISUB		VI01,VI00,VI08
		NOP								IAND		VI08,VI08,VI01		; ~Z & -(~Z)
		NOP								FMAND		VI08,VI08			; (~Z & -(~Z)) & S
		SUB.xyz		VF00,VF03,VF07		FMAND 		VI01,VI09			; Z
		NOP								ISUB		VI09,VI09,VI01		; ~Z
		NOP								ISUB		VI01,VI00,VI09
		NOP								IAND		VI09,VI09,VI01		; ~Z & -(~Z)
		NOP								FMAND		VI09,VI09			; (~Z & -(~Z)) & S

		ADDx.xyz	VF01,VF05,VF00x		IBEQ		VI07,VI00,NoSwap0
		NOP								IADDIU		VI01,VI00,0
		
		ADDx.xyz	VF05,VF06,VF00x		ISUB		VI08,VI08,VI09		; swap VF05 with VF06
		ADDx		VF26,VF27,VF00x		MOVE		VF27,VF26
		NOP								IADD		VI09,VI08,VI09		; and swap flags VI08 with VI09
		ADDx.xyz	VF06,VF01,VF00x		ISUB		VI08,VI09,VI08
		NOP								IADDIU		VI01,VI01,1			; set swap flag 0
NoSwap0:
		NOP								IBEQ		VI08,VI00,NoSwap1
		NOP								NOP
		
		ADDx.xyz	VF05,VF07,VF00x		MOVE.xyz	VF07,VF05			; swap VF05 with VF07
		ADDx		VF26,VF28,VF00x		MOVE		VF28,VF26
		NOP								IADDIU		VI01,VI01,2			; set swap flag 1
NoSwap1:
		NOP								IBEQ		VI09,VI00,NoSwap2
		NOP								NOP
		
		ADDx.xyz	VF06,VF07,VF00x		MOVE.xyz	VF07,VF06			; swap VF06 with VF07
		ADDx		VF27,VF28,VF00x		MOVE		VF28,VF27
		NOP								IADDIU		VI01,VI01,4			; set swap flag 2
NoSwap2:
		NOP								MFIR.x		VF30,VI01			; save the swap flags
		
		.endif
		;------------------------------------------------------------------------
		
		
		;--------------------------
		; apply full view transform
		;--------------------------
		
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF05x		NOP
		MADDAy		ACC,VF13,VF05y		NOP
		MADDz		VF05,VF14,VF05z		NOP

		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF06x		NOP
		MADDAy		ACC,VF13,VF06y		NOP
		MADDz		VF06,VF14,VF06z		NOP

		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF07x		NOP
		MADDAy		ACC,VF13,VF07y		NOP
		MADDz		VF07,VF14,VF07z		NOP



		;---------------------------
		; classify triangle vertices
		;---------------------------

		ADDx		VF01,VF26,VF00x		BAL			VI14,ClassifyTriangleVertex
		SUB.xyz		VF02,VF00,VF26		MR32.z		VF26,VF26
		NOP								MFIR.w		VF26,VI01
		
		ADDx		VF01,VF27,VF00x		BAL			VI14,ClassifyTriangleVertex
		SUB.xyz     VF02,VF00,VF27		MR32.z		VF27,VF27
		NOP								MFIR.w		VF27,VI01
		
		ADDx		VF01,VF28,VF00x		BAL			VI14,ClassifyTriangleVertex
		SUB.xyz     VF02,VF00,VF28		MR32.z		VF28,VF28
		NOP								MFIR.w		VF28,VI01


		;--------------------------
		; classify frustum vertices
		;--------------------------
		
		; in frustum coords, the 8 vertices of the frustum are
		; FTL	( f, f, f,-f)  ->  (-1,-1,-1)	0x8000
		; FTR	(-f, f, f,-f)  ->  ( 1,-1,-1)	0x4000
		; FBL	( f,-f, f,-f)  ->  (-1, 1,-1)	0x2000
		; FBR	(-f,-f, f,-f)  ->  ( 1, 1,-1)	0x1000
		; NTL	( n, n,-n,-n)  ->  (-1,-1, 1)	0x0800
		; NTR	(-n, n,-n,-n)  ->  ( 1,-1, 1)	0x0400
		; NBL	( n,-n,-n,-n)  ->  (-1, 1, 1)	0x0200
		; NBR	(-n,-n,-n,-n)  ->  ( 1, 1, 1)	0x0100
		
		; for point classification we can work entirely in frustum coords,
		; using just the x,y and w of each point (which form a right-handed set)
		
		; calculate normal to plane of triangle	(in frustum xyw-space)
		SUB.xyz		VF01,VF27,VF26		NOP
		SUB.xyz		VF02,VF28,VF26		NOP
		
		;ADDw.xyz	VF04,VF00,VF00w		NOP								; set VF04 = (1,1,1,1)...
		;NOP								MOVE.w		VF04,VF00
		MAXw		VF04,VF00,VF00w		NOP
		
		NOP								NOP
		OPMULA.xyz	ACC,VF02,VF01		NOP
		OPMSUB.xyz	VF01,VF01,VF02		NOP
		
		SUBA		ACC,VF00,VF00		NOP
		
		; set VF03 = (nx*x0, ny*y0, nz*w0, ?)
		MUL.xyz		VF03,VF01,VF26		NOP
		
		; set VF01 = (nx+ny-nz, -nx+ny-nz, nx-ny-nz, -nx-ny-nz)
		MADDAx.xz	ACC,VF04,VF01x		NOP
		MSUBAx.yw	ACC,VF04,VF01x		NOP
		MADDAy.xy	ACC,VF04,VF01y		NOP
		MSUBAy.zw	ACC,VF04,VF01y		NOP
		MSUBz		VF01,VF04,VF01z		NOP
		
		; set ACC = (n.x0, n.x0, n.x0, n.x0)
		MULAx		ACC,VF04,VF03x		NOP
		MADDAy		ACC,VF04,VF03y		NOP
		MADDAz		ACC,VF04,VF03z		NOP
		
		; calculate (n.x0) - far * VF01
		MSUBy		VF00,VF01,VF09y		NOP
		
		; calculate (n.x0) - near * VF01
		MSUBx		VF00,VF01,VF09x		NOP
		
		; classify 8 vertices of frustum wrt plane of triangle
		ADDx.x		VF01,VF00,VF28x		IADDIU		VI08,VI00,0x0F10
		ADDx.y		VF01,VF00,VF26x		IADDIU		VI01,VI00,0x00F0	; Sxyzw MAC flags
		ADDx.z		VF01,VF00,VF27x		FMAND		VI10,VI01
		ADDz.x		VF03,VF00,VF28z		FMAND		VI01,VI01
		ADDz.y		VF03,VF00,VF26z		IADD		VI10,VI10,VI10
		ADDz.z		VF03,VF00,VF27z		IADD		VI10,VI10,VI10
        ADDy.x		VF02,VF00,VF28y		IADD		VI10,VI10,VI10
		ADDy.y		VF02,VF00,VF26y		IADD		VI10,VI10,VI10
		ADDy.z		VF02,VF00,VF27y		IOR			VI10,VI10,VI01


		; frustum outcodes now in VI10


		;------------------------------------
		; trivial rejection of triangle plane
		;------------------------------------
		
		SUB.w		VF01,VF00,VF00		IADDIU		VI01,VI00,0x0FF0
		SUB.xyz		VF20,VF03,VF01		IBEQ		VI10,VI00,ClipNext	; reject if whole frustum is on '0' side of trianlge plane
		ADD.xyz		VF21,VF03,VF01		IADDIU		VI06,VI00,0			; zero output vertex count		
		SUB.xyz		VF22,VF03,VF02		IBEQ		VI10,VI01,ClipNext	; reject if whole frustum is on '1' side of triangle plane
		ADD.xyz		VF23,VF03,VF02		IADD		VI10,VI10,VI10		; shift another 4 bits left
		ADDx.xyz	VF24,VF03,VF09x		IADD		VI10,VI10,VI10
		SUBAy.xyz	ACC,VF00,VF09y		IADD		VI10,VI10,VI10
		MSUBw.xyz	VF25,VF03,VF00w		IADD		VI10,VI10,VI10
		
		;-------------------------------------------
		; initialise frustum in standard orientation
		;-------------------------------------------
		
		; VecSet(p[0],  w0-x0,  w1-x1,  w2-x2, 0),	m[0] = 0x5501;	; right
		; VecSet(p[1],  w0+x0,  w1+x1,  w2+x2, 0),	m[1] = 0xAA02;	; left
		; VecSet(p[2],  w0-y0,  w1-y1,  w2-y2, 0),	m[2] = 0x3304;	; bottom
		; VecSet(p[3],  w0+y0,  w1+y1,  w2+y2, 0),	m[3] = 0xCC08;	; top
		; VecSet(p[4],  w0+n ,  w1+n ,  w2+n , 0),	m[4] = 0x0F10;	; near
		; VecSet(p[5], -w0-f , -w1-f , -w2-f , 0),	m[5] = 0xF020;	; far

		
		NOP								LOI			0x3F805501
		ADDi.w		VF20,VF01,I			LOI			0x3F80AA02
		ADDi.w		VF21,VF01,I			LOI			0x3F803304
		ADDi.w		VF22,VF01,I			LOI			0x3F80CC08
		ADDi.w		VF23,VF01,I			LOI			0x3F800F10
		ADDi.w		VF24,VF01,I			LOI			0x3F80F020
		ADDi.w		VF25,VF01,I			ISUBIU		VI11,VI00,0x0100 	; 0xFF00
		


		;------------------------------------------
		; put a straddling edge in the primary face
		;------------------------------------------

		; while (((c & m[j4])==0) || ((c & m[j4])==(0xFF00 & m[j4])))
		; {
		;	jt=j2, j2=j5, j5=j3, j3=j4, j4=jt;
		; }
		
WhileA:	NOP								IAND		VI07,VI10,VI08
		NOP								IAND		VI01,VI11,VI08
		NOP								IBEQ		VI07,VI00,RotateA
		ADDx		VF01,VF22,VF00x		NOP
		NOP								IBNE		VI01,VI07,EndWhileA
		NOP								NOP
RotateA:ADDx		VF22,VF25,VF00x		MTIR		VI08,VF22w
		ADDx		VF25,VF23,VF00x		B			WhileA
		ADDx		VF23,VF24,VF00x		MOVE		VF24,VF01
EndWhileA:
		

		;-------------------------------------------
		; rotate straddling edge into secondary face
		;-------------------------------------------

		; while ((c & m[j3] & m[j4]) != (m[j1] & m[j3] & m[j4]))
		; {
		;	jt=j2, j2=j0, j0=j3, j3=j1, j1=jt;
		; }

		NOP								MTIR		VI07,VF23w			; m[j3]
		NOP								IAND		VI07,VI07,VI08		; m[j3] & m[j4]
		NOP								MTIR		VI11,VF21w			; m[j1]

WhileB:	NOP								IAND		VI01,VI11,VI07		; m[j1] & m[j3] & m[j4]
		NOP								IAND		VI07,VI07,VI10		; c & m[j3] & m[j4]
		ADDx		VF01,VF22,VF00x		NOP
		NOP								IBEQ		VI07,VI01,EndWhileB
		NOP								IAND		VI07,VI11,VI08
		ADDx		VF22,VF20,VF00x		MTIR		VI11,VF22w
		ADDx		VF20,VF23,VF00x		B			WhileB
		ADDx		VF23,VF21,VF00x		MOVE		VF21,VF01
EndWhileB:



		;------------------------------------------------
		; roll the frustum classifier bits into the masks
		;------------------------------------------------

		NOP								IADDIU		VI10,VI10,0xFF
		NOP								LOI			0x4B400000			; 2^23+2^22
		
		NOP								MTIR		VI01,VF20w
		NOP								IAND		VI01,VI01,VI10
		NOP								MFIR.w		VF20,VI01
		NOP								MTIR		VI01,VF21w
		NOP								IAND		VI01,VI01,VI10
		NOP								MFIR.w		VF21,VI01
		ITOF0.w		VF20,VF20			MTIR		VI01,VF22w
		NOP								IAND		VI01,VI01,VI10
		NOP								MFIR.w		VF22,VI01
		ITOF0.w		VF21,VF21			MTIR		VI01,VF23w
		ADDi.w		VF20,VF20,I			IAND		VI01,VI01,VI10
		NOP								MFIR.w		VF23,VI01
		ITOF0.w		VF22,VF22			MTIR		VI01,VF24w
		ADDi.w		VF21,VF21,I			IAND		VI01,VI01,VI10
		NOP								MFIR.w		VF24,VI01
		ITOF0.w		VF23,VF23			MTIR		VI01,VF25w
		ADDi.w		VF22,VF22,I			IAND		VI01,VI01,VI10
		NOP								MFIR.w		VF25,VI01
		ITOF0.w		VF24,VF24			NOP
		ADDi.w		VF23,VF23,I			NOP
		ITOF0.w		VF25,VF25			NOP
		ADDi.w		VF24,VF24,I			NOP
		ADDi.w		VF25,VF25,I			NOP
		
		

		;----------------------------------------------
		; classify initial straddling edge wrt triangle
		;----------------------------------------------
		
		OPMULA.xyz	ACC,VF23,VF24		NOP
		OPMSUB.xyz	VF31,VF24,VF23		NOP
		NOP								NOP
		NOP								NOP
		NOP								IADDIU		VI01,VI00,0x00E0	; Sxyz MAC flags
		NOP								FMAND		VI01,VI01
		NOP								MFIR.w		VF31,VI01

		; advance one face to fill the classification queue
		NOP								BAL			VI14,NextFrustumFace
		NOP								NOP


; ; GENERAL ALGORITHM
;
; ; mark our place in the triangle
; vT0 = vT;
;
; ; are we starting at a triangle vertex inside the frustum?
; if (vT inside F)
;	goto vTinsideF;
;
; ; find the first edge of the frustum poly with respect to which which the triangle vertex is out
; while (vT outside edge(vF,vF->next))
;	vF = vF->next;
; while (vT inside edge(vF,vF->next))
;	vF = vF->next;
;
; ; mark our place in the frustum poly
; vF0 = vF;
;
; ; find an intersection, or determine there isn't one and quit
; while (1)
; {
;	while (vT->next outside edge(vF,vF->next))
;		vT = vT->next;
;
;	do
;	{
;		if ( vF        inside edge(vT,vT->next) &&
;		     vF->next outside edge(vT,vT->next) &&
;		     vT       outside edge(vF,vF->next))
;		    goto Intersection;
;		vF = vF->next;
;		if (vF == vF0)
;			goto Reject;
;	}
;	while (vT->next inside edge(vF,vF->next));
; }
;
; Intersection:
;
; while (1)
; {
;
;	Output(Intersection(edge(vF,vF->next), edge(vT,vT->next));
;
;	while (vT->next inside F)
;	{
;		vT = vT->next;
;		if (vT==vT0)
;			goto Finish;
; vTinsideF:
;		Output(vT);
;	}
;
;	do {
;		vF = vF->next;
;	} while ((vF inside edge(vT,vT->next)) || (vF->next outside edge(vT,vT->next)));
;
;	Output(Intersection(edge(vT,vT->next), edge(vF,vF->next));
;
;	while (vF->next inside T)
;	{
;		vF = vF->next;
;		Output(vF);
;	}
;
;	do {
;		vT = vT->next;
;		if (vT==vT0)
;			goto Finish;
;	} while ((vT inside edge(vF,vF->next)) || (vT->next outside edge(vF,vF->next)));
;
; }
;
; Finish:

		; are we starting at an in-vertex?
		NOP								MTIR		VI07,VF26w
		NOP								IADDIU		VI10,VI00,0x80		; mark our place in the triangle
		NOP								IBEQ		VI07,VI00,StartIn
		NOP								NOP

		; find the first edge of the frustum poly with respect to which which the triangle vertex is out
FindEdge1:
		NOP								MTIR		VI01,VF23w
		NOP								IAND		VI01,VI01,VI07
		NOP								NOP
		NOP								IBNE		VI01,VI00,NextFrustumFace
		NOP								IADDIU		VI14,VI00,FindEdge1

FindEdge2:
		NOP								MTIR		VI01,VF23w
		NOP								IAND		VI01,VI01,VI07
		NOP								NOP
		NOP								IBEQ		VI01,VI00,NextFrustumFace
		NOP								IADDIU		VI14,VI00,FindEdge2

		; mark our place in the frustum poly
		NOP								MTIR		VI11,VF23w
		NOP								MTIR		VI07,VF23w

		; find an intersection, or determine there isn't one and quit
FindIntersection:
		NOP								MTIR		VI08,VF27w
		NOP								IAND		VI01,VI08,VI07
		NOP								NOP
		NOP								IBNE		VI01,VI00,NextTriangleVertex
		NOP								IADDIU		VI14,VI00,FindIntersection

While3:
		NOP								MTIR		VI01,VF26w
		NOP								IAND		VI01,VI01,VI07
		NOP								MTIR		VI07,VF31w
		NOP								IBEQ		VI01,VI00,NoIntersectionYet
		NOP								MTIR		VI01,VF30w
		
		NOP								IAND		VI01,VI01,VI10
		NOP								IAND		VI07,VI07,VI10
		NOP								ISUB		VI01,VI01,VI07
		NOP								NOP
		NOP								IBLTZ		VI01,While4
		NOP								NOP
		
NoIntersectionYet:
		
		NOP								BAL			VI14,NextFrustumFace
		NOP								NOP
		NOP								MTIR		VI07,VF23w
		NOP								IAND		VI01,VI08,VI07
		NOP								IBEQ		VI07,VI11,ClipNext	; reject
		NOP								NOP
		
		NOP								IBNE		VI01,VI00,FindIntersection
		NOP								NOP

		NOP								B			While3
		NOP								NOP











While4:	; output entering intersection
		NOP								BAL			VI14,OutputType2
		NOP								NOP

		; output any vertices of triangle inside frustum
OutputTriVerts:
		NOP								MTIR		VI01,VF27w
		NOP								NOP
		NOP								IBNE		VI01,VI00,EndOutputTriVerts
		NOP								NOP

		NOP								BAL			VI14,NextTriangleVertex
		NOP								NOP
		
StartIn:NOP								B			OutputType1
		NOP								IADDIU		VI14,VI00,OutputTriVerts
EndOutputTriVerts:

		; traverse frustum poly to find leaving intersection
		
		NOP								MTIR		VI01,VF31w
		
FindLeavingIntersection:
		NOP								BAL			VI14,NextFrustumFace
		NOP								IAND		VI07,VI01,VI10
		NOP								IAND		VI01,VI01,VI10
		NOP								ISUB		VI09,VI07,VI01
		NOP								NOP
		NOP								IBLEZ		VI09,FindLeavingIntersection
		NOP								NOP
		
		; output leaving intersection
		NOP								BAL			VI14,OutputType2
		NOP								NOP
		
		NOP								MTIR		VI01,VF31w
		NOP								NOP
		
		; output any vertices of frustum poly inside triangle
OutputFrustumVerts:

		NOP								IBNE		VI01,VI00,EndOutputFrustumVerts
		NOP								NOP
		
		NOP								BAL			VI14,OutputType3
		NOP								NOP
		
		NOP								B			NextFrustumFace
		NOP								IADDIU		VI14,VI00,OutputFrustumVerts
EndOutputFrustumVerts:

		; traverse triangle to find entering intersection
		NOP								MTIR		VI09,VF23w
		NOP								MTIR		VI07,VF27w
		NOP								IAND		VI07,VI07,VI09

FindEnteringIntersection:
		NOP								IADDIU		VI01,VI07,0
		NOP								MTIR		VI07,VF28w
		NOP								IAND		VI07,VI07,VI09
		NOP								BAL			VI14,NextTriangleVertex
		NOP								ISUB		VI01,VI01,VI07
		
		NOP								IBLEZ		VI01,FindEnteringIntersection
		NOP								NOP
		
		NOP								B			While4
		NOP								NOP



		;------------------------------------
		; classify triangle vertex subroutine
		;------------------------------------
		
		
		; classify a triangle vertex with respect to the 6 planes of the frustum
		; this info could possibly be retained from pass 1
		
ClassifyTriangleVertex:

		NOP								IADDIU		VI01,VI00,0x0010	; Sw MAC flag
		CLIPw.xyz	VF01,VF01w			FMAND		VI01,VI01
		CLIPw.xyz	VF02,VF01w			NOP
		NOP								IADDIU		VI07,VI00,0x003F
		NOP								IBEQ		VI01,VI00,wPos
		NOP								FCGET		VI01
		NOP								FCGET		VI01
		NOP								ISUB		VI01,VI07,VI01
wPos:	NOP								JR			VI14
		NOP								IAND		VI01,VI01,VI07
		

		;--------------------------------
		; next triangle vertex subroutine
		;--------------------------------

;NextTriangleVertex()
;{
;	it=i0, i0=i1, i1=i2, i2=it;
;}

NextTriangleVertex:

		NOP								ISUBIU		VI10,VI10,0x30
		NOP								MOVE		VF01,VF26
		NOP								IBLTZ		VI10,ProcessFan
		NOP								MOVE		VF26,VF27
		NOP								MOVE		VF27,VF28
		NOP								JR			VI14
		NOP								MOVE		VF28,VF01
		

		;-----------------------------
		; next frustum face subroutine
		;-----------------------------
		

;   1---------0
;   |\   3   /|
;   | *-----* |
;   | | (5) | |
;   |1|  4  |0|
;   | |     | |
;   | *-----* |
;   |/   2   \|
;   *---------*
;
;NextFrustumFace()
;{
;	; advance edge classification queue
;	e0 = e1;
;
;	; rotate frustum in 1 of 3 ways
;	jt=j3, j3=j4;
;	if (m[j4] & m[j0])
;		j4=j0, j0=jt, jt=j1, j1=j2;
;	else if (m[j4] & m[j2])
;		j4=j2;
;	else
;		j4=j1, j1=jt, jt=j0, j0=j2;
;	j2=j5, j5=jt;
;
;	; classify new straddling edge wrt triangle
;	CrossProduct(e1, p[j2], p[j4]);
;}


NextFrustumFace:

		ADDx		VF01,VF23,VF00x		MTIR		VI09,VF24w
		ADDx		VF23,VF24,VF00x		MTIR		VI01,VF20w
		NOP								IAND		VI01,VI09,VI01
		NOP								MOVE.w		VF30,VF31
		NOP								IBEQ		VI01,VI00,NFF1
		NOP								MTIR		VI01,VF22w
		
		OPMULA.xyz	ACC,VF23,VF20		MOVE		VF24,VF20
		OPMSUB.xyz	VF31,VF20,VF23		MOVE		VF20,VF01
		ADDx		VF01,VF21,VF00x		B			NFF3
		ADDx		VF21,VF22,VF00x		IADDIU		VI01,VI00,0x00E0	; Sxyz MAC flags

NFF1:	NOP								IAND		VI01,VI09,VI01
		NOP								NOP
		NOP								IBNE		VI01,VI00,NFF2
		NOP								IADDIU		VI01,VI00,0x00E0	; Sxyz MAC flags

		OPMULA.xyz	ACC,VF23,VF21		MOVE		VF24,VF21
		OPMSUB.xyz	VF31,VF21,VF23		MOVE		VF21,VF01
		ADDx		VF01,VF20,VF00x		B			NFF3
		ADDx		VF20,VF22,VF00x		NOP

NFF2:	OPMULA.xyz	ACC,VF23,VF22		MOVE		VF24,VF22
		OPMSUB.xyz	VF31,VF22,VF23		NOP

NFF3:	ADDx		VF00,VF31,VF00x		FMAND		VI01,VI01
		ADDx		VF22,VF25,VF00x		JR			VI14
		ADDx		VF25,VF01,VF00x		MFIR.w		VF31,VI01


		;--------------------------
		; vertex output subroutines
		;--------------------------


OutputWeightsFromAcc:

		MADDx		VF01,VF00,VF00x		NOP

OutputWeights:

		NOP								IADDIU		VI06,VI06,1
		NOP								IADDIU		VI03,VI03,3
		NOP								JR			VI14
		NOP								SQ			VF01,-2(VI03)
		



OutputType1:

		NOP								ISUBIU		VI01,VI10,0x50
		NOP								NOP
		ADDw.y		VF01,VF00,VF00w		IBEQ		VI01,VI00,OutputWeights
		ADDx.xzw	VF01,VF00,VF00x		NOP								; case i0=1 : VF01 = (0,1,0,1)
		ADDw.z		VF01,VF00,VF00w		IBLTZ		VI01,OutputWeights
		ADDx.xyw	VF01,VF00,VF00x		NOP								; case i0=2 : VF01 = (0,0,1,1)
		ADDw.x		VF01,VF00,VF00w		B			OutputWeights
		ADDx.yzw	VF01,VF00,VF00x		NOP								; case i0=0 : VF01 = (1,0,0,1)




;OutputIntersectionType2(Vec x[3])
;{
;	; x = ((a0.p)x1-(a1.p)x0)/(x0-x1).p
;	Vec Result;
;	VecWeightedMean2(Result, -p[j4][i1], x[i0], p[j4][i0], x[i1]);
;	Output(Result,&pClippedPoly);
;}

OutputType2:

		ADDw.xyz	VF01,VF00,VF00w		ISUBIU		VI01,VI10,0x50		; VF01 = (1,1,1,?)
		NOP								MOVE.w		VF01,VF00			; VF01 = (1,1,1,1)
		SUBA		ACC,VF00,VF00		IBEQ		VI01,VI00,Type2B	; ACC  = (0,0,0,0)
		NOP								NOP
		NOP								IBLTZ		VI01,Type2C
		NOP								NOP

Type2A:	MADDAy.yw	ACC,VF01,VF23y		B			OutputWeightsFromAcc; ACC  = (0,VF23y,0,VF23y)
		MSUBAz.xw	ACC,VF01,VF23z		NOP								; ACC  = (-VF23z,VF23y,0,VF23y-VF23z)
		
Type2B:	MADDAz.zw	ACC,VF01,VF23z		B			OutputWeightsFromAcc; ACC  = (0,0,VF23z,VF23z)
		MSUBAx.yw	ACC,VF01,VF23x		NOP								; ACC  = (0,-VF23x,VF23z,VF23z-VF23x)
		
Type2C:	MADDAx.xw	ACC,VF01,VF23x		B			OutputWeightsFromAcc; ACC  = (VF23x,0,0,VF23x)
		MSUBAy.zw	ACC,VF01,VF23y		NOP								; ACC  = (VF23x,0,-VF23y,VF23x-VF23y)



OutputType3:
		
		MULAx.w		ACC,VF00,VF31x		MR32.xy		VF01,VF31
		MADDAy.w	ACC,VF00,VF31y		NOP
		NOP								NOP

		MADDz.w		VF01,VF00,VF31z		B			OutputWeights
		ADDx.z		VF01,VF00,VF31x		NOP								; VF01 = (VF31y, VF31z, VF31x, VF31x+VF31y+VF31z)
		




ProcessFan:

		NOP								IBEQ		VI06,VI00,ClipNext	; if nothing was output, just forget it
		NOP								ILW.y		VI08,0(VI13)

		; get colours and texcoords from the source triangle
		NOP								ISUB		VI01,VI02,VI04
		NOP								ISUB		VI10,VI01,VI04
		NOP								ISUB		VI11,VI10,VI04
		NOP								LQ			VF23,-2(VI10)
		NOP								LQ			VF24,-2(VI01)
		NOP								LQ			VF25,-2(VI02)
		NOP								LQ.xyz		VF20,0(VI11)
		NOP								LQ.xyz		VF21,0(VI10)
		NOP								LQ.xyz		VF22,0(VI01)
		
		; convert rgba to float and test for reflection mapping
		ITOF0		VF23,VF23			ISUBIU		VI10,VI08,Refl
		ITOF0		VF24,VF24			IADDIU		VI01,VI00,0x0FF
		ITOF0		VF25,VF25			IAND		VI01,VI10,VI01
		NOP								XITOP		VI11
		NOP								IBEQ		VI01,VI00,NoConvST
		NOP								IADDIU		VI01,VI00,SHDW
		
		; convert st to float and supplement with 1, and check whether it's a shadow projection
		ITOF12.xy	VF20,VF20			IAND		VI01,VI01,VI11
		ITOF12.xy	VF21,VF21			MR32.z		VF20,VF00
		ITOF12.xy	VF22,VF22			IBNE		VI01,VI00,ClampedInUV
		ADDw.z		VF21,VF00,VF00w		MR32.z		VF22,VF00
		

		; reduce texture coordinates...
		
		; get u-clamp flag and start floating point calculation on both coords
		ADDw.xy		VF01,VF00,VF00w		LOI			-0.33333333
		MULAi.xy	ACC,VF20,I			IADDIU		VI01,VI00,0x1000
		MADDAi.xy	ACC,VF21,I			IAND		VI01,VI08,VI01
		MADDAi.xy	ACC,VF22,I			LOI			0xCB400000	; -2^23-2^22
		MADDAi.xy	ACC,VF01,I			IBNE		VI01,VI00,ClampedInU
		MSUBAi.xy	ACC,VF01,I          IADDIU		VI01,VI00,0x2000
		
		; reduce texture coords in s
		MADDw.x		VF20,VF20,VF00w		NOP
		MADDw.x		VF21,VF21,VF00w		NOP
		MADDw.x		VF22,VF22,VF00w		NOP
		
ClampedInU:

		; get v-clamp flag
		NOP								IAND		VI01,VI08,VI01
		NOP								NOP
		NOP								IBNE		VI01,VI00,ClampedInV
		NOP								NOP
		
		; reduce texture coords in t
		MADDw.y		VF20,VF20,VF00w		NOP
		MADDw.y		VF21,VF21,VF00w		NOP
		MADDw.y		VF22,VF22,VF00w		NOP
		
ClampedInV:
ClampedInUV:
NoConvST:


		; get giftag and replace NLOOP, NREG and PRIM fields
		NOP								LQ.y		VF01,0(VI13)											; VF01y = NREG:FLG:PRIM:PRE:000
		NOP								LOI			0x53400000												; 2^39+2^38
		ADDi.y		VF03,VF00,I			LOI			196616													; 3*2^16+8
		ADDi.y		VF04,VF00,I			LOI			0x3F800412			; XYZ2:RGBA:STQ
		ITOF12.y	VF01,VF01			IADDIU		VI11,VI08,0			; VF01y = float(NREG:FLG:PRIM:PRE)	; save GIFTAGy
		SUBA.y		ACC,VF03,VF01		IADDIU		VI08,VI03,0			; ACCy = 2^39+2^38-float(NREG)		; end pointer
		MSUBAw.y	ACC,VF03,VF00w		IADD		VI01,VI06,VI06		; ACCy = -float(NREG)				; 2 * vertex count
		MADDAw.y	ACC,VF01,VF00w		IADD		VI01,VI01,VI06		; ACCy = float(FLG:PRIM:PRE)		; 3 * vertex count
		MADDw.y		VF01,VF04,VF00w		ISUB		VI03,VI03,VI01		; VF01y = float(3:FLG:fanPRIM:PRE)	; address of giftag
		NOP								MFIR.x		VF01,VI06			; EOP:NLOOP
		ADDi.z		VF01,VF00,I			NOP
		FTOI12.y	VF01,VF01			NOP								; VF01y = 3:FLG:fanPRIM:PRE:000
		
		NOP								SQ.xyz		VF01,0(VI03)
		
		
		;------------------------------------------------------------------------
		; reorder the colours and texcoords
		.if 1
		
		; retrieve the swap flags
		NOP								MTIR		VI10,VF30x
		
		NOP								IADDIU		VI01,VI00,1
		NOP								IAND		VI01,VI01,VI10
		NOP								IADDIU		VI07,VI00,2
		NOP								IBEQ		VI01,VI00,NoSwap3
		NOP								IAND		VI07,VI07,VI10
		
		ADDx		VF23,VF24,VF00x		MOVE		VF24,VF23
		ADDx		VF20,VF21,VF00x		MOVE		VF21,VF20
NoSwap3:

		NOP								IADDIU		VI01,VI00,4
		NOP								IBEQ		VI07,VI00,NoSwap4
		NOP								IAND		VI01,VI01,VI10
		
		ADDx		VF23,VF25,VF00x		MOVE		VF25,VF23
		ADDx		VF20,VF22,VF00x		MOVE		VF22,VF20
NoSwap4:
		
		NOP								NOP
		NOP								IBEQ		VI01,VI00,NoSwap5
		NOP								NOP
		
		ADDx		VF24,VF25,VF00x		MOVE		VF25,VF24
		ADDx		VF21,VF22,VF00x		MOVE		VF22,VF21
NoSwap5:
		
		.endif
		;------------------------------------------------------------------------
		
		; prepare reflection-map test
		NOP								ISUBIU		VI11,VI10,Refl
		NOP								IADDIU		VI01,VI00,0x0FF
		NOP								IAND		VI11,VI11,VI01
		
		; fog setup
		NOP								DIV			Q,VF00w,VF10w
		ADDq.x		VF08,VF00,Q			WAITQ
		
		NOP								LOI			0x45000FFF
		ADDi.y		VF08,VF00,I			NOP								; VF08y = 2^11 + 1 - 2^-12
		SUBq.y		VF08,VF08,Q			NOP								; VF08y = 2^11 + 1-f0 - 2^-12
		
		NOP								LQ.w		VF01,-8(VI12)
		NOP								MR32.z		VF08,VF01			; VF08 = FogNear
		
FanLoop:
		NOP								LQ			VF04,1(VI03)
		
		MULz.w		VF08,VF04,VF08z		NOP
		
		MULAx		ACC,VF05,VF04x		ERCPR		P,VF04w
		MADDAy		ACC,VF06,VF04y		NOP
		MADDz		VF03,VF07,VF04z		IADDIU		VI01,VI00,0x0010	; Sw FMAC flag
		
		MULAx		ACC,VF23,VF04x		FMAND		VI01,VI01
		MADDAy		ACC,VF24,VF04y		NOP
		MADDz		VF02,VF25,VF04z		DIV			Q,VF08x,VF03w
		
		MULAx.xyz	ACC,VF20,VF04x		NOP
		MADDAy.xyz	ACC,VF21,VF04y		NOP
		MADDz.xyz	VF01,VF22,VF04z		LOI			1.0039				; fudgefactor to compensate for RGBA rounding error


		NOP								IBNE		VI01,VI00,NonStandard
		NOP								NOP

		MINI.w		VF03,VF03,VF08		NOP

		NOP								B			Standard
		NOP								NOP

NonStandard:

		MAX.w		VF03,VF03,VF08		NOP


Standard:

		MULi		VF02,VF02,I			NOP
		MULAy		ACC,VF00,VF08y		NOP
		MADDq.xyzw	VF03,VF03,Q			WAITQ

		
		
		; test for reflection mapping
		NOP								IBNE		VI11,VI00,StandardDivST
		NOP								LOI			8388608
		NOP								DIV			Q,VF00w,VF01z
		NOP								WAITQ
		
StandardDivST:
		MULq.xyz	VF01,VF01,Q			NOP
		FTOI4.xyz	VF03,VF03			WAITP
		ADDAi.xyz	ACC,VF00,I			MFP.w		VF04,P
		MULAi.w		ACC,VF00,I			IADDIU		VI03,VI03,3
		MADDw		VF02,VF02,VF04w		SQ.xyz		VF01,-2(VI03)
		NOP								SQ			VF03,0(VI03)
		NOP								IBNE		VI03,VI08,FanLoop
		NOP								SQ			VF02,-1(VI03)
		
		NOP								IADDIU		VI03,VI03,1			; add 1 for giftag


		; temporary overflow check
		NOP								ISUB		VI01,VI03,VI12
		NOP								ISUBIU		VI01,VI01,263		; MAX_VU1_BUFFER - # saved regs - 25
		NOP								NOP
		NOP								IBGEZ		VI01,KickFans
		NOP								NOP

		
		; go back for next triangle
ClipNext:
		NOP								IADD		VI01,VI02,VI04
		NOP								IBNE		VI02,VI05,ClipLoop
		NOP								ILW.w		VI01,-1(VI01)
		

KickFans:
		; add a terminal giftag
		NOP								IBEQ		VI03,VI12,PostClip
		NOP								NOP
		NOP								IADDIU		VI01,VI00,0x4000
		NOP								IADDIU		VI01,VI01,0x4000
		NOP								ISW.x		VI01,0(VI03)
		
		; kick the fan buffer
		NOP								XGKICK		VI12
		
		; are there any more source triangles?
		NOP								IBEQ		VI02,VI05,PostClip
		NOP								NOP
		
		; stall VU till fan buffer is free
		NOP								XGKICK		VI03
		
		; reset output pointer and go back for more
		NOP								IADDIU		VI03,VI12,0
		NOP								B			ClipNext
		NOP								NOP

PostClip:
		; restore some registers
		NOP								LQ			VF20,-12(VI12)
		NOP								LQ			VF21,-11(VI12)
		NOP								LQ			VF22,-10(VI12)
		NOP								LQ			VF23,-9(VI12)
		NOP								LQ			VF24,-8(VI12)
		NOP								LQ			VF25,-7(VI12)
		NOP								LQ			VF26,-6(VI12)
		NOP								LQ			VF27,-5(VI12)
		NOP								LQ			VF28,-4(VI12)
		NOP								LQ			VF29,-3(VI12)
		NOP								LQ			VF30,-2(VI12)
		NOP								LQ			VF31,-1(VI12)
		
		; get renderer address
		NOP								ILW.w		VI01,0(VI13)
		
		; reset pointer
		NOP								IADDIU		VI02,VI13,1
		
		; restore render flags
		NOP								XITOP		VI14
		
		; jump to postclip pass
		NOP								JR			VI01
		NOP								NOP





;-----------------------------------------------------------------------------------------------------------------------------


; -------------
; PARTICLE CODE
; -------------


Sprites:

		.if 0

		NOP								IADDIU		VI03,VI02,0
		NOP								MR32.xyz	VF08,VF00			; upper left texcoords (0,0,1)
		ADDw.xyz	VF29,VF00,VF00w		NOP								; lower right texcoords (1,1,1)
		NOP								MFIR.w		VF05,VI00			; clear adc bit
		
SpriteLoop:

		NOP								IADD		VI03,VI03,VI04		; step source pointer
		NOP								LQ			VF01,-1(VI03)		; get vertex
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF01x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF01y		NOP								; row 1 view transform
		MADDz		VF02,VF14,VF01z		NOP								; row 2 view transform
		MULw.xyz	VF03,VF30,VF01w		NOP								; viewport scale time size parameter
		NOP								NOP
		NOP								NOP
		NOP								DIV			Q,VF00w,VF02w		; calc 1/w
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULAq.xyz	ACC,VF02,Q			NOP								; homogeneous div
		MSUBq.xyz	VF04,VF03,Q			NOP								; calc upper left vertex
		MADDq.xyz	VF05,VF03,Q			NOP								; calc lower right vertex
		NOP								NOP
		NOP								SQ.xyz		VF29,1(VI02)		; store lower right texcoords
		NOP								SQ			VF04,0(VI02)		; store upper left vertex
		NOP								IADD		VI02,VI02,VI04		; step destination pointer
		NOP								SQ			VF05,-1(VI02)		; store lower right vertex
		NOP								IBEQ		VI02,VI05,SpriteDone; break
		NOP								NOP
		
		
		NOP								IADD		VI03,VI03,VI04		; step source pointer
		NOP								LQ			VF01,-1(VI03)		; get vertex
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF01x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF01y		NOP								; row 1 view transform
		MADDz		VF02,VF14,VF01z		NOP								; row 2 view transform
		MULw.xyz	VF03,VF30,VF01w		NOP								; viewport scale time size parameter
		NOP								NOP
		NOP								NOP
		NOP								DIV			Q,VF00w,VF02w		; calc 1/w
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MULAq.xyz	ACC,VF02,Q			NOP								; homogeneous div
		MADDq.xyz	VF04,VF03,Q			NOP								; calc lower right vertex
		MSUBq.xyz	VF05,VF03,Q			NOP								; calc upper left vertex
		NOP								NOP
		NOP								SQ.xyz		VF08,1(VI02)		; store upper left texcoords
		NOP								SQ			VF04,0(VI02)		; store lower right vertex
		NOP								IADD		VI02,VI02,VI04		; step destination pointer
		NOP								SQ			VF05,-1(VI02)		; store upper left vertex
		NOP								IBNE		VI02,VI05,SpriteLoop; loop
		NOP								NOP
		
		
SpriteDone:

		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag


		.else
		
		; optimised version

		ADDAx		ACC,VF15,VF00x		LQ			VF03,3(VI02)
		MADDAx		ACC,VF12,VF03x		LQ			VF01,7(VI02)
		MADDAy		ACC,VF13,VF03y		NOP
		MADDz		VF02,VF14,VF03z		NOP
		MULw.xyz	VF04,VF30,VF03w		DIV			Q,VF00w,VF02w
		ADDw.xyz	VF29,VF00,VF00w		MR32.xyz	VF08,VF00
		ADDAx		ACC,VF15,VF00x		MFIR.w		VF04,VI00
		MADDAx		ACC,VF12,VF01x		MFIR.w		VF05,VI00
		MADDAy		ACC,VF13,VF01y		NOP
		MADDz		VF03,VF14,VF01z		NOP
		
SpriteLoop:
		
		MULw.xyz	VF05,VF30,VF01w		NOP
		MULAq.xyz	ACC,VF02,Q			IADD		VI02,VI02,VI04
		MADDq.xyz	VF02,VF04,Q			LQ			VF01,7(VI02)
		MSUBq.xyz	VF04,VF04,Q			DIV			Q,VF00w,VF03w
		NOP								SQ.xyz		VF29,-3(VI02)
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF01x		SQ			VF02,-4(VI02)
		MADDAy		ACC,VF13,VF01y		IBEQ		VI02,VI05,SpriteDone
		MADDz		VF02,VF14,VF01z		SQ			VF04,-1(VI02)


		MULw.xyz	VF04,VF30,VF01w		NOP
		MULAq.xyz	ACC,VF03,Q			IADD		VI02,VI02,VI04
		MSUBq.xyz	VF03,VF05,Q			LQ			VF01,-1(VI02)
		MADDq.xyz	VF05,VF05,Q			DIV			Q,VF00w,VF02w
		NOP								SQ.xyz		VF08,-3(VI02)
		ADDAx		ACC,VF15,VF00x		NOP
		MADDAx		ACC,VF12,VF01x		SQ			VF03,-4(VI02)
		MADDAy		ACC,VF13,VF01y		IBNE		VI02,VI05,SpriteLoop
		MADDz		VF03,VF14,VF01z		SQ			VF05,-1(VI02)
		
SpriteDone:

		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag


		.endif





SpriteCull:

		.if 0

		NOP								MR32.xyz	VF08,VF00			; upper left texcoords (0,0,1)
		ADDw.xyz	VF29,VF00,VF00w		NOP								; lower right texcoords (1,1,1)
		
SpriteCullLoop:

		NOP								IADD		VI02,VI02,VI04
		NOP								LQ			VF01,-1(VI02)		; get xyzr
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF01x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF01y		NOP								; row 1 view transform
		MADDAz		ACC,VF14,VF01z		NOP								; row 2 view transform
		MSUBw		VF02,VF31,VF01w		NOP								; 1st vertex frustum coords
		MADDw		VF03,VF31,VF01w		NOP								; 2nd vertex frustum coords
		NOP								NOP
		NOP								NOP
		MULAw		ACC,VF11,VF02w		DIV			Q,VF00w,VF02w
		MADD		VF04,VF10,VF02		NOP								; apply viewport scale to 1st vertex
		MADD		VF05,VF10,VF03		NOP								; apply viewport scale to 2nd vertex
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF04xyz,VF04w		NOP
		CLIPw.xyz	VF05xyz,VF05w		NOP
		MULq.xyz	VF02,VF02,Q			NOP
		MULq.xyz	VF03,VF03,Q			NOP
		NOP								SQ.xyz		VF29,-3(VI02)
		NOP								FCAND		VI01,0x000FFF
		NOP								IADDIU		VI01,VI01,0x7FFF
		NOP								MFIR.w		VF03,VI01
		NOP								SQ			VF02,-4(VI02)
		NOP								NOP
		NOP								IBEQ		VI02,VI05,SpriteCullDone
		NOP								SQ			VF03,-1(VI02)


		NOP								IADD		VI02,VI02,VI04
		NOP								LQ			VF01,-1(VI02)		; get xyzr
		NOP								NOP
		NOP								NOP
		ADDAx		ACC,VF15,VF00x		NOP								; row 3 view transform
		MADDAx		ACC,VF12,VF01x		NOP								; row 0 view transform
		MADDAy		ACC,VF13,VF01y		NOP								; row 1 view transform
		MADDAz		ACC,VF14,VF01z		NOP								; row 2 view transform
		MADDw		VF02,VF31,VF01w		NOP								; 1st vertex frustum coords
		MSUBw		VF03,VF31,VF01w		NOP								; 2nd vertex frustum coords
		NOP								NOP
		NOP								NOP
		MULAw		ACC,VF11,VF02w		DIV			Q,VF00w,VF02w
		MADD		VF04,VF10,VF02		NOP								; apply viewport scale to 1st vertex
		MADD		VF05,VF10,VF03		NOP								; apply viewport scale to 2nd vertex
		NOP								NOP
		NOP								NOP
		CLIPw.xyz	VF04xyz,VF04w		NOP
		CLIPw.xyz	VF05xyz,VF05w		NOP
		MULq.xyz	VF02,VF02,Q			NOP
		MULq.xyz	VF03,VF03,Q			NOP
		NOP								SQ.xyz		VF08,-3(VI02)
		NOP								FCAND		VI01,0x000FFF
		NOP								IADDIU		VI01,VI01,0x7FFF
		NOP								MFIR.w		VF03,VI01
		NOP								SQ			VF02,-4(VI02)
		NOP								NOP
		NOP								IBNE		VI02,VI05,SpriteCullLoop
		NOP								SQ			VF03,-1(VI02)

SpriteCullDone:

		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag


		.else

		ADDAx		ACC,VF15,VF00x		LQ			VF01,3(VI02)
		MADDAx		ACC,VF12,VF01x		MR32.z		VF29,VF00
		MADDAy		ACC,VF13,VF01y		MR32.xyz	VF08,VF00
		MADDAz		ACC,VF14,VF01z		NOP
		MADDw		VF02,VF31,VF01w		NOP
		MSUBw		VF03,VF31,VF01w		MR32.y		VF29,VF29
		MULAw		ACC,VF11,VF02w		LQ			VF01,7(VI02)
		MADD		VF06,VF10,VF02		DIV			Q,VF00w,VF02w
		MADD		VF07,VF10,VF03		NOP
		ADDAx		ACC,VF15,VF00x		MR32.x		VF29,VF29
		MADDAx		ACC,VF12,VF01x		NOP

SpriteCullLoop:

		CLIPw.xyz	VF06xyz,VF06w		IADD		VI02,VI02,VI04
		CLIPw.xyz	VF07xyz,VF07w		NOP
		MADDAy		ACC,VF13,VF01y		NOP
		MADDAz		ACC,VF14,VF01z		SQ.xyz		VF29,-3(VI02)
		MSUBw		VF04,VF31,VF01w		NOP
		MADDw		VF05,VF31,VF01w		FCAND		VI01,0x000FFF
		MULq.xyz	VF02,VF02,Q			IADDIU		VI01,VI01,0x7FFF
		MULq.xyz	VF03,VF03,Q			LQ			VF01,7(VI02)
		MULAw		ACC,VF11,VF04w		MFIR.w		VF03,VI01
		MADD		VF06,VF10,VF04		DIV			Q,VF00w,VF04w
		MADD		VF07,VF10,VF05		SQ			VF02,-4(VI02)
		ADDAx		ACC,VF15,VF00x		IBEQ		VI02,VI05,SpriteCullDone
		MADDAx		ACC,VF12,VF01x		SQ			VF03,-1(VI02)


		CLIPw.xyz	VF06xyz,VF06w		IADD		VI02,VI02,VI04
		CLIPw.xyz	VF07xyz,VF07w		NOP
		MADDAy		ACC,VF13,VF01y		NOP
		MADDAz		ACC,VF14,VF01z		SQ.xyz		VF08,-3(VI02)
		MADDw		VF02,VF31,VF01w		NOP
		MSUBw		VF03,VF31,VF01w		FCAND		VI01,0x000FFF
		MULq.xyz	VF04,VF04,Q			IADDIU		VI01,VI01,0x7FFF
		MULq.xyz	VF05,VF05,Q			LQ			VF01,7(VI02)
		MULAw		ACC,VF11,VF02w		MFIR.w		VF05,VI01
		MADD		VF06,VF10,VF02		DIV			Q,VF00w,VF02w
		MADD		VF07,VF10,VF03		SQ			VF04,-4(VI02)
		ADDAx		ACC,VF15,VF00x		IBNE		VI02,VI05,SpriteCullLoop
		MADDAx		ACC,VF12,VF01x		SQ			VF05,-1(VI02)

SpriteCullDone:

		NOP								B			NextPrim			; go back for next prim
		NOP								LQI			VF01,(VI02++)		; prefetch next tag

		.endif
		

;-----------------------------------------------------------------------------------------------------------------------------

; ------------------
; VU1 BILLBOARD CODE
; ------------------

; The most general data format for billboards:

; input              output       
; (s0,t0)            (s0,t0,1,0)   
; (r0,g0,b0,a0)      (r0,g0,b0,a0) 
; (x,y,z)            (X0,Y0,Z0,0)  
; (s1,t1)            (s1,t1,1,0)   
; (r1,g1,b1,a1)      (r1,g1,b1,a1) 
; (w,h)              (X1,Y1,Z1,0)  
; (s2,t2)            (s2,t2,1,0)   
; (r2,g2,b2,a2)      (r2,g2,b2,a2) 
; (tx,ty,ty)         (X2,Y2,Z2,0)  
; (s3,t3)            (s3,t3,1,0)   
; (r3,g3,b3,a3)      (r3,g3,b3,a3) 
; (ax,ay,az)         (X3,Y3,Z3,0)  

; screen aligned billboards omit the axis vector (ax,ay,az)
; and various optimised types can omit much of the rest of the data.


		.include "vu1/defs.vsm"





		; float regs
		.equr	Tag,	VF01
		
		.equr	pvw,	VF01
		.equr	xyz0,	VF02
		.equr	xyz1,	VF03
		.equr	xyz2,	VF04
		.equr	xyz3,	VF05
		.equr	dim,	VF06
		.equr	pvl,	VF07
		.equr	axis,	VF08
		.equr	wdir,	VF13
		.equr	vdir2,	VF13
		.equr	viewvec,VF14
		.equr	trans,	VF15
		.equr	stq0,	VF16
		.equr	stq1,	VF17
		.equr	stq2,	VF18
		.equr	stq3,	VF19
		
		.equr	udir,	VF20
		.equr	vdir,	VF21
		.equr	vscale,	VF22
		.equr	cam,	VF25
		.equr	col,	VF26
		.equr	voff,	VF27
		.equr	matWF0,	VF28
		.equr	matWF1,	VF29
		.equr	matWF2,	VF30
		.equr	matWF3,	VF31

		; integer regs
		.equr	Input,	VI02
		.equr	Output,	VI03
		.equr	Step,	VI04
		.equr	End,	VI05
		


		.scope

ScreenAlignedBillboards:

		NOP								IADDIU		VI01,VI00,COLR
		NOP								IAND		VI01,VI14,VI01
		NOP								NOP
		NOP								IBNE		VI01,VI00,ApplyColourBillboard
		NOP								IADDIU		VI01,VI00,Label3
Label3:


		; double v basis vec
		ADD			vdir2,vdir,vdir		NOP
		
		; init output pointer
		NOP								IADDIU		Output,Input,0
		
@Loop:

		; load geometric values
		NOP								LQ			pvw,2(Input)
		NOP								LQ			dim,5(Input)
		NOP								LQ			pvl,8(Input)
		
		; transform world position of pivot by matWF:
		ADDAx		ACC,matWF3,zero		NOP
		MADDAx		ACC,matWF0,pvw.x	NOP
		MADDAy		ACC,matWF1,pvw.y	NOP
		MADDAz		ACC,matWF2,pvw.z	NOP
		
		; offset by pivot's local coords
		MSUBAx		ACC,udir,pvl.x		NOP
		MSUBAy		ACC,vdir,pvl.y		NOP
		
		; generate the 4 corners in frustum coords
		MSUBAy		ACC,vdir,dim.y		NOP
		MSUBx		xyz0,udir,dim.x		NOP
		MADDx		xyz1,udir,dim.x		NOP
		MADDAy		ACC,vdir2,dim.y		NOP
		MSUBx		xyz2,udir,dim.x		NOP
		MADDx		xyz3,udir,dim.x		NOP
		
		; culling tests
		CLIPw.xyz	xyz0.xyz,xyz0.w		NOP
		CLIPw.xyz	xyz1.xyz,xyz1.w		NOP
		CLIPw.xyz	xyz2.xyz,xyz2.w		NOP
		CLIPw.xyz	xyz3.xyz,xyz3.w		NOP
		
		; calc 1/w
		NOP								DIV			Q,voff.w,xyz0.w
		NOP								WAITQ
		
		; transform to homogeneous viewport coords
		MULAw.xyz	ACC,voff,xyz0.w		NOP
		MADD.xyz	xyz0,vscale,xyz0	NOP
		MADD.xyz	xyz1,vscale,xyz1	NOP
		MADD.xyz	xyz2,vscale,xyz2	NOP
		MADD.xyz	xyz3,vscale,xyz3	NOP
		
		; projection
		MULq.xyz	xyz0,xyz0,Q			NOP
		MULq.xyz	xyz1,xyz1,Q			NOP
		MULq.xyz	xyz2,xyz2,Q			NOP
		MULq.xyz	xyz3,xyz3,Q			NOP
		
		; culling results
		NOP								ISUBIU		VI01,VI00,1
		NOP								MFIR.w		xyz0,VI01
		NOP								MFIR.w		xyz1,VI01
		NOP								FCAND		VI01,0xFFFFC0
		NOP								IADDIU		VI01,VI01,0x7FFF
		NOP								MFIR.w		xyz2,VI01
		NOP								FCAND		VI01,0x03FFFF
		NOP								IADDIU		VI01,VI01,0x7FFF
		NOP								MFIR.w		xyz3,VI01
		
		; store corners
		NOP								SQ			xyz0, 2(Output)
		NOP								SQ			xyz1, 5(Output)
		NOP								SQ			xyz2, 8(Output)
		NOP								SQ			xyz3,11(Output)

		; step pointers and loop
		NOP								IADD		Input,Input,Step
		NOP								IADD		Output,Output,Step
		NOP								NOP
		NOP								IBNE		Output,End,@Loop
		NOP								NOP
		
		; go back for more
		NOP								LQI			Tag,(Output++)
		NOP								B			NextPrim
		NOP								IADDIU		Input,Output,0

		.endscope
		
		
		
		.scope
		
LongAxisBillboards:

		NOP								IADDIU		VI01,VI00,COLR
		NOP								IAND		VI01,VI14,VI01
		NOP								NOP
		NOP								IBNE		VI01,VI00,ApplyColourBillboard
		NOP								IADDIU		VI01,VI00,Label4
Label4:

		; init output pointer
		NOP								IADDIU		Output,Input,0

@Loop:

		; load geometric values
		NOP								LQ			pvw,  2(Input)
		NOP								LQ			dim,  5(Input)
		NOP								LQ			pvl,  8(Input)
		NOP								LQ			axis,11(Input)
		
		; get view vector in world space
		SUB.xyz		viewvec,pvw,cam		NOP
		
		; generate transverse axis in world space
		OPMULA.xyz	ACC,viewvec,axis	NOP
		OPMSUB.xyz	trans,axis,viewvec	NOP
		NOP								ERLENG		P,trans
		NOP								WAITP
		NOP								MFP.w		trans,P
		MULw.xyz	trans,trans,trans.w	NOP
		
		; generate wdir
		OPMULA.xyz	ACC,trans,axis		NOP
		OPMSUB.xyz	wdir,axis,trans		NOP
		
		; transform to frustum coords
		MULAx		ACC,matWF0,trans.x	NOP
		MADDAy		ACC,matWF1,trans.y	NOP
		MADDz		trans,matWF2,trans.z	NOP
		
		MULAx		ACC,matWF0,axis.x	NOP
		MADDAy		ACC,matWF1,axis.y	NOP
		MADDz		axis,matWF2,axis.z	NOP
		
		MULAx		ACC,matWF0,wdir.x	NOP
		MADDAy		ACC,matWF1,wdir.y	NOP
		MADDz		wdir,matWF2,wdir.z	NOP
		
		; transform world position of pivot by matWF:
		ADDAx		ACC,matWF3,zero		NOP
		MADDAx		ACC,matWF0,pvw.x	NOP
		MADDAy		ACC,matWF1,pvw.y	NOP
		MADDAz		ACC,matWF2,pvw.z	NOP
		
		; offset by pivot's local coords
		MSUBAx		ACC,trans,pvl.x		NOP
		MSUBAy		ACC,axis,pvl.y		NOP
		MSUBAz		ACC,wdir,pvl.z		NOP
		
		; generate the 2 'left' corners in frustum coords
		MSUBAx.xy	ACC,trans,dim.x		NOP
		MSUBy		xyz0,axis,dim.y		NOP
		MADDy		xyz2,axis,dim.y		NOP
		
		; generate the 2 'right' corners in frustum coords
		MADDAx.xy	ACC,trans,dim.x		NOP
		MADDAx.xy	ACC,trans,dim.x		NOP
		MSUBy		xyz1,axis,dim.y		NOP
		MADDy		xyz3,axis,dim.y		NOP
		
		; culling tests
		CLIPw.xyz	xyz0.xyz,xyz0.w		NOP
		CLIPw.xyz	xyz1.xyz,xyz1.w		NOP
		CLIPw.xyz	xyz2.xyz,xyz2.w		NOP
		CLIPw.xyz	xyz3.xyz,xyz3.w		NOP
		
		; transform to homogeneous viewport coords
		MULAw.xyz	ACC,voff,xyz0.w		NOP
		MADD.xyz	xyz0,vscale,xyz0	NOP
		MADD.xyz	xyz1,vscale,xyz1	NOP
		MULAw.xyz	ACC,voff,xyz2.w		NOP
		MADD.xyz	xyz2,vscale,xyz2	NOP
		MADD.xyz	xyz3,vscale,xyz3	NOP
		
		; load texcoords
		NOP								LQ.xyz		stq0,0(Input)
		NOP								LQ.xyz		stq1,3(Input)
		NOP								LQ.xyz		stq2,6(Input)
		NOP								LQ.xyz		stq3,9(Input)
		
		; homogeneous divs for 'top' corners
		NOP								DIV			Q,voff.w,xyz0.w
		NOP								WAITQ
		MULq.xyz	xyz0,xyz0,Q			NOP
		MULq.xyz	xyz1,xyz1,Q			NOP
		MULq.xyz	stq0,stq0,Q			NOP
		MULq.xyz	stq1,stq1,Q			NOP
		
		; homogeneous divs for 'bottom' corners
		NOP								DIV			Q,voff.w,xyz2.w
		NOP								WAITQ
		MULq.xyz	xyz2,xyz2,Q			NOP
		MULq.xyz	xyz3,xyz3,Q			NOP
		MULq.xyz	stq2,stq2,Q			NOP
		MULq.xyz	stq3,stq3,Q			NOP
		
		; culling results
		NOP								ISUBIU		VI01,VI00,1
		NOP								MFIR.w		xyz0,VI01
		NOP								MFIR.w		xyz1,VI01
		NOP								FCAND		VI01,0xFFFFC0
		NOP								IADDIU		VI01,VI01,0x7FFF
		NOP								MFIR.w		xyz2,VI01
		NOP								FCAND		VI01,0x03FFFF
		NOP								IADDIU		VI01,VI01,0x7FFF
		NOP								MFIR.w		xyz3,VI01
		
		; store corners
		NOP								SQ			xyz0, 2(Output)
		NOP								SQ			xyz1, 5(Output)
		NOP								SQ			xyz2, 8(Output)
		NOP								SQ			xyz3,11(Output)

		; store texcoords
		NOP								SQ.xyz		stq0,0(Output)
		NOP								SQ.xyz		stq1,3(Output)
		NOP								SQ.xyz		stq2,6(Output)
		NOP								SQ.xyz		stq3,9(Output)

		; step pointers and loop
		NOP								IADD		Input,Input,Step
		NOP								IADD		Output,Output,Step
		NOP								NOP
		NOP								IBNE		Output,End,@Loop
		NOP								NOP
		
		; go back for more
		NOP								LQI			Tag,(Output++)
		NOP								B			NextPrim
		NOP								IADDIU		Input,Output,0

		.endscope
		
		
		
		
		.scope
		
ShortAxisBillboards:

		NOP								IADDIU		VI01,VI00,COLR
		NOP								IAND		VI01,VI14,VI01
		NOP								NOP
		NOP								IBNE		VI01,VI00,ApplyColourBillboard
		NOP								IADDIU		VI01,VI00,Label5
Label5:

		; init output pointer
		NOP								IADDIU		Output,Input,0

@Loop:

		; load geometric values
		NOP								LQ			pvw,  2(Input)
		NOP								LQ			dim,  5(Input)
		NOP								LQ			pvl,  8(Input)
		NOP								LQ			axis,11(Input)
		
		; get view vector in world space
		SUB.xyz		viewvec,pvw,cam		NOP
		
		; generate transverse axis in world space
		OPMULA.xyz	ACC,axis,viewvec	NOP
		OPMSUB.xyz	trans,viewvec,axis	NOP
		NOP								ERLENG		P,trans
		NOP								WAITP
		NOP								MFP.w		trans,P
		MULw.xyz	trans,trans,trans.w	NOP
		
		; generate wdir
		OPMULA.xyz	ACC,trans,axis		NOP
		OPMSUB.xyz	wdir,axis,trans		NOP
		
		; transform to frustum coords
		MULAx		ACC,matWF0,trans.x	NOP
		MADDAy		ACC,matWF1,trans.y	NOP
		MADDz		trans,matWF2,trans.z	NOP
		
		MULAx		ACC,matWF0,axis.x	NOP
		MADDAy		ACC,matWF1,axis.y	NOP
		MADDz		axis,matWF2,axis.z	NOP
		
		MULAx		ACC,matWF0,wdir.x	NOP
		MADDAy		ACC,matWF1,wdir.y	NOP
		MADDz		wdir,matWF2,wdir.z	NOP
		
		; transform world position of pivot by matWF:
		ADDAx		ACC,matWF3,zero		NOP
		MADDAx		ACC,matWF0,pvw.x	NOP
		MADDAy		ACC,matWF1,pvw.y	NOP
		MADDAz		ACC,matWF2,pvw.z	NOP
		
		; offset by pivot's local coords
		MSUBAy		ACC,trans,pvl.y		NOP
		MSUBAx		ACC,axis,pvl.x		NOP
		MSUBAz		ACC,wdir,pvl.z		NOP
		
		; generate the 2 'left' corners in frustum coords
		MSUBAy.xy	ACC,trans,dim.y		NOP
		MSUBx		xyz0,axis,dim.x		NOP
		MADDx		xyz2,axis,dim.x		NOP
		
		; generate the 2 'right' corners in frustum coords
		MADDAy.xy	ACC,trans,dim.y		NOP
		MADDAy.xy	ACC,trans,dim.y		NOP
		MSUBx		xyz1,axis,dim.x		NOP
		MADDx		xyz3,axis,dim.x		NOP
		
		; culling tests
		CLIPw.xyz	xyz0.xyz,xyz0.w		NOP
		CLIPw.xyz	xyz1.xyz,xyz1.w		NOP
		CLIPw.xyz	xyz2.xyz,xyz2.w		NOP
		CLIPw.xyz	xyz3.xyz,xyz3.w		NOP
		
		; transform to homogeneous viewport coords
		MULAw.xyz	ACC,voff,xyz0.w		NOP
		MADD.xyz	xyz0,vscale,xyz0	NOP
		MADD.xyz	xyz1,vscale,xyz1	NOP
		MULAw.xyz	ACC,voff,xyz2.w		NOP
		MADD.xyz	xyz2,vscale,xyz2	NOP
		MADD.xyz	xyz3,vscale,xyz3	NOP
		
		; load texcoords
		NOP								LQ.xyz		stq0,0(Input)
		NOP								LQ.xyz		stq1,3(Input)
		NOP								LQ.xyz		stq2,6(Input)
		NOP								LQ.xyz		stq3,9(Input)
		
		; homogeneous divs for 'top' corners
		NOP								DIV			Q,voff.w,xyz0.w
		NOP								WAITQ
		MULq.xyz	xyz0,xyz0,Q			NOP
		MULq.xyz	xyz1,xyz1,Q			NOP
		MULq.xyz	stq0,stq0,Q			NOP
		MULq.xyz	stq1,stq1,Q			NOP
		
		; homogeneous divs for 'bottom' corners
		NOP								DIV			Q,voff.w,xyz2.w
		NOP								WAITQ
		MULq.xyz	xyz2,xyz2,Q			NOP
		MULq.xyz	xyz3,xyz3,Q			NOP
		MULq.xyz	stq2,stq2,Q			NOP
		MULq.xyz	stq3,stq3,Q			NOP
		
		; culling results
		NOP								ISUBIU		VI01,VI00,1
		NOP								MFIR.w		xyz0,VI01
		NOP								MFIR.w		xyz1,VI01
		NOP								FCAND		VI01,0xFFFFC0
		NOP								IADDIU		VI01,VI01,0x7FFF
		NOP								MFIR.w		xyz2,VI01
		NOP								FCAND		VI01,0x03FFFF
		NOP								IADDIU		VI01,VI01,0x7FFF
		NOP								MFIR.w		xyz3,VI01
		
		; store corners
		NOP								SQ			xyz0, 2(Output)
		NOP								SQ			xyz1, 5(Output)
		NOP								SQ			xyz2, 8(Output)
		NOP								SQ			xyz3,11(Output)

		; store texcoords
		NOP								SQ.xyz		stq0,0(Output)
		NOP								SQ.xyz		stq1,3(Output)
		NOP								SQ.xyz		stq2,6(Output)
		NOP								SQ.xyz		stq3,9(Output)

		; step pointers and loop
		NOP								IADD		Input,Input,Step
		NOP								IADD		Output,Output,Step
		NOP								NOP
		NOP								IBNE		Output,End,@Loop
		NOP								NOP
		
		; go back for more
		NOP								LQI			Tag,(Output++)
		NOP								B			NextPrim
		NOP								IADDIU		Input,Output,0

		.endscope
		
		
; applying material colour to a billboard
ApplyColourBillboard:

		.if 0
		; unoptimised version

		NOP								LOI			8388608
		ADDAi		ACC,VF00,I			LOI			8388863
		
LoopACB:NOP								LQ.xyz		VF01,1(VI02)
		NOP								IADDIU		VI02,VI02,3
		NOP								NOP
		NOP								NOP
		ITOF0.xyz	VF02,VF01			NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MADD.xyz	VF03,VF02,col		NOP
		NOP								NOP
		NOP								NOP
		NOP								NOP
		MINIi.xyz	VF04,VF03,I			NOP
		NOP								NOP
		NOP								NOP
		NOP								IBNE		VI02,VI05,LoopACB
		NOP								SQ.xyz		VF04,-2(VI02)
		
		.else
		; optimised version
		
		NOP								LOI			8388608
		ADDAi		ACC,VF00,I			LQ.xyz		VF03,1(VI02)
		ITOF0.xyz	VF03,VF03			LQ.xyz		VF02,4(VI02)
		MADD.xyz	VF03,VF03,col		LOI			8388863
		ITOF0.xyz	VF02,VF02			LQ.xyz		VF01,7(VI02)
		
LoopACB:MINIi.xyz	VF04,VF03,I			IADDIU		VI02,VI02,3
		MADD.xyz	VF03,VF02,col		NOP
		ITOF0.xyz	VF02,VF01			LQ.xyz		VF01,7(VI02)
		NOP								IBNE		VI02,VI05,LoopACB
		NOP								SQ.xyz		VF04,-2(VI02)
		
		.endif

		NOP								JR			VI01
		NOP								ISUB		VI02,VI02,VI06
		
;-----------------------------------------------------------------------------------------------------------------------------

		; Can use this to see how much micromem is left. (The assembler warns if the code overflows.)
		;.rept 93
		;NOP								NOP
		;.endr


.EndMPG

MPGEnd:





================================================
FILE: Code/Gfx/NGPS/NX/vu1code.h
================================================
#ifndef __MICROCODE_H
#define __MICROCODE_H

// the __attribute__ things are there so the code can compile using GP relative
// otherwise, the compiler assumes these go in the .lit4 section, and we get a link error, as they don't

#ifdef __PLAT_NGPS__

extern uint MPGStart       __attribute__((section(".vudata")));
extern uint MPGEnd         __attribute__((section(".vudata")));
extern uint Setup          __attribute__((section(".vudata")));
extern uint Jump           __attribute__((section(".vudata")));
extern uint Breakpoint     __attribute__((section(".vudata")));
extern uint ParseInit      __attribute__((section(".vudata")));
extern uint Parser         __attribute__((section(".vudata")));
extern uint L_VF09         __attribute__((section(".vudata")));
extern uint L_VF10         __attribute__((section(".vudata")));
extern uint L_VF11         __attribute__((section(".vudata")));
extern uint L_VF12         __attribute__((section(".vudata")));
extern uint L_VF13         __attribute__((section(".vudata")));
extern uint L_VF14         __attribute__((section(".vudata")));
extern uint L_VF15         __attribute__((section(".vudata")));
extern uint L_VF16         __attribute__((section(".vudata")));
extern uint L_VF17         __attribute__((section(".vudata")));
extern uint L_VF18         __attribute__((section(".vudata")));
extern uint L_VF19         __attribute__((section(".vudata")));
extern uint L_VF20         __attribute__((section(".vudata")));
extern uint L_VF21         __attribute__((section(".vudata")));
extern uint L_VF22         __attribute__((section(".vudata")));
extern uint L_VF23         __attribute__((section(".vudata")));
extern uint L_VF24         __attribute__((section(".vudata")));
extern uint L_VF25         __attribute__((section(".vudata")));
extern uint L_VF26         __attribute__((section(".vudata")));
extern uint L_VF27         __attribute__((section(".vudata")));
extern uint L_VF28         __attribute__((section(".vudata")));
extern uint L_VF39         __attribute__((section(".vudata")));
extern uint L_VF30         __attribute__((section(".vudata")));
extern uint L_VF31         __attribute__((section(".vudata")));
extern uint GSPrim         __attribute__((section(".vudata")));
extern uint Proj           __attribute__((section(".vudata")));
extern uint PTex           __attribute__((section(".vudata")));
extern uint Refl           __attribute__((section(".vudata")));
extern uint Line           __attribute__((section(".vudata")));
extern uint Skin           __attribute__((section(".vudata")));
extern uint Sprites        __attribute__((section(".vudata")));
extern uint SpriteCull     __attribute__((section(".vudata")));
extern uint ReformatXforms __attribute__((section(".vudata")));
extern uint ShadowVolumeSkin __attribute__((section(".vudata")));

#endif

#ifdef __PLAT_WN32__

//#define Setup		 0
//#define Jump		 2
#define Breakpoint	 4
#define ParseInit	 6
#define Parser		 8
#define L_VF09		10	// (Near, Far, k/(xRes/2), k/(yRes/2)) where k=viewport_scale_x, should be 2048 but is 1900 because of clipper problem
#define L_VF10		12	// inverse viewport scale vector
#define L_VF11		14	// inverse viewport offset vector
#define L_VF12		16	// row 0, local to viewport transform
#define L_VF13		18	// row 1, local to viewport transform
#define L_VF14		20	// row 2, local to viewport transform
#define L_VF15		22	// row 3, local to viewport transform
#define L_VF16		24	// lightsource 2 colour (r,g,b,?)
#define L_VF17		26	// row 0, reflection map transform
#define L_VF18		28	// row 1, reflection map transform
#define L_VF19		30	// row 2, reflection map transform
#define L_VF20		32	// light vectors, x components
#define L_VF21		34	// light vectors, y components
#define L_VF22		36	// light vectors, z components
#define L_VF23		38	// ambient colour (r,g,b,?)
#define L_VF24		40	// lightsource 0 colour (r,g,b,?)
#define L_VF25		42	// lightsource 1 colour (r,g,b,?)
#define L_VF26		44	// texture projection scale vector
#define L_VF27		46	// texture projection offset vector
#define L_VF28		48	// saves the z-components of the view matrix during a z-push
#define L_VF29		50	// temp skinning
#define L_VF30		52	// temp skinning
#define L_VF31		54	// temp skinning
#define GSPrim		56
#define Proj		58
#define PTex		60
#define Refl		62
#define Line		64
#define Skin		66
#define Light		68
#define LightT		70
#define WibbleT		72
#define LWibT		74
#define AddZPush	76
#define SubZPush	78
#define Setup		80
#define Jump		82
#define SCAB		84
#define LAB			86
#define SHAB		88

#endif

#define PROJ 0x00
#define CULL 0x01		// per-triangle view culling
#define CLIP 0x02		// full 3D clipping of triangles
#define SHDW 0x04		// skinned=>cast shadow into texture; non-skinned=>render mesh with projected shadow texture on it
#define COLR 0x08		// apply colour at vertices
#define FOGE 0x10		// calculate per-vertex fog coefficient
#define WIRE 0x20		// render skinned as wireframe (but doesn't render all edges)

#endif //__MICROCODE_H



================================================
FILE: Code/Gfx/NGPS/NX/vu1context.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		THPS4													**
**																			**
**	Module:			vu1context				 								**
**																			**
**	File name:		vu1context.cpp											**
**																			**
**	Created by:		00/00/00	-	mrd										**
**																			**
**	Description:	sends contextual setup data to vu1 float registers		**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include "geomnode.h"
#include "vu1context.h"
#include "vu1code.h"
#include "render.h"
#include "dma.h"
#include "vif.h"
#include "gif.h"
#include "gs.h"
#include "light.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace NxPs2
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

CVu1Context::CVu1Context()
{
	Dbg_Assert("Don't use this constructor!");
}


void CVu1Context::Init()
{
	uint8 *dma_ploc = dma::pLoc;

	//dma::pLoc = (uint8 *)&m_stmask;
	dma::pLoc = (uint8 *)&m_stmask;
	vu1::Loc = 0;

	// set vifcodes
	vif::STMASK(0);				// mp_light_group = NULL; in a very cheeky way
	vif::STCYCL(1,1);
	vif::UNPACK(0,V4_32,STD_CTXT_SIZE+1,REL,UNSIGNED,0);

	// set m_giftag
	gif::Tag1(gs::NOP, 1, PACKED, 0, 0, 0, STD_CTXT_SIZE, VU1_ADDR(L_VF12));

	//SetExtended(false);

	dma::pLoc = dma_ploc;
}


void CVu1Context::InitExtended()
{
	uint8 *dma_ploc = dma::pLoc;

	//dma::pLoc = (uint8 *)&m_stmask;
	dma::pLoc = (uint8 *)&m_stmask;
	vu1::Loc = 0;

	// set vifcodes
	vif::STMASK(0);				// mp_light_group = NULL; in a very cheeky way
	vif::STCYCL(1,1);
	vif::UNPACK(0,V4_32,EXT_CTXT_SIZE+1,REL,UNSIGNED,0);

	// set m_giftag
	gif::Tag1(gs::NOP, 1, PACKED, 0, 0, 0, EXT_CTXT_SIZE, VU1_ADDR(L_VF12));

	SetExtended(true);

	update_lights();

	dma::pLoc = dma_ploc;
}


CVu1Context::CVu1Context(const CVu1Context &ctxt)
{
	// Mick: Since this does not contain any pointers, it's safe to just copy it
	// this cuts down on unwarrented initialization of the various vectors and stuff
	// and should be fast as we are quad word aligned.
	// Garrett: Even though we have a pointer to a light group, it is still OK to
	// use memcpy() since this class doesn't manage the instance it points to.

	if (ctxt.IsExtended())
	{
		memcpy(this, &ctxt, (EXT_CTXT_SIZE+7)*16);
	}
	else
	{
		// reduced version
		memcpy(this, &ctxt, (STD_CTXT_SIZE+7)*16);
	}

}


void CVu1Context::StandardSetup(Mth::Matrix &localToWorld)
{
	// get the local to camera matrix
	m_matrix = localToWorld * render::WorldToCamera;

	// get the massaged matrix values
	Mth::Matrix inv = m_matrix;
	inv.InvertUniform();
	m_translation = -inv[3] * SUB_INCH_PRECISION;
	m_translation[0] = float(int(m_translation[0]));
	m_translation[1] = float(int(m_translation[1]));
	m_translation[2] = float(int(m_translation[2]));

	Mth::Matrix adjusted_matrix = m_matrix;
	//m_adjustedMatrix = m_matrix;
	adjusted_matrix[3] = -m_translation * m_matrix * RECIPROCAL_SUB_INCH_PRECISION;

	// generate the vu1 context data
	//m_localToFrustum  = m_adjustedMatrix * render::CameraToFrustum;
	Mth::Matrix temp;
	sceVu0MulMatrix((sceVu0FVECTOR*)&temp,
					(sceVu0FVECTOR*)&render::CameraToFrustum,
					(sceVu0FVECTOR*)&adjusted_matrix);   	
	//m_localToFrustum = temp;
	
//	m_localToViewport = m_localToFrustum * render::FrustumToViewport;
	sceVu0MulMatrix((sceVu0FVECTOR*)&m_localToViewport,
					(sceVu0FVECTOR*)&render::FrustumToViewport,
					(sceVu0FVECTOR*)&temp);

	// Update light directions
	if (IsExtended())
	{
		update_lights();
	}
}



CVu1Context *CVu1Context::Localise()
{
	dma::SetList(NULL);
	CVu1Context *p_new_context = (CVu1Context*)dma::pLoc;

	if (IsExtended())
	{
		memcpy(dma::pLoc, this, sizeof(CVu1Context));
		dma::pLoc += sizeof(CVu1Context);
	}
	else
	{
		// reduced version
		memcpy(dma::pLoc, this, (STD_CTXT_SIZE+7)*16);
		dma::pLoc += (STD_CTXT_SIZE+7)*16;
	}

	return p_new_context;
}




CVu1Context *CVu1Context::LocaliseExtend()
{
	dma::SetList(NULL);
	CVu1Context *p_new_context = (CVu1Context*)dma::pLoc;

	if (IsExtended())
	{
		memcpy(dma::pLoc, this, sizeof(CVu1Context));
	}
	else
	{
		memcpy(dma::pLoc, this, (STD_CTXT_SIZE+7)*16);
		p_new_context->InitExtended();
	}
	dma::pLoc += sizeof(CVu1Context);

	return p_new_context;
}




void CVu1Context::HierarchicalSetup(Mth::Matrix &localToParent)
{
	// get the local to camera matrix
	m_matrix = localToParent * m_matrix;

	// get the massaged matrix values
	Mth::Matrix temp = m_matrix;
	m_translation = -(temp.InvertUniform())[3] * SUB_INCH_PRECISION;
	m_translation[0] = float(int(m_translation[0]));
	m_translation[1] = float(int(m_translation[1]));
	m_translation[2] = float(int(m_translation[2]));

	Mth::Matrix adjusted_matrix = m_matrix;
	//m_adjustedMatrix = m_matrix;
	adjusted_matrix[3] = -m_translation * m_matrix * RECIPROCAL_SUB_INCH_PRECISION;

	// generate the vu1 context data
//	m_localToFrustum  = m_adjustedMatrix * render::CameraToFrustum;
	sceVu0MulMatrix((sceVu0FVECTOR*)&temp,
					(sceVu0FVECTOR*)&render::CameraToFrustum,
					(sceVu0FVECTOR*)&adjusted_matrix);   	
	//m_localToFrustum = temp;
	
//	m_localToViewport = m_localToFrustum * render::FrustumToViewport;
	sceVu0MulMatrix((sceVu0FVECTOR*)&m_localToViewport,
					(sceVu0FVECTOR*)&render::FrustumToViewport,
					(sceVu0FVECTOR*)&temp);

	// Update light directions
	if (IsExtended())
	{
		update_lights();
	}
}

// Mick: This is taking 1.5% of our precious CPU time
void CVu1Context::update_lights()
{
	Mth::Matrix temp;
	temp[W].Set(0.0f,0.0f,0.0f,1.0f);

	// light vectors
	if (mp_light_group)
	{
		temp[0] = mp_light_group->GetDirection(0);
		temp[1] = mp_light_group->GetDirection(1);
		temp[2] = mp_light_group->GetDirection(2);
	
		temp *= render::WorldToCamera;
		temp.Transpose();
		
		sceVu0MulMatrix((sceVu0FVECTOR*)&m_lightVecsX,
						(sceVu0FVECTOR*)&temp,
						(sceVu0FVECTOR*)&m_matrix);   	
	
		m_ambientColour = mp_light_group->GetAmbientColor() * 1.0f/128.0f;
		m_lightColour0  = mp_light_group->GetDiffuseColor(0) * 1.0f/128.0f;
		m_lightColour1  = mp_light_group->GetDiffuseColor(1) * 1.0f/128.0f;
		m_lightColour2  = mp_light_group->GetDiffuseColor(2) * 1.0f/128.0f;
	}
	else
	{
		temp[0] = CLightGroup::sGetDefaultDirection(0);
		temp[1] = CLightGroup::sGetDefaultDirection(1);
		temp[2] = CLightGroup::sGetDefaultDirection(2);

		temp *= render::WorldToCamera;
		temp.Transpose();
		
		sceVu0MulMatrix((sceVu0FVECTOR*)&m_lightVecsX,
						(sceVu0FVECTOR*)&temp,
						(sceVu0FVECTOR*)&m_matrix);   	
	
		m_ambientColour = CLightGroup::sGetDefaultAmbientColor() * 1.0f/128.0f;
		m_lightColour0  = CLightGroup::sGetDefaultDiffuseColor(0) * 1.0f/128.0f;
		m_lightColour1  = CLightGroup::sGetDefaultDiffuseColor(1) * 1.0f/128.0f;
		m_lightColour2  = CLightGroup::sGetDefaultDiffuseColor(2) * 1.0f/128.0f;
	}

	
}


void CVu1Context::SetupAsSky()
{
	// get the local to camera matrix
	m_matrix = render::WorldToCamera;

	// get the massaged matrix values
	m_matrix[3].Set(0,0,0,1);
	m_translation.Set(0,0,0,1);

	Mth::Matrix adjusted_matrix = m_matrix;
	//m_adjustedMatrix = m_matrix;
	adjusted_matrix.ScaleLocal(Mth::Vector(RECIPROCAL_SUB_INCH_PRECISION, RECIPROCAL_SUB_INCH_PRECISION, RECIPROCAL_SUB_INCH_PRECISION, 1.0f));

	// generate the vu1 context data
	//m_viewportScale   = render::SkyViewportScale;
	//m_viewportOffset  = render::SkyViewportOffset;
	//m_localToFrustum  = adjusted_matrix * render::CameraToFrustum;
	m_localToViewport = adjusted_matrix * render::CameraToFrustum * render::SkyFrustumToViewport;
}


void CVu1Context::AddZPush(float zPush)
{
	m_localToViewport[0][2] += m_localToViewport[0][3] * zPush * 16.0f * render::InverseViewportScale[3];
	m_localToViewport[1][2] += m_localToViewport[1][3] * zPush * 16.0f * render::InverseViewportScale[3];
	m_localToViewport[2][2] += m_localToViewport[2][3] * zPush * 16.0f * render::InverseViewportScale[3];
	m_localToViewport[3][2] += m_localToViewport[3][3] * zPush * 16.0f * render::InverseViewportScale[3];
}


void CVu1Context::WibbleUVs(float *pData, uint Explicit)
{
	if (Explicit)
	{
		m_texOffset[0] = pData[8];
		m_texOffset[1] = pData[9];
	}
	else
	{
		// Get number of vblanks, but reduce it to 24 bits so we don't lose accuracy in the float conversion.
		// This will introduce a glitch every 6 days, but that's preferrable to the loss of accuracy.
		int vblanks = Tmr::GetVblanks() & 0x00FFFFFF;

		// convert to seconds as float
		float t = (float)vblanks * (1.0f/60.0f);
		float angle;

		// u offset
		angle = pData[2] * t + pData[6];
		angle -= (2.0f*Mth::PI) * (float)(int)(angle * (0.5f/Mth::PI));		// reduce angle mod 2pi
		m_texOffset[0] = pData[0] * t + pData[4] * sinf(angle);
		m_texOffset[0] += 8.0f;
		m_texOffset[0] -= (float)(((int)m_texOffset[0] >> 4) << 4);			// reduce offset mod 16
		if (m_texOffset[0] < 0.0f)											// and put it in the range -8 to +8
			m_texOffset[0] += 8.0f;
		else
			m_texOffset[0] -= 8.0f;

		// v offset
		angle = pData[3] * t + pData[7];
		angle -= (2.0f*Mth::PI) * (float)(int)(angle * (0.5f/Mth::PI));		// reduce angle mod 2pi
		m_texOffset[1] = pData[1] * t + pData[5] * sinf(angle);
		m_texOffset[1] += 8.0f;
		m_texOffset[1] -= (float)(((int)m_texOffset[1] >> 4) << 4);			// reduce offset mod 16
		if (m_texOffset[1] < 0.0f)											// and put it in the range -8 to +8
			m_texOffset[1] += 8.0f;
		else
			m_texOffset[1] -= 8.0f;

	}
}


void CVu1Context::SetShadowVecs(float y)
{
	m_texScale  = Mth::Vector(0.0078125f, 0.0078125f, 0.0f, 0.0f);
	m_texOffset = Mth::Vector(0.5f-0.0078125f*(render::ShadowCameraPosition[X]+RECIPROCAL_SUB_INCH_PRECISION*m_translation[X]),
							  0.5f-0.0078125f*(render::ShadowCameraPosition[Z]+RECIPROCAL_SUB_INCH_PRECISION*m_translation[Z]),
							  -((y+36.0f)+RECIPROCAL_SUB_INCH_PRECISION*m_translation[Y]),
							  0.0f);
}


void CVu1Context::SetColour(CGeomNode *pNode)
{
	uint32 rgba = pNode->GetColor();
   	m_texScale[X] = (float)(rgba     & 0xFF) * 0.0078125f;
	m_texScale[Y] = (float)(rgba>> 8 & 0xFF) * 0.0078125f;
	m_texScale[Z] = (float)(rgba>>16 & 0xFF) * 0.0078125f;
	m_texScale[W] = (float)(rgba>>24       ) * 0.0078125f;
}


Mth::Vector *CVu1Context::GetColour()
{
	return &m_texScale;
}


void CVu1Context::SetLights(CLightGroup *pLightGroup)
{
	mp_light_group = pLightGroup;
	update_lights();
}


void CVu1Context::SetReflectionVecs(sint16 uScale, sint16 vScale)
{
	Mth::Matrix temp;
	float uf,vf;
	uf = float(uScale)*(0.5f/256.0f);
	vf = float(vScale)*(0.5f/256.0f);

	temp[0] = Mth::Vector(uf, 0,  0,  0);
	temp[1] = Mth::Vector(0,  vf, 0,  0);
	temp[2] = Mth::Vector(0,  0,  1.4,0);
	temp.Transpose();

	sceVu0MulMatrix((sceVu0FVECTOR*)&m_reflVecsX,
					(sceVu0FVECTOR*)&temp,
					(sceVu0FVECTOR*)&m_matrix);   	
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace NxPs2






================================================
FILE: Code/Gfx/NGPS/NX/vu1context.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		THPS4													**
**																			**
**	Module:			vu1context				 								**
**																			**
**	File name:		vu1context.h											**
**																			**
**	Created by:		00/00/00	-	mrd										**
**																			**
**	Description:	sends contextual setup data to vu1 float registers		**
**																			**
*****************************************************************************/

#ifndef __NGPS_NX_VU1CONTEXT_H
#define __NGPS_NX_VU1CONTEXT_H

#ifdef __PLAT_NGPS__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace NxPs2
{


#define STD_CTXT_SIZE 5
#define EXT_CTXT_SIZE 16



class CVu1Context
{
public:

	CVu1Context();
	void Init();
	void InitExtended();
	CVu1Context(const CVu1Context &node);
	~CVu1Context() {}

	void			StandardSetup(Mth::Matrix &localToWorld);
	void			HierarchicalSetup(Mth::Matrix &localToParent);
	void			SetupAsSky();
	void			AddZPush(float zPush);
	void			WibbleUVs(float *pData, uint Explicit);
	void			SetShadowVecs(float y);
	void			SetColour(CGeomNode *pNode);
	void			SetAmbient(uint32 rgba);
	void			SetLights(CLightGroup *pLightGroup);
	void			SetReflectionVecs(sint16 uScale, sint16 vScale);
	void			SetExtended(bool yes);

	CVu1Context		*Localise();
	CVu1Context		*LocaliseExtend();


	Mth::Matrix 	*GetMatrix();
	Mth::Vector		*GetTranslation();
	int				GetDma();
	Mth::Vector		*GetColour();
	CLightGroup		*GetLights();
	bool			IsExtended() const;

private:
	void			update_lights();

	// non-dma data
	Mth::Matrix		m_matrix;
	Mth::Vector		m_translation;

	// dma data... don't rearrange this part!
	uint32			m_stmask;
	CLightGroup *	mp_light_group;
	uint32			m_stcycl;
	uint32			m_unpack;
	uint128			m_giftag;
	Mth::Matrix		m_localToViewport;
	Mth::Vector		m_texScale;
	Mth::Vector		m_reflVecsX;
	Mth::Vector		m_reflVecsY;
	Mth::Vector		m_reflVecsZ;
	Mth::Vector		m_lightVecsX;
	Mth::Vector		m_lightVecsY;
	Mth::Vector		m_lightVecsZ;
	Mth::Vector		m_ambientColour;
	Mth::Vector		m_lightColour0;
	Mth::Vector		m_lightColour1;
	Mth::Vector		m_lightColour2;
	Mth::Vector		m_texOffset;

	// keep the size a whole number of a quadwords
};


inline Mth::Matrix *CVu1Context::GetMatrix()
{
	return &m_matrix;
}


inline Mth::Vector *CVu1Context::GetTranslation()
{
	return &m_translation;
}

inline int CVu1Context::GetDma()
{
	return (int)&m_stmask;
}

inline CLightGroup *CVu1Context::GetLights()
{
	return mp_light_group;
}



/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline void CVu1Context::SetExtended(bool yes)
{
	if (yes)
	{
		m_stmask |= 1;
	}
	else
	{
		m_stmask &= ~1;
	}
}


inline bool CVu1Context::IsExtended() const
{
	return (m_stmask & 1) ? true : false;
}
										
										
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace NxPs2

#endif	// __PLAT_NGPS__

#endif	// __NGPS_NX_VU1CONTEXT_H




================================================
FILE: Code/Gfx/NGPS/PaletteGen.cpp
================================================

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 


namespace Nx
{

#define	RED	1
#define	GREEN	2
#define BLUE	3
#define ALPHA	4

#define MAXDEPTH 5
#define MAXCOLOR 256

#define ColorRealLengthSq(a)	((a)->red*(a)->red + (a)->green*(a)->green + (a)->blue*(a)->blue + (a)->alpha*(a)->alpha)
#define	ColorRealFromColorInt(o,i)										\
{                                                                        \
    (o)->red   =                                                         \
        (((float)(((i)->r))) * (   (float)((1.0/255.0))));           \
    (o)->green =                                                         \
        (((float)(((i)->g))) * ( (float)((1.0/255.0))));           \
    (o)->blue  =                                                         \
        (((float)(((i)->b))) * (  (float)((1.0/255.0))));           \
    (o)->alpha =                                                         \
        (((float)(((i)->a))) * ( (float)((1.0/255.0))));           \
}                                                                        \

#define ColorRealScale(o,a,scale)										\
{                                                                        \
    (o)->red   = (((a)->red) * (   scale));                              \
    (o)->green = (((a)->green) * ( scale));                              \
    (o)->blue  = (((a)->blue) * (  scale));                              \
    (o)->alpha = (((a)->alpha) * ( scale));                              \
}                                                                        \

#define ColorRealAdd(o,a,b)                                        \
{                                                                        \
    (o)->red   = (((a)->red) + (   (b)->red));                           \
    (o)->green = (((a)->green) + ( (b)->green));                         \
    (o)->blue  = (((a)->blue) + (  (b)->blue));                          \
    (o)->alpha = (((a)->alpha) + ( (b)->alpha));                         \
}                                                                        \

#define ColorRealSub(o,a,b)                                        \
{                                                                        \
    (o)->red   = (((a)->red) - (   (b)->red));                           \
    (o)->green = (((a)->green) - ( (b)->green));                         \
    (o)->blue  = (((a)->blue) - (  (b)->blue));                          \
    (o)->alpha = (((a)->alpha) - ( (b)->alpha));                         \
}                                                                        \

typedef uint32 OctantMap;

/* local */
static OctantMap    *p_splice = NULL;

/*************************************************************************/
static LeafNode    *
InitLeaf(LeafNode * Leaf)
{
    Dbg_Assert( Leaf );

    Leaf->palIndex = 0;
    Leaf->weight = 0.0f;
    Leaf->ac.red = 0.0f;
    Leaf->ac.green = 0.0f;
    Leaf->ac.blue = 0.0f;
    Leaf->ac.alpha = 0.0f;
    Leaf->m2 = 0.0f;
    
	return Leaf;
}

/*************************************************************************/
static BranchNode  *
InitBranch(BranchNode * Branch)
{
    int                 i;

    Dbg_Assert( Branch );

    for (i = 0; i < 16; i++)
    {
        Branch->dir[i] = (OctNode *)NULL;
    }

    return Branch;
}

/*************************************************************************/
static OctNode     *
CreateCube( void )
{
    OctNode            *cube;

	cube = (OctNode*) Mem::Calloc( 1, sizeof( OctNode ));
    return(cube);
}

/*************************************************************************/
static              OctantMap
GetOctAdr(Image::RGBA * c)
{
    int                 cs = 8 - MAXDEPTH;

    Dbg_Assert(c);

    return((p_splice[c->r >> cs] << 3) | (p_splice[c->g >> cs] << 2) |
             (p_splice[c->b >> cs] << 1) | (p_splice[c->a >> cs] << 0));
}

/*************************************************************************/
static OctNode     *
AllocateToLeaf(PalQuant * pq, OctNode * root, OctantMap Octs, int depth)
{

    Dbg_Assert(pq);
    Dbg_Assert(root);

    /* return leaf */
    if (depth == 0)
    {
        return(root);
    }

    /* populate branch */
    if (!root->Branch.dir[Octs & 15])
    {
        OctNode            *node;

        node = CreateCube();//(pq->cubefreelist);
        root->Branch.dir[Octs & 15] = node;
        if (depth == 1)
        {
            InitLeaf(&node->Leaf);
        }
        else
        {
            InitBranch(&node->Branch);
        }
    }

    return(AllocateToLeaf
             (pq, root->Branch.dir[Octs & 15], Octs >> 4, depth - 1));
}

/*************************************************************************/
void
PalQuantAddImage(PalQuant *pq, uint8 *img, int width, int height, int image_bpp, float weight)
{
    int     i, size;//, stride;
    //unsigned char		*pixels;    

    Dbg_Assert(pq);
    Dbg_Assert(img);

    //pixels = (unsigned char*) texture->buf;//GetTexelData( 0 );    
	size = width * height;

    switch (image_bpp / 8)
    {
		case 3:
		{			
#if 0
			Image::RGBA         color;
            ColorReal         rColor;
            OctNode            *leaf;
            OctantMap           Octs;

			color24* colors= (color24*)img;
			for( i = 0; i < size; i++ )
			{
				color.red = colors[i].r;
				color.green = colors[i].g;
				color.blue = colors[i].b;
				color.alpha = 255;			
			
				/* build down to leaf */
				Octs = GetOctAdr(&color);
				leaf = AllocateToLeaf(pq, pq->root, Octs, MAXDEPTH);

				ColorRealFromColorInt(&rColor, &color);
				leaf->Leaf.weight += weight;
				ColorRealScale(&rColor, &rColor, weight);
				ColorRealAdd(&leaf->Leaf.ac, &leaf->Leaf.ac,
							  &rColor);
				leaf->Leaf.m2 += weight*ColorRealLengthSq(&rColor);
			}
#endif
			Dbg_Assert(0);
			break;
		}
		case 4:
		{
			Image::RGBA         color;
            ColorReal         rColor;
            OctNode            *leaf;
            OctantMap           Octs;

			uint32* p_colors= (uint32*)img;
			for( i = 0; i < size; i++ )
			{
				color = *((Image::RGBA *) &p_colors[i]);
			
				/* build down to leaf */
				Octs = GetOctAdr(&color);
				leaf = AllocateToLeaf(pq, pq->root, Octs, MAXDEPTH);

				ColorRealFromColorInt(&rColor, &color);
				leaf->Leaf.weight += weight;
				ColorRealScale(&rColor, &rColor, weight);
				ColorRealAdd(&leaf->Leaf.ac, &leaf->Leaf.ac,
							  &rColor);
				leaf->Leaf.m2 += weight*ColorRealLengthSq(&rColor);
			}
			break;
		}

        default:
			break;
    }    
}

/*************************************************************************/

/*************************************************************************/
static void
assignindex(OctNode * root, Image::RGBA * origin, int depth, box * region,
            int palIndex)
{
    int                 width, dr, dg, db, da, dR, dG, dB, dA;
    int                 i;

    Dbg_Assert(origin);
    Dbg_Assert(region);

    if (!root)
        return;

    width = 1 << depth;

    dr = origin->r - region->col1.r;
    dg = origin->g - region->col1.g;
    db = origin->b - region->col1.b;
    da = origin->a - region->col1.a;
    if (dr >= 0 || dg >= 0 || db >= 0 || da >= 0)
    {
        return;
    }

    dR = region->col0.r - origin->r;
    dG = region->col0.g - origin->g;
    dB = region->col0.b - origin->b;
    dA = region->col0.a - origin->a;
    if (dR >= width || dG >= width || dB >= width || dA >= width)
    {
        return;
    }

    /* wholly inside region and a leaf? */
    if (dr <= -width && dg <= -width && db <= -width && da <= -width)
        if (dR <= 0 && dG <= 0 && dB <= 0 && dA <= 0)
            if (depth == 0)
            {
                root->Leaf.palIndex = (unsigned char) palIndex;
                return;
            }

    /* try children */
    depth--;
    for (i = 0; i < 16; i++)
    {
        Image::RGBA              suborigin;

        suborigin.r = origin->r + (((i >> 3) & 1) << depth);
        suborigin.g = origin->g + (((i >> 2) & 1) << depth);
        suborigin.b = origin->b + (((i >> 1) & 1) << depth);
        suborigin.a = origin->a + (((i >> 0) & 1) << depth);

        assignindex(root->Branch.dir[i], &suborigin, depth, region, palIndex);
    }    
}

/*************************************************************************/

/* Assign palIndex to leaves */
static void
nAssign(int palIndex, OctNode * root, box * cube)
{
    Image::RGBA              origin;

    Dbg_Assert(root);
    Dbg_Assert(cube);

    origin.r = 0;
    origin.g = 0;
    origin.b = 0;
    origin.a = 0;
    assignindex(root, &origin, MAXDEPTH, cube, palIndex);    
}

/*************************************************************************/
static void
addvolume(OctNode * root, Image::RGBA * origin, int depth, box * region,
          LeafNode * volume)
{
    int                 width, dr, dg, db, da, dR, dG, dB, dA;
    int                 i;

    Dbg_Assert(origin);
    Dbg_Assert(region);

    if (!root)
        return;

    width = 1 << depth;

    dr = origin->r - region->col1.r;
    dg = origin->g - region->col1.g;
    db = origin->b - region->col1.b;
    da = origin->a - region->col1.a;
    if (dr >= 0 || dg >= 0 || db >= 0 || da >= 0)
    {
        return;
    }

    dR = region->col0.r - origin->r;
    dG = region->col0.g - origin->g;
    dB = region->col0.b - origin->b;
    dA = region->col0.a - origin->a;
    if (dR >= width || dG >= width || dB >= width || dA >= width)
    {
        return;
    }

    /* wholly inside region? */
    if (dr <= -width && dg <= -width && db <= -width && da <= -width)
        if (dR <= 0 && dG <= 0 && dB <= 0 && dA <= 0)
#ifndef CACHEWEIGHTS
            if (depth == 0)    /* we need to visit each leaf */
#endif
            {
                volume->weight += root->Leaf.weight;
                ColorRealAdd(&volume->ac, &volume->ac, &root->Leaf.ac);
                volume->m2 += root->Leaf.m2;
                return;
            }

    /* try children */
    depth--;
    for (i = 0; i < 16; i++)
    {
        Image::RGBA              suborigin;

        suborigin.r = origin->r + (((i >> 3) & 1) << depth);
        suborigin.g = origin->g + (((i >> 2) & 1) << depth);
        suborigin.b = origin->b + (((i >> 1) & 1) << depth);
        suborigin.a = origin->a + (((i >> 0) & 1) << depth);

        addvolume(root->Branch.dir[i], &suborigin, depth, region, volume);
    }    
}

/*************************************************************************/

/* Compute sum over a box of any given statistic */
static LeafNode    *
nVol(LeafNode * Vol, OctNode * root, box * cube)
{
    Image::RGBA              origin;

    Dbg_Assert(root);
    Dbg_Assert(cube);

    origin.r = 0;
    origin.g = 0;
    origin.b = 0;
    origin.a = 0;
    InitLeaf(Vol);
    addvolume(root, &origin, MAXDEPTH, cube, Vol);

    return(Vol);
}

/*************************************************************************/

/* Compute the weighted variance of a box */

/* NB: as with the raw statistics, this is really the variance * size */
static              float
nVar(OctNode * root, box * cube)
{
    LeafNode            Node;

    Dbg_Assert(root);
    Dbg_Assert(cube);

    nVol(&Node, root, cube);

    return(Node.m2 - (ColorRealLengthSq(&Node.ac) / Node.weight));
}

/*************************************************************************/

/* We want to minimize the sum of the variances of two subboxes.
 * The sum(c^2) terms can be ignored since their sum over both subboxes
 * is the same (the sum for the whole box) no matter where we split.
 * The remaining terms have a minus sign in the variance formula,
 * so we drop the minus sign and MAXIMIZE the sum of the two terms.
 */
static              float
nMaximize(OctNode * root, box * cube, int dir, int * cut,
          LeafNode * whole)
{
    box                 infcube;
    LeafNode            left, right;
    float              maxsum, val, lastval;
    int             i;

    Dbg_Assert(root);
    Dbg_Assert(cube);
    Dbg_Assert(cut);
    Dbg_Assert(whole);

    lastval = maxsum = 0.0f;
    *cut = -1;
    infcube = *cube;
    switch (dir)
    {
        case RED:
            for (i = cube->col0.r; i < cube->col1.r; i++)
            {
                infcube.col1.r = (unsigned char) i;

                nVol(&left, root, &infcube);
                ColorRealSub(&right.ac, &whole->ac, &left.ac);
                right.weight = whole->weight - left.weight;
                if ((left.weight > 0.0f) && (right.weight > 0.0f))
                {
                    val = ColorRealLengthSq(&left.ac) / left.weight;
                    val += ColorRealLengthSq(&right.ac) / right.weight;

                    if (val > maxsum)
                    {
                        maxsum = val;
                        *cut = i;
                    }
                    else if (val < lastval)
                    {
                        /* we've past the peak */
                        break;
                    }

                    lastval = val;
                }
            }
            break;

        case GREEN:
            for (i = cube->col0.g; i < cube->col1.g; i++)
            {
                infcube.col1.g = (unsigned char) i;

                nVol(&left, root, &infcube);
                ColorRealSub(&right.ac, &whole->ac, &left.ac);
                right.weight = whole->weight - left.weight;
                if ((left.weight > 0.0f) && (right.weight > 0.0f))
                {
                    val = ColorRealLengthSq(&left.ac) / left.weight;
                    val += ColorRealLengthSq(&right.ac) / right.weight;

                    if (val > maxsum)
                    {
                        maxsum = val;
                        *cut = i;
                    }
                    else if (val < lastval)
                    {
                        /* we've past the peak */
                        break;
                    }

                    lastval = val;
                }
            }
            break;

        case BLUE:
            for (i = cube->col0.b; i < cube->col1.b; i++)
            {
                infcube.col1.b = (unsigned char) i;

                nVol(&left, root, &infcube);
                ColorRealSub(&right.ac, &whole->ac, &left.ac);
                right.weight = whole->weight - left.weight;
                if ((left.weight > 0.0f) && (right.weight > 0.0f))
                {
                    val = ColorRealLengthSq(&left.ac) / left.weight;
                    val += ColorRealLengthSq(&right.ac) / right.weight;

                    if (val > maxsum)
                    {
                        maxsum = val;
                        *cut = i;
                    }
                    else if (val < lastval)
                    {
                        /* we've past the peak */
                        break;
                    }

                    lastval = val;
                }
            }
            break;

        case ALPHA:
            for (i = cube->col0.a; i < cube->col1.a; i++)
            {
                infcube.col1.a = (unsigned char) i;

                nVol(&left, root, &infcube);
                ColorRealSub(&right.ac, &whole->ac, &left.ac);
                right.weight = whole->weight - left.weight;
                if ((left.weight > 0.0f) && (right.weight > 0.0f))
                {
                    val = ColorRealLengthSq(&left.ac) / left.weight;
                    val += ColorRealLengthSq(&right.ac) / right.weight;

                    if (val > maxsum)
                    {
                        maxsum = val;
                        *cut = i;
                    }
                    else if (val < lastval)
                    {
                        /* we've past the peak */
                        break;
                    }

                    lastval = val;
                }
            }
            break;
    }

    return(maxsum);
}

/*************************************************************************/
static              bool
nCut(OctNode * root, box * set1, box * set2)
{
    int             cutr, cutg, cutb, cuta;
    float              maxr, maxg, maxb, maxa;
    LeafNode            whole;

    Dbg_Assert(root);
    Dbg_Assert(set1);
    Dbg_Assert(set2);

    nVol(&whole, root, set1);
    maxr = nMaximize(root, set1, RED, &cutr, &whole);
    maxg = nMaximize(root, set1, GREEN, &cutg, &whole);
    maxb = nMaximize(root, set1, BLUE, &cutb, &whole);
    maxa = nMaximize(root, set1, ALPHA, &cuta, &whole);

    /* did we find any splits? */
    if ( (maxr == 0.0f) &&
         (maxg == 0.0f) &&
         (maxb == 0.0f) &&
         (maxa == 0.0f) )
    {
        return false;
    }

    *set2 = *set1;

    /* NEED TO CHECK FOR ALPHA TOO */

    if (maxr >= maxg)
    {
        if (maxr >= maxb)
        {
            if (maxr >= maxa)
            {
                set1->col1.r = set2->col0.r = (unsigned char) cutr;
            }
            else
            {
                set1->col1.a = set2->col0.a = (unsigned char) cuta;
            }
        }
        else
        {
            if (maxb >= maxa)
            {
                set1->col1.b = set2->col0.b = (unsigned char) cutb;
            }
            else
            {
                set1->col1.a = set2->col0.a = (unsigned char) cuta;
            }
        }
    }
    else if (maxg >= maxb)
    {
        if (maxg >= maxa)
        {
            set1->col1.g = set2->col0.g = (unsigned char) cutg;
        }
        else
        {
            set1->col1.a = set2->col0.a = (unsigned char) cuta;
        }
    }
    else if (maxb >= maxa)
    {
        set1->col1.b = set2->col0.b = (unsigned char) cutb;
    }
    else
    {
        set1->col1.a = set2->col0.a = (unsigned char) cuta;
    }


    return true;
}

/*************************************************************************/
#ifdef CACHEWEIGHTS
static LeafNode    *
CalcNodeWeights(OctNode * root, int depth)
{
    LeafNode           *Leaf;
    int                 i;

    Leaf = NULL;
    if (root)
    {
        Leaf = &root->Leaf;

        /* is it a branch? */
        if (depth > 0)
        {
            InitLeaf(Leaf);
            for (i = 0; i < 16; i++)
            {
                LeafNode           *SubNode =
                    CalcNodeWeights(root->Branch.dir[i], depth - 1);

                if (SubNode)
                {
                    Leaf->weight += SubNode->weight;
                    ColorRealAdd(&Leaf->ac, &Leaf->ac, &SubNode->ac);
                    Leaf->m2 += SubNode->m2;
                }
            }
        }
    }

    return(Leaf);
}
#endif

/*************************************************************************/
static int
CountLeafs(OctNode * root, int depth)
{
    int                 i, n;

    n = 0;
    if (root)
    {
        /* is it a branch? */
        if (depth > 0)
        {
            for (i = 0; i < 16; i++)
            {
                n += CountLeafs(root->Branch.dir[i], depth - 1);
            }
        }
        else
        {
            n = 1;
        }
    }

    return(n);
}

/*************************************************************************/
static int
CountNodes(OctNode * root, int depth)
{
    int                 i, n;

    n = 0;
    if (root)
    {
        n = 1;

        /* is it a branch? */
        if (depth > 0)
        {
            for (i = 0; i < 16; i++)
            {
                n += CountNodes(root->Branch.dir[i], depth - 1);
            }
        }
    }

    return(n);
}

/*************************************************************************/

/* 
 * Note the use of 255.9999f value when generating the scale variable.
 * This is used instead of 255.0f to prevent rounding errors.
 */

#define node2pal(rgb, node)                                              \
{                                                                        \
    int             quantize;                                        \
    float              scale = ( ((node)->weight > 0) ?                 \
                                  (255.9999f / (node)->weight) :         \
                                  (float) 0 );                          \
                                                                         \
    quantize = (int) ((node)->ac.red * scale);                       \
    (rgb)->r = (unsigned char) quantize;                                     \
    quantize = (int) ((node)->ac.green * scale);                     \
    (rgb)->g = (unsigned char) quantize;                                   \
    quantize = (int) ((node)->ac.blue * scale);                      \
    (rgb)->b = (unsigned char) quantize;                                    \
    quantize = (int) ((node)->ac.alpha * scale);                     \
    (rgb)->a = (unsigned char) quantize;                                   \
}                                                                        \

/*************************************************************************/
int
PalQuantResolvePalette(Image::RGBA * palette, int maxcols, PalQuant * pq)
{
    int             numcols, uniquecols, i, k, next;
    
#if (defined(RWDEBUG))
    if (256 < maxcols)
    {
        RWMESSAGE(("256 < %d == maxcols", (int) maxcols));
    }
#endif /* (defined(RWDEBUG)) */


    Dbg_Assert(palette);
    Dbg_Assert(maxcols <= 256);
    Dbg_Assert(pq);

    numcols = maxcols;
    uniquecols = CountLeafs(pq->root, MAXDEPTH);
    if (numcols > uniquecols)
    {
        numcols = uniquecols;
    }

#ifdef CACHEWEIGHTS
    /* cache weightings at every node */
    CalcNodeWeights(pq->root, MAXDEPTH);
#endif

    /* divide and conquer */
    pq->Mcube[0].col0.r = 0;
    pq->Mcube[0].col0.g = 0;
    pq->Mcube[0].col0.b = 0;
    pq->Mcube[0].col0.a = 0;
    pq->Mcube[0].col1.r = 1 << MAXDEPTH;
    pq->Mcube[0].col1.g = 1 << MAXDEPTH;
    pq->Mcube[0].col1.b = 1 << MAXDEPTH;
    pq->Mcube[0].col1.a = 1 << MAXDEPTH;
    pq->Mvv[0] = nVar(pq->root, &pq->Mcube[0]);
    for (i = 1; i < numcols; i++)
    {
        float               maxvar;

        /* find best box to split */
        next = -1;
        maxvar = 0.0f;
        for (k = 0; k < i; k++)
        {
            if (pq->Mvv[k] > maxvar)
            {
                maxvar = pq->Mvv[k];
                next = k;
            }
        }

        /* stop if we couldn't find a box to split */
        if (next == -1)
        {
            break;
        }

        /* split box */
        if (nCut(pq->root, &pq->Mcube[next], &pq->Mcube[i]))
        {
            /* volume test ensures we won't try to cut one-cell box */
            pq->Mvv[next] = nVar(pq->root, &pq->Mcube[next]);
            pq->Mvv[i] = nVar(pq->root, &pq->Mcube[i]);
        }
        else
        {
            /* don't try to split this box again */
            pq->Mvv[next] = 0.0f;
            i--;
        }
    }

    /* extract the new palette */
    for (k = 0; k < maxcols; k++)
    {
        if (k < numcols)
        {
            LeafNode            Node;

            nAssign(k, pq->root, &pq->Mcube[k]);
            nVol(&Node, pq->root, &pq->Mcube[k]);
            node2pal(&palette[k], &Node);
        }
        else
        {
            palette[k].r = 0;
            palette[k].g = 0;
            palette[k].b = 0;
            palette[k].a = 0;
        }
    }

    return(numcols);
}

#if 0
/*************************************************************************/
static              unsigned char
GetIndex(OctNode * root, OctantMap Octs, int depth)
{
    unsigned char             result;

    Dbg_Assert(root);

    if (depth == 0)
    {
        result = root->Leaf.palIndex;
    }
    else
    {
        result = GetIndex(root->Branch.dir[Octs & 15], Octs >> 4, depth - 1);
    }

    return(result);
}
#endif

/*************************************************************************/
bool
PalQuantInit(PalQuant * pq)
{
    int                 i, j, maxval;

    Dbg_Assert(pq);
    Dbg_Assert(!p_splice);

	p_splice = (OctantMap *) Mem::Malloc(sizeof(OctantMap) * MAXCOLOR);

    /* lookup mapping (8) bit-patterns to every 4th bit b31->b00 (least to most) */
    maxval = 1 << MAXDEPTH;
    for (i = 0; i < maxval; i++)
    {
        OctantMap           mask = 0;

        for (j = 0; j < MAXDEPTH; j++)
        {
            mask |= (i & (1 << j)) ? (1 << ((MAXDEPTH - 1 - j) * 4)) : 0;
        }
        p_splice[i] = mask;
    }

    pq->Mcube = (box *) Mem::Calloc(sizeof(box), MAXCOLOR);
    pq->Mvv = (float *) Mem::Calloc(sizeof(float), MAXCOLOR);

    pq->root = CreateCube();
    InitBranch(&pq->root->Branch);

    return true;
}

/*************************************************************************/
static void
DeleteOctTree(PalQuant * pq, OctNode * root, int depth)
{
    int                 i;

    Dbg_Assert(pq);

    if (root)
    {
        /* is it a branch? */
        if (depth > 0)
        {
            for (i = 0; i < 16; i++)
            {
                DeleteOctTree(pq, root->Branch.dir[i], depth - 1);
            }
        }

		Mem::Free(root);
    }
}

/*************************************************************************/
void
PalQuantTerm(PalQuant * pq)
{

    Dbg_Assert(pq);
    Dbg_Assert(p_splice);


    DeleteOctTree(pq, pq->root, MAXDEPTH);
    pq->root = (OctNode *)NULL;

    Mem::Free( pq->Mvv );
    Mem::Free( pq->Mcube );
	Mem::Free( p_splice );
	p_splice = NULL;
}

/*************************************************************************/
void GeneratePalette( Image::RGBA *p_palette, uint8* src_image, int width, int height, int image_bpp, int max_colors )
{
	int num_colors;
	PalQuant pal_quant;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

	if( !PalQuantInit( &pal_quant ))
	{
		return;
	}

	PalQuantAddImage( &pal_quant, src_image, width, height, image_bpp, 1 );	
	
	// Create a palette for all of the mips
	num_colors = PalQuantResolvePalette( p_palette, max_colors, &pal_quant );

	// And swizzle for the PS2
	Image::RGBA temp_rgba;
	for (int j=0; j<256; j+=32)
	{
		for (int k=0; k<8; k++)
		{
			temp_rgba = p_palette[j+k+8];
			p_palette[j+k+8] = p_palette[j+k+16];
			p_palette[j+k+16] = temp_rgba;
		}
	}

	
	PalQuantTerm( &pal_quant );

	Mem::Manager::sHandle().PopContext();
}


} // Namespace Nx  			



================================================
FILE: Code/Gfx/NGPS/PaletteGen.h
================================================
#ifndef __NX_NGPS_PALETTEGEN_H__
#define __NX_NGPS_PALETTEGEN_H__

#include 

/* caching weights at every node improves performance at expense of memory */
#define CACHEWEIGHTSx

namespace Nx
{

/****************************************************************************
 Global Types
 */

typedef struct
{
	float	red;
	float	green;
	float	blue;
	float	alpha;
} ColorReal;

typedef struct
{
	Image::RGBA col0;	/* min value, inclusive */
	Image::RGBA col1;	/* max value, exclusive */
} box;



#ifdef CACHEWEIGHTS
typedef struct OctNode OctNode;
#else
typedef union OctNode OctNode;
#endif

typedef struct LeafNode LeafNode;
struct LeafNode
{
	float weight;
	ColorReal ac;
	float m2;
	unsigned char palIndex;
};

typedef struct BranchNode BranchNode;
struct BranchNode
{
	OctNode *dir[16];
};

#ifdef CACHEWEIGHTS
struct OctNode
#else
union OctNode
#endif
{
	LeafNode   Leaf;
	BranchNode Branch;
};

/* working data */
typedef struct
{
	box *Mcube;
	float *Mvv;
	OctNode *root;	
}  PalQuant;

/****************************************************************************
 Function prototypes
 */

bool PalQuantInit(PalQuant *pq);
void PalQuantTerm(PalQuant *pq);

void PalQuantAddImage(PalQuant *pq, uint8 *img, int width, int height, int image_bpp, float weight);
int PalQuantResolvePalette(unsigned char *palette, int maxcols, PalQuant *pq, int pbpp);
void GeneratePalette( Image::RGBA *p_palette, uint8* src_image, int width, int height, int image_bpp, int max_colors );


} // Namespace Nx  			

#endif	// __NX_NGPS_PALETTEGEN_H__


================================================
FILE: Code/Gfx/NGPS/p_NxAnimCache.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_NxAnimCache.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  5/06/2002
//****************************************************************************

#include 

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2AnimCache::CPs2AnimCache( int lookupTableSize ) : CAnimCache( lookupTableSize )
{
	// Machine specific code here ............
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2AnimCache::~CPs2AnimCache()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx
				


================================================
FILE: Code/Gfx/NGPS/p_NxAnimCache.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_NxAnimCache.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  5/06/2002
//****************************************************************************

#ifndef	__GFX_P_NXANIMCACHE_H__
#define	__GFX_P_NXANIMCACHE_H__
    
#include 

namespace Nx
{
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CGeom
    
class CPs2AnimCache : public CAnimCache
{
                                      
public:
						CPs2AnimCache( int lookupTableSize );
	virtual 			~CPs2AnimCache();

private:				// It's all private, as it is machine specific
};

} // Nx

#endif 


================================================
FILE: Code/Gfx/NGPS/p_NxFont.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxFont.cpp

#include 	"Gfx/NGPS/p_NxFont.h"
#include 	"Gfx/NGPS/p_NxWin2D.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CFont

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Font::CPs2Font() : mp_plat_font(NULL)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Font::~CPs2Font()
{
	if (mp_plat_font)
	{
		plat_unload();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CPs2Font::plat_load(const char *filename)
{
	mp_plat_font = NxPs2::LoadFont(filename);

	return (mp_plat_font != NULL);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Font::plat_set_spacings(int charSpacing, int spaceSpacing)
{
	mp_plat_font->mCharSpacing = charSpacing;
	if (spaceSpacing > 0)
		mp_plat_font->mSpaceSpacing = spaceSpacing;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Font::plat_set_rgba_table(Image::RGBA *pTab)
{
	for (int i = 0; i < 16; i++)
		mp_plat_font->mRGBATab[i] = *((uint32 *) &pTab[i]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Font::plat_mark_as_button_font(bool isButton)
{
	NxPs2::pButtonsFont = (isButton) ? mp_plat_font : NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Font::plat_unload()
{
	NxPs2::UnloadFont(mp_plat_font);
	mp_plat_font = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32		CPs2Font::plat_get_default_height() const
{
	Dbg_Assert(mp_plat_font);

	return mp_plat_font->GetDefaultHeight();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32		CPs2Font::plat_get_default_base() const
{
	Dbg_Assert(mp_plat_font);

	return mp_plat_font->GetDefaultBase();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Font::plat_query_string(char *String, float &width, float &height) const
{
	Dbg_Assert(mp_plat_font);

	mp_plat_font->QueryString(String, width, height);
}

/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CText

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Text::CPs2Text(CWindow2D *p_window) : CText(p_window)
{
	mp_plat_text = new NxPs2::SText();

	plat_initialize();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Text::~CPs2Text()
{
	if (mp_plat_text)
	{
		delete mp_plat_text;
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Text::plat_initialize()
{
	plat_update_engine();
	plat_update_priority();
	plat_update_hidden();
	plat_update_window();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Text::plat_update_hidden()
{
	mp_plat_text->SetHidden(m_hidden);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Text::plat_update_engine()
{
	CPs2Font *p_ps2_font = static_cast(mp_font);

	mp_plat_text->mp_string	= m_string;
	if (p_ps2_font) {
		mp_plat_text->mp_font	= p_ps2_font->GetEngineFont();
	}

	mp_plat_text->m_xpos	= m_xpos;
	mp_plat_text->m_ypos	= m_ypos;
	mp_plat_text->m_xscale	= m_xscale;
	mp_plat_text->m_yscale	= m_yscale;
	mp_plat_text->m_rgba	= *((uint32 *) &m_rgba);
	mp_plat_text->m_color_override = m_color_override;
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Text::plat_update_priority()
{
	// Update draw list
	if (m_use_zbuffer)
	{
		mp_plat_text->SetZValue((uint32) m_zvalue);
	} else {
		mp_plat_text->SetPriority(m_priority);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Text::plat_update_window()
{
	CPs2Window2D *p_ps2_window = static_cast(mp_window);

	if (p_ps2_window)
	{
		mp_plat_text->SetScissorWindow(p_ps2_window->GetEngineWindow());
	} else {
		mp_plat_text->SetScissorWindow(NULL);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CTextMan::s_plat_alloc_text_pool()
{
   	CPs2Text *p_text_array = new CPs2Text[vMAX_TEXT_INSTANCES];

	for (int i = 0; i < vMAX_TEXT_INSTANCES; i++)
	{
//	   	CPs2Text *p_text = new CPs2Text(NULL);
	   	CPs2Text *p_text = &p_text_array[i];
		p_text->mp_next = sp_dynamic_text_list;
		sp_dynamic_text_list = p_text;
	}
}

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/NGPS/p_NxFont.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxFont.h

#ifndef	__GFX_P_NX_FONT_H__
#define	__GFX_P_NX_FONT_H__

#include 	"Gfx/NxFont.h"
#include 	"Gfx/NGPS/NX/chars.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CFont
class	CPs2Font : public CFont
{
public:
								CPs2Font();
	virtual						~CPs2Font();

	NxPs2::SFont *				GetEngineFont() const;

private:		// It's all private, as it is machine specific
	virtual	bool				plat_load(const char *filename);
	virtual void				plat_set_spacings(int charSpacing, int spaceSpacing);
	virtual void				plat_set_rgba_table(Image::RGBA *pTab);
	virtual void 				plat_mark_as_button_font(bool isButton);
	virtual void				plat_unload();

	virtual	uint32				plat_get_default_height() const;
	virtual	uint32				plat_get_default_base() const;
	virtual void				plat_query_string(char *String, float &width, float &height) const;

	// Machine specific members
	NxPs2::SFont *				mp_plat_font;		// Pointer to engine font
};

/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CText
class	CPs2Text : public CText
{
public:
								CPs2Text(CWindow2D *p_window = NULL);
	virtual						~CPs2Text();

private:
	//
	virtual void				plat_initialize();

	virtual void				plat_update_hidden();		// Tell engine of update
	virtual void				plat_update_engine();		// Update engine primitives
	virtual void				plat_update_priority();
	virtual void				plat_update_window();

	// Machine specific members
	NxPs2::SText *				mp_plat_text;		// Pointer to engine text
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline NxPs2::SFont *		CPs2Font::GetEngineFont() const
{
	return mp_plat_font;
}

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/NGPS/p_NxFontMan.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxFontMan.cpp - PS2 platform specific interface to the Font Manager
//
// This is PS2 SPECIFIC!!!!!!  So might get a bit messy
//

#include	"gfx\nx.h"
#include	"gfx\NxFontMan.h"
#include	"gfx\NGPS\p_NxFont.h"

#include 	"gfx\ngps\nx\chars.h"

namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CFont	*	CFontManager::s_plat_load_font(const char *pName)
{
	CPs2Font *p_new_font;

	p_new_font = new CPs2Font;
	p_new_font->Load(pName);

	return p_new_font;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFontManager::s_plat_unload_font(CFont *pFont)
{
	pFont->Unload();
}

} 
 


================================================
FILE: Code/Gfx/NGPS/p_NxGeom.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxGeom.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  3/4/2002
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
								
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Geom::CPs2Geom()
{
	// Machine specific code here ............
	mp_oldInstance = NULL;
	mp_instance = NULL;
	mp_matrices = NULL;
	m_rootMatrix.Ident();	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Geom::~CPs2Geom()
{
	if ( mp_oldInstance != NULL )
	{
		delete mp_oldInstance;
		mp_oldInstance = NULL;
	}

	if ( mp_instance != NULL )
	{
		if ( m_cloned == vINSTANCE )
		{
			mp_instance->DeleteInstance();
		}
		else if ( m_cloned == vCOPY )
		{
			mp_instance->DeleteCopy();
		}
		mp_instance = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Geom::plat_load_geom_data(CMesh* pMesh, CModel* pModel, bool color_per_material)
{
	int numBones;
	numBones = pModel->GetNumBones();

	Mth::Matrix temp;
	temp.Identity();
	Dbg_Assert( mp_oldInstance == NULL );

	// attempting to move skins over to .geom.ps2
	mp_matrices = ((CPs2Model*)pModel)->GetMatrices();
    if ( ((CPs2Mesh*)pMesh)->GetGeomNode() )
	{
		if ( numBones )
		{
			mp_instance = ((CPs2Mesh*)pMesh)->GetGeomNode()->CreateInstance( &m_rootMatrix, numBones, (Mth::Matrix *)mp_matrices );
		}
		else
		{
			mp_instance = ((CPs2Mesh*)pMesh)->GetGeomNode()->CreateInstance( &m_rootMatrix );
		}
	}
	else
	{
		Dbg_Assert( pMesh );
		Dbg_Assert( pMesh->GetTextureDictionary() );
		NxPs2::sScene* pScene = ((Nx::CPs2TexDict*)pMesh->GetTextureDictionary())->GetEngineTextureDictionary();
		Dbg_Assert( pScene );

		if ( numBones )
		{
			mp_oldInstance = new NxPs2::CInstance(pScene, temp, color_per_material, numBones, mp_matrices );
		}
		else
		{
			mp_oldInstance = new NxPs2::CInstance(pScene, temp, color_per_material);
		}
	}

	// remember the source mesh, so that we can do poly-hiding on it
	mp_sourceMesh = (CPs2Mesh*)pMesh;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_finalize()
{
	if (mp_oldInstance)
	{
		// only supported on old-style instances
		mp_oldInstance->SqueezeADC();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom *	CPs2Geom::plat_clone(bool instance, CScene* pDestScene)
{
	Dbg_MsgAssert(mp_oldInstance == NULL, ("Wrong version of CPs2Geom::plat_clone() for CInstances"));
	Dbg_MsgAssert(mp_matrices == NULL, ("Wrong version of CPs2Geom::plat_clone() for matrix arrays"));

	CPs2Scene *p_ps2_dest_scene = static_cast(pDestScene);
	NxPs2::CGeomNode *p_scene_geom = (p_ps2_dest_scene) ? p_ps2_dest_scene->GetEngineCloneScene() : NULL;

	// Copy into new sector
	CPs2Geom *p_new_geom = new CPs2Geom(*this);

	// Copy engine data
	if (p_new_geom && mp_instance)
	{
		if (instance)
		{
			p_new_geom->mp_instance = mp_instance->CreateInstance(&p_new_geom->m_rootMatrix, p_scene_geom);
		}
		else
		{
			p_new_geom->mp_instance = mp_instance->CreateCopy(p_scene_geom);
			//p_new_geom->mp_instance->SetMatrix(&p_new_geom->m_rootMatrix);
		}
	}

	return p_new_geom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom *	CPs2Geom::plat_clone(bool instance, CModel* pDestModel)
{
	Dbg_MsgAssert(mp_instance == NULL, ("Wrong version of CPs2Geom::plat_clone() for CGeomNodes"));
	Dbg_Assert(mp_oldInstance);

	// Copy into new geom
	CPs2Geom *p_new_geom = new CPs2Geom(*this);

	Dbg_Assert(p_new_geom);

	int numBones;
	numBones = pDestModel->GetNumBones();

	Mth::Matrix temp;
	temp.Identity();

	p_new_geom->mp_matrices = ((CPs2Model*)pDestModel)->GetMatrices();

	NxPs2::sScene* pScene = mp_oldInstance->GetScene();
	Dbg_Assert( pScene );

	if ( numBones )
	{
		p_new_geom->mp_oldInstance = new NxPs2::CInstance(pScene, temp, mp_oldInstance->HasColorPerMaterial(), numBones, p_new_geom->mp_matrices );
	}
	else
	{
		p_new_geom->mp_oldInstance = new NxPs2::CInstance(pScene, temp, mp_oldInstance->HasColorPerMaterial());
	}

	Dbg_Assert(p_new_geom->mp_oldInstance);

	// Copy the colors from instance
	if (mp_oldInstance->HasColorPerMaterial())
	{
		int num_colors = mp_oldInstance->GetScene()->NumMeshes;
		for (int i = 0; i < num_colors; i++)
		{
			p_new_geom->mp_oldInstance->SetMaterialColorByIndex(i, mp_oldInstance->GetMaterialColorByIndex(i));
		}
	}
	else
	{
		p_new_geom->mp_oldInstance->SetColor(mp_oldInstance->GetColor());
	}

	return p_new_geom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CPs2Geom::plat_get_checksum()
{
	Dbg_Assert(mp_instance);

	return mp_instance->GetChecksum();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_color(Image::RGBA rgba)
{
	// Engine call here
	if ( mp_instance )
	{
		mp_instance->SetColored(true);
//		printf( "Color 0x%x\n", *((uint32 *) &rgba ));
		mp_instance->SetColor(*((uint32 *) &rgba));
	}
	else if (mp_oldInstance)
	{
		if (mp_oldInstance->HasColorPerMaterial())
		{
			for (int i = 0; i < mp_oldInstance->GetScene()->NumMeshes; i++)
			{
				mp_oldInstance->SetMaterialColorByIndex(i, *((uint32 *) &rgba));
			}
		}
		else
		{
			mp_oldInstance->SetColor(*((uint32 *) &rgba));
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA CPs2Geom::plat_get_color() const
{
	// Engine call here
	if ( mp_instance && mp_instance->IsColored() )
	{
		uint32 raw_data = mp_instance->GetColor();
		return *((Image::RGBA *) &raw_data);
	}
	else if (mp_oldInstance)
	{
		uint32 raw_data = mp_oldInstance->GetColor();
		return *((Image::RGBA *) &raw_data);
	}
	else
	{
		return Image::RGBA(128, 128, 128, 128);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_clear_color()
{
	// Engine call here
	if ( mp_instance )
	{
		mp_instance->SetColored(false);
	} else {
		// Set to white
		plat_set_color(Image::RGBA(128, 128, 128, 128));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CPs2Geom::plat_set_material_color(uint32 mat_checksum, int pass, Image::RGBA rgba)
{
	if (mp_oldInstance)
	{
		if (mp_oldInstance->HasColorPerMaterial())
		{
			mp_oldInstance->SetMaterialColor(mat_checksum, pass, *((uint32 *) &rgba));
		}
		else
		{
//			Dbg_Message( "%s %d doesn't have multicolor set", Script::FindChecksumName(mat_checksum), pass );
		}
		return true;
	}
	else
	{
		Dbg_MsgAssert(0, ("Trying to set the material color on a Geom that doesn't support it"));
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA	CPs2Geom::plat_get_material_color(uint32 mat_checksum, int pass)
{
	if (mp_oldInstance && mp_oldInstance->HasColorPerMaterial())
	{
		uint32 raw_data = mp_oldInstance->GetMaterialColor(mat_checksum, pass);
		return *((Image::RGBA *) &raw_data);
	}
	else
	{
		Dbg_MsgAssert(0, ("Trying to get the material color on a Geom that doesn't support it"));
		return Image::RGBA(128, 128, 128, 128);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_visibility(uint32 mask)
{
	// Engine call here
	if (mp_instance)
	{
		mp_instance->SetVisibility(mask & 0xFF);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CPs2Geom::plat_get_visibility() const
{
	// Engine call here
	if ( mp_instance )
	{
		return mp_instance->GetVisibility() | 0xFFFFFF00;		// To keep format compatible
	}
	else
	{
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_active( bool active )
{
	if ( mp_oldInstance )
	{
		if ( active != mp_oldInstance->IsActive() )
		{
			mp_oldInstance->SetActive( active );
		}
	}

	// New engine
	if ( mp_instance )
	{
		if ( active != mp_instance->IsActive() )
		{
			mp_instance->SetActive( active );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Geom::plat_is_active() const
{
	if ( mp_instance )
	{
		return mp_instance->IsActive();
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::CBBox & CPs2Geom::plat_get_bounding_box() const
{
#if 0	// Enable this after THPS4 and do something about needing a static
	if ( mp_instance )
	{
		static Mth::CBBox bbox;
		bbox = mp_instance->GetBoundingBox();
		return bbox;
	} 
	else
#endif
	{
		// Garrett: TODO: change to renderable bounding box
		Dbg_Assert(mp_coll_tri_data);
		return mp_coll_tri_data->GetBBox();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector CPs2Geom::plat_get_bounding_sphere() const
{
	if ( mp_instance )
	{
		// CGeomNode-style
		return mp_instance->GetBoundingSphere();
	}
	else if ( mp_oldInstance )
	{
		// CInstance-style
		return mp_oldInstance->GetBoundingSphere();
	}
	else
	{
		return Mth::Vector(0.0f, 0.0f, 0.0f, 1.0e+10f);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_bounding_sphere( const Mth::Vector& boundingSphere )
{
	if ( mp_instance )
	{
		// CGeomNode-style
		mp_instance->SetBoundingSphere( boundingSphere[X], boundingSphere[Y], boundingSphere[Z], boundingSphere[W] );
	}
	else if ( mp_oldInstance )
	{
		// CInstance-style
		mp_oldInstance->SetBoundingSphere( boundingSphere[X], boundingSphere[Y], boundingSphere[Z], boundingSphere[W] );
	}
	else
	{
		Dbg_MsgAssert( 0, ( "Shouldn't get here" ) );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_world_position(const Mth::Vector& pos)
{
	// Garrett: we may need to do this in integer format instead to make it lossless
	Mth::Vector delta_pos(pos - m_rootMatrix[W]);

	// Set values
	m_rootMatrix[W][X] = pos[X];
	m_rootMatrix[W][Y] = pos[Y];
	m_rootMatrix[W][Z] = pos[Z];	
	//m_rootMatrix[W][W] = 1.0f;

	// Engine call here
	if (m_cloned != vCOPY)
	{
		update_engine_matrix();
	} else {
		mp_instance->Translate(delta_pos);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &	CPs2Geom::plat_get_world_position() const
{
	return m_rootMatrix[W];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_orientation(const Mth::Matrix& orient)
{
	// Set values
	m_rootMatrix[X] = orient[X];
	m_rootMatrix[Y] = orient[Y];
	m_rootMatrix[Z] = orient[Z];

	// Engine call here
	if (m_cloned != vCOPY)
	{
		update_engine_matrix();
	} else {
		Dbg_MsgAssert(0, ("Don't call SetOrientation() on a CGeom copy"));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Matrix &	CPs2Geom::plat_get_orientation() const
{
	Dbg_MsgAssert(0, ("CPs2Geom::plat_get_orientation() not implemented yet"));
	return m_rootMatrix;			// This is not correct
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_rotate_y(Mth::ERot90 rot)
{
	// Engine call here
	// Garrett: TEMP just set the world matrix
	Mth::Matrix orig_rot_mat(m_rootMatrix), rot_mat;
	float rad = (float) ((int) rot) * (Mth::PI * 0.5f);
	CreateRotateYMatrix(rot_mat, rad);

	orig_rot_mat[W] = Mth::Vector(0.0f, 0.0f, 0.0f, 1.0f);

	rot_mat = orig_rot_mat * rot_mat;
	//Dbg_Message("[X] (%f, %f, %f, %f)", rot_mat[X][X], rot_mat[X][Y],  rot_mat[X][Z],  rot_mat[X][W]);
	//Dbg_Message("[Y] (%f, %f, %f, %f)", rot_mat[Y][X], rot_mat[Y][Y],  rot_mat[Y][Z],  rot_mat[Y][W]);
	//Dbg_Message("[Z] (%f, %f, %f, %f)", rot_mat[Z][X], rot_mat[Z][Y],  rot_mat[Z][Z],  rot_mat[Z][W]);
	//Dbg_Message("[W] (%f, %f, %f, %f)", rot_mat[W][X], rot_mat[W][Y],  rot_mat[W][Z],  rot_mat[W][W]);
	
	m_rootMatrix[X] = rot_mat[X];
	m_rootMatrix[Y] = rot_mat[Y];
	m_rootMatrix[Z] = rot_mat[Z];

	if (m_cloned != vCOPY)
	{
		update_engine_matrix();
	}
	else
	{
		// Garrett: We may need to change the world pos to integer to avoid float losses
		mp_instance->RotateY(m_rootMatrix[W], rot);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_transform(const Mth::Matrix& transform)
{
	// Set values
	m_rootMatrix = transform;

	// Engine call here
	if (m_cloned != vCOPY)
	{
		update_engine_matrix();
	}
	else
	{
		Dbg_MsgAssert(0, ("Don't call SetTransform() on a CGeom copy"));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Matrix &	CPs2Geom::plat_get_transform() const
{
	return m_rootMatrix;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_scale(const Mth::Vector & scale)
{
	// Engine call here
	Mth::Matrix orig_rot_mat(m_rootMatrix);

	// orientation
	m_rootMatrix[X] *= scale[X];
	m_rootMatrix[Y] *= scale[Y];
	m_rootMatrix[Z] *= scale[Z];

#if 0		// Don't do this now since it is a local space scale
	// position (since this is what will happen in the copy geometry)
	m_rootMatrix[Mth::POS][X] *= scale[X];
	m_rootMatrix[Mth::POS][Y] *= scale[Y];
	m_rootMatrix[Mth::POS][Z] *= scale[Z];
#endif

	if (m_cloned != vCOPY)
	{
		update_engine_matrix();
	}
	else
	{
		Mth::Vector delta_scale;

		delta_scale[X] = scale[X] /  orig_rot_mat[X].Length();
		delta_scale[Y] = scale[Y] /  orig_rot_mat[Y].Length();
		delta_scale[Z] = scale[Z] /  orig_rot_mat[Z].Length();

		mp_instance->Scale(m_rootMatrix[W], delta_scale);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Mth::Vector CPs2Geom::plat_get_scale() const
{
	Mth::Vector scale;

	scale[X] = m_rootMatrix[X].Length();
	scale[Y] = m_rootMatrix[Y].Length();
	scale[Z] = m_rootMatrix[Z].Length();

	return scale;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::update_engine_matrix()
{
	if (m_cloned != vCOPY)
	{
		mp_instance->SetMatrix(&m_rootMatrix);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// not needed anymore
#if 0
void ConvertMatrix(Mth::Matrix* pMatrix, Mth::Matrix* pMat)
{
	/*
	// right
	(*pMat)[0][0] = (*pMatrix)[0][0];
	(*pMat)[0][1] = (*pMatrix)[0][1];
	(*pMat)[0][2] = (*pMatrix)[0][2];
	(*pMat)[0][3] = (*pMatrix)[0][3];

	// up
	(*pMat)[1][0] = (*pMatrix)[1][0];
	(*pMat)[1][1] = (*pMatrix)[1][1];
	(*pMat)[1][2] = (*pMatrix)[1][2];
	(*pMat)[1][3] = (*pMatrix)[1][3];

	// at
	(*pMat)[2][0] = (*pMatrix)[2][0];
	(*pMat)[2][1] = (*pMatrix)[2][1];
	(*pMat)[2][2] = (*pMatrix)[2][2];
	(*pMat)[2][3] = (*pMatrix)[2][3];

	// position
	(*pMat)[3][0] = (*pMatrix)[3][0];
	(*pMat)[3][1] = (*pMatrix)[3][1];
	(*pMat)[3][2] = (*pMatrix)[3][2];
	(*pMat)[3][3] = (*pMatrix)[3][3];

	*/
	
	// the copy operator is more efficient
	// (on PS2, does 4 quad copies.  Other platform does the same as above
	*pMat  = *pMatrix;				 

	// clear out the final column
	(*pMat)[0][3] = 0.0f;//(*pMatrix)[3][0];
	(*pMat)[1][3] = 0.0f;//(*pMatrix)[3][0];
	(*pMat)[2][3] = 0.0f;//(*pMatrix)[3][0];
	(*pMat)[3][3] = 1.0f;//(*pMatrix)[3][0];
		
	#if 0
	// right
	(*pMat)[0][0] = 1.0f;//(*pMatrix)[0][0];
	(*pMat)[0][1] = 0.0f;//(*pMatrix)[1][0];
	(*pMat)[0][2] = 0.0f;//(*pMatrix)[2][0];
	(*pMat)[0][3] = 0.0f;//(*pMatrix)[3][0];

	// up
	(*pMat)[1][0] = 0.0f;//(*pMatrix)[0][1];
	(*pMat)[1][1] = 1.0f;//(*pMatrix)[1][1];
	(*pMat)[1][2] = 0.0f;//(*pMatrix)[2][1];
	(*pMat)[1][3] = 0.0f;//(*pMatrix)[3][1];

	// at
	(*pMat)[2][0] = 0.0f;//(*pMatrix)[0][2];
	(*pMat)[2][1] = 0.0f;//(*pMatrix)[1][2];
	(*pMat)[2][2] = 1.0f;//(*pMatrix)[2][2];
	(*pMat)[2][3] = 0.0f;//(*pMatrix)[3][2];

	// position
//	(*pMat)[3][0] = (*pMatrix)[3][0];
//	(*pMat)[3][1] = (*pMatrix)[3][1];
//	(*pMat)[3][2] = (*pMatrix)[3][2];
//	(*pMat)[3][3] = 1.0f;//(*pMatrix)[3][3];
	#endif
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Geom::plat_render(Mth::Matrix* pRootMatrix, Mth::Matrix* pBoneMatrices, int numBones)
{
	// implies it's got a skin
	if ( mp_oldInstance )
	{
		Dbg_Assert( mp_oldInstance );
		
		if ( numBones > 0 )
		{
			mp_oldInstance->SetBoneTransforms( pBoneMatrices ); 
		}

		mp_oldInstance->SetTransform(*pRootMatrix);
	
		Dbg_MsgAssert( numBones <= mp_oldInstance->GetNumBones(), ( "Bone mismatch Trying to render %d bones, but model was initialized with %d bones", 
																	numBones, 
																	mp_oldInstance->GetNumBones() ) );
	}

	if ( mp_instance )
	{
		m_rootMatrix = *pRootMatrix;

		if ( numBones )
		{
			mp_instance->SetBoneTransforms( pBoneMatrices );
		}
		
		Dbg_MsgAssert( numBones <= mp_instance->GetNumBones(), ( "Bone mismatch Trying to render %d bones, but model was initialized with %d bones", 
																	numBones, 
																	mp_instance->GetNumBones() ) );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Geom::plat_hide_polys( uint32 mask )
{
	if ( mp_instance )
	{
	// Only supporting old style geom for now
	//	mp_instance->HidePolys( mask );
	}
	else
	{
		Dbg_Assert( mp_sourceMesh );
		NxPs2::CGeomNode* pGeomNode = mp_sourceMesh->GetGeomNode();
		if ( pGeomNode )
		{
		// Only supporting old style geom for now
		//	pGeomNode->HidePolys( mask );
		}
		else
		{
			mp_sourceMesh->HidePolys( mask );
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Geom::plat_enable_shadow( bool enabled )
{
	if ( mp_instance )
	{
		// only supporting old style geom for now
	}
	else
	{
		mp_oldInstance->EnableShadow( enabled );
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_model_lights(CModelLights *p_model_lights)
{
	NxPs2::CLightGroup *p_light_group;
	if (p_model_lights)
	{
		CPs2ModelLights *p_ps2_model_lights;
		p_ps2_model_lights = static_cast(p_model_lights);
		p_light_group = p_ps2_model_lights->GetEngineLightGroup();
	} else {
		p_light_group = NULL;
	}


	// Model lights only work on CGeomNodes
	if (mp_instance)
	{
		Dbg_Assert(!mp_instance->IsLeaf());
		mp_instance->SetLightGroup(p_light_group);
	}
	else if (mp_oldInstance)
	{
		mp_oldInstance->SetLightGroup(p_light_group);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CPs2Geom::plat_get_num_render_verts()
{
	Dbg_Assert(mp_instance);

	return mp_instance->GetNumVerts();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_get_render_verts(Mth::Vector *p_verts)
{
	Dbg_Assert(mp_instance);

	mp_instance->GetVerts(p_verts);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CPs2Geom::plat_get_num_render_polys()								
{
	return mp_instance->GetNumPolys();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CPs2Geom::plat_get_num_render_base_polys()								
{
	return mp_instance->GetNumBasePolys();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_get_render_colors(Image::RGBA *p_colors)
{
	Dbg_Assert(mp_instance);

	mp_instance->GetColors((uint32 *) p_colors);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_render_verts(Mth::Vector *p_verts)
{
	Dbg_Assert(mp_instance);

	mp_instance->SetVerts(p_verts);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_render_colors(Image::RGBA *p_colors)
{
	Dbg_Assert(mp_instance);

	mp_instance->SetColors((uint32 *) p_colors);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_uv_wibble_params(float u_vel, float u_amp, float u_freq, float u_phase,
										 float v_vel, float v_amp, float v_freq, float v_phase)
{
	Dbg_Assert(mp_instance);

	NxPs2::CGeomNode *p_node = mp_instance;

	// Get leaf
	while (!p_node->IsLeaf())
	{
		p_node = p_node->GetChild();
	}

	// Do on all siblings
	while (p_node)
	{
		if (p_node->IsUVWibbled())
		{
			p_node->SetUVWibbleParams(u_vel, u_amp, u_freq, u_phase, v_vel, v_amp, v_freq, v_phase);
		}

		p_node = p_node->GetSibling();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_use_explicit_uv_wibble(bool yes)
{
	Dbg_Assert(mp_instance);

	NxPs2::CGeomNode *p_node = mp_instance;

	// Get leaf
	while (!p_node->IsLeaf())
	{
		p_node = p_node->GetChild();
	}

	// Do on all siblings
	while (p_node)
	{
		if (p_node->IsUVWibbled())
		{
			p_node->UseExplicitUVWibble(yes);
		}

		p_node = p_node->GetSibling();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Geom::plat_set_uv_wibble_offsets(float u_offset, float v_offset)
{
	Dbg_Assert(mp_instance);

	NxPs2::CGeomNode *p_node = mp_instance;

	// Get leaf
	while (!p_node->IsLeaf())
	{
		p_node = p_node->GetChild();
	}

	// Do on all siblings
	while (p_node)
	{
		if (p_node->IsUVWibbled())
		{
			p_node->SetUVWibbleOffsets(u_offset, v_offset);
			// If we are setting the offsets, then we should want to use the explicit mode
			p_node->UseExplicitUVWibble(true);
		}

		p_node = p_node->GetSibling();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Geom::plat_set_uv_wibble_offsets(uint32 mat_checksum, int pass, float u_offset, float v_offset)
{
	if (mp_oldInstance)
	{
		mp_oldInstance->SetUVOffset(mat_checksum, pass, u_offset, v_offset);
		return true;
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't set the UV offset with material checksum on CGeomNode"));
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Geom::plat_set_uv_matrix(uint32 mat_checksum, int pass, const Mth::Matrix &mat)
{
	if (mp_oldInstance)
	{
		mp_oldInstance->SetUVMatrix(mat_checksum, pass, mat);
		return true;
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't set the UV matrix with material checksum on CGeomNode"));
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx
				


================================================
FILE: Code/Gfx/NGPS/p_NxGeom.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxGeom.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  3/5/2002
//****************************************************************************

#ifndef	__GFX_P_NX_GEOM_H__
#define	__GFX_P_NX_GEOM_H__
    
#include "core/math.h"

#include "gfx/nxgeom.h"

namespace NxPs2
{
	class CInstance;
	class CGeomNode;
}
						   
namespace Nx
{
	class CPs2Mesh;
	class CModelLights;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CGeom
    
class CPs2Geom : public CGeom
{
                                      
public:
						CPs2Geom();
	virtual 			~CPs2Geom();

	void				SetEngineObject(NxPs2::CGeomNode *p_object);
	NxPs2::CGeomNode *	GetEngineObject() const;

	Mth::Vector			GetBoundingSphere();


private:				// It's all private, as it is machine specific
	virtual CGeom *		plat_clone(bool instance, CScene* pDestScene=NULL);
	virtual CGeom *		plat_clone(bool instance, CModel* pDestModel);

	virtual	bool		plat_load_geom_data(CMesh* pMesh, CModel* pModel, bool color_per_material);
	virtual void		plat_finalize();

	virtual uint32		plat_get_checksum();

	virtual	void		plat_set_color(Image::RGBA rgba);
	virtual	Image::RGBA	plat_get_color() const;
	virtual	void		plat_clear_color();

	virtual bool		plat_set_material_color(uint32 mat_checksum, int pass, Image::RGBA rgba);
	virtual Image::RGBA	plat_get_material_color(uint32 mat_checksum, int pass);

	virtual	void		plat_set_visibility(uint32 mask);
	virtual	uint32		plat_get_visibility() const;

	virtual	void		plat_set_active(bool active);
	virtual	bool		plat_is_active() const;

	virtual const Mth::CBBox &	plat_get_bounding_box() const;

	virtual void	plat_set_bounding_sphere( const Mth::Vector& boundingSphere );
	virtual const Mth::Vector	plat_get_bounding_sphere() const;

	virtual void		plat_set_world_position(const Mth::Vector& pos);
	virtual const Mth::Vector &	plat_get_world_position() const;

	virtual void		plat_set_orientation(const Mth::Matrix& orient);
	virtual const Mth::Matrix &	plat_get_orientation() const;

	virtual void 		plat_rotate_y(Mth::ERot90 rot);

	virtual void		plat_set_transform(const Mth::Matrix& transform);
	virtual const Mth::Matrix &	plat_get_transform() const;

	virtual void		plat_set_scale(const Mth::Vector& scale);
	virtual Mth::Vector plat_get_scale() const;

	virtual void		plat_set_model_lights(CModelLights *);

	virtual bool		plat_render(Mth::Matrix* pRootMatrix, Mth::Matrix* ppBoneMatrices, int numBones);
	virtual bool		plat_hide_polys( uint32 mask );
	virtual bool		plat_enable_shadow( bool enabled );

	virtual	int 		plat_get_num_render_verts();								// - returns number of renderable verts
	virtual	int 		plat_get_num_render_polys();
	virtual	int 		plat_get_num_render_base_polys();
	virtual	void 		plat_get_render_verts(Mth::Vector *p_verts);				// - gets a single array of the verts
	virtual	void 		plat_get_render_colors(Image::RGBA *p_colors);				// - gets an array of vertex colors
	virtual void 		plat_set_render_verts(Mth::Vector *p_verts);				// - sets the verts after modification
	virtual	void 		plat_set_render_colors(Image::RGBA *p_colors);				// - sets the colors after modification

	// Wibble functions
	virtual void		plat_set_uv_wibble_params(float u_vel, float u_amp, float u_freq, float u_phase,
												  float v_vel, float v_amp, float v_freq, float v_phase);
	virtual void		plat_use_explicit_uv_wibble(bool yes);
	virtual void		plat_set_uv_wibble_offsets(float u_offset, float v_offset);
	virtual bool		plat_set_uv_wibble_offsets(uint32 mat_checksum, int pass, float u_offset, float v_offset);
	virtual bool		plat_set_uv_matrix(uint32 mat_checksum, int pass, const Mth::Matrix &mat);

	// local functions
	void 				update_engine_matrix();

private:
	// old format
	NxPs2::CInstance*	mp_oldInstance;
	
	// access to the scene means access to the polys for hiding purposes
	// this code will all be thrown away once we convert to the new geomnode
	// stuff anyway
	CPs2Mesh*			mp_sourceMesh;			

	// new format
	NxPs2::CGeomNode*	mp_instance;
	Mth::Matrix			m_rootMatrix;

	Mth::Matrix *		mp_matrices;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CPs2Geom::SetEngineObject(NxPs2::CGeomNode *p_object)
{
	mp_instance = p_object;
	Dbg_MsgAssert(((int)(mp_instance) & 0xf) == 0,("mp_instance (0x%x) not multiple of 16 (Maybe .SCN corrupted on export?)\n"
													,(int)mp_instance));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline NxPs2::CGeomNode *	CPs2Geom::GetEngineObject() const
{
	return mp_instance;
}

} // Nx

#endif 


================================================
FILE: Code/Gfx/NGPS/p_NxImposter.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxImposter.cpp

#include 	"gfx/ngps/p_NxGeom.h"
#include 	"gfx/ngps/p_NxImposter.h"

namespace Nx
{
	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CImposterGroup* CImposterManager::plat_create_imposter_group( void )
{
	return new CPs2ImposterGroup;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::plat_pre_render_imposters( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::plat_post_render_imposters( void )
{
}



/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CImposterGroup

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CPs2ImposterGroup::CPs2ImposterGroup()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CPs2ImposterGroup::~CPs2ImposterGroup()
{
}



} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/NGPS/p_NxImposter.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxFont.h

#ifndef	__GFX_P_NX_IMPOSTER_H__
#define	__GFX_P_NX_IMPOSTER_H__

#include 	"gfx/NxImposter.h"

namespace Nx
{
	
/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CImposterGroup
class CPs2ImposterGroup : public CImposterGroup
{
	public:
								CPs2ImposterGroup();
	virtual						~CPs2ImposterGroup();


	private:					// It's all private, as it is machine specific

	// Machine specific members
};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/NGPS/p_NxLight.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxLight.cpp - PS2 platform specific interface to CModelLights
//
// This is PS2 SPECIFIC!!!!!!  So might get a bit messy

#include 

#include 
#include 


namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2ModelLights::CPs2ModelLights()
{
	mp_light_group = new NxPs2::CLightGroup;

	plat_enable_ambient_light(false);
	for (int i = 0; i < CLightManager::MAX_LIGHTS; i++)
	{
		plat_enable_diffuse_light(i, false);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2ModelLights::~CPs2ModelLights()
{
	if (mp_light_group)
	{
		delete mp_light_group;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2ModelLights::plat_update_engine( Mth::Vector & pos, bool add_scene_light )
{
	// Figure Scene Lighting if required.
	if( add_scene_light )
	{
		// Copy the object position into our position.  If this pointer is set to NULL,
		// we probably won't find any Scene Lights.
		if (mp_pos)
		{
			pos = *mp_pos;
		}
		else
		{
			Dbg_MsgAssert(0, ("No object position pointer for scene lights"));
		}

		// We should check if any calcs are necessary first by seeing if this position is
		// "visible", but for now, we'll do them all.
		Nx::CSceneLight *p_scene_light = CLightManager::sGetOptimumSceneLight( pos );
		if( p_scene_light )
		{
			Dbg_Assert(mp_light_group);

			Mth::Vector light_pos = p_scene_light->GetLightPosition();

			float dist	= Mth::Distance( pos, light_pos );
			float ratio	= dist * p_scene_light->GetLightReciprocalRadius();

			light_pos = - ( pos - light_pos ).Normalize();

			// Figure the direction...
			mp_light_group->SetDirection(SCENE_LIGHT_INDEX, light_pos);

			// ...and the color.
			ratio = sqrtf( 1.0f - ratio ) * p_scene_light->GetLightIntensity();
			Mth::Vector color(ratio * p_scene_light->GetLightColor().r,
							  ratio * p_scene_light->GetLightColor().g,
							  ratio * p_scene_light->GetLightColor().b,
							  0);

			mp_light_group->SetBaseDiffuseColor(SCENE_LIGHT_INDEX, color);

			plat_enable_diffuse_light(SCENE_LIGHT_INDEX, true);
		}
		else
		{
#if 0
			// Disable this light by setting zero color.
			Mth::Vector color(0, 0, 0, 0);

			mp_light_group->SetBaseDiffuseColor(SCENE_LIGHT_INDEX, color);
#endif
			plat_enable_diffuse_light(SCENE_LIGHT_INDEX, false);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2ModelLights::plat_update_brightness()
{
	mp_light_group->SetAmbientBrightness(m_ambient_brightness);

	for (int i = 0; i < CLightManager::MAX_LIGHTS; i++)
	{
		mp_light_group->SetDiffuseBrightness(i, m_diffuse_brightness[i]);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2ModelLights::plat_set_light_ambient_color(const Image::RGBA &rgba)
{
	Mth::Vector color(((float) rgba.r),
					  ((float) rgba.g),
					  ((float) rgba.b),
					  0);

	mp_light_group->SetBaseAmbientColor(color);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA		CPs2ModelLights::plat_get_light_ambient_color() const
{
	Mth::Vector color = mp_light_group->GetBaseAmbientColor();

	Image::RGBA rgba(color[0], color[1], color[2], 0);
	
	return rgba;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2ModelLights::plat_set_light_direction(int light_index, const Mth::Vector &direction)
{
	mp_light_group->SetDirection(light_index, direction);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &	CPs2ModelLights::plat_get_light_direction(int light_index) const
{
	return mp_light_group->GetDirection(light_index);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2ModelLights::plat_set_light_diffuse_color(int light_index, const Image::RGBA &rgba)
{
	Mth::Vector color(((float) rgba.r),
					  ((float) rgba.g),
					  ((float) rgba.b),
					  0);

	mp_light_group->SetBaseDiffuseColor(light_index, color);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA		CPs2ModelLights::plat_get_light_diffuse_color(int light_index) const
{
	Mth::Vector color = mp_light_group->GetBaseDiffuseColor(light_index);

	Image::RGBA rgba(color[0], color[1], color[2], 0);
	
	return rgba;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2ModelLights::plat_enable_ambient_light(bool enable)
{
	mp_light_group->EnableAmbientLight(enable);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2ModelLights::plat_enable_diffuse_light(int light_index, bool enable)
{
	mp_light_group->EnableDiffuseLight(light_index, enable);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2ModelLights::plat_is_ambient_light_enabled() const
{
	return mp_light_group->IsAmbientLightEnabled();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2ModelLights::plat_is_diffuse_light_enabled(int light_index) const
{
	return mp_light_group->IsDiffuseLightEnabled(light_index);
}

} 
 


================================================
FILE: Code/Gfx/NGPS/p_NxLight.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// p_NxLight.H - Neversoft Engine, Rendering portion, Platform dependent interface

#ifndef	__GFX_P_NX_LIGHT_H__
#define	__GFX_P_NX_LIGHT_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
#include 
#include 

namespace NxPs2
{
	class CLightGroup;
}

namespace Nx
{

///////////////////////////////////////////////////////////////////////////////////
// Nx::CPs2ModelLights
class	CPs2ModelLights : public CModelLights
{
public:
								CPs2ModelLights();
	virtual						~CPs2ModelLights();

	NxPs2::CLightGroup *		GetEngineLightGroup() const;

private:	
	// Constants
	enum
	{
		SCENE_LIGHT_INDEX = 2,
	};

	// The engine light group
	NxPs2::CLightGroup *		mp_light_group;

	// Platform-specific calls
	//virtual void				plat_update_lights();
	virtual bool				plat_set_light_ambient_color(const Image::RGBA &rgba);
	virtual bool				plat_set_light_direction(int light_index, const Mth::Vector &direction);
	virtual bool				plat_set_light_diffuse_color(int light_index, const Image::RGBA &rgba);
	virtual Image::RGBA			plat_get_light_ambient_color() const;
	virtual const Mth::Vector &	plat_get_light_direction(int light_index) const;
	virtual Image::RGBA			plat_get_light_diffuse_color(int light_index) const;

	virtual void				plat_update_engine( Mth::Vector & pos, bool add_scene_light );

	virtual void				plat_update_brightness();

	virtual void				plat_enable_ambient_light(bool enable);
	virtual void				plat_enable_diffuse_light(int light_index, bool enable);
	virtual bool				plat_is_ambient_light_enabled() const;
	virtual bool				plat_is_diffuse_light_enabled(int light_index) const;
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline NxPs2::CLightGroup *	CPs2ModelLights::GetEngineLightGroup() const
{
	return mp_light_group;
}

}


#endif



================================================
FILE: Code/Gfx/NGPS/p_NxLightMan.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxLightMan.cpp - PS2 platform specific interface to CLightManager
//
// This is PS2 SPECIFIC!!!!!!  So might get a bit messy

#include 

#include 
#include 
#include 


namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void			CLightManager::s_plat_update_engine( void )
{
	CPs2LightManager::sUpdateEngine();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CLightManager::s_plat_update_lights()
{
	s_plat_set_light_ambient_color();
	for (int i = 0; i < MAX_LIGHTS; i++)
	{
		s_plat_set_light_direction(i);
		s_plat_set_light_diffuse_color(i);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CLightManager::s_plat_update_colors()
{
	s_plat_set_light_ambient_color();
	for (int i = 0; i < MAX_LIGHTS; i++)
	{
		s_plat_set_light_diffuse_color(i);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CLightManager::s_plat_set_light_ambient_color()
{
	Mth::Vector color(((float) s_world_lights.m_light_ambient_rgba.r),
					  ((float) s_world_lights.m_light_ambient_rgba.g),
					  ((float) s_world_lights.m_light_ambient_rgba.b),
					  0);

	NxPs2::CLightGroup::sSetDefaultAmbientColor(color);

	//color *= s_ambient_brightness;
	//NxPs2::CLightGroup::sSetDefaultModAmbientColor(color);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA		CLightManager::s_plat_get_light_ambient_color()
{
	Mth::Vector color(NxPs2::CLightGroup::sGetDefaultAmbientColor());

	Image::RGBA rgb;

	rgb.r = (uint8) color[X];
	rgb.g = (uint8) color[Y];
	rgb.b = (uint8) color[Z];
	rgb.a = 0x80;

	return rgb;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CLightManager::s_plat_set_light_direction(int light_index)
{
	NxPs2::CLightGroup::sSetDefaultDirection(light_index, s_world_lights.m_light_direction[light_index]);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector & CLightManager::s_plat_get_light_direction(int light_index)
{
	return NxPs2::CLightGroup::sGetDefaultDirection(light_index);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CLightManager::s_plat_set_light_diffuse_color(int light_index)
{
	Mth::Vector color(((float) s_world_lights.m_light_diffuse_rgba[light_index].r),
					  ((float) s_world_lights.m_light_diffuse_rgba[light_index].g),
					  ((float) s_world_lights.m_light_diffuse_rgba[light_index].b),
					  0);

	NxPs2::CLightGroup::sSetDefaultDiffuseColor(light_index, color);
	
	//color *= s_diffuse_brightness[light_index];
	//NxPs2::CLightGroup::sSetDefaultModDiffuseColor(light_index, color);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA		CLightManager::s_plat_get_light_diffuse_color(int light_index)
{
	Mth::Vector color(NxPs2::CLightGroup::sGetDefaultDiffuseColor(light_index));

	Image::RGBA rgb;

	rgb.r = (uint8) color[X];
	rgb.g = (uint8) color[Y];
	rgb.b = (uint8) color[Z];
	rgb.a = 0x80;

	return rgb;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModelLights *	CLightManager::s_plat_create_model_lights()
{
	CModelLights *p_model_lights = new CPs2ModelLights;

	if (p_model_lights)
	{
		CPs2LightManager::sAddToModelLightsList(p_model_lights);
	}

	return p_model_lights;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CLightManager::s_plat_free_model_lights(CModelLights *p_model_lights)
{
	Dbg_Assert(p_model_lights);

	CPs2LightManager::sRemoveFromModelLightsList(p_model_lights);

	delete p_model_lights;

	return true;
}

///////////////////////////////////////////////////////////////////////////////////
// Nx::CPs2LightManager

// Model light list
Lst::Head< CModelLights > CPs2LightManager::s_model_lights_list;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2LightManager::sUpdateEngine( void )
{
	s_update_model_lights();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2LightManager::sAddToModelLightsList(CModelLights *p_model_lights)
{
	Dbg_Assert(p_model_lights);

	Lst::Node< CModelLights > *node = new Lst::Node< CModelLights > (p_model_lights);
	if (node)
	{
		s_model_lights_list.AddToTail(node);
		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2LightManager::sRemoveFromModelLightsList(CModelLights *p_model_lights)
{
	Dbg_Assert(p_model_lights);

	Lst::Node< CModelLights > *obj_node;

	for(obj_node = s_model_lights_list.GetNext(); obj_node; obj_node = obj_node->GetNext())
	{
		if (obj_node->GetData() == p_model_lights)
		{
			delete obj_node;
			return true;
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2LightManager::s_update_model_lights()
{
	Mth::Vector dummy_pos(0, 0, 0, 1);		// Function requires this to be passed in, even though not used
	Lst::Node< CModelLights > *obj_node;

	for(obj_node = s_model_lights_list.GetNext(); obj_node; obj_node = obj_node->GetNext())
	{
		CModelLights *p_model_lights = obj_node->GetData();
		
		Dbg_Assert(p_model_lights);

		p_model_lights->UpdateEngine(dummy_pos, true);
	}
}

} 
 


================================================
FILE: Code/Gfx/NGPS/p_NxLightMan.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// p_NxLightMan.H - Neversoft Engine, Rendering portion, Platform dependent interface

#ifndef	__GFX_P_NX_LIGHT_MAN_H__
#define	__GFX_P_NX_LIGHT_MAN_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 

#include 


namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

///////////////////////////////////////////////////////////////////////////////////
// Nx::CPs2LightManager
class	CPs2LightManager : public CLightManager
{
public:

	// Does any once-per-frame light update
	static void					sUpdateEngine( void );

	// Model Lights List functions
	static bool					sAddToModelLightsList(CModelLights *p_model_lights);
	static bool					sRemoveFromModelLightsList(CModelLights *p_model_lights);

private:	

	// once-per-frame update functions called from sUpdateEngine()
	static void					s_update_model_lights();

	// Model light list
	static Lst::Head< CModelLights > s_model_lights_list;
};

}


#endif



================================================
FILE: Code/Gfx/NGPS/p_NxLoadScreen.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxLoadScreen.cpp - PS2 platform specific interface for the load screen
//
// This is PS2 SPECIFIC!!!!!!  So might get a bit messy
//

#include	"gfx\Nx.h"
#include	"gfx\NxLoadScreen.h"
#include	"gfx\NGPS\p_NxTexture.h"
#include	"gfx\NGPS\p_NxSprite.h"
#include	"gfx\NGPS\NX\sprite.h"
#include	"gfx\NGPS\NX\render.h"
#include	"gfx\NGPS\NX\loadscreen.h"
#include	"gfx\NGPS\NX\dma.h"
#include	"gfx\NGPS\NX\dmacalls.h"
#include	"gfx\NGPS\NX\nx_init.h"

#include	"gel\movies\NGPS\disp.h"  // for clear screen

#include	"core\macros.h"
#include 
#include 
#include 

#include 

namespace NxPs2
{
	void	WaitForRendering();
}


namespace	Nx
{


Nx::CPs2Texture	*sp_load_screen_texture;
Nx::CPs2Sprite	*sp_load_screen_sprite;

/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions

#define USE_SPRITES 0

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_display(const char* filename, bool just_freeze, bool blank)
{
	// Engine stuff
	Dbg_Assert(!sp_load_screen_texture);
	Dbg_Assert(!sp_load_screen_sprite);

	char adjustedFilename[144];

	if (!just_freeze && !blank)
	{
		sp_load_screen_texture = new CPs2Texture(true);

		if (Config::NTSC())
		{
			strcpy(adjustedFilename, filename);
		} else {
			// Find correct directory to use based on language
			switch (Config::GetLanguage())
			{
			case Config::LANGUAGE_FRENCH:
				sprintf(adjustedFilename, "PALimages/FRImages/%s", filename);
				break;
			case Config::LANGUAGE_SPANISH:
				sprintf(adjustedFilename, "PALimages/SPImages/%s", filename);
				break;
			case Config::LANGUAGE_GERMAN:
				sprintf(adjustedFilename, "PALimages/GRImages/%s", filename);
				break;
			case Config::LANGUAGE_ITALIAN:
				sprintf(adjustedFilename, "PALimages/ITImages/%s", filename);
				break;
			default:
				sprintf(adjustedFilename, "PALimages/%s", filename);
				break;
			}

			// Since LoadTexture() will assert if the file doesn't exist, we need to test ahead of time
			char ExtendedFilename[160];
			sprintf(ExtendedFilename, "images/%s.img.ps2", adjustedFilename);

			if (!File::Exist(ExtendedFilename))
			{
				// Can't find it, so lets try the base directory before giving up
				sprintf(adjustedFilename, "PALimages/%s", filename);
			}
		}

		if (!sp_load_screen_texture->LoadTexture(adjustedFilename, true))
		{
			Dbg_Error("Can't load texture %s", adjustedFilename);
		}
	
	#if USE_SPRITES
		sp_load_screen_sprite = static_cast(CEngine::sCreateSprite());
		sp_load_screen_sprite->SetTexture(sp_load_screen_texture);
		sp_load_screen_sprite->SetPos(0, 0);
		sp_load_screen_sprite->SetScale(1, 1);
		sp_load_screen_sprite->SetRGBA(Image::RGBA(128, 128, 128, 128));
		sp_load_screen_sprite->SetHidden(false);
	#else
		// Copy directly into display buffer
		sceGsLoadImage gs_image;
		NxPs2::SSingleTexture *p_engine_texture = sp_load_screen_texture->GetSingleTexture();
	
		FlushCache( 0 );
		sceGsSyncPath( 0, 0 );
		
		int display_addr = (SCREEN_CONV_X( 640 ) * SCREEN_CONV_Y( 448 ) * 4) / (64 * 4);
	
		const int download_parts = 2;
		int partial_height = sp_load_screen_texture->GetHeight() / download_parts;
		int partial_size = sp_load_screen_texture->GetWidth() * partial_height * 2;
		for (int i = 0; i < download_parts; i++)
		{
			sceGsSetDefLoadImage( &gs_image, display_addr, SCREEN_CONV_X( 640 )/64, SCE_GS_PSMCT16S,
							   0, partial_height * i, sp_load_screen_texture->GetWidth(), partial_height );
					
			FlushCache( 0 );		
			sceGsExecLoadImage( &gs_image, ( u_long128 * )(p_engine_texture->mp_PixelData + (partial_size * i)) );
			sceGsSyncPath( 0, 0 );
		}
	
		// Done with texture
		delete sp_load_screen_texture;
		sp_load_screen_texture = NULL;
	}
	
	if (blank)
	{
		// This is esentially the same as playing a movie, without playing a movie
		// the ResetEngine handles clearing the screen 
		NxPs2::SuspendEngine();
		NxPs2::ResetEngine();		
	}
	
	
	
	NxPs2::EnableFlipCopy(false);
#endif // USE_SPRITES
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_start_loading_bar(float seconds)
{
	NxPs2::StartLoadingBar((int) (seconds * Config::FPS()));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_hide()
{
	NxPs2::RemoveLoadingBar();
	//Tmr::VSync();
#if !USE_SPRITES
	NxPs2::EnableFlipCopy(true);
#endif // USE_SPRITES
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_update_bar_properties()
{
	// Bar size and position
	NxPs2::gLoadBarX = s_bar_x;
	NxPs2::gLoadBarY = s_bar_y;
	NxPs2::gLoadBarWidth = s_bar_width;
	NxPs2::gLoadBarHeight = s_bar_height;

	// Bar colors
	NxPs2::gLoadBarStartColor[0] = s_bar_start_color.r;
	NxPs2::gLoadBarStartColor[1] = s_bar_start_color.g;
	NxPs2::gLoadBarStartColor[2] = s_bar_start_color.b;
	NxPs2::gLoadBarStartColor[3] = s_bar_start_color.a;
	NxPs2::gLoadBarDeltaColor[0] = (int) s_bar_end_color.r - NxPs2::gLoadBarStartColor[0];
	NxPs2::gLoadBarDeltaColor[1] = (int) s_bar_end_color.g - NxPs2::gLoadBarStartColor[1];
	NxPs2::gLoadBarDeltaColor[2] = (int) s_bar_end_color.b - NxPs2::gLoadBarStartColor[2];
	NxPs2::gLoadBarDeltaColor[3] = (int) s_bar_end_color.a - NxPs2::gLoadBarStartColor[3];

	// Border size
	NxPs2::gLoadBarBorderWidth = s_bar_border_width;
	NxPs2::gLoadBarBorderHeight = s_bar_border_height;

	// Border color
	NxPs2::gLoadBarBorderColor[0] = s_bar_border_color.r;
	NxPs2::gLoadBarBorderColor[1] = s_bar_border_color.g;
	NxPs2::gLoadBarBorderColor[2] = s_bar_border_color.b;
	NxPs2::gLoadBarBorderColor[3] = s_bar_border_color.a;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_clear()
{
	if (sp_load_screen_sprite)
	{
		CEngine::sDestroySprite(sp_load_screen_sprite);
		sp_load_screen_sprite = NULL;
	}

	if (sp_load_screen_texture)
	{
		delete sp_load_screen_texture;
		sp_load_screen_texture = NULL;
	}

	NxPs2::RemoveLoadingBar();
}

} 
 


================================================
FILE: Code/Gfx/NGPS/p_NxMesh.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxMesh.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/15/2002
//****************************************************************************

#include 

#include 
								  
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Mesh::build_casdata_table(uint8* p_casData)
{
	#ifdef __NOPT_ASSERT__
	uint32 version = *((uint32*)p_casData);
	#endif
	p_casData += sizeof(uint32);

	Dbg_MsgAssert( version >= 2, ( "Obsolete version of CAS file.  Please re-export." ) )
		
	m_CASRemovalMask = *((uint32*)p_casData);
	p_casData += sizeof(uint32);

	m_numCASData = *((int*)p_casData);
	p_casData += sizeof(int);

	// temporary lookup table, for the duration of the scene loading
	NxPs2::CreateCASDataLookupTable();

	if ( m_numCASData > 0 )
	{
		// create-a-skater flags
		mp_CASData = new NxPs2::sCASData[m_numCASData];
		
		for ( int i = 0; i < m_numCASData; i++ )
		{
			mp_CASData[i].mask = *((uint32*)p_casData);
			p_casData += sizeof(uint32);

			// this will get converted into a pointer to the DMA data
			int vertIndex = *((int*)p_casData);
			p_casData += sizeof(int);
			
			NxPs2::SetCASDataLookupData( vertIndex, &mp_CASData[i] );
		}		
	}

	return ( m_numCASData > 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Mesh::build_casdata_table(const char* pFileName)
{
	void *pFile = File::Open(pFileName, "rb");
	Dbg_MsgAssert(pFile, ("Couldn't open CAS data file %s\n", pFileName));

	int file_size = File::GetFileSize(pFile);

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	uint8* p_fileBuffer = (uint8*)Mem::Malloc( file_size );
	Mem::Manager::sHandle().PopContext();

	File::Read(p_fileBuffer, file_size, 1, pFile);
	File::Close( pFile );

	bool success = build_casdata_table( p_fileBuffer );

	Mem::Free(p_fileBuffer);
	
	// some debugging information for the artists
	if ( Script::GetInteger( "DebugCAS", Script::NO_ASSERT ) )
	{
		printf( "%s removes %08x: [", pFileName, m_CASRemovalMask );
		for ( int i = 0; i < 32; i++ )
		{
			uint32 mask = ( 1 << i );
			if ( m_CASRemovalMask & mask )
			{
				printf( " %d", i );
			}
		}
		printf( "]\n" );
	}

	return success;
}
	
/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool use_new_geom_format( const char* pMeshFileName )
{
	// "reetest" is a quick kludge to get the skeletal trees
	// up and running...  eventually, pass this in as a parameter
	// from the nx.cpp level...
	if ( strstr( pMeshFileName, "eh_" ) || strstr( pMeshFileName, "reetest" ) )
	{
		// if it's a vehicle, then use the new geomnode format
		return true;
	}
	else
	{
		return false; 
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Mesh::CPs2Mesh(uint32* pModelData, int modelDataSize, uint8* pCASData, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume)
{
	Dbg_Assert( pTexDict );

	mp_texDict = pTexDict;
	
	// polys should only be hidden once
	m_polysAlreadyHidden = false;

	m_isPipped = false;
	m_numCASData = 0;

	// don't do this for models
	if ( isSkin && pCASData )
	{
		build_casdata_table( pCASData );
	}
	
	// GJ:  The geomnodes blow off the texDictOffset, 
	// which means it won't work with skater models, 
	// or the 10 board gameobjs in the boardshop.
	if ( !isSkin && texDictOffset == 0 )
	{		 
		// since we're not actually opening a PIP file
		// we don't need to keep track of it
		m_isPipped = false;

		// this should already be in GEOM format,
		// by the time the data arrives here...
		// (we're not loading up the Pip through the
		// normal Pip::Load process, so we need
		// to remember the pointer to the pip data
		// so that we can delete it later)
		mp_pipData = (uint8*)Mem::Malloc( modelDataSize );
		Dbg_Assert( mp_pipData );
		
		memcpy( mp_pipData, pModelData, modelDataSize );

		// process data in place
		// final arg (0) says to hold it in a database to be rendered later
		// (as opposed to rendering it unconditionally, LOADFLAG_RENDERNOW)
		mp_geomNode = NxPs2::CGeomNode::sProcessInPlace( mp_pipData, 0 );

		// Get hierarchy array
		mp_hierarchyObjects = (CHierarchyObject*) NxPs2::CGeomNode::sGetHierarchyArray(mp_pipData, m_numHierarchyObjects);
	}
	else
	{
		NxPs2::sScene* pTextureDictionary = ((Nx::CPs2TexDict*)mp_texDict)->GetEngineTextureDictionary();
	
		NxPs2::LoadScene( pModelData, modelDataSize, pTextureDictionary, true, true, texDictOffset, doShadowVolume );

		mp_geomNode = NULL;
	}

	// temporary lookup table is no longer needed
	NxPs2::DestroyCASDataLookupTable();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Mesh::CPs2Mesh(const char* pMeshFileName, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume)
{
	Dbg_Assert( pTexDict );

	m_isPipped = false;
	mp_texDict = pTexDict;
	
	m_numCASData = 0;

	// don't do this for models
	if ( isSkin )
	{
		char CASFileName[256];
		strcpy(CASFileName, pMeshFileName);
		Str::LowerCase( CASFileName );
		char* pExt = strstr( CASFileName, "skin.ps2" );
		if ( pExt )
		{
			Dbg_MsgAssert( pExt, ("Couldn't find skin.ps2 extension in %s", CASFileName));
			strcpy(pExt, "cas.ps2");
			build_casdata_table( CASFileName );
		}
	}
	
	// GJ:  The geomnodes blow off the texDictOffset, 
	// which means it won't work with skater models, 
	// or the 10 board gameobjs in the boardshop.
	if ( !isSkin && texDictOffset == 0 )
	{		 
		// change the file name so that it's using the new Geom
		// file format rather than Mdl.
		char msg[512];
		strcpy( msg, pMeshFileName );
		char* pDot = strstr( msg, "." );
		Dbg_Assert( pDot );
		*pDot = NULL;
		sprintf( pDot, ".geom.%s", Nx::CEngine::sGetPlatformExtension() );
		m_pipFileName = msg;
		m_isPipped = true;

//		printf( "about to load pip file %s %d\n", m_pipFileName.getString(), texDictOffset );

		// Pip::Load the new file
		uint8* pPipData = (uint8*)Pip::Load( m_pipFileName.getString() );

		Dbg_Assert( pPipData );

		// process data in place
		// final arg (0) says to hold it in a database to be rendered later
		// (as opposed to rendering it unconditionally, LOADFLAG_RENDERNOW)
		mp_geomNode = NxPs2::CGeomNode::sProcessInPlace( pPipData, 0 );

		// Find CGeomNodeObjects
//		if ( use_new_geom_format(pMeshFileName) )
		{
			// Get hierarchy array
			mp_hierarchyObjects = (CHierarchyObject *) NxPs2::CGeomNode::sGetHierarchyArray(pPipData, m_numHierarchyObjects);
		}
	}
	else
	{
		NxPs2::sScene* pTextureDictionary = ((Nx::CPs2TexDict*)mp_texDict)->GetEngineTextureDictionary();
	
		NxPs2::LoadScene( pMeshFileName, pTextureDictionary, true, true, texDictOffset, doShadowVolume );

		mp_geomNode = NULL;
	}

	// temporary lookup table is no longer needed
	NxPs2::DestroyCASDataLookupTable();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Mesh::~CPs2Mesh()
{
	//if (mp_geomNodeObjects)
	//{
	//	delete[] mp_geomNodeObjects;
	//}

	if ( mp_geomNode )
	{
		// clean up the node we created
		Dbg_Assert( mp_geomNode );
		mp_geomNode->Cleanup();

		// Pip::Unload the file
		if ( m_isPipped )
		{
			Pip::Unload( m_pipFileName.getString() );
		}

		// the file was not loaded using Pip::Load()
		if ( mp_pipData )
		{
			Mem::Free( mp_pipData );
			mp_pipData = NULL;
		}
	}
	else
	{
		NxPs2::sScene* pScene = ((Nx::CPs2TexDict*)mp_texDict)->GetEngineTextureDictionary();
	
		// unload scene
		NxPs2::DeleteScene( pScene );
	}

	if ( mp_CASData )
	{
		delete[] mp_CASData;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Mesh::HidePolys( uint32 mask )
{
	int count = 0;

	// this function should only be called once per mesh
	// because after the ADC bits are squeezed,
	// the pointers are no longer valid...
	// (was causing a crash in the secret level
	// with random peds that were doing poly removal)
	if ( m_polysAlreadyHidden )
	{
		return;
	}

//	printf( "Hiding polys with mask 0x%08x\n", mask);

	for ( int j = 0; j < m_numCASData; j++ )
	{
		Dbg_Assert( mp_CASData );

		if ( mp_CASData[j].mask & mask )
		{
			if ( mp_CASData[j].pADCBit )
			{
				*mp_CASData[j].pADCBit = 0xC000;
			}
			count++;
		}
	}

//	printf( "Hiding %d polys\n", count );

	m_polysAlreadyHidden = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx
				


================================================
FILE: Code/Gfx/NGPS/p_NxMesh.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxMesh.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/15/2002
//****************************************************************************

#ifndef	__GFX_P_NX_MESH_H__
#define	__GFX_P_NX_MESH_H__
    
#include "gfx/nxmesh.h"

#include 
			
namespace NxPs2
{
	class	CGeomNode;
	struct	sCASData;
}
			 
namespace Nx
{

	class	CHierarchyObject;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CMesh
    
class CPs2Mesh : public CMesh
{
                                      
public:
	CPs2Mesh( uint32* pModelData, int modelDataSize, uint8* pCASData, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume );
	CPs2Mesh( const char* pMeshFileName, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume );
	virtual 			~CPs2Mesh();

	// temporary?
	NxPs2::CGeomNode*	GetGeomNode() { return mp_geomNode; }
	void				HidePolys( uint32 mask );

protected:
	NxPs2::CGeomNode*	mp_geomNode;
	bool				m_polysAlreadyHidden:1;
	bool				m_isPipped:1;
	uint8*				mp_pipData;	// for pip-style geoms that are loaded from a data buffer
	Str::String			m_pipFileName;

	bool				build_casdata_table(uint8* pCASData);
	bool				build_casdata_table(const char* pFileName);
	
	NxPs2::sCASData*	mp_CASData;
	int					m_numCASData;
};


} // Nx

#endif 


================================================
FILE: Code/Gfx/NGPS/p_NxModel.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxModel.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/21/2001
//****************************************************************************

#include 

#include 
#include "gfx/nxgeom.h"

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CPs2Model::plat_init_skeleton( int numBones )
{
	Dbg_Assert( mp_matrices == NULL );

	// matrix buffer is double-buffered
	mp_matrices = new Mth::Matrix[numBones * 2];
	for ( int i = 0; i < numBones * 2; i++ )
	{
		mp_matrices[i].Identity();
	}
    
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CPs2Model::plat_get_bounding_sphere()
{
	Mth::Vector sphere, sphere1, sum, diff;
	float dist;

	// this should probably never happen
	if (m_numGeoms == 0)
		return Mth::Vector(0.0f, 0.0f, 0.0f, 0.0f);

	// combine the spheres of all geoms
	// (this should really be done once at load time)

	// start with first sphere
	sphere = mp_geom[0]->GetBoundingSphere();

	// loop over remaining spheres, expanding as necessary
	for (int i=1; iGetBoundingSphere();

		// centre-to-centre vector, and distance
		diff = sphere1-sphere;
		dist = diff.Length();

		// test for sphere1 inside sphere
		if (dist+sphere1[3] <= sphere[3])
			continue;			// keep sphere

		// test for sphere inside sphere1
		if (dist+sphere[3] <= sphere1[3])
		{
			sphere = sphere1;	// replace sphere
			continue;
		}

		// otherwise make a larger sphere that contains both
		sum       = sphere+sphere1;
		sphere    = 0.5f * (sum + (diff[3]/dist) * diff);
		sphere[3] = 0.5f * (dist + sum[3]);
	}

	return sphere;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Model::plat_set_bounding_sphere( const Mth::Vector& boundingSphere )
{
	// loop over all spheres
	for ( int i = 0; i < m_numGeoms; i++ )
	{
		mp_geom[i]->SetBoundingSphere( boundingSphere );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2Model::CPs2Model()
{
	mp_matrices = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2Model::~CPs2Model()
{	
	if ( mp_matrices != NULL )
	{
		delete[] mp_matrices;
		mp_matrices = NULL;
	}
}

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Matrix* CPs2Model::GetMatrices()
{
	return mp_matrices;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx

				


================================================
FILE: Code/Gfx/NGPS/p_NxModel.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxModel.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  1/8/2002
//****************************************************************************

#ifndef	__GFX_P_NX_MODEL_H__
#define	__GFX_P_NX_MODEL_H__
    
#include "gfx/nxmodel.h"


namespace NxPs2
{
	class CInstance;             
	struct sScene;
}
							   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CModel
    
class CPs2Model : public CModel
{
                                      
public:
	CPs2Model();
	virtual				~CPs2Model();
	Mth::Matrix*		GetMatrices();

private:				// It's all private, as it is machine specific
	bool				plat_init_skeleton( int numBones );
	Mth::Vector 		plat_get_bounding_sphere();
	void				plat_set_bounding_sphere( const Mth::Vector& boundingSphere );

private:
	Mth::Matrix*		mp_matrices;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx

#endif 


================================================
FILE: Code/Gfx/NGPS/p_NxScene.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.cpp

#include 	"Gfx/nx.h"
#include 	"Gfx/NGPS/p_NxGeom.h"
#include 	"Gfx/NGPS/p_NxScene.h"
#include 	"Gfx/NGPS/p_NxSector.h"
#include 	"Gfx/NGPS/NX/geomnode.h"

#include 

#include 

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Scene::CPs2Scene()
{
	mp_plat_scene = NULL;
	mp_plat_clone_scene = NULL;
	mp_plat_add_scene = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Scene::~CPs2Scene()
{
	if (mp_plat_clone_scene)
	{
		// Have to delete the sectors here first
		mp_sector_table->IterateStart();
		CSector *p_sector;
		while ((p_sector = mp_sector_table->IterateNext()))
		{
			p_sector->clear_flags(CSector::mIN_SUPER_SECTORS);	// Tells cloned sectors it is OK to go away (and SuperSectors will be dead, anyway)
			delete p_sector;
		}
		delete mp_sector_table;
		mp_sector_table = NULL;

		mp_plat_clone_scene->Cleanup();
		delete mp_plat_clone_scene;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

NxPs2::CGeomNode *	CPs2Scene::GetEngineScene() const
{
	return mp_plat_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2Scene::SetEngineScene(NxPs2::CGeomNode *p_scene)
{
	mp_plat_scene = p_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

NxPs2::CGeomNode *	CPs2Scene::GetEngineCloneScene() const
{
	return mp_plat_clone_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2Scene::SetEngineCloneScene(NxPs2::CGeomNode *p_scene)
{
	mp_plat_clone_scene = p_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

NxPs2::CGeomNode *	CPs2Scene::GetEngineAddScene() const
{
	return mp_plat_add_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2Scene::SetEngineAddScene(NxPs2::CGeomNode *p_scene)
{
	mp_plat_add_scene = p_scene;
}

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CScene

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2Scene::create_sectors(NxPs2::CGeomNode *p_node)
{
	if (!p_node)
		return;

	while (p_node)
	{
		// Recursively traverse tree, treating the object-level nodes as leaves.
		// From each object, create a sector.
		// (Mick: optimized tail recursion, after suggestion from John Brandwood)
		if (p_node->IsObject())			// Create CSector from object
		{
			CSector *pSector;
			CPs2Sector *pPs2Sector;		// platform dependent class
			CPs2Geom *pGeom;
	
			// Find Sector
			pSector = GetSector(p_node->GetChecksum());
			Dbg_MsgAssert(!pSector, ("We already have a sector with checksum %x", p_node->GetChecksum()));
	
			// If it doesn't exist yet, create it
			if (!pSector)
			{
				pSector = new CPs2Sector;		// Create platform specific data
				pSector->SetChecksum(p_node->GetChecksum());
	
				pGeom = new CPs2Geom;
				pGeom->SetEngineObject(p_node);
	
				pSector->SetGeom(pGeom);
	
				AddSector(pSector);
	
				//Dbg_Message("Creating new sector %x", p_node->GetChecksum());
			}
	
			// Access platform dependent data
			pPs2Sector = static_cast(pSector);
	
			// Add node to sector
			//pPs2Sector->SetEngineObject(p_node);
			pPs2Sector->SetEngineObject(NULL);
		} else {	// Traverse children and siblings
			create_sectors(p_node->GetChild());
		}
	
		p_node = p_node->GetSibling();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2Scene::add_sectors(NxPs2::CGeomNode *p_node)
{
	if (!p_node)
		return;

	// Recursively traverse tree, treating the object-level nodes as leaves.
	// From each object, create a sector.
	if (p_node->IsObject())			// Create CSector from object
	{
		CSector *pSector, *p_found_sector;
		CPs2Sector *pPs2Sector;		// platform dependent class
		CPs2Geom *pGeom;

		// Find Sector
		p_found_sector = GetSector(p_node->GetChecksum());
		//Dbg_MsgAssert(!pSector, ("We already have a sector with checksum %x", p_node->GetChecksum()));

		// Even if it does exist, create it
		pSector = new CPs2Sector;		// Create platform specific data
		pSector->SetChecksum(p_node->GetChecksum());

		pGeom = new CPs2Geom;
		pGeom->SetEngineObject(p_node);

		pSector->SetGeom(pGeom);

		#ifdef	__NOPT_ASSERT__
		CSector *p_old_sector =
		#endif
		ReplaceSector(pSector);

		Dbg_Assert(p_old_sector == p_found_sector);
		//Dbg_Message("Creating new sector %x", p_node->GetChecksum());

		// Access platform dependent data
		pPs2Sector = static_cast(pSector);

		// Add node to sector
		pPs2Sector->SetEngineObject(NULL);
	} else {	// Traverse children and siblings
		add_sectors(p_node->GetChild());
	}

	add_sectors(p_node->GetSibling());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2Scene::delete_sectors(NxPs2::CGeomNode *p_node)
{
	if (!p_node)
		return;

	// Recursively traverse tree, treating the object-level nodes as leaves.
	// From each object, delete the sector.
	if (p_node->IsObject())			// Create CSector from object
	{
		// Find Sector
		CSector *pSector = GetSector(p_node->GetChecksum());
		Dbg_MsgAssert(pSector, ("Cant find a sector with checksum %x to delete", p_node->GetChecksum()));

		DeleteSector(pSector);
	} else {	// Traverse children and siblings
		delete_sectors(p_node->GetChild());
	}

	delete_sectors(p_node->GetSibling());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2Scene::plat_post_load()
{
	// Generate CSectors first
	// Garrett: TEMP HACK: We don't want to traverse siblings, so we send down the child.
	//          This works because we know there won't be any data in the root node.
	//          THIS WILL GO AWAY VERY SOON.  DON'T COPY THIS HACK WITHOUT THIS MESSAGE.
	create_sectors(mp_plat_scene->GetChild());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2Scene::plat_post_add()
{
	// Generate CSectors first
	// Garrett: TEMP HACK: We don't want to traverse siblings, so we send down the child.
	//          This works because we know there won't be any data in the root node.
	//          THIS WILL GO AWAY VERY SOON.  DON'T COPY THIS HACK WITHOUT THIS MESSAGE.
	add_sectors(mp_plat_add_scene->GetChild());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2Scene::plat_load_textures(const char *p_name)
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2Scene::plat_load_collision(const char *p_name)
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2Scene::plat_add_collision(const char *p_name)
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2Scene::plat_unload_add_scene()
{
	if (mp_plat_add_scene)
	{
		sceGsSyncPath(0, 0);

		// Delete CSectors first
		// Garrett: TEMP HACK: We don't want to traverse siblings, so we send down the child.
		//          This works because we know there won't be any data in the root node.
		//          THIS WILL GO AWAY VERY SOON.  DON'T COPY THIS HACK WITHOUT THIS MESSAGE.
		//delete_sectors(mp_plat_add_scene->GetChild());

		// clean up the node we created
		mp_plat_add_scene->Cleanup();

		// Pip::Unload the file
		Pip::Unload(m_add_scene_filename);
		m_add_scene_filename[0] = '\0';

		return true;
	}

	return false;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Create an empty sector
CSector	*			CPs2Scene::plat_create_sector()
{
	CPs2Sector * p_sector = new CPs2Sector(); 

	return p_sector;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void				CPs2Scene::plat_set_majority_color(Image::RGBA rgba)
{
	if (mp_plat_scene)
	{
		// Set the color of the root node
		mp_plat_scene->SetColored(true);
		mp_plat_scene->SetColor(*((uint32 *) &rgba));
	}
	if (mp_plat_clone_scene)
	{
		// Set the color of the root clone node
		mp_plat_clone_scene->SetColored(true);
		mp_plat_clone_scene->SetColor(*((uint32 *) &rgba));
	}
}
		 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA			CPs2Scene::plat_get_majority_color() const
{
	uint32 color = mp_plat_scene->GetColor();
	return *((Image::RGBA *) &color);
}
	

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/NGPS/p_NxScene.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.h

#ifndef	__GFX_P_NX_SCENE_H__
#define	__GFX_P_NX_SCENE_H__

#include 	"Gfx/nxscene.h"

namespace NxPs2
{
	class CGeomNode;
}

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CScene
class	CPs2Scene : public CScene
{
public:
								CPs2Scene();
	virtual						~CPs2Scene();

	NxPs2::CGeomNode *			GetEngineScene() const;
	void						SetEngineScene(NxPs2::CGeomNode *p_scene);

	NxPs2::CGeomNode *			GetEngineAddScene() const;
	void						SetEngineAddScene(NxPs2::CGeomNode *p_scene);

	NxPs2::CGeomNode *			GetEngineCloneScene() const;
	void						SetEngineCloneScene(NxPs2::CGeomNode *p_scene);

private:		
	virtual void				plat_post_load();	
	virtual void				plat_post_add();	
	virtual bool				plat_load_textures(const char *p_name);		// load textures 
	virtual bool				plat_load_collision(const char *p_name);	// load collision data
	virtual bool				plat_add_collision(const char *p_name);		// add collision data
	virtual bool				plat_unload_add_scene();					// unloads previous add scene
	virtual	CSector	*			plat_create_sector();	 					// empty sector
	virtual void				plat_set_majority_color(Image::RGBA rgba);	// set the most common sector color
	virtual Image::RGBA			plat_get_majority_color() const;			// get the most common sector color
	

	void						create_sectors(NxPs2::CGeomNode *p_node);
	void						add_sectors(NxPs2::CGeomNode *p_node);
	void						delete_sectors(NxPs2::CGeomNode *p_node);

	NxPs2::CGeomNode *			mp_plat_scene;		// Platform-dependent data
	NxPs2::CGeomNode *			mp_plat_clone_scene;
	NxPs2::CGeomNode *			mp_plat_add_scene;
};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/NGPS/p_NxSector.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxSector.cpp

#include 	"Gfx/nx.h"
#include 	"Gfx/NGPS/p_NxGeom.h"
#include 	"Gfx/NGPS/p_NxSector.h"
#include 	"Gfx/NxMiscFX.h"
#include 	
#include 	"gfx\ngps\nx\geomnode.h"

#include 	"gel/collision/collision.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Sector::CPs2Sector() :
	mp_plat_object(NULL), 
	m_rgba(Image::RGBA(128, 128, 128, 128)),
	m_shatter(false)
{
	m_world_matrix.Ident();
	m_active = true;						// default to be active....
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Sector::~CPs2Sector()
{
	if (mp_geom)
	{
		//CPs2Geom *p_ps2_geom = static_cast(mp_geom);
		//p_ps2_geom->SetEngineObject(NULL);		// Hack so that CGeom doesn't try to free the CGeomNode
	}
}

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CSector
// and we will also have a CXboxSector, CNgcSector, even a CPcSector
// maybe in the future we will have a CPS3Sector?

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Sector::plat_set_color(Image::RGBA rgba)
{
	// Set values
	m_rgba = rgba;

	// Engine call here
	if (mp_plat_object)
	{
		mp_plat_object->SetColored(true);
		mp_plat_object->SetColor(*((uint32 *) &rgba));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA CPs2Sector::plat_get_color() const
{
	return m_rgba;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Sector::plat_clear_color()
{
#if 0
	// Set to white
	plat_set_color(Image::RGBA(128, 128, 128, 128));
#endif
	// Engine call here
	if (mp_plat_object)
	{
		mp_plat_object->SetColored(false);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Sector::plat_set_visibility(uint32 mask)
{
	// Engine call here
	if (mp_plat_object)
	{
		mp_plat_object->SetVisibility(mask & 0xFF);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CPs2Sector::plat_get_visibility() const
{
	if ( mp_plat_object )
	{
		return mp_plat_object->GetVisibility() | 0xFFFFFF00;		// To keep format compatible
	}
	else
	{
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Sector::plat_set_active(bool on)	
{
	// Set values
	if (m_active != on)
	{
		m_active = on;

		if (mp_plat_object)
		{
			// Engine call here
			mp_plat_object->SetActive(on);
		}
	}
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Sector::plat_is_active() const
{
	return m_active;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::CBBox & CPs2Sector::plat_get_bounding_box() const
{
	// Garrett: TODO: change to renderable bounding box
	Dbg_Assert(mp_coll_sector);
	Dbg_Assert(mp_coll_sector->GetGeometry());
	return mp_coll_sector->GetGeometry()->GetBBox();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Sector::plat_set_world_position(const Mth::Vector& pos)
{
	 // Set values
	 m_world_matrix[W] = pos;

	 // Engine call here
	 // TEMP, just update matrix of CGeomNode
	 update_engine_matrix();
 }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &	CPs2Sector::plat_get_world_position() const
{
	return m_world_matrix[W];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Sector::plat_set_y_rotation(Mth::ERot90 rot)
{
	// Engine call here
	// Garrett: TEMP just set the world matrix
	Mth::Matrix orig_rot_mat(m_world_matrix), rot_mat;
	float rad = (float) ((int) rot) * (Mth::PI * 0.5f);
	CreateRotateYMatrix(rot_mat, rad);

	orig_rot_mat[W] = Mth::Vector(0.0f, 0.0f, 0.0f, 1.0f);

	rot_mat = orig_rot_mat * rot_mat;
	//Dbg_Message("[X] (%f, %f, %f, %f)", rot_mat[X][X], rot_mat[X][Y],  rot_mat[X][Z],  rot_mat[X][W]);
	//Dbg_Message("[Y] (%f, %f, %f, %f)", rot_mat[Y][X], rot_mat[Y][Y],  rot_mat[Y][Z],  rot_mat[Y][W]);
	//Dbg_Message("[Z] (%f, %f, %f, %f)", rot_mat[Z][X], rot_mat[Z][Y],  rot_mat[Z][Z],  rot_mat[Z][W]);
	//Dbg_Message("[W] (%f, %f, %f, %f)", rot_mat[W][X], rot_mat[W][Y],  rot_mat[W][Z],  rot_mat[W][W]);
	
	m_world_matrix[X] = rot_mat[X];
	m_world_matrix[Y] = rot_mat[Y];
	m_world_matrix[Z] = rot_mat[Z];

	update_engine_matrix();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Sector::update_engine_matrix()
{
	if ( mp_plat_object )
	{
		mp_plat_object->SetMatrix(&m_world_matrix);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2Sector::plat_set_shatter(bool on)	
{
	// Set values
	m_shatter = on;

	// Engine call here
	if( on && mp_geom )
	{
		Shatter( mp_geom );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2Sector::plat_get_shatter() const
{
	return m_shatter;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSector * CPs2Sector::plat_clone(bool instance, CScene *p_dest_scene)
{
	// Copy into new sector
	CPs2Sector *p_new_sector = new CPs2Sector(*this);

	// Copy engine data
	if (p_new_sector && mp_plat_object)
	{
		if (instance)
		{
			p_new_sector->mp_plat_object = mp_plat_object->CreateInstance(&p_new_sector->m_world_matrix);
		} else {
			p_new_sector->mp_plat_object = mp_plat_object->CreateInstance(&p_new_sector->m_world_matrix);
			Dbg_Message("CPs2Sector: Can't create copy of CGeomNode yet");
			//p_new_sector->mp_plat_object = mp_plat_object->CreateCopy();
		}
	}

	return p_new_sector;
}

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/NGPS/p_NxSector.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxSector.h

#ifndef	__GFX_P_NX_SECTOR_H__
#define	__GFX_P_NX_SECTOR_H__

#include 	

#include 	
#include 	

namespace NxPs2
{
	class CGeomNode;
}

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CSector
// and we will also have a CXboxSector, CNgcSector, even a CPcSector
// maybe in the future we will have a CPS3Sector?
class	CPs2Sector : public CSector
{
public:
								CPs2Sector();
	virtual						~CPs2Sector();

	void						SetEngineObject(NxPs2::CGeomNode *p_object);
	NxPs2::CGeomNode *			GetEngineObject() const;

private:		// It's all private, as it is machine specific
	virtual void				plat_set_color(Image::RGBA rgba);
	virtual	Image::RGBA			plat_get_color() const;
	virtual void				plat_clear_color();

	virtual void				plat_set_visibility(uint32 mask);
	virtual	uint32				plat_get_visibility() const;

	virtual void				plat_set_active(bool on);	
	virtual	bool				plat_is_active() const;

	virtual const Mth::CBBox &	plat_get_bounding_box() const;

	virtual void				plat_set_world_position(const Mth::Vector& pos);
	virtual const Mth::Vector &	plat_get_world_position() const;

	virtual void				plat_set_y_rotation(Mth::ERot90 rot);

	virtual	void				plat_set_shatter(bool on);
	virtual	bool				plat_get_shatter() const;
	
	virtual CSector *			plat_clone(bool instance, CScene *p_dest_scene = NULL);

	// local functions
	void 						update_engine_matrix();

	NxPs2::CGeomNode *			mp_plat_object;

	Mth::Matrix					m_world_matrix;

	Image::RGBA					m_rgba;
	bool						m_active;
	bool						m_shatter;
};

/////////////////////////////////////////////////////////////////////////////////////
// Inlines
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CPs2Sector::SetEngineObject(NxPs2::CGeomNode *p_object)
{
	mp_plat_object = p_object;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline NxPs2::CGeomNode *	CPs2Sector::GetEngineObject() const
{
	return mp_plat_object;
}

} // Namespace Nx  			

#endif

================================================
FILE: Code/Gfx/NGPS/p_NxSprite.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxSprite.cpp

#include 	"Gfx/NGPS/p_NxSprite.h"
#include 	"Gfx/NGPS/p_NxTexture.h"
#include 	"Gfx/NGPS/p_NxWin2D.h"

namespace Nx
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CSprite

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Sprite::CPs2Sprite(CWindow2D *p_window) : CSprite(p_window)
{
	mp_plat_sprite = new NxPs2::SSprite();

	plat_initialize();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Sprite::~CPs2Sprite()
{
	delete mp_plat_sprite;
}

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Sprite::plat_initialize()
{
	plat_update_engine();
	plat_update_priority();
	plat_update_hidden();
	plat_update_window();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Sprite::plat_update_hidden()
{
	// Take sprite on or off draw list
	mp_plat_sprite->SetHidden(m_hidden);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Sprite::plat_update_engine()
{
	CPs2Texture *p_ps2_texture = static_cast(mp_texture);

	// Rebuild sprite primitives
	if (p_ps2_texture)
	{
		mp_plat_sprite->SetTexture(p_ps2_texture->GetSingleTexture());
	} else {
		mp_plat_sprite->SetTexture(NULL);
	}

	mp_plat_sprite->m_xpos		= m_pos_x;
	mp_plat_sprite->m_ypos		= m_pos_y;
	mp_plat_sprite->m_width		= m_width;
	mp_plat_sprite->m_height	= m_height;
	mp_plat_sprite->m_scale_x	= m_scale_x;
	mp_plat_sprite->m_scale_y	= m_scale_y;

	mp_plat_sprite->m_xhot		= ((m_anchor_x + 1.0f) * 0.5f) * (m_width);
	mp_plat_sprite->m_yhot		= ((m_anchor_y + 1.0f) * 0.5f) * (m_height);

	mp_plat_sprite->m_rot		= m_rotation;
	mp_plat_sprite->m_rgba		= *((uint32 *) &m_rgba);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Sprite::plat_update_priority()
{
	// Update draw list
	if (m_use_zbuffer)
	{
		mp_plat_sprite->SetZValue((uint32) m_zvalue);
	} else {
		mp_plat_sprite->SetPriority(m_priority);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Sprite::plat_update_window()
{
	CPs2Window2D *p_ps2_window = static_cast(mp_window);

	if (p_ps2_window)
	{
		mp_plat_sprite->SetScissorWindow(p_ps2_window->GetEngineWindow());
	} else {
		mp_plat_sprite->SetScissorWindow(NULL);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSprite::plat_enable_constant_z_value(bool enable)
{
	NxPs2::SSprite::EnableConstantZValue(enable);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSprite::plat_set_constant_z_value(Nx::ZBufferValue z)
{
	NxPs2::SSprite::SetConstantZValue(z);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::ZBufferValue	CSprite::plat_get_constant_z_value()
{
	return NxPs2::SSprite::GetConstantZValue();
}

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/NGPS/p_NxSprite.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.h

#ifndef	__GFX_P_NX_SPRITE_H__
#define	__GFX_P_NX_SPRITE_H__

#include 	"Gfx/NxSprite.h"
#include 	"Gfx/NGPS/NX/texture.h"
#include 	"Gfx/NGPS/NX/sprite.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Machine specific implementation of the CSprite
class	CPs2Sprite : public CSprite
{
public:
								CPs2Sprite(CWindow2D *p_window = NULL);
	virtual						~CPs2Sprite();

private:		// It's all private, as it is machine specific
	virtual void				plat_initialize();

	virtual void				plat_update_hidden();		// Tell engine of update
	virtual void				plat_update_engine();		// Update engine primitives
	virtual void				plat_update_priority();
	virtual void				plat_update_window();

	NxPs2::SSprite *			mp_plat_sprite;
};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/NGPS/p_NxTexMan.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxTexMan.cpp - PS2 platform specific interface to CTexMan
//
// This is PS2 SPECIFIC!!!!!!  So might get a bit messy

#include 

#include "gfx/NxTexMan.h"
#include "gfx/NGPS/p_NxTexture.h"

#include 

namespace NxPs2
{
	void WaitForRendering();
}


namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict *		CTexDictManager::s_plat_load_texture_dictionary(const char *p_tex_dict_name, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup)
{
	return new CPs2TexDict(p_tex_dict_name, is_level_data, texDictOffset, isSkin, forceTexDictLookup);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict *		CTexDictManager::s_plat_load_texture_dictionary(uint32 checksum, uint32* pData, int dataSize, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup)
{
	CPs2TexDict* pTexDict = new CPs2TexDict( checksum );
	pTexDict->LoadTextureDictionary(NULL, pData, dataSize, is_level_data, texDictOffset, isSkin, forceTexDictLookup);
	return pTexDict;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict *		CTexDictManager::s_plat_create_texture_dictionary(uint32 checksum)
{
	return new CPs2TexDict(checksum);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CTexDictManager::s_plat_unload_texture_dictionary(CTexDict *p_tex_dict)
{
	#if 0
	sceGsSyncPath(0, 0);		// To make sure it isn't being used
	#else
	NxPs2::WaitForRendering();
	#endif
	
	delete p_tex_dict;

	return true;
}


} 
 


================================================
FILE: Code/Gfx/NGPS/p_NxTexture.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxTexture.cpp

#include 	"Gfx/gfxutils.h"
#include 	"Gfx/Nx.h"
#include 	"Gfx/NGPS/p_NxTexture.h"
#include 	"Gfx/NGPS/PaletteGen.h"
#include	"Gfx/NGPS/nx/scene.h"
#include	"Gfx/NGPS/nx/sprite.h"
#include	
#include	
#include	

#define min(x, y) (((x) > (y))? (y): (x))
#define max(x, y) (((x) < (y))? (y): (x))

#define USE_FAST_COLOR_FIND 1
#define PRINT_TIMES 0
#define PRINT_FAST_TIMES 0

namespace Nx
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CTexture

bool				CPs2Texture::s_tables_initialized = false;
uint8				CPs2Texture::s_clut8_index_to_offset_table[256];
uint8				CPs2Texture::s_clut8_offset_to_index_table[256];

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Texture::CPs2Texture(bool loading_screen) :
	m_transparent(false),
	m_loading_screen(loading_screen),
	mp_single_texture(NULL),
	mp_group_texture(NULL),
	mp_orig_temp_32bit_image(NULL),
	mp_cur_temp_32bit_image(NULL),
	mp_temp_32bit_single_texture(NULL)
{
	m_width = m_height = m_bitdepth = m_clut_bitdepth = m_num_mipmaps = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Texture::CPs2Texture(const CPs2Texture & src_texture) :
	CTexture(src_texture)
{
	Dbg_MsgAssert(src_texture.mp_group_texture == NULL, ("Can't copy a CPs2Texture that is in a texture group"));

	m_width = src_texture.m_width;
	m_height = src_texture.m_height;
	m_bitdepth = src_texture.m_bitdepth;
	m_clut_bitdepth = src_texture.m_clut_bitdepth;
	m_num_mipmaps = src_texture.m_num_mipmaps;

	m_transparent = src_texture.m_transparent;
	m_loading_screen = src_texture.m_loading_screen;

	mp_group_texture = NULL;

	if (src_texture.mp_orig_temp_32bit_image)
	{
		mp_orig_temp_32bit_image = new uint32[m_width * m_height];
		memcpy(mp_orig_temp_32bit_image, src_texture.mp_orig_temp_32bit_image, m_width * m_height * sizeof(uint32));
	}
	else
	{
		mp_orig_temp_32bit_image = NULL;
	}

	if (src_texture.mp_cur_temp_32bit_image)
	{
		mp_cur_temp_32bit_image = new uint32[m_width * m_height];
		memcpy(mp_cur_temp_32bit_image, src_texture.mp_cur_temp_32bit_image, m_width * m_height * sizeof(uint32));
	}
	else
	{
		mp_cur_temp_32bit_image = NULL;
	}

	if (src_texture.mp_single_texture)
	{
		mp_single_texture = new NxPs2::SSingleTexture(*src_texture.mp_single_texture);
	}
	else
	{
		mp_single_texture = NULL;
	}

	if (src_texture.mp_temp_32bit_single_texture)
	{
		mp_temp_32bit_single_texture = new NxPs2::SSingleTexture(*src_texture.mp_temp_32bit_single_texture);
	}
	else
	{
		mp_temp_32bit_single_texture = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Texture::~CPs2Texture()
{
	// delete the actual single texture, if any
	if (mp_single_texture)
	{
		delete mp_single_texture;
	}

	if (mp_orig_temp_32bit_image)
	{
		delete mp_orig_temp_32bit_image;
	}

	if (mp_cur_temp_32bit_image)
	{
		delete mp_cur_temp_32bit_image;
	}

	if (mp_temp_32bit_single_texture)
	{
		delete mp_temp_32bit_single_texture;
	}

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	CPs2Texture::sInitTables()
{
    if (!s_tables_initialized)
	{
		uint i, j, idx;

		idx = 0;
        for(i = 0; i < 256; i+=32)
		{
            for(j = i; j < i+8; j++)
                s_clut8_index_to_offset_table[j] = (uint8) idx++;
            for(j = i+16; j < i+16+8; j++)
                s_clut8_index_to_offset_table[j] = (uint8) idx++;
            for(j = i+8;  j < i+8+8;  j++)
                s_clut8_index_to_offset_table[j] = (uint8) idx++;
            for(j = i+24; j < i+24+8; j++)
                s_clut8_index_to_offset_table[j] = (uint8) idx++;
        }

		idx = 0;
		for(i = 0; i < 256; i+=32)
		{
			for(j = i; j < i+8; j++)
				s_clut8_offset_to_index_table[idx++] = (uint8) j;
			for(j = i+16; j < i+16+8; j++)
				s_clut8_offset_to_index_table[idx++] = (uint8) j;
			for(j = i+8;  j < i+8+8;  j++)
				s_clut8_offset_to_index_table[idx++] = (uint8) j;
			for(j = i+24; j < i+24+8; j++)
				s_clut8_offset_to_index_table[idx++] = (uint8) j;
		}
	
        s_tables_initialized = true;
	}
}

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram)
{
	Dbg_Assert(mp_group_texture == NULL);

	// call engine
	mp_single_texture = new NxPs2::SSingleTexture(p_texture_name, sprite, alloc_vram, m_loading_screen);
	if (mp_single_texture)
	{
		m_width			= mp_single_texture->GetOrigWidth();	// Garrett: This is probably not what we want, but CSprite
		m_height		= mp_single_texture->GetOrigHeight();  // needs this.  Probably will need to change CTexture.
		m_bitdepth		= mp_single_texture->GetBitdepth();
		m_clut_bitdepth	= mp_single_texture->GetClutBitdepth();
		m_num_mipmaps	= mp_single_texture->GetNumMipmaps();
		m_transparent	= mp_single_texture->IsTransparent();
	}

	return mp_single_texture != NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_load_texture_from_buffer(uint8* p_buffer, int buffer_size, bool sprite, bool alloc_vram)
{
	Dbg_Assert(mp_group_texture == NULL);

	// call engine
	mp_single_texture = new NxPs2::SSingleTexture(p_buffer, buffer_size, sprite, alloc_vram, m_loading_screen);
	if (mp_single_texture)
	{
		m_width			= mp_single_texture->GetOrigWidth();	// Garrett: This is probably not what we want, but CSprite
		m_height		= mp_single_texture->GetOrigHeight();  // needs this.  Probably will need to change CTexture.
		m_bitdepth		= mp_single_texture->GetBitdepth();
		m_clut_bitdepth	= mp_single_texture->GetClutBitdepth();
		m_num_mipmaps	= mp_single_texture->GetNumMipmaps();
		m_transparent	= mp_single_texture->IsTransparent();
	}

	return mp_single_texture != NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_replace_texture(CTexture *p_texture)
{								 	
	Dbg_Assert(p_texture);
	Dbg_Assert(mp_group_texture);

	CPs2Texture *p_ps2_texture = static_cast(p_texture);
	Dbg_Assert(p_ps2_texture->mp_single_texture);

	if ((m_width != p_ps2_texture->m_width) ||
		(m_height != p_ps2_texture->m_height) ||
		(m_bitdepth != p_ps2_texture->m_bitdepth) ||
		(m_num_mipmaps != p_ps2_texture->m_num_mipmaps))
	{
		Dbg_Message("Original size (%d, %d); Replacement size (%d, %d)", m_width, m_height, p_ps2_texture->m_width, p_ps2_texture->m_height);
		Dbg_Message("Original bitdepth %d; Replacement bitdepth %d", m_bitdepth, p_ps2_texture->m_bitdepth);
		Dbg_Message("Original num mipmaps %d; Replacement num mipmaps %d", m_num_mipmaps, p_ps2_texture->m_num_mipmaps);
		return false;
	}

#if 0
	if ((m_bitdepth <= 8) && (m_clut_bitdepth < p_ps2_texture->m_clut_bitdepth))
	{
		Dbg_Message("Original clut bitdepth %d; Replacement clut bitdepth %d", m_clut_bitdepth, p_ps2_texture->m_clut_bitdepth);
		return false;
	}
#endif

	// Copy texture
	uint8 *p_orig_tex_buffer = mp_group_texture->GetTextureBuffer();
	uint8 *p_new_tex_buffer = p_ps2_texture->mp_single_texture->GetTextureBuffer();
	if (mp_group_texture->HasSwizzleMip())
	{
		mp_group_texture->ReplaceTextureData(p_new_tex_buffer);
	}
	else
	{
		memcpy(p_orig_tex_buffer, p_new_tex_buffer, p_ps2_texture->mp_single_texture->GetTextureBufferSize());
	}

	// And clut (if any)
	uint32 *p_orig_clut_buffer = (uint32 *) mp_group_texture->GetClutBuffer();
	if (p_orig_clut_buffer)
	{
		uint32 *p_new_clut_buffer = (uint32 *) p_ps2_texture->mp_single_texture->GetClutBuffer();
		Dbg_Assert(p_new_clut_buffer);

		if (m_clut_bitdepth == p_ps2_texture->m_clut_bitdepth)
		{
			memcpy(p_orig_clut_buffer, p_new_clut_buffer, p_ps2_texture->mp_single_texture->GetClutBufferSize());
		} else if ((m_clut_bitdepth == 32) && (p_ps2_texture->m_clut_bitdepth == 16))
		{
			uint16 *p_clut_16_buffer = (uint16 *) p_new_clut_buffer;
			Image::RGBA new_color;
			new_color.a = 0x80;

			int num_colors = p_ps2_texture->mp_single_texture->GetClutBufferSize() / 2;
			for (int i = 0; i < num_colors; i++)
			{
				// Convert from 16 to 32 bit
				new_color.r = ((*(p_clut_16_buffer)   >>  0) & 0x1f) << 3;
				new_color.g = ((*(p_clut_16_buffer)   >>  5) & 0x1f) << 3;
				new_color.b = ((*(p_clut_16_buffer++) >> 10) & 0x1f) << 3;

				//Dbg_Message("Replacing color #%d %x with %x", i, *p_orig_clut_buffer, *((uint32 *) &new_color));
				*(p_orig_clut_buffer++) = *((uint32 *) &new_color);
			}
		} else if ((m_clut_bitdepth == 16) && (p_ps2_texture->m_clut_bitdepth == 32))
		{
			uint16 *p_clut_16_buffer = (uint16 *) p_orig_clut_buffer;
			uint16 new_color;
			Image::RGBA *p_full_color;

			int num_colors = p_ps2_texture->mp_single_texture->GetClutBufferSize() / 4;
			for (int i = 0; i < num_colors; i++)
			{
				p_full_color = (Image::RGBA *) p_new_clut_buffer++;
				new_color = 0x8000 | (p_full_color->r >> 3) | ( (p_full_color->g >> 3) << 5 ) | ( (p_full_color->b >> 3) << 10 );
				*(p_clut_16_buffer++) = new_color;		
			}
		} else {
			Dbg_Message("Can't handle this combination: clut bitdepth %d; Replacement clut bitdepth %d", m_clut_bitdepth, p_ps2_texture->m_clut_bitdepth);
			return false;
		}

		//Dbg_Message("Original clut size %d; Replacement clut size %d", mp_group_texture->GetClutBufferSize(), p_ps2_texture->mp_single_texture->GetClutBufferSize());
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_generate_32bit_image(bool renderable, bool store_original)
{
	Dbg_MsgAssert(!mp_cur_temp_32bit_image, ("Can't generate temp 32-bit image: one already exists"));

	// For now, just coded up the 8-bit image with 32-bit clut
	Dbg_Assert(m_bitdepth == 8);
	Dbg_MsgAssert(m_clut_bitdepth == 32, ("Clut bitdepth is only %d, not 32", m_clut_bitdepth));
	Dbg_Assert(mp_single_texture);
	Dbg_Assert(s_tables_initialized);

	// Allocate buffer
	mp_cur_temp_32bit_image = new uint32[m_width * m_height];
	uint32 *p_pixel_buffer = mp_cur_temp_32bit_image;

	// Get source buffers
	uint8 *p_orig_tex_buffer = mp_single_texture->GetTextureBuffer();
	// And clut (if any)
	uint32 *p_orig_clut_buffer = (uint32 *) mp_single_texture->GetClutBuffer();

	// Move texture buffer to start (and flip y)
	p_orig_tex_buffer += (m_height - 1) * m_width;

	for (uint h = 0; h < m_height; h++)
	{
		for (uint w = 0; w < m_width; w++)
		{
			// Copies from 8-bit to 32-bit
			*p_pixel_buffer++ = p_orig_clut_buffer[ s_clut8_index_to_offset(p_orig_tex_buffer[w]) ];
		}
		p_orig_tex_buffer -= m_width;	// Negative because were flipping the lines
	}

	// Copy to original buffer
	if (store_original)
	{
		mp_orig_temp_32bit_image = new uint32[m_width * m_height];
		memcpy(mp_orig_temp_32bit_image, mp_cur_temp_32bit_image, m_width * m_height * sizeof(uint32));
	}

	// Allocate a SSingleTexture
	if (renderable)
	{
		mp_temp_32bit_single_texture = new NxPs2::SSingleTexture((uint8 *) mp_cur_temp_32bit_image, m_width, m_height, 32,
																 m_clut_bitdepth, m_num_mipmaps, true, true, m_loading_screen);
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_put_32bit_image_into_texture(bool new_palette)
{
	Dbg_MsgAssert(mp_cur_temp_32bit_image, ("No temp 32-bit image"));

	// For now, just coded up the 8-bit image with 32-bit clut
	Dbg_Assert(m_bitdepth == 8);
	Dbg_Assert(m_clut_bitdepth == 32);
	Dbg_Assert(mp_single_texture);
	Dbg_Assert(s_tables_initialized);

	// Generate a new palette
	if (new_palette)
	{
		GeneratePalette((Image::RGBA *) mp_single_texture->GetClutBuffer(), (uint8 *) mp_cur_temp_32bit_image, m_width, m_height, 32, 1 << m_bitdepth);
	}

	// Get buffers
	uint8 *p_tex_buffer = mp_single_texture->GetTextureBuffer();
	uint32 *p_pixel_buffer = mp_cur_temp_32bit_image;

	// Move texture buffer to start (and flip y)
	p_tex_buffer += (m_height - 1) * m_width;

#if USE_FAST_COLOR_FIND
#if PRINT_FAST_TIMES
	uint32 start_time = Tmr::GetTimeInUSeconds();
#endif // PRINT_FAST_TIMES

	setup_fast_clut_color_find(false);

#if PRINT_FAST_TIMES
	uint32 end_time = Tmr::GetTimeInUSeconds();
	Dbg_Message("plat_put_32bit_image_into_texture fast setup time %d us", end_time - start_time);
#endif // PRINT_FAST_TIMES
#endif // USE_FAST_COLOR_FIND

	for (uint h = 0; h < m_height; h++)
	{
		for (uint w = 0; w < m_width; w++)
		{
			// Copies from 32-bit to 8-bit
#if USE_FAST_COLOR_FIND
			p_tex_buffer[w] = s_clut8_offset_to_index( fast_find_closest_clut_color( *((Image::RGBA *) p_pixel_buffer++) ) );
#else
			p_tex_buffer[w] = s_clut8_offset_to_index( find_closest_clut_color( *((Image::RGBA *) p_pixel_buffer++) ) );
#endif // USE_FAST_COLOR_FIND
		}
		p_tex_buffer -= m_width;		// Negative because were flipping the lines
	}

#if USE_FAST_COLOR_FIND
	cleanup_fast_clut_color_find();

#if PRINT_FAST_TIMES
	end_time = Tmr::GetTimeInUSeconds();
	Dbg_Message("plat_put_32bit_image_into_texture fast total time %d us", end_time - start_time);
#endif // PRINT_FAST_TIMES
#endif // USE_FAST_COLOR_FIND

	// Delete buffers
	delete [] mp_cur_temp_32bit_image;
	mp_cur_temp_32bit_image = NULL;

	if (mp_orig_temp_32bit_image)
	{
		delete [] mp_orig_temp_32bit_image;
		mp_orig_temp_32bit_image = NULL;
	}

	if (mp_temp_32bit_single_texture)
	{
		delete mp_temp_32bit_single_texture;
		mp_temp_32bit_single_texture = NULL;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2Texture::s_get_nearest_pixels_from_32bit_texture(Image::RGBA p_nearest[][2], uint32 *p_buffer,
															 uint16 x_pos, uint16 y_pos, uint16 width, uint16 height)
{
	int next_x = (x_pos >= (width - 1)) ? 0 : 1;		// Clamp x if at end
	bool clamp_y = (y_pos >= (height - 1));				// And same for y

	p_buffer += (y_pos * width) + x_pos;

	// First row
	p_nearest[0][0] = *((Image::RGBA *) p_buffer);
	p_nearest[0][1] = *((Image::RGBA *) &p_buffer[next_x]);

	if (!clamp_y)
	{
		p_buffer += width;
	}

	// Second row
	p_nearest[1][0] = *((Image::RGBA *) p_buffer);
	p_nearest[1][1] = *((Image::RGBA *) &p_buffer[next_x]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2Texture::s_get_region(uint8 *p_buffer, uint16 x_pos, uint16 y_pos, uint16 tex_width, uint16 tex_height,
								  uint8 *p_region, uint16 region_width, uint16 region_height, uint16 bitdepth)
{
	int bytes_per_pixel = bitdepth / 8;
	int copy_bytes;
	int width_in_bytes;
	int stride_in_bytes;

	// Clamp region values
	int clamp_region_width = min(region_width, tex_width - x_pos);
	int clamp_region_height = min(region_height, tex_height - y_pos);
	Dbg_Assert(clamp_region_width >= 0);
	Dbg_Assert(clamp_region_height >= 0);

	// Move texture buffer to start
	if (bytes_per_pixel)
	{
		p_buffer += ((y_pos * tex_width) + x_pos) * bytes_per_pixel;
		copy_bytes = clamp_region_width * bytes_per_pixel;
		width_in_bytes = region_width * bytes_per_pixel;
		stride_in_bytes = tex_width * bytes_per_pixel;
	}
	else // 4-bit
	{
		p_buffer += ((y_pos * tex_width) + x_pos) / 2;
		copy_bytes = clamp_region_width / 2;
		width_in_bytes = region_width / 2;
		stride_in_bytes = tex_width / 2;
	}

	//Dbg_Message("s_get_region(): xpos %d, ypos %d, tex_width %d, tex_height %d, region_width %d, region_height %d, width_in_bytes %d, stride_in_bytes %d, bitdepth %d",
	//			x_pos, y_pos, tex_width, tex_height, region_width, region_height, width_in_bytes, stride_in_bytes, bitdepth);
	for (int h = 0; h < clamp_region_height; h++)
	{
		// Copy each line
		memcpy(p_region, p_buffer, copy_bytes);
		p_region += width_in_bytes;
		p_buffer += stride_in_bytes;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2Texture::s_put_region(uint8 *p_buffer, uint16 x_pos, uint16 y_pos, uint16 tex_width, uint16 tex_height,
								  uint8 *p_region, uint16 region_width, uint16 region_height, uint16 bitdepth)
{
	int bytes_per_pixel = bitdepth / 8;
	int copy_bytes;
	int width_in_bytes;
	int stride_in_bytes;

	// Clamp region values
	int clamp_region_width = min(region_width, tex_width - x_pos);
	int clamp_region_height = min(region_height, tex_height - y_pos);
	Dbg_Assert(clamp_region_width >= 0);
	Dbg_Assert(clamp_region_height >= 0);

	// Move texture buffer to start
	if (bytes_per_pixel)
	{
		p_buffer += ((y_pos * tex_width) + x_pos) * bytes_per_pixel;
		copy_bytes = clamp_region_width * bytes_per_pixel;
		width_in_bytes = region_width * bytes_per_pixel;
		stride_in_bytes = tex_width * bytes_per_pixel;
	}
	else // 4-bit
	{
		p_buffer += ((y_pos * tex_width) + x_pos) / 2;
		copy_bytes = clamp_region_width / 2;
		width_in_bytes = region_width / 2;
		stride_in_bytes = tex_width / 2;
	}

	//Dbg_Message("s_put_region(): xpos %d, ypos %d, tex_width %d, tex_height %d, region_width %d, region_height %d, width_in_bytes %d, stride_in_bytes %d, bitdepth %d",
	//			x_pos, y_pos, tex_width, tex_height, region_width, region_height, width_in_bytes, stride_in_bytes, bitdepth);
	for (int h = 0; h < clamp_region_height; h++)
	{
		// Copy each line
		memcpy(p_buffer, p_region, copy_bytes);
		p_region += width_in_bytes;
		p_buffer += stride_in_bytes;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2Texture::s_scale_texture(uint8 *p_buffer, uint16 width, uint16 height,
									 uint8 *p_result_buffer, uint16 new_width, uint16 new_height, uint16 bitdepth)
{
	Dbg_MsgAssert(bitdepth > 8, ("Can't scale a paletted texture.  Convert to unpaletted first."));
	Dbg_Assert(bitdepth == 32);		// We'll worry about 16 and 24 bit later.

#if PRINT_TIMES
	static uint32 total_time = 0;
	uint32 start_time = Tmr::GetTimeInUSeconds();
#endif

	float orig_coord[2] = { 0.0f, 0.0f };
	float delta_dim[2];
	float delta_coord[2] = { 0.0f, 0.0f };
	uint16 orig_index[2] = { 0, 0 };

	Image::RGBA closest_pixels[2][2];
	float pixel_portion[2][2];
	Image::RGBA combined_pixel;

	delta_dim[X] = (float) width / (float) new_width;
	delta_dim[Y] = (float) height / (float) new_height;

	for (int h = 0; h < new_height; h++)
	{
		orig_index[Y] = (uint16) orig_coord[Y];			// Get integer version
		delta_coord[Y] = orig_coord[Y] - orig_index[Y];	// And now get fractional part

		for (int w = 0; w < new_width; w++)
		{
			orig_index[X] = (uint16) orig_coord[X];			// Get integer version
			delta_coord[X] = orig_coord[X] - orig_index[X];	// And now get fractional part

			// Calculate the pixel portions
			pixel_portion[0][0] = (1.0f - delta_coord[X]) * (1.0f - delta_coord[Y]);
			pixel_portion[0][1] =        (delta_coord[X]) * (1.0f - delta_coord[Y]);
			pixel_portion[1][0] = (1.0f - delta_coord[X]) *        (delta_coord[Y]);
			pixel_portion[1][1] =        (delta_coord[X]) *        (delta_coord[Y]);
				
			Dbg_MsgAssert((orig_index[X] + 0) < width, ("X coord out of range: %d", orig_index[X]));
			Dbg_MsgAssert((orig_index[Y] + 0) < height, ("Y coord out of range: %d", orig_index[Y]));

			// Get the 4 nearest pixels
			s_get_nearest_pixels_from_32bit_texture(closest_pixels, (uint32 *) p_buffer, orig_index[X], orig_index[Y], width, height);

			// Calculate each color
			combined_pixel.r = (uint8) ((pixel_portion[0][0] * (float) closest_pixels[0][0].r) +
									    (pixel_portion[0][1] * (float) closest_pixels[0][1].r) +
									    (pixel_portion[1][0] * (float) closest_pixels[1][0].r) +
									    (pixel_portion[1][1] * (float) closest_pixels[1][1].r));
			combined_pixel.g = (uint8) ((pixel_portion[0][0] * (float) closest_pixels[0][0].g) +
									    (pixel_portion[0][1] * (float) closest_pixels[0][1].g) +
									    (pixel_portion[1][0] * (float) closest_pixels[1][0].g) +
									    (pixel_portion[1][1] * (float) closest_pixels[1][1].g));
			combined_pixel.b = (uint8) ((pixel_portion[0][0] * (float) closest_pixels[0][0].b) +
									    (pixel_portion[0][1] * (float) closest_pixels[0][1].b) +
									    (pixel_portion[1][0] * (float) closest_pixels[1][0].b) +
									    (pixel_portion[1][1] * (float) closest_pixels[1][1].b));
			combined_pixel.a = (uint8) ((pixel_portion[0][0] * (float) closest_pixels[0][0].a) +
									    (pixel_portion[0][1] * (float) closest_pixels[0][1].a) +
									    (pixel_portion[1][0] * (float) closest_pixels[1][0].a) +
									    (pixel_portion[1][1] * (float) closest_pixels[1][1].a));

			// And copy
			*((uint32 *) p_result_buffer)++ = *((uint32 *) &(combined_pixel));

			// Advance to next coord
			orig_coord[X] += delta_dim[X];
		}

		orig_coord[X] = 0.0f;
		orig_coord[Y] += delta_dim[Y];
	}

#if PRINT_TIMES
	uint32 end_time = Tmr::GetTimeInUSeconds();
	total_time += end_time - start_time;
	Dbg_Message("scale_texture Update time %d us; Total Time %d", end_time - start_time, total_time);
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2Texture::s_combine_adjacent_borders(uint8 *p_first_rect, uint8 *p_second_rect, uint16 first_width,
												uint16 first_height, uint16 second_stride, float first_portion,
												int split_axis, uint16 bitdepth)
{
	Dbg_MsgAssert(bitdepth > 8, ("Can't adjust the border of a paletted texture.  Convert to unpaletted first."));
	Dbg_Assert(bitdepth == 32);		// We'll worry about 16 and 24 bit later.

	Image::RGBA *p_rect_pixel[2] = { (Image::RGBA *) p_first_rect, (Image::RGBA *) p_second_rect };
	float second_portion = 1.0f - first_portion;

	int num_pixels, inc_pixels;

	// Init
	if (split_axis == X)
	{
		num_pixels = first_width;
		inc_pixels = 1;
	}
	else
	{
		Dbg_Assert(first_width == second_stride);

		num_pixels = first_height;
		inc_pixels = second_stride;
	}

	// Combine the pixels
	for (int pidx = 0; pidx < num_pixels; pidx++)
	{
		p_rect_pixel[1]->r = (uint8) ( (first_portion * (float) p_rect_pixel[0]->r) + (second_portion * (float) p_rect_pixel[1]->r) );
		p_rect_pixel[1]->g = (uint8) ( (first_portion * (float) p_rect_pixel[0]->g) + (second_portion * (float) p_rect_pixel[1]->g) );
		p_rect_pixel[1]->b = (uint8) ( (first_portion * (float) p_rect_pixel[0]->b) + (second_portion * (float) p_rect_pixel[1]->b) );
		p_rect_pixel[1]->a = (uint8) ( (first_portion * (float) p_rect_pixel[0]->a) + (second_portion * (float) p_rect_pixel[1]->a) );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static const int ALPHA_WEIGHT = 2;    // how much more weight to give to alpha
uint8 	CPs2Texture::find_closest_clut_color(Image::RGBA rgba)
{
	// For now, just coded up the 8-bit image with 32-bit clut
	Dbg_Assert(m_bitdepth == 8);
	Dbg_Assert(m_clut_bitdepth == 32);
	Dbg_Assert(mp_single_texture);

	int clutsize = 1 << m_bitdepth;

	Image::RGBA *p_clut_buffer = (Image::RGBA *) mp_single_texture->GetClutBuffer();

	Dbg_Assert(p_clut_buffer);

    int delR, delG, delB, delA;
    int bestDist, curDist;
    uint8 bestIdx = 0;

    bestDist = 0x7FFFFFFF;

    for (int i = 0; i < clutsize; i++)
	{
        delR = (int) (p_clut_buffer)->r   - (int) rgba.r;
        delG = (int) (p_clut_buffer)->g   - (int) rgba.g;
        delB = (int) (p_clut_buffer)->b   - (int) rgba.b;
        delA = (int) (p_clut_buffer++)->a - (int) rgba.a;

        // Calculate 4-dimensional distance between color components
        curDist = (delR * delR) + (delG * delG) + (delB * delB) +
                  (delA * delA * ALPHA_WEIGHT);

        // Choose the index with the smallest distance from src color
        if (curDist < bestDist)
		{
            bestDist = curDist;
            bestIdx = i;
        }
    }

    return bestIdx;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Holds clut index array start and size
struct SClutEntries
{
	uint16	m_clut_buffer_index;
	uint8	m_num_clut_entries;
};

static const int s_axis_level_bits = 3;			// Significant bits used to break down each color into the tree
static const int s_axis_levels = 1 << s_axis_level_bits;
static bool s_use_alpha_axis;
static uint8 *sp_clut_index_buffer = NULL;

//static int s_num_color_searches;
//static int s_num_color_compares;

// Array is of form [R][G][B][A]
static SClutEntries s_palette_octree[s_axis_levels][s_axis_levels][s_axis_levels][s_axis_levels];

void 	CPs2Texture::setup_fast_clut_color_find(bool use_alpha)
{
	// For now, just coded up the 8-bit image with 32-bit clut
	Dbg_Assert(m_bitdepth == 8);
	Dbg_Assert(m_clut_bitdepth == 32);
	Dbg_Assert(mp_single_texture);
	Dbg_Assert(!sp_clut_index_buffer);

	s_use_alpha_axis = use_alpha;

	int clutsize = 1 << m_bitdepth;
	int range = clutsize >> s_axis_level_bits;
	int alpha_axis_levels = (s_use_alpha_axis) ? s_axis_levels : 1;
	int index_buffer_size = s_axis_levels * s_axis_levels * s_axis_levels * alpha_axis_levels * (clutsize / 2);	// Could be made smaller

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	sp_clut_index_buffer = new uint8[index_buffer_size];
	Mem::Manager::sHandle().PopContext();

	int min_level[4];
	int max_level[4];

	const int overlap = (range >> 1);
	const int min_start = -overlap;
	const int max_start = range + overlap;
	const int max_alpha_start = (s_use_alpha_axis) ? max_start : clutsize;

	int buffer_index = 0;

	min_level[0] = min_start;
	max_level[0] = max_start;

	for (int r = 0; r < s_axis_levels; r++)
	{
		min_level[1] = min_start;
		max_level[1] = max_start;

		for (int g = 0; g < s_axis_levels; g++)
		{
			min_level[2] = min_start;
			max_level[2] = max_start;

			for (int b = 0; b < s_axis_levels; b++)
			{
				min_level[3] = min_start;
				max_level[3] = max_alpha_start;

				for (int a = 0; a < alpha_axis_levels; a++)
				{
					SClutEntries *p_palette_node = &s_palette_octree[r][g][b][a];
					p_palette_node->m_clut_buffer_index = buffer_index;
					p_palette_node->m_num_clut_entries = 0;
					Image::RGBA *p_clut_buffer = (Image::RGBA *) mp_single_texture->GetClutBuffer();

					Dbg_MsgAssert(buffer_index < (index_buffer_size - clutsize), ("Running out of index buffer space"));

					for (int index = 0; index < clutsize; index++, p_clut_buffer++)
					{
						if ((p_clut_buffer->r >= min_level[0]) && (p_clut_buffer->r <= max_level[0]) &&
							(p_clut_buffer->g >= min_level[1]) && (p_clut_buffer->g <= max_level[1]) &&
							(p_clut_buffer->b >= min_level[2]) && (p_clut_buffer->b <= max_level[2]) &&
							(p_clut_buffer->a >= min_level[3]) && (p_clut_buffer->a <= max_level[3]))
						{
							p_palette_node->m_num_clut_entries++;
							sp_clut_index_buffer[buffer_index++] = index;
						}

					} // for index

					min_level[3] += range;
					max_level[3] += range;

				} // for a

				min_level[2] += range;
				max_level[2] += range;

			} // for b

			min_level[1] += range;
			max_level[1] += range;

		} // for g

		min_level[0] += range;
		max_level[0] += range;

	} // for r

	//s_num_color_searches = 0;
	//s_num_color_compares = 0;
}


uint8 	CPs2Texture::fast_find_closest_clut_color(Image::RGBA rgba)
{
	// For now, just coded up the 8-bit image with 32-bit clut
	Dbg_Assert(m_bitdepth == 8);
	Dbg_Assert(m_clut_bitdepth == 32);
	Dbg_Assert(mp_single_texture);
	Dbg_Assert(sp_clut_index_buffer);

	int range_bits = m_bitdepth - s_axis_level_bits;

	int r_index = rgba.r >> range_bits;
	int g_index = rgba.g >> range_bits;
	int b_index = rgba.b >> range_bits;
	int a_index = (s_use_alpha_axis) ? rgba.a >> range_bits : 0;

	SClutEntries *p_palette_node = &s_palette_octree[r_index][g_index][b_index][a_index];

	//s_num_color_searches++;
	if (p_palette_node->m_num_clut_entries == 0)
	{
		//s_num_color_compares += 256;
		return find_closest_clut_color(rgba);
	}
	else
	{
		uint8 *p_palette_index = &sp_clut_index_buffer[p_palette_node->m_clut_buffer_index];
		Image::RGBA *p_clut_buffer = (Image::RGBA *) mp_single_texture->GetClutBuffer();
		Dbg_Assert(p_clut_buffer);

		int delR, delG, delB, delA;
		int bestDist, curDist;
		uint8 bestIdx = 0;

		bestDist = 0x7FFFFFFF;

		for (int entry_index = 0; entry_index < p_palette_node->m_num_clut_entries; entry_index++, p_palette_index++)
		{
			Image::RGBA *p_clut_color = &p_clut_buffer[*p_palette_index];

			delR = (int) p_clut_color->r - (int) rgba.r;
			delG = (int) p_clut_color->g - (int) rgba.g;
			delB = (int) p_clut_color->b - (int) rgba.b;
			delA = (int) p_clut_color->a - (int) rgba.a;

			// Calculate 4-dimensional distance between color components
			curDist = (delR * delR) + (delG * delG) + (delB * delB) +
					  (delA * delA * ALPHA_WEIGHT);

			// Choose the index with the smallest distance from src color
			if (curDist < bestDist)
			{
				bestDist = curDist;
				bestIdx = *p_palette_index;
			}
		}

		//s_num_color_compares += p_palette_node->m_num_clut_entries;

		return bestIdx;
	}
}

void 	CPs2Texture::cleanup_fast_clut_color_find()
{
	Dbg_Assert(sp_clut_index_buffer);

	//Dbg_Message("Average color compares per search: %f", (float) s_num_color_compares / (float) s_num_color_searches);

	delete [] sp_clut_index_buffer;
	sp_clut_index_buffer = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2Texture::copy_region_to_32bit_buffer(uint32 *p_pixel_buffer, uint16 x_pos, uint16 y_pos,
												 uint16 width, uint16 height)
{
	// For now, just coded up the 8-bit image with 32-bit clut
	Dbg_Assert(m_bitdepth == 8);
	Dbg_MsgAssert(m_clut_bitdepth == 32, ("Clut bitdepth is only %d, not 32", m_clut_bitdepth));
	Dbg_Assert(mp_single_texture);
	Dbg_Assert(s_tables_initialized);

#if PRINT_TIMES
	static uint32 total_time = 0;
	uint32 start_time = Tmr::GetTimeInUSeconds();
#endif

	if (mp_cur_temp_32bit_image)
	{
		// Get source buffer
		uint32 *p_orig_tex_buffer = mp_cur_temp_32bit_image;

		// Move texture buffer to start
		p_orig_tex_buffer += (y_pos * m_width) + x_pos;

		for (uint h = 0; h < height; h++)
		{
			memcpy(p_pixel_buffer, p_orig_tex_buffer, width * sizeof(uint32));
			p_pixel_buffer += width;
			p_orig_tex_buffer += m_width;
		}
	}
	else
	{
		// Get source buffers
		uint8 *p_orig_tex_buffer = mp_single_texture->GetTextureBuffer();
		// And clut (if any)
		uint32 *p_orig_clut_buffer = (uint32 *) mp_single_texture->GetClutBuffer();

		// Move texture buffer to start (and flip y)
		p_orig_tex_buffer += ((m_height - 1 - y_pos) * m_width) + x_pos;

		for (uint h = 0; h < height; h++)
		{
			for (uint w = 0; w < width; w++)
			{
				// Copies from 8-bit to 32-bit
				*p_pixel_buffer++ = p_orig_clut_buffer[ s_clut8_index_to_offset(p_orig_tex_buffer[w]) ];
			}
			p_orig_tex_buffer -= m_width;	// Negative because were flipping the lines
		}
	}

#if PRINT_TIMES
	uint32 end_time = Tmr::GetTimeInUSeconds();
	total_time += end_time - start_time;
	Dbg_Message("copy_region_to_32bit_buffer Update time %d us; Total Time %d", end_time - start_time, total_time);
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2Texture::blit_32bit_buffer_to_texture(uint32 *p_pixel_buffer, uint16 x_pos, uint16 y_pos,
												  uint16 width, uint16 height)
{
	// For now, just coded up the 8-bit image with 32-bit clut
	Dbg_Assert(m_bitdepth == 8);
	Dbg_Assert(m_clut_bitdepth == 32);
	Dbg_Assert(mp_single_texture);
	Dbg_Assert(s_tables_initialized);

#if PRINT_TIMES
	static uint32 total_time = 0;
	uint32 start_time = Tmr::GetTimeInUSeconds();
#endif

	if (mp_cur_temp_32bit_image)
	{
		// Get source buffer
		uint32 *p_tex_buffer = mp_cur_temp_32bit_image;

		// Move texture buffer to start
		p_tex_buffer += (y_pos * m_width) + x_pos;

		for (uint h = 0; h < height; h++)
		{
			memcpy(p_tex_buffer, p_pixel_buffer, width * sizeof(uint32));
			p_pixel_buffer += width;
			p_tex_buffer += m_width;
		}
	}
	else
	{
		// Get source buffers
		uint8 *p_tex_buffer = mp_single_texture->GetTextureBuffer();

		// Move texture buffer to start (and flip y)
		p_tex_buffer += ((m_height - 1 - y_pos) * m_width) + x_pos;

		for (uint h = 0; h < height; h++)
		{
			for (uint w = 0; w < width; w++)
			{
				// Copies from 32-bit to 8-bit
				p_tex_buffer[w] = s_clut8_offset_to_index( find_closest_clut_color( *((Image::RGBA *) p_pixel_buffer++) ) );
			}
			p_tex_buffer -= m_width;		// Negative because were flipping the lines
		}
	}

#if PRINT_TIMES
	uint32 end_time = Tmr::GetTimeInUSeconds();
	total_time += end_time - start_time;
	Dbg_Message("blit_32bit_buffer_to_texture Update time %d us; Total Time %d", end_time - start_time, total_time);
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2Texture::set_32bit_pixel_to_texture(uint32 pixel, uint16 x_pos, uint16 y_pos)
{
	// For now, just coded up the 8-bit image with 32-bit clut
	Dbg_Assert(m_bitdepth == 8);
	Dbg_Assert(m_clut_bitdepth == 32);
	Dbg_Assert(mp_single_texture);
	Dbg_Assert(s_tables_initialized);

	if (mp_cur_temp_32bit_image)
	{
		// Set pixel in buffer
		mp_cur_temp_32bit_image[(y_pos * m_width) + x_pos] = pixel;
	}
	else
	{
		// Get source buffers
		uint8 *p_tex_buffer = mp_single_texture->GetTextureBuffer();

		// Move texture buffer to start (and flip y)
		p_tex_buffer[((m_height - 1 - y_pos) * m_width) + x_pos] = s_clut8_offset_to_index( find_closest_clut_color( *((Image::RGBA *) &pixel) ) );
	}

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_offset(int x_pixels, int y_pixels, bool use_fill_color, Image::RGBA fill_color)
{
	int region_start_pos[2];
	int region_end_pos[2];
	int region_width;
	int region_height;

	// Figure out X info
	if (x_pixels >= 0)
	{
		region_start_pos[X] = 0;
		region_end_pos[X] = x_pixels;
		region_width = m_width - x_pixels;
	}
	else
	{
		region_start_pos[X] = -x_pixels;
		region_end_pos[X] = 0;
		region_width = m_width - (-x_pixels);
	}

	// Figure out Y info
	if (y_pixels >= 0)
	{
		region_start_pos[Y] = 0;
		region_end_pos[Y] = y_pixels;
		region_height = m_height - y_pixels;
	}
	else
	{
		region_start_pos[Y] = -y_pixels;
		region_end_pos[Y] = 0;
		region_height = m_height - (-y_pixels);
	}

	// Copy texture region into temp buffers in 32-bit format
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	uint32 *p_texture_buffer = new uint32[region_width * region_height];
	Mem::Manager::sHandle().PopContext();

	// Get section that will be in offset texture
	copy_region_to_32bit_buffer(p_texture_buffer, region_start_pos[X], region_start_pos[Y], region_width, region_height);

	// And put that region back into texture
	blit_32bit_buffer_to_texture(p_texture_buffer, region_end_pos[X], region_end_pos[Y], region_width, region_height);

	//static uint32 total_time = 0;
	//uint32 start_time = Tmr::GetTimeInUSeconds();

	// Check if we have a fill color
	if (use_fill_color)
	{
		Dbg_MsgAssert(0, ("Fill color not implemented yet"));
	}
	else
	{
		for (int h = 0, y_pos = -region_end_pos[Y]; h < m_height; h++, y_pos++)
		{
			int y_clamp_pos = max(y_pos, 0);
			y_clamp_pos = min(y_clamp_pos, region_height - 1);
			int y_buffer_index = y_clamp_pos * region_width;

			for (int w = 0, x_pos = -region_end_pos[X]; w < m_width; w++, x_pos++)
			{
				// Check if we are within the new region
				if ((x_pos >= 0) && (x_pos < region_width) &&
					(y_pos >= 0) && (y_pos < region_height))
				{
					continue;
				}

				int x_clamp_pos = max(x_pos, 0);
				x_clamp_pos = min(x_clamp_pos, region_width - 1);

				set_32bit_pixel_to_texture(p_texture_buffer[y_buffer_index + x_clamp_pos], w, h);
			}
		}
	}

	//uint32 end_time = Tmr::GetTimeInUSeconds();
	//total_time += end_time - start_time;
	//Dbg_Message("offset_texture fill Update time %d us; Total Time %d", end_time - start_time, total_time);

	delete [] p_texture_buffer;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_adjust_region(uint16 x_pos, uint16 y_pos, uint16 width, uint16 height,
										int split_axis, uint16 start_point, uint16 end_point)
{								 	
	// Copy original rectangle in a temp buffer in 32-bit format (needed for interpolation)
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	uint32 *p_orig_region_pixels = new uint32[width * height];
	Mem::Manager::sHandle().PopContext();

	copy_region_to_32bit_buffer(p_orig_region_pixels, x_pos, y_pos, width, height);

	float first_rect_portion;
	uint16 rect_pos[2][2];
	uint16 rect_new_pos[2];
	uint16 rect_orig_width[2];
	uint16 rect_orig_height[2];
	uint16 rect_new_width[2];
	uint16 rect_new_height[2];

	// Figure out first rectangle
	rect_pos[0][X] = 0;
	rect_pos[0][Y] = 0;

	if (split_axis == X)
	{
		Dbg_Assert(start_point >= x_pos);
		Dbg_Assert(start_point <= x_pos + width);
		Dbg_Assert(end_point >= x_pos);
		Dbg_Assert(end_point <= x_pos + width);

		// Dimensions of first rectangle
		rect_orig_width[0] = start_point - x_pos + 1;
		rect_orig_height[0] = height;
		rect_new_width[0] = end_point - x_pos + 1;
		rect_new_height[0] = height;
		
		// Second rectangle
		rect_pos[1][X] = start_point - x_pos;
		rect_pos[1][Y] = 0;
		rect_new_pos[X] = end_point - x_pos;
		rect_new_pos[Y] = 0;

		rect_orig_width[1] = width - (start_point - x_pos);
		rect_orig_height[1] = height;
		rect_new_width[1] = width - (end_point - x_pos);
		rect_new_height[1] = height;

		// Figure out portion of first rectangle to whole region
		first_rect_portion = (float) rect_new_width[0] / (float) width;
	}
	else
	{
		Dbg_Assert(start_point >= y_pos);
		Dbg_Assert(start_point <= y_pos + height);
		Dbg_Assert(end_point >= y_pos);
		Dbg_Assert(end_point <= y_pos + height);

		// Dimensions of first rectangle
		rect_orig_width[0] = width;
		rect_orig_height[0] = start_point - y_pos + 1;
		rect_new_width[0] = width;
		rect_new_height[0] = end_point - y_pos + 1;
		
		// Second rectangle
		rect_pos[1][X] = 0;
		rect_pos[1][Y] = start_point - y_pos;
		rect_new_pos[X] = 0;
		rect_new_pos[Y] = end_point - y_pos;

		rect_orig_width[1] = width;
		rect_orig_height[1] = height - (start_point - y_pos);
		rect_new_width[1] = width;
		rect_new_height[1] = height - (end_point - y_pos);

		// Figure out portion of first rectangle to whole region
		first_rect_portion = (float) rect_new_height[0] / (float) height;
	}

	// Make room for the original and new rectangles
	uint32 *p_orig_rect_pixels[2];
	uint32 *p_new_rect_pixels[2];

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	p_orig_rect_pixels[0] = new uint32[rect_orig_width[0] * rect_orig_height[0]];
	p_orig_rect_pixels[1] = new uint32[rect_orig_width[1] * rect_orig_height[1]];

	p_new_rect_pixels[0] = new uint32[rect_new_width[0] * rect_new_height[0]];
	p_new_rect_pixels[1] = new uint32[rect_new_width[1] * rect_new_height[1]];
	Mem::Manager::sHandle().PopContext();

	// Get the original rectangles
	s_get_region((uint8 *) p_orig_region_pixels, rect_pos[0][X], rect_pos[0][Y], width, height,
				 (uint8 *) p_orig_rect_pixels[0], rect_orig_width[0], rect_orig_height[0], 32);
	s_get_region((uint8 *) p_orig_region_pixels, rect_pos[1][X], rect_pos[1][Y], width, height,
				 (uint8 *) p_orig_rect_pixels[1], rect_orig_width[1], rect_orig_height[1], 32);

	// Scale each rectangle
	s_scale_texture((uint8 *) p_orig_rect_pixels[0], rect_orig_width[0], rect_orig_height[0],
					(uint8 *) p_new_rect_pixels[0], rect_new_width[0], rect_new_height[0], 32);
	s_scale_texture((uint8 *) p_orig_rect_pixels[1], rect_orig_width[1], rect_orig_height[1],
					(uint8 *) p_new_rect_pixels[1], rect_new_width[1], rect_new_height[1], 32);

	// Combine two adjacent side lines into 1 (and put into the second rectangle since it will overwrite the first)
	s_combine_adjacent_borders((uint8 *) p_new_rect_pixels[0], (uint8 *) p_new_rect_pixels[1],
										 rect_new_width[0], rect_new_height[0], rect_new_width[1], first_rect_portion, split_axis, 32);

	// Put new rectangles back into region
	s_put_region((uint8 *) p_orig_region_pixels, rect_pos[0][X], rect_pos[0][Y], width, height,
				 (uint8 *) p_new_rect_pixels[0], rect_new_width[0], rect_new_height[0], 32);
	s_put_region((uint8 *) p_orig_region_pixels, rect_new_pos[X], rect_new_pos[Y], width, height,
				 (uint8 *) p_new_rect_pixels[1], rect_new_width[1], rect_new_height[1], 32);

	// And put region back into texture
	blit_32bit_buffer_to_texture(p_orig_region_pixels, x_pos, y_pos, width, height);

	// Free all the buffers
	delete [] p_orig_rect_pixels[0];
	delete [] p_orig_rect_pixels[1];

	delete [] p_new_rect_pixels[0];
	delete [] p_new_rect_pixels[1];

	delete [] p_orig_region_pixels;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_pull_to_edge(uint16 point, int axis, int num_pixels)
{
#if PRINT_TIMES
	static uint32 total_time = 0;
	uint32 start_time = Tmr::GetTimeInUSeconds();
#endif

	uint32 *p_texture_buffer = NULL;
	uint32 *p_scaled_texture_buffer = NULL;

	uint16 rect_pos[2];
	uint16 rect_orig_width;
	uint16 rect_orig_height;
	uint16 rect_new_width;
	uint16 rect_new_height;
	uint16 scaled_offset;

	if (num_pixels == 0)
	{
		// Nothing to do
		return true;
	}

	// Cap num_pixels on low end
	if ((point + num_pixels) <= 0)
	{
		num_pixels = -point + 1;
	}

	Dbg_Assert((point + num_pixels) >= 0);

	if (axis == X)
	{
		Dbg_Assert(point < m_width);

		// Cap num_pixels on high end
		if ((point + num_pixels) >= m_width)
		{
			num_pixels = m_width - point - 1;
		}

		Dbg_Assert((point + num_pixels) < m_width);

		bool left = (num_pixels < 0);

		if (left)
		{
			// Figure out orig rectangle
			rect_pos[X] = 0;
			rect_pos[Y] = 0;
			rect_orig_width = point + 1;
			rect_orig_height = m_height;

			// Figure out new rectangle
			rect_new_width = point + (-num_pixels) + 1;
			rect_new_height = m_height;

			scaled_offset = -num_pixels;
		}
		else
		{
			// Figure out orig rectangle
			rect_pos[X] = point;
			rect_pos[Y] = 0;
			rect_orig_width = (m_width - point);
			rect_orig_height = m_height;

			// Figure out new rectangle
			rect_new_width = (m_width - point) + num_pixels;
			rect_new_height = m_height;

			scaled_offset = 0;
		}
	}
	else
	{
		Dbg_Assert(point < m_height);

		// Cap num_pixels on high end
		if ((point + num_pixels) >= m_height)
		{
			num_pixels = m_height - point - 1;
		}

		Dbg_Assert((point + num_pixels) < m_height);

		bool top = (num_pixels < 0);

		if (top)
		{
			// Figure out orig rectangle
			rect_pos[X] = 0;
			rect_pos[Y] = 0;
			rect_orig_width = m_width;
			rect_orig_height = point + 1;

			// Figure out new rectangle
			rect_new_width = m_width;
			rect_new_height = point + (-num_pixels) + 1;

			scaled_offset = -num_pixels * m_width;
		}
		else
		{
			// Figure out orig rectangle
			rect_pos[X] = 0;
			rect_pos[Y] = point;
			rect_orig_width = m_width;
			rect_orig_height = (m_height - point);

			// Figure out new rectangle
			rect_new_width = m_width;
			rect_new_height = (m_height - point) + num_pixels;

			scaled_offset = 0;
		}
	}

	// Copy texture region into temp buffers in 32-bit format
	if (Mem::Manager::sHandle().CutsceneTopDownHeap())
	{
		// GJ FIX 9/9/03 FOR SK5:TT13114 - "Failing to allocate 
		// face texture memory in cutscenes"
		// if the cutscene top down heap exists, we want to use
		// that first, because there won't be much on the
		// top down heap during cutscenes...
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneTopDownHeap());
	}
	else
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	}
	p_texture_buffer = new uint32[rect_orig_width * rect_orig_height];
	p_scaled_texture_buffer = new uint32[rect_new_width * rect_new_height];
	Mem::Manager::sHandle().PopContext();

	//Dbg_Message("Copying region from (%d, %d) of size (%d, %d)", rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height);
	copy_region_to_32bit_buffer(p_texture_buffer, rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height);

	// Scale the rectangle
	s_scale_texture((uint8 *) p_texture_buffer, rect_orig_width, rect_orig_height,
					(uint8 *) p_scaled_texture_buffer, rect_new_width, rect_new_height, 32);

	//Dbg_Message("Pull: Original size (%d, %d), new size (%d, %d)", rect_orig_width, rect_orig_height, rect_new_width, rect_new_height);
	// Crop the rectangle and put back into original buffer
	s_put_region((uint8 *) p_texture_buffer, 0, 0, rect_orig_width, rect_orig_height,
				 (uint8 *) (p_scaled_texture_buffer + scaled_offset), rect_new_width, rect_new_height, 32);

	// And put region back into texture
	blit_32bit_buffer_to_texture(p_texture_buffer, rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height);

	// Free buffers
	if (p_texture_buffer)
	{
		delete [] p_texture_buffer;
	}
	if (p_scaled_texture_buffer)
	{
		delete [] p_scaled_texture_buffer;
	}

#if PRINT_TIMES
	uint32 end_time = Tmr::GetTimeInUSeconds();
	total_time += end_time - start_time;
	Dbg_Message("pull_to_edge Update time %d us; Total Time %d", end_time - start_time, total_time);
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_push_to_point(uint16 point, int axis, int num_pixels, bool use_fill_color, Image::RGBA fill_color)
{
#if PRINT_TIMES
	static uint32 total_time = 0;
	uint32 start_time = Tmr::GetTimeInUSeconds();
#endif

	uint32 *p_texture_buffer = NULL;
	uint32 *p_scaled_texture_buffer = NULL;

	uint16 rect_pos[2];
	uint16 rect_new_offset[2];
	uint16 rect_orig_width;
	uint16 rect_orig_height;
	uint16 rect_new_width;
	uint16 rect_new_height;

	if (num_pixels == 0)
	{
		// Nothing to do
		return true;
	}
	//Dbg_Message("Moving %d pixels along %d axis starting at %d", num_pixels, axis, point);

	// Cap num_pixels on low end
	if ((point + num_pixels) <= 0)
	{
		num_pixels = -point + 1;
	}

	Dbg_Assert((point + num_pixels) >= 0);

	if (axis == X)
	{
		Dbg_Assert(point < m_width);

		// Cap num_pixels on high end
		if ((point + num_pixels) >= m_width)
		{
			num_pixels = m_width - point - 1;
		}

		Dbg_Assert((point + num_pixels) < m_width);

		bool left = (num_pixels < 0);

		if (left)
		{
			// Figure out orig rectangle
			rect_pos[X] = point;
			rect_pos[Y] = 0;
			rect_orig_width = (m_width - point);
			rect_orig_height = m_height;

			// Figure out new rectangle
			rect_new_offset[X] = 0;
			rect_new_offset[Y] = 0;
			rect_new_width = (m_width - point) - (-num_pixels);
			rect_new_height = m_height;
		}
		else
		{
			// Figure out orig rectangle
			rect_pos[X] = 0;
			rect_pos[Y] = 0;
			rect_orig_width = point + 1;
			rect_orig_height = m_height;

			// Figure out new rectangle
			rect_new_offset[X] = num_pixels;
			rect_new_offset[Y] = 0;
			rect_new_width = point - num_pixels + 1;
			rect_new_height = m_height;
		}
	}
	else
	{
		Dbg_Assert(point < m_height);

		// Cap num_pixels on high end
		if ((point + num_pixels) >= m_height)
		{
			num_pixels = m_height - point - 1;
		}

		Dbg_Assert((point + num_pixels) < m_height);

		bool top = (num_pixels < 0);

		if (top)
		{
			// Figure out orig rectangle
			rect_pos[X] = 0;
			rect_pos[Y] = point;
			rect_orig_width = m_width;
			rect_orig_height = (m_height - point);

			// Figure out new rectangle
			rect_new_offset[X] = 0;
			rect_new_offset[Y] = 0;
			rect_new_width = m_width;
			rect_new_height = (m_height - point) - (-num_pixels);
		}
		else
		{
			// Figure out orig rectangle
			rect_pos[X] = 0;
			rect_pos[Y] = 0;
			rect_orig_width = m_width;
			rect_orig_height = point + 1;

			// Figure out new rectangle
			rect_new_offset[X] = 0;
			rect_new_offset[Y] = num_pixels;
			rect_new_width = m_width;
			rect_new_height = point - num_pixels + 1;
		}
	}

	Dbg_Assert(rect_new_width);
	Dbg_Assert(rect_new_height);

	// Copy texture region into temp buffers in 32-bit format
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	p_texture_buffer = new uint32[rect_orig_width * rect_orig_height];
	p_scaled_texture_buffer = new uint32[rect_new_width * rect_new_height];
	Mem::Manager::sHandle().PopContext();

	//Dbg_Message("Copying region from (%d, %d) of size (%d, %d)", rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height);
	copy_region_to_32bit_buffer(p_texture_buffer, rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height);

	// Scale the rectangle
	s_scale_texture((uint8 *) p_texture_buffer, rect_orig_width, rect_orig_height,
					(uint8 *) p_scaled_texture_buffer, rect_new_width, rect_new_height, 32);

	// Check if we have a fill color
	if (use_fill_color)
	{
		int num_pixels = rect_orig_width * rect_orig_height;

		// Just brute force it for now
		// Garrett: This doesn't work since p_texture_buffer is only a subset of the texture
		Dbg_MsgAssert(0, ("Fill color not implemented properly yet"));
		for (int i = 0; i < num_pixels; i++)
		{
			p_texture_buffer[i] = *((uint32 *) &fill_color);
		}
	}

	// Put smaller rectangle back into original buffer
	s_put_region((uint8 *) p_texture_buffer, rect_new_offset[X], rect_new_offset[Y], rect_orig_width, rect_orig_height,
				 (uint8 *) p_scaled_texture_buffer, rect_new_width, rect_new_height, 32);

	// And put region back into texture
	blit_32bit_buffer_to_texture(p_texture_buffer, rect_pos[X], rect_pos[Y], rect_orig_width, rect_orig_height);

	// Cover up old pixels
	if (!use_fill_color)
	{
		if (axis == X)
		{
			bool use_left = (num_pixels > 0);
			int scaled_coord = (use_left) ? 0 : rect_new_width - 1; 

			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
			uint32 *p_strip_buffer = new uint32[m_height];
			Mem::Manager::sHandle().PopContext();
			for (int pidx = 0; pidx < m_height; pidx++)
			{
				p_strip_buffer[pidx] = p_scaled_texture_buffer[(pidx * rect_new_width) + scaled_coord];
			}

			int start = (use_left) ? 0 : m_width - (-num_pixels);
			int end = (use_left) ? num_pixels - 1 : m_width - 1;

			for (int line = start; line <= end; line++)
			{
				blit_32bit_buffer_to_texture(p_strip_buffer, line, 0, 1, m_height);
			}

			delete [] p_strip_buffer;
		}
		else
		{
			bool use_top = (num_pixels > 0);
			int scaled_coord = (use_top) ? 0 : rect_new_height - 1; 

			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
			uint32 *p_strip_buffer = new uint32[m_width];
			Mem::Manager::sHandle().PopContext();
			memcpy(p_strip_buffer, &(p_scaled_texture_buffer[scaled_coord * rect_new_width]), m_width * sizeof(uint32));

			int start = (use_top) ? 0 : m_height - (-num_pixels);
			int end = (use_top) ? num_pixels - 1 : m_height - 1;

			for (int line = start; line <= end; line++)
			{
				blit_32bit_buffer_to_texture(p_strip_buffer, 0, line, m_width, 1);
			}

			delete [] p_strip_buffer;
		}
	}

	// Free buffers
	if (p_texture_buffer)
	{
		delete [] p_texture_buffer;
	}
	if (p_scaled_texture_buffer)
	{
		delete [] p_scaled_texture_buffer;
	}

#if PRINT_TIMES
	uint32 end_time = Tmr::GetTimeInUSeconds();
	total_time += end_time - start_time;
	Dbg_Message("push_to_point Update time %d us; Total Time %d", end_time - start_time, total_time);
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_adjust_brightness(float brightness_scale, bool force_adjust_current)
{
#if PRINT_TIMES
	static uint32 total_time = 0;
	uint32 start_time = Tmr::GetTimeInUSeconds();
#endif

	Dbg_Assert(mp_single_texture);
	Dbg_MsgAssert(mp_cur_temp_32bit_image, ("For now, AdjustBrightness() only supports 32-bit mode"));

	Image::RGBA *p_src_pixels;
	Image::RGBA *p_dst_pixels = (Image::RGBA *) mp_cur_temp_32bit_image;

	// Figure out the source
	if (mp_orig_temp_32bit_image && !force_adjust_current)
	{
		p_src_pixels = (Image::RGBA *) mp_orig_temp_32bit_image;
	}
	else
	{
		p_src_pixels = (Image::RGBA *) mp_cur_temp_32bit_image;
	}

	// Adjust each pixel
	int num_pixels = m_width * m_height;
	for (int i = 0; i < num_pixels; i++, p_src_pixels++, p_dst_pixels++)
	{
		p_dst_pixels->r = (uint8) min(p_src_pixels->r * brightness_scale, 255);
		p_dst_pixels->g = (uint8) min(p_src_pixels->g * brightness_scale, 255);
		p_dst_pixels->b = (uint8) min(p_src_pixels->b * brightness_scale, 255);
	}

#if PRINT_TIMES
	uint32 end_time = Tmr::GetTimeInUSeconds();
	total_time += end_time - start_time;
	Dbg_Message("adjust_brightness(%f) Update time %d us; Total Time %d", brightness_scale, end_time - start_time, total_time);
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_adjust_hsv(float h, float s, float v, bool force_adjust_current)
{
#if PRINT_TIMES
	static uint32 total_time = 0;
	uint32 start_time = Tmr::GetTimeInUSeconds();
#endif

	Dbg_Assert(mp_single_texture);
	Dbg_MsgAssert(mp_cur_temp_32bit_image, ("For now, AdjustHSV() only supports 32-bit mode"));

	Dbg_MsgAssert((h >= 0.0f) && (h <= 360.0f), ("h is out of range: %f", h));
	Dbg_MsgAssert(s >= 0.0f, ("s is negative: %f", s));
	Dbg_MsgAssert(v >= 0.0f, ("v is negative: %f", v));

	Image::RGBA *p_src_pixels;
	Image::RGBA *p_dst_pixels = (Image::RGBA *) mp_cur_temp_32bit_image;

	// Figure out the source
	if (mp_orig_temp_32bit_image && !force_adjust_current)
	{
		p_src_pixels = (Image::RGBA *) mp_orig_temp_32bit_image;
	}
	else
	{
		p_src_pixels = (Image::RGBA *) mp_cur_temp_32bit_image;
	}

	// Adjust each pixel
	float pixel_h, pixel_s, pixel_v;
	float pixel_r, pixel_g, pixel_b;
	int num_pixels = m_width * m_height;
	for (int i = 0; i < num_pixels; i++, p_src_pixels++, p_dst_pixels++)
	{
		// Convert
		Gfx::inlineRGBtoHSV(p_src_pixels->r / 255.0f, p_src_pixels->g / 255.0f, p_src_pixels->b / 255.0f, pixel_h, pixel_s, pixel_v);

		// Adjust
		pixel_h += h;
		if (pixel_h > 360.0f)
		{
			pixel_h -= 360.0f;
		}
		pixel_s = Mth::Min(pixel_s * s, 1.0f);
		pixel_v = Mth::Min(pixel_v * v, 1.0f);

		// Convert back
		Gfx::inlineHSVtoRGB(pixel_r, pixel_g, pixel_b, pixel_h, pixel_s, pixel_v);
		p_dst_pixels->r = (unsigned char)( pixel_r * 255.0f + 0.5f );
		p_dst_pixels->g = (unsigned char)( pixel_g * 255.0f + 0.5f );
		p_dst_pixels->b = (unsigned char)( pixel_b * 255.0f + 0.5f );
	}

#if PRINT_TIMES
	uint32 end_time = Tmr::GetTimeInUSeconds();
	total_time += end_time - start_time;
	Dbg_Message("adjust_hsv(%f) Update time %d us; Total Time %d", s, end_time - start_time, total_time);
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_add_to_vram()
{								 	
	Dbg_MsgAssert(mp_single_texture, ("CPs2Texture::plat_add_to_vram() only works for sSingleTexture types"));

	return mp_single_texture->AddToVRAM();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_remove_from_vram()
{								 	
	Dbg_MsgAssert(mp_single_texture, ("CPs2Texture::plat_remove_from_vram() only works for sSingleTexture types"));

	return mp_single_texture->RemoveFromVRAM();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint16	CPs2Texture::plat_get_width() const
{
	return m_width;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint16	CPs2Texture::plat_get_height() const
{
	return m_height;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CPs2Texture::plat_get_bitdepth() const
{
	return m_bitdepth;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CPs2Texture::plat_get_palette_bitdepth() const
{
	return m_clut_bitdepth;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CPs2Texture::plat_get_num_mipmaps() const
{
	return m_num_mipmaps;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_is_transparent() const
{
	return m_transparent;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CPs2Texture::plat_combine_textures(CTexture *p_texture, bool palette_gen)
{
	CPs2Texture *p_ps2_texture = static_cast(p_texture);

	// Make sure both textures have same attributes
	Dbg_Assert(m_width == p_ps2_texture->m_width);
	Dbg_Assert(m_height == p_ps2_texture->m_height);
	Dbg_Assert(m_bitdepth == p_ps2_texture->m_bitdepth);
	Dbg_Assert(m_clut_bitdepth == p_ps2_texture->m_clut_bitdepth);

	Dbg_Assert((m_bitdepth == 8) || (m_bitdepth == 32));		// Maybe support more later
	Dbg_Assert(mp_single_texture);
	Dbg_Assert(p_ps2_texture->mp_single_texture);

	bool paletted = (m_bitdepth <= 8);
	uint32 *p_texture1_buffer = NULL;
	uint32 *p_texture2_buffer = NULL;
	Image::RGBA *p_src;
	Image::RGBA *p_dst;

	if (paletted)
	{
		// Try temp 32-bit images first
		p_dst = (Image::RGBA *) mp_cur_temp_32bit_image;
		p_src = (Image::RGBA *) p_ps2_texture->mp_cur_temp_32bit_image;

		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

		// Copy original textures iton temp buffers in 32-bit format
		if (!p_dst)
		{
			p_texture1_buffer = new uint32[m_width * m_height];
			copy_region_to_32bit_buffer(p_texture1_buffer, 0, 0, m_width, m_height);
			p_dst = (Image::RGBA *) p_texture1_buffer;
		}

		if (!p_src)
		{
			p_texture2_buffer = new uint32[p_ps2_texture->m_width * p_ps2_texture->m_height];
			p_ps2_texture->copy_region_to_32bit_buffer(p_texture2_buffer, 0, 0, p_ps2_texture->m_width, p_ps2_texture->m_height);
			p_src = (Image::RGBA *) p_texture2_buffer;
		}

		Mem::Manager::sHandle().PopContext();
	}
	else
	{
		p_src = (Image::RGBA *) p_ps2_texture->mp_single_texture->GetTextureBuffer();
		p_dst = (Image::RGBA *) mp_single_texture->GetTextureBuffer();
	}

	// Do the actual alpha blend
	uint8 om_src_alpha;
	int size = m_width * m_height;

	for (int i = 0; i < size; i++, p_src++, p_dst++)
	{
		om_src_alpha = 0x80 - p_src->a;

		p_dst->r = (uint8) ( ( ((int) p_src->r * p_src->a) + ((int) p_dst->r * om_src_alpha) ) >> 7 /* divide by 0x80 */);
		p_dst->g = (uint8) ( ( ((int) p_src->g * p_src->a) + ((int) p_dst->g * om_src_alpha) ) >> 7 /* divide by 0x80 */);
		p_dst->b = (uint8) ( ( ((int) p_src->b * p_src->a) + ((int) p_dst->b * om_src_alpha) ) >> 7 /* divide by 0x80 */);
		p_dst->a = max(p_src->a, p_dst->a);	// Choose the highest alpha (since we don't want solid pixels to become transparent)
	}

	// Go back to original bitdepth if necessary
	if (paletted && !mp_cur_temp_32bit_image)
	{
		Dbg_Assert(m_clut_bitdepth == 32);

		// Generate a new palette
		if (palette_gen)
		{
			GeneratePalette((Image::RGBA *) mp_single_texture->GetClutBuffer(), (uint8 *) p_texture1_buffer, m_width, m_height, 32, 1 << m_bitdepth);
		}

		// And repalettize and put back into texture
		blit_32bit_buffer_to_texture(p_texture1_buffer, 0, 0, m_width, m_height);

	}

	// Free buffers
	if (p_texture1_buffer)
	{
		delete [] p_texture1_buffer;
	}
	if (p_texture2_buffer)
	{
		delete [] p_texture2_buffer;
	}

	return false;
}

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CTexDict

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2TexDict::CPs2TexDict(uint32 checksum) : CTexDict(checksum)
{
	// Load nothing
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2TexDict::CPs2TexDict(const char *p_tex_dict_name, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup) : CTexDict(p_tex_dict_name, !is_level_data)
{								  
	LoadTextureDictionary(p_tex_dict_name, NULL, 0, is_level_data, texDictOffset, isSkin, forceTexDictLookup);	// the derived class will does this
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2TexDict::~CPs2TexDict()
{
	UnloadTextureDictionary();				// the derived class does this
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

NxPs2::sScene *	CPs2TexDict::GetEngineTextureDictionary() const
{
	//printf( "Returning scene from GetEngineTextureDictionary %08x\n", (uint32)mp_tex_dict );

	return mp_tex_dict;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPs2TexDict::add_textures_to_hash_table()
{
	for (int i = 0; i < mp_tex_dict->NumTextures; i++)
	{
		// Make sure we don't add duplicate textures.  We can have the same texture in two
		// different texture groups (for sorting purposes).  This will break texture
		// replacement, though.
		if (!GetTexture(mp_tex_dict->pTextures[i].Checksum))
		{
			CPs2Texture *p_texture = new CPs2Texture;

			p_texture->m_checksum		= mp_tex_dict->pTextures[i].Checksum;
			p_texture->m_width			= mp_tex_dict->pTextures[i].GetWidth();
			p_texture->m_height			= mp_tex_dict->pTextures[i].GetHeight();
			p_texture->m_bitdepth		= mp_tex_dict->pTextures[i].GetBitdepth();
			p_texture->m_clut_bitdepth	= mp_tex_dict->pTextures[i].GetClutBitdepth();
			p_texture->m_num_mipmaps	= mp_tex_dict->pTextures[i].GetNumMipmaps();

			p_texture->mp_group_texture = &(mp_tex_dict->pTextures[i]);

			AddTexture(p_texture);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPs2TexDict::LoadTextureDictionary(const char *p_tex_dict_name, uint32* pData, int dataSize, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup)
{
	// set up the texture dictionary's main parameters

	bool IsSkin;
	bool IsInstanceable;
	bool UsesPip;

	if ( is_level_data )
	{
		// for level data
		IsSkin = false;
		IsInstanceable = false;
		UsesPip = true;
	}
	else if ( !isSkin && texDictOffset==0 )
	{
		// for non-skinned models (unless they 
		// do texture replacement, such as the
		// boards in the skateshop do)
		IsSkin = false;
		IsInstanceable = false;
		UsesPip = true;
	}   
	else
	{
		// for all skinned models, and for
		// non-skinned models that do
		// texture replacement)
		IsSkin = true;
		IsInstanceable = true;
		UsesPip = false;
	}

	if ( !is_level_data && isSkin )
	{
		// add all skinned model textures
		// to the hash table
		forceTexDictLookup = true;
	}

	if ( p_tex_dict_name )
	{
		// either the filename OR the data pointer should have been specified, but not both
		Dbg_MsgAssert( !pData, ( "You can't specify both a filename %s AND a data pointer", p_tex_dict_name ) )
		
		// p_tex_dict_name is assumed to be the full path name of the texture dictionary
		mp_tex_dict = NxPs2::LoadTextures(p_tex_dict_name, IsSkin, IsInstanceable, UsesPip, texDictOffset, &m_file_size );
		Dbg_Assert( mp_tex_dict );
	}
	else
	{
		Dbg_MsgAssert( pData, ( "No data pointer specified" ) );
		
		m_file_size = dataSize;
		mp_tex_dict = NxPs2::LoadTextures(pData, dataSize, IsSkin, IsInstanceable, UsesPip, texDictOffset );
		Dbg_Assert( mp_tex_dict );
	}

	// Add textures to hash table (just do it for non-level dictionaries now)
	// should this go on bottom up heap?
	if ( forceTexDictLookup )
	{
		add_textures_to_hash_table();
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2TexDict::UnloadTextureDictionary()
{
	NxPs2::DeleteTextures(mp_tex_dict);
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *		CPs2TexDict::plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram)
{
	CPs2Texture *p_texture = new CPs2Texture;
	if (!p_texture->LoadTexture(p_texture_name, sprite, alloc_vram))
	{
		Dbg_Error("Can't load texture %s", p_texture_name);
	}

	return p_texture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *		CPs2TexDict::plat_load_texture_from_buffer(uint8* p_buffer, int buffer_size, uint32 texture_checksum, bool sprite, bool alloc_vram)
{
	CPs2Texture *p_texture = new CPs2Texture;
	if (!p_texture->LoadTextureFromBuffer(p_buffer, buffer_size, texture_checksum, sprite, alloc_vram))
	{
		Dbg_MsgAssert(0, ("Can't load texture from buffer %s", Script::FindChecksumName(texture_checksum)));
	}

	return p_texture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *		CPs2TexDict::plat_reload_texture(const char *p_texture_name)
{
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2TexDict::plat_unload_texture(CTexture *p_texture)
{
	delete p_texture;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2TexDict::plat_add_texture(CTexture *p_texture)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CPs2TexDict::plat_remove_texture(CTexture *p_texture)
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *		CPs2TexDict::plat_copy_texture(uint32 new_texture_checksum, CTexture *p_texture)
{
	CPs2Texture *p_ps2_texture = static_cast(p_texture);

	return new CPs2Texture(*p_ps2_texture);
}

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/NGPS/p_NxTexture.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.h

#ifndef	__GFX_P_NX_TEXTURE_H__
#define	__GFX_P_NX_TEXTURE_H__

#include 	
#include 	"gfx/NGPS/NX/texture.h"
#include 	"gfx/NGPS/NX/material.h"
#include 	"gfx/NGPS/NX/scene.h"
#include 	"gfx/NGPS/NX/sprite.h"

namespace Nx
{

// Forward declarations
class CPs2TexDict;

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Machine specific implementation of the CTexture
class	CPs2Texture : public CTexture
{
public:
								CPs2Texture(bool loading_screen = false);
								CPs2Texture(const CPs2Texture & src_texture);		// Copy constructor
	virtual						~CPs2Texture();

	NxPs2::SSingleTexture *		GetSingleTexture() const;
	NxPs2::sTexture *			GetGroupTexture() const;

	static void					sInitTables();

private:		// It's all private, as it is machine specific

	uint8 						find_closest_clut_color(Image::RGBA rgba);

	void						setup_fast_clut_color_find(bool use_alpha = true);
	uint8 						fast_find_closest_clut_color(Image::RGBA rgba);
	void 						cleanup_fast_clut_color_find();

	void						copy_region_to_32bit_buffer(uint32 *p_pixel_buffer, uint16 x_pos, uint16 y_pos,
															uint16 width, uint16 height);
	void						blit_32bit_buffer_to_texture(uint32 *p_pixel_buffer, uint16 x_pos, uint16 y_pos,
															 uint16 width, uint16 height);
	void						set_32bit_pixel_to_texture(uint32 pixel, uint16 x_pos, uint16 y_pos);

	// Plat functions
	virtual bool				plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram);
	virtual bool				plat_load_texture_from_buffer(uint8* p_buffer, int buffer_size, bool sprite, bool alloc_vram);
	virtual bool				plat_replace_texture(CTexture *p_texture);

	virtual bool				plat_generate_32bit_image(bool renderble = false, bool store_original = false);
	virtual bool				plat_put_32bit_image_into_texture(bool new_palette = false);

	virtual bool				plat_offset(int x_pixels, int y_pixels, bool use_fill_color, Image::RGBA fill_color);

	virtual bool				plat_adjust_region(uint16 x_pos, uint16 y_pos, uint16 width, uint16 height,
												   int split_axis, uint16 start_point, uint16 end_point);

	virtual bool				plat_pull_to_edge(uint16 point, int axis, int num_pixels);
	virtual bool				plat_push_to_point(uint16 point, int axis, int num_pixels, bool use_fill_color,
												   Image::RGBA fill_color);

	virtual bool				plat_adjust_brightness(float brightness_scale, bool force_adjust_current = false);
	virtual bool				plat_adjust_hsv(float h, float s, float v, bool force_adjust_current = false);

	virtual bool				plat_add_to_vram();
	virtual bool				plat_remove_from_vram();

	virtual uint16				plat_get_width() const;
	virtual uint16				plat_get_height() const;
	virtual uint8				plat_get_bitdepth() const;
	virtual uint8				plat_get_palette_bitdepth() const;
	virtual uint8				plat_get_num_mipmaps() const;
	virtual bool				plat_is_transparent() const;

	virtual bool				plat_combine_textures(CTexture *p_texture, bool palette_gen);

	// Static functions
	static uint8 				s_clut8_index_to_offset(uint8 PS2idx);
	static uint8 				s_clut8_offset_to_index(uint8 offset);

	static inline uint32		s_get_pixel_from_32bit_texture(uint32 *p_buffer, uint16 x_pos, uint16 y_pos, uint16 stride);
	static void					s_get_nearest_pixels_from_32bit_texture(Image::RGBA p_nearest[][2], uint32 *p_buffer,
																		uint16 x_pos, uint16 y_pos, uint16 width, uint16 height);
	
	static void					s_get_region(uint8 *p_buffer, uint16 x_pos, uint16 y_pos, uint16 tex_width, uint16 tex_height,
											 uint8 *p_region, uint16 region_width, uint16 region_height, uint16 bitdepth);
	static void					s_put_region(uint8 *p_buffer, uint16 x_pos, uint16 y_pos, uint16 tex_width, uint16 tex_height,
											 uint8 *p_region, uint16 region_width, uint16 region_height, uint16 bitdepth);

	static void					s_scale_texture(uint8 *p_buffer, uint16 width, uint16 height,
												uint8 *p_result_buffer, uint16 new_width, uint16 new_height, uint16 bitdepth);

	static void					s_combine_adjacent_borders(uint8 *p_first_rect, uint8 *p_second_rect, uint16 first_width,
														   uint16 first_height, uint16 second_stride, float first_portion,
														   int split_axis, uint16 bitdepth);

	uint16						m_width;
	uint16						m_height;
	uint8						m_bitdepth;
	uint8						m_clut_bitdepth;
	uint8						m_num_mipmaps;
	bool						m_transparent;

	// Tells is this texture is used as loading screen (so it isn't allocated VRAM, etc)
	bool						m_loading_screen;

	// The actual data in the engine (can only be one or the other
	NxPs2::SSingleTexture *		mp_single_texture;	// Uses SSingleTexture
	NxPs2::sTexture *			mp_group_texture;	// Uses sTexture (part of texture group

	// Temp 32-bit texture images for texture manipulations (so we don't need to palettize every operation)
	uint32 *					mp_orig_temp_32bit_image;		// Original
	uint32 *					mp_cur_temp_32bit_image;		// Current
	NxPs2::SSingleTexture *		mp_temp_32bit_single_texture;	// So we can draw it

	// PS2 clut index conversions
	static bool					s_tables_initialized;
    static uint8				s_clut8_index_to_offset_table[256];
    static uint8				s_clut8_offset_to_index_table[256];
	
	// Friends
	friend CPs2TexDict;
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline NxPs2::SSingleTexture *	CPs2Texture::GetSingleTexture() const
{
	if (mp_temp_32bit_single_texture)
	{
		return mp_temp_32bit_single_texture;		// This can be a problem if plat_put_32bit_image_into_texture()
													// is called before the corresponding CSprite is deleted.
	}
	else
	{
		return mp_single_texture;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline NxPs2::sTexture *		CPs2Texture::GetGroupTexture() const
{
	return mp_group_texture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint32					CPs2Texture::s_get_pixel_from_32bit_texture(uint32 *p_buffer, uint16 x_pos, uint16 y_pos,
																			uint16 stride)
{
	return p_buffer[(y_pos * stride) + x_pos];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint8 					CPs2Texture::s_clut8_index_to_offset(uint8 PS2idx)
{
    return s_clut8_index_to_offset_table[PS2idx];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint8 					CPs2Texture::s_clut8_offset_to_index(uint8 offset)
{
    return s_clut8_offset_to_index_table[offset];
}

//////////////////////////////////////////////////////////////////////////////////
// Machine specific implementation of the CMaterial
class	CPs2Material : public CMaterial
{
public:
								CPs2Material();
	virtual						~CPs2Material();

private:
	virtual Image::RGBA			plat_get_rgba() const;
	virtual void				plat_set_rgba(Image::RGBA rgba);
	virtual void				plat_set_texture();

	Image::RGBA					m_rgba;

	// The actual data in the engine
	NxPs2::sMaterial *			mp_material;
};

//////////////////////////////////////////////////////////////////////////////////
// Machine specific implementation of the CTexDict
class	CPs2TexDict : public CTexDict
{
public:
								CPs2TexDict(uint32 checksum);		// loads nothing
								CPs2TexDict(uint32* pData, int dataSize, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup = false);
								CPs2TexDict(const char *p_tex_dict_name, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup = false);
	virtual						~CPs2TexDict();

	NxPs2::sScene *				GetEngineTextureDictionary() const;

public:
	// made this public so that the tex dict manager needs to be able to 
	// create a texture dictionary and load up its textures in two separate steps...
	bool						LoadTextureDictionary(const char *p_tex_dict_name, uint32* pData, int dataSize, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup = false);

private:
	void						add_textures_to_hash_table();
	bool						UnloadTextureDictionary();

	// Platform-specific calls
	virtual CTexture *			plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram);
	virtual CTexture *			plat_load_texture_from_buffer(uint8* p_buffer, int buffer_size, uint32 texture_checksum, bool sprite, bool alloc_vram);
	virtual CTexture *			plat_reload_texture(const char *p_texture_name);
	virtual bool				plat_unload_texture(CTexture *p_texture);
	virtual void				plat_add_texture(CTexture *p_texture);
	virtual bool				plat_remove_texture(CTexture *p_texture);
	virtual CTexture *			plat_copy_texture(uint32 new_texture_checksum, CTexture *p_texture);
	//virtual CTexture *			plat_combine_textures(uint32 new_texture_checksum, CTexture *p_texture1, CTexture *p_texture2);

	NxPs2::sScene *				mp_tex_dict;		// Platform-dependent data
};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/NGPS/p_NxTextured3dPoly.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 

namespace NxPs2
{

CPs2Textured3dPoly::CPs2Textured3dPoly()
{
}

CPs2Textured3dPoly::~CPs2Textured3dPoly()
{
}

void CPs2Textured3dPoly::plat_set_texture(uint32 texture_checksum)
{
	Nx::CTexture *p_texture = Nx::CTexDictManager::sp_sprite_tex_dict->GetTexture(texture_checksum);
	Dbg_MsgAssert(p_texture, ("no texture found for sprite"));
	Nx::CPs2Texture *p_ps2_texture = static_cast( p_texture );
	Dbg_MsgAssert(p_ps2_texture,("NULL p_ps2_texture"));
	mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	Dbg_MsgAssert(mp_engine_texture,("NULL mp_engine_texture"));
}	

void CPs2Textured3dPoly::plat_render()
{
	CImmediateMode::sStartPolyDraw( mp_engine_texture, PackALPHA(0,0,0,0,0), ABS );
	
	CImmediateMode::sDrawQuadTexture(mp_engine_texture, mp_pos[0], mp_pos[1], mp_pos[2], mp_pos[3], 0x80808080,0x80808080,0x80808080,0x80808080);
}


} // Namespace NxPs2
				
				


================================================
FILE: Code/Gfx/NGPS/p_NxTextured3dPoly.h
================================================
#ifndef	__GFX_P_NX_TEXTURED_3D_POLY_H__
#define	__GFX_P_NX_TEXTURED_3D_POLY_H__

#include 
#include 

namespace NxPs2
{

// Machine specific implementation of CTextured3dPoly
class	CPs2Textured3dPoly : public Nx::CTextured3dPoly
{
public:
							CPs2Textured3dPoly();
	virtual					~CPs2Textured3dPoly();
private:
	void					plat_render();
	void					plat_set_texture(uint32 texture_checksum);
	
	NxPs2::SSingleTexture*	mp_engine_texture;
};

}	// namespace NxPs2

#endif
				   


================================================
FILE: Code/Gfx/NGPS/p_NxViewMan.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxViewMan.cpp - PS2 platform specific interface to CViewportManager
//
// This is PS2 SPECIFIC!!!!!!  So might get a bit messy

#include 

#include "gfx/NxViewMan.h"
#include "gfx/NGPS/p_NxViewport.h"

namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CViewport *		CViewportManager::s_plat_create_viewport(const Mth::Rect* rect, Gfx::Camera* cam)
{
	return new CPs2Viewport(rect, cam);
}

} 
 


================================================
FILE: Code/Gfx/NGPS/p_NxViewport.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxViewport.cpp

#include 	"Gfx/NxViewMan.h"
#include 	"Gfx/NGPS/p_NxViewport.h"

#include 	"Gfx/NGPS/NX/render.h"
#include 	"Gfx/NGPS/NX/sprite.h"
#include 	"Gfx/NGPS/NX/switches.h"

namespace Nx
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CViewport

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Viewport::CPs2Viewport()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Viewport::CPs2Viewport( const Mth::Rect* rect, Gfx::Camera* cam) :
	CViewport(rect, cam)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Viewport::~CPs2Viewport()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CPs2Viewport::update_render_vars()
{
	int view_idx = CViewportManager::sGetActiveViewportNumber(this);
	Dbg_MsgAssert(view_idx >= 0, ("Can't find viewport in active viewport list"));

	if (!NxPs2::render::VarsUpToDate(view_idx) && mp_camera)
	{
		Mth::Matrix cam_matrix(mp_camera->GetMatrix());
		cam_matrix[W] = mp_camera->GetPos();

		NxPs2::render::SetupVars(view_idx, cam_matrix, GetRect(), mp_camera->GetAdjustedHFOV(), GetAspectRatio(),
							  /*-mp_camera->GetNearClipPlane(), -mp_camera->GetFarClipPlane()*/ -1.0f, -100000.0f);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float			CPs2Viewport::plat_transform_to_screen_coord(const Mth::Vector & world_pos, float & screen_pos_x, float & screen_pos_y, ZBufferValue & screen_pos_z)
{
	float zdistance;
	float scale;

	update_render_vars();

	// First check frustum
	Mth::Vector screen_pos(NxPs2::render::WorldToFrustum.Transform(world_pos));

	//if ((Tmr::GetVblanks() % 200) == 0)
	//{
	//	Dbg_Message("Homogenious (%g, %g, %g, %g)", screen_pos[X], screen_pos[Y], screen_pos[Z], screen_pos[W]);
	//}

	if ((screen_pos[X] > screen_pos[W]) || (screen_pos[X] < -screen_pos[W]) ||
		(screen_pos[Y] > screen_pos[W]) || (screen_pos[Y] < -screen_pos[W]) ||
		(screen_pos[Z] > screen_pos[W]) || (screen_pos[Z] < -screen_pos[W]))
	{
		return -1.0f;
	} else {
		zdistance = -screen_pos[Z]; //(screen_pos[Z] - screen_pos[W]) / (2.0f * screen_pos[W]);
	}

	// Now find screen coordinates
	screen_pos = NxPs2::render::WorldToIntViewport.Transform(world_pos);

	//if ((Tmr::GetVblanks() % 200) == 0)
	//{
	//	Dbg_Message("Converted (%f, %f, %f, %f) to (%g, %g, %g, %g)", world_pos[X], world_pos[Y], world_pos[Z], world_pos[W],
	//																  screen_pos[X], screen_pos[Y], screen_pos[Z], screen_pos[W]);
	//}

	// Get into homogenious space
	float oow = 1.0f / screen_pos[W];
	screen_pos *= oow;

	// Convert the int back to float
	screen_pos_x = (((float) ( *((int *) &(screen_pos[X])) & 0xFFFF )) - XOFFSET) / 16.0f;
	screen_pos_y = (((float) ( *((int *) &(screen_pos[Y])) & 0xFFFF )) - YOFFSET) / 16.0f;
	screen_pos_z = (ZBufferValue) ( *((int *) &(screen_pos[Z])) & 0xFFFFFF );

	// Convert back to NTSC, if necessary
	screen_pos_x /= NxPs2::SDraw2D::GetScreenScaleX();
	screen_pos_y /= NxPs2::SDraw2D::GetScreenScaleY();

	//if ((Tmr::GetVblanks() % 200) == 0)
	//{
	//	Dbg_Message("Divide by W (%g, %g, %x, %g) Z distance: %g", screen_pos_x, screen_pos_y, screen_pos_z, screen_pos[W], zdistance);
	//}

	// Calculate scale
	scale = CViewportManager::sGet2DIn3DSpaceNoscaleDistance() / zdistance;
	scale = Mth::Min(scale, CViewportManager::sGet2DIn3DSpaceMaxScale());
	scale = Mth::Max(scale, CViewportManager::sGet2DIn3DSpaceMinScale());

#if 0		// This scales 1 sprite or text pixel to an inch!
	float hfov;
	if (mp_camera)
	{
		hfov = mp_camera->GetAdjustedHFOV();
	} else {
		hfov = CViewportManager::sGetScreenAngle();
	}

	// Calculate scale
	float h = ((float) HRES) / (tanf(Mth::DegToRad(hfov / 2.0f)));
	scale = h / zdistance;

	if ((Tmr::GetVblanks() % 300) == 0)
	{
		Dbg_Message("H %f, ZDistance %f, Scale %f, HFOC %f", h, zdistance, scale, hfov);
	}
#endif

	return scale;
}

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/NGPS/p_NxViewport.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxViewport.h

#ifndef	__GFX_P_NX_VIEWPORT_H__
#define	__GFX_P_NX_VIEWPORT_H__

#include 	"Gfx/NxViewport.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Machine specific implementation of the CViewport
class	CPs2Viewport : public CViewport
{
public:
								CPs2Viewport();
								CPs2Viewport(const Mth::Rect* rect, Gfx::Camera* cam = NULL);
	virtual						~CPs2Viewport();

protected:
	void						update_render_vars();

private:		// It's all private, as it is machine specific
	virtual float				plat_transform_to_screen_coord(const Mth::Vector & world_pos, float & screen_pos_x, float & screen_pos_y, ZBufferValue & screen_pos_z);

};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/NGPS/p_NxWin2D.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxWin2D.cpp

#include 	"Gfx/NGPS/p_NxWin2D.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CText

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Window2D::CPs2Window2D(int x, int y, int width, int height) : CWindow2D(x, y, width, height)
{
	mp_plat_window = new NxPs2::SScissorWindow();

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2Window2D::~CPs2Window2D()
{
	if (mp_plat_window)
	{
		delete mp_plat_window;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CPs2Window2D::plat_update_engine()
{
	int x0 = (int) (((float) m_xpos) * NxPs2::SDraw2D::GetScreenScaleX());
	int y0 = (int) (((float) m_ypos) * NxPs2::SDraw2D::GetScreenScaleY());

	Dbg_Assert(x0 >= 0);
	Dbg_Assert(y0 >= 0);

	int x1 = x0 + (int) (((float) m_width ) * NxPs2::SDraw2D::GetScreenScaleX()) - 1;
	int y1 = y0 + (int) (((float) m_height) * NxPs2::SDraw2D::GetScreenScaleY()) - 1;

	Dbg_Assert(x1 >= 0);
	Dbg_Assert(y1 >= 0);

	mp_plat_window->SetScissor(x0, y0, x1, y1);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CWindow2DManager::s_plat_alloc_window2d_pool()
{
	for (int i = 0; i < vMAX_WINDOW_INSTANCES; i++)
	{
	   	CPs2Window2D *p_window = new CPs2Window2D;
		p_window->mp_next = sp_window_list;
		sp_window_list = p_window;
	}
}

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/NGPS/p_NxWin2D.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxWin2D.h

#ifndef	__GFX_P_NX_WIN2D_H__
#define	__GFX_P_NX_WIN2D_H__

#include 	"Gfx/NxWin2D.h"
#include 	"Gfx/NGPS/NX/sprite.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CWindow2D
class	CPs2Window2D : public CWindow2D
{
public:
								CPs2Window2D(int x = 0, int y = 0, int width = 640, int height = 448);
								CPs2Window2D(const Mth::Rect & win_rect);
	virtual						~CPs2Window2D();

	NxPs2::SScissorWindow *		GetEngineWindow() const;

private:
	//
	virtual void				plat_update_engine();		// Update engine primitives

	// Machine specific members
	NxPs2::SScissorWindow *		mp_plat_window;				// Pointer to engine window
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline NxPs2::SScissorWindow *	CPs2Window2D::GetEngineWindow() const
{
	return mp_plat_window;
}

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/NGPS/p_gfxman.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		p_gfxman.cpp											**
**																			**
**	Created:		07/26/99	-	mjb										**
**																			**
**	Description:	Graphics device manager									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/
		
extern "C"				 
{

#include 
#include 
#include 
}

#include 
#include 
#include 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

#define NUMPIXELS ( width * height )

#define HEADSIZE 54
#define IMAGESIZE ( NUMPIXELS * 3 )
#define BITMAPSIZE ( IMAGESIZE + HEADSIZE )

// Macro to store 32-bit data to unaligned address.
#define SET_UINT32( addr, x ) ( \
	*(( uint16 * )( addr )) = ( x ), \
	*(( uint16 * )(( addr ) + 2 )) = (( x ) >> 16 ) \
)

void SetupBMPHeader(uint8 *bmp, int width, int height)
{
	// Initialize the BMP header structure.
	// Bitmapfileheader
	*bmp						= 'B';              // Type
	*( bmp + 1 )				= 'M';
	SET_UINT32( bmp + 2, BITMAPSIZE );				// Size
	*( uint16 * )( bmp + 6 )	= 0;                // Reserved
	SET_UINT32( bmp + 10, HEADSIZE );				// Offset

	// Bitmapinfoheader
	SET_UINT32( bmp + 14, 40 );						// Bitmapinfoheader size
	SET_UINT32( bmp + 18, width );					// Width
	SET_UINT32( bmp + 22, height );					// Height
	*( uint16 * )( bmp + 26 )	= 1;      			// Planes
	*( uint16 * )( bmp + 28 )	= 24;     			// Bitcount
	SET_UINT32( bmp + 30, 0 );						// Compression
	SET_UINT32( bmp + 34, IMAGESIZE );				// Image size in bytes
	SET_UINT32( bmp + 38, 4740 );					// X Pels per metre
	SET_UINT32( bmp + 42, 4740 );					// Y Pels per metre
	SET_UINT32( bmp + 46, 0 );						// ClrUsed
	SET_UINT32( bmp + 50, 0 );						// ClrImportant

}


//--------------------------------------------------------------------------------------------
// File: main.c
// Date: August 19, 2000
// Author: George Bain @ Sony Computer Entertainment Europe
// Description: Store GS local memory -> Main/SPR memory 
// Notes: - read StoreTextureVIF1() and DumpImage() for more information
//        - pause screen using "square + cross buttons" to avoid blur
//
//--------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------
// I N C L U D E S
//--------------------------------------------------------------------------------------------

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include	


//--------------------------------------------------------------------------------------------
// D E F I N E S
//--------------------------------------------------------------------------------------------

#define SCRN_W (640)
#define SCRN_H (224)
#define SCRN_CENTER_X (2048.0f)
#define SCRN_CENTER_Y (2048.0f)
#define SCRN_Z (512.0f)
#define ZBUF_MAX (16777000.0f)
#define ZBUF_MIN (1.0f)
#define ZCLIP_MAX (16777216.0f)
#define ZCLIP_MIN (1.0f)
#define ASPECT_X (1.0f)
#define ASPECT_Y (((float)SCRN_H*4.0f)/((float)SCRN_W*3.0f))
#define STRING_SIZE   (64)
#define OFFX (((4096-SCRN_W)/2)<<4)
#define OFFY (((4096-SCRN_H)/2)<<4)
#define PI (3.141592f)

#define RAD_TO_DEG(x) (x * 180.0f / PI)
#define DEG_TO_RAD(x) (x * PI / 180.0f)

#define ROT_SPEED (1.0f)
#define SET_VECTOR(_p,_x,_y,_z,_w)  ((_p)[0] = _x, (_p)[1] = _y, (_p)[2] = _z, (_p)[3] = _w)
#define TRANS (10.0f)
#define SPR_MEM (0x70000000)
#define UNCACHED_MEM (0x20000000)
#define DMA_SPR (0x80000000)
#define DMA_MEM (0x0FFFFFFF)
#define CUBE_VERT (36)

#define GIFTAG_NLOOP (CUBE_VERT)
#define GS_PRIM_SHADED (1)
#define GS_PRIM_ALPHA (1)
#define GIFTAG_EOP (1)
#define GIFTAG_PRE (1)
#define GS_PRIM_TEX (1)

#define RGB_FRAME (1)

#define GIFTAG_MAX_NLOOP (32767)
#define IMAGE_W (256)
#define IMAGE_H (256)
#define STORE_IMAGE_W (SCRN_W)
#define STORE_IMAGE_H (SCRN_H)
#define IMAGE_ADDR ( ((SCRN_W*SCRN_H*4)*3) / 256 )
#define CLUT_CSM1_MODE (0)
#define CLUT_CSM2_MODE (1)

//--------------------------------------------------------------------------------------------
// S T R U C T U R E S
//--------------------------------------------------------------------------------------------

typedef struct rect_tag
{

  short x, y;
  short w, h;

}
RECT;

typedef struct timimage_tag
{

  u_int mode;
  u_int id;
  u_int flag;
  u_int cbnum;
  RECT crect;
  u_int *caddr;
  u_int pbnum;
  RECT prect;
  u_int *paddr;

}
TIM_IMAGE;


//--------------------------------------------------------------------------------------------
// G L O B A L S
//--------------------------------------------------------------------------------------------

sceVu0FVECTOR camera_p = { 0.0f, 0.0f, -300.0f, 0.0f };
sceVu0FVECTOR camera_zd = { 0.0f, 0.0f, 1.0f, 1.0f };
sceVu0FVECTOR camera_yd = { 0.0f, 1.0f, 0.0f, 1.0f };
sceVu0FVECTOR light0 = { 1.0f, 0.0f, 0.3f, 0.0f };
sceVu0FVECTOR light1 = { 0.0f, 1.0f, 0.3f, 0.0f };
sceVu0FVECTOR light2 = { 0.0f, 0.0f, 1.0f, 0.0f };
sceVu0FVECTOR color0 = { 0.3f, 0.3f, 0.3f, 1.0f };
sceVu0FVECTOR color1 = { 0.3f, 0.3f, 0.3f, 1.0f };
sceVu0FVECTOR color2 = { 0.4f, 0.4f, 0.4f, 1.0f };
sceVu0FVECTOR ambient = { 0.2f, 0.2f, 0.2f, 0.0f };
sceVu0FVECTOR obj_trans = { 0.0f, 0.0f, 0.0f, 0.0f };
sceVu0FVECTOR obj_rot = { 0.0f, 0.0f, 0.0f, 0.0f };
sceVu0FMATRIX local_world;
sceVu0FMATRIX world_view;
sceVu0FMATRIX view_screen;
sceVu0FMATRIX local_screen;
sceVu0FMATRIX normal_light;
sceVu0FMATRIX light_color;
sceVu0FMATRIX local_light;
sceVu0FMATRIX work;
sceGsDBuff db;
sceDmaChan *dmaGIF = NULL;
sceDmaChan *dmaVIF1 = NULL;
float delta = ROT_SPEED;
u_int paddata;
u_char rdata[32];
u_long128 pad_dma_buf[scePadDmaBufferMax] __attribute__ ( ( aligned( 64 ) ) );
TIM_IMAGE texture;


//--------------------------------------------------------------------------------------------
// P R O T O T Y P E S
//--------------------------------------------------------------------------------------------

void InitPad( void );
void InitSystem( void );
void InitGraphics( void );
void ControlInput( void );
void SetAlphaBlend( void );
void SetTexFilter( void );
void SetTextureInfo( void );
void LoadTexture( u_long128 * base_addr, short pixel_mode, short addr, short w, short h,
		  short dest_x, short dest_y );
void ClearVRAM( u_char r, u_char g, u_char b, u_char a );
void CubePacket( sceVu0FVECTOR * vertex, sceVu0FVECTOR * normal, sceVu0FVECTOR * color,
		 sceVu0FVECTOR * st );
void sceVu0NormalColorVector( sceVu0IVECTOR c0, sceVu0FMATRIX local_light,
			      sceVu0FMATRIX light_color, sceVu0FVECTOR v0, sceVu0FVECTOR c1 );
void ReadTIM( u_char * file, TIM_IMAGE * tim, short clut_store_mode );
void StoreTextureVIF1( u_long128 * base_addr, short start_addr, short pixel_mode, short x,
		       short y, short w, short h, short frame_width );
void DumpImage( int frame_cnt, int oddeven );


//--------------------------------------------------------------------------------------------
// Function: DumpImage()
// Description: Uses StoreTextureVIF1() to grab both the odd and even frame buffers in GS local
//              memory.  A 24-bit TIM image is written to a file called "image.tim".
// Paramaters:  int frame_cnt: odd or even frame
//              int oddeven: odd or even frame (Vsync)
// Returns: none
// Notes: N/A
//--------------------------------------------------------------------------------------------

/*
void DumpImage( int frame_cnt, int oddeven )
{

  u_long128 *dst1 = NULL, *dst2 = NULL;
  int fd;
  int cnt = 0;
  int index = 0;
  u_int bnum __attribute__ ( ( aligned( 16 ) ) );
  u_short pixel_head[4] __attribute__ ( ( aligned( 16 ) ) );
  u_int header[3] __attribute__ ( ( aligned( 16 ) ) );

  // create image file
  fd = sceOpen( "host:image.tim", SCE_WRONLY | SCE_TRUNC | SCE_CREAT );

  header[0] = 0x10;		// id
  header[1] = 0x03;		// flag 2= 16bit 3 = 24bit

  // write header
  sceWrite( fd, &header[0], 4 );
  sceWrite( fd, &header[1], 4 );

  bnum = ( ( STORE_IMAGE_W * STORE_IMAGE_H * 3 ) * 2 ) + 12;	// BNUM
  pixel_head[0] = 0;		//x
  pixel_head[1] = 0;		//y
  pixel_head[2] = 960;		//w
  pixel_head[3] = 448;		//h

  // write pixel header information
  sceWrite( fd, &bnum, 4 );
  sceWrite( fd, &pixel_head[0], 2 );
  sceWrite( fd, &pixel_head[1], 2 );
  sceWrite( fd, &pixel_head[2], 2 );
  sceWrite( fd, &pixel_head[3], 2 );

  // allocate some memory
  dst1 = ( u_long128 * ) memalign( 64, STORE_IMAGE_W * STORE_IMAGE_H * 3 );
  dst2 = ( u_long128 * ) memalign( 64, STORE_IMAGE_W * STORE_IMAGE_H * 3 );

  StoreTextureVIF1( dst1, 0, SCE_GS_PSMCT24, 0, 0, STORE_IMAGE_W, STORE_IMAGE_H, SCRN_W );
  StoreTextureVIF1( dst2, 2240, SCE_GS_PSMCT24, 0, 0, STORE_IMAGE_W, STORE_IMAGE_H, SCRN_W );

  index = 0;

  for ( cnt = 0; cnt < STORE_IMAGE_H; cnt++ )
    {

      if ( ( ( frame_cnt == NULL ) && ( oddeven == NULL ) )
	   || ( ( frame_cnt ) && ( oddeven ) ) )
	{
	  sceWrite( fd, dst1 + index, STORE_IMAGE_W * 3 );
	  sceWrite( fd, dst2 + index, STORE_IMAGE_W * 3 );
	}
      else
	{
	  sceWrite( fd, dst2 + index, STORE_IMAGE_W * 3 );
	  sceWrite( fd, dst1 + index, STORE_IMAGE_W * 3 );
	}


      index += 120;		// (STORE_IMAGE_W * 3)/16;
      printf( "written line:%d\n", cnt );

    }

  free( dst1 );
  free( dst2 );

  sceClose( fd );


}				// end DumpImage

*/



//--------------------------------------------------------------------------------------------
// Function:    StoreTextureVIF1()
// Description: Stores texture data from GS to main memory or scratchpad memory.  See notes on
//              data flow
// Paramaters:  u_long128* base_addr: base address of stored texture data in MAIN/SPR memory
//              short start_addr: start address in GS memory to transfer from
//              short pixel_mode: pixel mode of stored data
//              short x: x location in GS memory
//              short y: y location in GS memory
//              short w: width of stored image
//              short h: height of stored image
//							short frame_width: width of frame buffer
// Returns:     N/A
// Notes: -     data flow of store image
//              	- wait for all 3 PATHS to GIF to be complete (FLUSHA vif code)
//              	- disable PATH3 transfer using MSKPATH3 vifcode (mask)
//              	- set BITBLTBUF register (source parameters)
//              	- set TRXPOS register (x,y postion)
//              	- set TRXREG register (width and height)
//              	- set FINISH register (set event)
//              	- set TRXDIR register (LOCAL->HOST)
//              	- get previous Interrupt Mask Control (IMR)
//              	- enable the FINISH event
//              	- send GIF packet to GS
//              	- wait for DMA to be complete
//              	- wait for FINISH event to be generated  (all drawing is complete)
//              	- change direction of the GS bus
//              	- change direction of VIF1-FIFO (VIF1_STAT.FDR)
//              	- DMA GS data to main/spr memory
//              	- wait for DMA to be complete
//              	- restore direction of the GS bus
//              	- restore direction of VIF1-FIFO 
//              	- restore previous Interrupt Mask Control (IMR)
//              	- enable the FINISH event
//              	- enable PATH3 transfer using MSKPATH3 vifcode
//--------------------------------------------------------------------------------------------

void StoreTextureVIF1( u_long128 * base_addr, short start_addr, short pixel_mode, short x,
		       short y, short w, short h, short frame_width )
{

  int texture_qwc;
  sceVif1Packet vif1_pkt;
  u_long128 settup_base[10];
  int buff_width;
  static u_int enable_path3[4] __attribute__ ( ( aligned( 16 ) ) ) = {
    0x06000000,
    0x00000000,
    0x00000000,
    0x00000000,
  };

  // get quad word count for image
  if ( pixel_mode == SCE_GS_PSMCT32 )
    texture_qwc = ( w * h * 32 ) >> 7;
  else if ( pixel_mode == SCE_GS_PSMCT24 )
    texture_qwc = ( w * h * 24 ) >> 7;
  else if ( pixel_mode == SCE_GS_PSMCT16 )
    texture_qwc = ( w * h * 16 ) >> 7;
  else if ( pixel_mode == SCE_GS_PSMT8 )
    texture_qwc = ( w * h * 8 ) >> 7;
  else
    texture_qwc = ( w * h * 4 ) >> 7;

  if ( texture_qwc > GIFTAG_MAX_NLOOP )
    {
      printf( "ERROR: Texture QWC is greater then GIFTAG_NLOOP! line:(%d), file:(%s)\n", __LINE__,
	       __FILE__ ); 
			exit( 0 );
    }

  buff_width = frame_width >> 6;

  if ( buff_width <= 0 )
    buff_width = 1;

  // set base address of GIF packet
  sceVif1PkInit( &vif1_pkt, &settup_base[0] );
  sceVif1PkReset( &vif1_pkt );

  // will start transfer with VIF code and GS data will follow
  sceVif1PkAddCode( &vif1_pkt, SCE_VIF1_SET_NOP( 0 ) );
  // disable PATH 3 transfer
  sceVif1PkAddCode( &vif1_pkt, SCE_VIF1_SET_MSKPATH3( 0x8000, 0 ) );
  // wait for all 3 PATHS to GS to be complete
  sceVif1PkAddCode( &vif1_pkt, SCE_VIF1_SET_FLUSHA( 0 ) );
  // transfer 6 QW's to GS
  sceVif1PkAddCode( &vif1_pkt, SCE_VIF1_SET_DIRECT( 6, 0 ) );

  // GIF tag for texture settings         
  sceVif1PkAddGsData( &vif1_pkt, SCE_GIF_SET_TAG( 5, GIFTAG_EOP, NULL, NULL, SCE_GIF_PACKED, 1 ) );
  sceVif1PkAddGsData( &vif1_pkt, 0xEL );

  // set transmission between buffers
  sceVif1PkAddGsData( &vif1_pkt, SCE_GS_SET_BITBLTBUF( start_addr, buff_width, pixel_mode,	// SRC
						       NULL, NULL, NULL ) );	// DEST
  sceVif1PkAddGsData( &vif1_pkt, SCE_GS_BITBLTBUF );

  // set transmission area between buffers        ( source x,y  dest x,y  and direction )
  sceVif1PkAddGsData( &vif1_pkt, SCE_GS_SET_TRXPOS( x, y, 0, 0, 0 ) );
  sceVif1PkAddGsData( &vif1_pkt, SCE_GS_TRXPOS );

  // set size of transmission area 
  sceVif1PkAddGsData( &vif1_pkt, SCE_GS_SET_TRXREG( w, h ) );
  sceVif1PkAddGsData( &vif1_pkt, SCE_GS_TRXREG );

  // set FINISH event occurrence request
  sceVif1PkAddGsData( &vif1_pkt, ( u_long ) ( 0x0 ) );
  sceVif1PkAddGsData( &vif1_pkt, SCE_GS_FINISH );

  // set transmission direction  ( LOCAL -> HOST Transmission )
  sceVif1PkAddGsData( &vif1_pkt, SCE_GS_SET_TRXDIR( 1 ) );
  sceVif1PkAddGsData( &vif1_pkt, SCE_GS_TRXDIR );

  // get packet size in quad words        
  sceVif1PkTerminate( &vif1_pkt );




  // get current IMR status
//  u_long prev_imr = 0;
//  prev_imr = sceGsPutIMR( sceGsGetIMR(  ) | 0x0200 );    // <<<<<  Mick, removed.


  // set the FINISH event
  DPUT_GS_CSR( GS_CSR_FINISH_M );



  // DMA from memory and start DMA transfer
  FlushCache( WRITEBACK_DCACHE );
  
	DPUT_D1_QWC( 0x7 );
  
	DPUT_D1_MADR( ( u_int ) vif1_pkt.pBase & DMA_MEM );

  
	DPUT_D1_CHCR( 1 | ( 1 << 8 ) );

  asm __volatile__( " sync.l " );


  // check if DMA is complete (STR=0)
  while ( DGET_D1_CHCR(  ) & 0x0100 );
//  printf( " 5 GS registers set\n" );

  // check if FINISH event occured
  while ( ( DGET_GS_CSR(  ) & GS_CSR_FINISH_M ) == 0 );
//  printf( " Finish event complete\n" );



  // change VIF1-FIFO transfer direction (VIF1 -> MAIN MEM or SPR)
  *VIF1_STAT = 0x00800000;


  // change GS bus direction (LOCAL->HOST)
  DPUT_GS_BUSDIR( ( u_long ) 0x00000001 );


//  printf( " Changed VIF1 and GS direction complete\n" );

  // DMA to memory and start DMA transfer
  FlushCache( WRITEBACK_DCACHE );
  
	DPUT_D1_QWC( texture_qwc );
  
	DPUT_D1_MADR( ( u_int ) base_addr & DMA_MEM );

  
	DPUT_D1_CHCR( 0 | ( 1 << 8 ) );
  
	asm __volatile__( " sync.l " );

  // check if DMA is complete (STR=0)
  while ( DGET_D1_CHCR(  ) & 0x0100 );
//  printf( " Transferred:(%d) QW from GS -> MEM complete\n", texture_qwc );



  // change VIF1-FIFO transfer direction (MAIN MEM or SPR -> VIF1)
  *VIF1_STAT = 0;

  // change GS bus direction (HOST->LOCAL)
  DPUT_GS_BUSDIR( ( u_long ) 0 );


  // restore previous IMR status
//  sceGsPutIMR( prev_imr );	 // <<<<<  Mick, removed.

  
  
  // set the FINISH event
  DPUT_GS_CSR( GS_CSR_FINISH_M );
//  printf( " Restore VIF1 and GS direction complete\n" );

  // MSKPATH3 is now enabled to allow transfer via PATH3
  DPUT_VIF1_FIFO( *( u_long128 * ) enable_path3 );

//  printf( " Restore PATH3 direction complete\n" );


}				// end StoreTextureVIF1



//----------------------------------------------EOF-------------------------------------------






/*****************************************************************************
**								  Externals									**
*****************************************************************************/


namespace NxPs2
{
	void	WaitForRendering();
}

namespace Gfx
{




/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

void	Manager::DumpVRAMUsage( void )
{
	printf ("ERROR: DumpVRAMUsage is no longer supported, due to dynamic textures\n");	
}


void 	save_to_pc(const char *fileroot,uint8* pixelBuffer)
{
		// Try to find a good filename of the format filebasexxx.bmp.  "Good" is
		// defined here as one that isn't already used.
		char fileName[ 132 ];
		int i = 0;
		while ( TRUE ) {
			sprintf( fileName, "Screens\\%s%03d.bmp", fileroot, i );
	
			// Found an unused one!  Yay!
			if ( FALSE == File::Exist( fileName ))
				break;
	
			i++;
		}
		printf ("Saving Screenshot %s\n",fileName);
		// Write out the file.	
		void *rwfd;
		rwfd = File::Open( fileName, "wb" );
		Dbg_MsgAssert(rwfd, ("Couldn't open %s for writing on the PC.", fileName));
		File::Write(pixelBuffer,1,640*448*3+HEADSIZE,rwfd);
		File::Close( rwfd );

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ScreenShot( const char *fileroot )
{


	#if 1

	bool	compact = true;

	FlushCache( 0 );
	sceGsSyncPath( 0, 0 );

	NxPs2::WaitForRendering(); 		// This will also ensure the DMA buffers are free
	sceGsSyncPath( 0, 0 );



	uint8 *pixelBuffer;
	// Get a huge chunk of memory and dump the video buffer into it.
	pixelBuffer = NxPs2::dma::pRuntimeBuffer + HEADSIZE + 10;

	// for compact mode, we need 640*448*31/32*3 + 640*448/32*4  + 54 bytes = 833280 + 35840 + 54 = 869174 bytes

	int lines=32;			// lines per StoreImage (we need to do it in at last two)
	for (int i=0;i<448/lines;i++)
	{
		// not got that much memory, so we convert the buffer in place
		uint32 *p_pixel4 =  (uint32*)( pixelBuffer +i*640*3*lines );
		
		StoreTextureVIF1(
		(u_long128*)( p_pixel4 ),
		0,  // offset....
		SCE_GS_PSMCT32,
		0,
		i*lines,
		640,
		lines,
		640 );

		uint8 *p_pixel3 =  (uint8*) p_pixel4;
		uint8 r, g, b;
		for (int x = 0;x < 640*lines;x++)
		{
			// 
			r = ((( *p_pixel4 ) >> 16 ) & 0xff ) ;
			g = ((( *p_pixel4 ) >> 8 ) & 0xff ) ;
			b = ((( *p_pixel4++ )  ) & 0xff ) ;
			
			*p_pixel3++ = r;
			*p_pixel3++ = g;
			*p_pixel3++ = b;
		}
	}
	
	// flip the buffer upside down in place
	uint8*		top_line = pixelBuffer;
	uint8*		bot_line = pixelBuffer + 640*3*447;
	for (int line = 0;line <224; line++)
	{
		for (int b = 0;b<640*3;b++)
		{
			uint8 t = top_line[b];
			top_line[b] = bot_line[b];
			bot_line[b] = t;
		}
		top_line += 640*3;
		bot_line -= 640*3;
	}

	// and insert the header
	SetupBMPHeader((uint8*)NxPs2::dma::pRuntimeBuffer+10,640,448);

	// Finally, I'm going to copy the whole pile of crap down 10 bytes
	// in case anything that saves it relies on it being 16 byte aligned
	uint8 *p1 = (uint8*)NxPs2::dma::pRuntimeBuffer+10;
	uint8 *p2 = (uint8*)NxPs2::dma::pRuntimeBuffer;
	for (int i=0;i<640*448*3+HEADSIZE;i++)
	{
		*p2++ = *p1++;
	}  
	pixelBuffer = (uint8*)NxPs2::dma::pRuntimeBuffer; 

	if (Script::GetInt("memcard_screenshots"))
	{
		printf ("STUBBED!!! Saving Screenshot to TH4MC???\n");
//		CFuncs::SaveDataFile("TH4MC", NxPs2::dma::pRuntimeBuffer, 640*448*3+HEADSIZE);
	}
	else
	{
		save_to_pc(fileroot,pixelBuffer);	
	}
	
	if (!compact)
	{
		// Release the memory for the video buffer.
		delete( pixelBuffer );
	}	
	
	#else
		printf ("Screenshot functionality stubbed out\n");
	
	#endif

	
    FlushCache(0);


}

// Called byte dumpshots
void Manager::DumpMemcardScreeenshots()
{
/*
	char name[100];
	for (int i=0;i<12;i++)
	{
		sprintf(name,"TH4MC%03d",i);
		printf ("Checking for %s \n",name);
		if (CFuncs::LoadDataFile(name, NxPs2::dma::pRuntimeBuffer, 640*448*3+HEADSIZE))
		{
			save_to_pc("MemCard", NxPs2::dma::pRuntimeBuffer);
		}
		else
		{
			printf ("not there\n");
		}
	}
*/	
	printf ("DumpMemcardScreeenshots STUBBED\n");
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx


================================================
FILE: Code/Gfx/NGPS/p_memview.cpp
================================================
//////////////////////////////////////////////////////
// p_memview.cpp
//
// code for tracking memory usage, and displaying it in a graphical manner
// keeps extra info about allocated blocks
// including the call stack, so we can print out information
// about specific allocated blocks
// which we will select using the graphical memory browser
//
//
// tried to use a little of the task system as possible
// so we can run the inspector
// without it messing with the heap it inspects
//
//


extern char _mem_dump_start[];
extern char _map_file_start[];
extern char _symbols_start[];
extern char _callstack_start[];
extern char _code_end[];
extern char	_std_mem_end[];
extern char	_stack_size[];

extern char __text_org[];
extern char __data_org[];
extern char __rodata_org[];
extern char __bss_org[];

extern char __rodata_orgend[];
extern char __bss_objend[];
extern char __text_objend[];

extern char _debug_heap_start[];
extern char _script_heap_start[];


//extern char _rwheapdebug_start[];

			 
#define	STACKDEPTH  30			 

int 	mem_strings;


namespace Nx
{
	extern void	_debug_change_2d_scale(float x);

}
			 
extern "C"				 
{
extern char ENTRYPOINT[];		


#include 
#include 
#include 
}

#include 
#include 
#include 					// needed for buttons
#include 
#include  			// needed for loading map file
#include 
#include 

#include 
#include 
#include 
#include 
#include 


#include 
#include 
#include 

// needed for some VerticalMenu specific debugging							
#include 
#include 
#include 


extern volatile int	test_vblanks;


class CCallStack
{
public:
	void Append(CCallStack *p);
	void Remove();
	void InitHead();
	int	used;
	int	size;
	CCallStack *pNext;
	CCallStack *pPrev;
	int	addr[STACKDEPTH];
	uint32	flags;
	Mem::Allocator::BlockHeader * pBlock;	// pointer to block that has this callstack

};

CCallStack		free_list;	// list of created objects
CCallStack		used_list;	// list of created objects

// init a node, so it can act as the head			 
inline void CCallStack::InitHead()
{
	pPrev = this;
	pNext = this;
}
	
// append node p to this node (after it)									 
inline void CCallStack::Append(CCallStack *p)
{

	p->pNext = this->pNext;
	p->pPrev = this;
	this->pNext = p;
	p->pNext->pPrev = p;
}

// simply unlink it from the list			   
inline void CCallStack::Remove()
{
	pPrev->pNext = pNext;
	pNext->pPrev = pPrev;
}


						
//CCallStack * CallStack_FirstFree;
//CCallStack * CallStack_FirstUsed; 

static int MemView_Active = 0;



#define	MAX_CALLSTACK (8192 * 8)		// we got 8 mb, woo woo.


static float step = 128.0f;


static char HexByte(char a)
{
	if (a >= '0' && a <='9')
	{
		return a-'0';
	}
	if (a >= 'A' && a <='F')
	{
		return 10 + a-'A';
	}
	if (a >= 'a' && a <='f')
	{
		return 10 + a-'a';
	}

	// should really be an error, but just ignore it and return 0
	// as this is only used for parsing the map file	
	return 0;
	
	
}


static int doneonce = 0;


char *MemView_GetFunctionName(int pc, int *p_size)
{
	if (!Config::GotExtraMemory())
	{
		return "NULL";
	}
		
	if (!pc)
	{
		return "NULL";
	}
				   
	// given an address, return the name of the function
	// does this by intially loading and buuilding a list of
	// all the start points, and names, of all the functions
	// by loading the skate3.map

	
	static int symbols = 0;
	
	if (!doneonce)
	{
	
//		mdl.m_fd = sceOpen( "host:ctrl_out.dat", SCE_RDWR );
///		sceRead( mdl.m_fd, mdl.m_recorded_data, 72000 * sizeof( Inp::RecordedData ));
//		sceClose( mdl.m_fd );


		char map_name[256];
		sprintf (map_name,"host0:..\\build\\ngpsgnu\\%s.map", Config::GetElfName());
		printf ("Map name = %s\n",map_name);

		int map_file_handle;
		map_file_handle= sceOpen(map_name, SCE_RDONLY);		

		if (map_file_handle < 0)
		{
			return "(map file not loaded yet)";
		}


		doneonce = 1;
	
		char *pQB= _map_file_start ;
		
		//sceRead(map_file_handle,pQB,4000000);
		long HedSize=sceLseek(map_file_handle, 0, SCE_SEEK_END);
		sceLseek(map_file_handle, 0, SCE_SEEK_SET);
		sceRead(map_file_handle,pQB,HedSize);
		sceClose(map_file_handle);
		// Now the file is loaded, we need to extract all the functions
		// so, search for the text 
		
		char *p = strstr(pQB,"0");	// Find the first address
		int	 *d = (int*)_symbols_start; 
		while (*p)
		{
			// the next 8 characters are the address in lower case hex
			int addr = 0;
			for (int i=0;i<8;i++)
			{
				addr <<= 4;
				addr += HexByte(*p++);
			}
			p+= 1;			// skip the space
			
			// the next 8 characters are the size in lower case hex
			int size = 0;
			for (int i=0;i<8;i++)
			{
				size <<= 4;
				size += HexByte(*p++);
			}
			
			// skip white spaces
			while( *p == ' ' )
			{
				p++;
			}
			
			int alignment = 0;
			do
			{
				alignment <<= 4;
				alignment += HexByte(*p++);
			} while( *p != ' ' );

			// skip white spaces
			while( *p == ' ' )
			{
				p++;
			}

			// only store symbols of non-zero size
			// otherwise, we get confused by having things like _bss_size in there
			// as they are not addresses, they just look like them, being so big...
			if (size || (addr >(int) __text_objend))
			{
				if( alignment == 0 )
				{
					*d++ = addr;		// store the address of the symbol
					*d++ = (int)p;			// store the start of the symbol name
					symbols++;		 	// one more symbol
				}
			}
			
			// search for first space, or CF, and replace with a 0
			// that way we ignore the "unmangled" version of the function
			while (*p && /**p!=' ' &&*/ *p!=0x0a && *p!='(' && *p != 0x0d) p++;	
			*p++ = 0;
			

			// skip to LF, and replace the 			
			while (*p && *p!=0x0a) p++;		// skip to start of next line
			p++;						// skip over 0a, will now be at the space on next line
		}

		uint32 *p_top  =  (uint32*)_symbols_start;
		for	(int i = 0;i pc)	 		// if this one is above the pc
		{
			*p_size = addr-s[-2];		// calculate the size of the function
			return (char*) (s[-1]);		// then the previous one is the function
		}
		s += 2;	
	}
		 
	return "UNKNOWN";
	
}

// Track the call stack by looking the instructions to
// see where the ra was stored on the stack
// iMadDepth is the number of address to trace back
// pDest is the location to put the results, whcih are stored as
//	dw	ra,0
// (not sure why the zero is there...)
int DumpUnwindStack( int iMaxDepth, int *pDest )
{
	if (!Config::GotExtraMemory())
	{
		return 0;
	}
	
	uint32* ra;
	uint64* sp;											// frame pointer
	ra = ((uint32*)DumpUnwindStack)+64;				// fake point in function to unwind from (
														// after the sd ra,0(sp), but before getting it back
	asm ( "daddu %0, $29, $0" : "=r" (sp) );			// get current sp

	if (!pDest)						   	
	{
		printf("\n");
	}
		
	int icd = iMaxDepth;	   							// depth counter
	uint32* last_ra = NULL;
	while ( icd-- )
	{
			/* scan instruction*/
		uint32* pc = ra;								// current pc, somewehre in middle of function
		uint32 count = 4096;							// enought to cover large functions	(16k)
		while ( count-- )
		{
			uint32 ins = *pc;		 					// get 32 bit instruction
			if (((ins >> 16) & 0x7fff) == 0x7fbf)		// sd ra,offset(sp)  (or sq, for .C files)
			{
				uint32 offset = *(short*)pc;			// get offset (bottom 16 bits)
				ra = (uint32*)(sp[offset>>3]);					// >>3 as it's at 64 bit word pointer
				break;
			}
			pc--;
		}
		while ( count--)
		{
			uint32 ins = *pc;		 					// get 32 bit instruction
			if ((ins >> 16) == 0x27bd)					// addiu sp,sp,offset
			{
				int offset = *(short*)pc;				// get offset (bottom 16 bits)
				if (offset & 0x8000)
				{
					offset |= 0xffff0000;
				}
				sp = (uint64*)( (int)(sp) - (offset));	   
				break; 	
			}			
			pc--;
		}

//		if (last_ra == ra)
//		{
//			icd++;			// one more please....
//		}
//		else
		{
			last_ra = ra;
			if (pDest)
			{
				*pDest++ = (int)ra;
				*pDest = 0;
			}
			else
			{
				int size;
	//			printf ("sp = %p, ra = %p %s\n",sp,ra,MemView_GetFunctionName((int)ra));
				printf ("%p: %s\n",ra,MemView_GetFunctionName((int)ra,&size));
			}
		}
	    
		// test to see if we have recursed up all the way...
		if (abs(int((int)ra - (int)&ENTRYPOINT)) < 1024
		   || (int)ra &3 
		   || (int)ra < 0x100000
		   || (int)ra > (int)_code_end		// and check it's not totally crazy....
		   )
		{
			return 0;
		}
	
	}
	return iMaxDepth - icd;
}
				   
				   
//        mD_L2           = nBit( vD_L2 ),
//        mD_R2           = nBit( vD_R2 ),
//        mD_L1           = nBit( vD_L1 ),
//        mD_R1           = nBit( vD_R1 ),
//        mD_TRIANGLE     = nBit( vD_TRIANGLE ),
//	      mD_CIRCLE       = nBit( vD_CIRCLE ),
//        mD_X            = nBit( vD_X ),
//        mD_SQUARE       = nBit( vD_SQUARE ),
//        mD_SELECT       = nBit( vD_SELECT ),
//        mD_L3           = nBit( vD_L3 ),
//        mD_R3           = nBit( vD_R3 ),
//        mD_START        = nBit( vD_START ),
//        mD_UP           = nBit( vD_UP ),
//        mD_RIGHT        = nBit( vD_RIGHT ),
//        mD_DOWN         = nBit( vD_DOWN ),
//        mD_LEFT         = nBit( vD_LEFT ),


void MemViewToggle()
{
	MemView_Active ^=1;
}


	 

void MemView_Alloc( void *v)
{
	if (!Config::GotExtraMemory())
	{
		return;
	}
	
#ifdef	__LINKED_LIST_HEAP__

	Mem::Allocator::BlockHeader *p = (Mem::Allocator::BlockHeader *)v;	
	
	static int cleared = 0;
	if (!cleared)
	{
		cleared = 1;
		free_list.InitHead();
		used_list.InitHead();
		CCallStack *p = (CCallStack *)_callstack_start;		
		for (int i=0;iRemove();
	used_list.Append(c);

	DumpUnwindStack(STACKDEPTH-1,c->addr);	   				// stick the call stack in there
	c->size = p->mSize;

	c->flags = 0;
 
	p->mp_debug_data = (void*)c;		 					// and store it in the block header
	c->pBlock = p;											// store pointer back
	
#endif
}

void MemView_Free( void *v)
{
	if (!Config::GotExtraMemory())
	{
		return;
	}
	
#ifdef	__LINKED_LIST_HEAP__
	// Need to remove it from the used list
	// and add it back to the full list
	
	Mem::Allocator::BlockHeader *p = (Mem::Allocator::BlockHeader *)v;						  
						  
	CCallStack *c = (CCallStack*)p->mp_debug_data;	
	
	if (!c)
	{
		// no debug data, so probably a re-alloc
		// should probably handle those later
		return;
	}

	// we clear it, in case this header is re-used later 
	// I'm not entirely sure how well this will work
	p->mp_debug_data = NULL;	
	c->Remove();
	free_list.Append(c);
	
#endif	
}


Mem::Allocator::BlockHeader *MemView_FindBlock( int addr)
{
	if (!Config::GotExtraMemory())
	{
		return NULL;
	}

#ifdef	__LINKED_LIST_HEAP__

	
	Mem::Allocator::BlockHeader *pSmallestBlock	= NULL;
	uint32 smallest_block_size = 100000000;
	Mem::Manager& mem_man = Mem::Manager::sHandle();
	for (Mem::Heap* heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
	{
		Mem::Allocator::BlockHeader *pBlock = (Mem::Allocator::BlockHeader *) heap->find_block((void*)addr);	
		if (pBlock)
		{
			if (pBlock->mSize < smallest_block_size)
			{
				smallest_block_size = pBlock->mSize;
				pSmallestBlock = pBlock;
			}
		}
	}
	return pSmallestBlock;
#else
	return NULL;
#endif
}

char * MemView_GetClassName(CCallStack *c)
{
	if (!Config::GotExtraMemory())
	{
		return NULL;
	}

#ifdef	__LINKED_LIST_HEAP__
	int *ra = (int*)(c->addr[4]);
	if (!ra) return NULL;
	int count = STACKDEPTH-4;
	while (count--)
	{
		int instruction = *ra++;
		if (instruction >> 24 == 0x0c)
		{
			int code = (instruction & 0xffffff)<<2;
			int size;
			char *p = MemView_GetFunctionName(code,&size); 
			// to tell if this is class or not
			// we see if the text is of the form  
			//    classname::classname (teminated by a 0)
			// as that indicates that it is a constructor
			// dude... this is where we need a regular expression....
			char *end = p;
			while (*end) end++;	   						// scan to end
			while (end[-1] != ':' && end > p)	end--;	// skip to char after the last :
			char *other = strstr(p,end);				// find fist occurance of end of string
			if (other != end)							// if different, then this is it!!
			{
				return MemView_GetFunctionName(code,&size);
				break;
			}
		}
	}
#endif
	return NULL;
}


void MemView_DumpBlockInfo(int cursor)
{
	if (!Config::GotExtraMemory())
	{
		return;
	}

#ifdef	__LINKED_LIST_HEAP__
	
	Mem::Allocator::BlockHeader *pBlock = MemView_FindBlock(cursor);
	if (!pBlock)
	{
		// should search free blocks here???
	}
	// find this in the allocators used list
	// and say if it is free, or not	
	if (pBlock == NULL)
	{
		if (cursor > (int)__text_org && cursor < (int)__bss_objend)		// check to see if in code/data
		{
			
			if (cursor < (int)__data_org)
				printf("Code: ");
			else if (cursor < (int)__rodata_org)
				printf("Data: ");
			else if (cursor < (int)__bss_org)
				printf("RO-Data: ");
			else 
				printf("BSS: ");
			
		
			int size;
			char *p_name = MemView_GetFunctionName(cursor,&size);
			printf ( "%s, size %d\n",p_name,size);
		}
		else
		{
			printf ("Block Not Found\n");
		}
	}
	else
	{
		void * p_start = (void*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize);
		printf ("Block found, addr = %p, size = %d (Header = %d)\n",p_start,pBlock->mSize,Mem::Allocator::BlockHeader::sSize);
		CCallStack *c = (CCallStack*)pBlock->mp_debug_data;
		if (!c)
		{
			printf ("Block with No Debug Info!!\n");
		}
		else
		{
			// assume this is a "new", then the fourth callstack ra will point to the 
			// jal xxxxxx  instruction, where xxxxx is the constructor for the 
			// or it might be sortly thereafter, so check 16 instructions
			
			char * classname = MemView_GetClassName(c);
						
			if (classname)
			{
				printf ("CLASS: %s\n",classname);		
			}

			// then list out the call stack (skipping the MemView_Alloc, as that's a given, and irrelevant);			
		
			int *p = c->addr + 1;
			while (p[1])			// also skip the ENTRYPOINT, just go back to main()
			{
				int size;
				printf ("%p: %s\n",(void*)*p,MemView_GetFunctionName(*p,&size));
				p++;
			}
		}
	}
#endif
}

static	int	blockstart;

static float cursor;

void 		MemView_Display()
{
#ifdef	__DEBUG_CODE__

	if (!Config::GotExtraMemory())
	{
		return;
	}

	if (Config::CD())
	{
		return;
	}	


	if (!MemView_Active)
	{
		return;
	}

	FlushCache( 0 );
	sceGsSyncPath( 0, 0 );
	
	//perfrom the copying
	// there are 512x256 words in the rectangle
	// and 32768*1024 bytes in memory
	// giving us a step of 256 (i.e, sample every 256th bytes)
	
	
	// The start of the middle line will be at 
	//       start + 512 * 2 * 128 * step;
	// then  start1 + 512 * 2 * 128 * step1
	// for them to be the same, start + 512 * 2 * 128 * step = start1 + 512 * 2 * 128 * step1
	// so start1 =  start + 512 * 2 * 128 * (step - step1)
	
	

	blockstart = 0;
	int blockend = 0;
	
	static float last_start;				 
	
	float start = cursor - (512.0f * 2.0f * 128.0f * step);
	
	int i_cursor = (int)cursor;
	
	Mem::Allocator::BlockHeader *pBlock = MemView_FindBlock(i_cursor);

	if (pBlock)
	{
		blockstart = (int)((uint)pBlock + Mem::Allocator::BlockHeader::sSize);
		int size = pBlock->mSize;
		blockend = (int)((int)blockstart + size);
	}
	
	if (start != last_start)
	{
		last_start = start;
		printf ("\nCursor Addr = %p\n",(void*)i_cursor);   	
		MemView_DumpBlockInfo(i_cursor);
	}
	
	
	static int color = 10 + (10<<5) ;
//	color ^= 5 << 10;
	
	float f_source = start;
	float f_off = 0.0f;
//	uint16 *source = (uint16*)(intstart&~1);   		// converting from a float to a pointer... yowza!!!
	uint16 *dest  =  (uint16*)_mem_dump_start;
	for (int i=0;i<512*256-4096;i++)
	{
		uint16 *source = (uint16*)((int)(f_source + f_off) &~1);		
	
		uint32 word;
		if (source < (uint16*)0x00100000 || source >= (uint16*)(0x08000000))
		{
			word = (3<<10)+(3<<5)+(3);			// grey for outside of memory		
		}
		else
		{
			word = *source;
			
//			#if 0 		// only display the unused memor
//			if (word !=0 && word !=0x5555)
//			{
//				word = 0x8;		// a used word, set to some dark color
//			}
//			#endif
			
			if (blockstart && (int)(source)>=blockstart && (int)(source)  (uint32)_code_end /*&& x < (uint32)_std_mem_end*/) // don't check for end now, as we have some debug heaps up there we want to include
		{
			// check to see if it points to one of the heap members
			
			uint32 *p_refs = (uint32*)_mem_dump_start;
			
		#if 0
			for (int i=0;i x)
					{
						high = mid;
					}
					else
					{
						// if the low point is already the same as the mid point
						// then the only way to go is up!
						// as this will only occur when low + 1 == high
						if (low == mid)
						{
							low = high;
						}
						else
						{
							low = mid;
						}
					}				
				}
				x -= 16;
			}
			x = oldx;
		#endif						  		
		}
	}
	
	
}
#endif

#ifdef	__LINKED_LIST_HEAP__
static uint32 *p_used;		
#endif

int MemView_CountBlocks(Mem::Allocator::BlockHeader *p_header)
{
	if (!Config::GotExtraMemory())
	{
		return 0;
	}

#ifdef	__LINKED_LIST_HEAP__
	int num_used = 0;
	while ( p_header )
	{
		void * p_start = (void*)((uint)p_header + Mem::Allocator::BlockHeader::sSize);
		
		*p_used++ = (uint32)p_start;	 		// store the start of the block
		*p_used++ = 0;							// store a count
		p_header = p_header->mp_next_used;
		num_used++;
	}
	return num_used;
#else
	return 0;
#endif
}


int blockCompFunc( const void *arg1, const void *arg2 )
{
	uint32 addr1 = (*(uint32*)arg1);
	uint32 addr2 = (*(uint32*)arg2);

	if ( addr1 == addr2 )
	{
		return 0;
	}
	else if ( addr1 < addr2 )
	{
		return 1;
	}
	else
	{
		return -1;
	}

}


// Find memory leaks
// the algorithm is quite simple:
// 1) Make a list of all "used" memory blocks, and set their usage count to 0
// 2) Scan all of the heap, and the stack, for each word that looks like a pointer, 
//    check to see if it is in the list of "used", and increment the usage count if so
// 3) Scan the list of used pointers, and check for any with usage == 0

void MemView_FindLeaks()
{
	if (!Config::GotExtraMemory())
	{
		return;
	}
	
#ifdef	__LINKED_LIST_HEAP__
		p_used  =  (uint32*)_mem_dump_start;		
		num_used = 0;
		printf ("Counting blocks....");		
  		Mem::Heap * p_heap = Mem::Manager::sHandle().FirstHeap();
		while (p_heap)
		{
			num_used += MemView_CountBlocks(p_heap->first_block()); 
			p_heap = Mem::Manager::sHandle().NextHeap(p_heap);
		}

		printf (" %d\n",num_used);
		printf ("Sorting .....\n");			
		// Now we've done that, let's sort the list, so we can use a binary search later
		
		
		#if 1
		uint32 *p_top  =  (uint32*)_mem_dump_start;		
		for	(int i = 0;i= 10)
		{
			printf ("Stopping after %d refs\n",count);
			return;
		}
		if (p_start >= p_end)
		{
			printf ("No more References Found in heap \n");
			return;
		}
	}
#endif
}

// Find the first block in the free list
// if no free blocks, then return
// scan all used blocks, and print out the info for all the blocks
// that have an address above the first free block
void MemView_DumpFragments(Mem::Heap *pHeap)
{
	if (!Config::GotExtraMemory())
	{
		return;
	}

	#ifdef	__LINKED_LIST_HEAP__    

	if (!pHeap->mFreeBlocks.m_count)
	{
		printf ("NO Fragmentation\n");
		return;
	}
	
	if (!pHeap->mp_context->mp_free_list)
	{
		printf ("!!!!!! No free list, but there are %d free blocks???\n",pHeap->mFreeBlocks.m_count);
		return;
	}

	Mem::Allocator::BlockHeader *p_free = pHeap->mp_context->mp_free_list;
	
	while (p_free->mSize < 10000)
	{
		Mem::Allocator::BlockHeader *p_next = p_free->mpNext;
		if (!p_next)
		{
			printf ("Did not find a free block >10K ?????\n");
			return;
		}		
		p_free = p_next;
	}
	
	Mem::Allocator::BlockHeader *p_full = pHeap->mp_context->mp_used_list;
	
	printf ("!!!!!! Free list starts at %p\n",p_free);
	

	// The first p_free will be the start of fragmentations
	while (p_full)
	{
		if (p_full > p_free)
		{
			printf ("\nFramgented Block\n\n");
			void * p_start = (void*)((uint)p_full + Mem::Allocator::BlockHeader::sSize);
			MemView_DumpBlockInfo((int)p_start);
			for (int xx=0;xx<1000000;xx++);		// little delay, to allow printfs to work
		}
		p_full = p_full->mp_next_used;
	}
	#endif
}

void MemView_DumpHeap(Mem::Heap *pHeap, uint32 mask)
{
	if (!Config::GotExtraMemory())
	{
		return;
	}

	#ifdef	__LINKED_LIST_HEAP__    

//	Mem::Allocator::BlockHeader *p_free = pHeap->mp_context->mp_free_list;
	Mem::Allocator::BlockHeader *p_full = pHeap->mp_context->mp_used_list;
	
	// The first p_free will be the start of fragmentations
	while (p_full)
	{
//		if (p_full > p_free)
//		CCallStack *c = (CCallStack*)p_full->mp_debug_data;	
//		if (!mask || !c || !(c->flags && mask))
		{
			printf ("\n");
			void * p_start = (void*)((uint)p_full + Mem::Allocator::BlockHeader::sSize);
			MemView_DumpBlockInfo((int)p_start);
		}
		p_full = p_full->mp_next_used;
	}
	#endif
}



void MemView_DumpBottomFragments()
{
	if (!Config::GotExtraMemory())
	{
		return;
	}

	MemView_DumpFragments(Mem::Manager::sHandle().BottomUpHeap());
}

void MemView_DumpTopFragments()
{
	if (!Config::GotExtraMemory())
	{
		return;
	}

	MemView_DumpFragments(Mem::Manager::sHandle().TopDownHeap());
}



/*
class CCallStack
{
public:
	void Append(CCallStack *p);
	void Remove();
	void InitHead();
	int	used;
	int	size;
	CCallStack *pNext;
	CCallStack *pPrev;
	int	addr[STACKDEPTH];

};
*/

struct SBlockType
{
	int	return_addr; 			// first meaningful return addr 
	
	int	size;					// size of block (if we want to sort by it
	int	total;					// total size of this type
	int	actual;					// actual total size, including headers
	char *p_class;				// points to class node 
	
	int	count;
};

// scan throught the list of "used" blocks
// and sort them into a list, organized by "type"
// the "type" is determined by the first return address after
// a callstack entry that is either "Malloc" or "Spt::Class::operator new"
// the "type" is furthur sorted by either "size" or "Class"
// where "size" is the size of the block (for a Malloc)
// and "Class" is the type of class that constructed this block 

#define	MAX_TYPES 10000


void MemView_DumpAnalysis( SBlockType* blocks, int numBlocksToPrint )
{
	if (!Config::GotExtraMemory())
	{
		return;
	}
	
#ifdef	__LINKED_LIST_HEAP__
	// Sorts the types, and print out totals

	int temp;

	for (int i = 0; i < numBlocksToPrint; i++)
	{
		for (int j = i+1;jsize;	  	// size is the only thing we know for sure
	int	return_addr = 0;				// default unknown return address
	char *p_class = "not a class";
	int latest = 1;
	int i = 0;
	
	for ( i = 1; i < 8; i++ )
	{
		int xsize;

		/*
		// the types of call stack we may encounter:
		// need to 
			0x10be48: Mem::Heap::allocate                                                   
			0x109914: Mem::Manager::New                                                     
			0x1035b0: Spt::Class::operator new                                              
			0x161094: Front::KeyboardControl::sCreateInstance   

		*/

		char *p_name = MemView_GetFunctionName(pCallStack->addr[i],&xsize);
		if (!strcmp("Malloc",p_name) 
			|| !strcmp("Spt::Class::operator new",p_name)
			|| !strcmp("Mem::Manager::New",p_name)
			|| !strcmp("Mem::AllocRegion::AllocRegion",p_name)
			|| !strcmp("ReallocateShrink",p_name)
			|| !strcmp("Mem::Manager::ReallocateShrink",p_name)
			|| !strcmp("ReallocateDown",p_name)
			|| !strcmp("Pip::Load",p_name)
			|| !strcmp("Pip::LoadPre",p_name)
			|| !strcmp("Calloc",p_name)
			
			)
		{
			latest = i;
		}
	}

	if (latest != 1)
	{
		return_addr = pCallStack->addr[latest+1];
	}

	p_class = MemView_GetClassName(pCallStack);				// get class
	// right, now we have all the info on this block
	// let's see if we've got one just like it

	//		if (!p_class && !MemView_GetFunctionName(return_addr,&temp))
	/*		
			if (!return_addr)
			{
				for (int i = 0;i>%s<<\n",i,MemView_GetFunctionName(p->addr[i],&temp));

				}
				return;

			}
	*/
	
									  
	// check if it is a string, and print it out, if so					

	
	int temp;				  
	if (!strcmp("Str::String::copy",MemView_GetFunctionName(return_addr,&temp)))
	{
		printf ("Str::String   <%s>\n",(char*)((char*)(pCallStack->pBlock)+32)); 
		mem_strings++;
	}	
	else if (!strcmp("Script::CreateString",MemView_GetFunctionName(return_addr,&temp)))
	{
		printf ("Script String <%s>\n",(char*)((char*)(pCallStack->pBlock)+32)); 
		mem_strings++;
	}

	
	

	// Analyze the contents of the block
	// counting zero, and unused (0x555555) words
//	uint32 *p


	int dummy;
	// check to see if this block is already included
	for ( i = 0; i < num; i++ )
	{
		//int diff = pBlocks[i].return_addr - 
		if ( /* pBlocks[i].p_class == p_class*/
			 /*&& pBlocks[i].size == size */
			 /*&& pBlocks[i].return_addr == return_addr*/
			 // just check if allocated by the same function
			 
			 MemView_GetFunctionName(pBlocks[i].return_addr,&dummy) == MemView_GetFunctionName(return_addr,&dummy)
			  
			  )
		{
			pBlocks[i].count++;
			pBlocks[i].total += size;
			pBlocks[i].actual += size + Mem::Allocator::BlockHeader::sSize;
			break;				
		}
	}
	
	if (return_addr == NULL)
	{
		
		printf ("\nUntraceable Block");   	
		MemView_DumpBlockInfo(((int)(pCallStack->pBlock)+32));	
	}
	

	// if not, then add the block
	if ( i == num )
	{
		pBlocks[i].p_class = p_class;
		pBlocks[i].size = size;
		pBlocks[i].total = size;
		pBlocks[i].actual = size + Mem::Allocator::BlockHeader::sSize;
		pBlocks[i].return_addr = return_addr;
		pBlocks[i].count = 1;
		num++;
	}
}

#ifdef	__LINKED_LIST_HEAP__
static int bbb = 0;	   	// compiler patch var, see below
#endif

void MemView_AnalyzeBlocks(uint32 mask)
{
	if (!Config::GotExtraMemory())
	{
		return;
	}
	
#ifdef	__LINKED_LIST_HEAP__
	SBlockType  *pBlocks = (SBlockType  *)_mem_dump_start;	// temp memory
	int	num_blocks = 0;
	int num = 0;

	printf ("\nAnalyzing blocks....\n");
	
	mem_strings = 0;
					
	CCallStack *p = used_list.pNext;  
	while (p != &used_list)
	{
		// Get the actualy block we referred to
//		Mem::Allocator::BlockHeader * pBlock = p->pBlock;
//		void * p_start = (void*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize);
		// Otionally check to see if it on the front end heap
//		if (Mem::SameContext(p_start,Mem::Manager::sHandle().FrontEndHeap()))
		{
			if (!mask || !(p->flags & mask))
			{
				MemView_AnalyzeCallStack( p, pBlocks, num );			
				num_blocks++;
			}
		}
		p = p->pNext; 
	}
	printf("\n  THERE ARE %d STRINGS (above) \n\n",mem_strings);
	
	
	printf ("%d types, in %d total blocks\n", num, num_blocks); 

	MemView_DumpAnalysis( pBlocks, num );
	if (bbb)
	{
		MemView_DumpBottomFragments();			// just to get it compiling 
		MemView_DumpTopFragments();			// just to get it compiling 
	}		
#endif
}


void MemView_MarkBlocks(uint32 mask)
{
	if (!Config::GotExtraMemory())
	{
		return;
	}
	
#ifdef	__LINKED_LIST_HEAP__

	CCallStack *p = used_list.pNext;  
	while (p != &used_list)
	{
		p->flags |= mask;
		
		p = p->pNext;
	}
#endif
}



void MemView_Input(uint buttons, uint makes, uint breaks)
{
	if (!Config::GotExtraMemory())
	{
		return;
	}

	if (Config::CD())
	{
		return;
	}	

//	if (makes & Inp::Data::mD_TRIANGLE)
//	{
//		MemView_Active = !MemView_Active;
//	}


	if (!MemView_Active)
	{
		// Stupid hook up of scaling buttons for overhead view

		#ifdef	__DEBUG_CODE__  	
		if (buttons & Inp::Data::mD_CIRCLE)
			Nx::_debug_change_2d_scale(1.11);
		if (buttons & Inp::Data::mD_SQUARE)
			Nx::_debug_change_2d_scale(0.9);
		#endif		
	
	
	
		return;
	}

					  
	float step1 = step;
	
	float zoom = 1.1f;
	
	float scroll = 4.0f;

	



	if (buttons & Inp::Data::mD_LEFT)
	{
		step1 = step * zoom;
	}
	if (buttons & Inp::Data::mD_RIGHT)
	{
		step1 = step / zoom;
	}
	
	if (buttons & Inp::Data::mD_UP)
	{
//		start = start - scroll * 512.0f * 2.0f * step;
		cursor = cursor - scroll * 512.0f * 2.0f * step;
	}
	if (buttons & Inp::Data::mD_DOWN)
	{
//		start = start + scroll * 512.0f * 2.0f * step;
		cursor = cursor + scroll * 512.0f * 2.0f * step;
	}

	if (buttons & Inp::Data::mD_L1)
	{
//		start = start - scroll * 512.0f * 2.0f * step / 256.0f;
		cursor = cursor - scroll * 2.0f * 2.0f * step;
	}
	if (buttons & Inp::Data::mD_L2)
	{
//		start = start + scroll * 512.0f * 2.0f * step / 256.0f;
		cursor = cursor + scroll * 2.0f * 2.0f * step;
	}

#define 	MMMIN 	(0.0078125f)
	 				
	if (step1 1024.0f)
	{
		step1 = 1024.0f;
	}

//	start = start + (512.0f * 2.0f * 128.0f * (step - step1));

	step = step1;

	if (makes & Inp::Data::mD_CIRCLE)
	{
		if (blockstart)
		{
			MemView_DumpRefs(blockstart);
		}
//		MemView_MarkBlocks(1);
	}

	// We don't look for leaks automatically now, so I'v put it on "SQUARE"	
	if (makes & Inp::Data::mD_SQUARE)
	{
		MemView_FindLeaks();
//	Mem::Manager& mem_man = Mem::Manager::sHandle();		MemView_DumpHeap(1);
//	heap	= mem_man.TopDownHeap();
//	MemView_DumpFragments(heap);
//	MemView_DumpHeap(heap,1);

	}

	if (makes & Inp::Data::mD_X)
	{
//		MemView_AnalyzeBlocks();
	}

	// Triangle = Dump Fragmentation
/*	if (makes & Inp::Data::mD_TRIANGLE)
	{
		Mem::Manager& mem_man = Mem::Manager::sHandle();
		Mem::Heap* heap = mem_man.BottomUpHeap();
		Mem::Region* region = heap->ParentRegion();
		printf ("BottomUp Frag %dK, %d Blocks\n",heap->mFreeMem.m_count / 1024, heap->mFreeBlocks.m_count);
		printf ("Region %d/%d K", region->MemAvailable() / 1024, region->TotalSize() / 1024 );
		MemView_DumpFragments(heap);
	}
*/	

}

void MemView_AnalyzeHeap(Mem::Heap *pHeap)
{
	if (!Config::GotExtraMemory())
	{
		return;
	}
	
	if ( !pHeap )
		return;
	
#ifdef	__LINKED_LIST_HEAP__
	SBlockType  *pBlocks = (SBlockType  *)_mem_dump_start;	// temp memory
	int	num_blocks = 0;
	int num = 0;

	Mem::Allocator::BlockHeader *p_full = pHeap->mp_context->mp_used_list;

	while (p_full)
	{
		CCallStack* pCallStack = (CCallStack*)p_full->mp_debug_data;
		
		if ( pCallStack )
		{
			MemView_AnalyzeCallStack( pCallStack, pBlocks, num );
		}
		else
		{
			printf ("Block with No Debug Info!!\n");
		}

		p_full = p_full->mp_next_used;
	}
	printf("\n  THERE ARE %d STRINGS (above) \n\n",mem_strings);

	printf ("%d types, in %d total blocks\n", num, num_blocks); 
	
	MemView_DumpAnalysis( pBlocks, num );
#endif
}
  
  



================================================
FILE: Code/Gfx/NGPS/p_memview.h
================================================
////

#ifndef	__P_MEMVIEW_H__
#define	__P_MEMVIEW_H__

void MemView_Display();
void MemView_Input(uint buttons, uint makes, uint breaks);
void MemView_Alloc( void *v);
void MemView_Free( void *v);
void MemViewToggle();
void MemView_FindLeaks();
int DumpUnwindStack( int iMaxDepth, int *pDest );
char *MemView_GetFunctionName(int pc, int *p_size);
void MemView_DumpFragments(Mem::Heap *pHeap);
void MemView_AnalyzeBlocks(uint32 mask = 0);
void MemView_MarkBlocks(uint32 flags = 1 );
void MemView_DumpHeap(Mem::Heap *pHeap, uint32 mask =0);
void MemView_AnalyzeHeap(Mem::Heap *pHeap);

#endif



================================================
FILE: Code/Gfx/NGPS/p_nx.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_nx.cpp - PS2 platform specific interface to the engine
//
// This is PS2 SPECIFIC!!!!!!  So might get a bit messy
//
// If there is anything in here that is not PS2 specific, then it needs to 
// be in nx.cpp

#include	"gfx\nx.h"
#include	"gfx\NxViewMan.h"
#include	"gfx\NxMiscFX.h"
#include	"gfx\NxParticleMgr.h"
#include	"gfx\NxQuickAnim.h"
#include	"gfx\NGPS\p_NxGeom.h"
#include	"gfx\NGPS\p_NxLightMan.h"
#include	"gfx\NGPS\p_NxMesh.h"
#include	"gfx\NGPS\p_NxModel.h"
#include	"gfx\NGPS\p_NxSector.h"
#include	"gfx\NGPS\p_NxScene.h"
#include	"gfx\NGPS\p_NxSprite.h"
#include	"gfx\NGPS\p_NxTexture.h"
#include	"gfx\NGPS\p_NxTextured3dPoly.h"
#include	"gfx\NGPS\p_NxNewParticleMgr.h"
#include	"core\math.h"
#include    "gel\collision\collision.h"

#include 	"gfx\ngps\nx\nx_init.h"
#include 	"gfx\ngps\nx\material.h"
#include 	"gfx\ngps\nx\mesh.h"
#include 	"gfx\ngps\nx\occlude.h"
#include 	"gfx\ngps\nx\scene.h"
#include 	"gfx\ngps\nx\chars.h"
#include 	"gfx\ngps\nx\render.h"
#include 	"gfx\ngps\nx\geomnode.h"
#include 	"gfx\ngps\nx\immediate.h"
#include 	"gfx\ngps\p_nxweather.h"
#include 	"gfx\ngps\nx\switches.h"

#include 
#include 
#include 
#include 
#include 
#include 

namespace NxPs2
{
extern void	test_render_start_frame();
extern void	test_render(Mth::Matrix* camera_orient, Mth::Vector* camera_pos,  float view_angle, float screen_aspect);
extern void	test_render_end_frame();
extern void	test_init();			
extern bool IsVisible(Mth::Vector ¢er, float radius);
extern void	WaitForRendering();
extern uint32	*p_patch_ALPHA;
extern uint32	*p_patch_ALPHA2;
} // namespace NxPs2


namespace	Nx
{

/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CEngine::s_plat_start_engine()
{
	NxPs2::InitialiseEngine();
	mp_particle_manager = new CPs2NewParticleManager;
	Nx::CPs2Texture::sInitTables();
	mp_weather = new CPs2Weather;
}


void 		CEngine::s_plat_pre_render()
{
// Moved to StuffAfterGSFinished
//	CPs2LightManager::sUpdateEngine();
//
//	// particle systems effectively have their transform double-buffered for dma purposes
//	// so now is the time to do the buffer flip by updating the transforms
//	mp_particle_manager->UpdateParticles();

	NxPs2::test_render_start_frame();
}

void 		CEngine::s_plat_post_render()
{
	// bit of a hack, see below
//	Gfx::Camera *cur_camera = CViewportManager::sGetCamera( 0 );
//	if (cur_camera)
	{

		
		
		NxPs2::test_render_end_frame();
	}
}

//void shadow_test( float * p_sn, uint16 * p_index_list, int count, float * dir )
//{
//	// Test...
//
//	asm __volatile__(
//		"
//		.set noreorder
//
//		lqc2	vf09, 0x0(%1)		# light dir
//
//		lqc2	vf10, 0x0(%0)		# surface normal
//		addi	%2, %2, -1
//
//dot_loop:
//		vmul.xyz	vf13,	vf09,	vf10	# result = plane * center + plane_w [start]
//		addiu		%0,		%0,		12
//		lqc2		vf10,	0x4(%0)			# surface normal
//		vaddy.x		vf13,	vf13,	vf13y
//		vaddz.x		vf13,	vf13,	vf13z
//
//		qmfc2		$8,		vf13
//		sw			$8,		0x0(%0)
//		addiu		%0,		%0,		4
//
//		bne			%2,		$0,		dot_loop
//		addi		%2,		%2,		-1
//done:
//
//		.set reorder
//		": : "r" (p_sn), "r" (dir), "r" (count) : "$8" ); 
////
////		": "=r" (p_dot) : "r" (p_sn) : "$8", "$9", "$10");
//	
//
////#		lhu		$8, 0(%2)
////#		lhu		$9, 2(%2)
////#		lhu		$10, 4(%2)
////#		addiu	%2, %2, 6
////#
////#		add		$8, $8, %1
////#		add		$9, $9, %1
////#		add		$10, $10, %1
////#		lqc2	vf10, 0x0($8)		# p0
////#		lqc2	vf11, 0x0($8)		# p1
////#		lqc2	vf12, 0x0($8)		# p2
//
//}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CEngine::s_plat_render_world()
{
	NxPs2::RenderPrologue();	 	// should this be in test_render_start_frame? (Garrett: Answer is no, it won't work)
	NxPs2::RenderInitImmediateMode();


//#define NUM_TRIS 1851
////	float	xyz[(NUM_TRIS+2)*3];
//	float	sn[NUM_TRIS*4];
//	uint16	list[NUM_TRIS*3];
//	float	vec[3] = { 1.0f, 1.5f, 2.0f };
//
//	for ( int lp = 0; lp < NUM_TRIS; lp++ )
//	{
//		sn[(lp*4)+0] = 6.0f;
//		sn[(lp*4)+1] = 7.0f;
//		sn[(lp*4)+2] = 8.0f;
//
////		xyz[(lp*3)+0] = ((float)(lp/2));
////		xyz[(lp*3)+1] = ((float)(lp&1));
////		xyz[(lp*3)+2] = 0.0f;
//
//		list[(lp*3)+0] = ( lp + 0 ) * 12;
//		list[(lp*3)+1] = ( lp + 1 ) * 12;
//		list[(lp*3)+2] = ( lp + 2 ) * 12;
//	}
//
//	Tmr::CPUCycles cycles = Tmr::GetTimeInCPUCycles();
//	shadow_test( sn, list, NUM_TRIS, vec );
//	cycles = Tmr::GetTimeInCPUCycles() - cycles;
//	printf( "CPU Cycles for shadow test: %d\n", cycles );

	#if 0
	// (Mike) see if there are any viewports to render, and return if there aren't
	// (Mike) ...perhaps make this trivial by having a CViewportManager::sGetNumActiveCameras() ?
	bool no_viewports = true;
	for (int view_idx = 0; view_idx < CViewportManager::sGetNumActiveViewports(); view_idx++)
	{
		if (CViewportManager::sGetActiveViewport(view_idx)->GetCamera())
		{
			no_viewports = false;
			break;
		}
	}
	if (no_viewports)
	{
		return;
	}
	// (Mike) now we know there's something to render...
	#endif


/*
	#if 1
	// normal 4:3 TV	
	float view_angle = 72.0f;   			// The angle prepended by the width of the screen
	float screen_aspect = 4.0f/3.0f;		// physical ration of width to height on the screen	(4:3 or 16:9)
	#else 
	// anamorphic widescreen
	float view_angle = 80.0f;   			// The angle prepended by the width of the screen
	float screen_aspect = 16.0f/9.0f;	// physical ratio of width to height on the screen	(4:3 or 16:9)
	#endif
*/
//	CViewportManager::sSetScreenMode(vSPLIT_V);
	
	

	bool got_vu0 = NxPs2::OccludeUseVU0();
	Dbg_Assert(got_vu0);
	
	// Note: this method of setting up the camera must change
	// so that the p_nx module does not reference things higher up the hierarchy
	for (int view_idx = 0; view_idx < CViewportManager::sGetNumActiveViewports(); view_idx++)
	{
		Gfx::Camera *cur_camera;
		CViewport *p_cur_viewport = CViewportManager::sGetActiveViewport(view_idx);
		
		Dbg_MsgAssert(p_cur_viewport,("Unable to get viewport %d",view_idx));
		cur_camera = p_cur_viewport->GetCamera();
		
		if (cur_camera)
		{
			
			// Build the occluders first now that we know where the camera is for this frame.
			NxPs2::BuildOccluders( &( cur_camera->GetPos()));

			// Make camera matrix
			Mth::Matrix cam_matrix(cur_camera->GetMatrix());
			cam_matrix[W] = cur_camera->GetPos();

			// Garrett: For some reason, only these clipping values work
			NxPs2::RenderViewport(view_idx, cam_matrix, p_cur_viewport->GetRect(), cur_camera->GetAdjustedHFOV(), p_cur_viewport->GetAspectRatio(),
								  /*-cur_camera->GetNearClipPlane(), -cur_camera->GetFarClipPlane()*/ -1.0f, -100000.0f, s_render_mode > 1);
#	ifdef __USE_PROFILER__
			Sys::CPUProfiler->PushContext( 0, 0, 255 );	 	// Blue (Under Yellow) = Immediate mode rnedering
#	endif // __USE_PROFILER__

			// Draw the immediate mode stuff
			NxPs2::RenderSwitchImmediateMode();
			NxPs2::CImmediateMode::sViewportInit();

// Mick: Moved this here from test_render_end_frame
			NxPs2::Reallocate2DVRAM();

			if (NxPs2::FlipCopyEnabled()) // TT 13470, 13471, 11061 Particle systems displayed when screen is blank
			{
			
				Nx::render_particles();
				mp_particle_manager->RenderParticles();
	
				// Render Weather effects.
				mp_weather->Process( Tmr::FrameLength() );
				mp_weather->Render();
	
				//Nx::CTextured3dPoly::sRenderAll();
				TextureSplatRender();
				ShatterRender();			// Make sure this is always the last immediate draw since it switches DMA lists
			}
						
			NxPs2::RenderSwitchImmediateMode();

#	ifdef __USE_PROFILER__
			Sys::CPUProfiler->PopContext(  );	 
#	endif // __USE_PROFILER__

			if (s_render_mode)
			{
#ifdef __NOPT_ASSERT__
				for (int i = 0; i < MAX_LOADED_SCENES; i++)
				{
					if (sp_loaded_scenes[i])
					{
						sp_loaded_scenes[i]->DebugRenderCollision(s_debug_ignore_1, s_debug_ignore_0);
					}
				}
#endif

				const Lst::Head& composite_obj_list = Nx::CEngine::sGetMovableObjects();
				Lst::Node* p_composite_node = composite_obj_list.GetNext();
				while( p_composite_node )
				{
					Obj::CCompositeObject* p_composite_object = static_cast(p_composite_node->GetData());
					Dbg_MsgAssert(p_composite_object, ("Node in CObject list wasn't a CCompositeObject"));

					Obj::CCollisionComponent* p_collision_component = GetCollisionComponentFromObject( p_composite_object );
					if ( p_collision_component )
					{
						CCollObj* p_coll_obj = p_collision_component->GetCollision();
						if (p_coll_obj)
						{
							p_coll_obj->DebugRender( s_debug_ignore_1, s_debug_ignore_0 );
						}
					}

					p_composite_node = p_composite_node->GetNext();
				}
			}	
		}
		else
		{
			//printf ("RENDERING FRAME WITH NO CAMERA (Nothing rendered)\n");
		}
		
		// Screen flash rendering.
		Nx::ScreenFlashRender( view_idx, 0 );
	}

	//NxPs2::RenderEpilogueStart();
	
	if (got_vu0)
	{
		NxPs2::OccludeDisableVU0();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CScene	*	CEngine::s_plat_create_scene(const char *p_name, CTexDict *p_tex_dict, bool add_super_sectors)
{
	// Create scene class instance
	CPs2Scene *p_ps2_scene = new CPs2Scene;

	NxPs2::CGeomNode *p_geomnode = new NxPs2::CGeomNode;
	p_geomnode->AddToTree(LOADFLAG_RENDERNOW);
	p_geomnode->SetActive(true);
	p_ps2_scene->SetEngineCloneScene(p_geomnode);

	CScene *new_scene = p_ps2_scene;
	new_scene->SetInSuperSectors(add_super_sectors);
	new_scene->SetIsSky(false);

	return new_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CScene *CEngine::s_plat_load_scene_from_memory( void *p_mem, CTexDict *p_tex_dict, bool add_super_sectors, bool is_sky, bool is_dictionary )
{
	// GJ:  This is a stub function that should never be called by the PS2.
	// Currently, it's only needed by the X-box and the NGC
	// because all models are treated as "scenes" on those platforms.
	// On the PS2, models are treated as CPS2Mesh-es...

	Dbg_MsgAssert( 0, ( "This function is not supported on the PS2 right now" ) );

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CScene	*	CEngine::s_plat_load_scene(const char *p_name, CTexDict *p_tex_dict,
									   bool add_super_sectors, bool is_sky, bool is_dictionary)
{
	// Create scene class instance
	CPs2Scene *p_ps2_scene = new CPs2Scene;
	CScene *new_scene = p_ps2_scene;
	new_scene->SetInSuperSectors(add_super_sectors);
	new_scene->SetIsSky(is_sky);

	#ifdef __NOPT_ASSERT__
	CPs2TexDict *p_ps2_tex_dict = static_cast(p_tex_dict);
	Dbg_MsgAssert(p_ps2_tex_dict,("Invalid texture dictionsary for %s",p_name));
	#endif
	
	// load scene
	// Pip::Load the new file
	uint8 *p_pipData = (uint8*)Pip::Load(p_name);

	// 1st section is shadow volume data.
//	p_ps2_scene->mp_shadow_volume_header = (Nx::sShadowVolumeHeader*)p_pipData;
//	p_ps2_scene->mp_shadow_volume_header->p_vertex = (Nx::sShadowVertex *)&p_ps2_scene->mp_shadow_volume_header[1];
//		p_ps2_scene->mp_shadow_volume_header->p_connect = (Nx::sShadowConnect *)&p_ps2_scene->mp_shadow_volume_header->p_vertex[p_ps2_scene->mp_shadow_volume_header->num_verts];
//	p_ps2_scene->mp_shadow_volume_header->p_neighbor = (Nx::sShadowNeighbor *)&p_ps2_scene->mp_shadow_volume_header->p_connect[p_ps2_scene->mp_shadow_volume_header->num_faces];
//
//	uint8 * p_geom_data = (uint8*)p_ps2_scene->mp_shadow_volume_header->p_vertex;
//	p_geom_data = &p_geom_data[p_ps2_scene->mp_shadow_volume_header->byte_size];
	// PJR
	// Process my data here.

	// set up load flags
	uint32 load_flags = 0;
	if (!is_dictionary)
	{
		// render it unconditionally as opposed to holding in a database to be instanced later)
		load_flags |= LOADFLAG_RENDERNOW;
	} else {
		Dbg_MsgAssert(!add_super_sectors, ("Why would you need collision SuperSectors for a scene dictionary?"));
	}
	if (is_sky)
	{
		load_flags |= LOADFLAG_SKY;
	}

	// process data in place
	NxPs2::CGeomNode *p_geomnode = NxPs2::CGeomNode::sProcessInPlace(p_pipData, load_flags);
//	NxPs2::CGeomNode *p_geomnode = NxPs2::CGeomNode::sProcessInPlace(p_geom_data, load_flags);
	
	Dbg_MsgAssert(((int)(p_geomnode) & 0xf) == 0,("p_geomnode (0x%x) not multiple of 16 after sProcessInPlace\n"
													,(int)p_geomnode));

	
	p_ps2_scene->SetEngineScene(p_geomnode);

	return new_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CEngine::s_plat_add_scene(CScene *p_scene, const char *p_filename)
{
	Dbg_MsgAssert(/*!p_scene->is_dictionary &&*/ !p_scene->IsSky(), ("Can't add to sky or dictionary scene."));

	// load scene
	// Pip::Load the new file
	uint8 *p_pipData = (uint8*)Pip::Load(p_filename);

	Dbg_MsgAssert(p_pipData, ("s_plat_add_scene(): Can't open file %s", p_filename));

	// process data in place
	NxPs2::CGeomNode *p_geomnode = NxPs2::CGeomNode::sProcessInPlace(p_pipData, LOADFLAG_RENDERNOW);

	CPs2Scene *p_ps2_scene = static_cast(p_scene);
	p_ps2_scene->SetEngineAddScene(p_geomnode);

	return p_geomnode != NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CEngine::s_plat_unload_scene(CScene *p_scene)
{

	Dbg_MsgAssert(p_scene,("Trying to delete a NULL scene"));

	CPs2Scene * p_ps2_scene = (CPs2Scene*)p_scene;

	NxPs2::CGeomNode *p_geomnode = p_ps2_scene->GetEngineScene();

	if (p_geomnode)
	{
		// clean up the node we created
		p_geomnode->Cleanup();

		// Pip::Unload the file
		Pip::Unload(p_scene->GetSceneFilename());
	}
	
	// Clone scene node
#if 0		// Moved this into ~CPs2Scene so that this CGeomNode isn't destroyed before the cloned sectors
	NxPs2::CGeomNode *p_clone_geomnode = p_ps2_scene->GetEngineCloneScene();
	if (p_clone_geomnode)
	{
		p_clone_geomnode->Cleanup();
		delete p_clone_geomnode;
	}
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModel*		CEngine::s_plat_init_model(void)
{
	CPs2Model* pModel = new CPs2Model;

	return pModel;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CEngine::s_plat_uninit_model(CModel* pModel)
{
	Dbg_Assert( pModel );

	delete pModel;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom*	CEngine::s_plat_init_geom(void)
{
	CPs2Geom* pGeom = new CPs2Geom;

	return pGeom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CEngine::s_plat_uninit_geom(CGeom* pGeom)
{
	Dbg_Assert( pGeom );

	delete pGeom;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CQuickAnim* CEngine::s_plat_init_quick_anim()
{
	CQuickAnim* pQuickAnim = new CQuickAnim;

	return pQuickAnim;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEngine::s_plat_uninit_quick_anim(CQuickAnim* pQuickAnim)
{
	Dbg_Assert( pQuickAnim );

	delete pQuickAnim;

	return;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMesh*	CEngine::s_plat_load_mesh(uint32 id, uint32* pModelData, int modelDataSize, uint8* pCASData, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume)
{
	CPs2Mesh* pMesh = new CPs2Mesh(pModelData, modelDataSize, pCASData, pTexDict, texDictOffset, isSkin, doShadowVolume);

	return pMesh;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMesh*	CEngine::s_plat_load_mesh(const char* pMeshFileName, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume)
{
	CPs2Mesh* pMesh = new CPs2Mesh(pMeshFileName, pTexDict, texDictOffset, isSkin, doShadowVolume);

	return pMesh;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CEngine::s_plat_unload_mesh(CMesh* pMesh)
{
	Dbg_Assert( pMesh );

	delete pMesh;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CEngine::s_plat_set_mesh_scaling_parameters( SMeshScalingParameters* pParams )
{
	NxPs2::SetMeshScalingParameters( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSprite *	CEngine::s_plat_create_sprite(CWindow2D *p_window)
{
	return new CPs2Sprite(p_window);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CEngine::s_plat_destroy_sprite(CSprite *p_sprite)
{
	delete p_sprite;
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextured3dPoly *	CEngine::s_plat_create_textured_3d_poly()
{
	return new NxPs2::CPs2Textured3dPoly;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CEngine::s_plat_destroy_textured_3d_poly(CTextured3dPoly *p_poly)
{
	delete p_poly;
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::CTexture *CEngine::s_plat_create_render_target_texture( int width, int height, int depth, int z_depth )
{
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEngine::s_plat_project_texture_into_scene( Nx::CTexture *p_texture, Nx::CModel *p_model, Nx::CScene *p_scene )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEngine::s_plat_set_projection_texture_camera( Nx::CTexture *p_texture, Gfx::Camera *p_camera )
{
	#if !STENCIL_SHADOW
	// Currently assumes a top down view. So all we need to store is the position of the camera.
	// The width and height of the view frustum (this is an orthographic camera) are constant.
	Mth::Vector pos = p_camera->GetPos();
	NxPs2::SetTextureProjectionCamera( &pos, &pos );
	#endif
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_stop_projection_texture( Nx::CTexture *p_texture )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_add_occlusion_poly( uint32 num_verts, Mth::Vector *p_vert_array, uint32 checksum )
{
	if( num_verts == 4 )
	{
		NxPs2::AddOcclusionPoly( p_vert_array[0], p_vert_array[1], p_vert_array[2], p_vert_array[3], checksum );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_enable_occlusion_poly( uint32 checksum, bool enable )
{
	NxPs2::EnableOcclusionPoly( checksum, enable );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_remove_all_occlusion_polys( void )
{
	NxPs2::RemoveAllOcclusionPolys();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// returns true if the sphere at "center", with the "radius"
// is visible to the current camera
// (note, currently this is the last frame's camera on PS2)
bool CEngine::s_plat_is_visible( Mth::Vector&	center, float radius  )
{
	return NxPs2::IsVisible(center,radius);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_max_multipass_distance(float dist)
{
	NxPs2::render::sMultipassMaxDist = dist;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char *		CEngine::s_plat_get_platform_extension()
{
	return "PS2";	// String literals are statically allocated so can be returned safely, (Bjarne, p90)
}


/******************************************************************/
// Wait for any pending asyncronous rendering to finish, so rendering
// data can be unloaded
/******************************************************************/

void CEngine::s_plat_finish_rendering()
{
		NxPs2::WaitForRendering();		// PS2 Specific, wait for prior frame's DMA to finish	
} 

/******************************************************************/
// Set the amount that the previous frame is blended with this frame
// 0 = none	  	(just see current frame) 	
// 128 = 50/50
// 255 = 100% 	(so you only see the previous frame)												  
/******************************************************************/

void CEngine::s_plat_set_screen_blur(uint32 amount )
{
	amount = (255-amount)/2;
	uint32 alpha = *NxPs2::p_patch_ALPHA;
	alpha &= 0xffffff00;
	alpha |= amount; 
	*NxPs2::p_patch_ALPHA = alpha;	
	*NxPs2::p_patch_ALPHA2 = alpha;	
} 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	CEngine::s_plat_get_num_soundtracks()
{
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* CEngine::s_plat_get_soundtrack_name( int soundtrack_number )
{
	return NULL;
}



// Add some PS2 specific debug info to the metrics script structure
void	CEngine::s_plat_get_metrics(Script::CStruct * p_info)
{

	// get the scene, and get the root 

	Script::CStruct *p_ps2 = new Script::CStruct();
	
	NxPs2::CGeomMetrics * p_metrics = new NxPs2::CGeomMetrics;

	CPs2Scene * p_ps2_scene = (CPs2Scene *) sGetMainScene();
	if (p_ps2_scene->GetEngineScene())
	{
		p_ps2_scene->GetEngineScene()->CountMetrics(p_metrics);
		p_ps2->AddInteger(CRCD(0xd55cd658,"NULLEngineScene"),0);
	}	
	else
	{
		// K: Added this so that the script refresh_poly_count in lighttool.q
		// can display "N/A" for the values.
		p_ps2->AddInteger(CRCD(0xd55cd658,"NULLEngineScene"),1);
	}

	p_ps2->AddInteger(CRCD(0x3dd01ea1,"total"), p_metrics->m_total);        
	p_ps2->AddInteger(CRCD(0x3960ff18,"leaf"),  p_metrics->m_leaf);  
	p_ps2->AddInteger(CRCD(0x4de5330c,"objects"),  p_metrics->m_object);  
	p_ps2->AddInteger(CRCD(0x94927839,"vert"),  p_metrics->m_verts);  
	p_ps2->AddInteger(CRCD(0x169ee151,"poly"),  p_metrics->m_polys);  
	p_ps2->AddFloat(CRCD(0x58785e10,"verts_per_poly"),  (float)p_metrics->m_verts/(float)p_metrics->m_polys);  
	p_ps2->AddFloat(CRCD(0x3b75d7cc,"polys_per_object"),  (float)p_metrics->m_polys/(float)p_metrics->m_object);  
	p_ps2->AddFloat(CRCD(0xbc56249d,"polys_per_mesh"),  (float)p_metrics->m_polys/(float)p_metrics->m_leaf);  
		   
	p_info->AddStructurePointer(CRCD(0x26861025,"scene"),p_ps2);
	
	delete	p_metrics;


}


void CEngine::s_plat_set_letterbox( bool letterbox )
{
	NxPs2::DoLetterbox = letterbox;
} 
 


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_color_buffer_clear( bool clear )
{
}



}

================================================
FILE: Code/Gfx/NGPS/p_nxmiscfx.cpp
================================================
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Nx
{

#define	DRAW_DEBUG_LINES		0
#define PRINT_SHATTER_MEMORY	0


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sNGPSVert
{
	Mth::Vector		pos;
	Image::RGBA		col;
	float			u, v;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sNGPSSplatInstanceDetails : public sSplatInstanceDetails
{
	// Platform specific part.
	NxPs2::SSingleTexture	*mp_texture;
	sNGPSVert				m_verts[SPLAT_POLYS_PER_MESH * 3];
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sNGPSShatterInstanceDetails : public sShatterInstanceDetails
{
	// Platform specific part.
							sNGPSShatterInstanceDetails( int num_tris, NxPs2::CGeomNode *p_geom_node );
							~sNGPSShatterInstanceDetails( void );

	static int				sQueryMemoryNeeded(int num_tris);

	NxPs2::CGeomNode *		mp_geom_node;		// Corresponding source mesh
	sNGPSVert *				mp_vert_array;		// Array of vertices

	uint32 *				mp_tex_regs;		// packed texture registers
	int						m_num_tex_regs;		// number of texture registers
};



/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sNGPSShatterInstanceDetails::sNGPSShatterInstanceDetails( int num_tris, NxPs2::CGeomNode *p_geom_node ) : sShatterInstanceDetails( num_tris )
{
	mp_geom_node = p_geom_node;

#if PRINT_SHATTER_MEMORY
	Dbg_Message("Allocating %d bytes for vert array of %d tris", sizeof(sNGPSVert) * 3 * num_tris, num_tris);
#endif
	mp_vert_array = new sNGPSVert[num_tris * 3];

	// Get texture info
	if (p_geom_node->GetGroup())		// Garrett: Need to find the real way to figure out if a mesh
	{									// is textures or not (can always look at the GIFtag)
		uint32 *p_dma = (uint32 *) p_geom_node->GetDma();

		p_dma += 9;			// Go to vif::UNPACK() for texture regs

		m_num_tex_regs = (*p_dma >> 16) & 0xf;
		mp_tex_regs = p_dma + 1;
#if 0
		Dbg_Message("Found %d texture registers for type %x", m_num_tex_regs, (*p_dma >> 24));
		for (int i = 0; i < m_num_tex_regs; i++)
		{
			Dbg_Message(" - Register %x", mp_tex_regs[(i * 3) + 2]);
		}
#endif
	} else {
		mp_tex_regs = NULL;
		m_num_tex_regs = 0;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sNGPSShatterInstanceDetails::~sNGPSShatterInstanceDetails( void )
{
	delete [] mp_vert_array;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int			sNGPSShatterInstanceDetails::sQueryMemoryNeeded(int num_tris)
{
	int size = sShatterInstanceDetails::s_query_memory_needed(num_tris);

	size += (sizeof(sNGPSShatterInstanceDetails) - sizeof(sShatterInstanceDetails));
	size += sizeof(sNGPSVert) * num_tris * 3;

	return size;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sNGPSSplatInstanceDetails * getDetailsForTextureSplat( NxPs2::SSingleTexture *p_texture )
{
	sNGPSSplatInstanceDetails *p_ngps_details;

	Dbg_Assert( p_splat_details_table );
	
	// Check to see whether we have a scene already created for this type of texture splat.
	p_splat_details_table->IterateStart();
	sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
	while( p_details )
	{
		p_ngps_details					= static_cast( p_details );

		// If this one matches, return it...
		if (p_ngps_details->mp_texture == p_texture)
		{
			return p_ngps_details;
		}
		p_details = p_splat_details_table->IterateNext();
	}

	// Check to see that we have memory first
	if (Mem::Available() < (int) ((sizeof(sNGPSSplatInstanceDetails) * 3) / 2))
	{
		return NULL;
	}

	p_ngps_details = new sNGPSSplatInstanceDetails;
	p_ngps_details->m_highest_active_splat	= 0;
	p_ngps_details->mp_texture = p_texture;
	memset( p_ngps_details->m_lifetimes, 0, sizeof( int ) * SPLAT_POLYS_PER_MESH );
	
	for( int v = 0; v < SPLAT_POLYS_PER_MESH * 3; ++v )
	{
		p_ngps_details->m_verts[v].pos = Mth::Vector(0.0f, 0.0f, 0.0f, 1.0f);
		p_ngps_details->m_verts[v].col = Image::RGBA( 0x80, 0x80, 0x80, 0x80 );
	}

	p_splat_details_table->PutItem((uint32)p_ngps_details, p_ngps_details );

	return p_ngps_details;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool subdivide_tri_stack( float shatterArea, sNGPSVert **p_write, NxPs2::CGeomNode *p_node )
{
	// If there are elements on the stack, pop off the top three vertices and subdivide if necessary.
	if( triSubdivideStack.IsEmpty())
	{
		return false;
	}

	// Three temporary buffers.
	sNGPSVert v0;
	sNGPSVert v1;
	sNGPSVert v2;
	
	// Stack is LIFO, so Pop() off in reverse order.
	triSubdivideStack.Pop( &v2 );
	triSubdivideStack.Pop( &v1 );
	triSubdivideStack.Pop( &v0 );
	
	// Calculate the area of this tri.
	Mth::Vector p(	v1.pos[X] - v0.pos[X], v1.pos[Y] - v0.pos[Y], v1.pos[Z] - v0.pos[Z] );
	Mth::Vector q(	v2.pos[X] - v0.pos[X], v2.pos[Y] - v0.pos[Y], v2.pos[Z] - v0.pos[Z] );
	Mth::Vector r(( p[Y] * q[Z] ) - ( q[Y] * p[Z] ), ( p[Z] * q[X] ) - ( q[Z] * p[X] ), ( p[X] * q[Y] ) - ( q[X] * p[Y] ));
	float area_squared = r.LengthSqr();

	if( area_squared > shatterArea )
	{
		// We need to subdivide this tri. Calculate the three intermediate points.
		int block_size = triSubdivideStack.GetBlockSize();

		// Three temporary buffers.
		sNGPSVert i01;
		sNGPSVert i12;
		sNGPSVert i20;

		memcpy( &i01, &v0, block_size );
		memcpy( &i12, &v1, block_size );
		memcpy( &i20, &v2, block_size );

		// Deal with positions (always present).
		i01.pos[X] = v0.pos[X] + (( v1.pos[X] - v0.pos[X] ) * 0.5f );
		i01.pos[Y] = v0.pos[Y] + (( v1.pos[Y] - v0.pos[Y] ) * 0.5f );
		i01.pos[Z] = v0.pos[Z] + (( v1.pos[Z] - v0.pos[Z] ) * 0.5f );

		i12.pos[X] = v1.pos[X] + (( v2.pos[X] - v1.pos[X] ) * 0.5f );
		i12.pos[Y] = v1.pos[Y] + (( v2.pos[Y] - v1.pos[Y] ) * 0.5f );
		i12.pos[Z] = v1.pos[Z] + (( v2.pos[Z] - v1.pos[Z] ) * 0.5f );

		i20.pos[X] = v2.pos[X] + (( v0.pos[X] - v2.pos[X] ) * 0.5f );
		i20.pos[Y] = v2.pos[Y] + (( v0.pos[Y] - v2.pos[Y] ) * 0.5f );
		i20.pos[Z] = v2.pos[Z] + (( v0.pos[Z] - v2.pos[Z] ) * 0.5f );

		// Deal with colors (not always present).
		//if( p_node->m_diffuse_offset > 0 )
		{
			uint8	*p_v0col	= (uint8*)( &(v0.col));
			uint8	*p_v1col	= (uint8*)( &(v1.col));
			uint8	*p_v2col	= (uint8*)( &(v2.col));
			uint8	*p_i01col	= (uint8*)( &(i01.col));
			uint8	*p_i12col	= (uint8*)( &(i12.col));
			uint8	*p_i20col	= (uint8*)( &(i20.col));
		
			for( int i = 0; i < 4; ++i )
			{
				p_i01col[i]		= p_v0col[i] + (char)(( p_v1col[i] - p_v0col[i] ) * 0.5f );
				p_i12col[i]		= p_v1col[i] + (char)(( p_v2col[i] - p_v1col[i] ) * 0.5f );
				p_i20col[i]		= p_v2col[i] + (char)(( p_v0col[i] - p_v2col[i] ) * 0.5f );
			}
		}

		// Deal with uv0 (not always present).
		//if( p_node->m_uv0_offset > 0 )
		{
			float	*p_v0uv		= (float*)( &v0.u );
			float	*p_v1uv		= (float*)( &v1.u );
			float	*p_v2uv		= (float*)( &v2.u );
			float	*p_i01uv	= (float*)( &i01.u );
			float	*p_i12uv	= (float*)( &i12.u );
			float	*p_i20uv	= (float*)( &i20.u );
		
			// We know that v is contiguous to u
			for( int i = 0; i < 2; ++i )
			{
				p_i01uv[i]		= p_v0uv[i] + (( p_v1uv[i] - p_v0uv[i] ) * 0.5f );
				p_i12uv[i]		= p_v1uv[i] + (( p_v2uv[i] - p_v1uv[i] ) * 0.5f );
				p_i20uv[i]		= p_v2uv[i] + (( p_v0uv[i] - p_v2uv[i] ) * 0.5f );
			}
		}
		
		// Push the four new tris onto the stack.
		triSubdivideStack.Push( &v0 );
		triSubdivideStack.Push( &i01 );
		triSubdivideStack.Push( &i20 );

		triSubdivideStack.Push( &i01 );
		triSubdivideStack.Push( &v1 );
		triSubdivideStack.Push( &i12 );

		triSubdivideStack.Push( &i01 );
		triSubdivideStack.Push( &i12 );
		triSubdivideStack.Push( &i20 );

		triSubdivideStack.Push( &i20 );
		triSubdivideStack.Push( &i12 );
		triSubdivideStack.Push( &v2 );
	}
	else
	{
		// Don't need to subdivide this tri.
		int block_size = triSubdivideStack.GetBlockSize();

		// Just copy the tri into the next available slot.
		memcpy( *p_write, &v0, block_size );
		(*p_write)++;
		memcpy( *p_write, &v1, block_size );
		(*p_write)++;
		memcpy( *p_write, &v2, block_size );
		(*p_write)++;
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static bool same_side( Mth::Vector &p1, Mth::Vector &p2, Mth::Vector &a, Mth::Vector &b )
{
	Mth::Vector cp1 = Mth::CrossProduct( b - a, p1 - a );
	Mth::Vector cp2 = Mth::CrossProduct( b - a, p2 - a );
	if( Mth::DotProduct( cp1, cp2 ) >= 0.0f )
		return true;
    else
		return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static bool point_in_triangle( Mth::Vector &p, Mth::Vector &a, Mth::Vector &b, Mth::Vector &c )
{
	if( same_side( p, a, b, c ) && same_side( p, b, a, c ) && same_side( p, c, a, b ))
		return true;
    else
		return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline bool line_segment_intersection( float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4 )
{
	float den = (( y4 - y3 ) * ( x2 - x1 )) - (( x4 - x3 ) * ( y2 - y1 ));

	if( den == 0.0f )
	{
		// Parallel lines.
		return false;
	}
	
	float num_a = (( x4 - x3 ) * ( y1 - y3 )) - (( y4 - y3 ) * ( x1 - x3 ));
	float num_b = (( x2 - x1 ) * ( y1 - y3 )) - (( y2 - y1 ) * ( x1 - x3 ));

	num_a /= den;
	num_b /= den;

	if(( num_a <= 1.0f ) && ( num_b <= 1.0f ))
		return true;

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline bool tri_texture_intersect( float u0, float v0, float u1, float v1, float u2, float v2 )
{
	// Trivial check to see if all three points are outside range of texture.
	if(( u0 < -1.0f ) && ( u1 < -1.0f ) && ( u2 < -1.0f ))
		return false;
	if(( u0 > 1.0f ) && ( u1 > 1.0f ) && ( u2 > 1.0f ))
		return false;
	if(( v0 < -1.0f ) && ( v1 < -1.0f ) && ( v2 < -1.0f ))
		return false;
	if(( v0 > 1.0f ) && ( v1 > 1.0f ) && ( v2 > 1.0f ))
		return false;
	
	// Check that at least one corner of the texture falls within the tri.
	Mth::Vector texture_square[4] = {	Mth::Vector( -1.0f, -1.0f, 0.0f, 0.0f ),
										Mth::Vector(  1.0f, -1.0f, 0.0f, 0.0f ),
										Mth::Vector(  1.0f,  1.0f, 0.0f, 0.0f ),
										Mth::Vector( -1.0f,  1.0f, 0.0f, 0.0f )};

	Mth::Vector a( u0, v0, 0.0f );
	Mth::Vector b( u1, v1, 0.0f );
	Mth::Vector c( u2, v2, 0.0f );

	for( int p = 0; p < 4; ++p )
	{
		if( point_in_triangle( texture_square[p], a, b, c ))
		{
			return true;
		}
	}

	// No corners of the texture fall within the tri. There are 3 possible explanations:
	// 1) The tri intersects the texture, but does not contain a texture corner.
	// 2) The texture lies entirely outside of the tri.
	// 3) The tri falls entirely within the texture.
	// Given the relatively small size of the textures, case (3) is extremely unlikely.

	// Perform a trivial check to see whether a corner of the tri lies within the texture. This will catch (3) and sometimes (1).
	if(( u0 >= -1.0f ) && ( u0 <= 1.0f ) && ( v0 >= -1.0f ) && ( v0 <= 1.0f ))
		return true;
	if(( u1 >= -1.0f ) && ( u1 <= 1.0f ) && ( v1 >= -1.0f ) && ( v1 <= 1.0f ))
		return true;
	if(( u2 >= -1.0f ) && ( u2 <= 1.0f ) && ( v2 >= -1.0f ) && ( v2 <= 1.0f ))
		return true;

	// Perform the complete check to see if any line segment forming the tri intersects any line segment forming the texture.
	for( int p = 0; p < 4; ++p )
	{
		int q = ( p + 1 ) % 4;
		if( line_segment_intersection( u0, v0, u1, v1, texture_square[p][X], texture_square[p][Y], texture_square[q][X], texture_square[q][Y] ))
			return true;
	}
	
	return false;
}

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_screen_flash_render( sScreenFlashDetails *p_details )
{
	// Get viewport details.
	CViewport *p_vp = CViewportManager::sGetActiveViewport( p_details->m_viewport );
	
	// Centre of screen is (0x8000, 0x8000), unit is 1/16 pixel.
	
	int x0 = XOFFSET + (int)( p_vp->GetOriginX() * (HRES << 4) );
	int y0 = YOFFSET + (int)( p_vp->GetOriginY() * (VRES << 4) );
	int x1 = x0 + (int)( p_vp->GetWidth() * (HRES << 4) );
	int y1 = y0 + (int)( p_vp->GetHeight() * (VRES << 4) );

	uint32 rgba = p_details->m_current.r | ((uint32)p_details->m_current.g << 8 ) | ((uint32)p_details->m_current.b << 16 ) | ((uint32)p_details->m_current.a << 24 );
	
	NxPs2::DrawRectangle( x0, y0, x1, y1, rgba );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_initialize( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_cleanup( void )
{
	sNGPSSplatInstanceDetails *p_ngps_details;

	Dbg_Assert( p_splat_details_table );
	
	p_splat_details_table->IterateStart();
	sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
	while( p_details )
	{
		p_ngps_details = static_cast( p_details );
		
		p_details = p_splat_details_table->IterateNext();

		p_splat_details_table->FlushItem((uint32)p_ngps_details );
		delete p_ngps_details;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_reset_poly( sSplatInstanceDetails *p_details, int index )
{
	// Cast the details to NGPS details.
	sNGPSSplatInstanceDetails *p_ngps_details = static_cast( p_details );
	
	// Force this poly to be degenerate.
	p_ngps_details->m_verts[index * 3 + 1]	= p_ngps_details->m_verts[index * 3];
	p_ngps_details->m_verts[index * 3 + 2]	= p_ngps_details->m_verts[index * 3];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool plat_texture_splat( Nx::CSector **pp_sectors, Nx::CCollStatic **pp_collision, Mth::Vector& start, Mth::Vector& end,
						 float size, float lifetime, Nx::CTexture *p_texture, Nx::sSplatTrailInstanceDetails *p_trail_details )
{
	Mth::Matrix view_matrix, ortho_matrix, projection_matrix;

#	if DRAW_DEBUG_LINES
	Gfx::AddDebugLine( start, end, MAKE_RGB( 200, 200, 0 ), MAKE_RGB( 200, 200, 0 ), 1 );
#	endif // DRAW_DEBUG_LINES
	
	// The length of the start->end line defines the view depth of the frustum.
	Mth::Vector	splat_vector	= end - start;
	float		view_depth		= splat_vector.Length();
	splat_vector.Normalize();
	
	// Calculate the parallel projection matrix. Generally the projection vector will tend to point downwards, so we use a
	// random vector in the x-z plane to define the up vector for the projection. However if this splat is part of a trail,
	// use the previous point to generate the up vector.
	if( p_trail_details )
	{
		Mth::Vector up( start[X] - p_trail_details->m_last_pos[X], start[Y] - p_trail_details->m_last_pos[Y], start[Z] - p_trail_details->m_last_pos[Z], 0.0f );
		Dbg_MsgAssert((up[X] != 0.0f) || (up[Z] != 0.0f), ("Up vector has no length"));
		//Dbg_Message("Splat up vector (%.8f, %.8f, %.8f)", up[X], up[Y], up[Z]);

		// The height of the viewport is defined by the distance between the two points.
		float height = up.Length() * 0.5f;

		start	-= up * 0.5f;
		end		-= up * 0.5f;

		up.Normalize();

		Mth::CreateMatrixLookAt( view_matrix, start, end, up );
		Mth::CreateMatrixOrtho( ortho_matrix, size, height, 0.1f, view_depth );
	}
	else if( fabsf( splat_vector[Y] ) > 0.5f )
	{
		float angle = ((float)rand() * 2.0f * Mth::PI ) / (float)RAND_MAX;
		Mth::CreateMatrixLookAt( view_matrix, start, end, Mth::Vector( sinf( angle ), 0.0f, cosf( angle ), 0.0f ));
		Mth::CreateMatrixOrtho( ortho_matrix , size, size, 0.1f, view_depth );
	}
	else
	{
		Mth::CreateMatrixLookAt( view_matrix, start, end, Mth::Vector( 0.0f, 1.0f, 0.0f, 0.0f ));
		Mth::CreateMatrixOrtho( ortho_matrix , size, size, 0.1f, view_depth );
	}

	projection_matrix = view_matrix * ortho_matrix;

	// Find texture info
	Dbg_Assert(p_texture);
	CPs2Texture *p_ps2_texture = static_cast(p_texture);
	NxPs2::SSingleTexture *p_single_texture = p_ps2_texture->GetSingleTexture();
	Dbg_Assert(p_single_texture);

	// Pointer to the mesh we will be modifying. (Don't want to set the pointer up until we know for
	// sure that we will be adding some polys).
	sNGPSSplatInstanceDetails	*p_details		= NULL;
	sNGPSVert					*p_target_verts	= NULL;

#if DRAW_DEBUG_LINES
	Nx::CSector *p_sector;
	while(( p_sector = *pp_sectors ))
	{
		Nx::CPs2Geom *p_ngps_geom = static_cast( p_sector->GetGeom());

		if( p_ngps_geom )
		{
			Mth::Vector min = p_ngps_geom->GetBoundingBox().GetMin();
			Mth::Vector max = p_ngps_geom->GetBoundingBox().GetMax();

			Mth::Vector box[8];
			box[0] = box[1] = box[2] = box[3] = max;
			box[1][X] = min[X];
			box[2][Y] = min[Y];
			box[3][Z] = min[Z];
			box[5] = box[6] = box[7] = box[4] = min;;
			box[5][X] = max[X];
			box[6][Y] = max[Y];
			box[7][Z] = max[Z];

			for ( int i = 1; i < 4; i++ )
			{
				Gfx::AddDebugLine( box[0], box[i], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			}
			for ( int i = 5; i < 8; i++ )
			{
				Gfx::AddDebugLine( box[4], box[i], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			}
			Gfx::AddDebugLine( box[1], box[6], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			Gfx::AddDebugLine( box[1], box[7], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			Gfx::AddDebugLine( box[2], box[5], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			Gfx::AddDebugLine( box[2], box[7], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			Gfx::AddDebugLine( box[3], box[5], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			Gfx::AddDebugLine( box[3], box[6], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
		}

		++pp_sectors;
	}
#endif // DRAW_DEBUG_LINES

	// Make initial line bounding box.  (Possibly pass previous one in if this takes too long)
	Mth::CBBox line_bbox( start );
	line_bbox.AddPoint( end );

	//Dbg_Message("Using line (%f, %f, %f) - (%f, %f, %f)", start[X], start[Y], start[Z], end[X], end[Y], end[Z]);

	Nx::CCollStatic *p_collision;
	while(( p_collision = *(pp_collision++) ))
	{
		// Since the collision is static, we can assume the collision geometry data is in world space
		Nx::CCollObjTriData *p_coll_geom = p_collision->GetGeometry();
		
		// narrow down list of possible faces
		uint num_faces;
		FaceIndex *p_face_indexes;
		p_face_indexes = p_coll_geom->FindIntersectingFaces(line_bbox, num_faces);
		Dbg_Assert(p_face_indexes);

		Mth::Vector uvprojections[3];

		for (uint fidx = 0; fidx < num_faces; fidx++, p_face_indexes++)
		{
			// Make sure it is collidable and visible
			if (p_coll_geom->GetFaceFlags(*p_face_indexes) & (mFD_NON_COLLIDABLE | mFD_INVISIBLE))
			{
				continue;
			}

			Mth::Vector v0(p_coll_geom->GetRawVertexPos(p_coll_geom->GetFaceVertIndex(*p_face_indexes, 0)));
			Mth::Vector v1(p_coll_geom->GetRawVertexPos(p_coll_geom->GetFaceVertIndex(*p_face_indexes, 1)));
			Mth::Vector v2(p_coll_geom->GetRawVertexPos(p_coll_geom->GetFaceVertIndex(*p_face_indexes, 2)));
			v0[W] = v1[W] = v2[W] = 1.0f;		// Make sure they are points, not vectors

#if 1
			uvprojections[0] = v0 * projection_matrix;
			uvprojections[1] = v1 * projection_matrix;
			uvprojections[2] = v2 * projection_matrix;
#else
			// Test each component
			uvprojections[0] = v0 * view_matrix;
			uvprojections[1] = v1 * view_matrix;
			uvprojections[2] = v2 * view_matrix;

			Dbg_Message("Looking at possible splat poly vert 0 (%f, %f, %f, %f)", v0[X], v0[Y], v0[Z], v0[W]);
			Dbg_Message("Looking at possible splat poly vert 1 (%f, %f, %f, %f)", v1[X], v1[Y], v1[Z], v1[W]);
			Dbg_Message("Looking at possible splat poly vert 2 (%f, %f, %f, %f)", v2[X], v2[Y], v2[Z], v2[W]);

			Dbg_Message("Converted vert 0 to (%f, %f, %f)", uvprojections[0][X], uvprojections[0][Y], uvprojections[0][Z]);
			Dbg_Message("Converted vert 1 to (%f, %f, %f)", uvprojections[1][X], uvprojections[1][Y], uvprojections[1][Z]);
			Dbg_Message("Converted vert 2 to (%f, %f, %f)", uvprojections[2][X], uvprojections[2][Y], uvprojections[2][Z]);

			uvprojections[0] = uvprojections[0] * ortho_matrix;
			uvprojections[1] = uvprojections[1] * ortho_matrix;
			uvprojections[2] = uvprojections[2] * ortho_matrix;

			Dbg_Message("Converted vert 0 again to (%f, %f, %f)", uvprojections[0][X], uvprojections[0][Y], uvprojections[0][Z]);
			Dbg_Message("Converted vert 1 again to (%f, %f, %f)", uvprojections[1][X], uvprojections[1][Y], uvprojections[1][Z]);
			Dbg_Message("Converted vert 2 again to (%f, %f, %f)", uvprojections[2][X], uvprojections[2][Y], uvprojections[2][Z]);
#endif

			// Check that they are in the frustum
//			if(( uvprojections[0][X] < -1.0f ) && ( uvprojections[1][X] < -1.0f ) && ( uvprojections[2][X] < -1.0f ))
//				continue;
//			if(( uvprojections[0][Y] < -1.0f ) && ( uvprojections[1][Y] < -1.0f ) && ( uvprojections[2][Y] < -1.0f ))
//				continue;
//			if(( uvprojections[0][X] > 1.0f ) && ( uvprojections[1][X] > 1.0f ) && ( uvprojections[2][X] > 1.0f ))
//				continue;
//			if(( uvprojections[0][Y] > 1.0f ) && ( uvprojections[1][Y] > 1.0f ) && ( uvprojections[2][Y] > 1.0f ))
//				continue;
			if(( uvprojections[0][Z] < -1.0f ) && ( uvprojections[1][Z] < -1.0f ) && ( uvprojections[2][Z] < -1.0f ))
				continue;
			if(( uvprojections[0][Z] > 1.0f ) && ( uvprojections[1][Z] > 1.0f ) && ( uvprojections[2][Z] > 1.0f ))
				continue;

			// Okay, this tri lies within the projection frustum. Now check that it intersects the texture
			if( !tri_texture_intersect( uvprojections[0][X], uvprojections[0][Y],
										uvprojections[1][X], uvprojections[1][Y],
										uvprojections[2][X], uvprojections[2][Y] ))
			{
				continue;
			}

			//Dbg_Message("Found splat poly vert 0 (%f, %f, %f) uv (%f, %f)", v0[X], v0[Y], v0[Z], uvprojections[0][X], uvprojections[0][Y]);
			//Dbg_Message("Found splat poly vert 1 (%f, %f, %f) uv (%f, %f)", v1[X], v1[Y], v1[Z], uvprojections[1][X], uvprojections[1][Y]);
			//Dbg_Message("Found splat poly vert 2 (%f, %f, %f) uv (%f, %f)", v2[X], v2[Y], v2[Z], uvprojections[2][X], uvprojections[2][Y]);

			// Check if we have a line or point.  For some reason, the rendering code will crash
			// with this (I think it is the clipping).  Since we really don't want it anyways,
			// just skip them.
			int equal_axis = 0;
			if ((v0[X] == v1[X]) && (v1[X] == v2[X]))	equal_axis++;
			if ((v0[Y] == v1[Y]) && (v1[Y] == v2[Y]))	equal_axis++;
			if ((v0[Z] == v1[Z]) && (v1[Z] == v2[Z]))	equal_axis++;
			if (equal_axis > 1)
			{
				//Dbg_Message("Skipping line poly...");
				continue;
			}

			// Okay, this tri lies within the projection frustum. Get a pointer to the mesh used for rendering texture splats
			// with the given texture. (Note this will create a new instance to handle texture splats of this texture if one
			// does not already exist).
			if( p_target_verts == NULL )
			{
				p_details						= getDetailsForTextureSplat(p_single_texture);
				if ( p_details == NULL )
				{
					continue;
				}

				p_target_verts					= p_details->m_verts;
				Dbg_Assert( p_target_verts );
			}

			// If we have trails, scale up the mapping by one pixel in all directions.
			// (effectively 2 pixels in u and 2 pixels in v ).
			float up_one_u_pixel;
			float up_one_v_pixel;
			if( p_trail_details )
			{
				up_one_u_pixel = 1.0f - ( 1.0f / ( p_texture->GetWidth() / 2.0f ) );
				up_one_v_pixel = 1.0f - ( 1.0f / ( p_texture->GetHeight() / 2.0f ) );
			}
			else
			{
				up_one_u_pixel = 1.0f;
				up_one_v_pixel = 1.0f;
			}

			// Scan through the lifetimes, finding a 'dead' poly (lifetime == 0), or the oldest.
			uint32 idx						= p_details->GetOldestSplat();

			// Convert lifetime from seconds to milliseconds.
			p_details->m_lifetimes[idx]		= (int)( lifetime * 1000.0f );

			// Set up the corresponding vertices. First write the positions.
			uint32 index					= idx * 3;
			Dbg_Assert((index + 2) < (SPLAT_POLYS_PER_MESH * 3));
			p_target_verts[index + 0].pos	= v0;
			p_target_verts[index + 1].pos	= v1;
			p_target_verts[index + 2].pos	= v2;
			//p_target_verts[index + 0].pos[Y] += ( 12.0f * (float)rand() / RAND_MAX );
			//p_target_verts[index + 1].pos[Y] += ( 12.0f * (float)rand() / RAND_MAX );
			//p_target_verts[index + 2].pos[Y] += ( 12.0f * (float)rand() / RAND_MAX );

			// Then the uv's.
			p_target_verts[index + 0].u		= ( (uvprojections[0][X] * 0.5f) + 0.5f ) * up_one_u_pixel;
			p_target_verts[index + 0].v		= ( (uvprojections[0][Y] * 0.5f) + 0.5f ) * up_one_v_pixel;
			p_target_verts[index + 1].u		= ( (uvprojections[1][X] * 0.5f) + 0.5f ) * up_one_u_pixel;
			p_target_verts[index + 1].v		= ( (uvprojections[1][Y] * 0.5f) + 0.5f ) * up_one_v_pixel;
			p_target_verts[index + 2].u		= ( (uvprojections[2][X] * 0.5f) + 0.5f ) * up_one_u_pixel;
			p_target_verts[index + 2].v		= ( (uvprojections[2][Y] * 0.5f) + 0.5f ) * up_one_v_pixel;

			// Now the colors
			uint8 intensity0 = p_coll_geom->GetVertexIntensity(p_coll_geom->GetFaceVertIndex(*p_face_indexes, 0));
			uint8 intensity1 = p_coll_geom->GetVertexIntensity(p_coll_geom->GetFaceVertIndex(*p_face_indexes, 1));
			uint8 intensity2 = p_coll_geom->GetVertexIntensity(p_coll_geom->GetFaceVertIndex(*p_face_indexes, 2));
			p_target_verts[index + 0].col = Image::RGBA(intensity0, intensity0, intensity0, 0x80);
			p_target_verts[index + 1].col = Image::RGBA(intensity1, intensity1, intensity1, 0x80);
			p_target_verts[index + 2].col = Image::RGBA(intensity2, intensity2, intensity2, 0x80);

			Mth::Vector	*p_v0 = &( p_target_verts[index + 0].pos );
			Mth::Vector	*p_v1 = &( p_target_verts[index + 1].pos );
			Mth::Vector	*p_v2 = &( p_target_verts[index + 2].pos );
			Mth::Vector pv(	p_v1->GetX() - p_v0->GetX(), p_v1->GetY() - p_v0->GetY(), p_v1->GetZ() - p_v0->GetZ() );
			Mth::Vector qv(	p_v2->GetX() - p_v0->GetX(), p_v2->GetY() - p_v0->GetY(), p_v2->GetZ() - p_v0->GetZ() );
			Mth::Vector r(( pv[Y] * qv[Z] ) - ( qv[Y] * pv[Z] ), ( pv[Z] * qv[X] ) - ( qv[Z] * pv[X] ), ( pv[X] * qv[Y] ) - ( qv[X] * pv[Y] ));
			float area_squared = r.LengthSqr();

			// Set the shatter test to ensure that we don't subdivide too far. Note that each successive subdivision will quarter
			// the area of each triangle, which means the area *squared* of each triangle will become 1/16th of the previous value.
			float shatterArea = area_squared / 128.0f;

			triSubdivideStack.Clear();
			triSubdivideStack.SetBlockSize( sizeof(sNGPSVert) );
			triSubdivideStack.Push( &p_target_verts[index + 0] );
			triSubdivideStack.Push( &p_target_verts[index + 1] );
			triSubdivideStack.Push( &p_target_verts[index + 2] );

			// Allocate a block of memory into which the subdivision stack will write the results.
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
			sNGPSVert		*p_array		= new sNGPSVert[100];
			sNGPSVert		*p_array_start	= p_array;
			sNGPSVert		*p_array_loop	= p_array;
			//memset( p_array, 0, sizeof(sNGPSVert) * 100 );
			Mem::Manager::sHandle().PopContext();

			while( subdivide_tri_stack( shatterArea, &p_array, NULL ));

			// Ensure we haven't overrun the buffer.
			Dbg_Assert((uint32)p_array - (uint32)p_array_loop < ( sizeof(sNGPSVert) * 100 ));

			float oo_up_one_u_pixel = 1.0f / up_one_u_pixel;
			float oo_up_one_v_pixel = 1.0f / up_one_v_pixel;

//			int vert_idx = 0;
			bool no_polys = true;

			while( p_array_loop != p_array )
			{
				Dbg_Assert(((uint32) p_array_loop) < ((uint32) p_array));
//				Dbg_Message("Looking at vert %d", vert_idx);
				// Add this triangle, *if* it is valid.
				if( tri_texture_intersect(((p_array_loop[0].u * oo_up_one_u_pixel) - 0.5f) * 2.0f,
										  ((p_array_loop[0].v * oo_up_one_v_pixel) - 0.5f) * 2.0f,
										  ((p_array_loop[1].u * oo_up_one_u_pixel) - 0.5f) * 2.0f,
										  ((p_array_loop[1].v * oo_up_one_v_pixel) - 0.5f) * 2.0f,
										  ((p_array_loop[2].u * oo_up_one_u_pixel) - 0.5f) * 2.0f,
										  ((p_array_loop[2].v * oo_up_one_v_pixel) - 0.5f) * 2.0f ))
//				if( tri_texture_intersect((p_array_loop[0].u * 2.0f * oo_up_one_u_pixel) - 1.0f,
//										  (p_array_loop[0].v * 2.0f * oo_up_one_v_pixel) - 1.0f,
//										  (p_array_loop[1].u * 2.0f * oo_up_one_u_pixel) - 1.0f,
//										  (p_array_loop[1].v * 2.0f * oo_up_one_v_pixel) - 1.0f,
//										  (p_array_loop[2].u * 2.0f * oo_up_one_u_pixel) - 1.0f,
//										  (p_array_loop[2].v * 2.0f * oo_up_one_v_pixel) - 1.0f ))
				{
//					Dbg_Message("Accepted vert %d", vert_idx);
					// Convert lifetime from seconds to milliseconds.
					p_details->m_lifetimes[idx]		= (int)( lifetime * 1000.0f );

					p_target_verts[index + 0].pos	= p_array_loop[0].pos;
					p_target_verts[index + 1].pos	= p_array_loop[1].pos;
					p_target_verts[index + 2].pos	= p_array_loop[2].pos;

					p_target_verts[index + 0].u		= p_array_loop[0].u;
					p_target_verts[index + 0].v		= p_array_loop[0].v;
					p_target_verts[index + 1].u		= p_array_loop[1].u;
					p_target_verts[index + 1].v		= p_array_loop[1].v;
					p_target_verts[index + 2].u		= p_array_loop[2].u;
					p_target_verts[index + 2].v		= p_array_loop[2].v;

					p_target_verts[index + 0].col	= p_array_loop[0].col;
					p_target_verts[index + 1].col	= p_array_loop[1].col;
					p_target_verts[index + 2].col	= p_array_loop[2].col;

					idx								= p_details->GetOldestSplat();
					index							= idx * 3;
					Dbg_Assert((index + 2) < (SPLAT_POLYS_PER_MESH * 3));
					no_polys 						= false;
				}

				p_array_loop					+= 3;
//				vert_idx++;
			}

			delete [] p_array_start;

			// Check if all the new polys were rejected.  Don't know why this happens, but we want
			// to get rid of the original texture.
			if (no_polys)
			{
				plat_texture_splat_reset_poly( p_details, idx );
			}
		}
	}
	
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_render( void )
{
	sNGPSSplatInstanceDetails *p_ngps_details;

	Dbg_Assert( p_splat_details_table );

	p_splat_details_table->IterateStart();
	sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);

	bool set_zpush = (p_details != NULL);
	if (set_zpush)
	{
		NxPs2::CImmediateMode::sSetZPush(24.0f);
	}

	while( p_details )
	{

		// See if we have anything to draw first
		if (p_details->m_highest_active_splat >= 0)
		{
			p_ngps_details = static_cast( p_details );

			// Init drawing, since we know we have to draw
#if 1
			NxPs2::CImmediateMode::sStartPolyDraw(p_ngps_details->mp_texture, PackALPHA(0,1,0,1,0), ABS, true);
#else
			if ((Tmr::GetVblanks() % 600) < 300)
			{
				NxPs2::CImmediateMode::sStartPolyDraw(p_ngps_details->mp_texture, PackALPHA(0,0,0,0,0), ABS, true);
			} else {
				NxPs2::CImmediateMode::sStartPolyDraw(p_ngps_details->mp_texture, PackALPHA(0,0,0,0,0), ABS, false);
			}
#endif

			// Render the triangles
			sNGPSVert *p_vert_array = p_ngps_details->m_verts;
			for (int i = 0; i <= p_details->m_highest_active_splat; i++, p_vert_array += 3)
			{
				NxPs2::CImmediateMode::sDrawTriUV(p_vert_array[0].pos, p_vert_array[1].pos, p_vert_array[2].pos,
												  p_vert_array[0].u, p_vert_array[0].v,
												  p_vert_array[1].u, p_vert_array[1].v,
												  p_vert_array[2].u, p_vert_array[2].v,
												  *((uint32 *) &p_vert_array[0].col),
												  *((uint32 *) &p_vert_array[1].col),
												  *((uint32 *) &p_vert_array[2].col),
												  ABS);

#if DRAW_DEBUG_LINES
				Gfx::AddDebugLine( p_vert_array[0].pos, p_vert_array[1].pos, MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
				Gfx::AddDebugLine( p_vert_array[1].pos, p_vert_array[2].pos, MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
				Gfx::AddDebugLine( p_vert_array[2].pos, p_vert_array[0].pos, MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
#endif
			}
		}
		
		p_details = p_splat_details_table->IterateNext();
	}

	if (set_zpush)
	{
		NxPs2::CImmediateMode::sClearZPush();
	}

	NxPs2::dma::EndTag();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_shatter_initialize( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_shatter_cleanup( void )
{
	sNGPSShatterInstanceDetails *p_ngps_details;

	Dbg_Assert( p_shatter_details_table );
	
	p_shatter_details_table->IterateStart();
	sShatterInstanceDetails *p_details = p_shatter_details_table->IterateNext();
	while( p_details )
	{
		p_ngps_details = static_cast( p_details );
		
		p_details = p_shatter_details_table->IterateNext();

		p_shatter_details_table->FlushItem((uint32)p_ngps_details );
		delete p_ngps_details;
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const int MAX_GEOM_NODES = 10;

void find_geom_leaves( NxPs2::CGeomNode *p_node, NxPs2::CGeomNode **leaf_array, int & num_nodes)
{
	if (p_node->IsLeaf())
	{
		Dbg_Assert(num_nodes < MAX_GEOM_NODES);

		leaf_array[num_nodes++] = p_node;
		return;
	} else {
		NxPs2::CGeomNode *p_child;
		for (p_child = p_node->GetChild(); p_child; p_child = p_child->GetSibling())
		{
			find_geom_leaves(p_child, leaf_array, num_nodes);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int get_num_extra_shatter_tris(float shatterArea, const Mth::Vector & pos0, const Mth::Vector & pos1, const Mth::Vector & pos2)
{
	int num_extra_tris = 0;

	// Figure the area of this tri.
	Mth::Vector p( pos1[X] - pos0[X], pos1[Y] - pos0[Y], pos1[Z] - pos0[Z], 0.0f );
	Mth::Vector q( pos2[X] - pos0[X], pos2[Y] - pos0[Y], pos2[Z] - pos0[Z], 0.0f );
	Mth::Vector r(( p[Y] * q[Z] ) - ( q[Y] * p[Z] ), ( p[Z] * q[X] ) - ( q[Z] * p[X] ), ( p[X] * q[Y] ) - ( q[X] * p[Y] ), 0.0f);
	float area_squared = r.LengthSqr();

	if( area_squared > shatterArea )
	{
		//Dbg_Message("Area of triangle: %f", sqrtf(area_squared));

		// We will need to subdivide - each subdivision will result in an area one quarter the previous area
		// (and thusly the square of the area will be one sixteenth the previous area).
		num_extra_tris = 1;
		while( area_squared > shatterArea )
		{
			num_extra_tris *= 4;
			area_squared *= ( 1.0f / 16.0f );
		}

		// This original tri will not be added...
		// ...however, the subdivided versions will.
		num_extra_tris--;
		//Dbg_Message("Adding %d triangles", num_extra_tris);
	}

	return num_extra_tris;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void plat_shatter( CGeom *p_geom )
{
	CPs2Geom *p_ps2_geom = static_cast( p_geom );
	NxPs2::CGeomNode *p_root_node = p_ps2_geom->GetEngineObject();
	Dbg_Assert(p_root_node);

	// Get Time of Day color
	Image::RGBA geom_rgba = Nx::CEngine::sGetMainScene()->GetMajorityColor();

	// Put all the meshes in an array
	int num_meshes = 0;
	NxPs2::CGeomNode *mesh_array[MAX_GEOM_NODES];
	find_geom_leaves(p_root_node, mesh_array, num_meshes);

	// For each mesh in the geom...
	for( int m = 0; m < num_meshes; ++m )
	{
		NxPs2::CGeomNode *p_node = mesh_array[m];

		// Since these are extra passes 99% of the time, Environment Mapped CGeomNodes are rejected
		if (p_node->IsEnvMapped())
		{
			continue;
		}

		uint8 *p_dma = p_node->GetDma();

		int num_verts = NxPs2::dma::GetNumVertices(p_dma);
		if( num_verts >= 3 )
		{
			bool short_xyz = (NxPs2::dma::GetBitLengthXYZ(p_dma) == 16);
			Mth::Vector mesh_center = p_node->GetBoundingSphere();

			// Set the block size for this mesh.
			triSubdivideStack.SetBlockSize( sizeof(sNGPSVert) );

			// Get DMA arrays
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
			sint32 *p_vert_array = new sint32[4 * num_verts];
			sint32 *p_uv_array = new sint32[2 * num_verts];
			uint32 *p_rgb_array = new uint32[num_verts];
#if PRINT_SHATTER_MEMORY
			Dbg_Message("Mesh #%d; Verts %d; CGeomNode pointer %x checksum %x", m, num_verts, p_node, p_node->GetChecksum());
			Dbg_Message("Allocating %d bytes temporarily for DMA", 7 * num_verts * sizeof(uint32));
#endif
			Mem::Manager::sHandle().PopContext();

			// Copy DMA data
			NxPs2::dma::ExtractXYZs(p_dma, (uint8 *) p_vert_array);
			NxPs2::dma::ExtractSTs(p_dma, (uint8 *) p_uv_array);
			NxPs2::dma::ExtractRGBAs(p_dma, (uint8 *) p_rgb_array);

			Dbg_Assert(p_vert_array[(0 * 4) + 3] & 0x8000);
			Dbg_Assert(p_vert_array[(1 * 4) + 3] & 0x8000);

			sNGPSVert v0, v1, v2;

			if (short_xyz)
			{
				NxPs2::dma::ConvertXYZToFloat(v0.pos, &(p_vert_array[0 * 4]), mesh_center);
				NxPs2::dma::ConvertXYZToFloat(v1.pos, &(p_vert_array[1 * 4]), mesh_center);
			} else {
				NxPs2::dma::ConvertXYZToFloat(v0.pos, &(p_vert_array[0 * 4]));
				NxPs2::dma::ConvertXYZToFloat(v1.pos, &(p_vert_array[1 * 4]));
			}
			NxPs2::dma::ConvertSTToFloat(v0.u, v0.v, &(p_uv_array[0 * 2]));
			NxPs2::dma::ConvertSTToFloat(v1.u, v1.v, &(p_uv_array[1 * 2]));

			v0.col = *((Image::RGBA *) &(p_rgb_array[0]));
			v0.col.Blend128(geom_rgba);
			v1.col = *((Image::RGBA *) &(p_rgb_array[1]));
			v1.col.Blend128(geom_rgba);

			int orig_tris = 0;
			int valid_tris = 0;

			//Dbg_Message("Number of verts: %d", num_verts);

			for (int idx = 2; idx < num_verts; idx++)
			{
				if (short_xyz)
				{
					NxPs2::dma::ConvertXYZToFloat(v2.pos, &(p_vert_array[idx * 4]), mesh_center);
				} else {
					NxPs2::dma::ConvertXYZToFloat(v2.pos, &(p_vert_array[idx * 4]));
				}
				NxPs2::dma::ConvertSTToFloat(v2.u, v2.v, &(p_uv_array[idx * 2]));
				v2.col = *((Image::RGBA *) &(p_rgb_array[idx]));
				v2.col.Blend128(geom_rgba);

				// Dont make triangle if vert has ADC bit set
				if (!(p_vert_array[(idx * 4) + 3] & 0x8000))		// if adc bit not set
				{
					orig_tris++;
					valid_tris++;

					// Push this tri onto the stack.
					triSubdivideStack.Push( &v0 );
					triSubdivideStack.Push( &v1 );
					triSubdivideStack.Push( &v2 );

					//Dbg_Message("Triangle (%f, %f, %f, %f) - (%f, %f, %f, %f) - (%f, %f, %f, %f)", v0.pos[X], v0.pos[Y], v0.pos[Z], v0.pos[W],
					//			 v1.pos[X], v1.pos[Y], v1.pos[Z], v1.pos[W], v2.pos[X], v2.pos[Y], v2.pos[Z], v2.pos[W]);
					//Dbg_Message("UVs (%f, %f) - (%f, %f) - (%f, %f)", v0.u, v0.v, v1.u, v1.v, v2.u, v2.v);
					//Dbg_Message("Colors (%d, %d, %d, %d) - (%d, %d, %d, %d) - (%d, %d, %d, %d)", v0.col.r, v0.col.g, v0.col.b, v0.col.a,
					//			 v1.col.r, v1.col.g, v1.col.b, v1.col.a, v2.col.r, v2.col.g, v2.col.b, v2.col.a);

					// Add extra triangles that we need
					valid_tris += get_num_extra_shatter_tris(shatterAreaTest, v0.pos, v1.pos, v2.pos);
				}

				v0 = v1;
				v1 = v2;
			}

			// Free DMA buffers
			delete [] p_vert_array;
			delete [] p_uv_array;
			delete [] p_rgb_array;

			if( valid_tris == 0 )
			{
				continue;
			}

			float newShatterArea = shatterAreaTest;

			// Make sure there is memory available and that we don't take it all
			int free_mem = Mem::Available();
			int needed_mem = (sNGPSShatterInstanceDetails::sQueryMemoryNeeded(valid_tris) * 3) / 2; 
			if (free_mem < needed_mem)
			{
				// Calculate how much bigger we need to make the area
				Dbg_Message("Old needed memory %d free memory %d for %d tris", needed_mem, free_mem, valid_tris);
				float mem_ratio = ((float) needed_mem) / ((float) free_mem);
				int iratio = (int) (mem_ratio + 0.99f);		// Round up
				iratio = (iratio + 3) & ~3;					// Round up to the nearest divisible by 4
				newShatterArea = newShatterArea * (iratio * iratio);	// And square the ratio

				Dbg_Message("mem ratio %f iratio %d", mem_ratio, iratio);

				const Mth::Vector *pos0, *pos1, *pos2;
				valid_tris = orig_tris;
				for (int idx = 0; idx < orig_tris; idx++)
				{
					pos0 = &(((const sNGPSVert *) triSubdivideStack.Peek((idx * 3) + 0))->pos);
					pos1 = &(((const sNGPSVert *) triSubdivideStack.Peek((idx * 3) + 1))->pos);
					pos2 = &(((const sNGPSVert *) triSubdivideStack.Peek((idx * 3) + 2))->pos);
				   
					// Add extra triangles that we need
					valid_tris += get_num_extra_shatter_tris(newShatterArea, *pos0, *pos1, *pos2);
				}
				needed_mem = (sNGPSShatterInstanceDetails::sQueryMemoryNeeded(valid_tris) * 3) / 2; 
				Dbg_Message("New needed memory %d free memory %d for %d tris", needed_mem, free_mem, valid_tris);

				// See if this one is smaller, otherwise, give up
				if (free_mem < needed_mem)
				{
					triSubdivideStack.Clear();
					continue;
				}

				Dbg_Message("Increased shatter area from %f to %f", shatterAreaTest, newShatterArea);
			}

			//Dbg_Message("Making %d triangles", valid_tris);

			// Create a tracking structure for this mesh.
			sNGPSShatterInstanceDetails *p_details		= new sNGPSShatterInstanceDetails( valid_tris, p_node );
			sNGPSVert	 				*p_write_vertex	= p_details->mp_vert_array;
			uint32						details_index	= 0;

#if PRINT_SHATTER_MEMORY
			Dbg_Message("Allocated %d bytes for shatter of %d tris", sNGPSShatterInstanceDetails::sQueryMemoryNeeded(valid_tris), valid_tris);
#endif

			Mth::Vector					spread_center	= shatterVelocity * -shatterSpreadFactor;
			float						base_speed		= shatterVelocity.Length();

			spread_center[X] += mesh_center[X];
			spread_center[Y] += mesh_center[Y];
			spread_center[Z] += mesh_center[Z];
			spread_center[W] = 1.0f;
			
			// Add the tracking structure to the table.
			p_shatter_details_table->PutItem((uint32)p_details, p_details );
			
			// Process-subdivide the entire stack.
			sNGPSVert *p_copy_vertex = p_write_vertex;
			while( subdivide_tri_stack( newShatterArea, &p_write_vertex, p_node ));

			// Copy the (possibly subdivided) vertex data over.
			while( p_copy_vertex < p_write_vertex )
			{
				Mth::Vector *p_vert0 = &((p_copy_vertex + 0)->pos);
				Mth::Vector *p_vert1 = &((p_copy_vertex + 1)->pos);
				Mth::Vector *p_vert2 = &((p_copy_vertex + 2)->pos);
				
				// Calculate position as the midpoint of the three vertices per poly.
				p_details->mp_positions[details_index][X] = ( p_vert0->GetX() + p_vert1->GetX() + p_vert2->GetX() ) * ( 1.0f / 3.0f );
				p_details->mp_positions[details_index][Y] = ( p_vert0->GetY() + p_vert1->GetY() + p_vert2->GetY() ) * ( 1.0f / 3.0f );
				p_details->mp_positions[details_index][Z] = ( p_vert0->GetZ() + p_vert1->GetZ() + p_vert2->GetZ() ) * ( 1.0f / 3.0f );
				p_details->mp_positions[details_index][W] = 1.0f;

				// Calculate the vector  back from the bounding box of the object. Then use this to figure the 'spread' of the
				// shards by calculating the vector from this position to the center of each shard.
				float speed = base_speed + ( base_speed * (( shatterVelocityVariance * rand() ) / RAND_MAX ));
				p_details->mp_velocities[details_index] = ( p_details->mp_positions[details_index] - spread_center ).Normalize( speed );

				Mth::Vector axis( -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), 0.0f);
				axis.Normalize();
				p_details->mp_matrices[details_index].Ident();
				p_details->mp_matrices[details_index].Rotate( axis, 0.1f * ((float)rand() / RAND_MAX ));

				p_copy_vertex += 3;
				//p_copy_u += 3;
				//p_copy_v += 3;
						
				++details_index;
			}
		}
	}

}



/******************************************************************************
 *
 * 
 *****************************************************************************/
void plat_shatter_update( sShatterInstanceDetails *p_details, float framelength )
{
	sNGPSShatterInstanceDetails *p_ps2_details = static_cast( p_details );
	
	// Load up initial three vertex pointers.
	sNGPSVert *p_v0	= p_ps2_details->mp_vert_array;
	sNGPSVert *p_v1	= p_ps2_details->mp_vert_array + 1;
	sNGPSVert *p_v2	= p_ps2_details->mp_vert_array + 2;
	
	for( int i = 0; i < p_details->m_num_triangles; ++i )
	{
		// To move the shatter pieces:
		// 1) subtract position from each vertex
		// 2) rotate
		// 3) update position with velocity
		// 4) add new position to each vertex

		// The matrix holds 3 vectors at once.
		Mth::Matrix m;
		m[X].Set( p_v0->pos[X] - p_details->mp_positions[i][X], p_v0->pos[Y] - p_details->mp_positions[i][Y], p_v0->pos[Z] - p_details->mp_positions[i][Z] );
		m[Y].Set( p_v1->pos[X] - p_details->mp_positions[i][X], p_v1->pos[Y] - p_details->mp_positions[i][Y], p_v1->pos[Z] - p_details->mp_positions[i][Z] );
		m[Z].Set( p_v2->pos[X] - p_details->mp_positions[i][X], p_v2->pos[Y] - p_details->mp_positions[i][Y], p_v2->pos[Z] - p_details->mp_positions[i][Z] );
         
		m[X].Rotate( p_details->mp_matrices[i] );
		m[Y].Rotate( p_details->mp_matrices[i] );
		m[Z].Rotate( p_details->mp_matrices[i] );

		// Update the position and velocity of the shatter piece, dealing with bouncing if necessary.
		p_details->UpdateParameters( i, framelength );
      
		m[X] += p_details->mp_positions[i]; 
		m[Y] += p_details->mp_positions[i]; 
		m[Z] += p_details->mp_positions[i];

		p_v0->pos[X] = m[X][X]; p_v0->pos[Y] = m[X][Y]; p_v0->pos[Z] = m[X][Z];
		p_v1->pos[X] = m[Y][X]; p_v1->pos[Y] = m[Y][Y]; p_v1->pos[Z] = m[Y][Z];
		p_v2->pos[X] = m[Z][X]; p_v2->pos[Y] = m[Z][Y]; p_v2->pos[Z] = m[Z][Z];

		p_v0 = p_v0 + 3;
		p_v1 = p_v1 + 3;
		p_v2 = p_v2 + 3;
	}
}



/******************************************************************************
 *
 * 
 *****************************************************************************/
void plat_shatter_render( sShatterInstanceDetails *p_details )
{
	//Dbg_Message("In plat_shatter_render()");
	sNGPSShatterInstanceDetails *p_ps2_details = static_cast( p_details );

	Dbg_Assert( p_ps2_details );

	if (p_ps2_details->m_num_triangles == 0)
		return;

	bool textured = (p_ps2_details->mp_tex_regs != NULL);
	if (textured)
	{
		NxPs2::sGroup *p_group = p_ps2_details->mp_geom_node->GetGroup();
		NxPs2::dma::SetList(p_group);
		p_group->Used[NxPs2::render::Field] = true;
		p_ps2_details->mp_geom_node->GetTexture()->m_render_count++;
	}

	Mth::Vector sort_pos = p_ps2_details->mp_vert_array[0].pos;			// just use 1st vert of 1st tri
	sort_pos *= NxPs2::render::WorldToCamera;
	sort_pos[2] = -1000.0f;
	NxPs2::dma::BeginTag(NxPs2::dma::cnt, *(uint32 *)&sort_pos[2]);		// z-sort key
	NxPs2::vif::BASE(NxPs2::vu1::Loc);
	NxPs2::vif::OFFSET(0);
	uint vu1_loc = NxPs2::vu1::Loc;
	NxPs2::vu1::Loc = 0;						// must do this as a relative prim for a sortable list...

	if (textured)
	{
		NxPs2::CImmediateMode::sTextureGroupInit(REL);
		NxPs2::CImmediateMode::sStartPolyDraw(p_ps2_details->mp_tex_regs, p_ps2_details->m_num_tex_regs, REL, false);
	}
	else
	{
		NxPs2::CImmediateMode::sStartPolyDraw(NULL, PackALPHA(0,1,0,1,0), REL, false);
	}

	// Render the triangles
	sNGPSVert *p_vert_array = p_ps2_details->mp_vert_array;
	for (int i = 0; i < p_ps2_details->m_num_triangles; i++, p_vert_array += 3)
	{
		if (textured)
		{
			NxPs2::CImmediateMode::sDrawTriUV(p_vert_array[0].pos, p_vert_array[1].pos, p_vert_array[2].pos,
											  p_vert_array[0].u, p_vert_array[0].v,
											  p_vert_array[1].u, p_vert_array[1].v,
											  p_vert_array[2].u, p_vert_array[2].v,
											  *((uint32 *) &p_vert_array[0].col),
											  *((uint32 *) &p_vert_array[1].col),
											  *((uint32 *) &p_vert_array[2].col),
											  REL);
		} else {
			NxPs2::CImmediateMode::sDrawTri(p_vert_array[0].pos, p_vert_array[1].pos, p_vert_array[2].pos,
											*((uint32 *) &p_vert_array[0].col),
											*((uint32 *) &p_vert_array[1].col),
											*((uint32 *) &p_vert_array[2].col),
											REL);
		}

// this may break if put back in...
//#if DRAW_DEBUG_LINES
//		Gfx::AddDebugLine( p_vert_array[0].pos, p_vert_array[1].pos, MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
//		Gfx::AddDebugLine( p_vert_array[1].pos, p_vert_array[2].pos, MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
//		Gfx::AddDebugLine( p_vert_array[2].pos, p_vert_array[0].pos, MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
//#endif
	}

	NxPs2::dma::EndTag();
	((uint16 *)NxPs2::dma::pTag)[1] |= NxPs2::vu1::Loc & 0x3FF;	// must write some code for doing this automatically
	NxPs2::vu1::Loc += vu1_loc;

	if (p_ps2_details->mp_geom_node->GetGroup()->flags & GROUPFLAG_SORT)
	{
		NxPs2::dma::Tag(NxPs2::dma::cnt,0,0);
		NxPs2::vif::NOP();
		NxPs2::vif::NOP();
		NxPs2::dma::SetList(NULL);
	}

}

///////////////////////////////////////////////////////////////////
//
// FOG
//
///////////////////////////////////////////////////////////////////


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFog::s_plat_enable_fog(bool enable)
{
	NxPs2::render::EnableFog = enable;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFog::s_plat_set_fog_near_distance(float distance)
{
	NxPs2::render::FogNear = distance;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFog::s_plat_set_fog_exponent(float exponent)
{
	Dbg_Message("Stub: CFog::SetFogExponent()");
	//NxPs2::Fx::SetupFogPalette(*((unsigned int *) &m_rgba), exponent);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFog::s_plat_set_fog_rgba(Image::RGBA rgba)
{
	//NxPs2::Fx::SetupFogPalette(*((unsigned int *) &rgba), m_exponent);

	// Set alpha (and clamp between 0.0 and 1.0)
	float f_alpha = (float) rgba.a / 128.0f;
	f_alpha = Mth::Min(f_alpha, 1.0f);
	f_alpha = Mth::Max(f_alpha, 0.0f);
	NxPs2::render::FogAlpha = f_alpha;

	// Set color
	NxPs2::render::FogCol = *((uint32 *) &rgba) & 0xFFFFFF;	// mask out alpha
}

void		CFog::s_plat_set_fog_color( void )
{
	// Doesn't need to do anything on PS2.
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFog::s_plat_fog_update( void )
{
}
	


} // Nx


================================================
FILE: Code/Gfx/NGPS/p_nxnewparticle.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_NxNewParticle.cpp										**
**																			**
**	Created by:		3/25/03	-	SPG											**
**																			**
**	Description:	PS2 new parametric particle system						**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

void	CPs2NewParticle::plat_render( void )
{
	CParticleStream *p_stream;
	int i;

	//--------------------------------------------------------------------------------------------------------------------
	// process the streams
	if (m_params.m_EmitRate && (!m_emitting || (m_params.m_EmitRate != mp_newest_stream->m_rate)))
	{	
		if (m_num_streams < m_max_streams)
		{
			// add new stream to cyclic buffer
			m_num_streams++;
			mp_newest_stream++;
			if (mp_newest_stream == mp_stream + m_max_streams)
			{
				mp_newest_stream = mp_stream;
			}

			// initialise new stream
			mp_newest_stream->m_rate			= m_params.m_EmitRate;
			mp_newest_stream->m_interval		= 1.0f/m_params.m_EmitRate;
			mp_newest_stream->m_oldest_age		= 0.0f;
			mp_newest_stream->m_num_particles	= 0;
			mp_newest_stream->m_seed[0]			= rand();
			mp_newest_stream->m_seed[1]			= rand();
			mp_newest_stream->m_seed[2]			= rand();
			mp_newest_stream->m_seed[3]			= rand();
			m_emitting = true;
		}
		else
		{
			m_emitting = false;
		}
	}
	else
	{
		m_emitting = m_params.m_EmitRate;
	}

	if (!m_num_streams)
		return;

	// age all streams
	for (i=0, p_stream=mp_oldest_stream; im_oldest_age += 1.0f/60.0f;

		// step pointer within cyclic buffer
		p_stream++;
		if (p_stream == mp_stream + m_max_streams)
		{
			p_stream = mp_stream;
		}
	}

	// births into newest stream
	if (m_emitting)
	{
		// how many particles so far emitted?
		mp_newest_stream->m_num_particles = (int)(mp_newest_stream->m_oldest_age * mp_newest_stream->m_rate + 1.0f);
	}

	// deaths from oldest stream
	if (mp_oldest_stream->m_oldest_age > m_params.m_Lifetime)
	{
		// work out number dead
		int particles_dead = (int)((mp_oldest_stream->m_oldest_age - m_params.m_Lifetime) * mp_oldest_stream->m_rate + 1.0f);

		// remove dead particles
		mp_oldest_stream->m_num_particles -= particles_dead;

		// should we keep processing the oldest stream?
		if (mp_oldest_stream->m_num_particles>0 || (m_num_streams==1 && m_emitting))
		{
			// adjust age of oldest particle
			mp_oldest_stream->m_oldest_age -= (float)particles_dead * mp_oldest_stream->m_interval;

			// advance seed
			mp_oldest_stream->AdvanceSeed(particles_dead);
		}
		else
		{
			// remove oldest stream and wrap in cyclic buffer if necessary
			m_num_streams--;
			mp_oldest_stream++;
			if (mp_oldest_stream == mp_stream + m_max_streams)
			{
				mp_oldest_stream = mp_stream;
			}
			if (!m_num_streams)
				return;
		}
	}


	//--------------------------------------------------------------------------------------------------------------------
	// now render the streams

	if (mp_engine_texture)
	{
		// Mick:  If this is textured, we reset the TEX0, TEX1 regs as the texture might move
		// Since 2D textures are dynamically packed every frame
		*(uint64 *)&m_systemDmaData.m_GScontext[8] = mp_engine_texture->m_RegTEX0;
		*(uint64 *)&m_systemDmaData.m_GScontext[12] = mp_engine_texture->m_RegTEX1;
	}

	// FOG
	Mth::Vector x = 0.5f * (m_params.m_BoxPos[0] + m_params.m_BoxPos[2]);
	x[3] = 1.0f;
	x *= NxPs2::render::WorldToFrustum;
	float f;
	if ((x[3] > NxPs2::render::FogNear) && NxPs2::render::EnableFog)	// Garrett: We have to check for EnableFog here because the VU1 code isn't
	{
		f = 1.0 + NxPs2::render::EffectiveFogAlpha * (NxPs2::render::FogNear/x[3] - 1.0f);
	}
	else
	{
		f = 1.0f;
	}
	m_systemDmaData.m_GScontext[21] = ((uint32)(int)(f*255.99f)) << 24;

	// set the group
	NxPs2::dma::SetList(NxPs2::sGroup::pParticles);

	// the system and streams will be loaded to a double-buffered input area of VUMem1
	// ref the system data, and include an unpack for the system and streams:
	NxPs2::dma::Tag(NxPs2::dma::ref, (sizeof(CSystemDmaData)>>4), (uint)&m_systemDmaData);
	NxPs2::vif::STCYCL(1,1);
	NxPs2::vif::UNPACK(0, V4_32, (sizeof(CSystemDmaData)>>4)+m_num_streams*2, REL, SIGNED, 0);

	// construct a packet with data for each stream
	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	for (i=0,p_stream=mp_oldest_stream; im_num_particles<65536, ("particle limit reached (65536)"));

		// wrap at end of cyclic buffer
		if (p_stream == mp_stream + m_max_streams)
		{
			p_stream = mp_stream;
		}

		((float  *)NxPs2::dma::pLoc)[0] = p_stream->m_oldest_age;
		((float  *)NxPs2::dma::pLoc)[1] = p_stream->m_interval;
		((uint32 *)NxPs2::dma::pLoc)[2] = p_stream->m_num_particles;
		((uint32 *)NxPs2::dma::pLoc)[3] = (p_stream==mp_newest_stream) ? 0x8000 : 0;
		((uint32 *)NxPs2::dma::pLoc)[4] = p_stream->m_seed[0];
		((uint32 *)NxPs2::dma::pLoc)[5] = p_stream->m_seed[1];
		((uint32 *)NxPs2::dma::pLoc)[6] = p_stream->m_seed[2];
		((uint32 *)NxPs2::dma::pLoc)[7] = p_stream->m_seed[3];

		//printf("stream %d, num particles=%d, oldest age=%f\n", s, p_stream->m_num_particles, p_stream->m_oldest_age);

		// step dma pointer
		NxPs2::dma::pLoc += 8*4;

		// count particles
		NxPs2::render::sTotalNewParticles += p_stream->m_num_particles;
	}

	NxPs2::vif::MSCAL(10);	// sprites
	NxPs2::dma::EndTag();
	NxPs2::dma::SetList(NxPs2::sGroup::pEpilogue);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2NewParticle::update_position( void )
{
	// convert 3-point -> PVA format
	float t1 = m_params.m_Lifetime * m_params.m_MidpointPct * 0.01f;
	float t2 = m_params.m_Lifetime;
	Mth::Vector x0,x1,x2,u,a_;

	x0    = m_params.m_BoxPos[0];
	x0[3] = m_params.m_Radius[0];
	x1    = m_params.m_BoxPos[1];
	x1[3] = m_params.m_Radius[1];
	x2    = m_params.m_BoxPos[2];
	x2[3] = m_params.m_Radius[2];

	if (m_params.m_UseMidpoint)
	{
		u  =  ( t2*t2*(x1 - x0) - t1*t1*(x2 - x0) ) / ( t1*t2*(t2 - t1) );
		a_ =  ( t1*(x2 - x0) - t2*(x1 - x0) ) / ( t1*t2*(t2 - t1) );
	}
	else
	{
		u  = ( x2 - x0 ) / t2;
		a_.Set(0,0,0,0);
	}

	m_systemDmaData.m_p0 = x0 - 1.5f * m_systemDmaData.m_s0;
	m_systemDmaData.m_p1 = u  - 1.5f * m_systemDmaData.m_s1;
	m_systemDmaData.m_p2 = a_ - 1.5f * m_systemDmaData.m_s2;
	m_systemDmaData.m_p0[3] = x0[3] - 1.5f * m_systemDmaData.m_s0[3];
	m_systemDmaData.m_p1[3] = u[3]  - 1.5f * m_systemDmaData.m_s1[3];
	m_systemDmaData.m_p2[3] = a_[3] - 1.5f * m_systemDmaData.m_s2[3];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2NewParticle::plat_update( void )
{
	if (m_params.m_LocalCoord)
	{
		update_position();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2NewParticle::plat_build( void )
{

	// initialise streams
	m_max_streams = 5;
	m_num_streams = 0;
	mp_stream = new CParticleStream[m_max_streams]; 
	mp_newest_stream = mp_stream + m_max_streams - 1;
	mp_oldest_stream = mp_stream;
	m_emitting = false;


	// giftag for gs context
	m_systemDmaData.m_GScontext[0] = 0x00008005;
	m_systemDmaData.m_GScontext[1] = 0x10000000;
	m_systemDmaData.m_GScontext[2] = 0x0000000E;
	m_systemDmaData.m_GScontext[3] = 0x00000000;

	// ALPHA_1 register
	uint64 AlphaReg = 0;
	uint8 fix=64;
	switch (m_params.m_BlendMode)
	{
		case 0x54628ed7:		// Blend
			AlphaReg = PackALPHA(0,1,0,1,0);
			break;
		case 0x02e58c18:		// Add
			AlphaReg = PackALPHA(0,2,0,1,0);
			break;
		case 0xa7fd7d23:		// Sub
		case 0xdea7e576:		// Subtract
			AlphaReg = PackALPHA(2,0,0,1,0);
			break;
		case 0x40f44b8a:		// Modulate
			AlphaReg = PackALPHA(1,2,0,2,0);
			break;
		case 0x68e77f40:		// Brighten
			AlphaReg = PackALPHA(1,2,0,1,0);
			break;
		case 0x18b98905:		// FixBlend
			AlphaReg = PackALPHA(0,1,2,1,fix);
			break;
		case 0xa86285a1:		// FixAdd
			AlphaReg = PackALPHA(0,2,2,1,fix);
			break;
		case 0x0d7a749a:		// FixSub
		case 0x0eea99ff:		// FixSubtract
			AlphaReg = PackALPHA(2,0,2,1,fix);
			break;
		case 0x90b93703:		// FixModulate
			AlphaReg = PackALPHA(1,2,2,2,fix);
			break;
		case 0xb8aa03c9:		// FixBrighten
			AlphaReg = PackALPHA(1,2,2,1,fix);
			break;
		case 0x515e298e:		// Diffuse
		case 0x806fff30:		// None
			AlphaReg = PackALPHA(0,0,0,0,0);
			break;
		default:
			Dbg_MsgAssert(0,("Illegal blend mode specified. Please use (fix)blend/add/sub/modulate/brighten or diffuse/none."));
			break;
	}
	m_systemDmaData.m_GScontext[6] = 0x00000042;
	*(uint64 *)&m_systemDmaData.m_GScontext[4] = AlphaReg;

	// TEX0_1 and TEX1_1 registers
	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;
	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( m_params.m_Texture );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}
	//printf("m_Texture = %08X\n", m_params.m_Texture);
	//printf("p_texture = %08X\n", p_texture);
	//printf("p_ps2_texture = %08X\n", p_ps2_texture);
	//printf("mp_engine_texture = %08X\n", mp_engine_texture);

	m_systemDmaData.m_GScontext[10] = 0x00000006;

	m_systemDmaData.m_GScontext[14] = 0x00000014;

	if (mp_engine_texture)
	{
		//printf("\n\nTEX0_1 = %08X%08X\n\n\n",
		//					(uint32)(mp_engine_texture->m_RegTEX0>>32),
		//					(uint32)(mp_engine_texture->m_RegTEX0));

		// texture coords
		m_systemDmaData.m_u0 = 8;
		m_systemDmaData.m_v0 = (mp_engine_texture->GetHeight()<<4) - 8;
		m_systemDmaData.m_u1 = (mp_engine_texture->GetWidth() <<4) - 8;
		m_systemDmaData.m_v1 = 8;
	}

	// TEST_1
	m_systemDmaData.m_GScontext[18] = 0x00000047;
	m_systemDmaData.m_GScontext[16] = 0x0005000B | m_params.m_AlphaCutoff<<4;

	// FOG
	m_systemDmaData.m_GScontext[22] = 0x0000000A;

	// giftag for particles
	m_systemDmaData.m_tagy = 0x60BB4000;
	m_systemDmaData.m_tagz = 0x00535312;



	// have already set m_Lifetime, which is called m_life in newflat

	// x-component will be overwritten by vu1 code, so can store midtime there
	*(float *)&m_systemDmaData.m_tagx = m_params.m_UseMidcolor ?
										m_params.m_Lifetime * m_params.m_ColorMidpointPct * 0.01f :
										0.0f;

	// and now a load of redundant duplication of data, which should later be removed...

	// convert 3-point -> PVA format
	float t1 = m_params.m_Lifetime * m_params.m_MidpointPct * 0.01f;
	float t2 = m_params.m_Lifetime;
	Mth::Vector x0,x1,x2,u,a_;

	//printf("\n\n\n\n\n%g,%g,%g\n%g,%g,%g\n\n\n\n\n",
	//	   m_params.m_BoxDims[0][3], m_params.m_BoxDims[1][3], m_params.m_BoxDims[2][3],
	//	   m_params.m_BoxPos[0][3], m_params.m_BoxPos[1][3], m_params.m_BoxPos[2][3]);

	x0    = m_params.m_BoxDims[0];
	x0[3] = m_params.m_RadiusSpread[0];
	x1    = m_params.m_BoxDims[1];
	x1[3] = m_params.m_RadiusSpread[1];
	x2    = m_params.m_BoxDims[2];
	x2[3] = m_params.m_RadiusSpread[2];

	if (m_params.m_UseMidpoint)
	{
		u  = ( t2*t2*(x1 - x0) - t1*t1*(x2 - x0) ) / ( t1*t2*(t2 - t1) );
		a_ = ( t1*(x2 - x0) - t2*(x1 - x0) ) / ( t1*t2*(t2 - t1) );
	}
	else
	{
		u  = ( x2 - x0 ) / t2;
		a_.Set(0,0,0,0);
	}

	m_systemDmaData.m_s0 = x0;
	m_systemDmaData.m_s1 = u;
	m_systemDmaData.m_s2 = a_;

	update_position();


	// colour
	if (m_params.m_UseMidcolor)
	{
		float q0 = 100.0f / (m_params.m_Lifetime * m_params.m_ColorMidpointPct);
		float q1 = 100.0f / (m_params.m_Lifetime * (100.0f - m_params.m_ColorMidpointPct));

		m_systemDmaData.m_c0[0] = ((float)m_params.m_Color[1].r - (float)m_params.m_Color[0].r) * q0;
		m_systemDmaData.m_c0[1] = ((float)m_params.m_Color[1].g - (float)m_params.m_Color[0].g) * q0;
		m_systemDmaData.m_c0[2] = ((float)m_params.m_Color[1].b - (float)m_params.m_Color[0].b) * q0;
		m_systemDmaData.m_c0[3] = ((float)m_params.m_Color[1].a - (float)m_params.m_Color[0].a) * q0;

		m_systemDmaData.m_c1[0] = (float)m_params.m_Color[1].r;
		m_systemDmaData.m_c1[1] = (float)m_params.m_Color[1].g;
		m_systemDmaData.m_c1[2] = (float)m_params.m_Color[1].b;
		m_systemDmaData.m_c1[3] = (float)m_params.m_Color[1].a;

		m_systemDmaData.m_c2[0] = ((float)m_params.m_Color[2].r - (float)m_params.m_Color[1].r) * q1;
		m_systemDmaData.m_c2[1] = ((float)m_params.m_Color[2].g - (float)m_params.m_Color[1].g) * q1;
		m_systemDmaData.m_c2[2] = ((float)m_params.m_Color[2].b - (float)m_params.m_Color[1].b) * q1;
		m_systemDmaData.m_c2[3] = ((float)m_params.m_Color[2].a - (float)m_params.m_Color[1].a) * q1;

	}
	else // else suppress mid-colour
	{
		float q = 1.0f / m_params.m_Lifetime;

		m_systemDmaData.m_c1[0] = (float)m_params.m_Color[0].r;
		m_systemDmaData.m_c1[1] = (float)m_params.m_Color[0].g;
		m_systemDmaData.m_c1[2] = (float)m_params.m_Color[0].b;
		m_systemDmaData.m_c1[3] = (float)m_params.m_Color[0].a;

		m_systemDmaData.m_c2[0] = ((float)m_params.m_Color[2].r - (float)m_params.m_Color[0].r) * q;
		m_systemDmaData.m_c2[1] = ((float)m_params.m_Color[2].g - (float)m_params.m_Color[0].g) * q;
		m_systemDmaData.m_c2[2] = ((float)m_params.m_Color[2].b - (float)m_params.m_Color[0].b) * q;
		m_systemDmaData.m_c2[3] = ((float)m_params.m_Color[2].a - (float)m_params.m_Color[0].a) * q;
	}



	// rotation matrix
	//m_rotation = m_params.m_RotMatrix;
	m_rotation.Identity();

	#if 0
	// invert rotation and apply to spatial params
	// leaving this code a bit shoddy and slow until full transition to new-style params
	Mth::Matrix mat;
	mat=m_rotation;
	mat.Transpose();
	Mth::Vector vec;

	vec = m_systemDmaData.m_p0 + 1.5f * m_systemDmaData.m_s0;
	vec *= mat;
	m_systemDmaData.m_p0 = vec - 1.5f * m_systemDmaData.m_s0;

	vec = m_systemDmaData.m_p1 + 1.5f * m_systemDmaData.m_s1;
	vec *= mat;
	m_systemDmaData.m_p1 = vec - 1.5f * m_systemDmaData.m_s1;

	vec = m_systemDmaData.m_p2 + 1.5f * m_systemDmaData.m_s2;
	vec *= mat;
	m_systemDmaData.m_p2 = vec - 1.5f * m_systemDmaData.m_s2;
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CPs2NewParticle::plat_destroy( void )
{
	
	// Bit of a patch, but need to wait until particle system DMA packet has
	// been used before we can delete it, otherwise memory might get corrupted
	// in normal gameplay particle systems are rarely created
	// but in "FireFight" it shows up a lot more.
	// This really should be handled at a lower level
	Nx::CEngine::sFinishRendering();
	
	
	if( mp_stream )
	{
		delete [] mp_stream;
	}
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx






================================================
FILE: Code/Gfx/NGPS/p_nxnewparticle.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_NxNewParticle.h										**
**																			**
**	Created by:		3/24/03	-	SPG											**
**																			**
**	Description:	PS2 implementation of new parametric particle system	**
**																			**
*****************************************************************************/

#ifndef __GFX_NGPS_P_NXNEWPARTICLE_H__
#define __GFX_NGPS_P_NXNEWPARTICLE_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/



namespace Nx
{

                        
/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/


class CParticleStream
{
public:
	int						m_num_particles;
	float					m_rate;
	float					m_interval;
	float					m_oldest_age;
	uint32					m_seed[4];
	void					AdvanceSeed(int num_places);
};

class CSystemDmaData
{
public:
	uint32		m_GScontext[24];
	uint32		m_u0,  m_v0,  m_u1,  m_v1;
	Mth::Vector	m_p0;
	Mth::Vector	m_p1;
	Mth::Vector	m_p2;
	Mth::Vector	m_s0;
	Mth::Vector	m_s1;
	Mth::Vector	m_s2;
	Mth::Vector	m_c0;
	Mth::Vector	m_c1;
	Mth::Vector	m_c2;
	uint32		m_tagx,m_tagy,m_tagz,m_tagw;
} nAlign(128);


class CPs2NewParticle : public CNewParticle
{
private:
	void				update_position();

	bool				m_emitting;
	int					m_max_streams;
	int					m_num_streams;
	CParticleStream*	mp_stream;
	CParticleStream*	mp_newest_stream;
	CParticleStream*	mp_oldest_stream;
	Mth::Matrix 		m_rotation;
	CSystemDmaData		m_systemDmaData;
	NxPs2::SSingleTexture*	mp_engine_texture;


protected:
	void	plat_render( void );
	void	plat_update( void );
	void	plat_build( void );
	void	plat_destroy( void );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx

#endif	// __GFX_NGPS_P_NXNEWPARTICLE_H__




================================================
FILE: Code/Gfx/NGPS/p_nxnewparticlemgr.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_NxNewParticleMgr.cpp									**
**																			**
**	Created by:		3/25/03	-	SPG											**
**																			**
**	Description:	Ps2-specific parametric particle system manager			**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

CNewParticle*	CPs2NewParticleManager::plat_create_particle( void )
{
	return new CPs2NewParticle;
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx






================================================
FILE: Code/Gfx/NGPS/p_nxnewparticlemgr.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5 													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_nxnewparticlemgr.h									**
**																			**
**	Created by:		3/24/03	-	SPG											**
**																			**
**	Description:	PS2-specific new parametric particle system manager		**
**																			**
*****************************************************************************/

#ifndef __GFX_NGPS__P_NXNEWPARTICLEMGR_H__
#define __GFX_NGPS__P_NXNEWPARTICLEMGR_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Nx
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CPs2NewParticleManager : public CNewParticleManager
{
protected:
	virtual	CNewParticle*	plat_create_particle( void );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx

#endif	// __GFX_NGPS__P_NXNEWPARTICLEMGR_H__




================================================
FILE: Code/Gfx/NGPS/p_nxparticle.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 


#include "gfx/ngps/nx/vu1code.h"


#include "gfx/ngps/nx/mesh.h"
#include "gfx/ngps/p_nxparticleflat.h"
#include "gfx/ngps/p_nxparticleshaded.h"
#include "gfx/ngps/p_nxparticlesmooth.h"
#include "gfx/ngps/p_nxparticleglow.h"
#include "gfx/ngps/p_nxparticlestar.h"
#include "gfx/ngps/p_nxparticlesmoothstar.h"
#include "gfx/ngps/p_nxparticleline.h"
#include "gfx/ngps/p_nxparticleribbon.h"
#include "gfx/ngps/p_nxparticleribbontrail.h"
#include "gfx/ngps/p_nxparticlesmoothribbon.h"
#include "gfx/ngps/p_nxparticleglowribbontrail.h"
#include "gfx/ngps/p_nxparticlenewflat.h"


namespace Nx
{


CParticle* plat_create_particle( uint32 checksum, uint32 type_checksum, int max_particles, int max_streams, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	// Types to add:
	// 1. x Flat color quad
	// 2. x Gouraud quad
	// 3. x Quad with center point & outer color
	// 4. x n sided glow (center color + outer color)
	// 5. x n sided 2 layer glow ( center color + mid color + outer color)
	// 6. x n spiked star (center color + mid color + spike color)
	// 7. Lines (current color + previous color)
	// 8. Ribbons - volumetric lines made from quads (current color + previous color)

	switch ( type_checksum )
	{
		case 0xf4d8d486:		// Shaded
			{
				CPs2ParticleShaded* p_particle = new CPs2ParticleShaded( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x8addac1f:		// Smooth
			{
				CPs2ParticleSmooth* p_particle = new CPs2ParticleSmooth( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x15834eea:		// Glow
			{
				CPs2ParticleGlow* p_particle = new CPs2ParticleGlow( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x3624a5eb:		// Star
			{
				CPs2ParticleStar* p_particle = new CPs2ParticleStar( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x97cb7a9:		// SmoothStar
			{
				CPs2ParticleSmoothStar* p_particle = new CPs2ParticleSmoothStar( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x2eeb4b09:		// Line
			{
				CPs2ParticleLine* p_particle = new CPs2ParticleLine( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0xee6fc5b:		// Ribbon
			{
				CPs2ParticleRibbon* p_particle = new CPs2ParticleRibbon( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0xc4d5a4cb:		// RibbonTrail
			{
				CPs2ParticleRibbonTrail* p_particle = new CPs2ParticleRibbonTrail( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x3f109fcc:		// SmoothRibbon
			{
				CPs2ParticleSmoothRibbon* p_particle = new CPs2ParticleSmoothRibbon( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0x7ec7252d:		// GlowRibbonTrail
			{
				CPs2ParticleGlowRibbonTrail* p_particle = new CPs2ParticleGlowRibbonTrail( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
		case 0xdedfc057:		// NewFlat
			{
				CPs2ParticleNewFlat* p_particle = new CPs2ParticleNewFlat( checksum, max_streams, texture_checksum, blendmode_checksum, fix );
				return static_cast( p_particle );
			}
		case 0xaab555bb:		// Flat
		default:
			{
				CPs2ParticleFlat* p_particle = new CPs2ParticleFlat( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
				return static_cast( p_particle );
			}
			break;
	}
}

} // Nx

				






================================================
FILE: Code/Gfx/NGPS/p_nxparticle.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLE_H__
#define	__GFX_P_NX_PARTICLE_H__
    
#include "gfx/nxparticle.h"
                   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	

#endif 






================================================
FILE: Code/Gfx/NGPS/p_nxparticleflat.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 

#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleflat.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleFlat::CPs2ParticleFlat()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleFlat::CPs2ParticleFlat( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	m_start_color.r = 128;
	m_start_color.g = 128;
	m_start_color.b = 128;
	m_start_color.a = 128;
	m_mid_color.r = 128;
	m_mid_color.g = 128;
	m_mid_color.b = 128;
	m_mid_color.a = 128;
	m_end_color.r = 128;
	m_end_color.g = 128;
	m_end_color.b = 128;
	m_end_color.a = 128;

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleFlat::~CPs2ParticleFlat()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleFlat::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleFlat::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleFlat::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleFlat::plat_get_num_particle_colors( void ) { return 1; }
int CPs2ParticleFlat::plat_get_num_vertex_lists( void ) { return 1; }
void CPs2ParticleFlat::plat_set_sr( int entry, uint8 value ) { m_start_color.r = value; }
void CPs2ParticleFlat::plat_set_sg( int entry, uint8 value ) { m_start_color.g = value; }
void CPs2ParticleFlat::plat_set_sb( int entry, uint8 value ) { m_start_color.b = value; }
void CPs2ParticleFlat::plat_set_sa( int entry, uint8 value ) { m_start_color.a = value >> 1; }
void CPs2ParticleFlat::plat_set_mr( int entry, uint8 value ) { m_mid_color.r = value; }
void CPs2ParticleFlat::plat_set_mg( int entry, uint8 value ) { m_mid_color.g = value; }
void CPs2ParticleFlat::plat_set_mb( int entry, uint8 value ) { m_mid_color.b = value; }
void CPs2ParticleFlat::plat_set_ma( int entry, uint8 value ) { m_mid_color.a = value >> 1; }
void CPs2ParticleFlat::plat_set_er( int entry, uint8 value ) { m_end_color.r = value; }
void CPs2ParticleFlat::plat_set_eg( int entry, uint8 value ) { m_end_color.g = value; }
void CPs2ParticleFlat::plat_set_eb( int entry, uint8 value ) { m_end_color.b = value; }
void CPs2ParticleFlat::plat_set_ea( int entry, uint8 value ) { m_end_color.a = value >> 1; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
#if 0

void CPs2ParticleFlat::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	//Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraOrientation;
	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;

	// Concatenate p_matrix with the emmission angle to create the direction.
	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

	screen_right.Normalize();
	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );
		
		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
	
		ss_right	= screen_right * w;
		ss_up		= screen_up * h;
	
		tmp[0]		= pos - ss_right + ss_up;
		tmp[1]		= pos + ss_right + ss_up;		
		tmp[2]		= pos + ss_right - ss_up;		
		tmp[3]		= pos - ss_right - ss_up;		
	
		Image::RGBA color;
		Image::RGBA *p_col0;
		Image::RGBA *p_col1;
		
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = &m_start_color;
				p_col1 = &m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = &m_mid_color;
				p_col1 = &m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = &m_start_color;
			p_col1 = &m_end_color;
		}

		Image::RGBA start = *p_col0;
		Image::RGBA end = *p_col1;

		color.r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
		color.g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
		color.b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
		color.a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
	
		NxPs2::CImmediateMode::sDrawQuadTexture( mp_engine_texture, tmp[0], tmp[1], tmp[2], tmp[3],
												 *((uint32 *) &color),
												 *((uint32 *) &color),
												 *((uint32 *) &color),
												 *((uint32 *) &color));
	}

	NxPs2::dma::EndTag();
}

#else

void CPs2ParticleFlat::plat_render( void )
{
	if (m_num_particles <= 0)
		return;

	Dbg_MsgAssert(mp_engine_texture, ("no support for non-textured sprites yet..."));

	int i,j=0;
	CParticleEntry *p_particle;
	float *p_v;
	float *p_xyzr=NULL;
	uint32 *p_rgba=NULL;

	// add a dma packet
	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);

	// VU context
	NxPs2::vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));
	NxPs2::vu1::StoreVec(*(NxPs2::Vec *)&NxPs2::render::InverseIntViewportScale);
	NxPs2::vu1::StoreVec(*(NxPs2::Vec *)&NxPs2::render::InverseIntViewportOffset);
	NxPs2::vu1::StoreMat(*(NxPs2::Mat *)&NxPs2::render::WorldToIntViewport);	// VF12-15
	NxPs2::vu1::EndPrim(0);
	NxPs2::vu1::BeginPrim(ABS, VU1_ADDR(L_VF30));
	NxPs2::vif::StoreV4_32F(640.0f, 480.0f, 0.0f, 0.0f);						// VF30
	NxPs2::vif::StoreV4_32F(NxPs2::render::IntViewportScale[0]/NxPs2::render::Tx,	// VF31
							NxPs2::render::IntViewportScale[1]/NxPs2::render::Ty,
							0.0f, 0.0f);
	NxPs2::vu1::EndPrim(0);

	// GS context
	NxPs2::gs::BeginPrim(ABS, 0, 0);
	NxPs2::gs::Reg1(NxPs2::gs::ALPHA_1,	m_blend);
	NxPs2::gs::Reg1(NxPs2::gs::TEX0_1,	mp_engine_texture->m_RegTEX0);
	NxPs2::gs::Reg1(NxPs2::gs::TEX1_1,	mp_engine_texture->m_RegTEX1);
	NxPs2::gs::Reg1(NxPs2::gs::ST,		PackST(0x3F800000,0x3F800000));
	NxPs2::gs::Reg1(NxPs2::gs::RGBAQ,	PackRGBAQ(0,0,0,0,0x3F800000));
	NxPs2::gs::EndPrim(0);

	for (i=0, p_particle=mp_particle_array, p_v=mp_vertices; i 80)
				j = 80;
				
			NxPs2::BeginModelPrimImmediate(NxPs2::gs::XYZ2		|
										   NxPs2::gs::ST<<4		|
										   NxPs2::gs::RGBAQ<<8	|
										   NxPs2::gs::XYZ2<<12,
										   4, SPRITE|ABE|TME, 1, VU1_ADDR(SpriteCull));

			// create an unpack for the colours
			NxPs2::vif::BeginUNPACK(0, V4_8, ABS, UNSIGNED, 3);
			p_rgba = (uint32 *)NxPs2::dma::pLoc;
			NxPs2::dma::pLoc += j * 4;
			NxPs2::vif::EndUNPACK();

			// and one for the positions (& sizes)
			NxPs2::vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 4);
			p_xyzr = (float *)NxPs2::dma::pLoc;
			NxPs2::dma::pLoc += j * 16;
			NxPs2::vif::EndUNPACK();

			NxPs2::EndModelPrimImmediate(1);

			NxPs2::vif::MSCAL(VU1_ADDR(Parser));
		}

		float t = p_particle->m_time / p_particle->m_life;

		// just use the width for now...
		float width = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * t );
		
		// store position
		*p_xyzr++ = p_v[0]+m_pos[0];
		*p_xyzr++ = p_v[1]+m_pos[1];
		*p_xyzr++ = p_v[2]+m_pos[2];
		*p_xyzr++ = width;

		Image::RGBA start, end, colour;

		if ( m_mid_time >= 0.0f )
		{
			if ( t < m_mid_time )
			{
				start = m_start_color;
				end   = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				t /= m_mid_time;
			}
			else
			{
				start = m_mid_color;
				end   = m_end_color;
				// Adjust interpolation for this half of the color blend.
				t = ( t - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			start = m_start_color;
			end   = m_end_color;
		}

		// compute interpolated colour
		colour.r = start.r + (uint8) (int) ( ((float)( end.r - start.r )) * t );
		colour.g = start.g + (uint8) (int) ( ((float)( end.g - start.g )) * t );
		colour.b = start.b + (uint8) (int) ( ((float)( end.b - start.b )) * t );
		colour.a = start.a + (uint8) (int) ( ((float)( end.a - start.a )) * t );

		// store colour
		*p_rgba++ = *(uint32 *)&colour;
	}

	// restore transform
	NxPs2::vu1::BeginPrim(ABS, VU1_ADDR(L_VF12));		// was L_VF16
	NxPs2::vu1::StoreMat(*(NxPs2::Mat *)&NxPs2::render::AdjustedWorldToViewport);		// VF16-19
	NxPs2::vu1::EndPrim(1);
	NxPs2::vif::MSCAL(VU1_ADDR(Parser));

	// end the dma tag
	NxPs2::dma::EndTag();

}

#endif

} // Nx



================================================
FILE: Code/Gfx/NGPS/p_nxparticleflat.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLEFLAT_H__
#define	__GFX_P_NX_PARTICLEFLAT_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleFlat : public CParticle
{
public:
							CPs2ParticleFlat();
							CPs2ParticleFlat( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleFlat();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
//	NxPs2::sParticleSystem*	mp_engine_particle;

	Image::RGBA				m_start_color;				// Start color for each corner.
	Image::RGBA				m_mid_color;				// Mid color for each corner.
	Image::RGBA				m_end_color;				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 







================================================
FILE: Code/Gfx/NGPS/p_nxparticleglow.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 

#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleGlow.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleGlow::CPs2ParticleGlow()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleGlow::CPs2ParticleGlow( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	for ( int lp = 0; lp < 3; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;

	m_num_segments = num_segments;
	m_split = split;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleGlow::~CPs2ParticleGlow()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleGlow::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleGlow::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleGlow::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleGlow::plat_get_num_particle_colors( void ) { return 3; }
int CPs2ParticleGlow::plat_get_num_vertex_lists( void ) { return 1; }
void CPs2ParticleGlow::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CPs2ParticleGlow::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CPs2ParticleGlow::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CPs2ParticleGlow::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CPs2ParticleGlow::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CPs2ParticleGlow::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CPs2ParticleGlow::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CPs2ParticleGlow::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CPs2ParticleGlow::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CPs2ParticleGlow::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CPs2ParticleGlow::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CPs2ParticleGlow::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleGlow::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	//Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraOrientation;
	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;

	// Concatenate p_matrix with the emmission angle to create the direction.
	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

	screen_right.Normalize();
	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend, ABS );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
	
		ss_right	= screen_right * w;
		ss_up		= screen_up * h;
	
		Image::RGBA color[3];
		Image::RGBA *p_col0;
		Image::RGBA *p_col1;

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 3; c++ )
		{
			Image::RGBA start = *p_col0++;
			Image::RGBA end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		tmp[0]  = pos;
		tmp[0] += ss_right * sinf( Mth::DegToRad( 0.0f ) ) * m_split;
		tmp[0] += ss_up    * cosf( Mth::DegToRad( 0.0f ) ) * m_split;

		tmp[2]  = pos;
		tmp[2] += ss_right * sinf( Mth::DegToRad( 0.0f ) );
		tmp[2] += ss_up    * cosf( Mth::DegToRad( 0.0f ) );

		for ( int lp2 = 0; lp2 < m_num_segments; lp2++ )
		{
			tmp[1]  = pos;
			tmp[1] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;
			tmp[1] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;

			tmp[3]  = pos;
			tmp[3] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) );
			tmp[3] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) );

			NxPs2::CImmediateMode::sDrawGlowSegment( pos, tmp[0], tmp[1], tmp[2], tmp[3],
													 *((uint32 *) &color[0]),
													 *((uint32 *) &color[1]),
													 *((uint32 *) &color[2]));
			tmp[0] = tmp[1];
			tmp[2] = tmp[3];
		}
	}

	NxPs2::dma::EndTag();
}

} // Nx





================================================
FILE: Code/Gfx/NGPS/p_nxparticleglow.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLEGlow_H__
#define	__GFX_P_NX_PARTICLEGlow_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleGlow : public CParticle
{
public:
							CPs2ParticleGlow();
							CPs2ParticleGlow( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleGlow();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;

//	NxPs2::sParticleSystem*	mp_engine_particle;
	int						m_num_segments;
	float					m_split;
	Image::RGBA				m_start_color[3];			// Start color for each corner.
	Image::RGBA				m_mid_color[3];				// Mid color for each corner.
	Image::RGBA				m_end_color[3];				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/NGPS/p_nxparticleglowribbontrail.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 


#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleGlowRibbonTrail.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleGlowRibbonTrail::CPs2ParticleGlowRibbonTrail()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleGlowRibbonTrail::CPs2ParticleGlowRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	m_start_color = new Image::RGBA[m_num_vertex_buffers+3];
	m_mid_color = new Image::RGBA[m_num_vertex_buffers+3];
	m_end_color = new Image::RGBA[m_num_vertex_buffers+3];
	for ( int lp = 0; lp < (m_num_vertex_buffers+3); lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;

	m_num_segments = num_segments;
	m_split = split;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleGlowRibbonTrail::~CPs2ParticleGlowRibbonTrail()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
	delete [] m_start_color;
	delete [] m_mid_color;
	delete [] m_end_color;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleGlowRibbonTrail::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleGlowRibbonTrail::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleGlowRibbonTrail::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleGlowRibbonTrail::plat_get_num_particle_colors( void ) { return m_num_vertex_buffers + 3; }
int CPs2ParticleGlowRibbonTrail::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CPs2ParticleGlowRibbonTrail::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CPs2ParticleGlowRibbonTrail::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CPs2ParticleGlowRibbonTrail::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CPs2ParticleGlowRibbonTrail::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CPs2ParticleGlowRibbonTrail::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CPs2ParticleGlowRibbonTrail::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CPs2ParticleGlowRibbonTrail::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CPs2ParticleGlowRibbonTrail::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CPs2ParticleGlowRibbonTrail::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CPs2ParticleGlowRibbonTrail::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CPs2ParticleGlowRibbonTrail::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CPs2ParticleGlowRibbonTrail::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleGlowRibbonTrail::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the ribbontrail.
	
//	// Used to figure the right and up vectors for creating screen-aligned particle quads.
//	//Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraOrientation;
//	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;
//
//	// Concatenate p_matrix with the emmission angle to create the direction.
//	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );
//
//	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
//	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
//	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
//	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );
//
//	screen_right.Normalize();
//	screen_up.Normalize();
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;

	// Concatenate p_matrix with the emmission angle to create the direction.
	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

	screen_right.Normalize();
	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend, ABS );

	Image::RGBA color[3];
	Image::RGBA *p_col0;
	Image::RGBA *p_col1;

	for ( lp = 0, p_particle = mp_particle_array; lp < m_num_particles; lp++, p_particle++ )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		Mth::Vector	pos[2];
		p_v = &mp_vertices[0][lp*3];
		pos[0].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		p_v = &mp_vertices[1][lp*3];
		pos[1].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );

		Mth::Vector	part_vec = pos[1] - pos[0];
		Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
		perp_vec.Normalize();

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );
		
		Mth::Vector tmp[4];

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = &m_start_color[3];
				p_col1 = &m_mid_color[3];
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = &m_mid_color[3];
				p_col1 = &m_end_color[3];
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = &m_start_color[3];
			p_col1 = &m_end_color[3];
		}

		Image::RGBA start = *p_col0++;
		Image::RGBA end = *p_col1++;

		color[0].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
		color[0].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
		color[0].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
		color[0].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));

		tmp[0]		= pos[0] + ( perp_vec * w * m_split );
		tmp[1]		= pos[0] - ( perp_vec * w * m_split );

		for ( int c = 1; c < m_num_vertex_buffers; c++ )
		{
			start = *p_col0++;
			end = *p_col1++;

			color[1].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[1].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[1].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[1].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));

			if ( c > 1 )
			{
				p_v = &mp_vertices[c][lp*3];
				pos[1].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				part_vec = pos[1] - pos[0];
				perp_vec = Mth::CrossProduct( part_vec, at );
				perp_vec.Normalize();
			}

			tmp[2]		= pos[1] - ( perp_vec * w * m_split );
			tmp[3]		= pos[1] + ( perp_vec * w * m_split );

			NxPs2::CImmediateMode::sDrawQuadTexture( mp_engine_texture, tmp[0], tmp[1], tmp[2], tmp[3], *((uint32 *) &color[0]), *((uint32 *) &color[0]), *((uint32 *) &color[1]), *((uint32 *) &color[1]) );

			color[0] = color[1];
			pos[0] = pos[1];
			tmp[0] = tmp[3];
			tmp[1] = tmp[2];
		}

		// Draw the glow.

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		p_v = &mp_vertices[0][lp*3];
		pos[0].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
	
		ss_right	= screen_right * w;
		ss_up		= screen_up * h;
	
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 3; c++ )
		{
			Image::RGBA start = *p_col0++;
			Image::RGBA end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		tmp[0]  = pos[0];
		tmp[0] += ss_right * sinf( Mth::DegToRad( 0.0f ) ) * m_split;
		tmp[0] += ss_up    * cosf( Mth::DegToRad( 0.0f ) ) * m_split;

		tmp[2]  = pos[0];
		tmp[2] += ss_right * sinf( Mth::DegToRad( 0.0f ) );
		tmp[2] += ss_up    * cosf( Mth::DegToRad( 0.0f ) );

		for ( int lp2 = 0; lp2 < m_num_segments; lp2++ )
		{
			tmp[1]  = pos[0];
			tmp[1] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;
			tmp[1] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;

			tmp[3]  = pos[0];
			tmp[3] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) );
			tmp[3] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) );

			NxPs2::CImmediateMode::sDrawGlowSegment( pos[0], tmp[0], tmp[1], tmp[2], tmp[3],
													 *((uint32 *) &color[0]),
													 *((uint32 *) &color[1]),
													 *((uint32 *) &color[2]));
			tmp[0] = tmp[1];
			tmp[2] = tmp[3];
		}
	}

	NxPs2::dma::EndTag();
}

} // Nx




================================================
FILE: Code/Gfx/NGPS/p_nxparticleglowribbontrail.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLEGlowRibbonTrail_H__
#define	__GFX_P_NX_PARTICLEGlowRibbonTrail_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleGlowRibbonTrail : public CParticle
{
public:
							CPs2ParticleGlowRibbonTrail();
							CPs2ParticleGlowRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleGlowRibbonTrail();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
//	NxPs2::sParticleSystem*	mp_engine_particle;

	int						m_num_segments;
	float					m_split;
	Image::RGBA*			m_start_color;				// Start color for each corner.
	Image::RGBA*			m_mid_color;				// Mid color for each corner.
	Image::RGBA*			m_end_color;				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGPS/p_nxparticleline.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 


#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleLine.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleLine::CPs2ParticleLine()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleLine::CPs2ParticleLine( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffers.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	m_start_color = new Image::RGBA[m_num_vertex_buffers];
	m_mid_color = new Image::RGBA[m_num_vertex_buffers];
	m_end_color = new Image::RGBA[m_num_vertex_buffers];
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleLine::~CPs2ParticleLine()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleLine::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleLine::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleLine::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleLine::plat_get_num_particle_colors( void ) { return 2; }
int CPs2ParticleLine::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CPs2ParticleLine::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CPs2ParticleLine::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CPs2ParticleLine::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CPs2ParticleLine::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CPs2ParticleLine::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CPs2ParticleLine::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CPs2ParticleLine::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CPs2ParticleLine::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CPs2ParticleLine::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CPs2ParticleLine::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CPs2ParticleLine::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CPs2ParticleLine::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleLine::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the particles.
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v0;
	float			*p_v1;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend, ABS );

	Image::RGBA color[2];
	Image::RGBA *p_col0;
	Image::RGBA *p_col1;

	for ( lp = 0, p_particle = mp_particle_array, p_v0 = mp_vertices[0], p_v1 = mp_vertices[(m_num_vertex_buffers-1)]; lp < m_num_particles; lp++, p_particle++, p_v0 += 3, p_v1 += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 2; c++ )
		{
			Image::RGBA start = *p_col0++;
			Image::RGBA end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos0( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
		Mth::Vector	pos1( p_v1[0] + m_pos[X], p_v1[1] + m_pos[Y], p_v1[2] + m_pos[Z] );
	
		NxPs2::CImmediateMode::sDrawLine( pos0, pos1, *((uint32 *) &color[0]), *((uint32 *) &color[1]) );
	}

	NxPs2::dma::EndTag();
}

} // Nx




================================================
FILE: Code/Gfx/NGPS/p_nxparticleline.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLELine_H__
#define	__GFX_P_NX_PARTICLELine_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleLine : public CParticle
{
public:
							CPs2ParticleLine();
							CPs2ParticleLine( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleLine();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
//	NxPs2::sParticleSystem*	mp_engine_particle;

	Image::RGBA*			m_start_color;				// Start color for each corner.
	Image::RGBA*			m_mid_color;				// Mid color for each corner.
	Image::RGBA*			m_end_color;				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGPS/p_nxparticlenewflat.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 

#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"
#include "gfx/ngps/nx/group.h"

#include "gfx/ngps/p_nxparticlenewflat.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleNewFlat::CPs2ParticleNewFlat()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleNewFlat::CPs2ParticleNewFlat( uint32 checksum, int max_streams, uint32 texture_checksum, uint32 blendmode_checksum, int fix )
{
	m_checksum = checksum;
	m_max_streams = max_streams;
	m_num_streams = 0;

	// Get the texture.
	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Allocate space for span information & streams. 
	m_num_spans = 1;		// Currently defaulting to 1 span.
	mp_span = new CParticleSpan[m_num_spans];
	mp_stream = new CParticleStream[m_max_streams]; 

	mp_newest_stream = mp_stream + m_max_streams - 1;
	mp_oldest_stream = mp_stream;
	m_life = 0.5f * (m_life_min + m_life_max);
	m_emitting = false;


	// set up system dma data, apart from new coefficients, which are calculated in plat_build_path()
	m_systemDmaData.m_GScontext[0] = 0x00008004;
	m_systemDmaData.m_GScontext[1] = 0x10000000;
	m_systemDmaData.m_GScontext[2] = 0x0000000E;
	m_systemDmaData.m_GScontext[3] = 0x00000000;

	*(uint64 *)&m_systemDmaData.m_GScontext[4] = m_blend;
	m_systemDmaData.m_GScontext[6] = 0x00000042;

	if (mp_engine_texture)
	{
		m_systemDmaData.m_u0 = 8;
		m_systemDmaData.m_v0 = (mp_engine_texture->GetHeight()<<4) - 8;
		m_systemDmaData.m_u1 = (mp_engine_texture->GetWidth() <<4) - 8;
		m_systemDmaData.m_v1 = 8;
		//m_systemDmaData.m_GScontext[16] = ((mp_engine_texture->GetHeight()<<4) - 8) << 16 | 8;

		*(uint64 *)&m_systemDmaData.m_GScontext[8] = mp_engine_texture->m_RegTEX0;
		*(uint64 *)&m_systemDmaData.m_GScontext[12] = mp_engine_texture->m_RegTEX1;
	}
	m_systemDmaData.m_GScontext[10] = 0x00000006;
	m_systemDmaData.m_GScontext[14] = 0x00000014;

	m_systemDmaData.m_GScontext[16] = 0x0005000B /* | m_alphaCutoff<<4 */;	//101 0000 xxxx xxxx 1011
	m_systemDmaData.m_GScontext[18] = 0x00000047;

	m_systemDmaData.m_tagy = 0x60AB4000;
	m_systemDmaData.m_tagz = 0x00434312;


	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleNewFlat::~CPs2ParticleNewFlat()
{
	delete [] mp_span;
	delete [] mp_stream;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleNewFlat::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleNewFlat::plat_set_position( int entry, int list, float x, float y, float z )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleNewFlat::plat_add_position( int entry, int list, float x, float y, float z )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleNewFlat::plat_get_num_particle_colors( void ) { return 1; };
int CPs2ParticleNewFlat::plat_get_num_vertex_lists( void ) { return 0; };
void CPs2ParticleNewFlat::plat_set_sr( int entry, uint8 value ) { m_start_color.r = value; }
void CPs2ParticleNewFlat::plat_set_sg( int entry, uint8 value ) { m_start_color.g = value; }
void CPs2ParticleNewFlat::plat_set_sb( int entry, uint8 value ) { m_start_color.b = value; }
void CPs2ParticleNewFlat::plat_set_sa( int entry, uint8 value ) { m_start_color.a = value >> 1; }
void CPs2ParticleNewFlat::plat_set_mr( int entry, uint8 value ) { m_mid_color.r = value; }
void CPs2ParticleNewFlat::plat_set_mg( int entry, uint8 value ) { m_mid_color.g = value; }
void CPs2ParticleNewFlat::plat_set_mb( int entry, uint8 value ) { m_mid_color.b = value; }
void CPs2ParticleNewFlat::plat_set_ma( int entry, uint8 value ) { m_mid_color.a = value >> 1; }
void CPs2ParticleNewFlat::plat_set_er( int entry, uint8 value ) { m_end_color.r = value; }
void CPs2ParticleNewFlat::plat_set_eg( int entry, uint8 value ) { m_end_color.g = value; }
void CPs2ParticleNewFlat::plat_set_eb( int entry, uint8 value ) { m_end_color.b = value; }
void CPs2ParticleNewFlat::plat_set_ea( int entry, uint8 value ) { m_end_color.a = value >> 1; }


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	

void CPs2ParticleNewFlat::plat_render( void )
{
	CParticleStream *p_stream;
	int i;


	//--------------------------------------------------------------------------------------------------------------------
	// process the streams
	if (m_emit_rate && (!m_emitting || (m_emit_rate != mp_newest_stream->m_rate)))
	{	
		if (m_num_streams < m_max_streams)
		{
			// add new stream to cyclic buffer
			m_num_streams++;
			mp_newest_stream++;
			if (mp_newest_stream == mp_stream + m_max_streams)
			{
				mp_newest_stream = mp_stream;
			}

			// initialise new stream
			mp_newest_stream->m_rate			= m_emit_rate;
			mp_newest_stream->m_interval		= 1.0f/m_emit_rate;
			mp_newest_stream->m_oldest_age		= 0.0f;
			mp_newest_stream->m_num_particles	= 0;
			mp_newest_stream->m_seed[0]			= rand();
			mp_newest_stream->m_seed[1]			= rand();
			mp_newest_stream->m_seed[2]			= rand();
			mp_newest_stream->m_seed[3]			= rand();
			m_emitting = true;
		}
		else
		{
			m_emitting = false;
		}
	}
	else
	{
		m_emitting = m_emit_rate;
	}

	if (!m_num_streams)
		return;

	// age all streams
	for (i=0, p_stream=mp_oldest_stream; im_oldest_age += 1.0f/60.0f;

		// step pointer within cyclic buffer
		p_stream++;
		if (p_stream == mp_stream + m_max_streams)
		{
			p_stream = mp_stream;
		}
	}

	// births into newest stream
	if (m_emitting)
	{
		// how many particles so far emitted?
		mp_newest_stream->m_num_particles = (int)(mp_newest_stream->m_oldest_age * mp_newest_stream->m_rate + 1.0f);
	}

	// deaths from oldest stream
	if (mp_oldest_stream->m_oldest_age > m_life)
	{
		// work out number dead
		int particles_dead = (int)((mp_oldest_stream->m_oldest_age - m_life) * mp_oldest_stream->m_rate + 1.0f);

		// remove dead particles
		mp_oldest_stream->m_num_particles -= particles_dead;

		// should we keep processing the oldest stream?
		if (mp_oldest_stream->m_num_particles>0 || (m_num_streams==1 && m_emitting))
		{
			// adjust age of oldest particle
			mp_oldest_stream->m_oldest_age -= (float)particles_dead * mp_oldest_stream->m_interval;

			// advance seed
			mp_oldest_stream->AdvanceSeed(particles_dead);
		}
		else
		{
			// remove oldest stream and wrap in cyclic buffer if necessary
			m_num_streams--;
			mp_oldest_stream++;
			if (mp_oldest_stream == mp_stream + m_max_streams)
			{
				mp_oldest_stream = mp_stream;
			}
			if (!m_num_streams)
				return;
		}
	}


	//--------------------------------------------------------------------------------------------------------------------
	// now render the streams

	if (mp_engine_texture)
	{
		// Mick:  If this is textured, we reset the TEX0, TEX1 regs as the texture might move
		// Since 2D textures are dynamically packed every frame
		*(uint64 *)&m_systemDmaData.m_GScontext[8] = mp_engine_texture->m_RegTEX0;
		*(uint64 *)&m_systemDmaData.m_GScontext[12] = mp_engine_texture->m_RegTEX1;
	}

	// matrix is system rotation times view transform
	m_systemDmaData.m_matrix = m_rotation * NxPs2::render::WorldToIntViewport;

	// in the per-frame setup dma we must have:
	// dma tag
	// base
	// offset

	// set the group
	NxPs2::dma::SetList(NxPs2::sGroup::pParticles);

	// the system and streams will be loaded to a double-buffered input area of VUMem1
	// ref the system data, and include an unpack for the system and streams:
	NxPs2::dma::Tag(NxPs2::dma::ref, (sizeof(CSystemDmaDataFlat)>>4), (uint)&m_systemDmaData);
	NxPs2::vif::STCYCL(1,1);
	NxPs2::vif::UNPACK(0, V4_32, (sizeof(CSystemDmaDataFlat)>>4)+m_num_streams*2, REL, SIGNED, 0);

	// construct a packet with data for each stream
	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	for (i=0,p_stream=mp_oldest_stream; im_oldest_age;
		((float  *)NxPs2::dma::pLoc)[1] = p_stream->m_interval;
		((uint32 *)NxPs2::dma::pLoc)[2] = p_stream->m_num_particles;
		((uint32 *)NxPs2::dma::pLoc)[3] = (p_stream==mp_newest_stream) ? 0x8000 : 0;
		((uint32 *)NxPs2::dma::pLoc)[4] = p_stream->m_seed[0];
		((uint32 *)NxPs2::dma::pLoc)[5] = p_stream->m_seed[1];
		((uint32 *)NxPs2::dma::pLoc)[6] = p_stream->m_seed[2];
		((uint32 *)NxPs2::dma::pLoc)[7] = p_stream->m_seed[3];

		//printf("stream %d, num particles=%d, oldest age=%f\n", s, p_stream->m_num_particles, p_stream->m_oldest_age);

		// step dma pointer
		NxPs2::dma::pLoc += 8*4;

		// count particles
		NxPs2::render::sTotalNewParticles += p_stream->m_num_particles;
	}

	NxPs2::vif::MSCAL(10);	// sprites
	NxPs2::dma::EndTag();
	NxPs2::dma::SetList(NxPs2::sGroup::pEpilogue);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleNewFlat::plat_build_path( void )
{
	float midTime;

	// set m_life
	m_life = 0.5f * (m_life_min + m_life_max);

	// set clamped local midtime variable
	if (m_mid_time <= 0.0f)
	{
		midTime = 0.0f;
	}
	else if (m_mid_time >= 100.0f)
	{
		midTime = m_life;
	}
	else
	{
		midTime = m_mid_time * 0.01f * m_life;
	}

	// x-component will be overwritten by vu1 code, so can store midtime there
	*(float *)&m_systemDmaData.m_tagx = midTime;


	// constant part
	m_systemDmaData.m_s0x = m_emit_w;
	m_systemDmaData.m_s0y = 0.0f;
	m_systemDmaData.m_s0z = m_emit_h;
	m_systemDmaData.m_s0w = 0.0f;

	m_systemDmaData.m_c0x = m_pos[0] - 1.5f * m_systemDmaData.m_s0x;
	m_systemDmaData.m_c0y = m_pos[1] - 1.5f * m_systemDmaData.m_s0y;
	m_systemDmaData.m_c0z = m_pos[2] - 1.5f * m_systemDmaData.m_s0z;
	m_systemDmaData.m_c0w = m_sw     - 1.5f * m_systemDmaData.m_s0w;


	// linear part
	Mth::Vector vel(m_tx,m_ty,m_tz,0.0f);
	float speed = 60.0f * 0.5 * (m_speed_min + m_speed_max);
	vel.Normalize();
	vel *= speed;
	m_systemDmaData.m_s1x = speed * Mth::PI/2.0f * m_emit_angle_spread;
	m_systemDmaData.m_s1y = 60.0f * (m_speed_max - m_speed_min);
	m_systemDmaData.m_s1z = speed * Mth::PI/2.0f * m_emit_angle_spread;
	m_systemDmaData.m_s1w = 0.0f;

	m_systemDmaData.m_c1x = vel[0]               - 1.5f * m_systemDmaData.m_s1x;
	m_systemDmaData.m_c1y = vel[1]               - 1.5f * m_systemDmaData.m_s1y;
	m_systemDmaData.m_c1z = vel[2]               - 1.5f * m_systemDmaData.m_s1z;
	m_systemDmaData.m_c1w = (m_ew-m_sw) / m_life - 1.5f * m_systemDmaData.m_s1w;


	// quadratic part
	m_systemDmaData.m_s2x = 0.0f;
	m_systemDmaData.m_s2y = 0.0f;
	m_systemDmaData.m_s2z = 0.0f;
	m_systemDmaData.m_s2w = 0.0f;

	m_systemDmaData.m_c2x = m_fx*0.5f*(60.0f*60.0f) - 1.5f * m_systemDmaData.m_s2x;
	m_systemDmaData.m_c2y = m_fy*0.5f*(60.0f*60.0f) - 1.5f * m_systemDmaData.m_s2y;
	m_systemDmaData.m_c2z = m_fz*0.5f*(60.0f*60.0f) - 1.5f * m_systemDmaData.m_s2z;
	m_systemDmaData.m_c2w = 0.0f                    - 1.5f * m_systemDmaData.m_s2w;


	// cubic part
	//m_systemDmaData.m_s3x = 0.0f;
	//m_systemDmaData.m_s3y = 0.0f;
	//m_systemDmaData.m_s3z = 0.0f;
	//m_systemDmaData.m_s3w = 0.0f;

	//m_systemDmaData.m_c3x = 0.0f - 1.5f * m_systemDmaData.m_s3x;
	//m_systemDmaData.m_c3y = 0.0f - 1.5f * m_systemDmaData.m_s3y;
	//m_systemDmaData.m_c3z = 0.0f - 1.5f * m_systemDmaData.m_s3z;
	//m_systemDmaData.m_c3w = 0.0f - 1.5f * m_systemDmaData.m_s3w;
	

	// colour
	if (m_mid_time >= 0.0f)		// if using mid-colour
	{
		float q0 = 1.0f / midTime;
		float q1 = 1.0f / (m_life - midTime);

		m_systemDmaData.m_c0r = ((float)m_mid_color.r - (float)m_start_color.r) * q0;
		m_systemDmaData.m_c0g = ((float)m_mid_color.g - (float)m_start_color.g) * q0;
		m_systemDmaData.m_c0b = ((float)m_mid_color.b - (float)m_start_color.b) * q0;
		m_systemDmaData.m_c0a = ((float)m_mid_color.a - (float)m_start_color.a) * q0;

		m_systemDmaData.m_c1r = (float)m_mid_color.r;
		m_systemDmaData.m_c1g = (float)m_mid_color.g;
		m_systemDmaData.m_c1b = (float)m_mid_color.b;
		m_systemDmaData.m_c1a = (float)m_mid_color.a;

		m_systemDmaData.m_c2r = ((float)m_end_color.r - (float)m_mid_color.r) * q1;
		m_systemDmaData.m_c2g = ((float)m_end_color.g - (float)m_mid_color.g) * q1;
		m_systemDmaData.m_c2b = ((float)m_end_color.b - (float)m_mid_color.b) * q1;
		m_systemDmaData.m_c2a = ((float)m_end_color.a - (float)m_mid_color.a) * q1;
	}
	else						// else suppress mid-colour
	{
		float q = 1.0f / m_life;

		m_systemDmaData.m_c1r = (float)m_start_color.r;
		m_systemDmaData.m_c1g = (float)m_start_color.g;
		m_systemDmaData.m_c1b = (float)m_start_color.b;
		m_systemDmaData.m_c1a = (float)m_start_color.a;

		m_systemDmaData.m_c2r = ((float)m_end_color.r - (float)m_start_color.r) * q;
		m_systemDmaData.m_c2g = ((float)m_end_color.g - (float)m_start_color.g) * q;
		m_systemDmaData.m_c2b = ((float)m_end_color.b - (float)m_start_color.b) * q;
		m_systemDmaData.m_c2a = ((float)m_end_color.a - (float)m_start_color.a) * q;
	}


	// rotation matrix, hardwired to the identity for now
	m_rotation.Identity();

	// temp test
	//m_rotation[0][0] =  0.8;
	//m_rotation[0][1] =  0.6;
	//m_rotation[1][0] = -0.6;
	//m_rotation[1][1] =  0.8;

	// invert rotation and apply to spatial params
	// leaving this code a bit shoddy and slow until full transition to new-style params
	Mth::Matrix mat;
	mat=m_rotation;
	mat.Transpose();
	Mth::Vector vec;

	vec[0] = m_systemDmaData.m_c0x + 1.5f * m_systemDmaData.m_s0x;
	vec[1] = m_systemDmaData.m_c0y + 1.5f * m_systemDmaData.m_s0y;
	vec[2] = m_systemDmaData.m_c0z + 1.5f * m_systemDmaData.m_s0z;
	vec[3] = 0;
	vec *= mat;
	m_systemDmaData.m_c0x = vec[0] - 1.5f * m_systemDmaData.m_s0x;
	m_systemDmaData.m_c0y = vec[1] - 1.5f * m_systemDmaData.m_s0y;
	m_systemDmaData.m_c0z = vec[2] - 1.5f * m_systemDmaData.m_s0z;

	vec[0] = m_systemDmaData.m_c1x + 1.5f * m_systemDmaData.m_s1x;
	vec[1] = m_systemDmaData.m_c1y + 1.5f * m_systemDmaData.m_s1y;
	vec[2] = m_systemDmaData.m_c1z + 1.5f * m_systemDmaData.m_s1z;
	vec[3] = 0;
	vec *= mat;
	m_systemDmaData.m_c1x = vec[0] - 1.5f * m_systemDmaData.m_s1x;
	m_systemDmaData.m_c1y = vec[1] - 1.5f * m_systemDmaData.m_s1y;
	m_systemDmaData.m_c1z = vec[2] - 1.5f * m_systemDmaData.m_s1z;
	
	vec[0] = m_systemDmaData.m_c2x + 1.5f * m_systemDmaData.m_s2x;
	vec[1] = m_systemDmaData.m_c2y + 1.5f * m_systemDmaData.m_s2y;
	vec[2] = m_systemDmaData.m_c2z + 1.5f * m_systemDmaData.m_s2z;
	vec[3] = 0;
	vec *= mat;
	m_systemDmaData.m_c2x = vec[0] - 1.5f * m_systemDmaData.m_s2x;
	m_systemDmaData.m_c2y = vec[1] - 1.5f * m_systemDmaData.m_s2y;
	m_systemDmaData.m_c2z = vec[2] - 1.5f * m_systemDmaData.m_s2z;

}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CParticleStream::AdvanceSeed(int num_places)
{
	for (int i=0; i>4 ^ m_seed[3]>>22) & 1);
		m_seed[1] ^= m_seed[0];
		m_seed[2] ^= m_seed[1];
		m_seed[3] ^= m_seed[2];
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleNewFlat::GetNumParticles()
{
	int i, total=0;
	CParticleStream *p_stream;

	for (i=0, p_stream=mp_oldest_stream; im_num_particles;

		// step pointer within cyclic buffer
		p_stream++;
		if (p_stream == mp_stream + m_max_streams)
		{
			p_stream = mp_stream;
		}
	}

	return total;
}


} // Nx



================================================
FILE: Code/Gfx/NGPS/p_nxparticlenewflat.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLENEWFLAT_H__
#define	__GFX_P_NX_PARTICLENEWFLAT_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

class CParticleSpan
{
public:
	float					m_sx,m_sy,m_sz;			// Start, mid & end points of span path.
	float					m_mx,m_my,m_mz;			// This should really be the coefficients you need to
	float					m_ex,m_ey,m_ez;			// describe the path, not the raw position data.
	unsigned char			m_sr,m_sg,m_sb,m_sa;	// Start color
	unsigned char			m_er,m_eg,m_eb,m_ea;	// End color
};

class CParticleStream
{
public:
	int						m_num_particles;
	float					m_rate;
	float					m_interval;
	float					m_oldest_age;
	uint32					m_seed[4];
	void					AdvanceSeed(int num_places);
};

class CSystemDmaDataFlat
{
public:
	uint32					m_GScontext[20];
	uint32					m_u0,  m_v0,  m_u1,  m_v1;
	float					m_c0x, m_c0y, m_c0z, m_c0w;
	float					m_c1x, m_c1y, m_c1z, m_c1w;
	float					m_c2x, m_c2y, m_c2z, m_c2w;
	float					m_s0x, m_s0y, m_s0z, m_s0w;
	float					m_s1x, m_s1y, m_s1z, m_s1w;
	float					m_s2x, m_s2y, m_s2z, m_s2w;
	float					m_c0r, m_c0g, m_c0b, m_c0a;
	float					m_c1r, m_c1g, m_c1b, m_c1a;
	float					m_c2r, m_c2g, m_c2b, m_c2a;
	uint32					m_tagx,m_tagy,m_tagz,m_tagw;
	Mth::Matrix				m_matrix;
} nAlign(128);

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleNewFlat : public CParticle
{
public:
							CPs2ParticleNewFlat();
							CPs2ParticleNewFlat( uint32 checksum, int max_streams, uint32 texture_checksum, uint32 blendmode_checksum, int fix );
	virtual 				~CPs2ParticleNewFlat();

	int						GetNumParticles();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
	void					plat_build_path( void );
		
	Image::RGBA				m_start_color;				// Start color for each corner.
	Image::RGBA				m_mid_color;				// Mid color for each corner.
	Image::RGBA				m_end_color;				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;

	uint64					m_blend;

	int						m_max_streams;			// Maximum number of particle streams.
	int						m_num_streams;			// Number of active streams.
	int						m_num_spans;			// Number of span segments.

	CParticleSpan*			mp_span;
	CParticleStream*		mp_stream;
	CParticleStream*		mp_newest_stream;
	CParticleStream*		mp_oldest_stream;
	float					m_life;
	bool					m_emitting;


	Mth::Matrix				m_rotation;

	// dma data
	CSystemDmaDataFlat			m_systemDmaData;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 




================================================
FILE: Code/Gfx/NGPS/p_nxparticleribbon.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 


#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleRibbon.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleRibbon::CPs2ParticleRibbon()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleRibbon::CPs2ParticleRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	for ( int lp = 0; lp < 2; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleRibbon::~CPs2ParticleRibbon()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;	
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleRibbon::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleRibbon::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleRibbon::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleRibbon::plat_get_num_particle_colors( void ) { return 2; }
int CPs2ParticleRibbon::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CPs2ParticleRibbon::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CPs2ParticleRibbon::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CPs2ParticleRibbon::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CPs2ParticleRibbon::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CPs2ParticleRibbon::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CPs2ParticleRibbon::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CPs2ParticleRibbon::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CPs2ParticleRibbon::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CPs2ParticleRibbon::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CPs2ParticleRibbon::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CPs2ParticleRibbon::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CPs2ParticleRibbon::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleRibbon::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the particles.
	
//	// Used to figure the right and up vectors for creating screen-aligned particle quads.
//	//Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraOrientation;
//	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;
//
//	// Concatenate p_matrix with the emmission angle to create the direction.
//	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );
//
//	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
//	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
//	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
//	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );
//
//	screen_right.Normalize();
//	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v0;
	float			*p_v1;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend, ABS );

	Image::RGBA color[2];
	Image::RGBA *p_col0;
	Image::RGBA *p_col1;

	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;
	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );

	for ( lp = 0, p_particle = mp_particle_array, p_v0 = mp_vertices[0], p_v1 = mp_vertices[(m_num_vertex_buffers-1)]; lp < m_num_particles; lp++, p_particle++, p_v0 += 3, p_v1 += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		Mth::Vector	pos0( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
		Mth::Vector	pos1( p_v1[0] + m_pos[X], p_v1[1] + m_pos[Y], p_v1[2] + m_pos[Z] );

		Mth::Vector	part_vec = pos1 - pos0;
		Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
		perp_vec.Normalize();

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
//		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );
		
//		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
//		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
//	
		tmp[0]		= pos0 + ( perp_vec * w );
		tmp[1]		= pos0 - ( perp_vec * w );
		tmp[2]		= pos1 - ( perp_vec * w );
		tmp[3]		= pos1 + ( perp_vec * w );
	
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 2; c++ )
		{
			Image::RGBA start = *p_col0++;
			Image::RGBA end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		NxPs2::CImmediateMode::sDrawQuadTexture( mp_engine_texture, tmp[0], tmp[1], tmp[2], tmp[3], *((uint32 *) &color[0]), *((uint32 *) &color[0]), *((uint32 *) &color[1]), *((uint32 *) &color[1]) );
	}

	NxPs2::dma::EndTag();
}

} // Nx




================================================
FILE: Code/Gfx/NGPS/p_nxparticleribbon.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLERibbon_H__
#define	__GFX_P_NX_PARTICLERibbon_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleRibbon : public CParticle
{
public:
							CPs2ParticleRibbon();
							CPs2ParticleRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleRibbon();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
//	NxPs2::sParticleSystem*	mp_engine_particle;

	Image::RGBA				m_start_color[2];			// Start color for each corner.
	Image::RGBA				m_mid_color[2];				// Mid color for each corner.
	Image::RGBA				m_end_color[2];				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGPS/p_nxparticleribbontrail.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 


#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleRibbonTrail.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleRibbonTrail::CPs2ParticleRibbonTrail()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleRibbonTrail::CPs2ParticleRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	m_start_color = new Image::RGBA[m_num_vertex_buffers];
	m_mid_color = new Image::RGBA[m_num_vertex_buffers];
	m_end_color = new Image::RGBA[m_num_vertex_buffers];
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleRibbonTrail::~CPs2ParticleRibbonTrail()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;	
	delete [] m_start_color;
	delete [] m_mid_color;
	delete [] m_end_color;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleRibbonTrail::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleRibbonTrail::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleRibbonTrail::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleRibbonTrail::plat_get_num_particle_colors( void ) { return m_num_vertex_buffers; }
int CPs2ParticleRibbonTrail::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CPs2ParticleRibbonTrail::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CPs2ParticleRibbonTrail::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CPs2ParticleRibbonTrail::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CPs2ParticleRibbonTrail::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CPs2ParticleRibbonTrail::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CPs2ParticleRibbonTrail::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CPs2ParticleRibbonTrail::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CPs2ParticleRibbonTrail::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CPs2ParticleRibbonTrail::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CPs2ParticleRibbonTrail::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CPs2ParticleRibbonTrail::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CPs2ParticleRibbonTrail::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleRibbonTrail::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the particles.
	
//	// Used to figure the right and up vectors for creating screen-aligned particle quads.
//	//Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraOrientation;
//	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;
//
//	// Concatenate p_matrix with the emmission angle to create the direction.
//	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );
//
//	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
//	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
//	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
//	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );
//
//	screen_right.Normalize();
//	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend, ABS );

	Image::RGBA color[2];
	Image::RGBA *p_col0;
	Image::RGBA *p_col1;

	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;
	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );

	for ( lp = 0, p_particle = mp_particle_array; lp < m_num_particles; lp++, p_particle++ )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		Mth::Vector	pos[2];
		p_v = &mp_vertices[0][lp*3];
		pos[0].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		p_v = &mp_vertices[1][lp*3];
		pos[1].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );

		Mth::Vector	part_vec = pos[1] - pos[0];
		Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
		perp_vec.Normalize();

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		
		Mth::Vector tmp[4];

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		Image::RGBA start = *p_col0++;
		Image::RGBA end = *p_col1++;

		color[0].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
		color[0].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
		color[0].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
		color[0].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));

		tmp[0]		= pos[0] + ( perp_vec * w );
		tmp[1]		= pos[0] - ( perp_vec * w );

		for ( int c = 1; c < m_num_vertex_buffers; c++ )
		{
			start = *p_col0++;
			end = *p_col1++;

			color[1].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[1].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[1].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[1].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));

			if ( c > 1 )
			{
				p_v = &mp_vertices[c][lp*3];
				pos[1].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				part_vec = pos[1] - pos[0];
				perp_vec = Mth::CrossProduct( part_vec, at );
				perp_vec.Normalize();
			}

			tmp[2]		= pos[1] - ( perp_vec * w );
			tmp[3]		= pos[1] + ( perp_vec * w );

			NxPs2::CImmediateMode::sDrawQuadTexture( mp_engine_texture, tmp[0], tmp[1], tmp[2], tmp[3], *((uint32 *) &color[0]), *((uint32 *) &color[0]), *((uint32 *) &color[1]), *((uint32 *) &color[1]) );

			color[0] = color[1];
			pos[0] = pos[1];
			tmp[0] = tmp[3];
			tmp[1] = tmp[2];
		}
	}

	NxPs2::dma::EndTag();
}

} // Nx




================================================
FILE: Code/Gfx/NGPS/p_nxparticleribbontrail.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLERibbonTrail_H__
#define	__GFX_P_NX_PARTICLERibbonTrail_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleRibbonTrail : public CParticle
{
public:
							CPs2ParticleRibbonTrail();
							CPs2ParticleRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleRibbonTrail();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
//	NxPs2::sParticleSystem*	mp_engine_particle;

	Image::RGBA*			m_start_color;				// Start color for each corner.
	Image::RGBA*			m_mid_color;				// Mid color for each corner.
	Image::RGBA*			m_end_color;				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGPS/p_nxparticleshaded.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 


#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleShaded.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleShaded::CPs2ParticleShaded()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleShaded::CPs2ParticleShaded( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	for ( int lp = 0; lp < 4; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleShaded::~CPs2ParticleShaded()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleShaded::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleShaded::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleShaded::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleShaded::plat_get_num_particle_colors( void ) { return 4; }
int CPs2ParticleShaded::plat_get_num_vertex_lists( void ) { return 1; }
void CPs2ParticleShaded::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CPs2ParticleShaded::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CPs2ParticleShaded::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CPs2ParticleShaded::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CPs2ParticleShaded::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CPs2ParticleShaded::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CPs2ParticleShaded::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CPs2ParticleShaded::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CPs2ParticleShaded::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CPs2ParticleShaded::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CPs2ParticleShaded::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CPs2ParticleShaded::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleShaded::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	//Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraOrientation;
	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;

	// Concatenate p_matrix with the emmission angle to create the direction.
	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

	screen_right.Normalize();
	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend, ABS );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
	
		ss_right	= screen_right * w;
		ss_up		= screen_up * h;
	
		tmp[0]		= pos - ss_right + ss_up;
		tmp[1]		= pos + ss_right + ss_up;		
		tmp[2]		= pos + ss_right - ss_up;		
		tmp[3]		= pos - ss_right - ss_up;		
	
		Image::RGBA color[4];
		Image::RGBA *p_col0;
		Image::RGBA *p_col1;
		
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 4; c++ )
		{
			Image::RGBA start = *p_col0++;
			Image::RGBA end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}
		NxPs2::CImmediateMode::sDrawQuadTexture( mp_engine_texture, tmp[0], tmp[1], tmp[2], tmp[3],
												 *((uint32 *) &color[0]),
												 *((uint32 *) &color[1]),
												 *((uint32 *) &color[2]),
												 *((uint32 *) &color[3]));
	}

	NxPs2::dma::EndTag();
}

} // Nx




================================================
FILE: Code/Gfx/NGPS/p_nxparticleshaded.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLEShaded_H__
#define	__GFX_P_NX_PARTICLEShaded_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleShaded : public CParticle
{
public:
							CPs2ParticleShaded();
							CPs2ParticleShaded( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleShaded();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
//	NxPs2::sParticleSystem*	mp_engine_particle;

	Image::RGBA				m_start_color[4];			// Start color for each corner.
	Image::RGBA				m_mid_color[4];				// Mid color for each corner.
	Image::RGBA				m_end_color[4];				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGPS/p_nxparticlesmooth.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 


#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleSmooth.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleSmooth::CPs2ParticleSmooth()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleSmooth::CPs2ParticleSmooth( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	for ( int lp = 0; lp < 2; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleSmooth::~CPs2ParticleSmooth()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmooth::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmooth::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmooth::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleSmooth::plat_get_num_particle_colors( void ) { return 2; }
int CPs2ParticleSmooth::plat_get_num_vertex_lists( void ) { return 1; }
void CPs2ParticleSmooth::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CPs2ParticleSmooth::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CPs2ParticleSmooth::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CPs2ParticleSmooth::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CPs2ParticleSmooth::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CPs2ParticleSmooth::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CPs2ParticleSmooth::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CPs2ParticleSmooth::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CPs2ParticleSmooth::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CPs2ParticleSmooth::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CPs2ParticleSmooth::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CPs2ParticleSmooth::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmooth::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	//Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraOrientation;
	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;

	// Concatenate p_matrix with the emmission angle to create the direction.
	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

	screen_right.Normalize();
	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend, ABS );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
	
		ss_right	= screen_right * w;
		ss_up		= screen_up * h;
	
		tmp[0]		= pos - ss_right + ss_up;
		tmp[1]		= pos + ss_right + ss_up;		
		tmp[2]		= pos + ss_right - ss_up;		
		tmp[3]		= pos - ss_right - ss_up;		
	
		Image::RGBA color[2];
		Image::RGBA *p_col0;
		Image::RGBA *p_col1;
		
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 2; c++ )
		{
			Image::RGBA start = *p_col0++;
			Image::RGBA end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}
		NxPs2::CImmediateMode::sDraw5QuadTexture( mp_engine_texture, pos, tmp[0], tmp[1], tmp[2], tmp[3],
												  *((uint32 *) &color[0]),
												  *((uint32 *) &color[1]));
	}

	NxPs2::dma::EndTag();
}

} // Nx





================================================
FILE: Code/Gfx/NGPS/p_nxparticlesmooth.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLESmooth_H__
#define	__GFX_P_NX_PARTICLESmooth_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleSmooth : public CParticle
{
public:
							CPs2ParticleSmooth();
							CPs2ParticleSmooth( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleSmooth();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
//	NxPs2::sParticleSystem*	mp_engine_particle;

	Image::RGBA				m_start_color[2];			// Start color for each corner.
	Image::RGBA				m_mid_color[2];				// Mid color for each corner.
	Image::RGBA				m_end_color[2];				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/NGPS/p_nxparticlesmoothribbon.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 


#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleSmoothRibbon.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleSmoothRibbon::CPs2ParticleSmoothRibbon()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleSmoothRibbon::CPs2ParticleSmoothRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	for ( int lp = 0; lp < 4; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleSmoothRibbon::~CPs2ParticleSmoothRibbon()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;	
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmoothRibbon::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmoothRibbon::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmoothRibbon::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleSmoothRibbon::plat_get_num_particle_colors( void ) { return 4; }
int CPs2ParticleSmoothRibbon::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CPs2ParticleSmoothRibbon::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CPs2ParticleSmoothRibbon::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CPs2ParticleSmoothRibbon::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CPs2ParticleSmoothRibbon::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CPs2ParticleSmoothRibbon::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CPs2ParticleSmoothRibbon::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CPs2ParticleSmoothRibbon::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CPs2ParticleSmoothRibbon::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CPs2ParticleSmoothRibbon::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CPs2ParticleSmoothRibbon::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CPs2ParticleSmoothRibbon::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CPs2ParticleSmoothRibbon::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmoothRibbon::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the particles.
	
//	// Used to figure the right and up vectors for creating screen-aligned particle quads.
//	//Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraOrientation;
//	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;
//
//	// Concatenate p_matrix with the emmission angle to create the direction.
//	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );
//
//	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
//	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
//	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
//	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );
//
//	screen_right.Normalize();
//	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v0;
	float			*p_v1;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend, ABS );

	Image::RGBA color[4];
	Image::RGBA *p_col0;
	Image::RGBA *p_col1;

	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;
	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );

	for ( lp = 0, p_particle = mp_particle_array, p_v0 = mp_vertices[0], p_v1 = mp_vertices[(m_num_vertex_buffers-1)]; lp < m_num_particles; lp++, p_particle++, p_v0 += 3, p_v1 += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		Mth::Vector	pos0( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
		Mth::Vector	pos1( p_v1[0] + m_pos[X], p_v1[1] + m_pos[Y], p_v1[2] + m_pos[Z] );

		Mth::Vector	part_vec = pos1 - pos0;
		Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
		perp_vec.Normalize();

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
//		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );
		
//		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
//		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
//	
		tmp[0]		= pos0 + ( perp_vec * w );
		tmp[1]		= pos0 - ( perp_vec * w );
		tmp[2]		= pos1 - ( perp_vec * w );
		tmp[3]		= pos1 + ( perp_vec * w );
	
		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 4; c++ )
		{
			Image::RGBA start = *p_col0++;
			Image::RGBA end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		NxPs2::CImmediateMode::sDrawQuadTexture( mp_engine_texture, pos0, tmp[0], tmp[3], pos1, *((uint32 *) &color[0]), *((uint32 *) &color[1]), *((uint32 *) &color[2]), *((uint32 *) &color[3]) );
		NxPs2::CImmediateMode::sDrawQuadTexture( mp_engine_texture, pos0, tmp[1], tmp[2], pos1, *((uint32 *) &color[0]), *((uint32 *) &color[1]), *((uint32 *) &color[2]), *((uint32 *) &color[3]) );
	}
	
	NxPs2::dma::EndTag();
}

} // Nx




================================================
FILE: Code/Gfx/NGPS/p_nxparticlesmoothribbon.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLESmoothRibbon_H__
#define	__GFX_P_NX_PARTICLESmoothRibbon_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleSmoothRibbon : public CParticle
{
public:
							CPs2ParticleSmoothRibbon();
							CPs2ParticleSmoothRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleSmoothRibbon();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	//	void*					mp_display_list;
//	int						m_display_list_size;
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
//	NxPs2::sParticleSystem*	mp_engine_particle;

	Image::RGBA				m_start_color[4];			// Start color for each corner.
	Image::RGBA				m_mid_color[4];				// Mid color for each corner.
	Image::RGBA				m_end_color[4];				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/NGPS/p_nxparticlesmoothstar.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 


#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleSmoothStar.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleSmoothStar::CPs2ParticleSmoothStar()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleSmoothStar::CPs2ParticleSmoothStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];
	
	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	for ( int lp = 0; lp < 3; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;

	m_num_segments = num_segments;
	m_split = split;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleSmoothStar::~CPs2ParticleSmoothStar()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmoothStar::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmoothStar::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmoothStar::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleSmoothStar::plat_get_num_particle_colors( void ) { return 3; }
int CPs2ParticleSmoothStar::plat_get_num_vertex_lists( void ) { return 1; }
void CPs2ParticleSmoothStar::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CPs2ParticleSmoothStar::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CPs2ParticleSmoothStar::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CPs2ParticleSmoothStar::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CPs2ParticleSmoothStar::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CPs2ParticleSmoothStar::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CPs2ParticleSmoothStar::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CPs2ParticleSmoothStar::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CPs2ParticleSmoothStar::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CPs2ParticleSmoothStar::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CPs2ParticleSmoothStar::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CPs2ParticleSmoothStar::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleSmoothStar::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	//Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraOrientation;
	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;

	// Concatenate p_matrix with the emmission angle to create the direction.
	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

	screen_right.Normalize();
	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend, ABS );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[4];
	
		ss_right	= screen_right * w;
		ss_up		= screen_up * h;
	
		Image::RGBA color[3];
		Image::RGBA *p_col0;
		Image::RGBA *p_col1;

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 3; c++ )
		{
			Image::RGBA SmoothStart = *p_col0++;
			Image::RGBA end = *p_col1++;

			color[c].r = SmoothStart.r + (uint8)(( ((float)( end.r - SmoothStart.r )) * terp ));
			color[c].g = SmoothStart.g + (uint8)(( ((float)( end.g - SmoothStart.g )) * terp ));
			color[c].b = SmoothStart.b + (uint8)(( ((float)( end.b - SmoothStart.b )) * terp ));
			color[c].a = SmoothStart.a + (uint8)(( ((float)( end.a - SmoothStart.a )) * terp ));
		}

		tmp[1]  = pos;
		tmp[1] += ss_right * sinf( Mth::DegToRad( 0.0f ) ) * m_split;
		tmp[1] += ss_up    * cosf( Mth::DegToRad( 0.0f ) ) * m_split;

		for ( int lp2 = 0; lp2 < m_num_segments; lp2++ )
		{
			tmp[0]  = pos;
			tmp[0] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) ) * m_split;
			tmp[0] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) ) * m_split;
			
			tmp[3]  = pos;
			tmp[3] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 2 ) ) ) ) * m_split;
			tmp[3] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 2 ) ) ) ) * m_split;
			
			tmp[2]  = pos;
			tmp[2] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );
			tmp[2] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );

			NxPs2::CImmediateMode::sDrawSmoothStarSegment( tmp[0], pos, tmp[1], tmp[2], tmp[3],
														  *((uint32 *) &color[0]),
														  *((uint32 *) &color[1]),
														  *((uint32 *) &color[2]));
			tmp[1] = tmp[3];
		}
	}
	
	NxPs2::dma::EndTag();
}

} // Nx





================================================
FILE: Code/Gfx/NGPS/p_nxparticlesmoothstar.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLESmoothStar_H__
#define	__GFX_P_NX_PARTICLESmoothStar_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleSmoothStar : public CParticle
{
public:
							CPs2ParticleSmoothStar();
							CPs2ParticleSmoothStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleSmoothStar();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;

//	NxPs2::sParticleSystem*	mp_engine_particle;
	int						m_num_segments;
	float					m_split;
	Image::RGBA				m_start_color[3];			// SmoothStart color for each corner.
	Image::RGBA				m_mid_color[3];				// Mid color for each corner.
	Image::RGBA				m_end_color[3];				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/NGPS/p_nxparticlestar.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 


#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxparticleStar.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleStar::CPs2ParticleStar()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleStar::CPs2ParticleStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum = checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;

	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

//	// Create the engine representation.
//	mp_engine_particle = new NxPs2::sParticleSystem( max_particles, texture_checksum, blendmode_checksum, fix );
//

	// Get the texture.

	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_engine_texture = NULL;

	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_engine_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set blendmode.
	m_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );

	// Default color.
	for ( int lp = 0; lp < 3; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}

	m_mid_time = -1.0f;

	m_num_segments = num_segments;
	m_split = split;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2ParticleStar::~CPs2ParticleStar()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
//	delete mp_engine_particle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleStar::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleStar::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleStar::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CPs2ParticleStar::plat_get_num_particle_colors( void ) { return 3; }
int CPs2ParticleStar::plat_get_num_vertex_lists( void ) { return 1; }
void CPs2ParticleStar::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CPs2ParticleStar::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CPs2ParticleStar::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CPs2ParticleStar::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CPs2ParticleStar::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CPs2ParticleStar::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CPs2ParticleStar::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CPs2ParticleStar::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CPs2ParticleStar::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CPs2ParticleStar::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CPs2ParticleStar::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CPs2ParticleStar::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2ParticleStar::plat_render( void )
{
	if (m_num_particles == 0)
		return;

	// Draw the particles.
	
	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	//Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraOrientation;
	Mth::Matrix* p_matrix = (Mth::Matrix*)&NxPs2::render::CameraToWorldRotation;

	// Concatenate p_matrix with the emmission angle to create the direction.
	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	Mth::Vector at( p_matrix->GetAt()[X], p_matrix->GetAt()[Y], p_matrix->GetAt()[Z], 0.0f );
	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

	screen_right.Normalize();
	screen_up.Normalize();
	
	int				lp;
	CParticleEntry	*p_particle;
	float			*p_v;

	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( mp_engine_texture, m_blend, ABS );

	for ( lp = 0, p_particle = mp_particle_array, p_v = mp_vertices; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
	{
		float terp = p_particle->m_time / p_particle->m_life;

		float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		float h = p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

		// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
		Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
		Mth::Vector	ss_right, ss_up;	//, ss_pos;
		Mth::Vector tmp[3];
	
		ss_right	= screen_right * w;
		ss_up		= screen_up * h;
	
		Image::RGBA color[3];
		Image::RGBA *p_col0;
		Image::RGBA *p_col1;

		if ( m_mid_time >= 0.0f )
		{
			if ( terp < m_mid_time )
			{
				p_col0 = m_start_color;
				p_col1 = m_mid_color;
				// Adjust interpolation for this half of the color blend.
				terp = terp / m_mid_time;
			}
			else
			{
				p_col0 = m_mid_color;
				p_col1 = m_end_color;
				// Adjust interpolation for this half of the color blend.
				terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
			}
		}
		else
		{
			// No mid color specified.
			p_col0 = m_start_color;
			p_col1 = m_end_color;
		}

		for ( int c = 0; c < 3; c++ )
		{
			Image::RGBA start = *p_col0++;
			Image::RGBA end = *p_col1++;

			color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
			color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
			color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
			color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
		}

		tmp[0]  = pos;
		tmp[0] += ss_right * sinf( Mth::DegToRad( 0.0f ) ) * m_split;
		tmp[0] += ss_up    * cosf( Mth::DegToRad( 0.0f ) ) * m_split;

		for ( int lp2 = 0; lp2 < m_num_segments; lp2++ )
		{
			tmp[1]  = pos;
			tmp[1] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;
			tmp[1] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;

			tmp[2]  = pos;
			tmp[2] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );
			tmp[2] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );

			NxPs2::CImmediateMode::sDrawStarSegment( pos, tmp[0], tmp[1], tmp[2],
													 *((uint32 *) &color[0]),
													 *((uint32 *) &color[1]),
													 *((uint32 *) &color[2]));
			tmp[0] = tmp[1];
		}
	}

	NxPs2::dma::EndTag();
}

} // Nx





================================================
FILE: Code/Gfx/NGPS/p_nxparticlestar.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLEStar_H__
#define	__GFX_P_NX_PARTICLEStar_H__
    
#include "gfx/ngps/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CPs2ParticleStar : public CParticle
{
public:
							CPs2ParticleStar();
							CPs2ParticleStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CPs2ParticleStar();

//	NxPs2::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
//	void*					mp_display_list;
//	int						m_display_list_size;
	float*					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;

//	NxPs2::sParticleSystem*	mp_engine_particle;
	int						m_num_segments;
	float					m_split;
	Image::RGBA				m_start_color[3];			// Start color for each corner.
	Image::RGBA				m_mid_color[3];				// Mid color for each corner.
	Image::RGBA				m_end_color[3];				// End color for each corner.
	NxPs2::SSingleTexture*	mp_engine_texture;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/NGPS/p_nxweather.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxWeather.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  6/2/2003
//****************************************************************************

#include 
#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"

#include "gfx/ngps/nx/line.h"
#include 
#include 

#include "gfx/ngps/nx/immediate.h"
#include "gfx/ngps/nx/vu1code.h"

#include "gfx/ngps/nx/mesh.h"

#include "gfx/ngps/p_nxweather.h"

#include 
#include 

#include 
#include 
#include "gfx/nx.h"

#include "gfx/ngps/nx/render.h"
#include "gfx/ngps/nx/dma.h"
#include "gfx/ngps/nx/vif.h"
#include "gfx/ngps/nx/vu1.h"
#include "gfx/ngps/nx/gif.h"
#include "gfx/ngps/nx/gs.h"
#include "gfx/ngps/nx/sprite.h"
#include "gfx/ngps/nx/switches.h"
#include "gfx/ngps/nx/vu1code.h"

#define FEELER_START 50000.0f

#define _STCYCL(a,b) ((uint32)0x01<<24 | (uint32)0<<16 | (uint32)((a)<<8|(b)))
#define _UNPACK(m,v,s,f,u,a) (((0x60|(m)<<4|(v))<<24) | ((s)<<16) | ((f)<<15 | (u)<<14 | ((NxPs2::vu1::Loc+(a))&0x03FF)))

#define _NREG(n) ((float)(n) * 1.1920928955e-07f)
#define _PRIM(n,f,p,r,a) (n)<<28 | (f)<<26 | (p)<<15 | (r)<<14 | (a)

#define _BEGINUNPACK(m,v,f,u,a,nbytes)																				\
					((uint32)(0x60 | (m)<<4 | (v))<<24 |														\
					(uint32)(( ((nbytes) << 3) / ( (((v)>>2 & 3) + 1) * (32 >> ((v) & 3)) ) ))<<16 |			\
					(uint32)((f)<<15 | (u)<<14 | ((NxPs2::vu1::Loc+(a))&0x03FF)))


#define _STMOD(o) ((uint32)0x05<<24 | (uint32)0<<16 | (uint32)(o)) 


#define _SIZE(v,nbytes) ((uint32)(( ((nbytes) << 3) / ( (((v)>>2 & 3) + 1) * (32 >> ((v) & 3)) ) )))

#define _PARSER ((uint32)0x14<<24 | (uint32)0<<16 | (uint32)VU1_ADDR(Parser)) 

#define NUM_LINES_PER_BATCH 80
#define NUM_SPRITES_PER_BATCH 80

#define PRECISION_SHIFT 4

unsigned char grid_bytes[70*1024];

#define RENDER_DIST 16
//#define RAIN_HEIGHT 2000
//#define RAIN_FRAMES 40
//#define RAIN_LENGTH 100.0f
#define SEQ_START 411		// Between 1 and 4095.
#define SEQ_MASK 0x0240
//#define SEQ_MASK 0x0ca0
//#define NUM_DROPS_PER_FRAME 25

//1-3: 0x03
//1-7: 0x06
//1-15: 0x0C
//1-31: 0x14
//1-63: 0x30
//1-127: 0x60
//1-255: 0xB8
//1-511: 0x0110
//1-1023: 0x0240
//1-2047: 0x0500
//1-4095: 0x0CA0
//1-8191: 0x1B00
//1-16383: 0x3500
//1-32767: 0x6000
//1-65535: 0xB400
//0x00012000
//0x00020400
//0x00072000
//0x00090000
//0x00140000
//0x00300000
//0x00400000
//0x00D80000
//0x01200000
//0x03880000
//0x07200000
//0x09000000
//0x14000000
//0x32800000
//0x48000000
//0xA3000000


//#define DEBUG_LINES

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2Weather::CPs2Weather()
{
	mp_roof_height_index = NULL;
	mp_rain_texture = NULL;

	m_rain_blend = NxPs2::CImmediateMode::sGetTextureBlend( CRCD(0xa86285a1,"fixadd"), 64 );		// FixAdd / 64
	m_splash_blend = NxPs2::CImmediateMode::sGetTextureBlend( CRCD(0xa86285a1,"fixadd"), 64 );		// FixAdd / 64
	m_snow_blend = NxPs2::CImmediateMode::sGetTextureBlend( CRCD(0xa86285a1,"fixadd"), 64 );		// FixAdd / 64

	m_rain_rate = 0.0f;
	m_splash_rate = 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CPs2Weather::~CPs2Weather()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2Weather::plat_update_grid( void )
{
	m_system_active = false;
	// Get super sector manager.
	SSec::Manager *ss_man;
	Mth::Line line;
	ss_man = Nx::CEngine::sGetNearestSuperSectorManager( line );		// Line is ignored, 1st manager is returned.
	if ( !ss_man ) return;

	// Calculate the size of the world in cels.
	int min_x = ( ((int)ss_man->GetWorldBBox()->GetMin()[X]) / WEATHER_CEL_SIZE ) - 1;
	int max_x = ( ((int)ss_man->GetWorldBBox()->GetMax()[X]) / WEATHER_CEL_SIZE ) + 1;
	int min_z = ( ((int)ss_man->GetWorldBBox()->GetMin()[Z]) / WEATHER_CEL_SIZE ) - 1;
	int max_z = ( ((int)ss_man->GetWorldBBox()->GetMax()[Z]) / WEATHER_CEL_SIZE ) + 1;

	// Define a maximum...
	if ( ( max_x - min_x ) > 350 )
	{
		int wid = ( max_x - min_x );
		int excess = ( wid - 350 );
		min_x += ( excess / 2 );
		max_x -= ( excess / 2 );
	}

	if ( ( max_z - min_z ) > 300 )
	{
		int wid = ( max_z - min_z );
		int excess = ( wid - 300 );
		min_z += ( excess / 2 );
		max_z -= ( excess / 2 );
	}

	// This is the actual width;
	m_width = ( max_x - min_x ) + 1;
	m_height = ( max_z - min_z ) + 1;

	// Allocate a new piece of memory for the grid.
//	if ( mp_roof_height_index ) delete mp_roof_height_index;
//	mp_roof_height_index = new unsigned char[m_width * m_height];

//	mp_roof_height_index = grid_bytes;

	m_min_x = ( min_x * WEATHER_CEL_SIZE );
	m_min_z = ( min_z * WEATHER_CEL_SIZE );

	// Temporary buffer for the raw array.
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	unsigned char * p8 = new unsigned char[m_width*m_height];
	Mem::Manager::sHandle().PopContext();

	// Go through and get the height for each cel corner.
	CFeeler feeler;
	int num_heights = 0;
	int xx, zz;
	for ( zz = min_z; zz <= max_z; zz++ )
	{
		for ( xx = min_x; xx <= max_x; xx++ )
		{
			// The cel to fill.
			int cel = ( ( zz - min_z ) * m_width ) + ( xx - min_x );

			// The position to check.
			Mth::Vector pos;
			pos[X] = (float)( xx * WEATHER_CEL_SIZE );
			pos[Y] = FEELER_START;
			pos[Z] = (float)( zz * WEATHER_CEL_SIZE );
			pos[W] = 1.0f;

			feeler.SetStart( pos );
			pos[Y] = -FEELER_START;
			feeler.SetEnd( pos );

			// Get the y position.
			sint32 y;
			if ( feeler.GetCollision( false, false ) )		// No movables, nearest collision.
			{
				y = (sint32)( feeler.GetPoint()[Y] * SUB_INCH_PRECISION );
			}
			else
			{
				y = (sint32)( FEELER_START * SUB_INCH_PRECISION );
			}

			// See if a close enough y pos already exists.
			int found = -1;
			for ( int lp = 0; lp < num_heights; lp++ )
			{
				if ( abs( ( m_roof_height[lp] - y ) ) < HEIGHT_TOLERANCE )
				{
					found = lp;
					break;
				}
			}

			// Fill in the cel.
			if ( found != -1 )
			{
				// Existing height.
				p8[cel] = found;
			}
			else
			{
				// New height.
				p8[cel] = num_heights;
				m_roof_height[num_heights] = y;
				num_heights++;
			}
		}
	}

	// Work out highest height for each cel.
	for ( zz = min_z; zz <= max_z; zz++ )
	{
		for ( xx = min_x; xx <= max_x; xx++ )
		{
			// The cel to fill.
			int cel = ( ( zz - min_z ) * m_width ) + ( xx - min_x );
			int celx = ( ( zz - min_z ) * m_width ) + ( ( xx + 1 ) - min_x );
			int celz = ( ( ( zz + 1 ) - min_z ) * m_width ) + ( xx - min_x );
			int celxz = ( ( ( zz + 1 ) - min_z ) * m_width ) + ( ( xx + 1 ) - min_x );

			if ( m_roof_height[p8[celx]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celx];
			if ( m_roof_height[p8[celz]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celz];
			if ( m_roof_height[p8[celxz]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celxz];
		}
	}

	// Create a sparse array.
	mp_roof_row = (sRowEntry *)grid_bytes;
	mp_roof_height_index = (unsigned char *)&mp_roof_row[m_height];

	// 0 = offset
	// 1 = width
	// 2 = index

	unsigned short index = 0;
	for ( zz = 0; zz <= m_height; zz++ )
	{
		unsigned short start = 0;
		unsigned short end = m_width - 1;

		// Scan to find the start.
		bool start_set = false;
		for ( xx = 0; xx < m_width; xx++ )
		{
			int cel = ( zz * m_width ) + xx;

			if ( m_roof_height[p8[cel]] != (sint32)( FEELER_START * SUB_INCH_PRECISION ) )
			{
				if ( !start_set )
				{
					// Set start value.
					start = xx;
					start_set = true;
				}
				else
				{
					// Set end value.
					end = xx;
				}

			}
		}

		// Copy data & set row entry.
		if ( start < end )
		{
			mp_roof_row[zz].start = start;
			mp_roof_row[zz].end = end;
			mp_roof_row[zz].index = index;
			for ( xx = start; xx <= end ; xx++ )
			{
				int cel = ( zz * m_width ) + xx;

				mp_roof_height_index[index] = p8[cel];
				index++;
			}
		}
		else
		{
			// Row doesn't exist.
			mp_roof_row[zz].start = 16384;
			mp_roof_row[zz].end = 0;
			mp_roof_row[zz].index = 0;
		}
	}

	delete p8;

	int new_size = ( m_height * 6 ) + index;

	printf( "Grid Size: Old: %d New: %d Num Heights: %d\n", ( m_width * m_height ), new_size, num_heights );
//	printf( "Num Weather Grid Heights: %d (%d x %d)\n", num_heights, m_width, m_height );

	// Set all drip time counters to 255.
	// 255 means the drop won't be rendered.
	// Setting to anything other than 255 will mean that it will increment to 255 and stop.
	for ( int lp = 0; lp < ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ); lp++ )
	{
		m_drop_time[lp] = 255;
		m_x_offset[lp] = Mth::Rnd( 255 );
		m_z_offset[lp] = Mth::Rnd( 255 );
	}
	m_active_drops = 0;

	m_seq = SEQ_START;

	// Get the texture.
	Nx::CTexture *p_texture;
	Nx::CPs2Texture *p_ps2_texture;
	mp_rain_texture = NULL;

	// Set Rain Texture.
	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( CRCD(0x45c7eb0f,"splash") );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_rain_texture = p_ps2_texture->GetSingleTexture(); 
	}

	// Set Snow Texture.
	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( CRCD(0xc97c5a4c,"snow") );
	p_ps2_texture = static_cast( p_texture );
	if ( p_ps2_texture )
	{
		mp_snow_texture = p_ps2_texture->GetSingleTexture(); 
	}

	m_system_active = true;

	// Zero out the splashes.
	for ( int sp = 0; sp < NUM_SPLASH_ACTIVE; sp++ )
	{
		m_splash_current_life[sp] = 0;
	}
	m_current_splash = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2Weather::plat_process( float delta_time )
{
	if ( !m_system_active ) return;

	if ( m_raining )
	{
		// It's raining.
		float rate = m_rain_drops_per_frame;
		if ( rate > (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_rain_frames ) ) rate = (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_rain_frames );

		float last = m_rain_rate;
		m_rain_rate += rate;
		int new_drops = (int)m_rain_rate - (int)last;

		for ( int lp = 0; lp < new_drops; lp++ )
		{
			// If this cel is not inactive, we caught up with ourselves.
			if ( m_drop_time[m_seq] != 255 ) break;

			// Setup the drop.
			m_drop_time[m_seq] = ( 254 - m_rain_frames );
			m_x_offset[m_seq] = Mth::Rnd( 255 );
			m_z_offset[m_seq] = Mth::Rnd( 255 );
			m_active_drops++;

			// Calculate next sequence value. Notice hack to get entry 0 into the sequence.
			switch ( m_seq )
			{
				case SEQ_START:
					m_seq = 0;
					break;
				case 0:
					m_seq = SEQ_START;
					// Fall through to default case...
				default:
					if ( m_seq & 1 )
					{
						m_seq = ( m_seq >> 1 ) ^ SEQ_MASK;
					}
					else
					{
						m_seq = ( m_seq >> 1 );
					}
					break;
			}
		}
	}
	else
	{
		// It's snowing.
		float rate = m_snow_flakes_per_frame;
		if ( rate > (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_snow_frames ) ) rate = (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_snow_frames );

		float last = m_snow_rate;
		m_snow_rate += rate;
		int new_drops = (int)m_snow_rate - (int)last;

		for ( int lp = 0; lp < new_drops; lp++ )
		{
			// If this cel is not inactive, we caught up with ourselves.
			if ( m_drop_time[m_seq] != 255 ) break;

			// Setup the drop.
			m_drop_time[m_seq] = ( 254 - m_snow_frames );
			m_x_offset[m_seq] = Mth::Rnd( 255 );
			m_z_offset[m_seq] = Mth::Rnd( 255 );
			m_active_drops++;

			// Calculate next sequence value. Notice hack to get entry 0 into the sequence.
			switch ( m_seq )
			{
				case SEQ_START:
					m_seq = 0;
					break;
				case 0:
					m_seq = SEQ_START;
					// Fall through to default case...
				default:
					if ( m_seq & 1 )
					{
						m_seq = ( m_seq >> 1 ) ^ SEQ_MASK;
					}
					else
					{
						m_seq = ( m_seq >> 1 );
					}
					break;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2Weather::plat_render_snow( float skx, float skz )
{
	// Early out if no drops to draw.
	if ( !m_active_drops ) return;

	// Get skater position.
	int x = (int)( ( skx - m_min_x ) / WEATHER_CEL_SIZE );
	int z = (int)( ( skz - m_min_z ) / WEATHER_CEL_SIZE );

	// Calculate area to render.
	int sx = x - RENDER_DIST;
	int ex = x + DROP_SIZE;	//RENDER_DIST;
	int sz = z - RENDER_DIST;
	int ez = z + DROP_SIZE;	//RENDER_DIST;

	// Clip z values.
	if ( ez < 0 ) return;
	if ( sz > ( m_height - 1 ) ) return;

//	if ( sz < 0 ) sz = 0;
//	if ( ez > ( m_height - 1 ) ) ez = ( m_height - 1 );

#ifdef DEBUG_LINES
	NxPs2::BeginLines3D( 0xffffffff );
#else
	// add a dma packet
	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);

	// VU context
	NxPs2::vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));
	NxPs2::vu1::StoreVec(*(NxPs2::Vec *)&NxPs2::render::InverseIntViewportScale);
	NxPs2::vu1::StoreVec(*(NxPs2::Vec *)&NxPs2::render::InverseIntViewportOffset);
	NxPs2::vu1::StoreMat(*(NxPs2::Mat *)&NxPs2::render::WorldToIntViewport);	// VF12-15
	NxPs2::vu1::EndPrim(0);
	NxPs2::vu1::BeginPrim(ABS, VU1_ADDR(L_VF30));
	NxPs2::vif::StoreV4_32F(640.0f, 480.0f, 0.0f, 0.0f);						// VF30
	NxPs2::vif::StoreV4_32F(NxPs2::render::IntViewportScale[0]/NxPs2::render::Tx,	// VF31
							NxPs2::render::IntViewportScale[1]/NxPs2::render::Ty,
							0.0f, 0.0f);
	NxPs2::vu1::EndPrim(0);

	// GS context
	NxPs2::gs::BeginPrim(ABS, 0, 0);
	NxPs2::gs::Reg1(NxPs2::gs::ALPHA_1,	m_snow_blend);
	NxPs2::gs::Reg1(NxPs2::gs::TEX0_1,	mp_snow_texture->m_RegTEX0);
	NxPs2::gs::Reg1(NxPs2::gs::TEX1_1,	mp_snow_texture->m_RegTEX1);
	NxPs2::gs::Reg1(NxPs2::gs::ST,		PackST(0x3F800000,0x3F800000));
	NxPs2::gs::Reg1(NxPs2::gs::RGBAQ,	PackRGBAQ(0,0,0,0,0x3F800000));
	NxPs2::gs::EndPrim(0);

#endif		// DEBUG_LINES

	float minx = m_min_x;
	float minz = m_min_z;
//	sint32 rlength = (sint32)( m_rain_length * SUB_INCH_PRECISION );
	
	// Calculate drop height list.
	int lp;
	float y_off[256];

	for ( lp = ( 254 - m_snow_frames ); lp < 256; lp++ )
	{
		y_off[lp] = m_snow_height - ( ( (float)( ( lp - 254 ) + m_snow_frames ) / (float)m_snow_frames ) * m_snow_height );
	}

	// Calculate xz offset list.
	float xz_off[256];

	for ( lp = 0; lp < 256; lp++ )
	{
		xz_off[lp] = ( (float)lp * (float)( WEATHER_CEL_SIZE / 2 ) ) / 255.0f;
	}

	// Calculate sine wave.
	float sin_off[256];

	for ( lp = 0; lp < 256; lp++ )
	{
		sin_off[lp] = sinf( Mth::PI * 2.0f * ( (float)lp / 256.0f ) ) * ( WEATHER_CEL_SIZE / 2 );
//		sin_off[lp] = sinf( Mth::PI * 2.0f * (float)lp ) * 128.0f;
	}

	int flakes = 0;
	uint32 * p_col = NULL;
	float * p_xyz = NULL;
//	uint32 * p_last_loc = NULL;
//	uint32 * p_tag = NULL;

	for ( int lzz = sz; lzz <= ez; lzz++ )
	{
		int zz = ( lzz < 0 ) ? 0 : ( lzz > ( m_height - 1 ) ) ? ( m_height - 1 ) : lzz;

		if ( mp_roof_row[zz].start == 16384 ) continue;

//		// Calculate actual span to scan.
//		int rsx = sx > mp_roof_row[zz].start ? sx : mp_roof_row[zz].start;
//		int rex = ex < mp_roof_row[zz].end ? ex : mp_roof_row[zz].end;
//
//		// Start position.
//		sint32 vx = ( (sint32)rsx * WEATHER_CEL_SIZE * (sint32)SUB_INCH_PRECISION ) + minx;
//		sint32 vz = ( (sint32)zz * WEATHER_CEL_SIZE * (sint32)SUB_INCH_PRECISION ) + minz;

		float vx = ( (float)sx * (float)WEATHER_CEL_SIZE ) + minx;
		float vz = ( (float)zz * (float)WEATHER_CEL_SIZE ) + minz;

		int cel = mp_roof_row[zz].index + ( sx - mp_roof_row[zz].start );

		int drop_cel_z = ( ( zz & ( DROP_SIZE - 1 ) ) << DROP_SIZE_SHIFT );

//		for ( int xx = rsx; xx <= rex; xx++, cel++ )
		for ( int lxx = sx; lxx <= ex; lxx++, cel++ )
		{
			int xx = ( lxx > mp_roof_row[zz].start ) ? ( ( lxx < mp_roof_row[zz].end ) ? lxx : mp_roof_row[zz].end ) : mp_roof_row[zz].start;

			// Get the current drop value. Skip this one if it's inactive.
			int drop_cel = drop_cel_z + ( xx & ( DROP_SIZE - 1 ) );

//			vx += ( WEATHER_CEL_SIZE << PRECISION_SHIFT );
			vx += (float)WEATHER_CEL_SIZE;
			float vy = (float)( m_roof_height[mp_roof_height_index[cel]] >> PRECISION_SHIFT );

			for ( int d = 0; d < DROP_LAYERS; d++, drop_cel += ( DROP_SIZE * DROP_SIZE ) )
			{
				if ( m_drop_time[drop_cel] == 255 ) continue;

				// Create the position for rendering.
				float v0x = vx + xz_off[m_x_offset[drop_cel]] + sin_off[(m_z_offset[drop_cel]+m_drop_time[drop_cel])&255];
				float v0y = vy + y_off[m_drop_time[drop_cel]] + m_snow_size;
				float v0z = vz + xz_off[m_z_offset[drop_cel]] + sin_off[(m_x_offset[drop_cel]+m_drop_time[drop_cel])&255];

//				sint32 v1y = v0y + rlength;

#ifdef DEBUG_LINES
				NxPs2::DrawLine3D( v0[X], v0[Y], v0[Z], v1[X], v1[Y], v1[Z] );
#else
				if ( flakes == 0 )
				{
					NxPs2::BeginModelPrimImmediate(NxPs2::gs::XYZ2		|
												   NxPs2::gs::ST<<4		|
												   NxPs2::gs::RGBAQ<<8	|
												   NxPs2::gs::XYZ2<<12,
												   4, SPRITE|ABE|TME, 1, VU1_ADDR(SpriteCull));

					// create an unpack for the colours
					NxPs2::vif::BeginUNPACK(0, V4_8, ABS, UNSIGNED, 3);
					p_col = (uint32 *)NxPs2::dma::pLoc;
					NxPs2::dma::pLoc += NUM_SPRITES_PER_BATCH * 4;
					NxPs2::vif::EndUNPACK();

					// and one for the positions (& sizes)
					NxPs2::vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 4);
					p_xyz = (float *)NxPs2::dma::pLoc;
					NxPs2::dma::pLoc += NUM_SPRITES_PER_BATCH * 16;
					NxPs2::vif::EndUNPACK();

					NxPs2::EndModelPrimImmediate(1);

					NxPs2::vif::MSCAL(VU1_ADDR(Parser));
				
					flakes = NUM_SPRITES_PER_BATCH;
				}

				*p_col++ = *((uint32 *)&m_snow_color);

				p_xyz[0] = v0x;
				p_xyz[1] = v0y;
				p_xyz[2] = v0z;
				p_xyz[3] = m_snow_size;
				p_xyz += 4;

				flakes--;

#endif		// DEBUG_LINES
			}
		}
	}
//	printf( "Drop comparison: Counted: %d Calculated: %d\n", drops, m_active_drops );

	// Fix the last packet.
//	if ( flakes == NUM_SPRITES_PER_BATCH )
//	{
//		NxPs2::vu1::Loc -= _SIZE(V4_32,16*NUM_SPRITES_PER_BATCH) * 2 + 1;
//		p_loc = p_last_loc;
//	}
//	else if ( lines )
//	{
	if ( flakes )
	{
		// Fill in with degenerate lines.
		for ( int l = 0; l < flakes; l++ )
		{
			p_xyz[0] = 0;
			p_xyz[1] = 0;
			p_xyz[2] = 0;
			p_xyz[3] = 0;

			p_xyz += 4;
		}
	}

#ifdef DEBUG_LINES
	NxPs2::EndLines3D();
#else
	// restore transform
	NxPs2::vu1::BeginPrim(ABS, VU1_ADDR(L_VF12));		// was L_VF16
	NxPs2::vu1::StoreMat(*(NxPs2::Mat *)&NxPs2::render::AdjustedWorldToViewport);		// VF16-19
	NxPs2::vu1::EndPrim(1);
	NxPs2::vif::MSCAL(VU1_ADDR(Parser));

	// end the dma tag
	NxPs2::dma::EndTag();
#endif		// DEBUG_LINES
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2Weather::plat_render_rain( float skx, float skz )
{
	// Early out if no drops to draw.
	if ( !m_active_drops ) return;

	// Get skater position.
	int x = (int)( ( skx - m_min_x ) / WEATHER_CEL_SIZE );
	int z = (int)( ( skz - m_min_z ) / WEATHER_CEL_SIZE );

	// Calculate area to render.
	int sx = x - RENDER_DIST;
	int ex = x + DROP_SIZE;	//RENDER_DIST;
	int sz = z - RENDER_DIST;
	int ez = z + DROP_SIZE;	//RENDER_DIST;

	// Clip z values.
	if ( ez < 0 ) return;
	if ( sz > ( m_height - 1 ) ) return;

//	if ( sz < 0 ) sz = 0;
//	if ( ez > ( m_height - 1 ) ) ez = ( m_height - 1 );

#ifdef DEBUG_LINES
	NxPs2::BeginLines3D( 0xffffffff );
#else
	NxPs2::BeginLines3D( 0xffffffff );
	NxPs2::EndLines3D();
	NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);
	NxPs2::CImmediateMode::sStartPolyDraw( NULL, m_rain_blend, ABS );
	register uint32 *p_loc = (uint32*)NxPs2::dma::pLoc;
#endif		// DEBUG_LINES

	sint32 minx = (sint32)( m_min_x * SUB_INCH_PRECISION );
	sint32 minz = (sint32)( m_min_z * SUB_INCH_PRECISION );
	sint32 rlength = (sint32)( m_rain_length * SUB_INCH_PRECISION );
	
	// Calculate drop height list.
	int lp;
	sint32 y_off[256];

	for ( lp = ( 254 - m_rain_frames ); lp < 256; lp++ )
	{
		y_off[lp] = (sint32)( ( m_rain_height - ( ( (float)( ( lp - 254 ) + m_rain_frames ) / (float)m_rain_frames ) * m_rain_height ) ) * SUB_INCH_PRECISION );
	}

	// Calculate xz offset list.
	sint32 xz_off[256];

	for ( lp = 0; lp < 256; lp++ )
	{
		xz_off[lp] = (sint32)( ( ( (float)lp * (float)WEATHER_CEL_SIZE * SUB_INCH_PRECISION ) / 255.0f ) );
	}

	int lines = 0;
	uint32 * p_col = NULL;
	sint32 * p_xyz = NULL;
	uint32 * p_last_loc = NULL;
//	uint32 * p_tag = NULL;

	for ( int lzz = sz; lzz <= ez; lzz++ )
	{
		int zz = ( lzz < 0 ) ? 0 : ( lzz > ( m_height - 1 ) ) ? ( m_height - 1 ) : lzz;

		if ( mp_roof_row[zz].start == 16384 ) continue;

//		// Calculate actual span to scan.
//		int rsx = sx > mp_roof_row[zz].start ? sx : mp_roof_row[zz].start;
//		int rex = ex < mp_roof_row[zz].end ? ex : mp_roof_row[zz].end;
//
//		// Start position.
//		sint32 vx = ( (sint32)rsx * WEATHER_CEL_SIZE * (sint32)SUB_INCH_PRECISION ) + minx;
//		sint32 vz = ( (sint32)zz * WEATHER_CEL_SIZE * (sint32)SUB_INCH_PRECISION ) + minz;

		sint32 vx = ( (sint32)sx << ( WEATHER_CEL_SIZE_SHIFT + PRECISION_SHIFT ) ) + minx;
		sint32 vz = ( (sint32)zz << ( WEATHER_CEL_SIZE_SHIFT + PRECISION_SHIFT ) ) + minz;

		int cel = mp_roof_row[zz].index + ( sx - mp_roof_row[zz].start );

		int drop_cel_z = ( ( zz & ( DROP_SIZE - 1 ) ) << DROP_SIZE_SHIFT );

//		for ( int xx = rsx; xx <= rex; xx++, cel++ )
		for ( int lxx = sx; lxx <= ex; lxx++, cel++ )
		{
			int xx = ( lxx > mp_roof_row[zz].start ) ? ( ( lxx < mp_roof_row[zz].end ) ? lxx : mp_roof_row[zz].end ) : mp_roof_row[zz].start;

			// Get the current drop value. Skip this one if it's inactive.
			int drop_cel = drop_cel_z + ( xx & ( DROP_SIZE - 1 ) );

			vx += ( WEATHER_CEL_SIZE << PRECISION_SHIFT );
			sint32 vy = m_roof_height[mp_roof_height_index[cel]];

			for ( int d = 0; d < DROP_LAYERS; d++, drop_cel += ( DROP_SIZE * DROP_SIZE ) )
			{
				if ( m_drop_time[drop_cel] == 255 ) continue;

				// Create the position for rendering.
				sint32 v0x = vx + xz_off[m_x_offset[drop_cel]];
				sint32 v0y = vy + y_off[m_drop_time[drop_cel]]; 
				sint32 v0z = vz + xz_off[m_z_offset[drop_cel]];

				sint32 v1y = v0y + rlength;

#ifdef DEBUG_LINES
				NxPs2::DrawLine3D( v0[X], v0[Y], v0[Z], v1[X], v1[Y], v1[Z] );
#else
				if ( lines == 0 )
				{
					p_last_loc = p_loc;

					p_loc[0] = _STCYCL(1,2);
					p_loc[1] = _UNPACK(0,V4_32,1,ABS,UNSIGNED,0);
//					p_tag = &p_loc[2];
					*(float*)&p_loc[2] = _NREG(2);
					p_loc[2] |= (1<<15) | _SIZE(V4_32,16*2*NUM_LINES_PER_BATCH);

					p_loc[3] = _PRIM(2, PACKED, LINE|IIP|ABE, 1, VU1_ADDR(Line));
					p_loc[4] = NxPs2::gs::XYZ2<<4|NxPs2::gs::RGBAQ;
					p_loc[5] = ( _SIZE(V4_32,16*2*NUM_LINES_PER_BATCH) * 2 ); 
					p_loc[6] = _BEGINUNPACK(0, V4_8, ABS, UNSIGNED, 1, 4*2*NUM_LINES_PER_BATCH); 

					lines = NUM_LINES_PER_BATCH;

					p_col = &p_loc[7];
					p_loc = &p_loc[7+(2*NUM_LINES_PER_BATCH)];

					p_loc[0] = _STMOD(OFFSET_MODE); 
					p_loc[1] = _BEGINUNPACK(1, V4_32, ABS, SIGNED, 2, 16*2*NUM_LINES_PER_BATCH);

					p_xyz = (sint32 *)&p_loc[2];
					p_loc = &p_loc[2+(8*NUM_LINES_PER_BATCH)];
				
					p_loc[0] = _STMOD(NORMAL_MODE); 
	
					// finish batch of vertices
//					p_tag[0] |= (1<<15) | SIZE(V4_32,16*2*NUM_LINES_PER_BATCH);
					NxPs2::vu1::Loc += _SIZE(V4_32,16*2*NUM_LINES_PER_BATCH) * 2 + 1;
	
					p_loc[1] = _PARSER;
					NxPs2::vu1::Buffer = NxPs2::vu1::Loc;
	
					p_loc = &p_loc[2];

					// Fill color regardless.
					uint32 col0 = *((uint32 *)&m_rain_bottom_color); 
					uint32 col1 = *((uint32 *)&m_rain_top_color);    
//					for ( int lp = 0; lp < ( NUM_LINES_PER_BATCH / 4 ); lp++ )
//					{
//						p_col[0] = col0;
//						p_col[1] = col1;
//
//						p_col[2] = col0;
//						p_col[3] = col1;
//					
//						p_col[4] = col0;
//						p_col[5] = col1;
//					
//						p_col[6] = col0;
//						p_col[7] = col1;
//
//						p_col += 8;
//					}

					for ( int lp = 0; lp < NUM_LINES_PER_BATCH; lp++ )
					{
						p_col[0] = col0;
						p_col[1] = col1;

						p_col += 2;
					}
				}

//				p_col[0] = col0;
//				p_col[1] = col1;

				p_xyz[0] = v0x;
				p_xyz[1] = v0y;
				p_xyz[2] = v0z;
				p_xyz[3] = 0;
				p_xyz[4] = v0x;
				p_xyz[5] = v1y;
				p_xyz[6] = v0z;
				p_xyz[7] = 0;

//				p_col += 2;
				p_xyz += 8;

				lines --;
#endif		// DEBUG_LINES
			}
		}
	}
//	printf( "Drop comparison: Counted: %d Calculated: %d\n", drops, m_active_drops );

	// Fix the last packet.
	if ( lines == NUM_LINES_PER_BATCH )
	{
		NxPs2::vu1::Loc -= _SIZE(V4_32,16*2*NUM_LINES_PER_BATCH) * 2 + 1;
		p_loc = p_last_loc;
	}
	else if ( lines )
	{
		// Fill in with degenerate lines.
		for ( int l = 0; l < lines; l++ )
		{
			p_xyz[0] = 0;
			p_xyz[1] = 0;
			p_xyz[2] = 0;
			p_xyz[3] = 0;
			p_xyz[4] = 0;
			p_xyz[5] = 0;
			p_xyz[6] = 0;
			p_xyz[7] = 0;

			p_xyz += 8;
		}
	}

#ifdef DEBUG_LINES
	NxPs2::EndLines3D();
#else
	NxPs2::dma::pLoc = (uint8*)p_loc;
	NxPs2::dma::EndTag();





#endif		// DEBUG_LINES
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2Weather::plat_render_splashes( float skx, float skz )
{
	// Create a new splash if required.
	float last = m_splash_rate;
	m_splash_rate += m_splash_per_frame;
	int new_splashes = (int)m_splash_rate - (int)last;

	if ( new_splashes )
	{
		CFeeler feeler;
		Mth::Vector pos;

		pos[X] = NxPs2::render::CameraToWorld.GetPos()[X];	// + ( Mth::Rnd( ( WEATHER_CEL_SIZE * RENDER_DIST * 2 ) ) ) - ( WEATHER_CEL_SIZE * RENDER_DIST );
		pos[Y] = FEELER_START;
		pos[Z] = NxPs2::render::CameraToWorld.GetPos()[Z];	// + ( Mth::Rnd( ( WEATHER_CEL_SIZE * RENDER_DIST * 2 ) ) ) - ( WEATHER_CEL_SIZE * RENDER_DIST ); 
		pos[W] = 1.0f;

		Mth::Vector dir;
		dir[X] = skx - NxPs2::render::CameraToWorld.GetPos()[X];
		dir[Y] = 0.0f;
		dir[Z] = skz - NxPs2::render::CameraToWorld.GetPos()[Z];
		dir[W] = 1.0f;
		dir.Normalize();

		// Add distance.
		float r1 = (float)Mth::Rnd( 32768 ) / 32768.0f;
		pos += ( dir * ( (float)WEATHER_CEL_SIZE * RENDER_DIST * r1 ) );

		// Add lateral.
		Mth::Vector lat;
		lat[X] = dir[Z];
		lat[Y] = 0.0f;
		lat[Z] = -dir[X];
		lat[W] = 1.0f;

		float r2 = 1.0f - ( (float)Mth::Rnd( 32768 ) / 32768.0f );
		if ( m_current_splash & 1 )
		{
			pos += ( lat * ( (float)WEATHER_CEL_SIZE * RENDER_DIST * r1 * r2 ) );
		}
		else
		{
			pos += ( lat * ( (float)WEATHER_CEL_SIZE * RENDER_DIST * r1 * -r2 ) );
		}

		feeler.SetStart( pos );
		pos[Y] = -FEELER_START;
		feeler.SetEnd( pos );

		// Get the y position.
		float y;
		if ( feeler.GetCollision( false, false ) )		// No movables, nearest collision.
		{
			y = feeler.GetPoint()[Y];
		}
		else
		{
			y = FEELER_START;
		}

		m_splash_x[m_current_splash] = pos[X];
		m_splash_y[m_current_splash] = y + m_splash_size;
		m_splash_z[m_current_splash] = pos[Z];
		m_splash_current_life[m_current_splash] = m_splash_life;

		m_current_splash++;
		m_current_splash %= NUM_SPLASH_ACTIVE;
	}
	
	// Count active splashes.
	int splashes = 0;
	for ( int spl = 0; spl < NUM_SPLASH_ACTIVE; spl++ )
	{
		if ( m_splash_current_life[spl] != 0 ) splashes++;
	}

	if ( splashes )
	{
		// add a dma packet
		NxPs2::dma::BeginTag(NxPs2::dma::cnt, 0);

		// VU context
		NxPs2::vu1::BeginPrim(ABS, VU1_ADDR(L_VF10));
		NxPs2::vu1::StoreVec(*(NxPs2::Vec *)&NxPs2::render::InverseIntViewportScale);
		NxPs2::vu1::StoreVec(*(NxPs2::Vec *)&NxPs2::render::InverseIntViewportOffset);
		NxPs2::vu1::StoreMat(*(NxPs2::Mat *)&NxPs2::render::WorldToIntViewport);	// VF12-15
		NxPs2::vu1::EndPrim(0);
		NxPs2::vu1::BeginPrim(ABS, VU1_ADDR(L_VF30));
		NxPs2::vif::StoreV4_32F(640.0f, 480.0f, 0.0f, 0.0f);						// VF30
		NxPs2::vif::StoreV4_32F(NxPs2::render::IntViewportScale[0]/NxPs2::render::Tx,	// VF31
								NxPs2::render::IntViewportScale[1]/NxPs2::render::Ty,
								0.0f, 0.0f);
		NxPs2::vu1::EndPrim(0);

		// GS context
		NxPs2::gs::BeginPrim(ABS, 0, 0);
		NxPs2::gs::Reg1(NxPs2::gs::ALPHA_1,	m_splash_blend);
		NxPs2::gs::Reg1(NxPs2::gs::TEX0_1,	mp_rain_texture->m_RegTEX0);
		NxPs2::gs::Reg1(NxPs2::gs::TEX1_1,	mp_rain_texture->m_RegTEX1);
		NxPs2::gs::Reg1(NxPs2::gs::ST,		PackST(0x3F800000,0x3F800000));
		NxPs2::gs::Reg1(NxPs2::gs::RGBAQ,	PackRGBAQ(0,0,0,0,0x3F800000));
		NxPs2::gs::EndPrim(0);

		// Render the splashes.
	//	int u = mp_rain_texture->GetWidth() << 4;
	//	int v = mp_rain_texture->GetHeight() << 4;
	//	uint32 col = *((uint32 *)&m_splash_color); 

		NxPs2::BeginModelPrimImmediate(NxPs2::gs::XYZ2		|
									   NxPs2::gs::ST<<4		|
									   NxPs2::gs::RGBAQ<<8	|
									   NxPs2::gs::XYZ2<<12,
									   4, SPRITE|ABE|TME, 1, VU1_ADDR(SpriteCull));

		// create an unpack for the colours
		NxPs2::vif::BeginUNPACK(0, V4_8, ABS, UNSIGNED, 3);
		uint32 * p_col = (uint32 *)NxPs2::dma::pLoc;
		NxPs2::dma::pLoc += splashes * 4;
		NxPs2::vif::EndUNPACK();

		// and one for the positions (& sizes)
		NxPs2::vif::BeginUNPACK(0, V4_32, ABS, SIGNED, 4);
		float * p_xyz = (float *)NxPs2::dma::pLoc;
		NxPs2::dma::pLoc += splashes * 16;
		NxPs2::vif::EndUNPACK();

		NxPs2::EndModelPrimImmediate(1);

		NxPs2::vif::MSCAL(VU1_ADDR(Parser));

		for ( int spl = 0; spl < NUM_SPLASH_ACTIVE; spl++ )
		{
			if ( m_splash_current_life[spl] == 0 ) continue;

			// Interpolate color.
			Image::RGBA	icol;
			icol.r = (uint8)( ( (int)m_splash_color.r * m_splash_current_life[spl] ) / m_splash_life );
			icol.g = (uint8)( ( (int)m_splash_color.g * m_splash_current_life[spl] ) / m_splash_life );
			icol.b = (uint8)( ( (int)m_splash_color.b * m_splash_current_life[spl] ) / m_splash_life );
			icol.a = (uint8)( ( (int)m_splash_color.a * m_splash_current_life[spl] ) / m_splash_life );

			// Draw splashes...
			*p_col++ = *((uint32 *)&icol);

			p_xyz[0] = m_splash_x[spl];
			p_xyz[1] = m_splash_y[spl];
			p_xyz[2] = m_splash_z[spl];
			p_xyz[3] = m_splash_size;
			p_xyz += 4;

			m_splash_current_life[spl]--;
		}

		// restore transform
		NxPs2::vu1::BeginPrim(ABS, VU1_ADDR(L_VF12));		// was L_VF16
		NxPs2::vu1::StoreMat(*(NxPs2::Mat *)&NxPs2::render::AdjustedWorldToViewport);		// VF16-19
		NxPs2::vu1::EndPrim(1);
		NxPs2::vif::MSCAL(VU1_ADDR(Parser));

		// end the dma tag
		NxPs2::dma::EndTag();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CPs2Weather::plat_render( void )
{
	if ( !m_system_active ) return;

	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = pSkate->GetSkater(0);
	if (!pSkater) return;
	
	float skx = pSkater->m_pos[X];
	float skz = pSkater->m_pos[Z];

	if ( m_raining )
	{
		plat_render_splashes( skx, skz );
		plat_render_rain( skx, skz );
	}
	else
	{
		plat_render_snow( skx, skz );
	}

	// Increment rain drop/snow flake positions.
	if ( m_active_drops )
	{
		for ( int lp = 0; lp < ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ); lp++ )
		{
			switch ( m_drop_time[lp] )
			{
				case 255:
					// Inactive.
					break;
				case 254:
					// About to become inactive, so decrement active drops.
					m_active_drops--;
					// Deliberately falls through...
				default:
					// Increment time.
					m_drop_time[lp]++;
					break;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CPs2Weather::plat_set_rain_blend_mode( uint32 blendmode_checksum, int fix )
{
	m_rain_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CPs2Weather::plat_set_splash_blend_mode( uint32 blendmode_checksum, int fix )
{
	m_splash_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CPs2Weather::plat_set_snow_blend_mode( uint32 blendmode_checksum, int fix )
{
	m_snow_blend = NxPs2::CImmediateMode::sGetTextureBlend( blendmode_checksum, fix );
}

} // Nx






================================================
FILE: Code/Gfx/NGPS/p_nxweather.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_WEATHER_H__
#define	__GFX_P_NX_WEATHER_H__
    
#include "gfx/nxweather.h"

namespace Nx
{

//#define WEATHER_GRID_W 65
//#define WEATHER_GRID_H 65
//
//#define WEATHER_CEL_W 32
//#define WEATHER_CEL_H 32

#define WEATHER_CEL_SIZE 128
#define WEATHER_CEL_SIZE_SHIFT 7
#define HEIGHT_TOLERANCE (sint32)( 30.0f * SUB_INCH_PRECISION )
#define DROP_SIZE 16
#define DROP_SIZE_SHIFT 4
#define DROP_LAYERS 4

#define NUM_SPLASH_ACTIVE 32

typedef struct
{
	unsigned short start;
	unsigned short end;
	unsigned short index;
} sRowEntry;

class CPs2Weather : public CWeather
{
public:
							CPs2Weather();
	virtual 				~CPs2Weather();

private:					// It's all private, as it is machine specific
	void					plat_update_grid( void );
	void					plat_process( float delta_time );
	void					plat_render( void );
	void					plat_set_rain_blend_mode( uint32 blendmode_checksum, int fix );
	void					plat_set_splash_blend_mode( uint32 blendmode_checksum, int fix );
	void					plat_set_snow_blend_mode( uint32 blendmode_checksum, int fix );

	void					plat_render_splashes( float skx, float skz );
	void					plat_render_rain( float skx, float skz );
	void					plat_render_snow( float skx, float skz );

	NxPs2::SSingleTexture*	mp_rain_texture;
	NxPs2::SSingleTexture*	mp_snow_texture;

	unsigned char *			mp_roof_height_index;
	sRowEntry *				mp_roof_row;
	sint32					m_roof_height[256];
	int						m_width;
	int						m_height;

	float					m_min_x;
	float					m_min_z;

	float					m_rain_rate;
	float					m_splash_rate;
	float					m_snow_rate;

	Mth::Vector				m_grid_base;

	unsigned char			m_drop_time[DROP_SIZE*DROP_SIZE*DROP_LAYERS];
	unsigned char			m_x_offset[DROP_SIZE*DROP_SIZE*DROP_LAYERS];
	unsigned char			m_z_offset[DROP_SIZE*DROP_SIZE*DROP_LAYERS];

	uint32					m_seq;

	uint64					m_rain_blend;
	uint64					m_splash_blend;
	uint64					m_snow_blend;

	float					m_splash_x[NUM_SPLASH_ACTIVE];
	float					m_splash_y[NUM_SPLASH_ACTIVE];
	float					m_splash_z[NUM_SPLASH_ACTIVE];
	int						m_splash_current_life[NUM_SPLASH_ACTIVE]; 
	int						m_current_splash;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif



================================================
FILE: Code/Gfx/NxAnimCache.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxAnimCache.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  5/06/2002
//****************************************************************************

#include 

#include 

#include 
#include 

#include 
#include 

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions are provided here:
// so engine implementors can leave certain functionality until later
// (Mick Perforce: added line) 
						
/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

// These functions are the platform independent part of the interface to 
// the platform specific code
// parameter checking can go here....
// although we might just want to have these functions inline, or not have them at all?

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::CBonedAnimFrameData* GetCachedAnim( uint32 animChecksum, bool assertOnFail )
{
	if ( animChecksum == 0 )
	{
		return NULL;
	}

	Ass::CAssMan* ass_man = Ass::CAssMan::Instance();
	return (Gfx::CBonedAnimFrameData*)ass_man->GetAsset( animChecksum, assertOnFail );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx


================================================
FILE: Code/Gfx/NxAnimCache.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxAnimCache.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  5/06/2002
//****************************************************************************

#ifndef	__GFX_NXANIMCACHE_H__
#define	__GFX_NXANIMCACHE_H__
                   
#include 
#include 

namespace Gfx
{
	class CBonedAnimFrameData;
};

namespace Nx
{

Gfx::CBonedAnimFrameData* GetCachedAnim( uint32 animChecksum, bool assertOnFail = true );

}

#endif // __GFX_NXANIMCACHE_H__


================================================
FILE: Code/Gfx/NxFont.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxFont.cpp

#include "gfx/NxFont.h"

namespace	Nx
{
// These functions are the platform independent part of the interface to 
// the platform specific code
// parameter checking can go here....
// although we might just want to have these functions inline, or not have them at all?
					 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CFont::CFont()
{
	m_checksum = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CFont::~CFont()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CFont::Load(const char *filename)
{
	return plat_load(filename);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFont::SetSpacings(int charSpacing, int spaceSpacing)
{
	plat_set_spacings(charSpacing, spaceSpacing);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFont::SetRGBATable(Image::RGBA *pTab)
{
	Dbg_Assert(pTab);
	plat_set_rgba_table(pTab);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFont::MarkAsButtonFont(bool isButton)
{
	plat_mark_as_button_font(isButton);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CFont::Unload()
{
	plat_unload();

	m_checksum = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CFont::GetChecksum() const
{
	return m_checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CFont::GetDefaultHeight() const
{
	return plat_get_default_height();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CFont::GetDefaultBase() const
{
	return plat_get_default_base();
}

#if 0
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CFont::BeginText(uint32 rgba, float Scale)
{
	plat_begin_text(rgba, Scale);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CFont::DrawString(char *String, float x0, float y0)
{
	plat_draw_string(String, x0, y0);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CFont::EndText(void)
{
	plat_end_text();
}
#endif // 0

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CFont::QueryString(char *String, float &width, float &height) const
{
	plat_query_string(String, width, height);
}

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions are provided here:
// so engine implementors can leave certain functionality until later
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CFont::plat_load(const char *filename)
{
	printf ("STUB: PlatLoad\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFont::plat_set_spacings(int charSpacing, int spaceSpacing)
{
	printf("STUB A DUB DUB");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFont::plat_set_rgba_table(Image::RGBA *pTab)
{
	printf("STUB A DUB DUB");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFont::plat_mark_as_button_font(bool isButton)
{
	printf("STUB A DUB DUB");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CFont::plat_unload()
{
	printf ("STUB: PlatUnload\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CFont::plat_get_default_height() const
{
	printf ("STUB: PlatGetDefaultHeight\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CFont::plat_get_default_base() const
{
	printf ("STUB: PlatGetDefaultBase\n");
	return 0;
}

#if 0
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CFont::plat_begin_text(uint32 rgba, float Scale)
{
	Dbg_Assert(0);	// Don't call anymore
	printf ("STUB: PlatBeginText\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CFont::plat_draw_string(char *String, float x0, float y0)
{
	Dbg_Assert(0);	// Don't call anymore
	printf ("STUB: PlatDrawString\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CFont::plat_end_text(void)
{
	Dbg_Assert(0);	// Don't call anymore
	printf ("STUB: PlatEndText\n");
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CFont::plat_query_string(char *String, float &width, float &height) const
{
	printf ("STUB: PlatQueryString\n");
}

///////////////////////////////////////////////////////////////////////////////
// CText

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CText::CText(CWindow2D *p_window) : 
	mp_font(NULL),
	m_xpos(0.0f),
	m_ypos(0.0f),
	m_xscale(1.0f),
	m_yscale(1.0f),
	m_priority(0.0f),
	m_rgba(128, 128, 128, 128),
	m_color_override(false),
	m_hidden(true),
	m_use_zbuffer(false),
	mp_window(p_window),
	mp_next(NULL)
{
	#if						__STATIC_FONT_STRINGS__
	m_string[0] = '\0';
	#else
	m_string = NULL;
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CText::~CText() 
{
	#if						__STATIC_FONT_STRINGS__
	#else
	if (m_string)
	{
		delete [] m_string;
	}
	#endif	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::SetHidden(bool hide)
{
	m_hidden = hide;

	plat_update_hidden();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::SetFont(CFont *p_font)
{
	mp_font = p_font;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::SetString(const char *p_string)
{
	Dbg_MsgAssert(strlen(p_string) < vMAX_TEXT_CHARS, ("CText: string too long %d", strlen(p_string)));

	#if						__STATIC_FONT_STRINGS__
	strcpy(m_string, p_string);
	#else
	if (!m_string)
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		m_string = new char[vMAX_TEXT_CHARS];
		Mem::Manager::sHandle().PopContext();
	}
	strcpy(m_string, p_string);
	#endif

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::ClearString()
{
	#if						__STATIC_FONT_STRINGS__
	#else
	if (m_string)
	{
		delete m_string;
		m_string = NULL;
	}
	#endif
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::SetPos(float x, float y)
{
	m_xpos = x;
	m_ypos = y;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::SetScale(float scale_x, float scale_y)
{
	m_xscale = scale_x;
	m_yscale = scale_y;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::SetPriority(float pri)
{
	m_priority = pri;
	m_use_zbuffer = false;

	plat_update_priority();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::SetZValue(ZBufferValue z)
{
	m_zvalue = z;
	m_use_zbuffer = true;

	plat_update_priority();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::SetRGBA(Image::RGBA rgba, bool colorOverride)
{
	m_rgba = rgba;
	m_color_override = colorOverride;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::SetWindow(CWindow2D *p_window)
{
	mp_window = p_window;

	plat_update_window();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::plat_initialize()
{
	printf ("STUB: PlatInitialize\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::plat_update_hidden()
{
	printf ("STUB: PlatUpdateHidden\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::plat_update_engine()
{
	printf ("STUB: PlatUpdateEngine\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::plat_update_priority()
{
	printf ("STUB: PlatUpdatePriority\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CText::plat_update_window()
{
	printf ("STUB: PlatUpdateWindow\n");
}

///////////////////////////////////////////////////////////////////////////////
// CTextMan

CText *			CTextMan::sp_dynamic_text_list = NULL;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CTextMan::sAllocTextPool()
{
	s_plat_alloc_text_pool();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CText *			CTextMan::sGetTextInstance()
{
	CText *p_text = sp_dynamic_text_list;

	if (p_text)
	{
		sp_dynamic_text_list = p_text->mp_next;
		//p_text->AddToDrawList();
	} else {
		Dbg_MsgAssert(0, ("Out of CText Instances"));
	}

	return p_text;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CTextMan::sFreeTextInstance(CText *p_text)
{
	//p_text->RemoveFromDrawList();
	p_text->SetHidden(true);

	// Clear text, otherwise it hangs around
	// it would get re-used, but it's fragmenting memory	
	p_text->ClearString();

	p_text->mp_next = sp_dynamic_text_list;
	sp_dynamic_text_list = p_text;
}

}




================================================
FILE: Code/Gfx/NxFont.h
================================================
///////////////////////////////////////////////////////////////////////////////
// NxFont.h



#ifndef	__GFX_NXFONT_H__
#define	__GFX_NXFONT_H__

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#define		__STATIC_FONT_STRINGS__ 0			// set to 1 to have the old statically allocated strings

namespace Nx
{

// Forward declarations
class CTextMan;
class CWindow2D;

//////////////////////////////////////////////////////////////////////////////
// The CFont class is the platform independent abstract base class
// of the platform specific CFont classes
class	CFont
{
public:
							CFont();
	virtual					~CFont();

	bool					Load(const char *filename);
	void					SetSpacings(int charSpacing, int spaceSpacing);
	void					SetRGBATable(Image::RGBA *pTab);
	void					MarkAsButtonFont(bool isButton);
	void					Unload();

	uint32					GetChecksum() const;
	uint32					GetDefaultHeight() const;
	uint32					GetDefaultBase() const;
	void					QueryString(char *String, float &width, float &height) const;

	// Member variables (protected so the p-classes can access them)
protected:
	uint32					m_checksum;

	// The virtual functions have a stub implementation.
private:
	virtual	bool			plat_load(const char *filename);
	virtual void			plat_set_spacings(int charSpacing, int spaceSpacing);
	virtual void			plat_set_rgba_table(Image::RGBA *pTab);
	virtual void 			plat_mark_as_button_font(bool isButton);
	virtual	void			plat_unload();

	virtual	uint32			plat_get_default_height() const;
	virtual	uint32			plat_get_default_base() const;
	virtual void			plat_query_string(char *String, float &width, float &height) const;
};

//////////////////////////////////////////////////////////////////////////////
// Holds a string of text that needs to be displayed this frame
class CText
{
public:
							CText(CWindow2D *p_window = NULL);
	virtual					~CText();

	// If hidden, it won't be drawn that frame
	void					SetHidden(bool hide);
	bool					IsHidden() const;

	// Font used for the text.
	CFont *					GetFont() const;
	void					SetFont(CFont *p_font);

	// Actual text
	const char *			GetString() const;
	void					SetString(const char *p_string);
	void					ClearString();

	// Position on screen.  Assumes screen size of 640x448.
	float					GetXPos() const;
	float					GetYPos() const;
	void					SetPos(float x, float y);

	// Scale of text.
	float					GetXScale() const;
	float					GetYScale() const;
	void					SetScale(float scale_x, float scale_y);

	// Priority of text.  Higher priority text are
	// drawn on top of lower priority text.
	float					GetPriority() const;
	void					SetPriority(float pri);
	Nx::ZBufferValue		GetZValue() const;
	void					SetZValue(Nx::ZBufferValue z);

	// Color of text.
	Image::RGBA				GetRGBA() const;
	void					SetRGBA(Image::RGBA rgba, bool colorOverride);

	// Clipping window
	CWindow2D *				GetWindow() const;
	void					SetWindow(CWindow2D *p_window);

protected:
	// Constants
	enum {
		vMAX_TEXT_CHARS = 96
	};

	CFont *					mp_font;
	#if						__STATIC_FONT_STRINGS__
	char					m_string[vMAX_TEXT_CHARS];
	#else
	char *					m_string;
	#endif
	float					m_xpos;
	float					m_ypos;
	float					m_xscale;
	float					m_yscale;
	float					m_priority;
	ZBufferValue			m_zvalue;		  // for zbuffer sort
	Image::RGBA				m_rgba;
	bool					m_color_override; // colors encoded in text will be overridden if true

	bool					m_hidden;
	bool					m_use_zbuffer;

	CWindow2D *				mp_window;

	// For free list in CTextMan
	CText *					mp_next;

private:
	//
	virtual void			plat_initialize();

	virtual void			plat_update_hidden();		// Tell engine of update
	virtual void			plat_update_engine();		// Update engine primitives
	virtual void			plat_update_priority();
	virtual void			plat_update_window();

	friend CTextMan;

};

//////////////////////////////////////////////////////////////////////////////
// Static class for memory management of CTexts
class CTextMan
{
public:
	//
	static void				sAllocTextPool();
	static CText *			sGetTextInstance();
	static void				sFreeTextInstance(CText *p_text);

private:
	// Constants
	enum {
		vMAX_TEXT_INSTANCES = 512
	};

	// Because it is static, it is declared here, but defined in p_NxFont.cpp
	static void				s_plat_alloc_text_pool();
	static CText *			s_plat_get_text_instance();
	static void				s_plat_free_text_instance(CText *p_text);

	// Array of Text requests
	static CText *			sp_dynamic_text_list;
};

/////////////////////////////////////////////////////////
// CText inline function
inline bool					CText::IsHidden() const
{
	return m_hidden;
}

inline CFont *				CText::GetFont() const
{
	return mp_font;
}

inline float				CText::GetXPos() const
{
	return m_xpos;
}

inline float				CText::GetYPos() const
{
	return m_ypos;
}

inline float				CText::GetXScale() const
{
	return m_xscale;
}

inline float				CText::GetYScale() const
{
	return m_yscale;
}

inline float				CText::GetPriority() const
{
	return m_priority;
}

inline ZBufferValue			CText::GetZValue() const
{
	return m_zvalue;
}

inline Image::RGBA			CText::GetRGBA() const
{
	return m_rgba;
}

inline CWindow2D *			CText::GetWindow() const
{
	return mp_window;
}

}

#endif // 



================================================
FILE: Code/Gfx/NxFontMan.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Nx
{


CFontManager::FontEntry 				CFontManager::s_font_tab[vMAX_FONT_ENTRIES];
Lst::HashTable	CFontManager::s_font_lookup(4);
char									CFontManager::s_meta_button_map[NUM_META_BUTTON_ENTRIES];
bool									CFontManager::s_meta_button_map_initialized = false;


void CFontManager::sLoadFont(const char *pName, int charSpacing, int spaceSpacing, Image::RGBA *pColorTab, bool isButtonFont)
{
	Dbg_Assert(strlen(pName) < FontEntry::vMAX_NAME_SIZE);

	// See if this font is already loaded.
	if ( sGetFont( pName ) )
	{
		sGetFont( pName )->SetSpacings(charSpacing, spaceSpacing); 
		if (pColorTab) sGetFont( pName )->SetRGBATable(pColorTab); 
		sGetFont( pName )->MarkAsButtonFont(isButtonFont); 
		return;
	}

	for (int i = 0; i < vMAX_FONT_ENTRIES; i++)
	{
		if (!s_font_tab[i].mp_font)
		{
			s_font_tab[i].mp_font = s_plat_load_font(pName);
			s_font_tab[i].mp_font->SetSpacings(charSpacing, spaceSpacing);
			if (pColorTab)
				s_font_tab[i].mp_font->SetRGBATable(pColorTab);
			s_font_tab[i].mp_font->MarkAsButtonFont(isButtonFont);
						
			strcpy(s_font_tab[i].mName, pName);
			s_font_lookup.PutItem(Script::GenerateCRC(pName), &s_font_tab[i]);
			break;
		}
	}

	if (!s_meta_button_map_initialized)
	{
		#ifdef __PLAT_XBOX__
		Script::CArray *p_array = Script::GetArray("meta_button_map_xbox", Script::ASSERT);
		#else
		#ifdef __PLAT_NGC__
		Script::CArray *p_array = Script::GetArray("meta_button_map_gamecube");
		#else
		Script::CArray *p_array = Script::GetArray("meta_button_map_ps2", Script::ASSERT);
		#endif
		#endif
		
		if ( p_array )
		{
			for (uint i = 0; i < NUM_META_BUTTON_ENTRIES; i++)
			{
				int index = (i < p_array->GetSize()) ? p_array->GetInteger(i) : 0;

				if (index <= 9)
					s_meta_button_map[i] = '0' + index;
				else
					s_meta_button_map[i] = 'a' + index - 10;
			}

			s_meta_button_map_initialized = true;
		}
	}
}




void CFontManager::sUnloadFont(const char *pName)
{
	for (int i = 0; i < vMAX_FONT_ENTRIES; i++)
	{
		if (strcmp(s_font_tab[i].mName, pName) == 0)
		{
			s_font_lookup.FlushItem(Script::GenerateCRC(pName));
			
			s_plat_unload_font(s_font_tab[i].mp_font);
			s_font_tab[i].mp_font = NULL;
			
			break;
		}
	}
}


// returns pointer to font if loaded, NULL if not
Nx::CFont *CFontManager::sGetFont(uint32 checksum)
{
	FontEntry *p_entry = s_font_lookup.GetItem(checksum);
	if (p_entry)
		return p_entry->mp_font;
	else
		return NULL;
}

// returns pointer to font if loaded, NULL if not
Nx::CFont *CFontManager::sGetFont(const char *pName)
{
	return sGetFont(Script::GenerateCRC(pName));
}

// returns pointer to name if loaded, NULL if not
const char *CFontManager::sTestFontLoaded(uint32 checksum)
{
	FontEntry *p_entry = s_font_lookup.GetItem(checksum);
	if (p_entry)
		return p_entry->mName;
	else
		return NULL;
}




char CFontManager::sMapMetaCharacterToButton(const char *pMetaChar)
{
	Dbg_MsgAssert(s_meta_button_map_initialized, ("meta button character table not initialized"));
	return s_meta_button_map[Str::DehexifyDigit(pMetaChar)];
}




bool ScriptLoadFont(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	bool is_buttons_font = pParams->ContainsFlag("buttons_font");
	
	const char *p_name;
	
	
	if (!pParams->GetText(NONAME, &p_name) && !is_buttons_font)
		Dbg_MsgAssert(0, ("no font specified"));
	
	
	if (is_buttons_font)
	{
		#ifdef __PLAT_XBOX__
		p_name = "ButtonsXbox";
		#else
			#ifdef __PLAT_NGC__
			p_name = "ButtonsNgc";
			#else
			p_name = "ButtonsPs2";
			#endif
		#endif
	}

	Mem::PushMemProfile((char*)p_name);
	
	float char_spacing = 0;
	pParams->GetFloat("char_spacing", &char_spacing);
	
	float space_spacing = 0;
	pParams->GetFloat("space_spacing", &space_spacing);
	
	Script::CArray *p_script_col_tab;

   	// 15Jan02 JCB - Make this local to avoid fragmentation when allocating from the heap.
	Image::RGBA rgba_tab[16];
	Image::RGBA* p_rgba_tab = &rgba_tab[0];

	if (pParams->GetArray("color_tab", &p_script_col_tab))
	{
		for (int i = 0; i < (int) p_script_col_tab->GetSize() && i < 16; i++)
		{
			Script::CArray *p_entry = p_script_col_tab->GetArray(i);
			uint32 rgba = 0;
			int size = p_entry->GetSize();
			Dbg_MsgAssert(size >= 3 && size <= 4, ("wrong size %d for color array", size));
			for (int j = 0; j < size; j++) 
			{
				rgba |= (p_entry->GetInteger(j) & 255) << (j*8);
			}
			p_rgba_tab[i] = *((Image::RGBA *) &rgba);
		}
	}
	
	Nx::CFontManager::sLoadFont(p_name, (int) char_spacing, (int) space_spacing, p_rgba_tab, is_buttons_font);

	Mem::PopMemProfile();		

	return true;
}




bool ScriptUnloadFont(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	const char *p_name;
	if (!pParams->GetText(NONAME, &p_name))
		Dbg_MsgAssert(0, ("no font specified"));
	
	Nx::CFontManager::sUnloadFont(p_name);
	return true;
}




}



================================================
FILE: Code/Gfx/NxFontMan.h
================================================
#ifndef __GFX_2D_FONTMAN_H__
#define __GFX_2D_FONTMAN_H__

#include 

#include 

namespace Script
{
	class CScriptStructure;
	class CScript;
}

namespace Nx
{

class CFontManager
{
public:
	static void					sLoadFont(const char *pName, int charSpacing = 0, 
										  int spaceSpacing = 0, Image::RGBA *pColorTab = NULL,
										  bool isButtonFont = false);
	static void					sUnloadFont(const char *pName);
	static Nx::CFont *			sGetFont(const char *pName);
	static Nx::CFont *			sGetFont(uint32 checksum);

	static const char *			sTestFontLoaded(uint32 checksum);
	
	static char					sMapMetaCharacterToButton(const char *pMetaChar);

private:
	// Constants
	enum {
		vMAX_FONT_ENTRIES 		= 16,
		NUM_META_BUTTON_ENTRIES = 32,
	};


	struct FontEntry
	{
		enum {
			vMAX_NAME_SIZE = 24
		};

								FontEntry() : mp_font(NULL) { }

		char					mName[vMAX_NAME_SIZE];
		Nx::CFont *				mp_font;
	};
	
	static FontEntry 					s_font_tab[vMAX_FONT_ENTRIES];
	static Lst::HashTable	s_font_lookup;

	static char					s_meta_button_map[NUM_META_BUTTON_ENTRIES];
	static bool					s_meta_button_map_initialized;
	
	// The platform dependent calls
	static Nx::CFont *			s_plat_load_font(const char *pName);
	static void					s_plat_unload_font(Nx::CFont *pFont);
};



bool ScriptLoadFont(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptUnloadFont(Script::CScriptStructure *pParams, Script::CScript *pScript);

}

#endif // FONTMAN


================================================
FILE: Code/Gfx/NxGeom.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxGeom.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/27/2002
//****************************************************************************

#include 

#include 
#include 

#include 
#include 
#include 

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions are provided here:
// so engine implementors can leave certain functionality until later
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom* 			CGeom::plat_clone(bool instance, CScene* pDestScene)
{
	printf ("STUB: PlatClone\n");
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom* 			CGeom::plat_clone(bool instance, CModel* pDestModel)
{
	printf ("STUB: PlatClone\n");
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CGeom::plat_load_geom_data(CMesh* pMesh, CModel* pModel, bool color_per_material)
{
	printf ("STUB: PlatLoadGeomData\n");
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_finalize()
{
	// do nothing...  this is only needed for the PS2 right now
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32			CGeom::plat_get_checksum()
{
	printf ("STUB: PlatGetChecksum\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_set_color(Image::RGBA rgba)
{
	printf ("STUB: PlatSetColor\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA		CGeom::plat_get_color() const
{
	printf ("STUB: PlatGetColor\n");
	return Image::RGBA(0, 0, 0, 0);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_clear_color()
{
	printf ("STUB: PlatClearColor\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CGeom::plat_set_material_color(uint32 mat_checksum, int pass, Image::RGBA rgba)
{
	printf ("STUB: PlatSetMaterialColor\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA		CGeom::plat_get_material_color(uint32 mat_checksum, int pass)
{
	printf ("STUB: PlatGetMaterialColor\n");
	return Image::RGBA(0, 0, 0, 0);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_set_visibility(uint32 mask)
{
	printf ("STUB: PlatSetVisibility\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32			CGeom::plat_get_visibility() const
{
	printf ("STUB: PlatGetVisibility\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_set_active(bool active)
{
	printf ("STUB: PlatSetActive\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CGeom::plat_is_active() const
{
	printf ("STUB: PlatIsActive\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::CBBox &	CGeom::plat_get_bounding_box() const
{
	static Mth::CBBox stub_bbox;
	printf ("STUB: PlatGetBoundingBox\n");
	return stub_bbox;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector CGeom::plat_get_bounding_sphere() const
{
	printf ("STUB: PlatGetBoundingSphere\n");
	return Mth::Vector( 0.0f, 0.0f, 0.0f, 10000.0f );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_set_world_position(const Mth::Vector& pos)
{
	printf ("STUB: PlatSetWorldPosition\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &	CGeom::plat_get_world_position() const
{
	static Mth::Vector stub_vec;
	printf ("STUB: PlatGetWorldPosition\n");
	return stub_vec;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_set_orientation(const Mth::Matrix& orient)
{
	printf ("STUB: PlatSetOrientation\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Matrix & CGeom::plat_get_orientation() const
{
	static Mth::Matrix stub_mat;
	printf ("STUB: PlatGetOrientation\n");
	return stub_mat;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 			CGeom::plat_rotate_y(Mth::ERot90 rot)
{
	printf ("STUB: PlatRotateY\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_set_transform(const Mth::Matrix& transform)
{
	printf ("STUB: PlatSetTransform\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Matrix &	CGeom::plat_get_transform() const
{
	static Mth::Matrix stub_mat;
	printf ("STUB: PlatGetTransform\n");
	return stub_mat;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_set_scale(const Mth::Vector& scale)
{
	printf ("STUB: PlatSetScale\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector		CGeom::plat_get_scale() const
{
	printf ("STUB: PlatGetScale\n");
	return Mth::Vector(0, 0, 0, 0);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_set_model_lights(CModelLights *p_model_lights)
{
	printf ("STUB: PlatSetModelLights\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CGeom::plat_render(Mth::Matrix* pRootMatrix, Mth::Matrix* ppBoneMatrices, int numBones)
{
	printf ("STUB: PlatRender\n");
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CGeom::plat_set_bounding_sphere( const Mth::Vector& boundingSphere )
{
	printf ("STUB: PlatSetBoundingSphere\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CGeom::plat_hide_polys( uint32 mask )
{
	printf ("STUB: PlatHidePolys\n");
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CGeom::plat_enable_shadow( bool enabled )
{
//	printf ("STUB: PlatEnableShadow\n");
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// - returns number of renderable verts in a C...Geom
// this should be the same number of verts as is returned/expected by the following functions 
int 		CGeom::plat_get_num_render_verts()								
{
	printf ("STUB: CGeom::plat_get_num_render_verts\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// - gets a single array of the verts positions as regular floating point Mth::Vectors
// p_verts points to memory supplied by the caller
void 		CGeom::plat_get_render_verts(Mth::Vector *p_verts)			
{
	printf ("STUB: CGeom::plat_get_render_verts\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int 		CGeom::plat_get_num_render_polys()								
{
	printf ("STUB: CGeom::plat_get_num_render_polys\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int 		CGeom::plat_get_num_render_base_polys()								
{
	printf ("STUB: CGeom::plat_get_num_render_base_polys\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// - gets a single array of the verts colors as RGBA
// p_colors points to memory supplied by the caller
void 		CGeom::plat_get_render_colors(Image::RGBA *p_colors)		// - gets an array of vertex colors
{
	printf ("STUB: CGeom::plat_get_render_colors\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// - sets the verts after modification
void 		CGeom::plat_set_render_verts(Mth::Vector *p_verts)			  
{
	printf ("STUB: CGeom::plat_set_render_verts\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// - sets the colors after modification
void 		CGeom::plat_set_render_colors(Image::RGBA *p_colors)				
{
	printf ("STUB: CGeom::plat_set_render_colors\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void		CGeom::plat_set_bone_matrix_data( Mth::Matrix* pBoneMatrices, int numBones )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CGeom::plat_set_uv_wibble_params(float u_vel, float u_amp, float u_freq, float u_phase,
											 float v_vel, float v_amp, float v_freq, float v_phase)
{
	printf ("STUB: CGeom::plat_set_uv_wibble_params\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CGeom::plat_use_explicit_uv_wibble(bool yes)
{
	printf ("STUB: CGeom::plat_use_explicit_uv_wibble\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CGeom::plat_set_uv_wibble_offsets(float u_offset, float v_offset)
{
	printf ("STUB: CGeom::plat_set_uv_wibble_offsets\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CGeom::plat_set_uv_wibble_offsets(uint32 mat_checksum, int pass, float u_offset, float v_offset)
{
	printf ("STUB: CGeom::plat_set_uv_wibble_offsets\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CGeom::plat_set_uv_matrix(uint32 mat_checksum, int pass, const Mth::Matrix &mat)
{
	printf ("STUB: CGeom::plat_set_uv_matrix\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CGeom::plat_allocate_uv_matrix_params(uint32 mat_checksum, int pass)
{
	// only needed by the xbox right now...

	return true;
}

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

// These functions are the platform independent part of the interface to 
// the platform specific code
// parameter checking can go here....
// although we might just want to have these functions inline, or not have them at all?

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom::CGeom()
{
	m_cloned = vORIGINAL;
	mp_coll_tri_data = NULL;
	mp_orig_render_colors = NULL;
	m_multipleColorsEnabled = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom::~CGeom()
{
	if (m_cloned != vORIGINAL)			// delete cloned data
	{
		if (mp_coll_tri_data)
		{
			// Right now, cloning of collision isn't handled at the CGeom level
			//delete mp_coll_tri_data;
		}
	}
	
	if (mp_orig_render_colors)
	{
		delete mp_orig_render_colors;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom*  CGeom::Clone(bool instance, CScene* pDestScene)
{
#ifdef __PLAT_NGPS__
	// Garrett: Want to be able to disable copy just in case it opens up crashes on the Park Editor
	if (Script::GetInt( "disable_clone_copy", false ))
	{
		instance = true;
	}
#endif

	// Create new instance and clone stuff below p-line
	CGeom* p_new_geom = plat_clone(instance, pDestScene);

	if (p_new_geom)
	{
		p_new_geom->m_cloned = (instance) ? vINSTANCE : vCOPY;

		if (mp_coll_tri_data)
		{
#if 1
			// Garrett: Just copying the pointer now, since the CGeom version is just used for reference
			p_new_geom->mp_coll_tri_data = mp_coll_tri_data;
#else
			// Make NULL for now until we can figure out a better way of cloning collision with CSector
			p_new_geom->mp_coll_tri_data = NULL;
			//p_new_geom->mp_coll_tri_data = mp_coll_tri_data->Clone(instance);
			//Dbg_Assert(p_new_geom->mp_coll_tri_data);
#endif
		}
		// Mick:  bit of a patch - attempt to fix crash from deleting thigns twice
		p_new_geom->mp_orig_render_colors = NULL;
	}

	return p_new_geom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom* CGeom::Clone(bool instance, CModel* pDestModel)
{
	// Create new instance and clone stuff below p-line
	CGeom* p_new_geom = plat_clone(instance, pDestModel);

	if (p_new_geom)
	{
		p_new_geom->m_cloned = (instance) ? vINSTANCE : vCOPY;

		Dbg_MsgAssert(!mp_coll_tri_data, ("Cloning of model geometry doesn't support collision now"));

		// Mick:  bit of a patch - attempt to fix crash from deleting thigns twice
		p_new_geom->mp_orig_render_colors = NULL;
	}

	return p_new_geom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGeom::LoadGeomData( CMesh* pMesh, CModel* pModel, bool color_per_material )
{
	// All data created here is an instance of something else
	m_cloned = vINSTANCE;
	m_multipleColorsEnabled = color_per_material;

	return plat_load_geom_data( pMesh, pModel, color_per_material );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::Finalize()
{
	plat_finalize();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CGeom::GetChecksum()
{		
	return plat_get_checksum();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetColor(Image::RGBA rgba)
{		
	plat_set_color(rgba);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::ClearColor()
{		
	plat_clear_color();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA	CGeom::GetColor() const
{		
	return plat_get_color();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGeom::SetMaterialColor(uint32 mat_checksum, int pass, Image::RGBA rgba)
{
	return plat_set_material_color(mat_checksum, pass, rgba);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA	CGeom::GetMaterialColor(uint32 mat_checksum, int pass)
{
	return plat_get_material_color(mat_checksum, pass);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetVisibility(uint32 mask)
{
	plat_set_visibility(mask);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CGeom::GetVisibility() const
{
	return plat_get_visibility();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetActive( bool active )
{
	plat_set_active( active );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGeom::IsActive() const
{
	return plat_is_active();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::CBBox & CGeom::GetBoundingBox() const
{
	return plat_get_bounding_box();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector CGeom::GetBoundingSphere() const
{
	return plat_get_bounding_sphere();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetBoundingSphere( const Mth::Vector& boundingSphere )
{
	plat_set_bounding_sphere( boundingSphere );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetWorldPosition(const Mth::Vector& pos)
{
	plat_set_world_position(pos);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector & CGeom::GetWorldPosition() const
{
	return plat_get_world_position();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetOrientation(const Mth::Matrix& orient)
{
	plat_set_orientation(orient);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Matrix & CGeom::GetOrientation() const
{
	return plat_get_orientation();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::RotateY(Mth::ERot90 rot)
{
	plat_rotate_y(rot);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetScale(const Mth::Vector& scale)
{
	plat_set_scale(scale);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CGeom::GetScale() const
{
	return plat_get_scale();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetCollTriData(CCollObjTriData *p_coll_data)
{
	mp_coll_tri_data = p_coll_data;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCollObjTriData* CGeom::GetCollTriData() const
{
	return mp_coll_tri_data;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetModelLights(CModelLights *p_model_lights)
{
	plat_set_model_lights(p_model_lights);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGeom::Render( Mth::Matrix* pMatrix, Mth::Matrix* ppBoneMatrices, int numBones )
{
	return plat_render( pMatrix, ppBoneMatrices, numBones );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CGeom::SetBoneMatrixData( Mth::Matrix* pBoneMatrices, int numBones )
{
	plat_set_bone_matrix_data( pBoneMatrices, numBones );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGeom::HidePolys( uint32 mask )
{
	return plat_hide_polys( mask );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGeom::EnableShadow( bool enabled )
{
	return plat_enable_shadow( enabled );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGeom::GetNumRenderVerts()								// - returns number of renderable verts
{
		return plat_get_num_render_verts();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::GetRenderVerts(Mth::Vector *p_verts)				// - gets a single array of the verts
{
		return plat_get_render_verts(p_verts);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::GetRenderColors(Image::RGBA *p_colors)				// - gets an array of vertex colors
{
		return plat_get_render_colors(p_colors);

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Return the total number of renerable polygons 
int	CGeom::GetNumRenderPolys()
{
 	return		plat_get_num_render_polys();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Return the total number of base polygons (excludes those generated for multipass)
int	CGeom::GetNumRenderBasePolys()
{
 	return		plat_get_num_render_base_polys();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Get the original render colors
// if this is the first time this function was called
// then we will allocate memory and store the colors
void CGeom::GetOrigRenderColors(Image::RGBA *p_colors)				// - gets an array of vertex colors
{
	int verts = GetNumRenderVerts();
	if (!mp_orig_render_colors)
	{
		// GJ:  this check was previously used to make sure the malloc caused no bottomup
		// fragmentation during cutscenes.	however, it's no longer needed because of
		// the new cutscene heap.  i'm going to leave the code here commented out, just in case
//		#ifdef	__NOPT_ASSERT__
//		Mdl::Skate* skate_mod = Mdl::Skate::Instance();
//		Dbg_MsgAssert( !skate_mod->GetMovieManager()->IsRolling(), ( "Can't create lights during cutscenes" ) );
//		#endif

		// Mick: Game specific optimization here
		// because the "fake lights" are only used in non-net games
		// we use the frontend/net heap to store the original colors
		// as it is under utilized in a a single player game
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());		
		mp_orig_render_colors = (Image::RGBA*) Mem::Malloc(verts * sizeof(Image::RGBA));
		Mem::Manager::sHandle().PopContext();
		
		GetRenderColors(mp_orig_render_colors);
	}
	memcpy(p_colors,mp_orig_render_colors,verts * sizeof(Image::RGBA));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetRenderVerts(Mth::Vector *p_verts)				// - sets the verts after modification
{
		return plat_set_render_verts(p_verts);

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetRenderColors(Image::RGBA *p_colors)				// - sets the colors after modification
{

		return plat_set_render_colors(p_colors);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetUVWibbleParams(float u_vel, float u_amp, float u_freq, float u_phase,
							  float v_vel, float v_amp, float v_freq, float v_phase)
{
	plat_set_uv_wibble_params(u_vel, u_amp, u_freq, u_phase, v_vel, v_amp, v_freq, v_phase);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::UseExplicitUVWibble(bool yes)
{
	plat_use_explicit_uv_wibble(yes);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGeom::SetUVWibbleOffsets(float u_offset, float v_offset)
{
	plat_set_uv_wibble_offsets(u_offset, v_offset);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGeom::SetUVWibbleOffsets(uint32 mat_checksum, int pass, float u_offset, float v_offset)
{
	return plat_set_uv_wibble_offsets(mat_checksum, pass, u_offset, v_offset);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGeom::SetUVMatrix(uint32 mat_checksum, int pass, const Mth::Matrix &mat)
{
	return plat_set_uv_matrix(mat_checksum, pass, mat);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGeom::AllocateUVMatrixParams(uint32 mat_checksum, int pass)
{
	return plat_allocate_uv_matrix_params(mat_checksum, pass);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGeom::MultipleColorsEnabled()
{
	return m_multipleColorsEnabled;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx


================================================
FILE: Code/Gfx/NxGeom.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxGeom.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/27/2002
//****************************************************************************

#ifndef	__GFX_NXGEOM_H__
#define	__GFX_NXGEOM_H__
                   
#ifndef __CORE_DEFINES_H
    #include 
#endif

#ifndef	__CORE_ROT90_H
    #include 
#endif

#ifdef __PLAT_NGC__
#include "sys/ngc/p_gx.h"
#endif		// __PLAT_NGC__

namespace Mth
{
	class Matrix;
	class Vector;
	class CBBox;
}
			
namespace Image
{
	struct RGBA;
}

namespace Nx
{
	class CScene;
	class CMesh;
	class CModel;
	class CModelLights;
	class CCollObjTriData;

class CGeom : public Spt::Class
{

public:

    // The basic interface to the geom object
    // this is the machine independent part	
    // machine independent range checking, etc can go here	
						CGeom();
    virtual				~CGeom();

	CGeom *				Clone(bool instance, CScene* pDestScene=NULL);
	CGeom *				Clone(bool instance, CModel* pDestModel);		// Clones a Geom from one model into a new model

	bool				LoadGeomData(CMesh* pMesh, CModel* pModel, bool color_per_material);
	void				Finalize();

	uint32				GetChecksum();

	void				SetColor(Image::RGBA rgba);
	Image::RGBA			GetColor() const;
	void				ClearColor();

	// Can only be used on geometries that were created with color_per_material
	bool				SetMaterialColor(uint32 mat_checksum, int pass, Image::RGBA rgba);
	Image::RGBA			GetMaterialColor(uint32 mat_checksum, int pass);

	void				SetVisibility(uint32 mask);
	uint32				GetVisibility() const;

	void				SetActive(bool active);
	bool				IsActive() const;

	const Mth::CBBox &	GetBoundingBox() const;

	void				SetBoundingSphere(const Mth::Vector& boundingSphere);
	const Mth::Vector	GetBoundingSphere() const;

	void				SetWorldPosition(const Mth::Vector& pos);
	const Mth::Vector &	GetWorldPosition() const;

	void				SetOrientation(const Mth::Matrix& orient);
	const Mth::Matrix &	GetOrientation() const;

	void 				RotateY(Mth::ERot90 rot);

	void				SetTransform(const Mth::Matrix& transform);		// Overrides both world pos and orientation
	const Mth::Matrix &	GetTransform() const;

	void				SetScale(const Mth::Vector& scale);
	Mth::Vector			GetScale() const;

	void				SetCollTriData(CCollObjTriData *);
	CCollObjTriData *	GetCollTriData() const;

	void				SetModelLights(CModelLights *);

	bool                Render( Mth::Matrix* pMatrix, Mth::Matrix* ppBoneMatrices, int numBones );
	void				SetBoneMatrixData( Mth::Matrix* pBoneMatrices, int numBones );
	bool				HidePolys(uint32 mask);
	bool				EnableShadow(bool enabled);

	// used by CModelBuilder to figure out which color modulation function to use
	bool				MultipleColorsEnabled();

// Functions for getting and modifying the raw vertex information in a CGeom	
	int 				GetNumRenderPolys();									// - returns number of renderable polys
	int 				GetNumRenderBasePolys();							// - returns number of first pass polys
	int 				GetNumRenderVerts();								// - returns number of renderable verts
	void 				GetRenderVerts(Mth::Vector *p_verts);				// - gets a single array of the verts
	void 				GetOrigRenderColors(Image::RGBA *p_colors);			// - gets original render colors, storing them if this si the first time
	void 				GetRenderColors(Image::RGBA *p_colors);				// - gets an array of vertex colors
	void 				SetRenderVerts(Mth::Vector *p_verts);				// - sets the verts after modification
	void 				SetRenderColors(Image::RGBA *p_colors);				// - sets the colors after modification

	// Wibble functions
	void				SetUVWibbleParams(float u_vel, float u_amp, float u_freq, float u_phase,
										  float v_vel, float v_amp, float v_freq, float v_phase);
	void				UseExplicitUVWibble(bool yes);
	void				SetUVWibbleOffsets(float u_offset, float v_offset);
	bool				SetUVWibbleOffsets(uint32 mat_checksum, int pass, float u_offset, float v_offset);
	bool				SetUVMatrix(uint32 mat_checksum, int pass, const Mth::Matrix &mat);
	bool				AllocateUVMatrixParams(uint32 mat_checksum, int pass);

protected:
	// Declares if and what type of clone it is
	enum CloneType
	{
		vORIGINAL = 0,			// not cloned
		vINSTANCE,
		vCOPY,
	};

	CloneType			m_cloned;										// marks if cloned object
	CCollObjTriData *	mp_coll_tri_data;								// Collision geom data

	Image::RGBA		*	mp_orig_render_colors;
	bool				m_multipleColorsEnabled;

private:
    // The virtual functions will have a stub implementation
    // in p_nxgeom.cpp
	virtual CGeom *		plat_clone(bool instance, CScene* pDestScene=NULL);
	virtual CGeom *		plat_clone(bool instance, CModel* pDestModel);

	virtual	bool		plat_load_geom_data(CMesh* pMesh, CModel* pModel, bool color_per_material);

	virtual void		plat_finalize();

	virtual uint32		plat_get_checksum();

	virtual	void		plat_set_color(Image::RGBA rgba);
	virtual	Image::RGBA	plat_get_color() const;
	virtual	void		plat_clear_color();

	virtual bool		plat_set_material_color(uint32 mat_checksum, int pass, Image::RGBA rgba);
	virtual Image::RGBA	plat_get_material_color(uint32 mat_checksum, int pass);

	virtual	void		plat_set_visibility(uint32 mask);
	virtual	uint32		plat_get_visibility() const;

	virtual	void		plat_set_active(bool active);
	virtual	bool		plat_is_active() const;

	virtual const Mth::CBBox &	plat_get_bounding_box() const;
	
	virtual void		plat_set_bounding_sphere(const Mth::Vector& boundingSphere);
	virtual const Mth::Vector	plat_get_bounding_sphere() const;

	virtual void		plat_set_world_position(const Mth::Vector& pos);
	virtual const Mth::Vector &	plat_get_world_position() const;

	virtual void		plat_set_orientation(const Mth::Matrix& orient);
	virtual const Mth::Matrix &	plat_get_orientation() const;

	virtual void 		plat_rotate_y(Mth::ERot90 rot);

	virtual void		plat_set_transform(const Mth::Matrix& transform);
	virtual const Mth::Matrix &	plat_get_transform() const;

	virtual void		plat_set_scale(const Mth::Vector& scale);
	virtual Mth::Vector plat_get_scale() const;

	virtual void		plat_set_model_lights(CModelLights *);

	virtual bool		plat_render(Mth::Matrix* pRootMatrix, Mth::Matrix* ppBoneMatrices, int numBones);
	virtual void		plat_set_bone_matrix_data( Mth::Matrix* pBoneMatrices, int numBones );
	virtual bool		plat_hide_polys(uint32 mask);
	virtual bool		plat_enable_shadow( bool enabled );

	virtual	int 		plat_get_num_render_polys();
	virtual	int 		plat_get_num_render_base_polys();
	virtual	int 		plat_get_num_render_verts();								// - returns number of renderable verts
	virtual	void 		plat_get_render_verts(Mth::Vector *p_verts);				// - gets a single array of the verts
	virtual	void 		plat_get_render_colors(Image::RGBA *p_colors);				// - gets an array of vertex colors
	virtual void 		plat_set_render_verts(Mth::Vector *p_verts);				// - sets the verts after modification
	virtual	void 		plat_set_render_colors(Image::RGBA *p_colors);				// - sets the colors after modification

	virtual void		plat_set_uv_wibble_params(float u_vel, float u_amp, float u_freq, float u_phase,
												  float v_vel, float v_amp, float v_freq, float v_phase);
	virtual void		plat_use_explicit_uv_wibble(bool yes);
	virtual void		plat_set_uv_wibble_offsets(float u_offset, float v_offset);
	virtual bool		plat_set_uv_wibble_offsets(uint32 mat_checksum, int pass, float u_offset, float v_offset);
	virtual bool		plat_set_uv_matrix(uint32 mat_checksum, int pass, const Mth::Matrix &mat);
	virtual bool		plat_allocate_uv_matrix_params(uint32 mat_checksum, int pass);
};

}

#endif // __GFX_NXGEOM_H__



================================================
FILE: Code/Gfx/NxHierarchy.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxHierarchy.h
//* OWNER:          Garrett Jost
//* CREATION DATE:  7/26/2002
//****************************************************************************

#ifndef	__GFX_NXHIERARCHY_H__
#define	__GFX_NXHIERARCHY_H__

                           
#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

namespace Nx
{

// Hierarchy information available to game.  It is found at the beginning of a geom. file
// although it is not used by the geometry directly.
class CHierarchyObject
{
public:
						CHierarchyObject();
						~CHierarchyObject();

	void				SetChecksum(uint32 checksum);
	uint32				GetChecksum() const;
	void				SetParentChecksum(uint32 checksum);
	uint32				GetParentChecksum() const;
	void				SetParentIndex(int16 index);
	int16				GetParentIndex() const;
	void				SetBoneIndex(sint8 index);
	sint8				GetBoneIndex() const;

	void				SetSetupMatrix(const Mth::Matrix& mat);
	const Mth::Matrix &	GetSetupMatrix() const;

	//static CHierarchyObject*	sGetHierarchyArray(uint8 *pPipData, int& size);
	
protected:
	uint32				m_checksum;			// Object checksum
	uint32				m_parent_checksum;	// Checksum of parent, or 0 if root object
	int16				m_parent_index;		// Index of parent in the hierarchy array (or -1 if root object)
	sint8				m_bone_index;		// The index of the bone matrix used on this object
	uint8				m_pad_8;
	uint32				m_pad_32;
	Mth::Matrix			m_setup_matrix;		// Initial local to parent matrix
};

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline CHierarchyObject::CHierarchyObject()
{
	m_checksum = 0;
	m_parent_checksum = 0;

	m_parent_index = -1;
	m_bone_index = 0;

	m_setup_matrix.Ident();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline CHierarchyObject::~CHierarchyObject()
{
}

inline void					CHierarchyObject::SetChecksum(uint32 checksum)
{
	m_checksum = checksum;
}

inline uint32				CHierarchyObject::GetChecksum() const
{
	return m_checksum;
}

inline void					CHierarchyObject::SetParentChecksum(uint32 checksum)
{
	m_parent_checksum = checksum;
}

inline uint32				CHierarchyObject::GetParentChecksum() const
{
	return m_parent_checksum;
}

inline void					CHierarchyObject::SetParentIndex(int16 index)
{
	m_parent_index = index;
}

inline int16				CHierarchyObject::GetParentIndex() const
{
	return m_parent_index;
}

inline void					CHierarchyObject::SetBoneIndex(sint8 index)
{
	m_bone_index = index;
}

inline sint8				CHierarchyObject::GetBoneIndex() const
{
	return m_bone_index;
}

inline void					CHierarchyObject::SetSetupMatrix(const Mth::Matrix& mat)
{
	m_setup_matrix = mat;
}

inline const Mth::Matrix &	CHierarchyObject::GetSetupMatrix() const
{
	return m_setup_matrix;
}

}

#endif // __GFX_NXMESH_H__




================================================
FILE: Code/Gfx/NxImposter.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:			Nx Imposter												**
**																			**
**	File name:		gfx/NxImposter.cpp										**
**																			**
**	Created by:		03/13/03	-	dc										**
**																			**
**	Description:	Imposter and Imposter Management code					**
**																			**
*****************************************************************************/

// start autoduck documentation
// @DOC nximposter
// @module nximposter | None
// @subindex Scripting Database
// @index script | nximposter

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CImposterManager::CImposterManager( void )
{
	mp_group_table						= new Lst::HashTable< CImposterGroup >( 8 );
	m_max_imposters_to_redraw_per_frame	= 1;
}

	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CImposterManager::~CImposterManager( void )
{
	delete mp_group_table;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::Cleanup( void )
{
	// Goes through and removes all imposter groups.
	mp_group_table->IterateStart();
	CImposterGroup *p_imposter_group = mp_group_table->IterateNext();
	while( p_imposter_group )
	{
		CImposterGroup *p_next	= mp_group_table->IterateNext();
		delete p_imposter_group;
		p_imposter_group		= p_next;
	}
	mp_group_table->FlushAllItems();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::AddGeomToImposter( uint32 group_checksum, Nx::CGeom *p_geom )
{
	// See if this imposter group exists already, create it if not.
	CImposterGroup *p_group = mp_group_table->GetItem( group_checksum );
	if( p_group == NULL )
	{
		p_group = plat_create_imposter_group();
		mp_group_table->PutItem( group_checksum, p_group );

		// Set the bounding box to be that of the 'founding' CGeom.
		p_group->SetCompositeBoundingBox( p_geom->GetBoundingBox());
	}
	else
	{
		// Add the bounding box details of this CGeom.
		p_group->AddBoundingBox( p_geom->GetBoundingBox());
	}

	// Add the specified CGeom to the CImposterGroup's list.
	p_group->AddGeom( p_geom );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::RemoveGeomFromImposter( uint32 group_checksum, Nx::CGeom *p_geom )
{
	CImposterGroup *p_group = mp_group_table->GetItem( group_checksum );
	if( p_group )
	{
		p_group->RemoveGeom( p_geom );
	}
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::ProcessImposters( void )
{
	int imposters_drawn = 0;

	mp_group_table->IterateStart();
	CImposterGroup *p_imposter_group = mp_group_table->IterateNext();
	while( p_imposter_group )
	{
		// Once per frame process function, for platform-specific tasks.
		p_imposter_group->Process();

		// Determine whether this imposter is no longer visible, or is outside the switch distance.
		float d = p_imposter_group->CheckDistance();

		if( d <= p_imposter_group->GetSwitchDistance())
		{
			// This imposter is no longer outside the switch distance, or is not visible. Remove the imposter polygon if it exists.
			p_imposter_group->RemoveImposterPolygon();
		}
		else
		{
			// This imposter is outside the switch distance. Create the imposter polygon if it doesn't exist...
			if( p_imposter_group->ImposterPolygonExists() == false )
			{
				if( imposters_drawn < m_max_imposters_to_redraw_per_frame )
				{
					p_imposter_group->CreateImposterPolygon();
					++imposters_drawn;
				}
			}
			else
			{
				// ...Otherwise determine whether the imposter polygon requires updating.
				if( imposters_drawn < m_max_imposters_to_redraw_per_frame )
				{
					bool drawn = p_imposter_group->UpdateImposterPolygon();
					if( drawn )
						++imposters_drawn;
				}
			}
		}
		p_imposter_group = mp_group_table->IterateNext();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::DrawImposters( void )
{
	plat_pre_render_imposters();

	mp_group_table->IterateStart();
	CImposterGroup *p_imposter_group = mp_group_table->IterateNext();
	while( p_imposter_group )
	{
		if( p_imposter_group->ImposterPolygonExists())
		{
			p_imposter_group->DrawImposterPolygon();
		}
		p_imposter_group = mp_group_table->IterateNext();
	}

	plat_post_render_imposters();
}


















/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CImposterGroup::CImposterGroup( void )
{
	m_switch_distance			= 960.0f;
	mp_geom_list				= new Lst::Head < Nx::CGeom >;
	m_imposter_polygon_exists	= false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CImposterGroup::~CImposterGroup( void )
{
	// Remove all nodes from the table.
	Lst::Node *p_node, *p_next;
	for( p_node = mp_geom_list->GetNext(); p_node; p_node = p_next )
	{
		p_next = p_node->GetNext();
		delete p_node;
	}

	// Remove the table itself.
	delete mp_geom_list;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::AddGeom( CGeom *p_geom )
{
	// Create a new node.
	Lst::Node *p_new_node = new Lst::Node( p_geom );

	// Link in the new node.
	mp_geom_list->AddToTail( p_new_node );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::RemoveGeom( CGeom *p_geom )
{
	Lst::Node *p_node, *p_next;
	for( p_node = mp_geom_list->GetNext(); p_node; p_node = p_next )
	{
		p_next = p_node->GetNext();

		if( p_node->GetData() == p_geom )
		{
			delete p_node;
			break;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::SetCompositeBoundingBox( const Mth::CBBox & bbox )
{
	m_composite_bbox			= bbox;
	m_composite_bbox_mid		= m_composite_bbox.GetMin() + ( 0.5f * ( m_composite_bbox.GetMax() - m_composite_bbox.GetMin()));
	m_composite_bsphere_radius	= ( m_composite_bbox.GetMax() - m_composite_bbox_mid ).Length();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::AddBoundingBox( const Mth::CBBox & bbox )
{
	m_composite_bbox.AddPoint( bbox.GetMin());
	m_composite_bbox.AddPoint( bbox.GetMax());

	m_composite_bbox_mid		= m_composite_bbox.GetMin() + ( 0.5f * ( m_composite_bbox.GetMax() - m_composite_bbox.GetMin()));
	m_composite_bsphere_radius	= ( m_composite_bbox.GetMax() - m_composite_bbox_mid ).Length();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::Process( void )
{
	return plat_process();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float CImposterGroup::CheckDistance( void )
{
	return plat_check_distance();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::CreateImposterPolygon( void )
{
	plat_create_imposter_polygon();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::RemoveImposterPolygon( void )
{
	plat_remove_imposter_polygon();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CImposterGroup::UpdateImposterPolygon( void )
{
	++m_update_count;
	return plat_update_imposter_polygon();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::DrawImposterPolygon( void )
{
	plat_draw_imposter_polygon();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::plat_create_imposter_polygon( void )
{
	printf( "STUB: plat_create_imposter_polygon\n");
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::plat_remove_imposter_polygon( void )
{
	printf( "STUB: plat_remove_imposter_polygon\n");
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CImposterGroup::plat_update_imposter_polygon( void )
{
	printf( "STUB: plat_update_imposter_polygon\n");
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::plat_draw_imposter_polygon( void )
{
	printf( "STUB: plat_draw_imposter_polygon\n");
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float CImposterGroup::plat_check_distance( void )
{
	printf( "STUB: plat_check_distance\n");
	return 0.0f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterGroup::plat_process( void )
{
	printf( "STUB: plat_process\n");
}

}



================================================
FILE: Code/Gfx/NxImposter.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// NxImposter.H - Neversoft Engine, Rendering portion, Platform independent interface

#ifndef	__GFX_NX_IMPOSTER_H__
#define	__GFX_NX_IMPOSTER_H__

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#include 

namespace Nx
{
/////////////////////////////////////////////////////////////////////////
// Forward declarations


	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CImposterGroup
{
	public:
								CImposterGroup( void );
	virtual						~CImposterGroup();

	void						AddGeom( CGeom *p_geom );
	void						RemoveGeom( CGeom *p_geom );
								
	Mth::CBBox &				GetCompositeBoundingBox( void )					{ return m_composite_bbox; }
	void						SetCompositeBoundingBox( const Mth::CBBox & bbox );
	void						AddBoundingBox( const Mth::CBBox & bbox );
	float						GetSwitchDistance( void )						{ return m_switch_distance; }
	float						CheckDistance( void );
	void						Process( void );			// Called once per frame for platform specific processing.

	void						CreateImposterPolygon( void );
	void						RemoveImposterPolygon( void );
	bool						UpdateImposterPolygon( void );
	void						DrawImposterPolygon( void );

	bool						ImposterPolygonExists( void )					{ return m_imposter_polygon_exists; }

	protected:

	float						m_switch_distance;			// Distance at which the imposter switches in/out.
	Mth::CBBox					m_composite_bbox;			// Composite bounding box from all geometey added to the group.
	Mth::Vector					m_composite_bbox_mid;		// Mid point of composite bounding box (for speed).
	float						m_composite_bsphere_radius;	// Composite bounding sphere radius (mid point is same as box).
	Lst::Head  *		mp_geom_list;				// List of geometry for this imposter group.
	Mth::Vector					m_cam_pos;					// The camera position at the time of creation.
	int							m_tex_width, m_tex_height;	// Current imposter texture width and height.
	int							m_update_count;				// Incremented each update, used to allow intermittent checks.
	bool						m_imposter_polygon_exists;

	private:

	// The virtual functions have a stub implementation.
	virtual void				plat_create_imposter_polygon( void );
	virtual void				plat_remove_imposter_polygon( void );
	virtual bool				plat_update_imposter_polygon( void );
	virtual void				plat_draw_imposter_polygon( void );
	virtual float				plat_check_distance( void );
	virtual void				plat_process( void );
};
	
	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CImposterManager
{
public:
										CImposterManager( void );
										~CImposterManager();

	void								Cleanup( void );
	void								AddGeomToImposter( uint32 group_checksum, Nx::CGeom *p_geom );
	void								RemoveGeomFromImposter( uint32 group_checksum, Nx::CGeom *p_geom );
	void								ProcessImposters( void );
	void								DrawImposters( void );

	CImposterGroup*						plat_create_imposter_group( void );
	void								plat_pre_render_imposters( void );
	void								plat_post_render_imposters( void );

protected:

private:

	Lst::HashTable< CImposterGroup >	*mp_group_table;
	int									m_max_imposters_to_redraw_per_frame;


};


///////////////////////////////////////////////////////////////////////
//
// Inline functions
//
}
#endif

================================================
FILE: Code/Gfx/NxLight.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxLight.cpp


#include 

#include "gfx/NxLight.h"

namespace	Nx
{

/////////////////////////////////////////////////////////////////////////////
// CModelLights

/////////////////////////////////////////////////////////////////////////////
// These functions are the platform independent part of the interface.
					 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModelLights::CModelLights()
{
	m_brightness = m_brightness_target = 0.0f;

	m_ambient_brightness = 1.0f;

	for (int i = 0; i < CLightManager::MAX_LIGHTS; i++)
	{
		m_diffuse_brightness[i] = 1.0f;
	}

	mp_pos = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModelLights::~CModelLights()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CModelLights::SetLightAmbientColor(const Image::RGBA &rgba)
{
	//m_light_ambient_rgba = rgba;

	bool success = plat_set_light_ambient_color(rgba);

	// need to call this whenever the color/brightness changes
	plat_update_brightness();

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA			CModelLights::GetLightAmbientColor()
{
	return plat_get_light_ambient_color();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CModelLights::SetLightDirection(int light_index, const Mth::Vector &direction)
{
	Dbg_Assert(light_index < CLightManager::MAX_LIGHTS);

	//m_light_direction[light_index] = direction;

	return plat_set_light_direction(light_index, direction);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &	CModelLights::GetLightDirection(int light_index)
{
	Dbg_Assert(light_index < CLightManager::MAX_LIGHTS);

	return plat_get_light_direction(light_index);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CModelLights::SetLightDiffuseColor(int light_index, const Image::RGBA &rgba)
{
	Dbg_Assert(light_index < CLightManager::MAX_LIGHTS);

	//m_light_diffuse_rgba[light_index] = rgba;

	bool success = plat_set_light_diffuse_color(light_index, rgba);

	// need to call this whenever the color/brightness changes
	plat_update_brightness();

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA			CModelLights::GetLightDiffuseColor(int light_index)
{
	Dbg_Assert(light_index < CLightManager::MAX_LIGHTS);

	return plat_get_light_diffuse_color(light_index);
}



/******************************************************************/
/*                                                                */
/* The 'add_scene_light' parameter is used optionally to		  */
/* determine whether a nearby Scene Light should be added to the  */
/* lighting calc (based on the 'pos' position).					  */
/*                                                                */
/******************************************************************/
void				CModelLights::UpdateEngine( Mth::Vector & pos, bool add_scene_light )
{
	plat_update_engine( pos, add_scene_light );
}







/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CModelLights::EnableAmbientLight(bool enable)
{
	plat_enable_ambient_light(enable);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CModelLights::EnableDiffuseLight(int light_index, bool enable)
{
	plat_enable_diffuse_light(light_index, enable);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CModelLights::IsAmbientLightEnabled() const
{
	return plat_is_ambient_light_enabled();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CModelLights::IsDiffuseLightEnabled(int light_index) const
{
	return plat_is_diffuse_light_enabled(light_index);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CModelLights::SetBrightness(float brightness)
{
	m_brightness_target = brightness;

	// We've found the target brightness, so move actual brightness towards this to avoid sudden unnatural transitions.
	const float BRIGHTNESS_SPEED = 0.02f;
	if( m_brightness < m_brightness_target )
	{
		m_brightness += BRIGHTNESS_SPEED;
		if( m_brightness > m_brightness_target )
		{
			m_brightness = m_brightness_target;
		}
	}
	else if( m_brightness > m_brightness_target )
	{
		m_brightness -= BRIGHTNESS_SPEED;
		if( m_brightness < m_brightness_target )
		{
			m_brightness = m_brightness_target;
		}
	}

	m_ambient_brightness = 1.0f + ((2.0f * m_brightness) - 1.0f) * CLightManager::s_world_lights.m_ambient_light_modulation_factor;
	if (m_ambient_brightness < 0.0f)
	{
		m_ambient_brightness = 0.0f;
	}

	for (int i = 0; i < CLightManager::MAX_LIGHTS; i++)
	{
		m_diffuse_brightness[i] = 1.0f + ((2.0f * m_brightness) - 1.0f) * CLightManager::s_world_lights.m_diffuse_light_modulation_factor[i];
		if (m_diffuse_brightness[i] < 0.0f)
		{
			m_diffuse_brightness[i] = 0.0f;
		}
	}

	// need to call this whenever the color/brightness changes
	plat_update_brightness();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CModelLights::UpdateBrightness()
{
	m_ambient_brightness = 1.0f + ((2.0f * m_brightness) - 1.0f) * CLightManager::s_world_lights.m_ambient_light_modulation_factor;
	if (m_ambient_brightness < 0.0f)
	{
		m_ambient_brightness = 0.0f;
	}

	for (int i = 0; i < CLightManager::MAX_LIGHTS; i++)
	{
		m_diffuse_brightness[i] = 1.0f + ((2.0f * m_brightness) - 1.0f) * CLightManager::s_world_lights.m_diffuse_light_modulation_factor[i];
		if (m_diffuse_brightness[i] < 0.0f)
		{
			m_diffuse_brightness[i] = 0.0f;
		}
	}

	// GJ: the normal PS2 lighting code assumes that the collision 
	// code will be run once a frame to copy over the m_ambient_base_color 
	// into the m_ambient_mod_color.  unfortunately, the cutscene objects' 
	// ref objects don't go through this code, so I had to add this function
	// to explicitly copy over the data...  there's probably a cleaner way
	// to do it, but I didn't want to break the existing code.
	
	// need to call this whenever the color/brightness changes
    plat_update_brightness();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CModelLights::SetPositionPointer(Mth::Vector *p_pos)
{
	mp_pos = p_pos;
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector *		CModelLights::GetPositionPointer()
{
	return mp_pos;
}

/////////////////////////////////////////////////////////////////////////////
// These functions are the stubs of the platform dependent part of the interface.

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CModelLights::plat_set_light_ambient_color(const Image::RGBA &rgba)
{
	printf ("STUB: CModelLights::SetLightAmbientColor\n");

	return false;
}

bool				CModelLights::plat_set_light_direction(int light_index, const Mth::Vector &direction)
{
	printf ("STUB: CModelLights::SetLightDirection\n");

	return false;
}

bool				CModelLights::plat_set_light_diffuse_color(int light_index, const Image::RGBA &rgba)
{
	printf ("STUB: CModelLights::SetLightDiffuseColor\n");

	return false;
}

Image::RGBA			CModelLights::plat_get_light_ambient_color() const
{
	printf ("STUB: CModelLights::GetLightAmbientColor\n");

	return Image::RGBA(0, 0, 0, 0);
}

const Mth::Vector &	CModelLights::plat_get_light_direction(int light_index) const
{
	printf ("STUB: CModelLights::GetLightDirection\n");

	static Mth::Vector stub(0, 0, 0, 0);

	return stub;
}

Image::RGBA			CModelLights::plat_get_light_diffuse_color(int light_index) const
{
	printf ("STUB: CModelLights::GetLightDiffuseColor\n");

	return Image::RGBA(0, 0, 0, 0);
}

void				CModelLights::plat_update_brightness()
{
	printf ("STUB: CModelLights::UpdateBrightness\n");
}

void				CModelLights::plat_enable_ambient_light(bool enable)
{
	printf ("STUB: CModelLights::EnableAmbientLight\n");
}

void				CModelLights::plat_enable_diffuse_light(int light_index, bool enable)
{
	printf ("STUB: CModelLights::EnableDiffuseLight\n");
}

bool				CModelLights::plat_is_ambient_light_enabled() const
{
	printf ("STUB: CModelLights::IsAmbientLightEnabled\n");

	return false;
}

bool				CModelLights::plat_is_diffuse_light_enabled(int light_index) const
{
	printf ("STUB: CModelLights::IsDiffuseLightEnabled\n");

	return false;
}

void CModelLights::plat_update_engine( Mth::Vector & pos, bool add_scene_light )
{
	printf ("STUB: CModelLights::plat_update_engine\n");
}

}




================================================
FILE: Code/Gfx/NxLight.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// NxLight.H - Neversoft Engine, Rendering portion, Platform independent interface

#ifndef	__GFX_NX_LIGHT_H__
#define	__GFX_NX_LIGHT_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
#include 
#include 


namespace Nx
{


///////////////////////////////////////////////////////////////////////////////////
// Nx::CModelLights
class	CModelLights
{
public:
								CModelLights();
	virtual						~CModelLights();

	// lights
	bool						SetLightAmbientColor(const Image::RGBA &rgba);
	Image::RGBA					GetLightAmbientColor();

	bool						SetLightDirection(int light_index, const Mth::Vector &direction);
	const Mth::Vector &			GetLightDirection(int light_index);

	bool						SetLightDiffuseColor(int light_index, const Image::RGBA &rgba);
	Image::RGBA					GetLightDiffuseColor(int light_index);

	// Upload values to engine (where applicable)
	void						UpdateEngine( Mth::Vector & pos, bool add_scene_light = false );
	
	// Enable lights
	void						EnableAmbientLight(bool enable);
	void						EnableDiffuseLight(int light_index, bool enable);
	bool						IsAmbientLightEnabled() const;
	bool						IsDiffuseLightEnabled(int light_index) const;

	// Sets the modulation brightness
	bool						SetBrightness(float brightness);
	float						GetBrightness() const;
	void						UpdateBrightness();

	// Position pointer
	bool						SetPositionPointer(Mth::Vector *p_pos);
	Mth::Vector *				GetPositionPointer();

protected:	
	// Internal flags.
	enum {
		mUSE_MODEL_AMBIENT		= 0x0001,					// Use ambient light
		mUSE_MODEL_DIFFUSE_0	= 0x0002,					// Use diffuse light 0
		mUSE_MODEL_DIFFUSE_1	= 0x0004,					// rest of diffuse must follow 0
	};

	//Image::RGBA					m_light_ambient_rgba;
	//Mth::Vector					m_light_direction[CLightManager::MAX_LIGHTS];
	//Image::RGBA					m_light_diffuse_rgba[CLightManager::MAX_LIGHTS];

	// Modulation brightness
	float						m_brightness;
	float						m_brightness_target;
	float						m_ambient_brightness;
	float						m_diffuse_brightness[CLightManager::MAX_LIGHTS];

	// Position pointer (for scene lights)
	Mth::Vector *				mp_pos;

private:
	// Platform-specific calls
	virtual bool				plat_set_light_ambient_color(const Image::RGBA &rgba);
	virtual bool				plat_set_light_direction(int light_index, const Mth::Vector &direction);
	virtual bool				plat_set_light_diffuse_color(int light_index, const Image::RGBA &rgba);
	virtual Image::RGBA			plat_get_light_ambient_color() const;
	virtual const Mth::Vector &	plat_get_light_direction(int light_index) const;
	virtual Image::RGBA			plat_get_light_diffuse_color(int light_index) const;

	virtual void				plat_update_engine( Mth::Vector & pos, bool add_scene_light );

	virtual void				plat_update_brightness();

	virtual void				plat_enable_ambient_light(bool enable);
	virtual void				plat_enable_diffuse_light(int light_index, bool enable);
	virtual bool				plat_is_ambient_light_enabled() const;
	virtual bool				plat_is_diffuse_light_enabled(int light_index) const;
};


}


#endif



================================================
FILE: Code/Gfx/NxLightMan.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxLightMan.cpp

// start autoduck documentation
// @DOC NxLightMan
// @module NxLightMan | None
// @subindex Scripting Database
// @index script | NxLightMan

#include 
#include 
#include 

#include  // for AddDebugLine( )
#include 
#include 
#include 
#include 

// This should eventually be removed, only needed for a call to GamePaused, which should not be
// called from here anyway.
#include 

#include 
#include 

#include "gfx/NxLightMan.h"

namespace	Nx
{

/////////////////////////////////////////////////////////////////////////////
// CLightManager

// World lights
CLightManager::SLightSet CLightManager::s_world_lights =
{
					Image::RGBA(72, 72, 72, 0x80),															// Ambient RGBA
					{ Mth::Vector(0.5f, 0.8660254f, 0.0f, 0.0f), Mth::Vector(0.0f, 1.0f, 0.0f, 0.0f) },		// Light direction
					{ Image::RGBA(75, 75, 75, 0x80), Image::RGBA(0, 0, 0, 0x80) },							// Diffuse RGBA
					0.5f,																					// Ambient modulation
					{ 0.7f, 1.0f }																			// Diffuse modulation
};
CLightManager::SLightSet CLightManager::s_world_lights_stack[WORLD_LIGHTS_STACK_SIZE];
int						 CLightManager::s_world_lights_stack_index = -1;

// The following are temp variables.  These should be done per model
float				CLightManager::s_brightness = 1.0f;
float				CLightManager::s_brightness_target = 1.0f;

float				CLightManager::s_min_brightness = 0.0f;
float				CLightManager::s_ambient_brightness = 1.0f;
float				CLightManager::s_diffuse_brightness[MAX_LIGHTS] = { 1.0f, 1.0f };

CSceneLight			CLightManager::s_scene_lights[MAX_SCENE_LIGHTS];
int					CLightManager::s_num_scene_lights	= 0;

SVCLight			CLightManager::sp_vc_lights[MAX_VC_LIGHTS];
int					CLightManager::s_num_vc_lights = 0;
uint16 *			CLightManager::sp_fake_lights_nodes=NULL;
int					CLightManager::s_num_fake_lights_nodes=0;
int					CLightManager::s_next_fake_light_node_to_process=0;
int					CLightManager::s_fake_lights_period=0;
int					CLightManager::s_fake_lights_current_time=0;
Script::CStruct *	CLightManager::sp_fake_lights_params=NULL;


/////////////////////////////////////////////////////////////////////////////
// These functions are the platform independent part of the interface.
					 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void				CLightManager::sUpdateEngine( void )
{
	s_plat_update_engine();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CLightManager::sClearLights()
{
	s_ambient_brightness = 1.0f;

	s_world_lights.m_light_ambient_rgba = Image::RGBA(0, 0, 0, 0x80);
	for (int i = 0; i < MAX_LIGHTS; i++)
	{
		s_world_lights.m_light_direction[i] = Mth::Vector(0.0f, 1.0f, 0.0f, 0.0f);
		s_world_lights.m_light_diffuse_rgba[i] = Image::RGBA(0, 0, 0, 0x80);

		s_diffuse_brightness[i] = 1.0f;
	}

	s_plat_update_lights();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CLightManager::sSetLightAmbientColor(const Image::RGBA rgba)
{
	s_world_lights.m_light_ambient_rgba = rgba;

	return s_plat_set_light_ambient_color();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA			CLightManager::sGetLightAmbientColor()
{
	//return s_light_ambient_rgba;
	return s_plat_get_light_ambient_color();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CLightManager::sSetLightDirection(int light_index, const Mth::Vector &direction)
{
	Dbg_Assert(light_index < MAX_LIGHTS);

	s_world_lights.m_light_direction[light_index] = direction;

	return s_plat_set_light_direction(light_index);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &	CLightManager::sGetLightDirection(int light_index)
{
	Dbg_Assert(light_index < MAX_LIGHTS);

	//return s_light_direction[light_index];
	return s_plat_get_light_direction(light_index);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CLightManager::sSetLightDiffuseColor(int light_index, const Image::RGBA rgba)
{
	s_world_lights.m_light_diffuse_rgba[light_index] = rgba;

	return s_plat_set_light_diffuse_color(light_index);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA			CLightManager::sGetLightDiffuseColor(int light_index)
{
	Dbg_Assert(light_index < MAX_LIGHTS);

	//return s_light_diffuse_rgba[light_index];
	return s_plat_get_light_diffuse_color(light_index);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CLightManager::sSetAmbientLightModulationFactor(float factor)
{
	s_world_lights.m_ambient_light_modulation_factor = factor;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CLightManager::sSetDiffuseLightModulationFactor(int light_index, float factor)
{
	Dbg_Assert(light_index < MAX_LIGHTS);

	s_world_lights.m_diffuse_light_modulation_factor[light_index] = factor;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float				CLightManager::sGetAmbientLightModulationFactor()
{
	return s_world_lights.m_ambient_light_modulation_factor;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float				CLightManager::sGetDiffuseLightModulationFactor(int light_index)
{
	Dbg_Assert(light_index < MAX_LIGHTS);

	return s_world_lights.m_diffuse_light_modulation_factor[light_index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CLightManager::sPushWorldLights()
{
	if (++s_world_lights_stack_index < WORLD_LIGHTS_STACK_SIZE)
	{
		// Put on top of stack
		s_world_lights_stack[s_world_lights_stack_index] = s_world_lights;
	}
	else
	{
		--s_world_lights_stack_index;
		Dbg_MsgAssert(0, ("World light stack full"));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CLightManager::sPopWorldLights()
{
	if (s_world_lights_stack_index >= 0)
	{
		// Get from top of stack and update the engine
		s_world_lights = s_world_lights_stack[s_world_lights_stack_index--];
		s_plat_update_lights();

		return true;
	}
	else
	{
		Dbg_MsgAssert(0, ("World light stack empty"));
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModelLights *		CLightManager::sCreateModelLights()
{
	return s_plat_create_model_lights();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CLightManager::sFreeModelLights(CModelLights *p_model_lights)
{
	return s_plat_free_model_lights(p_model_lights);
}

/******************************************************************/
/*                                                                */
/*   This will eventually be done through CModel                  */
/*                                                                */
/******************************************************************/

bool				CLightManager::sSetBrightness(float brightness)
{
#if 0
	// Don't do anything now
	return true;
#endif

	s_brightness_target = brightness;

	// We've found the target brightness, so move actual brightness towards this to avoid sudden unnatural transitions.
	const float BRIGHTNESS_SPEED = 0.02f;
	if( s_brightness < s_brightness_target )
	{
		s_brightness += BRIGHTNESS_SPEED;
		if( s_brightness > s_brightness_target )
		{
			s_brightness = s_brightness_target;
		}
	}
	else if( s_brightness > s_brightness_target )
	{
		s_brightness -= BRIGHTNESS_SPEED;
		if( s_brightness < s_brightness_target )
		{
			s_brightness = s_brightness_target;
		}
	}

	s_ambient_brightness = 1.0f + ((2.0f * s_brightness) - 1.0f) * s_world_lights.m_ambient_light_modulation_factor;
	if (s_ambient_brightness < s_min_brightness)
	{
		s_ambient_brightness = s_min_brightness;
	}

	for (int i = 0; i < MAX_LIGHTS; i++)
	{
		s_diffuse_brightness[i] = 1.0f + ((2.0f * s_brightness) - 1.0f) * s_world_lights.m_diffuse_light_modulation_factor[i];
		if (s_diffuse_brightness[i] < s_min_brightness)
		{
			s_diffuse_brightness[i] = s_min_brightness;
		}
	}

	s_plat_update_colors();

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSceneLight * CLightManager::sAddSceneLight( void )
{
	if( s_num_scene_lights < MAX_SCENE_LIGHTS )
	{
		++s_num_scene_lights;
		return &s_scene_lights[s_num_scene_lights - 1];
	}
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::sClearSceneLights( void )
{
	s_num_scene_lights = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::sClearVCLights()
{

// Need to flush the entries from each sector list before we "ignore" the light
// otherwise the hash items from one level hang around for another level, confusing us.
	
	for( int i = 0; i < s_num_vc_lights; ++i )
	{
		sp_vc_lights[i].ResetSectorList();
	}
	
	s_num_vc_lights=0;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CLightManager::sAddVCLight(uint32 name, Mth::Vector &pos, float intensity,
								Image::RGBA col, float outer_radius)
{
	Dbg_MsgAssert(s_num_vc_lights < MAX_VC_LIGHTS, ("Too many level lights that affect geometry (%d max)",MAX_VC_LIGHTS));
	if (s_num_vc_lights < MAX_VC_LIGHTS)
	{
		SVCLight *p_new_light = &sp_vc_lights[s_num_vc_lights++];
	
		p_new_light->SetNameChecksum( name );
		p_new_light->SetPosition( pos );
	
		p_new_light->SetIntensity( intensity * 0.01f );
		p_new_light->SetColor( col );
	
		// Setting the radius will implicitly calculate the sector list for this light.
		p_new_light->SetRadius( outer_radius );
	
		// Calculate which sectors will be affected.
		p_new_light->CalculateSectorList();
	
		if( intensity > 0.0f )
		{
			s_apply_lighting( Nx::CEngine::sGetMainScene(), name );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::sClearCurrentFakeLightsCommand()
{
	if (sp_fake_lights_nodes)
	{
		Mem::Free(sp_fake_lights_nodes);
		sp_fake_lights_nodes=NULL;
	}	
	s_num_fake_lights_nodes=0;
	s_next_fake_light_node_to_process=0;
	
	s_fake_lights_period=0;
	s_fake_lights_current_time=0;
	
	if (sp_fake_lights_params)
	{
		delete sp_fake_lights_params;
		sp_fake_lights_params=NULL;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Gets called from ScriptFakeLights in cfuncs.cpp
void CLightManager::sFakeLights(const uint16 *p_nodes, int numNodes, int period, Script::CStruct *pParams)
{
	sClearCurrentFakeLightsCommand();

	sp_fake_lights_params=new Script::CStruct;
	sp_fake_lights_params->AppendStructure(pParams);
	
	if (numNodes)
	{
		sp_fake_lights_nodes=(uint16*)Mem::Malloc(numNodes*sizeof(uint16));
		for (int i=0; iGamePaused())
	{
		return;
	}

	// If finished, clean up & stop.	
	if (s_fake_lights_current_time > s_fake_lights_period)
	{
		sClearCurrentFakeLightsCommand();
		return;
	}	
	
	// Calculate how many nodes ought to have been processed by now, based on the ratio of s_fake_lights_current_time
	// to s_fake_lights_period
	int required_num_nodes_processed;
	if (s_fake_lights_period)
	{
		required_num_nodes_processed = (s_num_fake_lights_nodes * s_fake_lights_current_time) / s_fake_lights_period;
	}	
	else
	{
		required_num_nodes_processed = s_num_fake_lights_nodes;
	}
	
	// Call sFakeLight for each node starting from where it got to last time, until the required number have
	// been processed.
	for (; s_next_fake_light_node_to_processGetChecksum(CRCD(0xa1dc81f9,"Name"),&id);
		sFakeLight(id,sp_fake_lights_params);
	}
	
	++s_fake_lights_current_time;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSceneLight *CLightManager::sGetSceneLight( uint32 checksum )
{
	for( int l = 0; l < s_num_scene_lights; ++l )
	{
		if( s_scene_lights[l].GetNameChecksum() == checksum )
		{
			return &s_scene_lights[l];
		}
	}
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSceneLight * CLightManager::sGetOptimumSceneLight( Mth::Vector & pos )
{
	if( s_num_scene_lights > 0 )
	{
		float	best_ratio		= 1.0f;
		int		best_index		= -1;

		for( int l = 0; l < s_num_scene_lights; ++l )
		{
			// Only interested in lights that are active.
			if( s_scene_lights[l].GetLightIntensity() > 0.0f )
			{
				float dist = Mth::Distance( pos, s_scene_lights[l].GetLightPosition());
				if( dist < s_scene_lights[l].GetLightRadius())
				{
					// Potentially a usable light.
					float ratio = dist * s_scene_lights[l].GetLightReciprocalRadius();
					if( ratio < best_ratio )
					{
						// I wonder whether we should also consider intensity here? A further light, with a higher
						// intensity may have more effect than a nearer light with smaller intensity.
						best_ratio = ratio;
						best_index = l;
					}
				}
			}
		}

		if( best_index >= 0 )
		{
			return &s_scene_lights[best_index];
		}
	}
	return NULL;
}



/////////////////////////////////////////////////////////////////////////////
// The script functions

// @script | SetLightAmbientColor | Sets the global light ambient color
// @parm int | r | Red
// @parm int | g | Green
// @parm int | b | Blue
bool ScriptSetLightAmbientColor(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int r, g, b;
	if (!pParams->GetInteger("r", &r))
	{
		Dbg_MsgAssert(0, ("Can't find 'r' color"));
	}
	if (!pParams->GetInteger("g", &g))
	{
		Dbg_MsgAssert(0, ("Can't find 'g' color"));
	}
	if (!pParams->GetInteger("b", &b))
	{
		Dbg_MsgAssert(0, ("Can't find 'b' color"));
	}
	
	Image::RGBA rgb(r, g, b, 0x80);

	return CLightManager::sSetLightAmbientColor(rgb);
}


// @script | SetLightDirection | Sets the unit direction vector of a global light
// @parm int | index | Light number
// @parm vector | direction | Unit direction vector (overrides heading and pitch)
// @parm float | heading | Heading angle (in degrees)
// @parm float | pitch | Pitch angle (in degrees)
bool ScriptSetLightDirection(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int index;
	if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
	{
		Dbg_MsgAssert(0, ("Can't find 'index' of light"));
	}

	float heading, pitch;
	Mth::Vector direction(0, 0, 1, 0);
	if (pParams->GetVector(CRCD(0xc1b52e4c,"direction"), &direction))
	{
		direction[W] = 0.0f;		// This is the only way to force this to be a vector (as opposed to a point)
		//Dbg_Message("************ direction (%f, %f, %f)", direction[X], direction[Y], direction[Z]);
	} else if (pParams->GetFloat(CRCD(0xfd4bc03e,"heading"), &heading) && pParams->GetFloat(CRCD(0xd8604126,"pitch"), &pitch)) {
		direction.RotateX(Mth::DegToRad(pitch));
		direction.RotateY(Mth::DegToRad(heading));
		//Dbg_Message("************ heading and pitch direction (%f, %f, %f)", direction[X], direction[Y], direction[Z]);
	} else {
		Dbg_MsgAssert(0, ("Can't find 'direction' or 'heading' and 'pitch' of light"));
	}

	return CLightManager::sSetLightDirection(index, direction);
}


// @script | SetLightDiffuseColor | Sets the diffuse color of a global light
// @parm int | index | Light number
// @parm int | r | Red
// @parm int | g | Green
// @parm int | b | Blue
bool ScriptSetLightDiffuseColor(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int index;
	if (!pParams->GetInteger(CRCD(0x7f8c98fe,"index"), &index))
	{
		Dbg_MsgAssert(0, ("Can't find 'index' of light"));
	}

	int r, g, b;
	if (!pParams->GetInteger(CRCD(0x93f60062,"r"), &r))
	{
		Dbg_MsgAssert(0, ("Can't find 'r' color"));
	}
	if (!pParams->GetInteger(CRCD(0xfe2be489,"g"), &g))
	{
		Dbg_MsgAssert(0, ("Can't find 'g' color"));
	}
	if (!pParams->GetInteger(CRCD(0x8e411006,"b"), &b))
	{
		Dbg_MsgAssert(0, ("Can't find 'b' color"));
	}
	
	Image::RGBA rgb(r, g, b, 0x80);

	return CLightManager::sSetLightDiffuseColor(index, rgb);
}


// @script | GetLightCurrentColor | Gets the rgb values for ambient and directional lights
bool ScriptGetLightCurrentColor(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int r=0, g=0, b=0;
    Image::RGBA rgb(r, g, b, 0x80);
    
    rgb = CLightManager::sGetLightAmbientColor();

    pScript->GetParams()->AddInteger(CRCD(0xed664897,"ambient_red"), rgb.r);
    pScript->GetParams()->AddInteger(CRCD(0x908bf647,"ambient_green"), rgb.g);
    pScript->GetParams()->AddInteger(CRCD(0x5f4fba78,"ambient_blue"), rgb.b);

    rgb = CLightManager::sGetLightDiffuseColor(0);

    pScript->GetParams()->AddInteger(CRCD(0xe213aeb7,"red_0"), rgb.r);
    pScript->GetParams()->AddInteger(CRCD(0x6ef19631,"green_0"), rgb.g);
    pScript->GetParams()->AddInteger(CRCD(0x5ec44213,"blue_0"), rgb.b);

    rgb = CLightManager::sGetLightDiffuseColor(1);

    pScript->GetParams()->AddInteger(CRCD(0x95149e21,"red_1"), rgb.r);
    pScript->GetParams()->AddInteger(CRCD(0x19f6a6a7,"green_1"), rgb.g);
    pScript->GetParams()->AddInteger(CRCD(0x29c37285,"blue_1"), rgb.b);
    
    /*float heading=0, pitch=0;
    Mth::Vector direction(0, 0, 1, 0);

    direction = CLightManager::sGetLightDirection(0);

    //printf("x=%i y=%i z=%i", direction[0], direction[1], direction[2]);

    //Do the opposite of these two lines
    //direction.RotateX(Mth::DegToRad(pitch));
    //direction.RotateY(Mth::DegToRad(heading));
        
    pScript->GetParams()->AddInteger("pitch_0", pitch);
    pScript->GetParams()->AddInteger("heading_0", heading);
    
    */

    return true;
}

// @script | PushWorldLights | Pushes current world lights onto stack, so they can be restored later.
bool ScriptPushWorldLights(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CLightManager::sPushWorldLights();
	return true;
}

// @script | PopWorldLights | Restores last pushed world lights to the current world lights.
bool ScriptPopWorldLights(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	return CLightManager::sPopWorldLights();
}

// @script | DrawDirectionalLightLines | Draws two lines to represent the directional lights
bool ScriptDrawDirectionalLightLines(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	float heading;
	pParams->GetFloat( CRCD(0xfd4bc03e,"heading"), &heading, Script::ASSERT );

    float pitch;
	pParams->GetFloat( CRCD(0xd8604126,"pitch"), &pitch, Script::ASSERT );

    pitch = -pitch * Mth::PI / 180.0f;
    heading = ((-heading+90) * Mth::PI / 180.0f);
    
    int r, g, b;
    pParams->GetInteger( CRCD(0x93f60062,"r"), &r, Script::ASSERT );
    pParams->GetInteger( CRCD(0xfe2be489,"g"), &g, Script::ASSERT );
    pParams->GetInteger( CRCD(0x8e411006,"b"), &b, Script::ASSERT );
    Image::RGBA rgb(r, g, b, 0x80);
    //rgb = CLightManager::sGetLightDiffuseColor(0);

    Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetLocalSkater();
	
	Mth::Vector pos = pSkater->GetPos();
	float x = pos.GetX();
	float y = ( pos.GetY() + 75 );
	float z = pos.GetZ();

    float hypyh = 50;
    
    float y0 = ( (sin(pitch) * hypyh) + y );
    float hypxz = (cos(pitch) * hypyh);
    float x0 = ( (cos(heading) * hypxz) + x );
    float z0 = ( (sin(heading) * hypxz) + z );
    
    Mth::Vector skaterpos(x, y, z, 0);
    Mth::Vector direction0(x0, y0, z0, 0);
    
    //printf("x=%i y=%i z=%i\n", direction0[0], direction0[1], direction0[2]);
    
    Gfx::AddDebugLine( skaterpos, direction0, MAKE_RGB( rgb.r, rgb.g, rgb.b ), MAKE_RGB( rgb.r, rgb.g, rgb.b ), 1 );
    
    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
SVCLight *CLightManager::s_get_vclight_from_checksum( uint32 cs )
{
	for( int i = 0; i < s_num_vc_lights; ++i )
	{
		if( sp_vc_lights[i].m_checksum == cs )
		{
			return &sp_vc_lights[i];
		}
	}
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
SVCLight::SVCLight( void )
{
	mp_sector_list = new Lst::HashTable< Nx::CSector >( 4 );

	m_radius	= 0.0f;
	m_intensity = 0.0f;

	// Default the color to pure white.
	m_color.r	= 0x80;
	m_color.g	= 0x80;
	m_color.b	= 0x80;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
SVCLight::~SVCLight( void )
{
	ResetSectorList();
	delete mp_sector_list;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SVCLight::SetRadius( float radius )
{
	Dbg_Assert( radius >= 0.0f );

	if( radius != m_radius )
	{
		m_radius = radius;

		// Deal with the SceneLight that may have been created from this node.
		Nx::CSceneLight *p_light = Nx::CLightManager::sGetSceneLight( m_checksum );
		if( p_light )
		{
			p_light->SetLightRadius( radius );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SVCLight::SetIntensity( float i )
{
	Dbg_Assert(( i >= 0.0f ) && ( i <= 1.0f ));

	if( i != m_intensity )
	{
		m_intensity = i;

		// Deal with the SceneLight that may have been created from this node.
		Nx::CSceneLight *p_light = Nx::CLightManager::sGetSceneLight( m_checksum );
		if( p_light )
		{
			p_light->SetLightIntensity( i );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SVCLight::SetColor( Image::RGBA col )
{
	m_color = col;

	// Deal with the SceneLight that may have been created from this node.
	Nx::CSceneLight *p_light = Nx::CLightManager::sGetSceneLight( m_checksum );
	if( p_light )
	{
		p_light->SetLightColor( col );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Lst::HashTable< Nx::CSector > *SVCLight::GetSectorList( void )
{
	return mp_sector_list;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SVCLight::ResetSectorList( void )
{
	mp_sector_list->FlushAllItems();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool SVCLight::AffectsSector( Nx::CSector *p_sector )
{
	Nx::CSector *p_match = mp_sector_list->GetItem((uint32)p_sector );
    return ( p_match != NULL );	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SVCLight::CalculateSectorList( void )
{
	// First reset the sector list.
	ResetSectorList();
	
	Nx::CScene *p_scene = Nx::CEngine::sGetMainScene();
	if( p_scene )
	{
		Lst::HashTable< Nx::CSector > *p_sector_list = p_scene->GetSectorList();
		if( p_sector_list )
		{
			p_sector_list->IterateStart();	
			Nx::CSector *p_sector = p_sector_list->IterateNext();
			while( p_sector )
			{
				// Only consider this sector if it is not in the "NoLevelLights" lightgroup.
				// and not in the Indoor group
				if( p_sector->GetLightGroup() != CRCD( 0xed82767b, "NoLevelLights" )
					&& p_sector->GetLightGroup() != CRCD(0xec6542d3,"indoor"))
				{
					Nx::CGeom *p_geom = p_sector->GetGeom();
					if( p_geom )
					{
						// See of the radius of the light will intersect the radius of the bounding box of the geometry.
						Mth::CBBox	bbox	= p_sector->GetBoundingBox();
						Mth::Vector	mid		= ( bbox.GetMax() + bbox.GetMin()) / 2.0f;
						float		r		= ( bbox.GetMax() - mid ).Length();
						float		dist	= ( mid - m_pos ).Length();
						if( dist < ( r + m_radius ))
						{
							mp_sector_list->PutItem((uint32)p_sector, p_sector );
						}
					}
				}
				p_sector = p_sector_list->IterateNext();
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::s_recalculate_sector_lighting( Nx::CSector *p_sector )
{
	Nx::CGeom *p_geom = p_sector->GetGeom();
	if( p_geom == NULL )
	{
		return;
	}

	const int MAX_LIGHTS_AFFECTING_SECTOR = 8;

	int			current_lights_affecting_sector = 0;
	int			light_index[MAX_LIGHTS_AFFECTING_SECTOR];

	// Build a list of lights that affect this sector, to the maximum of MAX_LIGHTS_AFFECTING_SECTOR.
	for( int l = 0; l < s_num_vc_lights; ++l )
	{
		if(( sp_vc_lights[l].m_intensity > 0.0f ) && sp_vc_lights[l].AffectsSector( p_sector ))
		{
			light_index[current_lights_affecting_sector] = l;
			if( ++current_lights_affecting_sector >= MAX_LIGHTS_AFFECTING_SECTOR )
			{
				break;
			}
		}
	}

	if( current_lights_affecting_sector == 0 )
	{
		// No lights are affecting this sector, which can happen in the situation where the last remaining
		// light affecting a sector is turned off. In this case we just want to restore the original
		// vertex colors.
		int verts = p_geom->GetNumRenderVerts();
		if( verts > 0 )
		{
			Image::RGBA	*p_colors	= new Image::RGBA[verts];
			p_geom->GetOrigRenderColors( p_colors );
			p_geom->SetRenderColors( p_colors );
			delete [] p_colors;
		}
	}
	else if( current_lights_affecting_sector > 0 )
	{
		int verts = p_geom->GetNumRenderVerts();
		if( verts > 0 )
		{
			Mth::Vector	*p_verts	= new Mth::Vector[verts];
			Image::RGBA	*p_colors	= new Image::RGBA[verts];

			p_geom->GetRenderVerts( p_verts );
			p_geom->GetOrigRenderColors( p_colors );
					
			Image::RGBA *p_color = p_colors;
			Mth::Vector *p_vert = p_verts;
			for( int i = 0; i < verts; ++i )
			{
				float ir = 0.0f;
				float ig = 0.0f;
				float ib = 0.0f;
			
				for( int l = 0; l < current_lights_affecting_sector; ++l )
				{
					SVCLight *p_current_light = &sp_vc_lights[light_index[l]];

					float dist_sqr	= ( *p_vert - p_current_light->m_pos ).LengthSqr();
					if( dist_sqr < ( p_current_light->m_radius * p_current_light->m_radius ))
					{
						float dist		= sqrtf( dist_sqr );
						float intensity	= p_current_light->m_intensity * (( p_current_light->m_radius - dist ) / p_current_light->m_radius );

						ir += intensity * (float)p_current_light->m_color.r;
						ig += intensity * (float)p_current_light->m_color.g;
						ib += intensity * (float)p_current_light->m_color.b;
					}
				}

				// Apply to each r,g,b idividually, so we don't loose original color.
				Image::RGBA rgb = *p_color;
				float v = (float)rgb.r + ir;
				rgb.r = (uint8)(( v > 255.0f ) ? 255.0f : v );
							
				v = (float)rgb.g + ig;
				rgb.g = (uint8)(( v > 255.0f ) ? 255.0f : v );

				v = (float)rgb.b + ib;
				rgb.b = (uint8)(( v > 255.0f ) ? 255.0f : v );

				*p_color = rgb;

				++p_color;
				++p_vert;
			}

			p_geom->SetRenderColors( p_colors );
						
			delete [] p_verts;
			delete [] p_colors;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::s_apply_lighting( Nx::CScene * p_scene, uint32 checksum )
{
	// Now requires a checksum.
	if( checksum == 0 )
		return;

	// Now deal with geometry.
	SVCLight *p_scene_light = s_get_vclight_from_checksum( checksum );
	if( p_scene_light == NULL )
	{
		return;
	}

	Lst::HashTable< Nx::CSector > * p_sector_list = p_scene_light->GetSectorList();
	if( p_sector_list )
	{
		p_sector_list->IterateStart();	
		Nx::CSector *p_sector = p_sector_list->IterateNext();		
		while( p_sector )
		{
			Nx::CGeom *p_geom = p_sector->GetGeom();
			if( p_geom )
			{
				s_recalculate_sector_lighting( p_sector );
			}
			p_sector = p_sector_list->IterateNext();
		}
	}

#	if 0
	// Set up an array of lights, (eventually) culled from the node array.
	s_num_vc_lights = 0;

	Script::CArray *p_nodearray=Script::GetArray("NodeArray");
	for (uint32 i=0; iGetSize(); ++i)
	{
		Script::CStruct *p_node_struct=p_nodearray->GetStructure(i);
		Dbg_MsgAssert(p_node_struct,("Error getting node from node array for rail generation"));

		uint32 class_checksum = 0;		
		p_node_struct->GetChecksum( CRCD(0x12b4e660, "Class"), &class_checksum );
		if (class_checksum == CRCD(0xa0e52802,"LevelLight"))
		{
			SkateScript::GetPosition(p_node_struct,&sp_vc_lights[s_num_vc_lights].m_pos);
			
			sp_vc_lights[s_num_vc_lights].m_value = 100.0f;  		// default values
			sp_vc_lights[s_num_vc_lights].m_radius = 300.0f;
			
			p_node_struct->GetFloat(CRCD(0x2689291c,"Brightness"),&sp_vc_lights[s_num_vc_lights].m_value);	
			p_node_struct->GetFloat(CRCD(0xc48391a5,"Radius"),&sp_vc_lights[s_num_vc_lights].m_radius);	
			
			s_num_vc_lights++;
		}
	}
	Dbg_Message( "Found %d LevelLights\n", s_num_vc_lights );

	if( s_num_vc_lights == 0 )
	{
		Dbg_Message( "Did not find any LevelLight nodes, so using Pedestrian nodes....\n" );

		// Scan through the node array creating all things that need to be created.
		Script::CArray *p_nodearray=Script::GetArray("NodeArray");
		for (uint32 i=0; iGetSize(); ++i)
		{
			Script::CStruct *p_node_struct=p_nodearray->GetStructure(i);
			Dbg_MsgAssert(p_node_struct,("Error getting node from node array for rail generation"));
	
			uint32 class_checksum = 0;		
			p_node_struct->GetChecksum( CRCD(0x12b4e660, "Class"), &class_checksum );
			if (class_checksum == CRCD(0xa0dfac98,"Pedestrian"))
			{
				SkateScript::GetPosition(p_node_struct,&sp_vc_lights[s_num_vc_lights].m_pos);
				sp_vc_lights[s_num_vc_lights].m_value = 100.0f;
				sp_vc_lights[s_num_vc_lights].m_radius = 300.0f;
				s_num_vc_lights++;
			}
		}
	}

	// Go through each object, find the nearest light that intersects the object
	// and the  apply lighting from that
	Lst::HashTable< Nx::CSector > * p_sector_list = p_scene->GetSectorList();
	
	Image::RGBA rgb;
	
	if (p_sector_list)
	{
		p_sector_list->IterateStart();	
		Nx::CSector *p_sector = p_sector_list->IterateNext();		
		while(p_sector)
		{

			Nx::CGeom 	*p_geom = p_sector->GetGeom();
			
			if (p_geom)
			{
				// Need to find a light (if any) such that the radius of the light
				// will intersect the radius of the bounding box of the geometry
				Mth::CBBox bbox = p_sector->GetBoundingBox();
				Mth::Vector	mid = (bbox.GetMax() + bbox.GetMin())/2.0f;
				float	radius = (bbox.GetMax() - bbox.GetMin()).Length();

				int best_light = -1;
				float best_dist = 100000000000.0f;				
				for (int light = 0; light < s_num_vc_lights; light++)
				{
					float dist = (mid - sp_vc_lights[light].m_pos).Length();					
					if (dist < best_dist && dist < (radius+sp_vc_lights[light].m_radius))
					{
						best_dist = dist;
						best_light = light;
					}
				}
				
				if (best_light != -1)
				{
					
					Mth::Vector	light_pos = sp_vc_lights[best_light].m_pos;
					float	light_radius = sp_vc_lights[best_light].m_radius;
					float	light_radius_squared = light_radius * light_radius;
					float	light_value = sp_vc_lights[best_light].m_value * amount;
					
					
					//
					// Do renderable geometry
					int verts = p_geom->GetNumRenderVerts();
				
					if (verts)
					{
						Mth::Vector	*p_verts = new Mth::Vector[verts];
						Image::RGBA	*p_colors = new Image::RGBA[verts];
						p_geom->GetRenderVerts(p_verts);
						// Note: getting the original render colors will allocate lots of memory
						// to store all the origianl colors the firs tiem it is called
						//p_geom->GetOrigRenderColors(p_colors);
						
						// For lighting, we get the current colors, so needs to be applied in conjunction with a compressVC
						p_geom->GetRenderColors(p_colors);
					
						Image::RGBA *p_color = p_colors;
						Mth::Vector *p_vert = p_verts;
						for (int i = 0; i < verts; i++)
						{
				//			CalculateVertexLighting(*p_vert, *p_color);

							float dist_sqr = (*p_vert - light_pos).LengthSqr();
							float intensity = (light_radius_squared-dist_sqr)/light_radius_squared * light_value;
							if (intensity<0.0f)
							{
								intensity = 0.0f;
							}
							
							rgb = *p_color;
							float v;

							// Apply to each r,g,b idividually, so we don't loose original color							
							v = (float) rgb.r + intensity;
							if (v > 255.0f) v = 255.0f;
							rgb.r = (uint8) v; 
							
							v = (float) rgb.g + intensity;
							if (v > 255.0f) v = 255.0f;
							rgb.g = (uint8) v; 
							
							v = (float) rgb.b + intensity;
							if (v > 255.0f) v = 255.0f;
							rgb.b = (uint8) v; 
							
							
							 
							*p_color = rgb;		//(*p_color & 0xff000000) | color;
							//*(uint32*)p_color = Mth::Rnd(32767);		//(*p_color & 0xff000000) | color;
				
							p_color++;
							p_vert++;
						} // end for
				
						p_geom->SetRenderColors(p_colors);
						
						delete [] p_verts;
						delete [] p_colors;
					} // end if
					else
					{
						// debuggery
						//p_geom->SetColor(Image::RGBA(0,0,100,0));
					}
				}
			}
			p_sector = p_sector_list->IterateNext();
		}
	}
#	endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// K: Factored this out so that the FakeLights command could also adjust a set of prefixed nodes.
void CLightManager::sFakeLight(uint32 id, Script::CStruct *pParams)
{
	Nx::CScene *p_scene = Nx::CEngine::sGetMainScene();
	
	float	amount, radius;
	int		red, grn, blu;

	// Get the SVCLight created from this node.
	SVCLight *p_light = s_get_vclight_from_checksum( id );
	if( p_light )
	{
		// Determine the intensity required.
		if( pParams->GetFloat( CRCD(0x9e497fc6,"percent"), &amount ))
		{
			// Want to adjust the intensity. Convert from percentage to [0.0, 1.0].
			amount = amount * 0.01f;

			// Adjust the light intensity for this amount.
			p_light->SetIntensity( amount );
		}

		if( pParams->GetFloat( CRCD(0x999151b,"outer_radius"), &radius ))
		{
			p_light->SetRadius( radius );

			// Changing the radius requires that the sector list be rebuilt.
			p_light->CalculateSectorList();
		}

		if( pParams->GetInteger( CRCD(0x59ea070,"red"), &red ))
		{
			// We must have a green and blue value too.
			pParams->GetInteger( CRCD(0x2f6511de,"green"), &grn, true );
			pParams->GetInteger( CRCD(0x61c9354b,"blue"), &blu, true );

			Image::RGBA col( red, grn, blu, 0xFF );

			// Adjust the light color.
			p_light->SetColor( col );
		}

		s_apply_lighting( p_scene, id );
	}
	else
	{
		// Perhaps there is just a light for objects?
		Nx::CSceneLight *p_obj_light = Nx::CLightManager::sGetSceneLight( id );
		if( p_obj_light )
		{
			// Determine the intensity required.
			if( pParams->GetFloat( CRCD(0x9e497fc6,"percent"), &amount ))
			{
				// Want to adjust the intensity. Convert from percentage to [0.0, 1.0].
				amount = amount * 0.01f;

				// Adjust the light intensity for this amount.
				p_obj_light->SetLightIntensity( amount );
			}

			if( pParams->GetFloat( CRCD(0x999151b,"outer_radius"), &radius ))
			{
				p_obj_light->SetLightRadius( radius );
			}

			if( pParams->GetInteger( CRCD(0x59ea070,"red"), &red ))
			{
				// We must have a green and blue value too.
				pParams->GetInteger( CRCD(0x2f6511de,"green"), &grn, true );
				pParams->GetInteger( CRCD(0x61c9354b,"blue"), &blu, true );

				Image::RGBA col( red, grn, blu, 0xFF );

				// Adjust the light color.
				p_obj_light->SetLightColor( col );
			}
		}
	}
}

}




================================================
FILE: Code/Gfx/NxLightMan.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// NxLightMan.H - Neversoft Engine, Rendering portion, Platform independent interface

#ifndef	__GFX_NX_LIGHT_MAN_H__
#define	__GFX_NX_LIGHT_MAN_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
#include 

#ifndef __CORE_LIST_HASHTABLE_H
#include 
#endif

#ifndef	__GFX_NXSECTOR_H__
#include 
#endif


namespace Script
{
	class CScriptStructure;
	class CScript;
	class CStruct;
}

namespace Nx
{

// Forward declarations
class CModelLights;
class CSector;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CSceneLight
{
public:

	inline void						SetLightPosition( Mth::Vector &pos )	{ m_pos = pos; }
	inline Mth::Vector &			GetLightPosition( void )				{ return m_pos; }

	inline void						SetNameChecksum( uint32 cs )			{ m_name_checksum = cs; }
	inline uint32					GetNameChecksum( void )					{ return m_name_checksum; }

	inline void						SetLightColor( Image::RGBA col )		{ m_color = col; }
	inline Image::RGBA &			GetLightColor( void )					{ return m_color; }

	inline void						SetLightIntensity( float i )			{ m_intensity = i; }
	inline float					GetLightIntensity( void )				{ return m_intensity; }

	inline void						SetLightRadius( float r )				{ m_radius = r; m_reciprocal_radius = 1.0f / r; }
	inline float					GetLightRadius( void )					{ return m_radius; }
	inline float					GetLightReciprocalRadius( void )		{ return m_reciprocal_radius; }

protected:

	Mth::Vector						m_pos;
	float							m_radius;
	float							m_reciprocal_radius;
	float							m_intensity;				// Intensity in range [0.0, 1.0].
	Image::RGBA						m_color;
	uint32							m_name_checksum;			// Provides a link between the light and the node that created it.
};


struct SVCLight
{
	Mth::Vector						m_pos;
	float							m_intensity;		// [0.0, 1.0].
	float							m_radius;
	uint32							m_checksum;			// Checksum of the name of the node that corresponds to this light.
	Image::RGBA						m_color;
	Lst::HashTable< Nx::CSector >	*mp_sector_list;	// List of all the sectors that this light can affect.

									SVCLight();
									~SVCLight();


	void							SetPosition( Mth::Vector & pos )			{ m_pos = pos; }
	void							SetNameChecksum( uint32 cs )				{ m_checksum = cs; }
	void							SetIntensity( float i );
	void							SetColor( Image::RGBA col );
	void							SetRadius( float radius );
	void							ResetSectorList( void );
	void							CalculateSectorList( void );
	bool							AffectsSector( Nx::CSector *p_sector );
	Lst::HashTable< Nx::CSector >	*GetSectorList( void );
};

///////////////////////////////////////////////////////////////////////////////////
// Nx::CLightManager
class	CLightManager
{
public:

	// Constants
	enum
	{
		MAX_LIGHTS				= 2,
		MAX_SCENE_LIGHTS		= 256,	// These are static, designer placed lights in the level.
		MAX_VC_LIGHTS			= 150,
		WORLD_LIGHTS_STACK_SIZE	= 4,
	};

	// Upload lights to the engine (if applicable)
	static void					sUpdateEngine( void );

	// Create and destroy Lights
	static void					sClearLights();

	// World lights
	static bool					sSetLightAmbientColor(const Image::RGBA rgba);
	static Image::RGBA			sGetLightAmbientColor();

	static bool					sSetLightDirection(int light_index, const Mth::Vector &direction);
	static const Mth::Vector &	sGetLightDirection(int light_index);

	static bool					sSetLightDiffuseColor(int light_index, const Image::RGBA rgba);
	static Image::RGBA			sGetLightDiffuseColor(int light_index);

	static void					sSetAmbientLightModulationFactor(float factor);
	static float				sGetAmbientLightModulationFactor();
	static void					sSetDiffuseLightModulationFactor(int light_index, float factor);
	static float				sGetDiffuseLightModulationFactor(int light_index);

	// Push and pop world lights on stack
	static void					sPushWorldLights();
	static bool					sPopWorldLights();

	// Model Lights
	static CModelLights *		sCreateModelLights();
	static bool					sFreeModelLights(CModelLights *p_model_lights);

	// This is here temporarily
	static bool					sSetBrightness(float brightness);

	// Static scene lights.
	static CSceneLight *		sAddSceneLight( void );
	static CSceneLight *		sGetSceneLight( uint32 checksum );
	static CSceneLight *		sGetOptimumSceneLight( Mth::Vector & pos );
	static void					sClearSceneLights( void );

	static void					sClearVCLights();
	static void					sAddVCLight(uint32 name, Mth::Vector &pos, float intensity,
											Image::RGBA col, float outer_radius);

	static void					sClearCurrentFakeLightsCommand();	
	static void					sUpdateVCLights();
	static void					sFakeLight(uint32 id, Script::CStruct *pParams);
	static void					sFakeLights(const uint16 *p_nodes, int numNodes, int period, Script::CStruct *pParams);
	
protected:	

	// Set of lights
	struct SLightSet
	{
		// Ambient and diffuse lights
		Image::RGBA				m_light_ambient_rgba;
		Mth::Vector				m_light_direction[MAX_LIGHTS];
		Image::RGBA				m_light_diffuse_rgba[MAX_LIGHTS];

		// Modulation info
		float					m_ambient_light_modulation_factor;
		float					m_diffuse_light_modulation_factor[MAX_LIGHTS];
	};

	// World lights
	static SLightSet			s_world_lights;						// Current world lights
	static SLightSet			s_world_lights_stack[WORLD_LIGHTS_STACK_SIZE];
	static int					s_world_lights_stack_index;			// index to top of stack (-1 if empty)

	static CSceneLight			s_scene_lights[MAX_SCENE_LIGHTS];
	static int					s_num_scene_lights;

	static SVCLight				sp_vc_lights[MAX_VC_LIGHTS];
	static int					s_num_vc_lights;

	static uint16 *				sp_fake_lights_nodes;
	static int					s_num_fake_lights_nodes;
	static int					s_next_fake_light_node_to_process;
	static int					s_fake_lights_period;
	static int					s_fake_lights_current_time;
	static Script::CStruct *	sp_fake_lights_params;
	
	// These are here temporarily
	static float				s_brightness;
	static float				s_brightness_target;
	static float				s_min_brightness;
	static float				s_ambient_brightness;
	static float				s_diffuse_brightness[MAX_LIGHTS];

	static SVCLight *			s_get_vclight_from_checksum( uint32 cs );
	static void					s_recalculate_sector_lighting( Nx::CSector *p_sector );
	static void					s_apply_lighting( Nx::CScene * p_scene, uint32 checksum = 0 );

	// Platform-specific calls
	static void					s_plat_update_engine();
	static void					s_plat_update_lights();
	static void					s_plat_update_colors();
	static bool					s_plat_set_light_ambient_color();
	static bool					s_plat_set_light_direction(int light_index);
	static bool					s_plat_set_light_diffuse_color(int light_index);
	static Image::RGBA			s_plat_get_light_ambient_color();
	static const Mth::Vector &	s_plat_get_light_direction(int light_index);
	static Image::RGBA			s_plat_get_light_diffuse_color(int light_index);

	static CModelLights *		s_plat_create_model_lights();
	static bool					s_plat_free_model_lights(CModelLights *p_model_lights);

	// Friends
	friend CModelLights;
	friend CSceneLight;
};

bool ScriptSetLightAmbientColor(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetLightDirection(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetLightDiffuseColor(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptGetLightCurrentColor(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptPushWorldLights(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptPopWorldLights(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptDrawDirectionalLightLines(Script::CScriptStructure *pParams, Script::CScript *pScript);

}


#endif



================================================
FILE: Code/Gfx/NxLoadScreen.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxLoadScreen.cpp

// start autoduck documentation
// @DOC NxLoadScreen
// @module NxLoadScreen | None
// @subindex Scripting Database
// @index script | NxLoadScreen

#include 
#include 

#include 
#include 

namespace Nx
{

bool			CLoadScreen::s_active = false;		// Set to true when loading screen is enabled
float			CLoadScreen::s_load_time;			// Amount of time it takes for loading bar to go to 100%
int				CLoadScreen::s_bar_x = 258;			// Bar position
int				CLoadScreen::s_bar_y = 400;
int				CLoadScreen::s_bar_width = 140;		// Bar size
int				CLoadScreen::s_bar_height = 8;
Image::RGBA		CLoadScreen::s_bar_start_color = Image::RGBA(0, 76, 129, 128);
Image::RGBA		CLoadScreen::s_bar_end_color = Image::RGBA(172, 211, 115, 128);
int				CLoadScreen::s_bar_border_width = 5;// Border width
int				CLoadScreen::s_bar_border_height = 5;// Border height
Image::RGBA		CLoadScreen::s_bar_border_color = Image::RGBA(40, 40, 40, 128);



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::sDisplay(const char* filename, bool just_freeze, bool blank)
{
	// Get rid of old stuff, if any
	s_clear();

	s_plat_display(filename, just_freeze, blank);

	//sStartLoadingBar(38.0f);

	s_active = true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::sStartLoadingBar(float seconds)
{
	if (seconds <= 0.0f)
	{
		return;
	}

	s_load_time = seconds;

	s_plat_update_bar_properties();			// Just in case
	s_plat_start_loading_bar(seconds);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void CLoadScreen::sHide()
{
	// Turn off drawing
	s_plat_hide();

	// Get rid of old stuff
	s_clear();

	s_active = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::sSetLoadingBarPos(int x, int y)
{
	s_bar_x = x;
	s_bar_y = y;

	s_plat_update_bar_properties();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::sSetLoadingBarSize(int width, int height)
{
	s_bar_width = width;
	s_bar_height = height;

	s_plat_update_bar_properties();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::sSetLoadingBarStartColor(Image::RGBA color)
{
	s_bar_start_color = color;

	s_plat_update_bar_properties();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::sSetLoadingBarEndColor(Image::RGBA color)
{
	s_bar_end_color = color;

	s_plat_update_bar_properties();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::sSetLoadingBarBorder(int width, int height)
{
	s_bar_border_width = width;
	s_bar_border_height = height;

	s_plat_update_bar_properties();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::sSetLoadingBarBorderColor(Image::RGBA color)
{
	s_bar_border_color = color;

	s_plat_update_bar_properties();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_clear()
{
	s_plat_clear();
}

/////////////////////////////////////////////////////////////////////////////
// The script functions

// @script | SetLoadingBarPos | Sets the position of the loading bar
// @parm int | x | X position of bar
// @parm int | y | Y position of bar
bool ScriptSetLoadingBarPos(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int x, y;
	if (!pParams->GetInteger("x", &x))
	{
		Dbg_MsgAssert(0, ("Can't find 'x' position of bar"));
	}
	if (!pParams->GetInteger("y", &y))
	{
		Dbg_MsgAssert(0, ("Can't find 'y' position of bar"));
	}

	CLoadScreen::sSetLoadingBarPos(x, y);

	return true;
}

// @script | SetLoadingBarSize | Sets the size of the loading bar
// @parm int | width | Width of bar
// @parm int | height | Heigh of bar
bool ScriptSetLoadingBarSize(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int width, height;
	if (!pParams->GetInteger("width", &width))
	{
		Dbg_MsgAssert(0, ("Can't find 'width' of bar"));
	}
	if (!pParams->GetInteger("height", &height))
	{
		Dbg_MsgAssert(0, ("Can't find 'height' of bar"));
	}

	CLoadScreen::sSetLoadingBarSize(width, height);

	return true;
}

// @script | SetLoadingBarStartColor | Sets the color of the left side of the loading bar
// @parm int | r | Red
// @parm int | g | Green
// @parm int | b | Blue
bool ScriptSetLoadingBarStartColor(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int r, g, b;
	if (!pParams->GetInteger("r", &r))
	{
		Dbg_MsgAssert(0, ("Can't find 'r' color"));
	}
	if (!pParams->GetInteger("g", &g))
	{
		Dbg_MsgAssert(0, ("Can't find 'g' color"));
	}
	if (!pParams->GetInteger("b", &b))
	{
		Dbg_MsgAssert(0, ("Can't find 'b' color"));
	}
	
	Image::RGBA rgb(r, g, b, 0x80);

	CLoadScreen::sSetLoadingBarStartColor(rgb);

	return true;
}

// @script | SetLoadingBarEndColor | Sets the color of the right side of the loading bar
// @parm int | r | Red
// @parm int | g | Green
// @parm int | b | Blue
bool ScriptSetLoadingBarEndColor(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int r, g, b;
	if (!pParams->GetInteger("r", &r))
	{
		Dbg_MsgAssert(0, ("Can't find 'r' color"));
	}
	if (!pParams->GetInteger("g", &g))
	{
		Dbg_MsgAssert(0, ("Can't find 'g' color"));
	}
	if (!pParams->GetInteger("b", &b))
	{
		Dbg_MsgAssert(0, ("Can't find 'b' color"));
	}
	
	Image::RGBA rgb(r, g, b, 0x80);

	CLoadScreen::sSetLoadingBarEndColor(rgb);

	return true;
}

// @script | SetLoadingBarBorder | Sets the size of the loading bar border
// @parm int | width | Width of bar border
// @parm int | height | Heigh of bar border
bool ScriptSetLoadingBarBorder(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int width, height;
	if (!pParams->GetInteger("width", &width))
	{
		Dbg_MsgAssert(0, ("Can't find 'width' of bar border"));
	}
	if (!pParams->GetInteger("height", &height))
	{
		Dbg_MsgAssert(0, ("Can't find 'height' of bar border"));
	}

	CLoadScreen::sSetLoadingBarBorder(width, height);

	return true;
}

// @script | SetLoadingBarBorderColor | Sets the color of the loading bar border
// @parm int | r | Red
// @parm int | g | Green
// @parm int | b | Blue
bool ScriptSetLoadingBarBorderColor(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int r, g, b;
	if (!pParams->GetInteger("r", &r))
	{
		Dbg_MsgAssert(0, ("Can't find 'r' color"));
	}
	if (!pParams->GetInteger("g", &g))
	{
		Dbg_MsgAssert(0, ("Can't find 'g' color"));
	}
	if (!pParams->GetInteger("b", &b))
	{
		Dbg_MsgAssert(0, ("Can't find 'b' color"));
	}
	
	Image::RGBA rgb(r, g, b, 0x80);

	CLoadScreen::sSetLoadingBarBorderColor(rgb);

	return true;
}

}



================================================
FILE: Code/Gfx/NxLoadScreen.h
================================================
#ifndef __GFX_NX_LOAD_SCREEN_H__
#define __GFX_NX_LOAD_SCREEN_H__

#include 

namespace Script
{
	class CScriptStructure;
	class CScript;
}

namespace Nx
{

class CLoadScreen
{
public:
	static void					sDisplay(const char* filename, bool just_freeze, bool blank);
	static void					sStartLoadingBar(float seconds);
	static void					sHide();
	static bool					sIsActive() { return s_active; }

	static void					sSetLoadingBarPos(int x, int y);
	static void					sSetLoadingBarSize(int width, int height);
	static void					sSetLoadingBarStartColor(Image::RGBA color);
	static void					sSetLoadingBarEndColor(Image::RGBA color);
	static void					sSetLoadingBarBorder(int width, int height);
	static void					sSetLoadingBarBorderColor(Image::RGBA color);

private:
	static void					s_clear();		// Clears out old memory (called by both Display and Hide)

	// The platform dependent calls
	static void					s_plat_display(const char *filename, bool just_freeze, bool blank);
	static void					s_plat_start_loading_bar(float seconds);
	static void					s_plat_hide();

	static void					s_plat_update_bar_properties();

	static void					s_plat_clear();

	static bool					s_active;			// Set to true when loading screen is enabled
	static float				s_load_time;		// Amount of time it takes for loading bar to go to 100%
	static int					s_bar_x;			// Bar position
	static int					s_bar_y;
	static int					s_bar_width;		// Bar size
	static int					s_bar_height;
	static Image::RGBA			s_bar_start_color;
	static Image::RGBA			s_bar_end_color;
	static int					s_bar_border_width;	// Border width
	static int					s_bar_border_height;// Border height
	static Image::RGBA			s_bar_border_color;
};

bool ScriptSetLoadingBarPos(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetLoadingBarSize(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetLoadingBarStartColor(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetLoadingBarEndColor(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetLoadingBarBorder(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetLoadingBarBorderColor(Script::CScriptStructure *pParams, Script::CScript *pScript);

}

#endif //__GFX_NX_LOAD_SCREEN_H__


================================================
FILE: Code/Gfx/NxMesh.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxMesh.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/15/2002
//****************************************************************************

#include 
#include 
#include 

#include 
#include 

#include 

#ifdef __PLAT_NGC__
#include 
#endif		// __PLAT_NGC__

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions are provided here:
// so engine implementors can leave certain functionality until later
						
/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

// These functions are the platform independent part of the interface to 
// the platform specific code
// parameter checking can go here....
// although we might just want to have these functions inline, or not have them at all?


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMesh::CMesh()
{
	m_CASRemovalMask = 0;

	// In case it isn't loaded below the p-line
	mp_hierarchyObjects = NULL;
	m_numHierarchyObjects = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMesh::~CMesh()
{
	// Remove Collision
	if (mp_coll_objects)
	{
		Pip::Unload(m_coll_filename);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CMesh::LoadCollision(const char *p_name)
{
	
	// for now collision is kind of assumed to be platform independent 
	strcpy(m_coll_filename, p_name);
	char *p_ext = strstr(m_coll_filename, ".");
	if (p_ext)
	{
		strcpy(p_ext, ".col.");
	} else {
		strcat(m_coll_filename, ".col.");
	}
	strcat(m_coll_filename, CEngine::sGetPlatformExtension());

//	Dbg_Message ( "Loading collision %s....", m_coll_filename );

	Mem::PushMemProfile((char*)m_coll_filename);

	uint8 *p_base_addr = (uint8 *) Pip::Load(m_coll_filename);
	if (p_base_addr)
	{
		Nx::CCollObjTriData::SReadHeader *p_header = (Nx::CCollObjTriData::SReadHeader *) p_base_addr;
		p_base_addr += sizeof(Nx::CCollObjTriData::SReadHeader);

//		Dbg_Message ( "Version # %d header sizeof %d", p_header->m_version, sizeof(Nx::CCollObjTriData));
#ifdef __PLAT_NGC__
		Dbg_Message ( "Number of objects: %d verts: %d faces: %d", p_header->m_num_objects, p_header->m_total_num_verts, p_header->m_total_num_faces );
#else
//		Dbg_Message ( "Number of objects: %d verts: %d faces: %d", p_header->m_num_objects, p_header->m_total_num_verts, p_header->m_total_num_faces_large + p_header->m_total_num_faces_small);
//		Dbg_Message ( "Small (%d) verts: %d Large (%d) verts: %d", Nx::CCollObjTriData::GetVertSmallElemSize(), p_header->m_total_num_verts_small, Nx::CCollObjTriData::GetVertElemSize(), p_header->m_total_num_verts_large);
#endif		// __PLAT_NGC__
		Dbg_MsgAssert(p_header->m_version >= 9, ("Collision version must be at least 9."));

		// reserve space for objects
		m_num_coll_objects = p_header->m_num_objects;
		mp_coll_objects = (Nx::CCollObjTriData *) p_base_addr;

		// Calculate base addresses for vert and face arrays
		uint8 *p_base_vert_addr = (uint8 *) (mp_coll_objects + m_num_coll_objects);
#ifndef __PLAT_NGC__
		p_base_vert_addr = (uint8 *)(((uint)(p_base_vert_addr+15)) & 0xFFFFFFF0);	// Align to 128 bit boundary
#ifdef FIXED_POINT_VERTICES
		uint8 *p_base_intensity_addr = p_base_vert_addr + (p_header->m_total_num_verts_large * Nx::CCollObjTriData::GetVertElemSize() +
														   p_header->m_total_num_verts_small * Nx::CCollObjTriData::GetVertSmallElemSize());
		uint8 *p_base_face_addr = p_base_intensity_addr + p_header->m_total_num_verts;
		p_base_face_addr = (uint8 *)(((uint)(p_base_face_addr+3)) & 0xFFFFFFFC);	// Align to 32 bit boundary
#else
		uint8 *p_base_intensity_addr = NULL;
		uint8 *p_base_face_addr = p_base_vert_addr + (p_header->m_total_num_verts * Nx::CCollObjTriData::GetVertElemSize());
		p_base_face_addr = (uint8 *)(((uint)(p_base_face_addr+15)) & 0xFFFFFFF0);	// Align to 128 bit boundary
#endif // FIXED_POINT_VERTICES
#else
		uint8 *p_base_face_addr = p_base_vert_addr + (p_header->m_total_num_faces * Nx::CCollObjTriData::GetVertElemSize());
		p_base_face_addr = (uint8 *)(((uint)(p_base_face_addr+3)) & 0xFFFFFFFC);	// Align to 32 bit boundary
#endif		// __PLAT_NGC__

		// Calculate addresses for BSP arrays
#ifndef __PLAT_NGC__
		uint8 *p_node_array_size = p_base_face_addr + (p_header->m_total_num_faces_large * Nx::CCollObjTriData::GetFaceElemSize() +
													   p_header->m_total_num_faces_small * Nx::CCollObjTriData::GetFaceSmallElemSize());
		p_node_array_size += ( p_header->m_total_num_faces_large & 1 ) ? 2 : 0;
#else
		uint8 *p_node_array_size = p_base_face_addr + ( p_header->m_total_num_faces * Nx::CCollObjTriData::GetFaceElemSize() );
		p_node_array_size += ( p_header->m_total_num_faces & 1 ) ? 2 : 0;
#endif		// __PLAT_NGC__
		uint8 *p_base_node_addr = p_node_array_size + 4;
		uint8 *p_base_face_idx_addr = p_base_node_addr + *((int *) p_node_array_size);

		// Read objects
		for (int oidx = 0; oidx < p_header->m_num_objects; oidx++)
		{
#ifdef __PLAT_NGC__
			CScene * p_scene = static_cast( (static_cast( this ))->GetScene() );
			mp_coll_objects[oidx].InitCollObjTriData(p_scene, p_base_vert_addr, NULL, p_base_face_addr, p_base_node_addr, p_base_face_idx_addr);
#else
			mp_coll_objects[oidx].InitCollObjTriData(NULL, p_base_vert_addr, p_base_intensity_addr, p_base_face_addr,
													 p_base_node_addr, p_base_face_idx_addr);
#endif		// __PLAT_NGC__
			mp_coll_objects[oidx].InitBSPTree();

			// Add to mesh bbox
			m_collision_bbox.AddPoint(mp_coll_objects[oidx].GetBBox().GetMin());
			m_collision_bbox.AddPoint(mp_coll_objects[oidx].GetBBox().GetMax());
		}

//		Dbg_Message("Mesh bounding box: min (%f, %f, %f) max (%f, %f, %f)", 
//					m_collision_bbox.GetMin()[X], m_collision_bbox.GetMin()[Y], m_collision_bbox.GetMin()[Z], 
//					m_collision_bbox.GetMax()[X], m_collision_bbox.GetMax()[Y], m_collision_bbox.GetMax()[Z]);

	} else {
		Dbg_Error ( "Could not open collision file\n" );
		return false;
	}

//	Dbg_Message ( "successfully loaded collision" );

	if (m_num_coll_objects > 0)
	{
#if 0
		// Add to CSectors
		for (int i = 0; i < m_num_coll_objects; i++)
		{
			CSector *p_sector = GetSector(mp_coll_objects[i].GetChecksum());
			if (p_sector)	// Don't assert now since there may not be renderable data
			{
				Dbg_MsgAssert(p_sector, ("LoadCollision: Can't find CSector with checksum %x", mp_coll_objects[i].GetChecksum()));
				p_sector->AddCollSector(&(mp_coll_objects[i]));
			}
		}
#endif
	}

	Mem::PopMemProfile(/*(char*)m_coll_filename*/);

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::CHierarchyObject* CMesh::GetHierarchy()
{
	return mp_hierarchyObjects;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CMesh::GetNumObjectsInHierarchy()
{
	return m_numHierarchyObjects;
}


} // Nx



================================================
FILE: Code/Gfx/NxMesh.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxMesh.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/15/2002
//****************************************************************************

#ifndef	__GFX_NXMESH_H__
#define	__GFX_NXMESH_H__

                           
#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 
#include 

namespace Nx
{
	class CTexDict;
	class CCollObjTriData;
	class CHierarchyObject;

class CMesh : public Spt::Class
{

public:
    // The basic interface to the model
    // this is the machine independent part	
    // machine independent range checking, etc can go here	
	CMesh();
    virtual				~CMesh();

	bool				LoadCollision(const char *p_name);			// load mesh collision data
	
	Nx::CHierarchyObject*		GetHierarchy();
	int							GetNumObjectsInHierarchy();
	Nx::CCollObjTriData*		GetCollisionTriDataArray() const { return mp_coll_objects; }
	int							GetCollisionTriDataArraySize() const { return m_num_coll_objects; }
	const Mth::CBBox &			GetCollisionBBox() const { return m_collision_bbox; }

public:
	Nx::CTexDict*		GetTextureDictionary()
	{
		return mp_texDict;
	}
	uint32				GetRemovalMask()
	{
		return m_CASRemovalMask;
	}

protected:
	Nx::CTexDict*		mp_texDict;

	char						m_coll_filename[128];						// collision filename (kept around for unload)
	int							m_num_coll_objects;							// non-cloned collision
	Nx::CCollObjTriData *		mp_coll_objects;
	Mth::CBBox					m_collision_bbox;							// Bounding box of whole mesh

	uint32						m_CASRemovalMask;

	// For mesh heirarchies
	CHierarchyObject*			mp_hierarchyObjects;						// array of hierarchy objects
	int							m_numHierarchyObjects;						// number of hierarchy objects
};

}

#endif // __GFX_NXMESH_H__




================================================
FILE: Code/Gfx/NxMiscFX.cpp
================================================
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "NxMiscFX.h"


namespace Nx
{

Lst::HashTable< sScreenFlashDetails >		*p_screen_flash_details_table	= NULL;
Lst::HashTable< sSplatInstanceDetails >		*p_splat_details_table			= NULL;
Lst::HashTable< sSplatTrailInstanceDetails >*p_splat_trail_details_table	= NULL;
Lst::HashTable< sShatterInstanceDetails >	*p_shatter_details_table		= NULL;

static const float	DEFAULT_AREA_TEST			= 288.0f;
static const float	DEFAULT_VELOCITY_VARIANCE	= 0.0f;
static const float	DEFAULT_SPREAD_FACTOR		= 1.0f;
static const float	DEFAULT_LIFETIME			= 4.0f;
static const float	DEFAULT_BOUNCE				= -10000.0f;
static const float	DEFAULT_BOUNCE_AMPLITUDE	= 0.8f;

Mth::Vector			shatterVelocity;
float				shatterAreaTest			= DEFAULT_AREA_TEST * DEFAULT_AREA_TEST;
float				shatterVelocityVariance	= DEFAULT_VELOCITY_VARIANCE;
float				shatterSpreadFactor		= DEFAULT_SPREAD_FACTOR;
float				shatterLifetime			= DEFAULT_LIFETIME;
float				shatterBounce			= DEFAULT_BOUNCE;
float				shatterBounceAmplitude	= DEFAULT_BOUNCE_AMPLITUDE;
sTriSubdivideStack	triSubdivideStack;



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int sSplatInstanceDetails::GetOldestSplat( void )
{
	int oldest		= m_lifetimes[0];
	int oldest_idx	= 0;
	
	for( uint32 idx = 0; idx < SPLAT_POLYS_PER_MESH; ++idx )
	{
		if( m_lifetimes[idx] == 0 )
		{
			return idx;
		}
		else if( m_lifetimes[idx] < oldest )
		{
			oldest		= m_lifetimes[idx];
			oldest_idx	= idx;
		}
	}

	// If we get here there wasn't a 'dead' splat, so return the oldest.
	return oldest_idx;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sTriSubdivideStack::Reset( void )
{
	m_offset		= 0;
	m_block_size	= 0;

	memset( m_data, 0x03, TRI_SUBDIVIDE_STACK_SIZE );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sTriSubdivideStack::Clear( void )
{
	m_offset		= 0;
	m_block_size	= 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sTriSubdivideStack::Push( void *p_data )
{
	Dbg_Assert( m_offset + m_block_size < TRI_SUBDIVIDE_STACK_SIZE );
	
	memcpy( m_data + m_offset, p_data, m_block_size );
	m_offset += m_block_size;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sTriSubdivideStack::Pop( void* p_data )
{
	Dbg_Assert( m_offset >= m_block_size );
	
	m_offset -= m_block_size;
	memcpy( p_data, m_data + m_offset, m_block_size );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const void * sTriSubdivideStack::Peek( uint index )
{
	int offset = index * m_block_size;
	Dbg_MsgAssert( offset < m_offset, ("Index %d is beyond end offset %d", index, m_offset) );
	
	return m_data + offset;
}

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sShatterInstanceDetails::sShatterInstanceDetails( int num_tris )
{
	//Dbg_Message("Allocating %d bytes for position arrays of %d tris", (sizeof(Mth::Vector) * 2 + sizeof(Mth::Matrix)) * num_tris, num_tris);

	mp_positions		= new Mth::Vector[num_tris];
	mp_velocities		= new Mth::Vector[num_tris];
	mp_matrices			= new Mth::Matrix[num_tris];
	m_num_triangles		= num_tris;

	m_gravity			= 128.0f;
	m_lifetime			= shatterLifetime;
	m_bounce_level		= shatterBounce;
	m_bounce_amplitude	= shatterBounceAmplitude;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sShatterInstanceDetails::~sShatterInstanceDetails( void )
{
	delete [] mp_positions;
	delete [] mp_velocities;
	delete [] mp_matrices;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sShatterInstanceDetails::UpdateParameters( int index, float timestep )
{
	Dbg_Assert( index < m_num_triangles );
	
	mp_positions[index]	+= mp_velocities[index] * timestep;

	if(( mp_positions[index][Y] < m_bounce_level ) && ( mp_velocities[index][Y] < 0.0f ))
	{
		// Hit the floor. Bounce back up.
		mp_positions[index][Y]	= m_bounce_level + ( m_bounce_level - mp_positions[index][Y] );
		mp_velocities[index][Y]	= mp_velocities[index][Y] * -m_bounce_amplitude;

		// And figure a new rotation matrix.
		Mth::Vector axis( -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), -1.0f + ( 2.0f * (float)rand() / RAND_MAX ));
		axis.Normalize();
		mp_matrices[index].Ident();
		mp_matrices[index].Rotate( axis, 0.1f * ((float)rand() / RAND_MAX ));
	}

	mp_velocities[index][Y]	-= m_gravity * timestep;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int	sShatterInstanceDetails::s_query_memory_needed(int num_tris)
{
	int size = sizeof(sShatterInstanceDetails);
	size += (2 * sizeof(Mth::Vector) + sizeof(Mth::Matrix)) * num_tris;

	return size;
}


/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void MiscFXInitialize( void )
{
	if( p_screen_flash_details_table == NULL )
	{
		p_screen_flash_details_table = new Lst::HashTable< sScreenFlashDetails >( 4 );
	}

	if( p_splat_details_table == NULL )
	{
		p_splat_details_table = new Lst::HashTable< sSplatInstanceDetails >( 4 );
	}

	if( p_splat_trail_details_table == NULL )
	{
		p_splat_trail_details_table = new Lst::HashTable< sSplatTrailInstanceDetails >( 4 );
	}
	
	if( p_shatter_details_table == NULL )
	{
		p_shatter_details_table = new Lst::HashTable< sShatterInstanceDetails >( 4 );
	}
	
	plat_texture_splat_initialize();
	plat_shatter_initialize();

	triSubdivideStack.Reset();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void MiscFXCleanup( void )
{
	// Can cleanup the screen flash details here, since they are not platform specific.
	p_screen_flash_details_table->IterateStart();
	sScreenFlashDetails *p_details = p_screen_flash_details_table->IterateNext();
	while( p_details )
	{
		sScreenFlashDetails *p_delete	= p_details;
		p_details						= p_screen_flash_details_table->IterateNext();

		p_screen_flash_details_table->FlushItem((uint32)p_delete );
		delete p_delete;
	}
	
	// Ditto for the tetxure splat trail details.
	KillAllTextureSplats();

	// Clean up the shatter details.
	plat_shatter_cleanup();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void KillAllTextureSplats( void )
{
	// This was split into a separate function because it can also be used after turning geometry off,
	// to ensure no splats are 'floating'.
	p_splat_trail_details_table->IterateStart();
	sSplatTrailInstanceDetails *p_trail_details = p_splat_trail_details_table->IterateNext();
	while( p_trail_details )
	{
		sSplatTrailInstanceDetails *p_delete	= p_trail_details;
		p_trail_details							= p_splat_trail_details_table->IterateNext();

		p_splat_trail_details_table->FlushItem((uint32)p_delete );
		delete p_delete;
	}
	
	// Call the platform specific cleanup function.
	plat_texture_splat_cleanup();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void AddScreenFlash( int viewport, Image::RGBA from, Image::RGBA to, float duration, float z, uint32 flags, const char *p_texture_name )
{
	Replay::WriteScreenFlash(viewport, from, to, duration, z, flags);
	
	// Ensure the supplied viewport is within bounds.
#ifdef __NOPT_ASSERT__
	int num_viewports = CViewportManager::sGetNumActiveViewports();
	Dbg_Assert( viewport < num_viewports );
#endif		// __NOPT_ASSERT__

	// Resolve the texture name if present.
	CTexture *p_texture = NULL;
	if( p_texture_name )
	{
	}

	// Store these details.
	sScreenFlashDetails *p_flash = new sScreenFlashDetails;
	p_flash->m_from		= from;
	p_flash->m_to		= to;
	p_flash->m_duration	= duration;
	p_flash->m_lifetime	= duration;
	p_flash->m_z		= z;
	p_flash->m_flags	= flags;
	p_flash->mp_texture	= p_texture;
	p_flash->m_viewport	= viewport;

	p_screen_flash_details_table->PutItem((uint32)p_flash, p_flash );

}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void ScreenFlashUpdate( void )
{
	float framelength = Tmr::FrameLength();

	if( p_screen_flash_details_table )
	{
		p_screen_flash_details_table->IterateStart();
		sScreenFlashDetails *p_details = p_screen_flash_details_table->IterateNext();
		while( p_details )
		{
			sScreenFlashDetails *p_delete = NULL;

			// Don't process if paused.
			if( !Mdl::FrontEnd::Instance()->GamePaused() || ( p_details->m_flags & Nx::SCREEN_FLASH_FLAG_IGNORE_PAUSE ))
			{
				p_details->m_lifetime -= framelength;
				if( p_details->m_lifetime > 0.0f )
				{
					// Calculate the current color.
					float mult				= p_details->m_lifetime / p_details->m_duration;
					p_details->m_current.r	= p_details->m_to.r + (int)(((float)p_details->m_from.r - (float)p_details->m_to.r ) * mult );
					p_details->m_current.g	= p_details->m_to.g + (int)(((float)p_details->m_from.g - (float)p_details->m_to.g ) * mult );
					p_details->m_current.b	= p_details->m_to.b + (int)(((float)p_details->m_from.b - (float)p_details->m_to.b ) * mult );
					p_details->m_current.a	= p_details->m_to.a + (int)(((float)p_details->m_from.a - (float)p_details->m_to.a ) * mult );
				}
				else
				{
					p_delete = p_details;
				}
			}	

			p_details = p_screen_flash_details_table->IterateNext();

			if( p_delete )
			{
				p_screen_flash_details_table->FlushItem((uint32)p_delete );
				delete p_delete;
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void ScreenFlashRender( int viewport, uint32 flags )
{
	p_screen_flash_details_table->IterateStart();
	sScreenFlashDetails *p_details = p_screen_flash_details_table->IterateNext();
	while( p_details )
	{
		if( viewport == p_details->m_viewport )
		{
			if(( flags & Nx::SCREEN_FLASH_FLAG_BEHIND_PANEL ) == ( p_details->m_flags & Nx::SCREEN_FLASH_FLAG_BEHIND_PANEL ))
			{
				plat_screen_flash_render( p_details );
			}
		}
		p_details = p_screen_flash_details_table->IterateNext();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void TextureSplatUpdate( void )
{
	// Don't process if paused.
	if( Mdl::FrontEnd::Instance()->GamePaused() && !Replay::RunningReplay())
	{
		return;
	}	

	if( p_splat_details_table )
	{
		int framelength_in_ms = (int)( Tmr::FrameLength() * 1000.0f );
		
		p_splat_details_table->IterateStart();
		sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
		while( p_details )
		{
			// Initialize to -1 to indicate no active splats so far.
			p_details->m_highest_active_splat = -1;
			
			for( int i = 0; i < SPLAT_POLYS_PER_MESH; ++i )
			{
				if( p_details->m_lifetimes[i] > 0 )
				{
					p_details->m_lifetimes[i] -= framelength_in_ms;
					if( p_details->m_lifetimes[i] <= 0 )
					{
						// Make sure to set lifetime back to exactly zero to indicate ready for re-use.
						p_details->m_lifetimes[i] = 0;

						// This splat has just 'expired'. Reset this poly.
						plat_texture_splat_reset_poly( p_details, i );
					}
					else
					{
						p_details->m_highest_active_splat = i;
					}
				}
			}
			p_details = p_splat_details_table->IterateNext();
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool TextureSplat( Mth::Vector& splat_start, Mth::Vector& splat_end, float size, float lifetime, const char *p_texture_name, uint32 trail )
{
	Replay::WriteTextureSplat(splat_start,splat_end,size,lifetime,p_texture_name,trail);
	
	// If this is a flagged as a trail splat, see if we have a trail instance already that matches.
	sSplatTrailInstanceDetails *p_trail_details = NULL;
	if( trail > 0 )
	{
		uint32 time = Tmr::GetTime();

		p_splat_trail_details_table->IterateStart();
		p_trail_details = p_splat_trail_details_table->IterateNext();
		while( p_trail_details )
		{
			sSplatTrailInstanceDetails *p_delete = NULL;

			if(( time < p_trail_details->m_last_pos_added_time ) || (( time - p_trail_details->m_last_pos_added_time ) > 500 ))
			{
				// This trail is over half a second old - remove it.
				p_delete = p_trail_details;
			}
			else
			{
				// This trail had a point added within the last half second, so check it for bone ID.
				if( p_trail_details->m_trail_id == trail )
				{
					// ID's match, so check it for proximity. Seems like fast moving reverts can cause successive calls to be up to 5.5 feet away.
					float sq_dist = ( splat_start - p_trail_details->m_last_pos ).LengthSqr();
					if( sq_dist < ( 66.0f * 66.0f ))
					{
						// This trail had a point added within the last half second that is within 3 feet of this point. Select this trail.
						break;
					}
				}
			}
			
			p_trail_details = p_splat_trail_details_table->IterateNext();

			if( p_delete )
			{
				p_splat_trail_details_table->FlushItem((uint32)p_delete );
				delete p_delete;
			}
		}

		// If there were no trail details, create a new instance and just return.
		if( p_trail_details == NULL )
		{
			p_trail_details = new sSplatTrailInstanceDetails;
			p_trail_details->m_trail_id				= trail;
			p_trail_details->m_last_pos				= splat_start;
			p_trail_details->m_last_pos_added_time	= time;
			p_splat_trail_details_table->PutItem((uint32)p_trail_details, p_trail_details );
			return true;
		}
	}
	
	// Convert the name string to a checksum.
	uint32 texture_checksum = Crc::GenerateCRCFromString( p_texture_name );
	
	// Obtain a pointer to the texture.
	Nx::CTexture *p_texture	= Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	Dbg_MsgAssert( p_texture, ("Couldn't find texture %s in particle texture dictionary", p_texture_name) );

	Mth::Line is( splat_start, splat_end );

	// Make initial line bounding box.
	Mth::CBBox line_bbox( is.m_start );
	line_bbox.AddPoint( is.m_end );
	
	// Get a list of all collision sectors within all super sectors that the line intersects.
	// This is a very fast call, but likely to provide many collision sectors that are not actually within the
	// bounding box of the line.
	SSec::Manager	*ss_man				= Nx::CEngine::sGetNearestSuperSectorManager( is );
	Nx::CCollStatic	**pp_coll_obj_list	= ss_man->GetIntersectingCollSectors( is );

	// Can't create blood splat if no objects found.
	if( !pp_coll_obj_list )
	{
		return false;
	}

	// The sector table is used to pass a platform independent set of CSector pointers to the below p-line code.
	// Garrett: Also added static collision table, just in case CSector points to a CCollMulti
	#define SECTOR_TABLE_SIZE		127
	static CSector* sector_table[SECTOR_TABLE_SIZE + 1];
	static Nx::CCollStatic* collision_table[SECTOR_TABLE_SIZE + 1];

	int				sector_table_index = 0;
	Nx::CCollStatic *p_coll_obj;
	while(( p_coll_obj = *pp_coll_obj_list ))
	{
		if( p_coll_obj->GetGeometry())
		{
			// Determine whether this collision sector actually falls within the bounding box of our line.
			if( p_coll_obj->GetGeometry()->GetBBox().Intersect( line_bbox ))
			{
				uint32 checksum = p_coll_obj->GetChecksum();

				// From the checksum we can get the CSector (the renderable version) from the hash table of the scene.
				Nx::CSector *p_sector = Nx::CEngine::sGetSector( checksum );

				// Store the pointer in the table.
				Dbg_Assert( sector_table_index < SECTOR_TABLE_SIZE );
				collision_table[sector_table_index] = p_coll_obj;
				sector_table[sector_table_index++] = p_sector;
			}
			++pp_coll_obj_list;
		}
	}

	// Mark the end of the sector table with a NULL pointer.
	if( sector_table_index > 0 )
	{
		sector_table[sector_table_index] = NULL;
		collision_table[sector_table_index] = NULL;

		// Make sure we can can calculate an up vector.  If not, just move on.
		bool rv;
		if ((trail == 0) || (splat_start[X] != p_trail_details->m_last_pos[X]) || (splat_start[Z] != p_trail_details->m_last_pos[Z]))
		{
			rv = plat_texture_splat( sector_table, collision_table, splat_start, splat_end, size, lifetime, p_texture, p_trail_details );
		} else {
			rv = false;
		}

		// Update the trail details if present.
		if( p_trail_details )
		{
			p_trail_details->m_last_pos	= splat_start;
			p_trail_details->m_last_pos_added_time	= Tmr::GetTime();
		}

		return rv;
	}

	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void TextureSplatRender( void )
{

	
	plat_texture_splat_render();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void ShatterSetParams( Mth::Vector& velocity, float area_test, float velocity_variance, float spread_factor, float lifetime, float bounce, float bounce_amplitude )
{
	Replay::WriteShatterParams(velocity, area_test, velocity_variance, spread_factor, lifetime, bounce, bounce_amplitude);

	shatterVelocity			= velocity;
	shatterAreaTest			= ( area_test == 0.0f ) ? ( DEFAULT_AREA_TEST * DEFAULT_AREA_TEST ) : ( area_test * area_test );
	shatterVelocityVariance	= ( velocity_variance == 0.0f ) ? DEFAULT_VELOCITY_VARIANCE : velocity_variance;
	shatterSpreadFactor		= ( spread_factor == 0.0f ) ? DEFAULT_SPREAD_FACTOR : spread_factor;
	shatterLifetime			= ( lifetime == 0.0f ) ? DEFAULT_LIFETIME : lifetime;
	shatterBounce			= ( bounce == 0.0f ) ? DEFAULT_BOUNCE : bounce;
	shatterBounceAmplitude	= ( bounce_amplitude == 0.0f ) ? DEFAULT_BOUNCE_AMPLITUDE : bounce_amplitude;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Shatter( CGeom *p_geom )
{
	plat_shatter( p_geom );

	// After shattering the subdivision stack should be empty.
	Dbg_Assert( triSubdivideStack.IsEmpty());
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void ShatterUpdate( void )
{
	// Don't process if paused.
	if( Mdl::FrontEnd::Instance()->GamePaused() && !Replay::RunningReplay())
	{
		return;
	}	
	if (Replay::Paused())
	{
		return;
	}
		
	if( p_shatter_details_table )
	{
		float framelength = Tmr::FrameLength();
		
		p_shatter_details_table->IterateStart();
		sShatterInstanceDetails *p_details = p_shatter_details_table->IterateNext();
		while( p_details )
		{
			sShatterInstanceDetails *p_delete = NULL;
			
			p_details->m_lifetime -= framelength;
			if( p_details->m_lifetime <= 0.0f )
			{
				// Remove this entry from the table and destroy at the bottom of the loop.
				p_delete = p_details;
			}
			else
			{
				plat_shatter_update( p_details, framelength );
			}
			p_details = p_shatter_details_table->IterateNext();

			if( p_delete )
			{
				p_shatter_details_table->FlushItem((uint32)p_delete );
				delete p_delete;
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void ShatterRender( void )
{
	if( p_shatter_details_table )
	{
		p_shatter_details_table->IterateStart();
		sShatterInstanceDetails *p_details = p_shatter_details_table->IterateNext();
		while( p_details )
		{
			plat_shatter_render( p_details );
			p_details = p_shatter_details_table->IterateNext();
		}
	}
}


///////////////////////////////////////////////////////////////////
//
// FOG
//
///////////////////////////////////////////////////////////////////

bool		CFog::s_enabled = false;
float		CFog::s_near_distance = 3000.0f;
float		CFog::s_exponent = 10.0f;
Image::RGBA	CFog::s_rgba(0x60, 0x60, 0x80, 0x66);

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFog::sEnableFog(bool enable)
{
	s_enabled = enable;
	s_plat_enable_fog(enable);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFog::sSetFogNearDistance(float distance)
{
	s_near_distance = distance;
	s_plat_set_fog_near_distance(distance);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFog::sSetFogExponent(float exponent)
{
	s_exponent = exponent;
	s_plat_set_fog_exponent(exponent);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFog::sSetFogRGBA(Image::RGBA rgba)
{
	s_rgba = rgba;
	s_plat_set_fog_rgba(rgba);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFog::sSetFogColor( void )
{
	s_plat_set_fog_color();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CFog::sFogUpdate( void )
{
	s_plat_fog_update();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CFog::sIsFogEnabled()
{
	return s_enabled;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float		CFog::sGetFogNearDistance()
{
	return s_near_distance;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float		CFog::sGetFogExponent()
{
	return s_exponent;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA	CFog::sGetFogRGBA()
{
	return s_rgba;
}

} // Nx


================================================
FILE: Code/Gfx/NxMiscFX.h
================================================
#ifndef	__GFX_NXMISCFX_H__
#define	__GFX_NXMISCFX_H__

#include 

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
enum eScreenFlashFlags
{
	SCREEN_FLASH_FLAG_BEHIND_PANEL	= 0x01,
	SCREEN_FLASH_FLAG_ADDITIVE		= 0x02,
	SCREEN_FLASH_FLAG_SUBTRACTIVE	= 0x04,
	SCREEN_FLASH_FLAG_IGNORE_PAUSE	= 0x08
};
	

	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sScreenFlashDetails
{
	int			m_viewport;
	Image::RGBA	m_from;
	Image::RGBA	m_to;
	Image::RGBA	m_current;
	uint32		m_flags;
	float		m_duration;
	float		m_lifetime;
	float		m_z;
	CTexture*	mp_texture;
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CFog
{
public:
	static void			sEnableFog(bool enable);
	static void			sSetFogExponent(float exponent);
	static void			sSetFogNearDistance(float distance);
	static void			sSetFogRGBA(Image::RGBA rgba);
	static void			sSetFogColor( void );
	static void			sFogUpdate( void );

	static bool			sIsFogEnabled();
	static float		sGetFogNearDistance();
	static float		sGetFogExponent();
	static Image::RGBA	sGetFogRGBA();

private:
	static void			s_plat_enable_fog(bool enable);
	static void			s_plat_set_fog_near_distance(float distance);
	static void			s_plat_set_fog_exponent(float exponent);
	static void			s_plat_set_fog_rgba(Image::RGBA rgba);
	static void			s_plat_set_fog_color( void );
	static void			s_plat_fog_update( void );

	// Keep the numbers here, just in case they need to be mangled below the p-line
	static bool			s_enabled;
	static float		s_near_distance;
	static float		s_exponent;
	static Image::RGBA	s_rgba;
};


#define SPLAT_POLYS_PER_MESH	256
	
/******************************************************************/
/*                                                                */
/* Platform independent structure for holding details on a set    */
/* of texture splats. The platform-specific code will most likely */
/* derive a structure from this to maintain additional details.   */
/*                                                                */
/******************************************************************/
struct sSplatInstanceDetails
{
	int					m_lifetimes[SPLAT_POLYS_PER_MESH];
	int					m_highest_active_splat;				// Can be used to dynamically optimise rendering lists.
	int					GetOldestSplat( void );
};



/******************************************************************/
/*                                                                */
/* Platform independent structure for holding details on a single */
/* trail texture splats.										  */
/*                                                                */
/******************************************************************/
struct sSplatTrailInstanceDetails
{
	Mth::Vector			m_last_pos;
	uint32				m_trail_id;				// Keyed from the bone checksum.
	uint32				m_last_pos_added_time;	// In milliseconds.
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sShatterInstanceDetails
{
						sShatterInstanceDetails( int num_tris );
	virtual				~sShatterInstanceDetails( void );
	void				UpdateParameters( int index, float timestep );
	
	Mth::Vector*		mp_positions;
	Mth::Vector*		mp_velocities;
	Mth::Vector*		mp_normals;    // For 'fake' lighting.
	Mth::Matrix*		mp_matrices;
	float				m_lifetime;	
	float				m_gravity;	
	float				m_bounce_level;
	float				m_bounce_amplitude;
	int					m_num_triangles;

protected:
	static int			s_query_memory_needed(int num_tris);
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sTriSubdivideStack
{
	static const int	TRI_SUBDIVIDE_STACK_SIZE	= 16 * 1024;

	void				Reset( void );
	void				Clear( void );
	bool				IsEmpty( void )				{ return m_offset == 0; }
	void				SetBlockSize( int size )	{ m_block_size = size; }
	int					GetBlockSize( void )		{ return m_block_size; }
	void				Pop( void *p_data );
	void				Push( void *p_data );
	const void *		Peek( uint index );

	private:	
	int					m_offset;
	int					m_block_size;
	char				m_data[TRI_SUBDIVIDE_STACK_SIZE];
};



extern Lst::HashTable< sSplatInstanceDetails >			*p_splat_details_table;
extern Lst::HashTable< sSplatTrailInstanceDetails >		*p_splat_trail_details_table;
extern Lst::HashTable< sShatterInstanceDetails >		*p_shatter_details_table;
extern Mth::Vector										shatterVelocity;
extern float											shatterAreaTest;
extern float											shatterVelocityVariance;
extern float											shatterSpreadFactor;
extern float											shatterLifetime;
extern float											shatterBounce;

extern sTriSubdivideStack								triSubdivideStack;

void MiscFXInitialize( void );
void MiscFXCleanup( void );

void AddScreenFlash( int viewport, Image::RGBA from, Image::RGBA to, float duration, float z, uint32 flags, const char *p_texture_name );
void ScreenFlashUpdate( void );
void ScreenFlashRender( int viewport, uint32 flags );

void TextureSplatRender( void );
void TextureSplatUpdate( void );
bool TextureSplat( Mth::Vector& splat_start, Mth::Vector& splat_end, float size, float lifetime, const char *p_texture_name, uint32 trail = 0 );
void KillAllTextureSplats( void );

void ShatterSetParams( Mth::Vector& velocity, float area_test, float velocity_variance, float spread_factor, float lifetime, float bounce, float bounce_amplitude );
void Shatter( CGeom *p_geom );
void ShatterUpdate( void );
void ShatterRender( void );

void plat_screen_flash_render( sScreenFlashDetails *p_details );

void plat_texture_splat_initialize( void );
void plat_texture_splat_cleanup( void );
void plat_texture_splat_render( void );
void plat_texture_splat_reset_poly( sSplatInstanceDetails *p_details, int index );
bool plat_texture_splat( Nx::CSector **pp_sectors, Nx::CCollStatic **pp_collision, Mth::Vector& start, Mth::Vector& end, float size, float lifetime, Nx::CTexture *p_texture, Nx::sSplatTrailInstanceDetails *p_trail_details = NULL );

void plat_shatter_initialize( void );
void plat_shatter_cleanup( void );
void plat_shatter( CGeom *p_geom );
void plat_shatter_update( sShatterInstanceDetails *p_details, float framelength );
void plat_shatter_render( sShatterInstanceDetails *p_details );

} // Nx

#endif // __GFX_NXMISCFX_H__


================================================
FILE: Code/Gfx/NxModel.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxModel.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/21/2001
//****************************************************************************

#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

namespace Nx
{
	
/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions are provided here:
// so engine implementors can leave certain functionality until later

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CModel::plat_init_skeleton( int numBones )
{
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CModel::plat_set_render_mode(ERenderMode mode)
{
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CModel::plat_set_color(uint8 r, uint8 g, uint8 b, uint8 a)
{
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CModel::plat_set_visibility(uint32 mask)
{
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CModel::plat_set_active(bool active)
{
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CModel::plat_set_scale(float scaleFactor)
{
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CModel::plat_render(Mth::Matrix* pRootMatrix, Mth::Matrix* ppBoneMatrices, int numBones)
{
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CModel::plat_replace_texture(char* p_srcFileName, char* p_dstFileName)
{
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CModel::plat_prepare_materials( void )
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CModel::plat_refresh_materials( void )
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CModel::plat_get_bounding_sphere()
{
	if ( m_numBones > 0 )
	{
		// the original hack for skinned models
		return Mth::Vector(0.0f, 36.0f, 0.0f, 48.0f);		
	}
	else
	{
		// the original hack for non-skinned models
		return Mth::Vector(0.0f, 36.0f, 0.0f, 1200.0f);		
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModel::plat_set_bounding_sphere( const Mth::Vector& boundingSphere )
{
	// Do nothing by default... 
	// (this is only needed by the PS2 version...)

	// TODO:  Set the appropriate bounding sphere that will be returned
	// by plat_get_bounding_sphere...
}

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

// These functions are the platform independent part of the interface to 
// the platform specific code
// parameter checking can go here....
// although we might just want to have these functions inline, or not have them at all?

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModel::CModel()
{
    m_numBones = 0;
    mp_skeletonMatrices = NULL;

    SetRenderMode( vNONE );

	m_numGeoms = 0;

	m_primaryMeshName = CRCD(0x6607dd3e,"Uninitialized");

	m_active = true;
	m_hidden = false;
	m_scale = Mth::Vector( 1.0f, 1.0f, 1.0f );
	m_scalingEnabled = false;
	m_shadowEnabled = false;
	m_doShadowVolume = false;

	mp_model_lights = NULL;

	m_boundingSphereCached = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModel::~CModel()
{
	if (mp_model_lights)
	{
		CLightManager::sFreeModelLights(mp_model_lights);
	}

	ClearGeoms();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*

// GJ 10/23/02:  This function didn't seem to be used, 
// so I commented it out for now

void CModel::ResetScale()
{
	SetScale( Mth::Vector( 1.0f, 1.0f, 1.0f ) );

	if ( mp_skeleton )
	{
		mp_skeleton->ResetScale();
	}
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::SetSkeleton( Gfx::CSkeleton* pSkeleton )
{

	if (!mp_skeletonMatrices)
	{
		m_numBones = pSkeleton->GetNumBones();
	
		plat_init_skeleton( pSkeleton->GetNumBones() );
	
		mp_skeletonMatrices = pSkeleton->GetMatrices();
	}
	else
	{
		Dbg_MsgAssert(m_numBones == pSkeleton->GetNumBones(),("Mismatch in number of bones"));
		
	}

    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::Render( Mth::Matrix* pMatrix, bool no_anim, Gfx::CSkeleton* pSkeleton )
{
	// don't display the skin, unless we're
	// in the standard "textured" mode
	bool is_textured = m_renderMode == vTEXTURED; 

	int numGeoms = GetNumGeoms();
	for ( int i = 0; i < numGeoms; i++ )
	{
		Dbg_Assert( GetGeomByIndex(i) );
		GetGeomByIndex(i)->SetActive( is_textured && m_active && (m_geomActiveMask&(1<ScaleLocal(m_scale);
	}
       
	// make sure the skeleton matches up...
	if ( pSkeleton )					    
	{
		Dbg_MsgAssert( m_numBones != 0, ( "Trying to update a model with incorrect number of bones (expected %d, found %d)", m_numBones, pSkeleton->GetNumBones() ) );
		Dbg_Assert( mp_skeletonMatrices );
	}
	else
	{
		Dbg_MsgAssert( m_numBones == 0, ( "Trying to update a model with incorrect number of bones (expected %d, found %d)", 0, m_numBones ) );
		Dbg_Assert( mp_skeletonMatrices == NULL );
	}

    switch ( m_renderMode )
    {
        case vTEXTURED:
        {
			if ( pSkeleton )
            {
				(*pMatrix)[Mth::RIGHT][W] = 0.0f;
				(*pMatrix)[Mth::UP][W] = 0.0f;
				(*pMatrix)[Mth::AT][W] = 0.0f;
				(*pMatrix)[Mth::POS][W] = 1.0f;

				int numGeoms = GetNumGeoms();
								
				if ( no_anim )
				{
					// update root position without updating bones
					for ( int i = 0; i < numGeoms; i++ )
					{
						Dbg_Assert( GetGeomByIndex(i) );
						GetGeomByIndex(i)->Render( pMatrix, NULL, 0 );
					}
				}
				else
				{
 					// update both root position AND bones
					for ( int i = 0; i < numGeoms; i++ )
					{
						Dbg_Assert( GetGeomByIndex(i) );
						GetGeomByIndex(i)->Render( pMatrix, pSkeleton->GetMatrices(), pSkeleton->GetNumBones() );
					}	
				}
			}
			else
			{
				// the following should NOT be necessary,
				// but it is.
				(*pMatrix)[Mth::RIGHT][W] = 0.0f;
				(*pMatrix)[Mth::UP][W] = 0.0f;
				(*pMatrix)[Mth::AT][W] = 0.0f;
				(*pMatrix)[Mth::POS][W] = 1.0f;
				
				int numGeoms = GetNumGeoms();
				for ( int i = 0; i < numGeoms; i++ )
				{
					Dbg_Assert( GetGeomByIndex(i) );
					GetGeomByIndex(i)->Render( pMatrix, NULL, 0 );
				}
			}
        }
        break;
        
		case vSKELETON:
		{
            if ( pSkeleton )
            {
				pSkeleton->Display( pMatrix, 0.5f, 0.5f, 1.0f );
			}
		}
		break;

        case vBBOX:
        default:
        {
            Mth::Matrix matrix = *pMatrix;

            // really, the AddDebugBox function should be
            // able to draw with only one matrix, rather
            // than matrix+pos.
            matrix[Mth::POS] = Mth::Vector( 0, 0, 0 );

            Mth::Vector pos = pMatrix->GetPos();

            // set up bounding box
            SBBox theBox;
            theBox.m_max.Set(10.0f, 10.0f, 10.0f);
            theBox.m_min.Set(-10.0f, -10.0f, -10.0f);    

            // For now, draw a bounding box
            Gfx::AddDebugBox( matrix, pos, &theBox, NULL, 1, NULL ); 
		}
        break;
        
		case vGOURAUD:
        case vFLAT:
        case vWIREFRAME:
            Dbg_Assert( 0 );
        break;

        case vNONE:
            // draw nothing...
            break;
        
    }

    return true;
}
                            
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::SetBoneMatrixData( Gfx::CSkeleton* pSkeleton )
{
	// GJ:  X-box doesn't copy over bone matrix data once per frame
	// like the PS2 does...  so the following allows me to hijack the
	// skater's model matrices and copy over the matrices
	// from a different model... (for skater substitution in cutscenes,
	// and perhaps for the bail board)

	Dbg_Assert( pSkeleton );
	
	int numGeoms = GetNumGeoms();
	for ( int i = 0; i < numGeoms; i++ )
	{
		Dbg_Assert( GetGeomByIndex(i) );
		GetGeomByIndex(i)->SetBoneMatrixData( pSkeleton->GetMatrices(), pSkeleton->GetNumBones() );
	}  

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::AddGeom(uint32 assetName, uint32 geomName, bool supportMultipleMaterialColors)
{
	// now that a new geom has been added to the model,
	// consider the bounding sphere "dirty"
	m_boundingSphereCached = false;

	// for now, assume that if we're using this method of creating assets,
	// then we'll be using the asset manager and a texDictOffset of 0
    
	bool success = false;

	Dbg_MsgAssert( m_numGeoms < MAX_GEOMS, ( "Too many geoms for this model (max=%d)", MAX_GEOMS ) );

	// the asset must already exist in the assman by this point!
	// note that we're not loading the asset, only getting it
	Ass::CAssMan * ass_man = Ass::CAssMan::Instance();

	Nx::CMesh* pMesh = NULL;
	pMesh = (Nx::CMesh*)ass_man->GetAsset( assetName, false );
	if( !pMesh )
	{
		Dbg_MsgAssert( false,( "Could not get asset %08x", assetName ));
	}	   		   

	m_preloadedMeshNames[m_numPreloadedMeshes] = assetName;
	m_numPreloadedMeshes++;

	mp_geom[m_numGeoms] = Nx::CEngine::sInitGeom();

	if ( mp_geom[m_numGeoms] )
	{
		mp_geom[m_numGeoms]->LoadGeomData(pMesh, this, supportMultipleMaterialColors);
		mp_geomTexDict[m_numGeoms] = pMesh->GetTextureDictionary();
		mp_geom[m_numGeoms]->EnableShadow(m_shadowEnabled);
		m_geomName[m_numGeoms] = geomName;
		m_geomActiveMask |= (1<SetModelLights(mp_model_lights);
		}

		m_numGeoms++;
		
		SetRenderMode( vTEXTURED );
	}

	// TODO:  The mesh file name represents
	// mesh #0, but the mesh file name is
	// already kind of kludge that will be
	// cleaned up when CGeoms are properly
	// implemented.
	if ( m_numGeoms == 1 )
	{
		// doesn't have a filename because it came from a stream...
		m_primaryMeshName = Script::GenerateCRC("data_from_stream");
	}
    
	return success;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::AddGeom(const char* pMeshFileName, uint32 geomName, bool useAssetManager, uint32 texDictOffset, bool forceTexDictLookup, bool supportMultipleMaterialColors)
{
	// now that a new geom has been added to the model,
	// consider the bounding sphere "dirty"
	m_boundingSphereCached = false;

    bool success = false;

	Dbg_MsgAssert( m_numGeoms < MAX_GEOMS, ( "Too many geoms for this model (max=%d)", MAX_GEOMS ) );

	// grabs the data from the asset manager if it's not already loaded
	Ass::CAssMan * ass_man = Ass::CAssMan::Instance();

	Nx::CMesh* pMesh = NULL;
	if ( useAssetManager )
	{
		Ass::SSkinAssetLoadContext theContext;
		theContext.forceTexDictLookup = forceTexDictLookup;
		theContext.doShadowVolume = m_doShadowVolume;
		theContext.texDictOffset = texDictOffset;

		pMesh = (Nx::CMesh*)ass_man->LoadOrGetAsset( pMeshFileName, false, false, ass_man->GetDefaultPermanent(), 0, &theContext );
		if( !pMesh )
		{
			Dbg_MsgAssert( false,( "Could not load asset %s", pMeshFileName ));
		}	   		   

		m_preloadedMeshNames[m_numPreloadedMeshes] = Script::GenerateCRC(pMeshFileName);
		m_numPreloadedMeshes++;
	}
	else
	{
		Mem::PushMemProfile((char*)pMeshFileName);
		pMesh = Nx::CEngine::sLoadMesh( pMeshFileName, texDictOffset, forceTexDictLookup, m_doShadowVolume );
		Mem::PopMemProfile(/*(char*)pMeshFileName*/);
		Dbg_MsgAssert( m_numMeshes < MAX_MESHES, ( "Too many meshes for this model (max=%d)", MAX_MESHES ) );
		mp_mesh[m_numMeshes] = pMesh;
		m_numMeshes++;
	}

	mp_geom[m_numGeoms] = Nx::CEngine::sInitGeom();

	if ( mp_geom[m_numGeoms] )
	{
		mp_geom[m_numGeoms]->LoadGeomData(pMesh, this, supportMultipleMaterialColors);
		mp_geomTexDict[m_numGeoms] = pMesh->GetTextureDictionary();
		mp_geom[m_numGeoms]->EnableShadow(m_shadowEnabled);
		m_geomName[m_numGeoms] = geomName;
		m_geomActiveMask |= (1<SetModelLights(mp_model_lights);
		}

		m_numGeoms++;
		
		SetRenderMode( vTEXTURED );
	}

	// TODO:  The mesh file name represents
	// mesh #0, but the mesh file name is
	// already kind of kludge that will be
	// cleaned up when CGeoms are properly
	// implemented.
	if ( m_numGeoms == 1 )
	{
		m_primaryMeshName = Script::GenerateCRC(pMeshFileName);
	}
    
	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::AddGeom(CGeom* pGeom, uint32 geomName)
{
	// now that a new geom has been added to the model,
	// consider the bounding sphere "dirty"
	m_boundingSphereCached = false;

    bool success = false;

	Dbg_MsgAssert( m_numGeoms < MAX_GEOMS, ( "Too many geoms for this model (max=%d)", MAX_GEOMS ) );

	mp_geom[m_numGeoms] = pGeom;
	mp_geom[m_numGeoms]->EnableShadow(m_shadowEnabled);
	m_geomName[m_numGeoms] = geomName;
	m_geomActiveMask |= (1<SetModelLights(mp_model_lights);
	}

	m_numGeoms++;
		
	SetRenderMode( vTEXTURED );

	// TODO:  The mesh file name represents
	// mesh #0, but the mesh file name is
	// already kind of kludge that will be
	// cleaned up when CGeoms are properly
	// implemented.
	if ( m_numGeoms == 1 )
	{
		m_primaryMeshName = CRCD(0x9facc1a5,"TempMeshName");
	}
    
	// Maybe need to pass in the collision volume instead?

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::RemovePolys(void)
{
	uint32 polyRemovalMask = GetPolyRemovalMask();

	HidePolys( polyRemovalMask );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CModel::GetPolyRemovalMask()
{
	uint32 polyRemovalMask = 0;
	
	// loop through the meshes to collect the poly removal masks
	for ( short i = 0; i < m_numMeshes; i++ )
	{
		polyRemovalMask |= ( mp_mesh[i]->GetRemovalMask() );
	}

	// also account for cutscene objects, which are loaded
	// through the asset manager
	for ( short i = 0; i < m_numPreloadedMeshes; i++ )
	{
		Ass::CAssMan * ass_man = Ass::CAssMan::Instance();
		Nx::CMesh* pMesh = (Nx::CMesh*)ass_man->GetAsset( m_preloadedMeshNames[i], true );
		if( pMesh )
		{
			polyRemovalMask |= pMesh->GetRemovalMask();
		}
	}
	
	return polyRemovalMask;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModel::HidePolys( uint32 polyRemovalMask )
{
	// loop through the existing models, and remove existing polys
	for ( short i = 0; i < m_numGeoms; i++ )
	{
		mp_geom[i]->HidePolys( polyRemovalMask );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::CreateModelLights()
{
	Dbg_MsgAssert( !mp_model_lights, ("Model lights already exist") );

	mp_model_lights = Nx::CLightManager::sCreateModelLights();

	for ( short i = 0; i < m_numGeoms; i++ )
	{
		mp_geom[i]->SetModelLights( mp_model_lights );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::DestroyModelLights()
{
	Dbg_MsgAssert( mp_model_lights, ("No Model lights to destroy") );

	Nx::CLightManager::sFreeModelLights( mp_model_lights );

	mp_model_lights = NULL;
	for ( short i = 0; i < m_numGeoms; i++ )
	{
		mp_geom[i]->SetModelLights( NULL );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModelLights * CModel::GetModelLights() const
{
	return mp_model_lights;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::SetRenderMode(ERenderMode mode)
{
    bool success = false;

    if (mode != m_renderMode)
    {
        success = plat_set_render_mode(mode);
        
        if (success)
        {
            m_renderMode = mode;
        }
    }

    return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ERenderMode CModel::GetRenderMode()
{
	return m_renderMode;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModel::Hide( bool should_hide )
{   
	m_hidden = should_hide;
	
	SetActive( !should_hide );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::SetActive( bool active )
{
	if( m_hidden && active )
	{
		return false;									
	}

	// (Mick) if the active state is already set correctly, then just return
	// (this assumes that the geoms are correctly set already)	
	if (m_active == active)
	{
		return true;
	}

	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		Dbg_Assert( GetGeomByIndex(i) );
		GetGeomByIndex(i)->SetActive( active );
	}
	
	m_active = active;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom* CModel::GetGeom(uint32 geomName)
{
	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		// TODO:  there might be multiple geoms with
		// the same checksum...
		if ( geomName == m_geomName[i] )
		{
			Dbg_Assert( GetGeomByIndex( i ) );
			return GetGeomByIndex( i );
		}
	}

    return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::ReplaceTexture( uint32 geomName, const char* p_SrcTextureName, const char* p_DstTextureName )
{
	bool success = false;
	
	bool applyToAll = (geomName == vREPLACE_GLOBALLY);
	
	plat_prepare_materials();

	// The lower level code is expecting the .PNG extension
	// for the source texture name, so add it if's not there
	char src_texture_name[512];
	strcpy( src_texture_name, p_SrcTextureName );
	Str::LowerCase( src_texture_name );
	char* pSrcExt = strstr( src_texture_name, ".png" );
	if ( !pSrcExt )
	{
		strcat( src_texture_name, ".png" );
	}

	// The lower level code is NOT expecting the .PNG extension
	// for the destination texture name, so strip it out if it's 
	// there (the artists sometimes accidentally leave the .PNG in there)...
	char dest_texture_name[512];
	strcpy( dest_texture_name, p_DstTextureName );
	Str::LowerCase( dest_texture_name );
	char* pDstExt = strstr( dest_texture_name, ".png" );
	if ( pDstExt )
	{
		*pDstExt = NULL;
	}

	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		// m_geomName[i] = 0 will handle cutscene objects,
		// for texture replacement on the hi-res heads

		if ( geomName == m_geomName[i] || applyToAll || m_geomName[i] == 0 )
		{
			if ( mp_geomTexDict[i] && mp_geomTexDict[i]->ReplaceTexture( src_texture_name, dest_texture_name) )
			{
				success = true;
			}
		}
	}

	if ( !success && Script::GetInteger( CRCD(0x2a648514,"cas_artist") ) )
	{
		Dbg_Message( "Texture replacement of %s by %s failed!", p_SrcTextureName, dest_texture_name );
	}

	plat_refresh_materials();

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::ReplaceTexture( uint32 geomName, const char* p_SrcFileName, uint32 DstChecksum )
{
	bool success = false;
	
	bool applyToAll = (geomName == vREPLACE_GLOBALLY);
	
	plat_prepare_materials();

	// The lower level code is expecting the .PNG extension
	// for the source texture name, so add it if's not there
	char src_texture_name[512];
	strcpy( src_texture_name, p_SrcFileName );
	Str::LowerCase( src_texture_name );
	char* pSrcExt = strstr( src_texture_name, ".png" );
	if ( !pSrcExt )
	{
		strcat( src_texture_name, ".png" );
	}

	CTexture *p_dest_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(DstChecksum);
	if (!p_dest_texture)
	{
		Dbg_MsgAssert(0, ("Can't find destination texture %s", Script::FindChecksumName(DstChecksum)));
	}

	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		if ( geomName == m_geomName[i] || applyToAll )
		{
			if ( mp_geomTexDict[i] && mp_geomTexDict[i]->ReplaceTexture( src_texture_name, p_dest_texture) )
			{
				success = true;
			}
		}
	}

	if ( !success && Script::GetInteger( CRCD(0x2a648514,"cas_artist") ) )
	{
		Dbg_Message( "Texture replacement of %s by %s failed!", p_SrcFileName, Script::FindChecksumName(DstChecksum) );
	}

	plat_refresh_materials();

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::ClearColor( uint32 geomName )
{
	bool success = false;
	
	bool applyToAll = (geomName == vREPLACE_GLOBALLY);

	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		if ( geomName == m_geomName[i] || applyToAll )
		{
			Dbg_MsgAssert( !mp_geom[i]->MultipleColorsEnabled(), ( "Wasn't expecting multiple material colors" ) );

			mp_geom[i]->ClearColor();
			success = true;
		}
	}

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::ModulateColor( uint32 geomName, float h, float s, float v )
{
	// hue = [0.0, 360.0f)
	// satuation = [0.0, 1.0]
	// value = [0.0, 1.0]

	bool success = false;
	
	bool applyToAll = (geomName == vREPLACE_GLOBALLY);
	
	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		if ( geomName == m_geomName[i] || applyToAll )
		{
			if ( mp_geom[i]->MultipleColorsEnabled() )
			{
				// ignore geoms with the "multiple colors" flag
				// (you're supposed to use SetColorWithParams() for that)
			}
			else
			{
				float r, g, b;
				Gfx::HSVtoRGB( &r, &g, &b, h, s, v );

				Image::RGBA theColor;
				theColor.r = (unsigned char)( r * 255.0f + 0.5f );
				theColor.g = (unsigned char)( g * 255.0f + 0.5f );
				theColor.b = (unsigned char)( b * 255.0f + 0.5f );
				theColor.a = 255;

				Dbg_MsgAssert( !mp_geom[i]->MultipleColorsEnabled(), ( "Wasn't expecting multiple material colors" ) );

				mp_geom[i]->SetColor( theColor );
				success = true;
			}
		}
	}

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::SetUVOffset(uint32 mat_checksum, int pass, float u_offset, float v_offset)
{
	bool success = false;
	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		success |= mp_geom[i]->SetUVWibbleOffsets(mat_checksum, pass, u_offset, v_offset);
	}

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::SetUVMatrix(uint32 mat_checksum, int pass, const Mth::Matrix &mat)
{
	bool success = false;
	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		success |= mp_geom[i]->SetUVMatrix(mat_checksum, pass, mat);
	}

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::AllocateUVMatrixParams(uint32 mat_checksum, int pass)
{
	bool success = false;
	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		success |= mp_geom[i]->AllocateUVMatrixParams(mat_checksum, pass);
	}

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::SetColor( Script::CStruct* pParams, float h, float s, float v )
{
	// GJ:  This is a generic way of changing the colors in a model,
	// without having to know whether it has a separate color per material
	
	// GJ TODO:  Ideally, this function would not reference CStruct,
	// but would rather be passed the appropriate material/pass
	// parameters from a higher level...

	bool success = false;

	uint32 geomName;
	pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &geomName, Script::ASSERT );

	float r, g, b;
	Gfx::HSVtoRGB( &r, &g, &b, h, s, v );

	Image::RGBA theColor;
	theColor.r = (unsigned char)( r * 255.0f + 0.5f );
	theColor.g = (unsigned char)( g * 255.0f + 0.5f );
	theColor.b = (unsigned char)( b * 255.0f + 0.5f );
	theColor.a = 255;

	bool applyToAll = (geomName == vREPLACE_GLOBALLY);

	int numGeoms = GetNumGeoms();
	for ( int i = 0; i < numGeoms; i++ )
	{
		uint32 mat_checksum;
		int pass = 0;
		Script::CArray* pMaterialArray;
		
		if ( !mp_geom[i]->MultipleColorsEnabled() )
		{
			if ( geomName == m_geomName[i] || applyToAll )
			{
				mp_geom[i]->SetColor( theColor );
				success = true;
			}
		}
		else if ( pParams->GetArray( CRCD(0x64e8e94a,"materials"), &pMaterialArray, Script::NO_ASSERT ) )
		{
			for ( uint32 j = 0; j < pMaterialArray->GetSize(); j++ )
			{
				Script::CStruct* pMaterialStruct = pMaterialArray->GetStructure( j );
				pMaterialStruct->GetChecksum( NONAME, &mat_checksum, Script::ASSERT );
				pMaterialStruct->GetInteger( NONAME, &pass, Script::ASSERT ); 

				success |= mp_geom[i]->SetMaterialColor(mat_checksum, pass, theColor);
			}
		}
		else if ( pParams->GetChecksum( CRCD(0x83418a6a,"material"), &mat_checksum, Script::NO_ASSERT )	)
		{
			Script::CArray* pPassArray;
			if ( pParams->GetArray( CRCD(0x318f2bdb,"pass"), &pPassArray, Script::NO_ASSERT ) )
			{
				for ( uint32 j = 0; j < pPassArray->GetSize(); j++ )
				{
					success |= mp_geom[i]->SetMaterialColor(mat_checksum, pPassArray->GetInteger(j), theColor);
				}
			}
			else if ( pParams->GetInteger( CRCD(0x318f2bdb,"pass"), &pass, Script::ASSERT ) )
			{
				success |= mp_geom[i]->SetMaterialColor(mat_checksum, pass, theColor);
			}
		}
		
		// kludge for changing skin color
		// (ideally this would be in script, but we would have to uglify the
		// code to get around some of the model-building operations that
		// currently overwrite the body substructure, such as when
		// the head forces the body to a specific skin tone)
		if ( ( geomName == CRCD(0x650fab6d,"skater_m_head") 
			   || geomName == CRCD(0x0fc85bae,"skater_f_head") )
			 && m_geomName[i] == CRCD(0x2457f44d,"body") )
		{
			Dbg_MsgAssert( !mp_geom[i]->MultipleColorsEnabled(), ( "Wasn't expecting color per material on body geom" ) );
			this->ModulateColor( CRCD(0x2457f44d,"body"), h, s, v );
		}
	}	
	
	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::ClearColor( Script::CStruct* pParams )
{
	// GJ:  This is a generic way of changing the colors in a model,
	// without having to know whether it has a separate color per material
	
	bool success = false;

	uint32 geomName;
	pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &geomName, Script::ASSERT );

	bool applyToAll = (geomName == vREPLACE_GLOBALLY);

	int numGeoms = GetNumGeoms();
	for ( int i = 0; i < numGeoms; i++ )
	{
		uint32 mat_checksum;
		int pass = 0;
		Script::CArray* pMaterialArray;
		
		if ( !mp_geom[i]->MultipleColorsEnabled() )
		{
			if ( geomName == m_geomName[i] || applyToAll )
			{
				mp_geom[i]->ClearColor();
				success = true;
			}
		}
		else if ( pParams->GetArray( CRCD(0x64e8e94a,"materials"), &pMaterialArray, Script::NO_ASSERT ) )
		{
			Image::RGBA theColor;
			theColor.r = 128;
			theColor.g = 128;
			theColor.b = 128;
			theColor.a = 128;

			for ( uint32 j = 0; j < pMaterialArray->GetSize(); j++ )
			{
				Script::CStruct* pMaterialStruct = pMaterialArray->GetStructure( j );
				pMaterialStruct->GetChecksum( NONAME, &mat_checksum, Script::ASSERT );
				pMaterialStruct->GetInteger( NONAME, &pass, Script::ASSERT ); 

				success |= mp_geom[i]->SetMaterialColor(mat_checksum, pass, theColor);
			}
		}
		else if ( pParams->GetChecksum( CRCD(0x83418a6a,"material"), &mat_checksum, Script::NO_ASSERT )	)
		{
			Image::RGBA theColor;
			theColor.r = 128;
			theColor.g = 128;
			theColor.b = 128;
			theColor.a = 128;
			
			Script::CArray* pPassArray;
			if ( pParams->GetArray( CRCD(0x318f2bdb,"pass"), &pPassArray, Script::NO_ASSERT ) )
			{
				for ( uint32 j = 0; j < pPassArray->GetSize(); j++ )
				{
					success |= mp_geom[i]->SetMaterialColor(mat_checksum, pPassArray->GetInteger(j), theColor);
				}
			}
			else if ( pParams->GetInteger( CRCD(0x318f2bdb,"pass"), &pass, Script::ASSERT ) )
			{
				success |= mp_geom[i]->SetMaterialColor(mat_checksum, pass, theColor);
			}
		}

		// kludge for changing skin color
		if ( ( geomName == CRCD(0x650fab6d,"skater_m_head") 
			   || geomName == CRCD(0x0fc85bae,"skater_f_head") )
			 && m_geomName[i] == CRCD(0x2457f44d,"body") )
		{
			Dbg_MsgAssert( !mp_geom[i]->MultipleColorsEnabled(), ( "Wasn't expecting color per material on body geom" ) );
			mp_geom[i]->ClearColor();
			success = true;
		}
	}
	
	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom* CModel::GetGeomByIndex(int index)
{
	Dbg_Assert( index >= 0 && index < m_numGeoms );

    return mp_geom[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CModel::GetGeomNameByIndex(int index)
{
	Dbg_Assert( index >= 0 && index < m_numGeoms );

    return m_geomName[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict* CModel::GetTexDictByIndex(int index)
{
	Dbg_Assert( index >= 0 && index < m_numGeoms );

    return mp_geomTexDict[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CModel::GetNumGeoms() const
{
    return m_numGeoms;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CModel::GetGeomActiveMask() const
{
	// K: Used by replay code.
	return m_geomActiveMask;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModel::SetGeomActiveMask(uint32 mask)
{
	// K: Used by replay code.
	m_geomActiveMask=mask;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModel::Finalize()
{
	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		Dbg_Assert( GetGeomByIndex(i) );
		GetGeomByIndex(i)->Finalize();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModel::ClearGeoms()
{
	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		Dbg_Assert( GetGeomByIndex(i) );
		Nx::CEngine::sUninitGeom( GetGeomByIndex(i) );
	}

	m_numGeoms = 0;

	// eliminate all the meshes
	for ( short i = 0; i < m_numMeshes; i++ )
	{
		Nx::CEngine::sUnloadMesh( mp_mesh[i] );
	}

	m_numMeshes = 0;

	m_numPreloadedMeshes = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::HideGeom( uint32 geomName, bool hidden )
{
	bool success = false;

	bool applyToAll = (geomName == vREPLACE_GLOBALLY);
	
	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		if ( (geomName == m_geomName[i]) || applyToAll )
		{
			if ( hidden )
			{
				m_geomActiveMask &= ~(1<SetColor(rgba);
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::SetVisibility(uint32 mask)
{
	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		Dbg_Assert( GetGeomByIndex(i) );
		GetGeomByIndex(i)->SetVisibility(mask);
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::SetScale( const Mth::Vector& scale )
{
	m_scale = scale;
	
	m_scalingEnabled = ( scale[X] != 1.0f || scale[Y] != 1.0f || scale[Z] != 1.0f );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModel::EnableScaling( bool enabled )
{
	m_scalingEnabled = enabled;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModel::EnableShadow( bool enabled )
{
	m_shadowEnabled = enabled;

	int numGeoms = GetNumGeoms();
	for ( short i = 0; i < numGeoms; i++ )
	{
		Dbg_Assert( GetGeomByIndex(i) );
		GetGeomByIndex(i)->EnableShadow(enabled);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CModel::GetNumObjectsInHierarchy()
{
	// This function only works with single-CMesh items
	if ( m_numPreloadedMeshes == 1 )
	{
		Ass::CAssMan * ass_man = Ass::CAssMan::Instance();
		Nx::CMesh* pMesh = (Nx::CMesh*)ass_man->GetAsset( m_preloadedMeshNames[0] );
		Dbg_MsgAssert( pMesh, ( "Couldn't find preloaded mesh[0] to get hierarchy from" ) );
		return pMesh->GetNumObjectsInHierarchy();
	}
	else
	{
		return NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::CHierarchyObject* CModel::GetHierarchy()
{
	// This function only works with single-CMesh items
	if ( m_numPreloadedMeshes == 1 )
	{
		Ass::CAssMan * ass_man = Ass::CAssMan::Instance();
		Nx::CMesh* pMesh = (Nx::CMesh*)ass_man->GetAsset( m_preloadedMeshNames[0] );
		Dbg_MsgAssert( pMesh, ( "Couldn't find preloaded mesh[0] to get hierarchy from" ) );
		return pMesh->GetHierarchy();
	}
	else
	{
		return NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CModel::GetBoundingSphere()
{
	// unless the dirty flag has been set
	if ( m_boundingSphereCached )
	{
		return m_boundingSphere;
	}

	// recalculate the bounding sphere
	m_boundingSphere = plat_get_bounding_sphere();
	m_boundingSphereCached = true;

	return m_boundingSphere;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModel::SetBoundingSphere( const Mth::Vector& boundingSphere )
{
	m_boundingSphereCached = false;

	plat_set_bounding_sphere( boundingSphere );
}					  

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CModel::ApplyFaceTexture( Gfx::CFaceTexture* pFaceTexture, const char* pSrcTexture, uint32 partChecksumToReplace )
{
	bool success = false;

	Dbg_Assert( pFaceTexture );
	Dbg_Assert( pFaceTexture->IsValid() );

	//-------------------------------------------------------------
	// step 1:  load up the temporary texture from the appearance's CFaceTexture
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

	bool alloc_vram = true;
	uint32 temp_checksum = CRCD(0xb00b0dc0,"dummy");
	Nx::CTexture* p_temp_texture = Nx::CTexDictManager::sp_sprite_tex_dict->LoadTextureFromBuffer(pFaceTexture->GetTextureData(), pFaceTexture->GetTextureSize(), temp_checksum, true, alloc_vram, false);
	Dbg_Assert( p_temp_texture );

	Nx::CTexture* p_temp_overlay = Nx::CTexDictManager::sp_sprite_tex_dict->LoadTexture(pFaceTexture->GetOverlayTextureName(), alloc_vram);
	Dbg_Assert( p_temp_overlay );
	
	Mem::Manager::sHandle().PopContext();
	//-------------------------------------------------------------

	//-------------------------------------------------------------
	// step 2:  massage the temporary texture here, based on the appearance's CFaceTexture
	Nx::SFacePoints originalModelFacePoints;
	Script::CStruct* pModelFacePoints = Script::GetStructure( CRCD(0xa435752c,"original_model_face_points"), Script::ASSERT );
	GetFacePointsStruct( originalModelFacePoints, pModelFacePoints );
	Nx::CFaceTexMassager::sSetModelFacePoints( originalModelFacePoints );

	Nx::CFaceTexMassager::sSetFaceTextureOverlay( p_temp_overlay );

	// this speeds things up a little because the palette doesn't get rebuilt
	// (the image is not accurate, but it does end up matching the skintone more)
	//bool do_palette_gen = false;
	bool do_palette_gen = pFaceTexture->GetFacePoints().m_adjust_hsv;		// Rebuild the palette if HSV is modified

	// this is a test for Nolan to see if the colors match better
	// when the palette is generated
//	do_palette_gen = Script::GetInt( CRCD(0x8b32fff0,"do_palette_gen"), Script::NO_ASSERT );

	#ifdef	__NOPT_ASSERT__
	bool massage_success = 
	#endif
	Nx::CFaceTexMassager::sMassageTexture( p_temp_texture,
										   pFaceTexture->GetFacePoints(),
										   do_palette_gen );

	Dbg_Assert( massage_success );
	//-------------------------------------------------------------

	//-------------------------------------------------------------
	// step 3:  do texture replacement on the actual model
	Dbg_Assert( pSrcTexture );
	success = this->ReplaceTexture( partChecksumToReplace, pSrcTexture, p_temp_texture->GetChecksum() );
	//-------------------------------------------------------------

	//-------------------------------------------------------------
	// step 4:  get rid of the temporary texture
	Nx::CTexDictManager::sp_sprite_tex_dict->UnloadTexture( p_temp_texture );
	Nx::CTexDictManager::sp_sprite_tex_dict->UnloadTexture( p_temp_overlay );
	//-------------------------------------------------------------

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CModel::EnableShadowVolume( bool enabled )
{
	// if true, any future geoms added to the 
	// model will use shadow volumes
	m_doShadowVolume = enabled;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx



================================================
FILE: Code/Gfx/NxModel.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxModel.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/21/2001
//****************************************************************************

#ifndef	__GFX_NXMODEL_H__
#define	__GFX_NXMODEL_H__

                           
#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

// Forward declarations
namespace Gfx
{
	class	CFaceTexture;
    class   CSkeleton;
};

namespace Script
{
	class	CStruct;
};
                  
namespace Nx
{

// Forward declarations
class	CGeom;
class	CHierarchyObject;
class	CMesh;
class	CTexDict;
class	CModelLights;

// TODO:  Insert class description here...
// This is the machine-independent representation of a model,
// which is the graphical representation of some object

enum ERenderMode
{
    vTEXTURED,
	vSKELETON,
    vGOURAUD,
    vFLAT,
    vWIREFRAME,
    vBBOX,
    vNONE
};

class CModel : public Spt::Class
{
public:
	enum
	{
		// special flag for global texture replacement
		vREPLACE_GLOBALLY = 0xc4e78e22			// all
	};

public:
    // The basic interface to the model
    // this is the machine independent part	
    // machine independent range checking, etc can go here	
	CModel();
    virtual				~CModel();

public:
	bool				SetBoneMatrixData( Gfx::CSkeleton* pSkeleton );
    bool                Render( Mth::Matrix* pMatrix, bool no_anim, Gfx::CSkeleton* pSkeleton );
    uint32				GetFileName() { return m_primaryMeshName; }

    // Grabs a specific geom object so that you can do part-specific operations
    CGeom*     			GetGeom(uint32 geomName);
	CGeom*				GetGeomByIndex(int index);
	uint32				GetGeomNameByIndex(int index);
	CTexDict*			GetTexDictByIndex(int index);
	int					GetNumGeoms() const;
	void				ClearGeoms();
	void				Finalize();
	uint32				GetGeomActiveMask() const;
	void				SetGeomActiveMask(uint32 mask);
	
	bool				AddGeom(uint32 assetName, uint32 geomName, bool supportMultipleMaterialColors=false);
	bool				AddGeom(const char* pMeshName, uint32 geomName, bool useAssetManager, uint32 texDictOffset=0, bool forceTexDictLookup=false, bool supportMultipleMaterialColors=false);
	bool				HideGeom( uint32 geomName, bool hidden );
    bool				GeomHidden( uint32 geomName );

	// probably need to pass the collision as well
	bool				AddGeom(Nx::CGeom* pGeom, uint32 geomName);

	// model lights
	bool				CreateModelLights();
	bool				DestroyModelLights();
	CModelLights *		GetModelLights() const;

public:
    bool                SetRenderMode(ERenderMode mode);
	ERenderMode			GetRenderMode();
	bool				SetColor(uint8 r, uint8 g, uint8 b, uint8 a);
	bool				SetVisibility(uint32 mask);
	void				ResetScale();
	bool				SetScale(const Mth::Vector& scale);
	Mth::Vector			GetScale() {return m_scale;}
	void				EnableScaling( bool enabled );
	bool				IsScalingEnabled() { return m_scalingEnabled; }
	void				Hide( bool should_hide = true );
	bool				IsHidden() { return m_hidden; }
	bool				SetActive(bool active);
	bool				GetActive() {return m_active && !m_hidden;}    
	bool                ReplaceTexture( uint32 geomName, const char* p_SrcFileName, const char* p_DstFileName );
	bool                ReplaceTexture( uint32 geomName, const char* p_SrcFileName, uint32 DstChecksum );
	bool				SetUVOffset(uint32 mat_checksum, int pass, float u_offset, float v_offset);
	bool				SetUVMatrix(uint32 mat_checksum, int pass, const Mth::Matrix &mat);
	bool				AllocateUVMatrixParams(uint32 mat_checksum, int pass);
    bool                SetSkeleton( Gfx::CSkeleton* pSkeleton );
    Mth::Matrix*		GetBoneTransforms();
    void				EnableShadow(bool enabled);
    bool				RemovePolys();
	uint32 				GetPolyRemovalMask();
	void 				HidePolys( uint32 polyRemovalMask );

	// way of changing object color (for general objects)
	bool				ModulateColor( uint32 geomName, float h, float s, float v );
	bool				ClearColor( uint32 geomName );
	
	// generic way of changing colors, without having to know whether
	// you've got material colors or object colors...  (used from
	// model builder)
	bool				SetColor( Script::CStruct* pParams, float h, float s, float v );
	bool				ClearColor( Script::CStruct* pParams );
	
	// apply face texture (kind of like a texture replacement)
	bool				ApplyFaceTexture( Gfx::CFaceTexture* pFaceTexture, const char* pSrcTexture, uint32 geomName );

    // phase this out, eventually...
    int                 GetNumBones() {return m_numBones;}      // not really happy about this

protected:
    ERenderMode         m_renderMode;
    
protected:
	// GJ:  skeleton related stuff...  I'm not sure
	// if this is the appropriate place to store this
	// data...  maybe model should be able to query the 
	// composite object for this data?
	int                 m_numBones;
    Mth::Matrix*        mp_skeletonMatrices;

public:
	Mth::Vector			GetBoundingSphere();
	void				SetBoundingSphere( const Mth::Vector& boundingSphere );

	// for skeletal models, such as cars
	Nx::CHierarchyObject* GetHierarchy();
	int					GetNumObjectsInHierarchy();

	void				EnableShadowVolume(bool enabled);

protected:
	// TODO:  replace this with a linked list
	enum
	{
		MAX_GEOMS = 18
	};
	CGeom*				mp_geom[MAX_GEOMS];
	Nx::CTexDict*		mp_geomTexDict[MAX_GEOMS];
	uint32				m_geomName[MAX_GEOMS];
	uint32				m_geomActiveMask;
	Mth::Vector			m_scale;

	CModelLights*		mp_model_lights;

	enum
	{
		MAX_MESHES = 18
	};

	CMesh*				mp_mesh[MAX_MESHES];
	uint32				m_preloadedMeshNames[MAX_MESHES];
	uint32				m_primaryMeshName;

	// remember the bounding sphere
	Mth::Vector			m_boundingSphere;
	
	bool				m_boundingSphereCached:1;
	bool				m_active:1;
	bool				m_hidden:1;
	bool				m_scalingEnabled:1;
	bool				m_shadowEnabled:1;
	bool				m_doShadowVolume:1;

	short				m_numGeoms;
	short				m_numMeshes;
	short				m_numPreloadedMeshes;
	
private:
    // The virtual functions will have a stub implementation
    // in p_nxmodel.cpp
	virtual	bool		plat_init_skeleton( int numBones );
	virtual	bool		plat_set_render_mode(ERenderMode mode);
	virtual	bool		plat_set_color(uint8 r, uint8 g, uint8 b, uint8 a);
	virtual	bool		plat_set_visibility(uint32 mask);
	virtual	bool		plat_set_active(bool active);
    virtual bool        plat_set_scale(float scaleFactor);
    virtual bool        plat_replace_texture(char* p_srcFileName, char* p_dstFileName);
	virtual bool		plat_render(Mth::Matrix* pRootMatrix, Mth::Matrix* ppBoneMatrices, int numBones);
	virtual bool		plat_prepare_materials( void );
	virtual bool		plat_refresh_materials( void );
	virtual Mth::Vector	plat_get_bounding_sphere();
	virtual void		plat_set_bounding_sphere( const Mth::Vector& boundingSphere );
};

}

#endif // __GFX_NXMODEL_H__



================================================
FILE: Code/Gfx/NxNewParticle.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		NxNewParticle.cpp										**
**																			**
**	Created by:		3/24/03	-	SPG											**
**																			**
**	Description:	New parametric particle system							**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static Mth::Vector extrema( Mth::Vector p0, Mth::Vector p1, Mth::Vector p2, float t1, float t2 )
{
	Mth::Vector e1, e2, u, a, p;
	float		q;

    e1	= t1 * ( p2 - p0 );
    e2	= t2 * ( p1 - p0 );
	q	= 1.0f / ( t1 * t2 * ( t2 - t1 )); 
	u	= ( t2 * e2 - t1 * e1 ) * q;			// Intitial velocity.
	a	= ( e1 - e2 ) * q;						// Twice initial acceleration.

	for( int i = 0; i < 3; i++ )
    {
		float t	= -0.5f * u[i] / a[i];				// Time of extremum for given coordinate.
		p[i]	= p0[i]  + u[i] * t + a[i] * t * t;	// Value of coordinate at time t.
    }
	p[3] = 0.0f;

	return p;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static Mth::Vector componentwise_min( Mth::Vector& p0, Mth::Vector& p1 )
{
	Mth::Vector r = p0;

	if( p1[X] < p0[X] )
		r[X] = p1[X];
	if( p1[Y] < p0[Y] )
		r[Y] = p1[Y];
	if( p1[Z] < p0[Z] )
		r[Z] = p1[Z];
	if( p1[W] < p0[W] )
		r[W] = p1[W];

	return r;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static Mth::Vector componentwise_max( Mth::Vector& p0, Mth::Vector& p1 )
{
	Mth::Vector r = p0;

	if( p1[X] > p0[X] )
		r[X] = p1[X];
	if( p1[Y] > p0[Y] )
		r[Y] = p1[Y];
	if( p1[Z] > p0[Z] )
		r[Z] = p1[Z];
	if( p1[W] > p0[W] )
		r[W] = p1[W];

	return r;
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

CParticleParams::CParticleParams( void )
{
	int i;

	m_Type = CRCD(0xdedfc057,"NEWFLAT");
	m_UseMidpoint = true;
	m_LocalCoord = false;
	for( i = 0; i < vNUM_BOXES; i++ )
	{
		m_Radius[i] = 1.0f;
		m_RadiusSpread[i] = 0.0f;

		m_BoxPos[i][X] = 0.0f;
		m_BoxPos[i][Y] = 0.0f;
		m_BoxPos[i][Z] = 0.0f;
		m_BoxPos[i][W] = 0.0f;

		m_BoxDims[i][X] = 20.0f;
		m_BoxDims[i][Y] = 20.0f;
		m_BoxDims[i][Z] = 20.0f;
		m_BoxDims[i][W] = 0.0f;

		m_LocalBoxPos[i][X] = 0.0f;
		m_LocalBoxPos[i][Y] = 0.0f;
		m_LocalBoxPos[i][Z] = 0.0f;
		m_LocalBoxPos[i][W] = 0.0f;

		m_Color[i].r = 128;
		m_Color[i].g = 128;
		m_Color[i].b = 128;
		m_Color[i].a = 128;
	}

	m_MaxStreams = 2;
	m_EmitRate = 300.0f;
	m_Lifetime = 4.0f;
	m_MidpointPct = 50.0f;
	
    m_UseMidcolor = false;
	m_ColorMidpointPct = 50.0f;
	m_BlendMode = CRCD(0xa86285a1,"FixAdd");
	m_AlphaCutoff = 1;
	m_Texture = 0;
	m_FixedAlpha = 0;

	m_LODDistance1 = 400;
	m_LODDistance2 = 401;
	m_SuspendDistance = 0;

	m_Hidden = false;
	m_Suspended = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNewParticle::CNewParticle( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNewParticle::~CNewParticle( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::CalculateBoundingVolumes( void )
{
	// Calculate the bounding box.
	Mth::Vector p0, p1, p2;				// box centres
	Mth::Vector s0, s1, s2;				// box dimensions
	Mth::Vector p0_lo, p1_lo, p2_lo;	// corresponding minimum possible coords
	Mth::Vector p0_hi, p1_hi, p2_hi;	// corresponding maximum possible coords
	Mth::Vector AABB_min, AABB_max;		// AABB corners
	Mth::Vector one( 1.0f, 1.0f, 1.0f, 1.0f );

	p0 = m_params.m_BoxPos[0];
	p1 = m_params.m_BoxPos[1];
	p2 = m_params.m_BoxPos[2];

	p0[W] = m_params.m_Radius[0];
	p1[W] = m_params.m_Radius[1];
	p2[W] = m_params.m_Radius[2];

	s0 = 0.5f * m_params.m_BoxDims[0];
	s1 = 0.5f * m_params.m_BoxDims[1];
	s2 = 0.5f * m_params.m_BoxDims[2];

	s0[W] = 0.5f * m_params.m_RadiusSpread[0];
	s1[W] = 0.5f * m_params.m_RadiusSpread[1];
	s2[W] = 0.5f * m_params.m_RadiusSpread[2];

	p0_lo = p0 - s0 - p0[W]*one - s0[W]*one;
	p1_lo = p1 - s1 - p1[W]*one - s1[W]*one; 
	p2_lo = p2 - s2 - p2[W]*one - s2[W]*one;

	p0_hi = p0 + s0 + p0[W]*one + s0[W]*one;
	p1_hi = p1 + s1 + p1[W]*one + s1[W]*one;
	p2_hi = p2 + s2 + p2[W]*one + s2[W]*one;

	AABB_min = componentwise_min( p0_lo, p2_lo );
	AABB_max = componentwise_max( p0_hi, p2_hi );

	if( m_params.m_UseMidpoint )
	{ 
		Mth::Vector p;

		p = extrema( p0_lo, p1_lo, p2_lo,
					m_params.m_Lifetime * m_params.m_MidpointPct * 0.01f,
					m_params.m_Lifetime );

		AABB_min = componentwise_min( AABB_min, p ); 
	 
		p = extrema( p0_hi, p1_hi, p2_hi,
					m_params.m_Lifetime * m_params.m_MidpointPct * 0.01f,
					m_params.m_Lifetime );

		AABB_max = componentwise_max( AABB_max, p ); 
	}

	// Set the bounding box.
	m_bbox.Set( AABB_min, AABB_max );

	// Now calculate the bounding sphere.
	Mth::Vector diag		= ( AABB_max - AABB_min ) * 0.5f;
	diag[W]					= 0.0f;
	m_bsphere				= AABB_min + diag;
	m_bsphere[W]			= diag.Length();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::Initialize( CParticleParams* params )
{
	m_params = *params;
	CalculateBoundingVolumes();
	plat_build();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CParticleParams*	CNewParticle::GetParameters( void )
{
	return &m_params;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::Render( void )
{
	if (!m_params.m_Hidden && !m_params.m_Suspended  )
	{
		plat_render();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::Update( void )
{
	if (!m_params.m_Hidden && !m_params.m_Suspended)
	{
		plat_update();
	}
	
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::Destroy( void )
{
	plat_destroy();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::Hide( bool  should_hide )
{
	if (m_params.m_Hidden != should_hide)
	{
		m_params.m_Hidden = should_hide;
		plat_hide(should_hide);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/



void	CNewParticle::Suspend( bool	should_suspend )
{
	if (m_params.m_Suspended != should_suspend)
	{
		// if we were not hidden, then hide/unhide based on the value of the suspend flag
		if (!m_params.m_Hidden)
		{
			plat_hide(should_suspend);
		}	
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::SetEmitRate( float emit_rate )
{
	m_params.m_EmitRate = emit_rate;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::SetLifetime( float lifetime )
{
	m_params.m_Lifetime = lifetime;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::SetMidpointPct( float midpoint_pct )
{
	m_params.m_MidpointPct = midpoint_pct;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::SetRadius( int which, float radius )
{
	Dbg_Assert( which < vNUM_BOXES );

	m_params.m_Radius[which] = radius;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::SetBoxPos( int which, Mth::Vector* pos )
{
	Dbg_Assert( pos );
	Dbg_Assert( which < vNUM_BOXES );

	m_params.m_BoxPos[which] = *pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::SetBoxDims( int which, Mth::Vector* dims )
{
	Dbg_Assert( dims );
	Dbg_Assert( which < vNUM_BOXES );

	m_params.m_BoxDims[which] = *dims;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::SetColor( int which, Image::RGBA* color )
{
	Dbg_Assert( which < vNUM_BOXES );
	Dbg_Assert( color );

	m_params.m_Color[which] = *color;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::SetMidpointColorPct( float midpoint_pct )
{
	m_params.m_ColorMidpointPct = midpoint_pct;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::plat_render( void )
{
	Dbg_Printf( "STUB: plat_render\n" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::plat_update( void )
{
	Dbg_Printf( "STUB: plat_update\n" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::plat_build( void )
{
	Dbg_Printf( "STUB: plat_build\n" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::plat_destroy( void )
{
	Dbg_Printf( "STUB: plat_destroy\n" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CNewParticle::plat_hide( bool should_hide )
{
	Dbg_Printf( "STUB: plat_destroy\n" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx






================================================
FILE: Code/Gfx/NxNewParticle.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		NxNewParticle.h											**
**																			**
**	Created by:		3/24/03	-	SPG											**
**																			**
**	Description:	New parametric particle system  	 					**
**																			**
*****************************************************************************/

#ifndef __GFX_NXNEWPARTICLE_H__
#define __GFX_NXNEWPARTICLE_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

#include 


/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Nx
{

						
enum
{
	vBOX_START,
	vBOX_MID,
	vBOX_END,
	vNUM_BOXES
};

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CParticleParams
{
public:
	CParticleParams( void );

	// Particle Params
	uint32			m_Name;
	uint32			m_Type;	// NEWFLAT is the only one for now
	bool			m_UseMidpoint;
	float			m_Radius[vNUM_BOXES];
	float			m_RadiusSpread[vNUM_BOXES];
	int				m_MaxStreams;
	float			m_EmitRate;
	float			m_Lifetime;		// in seconds
	float			m_MidpointPct;	// percent of lifetime at which midpoint occurs

	// Spatial params
	Mth::Matrix		m_RotMatrix;
	Mth::Vector		m_BoxPos[vNUM_BOXES];
	Mth::Vector		m_BoxDims[vNUM_BOXES];

	// Local data (CParticleComponent is the only class that supports this)
	bool			m_LocalCoord;
	Mth::Vector		m_LocalBoxPos[vNUM_BOXES];

	// Color params
	bool			m_UseMidcolor;
	float			m_ColorMidpointPct;	// percent of lifetime at which midcolor occurs
	Image::RGBA		m_Color[vNUM_BOXES];

	// Material params
	uint32			m_BlendMode;
	int				m_FixedAlpha;
	int				m_AlphaCutoff;
	uint32			m_Texture;

	// LOD params
	int				m_LODDistance1;
	int				m_LODDistance2;
	int				m_SuspendDistance;

	// Hidden flag
	bool			m_Hidden;
	bool			m_Suspended;

};

class CNewParticle
{
public:
	CNewParticle( void );
	virtual	~CNewParticle( void );

	void	Initialize( CParticleParams* params );
	CParticleParams*	GetParameters( void );

	void	SetEmitRate( float emit_rate );
	void	SetLifetime( float lifetime );
	void	SetMidpointPct( float midpoint_pct );
	void	SetMidpointColorPct( float midpoint_pct );

	void	SetRadius( int which, float radius );
	void	SetBoxPos( int which, Mth::Vector* pos );
	void	SetBoxDims( int which, Mth::Vector* dims );
	void	SetColor( int which, Image::RGBA* color );
	
	void	CalculateBoundingVolumes( void );

	void	Render( void );
	void	Update( void );
	void	Destroy( void );
	void	Hide(bool should_hide);
	void	Suspend( bool	should_suspend );
	
	uint32	GetName() {return m_params.m_Name;}
	
protected:
	
	CParticleParams	m_params;

	Mth::CBBox		m_bbox;
	Mth::Vector		m_bsphere;

	virtual void	plat_build( void );
	virtual void	plat_destroy( void );
	virtual void	plat_hide( bool should_hide );
	virtual	void	plat_render( void );
	virtual	void	plat_update( void );

};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx

#endif	// __GFX_NXNEWPARTICLE_H__




================================================
FILE: Code/Gfx/NxNewParticleMgr.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		NxNewParticleMgr.cpp									**
**																			**
**	Created by:		3/24/03	-	SPG											**
**																			**
**	Description:	New parametric particle system manager					**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

CNewParticle*	CNewParticleManager::plat_create_particle( void )
{
	Dbg_Assert( 0 );
	Dbg_Printf( "Stub plat_create_particle\n" );
	return NULL;
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

CNewParticleManager::CNewParticleManager( void )
{
	mp_particle_table = new Lst::HashTable< CNewParticle >( 8 );
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNewParticleManager::~CNewParticleManager( void )
{
	delete mp_particle_table;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNewParticle*	CNewParticleManager::CreateParticle( CParticleParams* params, bool generate_name )
{
	CNewParticle* particle;
	 
	particle = plat_create_particle();
	if( particle )
	{
		if( generate_name )
		{
			uint32 name;

			name = params->m_Name;
			// Ensure a unique name for this system
			while(( mp_particle_table->GetItem( name, false ) != NULL ))
			{
				name++;
			}

			params->m_Name = name;
		}

		particle->Initialize( params );
		mp_particle_table->PutItem( params->m_Name, particle );
	}

	return particle;
}

// Given an actual particle system
// remove it from the Particle
void	CNewParticleManager::KillParticle( CNewParticle* p_particle)
{
	Dbg_MsgAssert(p_particle,("NULL p_particle passed to KillParticle"));
	Dbg_MsgAssert(mp_particle_table->GetItem(p_particle->GetName()) == p_particle,("entry in particle table for %s does not match where it came from",Script::FindChecksumName(p_particle->GetName())));	
	mp_particle_table->FlushItem(p_particle->GetName());	
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CNewParticleManager::RenderParticles( void )
{
	mp_particle_table->IterateStart();
	CNewParticle *p_particle = mp_particle_table->IterateNext();
	while( p_particle )
	{
		p_particle->Render();
		p_particle = mp_particle_table->IterateNext();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CNewParticleManager::UpdateParticles( void )
{
	mp_particle_table->IterateStart();
	CNewParticle *p_particle = mp_particle_table->IterateNext();
	while( p_particle )
	{
		p_particle->Update();
		p_particle = mp_particle_table->IterateNext();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 			CNewParticleManager::Cleanup( void )
{
	// Goes through and removes all particle systems.
	mp_particle_table->IterateStart();
	CNewParticle *p_particle = mp_particle_table->IterateNext();
	while( p_particle )
	{
		CNewParticle *p_next	= mp_particle_table->IterateNext();
		p_particle->Destroy();
		delete p_particle;
		p_particle		= p_next;
	}
	mp_particle_table->FlushAllItems();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx






================================================
FILE: Code/Gfx/NxNewParticleMgr.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		NxNewParticleMgr.h										**
**																			**
**	Created by:		3/24/03	-	SPG											**
**																			**
**	Description:	New parametric particle system manager					**
**																			**
*****************************************************************************/

#ifndef __GFX_NXNEWPARTICKEMGR_H__
#define __GFX_NXNEWPARTICKEMGR_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Nx
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CNewParticleManager
{
public:
	CNewParticleManager( void );
	virtual	~CNewParticleManager( void );

	CNewParticle*	CreateParticle( CParticleParams* params, bool generate_name = false );
	void			KillParticle( CNewParticle* p_particle );
	void			RenderParticles( void );
	void			UpdateParticles( void );
	void			Cleanup( void );

protected:

	Lst::HashTable< CNewParticle >	*mp_particle_table;

private:
	virtual	CNewParticle*	plat_create_particle( void );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx

#endif	// __GFX_NXNEWPARTICKEMGR_H__




================================================
FILE: Code/Gfx/NxQuickAnim.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       quickanim.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/4/2003
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float quatDown(short theSource)
{
    return (float)(theSource / 16384.0f);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float transDown(short theSource, float scaleFactor)
{
    return (float)(theSource / scaleFactor);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void get_rotation_from_key( Gfx::CAnimQKey* p_in, Mth::Quat* pQuat, bool isHiRes )
{
	if ( isHiRes )
	{
		(*pQuat)[X] = ((Gfx::CHiResAnimQKey*)p_in)->qx;
		(*pQuat)[Y] = ((Gfx::CHiResAnimQKey*)p_in)->qy;
		(*pQuat)[Z] = ((Gfx::CHiResAnimQKey*)p_in)->qz;
//  	(*pQuat)[W] = ((Gfx::CHiResAnimQKey*)p_in)->qw;
	}
	else
	{
		(*pQuat)[X] = quatDown( ((Gfx::CStandardAnimQKey*)p_in)->qx );
		(*pQuat)[Y] = quatDown( ((Gfx::CStandardAnimQKey*)p_in)->qy );
		(*pQuat)[Z] = quatDown( ((Gfx::CStandardAnimQKey*)p_in)->qz );
//		(*pQuat)[W] = quatDown( ((Gfx::CStandardAnimQKey*)p_in)->qw );
	}
	
	float qx = (*pQuat)[X];
	float qy = (*pQuat)[Y];
	float qz = (*pQuat)[Z];

	// Dave note: added 09/12/02 - a simple check to ensure we don't try to take the square root of a negative
	// number, which will hose Nan-sensitive platforms later on...
	float sum	= 1.0f - qx * qx - qy * qy - qz * qz;
	(*pQuat)[W] = sqrtf (( sum < 0.0f ) ? 0.0f : sum );
	
	if ( p_in->signBit )
	{
		(*pQuat)[W] = -(*pQuat)[W];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void get_translation_from_key( Gfx::CAnimTKey* p_in, Mth::Vector* pVector, bool isHiRes )
{
	if ( isHiRes )
	{
		(*pVector)[X] = ((Gfx::CHiResAnimTKey*)p_in)->tx;
		(*pVector)[Y] = ((Gfx::CHiResAnimTKey*)p_in)->ty;
		(*pVector)[Z] = ((Gfx::CHiResAnimTKey*)p_in)->tz;
		(*pVector)[W] = 1.0f;
	}
	else
	{
		(*pVector)[X] = transDown( ((Gfx::CStandardAnimTKey*)p_in)->tx, 32.0f );
		(*pVector)[Y] = transDown( ((Gfx::CStandardAnimTKey*)p_in)->ty, 32.0f );
		(*pVector)[Z] = transDown( ((Gfx::CStandardAnimTKey*)p_in)->tz, 32.0f );
		(*pVector)[W] = 1.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float get_alpha( float timeStamp1, float timeStamp2, float time )
{
	Dbg_MsgAssert(timeStamp1 <= time && timeStamp2 >= time, ( "%f should be within [%f %f]", time, timeStamp1, timeStamp2 ));
    
	return (( time - timeStamp1 ) / ( timeStamp2 - timeStamp1 ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
						  
inline void interpolate_q_frame(Mth::Quat* p_out, Gfx::CAnimQKey* p_in1, Gfx::CAnimQKey* p_in2, float alpha, bool isHiRes)
{
	Dbg_Assert(p_out);
	Dbg_Assert(p_in1);
	Dbg_Assert(p_in2);

	Mth::Quat	qIn1;
	get_rotation_from_key( p_in1, &qIn1, isHiRes );

	if ( alpha == 0.0f )
	{
		// don't need to slerp, because it's the start time
		*p_out = qIn1; 
		return;
	}

	Mth::Quat   qIn2;
	get_rotation_from_key( p_in2, &qIn2, isHiRes );
    
	if ( alpha == 1.0f )
	{
		// don't need to slerp, because it's the end time
		*p_out = qIn2; 
		return;
	}

	// fast slerp, stolen from game developer magazine
	*p_out = Mth::FastSlerp( qIn1, qIn2, alpha );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void interpolate_t_frame(Mth::Vector* p_out, Gfx::CAnimTKey* p_in1, Gfx::CAnimTKey* p_in2, float alpha, bool isHiRes)
{
	Dbg_Assert(p_out);
	Dbg_Assert(p_in1);
	Dbg_Assert(p_in2);
    
	// INTERPOLATE T-COMPONENT
    Mth::Vector   tIn1;					  
	get_translation_from_key( p_in1, &tIn1, isHiRes );

	if ( alpha == 0.0f )
	{
		// don't need to lerp, because it's the start time
		*p_out = tIn1; 
		return;
	}

	Mth::Vector   tIn2;
	get_translation_from_key( p_in2, &tIn2, isHiRes );

	if ( alpha == 1.0f )
	{
		// don't need to slerp, because it's the end time
		*p_out = tIn2; 
		return;
	}

    /* Linearly interpolate positions */
    *p_out = Mth::Lerp( tIn1, tIn2, alpha );
}

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions are provided here:
// so engine implementors can leave certain functionality until later

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CQuickAnim::plat_get_interpolated_frames( Mth::Quat* pRotations, Mth::Vector* pTranslations, uint32* pSkipList, uint32 skipIndex, float time )
{
	Dbg_MsgAssert( mp_frameData, ( "No pointer to frame data" ) );

/*
	// this optimization doesn't really speed things up at all,
	// since the time-consuming part is walking through the compressed
	// key lists in CBonedAnimFrameData::GetCompressedInterpolatedFrames.
	// i've commented this out for now until I have more time
	// to figure out a better alternative...
	
	if ( m_quickAnimPointers.valid )
	{
		float timeStamp = time * 60.0f;
		int numBones = mp_frameData->GetNumBones();
		Mth::Quat* pCurrRotations = pRotations;
		Mth::Vector* pCurrTranslations = pTranslations;

		for ( int i = 0; i < numBones; i++ )
		{
			m_quickAnimPointers.qSkip[i] = false;

			Gfx::CAnimQKey* pStartQKey = &m_quickAnimPointers.theStartQKey[i];
			Gfx::CAnimQKey* pEndQKey = &m_quickAnimPointers.theEndQKey[i];

			// if the two adjacent keys haven't changed,
			// then we can use the optimization
			if ( timeStamp >= (int)pStartQKey->timestamp && timeStamp <= (int)pEndQKey->timestamp )
			{
				float qAlpha = get_alpha( (float)pStartQKey->timestamp, (float)pEndQKey->timestamp, timeStamp );

				interpolate_q_frame( pCurrRotations, 
								 pStartQKey, 
								 pEndQKey,
								 qAlpha,
								 false );

				m_quickAnimPointers.qSkip[i] = true;
			}

			pCurrRotations++;

			m_quickAnimPointers.tSkip[i] = false;

			Gfx::CAnimTKey* pStartTKey = &m_quickAnimPointers.theStartTKey[i];
			Gfx::CAnimTKey* pEndTKey = &m_quickAnimPointers.theEndTKey[i];

			// if the two adjacent keys haven't changed,
			// then we can use the optimization
			if ( timeStamp >= (int)pStartTKey->timestamp && timeStamp <= (int)pEndTKey->timestamp )
			{
				float tAlpha = get_alpha( (float)pStartTKey->timestamp, (float)pEndTKey->timestamp, timeStamp );

				interpolate_t_frame( pCurrTranslations, 
								 pStartTKey, 
								 pEndTKey,
								 tAlpha,
								 false );

				m_quickAnimPointers.tSkip[i] = true;
			}
			
			pCurrTranslations++;
		}
	}
*/



	m_quickAnimPointers.pSkipList	= pSkipList;
	m_quickAnimPointers.skipIndex	= skipIndex;
	
	Dbg_MsgAssert( mp_frameData, ( "No frame data" ) );
	mp_frameData->GetInterpolatedFrames(pRotations, pTranslations, time, this);
	
	m_quickAnimPointers.valid = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CQuickAnim::CQuickAnim()
{
	mp_frameData = NULL;
	m_quickAnimPointers.valid = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CQuickAnim::~CQuickAnim()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CQuickAnim::Enable( bool enabled )
{
	m_quickAnimPointers.valid = enabled;

	// if the cache is invalid, then regrab the pointer to the anim
	if ( !enabled )
	{
		mp_frameData = Nx::GetCachedAnim( m_animAssetName, true );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CQuickAnim::SetAnimAssetName( uint32 animAssetName )
{
	m_animAssetName = animAssetName;
	
	// set the pointer to the animation
	mp_frameData = Nx::GetCachedAnim( m_animAssetName, true );
	
	// invalidate the cache
	Enable(false);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// These functions are the platform independent part of the interface to 
// the platform specific code
// parameter checking can go here....
// although we might just want to have these functions inline, or not have them at all?

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CQuickAnim::GetInterpolatedFrames( Mth::Quat* pRotations, Mth::Vector* pTranslations, uint32* pSkipList, uint32 skipIndex, float time )
{
	plat_get_interpolated_frames( pRotations, pTranslations, pSkipList, skipIndex, time );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CQuickAnim::GetInterpolatedHiResFrames( Mth::Quat* pRotations, Mth::Vector* pTranslations, float time )
{
	Dbg_MsgAssert( mp_frameData, ( "No pointer to frame data" ) );

	mp_frameData->GetInterpolatedCameraFrames(pRotations, pTranslations, time, this);
	
	m_quickAnimPointers.valid = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CQuickAnim::GetNumBones()
{
	Dbg_MsgAssert( mp_frameData, ( "No pointer to frame data" ) );

	return mp_frameData->GetNumBones();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CQuickAnim::ResetCustomKeys()
{
	Dbg_MsgAssert( mp_frameData, ( "No pointer to frame data" ) );

	mp_frameData->ResetCustomKeys();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CQuickAnim::GetDuration()
{
	Dbg_MsgAssert( mp_frameData, ( "No pointer to frame data" ) );

	return mp_frameData->GetDuration();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
uint32 CQuickAnim::GetBoneName( int i )
{
	Dbg_MsgAssert( mp_frameData, ( "No pointer to frame data" ) );

	return mp_frameData->GetBoneName( i );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CQuickAnim::ProcessCustomKeys( float startTimeInclusive, float endTimeInclusive, Obj::CObject* pObject )
{
	Dbg_MsgAssert( mp_frameData, ( "No pointer to frame data" ) );

	bool inclusive = true;
	return mp_frameData->ProcessCustomKeys( startTimeInclusive, endTimeInclusive, pObject, inclusive );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}



================================================
FILE: Code/Gfx/NxQuickAnim.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxQuickAnim.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/4/2003
//****************************************************************************

#ifndef	__GFX_NXQUICKANIM_H__
#define	__GFX_NXQUICKANIM_H__

// The CQuickAnim is a per-object interface to the CBonedAnimFrameData
// (which is raw animation data).  Because multiple objects can be playing 
// the same animation at the same time, we can't do anything fancy like 
// caching pointers inside the CBonedAnimFrameData;  however, since each 
// object has its own CQuickAnim, we can store pointers or decompress data
// or whatever here.  I've decided to give this class a p-line interface
// so that different platforms can implement different optimizations
// based on whether speed or memory is the bottleneck.  							
							
/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

namespace Gfx
{
	class CBonedAnimFrameData;
}

namespace Obj
{
	class CObject;
}

namespace Mth
{
	class Quat;
	class Vector;
}

namespace Nx
{

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class CQuickAnim : public Spt::Class
{
public:
	CQuickAnim();
	virtual ~CQuickAnim();

public:
	void						SetAnimAssetName( uint32 animAssetName );
	void						GetInterpolatedFrames( Mth::Quat* pRotations, Mth::Vector* pTranslations, uint32* pSkipList, uint32 skipIndex, float time );
	void						GetInterpolatedHiResFrames( Mth::Quat* pRotations, Mth::Vector* pTranslations, float time );
	void						Enable(bool enabled);
	int							GetNumBones();
	void						ResetCustomKeys();
	float						GetDuration();
	uint32						GetBoneName( int i );
	bool						ProcessCustomKeys( float startTimeInclusive, float endTimeExclusive, Obj::CObject* pObject );

private:
    // The virtual functions will have a stub implementation in p_nxquickanim.cpp
	virtual	void				plat_get_interpolated_frames( Mth::Quat* pRotations, Mth::Vector* pTranslations, uint32* pSkipList, uint32 skipIndex, float time );

protected:
	Gfx::CBonedAnimFrameData*	mp_frameData;
	uint32						m_animAssetName;

public:
	Gfx::SQuickAnimPointers		m_quickAnimPointers;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // namespace Gfx

#endif	// __GFX_NXQUICKANIM_H__


================================================
FILE: Code/Gfx/NxScene.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////
// NX.CPP - Platform independent interface to the platfrom specific engine code 

#include "gfx/nx.h"
#include "gfx/NxSector.h"
#include "gfx/NxTexMan.h"

#include "core/debug.h"

#include 
#include 


#include 
#include 

#include 	   
#include 	   
#include 
#include 

#include 

#ifdef	__PLAT_NGPS__
// For wireframe debuging mode
#include 
#include 
#include 
#include 
#endif

#include 

namespace	Nx
{

///////////////////////////////////////////////////////////////////
// CScene definitions

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CScene::CScene( int sector_table_size ) : mp_sector_man(NULL)
{
	mp_sector_table = new Lst::HashTable< CSector >( sector_table_size );

//	Dbg_MsgAssert(0,("secotr-tabel_size = %d\n",sector_table_size))

	m_scene_filename[0] = '\0';
	
	// Initilizing supersector and collision crap to NULL, as 
	// we might not have any (like for skys and background stuff)
	mp_sector_man = NULL;
	mp_coll_sectors = NULL;
	mp_coll_sector_data = NULL;
	m_num_coll_sectors = 0;

	// Incremental update
	m_add_scene_filename[0] = '\0';
	mp_add_tex_dict = NULL; 
	mp_add_coll_sectors = NULL;
	mp_add_coll_sector_data = NULL;
	m_num_add_coll_sectors = 0;

	m_using_add_sectors = false;
	mp_orig_sectors = new Lst::Head < CSector >;
	mp_add_sectors = new Lst::Head < CSector >;

	m_in_super_sectors = false;
	m_sky = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CScene::~CScene()
{

	// Mick: iterate over all the sectors, and delete them
	// (deleting the mp_sector_table just deletes the references to them)
	if (mp_sector_table)
	{
		mp_sector_table->IterateStart();
		CSector *p_sector;
		while ((p_sector = mp_sector_table->IterateNext()))
		{
			p_sector->clear_flags(CSector::mIN_SUPER_SECTORS);	// Tells cloned sectors it is OK to go away (and SuperSectors will be dead, anyway)
			delete p_sector;
		}
		delete mp_sector_table;
	}
	
	// Remove SuperSectors
	if (mp_sector_man)
	{
		delete mp_sector_man;
	}
	
	// Remove Collision
	if (mp_coll_sectors)
	{
		delete[] mp_coll_sectors;
	}

	if (mp_coll_sector_data)
	{
		Pip::Unload(m_coll_filename);
	}

	// And the toggle list heads
	if (mp_orig_sectors)
	{
		delete mp_orig_sectors;
	}

	if (mp_add_sectors)
	{
		delete mp_add_sectors;
	}

	// Check if additional texture dictionary was loaded
	if (mp_add_tex_dict) 
	{
		Nx::CTexDictManager::sUnloadTextureDictionary(mp_add_tex_dict);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CScene::UnloadAddScene()
{
	if (m_using_add_sectors)
	{
	   	ToggleAddScene();
	}

	if (!plat_unload_add_scene())
	{
		return false;
	}

	if (mp_add_coll_sectors)
	{
		delete[] mp_add_coll_sectors;
	}

	if (mp_add_coll_sector_data)
	{
		Pip::Unload(m_add_coll_filename);
	}

	if (mp_orig_sectors)
	{
		Lst::Node< CSector > *obj_node, *next;

		for(obj_node = mp_orig_sectors->GetNext(); obj_node; obj_node = next)
		{
			next = obj_node->GetNext();

			delete obj_node;
		}
	}

	if	(mp_add_sectors)
	{
		Lst::Node< CSector > *obj_node, *next;

		for(obj_node = mp_add_sectors->GetNext(); obj_node; obj_node = next)
		{
			next = obj_node->GetNext();

			DeleteSector(obj_node->GetData());		// And delete the sector itself
			delete obj_node;
		}
	}

	m_using_add_sectors = false;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSector * 		CScene::GetSector(uint32 sector_checksum)
{
	return mp_sector_table->GetItem(sector_checksum);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 			CScene::AddSector(CSector *pSector)
{
	mp_sector_table->PutItem(pSector->GetChecksum(), pSector);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSector *		CScene::ReplaceSector(CSector *pSector)
{
	CSector *p_old_sector = mp_sector_table->GetItem(pSector->GetChecksum());

	// Possibly delete old sector
	if (p_old_sector)
	{
		//if (p_old_sector->get_flags() & CSector::mCLONE)
		//{
		//	DeleteSector(p_old_sector);
		//} else {
			remove_sector_from_table(p_old_sector->GetChecksum());	// can't delete something that is PIP'ed in
			p_old_sector->SetActive(false);
		//}

		// We also want this out of the SuperSectors
		p_old_sector->set_flags(CSector::mREMOVE_FROM_SUPER_SECTORS);

		// Also add to orig list
		Lst::Node *node = new Lst::Node(p_old_sector);
		mp_orig_sectors->AddToTail(node);

		Dbg_Message("Replacing sector %x", pSector->GetChecksum());
	} else {
		Dbg_Message("Adding sector %x", pSector->GetChecksum());
	}

	// And add new one
	// Garrett TODO: Should I check to see if there is a CCollObj here?  Or UpdateSuperSectors()?  Or does it matter?
	pSector->set_flags(CSector::mADD_TO_SUPER_SECTORS);			// And do an UpdateSuperSectors() after all the calls
	AddSector(pSector);

	Lst::Node *node = new Lst::Node(pSector);
	mp_add_sectors->AddToTail(node);

	return p_old_sector;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CScene::ToggleAddScene()
{
	if (mp_add_sectors->CountItems() == 0)
	{
		return false;
	}

	Lst::Head < CSector > *p_on_sectors, *p_off_sectors;
	Lst::Node< CSector > *obj_node, *next;
	CSector *p_sector;

	// Do actual toggle
	m_using_add_sectors = !m_using_add_sectors;

	// Figure out which to turn on and off
	if (m_using_add_sectors)
	{
		p_on_sectors = mp_add_sectors;
		p_off_sectors = mp_orig_sectors;
	} else {
		p_on_sectors = mp_orig_sectors;
		p_off_sectors = mp_add_sectors;
	}

	// Do off list first
	for(obj_node = p_off_sectors->GetNext(); obj_node; obj_node = next)
	{
		next = obj_node->GetNext();
		p_sector = obj_node->GetData();

		p_sector->set_flags(CSector::mREMOVE_FROM_SUPER_SECTORS);
		remove_sector_from_table(p_sector->GetChecksum());
		p_sector->SetActive(false);
		//Dbg_Message("Removed sector %x in toggle", p_sector->GetChecksum());
	}

	// Do on list
	for(obj_node = p_on_sectors->GetNext(); obj_node; obj_node = next)
	{
		next = obj_node->GetNext();
		p_sector = obj_node->GetData();

		p_sector->set_flags(CSector::mADD_TO_SUPER_SECTORS);
		AddSector(p_sector);
		p_sector->SetActive(true);
		//Dbg_Message("Added sector %x in toggle", p_sector->GetChecksum());
	}

	// And update the SuperSectors
	UpdateSuperSectors(p_off_sectors);

	return m_using_add_sectors;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 			CScene::CloneSector(uint32 orig_sector_checksum, CScene *p_dest_scene, bool instance, bool add_to_super_sectors)
{
	CSector *p_orig_sector = GetSector(orig_sector_checksum);

	if (p_orig_sector)
	{
		return CloneSector(p_orig_sector, p_dest_scene, instance, add_to_super_sectors);
	} else {
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 			CScene::CloneSector(CSector *p_orig_sector, CScene *p_dest_scene, bool instance, bool add_to_super_sectors)
{
	// Use current scene if dest is NULL
	if (!p_dest_scene)
	{
		p_dest_scene = this;
	}

	CSector *p_new_sector = p_orig_sector->clone(instance, add_to_super_sectors, p_dest_scene);

	if (p_new_sector)
	{
		p_dest_scene->AddSector(p_new_sector);

		return p_new_sector->GetChecksum();
	} else {
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool 			CScene::DeleteSector(uint32 sector_checksum)
{
	CSector *p_sector = GetSector(sector_checksum);

	if (p_sector)
	{
		return DeleteSector(p_sector);
	} else {
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool 			CScene::DeleteSector(CSector *p_sector)
{
	if (p_sector->get_flags() & CSector::mIN_SUPER_SECTORS)
	{
		p_sector->set_flags(CSector::mMARKED_FOR_DELETION);		// Can't delete, yet

		return false;
	} else {
		if (GetSector(p_sector->GetChecksum()) == p_sector)		// Just in case we're doing this with AddSectors
		{
			mp_sector_table->FlushItem(p_sector->GetChecksum());
		}

		delete p_sector;

		return true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CScene::PostLoad(const char *p_name)
{
	strcpy(m_scene_filename, p_name);	// needs name for Pip::Unload()

	plat_post_load();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CScene::PostAdd(const char *p_name, Nx::CTexDict * p_tex_dict)
{
	strcpy(m_add_scene_filename, p_name);	// needs to store name somewhere for Pip::Unload(), if we use Pip
	mp_add_tex_dict = p_tex_dict; 
	m_using_add_sectors = true;

	plat_post_add();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CScene::read_collision(const char *p_name, char *p_pip_name, int &num_coll_sectors,
									   CCollStaticTri * &p_coll_sectors, CCollObjTriData * &p_coll_sector_data, Mth::CBBox &bbox, bool is_net)
{
	// for now collision is kind of assumed to be platform independent 
	sprintf(p_pip_name,"levels\\%s\\%s%s.col.%s",p_name,p_name,is_net?"_net":"",CEngine::sGetPlatformExtension());

	Dbg_Message ( "Loading collision %s....", p_pip_name );
	
	bool	found_point = false;

	uint8 *p_base_addr = (uint8 *) Pip::Load(p_pip_name);
	if (p_base_addr)
	{
		Nx::CCollObjTriData::SReadHeader *p_header = (Nx::CCollObjTriData::SReadHeader *) p_base_addr;
		p_base_addr += sizeof(Nx::CCollObjTriData::SReadHeader);

#ifndef __PLAT_NGC__
		Dbg_Message ( "Version # %d header sizeof %d", p_header->m_version, sizeof(Nx::CCollObjTriData));
		Dbg_Message ( "Number of objects: %d verts: %d faces: %d", p_header->m_num_objects, p_header->m_total_num_verts, p_header->m_total_num_faces_large + p_header->m_total_num_faces_small);
		Dbg_Message ( "Small verts: %d Large verts: %d", p_header->m_total_num_verts_small, p_header->m_total_num_verts_large);
#endif		// __PLAT_NGC__
		Dbg_MsgAssert(p_header->m_version >= 9, ("Collision version must be at least 9."));

		// reserve space for objects
		num_coll_sectors = p_header->m_num_objects;
		p_coll_sector_data = (Nx::CCollObjTriData *) p_base_addr; //new Cld::CCollSector [m_num_coll_sectors];

		// Calculate base addresses for vert and face arrays
		uint8 *p_base_vert_addr = (uint8 *) (p_coll_sector_data + num_coll_sectors);
#ifndef __PLAT_NGC__
		p_base_vert_addr = (uint8 *)(((uint)(p_base_vert_addr+15)) & 0xFFFFFFF0);	// Align to 128 bit boundary
#ifdef FIXED_POINT_VERTICES
		uint8 *p_base_intensity_addr = p_base_vert_addr + (p_header->m_total_num_verts_large * Nx::CCollObjTriData::GetVertElemSize() +
														   p_header->m_total_num_verts_small * Nx::CCollObjTriData::GetVertSmallElemSize());
		uint8 *p_base_face_addr = p_base_intensity_addr + p_header->m_total_num_verts;
		p_base_face_addr = (uint8 *)(((uint)(p_base_face_addr+3)) & 0xFFFFFFFC);	// Align to 32 bit boundary
#else
		uint8 *p_base_intensity_addr = NULL;
		uint8 *p_base_face_addr = p_base_vert_addr + (p_header->m_total_num_verts * Nx::CCollObjTriData::GetVertElemSize());
		p_base_face_addr = (uint8 *)(((uint)(p_base_face_addr+15)) & 0xFFFFFFF0);	// Align to 128 bit boundary
#endif // FIXED_POINT_VERTICES
#else
		uint8 *p_base_intensity_addr = NULL;
		uint8 *p_base_face_addr = p_base_vert_addr + (p_header->m_total_num_faces * Nx::CCollObjTriData::GetVertElemSize());
		p_base_face_addr = (uint8 *)(((uint)(p_base_face_addr+3)) & 0xFFFFFFFC);	// Align to 32 bit boundary
#endif		// __PLAT_NGC__

		// Calculate addresses for BSP arrays
#ifndef __PLAT_NGC__
		uint8 *p_node_array_size = p_base_face_addr + (p_header->m_total_num_faces_large * Nx::CCollObjTriData::GetFaceElemSize() +
													   p_header->m_total_num_faces_small * Nx::CCollObjTriData::GetFaceSmallElemSize());
		p_node_array_size += ( p_header->m_total_num_faces_large & 1 ) ? 2 : 0;
#else
		uint8 *p_node_array_size = p_base_face_addr + ( p_header->m_total_num_faces * Nx::CCollObjTriData::GetFaceElemSize() );
		p_node_array_size += ( p_header->m_total_num_faces & 1 ) ? 2 : 0;
#endif		// __PLAT_NGC__
		uint8 *p_base_node_addr = p_node_array_size + 4;
		uint8 *p_base_face_idx_addr = p_base_node_addr + *((int *) p_node_array_size);

		// Reserve space for collsion objects
		p_coll_sectors = new CCollStaticTri[p_header->m_num_objects];

		// Read objects
		for (int oidx = 0; oidx < p_header->m_num_objects; oidx++)
		{
			p_coll_sector_data[oidx].InitCollObjTriData(this, p_base_vert_addr, p_base_intensity_addr, p_base_face_addr,
														p_base_node_addr, p_base_face_idx_addr);
			p_coll_sector_data[oidx].InitBSPTree();

			p_coll_sectors[oidx].SetGeometry(&(p_coll_sector_data[oidx]));

			if (p_coll_sector_data[oidx].GetNumFaces() > 0)	 // only add bbox if there are some faces in the object.....
			{
				// Add to scene bbox
				bbox.AddPoint(p_coll_sector_data[oidx].GetBBox().GetMin());
				bbox.AddPoint(p_coll_sector_data[oidx].GetBBox().GetMax());
				found_point = true;
			}
		}
		if (!found_point)
		{
			// if there was no collision, then set up a dummy 200 inch bounding box at the origin
			bbox.AddPoint(Mth::Vector (-100,-100,-100));
			bbox.AddPoint(Mth::Vector (100,100,100));
		}
	} 
	else 
	{
		Dbg_Error ( "Could not open collision file\n" );
		return false;
	}

	Dbg_Message ( "successfully read collision" );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CScene::LoadCollision(const char *p_name, bool is_net)
{
	read_collision(p_name, m_coll_filename, m_num_coll_sectors, mp_coll_sectors, mp_coll_sector_data, m_collision_bbox, is_net);

	Dbg_Message("Scene bounding box: min (%f, %f, %f) max (%f, %f, %f)", 
				m_collision_bbox.GetMin()[X], m_collision_bbox.GetMin()[Y], m_collision_bbox.GetMin()[Z], 
				m_collision_bbox.GetMax()[X], m_collision_bbox.GetMax()[Y], m_collision_bbox.GetMax()[Z]);

	if (m_num_coll_sectors > 0)
	{
		// Create super sectors
		if (m_in_super_sectors)
		{
			mp_sector_man = new SSec::Manager;
			mp_sector_man->GenerateSuperSectors(m_collision_bbox);

			mp_sector_man->AddCollisionToSuperSectors(mp_coll_sectors, m_num_coll_sectors);
		}

		// Add to CSectors
		for (int i = 0; i < m_num_coll_sectors; i++)
		{
			CSector *p_sector = GetSector(mp_coll_sectors[i].GetChecksum());
			if (!p_sector)	// Don't assert now since there may not be renderable data
			{
				// Collision info, but not renderable
				// so, we still need to create a Sector, but with no renderable component
				p_sector = CreateSector(); 			// create empty secotr
				p_sector->SetChecksum(mp_coll_sectors[i].GetChecksum());
				p_sector->SetActive(true);
				AddSector(p_sector);
			}
			p_sector->AddCollSector(&(mp_coll_sectors[i]));
			p_sector->set_flags(CSector::mIN_SUPER_SECTORS);
		}

	}

	CEngine::sGetWeather()->UpdateGrid();

	return plat_load_collision(p_name);	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CScene::AddCollision(const char *p_name)
{
	Mth::CBBox			add_collision_bbox;

	if (mp_coll_sectors == NULL)
	{
		return false;			// collision not needed
	}

	read_collision(p_name, m_add_coll_filename, m_num_add_coll_sectors, mp_add_coll_sectors, mp_add_coll_sector_data, add_collision_bbox);

	if ((add_collision_bbox.GetMin()[X] < m_collision_bbox.GetMin()[X]) ||
		(add_collision_bbox.GetMax()[X] > m_collision_bbox.GetMax()[X]) ||
		(add_collision_bbox.GetMin()[Z] < m_collision_bbox.GetMin()[Z]) ||
		(add_collision_bbox.GetMax()[Z] > m_collision_bbox.GetMax()[Z]))
	{
		Dbg_Message("Add Scene bounding box: min (%f, %f, %f) max (%f, %f, %f)", 
					add_collision_bbox.GetMin()[X], add_collision_bbox.GetMin()[Y], add_collision_bbox.GetMin()[Z], 
					add_collision_bbox.GetMax()[X], add_collision_bbox.GetMax()[Y], add_collision_bbox.GetMax()[Z]);
		Dbg_Message("Scene bounding box: min (%f, %f, %f) max (%f, %f, %f)", 
					m_collision_bbox.GetMin()[X], m_collision_bbox.GetMin()[Y], m_collision_bbox.GetMin()[Z], 
					m_collision_bbox.GetMax()[X], m_collision_bbox.GetMax()[Y], m_collision_bbox.GetMax()[Z]);
		Dbg_MsgAssert(add_collision_bbox.Within(m_collision_bbox), ("Can't add collision outside of original scene"));
	}

	if (m_num_add_coll_sectors > 0)
	{
		// Add to CSectors
		for (int i = 0; i < m_num_add_coll_sectors; i++)
		{
			CSector *p_sector = GetSector(mp_add_coll_sectors[i].GetChecksum());
			if (!p_sector)	// Don't assert now since there may not be renderable data
			{
				Dbg_Message("We don't necessarily want to be creating invisible sectors on an incremental update");

				// Collision info, but not renderable
				// so, we still need to create a Sector, but with no renderable component
				p_sector = CreateSector(); 			// create empty secotr
				p_sector->SetChecksum(mp_add_coll_sectors[i].GetChecksum());
				p_sector->SetActive(true);
				p_sector->set_flags(CSector::mADD_TO_SUPER_SECTORS);
				AddSector(p_sector);

				Lst::Node *node = new Lst::Node(p_sector);
				mp_add_sectors->AddToTail(node);
			}
			p_sector->AddCollSector(&(mp_add_coll_sectors[i]));
			Dbg_Assert(p_sector->get_flags() & CSector::mADD_TO_SUPER_SECTORS);
		}

		// Now add to SuperSectors
		UpdateSuperSectors(mp_orig_sectors);
	}

	return plat_add_collision(p_name);	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CScene::CreateCollision(const Mth::CBBox & scene_bbox)
{
	// Make sure we asked for it at scene creation
	if (!m_in_super_sectors)
	{
		return false;
	}

	m_collision_bbox = scene_bbox;
	mp_sector_man = new SSec::Manager;
	mp_sector_man->GenerateSuperSectors(m_collision_bbox);

	return true;
}

#ifdef __NOPT_ASSERT__
void			CScene::DebugRenderCollision(uint32 ignore_1, uint32 ignore_0)
{
	if (Nx::CEngine::GetWireframeMode() == 4 || Nx::CEngine::GetWireframeMode() == 5)
	{
	
		#ifdef	__PLAT_NGPS__
		#if 1
		
		// Traverse the tree at the mesh level
		// so we can see the actual meshes
		
		CPs2Scene * p_ps2_scene = (CPs2Scene *) Nx::CEngine::sGetMainScene();
		p_ps2_scene->GetEngineScene()->RenderWireframe(Nx::CEngine::GetWireframeMode());

		
			 
		#else	 
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
		Mth::Vector	*p_verts = new Mth::Vector[100000];
		Mem::Manager::sHandle().PopContext();
		
		
		Nx::CScene	*p_scene = Nx::CEngine::sGetMainScene();
		// crappy wireframe rendering of renderable, so we can see if there is any invislbe junk there
		Lst::HashTable< Nx::CSector > * p_sector_list = p_scene->GetSectorList();
		
		int sectors = 0;
		int vert_count=0;
		int lines = 0;
		if (p_sector_list)
		{
			p_sector_list->IterateStart();	
			Nx::CSector *p_sector = p_sector_list->IterateNext();		
			while(p_sector && lines <100000)   // Can't draw too many lines in this mode for some reason
			{
	
				if (p_sector->IsActive())
				{
			
							 
					Nx::CGeom 	*p_geom = p_sector->GetGeom();
					if (p_geom)
					{
					
						NxPs2::CGeomNode *p_node = (((CPs2Geom*)p_geom)->GetEngineObject());
						if (p_node->WasRendered())
						{
							sectors++;
						
							//
							// Do renderable geometry
							int verts = p_geom->GetNumRenderVerts();
							if ( verts > 3000)
							{
							//	printf ("Strange no of verts %d on %s\n",verts, Script::FindChecksumName(p_sector->GetChecksum()));
							}
							if (verts < 30000)
							{
								//NxPs2::BeginLines3D(0x80000000 + (0x00ff00ff));		// Magenta 
								
								vert_count += verts;  
								  
								#define	peak 500
								int n=p_geom->GetNumRenderPolys();
								int r,g,b;
								r=g=b=0;
								if (n <= peak )
								{
									r = (255 * (n)) / peak;  	// r ramps up (black to red)	
								}
								else if (n <= peak * 2)
								{
									r = 255;
									b = (255 * (n - peak) / (peak));	// b&g ramps up (to white)
									g = b;
									
								}
								else
								{
									r = g = b = 255;
								}
								uint32 rgb = (b<<16)|(g<<8)|r;	

								
								if (Nx::CEngine::GetWireframeMode() == 4)
								{
									NxPs2::BeginLines3D(0x80000000 + (0x00ffffff & p_sector->GetChecksum()));		
								}
								else
								{
									NxPs2::BeginLines3D(0x80000000 + (0x00ffffff & rgb));		
								}
								
								p_geom->GetRenderVerts(p_verts);
								Mth::Vector *p_vert = p_verts+1;
								for (int i = 1; i < verts; i++)
								{
									NxPs2::DrawLine3D((*p_vert)[X],(*p_vert)[Y],(*p_vert)[Z],p_vert[-1][X],p_vert[-1][Y],p_vert[-1][Z]);								
									p_vert++;
									lines++;
								} // end for
						
								NxPs2::EndLines3D();
							} // end if
						}						
					}
				}
				p_sector = p_sector_list->IterateNext();
			}
		}
//		printf ("Renderable wireframe %d sectors, %d verts, %d lines\n",sectors, vert_count, lines);
		delete [] p_verts;
		#endif
		#endif
	}	
	else
	{
		if (!m_in_super_sectors)
		{
			// if collision data is not being used, then just return
			return;
		}
	
		if (mp_coll_sectors)
		{
			for (int i = 0; i < m_num_coll_sectors; i++)
			{
				mp_coll_sectors[i].DebugRender(ignore_1,ignore_0);
			}
		} else {
			// Try finding the collision through the sectors
			mp_sector_table->IterateStart();
			CSector *p_sector;
			while ((p_sector = mp_sector_table->IterateNext()))
			{
				if (p_sector->GetCollSector())	  // Might not have it yet in park editor
				{
					p_sector->GetCollSector()->DebugRender(ignore_1, ignore_0);
				}
			}
		}
	}
}
#endif

void			CScene::DebugCheckForHoles()
{

	for (int i = 0; i < m_num_coll_sectors; i++)
	{
		printf ("%4d/%d ",i,m_num_coll_sectors);
		CCollStaticTri *p_static_tri = static_cast(&(mp_coll_sectors[i]));
		Dbg_Assert(p_static_tri);
		p_static_tri->CheckForHoles();
	}

}


// Return the number of collision sectors in the scene
int		CScene::GetNumCollSectors()
{
	return m_num_coll_sectors;
}

// return a pointer to static collision data
// for a collision sector
// used
CCollStaticTri * CScene::GetCollStatic(int n)
{
	return &(mp_coll_sectors[n]);	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CScene::UpdateSuperSectors(Lst::Head *p_additional_remove_list)
{
	// Check if we even have SuperSectors
	if (!mp_sector_man)
	{
		return;
	}

	Lst::Head< Nx::CCollStatic > coll_add_list;
	Lst::Head< Nx::CCollStatic > coll_remove_list;
	Lst::Head< Nx::CCollStatic > coll_update_list;
	Lst::Head< CSector > 	  sector_delete_list;

	/*
	Mem::Heap *p_top_heap = Mem::Manager::sHandle().TopDownHeap();
	Mem::Region *p_top_region = p_top_heap->mp_region;
	Ryan("UpdateSuperSectors()\n");
	Ryan("  free %d\n", p_top_heap->mFreeMem.m_count + p_top_region->MemAvailable());
	Ryan("  used %d\n", p_top_heap->mUsedMem.m_count);
	Ryan("  largest %d\n", p_top_heap->LargestFreeBlock());
	*/

	// Allocate temp list on high heap
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

	mp_sector_table->IterateStart();

	Lst::Node< Nx::CCollStatic > *node;
	CSector *p_sector;

	// Make Add and remove list
	while ((p_sector = mp_sector_table->IterateNext()))
	{
		// Add List
		if (p_sector->get_flags() & CSector::mADD_TO_SUPER_SECTORS)
		{
			node = new Lst::Node< Nx::CCollStatic > ( p_sector->GetCollSector() );
			coll_add_list.AddToTail(node);
			p_sector->clear_flags(CSector::mADD_TO_SUPER_SECTORS);
			p_sector->set_flags(CSector::mIN_SUPER_SECTORS);

			//Dbg_Message("Adding sector %x and collision %x of checksum %x", p_sector, p_sector->GetCollSector(), p_sector->GetChecksum());
			//Mth::CBBox b_box = p_sector->GetCollSector()->GetGeometry()->GetBBox();
			//Dbg_Message("Adding sector %x and collision %x and bounding box [%f,%f,%f]-[%f,%f,%f]", p_sector->GetChecksum(), p_sector->GetCollSector()->GetChecksum(),
			//			   b_box.GetMin()[X], b_box.GetMin()[Y], b_box.GetMin()[Z],
			//			   b_box.GetMax()[X], b_box.GetMax()[Y], b_box.GetMax()[Z]);

			Dbg_MsgAssert(!(p_sector->get_flags() & CSector::mINVALID_SUPER_SECTORS),
						  ("Trying to add collision that needed updating"));
		}

		// Remove List
		if (p_sector->get_flags() & (CSector::mMARKED_FOR_DELETION | CSector::mREMOVE_FROM_SUPER_SECTORS))
		{
			node = new Lst::Node< Nx::CCollStatic > ( p_sector->GetCollSector() );
			coll_remove_list.AddToTail(node);

			// Also make a sector delete list
			if (p_sector->get_flags() & CSector::mMARKED_FOR_DELETION)
			{
				Lst::Node< CSector > *sec_node = new Lst::Node< CSector > (p_sector);
				sector_delete_list.AddToTail(sec_node);
			} else {
				p_sector->clear_flags(CSector::mREMOVE_FROM_SUPER_SECTORS);
			}

			//Dbg_Message("Deleting sector %x and collision %x of checksum %x", p_sector, p_sector->GetCollSector(), p_sector->GetChecksum());
			//Mth::CBBox b_box = p_sector->GetCollSector()->GetGeometry()->GetBBox();
			//Dbg_Message("Deleting sector %x and collision %x and bounding box [%f,%f,%f]-[%f,%f,%f]", p_sector->GetChecksum(), p_sector->GetCollSector()->GetChecksum(),
			//				b_box.GetMin()[X], b_box.GetMin()[Y], b_box.GetMin()[Z],
			//				b_box.GetMax()[X], b_box.GetMax()[Y], b_box.GetMax()[Z]);
			Dbg_MsgAssert(!(p_sector->get_flags() & CSector::mADD_TO_SUPER_SECTORS),
						  ("Collision being both added and deleted from SuperSectors"));
			Dbg_MsgAssert(p_sector->get_flags() & CSector::mIN_SUPER_SECTORS,
						  ("Trying to remove sector from SuperSector that isn't in any SuperSector"));

			Dbg_MsgAssert(!(p_sector->get_flags() & CSector::mINVALID_SUPER_SECTORS),
						  ("Trying to remove collision that hasn't been updated"));
		}

		// Update List
		if (p_sector->get_flags() & CSector::mINVALID_SUPER_SECTORS)
		{
			node = new Lst::Node< Nx::CCollStatic > ( p_sector->GetCollSector() );
			coll_update_list.AddToTail(node);
			p_sector->clear_flags(CSector::mINVALID_SUPER_SECTORS);

			//Mth::CBBox b_box = p_sector->GetCollSector()->GetGeometry()->GetBBox();
			//Dbg_Message("Updating sector %x and collision %x and bounding box [%f,%f,%f]-[%f,%f,%f]", p_sector->GetChecksum(), p_sector->GetCollSector()->GetChecksum(),
			//			   b_box.GetMin()[X], b_box.GetMin()[Y], b_box.GetMin()[Z],
			//			   b_box.GetMax()[X], b_box.GetMax()[Y], b_box.GetMax()[Z]);

			Dbg_MsgAssert(p_sector->get_flags() & CSector::mIN_SUPER_SECTORS,
						  ("Collision being updated that isn't in SuperSectors"));
		}
	}

	// Check additional remove list
	if (p_additional_remove_list)
	{
		Lst::Node< CSector > *obj_node, *next;

		for(obj_node = p_additional_remove_list->GetNext(); obj_node; obj_node = next)
		{
			next = obj_node->GetNext();
			p_sector = obj_node->GetData();

			Dbg_Assert(!(p_sector->get_flags() & CSector::mMARKED_FOR_DELETION));		// Make sure it shouldn't be deleted

			// Make sure it isn't already on the list
			bool new_node = true;
			if(coll_remove_list.CountItems() > 0)
			{
				Lst::Node *node_coll, *node_next;
				for(node_coll = coll_remove_list.GetNext(); node_coll; node_coll = node_next)
				{
					node_next = node_coll->GetNext();
					if (node_coll->GetData() == p_sector->GetCollSector())
					{
						new_node = false;
						break;
					}

				}
			}

			if (new_node)
			{
				node = new Lst::Node< Nx::CCollStatic > ( p_sector->GetCollSector() );
				coll_remove_list.AddToTail(node);
				p_sector->clear_flags(CSector::mREMOVE_FROM_SUPER_SECTORS);
				//Dbg_Message("Deleting additional sector %x and collision %x of checksum %x", p_sector, p_sector->GetCollSector(), p_sector->GetChecksum());
				//Dbg_Message("Deleting additional sector %x and collision %x", p_sector->GetChecksum(), p_sector->GetCollSector()->GetChecksum());
			}
		}
	}

	Mem::Manager::sHandle().PopContext();

	// Do the actual update
	mp_sector_man->UpdateCollisionSuperSectors(coll_add_list, coll_remove_list, coll_update_list);

	Lst::Node *node_coll, *node_next;

	//
	// Free lists
	//
	if(coll_add_list.CountItems() > 0)
	{
		for(node_coll = coll_add_list.GetNext(); node_coll; node_coll = node_next)
		{
			node_next = node_coll->GetNext();

			delete node_coll;
		}
	}

	if(coll_remove_list.CountItems() > 0)
	{
		for(node_coll = coll_remove_list.GetNext(); node_coll; node_coll = node_next)
		{
			node_next = node_coll->GetNext();

			delete node_coll;
		}
	}

	if(coll_update_list.CountItems() > 0)
	{
		for(node_coll = coll_update_list.GetNext(); node_coll; node_coll = node_next)
		{
			node_next = node_coll->GetNext();

			delete node_coll;
		}
	}

	// And finally delete the marked sectors
	if(sector_delete_list.CountItems() > 0)
	{
		Lst::Node *node_sector, *node_sec_next;
		for(node_sector = sector_delete_list.GetNext(); node_sector; node_sector = node_sec_next)
		{
			node_sec_next = node_sector->GetNext();

			p_sector = node_sector->GetData();

			mp_sector_table->FlushItem(p_sector->GetChecksum());

			p_sector->clear_flags(CSector::mIN_SUPER_SECTORS);
			delete p_sector;

			delete node_sector;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CScene::ClearSuperSectors()
{
	// Check if we even have SuperSectors
	if (!mp_sector_man)
	{
		return;
	}

	mp_sector_table->IterateStart();

	CSector *p_sector;

	// Check all sectors to make sure they need deleting
	while ((p_sector = mp_sector_table->IterateNext()))
	{
		// Remove List
		if (p_sector->get_flags() & CSector::mMARKED_FOR_DELETION)
		{
			//Dbg_Message("Deleting sector %x and collision %x of checksum %x", p_sector, p_sector->GetCollSector(), p_sector->GetChecksum());
			Dbg_MsgAssert(!(p_sector->get_flags() & CSector::mADD_TO_SUPER_SECTORS),
						  ("Collision being both added and deleted from SuperSectors"));
			Dbg_MsgAssert(p_sector->get_flags() & CSector::mIN_SUPER_SECTORS,
						  ("Trying to remove sector from SuperSector that isn't in any SuperSector"));

			Dbg_MsgAssert(!(p_sector->get_flags() & CSector::mINVALID_SUPER_SECTORS),
						  ("Trying to remove collision that hasn't been updated"));

			p_sector->clear_flags(CSector::mIN_SUPER_SECTORS);
			delete p_sector;
		} else {
			Dbg_MsgAssert(0, ("Trying to clear SuperSectors even though sector %x hasn't been marked for deletion", p_sector->GetChecksum()));
		}
	}

	// Do the actual clear
	mp_sector_man->ClearCollisionSuperSectors();

	// And clear the hash table
	mp_sector_table->FlushAllItems();
}

// Given a bounding box, then set the active flag of those 
// sectors that intersect this box by at least a certain abount 
void CScene::SetActiveInBox(Mth::CBBox &box, bool active, float min_intersect)
{
	
	Mth::CBBox smaller_box = box;
	
	// later..
//	smaller_box.Shrink(min_intersect);   
	
	mp_sector_table->IterateStart();
	CSector *p_sector;
	while ((p_sector = mp_sector_table->IterateNext()))
	{
//		printf ("Checking sector\n");
		if (smaller_box.Intersect(p_sector->GetBoundingBox()))
		{
//			printf("Setting active = %d\n",active);
			p_sector->SetActive(active);
		}
	}

}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Create an empty sector
CSector	*			CScene::CreateSector()
{
	CSector	*p_sector = plat_create_sector();
	p_sector->mp_geom = NULL;
	p_sector->mp_coll_sector = NULL;
	return p_sector;
}


void	CScene::GetMetrics(Script::CStruct * p_info)
{
	p_info->AddString(CRCD(0xc3f4169a,"FileName"),GetSceneFilename());

	int total_sectors = 0;
	int render_sectors = 0;	
	int render_verts = 0;	
	int render_polys = 0;	
	int render_base_polys = 0;	
	// Iterate over the sectors, counting verious things	
	Lst::HashTable< Nx::CSector > * p_sector_list = GetSectorList();
	if (p_sector_list)
	{
		p_sector_list->IterateStart();	
		Nx::CSector *p_sector = p_sector_list->IterateNext();		
		while(p_sector)
		{
			total_sectors++;
			Nx::CGeom 	*p_geom = p_sector->GetGeom();
			if (p_geom)
			{
				// Do renderable geometry
				render_sectors++;
				render_verts += p_geom->GetNumRenderVerts();
				render_polys += p_geom->GetNumRenderPolys();
				render_base_polys += p_geom->GetNumRenderBasePolys();
			}
			p_sector = p_sector_list->IterateNext();
		}
	}
	
	p_info->AddInteger(CRCD(0x4a6bf967,"Sectors"),total_sectors);
	p_info->AddInteger(CRCD(0xac30a9d,"ColSectors"),GetNumCollSectors());
	p_info->AddInteger(CRCD(0x969d3af6,"Verts"),render_verts);
	p_info->AddInteger(CRCD(0xd576df05,"Polys"),render_polys);
	p_info->AddInteger(CRCD(0x1e3061ee,"BasePolys"),render_base_polys);

	p_info->AddInteger(CRCD(0x5714c480,"TextureMemory"),GetTexDict()->GetFileSize());
	
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CScene::SetMajorityColor(Image::RGBA rgba)
{
	plat_set_majority_color(rgba);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA	CScene::GetMajorityColor() const
{
	return plat_get_majority_color();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CScene::plat_post_load()
{
	printf ("STUB: PostLoad\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CScene::plat_post_add()
{
	printf ("STUB: PostAdd\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CScene::plat_load_textures(const char *p_name)
{
	printf ("STUB: LoadTextures\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CScene::plat_load_collision(const char *p_name)
{
	printf ("STUB: LoadCollision\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CScene::plat_add_collision(const char *p_name)
{
	printf ("STUB: AddCollision\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CScene::plat_unload_add_scene()
{
	printf ("STUB: UnloadAddScene\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSector*			CScene::plat_create_sector()
{
	printf ("STUB: plat_create_sector\n");
	return NULL;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CScene::plat_set_majority_color(Image::RGBA rgba)
{
	// STUB: only really needed on PS2, so ignorable on other platforms
}
		 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA			CScene::plat_get_majority_color() const
{
	// STUB: only really needed on PS2, so ignorable on other platforms
	return Image::RGBA(0x80, 0x80, 0x80, 0x80);
}




} // namespace Nx






================================================
FILE: Code/Gfx/NxScene.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// NXSCENE.H - Neversoft Engine, Rendering portion, Platform independent interface

#ifndef	__GFX_NX_SCENE_H__
#define	__GFX_NX_SCENE_H__

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 

#include 
#include 	


namespace Script
{
	class	CStruct;
}

namespace Nx
{
/////////////////////////////////////////////////////////////////////////
// Forward declarations
class	CSector;
class	CTexDict;
class	CCollStaticTri;
class	CCollObjTriData;

////////////////////////////////////////////////////////////////////////
// CScene:
//
// Holds all the CSectors for a scene.
//

class		CScene
{
public:
								CScene( int sector_table_size = 10 );
	virtual						~CScene();

	void						PostLoad(const char *p_name);
	void						PostAdd(const char *p_name, Nx::CTexDict * p_tex_dict);
	bool						LoadTextures(const char *p_name);			// load scene textures
	bool						LoadCollision(const char *p_name, bool is_net=false);			// load scene collision data
	bool						AddCollision(const char *p_name);			// add scene collision data
	bool						CreateCollision(const Mth::CBBox & scene_bbox); // load scene collision data

	bool						ToggleAddScene();							// Toggle the orig and add sectors
	bool						UnloadAddScene();							// unloads previous add scene

	bool						InSuperSectors() const;
	void						SetInSuperSectors(bool);
	bool						IsSky() const;
	void						SetIsSky(bool);

	CSector	*					GetSector(uint32 sector_checksum);			// get a sector pointer, based on the checksum of the name
	void						AddSector(CSector *pSector);				// Add a sector to the list
	Lst::HashTable *	GetSectorList() const;			  			// get the whole list

	// Replacing a sector is for in-house testing.  Since PIP'ed CSectors can't be deleted individually,
	// the replace will hide the old one.
	CSector *					ReplaceSector(CSector *pSector);			// returns old sector, if any

	// Cloning copies a sector, puts it in hash table, and returns its checksum (p_dest_scene of NULL uses current scene)
	uint32						CloneSector(uint32 orig_sector_checksum, CScene *p_dest_scene = NULL,
											bool instance = false, bool add_to_super_sectors = true);
	uint32						CloneSector(CSector *p_orig_sector, CScene *p_dest_scene = NULL,
											bool instance = false, bool add_to_super_sectors = true);

	// creates an empty sector
	CSector	*					CreateSector();
	
	
	// Deletes a sector and takes it off the list
	bool						DeleteSector(uint32 sector_checksum);
	bool						DeleteSector(CSector *p_sector);

	// Updates the SuperSectors with any clone or quickupdate changes
	void						UpdateSuperSectors(Lst::Head *p_additional_remove_list = NULL);
	void						ClearSuperSectors();						// note ALL sectors must already be marked for deletion

	// allow access to the collision sectors												  
	int							GetNumCollSectors();
	CCollStaticTri * 			GetCollStatic(int n);


	// Sets sectors active or inactive if they are inside a particular bounding box																								  
	void 						SetActiveInBox(Mth::CBBox &box, bool active, float min_intersect);
	
	const Mth::CBBox &			GetRenderBoundingBox() const;				// get bounding box of scene renderables
	const Mth::CBBox &			GetCollisionBoundingBox() const;			// get bounding box of scene collision
	SSec::Manager *				GetSuperSectorManager() const;				// get the super sectors, if any

	void						SetTexDict(CTexDict * p_tex_dict);			// set texture dictionary
	CTexDict *					GetTexDict() const;							// set texture dictionary

	void						SetID(uint32 id);							// Set name checksum ID
	uint32						GetID() const;
	const char *				GetSceneFilename() const;
	const char *				GetAddSceneFilename() const;

	void						DebugRenderCollision(uint32 ignore_1, uint32 ignore_0);						// render in wireframe
	void						DebugCheckForHoles();

	void						GetMetrics(Script::CStruct * p_info);

	void						SetMajorityColor(Image::RGBA rgba);
	Image::RGBA					GetMajorityColor() const;

protected:
	// read scene collision file
	bool						read_collision(const char *p_name, char *p_pip_name, int &num_coll_sectors,
											   CCollStaticTri * &p_coll_sectors, CCollObjTriData * &p_coll_sector_data,
											   Mth::CBBox &bbox, bool is_net=false);

	void						remove_sector_from_table(uint32 sector_checksum);

	char						m_scene_filename[128];						// scene filename (kept around for unload)
	uint32						m_id;										// checksum of name
	
	CTexDict *					mp_tex_dict;								// texture dictionary that this scene uses	
	
	bool						m_in_super_sectors;							// Is this scene in the super sectors
	bool						m_sky;										// Tells if scene is a sky type

	Mth::CBBox					m_render_bbox;								// scene renderable bounding box
	Mth::CBBox					m_collision_bbox;							// scene collision bounding box
	Lst::HashTable< CSector > *	mp_sector_table;							// All the CSector pointers
	SSec::Manager *				mp_sector_man;								// SuperSector manager

	char						m_coll_filename[128];						// collision filename (kept around for unload)
	int							m_num_coll_sectors;							// non-cloned collision
	CCollStaticTri *			mp_coll_sectors;							// Static collision objects
	CCollObjTriData *			mp_coll_sector_data;						// Static collision object data

	// For the incremental update code
	char						m_add_scene_filename[128];
	CTexDict *					mp_add_tex_dict; 
	char						m_add_coll_filename[128];
	int							m_num_add_coll_sectors;
	CCollStaticTri *			mp_add_coll_sectors;
	CCollObjTriData *			mp_add_coll_sector_data;

	bool						m_using_add_sectors;
	Lst::Head < CSector > *		mp_orig_sectors;
	Lst::Head < CSector > *		mp_add_sectors;

private:
	////////////////////////////////////////////////////////////
	// Platform specific function calls	
	//
	virtual void				plat_post_load();
	virtual void				plat_post_add();
	virtual bool				plat_load_textures(const char *p_name);		// load textures 
	virtual bool				plat_load_collision(const char *p_name);	// load collision data
	virtual bool				plat_add_collision(const char *p_name);		// add collision data
	virtual bool				plat_unload_add_scene();					// unloads previous add scene
	virtual CSector	*			plat_create_sector();  						// create empty sector
	virtual void				plat_set_majority_color(Image::RGBA rgba);	// set the most common sector color
	virtual Image::RGBA			plat_get_majority_color() const;			// get the most common sector color
};

///////////////////////////////////////////////////////////////////////
//
// Inline functions
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Lst::HashTable< CSector > * CScene::GetSectorList() const
{
	return mp_sector_table;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool					CScene::InSuperSectors() const
{
	return m_in_super_sectors;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CScene::SetInSuperSectors(bool s)
{
	m_in_super_sectors = s;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool					CScene::IsSky() const
{
	return m_sky;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void					CScene::SetIsSky(bool s)
{
	m_sky = s;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::CBBox &	CScene::GetRenderBoundingBox() const
{
	return m_render_bbox;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::CBBox &	CScene::GetCollisionBoundingBox() const
{
	return m_collision_bbox;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline SSec::Manager *		CScene::GetSuperSectorManager() const
{
	return mp_sector_man;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline 	void				CScene::SetTexDict(CTexDict * p_tex_dict)
{
	mp_tex_dict = p_tex_dict;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	CTexDict *			CScene::GetTexDict() const
{
	return mp_tex_dict;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline 	void				CScene::SetID(uint32 id)
{
	m_id = id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	uint32				CScene::GetID() const
{
	return m_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	const char *		CScene::GetSceneFilename() const
{
	return m_scene_filename;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	const char *		CScene::GetAddSceneFilename() const
{
	return m_add_scene_filename;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline void					CScene::remove_sector_from_table(uint32 sector_checksum)
{
	mp_sector_table->FlushItem(sector_checksum);
}

}


#endif



================================================
FILE: Code/Gfx/NxSector.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxSector.cpp

#include "gfx/nx.h"
#include "gfx/nxgeom.h"
#include "gfx/nxsector.h"
#include "gfx/nxflags.h"
#include "gel/collision/collision.h"
#include 

namespace	Nx
{
///////////////////////////////////////////////////////////////////
// CSector definitions

// These functions are the platform independent part of the interface to 
// the platform specific code
// parameter checking can go here....
// although we might just want to have these functions inline, or not have them at all?
					 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSector::CSector()
{
	m_flags = mCOLLIDE;
	mp_geom = NULL;
	mp_coll_sector = NULL;
	m_rot_y = Mth::ROT_0;
	uint32	light_group = CRCD(0xe3714dc1,"outdoor");			 // default to OutDoors
	SetLightGroup(light_group);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSector::~CSector()
{
	if (mp_coll_sector && (m_flags & mCLONE))	// cloned collision isn't in scene array
	{
		// Check if in SuperSectors
		if (m_flags & mIN_SUPER_SECTORS)
		{
			Dbg_MsgAssert(0, ("Can't free CSector when it is still on the SuperSector lists"));
		}

		delete mp_coll_sector;
	}

	if (mp_geom /*&& (m_flags & mCLONE)*/)			// cloned geometry isn't in scene array
	{
		delete mp_geom;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CSector::GetChecksum() const
{
	return m_checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetChecksum(uint32 c)
{
	m_checksum = c;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom *	CSector::GetGeom() const
{
	return mp_geom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetGeom(CGeom *p_geom)
{
	mp_geom = p_geom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetColor(Image::RGBA rgba)
{		
	if (mp_geom)
	{
		mp_geom->SetColor(rgba);
	//} else {
	//	plat_set_color(rgba);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::ClearColor()
{		
	if (mp_geom)
	{
		mp_geom->ClearColor();
	//} else {
	//	plat_clear_color();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA	CSector::GetColor() const
{		
	if (mp_geom)
	{
		return mp_geom->GetColor();
	} else {
		return Image::RGBA(0, 0, 0, 0);
	//	return plat_get_color();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetVisibility(uint32 mask)
{
	if (mp_geom)
	{
		uint32 vis=mp_geom->GetVisibility();
		if ( (vis^mask) & 0x01 )
		{
			Replay::WriteSectorVisibleStatus(m_checksum,mask&0x01);
		}	
		mp_geom->SetVisibility(mask);
	//} else {
	//	plat_set_visibility(mask);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CSector::GetVisibility() const
{
	if (mp_geom)
	{
		return mp_geom->GetVisibility();
	} else {
		return 0;
	//	return plat_get_visibility();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetActive(bool on)
{
	// Do collision also
	if (mp_coll_sector)
	{
		if (on) {
			mp_coll_sector->ClearObjectFlags(mSD_KILLED);
		} else {
			mp_coll_sector->SetObjectFlags(mSD_KILLED);
		}
	}

	if (mp_geom)
	{
		if (mp_geom->IsActive() != on)
		{
			Replay::WriteSectorActiveStatus(m_checksum,on);
		}
		mp_geom->SetActive(on);
	//} else {
	//	plat_set_active(on);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSector::IsActive() const
{
	if (mp_geom)
	{
		return mp_geom->IsActive();
	} else {
		Dbg_Assert(mp_coll_sector);

		return !(mp_coll_sector->GetObjectFlags() & mSD_KILLED);
	//	return plat_is_active();
	}
}

void CSector::SetActiveAtReplayStart(bool on)
{
	if (on)
	{
		m_flags|=mACTIVE_AT_REPLAY_START;
	}
	else
	{
		m_flags&=~mACTIVE_AT_REPLAY_START;
	}
}

bool CSector::GetActiveAtReplayStart() const
{
	return m_flags&mACTIVE_AT_REPLAY_START;
}

void CSector::SetVisibleAtReplayStart(bool on)
{
	if (on)
	{
		m_flags|=mVISIBLE_AT_REPLAY_START;
	}
	else
	{
		m_flags&=~mVISIBLE_AT_REPLAY_START;
	}
}

bool CSector::GetVisibleAtReplayStart() const
{
	return m_flags&mVISIBLE_AT_REPLAY_START;
}

void CSector::SetStoredActive(bool on)
{
	if (on)
	{
		m_flags|=mSTORED_ACTIVE;
	}
	else
	{
		m_flags&=~mSTORED_ACTIVE;
	}
}

bool CSector::GetStoredActive() const
{
	return m_flags&mSTORED_ACTIVE;
}

void CSector::SetStoredVisible(bool on)
{
	if (on)
	{
		m_flags|=mSTORED_VISIBLE;
	}
	else
	{
		m_flags&=~mSTORED_VISIBLE;
	}
}

bool CSector::GetStoredVisible() const
{
	return m_flags&mSTORED_VISIBLE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::CBBox & CSector::GetBoundingBox() const
{
	if (mp_geom)
	{
		return mp_geom->GetBoundingBox();
	} else {
		Dbg_Assert(mp_coll_sector);

		CCollObjTriData *p_coll_data = mp_coll_sector->GetGeometry();

		Dbg_Assert(p_coll_data);

		return p_coll_data->GetBBox();
	//	return plat_get_bounding_box();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetWorldPosition(const Mth::Vector& pos)
{
	// Do collision first
	if (mp_coll_sector)
	{
		mp_coll_sector->SetWorldPosition(pos);
		if (m_flags & mIN_SUPER_SECTORS)
		{
			m_flags |= mINVALID_SUPER_SECTORS;
		}

	}

	// Then Geometry
	if (mp_geom)
	{
		mp_geom->SetWorldPosition(pos);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &	CSector::GetWorldPosition() const
{
	if (mp_geom)
	{
		return mp_geom->GetWorldPosition();
	} else {
		Dbg_Assert(mp_coll_sector);

		return mp_coll_sector->GetWorldPosition();
	//	return plat_get_world_position();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetYRotation(Mth::ERot90 rot)
{
	Mth::ERot90 delta_rot = (Mth::ERot90) (((rot + Mth::NUM_ROTS) - m_rot_y) % Mth::NUM_ROTS);
	//Dbg_Message("SetYRotation: new rot %d sector rot %d delta rot %d", rot, m_rot_y, delta_rot);

	// Do collision first
	if (mp_coll_sector)
	{
		mp_coll_sector->RotateY(GetWorldPosition(), delta_rot);
	}

	if (mp_geom)
	{
		mp_geom->RotateY(delta_rot);
	//} else {
	//	plat_set_y_rotation(delta_rot);
	}

	// Now change value
	m_rot_y = rot;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::ERot90	CSector::GetYRotation() const
{
	return m_rot_y;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetScale(const Mth::Vector & scale)
{
	// Do collision first
	if (mp_coll_sector)
	{
		mp_coll_sector->Scale(GetWorldPosition(), scale);
		if (m_flags & mIN_SUPER_SECTORS)
		{
			m_flags |= mINVALID_SUPER_SECTORS;
		}

	}

	if (mp_geom)
	{
		mp_geom->SetScale(scale);
	}

	// Now change value
	m_scale = scale;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CSector::GetScale() const
{
	return m_scale;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		CSector::GetNumCollVertices() const
{
	if (mp_coll_sector && mp_coll_sector->GetGeometry())
	{
		return mp_coll_sector->GetGeometry()->GetNumVerts();
	}
	else
	{
		Dbg_MsgAssert(0, ("SetRawCollVertices(): Can't find collision geometry"));
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::GetRawCollVertices(Mth::Vector *p_vert_array) const
{
	if (mp_coll_sector && mp_coll_sector->GetGeometry())
	{
		mp_coll_sector->GetGeometry()->GetRawVertices(p_vert_array);
	}
	else
	{
		Dbg_MsgAssert(0, ("SetRawCollVertices(): Can't find collision geometry"));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetRawCollVertices(const Mth::Vector *p_vert_array)
{
	if (mp_coll_sector && mp_coll_sector->GetGeometry())
	{
		mp_coll_sector->GetGeometry()->SetRawVertices(p_vert_array);

		// Invalidate sector in SuperSectors
		if (m_flags & mIN_SUPER_SECTORS)
		{
			Dbg_MsgAssert(!(m_flags & mREMOVE_FROM_SUPER_SECTORS), ("Can't change collision verts of a sector being removed"));
			m_flags |= mINVALID_SUPER_SECTORS;
		}
	}
	else
	{
		Dbg_MsgAssert(0, ("SetRawCollVertices(): Can't find collision geometry"));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetCollidable(bool on)
{
	if (on) {
		m_flags |= mCOLLIDE;
	} else {
		m_flags &= ~mCOLLIDE;
	}

	if (mp_coll_sector)
	{
		if (on) {
			mp_coll_sector->ClearObjectFlags(mSD_NON_COLLIDABLE);
		} else {
			mp_coll_sector->SetObjectFlags(mSD_NON_COLLIDABLE);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSector::IsCollidable() const
{
	return (m_flags & mCOLLIDE) || IsActive();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetOccluder(bool on)
{
	Dbg_MsgAssert(mp_coll_sector,("Flagging a sector as occluder that has no collision info"));
	if (mp_coll_sector)
	{
		if (!on)
		{
			mp_coll_sector->ClearObjectFlags(mSD_OCCLUDER);
		}
		else 
		{
			if (! (mp_coll_sector->GetObjectFlags() & mSD_OCCLUDER))
			{
				mp_coll_sector->SetObjectFlags(mSD_OCCLUDER);
				
				// we've just set some collision as an occluder for the first time
				// so now would be a good time to tell the engine about these new occlusion polygons
				
				CCollStaticTri * p_coll_static = static_cast(mp_coll_sector);
				Dbg_MsgAssert(p_coll_static,("World sector has non-static collision???"));
				
				p_coll_static->ProcessOcclusion();
			}
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::SetShatter(bool on)
{
	Replay::WriteShatter(m_checksum,on);
	plat_set_shatter(on);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSector::GetShatter() const
{
	return plat_get_shatter();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSector::AddCollSector(Nx::CCollStatic *p_coll_sector)
{
	Dbg_Assert(m_checksum == p_coll_sector->GetChecksum());

	if (!mp_coll_sector)
	{
		mp_coll_sector = p_coll_sector;
		if (mp_geom)
		{
			mp_geom->SetCollTriData(p_coll_sector->GetGeometry());
		}

		return true;
	} else {
		Dbg_MsgAssert(0, ("CSector::AddCollSector(): adding second collision sector to %x\n", m_checksum));
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::CCollStatic *	CSector::GetCollSector() const
{
	return mp_coll_sector;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32		CSector::get_flags() const
{
	return m_flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSector::SetUVWibbleParams(float u_vel, float u_amp, float u_freq, float u_phase,
											   float v_vel, float v_amp, float v_freq, float v_phase)
{
	if (mp_geom)
	{
		mp_geom->SetUVWibbleParams(u_vel, u_amp, u_freq, u_phase, v_vel, v_amp, v_freq, v_phase);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSector::UseExplicitUVWibble(bool yes)
{
	if (mp_geom)
	{
		mp_geom->UseExplicitUVWibble(yes);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSector::SetUVWibbleOffsets(float u_offset, float v_offset)
{
	if (mp_geom)
	{
		mp_geom->SetUVWibbleOffsets(u_offset, v_offset);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSector::set_flags(uint32 flags)
{
	m_flags |= flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSector::clear_flags(uint32 flags)
{
	m_flags &= ~flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSector *	CSector::clone(bool instance, bool add_to_super_sectors, CScene *p_dest_scene)
{
	// Create new instance and clone stuff below p-line
	CSector *p_new_sector = plat_clone(instance, p_dest_scene);

	if (p_new_sector)
	{
		// Get new checksum
		p_new_sector->m_checksum = CEngine::sGetUnusedSectorChecksum();

		// Now clone stuff above p-line
		p_new_sector->m_flags	= m_flags | mCLONE;		// mark as clone
		p_new_sector->m_rot_y 	= m_rot_y;

		if (mp_geom)
		{
			p_new_sector->mp_geom = mp_geom->Clone(instance, p_dest_scene);
			Dbg_Assert(p_new_sector->mp_geom);
		}

		if (mp_coll_sector)
		{
			
			// Note, static_cast seems to be essentially a macro
			// so you must pass it a pointer, and not the result of an expression
			// otherwise it will continually re-evaluate that expression
			// which here would casue memory leaks.																			  
			CCollObj *p_coll_obj = mp_coll_sector->Clone(false /*instance*/);	// Garrett: We're not even going to try to instamce it
			if (p_coll_obj)
			{
				p_new_sector->mp_coll_sector = static_cast(p_coll_obj);

				p_new_sector->mp_coll_sector->SetChecksum(p_new_sector->m_checksum);

				p_new_sector->m_flags &= ~mIN_SUPER_SECTORS;		// Not in there yet

				if (add_to_super_sectors)
				{
					p_new_sector->m_flags |= mADD_TO_SUPER_SECTORS;
				}

				Dbg_Assert(p_new_sector->mp_coll_sector);

				// For now, just copy the CCollObjTriData pointer over
				if (p_new_sector->mp_geom)
				{
					// Garrett: Commented assert out because the pointer is now copied in CGeom
					//Dbg_Assert(p_new_sector->mp_geom->GetCollTriData() == NULL);
					p_new_sector->mp_geom->SetCollTriData(p_coll_obj->GetGeometry());
				}

				Dbg_Assert(p_coll_obj->GetGeometry() != mp_coll_sector->GetGeometry());
			}
			else
			{
				p_new_sector->mp_coll_sector = NULL;
				p_new_sector->m_flags &= ~mIN_SUPER_SECTORS;		// Not in there at all
				if (p_new_sector->mp_geom)
				{
					p_new_sector->mp_geom->SetCollTriData(NULL);
				}
			}
		}
		
		return p_new_sector;
	} else {
		return NULL;
	}
}

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions are provided here:
// so engine implementors can leave certain functionality until later
						
#if 0
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::plat_set_color(Image::RGBA rgba)
{
	printf ("STUB: PlatSetColor\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA CSector::plat_get_color() const
{
	printf ("STUB: PlatGetColor\n");
	return Image::RGBA(0, 0, 0, 0);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::plat_clear_color()
{
	printf ("STUB: PlatClearColor\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::plat_set_visibility(uint32 mask)
{
	printf ("STUB: PlatSetVisibility\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CSector::plat_get_visibility() const
{
	printf ("STUB: PlatGetVisibility\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::plat_set_active(bool on)
{
	printf ("STUB: PlatSetActive\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSector::plat_is_active() const
{
	printf ("STUB: PlatIsActive\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

 void	CSector::plat_set_world_position(const Mth::Vector& pos)
 {
	 printf ("STUB: PlatSetWorldPosition\n");
 }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::CBBox &	CSector::plat_get_bounding_box() const
{
	static Mth::CBBox stub_bbox;
	printf ("STUB: PlatGetBoundingBox\n");
	return stub_bbox;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const Mth::Vector &	CSector::plat_get_world_position() const
{
	static Mth::Vector stub_vec;
	printf ("STUB: PlatGetWorldPosition\n");
	return stub_vec;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::plat_set_y_rotation(Mth::ERot90 rot)
{
	printf ("STUB: PlatSetYRotation\n");
}
#endif // 0

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSector::plat_set_shatter(bool on)
{
	printf ("STUB: PlatSetShatter\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSector::plat_get_shatter() const
{
	printf ("STUB: PlatGetShatter\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSector *	CSector::plat_clone(bool instance, CScene *p_dest_scene)
{
	printf ("STUB: PlatClone\n");
	return NULL;
}

}




================================================
FILE: Code/Gfx/NxSector.h
================================================
///////////////////////////////////////////////////////////////////////////////
// NxSector.h



#ifndef	__GFX_NXSECTOR_H__
#define	__GFX_NXSECTOR_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 	

namespace Mth {
	class Vector;
	class CBBox;
}

namespace Nx
{

////////////////////////////////////////////////////////////////
// The Sector Story:
//
// To the designer, A "Sector" (previously a "world sector" is an
// object in 3D Studio Max.  Typically this might be a tree, a wall
// a section of a building, a parked car or any other static part of the world
//
// The designer wants to be able to:
// -  load the sector as part of the scene, see it and collide with it
// -  reference the sector by name
// -  Turn it on and off (both collision and display)
// -  make it visible and invisible in particular viewports for LOD reasons
// -  change its color
// -  change various attributes, like UV wibble speed.  
//
// To the engine programmer, the sector will be split into various bits of data
// for optimization and platform specific reasons.  The geometry might be in different chunks
// and the collision data might be seperate from the rendering data.  
// The act of "turning off" a sector will be very different from platform to platform
//
// To get round this problem, we have a CSector class which acts as a facade
// to the actual machine specific sector data.
//
// When the engine loads a scene, it will create a list of CSectors, one for each
// sector in the scene.  
// Each CSector will actually be a platfrom specific class derived from CSector,
// like: CPs2Sector, CXboxSector or CNGC Sector
// The derived class (like, CPs2Sector), contains the platform specific data that
// identifies the sector in the engine.  On the PS2, this might have a list of the 
// meshes that make up the sector, maybe a list of the materials, probably a pointer to the 
// collision data, and maybe a pointer to a structure containing sector flags.
// CSector provides functions like SetVisibility(), which call a platform specific
// function (PlatSetVisibility()), which calls the actual engine specific
// function, which performs whatever is necessary at a low level to make the 
// sector visible/invisible (on the PS2, this might involve relinking a DMA chain,
// on other platforms, it might just involve flipping a bit.)
//
// The "engine programmers" have to implement and maintain the platform
// specific CPs2Sector (and CXBox.., etc) implementation.
//
// The "game programmers" simply use the CSector class, and will never need to know 
// anything about the derived class, and their code will work across all three platforms.  
//
// The End
/////////////////////////////////////////////////////////////////////////////////

// Forward declaration
class CGeom;
class CScene;
class CCollStatic;
#ifdef	__PLAT_NGPS__		
class CPs2Scene;
#endif //	__PLAT_NGPS__		

// The CSector class is the platform independent abstract base class
// of the platform specific CSector classes
class	CSector
{
public:
// The basic interface to the sector
// this is the machine independent part	
// machine independ range checking, etc can go here	
							CSector();
	virtual					~CSector();

	uint32					GetChecksum() const;
	void					SetChecksum(uint32 c);

	CGeom *					GetGeom() const;
	void					SetGeom(CGeom *p_geom);

	void					SetColor(Image::RGBA rgba);
	Image::RGBA				GetColor() const;
	void					ClearColor();
	
	void 					SetLightGroup(uint32 light_group) {m_light_group = light_group;}
	uint32 					GetLightGroup() {return m_light_group;}

	void					SetVisibility(uint32 mask);
	uint32					GetVisibility() const;

	void					SetActive(bool on);
	bool					IsActive() const;

	void					SetActiveAtReplayStart(bool on);
	bool					GetActiveAtReplayStart() const;
	void					SetVisibleAtReplayStart(bool on);
	bool					GetVisibleAtReplayStart() const;
	void					SetStoredActive(bool on);
	bool					GetStoredActive() const;
	void					SetStoredVisible(bool on);
	bool					GetStoredVisible() const;
	
	const Mth::CBBox &		GetBoundingBox() const;

	void					SetWorldPosition(const Mth::Vector& pos);
	const Mth::Vector &		GetWorldPosition() const;

	// Rotation is applied to meshes permanently
	void					SetYRotation(Mth::ERot90 rot);
	Mth::ERot90				GetYRotation() const;			// Gets applied rotation

	// Scale is applied to meshes permanently
	void					SetScale(const Mth::Vector& scale);
	Mth::Vector				GetScale() const;				// Gets scale

	// Change collision data of a sector
	int						GetNumCollVertices() const;
	void					GetRawCollVertices(Mth::Vector *p_vert_array) const;
	void					SetRawCollVertices(const Mth::Vector *p_vert_array);

	void					SetCollidable(bool on);
	bool					IsCollidable() const;

	void					SetShatter(bool on);
	bool					GetShatter() const;

	bool					AddCollSector(Nx::CCollStatic *p_coll_sector);
	CCollStatic *			GetCollSector() const;

	void					SetOccluder(bool on);
	
	// Wibble functions
	void					SetUVWibbleParams(float u_vel, float u_amp, float u_freq, float u_phase,
											  float v_vel, float v_amp, float v_freq, float v_phase);
	void					UseExplicitUVWibble(bool yes);
	void					SetUVWibbleOffsets(float u_offset, float v_offset);
	
protected:
	// Internal sector flags.
	enum {
		mCOLLIDE					= 0x0001,
		mCLONE						= 0x0002,
		mADD_TO_SUPER_SECTORS		= 0x0004,					// Cloned sector needs adding to Super Sectors
		mMARKED_FOR_DELETION		= 0x0008,					// Cloned object no longer used but not deleted yet
		mREMOVE_FROM_SUPER_SECTORS	= 0x0010,					// Take out of Super Sectors w/o deleting
		mINVALID_SUPER_SECTORS		= 0x0020,					// Tells if SuperSectors needs updating
		mIN_SUPER_SECTORS			= 0x0040,					// Tells if in SuperSectors
		// These 4 used by replay code
		mACTIVE_AT_REPLAY_START		= 0x0080,					// Whether the sector is active at the start of the replay
		mVISIBLE_AT_REPLAY_START	= 0x0100,					// Whether the sector is visible at the start of the replay
		mSTORED_ACTIVE				= 0x0200,					// Stored active status, so that it can be restored at the end of the replay
		mSTORED_VISIBLE				= 0x0400,					// Stored visible status, so that it can be restored at the end of the replay
	};

	uint32					get_flags() const;				// Get internal flags
	void					set_flags(uint32 flags);		// Set internal flags
	void					clear_flags(uint32 flags);		// Clear internal flags

	uint32					m_checksum;
	uint32					m_flags;
	uint32					m_light_group;		// checksum of light group as specified in node array

	Mth::ERot90				m_rot_y;		// since rotation already applied below p-line, we keep the value above
	Mth::Vector				m_scale;		// since scale already applied below p-line, we keep the value above

	CGeom *					mp_geom;		// The geometry of the sector
	CCollStatic *			mp_coll_sector;	// Collision object for the sector

private:
	CSector *				clone(bool instance, bool add_to_super_sectors, CScene *p_dest_scene = NULL);	// must only be called through CScene

	// The virtual functions will have a stub implementation
	// in nxsector.cpp
	//virtual	void			plat_set_color(Image::RGBA rgba);
	//virtual	Image::RGBA		plat_get_color() const;
	//virtual	void			plat_clear_color();

	//virtual	void			plat_set_visibility(uint32 mask);
	//virtual	uint32			plat_get_visibility() const;

	//virtual	void			plat_set_active(bool on);
	//virtual	bool			plat_is_active() const;

	//virtual const Mth::CBBox &	plat_get_bounding_box() const;

	//virtual void			plat_set_world_position(const Mth::Vector& pos);
	//virtual const Mth::Vector &	plat_get_world_position() const;

	//virtual void			plat_set_y_rotation(Mth::ERot90 rot);

	virtual	void			plat_set_shatter(bool on);
	virtual	bool			plat_get_shatter() const;

	virtual CSector *		plat_clone(bool instance, CScene *p_dest_scene);

	// CScene needs access to clone() and internal flags
	friend CScene;
#ifdef	__PLAT_NGPS__		
	friend CPs2Scene;
#endif //	__PLAT_NGPS__		
};

}

#endif // 



================================================
FILE: Code/Gfx/NxSkinComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxSkinComponent.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/21/2001
//****************************************************************************

#include "gfx/nxskincomponent.h"

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions are provided here:
// so engine implementors can leave certain functionality until later
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkinComponent::plat_set_color(uint8 r, uint8 g, uint8 b, uint8 a)
{
    // STUB
    printf("STUB FUNCTION called:  CSkinComponent::set_color %d %d %d %d", r, g, b, a);
}
         
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkinComponent::plat_set_visibility(uint32 mask)
{
    // STUB
    printf("STUB FUNCTION called:  CSkinComponent::set_visibility %08x", mask);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkinComponent::plat_set_scale(float scaleFactor)
{
    // STUB
    printf("STUB FUNCTION called:  CSkinComponent::set_scale %f", scaleFactor);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkinComponent::plat_replace_texture(char* p_srcFileName, char* p_dstFileName)
{
    Dbg_Assert(p_srcFileName);
    Dbg_Assert(p_dstFileName);
    
    // STUB
    printf("STUB FUNCTION called:  CSkinComponent::replace_texture %s %s", p_srcFileName, p_dstFileName);
}

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

// These functions are the platform independent part of the interface to 
// the platform specific code
// parameter checking can go here....
// although we might just want to have these functions inline, or not have them at all?

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkinComponent::CSkinComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkinComponent::~CSkinComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkinComponent::SetColor(uint8 r, uint8 g, uint8 b, uint8 a)
{
    plat_set_color(r,g,b,a);
}
         
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkinComponent::SetVisibility(uint32 mask)
{
    plat_set_visibility(mask);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkinComponent::SetScale(float scaleFactor)
{
    plat_set_scale(scaleFactor);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkinComponent::ReplaceTexture(char* p_srcFileName, char* p_dstFileName)
{
    plat_replace_texture(p_srcFileName, p_dstFileName);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx





================================================
FILE: Code/Gfx/NxSkinComponent.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       NxSkinComponent.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/21/2001
//****************************************************************************

#ifndef	__GFX_NXSKINCOMPONENT_H__
#define	__GFX_NXSKINCOMPONENT_H__

#include 

namespace Nx
{

class CSkinComponent
{

public:
    // The basic interface to the skin component
    // this is the machine independent part	
    // machine independent range checking, etc can go here
    CSkinComponent();
	virtual					~CSkinComponent();

public:
	void					SetColor(uint8 r, uint8 g, uint8 b, uint8 a);
	void					SetVisibility(uint32 mask);
	void					SetScale(float scaleFactor);
    void                    ReplaceTexture(char* p_srcFileName, char* p_dstFileName);

protected:
    uint32                  m_componentName;

private:
    // The virtual functions will have a stub implementation
    // in p_nxskincomponent.cpp
	virtual	void			plat_set_color(uint8 r, uint8 g, uint8 b, uint8 a);
	virtual	void			plat_set_visibility(uint32 mask);
    virtual void            plat_set_scale(float scaleFactor);
    virtual void            plat_replace_texture(char* p_srcFileName, char* p_dstFileName);
};

}

#endif // __GFX_NXSKINCOMPONENT_H__



================================================
FILE: Code/Gfx/NxSprite.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxSprite.cpp

#include 

#include "gfx/NxSprite.h"

namespace	Nx
{

/////////////////////////////////////////////////////////////////////////////
// CSprite

/////////////////////////////////////////////////////////////////////////////
// These functions are the platform independent part of the interface.
					 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSprite::CSprite(CWindow2D *p_window) :
	mp_texture(NULL),
	m_pos_x(-1.0f),
	m_pos_y(-1.0f),
	m_scale_x(1.0f),
	m_scale_y(1.0f),
	m_anchor_x(-1.0f),
	m_anchor_y(-1.0f),
	m_rotation(0.0f),
	m_priority(0.0f),
	m_width(0),
	m_height(0),
	m_rgba(0x80, 0x80, 0x80, 0x80),
	mp_window(p_window)
{
	m_hidden = true;	// until we get more information
	m_use_zbuffer = false;
}

CSprite::CSprite(CTexture *p_tex, float xpos, float ypos,
				uint16 width, uint16 height, CWindow2D *p_window, Image::RGBA rgba,
				float xscale, float yscale, float xanchor,
				float yanchor, float rot, float pri, bool hide)
{
	Initialize(p_tex, xpos, ypos, width, height, p_window, rgba, xscale, yscale,
			   xanchor, yanchor, rot, pri, hide);

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSprite::~CSprite()
{
	// TODO: Possibly free texture
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::Initialize(CTexture *p_tex, float xpos, float ypos,
								uint16 width, uint16 height, CWindow2D *p_window,
								Image::RGBA rgba, float xscale, float yscale, float xanchor,
								float yanchor, float rot, float pri, bool hide)
{
	mp_texture = p_tex;
	m_pos_x = xpos;
	m_pos_y = ypos;
	m_width = width;
	m_height = height;
	m_rgba = rgba;
	m_scale_x = xscale;
	m_scale_y = yscale;
	m_anchor_x = xanchor;
	m_anchor_y = yanchor;
	m_rotation = rot;
	m_priority = pri;

	m_hidden = hide;
	m_use_zbuffer = false;
	mp_window = p_window;

	plat_initialize();			// This calls nothing when called from the constructor
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::SetHidden(bool hide)
{
	m_hidden = hide;

	plat_update_hidden();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::SetTexture(CTexture *p_texture)
{
	mp_texture = p_texture;
	if (p_texture)
	{
		m_width = p_texture->GetWidth();
		m_height = p_texture->GetHeight();
	}

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::SetPos(float x, float y)
{
	m_pos_x = x;
	m_pos_y = y;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::SetSize(uint16 width, uint16 height)
{
	m_width = width;
	m_height = height;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::SetScale(float x, float y)
{
	m_scale_x = x;
	m_scale_y = y;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::SetAnchor(float x, float y)
{
	m_anchor_x = x;
	m_anchor_y = y;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::SetRotation(float rot)
{
	m_rotation = rot;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::SetPriority(float pri)
{
	m_priority = pri;
	m_use_zbuffer = false;

	plat_update_priority();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		 CSprite::SetZValue(ZBufferValue z)
{
	m_zvalue = z;
	m_use_zbuffer = true;

	plat_update_priority();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::SetRGBA(Image::RGBA rgba)
{
	m_rgba = rgba;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::SetWindow(CWindow2D *p_window)
{
	mp_window = p_window;

	plat_update_window();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSprite::EnableConstantZValue(bool enable)
{
	plat_enable_constant_z_value(enable);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSprite::SetConstantZValue(Nx::ZBufferValue z)
{
	plat_set_constant_z_value(z);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::ZBufferValue	CSprite::GetConstantZValue()
{
	return plat_get_constant_z_value();
}

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions are provided here:
// so engine implementors can leave certain functionality until later
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::plat_initialize()
{
	printf ("STUB: PlatInitialize\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::plat_update_hidden()
{
	printf ("STUB: PlatUpdateHidden\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::plat_update_engine()
{
	printf ("STUB: PlatUpdateEngine\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::plat_update_priority()
{
	printf ("STUB: PlatUpdatePriority\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CSprite::plat_update_window()
{
	printf ("STUB: PlatUpdateWindow\n");
}

}




================================================
FILE: Code/Gfx/NxSprite.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// NxSprite.H - Neversoft Engine, Rendering portion, Platform independent interface

#ifndef	__GFX_NX_SPRITE_H__
#define	__GFX_NX_SPRITE_H__


#ifndef __CORE_DEFINES_H
#include 
#endif
#include 

#include 
#include 
#include 


namespace Nx
{

// Forward declarations
class CWindow2D;

//////////////////////////////////////////////////////////////////////////////////
// Nx::CSprite: Class for 2D sprites
class	CSprite
{
public:
							CSprite(CWindow2D *p_window = NULL);
							CSprite(CTexture *p_tex, float xpos, float ypos,
									uint16 width, uint16 height, CWindow2D *p_window = NULL,
									Image::RGBA rgba = Image::RGBA(0x80, 0x80, 0x80, 0x80),
									float xscale = 1.0f, float yscale = 1.0f,
									float xanchor = -1.0f, float yanchor = -1.0f,
									float rot = 0.0f, float pri = 0.0f, bool hide = false);
	virtual					~CSprite();

	// Initialization
	void					Initialize(CTexture *p_tex, float xpos, float ypos,
									   uint16 width, uint16 height, CWindow2D *p_window = NULL,
									   Image::RGBA rgba = Image::RGBA(0x80, 0x80, 0x80, 0x80),
									   float xscale = 1.0f, float yscale = 1.0f,
									   float xanchor = -1.0f, float yanchor = -1.0f,
									   float rot = 0.0f, float pri = 0.0f, bool hide = false);

	// If hidden, it won't be drawn that frame
	void					SetHidden(bool hide);
	bool					IsHidden() const;

	// Texture used for the sprite.  But it is optional.
	CTexture *				GetTexture() const;
	void					SetTexture(CTexture *p_texture);

	// Position on screen.  Assumes screen size of 640x448.
	float					GetXPos() const;
	float					GetYPos() const;
	void					SetPos(float x, float y);

	// Actual width and height of sprite.  If textured, it will also
	// use this for the texture width and height.
	uint16					GetWidth() const;
	uint16					GetHeight() const;
	void					SetSize(uint16 w, uint16 h);

	// Scale of sprite.  A negative scale will flip the axis.
	float					GetXScale() const;
	float					GetYScale() const;
	void					SetScale(float x, float y);

	// The anchor position of the sprite.  Basically, the origin within
	// the sprite.  This point is going to be at the screen position.  It
	// is also the point of rotation.  The range is [-1.0, 1.0], where
	// (-1.0, -1.0) is the top left corner and (1.0, 1.0) is the bottom
	// right corner.
	float					GetXAnchor() const;
	float					GetYAnchor() const;
	void					SetAnchor(float x, float y);

	// Rotation of sprite in radians
	float					GetRotation() const;
	void					SetRotation(float rot);

	// Priority of sprite from 0.0 to 1.0.  Higher priority sprites are
	// drawn on top of lower priority sprites.
	float					GetPriority() const;
	void					SetPriority(float pri);
	Nx::ZBufferValue		GetZValue() const;
	void					SetZValue(Nx::ZBufferValue z);

	// Color of sprite.  All 4 corners are the same.
	Image::RGBA				GetRGBA() const;
	void					SetRGBA(Image::RGBA rgba);

	// Clipping window
	CWindow2D *				GetWindow() const;
	void					SetWindow(CWindow2D *p_window);

	// Constant z-buffer value (only affect sprites using a priority)
	static void				EnableConstantZValue(bool enable);
	static void				SetConstantZValue(Nx::ZBufferValue z);
	static Nx::ZBufferValue	GetConstantZValue();

protected:
	CTexture *				mp_texture;				// Texture

	float					m_pos_x;				// Position
	float					m_pos_y;
	float					m_scale_x;				// Scale
	float					m_scale_y;
	float					m_anchor_x;				// Anchor Position
	float					m_anchor_y;

	float					m_rotation;				// Rotation
	float					m_priority;				// Priority
	ZBufferValue			m_zvalue;				// for zbuffer sort

	uint16					m_width;				// Size
	uint16					m_height;

	bool					m_hidden;				// Hidden
	bool					m_use_zbuffer;
	Image::RGBA				m_rgba;					// Color

	CWindow2D *				mp_window;

private:
	virtual void			plat_initialize();

	virtual void			plat_update_hidden();		// Tell engine of update
	virtual void			plat_update_engine();		// Update engine primitives
	virtual void			plat_update_priority();
	virtual void			plat_update_window();

	static void				plat_enable_constant_z_value(bool enable);
	static void				plat_set_constant_z_value(Nx::ZBufferValue z);
	static Nx::ZBufferValue	plat_get_constant_z_value();
};

/////////////////////////////////////////////////////////
// CSprite inline function
inline bool					CSprite::IsHidden() const
{
	return m_hidden;
}

inline CTexture *			CSprite::GetTexture() const
{
	return mp_texture;
}

inline float				CSprite::GetXPos() const
{
	return m_pos_x;
}

inline float				CSprite::GetYPos() const
{
	return m_pos_y;
}

inline uint16				CSprite::GetWidth() const
{
	return m_width;
}

inline uint16				CSprite::GetHeight() const
{
	return m_height;
}

inline float				CSprite::GetXScale() const
{
	return m_scale_x;
}

inline float				CSprite::GetYScale() const
{
	return m_scale_y;
}

inline float				CSprite::GetXAnchor() const
{
	return m_anchor_x;
}

inline float				CSprite::GetYAnchor() const
{
	return m_anchor_y;
}

inline float				CSprite::GetRotation() const
{
	return m_rotation;
}

inline float				CSprite::GetPriority() const
{
	return m_priority;
}

inline ZBufferValue			CSprite::GetZValue() const
{
	return m_zvalue;
}

inline Image::RGBA			CSprite::GetRGBA() const
{
	return m_rgba;
}

inline CWindow2D *			CSprite::GetWindow() const
{
	return mp_window;
}

}


#endif



================================================
FILE: Code/Gfx/NxTexMan.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxTexMan.cpp

// start autoduck documentation
// @DOC nxtexman
// @module nxtexman | None
// @subindex Scripting Database
// @index script | nxtexman

#include 
#include 
#include 
#include 
#include 

#include "gfx/NxTexMan.h"
#include "gfx/NxSprite.h"
#include "gfx/Nx.h"

#include 

// for downloading faces
#include 
#include 
#include 
#include 

namespace	Nx
{

/////////////////////////////////////////////////////////////////////////////
// CTexMan

Lst::HashTable	CTexDictManager::s_tex_dict_lookup(4);

// temporary sprite texture dictionary
CTexDict *					CTexDictManager::sp_sprite_tex_dict = sCreateTextureDictionary("sprite");
CTexDict *					CTexDictManager::sp_particle_tex_dict = sCreateTextureDictionary("particle");

/////////////////////////////////////////////////////////////////////////////
// These functions are the platform independent part of the interface.
					 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict *			CTexDictManager::sCreateTextureDictionary(const char *p_tex_dict_name)
{
	CTexDict *p_dict;
	uint32 checksum = Crc::GenerateCRCFromString(p_tex_dict_name);

	p_dict = sGetTextureDictionary(checksum);

	// Assert for now unless we can think of a reason to use with ref counts
	Dbg_MsgAssert((p_dict == NULL), ("Texture dictionary %s already exists", p_tex_dict_name));

	if (p_dict)	
	{
		// If already loaded, then just link up with it
		p_dict->IncRefCount();
	} else {
		p_dict = s_plat_create_texture_dictionary(checksum);
		s_tex_dict_lookup.PutItem(checksum, p_dict);
	}
	return p_dict;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict *			CTexDictManager::sLoadTextureDictionary(uint32 fileNameChecksum, uint32* pData, int dataSize, bool isLevelData, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup )
{
	Mem::PushMemProfile((char*)"TEX_data_buffer");

	uint32 checksum = fileNameChecksum + texDictOffset;

	CTexDict *p_dict;
	p_dict = sGetTextureDictionary(checksum);

	if (p_dict)	
	{
		// Mick: temporary assertion to track down annoyingly persisten phantom texture dcitionaries
		// you can probably remove this
		//Dbg_MsgAssert(0,("Not supposed to be multiply using tex dicts yet! (%s/%x)",texture_dict_name,texDictOffset));

		// If already loaded, then just link up with it
		p_dict->IncRefCount();
	}
	else
	{
		p_dict = s_plat_load_texture_dictionary(checksum, pData, dataSize, isLevelData, texDictOffset, isSkin, forceTexDictLookup);
		p_dict->set_checksum(checksum);			// Since it just uses the base name
		s_tex_dict_lookup.PutItem(checksum, p_dict);
	}

	Mem::PopMemProfile(/*(char*)"TEX_data_buffer"*/);

	return p_dict;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict *			CTexDictManager::sLoadTextureDictionary(const char *p_tex_dict_name, bool isLevelData, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup )
{
	// append the extension
	char	texture_dict_name[128];
	sprintf(texture_dict_name,"%s.%s",p_tex_dict_name,CEngine::sGetPlatformExtension());

	Mem::PushMemProfile((char*)p_tex_dict_name);
	
	// the texDictOffset prevents clashes when we need multiple
	// separate versions of the same texture dictionary, such as
	// for multiplayer create-a-skater parts (note:  peds should not
	// use this system because they should share actual texture
	// dictionaries)
	uint32 checksum = Crc::GenerateCRCFromString(p_tex_dict_name) + texDictOffset;

	CTexDict *p_dict;
	p_dict = sGetTextureDictionary(checksum);

	if (p_dict)	
	{
		// Mick: temporary assertion to track down annoyingly persisten phantom texture dcitionaries
		// you can probably remove this
		//Dbg_MsgAssert(0,("Not supposed to be multiply using tex dicts yet! (%s/%x)",texture_dict_name,texDictOffset));

		// If already loaded, then just link up with it
		p_dict->IncRefCount();
	}
	else
	{
		p_dict = s_plat_load_texture_dictionary(texture_dict_name, isLevelData, texDictOffset, isSkin, forceTexDictLookup);
		p_dict->set_checksum(checksum);			// Since it just uses the base name
		s_tex_dict_lookup.PutItem(checksum, p_dict);
	}

	Mem::PopMemProfile(/*(char*)p_tex_dict_name*/);

	return p_dict;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CTexDictManager::sUnloadTextureDictionary(CTexDict *p_tex_dict)
{
	if (p_tex_dict->DecRefCount() == 0)
	{
		s_tex_dict_lookup.FlushItem(p_tex_dict->GetChecksum());  // Remove the reference to it before we delete it
		s_plat_unload_texture_dictionary(p_tex_dict);			 // as deleting it can kill the checksum with memory trashing
		return TRUE;
	}

	return FALSE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict *		  	CTexDictManager::sGetTextureDictionary(uint32 checksum)
{
	return s_tex_dict_lookup.GetItem(checksum);
}


// @script | LoadTexture | Loads a 2D sprite texture
// @parm string |  | path and name of texture
// @flag no_vram_alloc | won't allocate in vram
bool ScriptLoadTexture(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	const char *p_name;
	if (!pParams->GetText(NONAME, &p_name))
		Dbg_MsgAssert(0, ("no texture specified"));

	bool alloc_vram = true;
	if (pParams->ContainsFlag(CRCD(0x3955ff2e,"no_vram_alloc")))
	{
		alloc_vram = false;
	}
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->LoadTexture(p_name, true, alloc_vram);

	return p_texture != NULL;
}



// @script | UnloadTexture | Unloads a 2D sprite texture
// @parm string |  | name of texture
bool ScriptUnloadTexture(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	CTexture *p_texture = NULL;
	const char *p_name;
	uint32 checksum;

	if (pParams->GetText(NONAME, &p_name))
	{
		// get texture based on string
		p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(p_name);
	}
	else if (pParams->GetChecksum(NONAME, &checksum))
	{							 
		// get texture based on checksum
		p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	}
	else
	{
		Dbg_MsgAssert(0, ("no texture specified in %s", pScript->GetScriptInfo()));
	}

	if (p_texture)
	{
		CTexDictManager::sp_sprite_tex_dict->UnloadTexture(p_texture);
	}
	else
	{
		if ( !pParams->ContainsFlag( CRCD(0x3d92465e,"dont_assert") ) )
		{
			Dbg_MsgAssert(0, ("Can't find texture %s to unload", p_name));
		}
	}
	
	return true;
}

// @script | AddTextureToVram | Puts 2D sprite texture into VRAM (so it is drawable)
// @parm string |  | name of texture
bool ScriptAddTextureToVram(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
#if 0
	const char *p_name;
	if (!pParams->GetText(NONAME, &p_name))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(p_name);
	if (p_texture)
	{
		p_texture->AddToVram();
	} else {
		Dbg_MsgAssert(0, ("Can't find texture %s to add to vram", p_name));
	}
#endif

	return true;
}

// @script | RemoveTextureFromVram | Removes 2D sprite texture from VRAM
// @parm string |  | name of texture
// @flag no_assert | won't assert on failure
bool ScriptRemoveTextureFromVram(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
#if 0
	const char *p_name;
	if (!pParams->GetText(NONAME, &p_name))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(p_name);
	if (p_texture)
	{
		p_texture->RemoveFromVram();
	} else if (!pParams->ContainsFlag(CRCD(0x512c7426,"no_assert"))) {
		Dbg_MsgAssert(0, ("Can't find texture %s to remove from vram", p_name));
	}
#endif

	return true;
}

// @script | LoadFaceTextureFromProfile | Loads a 2D sprite texture
bool ScriptLoadFaceTextureFromProfile(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    bool alloc_vram = true;
	uint32 checksum;
    pParams->GetChecksum( "checksum", &checksum, Script::ASSERT );
	
    // Copy the downloaded face for use as a sprite
    Mdl::Skate* skate_mod = Mdl::Skate::Instance();
    Obj::CSkaterProfile* pSkaterProfile = skate_mod->GetCurrentProfile();
    Dbg_Assert( pSkaterProfile );
    Dbg_MsgAssert( !pSkaterProfile->IsPro(), ( "Can only map face onto a custom skater.  UI must make the custom skater active before this point" ) );
    Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
    Dbg_Assert( pAppearance );
    Gfx::CFaceTexture* pFaceTexture = pAppearance->GetFaceTexture();

	Dbg_MsgAssert(pFaceTexture,("NULL pFaceTexture"));
	Dbg_MsgAssert(pFaceTexture->IsValid(),("Invalid pFaceTexture"));

    Nx::CTexture* p_texture = Nx::CTexDictManager::sp_sprite_tex_dict->LoadTextureFromBuffer(pFaceTexture->GetTextureData(), pFaceTexture->GetTextureSize(), checksum, true, alloc_vram);
    Dbg_MsgAssert( p_texture, ( "Appearance has no face texture" ) );


	return p_texture != NULL;
}

// @script | Generate32BitImage | Generates 32bit image data for texture
// @parm name |  | name of texture
// @parmopt int | renderable | 0 | If set to 1, make 32-bit image renderable in a sprite
// @parmopt int | store_original | 0 | If set to 1, keeps original 32-bit image around that can be used with some functions
bool ScriptGenerate32BitImage(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(NONAME, &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		int value;
		bool renderable = false;
		bool store_original = false;

		if (pParams->GetInteger(CRCD(0xa5d7cfaa,"renderable"), &value) && (value == 1))
		{
			renderable = true;
		}
		if (pParams->GetInteger(CRCD(0xeefaf080,"store_original"), &value) && (value == 1))
		{
			store_original = true;
		}

		p_texture->Generate32BitImage(renderable, store_original);
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't find texture %s to generate 32-bit image", Script::FindChecksumName(checksum)));
	}

	return true;
}

// @script | OffsetTexture | Move texture (cropping the part that moves off and filling in the new parts with
// either the old edge or a supplied fill color)
// @parm name |  | name of texture
// @parm int | x_offset | x offset in pixels
// @parm int | y_offset | y offset in pixels
// @parmopt int | fill_r | 128 | fill color red component
// @parmopt int | fill_g | 128 | fill color green component
// @parmopt int | fill_b | 128 | fill color blue component
// @parmopt int | fill_a | 128 | fill color alpha component
bool ScriptOffsetTexture(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(NONAME, &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		int x_offset = 0;
		int y_offset = 0;
		bool use_fill_color = false;
		int r = 128, g = 128, b = 128, a = 128;

		pParams->GetInteger(CRCD(0xd83d589e,"x_offset"), &x_offset);
		pParams->GetInteger(CRCD(0x14975800,"y_offset"), &y_offset);

		use_fill_color |= pParams->GetInteger(CRCD(0xb7a78c53,"fill_r"), &r);
		use_fill_color |= pParams->GetInteger(CRCD(0xda7a68b8,"fill_g"), &g);
		use_fill_color |= pParams->GetInteger(CRCD(0xaa109c37,"fill_b"), &b);
		use_fill_color |= pParams->GetInteger(CRCD(0x3319cd8d,"fill_a"), &a);

		Image::RGBA fill_color(r, g, b, a);

		p_texture->Offset(x_offset, y_offset, use_fill_color, fill_color);
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't find texture %s to offset", Script::FindChecksumName(checksum)));
	}

	return true;
}

// @script | AdjustTextureRegion | Stretches and shrinks a texture region from the start point to the end point
// @parm name |  | name of texture
// @parm int | xpos | X position of regiom
// @parm int | ypos | Y position of regiom
// @parm int | width | width of regiom
// @parm int | height | height of regiom
// @parm int | split_axis | axis where the split line crosses (0 = X, 1 = Y)
// @parm int | start_point | split line start point on axis
// @parm int | end_point | split line end point on axis
bool ScriptAdjustTextureRegion(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(NONAME, &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		int x_pos = 0;
		int y_pos = 0;
		int width = p_texture->GetWidth();
		int height = p_texture->GetHeight();
		int split_axis = X;
		int start_point = 0;
		int end_point = 0;

		pParams->GetInteger(CRCD(0xfa8972e,"xpos"), &x_pos);
		pParams->GetInteger(CRCD(0xb714f04b,"ypos"), &y_pos);
		pParams->GetInteger(CRCD(0x73e5bad0,"width"), &width);
		pParams->GetInteger(CRCD(0xab21af0,"height"), &height);
		pParams->GetInteger(CRCD(0xe679982e,"split_axis"), &split_axis);
		pParams->GetInteger(CRCD(0x3d0f162a,"start_point"), &start_point);
		pParams->GetInteger(CRCD(0x39c54bde,"end_point"), &end_point);

		p_texture->AdjustRegion(x_pos, y_pos, width, height, split_axis, start_point, end_point);
	} else {
		Dbg_MsgAssert(0, ("Can't find texture %s to adjust", Script::FindChecksumName(checksum)));
	}

	return true;
}

// @script | PullTextureToEdge | Pull texture from point along the axis by num_pixels (+ or - determines the direction)
// and crop anything going outside the texture border.
// @parm name |  | name of texture
// @parm int | point | pull point on axis
// @parm int | axis | axis to pull along (0 = X, 1 = Y)
// @parm int | num_pixels | number of pixels (+ or - determines the direction)
bool ScriptPullTextureToEdge(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(NONAME, &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		int point = 0;
		int axis = X;
		int num_pixels = 0;

		pParams->GetInteger(CRCD(0x485a0cdb,"point"), &point);
		pParams->GetInteger(CRCD(0x7af07905,"axis"), &axis);
		pParams->GetInteger(CRCD(0x88cf948c,"num_pixels"), &num_pixels);

		p_texture->PullToEdge(point, axis, num_pixels);
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't find texture %s to pull", Script::FindChecksumName(checksum)));
	}

	return true;
}
// @script | PushTextureToPoint | Push texture to point along the axis by num_pixels (+ or - determines the direction)
// and either stretch the edge line or fill it in with a fill color.
// @parm name |  | name of texture
// @parm int | point | pull point on axis
// @parm int | axis | axis to pull along (0 = X, 1 = Y)
// @parm int | num_pixels | number of pixels (+ or - determines the direction)
// @parmopt int | fill_r | 128 | fill color red component
// @parmopt int | fill_g | 128 | fill color green component
// @parmopt int | fill_b | 128 | fill color blue component
// @parmopt int | fill_a | 128 | fill color alpha component
bool ScriptPushTextureToPoint(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(NONAME, &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		int point = 0;
		int axis = X;
		int num_pixels = 0;
		bool use_fill_color = false;
		int r = 128, g = 128, b = 128, a = 128;

		pParams->GetInteger(CRCD(0x485a0cdb,"point"), &point);
		pParams->GetInteger(CRCD(0x7af07905,"axis"), &axis);
		pParams->GetInteger(CRCD(0x88cf948c,"num_pixels"), &num_pixels);

		use_fill_color |= pParams->GetInteger(CRCD(0xb7a78c53,"fill_r"), &r);
		use_fill_color |= pParams->GetInteger(CRCD(0xda7a68b8,"fill_g"), &g);
		use_fill_color |= pParams->GetInteger(CRCD(0xaa109c37,"fill_b"), &b);
		use_fill_color |= pParams->GetInteger(CRCD(0x3319cd8d,"fill_a"), &a);

		Image::RGBA fill_color(r, g, b, a);

		p_texture->PushToPoint(point, axis, num_pixels, use_fill_color, fill_color);
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't find texture %s to push", Script::FindChecksumName(checksum)));
	}

	return true;
}

// @script | AdjustTextureBrightness | Scales the texture brightness by the supplied value
// @parm name |  | name of texture
// @parm float | brightness | Brightness value (1.0 = no change)
// @parmopt int | adjust_current | 0 | If set to 1, adjusts the current image, even if the original image exists
bool ScriptAdjustTextureBrightness(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(NONAME, &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		float brightness = 1.0f;
		int adjust_current = 0;
		bool force_adjust_current = false;

		pParams->GetFloat(CRCD(0x2689291c,"brightness"), &brightness, true);
		pParams->GetInteger(CRCD(0xc164842a,"adjust_current"), &adjust_current);
		if (adjust_current == 1)
		{
			force_adjust_current = true;
		}

		p_texture->AdjustBrightness(brightness, force_adjust_current);
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't find texture %s", Script::FindChecksumName(checksum)));
	}

	return true;
}

// @script | AdjustTextureHSV | Scales the texture HSV by the supplied values
// @parm name |  | name of texture
// @parm float | h | Hue offset (0 - 360)
// @parm float | s | Saturation scale (1.0 = no change)
// @parm float | v | Brightness scale (1.0 = no change)
// @parmopt int | adjust_current | 0 | If set to 1, adjusts the current image, even if the original image exists
bool ScriptAdjustTextureHSV(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(NONAME, &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		float h = 0.0f;
		float s = 1.0f;
		float v = 1.0f;
		int adjust_current = 0;
		bool force_adjust_current = false;

		bool got_params;
		got_params = pParams->GetFloat(CRCD(0x6e94f918,"h"), &h);
		got_params = pParams->GetFloat(CRCD(0xe4f130f4,"s"), &s) && got_params;
		got_params = pParams->GetFloat(CRCD(0x949bc47b,"v"), &v) && got_params;

		Dbg_MsgAssert(got_params, ("Need to supply values for h, s, and v"));
		Dbg_MsgAssert((h >= 0.0f) && (h <= 360.0f), ("h must be in the range of 0-360"));
		Dbg_MsgAssert(s >= 0.0f, ("s cannot be negative"));

		pParams->GetInteger(CRCD(0xc164842a,"adjust_current"), &adjust_current);
		if (adjust_current == 1)
		{
			force_adjust_current = true;
		}

		p_texture->AdjustHSV(h, s, v, force_adjust_current);
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't find texture %s", Script::FindChecksumName(checksum)));
	}

	return true;
}
// @script | CopyTexture | Copy 2D sprite texture into a new texture
// @parm name | src | name of texture
// @parm name | new | name of new texture
bool ScriptCopyTexture(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 checksum;
	if (!pParams->GetChecksum(CRCD(0x9fbbdb72,"src"), &checksum))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_sprite_tex_dict->GetTexture(checksum);
	if (p_texture)
	{
		uint32 new_checksum;
		if (pParams->GetChecksum(CRCD(0x941cbbba,"new"), &new_checksum))
		{
			CTexDictManager::sp_sprite_tex_dict->CopyTexture(new_checksum, p_texture);
		}
		else
		{
			Dbg_MsgAssert(0, ("no new texture specified"));
		}
	} else {
		Dbg_MsgAssert(0, ("Can't find texture %s to copy", Script::FindChecksumName(checksum)));
	}

	return true;
}

// @script | CombineTextures | Combine two textures
// @parm name | src | name of orig texture (and final texture if no new texture is supplied)
// @parm name | top | name of top additional texture
// @parmopt name | new | | name of new destination texture
// @parmopt flag | no_palette_gen | | use original palette (faster)
bool ScriptCombineTextures(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 new_checksum = 0;
	uint32 tex1_checksum;
	uint32 tex2_checksum;
	bool gen_palette = true;

	if (!pParams->GetChecksum(CRCD(0x9fbbdb72,"src"), &tex1_checksum))
	{
		Dbg_MsgAssert(0, ("no original source texture specified"));
	}
	if (!pParams->GetChecksum(CRCD(0xe126e035,"top"), &tex2_checksum))
	{
		Dbg_MsgAssert(0, ("no top source texture specified"));
	}
	pParams->GetChecksum(CRCD(0x941cbbba,"new"), &new_checksum);
	if (pParams->ContainsFlag(CRCD(0x5905256b,"no_palette_gen")))
	{
		gen_palette = false;
	}

	CTexture *p_texture1 = CTexDictManager::sp_sprite_tex_dict->GetTexture(tex1_checksum);
	if (p_texture1)
	{
		CTexture *p_texture2 = CTexDictManager::sp_sprite_tex_dict->GetTexture(tex2_checksum);
		if (p_texture2)
		{
			if (new_checksum)
			{
				// Make new texture
				CTexDictManager::sp_sprite_tex_dict->CombineTextures(new_checksum, p_texture1, p_texture2, gen_palette);
			}
			else
			{
				// Just overwrite original texture
				p_texture1->CombineTextures(p_texture2, gen_palette);
			}
		}
		else
		{
			Dbg_MsgAssert(0, ("Can't find texture %s to combine", Script::FindChecksumName(tex2_checksum)));
		}
	}
	else
	{
		Dbg_MsgAssert(0, ("Can't find texture %s to combine", Script::FindChecksumName(tex1_checksum)));
	}

	return true;
}

bool ScriptLoadParticleTexture(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	const char *p_name;
	if (!pParams->GetText(NONAME, &p_name))
		Dbg_MsgAssert(0, ("no texture specified"));

//	Dbg_MsgAssert(*p_name,("%s\n Empty name string for Particle Texture\n",pScript->GetScriptInfo()));

	if (! *p_name)
	{
		Dbg_Message("WARNING:  empty name in LoadAllParticleTextures - proabably an old level\n");
		return false;
	}

	
	CTexture *p_texture = CTexDictManager::sp_particle_tex_dict->GetTexture(p_name);
	
	bool	perm = pParams->ContainsFlag(CRCD(0xd5928f25,"perm"));
	
	if (!p_texture)
	{
		p_texture = CTexDictManager::sp_particle_tex_dict->LoadTexture(p_name, true, true, perm);
	}
	else
	{
		// The testure is already there
		// if we are on CD, we assume that's the one we want to load
		// but if not, then we assume we want to re-load the texture
		// for quick previewing by artists
		// so we unload it, and load it up agian
		#ifdef	__PLAT_NGPS__		
		if (!Config::CD())
		{	
			if (Config::gGotExtraMemory)
			{
				// If we have the extra memory, then use that
				// to avoid fragmentation when testing variants of perm textures
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
			}
			perm = p_texture->IsPerm();
			CTexDictManager::sp_particle_tex_dict->UnloadTexture(p_texture);
			p_texture = CTexDictManager::sp_particle_tex_dict->LoadTexture(p_name, true, true, perm);
			if (Config::gGotExtraMemory)
			{
				Mem::Manager::sHandle().PopContext();
			}
		}
		#endif
	}
	
	return p_texture != NULL;
}




bool ScriptUnloadParticleTexture(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	const char *p_name;
	if (!pParams->GetText(NONAME, &p_name))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_particle_tex_dict->GetTexture(p_name);
	if (p_texture)
	{
		p_texture->RemoveFromVram();
		CTexDictManager::sp_particle_tex_dict->UnloadTexture(p_texture);
	} else {

		printf ("Can't find particle texture %s to unload", p_name);
	}
	return true;
}

bool ScriptLoadSFPTexture(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	const char *p_name;
	if (!pParams->GetText(NONAME, &p_name))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_particle_tex_dict->LoadTexture(p_name, false);

	return p_texture != NULL;
}




bool ScriptUnloadSFPTexture(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	const char *p_name;
	if (!pParams->GetText(NONAME, &p_name))
		Dbg_MsgAssert(0, ("no texture specified"));
	
	CTexture *p_texture = CTexDictManager::sp_particle_tex_dict->GetTexture(p_name);
	if (p_texture)
	{
		CTexDictManager::sp_particle_tex_dict->UnloadTexture(p_texture);
	} else {
		Dbg_MsgAssert(0, ("Can't find texture %s to unload", p_name));
	}
	return true;
}

bool ScriptDumpTextures(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	#ifdef	__NOPT_ASSERT__
	// Get the hashable that has the textures in it
	Lst::HashTable * p_textures = CTexDictManager::sp_particle_tex_dict->GetTexLookup();
	p_textures->PrintContents();

	p_textures->IterateStart();
	Nx::CTexture *p_tex = p_textures->IterateNext();		
	while(p_tex)
	{
	
		printf ("0x%8x: (%d x %d) %s\n",
					p_tex->GetChecksum(),
					 p_tex->GetWidth(),
					 p_tex->GetHeight(),
					 p_tex->GetName()
					);  
	
		p_tex = p_textures->IterateNext();		
	}
	#endif
	
	return true;
}


void 	FlushParticleTextures(bool all)
{
   
   CTexDictManager::sp_particle_tex_dict->FlushTextures(all); 
	
}


} //end namespace Nx




================================================
FILE: Code/Gfx/NxTexMan.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// NxTexMan.H - Neversoft Engine, Rendering portion, Platform independent interface

#ifndef	__GFX_NX_TEX_MAN_H__
#define	__GFX_NX_TEX_MAN_H__

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 

#include 


namespace Script
{
	class CScriptStructure;
	class CScript;
}

namespace Nx
{

///////////////////////////////////////////////////////////////////////////////////
// Nx::CTexDictManager
class	CTexDictManager
{
public:

	// Create and destroy CTexDicts
	static CTexDict *			sCreateTextureDictionary(const char *p_tex_dict_name);	// Creates an empty dictionary
	static CTexDict *			sLoadTextureDictionary(uint32 checksum, uint32* pData, int dataSize, bool isLevelData, uint32 texDictOffset=0, bool isSkin=0, bool forceTexDictLookup=false );    // Loads dictionary from file
	static CTexDict *			sLoadTextureDictionary(const char *p_tex_dict_name, bool isLevelData, uint32 texDictOffset=0, bool isSkin=0, bool forceTexDictLookup=false );    // Loads dictionary from file
	static bool					sUnloadTextureDictionary(CTexDict *p_tex_dict);

	static CTexDict *  			sGetTextureDictionary(uint32 checksum);		// Will not affect ref_count

	// temporary sprite texture dictionary
	static CTexDict *			sp_sprite_tex_dict;
	static CTexDict *			sp_particle_tex_dict;

private:	
	static Lst::HashTable	s_tex_dict_lookup;

	// Platform-specific calls
	// The following two will only be called if a physical load or unload is done
	static CTexDict *			s_plat_load_texture_dictionary(const char *p_tex_dict_name, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup = false );
	static CTexDict *			s_plat_load_texture_dictionary(uint32 checksum, uint32* pData, int dataSize, bool is_level_data, uint32 texDictOffset, bool isSkin, bool forceTexDictLookup = false );
	static CTexDict *			s_plat_create_texture_dictionary(uint32 checksum);
	static bool					s_plat_unload_texture_dictionary(CTexDict *p_tex_dict);
};

bool ScriptLoadTexture(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptUnloadTexture(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptAddTextureToVram(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptRemoveTextureFromVram(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptLoadFaceTextureFromProfile(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptGenerate32BitImage(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptOffsetTexture(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptAdjustTextureRegion(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptPullTextureToEdge(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptPushTextureToPoint(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptAdjustTextureBrightness(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptAdjustTextureHSV(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCopyTexture(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCombineTextures(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptLoadParticleTexture(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptUnloadParticleTexture(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptLoadSFPTexture(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptUnloadSFPTexture(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptDumpTextures(Script::CScriptStructure *pParams, Script::CScript *pScript);


void 	FlushParticleTextures(bool all);


}


#endif



================================================
FILE: Code/Gfx/NxTexture.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxTexture.cpp

#include 

#include 
#include 
#include 
#include 
									 
#include 
#include 

namespace	Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static uint32 s_convert_filename_to_checksum( const char* pFileName )
{
	// Find base name
	int idx	= strlen(pFileName);
	while ((idx > 0) && pFileName[idx - 1] != '\\' && pFileName[idx - 1] != '/')
		--idx;

	const char *p_base_name = &(pFileName[idx]);

	return Crc::GenerateCRCFromString(p_base_name);
}

/////////////////////////////////////////////////////////////////////////////
// CTexture

/////////////////////////////////////////////////////////////////////////////
// These functions are the platform independent part of the interface.
					 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture::CTexture()
{
	#ifdef	__NOPT_ASSERT__
	mp_name = NULL;
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture::CTexture(const CTexture & src_texture)
{
	m_checksum = src_texture.m_checksum;
	m_perm = src_texture.m_perm;

	#ifdef	__NOPT_ASSERT__
	mp_name = new char[strlen("Copy of ")+strlen(src_texture.mp_name)+1];
	sprintf (mp_name,"Copy of %s",src_texture.mp_name);	
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture::~CTexture()
{
	#ifdef	__NOPT_ASSERT__
	if (mp_name)
	{
		delete [] mp_name;
	}
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::LoadTexture(const char *p_texture_name, bool sprite, bool alloc_vram)
{								 	
#ifdef	__NOPT_ASSERT__
	mp_name = new char[strlen(p_texture_name)+1];
	sprintf (mp_name,"%s",p_texture_name);	
#endif

	char texture_name[512];
	sprintf(texture_name,"%s",p_texture_name);
	if (sprite)
	{

		if (p_texture_name[0]=='.'
			&& p_texture_name[1]=='.'
			&& p_texture_name[2]=='/')
		{
			// detected a ../ at the start of the file name, so use the full file name
			sprintf(texture_name,"%s",p_texture_name+3);
			
		}
		else
		{
			sprintf(texture_name,"images/%s",p_texture_name);
		}
	}
	
	m_checksum = s_convert_filename_to_checksum( texture_name );
	
	return plat_load_texture(texture_name, sprite, alloc_vram);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::LoadTextureFromBuffer(uint8* p_buffer, int buffer_size, uint32 texture_checksum, bool sprite, bool alloc_vram)
{								 	
#ifdef	__NOPT_ASSERT__
	const char* p_name = Script::FindChecksumName(texture_checksum);
	mp_name = new char[strlen(p_name)+1];
	sprintf (mp_name,"%s",p_name);	
#endif
	m_checksum = texture_checksum;
	return plat_load_texture_from_buffer(p_buffer, buffer_size, sprite, alloc_vram);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::ReplaceTexture(CTexture *p_texture)
{								 	
	return plat_replace_texture(p_texture);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::Generate32BitImage(bool renderable, bool store_original)
{
	return plat_generate_32bit_image(renderable, store_original);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::Put32BitImageIntoTexture(bool new_palette)
{
	return plat_put_32bit_image_into_texture(new_palette);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::Offset(int x_pixels, int y_pixels, bool use_fill_color, Image::RGBA fill_color)
{
	Dbg_MsgAssert(abs(x_pixels) < GetWidth(), ("x_pixels is out of range: %d", x_pixels));
	Dbg_MsgAssert(abs(y_pixels) < GetHeight(), ("y_pixels is out of range: %d", y_pixels));

	return plat_offset(x_pixels, y_pixels, use_fill_color, fill_color);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::AdjustRegion(uint16 x_pos, uint16 y_pos, uint16 width, uint16 height,
							   int split_axis, uint16 start_point, uint16 end_point)
{
	Dbg_MsgAssert((split_axis >= X) && (split_axis <= Y), ("Split axis is out of range: %d", split_axis));
	Dbg_MsgAssert(x_pos < GetWidth(), ("x_pos is out of range: %d", x_pos));
	Dbg_MsgAssert(y_pos < GetHeight(), ("y_pos is out of range: %d", y_pos));
	Dbg_MsgAssert(width <= GetWidth(), ("width is out of range: %d", width));
	Dbg_MsgAssert(height <= GetHeight(), ("height is out of range: %d", height));

	return plat_adjust_region(x_pos, y_pos, width, height, split_axis, start_point, end_point);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::PullToEdge(uint16 point, int axis, int num_pixels)
{
	Dbg_MsgAssert((axis >= X) && (axis <= Y), ("axis is out of range: %d", axis));

	return plat_pull_to_edge(point, axis, num_pixels);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::PushToPoint(uint16 point, int axis, int num_pixels, bool use_fill_color, Image::RGBA fill_color)
{
	Dbg_MsgAssert((axis >= X) && (axis <= Y), ("axis is out of range: %d", axis));

	return plat_push_to_point(point, axis, num_pixels, use_fill_color, fill_color);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::AdjustBrightness(float brightness_scale, bool force_adjust_current)
{
	return plat_adjust_brightness(brightness_scale, force_adjust_current);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::AdjustHSV(float h, float s, float v, bool force_adjust_current)
{
	return plat_adjust_hsv(h, s, v, force_adjust_current);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::AddToVram()
{								 	
	return plat_add_to_vram();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::RemoveFromVram()
{								 	
	return plat_remove_from_vram();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CTexture::GetChecksum() const
{
	return m_checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint16	CTexture::GetWidth() const
{
	return plat_get_width();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint16	CTexture::GetHeight() const
{
	return plat_get_height();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CTexture::GetBitDepth() const
{
	return plat_get_bitdepth();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CTexture::GetPaletteBitDepth() const
{
	return plat_get_palette_bitdepth();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CTexture::GetNumMipmaps() const
{
	return plat_get_num_mipmaps();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::IsTransparent() const
{
	return plat_is_transparent();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::CombineTextures(CTexture *p_texture, bool palette_gen)
{
	return plat_combine_textures(p_texture, palette_gen);
}

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions are provided here:
// so engine implementors can leave certain functionality until later
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram)
{
	printf ("STUB: PlatLoadTexture\n");
	Dbg_Assert(0);
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_load_texture_from_buffer(uint8* p_buffer, int buffer_size, bool sprite, bool alloc_vram)
{
	printf ("STUB: PlatLoadTextureFromBuffer\n");
	Dbg_MsgAssert(0, ("This function was only supposed to be called on the PS2"));
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_replace_texture(CTexture *p_texture)
{								 	
	printf ("STUB: PlatReplaceTexture\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_generate_32bit_image(bool renderable, bool store_original)
{
	printf ("STUB: PlatGenerate32BitImage\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_put_32bit_image_into_texture(bool new_palette)
{
	printf ("STUB: PlatPut32BitImageIntoTexture\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_offset(int x_pixels, int y_pixels, bool use_fill_color, Image::RGBA fill_color)
{
	printf ("STUB: PlatOffset\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_adjust_region(uint16 x_pos, uint16 y_pos, uint16 width, uint16 height,
									 int split_axis, uint16 start_point, uint16 end_point)
{								 	
	printf ("STUB: PlatAdjustRegion\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_pull_to_edge(uint16 point, int axis, int num_pixels)
{
	printf ("STUB: PlatPullToEdge\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_push_to_point(uint16 point, int axis, int num_pixels, bool use_fill_color, Image::RGBA fill_color)
{
	printf ("STUB: PlatPushToPoint\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_adjust_brightness(float brightness_scale, bool force_adjust_current)
{
	printf ("STUB: PlatAdjustBrightness\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_adjust_hsv(float h, float s, float v, bool force_adjust_current)
{
	printf ("STUB: PlatAdjustHSV\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_add_to_vram()
{								 	
	printf ("STUB: PlatAddToVram\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_remove_from_vram()
{								 	
	printf ("STUB: PlatRemoveFromVram\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint16	CTexture::plat_get_width() const
{
	printf ("STUB: PlatGetWidth\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint16	CTexture::plat_get_height() const
{
	printf ("STUB: PlatGetHeight\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CTexture::plat_get_bitdepth() const
{
	printf ("STUB: PlatGetBitDepth\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CTexture::plat_get_palette_bitdepth() const
{
	printf ("STUB: PlatGetPaletteBitDepth\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CTexture::plat_get_num_mipmaps() const
{
	printf ("STUB: PlatGetNumMipmaps\n");
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_is_transparent() const
{
	printf ("STUB: PlatIsTransparent\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTexture::plat_combine_textures(CTexture *p_texture, bool palette_gen)
{
	printf ("STUB: PlatCombineTextures\n");
	return false;
}

/////////////////////////////////////////////////////////////////////////////
// CMaterial

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMaterial::CMaterial() : mp_texture(NULL)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA			CMaterial::GetRGBA() const
{
	return plat_get_rgba();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CMaterial::SetRGBA(Image::RGBA rgba)
{
	plat_set_rgba(rgba);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *			CMaterial::GetTexture() const
{
	return mp_texture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CMaterial::SetTexture(CTexture *tex)
{
	mp_texture = tex;
	plat_set_texture(tex);
}

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions:

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Image::RGBA 		CMaterial::plat_get_rgba() const
{
	printf ("STUB: PlatGetRGBA\n");
	return Image::RGBA();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CMaterial::plat_set_rgba(Image::RGBA rgba)
{
	printf ("STUB: PlatSetRGBA\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CMaterial::plat_set_texture(CTexture *tex)
{
	printf ("STUB: PlatSetTexture\n");
}

/////////////////////////////////////////////////////////////////////////////
// CTexDict

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict::CTexDict(uint32 checksum, bool create_lookup_table)
{
	m_checksum = checksum;
	m_ref_count = 1;	// Belongs to someone
	
	// Mick: for now, since most texture dictionaries don't need to reference
	// the textures by name, we only created the lookup table when it is needed
	// so we save memory	
	// Gary: Well, actually, the create-a-skater texture dictionaries
	// do need lookup tables, just in case there's texture
	// replacement.  So, this constructor should be rewritten so
	// that it takes a parameter telling it whether it should
	// create the hashtable.
	// Garrett: OK
	if (create_lookup_table)
	{
		mp_texture_lookup = new Lst::HashTable(5);
	}
	else
	{
		mp_texture_lookup = NULL;
	}

	// Derived class loads the actual file
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict::CTexDict(const char* p_tex_dict_name, bool create_lookup_table)
{
	m_checksum = Crc::GenerateCRCFromString(p_tex_dict_name);
	m_ref_count = 1;	// Just belong to one scene

	// Mick: for now, since most texture dictionaries don't need to reference
	// the textures by name, we only created the lookup table when it is needed
	// so we save memory	
	// Gary: Well, actually, the create-a-skater texture dictionaries
	// do need lookup tables, just in case there's texture
	// replacement.  So, this constructor should be rewritten so
	// that it takes a parameter telling it whether it should
	// create the hashtable.
	// Garrett: OK
	if (create_lookup_table)
	{
		mp_texture_lookup = new Lst::HashTable(5);
	}
	else
	{
		mp_texture_lookup = NULL;
	}

	// Derived class loads the actual file
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexDict::~CTexDict()
{
	// Unload everything
	//plat_unload_texture_dictionary();					// the derived class does this

	FlushTextures(true);

	if (mp_texture_lookup)
	{
		delete mp_texture_lookup;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Flush all non permanent textures
void CTexDict::FlushTextures(bool flush_all)
{
	if (mp_texture_lookup)
	{
		// Delete textures
		mp_texture_lookup->IterateStart();
		CTexture *p_texture = mp_texture_lookup->IterateNext();
		while (p_texture)
		{
			CTexture *p_next = mp_texture_lookup->IterateNext();
			if (flush_all || !p_texture->IsPerm())
			{
				#ifdef	__NOPT_ASSERT__
				if (p_texture->GetName())
				{
//					printf ("deleting particle tex %s\n",p_texture->GetName());
				}
				#endif				
				uint32 checksum = p_texture->GetChecksum();			
				delete p_texture;
				mp_texture_lookup->FlushItem(checksum);			
			}
			else
			{
							#ifdef	__NOPT_ASSERT__
				if (p_texture->GetName())
				{
//					printf ("NOT deleting particle tex %s  perm = %d\n",p_texture->GetName(),p_texture->IsPerm());
				}
				#endif				

			}
			p_texture = p_next;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *			CTexDict::LoadTexture(const char *p_texture_name, bool sprite, bool alloc_vram, bool perm)
{
	Mem::PushMemProfile((char*)p_texture_name);
	
	
	CTexture *p_texture = plat_load_texture(p_texture_name, sprite, alloc_vram);

	if (p_texture)
	{
		p_texture->SetPerm(perm);
		mp_texture_lookup->PutItem(p_texture->GetChecksum(), p_texture);
		
	} else {
		Dbg_Error("Texture %s not found", p_texture_name);
	}

	Mem::PopMemProfile();
	return p_texture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture * 			CTexDict::LoadTextureFromBuffer(uint8* p_buffer, int buffer_size, uint32 texture_checksum, bool sprite, bool alloc_vram, bool perm)
{
	Mem::PushMemProfile((char*)"texture from buffer");
	CTexture *p_texture = plat_load_texture_from_buffer(p_buffer, buffer_size, texture_checksum, sprite, alloc_vram);

	if (p_texture)
	{
		p_texture->SetPerm(perm);
		mp_texture_lookup->PutItem(p_texture->GetChecksum(), p_texture);
		
	} else {
		Dbg_MsgAssert(0,("Couldn't load texture %s from buffer",Script::FindChecksumName(texture_checksum)));
	}

	Mem::PopMemProfile();
	return p_texture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CTexDict::UnloadTexture(CTexture *p_texture)
{
	uint32 checksum = p_texture->GetChecksum();

	// Because the texture might be still being used, we have to wait for the frame to 
	// finish rendering
	Nx::CEngine::sFinishRendering();

	if (plat_unload_texture(p_texture))
	{
		mp_texture_lookup->FlushItem(checksum);
		return true;
	} else {
		Dbg_Error("Cannot unload texture %x", checksum);
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CTexDict::AddTexture(CTexture *p_texture)
{
	Dbg_Assert(mp_texture_lookup);
	Dbg_Assert(p_texture);

	mp_texture_lookup->PutItem(p_texture->GetChecksum(), p_texture);

	plat_add_texture(p_texture);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture* CTexDict::get_source_texture( const char* p_src_texture_name )
{
	uint32 src_checksum = Crc::GenerateCRCFromString( p_src_texture_name );
	//src_checksum *= 24;

	// look for the texture with that checksum
	static int modifiers[] = { 2, 3, 4, 2 * 3, 3 * 4, 2 * 4, 2 * 3 * 4, -1 };
	CTexture* pTexture = NULL;
	for (int idx = 0; modifiers[idx] > 0; idx++)
	{
		uint32 calc_checksum = src_checksum * modifiers[idx];
		pTexture = GetTexture( calc_checksum );

		if (pTexture)
		{
			src_checksum *= calc_checksum;
			break;
		}
	}

	// If we haven't found the texture at this stage, it may be because an auto-generated MIP version was used.
	// In which case, the name will have _auto32m0 appended to it. Try searching for this name instead.
	if( pTexture == NULL )
	{
		char auto32_name[512];
		strcpy( auto32_name, p_src_texture_name );

		// Remove the .png, and add the new suffix.
		int length = strlen( auto32_name );
		if( length > 4 )
		{
			auto32_name[strlen( auto32_name ) - 4] = 0;

			// Append the extra bit.
			strcat( auto32_name, "_auto32m0.png" );
		}
		
		src_checksum = Crc::GenerateCRCFromString( auto32_name );
		for( int idx = 0; modifiers[idx] > 0; idx++ )
		{
			uint32 calc_checksum = src_checksum * modifiers[idx];
			pTexture = GetTexture( calc_checksum );
			if( pTexture )
			{
				src_checksum *= calc_checksum;
				break;
			}
		}
	}

	return pTexture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTexDict::ReplaceTexture(const char* p_src_texture_name, const char* p_dst_texture_name)
{
	CTexture* pTexture = get_source_texture( p_src_texture_name );
	
	if ( pTexture )
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

#ifdef __NOPT_ASSERT__
		if ( Script::GetInt( CRCD(0x2a648514,"cas_artist"), false ) )
		{
			Dbg_Message( "Replacing %s with %s here", p_src_texture_name, p_dst_texture_name );
		}
#endif

		CTexture *p_new_texture = LoadTexture(p_dst_texture_name, false, false);
		Dbg_MsgAssert(p_new_texture, ("Can't find replacement texture %s", p_dst_texture_name));

		bool result = pTexture->ReplaceTexture( p_new_texture );
		Dbg_MsgAssert(result, ("Can't replace texture %s with %s", p_src_texture_name, p_dst_texture_name));

		// We're done with the new texture, get rid of it
		UnloadTexture(p_new_texture);

		Mem::Manager::sHandle().PopContext();

		return result;
	}
	else
	{
		// GJ:  The texture checksum lookup table is only
		// available for sprite tex dicts, not for CAS
		// parts, so I've commented out the following for now...
//		if ( Script::GetInt( "cas_artist", false ) )
//		{
//			Dbg_Message( "Couldn't find %s (%08x) in order to replace texture %s", p_src_texture_name, src_checksum, p_dst_texture_name );
//		}
		
		Dbg_Assert( mp_texture_lookup );

/*
		// For debugging...
		
		if (mp_texture_lookup)
		{
			mp_texture_lookup->IterateStart();
			CTexture *p_texture;
			int count = 0;
			while ((p_texture = mp_texture_lookup->IterateNext()))
			{
				Dbg_Message("Found Checksum #%d %08x", count, p_texture->GetChecksum());
				count++;
			}
			Dbg_Assert( count );
		}
*/
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTexDict::ReplaceTexture( const char* p_src_texture_name, CTexture* p_dst_texture )
{
	CTexture* pTexture = get_source_texture( p_src_texture_name );
	
	if ( pTexture )
	{
		bool result = pTexture->ReplaceTexture( p_dst_texture );
		Dbg_MsgAssert(result, ("Can't replace texture %s with CTexture %p", p_src_texture_name, p_dst_texture));

		return result;
	}
	else
	{
		// GJ:  The texture checksum lookup table is only
		// available for sprite tex dicts, not for CAS
		// parts, so I've commented out the following for now...
//		if ( Script::GetInt( "cas_artist", false ) )
//		{
//			Dbg_Message( "Couldn't find %s (%08x) in order to replace texture %s", p_src_texture_name, src_checksum, p_dst_texture_name );
//		}
		
		Dbg_Assert( mp_texture_lookup );

/*
		// For debugging...
		
		if (mp_texture_lookup)
		{
			mp_texture_lookup->IterateStart();
			CTexture *p_texture;
			int count = 0;
			while ((p_texture = mp_texture_lookup->IterateNext()))
			{
				Dbg_Message("Found Checksum #%d %08x", count, p_texture->GetChecksum());
				count++;
			}
			Dbg_Assert( count );
		}
*/
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *			CTexDict::CopyTexture(uint32 new_texture_checksum, CTexture *p_texture)
{
	CTexture *p_new_texture = plat_copy_texture(new_texture_checksum, p_texture);

	if (p_new_texture)
	{
		p_new_texture->m_checksum = new_texture_checksum;
		mp_texture_lookup->PutItem(new_texture_checksum, p_new_texture);
	} else {
		Dbg_Error("Could not create new CTexture %x", new_texture_checksum);
	}

	return p_new_texture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *			CTexDict::CombineTextures(uint32 new_texture_checksum, CTexture *p_texture1, CTexture *p_texture2, bool palette_gen)
{
	CTexture *p_new_texture = CopyTexture(new_texture_checksum, p_texture1);

	if (p_new_texture)
	{
		p_new_texture->CombineTextures(p_texture2, palette_gen);
	} else {
		Dbg_Error("Could not create new CTexture for CombineTexture of %x", new_texture_checksum);
	}

	return p_new_texture;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *			CTexDict::GetTexture(uint32 checksum) const
{
	if ( !mp_texture_lookup )
	{
		Dbg_Error( "No texture lookup has been created for this texdict" );
		return NULL;
	}

	return mp_texture_lookup->GetItem(checksum);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *			CTexDict::GetTexture(const char *p_texture_name) const
{
	return GetTexture(s_convert_filename_to_checksum(p_texture_name));
}


///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions:

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *			CTexDict::plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram)
{
	printf ("STUB: PlatLoadTexture\n");
	Dbg_Assert(0);
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *			CTexDict::plat_load_texture_from_buffer(uint8* p_buffer, int buffer_size, uint32 texture_checksum, bool sprite, bool alloc_vram)
{
	printf ("STUB: PlatLoadTextureFromBuffer\n");
	Dbg_MsgAssert(0, ("This function was only supposed to be called on the PS2"));
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *			CTexDict::plat_reload_texture(const char *p_texture_name)
{
	printf ("STUB: PlatReloadTexture\n");
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CTexDict::plat_unload_texture(CTexture *p_texture)
{
	printf ("STUB: PlatUnloadTexture\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CTexDict::plat_add_texture(CTexture *p_texture)
{
	printf ("STUB: PlatAddTexture\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CTexDict::plat_remove_texture(CTexture *p_texture)
{
	printf ("STUB: PlatRemoveTexture\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *			CTexDict::plat_copy_texture(uint32 new_texture_checksum, CTexture *p_texture)
{
	printf ("STUB: PlatCopyTexture\n");
	return NULL;
}

}




================================================
FILE: Code/Gfx/NxTexture.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// NxTexture.H - Neversoft Engine, Rendering portion, Platform independent interface

#ifndef	__GFX_NX_TEXTURE_H__
#define	__GFX_NX_TEXTURE_H__

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 

#include 


namespace Nx
{

class CTexDict;
class CTexDictManager;

//////////////////////////////////////////////////////////////////////////////////
// Nx::CTexture replaces RwTexture
class	CTexture
{
public:
							CTexture();
							CTexture(const CTexture & src_texture);		// Copy constructor
	virtual					~CTexture();

	bool					LoadTexture(const char *p_texture_name, bool sprite, bool alloc_vram = true);
	bool					LoadTextureFromBuffer(uint8* p_buffer, int buffer_size, uint32 texture_checksum, bool sprite, bool alloc_vram = true);
	bool					ReplaceTexture(CTexture *p_texture);

	// Temp 32-bit texture image generation.  All the texture manipulation routines need to use
	// 32-bit versions of the texture.  But to palettize every operation can be slow, so keeping
	// the temp buffer around speeds things up greatly.
	bool					Generate32BitImage(bool renderble = false, bool store_original = false);
	bool					Put32BitImageIntoTexture(bool new_palette = false);

	// Combines current texture with new texture (overwriting original one)
	bool					CombineTextures(CTexture *p_texture, bool palette_gen = true);

	// Move texture (cropping the part that moves off and filling in the new parts with either the old edge or a
	// supplied fill color)
	bool					Offset(int x_pixels, int y_pixels, bool use_fill_color = false,
								   Image::RGBA fill_color = Image::RGBA(128, 128, 128, 128));

	// This function will scale a rectangular section of a texture.  Each side of the texture
	// will be stretched or shrunk from the start_point to the end_point along the split axis.
	bool					AdjustRegion(uint16 x_pos, uint16 y_pos, uint16 width, uint16 height,
										 int split_axis, uint16 start_point, uint16 end_point);

	// Pull texture from point along the axis by num_pixels (+ or - determines the direction)
	// and crop anything going outside the texture border.
	bool					PullToEdge(uint16 point, int axis, int num_pixels);
	// Similar to PullToEdge(), except it pushes the texture from the edge to the point.  Instead
	// of cropping, it either stretches the edge line or fills it in with a fill color
	bool					PushToPoint(uint16 point, int axis, int num_pixels, bool use_fill_color = false,
										Image::RGBA fill_color = Image::RGBA(128, 128, 128, 128));

	bool					AdjustBrightness(float brightness_scale, bool force_adjust_current = false);
	bool					AdjustHSV(float h, float s, float v, bool force_adjust_current = false);

	// These VRAM calls won't do anything on platforms that don't have separate
	// texture memory
	bool					AddToVram();
	bool					RemoveFromVram();

	uint32					GetChecksum() const;
	uint16					GetWidth() const;
	uint16					GetHeight() const;
	uint8					GetBitDepth() const;
	uint8					GetPaletteBitDepth() const;
	uint8					GetNumMipmaps() const;
	bool					IsTransparent() const;

	void					SetPerm( bool perm) {m_perm = perm;}
	bool					IsPerm() {return m_perm;}

#ifdef	__NOPT_ASSERT__
	char *					GetName() {return mp_name;}
#else
	char *					GetName() {return "WARNING NO NAME";}
#endif	
	

protected:

	uint32					m_checksum;

	// So it can access set_checksum()
	friend CTexDict;

private:
	virtual bool			plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram);
	virtual bool			plat_load_texture_from_buffer(uint8* p_buffer, int buffer_size, bool sprite, bool alloc_vram);
	virtual bool			plat_replace_texture(CTexture *p_texture);

	virtual bool			plat_generate_32bit_image(bool renderble = false, bool store_original = false);
	virtual bool			plat_put_32bit_image_into_texture(bool new_palette = false);

	virtual bool			plat_offset(int x_pixels, int y_pixels, bool use_fill_color, Image::RGBA fill_color);

	virtual bool			plat_adjust_region(uint16 x_pos, uint16 y_pos, uint16 width, uint16 height,
											   int split_axis, uint16 start_point, uint16 end_point);

	virtual bool			plat_pull_to_edge(uint16 point, int axis, int num_pixels);
	virtual bool			plat_push_to_point(uint16 point, int axis, int num_pixels, bool use_fill_color,
											   Image::RGBA fill_color);

	virtual bool			plat_adjust_brightness(float brightness_scale, bool force_adjust_current = false);
	virtual bool			plat_adjust_hsv(float h, float s, float v, bool force_adjust_current = false);

	virtual bool			plat_add_to_vram();
	virtual bool			plat_remove_from_vram();

	virtual uint16			plat_get_width() const;
	virtual uint16			plat_get_height() const;
	virtual uint8			plat_get_bitdepth() const;
	virtual uint8			plat_get_palette_bitdepth() const;
	virtual uint8			plat_get_num_mipmaps() const;
	virtual bool			plat_is_transparent() const;

	virtual bool			plat_combine_textures(CTexture *p_texture, bool palette_gen);
	
	bool					m_perm;		// set to true if this texture should survive a level change 
	
	#ifdef	__NOPT_ASSERT__
	char	*				mp_name;
	#endif

};

//////////////////////////////////////////////////////////////////////////////////
// Nx::CMaterial
class	CMaterial
{
public:
							CMaterial();
	virtual					~CMaterial()	{}

	Image::RGBA				GetRGBA() const;
	void					SetRGBA(Image::RGBA rgba);
	CTexture *				GetTexture() const;
	void					SetTexture(CTexture *tex);

protected:
	uint32					m_checksum;
	CTexture *				mp_texture;

private:
	virtual Image::RGBA		plat_get_rgba() const;
	virtual void			plat_set_rgba(Image::RGBA rgba);
	virtual void			plat_set_texture(CTexture *tex);

};

//////////////////////////////////////////////////////////////////////////////////
// Nx::CTexDict
class	CTexDict
{
public:
	// The constructor and destructor start the loading and unloading
							CTexDict(uint32 checksum, bool create_lookup_table = true);	// loads nothing
							CTexDict(const char *p_tex_dict_name, bool create_lookup_table = false);
	virtual					~CTexDict();

	void					IncRefCount();
	int16					DecRefCount();

	// The load and unload functions probably will change, since
	// we will only load and unload whole texture dictionaries.
	// For now, they will only be used for the 2D textures.
	CTexture *				LoadTexture(const char *p_texture_name, bool sprite, bool alloc_vram = true, bool perm = true);
	CTexture *				LoadTextureFromBuffer(uint8* p_buffer, int buffer_size, uint32 texture_checksum, bool sprite, bool alloc_vram = true, bool perm = true);
	CTexture *				ReloadTexture(const char *p_texture_name);
	bool					UnloadTexture(CTexture *p_texture);
	void					AddTexture(CTexture *p_texture);
	bool					RemoveTexture(CTexture *p_texture);
	bool					ReplaceTexture( const char* p_src_texture_name, const char* p_dst_texture_name );
	bool					ReplaceTexture( const char* p_src_texture_name, CTexture* p_dst_texture );

	CTexture *				CopyTexture(uint32 new_texture_checksum, CTexture *p_texture);
	// Combine two textures to make a new texture
	CTexture *				CombineTextures(uint32 new_texture_checksum, CTexture *p_texture1, CTexture *p_texture2, bool palette_gen = true);

	uint32					GetChecksum() const;
	CTexture *  			GetTexture(uint32 checksum) const;
	CTexture *  			GetTexture(const char *p_texture_name) const;
	
	uint32					GetFileSize() {return m_file_size;}

	Lst::HashTable *GetTexLookup( void ) { return mp_texture_lookup; }

	void					FlushTextures(bool flush_all);	// flush textures, optionally flushing perm textures

protected:
	void					set_checksum(uint32 checksum);
	CTexture*				get_source_texture( const char* p_src_texture_name );

	uint32					m_checksum;
	int16					m_ref_count;

	Lst::HashTable *	mp_texture_lookup;

	// So it can access set_checksum()
	friend CTexDictManager;

private:	
	// Platform-specific calls
	virtual CTexture *			plat_load_texture(const char *p_texture_name, bool sprite, bool alloc_vram);
	virtual CTexture *			plat_load_texture_from_buffer(uint8* p_buffer, int buffer_size, uint32 texture_checksum, bool sprite, bool alloc_vram);
	virtual CTexture *			plat_reload_texture(const char *p_texture_name);
	virtual bool				plat_unload_texture(CTexture *p_texture);
	virtual void				plat_add_texture(CTexture *p_texture);
	virtual bool				plat_remove_texture(CTexture *p_texture);
	virtual CTexture *			plat_copy_texture(uint32 new_texture_checksum, CTexture *p_texture);

protected:
	uint32						m_file_size;		// DEBUGGING USE ONLY, NOT GUARENTEED							 
								 
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void			CTexDict::IncRefCount()
{
	m_ref_count++;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline int16		CTexDict::DecRefCount()
{
	return --m_ref_count;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint32		CTexDict::GetChecksum() const
{
	return m_checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void			CTexDict::set_checksum(uint32 checksum)
{
	m_checksum = checksum;
}

}


#endif



================================================
FILE: Code/Gfx/NxTextured3dPoly.cpp
================================================
#include 
#include 

#include 
#include 
#include 

namespace Nx
{

static Lst::HashTable< Nx::CTextured3dPoly > s_poly_table(1);

void CTextured3dPoly::sRenderAll()
{
	CTextured3dPoly *p_poly;
	s_poly_table.IterateStart();
	while(( p_poly = s_poly_table.IterateNext()))
	{
		p_poly->Render();
	}
}

CTextured3dPoly::CTextured3dPoly()
{
	s_poly_table.PutItem((uint32)this,this);
}

CTextured3dPoly::~CTextured3dPoly()
{
	s_poly_table.FlushItem((uint32)this);
}

void CTextured3dPoly::SetTexture(uint32 texture_checksum)
{
	plat_set_texture(texture_checksum);
}

void CTextured3dPoly::SetTexture(const char *p_textureName)
{
	SetTexture(Crc::GenerateCRCFromString(p_textureName));
}
	
void CTextured3dPoly::SetPos(const Mth::Vector &pos, float width, float height, const Mth::Vector &normal, float angle)
{
	Mth::Vector offx(1.0f,0.0f,0.0f);
	Mth::Vector offz=Mth::CrossProduct(normal,offx);
	
	offx*=width/2.0f;
	offz*=height/2.0f;
	
	mp_pos[0]=pos-offx-offz;
	mp_pos[1]=pos+offx-offz;
	mp_pos[2]=pos+offx+offz;
	mp_pos[3]=pos-offx+offz;
}

void CTextured3dPoly::Render()
{
	plat_render();
}	

} // namespace Nx



================================================
FILE: Code/Gfx/NxTextured3dPoly.h
================================================
//////////////////////////////////////////////////////////////////////////////////////////////
// NxTextured3dPoly.h - Neversoft Engine, Rendering portion, Platform independent interface

#ifndef	__GFX_NX_TEXTURED_3D_POLY_H__
#define	__GFX_NX_TEXTURED_3D_POLY_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __CORE_MATH_VECTOR_H
#include 
#endif

namespace Nx
{

// Class for displaying a single textured 3d poly. Currently used by the pedestrian shadows, which
// are just simple circular textures oriented to the ground.
class	CTextured3dPoly
{
protected:
	Mth::Vector mp_pos[4];
	
public:
							CTextured3dPoly();
	virtual					~CTextured3dPoly();

	void					SetTexture(const char *p_textureName);
	void 					SetTexture(uint32 texture_checksum);
	
	void					SetPos(const Mth::Vector &pos, float width, float height, const Mth::Vector &normal, float angle=0.0f);

	void					Render();

	static void				sRenderAll();
		
private:
	virtual void			plat_render() {}
	virtual void			plat_set_texture(uint32 texture_checksum) {}
	
};

}

#endif



================================================
FILE: Code/Gfx/NxViewMan.cpp
================================================

/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Nx Viewport Manager										**
**																			**
**	File name:		gfx/nxviewman.cpp										**
**																			**
**	Created by:		04/30/02	-	grj										**
**																			**
**	Description:	Viewport Manager										**
**																			**
*****************************************************************************/

// start autoduck documentation
// @DOC nxviewman
// @module nxviewman | None
// @subindex Scripting Database
// @index script | nxviewman

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Nx
{

// This defines the width to height ratio of the camera's *view* window, and
// as such does not need to be changed for different screen modes or systems.
const float vBASE_ASPECT_RATIO = 1.428566f;

const float	CViewportManager::s_viewport_rects[vNUM_VIEWPORTS][4] =
{
//    x      y      w     h
	{ 0.0f,  0.0f,  1.0f, 1.0f},	// vMAIN
	{ 0.0f,  0.0f,  1.0f, 1.0f},	// vSECONDPLAYER (2nd player fullscreen)
	{ 0.0f,  0.0f,  0.5f, 1.0f},	// vSPLIT_V_LEFT
	{ 0.5f,  0.0f,  0.5f, 1.0f},	// vSPLIT_V_RIGHT
	{ 0.0f,  0.0f,  1.0f, 0.5f},	// vSPLIT_H_TOP
	{ 0.0f,  0.5f,  1.0f, 0.5f},	// vSPLIT_H_BOTTOM
	{ 0.0f,  0.0f,  0.5f, 0.5f},	// vSPLIT_QUARTER_UL
	{ 0.5f,  0.0f,  0.5f, 0.5f},	// vSPLIT_QUARTER_UR
	{ 0.0f,  0.5f,  0.5f, 0.5f},	// vSPLIT_QUARTER_LL
	{ 0.5f,  0.5f,  0.5f, 0.5f}		// vSPLIT_QUARTER_LR
};

const int 	CViewportManager::s_num_active_viewports[vNUM_SCREEN_MODES]
							 = { 1, 2, 2, 4, 1, 1 };
const int	CViewportManager::s_screen_mode_viewport_indexes[vNUM_SCREEN_MODES][vMAX_NUM_ACTIVE_VIEWPORTS]
					= { { vMAIN,			 -1,				-1,				   -1 },				// vONE_CAM
						{ vSPLIT_V_LEFT,	 vSPLIT_V_RIGHT,	-1,				   -1 },				// vSPLIT_V
						{ vSPLIT_H_TOP,		 vSPLIT_H_BOTTOM,	-1,				   -1 },				// vSPLIT_H
						{ vSPLIT_QUARTER_UL, vSPLIT_QUARTER_UR, vSPLIT_QUARTER_LL, vSPLIT_QUARTER_LR },	// vSPLIT_QUARTERS
						{ vMAIN,			 -1,				-1,				   -1 },				// vHORSE1
						{ -1,				 vSECONDPLAYER,		-1,				   -1 },				// vHORSE2
					  };


CViewport*	CViewportManager::sp_viewports[vNUM_VIEWPORTS];
const int*	CViewportManager::sp_active_viewport_indexes = s_screen_mode_viewport_indexes[0];

ScreenMode	CViewportManager::s_screen_mode;
int			CViewportManager::s_num_active_viewports_cached=0;

float		CViewportManager::s_default_angle;
float		CViewportManager::s_screen_angle;
float		CViewportManager::s_screen_angle_factor = 1.0f;
float		CViewportManager::s_screen_aspect;

float		CViewportManager::s_2D_in_3D_space_noscale_distance = 300.0f;
float		CViewportManager::s_2D_in_3D_space_max_scale = 3.0f;
float		CViewportManager::s_2D_in_3D_space_min_scale = 0.0f;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 		CViewportManager::sInit( void )
{
	// Create viewports
	for ( uint32 i = 0; i < vNUM_VIEWPORTS; i++ )
	{
		Mth::Rect theRect( s_viewport_rects[i][0], s_viewport_rects[i][1], s_viewport_rects[i][2], s_viewport_rects[i][3] );

		sp_viewports[i] = s_create_viewport(&theRect);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CViewportManager::sCleanup( void )
{
	for ( uint32 i = 0; i < vNUM_VIEWPORTS; i++ )
	{
		if ( sp_viewports[i] )
		{
			delete sp_viewports[i];
			sp_viewports[i] = NULL;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CViewport*	CViewportManager::sGetActiveViewport(int view)
{
	Dbg_MsgAssert(view < s_num_active_viewports[s_screen_mode], ("Active viewport number is invalid %d", view));

	int found_views = 0;

	// Go through array, and don't count invalid viewports
	for ( uint32 i = 0; i < vNUM_VIEWPORTS; i++ )
	{
		if (sp_active_viewport_indexes[i] >= 0)
		{
			if (found_views == view)
			{
				return sp_viewports[sp_active_viewport_indexes[i]];
			}
			found_views++;
		}
	}

	Dbg_MsgAssert(0, ("Cant find active viewport # %d", view));

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int			CViewportManager::sGetActiveViewportNumber(CViewport *p_viewport)
{
	int found_views = 0;

	// Go through array, and don't count invalid viewports
	for ( uint32 i = 0; i < vNUM_VIEWPORTS; i++ )
	{
		if (sp_active_viewport_indexes[i] >= 0)
		{
			if (sp_viewports[sp_active_viewport_indexes[i]] == p_viewport)
			{
				return found_views;
			}
			found_views++;
		}
	}

	// Not found
	return -1;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::Camera* CViewportManager::sGetCamera(int index)
{
	//Dbg_MsgAssert(sp_active_viewport_indexes[index] >= 0, ("Viewport index invalid %d from camera %d, screen mode %d ", sp_active_viewport_indexes[index], index, s_screen_mode));

	if (sp_active_viewport_indexes[index] == -1)
	{
		return NULL;
		//printf ("CAMERA WARNING: - Getting invalid camera %d when in screen mode %d (Returning default camera 0)\n",index,s_screen_mode);  
		//index = 0;
	}

	return sp_viewports[sp_active_viewport_indexes[index]]->GetCamera();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CViewportManager::sSetCamera(int index, Gfx::Camera* pCamera)
{
	Dbg_Assert( index >= 0 && index < vMAX_NUM_ACTIVE_VIEWPORTS );
	if (sp_active_viewport_indexes[index] == -1)
	{
		//printf ("CAMERA WARNING: - Setting invalid camera %d when in screen mode %d (Ignoring)\n",index,s_screen_mode);  
		return;
	}
	sp_viewports[sp_active_viewport_indexes[index]]->SetCamera(pCamera);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CViewportManager::sSetCameraAllScreenModes(int index, Gfx::Camera* pCamera)
{
	for (int mode = 0; mode < vNUM_SCREEN_MODES; mode++)
	{
		if (s_screen_mode_viewport_indexes[mode][index] >= 0)
		{
			//Dbg_Message("******************* SetCameraAllScreenMode: Setting viewport mode %d camera #%d to Camera (%x)", mode, index, pCamera);
			sp_viewports[s_screen_mode_viewport_indexes[mode][index]]->SetCamera(pCamera);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ScreenMode	CViewportManager::sGetScreenMode()
{
	return s_screen_mode;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::Camera* CViewportManager::sGetActiveCamera(int cam)
{
	CViewport *p_viewport = sGetActiveViewport(cam);
	Dbg_MsgAssert(p_viewport, ("sGetActiveCamera(): Active viewport index %d invalid for screen mode %d ", cam, s_screen_mode));

	return p_viewport->GetCamera();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::Camera* CViewportManager::sGetClosestCamera(const Mth::Vector &pos, float *distParam)
{
	int i;
	int numCams = sGetNumActiveCameras( );
	Gfx::Camera *p_camera;
	Gfx::Camera *p_closestCamera = NULL;
	float closestDist = -1.0f;
	float tempDist;

	for ( i = 0; i < numCams; i++ )
	{
		p_camera = sGetActiveCamera( i );
		if ( p_camera )
		{
			tempDist = Mth::DistanceSqr( pos, p_camera->GetPos() );
			if ( ( closestDist < 0 ) || ( tempDist < closestDist ) )
			{
				closestDist = tempDist;
				p_closestCamera = p_camera;
			}
		}
	}

	//Dbg_MsgAssert( p_closestCamera,( "No cameras or what?" ));
	if ( p_closestCamera == NULL )
	{
		return NULL;
	}

	if ( distParam )
	{
		*distParam = sqrtf(closestDist);
	}
	return p_closestCamera;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int			CViewportManager::sGetNumActiveCameras(void)
{

	return s_num_active_viewports[s_screen_mode];		// Same number of cams as there are viewports
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CViewportManager::sMarkCameraForDeletion(Gfx::Camera *pCamera)
{
	// Check all viewports in case the screen mode is changed after this
	for ( uint32 i = 0; i < vNUM_VIEWPORTS; i++ )
	{
		if ( sp_viewports[i] )
		{
			if (sp_viewports[i]->MarkCameraForDeletion(pCamera))
			{
				return true;
			}
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CViewportManager::sDeleteMarkedCameras()
{
	// Check all viewports in case the screen mode was changed
	for ( uint32 i = 0; i < vNUM_VIEWPORTS; i++ )
	{
		if ( sp_viewports[i] )
		{
			sp_viewports[i]->DeleteMarkedCamera();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void    	CViewportManager::sSetScreenMode( ScreenMode mode )
{
	// Update active viewport indexes
	sp_active_viewport_indexes = s_screen_mode_viewport_indexes[mode];

	s_screen_mode = mode;
	
	s_num_active_viewports_cached = s_num_active_viewports[s_screen_mode];
	
	 
	// Now update cameras
	for ( int i = 0; i < s_num_active_viewports[mode]; i++ )
	{
		CViewport *p_viewport = sGetActiveViewport(i);
		Dbg_MsgAssert(p_viewport, ("SetScreenMode: Couldn't find active viewport # %d for mode %d", i, mode));

		// no camera has been bound
		Gfx::Camera *p_camera = p_viewport->GetCamera();
		if ( !p_camera )
		{
			//return;
			continue;	   			// just continue, even if there is no camera set up yet.....
		}

		//Dbg_Message("******************* SetScreenMode: Setting Camera %d (%x) of mode %d", i, p_camera, mode);

		// set up proper aspect ratio for camera
		switch (mode)
		{
			case vSPLIT_V:
				// camera "window" at same height, half width as full screen camera
				//p_camera->SetFocalLength ( Script::GetFloat("Skater_Cam_Focal_Length"), vBASE_ASPECT_RATIO / 2.0f);
				p_camera->SetHFOV(Script::GetFloat("Skater_Cam_Horiz_FOV") / 2.0f);
				break;
			case vSPLIT_H:
				// camera "window" at same width, half height as full screen camera
				//p_camera->SetFocalLength ( Script::GetFloat("Skater_Cam_Focal_Length") * 2.0f, vBASE_ASPECT_RATIO * 2.0f);
				p_camera->SetHFOV(Script::GetFloat("Skater_Cam_Horiz_FOV"));
				break;
			default:
				// camera "window" at same width, height as full screen camera
				//p_camera->SetFocalLength ( Script::GetFloat("Skater_Cam_Focal_Length"), vBASE_ASPECT_RATIO);
				p_camera->SetHFOV(Script::GetFloat("Skater_Cam_Horiz_FOV"));
				break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CViewport *	CViewportManager::s_create_viewport(const Mth::Rect* rect, Gfx::Camera* cam)
{
	return s_plat_create_viewport(rect, cam);
}


// some fundamental things about the TV
//
// s_screen_aspect is the aspect ratio of the TV
// this is the physical aspect ratio, and not the pixel ratio
// thus it will be either 4:3 (4.0f/3.0f) or 16:9 (16.0f/9.0f) 
//
// s_screen_angle is the default view angle for a full screen viewport
// currently (2/8/02) it is set at 72 degrees when screen_aspect is 4:3
// when we have a wider aspect ratio, we want a wider screen angle
// however, we don't want to go too wide, or we will render too much
// 80 degrees is okay, providing us with reduced vertical coverage, but increased
// horizontal coverage (so skater seems bigger on screen, and you can see more of the level)


void				CViewportManager::sSetScreenAspect(float aspect)
{
	Dbg_MsgAssert(aspect > 0.1f && aspect <5.0f,("Unlikely value (%f) for screen aspect",aspect));
	s_screen_aspect = aspect;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float				CViewportManager::sGetScreenAspect()
{
	return s_screen_aspect;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void				CViewportManager::sSetDefaultAngle(float angle)
{
	Dbg_MsgAssert(angle > 0.01f && angle < 180.0f,("Unlikely value (%f) for screen default angle",angle));
	s_default_angle = angle;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float				CViewportManager::sGetDefaultAngle()
{
	return s_default_angle;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CViewportManager::sSetScreenAngle(float angle)
{
	if (angle == 0.0f)
	{
		angle = s_default_angle;
	}

	Dbg_MsgAssert(angle > 0.01f && angle < 180.0f,("Unlikely value (%f) for screen angle",angle));
	s_screen_angle = angle;

	s_screen_angle_factor = tanf(Mth::DegToRad(angle / 2.0f)) / tanf(Mth::DegToRad(s_default_angle / 2.0f));

	//Dbg_Message("Screen angle %f; Screen angle factor %f", s_screen_angle, s_screen_angle_factor);

	// Update cameras
	// Garrett: This isn't the right way to do it since it won't touch all the possible cameras.
	// It would be better to go through a list of all the cameras.
	for (int view_idx = 0; view_idx < sGetNumActiveViewports(); view_idx++)
	{
		CViewport *p_cur_viewport = CViewportManager::sGetActiveViewport(view_idx);
		if (p_cur_viewport)
		{
			Gfx::Camera *p_cur_camera = p_cur_viewport->GetCamera();

			if (p_cur_camera)
			{
				p_cur_camera->UpdateAdjustedHFOV();
			}
		}
	}

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float				CViewportManager::sGetScreenAngle()
{
	return s_screen_angle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float				CViewportManager::sGetScreenAngleFactor()
{
	return s_screen_angle_factor;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CViewportManager::sSet2DIn3DSpaceNoscaleDistance(float distance)
{
	s_2D_in_3D_space_noscale_distance = distance;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CViewportManager::sSet2DIn3DSpaceMaxScale(float scale)
{
	s_2D_in_3D_space_max_scale = scale;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CViewportManager::sSet2DIn3DSpaceMinScale(float scale)
{
	s_2D_in_3D_space_min_scale = scale;
}

// @script | SetScreenMode | Sets the way the screen will be split up into separate
// viewports in the game. If there is more than one viewport, and multiple skaters
// are active, then each skater will get his own viewport
// @uparm One_Camera | the mode to use - one of the following: 
// One_Camera 
// Split_Vertical 
// Split_Horizontal 
// Split_Quarters
bool ScriptSetScreenMode(Script::CScriptStructure *pParams, Script::CScript *pScript)
{


	uint32 mode;
	pParams->GetChecksum(NONAME, &mode, true);

	switch (mode)
	{
		case 0x6f70ea65: // "one_camera"
			CViewportManager::sSetScreenMode(vONE_CAM);
			break;
		case 0x9b62e5a6: // "split_vertical"
			CViewportManager::sSetScreenMode(vSPLIT_V);
			break;
		case 0xb07b580e: // "split_horizontal"
			CViewportManager::sSetScreenMode(vSPLIT_H);
			break;
		case 0xb6624a73: // "split_quarters"
			CViewportManager::sSetScreenMode(vSPLIT_QUARTERS);
			break;
		case 0x6f2d1231: // "horse1"
			CViewportManager::sSetScreenMode(vHORSE1);
			break;
		case 0xf624438b: // "horse2"
			CViewportManager::sSetScreenMode(vHORSE2);
			break;
		default:
			Dbg_MsgAssert(0,( "invalid screen mode"));
			break;
	}
	return true;
}

bool ScriptGetScreenMode(Script::CScriptStructure *pParams, Script::CScript *pScript)
{

	Script::CStruct* p_return_params;

	p_return_params = pScript->GetParams();
	switch( CViewportManager::sGetScreenMode())
	{
		case vSPLIT_V:
			p_return_params->AddChecksum( "screen_mode", Script::GenerateCRC( "split_vertical" ));
			break;
		case vSPLIT_H:
			p_return_params->AddChecksum( "screen_mode", Script::GenerateCRC( "split_horizontal" ));
			break;
		case vSPLIT_QUARTERS:
			p_return_params->AddChecksum( "screen_mode", Script::GenerateCRC( "split_quarters" ));
			break;
		case vONE_CAM:
			p_return_params->AddChecksum( "screen_mode", Script::GenerateCRC( "one_camera" ));
			break;
		case vHORSE1:
			p_return_params->AddChecksum( "screen_mode", Script::GenerateCRC( "horse1" ));
			break;
		case vHORSE2:
			p_return_params->AddChecksum( "screen_mode", Script::GenerateCRC( "horse2" ));
			break;
			
		default:
			Dbg_MsgAssert(0,( "invalid screen mode"));
			break;
	}

	return true;
}

// @script | Set2DIn3DSpaceParams | Sets the parameters for the 2D Screen Elements in 3D space
// @parmopt float | noscale_distance | 100.0 | Distance at which the 3D scale is equal to 1.0 (in inches)
// @parmopt float | max_scale | 3.0 | Maximum 3D scale
// @parmopt float | min_scale | 0.0 | Minimum 3D scale
bool ScriptSet2DIn3DSpaceParams(Script::CStruct *pParams, Script::CScript *pScript)
{
	float distance, scale;

	if (pParams->GetFloat(CRCD(0x33aa4915,"noscale_distance"), &distance))
	{
		CViewportManager::sSet2DIn3DSpaceNoscaleDistance(distance);
	}

	if (pParams->GetFloat(CRCD(0xa0a0db70,"max_scale"), &scale))
	{
		CViewportManager::sSet2DIn3DSpaceMaxScale(scale);
	}

	if (pParams->GetFloat(CRCD(0x774b6931,"min_scale"), &scale))
	{
		CViewportManager::sSet2DIn3DSpaceMinScale(scale);
	}

	return true;
}

}



================================================
FILE: Code/Gfx/NxViewMan.h
================================================
#ifndef __GFX_NX_VIEWMAN_H__
#define __GFX_NX_VIEWMAN_H__

#include 
#include 

namespace Script
{
	class CScriptStructure;
	class CScript;
}

namespace Nx
{

extern const float vBASE_ASPECT_RATIO;

enum ScreenMode
{
	vONE_CAM = 0,
	vSPLIT_V,
	vSPLIT_H,
	vSPLIT_QUARTERS,
	vHORSE1,
	vHORSE2,
	vNUM_SCREEN_MODES
};

enum ViewportType
{
	vMAIN = 0,
	vSECONDPLAYER,		// 2nd player fullscreen, for horse
	vSPLIT_V_LEFT,
	vSPLIT_V_RIGHT,
	vSPLIT_H_TOP,
	vSPLIT_H_BOTTOM,
	vSPLIT_QUARTER_UL,
	vSPLIT_QUARTER_UR,
	vSPLIT_QUARTER_LL,
	vSPLIT_QUARTER_LR,
	vNUM_VIEWPORTS
};
	
class CViewportManager
{
public:
	enum
	{
		vMAX_NUM_ACTIVE_VIEWPORTS	= 4,
	};

	static void					sInit(void);
    static void                 sCleanup(void);

	static CViewport*			sGetActiveViewport(int view = 0);
	static int					sGetActiveViewportNumber(CViewport *p_viewport);
	static int					sGetNumActiveViewports();

	static Gfx::Camera*			sGetCamera(int index = 0);
	static void					sSetCamera(int index, Gfx::Camera* pCamera);
	static void					sSetCameraAllScreenModes(int index, Gfx::Camera* pCamera);

	static Gfx::Camera*			sGetActiveCamera(int cam = 0);
	static Gfx::Camera*			sGetClosestCamera(const Mth::Vector &pos, float *distParam = NULL);
	static int					sGetNumActiveCameras();

	static bool					sMarkCameraForDeletion(Gfx::Camera *pCamera);
	static void					sDeleteMarkedCameras();

	// it would be nice to have descriptions of what these 'modes' are...
	// for now, i'm assuming screen mode 0 is where the camera is free from the skater.
	static ScreenMode			sGetScreenMode();
	static void					sSetScreenMode(ScreenMode mode);

	// some fundamental things about the TV
	static	void				sSetScreenAspect(float aspect);
	static	float				sGetScreenAspect();
	static	void				sSetDefaultAngle(float angle);
	static	float				sGetDefaultAngle();
	static	void				sSetScreenAngle(float angle);
	static	float				sGetScreenAngle();
	static	float				sGetScreenAngleFactor();

	// some parameters for 2D objects in 3D space
	static	void				sSet2DIn3DSpaceNoscaleDistance(float distance);
	static	float				sGet2DIn3DSpaceNoscaleDistance();
	static	void				sSet2DIn3DSpaceMaxScale(float scale);
	static	float				sGet2DIn3DSpaceMaxScale();
	static	void				sSet2DIn3DSpaceMinScale(float scale);
	static	float				sGet2DIn3DSpaceMinScale();

protected:
	static CViewport *			s_create_viewport(const Mth::Rect* rect, Gfx::Camera* cam = NULL);

private:
	// The platform dependent calls
	static CViewport *			s_plat_create_viewport(const Mth::Rect* rect, Gfx::Camera* cam);

	static CViewport*			sp_viewports[vNUM_VIEWPORTS];
	static const int*			sp_active_viewport_indexes;
	static int					s_num_active_viewports_cached;

	static const float			s_viewport_rects[vNUM_VIEWPORTS][4];
	static const int 			s_num_active_viewports[vNUM_SCREEN_MODES];
	static const int			s_screen_mode_viewport_indexes[vNUM_SCREEN_MODES][vMAX_NUM_ACTIVE_VIEWPORTS];

	static ScreenMode			s_screen_mode;

	static	float				s_screen_aspect;				 // aspect ratio, usually 4/3 (1.3333333) or 16/9 (1.7777777)
	static	float				s_screen_angle;					 // angle subtended by the width of the screen usually 72 degrees
	static	float				s_default_angle;				 // the default value for s_screen_angle
	static	float				s_screen_angle_factor;			 // factor to multiply all the tan(cam_angle/2) camera values by

	static	float				s_2D_in_3D_space_noscale_distance;	// z distance at which 3D scale would be 1.0
	static	float				s_2D_in_3D_space_max_scale;			// maximum 3D scale
	static	float				s_2D_in_3D_space_min_scale;			// minimum 3D scale
};



bool ScriptSetScreenMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetScreenMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSet2DIn3DSpaceParams(Script::CStruct *pParams, Script::CScript *pScript);

///////////
// Inlines
///////////

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float		CViewportManager::sGet2DIn3DSpaceNoscaleDistance()
{
	return s_2D_in_3D_space_noscale_distance;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float		CViewportManager::sGet2DIn3DSpaceMaxScale()
{
	return s_2D_in_3D_space_max_scale;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float		CViewportManager::sGet2DIn3DSpaceMinScale()
{
	return s_2D_in_3D_space_min_scale;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline int			CViewportManager::sGetNumActiveViewports()
{
//	return s_num_active_viewports[s_screen_mode];
	return s_num_active_viewports_cached;
}


}

#endif // VIEWMAN


================================================
FILE: Code/Gfx/NxViewport.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		viewport.cpp											**
**																			**
**	Created:		01/29/00	-	mjb										**
**																			**
**	Description:	Graphics viewports										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/


#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CViewport::CViewport( ) 
: m_Rect(0,0,1,1), m_flags(0), mp_camera(NULL)
{

}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

CViewport::CViewport(const Mth::Rect* rect, Gfx::Camera* cam)
: m_Rect(0,0,1,1), m_flags(0)
{
	

	if (rect)
	{	
		m_Rect.SetOriginX(rect->GetOriginX());
		m_Rect.SetOriginY(rect->GetOriginY());
		m_Rect.SetWidth(rect->GetWidth());
		m_Rect.SetHeight(rect->GetHeight());
	}
	else
	{
		m_Rect.SetOriginX(0);
		m_Rect.SetOriginY(0);
		m_Rect.SetWidth(1);
		m_Rect.SetHeight(1);
	}

	//m_pixel_ratio = parent->GetPixelRatio();

	mp_camera = cam;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CViewport::~CViewport ( void )
{	
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const float		CViewport::GetAspectRatio() const
{
	return CViewportManager::sGetScreenAspect() * m_Rect.GetWidth() / m_Rect.GetHeight();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CViewport::MarkCameraForDeletion(Gfx::Camera *pCamera)
{
	if (mp_camera == pCamera)
	{
		m_flags |= mCAMERA_MARKED_FOR_DELETION;
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CViewport::DeleteMarkedCamera()
{
	if (m_flags & mCAMERA_MARKED_FOR_DELETION)
	{
		Dbg_Assert(mp_camera);
		delete mp_camera;
		mp_camera = NULL;

		m_flags &= ~mCAMERA_MARKED_FOR_DELETION;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float			CViewport::TransformToScreenCoord(const Mth::Vector & world_pos, float & screen_pos_x, float & screen_pos_y, ZBufferValue & screen_pos_z)
{
	return plat_transform_to_screen_coord(world_pos, screen_pos_x, screen_pos_y, screen_pos_z);
}

///////////////////////////////////////////////////////////////////////////////
// Stub versions of all platform specific functions:

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float			CViewport::plat_transform_to_screen_coord(const Mth::Vector & world_pos, float & screen_pos_x, float & screen_pos_y, ZBufferValue & screen_pos_z)
{
	printf ("STUB: PlatTransformToScreenCoord\n");
	return -1.0f;
}

} // namespace Gfx




================================================
FILE: Code/Gfx/NxViewport.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics  (GFX)											**
**																			**
**	File name:		gfx/viewport.h											**
**																			**
**	Created: 		01/29/00	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __GFX_VIEWPORT_H
#define __GFX_VIEWPORT_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#include 
#include 
			  
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Nx
{

// Since a ZBuffer value is a platform-specific type, we need "void" type for it.
// Since we do have to give it a size, the type has been assigned to a uint32.
// It should not be treated as a uint32 above the p-line, though.
typedef uint32 ZBufferValue;

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class  CViewport
{
	
	public :
						CViewport();
						CViewport(const Mth::Rect* Rect, Gfx::Camera* cam = NULL);
	virtual				~CViewport();

	Gfx::Camera *		GetCamera();
	void				SetCamera(Gfx::Camera* cam);

	const Mth::Rect &	GetRect() const;

	const		float	GetOriginX() const;
	const		float	GetOriginY() const;
	const		float	GetWidth() const;
	const		float	GetHeight() const;

	const		float	GetAspectRatio() const;

	// These functions are used to prevent the camera from being deleted prematurely
				bool	MarkCameraForDeletion(Gfx::Camera *pCamera);
				void	DeleteMarkedCamera();

				float	TransformToScreenCoord(const Mth::Vector & world_pos, float & screen_pos_x, float & screen_pos_y, ZBufferValue & screen_pos_z);

protected:	
	// Internal flags
	enum
	{
		mCAMERA_MARKED_FOR_DELETION		= 0x00000001,
	};

	
	Mth::Rect			m_Rect;
	uint32				m_flags;

	Obj::CSmtPtr mp_camera;			// Smart pointer to camera   
//	Gfx::Camera *		mp_camera;

private :	
	virtual float		plat_transform_to_screen_coord(const Mth::Vector & world_pos, float & screen_pos_x, float & screen_pos_y, ZBufferValue & screen_pos_z);
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Mth::Rect & CViewport::GetRect() const
{
	return m_Rect;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const float	CViewport::GetOriginX ( void ) const
{
   	

	return m_Rect.GetOriginX();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const float	CViewport::GetOriginY ( void ) const
{
   	

	return m_Rect.GetOriginY();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const float	CViewport::GetWidth ( void ) const
{
   	

	return m_Rect.GetWidth();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const float	CViewport::GetHeight ( void ) const
{
   	

	return m_Rect.GetHeight();
}
	
#if 0
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
			
inline const float		CViewport::GetPixelRatio ( void ) const
{
   	

	return 	m_pixel_ratio;
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Returns a pointer to the viewport's camera
// since this uses a smart pointer, it will be NULL
// if the camera has been deleted
			
inline Gfx::Camera *	CViewport::GetCamera ( void )
{
	return 	mp_camera.Convert();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
			
inline void 			CViewport::SetCamera ( Gfx::Camera* cam )
{
	DeleteMarkedCamera();			// In case there is an old camera
	mp_camera = cam;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
			
} // namespace Nx

#endif	// __GFX_VIEWPORT_H


================================================
FILE: Code/Gfx/NxWin2D.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxWin2D.cpp

#include "gfx/NxWin2D.h"

namespace	Nx
{
// These functions are the platform independent part of the interface to 
// the platform specific code
// parameter checking can go here....
// although we might just want to have these functions inline, or not have them at all?
					 
///////////////////////////////////////////////////////////////////////////////
// CWindow2D

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CWindow2D::CWindow2D(int x, int y, int width, int height) : 
	m_xpos(x),
	m_ypos(y),
	m_width(width),
	m_height(height),
	mp_next(NULL)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CWindow2D::~CWindow2D() 
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CWindow2D::SetPos(int x, int y)
{
	m_xpos = x;
	m_ypos = y;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CWindow2D::SetSize(int width, int height)
{
	m_width = width;
	m_height = height;

	plat_update_engine();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CWindow2D::plat_update_engine()
{
	printf ("STUB: PlatUpdateEngine\n");
}

///////////////////////////////////////////////////////////////////////////////
// CWindow2DManager

CWindow2D *			CWindow2DManager::sp_window_list = NULL;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CWindow2DManager::sAllocWindow2DPool()
{
	s_plat_alloc_window2d_pool();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CWindow2D *		CWindow2DManager::sGetWindowInstance(int x, int y, int width, int height)
{
	CWindow2D *p_window = sp_window_list;

	if (p_window)
	{
		sp_window_list = p_window->mp_next;
	} else {
		Dbg_MsgAssert(0, ("Out of CWindow2D Instances"));
	}

	// Initialize
	p_window->SetPos(x, y);
	p_window->SetSize(width, height);

	return p_window;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CWindow2DManager::sFreeWindowInstance(CWindow2D *p_window)
{
	p_window->mp_next = sp_window_list;
	sp_window_list = p_window;
}

}




================================================
FILE: Code/Gfx/NxWin2D.h
================================================
///////////////////////////////////////////////////////////////////////////////
// NxWin2D.h



#ifndef	__GFX_NXWIN2D_H__
#define	__GFX_NXWIN2D_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __CORE_MATH_RECT_H
#include 
#endif

namespace Nx
{

// Forward declarations
class CWindow2DManager;

//////////////////////////////////////////////////////////////////////////////
// Holds a string of text that needs to be displayed this frame
class CWindow2D
{
public:
							CWindow2D(int x = 0, int y = 0, int width = 640, int height = 448);
							CWindow2D(const Mth::Rect & win_rect);
	virtual					~CWindow2D();

	// Position of screen.
	int						GetXPos() const;
	int						GetYPos() const;
	void					SetPos(int x, int y);

	// Size of screen.
	int						GetWidth() const;
	int						GetHeight() const;
	void					SetSize(int width, int height);

protected:
	int						m_xpos;
	int						m_ypos;
	int						m_width;
	int						m_height;

	// For free list in CWindow2DManager
	CWindow2D *				mp_next;

private:
	//
	virtual void			plat_update_engine();

	friend CWindow2DManager;

};

//////////////////////////////////////////////////////////////////////////////
// Static class for memory management of CWindow2Ds
class CWindow2DManager
{
public:
	//
	static void				sAllocWindow2DPool();
	static CWindow2D *		sGetWindowInstance(int x = 0, int y = 0, int width = 640, int height = 448);
	static void				sFreeWindowInstance(CWindow2D *p_window);

private:
	// Constants
	enum {
		vMAX_WINDOW_INSTANCES = 10
	};

	// Because it is static, it is declared here, but defined in p_NxWin2D.cpp
	static void				s_plat_alloc_window2d_pool();

	// Array of Text requests
	static CWindow2D *	   	sp_window_list;
};

/////////////////////////////////////////////////////////
// CText inline function
inline int					CWindow2D::GetXPos() const
{
	return m_xpos;
}

inline int					CWindow2D::GetYPos() const
{
	return m_ypos;
}

inline int					CWindow2D::GetWidth() const
{
	return m_width;
}

inline int					CWindow2D::GetHeight() const
{
	return m_height;
}

}

#endif // 



================================================
FILE: Code/Gfx/Pose.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       Pose.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/13/2002
//****************************************************************************

#ifndef __GFX_POSE_H
#define __GFX_POSE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Gfx
{

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/
    
	const int vMAX_BONES = 64;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class CPose
{
public:
	Mth::Quat		m_rotations[vMAX_BONES];
	Mth::Vector		m_translations[vMAX_BONES];
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Gfx

#endif	// __GFX_POSE_H



================================================
FILE: Code/Gfx/Skeleton.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       skeleton.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/15/2001
//****************************************************************************

// TODO:
// Assert that the anim sequence contains the same number of bones as the skeleton?

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 

#ifdef __PLAT_NGC__
#include "dolphin.h"
extern bool g_in_cutscene;
#endif
/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Gfx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

#define nxBONEFLAGS_ROTATE			(1<<31)
#define nxBONEFLAGS_NOANIM			(1<<30)
#define nxBONEFLAGS_SCALELOCAL		(1<<29)
#define nxBONEFLAGS_SCALENONLOCAL	(1<<28)

#define MAX_LOD_DISTANCE			3.4e+38f;

class CBone
{
public:
	CBone();

public:
	uint32					m_name;
	Mth::Matrix*			mp_parentMatrix;
	Mth::Matrix*			mp_flippedMatrix;

// GJ:  The neutral pose has been moved to the skeleton data,
// since it can be shared by all the instances of this skeleton
//	Mth::Matrix				m_invertedNeutralMatrix;
//	Mth::Matrix*			mp_invertedNeutralParentMatrix;
	
	uint32					m_flags;
// GJ:  The parent name doesn't seem to be needed, and
// can be accessed from the skeleton data if necessary...
//	uint32					m_parentName;
	int						m_flipIndex;
	Mth::Vector				m_scale;
};

// GJ:  Removed reference to skateboard, because
// there shouldn't be anything skate-specific in here!
//#define nxSKELETONFLAGS_ROTATESKATEBOARD	(1<<30)

// This flag has been moved to CAnimationComponent,
// and will eventually be removed...
#define nxSKELETONFLAGS_FLIPPED				(1<<29)

// This flag was used as a kludge on THPS4 to get
// female models to scale properly (because supposedly-
// equivalent vertices in the female model were weighted
// to different bones in the male model)
#define nxSKELETONFLAGS_FEMALESKELETON		(1<<28)

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBone* CSkeleton::get_bone_by_id( uint32 boneId )
{
	for ( int i = 0; i < m_numBones; i++ )
	{
		if ( boneId == mp_bones[i].m_name )
			return &mp_bones[i];
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBone::CBone()
{
	m_name = 0;
	mp_parentMatrix = NULL;
// GJ:  Parent name has been moved to skeleton data
//	m_parentName = 0;
	mp_flippedMatrix = NULL;
// GJ:  Neutral pose has been moved to skeleton data
//	m_invertedNeutralMatrix.Ident();
//	mp_invertedNeutralParentMatrix = NULL;
	m_flipIndex = -1;
	m_flags = 0;
	m_scale = Mth::Vector( 1.0f, 1.0f, 1.0f );
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkeleton::CSkeleton( CSkeletonData* pSkeletonData )
{
    Dbg_Assert( pSkeletonData );

	// clear out flags completely
	m_flags = 0;
	
	if ( pSkeletonData->m_flags & 0x1 )
	{
		m_flags |= nxSKELETONFLAGS_FEMALESKELETON;
	}

#ifdef __PLAT_NGC__
	int size = sizeof( CBone ) * pSkeletonData->GetNumBones();
	int mem_available;
	bool need_to_pop = false;
	if ( g_in_cutscene )
	{
		Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
		mem_available = Mem::Manager::sHandle().Available();
		if ( size < ( mem_available - ( 40 * 1024 ) ) )
		{
			need_to_pop = true;
		}
		else
		{
			Mem::Manager::sHandle().PopContext();
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
			mem_available = Mem::Manager::sHandle().Available();
			if ( size < ( mem_available - ( 5 * 1024 ) ) )
			{
				need_to_pop = true;
			}
			else
			{
				Mem::Manager::sHandle().PopContext();
				Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ScriptHeap() );
				mem_available = Mem::Manager::sHandle().Available();
				if ( size < ( mem_available - ( 40 * 1024 ) ) )
				{
					need_to_pop = true;
				}
				else
				{
					Mem::Manager::sHandle().PopContext();
				}
			}
		}
	}
#endif	// __PLAT_NGC__

	mp_bones = new CBone[pSkeletonData->GetNumBones()];

#ifdef __PLAT_NGC__
	if ( need_to_pop )
	{
		Mem::Manager::sHandle().PopContext();
	}
#endif	// __PLAT_NGC__

#ifdef __PLAT_NGC__
	size = sizeof( Mth::Matrix ) * pSkeletonData->GetNumBones();
	need_to_pop = false;
	if ( g_in_cutscene )
	{
		Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
		mem_available = Mem::Manager::sHandle().Available();
		if ( size < ( mem_available - ( 40 * 1024 ) ) )
		{
			need_to_pop = true;
		}
		else
		{
			Mem::Manager::sHandle().PopContext();
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
			mem_available = Mem::Manager::sHandle().Available();
			if ( size < ( mem_available - ( 5 * 1024 ) ) )
			{
				need_to_pop = true;
			}
			else
			{
				Mem::Manager::sHandle().PopContext();
				Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ScriptHeap() );
				mem_available = Mem::Manager::sHandle().Available();
				if ( size < ( mem_available - ( 40 * 1024 ) ) )
				{
					need_to_pop = true;
				}
				else
				{
					Mem::Manager::sHandle().PopContext();
				}
			}
		}
	}
#endif	// __PLAT_NGC__

	mp_matrices = new Mth::Matrix[pSkeletonData->GetNumBones()];
	
#ifdef __PLAT_NGC__
	if ( need_to_pop )
	{
		Mem::Manager::sHandle().PopContext();
	}
#endif	// __PLAT_NGC__

	// clear out all the matrices to the identity
	Mth::Matrix* p_currentMatrix = mp_matrices;
	for ( int i = 0; i < pSkeletonData->GetNumBones(); i++ )
	{
		p_currentMatrix->Ident();
		p_currentMatrix++;
	}

	m_numBones = pSkeletonData->GetNumBones();
	
    initialize_bone_names( pSkeletonData );
    initialize_hierarchy( pSkeletonData );
    initialize_flip_matrices( pSkeletonData );

	mp_skeletonData		= pSkeletonData;

	// Set the maxmimum possible bone skip LOD level.
	SetMaxBoneSkipLOD( CSkeletonData::BONE_SKIP_LOD_BITS - 1 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkeleton::~CSkeleton()
{
	delete[] mp_bones;
	delete[] mp_matrices;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CSkeleton::GetBoneIndexById( uint32 boneId )
{
	for ( int i = 0; i < m_numBones; i++ )
	{
		if ( boneId == mp_bones[i].m_name )
			return i;
	}

	return -1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CSkeleton::GetFlipIndex( int i )
{
	CBone* pBone = &mp_bones[i];
	return pBone->m_flipIndex;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkeleton::initialize_flip_matrices( CSkeletonData* pSkeletonData )
{
    Dbg_Assert( pSkeletonData );
    Dbg_Assert( pSkeletonData->GetNumBones() == m_numBones );
	
	for ( int i = 0; i < pSkeletonData->GetNumBones(); i++ )
	{
        // if it's got a flip
        if ( pSkeletonData->GetFlipName(i) )
        {
            int index = pSkeletonData->GetIndex( pSkeletonData->GetFlipName(i) );
            mp_bones[i].mp_flippedMatrix = mp_matrices + index;
			mp_bones[i].m_flipIndex = index;

			// and vice versa...
			mp_bones[index].m_flipIndex = i;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkeleton::initialize_hierarchy( CSkeletonData* pSkeletonData )
{
	// the parent of the root is itself.
    
    Dbg_Assert( pSkeletonData );
    Dbg_Assert( pSkeletonData->GetNumBones() == m_numBones );

    for ( int i = 0; i < pSkeletonData->GetNumBones(); i++ )
    {
        int parentIndex;
         
        if ( pSkeletonData->GetParentName(i) == 0 )
        {
            parentIndex = 0;
        }
        else
        {
            parentIndex = pSkeletonData->GetParentIndex( i );
        }

		CBone* p_currentBone = &mp_bones[i];

//		Dbg_Message( "Parent of %d is %d\n", i, parentIndex );

// GJ:  Parent name has been moved to skeleton data
//		p_currentBone->m_parentName = pSkeletonData->GetParentName(i);

		// Assign parentage here...
		p_currentBone->mp_parentMatrix = mp_matrices + parentIndex;

// GJ:  Neutral pose has been moved to skeleton data
//		CBone* p_parentBone = &mp_bones[parentIndex];
//		p_currentBone->mp_invertedNeutralParentMatrix = &p_parentBone->m_invertedNeutralMatrix;

		p_currentBone->m_flags = 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkeleton::initialize_bone_names( CSkeletonData* pSkeletonData )
{
    Dbg_Assert( pSkeletonData );
    Dbg_Assert( pSkeletonData->GetNumBones() == m_numBones );

    for ( int i = 0; i < pSkeletonData->GetNumBones(); i++ )
    {
		mp_bones[i].m_name = pSkeletonData->GetBoneName( i );
   	}
}

#if 0
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Mth::Matrix CSkeleton::GetNeutralMatrix( int boneIndex )
{
	Dbg_MsgAssert( 0, ( "This function has been deprecated." ) );

	Dbg_Assert( boneIndex >= 0 && boneIndex < m_numBones );

	Mth::Matrix theReturnMatrix;

	theReturnMatrix = mp_bones[boneIndex].m_invertedNeutralMatrix;
	theReturnMatrix.InvertUniform();

	return theReturnMatrix;
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Mth::Matrix* CSkeleton::GetMatrices()
{
	return mp_matrices;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CSkeleton::GetNumBones() const
{
	return m_numBones;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkeleton::Display( Mth::Matrix* pRootMatrix, float r, float g, float b )
{
	Mth::Matrix* p_inverseNeutralPoseMatrices = mp_skeletonData->GetInverseNeutralPoseMatrices();

	for ( int i = 0; i < m_numBones; i++ )
	{
		CBone* p_currentBone = &mp_bones[i];
    
		Mth::Matrix tempMatrix0, tempMatrix1;
					
		Mth::Matrix boneMatrix;
		boneMatrix = *( mp_matrices + i );
		tempMatrix0 = *( p_inverseNeutralPoseMatrices + i );
		tempMatrix0.InvertUniform();
		tempMatrix0 = tempMatrix0 * boneMatrix;
		tempMatrix0 *= (*pRootMatrix);
		
		boneMatrix = *p_currentBone->mp_parentMatrix;
		if ( i == 0 )
		{
			// the root has no parent
			tempMatrix1.Ident();
		}
		else
		{
			tempMatrix1 = *( p_inverseNeutralPoseMatrices + mp_skeletonData->GetParentIndex(i) );
		}
		tempMatrix1.InvertUniform();
		tempMatrix1 = tempMatrix1 * boneMatrix; 
		tempMatrix1 *= (*pRootMatrix);
		
		AddDebugBone( tempMatrix0.GetPos(), tempMatrix1.GetPos(), r, g, b );
   }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkeleton::SetBoneActive( uint32 boneId, bool active )
{
	CBone* pBone = get_bone_by_id( boneId );

	if ( pBone )
	{
		pBone->m_flags &= ~nxBONEFLAGS_NOANIM;
		pBone->m_flags |= ( active ? 0 : nxBONEFLAGS_NOANIM );
	}

	return pBone;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkeleton::ApplyBoneScale( Script::CStruct* pBodyShapeStructure )
{
	// TODO:  CSkeleton shouldn't know anything about
	// Script::CStruct*...  this should be moved
	// out to a higher level...

	if ( !pBodyShapeStructure ) 
	{
		return false;
	}

	int scaleFromOrigin = 0;
	pBodyShapeStructure->GetInteger( "scaleFromOrigin", &scaleFromOrigin, Script::NO_ASSERT );
	bool localScale = !scaleFromOrigin;

	Mth::Vector theBoneScaleVector;
	
	for ( int i = 0; i < m_numBones; i++ )
	{
		if ( pBodyShapeStructure->GetVector(mp_bones[i].m_name,&theBoneScaleVector,Script::NO_ASSERT) )
		{
			uint32 name = mp_bones[i].m_name;

			if ( m_flags & nxSKELETONFLAGS_FEMALESKELETON )
			{
				if ( name == CRCD(0x580c0963, "head") )
				{
					name = CRCD(0xdfee29ac,"neck");
				}		   
			}

			// GJ PATCH:  test for Nolan
			Script::CArray* pNolanScalingTest = Script::GetArray( "nonlocal_bones", Script::NO_ASSERT );
			if ( pNolanScalingTest )
			{
				for ( uint32 i = 0; i < pNolanScalingTest->GetSize(); i++ )
				{
					if ( pNolanScalingTest->GetChecksum(i) == name )
					{
						localScale = false;
					}
				}
			}

			Mth::Vector vec = Mth::Vector( 1.0f, 1.0f, 1.0f, 1.0f );
			if ( GetBoneScale( name, &vec ) )
			{
				vec[X] *= theBoneScaleVector[X];
				vec[Y] *= theBoneScaleVector[Y];
				vec[Z] *= theBoneScaleVector[Z];
				vec[W] = 1.0f;

				bool success = SetBoneScale(name, vec, localScale);
				if ( !success )
				{
					Dbg_MsgAssert( 0, ( "Couldn't apply bone scale %s", Script::FindChecksumName(mp_bones[i].m_name) ) );
				}
			}
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkeleton::ResetScale( void )
{
	for ( int i = 0; i < m_numBones; i++ )
	{
		CBone* pBone = &mp_bones[i];
		pBone->m_flags &= ~(nxBONEFLAGS_SCALELOCAL | nxBONEFLAGS_SCALENONLOCAL);
		pBone->m_scale = Mth::Vector( 1.0f, 1.0f, 1.0f, 1.0f );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkeleton::GetBoneScale( uint32 boneId, Mth::Vector* pBoneScaleVector )
{
	CBone* pBone = get_bone_by_id( boneId );

	if ( pBone )
	{
		Dbg_Assert( pBoneScaleVector );
		*pBoneScaleVector = pBone->m_scale;
	}
	else
	{
		Dbg_Message( "Couldn't find bone to scale %s", Script::FindChecksumName(boneId) );
	}

	return pBone;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkeleton::SetBoneScale( uint32 boneId, const Mth::Vector& theBoneScale, bool isLocalScale )
{
	CBone* pBone = get_bone_by_id( boneId );

	if ( pBone )
	{
		pBone->m_scale = theBoneScale;

		bool isIdentityScale = ( theBoneScale[X] == 1.0f && theBoneScale[Y] == 1.0f && theBoneScale[Z] == 1.0f );
		pBone->m_flags &= ~(nxBONEFLAGS_SCALELOCAL | nxBONEFLAGS_SCALENONLOCAL);
		if ( !isIdentityScale )
		{
			pBone->m_flags |= ( isLocalScale ? nxBONEFLAGS_SCALELOCAL : nxBONEFLAGS_SCALENONLOCAL );
		}
	}
	else
	{
		Dbg_Message( "Couldn't find bone to scale %s", Script::FindChecksumName(boneId) );
	}

	return pBone;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkeleton::CopyBoneScale( Gfx::CSkeleton* pSourceSkeleton )
{
	CSkeletonData* pSkeletonData = pSourceSkeleton->GetSkeletonData();
	for ( int i = 0; i < pSkeletonData->GetNumBones(); i++ )
	{
		uint32 boneName = pSkeletonData->GetBoneName( i );

		Mth::Vector sourceScale;

		// if the bone exists in the destination skeleton,
		// and the source skeleton, then copy the scale
		if ( get_bone_by_id( boneName )
			 && pSourceSkeleton->GetBoneScale( boneName, &sourceScale ) )
		{
			bool isLocalScale = true;

			#if 0
			// GJ:  This code handles the shoulder bones differently
			// so that the female skaters aren't so broad-shouldered.
			// However, it ends up screwing stuff up during cutscenes,
			// because the female's hands will not be aligned to the
			// items that she might be holding.  The only way I can
			// think of around this (right now) is to create female-
			// specific versions of the cutscenes, but it's too late 
			// for that now.
			Script::CArray* pShoulderScalingArray = Script::GetArray( CRCD(0x20d9ac2f,"nonlocal_bones"), Script::NO_ASSERT );
			if ( pShoulderScalingArray )
			{
				for ( uint32 i = 0; i < pShoulderScalingArray->GetSize(); i++ )
				{
					if ( pShoulderScalingArray->GetChecksum(i) == boneName )
					{
						isLocalScale = false;
					}
				}
			}
			#endif

			SetBoneScale( boneName, sourceScale, isLocalScale );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void sQuatVecToMatrix( Mth::Quat* pQ, Mth::Vector* pT, Mth::Matrix* pMatrix, bool rotate, bool flip )
{
	Dbg_Assert( pQ );
   	Dbg_Assert( pT );
	Dbg_Assert( pMatrix );
	
#ifdef __PLAT_NGPS__
	Dbg_MsgAssert( ( (uint32)&(pQ->GetVector()) - (uint32)pQ )==0, ("Quat::quat is no longer offset 0 bytes from the start of the Quat, need to modify the VU code in sQuatVecToMatrix"));
	
	asm __volatile__("
	
	lw	$8,(%3)	# Put 'rotate' flag in t0
	lw	$9,(%4)	# Put 'flip' flag in t1
	
	lqc2    vf4,0x00(%0)			# Load pQ into vf4
	lqc2    vf7,0x00(%1)			# Load pT into vf7
	
	#	square[X] = (*pQ)[X] * (*pQ)[X];
	#	square[Y] = (*pQ)[Y] * (*pQ)[Y];
	#	square[Z] = (*pQ)[Z] * (*pQ)[Z];
	vmul.xyz vf5,vf4,vf4			# vf5=square
									
	#	cross[X] = (*pQ)[Y] * (*pQ)[Z];
	#	cross[Y] = (*pQ)[Z] * (*pQ)[X];
	#	cross[Z] = (*pQ)[X] * (*pQ)[Y];
	vopmula.xyz     ACC,vf4,vf4		# ACC=cross

	#	wimag[X] = -(*pQ)[W] * (*pQ)[X];
	#	wimag[Y] = -(*pQ)[W] * (*pQ)[Y];
	#	wimag[Z] = -(*pQ)[W] * (*pQ)[Z];
	vmsubw.xyzw vf8,vf4,vf4w	# vf8=cross+wimag
	vmaddw.xyzw vf9,vf4,vf4w	# vf9=cross-wimag

	
	vmulax.w	ACC,vf0,vf5x	# ACCw=pQx squared
	vmadday.w	ACC,vf0,vf5y	# ACCw=pQx squared + pQy squared
	vmaddz.w	vf1,vf0,vf5z	# vf1w=pQx squared + pQy squared + pQz squared
	
	# 3 vnops here ... use 2 of them with the following.
	vadd.xyz	vf8,vf8,vf8		# vf8=2*(cross+wimag)
	vadd.xyz	vf9,vf9,vf9		# vf9=2*(cross-wimag)
	
	vsubw.xyz	vf1,vf5,vf1w	# vf1=(-y2-z2,-x2-z2,-x2-y2)
	# 3 vnops here that could be used for something
	vaddaw.xyz	ACC,vf1,vf0w	# ACC=(1-y2-z2,1-x2-z2,1-x2-y2)
	vmaddw.xyz	vf1,vf1,vf0w	# vf1=(1-2(y2+z2),1-2(x2+z2),1-2(x2+y2))


	beqz	$8,norotate		# if (rotate)
	nop

	beqz	$9,noflip		# if (flip)
	nop

	# (*pMatrix)[Mth::RIGHT][X] = -(1 - 2 * (square[Y] + square[Z]));
	# (*pMatrix)[Mth::RIGHT][Y] = 2 * (cross[Z] + wimag[Z]);
	# (*pMatrix)[Mth::RIGHT][Z] = 2 * (cross[Y] - wimag[Y]);
	# (*pMatrix)[Mth::RIGHT][W] = 0.0f;
	vsubax.x	ACC,vf0,vf1x
	vaddaz.y	ACC,vf0,vf8z
	vadday.z 	ACC,vf0,vf9y
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::RIGHT]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x00(%2)

	# (*pMatrix)[Mth::UP][X] = 2 * (cross[Z] - wimag[Z]);
	# (*pMatrix)[Mth::UP][Y] = -(1 - 2 * (square[X] + square[Z]));
	# (*pMatrix)[Mth::UP][Z] = -(2 * (cross[X] + wimag[X]));
	# (*pMatrix)[Mth::UP][W] = 0.0f;
	vaddaz.x	ACC,vf0,vf9z
	vsubay.y	ACC,vf0,vf1y
	vsubax.z	ACC,vf0,vf8x
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::UP]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x10(%2)

	# (*pMatrix)[Mth::AT][X] = -(2 * (cross[Y] + wimag[Y]));
	# (*pMatrix)[Mth::AT][Y] = 2 * (cross[X] - wimag[X]);
	# (*pMatrix)[Mth::AT][Z] = 1 - 2 * (square[X] + square[Y]);
	# (*pMatrix)[Mth::AT][W] = 0.0f;
	vsubay.x	ACC,vf0,vf8y
	vaddax.y	ACC,vf0,vf9x
	vaddaz.z	ACC,vf0,vf1z
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::AT]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x20(%2)

	# (*pMatrix)[Mth::POS][X] = -pT->GetX();
	# (*pMatrix)[Mth::POS][Y] = pT->GetY();
	# (*pMatrix)[Mth::POS][Z] = pT->GetZ();
	# (*pMatrix)[Mth::POS][W] = 1.0f;
	vsubax.x	ACC,vf0,vf7x
	vadday.y	ACC,vf0,vf7y
	vaddaz.z	ACC,vf0,vf7z
	vaddax.w	ACC,vf0,vf0x
	# Save out (*pMatrix)[Mth::POS]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x30(%2)
	
	j	overnorotate		
	nop
noflip:							# else  (if (flip))

	# (*pMatrix)[Mth::RIGHT][X] = -(1 - 2 * (square[Y] + square[Z]));
	# (*pMatrix)[Mth::RIGHT][Y] = -(2 * (cross[Z] + wimag[Z]));
	# (*pMatrix)[Mth::RIGHT][Z] = -(2 * (cross[Y] - wimag[Y]));
	# (*pMatrix)[Mth::RIGHT][W] = 0.0f;
	vsubax.x	ACC,vf0,vf1x
	vsubaz.y	ACC,vf0,vf8z
	vsubay.z 	ACC,vf0,vf9y
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::RIGHT]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x00(%2)

	# (*pMatrix)[Mth::UP][X] = -(2 * (cross[Z] - wimag[Z]));
	# (*pMatrix)[Mth::UP][Y] = -(1 - 2 * (square[X] + square[Z]));
	# (*pMatrix)[Mth::UP][Z] = -(2 * (cross[X] + wimag[X]));
	# (*pMatrix)[Mth::UP][W] = 0.0f;
	vsubaz.x	ACC,vf0,vf9z
	vsubay.y	ACC,vf0,vf1y
	vsubax.z	ACC,vf0,vf8x
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::UP]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x10(%2)

	# (*pMatrix)[Mth::AT][X] = 2 * (cross[Y] + wimag[Y]);
	# (*pMatrix)[Mth::AT][Y] = 2 * (cross[X] - wimag[X]);
	# (*pMatrix)[Mth::AT][Z] = 1 - 2 * (square[X] + square[Y]);
	# (*pMatrix)[Mth::AT][W] = 0.0f;
	vadday.x	ACC,vf0,vf8y
	vaddax.y	ACC,vf0,vf9x
	vaddaz.z	ACC,vf0,vf1z
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::AT]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x20(%2)

	# (*pMatrix)[Mth::POS][X] = pT->GetX();
	# (*pMatrix)[Mth::POS][Y] = pT->GetY();
	# (*pMatrix)[Mth::POS][Z] = pT->GetZ();
	# (*pMatrix)[Mth::POS][W] = 1.0f;
	vaddax.x	ACC,vf0,vf7x
	vadday.y	ACC,vf0,vf7y
	vaddaz.z	ACC,vf0,vf7z
	vaddax.w	ACC,vf0,vf0x
	# Save out (*pMatrix)[Mth::POS]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x30(%2)

	j	overnorotate		
	nop						# else (if (rotate))
norotate:

	beqz	$9,noflip2		# if (flip)
	nop

	# (*pMatrix)[Mth::RIGHT][X] = 1 - 2 * (square[Y] + square[Z]);
	# (*pMatrix)[Mth::RIGHT][Y] = -(2 * (cross[Z] + wimag[Z]));
	# (*pMatrix)[Mth::RIGHT][Z] = -(2 * (cross[Y] - wimag[Y]));
	# (*pMatrix)[Mth::RIGHT][W] = 0.0f;
	vaddax.x	ACC,vf0,vf1x
	vsubaz.y	ACC,vf0,vf8z
	vsubay.z 	ACC,vf0,vf9y
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::RIGHT]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x00(%2)

	# (*pMatrix)[Mth::UP][X] = -(2 * (cross[Z] - wimag[Z]));
	# (*pMatrix)[Mth::UP][Y] = 1 - 2 * (square[X] + square[Z]);
	# (*pMatrix)[Mth::UP][Z] = 2 * (cross[X] + wimag[X]);
	# (*pMatrix)[Mth::UP][W] = 0.0f;
	vsubaz.x	ACC,vf0,vf9z
	vadday.y	ACC,vf0,vf1y
	vaddax.z	ACC,vf0,vf8x
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::UP]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x10(%2)

	# (*pMatrix)[Mth::AT][X] = -(2 * (cross[Y] + wimag[Y]));
	# (*pMatrix)[Mth::AT][Y] = 2 * (cross[X] - wimag[X]);
	# (*pMatrix)[Mth::AT][Z] = 1 - 2 * (square[X] + square[Y]);
	# (*pMatrix)[Mth::AT][W] = 0.0f;
	vsubay.x	ACC,vf0,vf8y
	vaddax.y	ACC,vf0,vf9x
	vaddaz.z	ACC,vf0,vf1z
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::AT]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x20(%2)

	# (*pMatrix)[Mth::POS][X] = -pT->GetX();
	# (*pMatrix)[Mth::POS][Y] = pT->GetY();
	# (*pMatrix)[Mth::POS][Z] = pT->GetZ();
	# (*pMatrix)[Mth::POS][W] = 1.0f;
	vsubax.x	ACC,vf0,vf7x
	vadday.y	ACC,vf0,vf7y
	vaddaz.z	ACC,vf0,vf7z
	vaddax.w	ACC,vf0,vf0x
	# Save out (*pMatrix)[Mth::POS]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x30(%2)

	j	overnoflip2			# else  (if (flip))
	nop
noflip2:

	# (*pMatrix)[Mth::RIGHT][X] = 1 - 2 * (square[Y] + square[Z]);
	# (*pMatrix)[Mth::RIGHT][Y] = 2 * (cross[Z] + wimag[Z]);
	# (*pMatrix)[Mth::RIGHT][Z] = 2 * (cross[Y] - wimag[Y]);
	# (*pMatrix)[Mth::RIGHT][W] = 0.0f;
	vaddax.x	ACC,vf0,vf1x
	vaddaz.y	ACC,vf0,vf8z
	vadday.z 	ACC,vf0,vf9y
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::RIGHT]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x00(%2)

	# (*pMatrix)[Mth::UP][X] = 2 * (cross[Z] - wimag[Z]);
	# (*pMatrix)[Mth::UP][Y] = 1 - 2 * (square[X] + square[Z]);
	# (*pMatrix)[Mth::UP][Z] = 2 * (cross[X] + wimag[X]);
	# (*pMatrix)[Mth::UP][W] = 0.0f;
	vaddaz.x	ACC,vf0,vf9z
	vadday.y	ACC,vf0,vf1y
	vaddax.z	ACC,vf0,vf8x
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::UP]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x10(%2)

	# (*pMatrix)[Mth::AT][X] = 2 * (cross[Y] + wimag[Y]);
	# (*pMatrix)[Mth::AT][Y] = 2 * (cross[X] - wimag[X]);
	# (*pMatrix)[Mth::AT][Z] = 1 - 2 * (square[X] + square[Y]);
	# (*pMatrix)[Mth::AT][W] = 0.0f;
	vadday.x	ACC,vf0,vf8y
	vaddax.y	ACC,vf0,vf9x
	vaddaz.z	ACC,vf0,vf1z
	vsubaw.w	ACC,vf0,vf0w
	# Save out (*pMatrix)[Mth::AT]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x20(%2)

	# (*pMatrix)[Mth::POS][X] = pT->GetX();
	# (*pMatrix)[Mth::POS][Y] = pT->GetY();
	# (*pMatrix)[Mth::POS][Z] = pT->GetZ();
	# (*pMatrix)[Mth::POS][W] = 1.0f;
	vaddax.x	ACC,vf0,vf7x
	vadday.y	ACC,vf0,vf7y
	vaddaz.z	ACC,vf0,vf7z
	vaddax.w	ACC,vf0,vf0x
	# Save out (*pMatrix)[Mth::POS]
	vmaddx.xyzw	vf11,vf0,vf0x
	sqc2		vf11,0x30(%2)

overnoflip2:
	
overnorotate:

	": : "r" (pQ), "r" (pT), "r" (pMatrix), "r" (&rotate), "r" (&flip) : "$8","$9");

#else
#ifdef __PLAT_NGC__
	MTXQuat( (Mtx)pMatrix, (QuaternionPtr)pQ );

	(*pMatrix)[Mth::POS][X] = pT->GetX();
	(*pMatrix)[Mth::POS][Y] = pT->GetY();
	(*pMatrix)[Mth::POS][Z] = pT->GetZ();
	(*pMatrix)[Mth::POS][W] = 1.0f;
#else
	// Ye olde slowe code
	Mth::Vector		square;
	Mth::Vector		cross;
	Mth::Vector		wimag;

	square[X] = (*pQ)[X] * (*pQ)[X];
	square[Y] = (*pQ)[Y] * (*pQ)[Y];
	square[Z] = (*pQ)[Z] * (*pQ)[Z];

	cross[X] = (*pQ)[Y] * (*pQ)[Z];
	cross[Y] = (*pQ)[Z] * (*pQ)[X];
	cross[Z] = (*pQ)[X] * (*pQ)[Y];

	wimag[X] = -(*pQ)[W] * (*pQ)[X];
	wimag[Y] = -(*pQ)[W] * (*pQ)[Y];
	wimag[Z] = -(*pQ)[W] * (*pQ)[Z];

	(*pMatrix)[Mth::RIGHT][X] = 1 - 2 * (square[Y] + square[Z]);
	(*pMatrix)[Mth::RIGHT][Y] = 2 * (cross[Z] + wimag[Z]);
	(*pMatrix)[Mth::RIGHT][Z] = 2 * (cross[Y] - wimag[Y]);
	(*pMatrix)[Mth::RIGHT][W] = 0.0f;
	
	(*pMatrix)[Mth::UP][X] = 2 * (cross[Z] - wimag[Z]);
	(*pMatrix)[Mth::UP][Y] = 1 - 2 * (square[X] + square[Z]);
	(*pMatrix)[Mth::UP][Z] = 2 * (cross[X] + wimag[X]);
	(*pMatrix)[Mth::UP][W] = 0.0f;

	(*pMatrix)[Mth::AT][X] = 2 * (cross[Y] + wimag[Y]);
	(*pMatrix)[Mth::AT][Y] = 2 * (cross[X] - wimag[X]);
	(*pMatrix)[Mth::AT][Z] = 1 - 2 * (square[X] + square[Y]);
	(*pMatrix)[Mth::AT][W] = 0.0f;

	(*pMatrix)[Mth::POS][X] = pT->GetX();
	(*pMatrix)[Mth::POS][Y] = pT->GetY();
	(*pMatrix)[Mth::POS][Z] = pT->GetZ();
	(*pMatrix)[Mth::POS][W] = 1.0f;
#endif		// __PLAT_NGC__

	if (rotate)
	{
		// compensate for bone rotation (like for the skateboards)

		if (flip)
		{
			// Right: -x,y,z
			(*pMatrix)[Mth::RIGHT][X] = -(*pMatrix)[Mth::RIGHT][X];
			// Up: x,-y,-z
			(*pMatrix)[Mth::UP][Y]=-(*pMatrix)[Mth::UP][Y];
			(*pMatrix)[Mth::UP][Z]=-(*pMatrix)[Mth::UP][Z];
			// At: -x,y,z
			(*pMatrix)[Mth::AT][X]=-(*pMatrix)[Mth::AT][X];
			// Pos: -x,y,z
			(*pMatrix)[Mth::POS][X]=-(*pMatrix)[Mth::POS][X];
		}
		else
		{
			// Right: -x,-y,-z
			(*pMatrix)[Mth::RIGHT][X] = -(*pMatrix)[Mth::RIGHT][X];
			(*pMatrix)[Mth::RIGHT][Y] = -(*pMatrix)[Mth::RIGHT][Y];
			(*pMatrix)[Mth::RIGHT][Z] = -(*pMatrix)[Mth::RIGHT][Z];
			// Up: -x,-y,-z
			(*pMatrix)[Mth::UP][X]=-(*pMatrix)[Mth::UP][X];
			(*pMatrix)[Mth::UP][Y]=-(*pMatrix)[Mth::UP][Y];
			(*pMatrix)[Mth::UP][Z]=-(*pMatrix)[Mth::UP][Z];
			// At: x,y,z
			// Pos: x,y,z
		}	
	}
	else
	{
		if (flip)
		{
			// Right: x,-y,-z
			(*pMatrix)[Mth::RIGHT][Y] = -(*pMatrix)[Mth::RIGHT][Y];
			(*pMatrix)[Mth::RIGHT][Z] = -(*pMatrix)[Mth::RIGHT][Z];
			// Up: -x,y,z
			(*pMatrix)[Mth::UP][X]=-(*pMatrix)[Mth::UP][X];
			// At: -x,y,z
			(*pMatrix)[Mth::AT][X]=-(*pMatrix)[Mth::AT][X];
			// Pos: -x,y,z
			(*pMatrix)[Mth::POS][X]=-(*pMatrix)[Mth::POS][X];
		}
		else
		{
			// Right: x,y,z
			// Up: x,y,z
			// At: x,y,z
			// Pos: x,y,z
		}	
	}	
#endif	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkeleton::Update( CPose* pPose )
{
	this->Update( &pPose->m_rotations[0], &pPose->m_translations[0] );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkeleton::Update( Mth::Quat* pQuat, Mth::Vector* pTrans )
{
	CBone* p_currentBone = mp_bones;
	Mth::Matrix* p_currentMatrix=mp_matrices;
	Mth::Quat* p_current_quat=pQuat;
	Mth::Vector* p_current_trans=pTrans;

#ifdef __NOPT_ASSERT__
	bool flipped = m_flags & nxSKELETONFLAGS_FLIPPED;
	if ( flipped )
	{
		Dbg_MsgAssert( 0, ( "Flipping has been moved to CAnimationComponent" ) );
	}
#endif

	// Calculate the mask for testing the bone skip data.
	uint32 skip_index_mask	= 1 << GetBoneSkipIndex();
	uint32*	p_skip_list		= mp_skeletonData->GetBoneSkipList();

	for ( int i = 0; i < m_numBones; i++ )
	{
		if( !( p_currentBone->m_flags & nxBONEFLAGS_NOANIM ))
		{
			// Decide whether we can skip this bone. Skipping bones where a child bone is not skipped will cause
			// problems.
			bool skip_this_bone = (( p_skip_list[i] & skip_index_mask ) != 0 );
			if( !skip_this_bone )
			{
				// Skateboard rotation has been moved to CAnimationComponent.
				Dbg_MsgAssert( !( p_currentBone->m_flags & nxBONEFLAGS_ROTATE ), ( "Skateboard rotation has been moved to CAnimationComponent" ) );

				sQuatVecToMatrix( p_current_quat, p_current_trans, p_currentMatrix, false, false );
		
				if ( p_currentBone->m_flags & nxBONEFLAGS_SCALENONLOCAL )
				{
					p_currentMatrix->Scale( p_currentBone->m_scale );
				}

				if ( i != 0 )
				{
					// if it's not the root, then apply the parent's xform
					(*p_currentMatrix) *= *(p_currentBone->mp_parentMatrix);
				}
			}
			else
			{
				// Use the parent bone's matrix for now.
				(*p_currentMatrix) = *(p_currentBone->mp_parentMatrix);
			}
		}
		
		p_currentMatrix++;
		p_currentBone++;
		p_current_quat++;
		p_current_trans++;
	}

	Mth::Matrix* p_currentInverseNeutralPoseMatrix = mp_skeletonData->GetInverseNeutralPoseMatrices();
	Dbg_MsgAssert( p_currentInverseNeutralPoseMatrix, ( "Was expecting neutral pose matrices to be initialized.  SKE files not in version 2 format?" ) );

	// Loop through each bone and update its matrices
	p_currentBone=mp_bones;
	p_currentMatrix=mp_matrices;
	for ( int i = 0; i < m_numBones; i++ )
	{
		if ( !(p_currentBone->m_flags & nxBONEFLAGS_NOANIM) )
		{
			if ( p_currentBone->m_flags & nxBONEFLAGS_SCALELOCAL )
			{
				p_currentMatrix->ScaleLocal( p_currentBone->m_scale );
			}

			// switching over to the new system of having the neutral pose
//			Dbg_MsgAssert( p_currentInverseNeutralPoseMatrix, ( "default anims have been deprecated" ) );
//			(*p_currentMatrix) = p_currentBone->m_invertedNeutralMatrix * (*p_currentMatrix);		

			// data inside the SKE file, rather than the skeleton instance
			(*p_currentMatrix) = (*p_currentInverseNeutralPoseMatrix) * (*p_currentMatrix);		
				
			// clear out final component, just in case...
			// it's probably be worth investigating whether we need to do this...
			(*p_currentMatrix)[Mth::RIGHT][W] = 0.0f;
			(*p_currentMatrix)[Mth::UP][W] = 0.0f;
			(*p_currentMatrix)[Mth::AT][W] = 0.0f;
//			(*p_currentMatrix)[Mth::POS][W] = 1.0f;
		}

		p_currentInverseNeutralPoseMatrix++;
		p_currentMatrix++;
		p_currentBone++;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkeleton::GetBoneMatrix( uint32 boneId, Mth::Matrix* pBoneMatrix )
{
	Dbg_Assert( pBoneMatrix );

	CBone* pBone = get_bone_by_id( boneId );
	int i = GetBoneIndexById( boneId );

	if ( !pBone )
	{
		// for now, since we're changing our bone names around,
		// don't assert if we can't find the bone...
		return false;
	}

	Dbg_MsgAssert( pBone, ( "Couldn't find bone with id %s", Script::FindChecksumName(boneId) ) );

	// m_matrix represents the transform needed to go from the default (neutral)
	// pose to the actual pose with animation applied to it.  So, we need to
	// transform this value by the root-to-neutral matrix (m_neutralMatrix)
	// in order to get the actual animated-bone's position for this frame (relative
	// to the root of the object).

	Mth::Matrix boneMatrix;
	boneMatrix = *(mp_matrices + i);
	
	// GJ 2/21/03:  i'm not sure why we needed to deal with the
	// neutral matrix at all...   should look into this at some
	// point...
	Mth::Matrix theMat = *( mp_skeletonData->GetInverseNeutralPoseMatrices() + i );
//	Mth::Matrix theMat = pBone->m_invertedNeutralMatrix;
	theMat.InvertUniform();
	theMat = theMat * boneMatrix;
    
	// GJ kludge:  the root bone contains a 90 degree rotation
	// to compensate for the MAX->Game coordinate change
	// but we don't want that to affect the bone matrix, 
	// so transform it by the inverse of the root matrix
//	CBone* pDummyBone = get_bone_by_id( CRCD(0x9f173eeb,"dummy_scale_zz") );
//	if ( pDummyBone )
	
	int dummyIndex = GetBoneIndexById( CRCD(0x9f173eeb,"dummy_scale_zz") );
	if ( dummyIndex != -1 )
	{
		Mth::Vector temp = theMat[Mth::POS];
		theMat[Mth::POS] = Mth::Vector(0.0f,0.0f,0.0f,1.0f);
		Mth::Matrix dummyMatrix = *( mp_skeletonData->GetInverseNeutralPoseMatrices() + dummyIndex );
//		Mth::Matrix dummyMatrix = pDummyBone->m_invertedNeutralMatrix;
		theMat = dummyMatrix * theMat;
		theMat[Mth::POS] = temp;
	}

	(*pBoneMatrix) = theMat;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32* CSkeleton::GetBoneSkipList( void )
{
	if( mp_skeletonData )
	{
		return mp_skeletonData->GetBoneSkipList();
	}
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkeleton::SetBoneSkipDistance( float dist )
{
	if( mp_skeletonData )
	{
		for( uint32 lod = 0; lod <= m_maxBoneSkipLOD; ++lod )
		{
			if( dist < mp_skeletonData->GetBoneSkipDistance( lod ))
			{
				m_skipIndex = lod;
				return;
			}
		}
	}
	m_skipIndex = m_maxBoneSkipLOD;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkeleton::GetBonePosition( uint32 boneId, Mth::Vector* pBonePos )
{
	Dbg_Assert( pBonePos );

	CBone* pBone = get_bone_by_id( boneId );
	int i = GetBoneIndexById( boneId );

	if ( !pBone )
	{
		// for now, since we're changing our bone names around,
		// don't assert if we can't find the bone...
		return false;
	}

	Dbg_MsgAssert( pBone, ( "Couldn't find bone with id %s", Script::FindChecksumName(boneId) ) );

	// m_matrix represents the transform needed to go from the default (neutral)
	// pose to the actual pose with animation applied to it.  So, we need to
	// transform this value by the root-to-neutral matrix (m_neutralMatrix)
	// in order to get the actual animated-bone's position for this frame (relative
	// to the root of the object).

	// GJ 2/21/03:  i'm not sure why we needed to deal with the
	// neutral matrix at all...   should look into this at some
	// point...

	Mth::Matrix boneMatrix;
	boneMatrix = *(mp_matrices + i);
	Mth::Matrix theMat = *( mp_skeletonData->GetInverseNeutralPoseMatrices() + i );
//	Mth::Matrix theMat = pBone->m_invertedNeutralMatrix;
	theMat.InvertUniform();
	theMat = theMat * boneMatrix;
    
    (*pBonePos) = theMat[Mth::POS];

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkeletonData::CSkeletonData()
{
    m_numBones = 0;
	m_flags = 0;
    m_animScriptName = 0;
	mp_inverseNeutralPoseMatrices = NULL;

	// Default the Level0 LOD distance to a large number.
	m_skipLODDistances[0] = MAX_LOD_DISTANCE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkeletonData::~CSkeletonData()
{
	if ( mp_inverseNeutralPoseMatrices )
	{
		delete[] mp_inverseNeutralPoseMatrices;
		mp_inverseNeutralPoseMatrices = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkeletonData::Load( uint32* p_data32, int data_size, bool assertOnFail )
{
	uint8* p_data = (uint8*)p_data32;

	// new load of platform-specific SKE files...
	bool success = false;

#ifdef __NOPT_ASSERT__
	int versionNumber = *((int*)p_data);
	Dbg_MsgAssert( versionNumber >= 2, ( "No longer supporting old SKE format" ) );
#endif
	p_data += sizeof(int);
		
	m_flags = *((uint32*)p_data);
	p_data += sizeof(uint32);

	m_numBones = *((int*)p_data);
	p_data += sizeof(int);
    
    Dbg_MsgAssert( m_numBones < vMAX_BONES, ( "Too many bones in skeleton (raise vMAX_BONES from %d to %d)", vMAX_BONES, m_numBones ) );

	memcpy( m_boneNameTable, p_data, m_numBones * sizeof(uint32) );
	p_data += ( m_numBones * sizeof(uint32) );

	memcpy( m_parentNameTable, p_data, m_numBones * sizeof(uint32) );
	p_data += ( m_numBones * sizeof(uint32) );

	memcpy( m_flipNameTable, p_data, m_numBones * sizeof(uint32) );
	p_data += ( m_numBones * sizeof(uint32) );
	
	Dbg_MsgAssert( mp_inverseNeutralPoseMatrices == NULL, ( "Inverse neutral pose matrices not NULL?!?" ) );
	mp_inverseNeutralPoseMatrices = new Mth::Matrix[m_numBones];

	for ( int i = 0; i < m_numBones; i++ )
	{
// Mick: read a word at a time, as it's not aligned and I'm trying to ensure
// that all vector operations work on aligned boundries
// so we can optimize with that assumption	
//		Mth::Quat theQuat = *((Mth::Quat*)p_data);
		Mth::Quat theQuat;
		theQuat[X] = *((float*)p_data);
		theQuat[Y] = *((float*)(p_data+4));
		theQuat[Z] = *((float*)(p_data+8));
		theQuat[W] = *((float*)(p_data+12));
		p_data += sizeof(Mth::Quat);
		
//		Mth::Vector theVector = *((Mth::Vector*)p_data);
		Mth::Vector theVector;
		theVector[X] = *((float*)(p_data));
		theVector[Y] = *((float*)(p_data+4));
		theVector[Z] = *((float*)(p_data+8));
		theVector[W] = *((float*)(p_data+12));
		p_data += sizeof(Mth::Vector);

		Mth::Matrix theNeutralPoseMatrix;

		// gets the skeleton into the model's space
		Mth::QuatVecToMatrix( &theQuat, &theVector, &theNeutralPoseMatrix );

		if ( i != 0 )
		{
			Mth::Matrix neutral_parent_matrix = *(mp_inverseNeutralPoseMatrices + GetIndex(GetParentName(i)));

			// GJ:  shouldn't this already be inverted from
			// previous iterations of the for-loop?
			neutral_parent_matrix.InvertUniform();

			theNeutralPoseMatrix *= neutral_parent_matrix;

			theNeutralPoseMatrix[X][W] = 0.0f;
			theNeutralPoseMatrix[Y][W] = 0.0f;
			theNeutralPoseMatrix[Z][W] = 0.0f;
			theNeutralPoseMatrix[W][W] = 1.0f;
		}

		theNeutralPoseMatrix.InvertUniform();
		*(mp_inverseNeutralPoseMatrices + i) = theNeutralPoseMatrix;
	}

	// if we get here, then it's successful
	success = true;

//ERROR:
    return success;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkeletonData::InitialiseBoneSkipList( const char* p_fileName )
{
	// Stip leading path information and trailing extensions to obtain the name of the skeleton.
	char skeleton_name[128];

	char *p_copy = strrchr( p_fileName, '/' );
	if( p_copy == NULL )
		p_copy = strrchr( p_fileName, '\\' );

	if( p_copy )
	{
		++p_copy;
		strcpy( skeleton_name, p_copy );
	}
	else
	{
		strcpy( skeleton_name, p_fileName );
	}

	p_copy = strchr( skeleton_name, '.' );
	if( p_copy )
	{
		*p_copy = '\0';
	}

	// First, get a pointer to the script global SkipBoneLODInfo.
	Script::CStruct *p_all_lod_info = Script::GetStructure( CRCD( 0x7da53781, "BoneSkipLODInfo" ));
	if( p_all_lod_info )
	{
		Script::CArray *p_skeleton_lod_info = NULL;
		p_all_lod_info->GetArray( skeleton_name, &p_skeleton_lod_info );

		// No requirement that there *must* be SkipBoneLODInfo for a given skeleton.
		if( p_skeleton_lod_info )
		{
			// Now run through each element of the array. Each element is a structure.
			uint32 array_size = p_skeleton_lod_info->GetSize();
			for( uint32 lod_level = 0; lod_level < array_size; ++lod_level )
			{
				Script::CStruct *p_element = p_skeleton_lod_info->GetStructure( lod_level );
	
				// Obtain the distance, required, below which this LOD is active.
				float lod_distance = 0.0f;
				if( p_element->GetFloat( "LODDistance", &lod_distance ))
				{
					// Convert to inches.
					lod_distance = FEET_TO_INCHES( lod_distance );

					// Store the square of the distance (in inches).
					// Special case - if this is the last lod level, set the distance such that it will always be active.
					if( lod_level == ( array_size - 1 ))
					{
						m_skipLODDistances[lod_level] = MAX_LOD_DISTANCE;
					}
					else
					{
						m_skipLODDistances[lod_level] = lod_distance * lod_distance;
					}
				}
				else
				{
					Dbg_MsgAssert( 0, ( "Missing LODDistance field for LOD level %d in skeleton %s\n", lod_level, skeleton_name ));
				}
	
				Script::CArray *p_skip_bones_array = NULL;
				if( p_element->GetArray( "SkipBones", &p_skip_bones_array ))
				{
					// There are bones to be skipped for this entry.
					uint32 size = p_skip_bones_array->GetSize();
					for( uint32 bone = 0; bone < size; ++bone )
					{
						// Get the checksum of the bone name.
						uint32 bone_checksum = p_skip_bones_array->GetChecksum( bone );

						// Obtain the bone index from this bone checksum.
						uint32 bone_index			= GetIndex( bone_checksum );

						// Set the bitfield flag for this bone.
						m_skipLODTable[bone_index]	= 0xFFFFFFFF & ~(( 1 << lod_level ) - 1 );
					}
				}
			}
		}
	}	
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkeletonData::Load(const char* p_fileName, bool assertOnFail)
{
	// new load of platform-specific SKE files...
	bool success = false;
	int file_size;
	uint32* p_fileBuffer = NULL;

	// open the file as a stream
	void* pStream = File::Open( p_fileName, "rb" );
	
    // make sure the file is valid
	if ( !pStream )
	{
		Dbg_MsgAssert( assertOnFail, ("Load of %s failed - file not found?", p_fileName) );
		goto ERROR;
	}

	file_size = File::GetFileSize(pStream);
	Dbg_MsgAssert( file_size, ("Skeleton file %s size is 0", p_fileName) );
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	p_fileBuffer = (uint32*)Mem::Malloc( file_size );
	Mem::Manager::sHandle().PopContext();
	
	if ( !File::Read( p_fileBuffer, file_size, 1, pStream ) )
	{
		Dbg_MsgAssert( assertOnFail, ("Load of %s failed - read failed?", p_fileName) );
		goto ERROR;
	}

	if ( !Load( p_fileBuffer, file_size, assertOnFail ) )
	{
		Dbg_MsgAssert( assertOnFail, ("Load of %s failed - parse failed?", p_fileName) );
		goto ERROR;
	}

	// GJ:  THPS4 patch for weird head scaling on female peds...
	if ( strstr( p_fileName, "ped_f" ) )
	{
		m_flags |= 0x1;
	}

	// if we get here, then it's successful
	success = true;

ERROR:
	if ( p_fileBuffer )
	{
		Mem::Free( p_fileBuffer );
	}
	File::Close(pStream);    

	InitialiseBoneSkipList( p_fileName );

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CSkeletonData::GetNumBones() const
{
    return m_numBones;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CSkeletonData::GetBoneName( int index )
{
    Dbg_Assert( index >= 0 && index < m_numBones );
    
    return m_boneNameTable[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CSkeletonData::GetParentName( int index )
{
    Dbg_Assert( index >= 0 && index < m_numBones );
    
    return m_parentNameTable[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CSkeletonData::GetParentIndex( int index )
{
    Dbg_Assert( index >= 0 && index < m_numBones );
    
    return GetIndex( m_parentNameTable[index] );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CSkeletonData::GetFlipName( int index )
{
    Dbg_Assert( index >= 0 && index < m_numBones );
    
    return m_flipNameTable[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CSkeletonData::GetIndex( uint32 boneName )
{
    for ( int i = 0; i < m_numBones; i++ )
    {
        if ( boneName == m_boneNameTable[i] )
            return i;
    }

    // not found
    Dbg_Assert( 0 );
    return 0;
}



#if 0
// The following functions have been deprecated...
// they were formerly used to find the correct
// default SKA file in order to build the neutral
// pose matrices.  Now, however, the neutral pose
// is stored inside the SKE file
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkeletonData::SetAnimScriptName( uint32 anim_script_name )
{
	m_animScriptName = anim_script_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CSkeletonData::GetAnimScriptName() const
{
	return m_animScriptName;
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Matrix* CSkeletonData::GetInverseNeutralPoseMatrices()
{
	return mp_inverseNeutralPoseMatrices;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx


// DEBUG FUNCTIONS

#if 0

	#if 0
		// draw the bone between the origin of that bone, and its parent
		tempMatrix0 = (*p_currentBone->mp_parentMatrix) * (*pRootMatrix);
		tempMatrix1 = ( ( mp_matrices + i ) ) * (*pRootMatrix); 
		AddDebugBone( tempMatrix0.GetPos(), tempMatrix1.GetPos(), r, g, b );	
	#endif

	#if 0
		// * test neutral-in-skater-space pose *
		tempMatrix0 = *p_currentBone->mp_invertedNeutralParentMatrix;
		tempMatrix0.InvertUniform();
		tempMatrix0	= tempMatrix0 * (*pRootMatrix); 
			
		tempMatrix1 = p_currentBone->m_invertedNeutralMatrix;
		tempMatrix1.InvertUniform();
		tempMatrix1 = tempMatrix1 * (*pRootMatrix);
			
		AddDebugBone( tempMatrix0.GetPos(), tempMatrix1.GetPos(), r, g, b );	
	#endif
	
	#if 0
		// * test inverted-to-neutral *
		Mth::Matrix boneMatrix;
		boneMatrix = *( ( mp_matrices + i ) );
		tempMatrix0 = tempMatrix1;
		tempMatrix1 = p_currentBone->m_neutralMatrix * boneMatrix;
		tempMatrix1 *= (*pRootMatrix);
		AddDebugBone( tempMatrix0.GetPos(), tempMatrix1.GetPos(), r, g, b );
	#endif

#endif


#if 0
	// Programmatic jaw test
	if ( p_currentBone->m_name == Script::GenerateCRC("jaw") )
	{
		static int jaw_rotation = 0;
		jaw_rotation++;
		p_currentMatrix->RotateLocal(Mth::Vector( Mth::DegToRad((float)(jaw_rotation)/10.0f), 0.0f, 0.0f));
		if (jaw_rotation>60)
		{
			jaw_rotation=0;
		}
	}
#endif

#if 0
	// This scales the item, with respect to the model's
	// origin.  There are some neat looking effects
	// that can be done with this, such as a gorilla man
	// with a over-sized upper body, but makes the lip
	// tricks look incorrect.
	if ( Script::GetInt( "ScalingTest", false ) )
	{
		// override the scale if the GorillaMode is set...
		Script::CArray* pArray = Script::GetArray( "master_scaling_table" );
		if ( pArray && i < (int)pArray->GetSize() )
		{
			Script::CVector* pVector = pArray->GetVector(i);
			Mth::Vector theVector;
			const float weight_scale=Script::GetFloat("weight_scale");
			theVector[X] = 1.0f + (pVector->mX-1.0f) * weight_scale;
			theVector[Y] = 1.0f + (pVector->mY-1.0f) * weight_scale;
			theVector[Z] = 1.0f + (pVector->mZ-1.0f) * weight_scale;
			p_currentMatrix->Scale(theVector);
		}
	}
#endif


================================================
FILE: Code/Gfx/Skeleton.h
================================================
//****************************************************************************
//* MODULE:			Gfx
//* FILENAME:		Skeleton.h
//* OWNER:			Gary Jesdanun
//* CREATION DATE:	11/15/2001
//****************************************************************************

#ifndef __GFX_SKELETON_H
#define __GFX_SKELETON_H

/*****************************************************************************
**								Includes									**
*****************************************************************************/

#include 
#include 

#include 

/*****************************************************************************
**								Defines									**
*****************************************************************************/

namespace Mth
{
	class Matrix;
	class Quat;
	class Vector;
}
									
namespace Script
{
	class CStruct;
};

namespace Gfx
{

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

	class CBone;

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/
    
class CSkeletonData	: public Spt::Class
{
public:

	static const int	BONE_SKIP_LOD_BITS = 32;

					CSkeletonData();
	virtual			~CSkeletonData();
	bool			Load( const char* p_fileName, bool assertOnFail );
	bool			Load( uint32* p_data, int data_size, bool assertOnFail );
	void			InitialiseBoneSkipList( const char* p_fileName );
//	void			SetAnimScriptName( uint32 animScriptName );

public:
	int				GetNumBones() const;
	uint32			GetBoneName( int index );
	uint32			GetParentName( int index );
	uint32			GetParentIndex( int index );
	uint32			GetFlipName( int index );
	uint32			GetIndex( uint32 boneName );
//	uint32			GetAnimScriptName() const;
	uint32*			GetBoneSkipList( void )				{ return m_skipLODTable; }
	float			GetBoneSkipDistance( uint32 lod )	{ return m_skipLODDistances[lod]; }

	Mth::Matrix*	GetInverseNeutralPoseMatrices();

protected:
	int				m_numBones;
	uint32			m_boneNameTable[vMAX_BONES];
	uint32			m_parentNameTable[vMAX_BONES];
	uint32			m_flipNameTable[vMAX_BONES];
	uint32			m_skipLODTable[vMAX_BONES];				// Each uint32 for a specific bone provides 32 levels of skip information.
	float			m_skipLODDistances[BONE_SKIP_LOD_BITS];
	uint32			m_animScriptName;
	Mth::Matrix* 	mp_inverseNeutralPoseMatrices;
	
public:
	int				m_flags;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class CSkeleton
{
public:
	CSkeleton( CSkeletonData* pSkeletonData );
	~CSkeleton();

public:
	int						GetNumBones( void ) const;
	bool					ApplyBoneScale( Script::CStruct* pBodyShapeStructure );
	void					ResetScale( void );
	
public:
	void 					Update( CPose* pPose );
	void					Update( Mth::Quat* pQuat, Mth::Vector* pTrans );
	void					Display( Mth::Matrix* pMatrix, float r, float g, float b );
	
public:
	Mth::Matrix*			GetMatrices();
	Mth::Matrix				GetNeutralMatrix( int boneIndex );
	bool					GetBoneMatrix( uint32 boneId, Mth::Matrix* pMatrix );
	bool					GetBonePosition( uint32 boneId, Mth::Vector* pVector );
	int						GetBoneIndexById( uint32 boneId );
	int						GetFlipIndex( int boneIndex );
//	void					GetSkipList( int* pSkipList );
	uint32*					GetBoneSkipList( void );
	uint32					GetBoneSkipIndex( void )		{ return m_skipIndex; }
	void					SetBoneSkipDistance( float dist );
	void					SetMaxBoneSkipLOD( uint32 max )	{ m_maxBoneSkipLOD = max; }

public:
	// The following should be moved to the CAnimationComponent class	
//	bool					GetBoneRotationByIndex( int boneIndex );

public:
	bool					SetBoneActive( uint32 boneId, bool active );
	bool					SetBoneScale( uint32 boneId, const Mth::Vector& theBoneScale, bool isLocalScale );
	bool					GetBoneScale( uint32 boneId, Mth::Vector* pBoneScaleVector );
	void					CopyBoneScale( Gfx::CSkeleton* pSourceSkeleton );
	void					SetNeutralPose( Mth::Quat* pQuat, Mth::Vector* pTrans );
	CSkeletonData*			GetSkeletonData() { return mp_skeletonData; }

protected:
	CBone*					get_bone_by_id( uint32 boneId );
	void					update_matrices();

protected:
	uint32					m_flags;
	Mth::Matrix*			mp_matrices;
    CSkeletonData*			mp_skeletonData;
	uint32					m_skipIndex;
	uint32					m_maxBoneSkipLOD;

	// for non-procedural bone anims
	int						m_numBones;
	CBone*					mp_bones;
	
protected:
	void					initialize_hierarchy( CSkeletonData* pSkeletonData );
	void					initialize_flip_matrices( CSkeletonData* pSkeletonData );
	void					initialize_bone_names( CSkeletonData* pSkeletonData );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Gfx

#endif	// __GFX_SKELETON_H



================================================
FILE: Code/Gfx/XBox/NX/BillboardScreenAlignedVS.vsh
================================================
xvs.1.1

; Constants:
;  c0 - c3		WVP Matrix (WORLD*VIEW*PROJECTION)
;  c4			Screen right vector
;  c5			Screen up vector
;  c6			Screen at vector

; In:
;   v0 - Position		(actually the position of the pivot point)
;	v1 - Normal			(actually the position of the point relative to the pivot)
;   v2 - Vertex color
;   v3 - TexCoord0
;   v4 - TexCoord1
;   v5 - TexCoord2
;   v6 - TexCoord3

; Out:
;   oPos - Position
;   oTn - TextureCoords

;------------------------------------------------------------------------------
; Pivot relative position -> world relative position, plus copy texture coordinates
; (interleaving these helps prevent stalls)
;------------------------------------------------------------------------------
mul	r0.xyz,	v1.x, c4
mov oT0,	v3
mad r0.xyz, v1.y, c5, r0
mov oT1,	v4
mad r0.xyz,	v1.z, c6, r0
mov oT2,	v5
add r0.xyz,	v0, r0
mov oT3,	v6
sge r0.w,	c0.x, c0.x		; Set r0.w = 1.0

;------------------------------------------------------------------------------
; Vertex color
;------------------------------------------------------------------------------
mov oD0, v2

;------------------------------------------------------------------------------
; Vertex->screen transform
;------------------------------------------------------------------------------
dp4 oPos.x, r0, c0
dp4 oPos.y, r0, c1
dp4 oPos.z, r0, c2
dp4 oPos.w, r0, c3

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------
mov	oFog.x, -r12.w



================================================
FILE: Code/Gfx/XBox/NX/ParticleFlatVS.vsh
================================================
xvs.1.1

; Constants:
;  c0 - c3		WVP Matrix (WORLD*VIEW*PROJECTION)
;  c4			Screen right vector
;  c5			Screen up vector
;  c8-c11		Vertex texture coordinates (for index0 through index3)
;  c12-c15		Vertex width and height multipliers (for index0 through index3)

;  c16			Particle position (xyz) (set in the data stream per particle)
;  c17			Particle start width and height (xy) and end width and height (zw) (set in the data stream per particle)
;  c18			Size interpolator (x) and color interpolator (y) (set in the data stream per particle)


; In:
;   v0 - Vertex start color
;   v1 - Vertex end color
;   v2 - Index

; Out:
;   oPos	- Position
;   oD0		- Vertex color
;   oT0		- TextureCoords

; Get the index of this vert
mov	a0.x, v2.x

; Move the width and height multipliers into a general register.
mov r1, c[12 + a0.x]

; Calculate the interpolated width and height of the particle.
mov r2, c17.zw		// r2 = ( c17.z, c17.w, c17.w, c17.w )
sub r2, r2, c17.xy	// r2 = ( c17.z - c17.x, c17.w - c17.y, ...)
mul r2, r2, c18.x
add r2, r2, c17.xy

; Multiply by the width and height of this particle.
mul r1, r1, r2

; Add width and height multiples of the screen right and up vectors
mul r0.xyz, r1.x, c4
mad r0.xyz, r1.y, c5, r0

; Add particle position
add r0.xyz, c16, r0
sge r0.w,	r1.x, r1.x		; r0.w = 1.0

; Vertex->screen transform
dp4 oPos.x, r0, c0
dp4 oPos.y, r0, c1
dp4 oPos.z, r0, c2
dp4 oPos.w, r0, c3

; Calculate the interpolated vertex color
mov r2, v1
sub r2, r2, v0
mad r2, r2, c18.y, v0

; Vertex color
mov oD0, r2

; Deal with fog value (r12 shadows oPos)...
mov	oFog.x, -r12.w

; Move texture coordinate in based on index
mov oT0, c[8 + a0.x]


================================================
FILE: Code/Gfx/XBox/NX/ParticleNewFlatPointSpriteVS.vsh
================================================
xvs.1.1

; Constants:
;  c0 - c3		WVP Matrix (WORLD*VIEW*PROJECTION)
;  c18-c20		Particle s0, s1 and s2 vectors
;  c21-c23		Particle p0, p1 and p2 vectors
;  c24			Eye position (x,y,z) and viewport height (w)

; In:
;   v0 - 4-element random vector
;   v1 - Time (x) and color interpolator (y)
;   v2 - Vertex start color
;   v3 - Vertex end color

; Out:
;   oPos	- Position
;	oPts	- Point sprite size
;   oD0		- Vertex color


; Calculate the square of the time interpolator.
mul r2.x, v1.x, v1.x

; Calculate the position of the particle, from pos = ( m_p0 + ( t * m_p1 ) + (( t * t ) * m_p2 )) + ( m_s0 + ( t * m_s1 ) + (( t * t ) * m_s2 )).Scale( r );
mov r0, c21					; pos = m_p0
mad r0, c22, v1.x, r0		; pos = m_p0 + ( t * m_p1 )
mad r0, c23, r2.x, r0		; pos = m_p0 + ( t * m_p1 ) + (( t * t ) * m_p2 )

mov r1, c18					; tmp = ( m_s0 )
mad r1, c19, v1.x, r1		; tmp = ( m_s0 + ( t * m_s1 ))
mad r1, c20, r2.x, r1		; tmp = ( m_s0 + ( t * m_s1 ) + (( t * t ) * m_s2 ))

mad r0, r1, v0, r0			; pos = ( m_p0 + ( t * m_p1 ) + (( t * t ) * m_p2 )) + ( m_s0 + ( t * m_s1 ) + (( t * t ) * m_s2 )).Scale( r );

; Calculate the distance from the camera to the particle.
sub r4, c24, r0
dp3	r4.x, r4, r4
rsq r4.x, r4.x
mul r4.x, r4.x, r0.w

; Now set position w to 1.0
sge r0.w, r0.x, r0.x

; Vertex->screen transform
dp4 oPos.x, r0, c0
dp4 oPos.y, r0, c1
dp4 oPos.z, r0, c2
dp4 oPos.w, r0, c3

; Save off the size of the particle
mul oPts.x, r4.x, c24.w

; Calculate the interpolated vertex color
mov r2, v2					; r2 = start_col
sub r3, v3, r2				; r3 = end_col - start_col
mad oD0, r3, v1.y, r2		; r3 = start_col + (( end col - start_col ) * color_interpolator )

; Deal with fog value (r12 shadows oPos)...
mov	oFog.x, -r12.w


================================================
FILE: Code/Gfx/XBox/NX/ParticleNewFlatVS.vsh
================================================
xvs.1.1

; Constants:
;  c0 - c3		WVP Matrix (WORLD*VIEW*PROJECTION)
;  c4			Screen right vector
;  c5			Screen up vector
;  c8-c11		Vertex texture coordinates (for index0 through index3)
;  c12-c15		Vertex width and height multipliers (for index0 through index3)

;  c16			Particle random seed vector
;  c17			Particle time interpolator (c17.x), color interpolator (c17.y)

;  c18-c20		Particle s0, s1 and s2 vectors
;  c21-c23		Particle p0, p1 and p2 vectors

; In:
;   v0 - Vertex start color
;   v1 - Vertex end color
;   v2 - Index

; Out:
;   oPos	- Position
;   oD0		- Vertex color
;   oT0		- TextureCoords

; Get the index of this vert
mov	a0.x, v2.x

; Move the time interpolator into a general register, and calculate the square.
mov r4, c17
mul r4.y, r4.x, r4.x

; Move the width and height multipliers into a general register.
mov r3, c[12 + a0.x]

; Calculate the position of the particle, from pos = ( m_p0 + ( t * m_p1 ) + (( t * t ) * m_p2 )) + ( m_s0 + ( t * m_s1 ) + (( t * t ) * m_s2 )).Scale( r );
mov r0, c21					; pos = m_p0
mad r0, c22, r4.x, r0		; pos = m_p0 + ( t * m_p1 )
mad r0, c23, r4.y, r0		; pos = m_p0 + ( t * m_p1 ) + (( t * t ) * m_p2 )

mov r1, c18					; tmp = ( m_s0 )
mad r1, c19, r4.x, r1		; tmp = ( m_s0 + ( t * m_s1 ))
mad r1, c20, r4.y, r1		; tmp = ( m_s0 + ( t * m_s1 ) + (( t * t ) * m_s2 ))

mad r0, r1, c16, r0			; pos = ( m_p0 + ( t * m_p1 ) + (( t * t ) * m_p2 )) + ( m_s0 + ( t * m_s1 ) + (( t * t ) * m_s2 )).Scale( r );

; Calculate the interpolated width and height of the particle.
mov r2, r0.w

; Now set position w to 1.0
sge r0.w, r0.x, r0.x

; Multiply width and height by the width and height multipliers of this vertex index
mul r2.xy, r2.xy, r3.xy

; Add width and height multiples of the screen right and up vectors to the position.
mad r0.xyz, r2.x, c4, r0
mad r0.xyz, r2.y, c5, r0

; Vertex->screen transform
dp4 oPos.x, r0, c0
dp4 oPos.y, r0, c1
dp4 oPos.z, r0, c2
dp4 oPos.w, r0, c3

; Calculate the interpolated vertex color
mov r2, v1
sub r2, r2, v0
mad oD0, r2, c17.y, v0

; Deal with fog value (r12 shadows oPos)...
mov	oFog.x, -r12.w

; Move texture coordinate in based on index
mov oT0, c[8 + a0.x]


================================================
FILE: Code/Gfx/XBox/NX/PixelShader0.psh
================================================
xps.1.1

tex t0
mul		t0.rgb,t0.rgb,c0.rgb	; Modulate texture color0 with material color0
mul_x4	r0.rgb,v0.rgb,t0.rgb	; Modulate (x4) resultant color with vertex color
+mul_x2	r0.a,v0.a,t0.a			; Modulate (x2) result alpha with vertex alpha

xfc prod, fog.rgb, sum, zero, 1 - fog.a, c4, r0.a


================================================
FILE: Code/Gfx/XBox/NX/PixelShader0IVA.psh
================================================
xps.1.1

tex t0
mul		t0.rgb,t0.rgb,c0.rgb	; Modulate texture color0 with material color0
mul_x4	r0.rgb,v0.rgb,t0.rgb	; Modulate (x4) resultant color with vertex color
+mul_x2	r0.a,c4.a,t0.a			; Modulate (x2) result alpha with constant alpha

xfc prod, fog.rgb, sum, zero, 1 - fog.a, c4, r0.a


================================================
FILE: Code/Gfx/XBox/NX/PixelShader1.psh
================================================
xps.1.1
mul_x2	v0.rgb,v0.rgb,c0.rgb						; Modulate vertex color0 with material color0
mov		r0,v0


================================================
FILE: Code/Gfx/XBox/NX/PixelShader2.psh
================================================
xps.1.1
tex t0
mov r0,t0


================================================
FILE: Code/Gfx/XBox/NX/PixelShader3.psh
================================================
xps.1.1
mov r0,c0


================================================
FILE: Code/Gfx/XBox/NX/PixelShader4.psh
================================================
xps.1.1
tex t0
mul_x2	r0,v0,t0				; Modulate (x2) texture color with vertex color.


================================================
FILE: Code/Gfx/XBox/NX/PixelShader5.psh
================================================
xps.1.1
mov	r0,v0		; Just use vertex color.


================================================
FILE: Code/Gfx/XBox/NX/PixelShaderBrighten.psh
================================================
xps.1.1

tex t0
;mul	t0.rgb,t0.rgb,c0.rgb	; Modulate texture color0 with material color0
;mul_x4	r0.rgb,v0.rgb,t0.rgb	; Modulate (x4) resultant color with vertex color
;mul_x2	r0.a,v0.a,t0.a			; Modulate (x2) result alpha with vertex alpha

mul_x2	r0,v0.a,t0.a			; Modulate (x2) result alpha with vertex alpha and copy into all 4 channels

xfc prod, fog.rgb, sum, zero, 1 - fog.a, c4, r0.a


================================================
FILE: Code/Gfx/XBox/NX/PixelShaderBrightenIVA.psh
================================================
xps.1.1

tex t0
;mul		t0.rgb,t0.rgb,c0.rgb	; Modulate texture color0 with material color0
;mul_x4		r0.rgb,v0.rgb,t0.rgb	; Modulate (x4) resultant color with vertex color
;+mul_x2	r0.a,c4.a,t0.a			; Modulate (x2) result alpha with constant alpha

mul_x2		r0,c4.a,t0.a			; Modulate (x2) result alpha with constant alpha and copy into all 4 channels

xfc prod, fog.rgb, sum, zero, 1 - fog.a, c4, r0.a


================================================
FILE: Code/Gfx/XBox/NX/PixelShaderBumpWater.psh
================================================
xps.1.1

tex		t0
texbem	t1, t0
mul		t1.rgb,t1.rgb,c0.rgb	; Modulate texture1 color with material color0
mul_x4	r0.rgb,v0.rgb,t1.rgb	; Modulate (x4) resultant color with vertex color
+mul_x2	r0.a,v0.a,t1.a			; Modulate (x2) result alpha with vertex alpha

xfc prod, fog.rgb, sum, zero, 1 - fog.a, c4, r0.a


================================================
FILE: Code/Gfx/XBox/NX/PixelShaderFocusBlur.psh
================================================
;------------------------------------------------------------------------------
; 4-tap filtering
; Copyright (C) 2001 Microsoft Corporation
; All rights reserved.
;------------------------------------------------------------------------------
xps.1.1

; default filter is box filter, but this is easily overwritten using SetPixelShaderConstant

; When setting a pixel shader constant, we must check to see if the
; filter coefficient is negative, in which case we set the constant as a positive
; number and negate the constant in the expression below.
def c0, 0.25f, 0.25f, 0.25f, 0.25f
def c1, 0.25f, 0.25f, 0.25f, 0.25f 
def c2, 0.25f, 0.25f, 0.25f, 0.25f 
def c3, 0.25f, 0.25f, 0.25f, 0.25f 

; source textures
tex t0
tex t1
tex t2
tex t3

; simple way
; mul r0, c0, t0
; mad r0, c1, t1, r0
; mad r0, c2, t2, r0
; mad r0, c3, t3, r0

; This has better precision (assuming the xmma_x2 intermediates don't overflow):
xmma_x2 discard, discard, r0, c0, t0, c1, t1	; 2 * (c0 * t0 + c1 * t1)
xmma_x2 discard, discard, r1, c2, t2, c3, t3	; 2 * (c2 * t2 + c3 * t3)
add_d2 r0, r0, r1


================================================
FILE: Code/Gfx/XBox/NX/PixelShaderFocusIntegrate.psh
================================================
;------------------------------------------------------------------------------
; Depth of field by choosing a range of z values and using that to select
; a blurred version of the original image.
; 
; Copyright (C) 2001 Microsoft Corporation
; All rights reserved.
;------------------------------------------------------------------------------
xps.1.1

; override with SetPixelShaderConstant
def c0, 0.f, 0.f, 0.f, 0.f      ; offset
def c1, 0.f, 0.f, 1.f, 0.f      ; slope x1
def c2, 0.f, 0.f, 0.f, 0.f      ; slope x4
def c3, 0.f, 0.f, 0.f, 1.f      ; slope x16
def c4, 1.f, 1.f, 1.f, 0.498039215      ; 0x7f

; source textures
tex t0                  ; z-buffer texture
tex t1                  ; pre-blurred texture

; get the range of active z values
sub    r0, t0, c0               ; offset
mul_x4 r1, c3, r0               ; scale x16
mad_x4 r1, c2, r0, r1           ; scale x4
mad    r1, c1, r0, r1           ; scale x1
sub r0.a, r1.a, c4.a            ; subtract 0x7f from r1.a
cnd r0.a, r0.a, zero.a, r1.b    ; keep blue only if r1.a is not 0xff
add r1.a, r1.a, r0.a            ; add alpha + blue, the desired range is now mapped to [0,1]
mul r1.a, r1_bx2.a, r1_bx2.a    ; convert to -1,1 range and square to get parabola

; modulate pre-blurred texture by depth range
xfc r1.a, t1, zero, zero, zero, zero, r1.a


================================================
FILE: Code/Gfx/XBox/NX/PixelShaderFocusLookupIntegrate.psh
================================================
;------------------------------------------------------------------------------
; FocusBlur by choosing a range of z values and using that to select
; a blurred version of the original image.
; 
; Copyright (C) 2001 Microsoft Corporation
; All rights reserved.
;------------------------------------------------------------------------------
xps.1.1

def c0, 0.5f, 0.5f, 0.5f, 0.5f		; blur value

; source textures
tex t0				; z-buffer texture
texreg2ar t1, t0	; lookup-table texture to choose what range of depth to use
tex t2				; pre-blurred texture, first offset
tex t3				; pre-blurred texture, second offset

; blur pre-blurred texture even more
lrp r0, c0, t2, t3

; modulate by depth range
xfc r0_sat, 1-t1.a, zero, zero, zero, zero, 1-t1.a


================================================
FILE: Code/Gfx/XBox/NX/PixelShaderNULL.psh
================================================
xps.1.1
def c7, 1.0f, 1.0f, 1.0f, 1.0f
mov r0, c7


================================================
FILE: Code/Gfx/XBox/NX/PixelShaderPointSprite.psh
================================================
xps.1.1
tex t3
mul_x2	r0,v0,t3				; Modulate (x2) texture color with vertex color.


================================================
FILE: Code/Gfx/XBox/NX/PixelShader_ShadowBuffer.psh
================================================
; Shadow buffer pixel shader.
; On entry:
; c0 contains the base percentage of color (even in shadow),
; c1 contains the percentage of color that shadow affects.

xps.1.1

tex t3			; get shadow from light

mul r1, c1, t3	; Use the shadow buffer to affect the shadow portion of the color change
add r0, c0, r1	; Add to the base color to get final color


================================================
FILE: Code/Gfx/XBox/NX/ShadowBufferStaticGeomPS.psh
================================================
;Shadowbuffer pixel shader.

xps.1.1

def c7, 0.6f, 0.6f, 0.6f, 0.0f

tex t0							; Base geometry texture (for alpha modulation).
tex t1							; Shadow depth buffer texture.

; White texture (outside of skater) we don't want to draw - so negate blue component into alpha.
; This way the white part of the texture will have zero alpha, the black part will have full alpha.

mul		r0.a, 1-t1.b, t0.a		; Modulate shadow alpha with base texture alpha (to neatly deal with holes in texture).
+add	r0.rgb, t1.rgb, c7.rgb	; Add in the ambient component to brighten up the shadow a bit.



================================================
FILE: Code/Gfx/XBox/NX/ShadowBufferStaticGeomVS.vsh
================================================
xvs.1.1

; Constants:
;  c0  - c3  - WVP Matrix (WORLD*VIEW*PROJECTION)
;  c4  - c7  - WT Matrix (WORLD*TEXTURETRANSFORM)
;  c8  - local space light position.

; In:
;   v0 - Position
;   v1 - Vertex color
;   v2 - TexCoord0

; Out:
;   oPos - Position
;   oTn - TextureCoords

;vertex->screen
dp4 oPos.x, v0, c0
dp4 oPos.y, v0, c1
dp4 oPos.z, v0, c2
dp4 oPos.w, v0, c3

;diffuse lighting (not necessary for our purposes)
;add	r0,c8,-v0
;dp3 r0.w,r0,r0
;rsq r1.x,r0.w
;mul r0,r0,r1.x
;dp3 oD0,v1,r0

;decal texture
mov oT0, v2

;vertex->shadowbuffer texcoords
dp4 oT1.x, v0, c4
dp4 oT1.y, v0, c5
dp4 oT1.z, v0, c6
dp4 r0.w, v0, c7

;clamp w (q) to 0
slt r1, c0, c0
max r0.w, r0.w, r1.w
mov oT1.w, r0.w


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_1Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight
    mul     VSTMP_REG_POS_ACCUM.xyz, VSTMP_REG_POS_TMP.xyz, VSIN_REG_WEIGHTS.x

    ; Transform normal
    dp3		VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed normal by weight
    mul     VSTMP_REG_NORMAL_ACCUM.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSIN_REG_WEIGHTS.x

    ; Copy w
    mov		VSTMP_REG_POS_ACCUM.w, VSIN_REG_POS.w

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dp4		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dp4		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dp4		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dp4     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, r12.w

;------------------------------------------------------------------------------
; Transform normal by combined camera & projection matrix
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_TMP.x, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_X
    dp3		VSTMP_REG_NORMAL_TMP.y, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_Y
    dp3		VSTMP_REG_NORMAL_TMP.z, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_Z

;------------------------------------------------------------------------------
; Single directional light + Ambient
;------------------------------------------------------------------------------

    ; Normalize
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSTMP_REG_NORMAL_TMP
    rsq     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP.w
    mul     VSTMP_REG_NORMAL_TMP.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSTMP_REG_NORMAL_TMP.w

    ; DP normal & light0 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_TMP.w

	; This is where the ambient gets added in.
    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR

    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light1 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_TMP.w
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light2 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_TMP.w
;    mad     oD0, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11
    mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11

	; Material color attenuation
	mul		oD0, r11, VSCONST_REG_MATERIAL_COLOR

;------------------------------------------------------------------------------
; Copy texture coordinates
;------------------------------------------------------------------------------

    mov     oT0, VSIN_REG_TEXCOORDS0
    mov     oT1, VSIN_REG_TEXCOORDS1
    mov     oT2, VSIN_REG_TEXCOORDS2

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_2Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

	; Transform normal by weighted matrix
    dp3     VSTMP_REG_NORMAL_ACCUM.x, VSIN_REG_NORMAL, VSTMP_REG_MAT0
	dp3		VSTMP_REG_NORMAL_ACCUM.y, VSIN_REG_NORMAL, VSTMP_REG_MAT1
	dp3		VSTMP_REG_NORMAL_ACCUM.z, VSIN_REG_NORMAL, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, r12.w

;------------------------------------------------------------------------------
; Transform normal by combined camera & projection matrix
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_TMP.x, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_X
    dp3		VSTMP_REG_NORMAL_TMP.y, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_Y
    dp3		VSTMP_REG_NORMAL_TMP.z, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_Z

;------------------------------------------------------------------------------
; Single directional light + Ambient
;------------------------------------------------------------------------------

    ; Normalize
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSTMP_REG_NORMAL_TMP
    rsq     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP.w
    mul     VSTMP_REG_NORMAL_TMP.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSTMP_REG_NORMAL_TMP.w

    ; DP normal & light0 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_TMP.w

	; This is where the ambient gets added in.
    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR

    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light1 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_TMP.w
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light2 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_TMP.w
;    mad     oD0, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11
    mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11

	; Material color attenuation
	mul		oD0, r11, VSCONST_REG_MATERIAL_COLOR

;------------------------------------------------------------------------------
; Copy texture coordinates
;------------------------------------------------------------------------------

    mov     oT0, VSIN_REG_TEXCOORDS0
    mov     oT1, VSIN_REG_TEXCOORDS1
    mov     oT2, VSIN_REG_TEXCOORDS2

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_3Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.z, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.z, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.z, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

	; Transform normal by weighted matrix
    dp3     VSTMP_REG_NORMAL_ACCUM.x, VSIN_REG_NORMAL, VSTMP_REG_MAT0
	dp3		VSTMP_REG_NORMAL_ACCUM.y, VSIN_REG_NORMAL, VSTMP_REG_MAT1
	dp3		VSTMP_REG_NORMAL_ACCUM.z, VSIN_REG_NORMAL, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, r12.w

;------------------------------------------------------------------------------
; Transform normal by combined camera & projection matrix
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_TMP.x, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_X
    dp3		VSTMP_REG_NORMAL_TMP.y, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_Y
    dp3		VSTMP_REG_NORMAL_TMP.z, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_Z

;------------------------------------------------------------------------------
; Single directional light + Ambient
;------------------------------------------------------------------------------

    ; Normalize
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSTMP_REG_NORMAL_TMP
    rsq     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP.w
    mul     VSTMP_REG_NORMAL_TMP.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSTMP_REG_NORMAL_TMP.w

    ; DP normal & light0 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_TMP.w

	; This is where the ambient gets added in.
    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR

    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light1 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_TMP.w
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light2 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_TMP.w
;    mad     oD0, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11
    mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11

	; Material color attenuation
	mul		oD0, r11, VSCONST_REG_MATERIAL_COLOR

;------------------------------------------------------------------------------
; Copy texture coordinates
;------------------------------------------------------------------------------

    mov     oT0, VSIN_REG_TEXCOORDS0
    mov     oT1, VSIN_REG_TEXCOORDS1
    mov     oT2, VSIN_REG_TEXCOORDS2

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_1Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]
    mov		VSTMP_REG_POS_TMP.w, VSIN_REG_POS.w

    ; Transform normal
    dp3		VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

	; No need to scale by results by vertex weights, since this is a one-weight shader so the weight
	; is assumed to be 1.0

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dp4		oPos.x, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_X
    dp4		oPos.y, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_Y
    dp4		oPos.z, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_Z
    dp4     oPos.w, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient, plus copy texture coordinates
; (interleaving these helps prevent stalls)
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_ACCUM.x, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0			; Light0 dir.normal
    dp3		VSTMP_REG_NORMAL_ACCUM.y, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1			; Light1 dir.normal
	dp3     VSTMP_REG_NORMAL_ACCUM.z, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2			; Light2 dir.normal

	max     VSTMP_REG_NORMAL_ACCUM.x, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_ACCUM.x	; Light0 result clamp
    max     VSTMP_REG_NORMAL_ACCUM.y, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_ACCUM.y	; Light1 result clamp
	max     VSTMP_REG_NORMAL_ACCUM.z, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_ACCUM.z	; Light2 result clamp

    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR												; Accumulate start with ambient
    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_ACCUM.x, r11					; Accumulate Light0 result modulated with Light0 color
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_ACCUM.y, r11					; Accumulate Light1 result modulated with Light1 color
	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_ACCUM.z, r11					; Accumulate Light2 result modulated with Light2 color

	mov     oT0, VSIN_REG_TEXCOORDS0
	mov     oT1, VSIN_REG_TEXCOORDS1
	mov     oT2, VSIN_REG_TEXCOORDS2
	mov     oT3, VSIN_REG_TEXCOORDS3

	mul		oD0, r11, VSIN_REG_COLOR														; Vertex color attenuation

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_1Weight_SBPassThru.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]
    mov		VSTMP_REG_POS_TMP.w, VSIN_REG_POS.w

	; No need to scale by results by vertex weights, since this is a one-weight shader so the weight
	; is assumed to be 1.0

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dp4		oPos.x, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_X
    dp4		oPos.y, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_Y
    dp4		oPos.z, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_Z
    dp4     oPos.w, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Calculate second set of texture coordinates (into 4th slot) by transforming the world space
; position into the shadowbuffer space
;------------------------------------------------------------------------------

	dp4		oT3.x, VSTMP_REG_POS_TMP, VSCONST_REG_SHADOWBUFFER_TRANSFORM_X
	dp4		oT3.y, VSTMP_REG_POS_TMP, VSCONST_REG_SHADOWBUFFER_TRANSFORM_Y
	dp4		oT3.z, VSTMP_REG_POS_TMP, VSCONST_REG_SHADOWBUFFER_TRANSFORM_Z
	dp4		r0.w,  VSTMP_REG_POS_TMP, VSCONST_REG_SHADOWBUFFER_TRANSFORM_W

	;clamp w (q) to 0
	slt		r1, c0, c0
	max		r0.w, r0.w, r1.w
	mov		oT3.w, r0.w

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_1Weight_UVTransform.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]
    mov		VSTMP_REG_POS_TMP.w, VSIN_REG_POS.w

    ; Transform normal
    dp3		VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

	; No need to scale by results by vertex weights, since this is a one-weight shader so the weight
	; is assumed to be 1.0

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dp4		oPos.x, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_X
    dp4		oPos.y, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_Y
    dp4		oPos.z, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_Z
    dp4     oPos.w, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient, plus transform texture coordinates
; (interleaving these helps prevent stalls)
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_ACCUM.x, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0			; Light0 dir.normal
    dp3		VSTMP_REG_NORMAL_ACCUM.y, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1			; Light1 dir.normal
	dp3     VSTMP_REG_NORMAL_ACCUM.z, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2			; Light2 dir.normal

	max     VSTMP_REG_NORMAL_ACCUM.x, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_ACCUM.x	; Light0 result clamp
    max     VSTMP_REG_NORMAL_ACCUM.y, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_ACCUM.y	; Light1 result clamp
	max     VSTMP_REG_NORMAL_ACCUM.z, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_ACCUM.z	; Light2 result clamp

    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR												; Accumulate start with ambient
    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_ACCUM.x, r11					; Accumulate Light0 result modulated with Light0 color
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_ACCUM.y, r11					; Accumulate Light1 result modulated with Light1 color
	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_ACCUM.z, r11					; Accumulate Light2 result modulated with Light2 color

	dp4     oT0.x, VSIN_REG_TEXCOORDS0, VSCONST_REG_UV_MAT00								; u' = ( s * mat00.x ) + ( t * mat00.y ) + ( 1.0 * mat00.w )
	dp4     oT0.y, VSIN_REG_TEXCOORDS0, VSCONST_REG_UV_MAT01								; v' = ( s * mat01.x ) + ( t * mat01.y ) + ( 1.0 * mat01.w )
	dp4     oT1.x, VSIN_REG_TEXCOORDS1, VSCONST_REG_UV_MAT10								; u' = ( s * mat10.x ) + ( t * mat10.y ) + ( 1.0 * mat10.w )
	dp4     oT1.y, VSIN_REG_TEXCOORDS1, VSCONST_REG_UV_MAT11								; v' = ( s * mat11.x ) + ( t * mat11.y ) + ( 1.0 * mat11.w )
	dp4     oT2.x, VSIN_REG_TEXCOORDS2, VSCONST_REG_UV_MAT20								; u' = ( s * mat20.x ) + ( t * mat20.y ) + ( 1.0 * mat20.w )
	dp4     oT2.y, VSIN_REG_TEXCOORDS2, VSCONST_REG_UV_MAT21								; v' = ( s * mat21.x ) + ( t * mat21.y ) + ( 1.0 * mat21.w )
	dp4     oT3.x, VSIN_REG_TEXCOORDS3, VSCONST_REG_UV_MAT30								; u' = ( s * mat30.x ) + ( t * mat30.y ) + ( 1.0 * mat30.w )
	dp4     oT3.y, VSIN_REG_TEXCOORDS3, VSCONST_REG_UV_MAT31								; v' = ( s * mat31.x ) + ( t * mat31.y ) + ( 1.0 * mat31.w )

	mul		oD0, r11, VSIN_REG_COLOR														; Vertex color attenuation

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_2Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

	; Transform normal by weighted matrix
	dp3     VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, VSTMP_REG_MAT0
	dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, VSTMP_REG_MAT1
	dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient, plus copy texture coordinates
; (interleaving these helps prevent stalls)
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_ACCUM.x, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0			; Light0 dir.normal
    dp3		VSTMP_REG_NORMAL_ACCUM.y, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1			; Light1 dir.normal
	dp3     VSTMP_REG_NORMAL_ACCUM.z, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2			; Light2 dir.normal

	max     VSTMP_REG_NORMAL_ACCUM.x, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_ACCUM.x	; Light0 result clamp
    max     VSTMP_REG_NORMAL_ACCUM.y, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_ACCUM.y	; Light1 result clamp
	max     VSTMP_REG_NORMAL_ACCUM.z, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_ACCUM.z	; Light2 result clamp

    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR												; Accumulate start with ambient
    mov     oT0, VSIN_REG_TEXCOORDS0
    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_ACCUM.x, r11					; Accumulate Light0 result modulated with Light0 color
    mov     oT1, VSIN_REG_TEXCOORDS1
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_ACCUM.y, r11					; Accumulate Light1 result modulated with Light1 color
    mov     oT2, VSIN_REG_TEXCOORDS2
	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_ACCUM.z, r11					; Accumulate Light2 result modulated with Light2 color
    mov     oT3, VSIN_REG_TEXCOORDS3

	mul		oD0, r11, VSIN_REG_COLOR														; Vertex color attenuation

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_2Weight_SBPassThru.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

    ; Copy w
    mov		VSTMP_REG_POS_ACCUM.w, VSIN_REG_POS.w
    
;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Calculate second set of texture coordinates (into 4th slot) by transforming the world space
; position into the shadowbuffer space
;------------------------------------------------------------------------------

	dp4		oT3.x, VSTMP_REG_POS_ACCUM,	VSCONST_REG_SHADOWBUFFER_TRANSFORM_X
	dp4		oT3.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_SHADOWBUFFER_TRANSFORM_Y
	dp4		oT3.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_SHADOWBUFFER_TRANSFORM_Z
	dp4		r0.w,  VSTMP_REG_POS_ACCUM, VSCONST_REG_SHADOWBUFFER_TRANSFORM_W

	;clamp w (q) to 0
	slt		r1, c0, c0
	max		r0.w, r0.w, r1.w
	mov		oT3.w, r0.w

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_2Weight_UVTransform.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

	; Transform normal by weighted matrix
	dp3     VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, VSTMP_REG_MAT0
	dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, VSTMP_REG_MAT1
	dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient, plus transform texture coordinates
; (interleaving these helps prevent stalls)
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_ACCUM.x, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0			; Light0 dir.normal
    dp3		VSTMP_REG_NORMAL_ACCUM.y, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1			; Light1 dir.normal
	dp3     VSTMP_REG_NORMAL_ACCUM.z, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2			; Light2 dir.normal

	max     VSTMP_REG_NORMAL_ACCUM.x, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_ACCUM.x	; Light0 result clamp
    max     VSTMP_REG_NORMAL_ACCUM.y, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_ACCUM.y	; Light1 result clamp
	max     VSTMP_REG_NORMAL_ACCUM.z, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_ACCUM.z	; Light2 result clamp

    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR												; Accumulate start with ambient
    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_ACCUM.x, r11					; Accumulate Light0 result modulated with Light0 color
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_ACCUM.y, r11					; Accumulate Light1 result modulated with Light1 color
	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_ACCUM.z, r11					; Accumulate Light2 result modulated with Light2 color

	dp4     oT0.x, VSIN_REG_TEXCOORDS0, VSCONST_REG_UV_MAT00								; u' = ( s * mat00.x ) + ( t * mat00.y ) + ( 1.0 * mat00.w )
	dp4     oT0.y, VSIN_REG_TEXCOORDS0, VSCONST_REG_UV_MAT01								; v' = ( s * mat01.x ) + ( t * mat01.y ) + ( 1.0 * mat01.w )
	dp4     oT1.x, VSIN_REG_TEXCOORDS1, VSCONST_REG_UV_MAT10								; u' = ( s * mat10.x ) + ( t * mat10.y ) + ( 1.0 * mat10.w )
	dp4     oT1.y, VSIN_REG_TEXCOORDS1, VSCONST_REG_UV_MAT11								; v' = ( s * mat11.x ) + ( t * mat11.y ) + ( 1.0 * mat11.w )
	dp4     oT2.x, VSIN_REG_TEXCOORDS2, VSCONST_REG_UV_MAT20								; u' = ( s * mat20.x ) + ( t * mat20.y ) + ( 1.0 * mat20.w )
	dp4     oT2.y, VSIN_REG_TEXCOORDS2, VSCONST_REG_UV_MAT21								; v' = ( s * mat21.x ) + ( t * mat21.y ) + ( 1.0 * mat21.w )
	dp4     oT3.x, VSIN_REG_TEXCOORDS3, VSCONST_REG_UV_MAT30								; u' = ( s * mat30.x ) + ( t * mat30.y ) + ( 1.0 * mat30.w )
	dp4     oT3.y, VSIN_REG_TEXCOORDS3, VSCONST_REG_UV_MAT31								; v' = ( s * mat31.x ) + ( t * mat31.y ) + ( 1.0 * mat31.w )

	mul		oD0, r11, VSIN_REG_COLOR														; Vertex color attenuation

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_3Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.z, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.z, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.z, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

	; Transform normal by weighted matrix
	dp3     VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, VSTMP_REG_MAT0
	dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, VSTMP_REG_MAT1
	dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient, plus copy texture coordinates
; (interleaving these helps prevent stalls)
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_ACCUM.x, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0			; Light0 dir.normal
    dp3		VSTMP_REG_NORMAL_ACCUM.y, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1			; Light1 dir.normal
	dp3     VSTMP_REG_NORMAL_ACCUM.z, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2			; Light2 dir.normal

	max     VSTMP_REG_NORMAL_ACCUM.x, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_ACCUM.x	; Light0 result clamp
    max     VSTMP_REG_NORMAL_ACCUM.y, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_ACCUM.y	; Light1 result clamp
	max     VSTMP_REG_NORMAL_ACCUM.z, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_ACCUM.z	; Light2 result clamp

    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR												; Accumulate start with ambient
    mov     oT0, VSIN_REG_TEXCOORDS0
    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_ACCUM.x, r11					; Accumulate Light0 result modulated with Light0 color
    mov     oT1, VSIN_REG_TEXCOORDS1
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_ACCUM.y, r11					; Accumulate Light1 result modulated with Light1 color
    mov     oT2, VSIN_REG_TEXCOORDS2
	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_ACCUM.z, r11					; Accumulate Light2 result modulated with Light2 color
    mov     oT3, VSIN_REG_TEXCOORDS3

	mul		oD0, r11, VSIN_REG_COLOR														; Vertex color attenuation

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_3Weight_SBPassThru.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.z, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.z, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.z, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

    ; Copy w
    mov		VSTMP_REG_POS_ACCUM.w, VSIN_REG_POS.w
    
;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Calculate second set of texture coordinates (into 4th slot) by transforming the world space
; position into the shadowbuffer space
;------------------------------------------------------------------------------

	dp4		oT3.x, VSTMP_REG_POS_ACCUM,	VSCONST_REG_SHADOWBUFFER_TRANSFORM_X
	dp4		oT3.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_SHADOWBUFFER_TRANSFORM_Y
	dp4		oT3.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_SHADOWBUFFER_TRANSFORM_Z
	dp4		r0.w,  VSTMP_REG_POS_ACCUM, VSCONST_REG_SHADOWBUFFER_TRANSFORM_W

	;clamp w (q) to 0
	slt		r1, c0, c0
	max		r0.w, r0.w, r1.w
	mov		oT3.w, r0.w

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_3Weight_UVTransform.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.z, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.z, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.z, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

	; Transform normal by weighted matrix
	dp3     VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, VSTMP_REG_MAT0
	dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, VSTMP_REG_MAT1
	dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient, plus copy texture coordinates
; (interleaving these helps prevent stalls)
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_ACCUM.x, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0			; Light0 dir.normal
    dp3		VSTMP_REG_NORMAL_ACCUM.y, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1			; Light1 dir.normal
	dp3     VSTMP_REG_NORMAL_ACCUM.z, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2			; Light2 dir.normal

	max     VSTMP_REG_NORMAL_ACCUM.x, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_ACCUM.x	; Light0 result clamp
    max     VSTMP_REG_NORMAL_ACCUM.y, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_ACCUM.y	; Light1 result clamp
	max     VSTMP_REG_NORMAL_ACCUM.z, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_ACCUM.z	; Light2 result clamp

    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR												; Accumulate start with ambient
    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_ACCUM.x, r11					; Accumulate Light0 result modulated with Light0 color
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_ACCUM.y, r11					; Accumulate Light1 result modulated with Light1 color
	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_ACCUM.z, r11					; Accumulate Light2 result modulated with Light2 color

	dp4     oT0.x, VSIN_REG_TEXCOORDS0, VSCONST_REG_UV_MAT00								; u' = ( s * mat00.x ) + ( t * mat00.y ) + ( 1.0 * mat00.w )
	dp4     oT0.y, VSIN_REG_TEXCOORDS0, VSCONST_REG_UV_MAT01								; v' = ( s * mat01.x ) + ( t * mat01.y ) + ( 1.0 * mat01.w )
	dp4     oT1.x, VSIN_REG_TEXCOORDS1, VSCONST_REG_UV_MAT10								; u' = ( s * mat10.x ) + ( t * mat10.y ) + ( 1.0 * mat10.w )
	dp4     oT1.y, VSIN_REG_TEXCOORDS1, VSCONST_REG_UV_MAT11								; v' = ( s * mat11.x ) + ( t * mat11.y ) + ( 1.0 * mat11.w )
	dp4     oT2.x, VSIN_REG_TEXCOORDS2, VSCONST_REG_UV_MAT20								; u' = ( s * mat20.x ) + ( t * mat20.y ) + ( 1.0 * mat20.w )
	dp4     oT2.y, VSIN_REG_TEXCOORDS2, VSCONST_REG_UV_MAT21								; v' = ( s * mat21.x ) + ( t * mat21.y ) + ( 1.0 * mat21.w )
	dp4     oT3.x, VSIN_REG_TEXCOORDS3, VSCONST_REG_UV_MAT30								; u' = ( s * mat30.x ) + ( t * mat30.y ) + ( 1.0 * mat30.w )
	dp4     oT3.y, VSIN_REG_TEXCOORDS3, VSCONST_REG_UV_MAT31								; v' = ( s * mat31.x ) + ( t * mat31.y ) + ( 1.0 * mat31.w )

	mul		oD0, r11, VSIN_REG_COLOR														; Vertex color attenuation

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_4Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.z, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.z, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.z, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 4
    mov     a0.x, VSIN_REG_INDICES.w

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.w, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.w, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.w, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

	; Transform normal by weighted matrix
	dp3     VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, VSTMP_REG_MAT0
	dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, VSTMP_REG_MAT1
	dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient
;------------------------------------------------------------------------------

    ; DP normal & light0 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_TMP.w

	; This is where the ambient gets added in.
    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR

    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light1 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_TMP.w
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light2 dir clamp then scale by light color
	dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2
	max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_TMP.w
	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11

	; Vertex color attenuation
	mul		oD0, r11, VSIN_REG_COLOR

;------------------------------------------------------------------------------
; Copy texture coordinates
;------------------------------------------------------------------------------

    mov     oT0, VSIN_REG_TEXCOORDS0
    mov     oT1, VSIN_REG_TEXCOORDS1
    mov     oT2, VSIN_REG_TEXCOORDS2
    mov     oT3, VSIN_REG_TEXCOORDS3

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_4Weight_SBPassThru.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.z, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.z, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.z, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 4
    mov     a0.x, VSIN_REG_INDICES.w

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.w, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.w, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.w, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

    ; Copy w
    mov		VSTMP_REG_POS_ACCUM.w, VSIN_REG_POS.w
    
;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Calculate second set of texture coordinates (into 4th slot) by transforming the world space
; position into the shadowbuffer space
;------------------------------------------------------------------------------

	dp4		oT3.x, VSTMP_REG_POS_ACCUM,	VSCONST_REG_SHADOWBUFFER_TRANSFORM_X
	dp4		oT3.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_SHADOWBUFFER_TRANSFORM_Y
	dp4		oT3.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_SHADOWBUFFER_TRANSFORM_Z
	dp4		r0.w,  VSTMP_REG_POS_ACCUM, VSCONST_REG_SHADOWBUFFER_TRANSFORM_W

	;clamp w (q) to 0
	slt		r1, c0, c0
	max		r0.w, r0.w, r1.w
	mov		oT3.w, r0.w

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_Specular_1Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]
    mov		VSTMP_REG_POS_TMP.w, VSIN_REG_POS.w

    ; Transform normal
    dp3		VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

	; No need to scale by results by vertex weights, since this is a one-weight shader so the weight
	; is assumed to be 1.0

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dp4		oPos.x, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_X
    dp4		oPos.y, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_Y
    dp4		oPos.z, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_Z
    dp4     oPos.w, VSTMP_REG_POS_TMP, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient, plus copy texture coordinates
; (interleaving these helps prevent stalls)
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_ACCUM.x, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0			; Light0 dir.normal
    dp3		VSTMP_REG_NORMAL_ACCUM.y, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1			; Light1 dir.normal
	dp3     VSTMP_REG_NORMAL_ACCUM.z, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2			; Light2 dir.normal

	max     VSTMP_REG_NORMAL_ACCUM.x, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_ACCUM.x	; Light0 result clamp
    max     VSTMP_REG_NORMAL_ACCUM.y, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_ACCUM.y	; Light1 result clamp
	max     VSTMP_REG_NORMAL_ACCUM.z, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_ACCUM.z	; Light2 result clamp

    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR												; Accumulate start with ambient
    mov     oT0, VSIN_REG_TEXCOORDS0
    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_ACCUM.x, r11					; Accumulate Light0 result modulated with Light0 color
    mov     oT1, VSIN_REG_TEXCOORDS1
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_ACCUM.y, r11					; Accumulate Light1 result modulated with Light1 color
    mov     oT2, VSIN_REG_TEXCOORDS2
	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_ACCUM.z, r11					; Accumulate Light2 result modulated with Light2 color
    mov     oT3, VSIN_REG_TEXCOORDS3

	mul		oD0, r11, VSIN_REG_COLOR														; Vertex color attenuation

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; Specular calculation
;------------------------------------------------------------------------------

	; Calculate vector to eye (V)
	sub		r7.xyz, VSCONST_REG_CAM_POS, VSTMP_REG_POS_TMP
	
	; Store Light0 dir.normal off into r0
	mov		r0.x, VSTMP_REG_NORMAL_ACCUM.x

	; Normalize V
	dp3		r7.w, r7.xyz, r7.xyz
	rsq		r1.x, r7.w
	mul		r7, r7, r1.x

	; Calculate H = L + V
	add		r7, -VSCONST_REG_LIGHT_DIR0, r7

	; Normalize H
	dp3		r7.w, r7.xyz, r7.xyz
	rsq		r1.x, r7.w
	mul		r7, r7, r1.x

	; Calculate N.H (don't worry about clamping to zero, since the lit instruction below does this)
	dp3		r0.y, VSTMP_REG_NORMAL_TMP, r7

	; Move the power term over into r0
	mov		r0.w, VSCONST_REG_SPECULAR_COLOR.w
	
	; Specular lighting calc - (N.H)^pow
	lit		r1.z, r0

	; Modulate specular color by specular result.
	mul		oD1.xyz, r1.z, VSCONST_REG_SPECULAR_COLOR.xyz

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_Specular_2Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

	; Transform normal by weighted matrix
	dp3     VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, VSTMP_REG_MAT0
	dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, VSTMP_REG_MAT1
	dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient, plus copy texture coordinates
; (interleaving these helps prevent stalls)
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_ACCUM.x, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0			; Light0 dir.normal
    dp3		VSTMP_REG_NORMAL_ACCUM.y, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1			; Light1 dir.normal
	dp3     VSTMP_REG_NORMAL_ACCUM.z, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2			; Light2 dir.normal

	max     VSTMP_REG_NORMAL_ACCUM.x, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_ACCUM.x	; Light0 result clamp
    max     VSTMP_REG_NORMAL_ACCUM.y, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_ACCUM.y	; Light1 result clamp
	max     VSTMP_REG_NORMAL_ACCUM.z, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_ACCUM.z	; Light2 result clamp

    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR												; Accumulate start with ambient
    mov     oT0, VSIN_REG_TEXCOORDS0
    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_ACCUM.x, r11					; Accumulate Light0 result modulated with Light0 color
    mov     oT1, VSIN_REG_TEXCOORDS1
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_ACCUM.y, r11					; Accumulate Light1 result modulated with Light1 color
    mov     oT2, VSIN_REG_TEXCOORDS2
	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_ACCUM.z, r11					; Accumulate Light2 result modulated with Light2 color
    mov     oT3, VSIN_REG_TEXCOORDS3

	mul		oD0, r11, VSIN_REG_COLOR														; Vertex color attenuation

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; Specular calculation
;------------------------------------------------------------------------------

	; Calculate vector to eye (V)
	sub		r7.xyz, VSCONST_REG_CAM_POS, VSTMP_REG_POS_ACCUM
	
	; Store Light0 dir.normal off into r0
	mov		r0.x, VSTMP_REG_NORMAL_ACCUM.x

	; Normalize V
	dp3		r7.w, r7.xyz, r7.xyz
	rsq		r1.x, r7.w
	mul		r7, r7, r1.x

	; Calculate H = L + V
	add		r7, -VSCONST_REG_LIGHT_DIR0, r7

	; Normalize H
	dp3		r7.w, r7.xyz, r7.xyz
	rsq		r1.x, r7.w
	mul		r7, r7, r1.x

	; Calculate N.H (don't worry about clamping to zero, since the lit instruction below does this)
	dp3		r0.y, VSTMP_REG_NORMAL_TMP, r7

	; Move the power term over into r0
	mov		r0.w, VSCONST_REG_SPECULAR_COLOR.w
	
	; Specular lighting calc - (N.H)^pow
	lit		r1.z, r0

	; Modulate specular color by specular result.
	mul		oD1.xyz, r1.z, VSCONST_REG_SPECULAR_COLOR.xyz

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_Specular_3Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.z, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.z, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.z, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

	; Transform normal by weighted matrix
	dp3     VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, VSTMP_REG_MAT0
	dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, VSTMP_REG_MAT1
	dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient, plus copy texture coordinates
; (interleaving these helps prevent stalls)
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_ACCUM.x, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0			; Light0 dir.normal
    dp3		VSTMP_REG_NORMAL_ACCUM.y, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1			; Light1 dir.normal
	dp3     VSTMP_REG_NORMAL_ACCUM.z, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2			; Light2 dir.normal

	max     VSTMP_REG_NORMAL_ACCUM.x, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_ACCUM.x	; Light0 result clamp
    max     VSTMP_REG_NORMAL_ACCUM.y, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_ACCUM.y	; Light1 result clamp
	max     VSTMP_REG_NORMAL_ACCUM.z, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_ACCUM.z	; Light2 result clamp

    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR												; Accumulate start with ambient
    mov     oT0, VSIN_REG_TEXCOORDS0
    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_ACCUM.x, r11					; Accumulate Light0 result modulated with Light0 color
    mov     oT1, VSIN_REG_TEXCOORDS1
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_ACCUM.y, r11					; Accumulate Light1 result modulated with Light1 color
    mov     oT2, VSIN_REG_TEXCOORDS2
	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_ACCUM.z, r11					; Accumulate Light2 result modulated with Light2 color
    mov     oT3, VSIN_REG_TEXCOORDS3

	mul		oD0, r11, VSIN_REG_COLOR														; Vertex color attenuation

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; Specular calculation
;------------------------------------------------------------------------------

	; Calculate vector to eye (V)
	sub		r7.xyz, VSCONST_REG_CAM_POS, VSTMP_REG_POS_ACCUM
	
	; Store Light0 dir.normal off into r0
	mov		r0.x, VSTMP_REG_NORMAL_ACCUM.x

	; Normalize V
	dp3		r7.w, r7.xyz, r7.xyz
	rsq		r1.x, r7.w
	mul		r7, r7, r1.x

	; Calculate H = L + V
	add		r7, -VSCONST_REG_LIGHT_DIR0, r7

	; Normalize H
	dp3		r7.w, r7.xyz, r7.xyz
	rsq		r1.x, r7.w
	mul		r7, r7, r1.x

	; Calculate N.H (don't worry about clamping to zero, since the lit instruction below does this)
	dp3		r0.y, VSTMP_REG_NORMAL_TMP, r7

	; Move the power term over into r0
	mov		r0.w, VSCONST_REG_SPECULAR_COLOR.w
	
	; Specular lighting calc - (N.H)^pow
	lit		r1.z, r0

	; Modulate specular color by specular result.
	mul		oD1.xyz, r1.z, VSCONST_REG_SPECULAR_COLOR.xyz

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVS_VXC_Specular_4Weight.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.z, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.z, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.z, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 4
    mov     a0.x, VSIN_REG_INDICES.w

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.w, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.w, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.w, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

	; Transform normal by weighted matrix
	dp3     VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, VSTMP_REG_MAT0
	dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, VSTMP_REG_MAT1
	dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, -r12.w

;------------------------------------------------------------------------------
; Multiple directional lights plus ambient
;------------------------------------------------------------------------------

    ; DP normal & light0 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_TMP.w

	; For specular calculations further down, save this result off into r0.x
	mov		r0.x, VSTMP_REG_NORMAL_TMP.w

	; This is where the ambient gets added in.
    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR

    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light1 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_TMP.w
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light2 dir clamp then scale by light color (third light currently deactivated).
;	dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2
;	max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_TMP.w
;	mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11

	; Vertex color attenuation
	mul		oD0, r11, VSIN_REG_COLOR

;------------------------------------------------------------------------------
; Specular calculation
;------------------------------------------------------------------------------

	; Calculate vector to eye (V)
	sub		r7.xyz, VSCONST_REG_CAM_POS, VSTMP_REG_POS_ACCUM
	
	; Normalize V
	dp3		r7.w, r7.xyz, r7.xyz
	rsq		r1.x, r7.w
	mul		r7, r7, r1.x

	; Calculate H = L + V
	add		r7, -VSCONST_REG_LIGHT_DIR0, r7

	; Normalize H
	dp3		r7.w, r7.xyz, r7.xyz
	rsq		r1.x, r7.w
	mul		r7, r7, r1.x

	; Calculate N.H (don't worry about clamping to zero, since the lit instruction below does this)
	dp3		r0.y, VSTMP_REG_NORMAL_TMP, r7

	; Move the power term over into r0
	mov		r0.w, VSCONST_REG_SPECULAR_COLOR.w
	
	; Specular lighting calc - (N.H)^pow
	lit		r1.z, r0

	; Modulate specular color by specular result.
;	mov		oD1.xyz, r1.z
	mul		oD1.xyz, r1.z, VSCONST_REG_SPECULAR_COLOR.xyz

;------------------------------------------------------------------------------
; Copy texture coordinates
;------------------------------------------------------------------------------

    mov     oT0, VSIN_REG_TEXCOORDS0
    mov     oT1, VSIN_REG_TEXCOORDS1
    mov     oT2, VSIN_REG_TEXCOORDS2
    mov     oT3, VSIN_REG_TEXCOORDS3

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVertexShader0.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight
    mul     VSTMP_REG_POS_ACCUM.xyz, VSTMP_REG_POS_TMP.xyz, VSIN_REG_WEIGHTS.x

    ; Transform normal
    dp3		VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed normal by weight
    mul     VSTMP_REG_NORMAL_ACCUM.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSIN_REG_WEIGHTS.x

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight and add to previous
    mad     VSTMP_REG_POS_ACCUM.xyz, VSTMP_REG_POS_TMP.xyz, VSIN_REG_WEIGHTS.y, VSTMP_REG_POS_ACCUM.xyz

    ; Transform normal
    dp3		VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight
    mad     VSTMP_REG_NORMAL_ACCUM.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSIN_REG_WEIGHTS.y, VSTMP_REG_NORMAL_ACCUM.xyz

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight and add to previous
    mad     VSTMP_REG_POS_ACCUM.xyz, VSTMP_REG_POS_TMP.xyz, VSIN_REG_WEIGHTS.z, VSTMP_REG_POS_ACCUM.xyz

    ; Transform normal
    dp3		VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight
    mad     VSTMP_REG_NORMAL_ACCUM.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSIN_REG_WEIGHTS.z, VSTMP_REG_NORMAL_ACCUM.xyz

    ; Copy w
    mov		VSTMP_REG_POS_ACCUM.w, VSIN_REG_POS.w

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dp4		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dp4		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dp4		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dp4     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, r12.w

;------------------------------------------------------------------------------
; Transform normal by combined camera & projection matrix
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_TMP.x, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_X
    dp3		VSTMP_REG_NORMAL_TMP.y, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_Y
    dp3		VSTMP_REG_NORMAL_TMP.z, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_Z

;------------------------------------------------------------------------------
; Single directional light + Ambient
;------------------------------------------------------------------------------

    ; Normalize
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSTMP_REG_NORMAL_TMP
    rsq     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP.w
    mul     VSTMP_REG_NORMAL_TMP.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSTMP_REG_NORMAL_TMP.w

    ; DP normal & light0 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_TMP.w

	; This is where the ambient gets added in.
    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR

    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light1 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_TMP.w
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light2 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_TMP.w
;    mad     oD0, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11
    mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11

	; Material color attenuation
	mul		oD0, r11, VSCONST_REG_MATERIAL_COLOR

;------------------------------------------------------------------------------
; Copy texture coordinates
;------------------------------------------------------------------------------

    mov     oT0, VSIN_REG_TEXCOORDS0

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVertexShader1.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight
    mul     VSTMP_REG_POS_ACCUM.xyz, VSTMP_REG_POS_TMP.xyz, VSIN_REG_WEIGHTS.x

    ; Transform normal
    dp3		VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed normal by weight
    mul     VSTMP_REG_NORMAL_ACCUM.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSIN_REG_WEIGHTS.x

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight and add to previous
    mad     VSTMP_REG_POS_ACCUM.xyz, VSTMP_REG_POS_TMP.xyz, VSIN_REG_WEIGHTS.y, VSTMP_REG_POS_ACCUM.xyz

    ; Transform normal
    dp3		VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight
    mad     VSTMP_REG_NORMAL_ACCUM.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSIN_REG_WEIGHTS.y, VSTMP_REG_NORMAL_ACCUM.xyz

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

    ; Transform position
    dp4		VSTMP_REG_POS_TMP.x, VSIN_REG_POS, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.y, VSIN_REG_POS, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp4		VSTMP_REG_POS_TMP.z, VSIN_REG_POS, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight and add to previous
    mad     VSTMP_REG_POS_ACCUM.xyz, VSTMP_REG_POS_TMP.xyz, VSIN_REG_WEIGHTS.z, VSTMP_REG_POS_ACCUM.xyz

    ; Transform normal
    dp3		VSTMP_REG_NORMAL_TMP.x, VSIN_REG_NORMAL, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.y, VSIN_REG_NORMAL, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
    dp3		VSTMP_REG_NORMAL_TMP.z, VSIN_REG_NORMAL, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

    ; Scale transformed point by weight
    mad     VSTMP_REG_NORMAL_ACCUM.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSIN_REG_WEIGHTS.z, VSTMP_REG_NORMAL_ACCUM.xyz

    ; Copy w
    mov		VSTMP_REG_POS_ACCUM.w, VSIN_REG_POS.w

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dp4		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dp4		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dp4		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dp4     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W

;------------------------------------------------------------------------------
; Deal with fog value (r12 shadows oPos)...
;------------------------------------------------------------------------------

	mov		oFog.x, r12.w

;------------------------------------------------------------------------------
; Transform normal by combined camera & projection matrix
;------------------------------------------------------------------------------

    dp3		VSTMP_REG_NORMAL_TMP.x, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_X
    dp3		VSTMP_REG_NORMAL_TMP.y, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_Y
    dp3		VSTMP_REG_NORMAL_TMP.z, VSTMP_REG_NORMAL_ACCUM, VSCONST_REG_WORLD_TRANSFORM_Z

;------------------------------------------------------------------------------
; Single directional light + Ambient
;------------------------------------------------------------------------------

    ; Normalize
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSTMP_REG_NORMAL_TMP
    rsq     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP.w
    mul     VSTMP_REG_NORMAL_TMP.xyz, VSTMP_REG_NORMAL_TMP.xyz, VSTMP_REG_NORMAL_TMP.w

    ; DP normal & light0 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR0
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR0.w, -VSTMP_REG_NORMAL_TMP.w

	; This is where the ambient gets added in.
    mov     r11, VSCONST_REG_AMB_LIGHT_COLOR

    mad     r11, VSCONST_REG_LIGHT_COLOR0, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light1 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR1
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR1.w, -VSTMP_REG_NORMAL_TMP.w
    mad     r11, VSCONST_REG_LIGHT_COLOR1, VSTMP_REG_NORMAL_TMP.w, r11

    ; DP normal & light2 dir clamp then scale by light color
    dp3     VSTMP_REG_NORMAL_TMP.w, VSTMP_REG_NORMAL_TMP, VSCONST_REG_LIGHT_DIR2
    max     VSTMP_REG_NORMAL_TMP.w, VSCONST_REG_LIGHT_DIR2.w, -VSTMP_REG_NORMAL_TMP.w
;    mad     oD0, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11
    mad     r11, VSCONST_REG_LIGHT_COLOR2, VSTMP_REG_NORMAL_TMP.w, r11

	; Vertex color attenuation
	mul		oD0, r11, VSIN_REG_COLOR

;------------------------------------------------------------------------------
; Copy texture coordinates
;------------------------------------------------------------------------------

    mov     oT0, VSIN_REG_TEXCOORDS0

;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/WeightedMeshVertexShader_SBWrite.vsh
================================================
xvs.1.1

#include "anim_vertdefs.h"

#pragma screenspace

;------------------------------------------------------------------------------
; Bone space transforms
;------------------------------------------------------------------------------

    ; Get matrix index 1
    mov     a0.x, VSIN_REG_INDICES.x

	; Weight matrix
	mul		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.x, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.x, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET]
	mul		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.x, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET]

;------------------------------------------------------------------------------

    ; Get matrix index 2
    mov     a0.x, VSIN_REG_INDICES.y

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.y, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.y, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.y, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

;------------------------------------------------------------------------------

    ; Get matrix index 3
    mov     a0.x, VSIN_REG_INDICES.z

	; Weight matrix
	mad		VSTMP_REG_MAT0, VSIN_REG_WEIGHTS.z, c[0 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT0
	mad		VSTMP_REG_MAT1, VSIN_REG_WEIGHTS.z, c[1 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT1
	mad		VSTMP_REG_MAT2, VSIN_REG_WEIGHTS.z, c[2 + a0.x VSCONST_REG_MATRIX_OFFSET], VSTMP_REG_MAT2

	; Transform position by weighted matrix
    dp4     VSTMP_REG_POS_ACCUM.x, VSIN_REG_POS, VSTMP_REG_MAT0
	dp4		VSTMP_REG_POS_ACCUM.y, VSIN_REG_POS, VSTMP_REG_MAT1
	dp4		VSTMP_REG_POS_ACCUM.z, VSIN_REG_POS, VSTMP_REG_MAT2

;------------------------------------------------------------------------------
; Combined camera & projection matrix
;------------------------------------------------------------------------------

    dph		oPos.x, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_X
    dph		oPos.y, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Y
    dph		oPos.z, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_Z
    dph     oPos.w, VSTMP_REG_POS_ACCUM, VSCONST_REG_TRANSFORM_W


;------------------------------------------------------------------------------
; oPos to screenspace transformation
;------------------------------------------------------------------------------

	mul		oPos.xyz, r12, c94			; scale
	+ rcc	r1.x, r12.w					; compute 1/w
	mad		oPos.xyz, r12, r1.x, c95	; scale by 1/w, add offset


================================================
FILE: Code/Gfx/XBox/NX/anim.cpp
================================================
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "nx_init.h"
#include "mesh.h"
#include "scene.h"
#include "anim.h"
#include "anim_vertdefs.h"

#include "WeightedMeshVS_VXC_1Weight.h"
#include "WeightedMeshVS_VXC_2Weight.h"
#include "WeightedMeshVS_VXC_3Weight.h"

#include "WeightedMeshVS_VXC_Specular_1Weight.h"
#include "WeightedMeshVS_VXC_Specular_2Weight.h"
#include "WeightedMeshVS_VXC_Specular_3Weight.h"

#include "WeightedMeshVS_VXC_1Weight_UVTransform.h"
#include "WeightedMeshVS_VXC_2Weight_UVTransform.h"
#include "WeightedMeshVS_VXC_3Weight_UVTransform.h"

#include "WeightedMeshVS_VXC_1Weight_SBPassThru.h"
#include "WeightedMeshVS_VXC_2Weight_SBPassThru.h"
#include "WeightedMeshVS_VXC_3Weight_SBPassThru.h"

#include "WeightedMeshVertexShader_SBWrite.h"
#include "ShadowBufferStaticGeomVS.h"
#include "BillboardScreenAlignedVS.h"
#include "ParticleFlatVS.h"
#include "ParticleNewFlatVS.h"
#include "ParticleNewFlatPointSpriteVS.h"

DWORD WeightedMeshVS_VXC_1Weight;
DWORD WeightedMeshVS_VXC_2Weight;
DWORD WeightedMeshVS_VXC_3Weight;
DWORD WeightedMeshVS_VXC_Specular_1Weight;
DWORD WeightedMeshVS_VXC_Specular_2Weight;
DWORD WeightedMeshVS_VXC_Specular_3Weight;
DWORD WeightedMeshVS_VXC_1Weight_UVTransform;
DWORD WeightedMeshVS_VXC_2Weight_UVTransform;
DWORD WeightedMeshVS_VXC_3Weight_UVTransform;
DWORD WeightedMeshVertexShader_SBWrite;
DWORD WeightedMeshVS_VXC_1Weight_SBPassThru;
DWORD WeightedMeshVS_VXC_2Weight_SBPassThru;
DWORD WeightedMeshVS_VXC_3Weight_SBPassThru;
DWORD WeightedMeshVertexShader_VXC_SBPassThru;
DWORD BillboardScreenAlignedVS;
DWORD ParticleFlatVS;
DWORD ParticleNewFlatVS;
DWORD ParticleNewFlatPointSpriteVS;
DWORD ShadowBufferStaticGeomVS;

namespace NxXbox
{
	// Vertex color attenuation, 4 sets of tex coords.
	static DWORD WeightedMeshVertexShaderVertColUV4Decl[] = {
	D3DVSD_STREAM( 0 ),
	D3DVSD_REG( VSD_REG_POS,		D3DVSDT_FLOAT3 ),		// Position.
	D3DVSD_REG( VSD_REG_WEIGHTS,	D3DVSDT_NORMPACKED3 ),	// Weights.
	D3DVSD_REG( VSD_REG_INDICES,	D3DVSDT_SHORT4 ),		// Indices.
	D3DVSD_REG( VSD_REG_NORMAL,		D3DVSDT_NORMPACKED3 ),	// Normals.
	D3DVSD_REG( VSD_REG_COLOR,		D3DVSDT_D3DCOLOR ),		// Diffuse color.
	D3DVSD_REG( VSD_REG_TEXCOORDS0,	D3DVSDT_FLOAT2 ),		// Texture coordinates 0.
	D3DVSD_REG( VSD_REG_TEXCOORDS1,	D3DVSDT_FLOAT2 ),		// Texture coordinates 1.
	D3DVSD_REG( VSD_REG_TEXCOORDS2,	D3DVSDT_FLOAT2 ),		// Texture coordinates 2.
	D3DVSD_REG( VSD_REG_TEXCOORDS3,	D3DVSDT_FLOAT2 ),		// Texture coordinates 3.
	D3DVSD_END() };
	
	// Billboards.
	static DWORD BillboardVSDecl[] = {
	D3DVSD_STREAM( 0 ),
	D3DVSD_REG( 0,	D3DVSDT_FLOAT3 ),		// Position (actually pivot position).
	D3DVSD_REG( 1,	D3DVSDT_FLOAT3 ),		// Normal (actually position of point relative to pivot).
	D3DVSD_REG( 2,	D3DVSDT_D3DCOLOR ),		// Diffuse color.
	D3DVSD_REG( 3,	D3DVSDT_FLOAT2 ),		// Texture coordinates 0.
	D3DVSD_REG( 4,	D3DVSDT_FLOAT2 ),		// Texture coordinates 1.
	D3DVSD_REG( 5,	D3DVSDT_FLOAT2 ),		// Texture coordinates 2.
	D3DVSD_REG( 6,	D3DVSDT_FLOAT2 ),		// Texture coordinates 3.
	D3DVSD_END() };

	// Particles.
	static DWORD ParticleFlatVSDecl[] = {
	D3DVSD_STREAM( 0 ),
	D3DVSD_REG( 0,	D3DVSDT_D3DCOLOR ),		// Diffuse color (start)
	D3DVSD_REG( 1,	D3DVSDT_D3DCOLOR ),		// Diffuse color (end)
	D3DVSD_REG( 2,	D3DVSDT_SHORT2 ),		// Indices.
	D3DVSD_END() };

	// New, Ps2 style particles using PointSprites.
	static DWORD NewParticleFlatVSDecl[] = {
	D3DVSD_STREAM( 0 ),
	D3DVSD_REG( 0,	D3DVSDT_FLOAT4 ),		// Random 4-element 'R' vector.
	D3DVSD_REG( 1,	D3DVSDT_FLOAT2 ),		// Time and color interpolator.
	D3DVSD_REG( 2,	D3DVSDT_D3DCOLOR ),		// Diffuse color (start)
	D3DVSD_REG( 3,	D3DVSDT_D3DCOLOR ),		// Diffuse color (end)
	D3DVSD_END() };

	// Shadow buffer, static geom.
	static DWORD ShadowBufferStaticGeomVSDecl[] = {
	D3DVSD_STREAM( 0 ),
	D3DVSD_REG( 0,	D3DVSDT_FLOAT3 ),		// Position.
	D3DVSD_REG( 1,	D3DVSDT_D3DCOLOR ),		// Diffuse color.
	D3DVSD_REG( 2,	D3DVSDT_FLOAT2 ),		// Texture coordinates 0.
	D3DVSD_REG( 3,	D3DVSDT_FLOAT2 ),		// Texture coordinates 1.
	D3DVSD_REG( 4,	D3DVSDT_FLOAT2 ),		// Texture coordinates 2.
	D3DVSD_END() };
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
DWORD GetVertexShader( bool vertex_colors, bool specular, uint32 max_weights_used )
{
	Dbg_Assert( max_weights_used > 0 );
	
	if( vertex_colors )
	{
		if( max_weights_used == 1 )
		{
			return ( specular ) ? WeightedMeshVS_VXC_Specular_1Weight : WeightedMeshVS_VXC_1Weight;
		}
		else if( max_weights_used == 2 )
		{
			return ( specular ) ? WeightedMeshVS_VXC_Specular_2Weight : WeightedMeshVS_VXC_2Weight;
		}
		else if( max_weights_used == 3 )
		{
			return ( specular ) ? WeightedMeshVS_VXC_Specular_3Weight : WeightedMeshVS_VXC_3Weight;
		}
	}

	Dbg_Assert( 0 );
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CreateWeightedMeshVertexShaders( void )
{
	static bool created_shaders = false;

	if( !created_shaders )
	{
		created_shaders = true;

		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_1WeightVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_1Weight,
													0 ))
		{
			exit( 0 );
		}
		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_2WeightVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_2Weight,
													0 ))
		{
			exit( 0 );
		}
		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_3WeightVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_3Weight,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_Specular_1WeightVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_Specular_1Weight,
													0 ))
		{
			exit( 0 );
		}
		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_Specular_2WeightVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_Specular_2Weight,
													0 ))
		{
			exit( 0 );
		}
		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_Specular_3WeightVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_Specular_3Weight,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_1Weight_UVTransformVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_1Weight_UVTransform,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_2Weight_UVTransformVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_2Weight_UVTransform,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_3Weight_UVTransformVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_3Weight_UVTransform,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVertexShader_SBWriteVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVertexShader_SBWrite,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_1Weight_SBPassThruVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_1Weight_SBPassThru,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_2Weight_SBPassThruVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_2Weight_SBPassThru,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	WeightedMeshVertexShaderVertColUV4Decl,
													dwWeightedMeshVS_VXC_3Weight_SBPassThruVertexShader,	// Defined in the header file from xsasm.
													&WeightedMeshVS_VXC_3Weight_SBPassThru,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	BillboardVSDecl,
													dwBillboardScreenAlignedVSVertexShader,					// Defined in the header file from xsasm.
													&BillboardScreenAlignedVS,
													0 ))
		{
			exit( 0 );
		}


		if( D3D_OK != D3DDevice_CreateVertexShader(	ParticleFlatVSDecl,
													dwParticleFlatVSVertexShader,							// Defined in the header file from xsasm.
													&ParticleFlatVS,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	ParticleFlatVSDecl,
													dwParticleNewFlatVSVertexShader,						// Defined in the header file from xsasm.
													&ParticleNewFlatVS,
													0 ))
		{
			exit( 0 );
		}

		if( D3D_OK != D3DDevice_CreateVertexShader(	NewParticleFlatVSDecl,
													dwParticleNewFlatPointSpriteVSVertexShader,				// Defined in the header file from xsasm.
													&ParticleNewFlatPointSpriteVS,
													0 ))
		{
			exit( 0 );
		}
		if( D3D_OK != D3DDevice_CreateVertexShader(	ShadowBufferStaticGeomVSDecl,
													dwShadowBufferStaticGeomVSVertexShader,					// Defined in the header file from xsasm.
													&ShadowBufferStaticGeomVS,
													0 ))
		{
			exit( 0 );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void setup_weighted_mesh_vertex_shader( void *p_root_matrix, void *p_bone_matrices, int num_bone_matrices )
{
	XGMATRIX	dest_matrix;
	XGMATRIX	inverse_view_matrix;
	XGMATRIX	temp_matrix;
	XGMATRIX	projMatrix;
	XGMATRIX	viewMatrix;
	XGMATRIX	worldMatrix;

	// Projection matrix.
	XGMatrixTranspose( &projMatrix, &EngineGlobals.projection_matrix );
	
	// View matrix.
	XGMatrixTranspose( &viewMatrix, &EngineGlobals.view_matrix );
    viewMatrix.m[3][0] = 0.0f;
    viewMatrix.m[3][1] = 0.0f;
    viewMatrix.m[3][2] = 0.0f;
    viewMatrix.m[3][3] = 1.0f;
	
	// World space transformation matrix. (3x3 rotation plus 3 element translation component).
	worldMatrix.m[0][0] = ((float*)p_root_matrix )[0];
	worldMatrix.m[0][1] = ((float*)p_root_matrix )[1];
	worldMatrix.m[0][2] = ((float*)p_root_matrix )[2];
	worldMatrix.m[0][3] = ((float*)p_root_matrix )[3];
	worldMatrix.m[1][0] = ((float*)p_root_matrix )[4];
	worldMatrix.m[1][1] = ((float*)p_root_matrix )[5];
	worldMatrix.m[1][2] = ((float*)p_root_matrix )[6];
	worldMatrix.m[1][3] = ((float*)p_root_matrix )[7];
	worldMatrix.m[2][0] = ((float*)p_root_matrix )[8];
	worldMatrix.m[2][1] = ((float*)p_root_matrix )[9];
	worldMatrix.m[2][2] = ((float*)p_root_matrix )[10];
	worldMatrix.m[2][3] = ((float*)p_root_matrix )[11];
	worldMatrix.m[3][0] = 0.0f;
	worldMatrix.m[3][1] = 0.0f;
	worldMatrix.m[3][2] = 0.0f;
	worldMatrix.m[3][3] = 1.0f;

	// Calculate composite world->view->projection matrix.
	XGMatrixMultiply( &temp_matrix, &viewMatrix, &worldMatrix );
	XGMatrixMultiply( &dest_matrix, &projMatrix, &temp_matrix );

	// Switch to 192 constant mode, removing the lock on the reserved constants c-38 and c-37.
//	D3DDevice_SetShaderConstantMode( D3DSCM_192CONSTANTS | D3DSCM_NORESERVEDCONSTANTS );

	// Load up the combined world, camera & projection matrix.
	D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_TRANSFORM_OFFSET, (void*)&dest_matrix, VSCONST_REG_TRANSFORM_SIZE );
	D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_WORLD_TRANSFORM_OFFSET,	(void*)&worldMatrix, VSCONST_REG_WORLD_TRANSFORM_SIZE );

	// We want to transform the light directions by the inverse of the world transform - this means we don't have to transform
	// the normal by the world transform for every vertex in the vertex shader. However, the function D3DXVec3TransformNormal
	// (used below) does the inverse transform for us, so need to actually figure the inverse...
//	XGMATRIX inverse_world_transform = worldMatrix;
//	D3DXMatrixInverse( &inverse_world_transform, NULL, &worldMatrix );

	float directional_light_color[24];
	CopyMemory( directional_light_color, EngineGlobals.directional_light_color, sizeof( float ) * 24 );
	
	XGVec3TransformNormal((XGVECTOR3*)&directional_light_color[0],	(XGVECTOR3*)&EngineGlobals.directional_light_color[0], &worldMatrix );
	XGVec3TransformNormal((XGVECTOR3*)&directional_light_color[8],	(XGVECTOR3*)&EngineGlobals.directional_light_color[8], &worldMatrix );
	XGVec3TransformNormal((XGVECTOR3*)&directional_light_color[16],	(XGVECTOR3*)&EngineGlobals.directional_light_color[16], &worldMatrix );
	
	XGVec3Normalize((XGVECTOR3*)&directional_light_color[0], (XGVECTOR3*)&directional_light_color[0] ); 
	XGVec3Normalize((XGVECTOR3*)&directional_light_color[8], (XGVECTOR3*)&directional_light_color[8] ); 
	XGVec3Normalize((XGVECTOR3*)&directional_light_color[16], (XGVECTOR3*)&directional_light_color[16] ); 

	// Load up the directional light data.
	D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_DIR_LIGHT_OFFSET, (void*)directional_light_color, 6 );

	// Load up the ambient light color.
	D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_AMB_LIGHT_OFFSET, (void*)EngineGlobals.ambient_light_color, 1 );
	
	// Calculate and load up the model-relative camera position.
	EngineGlobals.model_relative_cam_position = XGVECTOR3( EngineGlobals.cam_position.x - worldMatrix.m[0][3],
														   EngineGlobals.cam_position.y - worldMatrix.m[1][3],
														   EngineGlobals.cam_position.z - worldMatrix.m[2][3] );
	XGVec3TransformNormal( &EngineGlobals.model_relative_cam_position, &EngineGlobals.model_relative_cam_position, &worldMatrix );

	float specular_attribs[4] = { EngineGlobals.model_relative_cam_position.x,
								  EngineGlobals.model_relative_cam_position.y,
								  EngineGlobals.model_relative_cam_position.z,
								  0.0f };
	D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_CAM_POS_OFFSET, (void*)&specular_attribs, 1 );

	// Safety check here to limit number of bones to that available.
	if( num_bone_matrices > 55 )
	{
		num_bone_matrices = 55;
	}

	DWORD*	p_bone_element	= (DWORD*)p_bone_matrices;

	// Begin state block to set vertex shader constants for bone transforms.
	DWORD *p_push;
	EngineGlobals.p_Device->BeginState(( num_bone_matrices * ( 12 + 1 )) + 3, &p_push );

	// 1 here isn't the parameter for SET_TRANSFORM_CONSTANT_LOAD; rather, it's the number of dwords written to that register.
	p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_TRANSFORM_CONSTANT_LOAD, 1 ); 

	// Here is the actual parameter for SET_TRANSFORM_CONSTANT_LOAD. Always add 96 to the constant register.
	p_push[1] = VSCONST_REG_MATRIX_OFFSET + 96;

	p_push += 2;

	while( num_bone_matrices > 0 )
	{
		// A 3x4 matrix is 12 dwords. You can encode a maximum of 32 dwords to D3DPUSH_SET_TRANSFORM_CONSTANT before needing another D3DPUSH_ENCODE.
		p_push[0]		= D3DPUSH_ENCODE( D3DPUSH_SET_TRANSFORM_CONSTANT, 12 );

		p_push[1]		= p_bone_element[0];
		p_push[2]		= p_bone_element[4];
		p_push[3]		= p_bone_element[8];
		p_push[4]		= p_bone_element[12];

		p_push[5]		= p_bone_element[1];
		p_push[6]		= p_bone_element[5];
		p_push[7]		= p_bone_element[9];
		p_push[8]		= p_bone_element[13];

		p_push[9]		= p_bone_element[2];
		p_push[10]		= p_bone_element[6];
		p_push[11]		= p_bone_element[10];
		p_push[12]		= p_bone_element[14];

		--num_bone_matrices;

		p_bone_element	+= 16;
		p_push			+= 13;
	}

	EngineGlobals.p_Device->EndState( p_push );

	// Load up the replacement registers for c-38 and c-37
	// The z value is 2^24 - to take 1.0f to the max z buffer value for a 24bit z buffer.
	static float homogenous_to_screen_reg[8] = { 320.0f, -240.0f, 1.6777215e7f, 0.0f, 320.53125f, 240.53125f, 0.0f, 0.0f };
	
	if( EngineGlobals.is_orthographic )
	{
		homogenous_to_screen_reg[0] = 128.0f;
		homogenous_to_screen_reg[1] = -128.0f;

		homogenous_to_screen_reg[2] = 1.6777215e7f;
		
		homogenous_to_screen_reg[4] = 128.53125f;
		homogenous_to_screen_reg[5] = 128.53125f;
	}
	else
	{
		homogenous_to_screen_reg[0] = (float)EngineGlobals.viewport.Width * 0.5f;
		homogenous_to_screen_reg[1] = (float)EngineGlobals.viewport.Height * -0.5f;

		homogenous_to_screen_reg[2] = ( EngineGlobals.zstencil_depth == 16 ) ? 65535.0f : 1.6777215e7f;

		homogenous_to_screen_reg[4] = (float)NxXbox::EngineGlobals.viewport.X + ((float)NxXbox::EngineGlobals.viewport.Width * 0.5f ) + 0.53125f;
		homogenous_to_screen_reg[5] = (float)NxXbox::EngineGlobals.viewport.Y + ((float)NxXbox::EngineGlobals.viewport.Height * 0.5f ) + 0.53125f;
	}

	D3DDevice_SetVertexShaderConstantFast( 94, (void*)homogenous_to_screen_reg, 2 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void startup_weighted_mesh_vertex_shader( void )
{
	// Switch to 192 constant mode, removing the lock on the reserved constants c-38 and c-37.
	D3DDevice_SetShaderConstantMode( D3DSCM_192CONSTANTS | D3DSCM_NORESERVEDCONSTANTS );

	// Flag the custom pipeline is in operation.
	EngineGlobals.custom_pipeline_enabled = true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void shutdown_weighted_mesh_vertex_shader( void )
{
	// Switch back to 96 constant mode.
	D3DDevice_SetShaderConstantMode( D3DSCM_96CONSTANTS );

	// Flag the custom pipeline is no longer in operation.
	EngineGlobals.custom_pipeline_enabled = false;
}


} // namespace NxXbox

================================================
FILE: Code/Gfx/XBox/NX/anim.h
================================================
#ifndef __ANIM_H
#define __ANIM_H

extern DWORD WeightedMeshVS_VXC_1Weight;
extern DWORD WeightedMeshVS_VXC_2Weight;
extern DWORD WeightedMeshVS_VXC_3Weight;
extern DWORD WeightedMeshVS_VXC_Specular_1Weight;
extern DWORD WeightedMeshVS_VXC_Specular_2Weight;
extern DWORD WeightedMeshVS_VXC_Specular_3Weight;
extern DWORD WeightedMeshVS_VXC_1Weight_UVTransform;
extern DWORD WeightedMeshVS_VXC_2Weight_UVTransform;
extern DWORD WeightedMeshVS_VXC_3Weight_UVTransform;
extern DWORD WeightedMeshVS_VXC_1Weight_SBPassThru;
extern DWORD WeightedMeshVS_VXC_2Weight_SBPassThru;
extern DWORD WeightedMeshVS_VXC_3Weight_SBPassThru;
extern DWORD WeightedMeshVertexShader_SBWrite;
extern DWORD BillboardScreenAlignedVS;
extern DWORD ParticleFlatVS;
extern DWORD ParticleNewFlatVS;
extern DWORD ParticleNewFlatPointSpriteVS;
extern DWORD ShadowBufferStaticGeomVS;

namespace NxXbox
{

DWORD	GetVertexShader( bool vertex_colors, bool specular, uint32 max_weights_used );
void	CreateWeightedMeshVertexShaders( void );
void	startup_weighted_mesh_vertex_shader( void );
void	setup_weighted_mesh_vertex_shader( void *p_root_matrix, void *p_bone_matrices, int num_bone_matrices );
void	shutdown_weighted_mesh_vertex_shader( void );


} // namespace NxXbox

#endif // __ANIM_H



================================================
FILE: Code/Gfx/XBox/NX/anim_vertdefs.h
================================================
#ifndef ANIM_VERTDEFS_H
#define ANIM_VERTDEFS_H

// Used in the vertex shader descriptor.
#define VSD_REG_POS                     0
#define VSD_REG_WEIGHTS                 1
#define VSD_REG_INDICES                 2
#define VSD_REG_NORMAL                  3
#define VSD_REG_COLOR                   4
#define VSD_REG_TEXCOORDS0              5
#define VSD_REG_TEXCOORDS1              6
#define VSD_REG_TEXCOORDS2              7
#define VSD_REG_TEXCOORDS3              8

// Input register - used in the vertex shader code.
#define VSIN_REG_POS                    v0
#define VSIN_REG_WEIGHTS                v1
#define VSIN_REG_INDICES                v2
#define VSIN_REG_NORMAL                 v3
#define VSIN_REG_COLOR                  v4
#define VSIN_REG_TEXCOORDS0             v5
#define VSIN_REG_TEXCOORDS1             v6
#define VSIN_REG_TEXCOORDS2             v7
#define VSIN_REG_TEXCOORDS3             v8

// Temporary register - used in the vertex shader code.
#define VSTMP_REG_POS_TMP               r0
#define VSTMP_REG_POS_ACCUM             r1
#define VSTMP_REG_NORMAL_TMP            r2
#define VSTMP_REG_NORMAL_ACCUM          r3
#define VSTMP_REG_MAT0					r8
#define VSTMP_REG_MAT1					r9
#define VSTMP_REG_MAT2					r10

// Vertex shader defines.
#define VSCONST_REG_BASE					-96	// Don't have to worry about the constant -38 and -37, we are not using them.

#define VSCONST_REG_TRANSFORM_OFFSET		VSCONST_REG_BASE
#define VSCONST_REG_TRANSFORM_SIZE			4

#define VSCONST_REG_WORLD_TRANSFORM_OFFSET	VSCONST_REG_TRANSFORM_OFFSET + VSCONST_REG_TRANSFORM_SIZE
#define VSCONST_REG_WORLD_TRANSFORM_SIZE	3

#define VSCONST_REG_DIR_LIGHT_OFFSET		VSCONST_REG_WORLD_TRANSFORM_OFFSET + VSCONST_REG_WORLD_TRANSFORM_SIZE
#define VSCONST_REG_DIR_LIGHT_SIZE			6	// Support for 3 directional lights.

#define VSCONST_REG_AMB_LIGHT_OFFSET		VSCONST_REG_DIR_LIGHT_OFFSET + VSCONST_REG_DIR_LIGHT_SIZE
#define VSCONST_REG_AMB_LIGHT_SIZE			1

#define VSCONST_REG_CAM_POS_OFFSET			VSCONST_REG_AMB_LIGHT_OFFSET + VSCONST_REG_AMB_LIGHT_SIZE
#define VSCONST_REG_CAM_POS_SIZE			1

#define VSCONST_REG_SPECULAR_COLOR_OFFSET	VSCONST_REG_CAM_POS_OFFSET + VSCONST_REG_CAM_POS_SIZE
#define VSCONST_REG_SPECULAR_COLOR_SIZE		1

#define VSCONST_REG_UV_MAT_OFFSET			VSCONST_REG_SPECULAR_COLOR_OFFSET + VSCONST_REG_SPECULAR_COLOR_SIZE
#define VSCONST_REG_UV_MAT_SIZE				8


//#define VSCONST_REG_MATRIX_OFFSET			( VSCONST_REG_UV_MAT_OFFSET + VSCONST_REG_UV_MAT_SIZE )
#define VSCONST_REG_MATRIX_OFFSET			-72

// Constant registers - used in the vertex shader code.
#define VSCONST_REG_TRANSFORM_X					c[0 + VSCONST_REG_BASE]
#define VSCONST_REG_TRANSFORM_Y					c[1 + VSCONST_REG_BASE]
#define VSCONST_REG_TRANSFORM_Z					c[2 + VSCONST_REG_BASE]
#define VSCONST_REG_TRANSFORM_W					c[3 + VSCONST_REG_BASE]
#define VSCONST_REG_WORLD_TRANSFORM_X			c[4 + VSCONST_REG_BASE]
#define VSCONST_REG_WORLD_TRANSFORM_Y			c[5 + VSCONST_REG_BASE]
#define VSCONST_REG_WORLD_TRANSFORM_Z			c[6 + VSCONST_REG_BASE]

#define VSCONST_REG_LIGHT_DIR0					c[7 + VSCONST_REG_BASE]
#define VSCONST_REG_LIGHT_COLOR0				c[8 + VSCONST_REG_BASE]
#define VSCONST_REG_LIGHT_DIR1					c[9 + VSCONST_REG_BASE]
#define VSCONST_REG_LIGHT_COLOR1				c[10 + VSCONST_REG_BASE]
#define VSCONST_REG_LIGHT_DIR2					c[11 + VSCONST_REG_BASE]
#define VSCONST_REG_LIGHT_COLOR2				c[12 + VSCONST_REG_BASE]

// Shadowbuffer transform shares space with lighting params, since both not required at the same time.
#define VSCONST_REG_SHADOWBUFFER_TRANSFORM_X	c[7 + VSCONST_REG_BASE]
#define VSCONST_REG_SHADOWBUFFER_TRANSFORM_Y	c[8 + VSCONST_REG_BASE]
#define VSCONST_REG_SHADOWBUFFER_TRANSFORM_Z	c[9 + VSCONST_REG_BASE]
#define VSCONST_REG_SHADOWBUFFER_TRANSFORM_W	c[10 + VSCONST_REG_BASE]

#define VSCONST_REG_AMB_LIGHT_COLOR				c[13 + VSCONST_REG_BASE]
#define VSCONST_REG_CAM_POS						c[14 + VSCONST_REG_BASE]
#define VSCONST_REG_SPECULAR_COLOR				c[15 + VSCONST_REG_BASE]
#define VSCONST_REG_UV_MAT00					c[16 + VSCONST_REG_BASE]
#define VSCONST_REG_UV_MAT01					c[17 + VSCONST_REG_BASE]
#define VSCONST_REG_UV_MAT10					c[18 + VSCONST_REG_BASE]
#define VSCONST_REG_UV_MAT11					c[19 + VSCONST_REG_BASE]
#define VSCONST_REG_UV_MAT20					c[20 + VSCONST_REG_BASE]
#define VSCONST_REG_UV_MAT21					c[21 + VSCONST_REG_BASE]
#define VSCONST_REG_UV_MAT30					c[22 + VSCONST_REG_BASE]
#define VSCONST_REG_UV_MAT31					c[23 + VSCONST_REG_BASE]

#endif // ANIM_VERTDEFS_H

================================================
FILE: Code/Gfx/XBox/NX/billboard.cpp
================================================
#include "billboard.h"
#include "nx_init.h"

namespace NxXbox
{

sBillboardManager BillboardManager;
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sBillboardManager::sBillboardManager( void )
{
	m_num_batches	= 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sBillboardManager::~sBillboardManager( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int sort_batches_by_draw_order( const void *p1, const void *p2 )
{
	sBillboardMaterialBatch *p_batch0 = *((sBillboardMaterialBatch**)p1 );
	sBillboardMaterialBatch *p_batch1 = *((sBillboardMaterialBatch**)p2 );

	// Deal with NULL pointers first (caused by removing batches from the list).
	if( p_batch0 == NULL )
	{
		if( p_batch1 == NULL )
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}
	else if( p_batch1 == NULL )
	{
		return -1;
	}

	return p_batch0->GetDrawOrder() < p_batch1->GetDrawOrder() ? -1 : p_batch0->GetDrawOrder() > p_batch1->GetDrawOrder() ? 1 : 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sBillboardMaterialBatch *sBillboardManager::GetBatch( sMaterial *p_material )
{
	for( int b = 0; b < m_num_batches; ++b )
	{
		if( mp_batches[b]->GetMaterial() == p_material )
		{
			return mp_batches[b];
		}
	}
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sBillboardManager::AddEntry( sMesh *p_mesh )
{
	if( p_mesh->mp_material )
	{
		// Make sure it is textured.
		if( p_mesh->mp_material->mp_tex[0] != NULL )
		{
			sBillboardMaterialBatch *p_batch = GetBatch( p_mesh->mp_material );
			if( p_batch == NULL )
			{
				Dbg_Assert( m_num_batches < ( MAX_BILLBOARD_BATCHES - 1 ));

				// Need to create a new batch for this material.
				p_batch = new sBillboardMaterialBatch( p_mesh->mp_material );
				mp_batches[m_num_batches++]	= p_batch;

				// Resort the list of batches.
				SortBatches();
			}

			// Now add the mesh to this batch.
			p_batch->AddEntry( p_mesh );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sBillboardManager::RemoveEntry( sMesh *p_mesh )
{
	if( p_mesh->mp_material )
	{
		sBillboardMaterialBatch *p_batch = GetBatch( p_mesh->mp_material );
		if( p_batch )
		{
			// Removes the entry if it exists.
			p_batch->RemoveEntry( p_mesh );

			// If the batch is now empty, remove it.
			if( p_batch->IsEmpty())
			{
				for( int i = 0; i < m_num_batches; ++i )
				{
					if( mp_batches[i] == p_batch )
					{
						delete p_batch;
						mp_batches[i] = NULL;
						break;
					}
				}

				// Resort the batches (will move the NULL pointer to the end).
				SortBatches();

				// Important not to decrement this until after the resort has been performed.
				--m_num_batches;
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sBillboardManager::SortBatches( void )
{
	qsort( mp_batches, m_num_batches, sizeof( sBillboardMaterialBatch* ), sort_batches_by_draw_order );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sBillboardManager::SetCameraMatrix( void )
{
	Mth::Vector up( 0.0f, 1.0f, 0.0f );

	m_at.Set( NxXbox::EngineGlobals.view_matrix.m[0][2], NxXbox::EngineGlobals.view_matrix.m[1][2], NxXbox::EngineGlobals.view_matrix.m[2][2] );
	m_screen_right		= Mth::CrossProduct( m_at, up ).Normalize();
	m_screen_up			= Mth::CrossProduct( m_screen_right, m_at ).Normalize();
	m_at_xz				= Mth::Vector( m_at[X], 0.0f, m_at[Z] ).Normalize();
	m_screen_right_xz	= Mth::Vector( m_screen_right[X], 0.0f, m_screen_right[Z] ).Normalize();
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sBillboardManager::Render( uint32 flags )
{
	// Render the opaque billboards if requested.
	if( flags & vRENDER_OPAQUE )
	{
		for( int b = 0; b < m_num_batches; ++b )
		{
			if( !( mp_batches[b]->mp_material->m_flags[0] & 0x40 ))
			{
				mp_batches[b]->Render();
			}
		}
	}

	// Render the semitransparent billboards if requested.
	if( flags & vRENDER_SEMITRANSPARENT )
	{
		for( int b = 0; b < m_num_batches; ++b )
		{
			if( mp_batches[b]->mp_material->m_flags[0] & 0x40 )
			{
				mp_batches[b]->Render();
			}
		}
	}

}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sBillboardMaterialBatch::sBillboardMaterialBatch( sMaterial *p_material )
{
	mp_material		= p_material;
	mp_entries[0]	= new Lst::Head ;
	mp_entries[1]	= new Lst::Head ;
	mp_entries[2]	= new Lst::Head ;

	// We can calculate what the per-entry size will be based on the material properties.
	m_entry_size	 = 4 * sizeof( float ) * 3;							// Four vertex positions.
	m_entry_size	+= 4 * sizeof( D3DCOLOR );							// Four vertex colors.
	m_entry_size	+= 4 * sizeof( float ) * 2 * p_material->m_passes;	// Four uv pairs for each pass.
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sBillboardMaterialBatch::~sBillboardMaterialBatch( void )
{
	delete mp_entries[0];
	delete mp_entries[1];
	delete mp_entries[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool sBillboardMaterialBatch::IsEmpty( void )
{
	return ( mp_entries[0]->IsEmpty() && mp_entries[1]->IsEmpty() && mp_entries[2]->IsEmpty());
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float sBillboardMaterialBatch::GetDrawOrder( void )
{
	return mp_material ? mp_material->m_draw_order : 0.0f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMaterial *sBillboardMaterialBatch::GetMaterial( void )
{
	return mp_material;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sBillboardMaterialBatch::AddEntry( sMesh *p_mesh )
{
	// Create a new billboard entry.
	sBillboardEntry *p_entry = new sBillboardEntry( p_mesh );

	// And a new node.
	Lst::Node *node = new Lst::Node( p_entry );

	mp_entries[p_entry->m_type]->AddToTail( node );

	// Now process the mesh.
	ProcessMesh( p_mesh );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sBillboardMaterialBatch::ProcessMesh( sMesh *p_mesh )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sBillboardMaterialBatch::RemoveEntry( sMesh *p_mesh )
{
	for( int e = 0; e < 3; ++e )
	{
		Lst::Node *node, *next;
		node = mp_entries[e]->GetNext();
		while( node )
		{
			next = node->GetNext();
			sBillboardEntry *p_entry = node->GetData();
			if( p_entry->mp_mesh == p_mesh )
			{
				// This is the entry. Delete the node and the entry.
				delete node;
				delete p_entry;
				return;
			}
			node = next;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sBillboardMaterialBatch::Render( void )
{
	static float vector_upload[12] = {	0.0f, 0.0f, 0.0f, 1.0f,
										0.0f, 0.0f, 0.0f, 1.0f,
										0.0f, 0.0f, 0.0f, 1.0f };

	if( mp_material )
	{
		mp_material->Submit();

		// Set up correct vertex shader.
		NxXbox::set_vertex_shader( BillboardScreenAlignedVS );

		// Lock out vertex shader changes.
		EngineGlobals.vertex_shader_override = BillboardScreenAlignedVS;

		// Set up correct pixel shader - this assumes that all meshes with the same material will have the same pixel shader.
		for( int e = 0; e < 3; ++e )
		{
			if( mp_entries[e]->GetNext())
			{
				sBillboardEntry *p_entry = mp_entries[e]->GetNext()->GetData();
				set_pixel_shader( p_entry->mp_mesh->m_pixel_shader );
				break;
			}
		}

		// Load up the combined world->view_projection matrix.
		XGMATRIX	dest_matrix;
		XGMATRIX	temp_matrix;
		XGMATRIX	projMatrix;
		XGMATRIX	viewMatrix;
		XGMATRIX	worldMatrix;
		
		// Projection matrix.
		XGMatrixTranspose( &projMatrix, &EngineGlobals.projection_matrix );
	
		// View matrix.
		XGMatrixTranspose( &viewMatrix, &EngineGlobals.view_matrix );
		viewMatrix.m[3][0] = 0.0f;
		viewMatrix.m[3][1] = 0.0f;
		viewMatrix.m[3][2] = 0.0f;
		viewMatrix.m[3][3] = 1.0f;
	
		// World space transformation matrix.
		XGMatrixIdentity( &worldMatrix );

		// Calculate composite world->view->projection matrix.
		XGMatrixMultiply( &temp_matrix, &viewMatrix, &worldMatrix );
		XGMatrixMultiply( &dest_matrix, &projMatrix, &temp_matrix );

		// Load up the combined world, camera & projection matrix.
		D3DDevice_SetVertexShaderConstantFast( 0, (void*)&dest_matrix, 4 );

		Lst::Node *node, *next;

		// First do the screen aligned billboards.
		vector_upload[0]	= BillboardManager.m_screen_right[X];
		vector_upload[1]	= BillboardManager.m_screen_right[Y];
		vector_upload[2]	= BillboardManager.m_screen_right[Z];
		vector_upload[4]	= BillboardManager.m_screen_up[X];
		vector_upload[5]	= BillboardManager.m_screen_up[Y];
		vector_upload[6]	= BillboardManager.m_screen_up[Z];
		vector_upload[8]	= BillboardManager.m_at[X];
		vector_upload[9]	= BillboardManager.m_at[Y];
		vector_upload[10]	= BillboardManager.m_at[Z];
		D3DDevice_SetVertexShaderConstantFast( 4, (void*)( &vector_upload[0] ), 3 );

		for( node = mp_entries[0]->GetNext(); node; node = next )
		{
			next = node->GetNext();
			sBillboardEntry *p_entry = node->GetData();

			// Only render if the mesh is active.
			if( p_entry->mp_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE )
			{
				// Deal with material color override.
				if( p_entry->mp_mesh->m_flags & sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE )
					p_entry->mp_mesh->HandleColorOverride();
				else
					set_pixel_shader( p_entry->mp_mesh->m_pixel_shader );

				// Deal with vertex color wibble if present.
				if( p_entry->mp_mesh->mp_vc_wibble_data )
				{
					p_entry->mp_mesh->wibble_vc();
					IDirect3DVertexBuffer8*	p_submit_buffer = p_entry->mp_mesh->mp_vertex_buffer[p_entry->mp_mesh->m_current_write_vertex_buffer];
					p_entry->mp_mesh->SwapVertexBuffers();
					D3DDevice_SetStreamSource( 0, p_submit_buffer, p_entry->mp_mesh->m_vertex_stride );
					D3DDevice_DrawIndexedVertices( p_entry->mp_mesh->m_primitive_type, p_entry->mp_mesh->m_num_indices[0], p_entry->mp_mesh->mp_index_buffer[0] );
				}
				else
				{
					D3DDevice_SetStreamSource( 0, p_entry->mp_mesh->mp_vertex_buffer[0], p_entry->mp_mesh->m_vertex_stride );
					D3DDevice_DrawIndexedVertices( p_entry->mp_mesh->m_primitive_type, p_entry->mp_mesh->m_num_indices[0], p_entry->mp_mesh->mp_index_buffer[0] );
				}
			}
		}

		// Next do the the y axis aligned billboards.
		vector_upload[0]	= BillboardManager.m_screen_right_xz[X];
		vector_upload[1]	= BillboardManager.m_screen_right_xz[Y];
		vector_upload[2]	= BillboardManager.m_screen_right_xz[Z];
		vector_upload[4]	= 0.0f;
		vector_upload[5]	= 1.0f;
		vector_upload[6]	= 0.0f;
		vector_upload[8]	= BillboardManager.m_at_xz[X];
		vector_upload[9]	= BillboardManager.m_at_xz[Y];
		vector_upload[10]	= BillboardManager.m_at_xz[Z];
		D3DDevice_SetVertexShaderConstantFast( 4, (void*)vector_upload, 3 );

		for( node = mp_entries[1]->GetNext(); node; node = next )
		{
			next = node->GetNext();
			sBillboardEntry *p_entry = node->GetData();

			// Only render if the mesh is active.
			if( p_entry->mp_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE )
			{
				// Deal with material color override.
				if( p_entry->mp_mesh->m_flags & sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE )
					p_entry->mp_mesh->HandleColorOverride();
				else
					set_pixel_shader( p_entry->mp_mesh->m_pixel_shader );

				// Deal with vertex color wibble if present.
				if( p_entry->mp_mesh->mp_vc_wibble_data )
				{
					p_entry->mp_mesh->wibble_vc();
					IDirect3DVertexBuffer8*	p_submit_buffer = p_entry->mp_mesh->mp_vertex_buffer[p_entry->mp_mesh->m_current_write_vertex_buffer];
					p_entry->mp_mesh->SwapVertexBuffers();
					D3DDevice_SetStreamSource( 0, p_submit_buffer, p_entry->mp_mesh->m_vertex_stride );
					D3DDevice_DrawIndexedVertices( p_entry->mp_mesh->m_primitive_type, p_entry->mp_mesh->m_num_indices[0], p_entry->mp_mesh->mp_index_buffer[0] );
				}
				else
				{
					D3DDevice_SetStreamSource( 0, p_entry->mp_mesh->mp_vertex_buffer[0], p_entry->mp_mesh->m_vertex_stride );
					D3DDevice_DrawIndexedVertices( p_entry->mp_mesh->m_primitive_type, p_entry->mp_mesh->m_num_indices[0], p_entry->mp_mesh->mp_index_buffer[0] );
				}
			}
		}

		// Now do the arbitrary axis aligned billboards.
		for( node = mp_entries[2]->GetNext(); node; node = next )
		{
			next = node->GetNext();
			sBillboardEntry *p_entry		= node->GetData();
			sBillboardData *p_entry_data	= p_entry->mp_mesh->mp_billboard_data;
		
			// Only render if the mesh is active.
			if( p_entry->mp_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE )
			{
				// For this type we need to calculate the vector perpendicular to both the view vector and the axis of rotation.
				Mth::Vector axis_right = Mth::CrossProduct( BillboardManager.m_at, p_entry_data->m_pivot_axis ).Normalize();
				Mth::Vector axis_at = Mth::CrossProduct( p_entry_data->m_pivot_axis, axis_right ).Normalize();

				// Begin state block to set vertex shader constants for bone transforms.
				DWORD *p_push;
				EngineGlobals.p_Device->BeginState( 15, &p_push );

				// 1 here isn't the parameter for SET_TRANSFORM_CONSTANT_LOAD; rather, it's the number of dwords written to that register.
				p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_TRANSFORM_CONSTANT_LOAD, 1 ); 

				// Here is the actual parameter for SET_TRANSFORM_CONSTANT_LOAD. Always add 96 to the constant register.
				p_push[1]	= 4 + 96;
				p_push[2]	= D3DPUSH_ENCODE( D3DPUSH_SET_TRANSFORM_CONSTANT, 12 );

				p_push[3]	= *((DWORD*)&axis_right[X] );
				p_push[4]	= *((DWORD*)&axis_right[Y] );
				p_push[5]	= *((DWORD*)&axis_right[Z] );

				p_push[7]	= *((DWORD*)&p_entry_data->m_pivot_axis[X] );
				p_push[8]	= *((DWORD*)&p_entry_data->m_pivot_axis[Y] );
				p_push[9]	= *((DWORD*)&p_entry_data->m_pivot_axis[Z] );

				p_push[11]	= *((DWORD*)&axis_at[X] );
				p_push[12]	= *((DWORD*)&axis_at[Y] );
				p_push[13]	= *((DWORD*)&axis_at[Z] );

				p_push		+= 15;
				EngineGlobals.p_Device->EndState( p_push );

				// Deal with material color override.
				if( p_entry->mp_mesh->m_flags & sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE )
					p_entry->mp_mesh->HandleColorOverride();
				else
					set_pixel_shader( p_entry->mp_mesh->m_pixel_shader );

				// Deal with vertex color wibble if present.
				if( p_entry->mp_mesh->mp_vc_wibble_data )
				{
					p_entry->mp_mesh->wibble_vc();
					IDirect3DVertexBuffer8*	p_submit_buffer = p_entry->mp_mesh->mp_vertex_buffer[p_entry->mp_mesh->m_current_write_vertex_buffer];
					p_entry->mp_mesh->SwapVertexBuffers();
					D3DDevice_SetStreamSource( 0, p_submit_buffer, p_entry->mp_mesh->m_vertex_stride );
					D3DDevice_DrawIndexedVertices( p_entry->mp_mesh->m_primitive_type, p_entry->mp_mesh->m_num_indices[0], p_entry->mp_mesh->mp_index_buffer[0] );
				}
				else
				{
					D3DDevice_SetStreamSource( 0, p_entry->mp_mesh->mp_vertex_buffer[0], p_entry->mp_mesh->m_vertex_stride );
					D3DDevice_DrawIndexedVertices( p_entry->mp_mesh->m_primitive_type, p_entry->mp_mesh->m_num_indices[0], p_entry->mp_mesh->mp_index_buffer[0] );
				}
			}
		}

		// Finally do the world aligned billboards.
//		for( node = mp_entries[3]->GetNext(); node; node = next )
//		{
//			next = node->GetNext();
//			sBillboardEntry *p_entry = node->GetData();
		
			// Only render if the mesh is active.
//			if( p_entry->mp_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE )
//			{
				// For this type we need to calculate the vector perpendicular to both the view vector and the axis of rotation.
//				Mth::Vector at( p_entry->m_pivot_axis[X] - EngineGlobals.cam_position.x,
//								p_entry->m_pivot_axis[Y] - EngineGlobals.cam_position.y,
//								p_entry->m_pivot_axis[Z] - EngineGlobals.cam_position.z );
//				at.Normalize();

//				Mth::Vector axis_right = Mth::CrossProduct( at, BillboardManager.m_screen_up ).Normalize();
//				Mth::Vector axis_at = Mth::CrossProduct( BillboardManager.m_screen_up, axis_right ).Normalize();

//				vector_upload[0]	= axis_right[X];
//				vector_upload[1]	= axis_right[Y];
//				vector_upload[2]	= axis_right[Z];
//				vector_upload[4]	= BillboardManager.m_screen_up[X];
//				vector_upload[5]	= BillboardManager.m_screen_up[Y];
//				vector_upload[6]	= BillboardManager.m_screen_up[Z];
//				vector_upload[8]	= axis_at[X];
//				vector_upload[9]	= axis_at[Y];
//				vector_upload[10]	= axis_at[Z];
//				D3DDevice_SetVertexShaderConstant( 4, (void*)vector_upload, 3 );

//				D3DDevice_SetStreamSource( 0, p_entry->mp_mesh->mp_vertex_buffer[0], p_entry->mp_mesh->m_vertex_stride );
//				D3DDevice_DrawIndexedVertices( p_entry->mp_mesh->m_primitive_type, p_entry->mp_mesh->m_num_indices[0], p_entry->mp_mesh->mp_index_buffer[0] );
//			}
//		}

		// Undo vertex shader lock.
		EngineGlobals.vertex_shader_override = 0;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sBillboardMaterialBatch::Reset( void )
{
	Lst::Node *node, *next;

	for( int e = 0; e < 3; ++e )
	{
		if( mp_entries[e] )
		{
			for( node = mp_entries[e]->GetNext(); node; node = next )
			{
				next = node->GetNext();
				delete node;
			}
		}
	}

	mp_material		= NULL;
	m_entry_size	= 0;
}







/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sBillboardEntry::sBillboardEntry( sMesh *p_mesh )
{
	Dbg_Assert( p_mesh->mp_billboard_data != NULL );

	mp_mesh			= p_mesh;
	m_type			= p_mesh->mp_billboard_data->m_type;

	// For now set the pivot point as the center of the bounding sphere.
//	m_pivot_pos.Set( p_mesh->m_sphere_center.x, p_mesh->m_sphere_center.y, p_mesh->m_sphere_center.z );

	// Frig the verts for now.
//	m_verts[0][0]	= -12.0f;
//	m_verts[0][1]	= -24.0f;
//	m_verts[0][2]	= 0.0f;

//	m_verts[1][0]	= -12.0f;
//	m_verts[1][1]	= 24.0f;
//	m_verts[1][2]	= 0.0f;

//	m_verts[2][0]	= 12.0f;
//	m_verts[2][1]	= 24.0f;
//	m_verts[2][2]	= 0.0f;

//	m_verts[3][0]	= 12.0f;
//	m_verts[3][1]	= -24.0f;
//	m_verts[3][2]	= 0.0f;


//	switch( rand() & 0x03 )
//	{
//		case 0:
//		{
//			m_type = vBILLBOARD_TYPE_SCREEN_ALIGNED;
//			break;
//		}
//		case 1:
//		{
//			m_type = vBILLBOARD_TYPE_Y_AXIS_ALIGNED;
//			break;
//		}
//		case 2:
//		case 3:
//		{
//			m_type = vBILLBOARD_TYPE_ARBITRARY_AXIS_ALIGNED;
//			m_pivot_axis = Mth::Vector((float)( rand() - rand()), (float)( rand() - rand()), (float)( rand() - rand())).Normalize();
//			break;
//		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sBillboardEntry::~sBillboardEntry( void )
{
}








} // namespace NxXbox



================================================
FILE: Code/Gfx/XBox/NX/billboard.h
================================================
#ifndef __BILLBOARD_H
#define __BILLBOARD_H


#include 
#include "render.h"

namespace NxXbox
{

/******************************************************************/
/*                                                                */
/* A single billboard entry.									  */
/*                                                                */
/******************************************************************/
struct sBillboardEntry
{
	sBillboardData::EBillboardType	m_type;
	sMesh							*mp_mesh;
//	Mth::Vector						m_pivot_pos;
//	Mth::Vector						m_pivot_axis;	// Normalised axis of rotation, valid only for type ARBITRARY_AXIS_ALIGNED.
//	float							m_verts[4][3];	// Verts defined as an offset from the pivot point.

									sBillboardEntry( sMesh *p_mesh );
									~sBillboardEntry( void );
};
	

	
/******************************************************************/
/*                                                                */
/* Stores information about a billboard batch, effectively a set  */
/* of billboards with the same material.                          */
/*                                                                */
/******************************************************************/
struct sBillboardMaterialBatch
{
	int							m_entry_size;		// Push buffer size of each billboard entry (bytes).
	sMaterial					*mp_material;
	Lst::Head 	*mp_entries[3];		// One entry each for screen aligned, y axis aligned, and arbitrary aligned.

								sBillboardMaterialBatch( sMaterial *p_material );
								~sBillboardMaterialBatch( void );

	bool						IsEmpty( void );
	float						GetDrawOrder( void );
	sMaterial					*GetMaterial( void );
	void						AddEntry( sMesh *p_mesh );
	void						RemoveEntry( sMesh *p_mesh );
	void						ProcessMesh( sMesh *p_mesh );
	void						Render( void );
	void						Reset( void );
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sBillboardManager
{
	enum
	{
				MAX_BILLBOARD_BATCHES	= 256
	};

	int							m_num_batches;
	sBillboardMaterialBatch		*mp_batches[MAX_BILLBOARD_BATCHES];

	Mth::Vector					m_at;
	Mth::Vector					m_screen_right;
	Mth::Vector					m_screen_up;
	Mth::Vector					m_at_xz;
	Mth::Vector					m_screen_right_xz;

								sBillboardManager( void );
								~sBillboardManager( void );

	void						Render( uint32 flags );
	void						SetCameraMatrix( void );
	sBillboardMaterialBatch		*GetBatch( sMaterial *p_material );
	void						AddEntry( sMesh *p_mesh );
	void						RemoveEntry( sMesh *p_mesh );
	void						SortBatches( void );
};


extern sBillboardManager BillboardManager;


	

} // namespace NxXbox


#endif // __BILLBOARD_H



================================================
FILE: Code/Gfx/XBox/NX/chars.cpp
================================================
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "nx_init.h"
#include "render.h"
#include "chars.h"
#include "xbmemfnt.h"


/*


	
.fnt file format (by Ryan)
--------------------------

	4	File size in bytes
	4	Number of characters
	4	Default height
	4	Default base
	
	?	Character table (see below)
	?	Texture (see below)

	Character
	2	Baseline (how many pixels down relative to top of image)
	2	Ascii value

	Texture
	4	Size of texture
	2	Width
	2	Height
	2	Bit depth
	6	Padding
	W*H	Raw data
	0-3	Padding for uint32 alignment
	1K	Palette data
	4	Number of subtextures
	?	Subtexture table (see below)

	Subtexture
	2	X
	2	Y
	2	W
	2	H
	
*/


namespace NxXbox
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
typedef struct
{
	float		x, y, z;
	float		rhw;
	D3DCOLOR	col;
	float		u, v;
}
sFontVert;


SFont			*pFontList;
SFont			*pButtonsFont				= NULL;
SFont			*SText::spOverrideFont		= NULL;

const uint32	CHARS_PER_BUFFER			= 256;
static int		font_vertex_offset			= 0;
static BYTE*	p_locked_font_vertex_buffer	= NULL;
static BYTE		font_vertex_buffer[CHARS_PER_BUFFER * 4 * sizeof( sFontVert )];

static uint32	swizzle_table[4096];
static bool		swizzle_table_generated		= false;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void generateSwizzleTable( void )
{
	if( !swizzle_table_generated )
	{
		for( uint32 i = 0, value = 0; i < 4096; i++ )
		{
			swizzle_table[i] = value;
			value += 0x2AAAAAAB;
			value &= 0x55555555;
		}
		swizzle_table_generated = true;
	}
}



#define TWIDDLE(_u, _v) ((swizzle_table[(_v)] << 1) | (swizzle_table[(_u)]))


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SwizzleTexture( void *dstBuffer, void *srcBuffer, int width, int height, int32 depth, int32 stride )
{
    int32 tilesX, tilesY;
    int32 tilesSizeX, tilesSizeY;
    int32 tileSize;

	generateSwizzleTable();

	if( width > height )
    {
        tilesX = width / height;
        tilesY = 1;

        tilesSizeX = width / tilesX;
        tilesSizeY = height;
    }
    else
    {
        tilesX = 1;
        tilesY = height / width;

        tilesSizeX = width;
        tilesSizeY = height / tilesY;
    }

    tileSize = tilesSizeX * tilesSizeY;

	switch (depth)
	{
		case 4:
	    case 8:
        {
            int32 j;

            for (j = 0; j < tilesY; j++)
            {
                int32 i;

                for (i = 0; i < tilesX; i++)
                {
            int32 y;
                    uint8 *base;

                    base = (uint8 *)(((uint8 *)dstBuffer) +
                                       ((tileSize * tilesX) * j) +
                                       (tileSize * i));

                    for (y = 0; y < tilesSizeY; y++)
            {
                uint8    *srcPixel;
                int32     x;

                        srcPixel = (uint8 *)(((uint8 *)srcBuffer) +
                                               (stride * (tilesSizeY * j)) +
                                               (tilesSizeX * i) +
                                               (stride * y));

                        for (x = 0; x < tilesSizeX; x++)
                {
                    uint8    *dstPixel;
                        dstPixel = (uint8 *)(base + TWIDDLE(x, y));
		                *dstPixel = *srcPixel;

                    srcPixel++;
                }
            }
        }
            }
        }
        break;

    case 16:
        {
            int32 j;

            for (j = 0; j < tilesY; j++)
            {
                int32 i;

                for (i = 0; i < tilesX; i++)
                {
            int32 y;
                    uint8 *base;

                    base = (uint8 *)(((uint16 *)dstBuffer) +
                                       ((tileSize * tilesX) * j) +
                                       (tileSize * i));

                    for (y = 0; y < tilesSizeY; y++)
            {
                uint16    *srcPixel;
                int32     x;

                        srcPixel = (uint16 *)(((uint8 *)srcBuffer) +
                                                (stride * (tilesSizeY * j)) +
                                                (2 * tilesSizeX * i) +
                                                (stride * y));

                        for (x = 0; x < tilesSizeX; x++)
                {
                    uint16    *dstPixel;
                    dstPixel = (uint16 *)(base + (TWIDDLE(x, y) << 1));
                    *dstPixel = *srcPixel;

                    srcPixel++;
                }
            }
        }
            }
        }
        break;

    case 24:
    case 32:
        {
            int32 j;

            for (j = 0; j < tilesY; j++)
            {
                int32 i;

                for (i = 0; i < tilesX; i++)
                {
            int32 y;
                    uint8 *base;

                    base = (uint8 *)(((uint32 *)dstBuffer) +
                                       ((tileSize * tilesX) * j) +
                                       (tileSize * i));

                    for (y = 0; y < tilesSizeY; y++)
            {
                uint32    *srcPixel;
                int32     x;

                        srcPixel = (uint32 *)(((uint8 *)srcBuffer) +
                                                (stride * (tilesSizeY * j)) +
                                                (4 * tilesSizeX * i) +
                                                (stride * y));

                        for (x = 0; x < tilesSizeX; x++)
                {
                    uint32    *dstPixel;
                    dstPixel = (uint32 *)(base + (TWIDDLE(x, y) << 2));
                    *dstPixel = *srcPixel;

                    srcPixel++;
                }
            }
        }
            }
        }
        break;

    default:
		exit( 0 );
        break;
    }
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
SFont* InitialiseMemoryResidentFont( void )
{
	return LoadFont((const char*)xbmemfnt, true );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
SFont* LoadFont( const char *Filename, bool memory_resident )
{
	SFont*	pFont;
	SChar*	pChar;
	uint8*	pData;
	void*	p_FH;
	int		i,Len,NumChars,Width,Height,Depth,NumBytes;

	// Build the full filename.
	char filename[128];

	if( !memory_resident )
	{
		strcpy( filename, "fonts/" );
		strcat( filename, Filename );
		strcat( filename, ".fnt.xbx" );
	}
	
	// Open the font file.
	if( !memory_resident )
		p_FH = File::Open( filename, "rb" );

	// Allocate memory for the font structure.
	pFont = new SFont();

	// Allocate a temporary buffer.
	uint8 FontBuf[2048];

	// Load file header.
	if( !memory_resident )
	{
		Len = File::Read( FontBuf, 16, 1, p_FH );
		Dbg_MsgAssert( Len == 16, ( "couldn't read file header from font file %s", Filename ));
	}
	else
	{
		CopyMemory( FontBuf, Filename, 16 );
		Filename += 16;
	}

	NumChars			 = ((uint32 *)FontBuf)[1];
	pFont->DefaultHeight = ((uint32 *)FontBuf)[2];
	pFont->DefaultBase	 = ((uint32 *)FontBuf)[3];

	// Clear character map to zero.
	memset( pFont->Map, 0, 256 );
	memset( pFont->SpecialMap, 0, 32 );

	// Allocate memory for character table.
	pFont->pChars = new SChar[NumChars];

	// Load character map and character table.
	if( !memory_resident )
	{
		Len = File::Read( FontBuf, NumChars << 2, 1, p_FH );
		Dbg_MsgAssert( Len == ( NumChars << 2 ), ( "couldn't read character table in font file %s", Filename ));
	}
	else
	{
		CopyMemory( FontBuf, Filename, NumChars << 2 );
		Filename += NumChars << 2;
	}

	for( i = 0, pChar = pFont->pChars, pData = FontBuf; i < NumChars; i++,pChar++,pData += 4 )
	{
		pChar->Baseline							= ((uint16 *)pData)[0];
		sint16 ascii_val = ((sint16 *)pData)[1];
		if (ascii_val >= 0)
			pFont->Map[(uint8) ascii_val] = i;
		else
		{
			Dbg_Assert(ascii_val >= -32)
			pFont->SpecialMap[(uint8) (-ascii_val - 1)] = i;
		}
	}

	// If there is a null character in the font, make characters that could not be found
	// in the font display that instead of 'A'
	if( pFont->SpecialMap[31] != 0 )
	{
		for( i = 0; i < 256; ++i ) 
		{
			if( pFont->Map[i] == 0 && i != 'A' && i != 'a')
				pFont->Map[i] = pFont->SpecialMap[31];

			if( i < 31 && pFont->SpecialMap[i] == 0 )
				pFont->SpecialMap[i] = pFont->SpecialMap[31];
		}
	}	
	
	// Load texture header.
	if( !memory_resident )
	{
		Len = File::Read( FontBuf, 16, 1, p_FH );
		Dbg_MsgAssert( Len == 16, ( "couldn't read texture header from font file %s", Filename ));
	}
	else
	{
		CopyMemory( FontBuf, Filename, 16 );
		Filename += 16;
	}

	Width	= ((uint16 *)FontBuf)[2];
	Height	= ((uint16 *)FontBuf)[3];
	Depth	= ((uint16 *)FontBuf)[4];

	// Create texture.
	Dbg_Assert( Depth == 8 );
	if( D3D_OK != D3DDevice_CreateTexture(	Width, Height, 1, 0, D3DFMT_P8, 0, &pFont->pD3DTexture ))
	{
		Dbg_Assert( 0 );
		return NULL;
	}
	
	// Read texture bitmap data (into temp buffer so we can then swizzle it).
	NumBytes = ( Width * Height + 3 ) & 0xFFFFFFFC;

	uint8* p_temp_texel_data = new uint8[NumBytes];
	if( !memory_resident )
	{
		Len = File::Read( p_temp_texel_data, NumBytes, 1, p_FH );
		Dbg_MsgAssert( Len == NumBytes, ( "Couldn't read texture bitmap from font file %s", Filename ));
	}
	else
	{
		CopyMemory( p_temp_texel_data, Filename, NumBytes );
		Filename += NumBytes;
	}

	// Lock the texture so we can swizzle into it directly.
	D3DLOCKED_RECT locked_rect;
	if( D3D_OK != pFont->pD3DTexture->LockRect( 0, &locked_rect, NULL, 0 ))
	{
		Dbg_Assert( 0 );
		return NULL;
	}
	
	// Swizzle the texture data.
	SwizzleTexture( locked_rect.pBits, p_temp_texel_data, Width, Height, 8, Width );

	// No longer need this data.
	delete[] p_temp_texel_data;	
	
	// Create palette.
	if( D3D_OK != D3DDevice_CreatePalette(	D3DPALETTE_256,	&pFont->pD3DPalette ))
	{
		Dbg_Assert( 0 );
		return NULL;
	}
	
	// Read clut bitmap data.
	D3DCOLOR *p_clut;
	pFont->pD3DPalette->Lock( &p_clut, 0 );

	if( !memory_resident )
	{
		Len	= File::Read( p_clut, 1024, 1, p_FH );
		Dbg_MsgAssert( Len == 1024, ( "couldn't read clut bitmap from font file %s", Filename ));
	}
	else
	{
		CopyMemory( p_clut, Filename, 1024 );
		Filename += 1024;
	}

	// Switch from RGBA to BGRA format palette.
	for( i = 0; i < 256; ++i )
	{
		uint32	red = p_clut[i] & 0xFF;
		uint32	blu = ( p_clut[i] >> 16 ) & 0xFF;

		// Double the alpha in the clut (currently limited to 0x80).
		uint32 alpha = p_clut[i] >> 24;
		alpha = ( alpha >= 0x80 ) ? 0xFF : ( alpha * 2 );
		p_clut[i] = ( alpha << 24 ) | ( p_clut[i] & 0x0000FF00 ) | ( red << 16 ) | ( blu );
	}

	// Skip numsubtextures, and load subtextures.
	if( !memory_resident )
	{
		Len = File::Read( FontBuf, ( NumChars << 3 ) + 4, 1, p_FH );
		Dbg_MsgAssert( Len == ( NumChars << 3 ) + 4, ( "couldn't read subtexture table from font file %s", Filename ));
	}
	else
	{
		CopyMemory( FontBuf, Filename, ( NumChars << 3 ) + 4 );
		Filename += ( NumChars << 3 ) + 4;
	}

	for( i = 0, pChar = pFont->pChars, pData = FontBuf + 4; i < NumChars; i++, pChar++, pData += 8 )
	{
		uint16 x	= ((uint16 *)pData )[0];
		uint16 y	= ((uint16 *)pData )[1];
		uint16 w	= ((uint16 *)pData )[2];
		uint16 h	= ((uint16 *)pData )[3];
		
		pChar->w	= w;
		pChar->h	= h;
		pChar->u0	= (float)x / (float)Width;
		pChar->v0	= (float)y / (float)Height;
		pChar->u1	= pChar->u0 + ((float)w / (float)Width );
		pChar->v1	= pChar->v0 + ((float)h / (float)Height );
	}

	// Add font to font list.
	pFont->pNext	= pFontList;
	pFontList		= pFont;

	// We're done with the font file now.
	if( !memory_resident )
		File::Close( p_FH );
	
	// this will serve as the default spacing
	pFont->mSpaceSpacing = pFont->pChars[pFont->Map['I']].w;
	
	return pFont;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void UnloadFont( SFont *pFont )
{
	SFont*	pPrevFont;
	int		found = 0;

	// Find font and unchain from list.
	if( pFontList == pFont )
	{
		found=1;
		pFontList = pFontList->pNext;
	}
	else
	{
		for( pPrevFont=pFontList; pPrevFont->pNext; pPrevFont=pPrevFont->pNext )
		{
			if( pPrevFont->pNext == pFont )
			{
				found = 1;
				pPrevFont->pNext = pFont->pNext;
				break;
			}
		}
	}

	Dbg_MsgAssert( found, ( "Attempt to unload font which has not been loaded" ));

	// Free memory.
	delete [] pFont->pChars;
	delete pFont;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 SFont::GetDefaultHeight() const
{
	return DefaultHeight;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 SFont::GetDefaultBase() const
{
	return DefaultBase;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SFont::QueryString( char *String, float &width, float &height )
{
	SChar	*pChar;
	char	*pLetter;
	int		x0,x1;

	x0 = 0;

	for (pLetter=String;; pLetter++)
	{
		pChar = NULL;
		// may be overridden by the '\b' tag
		SFont *p_font = this;
		
		// acount for tags (might be multiple ones in a row)
		bool got_char_tag = false; // tag resulting in output of character
		while (*pLetter == '\\' && !got_char_tag)
		{
			pLetter++;
			if (*pLetter == '\\')
				break;

			switch(*pLetter)
			{
				case '\\':
					got_char_tag = true;
					break;
				case 'c':
				case 'C':
					pLetter += 2; // skip over "c#"
					break;
				case 's':
				case 'S':
				{
					pLetter++; // skip "s"
					uint digit = Str::DehexifyDigit(pLetter);
					pChar = pChars + SpecialMap[digit];
					got_char_tag = true;
					break;
				}
				case 'b':
				case 'B':
				{
					pLetter++; // skip "b"
					uint digit = Str::DehexifyDigit(pLetter);
					
					// switch over to buttons font, the regular font will be used again on the next character

					p_font = pButtonsFont;
					Dbg_Assert(p_font);
					pChar = p_font->pChars + p_font->SpecialMap[digit];
					got_char_tag = true;
					break;
				}
				case 'm':
				case 'M':
				{
					pLetter++; // skip "m"
					char button_char = Nx::CFontManager::sMapMetaCharacterToButton(pLetter);
					uint digit = Str::DehexifyDigit(&button_char);
					
					p_font = pButtonsFont;
					Dbg_Assert(p_font);
					pChar = p_font->pChars + p_font->SpecialMap[digit];
					got_char_tag = true;
					break;
				}
				default:
					Dbg_MsgAssert(0, ("unknown tag"));
					break;
			}
		} // end while
		
		if (*pLetter == '\0') break;
		
		if (*pLetter!=' ' || pChar)
		{
			if (!pChar)
				pChar = p_font->pChars + p_font->Map[(uint8)*pLetter];
			x1 = x0 + pChar->w;
		}
		else
		{
			x1 = x0 + mSpaceSpacing;
		}

		//x0 = x1 + mCharSpacing + 1;
		x0 = x1 + mCharSpacing;
	}

	width  = (float)x0;
	height = (float)DefaultHeight;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
SText::SText( float pri ) : SDraw2D( pri, true )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
SText::~SText( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SText::BeginDraw( void )
{
	p_locked_font_vertex_buffer = &( font_vertex_buffer[0] );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SText::Draw( void )
{
	SChar	*pChar;
	char	*pLetter;
	float	u0,v0,x0,y0,u1,v1,x1,y1,yt;

	x0 = SCREEN_CONV_X( m_xpos );
	y0 = SCREEN_CONV_Y( m_ypos );

	float char_spacing	= (float)mp_font->mCharSpacing * m_xscale;
	float space_spacing = (float)mp_font->mSpaceSpacing * m_xscale;
	
	DWORD current_color	= ( m_rgba & 0xFF00FF00 ) | (( m_rgba & 0xFF ) << 16 ) | (( m_rgba & 0xFF0000 ) >> 16 );
	
	float text_z = GetZValue();
	
	for( pLetter = mp_string;; pLetter++ )
	{
		pChar = NULL;
		SFont *p_font = mp_font;

		sFontVert* p_vert = ((sFontVert*)p_locked_font_vertex_buffer ) + font_vertex_offset;

		// acount for tags (might be multiple ones in a row)
		bool got_char_tag = false; // tag resulting in output of character
		while (*pLetter == '\\' && !got_char_tag)
		{
			pLetter++;

			switch(*pLetter)
			{
				case '\\':
					got_char_tag = true;
					break;
				case 'c':
				case 'C':
				{
					pLetter++;	// skip "c"					
					uint digit = Str::DehexifyDigit(pLetter);
					pLetter++; // skip "#"
					
					// Set active color from font.
					if( digit == 0 || m_color_override)
					{
						// Switch from RGBA to BGRA format.
						current_color	= ( m_rgba & 0xFF00FF00 ) | (( m_rgba & 0xFF ) << 16 ) | (( m_rgba & 0xFF0000 ) >> 16 );
					}
					else
					{
						// Switch from RGBA to BGRA format.
						uint32 color	= mp_font->mRGBATab[digit-1];
						current_color	= ( color & 0xFF00FF00 ) | (( color & 0xFF ) << 16 ) | (( color & 0xFF0000 ) >> 16 );
					}
					break;
				}
				case 's':
				case 'S':
				{
					pLetter++;	// skip "s"
					uint digit = Str::DehexifyDigit(pLetter);
					
					pChar = mp_font->pChars + mp_font->SpecialMap[digit];
					got_char_tag = true;
					
					break;
				}
				case 'b':
				case 'B':
				{
					// 'B' stands for button, accesses the button font

					pLetter++; // skip "b"
					uint digit = Str::DehexifyDigit( pLetter );
					
					// switch to the buttons font!
					p_font = pButtonsFont;
					Dbg_Assert( p_font );
					
					pChar = p_font->pChars + p_font->SpecialMap[digit];
					got_char_tag = true;
					
					EndDraw();
					BeginDraw();

					// Reset the vertex data pointer.
					p_vert = ((sFontVert*)p_locked_font_vertex_buffer ) + font_vertex_offset;
					
					spOverrideFont = p_font;
					break;
				}
				default:
				{
					Dbg_MsgAssert( 0, ( "unknown tag" ));
					break;
				}
			}
		} // end while
		
		if (*pLetter == '\0') break;
		
		if( *pLetter != ' ' || pChar)
		{
			if (!pChar)
				pChar = p_font->pChars + p_font->Map[(uint8) *pLetter];
			yt = y0 + ((float)( p_font->DefaultBase - pChar->Baseline ) * m_yscale ) * EngineGlobals.screen_conv_y_multiplier;
			u0 = pChar->u0;
			v0 = pChar->v0;
			u1 = pChar->u1;
			v1 = pChar->v1;
			x1 = x0 + ( pChar->w * m_xscale * EngineGlobals.screen_conv_x_multiplier );
			y1 = yt + ( pChar->h * m_yscale * EngineGlobals.screen_conv_y_multiplier );
		}
		else
		{
			x0 += ( space_spacing + char_spacing ) * EngineGlobals.screen_conv_x_multiplier;
			continue;
		}

		p_vert->x	= x0;
		p_vert->y	= yt;
		p_vert->z	= text_z;
		p_vert->rhw	= 0.0f;
		p_vert->col	= current_color;
		p_vert->u	= u0;
		p_vert->v	= v0;
		++p_vert;

		p_vert->x	= x0;
		p_vert->y	= y1;
		p_vert->z	= text_z;
		p_vert->rhw	= 0.0f;
		p_vert->col	= current_color;
		p_vert->u	= u0;
		p_vert->v	= v1;
		++p_vert;

		p_vert->x	= x1;
		p_vert->y	= y1;
		p_vert->z	= text_z;
		p_vert->rhw	= 0.0f;
		p_vert->col	= current_color;
		p_vert->u	= u1;
		p_vert->v	= v1;
		++p_vert;

		p_vert->x	= x1;
		p_vert->y	= yt;
		p_vert->z	= text_z;
		p_vert->rhw	= 0.0f;
		p_vert->col	= current_color;
		p_vert->u	= u1;
		p_vert->v	= v0;

		font_vertex_offset += 4;

		if( font_vertex_offset >= ( CHARS_PER_BUFFER * 4 ))
		{
			// Draw this buffer and cycle through to the next.
			EndDraw();
			BeginDraw();
		}

		x0 = x1 + ( char_spacing * EngineGlobals.screen_conv_x_multiplier );

		if( p_font != mp_font )
		{
			// We just used the button font, so return to the regular one.
			EndDraw();
			BeginDraw();
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SText::EndDraw( void )
{
	if( font_vertex_offset > 0 )
	{
		// Subsequent processing within Draw() will use this font
		// Draw() may call this function to temporarily switch fonts
		SFont *p_font = ( spOverrideFont ) ? spOverrideFont : mp_font;
		
		// Set up the render state and submit.
		set_pixel_shader( PixelShader4 );
		set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));

		set_texture( 0, p_font->pD3DTexture, p_font->pD3DPalette );

		EngineGlobals.p_Device->DrawVerticesUP( D3DPT_QUADLIST, font_vertex_offset, &( font_vertex_buffer[0] ), sizeof( sFontVert ));

		// Reset offset.
		font_vertex_offset = 0;

		// We can now return to using the regular font (no override).
		spOverrideFont = NULL;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SetTextWindow( uint16 x0, uint16 x1, uint16 y0, uint16 y1 )
{
}

} // namespace Xbox



================================================
FILE: Code/Gfx/XBox/NX/chars.h
================================================
#ifndef __CHARS_H
#define __CHARS_H

#include 

namespace NxXbox
{


typedef struct
{
	float	u0, v0, u1, v1;
	uint16	w, h;
	uint16	Baseline;
}
SChar;


struct SFont
{
public:
	uint32		GetDefaultHeight() const;
	uint32		GetDefaultBase() const;

//	void		BeginText(uint32 rgba, float Scale);
//	void		DrawString(char *String, float x0, float y0);
//	void		EndText(void);
	void		QueryString(char *String, float &width, float &height);

	//char Name[16];
	uint32		DefaultHeight, DefaultBase;
	SChar		*pChars;
	uint8		Map[256];
	uint8		SpecialMap[32];
//	uint8		*pVifData;
//	uint32		VifSize;
//	uint64		RegTEX0, RegTEX1;
	SFont		*pNext;

	sint16		mCharSpacing;
	sint16		mSpaceSpacing;
	uint32		mRGBATab[16];
	
	IDirect3DTexture8*	pD3DTexture;		// To do - these should probably be replaced with an sTexture.
	IDirect3DPalette8*	pD3DPalette;
};



SFont*		InitialiseMemoryResidentFont( void );
SFont*		LoadFont( const char* Filename, bool memory_resident = false );
void		UnloadFont( SFont * );
void		SetTextWindow( uint16 x0, uint16 x1, uint16 y0, uint16 y1 );


struct SText : public SDraw2D
{
	public:
					SText( float pri = 0.0f );
	virtual			~SText();

	SFont			*mp_font;

	char			*mp_string;
	float			m_xpos;
	float			m_ypos;
	float			m_xscale;
	float			m_yscale;
	uint32			m_rgba;
	bool			m_color_override;
	
	// used in conjunction with BeginDraw()
	// if set, use specified font instead of mp_font
	// if not, use mp_font
	static SFont *	spOverrideFont;

	void			BeginDraw( void );
	void			Draw( void );
	void			EndDraw( void );
};

void SwizzleTexture( void *dstBuffer, void *srcBuffer, int width, int height, int32 depth, int32 stride );


extern uint32 FontVramBase;
extern SFont *pFontList;
extern SFont *pButtonsFont;


} // namespace NxXbox


#endif // __CHARS_H


================================================
FILE: Code/Gfx/XBox/NX/gamma.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		gamma.cpp												**
**																			**
**	Created:		02/27/02	-	dc										**
**																			**
**	Description:	Gamma setting code										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/
		
#include 
#include "gamma.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

namespace NxXbox
{


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// Some defines for gamma settings.
#define gammaDefault	1.0f
#define gammaDomain		256
#define gammaRange		256.0f
#define MIN_GAMMA		0.0f
#define MAX_GAMMA		2.0f



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static D3DGAMMARAMP	gammaTable;
static float		gammaValueR = gammaDefault;	// Current red gamma value.
static float		gammaValueG = gammaDefault;	// Current green gamma value.
static float		gammaValueB = gammaDefault;	// Current blue gamma value.



/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void gammaInitTable( void )
{
	// Create the gamma intensity lookup table.
	// It works like this: given a colour value r [0,255] do r' = gammaTable.red[r] etc.
	for( int i = 0; i < gammaDomain; ++i )
	{
		gammaTable.red[i]	= (BYTE)((float)gammaRange * pow(((float)i / (float)gammaDomain ), 1.0f / gammaValueR ));
		gammaTable.green[i]	= (BYTE)((float)gammaRange * pow(((float)i / (float)gammaDomain ), 1.0f / gammaValueG ));
		gammaTable.blue[i]	= (BYTE)((float)gammaRange * pow(((float)i / (float)gammaDomain ), 1.0f / gammaValueB ));
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void gammaSetValue( float gr, float gg, float gb )
{
	gammaValueR = gr;
	gammaValueG = gg;
	gammaValueB = gb;

	// Create the table.
	gammaInitTable();

	// Pass the table along to the hardware.
	D3DDevice_SetGammaRamp( D3DSGR_NO_CALIBRATION, &gammaTable );
}



/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SetGammaNormalized( float fr, float fg, float fb )
{
	// Cap it....
	fr = ( fr < 0.0f ) ? 0.0f : (( fr > 1.0f ) ? 1.0f : fr );
	fg = ( fg < 0.0f ) ? 0.0f : (( fg > 1.0f ) ? 1.0f : fg );
	fb = ( fb < 0.0f ) ? 0.0f : (( fb > 1.0f ) ? 1.0f : fb );

	// Scale it....
	fr *= MAX_GAMMA;
	fg *= MAX_GAMMA;
	fb *= MAX_GAMMA;

	// Offset it.
	fr += 0.5f;
	fg += 0.5f;
	fb += 0.5f;

	// f now goes from 0.5 to 2.5.
	gammaSetValue( fr, fg, fb );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void GetGammaNormalized( float *fr, float *fg, float *fb )
{
	// Peform the opposite operation to that done in SetGammaNormalized().
	*fr = ( gammaValueR - 0.5f ) / MAX_GAMMA;
	*fg = ( gammaValueG - 0.5f ) / MAX_GAMMA;
	*fb = ( gammaValueB - 0.5f ) / MAX_GAMMA;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


} // namespace Gfx


================================================
FILE: Code/Gfx/XBox/NX/gamma.h
================================================
#ifndef	__GAMMA_H__
#define	__GAMMA_H__

namespace NxXbox
{
	void SetGammaNormalized( float fr, float fg, float fb );
	void GetGammaNormalized( float *fr, float *fg, float *fb );
}

#endif



================================================
FILE: Code/Gfx/XBox/NX/grass.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "nx_init.h"
#include "render.h"
#include "grass.h"

static bool				bumpsLoaded = false;
NxXbox::sMaterial		waterMaterial;
NxXbox::sTexture		waterTextures[2];

IDirect3DTexture8		*pBumpTextures[17];
NxXbox::sTexture		bumpTextures[17];


namespace NxXbox
{

}



/******************************************************************/
/*                                                                */
/* Unswizzles a 2D texture before it gets unlocked.				  */
/* Note: this operation can be very slow.						  */
/*                                                                */
/******************************************************************/
VOID XBUtil_UnswizzleTexture2D( D3DLOCKED_RECT* pLock, const D3DSURFACE_DESC* pDesc )
{
    DWORD dwPixelSize   = XGBytesPerPixelFromFormat( pDesc->Format );
    DWORD dwTextureSize = pDesc->Width * pDesc->Height * dwPixelSize;

    BYTE* pSrcBits = new BYTE[ dwTextureSize ];
    memcpy( pSrcBits, pLock->pBits, dwTextureSize );
    
    XGUnswizzleRect( pSrcBits, pDesc->Width, pDesc->Height, NULL, pLock->pBits, 
                     0, NULL, dwPixelSize );

    delete [] pSrcBits;
}



/******************************************************************/
/*                                                                */
/* Swizzles a 2D texture before it gets unlocked.				  */
/* Note: this operation can be very slow.						  */
/*                                                                */
/******************************************************************/
VOID XBUtil_SwizzleTexture2D( D3DLOCKED_RECT* pLock, const D3DSURFACE_DESC* pDesc )
{
    DWORD dwPixelSize   = XGBytesPerPixelFromFormat( pDesc->Format );
    DWORD dwTextureSize = pDesc->Width * pDesc->Height * dwPixelSize;

    BYTE* pSrcBits = new BYTE[ dwTextureSize ];
    memcpy( pSrcBits, pLock->pBits, dwTextureSize );
    
    XGSwizzleRect( pSrcBits, 0, NULL, pLock->pBits,
                  pDesc->Width, pDesc->Height, 
                  NULL, dwPixelSize );

    delete [] pSrcBits;
}



/******************************************************************/
/*                                                                */
/* There are 17 frames. Each has 3 level mipmaps				  */
/*                                                                */
/******************************************************************/
HRESULT LoadBumpTextures( void )
{
	HRESULT hr = S_OK;
	for( int i = 0; i < 17; ++i )
    {
		int height = 128, width = 128;

        // Find file name
		char strBumpFile[128];
		sprintf( strBumpFile, "c:\\skate5\\data\\images\\miscellaneous\\%d.bum", i );
        
        // Open and read file.
		void *fp = File::Open( strBumpFile, "rb" );
        if( !fp )
            return E_FAIL;
      
		// Create texture.
		if( FAILED( hr = D3DDevice_CreateTexture( width,
            height,
            3,
            NULL,
            D3DFMT_V8U8,
            D3DPOOL_MANAGED,
			&pBumpTextures[i] )))
            return hr;
       
        // Lock first level.
        D3DLOCKED_RECT lr; 
        D3DSURFACE_DESC desc;
        pBumpTextures[i]->GetLevelDesc( 0, &desc );
        pBumpTextures[i]->LockRect( NULL, &lr, NULL, NULL );
        XBUtil_UnswizzleTexture2D( &lr, &desc );

		File::Read( lr.pBits, sizeof( WORD ), height * width, fp );

		// Modulate the size of the offset.
		uint8 *p_mod = (uint8*)lr.pBits;
		for( int p = 0; p < height * width * 2; ++p )
		{
			// Noticed occasionally this maps to outside of the map tetxure with texbem.
			// Culprit appears to be the maximum negative value (0, given 0x80 offset of values
			// in V8U8 format).
			if( *p_mod == 0 )
			{
				// Max negative offset.
				*p_mod = 1;
			}
			++p_mod;
		}
		
		XBUtil_SwizzleTexture2D( &lr, &desc );
        pBumpTextures[i]->UnlockRect( 0 );

        // Load mipmaps
		int level = 0;
        while( ++level < 3 )
        {
            width >>= 1;
            height >>= 1;
                        
            pBumpTextures[i]->GetLevelDesc( level, &desc );
            pBumpTextures[i]->LockRect( level, &lr, NULL,NULL );
            XBUtil_UnswizzleTexture2D( &lr, &desc );

			File::Read( lr.pBits, sizeof( WORD ), height * width, fp );

			// Modulate the size of the offset.
			p_mod = (uint8*)lr.pBits;
			for( int p = 0; p < height * width * 2; ++p )
			{
				// Noticed occasionally this maps to outside of the map tetxure with texbem.
				// Culprit appears to be the maximum negative value (0, given 0x80 offset of values
				// in V8U8 format).
				if( *p_mod == 0 )
				{
					// Max negative offset.
					*p_mod = 1;
				}
				++p_mod;
			}
			
			XBUtil_SwizzleTexture2D( &lr, &desc );
            pBumpTextures[i]->UnlockRect( level );
        }
		File::Close( fp );

		bumpTextures[i].pD3DTexture = pBumpTextures[i];
		bumpTextures[i].pD3DPalette	= NULL;
	}

	// Set up the texture container for the bump textures.
	ZeroMemory( &waterTextures[0],	sizeof( NxXbox::sTexture ));
	waterTextures[0].pD3DTexture = pBumpTextures[0];

	return hr;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CreateWaterMaterial( NxXbox::sMaterial *p_material )
{
	// Set texture 0 to be the first bump texture.
	p_material->mp_tex[0]			= &waterTextures[0];
	p_material->mp_tex[1]			= &waterTextures[1];
	p_material->m_reg_alpha[0]		= NxXbox::vBLEND_MODE_BLEND_FIXED | ( 0x40UL << 24 );
	p_material->m_color[0][3]		= 0.5f;
	p_material->m_passes			= 2;

	// Add transparent flag.
	p_material->m_flags[0]			|= 0x40;
	p_material->m_flags[1]			= 0x00;

	// Add bump setup flag.
	p_material->m_flags[0]			|= MATFLAG_BUMP_SIGNED_TEXTURE;
	p_material->m_flags[1]			|= MATFLAG_BUMP_LOAD_MATRIX;

	p_material->m_uv_wibble			= false;
	p_material->m_flags[0]			&= ~MATFLAG_ENVIRONMENT;
	p_material->m_flags[0]			&= ~MATFLAG_UV_WIBBLE;

	// Adjust K.
	p_material->m_k[0]				= 0.0f;
	p_material->m_k[1]				= 0.0f;

	// Set texture address mode.
	p_material->m_uv_addressing[0]	= 0x00000000UL;
	p_material->m_uv_addressing[1]	= 0x00000000UL;

	// Set the draw order to ensure meshes are drawn from the bottom up. Default draw order for transparent
	// meshes is 1000.0, so add a little onto that.
	p_material->m_sorted			= false;
	p_material->m_draw_order		= 1501.0f;

	// Create the animating texture details.
	p_material->m_texture_wibble	= true;
	p_material->m_flags[0]			|= MATFLAG_PASS_TEXTURE_ANIMATES;

	p_material->mp_wibble_texture_params						= new NxXbox::sTextureWibbleParams;
	p_material->mp_wibble_texture_params->m_num_keyframes[0]	= 18;
	p_material->mp_wibble_texture_params->m_phase[0]			= 0;
	p_material->mp_wibble_texture_params->m_num_iterations[0]	= 0;
	p_material->mp_wibble_texture_params->mp_keyframes[0]		= new NxXbox::sTextureWibbleKeyframe[18];
	p_material->mp_wibble_texture_params->mp_keyframes[1]		= NULL;
	p_material->mp_wibble_texture_params->mp_keyframes[2]		= NULL;
	p_material->mp_wibble_texture_params->mp_keyframes[3]		= NULL;

	for( int f = 0; f < 18; ++f )
	{
		p_material->mp_wibble_texture_params->mp_keyframes[0][f].m_time						= (( f * 1000 ) / 30 );
		p_material->mp_wibble_texture_params->mp_keyframes[0][f].mp_texture					= &bumpTextures[f % 17];
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool AddWater( Nx::CXboxGeom *p_geom, NxXbox::sMesh *p_mesh )
{
	// Ensure there is at least two sets of uv's available.
	Dbg_Assert( p_mesh->m_uv0_offset > 0 );

	// Check to see whether the texture we want is present.
	Nx::CScene		*p_sky_scene		= Nx::CEngine::sGetSkyScene();
	Nx::CTexDict	*p_tex_dict			= p_sky_scene->GetTexDict();
	Nx::CTexture	*p_texture			= p_tex_dict->GetTexture( 0x96d0bf08UL );

	if( p_texture == NULL )
		return false;

	Nx::CXboxTexture	*p_xbox_texture = static_cast( p_texture );
	NxXbox::sTexture	*p_engine_texture	= p_xbox_texture->GetEngineTexture();
	CopyMemory( &waterTextures[1], p_engine_texture, sizeof( NxXbox::sTexture ));

	int num_tc_sets = ( p_mesh->m_vertex_stride - p_mesh->m_uv0_offset ) / 8;
	if( num_tc_sets == 1 )
	{
		// We need to alter this mesh to create two sets of uv's.
		IDirect3DVertexBuffer8* p_new_buffer = p_mesh->AllocateVertexBuffer(( p_mesh->m_vertex_stride + 8 ) * p_mesh->m_num_vertices );
		BYTE* p_read_byte;
		BYTE* p_write_byte;
		p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_read_byte, D3DLOCK_READONLY | D3DLOCK_NOFLUSH );
		p_new_buffer->Lock( 0, 0, &p_write_byte, D3DLOCK_NOFLUSH );

		for( int v = 0; v < p_mesh->m_num_vertices; ++v )
		{
			// Copy all existing vertex information.
			CopyMemory( p_write_byte, p_read_byte, p_mesh->m_vertex_stride );
			p_read_byte		+= p_mesh->m_vertex_stride;
			p_write_byte	+= p_mesh->m_vertex_stride + 8;
		}

		delete p_mesh->mp_vertex_buffer[0];
		p_mesh->mp_vertex_buffer[0] = p_new_buffer;

		p_mesh->m_vertex_stride += 8;

		// Switch the vertex shader.
		p_mesh->m_vertex_shader[0]	&= ~( D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
		p_mesh->m_vertex_shader[0]	|= D3DFVF_TEX2 | D3DFVF_TEXCOORDSIZE2( 0 ) | D3DFVF_TEXCOORDSIZE2( 1 );
	}

	NxXbox::sMaterial *p_water_mat = p_mesh->mp_material;

	CreateWaterMaterial( p_water_mat );

	// Disable skater shadow on the mesh.
	p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_NO_SKATER_SHADOW;
	p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_BUMPED_WATER;

	// Set the water pixel shader.
	p_mesh->m_pixel_shader	= PixelShaderBumpWater;
		
	// Go through and calculate new texture coordinates based on the x-z space of the vertex positions.
	BYTE *p_byte_dest;
	p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_byte_dest, 0 );
	for( uint32 vert = 0; vert < p_mesh->m_num_vertices; ++vert )
	{
		D3DXVECTOR3 *p_pos	= (D3DXVECTOR3*)( p_byte_dest + ( vert * p_mesh->m_vertex_stride ));

		float u0				= p_pos->x / 192.0f;
		float v0				= p_pos->z / 192.0f;

		float u1				= p_pos->x / 96.0f;
		float v1				= p_pos->z / 96.0f;


		float *p_tex		= (float*)( p_byte_dest + p_mesh->m_uv0_offset + ( vert * p_mesh->m_vertex_stride ));
		p_tex[0]			= u0;
		p_tex[1]			= v0;
		p_tex[2]			= u1;
		p_tex[3]			= v1;
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool AddGrass( Nx::CXboxGeom *p_geom, NxXbox::sMesh *p_mesh )
{
	// Need a material to proceed.
	if( p_mesh->mp_material == NULL )
		return false;

	if( p_mesh->mp_material->m_flags[0] & MATFLAG_WATER_EFFECT )
	{
		return AddWater( p_geom, p_mesh );
	}

	if( p_mesh->mp_material->m_grass_layers == 0 )
	{
		return false;
	}

	// At this stage we know we want to add grass.
	Dbg_Assert( p_mesh->mp_material->m_grass_layers > 0 );

	int		grass_layers		= ( p_mesh->mp_material->m_grass_layers > 5 ) ? 5 : p_mesh->mp_material->m_grass_layers;
	float	height_per_layer	= p_mesh->mp_material->m_grass_height / grass_layers;
	
	for( int layer = 0; layer < grass_layers; ++layer )
	{
		Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().BottomUpHeap());
		NxXbox::sMesh *p_grass_mesh = p_mesh->Clone( false );
		Mem::Manager::sHandle().PopContext();
		
		// Disable skater shadow on the mesh.
		p_grass_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_NO_SKATER_SHADOW;
		
		// Turn off anisotropic filtering and z-write on the mesh.
		p_grass_mesh->m_flags |= ( NxXbox::sMesh::MESH_FLAG_NO_ANISOTROPIC | NxXbox::sMesh::MESH_FLAG_NO_ZWRITE );
		
		// Now point the mesh to a different material, first build the material checksum from the name of the parent
		// material (Grass) and the name of the sub material (Grass_LayerX)...
		char material_name[64];
		sprintf( material_name, "Grass-Grass_Layer%d", layer );
		uint32 material_checksum	= Crc::GenerateCRCCaseSensitive( material_name, strlen( material_name ));

		// ...then use the checksum to lookup the material.
		p_grass_mesh->mp_material	= p_geom->mp_scene->GetEngineScene()->GetMaterial( material_checksum );
		Dbg_MsgAssert( p_grass_mesh->mp_material, ( "Grass maaterial for layer %d appears to be named badly", layer ));
		
		// We need also to override the mesh pixel shader, which may have been setup for a multipass material.
		NxXbox::GetPixelShader( p_grass_mesh->mp_material, &p_grass_mesh->m_pixel_shader );
		
		// Add transparent flag to the material.
		p_grass_mesh->mp_material->m_flags[0]		|= 0x40;

		// Adjust K for the material.
		p_grass_mesh->mp_material->m_k[0]			= -2.0f;

		// Set the draw order to ensure meshes are drawn from the bottom up. Default draw order for transparent
		// meshes is 1000.0, so add a little onto that.
		p_grass_mesh->mp_material->m_sorted		 = false;
		p_grass_mesh->mp_material->m_draw_order	 = 1100.0f + ( layer * 0.1f );
		
		// Go through and move all the vertices up a little, and calculate new texture coordinates based on the x-z space of the vertex positions.
		BYTE *p_byte_dest;
		p_grass_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_byte_dest, 0 );

		for( uint32 vert = 0; vert < p_grass_mesh->m_num_vertices; ++vert )
		{
			D3DXVECTOR3 *p_pos = (D3DXVECTOR3*)( p_byte_dest + ( vert * p_grass_mesh->m_vertex_stride ));
			p_pos->y += ( height_per_layer * ( layer + 1 ));

			float u			= p_pos->x / 48.0f;
			float v			= p_pos->z / 48.0f;
			float *p_tex	= (float*)( p_byte_dest + p_grass_mesh->m_uv0_offset + ( vert * p_grass_mesh->m_vertex_stride ));
			p_tex[0]		= u;		
			p_tex[1]		= v;
		}

		p_geom->AddMesh( p_grass_mesh );
	}

	// Turn off anisotropic filtering on the base mesh also.
	p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_NO_ANISOTROPIC;
	
	return true;
}


================================================
FILE: Code/Gfx/XBox/NX/grass.h
================================================
#pragma once
//-----------------------------------------------------------------------------
// File: XBFur.h
//
// Copyright (c) 2000-2001 Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#include 
//#include "xfvf.h"
#include 

#define XBFUR_MAXSLICE_LOG2 5
#define XBFUR_MAXSLICE (1 << XBFUR_MAXSLICE_LOG2)

extern float g_fOneInch;

#define FVF_XYZDIFF (D3DFVF_XYZ|D3DFVF_DIFFUSE)

typedef struct sFVFT_XYZDIFF
{
    D3DVECTOR v;
    DWORD diff;
} FVFT_XYZDIFF;


// patch generation

// a fuzz is a single hair follicle, blade of grass, etc.
struct Fuzz
{
    D3DVECTOR dp;            // velocity
    D3DVECTOR ddp;            // acceleration
    D3DXCOLOR colorBase;
    D3DXCOLOR colorTip;
};

// a fuzz instance is a single instance of a fuzz
// located at x, z on the patch
// we create only a limited number of unique fuzzes
// and index the library with lidx.
struct FuzzInst
{
    float x, z;                // fuzz location
    int lidx;                // library index
};

// a fur patch is a volume that holds fuzzes.
// xsize and zsize are chosen by the user
// ysize is calculated using the height of the 
// tallest fuzz
class CXBFur
{
    friend class CXBFurMesh;
public:
    DWORD m_dwSeed;            // patch seed
    
    float m_fXSize;            // patch size in world coords
    float m_fYSize;
    float m_fZSize;

    // fuzz library
    DWORD m_dwNumSegments;    // # of segments in highest LOD
    Fuzz m_fuzzCenter;        // fuzz constant
    Fuzz m_fuzzRandom;        // random offset around center
    DWORD m_dwNumFuzzLib;    // # of fuzz in the library
    Fuzz *m_pFuzzLib;        // fuzz library

    // fuzz instances
    DWORD m_dwNumFuzz;        // # of fuzz in this patch
    FuzzInst *m_pFuzz;

    // patch volume
    DWORD m_dwNumSlices;    // # of layers in the volume
    DWORD m_dwSliceSize;        // width*height
    DWORD m_dwSliceXSize;        // width of volume texture slice
    DWORD m_dwSliceZSize;        // height of volume texture slice
    LPDIRECT3DTEXTURE8 m_apSliceTexture[XBFUR_MAXSLICE * 2 - 1];    // slices of volume texture
                    // ... followed by level-of-detail textures  N/2, N/4, N/8, ... 1

    // LOD textures
    DWORD m_dwNumSlicesLOD; // number of slices in current level of detail
    float m_fLevelOfDetail;    // current LOD value
    DWORD m_iLOD;            // current integer LOD value
    float m_fLODFraction;    // fraction towards next coarser level-of-detail
    DWORD m_dwLODMax;        // maximum LOD index
    LPDIRECT3DTEXTURE8 *m_pSliceTextureLOD; // current level of detail pointer into m_apSliceTexture array

    // hair lighting texture
    D3DMATERIAL8 m_HairLightingMaterial;
    LPDIRECT3DTEXTURE8 m_pHairLightingTexture;

    // fin texture
    DWORD m_finWidth, m_finHeight;        // size of fin texture
    float m_fFinXFraction, m_fFinZFraction;    // portion of hair texture to put into fin
    LPDIRECT3DTEXTURE8 m_pFinTexture;    // texture projected from the side

    CXBFur();
    ~CXBFur();
    void InitFuzz(DWORD nfuzz, DWORD nfuzzlib);
    void GenSlices(DWORD nslices, DWORD slicexsize, DWORD slicezsize);
    void GenFin(DWORD finWidth, DWORD finHeight, float fFinXFraction, float fFinZFraction);
    void GetLinesVertexBuffer(IDirect3DVertexBuffer8 **ppVB);
    void RenderLines();
    void Save(char *fname, int flags);
    void Load(char *fname);
    HRESULT SetHairLightingMaterial(D3DMATERIAL8 *pMaterial);
    void SetPatchSize(float x, float z)
    {
        m_fXSize = x;
        m_fZSize = z;
        InitFuzz(m_dwNumFuzz, m_dwNumFuzzLib);    // re-init the fuzz. automatically sets ysize
    };
    void SetFVel(float cx, float cy, float cz, float rx, float ry, float rz)
    {
        m_fuzzCenter.dp.x = cx; m_fuzzCenter.dp.y = cy; m_fuzzCenter.dp.z = cz;
        m_fuzzRandom.dp.x = rx; m_fuzzRandom.dp.y = ry; m_fuzzRandom.dp.z = rz;
    };
    void SetFAcc(float cx, float cy, float cz, float rx, float ry, float rz)
    {
        m_fuzzCenter.ddp.x = cx; m_fuzzCenter.ddp.y = cy; m_fuzzCenter.ddp.z = cz;
        m_fuzzRandom.ddp.x = rx; m_fuzzRandom.ddp.y = ry; m_fuzzRandom.ddp.z = rz;
    };

    // fLevelOfDetail can range from 0 to log2(NumSlices)
    HRESULT SetLevelOfDetail(float fLevelOfDetail);
    HRESULT ComputeLevelOfDetailTextures();
    inline UINT LevelOfDetailCount(UINT iLOD)
    {
        return m_dwNumSlices >> iLOD;
    }
    inline UINT LevelOfDetailIndex(UINT iLOD)
    {
        UINT offset = 0;
        for (UINT i = 1; i <= iLOD; i++)
            offset += LevelOfDetailCount(i-1);
        return offset;
    }
    inline UINT TotalTextureCount()
    {
        UINT TextureCount = 0;
        for (UINT iLOD = 0; m_dwNumSlices >> iLOD; iLOD++)
            TextureCount += LevelOfDetailCount(iLOD);
        return TextureCount;
    }

    // Compress textures one at a time until all are done.
    // Returns S_OK when all the textures are in fmtNew format.
    // Returns S_FALSE if there are textures still to be done.
    HRESULT CompressNextTexture(D3DFORMAT fmtNew, UINT *pTextureIndex);
};

HRESULT	FillHairLightingTexture(D3DMATERIAL8 *pMaterial, LPDIRECT3DTEXTURE8 pTexture);
bool	AddGrass( Nx::CXboxGeom *p_geom, NxXbox::sMesh *p_mesh );
HRESULT	LoadBumpTextures( void );



================================================
FILE: Code/Gfx/XBox/NX/instance.cpp
================================================
#include 
#include 
#include 
#include 
#include "nx_init.h"
#include "instance.h"
#include "anim.h"
#include "render.h"
#include "occlude.h"
#include "anim_vertdefs.h"

namespace NxXbox
{

static Mth::Matrix	*pLastBoneTransforms	= NULL;
CInstance			*pFirstInstance			= NULL;


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int sort_by_bone_transform( const void *p1, const void *p2 )
{
	CInstance	*p_mesh1		= *((CInstance**)p1 );
	CInstance	*p_mesh2		= *((CInstance**)p2 );

	Mth::Matrix *p_mat1			= p_mesh1->GetBoneTransforms();
	Mth::Matrix *p_mat2			= p_mesh2->GetBoneTransforms();
	
	if(( p_mat1 == NULL ) || ( p_mesh1->GetScene()->m_numHierarchyObjects > 0 ))
	{
		if(( p_mat2 == NULL ) || ( p_mesh2->GetScene()->m_numHierarchyObjects > 0 ))
		{
			int num_st1 = p_mesh1->GetScene()->m_num_semitransparent_mesh_entries;
			int num_st2 = p_mesh2->GetScene()->m_num_semitransparent_mesh_entries;

			if( num_st1 == num_st2 )
			{
				// Try sorting on the material draw order of the first semitransparent mesh.
				if( num_st1 > 0 )
				{
					NxXbox::sMaterial	*p_material1 = p_mesh1->GetScene()->m_meshes[p_mesh1->GetScene()->m_first_semitransparent_entry]->mp_material;
					NxXbox::sMaterial	*p_material2 = p_mesh2->GetScene()->m_meshes[p_mesh2->GetScene()->m_first_semitransparent_entry]->mp_material;

					if( p_material1 && p_material2 )
					{
						return ( p_material1->m_draw_order > p_material2->m_draw_order ) ? 1 : (( p_material1->m_draw_order < p_material2->m_draw_order ) ? -1 : 0 );
					}
				}
				return 0;
			}
	
			return ( num_st1 > num_st2 ) ? 1 : -1;
		}
		return 1;
	}
	else if(( p_mat2 == NULL ) || ( p_mesh2->GetScene()->m_numHierarchyObjects > 0 ))
	{
		return -1;
	}

	// At this stage we know both instances have bone transforms.
	if( p_mat1 == p_mat2 )
	{
		int num_st1 = p_mesh1->GetScene()->m_num_semitransparent_mesh_entries;
		int num_st2 = p_mesh2->GetScene()->m_num_semitransparent_mesh_entries;
	
		if( num_st1 == num_st2 )
			return 0;
	
		return ( num_st1 > num_st2 ) ? 1 : -1;
	}

	return((uint32)p_mat1 > (uint32)p_mat2 ) ? 1 : -1;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_instance( CInstance* p_instance, uint32 flags )
{
	// Seed the static pointer off to NULL, otherwise if there is only one object with bone transforms, it will never update.
	pLastBoneTransforms = NULL;

	if( p_instance->GetActive())
	{
		set_frustum_bbox_transform( p_instance->GetTransform());

		bool render = true;
		if( !( flags & vRENDER_NO_CULLING ))
		{
			// Check whether this instance is visible.
			render = false;
			if( frustum_check_sphere( &p_instance->GetScene()->m_sphere_center, p_instance->GetScene()->m_sphere_radius ))
			{
				if( !TestSphereAgainstOccluders( &p_instance->GetScene()->m_sphere_center, p_instance->GetScene()->m_sphere_radius ))
				{
					render = true;
				}
			}
		}

		if( render )
		{
			if( p_instance->GetBoneTransforms() != NULL )
			{
				startup_weighted_mesh_vertex_shader();
				p_instance->Render( vRENDER_OPAQUE | vRENDER_SEMITRANSPARENT );
				shutdown_weighted_mesh_vertex_shader();
			}
			else
			{
				p_instance->Render( vRENDER_OPAQUE | vRENDER_SEMITRANSPARENT );
			}
		}

		// Restore world transform to identity.
		D3DDevice_SetTransform( D3DTS_WORLD, &EngineGlobals.world_matrix );
		set_frustum_bbox_transform( NULL );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_instances( uint32 flags )
{
	#define INSTANCE_ARRAY_SIZE	4096
	static CInstance *p_instances[INSTANCE_ARRAY_SIZE];
	int current_index = 0;

	Mth::Matrix t;
	t.Identity();
	
	// Seed the static pointer off to NULL, otherwise if there is only one object with bone transforms, it will never update.
	pLastBoneTransforms = NULL;
	
	// First go through and build a list of the visible instances.
	CInstance *p_instance = pFirstInstance;
	while( p_instance )
	{
		if( p_instance->GetActive())
		{
			// Check to see whether this instance is of the type we want to render - opaque or semitransparent.
			if((( flags & vRENDER_OPAQUE ) && ( p_instance->GetScene()->m_num_mesh_entries > p_instance->GetScene()->m_num_semitransparent_mesh_entries )) ||
			   (( flags & vRENDER_SEMITRANSPARENT ) && ( p_instance->GetScene()->m_num_semitransparent_mesh_entries > 0 )))
			{
				// Check whether this instance is visible - if so, place it in the visible array.
				t.SetPos( p_instance->GetTransform()->GetPos());
				set_frustum_bbox_transform( &t );
				
				// For skinned objects, we have no idea how the skeleton transforms will be affecting the final position of each object.
				// This can sometimes result in small objects getting culled incorrectly. For these objects, increase the size of
				// the bounding sphere slightly.
				float radius = p_instance->GetScene()->m_sphere_radius;
				if( p_instance->GetBoneTransforms() && ( p_instance->GetScene()->m_numHierarchyObjects == 0 ))
				{
					radius = ( radius < 72.0f ) ? 72.0f : radius;
				}
				
				// The logic code already sets the active flag based on visibility, so there is no need to perform a second visibility check
				// at this point.
				// We do, however, want to test against occluders.
				if( !TestSphereAgainstOccluders( &p_instance->GetScene()->m_sphere_center, radius ))
				{
					Dbg_Assert( current_index < INSTANCE_ARRAY_SIZE );
					p_instances[current_index++] = p_instance;
				}
			}
		}
		p_instance = p_instance->GetNextInstance();
	}

	// Now sort the list based on bone transform and number of semitransparent objects in scene.
	if( current_index > 0 )
	{
		qsort( p_instances, current_index, sizeof( CInstance* ), sort_by_bone_transform );

		int i = 0;

		// First render the instances with bone transforms.
		startup_weighted_mesh_vertex_shader();
		for( ; i < current_index; ++i )
		{
			if(( p_instances[i]->GetBoneTransforms() == NULL ) || ( p_instances[i]->GetScene()->m_numHierarchyObjects > 0 ))
				break;

			if(( flags & vRENDER_OPAQUE ) || (( flags & vRENDER_SEMITRANSPARENT ) && ( flags & vRENDER_INSTANCE_PRE_WORLD_SEMITRANSPARENT )))
				p_instances[i]->Render( flags );
		}
		shutdown_weighted_mesh_vertex_shader();
		
		// Then the instances with no bone transforms. These will require fixed function lighting.
		D3DDevice_SetRenderState( D3DRS_LIGHTING,				TRUE );
		D3DDevice_SetRenderState( D3DRS_COLORVERTEX,			TRUE );
		D3DDevice_LightEnable( 0, TRUE );
		D3DDevice_LightEnable( 1, TRUE );

		Mth::Vector current_cam_pos( EngineGlobals.cam_position.x, EngineGlobals.cam_position.y, EngineGlobals.cam_position.z );

		// Get the (square of) the distance at which env mapping will be disabled, in inches.
		float env_map_disable_distance	= Script::GetFloat( CRCD( 0x4e7dc608, "EnvMapDisableDistance")) * 12.0f;
		env_map_disable_distance		= env_map_disable_distance * env_map_disable_distance;

		for( ; i < current_index; ++i )
		{
			// Calculate the distance from the current camera to the instance.
			float dist_squared = Mth::DistanceSqr( p_instances[i]->GetTransform()->GetPos(), current_cam_pos );

			// At forty feet, environment mapping is turned off.
			EngineGlobals.allow_envmapping = ( dist_squared > env_map_disable_distance ) ? false : true;

			if(( flags & vRENDER_OPAQUE ) || (( flags & vRENDER_SEMITRANSPARENT ) && ( flags & vRENDER_INSTANCE_POST_WORLD_SEMITRANSPARENT )))
				p_instances[i]->Render( flags );
		}

		// Restore environment mapping..
		EngineGlobals.allow_envmapping = true;

		// Shut down fixed function lighting.
		D3DDevice_SetRenderState( D3DRS_LIGHTING,				FALSE );
		D3DDevice_SetRenderState( D3DRS_COLORVERTEX,			FALSE );
		D3DDevice_LightEnable( 0, FALSE );
		D3DDevice_LightEnable( 1, FALSE );
	}

	// Restore world transform to identity.
	D3DDevice_SetTransform( D3DTS_WORLD, &EngineGlobals.world_matrix );
	set_frustum_bbox_transform( NULL );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CInstance::CInstance( sScene *pScene, Mth::Matrix &transform, int numBones, Mth::Matrix *pBoneTransforms )
{
	SetTransform( transform );
	mp_bone_transforms	= pBoneTransforms;
	m_num_bones			= numBones;
	mp_scene			= pScene;
	mp_model			= NULL;
	m_active			= true;
	m_flags				= 0;

	mp_next_instance	= pFirstInstance;
	pFirstInstance		= this;

	// Check to see whether this instance is allowed to be lit or not (for non-skinned instances).
	// Currently, we assume that instances with a model containing valid normals requires lighting, except in the
	// situation where that model contains a mesh specifically flagged not to be lit.
	if(( pBoneTransforms == NULL ) || ( GetScene()->m_numHierarchyObjects > 0 ))
	{
		for( int m = 0; m < pScene->m_num_mesh_entries; ++m )
		{
			NxXbox::sMesh *p_mesh = pScene->m_meshes[m];
			if(( p_mesh->m_vertex_shader[0] & D3DFVF_NORMAL ) == 0 )
				return;
			if( p_mesh->m_flags & sMesh::MESH_FLAG_UNLIT )
				return;
		}
		m_flags |= CInstance::INSTANCE_FLAG_LIGHTING_ALLOWED;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CInstance::~CInstance()
{
	if( m_flags & INSTANCE_FLAG_DELETE_ATTACHED_SCENE )
	{
		if( mp_scene )
		{
			delete mp_scene;
			mp_scene = NULL;
		}
	}
	
	// Remove this instance from the list.
	CInstance **pp_instance = &pFirstInstance;
	while( *pp_instance )
	{
		if( *pp_instance == this )
		{
			*pp_instance = mp_next_instance;
			break;
		}
		else
		{
			pp_instance = &(( *pp_instance )->mp_next_instance );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CInstance::Render( uint32 flags )
{
	const int			MAX_SUPPORTED_BONES	= 58;
	static float		root_matrix[12];

	static D3DLIGHT8 l0 =
	{
		D3DLIGHT_DIRECTIONAL,
		{ 0.0f, 0.0f, 0.0f, 1.0f },	// Diffuse color
		{ 1.0f, 1.0f, 1.0f, 0.0f },	// Specular color
		{ 0.0f, 0.0f, 0.0f, 0.0f },	// Ambient color
		{ 0.0f, 0.0f, 0.0f },		// Position
		{ 0.0f, 0.0f, 0.0f },		// Direction
		0.0f, 0.0f,					// Range, falloff
		0.0f, 0.0f, 0.0f,			// Attenuation0, 1, 2
		0.0f, 0.0f					// Theta, Phi
	};
	static D3DLIGHT8 l1 =
	{
		D3DLIGHT_DIRECTIONAL,
		{ 0.0f, 0.0f, 0.0f, 1.0f },	// Diffuse color
		{ 0.0f, 0.0f, 0.0f, 0.0f },	// Specular color
		{ 0.0f, 0.0f, 0.0f, 0.0f },	// Ambient color
		{ 0.0f, 0.0f, 0.0f },		// Position
		{ 0.0f, 0.0f, 0.0f },		// Direction
		0.0f, 0.0f,					// Range, falloff
		0.0f, 0.0f, 0.0f,			// Attenuation0, 1, 2
		0.0f, 0.0f					// Theta, Phi
	};

	if(( GetBoneTransforms() == NULL ) || ( GetScene()->m_numHierarchyObjects > 0 ))
	{
		// Is a non-skinned object.
		pLastBoneTransforms = NULL;
		
		// Do the lighting setup here.
		if((( m_flags & CInstance::INSTANCE_FLAG_LIGHTING_ALLOWED ) == 0 ) || ( GetScene()->m_flags & SCENE_FLAG_RENDERING_SHADOW ))
		{
			D3DDevice_SetRenderState( D3DRS_LIGHTING, FALSE );
		}
		else
		{
			Nx::CModelLights *p_lights = GetModel() ? GetModel()->GetModelLights() : NULL;
			if( p_lights )
			{
				// Obtain position from the transform, for calculating Scene Lighting.
				p_lights->UpdateEngine( GetTransform()->GetPos(), true );
			}
			else
			{
				Nx::CLightManager::sUpdateEngine();
			}

			D3DDevice_SetRenderState( D3DRS_LIGHTING, TRUE );
			
			l0.Diffuse.r	= EngineGlobals.directional_light_color[4];
			l0.Diffuse.g	= EngineGlobals.directional_light_color[5];
			l0.Diffuse.b	= EngineGlobals.directional_light_color[6];
			l0.Direction.x	= EngineGlobals.directional_light_color[0];
			l0.Direction.y	= EngineGlobals.directional_light_color[1];
			l0.Direction.z	= EngineGlobals.directional_light_color[2];
			D3DDevice_SetLight( 0, &l0 );

			l1.Diffuse.r	= EngineGlobals.directional_light_color[12];
			l1.Diffuse.g	= EngineGlobals.directional_light_color[13];
			l1.Diffuse.b	= EngineGlobals.directional_light_color[14];
			l1.Direction.x	= EngineGlobals.directional_light_color[8];
			l1.Direction.y	= EngineGlobals.directional_light_color[9];
			l1.Direction.z	= EngineGlobals.directional_light_color[10];
			D3DDevice_SetLight( 1, &l1 );

			D3DDevice_SetRenderState( D3DRS_AMBIENT, D3DCOLOR_RGBA(	Ftoi_ASM( EngineGlobals.ambient_light_color[0] * 255.0f ),
																	Ftoi_ASM( EngineGlobals.ambient_light_color[1] * 255.0f ),
																	Ftoi_ASM( EngineGlobals.ambient_light_color[2] * 255.0f ),
																	0xFF ));
		}

		// If the object has a 'fake skeleton', like the cars with rotating wheels, then set up accordingly.
		if( GetScene()->m_numHierarchyObjects )
		{
			int num_bones = ( GetNumBones() < MAX_SUPPORTED_BONES ) ? GetNumBones() : MAX_SUPPORTED_BONES;
			for( int lp = 0; lp < num_bones; ++lp )
			{
				Mth::Matrix temp = *GetTransform();
				temp = GetBoneTransforms()[lp] * temp;
				set_frustum_bbox_transform( &temp );
				D3DDevice_SetTransform( D3DTS_WORLD, (D3DXMATRIX*)&temp );

				// Scan through the meshes, setting only those with the current bone active.
				for( int m = 0; m < GetScene()->m_num_mesh_entries; ++m )
				{
					NxXbox::sMesh *p_mesh = GetScene()->m_meshes[m];
					if( p_mesh->GetBoneIndex() == lp )
						p_mesh->SetActive( true );
					else
						p_mesh->SetActive( false );
				}
				render_scene( GetScene(), flags | vRENDER_NO_CULLING );
			}
		}
		else
		{
			// Has no skeleton.
			set_frustum_bbox_transform( GetTransform());
			D3DDevice_SetTransform( D3DTS_WORLD, (D3DXMATRIX*)GetTransform());

			render_scene( GetScene(), flags | vRENDER_NO_CULLING );
		}
	}
	else
	{
		// Has bone transforms, is therefore an animated object such as a skater, pedestrian etc.
		set_frustum_bbox_transform( GetTransform());

		// We only want to upload all the bone transforms if they have changed from the last call.
		bool upload_bone_transforms = false;
		
		if( pLastBoneTransforms != GetBoneTransforms())
		{
			// Okay, the bone transforms have changed from the last call.
			upload_bone_transforms = true;
			
			// Do the lighting setup here (if not rendering shadow, which requires no lighting).
			if(( GetScene()->m_flags & SCENE_FLAG_RENDERING_SHADOW ) == 0 )
			{
				Nx::CModelLights *p_lights = GetModel() ? GetModel()->GetModelLights() : NULL;
				if( p_lights )
				{
					p_lights->UpdateEngine( GetTransform()->GetPos(), true );
				}
				else
				{
					Nx::CLightManager::sUpdateEngine();
				}
			}
			
			pLastBoneTransforms = GetBoneTransforms();

			int num_bones = ( GetNumBones() < MAX_SUPPORTED_BONES ) ? GetNumBones() : MAX_SUPPORTED_BONES;
		}
				
		root_matrix[0]	= GetTransform()->GetRight()[X];
		root_matrix[1]	= GetTransform()->GetUp()[X];
		root_matrix[2]	= GetTransform()->GetAt()[X];
		root_matrix[3]	= GetTransform()->GetPos()[X];
		root_matrix[4]	= GetTransform()->GetRight()[Y];
		root_matrix[5]	= GetTransform()->GetUp()[Y];
		root_matrix[6]	= GetTransform()->GetAt()[Y];
		root_matrix[7]	= GetTransform()->GetPos()[Y];
		root_matrix[8]	= GetTransform()->GetRight()[Z];
		root_matrix[9]	= GetTransform()->GetUp()[Z];
		root_matrix[10]	= GetTransform()->GetAt()[Z];
		root_matrix[11]	= GetTransform()->GetPos()[Z];
		
		setup_weighted_mesh_vertex_shader( &root_matrix, &GetBoneTransforms()[0][Mth::RIGHT][X], upload_bone_transforms ? GetNumBones() : 0 );

		if( GetScene()->m_flags & SCENE_FLAG_RENDERING_SHADOW )
		{
			// Set the simple vertex shader that does no normal transform or lighting.
			set_vertex_shader( WeightedMeshVertexShader_SBWrite );
			EngineGlobals.vertex_shader_override = 1;

			// Set the simple pixel shader that just writes constant (1,1,1,1) out.
			set_pixel_shader( PixelShaderNULL );
			EngineGlobals.pixel_shader_override = 1;

			// No backface culling.
			set_render_state( RS_CULLMODE, D3DCULL_NONE );

			// Lock out material changes.
			EngineGlobals.material_override = 1;

			render_scene( GetScene(), flags | vRENDER_NO_CULLING );

//			RenderShadowVolume();
		}
		else
		{
			render_scene( GetScene(), flags | vRENDER_NO_CULLING );

			// Render the self-shadowing pass here, if so flagged.
			if( GetScene()->m_flags & SCENE_FLAG_SELF_SHADOWS )
			{
				static float texture_projection_matrix[16];

				// Calculate distance to the camera - the self shadowing fades out beyond a certain distance, (and also fades out
				// close up, to avoid the visible streaks). Also need to take into account the view angle since that directly affects
				// the relative screen space size of the object.
				const float SHADOW_FADE_CLOSE_START			= 150.0f;
				const float SHADOW_FADE_CLOSE_COMPLETE		= 120.0f;
				const float SHADOW_FADE_FAR_START			= 500.0f;
				const float SHADOW_FADE_FAR_COMPLETE		= 600.0f;
				const float DEFAULT_SCREEN_ANGLE			= 0.72654f;	// tan( 72 / 2 )

				Mth::Vector pos_to_cam	= GetTransform()->GetPos() - Mth::Vector( NxXbox::EngineGlobals.cam_position.x, NxXbox::EngineGlobals.cam_position.y, NxXbox::EngineGlobals.cam_position.z );
				float		dist		= pos_to_cam.Length() * ( tanf( Mth::DegToRad( EngineGlobals.screen_angle * 0.5f )) / DEFAULT_SCREEN_ANGLE );

				if(( dist > SHADOW_FADE_CLOSE_COMPLETE ) && ( dist < SHADOW_FADE_FAR_COMPLETE ))
				{
					// Find which set of details relates to this instance.
					pTextureProjectionDetailsTable->IterateStart();
					sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->IterateNext();
					while( p_details )
					{
						if( p_details->p_model == mp_model )
							break;

						p_details = pTextureProjectionDetailsTable->IterateNext();
					}

					if( p_details )
					{
						// Need to incorporate the world matrix into the texture projection matrix, since we will be using the bone-transformed
						// vertex positions, which are in object space.
						XGMATRIX world_matrix;
						world_matrix.m[0][0] = GetTransform()->GetRight()[X];
						world_matrix.m[0][1] = GetTransform()->GetRight()[Y];
						world_matrix.m[0][2] = GetTransform()->GetRight()[Z];
						world_matrix.m[0][3] = 0.0f;
						world_matrix.m[1][0] = GetTransform()->GetUp()[X];
						world_matrix.m[1][1] = GetTransform()->GetUp()[Y];
						world_matrix.m[1][2] = GetTransform()->GetUp()[Z];
						world_matrix.m[1][3] = 0.0f;
						world_matrix.m[2][0] = GetTransform()->GetAt()[X];
						world_matrix.m[2][1] = GetTransform()->GetAt()[Y];
						world_matrix.m[2][2] = GetTransform()->GetAt()[Z];
						world_matrix.m[2][3] = 0.0f;
						world_matrix.m[3][0] = GetTransform()->GetPos()[X];
						world_matrix.m[3][1] = GetTransform()->GetPos()[Y];
						world_matrix.m[3][2] = GetTransform()->GetPos()[Z];
						world_matrix.m[3][3] = 1.0f;
						calculate_tex_proj_matrix( &p_details->view_matrix, &p_details->projection_matrix, &p_details->texture_projection_matrix, &world_matrix );

						// Upload that matrix to the GPU also.
						texture_projection_matrix[0]	= p_details->texture_projection_matrix.m[0][0];
						texture_projection_matrix[1]	= p_details->texture_projection_matrix.m[1][0];
						texture_projection_matrix[2]	= p_details->texture_projection_matrix.m[2][0];
						texture_projection_matrix[3]	= p_details->texture_projection_matrix.m[3][0];
						texture_projection_matrix[4]	= p_details->texture_projection_matrix.m[0][1];
						texture_projection_matrix[5]	= p_details->texture_projection_matrix.m[1][1];
						texture_projection_matrix[6]	= p_details->texture_projection_matrix.m[2][1];
						texture_projection_matrix[7]	= p_details->texture_projection_matrix.m[3][1];
						texture_projection_matrix[8]	= p_details->texture_projection_matrix.m[0][2];
						texture_projection_matrix[9]	= p_details->texture_projection_matrix.m[1][2];
						texture_projection_matrix[10]	= p_details->texture_projection_matrix.m[2][2];
						texture_projection_matrix[11]	= p_details->texture_projection_matrix.m[3][2];
						texture_projection_matrix[12]	= p_details->texture_projection_matrix.m[0][3];
						texture_projection_matrix[13]	= p_details->texture_projection_matrix.m[1][3];
						texture_projection_matrix[14]	= p_details->texture_projection_matrix.m[2][3];
						texture_projection_matrix[15]	= p_details->texture_projection_matrix.m[3][3];

						// We can upload this matrix to the space taken up by the directional lighting details, since this render pass requires no lighting.
						D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_DIR_LIGHT_OFFSET, (void*)texture_projection_matrix, 4 );

						// Scan through each mesh in this scene, setting the vertex shader to be the equivalent vertex shader
						// for shadow buffering.
						sScene *p_scene = GetScene();
						for( int m = 0; m < p_scene->m_num_mesh_entries; ++m )
						{
							sMesh *p_mesh = p_scene->m_meshes[m];
							if(( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_1Weight ) ||
							   ( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_Specular_1Weight ) ||
							   ( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_1Weight_UVTransform ))
							{
								p_mesh->PushVertexShader( WeightedMeshVS_VXC_1Weight_SBPassThru );
							}
							else if(( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_2Weight ) ||
									( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_Specular_2Weight ) ||
								    ( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_2Weight_UVTransform ))
							{
								p_mesh->PushVertexShader( WeightedMeshVS_VXC_2Weight_SBPassThru );
							}
							else
							{
								p_mesh->PushVertexShader( WeightedMeshVS_VXC_3Weight_SBPassThru );
							}
						}

						float max = 0.25f;
						if( dist < SHADOW_FADE_CLOSE_START )
						{
							max = (( dist - SHADOW_FADE_CLOSE_COMPLETE ) / ( SHADOW_FADE_CLOSE_START - SHADOW_FADE_CLOSE_COMPLETE )) * max;
						}
						else if( dist > SHADOW_FADE_FAR_START )
						{
							max = (( SHADOW_FADE_FAR_COMPLETE - dist ) / ( SHADOW_FADE_FAR_COMPLETE - SHADOW_FADE_FAR_START )) * max;
						}

						// Set ambient color multipliers in pixel shader C0 and C1.
						NxXbox::EngineGlobals.pixel_shader_constants[0]		= 1.0f - max;		// Always present
						NxXbox::EngineGlobals.pixel_shader_constants[1]		= 1.0f - max;
						NxXbox::EngineGlobals.pixel_shader_constants[2]		= 1.0f - max;
						NxXbox::EngineGlobals.pixel_shader_constants[3]		= 1.0f;
						NxXbox::EngineGlobals.pixel_shader_constants[4]		= max;		// The part the shadow affects.
						NxXbox::EngineGlobals.pixel_shader_constants[5]		= max;
						NxXbox::EngineGlobals.pixel_shader_constants[6]		= max;
						NxXbox::EngineGlobals.pixel_shader_constants[7]		= 1.0f;
						NxXbox::EngineGlobals.upload_pixel_shader_constants	= true;

						// Set the pixel shader that will do the shadow attenuation.			
						extern DWORD PixelShader_ShadowBuffer;
						set_pixel_shader( PixelShader_ShadowBuffer );
						EngineGlobals.pixel_shader_override = 1;

						// Set the material properties used for shadow modulation.
						set_blend_mode( vBLEND_MODE_MODULATE_COLOR );
						set_texture( 0, NULL );
						set_texture( 1, NULL );
						set_texture( 2, NULL );
						EngineGlobals.material_override = 1;

						// Set texture stage 3 to use the shadow buffer.
						EngineGlobals.texture_stage_override |= ( 1 << 3 );
						set_texture( 3, NULL );
						set_texture( 3, p_details->p_texture->pD3DTexture );
						set_render_state( RS_UVADDRESSMODE0 + 3, 0x00020002UL );
						D3DDevice_SetTextureStageState( 3, D3DTSS_BORDERCOLOR, 0x00000000UL );
						D3DDevice_SetTextureStageState( 3, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
						D3DDevice_SetTextureStageState( 3, D3DTSS_MINFILTER, D3DTEXF_LINEAR );

						D3DDevice_SetTextureStageState( 3, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
						D3DDevice_SetTextureStageState( 3, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU | 3 );

						// Set shadowbuffer state.
						D3DDevice_SetRenderState( D3DRS_SHADOWFUNC, D3DCMP_GREATER );

						// Render the shadow pass with fogging disabled.
						if( EngineGlobals.fog_enabled )
						{
							D3DDevice_SetRenderState( D3DRS_FOGENABLE, FALSE );
							render_scene( GetScene(), flags | vRENDER_NO_CULLING );
							D3DDevice_SetRenderState( D3DRS_FOGENABLE, TRUE );
						}
						else
						{
							render_scene( GetScene(), flags | vRENDER_NO_CULLING );
						}

						// Restore shadowbuffer state.
						D3DDevice_SetRenderState( D3DRS_SHADOWFUNC, D3DCMP_NEVER );

						// Scan through each mesh in this scene, restoring the vertex shader to the original.
						for( int m = 0; m < p_scene->m_num_mesh_entries; ++m )
						{
							sMesh *p_mesh = p_scene->m_meshes[m];
							p_mesh->PopVertexShader();
						}
					}
				}
			}
		}

		// Restore state.
		EngineGlobals.vertex_shader_override	= 0;
		EngineGlobals.pixel_shader_override		= 0;
		EngineGlobals.material_override			= 0;
		EngineGlobals.texture_stage_override	&= ~( 1 << 3 );
		set_texture( 3, NULL );

//		shutdown_weighted_mesh_vertex_shader();
	}
}



// Test code...
void CInstance::RenderShadowVolume( void )
{
	if( GetBoneTransforms())
	{
		XGMATRIX root_matrix = *((XGMATRIX*)GetTransform());

		sScene*	p_scene = GetScene();

		// Process each mesh in turn.
		for( int m = 0; m < p_scene->m_num_mesh_entries; ++m )
		{
			sMesh* p_mesh	= p_scene->m_meshes[m];

			// Lock the vertex buffer so we can read the data.
			BYTE* p_byte;
			p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_byte, D3DLOCK_READONLY );

			// Grab a buffer for the transformed points.
			XGVECTOR4*	p_t_buffer	= new XGVECTOR4[p_mesh->m_num_vertices];

			for( int v = 0; v < p_mesh->m_num_vertices; ++v )
			{
				// Get untransformed point.
				XGVECTOR3*	p_source_vertex	= (XGVECTOR3*)( p_byte + ( v * p_mesh->m_vertex_stride ));

				// Get and unpack weights.
				uint32		packed_weights	= *((uint32*)( p_byte + ( v * p_mesh->m_vertex_stride ) + 12 ));
				float		w0				= (( packed_weights >> 0 ) & 0x7ff ) * ( 1.0f / 1023.0f );
				float		w1				= (( packed_weights >> 11 ) & 0x7ff ) * ( 1.0f / 1023.0f );
				float		w2				= (( packed_weights >> 22 ) & 0x3ff ) * ( 1.0f / 511.0f );

				// Get and unpack matrix indices.
				uint32		packed_indices	= *((uint32*)( p_byte + ( v * p_mesh->m_vertex_stride ) + 16 ));
				uint32		i0				= ( packed_indices >> 0 ) & 0xff;
				uint32		i1				= ( packed_indices >> 8 ) & 0xff;
				uint32		i2				= ( packed_indices >> 16 ) & 0xff;

				// Get bone matrix pointers.
				XGMATRIX*	p_bone_mat0		= (XGMATRIX*)( GetBoneTransforms() + i0 );
				XGMATRIX*	p_bone_mat1		= (XGMATRIX*)( GetBoneTransforms() + i1 );
				XGMATRIX*	p_bone_mat2		= (XGMATRIX*)( GetBoneTransforms() + i2 );

				XGVECTOR4	tp0;
				XGVECTOR4	tp1;
				XGVECTOR4	tp2;

				// Transform point by bone matrix 0, 1 and 2 and scale with weight 0, 1, 2.
				XGVec3Transform( &tp0, p_source_vertex, p_bone_mat0 );
				XGVec3Scale((XGVECTOR3*)&tp0, (XGVECTOR3*)&tp0, w0 );

				XGVec3Transform( &tp1, p_source_vertex, p_bone_mat1 );
				XGVec3Scale((XGVECTOR3*)&tp1, (XGVECTOR3*)&tp1, w1 );

				XGVec3Transform( &tp2, p_source_vertex, p_bone_mat2 );
				XGVec3Scale((XGVECTOR3*)&tp2, (XGVECTOR3*)&tp2, w2 );

				// Obtain cumulative result.
				tp0 = tp0 + tp1 + tp2;

				// Tranform point by object transform.
				XGVec3Transform( p_t_buffer + v, (XGVECTOR3*)&tp0, &root_matrix );
			}
			delete [] p_t_buffer;
		}
	}
}






} // namespace NxXbox



================================================
FILE: Code/Gfx/XBox/NX/instance.h
================================================
#ifndef __INSTANCE_H
#define __INSTANCE_H


#include 
#include 
#include "scene.h"

namespace NxXbox
{

void	render_instance( CInstance *p_instance, uint32 flags );
void	render_instances( uint32 flags );

class CInstance
{
public:

	enum EInstanceFlag
	{
		INSTANCE_FLAG_DELETE_ATTACHED_SCENE	= 0x01,
		INSTANCE_FLAG_LIGHTING_ALLOWED		= 0x02
	};
	
	void			SetTransform( Mth::Matrix &transform )	{ m_transform = transform; }
	Mth::Matrix*	GetTransform(void)						{ return &m_transform; }
	int				GetNumBones( void )						{ return m_num_bones; }
	Mth::Matrix*	GetBoneTransforms( void )				{ return mp_bone_transforms; }
	void			SetBoneTransforms( Mth::Matrix* p_t )	{ mp_bone_transforms = p_t; }
	sScene*			GetScene( void )						{ return mp_scene; }
	void			SetActive( bool active )				{ m_active = active; }
	bool			GetActive( void )						{ return m_active; }
	void			SetFlag( EInstanceFlag flag )			{ m_flags |= flag; }
	void			ClearFlag( EInstanceFlag flag )			{ m_flags &= ~flag; }
	void			SetModel( Nx::CModel *p_model )			{ mp_model = p_model; }
	Nx::CModel*		GetModel( void )						{ return mp_model; }
	CInstance*		GetNextInstance( void )				{ return mp_next_instance; }

					CInstance( sScene *pScene, Mth::Matrix &transform, int numBones, Mth::Matrix *pBoneTransforms );
					~CInstance();

	void			Render( uint32 flags );
	void			RenderShadowVolume( void );

private:
	uint32			m_flags;
	Mth::Matrix		m_transform;
	Mth::Matrix*	mp_bone_transforms;
	int				m_num_bones;
	bool			m_active;
	Nx::CModel*		mp_model;		// Required in order to get pointer to CXboxLights structure at render time.
	sScene*			mp_scene;
	CInstance*		mp_next_instance;
};



} // namespace NxXbox


#endif // __INSTANCE_H



================================================
FILE: Code/Gfx/XBox/NX/material.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "gfx/xbox/p_nxtexture.h"
#include "anim_vertdefs.h"
#include "nx_init.h"
#include "material.h"
#include "scene.h"
#include "render.h"

namespace NxXbox
{


uint32						NumMaterials;

static const float pi_over_180 = (float)Mth::PI / 180.0f;



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sUVWibbleParams::sUVWibbleParams( void )
{
	// Zero out the members.
	ZeroMemory( this, sizeof( sUVWibbleParams ));

	// Set the matrix correctly.
	m_UVMatrix[0] = 1.0f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sUVWibbleParams::~sUVWibbleParams( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMaterial::sMaterial( void )
{
	m_num_wibble_vc_anims		= 0;
	mp_wibble_vc_params			= NULL;
	mp_wibble_vc_colors			= NULL;
	mp_wibble_texture_params	= NULL;
	m_uv_wibble					= false;
	m_texture_wibble			= false;
	m_grass_layers				= 0;
	m_zbias						= 0;
	for( int p = 0; p < MAX_PASSES; ++p )
	{
		m_flags[p]				= 0;
		mp_UVWibbleParams[p]	= NULL;

		m_envmap_tiling[p][0]	= 3.0f;
		m_envmap_tiling[p][1]	= 3.0f;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMaterial::~sMaterial( void )
{
	if( mp_wibble_vc_params	)
	{
		for( uint32 i = 0; i < m_num_wibble_vc_anims; ++i )
		{
			delete [] mp_wibble_vc_params[i].mp_keyframes;
		}
		delete [] mp_wibble_vc_params;
	}
	if( mp_wibble_vc_colors	)
	{
		delete [] mp_wibble_vc_colors;
	}

	for( int p = 0; p < MAX_PASSES; ++p )
	{
		if( mp_UVWibbleParams[p] )
			delete mp_UVWibbleParams[p];
	}

	if( mp_wibble_texture_params )
	{
		for( uint32 p = 0; p < MAX_PASSES; ++p )
		{
			delete [] mp_wibble_texture_params->mp_keyframes[p];
		}
		delete mp_wibble_texture_params;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMaterial::figure_wibble_uv( void )
{
	if( m_uv_wibble )
	{
		// Get the time.
		float t = (float)Tmr::GetTime() * 0.001f;

		for( uint32 p = 0; p < m_passes; ++p )
		{
			// Figure out UV offsets for wibbling if required.
			if( m_flags[p] & MATFLAG_UV_WIBBLE )
			{
				if( !( m_flags[p] & MATFLAG_EXPLICIT_UV_WIBBLE ))
				{
					float uoff, voff;
			
					uoff	= ( t * mp_UVWibbleParams[p]->m_UVel ) + ( mp_UVWibbleParams[p]->m_UAmplitude * sinf( mp_UVWibbleParams[p]->m_UFrequency * t + mp_UVWibbleParams[p]->m_UPhase ));
					voff	= ( t * mp_UVWibbleParams[p]->m_VVel ) + ( mp_UVWibbleParams[p]->m_VAmplitude * sinf( mp_UVWibbleParams[p]->m_VFrequency * t + mp_UVWibbleParams[p]->m_VPhase ));

					// Reduce offset mod 16 and put it in the range -8 to +8.
					uoff	+= 8.0f;
					uoff	-= (float)(( Ftoi_ASM( uoff ) >> 4 ) << 4 );
					voff	+= 8.0f;
					voff	-= (float)(( Ftoi_ASM( voff ) >> 4 ) << 4 );

					mp_UVWibbleParams[p]->m_UVMatrix[2]	= ( uoff < 0.0f ) ? ( uoff + 8.0f ) : ( uoff - 8.0f );
					mp_UVWibbleParams[p]->m_UVMatrix[3]	= ( voff < 0.0f ) ? ( voff + 8.0f ) : ( voff - 8.0f );
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMaterial::figure_wibble_vc( void )
{
	// The vertex color wibble flag is placed in pass 0.
	if( m_flags[0] & MATFLAG_VC_WIBBLE )
	{
		for( uint32 i = 0; i < m_num_wibble_vc_anims; ++i )
		{
			struct sVCWibbleParams	*p_sequence		= mp_wibble_vc_params + i;
			
			// Get phase-shift.
			int						phase_shift		= p_sequence->m_phase;

			// Time parameters.
			int						num_keys		= p_sequence->m_num_keyframes;
			int						start_time		= p_sequence->mp_keyframes[0].m_time;
			int						end_time		= p_sequence->mp_keyframes[num_keys - 1].m_time;
			int						period			= end_time - start_time;
			int						time			= start_time + ( NxXbox::EngineGlobals.render_start_time + phase_shift ) % period;

			// Locate the keyframe.
			int key;
			for( key = num_keys - 1; key >= 0; --key )
			{
				if( time >= p_sequence->mp_keyframes[key].m_time )
				{
					break;
				}
			}
			
			Dbg_Assert( key < ( num_keys - 1 ));

			// Parameter expressing how far we are between between this keyframe and the next.
			float					t				= (float)( time - p_sequence->mp_keyframes[key].m_time ) * ReciprocalEstimateNR_ASM((float)( p_sequence->mp_keyframes[key + 1].m_time - p_sequence->mp_keyframes[key].m_time ));

			// Interpolate the color.
			uint32 red = (uint32)Ftoi_ASM((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.r ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.r ));
			uint32 grn = (uint32)Ftoi_ASM((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.g ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.g ));
			uint32 blu = (uint32)Ftoi_ASM((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.b ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.b ));
			uint32 alp = (uint32)Ftoi_ASM((( 1.0f - t ) * p_sequence->mp_keyframes[key].m_color.a ) + ( t * p_sequence->mp_keyframes[key + 1].m_color.a ));

			// Switch red and blue, and store.
			mp_wibble_vc_colors[i] = ( alp << 24 ) | ( red << 16 ) | ( grn << 8 ) | blu;
		}
	}
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMaterial::figure_wibble_texture( void )
{
	// The vertex color wibble flag is placed in pass 0.
	if( m_texture_wibble )
	{
		int current_time = (int)Tmr::GetTime();

		struct sTextureWibbleParams	*p_sequence		= mp_wibble_texture_params;
			
		for( int pass = 0; pass < MAX_PASSES; ++pass )
		{
			if( m_flags[pass] & MATFLAG_PASS_TEXTURE_ANIMATES )
			{
				// Get phase-shift.
				int						phase_shift	= p_sequence->m_phase[pass];

				// Time parameters.
				int						num_keys	= p_sequence->m_num_keyframes[pass];
				int						start_time	= p_sequence->mp_keyframes[pass][0].m_time;
				int						end_time	= p_sequence->mp_keyframes[pass][num_keys - 1].m_time;
				int						period		= end_time - start_time;
				int						time		= start_time + (( current_time + phase_shift ) % period );

				// Keep track of the iterations, if iterations on this animation are required.
				if( p_sequence->m_num_iterations[pass] > 0 )
				{
					int iteration = ( current_time - start_time ) / period;
					if( iteration >= p_sequence->m_num_iterations[pass] )
					{
						// Set the time such that the animation no longer continues.
						time = start_time + ((( period * p_sequence->m_num_iterations[pass] ) + phase_shift ) % period );
					}
				}

				// Locate the keyframe.
				int key;
				for( key = num_keys - 1; key >= 0; --key )
				{
					if( time >= p_sequence->mp_keyframes[pass][key].m_time )
					{
						break;
					}
				}
			
				// Set the texture.
//				Dbg_Assert( p_sequence->mp_keyframes[pass][key].mp_texture );
				mp_tex[pass] = p_sequence->mp_keyframes[pass][key].mp_texture;
			}
		}
	}
}



inline DWORD F2DW( FLOAT f )
{
	return *( ( DWORD *) & f );
}

					
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMaterial::Submit( void )
{
	// Dummy 8 element uv 'matrix' for custom pipeline.
	static float custom_uv_mat[8] = {	1.0f, 0.0f, 0.0f, 0.0f,
										0.0f, 1.0f, 0.0f, 0.0f };

	// Matrix for UV wibbling texture transform.
	static D3DMATRIX uv_mat = { 1.0f, 0.0f, 0.0f, 0.0f,
								0.0f, 1.0f, 0.0f, 0.0f,
								0.0f, 0.0f, 0.0f, 0.0f,
								0.0f, 0.0f, 0.0f, 0.0f };

	// Matrix for env mapping texture transform. Note that the [0][0] and [1][1] elements of the matrix are set up to scale, based on the
	// material pass properties.
	static D3DMATRIX env_mat = { 0.5f,  0.0f, 0.0f, 0.0f,
								 0.0f, -0.5f, 0.0f, 0.0f,
								 0.0f,  0.0f, 0.0f,	0.0f,
								 0.5f,  0.5f, 0.0f, 0.0f };
	
	// Check for material change lockout.
	if( EngineGlobals.material_override )
	{
		return;
	}

	// Set the alpha blend mode.
	set_blend_mode( m_reg_alpha[0] );
	
	// Set the alpha cutoff value.
	set_render_state( RS_ALPHACUTOFF, (uint32)m_alpha_cutoff );
	
	// Set the backface cull mode.
	set_render_state( RS_CULLMODE, m_no_bfc ? D3DCULL_NONE : D3DCULL_CW );

	// Set the z-bias.
	set_render_state( RS_ZBIAS, m_zbias );
	
	// Figure uv, vc and texture wibble updates if required.
	figure_wibble_uv();
	figure_wibble_vc();
	figure_wibble_texture();
	
	// Set specular properties of this material.
	if( m_flags[0] & MATFLAG_SPECULAR )
	{
		if( EngineGlobals.specular_enabled == 0 )
		{
			set_render_state( RS_SPECULARENABLE, 1 );

			// Set the specular material.
			D3DMATERIAL8 test_mat;
			ZeroMemory( &test_mat, sizeof( D3DMATERIAL8 ));
			test_mat.Specular.r	= m_specular_color[0];
			test_mat.Specular.g	= m_specular_color[1];
			test_mat.Specular.b	= m_specular_color[2];
			test_mat.Specular.a	= 1.0f;
			test_mat.Power		= m_specular_color[3];
			D3DDevice_SetMaterial( &test_mat );

			// If using a custom vertex shader, also need to load the specular color and power to vert shader registers here.
			if( NxXbox::EngineGlobals.custom_pipeline_enabled )
			{
				D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_SPECULAR_COLOR_OFFSET, (void*)&m_specular_color[0], 1 );
			}
		}
	}
	else
	{
		if( EngineGlobals.specular_enabled > 0 )
		{
			set_render_state( RS_SPECULARENABLE, 0 );

			// Set the specular material (basically just to set the power to zero).
			D3DMATERIAL8 test_mat;
			ZeroMemory( &test_mat, sizeof( D3DMATERIAL8 ));
			D3DDevice_SetMaterial( &test_mat );
		}
	}

	// Set up the textures if present. This involves setting the texture and palette pointers and addressing mode.
	// Also, for multipass textures, it may require loading of fixed alpha values into specific constant registers.
	uint32 p;
	for( p = 0; p < m_passes; ++p )
	{
		if( !( EngineGlobals.texture_stage_override & ( 1 << p )))
		{
			// Load pass color and fixed alpha values to the pixel shader, if required.
			if( !EngineGlobals.upload_pixel_shader_constants )
			{
				// Do the comparison as 32bit unsigned ints.
				uint32 *p_test0 = (uint32*)&m_color[p][0];
				uint32 *p_test1 = (uint32*)&EngineGlobals.pixel_shader_constants[p * 4];

				if(( p_test0[0] != p_test1[0] ) || ( p_test0[1] != p_test1[1] ) || ( p_test0[2] != p_test1[2] ) || ( p_test0[3] != p_test1[3] ))
				{
					EngineGlobals.upload_pixel_shader_constants = true;
				}
			}

			if( mp_tex[p] )
			{
				set_texture( p, mp_tex[p]->pD3DTexture, mp_tex[p]->pD3DPalette );

				// Set UV adressing mode.
				set_render_state( RS_UVADDRESSMODE0 + p,	m_uv_addressing[p] );

				// Set filtering mode.
//				set_render_state( RS_MINMAGFILTER0 + p,		m_filtering_mode[p] );
						
				// Set MIP lod bias.
				set_render_state( RS_MIPLODBIASPASS0 + p,	*((uint32*)( &m_k[p] )));

				// Deal with bump mapping setup.
				if( m_flags[p] & MATFLAG_BUMP_SIGNED_TEXTURE )
				{
					// Channel of bump texture value (v,u) MUST be signed.
					if( EngineGlobals.color_sign[p] != ( D3DTSIGN_RSIGNED | D3DTSIGN_GSIGNED | D3DTSIGN_BSIGNED ))
					{
						EngineGlobals.color_sign[p] = ( D3DTSIGN_RSIGNED | D3DTSIGN_GSIGNED | D3DTSIGN_BSIGNED );
						D3DDevice_SetTextureStageState( p, D3DTSS_COLORSIGN, D3DTSIGN_RSIGNED | D3DTSIGN_GSIGNED | D3DTSIGN_BSIGNED );
					}
				}
				else
				{
					if( EngineGlobals.color_sign[p] != ( D3DTSIGN_RUNSIGNED | D3DTSIGN_GUNSIGNED | D3DTSIGN_BUNSIGNED ))
					{
						EngineGlobals.color_sign[p] = ( D3DTSIGN_RUNSIGNED | D3DTSIGN_GUNSIGNED | D3DTSIGN_BUNSIGNED );
						D3DDevice_SetTextureStageState( p, D3DTSS_COLORSIGN, D3DTSIGN_RUNSIGNED | D3DTSIGN_GUNSIGNED | D3DTSIGN_BUNSIGNED );
					}
				}

				if( m_flags[p] & MATFLAG_BUMP_LOAD_MATRIX )
				{
					D3DDevice_SetTextureStageState( p, D3DTSS_BUMPENVMAT00, F2DW( EngineGlobals.bump_env_matrix._11 ));
					D3DDevice_SetTextureStageState( p, D3DTSS_BUMPENVMAT01, F2DW( EngineGlobals.bump_env_matrix._13 ));
					D3DDevice_SetTextureStageState( p, D3DTSS_BUMPENVMAT10, F2DW( EngineGlobals.bump_env_matrix._31 ));
					D3DDevice_SetTextureStageState( p, D3DTSS_BUMPENVMAT11, F2DW( EngineGlobals.bump_env_matrix._33 ));
				}

				if(( m_flags[p] & MATFLAG_ENVIRONMENT ) && ( EngineGlobals.allow_envmapping ))
				{
					// Handle environment mapping.
					env_mat.m[0][0] = 0.5f * m_envmap_tiling[p][0];
					env_mat.m[1][1] = 0.5f * m_envmap_tiling[p][1];

					D3DDevice_SetTransform((D3DTRANSFORMSTATETYPE)( D3DTS_TEXTURE0 + p ), &env_mat );
					D3DDevice_SetTextureStageState( p, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );
					D3DDevice_SetTextureStageState( p, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR | p );
				}
				else if( m_flags[p] & MATFLAG_UV_WIBBLE )
				{
					D3DDevice_SetTextureStageState( p, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );
					D3DDevice_SetTextureStageState( p, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU | p );

					if( NxXbox::EngineGlobals.custom_pipeline_enabled )
					{
						// If using a custom vertex shader, need to load the uv matrix here.
						// If using a custom vertex shader, need to load the custom uv matrix here.
						custom_uv_mat[0] =  mp_UVWibbleParams[p]->m_UVMatrix[0];
						custom_uv_mat[1] = -mp_UVWibbleParams[p]->m_UVMatrix[1];
						custom_uv_mat[3] =  mp_UVWibbleParams[p]->m_UVMatrix[2];

						custom_uv_mat[4] =  mp_UVWibbleParams[p]->m_UVMatrix[1];
						custom_uv_mat[5] =	mp_UVWibbleParams[p]->m_UVMatrix[0];
						custom_uv_mat[7] =  mp_UVWibbleParams[p]->m_UVMatrix[3];

						D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_UV_MAT_OFFSET + ( p * 2 ), (void*)&custom_uv_mat[0], 2 );
					}
					else
					{
						// Handle fixed function UV matrix.
						uv_mat._31 = mp_UVWibbleParams[p]->m_UVMatrix[2];
						uv_mat._32 = mp_UVWibbleParams[p]->m_UVMatrix[3];
						
						D3DDevice_SetTransform((D3DTRANSFORMSTATETYPE)( D3DTS_TEXTURE0 + p ), &uv_mat );
					}
				}
				else
				{
					if( NxXbox::EngineGlobals.custom_pipeline_enabled )
					{
						// If using a custom vertex shader, need to load the custom uv matrix here.
						custom_uv_mat[0] = 1.0f; custom_uv_mat[1] = 0.0f; custom_uv_mat[3] = 0.0f;
						custom_uv_mat[4] = 0.0f; custom_uv_mat[5] = 1.0f; custom_uv_mat[7] = 0.0f;
						D3DDevice_SetVertexShaderConstantFast( VSCONST_REG_UV_MAT_OFFSET + ( p * 2 ), (void*)&custom_uv_mat[0], 2 );
					}

					// Regular case. Check states here since it is quicker for this common case than setting blindly.
					uint32 tex_coord_index, tex_trans_flags;

					D3DDevice_GetTextureStageState( p, D3DTSS_TEXTURETRANSFORMFLAGS, &tex_trans_flags );
					D3DDevice_GetTextureStageState( p, D3DTSS_TEXCOORDINDEX, &tex_coord_index );

					if( tex_trans_flags != D3DTTFF_DISABLE )
					{
						D3DDevice_SetTextureStageState( p, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
					}
					if( tex_coord_index != ( D3DTSS_TCI_PASSTHRU | p ))
					{
						D3DDevice_SetTextureStageState( p, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU | p );
					}
				}
			}
			else
			{
				set_texture( p, NULL, NULL );
			}
		}
	}

	if( EngineGlobals.upload_pixel_shader_constants )
	{
		CopyMemory( EngineGlobals.pixel_shader_constants, &m_color[0][0], sizeof( float ) * 4 * m_passes );
	}

	// Make sure to set the textures for unused stages to NULL, to reduce texture overhead.
//	for( ; p < 4; ++p )
//	{
//		if( !( EngineGlobals.texture_stage_override & ( 1 << p )))
//		{
//			set_texture( p, NULL, NULL );
//		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 sMaterial::GetIgnoreVertexAlphaPasses( void )
{
	// Return a bitfield with a bit set for any pass that is flagged to ignore vertex alpha.
	uint32 bf = 0;

	for( uint32 p = 0; p < m_passes; ++p )
	{
		if( m_flags[p] & MATFLAG_PASS_IGNORE_VERTEX_ALPHA )
		{
			bf |= ( 1 << p );
		}
	}
	
	return bf;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMaterial* GetMaterial( uint32 checksum, sScene *p_scene )
{
	if( p_scene->pMaterialTable )
	{
		p_scene->pMaterialTable->IterateStart();
		sMaterial *p_mat = p_scene->pMaterialTable->IterateNext();
		while( p_mat )
		{
			if( p_mat->m_checksum == checksum )
			{
				return p_mat;
			}
			p_mat = p_scene->pMaterialTable->IterateNext();
		}
	}
	return NULL;
}




#define MemoryRead( dst, size, num, src )	CopyMemory(( dst ), ( src ), (( num ) * ( size )));	\
											( src ) += (( num ) * ( size ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Lst::HashTable< sMaterial >	*LoadMaterialsFromMemory( void **pp_mem, Lst::HashTable< Nx::CTexture > *p_texture_table )
{
	uint8	*p_data = (uint8*)( *pp_mem );
	uint32	MMAG, MMIN, K, L, NumSeqs, seq, NumKeys;
	
	// Get number of materials.
	uint32 new_materials;
	MemoryRead( &new_materials, sizeof( uint32 ), 1, p_data );
	
	Lst::HashTable< sMaterial >* pMaterialTable;

	// Create table, dynamically sizing it based on the number of new materials.
	uint32 optimal_table_size	= new_materials * 2;
	uint32 test					= 4;
	uint32 size					= 2;

	for( ;; test <<= 1, ++size )
	{
		// Check if this iteration of table size is sufficient, or if we have hit the maximum size.
		if(( optimal_table_size < test ) || ( size >= 12 ))
		{
			pMaterialTable = new Lst::HashTable< sMaterial >( size );
			break;
		}
	}
	
	// Loop over materials.
	for( uint32 i = 0; i < new_materials; ++i )
	{
		// Create new material.
		sMaterial *pMat = new sMaterial;

		// Get material checksum.
		MemoryRead( &pMat->m_checksum, sizeof( uint32 ), 1, p_data );

		// Get material name checksum.
		MemoryRead( &pMat->m_name_checksum, sizeof( uint32 ), 1, p_data );

		// Get number of passes.
		MemoryRead( &pMat->m_passes, sizeof( uint32 ), 1, p_data );

		// Get alpha cutoff value.
		uint32 AlphaCutoff;
		MemoryRead( &AlphaCutoff, sizeof( uint32 ), 1, p_data );
		Dbg_Assert( AlphaCutoff <= 0xFF );
		pMat->m_alpha_cutoff = (uint8)AlphaCutoff;

		// Get sorted flag.
		MemoryRead( &pMat->m_sorted, sizeof( bool ), 1, p_data );
		
		// Get draw order.
		MemoryRead( &pMat->m_draw_order, sizeof( float ), 1, p_data );
		
		// Get single sided flag.
		bool single_sided;
		MemoryRead( &single_sided, sizeof( bool ), 1, p_data );

		// Get backface cull flag.
		MemoryRead( &pMat->m_no_bfc, sizeof( bool ), 1, p_data );

		// Get z-bias value.
		int zbias;
		MemoryRead( &zbias, sizeof( int ), 1, p_data );
		pMat->m_zbias = (uint8)(( zbias > 16 ) ? 16 : zbias );

		// Get grassify flag and (optionally) grassify data.
		bool grassify;
		MemoryRead( &grassify, sizeof( bool ), 1, p_data );
		if( grassify )
		{
			MemoryRead( &pMat->m_grass_height, sizeof( float ), 1, p_data );
			MemoryRead( &pMat->m_grass_layers, sizeof( int ), 1, p_data );
		}

		// Get specular power and (optionally) specular color.
		MemoryRead( &pMat->m_specular_color[3], sizeof( float ), 1, p_data );
		if( pMat->m_specular_color[3] > 0.0f )
		{
			MemoryRead( &pMat->m_specular_color[0], sizeof( float ) * 3, 1, p_data );
		}

		// Neutral under proven otherwise.
		bool neutral_material_color = true;
		for( uint32 pass = 0; pass < pMat->m_passes; ++pass )
		{
			// Get texture checksum.
			uint32 TextureChecksum;
			MemoryRead( &TextureChecksum, sizeof( uint32 ), 1, p_data );

			// Get material flags.
			MemoryRead( &pMat->m_flags[pass], sizeof( uint32 ), 1, p_data );

			// Get pass color flag.
			bool has_color;
			MemoryRead( &has_color, sizeof( bool ), 1, p_data );

			// Get pass color.
			MemoryRead( &pMat->m_color[pass][0], sizeof( float ) * 3, 1, p_data );

			// Check for color being neutral.
			if( neutral_material_color )
			{
				if(( pMat->m_color[pass][0] != 0.5f ) || ( pMat->m_color[pass][1] != 0.5f ) || ( pMat->m_color[pass][2] != 0.5f ))
				{
					neutral_material_color = false;
				}
			}
			
			// Get ALPHA register value.
			uint64 reg_alpha;
			MemoryRead( ®_alpha, sizeof( uint64 ), 1, p_data );

			uint32	blend_mode		= (uint32)( reg_alpha & 0xFFFFFFUL );
			uint32	fixed_alpha		= (uint32)( reg_alpha >> 32 );
			pMat->m_reg_alpha[pass]	= blend_mode | ( fixed_alpha << 24 );

			// Also calculate the floating point version of the fixed alpha.
			pMat->m_color[pass][3]	= fixed_alpha / 128.0f;

			// Backface cull test - if this is an alpha blended material, turn off backface culling, except
			// where the material has been explicitly flagged as single sided.
			if(( pass == 0 ) && !single_sided && (( pMat->m_reg_alpha[pass] & sMaterial::BLEND_MODE_MASK ) != 0x00 ))
			{
				pMat->m_no_bfc = true;
			}

			// Get UV addressing types.
			uint32 u_addressing, v_addressing;
			MemoryRead( &u_addressing, sizeof( uint32 ), 1, p_data );
			MemoryRead( &v_addressing, sizeof( uint32 ), 1, p_data );
			pMat->m_uv_addressing[pass] = (( v_addressing << 16 ) | u_addressing );
			
			// Get environment map u and v tiling multiples.
			MemoryRead( &pMat->m_envmap_tiling[pass][0], sizeof( float ) * 2, 1, p_data );

			// Get minification and magnification filtering mode.
			MemoryRead( &pMat->m_filtering_mode[pass], sizeof( uint32 ), 1, p_data );

			// Read uv wibble data if present.
			if( pMat->m_flags[pass] & MATFLAG_UV_WIBBLE )
			{
				// Flag that this material wibbles.
				pMat->m_uv_wibble = true;
				
				// Create uv wibble params structure.
				pMat->mp_UVWibbleParams[pass] = new sUVWibbleParams;
				MemoryRead( pMat->mp_UVWibbleParams[pass], sizeof( float ) * 8, 1, p_data );
			}

			// Read vertex color wibble data.
			if(( pass == 0 ) && ( pMat->m_flags[0] & MATFLAG_VC_WIBBLE ))
			{
				MemoryRead( &NumSeqs, sizeof( uint32 ), 1, p_data );
				pMat->m_num_wibble_vc_anims = NumSeqs;

				// Create sequence data array.
				pMat->mp_wibble_vc_params = new sVCWibbleParams[NumSeqs];
				
				// Create resultant color array.
				pMat->mp_wibble_vc_colors = new D3DCOLOR[NumSeqs];

				for( seq = 0; seq < NumSeqs; ++seq )
				{ 
					MemoryRead( &NumKeys, sizeof( uint32 ), 1, p_data );

					int phase;
					MemoryRead( &phase, sizeof( int ), 1, p_data );

					// Create array for keyframes.
					pMat->mp_wibble_vc_params[seq].m_num_keyframes	= NumKeys;
					pMat->mp_wibble_vc_params[seq].m_phase			= phase;
					pMat->mp_wibble_vc_params[seq].mp_keyframes		= new sVCWibbleKeyframe[NumKeys];

					// Read keyframes into array.
					MemoryRead( pMat->mp_wibble_vc_params[seq].mp_keyframes, sizeof( sVCWibbleKeyframe ), NumKeys, p_data );
				}
			}

			// Read texture wibble data.
			if( pMat->m_flags[pass] & MATFLAG_PASS_TEXTURE_ANIMATES )
			{
				// Create the texture wibble structure if not created yet.
				if( pMat->mp_wibble_texture_params == NULL )
				{
					pMat->mp_wibble_texture_params = new NxXbox::sTextureWibbleParams;
					ZeroMemory( pMat->mp_wibble_texture_params, sizeof( NxXbox::sTextureWibbleParams ));

					// Flag the material as having texture wibble.
					pMat->m_texture_wibble = true;
				}

				int num_keyframes, period, iterations, phase;
				MemoryRead( &num_keyframes, sizeof( int ), 1, p_data );
				MemoryRead( &period, sizeof( int ), 1, p_data );			// This value is currently discarded.
				MemoryRead( &iterations, sizeof( int ), 1, p_data );
				MemoryRead( &phase, sizeof( int ), 1, p_data );

				Dbg_Assert( num_keyframes > 0 );

				pMat->mp_wibble_texture_params->m_num_keyframes[pass]	= num_keyframes;
				pMat->mp_wibble_texture_params->m_phase[pass]			= phase;
				pMat->mp_wibble_texture_params->m_num_iterations[pass]	= iterations;
				pMat->mp_wibble_texture_params->mp_keyframes[pass]		= new NxXbox::sTextureWibbleKeyframe[num_keyframes];

				for( int ati = 0; ati < num_keyframes; ++ati )
				{
					MemoryRead( &pMat->mp_wibble_texture_params->mp_keyframes[pass][ati].m_time,		sizeof( uint32 ), 1, p_data );

					// Read the texture checksum.
					uint32 cs;
					MemoryRead( &cs, sizeof( uint32 ), 1, p_data );

					// Set the TextureChecksum variable so the mp_tex member will get populated.
					if( ati == 0 )
					{
						TextureChecksum = cs;
					}

					// Resolve the checksum to a texture pointer.
					Nx::CXboxTexture *p_xbox_texture = static_cast( p_texture_table->GetItem( cs ));
					pMat->mp_wibble_texture_params->mp_keyframes[pass][ati].mp_texture = ( p_xbox_texture ) ? p_xbox_texture->GetEngineTexture() : NULL;
				}
			}

			if( TextureChecksum )
			{
				// If textured, resolve texture checksum...
				Nx::CXboxTexture	*p_xbox_texture	= static_cast( p_texture_table->GetItem( TextureChecksum ));
				sTexture			*mp_tex			= ( p_xbox_texture ) ? p_xbox_texture->GetEngineTexture() : NULL;

				// Bail if checksum not found.
				if( mp_tex == NULL )
				{
					Dbg_Message( "error: couldn't find texture checksum %08X\n", TextureChecksum );
					pMat->mp_tex[pass] = NULL;
				}
				else
				{
					// Set texture pointer.
					pMat->mp_tex[pass] = mp_tex;
				}

				// Get mipmap info.
				MemoryRead( &MMAG, sizeof( uint32 ), 1, p_data );
				MemoryRead( &MMIN, sizeof( uint32 ), 1, p_data );
				MemoryRead( &K, sizeof( uint32 ), 1, p_data );
				MemoryRead( &L, sizeof( uint32 ), 1, p_data );
				
				// Default PS2 value for K appears to be -8.0f - we are interested in deviations from this value.
				pMat->m_k[pass]	= ( *(float*)&K ) + 8.0f;
				
				// Dave note 09/03/02 - having MIPs selected earlier than normal seems to cause some problems, since Xbox
				// MIP selection is so different to Ps2. Limit the k value such that Xbox can never select smaller MIPs
				// earlier than it would do by default.
				if( pMat->m_k[pass] > 0.0f )
				{
					pMat->m_k[pass] = 0.0f;
				}
			}
			else
			{
				// ...otherwise just step past mipmap info.
				pMat->mp_tex[pass] = NULL;
				p_data += 16;
			}
		}

		// Set the no material color flag if appropriate.
		if( neutral_material_color )
		{
			pMat->m_flags[0] |= MATFLAG_NO_MAT_COL_MOD;
		}
		
		// Set the specular flag if appropriate.
		if( pMat->m_specular_color[3] > 0.0f )
		{
			pMat->m_flags[0] |= MATFLAG_SPECULAR;
		}

		// There is a problem adding materials with the same checksum into the table.
		// This could happen when materials in different scenes share the same name.
		// It also happens for the dummy material (checksum == 0), so for now just special-case
		// that one.
		if( pMat->m_checksum == 0 )
		{
			if( !pMaterialTable->GetItem( 0 ))
			{
				pMaterialTable->PutItem( pMat->m_checksum, pMat );
			}
		}
		else		
		{
			if( pMaterialTable->GetItem( pMat->m_checksum ))
			{
				Dbg_MsgAssert( 0, ( "NXXBOX ERROR: duplicate material: %x\n", pMat->m_checksum ));
			}
			else
			{
				pMaterialTable->PutItem( pMat->m_checksum, pMat );
			}
		}
	}

	// Set the data pointer to the new position on return.
	*pp_mem = p_data;

	return pMaterialTable;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Lst::HashTable< sMaterial >	*LoadMaterials( void *p_FH, Lst::HashTable< Nx::CTexture > *p_texture_table )
{
	uint32 MMAG, MMIN, K, L, NumSeqs, seq, NumKeys;
	
	// Get number of materials.
	uint32 new_materials;
	File::Read( &new_materials, sizeof( uint32 ), 1, p_FH );
	
	Lst::HashTable< sMaterial >* pMaterialTable;

	// Create table, dynamically sizing it based on the number of new materials.
	uint32 optimal_table_size	= new_materials * 2;
	uint32 test					= 4;
	uint32 size					= 2;

	for( ;; test <<= 1, ++size )
	{
		// Check if this iteration of table size is sufficient, or if we have hit the maximum size.
		if(( optimal_table_size < test ) || ( size >= 12 ))
		{
			pMaterialTable = new Lst::HashTable< sMaterial >( size );
			break;
		}
	}
	
	// Loop over materials.
	for( uint32 i = 0; i < new_materials; ++i )
	{
		// Create new material.
		sMaterial *pMat = new sMaterial;

		// Get material checksum.
		File::Read( &pMat->m_checksum, sizeof( uint32 ), 1, p_FH );

		// Get material name checksum.
		File::Read( &pMat->m_name_checksum, sizeof( uint32 ), 1, p_FH );

		// Get number of passes.
		File::Read( &pMat->m_passes, sizeof( uint32 ), 1, p_FH );

		// Get alpha cutoff value.
		uint32 AlphaCutoff;
		File::Read( &AlphaCutoff, sizeof( uint32 ), 1, p_FH );
		pMat->m_alpha_cutoff = (uint8)AlphaCutoff;

		// Get sorted flag.
		File::Read( &pMat->m_sorted, sizeof( bool ), 1, p_FH );
		
		// Get draw order.
		File::Read( &pMat->m_draw_order, sizeof( float ), 1, p_FH );
		
		// Get single sided flag.
		bool single_sided;
		File::Read( &single_sided, sizeof( bool ), 1, p_FH );

		// Get backface cull flag.
		File::Read( &pMat->m_no_bfc, sizeof( bool ), 1, p_FH );

		// Get z-bias value.
		int zbias;
		File::Read( &zbias, sizeof( int ), 1, p_FH );
		pMat->m_zbias = (uint8)(( zbias > 16 ) ? 16 : zbias );

		// Get grassify flag and (optionally) grassify data.
		bool grassify;
		File::Read( &grassify, sizeof( bool ), 1, p_FH );
		if( grassify )
		{
			File::Read( &pMat->m_grass_height, sizeof( float ), 1, p_FH );
			File::Read( &pMat->m_grass_layers, sizeof( int ), 1, p_FH );
		}

		// Get specular power and (optionally) specular color.
		File::Read( &pMat->m_specular_color[3], sizeof( float ), 1, p_FH );
		if( pMat->m_specular_color[3] > 0.0f )
		{
			File::Read( &pMat->m_specular_color[0], sizeof( float ) * 3, 1, p_FH );
		}

		// Neutral under proven otherwise.
		bool neutral_material_color = true;
		for( uint32 pass = 0; pass < pMat->m_passes; ++pass )
		{
			// Get texture checksum.
			uint32 TextureChecksum;
			File::Read( &TextureChecksum, sizeof( uint32 ), 1, p_FH );

			// Get material flags.
			File::Read( &pMat->m_flags[pass], sizeof( uint32 ), 1, p_FH );

			// Get pass color flag.
			bool has_color;
			File::Read( &has_color, sizeof( bool ), 1, p_FH );

			// Get pass color.
			File::Read( &pMat->m_color[pass][0], sizeof( float ) * 3, 1, p_FH );

			// Check for color being neutral.
			if( neutral_material_color )
			{
				if(( pMat->m_color[pass][0] != 0.5f ) || ( pMat->m_color[pass][1] != 0.5f ) || ( pMat->m_color[pass][2] != 0.5f ))
				{
					neutral_material_color = false;
				}
			}
			
			// Get ALPHA register value.
			uint64 reg_alpha;
			File::Read( ®_alpha, sizeof( uint64 ), 1, p_FH );

			uint32	blend_mode		= (uint32)( reg_alpha & 0xFFFFFFUL );
			uint32	fixed_alpha		= (uint32)( reg_alpha >> 32 );
			pMat->m_reg_alpha[pass]	= blend_mode | ( fixed_alpha << 24 );

			// Also calculate the floating point version of the fixed alpha.
			pMat->m_color[pass][3]	= fixed_alpha / 128.0f;

			// Backface cull test - if this is an alpha blended material, turn off backface culling, except
			// where the material has been explicitly flagged as single sided.
			if(( pass == 0 ) && !single_sided && (( pMat->m_reg_alpha[pass] & sMaterial::BLEND_MODE_MASK ) != 0x00 ))
			{
				pMat->m_no_bfc = true;
			}

			// Get UV addressing types.
			uint32 u_addressing, v_addressing;
			File::Read( &u_addressing, sizeof( uint32 ), 1, p_FH );
			File::Read( &v_addressing, sizeof( uint32 ), 1, p_FH );
			pMat->m_uv_addressing[pass] = (( v_addressing << 16 ) | u_addressing );
			
			// Get environment map u and v tiling multiples.
			File::Read( &pMat->m_envmap_tiling[pass][0], sizeof( float ) * 2, 1, p_FH );

			// Get minification and magnification filtering mode.
			File::Read( &pMat->m_filtering_mode[pass], sizeof( uint32 ), 1, p_FH );

			// Read uv wibble data if present.
			if( pMat->m_flags[pass] & MATFLAG_UV_WIBBLE )
			{
				// Flag that this material wibbles.
				pMat->m_uv_wibble = true;
				
				// Create uv wibble params structure.
				pMat->mp_UVWibbleParams[pass] = new sUVWibbleParams;
				File::Read( pMat->mp_UVWibbleParams[pass], sizeof( float ) * 8, 1, p_FH );
			}

			// Read vertex color wibble data.
			if(( pass == 0 ) && ( pMat->m_flags[0] & MATFLAG_VC_WIBBLE ))
			{
				File::Read( &NumSeqs, sizeof( uint32 ), 1, p_FH );
				pMat->m_num_wibble_vc_anims = NumSeqs;

				// Create sequence data array.
				pMat->mp_wibble_vc_params = new sVCWibbleParams[NumSeqs];
				
				// Create resultant color array.
				pMat->mp_wibble_vc_colors = new D3DCOLOR[NumSeqs];

				for( seq = 0; seq < NumSeqs; ++seq )
				{ 
					File::Read( &NumKeys, sizeof( uint32 ), 1, p_FH );

					int phase;
					File::Read( &phase, sizeof( int ), 1, p_FH );

					// Create array for keyframes.
					pMat->mp_wibble_vc_params[seq].m_num_keyframes	= NumKeys;
					pMat->mp_wibble_vc_params[seq].m_phase			= phase;
					pMat->mp_wibble_vc_params[seq].mp_keyframes		= new sVCWibbleKeyframe[NumKeys];

					// Read keyframes into array.
					File::Read( pMat->mp_wibble_vc_params[seq].mp_keyframes, sizeof( sVCWibbleKeyframe ), NumKeys, p_FH );
				}
			}

			// Read texture wibble data.
			if( pMat->m_flags[pass] & MATFLAG_PASS_TEXTURE_ANIMATES )
			{
				// Create the texture wibble structure if not created yet.
				if( pMat->mp_wibble_texture_params == NULL )
				{
					pMat->mp_wibble_texture_params = new NxXbox::sTextureWibbleParams;
					ZeroMemory( pMat->mp_wibble_texture_params, sizeof( NxXbox::sTextureWibbleParams ));

					// Flag the material as having texture wibble.
					pMat->m_texture_wibble = true;
				}

				int num_keyframes, period, iterations, phase;
				File::Read( &num_keyframes, sizeof( int ), 1, p_FH );
				File::Read( &period, sizeof( int ), 1, p_FH );			// This value is currently discarded.
				File::Read( &iterations, sizeof( int ), 1, p_FH );
				File::Read( &phase, sizeof( int ), 1, p_FH );

				Dbg_Assert( num_keyframes > 0 );

				pMat->mp_wibble_texture_params->m_num_keyframes[pass]	= num_keyframes;
				pMat->mp_wibble_texture_params->m_phase[pass]			= phase;
				pMat->mp_wibble_texture_params->m_num_iterations[pass]	= iterations;
				pMat->mp_wibble_texture_params->mp_keyframes[pass]		= new NxXbox::sTextureWibbleKeyframe[num_keyframes];

				for( int ati = 0; ati < num_keyframes; ++ati )
				{
					File::Read( &pMat->mp_wibble_texture_params->mp_keyframes[pass][ati].m_time,		sizeof( uint32 ), 1, p_FH );

					// Read the texture checksum.
					uint32 cs;
					File::Read( &cs, sizeof( uint32 ), 1, p_FH );

					// Set the TextureChecksum variable so the mp_tex member will get populated.
					if( ati == 0 )
					{
						TextureChecksum = cs;
					}

					// Resolve the checksum to a texture pointer.
					Nx::CXboxTexture *p_xbox_texture = static_cast( p_texture_table->GetItem( cs ));
					pMat->mp_wibble_texture_params->mp_keyframes[pass][ati].mp_texture = ( p_xbox_texture ) ? p_xbox_texture->GetEngineTexture() : NULL;
				}
			}

			if( TextureChecksum )
			{
				// If textured, resolve texture checksum...
				Nx::CXboxTexture	*p_xbox_texture	= static_cast( p_texture_table->GetItem( TextureChecksum ));
				sTexture			*mp_tex			= ( p_xbox_texture ) ? p_xbox_texture->GetEngineTexture() : NULL;

				// Bail if checksum not found.
				if( mp_tex == NULL )
				{
					Dbg_Message( "error: couldn't find texture checksum %08X\n", TextureChecksum );
					pMat->mp_tex[pass] = NULL;
				}
				else
				{
					// Set texture pointer.
					pMat->mp_tex[pass] = mp_tex;
				}

				// Get mipmap info.
				File::Read( &MMAG, sizeof( uint32 ), 1, p_FH );
				File::Read( &MMIN, sizeof( uint32 ), 1, p_FH );
				File::Read( &K, sizeof( uint32 ), 1, p_FH );
				File::Read( &L, sizeof( uint32 ), 1, p_FH );
				
				// Default PS2 value for K appears to be -8.0f - we are interested in deviations from this value.
				pMat->m_k[pass]	= ( *(float*)&K ) + 8.0f;
				
				// Dave note 09/03/02 - having MIPs selected earlier than normal seems to cause some problems, since Xbox
				// MIP selection is so different to Ps2. Limit the k value such that Xbox can never select smaller MIPs
				// earlier than it would do by default.
				if( pMat->m_k[pass] > 0.0f )
				{
					pMat->m_k[pass] = 0.0f;
				}
			}
			else
			{
				// ...otherwise just step past mipmap info.
				pMat->mp_tex[pass] = NULL;
				File::Seek( p_FH, 16, SEEK_CUR );
			}
		}

		// Set the no material color flag if appropriate.
		if( neutral_material_color )
		{
			pMat->m_flags[0] |= MATFLAG_NO_MAT_COL_MOD;
		}
		
		// Set the specular flag if appropriate.
		if( pMat->m_specular_color[3] > 0.0f )
		{
			pMat->m_flags[0] |= MATFLAG_SPECULAR;
		}

		// There is a problem adding materials with the same checksum into the table.
		// This could happen when materials in different scenes share the same name.
		// It also happens for the dummy material (checksum == 0), so for now just special-case
		// that one.
		if( pMat->m_checksum == 0 )
		{
			if( !pMaterialTable->GetItem( 0 ))
			{
				pMaterialTable->PutItem( pMat->m_checksum, pMat );
			}
		}
		else		
		{
			if( pMaterialTable->GetItem( pMat->m_checksum ))
			{
				Dbg_MsgAssert( 0, ( "NXXBOX ERROR: duplicate material: %x\n", pMat->m_checksum ));
			}
			else
			{
				pMaterialTable->PutItem( pMat->m_checksum, pMat );
			}
		}
	}
	return pMaterialTable;
}








} // namespace NxXbox



================================================
FILE: Code/Gfx/XBox/NX/material.h
================================================
#ifndef __MATERIAL_H
#define __MATERIAL_H

#include 
#include 
#include 
#include "texture.h"

namespace NxXbox
{

// Material Flags
#define MATFLAG_UV_WIBBLE					(1<<0)
#define MATFLAG_VC_WIBBLE					(1<<1)
#define MATFLAG_TEXTURED					(1<<2)
#define MATFLAG_ENVIRONMENT					(1<<3)
#define MATFLAG_DECAL						(1<<4)
#define MATFLAG_SMOOTH						(1<<5)
#define MATFLAG_TRANSPARENT					(1<<6)
#define MATFLAG_PASS_COLOR_LOCKED			(1<<7)
#define MATFLAG_SPECULAR					(1<<8)		// Specular lighting is enabled on this material (Pass0).
#define MATFLAG_BUMP_SIGNED_TEXTURE			(1<<9)		// This pass uses an offset texture which needs to be treated as signed data.
#define MATFLAG_BUMP_LOAD_MATRIX			(1<<10)		// This pass requires the bump mapping matrix elements to be set up.
#define MATFLAG_PASS_TEXTURE_ANIMATES		(1<<11)		// This pass has a texture which animates.
#define MATFLAG_PASS_IGNORE_VERTEX_ALPHA	(1<<12)		// This pass should not have the texel alpha modulated by the vertex alpha.
#define MATFLAG_EXPLICIT_UV_WIBBLE			(1<<14)		// Uses explicit uv wibble (set via script) rather than calculated.
#define MATFLAG_WATER_EFFECT				(1<<27)		// This material should be processed to provide the water effect.
#define MATFLAG_NO_MAT_COL_MOD				(1<<28)		// No material color modulation required (all passes have m.rgb = 0.5).


const uint32 MAX_PASSES = 4;


	
struct sUVWibbleParams
{
			sUVWibbleParams( void );
			~sUVWibbleParams( void );

	float	m_UVel;
	float	m_VVel;
	float	m_UFrequency;
	float	m_VFrequency;
	float	m_UAmplitude;
	float	m_VAmplitude;
	float	m_UPhase;
	float	m_VPhase;
	float	m_UVMatrix[4];		// This value is written to dynamically. The first two values are rotation, the second two are translation.
};

struct sVCWibbleKeyframe
{
	int			m_time;
	Image::RGBA	m_color;
};

struct sVCWibbleParams
{
	uint32				m_num_keyframes;
	int					m_phase;
	sVCWibbleKeyframe	*mp_keyframes;
};


struct sTextureWibbleKeyframe
{
	int			m_time;
	sTexture	*mp_texture;
};

struct sTextureWibbleParams
{
	uint32					m_num_keyframes[MAX_PASSES];
	int						m_phase[MAX_PASSES];
	int						m_num_iterations[MAX_PASSES];
	sTextureWibbleKeyframe	*mp_keyframes[MAX_PASSES];
};



struct sMaterial
{
	public:

	static const uint32		BLEND_MODE_MASK	= 0x00FFFFFFUL;

							sMaterial( void );
							~sMaterial( void );
	
	void					Submit( void );
	uint32					GetIgnoreVertexAlphaPasses( void );
	void					figure_wibble_uv( void );
	void					figure_wibble_vc( void );
	void					figure_wibble_texture( void );

	uint32					m_checksum;
	uint32					m_name_checksum;
	uint32					m_passes;

	bool					m_sorted;
	bool					m_no_bfc;
	bool					m_uv_wibble;
	bool					m_texture_wibble;
	uint8					m_alpha_cutoff;
	uint8					m_zbias;

	float					m_grass_height;
	int						m_grass_layers;
	float					m_draw_order;
	uint32					m_flags[MAX_PASSES];
	sTexture*				mp_tex[MAX_PASSES];
	float					m_color[MAX_PASSES][4];				// Element [pass][3] holds the fixed alpha value where appropriate.
	uint32					m_reg_alpha[MAX_PASSES];			// Low 24 bits are blend mode, high 8 bits are fixed alpha value.
	uint32					m_uv_addressing[MAX_PASSES];
	float					m_envmap_tiling[MAX_PASSES][2];		// Tile multiples for env mapping (NOTE: could maybe be changed to byte array?)
	uint32					m_filtering_mode[MAX_PASSES];
	sUVWibbleParams			*mp_UVWibbleParams[MAX_PASSES];
	float					m_k[MAX_PASSES];
	uint32					m_num_wibble_vc_anims;
	sVCWibbleParams			*mp_wibble_vc_params;
	D3DCOLOR				*mp_wibble_vc_colors;				// Max of eight banks of vertex color wibble information.
	sTextureWibbleParams	*mp_wibble_texture_params;
	float					m_specular_color[4];				// Specular color (0-2) plus power term (3).
};


Lst::HashTable< sMaterial >	*LoadMaterials( void *p_FH, Lst::HashTable< Nx::CTexture > *p_texture_table );
Lst::HashTable< sMaterial >	*LoadMaterialsFromMemory( void **pp_mem, Lst::HashTable< Nx::CTexture > *p_texture_table );

//extern Lst::HashTable< sMaterial > *pMaterialTable;
extern uint32 NumMaterials;

} // namespace NxXbox

#endif // __MATERIAL_H



================================================
FILE: Code/Gfx/XBox/NX/mesh.cpp
================================================
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include "nx_init.h"
#include "texture.h"
#include "scene.h"
#include "mesh.h"
#include "anim.h"
#include "render.h"
#include "billboard.h"

namespace NxXbox
{

bool			s_meshScalingEnabled = false;
char*			s_pWeightIndices = NULL;
float*			s_pWeights = NULL;
Mth::Vector*	s_pBonePositions = NULL;
Mth::Vector*	s_pBoneScales = NULL;
int				s_currentVertIndex = 0;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SetMeshScalingParameters( Nx::SMeshScalingParameters* pParams )
{
	Dbg_Assert( pParams );

	s_meshScalingEnabled	= true;
	s_pWeights				= pParams->pWeights;
	s_pWeightIndices		= pParams->pWeightIndices;
	s_pBoneScales			= pParams->pBoneScales;
	s_pBonePositions		= pParams->pBonePositions;
	s_currentVertIndex		= 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void DisableMeshScaling( void )
{
	s_meshScalingEnabled	= false;
	s_pWeights				= NULL;
	s_pWeightIndices		= NULL;
	s_pBoneScales			= NULL;
	s_pBonePositions		= NULL;
	s_currentVertIndex		= 0;
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline Mth::Vector get_bone_scale( int bone_index )
{
	Mth::Vector returnVec( 1.0f, 1.0f, 1.0f, 1.0f );

	if( bone_index >= 29 && bone_index <= 33 )
	{
		// this only works with the thps5 skeleton, whose
		// head bones are between 29 and 33...
		// (eventually, we can remove the subtract 29
		// once the exporter is massaging the data correctly)
		returnVec = s_pBoneScales[ bone_index - 29 ];
		
		// Y & Z are reversed...  odd!
		Mth::Vector tempVec = returnVec;
		returnVec[Y] = tempVec[Z];
		returnVec[Z] = tempVec[Y];
	}
	else if( bone_index == -1 )
	{
		// implies that it's not weighted to a bone
		return returnVec;
	}
	else
	{
		// implies that it's weighted to the wrong bone
		return returnVec;
	}
	return returnVec;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline Mth::Vector get_bone_pos( int bone_index )
{
	Mth::Vector returnVec( 0.0f, 0.0f, 0.0f, 1.0f );
	
	if( bone_index >= 29 && bone_index <= 33 )
	{
		// this only works with the thps5 skeleton, whose
		// head bones are between 29 and 33...
		// (eventually, we can remove the subtract 29
		// once the exporter is massaging the data correctly)
		returnVec = s_pBonePositions[ bone_index - 29 ];
	}
	else if( bone_index == -1 )
	{
		// implies that it's not weighted to a bone
		return returnVec;
	}
	else
	{
		// implies that it's weighted to the wrong bone
		return returnVec;
	}
	returnVec[W] = 1.0f;

	return returnVec;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void ApplyMeshScaling( float* p_vertices, int num_verts )
{
	if( s_meshScalingEnabled )
	{
		for( int v = 0; v < num_verts; ++v, p_vertices += 3 )
		{
			float x = p_vertices[0];
			float y = p_vertices[1];
			float z = p_vertices[2];

    		Mth::Vector origPos( x, y, z, 1.0f );

			Mth::Vector bonePos0 = get_bone_pos( s_pWeightIndices[v * 3] );
			Mth::Vector bonePos1 = get_bone_pos( s_pWeightIndices[v * 3 + 1] );
			Mth::Vector bonePos2 = get_bone_pos( s_pWeightIndices[v * 3 + 2] );

			// Need to scale each vert relative to its parent bone.
			Mth::Vector localPos0 = origPos - bonePos0;
			Mth::Vector localPos1 = origPos - bonePos1;
			Mth::Vector localPos2 = origPos - bonePos2;

			localPos0.Scale( get_bone_scale( s_pWeightIndices[v * 3] ) );
			localPos1.Scale( get_bone_scale( s_pWeightIndices[v * 3 + 1] ) );
			localPos2.Scale( get_bone_scale( s_pWeightIndices[v * 3 + 2] ) );

			localPos0 += bonePos0;
			localPos1 += bonePos1;
			localPos2 += bonePos2;
			
			Mth::Vector scaledPos = ( localPos0 * s_pWeights[v * 3] ) +
									( localPos1 * s_pWeights[v * 3 + 1] ) +
									( localPos2 * s_pWeights[v * 3 + 2] );

			p_vertices[0] = scaledPos[X];
			p_vertices[1] = scaledPos[Y];
			p_vertices[2] = scaledPos[Z];
		}
	}
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMesh::sMesh( void )
{
	m_flags							= 0;
	m_num_vertex_buffers			= 1;
	m_current_write_vertex_buffer	= 0;
	for( int vb = 0; vb < MAX_VERTEX_BUFFERS; ++vb )
	{
		mp_vertex_buffer[vb]		= NULL;
	}
	for( int ib = 0; ib < MAX_INDEX_BUFFERS; ++ib )
	{
		mp_index_buffer[ib]			= NULL;
		m_num_indices[ib]			= 0;
	}
	mp_vc_wibble_data				= NULL;
	mp_index_lod_data				= NULL;
	mp_billboard_data				= NULL;
	m_bone_index					= -1;
	mp_transform					= NULL;
	m_diffuse_offset				= 0;
	m_uv0_offset					= 0;
	m_normal_offset					= 0;
	m_vertex_stride					= 0;
	m_vertex_shader[0]				= 0;
	m_pixel_shader					= 0;

	SetActive( true );
	SetVisibility( 0xFF );

	// Set default primitive type.
	m_primitive_type				= D3DPT_TRIANGLESTRIP;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMesh::~sMesh( void )
{
	// Remove this mesh from the billboard manager if appropriate.
	if( m_flags & sMesh::MESH_FLAG_BILLBOARD )
	{
		BillboardManager.RemoveEntry( this );
	}

	EngineGlobals.p_Device->BlockUntilIdle();

	if( mp_transform )
	{
		delete mp_transform;
	}
	
	if( !( m_flags & MESH_FLAG_IS_INSTANCE ))
	{
		for( int ib = 0; ib < MAX_INDEX_BUFFERS; ++ib )
		{
			delete [] mp_index_buffer[ib];
			mp_index_buffer[ib] = NULL;
		}

		if( mp_vc_wibble_data )
		{
			delete mp_vc_wibble_data;
			mp_vc_wibble_data = NULL;
		}

		if( mp_index_lod_data )
		{
			delete mp_index_lod_data;
			mp_index_lod_data = NULL;
		}

		if( mp_billboard_data )
		{
			delete mp_billboard_data;
			mp_billboard_data = NULL;
		}

		UINT					stride;
		IDirect3DVertexBuffer8	*p_vb;
		D3DDevice_GetStreamSource( 0, &p_vb, &stride );
		if( p_vb )
		{
			// GetStreamSource() increments the reference count, so call Release() here.
			p_vb->Release();
		}

		for( uint32 i = 0; i < m_num_vertex_buffers; ++i )
		{
			if( mp_vertex_buffer[i] )
			{
				if( p_vb == mp_vertex_buffer[i] )
				{
					// We are deleting a vertex buffer that is set as the current stream source. This can result in
					// problems with the internal D3D reference counter, so clear this up first.
					D3DDevice_SetStreamSource( 0, NULL, 0 );
				}
			
				uint8 *p_del = (uint8*)mp_vertex_buffer[i];
				delete p_del;
				mp_vertex_buffer[i]	= NULL;
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::PushVertexShader( uint32 shader_id )
{
	for( uint32 i = sMesh::VERTEX_SHADER_STACK_SIZE - 1; i > 0; --i )
	{
		m_vertex_shader[i] = m_vertex_shader[i - 1];
	}
	m_vertex_shader[0] = shader_id;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::PopVertexShader( void )
{
	for( uint32 i = 0; i < sMesh::VERTEX_SHADER_STACK_SIZE - 1; ++i )
	{
		m_vertex_shader[i] = m_vertex_shader[i + 1];
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::wibble_normals( void )
{
	if( m_flags & 0 )
	{
		// Angle in the range [-PI/16, PI/16], period is 1 second.
		float time = (float)Tmr::GetTime() * 0.0005f;

		BYTE		*p_byte;
		float		*p_normal;
		float		*p_pos;
		mp_vertex_buffer[m_current_write_vertex_buffer]->Lock( 0, 0, &p_byte, 0 );
		p_pos		= (float*)( p_byte + 0 );
		p_normal	= (float*)( p_byte + m_normal_offset );

		for( uint32 i = 0; i < m_num_vertices; ++i )
		{
			float x				= p_pos[0] - m_sphere_center.x;
			float z				= p_pos[2] - m_sphere_center.z;
			
			float time_offset_x	= time + (( x / m_sphere_radius ) * 0.5f );
			float time_offset_z	= time + (( z / m_sphere_radius ) * 0.5f );

			float angle_x		= ( Mth::PI * ( 1.0f / 64.0f ) * (float)fabs( sinf( time_offset_x * Mth::PI ))) - ( Mth::PI * ( 1.0f / 128.0f ));
			float angle_z		= ( Mth::PI * ( 1.0f / 64.0f ) * (float)fabs( sinf( time_offset_z * Mth::PI ))) - ( Mth::PI * ( 1.0f / 129.0f ));
			
			Mth::Vector	n( sinf( angle_x ), cosf(( angle_x + angle_z ) * 0.5f ), sinf( angle_z ));
			n.Normalize();
			
			p_normal[0]			= n[X];
			p_normal[1]			= n[Y];
			p_normal[2]			= n[Z];
			
			p_pos				= (float*)((BYTE*)p_pos + m_vertex_stride );
			p_normal			= (float*)((BYTE*)p_normal + m_vertex_stride );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::wibble_vc( void )
{
	if( mp_vc_wibble_data )
	{
		// Grab byte pointer to current 'write' vertex buffer.
		BYTE		*p_byte;
		D3DCOLOR	*p_color;
		mp_vertex_buffer[m_current_write_vertex_buffer]->Lock( 0, 0, &p_byte, 0 );
		p_color = (D3DCOLOR*)( p_byte + m_diffuse_offset );

		D3DCOLOR *p_color_array	= mp_material->mp_wibble_vc_colors;

		// Scan through each vertex, setting the new color.
		for( uint32 i = 0; i < m_num_vertices; ++i )
		{
			// An index of zero means no update for this vert.
			uint32 index	= mp_vc_wibble_data[i];
			if( index > 0 )
			{
				*p_color	= p_color_array[index - 1];
			}
			p_color		= (D3DCOLOR*)((BYTE*)p_color + m_vertex_stride );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::CreateDuplicateVertexBuffers( int n )
{
	// Ensure this hasn't already been called.
	Dbg_Assert( mp_vertex_buffer[0] != NULL );
	Dbg_Assert( mp_vertex_buffer[1] == NULL );
	Dbg_Assert(( n > 0 ) && ( n < MAX_VERTEX_BUFFERS ));

	// Lock the source buffer.
	BYTE *p_byte;
	if( D3D_OK != mp_vertex_buffer[0]->Lock( 0, 0, &p_byte, D3DLOCK_READONLY ))
	{
		exit( 0 );
	}
	
	for( int i = 0; i < n; ++i )
	{
		mp_vertex_buffer[i + 1] = AllocateVertexBuffer( m_vertex_stride * m_num_vertices );

		// Lock the destination buffer and copy the contents of the original buffer into the new buffer.
		BYTE *p_byte2;
		if( D3D_OK != mp_vertex_buffer[i + 1]->Lock( 0, 0, &p_byte2, 0 ))
		{
			exit( 0 );
		}
		CopyMemory( p_byte2, p_byte, m_vertex_stride * m_num_vertices );
	}

	m_num_vertex_buffers = 1 + n;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::SetPosition( Mth::Vector &pos )
{
	// Create a transform if one doesn't exist yet.
	if( mp_transform == NULL )
	{
		mp_transform = new Mth::Matrix();
		mp_transform->Ident();
	}
	
	// Figure what we need to add to each vertex, based on current position.
	Mth::Vector offset(	pos[X] - mp_transform->GetPos()[X],
						pos[Y] - mp_transform->GetPos()[Y],
						pos[Z] - mp_transform->GetPos()[Z] );

	mp_transform->SetPos( pos );
	
	for( uint32 vb = 0; vb < m_num_vertex_buffers; ++vb )
	{
		BYTE *p_byte;
		mp_vertex_buffer[vb]->Lock( 0, 0, &p_byte, 0 );

		for( uint32 v = 0; v < m_num_vertices; ++v )
		{
			((D3DVECTOR*)p_byte )->x += offset[X];
			((D3DVECTOR*)p_byte )->y += offset[Y];
			((D3DVECTOR*)p_byte )->z += offset[Z];
			p_byte += m_vertex_stride;
		}
	}

	// We also need to adjust the bounding box and sphere information for this mesh.
	m_sphere_center.x += offset[X];
	m_sphere_center.y += offset[Y];
	m_sphere_center.z += offset[Z];
}
	

	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::GetPosition( Mth::Vector *p_pos )
{
	if( mp_transform == NULL )
	{
		p_pos->Set( 0.0f, 0.0f, 0.0f );
	}
	else
	{
		*p_pos = mp_transform->GetPos();
	}
}
	

	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::SetYRotation( Mth::ERot90 rot )
{
	if( rot > Mth::ROT_0 )
	{
		// Create a transform if one doesn't exist yet.
		if( mp_transform == NULL )
		{
			mp_transform = new Mth::Matrix();
			mp_transform->Ident();
		}

		for( uint32 vb = 0; vb < m_num_vertex_buffers; ++vb )
		{
			BYTE *p_byte;
			mp_vertex_buffer[vb]->Lock( 0, 0, &p_byte, 0 );

			switch( rot )
			{
				case Mth::ROT_90:
				{
					for( uint32 v = 0; v < m_num_vertices; ++v )
					{
						float x = ((D3DVECTOR*)p_byte )->x - mp_transform->GetPos()[X];
						float z = ((D3DVECTOR*)p_byte )->z - mp_transform->GetPos()[Z];
						((D3DVECTOR*)p_byte )->x = z + mp_transform->GetPos()[X];
						((D3DVECTOR*)p_byte )->z = -x + mp_transform->GetPos()[Z];
						p_byte += m_vertex_stride;
					}

					// Adjust the bounding sphere information for this mesh.
					m_sphere_center.x	-= mp_transform->GetPos()[X];
					m_sphere_center.z	-= mp_transform->GetPos()[Z];
					float t				= m_sphere_center.x;
					m_sphere_center.x	= m_sphere_center.z + mp_transform->GetPos()[X];
					m_sphere_center.z	= -t + mp_transform->GetPos()[Z];
					break;
				}
				case Mth::ROT_180:
				{
					for( uint32 v = 0; v < m_num_vertices; ++v )
					{
						float x = ((D3DVECTOR*)p_byte )->x - mp_transform->GetPos()[X];
						float z = ((D3DVECTOR*)p_byte )->z - mp_transform->GetPos()[Z];
						((D3DVECTOR*)p_byte )->x = -x + mp_transform->GetPos()[X];
						((D3DVECTOR*)p_byte )->z = -z + mp_transform->GetPos()[Z];
						p_byte += m_vertex_stride;
					}

					// Adjust the bounding sphere information for this mesh.
					m_sphere_center.x	-= mp_transform->GetPos()[X];
					m_sphere_center.z	-= mp_transform->GetPos()[Z];
					m_sphere_center.x	= -m_sphere_center.x + mp_transform->GetPos()[X];
					m_sphere_center.z	= -m_sphere_center.z + mp_transform->GetPos()[Z];
					break;
				}
				case Mth::ROT_270:
				{
					for( uint32 v = 0; v < m_num_vertices; ++v )
					{
						float x = ((D3DVECTOR*)p_byte )->x - mp_transform->GetPos()[X];
						float z = ((D3DVECTOR*)p_byte )->z - mp_transform->GetPos()[Z];
						((D3DVECTOR*)p_byte )->x = -z + mp_transform->GetPos()[X];
						((D3DVECTOR*)p_byte )->z = x + mp_transform->GetPos()[Z];
						p_byte += m_vertex_stride;
					}

					// Adjust the bounding sphere information for this mesh.
					m_sphere_center.x	-= mp_transform->GetPos()[X];
					m_sphere_center.z	-= mp_transform->GetPos()[Z];
					float t				= m_sphere_center.x;
					m_sphere_center.x	= -m_sphere_center.z + mp_transform->GetPos()[X];
					m_sphere_center.z	= t + mp_transform->GetPos()[Z];
					break;
				}
			}
		}
	}
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::SwapVertexBuffers( void )
{
	if( m_num_vertex_buffers > 1 )
	{
		m_current_write_vertex_buffer = ( m_current_write_vertex_buffer + 1 ) % m_num_vertex_buffers;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::HandleColorOverride( void )
{
	static float constants[16];

	Dbg_Assert( m_flags & sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE );

	// Re-jig the pixel shader constants for the material color override.
	CopyMemory( constants, EngineGlobals.pixel_shader_constants, sizeof( float ) * 16 );
			
	for( uint32 p = 0; p < mp_material->m_passes; ++p )
	{
		if( !( mp_material->m_flags[p] & MATFLAG_PASS_COLOR_LOCKED ))
		{
//			EngineGlobals.pixel_shader_constants[( p * 4 ) + 0]	*= m_material_color_override[0];
//			EngineGlobals.pixel_shader_constants[( p * 4 ) + 1]	*= m_material_color_override[1];
//			EngineGlobals.pixel_shader_constants[( p * 4 ) + 2]	*= m_material_color_override[2];
			EngineGlobals.pixel_shader_constants[( p * 4 ) + 0]	= m_material_color_override[0];
			EngineGlobals.pixel_shader_constants[( p * 4 ) + 1]	= m_material_color_override[1];
			EngineGlobals.pixel_shader_constants[( p * 4 ) + 2]	= m_material_color_override[2];
		}
	}
	EngineGlobals.upload_pixel_shader_constants = true;

	// Set the pixel shader (this will upload the new constants).
	set_pixel_shader( m_pixel_shader, mp_material->m_passes );

	// Restore the pixel shader constants and flag as needing a reload.
	CopyMemory( EngineGlobals.pixel_shader_constants, constants, sizeof( float ) * 16 );
	EngineGlobals.upload_pixel_shader_constants = true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::Submit( void )
{
	DWORD	stage_zero_minfilter;
	DWORD	zwrite;
	
	// Pointless submitting a mesh with zero indices.
	if( m_num_indices == 0 )
		return;

	// Deal with vertex color wibbling.
	wibble_vc();
	
	// Set vertex shader.
	set_vertex_shader( m_vertex_shader[0] );

	if( m_flags & sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE )
	{
		HandleColorOverride();
	}
	else
	{
		// Just set the pixel shader.
		set_pixel_shader( m_pixel_shader, mp_material->m_passes );
	}

	// Deal with meshes that set no anisotropic filtering.
	if( m_flags & MESH_FLAG_NO_ANISOTROPIC )
	{
		D3DDevice_GetTextureStageState( 0, D3DTSS_MINFILTER, &stage_zero_minfilter );
		if( stage_zero_minfilter != D3DTEXF_LINEAR )
		{
			D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
		}
	}
	
	// Deal with meshes that set no z-write.
	if( m_flags & MESH_FLAG_NO_ZWRITE )
	{
		D3DDevice_GetRenderState( D3DRS_ZWRITEENABLE, &zwrite );
		if( zwrite == TRUE )
		{
			D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
		}
	}
	
	// Get the vertex buffer to submit (which is the one we have potentially just been writing to).
	IDirect3DVertexBuffer8*	p_submit_buffer = mp_vertex_buffer[m_current_write_vertex_buffer];
	
	// Swap multiple vertex buffers if present.
	SwapVertexBuffers();

	// Set the stream source.
	D3DDevice_SetStreamSource( 0, p_submit_buffer, m_vertex_stride );

	// See if we have index LOD data, in which case we need to figure distance and select the correct LOD.
	if( mp_index_lod_data )
	{
		// Figure distance from this mesh to the camera. This is *not* an efficient way to do it.
		frustum_check_sphere( &m_sphere_center, m_sphere_radius );
		float dist = get_bounding_sphere_nearest_z();
		for( int idx = 0; idx < MAX_INDEX_BUFFERS; ++idx )
		{
			if( mp_index_buffer[idx] == NULL )
			{
				// We have got to the end of the set without drawing anything. Just use the last valid index set.
				if( idx > 0 )
				{
					--idx;
					D3DDevice_DrawIndexedVertices( m_primitive_type, m_num_indices[idx], mp_index_buffer[idx] );
				}
				else
				{
					Dbg_Assert( 0 );
				}
				break;
			}

			if( dist < mp_index_lod_data[idx] )
			{
				// This is the index set we want.
				D3DDevice_DrawIndexedVertices( m_primitive_type, m_num_indices[idx], mp_index_buffer[idx] );
				break;
			}
		}
	}
	else
	{
		// Submit.
		D3DDevice_DrawIndexedVertices( m_primitive_type, m_num_indices[0], mp_index_buffer[0] );
	}

	// Deal with meshes that set no anisotropic filtering.
	if( m_flags & MESH_FLAG_NO_ANISOTROPIC )
	{
		if( stage_zero_minfilter != D3DTEXF_LINEAR )
		{
			D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, stage_zero_minfilter );
		}
	}

	// Deal with meshes that set no z-write.
	if( m_flags & MESH_FLAG_NO_ZWRITE )
	{
		if( zwrite == TRUE )
		{
			D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE, zwrite );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMesh *sMesh::Clone( bool instance )
{
	sMesh *p_clone = new sMesh();

	// Copy over basic details.
	CopyMemory( p_clone, this, sizeof( sMesh ));
	
	if( instance )
	{
		p_clone->m_flags |= MESH_FLAG_IS_INSTANCE;
	}
	else
	{
		// Build new vertex and index lists.
		p_clone->mp_vertex_buffer[0] = AllocateVertexBuffer( p_clone->m_vertex_stride * p_clone->m_num_vertices );

		BYTE *p_byte_src, *p_byte_dest;
		if( D3D_OK != mp_vertex_buffer[0]->Lock( 0, 0, &p_byte_src, D3DLOCK_READONLY ))
		{
			return NULL;
		}
		if( D3D_OK != p_clone->mp_vertex_buffer[0]->Lock( 0, 0, &p_byte_dest, 0 ))
		{
			return NULL;
		}

		// Copy over vertex information.
		CopyMemory( p_byte_dest, p_byte_src, p_clone->m_vertex_stride * p_clone->m_num_vertices );

		// Create index buffer(s) and copy over index information.
		for( int ib = 0; ib < MAX_INDEX_BUFFERS; ++ib )
		{
			if( p_clone->m_num_indices[ib] > 0 )
			{
				p_clone->mp_index_buffer[ib] = new uint16[p_clone->m_num_indices[ib]];
				CopyMemory( p_clone->mp_index_buffer[ib], mp_index_buffer[ib], sizeof( uint16 ) * p_clone->m_num_indices[ib] );
			}
		}

		// Handle duplicate vertex buffers if they exist.
		if( m_num_vertex_buffers > 1 )
		{
			p_clone->mp_vertex_buffer[1] = NULL;
			p_clone->CreateDuplicateVertexBuffers( m_num_vertex_buffers - 1 );
		}
	}
	return p_clone;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
IDirect3DVertexBuffer8 *sMesh::AllocateVertexBuffer( uint32 size )
{
	uint8					*p_vb		= new uint8[sizeof( IDirect3DVertexBuffer8 ) + size];
	IDirect3DVertexBuffer8	*p_vb_ret	= (IDirect3DVertexBuffer8*)p_vb;
	
	XGSetVertexBufferHeader( 0, 0, 0, 0, p_vb_ret, 0 );
	p_vb_ret->Register( p_vb + sizeof( IDirect3DVertexBuffer8 ));

	return p_vb_ret;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::Crunch( void )
{
	uint16 *p_indices = mp_index_buffer[0];

	uint32 i0 = p_indices[0];
	uint32 i1 = p_indices[1];

	uint32	invalid			= 0;
	uint32	total_invalid	= 0;
	bool	crunch			= false;

	for( uint32 i = 2; i < m_num_indices[0]; ++i )
	{
		uint32 i2 = p_indices[i];

		if(( i0 == i1 ) || ( i0 == i2 ) || ( i1 == i2 ))
		{
			++invalid;
		}
		else
		{
			if( invalid > 5 )
			{
				if(( invalid & 1 ) == 0 )
				{
//					printf( "Crunching %d indices (even)\n", invalid - 4 );

					// Ensure the leading and trailing degenerate indices are correct.
					p_indices[i - 3]		= p_indices[i - 2];
					p_indices[i - invalid]	= p_indices[i - invalid - 1];

					// With an even number of invalid entries, the wind order won't change during crunch.
					MoveMemory( p_indices + i - invalid + 1, p_indices + i - 3, sizeof( uint16 ) * ( m_num_indices[0] - i + 3 ));

					m_num_indices[0]	-= (uint16)( invalid - 4 );
					i					-= invalid - 4;
				}
				else
				{
//					printf( "Crunching %d indices (odd)\n", invalid - 5 );

					// Ensure the leading and trailing degenerate indices are correct.
					p_indices[i - 3]			= p_indices[i - 2];
					p_indices[i - invalid]		= p_indices[i - invalid - 1];
					p_indices[i - invalid + 1]	= p_indices[i - invalid];

					// With an odd number of invalid entries, the wind order will change during crunch, so use one extra index.
					MoveMemory( p_indices + i - invalid + 2, p_indices + i - 3, sizeof( uint16 ) * ( m_num_indices[0] - i + 3 ));
					m_num_indices[0]	-= (uint16)( invalid - 5 );
					i					-= invalid - 5;
				}
			}
			invalid = 0;
		}
		
		i0 = i1;
		i1 = i2;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::SetBillboardData( uint32 type, Mth::Vector & pivot_pos, Mth::Vector & pivot_axis )
{
	Dbg_Assert( mp_billboard_data == NULL );

	// Create the billboard data.
	mp_billboard_data					= new sBillboardData;

	// Determine the billboard type.
	if( type == 1 )
	{
		mp_billboard_data->m_type = sBillboardData::vBILLBOARD_TYPE_SCREEN_ALIGNED;
	}
	else if( type == 2 )
	{
		// Axial aligned. See if this is y axis.
		if( fabsf( Mth::DotProduct( pivot_axis, Mth::Vector( 0.0f, 1.0f, 0.0f ))) > 0.99f )
		{
			mp_billboard_data->m_type = sBillboardData::vBILLBOARD_TYPE_Y_AXIS_ALIGNED;
		}
		else
		{
			mp_billboard_data->m_type = sBillboardData::vBILLBOARD_TYPE_ARBITRARY_AXIS_ALIGNED;
		}
	}
	else
	{
		Dbg_Assert( 0 );
	}
	
	mp_billboard_data->m_pivot_pos		= pivot_pos;
	if( mp_billboard_data->m_type == sBillboardData::vBILLBOARD_TYPE_ARBITRARY_AXIS_ALIGNED )
	{
		mp_billboard_data->m_pivot_axis	= pivot_axis;
	}

	// We need to go through at a low level and rebuild the vertex buffer. In all cases the
	// mesh won't have been exported with normals, which we use for billboards to store the
	// pivot-relative position, so we need to recalculate the vertex stride.
	Dbg_Assert( m_normal_offset == 0 );
	Dbg_Assert( m_diffuse_offset > 0 );
	Dbg_Assert( m_uv0_offset > 0 );
	Dbg_Assert( m_num_vertices == 4 );
	Dbg_Assert( m_num_indices[0] == 4 );

	// Add size of normal to vertex size.
	int old_vertex_stride = m_vertex_stride;
	int new_vertex_stride = old_vertex_stride + ( sizeof( float ) * 3 );

	IDirect3DVertexBuffer8*	p_old_buffer = mp_vertex_buffer[0];
	IDirect3DVertexBuffer8*	p_new_buffer = AllocateVertexBuffer( new_vertex_stride * 4 );

	// Lock old buffer (read) and new buffer (write).
	BYTE *p_old_vb_data;
	BYTE *p_new_vb_data;
	p_old_buffer->Lock( 0, 0, &p_old_vb_data, D3DLOCK_READONLY | D3DLOCK_NOFLUSH );
	p_new_buffer->Lock( 0, 0, &p_new_vb_data, 0 );

	// Calculate the normal of the billboard, using the first tri.
	float		*p_vert;
	uint16		indices[4];
	indices[0]			= mp_index_buffer[0][0];
	indices[1]			= mp_index_buffer[0][1];
	indices[2]			= mp_index_buffer[0][2];
	indices[3]			= mp_index_buffer[0][3];

	p_vert				= (float*)( p_old_vb_data + ( indices[0] * old_vertex_stride ));
	Mth::Vector v0( p_vert[0], p_vert[1], p_vert[2] );
	p_vert				= (float*)( p_old_vb_data + ( indices[1] * old_vertex_stride ));
	Mth::Vector v1( p_vert[0], p_vert[1], p_vert[2] );
	p_vert				= (float*)( p_old_vb_data + ( indices[2] * old_vertex_stride ));
	Mth::Vector v2( p_vert[0], p_vert[1], p_vert[2] );
	Mth::Vector	normal	= Mth::CrossProduct(( v1 - v0 ), ( v2 - v0 )).Normalize();

	// Given the normal, calculate the local right and up (u and v) vectors, based on the billboard type.
	Mth::Vector u, v;

	switch( mp_billboard_data->m_type )
	{
		case sBillboardData::vBILLBOARD_TYPE_SCREEN_ALIGNED:
		case sBillboardData::vBILLBOARD_TYPE_Y_AXIS_ALIGNED:
		case sBillboardData::vBILLBOARD_TYPE_WORLD_ORIENTED:
		{
			// Use the world 'up' vector to generate the 'u' vector.
			u = Mth::CrossProduct( normal, Mth::Vector( 0.0f, 1.0f, 0.0f )).Normalize();

			// Use the 'u' vector and the normal vector to generate the 'v' vector.
			v = Mth::CrossProduct( u, normal ).Normalize();
			break;
		}
		case sBillboardData::vBILLBOARD_TYPE_ARBITRARY_AXIS_ALIGNED:
		{
			// Use the pivot axis and the normal vector to generate the 'u' vector.
			u = Mth::CrossProduct( normal, pivot_axis ).Normalize();

			// Use the 'u' vector and the normal vector to generate the 'v' vector.
			v = Mth::CrossProduct( u, normal ).Normalize();
			break;
		}
	}

	for( int i = 0; i < 4; ++i )
	{
		// The new position is actually the position of the pivot point for the billboard.
		float *p_pos_old	= (float*)( p_old_vb_data + ( i * old_vertex_stride ));
		float *p_pos_new	= (float*)( p_new_vb_data + ( i * new_vertex_stride ));
		p_pos_new[0]		= pivot_pos[X];
		p_pos_new[1]		= pivot_pos[Y];
		p_pos_new[2]		= pivot_pos[Z];

		// Introduce normal (which is actually the position of the vertex relative to the pivot).
		Mth::Vector pos_relative_to_pivot( p_pos_old[0] - pivot_pos[X], p_pos_old[1] - pivot_pos[Y], p_pos_old[2] - pivot_pos[Z] );

		p_pos_new[3]		= Mth::DotProduct( pos_relative_to_pivot, u );
		p_pos_new[4]		= Mth::DotProduct( pos_relative_to_pivot, v );
		p_pos_new[5]		= Mth::DotProduct( pos_relative_to_pivot, normal );

		// Copy color.
		D3DCOLOR *p_col_old	= (D3DCOLOR*)( p_old_vb_data + ( i * old_vertex_stride ) + m_diffuse_offset );
		D3DCOLOR *p_col_new	= (D3DCOLOR*)( p_new_vb_data + ( i * new_vertex_stride ) + m_diffuse_offset + ( sizeof( float ) * 3 ));
		p_col_new[0]		= p_col_old[0];

		// Copy uv0...
		float *p_uv0_old	= (float*)( p_old_vb_data + ( i * old_vertex_stride ) + m_uv0_offset );
		float *p_uv0_new	= (float*)( p_new_vb_data + ( i * new_vertex_stride ) + m_uv0_offset + ( sizeof( float ) * 3 ));
		p_uv0_new[0]		= p_uv0_old[0];
		p_uv0_new[1]		= p_uv0_old[1];

		// ...and additional uv's if present.
		if(( m_vertex_shader[0] & D3DFVF_TEXCOUNT_MASK ) > D3DFVF_TEX1 )
		{
			p_uv0_new[2]		= p_uv0_old[2];
			p_uv0_new[3]		= p_uv0_old[3];
		}
		if(( m_vertex_shader[0] & D3DFVF_TEXCOUNT_MASK ) > D3DFVF_TEX2 )
		{
			p_uv0_new[4]		= p_uv0_old[4];
			p_uv0_new[5]		= p_uv0_old[5];
		}
		if(( m_vertex_shader[0] & D3DFVF_TEXCOUNT_MASK ) > D3DFVF_TEX3 )
		{
			p_uv0_new[6]		= p_uv0_old[6];
			p_uv0_new[7]		= p_uv0_old[7];
		}
	}

	// Now fix up the mesh. Flag the mesh as being a billboard (stop the mesh being rendered by the regular pathway).
	m_flags |= sMesh::MESH_FLAG_BILLBOARD;

	// Switch vertex buffers, deleting the old one.
	mp_vertex_buffer[0]	= p_new_buffer;
	delete p_old_buffer;

	// Set the new vertex stride, diffuse and uv0 offset (and normal offset, just to be complete).
	m_vertex_stride		= new_vertex_stride;
	m_diffuse_offset	+= sizeof( float ) * 3;
	m_uv0_offset		+= sizeof( float ) * 3;
	m_normal_offset		= sizeof( float ) * 3;

	// Copy the new vertex buffer into existing buffered buffers if m_num_vertex_buffers > 1.
	if( m_num_vertex_buffers > 1 )
	{
		BYTE *p_buffer0;
		BYTE *p_bufferN;
		mp_vertex_buffer[0]->Lock( 0, 0, &p_buffer0, D3DLOCK_READONLY | D3DLOCK_NOFLUSH );
		for( int vb = 1; vb < m_num_vertex_buffers; ++vb )
		{
			delete mp_vertex_buffer[vb];
			mp_vertex_buffer[vb] = AllocateVertexBuffer( new_vertex_stride * 4 );
			mp_vertex_buffer[vb]->Lock( 0, 0, &p_bufferN, 0 );
			CopyMemory( p_bufferN, p_buffer0, new_vertex_stride * 4 );
		}
	}

	// Set the new vertex shader.
	m_vertex_shader[0]	= 999;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::SetBoundingData( Mth::Vector & sphere_center, float radius, Mth::Vector & bb_min, Mth::Vector & bb_max )
{
//	m_bbox.Set( bb_min, bb_max );

	m_sphere_center = D3DXVECTOR3( sphere_center[X], sphere_center[Y], sphere_center[Z] );
	m_sphere_radius = radius;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::Initialize( int				num_vertices,
						float			*p_positions,
						float			*p_normals,
						float			*p_tex_coords,
						int				num_tc_sets,
						DWORD			*p_colors,
						int				num_index_sets,			// How many sets of indices there are (usually 1 set)
						int				*p_num_indices,			// Pointer to an array of ints containing number of indices per set
						uint16			**pp_indices,			// Pointer to an array of pointers to the actual indices
						unsigned long	material_checksum,
						void			*p_scene,
						uint16			*p_matrix_indices,
						uint32			*p_weights,
						char			*p_vc_wibble_anims )
{
	// First thing to do is grab the material pointer for this mesh.
	mp_material	= ((sScene*)p_scene )->GetMaterial( material_checksum );
	if( mp_material == NULL )
	{
		Dbg_Assert( 0 );
		return;
	}
	
	if(( num_index_sets == 0 ) || ( p_num_indices[0] == 0 ))
	{
		return;
	}

	uint16 min_index	= ( pp_indices[0] )[0];
	uint16 max_index	= ( pp_indices[0] )[0];
	for( int i = 0; i < p_num_indices[0]; ++i )
	{
		if(( pp_indices[0] )[i] > max_index )
		{
			max_index = ( pp_indices[0] )[i];
		}
		else if(( pp_indices[0] )[i] < min_index )
		{
			min_index = ( pp_indices[0] )[i];
		}
	}

	if( max_index >= num_vertices )
	{
		// Error!
		Dbg_Assert( 0 );
		return;
	}

	// Grab top-down heap memory for the mesh workspace buffer. This will need to be as big as the maximum vertex indexed.
	int16 *p_mesh_workspace_array = new (Mem::Manager::sHandle().TopDownHeap()) int16[max_index + 1];

	// Setup workspace buffer.
	memset( p_mesh_workspace_array, 1, sizeof( int16 ) * ( max_index + 1 ));
	for( int i = 0; i < p_num_indices[0]; ++i )
	{
		p_mesh_workspace_array[( pp_indices[0] )[i]] = 0;
	}
	
	// Now figure the wasted space.
	int wasted_verts = 0;
	for( int i = min_index; i <= max_index; ++i )
	{
		if( p_mesh_workspace_array[i] != 0 )
			++wasted_verts;
	}

	// Now figure the total number of vertices required for this mesh, to span the min->max indices.
	uint16 vertices_for_this_mesh	= ( max_index - min_index + 1 ) - wasted_verts;
	m_num_vertices					= vertices_for_this_mesh;

	// Create the index buffer(s). (Should be 16byte aligned for best performance).
	for( int ib = 0; ib < num_index_sets; ++ib )
	{
		mp_index_buffer[ib]	= new uint16[p_num_indices[ib]];
		m_num_indices[ib]	= p_num_indices[ib];
	}
	
	// Use the material flags to figure the vertex format.
	int vertex_size			= 3 * sizeof( float );

	// Include weights (for weighted animation) if present.
	uint32 biggest_index_used = 0;
	if( p_weights )
	{
		Dbg_Assert( p_matrix_indices );

		// Calculate the biggest weight used.
		uint32*	p_weight_read	= p_weights + min_index;
		for( int v = min_index; v <= max_index; ++v )
		{
			if( p_mesh_workspace_array[v] == 0 )
			{
				// This vertex is used.
				uint32 w2 = (( p_weight_read[0] >> 22 ) & 0x3FF );
				if( w2 > 0 )
				{
					biggest_index_used = 2;
					break;
				}
				else
				{
					uint32 w1 = (( p_weight_read[0] >> 11 ) & 0x7FF );
					if( w1 > 0 )
					{
						biggest_index_used = 1;
					}
				}
			}
			++p_weight_read;
		}
		vertex_size	+= sizeof( uint32 );
	}

	// Include indices (for weighted animation) if present.
	if( p_matrix_indices )
	{
		Dbg_Assert( p_weights );
		vertex_size	+= sizeof( uint16 ) * 4;
	}
	
	// Texture coordinates.
	uint32	tex_coord_pass	= 0;
	bool	env_mapped		= false;
	if( p_tex_coords )
	{
		for( uint32 pass = 0; pass < mp_material->m_passes; ++pass )
		{
			if( mp_material->m_flags[pass] & MATFLAG_ENVIRONMENT )
			{
				env_mapped		= true;
			}

			// Only need UV's for this stage if it is *not* environment mapped.
			if(( mp_material->mp_tex[pass] ) && ( !( mp_material->m_flags[pass] & MATFLAG_ENVIRONMENT )))
			{
				// Will need uv for this pass and all before it.
				tex_coord_pass	= pass + 1; 
			}
		}
	}
	else
	{
		for( uint32 pass = 0; pass < mp_material->m_passes; ++pass )
		{
			if( mp_material->m_flags[pass] & MATFLAG_ENVIRONMENT )
			{
				env_mapped		= true;
			}
		}
	}

	if( tex_coord_pass > 0 )
	{
		vertex_size			+= 2 * sizeof( float ) * tex_coord_pass;
	}

	// Assume no normals for now, unless weight information indicates an animating mesh.
	bool use_normals		= false;
	bool use_packed_normals	= false;

	if( p_normals || p_weights || env_mapped )
	{
		// Need to include normals. They will be packed differently for weighted meshes.
		use_normals	= true;
		if( p_weights )
		{
			use_packed_normals = true;
			vertex_size	+= sizeof( uint32 );
		}
		else
		{
			vertex_size	+= sizeof( float ) * 3;
		}
	}

	bool use_colors = false;
	if( p_colors )
	{
		// The raw vertex data does contain vertex colors.
		vertex_size	+= sizeof( D3DCOLOR );
		use_colors	= true;
	}


	// Create the vertex buffer.
	m_vertex_stride	= vertex_size;

	// One allocation for the header and the data buffer.	
	mp_vertex_buffer[0] = AllocateVertexBuffer( vertex_size * vertices_for_this_mesh );

	// Lock the vertex buffer.
	BYTE* p_byte;
	if( D3D_OK != mp_vertex_buffer[0]->Lock(	0,					// Offset to lock.
												0,					// Size to lock ( 0 means all).
												&p_byte,			// Pointer to data.
												D3DLOCK_NOFLUSH ))	// Flags.
	{
		Dbg_Assert( 0 );
		return;
	}
	
	// Copy in vertex position data (for vertices that are used).
	uint32		byte_write_offset	= 0;
	float*		p_read				= p_positions + ( min_index * 3 );
	float*		p_write				= (float*)p_byte;

	for( int v = min_index; v <= max_index; ++v )
	{
		if( p_mesh_workspace_array[v] == 0 )
		{
			// This vertex is used.
			p_write[0]	= p_read[0];
			p_write[1]	= p_read[1];
			p_write[2]	= p_read[2];
			p_write	= (float*)((char*)p_write + vertex_size );
		}
		p_read += 3;
	}

	byte_write_offset	+= sizeof( float ) * 3;
	m_vertex_shader[0]	|= D3DFVF_XYZ;

	// Copy in vertex weight data.
	if( p_weights )
	{
		uint32*	p_weight_read	= p_weights + min_index;
		uint32*	p_weight_write	= (uint32*)((char*)p_byte + byte_write_offset );

		for( int v = min_index; v <= max_index; ++v )
		{
			if( p_mesh_workspace_array[v] == 0 )
			{
				// This vertex is used.
				p_weight_write[0]	= p_weight_read[0];
				p_weight_write		= (uint32*)((char*)p_weight_write + vertex_size );
			}
			++p_weight_read;
		}
		byte_write_offset += sizeof( uint32 );

		// No fvf flag setting, since it will be determined at the end.
	}

	// Copy in vertex matrix index data.
	if( p_matrix_indices )
	{
		uint16*	p_index_read	= p_matrix_indices + ( min_index * 4 );
		uint16*	p_index_write	= (uint16*)((char*)p_byte + byte_write_offset );
		for( int v = min_index; v <= max_index; ++v )
		{
			if( p_mesh_workspace_array[v] == 0 )
			{
				// Have to multiply the indices by three to get the correct register offset, since there are 3 registers
				// per matrix.
				p_index_write[0]	= p_index_read[0] * 3;
				p_index_write[1]	= p_index_read[1] * 3;
				p_index_write[2]	= p_index_read[2] * 3;
				p_index_write[3]	= p_index_read[3] * 3;
				p_index_write		= (uint16*)((char*)p_index_write + vertex_size );
			}
			p_index_read += 4;
		}
		byte_write_offset += sizeof( uint16 ) * 4;

		// No fvf flag setting, since it will be determined at the end.
	}

	// Copy in normals data.
	if( use_normals )
	{
		m_normal_offset		= (uint8)byte_write_offset;
		if( use_packed_normals )
		{
			float *p_read	= p_normals + ( min_index * 3 );
			uint32 *p_write	= (uint32*)((char*)p_byte + byte_write_offset );
			for( int v = min_index; v <= max_index; ++v )
			{
				if( p_mesh_workspace_array[v] == 0 )
				{
					// The packed normal format is as follows:
					// 31                                             0
					// |----- 10 -----|----- 11 ------|----- 11 ------|
					// |       z      |       y       |       x       |
					uint32 snx	= Ftoi_ASM( p_read[0] * 1023.0f );
					uint32 sny	= Ftoi_ASM( p_read[1] * 1023.0f );
					uint32 snz	= Ftoi_ASM( p_read[2] * 511.0f );
					p_write[0]	= ( snx & 0x7FF ) | (( sny & 0x7FF ) << 11 ) | (( snz & 0x3FF ) << 22 );
					p_write		= (uint32*)((char*)p_write + vertex_size );
				}
				p_read += 3;
			}
			byte_write_offset += sizeof( uint32 );
		}
		else
		{
			float*	p_read	= p_normals + ( min_index * 3 );
			float*	p_write	= (float*)((char*)p_byte + byte_write_offset );
			for( int v = min_index; v <= max_index; ++v )
			{
				if( p_mesh_workspace_array[v] == 0 )
				{
					p_write[0]	= p_read[0];
					p_write[1]	= p_read[1];
					p_write[2]	= p_read[2];
					p_write		= (float*)((char*)p_write + vertex_size );
				}
				p_read += 3;
			}
			byte_write_offset += sizeof( float ) * 3;
		}
		m_vertex_shader[0]	|= D3DFVF_NORMAL;
	}

	// Copy in vertex color data.
	if( use_colors )
	{
		m_diffuse_offset	= (uint8)byte_write_offset;
		DWORD*	p_col_read	= p_colors + min_index;
		DWORD*	p_col_write	= (DWORD*)((char*)p_byte + byte_write_offset );
		for( int v = min_index; v <= max_index; ++v )
		{
			if( p_mesh_workspace_array[v] == 0 )
			{
				p_col_write[0]	= p_col_read[0];
				p_col_write		= (DWORD*)((char*)p_col_write + vertex_size );
			}
			p_col_read++;
		}
		byte_write_offset += sizeof( DWORD );
		m_vertex_shader[0] |= D3DFVF_DIFFUSE;
	}

	// Copy in vertex texture coordinate data.
	if(( tex_coord_pass > 0 ) && ( p_tex_coords != NULL ))
	{
		m_uv0_offset						= (uint8)byte_write_offset;
		p_read								= p_tex_coords + ( min_index * 2 * num_tc_sets );
		p_write								= (float*)((char*)p_byte + byte_write_offset );
		for( int v = min_index; v <= max_index; ++v )
		{
			if( p_mesh_workspace_array[v] == 0 )
			{
				for( uint32 pass = 0; pass < tex_coord_pass; ++pass )
				{
					p_write[( pass * 2 ) + 0]	= p_read[( pass * 2 ) + 0];
					p_write[( pass * 2 ) + 1]	= p_read[( pass * 2 ) + 1];
				}
				p_write	= (float*)((char*)p_write + vertex_size );
			}
			p_read = p_read + ( num_tc_sets * 2 );
		}
		byte_write_offset	+= sizeof( float ) * 2 * tex_coord_pass;

		switch( tex_coord_pass )
		{
			case 1:
			{
				m_vertex_shader[0]	|= D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 );
				break;
			}
			case 2:
			{
				m_vertex_shader[0]	|= D3DFVF_TEX2 | D3DFVF_TEXCOORDSIZE2( 0 ) | D3DFVF_TEXCOORDSIZE2( 1 );
				break;
			}
			case 3:
			{
				m_vertex_shader[0]	|= D3DFVF_TEX3 | D3DFVF_TEXCOORDSIZE2( 0 ) | D3DFVF_TEXCOORDSIZE2( 1 ) | D3DFVF_TEXCOORDSIZE2( 2 );
				break;
			}
			case 4:
			{
				m_vertex_shader[0]	|= D3DFVF_TEX4 | D3DFVF_TEXCOORDSIZE2( 0 ) | D3DFVF_TEXCOORDSIZE2( 1 ) | D3DFVF_TEXCOORDSIZE2( 2 ) | D3DFVF_TEXCOORDSIZE2( 3 );
				break;
			}
			default:
			{
				Dbg_MsgAssert( 0, ( "Bad number of passes" ));
				break;
			}
		}
	}

	// Create the vertex color wibble array if data is present.
	if( p_vc_wibble_anims )
	{
		mp_vc_wibble_data			= new char[m_num_vertices];
		int vc_wibble_data_offset	= 0;

		for( int v = min_index; v <= max_index; ++v )
		{
			if( p_mesh_workspace_array[v] == 0 )
			{
				mp_vc_wibble_data[vc_wibble_data_offset++] = p_vc_wibble_anims[v];
			}
		}
		// Double buffer the vertex buffer.
		CreateDuplicateVertexBuffers( 1 );
	}

	// Process the workspace array.
	int offset = 0;
	for( int v = 0; v <= max_index; ++v )
	{
		if( p_mesh_workspace_array[v] == 0 )
		{
			// This vertex is used.
			p_mesh_workspace_array[v] = offset;
		}
		else
		{
			// This vertex is not used. Update the offset for the next used vertex.
			--offset;
		}
	}
	
	// Copy in index data, normalising the indices for this vertex buffer (i.e. so the lowest index will reference
	// vertex 0 in the buffer built specifically for this mesh).
	for( int ib = 0; ib < num_index_sets; ++ib )
	{
		for( int i = 0; i < p_num_indices[ib]; ++i )
		{
			uint16 idx				= ( pp_indices[ib] )[i];
			mp_index_buffer[ib][i]	= idx + p_mesh_workspace_array[idx];
		}
	}
	
	// Can now remove the mesh workspace array.
	delete [] p_mesh_workspace_array;
	
	// Set the correct vertex shader if a weighted mesh.
	// The number of indices used will be one more than the biggest index used (given 0 base).
	if( p_weights )
	{
		m_vertex_shader[0] = GetVertexShader( use_colors, ( mp_material->m_flags[0] & MATFLAG_SPECULAR ) ? true : false, biggest_index_used + 1 );
	}

	// Set the pixel shader regardless.
	GetPixelShader( mp_material, &m_pixel_shader );

	if( num_index_sets > 1 )
	{
		mp_index_lod_data = new float[num_index_sets];
		for( int d = 0; d < num_index_sets; ++d )
		{
			float dist				= ( 15.0f + ( d * 10.0f )) * 12.0f;
			mp_index_lod_data[d]	= dist;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sMesh::DrawBoundingSphere( void )
{
#	ifdef __NOPT_ASSERT__
	const uint32 NUM_SPHERE_POINTS	= 64;

	static uint32 sphere_buffer[NUM_SPHERE_POINTS * 4];

	D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;
	Mth::Vector up( 0.0f, 1.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at and world 'up'.
	Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

	screen_right.Normalize();
	screen_up.Normalize();

	set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
	set_pixel_shader( PixelShader5 );

	int		index		= 0;
	float	angle		= 0.0f;
	float	angle_step	= ( Mth::PI * 2.0f ) / (float)NUM_SPHERE_POINTS;

	// Draw the screen aligned sphere.
	for( uint32 i = 0; i < NUM_SPHERE_POINTS; ++i, angle += angle_step )
	{
		float x = m_sphere_center.x + ( m_sphere_radius * cosf( angle ) * screen_right[X] ) + ( m_sphere_radius * sinf( angle ) * screen_up[X] );
		float y = m_sphere_center.y + ( m_sphere_radius * cosf( angle ) * screen_right[Y] ) + ( m_sphere_radius * sinf( angle ) * screen_up[Y] );
		float z = m_sphere_center.z + ( m_sphere_radius * cosf( angle ) * screen_right[Z] ) + ( m_sphere_radius * sinf( angle ) * screen_up[Z] );

		sphere_buffer[index++]	= *((uint32*)&x );
		sphere_buffer[index++]	= *((uint32*)&y );
		sphere_buffer[index++]	= *((uint32*)&z );
		sphere_buffer[index++]	= 0x80800000;
	}
	D3DDevice_DrawVerticesUP( D3DPT_LINELOOP, NUM_SPHERE_POINTS, sphere_buffer, sizeof( uint32 ) * 4 );

	// Draw the xz plane sphere.
	index		= 0;
	angle		= 0.0f;
	for( uint32 i = 0; i < NUM_SPHERE_POINTS; ++i, angle += angle_step )
	{
		float x = m_sphere_center.x + ( m_sphere_radius * cosf( angle ));
		float y = m_sphere_center.y;
		float z = m_sphere_center.z + ( m_sphere_radius * sinf( angle ));

		sphere_buffer[index++]	= *((uint32*)&x );
		sphere_buffer[index++]	= *((uint32*)&y );
		sphere_buffer[index++]	= *((uint32*)&z );
		sphere_buffer[index++]	= 0x80800000;
	}
	D3DDevice_DrawVerticesUP( D3DPT_LINELOOP, NUM_SPHERE_POINTS, sphere_buffer, sizeof( uint32 ) * 4 );
#	endif
}





} // namespace NxXbox




================================================
FILE: Code/Gfx/XBox/NX/mesh.h
================================================
#ifndef __MESH_H
#define __MESH_H

#include 
#include 
#include 
#include "material.h"

namespace NxXbox
{

struct sCASData
{
	uint32	mask;
	uint32	data0;
	uint32	data1;
	uint32	start_index;
};
	


struct sBillboardData
{
	enum EBillboardType
	{
		vBILLBOARD_TYPE_SCREEN_ALIGNED			= 0,
		vBILLBOARD_TYPE_Y_AXIS_ALIGNED			= 1,		// Specialised case of arbitrary axial alignment.
		vBILLBOARD_TYPE_ARBITRARY_AXIS_ALIGNED	= 2,
		vBILLBOARD_TYPE_WORLD_ORIENTED			= 3
	};
	
	EBillboardType	m_type;
	Mth::Vector		m_pivot_pos;
	Mth::Vector		m_pivot_axis;							// Normalised axis of rotation, valid only for type ARBITRARY_AXIS_ALIGNED.
};



struct sMesh
{
public:

	static const uint32	VERTEX_SHADER_STACK_SIZE	= 2;
	static const uint32	MAX_VERTEX_BUFFERS			= 3;
	static const uint32	MAX_INDEX_BUFFERS			= 8;		// Multiple index buffers are used for triangle-decimated LOD's.
	
	enum EMeshFlags
	{
		MESH_FLAG_IS_INSTANCE				= 0x01,
		MESH_FLAG_NO_SKATER_SHADOW			= 0x02,
		MESH_FLAG_MATERIAL_COLOR_OVERRIDE	= 0x04,
		MESH_FLAG_VERTEX_COLOR_WIBBLE		= 0x08,
		MESH_FLAG_BILLBOARD					= 0x10,		// This mesh is a billboard.
		MESH_FLAG_HAS_TRANSFORM				= 0x20,
		MESH_FLAG_ACTIVE					= 0x40,
		MESH_FLAG_NO_ANISOTROPIC			= 0x80,		// No texture 0 anisotropic filtering for this mesh.
		MESH_FLAG_NO_ZWRITE					= 0x100,	// No zwrite for this mesh.
		MESH_FLAG_SHADOW_VOLUME				= 0x200,	// This mesh represents a single shadow volume.
		MESH_FLAG_BUMPED_WATER				= 0x400,
		MESH_FLAG_UNLIT						= 0x20000	// This corresponds to the material unlit flag during the scene conversion process.
	};

					sMesh( void );
					~sMesh( void );

	// Functions
	void			wibble_vc( void );
	void			wibble_normals( void );
	uint32			GetChecksum()	const			{ return Checksum; }
	uint32			GetFlags()		const			{ return m_flags; }
	void			SetActive( bool active )		{ if( active ) m_flags |= MESH_FLAG_ACTIVE; else m_flags &= ~MESH_FLAG_ACTIVE; }
	void			SetVisibility( uint8 mask )		{ m_visibility_mask	= mask; }
	void			SetPosition( Mth::Vector &pos );
	void			GetPosition( Mth::Vector *p_pos );
	void			SetYRotation( Mth::ERot90 rot );
	sMesh			*Clone( bool instance = false );
	void			SetBoneIndex( int8 idx )		{ m_bone_index = idx; }
	int8			GetBoneIndex( void )			{ return m_bone_index; }
	void			SetBillboardData( uint32 type, Mth::Vector & pivot_pos, Mth::Vector & pivot_axis );
	void			SetBoundingData( Mth::Vector & sphere_center, float radius, Mth::Vector & bb_min, Mth::Vector & bb_max );

	// Grabs memory chunk and builds vertex buffer from heap memory, rather than getting DX to do it.
	static IDirect3DVertexBuffer8	*AllocateVertexBuffer( uint32 size );

	// All-purpose mesh instancing code, used for static geometry and animating weighted meshes.
	void			Initialize( int				num_vertices,
								float			*p_positions,
								float			*p_normals,
								float			*p_tex_coords,
								int				num_tc_sets,
								DWORD			*p_colors,
								int				num_index_sets,			// How many sets of indices there are (usually 1 set)
								int				*p_num_indices,			// Pointer to an array of ints containing number of indices per set
								uint16			**pp_indices,			// Pointer to an array of pointers to the actual indices
								unsigned long	material_checksum,
								void			*p_scene,
								uint16			*p_matrix_indices	= NULL,
								uint32			*p_weights			= NULL,
								char			*p_vc_wibble_anims	= NULL );

	void			Submit( void );
	void			HandleColorOverride( void );
	void			CreateDuplicateVertexBuffers( int n );
	void			SwapVertexBuffers( void );
	void			PushVertexShader( uint32 shader_id );
	void			PopVertexShader( void );
	void			Crunch( void );

	// Debug functions, will be empty stub for Final build.
	void			DrawBoundingSphere( void );

	// Members. Order is important here since details required for fast mesh rejection need to be in top 32 bytes of structure.
	uint32					m_flags;
	D3DXVECTOR3				m_sphere_center;
	float					m_sphere_radius;
	sMaterial				*mp_material;

	uint8					m_vertex_stride;
	uint8					m_current_write_vertex_buffer;
	uint8					m_num_vertex_buffers;
	uint8					m_visibility_mask;
	uint8					m_diffuse_offset;		// Offset into vertex format for diffuse color component.
	uint8					m_normal_offset;		// Offset into vertex format for normal component.
	uint8					m_uv0_offset;			// Offset into vertex format for uv0 component.
	int8					m_bone_index;

	uint16					m_load_order;
	uint16					m_num_vertices;
	uint16					m_num_indices[MAX_INDEX_BUFFERS];

	uint32					Checksum;
	uint32					m_vertex_shader[VERTEX_SHADER_STACK_SIZE];
	uint32					m_pixel_shader;

	float					*mp_index_lod_data;				// List of distances (squared) for which a particular index list should be used.
															// NULL for meshes that only have one set of index data.

	sBillboardData			*mp_billboard_data;				// Data defining billboard properties. NULL for non-billboard meshes.

	D3DPRIMITIVETYPE		m_primitive_type;
	uint16					*mp_index_buffer[MAX_INDEX_BUFFERS];
	IDirect3DVertexBuffer8*	mp_vertex_buffer[MAX_VERTEX_BUFFERS];

	float					m_bounding_sphere_nearest_z;	// Used for dynamic sorting during front-back block sorting.
	float					m_material_color_override[3];
	char					*mp_vc_wibble_data;
	Mth::Matrix				*mp_transform;
};


void SetMeshScalingParameters( Nx::SMeshScalingParameters* pParams );
void DisableMeshScaling( void );
void ApplyMeshScaling( float* p_vertices, int num_verts );


} // namespace NxXbox

#endif // __MESH_H



================================================
FILE: Code/Gfx/XBox/NX/mipmap.inl
================================================
// Xbox Shader Assembler 1.00.4705.1
D3DPIXELSHADERDEF psd;
ZeroMemory(&psd, sizeof(psd));
psd.PSCombinerCount=PS_COMBINERCOUNT(
    8,
    PS_COMBINERCOUNT_MUX_MSB | PS_COMBINERCOUNT_UNIQUE_C0 | PS_COMBINERCOUNT_UNIQUE_C1);
psd.PSTextureModes=PS_TEXTUREMODES(
    PS_TEXTUREMODES_PROJECT2D,
    PS_TEXTUREMODES_PROJECT2D,
    PS_TEXTUREMODES_PROJECT2D,
    PS_TEXTUREMODES_PROJECT2D);
psd.PSInputTexture=PS_INPUTTEXTURE(0,0,0,0);
psd.PSDotMapping=PS_DOTMAPPING(
    0,
    PS_DOTMAPPING_ZERO_TO_ONE,
    PS_DOTMAPPING_ZERO_TO_ONE,
    PS_DOTMAPPING_ZERO_TO_ONE);
psd.PSCompareMode=PS_COMPAREMODE(
    PS_COMPAREMODE_S_LT | PS_COMPAREMODE_T_LT | PS_COMPAREMODE_R_LT | PS_COMPAREMODE_Q_LT,
    PS_COMPAREMODE_S_LT | PS_COMPAREMODE_T_LT | PS_COMPAREMODE_R_LT | PS_COMPAREMODE_Q_LT,
    PS_COMPAREMODE_S_LT | PS_COMPAREMODE_T_LT | PS_COMPAREMODE_R_LT | PS_COMPAREMODE_Q_LT,
    PS_COMPAREMODE_S_LT | PS_COMPAREMODE_T_LT | PS_COMPAREMODE_R_LT | PS_COMPAREMODE_Q_LT);

//------------- Stage 0 -------------
psd.PSRGBInputs[0]=PS_COMBINERINPUTS(
    PS_REGISTER_T0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_ZERO | PS_CHANNEL_RGB | PS_INPUTMAPPING_UNSIGNED_IDENTITY,
    PS_REGISTER_ZERO | PS_CHANNEL_RGB | PS_INPUTMAPPING_UNSIGNED_IDENTITY);
psd.PSAlphaInputs[0]=PS_COMBINERINPUTS(
    PS_REGISTER_T0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_ZERO | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_UNSIGNED_IDENTITY,
    PS_REGISTER_ZERO | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_UNSIGNED_IDENTITY);
psd.PSRGBOutputs[0]=PS_COMBINEROUTPUTS(
    PS_REGISTER_R0,
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSAlphaOutputs[0]=PS_COMBINEROUTPUTS(
    PS_REGISTER_R0,
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSConstant0[0] = 0x40404040;
psd.PSConstant1[0] = 0x00000000;

//------------- Stage 1 -------------
psd.PSRGBInputs[1]=PS_COMBINERINPUTS(
    PS_REGISTER_T1 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_ZERO | PS_CHANNEL_RGB | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSAlphaInputs[1]=PS_COMBINERINPUTS(
    PS_REGISTER_T1 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_ZERO | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSRGBOutputs[1]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSAlphaOutputs[1]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSConstant0[1] = 0x40404040;
psd.PSConstant1[1] = 0x00000000;

//------------- Stage 2 -------------
psd.PSRGBInputs[2]=PS_COMBINERINPUTS(
    PS_REGISTER_T2 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_ZERO | PS_CHANNEL_RGB | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSAlphaInputs[2]=PS_COMBINERINPUTS(
    PS_REGISTER_T2 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_ZERO | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSRGBOutputs[2]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSAlphaOutputs[2]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSConstant0[2] = 0x40404040;
psd.PSConstant1[2] = 0x00000000;

//------------- Stage 3 -------------
psd.PSRGBInputs[3]=PS_COMBINERINPUTS(
    PS_REGISTER_T3 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_ZERO | PS_CHANNEL_RGB | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSAlphaInputs[3]=PS_COMBINERINPUTS(
    PS_REGISTER_T3 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_ZERO | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSRGBOutputs[3]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSAlphaOutputs[3]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSConstant0[3] = 0x40404040;
psd.PSConstant1[3] = 0x00000000;

//------------- Stage 4 -------------
psd.PSRGBInputs[4]=PS_COMBINERINPUTS(
    PS_REGISTER_T0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_NEGATE,
    PS_REGISTER_ZERO | PS_CHANNEL_RGB | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSAlphaInputs[4]=PS_COMBINERINPUTS(
    PS_REGISTER_T0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_NEGATE,
    PS_REGISTER_ZERO | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSRGBOutputs[4]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSAlphaOutputs[4]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSConstant0[4] = 0x00000000;
psd.PSConstant1[4] = 0x00000000;

//------------- Stage 5 -------------
psd.PSRGBInputs[5]=PS_COMBINERINPUTS(
    PS_REGISTER_T1 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_NEGATE,
    PS_REGISTER_ZERO | PS_CHANNEL_RGB | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSAlphaInputs[5]=PS_COMBINERINPUTS(
    PS_REGISTER_T1 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_NEGATE,
    PS_REGISTER_ZERO | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSRGBOutputs[5]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSAlphaOutputs[5]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSConstant0[5] = 0x00000000;
psd.PSConstant1[5] = 0x00000000;

//------------- Stage 6 -------------
psd.PSRGBInputs[6]=PS_COMBINERINPUTS(
    PS_REGISTER_T2 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_NEGATE,
    PS_REGISTER_ZERO | PS_CHANNEL_RGB | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSAlphaInputs[6]=PS_COMBINERINPUTS(
    PS_REGISTER_T2 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_NEGATE,
    PS_REGISTER_ZERO | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSRGBOutputs[6]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSAlphaOutputs[6]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSConstant0[6] = 0x00000000;
psd.PSConstant1[6] = 0x00000000;

//------------- Stage 7 -------------
psd.PSRGBInputs[7]=PS_COMBINERINPUTS(
    PS_REGISTER_T3 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_NEGATE,
    PS_REGISTER_ZERO | PS_CHANNEL_RGB | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_RGB | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSAlphaInputs[7]=PS_COMBINERINPUTS(
    PS_REGISTER_T3 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
    PS_REGISTER_C0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_NEGATE,
    PS_REGISTER_ZERO | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_UNSIGNED_INVERT,
    PS_REGISTER_R0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY);
psd.PSRGBOutputs[7]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSAlphaOutputs[7]=PS_COMBINEROUTPUTS(
    PS_REGISTER_DISCARD,
    PS_REGISTER_DISCARD,
    PS_REGISTER_R0,
    PS_COMBINEROUTPUT_IDENTITY | PS_COMBINEROUTPUT_AB_MULTIPLY | PS_COMBINEROUTPUT_CD_MULTIPLY | PS_COMBINEROUTPUT_AB_CD_SUM);
psd.PSConstant0[7] = 0x00000000;
psd.PSConstant1[7] = 0x00000000;

psd.PSC0Mapping = 0x76543210;
psd.PSC1Mapping = 0xffffffff;
psd.PSFinalCombinerConstants = 0x000001ff;


================================================
FILE: Code/Gfx/XBox/NX/nx_init.cpp
================================================
#include 

// Include the following two files for detailed timing data collection.
//#include 
//#include 

#include "sys/config/config.h"
#include "nx_init.h"
#include "anim.h"
#include "chars.h"
#include "scene.h"
#include "render.h"
#include "instance.h"
#include "gamma.h"
#include "grass.h"

namespace NxXbox
{

sEngineGlobals	EngineGlobals;


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void InitialiseRenderstates( void )
{
	D3DDevice_SetRenderState( D3DRS_LOCALVIEWER,		FALSE );

	D3DDevice_SetRenderState( D3DRS_COLORVERTEX,		FALSE );

	EngineGlobals.lighting_enabled		= false;
	D3DDevice_SetRenderState( D3DRS_LIGHTING,			FALSE );

	EngineGlobals.specular_enabled		= 0;
	D3DDevice_SetRenderState( D3DRS_SPECULARENABLE,		FALSE );
	
	EngineGlobals.cull_mode				= D3DCULL_CW;
	D3DDevice_SetRenderState( D3DRS_CULLMODE,			D3DCULL_CW );

	EngineGlobals.allow_envmapping		= true;

	EngineGlobals.dither_enable			= TRUE;
	D3DDevice_SetRenderState( D3DRS_DITHERENABLE,		TRUE );

	EngineGlobals.z_test_enabled		= TRUE;
	D3DDevice_SetRenderState( D3DRS_ZFUNC,				D3DCMP_LESSEQUAL );

	EngineGlobals.z_write_enabled		= TRUE;
	D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE,		TRUE );
	
	EngineGlobals.alpha_blend_enable	= 1;
	D3DDevice_SetRenderState( D3DRS_ALPHABLENDENABLE,	TRUE );

	EngineGlobals.alpha_test_enable		= 1;
	D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE,	TRUE );

	D3DDevice_SetRenderState( D3DRS_ALPHAFUNC,			D3DCMP_GREATEREQUAL );

	EngineGlobals.alpha_ref				= 0;
	D3DDevice_SetRenderState( D3DRS_ALPHAREF,			0x00 );

	for( int stage = 0; stage < 4; ++stage )
	{
		EngineGlobals.uv_addressing[stage]		= 0x00000000UL;
		EngineGlobals.mip_map_lod_bias[stage]	= 0x00000000UL;
		EngineGlobals.p_texture[stage]			= NULL;
		EngineGlobals.min_mag_filter[stage]		= 0x00010000UL;
		EngineGlobals.color_sign[stage]			= 0x00000000UL;

		D3DDevice_SetTextureStageState( stage, D3DTSS_MAGFILTER,		D3DTEXF_LINEAR );
		D3DDevice_SetTextureStageState( stage, D3DTSS_MIPFILTER,		D3DTEXF_LINEAR );

		if( stage == 0 )
		{
			// If we are running in 720p or 1080i mode, need max pixel pushing power - avoid anisotropic filtering.
			// MSM PERFCHANGE
			if( EngineGlobals.backbuffer_height > 480 )
			{
				D3DDevice_SetTextureStageState( stage, D3DTSS_MINFILTER,		D3DTEXF_LINEAR );
			}
			else
			{
				D3DDevice_SetTextureStageState( stage, D3DTSS_MINFILTER,		D3DTEXF_ANISOTROPIC );
				D3DDevice_SetTextureStageState( stage, D3DTSS_MAXANISOTROPY, 3 );
			}
		}
		else
		{
			D3DDevice_SetTextureStageState( stage, D3DTSS_MINFILTER,		D3DTEXF_LINEAR );
		}
		D3DDevice_SetTextureStageState( stage, D3DTSS_ADDRESSU,			D3DTADDRESS_WRAP );
		D3DDevice_SetTextureStageState( stage, D3DTSS_ADDRESSV,			D3DTADDRESS_WRAP );
	}

	// Set up material for specular properties for fixed function pipeline.
	D3DMATERIAL8	test_mat;
	ZeroMemory( &test_mat, sizeof( D3DMATERIAL8 ));
	D3DDevice_SetMaterial( &test_mat );

	D3DDevice_SetRenderState( D3DRS_DIFFUSEMATERIALSOURCE,	D3DMCS_COLOR1 );
    D3DDevice_SetRenderState( D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL );
    D3DDevice_SetRenderState( D3DRS_AMBIENTMATERIALSOURCE,	D3DMCS_COLOR1 );
    D3DDevice_SetRenderState( D3DRS_EMISSIVEMATERIALSOURCE,	D3DMCS_MATERIAL );

	// Set these up so they will get reset first time through.
	EngineGlobals.blend_mode_value			= 0xDEADBABE;
	EngineGlobals.blend_op					= 0xDEADBABE;
	EngineGlobals.src_blend					= 0xDEADBABE;
	EngineGlobals.dest_blend				= 0xDEADBABE;

	EngineGlobals.screenshot_name[0]		= 0;
	
	// Build the required vertex shaders.
	CreateWeightedMeshVertexShaders();

	// Build required pixel shaders.
	create_pixel_shaders();

	// Set pixel shader constants.
	EngineGlobals.pixel_shader_constants[16]	= 0.0f;
	EngineGlobals.pixel_shader_constants[17]	= 0.5f;
	EngineGlobals.pixel_shader_constants[18]	= 0.0f;	// Fog denisty.
	EngineGlobals.pixel_shader_constants[19]	= 0.5f;	// Constant 0.5

	EngineGlobals.pixel_shader_override			= 0;
	EngineGlobals.pixel_shader_id				= 0;
	EngineGlobals.upload_pixel_shader_constants = false;
	EngineGlobals.is_orthographic				= false;
	EngineGlobals.custom_pipeline_enabled		= false;
	EngineGlobals.vertex_shader_override		= 0;
	EngineGlobals.texture_stage_override		= 0;
	EngineGlobals.material_override				= 0;
	EngineGlobals.blend_mode_override			= 0;

	EngineGlobals.clear_color_buffer			= true;
	EngineGlobals.clear_color					= 0x00506070;
	EngineGlobals.letterbox_active				= false;

	// Set default directional lights
	EngineGlobals.directional_light_color[0]	= -0.5f;		// Dir0
	EngineGlobals.directional_light_color[1]	= -0.8660254f;
	EngineGlobals.directional_light_color[2]	= 0.0f;
	EngineGlobals.directional_light_color[3]	= 0.0f;
	EngineGlobals.directional_light_color[4]	= 0.586f;		// Col0
	EngineGlobals.directional_light_color[5]	= 0.586f;
	EngineGlobals.directional_light_color[6]	= 0.586f;
	EngineGlobals.directional_light_color[7]	= 1.0f;
	
	EngineGlobals.directional_light_color[8]	= 1.0f;			// Dir1
	EngineGlobals.directional_light_color[9]	= 0.0f;
	EngineGlobals.directional_light_color[10]	= 0.0f;
	EngineGlobals.directional_light_color[11]	= 0.0f;
	EngineGlobals.directional_light_color[12]	= 0.0f;			// Col1
	EngineGlobals.directional_light_color[13]	= 0.0f;
	EngineGlobals.directional_light_color[14]	= 0.0f;
	EngineGlobals.directional_light_color[15]	= 1.0f;
	
	EngineGlobals.directional_light_color[16]	= 1.0f;			// Dir2
	EngineGlobals.directional_light_color[17]	= 0.0f;
	EngineGlobals.directional_light_color[18]	= 0.0f;
	EngineGlobals.directional_light_color[19]	= 0.0f;
	EngineGlobals.directional_light_color[20]	= 0.0f;			// Col2
	EngineGlobals.directional_light_color[21]	= 0.0f;
	EngineGlobals.directional_light_color[22]	= 0.0f;
	EngineGlobals.directional_light_color[23]	= 1.0f;

	// Set default ambient light.
	EngineGlobals.ambient_light_color[0]		= 0.5865f;
	EngineGlobals.ambient_light_color[1]		= 0.5865f;
	EngineGlobals.ambient_light_color[2]		= 0.5865f;
	EngineGlobals.ambient_light_color[3]		= 1.0f;
 
	EngineGlobals.fog_enabled					= false;
	EngineGlobals.fog_color						= 0x00000000UL;
	EngineGlobals.fog_start						= FEET_TO_INCHES( -20.0f );
	EngineGlobals.fog_end						= FEET_TO_INCHES( -2050.0f );
	
	D3DDevice_SetRenderState( D3DRS_FOGENABLE,		EngineGlobals.fog_enabled );
	D3DDevice_SetRenderState( D3DRS_FOGTABLEMODE,	D3DFOG_LINEAR );
//	D3DDevice_SetRenderState( D3DRS_FOGTABLEMODE,	D3DFOG_EXP );
//	D3DDevice_SetRenderState( D3DRS_FOGSTART,		*((DWORD*)( &EngineGlobals.fog_start )));
//	D3DDevice_SetRenderState( D3DRS_FOGEND,			*((DWORD*)( &EngineGlobals.fog_end )));
	D3DDevice_SetRenderState( D3DRS_FOGCOLOR,		EngineGlobals.fog_color );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void InitialiseEngine( void )
{
	D3DPRESENT_PARAMETERS   params;
	DWORD					video_flags = XGetVideoFlags();

	EngineGlobals.loadingbar_timer_event	= 0;
	
	// Setup default values for the screen conversion macro.
	EngineGlobals.screen_conv_x_multiplier	= 640.0f / 640.0f;
	EngineGlobals.screen_conv_y_multiplier	= 480.0f / 480.0f;
	EngineGlobals.screen_conv_x_offset		= 0;
	EngineGlobals.screen_conv_y_offset		= 16;
	
	ZeroMemory( ¶ms, sizeof( D3DPRESENT_PARAMETERS ));

	// This setting required for any multisample presentation.
	params.SwapEffect						= D3DSWAPEFFECT_DISCARD;

	// Let D3D create the depth-stencil buffer for us.
	params.EnableAutoDepthStencil			= TRUE;

	// Select default refresh rate and presentation interval. Note: When we switch to the December SDK
	// we can use the ONE_OR_IMMEDIATE value (if the tearing looks okay).
	if(( Config::GetDisplayType() == Config::DISPLAY_TYPE_PAL ) && ( Config::FPS() == 60 ))
	{
		// PAL 60Hz has been selected - need to set this refresh rate explicitly.
		params.FullScreen_RefreshRateInHz	= 60;
	}
	else
	{
		params.FullScreen_RefreshRateInHz	= D3DPRESENT_RATE_DEFAULT;
	}
//	params.FullScreen_PresentationInterval	= D3DPRESENT_INTERVAL_ONE;
	params.FullScreen_PresentationInterval	= D3DPRESENT_INTERVAL_ONE_OR_IMMEDIATE;
//	params.FullScreen_PresentationInterval	= D3DPRESENT_INTERVAL_IMMEDIATE;

	// Set up the back buffer format.
	params.BackBufferCount					= 1;
	params.BackBufferWidth					= 640;
	params.BackBufferHeight					= 480;
	params.BackBufferFormat					= D3DFMT_LIN_X8R8G8B8;

	// Set up the Z-stencil buffer format and multisample format.
	params.AutoDepthStencilFormat			= D3DFMT_D24S8;
//	params.MultiSampleType					= D3DMULTISAMPLE_NONE;
	params.MultiSampleType					= D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR;
//	params.MultiSampleType					= D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_QUINCUNX;
//	params.MultiSampleType					= D3DMULTISAMPLE_4_SAMPLES_SUPERSAMPLE_LINEAR;

	// Set flag for widescreen where appropriate.
	if( video_flags & XC_VIDEO_FLAGS_WIDESCREEN )
	{
		params.Flags			|= D3DPRESENTFLAG_WIDESCREEN;

		// Optionally set up 720480 back buffer.
		// Set up 16:9 projection transform.
	}

	
	// Set flag for progrssive scan where appropriate.
	if( video_flags & XC_VIDEO_FLAGS_HDTV_720p )
	{
		params.Flags			|= D3DPRESENTFLAG_PROGRESSIVE | D3DPRESENTFLAG_WIDESCREEN;
		params.BackBufferWidth	= 1280;
		params.BackBufferHeight	= 720;

		// Turn off FSAA.
		params.MultiSampleType	= D3DMULTISAMPLE_NONE;

		EngineGlobals.screen_conv_x_multiplier	= 1280.0f / 704.0f;
		EngineGlobals.screen_conv_y_multiplier	= 720.0f / 480.0f;
		EngineGlobals.screen_conv_x_offset		= 32;
		EngineGlobals.screen_conv_y_offset		= 32;
	}
	else if( video_flags & XC_VIDEO_FLAGS_HDTV_480p )
	{
		params.Flags			|= D3DPRESENTFLAG_PROGRESSIVE;
	}
//	else if( video_flags & XC_VIDEO_FLAGS_HDTV_1080i )
//	{
//		params.Flags							|= D3DPRESENTFLAG_INTERLACED | D3DPRESENTFLAG_WIDESCREEN | D3DPRESENTFLAG_FIELD;
//		params.BackBufferWidth					= 1920;
//		params.BackBufferHeight					= 540;
//		params.BackBufferFormat					= D3DFMT_LIN_R5G6B5;
//		params.AutoDepthStencilFormat			= D3DFMT_D16;
//		params.FullScreen_PresentationInterval	= D3DPRESENT_INTERVAL_TWO;
//
//		// Turn off FSAA.
//		params.MultiSampleType	= D3DMULTISAMPLE_NONE;
//
//		EngineGlobals.screen_conv_x_multiplier	= 1920.0f / 704.0f;
//		EngineGlobals.screen_conv_y_multiplier	= 1080.0f / 480.0f;
//		EngineGlobals.screen_conv_x_offset		= 32;
//		EngineGlobals.screen_conv_y_offset		= 16;
//	}
	else
	{
		params.Flags			|= D3DPRESENTFLAG_INTERLACED;
	}
	
	if( params.BackBufferWidth == 640 )
	{
		params.Flags			|= D3DPRESENTFLAG_10X11PIXELASPECTRATIO;
	}
	
	// The default push buffer size is 512k. Double this to reduce stalls from filling the push buffer.
	Direct3D_SetPushBufferSize( 2 * 512 * 1024, 32 * 1024 );
	
	if( D3D_OK != Direct3D_CreateDevice(	D3DADAPTER_DEFAULT,
											D3DDEVTYPE_HAL,
											NULL,
											D3DCREATE_HARDWARE_VERTEXPROCESSING,	// Note: may want to consider adding the PUREDEVICE flag here also.
											¶ms,
											&EngineGlobals.p_Device ))
	{
		// Failed to start up engine. Bad!
		exit( 0 );
	}

	// Also create the render surface we will use when doing screen blur. (Creating this at 32bit depth also, since still
	// takes up less memory than 720p buffers).
	if( params.BackBufferWidth <= 640 )
	{
		D3DDevice_CreateRenderTarget( 640, 480, D3DFMT_LIN_X8R8G8B8, 0, 0, &EngineGlobals.p_BlurSurface[0] );
		D3DDevice_CreateRenderTarget( 320, 240, D3DFMT_LIN_X8R8G8B8, 0, 0, &EngineGlobals.p_BlurSurface[1] );
		D3DDevice_CreateRenderTarget( 160, 120, D3DFMT_LIN_X8R8G8B8, 0, 0, &EngineGlobals.p_BlurSurface[2] );
		D3DDevice_CreateRenderTarget(  80,  60, D3DFMT_LIN_X8R8G8B8, 0, 0, &EngineGlobals.p_BlurSurface[3] );
	}
	
	// Obtain pointers to the render and Z-stencil surfaces. Doing this increases their reference counts, so release
	// them following the operation.
	D3DDevice_GetRenderTarget( &EngineGlobals.p_RenderSurface );
	D3DDevice_GetDepthStencilSurface( &EngineGlobals.p_ZStencilSurface );

	LPDIRECT3DSURFACE8 pBackBuffer;
    D3DDevice_GetBackBuffer( 0, 0, &pBackBuffer );
	
	// Get back buffer information.
	EngineGlobals.backbuffer_width	= params.BackBufferWidth;
	EngineGlobals.backbuffer_height	= params.BackBufferHeight;
	EngineGlobals.backbuffer_format	= params.BackBufferFormat;
	EngineGlobals.zstencil_depth	= ( params.AutoDepthStencilFormat == D3DFMT_D16 ) ? 16 : 32;
	
	// Get blur buffer information.
	if( EngineGlobals.p_BlurSurface[0] )
	{
		D3DSURFACE_DESC blur_surface_desc;
		EngineGlobals.p_BlurSurface[0]->GetDesc( &blur_surface_desc );
		EngineGlobals.blurbuffer_format	= blur_surface_desc.Format;
	}
	
	// Set our renderstate to a known state.
	InitialiseRenderstates();

	// Set default gamma values.
	SetGammaNormalized( 0.14f, 0.13f, 0.12f );

	// Initialise the memory resident font, used for fatal i/o error messages etc.
	EngineGlobals.p_memory_resident_font = InitialiseMemoryResidentFont();

	// Code to enable detailed timing. Need to link with d3d8i.lib.
//	DmEnableGPUCounter( TRUE );
//	D3DPERF_SetShowFrameRateInterval( 1000 );
//	D3DPERF_GetStatistics()->m_dwDumpFPSInfoMask |= D3DPERF_DUMP_FPS_PERFPROFILE;

	// Now that the D3DDevice is created, it's safe to install vsync handlers.
	Tmr::InstallVSyncHandlers();

	// Load up the bump textures.
//	LoadBumpTextures();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void FatalFileError( uint32 error )
{
	static char*	p_error_message_english[2]	= {	"There's a problem with the disc you're using.",
													"It may be dirty or damaged." };
	static char*	p_error_message_french[2]	= {	"Le disque utilis prsente une anomalie.",
													"Il est peut-tre sale ou endommag." };
	static char*	p_error_message_german[2]	= {	"Bei der benutzten CD ist ein Problem aufgetreten.",
													"Mglicherweise ist sie verschmutzt oder beschdigt." };

	// Turn off the loading bar if it is active.
	if( EngineGlobals.loadingbar_timer_event != 0 )
	{
		timeKillEvent( EngineGlobals.loadingbar_timer_event );
		EngineGlobals.loadingbar_timer_event = 0;
	}

	// Ensure the graphics device has been initialised at this point.
	if( EngineGlobals.p_Device == NULL )
	{
		InitialiseEngine();
	}

	// Wait for any rendering to complete.
	EngineGlobals.p_Device->BlockUntilIdle();

	char*	p_error_message[2];
	switch( Config::GetLanguage())
	{
		case Config::LANGUAGE_FRENCH:
		{
			p_error_message[0] = p_error_message_french[0];
			p_error_message[1] = p_error_message_french[1];
			break;
		}
		case Config::LANGUAGE_GERMAN:
		{
			p_error_message[0] = p_error_message_german[0];
			p_error_message[1] = p_error_message_german[1];
			break;
		}
		default:
		{
			p_error_message[0] = p_error_message_english[0];
			p_error_message[1] = p_error_message_english[1];
			break;
		}

	}

	// Set up the text string used for the error message.
	SText error_text;
	error_text.mp_font		= (SFont*)EngineGlobals.p_memory_resident_font;
	error_text.m_xpos		= 48.0f;
	error_text.m_ypos		= 128.0f;
	error_text.m_xscale		= 0.8f;
	error_text.m_yscale		= 1.0f;
	error_text.m_rgba		= 0x80808080;
	error_text.mp_next		= NULL;

	set_texture( 1, NULL );
	set_texture( 2, NULL );
	set_texture( 3, NULL );

	// Want an infinite loop here.
	while( true )
	{
		D3DDevice_Swap( D3DSWAP_DEFAULT );

		// Now that the swap instruction has been pushed, clear the buffer for next frame.
		D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x00000000, 1.0f, 0 );

		set_blend_mode( vBLEND_MODE_BLEND );
		set_texture( 0, NULL );

		set_render_state( RS_UVADDRESSMODE0,	0x00010001UL );
		set_render_state( RS_ZBIAS,				0 );
		set_render_state( RS_ALPHACUTOFF,		1 );
		set_render_state( RS_ZWRITEENABLE,		0 );

		D3DDevice_SetTextureStageState( 0, D3DTSS_COLORSIGN, D3DTSIGN_RUNSIGNED | D3DTSIGN_GUNSIGNED | D3DTSIGN_BUNSIGNED );
		D3DDevice_SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
		D3DDevice_SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU | 0 );

		error_text.mp_string	= p_error_message[0];
		error_text.m_ypos		= error_text.m_ypos - 16.0f;
		error_text.BeginDraw();
		error_text.Draw();
		error_text.EndDraw();

		error_text.mp_string	= p_error_message[1];
		error_text.m_ypos		= error_text.m_ypos + 16.0f;
		error_text.BeginDraw();
		error_text.Draw();
		error_text.EndDraw();
	}
}



} // namespace NxXbox



================================================
FILE: Code/Gfx/XBox/NX/nx_init.h
================================================
#ifndef __NX_INIT_H
#define __NX_INIT_H

#include 
#include 

namespace NxXbox
{

void InitialiseEngine( void );
void FatalFileError( uint32 error );

typedef struct
{
	XGMATRIX			world_matrix;
	XGMATRIX			view_matrix;
	XGMATRIX			projection_matrix;

	XGMATRIX			bump_env_matrix;					// Used to set the D3DTSS_BUMPENVMATnn texture states where applicable.

	D3DVIEWPORT8		viewport;
	float				near_plane;
	float				far_plane;
	float				screen_angle;
	float				near_plane_width;
	float				near_plane_height;
	bool				is_orthographic;
	bool				clear_color_buffer;					// Whether the color buffer is cleared during buffer swap and clear process.
	bool				letterbox_active;					// Whether running in 4:3 letterbox mode.
	D3DCOLOR			clear_color;						// The color to which the color buffer is cleared.
	XGVECTOR3			cam_position;
	XGVECTOR3			model_relative_cam_position;		// Used in specular lighting calculations.
	XGVECTOR3			cam_at;
	XGVECTOR3			cam_up;
	XGVECTOR3			cam_right;

	int					render_start_time;					// Time (milliseconds) at which the current frame render started.

	bool				loadingscreen_visible;
	MMRESULT			loadingbar_timer_event;
		
	IDirect3DDevice8*	p_Device;
	IDirect3DSurface8*	p_RenderSurface;
	IDirect3DSurface8*	p_ZStencilSurface;
	IDirect3DSurface8*	p_BlurSurface[4];

	int					backbuffer_width;
	int					backbuffer_height;
	D3DFORMAT			backbuffer_format;
	D3DFORMAT			blurbuffer_format;
	int					zstencil_depth;
	float				screen_conv_x_multiplier;
	float				screen_conv_y_multiplier;
	int					screen_conv_x_offset;
	int					screen_conv_y_offset;
	
	void*				p_memory_resident_font;

	char				screenshot_name[128];

	// For bounding sphere culling calculations.
	float				ViewFrustumTX;
	float				ViewFrustumTY;
	float				ViewFrustumSX;
	float				ViewFrustumSY;
	float				ViewFrustumCX;
	float				ViewFrustumCY;

	uint32				blend_mode_value;
	uint32				blend_op;
	uint32				src_blend;
	uint32				dest_blend;

	uint32				alpha_blend_enable;
	uint32				alpha_test_enable;
	uint32				alpha_ref;
	uint32				specular_enabled;
	bool				lighting_enabled;
	bool				dither_enable;
	bool				z_write_enabled;
	bool				z_test_enabled;
	uint32				z_bias;
	int					cull_mode;
	bool				allow_envmapping;				// Set to true (default) to allow costly environment mapping
	uint32				uv_addressing[4];
	uint32				mip_map_lod_bias[4];
	uint32				min_mag_filter[4];
	void				*p_texture[4];
	DWORD				color_sign[4];
	bool				custom_pipeline_enabled;		// A true value indicates that the fixed function pipeline is not being used.
	DWORD				vertex_shader_id;
	DWORD				pixel_shader_override;
	DWORD				pixel_shader_id;
	float				pixel_shader_constants[20];		// 4 floats per constant.
														// c0 - c3	: material pass color (rgb) and fixed alpha (a) for relevant blend modes
														// c4		: fog denisty (b), 0.5 (a)
	bool				upload_pixel_shader_constants;
	DWORD				vertex_shader_override;
	DWORD				texture_stage_override;
	DWORD				material_override;
	DWORD				blend_mode_override;
	float				ambient_light_color[4];			// In format ready to load to GPU.
	float				directional_light_color[24];	// In format ready to load to GPU (dir0, col0, dir 1, col1, etc).
	uint32				screen_blur;					// [0, 255] - [no blur, max blur]
	uint32				screen_blur_duration;			// How many frames the screen blur has been active for.
	uint32				focus_blur;						// [0, 255] - [no blur, max blur]
	uint32				focus_blur_duration;			// How many frames the focus blur has been active for.

	uint32				fog_enabled;
	D3DCOLOR			fog_color;
	float				fog_start;
	float				fog_end;
}
sEngineGlobals;

extern sEngineGlobals EngineGlobals;



} // namespace NxXbox

#endif // __NX_INIT_H


================================================
FILE: Code/Gfx/XBox/NX/occlude.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		occlude.cpp												**
**																			**
**	Created:		03/18/02	-	dc										**
**																			**
**	Description:	Occlusion testing code									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/
		
#include 
#include 
#include 
#include 
#include "occlude.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

//extern D3DXMATRIX *p_bbox_transform;
extern XGMATRIX *p_bbox_transform;

namespace NxXbox
{


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

const uint32 MAX_NEW_OCCLUSION_POLYS_TO_CHECK_PER_FRAME = 4;

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

// Structure used to store details of a single poly. A list of these will be built at geometry load time.
struct sOcclusionPoly
{
	bool		in_use;		// Whether the poly is currently being used for occlusion.
	bool		available;	// Whether the poly is available for selection for occlusion.
	uint32		checksum;	// Name checksum of the occlusion poly.
	Mth::Vector	verts[4];
	Mth::Vector	normal;
};
	
const uint32	MAX_OCCLUDERS				= 8;
const uint32	MAX_VIEWS_PER_OCCLUDER		= 2;

struct sOccluder
{
	static uint32		NumOccluders;
	static sOccluder	Occluders[MAX_OCCLUDERS];

	static void			add_to_stack( sOcclusionPoly *p_poly );
	static void			sort_stack( void );
	static void			tidy_stack( void );

	sOcclusionPoly	*p_poly;
	Mth::Vector		planes[5];
	int				score[MAX_VIEWS_PER_OCCLUDER];	// Current rating on quality of occlusion - based on number of meshes occluded last frame.
};

const uint32	MAX_OCCLUSION_POLYS			= 512;
uint32			NumOcclusionPolys			= 0;
uint32			NextOcclusionPolyToCheck	= 0;
int				CurrentView					= 0;
sOcclusionPoly	OcclusionPolys[MAX_OCCLUSION_POLYS];

uint32		sOccluder::NumOccluders				= 0;
sOccluder	sOccluder::Occluders[MAX_OCCLUDERS];

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sOccluder::add_to_stack( sOcclusionPoly *p_poly )
{
	if( NumOccluders < MAX_OCCLUDERS )
	{
		Dbg_Assert( p_poly->available );

		Occluders[NumOccluders].p_poly	= p_poly;
		p_poly->in_use					= true;

		// Reset scores for all views.
		memset( Occluders[NumOccluders].score, 0, sizeof( int ) * MAX_VIEWS_PER_OCCLUDER );

		++NumOccluders;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int cmp( const void *p1, const void *p2 )
{
	// Sort based on the sum total of scores in all views.
	int score1 = 0;
	int score2 = 0;
	for( int v = 0; v < MAX_VIEWS_PER_OCCLUDER; ++v )
	{
		// Zero the score for any occlusion poly that is no longer available. This will force it out of the stack.
		if(((sOccluder*)p1)->p_poly->available == false )
			((sOccluder*)p1)->score[v] = 0;

		if(((sOccluder*)p2)->p_poly->available == false )
			((sOccluder*)p2)->score[v] = 0;

		score1 += ((sOccluder*)p1)->score[v];
		score2 += ((sOccluder*)p2)->score[v];
	}

	return score1 < score2 ? 1 : score1 > score2 ? -1 : 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sOccluder::sort_stack( void )
{
	qsort( Occluders, NumOccluders, sizeof( sOccluder ), cmp );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sOccluder::tidy_stack( void )
{
	if( NumOccluders > 0 )
	{
		// Sort in descending score order.
		sort_stack();

		// Count backwards so we know we get all the bad occluders.
		for( int i = NumOccluders - 1; i >= 0; --i )
		{
			// If we have hit an occluder with zero meshes culled, cut off the stack at this point.
			int total_score = 0;
			for( int v = 0; v < MAX_VIEWS_PER_OCCLUDER; ++v )
			{
				total_score += Occluders[i].score[v];
			}
			
			if( total_score == 0 )
			{
				// No longer using this poly.
				Occluders[i].p_poly->in_use = false;

				// One less occluder to worry about.
				--NumOccluders;
			}
			else
			{
				// Reset the good occluders.
				Occluders[i].score[CurrentView] = 0;
			}
		}
	}
}



/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/* Used to add an occlusion poly to the list. Likely to be called */
/* as geometry is spooled in.                                     */
/*                                                                */
/******************************************************************/
void AddOcclusionPoly( Mth::Vector &v0, Mth::Vector &v1, Mth::Vector &v2, Mth::Vector &v3, uint32 checksum )
{
	Dbg_Assert( NumOcclusionPolys < MAX_OCCLUSION_POLYS );	
	
	OcclusionPolys[NumOcclusionPolys].in_use	= false;
	OcclusionPolys[NumOcclusionPolys].available	= true;
	OcclusionPolys[NumOcclusionPolys].checksum	= checksum;
	OcclusionPolys[NumOcclusionPolys].verts[0]	= v0;
	OcclusionPolys[NumOcclusionPolys].verts[1]	= v1;
	OcclusionPolys[NumOcclusionPolys].verts[2]	= v2;
	OcclusionPolys[NumOcclusionPolys].verts[3]	= v3;
	OcclusionPolys[NumOcclusionPolys].normal	= Mth::CrossProduct( v1 - v0, v3 - v0 );
	OcclusionPolys[NumOcclusionPolys].normal.Normalize();
	
	++NumOcclusionPolys;
}



/******************************************************************/
/*                                                                */
/* Used to toggle whether an occlusion poly can be used or not	  */
/*                                                                */
/******************************************************************/
void EnableOcclusionPoly( uint32 checksum, bool available )
{
	for( uint32 i = 0; i < NumOcclusionPolys; ++i )
	{
		if( OcclusionPolys[i].checksum == checksum )
		{
			OcclusionPolys[i].available	= available;
		}
	}
}



/******************************************************************/
/*                                                                */
/* Used to clear all occlusion polys (when a level is unloaded)   */
/*                                                                */
/******************************************************************/
void RemoveAllOcclusionPolys( void )
{
	Dbg_Assert( NumOcclusionPolys < MAX_OCCLUSION_POLYS );	
	
 	sOccluder::NumOccluders		= 0;
	NumOcclusionPolys			= 0;
	NextOcclusionPolyToCheck	= 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CheckForOptimalOccluders( Mth::Vector &cam_pos, Mth::Vector &view_direction )
{
	if( NumOcclusionPolys > 0 )
	{
		uint32 added	= 0;
		uint32 checked	= 0;

		while( added < MAX_NEW_OCCLUSION_POLYS_TO_CHECK_PER_FRAME )
		{
			// Given the current position of the camera, check through the unused, available occlusion polys to see if one scores higher
			// than the lowest scoring occlusion poly in use.
			sOcclusionPoly *poly_to_check = &OcclusionPolys[NextOcclusionPolyToCheck++];
			if(( !poly_to_check->in_use ) && ( poly_to_check->available ))
			{
				sOccluder::add_to_stack( poly_to_check );
				++added;
			}
			++checked;

			// Ensure we are always checking within bounds.
			if( NextOcclusionPolyToCheck >= NumOcclusionPolys )
			{
				NextOcclusionPolyToCheck = 0;
			}

			// Quit out if we have less available occluders than spaces to fill.
			if( checked >= NumOcclusionPolys )
			{
				break;
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void BuildOccluders( Mth::Vector *p_cam_pos, int view )
{
//	for( uint32 i = 0; i < NumOcclusionPolys; ++i )
//	{
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[0], OcclusionPolys[i].verts[1], 0, 0, 1 );
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[1], OcclusionPolys[i].verts[2], 0, 0, 1 );
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[2], OcclusionPolys[i].verts[3], 0, 0, 1 );
//		Gfx::AddDebugLine( OcclusionPolys[i].verts[3], OcclusionPolys[i].verts[0], 0, 0, 1 );
//	}
	
	// Set the current view - this will remain active until another call to this function.
	Dbg_Assert( view < MAX_VIEWS_PER_OCCLUDER );
	CurrentView	= view;
	
	// Tidy up from last frame.
	sOccluder::tidy_stack();
	
	// Cyclically add more occluders for checking.
	CheckForOptimalOccluders( *p_cam_pos, *p_cam_pos );
	
	// Build all 5 planes for each occluder.
	Mth::Vector u0, u1, p;

	// The order in which the verts are used to build tha planes depends upon where the camera is in relation to the occlusion
	// poly. We use the default order when the viewpoint is on the side of the poly on which the default poly normal faces.
	for( uint32 i = 0; i < sOccluder::NumOccluders; ++i )
	{
		sOcclusionPoly *p_poly = sOccluder::Occluders[i].p_poly;

//		Gfx::AddDebugLine( p_poly->verts[0], p_poly->verts[1], 0, 0, 1 );
//		Gfx::AddDebugLine( p_poly->verts[1], p_poly->verts[2], 0, 0, 1 );
//		Gfx::AddDebugLine( p_poly->verts[2], p_poly->verts[3], 0, 0, 1 );
//		Gfx::AddDebugLine( p_poly->verts[3], p_poly->verts[0], 0, 0, 1 );
		
		if( Mth::DotProduct( *p_cam_pos - p_poly->verts[0], p_poly->normal ) >= 0.0f )
		{
			// Start with the front. We want to reverse the order of the front plane to ensure that objects *behind* the plane
			// are considered occluded. (1->3->4)...
			u0						= p_poly->verts[2] - p_poly->verts[0];
			u1						= p_poly->verts[3] - p_poly->verts[0];
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, p_poly->verts[0] );
			sOccluder::Occluders[i].planes[0]	= p;

			// ...then left (0->1->2)...
			u0						= p_poly->verts[0] - *p_cam_pos;
			u1						= p_poly->verts[1] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[1]	= p;

			// ...then right (0->3->4)...
			u0						= p_poly->verts[2] - *p_cam_pos;
			u1						= p_poly->verts[3] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[2]	= p;

			// ...then top (0->2->3)...
			u0						= p_poly->verts[1] - *p_cam_pos;
			u1						= p_poly->verts[2] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[3]	= p;

			// ...then bottom (0->4->1)...
			u0						= p_poly->verts[3] - *p_cam_pos;
			u1						= p_poly->verts[0] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[4]	= p;
		}
		else
		{
			// Start with the front. We want to reverse the order of the front plane to ensure that objects *behind* the plane
			// are considered occluded. (1->4->3)...
			u0						= p_poly->verts[3] - p_poly->verts[0];
			u1						= p_poly->verts[2] - p_poly->verts[0];
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, p_poly->verts[0] );
			sOccluder::Occluders[i].planes[0]	= p;

			// ...then left (0->2->1)...
			u0						= p_poly->verts[1] - *p_cam_pos;
			u1						= p_poly->verts[0] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[1]	= p;

			// ...then right (0->4->3)...
			u0						= p_poly->verts[3] - *p_cam_pos;
			u1						= p_poly->verts[2] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[2]	= p;

			// ...then top (0->3->2)...
			u0						= p_poly->verts[2] - *p_cam_pos;
			u1						= p_poly->verts[1] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[3]	= p;

			// ...then bottom (0->1->4)...
			u0						= p_poly->verts[0] - *p_cam_pos;
			u1						= p_poly->verts[3] - *p_cam_pos;
			p						= Mth::CrossProduct( u0, u1 );
			p.Normalize();
			p[W]					= Mth::DotProduct( p, *p_cam_pos );
			sOccluder::Occluders[i].planes[4]	= p;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool TestSphereAgainstOccluders( D3DXVECTOR3 *p_center, float radius, uint32 meshes )
{
	XGVECTOR3 center;

	// Build the composite transform if required.
	if( p_bbox_transform )
	{
		// Object to world, so transform the sphere center.
		center.x = p_center->x + p_bbox_transform->_41;
		center.y = p_center->y + p_bbox_transform->_42;
		center.z = p_center->z + p_bbox_transform->_43;
	}
	else
	{
		center.x = p_center->x;
		center.y = p_center->y;
		center.z = p_center->z;
	}

	// Test against each occluder.
	for( uint32 o = 0; o < sOccluder::NumOccluders; ++o )
	{
		bool occluded = true;

		// Test against each plane in the occluder.
		for( uint32 p = 0; p < 5; ++p )
		{
			float result =	( sOccluder::Occluders[o].planes[p][X] * center.x ) +
							( sOccluder::Occluders[o].planes[p][Y] * center.y ) +
							( sOccluder::Occluders[o].planes[p][Z] * center.z ) -
							( sOccluder::Occluders[o].planes[p][W] );
			if( result >= -radius )
			{
				// Outside of this plane, therefore not occluded by this occluder.
				occluded = false;
				break;
			}
		}

		if( occluded )
		{
			// Inside all planes, therefore occluded. Increase score for this occluder.
			sOccluder::Occluders[o].score[CurrentView] += meshes;
			return true;
		}
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


} // namespace NxXbox


================================================
FILE: Code/Gfx/XBox/NX/occlude.h
================================================
#ifndef	__OCCLUDE_H__
#define	__OCCLUDE_H__

namespace NxXbox
{
	void AddOcclusionPoly( Mth::Vector &v0, Mth::Vector &v1, Mth::Vector &v2, Mth::Vector &v3, uint32 checksum = 0 );
	void EnableOcclusionPoly( uint32 checksum, bool available );
	void RemoveAllOcclusionPolys( void );
	void BuildOccluders( Mth::Vector *p_cam_pos, int view );
	bool TestSphereAgainstOccluders( D3DXVECTOR3 *p_center, float radius, uint32 meshes = 1 );
}

#endif



================================================
FILE: Code/Gfx/XBox/NX/particles.cpp
================================================

/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		particle.cpp											**
**																			**
**	Created:		03/27/02	-	dc										**
**																			**
**	Description:	Low level particle rendering code						**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/
		
#include 
#include 
#include 
#include 
#include "render.h"
#include "particles.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

namespace NxXbox
{


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sParticleSystem::sParticleSystem( uint32 max_particles, eParticleType type, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, int history )
{
	// Obtain the texture.
	Nx::CTexture		* p_texture			= Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( texture_checksum );
	Nx::CXboxTexture	*p_xbox_texture		= static_cast( p_texture );
	NxXbox::sTexture	*p_engine_texture	= NULL;
	if( p_xbox_texture )
	{
		p_engine_texture = p_xbox_texture->GetEngineTexture(); 
	}
	
	// Create a (semi-transparent) material used to render the mesh.
	sMaterial *p_material			= new sMaterial();
	p_material->m_flags[0]			= 0x40 | (( p_engine_texture ) ? MATFLAG_TEXTURED : 0 );
	p_material->m_checksum			= (uint32)rand() * (uint32)rand();
	p_material->m_passes			= 1;
	p_material->mp_tex[0]			= p_engine_texture;
	p_material->m_reg_alpha[0]		= GetBlendMode( blendmode_checksum ) | ((uint32)fix << 24 );
	p_material->m_color[0][0]		= 0.5f;
	p_material->m_color[0][1]		= 0.5f;
	p_material->m_color[0][2]		= 0.5f;
	p_material->m_color[0][3]		= fix * ( 1.0f /  128.0f );

	p_material->m_uv_addressing[0]	= 0;
	p_material->m_k[0]				= 0.0f;
	p_material->m_alpha_cutoff		= 1;
	p_material->m_no_bfc			= true;
	p_material->m_uv_wibble			= false;

	mp_material						= p_material;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sParticleSystem::~sParticleSystem( void )
{
	delete mp_material;
}

} // namespace NxXbox


================================================
FILE: Code/Gfx/XBox/NX/particles.h
================================================

#ifndef	__PARTICLE_H__
#define	__PARTICLE_H__

#include "gfx\xbox\nx\scene.h"

namespace NxXbox
{

enum eParticleType
{
	PARTICLE_TYPE_LINE,
	PARTICLE_TYPE_FLAT,
	PARTICLE_TYPE_SHADED,
	PARTICLE_TYPE_SMOOTH,
	PARTICLE_TYPE_GLOW,
	PARTICLE_TYPE_STAR,
	PARTICLE_TYPE_SMOOTHSTAR,
	PARTICLE_TYPE_RIBBON,
	PARTICLE_TYPE_SMOOTHRIBBON,
	PARTICLE_TYPE_RIBBONTRAIL,
	PARTICLE_TYPE_GLOWRIBBONTRAIL,
};


struct sParticleSystem
{
public:
				sParticleSystem( uint32 max_particles, eParticleType type, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments = 0, int history = 0 );
				~sParticleSystem( void );

	sMaterial	*mp_material;
};

}

#endif



================================================
FILE: Code/Gfx/XBox/NX/render.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "nx_init.h"
#include "scene.h"
#include "render.h"
#include "instance.h"
#include "occlude.h"
#include "billboard.h"

#include "PixelShader0.h"
#include "PixelShader0IVA.h"
#include "PixelShader1.h"
#include "PixelShader2.h"
#include "PixelShader3.h"
#include "PixelShader4.h"
#include "PixelShader5.h"
#include "PixelShaderBrighten.h"
#include "PixelShaderBrightenIVA.h"
#include "PixelShaderFocusBlur.h"
#include "PixelShaderFocusIntegrate.h"
#include "PixelShaderFocusLookupIntegrate.h"
#include "PixelShaderNULL.h"
#include "PixelShaderPointSprite.h"
#include "PixelShader_ShadowBuffer.h"
#include "PixelShaderBumpWater.h"
#include "ShadowBufferStaticGeomPS.h"

DWORD PixelShader0;
DWORD PixelShader0IVA;
DWORD PixelShader1;
DWORD PixelShader2;
DWORD PixelShader3;
DWORD PixelShader4;
DWORD PixelShader5;
DWORD PixelShaderBrighten;
DWORD PixelShaderBrightenIVA;
DWORD PixelShaderFocusBlur;
DWORD PixelShaderFocusIntegrate;
DWORD PixelShaderFocusLookupIntegrate;
DWORD PixelShaderNULL;
DWORD PixelShaderPointSprite;
DWORD PixelShader_ShadowBuffer;
DWORD PixelShaderBumpWater;
DWORD ShadowBufferStaticGeomPS;

//D3DXMATRIX *p_bbox_transform = NULL;
//D3DXMATRIX bbox_transform;
XGMATRIX	*p_bbox_transform = NULL;
XGMATRIX	bbox_transform;

extern DWORD ShadowBufferStaticGeomVS;

namespace NxXbox
{

const float FRONT_TO_BACK_SORT_CUTOFF	= ( 50.0f * 12.0f );

Lst::HashTable< sTextureProjectionDetails >	*pTextureProjectionDetailsTable = NULL;

// For converting a FLOAT to a DWORD (useful for SetRenderState() calls)
inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); }

static Lst::HashTable sPixelShaderTable( 8 );


static const int MAX_FREE_TESTS					= 1024;
static bool		visibilityTestValuesInitialised = false;
static uint8	visibilityTestValues[MAX_FREE_TESTS];

struct sVisibilityTestFIFO
{
	static const int	MAX_FIFO_SIZE	= 4;

	uint32				m_status_fifo[MAX_FIFO_SIZE];
	uint32				m_fifo_index;

						sVisibilityTestFIFO();
						~sVisibilityTestFIFO();

	static uint32		GetFreeTestIndex( void );
	uint32				AddStatus( void );
	uint32				GetStatus( void );


	private:

	void				SlideQueue( void );
	uint32				m_last_valid_status;
};


struct sLightGlowDetails
{
	Mth::Vector			m_pos;
	float				m_glow_radius;
	float				m_current_radius;
	float				m_test_radius;
	float				m_radius_growth;
	sVisibilityTestFIFO	m_visibility_test_fifo;
};


static Lst::HashTable sLightGlowDetailsTable( 8 );




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
BlendModes GetBlendMode( uint32 blend_checksum )
{
	BlendModes rv = NxXbox::vBLEND_MODE_DIFFUSE;

	switch ( blend_checksum )
	{
		case 0x54628ed7:		// Blend
			rv = NxXbox::vBLEND_MODE_BLEND;
			break;
		case 0x02e58c18:		// Add
			rv = NxXbox::vBLEND_MODE_ADD;
			break;
		case 0xa7fd7d23:		// Sub
		case 0xdea7e576:		// Subtract
			rv = NxXbox::vBLEND_MODE_SUBTRACT;
			break;
		case 0x40f44b8a:		// Modulate
			rv = NxXbox::vBLEND_MODE_MODULATE;
			break;
		case 0x68e77f40:		// Brighten
			rv = NxXbox::vBLEND_MODE_BRIGHTEN;
			break;
		case 0x18b98905:		// FixBlend
			rv = NxXbox::vBLEND_MODE_BLEND_FIXED;
			break;
		case 0xa86285a1:		// FixAdd
			rv = NxXbox::vBLEND_MODE_ADD_FIXED;
			break;
		case 0x0d7a749a:		// FixSub
		case 0x0eea99ff:		// FixSubtract
			rv = NxXbox::vBLEND_MODE_SUB_FIXED;
			break;
		case 0x90b93703:		// FixModulate
			rv = NxXbox::vBLEND_MODE_MODULATE_FIXED;
			break;
		case 0xb8aa03c9:		// FixBrighten
			rv = NxXbox::vBLEND_MODE_BRIGHTEN_FIXED;
			break;
		case 0x515e298e:		// Diffuse
		case 0x806fff30:		// None
			rv = NxXbox::vBLEND_MODE_DIFFUSE;
			break;
		default:
			Dbg_MsgAssert(0,("Illegal blend mode specified. Please use (fix)blend/add/sub/modulate/brighten or diffuse/none."));
			break;
	}
	return rv;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVisibilityTestFIFO::sVisibilityTestFIFO()
{
	m_fifo_index		= 0;
	m_last_valid_status	= 0;

	if( !visibilityTestValuesInitialised )
	{
		ZeroMemory( visibilityTestValues, sizeof( uint8 ) * MAX_FREE_TESTS );

		// The first index is always reserved.
		visibilityTestValues[0] = 1;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVisibilityTestFIFO::~sVisibilityTestFIFO()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sVisibilityTestFIFO::SlideQueue( void )
{
	Dbg_Assert( m_fifo_index > 0 );

	m_status_fifo[0] = m_status_fifo[1];
	m_status_fifo[1] = m_status_fifo[2];
	m_status_fifo[2] = m_status_fifo[3];
	m_status_fifo[3] = 0;

	--m_fifo_index;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 sVisibilityTestFIFO::GetFreeTestIndex( void )
{
	for( uint32 i = 0; i < MAX_FREE_TESTS; ++i )
	{
		if( visibilityTestValues[i] == 0 )
		{
			visibilityTestValues[i] = 1;
			return i;
		}
	}
	Dbg_Assert( 0 );
	return MAX_FREE_TESTS - 1;
}
	


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 sVisibilityTestFIFO::AddStatus( void )
{
	// If the queue is already full, do nothing.
	if( m_fifo_index >= ( MAX_FIFO_SIZE - 1 ))
	{
		return 0;
	}

	// Get a free index.
	uint32 index = GetFreeTestIndex();
	m_status_fifo[m_fifo_index++] = index;

	return index;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 sVisibilityTestFIFO::GetStatus( void )
{
	Dbg_Assert( m_fifo_index > 0 );

	UINT result;

	// Get the result from the least recently issued test.
	HRESULT hr = D3DDevice_GetVisibilityTestResult( m_status_fifo[0], &result, NULL );

	if( hr == D3D_OK )
	{
		// Finished with this test. First mark this test index as no longer in use.
		visibilityTestValues[m_status_fifo[0]] = 0;

		// Remove entry from the queue.
		SlideQueue();

		m_last_valid_status = (uint32)result;
		return m_last_valid_status;
	}
	else if( hr == D3DERR_TESTINCOMPLETE )
	{
		// Use the last valid status.
		return m_last_valid_status;
	}

	return 0;
}







/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
DWORD get_pixel_shader( sMaterial *p_material )
{
	const  int	PIXEL_SHADER_BUFFER_SIZE	= 64 * 1024;
	static char pixel_shader_buffer[PIXEL_SHADER_BUFFER_SIZE];
	static int	pixel_shader_buffer_offset = 0;
	uint32		blend_modes[4];

	bool		mcm = true;
//	bool		mcm = false;
	for( uint32 p = 0; p < p_material->m_passes; ++p )
	{
		if( !mcm )
		{
			if(( p_material->m_color[p][0] != 0.5f ) || ( p_material->m_color[p][1] != 0.5f ) || ( p_material->m_color[p][2] != 0.5f ))
				mcm = true;
		}
		blend_modes[p] = p_material->m_reg_alpha[p] & sMaterial::BLEND_MODE_MASK;
	}

	if( p_material->m_passes > 1 )
	{
		// First we build the unique key from the properties of the required shader, to see if it already exists.
		uint32 code = p_material->m_passes;
		for( uint32 p = 0; p < p_material->m_passes; ++p )
		{
			code |= ( blend_modes[p] << ( 5 * ( p + 1 )));
		}

		// Integrate the ignore vertex alpha flags.
		uint32 ignore_bf = p_material->GetIgnoreVertexAlphaPasses();
		code |= ( ignore_bf << 25 );

		// Check also to see if material color modulation is required.
		if( mcm )
		{
			code |= ( 1 << 30 );
		}
		
		// Check to see if the shader exists, if so just return the shader handle.
		DWORD *p_shader = sPixelShaderTable.GetItem( code );
		if( p_shader )
		{
			return (DWORD)p_shader;
		}

		// We need to build the shader.
		char shader_buffer[1024];

		sprintf( shader_buffer, "xps.1.1\n" );

		strcat( shader_buffer, "tex t0\n" );
		strcat( shader_buffer, "tex t1\n" );

		switch( p_material->m_passes )
		{
			case 2:
			{
				Dbg_Assert( ignore_bf <= 0x03 );

				if( mcm )
				{
					// Modulate texture0 and texture 1 color with pass 0 and pass 1 material color, and place into t0.rgb and t1.rgb.
					strcat( shader_buffer, "xmma		t0.rgb,t1.rgb,discard.rgb,t0.rgb,c0.rgb,t1.rgb,c1.rgb\n" );
					
					// Modulate result color with vertex color.
					strcat( shader_buffer, "xmma_x4		r0.rgb,r1.rgb,discard.rgb,t0.rgb,v0.rgb,t1.rgb,v0.rgb\n" );

					// Modulate result alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
					if( ignore_bf == 0x00 )
					{
						// Pass0 modulates with vertex alpha. Pass1 modulates with vertex alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,v0.a\n" );
					}
					else if( ignore_bf == 0x01 )
					{
						// Pass0 modulates with constant alpha. Pass1 modulates with vertex alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,v0.a\n" );
					}
					else if( ignore_bf == 0x02 )
					{
						// Pass0 modulates with vertex alpha. Pass1 modulates with constant alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,c4.a\n" );
					}
					else if( ignore_bf == 0x03 )
					{
						// Pass0 modulates with constant alpha. Pass1 modulates with constant alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,c4.a\n" );
					}
				}
				else
				{
					// Modulate texture0 and texture1 color with vertex color.
					strcat( shader_buffer, "xmma_x2		r0.rgb,r1.rgb,discard.rgb,t0.rgb,v0.rgb,t1.rgb,v0.rgb\n" );

					// Modulate texture0 and tetxure1 alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
					if( ignore_bf == 0x00 )
					{
						// Pass0 modulates with vertex alpha. Pass1 modulates with vertex alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,v0.a\n" );
					}
					else if( ignore_bf == 0x01 )
					{
						// Pass0 modulates with constant alpha. Pass1 modulates with vertex alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,v0.a\n" );
					}
					else if( ignore_bf == 0x02 )
					{
						// Pass0 modulates with vertex alpha. Pass1 modulates with constant alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,c4.a\n" );
					}
					else if( ignore_bf == 0x03 )
					{
						// Pass0 modulates with constant alpha. Pass1 modulates with constant alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,c4.a\n" );
					}
				}

				// Then deal with the second pass blend.
				switch( blend_modes[1] )
				{
					case vBLEND_MODE_ADD:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,r1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_ADD_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,c1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUBTRACT:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,-r1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUB_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,-c1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND:
					{
						strcat( shader_buffer, "lrp r0.rgb,r1.a,r1.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_FIXED:
					{
						strcat( shader_buffer, "lrp r0.rgb,c1.a,r1.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_MODULATE:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,r1.a\n" );
						break;
					}
					case vBLEND_MODE_MODULATE_FIXED:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,c1.a\n" );
					}
					case vBLEND_MODE_BRIGHTEN:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,r1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BRIGHTEN_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,c1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,r0.a,r1.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,r0.a,r0.rgb,r1.rgb\n" );
						break;
					}
					case vBLEND_MODE_GLOSS_MAP:
					{
						strcat( shader_buffer, "mul v1.rgb,v1.rgb,t1.a\n" );
						break;
					}
				}
				break;
			}

			case 3:
			{
				Dbg_Assert( ignore_bf <= 0x07 );

				strcat( shader_buffer, "tex t2\n" );

				if( mcm )
				{
					// Modulate texture0 and texture 1 color with pass 0 and pass 1 material color, and place into t0.rgb and t1.rgb.
					strcat( shader_buffer, "xmma		t0.rgb,t1.rgb,discard.rgb,t0.rgb,c0.rgb,t1.rgb,c1.rgb\n" );
					
					// Modulate result color with vertex color.
					strcat( shader_buffer, "xmma_x4		r0.rgb,r1.rgb,discard.rgb,t0.rgb,v0.rgb,t1.rgb,v0.rgb\n" );

					// Modulate result alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
					if(( ignore_bf & 0x03 ) == 0x00 )
					{
						// Pass0 modulates with vertex alpha. Pass1 modulates with vertex alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,v0.a\n" );
					}
					else if(( ignore_bf & 0x03 ) == 0x01 )
					{
						// Pass0 modulates with constant alpha. Pass1 modulates with vertex alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,v0.a\n" );
					}
					else if(( ignore_bf & 0x03 ) == 0x02 )
					{
						// Pass0 modulates with vertex alpha. Pass1 modulates with constant alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,c4.a\n" );
					}
					else if(( ignore_bf & 0x03 ) == 0x03 )
					{
						// Pass0 modulates with constant alpha. Pass1 modulates with constant alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,c4.a\n" );
					}

					// Modulate texture2 with pass 2 material color.
					strcat( shader_buffer, "mul			t2.rgb,t2.rgb,c2.rgb\n" );

					// Modulate result color with vertex color.
					strcat( shader_buffer, "mul_x4		t2.rgb,t2.rgb,v0.rgb\n" );

					// Modulate result alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
					if(( ignore_bf & 0x04 ) == 0x00 )
					{
						// Pass2 modulates with vertex alpha.
						strcat( shader_buffer, "+mul_x2		t2.a,t2.a,v0.a\n" );
					}
					else if(( ignore_bf & 0x04 ) == 0x04 )
					{
						// Pass2 modulates with constant alpha.
						strcat( shader_buffer, "+mul_x2		t2.a,t2.a,c4.a\n" );
					}
				}
				else
				{
					// Modulate texture0 and texture1 with vertex color.
					strcat( shader_buffer, "xmma_x2	r0,r1,discard,t0,v0,t1,v0\n" );

					// Modulate texture2 with vertex color.
					strcat( shader_buffer, "mul_x2	t2,t2,v0\n" );
				}

				// Then deal with the second pass blend.
				switch( blend_modes[1] )
				{
					case vBLEND_MODE_ADD:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,r1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_ADD_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,c1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUBTRACT:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,-r1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUB_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,-c1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND:
					{
						strcat( shader_buffer, "lrp r0.rgb,r1.a,r1.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_FIXED:
					{
						strcat( shader_buffer, "lrp	r0.rgb,c1.a,r1.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_MODULATE:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,r1.a\n" );
						break;
					}
					case vBLEND_MODE_MODULATE_FIXED:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,c1.a\n" );
					}
					case vBLEND_MODE_BRIGHTEN:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,r1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BRIGHTEN_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,c1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,r0.a,r1.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,r0.a,r0.rgb,r1.rgb\n" );
						break;
					}
					case vBLEND_MODE_GLOSS_MAP:
					{
						strcat( shader_buffer, "mul v1.rgb,v1.rgb,t1.a\n" );
						break;
					}
				}
				
				// Then deal with the third pass blend.
				switch( blend_modes[2] )
				{
					case vBLEND_MODE_ADD:
					{
						strcat( shader_buffer, "mad r0.rgb,t2.rgb,t2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_ADD_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,t2.rgb,c2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUBTRACT:
					{
						strcat( shader_buffer, "mad r0.rgb,t2.rgb,-t2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUB_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,t2.rgb,-c2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND:
					{
						strcat( shader_buffer, "lrp r0.rgb,t2.a,t2.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_FIXED:
					{
						strcat( shader_buffer, "lrp	r0.rgb,c2.a,t2.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_MODULATE:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,t2.a\n" );
						break;
					}
					case vBLEND_MODE_MODULATE_FIXED:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,c2.a\n" );
					}
					case vBLEND_MODE_BRIGHTEN:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,t2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BRIGHTEN_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,c2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,r1.a,t2.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,r1.a,r0.rgb,t2.rgb\n" );
						break;
					}
					case vBLEND_MODE_GLOSS_MAP:
					{
						strcat( shader_buffer, "mul v1.rgb,v1.rgb,t2.a\n" );
						break;
					}
				}

				break;
			}
		
			case 4:
			{
				Dbg_Assert( ignore_bf <= 0x0F );

				strcat( shader_buffer, "tex t2\n" );
				strcat( shader_buffer, "tex t3\n" );

				if( mcm )
				{
					// Modulate texture0 and texture 1 color with pass 0 and pass 1 material color, and place into t0.rgb and t1.rgb.
					strcat( shader_buffer, "xmma		t0.rgb,t1.rgb,discard.rgb,t0.rgb,c0.rgb,t1.rgb,c1.rgb\n" );
					
					// Modulate result color with vertex color.
					strcat( shader_buffer, "xmma_x4		r0.rgb,r1.rgb,discard.rgb,t0.rgb,v0.rgb,t1.rgb,v0.rgb\n" );

					// Modulate result alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
					if(( ignore_bf & 0x03 ) == 0x00 )
					{
						// Pass0 modulates with vertex alpha. Pass1 modulates with vertex alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,v0.a\n" );
					}
					else if(( ignore_bf & 0x03 ) == 0x01 )
					{
						// Pass0 modulates with constant alpha. Pass1 modulates with vertex alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,v0.a\n" );
					}
					else if(( ignore_bf & 0x03 ) == 0x02 )
					{
						// Pass0 modulates with vertex alpha. Pass1 modulates with constant alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,v0.a,t1.a,c4.a\n" );
					}
					else if(( ignore_bf & 0x03 ) == 0x03 )
					{
						// Pass0 modulates with constant alpha. Pass1 modulates with constant alpha.
						strcat( shader_buffer, "+xmma_x2	r0.a,r1.a,discard.a,t0.a,c4.a,t1.a,c4.a\n" );
					}

					// Modulate texture2 and texture 3 color with pass 2 and pass 3 material color, and place into t0.rgb and t1.rgb.
					strcat( shader_buffer, "xmma		t2.rgb,t3.rgb,discard.rgb,t2.rgb,c2.rgb,t3.rgb,c3.rgb\n" );
					
					// Modulate result color with vertex color.
					strcat( shader_buffer, "xmma_x4		t2.rgb,t3.rgb,discard.rgb,t2.rgb,v0.rgb,t3.rgb,v0.rgb\n" );

					// Modulate result alpha with vertex alpha (or constant alpha = 0.5 for those passes that ignore alpha).
					if(( ignore_bf & 0x0C ) == 0x00 )
					{
						// Pass2 modulates with vertex alpha. Pass3 modulates with vertex alpha.
						strcat( shader_buffer, "+xmma_x2	t2.a,t3.a,discard.a,t2.a,v0.a,t3.a,v0.a\n" );
					}
					else if(( ignore_bf & 0x0C ) == 0x04 )
					{
						// Pass2 modulates with constant alpha. Pass3 modulates with vertex alpha.
						strcat( shader_buffer, "+xmma_x2	t2.a,t3.a,discard.a,t2.a,c4.a,t3.a,v0.a\n" );
					}
					else if(( ignore_bf & 0x0C ) == 0x08 )
					{
						// Pass2 modulates with vertex alpha. Pass3 modulates with constant alpha.
						strcat( shader_buffer, "+xmma_x2	t2.a,t3.a,discard.a,t2.a,v0.a,t3.a,c4.a\n" );
					}
					else if(( ignore_bf & 0x0C ) == 0x0C )
					{
						// Pass2 modulates with constant alpha. Pass3 modulates with constant alpha.
						strcat( shader_buffer, "+xmma_x2	t2.a,t3.a,discard.a,t2.a,c4.a,t3.a,c4.a\n" );
					}
				}
				else
				{
					// Modulate texture0 and texture1 with vertex color.
					strcat( shader_buffer, "xmma_x2		r0,r1,discard,t0,v0,t1,v0\n" );

					// Modulate texture2 and texture3 with vertex color.
					strcat( shader_buffer, "xmma_x2		t2,t3,discard,t2,v0,t3,v0\n" );
				}
				
				// Then deal with the second pass blend.
				switch( blend_modes[1] )
				{
					case vBLEND_MODE_ADD:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,r1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_ADD_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,c1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUBTRACT:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,-r1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUB_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r1.rgb,-c1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND:
					{
						strcat( shader_buffer, "lrp r0.rgb,r1.a,r1.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_FIXED:
					{
						strcat( shader_buffer, "lrp r0.rgb,c1.a,r1.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_MODULATE:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,r1.a\n" );
						break;
					}
					case vBLEND_MODE_MODULATE_FIXED:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,c1.a\n" );
					}
					case vBLEND_MODE_BRIGHTEN:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,r1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BRIGHTEN_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,c1.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,r0.a,r1.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,r0.a,r0.rgb,r1.rgb\n" );
						break;
					}
					case vBLEND_MODE_GLOSS_MAP:
					{
						strcat( shader_buffer, "mul v1.rgb,v1.rgb,t1.a\n" );
						break;
					}
				}

				// Then deal with the third pass blend.
				switch( blend_modes[2] )
				{
					case vBLEND_MODE_ADD:
					{
						strcat( shader_buffer, "mad r0.rgb,t2.rgb,t2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_ADD_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,t2.rgb,c2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUBTRACT:
					{
						strcat( shader_buffer, "mad r0.rgb,t2.rgb,-t2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUB_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,t2.rgb,-c2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND:
					{
						strcat( shader_buffer, "lrp r0.rgb,t2.a,t2.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_FIXED:
					{
						strcat( shader_buffer, "lrp	r0.rgb,c2.a,t2.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_MODULATE:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,t2.a\n" );
						break;
					}
					case vBLEND_MODE_MODULATE_FIXED:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,c2.a\n" );
					}
					case vBLEND_MODE_BRIGHTEN:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,t2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BRIGHTEN_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,c2.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,r1.a,t2.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,r1.a,r0.rgb,t2.rgb\n" );
						break;
					}
					case vBLEND_MODE_GLOSS_MAP:
					{
						strcat( shader_buffer, "mul v1.rgb,v1.rgb,t2.a\n" );
						break;
					}
				}
				
				// Then deal with the fourth pass blend.
				switch( blend_modes[3] )
				{
					case vBLEND_MODE_ADD:
					{
						strcat( shader_buffer, "mad r0.rgb,t3.rgb,t3.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_ADD_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,t3.rgb,c3.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUBTRACT:
					{
						strcat( shader_buffer, "mad r0.rgb,t3.rgb,-t3.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_SUB_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,t3.rgb,-c3.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND:
					{
						strcat( shader_buffer, "lrp r0.rgb,t3.a,t3.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_FIXED:
					{
						strcat( shader_buffer, "lrp	r0.rgb,c3.a,t3.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_MODULATE:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,t3.a\n" );
						break;
					}
					case vBLEND_MODE_MODULATE_FIXED:
					{
						strcat( shader_buffer, "mul r0.rgb,r0.rgb,c3.a\n" );
					}
					case vBLEND_MODE_BRIGHTEN:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,t3.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BRIGHTEN_FIXED:
					{
						strcat( shader_buffer, "mad r0.rgb,r0.rgb,c3.a,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,t2.a,t3.rgb,r0.rgb\n" );
						break;
					}
					case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
					{
						strcat( shader_buffer, "lrp r0.rgb,t2.a,r0.rgb,t3.rgb\n" );
						break;
					}
					case vBLEND_MODE_GLOSS_MAP:
					{
						strcat( shader_buffer, "mul v1.rgb,v1.rgb,t3.a\n" );
						break;
					}
				}
				break;
			}
		}

		// Final combiner.
		strcat( shader_buffer, "xfc prod, fog.rgb, sum, zero, 1 - fog.a, c4, r0.a\n" );

		LPXGBUFFER pCompiledShader;
		HRESULT hr = XGAssembleShader(	"autogen.ps",									// Source file name, used in error messages.
										shader_buffer,									// A pointer to the source data.
										strlen( shader_buffer ),						// The source data length.
										SASM_SKIPPREPROCESSOR | SASM_SKIPVALIDATION,	// SASM_xxx flags. See xgraphics.h for a complete list.
										NULL,											// If constants are declared in the shader, they are written here. Pass NULL if you don't care.
										&pCompiledShader,								// The shader microcode is written here. Pass NULL if you don't care.
										NULL,											// Errors are written here. Pass NULL if you don't care.
										NULL,											// A human-readable listing is written here. Pass NULL if you don't want it.
										NULL,											// Used by the preprocessor. Can be NULL if you don't use #include in your source file.
										NULL,											// Passed unmodified to the pResolver function.
										NULL );											// Returns the type of shader that was assembled. Pass NULL if you don't care.
		Dbg_Assert( hr == S_OK );
		
		// Copy the microcode into our buffer.
		Dbg_Assert( pixel_shader_buffer_offset + pCompiledShader->size < PIXEL_SHADER_BUFFER_SIZE );
		CopyMemory( pixel_shader_buffer + pixel_shader_buffer_offset, pCompiledShader->pData, pCompiledShader->size );
		
		// Generate a handle to this shader.
		DWORD shader_handle;
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( pixel_shader_buffer + pixel_shader_buffer_offset ), &shader_handle );

		// Update the buffer offset now we have copied a new shader into the buffer.
		pixel_shader_buffer_offset += pCompiledShader->size;

		// This was allocated during XGAssembleShader().
		XGBuffer_Release( pCompiledShader );

		// Place the shader handle in the table.
		sPixelShaderTable.PutItem( code, (DWORD*)shader_handle );
		
		// Return the handle.
		return shader_handle;
	}
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void create_pixel_shaders( void )
{
	static bool created_shaders = false;
	if( !created_shaders )
	{
		created_shaders = true;

		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader0PixelShader[0] ),						&PixelShader0 );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader0IVAPixelShader[0] ),					&PixelShader0IVA );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader1PixelShader[0] ),						&PixelShader1 );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader2PixelShader[0] ),						&PixelShader2 );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader3PixelShader[0] ),						&PixelShader3 );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader4PixelShader[0] ),						&PixelShader4 );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader5PixelShader[0] ),						&PixelShader5 );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderBrightenPixelShader[0] ),				&PixelShaderBrighten );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderBrightenIVAPixelShader[0] ),			&PixelShaderBrightenIVA );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderFocusBlurPixelShader[0] ),				&PixelShaderFocusBlur );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderFocusIntegratePixelShader[0] ),			&PixelShaderFocusIntegrate );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderFocusLookupIntegratePixelShader[0] ),	&PixelShaderFocusLookupIntegrate );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderNULLPixelShader[0] ),					&PixelShaderNULL );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderPointSpritePixelShader[0] ),			&PixelShaderPointSprite );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShaderBumpWaterPixelShader[0] ),				&PixelShaderBumpWater );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwPixelShader_ShadowBufferPixelShader[0] ),			&PixelShader_ShadowBuffer );
		D3DDevice_CreatePixelShader((D3DPIXELSHADERDEF*)( &dwShadowBufferStaticGeomPSPixelShader[0] ),			&ShadowBufferStaticGeomPS );
	
		// Shouldn't be doing this here!
		pTextureProjectionDetailsTable = new Lst::HashTable< sTextureProjectionDetails >( 8 );

#		if 0
		// Light glow test code.
		sLightGlowDetails *p_details = new sLightGlowDetails;
		p_details->m_pos.Set( 0.0f, 48.0f, 0.0f );
		p_details->m_glow_radius	= 24.0f;
		p_details->m_current_radius	= 0.0f;
		p_details->m_test_radius	= 2.0f;
		p_details->m_radius_growth	= 0.2f;
		sLightGlowDetailsTable.PutItem( (uint32)p_details, p_details );

		p_details = new sLightGlowDetails;
		p_details->m_pos.Set( 60.0f, 48.0f, 0.0f );
		p_details->m_glow_radius	= 24.0f;
		p_details->m_current_radius	= 0.0f;
		p_details->m_test_radius	= 2.0f;
		p_details->m_radius_growth	= 0.2f;
		sLightGlowDetailsTable.PutItem( (uint32)p_details, p_details );

		p_details = new sLightGlowDetails;
		p_details->m_pos.Set( -60.0f, 48.0f, 0.0f );
		p_details->m_glow_radius	= 24.0f;
		p_details->m_current_radius	= 0.0f;
		p_details->m_test_radius	= 2.0f;
		p_details->m_radius_growth	= 0.2f;
		sLightGlowDetailsTable.PutItem( (uint32)p_details, p_details );
#		endif
	}
}
	
	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void GetPixelShader( sMaterial *p_material, uint32 *p_pixel_shader_id )
{
	if( p_material->m_passes == 1 )
	{
		// There are only 2 shader for single pass materials, depending on whether a texture is required.
		if( p_material->mp_tex[0] == NULL )
		{
			*p_pixel_shader_id = PixelShader1;
		}
		else
		{
			uint32 ignore_bf = p_material->GetIgnoreVertexAlphaPasses();

			if(( p_material->m_reg_alpha[0] & sMaterial::BLEND_MODE_MASK ) == vBLEND_MODE_BRIGHTEN )
			{
				// The single pass mode is brighten, which requires special handling.
				if( ignore_bf == 0 )
					*p_pixel_shader_id = PixelShaderBrighten;
				else
					*p_pixel_shader_id = PixelShaderBrightenIVA;
			}
			else
			{
				if( ignore_bf == 0 )
					*p_pixel_shader_id = PixelShader0;
				else
					*p_pixel_shader_id = PixelShader0IVA;
			}
		}
	}
	else
	{
		// Get the pixel shader.
		*p_pixel_shader_id = get_pixel_shader( p_material );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void set_texture( uint32 pass, IDirect3DTexture8 *p_texture, IDirect3DPalette8 *p_palette )
{
	if((IDirect3DTexture8*)( EngineGlobals.p_texture[pass] ) != p_texture )
	{
		// Use SwitchTexture() whenever possible. Cannot switch from or to a NULL texture. Also cannot
		// switch to a liner texture (in this case the calling code should perform a set_texture( NULL )
		// call first, to force D3DDevice_SetTexture() to be called for the linear texture.
		if(( p_texture != NULL ) && ( EngineGlobals.p_texture[pass] != NULL ))
		{
			EngineGlobals.p_Device->SwitchTexture( pass, (LPDIRECT3DBASETEXTURE8)( p_texture ));
		}
		else
		{
			D3DDevice_SetTexture( pass, (LPDIRECT3DBASETEXTURE8)( p_texture ));
		}

		if( p_palette )
		{
			D3DDevice_SetPalette( pass, p_palette );
		}

		EngineGlobals.p_texture[pass] = p_texture;
	}
}

				
				
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void set_blend_mode( uint32 mode )
{
	if( NxXbox::EngineGlobals.blend_mode_override )
	{
		mode = NxXbox::EngineGlobals.blend_mode_override;
	}

	// Only do something if the blend mode is changing.
	if( mode != EngineGlobals.blend_mode_value )
	{
		// Low 24 bits contain the mode, high 8 bits contain the fixed alpha value.
		if(( mode & 0x00FFFFFFUL ) != ( EngineGlobals.blend_mode_value & 0x00FFFFFFUL ))
		{
			// For additive and subtractive, we set the fog color to black.
			if( EngineGlobals.fog_enabled )
			{
				if((( mode & 0x00FFFFFFUL ) >= vBLEND_MODE_ADD ) && (( mode & 0x00FFFFFFUL ) <= vBLEND_MODE_SUB_FIXED ))
				{
					D3DDevice_SetRenderState( D3DRS_FOGCOLOR, 0x00000000UL );
				}
				else
				{
					D3DDevice_SetRenderState( D3DRS_FOGCOLOR, NxXbox::EngineGlobals.fog_color );
				}
			}

			int blend_op, src_blend, dest_blend;

			switch( mode & 0x00FFFFFFUL )
			{
				case vBLEND_MODE_DIFFUSE:			// ( 0 - 0 ) * 0 + Src
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_ONE;
					dest_blend	= D3DBLEND_ZERO;
					break;				
				}
				case vBLEND_MODE_ADD:				// ( Src - 0 ) * Src + Dst
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_SRCALPHA;
					dest_blend	= D3DBLEND_ONE;
					break;				
				}
				case vBLEND_MODE_ADD_FIXED:			// ( Src - 0 ) * Fixed + Dst
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_CONSTANTALPHA;
					dest_blend	= D3DBLEND_ONE;
					break;				
				}
				case vBLEND_MODE_SUBTRACT:			// ( 0 - Src ) * Src + Dst
				{
					blend_op	= D3DBLENDOP_REVSUBTRACT;
					src_blend	= D3DBLEND_SRCALPHA;
					dest_blend	= D3DBLEND_ONE;
					break;				
				}
				case vBLEND_MODE_SUB_FIXED:			// ( 0 - Src ) * Fixed + Dst
				{
					blend_op	= D3DBLENDOP_REVSUBTRACT;
					src_blend	= D3DBLEND_CONSTANTALPHA;
					dest_blend	= D3DBLEND_ONE;
					break;				
				}
				case vBLEND_MODE_BLEND:				// ( Src - Dst ) * Src + Dst	
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_SRCALPHA;
					dest_blend	= D3DBLEND_INVSRCALPHA;
					break;				
				}
				case vBLEND_MODE_BLEND_FIXED:		// ( Src - Dst ) * Fixed + Dst	
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_CONSTANTALPHA;
					dest_blend	= D3DBLEND_INVCONSTANTALPHA;
					break;				
				}
				case vBLEND_MODE_MODULATE:			// ( Dst - 0 ) * Src + 0
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_ZERO;
					dest_blend	= D3DBLEND_SRCALPHA;
					break;				
				}
				case vBLEND_MODE_MODULATE_FIXED:	// ( Dst - 0 ) * Fixed + 0	
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_ZERO;
					dest_blend	= D3DBLEND_CONSTANTALPHA;
					break;				
				}
				case vBLEND_MODE_BRIGHTEN:			// ( Dst - 0 ) * Src + Dst
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_DESTCOLOR;
					dest_blend	= D3DBLEND_ONE;
					break;				
				}
				case vBLEND_MODE_BRIGHTEN_FIXED:	// ( Dst - 0 ) * Fixed + Dst	
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_DESTCOLOR;
					dest_blend	= D3DBLEND_CONSTANTALPHA;
					break;				
				}
				case vBLEND_MODE_GLOSS_MAP:
				{
					// Treat as diffuse for now.
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_ONE;
					dest_blend	= D3DBLEND_ZERO;
					break;				
				}
				case vBLEND_MODE_MODULATE_COLOR:	// ( Dst - 0 ) * Src(col) + 0 - specially for the shadow.
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_ZERO;
					dest_blend	= D3DBLEND_SRCCOLOR;
					break;				
				}
				case vBLEND_MODE_BLEND_PREVIOUS_MASK:
				{
					// Meaningless unless destination alpha is enabled.
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_DESTALPHA;
					dest_blend	= D3DBLEND_INVDESTALPHA;
					break;
				}
				case vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK:
				{
					// Meaningless unless destination alpha is enabled.
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_INVDESTALPHA;
					dest_blend	= D3DBLEND_DESTALPHA;
					break;
				}
				case vBLEND_MODE_ONE_INV_SRC_ALPHA:
				{
					blend_op	= D3DBLENDOP_ADD;
					src_blend	= D3DBLEND_ONE;
					dest_blend	= D3DBLEND_INVSRCALPHA;
					break;
				}
				default:
				{
					Dbg_Assert( 0 );
					break;
				}
			}

			// Now set the values if they have changed.
			if( blend_op != EngineGlobals.blend_op )
			{
				D3DDevice_SetRenderState( D3DRS_BLENDOP, blend_op );
				EngineGlobals.blend_op	= blend_op;
			}
			if( src_blend != EngineGlobals.src_blend )
			{
				D3DDevice_SetRenderState( D3DRS_SRCBLEND, src_blend );
				EngineGlobals.src_blend	= src_blend;
			}
			if( dest_blend != EngineGlobals.dest_blend )
			{
				D3DDevice_SetRenderState( D3DRS_DESTBLEND, dest_blend );
				EngineGlobals.dest_blend = dest_blend;
			}
		}

		// Change the fixed alpha value if different.
		if(( mode & 0xFF000000UL ) != ( EngineGlobals.blend_mode_value & 0xFF000000UL ))
		{
			uint32 fixed_alpha	= mode & 0xFF000000UL;
			fixed_alpha			= fixed_alpha >= 0x80000000UL ? 0xFF000000UL : ( fixed_alpha << 1 );
			D3DDevice_SetRenderState( D3DRS_BLENDCOLOR,	fixed_alpha );
		}

		// Set the new blend mode value.
		EngineGlobals.blend_mode_value	= mode;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void set_vertex_shader( DWORD shader_id )
{
	if( EngineGlobals.vertex_shader_override == 0 )
	{
		if( EngineGlobals.vertex_shader_id != shader_id )
		{
			// Set vertex shader.
			D3DDevice_SetVertexShader( shader_id );
			EngineGlobals.vertex_shader_id = shader_id;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void set_pixel_shader( uint32 shader_id )
{
	if( EngineGlobals.pixel_shader_override == 0 )
	{
		if( EngineGlobals.pixel_shader_id != shader_id )
		{
			// Set pixel shader.
			D3DDevice_SetPixelShader( shader_id );
			EngineGlobals.pixel_shader_id = shader_id;

			// Changing pixel shader invalidates the constants, so we need to upload.
			EngineGlobals.upload_pixel_shader_constants = true;
		}
			
		// Upload any pixel shader constants if required.
		if( EngineGlobals.upload_pixel_shader_constants && ( shader_id > 0 ))
		{
			D3DDevice_SetPixelShaderConstant( 0, EngineGlobals.pixel_shader_constants, 5 );
		}
	}

	// Want to clear this field even if the override is set, since otherwise it will persist and cause problems later.
	EngineGlobals.upload_pixel_shader_constants = false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void set_pixel_shader( uint32 shader_id, uint32 num_passes )
{
	if( EngineGlobals.pixel_shader_override == 0 )
	{
		if( EngineGlobals.pixel_shader_id != shader_id )
		{
			// Set pixel shader.
			D3DDevice_SetPixelShader( shader_id );
			EngineGlobals.pixel_shader_id = shader_id;

			// Changing pixel shader invalidates the constants, so we need to upload.
			EngineGlobals.upload_pixel_shader_constants = true;
		}
			
		// Upload any pixel shader constants if required.
		if( EngineGlobals.upload_pixel_shader_constants && ( shader_id > 0 ))
		{
			D3DDevice_SetPixelShaderConstant( 0, &EngineGlobals.pixel_shader_constants[0], num_passes );
			D3DDevice_SetPixelShaderConstant( 4, &EngineGlobals.pixel_shader_constants[16], 1 );
		}
	}

	// Want to clear this field even if the override is set, since otherwise it will persist and cause problems later.
	EngineGlobals.upload_pixel_shader_constants = false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void set_render_state( uint32 type, uint32 state )
{
	switch( type )
	{
		case RS_ZBIAS:
		{
			if( state != EngineGlobals.z_bias )
			{
				EngineGlobals.z_bias = state;
				D3DDevice_SetRenderState( D3DRS_ZBIAS, state );
			}
			break;
		}

		case RS_CULLMODE:
		{
			if( state != EngineGlobals.cull_mode )
			{
				EngineGlobals.cull_mode = state;
				D3DDevice_SetRenderState( D3DRS_CULLMODE, state );
			}
			break;
		}

		case RS_ALPHABLENDENABLE:
		{
			if( state != EngineGlobals.alpha_blend_enable )
			{
				EngineGlobals.alpha_blend_enable = state;
				D3DDevice_SetRenderState( D3DRS_ALPHABLENDENABLE, ( state > 0 ) ? TRUE : FALSE );
			}
			break;
		}

		case RS_ALPHATESTENABLE:
		{
			if( state != EngineGlobals.alpha_test_enable )
			{
				EngineGlobals.alpha_test_enable = state;
				D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE, ( state > 0 ) ? TRUE : FALSE );
			}
			break;
		}

		case RS_ZWRITEENABLE:
		{
			if( state > 0 )
			{
				if( EngineGlobals.z_write_enabled == FALSE )
				{
					D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
					EngineGlobals.z_write_enabled = TRUE;
				}
			}
			else
			{
				if( EngineGlobals.z_write_enabled == TRUE )
				{
					D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
					EngineGlobals.z_write_enabled = FALSE;
				}
			}
			break;
		}

		case RS_ZTESTENABLE:
		{
			if( state > 0 )
			{
				if( EngineGlobals.z_test_enabled == FALSE )
				{
					D3DDevice_SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL );
					EngineGlobals.z_test_enabled = TRUE;
				}
			}
			else
			{
				if( EngineGlobals.z_test_enabled == TRUE )
				{
					D3DDevice_SetRenderState( D3DRS_ZFUNC, D3DCMP_ALWAYS );
					EngineGlobals.z_test_enabled = FALSE;
				}
			}
			break;
		}

		case RS_ALPHACUTOFF:
		{
			// Convert from state (where 1 means "render all pixels with alpha 1 or higher") to the D3D.
			// Also, if alpha cutoff is 1 or greater, enable alphakill, which can in some cases provide an earlier
			// rejection of the pixel.
			if( state != EngineGlobals.alpha_ref )
			{
				EngineGlobals.alpha_ref = state;
				if( state > 0 )
				{
					D3DDevice_SetRenderState( D3DRS_ALPHAREF,				state );

					// Enable alpha testing.
					if( EngineGlobals.alpha_test_enable == 0 )
					{
						D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE,	TRUE );
						EngineGlobals.alpha_test_enable = 1;
					}

					D3DDevice_SetTextureStageState( 0, D3DTSS_ALPHAKILL,	D3DTALPHAKILL_ENABLE );
				}
				else
				{
					// Disable alpha testing.
					if( EngineGlobals.alpha_test_enable > 0 )
					{
						D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE,	FALSE );
						EngineGlobals.alpha_test_enable = 0;
					}
					D3DDevice_SetTextureStageState( 0, D3DTSS_ALPHAKILL,	D3DTALPHAKILL_DISABLE );
				}
			}
			break;
		}
		
		case RS_SPECULARENABLE:
		{
			if( state != EngineGlobals.specular_enabled )
			{
				EngineGlobals.specular_enabled = state;

				if( state > 0 )
				{
					D3DDevice_SetRenderState( D3DRS_SPECULARENABLE, TRUE );
					D3DDevice_SetRenderState( D3DRS_LOCALVIEWER, TRUE );
				}
				else
				{
					D3DDevice_SetRenderState( D3DRS_SPECULARENABLE, FALSE );
					D3DDevice_SetRenderState( D3DRS_LOCALVIEWER, FALSE );
				}
			}
			break;
		}

		case RS_FOGENABLE:
		{
			if( state != EngineGlobals.fog_enabled )
			{
				EngineGlobals.fog_enabled = state;
				D3DDevice_SetRenderState( D3DRS_FOGENABLE, ( state > 0 ) ? TRUE : FALSE );
			}
			break;
		}

		case RS_UVADDRESSMODE0:
		case RS_UVADDRESSMODE1:
		case RS_UVADDRESSMODE2:
		case RS_UVADDRESSMODE3:
		{
			int pass = type - RS_UVADDRESSMODE0;
			if(( state & 0xFFFFUL ) != ( EngineGlobals.uv_addressing[pass] & 0xFFFFUL ))
			{
				switch( state & 0xFFFFUL )
				{
					case 0x0000U:
					{
						D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP );
						break;
					}
					case 0x0001U:
					{
						D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP );
						break;
					}
					case 0x0002U:
					{
						D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSU, D3DTADDRESS_BORDER );
						break;
					}
					default:
					{
						Dbg_Assert( 0 );
						break;
					}
				}
				EngineGlobals.uv_addressing[pass] = ( EngineGlobals.uv_addressing[pass] & 0xFFFF0000UL ) | ( state & 0xFFFFUL );
			}

			if(( state & 0xFFFF0000UL ) != ( EngineGlobals.uv_addressing[pass] & 0xFFFF0000UL ))
			{
				switch( state & 0xFFFF0000UL )
				{
					case 0x00000000UL:
					{
						D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP );
						break;
					}
					case 0x00010000UL:
					{
						D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP );
						break;
					}
					case 0x00020000UL:
					{
						D3DDevice_SetTextureStageState( pass, D3DTSS_ADDRESSV, D3DTADDRESS_BORDER );
						break;
					}
					default:
					{
						Dbg_Assert( 0 );
						break;
					}
				}
				EngineGlobals.uv_addressing[pass] = ( EngineGlobals.uv_addressing[pass] & 0x0000FFFFUL ) | ( state & 0xFFFF0000UL );
			}
			break;
		}

		case RS_MIPLODBIASPASS0:
		case RS_MIPLODBIASPASS1:
		case RS_MIPLODBIASPASS2:
		case RS_MIPLODBIASPASS3:
		{
			int pass = type - RS_MIPLODBIASPASS0;
			if( state != EngineGlobals.mip_map_lod_bias[pass] )
			{
				D3DDevice_SetTextureStageState( pass, D3DTSS_MIPMAPLODBIAS, state );
				EngineGlobals.mip_map_lod_bias[pass] = state;
			}
			break;
		}

		case RS_MINMAGFILTER0:
		case RS_MINMAGFILTER1:
		case RS_MINMAGFILTER2:
		case RS_MINMAGFILTER3:
		{
			int pass = type - RS_MINMAGFILTER0;

			// Magnification filter.
			state = state & 0xFFFF0000UL;
			if( state != EngineGlobals.min_mag_filter[pass] )
			{
				if( state == 0x00000000UL )
				{
					// Point.
					D3DDevice_SetTextureStageState( pass, D3DTSS_MAGFILTER, D3DTEXF_POINT );
				}
				else
				{
					// Linear.
					D3DDevice_SetTextureStageState( pass, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
				}
				EngineGlobals.min_mag_filter[pass] = state;
			}
			break;
		}
	}
}











/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void create_texture_projection_details( sTexture *p_texture, Nx::CXboxModel *p_model, sScene *p_scene )
{
	sTextureProjectionDetails *p_details = new sTextureProjectionDetails;

	p_details->p_model		= p_model;
	p_details->p_scene		= p_scene;
	p_details->p_texture	= p_texture;
	
	XGMatrixIdentity( &p_details->view_matrix );
	XGMatrixIdentity( &p_details->projection_matrix );
	
	pTextureProjectionDetailsTable->PutItem((uint32)p_texture, p_details );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void destroy_texture_projection_details( sTexture *p_texture )
{
	sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->GetItem((uint32)p_texture );
	if( p_details )
	{
		pTextureProjectionDetailsTable->FlushItem((uint32)p_texture );
		delete p_details;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void set_texture_projection_camera( sTexture *p_texture, XGVECTOR3 *p_pos, XGVECTOR3 *p_at )
{
	sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->GetItem((uint32)p_texture );
	if( p_details )
	{
		// Check for 'straight down' vector.
		if(( p_pos->x == p_at->x ) && ( p_pos->z == p_at->z ))
		{
			XGMatrixLookAtRH( &p_details->view_matrix, p_pos, p_at, &XGVECTOR3( 0.0f, 0.0f, 1.0f ));
		}
		else
		{
			XGMatrixLookAtRH( &p_details->view_matrix, p_pos, p_at, &XGVECTOR3( 0.0f, 1.0f, 0.0f ));
		}
		XGMatrixOrthoRH( &p_details->projection_matrix, 96.0f, 96.0f, 1.0f, 128.0f );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// MSM PERFCHANGE - added scale.
void set_camera( Mth::Matrix *p_matrix, Mth::Vector *p_position, float screen_angle, float aspect_ratio, bool render_at_infinity )
{
	EngineGlobals.cam_position.x	= p_position->GetX();
	EngineGlobals.cam_position.y	= p_position->GetY();
	EngineGlobals.cam_position.z	= p_position->GetZ();
	
	EngineGlobals.cam_at.x			= p_matrix->GetAt().GetX();
	EngineGlobals.cam_at.y			= p_matrix->GetAt().GetY();
	EngineGlobals.cam_at.z			= p_matrix->GetAt().GetZ();
	
	EngineGlobals.cam_up.x			= p_matrix->GetUp().GetX();
	EngineGlobals.cam_up.y			= p_matrix->GetUp().GetY();
	EngineGlobals.cam_up.z			= p_matrix->GetUp().GetZ();
	
	XGMatrixIdentity( &EngineGlobals.world_matrix );

	// XGMatrixLookAtRH() takes an 'at' position rather than a direction, so we need it relative to the camera position.
	XGVECTOR3	at;
	at.x	= EngineGlobals.cam_position.x - EngineGlobals.cam_at.x;
	at.y	= EngineGlobals.cam_position.y - EngineGlobals.cam_at.y;
	at.z	= EngineGlobals.cam_position.z - EngineGlobals.cam_at.z;
	XGMatrixLookAtRH( &EngineGlobals.view_matrix, &EngineGlobals.cam_position, &at, &EngineGlobals.cam_up );

	EngineGlobals.near_plane	= 2.0f;
	EngineGlobals.far_plane		= 32000.0f;
	EngineGlobals.screen_angle	= screen_angle;

	// Figure width and height of viewport at near clip plane.
	float half_screen_angle_in_radians	= Mth::DegToRad( screen_angle * 0.5f );
	float width							= EngineGlobals.near_plane * 2.0f * tanf( half_screen_angle_in_radians );

	if( EngineGlobals.backbuffer_width == 640.0f )
	{
		// We need to adjust the aspect ratio for the Xbox, since it is now rendering with D3DPRESENTFLAG_10X11PIXELASPECTRATIO
		// set. This changes the regular aspect ratio from 4:3 to 4:3.3, so adjust the value here.
		aspect_ratio = aspect_ratio * (( 4.0f / 3.3f ) / ( 4.0f / 3.0f ));
	}
	
	float height	= width / aspect_ratio;
	XGMatrixPerspectiveRH( &EngineGlobals.projection_matrix, width, height, EngineGlobals.near_plane, EngineGlobals.far_plane );
	
	NxXbox::EngineGlobals.near_plane_width	= width;
	NxXbox::EngineGlobals.near_plane_height	= height;

	if( render_at_infinity )
	{
		// Rendering the sky, so set the projection transform up to calculate a constant z value of 1.0.
		// W value must remain correct however.
		EngineGlobals.projection_matrix.m[2][2] = -0.999999f;	// Setting this to -1.0f causes D3D to complain about WNear calculation.
		EngineGlobals.projection_matrix.m[3][2] =  0.0f;
	}
	
	D3DDevice_SetTransform( D3DTS_WORLD, &EngineGlobals.world_matrix );
	D3DDevice_SetTransform( D3DTS_VIEW, &EngineGlobals.view_matrix );
	D3DDevice_SetTransform( D3DTS_PROJECTION, &EngineGlobals.projection_matrix );

	// Set up view frustum values for bounding sphere culling.
	EngineGlobals.ViewFrustumTX	= tanf( Mth::DegToRad( screen_angle * 0.5f ));
	EngineGlobals.ViewFrustumTY	= -( EngineGlobals.ViewFrustumTX / aspect_ratio );
	EngineGlobals.ViewFrustumSX	= 1.0f / sqrtf( 1.0f + 1.0f / ( EngineGlobals.ViewFrustumTX * EngineGlobals.ViewFrustumTX ));
	EngineGlobals.ViewFrustumSY	= 1.0f / sqrtf( 1.0f + 1.0f / ( EngineGlobals.ViewFrustumTY * EngineGlobals.ViewFrustumTY ));
	EngineGlobals.ViewFrustumCX	= 1.0f / sqrtf( 1.0f + EngineGlobals.ViewFrustumTX * EngineGlobals.ViewFrustumTX );
	EngineGlobals.ViewFrustumCY	= 1.0f / sqrtf( 1.0f + EngineGlobals.ViewFrustumTY * EngineGlobals.ViewFrustumTY );

	// Set up matrix for offset bump mapping (the matrix that will be used to set the D3DTSS_BUMPENVMATnn texture states).
    float rotate_angle = atan2f( -EngineGlobals.cam_at.z, -EngineGlobals.cam_at.x );
	XGMatrixRotationY( &EngineGlobals.bump_env_matrix, rotate_angle - D3DX_PI / 2 );

	// Calculate vectors for billboard rendering.
	BillboardManager.SetCameraMatrix();
}



/******************************************************************/
/* Quick determination of if something is visible or not, uses	  */
/* the previously calculated s and c vectors and the			  */
/* WorldToCamera transform (note, no attempt is made to ensure	  */
/* this is the same camera that the object will eventually be	  */
/* rendered with.												  */
/******************************************************************/
bool IsVisible( Mth::Vector ¢er, float radius )
{
	XGVECTOR4 test_out;

	XGVec3Transform( &test_out, (XGVECTOR3*)¢er[X], (XGMATRIX*)&EngineGlobals.view_matrix );

	if( -test_out.z + radius < EngineGlobals.near_plane )
		return false;

	float sx_z	= EngineGlobals.ViewFrustumSX * test_out.z;
	float cx_x	= EngineGlobals.ViewFrustumCX * test_out.x;
	if(( radius < sx_z - cx_x ) || ( radius < sx_z + cx_x ))
		return false;

	float sy_z	= EngineGlobals.ViewFrustumSY * test_out.z;
	float cy_y	= EngineGlobals.ViewFrustumCY * test_out.y;
	if(( radius < sy_z + cy_y ) || ( radius < sy_z - cy_y ))
		return false;

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void set_frustum_bbox_transform( Mth::Matrix *p_transform )
{
	if( p_transform == NULL )
	{
		p_bbox_transform = NULL;
	}
	else
	{
		p_bbox_transform		= &bbox_transform;

		bbox_transform.m[0][0]	= ( *p_transform ).GetRight().GetX();
		bbox_transform.m[0][1]	= ( *p_transform ).GetRight().GetY();
		bbox_transform.m[0][2]	= ( *p_transform ).GetRight().GetZ();
		bbox_transform.m[0][3]	= 0.0f;

		bbox_transform.m[1][0]	= ( *p_transform ).GetUp().GetX();
		bbox_transform.m[1][1]	= ( *p_transform ).GetUp().GetY();
		bbox_transform.m[1][2]	= ( *p_transform ).GetUp().GetZ();
		bbox_transform.m[1][3]	= 0.0f;

		bbox_transform.m[2][0]	= ( *p_transform ).GetAt().GetX();
		bbox_transform.m[2][1]	= ( *p_transform ).GetAt().GetY();
		bbox_transform.m[2][2]	= ( *p_transform ).GetAt().GetZ();
		bbox_transform.m[2][3]	= 0.0f;

		bbox_transform.m[3][0]	= p_transform->GetPos().GetX();
		bbox_transform.m[3][1]	= p_transform->GetPos().GetY();
		bbox_transform.m[3][2]	= p_transform->GetPos().GetZ();
		bbox_transform.m[3][3]	= 1.0f;
	}
}



float boundingSphereNearestZ = 0.0f;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float get_bounding_sphere_nearest_z( void )
{
	return boundingSphereNearestZ;
}



/******************************************************************/
/*                                                                */
/* Checks a bounding sphere against the current view frustum	  */
/* (ignoring far clipping). Returns true if any part is visible.  */
/* Timings suggest this method runs on average around ~0.25us,    */
/* faster than test code doing world space culling against a set  */
/* of plane equations representing the view frustum in world	  */
/* space.														  */
/*                                                                */
/******************************************************************/
bool frustum_check_sphere( D3DXVECTOR3 *p_center, float radius )
{
	XGVECTOR4	test_out;

	// Build the composite transform if required.
	if( p_bbox_transform )
	{
		// Object to world.
		test_out.x = p_center->x + p_bbox_transform->_41;
		test_out.y = p_center->y + p_bbox_transform->_42;
		test_out.z = p_center->z + p_bbox_transform->_43;
//		XGVec3Transform( &test_out, (XGVECTOR3*)p_center, p_bbox_transform );

		// World to view.
		XGVec3Transform( &test_out, (XGVECTOR3*)&test_out, (XGMATRIX*)&EngineGlobals.view_matrix );
	}
	else
	{
		// World to view.
		XGVec3Transform( &test_out, (XGVECTOR3*)p_center, (XGMATRIX*)&EngineGlobals.view_matrix );
	}
		
	boundingSphereNearestZ = -test_out.z - radius;

	if( -test_out.z + radius < EngineGlobals.near_plane )
		return false;

	float sx_z	= EngineGlobals.ViewFrustumSX * test_out.z;
	float cx_x	= EngineGlobals.ViewFrustumCX * test_out.x;
	if(( radius < sx_z - cx_x ) || ( radius < sx_z + cx_x ))
		return false;

	float sy_z	= EngineGlobals.ViewFrustumSY * test_out.z;
	float cy_y	= EngineGlobals.ViewFrustumCY * test_out.y;
	if(( radius < sy_z + cy_y ) || ( radius < sy_z - cy_y ))
		return false;

	return true;
}




/******************************************************************/
/*                                                                */
/* Checks a bounding box against the current view frustum		  */
/* (ignoring far clipping). Returns true if any part is visible.  */
/*                                                                */
/******************************************************************/
bool frustum_check_box( Mth::CBBox *p_bbox )
{
	XGVECTOR3	test_in, test_out;
	XGVECTOR4	test_mid;
	
	uint32	cumulative_projection_space_outcode	= 0xFF;
	float	min_x = p_bbox->GetMin().GetX();
	float	min_y = p_bbox->GetMin().GetY();
	float	min_z = p_bbox->GetMin().GetZ();
	float	max_x = p_bbox->GetMax().GetX();
	float	max_y = p_bbox->GetMax().GetY();
	float	max_z = p_bbox->GetMax().GetZ();

	for( uint32 v = 0; v < 8; ++v )
	{
		uint32 projection_space_outcode = 0;

		test_in.x = ( v & 0x04 ) ? max_x : min_x;
		test_in.y = ( v & 0x02 ) ? max_y : min_y;
		test_in.z = ( v & 0x01 ) ? max_z : min_z;

		if( p_bbox_transform )
		{
			XGVec3Transform( &test_mid, &test_in, p_bbox_transform );
			test_in.x = test_mid.x;
			test_in.y = test_mid.y;
			test_in.z = test_mid.z;
		}
		
		XGVec3Transform( &test_mid, &test_in, &EngineGlobals.view_matrix );
		test_in.x = test_mid.x;
		test_in.y = test_mid.y;
		test_in.z = test_mid.z;
		
		// Do z-checking here.
		if( -test_mid.z < EngineGlobals.near_plane )
		{
			// Behind the camera near plane.
			projection_space_outcode |= 0x10;
		}
		else if( -test_mid.z > EngineGlobals.far_plane )
		{
			// Beyond the camera far plane.
			projection_space_outcode |= 0x20;
		}
		
		// At this point it's important to check to see whether the point is in postive or negative z-space, since
		// after the projection transform, both very large camera space z values and camera space z values where z < 0
		// will give results with z > 1. (Camera space values in the range [0,near] give negative projection space z values).
		XGVec3TransformCoord( &test_out, &test_in, &EngineGlobals.projection_matrix );

		if(( -test_mid.z < 0.0f ) && ( !EngineGlobals.is_orthographic ))
		{
			test_out.x = -test_out.x;
			test_out.y = -test_out.y;
		}

		if( test_out.x > 1.0f )
			projection_space_outcode |= 0x01;
		else if( test_out.x < -1.0f )
			projection_space_outcode |= 0x02;

		if( test_out.y > 1.0f )
			projection_space_outcode |= 0x04;
		else if( test_out.y < -1.0f )
			projection_space_outcode |= 0x08;

		cumulative_projection_space_outcode	&= projection_space_outcode;

		if( cumulative_projection_space_outcode == 0 )
		{
			// Early out.
			return true;
		}
	}
	return false;
}



struct sSortedMeshEntry
{
	sMesh				*p_mesh;
	float				sort;
	sSortedMeshEntry	*pNext;
};


static sSortedMeshEntry	sortedMeshArray[1000];


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void calculate_tex_proj_matrix( XGMATRIX *p_tex_view_matrix, XGMATRIX *p_tex_proj_matrix, XGMATRIX *p_tex_transform_matrix, XGMATRIX *p_world_matrix )
{
	// Get the current view matrix.
	XGMATRIX matView, matInvView;
	D3DDevice_GetTransform( D3DTS_VIEW, (XGMATRIX*)&matView );
	XGMatrixInverse( &matInvView,  NULL, &matView );

	XGMATRIX matBiasScale;
    XGMatrixIdentity( &matBiasScale );

	static float x0 = 256.0f;
	static float y0 = 256.0f;
	
	matBiasScale._11 = x0 * 0.5f;
	matBiasScale._22 = y0 * -0.5f;
	matBiasScale._33 = D3DZ_MAX_D24S8;

	static float x1 = 256.0f;
	static float y1 = 256.0f;
	
	matBiasScale._41 = x1 * 0.5f + 0.5f;
	matBiasScale._42 = y1 * 0.5f + 0.5f;

	XGMATRIX m_matTexProj;

	// Don't bother with inverse view transform for Shadow Buffer, since we are picking up world-space coordinates directly.
	if( p_world_matrix )
	{
		m_matTexProj = *p_world_matrix;												// Transform to world space.
		XGMatrixMultiply( &m_matTexProj, &m_matTexProj, p_tex_view_matrix );		// Transform to projection camera space.
	}
	else
	{
		m_matTexProj = *p_tex_view_matrix;											// Transform to projection camera space.
	}
	XGMatrixMultiply( &m_matTexProj, &m_matTexProj, p_tex_proj_matrix );			// Situate verts relative to projector's view
    XGMatrixMultiply( p_tex_transform_matrix, &m_matTexProj, &matBiasScale );		// Scale and bias to map the near clipping plane to texcoords
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_shadow_targets( void )
{
	XGMATRIX	stored_view_matrix			= EngineGlobals.view_matrix;
	XGMATRIX	stored_projection_matrix	= EngineGlobals.projection_matrix;
	uint32		stored_fog_state			= EngineGlobals.fog_enabled;
	DWORD		multisample_mode;

	// Get multisample mode.
	D3DDevice_GetRenderState( D3DRS_MULTISAMPLEMODE, &multisample_mode );

	// Disable fogging.
	set_render_state( RS_FOGENABLE, 0 );

	// Goes through the list of render target textures, rendering to each one in turn.
	pTextureProjectionDetailsTable->IterateStart();
	sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->IterateNext();
	
	D3DSurface fake_target;

	while( p_details )
	{
		if( p_details->p_model )
		{
			// Setup dummy color buffer (bad things will happen if you write to it).
			// The XGSetSurfaceHeader() function is slow, these values are now set explicitly from the observed values
			// set by the function.
//			ZeroMemory( &fake_target, sizeof( fake_target ));
//			XGSetSurfaceHeader( 256, 256, D3DFMT_LIN_R5G6B5, &fake_target, 0, 0 );
			fake_target.Common	= 0x00050001UL;
			fake_target.Data	= 0x00000000UL;
			fake_target.Lock	= 0x00000000UL;
			fake_target.Format	= 0x00011129UL;
			fake_target.Size	= 0x070FF0FFUL;
			fake_target.Parent	= 0x00000000UL;
			
			// Set the new render target.
			LPDIRECT3DSURFACE8 pSurface;

			// This call will increase the reference count of the IDirect3DTexture8 object.
			p_details->p_texture->pD3DTexture->GetSurfaceLevel( 0, &pSurface );

			// This call will increase the reference count of the IDirect3DSurface8 object.
			D3DDevice_SetRenderTarget( &fake_target, pSurface );
			
			// Clear the target.
			D3DDevice_Clear( 0, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 1.0f, 0 ); 
			
			// Disable color writes.
			D3DDevice_SetRenderState( D3DRS_COLORWRITEENABLE, 0 );

			// Turn on z-offset.
			D3DDevice_SetRenderState( D3DRS_SOLIDOFFSETENABLE,			TRUE );
			D3DDevice_SetRenderState( D3DRS_POLYGONOFFSETZOFFSET,		FtoDW( 4.0f ));
			D3DDevice_SetRenderState( D3DRS_POLYGONOFFSETZSLOPESCALE,	FtoDW( 2.0f ));

			// Performance optimiser suggested change.
			// Priority 2: Set D3DRS_MULTISAMPLEMODE to D3DMULTISAMPLEMODE_4X for faster fill rate with no quality loss when depth only rendering.
			D3DDevice_SetRenderState( D3DRS_MULTISAMPLEMODE,			D3DMULTISAMPLEMODE_4X );

			// Set the view and projection transforms.
			EngineGlobals.view_matrix		= p_details->view_matrix;
			EngineGlobals.projection_matrix	= p_details->projection_matrix;
			EngineGlobals.is_orthographic	= true;

			// Render all instances for the CGeom's contained in this model.
			int num_geoms = p_details->p_model->GetNumGeoms();
			for( int i = 0; i < num_geoms; ++i )
			{
				Nx::CXboxGeom *p_xbox_geom	= static_cast( p_details->p_model->GetGeomByIndex( i ));
				CInstance *p_instance		= p_xbox_geom->GetInstance();
				
				if( p_instance->GetActive())
				{
					// Flag the scene as having the shadow version rendered.
					p_instance->GetScene()->m_flags |= SCENE_FLAG_RENDERING_SHADOW;

					// Render the model.
					render_instance( p_instance, vRENDER_NO_CULLING );

					// Clear the flag the scene as having the shadow version rendered.
					p_instance->GetScene()->m_flags &= ~SCENE_FLAG_RENDERING_SHADOW;
			
					// Flag the scene as self shadowing.
					p_instance->GetScene()->m_flags |= SCENE_FLAG_SELF_SHADOWS;
				}
			}
			pSurface->Release();
		}
		p_details = pTextureProjectionDetailsTable->IterateNext();
	}

	// Restore important states.
	D3DDevice_SetRenderState( D3DRS_COLORWRITEENABLE,	D3DCOLORWRITEENABLE_ALL );
	D3DDevice_SetRenderState( D3DRS_SOLIDOFFSETENABLE,	FALSE );
	D3DDevice_SetRenderState( D3DRS_MULTISAMPLEMODE,	multisample_mode );
	set_render_state( RS_FOGENABLE, stored_fog_state );

	// Pixel shader override no longer required.
	EngineGlobals.pixel_shader_override = 0;
	
	// Restore the view and projection transforms.
	EngineGlobals.view_matrix		= stored_view_matrix;
	EngineGlobals.projection_matrix	= stored_projection_matrix;
	EngineGlobals.is_orthographic	= false;

	// It's important to set the internal reference count of the dummy color surface here, otherwise
	// the debug version of D3D will complain when it attempts to reduce the internal ref count during
	// the subsequent SetRenderTarget() call.
	fake_target.Common	= 0x000D0001UL;

	// Restore the default render target.
	D3DDevice_SetRenderTarget( EngineGlobals.p_RenderSurface, EngineGlobals.p_ZStencilSurface );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_shadow_meshes( sScene *p_scene, sMesh **p_mesh_indices, int num_meshes )
{
	// No anisotropic filtering for the base texture.
	DWORD stage_zero_minfilter;
	D3DDevice_GetTextureStageState( 0, D3DTSS_MINFILTER, &stage_zero_minfilter );
	D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
	
	// Disable fogging.
	uint32 stored_fog_state = EngineGlobals.fog_enabled;
	set_render_state( RS_FOGENABLE, 0 );

	// Scan through each entry in the TextureProjectionDetails table, and see whether it relates to this scene.
	pTextureProjectionDetailsTable->IterateStart();
	sTextureProjectionDetails *p_details = pTextureProjectionDetailsTable->IterateNext();
	while( p_details )
	{
		XGMATRIX	stored_view_matrix			= EngineGlobals.view_matrix;
		XGMATRIX	stored_projection_matrix	= EngineGlobals.projection_matrix;
			
		// Calculate the projection matrix that will project world coordinates into our shadow buffer.
		calculate_tex_proj_matrix( &p_details->view_matrix, &p_details->projection_matrix, &p_details->texture_projection_matrix );

		// Set the vertex shader.
		set_vertex_shader( ShadowBufferStaticGeomVS );

		// Set the pixel shader that just does straight texturing.
		set_pixel_shader( ShadowBufferStaticGeomPS );

		set_render_state( RS_ALPHACUTOFF, 1 );
		
		// Set the other textures to NULL.
		set_texture( 0, NULL );
		set_texture( 2, NULL );
		set_texture( 3, NULL );

		// Need to set this texture NULL first, to flush the texture state, since the incoming texture is linear.
		set_texture( 1, NULL );

		// Set the projected texture (as the second-pass texture).
		if( p_details->p_texture->pD3DSurface )
			set_texture( 1, (IDirect3DTexture8*)( p_details->p_texture->pD3DSurface ));
		else
			set_texture( 1, p_details->p_texture->pD3DTexture );

		// Set shadowbuffer texture details.
		set_render_state( RS_UVADDRESSMODE1, 0x00020002UL );					// Set (border,border) addressing.
		D3DDevice_SetTextureStageState( 1, D3DTSS_BORDERCOLOR, 0xffffffff );
		D3DDevice_SetTextureStageState( 1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
		D3DDevice_SetTextureStageState( 1, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
		
		// Set shadowbuffer state.
		D3DDevice_SetRenderState( D3DRS_SHADOWFUNC, D3DCMP_GREATEREQUAL );
			
		// Upload constants to the vertex shader for composite world->view->projection transform (c0 - c3) and
		// world->texture transform (c4 - c7).
		XGMATRIX	dest_matrix;
		XGMATRIX	temp_matrix;
		XGMATRIX	projMatrix;
		XGMATRIX	viewMatrix;
		XGMATRIX	worldMatrix;
		XGMATRIX	texProjMatrix;

		// Texture projection matrix.
		XGMatrixTranspose( &texProjMatrix, &p_details->texture_projection_matrix );
		
		// Projection matrix.
		XGMatrixTranspose( &projMatrix, &EngineGlobals.projection_matrix );
	
		// View matrix.
		XGMatrixTranspose( &viewMatrix, &EngineGlobals.view_matrix );
		viewMatrix.m[3][0] = 0.0f;
		viewMatrix.m[3][1] = 0.0f;
		viewMatrix.m[3][2] = 0.0f;
		viewMatrix.m[3][3] = 1.0f;
	
		// World space transformation matrix.
		XGMatrixIdentity( &worldMatrix );

		// Calculate composite world->view->projection matrix.
		XGMatrixMultiply( &temp_matrix, &viewMatrix, &worldMatrix );
		XGMatrixMultiply( &dest_matrix, &projMatrix, &temp_matrix );

		// Load up the combined world, camera & projection matrix, and the tetxure transform matrix.
		D3DDevice_SetVertexShaderConstantFast( 0, (void*)&dest_matrix, 4 );
		D3DDevice_SetVertexShaderConstantFast( 4, (void*)&texProjMatrix, 4 );

		// Turn on z-offset.
		D3DDevice_SetRenderState( D3DRS_SOLIDOFFSETENABLE,			TRUE );
		D3DDevice_SetRenderState( D3DRS_POLYGONOFFSETZOFFSET,		FtoDW( -4.0f ));
		D3DDevice_SetRenderState( D3DRS_POLYGONOFFSETZSLOPESCALE,	FtoDW( -2.0f ));

		// Set the blend mode to modulate, using the texture color.
		set_blend_mode( vBLEND_MODE_MODULATE_COLOR );

		// Set up the correct view and projection matrix for frustum culling.
		EngineGlobals.view_matrix		= p_details->view_matrix;
		EngineGlobals.projection_matrix	= p_details->projection_matrix;
		EngineGlobals.is_orthographic	= true;

		// Draw the meshes.
		for( int i = 0; i < num_meshes; ++i )
		{
			sMesh *p_mesh = p_mesh_indices[i];
				
			// Check this mesh is okay for shadow rendering.
			if( !( p_mesh->m_flags & sMesh::MESH_FLAG_NO_SKATER_SHADOW ))
			{
				// Cull this mesh against the second view frustum.
				if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
				{
//					if( frustum_check_box( &p_mesh->m_bbox ))
					{
						// Here we want to set up texture 0 as per the material on the mesh. This way we can use it as an alpha
						// mask to avoid drawing the shadow on transparent pixels.
						if( p_mesh->mp_material->mp_tex[0] )
						{
							set_texture( 0, p_mesh->mp_material->mp_tex[0]->pD3DTexture );
							set_render_state( RS_UVADDRESSMODE0, p_mesh->mp_material->m_uv_addressing[0] );
						}

						// Draw the mesh.
						D3DDevice_SetStreamSource( 0, p_mesh->mp_vertex_buffer[0], p_mesh->m_vertex_stride );
						D3DDevice_DrawIndexedVertices( p_mesh->m_primitive_type, p_mesh->m_num_indices[0], p_mesh->mp_index_buffer[0] );
					}
				}
			}
		}

		// Restore the view and projection transforms.
		EngineGlobals.view_matrix		= stored_view_matrix;
		EngineGlobals.projection_matrix	= stored_projection_matrix;
		EngineGlobals.is_orthographic	= false;

		p_details = pTextureProjectionDetailsTable->IterateNext();
	}

	// Turn off z-offset.
	D3DDevice_SetRenderState( D3DRS_SOLIDOFFSETENABLE, FALSE );
	D3DDevice_SetRenderState( D3DRS_SHADOWFUNC, D3DCMP_NEVER );
	set_pixel_shader( 0 );

	// Restore anisotropic filtering if present.
	D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, stage_zero_minfilter );

	// Restore fogging if present.
	set_render_state( RS_FOGENABLE, stored_fog_state );

	// Reflush linear texture.
	set_texture( 1, NULL );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int cmp( const void *p1, const void *p2 )
{
	return((sSortedMeshEntry*)p1)->sort < ((sSortedMeshEntry*)p2)->sort ? -1 : ((sSortedMeshEntry*)p1)->sort > ((sSortedMeshEntry*)p2)->sort ? 1 : 0;
}



static bool debug_shadow_volumes = false;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_shadow_volumes( sScene *p_scene, uint32 viewport )
{
	// Switch viewport from value to bitfield value.
	viewport = ( 1 << viewport );
	
	NxXbox::set_pixel_shader( PixelShader5 );
	EngineGlobals.pixel_shader_override	= PixelShader5;
	NxXbox::set_texture( 0, NULL );
	NxXbox::set_render_state( RS_ZWRITEENABLE,	0 );
	NxXbox::set_render_state( RS_ZTESTENABLE,	1 );
	NxXbox::set_render_state( RS_ALPHACUTOFF,	0 );

	if( debug_shadow_volumes == false )
		D3DDevice_SetRenderState( D3DRS_ZFUNC,		D3DCMP_GREATEREQUAL );

	// Render all meshes.
	for( int e = 0; e < p_scene->m_num_mesh_entries; ++e )
	{
		sMesh *p_mesh = p_scene->m_meshes[e];
		if(( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && ( p_mesh->m_flags & sMesh::MESH_FLAG_SHADOW_VOLUME ))
		{
			// Frustum cull this mesh, using the associated bounding sphere.
			if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
			{
				if( debug_shadow_volumes == false )
				{
					NxXbox::set_render_state( RS_CULLMODE, D3DCULL_CW );
					NxXbox::set_blend_mode( 0x10000000UL | NxXbox::vBLEND_MODE_ADD_FIXED );
					p_mesh->Submit();

					NxXbox::set_render_state( RS_CULLMODE, D3DCULL_CCW );
					NxXbox::set_blend_mode( 0x10000000UL | NxXbox::vBLEND_MODE_SUB_FIXED );
					p_mesh->Submit();
				}
				else
				{
					NxXbox::set_render_state( RS_CULLMODE, D3DCULL_NONE );
					NxXbox::set_blend_mode( 0x30000000UL | NxXbox::vBLEND_MODE_BLEND_FIXED );
					p_mesh->Submit();
				}
			}
		}
	}

	EngineGlobals.pixel_shader_override	= 0;
	NxXbox::set_render_state( RS_ZWRITEENABLE,	1 );
	NxXbox::set_render_state( RS_CULLMODE,		D3DCULL_CW );
	D3DDevice_SetRenderState( D3DRS_ZFUNC,		D3DCMP_LESSEQUAL );
}


#define			VISIBLE_MESH_ARRAY_SIZE		4096
static sMesh	*visible_mesh_array[VISIBLE_MESH_ARRAY_SIZE];

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_scene( sScene *p_scene, uint32 flags, uint32 viewport )
{
	sMaterial		*p_material					= NULL;
	bool			no_culling					= ( flags & vRENDER_NO_CULLING ) > 0 ;
	bool			render;
	int				visible_mesh_array_index	= 0;

	// Don't render dictionary scenes.
	if( p_scene->m_is_dictionary )
	{
		return;
	}
	
	if( flags & vRENDER_SHADOW_VOLUMES )
	{
		render_shadow_volumes( p_scene, viewport );
		return;
	}

	// Disallow front to back sorting if the number of opaque meshes is larger than the visible mesh array.
	if( p_scene->m_first_semitransparent_entry >= VISIBLE_MESH_ARRAY_SIZE )
	{
		flags &= ~vRENDER_SORT_FRONT_TO_BACK;
	}

	// Switch viewport from value to bitfield value.
	viewport = ( 1 << viewport );
	
	// Render opaque meshes.
	if( flags & vRENDER_OPAQUE )
	{
		for( int e = 0; e < p_scene->m_first_semitransparent_entry; ++e )
		{
			sMesh *p_mesh = p_scene->m_meshes[e];

			__asm mov eax, p_mesh			// Store mesh pointer.
			__asm prefetcht0 [eax]			// Get first 32 bytes of sMesh structure.
			
			if(( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && (( p_mesh->m_flags & ( sMesh::MESH_FLAG_SHADOW_VOLUME | sMesh::MESH_FLAG_BILLBOARD )) == 0 ))
			{
				if( no_culling )
				{
					render = true;
				}
				else
				{
					render = false;

					// Check the visibility mask.
					if( p_mesh->m_visibility_mask & viewport )
					{
						// Frustum cull this set of meshes, using the associated bounding sphere and bounding box.
						if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
						{
							// Check against any occluders.
							if(( !( flags & vRENDER_OCCLUDED )) || ( !TestSphereAgainstOccluders( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius )))
							{
								render = true;
							}
						}
					}
				}

				// Draw this mesh if we decided it is visible.
				if( render )
				{
					if(( flags & vRENDER_SORT_FRONT_TO_BACK ) == 0 )
					{
						// If the material has changed, submit the new material.
						if( p_mesh->mp_material != p_material )
						{
							p_material = p_mesh->mp_material;
							p_material->Submit();
						}
						p_mesh->Submit();
					}
					else
					{
						// If this mesh is within the 'near' section, render it now. Meshes in the far section will be
						// deferred for rendering later on, which should gain some benefit from the fast pixel occlusion.
						if( boundingSphereNearestZ <= FRONT_TO_BACK_SORT_CUTOFF )
						{
							if( p_mesh->mp_material != p_material )
							{
								p_material = p_mesh->mp_material;
								p_material->Submit();
							}
							p_mesh->Submit();
						}

						// Set the nearest point of the bounding sphere.
						p_mesh->m_bounding_sphere_nearest_z = boundingSphereNearestZ;
					}

					// Add this mesh to the visible list, providing it is within bounds.
					visible_mesh_array[visible_mesh_array_index] = p_mesh;
					if( visible_mesh_array_index < ( VISIBLE_MESH_ARRAY_SIZE - 1 ))
						++visible_mesh_array_index;
				}
			}
		}			
		
		if(( flags & vRENDER_SORT_FRONT_TO_BACK ) && ( visible_mesh_array_index > 0 ))
		{
			// At this stage we have an array of meshes, some of which may not yet have been rendered.
			// At this point simply scan through the list twice, drawing all 'far' meshes.
			for( int vm = 0; vm < visible_mesh_array_index; ++vm )
			{
				sMesh *p_sorted_mesh = visible_mesh_array[vm];

				if( p_sorted_mesh->m_bounding_sphere_nearest_z > FRONT_TO_BACK_SORT_CUTOFF )
				{				
					// If the material has changed, submit the new material.
					if( p_sorted_mesh->mp_material != p_material )
					{
						p_material = p_sorted_mesh->mp_material;
						p_material->Submit();
					}
					p_sorted_mesh->Submit();
				}
			}
		}

		// Now draw the opaque meshes with shadow mapped on them.
		if( p_scene->m_flags & SCENE_FLAG_RECEIVE_SHADOWS )
		{
			set_render_state( RS_ZWRITEENABLE,	0 );
			DWORD min_filter;
			D3DDevice_GetTextureStageState( 0, D3DTSS_MINFILTER, &min_filter );
			if( min_filter == D3DTEXF_ANISOTROPIC )
			{
				D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
			}
		
			render_shadow_meshes( p_scene, visible_mesh_array, visible_mesh_array_index );

			if( min_filter == D3DTEXF_ANISOTROPIC )
			{
				D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_ANISOTROPIC );
			}
			set_render_state( RS_ZWRITEENABLE,	1 );
		}

		// Reset mesh array
		visible_mesh_array_index = 0;

		if( flags & vRENDER_BILLBOARDS )
		{
			BillboardManager.Render( vRENDER_OPAQUE );
		}
	}
	
	if( flags & vRENDER_SEMITRANSPARENT )
	{
		int e						= p_scene->m_first_semitransparent_entry;
		int next_sorted_mesh_entry	= 0;
		
		// Semitransparent rendering is done in three stages.
		// The first stage is meshes in the list up to the point where dynamic sorting starts.
		// The second stage is meshes in the list which use dynamic sorting.
		// The third stage is meshes in the list beyond the point where dynamic sorting ends.
		for( ; e < p_scene->m_first_dynamic_sort_entry; ++e )
		{
			sMesh *p_mesh = p_scene->m_meshes[e];

			__asm mov eax, p_mesh			// Store mesh pointer.
			__asm prefetcht0 [eax]			// Get first 32 bytes of sMesh structure.

			if(( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && (( p_mesh->m_flags & ( sMesh::MESH_FLAG_SHADOW_VOLUME | sMesh::MESH_FLAG_BILLBOARD )) == 0 ))
			{
				if( no_culling )
				{
					render = true;
				}
				else
				{
					render = false;

					// Check the visibility mask.
					if( p_mesh->m_visibility_mask & viewport )
					{
						// Frustum cull this set of meshes, using the associated bounding box.
						if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
						{
							// Check against any occluders.
							if(( !( flags & vRENDER_OCCLUDED )) || ( !TestSphereAgainstOccluders( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius )))
							{
								render = true;
							}
						}
					}
				}
				if( render )
				{
					// If the material has changed, submit the new material.
					if( p_mesh->mp_material != p_material )
					{
						p_material = p_mesh->mp_material;
						p_material->Submit();
					}
					p_mesh->Submit();

					// Add this mesh to the visible list, providing it is within bounds.
					visible_mesh_array[visible_mesh_array_index] = p_mesh;
					if( visible_mesh_array_index < ( VISIBLE_MESH_ARRAY_SIZE - 1 ))
						++visible_mesh_array_index;
				}
			}
		}

		if( p_scene->m_num_dynamic_sort_entries > 0 )
		{
			// Second stage - dynamically sorted meshes.
			int last_dynamic_sort_entry = p_scene->m_first_dynamic_sort_entry + p_scene->m_num_dynamic_sort_entries;
			for( ; e < last_dynamic_sort_entry; ++e )
			{
				sMesh *p_mesh = p_scene->m_meshes[e];

				__asm mov eax, p_mesh			// Store mesh pointer.
				__asm prefetcht0 [eax]			// Get first 32 bytes of sMesh structure.

				if(( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && (( p_mesh->m_flags & ( sMesh::MESH_FLAG_SHADOW_VOLUME | sMesh::MESH_FLAG_BILLBOARD )) == 0 ))
				{
					if( no_culling )
					{
						render = true;
					}
					else
					{
						render = false;

						// Check the visibility mask.
						if( p_mesh->m_visibility_mask & viewport )
						{
							// Frustum cull this set of meshes, using the associated bounding box.
							if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
							{
								// Check against any occluders.
								if(( !( flags & vRENDER_OCCLUDED )) || ( !TestSphereAgainstOccluders( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius )))
								{
									render = true;
								}
							}
						}
					}
					if( render )
					{
						// Add this mesh to the visible list, providing it is within bounds.
						visible_mesh_array[visible_mesh_array_index] = p_mesh;
						if( visible_mesh_array_index < ( VISIBLE_MESH_ARRAY_SIZE - 1 ))
							++visible_mesh_array_index;

						sortedMeshArray[next_sorted_mesh_entry].p_mesh	= p_mesh;
						sortedMeshArray[next_sorted_mesh_entry].sort	= boundingSphereNearestZ;

						++next_sorted_mesh_entry;
					}
				}
			}
			if( next_sorted_mesh_entry > 0 )
			{
				// Sort the array into ascending sort order.
				qsort( sortedMeshArray, next_sorted_mesh_entry, sizeof( sSortedMeshEntry ), cmp );
		
				for( int m = 0; m < next_sorted_mesh_entry; ++m )
				{
					if( sortedMeshArray[m].p_mesh->mp_material != p_material )
					{
						sortedMeshArray[m].p_mesh->mp_material->Submit();
						p_material = sortedMeshArray[m].p_mesh->mp_material;
					}
					sortedMeshArray[m].p_mesh->Submit();
				}
			}
		
			// Third stage - meshes after the dynamically sorted set.
			for( ; e < p_scene->m_num_mesh_entries; ++e )
			{
				sMesh *p_mesh = p_scene->m_meshes[e];

				__asm mov eax, p_mesh			// Store mesh pointer.
				__asm prefetcht0 [eax]			// Get first 32 bytes of sMesh structure.

				if(( p_mesh->m_flags & sMesh::MESH_FLAG_ACTIVE ) && (( p_mesh->m_flags & ( sMesh::MESH_FLAG_SHADOW_VOLUME | sMesh::MESH_FLAG_BILLBOARD )) == 0 ))
				{
					if( no_culling )
					{
						render = true;
					}
					else
					{
						render = false;

						// Check the visibility mask.
						if( p_mesh->m_visibility_mask & viewport )
						{
							// Frustum cull this set of meshes, using the associated bounding box.
							if( frustum_check_sphere( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius ))
							{
								// Check against any occluders.
								if(( !( flags & vRENDER_OCCLUDED )) || ( !TestSphereAgainstOccluders( &p_mesh->m_sphere_center, p_mesh->m_sphere_radius )))
								{
									render = true;
								}
							}
						}
					}
					if( render )
					{
						// If the material has changed, submit the new material.
						if( p_mesh->mp_material != p_material )
						{
							p_material = p_mesh->mp_material;
							p_material->Submit();
						}
						p_mesh->Submit();

						// Add this mesh to the visible list, providing it is within bounds.
						visible_mesh_array[visible_mesh_array_index] = p_mesh;
						if( visible_mesh_array_index < ( VISIBLE_MESH_ARRAY_SIZE - 1 ))
							++visible_mesh_array_index;
					}
				}
			}
		}

		// Now draw the semitransparent meshes with shadow mapped on them.
		if( p_scene->m_flags & SCENE_FLAG_RECEIVE_SHADOWS )
		{
			render_shadow_meshes( p_scene, visible_mesh_array, visible_mesh_array_index );
		}

		if( flags & vRENDER_BILLBOARDS )
		{
			BillboardManager.Render( vRENDER_SEMITRANSPARENT );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void render_light_glows( bool test )
{
	struct sLightGlowVert
	{
		D3DVECTOR	m_pos;
		D3DCOLOR	m_col;
	};

	static sLightGlowVert verts[4];

	// This function will be called twice per render, once to test the amount of pixels drawn,
	// the other to actually draw the pixels.

	// Used to figure the right and up vectors for creating screen-aligned particle quads.
	D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

	// Concatenate p_matrix with the emmission angle to create the direction.
	Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

	// Get the 'right' vector as the cross product of camera 'at' and world 'up'.
	Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
	Mth::Vector screen_right	= Mth::CrossProduct( at, up );
	Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

	screen_right.Normalize();
	screen_up.Normalize();

	sLightGlowDetailsTable.IterateStart();
	sLightGlowDetails *p_details = sLightGlowDetailsTable.IterateNext();

	if( test )
	{
		// Turn of z and color writes.
//		D3DDevice_SetRenderState( D3DRS_COLORWRITEENABLE,	0 );
		D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE,		FALSE );

		set_blend_mode( vBLEND_MODE_DIFFUSE );
		set_texture( 0, NULL );
		set_pixel_shader( PixelShader5 );
		set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );

		while( p_details )
		{
			// Get a new test index.
			uint32 index = p_details->m_visibility_test_fifo.AddStatus();

			// Only do the check if there is room on the queue.
			if( index > 0 )
			{
				// For each light glow instance, add a render check.
				D3DDevice_BeginVisibilityTest();

				// Draw the glow.
				verts[0].m_pos.x	= p_details->m_pos[X] - ( screen_right[X] * p_details->m_test_radius ) - ( screen_up[X] * p_details->m_test_radius );
				verts[0].m_pos.y	= p_details->m_pos[Y] - ( screen_right[Y] * p_details->m_test_radius ) - ( screen_up[Y] * p_details->m_test_radius );
				verts[0].m_pos.z	= p_details->m_pos[Z] - ( screen_right[Z] * p_details->m_test_radius ) - ( screen_up[Z] * p_details->m_test_radius );
				verts[0].m_col		= 0xFFFFFFFF;

				verts[1].m_pos.x	= p_details->m_pos[X] - ( screen_right[X] * p_details->m_test_radius ) + ( screen_up[X] * p_details->m_test_radius );
				verts[1].m_pos.y	= p_details->m_pos[Y] - ( screen_right[Y] * p_details->m_test_radius ) + ( screen_up[Y] * p_details->m_test_radius );
				verts[1].m_pos.z	= p_details->m_pos[Z] - ( screen_right[Z] * p_details->m_test_radius ) + ( screen_up[Z] * p_details->m_test_radius );
				verts[1].m_col		= 0xFFFFFFFF;

				verts[2].m_pos.x	= p_details->m_pos[X] + ( screen_right[X] * p_details->m_test_radius ) + ( screen_up[X] * p_details->m_test_radius );
				verts[2].m_pos.y	= p_details->m_pos[Y] + ( screen_right[Y] * p_details->m_test_radius ) + ( screen_up[Y] * p_details->m_test_radius );
				verts[2].m_pos.z	= p_details->m_pos[Z] + ( screen_right[Z] * p_details->m_test_radius ) + ( screen_up[Z] * p_details->m_test_radius );
				verts[2].m_col		= 0xFFFFFFFF;

				verts[3].m_pos.x	= p_details->m_pos[X] + ( screen_right[X] * p_details->m_test_radius ) - ( screen_up[X] * p_details->m_test_radius );
				verts[3].m_pos.y	= p_details->m_pos[Y] + ( screen_right[Y] * p_details->m_test_radius ) - ( screen_up[Y] * p_details->m_test_radius );
				verts[3].m_pos.z	= p_details->m_pos[Z] + ( screen_right[Z] * p_details->m_test_radius ) - ( screen_up[Z] * p_details->m_test_radius );
				verts[3].m_col		= 0xFFFFFFFF;

				D3DDevice_DrawVerticesUP( D3DPT_QUADLIST, 4, verts, sizeof( sLightGlowVert ));

				// Push a visibility check onto the queue.
				D3DDevice_EndVisibilityTest( index );
			}

			p_details = sLightGlowDetailsTable.IterateNext();
		}

		// Restore z and color writes.
		D3DDevice_SetRenderState( D3DRS_COLORWRITEENABLE,	D3DCOLORWRITEENABLE_ALL );
		D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE,		TRUE );
	}
	else
	{
		set_blend_mode( vBLEND_MODE_BLEND );
		set_pixel_shader( PixelShader5 );
		set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );

		while( p_details )
		{
			// Get the visibility check result.
			uint32 result = p_details->m_visibility_test_fifo.GetStatus();

			if( result > 0 )
			{
				// Tend the radius towards the target.
				p_details->m_current_radius += ( p_details->m_glow_radius - p_details->m_current_radius ) * p_details->m_radius_growth;
			}
			else
			{
				// Tend the radius towards zero.
				p_details->m_current_radius -= p_details->m_current_radius * p_details->m_radius_growth;
			}

			if( p_details->
				m_current_radius > 0.0f )
			{
				// Draw the glow.
				verts[0].m_pos.x	= p_details->m_pos[X] - ( screen_right[X] * p_details->m_current_radius ) - ( screen_up[X] * p_details->m_current_radius );
				verts[0].m_pos.y	= p_details->m_pos[Y] - ( screen_right[Y] * p_details->m_current_radius ) - ( screen_up[Y] * p_details->m_current_radius );
				verts[0].m_pos.z	= p_details->m_pos[Z] - ( screen_right[Z] * p_details->m_current_radius ) - ( screen_up[Z] * p_details->m_current_radius );
				verts[0].m_col		= 0x40FFFFFF;

				verts[1].m_pos.x	= p_details->m_pos[X] - ( screen_right[X] * p_details->m_current_radius ) + ( screen_up[X] * p_details->m_current_radius );
				verts[1].m_pos.y	= p_details->m_pos[Y] - ( screen_right[Y] * p_details->m_current_radius ) + ( screen_up[Y] * p_details->m_current_radius );
				verts[1].m_pos.z	= p_details->m_pos[Z] - ( screen_right[Z] * p_details->m_current_radius ) + ( screen_up[Z] * p_details->m_current_radius );
				verts[1].m_col		= 0x40FFFFFF;

				verts[2].m_pos.x	= p_details->m_pos[X] + ( screen_right[X] * p_details->m_current_radius ) + ( screen_up[X] * p_details->m_current_radius );
				verts[2].m_pos.y	= p_details->m_pos[Y] + ( screen_right[Y] * p_details->m_current_radius ) + ( screen_up[Y] * p_details->m_current_radius );
				verts[2].m_pos.z	= p_details->m_pos[Z] + ( screen_right[Z] * p_details->m_current_radius ) + ( screen_up[Z] * p_details->m_current_radius );
				verts[2].m_col		= 0x40FFFFFF;

				verts[3].m_pos.x	= p_details->m_pos[X] + ( screen_right[X] * p_details->m_current_radius ) - ( screen_up[X] * p_details->m_current_radius );
				verts[3].m_pos.y	= p_details->m_pos[Y] + ( screen_right[Y] * p_details->m_current_radius ) - ( screen_up[Y] * p_details->m_current_radius );
				verts[3].m_pos.z	= p_details->m_pos[Z] + ( screen_right[Z] * p_details->m_current_radius ) - ( screen_up[Z] * p_details->m_current_radius );
				verts[3].m_col		= 0x40FFFFFF;

				D3DDevice_DrawVerticesUP( D3DPT_QUADLIST, 4, verts, sizeof( sLightGlowVert ));
			}

			p_details = sLightGlowDetailsTable.IterateNext();
		}
	}
}




} // namespace NxXbox



================================================
FILE: Code/Gfx/XBox/NX/render.h
================================================
#ifndef __RENDER_H
#define __RENDER_H

#include 
#include 
#include 
#include 
#include "mesh.h"

#define		RS_ZWRITEENABLE			1
#define		RS_ZTESTENABLE			2
#define		RS_ALPHACUTOFF			3
#define		RS_UVADDRESSMODE0		4
#define		RS_UVADDRESSMODE1		5
#define		RS_UVADDRESSMODE2		6
#define		RS_UVADDRESSMODE3		7
#define		RS_MIPLODBIASPASS0		8
#define		RS_MIPLODBIASPASS1		9
#define		RS_MIPLODBIASPASS2		10
#define		RS_MIPLODBIASPASS3		11
#define		RS_CULLMODE				16
#define		RS_ALPHABLENDENABLE		17
#define		RS_ALPHATESTENABLE		18
#define		RS_SPECULARENABLE		19
#define		RS_FOGENABLE			20
#define		RS_ZBIAS				21
#define		RS_MINMAGFILTER0		32
#define		RS_MINMAGFILTER1		33
#define		RS_MINMAGFILTER2		34
#define		RS_MINMAGFILTER3		35

extern		DWORD PixelShader0;
extern		DWORD PixelShader0IVA;
extern		DWORD PixelShader1;
extern		DWORD PixelShader2;
extern		DWORD PixelShader3;
extern		DWORD PixelShader4;
extern		DWORD PixelShader5;
extern		DWORD PixelShaderBrighten;
extern		DWORD PixelShaderBrightenIVA;
extern		DWORD PixelShaderFocusBlur;
extern		DWORD PixelShaderFocusIntegrate;
extern		DWORD PixelShaderFocusLookupIntegrate;
extern		DWORD PixelShaderNULL;
extern		DWORD PixelShaderPointSprite;
extern		DWORD PixelShaderBumpWater;

namespace NxXbox
{
	struct sTextureProjectionDetails
	{
		sTexture		*p_texture;
		Nx::CXboxModel	*p_model;
		sScene			*p_scene;
		XGMATRIX		view_matrix;
		XGMATRIX		projection_matrix;
		XGMATRIX		texture_projection_matrix;
	};


	extern Lst::HashTable< sTextureProjectionDetails > *pTextureProjectionDetailsTable;

	
	typedef enum
	{
		vBLEND_MODE_DIFFUSE,								// ( 0 - 0 ) * 0 + Src
		vBLEND_MODE_ADD,									// ( Src - 0 ) * Src + Dst
		vBLEND_MODE_ADD_FIXED,								// ( Src - 0 ) * Fixed + Dst
		vBLEND_MODE_SUBTRACT,								// ( 0 - Src ) * Src + Dst
		vBLEND_MODE_SUB_FIXED,								// ( 0 - Src ) * Fixed + Dst
		vBLEND_MODE_BLEND,									// ( Src * Dst ) * Src + Dst	
		vBLEND_MODE_BLEND_FIXED,							// ( Src * Dst ) * Fixed + Dst	
		vBLEND_MODE_MODULATE,								// ( Dst - 0 ) * Src + 0
		vBLEND_MODE_MODULATE_FIXED,							// ( Dst - 0 ) * Fixed + 0	
		vBLEND_MODE_BRIGHTEN,								// ( Dst - 0 ) * Src + Dst
		vBLEND_MODE_BRIGHTEN_FIXED,							// ( Dst - 0 ) * Fixed + Dst	
		vBLEND_MODE_GLOSS_MAP,								// Specular = Specular * Src	- special mode for gloss mapping
		vBLEND_MODE_BLEND_PREVIOUS_MASK,					// ( Src - Dst ) * Dst + Dst
		vBLEND_MODE_BLEND_INVERSE_PREVIOUS_MASK,			// ( Dst - Src ) * Dst + Src

		vBLEND_MODE_MODULATE_COLOR					= 15,	// ( Dst - 0 ) * Src(col) + 0	- special mode for the shadow.
		vBLEND_MODE_ONE_INV_SRC_ALPHA				= 17,	//								- special mode for imposter rendering.

		vNUM_BLEND_MODES
	} BlendModes; 

	typedef enum
	{
		vRENDER_OPAQUE								= 1,						
		vRENDER_SEMITRANSPARENT						= 2,
		vRENDER_OCCLUDED							= 4,
		vRENDER_NO_CULLING							= 8,		// Used for instances which have already been culled at a higher level
		vRENDER_SORT_FRONT_TO_BACK					= 16,		// Used to improve pixel rejection tests for opaque rendering only
		vRENDER_SHADOW_VOLUMES						= 32,		// Used to indicate that only shadow volumes should be (special-case) rendered
		vRENDER_BILLBOARDS							= 64,		// Used to indicate that billboards should be rendered
		vRENDER_INSTANCE_PRE_WORLD_SEMITRANSPARENT	= 128,		// Used to indicate that this instance rendering is happening prior to semitransparent world rendering
		vRENDER_INSTANCE_POST_WORLD_SEMITRANSPARENT	= 256,		// Used to indicate that this instance rendering is happening after semitransparent world rendering
	} SceneRenderFlags; 

	BlendModes	GetBlendMode( uint32 blend_checksum );

	void		create_pixel_shaders();
	void		GetPixelShader( sMaterial *p_material, uint32 *p_pixel_shader_id );
	void		set_pixel_shader( uint32 shader_id );
	void		set_pixel_shader( uint32 shader_id, uint32 num_passes );
	void		set_vertex_shader( DWORD shader_id );

	void		set_render_state( uint32 type, uint32 state );
	void		set_blend_mode( uint32 mode );
	void		set_texture( uint32 pass, IDirect3DTexture8 *p_texture, IDirect3DPalette8 *p_palette = NULL );
	
	void		create_texture_projection_details( sTexture *p_texture, Nx::CXboxModel *p_model, sScene *p_scene );
	void		destroy_texture_projection_details( sTexture *p_texture );
	void		set_texture_projection_camera( sTexture *p_texture, XGVECTOR3 *p_pos, XGVECTOR3 *p_at );
	void		calculate_tex_proj_matrix( XGMATRIX *p_tex_view_matrix, XGMATRIX *p_tex_proj_matrix, XGMATRIX *p_tex_transform_matrix, XGMATRIX *p_world_matrix = NULL );
	
	// MSM PERFCHANGE - added scale.
	void	set_camera( Mth::Matrix *p_matrix, Mth::Vector *p_position, float screen_angle, float aspect_ratio, bool render_at_infinity = false );
	void	set_frustum_bbox_transform( Mth::Matrix *p_transform );
	bool	frustum_check_sphere( D3DXVECTOR3 *p_center, float radius );
	float	get_bounding_sphere_nearest_z( void );
	bool	IsVisible( Mth::Vector ¢er, float radius );
	void	render_shadow_targets();
	void	render_light_glows( bool test );
	void	render_scene( sScene *p_scene, uint32 flags = ( vRENDER_OPAQUE | vRENDER_SEMITRANSPARENT ), uint32 viewport = 0 );

} // namespace NxXbox

#endif // __RENDER_H


================================================
FILE: Code/Gfx/XBox/NX/scene.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include "texture.h"
#include "mesh.h"
#include "scene.h"

namespace NxXbox
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int sort_by_material_draw_order( const void *p1, const void *p2 )
{
	sMesh		*p_mesh1		= *((sMesh**)p1 );
	sMesh		*p_mesh2		= *((sMesh**)p2 );
	sMaterial	*p_material1	= p_mesh1->mp_material;
	sMaterial	*p_material2	= p_mesh2->mp_material;
	
	Dbg_Assert( p_material1 != NULL );
	Dbg_Assert( p_material2 != NULL );

	if( p_material1->m_draw_order == p_material2->m_draw_order )
	{
		// Have to do some special case processing for the situation where two or more meshes have the same draw order, but only some are
		// marked as being dynamically sorted. In such a situation the non dynamically sorted ones should come first.
		if( p_material1->m_sorted == p_material2->m_sorted )
		{
			if( p_material1 == p_material2 )
			{
				// Same material, no further sorting required.
				return 0;
			}
			else
			{
				// If the pixel shaders are the same, sort by material address, otherwise sort by pixel shader value.
				if( p_mesh1->m_pixel_shader == p_mesh2->m_pixel_shader )
				{
					return ((uint32)p_material1 > (uint32)p_material2 ) ? 1 : -1;
				}
				else
				{
					return ((uint32)p_mesh1->m_pixel_shader > (uint32)p_mesh2->m_pixel_shader ) ? 1 : -1;
				}
			}
		}
		else if( p_material1->m_sorted )
		{
			return 1;
		}
		return -1;
	}
	return ( p_material1->m_draw_order > p_material2->m_draw_order ) ? 1 : -1;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sScene::sScene( void )
{
	m_flags								= 0;

	// No meshes as yet.
	m_num_mesh_entries					= 0;
	m_num_semitransparent_mesh_entries	= 0;
	m_num_filled_mesh_entries			= 0;
	m_first_dynamic_sort_entry			= 0xFFFFFFFFUL;
	m_first_semitransparent_entry		= 0xFFFFFFFFUL;
	m_num_dynamic_sort_entries			= 0;
	m_is_dictionary						= false;

	m_meshes							= NULL;
	pMaterialTable						= NULL;

	mp_hierarchyObjects					= NULL;
	m_numHierarchyObjects				= 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sScene::~sScene( void )
{
	// Remove the material table.
	if( pMaterialTable )
	{
		delete pMaterialTable;
	}

	if( m_meshes != NULL )
	{
		delete [] m_meshes;
	}

	if( mp_hierarchyObjects )
	{
		delete [] mp_hierarchyObjects;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::FigureBoundingVolumes( void )
{
	// Figure bounding sphere assuming bounding box has already been set up (during individual mesh initialisation).
	Mth::Vector radial	= ( m_bbox.GetMax() - m_bbox.GetMin() ) * 0.5f;
	Mth::Vector center	= m_bbox.GetMin() + radial;
	m_sphere_center		= D3DXVECTOR3( center[X], center[Y], center[Z] );
	m_sphere_radius		= sqrtf(( radial[X] * radial[X] ) +	( radial[Y] * radial[Y] ) + ( radial[Z] * radial[Z] ));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::CountMeshes( int num_meshes, sMesh **pp_meshes )
{
	// Count each mesh.
	for( int m = 0; m < num_meshes; ++m )
	{
		++m_num_mesh_entries;
		bool transparent = (( pp_meshes[m]->mp_material ) && ( pp_meshes[m]->mp_material->m_flags[0] & 0x40 ));
		if( transparent )
		{
			++m_num_semitransparent_mesh_entries;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::CreateMeshArrays( void )
{
	if( m_num_mesh_entries > 0 )
	{
		m_meshes = new sMesh*[m_num_mesh_entries];
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::AddMeshes( int num_meshes, sMesh **pp_meshes )
{
	// Add each mesh.
	for( int m = 0; m < num_meshes; ++m )
	{
		Dbg_Assert( m_num_filled_mesh_entries < m_num_mesh_entries );
		m_meshes[m_num_filled_mesh_entries] = pp_meshes[m];
		++m_num_filled_mesh_entries;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::RemoveMeshes( int num_meshes, sMesh **pp_meshes )
{
	int num_mesh_entries_removed				= 0;
	
	for( int m = 0; m < num_meshes; ++m )
	{
		sMesh *p_mesh = pp_meshes[m];

		bool found = false;
		for( int i = 0; i < m_num_mesh_entries; ++i )
		{
			if( m_meshes[i] == p_mesh )
			{
				found = true;
				m_meshes[i] = NULL;
				++num_mesh_entries_removed;
				break;
			}
		}

		Dbg_Assert( found );	
	}
	
	// Now go through and compact the arrays.
	while( num_mesh_entries_removed > 0 )
	{
		for( int i = 0; i < m_num_mesh_entries; ++i )
		{
			if( m_meshes[i] == NULL )
			{
				// Only worth copying if there is anything beyond this mesh.
				if( i < ( m_num_mesh_entries - 1 ))
				{
					CopyMemory( &m_meshes[i], &m_meshes[i + 1], sizeof( sMesh* ) * ( m_num_mesh_entries - ( i + 1 )));
				}
				--num_mesh_entries_removed;
				--m_num_mesh_entries;
				--m_num_filled_mesh_entries;
				break;
			}
		}
	}
	
	// Finally, scan through and mark the start and end of the dynamically sorted set of meshes.
	m_num_semitransparent_mesh_entries	= 0;
	m_num_dynamic_sort_entries			= 0;
	m_first_semitransparent_entry		= m_num_mesh_entries;
	m_first_dynamic_sort_entry			= m_num_mesh_entries;
	
	for( int i = 0; i < m_num_mesh_entries; ++i )
	{
		bool transparent = (( m_meshes[i]->mp_material ) && ( m_meshes[i]->mp_material->m_flags[0] & 0x40 ));
		if( transparent )
		{
			++m_num_semitransparent_mesh_entries;

			if( i < m_first_semitransparent_entry )
			{
				m_first_semitransparent_entry = i;
			}

			if( m_meshes[i]->mp_material->m_sorted )
			{
				++m_num_dynamic_sort_entries;
				if( i < m_first_dynamic_sort_entry )
				{
					m_first_dynamic_sort_entry = i;
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::SortMeshes( void )
{
	// Sort the list of meshes.
	qsort( m_meshes, m_num_mesh_entries, sizeof( sMesh* ), sort_by_material_draw_order );

	// Now scan through and mark the start and end of the dynamically sorted set of meshes.
	m_num_semitransparent_mesh_entries	= 0;
	m_first_semitransparent_entry		= m_num_mesh_entries;
	m_first_dynamic_sort_entry			= m_num_mesh_entries;
	m_num_dynamic_sort_entries			= 0;
	for( int i = 0; i < m_num_mesh_entries; ++i )
	{
		bool transparent = (( m_meshes[i]->mp_material ) && ( m_meshes[i]->mp_material->m_flags[0] & 0x40 ));
		if( transparent )
		{
			++m_num_semitransparent_mesh_entries;

			if( i < m_first_semitransparent_entry )
			{
				m_first_semitransparent_entry = i;
			}

			if( m_meshes[i]->mp_material->m_sorted )
			{
				++m_num_dynamic_sort_entries;
				if( i < m_first_dynamic_sort_entry )
				{
					m_first_dynamic_sort_entry = i;
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMesh *sScene::GetMeshByLoadOrder( int load_order )
{
	for( int i = 0; i < m_num_mesh_entries; ++i )
	{
		if( m_meshes[i]->m_load_order == load_order )
		{
			return m_meshes[i];
		}
	}
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sMaterial *sScene::GetMaterial( uint32 checksum )
{
	if( pMaterialTable )
	{
		pMaterialTable->IterateStart();
		sMaterial *p_mat = pMaterialTable->IterateNext();
		while( p_mat )
		{
			if( p_mat->m_checksum == checksum )
			{
				return p_mat;
			}
			p_mat = pMaterialTable->IterateNext();
		}
	}
	return NULL;
}



const uint32	INDEX_WORKBUFFER_SIZE	= 3072;
static uint16	index_workbuffer[INDEX_WORKBUFFER_SIZE];

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sScene::HidePolys( uint32 mask, sCASData *p_cas_data, uint32 num_entries )
{
	if(( num_entries == 0 ) || ( mask == 0 ))
	{
		return;
	}

//	Tmr::Time start = Tmr::GetTimeInUSeconds();
	
	int indices_added[64];
	ZeroMemory( indices_added, sizeof( int ) * 64 );
	
	// Check against every CAS entry.
	sCASData *p_cas_entry = p_cas_data;
	for( uint32 entry = 0; entry < num_entries; ++entry, ++p_cas_entry )
	{
		// Check this CAS entry has the correct mask...
		if( p_cas_entry->mask & mask )
		{
			// Get the mesh this entry references.
			uint32	load_order	= p_cas_entry->data0 >> 16;
			sMesh	*p_mesh		= GetMeshByLoadOrder( load_order );

			Dbg_Assert( load_order < 32 );
			
			if( p_mesh == NULL )
			{
				continue;
			}
			
			// Get the indices of the poly referenced.
//			uint32 i0 = ( p_cas_entry->data0 & 0xFFFF ) - p_mesh->m_index_base;
			uint32 i0 = p_cas_entry->data0 & 0xFFFF;
//			uint32 i1 = ( p_cas_entry->data1 >> 16 ) - p_mesh->m_index_base;
			uint32 i1 = p_cas_entry->data1 >> 16;
//			uint32 i2 = ( p_cas_entry->data1 & 0xFFFF ) - p_mesh->m_index_base;
			uint32 i2 = p_cas_entry->data1 & 0xFFFF;

			// For every vertex index in this mesh...
			uint16 *p_indices	= p_mesh->mp_index_buffer[0];
			uint32 index0		= p_indices[p_cas_entry->start_index] & 0x7FFF;
			uint32 index1		= p_indices[p_cas_entry->start_index + 1] & 0x7FFF;
			for( uint32 i = p_cas_entry->start_index + 2; i < p_mesh->m_num_indices[0]; ++i )
			{
				uint32 index2 = p_indices[i] & 0x7FFF;
				if(( index0 == i0 ) && ( index1 == i1 ) && ( index2 == i2 ))
				{
					// Indicate this mesh will need rebuilding.
					indices_added[load_order] = indices_added[load_order] + 1;

					// Flag the second index of the poly.
					p_indices[i - 1] |= 0x8000;
					break;
				}
				index0	= index1;
				index1	= index2;
			}
		}
	}

	// Now rebuild those meshes that need it.
	for( int m = 0; m < m_num_mesh_entries; ++m )
	{
		sMesh *p_mesh = m_meshes[m];
		if( indices_added[p_mesh->m_load_order] > 0 )
		{
			uint16 *p_indices						= p_mesh->mp_index_buffer[0];
			uint32 new_indices_index				= 0;
			for( uint32 i = 0; i < p_mesh->m_num_indices[0]; ++i )
			{
				uint16 index = p_indices[i];
				
				Dbg_Assert( new_indices_index < INDEX_WORKBUFFER_SIZE );
				index_workbuffer[new_indices_index++]	= index & 0x7FFF;

				if( index & 0x8000 )
				{
					Dbg_Assert( new_indices_index < INDEX_WORKBUFFER_SIZE );
					index_workbuffer[new_indices_index++]	= index & 0x7FFF;
					Dbg_Assert( new_indices_index < INDEX_WORKBUFFER_SIZE );
					index_workbuffer[new_indices_index++]	= index & 0x7FFF;
				}
			}

			// Create new index buffer, on the same heap as the existing index buffer.
			Mem::Allocator::BlockHeader*	p_bheader	= Mem::Allocator::BlockHeader::sRead( p_mesh->mp_index_buffer[0] );
			Mem::Allocator*					p_allocater	= p_bheader->mpAlloc;
			Mem::Manager::sHandle().PushContext( p_allocater );

			delete [] p_mesh->mp_index_buffer[0];
			p_mesh->m_num_indices[0]	= (uint16)new_indices_index;
			p_mesh->mp_index_buffer[0]	= new uint16[p_mesh->m_num_indices[0]];

			Mem::Manager::sHandle().PopContext();

			// And copy in the new indices from the workbuffer.
			CopyMemory( p_mesh->mp_index_buffer[0], index_workbuffer, sizeof( uint16 ) * p_mesh->m_num_indices[0] );
		}
	}

//	Tmr::Time end = Tmr::GetTimeInUSeconds() - start;
//	printf( "HidePolys() took %ld u seconds\n", end );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sScene *LoadScene( const char *Filename, sScene *pScene )
{
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void DeleteScene( sScene *pScene )
{
	// Iterate through the table of materials, deleting them.
	if( pScene->pMaterialTable )
	{
		pScene->pMaterialTable->IterateStart();
		sMaterial* p_mat = pScene->pMaterialTable->IterateNext();
		while( p_mat )
		{
			delete p_mat;
			p_mat = pScene->pMaterialTable->IterateNext();
		}
	}
	
	// Delete the scene itself.
	delete pScene;
}



sScene *sScene::pHead;


} // namespace NxXbox



================================================
FILE: Code/Gfx/XBox/NX/scene.h
================================================
#ifndef __SCENE_H
#define __SCENE_H


#include 
#include 
#include 
#include 
#include "texture.h"
#include "mesh.h"
#include "material.h"
#include "anim.h"

namespace NxXbox
{


struct sMeshEntry
{
	sMesh*					mp_mesh;			// Pointer to mesh.
	int						m_bbox;				// Bounding box index.
};

#define SCENE_FLAG_RENDERING_SHADOW		( 1 << 7 )
#define SCENE_FLAG_RECEIVE_SHADOWS		( 1 << 8 )
#define SCENE_FLAG_SELF_SHADOWS			( 1 << 9 )

struct sScene
{
								sScene( void );
								~sScene( void );

	sMaterial *					sScene::GetMaterial( uint32 checksum );
	void						CountMeshes( int num_meshes, sMesh **pp_meshes );
	void						CreateMeshArrays( void );
	void						AddMeshes( int num_meshes, sMesh **pp_meshes );
	void						RemoveMeshes( int num_meshes, sMesh **pp_meshes );
	void						SortMeshes( void );
	sMesh						*GetMeshByLoadOrder( int load_order );
	void						FigureBoundingVolumes( void );
	void						HidePolys( uint32 mask, sCASData *p_cas_data, uint32 num_entries );
	
	uint32						m_flags;
	int							NumTextures;
	uint8						*pTexBuffer;
	uint8						*pTexDma;
	sTexture					*pTextures;

	int							NumMaterials;
	Lst::HashTable< sMaterial >	*pMaterialTable;
	
	sMesh						**m_meshes;
	int							m_num_mesh_entries;
	int							m_num_semitransparent_mesh_entries;		// Used for making scene level draw order decisions.
	int							m_num_filled_mesh_entries;
	int							m_first_semitransparent_entry;
	int							m_first_dynamic_sort_entry;
	int							m_num_dynamic_sort_entries;
	
	class CInstance				*pInstances;

	sScene						*pNext;

	static sScene				*pHead;

	bool						m_is_dictionary;

	Mth::CBBox					m_bbox;	
	D3DXVECTOR3					m_sphere_center;
	float						m_sphere_radius;

	// For mesh heirarchies.
	Nx::CHierarchyObject*		mp_hierarchyObjects;						// Array of hierarchy objects.
	int							m_numHierarchyObjects;						// Number of hierarchy objects.
};


sScene	*LoadScene( const char *Filename, sScene *pScene );
void	DeleteScene( sScene *pScene );
int		sort_by_material_draw_order( const void *p1, const void *p2 );


} // namespace NxXbox


#endif // __SCENE_H



================================================
FILE: Code/Gfx/XBox/NX/screenfx.cpp
================================================
#include 
#include 
#include 
#include "nx_init.h"
#include "render.h"
#include "screenfx.h"

//-----------------------------------------------------------------------------
// A filter sample holds a subpixel offset and a filter value
// to be multiplied by a source texture to compute an arbitrary
// filter.  See filter_copy for more details.
//-----------------------------------------------------------------------------
struct FilterSample 
{
    FLOAT fValue;               // Coefficient
    FLOAT fOffsetX, fOffsetY;   // Subpixel offsets of supersamples in destination coordinates
};

//-----------------------------------------------------------------------------
// The depth-mapping pixel shader attempts to do higher precision
// math with eight-bit color registers.  The _x4 instruction modifier
// is used twice to get a 16x range of values.
//-----------------------------------------------------------------------------
FLOAT g_fPixelShaderScale = 16.0f;   // to get into the right range, we scale up the value in the pixel shader
FLOAT m_fDepth0	= 0.900f;
FLOAT m_fDepth1	= 0.997f;
D3DTexture* m_pTextureFocusRange = NULL;   // Lookup table for range of z values to use

// Enumeration of blur filters available in this sample to compare the speed and quality of different types of blur filters for
// the out-of-focus parts of the scene.
enum FILTERMODE 
{
        FM_BOX,
        FM_VERT,
        FM_HORIZ, 
        FM_BOX2,
        FM_VERT2,
        FM_HORIZ2,
        FM_BOX2_BOX2,
		FM_VERT2_HORIZ2,
        FM_HORIZ2_VERT2,
        FM_VERT2_HORIZ,
        FM_HORIZ2_VERT,
        FM_VERT2_HORIZ2_BOX2,
        FM_BOX2_BOX2_BOX2,
        FM_VERT2_HORIZ2_VERT2,
        FM_HORIZ2_VERT2_HORIZ2,
        FM_VERT2_HORIZ2_VERT2_HORIZ2,
        FM_HORIZ2_VERT2_HORIZ2_VERT2,
        FM_BOX2_BOX2_BOX2_BOX2,
        _FM_MAX
    };





namespace NxXbox
{


void draw_rain( void )
{
	static float table_x[512];
	static float table_y[512];
	static bool table_set = false;
	static int table_index = 0;
	if( !table_set )
	{
		table_set = true;
		for( int i = 0; i < 512; ++i )
		{
			table_x[i] = ((float)( rand() - ( RAND_MAX / 2 )) * 32.0f ) / (float)RAND_MAX;
			table_y[i] = ((float)rand() * 12.0f ) / (float)RAND_MAX;
		}
	}
	
	static float radius = 24.0f;

	// Distance of the drip from the camera.
	static float distance = 10.0f * 12.0f;

	// Drip vector.
	static Mth::Vector offset( 0.0f, -1.0f * radius, 0.0f );
	static Mth::Vector offset_target( 0.0f, -1.0f * radius, 0.0f );

	static int num_drops = 1000;

	if(( rand() & 63 ) == 1 )
	{
		offset_target[X] = ( radius * 0.25f * ( rand() - ( RAND_MAX / 2 ))) / RAND_MAX;
		offset_target[Y] = ( radius * -1.0f * rand() ) / RAND_MAX;
		offset_target[Z] = ( radius * 0.25f * ( rand() - ( RAND_MAX / 2 ))) / RAND_MAX;
		
		offset_target.Normalize( radius );
	}
	
	offset += ( offset_target - offset ) * 0.2f;
	offset.Normalize( radius );	
	
	D3DXVECTOR4 or( EngineGlobals.cam_position.x + ( distance * EngineGlobals.cam_at.x ),
					EngineGlobals.cam_position.y + ( distance * EngineGlobals.cam_at.y ),
					EngineGlobals.cam_position.z + ( distance * EngineGlobals.cam_at.z ),
					1.0f );
	D3DXVECTOR4 of( or.x + offset[X], or.y + offset[Y], or.z + offset[Z], 1.0f );

	D3DXVec4Transform( &or, &or, (D3DXMATRIX*)&EngineGlobals.view_matrix );
	D3DXVec4Transform( &of, &of, (D3DXMATRIX*)&EngineGlobals.view_matrix );

	D3DXVec4Transform( &or, &or, (D3DXMATRIX*)&EngineGlobals.projection_matrix );
	D3DXVec4Transform( &of, &of, (D3DXMATRIX*)&EngineGlobals.projection_matrix );

	or.x /= or.w;
	or.y /= or.w;

	of.x /= of.w;
	of.y /= of.w;

	of.y = -of.y;
	
//	printf( "(%.2f %.2f) (%.2f %.2f)\n", or.x, or.y, of.x, of.y );

	// Obtain push buffer lock.
	DWORD *p_push; 
	DWORD dwords_per_particle	= 10;
	DWORD dword_count			= dwords_per_particle * num_drops;

	// Submit particle material.
//	mp_engine_particle->mp_material->Submit();
	NxXbox::set_blend_mode( vBLEND_MODE_BLEND );
		
	// Set up correct vertex and pixel shader.
	NxXbox::set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE );
	NxXbox::set_pixel_shader( PixelShader5 );
		
	// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
	p_push = D3DDevice_BeginPush( dword_count + 32 );

	// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
	// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
	// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
	p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
	p_push[1]	= D3DPT_LINELIST;
	p_push		+= 2; 

	// Set up loop variables here, since we be potentially entering the loop more than once.
	int lp = 0;

	while( dword_count > 0 )
	{
		int dwords_written = 0;

		// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
		// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
		++p_push;
		
		for( ; lp < num_drops; lp++ )
		{
			// Check to see if writing another particle will take us over the edge.
			if(( dwords_written + dwords_per_particle ) > 2047 )
			{
				break;
			}

			float	screen_x0		= (float)(( NxXbox::EngineGlobals.backbuffer_width * rand() ) / RAND_MAX );
			float	screen_y0		= (float)(( NxXbox::EngineGlobals.backbuffer_height * rand() ) / RAND_MAX );

			int		random_length	= rand();
			
			float	screen_x1		= (float)( screen_x0 + (( of.x * ( EngineGlobals.backbuffer_width / 2 ) * random_length ) / RAND_MAX ));
			float	screen_y1		= (float)( screen_y0 + (( of.y * ( EngineGlobals.backbuffer_height / 2 ) * random_length ) / RAND_MAX ));

			screen_x1		+= table_x[table_index];
			screen_y1		+= table_y[table_index];
			table_index		= ( table_index >= 512 ) ? 0 : ( table_index + 1 );

			p_push[0]	= *((DWORD*)&screen_x0 );
			p_push[1]	= *((DWORD*)&screen_y0 );
			p_push[2]	= 0x00000000UL;
			p_push[3]	= 0x00000000UL;
			p_push[4]	= 0xA0808080UL;
			p_push		+= 5;

			p_push[0]	= *((DWORD*)&screen_x1 );
			p_push[1]	= *((DWORD*)&screen_y1 );
			p_push[2]	= 0x00000000UL;
			p_push[3]	= 0x00000000UL;
			p_push[4]	= 0x20808080UL;
			p_push		+= 5;

			dwords_written	+= dwords_per_particle;
			dword_count		-= dwords_per_particle;
		}
	}
	p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
	p_push[1] = 0;
	p_push += 2;
	D3DDevice_EndPush( p_push );
}

	
	
	
//////////////////////////////////////////////////////////////////////
// For mapping from the depth buffer to blend values using
// a texture map lookup. See media\shaders\depthlookup.psh
//
// This is more general than computing the range as in
// media\shaders\depth.psh, since the ramp can be filled in
// arbitrarily, but may be more expensive due to the extra texture
// lookup.
//
float FUnitMap(float fAlpha, float fBlue, float fAlphaOffset, float fAlphaSlope, float fBlueOffset, float fBlueSlope)
{
    //return g_fPixelShaderScale * fAlphaSlope * (fAlpha - fAlphaOffset) + fBlueSlope * fBlue + fBlueOffset - 0.5f;
    return g_fPixelShaderScale * fAlphaSlope * (fAlpha - fAlphaOffset) + fBlueSlope * (fBlue - fBlueOffset);
}

float FQuantizedDepth(float fDepth, float *pfAlpha, float *pfBlue)
{
    float fDepth16 = fDepth * (float)(1 << 16);
    DWORD dwDepth16 = (DWORD)(fDepth16 /*+ 0.5f*/);
    *pfAlpha = (dwDepth16 >> 8) * (1.0f / 255.0f);
    *pfBlue = (dwDepth16 & 0xff) * (1.0f / 255.0f);
    return (float)dwDepth16 / (float)(1 << 16);
}

	
	
	
//-----------------------------------------------------------------------------
// Name: CalculateDepthMapping()
// Desc: Calculate offsets and slope to map given z range to 0,1 in
//       the depth and focus pixel shaders.
//-----------------------------------------------------------------------------
static HRESULT calculate_depth_mapping( float fDepth0, float fDepth1, float* pfAlphaOffset, float* pfAlphaSlope, float* pfBlueOffset, float* pfBlueSlope )
{
    // Check range of args
    if( fDepth0 < 0.0f ) fDepth0 = 0.0f;
    if( fDepth0 > 1.0f ) fDepth0 = 1.0f;
    if( fDepth1 < 0.0f ) fDepth1 = 0.0f;
    if( fDepth1 > 1.0f ) fDepth1 = 1.0f;

    if( fDepth1 < fDepth0 )
    {
        // Swap depth to make fDepth0 <= fDepth1
        float t = fDepth1;
        fDepth1 = fDepth0;
        fDepth0 = t;
    }
    
    // Calculate quantized values
    float fAlpha0, fBlue0;
    float fQuantizedDepth0 = FQuantizedDepth(fDepth0, &fAlpha0, &fBlue0);
    float fAlpha1, fBlue1;
    float fQuantizedDepth1 = FQuantizedDepth(fDepth1, &fAlpha1, &fBlue1);

    // Calculate offset and slopes
    float fScale = 1.0f / (fQuantizedDepth1 - fQuantizedDepth0);
    if( fScale > g_fPixelShaderScale )
    {
        fScale = g_fPixelShaderScale; // This is the steepest slope we can handle
        fDepth0 = 0.5f * (fDepth0 + fDepth1) - 0.5f / fScale; // Move start so that peak is in middle of fDepth0 and fDepth1
        fDepth1 = fDepth0 + 1.0f / fScale;
        fQuantizedDepth0 = FQuantizedDepth(fDepth0, &fAlpha0, &fBlue0);
        fQuantizedDepth1 = FQuantizedDepth(fDepth1, &fAlpha1, &fBlue1);
    }
    
    (*pfAlphaOffset) = fAlpha0;
    (*pfAlphaSlope)  = fScale / g_fPixelShaderScale;
    (*pfBlueSlope)   = fScale * (1.0f/255.0f); // blue ramp adds more levels to the ramp

    // Align peak of map to center by calculating the quantized alpha value
//    *pfBlueOffset = 0.5f;   // zero biased up by 0.5f
//    float fZeroDesired = (fQuantizedDepth0 - fDepth0) / (fDepth1 - fDepth0);
//    float fZero = FUnitMap(fAlpha0, fBlue0, *pfAlphaOffset, *pfAlphaSlope, *pfBlueOffset, *pfBlueSlope);
//    float fOneDesired = (fQuantizedDepth1 - fDepth0) / (fDepth1 - fDepth0);
//    float fOne = FUnitMap(fAlpha1, fBlue1, *pfAlphaOffset, *pfAlphaSlope, *pfBlueOffset, *pfBlueSlope);
//    *pfBlueOffset = 0.5f * (fZeroDesired-fZero + fOneDesired-fOne) + 0.5f;  // biased up by 0.5f
    (*pfBlueOffset) = fBlue0;
    
    return S_OK;
}

	

	
	
static HRESULT fill_focus_range_texture( bool bRamp )
{
    HRESULT hr;

    static const DWORD Width	= 256;
    static const DWORD Height	= 256;
    
    // Create the focus range texture
    if( m_pTextureFocusRange )
        m_pTextureFocusRange->Release();
	EngineGlobals.p_Device->CreateTexture( Width, Height, 1, 0, D3DFMT_A8, 0, &m_pTextureFocusRange );
    
    // Fill the focus range texture
    D3DLOCKED_RECT lockedRect;
    hr = m_pTextureFocusRange->LockRect( 0, &lockedRect, NULL, 0L );
    if( FAILED(hr) )
        return hr;
    
    DWORD dwPixelStride = 1;
    Swizzler s(Width, Height, 0);
    s.SetV(s.SwizzleV(0));
    s.SetU(s.SwizzleU(0));
    if( bRamp )
    {
        for( DWORD j = 0; j < Height; j++ )
        {
            for( DWORD i = 0; i < Width; i++ )
            {
                BYTE *p = (BYTE *)lockedRect.pBits + dwPixelStride * s.Get2D();
                *p = (BYTE)i;
                s.IncU();
            }
            s.IncV();
        }
    }
    else
    {
//        float fAlphaOffset, fAlphaSlope, fBlueOffset, fBlueSlope;
//        calculate_depth_mapping( m_fDepth0, m_fDepth1, &fAlphaOffset, &fAlphaSlope, &fBlueOffset, &fBlueSlope );
        for( DWORD i = 0; i < Width; i++ )
        {
			float z_high = (float)i / ( Width - 1 );

            for( DWORD j = 0; j < Height; j++ )
            {
                BYTE *p = (BYTE *)lockedRect.pBits + dwPixelStride * s.Get2D();
//                float fAlpha = (float)i / (Width - 1);
//                float fBlue  = (float)j / (Height - 1);
//                float fUnit  = 2.0f * (FUnitMap(fAlpha, fBlue, fAlphaOffset, fAlphaSlope, fBlueOffset, fBlueSlope) - 0.5f);
//                float fMap   = 1.0f - fUnit * fUnit;
//                if( fMap < 0.0f ) fMap = 0.0f;
//                if( fMap > 1.0f ) fMap = 1.0f;
//                *p = (BYTE)(255 * fMap + 0.5f);
                
				float z_low			= (float)j / ( Height - 1 );
				float quantized_z	= ((( z_high * 256.0f ) + z_low ) * 256.0f ) / 65536.0f;

				if( quantized_z < m_fDepth0 )
				{
					*p = 0;
				}
				else if( quantized_z > m_fDepth1 )
				{
					*p = 0;
				}
				else
				{
					*p = 255;
				}
				s.IncV();
            }
            s.IncU();
        }
    }
    
	m_pTextureFocusRange->UnlockRect( 0 );

    return S_OK;
}
	
	
	
	
	
//-----------------------------------------------------------------------------
// Name: filter_copy()
// Desc: Filter the source texture by rendering into the destination texture
//       with subpixel offsets. Does 4 filter coefficients at a time, using all
//       the stages of the pixel shader.
//-----------------------------------------------------------------------------

D3DTexture* m_pBlur;
	
static HRESULT filter_copy( LPDIRECT3DTEXTURE8 pTextureDst,
                                 LPDIRECT3DTEXTURE8 pTextureSrc,
                                 DWORD dwNumSamples,
                                 FilterSample rSample[],
                                 DWORD dwSuperSampleX,
                                 DWORD dwSuperSampleY )
{
    // Set destination as render target, with no-depth buffer
    LPDIRECT3DSURFACE8 pSurface;
    pTextureDst->GetSurfaceLevel( 0, &pSurface );
    EngineGlobals.p_Device->SetRenderTarget( pSurface, NULL );
    pSurface->Release();

    // Get descriptions of source and destination
    D3DSURFACE_DESC descSrc;
    pTextureSrc->GetLevelDesc( 0, &descSrc );

	if( descSrc.MultiSampleType == D3DMULTISAMPLE_2_SAMPLES_MULTISAMPLE_LINEAR )
	{
		descSrc.Width *= 2;
	}
	
	// Set render state for filtering
    EngineGlobals.p_Device->SetRenderState( D3DRS_LIGHTING,         FALSE );
	set_render_state( RS_ZWRITEENABLE,		0 );
	set_render_state( RS_ZTESTENABLE,		0 );
	set_render_state( RS_ALPHATESTENABLE,	0 );
	set_render_state( RS_ALPHABLENDENABLE,	0 );
    EngineGlobals.p_Device->SetRenderState( D3DRS_BLENDOP,          D3DBLENDOP_ADD ); // Setup subsequent renderings to add to previous value
    EngineGlobals.p_Device->SetRenderState( D3DRS_SRCBLEND,         D3DBLEND_ONE );
    EngineGlobals.p_Device->SetRenderState( D3DRS_DESTBLEND,        D3DBLEND_ONE );

    // Set texture state
    DWORD xx;
    for( xx = 0; xx < 4; xx++ )
    {
        set_texture( xx, pTextureSrc );  // use our source texture for all four stages

        EngineGlobals.p_Device->SetTextureStageState( xx, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );  // pass texture coords without transformation
        EngineGlobals.p_Device->SetTextureStageState( xx, D3DTSS_TEXCOORDINDEX, xx ); // each texture has different tex coords
		set_render_state( RS_UVADDRESSMODE0 + xx, 0x00010001UL );
        EngineGlobals.p_Device->SetTextureStageState( xx, D3DTSS_ALPHAKILL, D3DTALPHAKILL_DISABLE );
    }
    
	// Use blur pixel shader.
	set_pixel_shader( PixelShaderFocusBlur );
	set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_TEX4 );

    // Prepare quadrilateral vertices
    float x0 = -0.5f;
    float y0 = -0.5f;
    float x1 = (float)( descSrc.Width  / dwSuperSampleX ) - 0.5f;
    float y1 = (float)( descSrc.Height / dwSuperSampleY ) - 0.5f;
    struct QUAD
    {
        float x, y, z, w1;
        struct uv 
        {
            float u, v;
        } tex[4];   // each texture has different offset
    };
    
    QUAD aQuad[4] = 
    {
        { x0, y0, 1.0f, 1.0f, }, // texture coords are set below
        { x1, y0, 1.0f, 1.0f, },
        { x0, y1, 1.0f, 1.0f, },
        { x1, y1, 1.0f, 1.0f, }
    };

    // Draw a quad for each block of 4 filter coefficients
    xx = 0; // current texture stage
    FLOAT fOffsetScaleX, fOffsetScaleY; // convert destination coords to source texture coords
    FLOAT u0, v0, u1, v1;   // base source rectangle.
	if( XGIsSwizzledFormat( descSrc.Format ))
    {
        FLOAT fWidthScale  = 1.0f / (FLOAT)descSrc.Width;
        FLOAT fHeightScale = 1.0f / (FLOAT)descSrc.Height;
        fOffsetScaleX = (FLOAT)dwSuperSampleX * fWidthScale;
        fOffsetScaleY = (FLOAT)dwSuperSampleY * fHeightScale;
        u0 = 0.0f;
        v0 = 0.0f;
        u1 = (FLOAT)descSrc.Width * fWidthScale;
        v1 = (FLOAT)descSrc.Height * fHeightScale;
    }
    else
    {
        fOffsetScaleX = (FLOAT)dwSuperSampleX;
        fOffsetScaleY = (FLOAT)dwSuperSampleY;
        u0 = 0.0f;
        v0 = 0.0f;
        u1 = (FLOAT)descSrc.Width;
        v1 = (FLOAT)descSrc.Height;
	}
    D3DCOLOR rColor[4];
    DWORD rPSInput[4];
    for( DWORD dwSample = 0; dwSample < dwNumSamples; dwSample++ )
    {
        // Set filter coefficients
        FLOAT fValue = rSample[dwSample].fValue;
//      float rf[4] = {fValue, fValue, fValue, fValue};
//      EngineGlobals.p_Device->SetPixelShaderConstant(xx, rf, 1);            // positive coeff
  
        if( fValue < 0.0f )
        {
            rColor[xx] = D3DXCOLOR(-fValue, -fValue, -fValue, -fValue);
            rPSInput[xx] = PS_INPUTMAPPING_SIGNED_NEGATE | ((xx % 2) ? PS_REGISTER_C1 : PS_REGISTER_C0);
        }
        else
        {
            rColor[xx] = D3DXCOLOR(fValue, fValue, fValue, fValue);
            rPSInput[xx] = PS_INPUTMAPPING_SIGNED_IDENTITY | ((xx % 2) ? PS_REGISTER_C1 : PS_REGISTER_C0);
        }

        // Align supersamples with center of destination pixels
        FLOAT fOffsetX = rSample[dwSample].fOffsetX * fOffsetScaleX;
        FLOAT fOffsetY = rSample[dwSample].fOffsetY * fOffsetScaleY;
        aQuad[0].tex[xx].u = u0 + fOffsetX;
        aQuad[0].tex[xx].v = v0 + fOffsetY;
        aQuad[1].tex[xx].u = u1 + fOffsetX;
        aQuad[1].tex[xx].v = v0 + fOffsetY;
        aQuad[2].tex[xx].u = u0 + fOffsetX;
        aQuad[2].tex[xx].v = v1 + fOffsetY;
        aQuad[3].tex[xx].u = u1 + fOffsetX;
        aQuad[3].tex[xx].v = v1 + fOffsetY;
        
        xx++; // Go to next stage
        if( xx == 4 || dwSample == dwNumSamples - 1 )  // max texture stages or last sample
        {
            // zero out unused texture stage coefficients 
            // (only for last filter sample, when number of samples is not divisible by 4)
            for( ; xx < 4; xx++ )
            {
				set_texture( xx, NULL );
                rColor[xx] = 0;
                rPSInput[xx] = PS_INPUTMAPPING_UNSIGNED_IDENTITY | PS_REGISTER_ZERO;
            }
        
            // Set coefficients
            EngineGlobals.p_Device->SetRenderState( D3DRS_PSCONSTANT0_0, rColor[0] );
            EngineGlobals.p_Device->SetRenderState( D3DRS_PSCONSTANT1_0, rColor[1] );
            EngineGlobals.p_Device->SetRenderState( D3DRS_PSCONSTANT0_1, rColor[2] );
            EngineGlobals.p_Device->SetRenderState( D3DRS_PSCONSTANT1_1, rColor[3] );

            // Remap coefficients to proper sign
            EngineGlobals.p_Device->SetRenderState( D3DRS_PSRGBINPUTS0,
                                          PS_COMBINERINPUTS( rPSInput[0] | PS_CHANNEL_RGB,   PS_REGISTER_T0 | PS_CHANNEL_RGB   | PS_INPUTMAPPING_SIGNED_IDENTITY,
                                                             rPSInput[1] | PS_CHANNEL_RGB,   PS_REGISTER_T1 | PS_CHANNEL_RGB   | PS_INPUTMAPPING_SIGNED_IDENTITY ) );
            EngineGlobals.p_Device->SetRenderState( D3DRS_PSALPHAINPUTS0,
                                          PS_COMBINERINPUTS( rPSInput[0] | PS_CHANNEL_ALPHA, PS_REGISTER_T0 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
                                                             rPSInput[1] | PS_CHANNEL_ALPHA, PS_REGISTER_T1 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY ) );
            EngineGlobals.p_Device->SetRenderState( D3DRS_PSRGBINPUTS1,
                                          PS_COMBINERINPUTS( rPSInput[2] | PS_CHANNEL_RGB,   PS_REGISTER_T2 | PS_CHANNEL_RGB   | PS_INPUTMAPPING_SIGNED_IDENTITY,
                                                             rPSInput[3] | PS_CHANNEL_RGB,   PS_REGISTER_T3 | PS_CHANNEL_RGB   | PS_INPUTMAPPING_SIGNED_IDENTITY ) );
            EngineGlobals.p_Device->SetRenderState( D3DRS_PSALPHAINPUTS1,
                                          PS_COMBINERINPUTS( rPSInput[2] | PS_CHANNEL_ALPHA, PS_REGISTER_T2 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY,
                                                             rPSInput[3] | PS_CHANNEL_ALPHA, PS_REGISTER_T3 | PS_CHANNEL_ALPHA | PS_INPUTMAPPING_SIGNED_IDENTITY ) );
            
            // Draw the quad to filter the coefficients so far
			EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_TRIANGLESTRIP, 2, aQuad, sizeof( QUAD )); // one quad blends 4 textures

			// On subsequent renderings, add to what's in the render target.
			set_render_state( RS_ALPHABLENDENABLE,	1 );
			xx = 0;
        }
    }

    // Clear texture stages
    for( xx=0; xx<4; xx++ )
    {
		set_texture( xx, NULL );
    }

	// Restore render target, zbuffer, and state.
    set_pixel_shader( NULL );
	EngineGlobals.p_Device->SetRenderTarget( EngineGlobals.p_RenderSurface, EngineGlobals.p_ZStencilSurface );

	return S_OK;
}
	
	
	
//-----------------------------------------------------------------------------
// Name: Blur()
// Desc: Blur backbuffer and set m_pBlur to the current blur texture.  Calls
//       filter_copy() with different filter coefficients and offsets, based on
//       the current FILTERMODE setting.
//-----------------------------------------------------------------------------
static HRESULT focus_blur( void )
{
	D3DTexture	m_BackBufferTexture;
	D3DTexture	m_BlurTexture[5];
	
	FILTERMODE filter_mode = FM_BOX2_BOX2;
	
	int	multisample_adjusted_width = EngineGlobals.backbuffer_width * 2;
	
	XGSetTextureHeader( multisample_adjusted_width, EngineGlobals.backbuffer_height, 1, 0,
                        EngineGlobals.backbuffer_format, 0, &m_BackBufferTexture, 
						EngineGlobals.p_RenderSurface->Data,
                        multisample_adjusted_width * XGBytesPerPixelFromFormat( EngineGlobals.backbuffer_format ));

	XGSetTextureHeader( EngineGlobals.backbuffer_width, EngineGlobals.backbuffer_height, 1, 0,
	                    EngineGlobals.blurbuffer_format, 0, &m_BlurTexture[0], 
						EngineGlobals.p_BlurSurface[0]->Data,
				        EngineGlobals.backbuffer_width * XGBytesPerPixelFromFormat( EngineGlobals.blurbuffer_format ));

	XGSetTextureHeader( EngineGlobals.backbuffer_width / 2, EngineGlobals.backbuffer_height / 2, 1, 0,
	                    EngineGlobals.blurbuffer_format, 0, &m_BlurTexture[1], 
						EngineGlobals.p_BlurSurface[1]->Data,
				        EngineGlobals.backbuffer_width / 2 * XGBytesPerPixelFromFormat( EngineGlobals.blurbuffer_format ));

	XGSetTextureHeader( EngineGlobals.backbuffer_width / 4, EngineGlobals.backbuffer_height / 4, 1, 0,
	                    EngineGlobals.blurbuffer_format, 0, &m_BlurTexture[2], 
						EngineGlobals.p_BlurSurface[2]->Data,
				        EngineGlobals.backbuffer_width / 4 * XGBytesPerPixelFromFormat( EngineGlobals.blurbuffer_format ));

	XGSetTextureHeader( EngineGlobals.backbuffer_width / 8, EngineGlobals.backbuffer_height / 8, 1, 0,
	                    EngineGlobals.blurbuffer_format, 0, &m_BlurTexture[3], 
						EngineGlobals.p_BlurSurface[3]->Data,
				        EngineGlobals.backbuffer_width / 8 * XGBytesPerPixelFromFormat( EngineGlobals.blurbuffer_format ));
	
	// Filters align to blurriest point in supersamples, on the 0.5 boundaries.
    // This takes advantage of the bilinear filtering in the texture map lookup.
    static FilterSample BoxFilter[] =     // for 2x2 downsampling
    {
        { 0.25f, -0.5f, -0.5f },
        { 0.25f,  0.5f, -0.5f },
        { 0.25f, -0.5f,  0.5f },
        { 0.25f,  0.5f,  0.5f },
    };
    static FilterSample YFilter[] =       // 1221 4-tap filter in Y
    {
        { 1.0f/6.0f, 0.0f, -1.5f },
        { 2.0f/6.0f, 0.0f, -0.5f },
        { 2.0f/6.0f, 0.0f,  0.5f },
        { 1.0f/6.0f, 0.0f,  1.5f },
    };
    static FilterSample XFilter[] =       // 1221 4-tap filter in X
    {
        { 1.0f/6.0f, -1.5f, 0.0f },
        { 2.0f/6.0f, -0.5f, 0.0f },
        { 2.0f/6.0f,  0.5f, 0.0f },
        { 1.0f/6.0f,  1.5f, 0.0f },
    };
    static FilterSample Y141Filter[] =    // 141 3-tap filter in Y
    {
        { 1.0f/6.0f, 0.0f, -1.0f },
        { 4.0f/6.0f, 0.0f,  0.0f },
        { 1.0f/6.0f, 0.0f,  1.0f },
    };
    static FilterSample X141Filter[] =        // 141 3-tap filter in X
    {
        { 1.0f/6.0f, -1.0f, 0.0f },
        { 4.0f/6.0f,  0.0f, 0.0f },
        { 1.0f/6.0f,  1.0f, 0.0f },
    };
    static FilterSample IdentityFilter[] = // No filtering
    {
        { 1.0f, 0.0f, 0.0f },
    };

    switch( filter_mode )
    {
        case FM_BOX:
        {
            // Blur from the backbuffer to the blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[0];
			filter_copy( pTextureDst, pTextureSrc, 4, BoxFilter, 1, 1 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[0];
            break;
        }

        case FM_VERT:
        {
            // Blur from the backbuffer to the blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[0];
            filter_copy( pTextureDst, pTextureSrc, 4, YFilter, 1, 1 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[0];
            break;
        }

        case FM_HORIZ:
        {
            // Blur from the backbuffer to the blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[0];
            filter_copy( pTextureDst, pTextureSrc, 4, XFilter, 1, 1 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[0];
            break;
        }

        case FM_BOX2:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture *pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, BoxFilter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[1];
            break;
        }

        case FM_VERT2:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, YFilter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[1];
            break;
        }

        case FM_HORIZ2:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture *pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, XFilter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[1];
            break;
        }
        
        case FM_VERT2_HORIZ2:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, YFilter, 2, 2 );
            
            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[1];
            pTextureDst = &m_BlurTexture[2];
            filter_copy( pTextureDst, pTextureSrc, 4, XFilter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[2];
            break;
        }

        case FM_HORIZ2_VERT2:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, XFilter, 2, 2 );
            
            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[1];
            pTextureDst = &m_BlurTexture[2];
            filter_copy( pTextureDst, pTextureSrc, 4, YFilter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[2];
            break;
        }

        case FM_VERT2_HORIZ:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, YFilter, 2, 2 );
            
            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[1];
            pTextureDst = &m_BlurTexture[2];
            filter_copy( pTextureDst, pTextureSrc, 3, X141Filter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[2];
            break;
        }

        case FM_HORIZ2_VERT:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, XFilter, 2, 2 );

            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[1];
            pTextureDst = &m_BlurTexture[2];  // destination is next blur texture
            filter_copy( pTextureDst, pTextureSrc, 3, Y141Filter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[2];
            break;
        }

        case FM_BOX2_BOX2:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[0];
            filter_copy( pTextureDst, pTextureSrc, 4, BoxFilter, 2, 1 );

            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[0];
            pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, BoxFilter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[1];
            break;
        }


        case FM_VERT2_HORIZ2_BOX2:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, YFilter, 2, 2 );

            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[1];
            pTextureDst = &m_BlurTexture[2];
            filter_copy( pTextureDst, pTextureSrc, 4, XFilter, 2, 2 );

            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[2];
            pTextureDst = &m_BlurTexture[3];
			filter_copy( pTextureDst, pTextureSrc, 4, BoxFilter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[3];
            break;
        }

        case FM_BOX2_BOX2_BOX2:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, BoxFilter, 2, 2 );

            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[1];
            pTextureDst = &m_BlurTexture[2];
            filter_copy( pTextureDst, pTextureSrc, 4, BoxFilter, 2, 2 );

            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[2];
            pTextureDst = &m_BlurTexture[3];
            filter_copy( pTextureDst, pTextureSrc, 4, BoxFilter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[3];
            break;
        }

        case FM_VERT2_HORIZ2_VERT2:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, YFilter, 2, 2 );

            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[1];
            pTextureDst = &m_BlurTexture[2];
            filter_copy( pTextureDst, pTextureSrc, 4, XFilter, 2, 2 );

            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[2];
            pTextureDst = &m_BlurTexture[3];
            filter_copy( pTextureDst, pTextureSrc, 4, YFilter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[3];
            break;
        }

        case FM_HORIZ2_VERT2_HORIZ2:
        {
            // Blur from the backbuffer to the 1/2 sized blur texture
            D3DTexture* pTextureSrc = &m_BackBufferTexture;
            D3DTexture* pTextureDst = &m_BlurTexture[1];
            filter_copy( pTextureDst, pTextureSrc, 4, XFilter, 2, 2 );

            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[1];
            pTextureDst = &m_BlurTexture[2];
            filter_copy( pTextureDst, pTextureSrc, 4, YFilter, 2, 2 );

            // Blur from the previous blur texture to the next blur texture
            pTextureSrc = &m_BlurTexture[2];
            pTextureDst = &m_BlurTexture[3];
            filter_copy( pTextureDst, pTextureSrc, 4, XFilter, 2, 2 );
            
            m_pBlur = (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[3];
            break;
        }

        default:
		{
            m_pBlur = NULL;
            break;
		}
    }
    return S_OK;
}




static HRESULT draw_focus_effect_using_planes( void )
{
    // Make a D3DTexture wrapper around the depth buffer surface
//    D3DTexture ZBufferTexture;
//    XGSetTextureHeader( EngineGlobals.backbuffer_width, EngineGlobals.backbuffer_height, 1, 0, 
//                        D3DFMT_LIN_A8B8G8R8, 0, &ZBufferTexture,
//                        EngineGlobals.p_ZStencilSurface->Data, EngineGlobals.backbuffer_width * 4 );

    // Get size of blur texture for setting texture coords of final blur
    D3DSURFACE_DESC descBlur;
    m_pBlur->GetLevelDesc( 0, &descBlur );
    float fOffsetX = 0.0f;
    float fOffsetY = 0.5f / (float)descBlur.Height; // vertical blur
    struct VERTEX 
    {
        D3DXVECTOR4 p;
        FLOAT tu0, tv0;

    } v[4];
    v[0].p = D3DXVECTOR4( -0.5f,									-0.5f,									1.0f, 1.0f );
    v[1].p = D3DXVECTOR4( EngineGlobals.backbuffer_width - 0.5f,	-0.5f,									1.0f, 1.0f );
    v[2].p = D3DXVECTOR4( -0.5f,									EngineGlobals.backbuffer_height - 0.5f,	1.0f, 1.0f );
    v[3].p = D3DXVECTOR4( EngineGlobals.backbuffer_width - 0.5f,	EngineGlobals.backbuffer_height - 0.5f, 1.0f, 1.0f );
    v[0].tu0 = fOffsetX;							v[0].tv0 = fOffsetY;
    v[1].tu0 = fOffsetX + (float)descBlur.Width;	v[1].tv0 = fOffsetY;
    v[2].tu0 = fOffsetX;							v[2].tv0 = fOffsetY + (float)descBlur.Height;
    v[3].tu0 = fOffsetX + (float)descBlur.Width;	v[3].tv0 = fOffsetY + (float)descBlur.Height;
	
	// Set pixel shader state
//    float fAlphaOffset, fAlphaSlope, fBlueOffset, fBlueSlope;
//   calculate_depth_mapping( m_fDepth0, m_fDepth1, &fAlphaOffset, &fAlphaSlope, &fBlueOffset, &fBlueSlope );
//    float Constants[] = 
//    {
//        0.0f, 0.0f, fBlueOffset, fAlphaOffset,      // offset
//        0.0f, 0.0f, fBlueSlope, 0.0f,               // 1x
//        0.0f, 0.0f, 0.0f, 0.0f,                     // 4x
//        0.0f, 0.0f, 0.0f, fAlphaSlope,              // 16x
//    };

	set_pixel_shader( 0 );
//    EngineGlobals.p_Device->SetPixelShaderConstant( 0, Constants, 4 );
    
    // Set render state
	set_render_state( RS_ZWRITEENABLE,		0 );
	set_render_state( RS_ZTESTENABLE,		1 );
    set_render_state( RS_ALPHATESTENABLE,	0 );
	set_render_state( RS_ALPHABLENDENABLE,	0 );
    set_render_state( RS_ALPHACUTOFF,		0 );
//    EngineGlobals.p_Device->SetRenderState( D3DRS_SRCBLEND,         D3DBLEND_ONE );
//    EngineGlobals.p_Device->SetRenderState( D3DRS_DESTBLEND,        D3DBLEND_INVSRCALPHA );
    EngineGlobals.p_Device->SetRenderState( D3DRS_SRCBLEND,         D3DBLEND_ONE );
    EngineGlobals.p_Device->SetRenderState( D3DRS_DESTBLEND,        D3DBLEND_ZERO );

	// Set texture state.
	set_texture( 0, m_pBlur );
	set_texture( 1, NULL );
	set_texture( 2, NULL );
    set_texture( 3, NULL );

	set_render_state( RS_UVADDRESSMODE0, 0x00010001UL );
    
	set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_TEX1 );

	// Render the screen-aligned quadrilateral
	for( int vx = 0; vx < 4; ++vx )
	{
		v[vx].p.z = m_fDepth1;
	}
	EngineGlobals.p_Device->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL );
    EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_QUADSTRIP, 1, v, sizeof( VERTEX ));

	// Render the screen-aligned quadrilateral
	for( int vx = 0; vx < 4; ++vx )
	{
		v[vx].p.z = m_fDepth0;
	}
	EngineGlobals.p_Device->SetRenderState( D3DRS_ZFUNC, D3DCMP_GREATEREQUAL );
    EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_QUADSTRIP, 1, v, sizeof( VERTEX ));

    // Reset render states
	EngineGlobals.p_Device->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL );

    set_render_state( RS_ALPHATESTENABLE,	1 );
	set_render_state( RS_ALPHABLENDENABLE,	1 );
	set_render_state( RS_ZWRITEENABLE,		1 );
	set_render_state( RS_ZTESTENABLE,		1 );

	set_pixel_shader( 0 );

	set_texture( 0, NULL );
	set_texture( 1, NULL );
	set_texture( 2, NULL );
	set_texture( 3, NULL );

    return S_OK;
}



//-----------------------------------------------------------------------------
// Name: DrawFocusRange()
// Desc: Choose the focus range by mapping z to a focus value using pixel
//       shader arithmetic.  See media/shaders/focus.psh for more details.
//
//       High focus values leave the back-buffer unchanged.
//       Low focus values blend in the blurred texture computed by Blur().
//-----------------------------------------------------------------------------
static HRESULT draw_focus_effect_using_range( void )
{
    // Make a D3DTexture wrapper around the depth buffer surface
    D3DTexture ZBufferTexture;
    XGSetTextureHeader( EngineGlobals.backbuffer_width, EngineGlobals.backbuffer_height, 1, 0, 
                        D3DFMT_LIN_A8B8G8R8, 0, &ZBufferTexture,
                        EngineGlobals.p_ZStencilSurface->Data, EngineGlobals.backbuffer_width * 4 );

    // Get size of blur texture for setting texture coords of final blur
    D3DSURFACE_DESC descBlur;
    m_pBlur->GetLevelDesc( 0, &descBlur );
    float fOffsetX = 0.0f;
    float fOffsetY = 0.5f / (float)descBlur.Height; // vertical blur
    struct VERTEX 
    {
        D3DXVECTOR4 p;
        FLOAT tu0, tv0;
        FLOAT tu1, tv1;
    } v[4];
    v[0].p = D3DXVECTOR4( -0.5f,                      -0.5f,                       1.0f, 1.0f );
    v[1].p = D3DXVECTOR4( EngineGlobals.backbuffer_width - 0.5f, -0.5f,                       1.0f, 1.0f );
    v[2].p = D3DXVECTOR4( -0.5f,                      EngineGlobals.backbuffer_height - 0.5f, 1.0f, 1.0f );
    v[3].p = D3DXVECTOR4( EngineGlobals.backbuffer_width - 0.5f, EngineGlobals.backbuffer_height - 0.5f, 1.0f, 1.0f );
    v[0].tu0 = 0.0f;                       v[0].tv0 = 0.0f;
    v[1].tu0 = (float)EngineGlobals.backbuffer_width; v[1].tv0 = 0.0f;
    v[2].tu0 = 0.0f;                       v[2].tv0 = (float)EngineGlobals.backbuffer_height;
    v[3].tu0 = (float)EngineGlobals.backbuffer_width; v[3].tv0 = (float)EngineGlobals.backbuffer_height;
    v[0].tu1 = fOffsetX;                         v[0].tv1 = fOffsetY;
    v[1].tu1 = fOffsetX + (float)descBlur.Width; v[1].tv1 = fOffsetY;
    v[2].tu1 = fOffsetX;                         v[2].tv1 = fOffsetY + (float)descBlur.Height;
    v[3].tu1 = fOffsetX + (float)descBlur.Width; v[3].tv1 = fOffsetY + (float)descBlur.Height;
    
    // Set pixel shader state
    float fAlphaOffset, fAlphaSlope, fBlueOffset, fBlueSlope;
    calculate_depth_mapping( m_fDepth0, m_fDepth1, &fAlphaOffset, &fAlphaSlope, &fBlueOffset, &fBlueSlope );
    float Constants[] = 
    {
        0.0f, 0.0f, fBlueOffset, fAlphaOffset,      // offset
        0.0f, 0.0f, fBlueSlope, 0.0f,               // 1x
        0.0f, 0.0f, 0.0f, 0.0f,                     // 4x
        0.0f, 0.0f, 0.0f, fAlphaSlope,              // 16x
    };

	set_pixel_shader( PixelShaderFocusIntegrate );
    EngineGlobals.p_Device->SetPixelShaderConstant( 0, Constants, 4 );
    
    // Set render state
	set_render_state( RS_ZWRITEENABLE,		0 );
	set_render_state( RS_ZTESTENABLE,		0 );
    set_render_state( RS_ALPHATESTENABLE,	1 );
	set_render_state( RS_ALPHABLENDENABLE,	1 );
    set_render_state( RS_ALPHACUTOFF,		1 );
    EngineGlobals.p_Device->SetRenderState( D3DRS_SRCBLEND,         D3DBLEND_ONE );
    EngineGlobals.p_Device->SetRenderState( D3DRS_DESTBLEND,        D3DBLEND_INVSRCALPHA );

	// Set texture state.
    set_texture( 0, &ZBufferTexture );
	set_texture( 1, m_pBlur );
	set_texture( 2, NULL );
    set_texture( 3, NULL );

	set_render_state( RS_UVADDRESSMODE0, 0x00010001UL );
	set_render_state( RS_UVADDRESSMODE1, 0x00010001UL );
	set_render_state( RS_UVADDRESSMODE2, 0x00010001UL );
	set_render_state( RS_UVADDRESSMODE3, 0x00010001UL );
    
    // Render the screen-aligned quadrilateral
	set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_TEX4 );
    EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_QUADSTRIP, 1, v, sizeof( VERTEX ));

    // Reset render states
    set_render_state( RS_ALPHATESTENABLE,	1 );
	set_render_state( RS_ALPHABLENDENABLE,	1 );
	set_render_state( RS_ZWRITEENABLE,		1 );
	set_render_state( RS_ZTESTENABLE,		1 );

	set_pixel_shader( 0 );

	set_texture( 0, NULL );
	set_texture( 1, NULL );
	set_texture( 2, NULL );
	set_texture( 3, NULL );

    return S_OK;
}
	

	




//-----------------------------------------------------------------------------
// Name: DrawFocusLookup()
// Desc: Choose the focus range by mapping z through a lookup texture.
//
//       See media/shaders/focuslookup.psh for more detail.
//
//       This technique has lower performance than using DrawFocus(),
//       but the focus values can be arbitrary, rather than the
//       limited types of z-to-focus value mappings available with
//       pixel shader arithmetic.
//
//       High focus values leave the back-buffer unchanged.
//       Low focus values blend in the blurred texture computed by Blur().
//-----------------------------------------------------------------------------
static HRESULT draw_focus_effect_using_lookup( void )
{
    // Make a D3DTexture wrapper around the depth buffer surface
    D3DTexture ZBufferTexture;
    XGSetTextureHeader( EngineGlobals.backbuffer_width, EngineGlobals.backbuffer_height, 1, 0, 
                        D3DFMT_LIN_A8B8G8R8, 0, &ZBufferTexture,
                        EngineGlobals.p_ZStencilSurface->Data, EngineGlobals.backbuffer_width * 4 );

    // Get size of blur texture for setting texture coords of final blur
    D3DSURFACE_DESC descBlur;
    m_pBlur->GetLevelDesc( 0, &descBlur );
    FLOAT fOffsetX = 0.0f;
    FLOAT fOffsetY = 0.5f / (FLOAT)descBlur.Height; // vertical blur

    // Define a set of vertices to draw a quad in screenspace
    struct VERTEX 
    {
        D3DXVECTOR4 p;
        FLOAT tu0, tv0;
        FLOAT tu1, tv1;
        FLOAT tu2, tv2;
        FLOAT tu3, tv3;
    } v[4];
    v[0].p = D3DXVECTOR4( -0.5f,                      -0.5f,                       1.0f, 1.0f );
    v[1].p = D3DXVECTOR4( EngineGlobals.backbuffer_width - 0.5f, -0.5f,                       1.0f, 1.0f );
    v[2].p = D3DXVECTOR4( -0.5f,                      EngineGlobals.backbuffer_height - 0.5f, 1.0f, 1.0f );
    v[3].p = D3DXVECTOR4( EngineGlobals.backbuffer_width - 0.5f, EngineGlobals.backbuffer_height - 0.5f, 1.0f, 1.0f );
    v[0].tu0 = 0.0f;                       v[0].tv0 = 0.0f;
    v[1].tu0 = (float)EngineGlobals.backbuffer_width; v[1].tv0 = 0.0f;
    v[2].tu0 = 0.0f;                       v[2].tv0 = (float)EngineGlobals.backbuffer_height;
    v[3].tu0 = (float)EngineGlobals.backbuffer_width; v[3].tv0 = (float)EngineGlobals.backbuffer_height;

    // tu1 and tv1 are ignored
    // offset final set of texture coords to apply an additional blur
    v[0].tu2 = -fOffsetX;                         v[0].tv2 = -fOffsetY;
    v[1].tu2 = -fOffsetX + (FLOAT)descBlur.Width; v[1].tv2 = -fOffsetY;
    v[2].tu2 = -fOffsetX;                         v[2].tv2 = -fOffsetY + (FLOAT)descBlur.Height;
    v[3].tu2 = -fOffsetX + (FLOAT)descBlur.Width; v[3].tv2 = -fOffsetY + (FLOAT)descBlur.Height;
    v[0].tu3 =  fOffsetX;                         v[0].tv3 =  fOffsetY;
    v[1].tu3 =  fOffsetX + (FLOAT)descBlur.Width; v[1].tv3 =  fOffsetY;
    v[2].tu3 =  fOffsetX;                         v[2].tv3 =  fOffsetY + (FLOAT)descBlur.Height;
    v[3].tu3 =  fOffsetX + (FLOAT)descBlur.Width; v[3].tv3 =  fOffsetY + (FLOAT)descBlur.Height;

    // Set pixel shader
	set_pixel_shader( PixelShaderFocusLookupIntegrate );

    // Set texture state
	set_texture( 0, &ZBufferTexture );
	set_texture( 1, m_pTextureFocusRange );
	set_texture( 2, m_pBlur );
	set_texture( 3, m_pBlur );

	set_render_state( RS_UVADDRESSMODE0, 0x00010001UL );
	set_render_state( RS_UVADDRESSMODE1, 0x00010001UL );
	set_render_state( RS_UVADDRESSMODE2, 0x00010001UL );
	set_render_state( RS_UVADDRESSMODE3, 0x00010001UL );
    
    // Set render state
	set_render_state( RS_ZWRITEENABLE,		0 );
	set_render_state( RS_ZTESTENABLE,		0 );
    set_render_state( RS_ALPHATESTENABLE,	1 );
	set_render_state( RS_ALPHABLENDENABLE,	1 );
    set_render_state( RS_ALPHACUTOFF,		1 );
    EngineGlobals.p_Device->SetRenderState( D3DRS_SRCBLEND,         D3DBLEND_ONE );
    EngineGlobals.p_Device->SetRenderState( D3DRS_DESTBLEND,        D3DBLEND_INVSRCALPHA );

    // Render the screen-aligned quadrilateral
	set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_TEX4 );
	EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_QUADSTRIP, 1, v, sizeof( VERTEX ));

    // Reset render states
    set_render_state( RS_ALPHATESTENABLE,	1 );
	set_render_state( RS_ALPHABLENDENABLE,	1 );
	set_render_state( RS_ZWRITEENABLE,		1 );
	set_render_state( RS_ZTESTENABLE,		1 );

	set_pixel_shader( 0 );

	set_texture( 0, NULL );
	set_texture( 1, NULL );
	set_texture( 2, NULL );
	set_texture( 3, NULL );

    return S_OK;
}





























/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void set_focus_blur_focus( Mth::Vector & focal_point, float offset, float near_depth, float far_depth )
{
	Mth::Vector diff		= focal_point - Mth::Vector( EngineGlobals.cam_position[0], EngineGlobals.cam_position[1], EngineGlobals.cam_position[2] );
	Mth::Vector unit_diff	= diff.Normalize();

	Mth::Vector p0			= focal_point + (( offset - near_depth ) * unit_diff );
	Mth::Vector p1			= focal_point + (( offset + far_depth ) * unit_diff );

	D3DXVECTOR4 v0( p0[X], p0[Y], p0[Z], 1.0f );
	D3DXVECTOR4 v1( p1[X], p1[Y], p1[Z], 1.0f );

    D3DXVec4Transform( &v0, &v0, (D3DXMATRIX*)&EngineGlobals.view_matrix );
    D3DXVec4Transform( &v0, &v0, (D3DXMATRIX*)&EngineGlobals.projection_matrix );

    D3DXVec4Transform( &v1, &v1, (D3DXMATRIX*)&EngineGlobals.view_matrix );
    D3DXVec4Transform( &v1, &v1, (D3DXMATRIX*)&EngineGlobals.projection_matrix );
    
	m_fDepth0				= v0.z / v0.w;
	m_fDepth1				= v1.z / v1.w;

	// If a z value ends up > 1.0, it is likely from intersecting the near plane, in which case just set it to 0.
	if( m_fDepth0 > 1.0f )
		m_fDepth0 = 0.0f;

//	printf( "%.4f %.4f\n", m_fDepth0, m_fDepth1 );

	// If the two values are sufficiently close, it will cause problems since they have
	// to get quantized down. We have to ensure that ( 1 / ( d1 - d0 )) < 16, that is ( d1 - d0 ) > ( 1 / 16 ).
//	m_fDepth0 = m_fDepth1  - 0.0625f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void start_focus_blur( void )
{
	D3DDevice_SetRenderTarget( NxXbox::EngineGlobals.p_RenderSurface, NxXbox::EngineGlobals.p_ZStencilSurface );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void finish_focus_blur( void )
{
	// If the focus blur is active, we want to render into a the blur buffer, otherwise into the regular frame buffer.
	if(( EngineGlobals.focus_blur > 0 ) && ( EngineGlobals.focus_blur_duration > 1 ))
	{
		EngineGlobals.p_Device->BlockUntilIdle();

		// Store and reset the min filter for each stage.
		DWORD min_filter[4];
		for( int s = 0; s < 4; ++s )
		{
			D3DDevice_GetTextureStageState( s, D3DTSS_MINFILTER, &min_filter[s] );
			D3DDevice_SetTextureStageState( s, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
		}
		
		// First step is to render the back buffer into the blur texture.
//		fill_focus_range_texture( false );

		focus_blur();

//		draw_focus_effect_using_range();
//		draw_focus_effect_using_lookup();
		draw_focus_effect_using_planes();

		// Restore the min filter for each stage.
		for( int s = 0; s < 4; ++s )
		{
			D3DDevice_SetTextureStageState( s, D3DTSS_MINFILTER, min_filter[s] );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void start_screen_blur( void )
{
	// If the screen blur is active, we want to render into a the blur buffer, otherwise into the regular frame buffer.
	if(( EngineGlobals.screen_blur > 0 ) && ( EngineGlobals.screen_blur_duration > 1 ))
	{
		D3DDevice_SetRenderTarget( NxXbox::EngineGlobals.p_BlurSurface[0], NxXbox::EngineGlobals.p_ZStencilSurface );
	}
	else
	{
		D3DDevice_SetRenderTarget( NxXbox::EngineGlobals.p_RenderSurface, NxXbox::EngineGlobals.p_ZStencilSurface );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void finish_screen_blur( void )
{
	// If the screen blur is active, we want to render into a the blur buffer, otherwise into the regular frame buffer.
	if(( EngineGlobals.screen_blur > 0 ) && ( EngineGlobals.screen_blur_duration > 1 ))
	{
		EngineGlobals.p_Device->BlockUntilIdle();

		// Now that everything has been drawn, set the backbuffer as the rendertarget, and draw the poly on top of it.
		D3DDevice_SetRenderTarget( NxXbox::EngineGlobals.p_RenderSurface, NxXbox::EngineGlobals.p_ZStencilSurface );

		NxXbox::set_blend_mode( NxXbox::vBLEND_MODE_BLEND );
	
		// Turn on clamping so that the linear textures work
		NxXbox::set_render_state( RS_UVADDRESSMODE0, 0x00010001UL );

		// Use a default vertex and pixel shader
		NxXbox::set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 );
		NxXbox::set_pixel_shader( PixelShader4 );

		// Select the texture (flush first, since the blur texture is linear).
		NxXbox::set_texture( 0, NULL );
		NxXbox::set_texture( 0, (IDirect3DTexture8*)NxXbox::EngineGlobals.p_BlurSurface[0] );

		// Setup up the vertices.
		struct sBlurVert
		{
			float	sx,sy,sz;
			float	rhw;
			uint32	color;
			float	tu,tv;
		};
	
		sBlurVert vertices[4];

		uint32 alpha		= ( 0xFF - EngineGlobals.screen_blur ) / 2;
		alpha				= ( alpha < 0x20 ) ? 0x20 : alpha;
		
		vertices[0].sx		= 0;
		vertices[0].sy		= 0;
		vertices[0].sz		= 0.0f;
		vertices[0].rhw		= 1.0f;
		vertices[0].color	= ( alpha << 24 ) | 0x808080;
		vertices[0].tu		= 0.0f;
		vertices[0].tv		= 0.0f;

		vertices[1]			= vertices[0];
		vertices[1].sx		= 640;
		vertices[1].tu		= 640.0f;

		vertices[2]			= vertices[0];
		vertices[2].sy		= 480;
		vertices[2].tv		= 480.0f;

		vertices[3]			= vertices[1];
		vertices[3].sy		= vertices[2].sy;
		vertices[3].tv		= vertices[2].tv;

		// Adjust if we are in letterbox mode.
		if( NxXbox::EngineGlobals.letterbox_active )
		{
			vertices[0].sy	+= NxXbox::EngineGlobals.backbuffer_height / 8;
			vertices[1].sy	+= NxXbox::EngineGlobals.backbuffer_height / 8;
			vertices[0].tv	+= (float)( NxXbox::EngineGlobals.backbuffer_height / 8 );
			vertices[1].tv	+= (float)( NxXbox::EngineGlobals.backbuffer_height / 8 );

			vertices[2].sy	-= NxXbox::EngineGlobals.backbuffer_height / 8;
			vertices[3].sy	-= NxXbox::EngineGlobals.backbuffer_height / 8;
			vertices[2].tv	-= (float)( NxXbox::EngineGlobals.backbuffer_height / 8 );
			vertices[3].tv	-= (float)( NxXbox::EngineGlobals.backbuffer_height / 8 );
		}

		// Draw the vertices.
		set_render_state( RS_CULLMODE,		D3DCULL_NONE );
		set_render_state( RS_ZWRITEENABLE,	0 );
		set_render_state( RS_ZTESTENABLE,	0 );

		D3DDevice_DrawVerticesUP( D3DPT_TRIANGLESTRIP, 4, vertices, sizeof( sBlurVert ));

		// Reflush linear texture.
		NxXbox::set_texture( 0, NULL );
	}
}


} // namespace NxXbox


================================================
FILE: Code/Gfx/XBox/NX/screenfx.h
================================================
#ifndef __SCREENFX_H
#define __SCREENFX_H

namespace NxXbox
{
	void	start_screen_blur( void );
	void	finish_screen_blur( void );

	void	start_focus_blur( void );
	void	finish_focus_blur( void );
	void	set_focus_blur_focus( Mth::Vector & focal_point, float offset, float near_depth, float far_depth );

	void	draw_rain( void );
} // namespace NxXbox

#endif // __SCREENFX_H


================================================
FILE: Code/Gfx/XBox/NX/sprite.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "nx_init.h"
#include "scene.h"
#include "render.h"
#include "sprite.h"

extern DWORD PixelShader4;
extern DWORD PixelShader5;

namespace NxXbox
{


/******************************************************************/
/*                                                                */
/* SDraw2D														  */
/*                                                                */
/******************************************************************/

SDraw2D *SDraw2D::sp_2D_draw_list = NULL;


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
SDraw2D::SDraw2D( float pri, bool hide )
{
	m_hidden	= hide;
	m_pri		= pri;
	m_zvalue	= 0.0f;

	mp_next = NULL;

	// add to draw list
	if( !m_hidden )
	{
		InsertDrawList();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
SDraw2D::~SDraw2D()
{
	// Try removing from draw list
	RemoveDrawList();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SDraw2D::SetPriority( float pri )
{
	if( m_pri != pri )
	{
		m_pri = pri;

		// By removing and re-inserting, we re-sort the list
		if( !m_hidden )
		{
			RemoveDrawList();
			InsertDrawList();
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SDraw2D::SetZValue( float z )
{
	m_zvalue = z;

	if( z > 0.0f )
	{
		// Set the priority to zero so it will always draw before everything else.
		SetPriority( 0.0f );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SDraw2D::SetHidden( bool hide )
{
	if (m_hidden != hide)
	{
		m_hidden = hide;
		if (hide)
		{
			RemoveDrawList();
		} else {
			InsertDrawList();
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SDraw2D::DrawAll( void )
{
	static uint32 z_test_required = 0;
	
	set_blend_mode( vBLEND_MODE_BLEND );
	set_render_state( RS_UVADDRESSMODE0, 0x00010001UL );
	set_render_state( RS_ZBIAS, 0 );

	// Set the alpha cutoff value.
	set_render_state( RS_ALPHACUTOFF, 1 );

	set_render_state( RS_ZWRITEENABLE,	0 );
	set_texture( 1, NULL );
	set_texture( 2, NULL );
	set_texture( 3, NULL );

	if( EngineGlobals.color_sign[0] != ( D3DTSIGN_RUNSIGNED | D3DTSIGN_GUNSIGNED | D3DTSIGN_BUNSIGNED ))
	{
		EngineGlobals.color_sign[0] = ( D3DTSIGN_RUNSIGNED | D3DTSIGN_GUNSIGNED | D3DTSIGN_BUNSIGNED );
		D3DDevice_SetTextureStageState( 0, D3DTSS_COLORSIGN, D3DTSIGN_RUNSIGNED | D3DTSIGN_GUNSIGNED | D3DTSIGN_BUNSIGNED );
	}

	// Unfortunately, now that we have 3D text, we may need to enable the z test for some strings.
	set_render_state( RS_ZTESTENABLE,	z_test_required );

	SDraw2D *pDraw	= sp_2D_draw_list;
	uint32	z_test	= 0;

	while( pDraw )
	{
		if (!pDraw->m_hidden)
		{
			pDraw->BeginDraw();
			pDraw->Draw();
			pDraw->EndDraw();

			if(( z_test == 0 ) && ( pDraw->GetZValue() > 0.0f ))
			{
				// There is at least one peice of text with nonzero z, so we need to z test.
				z_test = 1;
			}
		}
		pDraw = pDraw->mp_next;
	}

	z_test_required = z_test;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SDraw2D::InsertDrawList( void )
{
	if( !sp_2D_draw_list || ( m_pri <= sp_2D_draw_list->m_pri ))
	{
		// Empty or start of list.
		mp_next			= sp_2D_draw_list;
		sp_2D_draw_list	= this;
	}
	else
	{
		SDraw2D *p_cur	= sp_2D_draw_list;
	
		// Find where to insert.
		while( p_cur->mp_next )
		{
			if( m_pri <= p_cur->mp_next->m_pri )
				break;

			p_cur		= p_cur->mp_next;
		}

		// Insert at this point.
		mp_next			= p_cur->mp_next;
		p_cur->mp_next	= this;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SDraw2D::RemoveDrawList( void )
{
	// Take out from draw list
	if (sp_2D_draw_list == this)
	{
		sp_2D_draw_list = mp_next;
	} 
	else if (sp_2D_draw_list)
	{
		SDraw2D *p_cur = sp_2D_draw_list;

		while(p_cur->mp_next)
		{
			if (p_cur->mp_next == this)
			{
				p_cur->mp_next = mp_next;
				break;
			}

			p_cur = p_cur->mp_next;
		}
	}
}

	

typedef struct
{
	float		x, y, z;
	float		rhw;
	D3DCOLOR	col;
	float		u, v;
}
sSpriteVert;


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sSprite::sSprite( float pri ) : SDraw2D( pri, true )
{
	mp_texture = NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sSprite::~sSprite()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sSprite::BeginDraw( void )
{
	set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));

	if( mp_texture )
	{
		set_pixel_shader( PixelShader4 );
		set_texture( 0, mp_texture->pD3DTexture, mp_texture->pD3DPalette );
	}
	else
	{
		set_pixel_shader( PixelShader5 );
		set_texture( 0, NULL );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sSprite::Draw( void )
{
	// Sprites are based on .img files, which in turn are converted from .png files, which are upside down,
	// so reverse the v components of the texture coordinates.
	float u0 = 0.0f;
	float v0 = 1.0f;
	float u1, v1;

	if( mp_texture )
	{
		u1	= (float)mp_texture->ActualWidth / (float)mp_texture->BaseWidth;
		v1	= 1.0f - ((float)mp_texture->ActualHeight / (float)mp_texture->BaseHeight );
	}
	else
	{
		u1	= 1.0f;
		v1	= 0.0f;
	}

	// Check for flip.
	float abs_scale_x = m_scale_x;
	float abs_scale_y = m_scale_y;
	if( abs_scale_x < 0.0f )
	{
		float temp = u0;
		u0 = u1;
		u1 = temp;
		abs_scale_x = -abs_scale_x;
	}
	if( abs_scale_y < 0.0f )
	{
		float temp = v0;
		v0 = v1;
		v1 = temp;
		abs_scale_y = -abs_scale_y;
	}

	float x0 = -( m_xhot * abs_scale_x );
	float y0 = -( m_yhot * abs_scale_y );
	float x1 = x0 + ( m_width * abs_scale_x );
	float y1 = y0 + ( m_height * abs_scale_y );

	DWORD	current_color	= ( m_rgba & 0xFF00FF00 ) | (( m_rgba & 0xFF ) << 16 ) | (( m_rgba & 0xFF0000 ) >> 16 );
	DWORD*	p_push;

	if( m_rot != 0.0f )
	{
		Mth::Vector p0( x0, y0, 0.0f, 0.0f );
		Mth::Vector p1( x1, y0, 0.0f, 0.0f );
		Mth::Vector p2( x0, y1, 0.0f, 0.0f );
		Mth::Vector p3( x1, y1, 0.0f, 0.0f );

		p0.RotateZ( m_rot );
		p1.RotateZ( m_rot );
		p2.RotateZ( m_rot );
		p3.RotateZ( m_rot );

		p0[X]	= SCREEN_CONV_X( p0[X] + m_xpos );
		p0[Y]	= SCREEN_CONV_Y( p0[Y] + m_ypos );
		p1[X]	= SCREEN_CONV_X( p1[X] + m_xpos );
		p1[Y]	= SCREEN_CONV_Y( p1[Y] + m_ypos );
		p2[X]	= SCREEN_CONV_X( p2[X] + m_xpos );
		p2[Y]	= SCREEN_CONV_Y( p2[Y] + m_ypos );
		p3[X]	= SCREEN_CONV_X( p3[X] + m_xpos );
		p3[Y]	= SCREEN_CONV_Y( p3[Y] + m_ypos );

		// Now grab the push buffer space required.
		p_push			= D3DDevice_BeginPush( 34 );
		p_push[0]		= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]		= D3DPT_QUADLIST;
		p_push[2]		= D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, 28 );

		// Vertex0.
		p_push[3]		= *((uint32*)&p0[X] ); 
		p_push[4]		= *((uint32*)&p0[Y] );
		p_push[5]		= 0;
		p_push[6]		= 0;
		p_push[7]		= current_color;
		p_push[8]		= *((uint32*)&u0 );
		p_push[9]		= *((uint32*)&v0 );

		// Vertex1.
		p_push[10]		= *((uint32*)&p2[X] ); 
		p_push[11]		= *((uint32*)&p2[Y] );
		p_push[12]		= 0;
		p_push[13]		= 0;
		p_push[14]		= current_color;
		p_push[15]		= *((uint32*)&u0 );
		p_push[16]		= *((uint32*)&v1 );

		// Vertex2.
		p_push[17]		= *((uint32*)&p3[X] ); 
		p_push[18]		= *((uint32*)&p3[Y] );
		p_push[19]		= 0;
		p_push[20]		= 0;
		p_push[21]		= current_color;
		p_push[22]		= *((uint32*)&u1 );
		p_push[23]		= *((uint32*)&v1 );

		// Vertex3.
		p_push[24]		= *((uint32*)&p1[X] ); 
		p_push[25]		= *((uint32*)&p1[Y] );
		p_push[26]		= 0;
		p_push[27]		= 0;
		p_push[28]		= current_color;
		p_push[29]		= *((uint32*)&u1 );
		p_push[30]		= *((uint32*)&v0 );
	}
	else
	{
		x0 += m_xpos;
		y0 += m_ypos;
		x1 += m_xpos;
		y1 += m_ypos;

		// Nasty hack - if the sprite is intended to cover the screen from top to bottom or left to right,
		// bypass the addtional offset added by SCREEN_CONV.
		if(( x0 <= 0.0f ) && ( x1 >= 640.0f ))
		{
			x0 = 0.0f;
			x1 = (float)NxXbox::EngineGlobals.backbuffer_width;
		}
		else
		{
			x0 = SCREEN_CONV_X( x0 );
			x1 = SCREEN_CONV_X( x1 );
		}

		if(( y0 <= 0.0f ) && ( y1 >= 480.0f ))
		{
			y0 = 0.0f;
			y1 = (float)NxXbox::EngineGlobals.backbuffer_height;
		}
		else
		{
			y0 = SCREEN_CONV_Y( y0 );
			y1 = SCREEN_CONV_Y( y1 );
		}


		// Now grab the push buffer space required.
		p_push			= D3DDevice_BeginPush( 34 );
		p_push[0]		= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]		= D3DPT_QUADLIST;
		p_push[2]		= D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, 28 );

		// Vertex0.
		p_push[3]		= *((uint32*)&x0 ); 
		p_push[4]		= *((uint32*)&y0 );
		p_push[5]		= 0;
		p_push[6]		= 0;
		p_push[7]		= current_color;
		p_push[8]		= *((uint32*)&u0 );
		p_push[9]		= *((uint32*)&v0 );

		// Vertex1.
		p_push[10]		= *((uint32*)&x0 ); 
		p_push[11]		= *((uint32*)&y1 );
		p_push[12]		= 0;
		p_push[13]		= 0;
		p_push[14]		= current_color;
		p_push[15]		= *((uint32*)&u0 );
		p_push[16]		= *((uint32*)&v1 );

		// Vertex2.
		p_push[17]		= *((uint32*)&x1 ); 
		p_push[18]		= *((uint32*)&y1 );
		p_push[19]		= 0;
		p_push[20]		= 0;
		p_push[21]		= current_color;
		p_push[22]		= *((uint32*)&u1 );
		p_push[23]		= *((uint32*)&v1 );

		// Vertex3.
		p_push[24]		= *((uint32*)&x1 ); 
		p_push[25]		= *((uint32*)&y0 );
		p_push[26]		= 0;
		p_push[27]		= 0;
		p_push[28]		= current_color;
		p_push[29]		= *((uint32*)&u1 );
		p_push[30]		= *((uint32*)&v0 );
	}

	// End of vertex data for this sprite.
	p_push[31] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
	p_push[32] = 0;
	p_push += 33;
	D3DDevice_EndPush( p_push );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sSprite::EndDraw( void )
{
	// Vertices have been submitted - nothing more to do.
}



} // namespace NxXbox



================================================
FILE: Code/Gfx/XBox/NX/sprite.h
================================================
#ifndef __SPRITE_H
#define __SPRITE_H

#include "texture.h"

namespace NxXbox
{

struct SDraw2D
{
					SDraw2D( float pri = 0.0f, bool hide = true );
	virtual			~SDraw2D( void );

	void			SetPriority( float pri );
	float			GetPriority( void ) const;
	void			SetZValue( float z );
	float			GetZValue( void )			{ return m_zvalue; }

	void			SetHidden( bool hide );
	bool			IsHidden( void ) const;

	// members
	SDraw2D			*mp_next;

	// Statics
	static void		DrawAll( void );

private:
	void			InsertDrawList( void );
	void			RemoveDrawList( void );

	virtual void	BeginDraw( void ) = 0;
	virtual void	Draw( void ) = 0;
	virtual void	EndDraw( void ) = 0;

	// Not even the derived classes should have direct access
	bool			m_hidden;
	float			m_pri;
	float			m_zvalue;

	// 2D draw list (sorted by priority);
	static SDraw2D	*sp_2D_draw_list;
};


struct sSprite : public SDraw2D
{
	public:
					sSprite( float pri = 0.0f );
					~sSprite();

	sTexture		*mp_texture;

	float			m_xpos;
	float			m_ypos;
	uint16			m_width;
	uint16			m_height;
	float			m_scale_x;
	float			m_scale_y;
	float			m_xhot;
	float			m_yhot;
	float			m_rot;
	uint32			m_rgba;

private:
	
	void					BeginDraw();
	void					Draw();
	void					EndDraw(void);
};


} // namespace NxXbox


#endif // __SPRITE_H


================================================
FILE: Code/Gfx/XBox/NX/swizzleformat.h
================================================
//////////////////////////////////////////////////////////////////////
// Swizzled to linear and back format mapping.
//
//  Copyright (C) 2001 Microsoft Corporation
//  All rights reserved.
//////////////////////////////////////////////////////////////////////
inline D3DFORMAT MapLinearToSwizzledFormat(D3DFORMAT fmt)
{
    switch (fmt)
    {
    case D3DFMT_LIN_A1R5G5B5:     return D3DFMT_A1R5G5B5;
    case D3DFMT_LIN_A4R4G4B4:     return D3DFMT_A4R4G4B4;
    case D3DFMT_LIN_A8:         return D3DFMT_A8;
    case D3DFMT_LIN_A8B8G8R8:     return D3DFMT_A8B8G8R8;
    case D3DFMT_LIN_A8R8G8B8:     return D3DFMT_A8R8G8B8;
    case D3DFMT_LIN_B8G8R8A8:     return D3DFMT_B8G8R8A8;
    case D3DFMT_LIN_G8B8:         return D3DFMT_G8B8;
    case D3DFMT_LIN_R4G4B4A4:     return D3DFMT_R4G4B4A4;
    case D3DFMT_LIN_R5G5B5A1:     return D3DFMT_R5G5B5A1;
    case D3DFMT_LIN_R5G6B5:     return D3DFMT_R5G6B5;
    case D3DFMT_LIN_R6G5B5:     return D3DFMT_R6G5B5;
    case D3DFMT_LIN_R8B8:         return D3DFMT_R8B8;
    case D3DFMT_LIN_R8G8B8A8:     return D3DFMT_R8G8B8A8;
    case D3DFMT_LIN_X1R5G5B5:     return D3DFMT_X1R5G5B5;
    case D3DFMT_LIN_X8R8G8B8:     return D3DFMT_X8R8G8B8;
    case D3DFMT_LIN_A8L8:         return D3DFMT_A8L8;
    case D3DFMT_LIN_AL8:         return D3DFMT_AL8;
    case D3DFMT_LIN_L16:         return D3DFMT_L16;
    case D3DFMT_LIN_L8:         return D3DFMT_L8;
    case D3DFMT_LIN_V16U16:     return D3DFMT_V16U16;
    case D3DFMT_LIN_D24S8:         return D3DFMT_D24S8;
    case D3DFMT_LIN_F24S8:         return D3DFMT_F24S8;
    case D3DFMT_LIN_D16:         return D3DFMT_D16;
    case D3DFMT_LIN_F16:         return D3DFMT_F16;
    default:
        return fmt;
    }
}

inline D3DFORMAT MapSwizzledToLinearFormat(D3DFORMAT fmt)
{
    switch (fmt)
    {
    case D3DFMT_A1R5G5B5:     return D3DFMT_LIN_A1R5G5B5;
    case D3DFMT_A4R4G4B4:     return D3DFMT_LIN_A4R4G4B4;
    case D3DFMT_A8:         return D3DFMT_LIN_A8;
    case D3DFMT_A8B8G8R8:     return D3DFMT_LIN_A8B8G8R8;
    case D3DFMT_A8R8G8B8:     return D3DFMT_LIN_A8R8G8B8;
    case D3DFMT_B8G8R8A8:     return D3DFMT_LIN_B8G8R8A8;
    case D3DFMT_G8B8:         return D3DFMT_LIN_G8B8;
    case D3DFMT_R4G4B4A4:     return D3DFMT_LIN_R4G4B4A4;
    case D3DFMT_R5G5B5A1:     return D3DFMT_LIN_R5G5B5A1;
    case D3DFMT_R5G6B5:     return D3DFMT_LIN_R5G6B5;
    case D3DFMT_R6G5B5:     return D3DFMT_LIN_R6G5B5;
    case D3DFMT_R8B8:         return D3DFMT_LIN_R8B8;
    case D3DFMT_R8G8B8A8:     return D3DFMT_LIN_R8G8B8A8;
    case D3DFMT_X1R5G5B5:     return D3DFMT_LIN_X1R5G5B5;
    case D3DFMT_X8R8G8B8:     return D3DFMT_LIN_X8R8G8B8;
    case D3DFMT_A8L8:         return D3DFMT_LIN_A8L8;
    case D3DFMT_AL8:         return D3DFMT_LIN_AL8;
    case D3DFMT_L16:         return D3DFMT_LIN_L16;
    case D3DFMT_L8:         return D3DFMT_LIN_L8;
    case D3DFMT_V16U16:     return D3DFMT_LIN_V16U16;
    case D3DFMT_D24S8:         return D3DFMT_LIN_D24S8;
    case D3DFMT_F24S8:         return D3DFMT_LIN_F24S8;
    case D3DFMT_D16:         return D3DFMT_LIN_D16;
    case D3DFMT_F16:         return D3DFMT_LIN_F16;
    default:
        return fmt;
    }
}


================================================
FILE: Code/Gfx/XBox/NX/texture.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include "nx_init.h"
#include "chars.h"
#include "texture.h"
#include "render.h"

namespace NxXbox
{


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sTexture::sTexture()
{
	pD3DTexture = NULL;
	pD3DPalette	= NULL;
	pD3DSurface	= NULL;		// If used as a render target.
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sTexture::~sTexture()
{
	ULONG rr;

	if( pD3DTexture )
	{
		rr = pD3DTexture->Release();
		Dbg_Assert( rr == 0 );

		// Ensure that this texture is no longer referenced in the EngineGlobals.
		for( int p = 0; p < 4; ++p )
		{
			if( EngineGlobals.p_texture[p] == pD3DTexture )
			{
				set_texture( p, NULL );
			}
		}
	}
	if( pD3DPalette )
	{
		rr = pD3DPalette->Release();
		Dbg_Assert( rr == 0 );
	}
	if( pD3DSurface )
	{
		rr = pD3DSurface->Release();
		Dbg_Assert( rr == 0 );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sTexture::Set( int pass )
{
	// Set this texture as the active texture for a specific pass.
	set_texture( pass, pD3DTexture, pD3DPalette );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool sTexture::SetRenderTarget( int width, int height, int depth, int z_depth )
{
	HRESULT		hr;
	
	if( pD3DTexture )
	{
		pD3DTexture->Release();
	}
	if( pD3DPalette )
	{
		pD3DPalette->Release();
	}

	// Create the shadow buffer (essentially just a depth buffer).
	hr = D3DDevice_CreateTexture( width, height, 1, 0, D3DFMT_LIN_D24S8, 0, &pD3DTexture );
	Dbg_Assert( hr == D3D_OK );
	if( hr == D3D_OK )
	{
		// Set fields to reflect surface characteristics.
		Checksum		= 0;
		BaseWidth		= ActualWidth	= width;
		BaseHeight		= ActualHeight	= height;
		Levels			= 1;
		TexelDepth		= depth;
		PaletteDepth	= 0;
		DXT				= 0;
		return true;
	}
	return false;
}



// Eeeek - the .img contains PS2 specific register values for bit depth.
// Use these values to convert them.
#define PSMCT32		0x00
#define PSMCT24		0x01
#define PSMCT16		0x02
#define PSMCT16S	0x0A
#define PS_GPU24	0x12
#define PSMT8		0x13
#define PSMT4		0x14
#define PSMT8H		0x1B
#define PSMT4HL		0x24
#define PSMT4HH		0x2C
#define PSMZ32		0x30
#define PSMZ24		0x31
#define PSMZ16		0x32
#define PSMZ16S		0x3A



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static bool is_power_of_two( uint32 a )
{
	if( a == 0 )
	{
		return false;
	}
	return (( a & ( a - 1 )) == 0 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sTexture *LoadTexture( const char *p_filename )
{
	struct sIMGHeader
	{
		uint32	version;
		uint32	checksum;
		uint32	width;
		uint32	height;
		uint32	bit_depth;
		uint32	clut_bit_depth;
		uint16	original_width;
		uint16	original_height;
		uint32	palette_data_size;
	};

	void *p_FH = File::Open( p_filename, "rb" );
	
	if( p_FH )
	{
		// Read header.
		sIMGHeader header;
		File::Read( &header, sizeof( sIMGHeader ), 1, p_FH );
		
		// Bits per texel and palette size.
		switch( header.bit_depth )
		{
			case PSMCT32:
				header.bit_depth = 32;
				break;
			case PSMCT16:
				header.bit_depth = 16;
				break;
			case PSMT8:
				header.bit_depth = 8;
				break;
			default:
				Dbg_Assert( 0 );
		}

		// Bits per clut entry.
		if(	header.bit_depth < 16 )
		{
			switch( header.clut_bit_depth )
			{
				case PSMCT32:
					header.clut_bit_depth = 32;
					break;
				default:
					Dbg_Assert( 0 );
			}
		}
		else
		{
			header.clut_bit_depth = 0;
		}
		
		{
			// Create the texture object.
			sTexture *p_texture = new sTexture();

			// Create palette if required.
			if( header.clut_bit_depth == 0 )
			{
				p_texture->pD3DPalette = NULL;
			}
			else
			{
				if( D3D_OK != D3DDevice_CreatePalette( D3DPALETTE_256, &p_texture->pD3DPalette ))
				{
					Dbg_Assert( 0 );
				}
		
				// Read clut bitmap data.
				D3DCOLOR *p_clut;
				p_texture->pD3DPalette->Lock( &p_clut, 0 );

				int len	= File::Read( p_clut, header.palette_data_size, 1, p_FH );
				Dbg_MsgAssert( len == header.palette_data_size, ( "Couldn't read clut from texture file %s", p_filename ));
			}

			// Textures of width 512 and above will not have been resized. This means they cannot be in a swizzled format.
//			bool arbitrary_texture_size = ( header.original_width >= 512 );
			bool arbitrary_texture_size = false;
			
			if( !is_power_of_two( header.width ) || !is_power_of_two( header.height ))
				arbitrary_texture_size = true;

			// Create texture resource. Linear for arbitrary sized textures, swizzled for standard sizes.
			uint32 num_bytes;
			if( arbitrary_texture_size )
			{
				if( D3D_OK != D3DDevice_CreateTexture(	header.original_width,
														header.original_height,
														1,
														0,
														( header.bit_depth <= 8 ) ? D3DFMT_P8 : (( header.bit_depth == 16 ) ? D3DFMT_LIN_A1R5G5B5 : D3DFMT_LIN_A8R8G8B8 ),
														0,
														&p_texture->pD3DTexture ))
				{
					Dbg_Assert( 0 );
				}
				p_texture->BaseWidth	= header.original_width;
				p_texture->BaseHeight	= header.original_height;
				p_texture->ActualWidth	= header.original_width;
				p_texture->ActualHeight	= header.original_height;
				num_bytes				= ((( header.bit_depth / 8 ) * ( header.original_width ) * ( header.original_height )) + 3 ) & 0xFFFFFFFC;
			}
			else
			{
				if( D3D_OK != D3DDevice_CreateTexture(	header.width,
														header.height,
														1,
														0,
														( header.bit_depth <= 8 ) ? D3DFMT_P8 : (( header.bit_depth == 16 ) ? D3DFMT_A1R5G5B5 : D3DFMT_A8R8G8B8 ),
														0,
														&p_texture->pD3DTexture ))
				{
					Dbg_Assert( 0 );
				}
				p_texture->BaseWidth	= (uint16)header.width;
				p_texture->BaseHeight	= (uint16)header.height;
				p_texture->ActualWidth	= header.original_width;
				p_texture->ActualHeight	= header.original_height;
				num_bytes				= ((( header.bit_depth / 8 ) * header.width * header.height ) + 3 ) & 0xFFFFFFFC;
			}
	
			// Lock the texture so we can read data into it directly.
			D3DLOCKED_RECT locked_rect;
			if( D3D_OK != p_texture->pD3DTexture->LockRect( 0, &locked_rect, NULL, 0 ))
			{
				Dbg_Assert( 0 );
			}

			// Read texture bitmap data directly into texture. 
			int len = File::Read( locked_rect.pBits, num_bytes, 1, p_FH );
			Dbg_MsgAssert( len == num_bytes, ( "couldn't read texture data from texture file %s", p_filename ));
		
			File::Close( p_FH );

			// Set up some member values.
			p_texture->PaletteDepth	= (uint8)header.clut_bit_depth;
			p_texture->TexelDepth	= (uint8)header.bit_depth;
			p_texture->DXT			= 0;
			p_texture->Levels		= 1;
			
			return p_texture;
		}
	}
	return NULL;
}


} // namespace NxXbox



================================================
FILE: Code/Gfx/XBox/NX/texture.h
================================================
#ifndef __TEXTURE_H
#define __TEXTURE_H

#include 

namespace NxXbox
{

struct sTexture
{
						sTexture();
						~sTexture();
						
	bool				SetRenderTarget( int width, int height, int depth, int z_depth );
	void				Set( int pass );

	uint32				Checksum;
	uint16				BaseWidth, BaseHeight;		// The size of the D3D texture (will be power of 2).
	uint16				ActualWidth, ActualHeight;	// The size of the texture itself (may not be power of 2).

	uint8				Levels;
	uint8				TexelDepth;
	uint8				PaletteDepth;
	uint8				DXT;

	IDirect3DTexture8*	pD3DTexture;
	IDirect3DPalette8*	pD3DPalette;
	IDirect3DSurface8*	pD3DSurface;
};

sTexture	*LoadTexture( const char *p_filename );

} // namespace NxXbox

#endif // __TEXTURE_H



================================================
FILE: Code/Gfx/XBox/NX/verlet.cpp
================================================
#include 
#include 


#include "verlet.h"
#include "render.h"


const int	NUM_PARTICLES	= 16;
const float	PARTICLES_DIST	= 5.0f;


sVerletSystem*				pSystem				= NULL;
Obj::CSkeletonComponent*	pSkeletonComponent	= NULL;
float						ground				= -110.0f;


void CreateSystem( void )
{
	Obj::CCompositeObject *p_obj = (Obj::CCompositeObject *)Obj::CCompositeObjectManager::Instance()->GetObjectByID( 0 );
	if( p_obj )
	{
		pSkeletonComponent = GetSkeletonComponentFromObject( p_obj );
	}

	if( pSkeletonComponent )
	{
		pSystem = new sVerletSystem( NUM_PARTICLES );

		pSystem->m_timestep	= ( 1.0f / 60.0f );
		pSystem->m_gravity	= Mth::Vector( 0.0f, -1200.0f, 0.0f );

		// Get the skater's head position.
		Mth::Vector bone_pos;
		pSkeletonComponent->GetBoneWorldPosition( CRCD(0xe638eebc,"Bone_Forefinger_Tip_R"), &bone_pos );

		for( int i = 0; i < NUM_PARTICLES; ++i )
		{
			pSystem->mp_particles[i].m_pos		= bone_pos - ( Mth::Vector( 0.0f, PARTICLES_DIST * i, 0.0f ));
			pSystem->mp_particles[i].m_old_pos	= pSystem->mp_particles[i].m_pos;
		}

		for( int i = 0; i < ( NUM_PARTICLES - 1 ); ++i )
		{
			sVerletConstraint*	p_c = pSystem->AddConstraint();
			p_c->m_particle0		= i;
			p_c->m_particle1		= i + 1;
			p_c->m_constraint_dist	= PARTICLES_DIST;
		}
	}
}








void UpdateVerletSystem( void )
{
	if( pSystem == NULL )
	{
		CreateSystem();
	}

	if( pSystem )
	{
		// Set particle 0 to always be at the skater's head position.
		Mth::Vector bone_pos;
		pSkeletonComponent->GetBoneWorldPosition( CRCD(0xe638eebc,"Bone_Forefinger_Tip_R"), &bone_pos );
		pSystem->mp_particles[0].m_pos = bone_pos;

		pSystem->TimeStep();
	}
}

void DrawVerletSystem( void )
{
	struct sVerletDrawVert
	{
		float		x, y, z;
		D3DCOLOR	col;
	};

	if( pSystem )
	{
		sVerletDrawVert	dv[NUM_PARTICLES];
		for( int i = 0; i < NUM_PARTICLES; ++i )
		{
			dv[i].x		= pSystem->mp_particles[i].m_pos[X];
			dv[i].y		= pSystem->mp_particles[i].m_pos[Y];
			dv[i].z		= pSystem->mp_particles[i].m_pos[Z];
			dv[i].col	= 0xFFFFFFFF;
		}

		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
		NxXbox::set_pixel_shader( PixelShader5 );

		NxXbox::EngineGlobals.p_Device->DrawVerticesUP( D3DPT_LINESTRIP, NUM_PARTICLES, dv, sizeof( sVerletDrawVert ));
	}
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVerletParticle::sVerletParticle( void )
{
	m_pos		= Mth::Vector( 0.0f, 0.0f, 0.0f );
	m_old_pos	= m_pos;
	m_acc		= m_pos;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVerletParticle::~sVerletParticle( void )
{
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sVerletConstraint::Apply( sVerletParticle *p_particles )
{
	sVerletParticle* p_p0			= p_particles + m_particle0;
	sVerletParticle* p_p1			= p_particles + m_particle1;

	Mth::Vector		delta			= p_p1->m_pos - p_p0->m_pos;
	float			delta_length	= delta.Length();
	float			diff			= ( delta_length - m_constraint_dist ) / delta_length;

	if( m_particle0 == 0 )
	{
		// Move the points.
		p_p0->m_pos += delta * 0.0f * diff;
		p_p1->m_pos -= delta * 1.0f * diff;
	}
	else
	{
		// Move the points.
		p_p0->m_pos += delta * 0.5f * diff;
		p_p1->m_pos -= delta * 0.5f * diff;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVerletSystem::sVerletSystem( int num_particles )
{
	m_num_particles = num_particles;
	mp_particles	= new sVerletParticle[num_particles];
	mp_constraints	= new Lst::Head ;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVerletSystem::~sVerletSystem( void )
{
	delete [] mp_particles;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sVerletConstraint* sVerletSystem::AddConstraint( void )
{
	sVerletConstraint*				p_constraint	= new sVerletConstraint;
	Lst::Node*	p_node			= new Lst::Node( p_constraint );

	mp_constraints->AddToTail( p_node );

	return p_constraint;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sVerletSystem::Verlet( void )
{
	for( int i = 0; i < m_num_particles; ++i )
	{
		Mth::Vector &x		= mp_particles[i].m_pos;
		Mth::Vector t		= x;
		Mth::Vector &o		= mp_particles[i].m_old_pos;
		Mth::Vector a		= mp_particles[i].m_acc;
		x					= ( x * 2.0f ) - o + ( a * m_timestep * m_timestep );
		o					= t;
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sVerletSystem::AccumulateForces( void )
{
	// All particles are influenced by gravity.
	for( int i = 0; i < m_num_particles; ++i )
	{
		mp_particles[i].m_acc = m_gravity;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sVerletSystem::SatisfyConstraints( void )
{
	Lst::Node* p_node, *p_next;

	for( int i = 0; i < 2; ++i )
	{
		// Satisfy distance constraints.
		for( p_node = mp_constraints->GetNext(); p_node; p_node = p_next )
		{
			p_next = p_node->GetNext();
			sVerletConstraint *p_constraint = p_node->GetData();
			p_constraint->Apply( mp_particles );
		}

		// Satisfy ground constraints.
		for( int i = 0; i < m_num_particles; ++i )
		{
			if( mp_particles[i].m_pos[Y] < ground )
			{
				mp_particles[i].m_pos[Y] = ground;
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sVerletSystem::TimeStep( void )
{
	AccumulateForces();
	Verlet();
	SatisfyConstraints();
}


================================================
FILE: Code/Gfx/XBox/NX/verlet.h
================================================
#ifndef __VERLET_H
#define __VERLET_H

#include 

void			UpdateVerletSystem( void );
void			DrawVerletSystem( void );



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sVerletParticle
{
						sVerletParticle( void );
						~sVerletParticle( void );

	Mth::Vector			m_pos;
	Mth::Vector			m_old_pos;
	Mth::Vector			m_acc;
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sVerletConstraint
{
	int					m_particle0;
	int					m_particle1;
	float				m_elasticity;
	float				m_constraint_dist;
	void				Apply( sVerletParticle *p_particles );
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sVerletSystem
{
									sVerletSystem( int num_particles );
									~sVerletSystem();

	void							TimeStep( void );
	void							Verlet( void );
	void							SatisfyConstraints( void );
	void							AccumulateForces( void );

	sVerletConstraint*				AddConstraint( void );	

	int								m_num_particles;
	sVerletParticle*				mp_particles;
	Mth::Vector						m_gravity;
	float							m_timestep;

	Lst::Head *	mp_constraints;
};





#endif // __NX_INIT_H


================================================
FILE: Code/Gfx/XBox/NX/xbmemfnt.h
================================================

#define XBMEMFNT_LEN 68168

 static unsigned char xbmemfnt[]=
 {
  0x48,0x0a,0x01,0x00,0x83,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x13,
  0x00,0x00,0x00,0x10,0x00,0x41,0x00,0x10,0x00,0x42,0x00,0x10,0x00,
  0x43,0x00,0x10,0x00,0x44,0x00,0x10,0x00,0x45,0x00,0x10,0x00,0x46,
  0x00,0x10,0x00,0x47,0x00,0x10,0x00,0x48,0x00,0x10,0x00,0x49,0x00,
  0x10,0x00,0x4a,0x00,0x10,0x00,0x4b,0x00,0x10,0x00,0x4c,0x00,0x10,
  0x00,0x4d,0x00,0x10,0x00,0x4e,0x00,0x10,0x00,0x4f,0x00,0x10,0x00,
  0x50,0x00,0x10,0x00,0x51,0x00,0x10,0x00,0x52,0x00,0x10,0x00,0x53,
  0x00,0x10,0x00,0x54,0x00,0x10,0x00,0x55,0x00,0x10,0x00,0x56,0x00,
  0x10,0x00,0x57,0x00,0x10,0x00,0x58,0x00,0x10,0x00,0x59,0x00,0x10,
  0x00,0x5a,0x00,0x0d,0x00,0x61,0x00,0x10,0x00,0x62,0x00,0x0d,0x00,
  0x63,0x00,0x10,0x00,0x64,0x00,0x0d,0x00,0x65,0x00,0x10,0x00,0x66,
  0x00,0x0d,0x00,0x67,0x00,0x10,0x00,0x68,0x00,0x10,0x00,0x69,0x00,
  0x10,0x00,0x6a,0x00,0x10,0x00,0x6b,0x00,0x10,0x00,0x6c,0x00,0x0d,
  0x00,0x6d,0x00,0x0d,0x00,0x6e,0x00,0x0d,0x00,0x6f,0x00,0x0d,0x00,
  0x70,0x00,0x0d,0x00,0x71,0x00,0x0d,0x00,0x72,0x00,0x0d,0x00,0x73,
  0x00,0x0f,0x00,0x74,0x00,0x0d,0x00,0x75,0x00,0x0d,0x00,0x76,0x00,
  0x0d,0x00,0x77,0x00,0x0d,0x00,0x78,0x00,0x0d,0x00,0x79,0x00,0x0d,
  0x00,0x7a,0x00,0x0f,0x00,0x30,0x00,0x0f,0x00,0x31,0x00,0x0f,0x00,
  0x32,0x00,0x0f,0x00,0x33,0x00,0x0f,0x00,0x34,0x00,0x0f,0x00,0x35,
  0x00,0x10,0x00,0x36,0x00,0x0f,0x00,0x37,0x00,0x0f,0x00,0x38,0x00,
  0x10,0x00,0x39,0x00,0x08,0x00,0x2e,0x00,0x08,0x00,0x2c,0x00,0x0a,
  0x00,0x2d,0x00,0x10,0x00,0x21,0x00,0x10,0x00,0x3f,0x00,0x0d,0x00,
  0x3a,0x00,0x10,0x00,0x27,0x00,0x0e,0x00,0x2b,0x00,0x10,0x00,0x2f,
  0x00,0x10,0x00,0x5e,0x00,0x10,0x00,0xae,0x00,0x10,0x00,0x28,0x00,
  0x10,0x00,0x29,0x00,0x10,0x00,0x2a,0x00,0x10,0x00,0x40,0x00,0x12,
  0x00,0x60,0x00,0x10,0x00,0xa1,0x00,0x0f,0x00,0xa2,0x00,0x0f,0x00,
  0xa3,0x00,0x0f,0x00,0xa4,0x00,0x0f,0x00,0xa5,0x00,0x10,0x00,0xa6,
  0x00,0x10,0x00,0xa7,0x00,0x11,0x00,0xa8,0x00,0x10,0x00,0xa9,0x00,
  0x10,0x00,0xaa,0x00,0x0b,0x00,0xab,0x00,0x0b,0x00,0xac,0x00,0x10,
  0x00,0x7b,0x00,0x02,0x00,0x5f,0x00,0x10,0x00,0x23,0x00,0x10,0x00,
  0x24,0x00,0x10,0x00,0x25,0x00,0x10,0x00,0x26,0x00,0x11,0x00,0x5c,
  0x00,0x0c,0x00,0x3d,0x00,0x0e,0x00,0x3c,0x00,0x0e,0x00,0x3e,0x00,
  0x10,0x00,0xdf,0x00,0x13,0x00,0xc4,0x00,0x13,0x00,0xdc,0x00,0x13,
  0x00,0xd6,0x00,0x11,0x00,0xe0,0x00,0x11,0x00,0xe2,0x00,0x13,0x00,
  0xc4,0x00,0x11,0x00,0xea,0x00,0x12,0x00,0xe8,0x00,0x12,0x00,0xe9,
  0x00,0x11,0x00,0xeb,0x00,0x11,0x00,0xec,0x00,0x11,0x00,0xee,0x00,
  0x11,0x00,0xef,0x00,0x11,0x00,0xf4,0x00,0x11,0x00,0xf2,0x00,0x13,
  0x00,0xd6,0x00,0x12,0x00,0xf9,0x00,0x11,0x00,0xfb,0x00,0x13,0x00,
  0xdc,0x00,0x0d,0x00,0xe7,0x00,0x0d,0x00,0x9c,0x00,0x11,0x00,0xfc,
  0x00,0x11,0x00,0xe4,0x00,0x11,0x00,0xf6,0x00,0x10,0x00,0xbc,0x00,
  0x10,0x00,0xbd,0x00,0x10,0x00,0xbe,0x00,0x10,0x00,0xbf,0x00,0x10,
  0x00,0xba,0x00,0x10,0x00,0xe0,0xff,0x2c,0x08,0x01,0x00,0x00,0x01,
  0x00,0x01,0x08,0x00,0xfc,0xfe,0x12,0x00,0xdf,0xf2,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,
  0x35,0x35,0x35,0x35,0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x20,0x35,0x35,0x35,0x35,0x35,0x35,0x20,0x00,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x08,0x30,0x39,0x0f,
  0x2f,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x0e,0x35,0x35,
  0x35,0x35,0x35,0x20,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
  0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x34,0x01,0x01,0x01,0x01,
  0x01,0x01,0x0e,0x35,0x35,0x35,0x35,0x35,0x35,0x0e,0x34,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x2e,0x3a,0x1b,0x3a,0x2e,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x35,0x35,0x35,0x35,
  0x01,0x00,0x2b,0x20,0x35,0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x20,
  0x35,0x20,0x2b,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x34,0x35,0x35,0x35,0x35,0x01,0x01,0x01,0x01,0x01,0x35,
  0x35,0x35,0x35,0x00,0x01,0x01,0x35,0x35,0x20,0x0e,0x20,0x01,0x01,
  0x01,0x01,0x01,0x34,0x35,0x35,0x35,0x20,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x0e,0x35,0x35,0x35,0x35,0x35,0x34,0x01,0x20,
  0x0e,0x35,0x35,0x35,0x0e,0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x35,
  0x35,0x35,0x35,0x35,0x00,0x01,0x00,0x35,0x35,0x0e,0x00,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x34,0x3c,0x0f,0x11,0x37,0x3c,0x34,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,
  0x01,0x01,0x08,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x01,0x01,0x3d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x3d,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x04,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x04,0x01,0x01,0x01,0x01,0x01,
  0x17,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x17,0x01,0x01,
  0x01,0x01,0x01,0x04,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x38,0x2b,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x2c,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x3d,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x2c,0x01,0x01,0x01,0x3d,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x3d,0x01,0x01,0x01,0x3d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x3d,0x1e,0x1e,0x1e,0x1e,0x1e,0x3d,0x01,0x01,0x01,0x01,0x2b,
  0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x20,0x07,0x1e,0x2d,0x2d,0x09,0x09,0x2d,0x2d,0x1e,0x32,0x01,
  0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x09,0x2d,0x09,0x09,0x09,0x09,
  0x2d,0x09,0x1e,0x1e,0x07,0x2b,0x01,0x01,0x01,0x01,0x08,0x1e,0x1e,
  0x09,0x2a,0x29,0x0d,0x26,0x31,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x0f,
  0x1e,0x18,0x10,0x09,0x09,0x09,0x09,0x2d,0x09,0x1e,0x1e,0x40,0x2b,
  0x01,0x01,0x17,0x1e,0x1e,0x2d,0x2d,0x09,0x09,0x09,0x09,0x2d,0x2d,
  0x18,0x1e,0x30,0x01,0x20,0x07,0x1e,0x10,0x09,0x09,0x09,0x09,0x09,
  0x09,0x10,0x18,0x1e,0x1c,0x01,0x01,0x01,0x17,0x1e,0x1e,0x09,0x28,
  0x15,0x15,0x15,0x28,0x1e,0x1e,0x1e,0x17,0x01,0x01,0x01,0x30,0x1e,
  0x18,0x2d,0x2d,0x2d,0x2d,0x1e,0x1e,0x21,0x09,0x2d,0x2d,0x1e,0x38,
  0x01,0x01,0x38,0x1e,0x09,0x2d,0x09,0x21,0x1e,0x1e,0x2f,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2c,0x1e,0x18,0x2d,0x09,0x2d,0x2d,0x1e,
  0x38,0x01,0x38,0x1e,0x2d,0x2d,0x2d,0x2d,0x1e,0x1e,0x1e,0x2d,0x2d,
  0x09,0x10,0x09,0x1e,0x3b,0x01,0x1c,0x1e,0x18,0x2d,0x09,0x2d,0x09,
  0x1e,0x3e,0x34,0x01,0x01,0x01,0x01,0x38,0x1e,0x10,0x2d,0x09,0x09,
  0x2d,0x2d,0x1e,0x1e,0x09,0x10,0x09,0x09,0x09,0x10,0x1e,0x1e,0x3a,
  0x01,0x08,0x1e,0x1e,0x2d,0x2d,0x09,0x2d,0x2d,0x1e,0x1e,0x1e,0x2d,
  0x2d,0x10,0x1e,0x1e,0x2e,0x01,0x01,0x2b,0x3e,0x1e,0x1e,0x2a,0x06,
  0x19,0x24,0x16,0x1e,0x1e,0x1e,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3c,0x1e,0x16,
  0x13,0x13,0x13,0x13,0x13,0x13,0x03,0x1e,0x2e,0x01,0x01,0x01,0x01,
  0x35,0x1e,0x09,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x16,
  0x1e,0x38,0x01,0x01,0x01,0x3c,0x1e,0x1e,0x12,0x0c,0x13,0x1f,0x1f,
  0x13,0x05,0x14,0x2d,0x1e,0x38,0x01,0x01,0x39,0x1e,0x02,0x13,0x13,
  0x13,0x13,0x13,0x13,0x13,0x13,0x25,0x1e,0x38,0x01,0x01,0x0f,0x1e,
  0x06,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x22,0x1e,0x32,0x01,
  0x08,0x1e,0x25,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x0b,0x1e,
  0x1d,0x01,0x01,0x35,0x40,0x1e,0x06,0x13,0x0c,0x22,0x1f,0x1f,0x1f,
  0x13,0x26,0x1e,0x33,0x0e,0x01,0x01,0x32,0x1e,0x22,0x0c,0x13,0x05,
  0x14,0x1e,0x15,0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x36,0x1e,0x31,
  0x13,0x13,0x13,0x13,0x24,0x1e,0x37,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x07,0x1e,0x05,0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x1e,0x1e,
  0x13,0x0c,0x22,0x1f,0x12,0x1e,0x02,0x0c,0x13,0x1f,0x1f,0x29,0x1e,
  0x30,0x01,0x1d,0x1e,0x23,0x05,0x14,0x13,0x13,0x16,0x1e,0x3c,0x01,
  0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,0x13,0x13,0x13,0x02,0x1e,
  0x13,0x13,0x13,0x13,0x13,0x13,0x27,0x1e,0x2c,0x01,0x1c,0x1e,0x12,
  0x1f,0x1f,0x13,0x05,0x14,0x23,0x1e,0x06,0x13,0x0c,0x13,0x29,0x1e,
  0x04,0x01,0x01,0x38,0x1e,0x2a,0x13,0x13,0x13,0x13,0x13,0x13,0x13,
  0x15,0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x3a,0x1e,0x26,0x13,0x13,0x13,0x13,
  0x13,0x13,0x0d,0x1e,0x2c,0x01,0x01,0x01,0x01,0x34,0x1e,0x18,0x1f,
  0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,
  0x01,0x04,0x1e,0x24,0x13,0x0b,0x1f,0x0c,0x05,0x0c,0x0c,0x13,0x1f,
  0x1e,0x1e,0x2f,0x01,0x37,0x1e,0x06,0x23,0x14,0x13,0x13,0x13,0x13,
  0x13,0x13,0x13,0x10,0x1e,0x1b,0x01,0x3c,0x1e,0x16,0x13,0x13,0x13,
  0x13,0x13,0x13,0x13,0x13,0x27,0x1e,0x2c,0x01,0x36,0x1e,0x31,0x13,
  0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x27,0x1e,0x2c,0x01,0x01,0x3a,
  0x1e,0x25,0x0c,0x0c,0x0c,0x13,0x1f,0x14,0x0c,0x13,0x0b,0x0a,0x1e,
  0x11,0x01,0x01,0x30,0x1e,0x24,0x14,0x13,0x05,0x02,0x1e,0x25,0x0c,
  0x23,0x14,0x13,0x1e,0x1e,0x01,0x35,0x1e,0x2d,0x05,0x13,0x13,0x13,
  0x0a,0x1e,0x3b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2c,0x1e,0x0d,
  0x13,0x13,0x13,0x0c,0x1e,0x33,0x01,0x1e,0x1e,0x0c,0x0c,0x13,0x13,
  0x2a,0x1e,0x13,0x0b,0x1f,0x0c,0x05,0x1e,0x1e,0x3b,0x01,0x1c,0x1e,
  0x24,0x05,0x02,0x14,0x13,0x21,0x1e,0x2b,0x01,0x01,0x01,0x01,0x1e,
  0x1e,0x13,0x13,0x13,0x13,0x13,0x13,0x29,0x1e,0x22,0x13,0x13,0x13,
  0x13,0x13,0x26,0x1e,0x0f,0x01,0x3b,0x1e,0x0a,0x0c,0x05,0x0c,0x0c,
  0x13,0x19,0x1e,0x2a,0x14,0x14,0x13,0x28,0x1e,0x1b,0x01,0x36,0x1e,
  0x21,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x31,0x1e,0x0f,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2c,0x1e,0x27,0x13,0x13,0x14,0x13,0x13,0x13,0x05,0x1e,
  0x40,0x01,0x01,0x01,0x01,0x34,0x1e,0x18,0x0c,0x13,0x13,0x13,0x06,
  0x12,0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,0x01,0x3e,0x1e,0x14,
  0x13,0x0c,0x13,0x0d,0x31,0x13,0x02,0x14,0x0c,0x06,0x1e,0x37,0x01,
  0x37,0x1e,0x06,0x19,0x12,0x14,0x14,0x27,0x0b,0x14,0x13,0x13,0x27,
  0x1e,0x2c,0x01,0x08,0x1e,0x25,0x13,0x13,0x13,0x13,0x14,0x23,0x14,
  0x05,0x06,0x1e,0x0f,0x01,0x36,0x1e,0x31,0x13,0x13,0x13,0x13,0x14,
  0x19,0x14,0x0c,0x06,0x1e,0x0f,0x01,0x01,0x39,0x1e,0x1a,0x19,0x13,
  0x0c,0x19,0x1e,0x0c,0x0c,0x13,0x0c,0x0c,0x1e,0x33,0x01,0x01,0x3d,
  0x1e,0x29,0x0c,0x1f,0x23,0x0c,0x1e,0x2a,0x1f,0x19,0x12,0x14,0x1e,
  0x1e,0x01,0x35,0x1e,0x2d,0x1f,0x13,0x13,0x13,0x28,0x1e,0x2e,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x1a,0x13,0x13,0x13,0x0c,
  0x1e,0x33,0x01,0x1e,0x1e,0x13,0x0c,0x14,0x13,0x2a,0x25,0x13,0x0c,
  0x13,0x05,0x16,0x1e,0x38,0x01,0x01,0x1c,0x1e,0x24,0x23,0x13,0x0b,
  0x02,0x21,0x1e,0x36,0x01,0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,
  0x13,0x13,0x13,0x27,0x10,0x14,0x13,0x13,0x13,0x13,0x1f,0x15,0x1e,
  0x0f,0x01,0x2e,0x1e,0x0a,0x05,0x1f,0x13,0x02,0x14,0x0c,0x1e,0x10,
  0x0c,0x0c,0x1f,0x28,0x1e,0x1b,0x01,0x3a,0x1e,0x26,0x0b,0x14,0x13,
  0x22,0x10,0x05,0x13,0x13,0x13,0x19,0x1e,0x11,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x1e,0x18,
  0x1f,0x13,0x13,0x16,0x05,0x13,0x13,0x13,0x1e,0x1e,0x34,0x01,0x01,
  0x01,0x34,0x1e,0x18,0x0c,0x13,0x13,0x0c,0x1e,0x1e,0x13,0x13,0x13,
  0x13,0x1e,0x1e,0x01,0x01,0x01,0x07,0x1e,0x22,0x0c,0x22,0x13,0x0a,
  0x1e,0x0c,0x1f,0x13,0x0c,0x1a,0x1e,0x39,0x01,0x37,0x1e,0x06,0x0b,
  0x0c,0x1f,0x12,0x1e,0x24,0x24,0x14,0x13,0x05,0x1e,0x07,0x01,0x08,
  0x1e,0x25,0x13,0x13,0x13,0x13,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x17,
  0x01,0x36,0x1e,0x31,0x13,0x13,0x13,0x1f,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x17,0x01,0x01,0x38,0x1e,0x23,0x0c,0x13,0x0c,0x24,0x1e,0x31,
  0x1a,0x29,0x12,0x06,0x1e,0x1e,0x00,0x01,0x3d,0x1e,0x06,0x0b,0x0c,
  0x19,0x14,0x1e,0x0a,0x14,0x0b,0x0c,0x1f,0x1e,0x1e,0x01,0x35,0x1e,
  0x2d,0x1f,0x13,0x13,0x13,0x28,0x1e,0x2e,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x04,0x1e,0x1a,0x13,0x13,0x13,0x0c,0x1e,0x33,0x01,0x1e,
  0x1e,0x13,0x0c,0x13,0x1f,0x25,0x27,0x0c,0x22,0x13,0x22,0x1e,0x07,
  0x2b,0x01,0x01,0x1c,0x1e,0x12,0x19,0x05,0x05,0x14,0x21,0x1e,0x36,
  0x01,0x01,0x01,0x01,0x1e,0x1e,0x05,0x13,0x13,0x13,0x1f,0x13,0x19,
  0x2a,0x05,0x13,0x05,0x13,0x13,0x13,0x06,0x1e,0x0f,0x01,0x2e,0x1e,
  0x0a,0x0c,0x0c,0x0c,0x05,0x13,0x0c,0x16,0x2d,0x27,0x0b,0x0c,0x28,
  0x1e,0x1b,0x01,0x2c,0x1e,0x0d,0x02,0x24,0x14,0x1a,0x1e,0x12,0x05,
  0x13,0x13,0x02,0x1e,0x39,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x17,0x1e,0x31,0x22,0x13,0x13,0x2a,
  0x1a,0x13,0x13,0x13,0x16,0x1e,0x3b,0x01,0x01,0x01,0x34,0x1e,0x18,
  0x0c,0x13,0x13,0x13,0x12,0x0d,0x13,0x13,0x13,0x1f,0x1e,0x40,0x01,
  0x01,0x0e,0x1e,0x10,0x05,0x0c,0x13,0x05,0x26,0x1e,0x2a,0x16,0x16,
  0x25,0x21,0x1e,0x3a,0x01,0x37,0x1e,0x24,0x02,0x19,0x1f,0x23,0x1e,
  0x28,0x0c,0x14,0x27,0x0b,0x1e,0x1d,0x01,0x08,0x1e,0x25,0x13,0x13,
  0x13,0x13,0x12,0x15,0x06,0x06,0x1e,0x1e,0x00,0x01,0x36,0x1e,0x31,
  0x13,0x13,0x13,0x13,0x0d,0x28,0x06,0x06,0x10,0x1e,0x36,0x01,0x00,
  0x1e,0x1e,0x0b,0x0c,0x13,0x1f,0x1a,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x3d,0x01,0x01,0x3d,0x1e,0x29,0x0c,0x05,0x22,0x0c,0x06,0x29,
  0x0c,0x02,0x19,0x1f,0x1e,0x1e,0x01,0x35,0x1e,0x2d,0x23,0x13,0x13,
  0x1f,0x0a,0x1e,0x2e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,
  0x1a,0x13,0x13,0x13,0x0c,0x1e,0x33,0x01,0x1e,0x1e,0x13,0x1f,0x0c,
  0x13,0x23,0x1f,0x0c,0x13,0x05,0x31,0x1e,0x3d,0x01,0x01,0x01,0x1c,
  0x1e,0x24,0x22,0x0c,0x24,0x0d,0x31,0x1e,0x36,0x01,0x01,0x01,0x01,
  0x1e,0x1e,0x26,0x22,0x13,0x0c,0x29,0x1f,0x22,0x15,0x1f,0x0c,0x02,
  0x0c,0x13,0x0c,0x06,0x1e,0x0f,0x01,0x2e,0x1e,0x0a,0x23,0x0c,0x14,
  0x29,0x0c,0x13,0x29,0x2a,0x1f,0x0c,0x05,0x15,0x1e,0x1b,0x01,0x3d,
  0x1e,0x12,0x0b,0x0c,0x14,0x12,0x1e,0x29,0x26,0x22,0x13,0x14,0x1e,
  0x3e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x3a,0x1e,0x15,0x0c,0x19,0x0b,0x2d,0x25,0x1f,0x13,0x13,
  0x29,0x1e,0x04,0x01,0x01,0x01,0x34,0x1e,0x18,0x0c,0x13,0x13,0x13,
  0x13,0x13,0x13,0x13,0x0c,0x1e,0x1e,0x39,0x01,0x01,0x0e,0x1e,0x10,
  0x1f,0x05,0x0c,0x13,0x06,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x11,0x01,
  0x01,0x37,0x1e,0x06,0x14,0x1f,0x0d,0x29,0x1e,0x28,0x0b,0x13,0x05,
  0x23,0x1e,0x32,0x01,0x08,0x1e,0x25,0x13,0x13,0x13,0x13,0x13,0x13,
  0x13,0x13,0x21,0x1e,0x2b,0x01,0x36,0x1e,0x31,0x13,0x13,0x13,0x13,
  0x13,0x13,0x13,0x13,0x26,0x1e,0x3a,0x01,0x20,0x1e,0x09,0x1f,0x0c,
  0x05,0x05,0x12,0x10,0x29,0x12,0x24,0x12,0x26,0x1e,0x38,0x01,0x01,
  0x3d,0x1e,0x06,0x0c,0x0c,0x22,0x0b,0x14,0x23,0x22,0x14,0x1f,0x0d,
  0x1e,0x1e,0x01,0x35,0x1e,0x2d,0x14,0x19,0x0b,0x14,0x16,0x1e,0x2e,
  0x01,0x01,0x01,0x3c,0x37,0x1b,0x1c,0x04,0x1e,0x1a,0x13,0x13,0x13,
  0x0c,0x1e,0x33,0x01,0x1e,0x1e,0x05,0x05,0x13,0x05,0x13,0x13,0x05,
  0x0c,0x19,0x1e,0x32,0x34,0x01,0x01,0x01,0x1c,0x1e,0x12,0x22,0x0b,
  0x14,0x23,0x21,0x1e,0x36,0x01,0x01,0x01,0x01,0x1e,0x1e,0x1f,0x0c,
  0x19,0x23,0x15,0x06,0x14,0x1a,0x0c,0x05,0x12,0x14,0x13,0x1f,0x06,
  0x1e,0x0f,0x01,0x2e,0x1e,0x0a,0x05,0x0c,0x13,0x29,0x22,0x23,0x22,
  0x0a,0x1a,0x0c,0x0c,0x28,0x1e,0x1b,0x01,0x3d,0x1e,0x29,0x27,0x0b,
  0x13,0x27,0x1e,0x1a,0x1f,0x0c,0x19,0x0b,0x1e,0x1e,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x39,0x1e,
  0x12,0x13,0x05,0x0b,0x1e,0x18,0x0c,0x02,0x22,0x22,0x1e,0x3e,0x01,
  0x01,0x01,0x34,0x1e,0x18,0x0c,0x13,0x13,0x13,0x19,0x1f,0x13,0x13,
  0x1f,0x15,0x1e,0x38,0x01,0x01,0x0e,0x1e,0x10,0x14,0x05,0x13,0x0c,
  0x24,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x2b,0x01,0x37,0x1e,0x06,
  0x22,0x05,0x0c,0x02,0x1e,0x15,0x0c,0x02,0x0d,0x19,0x1e,0x32,0x01,
  0x08,0x1e,0x16,0x22,0x0c,0x13,0x13,0x13,0x13,0x13,0x13,0x31,0x1e,
  0x36,0x01,0x36,0x1e,0x31,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,
  0x06,0x1e,0x0f,0x01,0x35,0x1e,0x2d,0x02,0x0c,0x1f,0x22,0x26,0x12,
  0x0c,0x05,0x05,0x13,0x0c,0x1e,0x1e,0x01,0x01,0x3d,0x1e,0x29,0x05,
  0x27,0x05,0x0b,0x06,0x27,0x14,0x22,0x05,0x0c,0x1e,0x1e,0x01,0x35,
  0x1e,0x2d,0x1f,0x05,0x0b,0x14,0x0a,0x1e,0x2e,0x01,0x01,0x32,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1a,0x13,0x13,0x13,0x0c,0x1e,0x33,0x01,
  0x1e,0x1e,0x1f,0x22,0x13,0x1f,0x05,0x05,0x05,0x13,0x0c,0x09,0x1e,
  0x08,0x01,0x01,0x01,0x1c,0x1e,0x26,0x05,0x0b,0x06,0x27,0x31,0x1e,
  0x36,0x01,0x01,0x01,0x01,0x1e,0x1e,0x02,0x13,0x05,0x19,0x21,0x0c,
  0x0c,0x0d,0x0b,0x0b,0x15,0x0c,0x0c,0x02,0x06,0x1e,0x0f,0x01,0x2e,
  0x1e,0x0a,0x0c,0x13,0x1f,0x06,0x29,0x05,0x1f,0x0d,0x14,0x05,0x27,
  0x15,0x1e,0x1b,0x01,0x3d,0x1e,0x12,0x14,0x0c,0x02,0x24,0x1e,0x29,
  0x02,0x13,0x05,0x0b,0x1e,0x1e,0x00,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x1e,0x18,0x05,0x19,0x12,0x14,
  0x28,0x28,0x13,0x14,0x22,0x1f,0x1e,0x33,0x00,0x01,0x01,0x34,0x1e,
  0x18,0x0c,0x13,0x13,0x13,0x09,0x18,0x0c,0x13,0x13,0x1f,0x10,0x1e,
  0x0e,0x01,0x0e,0x1e,0x10,0x14,0x23,0x13,0x0c,0x06,0x1e,0x0d,0x23,
  0x27,0x19,0x15,0x1e,0x37,0x01,0x37,0x1e,0x28,0x14,0x22,0x29,0x02,
  0x1e,0x26,0x1f,0x0c,0x14,0x19,0x1e,0x1d,0x01,0x08,0x1e,0x16,0x22,
  0x0c,0x13,0x13,0x0a,0x31,0x2a,0x2a,0x1e,0x38,0x01,0x01,0x36,0x1e,
  0x31,0x13,0x13,0x13,0x13,0x26,0x21,0x25,0x16,0x09,0x1e,0x17,0x01,
  0x34,0x1e,0x18,0x05,0x0c,0x0c,0x13,0x24,0x2a,0x0c,0x1f,0x23,0x13,
  0x23,0x1e,0x1d,0x01,0x01,0x3d,0x1e,0x12,0x13,0x23,0x22,0x1f,0x10,
  0x24,0x29,0x14,0x22,0x29,0x1e,0x1e,0x01,0x35,0x1e,0x2d,0x02,0x12,
  0x14,0x23,0x25,0x1e,0x2e,0x01,0x3b,0x1e,0x03,0x24,0x15,0x24,0x28,
  0x1e,0x1a,0x13,0x13,0x13,0x0c,0x1e,0x33,0x01,0x1e,0x1e,0x0c,0x13,
  0x0c,0x19,0x06,0x23,0x23,0x13,0x0c,0x12,0x1e,0x38,0x01,0x01,0x01,
  0x1c,0x1e,0x06,0x22,0x1f,0x0c,0x14,0x10,0x1e,0x1e,0x38,0x1b,0x01,
  0x01,0x1e,0x1e,0x0c,0x19,0x12,0x0b,0x18,0x27,0x13,0x0b,0x22,0x19,
  0x03,0x05,0x13,0x23,0x06,0x1e,0x0f,0x01,0x2e,0x1e,0x0a,0x1f,0x13,
  0x05,0x25,0x21,0x1f,0x13,0x19,0x0b,0x13,0x23,0x28,0x1e,0x1b,0x01,
  0x3d,0x1e,0x29,0x22,0x1f,0x0c,0x1a,0x1e,0x0d,0x0c,0x19,0x12,0x02,
  0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x36,0x1e,0x21,0x1f,0x0c,0x0b,0x22,0x1f,0x13,0x23,0x0d,
  0x1f,0x22,0x2a,0x1e,0x08,0x01,0x01,0x34,0x1e,0x18,0x0c,0x13,0x13,
  0x13,0x1e,0x1e,0x1f,0x13,0x0c,0x1f,0x0a,0x1e,0x2e,0x01,0x01,0x3e,
  0x1e,0x14,0x13,0x0c,0x23,0x16,0x1e,0x23,0x05,0x0c,0x1f,0x02,0x1e,
  0x11,0x01,0x37,0x1e,0x15,0x02,0x1f,0x1f,0x0d,0x1e,0x1a,0x05,0x29,
  0x22,0x14,0x1e,0x40,0x01,0x08,0x1e,0x2a,0x1f,0x22,0x13,0x13,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x08,0x01,0x36,0x1e,0x31,0x13,0x13,0x13,
  0x0c,0x2d,0x1e,0x1e,0x1e,0x1e,0x2c,0x01,0x01,0x00,0x1e,0x1e,0x02,
  0x0c,0x1f,0x1f,0x06,0x1e,0x15,0x13,0x13,0x0c,0x27,0x1e,0x32,0x01,
  0x01,0x3d,0x1e,0x29,0x0c,0x05,0x13,0x19,0x1e,0x03,0x02,0x02,0x1f,
  0x1f,0x1e,0x1e,0x01,0x35,0x1e,0x2d,0x14,0x0b,0x22,0x1f,0x0a,0x1e,
  0x2e,0x01,0x30,0x1e,0x24,0x0c,0x13,0x0c,0x05,0x1e,0x06,0x13,0x13,
  0x13,0x0c,0x1e,0x33,0x01,0x1e,0x1e,0x1f,0x1f,0x13,0x1f,0x16,0x24,
  0x13,0x0c,0x23,0x05,0x18,0x1e,0x08,0x01,0x01,0x1c,0x1e,0x24,0x13,
  0x19,0x02,0x05,0x10,0x1e,0x1e,0x1e,0x1e,0x1b,0x01,0x1e,0x1e,0x1f,
  0x0c,0x0b,0x0b,0x1e,0x13,0x23,0x0d,0x1f,0x12,0x10,0x1f,0x05,0x27,
  0x26,0x1e,0x0f,0x01,0x2e,0x1e,0x25,0x05,0x13,0x23,0x16,0x1e,0x1f,
  0x0c,0x05,0x0c,0x0c,0x05,0x15,0x1e,0x1b,0x01,0x2c,0x1e,0x24,0x05,
  0x05,0x29,0x0d,0x1e,0x12,0x1f,0x0c,0x0b,0x27,0x1e,0x38,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3b,0x1e,
  0x25,0x13,0x1a,0x19,0x13,0x0b,0x05,0x0c,0x0b,0x05,0x1f,0x29,0x1e,
  0x30,0x01,0x01,0x34,0x1e,0x18,0x0c,0x13,0x1f,0x0c,0x1a,0x14,0x22,
  0x13,0x0c,0x1f,0x0a,0x1e,0x3b,0x01,0x01,0x07,0x1e,0x22,0x1f,0x13,
  0x1f,0x19,0x26,0x13,0x0c,0x0b,0x05,0x25,0x1e,0x37,0x01,0x37,0x1e,
  0x06,0x0c,0x0b,0x02,0x05,0x0d,0x02,0x0c,0x05,0x1f,0x24,0x1e,0x2c,
  0x01,0x08,0x1e,0x16,0x05,0x1f,0x13,0x13,0x0c,0x14,0x22,0x13,0x24,
  0x1e,0x1c,0x01,0x36,0x1e,0x31,0x13,0x13,0x13,0x13,0x03,0x1e,0x08,
  0x3c,0x20,0x01,0x01,0x01,0x01,0x39,0x1e,0x27,0x0c,0x0c,0x13,0x23,
  0x21,0x14,0x1f,0x1f,0x13,0x23,0x1e,0x32,0x01,0x01,0x3d,0x1e,0x12,
  0x1f,0x22,0x0c,0x23,0x1e,0x25,0x14,0x0c,0x0b,0x02,0x1e,0x1e,0x01,
  0x35,0x1e,0x2d,0x0d,0x19,0x13,0x0b,0x0a,0x1e,0x2e,0x01,0x1b,0x1e,
  0x15,0x14,0x13,0x1f,0x13,0x06,0x14,0x13,0x1f,0x0c,0x14,0x1e,0x1e,
  0x01,0x1e,0x1e,0x0c,0x13,0x0b,0x0b,0x2a,0x1e,0x1f,0x13,0x1f,0x0c,
  0x29,0x1e,0x38,0x34,0x01,0x1c,0x1e,0x06,0x0c,0x23,0x22,0x05,0x22,
  0x05,0x0b,0x12,0x1e,0x2c,0x01,0x1e,0x1e,0x13,0x1a,0x19,0x1f,0x1e,
  0x14,0x0c,0x0b,0x14,0x12,0x2d,0x1f,0x13,0x05,0x26,0x1e,0x0f,0x01,
  0x2e,0x1e,0x0a,0x1f,0x0b,0x13,0x28,0x1e,0x14,0x1f,0x02,0x05,0x1f,
  0x22,0x15,0x1e,0x1b,0x01,0x3a,0x1e,0x0a,0x02,0x0c,0x05,0x05,0x28,
  0x0b,0x13,0x1a,0x19,0x12,0x1e,0x1c,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x37,0x1e,0x28,0x13,0x05,0x22,
  0x02,0x18,0x1e,0x29,0x19,0x13,0x0b,0x23,0x1e,0x1d,0x01,0x01,0x34,
  0x1e,0x18,0x1f,0x13,0x1f,0x0c,0x23,0x13,0x13,0x13,0x05,0x14,0x1e,
  0x1e,0x35,0x01,0x01,0x04,0x1e,0x2a,0x13,0x0b,0x23,0x13,0x05,0x05,
  0x0c,0x1f,0x1f,0x1e,0x1e,0x35,0x01,0x37,0x1e,0x26,0x22,0x0b,0x22,
  0x1f,0x14,0x0c,0x14,0x27,0x0c,0x2d,0x1e,0x3b,0x01,0x3c,0x1e,0x16,
  0x13,0x0b,0x13,0x13,0x13,0x14,0x1f,0x13,0x0d,0x1e,0x04,0x01,0x36,
  0x1e,0x31,0x13,0x13,0x13,0x0c,0x31,0x1e,0x36,0x01,0x01,0x01,0x01,
  0x01,0x01,0x3a,0x1e,0x0a,0x13,0x1f,0x13,0x13,0x0c,0x13,0x1f,0x14,
  0x0b,0x1a,0x1e,0x1d,0x01,0x01,0x30,0x1e,0x12,0x23,0x0b,0x05,0x29,
  0x1e,0x25,0x0b,0x22,0x0b,0x22,0x1e,0x1e,0x01,0x35,0x1e,0x2d,0x22,
  0x22,0x14,0x14,0x0a,0x1e,0x2e,0x01,0x2b,0x1e,0x1e,0x1f,0x13,0x0c,
  0x13,0x0c,0x13,0x13,0x1f,0x0c,0x28,0x1e,0x38,0x01,0x1e,0x1e,0x1f,
  0x13,0x13,0x0c,0x25,0x1e,0x14,0x0b,0x23,0x13,0x05,0x1e,0x1e,0x04,
  0x01,0x1c,0x1e,0x06,0x05,0x29,0x02,0x1f,0x0b,0x22,0x0b,0x27,0x1e,
  0x38,0x01,0x1e,0x1e,0x13,0x05,0x22,0x22,0x1e,0x0d,0x1a,0x19,0x13,
  0x2a,0x2d,0x13,0x05,0x22,0x26,0x1e,0x0f,0x01,0x3b,0x1e,0x16,0x0c,
  0x05,0x05,0x26,0x1e,0x0d,0x0c,0x13,0x0c,0x23,0x0b,0x15,0x1e,0x1b,
  0x01,0x2b,0x1e,0x2d,0x0c,0x14,0x27,0x0c,0x19,0x12,0x13,0x05,0x22,
  0x18,0x1e,0x08,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x2c,0x1e,0x1a,0x1f,0x0b,0x1f,0x06,0x1e,0x1e,0x29,
  0x23,0x22,0x22,0x13,0x31,0x1e,0x36,0x01,0x35,0x1e,0x2d,0x13,0x13,
  0x13,0x05,0x14,0x13,0x23,0x13,0x14,0x09,0x1e,0x38,0x01,0x01,0x01,
  0x34,0x3e,0x1e,0x2a,0x13,0x1f,0x13,0x14,0x13,0x14,0x19,0x1e,0x1e,
  0x38,0x01,0x01,0x39,0x1e,0x29,0x14,0x0d,0x19,0x1f,0x0b,0x05,0x14,
  0x23,0x10,0x1e,0x38,0x01,0x01,0x0f,0x1e,0x15,0x22,0x22,0x13,0x13,
  0x13,0x13,0x22,0x13,0x22,0x1e,0x32,0x01,0x08,0x1e,0x16,0x13,0x13,
  0x1f,0x13,0x25,0x1e,0x08,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x33,
  0x1e,0x26,0x05,0x13,0x0c,0x0c,0x13,0x2d,0x1f,0x13,0x1f,0x1e,0x1e,
  0x00,0x01,0x32,0x1e,0x0b,0x05,0x05,0x0c,0x0c,0x1e,0x0a,0x23,0x14,
  0x0d,0x19,0x1e,0x1e,0x01,0x36,0x1e,0x21,0x0b,0x1f,0x0b,0x24,0x24,
  0x1e,0x37,0x01,0x01,0x11,0x1e,0x1e,0x0c,0x13,0x13,0x05,0x13,0x13,
  0x13,0x25,0x1e,0x1e,0x36,0x01,0x1e,0x1e,0x14,0x13,0x0c,0x0c,0x29,
  0x1e,0x06,0x13,0x1f,0x13,0x14,0x14,0x1e,0x07,0x01,0x38,0x1e,0x02,
  0x0c,0x0c,0x22,0x02,0x23,0x14,0x0d,0x19,0x1e,0x1e,0x01,0x1e,0x1e,
  0x1f,0x0b,0x1f,0x0b,0x1e,0x0d,0x05,0x19,0x22,0x03,0x31,0x13,0x05,
  0x0c,0x29,0x1e,0x2c,0x01,0x1c,0x1e,0x06,0x0c,0x27,0x13,0x0d,0x1e,
  0x24,0x05,0x05,0x0c,0x14,0x05,0x0d,0x1e,0x04,0x01,0x01,0x38,0x1e,
  0x10,0x14,0x22,0x1f,0x05,0x0c,0x1f,0x0b,0x2d,0x1e,0x38,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3a,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1d,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x2b,0x3e,0x1e,
  0x1e,0x10,0x0a,0x16,0x28,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x0f,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x17,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x30,0x01,0x20,0x07,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x40,
  0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x17,0x1e,0x1e,0x1e,0x2a,
  0x25,0x0a,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x30,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,
  0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x2f,0x01,0x01,
  0x0e,0x11,0x1e,0x1e,0x18,0x0a,0x16,0x0a,0x03,0x1e,0x1e,0x33,0x36,
  0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x04,0x01,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x3a,
  0x01,0x3c,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x2e,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x2d,0x25,
  0x16,0x25,0x2d,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3a,0x38,0x1e,0x1e,
  0x1e,0x38,0x3c,0x3b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x33,0x39,
  0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x33,0x39,0x35,0x01,0x01,0x01,0x01,0x0f,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x40,0x2c,0x20,0x01,0x01,0x01,0x01,0x17,
  0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x32,0x30,0x01,0x01,
  0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x17,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,
  0x33,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x30,0x32,0x1e,0x1e,0x1e,
  0x1e,0x38,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2f,0x01,0x01,0x01,0x01,0x0e,0x39,0x33,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x1e,0x1e,0x38,0x3b,0x38,0x1e,0x1e,0x1e,0x1e,0x07,0x04,
  0x34,0x01,0x01,0x1c,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x2b,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x38,0x1e,0x1e,
  0x1e,0x38,0x38,0x1e,0x1e,0x1e,0x38,0x3a,0x01,0x01,0x01,0x3c,0x38,
  0x1e,0x1e,0x1e,0x38,0x1b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2e,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x3b,0x3c,0x2e,0x0e,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x34,0x2f,0x08,0x3b,0x35,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x3b,0x3c,0x3b,
  0x17,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x35,0x08,0x08,0x08,0x35,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x00,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x20,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x2e,0x3a,0x3a,0x1b,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x35,0x35,0x35,0x35,
  0x35,0x35,0x35,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x34,0x08,0x30,0x1d,0x30,0x1b,0x36,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x00,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x35,0x0e,
  0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x0e,0x20,0x0e,0x34,0x01,0x20,
  0x35,0x35,0x35,0x20,0x01,0x01,0x01,0x01,0x01,0x34,0x35,0x35,0x35,
  0x0e,0x01,0x01,0x00,0x0e,0x35,0x35,0x35,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x35,0x35,0x35,0x34,0x01,0x34,0x35,0x35,0x35,0x35,0x35,
  0x01,0x01,0x0e,0x35,0x35,0x00,0x01,0x01,0x01,0x01,0x01,0x34,0x0e,
  0x20,0x35,0x35,0x01,0x01,0x34,0x35,0x35,0x20,0x36,0x01,0x01,0x01,
  0x01,0x01,0x01,0x35,0x0e,0x20,0x0e,0x34,0x01,0x01,0x20,0x35,0x35,
  0x35,0x20,0x01,0x01,0x01,0x01,0x01,0x20,0x35,0x35,0x35,0x35,0x35,
  0x35,0x35,0x0e,0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,
  0x2e,0x3d,0x3d,0x04,0x3d,0x2e,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x35,0x35,0x35,0x35,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x20,0x3b,0x3d,0x04,0x3d,0x04,0x3a,0x17,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3d,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1d,0x30,0x01,0x01,0x01,0x01,0x01,0x01,
  0x34,0x04,0x3e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1d,0x1c,0x01,0x01,
  0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x17,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x3d,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x01,0x3d,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x2b,0x01,0x01,0x3d,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x3d,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,
  0x1e,0x1e,0x39,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x38,0x1e,0x1e,
  0x1e,0x1e,0x3d,0x01,0x01,0x01,0x04,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,
  0x39,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x00,0x2c,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x2c,0x01,
  0x01,0x0e,0x11,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x11,0x0e,0x01,0x01,0x01,0x01,0x01,0x1c,0x33,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x33,0x0f,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x04,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x17,0x07,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x3b,0x1e,0x1e,0x2d,0x2d,0x09,0x09,0x09,0x09,0x2d,
  0x09,0x1e,0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x1e,0x2d,
  0x28,0x26,0x15,0x28,0x2d,0x1e,0x1e,0x04,0x34,0x01,0x01,0x38,0x1e,
  0x2d,0x2d,0x09,0x09,0x09,0x09,0x2d,0x2d,0x1e,0x1e,0x1e,0x17,0x01,
  0x01,0x01,0x01,0x2b,0x32,0x1e,0x1e,0x16,0x12,0x0b,0x28,0x15,0x1e,
  0x1e,0x1e,0x2b,0x01,0x01,0x01,0x3c,0x1e,0x1e,0x2d,0x2d,0x09,0x09,
  0x09,0x09,0x09,0x2d,0x10,0x1e,0x1e,0x2b,0x01,0x37,0x1e,0x18,0x10,
  0x09,0x10,0x18,0x1e,0x18,0x2d,0x09,0x2d,0x09,0x1e,0x38,0x01,0x17,
  0x1e,0x18,0x2d,0x2d,0x09,0x10,0x1e,0x1e,0x1e,0x10,0x09,0x2d,0x2d,
  0x1e,0x38,0x01,0x01,0x01,0x38,0x1e,0x2d,0x2d,0x2d,0x18,0x1e,0x18,
  0x2d,0x09,0x09,0x2d,0x2d,0x1e,0x1e,0x10,0x2d,0x2d,0x1e,0x1e,0x2f,
  0x01,0x2f,0x1e,0x18,0x10,0x09,0x2d,0x2d,0x1e,0x1e,0x1e,0x2d,0x09,
  0x09,0x31,0x1e,0x38,0x01,0x01,0x3d,0x1e,0x09,0x10,0x09,0x10,0x18,
  0x1e,0x1e,0x09,0x2d,0x2d,0x2d,0x09,0x1e,0x0f,0x01,0x11,0x1e,0x18,
  0x2d,0x09,0x09,0x09,0x09,0x09,0x09,0x10,0x09,0x1e,0x11,0x01,0x01,
  0x01,0x01,0x1c,0x1e,0x1e,0x28,0x0d,0x0d,0x1a,0x29,0x28,0x1e,0x1e,
  0x0f,0x01,0x01,0x01,0x38,0x1e,0x2d,0x2d,0x2d,0x2d,0x18,0x1e,0x1c,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x17,0x1e,0x1e,0x16,0x29,
  0x24,0x26,0x29,0x26,0x09,0x1e,0x38,0x35,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x30,
  0x1e,0x29,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x0b,0x09,0x1e,
  0x30,0x01,0x01,0x01,0x0f,0x1e,0x1e,0x14,0x0c,0x22,0x1f,0x1f,0x1f,
  0x13,0x23,0x1e,0x1e,0x04,0x01,0x01,0x1e,0x1e,0x0c,0x13,0x05,0x14,
  0x13,0x13,0x13,0x13,0x13,0x26,0x1e,0x07,0x20,0x01,0x01,0x01,0x38,
  0x1e,0x03,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x15,0x1e,0x32,0x00,
  0x01,0x01,0x1c,0x1e,0x12,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,
  0x13,0x28,0x1e,0x2e,0x01,0x38,0x1e,0x23,0x13,0x13,0x13,0x02,0x1e,
  0x13,0x13,0x13,0x0c,0x22,0x1e,0x1e,0x01,0x0f,0x1e,0x26,0x13,0x05,
  0x14,0x13,0x21,0x1e,0x19,0x0c,0x13,0x05,0x14,0x2d,0x1e,0x35,0x01,
  0x2b,0x1e,0x21,0x13,0x13,0x13,0x19,0x1e,0x0c,0x13,0x13,0x13,0x13,
  0x13,0x1e,0x06,0x13,0x13,0x13,0x06,0x1e,0x0f,0x01,0x0f,0x1e,0x06,
  0x13,0x13,0x0c,0x22,0x26,0x1e,0x14,0x13,0x0c,0x13,0x1f,0x1e,0x1e,
  0x01,0x01,0x3e,0x1e,0x19,0x05,0x1f,0x0b,0x14,0x1e,0x09,0x1f,0x1f,
  0x13,0x05,0x1a,0x1e,0x11,0x01,0x1e,0x1e,0x22,0x13,0x13,0x13,0x13,
  0x13,0x13,0x13,0x13,0x0c,0x09,0x1e,0x20,0x01,0x01,0x2f,0x1e,0x1e,
  0x0b,0x13,0x13,0x13,0x13,0x13,0x13,0x02,0x1e,0x33,0x0e,0x01,0x01,
  0x1e,0x1e,0x1f,0x1f,0x1f,0x13,0x23,0x1e,0x1d,0x2c,0x1b,0x0e,0x01,
  0x01,0x01,0x01,0x01,0x38,0x1e,0x26,0x14,0x14,0x0b,0x0c,0x1f,0x19,
  0x05,0x09,0x1e,0x39,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x0a,0x13,0x13,
  0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x0b,0x1e,0x1d,0x01,0x01,0x01,
  0x38,0x1e,0x02,0x0c,0x0c,0x13,0x13,0x14,0x0c,0x13,0x0b,0x0b,0x1e,
  0x3e,0x01,0x01,0x1e,0x1e,0x14,0x13,0x05,0x02,0x14,0x13,0x0c,0x23,
  0x14,0x13,0x16,0x1e,0x2e,0x01,0x01,0x0e,0x1e,0x10,0x13,0x13,0x13,
  0x13,0x0c,0x13,0x13,0x13,0x13,0x2a,0x1e,0x1b,0x01,0x01,0x1b,0x1e,
  0x15,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x16,0x1e,0x3c,
  0x01,0x1c,0x1e,0x12,0x13,0x13,0x13,0x29,0x1e,0x23,0x0c,0x0c,0x0c,
  0x23,0x1e,0x1d,0x01,0x17,0x1e,0x18,0x0c,0x0c,0x13,0x1f,0x31,0x1e,
  0x19,0x14,0x13,0x05,0x06,0x1e,0x38,0x01,0x01,0x01,0x1d,0x1e,0x0b,
  0x13,0x13,0x12,0x1e,0x23,0x13,0x13,0x13,0x13,0x13,0x1e,0x06,0x13,
  0x13,0x13,0x21,0x1e,0x2f,0x01,0x2f,0x1e,0x1e,0x27,0x0c,0x0c,0x13,
  0x0d,0x1e,0x0c,0x13,0x0b,0x1f,0x2d,0x1e,0x38,0x01,0x01,0x3d,0x1e,
  0x18,0x0b,0x22,0x14,0x02,0x1e,0x25,0x05,0x27,0x22,0x1f,0x1e,0x1e,
  0x0f,0x01,0x1d,0x1e,0x0d,0x14,0x13,0x0c,0x23,0x14,0x13,0x13,0x13,
  0x0b,0x1e,0x1e,0x00,0x01,0x01,0x37,0x1e,0x24,0x1f,0x1f,0x13,0x14,
  0x22,0x13,0x13,0x13,0x0a,0x1e,0x3b,0x01,0x01,0x33,0x1e,0x0c,0x14,
  0x0c,0x13,0x26,0x1e,0x1e,0x1e,0x1e,0x33,0x2b,0x01,0x01,0x01,0x17,
  0x1e,0x18,0x24,0x0d,0x0c,0x02,0x19,0x1f,0x05,0x22,0x19,0x1e,0x33,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x2e,0x1e,0x28,0x13,0x13,0x13,0x0c,0x0d,0x14,
  0x13,0x13,0x13,0x0c,0x2d,0x1e,0x35,0x01,0x35,0x1e,0x2d,0x19,0x13,
  0x0c,0x14,0x15,0x26,0x0c,0x13,0x0c,0x13,0x10,0x1e,0x0e,0x01,0x1e,
  0x1e,0x0c,0x1f,0x23,0x13,0x0d,0x31,0x23,0x19,0x12,0x14,0x0d,0x1e,
  0x04,0x01,0x01,0x3b,0x1e,0x0a,0x13,0x13,0x13,0x13,0x1e,0x05,0x13,
  0x13,0x13,0x1a,0x1e,0x04,0x01,0x01,0x08,0x1e,0x16,0x22,0x02,0x13,
  0x13,0x13,0x13,0x0c,0x19,0x0c,0x31,0x1e,0x36,0x01,0x30,0x1e,0x29,
  0x13,0x13,0x13,0x29,0x1e,0x19,0x19,0x13,0x0c,0x02,0x1e,0x1d,0x01,
  0x01,0x32,0x1e,0x22,0x02,0x14,0x0c,0x28,0x1e,0x1f,0x0c,0x1f,0x23,
  0x06,0x1e,0x0f,0x01,0x01,0x01,0x38,0x1e,0x23,0x0b,0x14,0x19,0x1e,
  0x14,0x13,0x13,0x13,0x13,0x13,0x1e,0x0d,0x13,0x13,0x1f,0x1e,0x40,
  0x01,0x01,0x01,0x3a,0x1e,0x09,0x13,0x0c,0x14,0x05,0x0d,0x0c,0x13,
  0x0c,0x0b,0x1e,0x32,0x2b,0x01,0x01,0x00,0x39,0x1e,0x27,0x05,0x23,
  0x0c,0x18,0x26,0x22,0x27,0x12,0x26,0x1e,0x38,0x01,0x01,0x1c,0x1e,
  0x12,0x19,0x0d,0x02,0x27,0x12,0x14,0x13,0x13,0x0d,0x1e,0x11,0x01,
  0x01,0x01,0x08,0x1e,0x16,0x27,0x0a,0x0a,0x1e,0x26,0x13,0x13,0x13,
  0x29,0x1e,0x30,0x01,0x01,0x33,0x1e,0x0c,0x0c,0x0c,0x13,0x06,0x1e,
  0x27,0x1a,0x28,0x1e,0x1e,0x2b,0x01,0x01,0x3a,0x1e,0x15,0x14,0x23,
  0x22,0x1a,0x21,0x0d,0x1a,0x0c,0x27,0x1e,0x1e,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x2e,0x1e,0x28,0x13,0x13,0x13,0x1f,0x1e,0x09,0x13,0x13,0x13,0x13,
  0x2a,0x1e,0x2f,0x01,0x36,0x1e,0x31,0x0c,0x13,0x0c,0x13,0x1e,0x1e,
  0x0c,0x0c,0x22,0x13,0x03,0x1e,0x2f,0x01,0x1e,0x1e,0x0b,0x0c,0x19,
  0x05,0x2a,0x1e,0x25,0x0b,0x0c,0x1f,0x12,0x1e,0x39,0x01,0x01,0x3a,
  0x1e,0x26,0x13,0x13,0x13,0x13,0x1e,0x2a,0x31,0x31,0x15,0x03,0x1e,
  0x1b,0x01,0x01,0x20,0x07,0x1e,0x1e,0x1e,0x13,0x13,0x13,0x13,0x02,
  0x1e,0x1e,0x1e,0x1d,0x01,0x01,0x30,0x1e,0x29,0x13,0x13,0x13,0x29,
  0x1e,0x23,0x0c,0x13,0x0c,0x0b,0x1e,0x1d,0x01,0x01,0x3d,0x1e,0x12,
  0x1f,0x13,0x0c,0x12,0x1e,0x27,0x0b,0x0c,0x19,0x16,0x1e,0x3c,0x01,
  0x01,0x01,0x37,0x1e,0x06,0x02,0x24,0x1a,0x1e,0x05,0x05,0x05,0x13,
  0x13,0x13,0x1e,0x14,0x13,0x13,0x22,0x1e,0x32,0x01,0x01,0x01,0x01,
  0x38,0x1e,0x1a,0x0c,0x13,0x1f,0x0b,0x0c,0x0c,0x22,0x18,0x1e,0x30,
  0x01,0x01,0x01,0x01,0x3a,0x1e,0x2a,0x23,0x02,0x14,0x06,0x27,0x22,
  0x19,0x05,0x1e,0x1e,0x08,0x01,0x01,0x08,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x27,0x0c,0x1f,0x19,0x05,0x10,0x1e,0x0f,0x01,0x01,0x01,0x2b,0x1e,
  0x1e,0x18,0x28,0x06,0x06,0x0b,0x0c,0x13,0x13,0x0d,0x1e,0x3d,0x01,
  0x01,0x33,0x1e,0x05,0x0b,0x0c,0x0c,0x02,0x13,0x0c,0x0c,0x0c,0x28,
  0x1e,0x04,0x01,0x01,0x3d,0x1e,0x12,0x06,0x27,0x14,0x24,0x1e,0x2a,
  0x28,0x0a,0x25,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x28,0x13,
  0x13,0x13,0x1f,0x1e,0x18,0x13,0x13,0x13,0x13,0x16,0x1e,0x3c,0x01,
  0x36,0x1e,0x31,0x0c,0x13,0x1f,0x0c,0x1e,0x21,0x0c,0x0c,0x13,0x05,
  0x25,0x1e,0x3b,0x01,0x1e,0x1e,0x0c,0x05,0x22,0x0c,0x06,0x25,0x05,
  0x02,0x19,0x1f,0x2a,0x1e,0x3a,0x01,0x01,0x3b,0x1e,0x0a,0x13,0x13,
  0x1f,0x1f,0x13,0x02,0x28,0x09,0x1e,0x1e,0x32,0x00,0x01,0x01,0x01,
  0x20,0x3d,0x1e,0x1e,0x13,0x13,0x13,0x13,0x19,0x1e,0x1e,0x11,0x0e,
  0x01,0x01,0x30,0x1e,0x29,0x13,0x13,0x13,0x29,0x1e,0x19,0x0c,0x13,
  0x1f,0x23,0x1e,0x1d,0x01,0x01,0x3b,0x1e,0x16,0x02,0x0c,0x13,0x02,
  0x1e,0x1f,0x0c,0x05,0x0b,0x09,0x1e,0x20,0x01,0x01,0x01,0x08,0x1e,
  0x16,0x0b,0x0c,0x23,0x03,0x14,0x0b,0x26,0x0b,0x13,0x13,0x1e,0x22,
  0x13,0x13,0x0b,0x1e,0x1d,0x01,0x01,0x01,0x01,0x1b,0x1e,0x1e,0x1f,
  0x0c,0x13,0x1f,0x0c,0x0c,0x1a,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,
  0x34,0x3e,0x1e,0x27,0x0b,0x05,0x24,0x12,0x19,0x1a,0x0d,0x1e,0x32,
  0x01,0x01,0x01,0x01,0x08,0x2c,0x07,0x1e,0x24,0x1a,0x27,0x1f,0x05,
  0x25,0x1e,0x38,0x01,0x01,0x01,0x01,0x2c,0x1e,0x0a,0x23,0x27,0x13,
  0x14,0x22,0x0c,0x13,0x13,0x29,0x1e,0x30,0x01,0x01,0x33,0x1e,0x0c,
  0x1f,0x0c,0x0c,0x13,0x05,0x23,0x0c,0x05,0x1a,0x18,0x1e,0x34,0x01,
  0x1c,0x1e,0x24,0x0c,0x14,0x29,0x0d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x04,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x28,0x13,0x13,0x13,0x13,0x29,
  0x0b,0x13,0x13,0x13,0x0c,0x09,0x1e,0x35,0x01,0x36,0x1e,0x31,0x0c,
  0x05,0x05,0x13,0x1e,0x21,0x13,0x05,0x0c,0x13,0x16,0x1e,0x08,0x01,
  0x1e,0x1e,0x0c,0x0c,0x22,0x0b,0x14,0x23,0x22,0x14,0x1f,0x25,0x1e,
  0x3e,0x00,0x01,0x01,0x0e,0x1e,0x18,0x19,0x0b,0x14,0x06,0x1f,0x13,
  0x13,0x13,0x1a,0x1e,0x1e,0x3c,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,
  0x13,0x13,0x13,0x13,0x19,0x1e,0x11,0x01,0x01,0x01,0x01,0x30,0x1e,
  0x29,0x13,0x13,0x13,0x12,0x1e,0x19,0x0c,0x05,0x05,0x0b,0x1e,0x1d,
  0x01,0x01,0x2b,0x1e,0x21,0x13,0x05,0x23,0x23,0x21,0x02,0x0c,0x0c,
  0x1a,0x1e,0x38,0x01,0x01,0x01,0x01,0x3c,0x1e,0x16,0x27,0x0b,0x22,
  0x03,0x0b,0x05,0x12,0x19,0x19,0x0b,0x1e,0x06,0x1f,0x13,0x27,0x1e,
  0x39,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x26,0x13,0x05,0x13,0x13,
  0x05,0x18,0x1e,0x2e,0x01,0x01,0x01,0x01,0x01,0x01,0x1c,0x1e,0x16,
  0x22,0x19,0x0b,0x19,0x23,0x0b,0x1e,0x1e,0x30,0x01,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x21,0x27,0x19,0x26,0x12,0x24,0x1e,0x33,0x2b,0x01,
  0x01,0x01,0x34,0x1e,0x18,0x23,0x1f,0x13,0x19,0x1e,0x03,0x22,0x13,
  0x13,0x29,0x1e,0x30,0x01,0x01,0x33,0x1e,0x14,0x13,0x13,0x05,0x22,
  0x06,0x05,0x0c,0x13,0x1f,0x09,0x1e,0x20,0x01,0x2c,0x1e,0x12,0x02,
  0x05,0x02,0x15,0x1e,0x02,0x14,0x27,0x0b,0x1e,0x1e,0x00,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x2e,0x1e,0x28,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,
  0x23,0x1e,0x38,0x01,0x01,0x36,0x1e,0x21,0x0c,0x1f,0x22,0x13,0x1e,
  0x21,0x05,0x05,0x13,0x0c,0x16,0x1e,0x08,0x01,0x1e,0x1e,0x05,0x27,
  0x05,0x0b,0x06,0x27,0x14,0x22,0x05,0x29,0x1e,0x33,0x0e,0x01,0x01,
  0x01,0x38,0x1e,0x09,0x0d,0x14,0x0c,0x0c,0x02,0x22,0x0c,0x13,0x12,
  0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,0x13,
  0x19,0x1e,0x11,0x01,0x01,0x01,0x01,0x30,0x1e,0x29,0x13,0x13,0x13,
  0x29,0x1e,0x0d,0x0c,0x1f,0x22,0x0b,0x1e,0x1d,0x01,0x01,0x34,0x1e,
  0x18,0x05,0x13,0x05,0x1f,0x28,0x13,0x05,0x27,0x12,0x1e,0x3d,0x01,
  0x01,0x01,0x01,0x2b,0x1e,0x21,0x14,0x0c,0x27,0x26,0x22,0x0b,0x26,
  0x19,0x05,0x0b,0x21,0x0c,0x0c,0x02,0x24,0x1e,0x30,0x01,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x29,0x13,0x1f,0x0c,0x05,0x05,0x2d,0x1e,0x11,
  0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x40,0x1e,0x23,0x02,0x06,0x1a,
  0x0b,0x23,0x1e,0x40,0x01,0x01,0x01,0x01,0x01,0x37,0x1e,0x1e,0x0d,
  0x27,0x19,0x22,0x05,0x09,0x1e,0x3a,0x01,0x01,0x01,0x01,0x20,0x1e,
  0x09,0x0b,0x0b,0x05,0x14,0x2d,0x12,0x1f,0x22,0x13,0x29,0x1e,0x30,
  0x01,0x01,0x33,0x1e,0x05,0x0c,0x05,0x05,0x06,0x1e,0x0c,0x13,0x1f,
  0x05,0x21,0x1e,0x36,0x01,0x1b,0x1e,0x03,0x22,0x05,0x14,0x0b,0x06,
  0x02,0x1f,0x27,0x02,0x1e,0x1e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x28,
  0x22,0x13,0x13,0x22,0x13,0x13,0x13,0x13,0x19,0x18,0x1e,0x37,0x01,
  0x01,0x36,0x1e,0x31,0x0c,0x0c,0x13,0x0c,0x1e,0x31,0x1f,0x23,0x13,
  0x0c,0x0a,0x1e,0x3b,0x01,0x1e,0x1e,0x13,0x23,0x22,0x1f,0x24,0x18,
  0x29,0x14,0x22,0x29,0x0a,0x1e,0x3b,0x01,0x01,0x2f,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x2d,0x0b,0x14,0x22,0x0c,0x13,0x1f,0x1e,0x40,0x01,0x01,
  0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,0x13,0x19,0x1e,0x11,0x01,
  0x01,0x01,0x01,0x30,0x1e,0x12,0x13,0x13,0x1f,0x12,0x1e,0x23,0x0c,
  0x0c,0x13,0x23,0x1e,0x1d,0x01,0x01,0x01,0x39,0x1e,0x02,0x1f,0x1f,
  0x13,0x24,0x14,0x13,0x23,0x28,0x1e,0x1b,0x01,0x01,0x01,0x01,0x0e,
  0x1e,0x10,0x22,0x1f,0x1f,0x02,0x22,0x05,0x21,0x24,0x12,0x14,0x06,
  0x27,0x13,0x14,0x03,0x1e,0x08,0x01,0x01,0x01,0x01,0x30,0x1e,0x31,
  0x13,0x0c,0x19,0x0c,0x1f,0x23,0x1f,0x1e,0x33,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x2c,0x1e,0x28,0x02,0x1f,0x0b,0x12,0x09,0x1e,0x2c,
  0x01,0x01,0x01,0x01,0x2b,0x07,0x1e,0x29,0x06,0x24,0x1a,0x29,0x03,
  0x1e,0x1e,0x38,0x0f,0x01,0x01,0x01,0x34,0x1e,0x18,0x0b,0x14,0x13,
  0x0d,0x02,0x23,0x0b,0x13,0x13,0x06,0x1e,0x38,0x01,0x01,0x33,0x1e,
  0x19,0x0c,0x1f,0x23,0x28,0x1e,0x1f,0x13,0x05,0x13,0x03,0x1e,0x17,
  0x01,0x00,0x1e,0x1e,0x02,0x1f,0x0b,0x22,0x0b,0x22,0x1f,0x14,0x29,
  0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x28,0x22,0x0c,0x13,0x1f,
  0x25,0x21,0x2a,0x21,0x1e,0x1e,0x04,0x01,0x01,0x01,0x2b,0x1e,0x21,
  0x0c,0x1f,0x1f,0x13,0x1e,0x1e,0x13,0x13,0x0c,0x23,0x31,0x1e,0x36,
  0x01,0x1e,0x1e,0x0c,0x05,0x13,0x19,0x25,0x1e,0x27,0x02,0x1f,0x1f,
  0x15,0x1e,0x3a,0x01,0x01,0x37,0x1e,0x06,0x02,0x1a,0x19,0x16,0x1e,
  0x24,0x1f,0x22,0x13,0x1f,0x09,0x1e,0x20,0x01,0x01,0x01,0x01,0x1e,
  0x1e,0x13,0x13,0x13,0x13,0x19,0x1e,0x11,0x01,0x01,0x01,0x01,0x30,
  0x1e,0x29,0x13,0x0c,0x1f,0x29,0x1e,0x29,0x0c,0x1f,0x1f,0x14,0x1e,
  0x3e,0x01,0x01,0x01,0x0f,0x1e,0x06,0x0c,0x1f,0x0c,0x0b,0x0c,0x0c,
  0x05,0x31,0x1e,0x36,0x01,0x01,0x01,0x01,0x01,0x1d,0x1e,0x19,0x05,
  0x29,0x19,0x1f,0x0b,0x10,0x15,0x0b,0x22,0x0b,0x13,0x23,0x0d,0x10,
  0x1e,0x0e,0x01,0x01,0x01,0x0e,0x40,0x1e,0x0b,0x1f,0x13,0x1f,0x13,
  0x13,0x13,0x0c,0x03,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x17,
  0x1e,0x03,0x19,0x27,0x02,0x0d,0x1e,0x1e,0x20,0x01,0x01,0x01,0x01,
  0x38,0x1e,0x2a,0x27,0x02,0x29,0x1a,0x24,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x3d,0x01,0x01,0x01,0x39,0x1e,0x16,0x24,0x13,0x05,0x02,0x16,0x22,
  0x23,0x13,0x1f,0x09,0x1e,0x20,0x01,0x33,0x1e,0x05,0x13,0x13,0x13,
  0x1a,0x1e,0x05,0x13,0x23,0x14,0x2d,0x1e,0x35,0x01,0x01,0x38,0x1e,
  0x2d,0x02,0x23,0x14,0x0d,0x19,0x1f,0x12,0x1e,0x1e,0x3b,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2e,0x1e,0x28,0x1f,0x14,0x13,0x05,0x1e,0x1e,0x1e,0x1e,
  0x38,0x37,0x01,0x01,0x01,0x01,0x35,0x1e,0x2d,0x0c,0x0c,0x13,0x0b,
  0x0d,0x02,0x1f,0x1f,0x13,0x1f,0x09,0x1e,0x20,0x01,0x1e,0x1e,0x1f,
  0x22,0x0c,0x23,0x15,0x1e,0x22,0x0c,0x0b,0x02,0x28,0x1e,0x2e,0x01,
  0x01,0x38,0x1e,0x12,0x19,0x13,0x0b,0x05,0x1e,0x19,0x05,0x1f,0x13,
  0x22,0x1e,0x32,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x0c,0x14,0x13,
  0x22,0x19,0x1e,0x11,0x01,0x01,0x01,0x01,0x3d,0x1e,0x0d,0x13,0x0c,
  0x1f,0x1f,0x0d,0x05,0x0c,0x0c,0x13,0x0d,0x1e,0x11,0x01,0x01,0x01,
  0x2f,0x1e,0x2a,0x0b,0x05,0x1f,0x02,0x05,0x1f,0x23,0x1e,0x07,0x01,
  0x01,0x01,0x01,0x01,0x01,0x11,0x1e,0x29,0x0c,0x05,0x1f,0x19,0x22,
  0x03,0x03,0x19,0x13,0x0b,0x05,0x0c,0x23,0x1e,0x1e,0x01,0x01,0x01,
  0x01,0x11,0x1e,0x25,0x0b,0x13,0x23,0x0d,0x16,0x0b,0x1f,0x22,0x1f,
  0x1e,0x1e,0x3c,0x01,0x01,0x01,0x01,0x01,0x2f,0x1e,0x03,0x02,0x0b,
  0x22,0x0b,0x18,0x1e,0x34,0x01,0x01,0x01,0x01,0x1e,0x1e,0x23,0x24,
  0x27,0x22,0x23,0x02,0x0d,0x02,0x29,0x12,0x1e,0x1e,0x34,0x01,0x01,
  0x35,0x40,0x1e,0x18,0x25,0x2a,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x01,0x01,0x33,0x1e,0x02,0x1f,0x0b,0x1f,0x22,0x14,0x0c,0x0c,
  0x13,0x05,0x09,0x1e,0x20,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x2d,0x28,
  0x25,0x21,0x1e,0x1e,0x1e,0x1b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,
  0x28,0x14,0x1f,0x13,0x1f,0x09,0x1e,0x2f,0x2b,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x38,0x1e,0x1a,0x1f,0x13,0x13,0x0c,0x13,0x1f,0x13,
  0x0b,0x0d,0x1e,0x38,0x01,0x01,0x1e,0x1e,0x23,0x0b,0x05,0x29,0x0a,
  0x1e,0x0b,0x22,0x0b,0x22,0x25,0x1e,0x38,0x01,0x01,0x37,0x1e,0x16,
  0x22,0x14,0x14,0x13,0x1a,0x19,0x13,0x0b,0x13,0x16,0x1e,0x30,0x01,
  0x01,0x01,0x01,0x01,0x1e,0x1e,0x0c,0x23,0x13,0x13,0x19,0x1e,0x11,
  0x01,0x01,0x01,0x01,0x3b,0x1e,0x18,0x13,0x05,0x05,0x13,0x05,0x0c,
  0x13,0x1f,0x13,0x1e,0x1e,0x37,0x01,0x01,0x01,0x20,0x1e,0x09,0x05,
  0x1f,0x0c,0x13,0x0c,0x23,0x02,0x1e,0x32,0x01,0x01,0x01,0x01,0x01,
  0x01,0x1c,0x1e,0x12,0x14,0x27,0x0c,0x19,0x12,0x2d,0x25,0x22,0x14,
  0x14,0x13,0x1a,0x02,0x1e,0x33,0x01,0x01,0x01,0x1c,0x1e,0x1e,0x0c,
  0x14,0x13,0x13,0x24,0x1e,0x27,0x13,0x0b,0x19,0x06,0x1e,0x38,0x01,
  0x01,0x01,0x01,0x01,0x2f,0x1e,0x03,0x29,0x29,0x02,0x23,0x1e,0x1e,
  0x00,0x01,0x01,0x01,0x01,0x1e,0x1e,0x29,0x1a,0x23,0x27,0x19,0x0b,
  0x12,0x27,0x14,0x1a,0x1e,0x1e,0x00,0x01,0x01,0x01,0x2b,0x38,0x1e,
  0x1e,0x1e,0x1e,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x33,
  0x1e,0x14,0x0c,0x02,0x13,0x0b,0x23,0x13,0x14,0x05,0x0b,0x1e,0x1e,
  0x00,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,
  0x1b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x30,0x1e,0x29,0x13,0x22,0x13,
  0x13,0x10,0x1e,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2e,
  0x1e,0x1e,0x12,0x13,0x0c,0x0c,0x13,0x05,0x13,0x06,0x1e,0x1e,0x37,
  0x01,0x01,0x1e,0x1e,0x05,0x05,0x0c,0x0c,0x0d,0x1e,0x19,0x14,0x0d,
  0x19,0x1f,0x1e,0x1e,0x00,0x01,0x20,0x07,0x1e,0x2a,0x0b,0x24,0x13,
  0x05,0x23,0x22,0x22,0x10,0x1e,0x3e,0x34,0x01,0x01,0x01,0x01,0x01,
  0x1e,0x1e,0x05,0x14,0x13,0x0b,0x1f,0x1e,0x40,0x01,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x10,0x1f,0x14,0x13,0x05,0x0c,0x1f,0x05,0x2a,0x1e,
  0x38,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x23,0x13,0x05,0x05,0x0c,
  0x05,0x1a,0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x01,0x3d,0x1e,0x12,
  0x14,0x22,0x1f,0x05,0x0c,0x16,0x09,0x1f,0x0b,0x24,0x13,0x05,0x23,
  0x1e,0x1e,0x01,0x01,0x01,0x1d,0x1e,0x23,0x1f,0x0b,0x13,0x0c,0x21,
  0x1e,0x0d,0x13,0x19,0x1f,0x05,0x09,0x1e,0x20,0x01,0x01,0x01,0x01,
  0x1b,0x1e,0x28,0x0c,0x22,0x27,0x19,0x2d,0x1e,0x35,0x01,0x01,0x01,
  0x01,0x1e,0x1e,0x05,0x27,0x1a,0x0d,0x26,0x0d,0x27,0x19,0x1a,0x02,
  0x18,0x1e,0x20,0x01,0x01,0x01,0x01,0x01,0x35,0x3b,0x3c,0x34,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x1f,0x14,0x23,
  0x13,0x0a,0x1f,0x13,0x0b,0x13,0x2a,0x1e,0x3d,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x0e,0x2e,0x08,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x08,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x1e,0x1e,
  0x22,0x0c,0x1f,0x29,0x1e,0x1e,0x1e,0x37,0x01,0x01,0x01,0x38,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,
  0x01,0x01,0x01,0x2b,0x3e,0x1e,0x1e,0x31,0x25,0x2a,0x25,0x09,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,
  0x1e,0x2d,0x25,0x16,0x28,0x10,0x1e,0x1e,0x32,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x3a,
  0x01,0x01,0x01,0x01,0x01,0x01,0x3b,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,
  0x01,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x2b,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x11,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x16,
  0x16,0x1e,0x1e,0x3e,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x08,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x3b,0x1e,0x1e,0x02,0x0c,0x1f,0x22,
  0x1f,0x1e,0x33,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,
  0x38,0x39,0x33,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x40,0x2c,
  0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x3a,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x3b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x38,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x1c,0x1d,
  0x1e,0x1e,0x1e,0x1e,0x38,0x0f,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,
  0x2b,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x11,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x40,0x1e,0x05,0x13,0x13,0x13,0x1f,0x1e,0x1e,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x36,
  0x3b,0x3c,0x08,0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x08,0x3c,0x2e,0x0e,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x20,0x08,0x08,0x20,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x39,
  0x1e,0x2d,0x29,0x12,0x0d,0x12,0x1e,0x1e,0x00,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x39,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x3b,0x30,0x1c,0x3d,0x1c,0x3c,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x35,
  0x35,0x35,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3c,0x37,0x04,
  0x3d,0x2c,0x37,0x3c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x35,0x35,0x35,0x0e,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x2f,
  0x37,0x2c,0x3d,0x3b,0x2b,0x2e,0x3b,0x3b,0x0e,0x01,0x01,0x01,0x01,
  0x01,0x35,0x35,0x35,0x35,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x00,0x35,0x35,0x35,0x0e,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x20,0x35,0x20,0x0e,0x00,0x01,0x01,0x01,0x01,0x01,0x34,
  0x35,0x35,0x0e,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x34,0x0e,0x35,0x35,0x34,0x01,0x01,0x01,0x01,0x01,
  0x2b,0x1b,0x3b,0x1b,0x36,0x2e,0x04,0x2c,0x3a,0x17,0x3b,0x3d,0x2c,
  0x37,0x2f,0x01,0x01,0x01,0x01,0x01,0x36,0x3a,0x08,0x2e,0x17,0x2f,
  0x37,0x39,0x2c,0x1b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,
  0x2e,0x3d,0x3d,0x3d,0x04,0x2e,0x35,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x17,0x2e,0x3b,0x3b,0x35,0x1b,0x04,0x2c,0x30,0x08,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2f,0x37,0x39,0x04,0x1b,0x17,0x3b,0x3b,
  0x2e,0x17,0x01,0x01,0x01,0x01,0x01,0x17,0x2e,0x3b,0x2e,0x0e,0x08,
  0x30,0x3d,0x3b,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x3c,0x37,0x2c,
  0x30,0x04,0x30,0x3b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,
  0x3b,0x30,0x0f,0x1c,0x08,0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x3d,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x35,0x39,0x40,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x38,0x2b,0x01,0x01,0x2b,0x11,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x11,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x30,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x3d,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x04,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3d,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x2c,0x20,0x01,0x01,0x11,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x01,
  0x01,0x01,0x36,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1d,0x2b,0x01,0x01,0x01,0x01,0x01,0x30,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x40,0x30,0x01,0x01,0x01,0x01,0x35,0x32,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1b,0x01,0x01,0x01,0x01,0x01,
  0x3d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1d,0x35,
  0x01,0x2b,0x32,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x3d,
  0x01,0x01,0x01,0x01,0x2b,0x32,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1d,0x2b,0x01,0x01,0x01,0x01,0x01,0x0e,0x33,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x40,0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x21,0x09,0x2d,0x09,0x1e,0x1e,0x2b,0x01,0x01,0x2b,
  0x3e,0x1e,0x21,0x24,0x1a,0x0d,0x27,0x24,0x21,0x1e,0x1d,0x2b,0x01,
  0x01,0x01,0x01,0x39,0x1e,0x1e,0x2d,0x2d,0x09,0x2d,0x18,0x1e,0x38,
  0x01,0x01,0x11,0x1e,0x2d,0x24,0x24,0x12,0x10,0x21,0x28,0x0a,0x0a,
  0x10,0x1e,0x2e,0x01,0x38,0x1e,0x2d,0x2d,0x2d,0x2d,0x1e,0x1e,0x3c,
  0x01,0x01,0x01,0x01,0x01,0x01,0x2f,0x1e,0x1e,0x2d,0x2d,0x2d,0x2d,
  0x1e,0x38,0x01,0x01,0x01,0x38,0x1e,0x09,0x2d,0x09,0x10,0x1e,0x1e,
  0x3c,0x01,0x38,0x1e,0x18,0x2d,0x09,0x10,0x18,0x1e,0x30,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x18,0x10,0x09,0x2d,0x18,
  0x1e,0x2c,0x01,0x2e,0x1e,0x10,0x15,0x0a,0x15,0x09,0x10,0x1a,0x27,
  0x26,0x1e,0x31,0x29,0x27,0x06,0x1e,0x1e,0x1c,0x01,0x01,0x38,0x1e,
  0x15,0x25,0x0a,0x03,0x1e,0x06,0x0d,0x1a,0x31,0x1e,0x38,0x01,0x01,
  0x01,0x01,0x30,0x1e,0x1e,0x28,0x12,0x12,0x0d,0x1a,0x0a,0x1e,0x1e,
  0x30,0x01,0x01,0x01,0x39,0x1e,0x03,0x28,0x0a,0x0a,0x1e,0x21,0x1a,
  0x27,0x29,0x1e,0x1e,0x1b,0x01,0x01,0x01,0x0f,0x1e,0x1e,0x24,0x02,
  0x0d,0x1e,0x03,0x0a,0x25,0x28,0x03,0x1e,0x39,0x01,0x38,0x1e,0x03,
  0x0a,0x25,0x0a,0x09,0x2d,0x12,0x24,0x1e,0x1e,0x00,0x01,0x01,0x2b,
  0x3e,0x1e,0x03,0x06,0x27,0x12,0x24,0x29,0x31,0x1e,0x3e,0x2b,0x01,
  0x01,0x01,0x01,0x1c,0x1e,0x0a,0x12,0x06,0x24,0x25,0x1e,0x30,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3b,0x30,0x39,0x1e,0x1e,0x13,
  0x13,0x13,0x13,0x28,0x1e,0x1b,0x01,0x01,0x38,0x1e,0x16,0x13,0x13,
  0x13,0x13,0x13,0x13,0x13,0x31,0x1e,0x38,0x01,0x01,0x01,0x00,0x1e,
  0x1e,0x1f,0x13,0x13,0x13,0x13,0x0c,0x1e,0x1e,0x01,0x01,0x1e,0x1e,
  0x1f,0x0c,0x1f,0x13,0x0c,0x1f,0x13,0x27,0x0b,0x0d,0x1e,0x04,0x01,
  0x1e,0x1e,0x13,0x13,0x13,0x13,0x12,0x1e,0x39,0x2c,0x1b,0x35,0x01,
  0x01,0x01,0x37,0x1e,0x24,0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,
  0x36,0x1e,0x21,0x13,0x13,0x13,0x13,0x12,0x1e,0x1c,0x01,0x1e,0x1e,
  0x1f,0x1f,0x13,0x0c,0x0b,0x1e,0x1d,0x3a,0x08,0x2e,0x2e,0x0e,0x01,
  0x01,0x01,0x04,0x1e,0x1a,0x13,0x13,0x13,0x05,0x1e,0x07,0x01,0x04,
  0x1e,0x29,0x13,0x13,0x13,0x05,0x0c,0x13,0x13,0x13,0x15,0x13,0x13,
  0x13,0x13,0x19,0x1e,0x38,0x01,0x01,0x1e,0x1e,0x0c,0x0c,0x0c,0x1f,
  0x1f,0x0c,0x1f,0x13,0x27,0x21,0x1e,0x3b,0x01,0x01,0x08,0x1e,0x1e,
  0x1a,0x05,0x02,0x24,0x14,0x13,0x13,0x19,0x1e,0x1e,0x1b,0x01,0x01,
  0x33,0x1e,0x0c,0x13,0x13,0x13,0x22,0x13,0x13,0x13,0x13,0x1a,0x1e,
  0x38,0x01,0x01,0x01,0x38,0x1e,0x19,0x13,0x13,0x13,0x02,0x13,0x0c,
  0x13,0x1f,0x23,0x1e,0x33,0x01,0x1e,0x1e,0x13,0x0c,0x1f,0x13,0x27,
  0x0b,0x0c,0x19,0x2d,0x1e,0x35,0x01,0x01,0x38,0x1e,0x2a,0x02,0x24,
  0x22,0x13,0x0c,0x14,0x14,0x2a,0x1e,0x38,0x01,0x01,0x01,0x2b,0x3e,
  0x1e,0x19,0x13,0x1f,0x14,0x0b,0x1e,0x1e,0x04,0x01,0x01,0x01,0x01,
  0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x13,0x13,0x13,0x13,0x2a,
  0x1e,0x2f,0x01,0x36,0x1e,0x1e,0x13,0x13,0x13,0x0c,0x02,0x13,0x13,
  0x13,0x13,0x1e,0x1e,0x01,0x01,0x01,0x04,0x1e,0x03,0x13,0x13,0x13,
  0x0c,0x22,0x15,0x1e,0x38,0x01,0x17,0x1e,0x31,0x0c,0x05,0x02,0x0c,
  0x13,0x1f,0x0c,0x1f,0x0c,0x15,0x1e,0x3a,0x01,0x1e,0x1e,0x13,0x13,
  0x13,0x13,0x16,0x1e,0x1e,0x1e,0x1e,0x33,0x2b,0x01,0x01,0x2f,0x1e,
  0x03,0x14,0x22,0x22,0x1f,0x1e,0x40,0x01,0x01,0x20,0x1e,0x18,0x02,
  0x14,0x0b,0x13,0x2a,0x1e,0x3c,0x01,0x38,0x1e,0x0d,0x0c,0x13,0x0b,
  0x12,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x39,0x01,0x01,0x3a,0x1e,
  0x26,0x0c,0x23,0x14,0x0d,0x1e,0x2c,0x01,0x3a,0x1e,0x25,0x22,0x13,
  0x13,0x1f,0x1f,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x0c,0x0c,0x21,
  0x1e,0x2b,0x01,0x1e,0x1e,0x23,0x0c,0x05,0x02,0x0c,0x13,0x1f,0x0c,
  0x1f,0x29,0x1e,0x3d,0x01,0x01,0x1c,0x1e,0x24,0x05,0x22,0x0b,0x05,
  0x14,0x27,0x0c,0x0b,0x15,0x1e,0x2c,0x01,0x01,0x39,0x1e,0x27,0x13,
  0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x2d,0x1e,0x0e,0x01,0x0e,
  0x1e,0x10,0x13,0x13,0x13,0x0c,0x0c,0x13,0x1f,0x0c,0x13,0x1a,0x1e,
  0x39,0x01,0x38,0x1e,0x02,0x13,0x1f,0x0c,0x1f,0x0c,0x05,0x0b,0x18,
  0x1e,0x34,0x01,0x01,0x1e,0x1e,0x22,0x0b,0x1f,0x22,0x26,0x14,0x0b,
  0x15,0x0b,0x1e,0x1e,0x34,0x01,0x01,0x38,0x1e,0x2a,0x1a,0x14,0x0c,
  0x14,0x0b,0x2a,0x18,0x1e,0x20,0x01,0x01,0x01,0x01,0x38,0x1e,0x10,
  0x29,0x02,0x06,0x1e,0x13,0x13,0x13,0x13,0x2a,0x1e,0x2f,0x01,0x1b,
  0x1e,0x15,0x13,0x13,0x13,0x02,0x1e,0x24,0x13,0x13,0x13,0x10,0x1e,
  0x08,0x01,0x1b,0x1e,0x18,0x0d,0x13,0x13,0x1f,0x19,0x26,0x10,0x1e,
  0x11,0x01,0x2e,0x1e,0x28,0x0c,0x13,0x13,0x05,0x1e,0x1e,0x14,0x02,
  0x0c,0x26,0x1e,0x3a,0x01,0x1e,0x1e,0x13,0x13,0x0b,0x14,0x25,0x10,
  0x02,0x27,0x0a,0x1e,0x07,0x34,0x01,0x0e,0x1e,0x0a,0x0a,0x0a,0x0a,
  0x0a,0x1e,0x38,0x01,0x01,0x00,0x1e,0x1e,0x0a,0x0a,0x0a,0x0a,0x0a,
  0x1e,0x2b,0x01,0x39,0x1e,0x02,0x0c,0x13,0x0c,0x29,0x1e,0x10,0x26,
  0x16,0x0a,0x0a,0x2d,0x1e,0x35,0x01,0x0f,0x1e,0x28,0x1f,0x19,0x12,
  0x29,0x1e,0x04,0x01,0x0f,0x1e,0x26,0x0c,0x19,0x0b,0x23,0x26,0x1f,
  0x13,0x13,0x13,0x02,0x14,0x13,0x1f,0x0c,0x16,0x1e,0x3c,0x01,0x1e,
  0x1e,0x05,0x0c,0x13,0x13,0x19,0x27,0x0c,0x14,0x02,0x19,0x1e,0x38,
  0x01,0x01,0x11,0x1e,0x24,0x1a,0x0c,0x27,0x16,0x0a,0x05,0x0c,0x05,
  0x22,0x1e,0x07,0x01,0x01,0x39,0x1e,0x27,0x13,0x13,0x13,0x0c,0x06,
  0x13,0x13,0x13,0x13,0x10,0x1e,0x0e,0x01,0x3b,0x1e,0x0a,0x13,0x14,
  0x13,0x05,0x1a,0x14,0x05,0x13,0x05,0x27,0x1e,0x39,0x01,0x11,0x1e,
  0x27,0x23,0x0c,0x14,0x02,0x0c,0x0c,0x22,0x09,0x1e,0x20,0x01,0x01,
  0x1e,0x1e,0x0c,0x27,0x27,0x1f,0x16,0x16,0x0a,0x03,0x16,0x1e,0x1e,
  0x34,0x01,0x35,0x1e,0x2d,0x0c,0x1f,0x13,0x0c,0x1f,0x13,0x27,0x16,
  0x1e,0x08,0x01,0x01,0x01,0x3b,0x1e,0x2d,0x13,0x13,0x13,0x13,0x29,
  0x13,0x13,0x13,0x13,0x2a,0x1e,0x2f,0x01,0x3a,0x1e,0x26,0x13,0x13,
  0x13,0x0c,0x02,0x1f,0x13,0x13,0x13,0x29,0x1e,0x30,0x01,0x04,0x1e,
  0x1a,0x13,0x13,0x13,0x13,0x0c,0x13,0x1f,0x1e,0x33,0x01,0x2e,0x1e,
  0x0a,0x13,0x1f,0x1f,0x14,0x1e,0x1e,0x0c,0x13,0x05,0x28,0x1e,0x3a,
  0x01,0x1e,0x1e,0x19,0x05,0x02,0x24,0x23,0x13,0x13,0x05,0x05,0x16,
  0x1e,0x1c,0x01,0x2e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x01,
  0x01,0x35,0x1e,0x2d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1b,0x01,0x39,
  0x1e,0x0d,0x0c,0x0c,0x22,0x12,0x1e,0x1f,0x0c,0x1f,0x13,0x0c,0x10,
  0x1e,0x0e,0x01,0x0f,0x1e,0x26,0x14,0x0b,0x0c,0x0d,0x1e,0x04,0x01,
  0x0f,0x1e,0x28,0x13,0x05,0x0b,0x24,0x1e,0x1f,0x02,0x22,0x1f,0x1e,
  0x0b,0x0c,0x02,0x0c,0x28,0x1e,0x1b,0x01,0x1e,0x1e,0x0c,0x13,0x1f,
  0x1f,0x1e,0x2a,0x1f,0x0c,0x13,0x0b,0x1e,0x07,0x01,0x20,0x1e,0x09,
  0x14,0x14,0x14,0x14,0x10,0x1e,0x0d,0x05,0x0b,0x27,0x2d,0x1e,0x35,
  0x01,0x39,0x1e,0x27,0x13,0x13,0x13,0x27,0x1e,0x13,0x13,0x13,0x13,
  0x03,0x1e,0x17,0x01,0x1b,0x1e,0x15,0x13,0x13,0x0c,0x02,0x1e,0x02,
  0x22,0x13,0x1f,0x27,0x1e,0x39,0x01,0x11,0x1e,0x19,0x05,0x1f,0x0c,
  0x13,0x05,0x24,0x23,0x18,0x1e,0x34,0x01,0x01,0x1e,0x1e,0x14,0x14,
  0x1f,0x27,0x15,0x1a,0x06,0x16,0x1e,0x1e,0x3d,0x01,0x01,0x00,0x1e,
  0x1e,0x02,0x02,0x0c,0x13,0x1f,0x0c,0x0b,0x21,0x1e,0x2b,0x01,0x01,
  0x01,0x30,0x1e,0x29,0x1f,0x1f,0x13,0x13,0x13,0x13,0x13,0x13,0x13,
  0x2a,0x1e,0x2f,0x01,0x1b,0x1e,0x15,0x13,0x13,0x13,0x13,0x13,0x0b,
  0x1a,0x27,0x0b,0x26,0x1e,0x3a,0x01,0x1b,0x1e,0x25,0x13,0x13,0x13,
  0x0c,0x0c,0x13,0x12,0x1e,0x11,0x01,0x3b,0x1e,0x0a,0x13,0x05,0x13,
  0x23,0x1e,0x1e,0x22,0x14,0x13,0x15,0x1e,0x3a,0x01,0x1e,0x1e,0x05,
  0x22,0x0b,0x0c,0x14,0x27,0x0c,0x0b,0x26,0x27,0x1e,0x38,0x01,0x3c,
  0x1e,0x16,0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,0x20,0x1e,0x09,
  0x05,0x13,0x13,0x13,0x25,0x1e,0x08,0x01,0x39,0x1e,0x27,0x0c,0x0c,
  0x13,0x0a,0x09,0x0c,0x05,0x02,0x0c,0x03,0x1e,0x38,0x01,0x01,0x0f,
  0x1e,0x28,0x0c,0x02,0x19,0x0d,0x1e,0x04,0x01,0x0f,0x1e,0x26,0x19,
  0x12,0x14,0x06,0x1e,0x0c,0x14,0x22,0x0c,0x1e,0x02,0x13,0x23,0x1f,
  0x0a,0x1e,0x3b,0x01,0x1e,0x1e,0x1f,0x13,0x05,0x13,0x1e,0x21,0x13,
  0x22,0x14,0x22,0x1e,0x3e,0x01,0x0e,0x1e,0x10,0x29,0x14,0x1f,0x22,
  0x2a,0x1e,0x14,0x14,0x05,0x14,0x18,0x1e,0x34,0x01,0x39,0x1e,0x27,
  0x13,0x13,0x13,0x24,0x1e,0x13,0x22,0x13,0x13,0x28,0x1e,0x2e,0x01,
  0x2e,0x1e,0x28,0x1f,0x1f,0x13,0x1f,0x1e,0x02,0x13,0x0c,0x19,0x27,
  0x1e,0x39,0x01,0x11,0x1e,0x02,0x1f,0x13,0x22,0x14,0x2d,0x1e,0x09,
  0x1e,0x04,0x01,0x01,0x01,0x38,0x1e,0x21,0x23,0x14,0x1f,0x0b,0x22,
  0x22,0x0c,0x29,0x1e,0x1e,0x00,0x01,0x01,0x3d,0x1e,0x18,0x0b,0x05,
  0x23,0x0c,0x27,0x1e,0x1e,0x1e,0x00,0x01,0x01,0x01,0x2c,0x1e,0x29,
  0x14,0x06,0x1f,0x0c,0x24,0x13,0x13,0x13,0x13,0x2a,0x1e,0x2f,0x01,
  0x37,0x1e,0x24,0x13,0x13,0x13,0x29,0x1e,0x18,0x16,0x03,0x16,0x21,
  0x1e,0x36,0x01,0x35,0x40,0x1e,0x15,0x14,0x13,0x1f,0x14,0x21,0x1e,
  0x1e,0x08,0x01,0x1b,0x1e,0x28,0x13,0x23,0x05,0x1f,0x25,0x1e,0x05,
  0x0c,0x0c,0x26,0x1e,0x3a,0x01,0x1e,0x1e,0x1a,0x0c,0x27,0x0b,0x22,
  0x19,0x0c,0x05,0x1f,0x22,0x1e,0x3e,0x01,0x3c,0x1e,0x16,0x13,0x13,
  0x13,0x13,0x1e,0x1e,0x01,0x01,0x20,0x1e,0x09,0x1f,0x13,0x13,0x13,
  0x25,0x1e,0x08,0x01,0x39,0x1e,0x02,0x13,0x05,0x0c,0x1a,0x27,0x0c,
  0x13,0x13,0x1a,0x1e,0x32,0x2b,0x01,0x01,0x0f,0x1e,0x15,0x22,0x14,
  0x1f,0x06,0x1e,0x04,0x01,0x0f,0x1e,0x26,0x0c,0x0b,0x22,0x12,0x1e,
  0x23,0x0d,0x1f,0x23,0x1e,0x23,0x05,0x27,0x1f,0x0a,0x1e,0x3b,0x01,
  0x1e,0x1e,0x05,0x13,0x23,0x05,0x1e,0x31,0x0c,0x05,0x0c,0x0b,0x1e,
  0x3e,0x01,0x34,0x1e,0x18,0x0b,0x05,0x27,0x05,0x18,0x1e,0x22,0x0c,
  0x0b,0x14,0x10,0x1e,0x0e,0x01,0x39,0x1e,0x0d,0x13,0x13,0x13,0x27,
  0x1e,0x13,0x0c,0x13,0x13,0x21,0x1e,0x36,0x01,0x0f,0x1e,0x26,0x1f,
  0x13,0x05,0x0b,0x1e,0x23,0x1f,0x13,0x1f,0x27,0x1e,0x39,0x01,0x11,
  0x1e,0x02,0x1f,0x0c,0x05,0x02,0x1e,0x38,0x1e,0x04,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x1e,0x09,0x16,0x28,0x1a,0x1f,0x19,0x22,0x1f,0x21,
  0x1e,0x2b,0x01,0x01,0x01,0x39,0x1e,0x27,0x13,0x05,0x1f,0x1a,0x1e,
  0x1e,0x3d,0x01,0x01,0x01,0x01,0x38,0x1e,0x1a,0x14,0x0c,0x0c,0x12,
  0x1e,0x0c,0x13,0x13,0x13,0x2a,0x1e,0x2f,0x01,0x2f,0x1e,0x03,0x0c,
  0x14,0x13,0x19,0x09,0x27,0x13,0x1f,0x13,0x03,0x1e,0x17,0x01,0x01,
  0x17,0x1e,0x03,0x13,0x0c,0x02,0x22,0x1e,0x1e,0x3d,0x01,0x01,0x3c,
  0x1e,0x16,0x0c,0x13,0x0c,0x0b,0x05,0x1f,0x02,0x05,0x1f,0x15,0x1e,
  0x3a,0x01,0x1e,0x1e,0x14,0x14,0x14,0x0c,0x1e,0x21,0x05,0x0b,0x02,
  0x0b,0x1e,0x1d,0x01,0x3c,0x1e,0x16,0x13,0x13,0x13,0x13,0x1e,0x1e,
  0x01,0x01,0x20,0x1e,0x09,0x1f,0x13,0x13,0x13,0x25,0x1e,0x08,0x01,
  0x39,0x1e,0x02,0x05,0x05,0x13,0x0c,0x0c,0x13,0x1f,0x22,0x1e,0x1e,
  0x3a,0x01,0x01,0x01,0x0f,0x1e,0x28,0x14,0x22,0x05,0x0d,0x1e,0x04,
  0x01,0x0f,0x1e,0x26,0x1a,0x19,0x13,0x24,0x1e,0x1f,0x0b,0x14,0x05,
  0x1e,0x19,0x13,0x05,0x22,0x0a,0x1e,0x3b,0x01,0x1e,0x1e,0x1f,0x0b,
  0x13,0x0c,0x1e,0x31,0x1f,0x02,0x05,0x0b,0x1e,0x3e,0x01,0x01,0x11,
  0x1e,0x29,0x1f,0x27,0x02,0x1a,0x12,0x1f,0x19,0x22,0x22,0x1e,0x3e,
  0x01,0x01,0x39,0x1e,0x27,0x13,0x13,0x13,0x1f,0x23,0x13,0x1f,0x13,
  0x14,0x10,0x1e,0x0e,0x01,0x17,0x1e,0x03,0x1f,0x0c,0x13,0x13,0x1f,
  0x0c,0x13,0x0b,0x0b,0x02,0x1e,0x39,0x01,0x11,0x1e,0x0d,0x05,0x1f,
  0x02,0x0d,0x1e,0x04,0x20,0x01,0x01,0x01,0x01,0x00,0x1e,0x1e,0x1a,
  0x29,0x0b,0x24,0x31,0x27,0x23,0x13,0x06,0x31,0x1e,0x17,0x01,0x01,
  0x01,0x38,0x1e,0x19,0x1f,0x23,0x13,0x1a,0x1e,0x11,0x01,0x01,0x01,
  0x01,0x00,0x1e,0x1e,0x02,0x23,0x27,0x13,0x24,0x1e,0x0c,0x13,0x13,
  0x13,0x2a,0x1e,0x2f,0x01,0x01,0x1e,0x1e,0x0c,0x23,0x13,0x13,0x13,
  0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,0x01,0x08,0x1e,0x16,0x1f,
  0x13,0x0c,0x14,0x18,0x1e,0x34,0x01,0x01,0x20,0x1e,0x1e,0x05,0x05,
  0x0c,0x1f,0x1f,0x0c,0x13,0x0c,0x23,0x15,0x1e,0x3a,0x01,0x1e,0x1e,
  0x14,0x1f,0x22,0x1f,0x1e,0x2d,0x14,0x05,0x0c,0x1a,0x1e,0x1d,0x01,
  0x3c,0x1e,0x16,0x13,0x13,0x22,0x13,0x1e,0x1e,0x01,0x01,0x20,0x1e,
  0x09,0x1f,0x13,0x13,0x13,0x25,0x1e,0x08,0x01,0x39,0x1e,0x02,0x1f,
  0x23,0x13,0x0c,0x1f,0x13,0x05,0x14,0x1e,0x1e,0x08,0x01,0x01,0x01,
  0x0f,0x1e,0x26,0x29,0x14,0x22,0x26,0x1e,0x04,0x01,0x3a,0x1e,0x26,
  0x05,0x22,0x14,0x24,0x1e,0x1a,0x19,0x13,0x02,0x1e,0x19,0x05,0x22,
  0x1f,0x0a,0x1e,0x3b,0x01,0x1e,0x1e,0x0c,0x05,0x05,0x0c,0x1e,0x31,
  0x0c,0x13,0x0c,0x27,0x1e,0x32,0x01,0x01,0x37,0x1e,0x28,0x1f,0x14,
  0x0c,0x14,0x27,0x0c,0x19,0x12,0x29,0x1e,0x3d,0x01,0x01,0x39,0x1e,
  0x0d,0x13,0x13,0x13,0x14,0x1f,0x13,0x0c,0x13,0x0c,0x09,0x1e,0x20,
  0x01,0x20,0x1e,0x09,0x14,0x13,0x05,0x0c,0x13,0x1f,0x13,0x13,0x0c,
  0x27,0x1e,0x39,0x01,0x38,0x1e,0x27,0x1f,0x0c,0x13,0x1a,0x1e,0x39,
  0x01,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x23,0x14,0x22,0x29,0x14,
  0x02,0x06,0x13,0x0b,0x1e,0x3e,0x00,0x01,0x01,0x01,0x11,0x1e,0x02,
  0x0c,0x23,0x05,0x29,0x1e,0x1e,0x2c,0x01,0x01,0x01,0x01,0x1d,0x1e,
  0x02,0x1f,0x13,0x23,0x06,0x1e,0x22,0x13,0x13,0x13,0x2a,0x1e,0x2f,
  0x01,0x01,0x38,0x1e,0x18,0x0b,0x13,0x0b,0x13,0x13,0x1f,0x13,0x1e,
  0x1e,0x38,0x01,0x01,0x01,0x08,0x1e,0x16,0x13,0x05,0x22,0x14,0x1e,
  0x1e,0x00,0x01,0x01,0x01,0x38,0x1e,0x1e,0x19,0x14,0x23,0x0a,0x12,
  0x05,0x0c,0x05,0x15,0x1e,0x3a,0x01,0x1e,0x1e,0x05,0x27,0x05,0x05,
  0x1e,0x10,0x0c,0x0b,0x1f,0x23,0x1e,0x1d,0x01,0x3c,0x1e,0x16,0x13,
  0x13,0x22,0x0c,0x1e,0x1e,0x01,0x01,0x20,0x1e,0x09,0x1f,0x13,0x13,
  0x0c,0x25,0x1e,0x08,0x01,0x39,0x1e,0x02,0x13,0x13,0x0c,0x0d,0x02,
  0x13,0x23,0x05,0x29,0x1e,0x38,0x01,0x01,0x01,0x0f,0x1e,0x26,0x02,
  0x02,0x1f,0x0d,0x1e,0x04,0x01,0x04,0x1e,0x0d,0x0b,0x1f,0x0b,0x26,
  0x1e,0x05,0x19,0x22,0x23,0x1e,0x13,0x05,0x0c,0x23,0x24,0x1e,0x1c,
  0x01,0x1e,0x1e,0x0c,0x27,0x13,0x14,0x1e,0x0a,0x05,0x05,0x0c,0x14,
  0x1e,0x1e,0x01,0x01,0x36,0x1e,0x1e,0x16,0x0b,0x05,0x14,0x22,0x1f,
  0x05,0x06,0x1e,0x1e,0x3b,0x01,0x01,0x39,0x1e,0x0d,0x13,0x13,0x13,
  0x27,0x27,0x13,0x13,0x13,0x03,0x1e,0x38,0x01,0x01,0x01,0x38,0x1e,
  0x15,0x13,0x05,0x0c,0x26,0x19,0x13,0x0c,0x0c,0x02,0x1e,0x39,0x01,
  0x1e,0x1e,0x23,0x13,0x05,0x05,0x05,0x1e,0x33,0x01,0x01,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x31,0x23,0x14,0x19,0x05,0x22,0x22,0x05,0x18,
  0x1e,0x04,0x01,0x01,0x01,0x01,0x1d,0x1e,0x23,0x0b,0x0b,0x0b,0x02,
  0x06,0x09,0x1e,0x35,0x01,0x01,0x01,0x2c,0x1e,0x1a,0x0b,0x05,0x0c,
  0x0b,0x22,0x1f,0x13,0x13,0x13,0x2a,0x1e,0x2f,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x1e,0x25,0x0a,0x2a,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x01,0x08,0x1e,0x16,0x0c,0x13,0x13,0x14,0x1e,0x1e,0x00,0x01,0x01,
  0x20,0x1e,0x09,0x1a,0x27,0x23,0x06,0x1e,0x0d,0x13,0x14,0x19,0x12,
  0x1e,0x1c,0x01,0x1e,0x1e,0x1f,0x27,0x02,0x0c,0x1e,0x10,0x19,0x22,
  0x13,0x29,0x1e,0x1d,0x01,0x3c,0x1e,0x16,0x13,0x13,0x1f,0x14,0x1e,
  0x1e,0x01,0x01,0x20,0x1e,0x09,0x1f,0x22,0x13,0x0c,0x25,0x1e,0x08,
  0x01,0x39,0x1e,0x02,0x1f,0x1f,0x13,0x25,0x16,0x0c,0x13,0x0c,0x0b,
  0x2d,0x1e,0x2e,0x01,0x01,0x0f,0x1e,0x26,0x14,0x0c,0x0b,0x24,0x1e,
  0x04,0x01,0x1b,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x3c,0x01,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,
  0x01,0x36,0x07,0x1e,0x1e,0x03,0x16,0x16,0x03,0x1e,0x1e,0x1e,0x3b,
  0x01,0x01,0x01,0x39,0x1e,0x15,0x13,0x13,0x13,0x27,0x1e,0x21,0x28,
  0x18,0x1e,0x32,0x2b,0x01,0x01,0x01,0x36,0x1e,0x1e,0x18,0x28,0x31,
  0x1e,0x27,0x0c,0x1f,0x0c,0x27,0x1e,0x39,0x01,0x38,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x39,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x1d,
  0x1e,0x1e,0x09,0x16,0x25,0x16,0x18,0x1e,0x1e,0x11,0x34,0x01,0x01,
  0x01,0x01,0x04,0x1e,0x0d,0x1f,0x0b,0x1f,0x13,0x05,0x31,0x1e,0x36,
  0x01,0x01,0x01,0x1c,0x1e,0x24,0x14,0x13,0x1a,0x19,0x13,0x0b,0x13,
  0x13,0x13,0x03,0x1e,0x2f,0x01,0x01,0x01,0x20,0x2c,0x40,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x3c,0x1e,0x16,
  0x13,0x05,0x0c,0x14,0x1e,0x1e,0x00,0x01,0x01,0x35,0x1e,0x2d,0x05,
  0x0c,0x1f,0x0c,0x14,0x23,0x0c,0x13,0x14,0x31,0x1e,0x3c,0x01,0x1e,
  0x1e,0x1f,0x14,0x0c,0x14,0x1e,0x10,0x19,0x12,0x13,0x19,0x1e,0x1d,
  0x01,0x3c,0x1e,0x16,0x13,0x13,0x14,0x1f,0x1e,0x1e,0x01,0x01,0x20,
  0x1e,0x09,0x05,0x13,0x13,0x05,0x25,0x1e,0x08,0x01,0x38,0x1e,0x27,
  0x1f,0x13,0x0b,0x28,0x1e,0x05,0x05,0x0c,0x1f,0x0d,0x1e,0x1e,0x3c,
  0x01,0x3a,0x1e,0x26,0x0b,0x22,0x0b,0x12,0x1e,0x04,0x01,0x01,0x1b,
  0x38,0x1e,0x1e,0x1e,0x38,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x1e,0x1e,
  0x1e,0x1e,0x38,0x3c,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,
  0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2f,0x01,0x01,0x01,0x01,0x39,
  0x1e,0x27,0x1f,0x23,0x13,0x02,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,
  0x01,0x01,0x01,0x01,0x36,0x38,0x1e,0x1e,0x1e,0x1e,0x19,0x1f,0x19,
  0x0c,0x27,0x1e,0x39,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x33,0x39,
  0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x11,0x0e,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,
  0x2a,0x23,0x13,0x19,0x05,0x0c,0x16,0x1e,0x08,0x01,0x01,0x01,0x08,
  0x1e,0x1e,0x24,0x13,0x05,0x23,0x2a,0x22,0x13,0x13,0x13,0x28,0x1e,
  0x2e,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x08,0x3b,0x3c,0x34,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x3a,0x1e,0x15,0x13,0x05,0x0c,0x1f,
  0x09,0x1e,0x20,0x01,0x01,0x01,0x33,0x1e,0x23,0x0c,0x13,0x0c,0x05,
  0x19,0x14,0x1f,0x0b,0x1e,0x32,0x01,0x01,0x1e,0x1e,0x1f,0x0b,0x05,
  0x14,0x1e,0x2a,0x05,0x0c,0x1f,0x0b,0x1e,0x1e,0x01,0x3a,0x1e,0x26,
  0x13,0x13,0x13,0x22,0x1e,0x1e,0x01,0x01,0x35,0x1e,0x09,0x05,0x23,
  0x13,0x1f,0x25,0x1e,0x08,0x01,0x1e,0x1e,0x13,0x05,0x13,0x13,0x23,
  0x1e,0x02,0x13,0x14,0x23,0x13,0x24,0x1e,0x37,0x01,0x04,0x1e,0x24,
  0x23,0x14,0x0d,0x27,0x1e,0x07,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x17,0x08,0x08,
  0x17,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1d,0x1e,0x19,0x05,0x14,
  0x13,0x22,0x1e,0x3e,0x2e,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2b,0x1b,0x33,0x1e,0x1a,0x0c,0x0c,0x13,0x0b,0x1e,0x1d,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x3b,0x3b,0x08,0x20,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x3e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x07,0x20,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x03,
  0x0a,0x09,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x17,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,
  0x01,0x01,0x11,0x1e,0x1e,0x12,0x05,0x0c,0x14,0x23,0x05,0x03,0x1e,
  0x1e,0x30,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x17,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x01,0x20,0x40,0x1e,0x1e,0x05,0x1f,0x13,0x13,0x25,0x1e,
  0x08,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x3c,0x01,0x2e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x04,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x30,0x1e,0x0a,0x29,0x12,0x29,0x28,0x1e,0x3d,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x39,0x1e,0x15,0x12,0x24,0x29,0x0a,0x1e,0x30,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x1e,
  0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x17,
  0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x0e,0x11,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x32,0x30,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x38,0x1e,0x1e,0x1e,0x1e,0x38,
  0x2b,0x01,0x01,0x17,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x2e,
  0x1e,0x25,0x05,0x13,0x0b,0x0c,0x0b,0x25,0x1e,0x1b,0x01,0x2b,0x38,
  0x1e,0x1e,0x1e,0x1e,0x32,0x30,0x1d,0x1e,0x1e,0x1e,0x1e,0x38,0x3c,
  0x01,0x01,0x01,0x2e,0x38,0x1e,0x1e,0x1e,0x07,0x04,0x20,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x0e,0x33,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x0e,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x36,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x33,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x00,0x17,0x1b,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x08,0x38,0x1e,0x1e,
  0x1e,0x1e,0x33,0x11,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3d,0x1e,0x0d,0x05,0x13,
  0x0c,0x1f,0x22,0x09,0x1e,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x2e,0x04,
  0x1c,0x3d,0x2e,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x36,0x1b,0x30,0x1c,0x30,0x3b,0x0e,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x0f,0x1e,0x06,0x13,0x14,0x27,0x0c,0x16,0x1e,
  0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x17,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x40,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x17,0x38,0x1e,
  0x33,0x07,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x2e,0x3b,0x2e,0x08,
  0x35,0x08,0x2e,0x3b,0x2e,0x0e,0x01,0x01,0x01,0x01,0x20,0x08,0x2e,
  0x3b,0x3b,0x0e,0x01,0x36,0x3a,0x08,0x3b,0x1b,0x2b,0x01,0x01,0x01,
  0x01,0x35,0x08,0x2e,0x1b,0x2f,0x00,0x17,0x3b,0x3b,0x3b,0x3b,0x08,
  0x35,0x2f,0x1b,0x3b,0x1b,0x2b,0x01,0x01,0x01,0x01,0x01,0x2b,0x2e,
  0x3b,0x1b,0x17,0x00,0x17,0x2e,0x08,0x2e,0x3c,0x34,0x01,0x01,0x01,
  0x01,0x0e,0x3b,0x3b,0x08,0x3a,0x36,0x0e,0x3b,0x3b,0x08,0x3a,0x17,
  0x01,0x01,0x01,0x01,0x01,0x01,0x36,0x1b,0x08,0x3b,0x3b,0x3b,0x08,
  0x3a,0x17,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x37,0x11,
  0x1d,0x32,0x1d,0x30,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x35,0x3b,0x30,0x0f,0x37,0x37,0x2f,0x01,0x01,0x01,0x01,0x01,0x01,
  0x00,0x0f,0x11,0x32,0x1d,0x32,0x30,0x3c,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x20,0x3a,0x2c,0x32,0x1d,0x1d,0x3d,0x3b,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x36,0x3a,0x37,0x0f,
  0x0f,0x1c,0x3b,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x1b,0x37,
  0x0f,0x0f,0x0f,0x0f,0x0f,0x1c,0x1b,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x20,0x3c,0x1c,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
  0x1c,0x0f,0x2f,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x08,0x30,0x32,
  0x1d,0x32,0x1d,0x1c,0x17,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x11,0x1e,0x1e,0x1e,0x1e,0x1e,0x40,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x11,0x01,0x01,0x2b,0x07,0x1e,0x1e,0x1e,0x1e,0x1e,0x39,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x2b,0x01,0x01,0x04,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x32,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x40,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x04,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x32,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x3e,0x34,0x01,0x01,0x39,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x33,0x1e,0x1e,0x1e,0x1e,0x1e,0x1d,0x01,0x01,0x01,
  0x00,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x11,0x01,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x39,0x01,0x01,0x01,0x01,0x37,0x32,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x08,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x04,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x33,
  0x0e,0x01,0x01,0x01,0x01,0x30,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x17,
  0x1c,0x1d,0x1e,0x1e,0x3e,0x38,0x1c,0x0e,0x01,0x01,0x01,0x01,0x35,
  0x07,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x04,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,
  0x10,0x0a,0x0a,0x28,0x16,0x1e,0x25,0x28,0x0a,0x28,0x10,0x1e,0x2e,
  0x01,0x38,0x1e,0x16,0x25,0x0a,0x0a,0x10,0x1e,0x10,0x15,0x25,0x0a,
  0x28,0x1e,0x38,0x01,0x20,0x1e,0x09,0x25,0x0a,0x28,0x2a,0x1e,0x03,
  0x16,0x16,0x25,0x25,0x16,0x1e,0x2a,0x28,0x25,0x28,0x09,0x1e,0x20,
  0x01,0x2b,0x1e,0x21,0x28,0x25,0x15,0x03,0x1e,0x03,0x25,0x25,0x0a,
  0x03,0x1e,0x3c,0x01,0x35,0x1e,0x2d,0x0a,0x0a,0x25,0x26,0x1e,0x1e,
  0x0a,0x0a,0x25,0x26,0x31,0x1e,0x0f,0x01,0x01,0x3d,0x1e,0x21,0x15,
  0x25,0x25,0x25,0x0a,0x16,0x15,0x10,0x1e,0x3b,0x01,0x01,0x01,0x01,
  0x38,0x1e,0x1e,0x0a,0x19,0x0b,0x22,0x0b,0x28,0x1e,0x1e,0x38,0x20,
  0x01,0x01,0x20,0x07,0x1e,0x18,0x25,0x12,0x06,0x06,0x26,0x2d,0x1e,
  0x35,0x01,0x01,0x37,0x1e,0x1e,0x03,0x02,0x22,0x23,0x22,0x24,0x21,
  0x1e,0x1e,0x2b,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x25,0x27,0x22,
  0x23,0x0b,0x0d,0x21,0x1e,0x1e,0x08,0x01,0x01,0x01,0x01,0x01,0x17,
  0x1e,0x18,0x26,0x24,0x06,0x06,0x24,0x25,0x1e,0x11,0x01,0x01,0x01,
  0x00,0x1e,0x1e,0x28,0x24,0x26,0x26,0x06,0x06,0x06,0x24,0x28,0x1e,
  0x1e,0x00,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x33,0x30,0x01,0x01,0x01,0x39,0x1e,0x16,0x24,0x06,
  0x26,0x06,0x06,0x06,0x06,0x24,0x06,0x09,0x1e,0x20,0x01,0x01,0x34,
  0x3e,0x1e,0x10,0x24,0x19,0x0b,0x22,0x23,0x06,0x1e,0x1e,0x38,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x0d,0x1f,0x19,0x05,
  0x02,0x1e,0x14,0x13,0x13,0x05,0x0d,0x1e,0x04,0x01,0x1e,0x1e,0x13,
  0x0c,0x13,0x0c,0x23,0x1e,0x24,0x0c,0x0c,0x22,0x13,0x1e,0x1e,0x01,
  0x36,0x1e,0x31,0x13,0x27,0x0b,0x0c,0x18,0x05,0x22,0x22,0x22,0x23,
  0x14,0x1e,0x27,0x22,0x29,0x24,0x03,0x1e,0x2f,0x01,0x3b,0x1e,0x0a,
  0x27,0x0b,0x0c,0x19,0x1e,0x05,0x14,0x14,0x0b,0x0a,0x1e,0x3b,0x01,
  0x3c,0x1e,0x16,0x13,0x05,0x05,0x13,0x2a,0x1e,0x13,0x13,0x13,0x13,
  0x19,0x1e,0x11,0x01,0x01,0x32,0x1e,0x0b,0x22,0x13,0x0c,0x0c,0x0c,
  0x1f,0x13,0x29,0x1e,0x3d,0x01,0x01,0x01,0x0f,0x1e,0x2d,0x0c,0x23,
  0x14,0x13,0x13,0x13,0x13,0x13,0x18,0x1e,0x2c,0x01,0x01,0x3b,0x1e,
  0x16,0x1f,0x0c,0x05,0x0c,0x0c,0x13,0x25,0x1e,0x08,0x01,0x0e,0x33,
  0x1e,0x27,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x28,0x1e,0x32,0x00,
  0x01,0x01,0x38,0x1e,0x1e,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,
  0x29,0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x06,0x13,0x13,
  0x0c,0x0c,0x0c,0x1f,0x1e,0x33,0x01,0x01,0x01,0x34,0x1e,0x18,0x13,
  0x1f,0x14,0x13,0x14,0x14,0x13,0x05,0x02,0x2d,0x1e,0x0e,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x1e,0x06,0x0b,0x13,0x13,0x14,0x23,0x0a,0x1e,
  0x1e,0x30,0x01,0x01,0x40,0x1e,0x05,0x0c,0x0c,0x13,0x13,0x14,0x0c,
  0x13,0x0b,0x1f,0x16,0x1e,0x3c,0x01,0x01,0x04,0x1e,0x2a,0x13,0x05,
  0x02,0x14,0x13,0x0c,0x23,0x14,0x1e,0x1e,0x3c,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x3a,0x1e,0x28,0x1f,0x05,0x22,0x23,0x1e,0x14,0x27,
  0x0c,0x0b,0x25,0x1e,0x3a,0x01,0x38,0x1e,0x29,0x0c,0x13,0x1f,0x19,
  0x1e,0x27,0x0c,0x0c,0x13,0x26,0x1e,0x38,0x01,0x01,0x38,0x1e,0x19,
  0x1f,0x0c,0x19,0x03,0x23,0x06,0x0d,0x1f,0x1a,0x02,0x2d,0x19,0x0b,
  0x0b,0x22,0x18,0x1e,0x34,0x01,0x0e,0x33,0x1e,0x24,0x05,0x05,0x22,
  0x1a,0x24,0x0d,0x0c,0x0d,0x1e,0x33,0x0e,0x01,0x34,0x3e,0x1e,0x19,
  0x0b,0x26,0x22,0x2a,0x1e,0x1f,0x1f,0x13,0x13,0x2d,0x1e,0x0f,0x01,
  0x01,0x3d,0x1e,0x06,0x13,0x02,0x02,0x0c,0x05,0x02,0x0c,0x26,0x1e,
  0x0f,0x01,0x01,0x01,0x11,0x1e,0x29,0x1f,0x19,0x12,0x22,0x14,0x13,
  0x0b,0x14,0x05,0x1e,0x40,0x01,0x01,0x30,0x1e,0x29,0x13,0x22,0x1f,
  0x13,0x02,0x14,0x03,0x1e,0x17,0x01,0x3b,0x1e,0x25,0x12,0x14,0x13,
  0x13,0x27,0x14,0x13,0x13,0x13,0x2a,0x1e,0x3b,0x01,0x01,0x1e,0x1e,
  0x13,0x13,0x13,0x13,0x13,0x0b,0x13,0x13,0x13,0x13,0x31,0x1e,0x36,
  0x01,0x01,0x01,0x00,0x1e,0x1e,0x14,0x13,0x1f,0x19,0x13,0x0c,0x27,
  0x1e,0x38,0x01,0x01,0x01,0x00,0x1e,0x1e,0x23,0x0c,0x14,0x0c,0x14,
  0x22,0x0b,0x23,0x0b,0x18,0x1e,0x20,0x01,0x01,0x01,0x1b,0x1e,0x18,
  0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x0b,0x1e,0x1d,0x01,0x01,
  0x39,0x1e,0x24,0x13,0x1f,0x0b,0x1f,0x0c,0x0c,0x13,0x0c,0x1f,0x09,
  0x1e,0x20,0x01,0x01,0x07,0x1e,0x14,0x1f,0x23,0x13,0x02,0x27,0x1f,
  0x19,0x12,0x24,0x1e,0x11,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3a,
  0x1e,0x15,0x0d,0x1a,0x0c,0x27,0x1e,0x13,0x05,0x0c,0x05,0x15,0x1e,
  0x3a,0x01,0x3b,0x1e,0x31,0x1f,0x05,0x05,0x23,0x1e,0x22,0x13,0x05,
  0x0c,0x18,0x1e,0x2f,0x01,0x01,0x1c,0x1e,0x06,0x02,0x1f,0x19,0x16,
  0x27,0x22,0x1a,0x23,0x14,0x0b,0x16,0x0d,0x14,0x1a,0x29,0x1e,0x11,
  0x01,0x01,0x01,0x08,0x1e,0x09,0x26,0x14,0x22,0x0b,0x22,0x23,0x22,
  0x1e,0x1e,0x3d,0x01,0x01,0x01,0x37,0x1e,0x25,0x05,0x1f,0x0c,0x28,
  0x25,0x14,0x06,0x0b,0x0b,0x1e,0x32,0x01,0x01,0x01,0x2f,0x1e,0x18,
  0x09,0x10,0x05,0x0c,0x13,0x13,0x22,0x09,0x1e,0x17,0x01,0x01,0x34,
  0x1e,0x18,0x23,0x14,0x0b,0x0c,0x21,0x09,0x05,0x02,0x24,0x22,0x09,
  0x1e,0x20,0x01,0x3b,0x1e,0x18,0x10,0x31,0x0c,0x0c,0x1f,0x13,0x03,
  0x1e,0x17,0x01,0x37,0x1e,0x06,0x0c,0x1f,0x19,0x24,0x1e,0x24,0x14,
  0x13,0x13,0x12,0x1e,0x30,0x01,0x01,0x38,0x1e,0x23,0x22,0x0b,0x23,
  0x2d,0x1e,0x13,0x13,0x13,0x13,0x2a,0x1e,0x2f,0x01,0x01,0x01,0x2e,
  0x1e,0x31,0x13,0x13,0x0b,0x05,0x13,0x0c,0x23,0x1e,0x38,0x01,0x01,
  0x01,0x00,0x1e,0x1e,0x14,0x0c,0x1f,0x24,0x1e,0x03,0x2a,0x21,0x1e,
  0x1e,0x04,0x01,0x01,0x01,0x01,0x2c,0x1e,0x0d,0x13,0x13,0x13,0x05,
  0x05,0x13,0x13,0x13,0x13,0x31,0x1e,0x08,0x01,0x3b,0x1e,0x09,0x21,
  0x21,0x1e,0x0a,0x0b,0x0c,0x0c,0x22,0x1a,0x1e,0x39,0x01,0x01,0x0e,
  0x1e,0x10,0x0b,0x0c,0x19,0x14,0x1e,0x2a,0x14,0x0b,0x0c,0x05,0x1e,
  0x1e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3a,0x1e,0x15,0x0c,0x14,
  0x14,0x14,0x1e,0x02,0x0d,0x05,0x0b,0x28,0x1e,0x3a,0x01,0x01,0x33,
  0x1e,0x14,0x1f,0x22,0x0b,0x21,0x05,0x05,0x05,0x13,0x1e,0x1e,0x01,
  0x01,0x01,0x3b,0x1e,0x0a,0x13,0x05,0x1a,0x12,0x12,0x26,0x24,0x02,
  0x22,0x0b,0x0a,0x19,0x23,0x14,0x0a,0x1e,0x3a,0x01,0x01,0x01,0x01,
  0x38,0x1e,0x16,0x1a,0x1a,0x27,0x26,0x27,0x28,0x1e,0x38,0x00,0x01,
  0x01,0x01,0x35,0x1e,0x18,0x23,0x02,0x13,0x24,0x06,0x14,0x1f,0x0c,
  0x15,0x1e,0x30,0x01,0x01,0x01,0x01,0x04,0x1e,0x1e,0x0d,0x0c,0x13,
  0x1f,0x1f,0x2a,0x1e,0x39,0x01,0x01,0x01,0x0e,0x1e,0x10,0x0d,0x0c,
  0x02,0x19,0x1e,0x1e,0x22,0x0b,0x1f,0x14,0x10,0x1e,0x0e,0x01,0x01,
  0x04,0x1e,0x1e,0x1e,0x0c,0x05,0x02,0x0c,0x03,0x1e,0x17,0x01,0x3c,
  0x1e,0x2d,0x25,0x03,0x2d,0x1e,0x31,0x1f,0x14,0x27,0x0c,0x12,0x1e,
  0x3d,0x01,0x00,0x1e,0x1e,0x1e,0x1e,0x1e,0x18,0x1e,0x2a,0x0c,0x13,
  0x13,0x13,0x31,0x1e,0x36,0x01,0x01,0x01,0x38,0x1e,0x1a,0x13,0x13,
  0x16,0x14,0x13,0x1f,0x19,0x1e,0x38,0x01,0x01,0x01,0x00,0x1e,0x1e,
  0x14,0x13,0x1f,0x27,0x29,0x23,0x23,0x02,0x10,0x1e,0x32,0x01,0x01,
  0x01,0x01,0x1d,0x1e,0x06,0x14,0x13,0x13,0x06,0x1e,0x24,0x19,0x23,
  0x13,0x29,0x1e,0x30,0x01,0x01,0x2c,0x1e,0x1e,0x1e,0x1e,0x0d,0x1f,
  0x0c,0x0c,0x13,0x2d,0x1e,0x1b,0x01,0x01,0x01,0x3e,0x1e,0x14,0x05,
  0x22,0x1f,0x21,0x28,0x0c,0x02,0x19,0x29,0x1e,0x11,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x3a,0x1e,0x15,0x29,0x14,0x1f,0x0b,0x1e,0x14,
  0x14,0x14,0x05,0x26,0x1e,0x3a,0x01,0x01,0x1d,0x1e,0x19,0x0c,0x13,
  0x23,0x0a,0x1f,0x1f,0x1a,0x02,0x1e,0x38,0x01,0x01,0x01,0x2b,0x1e,
  0x21,0x14,0x1f,0x27,0x06,0x22,0x1f,0x02,0x06,0x0b,0x0d,0x26,0x0b,
  0x02,0x0b,0x31,0x1e,0x36,0x01,0x01,0x01,0x01,0x2c,0x1e,0x03,0x23,
  0x02,0x23,0x05,0x14,0x31,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x11,
  0x1e,0x1a,0x0c,0x19,0x26,0x23,0x23,0x1a,0x05,0x1e,0x1e,0x2f,0x01,
  0x01,0x01,0x01,0x38,0x1e,0x2a,0x0c,0x1f,0x13,0x05,0x29,0x1e,0x32,
  0x35,0x01,0x01,0x01,0x36,0x1e,0x31,0x23,0x22,0x14,0x1f,0x1e,0x18,
  0x0c,0x27,0x27,0x0c,0x2d,0x1e,0x35,0x01,0x01,0x01,0x20,0x1e,0x09,
  0x1f,0x13,0x13,0x05,0x03,0x1e,0x17,0x01,0x01,0x39,0x1e,0x1e,0x1e,
  0x10,0x02,0x27,0x0b,0x13,0x05,0x0c,0x0a,0x1e,0x2e,0x01,0x01,0x30,
  0x1e,0x38,0x32,0x1e,0x19,0x13,0x13,0x13,0x13,0x05,0x2a,0x1e,0x1d,
  0x01,0x01,0x01,0x17,0x1e,0x10,0x13,0x14,0x29,0x2d,0x0c,0x05,0x05,
  0x23,0x1e,0x38,0x35,0x01,0x01,0x34,0x1e,0x18,0x14,0x23,0x0c,0x14,
  0x02,0x0c,0x0c,0x22,0x0b,0x03,0x1e,0x1b,0x01,0x01,0x01,0x3e,0x1e,
  0x0b,0x14,0x27,0x0c,0x12,0x18,0x06,0x27,0x06,0x1e,0x18,0x1e,0x08,
  0x01,0x01,0x01,0x35,0x2e,0x1e,0x09,0x05,0x13,0x13,0x05,0x27,0x1e,
  0x11,0x01,0x01,0x01,0x01,0x04,0x1e,0x09,0x14,0x22,0x0b,0x14,0x23,
  0x22,0x14,0x27,0x1e,0x1e,0x3b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x3a,0x1e,0x15,0x1f,0x05,0x27,0x05,0x1e,0x29,0x22,0x0c,0x0b,0x15,
  0x1e,0x3a,0x01,0x01,0x30,0x1e,0x25,0x1f,0x1f,0x0c,0x19,0x1f,0x13,
  0x0c,0x03,0x1e,0x0f,0x01,0x01,0x01,0x34,0x1e,0x18,0x14,0x05,0x14,
  0x22,0x0d,0x27,0x26,0x12,0x27,0x22,0x0d,0x14,0x27,0x22,0x10,0x1e,
  0x0e,0x01,0x01,0x01,0x17,0x1e,0x1e,0x14,0x02,0x0d,0x27,0x29,0x05,
  0x02,0x1e,0x1e,0x1b,0x01,0x01,0x01,0x01,0x3a,0x1e,0x16,0x1f,0x0c,
  0x19,0x22,0x05,0x1f,0x0d,0x1e,0x38,0x01,0x01,0x01,0x01,0x2e,0x1e,
  0x1e,0x0c,0x23,0x05,0x13,0x27,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,
  0x36,0x1e,0x21,0x27,0x14,0x22,0x05,0x1e,0x1e,0x14,0x14,0x1f,0x27,
  0x2d,0x1e,0x35,0x01,0x01,0x01,0x20,0x1e,0x09,0x0c,0x1f,0x1f,0x13,
  0x03,0x1e,0x17,0x01,0x01,0x30,0x1e,0x1e,0x0b,0x14,0x14,0x14,0x0c,
  0x02,0x0d,0x24,0x1e,0x33,0x0e,0x01,0x01,0x01,0x17,0x0f,0x1e,0x1e,
  0x13,0x13,0x13,0x13,0x13,0x0c,0x03,0x1e,0x38,0x01,0x01,0x01,0x38,
  0x1e,0x06,0x13,0x13,0x1e,0x1e,0x1f,0x1f,0x22,0x1a,0x1e,0x1e,0x40,
  0x35,0x01,0x34,0x1e,0x18,0x0c,0x05,0x1f,0x02,0x1e,0x22,0x27,0x05,
  0x0b,0x15,0x1e,0x2c,0x01,0x01,0x00,0x1e,0x1e,0x19,0x13,0x05,0x0c,
  0x05,0x1f,0x0c,0x19,0x0b,0x15,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,
  0x38,0x1e,0x0d,0x1f,0x0c,0x05,0x05,0x31,0x1e,0x0f,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x21,0x27,0x05,0x0b,0x06,0x27,0x14,0x22,0x23,0x1e,
  0x1e,0x11,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x37,0x1e,0x15,0x02,
  0x1f,0x27,0x02,0x05,0x05,0x1f,0x19,0x22,0x26,0x1e,0x3a,0x01,0x01,
  0x35,0x1e,0x1e,0x0c,0x13,0x0b,0x19,0x22,0x1f,0x14,0x18,0x1e,0x34,
  0x01,0x01,0x01,0x01,0x39,0x1e,0x1a,0x14,0x22,0x1f,0x19,0x0b,0x26,
  0x0a,0x23,0x1a,0x27,0x14,0x0d,0x0d,0x1e,0x1e,0x00,0x01,0x01,0x2b,
  0x38,0x1e,0x28,0x14,0x0b,0x19,0x27,0x23,0x05,0x14,0x1a,0x1e,0x38,
  0x2b,0x01,0x01,0x01,0x35,0x1e,0x18,0x0b,0x1a,0x1a,0x0c,0x23,0x22,
  0x0a,0x1e,0x37,0x01,0x01,0x01,0x01,0x04,0x1e,0x29,0x13,0x0b,0x1f,
  0x1f,0x06,0x2d,0x16,0x31,0x1e,0x0f,0x01,0x01,0x36,0x1e,0x21,0x14,
  0x29,0x14,0x22,0x1e,0x18,0x1f,0x23,0x14,0x1f,0x2d,0x1e,0x35,0x01,
  0x01,0x01,0x20,0x1e,0x09,0x1f,0x05,0x13,0x1f,0x03,0x1e,0x17,0x01,
  0x01,0x32,0x1e,0x02,0x29,0x14,0x1f,0x22,0x14,0x12,0x31,0x1e,0x1e,
  0x3c,0x01,0x01,0x01,0x2c,0x1e,0x1e,0x1e,0x1e,0x1e,0x16,0x1a,0x13,
  0x13,0x22,0x13,0x21,0x1e,0x3c,0x01,0x00,0x1e,0x1e,0x13,0x1f,0x1f,
  0x27,0x02,0x0c,0x0c,0x13,0x1f,0x29,0x25,0x1e,0x2e,0x01,0x17,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x12,0x23,0x22,0x1f,0x0b,0x1e,0x1e,
  0x00,0x01,0x01,0x07,0x1e,0x14,0x02,0x0d,0x05,0x19,0x29,0x13,0x05,
  0x0b,0x14,0x18,0x1e,0x17,0x01,0x01,0x01,0x36,0x1e,0x18,0x0c,0x19,
  0x0c,0x1f,0x23,0x1e,0x33,0x01,0x01,0x01,0x01,0x20,0x1e,0x09,0x13,
  0x23,0x22,0x1f,0x03,0x15,0x29,0x14,0x22,0x29,0x1e,0x1e,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x3b,0x1e,0x25,0x22,0x1f,0x22,0x0c,0x14,
  0x27,0x0c,0x19,0x12,0x26,0x1e,0x3a,0x01,0x01,0x01,0x1e,0x1e,0x05,
  0x13,0x13,0x22,0x1f,0x1f,0x0b,0x1e,0x32,0x01,0x01,0x01,0x01,0x01,
  0x3a,0x1e,0x28,0x1a,0x0b,0x0b,0x12,0x02,0x0a,0x0a,0x22,0x23,0x22,
  0x14,0x14,0x06,0x1e,0x30,0x01,0x01,0x01,0x38,0x1e,0x1e,0x22,0x19,
  0x24,0x0d,0x03,0x12,0x1f,0x0b,0x22,0x1e,0x1e,0x38,0x01,0x01,0x01,
  0x01,0x39,0x1e,0x12,0x05,0x22,0x14,0x23,0x14,0x1e,0x1e,0x0e,0x01,
  0x01,0x01,0x01,0x07,0x1e,0x14,0x0b,0x02,0x13,0x05,0x0b,0x1f,0x19,
  0x27,0x1e,0x39,0x01,0x01,0x35,0x1e,0x2d,0x05,0x02,0x02,0x1f,0x1e,
  0x1e,0x1a,0x22,0x05,0x24,0x2d,0x1e,0x35,0x01,0x01,0x01,0x20,0x1e,
  0x09,0x0b,0x23,0x05,0x0c,0x03,0x1e,0x17,0x01,0x36,0x1e,0x21,0x1f,
  0x1f,0x05,0x15,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x32,0x00,0x01,0x20,
  0x1e,0x09,0x26,0x26,0x06,0x26,0x1e,0x1e,0x0b,0x13,0x0c,0x13,0x12,
  0x1e,0x1c,0x01,0x34,0x1e,0x18,0x0c,0x1f,0x13,0x05,0x22,0x0c,0x1f,
  0x1f,0x13,0x1f,0x0d,0x1e,0x04,0x01,0x0f,0x1e,0x26,0x02,0x0d,0x19,
  0x1e,0x1e,0x06,0x05,0x13,0x19,0x0d,0x1e,0x1e,0x00,0x01,0x00,0x1e,
  0x1e,0x14,0x0c,0x14,0x14,0x06,0x1e,0x27,0x12,0x14,0x23,0x15,0x1e,
  0x0f,0x01,0x01,0x01,0x04,0x1e,0x28,0x13,0x1f,0x13,0x13,0x28,0x1e,
  0x11,0x01,0x01,0x01,0x01,0x08,0x1e,0x25,0x0c,0x05,0x13,0x27,0x1e,
  0x1e,0x02,0x02,0x1f,0x1f,0x1e,0x1e,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x35,0x40,0x1e,0x0d,0x1f,0x19,0x05,0x27,0x22,0x1f,0x14,0x1f,
  0x0d,0x1e,0x2c,0x01,0x01,0x01,0x11,0x1e,0x1a,0x13,0x0c,0x0c,0x22,
  0x05,0x26,0x1e,0x30,0x01,0x01,0x01,0x01,0x01,0x3b,0x1e,0x25,0x19,
  0x05,0x14,0x1f,0x0b,0x16,0x21,0x22,0x29,0x19,0x22,0x23,0x06,0x1e,
  0x37,0x01,0x01,0x01,0x1e,0x1e,0x02,0x06,0x14,0x0b,0x1a,0x1e,0x02,
  0x02,0x23,0x14,0x0d,0x1e,0x1e,0x01,0x01,0x01,0x01,0x2e,0x1e,0x2a,
  0x23,0x02,0x0b,0x26,0x27,0x1e,0x32,0x01,0x01,0x01,0x01,0x00,0x1e,
  0x1e,0x13,0x13,0x22,0x13,0x14,0x0c,0x23,0x27,0x1f,0x1e,0x1e,0x00,
  0x01,0x34,0x1e,0x18,0x0b,0x14,0x0c,0x0b,0x24,0x06,0x1a,0x1a,0x1f,
  0x05,0x1e,0x1e,0x34,0x01,0x01,0x01,0x20,0x1e,0x09,0x05,0x13,0x0c,
  0x0b,0x03,0x1e,0x17,0x01,0x1b,0x1e,0x15,0x27,0x02,0x1f,0x0d,0x29,
  0x0b,0x23,0x22,0x02,0x03,0x1e,0x17,0x01,0x20,0x1e,0x09,0x05,0x1f,
  0x13,0x13,0x12,0x10,0x14,0x13,0x1f,0x13,0x06,0x1e,0x37,0x01,0x34,
  0x1e,0x1e,0x0c,0x1f,0x0c,0x13,0x13,0x0c,0x0c,0x13,0x0b,0x0b,0x24,
  0x1e,0x37,0x01,0x11,0x1e,0x02,0x0b,0x05,0x1f,0x02,0x10,0x1f,0x22,
  0x0c,0x23,0x29,0x1e,0x2c,0x01,0x01,0x01,0x3e,0x1e,0x23,0x29,0x22,
  0x0c,0x0a,0x1e,0x23,0x0b,0x22,0x1f,0x24,0x1e,0x37,0x01,0x01,0x2b,
  0x3e,0x1e,0x0b,0x0b,0x27,0x13,0x1f,0x1e,0x1e,0x2b,0x01,0x01,0x01,
  0x01,0x08,0x1e,0x25,0x1f,0x22,0x0c,0x23,0x25,0x24,0x14,0x0c,0x0b,
  0x02,0x1e,0x1e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x37,0x1e,
  0x1e,0x2d,0x25,0x21,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1b,0x01,
  0x01,0x01,0x3a,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x17,
  0x01,0x01,0x01,0x01,0x01,0x35,0x33,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x3c,0x01,0x01,0x01,
  0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x01,0x01,0x01,0x3d,0x1e,0x1e,0x1e,0x27,0x0b,0x1f,0x0c,
  0x21,0x1e,0x30,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x39,0x01,0x01,0x01,0x04,0x1e,
  0x12,0x0b,0x22,0x0b,0x22,0x05,0x23,0x14,0x22,0x06,0x1e,0x38,0x01,
  0x01,0x01,0x01,0x20,0x1e,0x18,0x02,0x05,0x0c,0x1f,0x31,0x1e,0x17,
  0x01,0x3a,0x1e,0x15,0x0b,0x22,0x1f,0x22,0x0c,0x14,0x27,0x0c,0x19,
  0x2a,0x1e,0x08,0x01,0x01,0x1e,0x1e,0x13,0x0b,0x13,0x13,0x13,0x14,
  0x1f,0x13,0x0c,0x13,0x03,0x1e,0x3c,0x01,0x01,0x38,0x1e,0x18,0x1e,
  0x1e,0x1e,0x21,0x13,0x1f,0x13,0x19,0x1e,0x1e,0x1e,0x2f,0x01,0x0f,
  0x1e,0x16,0x1f,0x1f,0x0c,0x13,0x0c,0x23,0x0b,0x05,0x29,0x2a,0x1e,
  0x1b,0x01,0x01,0x01,0x11,0x1e,0x19,0x05,0x1f,0x19,0x29,0x25,0x0d,
  0x19,0x13,0x0b,0x15,0x1e,0x1b,0x01,0x01,0x38,0x1e,0x2a,0x1f,0x13,
  0x1f,0x13,0x28,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x0e,0x1e,0x10,
  0x23,0x0b,0x05,0x29,0x02,0x1f,0x0b,0x22,0x0b,0x22,0x1e,0x1e,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x37,0x38,0x1e,0x1e,0x1e,
  0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x1b,0x01,0x01,0x01,0x01,0x01,0x3a,
  0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x17,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x38,0x3c,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,
  0x1e,0x1e,0x32,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x34,0x1e,0x18,0x12,0x0d,0x29,0x02,0x23,0x02,0x1e,0x1e,0x01,0x01,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x33,0x39,0x35,0x01,0x01,0x01,0x08,0x1e,0x1e,0x15,0x14,0x0d,
  0x02,0x1f,0x02,0x23,0x26,0x1e,0x1e,0x1b,0x01,0x01,0x01,0x01,0x0e,
  0x1e,0x10,0x02,0x13,0x14,0x23,0x0a,0x1e,0x3b,0x01,0x30,0x1e,0x12,
  0x06,0x19,0x1f,0x19,0x05,0x14,0x22,0x1f,0x14,0x28,0x1e,0x1b,0x01,
  0x01,0x38,0x1e,0x1e,0x02,0x13,0x13,0x13,0x13,0x22,0x13,0x13,0x25,
  0x1e,0x32,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x03,0x1f,
  0x14,0x13,0x0c,0x1e,0x1e,0x3d,0x01,0x01,0x20,0x07,0x1e,0x03,0x13,
  0x05,0x05,0x0c,0x05,0x05,0x0c,0x31,0x1e,0x3e,0x34,0x01,0x01,0x01,
  0x37,0x1e,0x0a,0x27,0x0c,0x19,0x12,0x13,0x05,0x22,0x14,0x14,0x1e,
  0x1e,0x36,0x01,0x01,0x1e,0x1e,0x05,0x13,0x0c,0x14,0x13,0x1e,0x1e,
  0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x2d,0x05,0x0c,0x0c,
  0x22,0x02,0x23,0x14,0x12,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x2e,0x2b,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x1e,0x18,0x0c,
  0x14,0x05,0x22,0x02,0x21,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x08,0x1e,0x1e,0x1e,0x2d,0x25,0x0a,0x21,0x1e,
  0x1e,0x1e,0x1b,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x33,0x0e,0x01,0x3b,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x2b,0x01,0x01,0x2b,0x38,0x1e,
  0x1e,0x1e,0x2a,0x25,0x16,0x16,0x09,0x1e,0x1e,0x40,0x2b,0x01,0x01,
  0x01,0x01,0x01,0x34,0x34,0x34,0x32,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x01,0x01,0x01,0x01,0x2b,0x32,0x1e,0x1e,0x18,0x25,0x16,0x16,
  0x09,0x1e,0x1e,0x1d,0x2b,0x01,0x01,0x01,0x01,0x2b,0x1e,0x1e,0x0a,
  0x1f,0x05,0x0c,0x1f,0x0b,0x1f,0x0b,0x2d,0x1e,0x38,0x01,0x01,0x01,
  0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x03,0x25,0x0a,0x10,0x1e,
  0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x34,0x1e,0x18,0x0b,0x19,0x19,0x1a,0x31,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x17,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2f,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x01,0x3b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x30,0x1d,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x1e,0x1e,0x1e,0x2d,0x25,0x16,
  0x28,0x18,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x1c,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x04,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1d,0x2b,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x0e,0x08,0x2e,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x2f,0x08,0x08,0x3c,0x20,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x20,0x08,0x08,0x08,0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,
  0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,
  0x2f,0x3b,0x2e,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x30,
  0x32,0x1e,0x33,0x33,0x39,0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x35,0x08,0x3c,0x1b,0x36,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x20,0x3c,0x2b,0x17,0x20,0x01,0x01,0x01,0x01,0x01,0x0e,
  0x36,0x36,0x17,0x20,0x01,0x01,0x01,0x01,0x01,0x3c,0x37,0x3d,0x1c,
  0x1c,0x2c,0x1b,0x0e,0x01,0x01,0x01,0x01,0x35,0x0e,0x20,0x35,0x20,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x17,0x30,0x1d,0x30,0x3a,
  0x17,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x17,0x2b,0x2f,0x20,
  0x01,0x01,0x01,0x01,0x01,0x00,0x34,0x00,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x0e,0x3c,0x3c,0x34,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x36,0x38,0x1e,0x38,
  0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x35,0x35,0x34,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x3b,
  0x37,0x3d,0x3b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x00,0x35,0x35,0x20,0x0e,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,
  0x20,0x35,0x35,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
  0x3d,0x32,0x2c,0x1b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x34,0x3c,0x1c,0x38,0x30,0x3b,0x35,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x34,0x35,0x35,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x08,0x30,0x08,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x37,0x38,0x07,
  0x1e,0x07,0x38,0x1c,0x2f,0x01,0x01,0x01,0x01,0x01,0x2c,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x2c,0x01,0x01,0x01,0x11,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x04,0x01,0x01,0x01,0x04,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x33,
  0x0e,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x39,0x0e,0x01,0x01,
  0x01,0x0e,0x11,0x33,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x01,0x04,0x1e,0x1e,0x1e,0x1e,0x1e,0x04,0x01,0x01,0x01,
  0x3d,0x1e,0x1e,0x1e,0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x11,
  0x1e,0x1e,0x1e,0x07,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x36,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,
  0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x08,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x04,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x17,0x01,0x01,0x01,0x01,0x01,0x00,0x11,0x1e,0x1e,0x1e,0x1e,
  0x1b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2f,0x38,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x32,0x30,0x01,0x01,0x01,0x01,0x01,
  0x01,0x30,0x1e,0x1d,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x3d,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x3d,0x1e,0x1e,0x1e,0x30,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x35,0x39,0x1e,0x1e,0x1e,0x1e,0x18,0x1e,0x1e,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x2b,0x1e,0x09,0x2a,0x21,0x03,0x09,0x1e,
  0x36,0x01,0x0f,0x1e,0x10,0x31,0x31,0x03,0x18,0x1e,0x35,0x01,0x34,
  0x1e,0x18,0x24,0x29,0x06,0x24,0x0d,0x25,0x1e,0x30,0x01,0x38,0x1e,
  0x09,0x10,0x09,0x2d,0x09,0x1e,0x39,0x01,0x01,0x01,0x11,0x1e,0x1e,
  0x03,0x26,0x0b,0x24,0x26,0x03,0x1e,0x1e,0x38,0x20,0x01,0x20,0x1e,
  0x18,0x03,0x21,0x2a,0x18,0x1e,0x2b,0x01,0x30,0x1e,0x1e,0x18,0x1e,
  0x1e,0x1b,0x01,0x01,0x01,0x01,0x01,0x2c,0x1e,0x10,0x2a,0x16,0x1e,
  0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x04,0x1e,0x26,0x13,0x10,0x1e,0x17,0x01,0x01,0x01,0x0e,0x38,0x1e,
  0x18,0x2d,0x2d,0x18,0x1e,0x38,0x34,0x01,0x01,0x01,0x01,0x01,0x01,
  0x1c,0x1e,0x1e,0x1e,0x25,0x06,0x29,0x10,0x1e,0x1e,0x38,0x2b,0x01,
  0x01,0x01,0x01,0x38,0x1e,0x1e,0x2d,0x09,0x09,0x10,0x18,0x1e,0x0f,
  0x01,0x35,0x40,0x1e,0x21,0x09,0x09,0x2d,0x18,0x1e,0x1e,0x17,0x01,
  0x01,0x01,0x3d,0x1e,0x1e,0x10,0x22,0x27,0x1e,0x1e,0x38,0x2b,0x01,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x16,0x15,0x02,0x29,
  0x2a,0x2d,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x00,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x3a,0x1e,0x1e,0x2d,0x09,0x21,
  0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x36,0x04,0x1e,0x1e,0x29,0x1e,
  0x1e,0x04,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x39,
  0x1e,0x1e,0x28,0x23,0x05,0x13,0x05,0x23,0x24,0x18,0x1e,0x38,0x01,
  0x01,0x1b,0x1e,0x15,0x13,0x13,0x13,0x26,0x1e,0x3a,0x01,0x39,0x1e,
  0x02,0x1f,0x1f,0x13,0x25,0x1e,0x3b,0x01,0x0e,0x1e,0x2d,0x1f,0x13,
  0x05,0x1f,0x0c,0x0b,0x1e,0x32,0x01,0x1e,0x1e,0x13,0x13,0x13,0x13,
  0x0c,0x1e,0x1e,0x00,0x01,0x3a,0x1e,0x18,0x0c,0x13,0x13,0x13,0x13,
  0x13,0x13,0x13,0x1e,0x1e,0x2c,0x01,0x08,0x1e,0x25,0x13,0x13,0x13,
  0x15,0x1e,0x1b,0x01,0x1d,0x1e,0x0b,0x13,0x27,0x1e,0x2c,0x01,0x01,
  0x01,0x01,0x01,0x07,0x1e,0x14,0x0c,0x13,0x1e,0x1e,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x1e,0x1e,0x05,0x13,
  0x06,0x1e,0x0f,0x01,0x01,0x01,0x11,0x1e,0x1e,0x13,0x13,0x13,0x13,
  0x1e,0x1e,0x04,0x01,0x01,0x01,0x01,0x01,0x1c,0x1e,0x1e,0x29,0x1f,
  0x1f,0x13,0x05,0x14,0x13,0x2d,0x1e,0x38,0x2b,0x01,0x01,0x30,0x1e,
  0x10,0x14,0x13,0x05,0x14,0x13,0x02,0x1e,0x39,0x01,0x3b,0x1e,0x0a,
  0x13,0x13,0x13,0x13,0x13,0x06,0x1e,0x11,0x01,0x01,0x0e,0x1e,0x1e,
  0x1e,0x31,0x13,0x13,0x1e,0x18,0x1e,0x38,0x01,0x01,0x01,0x01,0x2b,
  0x38,0x1e,0x1e,0x06,0x13,0x1f,0x1f,0x13,0x05,0x14,0x13,0x19,0x18,
  0x1e,0x38,0x00,0x01,0x01,0x2b,0x1e,0x21,0x0b,0x13,0x2d,0x1e,0x1c,
  0x01,0x01,0x01,0x39,0x1e,0x02,0x13,0x13,0x13,0x10,0x1e,0x0e,0x01,
  0x01,0x01,0x08,0x1e,0x1e,0x1e,0x15,0x13,0x28,0x1e,0x1e,0x1e,0x08,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x33,0x1e,0x0c,0x13,0x13,
  0x13,0x13,0x13,0x13,0x13,0x13,0x09,0x1e,0x1b,0x01,0x0f,0x1e,0x26,
  0x13,0x1f,0x0c,0x26,0x1e,0x0f,0x01,0x1d,0x1e,0x23,0x0c,0x13,0x0b,
  0x0a,0x1e,0x2e,0x01,0x35,0x1e,0x2d,0x13,0x1f,0x1f,0x13,0x22,0x27,
  0x1e,0x38,0x01,0x38,0x1e,0x26,0x13,0x13,0x13,0x12,0x1e,0x39,0x01,
  0x01,0x39,0x1e,0x27,0x13,0x13,0x13,0x05,0x13,0x13,0x13,0x13,0x05,
  0x1e,0x07,0x01,0x08,0x1e,0x25,0x13,0x13,0x13,0x15,0x1e,0x1b,0x01,
  0x32,0x1e,0x22,0x13,0x02,0x1e,0x39,0x01,0x01,0x01,0x20,0x08,0x2c,
  0x1e,0x12,0x0c,0x0c,0x1e,0x1e,0x2e,0x0e,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x2c,0x1e,0x0a,0x14,0x13,0x1e,0x1e,0x17,0x01,
  0x01,0x1b,0x1e,0x1e,0x0c,0x13,0x13,0x13,0x13,0x14,0x1e,0x1e,0x2b,
  0x01,0x01,0x01,0x2b,0x1e,0x1e,0x1a,0x1f,0x19,0x31,0x1e,0x1e,0x2a,
  0x05,0x22,0x10,0x1e,0x38,0x01,0x01,0x32,0x1e,0x23,0x13,0x1f,0x14,
  0x13,0x14,0x0d,0x1e,0x2c,0x01,0x3c,0x1e,0x2a,0x13,0x13,0x13,0x13,
  0x13,0x0c,0x09,0x1e,0x20,0x01,0x3a,0x1e,0x28,0x13,0x12,0x13,0x14,
  0x12,0x13,0x2d,0x1e,0x17,0x01,0x01,0x01,0x38,0x1e,0x1e,0x13,0x0b,
  0x1f,0x0b,0x29,0x0a,0x24,0x0b,0x1f,0x14,0x13,0x2d,0x1e,0x3d,0x01,
  0x01,0x01,0x38,0x1e,0x19,0x13,0x0b,0x1e,0x3e,0x34,0x01,0x01,0x3a,
  0x1e,0x26,0x13,0x13,0x13,0x09,0x1e,0x20,0x01,0x01,0x08,0x1e,0x1e,
  0x15,0x05,0x13,0x13,0x13,0x14,0x28,0x1e,0x1e,0x08,0x01,0x01,0x01,
  0x01,0x01,0x01,0x3a,0x1e,0x25,0x13,0x13,0x13,0x13,0x02,0x1f,0x13,
  0x13,0x13,0x27,0x1e,0x2c,0x01,0x1b,0x1e,0x15,0x13,0x1f,0x0c,0x28,
  0x1e,0x3a,0x01,0x11,0x1e,0x19,0x1f,0x13,0x13,0x0a,0x1e,0x3b,0x01,
  0x20,0x1e,0x09,0x02,0x05,0x19,0x19,0x05,0x29,0x1e,0x30,0x01,0x37,
  0x1e,0x15,0x14,0x13,0x13,0x12,0x1e,0x3d,0x01,0x01,0x33,0x1e,0x0c,
  0x13,0x13,0x13,0x1e,0x1a,0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x08,
  0x1e,0x25,0x13,0x13,0x13,0x15,0x1e,0x1b,0x01,0x38,0x1e,0x23,0x13,
  0x1a,0x1e,0x04,0x01,0x01,0x04,0x1e,0x1e,0x1e,0x1e,0x12,0x0c,0x0c,
  0x1e,0x1e,0x1e,0x1e,0x11,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,
  0x1e,0x1e,0x02,0x12,0x2d,0x1e,0x38,0x01,0x01,0x08,0x1e,0x1e,0x27,
  0x13,0x13,0x24,0x23,0x13,0x13,0x15,0x1e,0x33,0x35,0x01,0x01,0x38,
  0x1e,0x28,0x0c,0x24,0x16,0x1a,0x0c,0x29,0x03,0x1e,0x22,0x1f,0x1e,
  0x1e,0x2f,0x01,0x32,0x1e,0x12,0x14,0x0c,0x27,0x22,0x19,0x0a,0x1e,
  0x1b,0x01,0x0e,0x1e,0x10,0x19,0x14,0x13,0x0b,0x14,0x0c,0x10,0x1e,
  0x0e,0x01,0x39,0x1e,0x27,0x13,0x13,0x13,0x13,0x13,0x13,0x26,0x1e,
  0x0f,0x01,0x01,0x1b,0x1e,0x1e,0x0c,0x13,0x1f,0x16,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x0b,0x0c,0x0b,0x1e,0x32,0x01,0x01,0x01,0x37,0x1e,
  0x1e,0x27,0x13,0x2a,0x1e,0x3c,0x01,0x01,0x1c,0x1e,0x12,0x13,0x13,
  0x13,0x2d,0x1e,0x35,0x01,0x01,0x38,0x1e,0x12,0x13,0x13,0x13,0x13,
  0x13,0x13,0x13,0x29,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x39,
  0x1e,0x1a,0x13,0x13,0x13,0x14,0x1e,0x12,0x13,0x13,0x13,0x22,0x1e,
  0x32,0x01,0x3d,0x1e,0x0d,0x13,0x13,0x05,0x0d,0x1e,0x2c,0x01,0x1e,
  0x1e,0x1f,0x14,0x13,0x0c,0x15,0x1e,0x1b,0x01,0x01,0x04,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x3b,0x01,0x0f,0x1e,0x06,0x1f,0x19,
  0x05,0x28,0x1e,0x1b,0x01,0x01,0x39,0x1e,0x15,0x27,0x27,0x28,0x1e,
  0x22,0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x3c,0x1e,0x16,0x13,0x0c,
  0x13,0x0a,0x1e,0x3b,0x01,0x40,0x1e,0x1f,0x13,0x23,0x1e,0x38,0x01,
  0x20,0x1e,0x18,0x16,0x0a,0x10,0x23,0x13,0x05,0x18,0x25,0x0a,0x10,
  0x1e,0x3c,0x01,0x01,0x01,0x01,0x01,0x01,0x39,0x1e,0x28,0x0b,0x14,
  0x1e,0x07,0x2b,0x01,0x01,0x30,0x1e,0x29,0x13,0x13,0x05,0x1e,0x1e,
  0x13,0x13,0x13,0x0a,0x1e,0x3b,0x01,0x01,0x1e,0x1e,0x0c,0x19,0x1e,
  0x19,0x0c,0x23,0x05,0x13,0x06,0x1e,0x0c,0x15,0x1e,0x39,0x01,0x1d,
  0x1e,0x27,0x02,0x22,0x27,0x26,0x1e,0x1e,0x33,0x35,0x01,0x01,0x11,
  0x1e,0x1e,0x2a,0x05,0x02,0x24,0x0b,0x2d,0x1e,0x35,0x01,0x3a,0x1e,
  0x21,0x29,0x13,0x13,0x13,0x05,0x0a,0x10,0x1e,0x17,0x01,0x01,0x38,
  0x1e,0x12,0x0c,0x0c,0x1e,0x1e,0x25,0x27,0x1a,0x31,0x25,0x31,0x09,
  0x13,0x27,0x31,0x1e,0x3c,0x01,0x01,0x01,0x37,0x1e,0x1e,0x05,0x24,
  0x1e,0x37,0x01,0x01,0x2e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x34,
  0x01,0x0e,0x1e,0x1e,0x13,0x13,0x13,0x13,0x0b,0x13,0x13,0x13,0x13,
  0x31,0x1e,0x36,0x01,0x01,0x01,0x01,0x01,0x11,0x1e,0x15,0x22,0x13,
  0x13,0x19,0x1e,0x06,0x13,0x13,0x13,0x0b,0x1e,0x1d,0x01,0x3b,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1b,0x01,0x11,0x1e,0x1e,0x0d,0x0c,
  0x1f,0x10,0x1e,0x36,0x01,0x01,0x01,0x37,0x11,0x1e,0x1d,0x1d,0x1e,
  0x38,0x3b,0x01,0x01,0x2f,0x1e,0x03,0x1f,0x05,0x22,0x2a,0x1e,0x2f,
  0x01,0x01,0x36,0x1e,0x1e,0x1e,0x1e,0x1e,0x13,0x13,0x13,0x13,0x13,
  0x15,0x1e,0x38,0x01,0x20,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x35,
  0x01,0x39,0x1e,0x24,0x14,0x15,0x1e,0x1c,0x01,0x17,0x1e,0x03,0x22,
  0x13,0x1f,0x0c,0x05,0x05,0x13,0x0c,0x0c,0x12,0x1e,0x1c,0x01,0x01,
  0x01,0x01,0x01,0x36,0x1e,0x1e,0x1f,0x02,0x21,0x1e,0x04,0x01,0x01,
  0x01,0x3a,0x1e,0x26,0x12,0x29,0x1e,0x1e,0x1e,0x1e,0x0d,0x1a,0x15,
  0x1e,0x1b,0x01,0x35,0x1e,0x2d,0x0c,0x15,0x1e,0x0d,0x0c,0x18,0x29,
  0x1f,0x0c,0x1e,0x05,0x22,0x1e,0x33,0x01,0x1d,0x1e,0x29,0x14,0x0b,
  0x19,0x0d,0x1e,0x2c,0x35,0x01,0x01,0x01,0x01,0x30,0x1e,0x0a,0x22,
  0x0b,0x0c,0x0b,0x2d,0x1e,0x35,0x01,0x01,0x38,0x1e,0x2a,0x13,0x13,
  0x13,0x1f,0x1e,0x1e,0x11,0x01,0x01,0x2b,0x1e,0x21,0x1f,0x0c,0x10,
  0x1e,0x27,0x23,0x0c,0x05,0x02,0x0c,0x13,0x1e,0x26,0x1f,0x24,0x1e,
  0x1c,0x01,0x01,0x01,0x01,0x04,0x1e,0x1e,0x1e,0x1e,0x3c,0x01,0x01,
  0x0e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x3b,0x1e,0x0a,
  0x13,0x13,0x13,0x0c,0x1e,0x1a,0x13,0x13,0x13,0x15,0x1e,0x1b,0x01,
  0x01,0x01,0x01,0x01,0x37,0x1e,0x06,0x0c,0x19,0x0b,0x22,0x10,0x27,
  0x13,0x13,0x13,0x0b,0x1e,0x1d,0x01,0x01,0x3b,0x38,0x1e,0x1e,0x1e,
  0x38,0x1b,0x01,0x01,0x3c,0x1e,0x1e,0x14,0x1f,0x0d,0x1e,0x38,0x01,
  0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x01,0x00,0x01,0x01,0x01,0x01,
  0x36,0x1e,0x31,0x0d,0x1a,0x0c,0x0a,0x1e,0x1b,0x01,0x01,0x01,0x36,
  0x3a,0x33,0x1e,0x05,0x13,0x13,0x13,0x13,0x16,0x1e,0x1e,0x2b,0x01,
  0x2f,0x1e,0x2a,0x1f,0x14,0x13,0x25,0x1e,0x08,0x01,0x3c,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x36,0x01,0x17,0x1e,0x03,0x13,0x0c,0x19,0x0c,0x1f,
  0x23,0x13,0x0c,0x1f,0x24,0x1e,0x37,0x01,0x01,0x01,0x01,0x01,0x38,
  0x1e,0x28,0x22,0x27,0x1e,0x1d,0x20,0x01,0x01,0x01,0x17,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x04,0x11,0x1e,0x1e,0x1e,0x1e,0x1e,0x36,0x01,0x36,
  0x1e,0x31,0x05,0x31,0x1e,0x27,0x1f,0x19,0x13,0x22,0x2a,0x1e,0x0d,
  0x02,0x1e,0x1e,0x01,0x1d,0x1e,0x12,0x29,0x23,0x1a,0x12,0x1e,0x39,
  0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x25,0x0c,0x1a,0x0b,0x05,0x2d,
  0x1e,0x35,0x01,0x20,0x1e,0x09,0x13,0x13,0x28,0x14,0x13,0x05,0x1e,
  0x07,0x01,0x01,0x2e,0x1e,0x0a,0x13,0x1f,0x1e,0x26,0x13,0x05,0x25,
  0x03,0x13,0x05,0x06,0x1e,0x25,0x02,0x0d,0x1e,0x04,0x01,0x01,0x01,
  0x01,0x34,0x04,0x07,0x04,0x3c,0x01,0x01,0x01,0x2e,0x1e,0x0a,0x14,
  0x0b,0x13,0x18,0x1e,0x34,0x01,0x0f,0x1e,0x06,0x13,0x13,0x13,0x13,
  0x1e,0x1e,0x21,0x09,0x10,0x18,0x1e,0x36,0x01,0x01,0x01,0x01,0x01,
  0x3c,0x1e,0x1e,0x13,0x05,0x0b,0x14,0x0c,0x0c,0x02,0x22,0x0c,0x0b,
  0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x1c,0x1e,0x12,0x27,0x0c,0x21,0x1e,0x37,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x1e,0x10,0x1f,
  0x1a,0x19,0x21,0x1e,0x36,0x01,0x01,0x01,0x01,0x34,0x1e,0x18,0x29,
  0x0d,0x1a,0x22,0x2a,0x1e,0x07,0x2b,0x01,0x01,0x3b,0x1e,0x25,0x0c,
  0x14,0x13,0x15,0x1e,0x1b,0x01,0x01,0x3c,0x04,0x3e,0x04,0x36,0x01,
  0x01,0x00,0x1e,0x1e,0x10,0x10,0x1e,0x02,0x13,0x13,0x1e,0x10,0x10,
  0x18,0x1e,0x3c,0x01,0x01,0x01,0x01,0x17,0x1e,0x18,0x27,0x14,0x03,
  0x1e,0x37,0x01,0x01,0x01,0x01,0x01,0x17,0x3a,0x30,0x3d,0x2e,0x01,
  0x01,0x3b,0x3d,0x04,0x1b,0x36,0x01,0x01,0x2b,0x1e,0x21,0x05,0x16,
  0x1e,0x02,0x13,0x05,0x05,0x27,0x1e,0x1e,0x27,0x0c,0x1e,0x1e,0x01,
  0x1d,0x1e,0x02,0x22,0x23,0x22,0x1a,0x1e,0x39,0x01,0x01,0x01,0x01,
  0x01,0x2e,0x1e,0x25,0x19,0x22,0x0c,0x27,0x2d,0x1e,0x35,0x01,0x01,
  0x38,0x1e,0x0a,0x02,0x1e,0x10,0x14,0x31,0x1e,0x2c,0x01,0x01,0x37,
  0x1e,0x06,0x0c,0x15,0x1e,0x13,0x0c,0x14,0x1e,0x1e,0x1f,0x13,0x15,
  0x1e,0x12,0x13,0x12,0x1e,0x30,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x1c,0x1e,0x12,0x02,0x22,0x0c,0x18,0x1e,
  0x20,0x01,0x0f,0x1e,0x06,0x13,0x13,0x13,0x13,0x18,0x1e,0x10,0x18,
  0x2d,0x18,0x1e,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,
  0x0a,0x12,0x29,0x06,0x23,0x14,0x22,0x0c,0x0b,0x1e,0x1d,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3c,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x34,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x20,0x01,0x01,0x01,0x01,0x01,0x3d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x3e,0x2b,0x01,0x01,0x01,0x3c,0x1e,0x16,0x0c,0x23,0x13,0x28,0x1e,
  0x2e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x30,0x1e,
  0x1e,0x1e,0x1e,0x0d,0x1f,0x1f,0x1e,0x1e,0x1e,0x1e,0x04,0x01,0x01,
  0x01,0x01,0x01,0x38,0x1e,0x26,0x14,0x06,0x1e,0x32,0x00,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x20,0x1e,0x18,0x23,0x0d,0x1e,0x19,0x1f,0x0d,
  0x0c,0x05,0x1e,0x1e,0x0b,0x02,0x1e,0x3e,0x01,0x1d,0x1e,0x24,0x0b,
  0x14,0x22,0x29,0x1e,0x39,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x0a,
  0x23,0x0b,0x1f,0x05,0x2d,0x1e,0x35,0x01,0x01,0x2b,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1d,0x20,0x01,0x01,0x1c,0x1e,0x26,0x0c,0x21,
  0x1e,0x13,0x0c,0x28,0x1e,0x1e,0x13,0x1f,0x03,0x1e,0x02,0x14,0x16,
  0x1e,0x08,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x1b,0x1e,0x15,0x14,0x22,0x0c,0x10,0x1e,0x2b,0x01,0x3b,0x1e,
  0x0a,0x13,0x13,0x13,0x0c,0x1e,0x26,0x13,0x13,0x13,0x0a,0x1e,0x3b,
  0x01,0x01,0x01,0x01,0x01,0x2f,0x1e,0x31,0x15,0x15,0x29,0x28,0x1e,
  0x28,0x0d,0x1f,0x22,0x0b,0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x3c,0x38,0x1e,0x1e,0x38,0x2b,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x2b,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x3c,0x01,0x01,0x01,
  0x01,0x00,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x01,0x01,0x01,
  0x01,0x0f,0x1e,0x06,0x05,0x14,0x13,0x06,0x1e,0x30,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x0e,0x2c,0x1e,0x1a,
  0x1f,0x13,0x1e,0x1e,0x0e,0x34,0x01,0x01,0x01,0x01,0x01,0x08,0x1e,
  0x1e,0x02,0x05,0x21,0x1e,0x3a,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x40,0x1e,0x1f,0x0c,0x1e,0x06,0x1f,0x09,0x19,0x0b,0x16,0x25,
  0x22,0x03,0x1e,0x3d,0x01,0x1d,0x1e,0x02,0x14,0x0d,0x22,0x0d,0x1e,
  0x39,0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x25,0x0d,0x19,0x05,0x12,
  0x2d,0x1e,0x35,0x01,0x01,0x01,0x2b,0x1c,0x1d,0x1c,0x3d,0x3e,0x3d,
  0x00,0x01,0x01,0x01,0x1c,0x1e,0x12,0x13,0x16,0x1e,0x0c,0x23,0x2a,
  0x1e,0x0d,0x05,0x19,0x1e,0x16,0x05,0x0c,0x1e,0x1e,0x20,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x12,
  0x0d,0x1f,0x22,0x28,0x1e,0x2e,0x01,0x35,0x1e,0x18,0x13,0x1f,0x0c,
  0x14,0x14,0x22,0x13,0x13,0x13,0x21,0x1e,0x2b,0x01,0x01,0x01,0x01,
  0x01,0x0f,0x1e,0x06,0x1a,0x19,0x13,0x23,0x15,0x23,0x0b,0x05,0x1f,
  0x22,0x1e,0x32,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x17,0x1e,0x31,
  0x02,0x1f,0x27,0x25,0x1e,0x2e,0x01,0x01,0x01,0x01,0x00,0x1e,0x1e,
  0x0b,0x0b,0x05,0x1f,0x1e,0x1e,0x01,0x01,0x01,0x01,0x17,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x08,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x40,0x1e,0x05,0x05,0x13,0x1e,0x1e,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x06,0x22,0x02,0x1e,
  0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x39,0x1e,0x10,
  0x22,0x1f,0x09,0x1e,0x1e,0x1e,0x1e,0x29,0x05,0x0d,0x1e,0x32,0x00,
  0x01,0x1d,0x1e,0x29,0x02,0x29,0x27,0x06,0x1e,0x38,0x3b,0x01,0x01,
  0x01,0x2b,0x38,0x1e,0x03,0x29,0x27,0x0c,0x22,0x2d,0x1e,0x35,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x0f,0x1e,0x15,0x1f,0x0d,0x1e,0x22,0x1f,0x05,0x23,0x13,0x0c,0x19,
  0x02,0x1f,0x02,0x2a,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x39,0x1e,0x27,0x0b,0x05,0x1f,0x15,
  0x1e,0x1b,0x01,0x01,0x38,0x1e,0x0d,0x1f,0x0c,0x23,0x13,0x13,0x13,
  0x13,0x29,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x2f,0x1e,0x1e,
  0x05,0x22,0x14,0x14,0x13,0x1a,0x19,0x13,0x0b,0x15,0x1e,0x3d,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x17,0x1e,0x31,0x22,0x1f,0x14,0x25,
  0x1e,0x08,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x12,0x19,0x13,0x0b,
  0x1e,0x1e,0x01,0x01,0x01,0x01,0x01,0x17,0x38,0x1e,0x1e,0x1e,0x38,
  0x08,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2c,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,
  0x01,0x01,0x1b,0x1e,0x1e,0x29,0x02,0x2d,0x1e,0x37,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x38,0x1e,0x03,0x19,0x14,0x0d,
  0x16,0x25,0x0b,0x1f,0x14,0x1e,0x1e,0x30,0x01,0x01,0x1d,0x1e,0x27,
  0x14,0x0b,0x1f,0x0b,0x2d,0x1e,0x1e,0x3b,0x01,0x00,0x32,0x1e,0x1e,
  0x12,0x14,0x23,0x14,0x1a,0x2d,0x1e,0x0e,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x17,0x1e,0x31,0x0c,
  0x02,0x1e,0x21,0x23,0x13,0x14,0x1a,0x23,0x1f,0x1f,0x14,0x09,0x1e,
  0x3e,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x06,0x19,0x13,0x0b,0x16,0x1e,0x38,0x01,0x01,
  0x2e,0x1e,0x1e,0x28,0x14,0x14,0x13,0x0b,0x0c,0x0a,0x1e,0x1e,0x3b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x2d,0x1f,0x0b,0x24,
  0x13,0x05,0x23,0x22,0x16,0x1e,0x1e,0x36,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x3c,0x1e,0x2a,0x19,0x1f,0x0b,0x26,0x1e,0x0f,0x01,0x01,
  0x01,0x01,0x34,0x1e,0x18,0x05,0x23,0x22,0x22,0x1e,0x1e,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x2c,
  0x40,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x1d,0x1e,
  0x1a,0x0c,0x0d,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x2b,0x32,0x1e,0x1e,0x1a,0x02,0x13,0x23,0x19,0x0a,
  0x1e,0x1e,0x39,0x01,0x01,0x01,0x38,0x1e,0x12,0x19,0x23,0x0b,0x19,
  0x0b,0x24,0x1e,0x30,0x01,0x17,0x1e,0x31,0x27,0x19,0x1a,0x19,0x14,
  0x0b,0x09,0x1e,0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x1d,0x1e,0x1a,0x23,0x13,0x2d,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x02,0x09,0x1e,0x1c,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x1e,0x2d,
  0x05,0x23,0x22,0x22,0x13,0x1e,0x1e,0x01,0x01,0x01,0x2e,0x1e,0x1e,
  0x1e,0x03,0x13,0x31,0x1e,0x1e,0x33,0x3b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x16,0x25,0x25,0x18,0x1e,
  0x1e,0x07,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x07,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x2f,0x01,0x01,0x01,0x01,0x01,0x38,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x36,0x1e,0x31,0x22,0x19,0x18,0x1e,
  0x3a,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x33,0x39,0x0e,0x01,
  0x01,0x01,0x30,0x1e,0x29,0x19,0x23,0x22,0x29,0x12,0x24,0x1e,0x2c,
  0x01,0x3c,0x1e,0x16,0x19,0x02,0x29,0x0a,0x0d,0x0d,0x1e,0x32,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x3a,0x1e,0x1e,0x1a,0x13,0x14,0x0b,0x03,0x1e,0x1e,0x0a,
  0x02,0x13,0x14,0x19,0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x2b,0x11,0x1e,0x2d,0x19,0x09,
  0x1e,0x39,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,
  0x38,0x2f,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x1e,0x1e,0x22,0x27,0x1e,0x1d,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3a,0x38,
  0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x3b,0x1e,
  0x1e,0x25,0x29,0x12,0x29,0x19,0x0a,0x1e,0x1b,0x01,0x0e,0x1e,0x10,
  0x27,0x06,0x12,0x27,0x24,0x1e,0x1e,0x3d,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x39,
  0x1e,0x1e,0x19,0x19,0x05,0x23,0x24,0x05,0x22,0x0c,0x13,0x29,0x1e,
  0x1e,0x1c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,
  0x01,0x01,0x01,0x01,0x01,0x39,0x1e,0x1e,0x1e,0x2c,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x20,
  0x3c,0x08,0x3b,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,
  0x1e,0x1e,0x1e,0x1e,0x30,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3b,0x33,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x2b,0x01,0x01,0x11,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x3d,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x39,0x1e,0x1e,0x1e,
  0x28,0x02,0x02,0x05,0x0b,0x15,0x1e,0x1e,0x1e,0x1c,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x0f,0x11,0x0f,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1d,0x30,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x35,0x3a,0x2c,0x04,0x3d,0x1d,0x30,0x2b,
  0x01,0x01,0x01,0x20,0x2c,0x40,0x2c,0x04,0x39,0x1c,0x08,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x37,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x38,0x0f,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x36,0x39,0x33,0x1e,0x1e,0x07,0x04,0x36,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x0e,0x37,0x38,0x3e,0x1d,0x32,0x04,0x2e,0x00,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2e,0x04,0x32,
  0x1d,0x1d,0x1c,0x3c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3b,0x3d,
  0x0f,0x37,0x37,0x2f,0x34,0x3b,0x30,0x0f,0x30,0x0f,0x2f,0x01,0x01,
  0x01,0x01,0x01,0x34,0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x00,0x2f,0x1c,0x38,0x1c,0x1b,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x34,0x34,0x01,0x34,0x34,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x2f,0x37,0x30,0x3b,0x35,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x08,0x1c,0x3a,0x17,0x00,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x1b,0x2c,0x3d,0x2e,0x2b,0x1b,0x30,0x2c,
  0x1b,0x01,0x01,0x01,0x01,0x01,0x00,0x17,0x3c,0x3c,0x3c,0x3c,0x3c,
  0x3c,0x3c,0x08,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x2f,0x0f,0x08,0x35,0x01,0x01,0x01,0x01,0x01,0x2b,0x2e,0x3a,0x3b,
  0x2e,0x2e,0x2e,0x1b,0x1b,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x0e,0x0e,0x00,0x01,0x35,0x0e,0x34,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3b,0x3d,0x3b,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x00,0x34,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x3d,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x32,0x17,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x36,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x2b,0x01,0x01,0x01,0x01,0x39,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x3e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x2f,0x01,0x01,0x2b,0x38,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x00,0x3d,0x3e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,
  0x3e,0x1e,0x1e,0x04,0x34,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x38,0x17,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x34,
  0x04,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1b,0x01,0x01,
  0x01,0x35,0x32,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x40,
  0x2b,0x01,0x01,0x01,0x2c,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x1e,0x38,0x1e,0x1e,0x1e,0x04,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2b,0x1d,0x1e,0x1e,0x1e,0x3d,0x20,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x3a,0x39,0x07,0x3e,0x3d,0x35,0x01,0x01,0x17,
  0x38,0x1e,0x1e,0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x3a,0x39,0x32,0x1e,0x33,0x38,0x1c,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3d,0x1e,0x1e,0x28,0x23,0x22,
  0x23,0x22,0x0d,0x03,0x1e,0x1e,0x17,0x01,0x01,0x01,0x01,0x01,0x36,
  0x1e,0x1e,0x10,0x29,0x19,0x23,0x23,0x12,0x18,0x1e,0x40,0x2b,0x01,
  0x01,0x35,0x1e,0x2d,0x0d,0x06,0x24,0x24,0x21,0x1e,0x2a,0x29,0x06,
  0x29,0x06,0x1e,0x38,0x01,0x01,0x38,0x1e,0x1e,0x18,0x1e,0x32,0x00,
  0x01,0x01,0x01,0x3d,0x1e,0x1e,0x2a,0x25,0x23,0x12,0x15,0x1e,0x1e,
  0x33,0x2b,0x01,0x01,0x00,0x32,0x1e,0x18,0x18,0x1e,0x18,0x18,0x1e,
  0x04,0x01,0x01,0x01,0x2b,0x1d,0x1e,0x1e,0x31,0x06,0x29,0x25,0x18,
  0x1e,0x1e,0x0f,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x10,0x12,0x26,
  0x03,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x04,0x1e,0x1e,0x1a,0x29,
  0x28,0x1e,0x28,0x24,0x1a,0x1e,0x38,0x01,0x01,0x01,0x39,0x1e,0x03,
  0x2a,0x16,0x2a,0x03,0x2a,0x16,0x16,0x25,0x21,0x1e,0x3a,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x1e,0x31,0x06,0x25,0x1e,0x38,0x01,0x01,0x20,
  0x1e,0x09,0x28,0x26,0x0a,0x0a,0x0a,0x25,0x28,0x15,0x1e,0x38,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1d,0x1e,0x10,0x10,0x1e,0x1e,
  0x2d,0x10,0x18,0x1e,0x3c,0x01,0x01,0x01,0x20,0x2c,0x40,0x1e,0x1e,
  0x03,0x0d,0x18,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x20,0x39,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x33,0x2b,0x17,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x18,
  0x1e,0x1e,0x1e,0x1e,0x3a,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x00,0x3e,0x1e,0x14,0x13,0x13,0x13,0x13,0x0c,0x0c,0x0c,
  0x26,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x26,0x1f,0x14,
  0x13,0x14,0x14,0x13,0x05,0x16,0x1e,0x38,0x01,0x01,0x01,0x1e,0x1e,
  0x13,0x13,0x13,0x13,0x27,0x1e,0x0b,0x13,0x13,0x13,0x13,0x1e,0x1e,
  0x01,0x0e,0x1e,0x10,0x13,0x13,0x03,0x1e,0x17,0x01,0x01,0x08,0x1e,
  0x1e,0x14,0x13,0x13,0x13,0x13,0x0c,0x22,0x0a,0x1e,0x38,0x01,0x01,
  0x2f,0x1e,0x2a,0x0c,0x22,0x0b,0x13,0x05,0x1e,0x07,0x01,0x01,0x2b,
  0x3e,0x1e,0x31,0x13,0x13,0x13,0x13,0x13,0x13,0x26,0x1e,0x1e,0x0f,
  0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,0x0c,0x22,0x1f,0x09,0x1e,
  0x2e,0x01,0x01,0x2f,0x1e,0x1e,0x02,0x0c,0x14,0x0a,0x16,0x0c,0x22,
  0x0b,0x1e,0x1e,0x01,0x01,0x34,0x1e,0x18,0x05,0x05,0x0c,0x05,0x1f,
  0x0c,0x19,0x0b,0x14,0x26,0x1e,0x39,0x01,0x01,0x01,0x20,0x1e,0x18,
  0x13,0x13,0x13,0x13,0x1e,0x1e,0x00,0x01,0x2f,0x1e,0x03,0x05,0x13,
  0x13,0x27,0x0c,0x0c,0x13,0x13,0x1e,0x1e,0x01,0x01,0x01,0x01,0x01,
  0x01,0x34,0x3a,0x1e,0x31,0x1f,0x14,0x0a,0x18,0x14,0x1f,0x24,0x1e,
  0x1c,0x01,0x01,0x01,0x2c,0x1e,0x1e,0x21,0x16,0x22,0x13,0x24,0x21,
  0x09,0x1e,0x1d,0x2b,0x01,0x01,0x01,0x2c,0x1e,0x2d,0x02,0x05,0x14,
  0x0a,0x1e,0x38,0x38,0x1e,0x26,0x13,0x25,0x1e,0x08,0x01,0x01,0x01,
  0x01,0x01,0x00,0x32,0x1e,0x21,0x27,0x22,0x13,0x0c,0x23,0x28,0x1e,
  0x1e,0x3a,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x08,0x1e,
  0x03,0x13,0x13,0x13,0x1f,0x0d,0x19,0x13,0x0c,0x14,0x21,0x1e,0x2b,
  0x01,0x01,0x01,0x17,0x1e,0x10,0x14,0x0c,0x14,0x0c,0x14,0x0c,0x1f,
  0x23,0x13,0x10,0x1e,0x0e,0x01,0x01,0x38,0x1e,0x0a,0x14,0x13,0x13,
  0x1a,0x1e,0x14,0x13,0x13,0x13,0x1e,0x1e,0x38,0x01,0x0e,0x1e,0x10,
  0x13,0x13,0x2a,0x1e,0x2f,0x01,0x01,0x30,0x1e,0x29,0x13,0x13,0x13,
  0x23,0x0c,0x0c,0x13,0x13,0x21,0x1e,0x36,0x01,0x2b,0x1e,0x10,0x13,
  0x05,0x26,0x13,0x02,0x1e,0x11,0x01,0x01,0x38,0x1e,0x2a,0x13,0x13,
  0x03,0x1e,0x1e,0x21,0x02,0x13,0x19,0x1e,0x3e,0x2b,0x01,0x0e,0x1e,
  0x10,0x13,0x23,0x02,0x29,0x13,0x13,0x29,0x1e,0x04,0x01,0x01,0x38,
  0x1e,0x06,0x05,0x1f,0x24,0x18,0x05,0x27,0x14,0x1e,0x1e,0x38,0x01,
  0x01,0x34,0x1e,0x18,0x27,0x0d,0x05,0x0b,0x02,0x13,0x05,0x0b,0x14,
  0x1a,0x1e,0x2c,0x01,0x01,0x01,0x08,0x1e,0x25,0x13,0x13,0x13,0x0b,
  0x1e,0x1d,0x01,0x01,0x08,0x1e,0x16,0x13,0x13,0x13,0x0c,0x1f,0x1f,
  0x13,0x13,0x1e,0x1e,0x00,0x01,0x01,0x01,0x01,0x2b,0x3e,0x1e,0x1e,
  0x27,0x19,0x27,0x1e,0x06,0x1a,0x02,0x1e,0x1e,0x1d,0x01,0x01,0x1c,
  0x1e,0x1e,0x22,0x02,0x14,0x13,0x0c,0x23,0x14,0x13,0x03,0x1e,0x38,
  0x01,0x01,0x01,0x40,0x1e,0x1f,0x13,0x13,0x13,0x13,0x21,0x1e,0x1e,
  0x09,0x13,0x13,0x1e,0x1e,0x35,0x01,0x01,0x01,0x01,0x01,0x1b,0x1e,
  0x03,0x13,0x13,0x13,0x13,0x13,0x0c,0x1f,0x1a,0x1e,0x38,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x30,0x1e,0x29,0x13,0x13,0x13,
  0x26,0x1e,0x29,0x13,0x0c,0x13,0x2a,0x1e,0x2f,0x01,0x01,0x35,0x33,
  0x1e,0x26,0x13,0x0c,0x1f,0x14,0x1e,0x29,0x19,0x1a,0x14,0x03,0x1e,
  0x2f,0x01,0x01,0x38,0x1e,0x10,0x24,0x14,0x13,0x23,0x25,0x14,0x13,
  0x13,0x1f,0x18,0x1e,0x38,0x01,0x35,0x1e,0x2d,0x0c,0x13,0x03,0x1e,
  0x17,0x01,0x01,0x04,0x1e,0x1a,0x13,0x13,0x1f,0x1e,0x10,0x1f,0x0b,
  0x13,0x15,0x1e,0x1b,0x01,0x00,0x1e,0x1e,0x03,0x2a,0x09,0x2a,0x31,
  0x1e,0x0f,0x01,0x36,0x1e,0x18,0x13,0x0b,0x1e,0x03,0x1f,0x13,0x12,
  0x1e,0x1a,0x13,0x16,0x1e,0x38,0x01,0x2b,0x07,0x1e,0x24,0x06,0x1a,
  0x19,0x14,0x13,0x29,0x1e,0x3d,0x01,0x01,0x1e,0x1e,0x1f,0x1f,0x13,
  0x2d,0x16,0x13,0x23,0x1a,0x1e,0x3e,0x2b,0x01,0x01,0x01,0x39,0x1e,
  0x10,0x21,0x10,0x10,0x10,0x2d,0x2d,0x23,0x23,0x12,0x1e,0x39,0x01,
  0x01,0x01,0x3c,0x1e,0x16,0x13,0x13,0x27,0x1e,0x1e,0x1c,0x01,0x01,
  0x20,0x07,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,
  0x01,0x01,0x01,0x01,0x2b,0x1e,0x1e,0x2a,0x16,0x0c,0x1f,0x02,0x2d,
  0x02,0x13,0x0d,0x16,0x31,0x1e,0x08,0x01,0x1d,0x1e,0x19,0x23,0x13,
  0x0b,0x27,0x05,0x19,0x12,0x14,0x13,0x09,0x1e,0x35,0x01,0x0e,0x1e,
  0x10,0x13,0x13,0x1e,0x13,0x13,0x1a,0x1e,0x1e,0x05,0x13,0x21,0x1e,
  0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x2c,0x1e,0x27,0x13,0x13,0x13,
  0x25,0x2a,0x27,0x02,0x1f,0x21,0x1e,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x3b,0x1e,0x2d,0x13,0x13,0x13,0x0a,0x1e,0x1e,0x25,
  0x25,0x25,0x18,0x1e,0x34,0x01,0x01,0x04,0x1e,0x25,0x27,0x0c,0x13,
  0x1f,0x1f,0x0a,0x02,0x23,0x12,0x21,0x1e,0x32,0x00,0x01,0x34,0x1e,
  0x18,0x0b,0x0c,0x14,0x27,0x1f,0x02,0x26,0x22,0x13,0x13,0x1f,0x1e,
  0x1e,0x01,0x35,0x1e,0x2d,0x13,0x13,0x03,0x1e,0x17,0x01,0x01,0x1b,
  0x1e,0x15,0x13,0x13,0x13,0x06,0x2a,0x16,0x2d,0x1e,0x18,0x1e,0x2b,
  0x01,0x01,0x3d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1d,0x01,0x01,0x0f,
  0x1e,0x28,0x05,0x1e,0x03,0x14,0x13,0x05,0x05,0x05,0x1e,0x0c,0x13,
  0x1e,0x1e,0x01,0x38,0x1e,0x16,0x13,0x0c,0x14,0x27,0x13,0x1f,0x24,
  0x1e,0x30,0x01,0x01,0x38,0x1e,0x21,0x1f,0x0c,0x14,0x1e,0x02,0x05,
  0x13,0x03,0x1e,0x38,0x01,0x01,0x01,0x35,0x39,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x02,0x1f,0x1a,0x1e,0x2c,0x01,0x01,0x01,0x3c,0x1e,
  0x16,0x13,0x13,0x2a,0x1e,0x3d,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,
  0x3d,0x1e,0x28,0x1f,0x1f,0x1f,0x12,0x23,0x1f,0x0d,0x05,0x14,0x0b,
  0x06,0x1e,0x1c,0x01,0x1e,0x1e,0x22,0x19,0x05,0x23,0x26,0x24,0x02,
  0x0c,0x1f,0x19,0x25,0x1e,0x08,0x01,0x0e,0x1e,0x10,0x13,0x13,0x1e,
  0x13,0x13,0x0d,0x1e,0x15,0x13,0x1a,0x1e,0x38,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x11,0x1e,0x19,0x13,0x13,0x13,0x10,0x09,0x0c,0x1f,
  0x0c,0x31,0x1e,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,
  0x1e,0x0d,0x13,0x13,0x14,0x23,0x15,0x2d,0x1e,0x1e,0x1e,0x1e,0x04,
  0x01,0x01,0x34,0x1e,0x18,0x19,0x13,0x05,0x23,0x0c,0x14,0x0d,0x0b,
  0x22,0x24,0x1e,0x38,0x0e,0x01,0x01,0x01,0x38,0x1e,0x28,0x16,0x13,
  0x05,0x0c,0x05,0x1f,0x0c,0x0d,0x25,0x25,0x1e,0x38,0x01,0x35,0x1e,
  0x2d,0x0c,0x13,0x03,0x1e,0x17,0x01,0x01,0x17,0x1e,0x1e,0x05,0x13,
  0x0c,0x0c,0x13,0x23,0x27,0x10,0x1e,0x1d,0x01,0x01,0x01,0x01,0x00,
  0x17,0x2f,0x20,0x3c,0x36,0x01,0x01,0x01,0x11,0x1e,0x27,0x0b,0x1e,
  0x0b,0x14,0x2d,0x1e,0x0d,0x26,0x1e,0x26,0x13,0x1e,0x1e,0x01,0x1e,
  0x1e,0x13,0x0c,0x0c,0x29,0x2a,0x0c,0x13,0x06,0x1e,0x11,0x01,0x01,
  0x2b,0x38,0x1e,0x28,0x1f,0x02,0x12,0x21,0x22,0x0c,0x23,0x18,0x1e,
  0x34,0x01,0x01,0x01,0x01,0x0e,0x2b,0x0e,0x0e,0x0e,0x30,0x1e,0x18,
  0x2d,0x1e,0x1e,0x1b,0x01,0x01,0x00,0x3d,0x1e,0x31,0x13,0x13,0x03,
  0x1e,0x17,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x32,0x1e,0x0b,0x1f,
  0x05,0x1f,0x14,0x1f,0x22,0x22,0x22,0x28,0x12,0x1e,0x1e,0x08,0x01,
  0x1e,0x1e,0x23,0x12,0x1f,0x15,0x25,0x1a,0x0a,0x21,0x09,0x2a,0x09,
  0x1e,0x35,0x01,0x0e,0x1e,0x10,0x0c,0x13,0x2d,0x1f,0x13,0x12,0x1e,
  0x13,0x13,0x1e,0x1e,0x1e,0x38,0x0f,0x01,0x01,0x01,0x01,0x01,0x3d,
  0x1e,0x0d,0x13,0x13,0x13,0x13,0x1f,0x1f,0x0c,0x19,0x1e,0x1e,0x1e,
  0x2c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,
  0x13,0x0c,0x02,0x02,0x1e,0x40,0x1e,0x38,0x08,0x01,0x01,0x01,0x04,
  0x1e,0x2a,0x05,0x13,0x05,0x1f,0x1f,0x25,0x12,0x06,0x09,0x1e,0x3b,
  0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x29,0x0d,0x05,0x0b,0x02,
  0x13,0x2a,0x1e,0x1e,0x1e,0x38,0x01,0x36,0x1e,0x31,0x13,0x13,0x25,
  0x1e,0x08,0x01,0x01,0x38,0x1e,0x26,0x14,0x13,0x1f,0x0c,0x05,0x05,
  0x13,0x05,0x03,0x1e,0x1b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x38,0x1e,0x12,0x19,0x09,0x0b,0x13,0x09,0x1e,
  0x1e,0x1e,0x1e,0x16,0x0b,0x1e,0x1e,0x01,0x38,0x1e,0x24,0x1f,0x0c,
  0x05,0x05,0x13,0x05,0x0c,0x1e,0x1e,0x00,0x01,0x01,0x36,0x1e,0x1e,
  0x18,0x18,0x09,0x1e,0x1e,0x18,0x09,0x1e,0x38,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x1e,0x1e,0x3d,0x01,
  0x01,0x00,0x3e,0x1e,0x1e,0x1a,0x13,0x13,0x25,0x1e,0x08,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x39,0x1e,0x1e,0x10,0x1f,0x23,0x28,0x1e,
  0x22,0x23,0x06,0x18,0x10,0x1e,0x04,0x01,0x01,0x32,0x1e,0x23,0x19,
  0x23,0x23,0x02,0x0b,0x29,0x24,0x25,0x1e,0x1e,0x2c,0x01,0x01,0x01,
  0x07,0x1e,0x27,0x0b,0x14,0x06,0x1f,0x1e,0x19,0x13,0x10,0x1e,0x03,
  0x10,0x1e,0x1e,0x3d,0x01,0x01,0x01,0x01,0x2e,0x1e,0x1e,0x05,0x13,
  0x14,0x22,0x05,0x05,0x0a,0x1e,0x1e,0x1e,0x09,0x1e,0x35,0x01,0x01,
  0x01,0x01,0x01,0x01,0x38,0x1e,0x25,0x22,0x1f,0x1f,0x23,0x31,0x18,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x08,0x01,0x20,0x1e,0x18,0x0b,0x13,0x1f,
  0x1f,0x13,0x22,0x14,0x0c,0x26,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x34,
  0x1e,0x18,0x22,0x1f,0x0c,0x14,0x14,0x05,0x0c,0x19,0x12,0x14,0x23,
  0x1e,0x1e,0x01,0x34,0x1e,0x18,0x0a,0x29,0x09,0x1e,0x35,0x01,0x2b,
  0x1e,0x21,0x13,0x13,0x0c,0x27,0x2d,0x28,0x22,0x13,0x1f,0x1a,0x1e,
  0x2c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x1d,0x1e,0x02,0x02,0x1e,0x0c,0x02,0x1e,0x1e,0x03,0x28,0x1e,0x16,
  0x0b,0x1e,0x1e,0x01,0x3c,0x1e,0x1e,0x24,0x27,0x24,0x21,0x25,0x26,
  0x0a,0x1e,0x1e,0x34,0x01,0x01,0x01,0x36,0x38,0x1e,0x1e,0x1e,0x38,
  0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x34,0x35,0x00,0x01,0x01,0x01,0x2f,0x1e,0x2a,
  0x22,0x13,0x13,0x13,0x03,0x1e,0x17,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x3a,0x1e,0x2d,0x31,0x2a,0x1f,0x05,0x28,0x31,0x29,0x05,0x06,0x10,
  0x1e,0x38,0x34,0x01,0x01,0x30,0x1e,0x1e,0x12,0x02,0x28,0x06,0x14,
  0x23,0x02,0x0b,0x19,0x1e,0x1e,0x36,0x01,0x01,0x04,0x1e,0x18,0x12,
  0x27,0x27,0x31,0x10,0x22,0x29,0x1e,0x13,0x13,0x13,0x02,0x18,0x1e,
  0x17,0x01,0x01,0x2b,0x3e,0x1e,0x09,0x1f,0x13,0x0c,0x0c,0x27,0x27,
  0x18,0x0a,0x23,0x1f,0x25,0x1e,0x3b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x2b,0x40,0x1e,0x24,0x1f,0x13,0x16,0x1e,0x09,0x22,0x14,0x13,0x12,
  0x1e,0x30,0x01,0x34,0x1e,0x18,0x1e,0x27,0x0c,0x1f,0x0c,0x22,0x1e,
  0x29,0x1a,0x26,0x06,0x21,0x1e,0x2b,0x01,0x01,0x38,0x1e,0x0a,0x2a,
  0x31,0x19,0x0c,0x0b,0x1f,0x1a,0x2a,0x16,0x2a,0x1e,0x38,0x01,0x01,
  0x04,0x1e,0x1e,0x1e,0x1e,0x39,0x01,0x01,0x08,0x1e,0x25,0x1f,0x1f,
  0x13,0x22,0x1e,0x2d,0x13,0x0c,0x19,0x0d,0x1e,0x04,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x29,0x1f,
  0x1e,0x02,0x0c,0x24,0x31,0x05,0x0c,0x1e,0x28,0x14,0x1e,0x1e,0x01,
  0x01,0x1b,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x3d,0x01,
  0x01,0x01,0x01,0x01,0x01,0x34,0x34,0x20,0x01,0x00,0x34,0x35,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x37,0x1e,0x24,0x22,0x13,0x13,0x1a,
  0x1e,0x32,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1a,0x05,
  0x1f,0x0c,0x0d,0x0b,0x05,0x0d,0x0b,0x14,0x05,0x1e,0x1e,0x34,0x01,
  0x01,0x3d,0x1e,0x1e,0x1e,0x09,0x28,0x19,0x26,0x06,0x02,0x26,0x29,
  0x15,0x1e,0x1b,0x01,0x01,0x34,0x04,0x1e,0x1e,0x1e,0x1e,0x1e,0x14,
  0x22,0x1e,0x19,0x13,0x19,0x0b,0x13,0x26,0x1e,0x0f,0x01,0x01,0x38,
  0x1e,0x2a,0x13,0x13,0x1f,0x14,0x1f,0x0c,0x1f,0x1f,0x0c,0x1f,0x12,
  0x1e,0x40,0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x0c,
  0x1f,0x0c,0x06,0x18,0x19,0x0c,0x13,0x0b,0x12,0x1e,0x04,0x01,0x01,
  0x04,0x1e,0x1e,0x15,0x0b,0x05,0x1f,0x27,0x03,0x1f,0x22,0x0c,0x23,
  0x21,0x1e,0x36,0x01,0x01,0x2b,0x33,0x1e,0x1e,0x1e,0x19,0x19,0x22,
  0x13,0x15,0x1e,0x1e,0x1e,0x3e,0x2b,0x01,0x20,0x1e,0x18,0x26,0x1a,
  0x2d,0x1e,0x35,0x01,0x2b,0x1e,0x21,0x1f,0x13,0x05,0x22,0x27,0x05,
  0x1f,0x13,0x1f,0x26,0x1e,0x3a,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2e,0x1e,0x25,0x27,0x16,0x1e,0x29,0x22,
  0x0c,0x0b,0x15,0x1e,0x0b,0x02,0x1e,0x32,0x01,0x01,0x01,0x1b,0x04,
  0x39,0x1c,0x3c,0x08,0x0f,0x3b,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2f,0x1e,0x03,0x05,0x13,0x13,0x13,0x21,0x1e,0x2b,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x20,0x1e,0x09,0x14,0x05,0x22,0x1f,0x22,0x0c,
  0x1f,0x05,0x0c,0x29,0x24,0x1e,0x38,0x01,0x01,0x01,0x32,0x1e,0x06,
  0x1a,0x24,0x24,0x26,0x26,0x24,0x23,0x14,0x14,0x28,0x1e,0x37,0x01,
  0x01,0x01,0x01,0x2e,0x04,0x07,0x1e,0x02,0x0d,0x03,0x1e,0x13,0x13,
  0x16,0x27,0x0c,0x12,0x1e,0x1c,0x01,0x01,0x1e,0x1e,0x13,0x13,0x0c,
  0x22,0x18,0x23,0x19,0x0b,0x05,0x14,0x15,0x1e,0x1e,0x3a,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x13,0x05,0x05,0x13,0x05,0x0c,
  0x13,0x1f,0x13,0x13,0x28,0x1e,0x1b,0x01,0x01,0x01,0x36,0x1e,0x1e,
  0x1f,0x1f,0x0c,0x13,0x0c,0x23,0x0b,0x05,0x29,0x1e,0x40,0x01,0x01,
  0x01,0x01,0x0e,0x3b,0x32,0x1e,0x0b,0x19,0x12,0x13,0x12,0x1e,0x30,
  0x3c,0x34,0x01,0x01,0x0e,0x1e,0x10,0x13,0x1f,0x2a,0x1e,0x2f,0x01,
  0x01,0x38,0x1e,0x15,0x0c,0x13,0x13,0x0c,0x0c,0x13,0x23,0x31,0x1e,
  0x1e,0x17,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x35,0x40,0x1e,0x12,0x02,0x03,0x1e,0x03,0x16,0x1e,0x1e,0x1a,
  0x19,0x1e,0x1e,0x30,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x32,
  0x1e,0x1e,0x12,0x14,0x13,0x16,0x1e,0x08,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x00,0x1e,0x1e,0x1e,0x16,0x14,0x19,0x2d,0x10,0x14,0x23,0x0a,0x1e,
  0x1e,0x1e,0x3a,0x01,0x01,0x01,0x1e,0x1e,0x23,0x14,0x29,0x27,0x19,
  0x26,0x29,0x23,0x12,0x22,0x28,0x1e,0x0f,0x01,0x01,0x01,0x01,0x2b,
  0x38,0x1e,0x16,0x1f,0x02,0x1e,0x2d,0x14,0x13,0x03,0x02,0x14,0x24,
  0x1e,0x37,0x01,0x34,0x1e,0x18,0x13,0x22,0x0c,0x14,0x18,0x24,0x0c,
  0x0c,0x1f,0x0c,0x19,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x28,0x1f,0x23,0x23,0x05,0x0c,0x1f,0x05,0x13,0x14,
  0x1e,0x1e,0x2b,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x05,0x05,0x05,
  0x0c,0x05,0x05,0x02,0x1e,0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x01,
  0x1e,0x1e,0x1f,0x05,0x0c,0x1f,0x27,0x1e,0x32,0x01,0x01,0x01,0x01,
  0x35,0x1e,0x2d,0x13,0x0c,0x03,0x1e,0x17,0x01,0x01,0x2f,0x1e,0x1e,
  0x1e,0x1e,0x26,0x13,0x1f,0x13,0x13,0x12,0x1e,0x38,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x37,0x1e,
  0x1e,0x05,0x14,0x0d,0x15,0x31,0x26,0x13,0x05,0x2d,0x1e,0x38,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x39,0x1e,0x03,0x0c,
  0x13,0x2a,0x1e,0x2f,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3d,0x1e,0x1e,
  0x24,0x05,0x0c,0x1e,0x0a,0x02,0x23,0x1e,0x1e,0x38,0x3a,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x03,0x14,0x15,0x28,0x1f,0x02,0x02,0x02,0x19,
  0x05,0x2d,0x1e,0x17,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1f,0x1a,
  0x1e,0x1e,0x2d,0x23,0x13,0x24,0x23,0x1f,0x06,0x1e,0x37,0x01,0x01,
  0x3e,0x1e,0x22,0x0c,0x14,0x0b,0x14,0x14,0x14,0x0c,0x05,0x05,0x13,
  0x0c,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x1e,0x1e,
  0x2d,0x1e,0x1e,0x1e,0x1e,0x16,0x28,0x31,0x1e,0x1e,0x2c,0x01,0x01,
  0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x28,0x16,0x03,0x1e,0x1e,
  0x1e,0x2c,0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x30,0x01,0x01,0x01,0x01,0x35,0x1e,0x2d,0x0c,
  0x13,0x03,0x1e,0x17,0x01,0x01,0x3d,0x1e,0x26,0x1a,0x27,0x26,0x1e,
  0x19,0x13,0x0c,0x0c,0x21,0x1e,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x39,0x1e,0x1e,0x28,0x22,
  0x1f,0x05,0x0c,0x27,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x3c,0x1e,0x2a,0x05,0x13,0x03,0x1e,0x04,
  0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x1e,0x21,0x02,0x13,0x1a,0x1e,
  0x0c,0x23,0x02,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x3e,
  0x1e,0x0a,0x14,0x0b,0x27,0x29,0x0b,0x06,0x28,0x2d,0x1e,0x38,0x01,
  0x01,0x01,0x01,0x01,0x1e,0x1e,0x24,0x05,0x03,0x1e,0x38,0x1e,0x29,
  0x13,0x0b,0x13,0x22,0x2d,0x1e,0x2f,0x01,0x01,0x3d,0x1e,0x18,0x19,
  0x05,0x0b,0x0b,0x05,0x27,0x28,0x22,0x14,0x1f,0x05,0x0c,0x2d,0x1e,
  0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x3e,0x1d,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x40,0x2c,0x20,0x01,0x01,0x01,0x01,0x01,0x01,
  0x20,0x2c,0x40,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x37,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x32,0x30,
  0x01,0x01,0x01,0x01,0x01,0x35,0x1e,0x2d,0x13,0x1f,0x03,0x1e,0x17,
  0x01,0x01,0x32,0x1e,0x0b,0x13,0x14,0x0c,0x10,0x0b,0x0c,0x1f,0x0c,
  0x31,0x1e,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x35,0x39,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x3c,0x1e,0x2a,0x13,0x13,0x12,0x1e,0x1e,0x04,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x37,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x33,0x1e,0x1e,0x1e,
  0x0a,0x0d,0x10,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x38,0x1e,0x28,0x0a,0x1e,0x32,0x2e,0x1e,0x1e,0x21,0x28,0x16,0x1e,
  0x1e,0x38,0x01,0x01,0x01,0x00,0x38,0x1e,0x1e,0x31,0x28,0x25,0x10,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x35,0x01,0x01,0x01,0x34,0x3c,0x1b,0x36,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,
  0x1b,0x08,0x2f,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x35,0x1e,0x2d,0x0c,0x0c,0x03,0x1e,0x17,0x01,0x01,0x3d,0x1e,
  0x28,0x0c,0x05,0x13,0x13,0x14,0x1f,0x19,0x05,0x1e,0x40,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x3a,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x08,0x1e,0x16,
  0x1f,0x13,0x13,0x22,0x1e,0x07,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,
  0x38,0x1e,0x1e,0x38,0x38,0x1e,0x1e,0x38,0x37,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x21,0x28,0x18,0x1e,
  0x1d,0x30,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x33,0x1e,0x1e,
  0x33,0x2b,0x01,0x2e,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x36,0x1e,0x31,
  0x13,0x22,0x25,0x1e,0x08,0x01,0x01,0x2f,0x1e,0x1e,0x2a,0x05,0x13,
  0x13,0x27,0x0c,0x0d,0x1e,0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x1e,0x10,0x05,0x13,0x13,0x0c,
  0x1e,0x1e,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x04,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x2e,0x2e,0x0e,0x01,0x01,0x01,
  0x01,0x2b,0x2e,0x08,0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x36,0x2e,0x08,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x40,
  0x35,0x01,0x01,0x01,0x2f,0x3e,0x1e,0x1e,0x1e,0x03,0x1e,0x1e,0x1e,
  0x1e,0x2c,0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x38,0x1e,0x1e,0x06,0x27,0x15,0x1e,0x39,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x3c,0x37,0x3c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,
  0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2e,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,
  0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x36,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x00,0x17,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2f,0x0f,0x2c,
  0x1b,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x1b,0x1b,0x2e,
  0x2e,0x2e,0x2e,0x2e,0x2e,0x1b,0x17,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x17,0x00,0x01,0x01,0x01,
  0x01,0x01,0x34,0x17,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x08,0x1c,0x1d,0x3d,0x3a,
  0x17,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,
  0x1e,0x38,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x2b,0x38,0x1e,0x1e,0x38,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x1c,0x38,0x1e,0x38,0x1e,0x1e,0x1d,0x30,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x38,
  0x3a,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x34,0x04,0x3e,0x38,0x37,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x08,0x38,0x1e,0x40,0x38,0x1e,0x1d,0x1c,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3a,0x2c,0x3e,0x3d,0x2e,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x00,0x35,0x01,0x34,0x20,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x08,0x11,0x33,0x11,0x17,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x0f,0x38,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x3d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x32,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x2b,0x39,0x1e,0x1e,0x1e,0x30,0x01,0x01,0x01,0x04,0x1e,0x1e,
  0x1e,0x11,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,
  0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x30,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x35,0x33,0x1e,0x1e,0x1e,0x1e,0x30,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x32,0x1e,0x1e,0x1e,
  0x1e,0x39,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x08,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1c,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x3a,0x1e,0x1e,0x1e,0x1e,0x1e,0x2e,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x30,0x1e,0x3e,0x3d,0x3b,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x3d,
  0x32,0x1e,0x2c,0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x38,0x1e,0x1e,0x38,0x08,0x01,0x01,0x01,0x01,0x01,0x3d,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x17,0x01,0x01,0x01,0x01,0x01,0x0f,0x1e,
  0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
  0x1e,0x1e,0x28,0x15,0x28,0x28,0x28,0x28,0x28,0x28,0x15,0x03,0x1e,
  0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3c,0x38,0x1e,0x1e,0x1e,
  0x03,0x1e,0x1e,0x00,0x01,0x34,0x1e,0x18,0x31,0x18,0x1e,0x1e,0x11,
  0x3c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x1e,0x1e,0x09,
  0x25,0x26,0x0b,0x29,0x26,0x21,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,
  0x01,0x20,0x1e,0x09,0x13,0x13,0x10,0x13,0x13,0x21,0x1e,0x2b,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x27,0x13,0x13,
  0x28,0x1e,0x2e,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x23,0x13,
  0x26,0x13,0x13,0x0b,0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x3b,0x1e,0x0a,0x13,0x02,0x1e,0x1e,0x36,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x38,0x1e,0x03,0x14,0x23,0x2d,0x1e,0x3a,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x30,0x1e,0x29,0x13,0x05,0x12,0x13,
  0x0b,0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x1e,
  0x1e,0x27,0x14,0x0d,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x1b,0x1e,0x1e,0x1e,0x1e,0x1e,0x3b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x40,0x1e,0x1e,0x1e,0x1e,0x2c,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x2d,0x1e,0x18,
  0x09,0x1e,0x1e,0x08,0x01,0x01,0x01,0x00,0x1e,0x1e,0x29,0x0c,0x26,
  0x1e,0x38,0x00,0x01,0x01,0x01,0x01,0x38,0x1e,0x19,0x13,0x1e,0x1e,
  0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x1e,0x2d,0x13,0x13,
  0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x14,0x1e,0x3e,0x01,0x01,0x01,
  0x01,0x20,0x1b,0x38,0x1e,0x1e,0x09,0x15,0x22,0x13,0x2d,0x1e,0x35,
  0x01,0x17,0x1e,0x03,0x19,0x1f,0x28,0x2d,0x1e,0x1e,0x38,0x3a,0x00,
  0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x28,0x13,0x13,0x13,0x13,0x13,
  0x13,0x13,0x13,0x1e,0x1e,0x3c,0x01,0x01,0x01,0x01,0x34,0x1e,0x18,
  0x05,0x13,0x18,0x13,0x13,0x2d,0x1e,0x35,0x01,0x01,0x01,0x01,0x01,
  0x01,0x34,0x1e,0x1e,0x13,0x13,0x26,0x05,0x13,0x2a,0x1e,0x2f,0x01,
  0x01,0x01,0x01,0x01,0x01,0x1c,0x1e,0x12,0x13,0x2a,0x19,0x13,0x29,
  0x1e,0x30,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x1e,0x18,0x13,
  0x13,0x15,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3b,
  0x1e,0x1e,0x13,0x13,0x13,0x27,0x1e,0x07,0x20,0x01,0x01,0x01,0x01,
  0x01,0x01,0x1b,0x1e,0x15,0x13,0x1a,0x26,0x13,0x12,0x1e,0x1c,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x28,0x13,0x13,0x13,
  0x09,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2c,0x1e,0x1a,
  0x14,0x29,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2c,0x1e,0x25,0x0b,0x22,0x1e,0x40,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x21,0x13,0x13,0x29,0x1e,0x30,
  0x01,0x01,0x01,0x01,0x3d,0x1e,0x03,0x13,0x13,0x09,0x1e,0x3d,0x01,
  0x01,0x01,0x34,0x1e,0x18,0x13,0x13,0x21,0x1e,0x38,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x20,0x1e,0x18,0x05,0x13,0x0c,0x13,0x13,0x13,
  0x13,0x13,0x13,0x27,0x1e,0x2c,0x01,0x01,0x01,0x37,0x40,0x1e,0x1e,
  0x21,0x24,0x13,0x0c,0x13,0x1f,0x18,0x1e,0x34,0x01,0x0e,0x1e,0x10,
  0x1f,0x1f,0x12,0x23,0x24,0x21,0x1e,0x1e,0x3e,0x0f,0x01,0x01,0x01,
  0x2f,0x1e,0x09,0x14,0x13,0x0c,0x23,0x23,0x0c,0x13,0x13,0x13,0x24,
  0x1e,0x37,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x21,0x28,0x1e,0x31,
  0x25,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x3d,0x1e,0x1e,0x1e,
  0x31,0x25,0x2d,0x03,0x16,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,
  0x01,0x1c,0x1e,0x18,0x26,0x18,0x21,0x28,0x18,0x1e,0x04,0x34,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x13,0x13,0x09,0x1e,
  0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x30,0x1e,0x29,0x13,0x12,
  0x27,0x22,0x16,0x1e,0x3c,0x01,0x01,0x01,0x01,0x01,0x01,0x36,0x1e,
  0x1e,0x28,0x18,0x18,0x28,0x09,0x1e,0x08,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x03,0x13,0x13,0x1e,0x1e,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x1b,0x1e,0x10,0x13,0x13,0x1e,0x1e,
  0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2f,0x1e,0x1e,
  0x14,0x1f,0x26,0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,
  0x1e,0x27,0x14,0x1e,0x1f,0x13,0x0a,0x1e,0x3b,0x01,0x01,0x01,0x01,
  0x01,0x1d,0x1e,0x03,0x13,0x22,0x1e,0x32,0x01,0x01,0x01,0x01,0x38,
  0x1e,0x25,0x1a,0x22,0x1e,0x1e,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x18,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1b,0x01,0x01,0x1d,0x1e,0x1e,0x25,0x27,0x0c,0x0c,0x13,0x1f,
  0x05,0x15,0x1e,0x38,0x01,0x01,0x01,0x38,0x1e,0x28,0x14,0x14,0x1f,
  0x14,0x22,0x0d,0x03,0x1e,0x1e,0x38,0x01,0x01,0x37,0x1e,0x24,0x0b,
  0x02,0x1f,0x19,0x1e,0x31,0x13,0x13,0x0b,0x0d,0x1e,0x2c,0x01,0x01,
  0x01,0x01,0x37,0x1e,0x1e,0x1e,0x1e,0x18,0x1e,0x1e,0x09,0x1e,0x38,
  0x01,0x01,0x01,0x01,0x1b,0x1e,0x1e,0x10,0x09,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x18,0x2d,0x2d,0x1e,0x38,0x01,0x01,0x34,0x04,0x1e,0x1e,0x09,
  0x2d,0x0d,0x26,0x03,0x1e,0x1e,0x1e,0x39,0x35,0x01,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x21,0x14,0x2d,0x1e,0x1d,0x2b,0x01,0x01,
  0x01,0x01,0x01,0x2b,0x38,0x1e,0x02,0x19,0x1e,0x1e,0x05,0x25,0x1e,
  0x08,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x2d,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x32,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x1e,
  0x10,0x1f,0x10,0x1e,0x15,0x14,0x1e,0x07,0x2b,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x38,0x1e,0x1e,0x13,0x13,0x1e,0x1e,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x37,0x1e,0x06,0x1f,0x26,0x1e,0x1e,
  0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x2a,0x1e,
  0x10,0x31,0x1e,0x33,0x1b,0x01,0x01,0x01,0x01,0x01,0x11,0x1e,0x1e,
  0x2a,0x23,0x1e,0x1d,0x20,0x01,0x01,0x01,0x2b,0x1e,0x1e,0x15,0x06,
  0x25,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x25,
  0x25,0x16,0x16,0x16,0x16,0x16,0x16,0x25,0x21,0x1e,0x0f,0x01,0x3b,
  0x1e,0x03,0x23,0x14,0x13,0x1f,0x0c,0x02,0x0a,0x1e,0x1e,0x1e,0x36,
  0x01,0x01,0x01,0x2b,0x1e,0x1e,0x1e,0x25,0x02,0x14,0x23,0x0b,0x23,
  0x12,0x21,0x1e,0x08,0x01,0x37,0x1e,0x06,0x0b,0x23,0x14,0x27,0x1e,
  0x29,0x19,0x05,0x02,0x03,0x1e,0x3a,0x01,0x01,0x01,0x01,0x38,0x1e,
  0x23,0x13,0x13,0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,0x01,0x01,
  0x2c,0x1e,0x1a,0x13,0x13,0x13,0x13,0x1e,0x0d,0x13,0x13,0x13,0x13,
  0x1e,0x1e,0x01,0x01,0x04,0x1e,0x1e,0x0b,0x05,0x14,0x13,0x05,0x14,
  0x13,0x22,0x1e,0x1e,0x39,0x01,0x01,0x01,0x0e,0x38,0x1e,0x10,0x24,
  0x1a,0x26,0x16,0x26,0x31,0x1e,0x3e,0x2b,0x01,0x01,0x01,0x35,0x38,
  0x1e,0x2d,0x03,0x16,0x0d,0x12,0x21,0x18,0x1e,0x3e,0x2b,0x01,0x01,
  0x01,0x01,0x36,0x1e,0x1e,0x14,0x13,0x0c,0x13,0x05,0x14,0x2a,0x1e,
  0x2f,0x01,0x01,0x01,0x01,0x01,0x2b,0x1e,0x1e,0x09,0x03,0x06,0x1a,
  0x28,0x03,0x21,0x1e,0x3e,0x2b,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,
  0x1e,0x1e,0x06,0x22,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x0d,0x27,0x1e,0x1e,0x1e,0x3e,0x3c,0x01,0x01,
  0x01,0x01,0x01,0x38,0x1e,0x09,0x25,0x15,0x24,0x28,0x26,0x31,0x1e,
  0x1e,0x1b,0x01,0x01,0x01,0x2e,0x1e,0x10,0x26,0x2d,0x2d,0x31,0x1e,
  0x2c,0x01,0x01,0x01,0x01,0x38,0x1e,0x31,0x14,0x1a,0x1e,0x1e,0x2f,
  0x01,0x01,0x01,0x01,0x01,0x20,0x1e,0x09,0x06,0x12,0x19,0x22,0x13,
  0x13,0x22,0x23,0x0c,0x02,0x1e,0x39,0x01,0x3d,0x1e,0x0d,0x13,0x13,
  0x0c,0x06,0x1e,0x1e,0x1e,0x33,0x39,0x36,0x01,0x01,0x01,0x01,0x01,
  0x2b,0x2c,0x33,0x1e,0x1e,0x1e,0x29,0x23,0x26,0x27,0x06,0x1e,0x1c,
  0x01,0x0f,0x1e,0x06,0x15,0x2a,0x0b,0x24,0x26,0x14,0x22,0x22,0x1a,
  0x1e,0x07,0x20,0x01,0x01,0x01,0x01,0x38,0x1e,0x23,0x13,0x13,0x13,
  0x13,0x13,0x13,0x09,0x1e,0x36,0x01,0x01,0x01,0x3a,0x1e,0x26,0x13,
  0x13,0x13,0x14,0x1e,0x28,0x13,0x13,0x0c,0x0c,0x1e,0x1e,0x01,0x01,
  0x07,0x1e,0x0b,0x0c,0x0c,0x13,0x1f,0x14,0x13,0x14,0x14,0x1f,0x1e,
  0x40,0x01,0x01,0x01,0x11,0x1e,0x2d,0x02,0x24,0x14,0x13,0x13,0x05,
  0x05,0x2a,0x1e,0x38,0x01,0x01,0x01,0x39,0x1e,0x10,0x13,0x13,0x0c,
  0x13,0x0c,0x13,0x1f,0x03,0x1e,0x38,0x01,0x01,0x01,0x01,0x04,0x1e,
  0x15,0x13,0x14,0x14,0x13,0x05,0x02,0x15,0x1e,0x0f,0x01,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x28,0x13,0x05,0x05,0x13,0x13,0x13,0x13,0x2a,
  0x1e,0x38,0x01,0x01,0x01,0x2b,0x32,0x1e,0x10,0x06,0x1a,0x25,0x16,
  0x24,0x21,0x1e,0x07,0x2b,0x01,0x01,0x01,0x0e,0x38,0x1e,0x09,0x26,
  0x15,0x2a,0x29,0x29,0x2a,0x1e,0x1e,0x3c,0x01,0x01,0x01,0x1c,0x1e,
  0x09,0x0c,0x19,0x05,0x05,0x14,0x14,0x0b,0x1a,0x1e,0x1e,0x36,0x01,
  0x01,0x04,0x1e,0x29,0x13,0x13,0x13,0x05,0x1e,0x07,0x01,0x01,0x01,
  0x01,0x2b,0x1e,0x1e,0x02,0x22,0x26,0x1e,0x11,0x01,0x01,0x01,0x01,
  0x01,0x20,0x1e,0x09,0x19,0x02,0x22,0x1f,0x13,0x13,0x1f,0x0b,0x14,
  0x1a,0x1e,0x2c,0x01,0x2c,0x1e,0x27,0x1f,0x1f,0x13,0x23,0x16,0x1e,
  0x1e,0x1e,0x38,0x0f,0x01,0x01,0x01,0x01,0x01,0x0f,0x38,0x1e,0x1e,
  0x1e,0x2a,0x02,0x14,0x05,0x14,0x15,0x1e,0x3d,0x01,0x0f,0x1e,0x15,
  0x23,0x02,0x0b,0x12,0x10,0x24,0x25,0x05,0x27,0x16,0x1e,0x04,0x01,
  0x01,0x01,0x36,0x1e,0x21,0x13,0x13,0x13,0x22,0x13,0x13,0x13,0x26,
  0x1e,0x30,0x01,0x01,0x01,0x0f,0x1e,0x26,0x13,0x13,0x13,0x05,0x1e,
  0x28,0x13,0x1f,0x19,0x13,0x1e,0x1e,0x01,0x0e,0x1e,0x10,0x1f,0x13,
  0x02,0x14,0x16,0x15,0x0c,0x0c,0x0c,0x1f,0x21,0x1e,0x2b,0x01,0x01,
  0x33,0x1e,0x0b,0x0b,0x0c,0x14,0x12,0x05,0x0b,0x26,0x22,0x09,0x1e,
  0x20,0x01,0x01,0x33,0x1e,0x1f,0x13,0x0c,0x0c,0x1a,0x05,0x0c,0x13,
  0x1f,0x18,0x1e,0x34,0x01,0x01,0x01,0x07,0x1e,0x23,0x0c,0x0c,0x05,
  0x22,0x23,0x13,0x0d,0x1e,0x11,0x01,0x01,0x01,0x01,0x0e,0x1e,0x10,
  0x27,0x0c,0x0b,0x26,0x12,0x13,0x13,0x1f,0x1f,0x1e,0x1e,0x0e,0x01,
  0x01,0x38,0x1e,0x2a,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x16,0x1e,
  0x38,0x01,0x01,0x01,0x39,0x1e,0x10,0x0c,0x13,0x1f,0x0b,0x0c,0x0c,
  0x22,0x24,0x1e,0x38,0x01,0x01,0x01,0x1d,0x1e,0x23,0x05,0x22,0x0c,
  0x26,0x0d,0x0c,0x02,0x19,0x15,0x1e,0x0f,0x01,0x01,0x0f,0x1e,0x0a,
  0x22,0x13,0x13,0x1a,0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,
  0x2d,0x28,0x0d,0x1e,0x1e,0x3c,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,
  0x18,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1b,0x01,
  0x1b,0x1e,0x18,0x06,0x13,0x05,0x22,0x0c,0x1f,0x0d,0x18,0x1e,0x1e,
  0x0f,0x01,0x01,0x01,0x04,0x1e,0x1e,0x09,0x1a,0x14,0x05,0x0c,0x29,
  0x0d,0x15,0x18,0x1e,0x3b,0x01,0x0f,0x1e,0x15,0x28,0x06,0x14,0x19,
  0x1e,0x28,0x0b,0x22,0x0d,0x02,0x1e,0x07,0x01,0x01,0x01,0x08,0x1e,
  0x25,0x13,0x13,0x13,0x25,0x13,0x13,0x13,0x22,0x1e,0x32,0x01,0x01,
  0x01,0x0f,0x1e,0x26,0x13,0x13,0x13,0x05,0x1e,0x28,0x13,0x13,0x0c,
  0x13,0x1e,0x1e,0x01,0x17,0x1e,0x31,0x0c,0x0c,0x1f,0x13,0x1e,0x1e,
  0x13,0x27,0x0b,0x0c,0x03,0x1e,0x2f,0x01,0x20,0x1e,0x18,0x0d,0x26,
  0x28,0x09,0x1e,0x0b,0x05,0x1f,0x0c,0x21,0x1e,0x36,0x01,0x00,0x1e,
  0x1e,0x24,0x0d,0x26,0x10,0x1e,0x23,0x13,0x05,0x13,0x21,0x1e,0x2b,
  0x01,0x01,0x01,0x1e,0x1e,0x1f,0x13,0x27,0x1a,0x29,0x19,0x05,0x14,
  0x1e,0x1e,0x00,0x01,0x01,0x01,0x3b,0x1e,0x0a,0x05,0x0c,0x05,0x12,
  0x1e,0x0d,0x0b,0x14,0x06,0x0a,0x1e,0x3b,0x01,0x36,0x1e,0x1e,0x13,
  0x13,0x13,0x13,0x1a,0x13,0x13,0x13,0x13,0x10,0x1e,0x0e,0x01,0x01,
  0x33,0x1e,0x1f,0x1f,0x0c,0x13,0x0d,0x14,0x0c,0x13,0x05,0x2d,0x1e,
  0x0f,0x01,0x35,0x1e,0x2d,0x1f,0x0c,0x22,0x0b,0x1e,0x10,0x22,0x14,
  0x1f,0x24,0x1e,0x39,0x01,0x01,0x0f,0x1e,0x26,0x0c,0x19,0x0b,0x0d,
  0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x2b,0x33,0x1e,0x23,0x19,0x06,
  0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x3d,0x01,0x01,0x01,0x3d,0x1e,0x1e,
  0x18,0x25,0x13,0x0c,0x0c,0x13,0x0b,0x0d,0x1e,0x38,0x01,0x01,0x34,
  0x1e,0x18,0x29,0x12,0x05,0x14,0x23,0x1f,0x2a,0x1e,0x1e,0x1e,0x3d,
  0x01,0x01,0x0f,0x1e,0x26,0x22,0x22,0x26,0x06,0x1e,0x25,0x29,0x1f,
  0x02,0x23,0x1e,0x1e,0x01,0x01,0x01,0x1c,0x1e,0x06,0x13,0x13,0x13,
  0x1e,0x13,0x13,0x13,0x1f,0x1e,0x33,0x01,0x01,0x01,0x0f,0x1e,0x26,
  0x13,0x13,0x13,0x05,0x1e,0x28,0x13,0x0c,0x0c,0x13,0x1e,0x1e,0x01,
  0x3c,0x1e,0x2a,0x0c,0x05,0x02,0x0c,0x18,0x2d,0x0c,0x1f,0x0c,0x05,
  0x16,0x1e,0x08,0x01,0x01,0x04,0x1e,0x1e,0x2a,0x26,0x26,0x24,0x05,
  0x0b,0x02,0x13,0x10,0x1e,0x2b,0x01,0x01,0x3d,0x1e,0x1e,0x31,0x15,
  0x06,0x02,0x22,0x13,0x1f,0x0c,0x10,0x1e,0x0e,0x01,0x01,0x2f,0x1e,
  0x1e,0x1f,0x0c,0x1f,0x12,0x2a,0x22,0x0c,0x24,0x10,0x1e,0x17,0x01,
  0x01,0x01,0x0f,0x1e,0x15,0x0d,0x05,0x0b,0x1a,0x23,0x05,0x0b,0x14,
  0x0c,0x29,0x1e,0x30,0x01,0x3a,0x1e,0x26,0x13,0x13,0x13,0x14,0x1e,
  0x26,0x13,0x13,0x13,0x16,0x1e,0x1b,0x01,0x34,0x1e,0x1e,0x05,0x05,
  0x13,0x23,0x1e,0x15,0x05,0x0c,0x13,0x27,0x1e,0x11,0x01,0x17,0x1e,
  0x03,0x05,0x27,0x05,0x23,0x26,0x0d,0x14,0x22,0x05,0x22,0x1e,0x3e,
  0x01,0x01,0x0f,0x1e,0x28,0x13,0x05,0x0b,0x0d,0x1e,0x2c,0x01,0x01,
  0x01,0x01,0x01,0x01,0x39,0x1e,0x1e,0x0b,0x23,0x1e,0x1e,0x17,0x01,
  0x01,0x01,0x01,0x01,0x01,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x34,0x00,0x01,0x01,0x01,0x01,0x01,0x2f,0x38,0x1e,0x1e,0x1e,0x21,
  0x22,0x13,0x13,0x0c,0x1e,0x1e,0x00,0x01,0x35,0x1e,0x2d,0x0c,0x0c,
  0x1f,0x1a,0x21,0x1e,0x1e,0x1e,0x38,0x36,0x01,0x01,0x01,0x0f,0x1e,
  0x15,0x12,0x22,0x27,0x12,0x1e,0x0a,0x14,0x24,0x12,0x22,0x1e,0x1e,
  0x01,0x01,0x01,0x1d,0x1e,0x26,0x1f,0x13,0x13,0x1e,0x13,0x13,0x13,
  0x13,0x21,0x1e,0x3c,0x01,0x01,0x0f,0x1e,0x26,0x13,0x13,0x13,0x05,
  0x1e,0x28,0x13,0x1f,0x0c,0x05,0x1e,0x1e,0x01,0x37,0x1e,0x06,0x0c,
  0x13,0x13,0x05,0x18,0x09,0x14,0x02,0x0c,0x0c,0x26,0x1e,0x37,0x01,
  0x01,0x1d,0x1e,0x1a,0x22,0x1f,0x0c,0x14,0x14,0x05,0x0c,0x19,0x10,
  0x1e,0x2b,0x01,0x34,0x07,0x1e,0x29,0x13,0x23,0x1f,0x0c,0x1a,0x0c,
  0x19,0x0c,0x10,0x1e,0x0e,0x01,0x01,0x38,0x1e,0x26,0x0c,0x14,0x02,
  0x26,0x2d,0x22,0x0b,0x14,0x28,0x1e,0x0f,0x01,0x01,0x01,0x37,0x1e,
  0x06,0x14,0x14,0x05,0x1f,0x19,0x24,0x29,0x29,0x29,0x25,0x1e,0x3b,
  0x01,0x3a,0x1e,0x26,0x13,0x13,0x13,0x1f,0x19,0x05,0x13,0x13,0x13,
  0x1a,0x1e,0x04,0x01,0x2f,0x1e,0x2a,0x1f,0x22,0x13,0x05,0x02,0x05,
  0x05,0x13,0x0c,0x22,0x1e,0x07,0x01,0x2f,0x1e,0x2a,0x13,0x23,0x22,
  0x05,0x05,0x0b,0x06,0x29,0x27,0x15,0x1e,0x3d,0x01,0x01,0x0f,0x1e,
  0x26,0x19,0x12,0x14,0x29,0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x01,
  0x35,0x38,0x1e,0x12,0x0d,0x15,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x09,0x19,0x0c,
  0x2d,0x1e,0x35,0x01,0x3c,0x1e,0x2a,0x22,0x27,0x18,0x1e,0x1e,0x1e,
  0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x0f,0x1e,0x28,0x27,0x05,0x12,
  0x0d,0x1e,0x24,0x22,0x0d,0x12,0x14,0x1e,0x1e,0x01,0x01,0x0e,0x1e,
  0x10,0x0c,0x0c,0x02,0x22,0x1e,0x1a,0x13,0x13,0x13,0x24,0x1e,0x37,
  0x01,0x01,0x0f,0x1e,0x26,0x13,0x13,0x13,0x05,0x1e,0x28,0x0c,0x02,
  0x0c,0x1f,0x1e,0x1e,0x01,0x08,0x1e,0x25,0x13,0x1f,0x1f,0x13,0x18,
  0x09,0x0c,0x13,0x05,0x27,0x15,0x1e,0x3a,0x01,0x36,0x1e,0x31,0x27,
  0x05,0x05,0x26,0x1e,0x0d,0x0b,0x1f,0x0c,0x10,0x1e,0x2b,0x01,0x3c,
  0x1e,0x16,0x13,0x05,0x27,0x02,0x1e,0x12,0x13,0x1f,0x13,0x10,0x1e,
  0x0e,0x01,0x01,0x1e,0x1e,0x05,0x1f,0x0c,0x13,0x10,0x1e,0x05,0x0b,
  0x06,0x12,0x1e,0x39,0x01,0x01,0x01,0x0f,0x1e,0x0a,0x22,0x0c,0x0b,
  0x2a,0x1e,0x09,0x2a,0x03,0x25,0x10,0x1e,0x17,0x01,0x1b,0x1e,0x15,
  0x13,0x22,0x13,0x13,0x0c,0x14,0x0d,0x27,0x19,0x26,0x1e,0x3a,0x01,
  0x0f,0x1e,0x26,0x0c,0x13,0x0c,0x19,0x0c,0x23,0x29,0x1a,0x02,0x26,
  0x1e,0x04,0x01,0x36,0x1e,0x31,0x0c,0x05,0x13,0x27,0x1e,0x1e,0x16,
  0x03,0x2a,0x03,0x1e,0x0f,0x01,0x01,0x0f,0x1e,0x26,0x0c,0x0b,0x22,
  0x1a,0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1c,0x1e,0x09,
  0x26,0x27,0x2d,0x1e,0x2f,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x20,0x2c,0x40,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,
  0x34,0x3e,0x1e,0x1e,0x1e,0x1e,0x40,0x2c,0x20,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x0f,0x1e,0x0a,0x28,0x1f,0x02,0x12,0x16,0x19,0x05,
  0x12,0x02,0x0b,0x1e,0x40,0x01,0x01,0x08,0x1e,0x16,0x27,0x13,0x14,
  0x0b,0x2a,0x23,0x13,0x13,0x22,0x19,0x1e,0x38,0x01,0x01,0x0f,0x1e,
  0x06,0x13,0x13,0x13,0x14,0x1e,0x28,0x13,0x0c,0x0c,0x0c,0x1e,0x1e,
  0x01,0x2b,0x1e,0x21,0x13,0x05,0x13,0x1f,0x18,0x09,0x22,0x14,0x13,
  0x23,0x03,0x1e,0x2f,0x01,0x17,0x1e,0x03,0x27,0x02,0x0c,0x0d,0x21,
  0x27,0x22,0x13,0x1a,0x21,0x1e,0x2b,0x01,0x3a,0x1e,0x26,0x0c,0x13,
  0x05,0x0d,0x09,0x1f,0x0b,0x27,0x13,0x09,0x1e,0x0e,0x01,0x01,0x1e,
  0x1e,0x1f,0x13,0x22,0x14,0x12,0x15,0x22,0x1f,0x0c,0x0b,0x1e,0x1e,
  0x00,0x01,0x01,0x08,0x1e,0x16,0x1f,0x19,0x22,0x19,0x1e,0x02,0x0c,
  0x23,0x05,0x26,0x1e,0x0f,0x01,0x37,0x1e,0x24,0x13,0x0c,0x13,0x27,
  0x1e,0x18,0x0a,0x03,0x16,0x21,0x1e,0x36,0x01,0x17,0x1e,0x31,0x1f,
  0x1f,0x13,0x1a,0x1e,0x1e,0x2a,0x2a,0x03,0x03,0x1e,0x0f,0x01,0x35,
  0x1e,0x2d,0x1f,0x22,0x0c,0x23,0x09,0x06,0x14,0x05,0x0b,0x29,0x1e,
  0x11,0x01,0x01,0x0f,0x1e,0x06,0x1a,0x19,0x13,0x29,0x1e,0x2c,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x24,0x22,0x28,0x1e,
  0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x37,0x38,0x1e,0x38,0x2b,0x01,0x01,0x01,0x2b,0x38,0x1e,
  0x38,0x37,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x39,
  0x1e,0x0d,0x0b,0x27,0x29,0x27,0x15,0x28,0x1f,0x02,0x27,0x1e,0x1e,
  0x39,0x01,0x01,0x30,0x1e,0x24,0x13,0x23,0x0d,0x1f,0x22,0x13,0x13,
  0x13,0x22,0x14,0x1e,0x40,0x01,0x01,0x1b,0x1e,0x15,0x13,0x13,0x13,
  0x22,0x1e,0x03,0x05,0x22,0x0c,0x1f,0x1e,0x1e,0x01,0x2f,0x1e,0x2a,
  0x13,0x23,0x05,0x0c,0x1e,0x1e,0x05,0x0c,0x0c,0x05,0x16,0x1e,0x3c,
  0x01,0x17,0x1e,0x03,0x14,0x0c,0x14,0x27,0x0c,0x19,0x12,0x13,0x05,
  0x18,0x1e,0x3d,0x01,0x08,0x1e,0x25,0x13,0x05,0x22,0x1f,0x1f,0x1f,
  0x13,0x1f,0x13,0x21,0x1e,0x11,0x01,0x08,0x1e,0x10,0x1f,0x0c,0x05,
  0x0c,0x0c,0x05,0x13,0x19,0x02,0x05,0x03,0x1e,0x17,0x01,0x01,0x20,
  0x1e,0x18,0x0c,0x19,0x12,0x13,0x05,0x22,0x14,0x14,0x13,0x1e,0x1e,
  0x17,0x01,0x3c,0x1e,0x03,0x13,0x1f,0x13,0x14,0x1e,0x1a,0x1f,0x1f,
  0x14,0x0a,0x1e,0x3b,0x01,0x01,0x1e,0x1e,0x0c,0x13,0x0b,0x23,0x10,
  0x1a,0x1f,0x1f,0x1f,0x02,0x1e,0x11,0x01,0x01,0x11,0x1e,0x06,0x0b,
  0x05,0x29,0x02,0x1f,0x0b,0x22,0x0b,0x15,0x1e,0x0f,0x01,0x01,0x0f,
  0x1e,0x26,0x05,0x22,0x14,0x0d,0x1e,0x39,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x08,0x1e,0x09,0x19,0x02,0x09,0x1e,0x37,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3a,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x39,0x35,0x01,0x01,0x1d,
  0x1e,0x27,0x23,0x1f,0x23,0x0b,0x05,0x14,0x13,0x22,0x1f,0x14,0x31,
  0x1e,0x17,0x01,0x08,0x1e,0x25,0x13,0x22,0x13,0x0c,0x0d,0x23,0x13,
  0x13,0x0c,0x0c,0x1e,0x1e,0x01,0x20,0x1e,0x09,0x0c,0x13,0x0c,0x0b,
  0x24,0x1a,0x02,0x05,0x1f,0x22,0x18,0x1e,0x20,0x01,0x00,0x32,0x1e,
  0x0d,0x05,0x14,0x22,0x27,0x12,0x0c,0x1f,0x0b,0x0b,0x1e,0x3e,0x01,
  0x35,0x40,0x1e,0x22,0x05,0x0c,0x23,0x02,0x1a,0x0c,0x14,0x13,0x14,
  0x1e,0x1e,0x01,0x38,0x1e,0x06,0x05,0x1f,0x02,0x05,0x1f,0x22,0x0c,
  0x23,0x22,0x05,0x26,0x1e,0x0f,0x01,0x01,0x01,0x38,0x1e,0x2d,0x05,
  0x0c,0x1f,0x0b,0x1f,0x0b,0x06,0x1e,0x1e,0x38,0x01,0x01,0x01,0x1e,
  0x1e,0x13,0x0c,0x13,0x0c,0x13,0x13,0x1f,0x0c,0x23,0x10,0x1e,0x0e,
  0x01,0x01,0x1d,0x1e,0x23,0x13,0x13,0x0c,0x13,0x1f,0x13,0x0b,0x23,
  0x1e,0x1e,0x0f,0x01,0x01,0x3b,0x1e,0x1e,0x12,0x0c,0x0c,0x22,0x02,
  0x23,0x14,0x31,0x1e,0x1e,0x36,0x01,0x01,0x04,0x1e,0x0d,0x0b,0x1f,
  0x0b,0x24,0x1e,0x40,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x38,0x1e,0x15,0x28,0x24,0x1e,0x11,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x3a,0x38,0x1e,0x1e,0x1e,0x32,0x38,0x1e,
  0x1e,0x1e,0x33,0x39,0x35,0x01,0x01,0x08,0x1e,0x09,0x22,0x1f,0x1a,
  0x19,0x25,0x1e,0x1e,0x13,0x13,0x14,0x1f,0x26,0x1e,0x04,0x01,0x35,
  0x1e,0x1e,0x13,0x13,0x13,0x05,0x14,0x13,0x05,0x0c,0x13,0x03,0x1e,
  0x38,0x01,0x01,0x38,0x1e,0x1a,0x05,0x0c,0x1f,0x1f,0x0c,0x13,0x0c,
  0x23,0x1a,0x1e,0x38,0x01,0x01,0x01,0x37,0x1e,0x1e,0x21,0x0a,0x10,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x3d,0x01,0x01,0x30,0x1e,0x1e,
  0x21,0x0a,0x31,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x11,0x01,0x1e,
  0x1e,0x05,0x1f,0x0c,0x13,0x23,0x1e,0x09,0x1a,0x29,0x02,0x1f,0x29,
  0x1e,0x38,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x18,0x16,0x25,0x16,
  0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x38,0x1e,0x1e,0x14,0x13,
  0x05,0x13,0x13,0x13,0x14,0x09,0x1e,0x38,0x01,0x01,0x01,0x30,0x1e,
  0x1e,0x0b,0x0c,0x0c,0x13,0x05,0x13,0x13,0x10,0x1e,0x38,0x01,0x01,
  0x01,0x01,0x3b,0x1e,0x1e,0x1e,0x03,0x16,0x25,0x2d,0x1e,0x1e,0x32,
  0x36,0x01,0x01,0x01,0x1b,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x39,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x3b,0x1e,0x21,0x05,
  0x15,0x1e,0x37,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x1c,0x1e,0x06,0x24,0x05,0x05,0x23,0x31,0x1e,0x1e,
  0x13,0x0b,0x13,0x22,0x22,0x1e,0x07,0x01,0x01,0x38,0x1e,0x1e,0x02,
  0x13,0x1f,0x22,0x13,0x05,0x0c,0x15,0x1e,0x32,0x2b,0x01,0x01,0x3a,
  0x1e,0x1e,0x29,0x14,0x23,0x13,0x05,0x05,0x0c,0x0d,0x1e,0x1e,0x37,
  0x01,0x01,0x01,0x01,0x37,0x38,0x1e,0x1e,0x1e,0x38,0x38,0x1e,0x1e,
  0x1e,0x3e,0x3d,0x00,0x01,0x01,0x01,0x30,0x32,0x1e,0x1e,0x1e,0x1d,
  0x38,0x1e,0x1e,0x1e,0x1e,0x11,0x0e,0x01,0x1e,0x1e,0x19,0x13,0x05,
  0x05,0x23,0x1e,0x1e,0x27,0x0c,0x22,0x02,0x23,0x10,0x1e,0x0e,0x01,
  0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x32,0x30,0x01,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x28,0x25,0x16,0x1e,
  0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x30,0x1e,0x1e,0x1e,0x31,
  0x16,0x28,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x3b,
  0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,
  0x01,0x1b,0x38,0x1e,0x1e,0x1e,0x40,0x39,0x35,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x17,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x08,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1d,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x04,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x18,0x25,0x25,0x28,
  0x10,0x1e,0x1e,0x1e,0x36,0x01,0x01,0x01,0x01,0x3a,0x1e,0x1e,0x1e,
  0x03,0x25,0x25,0x2a,0x1e,0x1e,0x1e,0x37,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x36,0x1b,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x36,0x2e,0x36,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x20,0x08,0x08,0x08,0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x34,0x04,0x3e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x11,0x2b,0x01,
  0x01,0x01,0x01,0x01,0x01,0x30,0x1d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x2f,0x08,
  0x08,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2b,0x38,0x1e,0x38,0x17,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x08,0x38,0x1e,0x1e,
  0x1e,0x1e,0x38,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x07,0x04,0x34,0x01,
  0x01,0x01,0x20,0x2c,0x07,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x36,
  0x01,0x01,0x01,0x01,0x01,0x01,0x3b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x1b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1d,0x30,0x0f,0x38,0x1e,0x1e,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x0e,0x2e,0x08,0x3c,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x17,0x08,0x1b,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x20,0x08,0x08,0x2e,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x17,0x08,0x08,0x2f,0x34,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x11,
  0x33,0x11,0x0f,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x34,0x01,
  0x35,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x30,0x32,
  0x3e,0x3d,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x39,
  0x33,0x39,0x0f,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x35,0x39,0x33,0x1e,0x38,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x39,0x33,
  0x38,0x37,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,
  0x1e,0x1e,0x38,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x34,0x2e,0x3d,0x04,0x3d,0x2c,0x0f,0x17,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x35,0x2e,0x3d,0x3d,0x2c,0x1c,0x3c,0x2f,
  0x0f,0x2c,0x3d,0x3d,0x3b,0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x0e,0x01,0x00,0x35,0x00,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x20,0x01,0x20,0x20,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x34,0x01,
  0x20,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x34,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x32,0x1e,0x1e,0x1e,0x1e,0x38,
  0x01,0x01,0x01,0x01,0x08,0x38,0x1e,0x1e,0x38,0x1e,0x1e,0x3d,0x01,
  0x01,0x01,0x01,0x01,0x01,0x35,0x3e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x11,0x1e,0x1e,0x1e,0x1e,0x39,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x39,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x30,0x1e,0x1d,0x30,0x36,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x3e,0x1e,0x1e,0x1e,0x1e,0x39,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x33,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x3e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x2e,0x33,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x07,0x3b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x37,0x38,
  0x1e,0x38,0x1e,0x1e,0x1e,0x3d,0x00,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x2b,0x38,0x1e,0x1e,0x38,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x33,0x1e,0x1e,0x04,0x34,
  0x01,0x01,0x01,0x01,0x01,0x01,0x3c,0x1c,0x1c,0x0f,0x17,0x01,0x01,
  0x01,0x2b,0x38,0x1e,0x1e,0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x2b,0x1b,0x37,0x2c,0x3a,0x01,0x01,0x01,0x01,0x1c,0x1d,0x1e,
  0x1e,0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x03,0x0c,0x19,0x21,0x1e,0x3a,0x01,0x01,0x08,
  0x1e,0x1e,0x09,0x18,0x1e,0x2d,0x18,0x1e,0x30,0x01,0x01,0x01,0x01,
  0x01,0x2c,0x1e,0x2a,0x22,0x22,0x21,0x1e,0x3a,0x01,0x01,0x01,0x01,
  0x01,0x01,0x0e,0x1e,0x10,0x0c,0x02,0x2d,0x1e,0x3a,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x00,0x1e,0x1e,0x0c,0x13,0x12,0x13,0x13,
  0x21,0x1e,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1c,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x2a,0x1f,0x19,0x2d,0x1e,0x3b,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x1e,0x13,0x13,0x0a,0x1e,0x3b,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x40,0x1e,0x2a,0x0d,0x0d,0x29,0x27,
  0x06,0x2d,0x1e,0x38,0x2b,0x01,0x01,0x01,0x2e,0x1e,0x1e,0x0a,0x0d,
  0x29,0x1a,0x24,0x1e,0x18,0x06,0x1a,0x06,0x12,0x16,0x1e,0x1e,0x3b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x37,0x1e,0x1e,0x10,0x1e,0x1e,0x2d,
  0x1e,0x1e,0x3d,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x18,
  0x09,0x1e,0x09,0x09,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
  0x3e,0x1e,0x18,0x18,0x1e,0x09,0x1e,0x1e,0x04,0x01,0x01,0x01,0x01,
  0x2b,0x32,0x1e,0x1e,0x1e,0x1e,0x1e,0x04,0x01,0x34,0x38,0x1e,0x1e,
  0x1e,0x1e,0x17,0x01,0x01,0x01,0x01,0x01,0x34,0x11,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x11,0x01,0x01,0x1c,0x1e,0x1e,0x1e,0x1e,0x1e,0x00,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x1e,0x1e,
  0x13,0x13,0x13,0x27,0x1e,0x40,0x20,0x01,0x1c,0x1e,0x12,0x13,0x05,
  0x29,0x13,0x22,0x1e,0x32,0x01,0x01,0x01,0x01,0x2f,0x1e,0x1e,0x1f,
  0x13,0x13,0x27,0x1e,0x40,0x35,0x01,0x01,0x01,0x01,0x01,0x01,0x07,
  0x1e,0x22,0x13,0x27,0x1e,0x3e,0x00,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x23,0x13,0x16,0x22,0x13,0x2d,0x1e,0x35,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x1d,0x1e,0x0b,0x0b,0x15,0x1e,0x38,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2f,0x1e,0x1e,0x13,
  0x1f,0x1f,0x29,0x1e,0x32,0x00,0x01,0x01,0x01,0x01,0x01,0x34,0x33,
  0x1e,0x0c,0x13,0x1e,0x13,0x13,0x2a,0x1e,0x2f,0x01,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x25,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x10,0x1e,
  0x38,0x01,0x01,0x34,0x3e,0x1e,0x0d,0x1f,0x0b,0x0c,0x0c,0x22,0x22,
  0x05,0x0c,0x0c,0x1f,0x13,0x0c,0x29,0x1e,0x40,0x20,0x01,0x01,0x01,
  0x01,0x01,0x38,0x1e,0x23,0x13,0x25,0x13,0x13,0x22,0x1e,0x3e,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x31,0x13,0x13,
  0x18,0x1e,0x34,0x01,0x01,0x01,0x01,0x01,0x2f,0x1e,0x2a,0x13,0x13,
  0x0c,0x13,0x05,0x1e,0x07,0x01,0x01,0x01,0x01,0x38,0x1e,0x03,0x24,
  0x12,0x15,0x18,0x1e,0x34,0x04,0x1e,0x10,0x1f,0x26,0x1e,0x0f,0x01,
  0x01,0x01,0x01,0x01,0x04,0x1e,0x10,0x15,0x24,0x27,0x10,0x1e,0x36,
  0x2b,0x32,0x1e,0x0b,0x0b,0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x1c,0x1e,0x28,0x13,0x27,0x02,0x13,
  0x25,0x1e,0x08,0x01,0x3b,0x1e,0x0a,0x13,0x0b,0x16,0x13,0x1a,0x1e,
  0x04,0x01,0x01,0x01,0x01,0x0f,0x1e,0x06,0x13,0x1a,0x23,0x0c,0x25,
  0x1e,0x08,0x01,0x01,0x01,0x01,0x01,0x01,0x2c,0x1e,0x1e,0x14,0x0c,
  0x2a,0x1e,0x2f,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,
  0x21,0x28,0x1e,0x16,0x16,0x1e,0x38,0x2f,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x1c,0x1e,0x2a,0x13,0x13,0x1e,0x1e,0x0f,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x36,0x37,0x1e,0x26,0x05,0x12,0x27,0x14,0x03,
  0x1e,0x17,0x01,0x01,0x01,0x01,0x3d,0x1e,0x1e,0x1e,0x21,0x0a,0x1e,
  0x0a,0x03,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x2f,0x1e,0x1e,0x13,
  0x13,0x1f,0x1f,0x13,0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,0x1b,
  0x1e,0x2a,0x0c,0x13,0x1f,0x0c,0x1f,0x13,0x05,0x23,0x0c,0x23,0x29,
  0x0c,0x13,0x1f,0x25,0x1e,0x08,0x01,0x01,0x01,0x01,0x35,0x37,0x1e,
  0x06,0x13,0x03,0x19,0x13,0x29,0x1e,0x3d,0x35,0x01,0x01,0x01,0x01,
  0x01,0x01,0x3e,0x1e,0x14,0x13,0x1e,0x13,0x0c,0x1e,0x33,0x01,0x01,
  0x01,0x01,0x01,0x01,0x0e,0x1e,0x10,0x13,0x0b,0x24,0x13,0x1a,0x1e,
  0x04,0x01,0x01,0x01,0x2b,0x1e,0x21,0x0c,0x0c,0x0c,0x13,0x10,0x1e,
  0x2b,0x07,0x1e,0x22,0x0c,0x31,0x1e,0x17,0x01,0x01,0x01,0x01,0x01,
  0x07,0x1e,0x14,0x0c,0x23,0x14,0x15,0x1e,0x1b,0x38,0x1e,0x03,0x13,
  0x27,0x1e,0x2c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x1d,0x1e,0x0b,0x24,0x1e,0x1e,0x0b,0x26,0x1e,0x3a,0x01,
  0x35,0x1e,0x18,0x2d,0x18,0x1e,0x10,0x2d,0x1e,0x1b,0x01,0x01,0x01,
  0x35,0x39,0x1e,0x29,0x23,0x1e,0x1e,0x14,0x25,0x1e,0x04,0x34,0x01,
  0x01,0x01,0x01,0x1c,0x40,0x1e,0x1e,0x1e,0x22,0x0a,0x1e,0x1e,0x04,
  0x34,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x10,0x16,0x0d,0x26,
  0x21,0x09,0x1e,0x1e,0x2f,0x01,0x01,0x01,0x01,0x00,0x17,0x1b,0x3e,
  0x1e,0x06,0x0c,0x1a,0x1e,0x11,0x3b,0x35,0x01,0x01,0x01,0x01,0x36,
  0x1e,0x1e,0x1e,0x1a,0x06,0x1e,0x1e,0x0d,0x10,0x1e,0x1e,0x1d,0x00,
  0x01,0x37,0x1e,0x18,0x2d,0x2d,0x1e,0x1e,0x1e,0x1e,0x1e,0x18,0x2d,
  0x09,0x1e,0x38,0x01,0x01,0x37,0x1e,0x06,0x19,0x0b,0x14,0x26,0x09,
  0x13,0x13,0x13,0x13,0x18,0x1e,0x34,0x01,0x04,0x1e,0x0d,0x13,0x05,
  0x13,0x26,0x2a,0x0c,0x13,0x05,0x0c,0x1e,0x2d,0x05,0x23,0x0c,0x24,
  0x1e,0x39,0x01,0x01,0x01,0x2b,0x40,0x1e,0x1e,0x1e,0x21,0x1e,0x09,
  0x2d,0x1e,0x1e,0x1e,0x40,0x2b,0x01,0x01,0x01,0x01,0x36,0x3e,0x1e,
  0x2d,0x16,0x1e,0x31,0x2d,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x2f,0x3e,0x1e,0x10,0x21,0x1e,0x2a,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x01,0x38,0x1e,0x10,0x05,0x0c,0x14,0x2d,0x1e,0x38,0x1e,0x03,0x13,
  0x24,0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x1e,0x12,
  0x19,0x12,0x16,0x1e,0x08,0x1e,0x1e,0x13,0x13,0x1e,0x1e,0x1b,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1c,0x1e,
  0x1e,0x31,0x25,0x16,0x1e,0x1e,0x1e,0x36,0x01,0x01,0x38,0x1e,0x21,
  0x2a,0x16,0x03,0x1e,0x1e,0x2b,0x01,0x01,0x01,0x39,0x1e,0x1e,0x31,
  0x16,0x24,0x12,0x2a,0x03,0x1e,0x1e,0x04,0x01,0x01,0x01,0x1c,0x1e,
  0x1e,0x25,0x0d,0x24,0x03,0x15,0x28,0x1e,0x1e,0x04,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x21,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x24,0x1e,
  0x1e,0x2b,0x01,0x01,0x00,0x32,0x1e,0x1e,0x1e,0x1e,0x1e,0x02,0x1a,
  0x1e,0x1e,0x1e,0x1e,0x04,0x01,0x01,0x01,0x38,0x1e,0x28,0x25,0x2d,
  0x18,0x1e,0x18,0x2d,0x31,0x0a,0x31,0x1e,0x3d,0x01,0x11,0x1e,0x19,
  0x13,0x05,0x14,0x13,0x1e,0x22,0x13,0x13,0x13,0x13,0x18,0x1e,0x34,
  0x01,0x30,0x1e,0x29,0x05,0x0b,0x14,0x02,0x1e,0x03,0x28,0x0a,0x26,
  0x1e,0x38,0x01,0x01,0x38,0x1e,0x27,0x13,0x1f,0x0c,0x10,0x1e,0x13,
  0x0c,0x0c,0x13,0x0d,0x14,0x13,0x05,0x1f,0x05,0x1e,0x1e,0x00,0x01,
  0x01,0x38,0x1e,0x25,0x25,0x16,0x03,0x1e,0x1e,0x31,0x2a,0x0a,0x0a,
  0x1e,0x38,0x01,0x01,0x01,0x36,0x1e,0x1e,0x2a,0x26,0x26,0x29,0x26,
  0x25,0x18,0x1e,0x38,0x01,0x01,0x01,0x01,0x2f,0x1e,0x1e,0x2a,0x15,
  0x06,0x24,0x26,0x15,0x09,0x1e,0x38,0x0e,0x01,0x01,0x2b,0x3e,0x1e,
  0x22,0x0c,0x13,0x2d,0x1e,0x1e,0x1e,0x22,0x13,0x1e,0x1e,0x3d,0x3d,
  0x1c,0x08,0x01,0x01,0x01,0x34,0x04,0x1e,0x16,0x0b,0x0c,0x25,0x1e,
  0x38,0x1e,0x16,0x14,0x24,0x1e,0x3e,0x1d,0x1c,0x3c,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x1e,0x1e,0x13,0x13,0x13,
  0x13,0x21,0x1e,0x36,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,0x13,0x15,
  0x1e,0x1b,0x01,0x01,0x2e,0x1e,0x18,0x0c,0x13,0x13,0x0c,0x13,0x0c,
  0x13,0x22,0x1e,0x40,0x20,0x01,0x2e,0x1e,0x1e,0x19,0x1f,0x13,0x27,
  0x0b,0x0c,0x19,0x0b,0x1e,0x1e,0x2e,0x01,0x01,0x00,0x1e,0x1e,0x13,
  0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x15,0x1e,0x3d,0x01,0x01,
  0x30,0x1e,0x03,0x15,0x25,0x0a,0x31,0x1e,0x2d,0x28,0x0a,0x0a,0x09,
  0x1e,0x35,0x01,0x20,0x1e,0x09,0x1f,0x0c,0x27,0x19,0x1e,0x26,0x14,
  0x19,0x22,0x19,0x1e,0x32,0x01,0x37,0x1e,0x26,0x13,0x05,0x02,0x14,
  0x1e,0x12,0x23,0x14,0x13,0x22,0x1e,0x1e,0x00,0x01,0x37,0x1e,0x15,
  0x12,0x14,0x23,0x0d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,
  0x11,0x1e,0x02,0x0c,0x19,0x0c,0x21,0x18,0x13,0x0c,0x1f,0x13,0x14,
  0x05,0x0d,0x1a,0x19,0x24,0x1e,0x39,0x01,0x01,0x34,0x1e,0x18,0x0b,
  0x0c,0x1f,0x19,0x1e,0x06,0x24,0x14,0x13,0x13,0x1e,0x1e,0x01,0x01,
  0x34,0x3e,0x1e,0x15,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x2d,0x1e,
  0x3a,0x01,0x01,0x00,0x32,0x1e,0x06,0x13,0x13,0x13,0x13,0x13,0x13,
  0x13,0x09,0x1e,0x11,0x01,0x01,0x01,0x07,0x1e,0x14,0x1f,0x1f,0x2d,
  0x1e,0x1e,0x0a,0x13,0x26,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x08,0x01,
  0x01,0x01,0x1b,0x1e,0x15,0x02,0x19,0x25,0x1e,0x1e,0x1e,0x0c,0x22,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x39,0x35,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,0x1f,0x2d,0x1e,0x35,
  0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,0x13,0x2a,0x1e,0x2f,0x01,0x01,
  0x04,0x1e,0x0d,0x13,0x13,0x0c,0x0c,0x0c,0x1f,0x0c,0x13,0x25,0x1e,
  0x1c,0x01,0x38,0x1e,0x0d,0x13,0x1f,0x0c,0x1f,0x05,0x05,0x22,0x0c,
  0x26,0x1e,0x04,0x01,0x01,0x3b,0x1e,0x2a,0x13,0x13,0x13,0x0c,0x03,
  0x27,0x13,0x13,0x13,0x14,0x1e,0x3e,0x01,0x01,0x1d,0x1e,0x0b,0x13,
  0x13,0x0c,0x05,0x1e,0x13,0x1f,0x0b,0x0c,0x25,0x1e,0x3b,0x01,0x00,
  0x1e,0x1e,0x1f,0x05,0x05,0x1f,0x1e,0x15,0x05,0x24,0x12,0x24,0x1e,
  0x04,0x01,0x37,0x1e,0x24,0x1f,0x23,0x13,0x0b,0x1e,0x12,0x19,0x12,
  0x14,0x22,0x1e,0x1e,0x00,0x01,0x3d,0x1e,0x29,0x0b,0x22,0x1f,0x27,
  0x1e,0x24,0x1f,0x23,0x13,0x1e,0x1e,0x01,0x01,0x11,0x1e,0x02,0x13,
  0x1f,0x13,0x21,0x1e,0x0c,0x23,0x05,0x13,0x1e,0x1e,0x25,0x03,0x2a,
  0x16,0x1e,0x2e,0x01,0x01,0x01,0x1e,0x1e,0x02,0x19,0x1f,0x05,0x1e,
  0x0a,0x0c,0x14,0x27,0x0c,0x1e,0x1e,0x01,0x01,0x2f,0x1e,0x2a,0x13,
  0x13,0x13,0x13,0x27,0x13,0x13,0x13,0x27,0x1e,0x39,0x01,0x01,0x08,
  0x1e,0x03,0x13,0x13,0x13,0x0c,0x0c,0x13,0x13,0x13,0x13,0x1e,0x1e,
  0x01,0x01,0x01,0x33,0x1e,0x22,0x05,0x13,0x2d,0x1e,0x1e,0x05,0x0c,
  0x1e,0x1e,0x15,0x0d,0x0d,0x24,0x1e,0x38,0x01,0x01,0x01,0x3a,0x1e,
  0x15,0x14,0x1f,0x16,0x1e,0x1e,0x26,0x0b,0x03,0x1e,0x02,0x23,0x19,
  0x24,0x1e,0x1e,0x39,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x1e,0x1e,0x13,0x13,0x13,0x1f,0x2d,0x1e,0x35,0x01,0x01,0x1e,0x1e,
  0x13,0x13,0x13,0x13,0x2a,0x1e,0x2f,0x01,0x34,0x1e,0x18,0x14,0x14,
  0x13,0x1f,0x25,0x06,0x05,0x13,0x05,0x23,0x1e,0x1d,0x01,0x1e,0x1e,
  0x05,0x23,0x0c,0x14,0x0a,0x31,0x0c,0x22,0x0b,0x23,0x1e,0x3e,0x01,
  0x01,0x3d,0x1e,0x15,0x14,0x13,0x13,0x0b,0x1e,0x16,0x13,0x13,0x13,
  0x0c,0x18,0x1e,0x34,0x01,0x39,0x1e,0x02,0x13,0x0c,0x0c,0x1a,0x1e,
  0x0c,0x13,0x1f,0x0c,0x31,0x1e,0x36,0x01,0x00,0x1e,0x1e,0x22,0x23,
  0x02,0x22,0x1e,0x15,0x19,0x0b,0x19,0x12,0x1e,0x04,0x01,0x37,0x1e,
  0x26,0x0c,0x27,0x0b,0x05,0x1e,0x24,0x0b,0x0c,0x1f,0x1a,0x1e,0x1e,
  0x00,0x01,0x1b,0x1e,0x0a,0x19,0x22,0x0b,0x23,0x15,0x19,0x23,0x1f,
  0x22,0x1e,0x1e,0x01,0x01,0x1c,0x1e,0x12,0x23,0x19,0x1f,0x0d,0x29,
  0x22,0x1f,0x0c,0x0c,0x2d,0x28,0x0b,0x22,0x1f,0x24,0x1e,0x04,0x01,
  0x01,0x00,0x1e,0x1e,0x14,0x1f,0x0d,0x1a,0x1e,0x25,0x0b,0x13,0x05,
  0x0c,0x1e,0x1e,0x01,0x01,0x3c,0x1e,0x16,0x27,0x06,0x06,0x1e,0x09,
  0x13,0x13,0x13,0x05,0x18,0x1e,0x34,0x01,0x1c,0x1e,0x12,0x13,0x13,
  0x13,0x12,0x31,0x0c,0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,0x01,0x40,
  0x1e,0x14,0x22,0x13,0x2d,0x1e,0x24,0x05,0x0a,0x1e,0x03,0x13,0x1f,
  0x1f,0x13,0x1e,0x1e,0x01,0x01,0x01,0x3a,0x1e,0x15,0x22,0x05,0x0a,
  0x1e,0x1e,0x14,0x14,0x1e,0x0d,0x05,0x0b,0x02,0x13,0x14,0x1e,0x40,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,
  0x13,0x1f,0x2d,0x1e,0x35,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,0x13,
  0x2a,0x1e,0x2f,0x01,0x0e,0x1e,0x10,0x13,0x13,0x0c,0x02,0x1e,0x25,
  0x22,0x13,0x1f,0x0c,0x1e,0x1e,0x01,0x33,0x1e,0x1f,0x05,0x1f,0x0c,
  0x2d,0x1e,0x27,0x05,0x0b,0x06,0x09,0x1e,0x20,0x01,0x38,0x1e,0x19,
  0x14,0x27,0x0c,0x23,0x1e,0x16,0x13,0x13,0x1f,0x05,0x18,0x1e,0x34,
  0x01,0x39,0x1e,0x1a,0x13,0x1f,0x0c,0x0d,0x1e,0x13,0x05,0x13,0x13,
  0x03,0x1e,0x17,0x01,0x00,0x1e,0x1e,0x14,0x0b,0x0c,0x22,0x1e,0x15,
  0x02,0x06,0x1a,0x29,0x1e,0x04,0x01,0x37,0x1e,0x15,0x14,0x27,0x15,
  0x15,0x1e,0x12,0x1a,0x27,0x1f,0x23,0x1e,0x1e,0x00,0x01,0x36,0x1e,
  0x1e,0x22,0x22,0x14,0x13,0x0d,0x02,0x23,0x0b,0x29,0x1e,0x38,0x01,
  0x01,0x08,0x1e,0x21,0x13,0x05,0x0c,0x02,0x13,0x0b,0x23,0x13,0x14,
  0x05,0x0c,0x1f,0x1f,0x0c,0x2a,0x1e,0x2e,0x01,0x01,0x00,0x1e,0x1e,
  0x22,0x05,0x0c,0x14,0x1e,0x0a,0x0c,0x02,0x0d,0x05,0x1e,0x1e,0x01,
  0x01,0x34,0x07,0x1e,0x1e,0x16,0x12,0x24,0x1f,0x13,0x13,0x13,0x0c,
  0x2d,0x1e,0x35,0x01,0x1c,0x1e,0x12,0x13,0x13,0x13,0x15,0x1e,0x05,
  0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,0x01,0x39,0x1e,0x10,0x31,0x21,
  0x1e,0x1e,0x1f,0x23,0x1e,0x1e,0x27,0x13,0x14,0x13,0x1f,0x1e,0x1e,
  0x2b,0x01,0x01,0x36,0x1e,0x18,0x21,0x2a,0x18,0x1e,0x02,0x22,0x1e,
  0x1e,0x14,0x12,0x16,0x02,0x19,0x12,0x10,0x1e,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x13,0x13,0x22,0x1f,0x2d,0x1e,
  0x35,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,0x13,0x2a,0x1e,0x2f,0x01,
  0x0e,0x1e,0x10,0x1f,0x1f,0x13,0x1f,0x1e,0x15,0x13,0x0c,0x19,0x0c,
  0x1e,0x1e,0x01,0x33,0x1e,0x05,0x1f,0x13,0x22,0x03,0x2d,0x23,0x22,
  0x1f,0x0c,0x31,0x1e,0x17,0x01,0x39,0x1e,0x29,0x13,0x05,0x0c,0x22,
  0x1e,0x25,0x19,0x0b,0x14,0x06,0x18,0x1e,0x34,0x01,0x39,0x1e,0x02,
  0x0c,0x02,0x0c,0x0d,0x1e,0x13,0x1f,0x0c,0x05,0x31,0x1e,0x17,0x01,
  0x00,0x1e,0x1e,0x0c,0x0b,0x0b,0x02,0x1e,0x0a,0x02,0x1f,0x0b,0x26,
  0x1e,0x04,0x01,0x37,0x1e,0x06,0x1f,0x19,0x19,0x22,0x1e,0x26,0x19,
  0x26,0x12,0x29,0x1e,0x1e,0x00,0x01,0x01,0x38,0x1e,0x18,0x19,0x24,
  0x13,0x05,0x02,0x19,0x0d,0x1e,0x1e,0x08,0x01,0x01,0x01,0x38,0x1e,
  0x03,0x1f,0x14,0x23,0x13,0x23,0x15,0x27,0x0b,0x13,0x14,0x23,0x13,
  0x10,0x1e,0x3e,0x00,0x01,0x01,0x00,0x1e,0x1e,0x14,0x22,0x29,0x14,
  0x1e,0x16,0x1f,0x0c,0x14,0x14,0x1e,0x1e,0x01,0x01,0x1b,0x1e,0x10,
  0x23,0x0c,0x13,0x13,0x14,0x22,0x13,0x13,0x23,0x09,0x1e,0x20,0x01,
  0x1c,0x1e,0x12,0x13,0x13,0x13,0x06,0x1e,0x1f,0x13,0x13,0x13,0x1e,
  0x1e,0x01,0x01,0x01,0x35,0x39,0x1e,0x1e,0x1e,0x1e,0x12,0x13,0x16,
  0x1e,0x2d,0x05,0x21,0x28,0x05,0x1f,0x1e,0x1e,0x38,0x01,0x01,0x01,
  0x04,0x1e,0x1e,0x1e,0x1e,0x1e,0x27,0x22,0x1e,0x1e,0x1e,0x0b,0x23,
  0x1f,0x0c,0x0b,0x1e,0x1e,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x1e,0x1e,0x0c,0x13,0x0c,0x1f,0x2d,0x1e,0x35,0x01,0x01,0x1e,
  0x1e,0x13,0x13,0x13,0x13,0x2a,0x1e,0x2f,0x01,0x0e,0x1e,0x10,0x1f,
  0x13,0x05,0x22,0x1e,0x16,0x1f,0x13,0x1f,0x13,0x1e,0x1e,0x01,0x33,
  0x1e,0x1f,0x1f,0x0c,0x05,0x2d,0x1e,0x05,0x13,0x19,0x1a,0x1e,0x1e,
  0x00,0x01,0x39,0x1e,0x27,0x02,0x0d,0x05,0x19,0x1e,0x25,0x05,0x0b,
  0x14,0x05,0x18,0x1e,0x34,0x01,0x39,0x1e,0x27,0x13,0x0b,0x0c,0x1a,
  0x1e,0x0c,0x19,0x0c,0x1f,0x31,0x1e,0x17,0x01,0x20,0x1e,0x09,0x05,
  0x22,0x0c,0x05,0x21,0x15,0x19,0x27,0x02,0x06,0x1e,0x04,0x01,0x37,
  0x1e,0x28,0x0d,0x02,0x12,0x06,0x1e,0x26,0x19,0x22,0x05,0x12,0x1e,
  0x1e,0x00,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x27,0x19,0x0a,0x1e,
  0x1e,0x1e,0x3a,0x01,0x01,0x01,0x01,0x2b,0x32,0x1e,0x1e,0x21,0x16,
  0x15,0x1e,0x1e,0x1e,0x1e,0x28,0x25,0x21,0x1e,0x1e,0x38,0x2b,0x01,
  0x01,0x01,0x00,0x1e,0x1e,0x02,0x1f,0x1f,0x05,0x2d,0x26,0x05,0x29,
  0x22,0x0c,0x1e,0x1e,0x01,0x01,0x04,0x1e,0x06,0x1f,0x22,0x13,0x0a,
  0x1e,0x22,0x0c,0x13,0x05,0x09,0x1e,0x20,0x01,0x30,0x1e,0x29,0x13,
  0x13,0x13,0x0a,0x1e,0x14,0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,0x01,
  0x01,0x01,0x0e,0x2e,0x1e,0x09,0x13,0x0b,0x1e,0x1e,0x29,0x0c,0x05,
  0x1f,0x0c,0x0b,0x05,0x1e,0x1e,0x01,0x01,0x01,0x01,0x34,0x2b,0x32,
  0x1e,0x23,0x27,0x1e,0x1e,0x1e,0x19,0x19,0x22,0x29,0x09,0x1e,0x1e,
  0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x14,
  0x13,0x1f,0x1f,0x2d,0x1e,0x35,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,
  0x13,0x2a,0x1e,0x2f,0x01,0x00,0x1e,0x1e,0x23,0x0c,0x13,0x13,0x0d,
  0x27,0x13,0x0b,0x0b,0x02,0x1e,0x38,0x01,0x3e,0x1e,0x02,0x05,0x1f,
  0x02,0x0d,0x06,0x22,0x0c,0x23,0x23,0x1e,0x40,0x01,0x01,0x38,0x1e,
  0x02,0x0c,0x14,0x14,0x22,0x1e,0x2a,0x12,0x14,0x23,0x27,0x18,0x1e,
  0x20,0x01,0x11,0x1e,0x02,0x05,0x0b,0x0c,0x05,0x1e,0x13,0x1f,0x13,
  0x13,0x03,0x1e,0x17,0x01,0x01,0x1e,0x1e,0x05,0x02,0x02,0x19,0x22,
  0x14,0x02,0x0b,0x22,0x29,0x1e,0x04,0x01,0x37,0x1e,0x06,0x27,0x19,
  0x05,0x02,0x1e,0x28,0x1a,0x29,0x29,0x27,0x1e,0x1e,0x00,0x01,0x01,
  0x01,0x2b,0x38,0x1e,0x1e,0x10,0x23,0x22,0x28,0x1e,0x3a,0x01,0x01,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x38,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x33,
  0x1e,0x1f,0x0b,0x02,0x1f,0x1a,0x02,0x0c,0x05,0x1f,0x19,0x1e,0x1e,
  0x01,0x00,0x1e,0x1e,0x27,0x05,0x1f,0x13,0x31,0x26,0x1f,0x14,0x13,
  0x14,0x09,0x1e,0x20,0x01,0x1c,0x1e,0x06,0x13,0x13,0x13,0x02,0x24,
  0x13,0x13,0x13,0x13,0x1e,0x1e,0x01,0x01,0x01,0x01,0x01,0x20,0x07,
  0x1e,0x0d,0x13,0x10,0x1e,0x1e,0x12,0x13,0x05,0x05,0x0c,0x1f,0x1f,
  0x1e,0x1e,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x31,0x1f,0x12,0x1e,
  0x1e,0x10,0x0c,0x19,0x24,0x06,0x24,0x24,0x1e,0x38,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x1f,0x13,0x0c,0x1f,0x2d,
  0x1e,0x35,0x01,0x01,0x1e,0x1e,0x13,0x13,0x13,0x13,0x2a,0x1e,0x2f,
  0x01,0x01,0x3d,0x1e,0x06,0x13,0x05,0x0c,0x13,0x1f,0x13,0x13,0x0c,
  0x21,0x1e,0x0f,0x01,0x3d,0x1e,0x25,0x1f,0x0c,0x13,0x0c,0x23,0x0b,
  0x05,0x29,0x26,0x1e,0x2c,0x01,0x01,0x3d,0x1e,0x12,0x29,0x22,0x0c,
  0x02,0x1e,0x25,0x0b,0x22,0x1f,0x05,0x1e,0x1e,0x00,0x01,0x1c,0x1e,
  0x06,0x13,0x05,0x0c,0x0b,0x1f,0x23,0x19,0x1f,0x0b,0x03,0x1e,0x17,
  0x01,0x01,0x1e,0x1e,0x05,0x13,0x0c,0x19,0x0b,0x22,0x29,0x29,0x02,
  0x12,0x1e,0x04,0x01,0x37,0x1e,0x26,0x14,0x0b,0x27,0x27,0x1e,0x28,
  0x1a,0x05,0x02,0x24,0x09,0x1e,0x20,0x01,0x01,0x01,0x01,0x11,0x1e,
  0x1a,0x03,0x31,0x1f,0x24,0x1e,0x04,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x2b,0x08,0x1b,0x36,0x01,0x01,0x2b,0x1b,0x08,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x27,0x0b,0x22,
  0x1f,0x14,0x0c,0x14,0x27,0x0c,0x19,0x1e,0x1e,0x01,0x01,0x2c,0x1e,
  0x12,0x13,0x0b,0x13,0x13,0x13,0x14,0x1f,0x13,0x0b,0x1e,0x1e,0x2f,
  0x01,0x3c,0x1e,0x09,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x23,
  0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x3c,0x1e,0x16,0x0c,0x22,0x1e,
  0x32,0x1e,0x1e,0x1e,0x1e,0x22,0x14,0x23,0x1e,0x1e,0x38,0x01,0x01,
  0x01,0x01,0x01,0x1e,0x1e,0x19,0x1f,0x1e,0x1e,0x1e,0x31,0x1f,0x05,
  0x0c,0x1f,0x0b,0x1f,0x1e,0x1e,0x00,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x34,0x1e,0x1e,0x22,0x13,0x13,0x13,0x21,0x1e,0x2b,0x01,0x01,
  0x1e,0x1e,0x13,0x1f,0x13,0x13,0x15,0x1e,0x1b,0x01,0x01,0x2f,0x1e,
  0x1e,0x29,0x05,0x0c,0x1f,0x05,0x13,0x0c,0x16,0x1e,0x38,0x01,0x01,
  0x20,0x40,0x1e,0x15,0x05,0x05,0x0c,0x05,0x05,0x0c,0x06,0x1e,0x1e,
  0x2f,0x01,0x01,0x3b,0x1e,0x10,0x05,0x1f,0x19,0x22,0x26,0x29,0x19,
  0x13,0x0b,0x02,0x1e,0x38,0x01,0x01,0x08,0x1e,0x16,0x05,0x1f,0x13,
  0x14,0x13,0x13,0x05,0x0c,0x02,0x31,0x1e,0x17,0x01,0x01,0x38,0x1e,
  0x21,0x14,0x1f,0x14,0x14,0x27,0x0c,0x22,0x27,0x27,0x1e,0x3e,0x01,
  0x3a,0x1e,0x15,0x0d,0x23,0x23,0x24,0x25,0x0b,0x23,0x19,0x27,0x0d,
  0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x33,0x1e,0x1f,0x0c,0x22,0x05,
  0x21,0x1e,0x2e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x37,0x1e,0x2d,0x0d,0x19,0x1f,0x0b,0x29,0x14,
  0x22,0x1f,0x05,0x1e,0x1e,0x01,0x01,0x1b,0x1e,0x09,0x22,0x22,0x13,
  0x13,0x15,0x13,0x22,0x13,0x13,0x24,0x1e,0x37,0x01,0x01,0x38,0x1e,
  0x21,0x13,0x1f,0x13,0x13,0x13,0x13,0x23,0x1e,0x1e,0x1c,0x01,0x01,
  0x01,0x01,0x01,0x36,0x1e,0x31,0x15,0x1e,0x1e,0x30,0x1b,0x38,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x20,0x1e,
  0x09,0x15,0x21,0x1e,0x38,0x1d,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,0x01,0x38,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x2b,0x01,0x01,0x01,0x3b,0x1e,0x1e,0x1e,0x31,
  0x25,0x28,0x10,0x1e,0x1e,0x07,0x2b,0x01,0x01,0x01,0x36,0x1e,0x1e,
  0x1e,0x2d,0x0a,0x16,0x10,0x1e,0x1e,0x1e,0x2f,0x01,0x01,0x01,0x01,
  0x1e,0x1e,0x27,0x0c,0x19,0x12,0x13,0x05,0x22,0x14,0x14,0x16,0x1e,
  0x37,0x01,0x01,0x20,0x07,0x1e,0x19,0x0c,0x1f,0x0b,0x14,0x22,0x1f,
  0x14,0x23,0x0a,0x1e,0x3b,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x28,0x2a,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x04,0x01,0x36,0x1e,0x18,0x29,
  0x06,0x29,0x1a,0x23,0x27,0x19,0x0b,0x12,0x15,0x1e,0x1c,0x01,0x01,
  0x01,0x01,0x01,0x11,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x38,0x1e,0x1e,0x16,0x25,0x18,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x01,0x01,0x01,0x38,0x1e,0x1e,0x31,0x15,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x2f,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x18,0x25,
  0x28,0x03,0x1e,0x1e,0x1e,0x1c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x1d,0x1e,0x1e,0x1e,0x30,0x01,0x01,0x01,0x00,0x3d,0x32,0x1e,0x1e,
  0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x1e,0x1e,0x38,
  0x2b,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,
  0x38,0x2b,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x3b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,
  0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x36,0x38,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x38,0x2f,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x0b,
  0x05,0x0c,0x1f,0x0b,0x1f,0x0b,0x03,0x1e,0x07,0x34,0x01,0x01,0x01,
  0x30,0x1e,0x1e,0x31,0x0a,0x31,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x33,
  0x35,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x33,0x38,0x1e,0x1e,
  0x1e,0x3e,0x04,0x34,0x01,0x01,0x38,0x1e,0x1e,0x05,0x05,0x27,0x1a,
  0x0d,0x26,0x0d,0x25,0x1e,0x1e,0x3c,0x01,0x01,0x01,0x01,0x01,0x0e,
  0x11,0x33,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,
  0x1e,0x1e,0x1e,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x1e,0x1e,0x1e,0x1e,0x38,0x2f,
  0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1d,
  0x1c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x36,0x1b,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x1b,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x17,0x08,0x1b,0x2b,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x0e,0x2e,0x08,0x0e,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x18,0x16,0x25,0x25,
  0x21,0x1e,0x1e,0x3e,0x2b,0x01,0x01,0x01,0x01,0x01,0x30,0x32,0x1e,
  0x1e,0x1e,0x3e,0x3e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2b,0x1b,0x3c,0x34,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x2b,0x38,0x1e,0x1e,0x18,0x2a,0x2a,0x16,0x21,0x1e,0x1e,
  0x33,0x3c,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x3c,0x2e,0x0e,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x2f,
  0x1b,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x20,0x08,0x2e,0x17,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x00,0x3d,0x3e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x36,0x3b,0x36,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,
  0x11,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x20,0x3c,0x08,0x08,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x08,
  0x08,0x08,0x36,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x34,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x20,0x0e,0x20,0x0e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x36,0x1b,0x0f,0x17,0x01,0x01,0x01,0x01,0x01,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,
  0x0f,0x11,0x1d,0x04,0x2e,0x34,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x3d,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x2c,0x1e,0x1e,
  0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,0x38,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x09,0x13,0x13,0x13,
  0x13,0x13,0x13,0x13,0x13,0x13,0x3f,0x18,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1b,0x32,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x3e,0x2b,0x2b,0x32,0x1e,0x18,0x1e,0x1e,0x20,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x04,0x1e,0x09,0x10,0x09,0x10,0x1e,0x32,
  0x00,0x01,0x01,0x01,0x01,0x01,0x38,0x1e,0x1e,0x21,0x15,0x06,0x10,
  0x1e,0x1e,0x07,0x2b,0x01,0x09,0x13,0x13,0x13,0x13,0x13,0x13,0x13,
  0x13,0x13,0x3f,0x18,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x1b,0x1e,0x1e,0x2a,0x19,0x0b,0x1a,0x2a,0x1e,0x1e,0x38,
  0x1e,0x03,0x13,0x25,0x1e,0x08,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x07,0x1e,0x22,0x13,0x0c,0x13,0x03,0x1e,0x17,0x01,0x01,0x01,
  0x01,0x1b,0x1e,0x1e,0x14,0x13,0x13,0x13,0x13,0x13,0x25,0x1e,0x38,
  0x01,0x09,0x13,0x16,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x24,0x0c,0x1e,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2c,0x1e,
  0x27,0x13,0x13,0x13,0x13,0x13,0x28,0x1e,0x1e,0x1e,0x13,0x13,0x1e,
  0x1e,0x20,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x11,0x1e,0x02,
  0x13,0x0b,0x1f,0x10,0x1e,0x0e,0x01,0x01,0x01,0x01,0x2c,0x1e,0x0d,
  0x02,0x14,0x19,0x05,0x23,0x14,0x13,0x1e,0x1e,0x01,0x09,0x13,0x16,
  0x2c,0x00,0x00,0x00,0x00,0x11,0x24,0x0c,0x1e,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x34,0x1e,0x18,0x28,0x12,0x31,0x06,
  0x13,0x13,0x02,0x1e,0x1e,0x0a,0x13,0x0a,0x1e,0x38,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x32,0x1e,0x0b,0x13,0x0c,0x13,0x31,
  0x1e,0x36,0x01,0x01,0x01,0x01,0x2c,0x1e,0x29,0x13,0x0b,0x18,0x2a,
  0x19,0x12,0x14,0x1e,0x1e,0x01,0x09,0x13,0x16,0x2c,0x00,0x00,0x00,
  0x00,0x11,0x24,0x0c,0x1e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x04,0x1e,0x1e,0x1e,0x27,0x13,0x13,0x13,0x18,0x1e,
  0x1e,0x13,0x13,0x1e,0x1e,0x3d,0x3d,0x2e,0x2b,0x01,0x01,0x01,0x01,
  0x01,0x01,0x30,0x1e,0x15,0x27,0x12,0x19,0x09,0x1e,0x20,0x01,0x01,
  0x01,0x01,0x2c,0x1e,0x12,0x05,0x05,0x09,0x31,0x0b,0x0c,0x1f,0x1e,
  0x1e,0x01,0x09,0x13,0x16,0x2c,0x00,0x00,0x00,0x00,0x11,0x24,0x0c,
  0x1e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x1e,
  0x1e,0x18,0x25,0x06,0x0d,0x13,0x13,0x15,0x1e,0x19,0x13,0x03,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x2b,0x01,0x01,0x01,0x01,0x0e,0x11,0x1e,
  0x09,0x16,0x03,0x2a,0x1e,0x1d,0x01,0x01,0x01,0x01,0x01,0x2c,0x1e,
  0x0d,0x0c,0x24,0x06,0x19,0x02,0x19,0x1f,0x1e,0x1e,0x01,0x09,0x13,
  0x16,0x39,0x00,0x00,0x00,0x00,0x11,0x24,0x1f,0x1e,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x35,0x1e,0x2d,0x13,0x13,0x0a,
  0x06,0x13,0x13,0x0b,0x1e,0x13,0x1f,0x1e,0x1e,0x12,0x0d,0x0d,0x28,
  0x1e,0x2c,0x01,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x05,0x05,0x0c,0x13,
  0x03,0x1e,0x17,0x01,0x01,0x01,0x01,0x3a,0x1e,0x2d,0x0b,0x14,0x23,
  0x22,0x14,0x1f,0x28,0x1e,0x38,0x01,0x09,0x19,0x2a,0x39,0x00,0x00,
  0x00,0x00,0x11,0x24,0x22,0x1e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x38,0x1e,0x0d,0x13,0x13,0x13,0x13,0x13,0x1e,
  0x22,0x13,0x1e,0x1e,0x16,0x13,0x13,0x13,0x1f,0x1e,0x40,0x01,0x01,
  0x01,0x38,0x1e,0x2d,0x1f,0x05,0x05,0x13,0x0c,0x1e,0x1e,0x40,0x39,
  0x17,0x01,0x01,0x01,0x38,0x1e,0x21,0x28,0x24,0x0d,0x06,0x31,0x1e,
  0x1e,0x17,0x01,0x09,0x05,0x03,0x39,0x00,0x00,0x00,0x00,0x38,0x24,
  0x1f,0x1e,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x2e,0x1e,0x1e,0x09,0x03,0x16,0x31,0x1e,0x1e,0x13,0x23,0x1e,0x1e,
  0x14,0x13,0x1f,0x13,0x23,0x1e,0x38,0x01,0x01,0x35,0x1e,0x09,0x19,
  0x0c,0x1f,0x23,0x13,0x24,0x1e,0x1e,0x1e,0x1e,0x1e,0x17,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x17,0x01,0x01,0x18,
  0x12,0x2a,0x11,0x00,0x00,0x00,0x00,0x38,0x06,0x05,0x1e,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2e,0x38,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x0c,0x13,0x1e,0x1e,0x2a,0x13,0x21,0x2a,0x13,
  0x29,0x1e,0x1e,0x1c,0x01,0x08,0x1e,0x25,0x1f,0x13,0x13,0x13,0x1e,
  0x0a,0x22,0x05,0x19,0x26,0x1e,0x3a,0x01,0x01,0x01,0x01,0x3c,0x1c,
  0x2c,0x2c,0x1c,0x3c,0x01,0x01,0x01,0x01,0x09,0x0b,0x2a,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x06,0x1f,0x1e,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x17,0x38,0x1e,0x2a,
  0x13,0x26,0x1e,0x1e,0x23,0x0c,0x0b,0x05,0x22,0x13,0x23,0x1e,0x38,
  0x01,0x08,0x1e,0x16,0x0b,0x13,0x1f,0x1f,0x1e,0x14,0x0c,0x0c,0x13,
  0x15,0x1e,0x1b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x18,0x19,0x13,0x0b,0x05,0x0c,0x0b,0x14,0x05,
  0x1f,0x05,0x18,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x0f,0x1e,0x1e,0x0c,0x13,0x1e,0x1e,0x1e,
  0x0b,0x0c,0x23,0x13,0x13,0x13,0x22,0x1e,0x33,0x01,0x2b,0x1e,0x21,
  0x0c,0x13,0x1f,0x13,0x0b,0x23,0x13,0x05,0x05,0x1e,0x1e,0x36,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x09,0x22,0x14,0x14,0x13,0x1a,0x19,0x13,0x02,0x05,0x05,0x18,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x11,0x1e,0x19,0x13,0x25,0x1e,0x38,0x1e,0x1e,0x1e,0x1e,0x0b,
  0x23,0x13,0x1e,0x1e,0x11,0x01,0x01,0x38,0x1e,0x21,0x13,0x05,0x13,
  0x13,0x1f,0x13,0x14,0x21,0x1e,0x38,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1e,0x1e,0x1e,0x1e,
  0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0f,0x1e,0x16,
  0x28,0x1e,0x40,0x2b,0x04,0x3e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,
  0x0e,0x01,0x01,0x2b,0x38,0x1e,0x1e,0x21,0x0a,0x16,0x25,0x03,0x1e,
  0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x20,0x07,0x1e,0x1e,0x1e,0x2b,0x01,
  0x01,0x01,0x01,0x1c,0x1d,0x1e,0x1e,0x38,0x2b,0x01,0x01,0x01,0x01,
  0x2b,0x38,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x38,0x2b,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x20,0x3c,0x2e,0x2b,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x2b,
  0x3b,0x3c,0x08,0x17,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
  0x00,0x00,0x00,0x02,0xff,0xff,0xff,0x00,0xb3,0xb3,0xb3,0x80,0x3b,
  0x3b,0x3b,0x80,0x00,0x00,0x00,0x52,0xe4,0xe4,0xe4,0x80,0x7b,0x7b,
  0x7b,0x80,0x00,0x00,0x00,0x72,0x00,0x00,0x00,0x2a,0x14,0x14,0x14,
  0x80,0x5b,0x5b,0x5b,0x80,0xcc,0xcc,0xcc,0x80,0xf4,0xf4,0xf4,0x80,
  0x9b,0x9b,0x9b,0x80,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3e,0x23,
  0x23,0x23,0x80,0x00,0x00,0x00,0x5e,0x8c,0x8c,0x8c,0x80,0xfe,0xfe,
  0xfe,0x80,0xdc,0xdc,0xdc,0x80,0x6b,0x6b,0x6b,0x80,0x4b,0x4b,0x4b,
  0x80,0x00,0x00,0x00,0x1e,0x0b,0x0b,0x0b,0x80,0xbc,0xbc,0xbc,0x80,
  0xa4,0xa4,0xa4,0x80,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x46,0x00,
  0x00,0x00,0x66,0x00,0x00,0x00,0x80,0xec,0xec,0xec,0x80,0x00,0x00,
  0x00,0x0a,0x2b,0x2b,0x2b,0x80,0xd4,0xd4,0xd4,0x80,0xc4,0xc4,0xc4,
  0x80,0x84,0x84,0x84,0x80,0x54,0x54,0x54,0x80,0x74,0x74,0x74,0x80,
  0xac,0xac,0xac,0x80,0x64,0x64,0x64,0x80,0x94,0x94,0x94,0x80,0x44,
  0x44,0x44,0x80,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x56,0x1b,0x1b,
  0x1b,0x80,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x22,0x00,0x00,0x00,
  0x4a,0x33,0x33,0x33,0x80,0x00,0x00,0x00,0x6a,0x00,0x00,0x00,0x7a,
  0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x1a,0x00,
  0x00,0x00,0x42,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x5a,0x00,0x00,
  0x00,0x3a,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x26,0x00,0x00,0x00,
  0x4e,0x00,0x00,0x00,0x6e,0xe7,0xe8,0xe8,0x80,0x00,0x00,0x00,0x76,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x00,0x00,
  0x00,0x01,0x00,0x01,0x00,0x11,0x00,0x11,0x00,0x13,0x00,0x01,0x00,
  0x10,0x00,0x11,0x00,0x24,0x00,0x01,0x00,0x10,0x00,0x12,0x00,0x35,
  0x00,0x01,0x00,0x0f,0x00,0x11,0x00,0x45,0x00,0x01,0x00,0x0e,0x00,
  0x11,0x00,0x54,0x00,0x01,0x00,0x0e,0x00,0x11,0x00,0x63,0x00,0x01,
  0x00,0x11,0x00,0x12,0x00,0x75,0x00,0x01,0x00,0x0f,0x00,0x11,0x00,
  0x85,0x00,0x01,0x00,0x0a,0x00,0x11,0x00,0x90,0x00,0x01,0x00,0x0f,
  0x00,0x12,0x00,0xa0,0x00,0x01,0x00,0x10,0x00,0x11,0x00,0xb1,0x00,
  0x01,0x00,0x0d,0x00,0x11,0x00,0xbf,0x00,0x01,0x00,0x13,0x00,0x11,
  0x00,0xd3,0x00,0x01,0x00,0x11,0x00,0x11,0x00,0xe5,0x00,0x01,0x00,
  0x10,0x00,0x12,0x00,0x01,0x00,0x1a,0x00,0x10,0x00,0x11,0x00,0x12,
  0x00,0x1a,0x00,0x10,0x00,0x15,0x00,0x23,0x00,0x1a,0x00,0x10,0x00,
  0x11,0x00,0x34,0x00,0x1a,0x00,0x10,0x00,0x12,0x00,0x45,0x00,0x1a,
  0x00,0x0f,0x00,0x11,0x00,0x55,0x00,0x1a,0x00,0x0f,0x00,0x12,0x00,
  0x65,0x00,0x1a,0x00,0x11,0x00,0x11,0x00,0x77,0x00,0x1a,0x00,0x16,
  0x00,0x11,0x00,0x8e,0x00,0x1a,0x00,0x11,0x00,0x12,0x00,0xa0,0x00,
  0x1a,0x00,0x10,0x00,0x11,0x00,0xb1,0x00,0x1a,0x00,0x0f,0x00,0x11,
  0x00,0xc1,0x00,0x1a,0x00,0x10,0x00,0x0f,0x00,0xd2,0x00,0x1a,0x00,
  0x0f,0x00,0x12,0x00,0xe2,0x00,0x1a,0x00,0x0f,0x00,0x0f,0x00,0x01,
  0x00,0x33,0x00,0x10,0x00,0x12,0x00,0x12,0x00,0x33,0x00,0x0f,0x00,
  0x0f,0x00,0x22,0x00,0x33,0x00,0x0c,0x00,0x11,0x00,0x2f,0x00,0x33,
  0x00,0x0f,0x00,0x12,0x00,0x3f,0x00,0x33,0x00,0x0e,0x00,0x11,0x00,
  0x4e,0x00,0x33,0x00,0x09,0x00,0x11,0x00,0x58,0x00,0x33,0x00,0x0b,
  0x00,0x15,0x00,0x64,0x00,0x33,0x00,0x10,0x00,0x11,0x00,0x75,0x00,
  0x33,0x00,0x09,0x00,0x11,0x00,0x7f,0x00,0x33,0x00,0x14,0x00,0x0e,
  0x00,0x94,0x00,0x33,0x00,0x0e,0x00,0x0e,0x00,0xa3,0x00,0x33,0x00,
  0x10,0x00,0x0f,0x00,0xb4,0x00,0x33,0x00,0x0f,0x00,0x12,0x00,0xc4,
  0x00,0x33,0x00,0x0f,0x00,0x12,0x00,0xd4,0x00,0x33,0x00,0x0d,0x00,
  0x0e,0x00,0xe2,0x00,0x33,0x00,0x0f,0x00,0x0f,0x00,0xf2,0x00,0x33,
  0x00,0x0d,0x00,0x10,0x00,0x01,0x00,0x4c,0x00,0x0f,0x00,0x0f,0x00,
  0x11,0x00,0x4c,0x00,0x0f,0x00,0x0e,0x00,0x21,0x00,0x4c,0x00,0x16,
  0x00,0x0e,0x00,0x38,0x00,0x4c,0x00,0x0f,0x00,0x0e,0x00,0x48,0x00,
  0x4c,0x00,0x10,0x00,0x12,0x00,0x59,0x00,0x4c,0x00,0x0f,0x00,0x0e,
  0x00,0x69,0x00,0x4c,0x00,0x10,0x00,0x11,0x00,0x7a,0x00,0x4c,0x00,
  0x0c,0x00,0x10,0x00,0x87,0x00,0x4c,0x00,0x0f,0x00,0x10,0x00,0x97,
  0x00,0x4c,0x00,0x10,0x00,0x11,0x00,0xa8,0x00,0x4c,0x00,0x10,0x00,
  0x10,0x00,0xb9,0x00,0x4c,0x00,0x10,0x00,0x11,0x00,0xca,0x00,0x4c,
  0x00,0x10,0x00,0x12,0x00,0xdb,0x00,0x4c,0x00,0x0f,0x00,0x10,0x00,
  0xeb,0x00,0x4c,0x00,0x0f,0x00,0x11,0x00,0x01,0x00,0x65,0x00,0x0f,
  0x00,0x12,0x00,0x11,0x00,0x65,0x00,0x09,0x00,0x09,0x00,0x1b,0x00,
  0x65,0x00,0x09,0x00,0x0c,0x00,0x25,0x00,0x65,0x00,0x0b,0x00,0x09,
  0x00,0x31,0x00,0x65,0x00,0x0a,0x00,0x12,0x00,0x3c,0x00,0x65,0x00,
  0x0e,0x00,0x12,0x00,0x4b,0x00,0x65,0x00,0x09,0x00,0x0e,0x00,0x55,
  0x00,0x65,0x00,0x07,0x00,0x0a,0x00,0x5d,0x00,0x65,0x00,0x0f,0x00,
  0x0f,0x00,0x6d,0x00,0x65,0x00,0x0e,0x00,0x13,0x00,0x7c,0x00,0x65,
  0x00,0x0e,0x00,0x0a,0x00,0x8b,0x00,0x65,0x00,0x11,0x00,0x11,0x00,
  0x9d,0x00,0x65,0x00,0x0b,0x00,0x13,0x00,0xa9,0x00,0x65,0x00,0x0c,
  0x00,0x13,0x00,0xb6,0x00,0x65,0x00,0x0c,0x00,0x0c,0x00,0xc3,0x00,
  0x65,0x00,0x14,0x00,0x14,0x00,0xd8,0x00,0x65,0x00,0x09,0x00,0x09,
  0x00,0xe2,0x00,0x65,0x00,0x0a,0x00,0x11,0x00,0xed,0x00,0x65,0x00,
  0x0f,0x00,0x12,0x00,0x01,0x00,0x7e,0x00,0x0f,0x00,0x11,0x00,0x11,
  0x00,0x7e,0x00,0x11,0x00,0x11,0x00,0x23,0x00,0x7e,0x00,0x10,0x00,
  0x10,0x00,0x34,0x00,0x7e,0x00,0x08,0x00,0x14,0x00,0x3d,0x00,0x7e,
  0x00,0x0f,0x00,0x15,0x00,0x4d,0x00,0x7e,0x00,0x0a,0x00,0x08,0x00,
  0x58,0x00,0x7e,0x00,0x10,0x00,0x11,0x00,0x69,0x00,0x7e,0x00,0x0d,
  0x00,0x0c,0x00,0x77,0x00,0x7e,0x00,0x0e,0x00,0x0b,0x00,0x86,0x00,
  0x7e,0x00,0x0f,0x00,0x0a,0x00,0x96,0x00,0x7e,0x00,0x0c,0x00,0x15,
  0x00,0xa3,0x00,0x7e,0x00,0x0e,0x00,0x07,0x00,0xb2,0x00,0x7e,0x00,
  0x12,0x00,0x11,0x00,0xc5,0x00,0x7e,0x00,0x0f,0x00,0x13,0x00,0xd5,
  0x00,0x7e,0x00,0x13,0x00,0x12,0x00,0xe9,0x00,0x7e,0x00,0x13,0x00,
  0x12,0x00,0x01,0x00,0x97,0x00,0x0e,0x00,0x14,0x00,0x10,0x00,0x97,
  0x00,0x0f,0x00,0x0c,0x00,0x20,0x00,0x97,0x00,0x0f,0x00,0x0f,0x00,
  0x30,0x00,0x97,0x00,0x0f,0x00,0x0f,0x00,0x40,0x00,0x97,0x00,0x0f,
  0x00,0x11,0x00,0x50,0x00,0x97,0x00,0x11,0x00,0x14,0x00,0x62,0x00,
  0x97,0x00,0x0f,0x00,0x15,0x00,0x72,0x00,0x97,0x00,0x10,0x00,0x15,
  0x00,0x83,0x00,0x97,0x00,0x0f,0x00,0x13,0x00,0x93,0x00,0x97,0x00,
  0x0f,0x00,0x13,0x00,0xa3,0x00,0x97,0x00,0x11,0x00,0x14,0x00,0xb5,
  0x00,0x97,0x00,0x0f,0x00,0x13,0x00,0xc5,0x00,0x97,0x00,0x0f,0x00,
  0x14,0x00,0xd5,0x00,0x97,0x00,0x0f,0x00,0x14,0x00,0xe5,0x00,0x97,
  0x00,0x0f,0x00,0x13,0x00,0xf5,0x00,0x97,0x00,0x0a,0x00,0x12,0x00,
  0x01,0x00,0xb0,0x00,0x0a,0x00,0x13,0x00,0x0c,0x00,0xb0,0x00,0x0a,
  0x00,0x12,0x00,0x17,0x00,0xb0,0x00,0x0f,0x00,0x13,0x00,0x27,0x00,
  0xb0,0x00,0x0f,0x00,0x13,0x00,0x37,0x00,0xb0,0x00,0x10,0x00,0x15,
  0x00,0x48,0x00,0xb0,0x00,0x0f,0x00,0x14,0x00,0x58,0x00,0xb0,0x00,
  0x0f,0x00,0x13,0x00,0x68,0x00,0xb0,0x00,0x10,0x00,0x15,0x00,0x79,
  0x00,0xb0,0x00,0x0f,0x00,0x12,0x00,0x89,0x00,0xb0,0x00,0x15,0x00,
  0x0f,0x00,0x9f,0x00,0xb0,0x00,0x0f,0x00,0x13,0x00,0xaf,0x00,0xb0,
  0x00,0x10,0x00,0x13,0x00,0xc0,0x00,0xb0,0x00,0x0e,0x00,0x13,0x00,
  0xcf,0x00,0xb0,0x00,0x15,0x00,0x12,0x00,0xe5,0x00,0xb0,0x00,0x15,
  0x00,0x12,0x00,0x01,0x00,0xc9,0x00,0x16,0x00,0x12,0x00,0x18,0x00,
  0xc9,0x00,0x0f,0x00,0x12,0x00,0x28,0x00,0xc9,0x00,0x0c,0x00,0x0c,
  0x00,0x35,0x00,0xc9,0x00,0x0c,0x00,0x0f,0x00,0x00
  };



================================================
FILE: Code/Gfx/XBox/p_NxGeom.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxGeom.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  3/4/2002
//****************************************************************************


#include 

#include 

#include 
#include 
#include 
#include 
#include 

#include 


namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxGeom::CXboxGeom() : mp_init_mesh_list( NULL ), m_mesh_array( NULL ), m_visible( 0xDEADBEEF )
{
	mp_instance = NULL;
//	mp_sector	= NULL;
	m_scale.Set( 1.0f, 1.0f, 1.0f );
	m_active	= true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxGeom::~CXboxGeom( void )
{
	DestroyMeshArray();

	if( mp_instance != NULL )
	{
		delete mp_instance;
		mp_instance = NULL;
	}
}



/////////////////////////////////////////////////////////////////////////////////////
// Mesh List Functions
//
// The mesh list is only for initialization of the CGeom.  As we find all the
// meshes for the CGeoms, we add them to the temporary lists.  When we are
// done, the list is copied into a permanent array.
//
// All the list functions use the TopDownHeap for memory allocation.

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::InitMeshList( void )
{
	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
	mp_init_mesh_list = new Lst::Head< NxXbox::sMesh >;
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::AddMesh( NxXbox::sMesh *mesh )
{
	Dbg_Assert( mp_init_mesh_list );

	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
	Lst::Node< NxXbox::sMesh > *node = new Lst::Node< NxXbox::sMesh > (mesh);
	mp_init_mesh_list->AddToTail( node );
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::CreateMeshArray( void )
{
	if( !m_mesh_array )
	{
		Dbg_Assert( mp_init_mesh_list );

		m_num_mesh = mp_init_mesh_list->CountItems();
		if (m_num_mesh)
		{
			Lst::Node< NxXbox::sMesh > *mesh, *next;

			m_mesh_array = new NxXbox::sMesh*[m_num_mesh];
			int k = 0;
			for( mesh = mp_init_mesh_list->GetNext(); mesh; mesh = next )
			{
				next			= mesh->GetNext();
				m_mesh_array[k]	= mesh->GetData();
				delete mesh;
				k++;
			}
		}

		// Delete temporary list.
		delete mp_init_mesh_list;
		mp_init_mesh_list = NULL;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxGeom::RegisterMeshArray( bool just_count )
{
	// Tells the engine to count or add the meshes for this sector to the rendering list for supplied scene id.
	if( just_count )
	{
		mp_scene->GetEngineScene()->CountMeshes( m_num_mesh, m_mesh_array );
	}
	else
	{
		mp_scene->GetEngineScene()->AddMeshes( m_num_mesh, m_mesh_array );
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::DestroyMeshArray( void )
{
	if( m_mesh_array )
	{
		// Tells the engine to remove the meshes for this sector from the rendering list for supplied scene id.
		if( mp_scene && mp_scene->GetEngineScene())
		{
			mp_scene->GetEngineScene()->RemoveMeshes( m_num_mesh, m_mesh_array );
		}

		// Now actually go through and delete each mesh.
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[m];
			delete p_mesh;
		}

		delete [] m_mesh_array;
		m_mesh_array = NULL;
	}
}



/******************************************************************/
/*                                                                */
/* Generates an entirely new engine scene.                        */
/* Used when instance-cloning CGeoms.                             */
/*                                                                */
/******************************************************************/
NxXbox::sScene* CXboxGeom::GenerateScene( void )
{
	NxXbox::sScene *p_scene = new NxXbox::sScene();

	p_scene->CountMeshes( m_num_mesh, m_mesh_array );
	p_scene->CreateMeshArrays();
	p_scene->AddMeshes( m_num_mesh, m_mesh_array );
	p_scene->SortMeshes();

	// Set the bounding box and bounding sphere.
	p_scene->m_bbox = m_bbox;
	p_scene->FigureBoundingVolumes();
	
	return p_scene;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxGeom::plat_load_geom_data( CMesh *pMesh, CModel *pModel, bool color_per_material )
{
	// The skeleton must exist by this point (unless it's a hacked up car).
	int			numBones = pModel->GetNumBones();
	Mth::Matrix	temp;
	
	temp.Identity();

	CXboxMesh *p_xbox_mesh = static_cast( pMesh );
	
	// Store a pointer to the CXboxMesh, used for obtaining CAS poly removal data.
	mp_mesh = p_xbox_mesh;

	CXboxModel *p_xbox_model = static_cast( pModel );

	NxXbox::CInstance *p_instance = new NxXbox::CInstance( p_xbox_mesh->GetScene()->GetEngineScene(), temp, numBones, pModel->GetBoneTransforms());
	
	SetInstance( p_instance );
	p_instance->SetModel( pModel );

    return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_finalize( void )
{
	// Scan through and remove degenerate indices...
	if( mp_instance )
	{
		NxXbox::sScene *p_scene = mp_instance->GetScene();
		if( p_scene )
		{
			for( int m = 0; m < p_scene->m_num_mesh_entries; ++m )
			{
				NxXbox::sMesh *p_mesh = p_scene->m_meshes[m];
				p_mesh->Crunch();
			}
		}
	}	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_active( bool active )
{
	if( mp_instance )
	{
		mp_instance->SetActive( active );
	}	
	else
	{
		m_active = active;
		if( m_mesh_array )
		{
			for( uint m = 0; m < m_num_mesh; ++m )
			{
				NxXbox::sMesh *p_mesh = m_mesh_array[m];
				p_mesh->SetActive( active );
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxGeom::plat_is_active( void ) const
{
	if( mp_instance )
	{
		return mp_instance->GetActive();
	}	
	return m_active;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector &CXboxGeom::plat_get_world_position( void ) const
{
	static Mth::Vector pos;
	
	if( mp_instance )
	{
		pos = mp_instance->GetTransform()->GetPos();
	}
	else
	{
		pos.Set( 0.0f, 0.0f, 0.0f, 1.0f );

		NxXbox::sMesh *p_mesh = m_mesh_array[0];
		if( p_mesh )
		{
			p_mesh->GetPosition( &pos );
		}
	}
	Dbg_Assert( pos[W] == 1.0f );
	return pos;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_world_position( const Mth::Vector& pos )
{
	// Ensure w component is correct.
	Mth::Vector proper_pos( pos[X], pos[Y], pos[Z], 1.0f );

	if( mp_instance )
	{
		mp_instance->GetTransform()->SetPos( proper_pos );
	}
	else
	{
		// Go through and adjust the individual meshes.
		for( uint32 i = 0; i < m_num_mesh; ++i )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[i];
			p_mesh->SetPosition( proper_pos );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::CBBox & CXboxGeom::plat_get_bounding_box( void ) const
{
	return m_bbox;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_bounding_sphere( const Mth::Vector& boundingSphere )
{
	if( mp_instance )
	{
		NxXbox::sScene *p_scene = mp_instance->GetScene();
		if( p_scene )
		{
			p_scene->m_sphere_center.x	= boundingSphere[X];
			p_scene->m_sphere_center.y	= boundingSphere[Y];
			p_scene->m_sphere_center.z	= boundingSphere[Z];
			p_scene->m_sphere_radius	= boundingSphere[W];
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector CXboxGeom::plat_get_bounding_sphere( void ) const
{
	if( mp_instance )
	{
		NxXbox::sScene *p_scene = mp_instance->GetScene();
		if( p_scene )
		{
			return Mth::Vector( p_scene->m_sphere_center.x, p_scene->m_sphere_center.y,  p_scene->m_sphere_center.z, p_scene->m_sphere_radius );
		}
	}
	
	return Mth::Vector( 0.0f, 0.0f, 0.0f, 10000.0f );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_orientation( const Mth::Matrix& orient )
{
	if( mp_instance )
	{
		Mth::Matrix *p_matrix = mp_instance->GetTransform();
		Mth::Matrix new_orientation = *p_matrix;
		new_orientation[X] = orient[X];
		new_orientation[Y] = orient[Y];
		new_orientation[Z] = orient[Z];
		mp_instance->SetTransform( new_orientation );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Matrix &CXboxGeom::plat_get_orientation( void ) const
{
	static Mth::Matrix orientation;
	
	if( mp_instance )
	{
		orientation = *( mp_instance->GetTransform());
		orientation[W] = Mth::Vector( 0.0f, 0.0f, 0.0f, 1.0f );
	}
	else
	{
		orientation.Identity();
	}

	return orientation;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_rotate_y( Mth::ERot90 rot )
{
	if( rot != Mth::ROT_0 )
	{
		if( mp_instance )
		{
			Mth::Matrix *p_matrix = mp_instance->GetTransform();

			// Zero out translation component.
			Mth::Matrix	instance_transform = *p_matrix;
			instance_transform[W] = Mth::Vector(0.0f, 0.0f, 0.0f, 1.0f);

			// Build rotation matrix.
			Mth::Matrix	rot_mat;
			float		rad = (float)((int)rot) * ( Mth::PI * 0.5f );
			Mth::CreateRotateYMatrix( rot_mat, rad );

			// Rotate matrix.
			rot_mat	= instance_transform * rot_mat;

			// Replace translation component.
			rot_mat[W] = p_matrix->GetPos();

			mp_instance->SetTransform( rot_mat );
		}
		else
		{
			// Go through and adjust the individual meshes.
			for( uint32 i = 0; i < m_num_mesh; ++i )
			{
				NxXbox::sMesh *p_mesh = m_mesh_array[i];
				p_mesh->SetYRotation( rot );
			}
		}
	}
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxGeom::plat_render(Mth::Matrix* pRootMatrix, Mth::Matrix* pBoneMatrices, int numBones)
{
	if( mp_instance )
	{
		mp_instance->SetTransform( *pRootMatrix );
	}
    return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_bone_matrix_data( Mth::Matrix* pBoneMatrices, int numBones )
{
	if( mp_instance )
	{
		Mth::Matrix* p_bone_matrices	= mp_instance->GetBoneTransforms();
		CopyMemory( p_bone_matrices, pBoneMatrices, numBones * sizeof( Mth::Matrix ));
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxGeom::plat_hide_polys( uint32 mask )
{
	if( mp_mesh )
	{
		// Obtain a pointer to the Xbox scene.
		NxXbox::sScene *p_engine_scene = GetInstance()->GetScene();

		// Request the scene to hide the relevant polys.
		p_engine_scene->HidePolys( mask, mp_mesh->GetCASData(), mp_mesh->GetNumCASData());
	}
	
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_visibility( uint32 mask )
{
	// Set values
	m_visible = mask;

	if( m_mesh_array )
	{
		for( uint m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[m];
			p_mesh->SetVisibility((uint8)mask );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 CXboxGeom::plat_get_visibility( void ) const
{
	return m_visible | 0xFFFFFF00;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_color( Image::RGBA rgba )
{
	// If we are setting the color (which is used as a multiplier) to (0x80,0x80,0x80), it is effectively
	// just turning the coloring off...
	// Note that some indoor stuff is set to 0x808081, since on Ps2 it needs to be set to something
	// other than 0x808080 in order to ensure that it doesn't inherit the color from the parent node.
	// This is not an issue for Xbox, so it treats the two values the same.
	if((( rgba.r == 0x80 ) || ( rgba.r == 0x81 )) && ( rgba.g == 0x80 ) && ( rgba.b == 0x80 ))
	{
		plat_clear_color();
		return;
	}

	// Oofa, nasty hack.
	if( mp_instance )
	{
		// Grab the engine scene from the geom, and set all meshes to the color.
		NxXbox::sScene *p_scene = mp_instance->GetScene();
		for( int i = 0; i < p_scene->m_num_mesh_entries; ++i )
		{
			NxXbox::sMesh *p_mesh = p_scene->m_meshes[i];
			p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE;
			p_mesh->m_material_color_override[0] = (float)rgba.r / 255.0f;
			p_mesh->m_material_color_override[1] = (float)rgba.g / 255.0f;
			p_mesh->m_material_color_override[2] = (float)rgba.b / 255.0f;
		}
	}
	else if( m_mesh_array != NULL )
	{
		for( uint32 i = 0; i < m_num_mesh; ++i )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[i];
			p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE;
			p_mesh->m_material_color_override[0] = (float)rgba.r / 255.0f;
			p_mesh->m_material_color_override[1] = (float)rgba.g / 255.0f;
			p_mesh->m_material_color_override[2] = (float)rgba.b / 255.0f;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_clear_color( void )
{
	// Oofa, nasty hack.
	if( mp_instance )
	{
		// Grab the engine scene from the geom, and clear all meshes of the flag.
		NxXbox::sScene *p_scene = mp_instance->GetScene();
		for( int i = 0; i < p_scene->m_num_mesh_entries; ++i )
		{
			NxXbox::sMesh *p_mesh = p_scene->m_meshes[i];
			p_mesh->m_flags &= ~NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE;
		}
	}
	else if( m_mesh_array != NULL )
	{
		for( uint32 i = 0; i < m_num_mesh; ++i )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[i];
			p_mesh->m_flags &= ~NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxGeom::plat_set_material_color( uint32 mat_name_checksum, int pass, Image::RGBA rgba )
{
	bool something_changed = false;

	if( mp_instance && mp_instance->GetScene())
	{
		for( int m = 0; m < mp_instance->GetScene()->m_num_mesh_entries; ++m )
		{
			NxXbox::sMesh		*p_mesh	= mp_instance->GetScene()->m_meshes[m];
			NxXbox::sMaterial	*p_mat	= p_mesh->mp_material;
			if( p_mat )
			{
				bool	want_this_material	= false;
				int		adjusted_pass		= pass;

				// We are searching for materials with a matching name. However there is a caveat in that the
				// conversion process sometimes creates new materials for geometry flagged as 'render as separate', or for
				// geometry which is mapped with only certain passes of a multipass material (or both cases).
				// In such a case, the new material name checksum will differ from the original material name checksum,
				// with the following bits having significance (i.e. consider bitflags = new_namechecksum - old_namechecksum ):
				//
				// Bits 3->6	Pass flags indicating which passes of the original material this material uses
				// Bits 0->2	Absolute number ('render as separate' flagged geometry) indicating which single pass of the material this material represents.
				if( p_mat->m_name_checksum == mat_name_checksum )
					want_this_material = true;
				else if(( p_mat->m_name_checksum > mat_name_checksum ) && (( p_mat->m_name_checksum - mat_name_checksum ) <= 0x7F ))
				{
					uint32 checksum_diff		= p_mat->m_name_checksum - mat_name_checksum;
					uint32 render_separate_pass	= checksum_diff & 0x07;
					uint32 pass_flags			= checksum_diff >> 3;

					if( render_separate_pass )
					{
						if( render_separate_pass == ( pass + 1 ))
						{
							want_this_material = true;

							// Readjust the pass to zero, since this material was formed as a single pass of a multipass material.
							adjusted_pass = 0;
						}
					}
					else if( pass_flags )
					{
						// This material was created during scene conversion from another material with more passes.
						if( pass_flags & ( 1 << pass ))
						{
							want_this_material = true;
							for( int p = 0; p < pass; ++p )
							{
								if(( pass_flags & ( 1 << p )) == 0 )
								{
									// Readjust the pass down by 1, since this material was created as a subset of another material.
									--adjusted_pass;
								}
							}
						}
					}
				}

				if( want_this_material )
				{
					if((uint32)adjusted_pass < p_mat->m_passes )
					{
						if( !( p_mat->m_flags[adjusted_pass] & MATFLAG_PASS_COLOR_LOCKED ))
						{
							p_mat->m_color[adjusted_pass][0] = (float)rgba.r / 255.0f;
							p_mat->m_color[adjusted_pass][1] = (float)rgba.g / 255.0f;
							p_mat->m_color[adjusted_pass][2] = (float)rgba.b / 255.0f;

							something_changed = true;
						}
					}
				}
			}
		}
	}
	return something_changed;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA	CXboxGeom::plat_get_color( void ) const
{
	Image::RGBA rgba( 0, 0, 0, 0 );

	NxXbox::sMesh *p_mesh = NULL;

	if( mp_instance )
	{
		// Grab the engine scene from the geom, and get a mesh color.
		NxXbox::sScene *p_scene = mp_instance->GetScene();

		if( p_scene->m_num_mesh_entries > 0 )
			p_mesh = p_scene->m_meshes[0];
	}
	else if( m_mesh_array != NULL )
	{
		p_mesh = m_mesh_array[0];
	}

	if( p_mesh && ( p_mesh->m_flags & NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE ))
	{
		rgba.r = (uint8)( p_mesh->m_material_color_override[0] * 255.0f );
		rgba.g = (uint8)( p_mesh->m_material_color_override[1] * 255.0f );
		rgba.b = (uint8)( p_mesh->m_material_color_override[2] * 255.0f );
	}
	
	return rgba;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxGeom::plat_get_num_render_verts( void )
{
	Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" ));
	
	int total_verts = 0;

	if( m_mesh_array )
	{
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[m];
			total_verts += p_mesh->m_num_vertices;
		}
	}
	return total_verts;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_get_render_verts( Mth::Vector *p_verts )
{
	Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" ));

	if( m_mesh_array )
	{
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[m];

			// Obtain a read-only lock on the mesh data.
			D3DVECTOR *p_pos;
			p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, (BYTE**)&p_pos, D3DLOCK_READONLY | D3DLOCK_NOFLUSH );

			// Copy over every vertex position in this mesh.
			for( uint32 v = 0; v < p_mesh->m_num_vertices; ++v )
			{
				p_verts->Set( p_pos->x, p_pos->y, p_pos->z );

				++p_verts;
				p_pos = (D3DVECTOR*)((BYTE*)p_pos + p_mesh->m_vertex_stride );
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_get_render_colors( Image::RGBA *p_colors )
{
	Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" ));

	if( m_mesh_array )
	{
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[m];

			// Obtain a read-only lock on the mesh data.
			Image::RGBA *p_col;
			p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, (BYTE**)&p_col, D3DLOCK_READONLY | D3DLOCK_NOFLUSH );
			p_col = (Image::RGBA*)((BYTE*)p_col + p_mesh->m_diffuse_offset );

			// Copy over every vertex color in this mesh, swapping red and blue.
			for( uint32 v = 0; v < p_mesh->m_num_vertices; ++v )
			{
				p_colors->r = p_col->b;
				p_colors->g = p_col->g;
				p_colors->b = p_col->r;
				p_colors->a = p_col->a;

				++p_colors;
				p_col = (Image::RGBA*)((BYTE*)p_col + p_mesh->m_vertex_stride );
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_render_verts( Mth::Vector *p_verts )
{
	Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" ));

	if( m_mesh_array )
	{
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[m];

			// Obtain a writeable lock on the mesh data.
			D3DVECTOR *p_pos;
			p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, (BYTE**)&p_pos, D3DLOCK_NOFLUSH );

			// Will need to store the min and max points in order to calculate the new bounding sphere for the mesh.
			Mth::CBBox bbox;

			// Copy over every vertex position in this mesh.
			for( uint32 v = 0; v < p_mesh->m_num_vertices; ++v )
			{
				p_pos->x = p_verts->GetX();
				p_pos->y = p_verts->GetY();
				p_pos->z = p_verts->GetZ();

				// Add this point to the bounding box.
				bbox.AddPoint( *p_verts );

				++p_verts;
				p_pos = (D3DVECTOR*)((BYTE*)p_pos + p_mesh->m_vertex_stride );
			}

			// Now refigure the bounding sphere.
			Mth::Vector sphere_center	= bbox.GetMin() + ( 0.5f * ( bbox.GetMax() - bbox.GetMin()));
			p_mesh->m_sphere_center.x	= sphere_center[X];
			p_mesh->m_sphere_center.y	= sphere_center[Y];
			p_mesh->m_sphere_center.z	= sphere_center[Z];
			p_mesh->m_sphere_radius		= ( bbox.GetMax() - sphere_center ).Length();
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_render_colors( Image::RGBA *p_colors )
{
	Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" ));

	if( m_mesh_array )
	{
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh*	p_mesh			= m_mesh_array[m];
			Image::RGBA*	p_colors_save	= p_colors;

			// The mesh may contain more than one vertex set, usually in the case of vertex wibbling.
			for( uint32 v = 0; v < p_mesh->m_num_vertex_buffers; ++v )
			{
				p_colors = p_colors_save;

				// Obtain a writeable lock on the mesh data.
				Image::RGBA *p_col;
				p_mesh->mp_vertex_buffer[v]->Lock( 0, 0, (BYTE**)&p_col, D3DLOCK_NOFLUSH );
				p_col = (Image::RGBA*)((BYTE*)p_col + p_mesh->m_diffuse_offset );

				// Copy over every vertex color in this mesh, swapping red and blue.
				for( uint32 v = 0; v < p_mesh->m_num_vertices; ++v )
				{
					p_col->b = p_colors->r;
					p_col->g = p_colors->g;
					p_col->r = p_colors->b;
					p_col->a = p_colors->a;

					++p_colors;
					p_col = (Image::RGBA*)((BYTE*)p_col + p_mesh->m_vertex_stride );
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_scale( const Mth::Vector & scale )
{
	Dbg_MsgAssert( m_mesh_array, ( "Invalid for instanced sectors" ));

	m_scale = scale;

	if( m_mesh_array )
	{
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[m];

			Mth::Vector current_pos( 0.0f, 0.0f, 0.0f, 1.0f );
			p_mesh->GetPosition( ¤t_pos );
			
			// Obtain a writeable lock on the mesh data.
			D3DVECTOR *p_pos;
			p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, (BYTE**)&p_pos, D3DLOCK_NOFLUSH );

			// Scale over every vertex position in this mesh.
			for( uint32 v = 0; v < p_mesh->m_num_vertices; ++v )
			{
				p_pos->x	= (( p_pos->x - current_pos[X] ) * scale[X] ) + current_pos[X];
				p_pos->y	= (( p_pos->y - current_pos[Y] ) * scale[Y] ) + current_pos[Y];
				p_pos->z	= (( p_pos->z - current_pos[Z] ) * scale[Z] ) + current_pos[Z];

				p_pos		= (D3DVECTOR*)((BYTE*)p_pos + p_mesh->m_vertex_stride );
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Mth::Vector CXboxGeom::plat_get_scale() const
{
	return m_scale;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_uv_wibble_params( float u_vel, float u_amp, float u_freq, float u_phase,
										   float v_vel, float v_amp, float v_freq, float v_phase )
{
	if( m_mesh_array )
	{
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh		*p_mesh	= m_mesh_array[m];
			NxXbox::sMaterial	*p_mat	= p_mesh->mp_material;
			if( p_mat )
			{
				// Find the first pass that wibbles.
				for( uint32 p = 0; p < p_mat->m_passes; ++p )
				{
					if( p_mat->mp_UVWibbleParams[p] )
					{
						p_mat->mp_UVWibbleParams[p]->m_UVel			= u_vel;
						p_mat->mp_UVWibbleParams[p]->m_VVel			= v_vel;
						p_mat->mp_UVWibbleParams[p]->m_UAmplitude	= u_amp;
						p_mat->mp_UVWibbleParams[p]->m_VAmplitude	= v_amp;
						p_mat->mp_UVWibbleParams[p]->m_UFrequency	= u_freq;
						p_mat->mp_UVWibbleParams[p]->m_VFrequency	= v_freq;
						p_mat->mp_UVWibbleParams[p]->m_UPhase		= u_phase;
						p_mat->mp_UVWibbleParams[p]->m_VPhase		= v_phase;
						break;
					}
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_use_explicit_uv_wibble( bool yes )
{
	if( mp_instance && mp_instance->GetScene())
	{
		for( int m = 0; m < mp_instance->GetScene()->m_num_mesh_entries; ++m )
		{
			NxXbox::sMesh		*p_mesh	= mp_instance->GetScene()->m_meshes[m];
			NxXbox::sMaterial	*p_mat	= p_mesh->mp_material;
			if( p_mat )
			{
				for( uint32 p = 0; p < p_mat->m_passes; ++p )
				{
					if( p_mat->mp_UVWibbleParams[p] )
					{
						p_mat->m_flags[p] |= MATFLAG_EXPLICIT_UV_WIBBLE;
					}
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_uv_wibble_offsets( float u_offset, float v_offset )
{
	if( m_mesh_array )
	{
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh		*p_mesh	= m_mesh_array[m];
			NxXbox::sMaterial	*p_mat	= p_mesh->mp_material;
			if( p_mat )
			{
				// Find the first pass that wibbles.
				for( uint32 p = 0; p < p_mat->m_passes; ++p )
				{
					if( p_mat->mp_UVWibbleParams[p] )
					{
						p_mat->mp_UVWibbleParams[p]->m_UVMatrix[2] = u_offset;
						p_mat->mp_UVWibbleParams[p]->m_UVMatrix[3] = v_offset;
						plat_use_explicit_uv_wibble( true );
						break;
					}
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxGeom::plat_set_uv_wibble_offsets( uint32 mat_name_checksum, int pass, float u_offset, float v_offset )
{
	if( m_mesh_array )
	{
		for( uint32 m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh		*p_mesh	= m_mesh_array[m];
			NxXbox::sMaterial	*p_mat	= p_mesh->mp_material;
			if( p_mat && ( p_mat->m_name_checksum == mat_name_checksum ))
			{
				// Find the first pass that wibbles.
//				for( uint32 p = 0; p < p_mat->m_passes; ++p )
//				{
//					if( p_mat->mp_UVWibbleParams[p] )
//					{
//						p_mat->mp_UVWibbleParams[p]->m_UWibbleOffset = u_offset;
//						p_mat->mp_UVWibbleParams[p]->m_VWibbleOffset = v_offset;
//						plat_use_explicit_uv_wibble( true );
//						break;
//					}
//				}
			}
		}
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxGeom::plat_set_uv_matrix( uint32 mat_name_checksum, int pass, const Mth::Matrix& mat )
{
	if( mp_instance && mp_instance->GetScene())
	{
		for( int m = 0; m < mp_instance->GetScene()->m_num_mesh_entries; ++m )
		{
			NxXbox::sMesh		*p_mesh	= mp_instance->GetScene()->m_meshes[m];
			NxXbox::sMaterial	*p_mat	= p_mesh->mp_material;
			if( p_mat )
			{
				bool	want_this_material	= false;
				int		adjusted_pass		= pass;

				// We are searching for materials with a matching name. However there is a caveat in that the
				// conversion process sometimes creates new materials for geometry flagged as 'render as separate', or for
				// geometry which is mapped with only certain passes of a multipass material (or both cases).
				// In such a case, the new material name checksum will differ from the original material name checksum,
				// with the following bits having significance (i.e. consider bitflags = new_namechecksum - old_namechecksum ):
				//
				// Bits 3->6	Pass flags indicating which passes of the original material this material uses
				// Bits 0->2	Absolute number ('render as separate' flagged geometry) indicating which single pass of the material this material represents.
				if( p_mat->m_name_checksum == mat_name_checksum )
				{
					want_this_material = true;
				}
				else if(( p_mat->m_name_checksum > mat_name_checksum ) && (( p_mat->m_name_checksum - mat_name_checksum ) <= 0x7F ))
				{
					uint32 checksum_diff		= p_mat->m_name_checksum - mat_name_checksum;
					uint32 render_separate_pass	= checksum_diff & 0x07;
					uint32 pass_flags			= checksum_diff >> 3;

					if( render_separate_pass )
					{
						if( render_separate_pass == ( pass + 1 ))
						{
							want_this_material = true;

							// Readjust the pass to zero, since this material was formed as a single pass of a multipass material.
							adjusted_pass = 0;
						}
					}
					else if( pass_flags )
					{
						// This material was created during scene conversion from another material with more passes.
						if( pass_flags & ( 1 << pass ))
						{
							want_this_material = true;
							for( int p = 0; p < pass; ++p )
							{
								if(( pass_flags & ( 1 << p )) == 0 )
								{
									// Readjust the pass down by 1, since this material was created as a subset of another material.
									--adjusted_pass;
								}
							}
						}
					}
				}

				if( want_this_material )
				{
					if((uint32)adjusted_pass < p_mat->m_passes )
					{
						// Create the wibble params if they don't exist already.
						if( p_mat->mp_UVWibbleParams[adjusted_pass] == NULL )
						{
							p_mat->mp_UVWibbleParams[adjusted_pass]	= new NxXbox::sUVWibbleParams;

							// Need to set flags to indicate that uv wibble is now in effect.
							p_mat->m_uv_wibble				= true;
							p_mat->m_flags[adjusted_pass]  |= MATFLAG_UV_WIBBLE | MATFLAG_EXPLICIT_UV_WIBBLE;
						}

						// Also need to switch vertex shaders if the current vertex shader does not support UV Transforms.
						if( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_1Weight )
						{
							p_mesh->m_vertex_shader[0] = WeightedMeshVS_VXC_1Weight_UVTransform;
						}
						else if( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_2Weight )
						{
							p_mesh->m_vertex_shader[0] = WeightedMeshVS_VXC_2Weight_UVTransform;
						}
						else if( p_mesh->m_vertex_shader[0] == WeightedMeshVS_VXC_3Weight )
						{
							p_mesh->m_vertex_shader[0] = WeightedMeshVS_VXC_3Weight_UVTransform;
						}

						// Set the matrix values.
						p_mat->mp_UVWibbleParams[adjusted_pass]->m_UVMatrix[0]	= mat[0][0];
						p_mat->mp_UVWibbleParams[adjusted_pass]->m_UVMatrix[1]	= mat[0][1];
						p_mat->mp_UVWibbleParams[adjusted_pass]->m_UVMatrix[2]	= mat[3][0];
						p_mat->mp_UVWibbleParams[adjusted_pass]->m_UVMatrix[3]	= mat[3][1];
					}
				}
			}
		}
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxGeom::plat_allocate_uv_matrix_params( uint32 mat_name_checksum, int pass )
{
	if( mp_instance && mp_instance->GetScene())
	{
		bool changed_something = false;

		for( int m = 0; m < mp_instance->GetScene()->m_num_mesh_entries; ++m )
		{
			NxXbox::sMesh		*p_mesh	= mp_instance->GetScene()->m_meshes[m];
			NxXbox::sMaterial	*p_mat	= p_mesh->mp_material;
			if( p_mat )
			{
				bool	want_this_material	= false;
				int		adjusted_pass		= pass;

				// We are searching for materials with a matching name. However there is a caveat in that the
				// conversion process sometimes creates new materials for geometry flagged as 'render as separate', or for
				// geometry which is mapped with only certain passes of a multipass material (or both cases).
				// In such a case, the new material name checksum will differ from the original material name checksum,
				// with the following bits having significance (i.e. consider bitflags = new_namechecksum - old_namechecksum ):
				//
				// Bits 3->6	Pass flags indicating which passes of the original material this material uses
				// Bits 0->2	Absolute number ('render as separate' flagged geometry) indicating which single pass of the material this material represents.
				if( p_mat->m_name_checksum == mat_name_checksum )
				{
					want_this_material = true;
				}
				else if(( p_mat->m_name_checksum > mat_name_checksum ) && (( p_mat->m_name_checksum - mat_name_checksum ) <= 0x7F ))
				{
					uint32 checksum_diff		= p_mat->m_name_checksum - mat_name_checksum;
					uint32 render_separate_pass	= checksum_diff & 0x07;
					uint32 pass_flags			= checksum_diff >> 3;

					if( render_separate_pass )
					{
						if( render_separate_pass == ( pass + 1 ))
						{
							want_this_material = true;

							// Readjust the pass to zero, since this material was formed as a single pass of a multipass material.
							adjusted_pass = 0;
						}
					}
					else if( pass_flags )
					{
						// This material was created during scene conversion from another material with more passes.
						if( pass_flags & ( 1 << pass ))
						{
							want_this_material = true;
							for( int p = 0; p < pass; ++p )
							{
								if(( pass_flags & ( 1 << p )) == 0 )
								{
									// Readjust the pass down by 1, since this material was created as a subset of another material.
									--adjusted_pass;
								}
							}
						}
					}
				}

				if( want_this_material )
				{
					if((uint32)adjusted_pass < p_mat->m_passes )
					{
						// Create the wibble params if they don't exist already.
						if( p_mat->mp_UVWibbleParams[adjusted_pass] == NULL )
						{
							p_mat->mp_UVWibbleParams[adjusted_pass]	= new NxXbox::sUVWibbleParams;

							// Need to set flags to indicate that uv wibble is now in effect.
							p_mat->m_uv_wibble						= true;
							p_mat->m_flags[adjusted_pass]		   |= MATFLAG_UV_WIBBLE | MATFLAG_EXPLICIT_UV_WIBBLE;

							changed_something = true;
						}
					}
				}
			}
		}
		return changed_something;
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxGeom::plat_set_model_lights( CModelLights* p_model_lights )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxGeom *CXboxGeom::plat_clone( bool instance, CScene *p_dest_scene )
{
	CXboxGeom *p_clone = NULL;
	
	// This is a CXboxGeom 'hanging' from a sector. Create a new CXboxGeom which will store the CInstance.
	p_clone = new CXboxGeom();
	
	// Create new meshes for the clone.
	p_clone->m_mesh_array	= new NxXbox::sMesh*[m_num_mesh];
	p_clone->m_num_mesh		= m_num_mesh;
	for( uint32 m = 0; m < p_clone->m_num_mesh; ++m )
	{
		p_clone->m_mesh_array[m] = m_mesh_array[m]->Clone( instance );
	}
	
	if( instance == false )
	{
		p_clone->SetActive( true );

		// In this situation, we need to add the individual meshes to the scene.
		// Grab a temporary workspace buffer.
		Nx::CXboxScene *p_xbox_scene						= static_cast( p_dest_scene );
		NxXbox::sScene *p_scene								= p_xbox_scene->GetEngineScene();
		NxXbox::sMesh **p_temp_mesh_buffer					= ( p_scene->m_num_mesh_entries > 0 ) ? new NxXbox::sMesh*[p_scene->m_num_mesh_entries] : NULL;

		// Set the scene pointer for the clone.
		p_clone->SetScene( p_xbox_scene );
		
		// Copy meshes over into the temporary workspace buffer.
		for( int i = 0; i < p_scene->m_num_mesh_entries; ++i )
		{
			p_temp_mesh_buffer[i]	= p_scene->m_meshes[i];
		}

		// Store how many meshes were present.
		int old_num_mesh_entries	= p_scene->m_num_mesh_entries;
		
		// Delete current mesh array.
		delete [] p_scene->m_meshes;

		// Important to set this to NULL.
		p_scene->m_meshes			= NULL;
		
		// Include new meshes in count.
		p_scene->CountMeshes( m_num_mesh, m_mesh_array );

		// Allocate new mesh arrays.
		p_scene->CreateMeshArrays();

		// Copy old mesh data back in.
		for( int i = 0; i < old_num_mesh_entries; ++i )
		{
			p_scene->m_meshes[i] = p_temp_mesh_buffer[i];
		}

		// Remove temporary arrays.
		delete [] p_temp_mesh_buffer;
		
		// Add new meshes.
		p_scene->AddMeshes( p_clone->m_num_mesh, p_clone->m_mesh_array );

		// Sort the meshes.
		p_scene->SortMeshes();
	}
	else
	{
		// Create a new scene which will be attached via the instance.
		p_clone->m_bbox			= m_bbox;
		NxXbox::sScene *p_scene = p_clone->GenerateScene();

		p_clone->SetActive( true );
	
		// Create the instance.
		Mth::Matrix temp;
		temp.Identity();
		NxXbox::CInstance *p_instance = new NxXbox::CInstance( p_scene, temp, 1, NULL );
		
		// This instance will be the only object maintaining a reference to the attached scene, so we want to delete
		// the scene when the instance gets removed.
		p_instance->SetFlag( NxXbox::CInstance::INSTANCE_FLAG_DELETE_ATTACHED_SCENE );

		// Hook the clone up to the instance.		
		p_clone->SetInstance( p_instance );
	}
	return p_clone;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxGeom* CXboxGeom::plat_clone( bool instance, CModel* p_dest_model )
{
	CXboxGeom* p_clone = NULL;

	Nx::CXboxScene* p_xbox_scene = new CXboxScene;
	p_xbox_scene->SetEngineScene( mp_instance->GetScene() );

	p_clone				= new CXboxGeom();
	p_clone->mp_scene	= p_xbox_scene;

	int num_mesh		= mp_instance->GetScene()->m_num_mesh_entries;

	// Create new meshes for the clone.
	p_clone->m_mesh_array	= num_mesh ? new NxXbox::sMesh*[num_mesh] : NULL;
	p_clone->m_num_mesh		= num_mesh;
	for( uint32 m = 0; m < p_clone->m_num_mesh; ++m )
	{
		p_clone->m_mesh_array[m] = mp_instance->GetScene()->m_meshes[m]->Clone( instance );
	}

	// Create a new scene which will be attached via the instance.
	p_clone->m_bbox			= m_bbox;
	NxXbox::sScene* p_scene = p_clone->GenerateScene();

	// Kill the temp scene.
	p_xbox_scene->SetEngineScene( NULL );
	delete p_xbox_scene;
	p_clone->mp_scene		= NULL;

	p_clone->SetActive( true );

	// Create the new bone array.
	int				num_bones	= p_dest_model->GetNumBones();
	Mth::Matrix*	p_bones		= new Mth::Matrix[num_bones];
	for( int b = 0; b < num_bones; ++b )
	{
		p_bones[b].Identity();
	}

	// Create the instance.
	Mth::Matrix temp;
	temp.Identity();
	NxXbox::CInstance* p_instance	= new NxXbox::CInstance( p_scene, temp, num_bones, p_bones );
	
	Nx::CXboxModel* p_xbox_model	= static_cast( p_dest_model );
	((CXboxModel*)p_dest_model )->SetInstance( p_instance );

	// This instance will be the only object maintaining a reference to the attached scene, so we want to delete
	// the scene when the instance gets removed.
	p_instance->SetFlag( NxXbox::CInstance::INSTANCE_FLAG_DELETE_ATTACHED_SCENE );

	// Hook the clone up to the instance.		
	p_clone->SetInstance( p_instance );

	return p_clone;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx
				


================================================
FILE: Code/Gfx/XBox/p_NxGeom.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxGeom.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  3/5/2002
//****************************************************************************

#ifndef	__GFX_P_NX_GEOM_H__
#define	__GFX_P_NX_GEOM_H__
    
#include "gfx/nxgeom.h"
#include "gfx/xbox/p_nxsector.h"
#include "gfx/xbox/p_nxmesh.h"

namespace Mth
{
	class Matrix;
}

namespace NxXbox
{
	class CInstance;
}
						   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CGeom
class CXboxGeom : public CGeom
{
                                      
public:
								CXboxGeom();
	virtual 					~CXboxGeom();
	void						SetInstance( NxXbox::CInstance *p_instance )		{ mp_instance = p_instance; }
	NxXbox::CInstance			*GetInstance( void )								{ return mp_instance; }
	void						InitMeshList();
	void						ClearMeshList();
	void						AddMesh( NxXbox::sMesh * );
	Lst::Head< NxXbox::sMesh >	*GetMeshList();

	void						CreateMeshArray();
	bool						RegisterMeshArray( bool just_count );
	void						DestroyMeshArray( void );

	const Mth::CBBox &			GetBoundingBox( void )			{ return m_bbox; }
	void						SetScene( CXboxScene *p_scene )	{ mp_scene = p_scene; }
	NxXbox::sScene				*GenerateScene( void );


private:						// It's all private, as it is machine specific
	virtual	bool					plat_load_geom_data( CMesh *pMesh, CModel *pModel, bool color_per_material );
	virtual void					plat_finalize( void );
	virtual	void					plat_set_active( bool active );
	virtual bool					plat_is_active( void ) const;
	virtual const Mth::CBBox &		plat_get_bounding_box( void ) const;
	virtual const Mth::Vector		plat_get_bounding_sphere( void ) const;
	virtual void					plat_set_bounding_sphere( const Mth::Vector& boundingSphere );

	virtual const Mth::Vector &		plat_get_world_position( void ) const;
	virtual void					plat_set_world_position( const Mth::Vector& pos );
	virtual const Mth::Matrix &		plat_get_orientation( void ) const;
	virtual void					plat_set_orientation( const Mth::Matrix& orient );
	virtual void					plat_set_scale( const Mth::Vector & scale );
	virtual Mth::Vector				plat_get_scale( void ) const;
	virtual void					plat_rotate_y( Mth::ERot90 rot );
	virtual bool					plat_render( Mth::Matrix* pRootMatrix, Mth::Matrix* ppBoneMatrices, int numBones );
	virtual void					plat_set_bone_matrix_data( Mth::Matrix* pBoneMatrices, int numBones );
	virtual bool					plat_hide_polys( uint32 mask );
	virtual uint32					plat_get_visibility( void ) const;
	virtual	void					plat_set_visibility( uint32 mask );
	virtual void					plat_set_color( Image::RGBA rgba );
	virtual void					plat_clear_color( void );
	virtual Image::RGBA				plat_get_color( void ) const;
	virtual	int						plat_get_num_render_verts( void );
	virtual	void					plat_get_render_verts( Mth::Vector *p_verts );
	virtual	void					plat_get_render_colors( Image::RGBA *p_colors );
	virtual void					plat_set_render_verts( Mth::Vector *p_verts );
	virtual	void					plat_set_render_colors( Image::RGBA *p_colors );
	virtual void					plat_set_model_lights( CModelLights* p_model_lights );
	virtual void					plat_set_uv_wibble_params( float u_vel, float u_amp, float u_freq, float u_phase, float v_vel, float v_amp, float v_freq, float v_phase );
	virtual void					plat_use_explicit_uv_wibble( bool yes );
	virtual void					plat_set_uv_wibble_offsets( float u_offset, float v_offset );
	virtual bool					plat_set_uv_wibble_offsets( uint32 mat_name_checksum, int pass, float u_offset, float v_offset );
	virtual bool					plat_set_uv_matrix( uint32 mat_name_checksum, int pass, const Mth::Matrix& mat );
	virtual bool					plat_set_material_color( uint32 mat_name_checksum, int pass, Image::RGBA rgba );
	virtual bool					plat_allocate_uv_matrix_params( uint32 mat_checksum, int pass );

	virtual CXboxGeom*				plat_clone( bool instance, CScene *p_dest_scene = NULL );
	virtual CXboxGeom*				plat_clone( bool instance, CModel *p_dest_model );

public:
	Mth::CBBox						m_bbox;	
	
	CXboxScene *					mp_scene;
	Lst::Head< NxXbox::sMesh >		*mp_init_mesh_list;   
	NxXbox::sMesh **				m_mesh_array;
	uint							m_num_mesh;
	uint32							m_visible;
	bool							m_active;

private:
	NxXbox::CInstance				*mp_instance;
	CXboxMesh						*mp_mesh;		// Used for obtaining CAS poly removal data.
	Mth::Vector						m_scale;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx

#endif 


================================================
FILE: Code/Gfx/XBox/p_NxImposter.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxImposter.cpp

#include 	"sys/timer.h"
#include 	"gfx/xbox/p_NxGeom.h"
#include 	"gfx/xbox/p_NxImposter.h"
#include 	"gfx/xbox/nx/nx_init.h"
#include 	"gfx/xbox/nx/render.h"

namespace Nx
{

	
const int XBOX_IMPOSTER_UPDATE_LIMIT		= 30;
const int XBOX_IMPOSTER_MAX_U_TEXTURE_SIZE	= 128;
const int XBOX_IMPOSTER_MAX_V_TEXTURE_SIZE	= 128;
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void frustum_project_box( Mth::CBBox &bbox, XGMATRIX *p_view_matrix, Mth::Vector *p_max_x, Mth::Vector *p_max_y, Mth::Vector *p_mid )
{
	float	max_projected_xx, max_projected_xz;		// The camera space position of the point with the greatest x axis value when projected to z = mid_z.
	float	max_projected_x_mid_z;					// The projected x axis value when this point is projected to z = mid_z;

	float	max_projected_yy, max_projected_yz;		// The camera space position of the point furthest along the y axis when projected to z = mid_z.
	float	max_projected_y_mid_z;					// The projected y axis value when this point is projected to z = mid_z;

	float	min_x = bbox.GetMin().GetX();
	float	min_y = bbox.GetMin().GetY();
	float	min_z = bbox.GetMin().GetZ();
	float	max_x = bbox.GetMax().GetX();
	float	max_y = bbox.GetMax().GetY();
	float	max_z = bbox.GetMax().GetZ();

	// Project the midpoint of the box, since this is the point through which the imposter polygon will pass.
	XGVECTOR3 mid_in( min_x + ( 0.5f * ( max_x - min_x )), min_y + ( 0.5f * ( max_y - min_y )), min_z + ( 0.5f * ( max_z - min_z )));
	XGVECTOR4 mid_out;
	XGVec3Transform( &mid_out, &mid_in, p_view_matrix );

	for( uint32 v = 0; v < 8; ++v )
	{
		XGVECTOR3 in;
		XGVECTOR4 out;

		in.x = ( v & 0x04 ) ? max_x : min_x;
		in.y = ( v & 0x02 ) ? max_y : min_y;
		in.z = ( v & 0x01 ) ? max_z : min_z;
		
		XGVec3Transform( &out, &in, p_view_matrix );

		out.x = fabsf( out.x );
		out.y = fabsf( out.y );

		float projected_x_mid_z	= out.x * ( mid_out.z / out.z );
		float projected_y_mid_z	= out.y * ( mid_out.z / out.z );

		if( v == 0 )
		{
			max_projected_x_mid_z	= projected_x_mid_z;
			max_projected_xx		= out.x;
			max_projected_xz		= out.z;

			max_projected_y_mid_z	= projected_y_mid_z;
			max_projected_yy		= out.y;
			max_projected_yz		= out.z;
		}
		else
		{
			if( projected_x_mid_z > max_projected_x_mid_z )
			{
				max_projected_xx		= out.x;
				max_projected_xz		= out.z;
				max_projected_x_mid_z	= projected_x_mid_z;
			}

			if( projected_y_mid_z > max_projected_y_mid_z )
			{
				max_projected_yy		= out.y;
				max_projected_yz		= out.z;
				max_projected_y_mid_z	= projected_y_mid_z;
			}
		}
	}

	p_max_x->Set( max_projected_xx, 0.0f, max_projected_xz, 0.0f );
	p_max_y->Set( 0.0f, max_projected_yy, max_projected_yz, 0.0f );
	p_mid->Set( mid_out.x, mid_out.y, mid_out.z );
}
	
	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CImposterGroup* CImposterManager::plat_create_imposter_group( void )
{
	return new CXboxImposterGroup;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::plat_pre_render_imposters( void )
{
	// Set up the common material attributes for the imposters.
//	NxXbox::set_blend_mode( NxXbox::vBLEND_MODE_BLEND );
	NxXbox::set_blend_mode( NxXbox::vBLEND_MODE_ONE_INV_SRC_ALPHA );

	NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
	NxXbox::set_pixel_shader( PixelShader2 );

	NxXbox::set_render_state( RS_UVADDRESSMODE0,	0x00010001UL );
	NxXbox::set_render_state( RS_ALPHACUTOFF,		1 );
	NxXbox::set_render_state( RS_ZWRITEENABLE,		1 );
	NxXbox::set_render_state( RS_ZTESTENABLE,		1 );
	NxXbox::set_render_state( RS_CULLMODE,			D3DCULL_NONE );

	D3DDevice_SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
	D3DDevice_SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU | 0 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CImposterManager::plat_post_render_imposters( void )
{
	// Clean up the common material attributes for the imposters.
	NxXbox::set_texture( 0, NULL );
	NxXbox::set_render_state( RS_CULLMODE, D3DCULL_CW );
}



/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CImposterGroup

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxImposterGroup::CXboxImposterGroup()
{
	mp_texture					= NULL;
	m_update_count				= Mth::Rnd( XBOX_IMPOSTER_UPDATE_LIMIT );
	mp_removed_textures_list	= new Lst::Head ;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxImposterGroup::~CXboxImposterGroup()
{
	if( mp_texture )
	{
		mp_texture->Release();
		mp_texture = NULL;
	}

	// Remove all nodes from the removed textures table.
	Lst::Node *p_node, *p_next;
	for( p_node = mp_removed_textures_list->GetNext(); p_node; p_node = p_next )
	{
		p_next = p_node->GetNext();

		sRemovedTextureDetails	*p_details	= p_node->GetData();
		IDirect3DTexture8		*p_texture	= p_details->mp_texture;
		p_texture->Release();

		delete p_details;
		delete p_node;
	}

	// Remove the table itself.
	delete mp_removed_textures_list;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxImposterGroup::process_removed_textures( void )
{
	Lst::Node *p_node, *p_next;
	for( p_node = mp_removed_textures_list->GetNext(); p_node; p_node = p_next )
	{
		p_next = p_node->GetNext();

		sRemovedTextureDetails	*p_details	= p_node->GetData();
		int						time		= p_details->m_time_removed;
		if((( NxXbox::EngineGlobals.render_start_time - time ) > 250 ) || ( time > NxXbox::EngineGlobals.render_start_time ))
		{
			IDirect3DTexture8*	p_texture	= p_details->mp_texture;
			p_texture->Release();

			delete p_details;
			delete p_node;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxImposterGroup::plat_process( void )
{
	process_removed_textures();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxImposterGroup::plat_create_imposter_polygon( void )
{
	Dbg_Assert( !m_imposter_polygon_exists );

	// Generate a camera matrix that will point the camera directly at the midpoint of the bounding box.
	XGMATRIX	cam_mat;
	XGVECTOR3	look_at( m_composite_bbox_mid[X], m_composite_bbox_mid[Y], m_composite_bbox_mid[Z] );
	XGMatrixLookAtRH( &cam_mat, &NxXbox::EngineGlobals.cam_position, &look_at, &NxXbox::EngineGlobals.cam_up );

	// Using this camera matrix, project all eight corners of the bounding box, in order to determine the best size for the texture.
	Mth::Vector proj_max_x, proj_max_y, proj_mid;
	frustum_project_box( m_composite_bbox, &cam_mat, &proj_max_x, &proj_max_y, &proj_mid );

	// Now project the minimum and maximum x and y values onto the projection plane - the plane through the midpoint of the box.
	float max_projected_x	= proj_max_x[X] * ( proj_mid[Z] / proj_max_x[Z] );
	float max_projected_y	= proj_max_y[Y] * ( proj_mid[Z] / proj_max_y[Z] );

	// Calculate the maximum width and height at the near plane.
	float wnp				= 2.0f * proj_max_x[X] * ( NxXbox::EngineGlobals.near_plane / fabsf( proj_max_x[Z] ));
	float hnp				= 2.0f * proj_max_y[Y] * ( NxXbox::EngineGlobals.near_plane / fabsf( proj_max_y[Z] ));

	m_tex_width				= 16 + Ftoi_ASM(( 640.0f * wnp ) / NxXbox::EngineGlobals.near_plane_width );
	m_tex_height			= 16 + Ftoi_ASM(( 480.0f * hnp ) / NxXbox::EngineGlobals.near_plane_height );

	// Round texture to the nearest 16 pixel limit.
	m_tex_width				= (( m_tex_width + 0x0F ) & ~0x0F );
	m_tex_height			= (( m_tex_height + 0x0F ) & ~0x0F );

	// Clamp texture to maximum allowed size.
	m_tex_width				= ( m_tex_width > XBOX_IMPOSTER_MAX_U_TEXTURE_SIZE ) ? XBOX_IMPOSTER_MAX_U_TEXTURE_SIZE : m_tex_width;
	m_tex_height			= ( m_tex_height > XBOX_IMPOSTER_MAX_V_TEXTURE_SIZE ) ? XBOX_IMPOSTER_MAX_V_TEXTURE_SIZE : m_tex_height;

	// Calculate the corresponding projection matrix.
	XGMATRIX proj_mat;
	XGMatrixPerspectiveRH( &proj_mat, wnp, hnp, NxXbox::EngineGlobals.near_plane, NxXbox::EngineGlobals.far_plane );

	// Set the calculated view and projection matrices.
	D3DDevice_SetTransform( D3DTS_VIEW, &cam_mat );
	D3DDevice_SetTransform( D3DTS_PROJECTION, &proj_mat );

	// Create a render target texture into which the object will be drawn.
	HRESULT hr;
	if( mp_texture == NULL )
	{
		hr = D3DDevice_CreateTexture( m_tex_width, m_tex_height, 1, 0, D3DFMT_LIN_A8R8G8B8, 0, &mp_texture );
		Dbg_Assert( hr == D3D_OK );
	}

	// Create a corresponding depth texture (we need this only for the render - then it can be removed).
	IDirect3DTexture8* p_depth_buffer;
	hr = D3DDevice_CreateTexture( m_tex_width, m_tex_height, 1, 0, D3DFMT_LIN_D24S8, 0, &p_depth_buffer );
	Dbg_Assert( hr == D3D_OK );

	// This call will increase the reference count of the IDirect3DTexture8 object.
	LPDIRECT3DSURFACE8 p_surface, p_depth_surface;
	mp_texture->GetSurfaceLevel( 0, &p_surface );
	p_depth_buffer->GetSurfaceLevel( 0, &p_depth_surface );

	// This call will increase the reference count of the IDirect3DSurface8 object.
	D3DDevice_SetRenderTarget( p_surface, p_depth_surface );
			
	// Clear the render target.
	D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x00000000, 1.0f, 0 );
//	D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x80800000, 1.0f, 0 );

	// The imposter polygon has now been created.
	m_imposter_polygon_exists = true;

	// Set the camera position at the time of creation.
	m_cam_pos.Set( NxXbox::EngineGlobals.cam_position.x, NxXbox::EngineGlobals.cam_position.y, NxXbox::EngineGlobals.cam_position.z );

	// Build a list of meshes, so we can sort them dynamically into draw order.
	NxXbox::sMesh*	mesh_list[256];
	uint32			num_meshes = 0;

	Lst::Node *node, *next;
	for( node = mp_geom_list->GetNext(); node; node = next )
	{
		next = node->GetNext();
		Nx::CXboxGeom *p_xbox_geom = static_cast( node->GetData());

		for( uint32 m = 0; m < p_xbox_geom->m_num_mesh; ++m )
		{
			Dbg_Assert( num_meshes < 256 );
			NxXbox::sMesh* p_mesh	= p_xbox_geom->m_mesh_array[m];
			mesh_list[num_meshes++]	= p_mesh;
		}
	}

	if( num_meshes > 0 )
	{
		// Sort the array of pointers into draw order.
		qsort( mesh_list, num_meshes, sizeof( NxXbox::sMesh* ), NxXbox::sort_by_material_draw_order );
	}

	// Render each mesh into the render target.
	NxXbox::EngineGlobals.blend_mode_override = NxXbox::vBLEND_MODE_ONE_INV_SRC_ALPHA;
	NxXbox::sMaterial*	p_material = NULL;
	for( uint32 m = 0; m < num_meshes; ++m )
	{
		NxXbox::sMesh* p_mesh = mesh_list[m];
		if( p_mesh->mp_material != p_material )
		{
			p_material	= p_mesh->mp_material;
			p_material->Submit();
		}
		p_mesh->Submit();
	}
	NxXbox::EngineGlobals.blend_mode_override = 0;

	// Can now set the meshes in the geom inactive.
	for( node = mp_geom_list->GetNext(); node; node = next )
	{
		next = node->GetNext();
		Nx::CXboxGeom *p_xbox_geom = static_cast( node->GetData());
		p_xbox_geom->SetActive( false );
	}

	// Remove references to surfaces.
	p_surface->Release();
	p_depth_surface->Release();
	p_depth_buffer->Release();

	// Restore the default render target.
	D3DDevice_SetRenderTarget( NxXbox::EngineGlobals.p_RenderSurface, NxXbox::EngineGlobals.p_ZStencilSurface );

	// Restore the view and projection transforms.
	D3DDevice_SetTransform( D3DTS_VIEW, &NxXbox::EngineGlobals.view_matrix );
	D3DDevice_SetTransform( D3DTS_PROJECTION, &NxXbox::EngineGlobals.projection_matrix );

	// Now figure the vertex positions for the polygon.
	Mth::Vector at = m_composite_bbox_mid - m_cam_pos;
	at.Normalize();
	Mth::Vector right	= Mth::CrossProduct( Mth::Vector( NxXbox::EngineGlobals.cam_up.x, NxXbox::EngineGlobals.cam_up.y, NxXbox::EngineGlobals.cam_up.z ), at );
	right.Normalize();
	Mth::Vector up		= Mth::CrossProduct( at, right );

	Mth::Vector	verts[4];
	verts[0]			= m_composite_bbox_mid - ( max_projected_x * right ) + ( max_projected_y * up );
	verts[1]			= m_composite_bbox_mid + ( max_projected_x * right ) + ( max_projected_y * up );
	verts[2]			= m_composite_bbox_mid + ( max_projected_x * right ) - ( max_projected_y * up );
	verts[3]			= m_composite_bbox_mid - ( max_projected_x * right ) - ( max_projected_y * up );

	for( int v = 0; v < 4; ++v )
	{
		m_vertex_buffer[v].x	= verts[v][X];
		m_vertex_buffer[v].y	= verts[v][Y];
		m_vertex_buffer[v].z	= verts[v][Z];
	}

	// The texture is a linear format, so the uv's are in texel space.
	m_vertex_buffer[0].u		= (float)m_tex_width;
	m_vertex_buffer[0].v		= 0.0f;
	m_vertex_buffer[1].u		= 0.0f;
	m_vertex_buffer[1].v		= 0.0f;
	m_vertex_buffer[2].u		= 0.0f;
	m_vertex_buffer[2].v		= (float)m_tex_height;
	m_vertex_buffer[3].u		= (float)m_tex_width;
	m_vertex_buffer[3].v		= (float)m_tex_height;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxImposterGroup::plat_remove_imposter_polygon( void )
{
	if( m_imposter_polygon_exists )
	{
		m_imposter_polygon_exists = false;

		if( mp_texture )
		{
			// At this point move the texture resource into a list of removed textures. Here it will remain
			// for sufficient time to ensure that the GPU will no longer try to access it during push buffer processing.
			sRemovedTextureDetails	*p_new_details			= new sRemovedTextureDetails;
			p_new_details->mp_texture						= mp_texture;
			p_new_details->m_time_removed					= NxXbox::EngineGlobals.render_start_time;

			Lst::Node *p_new_node	= new Lst::Node( p_new_details );
			mp_removed_textures_list->AddToTail( p_new_node );

			mp_texture = NULL;
		}

		Lst::Node *node, *next;
		for( node = mp_geom_list->GetNext(); node; node = next )
		{
			next = node->GetNext();
			Nx::CXboxGeom *p_xbox_geom = static_cast( node->GetData());

			// Can now set the meshes in the geom active.
			p_xbox_geom->SetActive( true );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxImposterGroup::plat_update_imposter_polygon( void )
{
	// Calculate the new vector from bounding box midpoint to camera.
	Mth::Vector new_vec( NxXbox::EngineGlobals.cam_position.x - m_composite_bbox_mid[X], 
						 NxXbox::EngineGlobals.cam_position.y - m_composite_bbox_mid[Y],
						 NxXbox::EngineGlobals.cam_position.z - m_composite_bbox_mid[Z] );
	new_vec.Normalize();

	// Calculate the old vector from bounding box midpoint to camera when the imposter was created.
	Mth::Vector old_vec = m_cam_pos - m_composite_bbox_mid;
	old_vec.Normalize();

	float angle_change = acosf( Mth::DotProduct( new_vec, old_vec ));

	// Rebuild the imposter polygon if the angle change is beyond some limit.
	if( angle_change > Mth::DegToRad( 5.0f ))
	{
		RemoveImposterPolygon();
		CreateImposterPolygon();
		return true;
	}

	// Check intermittently to see if the projected screen area of the imposter has changed sufficienty
	// to warrant regenerating a new texture.
	if( m_update_count >= XBOX_IMPOSTER_UPDATE_LIMIT )
	{
		m_update_count = 0;

		// Generate a camera matrix that will point the camera directly at the midpoint of the bounding box.
		XGMATRIX	cam_mat;
		XGVECTOR3	look_at( m_composite_bbox_mid[X], m_composite_bbox_mid[Y], m_composite_bbox_mid[Z] );
		XGMatrixLookAtRH( &cam_mat, &NxXbox::EngineGlobals.cam_position, &look_at, &NxXbox::EngineGlobals.cam_up );

		// Using this camera matrix, project all eight corners of the bounding box, in order to determine the best size for the texture.
		Mth::Vector proj_max_x, proj_max_y, proj_mid;
		frustum_project_box( m_composite_bbox, &cam_mat, &proj_max_x, &proj_max_y, &proj_mid );

		// Calculate the maximum width and height at the near plane.
		float wnp		= 2.0f * proj_max_x[X] * ( NxXbox::EngineGlobals.near_plane / fabsf( proj_max_x[Z] ));
		float hnp		= 2.0f * proj_max_y[Y] * ( NxXbox::EngineGlobals.near_plane / fabsf( proj_max_y[Z] ));

		// Round texture to the nearest 16 pixel limit.
		int tex_width	= 16 + Ftoi_ASM(( 640.0f * wnp ) / NxXbox::EngineGlobals.near_plane_width );
		int tex_height	= 16 + Ftoi_ASM(( 480.0f * hnp ) / NxXbox::EngineGlobals.near_plane_height );
		tex_width		= ( tex_width + 0x0F ) & ~0x0F;
		tex_height		= ( tex_height + 0x0F ) & ~0x0F;

		// Clamp texture to maximum allowed size.
		tex_width		= ( tex_width > XBOX_IMPOSTER_MAX_U_TEXTURE_SIZE ) ? XBOX_IMPOSTER_MAX_U_TEXTURE_SIZE : tex_width;
		tex_height		= ( tex_height > XBOX_IMPOSTER_MAX_V_TEXTURE_SIZE ) ? XBOX_IMPOSTER_MAX_V_TEXTURE_SIZE : tex_height;

		if(( tex_width != m_tex_width ) || ( tex_height != m_tex_height ))
		{
			RemoveImposterPolygon();
			CreateImposterPolygon();
			return true;
		}
	}

	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxImposterGroup::plat_draw_imposter_polygon( void )
{
	// Have to clear texture 0 before switching to the imposter texture, because it is a linear format.
	NxXbox::set_texture( 0, NULL );
	NxXbox::set_texture( 0, mp_texture );

	NxXbox::EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_QUADLIST, 1, m_vertex_buffer, sizeof( sImposterPolyVert ));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float CXboxImposterGroup::plat_check_distance( void )
{
	// First check the visibility, using the bounding sphere.
	bool visible = NxXbox::frustum_check_sphere((D3DXVECTOR3*)&( m_composite_bbox_mid[X] ), m_composite_bsphere_radius );

	if( !visible )
		return 0.0f;

	float	min_x = m_composite_bbox.GetMin().GetX();
	float	min_y = m_composite_bbox.GetMin().GetY();
	float	min_z = m_composite_bbox.GetMin().GetZ();
	float	max_x = m_composite_bbox.GetMax().GetX();
	float	max_y = m_composite_bbox.GetMax().GetY();
	float	max_z = m_composite_bbox.GetMax().GetZ();

	// The camera-space distance to the nearest point on the composite bounding box of the imposter.
	float	nearest = NxXbox::EngineGlobals.far_plane;

	for( uint32 v = 0; v < 8; ++v )
	{
		XGVECTOR3 test_in( ( v & 0x04 ) ? max_x : min_x, ( v & 0x02 ) ? max_y : min_y, ( v & 0x01 ) ? max_z : min_z );
		XGVECTOR4 test_mid;
		
		XGVec3Transform( &test_mid, &test_in, &NxXbox::EngineGlobals.view_matrix );

		// Do z-checking here.
		if( -test_mid.z < m_switch_distance )
		{
			return -test_mid.z;
		}
		else if( -test_mid.z < nearest )
		{
			nearest = -test_mid.z;
		}
	}
	return nearest;
}




} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/XBox/p_NxImposter.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxFont.h

#ifndef	__GFX_P_NX_IMPOSTER_H__
#define	__GFX_P_NX_IMPOSTER_H__

#include 	"gfx/NxImposter.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sImposterPolyVert
{
	float		x, y, z;
	float		u, v;
};
	
	
/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

struct sRemovedTextureDetails
{
	IDirect3DTexture8*	mp_texture;
	int					m_time_removed;
};



// Here's a machine specific implementation of the CImposterGroup
class CXboxImposterGroup : public CImposterGroup
{
	public:
												CXboxImposterGroup();
	virtual										~CXboxImposterGroup();


	private:									// It's all private, as it is machine specific

	virtual void								plat_create_imposter_polygon( void );
	virtual void								plat_remove_imposter_polygon( void );
	virtual bool								plat_update_imposter_polygon( void );
	virtual void								plat_draw_imposter_polygon( void );
	virtual float								plat_check_distance( void );
	virtual void								plat_process( void );

	void										process_removed_textures( void );

	// Machine specific members
	sImposterPolyVert							m_vertex_buffer[4];
	IDirect3DTexture8*							mp_texture;
	Lst::Head  *		mp_removed_textures_list;
};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/XBox/p_NxLight.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxLight.cpp - Xbox platform specific interface to CModelLights

#include 

#include 
#include 


namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxModelLights::CXboxModelLights()
{
	m_flags = 0;

	EnableAmbientLight( false );
	for( int i = 0; i < CLightManager::MAX_LIGHTS; ++i )
	{
		EnableDiffuseLight( i, false );
	}
	
	// Set valid default direction.
	m_diffuse_direction[0].Set( 0.0f, 1.0f, 0.0f );
	m_diffuse_direction[1].Set( 0.0f, 1.0f, 0.0f );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxModelLights::~CXboxModelLights()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxModelLights::plat_update_brightness()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxModelLights::plat_update_engine( Mth::Vector & pos, bool add_scene_light )
{
	if( m_flags & mUSE_MODEL_AMBIENT )
	{
		// Use the local ambient color, modulate it with the local ambient brightness.
		NxXbox::EngineGlobals.ambient_light_color[0] = m_ambient_color.r * ( 1.0f / 128.0f ) * m_ambient_brightness;
		NxXbox::EngineGlobals.ambient_light_color[1] = m_ambient_color.g * ( 1.0f / 128.0f ) * m_ambient_brightness;
		NxXbox::EngineGlobals.ambient_light_color[2] = m_ambient_color.b * ( 1.0f / 128.0f ) * m_ambient_brightness;
	}
	else
	{
		// Use the default ambient color, but modulate it with the local ambient brightness.
		Image::RGBA	amb = CLightManager::sGetLightAmbientColor();
		NxXbox::EngineGlobals.ambient_light_color[0] = amb.r * ( 1.0f / 128.0f ) * m_ambient_brightness;
		NxXbox::EngineGlobals.ambient_light_color[1] = amb.g * ( 1.0f / 128.0f ) * m_ambient_brightness;
		NxXbox::EngineGlobals.ambient_light_color[2] = amb.b * ( 1.0f / 128.0f ) * m_ambient_brightness;
	}

	for( int i = 0; i < 2; ++i )
	{
		if( m_flags & (( i == 0 ) ? mUSE_MODEL_DIFFUSE_0 : mUSE_MODEL_DIFFUSE_1 ))
		{
			// Use the local directional color, modulate it with the local directional brightness.
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 4] = m_diffuse_color[i].r * ( 1.0f / 128.0f ) * m_diffuse_brightness[i];
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 5] = m_diffuse_color[i].g * ( 1.0f / 128.0f ) * m_diffuse_brightness[i];
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 6] = m_diffuse_color[i].b * ( 1.0f / 128.0f ) * m_diffuse_brightness[i];

			// Use the local direction.
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 0] = -m_diffuse_direction[i][X];
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 1] = -m_diffuse_direction[i][Y];
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 2] = -m_diffuse_direction[i][Z];
		}
		else
		{
			// Use the default directional color, but modulate it with the local directional brightness.
			Image::RGBA	dif = CLightManager::sGetLightDiffuseColor( i );
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 4] = dif.r * ( 1.0f / 128.0f ) * m_diffuse_brightness[i];
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 5] = dif.g * ( 1.0f / 128.0f ) * m_diffuse_brightness[i];
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 6] = dif.b * ( 1.0f / 128.0f ) * m_diffuse_brightness[i];

			// Use the default direction.
			Mth::Vector dir = CLightManager::sGetLightDirection( i );
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 0] = -dir[X];
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 1] = -dir[Y];
			NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 2] = -dir[Z];
		}
	}

	// Figure Scene Lighting if required.
	if( add_scene_light )
	{
		Nx::CSceneLight *p_scene_light = CLightManager::sGetOptimumSceneLight( pos );
		if( p_scene_light )
		{
			Mth::Vector light_pos = p_scene_light->GetLightPosition();

			float dist	= Mth::Distance( pos, light_pos );
			float ratio	= dist * p_scene_light->GetLightReciprocalRadius();

			light_pos = ( pos - light_pos ).Normalize();

			// Figure the direction...
			NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 0] = light_pos[X];
			NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 1] = light_pos[Y];
			NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 2] = light_pos[Z];

			// ...and the color.
			ratio															= sqrtf( 1.0f - ratio ) * ( 1.0f / 255.0f ) * p_scene_light->GetLightIntensity();
			NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 4]	= ratio * p_scene_light->GetLightColor().r;
			NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 5]	= ratio * p_scene_light->GetLightColor().g;
			NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 6]	= ratio * p_scene_light->GetLightColor().b;
		}
		else
		{
			// Disbale this light by setting zero color.
			NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 4] = 0.0f;
			NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 5] = 0.0f;
			NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 6] = 0.0f;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxModelLights::plat_set_light_ambient_color( const Image::RGBA &rgba )
{
	m_ambient_color = rgba;
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA	CXboxModelLights::plat_get_light_ambient_color() const
{
	return m_ambient_color;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxModelLights::plat_set_light_direction( int light_index, const Mth::Vector &direction )
{
	m_diffuse_direction[light_index] = direction;
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector &	CXboxModelLights::plat_get_light_direction( int light_index ) const
{
	if( plat_is_diffuse_light_enabled( light_index ))
		return m_diffuse_direction[light_index];
	else
		return Nx::CLightManager::sGetLightDirection( light_index );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxModelLights::plat_set_light_diffuse_color( int light_index, const Image::RGBA &rgba )
{
	m_diffuse_color[light_index] = rgba;
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA	CXboxModelLights::plat_get_light_diffuse_color( int light_index ) const
{
	return m_diffuse_color[light_index];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxModelLights::plat_enable_ambient_light( bool enable )
{
	if( enable )
		m_flags |= mUSE_MODEL_AMBIENT;
	else
		m_flags &= ~mUSE_MODEL_AMBIENT;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxModelLights::plat_enable_diffuse_light( int light_index, bool enable )
{
	if( enable )
		m_flags |= ( light_index == 0 ) ? mUSE_MODEL_DIFFUSE_0 : mUSE_MODEL_DIFFUSE_1;
	else
		m_flags &= ~(( light_index == 0 ) ? mUSE_MODEL_DIFFUSE_0 : mUSE_MODEL_DIFFUSE_1 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxModelLights::plat_is_ambient_light_enabled() const
{
	return ( m_flags & mUSE_MODEL_AMBIENT ) > 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxModelLights::plat_is_diffuse_light_enabled( int light_index ) const
{
	return (( light_index == 0 ) ? (( m_flags & mUSE_MODEL_DIFFUSE_0 ) > 0 ) : (( m_flags & mUSE_MODEL_DIFFUSE_1 ) > 0 ));
}

} 
 


================================================
FILE: Code/Gfx/XBox/p_NxLight.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// p_NxLight.H - Neversoft Engine, Rendering portion, Platform dependent interface

#ifndef	__GFX_P_NX_LIGHT_H__
#define	__GFX_P_NX_LIGHT_H__

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
#include 
#include 

namespace Nx
{

///////////////////////////////////////////////////////////////////////////////////
// Nx::CXboxModelLights
class	CXboxModelLights : public CModelLights
{
public:
								CXboxModelLights();
	virtual						~CXboxModelLights();

private:	

	// Platform-specific calls
	virtual void				plat_update_engine( Mth::Vector & pos, bool add_scene_light );
	virtual bool				plat_set_light_ambient_color(const Image::RGBA &rgba);
	virtual bool				plat_set_light_direction(int light_index, const Mth::Vector &direction);
	virtual bool				plat_set_light_diffuse_color(int light_index, const Image::RGBA &rgba);
	virtual Image::RGBA			plat_get_light_ambient_color() const;
	virtual const Mth::Vector &	plat_get_light_direction(int light_index) const;
	virtual Image::RGBA			plat_get_light_diffuse_color(int light_index) const;

	virtual void				plat_update_brightness();

	virtual void				plat_enable_ambient_light(bool enable);
	virtual void				plat_enable_diffuse_light(int light_index, bool enable);
	virtual bool				plat_is_ambient_light_enabled() const;
	virtual bool				plat_is_diffuse_light_enabled(int light_index) const;

	uint32						m_flags;
	Image::RGBA					m_ambient_color;
	Image::RGBA					m_diffuse_color[CLightManager::MAX_LIGHTS];
	Mth::Vector					m_diffuse_direction[CLightManager::MAX_LIGHTS];
};


}


#endif



================================================
FILE: Code/Gfx/XBox/p_NxLightMan.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxLightMan.cpp - Xbox platform specific interface to CLightManager

#include 
#include "gfx/NxLightMan.h"
#include "gfx/xbox/p_NxLight.h"
#include "gfx/xbox/nx/nx_init.h"

namespace Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::s_plat_update_engine( void )
{
	NxXbox::EngineGlobals.ambient_light_color[0] = s_world_lights.m_light_ambient_rgba.r * ( 1.0f / 128.0f ) * s_ambient_brightness;
	NxXbox::EngineGlobals.ambient_light_color[1] = s_world_lights.m_light_ambient_rgba.g * ( 1.0f / 128.0f ) * s_ambient_brightness;
	NxXbox::EngineGlobals.ambient_light_color[2] = s_world_lights.m_light_ambient_rgba.b * ( 1.0f / 128.0f ) * s_ambient_brightness;

	for( int i = 0; i < 2; ++i )
	{
		Image::RGBA	dif = CLightManager::sGetLightDiffuseColor( i );
		NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 4] = dif.r * ( 1.0f / 128.0f ) * s_diffuse_brightness[i];
		NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 5] = dif.g * ( 1.0f / 128.0f ) * s_diffuse_brightness[i];
		NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 6] = dif.b * ( 1.0f / 128.0f ) * s_diffuse_brightness[i];

		Mth::Vector dir = CLightManager::sGetLightDirection( i );
		NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 0] = -dir[X];
		NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 1] = -dir[Y];
		NxXbox::EngineGlobals.directional_light_color[( i * 8 ) + 2] = -dir[Z];
	}

	// Set third color to black.
	NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 4] = 0.0f;
	NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 5] = 0.0f;
	NxXbox::EngineGlobals.directional_light_color[( 2 * 8 ) + 6] = 0.0f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::s_plat_update_lights( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CLightManager::s_plat_set_light_ambient_color( void )
{
	NxXbox::EngineGlobals.ambient_light_color[0] = s_world_lights.m_light_ambient_rgba.r * ( 1.0f / 128.0f ) * s_ambient_brightness;
	NxXbox::EngineGlobals.ambient_light_color[1] = s_world_lights.m_light_ambient_rgba.g * ( 1.0f / 128.0f ) * s_ambient_brightness;
	NxXbox::EngineGlobals.ambient_light_color[2] = s_world_lights.m_light_ambient_rgba.b * ( 1.0f / 128.0f ) * s_ambient_brightness;
	
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA	CLightManager::s_plat_get_light_ambient_color()
{
	return s_world_lights.m_light_ambient_rgba;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CLightManager::s_plat_set_light_direction( int light_index )
{
	int array_index = ( light_index * 8 );
	
	NxXbox::EngineGlobals.directional_light_color[array_index]		= -s_world_lights.m_light_direction[light_index][X];
	NxXbox::EngineGlobals.directional_light_color[array_index + 1]	= -s_world_lights.m_light_direction[light_index][Y];
	NxXbox::EngineGlobals.directional_light_color[array_index + 2]	= -s_world_lights.m_light_direction[light_index][Z];

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector & CLightManager::s_plat_get_light_direction( int light_index )
{
	static Mth::Vector dir;
	dir.Set( s_world_lights.m_light_direction[light_index][X], s_world_lights.m_light_direction[light_index][Y], s_world_lights.m_light_direction[light_index][Z] );
	return dir;
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CLightManager::s_plat_set_light_diffuse_color( int light_index )
{
	int array_index = ( light_index * 8 ) + 4;
	
	NxXbox::EngineGlobals.directional_light_color[array_index]		= s_world_lights.m_light_diffuse_rgba[light_index].r * ( 1.0f / 128.0f ) * s_diffuse_brightness[light_index];
	NxXbox::EngineGlobals.directional_light_color[array_index + 1]	= s_world_lights.m_light_diffuse_rgba[light_index].g * ( 1.0f / 128.0f ) * s_diffuse_brightness[light_index];
	NxXbox::EngineGlobals.directional_light_color[array_index + 2]	= s_world_lights.m_light_diffuse_rgba[light_index].b * ( 1.0f / 128.0f ) * s_diffuse_brightness[light_index];
	
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Image::RGBA	CLightManager::s_plat_get_light_diffuse_color( int light_index )
{
	return s_world_lights.m_light_diffuse_rgba[light_index];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLightManager::s_plat_update_colors( void )
{
	s_plat_set_light_ambient_color();
	for( int i = 0; i < MAX_LIGHTS; ++i )
	{
		s_plat_set_light_diffuse_color( i );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CModelLights *CLightManager::s_plat_create_model_lights()
{
	return new CXboxModelLights;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CLightManager::s_plat_free_model_lights( CModelLights *p_model_lights )
{
	Dbg_Assert( p_model_lights );

	delete p_model_lights;

	return true;
}



} 
 


================================================
FILE: Code/Gfx/XBox/p_NxLoadScreen.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxLoadScreen.cpp - Xbox platform specific interface for the load screen
//
//

#include	"gfx\Nx.h"
#include	"gfx\NxLoadScreen.h"
#include	"gfx\xbox\p_NxTexture.h"
#include	"gfx\xbox\p_NxSprite.h"
#include	"gfx\xbox\NX\sprite.h"
#include	"gfx\xbox\NX\scene.h"
#include	"gfx\xbox\NX\render.h"
#include	"sys\config\config.h"

#include	"core\macros.h"

namespace	Nx
{


Nx::CXboxTexture	*sp_load_screen_texture;
Nx::CXboxSprite	*sp_load_screen_sprite;

static float		loadingBarTotalSeconds;
static float		loadingBarCurrentSeconds;
static float		loadingBarDeltaSeconds;
static int			loadingBarStartColor[3];		// r,g,b
static int			loadingBarEndColor[3];			// r,g,b
static uint32		loadingBarColors[1280][3];		// r,g,b
static bool			loadingBarColorsSet = false;
static int			loadingBarWidth;
static uint32		loadingBarBorderColor;


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions

#define USE_SPRITES 0

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CALLBACK loadingBarTimerCallback( UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2 )
{
	if( NxXbox::EngineGlobals.loadingbar_timer_event != 0 )
	{
		loadingBarCurrentSeconds += loadingBarDeltaSeconds;
		float mult			= loadingBarCurrentSeconds / loadingBarTotalSeconds;
		mult				= ( mult > 1.0f ) ? 1.0f : mult;
		int bar_width		= (int)( loadingBarWidth * mult );
		
		// Get pointer to front buffer memory.
		IDirect3DSurface8	*p_buffer;
		D3DLOCKED_RECT		locked_rect;
		NxXbox::EngineGlobals.p_Device->GetBackBuffer( -1, D3DBACKBUFFER_TYPE_MONO, &p_buffer );
		p_buffer->LockRect( &locked_rect, NULL, D3DLOCK_TILED );
		uint32 *p_screen = (uint32*)locked_rect.pBits;

		// ----------------------------------
		// | ||||||||||||||||||||||          |
		// ----------------------------------
		// ^ ^                    ^          ^
		// a b                    c			 d
		// 
		// a = surround start
		// b = bar start
		// c = bar end
		// d = surround end
		int bar_start			= ( 640 - loadingBarWidth ) / 2;
		int surround_start		= bar_start - 5;
		int bar_end				= bar_start + bar_width;
		int surround_end		= surround_start + loadingBarWidth + 10;

		const int HDTV_OFFSET	= 48;	
		
		bar_start				= (int)SCREEN_CONV_X( bar_start );
		surround_start			= (int)SCREEN_CONV_X( surround_start );
		bar_end					= (int)SCREEN_CONV_X( bar_end );
		surround_end			= (int)SCREEN_CONV_X( surround_end );
		
		if( NxXbox::EngineGlobals.backbuffer_width > 640 )
		{
			bar_start			+= HDTV_OFFSET;
			surround_start		+= HDTV_OFFSET;
			bar_end				+= HDTV_OFFSET;
			surround_end		+= HDTV_OFFSET;
		}
		
		int base_y				= (int)SCREEN_CONV_Y( 410 );
		
		for( int i = 0; i < 20; ++i )
		{
			uint32 *p_loop = p_screen + (( base_y + i ) * NxXbox::EngineGlobals.backbuffer_width );

			if(( i < 5 ) || ( i >= 15 ))
			{
				for( int j = surround_start; j < surround_end; ++j )
					p_loop[j] = loadingBarBorderColor;
			}
			else
			{
				for( int j = surround_start; j < ( surround_start + 5 ); ++j )
					p_loop[j] = loadingBarBorderColor;

				for( int j = bar_end; j < surround_end; ++j )
					p_loop[j] = loadingBarBorderColor;

				for( int j = bar_start; j < bar_end; ++j )			
				{
					uint32 idx			= ( j - bar_start >= 1279 ) ? 1279 : ( j - bar_start );
					uint32 write_value	= 0x80000000UL | ( loadingBarColors[idx][0] << 16 ) | ( loadingBarColors[idx][1] << 8 ) | ( loadingBarColors[idx][2] << 0 );
					p_loop[j]			= write_value;
				}
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static bool is_power_of_two( uint32 a )
{
	if( a == 0 )
	{
		return false;
	}
	return (( a & ( a - 1 )) == 0 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLoadScreen::s_plat_display(const char* filename, bool just_freeze, bool blank)
{
	// Wait for asyncronous rendering to finish.
	NxXbox::EngineGlobals.p_Device->BlockUntilIdle();

	if( !just_freeze )
	{
		if( blank )
		{
			D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET, 0x00000000UL, 1.0f, 0 );
			D3DDevice_Swap( D3DSWAP_DEFAULT );
		}
		else
		{
			// Engine stuff
			Dbg_Assert(!sp_load_screen_texture);
			Dbg_Assert(!sp_load_screen_sprite);

			sp_load_screen_texture = new CXboxTexture();

#			ifdef __PAL_BUILD__
			switch( Config::GetLanguage())
			{
				case Config::LANGUAGE_FRENCH:
				{
					char t_filename[200];
					sprintf( t_filename, "PALImages/FRImages/%s", filename );
					if( !sp_load_screen_texture->LoadTexture( t_filename, true ))
					{
						Dbg_Error( "Can't load texture %s", t_filename );
						return;
					}
					break;
				}
				case Config::LANGUAGE_GERMAN:
				{
					char t_filename[200];
					sprintf( t_filename, "PALImages/GRImages/%s", filename );
					if( !sp_load_screen_texture->LoadTexture( t_filename, true ))
					{
						Dbg_Error( "Can't load texture %s", t_filename );
						return;
					}
					break;
				}
				default:
				{
					if( !sp_load_screen_texture->LoadTexture( filename, true ))
					{
						Dbg_Error( "Can't load texture %s", filename );
						return;
					}
					break;
				}
			}
#			else
			if( !sp_load_screen_texture->LoadTexture( filename, true ))
			{
				Dbg_Error( "Can't load texture %s", filename );
				return;
			}
#			endif // __PAL_BUILD__

			// Copy into frame buffer.
			float x_offset		= 0.0f;
			float y_offset		= 0.0f;
			float x_scale		= 1.0f;
			float y_scale		= 1.0f;
			float alpha_level	= 1.0f;
	
			D3DDevice_SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
			D3DDevice_SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
			D3DDevice_SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
			D3DDevice_SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );

			// Store the stage zero minfilter, since it may be anisotropic.
			DWORD	stage_zero_minfilter;
			D3DDevice_GetTextureStageState( 0, D3DTSS_MINFILTER, &stage_zero_minfilter );

			// Turn on texture filtering when scaling...
			D3DDevice_SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
			D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );

			// Turn on clamping so that the linear textures work
			NxXbox::set_render_state( RS_UVADDRESSMODE0, 0x00010001UL );

			D3DDevice_SetRenderState( D3DRS_LIGHTING,				FALSE );
	
			// Use a default vertex shader
			NxXbox::set_pixel_shader( 0 );
			NxXbox::set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_TEX1 );
			NxXbox::set_blend_mode( NxXbox::vBLEND_MODE_DIFFUSE );

			// Select the texture (flush first, since the screen texture is linear).
			NxXbox::set_texture( 0, NULL );
			NxXbox::set_texture( 0, sp_load_screen_texture->GetEngineTexture()->pD3DTexture, sp_load_screen_texture->GetEngineTexture()->pD3DPalette );

			// Setup up the vertices.
			typedef struct
			{
				float	sx,sy,sz;
				float	rhw;
				float	tu,tv;
			}
			LOADSCREEN_VERT;
	
			// Get the width height from the texture itself, since we may be using a texture not designed especially
			// for this screen dimension.
			float tex_w = (float)sp_load_screen_texture->GetEngineTexture()->ActualWidth;
			float tex_h = (float)sp_load_screen_texture->GetEngineTexture()->ActualHeight;
			float scr_w = (float)NxXbox::EngineGlobals.backbuffer_width;
			float scr_h = (float)NxXbox::EngineGlobals.backbuffer_height;

			LOADSCREEN_VERT	vertices[4];

			// The texture coordinate addressing will differ depending on whether this is a linear texture or not.
			if( is_power_of_two( sp_load_screen_texture->GetEngineTexture()->ActualWidth ) &&
				is_power_of_two( sp_load_screen_texture->GetEngineTexture()->ActualHeight ))
			{
				// Not a linear texture, will be swizzled, max uv is (1.0, 1.0).
				vertices[0].sx		= x_offset;
				vertices[0].sy		= y_offset;
				vertices[0].sz		= 0.0f;
				vertices[0].rhw		= 0.0f;
				vertices[0].tu		= 0.0f;
				vertices[0].tv		= 1.0f;
				vertices[1]			= vertices[0];
				vertices[1].sy		= y_offset + ( scr_h * y_scale );
				vertices[1].tv		= 0.0f;
				vertices[2]			= vertices[0];
				vertices[2].sx		= x_offset + ( scr_w * x_scale );
				vertices[2].tu		= 1.0f;
				vertices[3]			= vertices[2];
				vertices[3].sy		= vertices[1].sy;
				vertices[3].tv		= 0.0f;
			}
			else
			{
				// Linear texture, won't be swizzled, max uv is (tex_w, tex_h).
				vertices[0].sx		= x_offset;
				vertices[0].sy		= y_offset;
				vertices[0].sz		= 0.0f;
				vertices[0].rhw		= 0.0f;
				vertices[0].tu		= -0.5f;
				vertices[0].tv		= tex_h - 0.5f;
				vertices[1]			= vertices[0];
				vertices[1].sy		= y_offset + ( scr_h * y_scale );
				vertices[1].tv		= -0.5f;
				vertices[2]			= vertices[0];
				vertices[2].sx		= x_offset + ( scr_w * x_scale );
				vertices[2].tu		= tex_w - 0.5f;
				vertices[3]			= vertices[2];
				vertices[3].sy		= vertices[1].sy;
				vertices[3].tv		= -0.5f;
			}

			// Draw the vertices, and make sure they're displayed.
			D3DDevice_DrawVerticesUP( D3DPT_TRIANGLESTRIP, 4, vertices, sizeof( LOADSCREEN_VERT ));
			D3DDevice_Swap( D3DSWAP_DEFAULT );

			// Done with texture
			delete sp_load_screen_texture;
			sp_load_screen_texture = NULL;

			// Reflush linear texture out.
			NxXbox::set_texture( 0, NULL );

			// Restore the stage zero minfilter.
			D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, stage_zero_minfilter );
		}
	}

	// Indicate that the loading screen is visible, to stop any more rendering until it is hidden.
	NxXbox::EngineGlobals.loadingscreen_visible = true;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_hide()
{
	// Remove the loading bar.
	if( NxXbox::EngineGlobals.loadingbar_timer_event != 0 )
	{
		timeKillEvent( NxXbox::EngineGlobals.loadingbar_timer_event );
		NxXbox::EngineGlobals.loadingbar_timer_event = 0;
	}

	// Indicate that the loading screen is no longer visible.
	NxXbox::EngineGlobals.loadingscreen_visible = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLoadScreen::s_plat_clear()
{
	if( sp_load_screen_sprite )
	{
		CEngine::sDestroySprite(sp_load_screen_sprite);
		sp_load_screen_sprite = NULL;
	}

	if( sp_load_screen_texture )
	{
		delete sp_load_screen_texture;
		sp_load_screen_texture = NULL;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLoadScreen::s_plat_start_loading_bar( float seconds )
{
	loadingBarTotalSeconds		= seconds * 0.6f;
	loadingBarCurrentSeconds	= 0.0f;
	loadingBarDeltaSeconds		= 0.03f;	// 30 milliseconds.
	
	s_plat_update_bar_properties();

	// Set up the timer event.
	if( NxXbox::EngineGlobals.loadingbar_timer_event == 0 )
	{
		NxXbox::EngineGlobals.loadingbar_timer_event = timeSetEvent( (uint32)( loadingBarDeltaSeconds * 1000.0f ),	// Delay (ms).
																	 0,												// Ignored resolution (ms).
																	 loadingBarTimerCallback,						// Callback function.
																	 0,												// Callback data.
																	 TIME_PERIODIC | TIME_CALLBACK_FUNCTION );
		Dbg_Assert( NxXbox::EngineGlobals.loadingbar_timer_event != 0 );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CLoadScreen::s_plat_update_bar_properties( void )
{

	loadingBarStartColor[0] = s_bar_start_color.r;
	loadingBarStartColor[1] = s_bar_start_color.g;
	loadingBarStartColor[2] = s_bar_start_color.b;

	loadingBarEndColor[0]	= s_bar_end_color.r;
	loadingBarEndColor[1]	= s_bar_end_color.g;
	loadingBarEndColor[2]	= s_bar_end_color.b;

	loadingBarWidth			= s_bar_width;
	loadingBarBorderColor	= 0x80000000 | ((uint32)s_bar_border_color.r << 16 ) | ((uint32)s_bar_border_color.g << 8 )  | (uint32)s_bar_border_color.b;

	// Build the interpolated color array.
	int last_color = (int)( loadingBarWidth * NxXbox::EngineGlobals.screen_conv_x_multiplier );
	for( int i = 0; i < last_color; ++i )
	{
		for( int c = 0; c < 3; ++c )
		{
			loadingBarColors[i][c] = loadingBarStartColor[c] + ((( loadingBarEndColor[c] - loadingBarStartColor[c] ) * i ) / last_color );
		}
	}
}


} 
 


================================================
FILE: Code/Gfx/XBox/p_NxMesh.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxMesh.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/15/2002
//****************************************************************************

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//extern Nx::CXboxScene *p_skater;

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int cmp( const void *p1, const void *p2 )
{
	NxXbox::sCASData *p_casdata1 = (NxXbox::sCASData*)p1;
	NxXbox::sCASData *p_casdata2 = (NxXbox::sCASData*)p2;
	
	uint32 mesh1 = p_casdata1->data0 >> 16;
	uint32 mesh2 = p_casdata2->data0 >> 16;
	
	if( mesh1 > mesh2 )
	{
		return 1;
	}
	else if( mesh1 < mesh2 )
	{
		return -1;
	}
	else
	{
		uint32 indexzero1 = p_casdata1->data0 & 0xFFFF;
		uint32 indexzero2 = p_casdata2->data0 & 0xFFFF;
		if( indexzero1 > indexzero2 )
		{
			return 1;
		}
		else if( indexzero1 < indexzero2 )
		{
			return -1;
		}
	}
	return 0;
}


	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxMesh::build_casdata_table( const char* pFileName )
{
	void *pFile = File::Open( pFileName, "rb" );
	Dbg_MsgAssert( pFile, ( "Couldn't open CAS data file %s\n", pFileName ));

	uint32 version;
	File::Read( &version, sizeof( uint32 ), 1, pFile );

	if( version >= 2 )
	{
		File::Read( &m_CASRemovalMask, sizeof( uint32 ), 1, pFile );
	}
	
	File::Read( &m_numCASData, sizeof( int ), 1, pFile );

	if( m_numCASData > 0 )
	{
		// CAS flags.
		mp_CASData = new NxXbox::sCASData[m_numCASData];

		for( uint32 i = 0; i < m_numCASData; ++i )
		{
			File::Read( &mp_CASData[i].mask, sizeof( uint32 ) * 3, 1, pFile );
		}

		// Sort the CAS data based first on mesh, then on the first tri index, lowest to highest. This allows some efficient early-out checking
		// during the poly removal.
		qsort( mp_CASData, m_numCASData, sizeof( NxXbox::sCASData ), cmp );
	}

	File::Close( pFile );
	
	return ( m_numCASData > 0 );
}



#define MemoryRead( dst, size, num, src )	CopyMemory(( dst ), ( src ), (( num ) * ( size )));	\
											( src ) += (( num ) * ( size ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxMesh::build_casdata_table_from_memory( void **pp_mem )
{
	uint8 *p_data = (uint8*)( *pp_mem );

	uint32 version;
	MemoryRead( &version, sizeof( uint32 ), 1, p_data );

	if( version >= 2 )
	{
		MemoryRead( &m_CASRemovalMask, sizeof( uint32 ), 1, p_data );
	}
	
	MemoryRead( &m_numCASData, sizeof( int ), 1, p_data );

	if( m_numCASData > 0 )
	{
		// CAS flags.
		mp_CASData = new NxXbox::sCASData[m_numCASData];

		for( uint32 i = 0; i < m_numCASData; ++i )
		{
			MemoryRead( &mp_CASData[i].mask, sizeof( uint32 ) * 3, 1, p_data );
		}

		// Sort the CAS data based first on mesh, then on the first tri index, lowest to highest. This allows some efficient early-out checking
		// during the poly removal.
		qsort( mp_CASData, m_numCASData, sizeof( NxXbox::sCASData ), cmp );
	}
	
	// Set the data pointer to the new position on return.
	*pp_mem = p_data;

	return ( m_numCASData > 0 );
}



/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxMesh::CXboxMesh( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxMesh::CXboxMesh( const char *pMeshFileName )
{
	// Only do the CAS flag building for skinned objects.
	if( strstr( pMeshFileName, "skin" ))
	{
		char CASFileName[256];
		strcpy( CASFileName, pMeshFileName );
		Str::LowerCase( CASFileName );
		char *pExt = strstr( CASFileName, "skin.xbx" );
		if( pExt )
		{
			Dbg_MsgAssert( pExt, ( "Couldn't find skin.xbx extension in %s", CASFileName ));
			strcpy( pExt, "cas.xbx" );
			build_casdata_table( CASFileName );
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxMesh::~CXboxMesh( void )
{
	if( mp_CASData )
	{
		delete [] mp_CASData;
	}
	if( mp_scene )
	{
		CEngine::sUnloadScene( mp_scene );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxMesh::SetCASData( uint8 *p_cas_data )
{
	if( p_cas_data )
	{
		build_casdata_table_from_memory((void**)&p_cas_data );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxMesh::SetScene( CXboxScene *p_scene )
{
	mp_scene = p_scene;

	// Copy the hierarchy info from the scene so that above the p-line stuff can access it.
	mp_hierarchyObjects		= mp_scene->GetEngineScene()->mp_hierarchyObjects;
	m_numHierarchyObjects	= mp_scene->GetEngineScene()->m_numHierarchyObjects;

	// Now that we have a scene attached, resolve any cas flag data into specific indexes in the mesh data.
	if( mp_CASData )
	{
		NxXbox::sCASData *p_cas_entry = mp_CASData;
		for( uint32 entry = 0; entry < m_numCASData; ++entry, ++p_cas_entry )
		{
			// Get the mesh this entry references.
			uint32			load_order	= p_cas_entry->data0 >> 16;
			NxXbox::sMesh	*p_mesh		= p_scene->GetEngineScene()->GetMeshByLoadOrder( load_order );
			if( p_mesh )
			{
				// Get the indices of the poly referenced.
				uint32 i0 = p_cas_entry->data0 & 0xFFFF;
				uint32 i1 = p_cas_entry->data1 >> 16;
				uint32 i2 = p_cas_entry->data1 & 0xFFFF;

				// For every vertex index in this mesh...
				uint16 *p_indices	= p_mesh->mp_index_buffer[0];
				uint32 index0		= p_indices[0] & 0x7FFF;
				uint32 index1		= p_indices[1] & 0x7FFF;
				for( uint32 i = 2; i < p_mesh->m_num_indices[0]; ++i )
				{
					uint32 index2 = p_indices[i] & 0x7FFF;
					if(( index0 == i0 ) && ( index1 == i1 ) && ( index2 == i2 ))
					{
						p_cas_entry->start_index = i - 2;
						break;
					}
					index0	= index1;
					index1	= index2;
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx
				


================================================
FILE: Code/Gfx/XBox/p_NxMesh.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxMesh.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/15/2002
//****************************************************************************

#ifndef	__GFX_P_NX_MESH_H__
#define	__GFX_P_NX_MESH_H__
    
#include "gfx/nxmesh.h"
#include "p_nxscene.h"

namespace NxXbox
{
	struct sScene;
}
			 
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CMesh
    
class CXboxMesh : public CMesh
{
                                      
public:
						CXboxMesh( void );
						CXboxMesh( const char *pMeshFileName );
	virtual 			~CXboxMesh();
	void				SetScene( CXboxScene *p_scene );
	void				SetTexDict( Nx::CTexDict *p_tex_dict )	{ mp_texDict = p_tex_dict; }
	void				SetCASData( uint8 *p_cas_data );
	CXboxScene			*GetScene( void )						{ return mp_scene; }

	NxXbox::sCASData	*GetCASData( void )						{ return mp_CASData; }
	uint32				GetNumCASData( void )					{ return m_numCASData; }

protected:
	bool				build_casdata_table(const char* pFileName);
	bool				build_casdata_table_from_memory( void **pp_mem );

	NxXbox::sCASData	*mp_CASData;
	uint32				m_numCASData;

private:
	CXboxScene			*mp_scene;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx

#endif 


================================================
FILE: Code/Gfx/XBox/p_NxModel.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxModel.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/21/2002
//****************************************************************************

#include 

#include "gfx/nxmodel.h"
#include "gfx/skeleton.h"
#include 
#include "gfx/xbox/p_nxmodel.h"
#include "gfx/xbox/p_nxscene.h"
#include "gfx/xbox/p_nxgeom.h"
#include "gfx/xbox/nx/texture.h"
#include "gfx/xbox/nx/render.h"
			   
#include 
#include 

int			test_num_bones			= 0;
Mth::Matrix	*p_test_bone_matrices	= NULL;
Mth::Matrix	test_root_matrix;

namespace Nx
{

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxModel::plat_init_skeleton( int num_bones )
{
//	if ( !mp_instance ) return false;
//	Mth::Matrix * p_bone = new Mth::Matrix[numBones];
//
//	mp_instance->SetBoneTransforms( p_bone );
//	for ( int i = 0; i < numBones; i++ )
//	{
//		p_bone[i].Identity();
//	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/*
	bool CXboxModel::plat_load_file(const char* p_fileName)
{
	// Machine specific code here ............
	
	// TODO:  Make this more generalized

	// Load in the texture dictionary for the model.
	Lst::HashTable< NxXbox::sTexture > *p_texture_table = NxXbox::LoadTextureFile( "models/testskin/testskin.tex.xbx" );

    return true;
}
*/


/*
bool CXboxModel::plat_load_mesh( CMesh* pMesh )
{
	// The skeleton must exist by this point (unless it's a hacked up car).
	int numBones;
	numBones = mp_skeleton ? mp_skeleton->GetNumBones() : 1;
	
	Mth::Matrix temp;
	CXboxMesh *p_xbox_mesh = static_cast( pMesh );
	mp_instance = new NxXbox::CInstance( p_xbox_mesh->GetScene()->GetEngineScene(), temp, numBones, mp_boneTransforms );

    return true;
}
*/	
	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool CXboxModel::plat_unload_mesh( void )
{
	if ( mp_instance != NULL )
	{
		delete mp_instance;
		mp_instance = NULL;
	}

	return true;
}
*/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool CXboxModel::plat_set_render_mode(ERenderMode mode)
{
	// Machine specific code here ............
    return true;
}
*/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool CXboxModel::plat_set_color(uint8 r, uint8 g, uint8 b, uint8 a)
{
	// Machine specific code here ............
    return true;
}
*/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool CXboxModel::plat_set_visibility(uint32 mask)
{
    return true;
}
*/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool CXboxModel::plat_set_active( bool active )
{
	if( mp_instance )
	{
		mp_instance->SetActive( active );
	}
	return true;
}
*/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool CXboxModel::plat_set_scale(float scaleFactor)
{
	// Machine specific code here ............
    return true;
}
*/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool CXboxModel::plat_replace_texture(char* p_srcFileName, char* p_dstFileName)
{
	// Machine specific code here ............
    return true;
}
*/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool CXboxModel::plat_render( Mth::Matrix *pRootMatrix, Mth::Matrix *pBoneMatrices, int numBones )
{
	if( mp_instance )
	{
		mp_instance->SetTransform( *pRootMatrix );
	}
    return true;
}
*/



/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxModel::CXboxModel()
{
	mp_instance = NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxModel::~CXboxModel()
{
	// make sure it's deleted
//	plat_unload_mesh();

	if( mp_instance && mp_instance->GetBoneTransforms())
	{
		delete mp_instance->GetBoneTransforms();
		mp_instance->SetBoneTransforms( NULL );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxModel::plat_set_bounding_sphere( const Mth::Vector& boundingSphere )
{
	// Loop over all spheres.
	for( int i = 0; i < m_numGeoms; i++ )
	{
		mp_geom[i]->SetBoundingSphere( boundingSphere );
	}
}


	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Mth::Vector CXboxModel::plat_get_bounding_sphere( void )
{
	Mth::Vector sphere, sphere1, sum, diff;
	float dist;

	// This should probably never happen.
	if( m_numGeoms == 0 )
		return Mth::Vector( 0.0f, 0.0f, 0.0f, 0.0f );

	// Combine the spheres of all geoms (this should really be done once at load time).

	// Start with first sphere.
	sphere = mp_geom[0]->GetBoundingSphere();

	// Loop over remaining spheres, expanding as necessary.
	for( int i = 1; i < m_numGeoms; ++i )
	{
		// Get next sphere.
		sphere1 = mp_geom[i]->GetBoundingSphere();

		// Centre-to-centre vector, and distance.
		diff	= sphere1-sphere;
		dist	= diff.Length();

		// Test for sphere1 inside sphere.
		if( dist + sphere1[3] <= sphere[3] )
			continue;			// keep sphere

		// Test for sphere inside sphere1.
		if( dist + sphere[3] <= sphere1[3] )
		{
			sphere = sphere1;	// replace sphere
			continue;
		}

		// Otherwise make a larger sphere that contains both.
		sum			= sphere+sphere1;
		sphere		= 0.5f * ( sum + ( diff[3] / dist ) * diff );
		sphere[3]	= 0.5f * ( dist + sum[3] );
	}

	sphere[3] *= 2.0f;
	
	return sphere;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // Nx

				


================================================
FILE: Code/Gfx/XBox/p_NxModel.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxModel.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  1/8/2002
//****************************************************************************

#ifndef	__GFX_P_NX_MODEL_H__
#define	__GFX_P_NX_MODEL_H__
    
#include "gfx/nxmodel.h"
#include "gfx/xbox/nx/instance.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CModel
    
class CXboxModel : public CModel
{
public:
						CXboxModel();
	virtual 			~CXboxModel();
	NxXbox::CInstance	*GetInstance( void )								{ return mp_instance; }
	void				SetInstance( NxXbox::CInstance *p_instance )		{ mp_instance = p_instance; }

private:				// It's all private, as it is machine specific
	virtual Mth::Vector	plat_get_bounding_sphere( void );
	virtual void		plat_set_bounding_sphere( const Mth::Vector& boundingSphere );

	bool				plat_init_skeleton( int num_bones );

	NxXbox::CInstance	*mp_instance;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx

#endif 


================================================
FILE: Code/Gfx/XBox/p_NxParticleRibbonTrail.cpp
================================================
#include 
#include 

#include "gfx/xbox/p_nxparticleRibbonTrail.h"

extern DWORD PixelShader1;

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleRibbonTrail::CXboxParticleRibbonTrail()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleRibbonTrail::CXboxParticleRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum		= checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;
	m_mid_time		= -1.0f;
	m_history		= history;

	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

	// Create the engine representation.
	mp_engine_particle = new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_RIBBONTRAIL, texture_checksum, blendmode_checksum, fix, num_segments, history );

	// Default color.
	m_start_color = new Image::RGBA[m_num_vertex_buffers];
	m_mid_color = new Image::RGBA[m_num_vertex_buffers];
	m_end_color = new Image::RGBA[m_num_vertex_buffers];
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 255;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 255;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 255;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleRibbonTrail::~CXboxParticleRibbonTrail()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
	delete [] m_start_color;
	delete [] m_mid_color;
	delete [] m_end_color;
	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleRibbonTrail::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleRibbonTrail::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleRibbonTrail::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxParticleRibbonTrail::plat_get_num_particle_colors( void ) { return m_num_vertex_buffers; }
int CXboxParticleRibbonTrail::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CXboxParticleRibbonTrail::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CXboxParticleRibbonTrail::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CXboxParticleRibbonTrail::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CXboxParticleRibbonTrail::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CXboxParticleRibbonTrail::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CXboxParticleRibbonTrail::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CXboxParticleRibbonTrail::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CXboxParticleRibbonTrail::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CXboxParticleRibbonTrail::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CXboxParticleRibbonTrail::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CXboxParticleRibbonTrail::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CXboxParticleRibbonTrail::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleRibbonTrail::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v;
//		Mth::Vector		min, max;	// For dynamic bounding box calculation.

		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );

		Image::RGBA color[2];
		Image::RGBA *p_col0;
		Image::RGBA *p_col1;

		// Obtain push buffer lock.
		DWORD *p_push; 
		DWORD dwords_per_particle	= 16 * ( m_num_vertex_buffers - 1 );
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
		NxXbox::set_pixel_shader( PixelShader1 );
		
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		p_push = D3DDevice_BeginPush( dword_count + 32 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_QUADLIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v			= mp_vertices[0];

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}

				float terp = p_particle->m_time / p_particle->m_life;

				Mth::Vector	pos[2];
				p_v = &mp_vertices[0][lp*3];
				pos[0].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				p_v = &mp_vertices[1][lp*3];
				pos[1].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );

				Mth::Vector	part_vec = pos[1] - pos[0];
				Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
				perp_vec.Normalize();

				// Dynamic bounding box calculation.
//				if( lp == 0 )
//				{
//					min = pos[0];
//					max = pos[0];
//				}
//				else
//				{
//					if( pos[0][X] < min[X] ) min[X] = pos[0][X]; else if( pos[0][X] > max[X] ) max[X] = pos[0][X];
//					if( pos[0][Y] < min[Y] ) min[Y] = pos[0][Y]; else if( pos[0][Y] > max[Y] ) max[Y] = pos[0][Y];
//					if( pos[0][Z] < min[Z] ) min[Z] = pos[0][Z]; else if( pos[0][Z] > max[Z] ) max[Z] = pos[0][Z];
//				}

				float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		
				Mth::Vector tmp[4];

				if( m_mid_time >= 0.0f )
				{
					if ( terp < m_mid_time )
					{
						p_col0 = m_start_color;
						p_col1 = m_mid_color;

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = m_mid_color;
						p_col1 = m_end_color;

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = m_start_color;
					p_col1 = m_end_color;
				}

				Image::RGBA start = *p_col0++;
				Image::RGBA end = *p_col1++;

				color[0].b = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
				color[0].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
				color[0].r = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
				color[0].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));

				tmp[0]		= pos[0] + ( perp_vec * w );
				tmp[1]		= pos[0] - ( perp_vec * w );

				for( int c = 1; c < m_num_vertex_buffers; c++ )
				{
					start = *p_col0++;
					end = *p_col1++;

					color[1].b = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
					color[1].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
					color[1].r = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
					color[1].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));

					if( c > 1 )
					{
						p_v = &mp_vertices[c][lp*3];
						pos[1].Set( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
						part_vec = pos[1] - pos[0];
						perp_vec = Mth::CrossProduct( part_vec, at );
						perp_vec.Normalize();
					}

					tmp[2]		= pos[1] + ( perp_vec * w );
					tmp[3]		= pos[1] - ( perp_vec * w );

					p_push[0]	= *((DWORD*)&tmp[0][X] );
					p_push[1]	= *((DWORD*)&tmp[0][Y] );
					p_push[2]	= *((DWORD*)&tmp[0][Z] );
					p_push[3]	= *((DWORD*)&color[0] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[1][X] );
					p_push[1]	= *((DWORD*)&tmp[1][Y] );
					p_push[2]	= *((DWORD*)&tmp[1][Z] );
					p_push[3]	= *((DWORD*)&color[0] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[3][X] );
					p_push[1]	= *((DWORD*)&tmp[3][Y] );
					p_push[2]	= *((DWORD*)&tmp[3][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[2][X] );
					p_push[1]	= *((DWORD*)&tmp[2][Y] );
					p_push[2]	= *((DWORD*)&tmp[2][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;
					
					color[0] = color[1];
					pos[0] = pos[1];
					tmp[0] = tmp[2];
					tmp[1] = tmp[3];
				}
				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );

		// Set the mesh bounding box and sphere.
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMin( min );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMax( max );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center = D3DXVECTOR3( min[X] + (( max[X] - min[X] ) * 0.5f ), min[Y] + (( max[Y] - min[Y] ) * 0.5f ), min[Z] + (( max[Z] - min[Z] ) * 0.5f ));
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius = 360.0f;

		// And the scene bounding sphere.
//		mp_engine_particle->mp_scene->m_sphere_center = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center;
//		mp_engine_particle->mp_scene->m_sphere_radius = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius;
	}
}

} // Nx




================================================
FILE: Code/Gfx/XBox/p_NxParticleRibbonTrail.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLERibbonTrail_H__
#define	__GFX_P_NX_PARTICLERibbonTrail_H__
    
#include "gfx/xbox/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CXboxParticleRibbonTrail : public CParticle
{
	public:
								CXboxParticleRibbonTrail();
								CXboxParticleRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 					~CXboxParticleRibbonTrail();

	NxXbox::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }

	private:					// It's all private, as it is machine specific
	void						plat_render( void );
	void						plat_get_position( int entry, int list, float * x, float * y, float * z );
	void						plat_set_position( int entry, int list, float x, float y, float z );
	void						plat_add_position( int entry, int list, float x, float y, float z );
	int							plat_get_num_vertex_lists( void );
	int							plat_get_num_particle_colors( void );

	void						plat_set_sr( int entry, uint8 value );
	void						plat_set_sg( int entry, uint8 value );
	void						plat_set_sb( int entry, uint8 value );
	void						plat_set_sa( int entry, uint8 value );
	void						plat_set_mr( int entry, uint8 value );
	void						plat_set_mg( int entry, uint8 value );
	void						plat_set_mb( int entry, uint8 value );
	void						plat_set_ma( int entry, uint8 value );
	void						plat_set_er( int entry, uint8 value );
	void						plat_set_eg( int entry, uint8 value );
	void						plat_set_eb( int entry, uint8 value );
	void						plat_set_ea( int entry, uint8 value );
		
	int							m_num_vertex_buffers;
	int							m_history;
	float**						mp_vertices;
	uint32*						mp_colors;
	uint64						m_blend;
	NxXbox::sParticleSystem*	mp_engine_particle;

	Image::RGBA*				m_start_color;				// Start color for each corner.
	Image::RGBA*				m_mid_color;				// Mid color for each corner.
	Image::RGBA*				m_end_color;				// End color for each corner.
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/XBox/p_NxParticleShaded.cpp
================================================
#include 
#include 
#include "gfx/xbox/p_NxParticleShaded.h"

extern DWORD PixelShader0;

namespace Nx
{


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleShaded::CXboxParticleShaded()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleShaded::CXboxParticleShaded( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix )
{
	m_checksum		= checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;
	m_mid_time		= -1.0f;

	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices		= new float[max_particles * 3];

	// Create the engine representation.
	mp_engine_particle = new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_SHADED, texture_checksum, blendmode_checksum, fix );

	// Default color.
	for( int c = 0; c < 4; ++c )
	{
		m_start_color[c].r = 128;
		m_start_color[c].g = 128;
		m_start_color[c].b = 128;
		m_start_color[c].a = 255;

		m_mid_color[c].r = 128;
		m_mid_color[c].g = 128;
		m_mid_color[c].b = 128;
		m_mid_color[c].a = 255;

		m_end_color[c].r = 128;
		m_end_color[c].g = 128;
		m_end_color[c].b = 128;
		m_end_color[c].a = 255;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleShaded::~CXboxParticleShaded()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleShaded::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleShaded::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleShaded::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxParticleShaded::plat_get_num_particle_colors( void )
{
	return 4;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleShaded::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CXboxParticleShaded::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CXboxParticleShaded::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CXboxParticleShaded::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CXboxParticleShaded::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CXboxParticleShaded::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CXboxParticleShaded::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CXboxParticleShaded::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CXboxParticleShaded::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CXboxParticleShaded::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CXboxParticleShaded::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CXboxParticleShaded::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleShaded::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Concatenate p_matrix with the emmission angle to create the direction.
		Mth::Vector up( 0.0f, 1.0f, 0.0f );

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
		Mth::Vector screen_right	= Mth::CrossProduct( at, up );
		Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

		screen_right.Normalize();
		screen_up.Normalize();

		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v;
//		Mth::Vector		min, max;	// For dynamic bounding box calculation.
	
		// Obtain push buffer lock.
		DWORD *p_push; 
		DWORD dwords_per_particle	= 24;
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
		NxXbox::set_pixel_shader( PixelShader0 );
		
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		p_push = D3DDevice_BeginPush( dword_count + 16 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_QUADLIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v			= mp_vertices;

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}

				float terp	= p_particle->m_time / p_particle->m_life;
				float w		= p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
				float h		= p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

				// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
				Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				Mth::Vector	ss_right, ss_up, ss_pos;
				Mth::Vector tmp;
	
				// Dynamic bounding box calculation.
//				if( lp == 0 )
//				{
//					min = pos;
//					max = pos;
//				}
//				else
//				{
//					if( pos[X] < min[X] ) min[X] = pos[X]; else if( pos[X] > max[X] ) max[X] = pos[X];
//					if( pos[Y] < min[Y] ) min[Y] = pos[Y]; else if( pos[Y] > max[Y] ) max[Y] = pos[Y];
//					if( pos[Z] < min[Z] ) min[Z] = pos[Z]; else if( pos[Z] > max[Z] ) max[Z] = pos[Z];
//				}
		
				ss_right	= screen_right * w;
				ss_up		= screen_up * h;

				Image::RGBA color[4];
				Image::RGBA *p_col0;
				Image::RGBA *p_col1;
		
				if( m_mid_time >= 0.0f )
				{
					if( terp < m_mid_time )
					{
						p_col0 = m_start_color;
						p_col1 = m_mid_color;

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = m_mid_color;
						p_col1 = m_end_color;

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = m_start_color;
					p_col1 = m_end_color;
				}

				for ( int c = 0; c < 4; c++ )
				{
					Image::RGBA start = *p_col0++;
					Image::RGBA end = *p_col1++;

					// Swap red and blue here.
					color[c].b = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
					color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
					color[c].r = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
					color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
				}
		
				tmp			= pos - ss_right + ss_up;
				p_push[0]	= *((DWORD*)&tmp[X] );
				p_push[1]	= *((DWORD*)&tmp[Y] );
				p_push[2]	= *((DWORD*)&tmp[Z] );
				p_push[3]	= *((DWORD*)&color[0] );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x00000000UL;
				p_push		+= 6;
	
				tmp			= pos + ss_right + ss_up;		
				p_push[0]	= *((DWORD*)&tmp[X] );
				p_push[1]	= *((DWORD*)&tmp[Y] );
				p_push[2]	= *((DWORD*)&tmp[Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push[4]	= 0x3F800000UL;
				p_push[5]	= 0x00000000UL;
				p_push		+= 6;

				tmp			= pos + ss_right - ss_up;		
				p_push[0]	= *((DWORD*)&tmp[X] );
				p_push[1]	= *((DWORD*)&tmp[Y] );
				p_push[2]	= *((DWORD*)&tmp[Z] );
				p_push[3]	= *((DWORD*)&color[2] );
				p_push[4]	= 0x3F800000UL;
				p_push[5]	= 0x3F800000UL;
				p_push		+= 6;
		
				tmp			= pos - ss_right - ss_up;		
				p_push[0]	= *((DWORD*)&tmp[X] );
				p_push[1]	= *((DWORD*)&tmp[Y] );
				p_push[2]	= *((DWORD*)&tmp[Z] );
				p_push[3]	= *((DWORD*)&color[3] );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x3F800000UL;
				p_push		+= 6;

				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );

		// Set the mesh bounding box and sphere.
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMin( min );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMax( max );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center = D3DXVECTOR3( min[X] + (( max[X] - min[X] ) * 0.5f ), min[Y] + (( max[Y] - min[Y] ) * 0.5f ), min[Z] + (( max[Z] - min[Z] ) * 0.5f ));
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius = 360.0f;

		// And the scene bounding sphere.
//		mp_engine_particle->mp_scene->m_sphere_center = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center;
//		mp_engine_particle->mp_scene->m_sphere_radius = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius;
	}
}

} // Nx

				




================================================
FILE: Code/Gfx/XBox/p_NxParticleShaded.h
================================================
#ifndef	__GFX_P_NX_PARTICLESHADED_H__
#define	__GFX_P_NX_PARTICLESHADED_H__
    
#include "gfx/nxparticle.h"
#include "gfx/xbox/nx/particles.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CXboxParticleShaded : public CParticle
{
	public:
							CXboxParticleShaded();
							CXboxParticleShaded( uint32 checksum, int max_particles = 256, uint32 texture_checksum = 0, uint32 blendmode_checksum = 0, int fix = 128 );
	virtual 				~CXboxParticleShaded();

	NxXbox::sParticleSystem	*GetEngineParticle( void )	{ return mp_engine_particle; }


	private:				// It's all private, as it is machine specific
	virtual void			plat_render( void );
	virtual void			plat_get_position( int entry, int list, float *x, float *y, float *z );
	virtual void			plat_set_position( int entry, int list, float x, float y, float z );
	virtual void			plat_add_position( int entry, int list, float x, float y, float z );
	virtual int				plat_get_num_particle_colors( void );
	virtual int				plat_get_num_vertex_lists( void )		{ return 1; }

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );

	Image::RGBA				m_start_color[4];			// Start color for each corner.
	Image::RGBA				m_mid_color[4];				// Mid color for each corner.
	Image::RGBA				m_end_color[4];				// End color for each corner.
	
	float*					mp_vertices;
	NxXbox::sParticleSystem	*mp_engine_particle;

};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx

#endif 





================================================
FILE: Code/Gfx/XBox/p_NxParticleStar.cpp
================================================
#include 
#include "gfx/xbox/nx/render.h"

#include "gfx/xbox/p_nxparticleStar.h"

extern DWORD PixelShader1;

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleStar::CXboxParticleStar()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleStar::CXboxParticleStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split )
{
	m_checksum		= checksum;
	m_max_particles	= max_particles;
	m_num_particles	= 0;
	m_mid_time		= -1.0f;
	m_num_segments	= num_segments;
	m_split			= split;

	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

	// Create the engine representation.
	mp_engine_particle = new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_STAR, texture_checksum, blendmode_checksum, fix, num_segments );

	// Default color.
	for ( int lp = 0; lp < 3; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 255;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 255;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 255;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleStar::~CXboxParticleStar()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleStar::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleStar::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleStar::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxParticleStar::plat_get_num_particle_colors( void ) { return 3; }
int CXboxParticleStar::plat_get_num_vertex_lists( void ) { return 1; }
void CXboxParticleStar::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CXboxParticleStar::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CXboxParticleStar::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CXboxParticleStar::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CXboxParticleStar::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CXboxParticleStar::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CXboxParticleStar::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CXboxParticleStar::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CXboxParticleStar::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CXboxParticleStar::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CXboxParticleStar::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CXboxParticleStar::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }


		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleStar::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Concatenate p_matrix with the emmission angle to create the direction.
		Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
		Mth::Vector screen_right	= Mth::CrossProduct( at, up );
		Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

		screen_right.Normalize();
		screen_up.Normalize();
	
		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v;
//		Mth::Vector		min, max;	// For dynamic bounding box calculation.

		// Obtain push buffer lock.
		DWORD *p_push; 
		DWORD dwords_per_particle	= 16 * m_num_segments;
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
		NxXbox::set_pixel_shader( PixelShader1 );
		
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		p_push = D3DDevice_BeginPush( dword_count + 32 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_QUADLIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v			= mp_vertices;

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}

				float terp	= p_particle->m_time / p_particle->m_life;
				float w		= p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
				float h		= p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

				// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
				Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				Mth::Vector	ss_right, ss_up;
				Mth::Vector tmp[3];
	
				// Dynamic bounding box calculation.
//				if( lp == 0 )
//				{
//					min = pos;
//					max = pos;
//				}
//				else
//				{
//					if( pos[X] < min[X] ) min[X] = pos[X]; else if( pos[X] > max[X] ) max[X] = pos[X];
//					if( pos[Y] < min[Y] ) min[Y] = pos[Y]; else if( pos[Y] > max[Y] ) max[Y] = pos[Y];
//					if( pos[Z] < min[Z] ) min[Z] = pos[Z]; else if( pos[Z] > max[Z] ) max[Z] = pos[Z];
//				}

				ss_right	= screen_right * w;
				ss_up		= screen_up * h;
	
				Image::RGBA color[3];
				Image::RGBA *p_col0;
				Image::RGBA *p_col1;

				if( m_mid_time >= 0.0f )
				{
					if( terp < m_mid_time )
					{
						p_col0 = m_start_color;
						p_col1 = m_mid_color;

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = m_mid_color;
						p_col1 = m_end_color;

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = m_start_color;
					p_col1 = m_end_color;
				}

				for( int c = 0; c < 3; c++ )
				{
					Image::RGBA start = *p_col0++;
					Image::RGBA end = *p_col1++;

					// Swap red and blue here.
					color[c].b = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
					color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
					color[c].r = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
					color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
				}

				tmp[0]  = pos;
				tmp[0] += ss_right * sinf( Mth::DegToRad( 0.0f ) ) * m_split;
				tmp[0] += ss_up    * cosf( Mth::DegToRad( 0.0f ) ) * m_split;

				for( int lp2 = 0; lp2 < m_num_segments; lp2++ )
				{
					tmp[1]  = pos;
					tmp[1] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;
					tmp[1] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments) ) * ( lp2 + 1 ) ) ) ) * m_split;

					tmp[2]  = pos;
					tmp[2] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );
					tmp[2] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );

					p_push[0]	= *((DWORD*)&pos[X] );
					p_push[1]	= *((DWORD*)&pos[Y] );
					p_push[2]	= *((DWORD*)&pos[Z] );
					p_push[3]	= *((DWORD*)&color[0] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[0][X] );
					p_push[1]	= *((DWORD*)&tmp[0][Y] );
					p_push[2]	= *((DWORD*)&tmp[0][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[2][X] );
					p_push[1]	= *((DWORD*)&tmp[2][Y] );
					p_push[2]	= *((DWORD*)&tmp[2][Z] );
					p_push[3]	= *((DWORD*)&color[2] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[1][X] );
					p_push[1]	= *((DWORD*)&tmp[1][Y] );
					p_push[2]	= *((DWORD*)&tmp[1][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					tmp[0] = tmp[1];
				}
				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );

		// Set the mesh bounding box and sphere.
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMin( min );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMax( max );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center = D3DXVECTOR3( min[X] + (( max[X] - min[X] ) * 0.5f ), min[Y] + (( max[Y] - min[Y] ) * 0.5f ), min[Z] + (( max[Z] - min[Z] ) * 0.5f ));
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius = 360.0f;

		// And the scene bounding sphere.
//		mp_engine_particle->mp_scene->m_sphere_center = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center;
//		mp_engine_particle->mp_scene->m_sphere_radius = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius;
	}
}

} // Nx





================================================
FILE: Code/Gfx/XBox/p_NxParticleStar.h
================================================
#ifndef	__GFX_P_NX_PARTICLEStar_H__
#define	__GFX_P_NX_PARTICLEStar_H__
    
#include "gfx/xbox/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CXboxParticleStar : public CParticle
{
	public:
							CXboxParticleStar();
							CXboxParticleStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split );
	virtual 				~CXboxParticleStar();

	NxXbox::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }

	private:				// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	float*					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;

	NxXbox::sParticleSystem*	mp_engine_particle;
	int						m_num_segments;
	float					m_split;
	Image::RGBA				m_start_color[3];			// Start color for each corner.
	Image::RGBA				m_mid_color[3];				// Mid color for each corner.
	Image::RGBA				m_end_color[3];				// End color for each corner.
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/XBox/p_NxSprite.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxSprite.cpp

#include 	"Gfx/xbox/p_NxSprite.h"
#include 	"Gfx/xbox/p_NxTexture.h"

namespace Nx
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CSprite

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CXboxSprite::CXboxSprite()
{
	mp_plat_sprite = new NxXbox::sSprite();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CXboxSprite::~CXboxSprite()
{
	delete mp_plat_sprite;
}

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CXboxSprite::plat_initialize()
{
	plat_update_engine();
	plat_update_priority();
	plat_update_hidden();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxSprite::plat_update_hidden()
{
	// Take sprite on or off draw list
	mp_plat_sprite->SetHidden( m_hidden );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxSprite::plat_update_engine()
{
	CXboxTexture *p_xbox_texture = static_cast( mp_texture );

	if( p_xbox_texture )
	{
		// Rebuild sprite primitives
		mp_plat_sprite->mp_texture	= p_xbox_texture->GetEngineTexture();
	}
	
	mp_plat_sprite->m_xpos		= m_pos_x;
	mp_plat_sprite->m_ypos		= m_pos_y;
	mp_plat_sprite->m_width		= m_width;
	mp_plat_sprite->m_height	= m_height;
	mp_plat_sprite->m_scale_x	= m_scale_x;
	mp_plat_sprite->m_scale_y	= m_scale_y;

	mp_plat_sprite->m_xhot		= (( m_anchor_x + 1.0f ) * 0.5f) * ( m_width );
	mp_plat_sprite->m_yhot		= (( m_anchor_y + 1.0f ) * 0.5f) * ( m_height );

	mp_plat_sprite->m_rot		= m_rotation;
	mp_plat_sprite->m_rgba		= *((uint32 *) &m_rgba);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxSprite::plat_update_priority()
{
	// Update draw list.
	if( mp_plat_sprite )
	{
		mp_plat_sprite->SetPriority( m_priority );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSprite::plat_enable_constant_z_value(bool enable)
{
	//NxPs2::SSprite::EnableConstantZValue(enable);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CSprite::plat_set_constant_z_value(Nx::ZBufferValue z)
{
	//NxPs2::SSprite::SetConstantZValue(z);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::ZBufferValue	CSprite::plat_get_constant_z_value()
{
	//return NxPs2::SSprite::GetConstantZValue();
	return 0;
}

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/XBox/p_NxSprite.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.h

#ifndef	__GFX_P_NX_SPRITE_H__
#define	__GFX_P_NX_SPRITE_H__

#include 	"Gfx/NxSprite.h"
#include 	"Gfx/xbox/NX/texture.h"
#include 	"Gfx/xbox/NX/sprite.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Machine specific implementation of the CSprite
class	CXboxSprite : public CSprite
{
public:
								CXboxSprite();
	virtual						~CXboxSprite();

private:		// It's all private, as it is machine specific
	virtual void				plat_initialize();

	virtual void				plat_update_hidden();		// Tell engine of update
	virtual void				plat_update_engine();		// Update engine primitives
	virtual void				plat_update_priority();

	NxXbox::sSprite *			mp_plat_sprite;
};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/XBox/p_NxTextured3dPoly.cpp
================================================
#include 	"Gfx/Xbox/p_NxTextured3dPoly.h"

namespace Nx
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CTextured3dPoly

CXboxTextured3dPoly::CXboxTextured3dPoly( void )
{
}

CXboxTextured3dPoly::~CXboxTextured3dPoly( void )
{
}

void CXboxTextured3dPoly::plat_render( void )
{
}

} // Namespace NxXbox  			
				
				


================================================
FILE: Code/Gfx/XBox/p_NxTextured3dPoly.h
================================================
#ifndef	__GFX_P_NX_TEXTURED_3D_POLY_H__
#define	__GFX_P_NX_TEXTURED_3D_POLY_H__

#include 

namespace Nx
{

// Machine specific implementation of CTextured3dPoly
class	CXboxTextured3dPoly : public Nx::CTextured3dPoly
{
public:
		   					CXboxTextured3dPoly();
	virtual					~CXboxTextured3dPoly();
private:
	virtual void			plat_render();
};

}	// namespace NxXbox

#endif
				   


================================================
FILE: Code/Gfx/XBox/p_NxViewMan.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxViewMan.cpp - Xbox platform specific interface to CViewportManager

#include 

#include "gfx/NxViewMan.h"
#include "gfx/Xbox/p_NxViewport.h"

namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CViewport *CViewportManager::s_plat_create_viewport( const Mth::Rect *rect, Gfx::Camera *cam )
{
	return new CXboxViewport( rect, cam );
}

} 
 


================================================
FILE: Code/Gfx/XBox/p_NxViewport.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxViewport.cpp

#include 	"Gfx/NxViewMan.h"
#include 	"Gfx/Xbox/p_NxViewport.h"

namespace Nx
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CViewport

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxViewport::CXboxViewport()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxViewport::CXboxViewport( const Mth::Rect *rect, Gfx::Camera *cam) : CViewport( rect, cam )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxViewport::~CXboxViewport()
{
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float CXboxViewport::plat_transform_to_screen_coord( const Mth::Vector & world_pos, float & screen_pos_x, float & screen_pos_y, ZBufferValue & screen_pos_z )
{
	D3DXVECTOR3	in( world_pos[X], world_pos[Y], world_pos[Z] );
	D3DXVECTOR4	mid;
	D3DXVECTOR3	out;
	D3DXVec3Transform( &mid, &in, (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix );

	in.x = mid.x;
	in.y = mid.y;
	in.z = mid.z;
	D3DXVec3TransformCoord( &out, &in, (D3DXMATRIX*)&NxXbox::EngineGlobals.projection_matrix );

	// Convert from homogenous cube (-1.0, 1.0) to pixel coordinates. Have to be careful here, the initial
	// feeling is to use the backbuffer width and height as multipliers, but since this value is being used
	// for text which has it's position re-converted at draw time, we actually want to use the fixed Ps2 screen
	// values here.
	screen_pos_x = (( out.x + 1.0f ) * 0.5f ) * 640.0f;
	screen_pos_y = (( -out.y + 1.0f ) * 0.5f ) * 448.0f;
	
	float scale = -1.0f;
	
	if( out.z < 1.0f )
	{
		// Text is not clipped. Calculate scale factor.
		float d_noscale = Nx::CViewportManager::sGet2DIn3DSpaceNoscaleDistance();
		float s_min		= Nx::CViewportManager::sGet2DIn3DSpaceMinScale();
		float s_max		= Nx::CViewportManager::sGet2DIn3DSpaceMaxScale();
		
		scale = d_noscale / -mid.z;

		// Clamp scale.
		if( scale > s_max )
		{
			scale = s_max;
		}
		else if( scale < s_min )
		{
			scale = s_min;
		}
		
		// The z value is passed in as a uint32, so set the value here based on fixed point 16:16.
		screen_pos_z = (uint32)( out.z * 65536.0f );
	}
		
	// This is the scale factor. Returning a value < 0 will cause the text to be hidden.
	return scale;
}



/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/XBox/p_NxViewport.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxViewport.h

#ifndef	__GFX_P_NX_VIEWPORT_H__
#define	__GFX_P_NX_VIEWPORT_H__

#include "Gfx/NxViewport.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Machine specific implementation of the CViewport
class	CXboxViewport : public CViewport
{
public:
								CXboxViewport();
								CXboxViewport( const Mth::Rect *rect, Gfx::Camera *cam = NULL );
	virtual						~CXboxViewport();

private:		// It's all private, as it is machine specific
//	virtual void				plat_initialize();
	virtual float				plat_transform_to_screen_coord( const Mth::Vector & world_pos, float & screen_pos_x, float & screen_pos_y, ZBufferValue & screen_pos_z );

};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/XBox/p_NxWin2D.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxWin2D.cpp

#include 	"Gfx/Xbox/p_NxWin2D.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxWindow2D::CXboxWindow2D( int x, int y, int width, int height) : CWindow2D( x, y, width, height )
{
	plat_update_engine();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxWindow2D::~CXboxWindow2D()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxWindow2D::plat_update_engine()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CWindow2DManager::s_plat_alloc_window2d_pool()
{
	for( int i = 0; i < vMAX_WINDOW_INSTANCES; i++ )
	{
	   	CXboxWindow2D *p_window = new CXboxWindow2D;
		p_window->mp_next	= sp_window_list;
		sp_window_list		= p_window;
	}
}

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/XBox/p_NxWin2D.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxWin2D.h

#ifndef	__GFX_P_NX_WIN2D_H__
#define	__GFX_P_NX_WIN2D_H__

#include 	"Gfx/NxWin2D.h"
#include 	"Gfx/Xbox/NX/sprite.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CWindow2D
class	CXboxWindow2D : public CWindow2D
{
public:
								CXboxWindow2D( int x = 0, int y = 0, int width = 640, int height = 480 );
								CXboxWindow2D( const Mth::Rect & win_rect);
	virtual						~CXboxWindow2D();

private:
	//
	virtual void				plat_update_engine();		// Update engine primitives
};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/XBox/p_gfxman.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		p_gfxman.cpp											**
**																			**
**	Created:		07/26/99	-	mjb										**
**																			**
**	Description:	Graphics device manager									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/
		
#include 
#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

namespace Gfx
{


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Manager::SetGammaNormalized( float fr, float fg, float fb )
{
	NxXbox::SetGammaNormalized( fr, fg, fb );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Manager::GetGammaNormalized( float *fr, float *fg, float *fb )
{
	NxXbox::GetGammaNormalized( fr, fg, fb );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::DumpVRAMUsage( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::ScreenShot( const char *fileroot )
{
	// Called twice per frame - once to request the screenshot, and once (post Swap()), to actually perform it.
	if( NxXbox::EngineGlobals.screenshot_name[0] == 0 )
	{
		strcpy( NxXbox::EngineGlobals.screenshot_name, fileroot );
		return;
	}
	
	char fileName[32];
	char fullFileName[64];
	
	// Try to find a good filename of the format filebasexxx.bmp. "Good" is defined here as one that isn't already used.
	for( int i = 0;; ++i )
	{
		sprintf( fileName, "screens\\%s%03d.bmp", fileroot, i );

		// Found an unused one! Yay!
		if( !File::Exist( fileName ))
		{
			sprintf( fullFileName, "d:\\data\\screens\\%s%03d.bmp", fileroot, i );
			break;
		}
	}
	
	// Obtain the render surface.
	IDirect3DSurface8 *p_render_target = NxXbox::EngineGlobals.p_RenderSurface;

	// Get the surface description, just for s and g.
	D3DSURFACE_DESC surface_desc;
	p_render_target->GetDesc( &surface_desc );

	// This is great - this function spits surfaces straight out into a file.
	HRESULT hr = XGWriteSurfaceToFile( p_render_target, fullFileName );
	Dbg_Assert( hr == S_OK );
}






/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


} // namespace Gfx


================================================
FILE: Code/Gfx/XBox/p_loadscreen.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Gfx														**
**																			**
**	Module:			LoadScreen			 									**
**																			**
**	File name:		p_loadscreen.cpp										**
**																			**
**	Created by:		05/08/02	-	SPG										**
**																			**
**	Description:	XBox-specific loading screen calls						**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace LoadScreen
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

// Skate3 code, disabled for now.
#if 0
extern "C" RwBool _rwXbSetRenderState(RwRenderState nState, void *pParam);
extern "C" RwBool _rwXbGetRenderState(RwRenderState nState, void *pParam);
extern "C" RwBool _rwXbRenderStateTextureRaster(RwRaster *raster);
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

class LoadingIconData
{
public:
	int	m_X;
	int	m_Y;
	int m_FrameDelay;
//	Skate3 code, disabled for now.
#	if 0
	RwRaster** m_Rasters;
#	endif
	int m_NumFrames;
};

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static bool				s_loading_screen_on		= false;
static MMRESULT			s_timer_event			= 0;
static LoadingIconData	s_load_icon_data		= { 0 };


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/
void CALLBACK loadIconTimerCallback( UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2 )
{
	static int current_image = 0;

//	Skate3 code, disabled for now.
#	if 0
	
	// Here is where the load icon will be written to the screen.
	RwRect rect;
	rect.x = s_load_icon_data.m_X;
	rect.y = s_load_icon_data.m_Y;

	rect.w = SCREEN_CONV_X( 32.0f ) - SCREEN_CONV_X( 0.0f );
	rect.h = SCREEN_CONV_Y( 32.0f ) - SCREEN_CONV_Y( 0.0f );

	Spt::SingletonPtr< Gfx::Manager > gfx_manager;

	_rwXbSetRenderState( rwRENDERSTATETEXTURERASTER, (void *)NULL );

	RwRaster* p_context_raster;
	p_context_raster = gfx_manager->DefaultViewport().GetFrameBuffer();

	RwRasterPushContext( p_context_raster );

	RwRasterRenderScaled( s_load_icon_data.m_Rasters[current_image], &rect );
	RwRasterShowRaster( p_context_raster, NULL, 0 );

	RwRasterPopContext();

#	endif
	
	current_image = ( current_image + 1 ) % s_load_icon_data.m_NumFrames ;
}



/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void SetLoadingIconProperties( int x, int y, int frame_delay, int num_frames, char* basename, char* ext )
{
	int i;

//	Skate3 code, disabled for now.
#	if 0

	if( s_load_icon_data.m_NumFrames > 0  )
	{   
		for( i = 0; i < s_load_icon_data.m_NumFrames; i++ )
		{
			RwRasterDestroy( s_load_icon_data.m_Rasters[i] );
		}
		delete [] s_load_icon_data.m_Rasters;
	}

	s_load_icon_data.m_X			= SCREEN_CONV_X( x );
	s_load_icon_data.m_Y			= SCREEN_CONV_Y( y );
	s_load_icon_data.m_FrameDelay = frame_delay;
	s_load_icon_data.m_NumFrames = num_frames;
	s_load_icon_data.m_Rasters = new RwRaster*[s_load_icon_data.m_NumFrames];

	RwImageSetPath("");
	for( i = 0; i < s_load_icon_data.m_NumFrames; i++ )
	{
		char image_name[64];
		//sprintf( image_name, "Images/PanelSprites/wheel_0%d.png", i+1 );
		sprintf( image_name, "%s%d.%s", basename, i+1, ext );
		s_load_icon_data.m_Rasters[i] = RwRasterRead( image_name );
	}

#	endif
}

void Display( char* filename, bool just_freeze, bool blank )
{
	// Redundancy Check
	if( s_loading_screen_on )
	{
		return;
	}
    
	// Set filename up for foreign languages.
	char name[256];

	// Seems that filename may sometimes be NULL.
	if( filename )
	{
		char * p8 = &filename[strlen(filename)];

		if( IsEnglish())
		{
			strcpy ( name, filename );
		}
		else
		{
			switch ( XGetLanguage() ) {
				case XC_LANGUAGE_FRENCH:
					while ( ( *p8 != '\\' ) && ( *p8 != '/' ) ) p8--;
					sprintf( name, "images\\frimages\\%s", &p8[1] );
					break;
				case XC_LANGUAGE_GERMAN:
					while ( ( *p8 != '\\' ) && ( *p8 != '/' ) ) p8--;
					sprintf( name, "images\\grimages\\%s", &p8[1] );
					break;
				default:
					strcpy ( name, filename );
					break;
			}
		}
	}

//	Tmr::Time start_time = Tmr::GetTime();

//	Skate3 code, disabled for now.
#	if 0
    if (!just_freeze && ! blank)
	{
		RwImageSetPath( "" );
		RwRaster* raster = RwRasterRead( name );
	
		if( raster == NULL )
		{
			// Try the english version.
			raster = RwRasterRead( filename );
			if( raster == NULL )
			{
				Dbg_Printf( "Could not load %s\n", filename );
				return;
			}
		}

		// Wait a few VBlanks for RW stuff to disappear.
//		while( Tmr::GetTime() - start_time < Tmr::VBlanks( 4 ));

		RwRect rect;
		rect.x = SCREEN_CONV_X( 0 );
		rect.y = SCREEN_CONV_Y( 0 );
		rect.w = SCREEN_CONV_X( 640 ) - SCREEN_CONV_X( 0 );
		rect.h = SCREEN_CONV_Y( 448 ) - SCREEN_CONV_Y( 0 );

		Spt::SingletonPtr< Gfx::Manager > gfx_manager;

		_rwXbSetRenderState( rwRENDERSTATETEXTURERASTER, (void *)NULL );

		RwRaster* p_context_raster;
		p_context_raster = gfx_manager->DefaultViewport().GetFrameBuffer();

		RwInt32 format = p_context_raster->cFormat & 0x0000FF00L;
		RwRGBA black = { 0, 0, 0, 0 }; 

		RwRasterPushContext( p_context_raster );
		
		RwRasterClear( RwRGBAToPixel( &black, format ));		// 0: Clear to black
		RwRasterShowRaster( p_context_raster, NULL, 0 );

		RwRasterClear( RwRGBAToPixel( &black, format ));        // 1: Clear to black 
		RwRasterShowRaster( p_context_raster, NULL, 0 );

		RwRasterRenderScaled( raster, &rect );			        // 2: Draw image
		RwRasterShowRaster( p_context_raster, NULL, 0 );

		RwRasterRenderScaled( raster, &rect );                  // 3: Draw image
		RwRasterShowRaster( p_context_raster, NULL, 0 );
		RwRasterPopContext();

		RwRasterDestroy( raster );

		// Set up the timer event last so that the icon will only ever appear once the loadscreen is visible.
		if( s_timer_event == 0 )
		{
			s_timer_event = timeSetEvent(	( 1000 * s_load_icon_data.m_FrameDelay ) / 60,	// Delay (ms).
											0,												// Ignored resolution (ms).
											loadIconTimerCallback,							// Callback function.
											0,												// Callback data.
											TIME_PERIODIC | TIME_CALLBACK_FUNCTION );


			Dbg_Assert( s_timer_event != 0 );
		}
	}
	else
	{
		if (just_freeze)
		{
			Script::RunScript( "PreFreezeScreen" );
//			if( skyFrameBit & 0x1 )
//			{
//				copy_buffer( 1 );
//			}
//			else
//			{
//				copy_buffer( 0 );
//			}

			Script::RunScript( "PostFreezeScreen" );
		}
		else if( blank )
		{
			RwRect rect;
			rect.x = SCREEN_CONV_X( 0 );
			rect.y = SCREEN_CONV_Y( 0 );
			rect.w = SCREEN_CONV_X( 640 ) - SCREEN_CONV_X( 0 );
			rect.h = SCREEN_CONV_Y( 448 ) - SCREEN_CONV_Y( 0 );

			Spt::SingletonPtr< Gfx::Manager > gfx_manager;

			_rwXbSetRenderState( rwRENDERSTATETEXTURERASTER, (void *)NULL );

			RwRaster* p_context_raster;
			p_context_raster = gfx_manager->DefaultViewport().GetFrameBuffer();

			RwInt32 format = p_context_raster->cFormat & 0x0000FF00L;
			RwRGBA black = { 0, 0, 0, 0 }; 

			RwRasterPushContext( p_context_raster );

			RwRasterClear( RwRGBAToPixel( &black, format ));		// 0: Clear to black
			RwRasterShowRaster( p_context_raster, NULL, 0 );

			RwRasterClear( RwRGBAToPixel( &black, format ));        // 1: Clear to black 
			RwRasterShowRaster( p_context_raster, NULL, 0 );

			RwRasterPopContext();
		}
	}

#	endif
	
	Spt::SingletonPtr< Mlp::Manager > mlp_man;
	s_loading_screen_on = true;
	mlp_man->PauseDisplayTasks( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Hide( void )
{
	if( s_timer_event != 0 )
	{
		timeKillEvent( s_timer_event );
		s_timer_event = 0;
	}

	Spt::SingletonPtr< Mlp::Manager > mlp_man;
	mlp_man->PauseDisplayTasks( false ); 

	if( !s_loading_screen_on )
	{
		return;
	}

	s_loading_screen_on = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StartProgressBar( char* tick_image, int num_ticks, int x, int y )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	ProgressTick( int num_ticks )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	EndProgressBar( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace LoadScreen






================================================
FILE: Code/Gfx/XBox/p_memview.cpp
================================================
//////////////////////////////////////////////////////
// p_memview.cpp
//
// code for tracking memory usage, and displaying it in a graphical manner
// keeps extra info about allocated blocks
// including the call stack, so we can print out information
// about specific allocated blocks
// which we will select using the graphical memory browser
//
//
// tried to use a little of the task system as possible
// so we can run the inspector
// without it messing with the heap it inspects
//
//


//extern char _mem_dump_start[];
//extern char _map_file_start[];
//extern char _symbols_start[];
//extern char _callstack_start[];
//extern char _code_end[];
//extern char	_std_mem_end[];
//extern char	_stack_size[];

//extern char __text_org[];
//extern char __data_org[];
//extern char __rodata_org[];
//extern char __bss_org[];

//extern char __rodata_orgend[];
//extern char __bss_objend[];
//extern char __text_objend[];

//extern char _rwheapdebug_start[];

			 
#define	STACKDEPTH  30			 


			 
extern "C"				 
{
extern void* ENTRYPOINT;		
}

#include 
#include 
#include 					// needed for buttons
#include 
#include  			// needed for loading map file
#include 

#include 
#include 

#include 
#include 
#include 
#include 

// needed for some VerticalMenu specific debugging							
#include 
#include 
#include 


extern volatile int	test_vblanks;


class CCallStack
{
public:
	void Append(CCallStack *p);
	void Remove();
	void InitHead();
	int	used;
	int	size;
	CCallStack *pNext;
	CCallStack *pPrev;
	int	addr[STACKDEPTH];
	uint32	flags;
	Mem::Allocator::BlockHeader * pBlock;	// pointer to block that has this callstack

};

CCallStack		free_list;	// list of created objects
CCallStack		used_list;	// list of created objects

// init a node, so it can act as the head			 
inline void CCallStack::InitHead()
{
	pPrev = this;
	pNext = this;
}
	
// append node p to this node (after it)									 
inline void CCallStack::Append(CCallStack *p)
{

	p->pNext = this->pNext;
	p->pPrev = this;
	this->pNext = p;
	p->pNext->pPrev = p;
}

// simply unlink it from the list			   
inline void CCallStack::Remove()
{
	pPrev->pNext = pNext;
	pNext->pPrev = pPrev;
}


						
//CCallStack * CallStack_FirstFree;
//CCallStack * CallStack_FirstUsed; 

static int MemView_Active = 0;



#define	MAX_CALLSTACK (8192 * 8)		// we got 8 mb, woo woo.


static float step = 128.0f;


static char HexByte(char a)
{
	if (a >= '0' && a <='9')
	{
		return a-'0';
	}
	if (a >= 'A' && a <='F')
	{
		return 10 + a-'A';
	}
	if (a >= 'a' && a <='f')
	{
		return 10 + a-'a';
	}

	// should really be an error, but just ignore it and return 0
	// as this is only used for parsing the map file	
	return 0;
	
	
}


static int doneonce = 0;


char *MemView_GetFunctionName(int pc, int *p_size)
{
	return "UNKNOWN";
	
}

// Modifed version of Jamie's unwind stack function
// ignores the fp, and just goes directly off the sp
// seems to work much better (and faster too)
int DumpUnwindStack( int iMaxDepth, int *pDest )
{
	return 0;
}
				   
				   
//        mD_L2           = nBit( vD_L2 ),
//        mD_R2           = nBit( vD_R2 ),
//        mD_L1           = nBit( vD_L1 ),
//        mD_R1           = nBit( vD_R1 ),
//        mD_TRIANGLE     = nBit( vD_TRIANGLE ),
//	      mD_CIRCLE       = nBit( vD_CIRCLE ),
//        mD_X            = nBit( vD_X ),
//        mD_SQUARE       = nBit( vD_SQUARE ),
//        mD_SELECT       = nBit( vD_SELECT ),
//        mD_L3           = nBit( vD_L3 ),
//        mD_R3           = nBit( vD_R3 ),
//        mD_START        = nBit( vD_START ),
//        mD_UP           = nBit( vD_UP ),
//        mD_RIGHT        = nBit( vD_RIGHT ),
//        mD_DOWN         = nBit( vD_DOWN ),
//        mD_LEFT         = nBit( vD_LEFT ),


void MemViewToggle()
{
	MemView_Active ^=1;
}


	 

void MemView_Alloc( void *v)
{
#ifdef	__LINKED_LIST_HEAP__


#ifdef	__NOPT_CDROM__OLD
   return;
#endif 	

#endif
}

void MemView_Free( void *v)
{
#ifdef	__LINKED_LIST_HEAP__
#endif	
}


Mem::Allocator::BlockHeader *MemView_FindBlock( int addr)
{
#ifdef	__LINKED_LIST_HEAP__

	
	Mem::Allocator::BlockHeader *pSmallestBlock	= NULL;
	uint32 smallest_block_size = 100000000;
	Mem::Manager& mem_man = Mem::Manager::sHandle();
	for (Mem::Heap* heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
	{
		Mem::Allocator::BlockHeader *pBlock = (Mem::Allocator::BlockHeader *) heap->find_block((void*)addr);	
		if (pBlock)
		{
			if (pBlock->mSize < smallest_block_size)
			{
				smallest_block_size = pBlock->mSize;
				pSmallestBlock = pBlock;
			}
		}
	}
	return pSmallestBlock;
#else
	return NULL;
#endif
}

char * MemView_GetClassName(CCallStack *c)
{
#ifdef	__LINKED_LIST_HEAP__
	int *ra = (int*)(c->addr[4]);
	int count = STACKDEPTH-4;
	while (count--)
	{
		int instruction = *ra++;
		if (instruction >> 24 == 0x0c)
		{
			int code = (instruction & 0xffffff)<<2;
			int size;
			char *p = MemView_GetFunctionName(code,&size); 
			// to tell if this is class or not
			// we see if the text is of the form  
			//    classname::classname (teminated by a 0)
			// as that indicates that it is a constructor
			// dude... this is where we need a regular expression....
			char *end = p;
			while (*end) end++;	   						// scan to end
			while (end[-1] != ':' && end > p)	end--;	// skip to char after the last :
			char *other = strstr(p,end);				// find fist occurance of end of string
			if (other != end)							// if different, then this is it!!
			{
				return MemView_GetFunctionName(code,&size);
				break;
			}
		}
	}
#endif
	return NULL;
}


void MemView_DumpBlockInfo(int cursor)
{
#	ifdef	__LINKED_LIST_HEAP__
	
	char os[256];
	
	Mem::Allocator::BlockHeader *pBlock = MemView_FindBlock( cursor );
	if( !pBlock )
	{
		// should search free blocks here???
	}

	// find this in the allocators used list
	// and say if it is free, or not	
	if( pBlock == NULL )
	{
/*		if (cursor > (int)__text_org && cursor < (int)__bss_objend)		// check to see if in code/data
		{
			
			if (cursor < (int)__data_org)
				printf("Code: ");
			else if (cursor < (int)__rodata_org)
				printf("Data: ");
			else if (cursor < (int)__bss_org)
				printf("RO-Data: ");
			else 
				printf("BSS: ");
			
		
			int size;
			char *p_name = MemView_GetFunctionName(cursor,&size);
			printf ( "%s, size %d\n",p_name,size);
		}
		else
*/
		{
			OutputDebugString( "Block Not Found\n" );
		}
	}
	else
	{
		void * p_start = (void*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize);
		sprintf( os, "Block found, addr = %p, size = %d (Header = %d)\n",p_start,pBlock->mSize,Mem::Allocator::BlockHeader::sSize);
		OutputDebugString( os );

		CCallStack *c = (CCallStack*)pBlock->mp_debug_data;
		if (!c)
		{
//			OutputDebugString( "Block with No Debug Info!!\n" );
		}
		else
		{
			// assume this is a "new", then the fourth callstack ra will point to the 
			// jal xxxxxx  instruction, where xxxxx is the constructor for the 
			// or it might be sortly thereafter, so check 16 instructions
			char *classname = MemView_GetClassName( c );
			if( classname )
			{
				sprintf( os, "CLASS: %s\n", classname );
				OutputDebugString( os );
			}

			// then list out the call stack (skipping the MemView_Alloc, as that's a given, and irrelevant);			
		
			int *p = c->addr + 1;
			while (p[1])			// also skip the ENTRYPOINT, just go back to main()
			{
				int size;
				sprintf( os, "%p: %s\n", (void*)*p,MemView_GetFunctionName(*p,&size));
				OutputDebugString( os );
				p++;
			}
		}
	}
#endif
}

static	int	blockstart;

static float cursor;

void 		MemView_Display()
{
#	ifdef	__NOPT_CDROM__OLD
	return;
#	endif 	
}


#ifdef	__LINKED_LIST_HEAP__

static int num_used;

static void ScanRegion(uint32 *p_start, uint32 *p_end)
{
#if 0
	printf ("scanning from %p to %p\n",p_start,p_end);
	// scan the whole range of memeory
	while (p_start (uint32)_code_end /*&& x < (uint32)_std_mem_end*/) // don't check for end now, as we have some debug heaps up there we want to include
		{
			// check to see if it points to one of the heap members
			
			uint32 *p_refs = (uint32*)_mem_dump_start;
			
		#if 0
			for (int i=0;i x)
					{
						high = mid;
					}
					else
					{
						// if the low point is already the same as the mid point
						// then the only way to go is up!
						// as this will only occur when low + 1 == high
						if (low == mid)
						{
							low = high;
						}
						else
						{
							low = mid;
						}
					}				
				}
				x -= 16;
			}
			x = oldx;
		#endif						  		
		}
	}
#endif
}
#endif

#ifdef	__LINKED_LIST_HEAP__
static uint32 *p_used;		
#endif

int MemView_CountBlocks(Mem::Allocator::BlockHeader *p_header)
{
#ifdef	__LINKED_LIST_HEAP__
	int num_used = 0;
	while ( p_header )
	{
		void * p_start = (void*)((uint)p_header + Mem::Allocator::BlockHeader::sSize);
		
		*p_used++ = (uint32)p_start;	 		// store the start of the block
		*p_used++ = 0;							// store a count
		p_header = p_header->mp_next_used;
		num_used++;
	}
	return num_used;
#else
	return 0;
#endif
}

// Find memory leaks
// the algorithm is quite simple:
// 1) Make a list of all "used" memory blocks, and set their usage count to 0
// 2) Scan all of the heap, and the stack, for each word that looks like a pointer, 
//    check to see if it is in the list of "used", and increment the usage count if so
// 3) Scan the list of used pointers, and check for any with usage == 0

// NEED OT EXTEND FOR TOP_DOWN heap.....		
		
void MemView_FindLeaks()
{
#ifdef	__LINKED_LIST_HEAP__
#if 0
		p_used  =  (uint32*)_mem_dump_start;		
		num_used = 0;
		printf ("Counting blocks....");		
		num_used += MemView_CountBlocks(Mem::Manager::sHandle().BottomUpHeap()->first_block()); 
		num_used += MemView_CountBlocks(Mem::Manager::sHandle().TopDownHeap()->first_block()); 
		num_used += MemView_CountBlocks(Mem::Manager::sHandle().FrontEndHeap()->first_block()); 
		num_used += MemView_CountBlocks(Mem::Manager::sHandle().NetworkHeap()->first_block()); 
		num_used += MemView_CountBlocks(Mem::Manager::sHandle().ScriptHeap()->first_block()); 
		num_used += MemView_CountBlocks(Mem::Manager::sHandle().SkaterHeap(0)->first_block()); 
//		num_used += MemView_CountBlocks(Mem::Manager::sHandle().DebugHeap()->first_block()); 
		printf (" %d\n",num_used);
		printf ("Sorting .....\n");			
		// Now we've done that, let's sort the list, so we can use a binary search later
		uint32 *p_top  =  (uint32*)_mem_dump_start;		
		for	(int i = 0;i= 10)
		{
			printf ("Stopping after %d refs\n",count);
			return;
		}
		if (p_start >= p_end)
		{
			printf ("No more References Found in heap \n");
			return;
		}
	}
#endif
#endif
}

// Find the first block in the free list
// if no free blocks, then return
// scan all used blocks, and print out the info for all the blocks
// that have an address above the first free block
void MemView_DumpFragments(Mem::Heap *pHeap)
{
#	ifdef __LINKED_LIST_HEAP__ 

	char os[256];

	if (!pHeap->mFreeBlocks.m_count)
	{
		OutputDebugString( "NO Fragmentation\n" );
		return;
	}
	
	if( !pHeap->mp_context->mp_free_list )
	{
		sprintf( os, "!!!!!! No free list, but there are %d free blocks???\n",pHeap->mFreeBlocks.m_count);
		OutputDebugString( os );
		return;
	}

	Mem::Allocator::BlockHeader *p_free = pHeap->mp_context->mp_free_list;
	Mem::Allocator::BlockHeader *p_full = pHeap->mp_context->mp_used_list;
	
	sprintf( os, "!!!!!! Free list starts at %p\n",p_free );
	OutputDebugString( os );

	// The first p_free will be the start of fragmentations
	while (p_full)
	{
		if (p_full > p_free)
		{
			OutputDebugString( "Framgented Block: " );
			void * p_start = (void*)((uint)p_full + Mem::Allocator::BlockHeader::sSize);
			MemView_DumpBlockInfo((int)p_start);
		}
		p_full = p_full->mp_next_used;
	}
	#endif
}

void MemView_DumpHeap( Mem::Heap *pHeap )
{
	#ifdef	__LINKED_LIST_HEAP__    
	Mem::Allocator::BlockHeader *p_full = pHeap->mp_context->mp_used_list;
	
	// The first p_free will be the start of fragmentations
	while( p_full )
	{
		printf ("\n");
		void * p_start = (void*)((uint)p_full + Mem::Allocator::BlockHeader::sSize);
		MemView_DumpBlockInfo((int)p_start);
		p_full = p_full->mp_next_used;
	}
	#endif
}



void MemView_DumpBottomFragments()
{

	MemView_DumpFragments(Mem::Manager::sHandle().BottomUpHeap());
}

void MemView_DumpTopFragments()
{

	MemView_DumpFragments(Mem::Manager::sHandle().TopDownHeap());
}



/*
class CCallStack
{
public:
	void Append(CCallStack *p);
	void Remove();
	void InitHead();
	int	used;
	int	size;
	CCallStack *pNext;
	CCallStack *pPrev;
	int	addr[STACKDEPTH];

};
*/

struct SBlockType
{
	int	return_addr; 			// first meaningful return addr 
	
	int	size;					// size of block (if we want to sort by it
	int	total;					// total size of this type
	int	actual;					// actual total size, including headers
	char *p_class;				// points to class node 
	
	int	count;
};

// scan throught the list of "used" blocks
// and sort them into a list, organized by "type"
// the "type" is determined by the first return address after
// a callstack entry that is either "Malloc" or "Spt::Class::operator new"
// the "type" is furthur sorted by either "size" or "Class"
// where "size" is the size of the block (for a Malloc)
// and "Class" is the type of class that constructed this block 

#define	MAX_TYPES 10000


void MemView_DumpAnalysis( SBlockType* blocks, int numBlocksToPrint )
{
#ifdef	__LINKED_LIST_HEAP__
	// Sorts the types, and print out totals

	int temp;

	for (int i = 0; i < numBlocksToPrint; i++)
	{
		for (int j = i+1;jsize;	  	// size is the only thing we know for sure
	int	return_addr = 0;				// default unknown return address
	char *p_class = "not a class";
	int latest = 1;
	int i = 0;
	
	for ( i = 1; i < 8; i++ )
	{
		int xsize;

		/*
		// the types of call stack we may encounter:
		// need to 
			0x10be48: Mem::Heap::allocate                                                   
			0x109914: Mem::Manager::New                                                     
			0x1035b0: Spt::Class::operator new                                              
			0x161094: Front::KeyboardControl::sCreateInstance   

			0x10be48: Mem::Heap::allocate                                                   
			0x109914: Mem::Manager::New                                                     
			0x10a150: Malloc                                                                
			0x222df8: _SkyBuildPktForUpLoadAlignedContiguousRectangle  

			0x10be48: Mem::Heap::allocate                                                   
			0x109914: Mem::Manager::New                                                     
			0x10a210: Malloc_FreeList                                                       
			0x257034: _rwFreeListAllocReal
		*/

		char *p_name = MemView_GetFunctionName(pCallStack->addr[i],&xsize);
		if (!strcmp("Malloc",p_name) 
			|| !strcmp("Spt::Class::operator new",p_name)
			|| !strcmp("Mem::Manager::New",p_name)
			|| !strcmp("_rwFreeListAllocReal",p_name))
		{
			latest = i;
		}
	}

	if (latest != 1)
	{
		return_addr = pCallStack->addr[latest+1];
	}

	p_class = MemView_GetClassName(pCallStack);				// get class
	// right, now we have all the info on this block
	// let's see if we've got one just like it

	//		if (!p_class && !MemView_GetFunctionName(return_addr,&temp))
	/*		
			if (!return_addr)
			{
				for (int i = 0;i>%s<<\n",i,MemView_GetFunctionName(p->addr[i],&temp));

				}
				return;

			}
	*/
	
									  
	// check if it is a string, and print it out, if so					
/*	
	int temp;				  
	if (!strcmp("Str::String::copy",MemView_GetFunctionName(return_addr,&temp)))
	{
		printf ("String <%s>\n",(char*)((char*)(pCallStack->pBlock)+32)); 
	}
	
	
	if (!strcmp("Front::VerticalMenu::sCreateInstance",MemView_GetFunctionName(return_addr,&temp)))
	{
		void *p_start  =  (void*)((char*)(pCallStack->pBlock)+32);
		printf ("\nVertical Menu "); 
		
		Front::ScreenElement *pV =  (Front::ScreenElement *)p_start;
		printf (" id = %s\n", Script::FindChecksumName(pV->GetID()));
//		MemView_DumpBlockInfo((int)p_start);
		
	}	
*/	

	// check to see if this block is already included
	for ( i = 0; i < num; i++ )
	{
		if ( pBlocks[i].p_class == p_class
			/*&& pBlocks[i].size == size */
			 && pBlocks[i].return_addr == return_addr )
		{
			pBlocks[i].count++;
			pBlocks[i].total += size;
			pBlocks[i].actual += size + Mem::Allocator::BlockHeader::sSize;
			break;				
		}
	}

	// if not, then add the block
	if ( i == num )
	{
		pBlocks[i].p_class = p_class;
		pBlocks[i].size = size;
		pBlocks[i].total = size;
		pBlocks[i].actual = size + Mem::Allocator::BlockHeader::sSize;
		pBlocks[i].return_addr = return_addr;
		pBlocks[i].count = 1;
		num++;
	}
}

#ifdef	__LINKED_LIST_HEAP__
static int bbb = 0;	   	// compiler patch var, see below
#endif

void MemView_AnalyzeBlocks(uint32 mask)
{
#ifdef	__LINKED_LIST_HEAP__
#if 0
	SBlockType  *pBlocks = (SBlockType  *)_mem_dump_start;	// temp memory
	int	num_blocks = 0;
	int num = 0;

	printf ("\nAnalyzing blocks....\n");
	
	CCallStack *p = used_list.pNext;  
	while (p != &used_list)
	{
		// Get the actualy block we referred to
//		Mem::Allocator::BlockHeader * pBlock = p->pBlock;
//		void * p_start = (void*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize);
		// Otionally check to see if it on the front end heap
//		if (Mem::SameContext(p_start,Mem::Manager::sHandle().FrontEndHeap()))
		{
			if (!mask || !(p->flags & mask))
			{
				MemView_AnalyzeCallStack( p, pBlocks, num );			
				num_blocks++;
			}
		}
		p = p->pNext; 
	}
	
	printf ("%d types, in %d total blocks\n", num, num_blocks); 

	MemView_DumpAnalysis( pBlocks, num );
	if (bbb)
	{
		MemView_DumpBottomFragments();			// just to get it compiling 
		MemView_DumpTopFragments();			// just to get it compiling 
	}		
#endif
#endif
}


void MemView_MarkBlocks(uint32 mask)
{
#ifdef	__LINKED_LIST_HEAP__

	CCallStack *p = used_list.pNext;  
	while (p != &used_list)
	{
		p->flags |= mask;
		
		p = p->pNext;
	}
#endif
}



void MemView_Input(uint buttons, uint makes, uint breaks)
{

	if (Config::CD())
	{
		return;
	}	


//	if (makes & Inp::Data::mD_TRIANGLE)
//	{
//		MemView_Active = !MemView_Active;
//	}


	if (!MemView_Active)
	{
		return;
	}

					  
	float step1 = step;
	
	float zoom = 1.1f;
	
	float scroll = 4.0f;

	



	if (buttons & Inp::Data::mD_LEFT)
	{
		step1 = step * zoom;
	}
	if (buttons & Inp::Data::mD_RIGHT)
	{
		step1 = step / zoom;
	}
	
	if (buttons & Inp::Data::mD_UP)
	{
//		start = start - scroll * 512.0f * 2.0f * step;
		cursor = cursor - scroll * 512.0f * 2.0f * step;
	}
	if (buttons & Inp::Data::mD_DOWN)
	{
//		start = start + scroll * 512.0f * 2.0f * step;
		cursor = cursor + scroll * 512.0f * 2.0f * step;
	}

	if (buttons & Inp::Data::mD_L1)
	{
//		start = start - scroll * 512.0f * 2.0f * step / 256.0f;
		cursor = cursor - scroll * 2.0f * 2.0f * step;
	}
	if (buttons & Inp::Data::mD_L2)
	{
//		start = start + scroll * 512.0f * 2.0f * step / 256.0f;
		cursor = cursor + scroll * 2.0f * 2.0f * step;
	}

#define 	MMMIN 	(0.0078125f)
	 				
	if (step1 1024.0f)
	{
		step1 = 1024.0f;
	}

//	start = start + (512.0f * 2.0f * 128.0f * (step - step1));

	step = step1;

	if (makes & Inp::Data::mD_CIRCLE)
	{
		if (blockstart)
		{
			MemView_DumpRefs(blockstart);
		}
//		MemView_MarkBlocks(1);
	}

	// We don't look for leaks automatically now, so I'v put it on "SQUARE"	
	if (makes & Inp::Data::mD_SQUARE)
	{
		MemView_FindLeaks();
//	Mem::Manager& mem_man = Mem::Manager::sHandle();		MemView_DumpHeap(1);
//	heap	= mem_man.TopDownHeap();
//	MemView_DumpFragments(heap);
//	MemView_DumpHeap(heap,1);

	}

	if (makes & Inp::Data::mD_X)
	{
		MemView_AnalyzeBlocks();
	}

	// Triangle = Dump Fragmentation
/*	if (makes & Inp::Data::mD_TRIANGLE)
	{
		Mem::Manager& mem_man = Mem::Manager::sHandle();
		Mem::Heap* heap = mem_man.BottomUpHeap();
		Mem::Region* region = heap->ParentRegion();
		printf ("BottomUp Frag %dK, %d Blocks\n",heap->mFreeMem.m_count / 1024, heap->mFreeBlocks.m_count);
		printf ("Region %d/%d K", region->MemAvailable() / 1024, region->TotalSize() / 1024 );
		MemView_DumpFragments(heap);
	}
*/	

}

void MemView_AnalyzeHeap(Mem::Heap *pHeap)
{
	if ( !pHeap )
		return;
	
#ifdef	__LINKED_LIST_HEAP__
#if 0
	SBlockType  *pBlocks = (SBlockType  *)_mem_dump_start;	// temp memory
	int	num_blocks = 0;
	int num = 0;

	Mem::Allocator::BlockHeader *p_full = pHeap->mp_context->mp_used_list;

	while (p_full)
	{
		CCallStack* pCallStack = (CCallStack*)p_full->mp_debug_data;

		if ( pCallStack )
		{
			MemView_AnalyzeCallStack( pCallStack, pBlocks, num );
		}
		else
		{
			printf ("Block with No Debug Info!!\n");
		}

		p_full = p_full->mp_next_used;
	}

	printf ("%d types, in %d total blocks\n", num, num_blocks); 

	MemView_DumpAnalysis( pBlocks, num );
#endif
#endif
}
  
  



================================================
FILE: Code/Gfx/XBox/p_memview.h
================================================
////

#ifndef	__P_MEMVIEW_H__
#define	__P_MEMVIEW_H__

void MemView_Display();
void MemView_Input(uint buttons, uint makes, uint breaks);
void MemView_Alloc( void *v);
void MemView_Free( void *v);
void MemViewToggle();
void MemView_FindLeaks();
int DumpUnwindStack( int iMaxDepth, int *pDest );
char *MemView_GetFunctionName(int pc, int *p_size);
void MemView_DumpFragments(Mem::Heap *pHeap);
void MemView_AnalyzeBlocks(uint32 mask = 0);
void MemView_MarkBlocks(uint32 flags = 1 );
void MemView_DumpHeap(Mem::Heap *pHeap);
void MemView_AnalyzeHeap(Mem::Heap *pHeap);
#endif



================================================
FILE: Code/Gfx/XBox/p_nx.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:						 		 									**
**																			**
**	File name:		gfx/xbox/p_nx.cpp										**
**																			**
**	Created:		01/16/2002	-	dc										**
**																			**
**	Description:	Xbox platform specific interface to the engine			**
**					This is Xbox SPECIFIC!!!!!! If there is anything in		**
**					here that is not Xbox specific, then it needs to be		**
**					in nx.cpp												**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include	
#include	
#include	

#include	"gfx\camera.h"
#include	"gfx\gfxman.h"
#include	"gfx\nx.h"
#include	"gfx\nxtexman.h"
#include	"gfx\nxviewman.h"
#include	"gfx\NxQuickAnim.h"
#include	"gfx\NxParticleMgr.h"
#include	"gfx\NxMiscFX.h"
#include	"gfx\debuggfx.h"
#include	"gfx\xbox\p_NxSector.h"
#include	"gfx\xbox\p_NxScene.h"
#include	"gfx\xbox\p_NxModel.h"
#include	"gfx\xbox\p_NxGeom.h"
#include	"gfx\xbox\p_NxMesh.h"
#include	"gfx\xbox\p_NxSprite.h"
#include	"gfx\xbox\p_NxTexture.h"
#include	"gfx\xbox\p_NxParticle.h"
#include	"gfx\xbox\p_NxTextured3dPoly.h"
#include	"gfx\xbox\p_NxNewParticleMgr.h"
#include	"gfx\xbox\p_NxWeather.h"
#include	"core\math.h"
#include 	"sk\engine\SuperSector.h"					
#include 	"gel\scripting\script.h"

#include 	"gfx\xbox\nx\nx_init.h"
#include 	"gfx\xbox\nx\texture.h"
#include 	"gfx\xbox\nx\material.h"
#include 	"gfx\xbox\nx\render.h"
#include 	"gfx\xbox\nx\screenfx.h"
#include 	"gfx\xbox\nx\occlude.h"
#include 	"gfx\xbox\nx\scene.h"
#include 	"gfx\xbox\nx\chars.h"

#include	"gel\music\xbox\p_soundtrack.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_start_engine( void )
{
	NxXbox::InitialiseEngine();

	mp_particle_manager	= new CXboxNewParticleManager;
	mp_weather			= new CXboxWeather;

	// If the user selected widescreen from the dashboard, reset the default screen angle and aspect ratio.
	if( XGetVideoFlags() & ( XC_VIDEO_FLAGS_WIDESCREEN | XC_VIDEO_FLAGS_HDTV_720p ))
	{
		Script::RunScript( "screen_setup_widescreen" );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_pre_render( void )
{
	// The screen clear is now added to the push buffer directly after the swap, in s_plat_post_render().

	// No rendering should take place whilst the loading screen is visible.
//	if( !NxXbox::EngineGlobals.loadingscreen_visible )
//	{
//		D3DCOLOR col = 0x00506070;
//
//		if( NxXbox::EngineGlobals.screen_blur > 0 )
//		{
//			D3DDevice_Clear( 0, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, col, 1.0f, 0 );
//			++NxXbox::EngineGlobals.screen_blur_duration;
//		}
//		else
//		{
//			D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, col, 1.0f, 0 );
//			NxXbox::EngineGlobals.screen_blur_duration = 0;
//		}
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_post_render( void )
{
	// No rendering should take place whilst the loading screen is visible.
	if( !NxXbox::EngineGlobals.loadingscreen_visible )
	{
		D3DDevice_Swap( D3DSWAP_DEFAULT );

		if( NxXbox::EngineGlobals.screenshot_name[0] != 0 )
		{
			Spt::SingletonPtr< Gfx::Manager > gfx_manager;
			gfx_manager->ScreenShot( NxXbox::EngineGlobals.screenshot_name );
			NxXbox::EngineGlobals.screenshot_name[0] = 0;
		}

		// Now that the swap instruction has been pushed, clear the buffer for next frame.
		if( NxXbox::EngineGlobals.screen_blur > 0 )
		{
			D3DDevice_Clear( 0, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, NxXbox::EngineGlobals.clear_color, 1.0f, 0 );
			++NxXbox::EngineGlobals.screen_blur_duration;
		}
		else
		{
			if( NxXbox::EngineGlobals.letterbox_active )
			{
				D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x00000000UL, 1.0f, 0 );
			}
			else if( NxXbox::EngineGlobals.clear_color_buffer )
			{
				D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, NxXbox::EngineGlobals.clear_color, 1.0f, 0 );
			}
			else
			{
				D3DDevice_Clear( 0, NULL, D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, NxXbox::EngineGlobals.clear_color, 1.0f, 0 );
			}
			NxXbox::EngineGlobals.screen_blur_duration = 0;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_render_world( void )
{
	// Remove the loading bar.
	if( NxXbox::EngineGlobals.loadingbar_timer_event != 0 )
	{
		timeKillEvent( NxXbox::EngineGlobals.loadingbar_timer_event );
		NxXbox::EngineGlobals.loadingbar_timer_event = 0;
	}

	// No rendering should take place whilst the loading screen is visible.
	if( !NxXbox::EngineGlobals.loadingscreen_visible )
	{
		// Store time at start of render, used as reference throughout.
		NxXbox::EngineGlobals.render_start_time = (int)Tmr::GetTime();

		// Render objects of interest for the render target (shadow objects).
		NxXbox::set_render_state( RS_ZWRITEENABLE,	1 );
		NxXbox::set_render_state( RS_ZTESTENABLE,	1 );

		NxXbox::render_shadow_targets();

		CEngine::sGetImposterManager()->ProcessImposters();

		// Start up the screen blur if we're using it.
		NxXbox::start_screen_blur();

		int num_viewports = CViewportManager::sGetNumActiveViewports();
		for( int v = 0; v < num_viewports; ++v )
		{
			CViewport	*p_cur_viewport	= CViewportManager::sGetActiveViewport( v );
			Gfx::Camera	*p_cur_camera	= p_cur_viewport->GetCamera();
			
			// Check for a valid camera.
			if( p_cur_camera == NULL )
			{
				continue;
			}
		
			NxXbox::EngineGlobals.viewport.X		= (DWORD)( p_cur_viewport->GetOriginX() * NxXbox::EngineGlobals.backbuffer_width );
			NxXbox::EngineGlobals.viewport.Y		= (DWORD)( p_cur_viewport->GetOriginY() * NxXbox::EngineGlobals.backbuffer_height );
			NxXbox::EngineGlobals.viewport.Width	= (DWORD)( p_cur_viewport->GetWidth() * NxXbox::EngineGlobals.backbuffer_width );
			NxXbox::EngineGlobals.viewport.Height	= (DWORD)( p_cur_viewport->GetHeight() * NxXbox::EngineGlobals.backbuffer_height );
			NxXbox::EngineGlobals.viewport.MinZ		= 0.0f;
			NxXbox::EngineGlobals.viewport.MaxZ		= 1.0f;

			if( NxXbox::EngineGlobals.letterbox_active )
			{	
				NxXbox::EngineGlobals.viewport.Y		+= (DWORD)( NxXbox::EngineGlobals.backbuffer_height / 8 );
				NxXbox::EngineGlobals.viewport.Height	-= (DWORD)( NxXbox::EngineGlobals.backbuffer_height / 4 );
			}

			D3DDevice_SetViewport( &NxXbox::EngineGlobals.viewport );

			// There is no bounding box transform for rendering the world.
			NxXbox::set_frustum_bbox_transform( NULL );
		
			// Set up the camera..
			float aspect_ratio = p_cur_viewport->GetAspectRatio();

			NxXbox::set_camera( &( p_cur_camera->GetMatrix()), &( p_cur_camera->GetPos()), p_cur_camera->GetAdjustedHFOV(), aspect_ratio );

			// Render the non-sky world scenes.
			for( int i = 0; i < MAX_LOADED_SCENES; i++ )
			{
				if( sp_loaded_scenes[i] )
				{
					CXboxScene *pXboxScene = static_cast( sp_loaded_scenes[i] );

					if( !pXboxScene->IsSky())
					{
						// Build relevant occlusion poly list, now that the camera is set.
						NxXbox::BuildOccluders( &( p_cur_camera->GetPos()), v );

						NxXbox::set_render_state( RS_ZWRITEENABLE,	1 );
						NxXbox::set_render_state( RS_ZTESTENABLE,	1 );

						// Flag this scene as receiving shadows.
						pXboxScene->GetEngineScene()->m_flags |= SCENE_FLAG_RECEIVE_SHADOWS;

						NxXbox::render_scene( pXboxScene->GetEngineScene(), NxXbox::vRENDER_OPAQUE |
																			NxXbox::vRENDER_OCCLUDED |
																			NxXbox::vRENDER_SORT_FRONT_TO_BACK |
																			NxXbox::vRENDER_BILLBOARDS, v );
					}
				}
			}

			NxXbox::set_render_state( RS_ZWRITEENABLE,	0 );
			Nx::TextureSplatRender();
			NxXbox::set_render_state( RS_ZWRITEENABLE,	1 );

			CEngine::sGetImposterManager()->DrawImposters();

			// Render all opaque instances.
			NxXbox::render_instances( NxXbox::vRENDER_OPAQUE );
		
			// Now that opaque geometry is drawn, do the render tests for the light glows.
			NxXbox::render_light_glows( true );

			// Render the sky, followed by all the non-sky semitransparent scene geometry. There is no bounding box transform for rendering the world.
			NxXbox::set_frustum_bbox_transform( NULL );

			// Set up the sky camera.
			Mth::Vector	centre_pos( 0.0f, 0.0f, 0.0f );
			NxXbox::set_camera( &( p_cur_camera->GetMatrix()), ¢re_pos, p_cur_camera->GetAdjustedHFOV(), aspect_ratio, true );

			// Render the sky. We have to fudge the fog here to ensure the sky is fully fogged, since it is rendered with a non-standard projection
			// matrix to ensure a constant z=1, but which breaks the fog interpolation value.
			float fog_start = NxXbox::EngineGlobals.fog_start;
			float fog_end	= NxXbox::EngineGlobals.fog_end;
			NxXbox::EngineGlobals.fog_start	= -20.0f;
			NxXbox::EngineGlobals.fog_end	= -21.0f;
			D3DDevice_SetRenderState( D3DRS_FOGSTART,	*((DWORD*)( &NxXbox::EngineGlobals.fog_start )));
			D3DDevice_SetRenderState( D3DRS_FOGEND,		*((DWORD*)( &NxXbox::EngineGlobals.fog_end )));

			NxXbox::set_render_state( RS_ZWRITEENABLE,	0 );
			NxXbox::set_render_state( RS_ZTESTENABLE,	1 );
			for( int i = 0; i < MAX_LOADED_SCENES; i++ )
			{
				if( sp_loaded_scenes[i] )
				{
					CXboxScene *pXboxScene = static_cast( sp_loaded_scenes[i] );
					if( pXboxScene->IsSky())
					{
						// No anisotropic filtering for the sky.
						DWORD stage_zero_minfilter, stage_zero_mipfilter;
						D3DDevice_GetTextureStageState( 0, D3DTSS_MINFILTER, &stage_zero_minfilter );
						D3DDevice_GetTextureStageState( 0, D3DTSS_MIPFILTER, &stage_zero_mipfilter );
						D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
						D3DDevice_SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_NONE );

						NxXbox::render_scene( pXboxScene->GetEngineScene(), NxXbox::vRENDER_OPAQUE | NxXbox::vRENDER_SEMITRANSPARENT, v );
			
						D3DDevice_SetTextureStageState( 0, D3DTSS_MIPFILTER, stage_zero_mipfilter );
						D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, stage_zero_minfilter );
					}
				}
			}
			NxXbox::set_render_state( RS_ZWRITEENABLE,	1 );

			// Restore fog values.
			NxXbox::EngineGlobals.fog_start	= fog_start;
			NxXbox::EngineGlobals.fog_end	= fog_end;
			D3DDevice_SetRenderState( D3DRS_FOGSTART,	*((DWORD*)( &NxXbox::EngineGlobals.fog_start )));
			D3DDevice_SetRenderState( D3DRS_FOGEND,		*((DWORD*)( &NxXbox::EngineGlobals.fog_end )));

			// Revert to the regular camera.
			NxXbox::set_camera( &( p_cur_camera->GetMatrix()), &( p_cur_camera->GetPos()), p_cur_camera->GetAdjustedHFOV(), aspect_ratio );

			// Render all semitransparent instances.
			NxXbox::render_instances( NxXbox::vRENDER_SEMITRANSPARENT | NxXbox::vRENDER_INSTANCE_PRE_WORLD_SEMITRANSPARENT );

			// Render the non-sky semitransparent scene geometry.
			// Setting the depth clip control to clamp here means that semitransparent periphary objects that would usually cull out
			// are now drawn correctly (since they will clamp at 1.0, and the z test is <=).
			D3DDevice_SetRenderState( D3DRS_DEPTHCLIPCONTROL, D3DDCC_CLAMP );
			NxXbox::set_render_state( RS_ZWRITEENABLE,	1 );
			NxXbox::set_render_state( RS_ZTESTENABLE,	1 );
			for( int i = 0; i < MAX_LOADED_SCENES; i++ )
			{
				if( sp_loaded_scenes[i] )
				{
					CXboxScene *pXboxScene = static_cast( sp_loaded_scenes[i] );
					if( !pXboxScene->IsSky())
					{
						// Build relevant occlusion poly list, now that the camera is set.
						NxXbox::render_scene( pXboxScene->GetEngineScene(), NxXbox::vRENDER_SEMITRANSPARENT |
																			NxXbox::vRENDER_OCCLUDED |
																			NxXbox::vRENDER_BILLBOARDS, v );
					}
				}
			}
			D3DDevice_SetRenderState( D3DRS_DEPTHCLIPCONTROL, D3DDCC_CULLPRIMITIVE );

			// Render the particles.
			NxXbox::set_render_state( RS_ZWRITEENABLE,	0 );
			render_particles();

			// New style particles. Update should probably be somewhere else.
			mp_particle_manager->UpdateParticles();
			mp_particle_manager->RenderParticles();

			// Render weather effects.
			mp_weather->Process( Tmr::FrameLength());
			mp_weather->Render();

			NxXbox::set_render_state( RS_ZWRITEENABLE,	1 );

			// We want shatter objects to be rendered after the sky since they will typically be semitransparent.
			Nx::ShatterRender();

			// Render all semitransparent instances.
			NxXbox::render_instances( NxXbox::vRENDER_SEMITRANSPARENT | NxXbox::vRENDER_INSTANCE_POST_WORLD_SEMITRANSPARENT );

			// Render the light glows (the visibility tests were performed earlier).
			NxXbox::render_light_glows( false );

			// Render the shadow volumes.
//			for( int i = 0; i < MAX_LOADED_SCENES; i++ )
//			{
//				if( sp_loaded_scenes[i] )
//				{
//					CXboxScene *pXboxScene = static_cast( sp_loaded_scenes[i] );
//					if( !pXboxScene->IsSky())
//					{
//						NxXbox::render_scene( pXboxScene->GetEngineScene(), NxXbox::vRENDER_SHADOW_VOLUMES, v );
//					}
//				}
//			}
			
			Nx::ScreenFlashRender( v, 0 );
		}

		// This x86 instruction writes back and invalidates the cache. This should hopefully mean that all updated vertex buffer
		// data (vc wibble) are flushed out by the time the GPU hits them.
		_asm
		{
			wbinvd;
		}

		// Draw debug lines.
#		ifdef __NOPT_ASSERT__
		Gfx::DebugGfx_Draw();
#		endif

		// Reset viewport so text appears on full screen.
		NxXbox::EngineGlobals.viewport.X		= 0;
		NxXbox::EngineGlobals.viewport.Y		= 0;
		NxXbox::EngineGlobals.viewport.Width	= NxXbox::EngineGlobals.backbuffer_width;
		NxXbox::EngineGlobals.viewport.Height	= NxXbox::EngineGlobals.backbuffer_height;
		NxXbox::EngineGlobals.viewport.MinZ		= 0.0f;
		NxXbox::EngineGlobals.viewport.MaxZ		= 1.0f;

		if( NxXbox::EngineGlobals.letterbox_active )
		{	
			NxXbox::EngineGlobals.viewport.Y		+= (DWORD)( NxXbox::EngineGlobals.backbuffer_height / 8 );
			NxXbox::EngineGlobals.viewport.Height	-= (DWORD)( NxXbox::EngineGlobals.backbuffer_height / 4 );
		}

		D3DDevice_SetViewport( &NxXbox::EngineGlobals.viewport );

//		Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetLocalSkater();
//		Mth::Vector pos = pSkater->GetPos();
//		NxXbox::set_focus_blur_focus( pos, 0.0f, 10.0f * 12.0f, 100.0f * 12.0f );
//		NxXbox::finish_focus_blur();

		// Horrible hack - this should be somewhere else ASAP.
		NxXbox::SDraw2D::DrawAll();

		// Finish the screen blur if we're using it.
		NxXbox::finish_screen_blur();
	}

	// Reset viewport so screen clear will work.
	NxXbox::EngineGlobals.viewport.X		= 0;
	NxXbox::EngineGlobals.viewport.Y		= 0;
	NxXbox::EngineGlobals.viewport.Width	= NxXbox::EngineGlobals.backbuffer_width;
	NxXbox::EngineGlobals.viewport.Height	= NxXbox::EngineGlobals.backbuffer_height;
	NxXbox::EngineGlobals.viewport.MinZ		= 0.0f;
	NxXbox::EngineGlobals.viewport.MaxZ		= 1.0f;
	D3DDevice_SetViewport( &NxXbox::EngineGlobals.viewport );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CScene	*CEngine::s_plat_create_scene( const char *p_name, CTexDict *p_tex_dict, bool add_super_sectors )
{
	// Create scene class instance
	CXboxScene	*p_xbox_scene	= new CXboxScene;
	CScene		*p_new_scene	= p_xbox_scene;
	p_new_scene->SetInSuperSectors( add_super_sectors );
	p_new_scene->SetIsSky( false );

	// Create a new sScene so the engine can track assets for this scene.
	NxXbox::sScene *p_engine_scene = new NxXbox::sScene();
	p_xbox_scene->SetEngineScene( p_engine_scene );

	return p_new_scene;
}



#define MemoryRead( dst, size, num, src )	CopyMemory(( dst ), ( src ), (( num ) * ( size )));	\
											( src ) += (( num ) * ( size ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CScene *CEngine::s_plat_load_scene_from_memory( void *p_mem, CTexDict *p_tex_dict, bool add_super_sectors, bool is_sky, bool is_dictionary )
{
	uint8			*p_data = (uint8*)p_mem;
	CSector			*pSector;
	CXboxSector		*pXboxSector;

	// Create a new sScene so the engine can track assets for this scene.
	NxXbox::sScene *p_engine_scene = new NxXbox::sScene();

	// Set the dictionary flag.
	p_engine_scene->m_is_dictionary	= is_dictionary;

	// Version numbers.
	uint32 mat_version, mesh_version, vert_version;
	MemoryRead( &mat_version, sizeof( uint32 ), 1, p_data );
	MemoryRead( &mesh_version, sizeof( uint32 ), 1, p_data );
	MemoryRead( &vert_version, sizeof( uint32 ), 1, p_data );

	// Import materials (they will now be associated at the engine-level with this scene).
	p_engine_scene->pMaterialTable = NxXbox::LoadMaterialsFromMemory( (void**)&p_data, p_tex_dict->GetTexLookup());

	// Read number of sectors.
	int num_sectors;
	MemoryRead( &num_sectors, sizeof( int ), 1, p_data );

	// Figure optimum hash table lookup size.
	uint32 optimal_table_size	= num_sectors * 2;
	uint32 test					= 2;
	uint32 size					= 1;
	for( ;; test <<= 1, ++size )
	{
		// Check if this iteration of table size is sufficient, or if we have hit the maximum size.
		if(( optimal_table_size <= test ) || ( size >= 12 ))
		{
			break;
		}
	}

	// Create scene class instance, using optimum size sector table.
	CScene* new_scene = new CXboxScene( size );
	new_scene->SetInSuperSectors( add_super_sectors );
	new_scene->SetIsSky( is_sky );

	// Get a scene id from the engine.
	CXboxScene *p_new_xbox_scene = static_cast( new_scene );
	p_new_xbox_scene->SetEngineScene( p_engine_scene );

	for( int s = 0; s < num_sectors; ++s )
	{
		// Create a new sector to hold the incoming details.
		pSector						= p_new_xbox_scene->CreateSector();
		pXboxSector					= static_cast( pSector );
		
		// Generate a hanging geom for the sector, used for creating level objects etc.
		CXboxGeom	*p_xbox_geom	= new CXboxGeom();
		p_xbox_geom->SetScene( p_new_xbox_scene );
		pXboxSector->SetGeom( p_xbox_geom );
		
		// Prepare CXboxGeom for receiving data.
		p_xbox_geom->InitMeshList();
		
		// Load sector data.
		if( pXboxSector->LoadFromMemory( (void**)&p_data ))
		{
			new_scene->AddSector( pSector );
		}
	}

	// At this point get the engine scene to figure it's bounding volumes.
	p_engine_scene->FigureBoundingVolumes();
	
	// Read hierarchy information.
	int num_hierarchy_objects;
	MemoryRead( &num_hierarchy_objects, sizeof( int ), 1, p_data );

	if( num_hierarchy_objects > 0 )
	{
		p_engine_scene->mp_hierarchyObjects = new CHierarchyObject[num_hierarchy_objects];
		MemoryRead( p_engine_scene->mp_hierarchyObjects, sizeof( CHierarchyObject ), num_hierarchy_objects, p_data );
		p_engine_scene->m_numHierarchyObjects = num_hierarchy_objects;
	}

	return new_scene;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CScene *CEngine::s_plat_load_scene( const char *p_name, CTexDict *p_tex_dict, bool add_super_sectors, bool is_sky, bool is_dictionary )
{
	CSector*		pSector;
	CXboxSector*	pXboxSector;

	Dbg_Message( "loading scene from file %s\n", p_name );

	// Create a new NxXbox::sScene so the engine can track assets for this scene.
	NxXbox::sScene *p_engine_scene = new NxXbox::sScene();

	// Set the dictionary flag.
	p_engine_scene->m_is_dictionary	= is_dictionary;

	// Open the scene file.
	void* p_file = File::Open( p_name, "rb" );
	if( !p_file )
	{
		Dbg_MsgAssert( p_file, ( "Couldn't open scene file %s\n", p_name ));
		return NULL;
	}
	
	// Version numbers.
	uint32 mat_version, mesh_version, vert_version;
	File::Read( &mat_version, sizeof( uint32 ), 1, p_file );
	File::Read( &mesh_version, sizeof( uint32 ), 1, p_file );
	File::Read( &vert_version, sizeof( uint32 ), 1, p_file );

	// Import materials (they will now be associated at the engine-level with this scene).
	p_engine_scene->pMaterialTable = NxXbox::LoadMaterials( p_file, p_tex_dict->GetTexLookup());

	// Read number of sectors.
	int num_sectors;
	File::Read( &num_sectors, sizeof( int ), 1, p_file );

	// Figure optimum hash table lookup size.
	uint32 optimal_table_size	= num_sectors * 2;
	uint32 test					= 2;
	uint32 size					= 1;
	for( ;; test <<= 1, ++size )
	{
		// Check if this iteration of table size is sufficient, or if we have hit the maximum size.
		if(( optimal_table_size <= test ) || ( size >= 12 ))
		{
			break;
		}
	}

	// Create scene class instance, using optimum size sector table.
	CScene* new_scene = new CXboxScene( size );
	new_scene->SetInSuperSectors( add_super_sectors );
	new_scene->SetIsSky( is_sky );

	// Get a scene id from the engine.
	CXboxScene *p_new_xbox_scene = static_cast( new_scene );
	p_new_xbox_scene->SetEngineScene( p_engine_scene );

	for( int s = 0; s < num_sectors; ++s )
	{
		// Create a new sector to hold the incoming details.
		pSector						= p_new_xbox_scene->CreateSector();
		pXboxSector					= static_cast( pSector );
		
		// Generate a hanging geom for the sector, used for creating level objects etc.
		CXboxGeom	*p_xbox_geom	= new CXboxGeom();
		p_xbox_geom->SetScene( p_new_xbox_scene );
		pXboxSector->SetGeom( p_xbox_geom );
		
		// Prepare CXboxGeom for receiving data.
		p_xbox_geom->InitMeshList();
		
		// Load sector data.
		if( pXboxSector->LoadFromFile( p_file ))
		{
			new_scene->AddSector( pSector );
		}
	}

	// At this point get the engine scene to figure it's bounding volumes.
	p_engine_scene->FigureBoundingVolumes();
	
	// Read hierarchy information.
	int num_hierarchy_objects;
	File::Read( &num_hierarchy_objects, sizeof( int ), 1, p_file );

	if( num_hierarchy_objects > 0 )
	{
		p_engine_scene->mp_hierarchyObjects = new CHierarchyObject[num_hierarchy_objects];
		File::Read( p_engine_scene->mp_hierarchyObjects, sizeof( CHierarchyObject ), num_hierarchy_objects, p_file );
		p_engine_scene->m_numHierarchyObjects = num_hierarchy_objects;
	}
	
	File::Close( p_file );

	return new_scene;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CEngine::s_plat_unload_scene( CScene *p_scene )
{
	Dbg_MsgAssert( p_scene,( "Trying to delete a NULL scene" ));

	CXboxScene *p_xbox_scene = (CXboxScene*)p_scene;

	// Ask the engine to remove the associated meshes for each sector in the scene.
	p_xbox_scene->DestroySectorMeshes();

	// Get the engine specific scene data and pass it to the engine to delete.
	NxXbox::DeleteScene( p_xbox_scene->GetEngineScene());
	p_xbox_scene->SetEngineScene( NULL );
	
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CEngine::s_plat_add_scene( CScene *p_scene, const char *p_filename )
{
	// Function to incrementally add geometry to a scene - should NOT be getting called on Xbox.
	Dbg_Assert( 0 );
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//CTexDict* CEngine::s_plat_load_textures( const char* p_name )
//{
//	NxXbox::LoadTextureFile( p_name );
//	return NULL;
//}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CModel* CEngine::s_plat_init_model( void )
{
	CXboxModel *pModel = new CXboxModel;
	
	return pModel;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CEngine::s_plat_uninit_model( CModel* pModel )
{
	delete pModel;

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CGeom* CEngine::s_plat_init_geom( void )
{
	CXboxGeom *pGeom = new CXboxGeom;
	return pGeom;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CEngine::s_plat_uninit_geom( CGeom *p_geom )
{
	delete p_geom;
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CQuickAnim* CEngine::s_plat_init_quick_anim()
{
	CQuickAnim* pQuickAnim = new CQuickAnim;
	return pQuickAnim;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_uninit_quick_anim(CQuickAnim* pQuickAnim)
{
	delete pQuickAnim;
	return;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CMesh* CEngine::s_plat_load_mesh( const char* pMeshFileName, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume )
{
	// Load the scene.
	Nx::CScene *p_scene = Nx::CEngine::s_plat_load_scene( pMeshFileName, pTexDict, false, false, false );

	// Store the checksum of the scene name.
	p_scene->SetID(Script::GenerateCRC( pMeshFileName )); 	// store the checksum of the scene name

	p_scene->SetTexDict( pTexDict );
	p_scene->PostLoad( pMeshFileName );
	
	// Disable any scaling.
	NxXbox::DisableMeshScaling();

	CXboxMesh *pMesh = new CXboxMesh( pMeshFileName );

	Nx::CXboxScene *p_xbox_scene = static_cast( p_scene );
	pMesh->SetScene( p_xbox_scene );
	pMesh->SetTexDict( pTexDict );

	return pMesh;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CMesh* CEngine::s_plat_load_mesh( uint32 id, uint32 *p_model_data, int model_data_size, uint8 *p_cas_data, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume )
{
	// Convert the id into a usable string.
	Dbg_Assert( id > 0 );
	char id_as_string[16];
	sprintf( id_as_string, "%d\n", id );

	// Load the scene.
	Nx::CScene *p_scene = Nx::CEngine::s_plat_load_scene_from_memory( p_model_data, pTexDict, false, false, false );

	// Store the checksum of the scene name.
	p_scene->SetID( Script::GenerateCRC( id_as_string ));

	p_scene->SetTexDict( pTexDict );
	p_scene->PostLoad( id_as_string );
	
	CXboxMesh *pMesh = new CXboxMesh();

	// Set CAS data for mesh.
	pMesh->SetCASData( p_cas_data );

	// Disable any scaling.
	NxXbox::DisableMeshScaling();

	Nx::CXboxScene *p_xbox_scene = static_cast( p_scene );
	pMesh->SetScene( p_xbox_scene );
	pMesh->SetTexDict( pTexDict );
	return pMesh;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CEngine::s_plat_unload_mesh( CMesh *pMesh )
{
	delete pMesh;
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_mesh_scaling_parameters( SMeshScalingParameters* pParams )
{
	NxXbox::SetMeshScalingParameters( pParams );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSprite *CEngine::s_plat_create_sprite( CWindow2D *p_window )
{
	return new CXboxSprite;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CEngine::s_plat_destroy_sprite( CSprite *p_sprite )
{
	delete p_sprite;
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextured3dPoly *	CEngine::s_plat_create_textured_3d_poly()
{
	return new CXboxTextured3dPoly;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		CEngine::s_plat_destroy_textured_3d_poly(CTextured3dPoly *p_poly)
{
	delete p_poly;
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Nx::CTexture *CEngine::s_plat_create_render_target_texture( int width, int height, int depth, int z_depth )
{
	// Create the CXBoxTexture (just a container class for the NxXbox::sTexture).
	CXboxTexture *p_texture = new CXboxTexture();

	// Create the NxXbox::sTexture.
	NxXbox::sTexture *p_engine_texture = new NxXbox::sTexture;
	p_texture->SetEngineTexture( p_engine_texture );
	
	// Set the texture as a render target.
	p_engine_texture->SetRenderTarget( width, height, depth, z_depth );
	
	return p_texture;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_project_texture_into_scene( Nx::CTexture *p_texture, Nx::CModel *p_model, Nx::CScene *p_scene )
{
	Nx::CXboxTexture	*p_xbox_texture	= static_cast( p_texture );
	Nx::CXboxModel		*p_xbox_model	= static_cast( p_model );
//	Nx::CXboxScene		*p_xbox_scene	= static_cast( p_scene );
//	NxXbox::create_texture_projection_details( p_xbox_texture->GetEngineTexture(), p_xbox_model, p_xbox_scene->GetEngineScene());
	NxXbox::create_texture_projection_details( p_xbox_texture->GetEngineTexture(), p_xbox_model, NULL );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_projection_texture_camera( Nx::CTexture *p_texture, Gfx::Camera *p_camera )
{
	Nx::CXboxTexture	*p_xbox_texture	= static_cast( p_texture );
	XGVECTOR3			pos( p_camera->GetPos()[X], p_camera->GetPos()[Y], p_camera->GetPos()[Z] );
	XGVECTOR3			at = pos + D3DXVECTOR3( p_camera->GetMatrix()[Z][X], p_camera->GetMatrix()[Z][Y], p_camera->GetMatrix()[Z][Z] );
	
	NxXbox::set_texture_projection_camera( p_xbox_texture->GetEngineTexture(), &pos, &at );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_stop_projection_texture( Nx::CTexture *p_texture )
{
	Nx::CXboxTexture *p_xbox_texture = static_cast( p_texture );
	NxXbox::destroy_texture_projection_details( p_xbox_texture->GetEngineTexture());
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_add_occlusion_poly( uint32 num_verts, Mth::Vector *p_vert_array, uint32 checksum )
{
	if( num_verts == 4 )
	{
		NxXbox::AddOcclusionPoly( p_vert_array[0], p_vert_array[1], p_vert_array[2], p_vert_array[3], checksum );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_enable_occlusion_poly( uint32 checksum, bool enable )
{
	NxXbox::EnableOcclusionPoly( checksum, enable );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_remove_all_occlusion_polys( void )
{
	NxXbox::RemoveAllOcclusionPolys();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// returns true if the sphere at "center", with the "radius"
// is visible to the current camera
// (note, currently this is the last frame's camera on PS2)
bool CEngine::s_plat_is_visible( Mth::Vector& center, float radius )
{
	return NxXbox::IsVisible( center, radius );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_max_multipass_distance( float dist )
{
	// Has no meaning for Xbox.
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const char* CEngine::s_plat_get_platform_extension( void )
{
	// String literals are statically allocated so can be returned safely, (Bjarne, p90)
	return "Xbx";
}


/******************************************************************/
// Wait for any pending asyncronous rendering to finish, so rendering
// data can be unloaded
/******************************************************************/
void CEngine::s_plat_finish_rendering()
{
	// Wait for asyncronous rendering to finish.
	NxXbox::EngineGlobals.p_Device->BlockUntilIdle();
} 

/******************************************************************/
// Set the amount that the previous frame is blended with this frame
// 0 = none	  	(just see current frame) 	
// 128 = 50/50
// 255 = 100% 	(so you only see the previous frame)												  
/******************************************************************/
void CEngine::s_plat_set_screen_blur( uint32 amount )
{
	// Only set the blur if we have a blur buffer into which to render.
	if( NxXbox::EngineGlobals.p_BlurSurface[0] )
	{
		NxXbox::EngineGlobals.screen_blur = amount;
	}
} 



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int	CEngine::s_plat_get_num_soundtracks( void )
{
	return Pcm::GetNumSoundtracks();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const char* CEngine::s_plat_get_soundtrack_name( int soundtrack_number )
{
	static char buf[128];

	const WCHAR* p_soundtrack_name_wide = Pcm::GetSoundtrackName( soundtrack_number );

	if( p_soundtrack_name_wide )
	{
		// If p_soundtrack_name_wide contains characters not recognized by the XBox then wsprintfA
		// will write an empty string. It does this for the test bad soundtrack names provided by
		// the certification tool.
		// However, I'm not sure if wsprintf will write an empty string if there is just one bad
		// character in the name, or if it will only write an empty string if they are all bad.
		// So just to be sure, do a wsprintfA for each character in turn. That way, any good characters
		// will definitely be printed OK, and all bad characters will appear as xbox null characters.
		char *p_dest = buf;
		int count=0;
		WCHAR p_temp[2]; // A buffer for holding one WCHAR at a time for sending to wsprintfA.
		p_temp[1]	= 0;
		const WCHAR *p_scan = p_soundtrack_name_wide;

		while( *p_scan ) // WCHAR strings are terminated by a 0 just like normal strings, except its a 2byte 0.
		{
			p_temp[0] = *p_scan++;
				
			char p_one_char[10];
			wsprintfA( p_one_char, "%ls", p_temp);

			// p_one_char now contains a one char string.
			if( count < 99 )
			{
				if( *p_one_char )
				{
					*p_dest = *p_one_char;
				}
				else
				{
					// Bad char, so write a ~ so that it appears as a xbox null char.
					*p_dest='~';
				}	
				++p_dest;
				++count;
			}
		}	
		*p_dest = 0;
	}
	else
	{
		// In theory this should never happen, but make sure p_buf contains something if it does.
		sprintf( buf, "~" );
	}	
		
	int len = strlen( buf );
	for( int c = 0; c < len; ++c )
	{
		// Force any special characters (arrows or button icons) to be displayed
		// as the xbox NULL character by changing them to an invalid character.
		switch( buf[c] )
		{
			case '': case '': case '': case '':
			case '': case '': case '': case '':
			case '': case '': case '': case '':
				buf[c] = '~';
				break;
			default:
				break;
		}	
	}
	return buf;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_letterbox( bool letterbox )
{
	// Letterbox mode is designed for use on a regular 4:3 screen.
	// It should use the same, wider viewing angle as for widescreen mode, but shrink the resultant image down
	// vertically by 25%.
	if( letterbox )
	{
		if( NxXbox::EngineGlobals.letterbox_active == false )
		{
			// Need to adjust the screen y offset and multiplier to ensure sprites are scaled properly for this mode.
			NxXbox::EngineGlobals.screen_conv_y_offset		+= ( NxXbox::EngineGlobals.backbuffer_height / 4 ) / 2;
			NxXbox::EngineGlobals.screen_conv_y_multiplier	= 0.75f;
			NxXbox::EngineGlobals.letterbox_active			= true;
		}
	}
	else
	{
		if( NxXbox::EngineGlobals.letterbox_active == true )
		{
			// Restore the screen y offset and multiplier.
			NxXbox::EngineGlobals.screen_conv_y_offset		-= ( NxXbox::EngineGlobals.backbuffer_height / 4 ) / 2;
			NxXbox::EngineGlobals.screen_conv_y_multiplier	= 1.0f;
			NxXbox::EngineGlobals.letterbox_active			= false;
		}
	}
} 



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::s_plat_set_color_buffer_clear( bool clear )
{
//	NxXbox::EngineGlobals.clear_color_buffer = clear;
}

} // namespace Nx


================================================
FILE: Code/Gfx/XBox/p_nxfont.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxFont.cpp

#include 	"gfx/xbox/p_nxfont.h"
#include 	"sys/config/config.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CFont

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CXboxFont::CXboxFont() : mp_plat_font(NULL)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CXboxFont::~CXboxFont()
{
	if (mp_plat_font)
	{
		plat_unload();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxFont::plat_load(const char *filename)
{
#	ifdef __PAL_BUILD__
	// . Have to trap the small font, which needs to be loaded from a different
	// location for the French build.
	if( Config::GetLanguage() == Config::LANGUAGE_FRENCH )
	{
		if( strstr( filename, "small" ))
		{
			mp_plat_font = NxXbox::LoadFont( "small_fr" );
		}
		else
		{
			mp_plat_font = NxXbox::LoadFont( filename );
		}
	}
	else
	{
		mp_plat_font = NxXbox::LoadFont(filename);
	}
#	else
	mp_plat_font = NxXbox::LoadFont(filename);
#	endif

	return (mp_plat_font != NULL);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CXboxFont::plat_set_spacings(int charSpacing, int spaceSpacing)
{
	mp_plat_font->mCharSpacing = charSpacing;
	if (spaceSpacing > 0)
		mp_plat_font->mSpaceSpacing = spaceSpacing;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CXboxFont::plat_set_rgba_table(Image::RGBA *pTab)
{
	for (int i = 0; i < 16; i++)
		mp_plat_font->mRGBATab[i] = *((uint32 *) &pTab[i]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CXboxFont::plat_mark_as_button_font(bool isButton)
{
	NxXbox::pButtonsFont = (isButton) ? mp_plat_font : NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CXboxFont::plat_unload()
{
	NxXbox::UnloadFont(mp_plat_font);
	mp_plat_font = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 CXboxFont::plat_get_default_height() const
{
	Dbg_Assert(mp_plat_font);

	return mp_plat_font->GetDefaultHeight();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 CXboxFont::plat_get_default_base() const
{
	Dbg_Assert(mp_plat_font);

	return mp_plat_font->GetDefaultBase();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//void CXboxFont::plat_begin_text(uint32 rgba, float Scale)
//{
//	Dbg_Assert(mp_plat_font);
//
//	mp_plat_font->BeginText(rgba, Scale);
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//void CXboxFont::plat_draw_string(char *String, float x0, float y0)
//{
//	Dbg_Assert(mp_plat_font);
//
//	mp_plat_font->DrawString(String, x0, y0);
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
//void CXboxFont::plat_end_text(void)
//{
//	Dbg_Assert(mp_plat_font);
//
//	mp_plat_font->EndText();
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxFont::plat_query_string(char *String, float &width, float &height) const
{
	Dbg_Assert(mp_plat_font);

	mp_plat_font->QueryString(String, width, height);
}




/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CText

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxText::CXboxText()
{
	mp_plat_text	= new NxXbox::SText();
	m_zvalue		= 0;

	plat_initialize();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxText::~CXboxText()
{
	if( mp_plat_text )
	{
		delete mp_plat_text;
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CXboxText::plat_initialize()
{
	plat_update_engine();
	plat_update_priority();
	plat_update_hidden();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CXboxText::plat_update_hidden()
{
	mp_plat_text->SetHidden(m_hidden);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxText::plat_update_engine()
{
	CXboxFont *p_xbox_font = static_cast( mp_font );

	mp_plat_text->mp_string	= m_string;
	if( p_xbox_font)
	{
		mp_plat_text->mp_font = p_xbox_font->GetEngineFont();
	}

	mp_plat_text->m_xpos	= m_xpos;
	mp_plat_text->m_ypos	= m_ypos;
	mp_plat_text->m_xscale	= m_xscale;
	mp_plat_text->m_yscale	= m_yscale;
	mp_plat_text->m_rgba	= *((uint32 *) &m_rgba);
	mp_plat_text->m_color_override = m_color_override;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxText::plat_update_priority( void )
{
	// Update draw list
	mp_plat_text->SetPriority( m_priority );

	if( m_use_zbuffer )
	{
		// Convert the 16:16 fixed z value to a float here.
		float z = (float)m_zvalue / 65536.0f;
		mp_plat_text->SetZValue( z );
	}
	else
	{
		mp_plat_text->SetZValue( 0.0f );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CTextMan::s_plat_alloc_text_pool( void )
{
	for( int i = 0; i < vMAX_TEXT_INSTANCES; ++i )
	{
	   	CXboxText *p_text		= new CXboxText;
		p_text->mp_next			= sp_dynamic_text_list;
		sp_dynamic_text_list	= p_text;
	}
}






} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/XBox/p_nxfont.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxFont.h

#ifndef	__GFX_P_NX_FONT_H__
#define	__GFX_P_NX_FONT_H__

#include 	"gfx/nxfont.h"
#include 	"gfx/xbox/nx/chars.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CFont
class CXboxFont : public CFont
{
public:
								CXboxFont();
	virtual						~CXboxFont();
	NxXbox::SFont				*GetEngineFont() const;

private:		// It's all private, as it is machine specific
	virtual	bool				plat_load(const char *filename);
	virtual void				plat_set_spacings(int charSpacing, int spaceSpacing);
	virtual void				plat_set_rgba_table(Image::RGBA *pTab);
	virtual void 				plat_mark_as_button_font(bool isButton);
	virtual void				plat_unload();

	virtual	uint32				plat_get_default_height() const;
	virtual	uint32				plat_get_default_base() const;
//	virtual void				plat_begin_text(uint32 rgba, float Scale);
//	virtual void				plat_draw_string(char *String, float x0, float y0);
//	virtual void				plat_end_text(void);
	virtual void				plat_query_string(char *String, float &width, float &height) const;

	// Machine specific members
	NxXbox::SFont *				mp_plat_font;		// Pointer to engine font
};


/////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of the CText
class	CXboxText : public CText
{
public:
								CXboxText();
	virtual						~CXboxText();

private:
	//
	virtual void				plat_initialize();

	virtual void				plat_update_hidden();		// Tell engine of update
	virtual void				plat_update_engine();		// Update engine primitives
	virtual void				plat_update_priority();

	// Machine specific members
	NxXbox::SText				*mp_plat_text;		// Pointer to engine text
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline NxXbox::SFont *CXboxFont::GetEngineFont() const
{
	return mp_plat_font;
}


} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/XBox/p_nxfontman.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:						 		 									**
**																			**
**	File name:		gfx/xbox/p_nxfontman.cpp								**
**																			**
**	Created:		01/16/2002	-	dc										**
**																			**
**	Description:	Xbox platform specific interface to the Font Manager	**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include	"gfx\nx.h"
#include	"gfx\NxFontMan.h"
#include	"gfx\xbox\p_NxFont.h"
#include 	"gfx\xbox\nx\chars.h"

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

namespace Nx
{
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CFont* CFontManager::s_plat_load_font( const char* pName )
{
	CXboxFont *p_new_font = new CXboxFont;
	p_new_font->Load( pName );
	return p_new_font;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFontManager::s_plat_unload_font( CFont* pFont )
{
	pFont->Unload();
}

} // namespace Nx
 


================================================
FILE: Code/Gfx/XBox/p_nxmiscfx.cpp
================================================
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 

namespace Nx
{

#define	DRAW_DEBUG_LINES		0

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sXboxScreenFlashVert
{
	float		x, y, z;
	float		rhw;
	D3DCOLOR	col;
	float		u, v;
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sXboxSplatVert
{
	D3DXVECTOR3		pos;
	D3DCOLOR		col;
	float			u, v;
};


	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sXboxSplatInstanceDetails : public sSplatInstanceDetails
{
	// Platform specific part.
	NxXbox::CInstance	*mp_instance;
	NxXbox::sMaterial	*mp_material;
	sXboxSplatVert		m_verts[SPLAT_POLYS_PER_MESH * 3];
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sXboxShatterInstanceDetails : public sShatterInstanceDetails
{
	// Platform specific part.

						sXboxShatterInstanceDetails( int num_tris, NxXbox::sMesh *p_mesh );
						~sXboxShatterInstanceDetails( void );
	
	NxXbox::sMesh		*mp_mesh;
	uint8				*mp_vertex_buffer;
};


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sXboxShatterInstanceDetails::sXboxShatterInstanceDetails( int num_tris, NxXbox::sMesh *p_mesh ) : sShatterInstanceDetails( num_tris )
{
	mp_mesh				= p_mesh;
	mp_vertex_buffer	= new uint8[num_tris * 3 * p_mesh->m_vertex_stride];
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sXboxShatterInstanceDetails::~sXboxShatterInstanceDetails( void )
{
	delete [] mp_vertex_buffer;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sXboxSplatInstanceDetails * getDetailsForTextureSplat( NxXbox::sTexture *p_texture )
{
	sXboxSplatInstanceDetails *p_xbox_details;

	Dbg_Assert( p_splat_details_table );
	
	// Check to see whether we have a scene already created for this type of texture splat.
	p_splat_details_table->IterateStart();
	sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
	while( p_details )
	{
		p_xbox_details					= static_cast( p_details );
		NxXbox::sMaterial *p_material	= p_xbox_details->mp_material;
		if( p_material->mp_tex[0] == p_texture )
		{
			// This scene contains a material with the required texture, so use this scene.
			return p_xbox_details;
		}
		p_details = p_splat_details_table->IterateNext();
	}
	
	// Create an (opaque) material used to render the mesh.
	NxXbox::sMaterial *p_material	= new NxXbox::sMaterial();
	p_material->m_flags[0]			= (( p_texture ) ? MATFLAG_TEXTURED : 0 );
	p_material->m_checksum			= (uint32)rand() * (uint32)rand();
	p_material->m_passes			= 1;
	p_material->mp_tex[0]			= p_texture;
	p_material->m_no_bfc			= true;
	p_material->m_zbias				= 1;				// To ensure it will sort above most geometry.
	p_material->m_reg_alpha[0]		= 0x00000005UL;		// Blend for now.
	p_material->m_color[0][0]		= 0x80;
	p_material->m_color[0][1]		= 0x80;
	p_material->m_color[0][2]		= 0x80;
	p_material->m_uv_addressing[0]	= 0x00020002UL;		// We want the texture to border - most efficient for alphakill.
	p_material->m_k[0]				= 0.0f;
	p_material->m_alpha_cutoff		= 1;

	p_xbox_details = new sXboxSplatInstanceDetails;
	p_xbox_details->m_highest_active_splat	= 0;
	p_xbox_details->mp_material				= p_material;
	ZeroMemory( p_xbox_details->m_lifetimes, sizeof( int ) * SPLAT_POLYS_PER_MESH );

	for( int v = 0; v < SPLAT_POLYS_PER_MESH * 3; ++v )
	{
		p_xbox_details->m_verts[v].pos = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
		p_xbox_details->m_verts[v].col = D3DCOLOR_RGBA( 0x80, 0x80, 0x80, 0x80 );
	}
	
	p_splat_details_table->PutItem((uint32)p_xbox_details, p_xbox_details );

	return p_xbox_details;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool subdivide_tri_stack( uint8 **p_write, NxXbox::sMesh *p_mesh )
{
	// Three temporary buffers.
	static uint8 v0[128];
	static uint8 v1[128];
	static uint8 v2[128];

	// Three more temporary buffers.
	static uint8 i01[128];
	static uint8 i12[128];
	static uint8 i20[128];

	// If there are elements on the stack, pop off the top three vertices and subdivide if necessary.
	if( triSubdivideStack.IsEmpty())
	{
		return false;
	}
	
	D3DXVECTOR3	*p_v0 = (D3DXVECTOR3*)v0;
	D3DXVECTOR3	*p_v1 = (D3DXVECTOR3*)v1;
	D3DXVECTOR3	*p_v2 = (D3DXVECTOR3*)v2;
	
	// Stack is LIFO, so Pop() off in reverse order.
	triSubdivideStack.Pop( p_v2 );
	triSubdivideStack.Pop( p_v1 );
	triSubdivideStack.Pop( p_v0 );
	
	// Calculate the area of this tri.
	Mth::Vector p(	p_v1->x - p_v0->x, p_v1->y - p_v0->y, p_v1->z - p_v0->z );
	Mth::Vector q(	p_v2->x - p_v0->x, p_v2->y - p_v0->y, p_v2->z - p_v0->z );
	Mth::Vector r(( p[Y] * q[Z] ) - ( q[Y] * p[Z] ), ( p[Z] * q[X] ) - ( q[Z] * p[X] ), ( p[X] * q[Y] ) - ( q[X] * p[Y] ));
	float area_squared = r.LengthSqr();

	if( area_squared > shatterAreaTest )
	{
		// We need to subdivide this tri. Calculate the three intermediate points.
		int block_size = triSubdivideStack.GetBlockSize();

		memcpy( i01, v0, block_size );
		memcpy( i12, v1, block_size );
		memcpy( i20, v2, block_size );

		// Deal with positions (always present).
		((D3DXVECTOR3*)i01 )->x = p_v0->x + (( p_v1->x - p_v0->x ) * 0.5f );
		((D3DXVECTOR3*)i01 )->y = p_v0->y + (( p_v1->y - p_v0->y ) * 0.5f );
		((D3DXVECTOR3*)i01 )->z = p_v0->z + (( p_v1->z - p_v0->z ) * 0.5f );

		((D3DXVECTOR3*)i12 )->x = p_v1->x + (( p_v2->x - p_v1->x ) * 0.5f );
		((D3DXVECTOR3*)i12 )->y = p_v1->y + (( p_v2->y - p_v1->y ) * 0.5f );
		((D3DXVECTOR3*)i12 )->z = p_v1->z + (( p_v2->z - p_v1->z ) * 0.5f );

		((D3DXVECTOR3*)i20 )->x = p_v2->x + (( p_v0->x - p_v2->x ) * 0.5f );
		((D3DXVECTOR3*)i20 )->y = p_v2->y + (( p_v0->y - p_v2->y ) * 0.5f );
		((D3DXVECTOR3*)i20 )->z = p_v2->z + (( p_v0->z - p_v2->z ) * 0.5f );

		// Deal with colors (not always present).
		if( p_mesh->m_diffuse_offset > 0 )
		{
			uint8	*p_v0col	= (uint8*)( v0 + p_mesh->m_diffuse_offset );
			uint8	*p_v1col	= (uint8*)( v1 + p_mesh->m_diffuse_offset );
			uint8	*p_v2col	= (uint8*)( v2 + p_mesh->m_diffuse_offset );
			uint8	*p_i01col	= (uint8*)( i01 + p_mesh->m_diffuse_offset );
			uint8	*p_i12col	= (uint8*)( i12 + p_mesh->m_diffuse_offset );
			uint8	*p_i20col	= (uint8*)( i20 + p_mesh->m_diffuse_offset );
		
			for( int i = 0; i < 4; ++i )
			{
				p_i01col[i]		= p_v0col[i] + (((int)p_v1col[i] - (int)p_v0col[i] ) / 2 );
				p_i12col[i]		= p_v1col[i] + (((int)p_v2col[i] - (int)p_v1col[i] ) / 2 );
				p_i20col[i]		= p_v2col[i] + (((int)p_v0col[i] - (int)p_v2col[i] ) / 2 );
			}
		}

		// Deal with uv0 (not always present).
		if( p_mesh->m_uv0_offset > 0 )
		{
			float	*p_v0uv		= (float*)( v0 + p_mesh->m_uv0_offset );
			float	*p_v1uv		= (float*)( v1 + p_mesh->m_uv0_offset );
			float	*p_v2uv		= (float*)( v2 + p_mesh->m_uv0_offset );
			float	*p_i01uv	= (float*)( i01 + p_mesh->m_uv0_offset );
			float	*p_i12uv	= (float*)( i12 + p_mesh->m_uv0_offset );
			float	*p_i20uv	= (float*)( i20 + p_mesh->m_uv0_offset );
		
			for( int i = 0; i < 2; ++i )
			{
				p_i01uv[i]		= p_v0uv[i] + (( p_v1uv[i] - p_v0uv[i] ) * 0.5f );
				p_i12uv[i]		= p_v1uv[i] + (( p_v2uv[i] - p_v1uv[i] ) * 0.5f );
				p_i20uv[i]		= p_v2uv[i] + (( p_v0uv[i] - p_v2uv[i] ) * 0.5f );
			}
		}
		
		// Push the four new tris onto the stack.
		triSubdivideStack.Push( v0 );
		triSubdivideStack.Push( i01 );
		triSubdivideStack.Push( i20 );

		triSubdivideStack.Push( i01 );
		triSubdivideStack.Push( v1 );
		triSubdivideStack.Push( i12 );

		triSubdivideStack.Push( i01 );
		triSubdivideStack.Push( i12 );
		triSubdivideStack.Push( i20 );

		triSubdivideStack.Push( i20 );
		triSubdivideStack.Push( i12 );
		triSubdivideStack.Push( v2 );
	}
	else
	{
		// Don't need to subdivide this tri.
		int block_size = triSubdivideStack.GetBlockSize();

		// Just copy the tri into the next available slot.
		memcpy( *p_write, v0, block_size );
		*p_write += block_size;
		memcpy( *p_write, v1, block_size );
		*p_write += block_size;
		memcpy( *p_write, v2, block_size );
		*p_write += block_size;
	}
	return true;
}



/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_screen_flash_render( sScreenFlashDetails *p_details )
{
	// Get viewport details.
	CViewport *p_vp = CViewportManager::sGetActiveViewport( p_details->m_viewport );

	sXboxScreenFlashVert verts[4];

	verts[0].x	= p_vp->GetOriginX() * NxXbox::EngineGlobals.backbuffer_width;
	verts[0].y	= p_vp->GetOriginY() * NxXbox::EngineGlobals.backbuffer_height;
	verts[0].z	= p_details->m_z;
	
	verts[1].x	= verts[0].x + ( p_vp->GetWidth() * NxXbox::EngineGlobals.backbuffer_width );
	verts[1].y	= verts[0].y;
	verts[1].z	= verts[0].z;

	verts[2].x	= verts[0].x + ( p_vp->GetWidth() * NxXbox::EngineGlobals.backbuffer_width );
	verts[2].y	= verts[0].y + ( p_vp->GetHeight() * NxXbox::EngineGlobals.backbuffer_height );
	verts[2].z	= verts[0].z;

	verts[3].x	= verts[0].x;
	verts[3].y	= verts[0].y + ( p_vp->GetHeight() * NxXbox::EngineGlobals.backbuffer_height );
	verts[3].z	= verts[0].z;

	for( int v = 0; v < 4; ++v )
	{
		verts[v].col = D3DCOLOR_ARGB( p_details->m_current.a, p_details->m_current.r, p_details->m_current.g, p_details->m_current.b );
		verts[v].rhw = 1.0f;
	}

	if( p_details->mp_texture )
	{
		verts[0].u	= 0.0f;
		verts[0].v	= 0.0f;
		verts[1].u	= 1.0f;
		verts[1].v	= 0.0f;
		verts[2].u	= 1.0f;
		verts[2].v	= 1.0f;
		verts[3].u	= 0.0f;
		verts[3].v	= 1.0f;

		Nx::CXboxTexture *p_xbox_texture = static_cast( p_details->mp_texture );

		NxXbox::set_texture( 0, p_xbox_texture->GetEngineTexture()->pD3DTexture, p_xbox_texture->GetEngineTexture()->pD3DPalette );
		NxXbox::set_render_state( RS_UVADDRESSMODE0, 0x00010001UL );
	}
	else	
	{
		NxXbox::set_texture( 0, NULL );
	}
	
	NxXbox::set_blend_mode( NxXbox::vBLEND_MODE_BLEND );

	NxXbox::set_render_state( RS_ZWRITEENABLE,	0 );
	NxXbox::set_render_state( RS_ZTESTENABLE,	0 );

	NxXbox::set_pixel_shader( 0 );
	NxXbox::set_vertex_shader( D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
	
	NxXbox::EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_QUADLIST, 1, verts, sizeof( sXboxScreenFlashVert ));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_initialize( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_cleanup( void )
{
	sXboxSplatInstanceDetails *p_xbox_details;

	Dbg_Assert( p_splat_details_table );
	
	p_splat_details_table->IterateStart();
	sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
	while( p_details )
	{
		p_xbox_details = static_cast( p_details );

		delete p_xbox_details->mp_material;
		
		p_details = p_splat_details_table->IterateNext();

		p_splat_details_table->FlushItem((uint32)p_xbox_details );
		delete p_xbox_details;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_reset_poly( sSplatInstanceDetails *p_details, int index )
{
	// Cast the details to Xbox details.
	sXboxSplatInstanceDetails *p_xbox_details = static_cast( p_details );
	
	// Force this poly to be degenerate.
	p_xbox_details->m_verts[index * 3 + 1]	= p_xbox_details->m_verts[index * 3];
	p_xbox_details->m_verts[index * 3 + 2]	= p_xbox_details->m_verts[index * 3];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline float CrossProduct2D( const Mth::Vector& v1, const Mth::Vector& v2 )
{	
	// Assumes for both v1 and v2 that the [z] and [w] components are 0.
	return ( v1[X] * v2[Y] ) - ( v1[Y] * v2[X] );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static bool same_side( Mth::Vector &p1, Mth::Vector &p2, Mth::Vector &a, Mth::Vector &b )
{
	float cp1 = CrossProduct2D( b - a, p1 - a );
	float cp2 = CrossProduct2D( b - a, p2 - a );
	if(( cp1 * cp2 ) >= 0.0f )
		return true;
    else
		return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static bool point_in_triangle( Mth::Vector &p, Mth::Vector &a, Mth::Vector &b, Mth::Vector &c )
{
	if( same_side( p, a, b, c ) && same_side( p, b, c, a ) && same_side( p, c, a, b ))
		return true;
    else
		return false;
}






/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline bool line_segment_intersection( float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4 )
{
	float ax = x2 - x1;
	float bx = x3 - x4;
	float ay = y2 - y1;
	float by = y3 - y4;
	float cx = x1 - x3;
	float cy = y1 - y3;
	float d = by * cx - bx * cy;	// Alpha numerator.
	float f = ay * bx - ax * by;	// Both denominator.

	// Alpha tests.
	if( f > 0.0f )
	{
		if( d < 0.0f || d > f )
			return false;
	}
	else
	{
		if( d > 0 || d < f )
			return false;
	}

	float e = ax * cy - ay * cx;	// Beta numerator.
	
	// Beta tests.
	if( f > 0.0f )
	{
		if( e < 0.0f || e > f )
			return false;
	}
	else
	{
		if( e > 0 || e < f )
			return false;
	}

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline bool tri_texture_intersect( float u0, float v0, float u1, float v1, float u2, float v2 )
{
	// Trivial check to see if all three points are outside range of texture.
	if(( u0 < -1.0f ) && ( u1 < -1.0f ) && ( u2 < -1.0f ))
		return false;
	if(( u0 > 1.0f ) && ( u1 > 1.0f ) && ( u2 > 1.0f ))
		return false;
	if(( v0 < -1.0f ) && ( v1 < -1.0f ) && ( v2 < -1.0f ))
		return false;
	if(( v0 > 1.0f ) && ( v1 > 1.0f ) && ( v2 > 1.0f ))
		return false;
	
	// Perform the check to see if any line segment forming the tri intersects any line segment forming the texture.
	Mth::Vector texture_square[4] = {	Mth::Vector( -1.0f, -1.0f, 0.0f, 0.0f ),
										Mth::Vector(  1.0f, -1.0f, 0.0f, 0.0f ),
										Mth::Vector(  1.0f,  1.0f, 0.0f, 0.0f ),
										Mth::Vector( -1.0f,  1.0f, 0.0f, 0.0f )};
	for( int p = 0; p < 4; ++p )
	{
		int q = ( p + 1 ) % 4;
		if( line_segment_intersection( u0, v0, u1, v1, texture_square[p][X], texture_square[p][Y], texture_square[q][X], texture_square[q][Y] ))
			return true;
		if( line_segment_intersection( u1, v1, u2, v2, texture_square[p][X], texture_square[p][Y], texture_square[q][X], texture_square[q][Y] ))
			return true;
		if( line_segment_intersection( u2, v2, u0, v0, texture_square[p][X], texture_square[p][Y], texture_square[q][X], texture_square[q][Y] ))
			return true;
	}

	// If we reach this point there are three remaining possibilities:
	// 1) That the tri lies entirely within the texture
	// 2) That the texture lies entirely within the tri
	// 3) That there is no space shared by tri and texture

	// 1) Perform a trivial check to see whether a corner of the tri lies within the texture.
	if(( u0 >= -1.0f ) && ( u0 <= 1.0f ) && ( v0 >= -1.0f ) && ( v0 <= 1.0f ))
		return true;
	if(( u1 >= -1.0f ) && ( u1 <= 1.0f ) && ( v1 >= -1.0f ) && ( v1 <= 1.0f ))
		return true;
	if(( u2 >= -1.0f ) && ( u2 <= 1.0f ) && ( v2 >= -1.0f ) && ( v2 <= 1.0f ))
		return true;

	// 2) Check that at least one corner of the texture falls within the tri.
	Mth::Vector a( u0, v0, 0.0f );
	Mth::Vector b( u1, v1, 0.0f );
	Mth::Vector c( u2, v2, 0.0f );
	for( int p = 0; p < 4; ++p )
	{
		if( point_in_triangle( texture_square[p], a, b, c ))
		{
			return true;
		}
	}

	// 3) No space shared.
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool plat_texture_splat( Nx::CSector **pp_sectors, Nx::CCollStatic **pp_collision, Mth::Vector& start, Mth::Vector& end, float size, float lifetime, Nx::CTexture *p_texture, Nx::sSplatTrailInstanceDetails *p_trail_details )
{
	XGMATRIX view_matrix, ortho_matrix, projection_matrix;

#	if DRAW_DEBUG_LINES
	Gfx::AddDebugLine( start, end, MAKE_RGB( 200, 200, 0 ), MAKE_RGB( 200, 200, 0 ), 1 );
#	endif // DRAW_DEBUG_LINES
	
	// The length of the start->end line defines the view depth of the frustum.
	Mth::Vector	splat_vector	= end - start;
	float		view_depth		= splat_vector.Length();
	splat_vector.Normalize();
	
	// Calculate the parallel projection matrix. Generally the projection vector will tend to point downwards, so we use a
	// random vector in the x-z plane to define the up vector for the projection. However if this splat is part of a trail,
	// use the previous point to generate the up vector.
	Mth::Vector up;
	if( p_trail_details )
	{
		up.Set( start[X] - p_trail_details->m_last_pos[X], start[Y] - p_trail_details->m_last_pos[Y], start[Z] - p_trail_details->m_last_pos[Z] );

		// The height of the viewport is defined by the distance between the two points.
		float height = up.Length() * 0.5f;

		// Now we move start and end halfway back along the up vector.
		start	-= up * 0.5f;
		end		-= up * 0.5f;
		
		up.Normalize();
		
		XGMatrixLookAtRH( &view_matrix, (XGVECTOR3*)( &start[X] ), (XGVECTOR3*)( &end[X] ), (XGVECTOR3*)( &up[X] ));
		XGMatrixOrthoRH( &ortho_matrix, size, height, 0.1f, view_depth );
	}
	else if( fabsf( splat_vector[Y] ) > 0.5f )
	{
		float angle = ((float)rand() * 2.0f * Mth::PI ) / (float)RAND_MAX;
		up.Set( sinf( angle ), 0.0f, cosf( angle ));
		XGMatrixLookAtRH( &view_matrix, (XGVECTOR3*)( &start[X] ), (XGVECTOR3*)( &end[X] ), (XGVECTOR3*)( &up[X] ));
		XGMatrixOrthoRH( &ortho_matrix, size, size, 0.1f, view_depth );
	}
	else
	{
		up.Set( 0.0f, 1.0f, 0.0f );
		XGMatrixLookAtRH( &view_matrix, (XGVECTOR3*)( &start[X] ), (XGVECTOR3*)( &end[X] ), (XGVECTOR3*)( &up[X] ));
		XGMatrixOrthoRH( &ortho_matrix, size, size, 0.1f, view_depth );
	}

	XGMatrixMultiply( &projection_matrix, &view_matrix, &ortho_matrix );
	
	// Pointer to the mesh we will be modifying. (Don't want to set the pointer up until we know for
	// sure that we will be adding some polys).
	sXboxSplatInstanceDetails	*p_details		= NULL;
	sXboxSplatVert				*p_target_verts	= NULL;

	Nx::CSector *p_sector;

	while( p_sector = *pp_sectors )
	{
		Nx::CXboxGeom *p_xbox_geom = static_cast( p_sector->GetGeom());

		if( p_xbox_geom )
		{
#			if DRAW_DEBUG_LINES
			Mth::Vector min = p_xbox_geom->GetBoundingBox().GetMin();
			Mth::Vector max = p_xbox_geom->GetBoundingBox().GetMax();

			Mth::Vector box[8];
			box[0] = box[1] = box[2] = box[3] = max;
			box[1][X] = min[X];
			box[2][Y] = min[Y];
			box[3][Z] = min[Z];
			box[5] = box[6] = box[7] = box[4] = min;;
			box[5][X] = max[X];
			box[6][Y] = max[Y];
			box[7][Z] = max[Z];

			for ( int i = 1; i < 4; i++ )
			{
				Gfx::AddDebugLine( box[0], box[i], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			}
			for ( int i = 5; i < 8; i++ )
			{
				Gfx::AddDebugLine( box[4], box[i], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			}
			Gfx::AddDebugLine( box[1], box[6], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			Gfx::AddDebugLine( box[1], box[7], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			Gfx::AddDebugLine( box[2], box[5], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			Gfx::AddDebugLine( box[2], box[7], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			Gfx::AddDebugLine( box[3], box[5], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
			Gfx::AddDebugLine( box[3], box[6], MAKE_RGB( 200, 0, 0 ), MAKE_RGB( 200, 0, 0 ), 1 );
#			endif // DRAW_DEBUG_LINES
			
			// For each mesh in the geom...
			for( uint32 m = 0; m < p_xbox_geom->m_num_mesh; ++m )
			{
				NxXbox::sMesh *p_mesh = p_xbox_geom->m_mesh_array[m];

				// Not allowed on meshes which are flagged not to shadow.
				if( p_mesh->m_flags & NxXbox::sMesh::MESH_FLAG_NO_SKATER_SHADOW )
					continue;

				// Check the bounding box of this mesh falls within the scope of the line.
				
				// Transform the mesh bounding box to see whether it falls within the projection frustum.
				
				// If it falls within the projection frustum, we need to explicitly transform all the vertices.
				BYTE *p_vert_data;
				p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_vert_data, D3DLOCK_READONLY );
				
				// Now scan through each non-degenerate tri, checking the verts to see if they are within scope.
				uint32 index0;
				uint32 index1 = p_mesh->mp_index_buffer[0][0];
				uint32 index2 = p_mesh->mp_index_buffer[0][1];
				for( uint32 i = 2; i < p_mesh->m_num_indices[0]; ++i )
				{
					// Wrap the indices round.
					index0 = index1;
					index1 = index2;
					index2 = p_mesh->mp_index_buffer[0][i];

					if(( index0 != index1 ) && ( index0 != index2 ) && ( index1 != index2 ))
					{
						XGVECTOR3 uvprojections[3];
						XGVec3TransformCoord( &uvprojections[0], (XGVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 )), &projection_matrix );
						XGVec3TransformCoord( &uvprojections[1], (XGVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 )), &projection_matrix );
						XGVec3TransformCoord( &uvprojections[2], (XGVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 )), &projection_matrix );

						// Check the z-values here, everything else is checked in tri_texture_intersect().
						if(( uvprojections[0].z < 0.0f ) && ( uvprojections[1].z < 0.0f ) && ( uvprojections[2].z < 0.0f ))
							continue;
						if(( uvprojections[0].z > 1.0f ) && ( uvprojections[1].z > 1.0f ) && ( uvprojections[2].z > 1.0f ))
							continue;
						
						// Okay, this tri lies within the projection frustum. Now check that it intersects the texture
						if( !tri_texture_intersect( uvprojections[0].x, uvprojections[0].y,
													uvprojections[1].x, uvprojections[1].y,
													uvprojections[2].x, uvprojections[2].y ))
						{
							continue;
						}
						
						// Get a pointer to the mesh used for rendering texture splats with the given texture.
						// (Note this will create a new instance to handle texture splats of this texture if one does not already exist).
						if( p_target_verts == NULL )
						{
							CXboxTexture *p_xbox_texture	= static_cast( p_texture );
							p_details						= getDetailsForTextureSplat( p_xbox_texture->GetEngineTexture());
							p_target_verts					= p_details->m_verts;
							Dbg_Assert( p_target_verts );
						}
						
						// Scan through the lifetimes, finding a 'dead' poly (lifetime == 0), or the oldest.
						uint32 idx						= p_details->GetOldestSplat();

						// Convert lifetime from seconds to milliseconds.
						p_details->m_lifetimes[idx]		= (int)( lifetime * 1000.0f );

						// Set up the corresponding vertices. First write the positions...
						uint32 index					= idx * 3;
						p_target_verts[index + 0].pos	= *(D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 ));
						p_target_verts[index + 1].pos	= *(D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 ));
						p_target_verts[index + 2].pos	= *(D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 ));

						// ...then the uv's...
						p_target_verts[index + 0].u		= ( uvprojections[0].x + 1.0f ) * 0.5f;
						p_target_verts[index + 0].v		= ( uvprojections[0].y + 1.0f ) * 0.5f;
						p_target_verts[index + 1].u		= ( uvprojections[1].x + 1.0f ) * 0.5f;
						p_target_verts[index + 1].v		= ( uvprojections[1].y + 1.0f ) * 0.5f;
						p_target_verts[index + 2].u		= ( uvprojections[2].x + 1.0f ) * 0.5f;
						p_target_verts[index + 2].v		= ( uvprojections[2].y + 1.0f ) * 0.5f;

						// ...then the vertex colors.
						p_target_verts[index + 0].col	= ( *(D3DCOLOR*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 ) + p_mesh->m_diffuse_offset ) & 0xFFFFFFUL ) | 0x80000000UL;
						p_target_verts[index + 1].col	= ( *(D3DCOLOR*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 ) + p_mesh->m_diffuse_offset ) & 0xFFFFFFUL ) | 0x80000000UL;
						p_target_verts[index + 2].col	= ( *(D3DCOLOR*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 ) + p_mesh->m_diffuse_offset ) & 0xFFFFFFUL ) | 0x80000000UL;
						
						D3DXVECTOR3	*p_v0 = &( p_target_verts[index + 0].pos );
						D3DXVECTOR3	*p_v1 = &( p_target_verts[index + 1].pos );
						D3DXVECTOR3	*p_v2 = &( p_target_verts[index + 2].pos );
						Mth::Vector pv(	p_v1->x - p_v0->x, p_v1->y - p_v0->y, p_v1->z - p_v0->z );
						Mth::Vector qv(	p_v2->x - p_v0->x, p_v2->y - p_v0->y, p_v2->z - p_v0->z );
						Mth::Vector r(( pv[Y] * qv[Z] ) - ( qv[Y] * pv[Z] ), ( pv[Z] * qv[X] ) - ( qv[Z] * pv[X] ), ( pv[X] * qv[Y] ) - ( qv[X] * pv[Y] ));
						float area_squared = r.LengthSqr();

						// Set the shatter test to ensure that we don't subdivide too far. Note that each successive subdivision will quarter
						// the area of each triangle, which means the area *squared* of each triangle will become 1/16th of the previous value.
						shatterAreaTest = area_squared / 128.0f;
						
						triSubdivideStack.Reset();
						triSubdivideStack.SetBlockSize( sizeof( sXboxSplatVert ));
						triSubdivideStack.Push( &p_target_verts[index + 0] );
						triSubdivideStack.Push( &p_target_verts[index + 1] );
						triSubdivideStack.Push( &p_target_verts[index + 2] );

						// Allocate a block of memory into which the subdivision stack will write the results.
						uint8			*p_array		= new uint8[8 * 1024];
						uint8			*p_array_start	= p_array;
						uint8			*p_array_loop	= p_array;
						memset( p_array, 0, 8 * 1024 );

						NxXbox::sMesh	*p_dummy_mesh	= new NxXbox::sMesh();
						p_dummy_mesh->m_diffuse_offset	= 12;
						p_dummy_mesh->m_uv0_offset		= 16;

						while( subdivide_tri_stack( &p_array, p_dummy_mesh ));

						// Ensure we haven't overrun the buffer.
						Dbg_Assert((uint32)p_array - (uint32)p_array_loop < ( 8 * 1024 ));

						bool subdivided_tri_added = false;

						while( p_array_loop != p_array )
						{
							// Add this triangle, *if* it is valid.
							if( tri_texture_intersect((((sXboxSplatVert*)p_array_loop )[0].u * 2.0f ) - 1.0f,
													  (((sXboxSplatVert*)p_array_loop )[0].v * 2.0f ) - 1.0f,
													  (((sXboxSplatVert*)p_array_loop )[1].u * 2.0f ) - 1.0f,
													  (((sXboxSplatVert*)p_array_loop )[1].v * 2.0f ) - 1.0f,
													  (((sXboxSplatVert*)p_array_loop )[2].u * 2.0f ) - 1.0f,
													  (((sXboxSplatVert*)p_array_loop )[2].v * 2.0f ) - 1.0f ))
							{
								// We have added at least one subdivided tri.
								subdivided_tri_added = true;

								// Convert lifetime from seconds to milliseconds.
								p_details->m_lifetimes[idx]		= (int)( lifetime * 1000.0f );
							
								p_target_verts[index + 0].pos	= ((sXboxSplatVert*)p_array_loop )[0].pos;
								p_target_verts[index + 1].pos	= ((sXboxSplatVert*)p_array_loop )[1].pos;
								p_target_verts[index + 2].pos	= ((sXboxSplatVert*)p_array_loop )[2].pos;
							
								p_target_verts[index + 0].u		= ((sXboxSplatVert*)p_array_loop )[0].u;
								p_target_verts[index + 0].v		= ((sXboxSplatVert*)p_array_loop )[0].v;
								p_target_verts[index + 1].u		= ((sXboxSplatVert*)p_array_loop )[1].u;
								p_target_verts[index + 1].v		= ((sXboxSplatVert*)p_array_loop )[1].v;
								p_target_verts[index + 2].u		= ((sXboxSplatVert*)p_array_loop )[2].u;
								p_target_verts[index + 2].v		= ((sXboxSplatVert*)p_array_loop )[2].v;

								p_target_verts[index + 0].col	= (((sXboxSplatVert*)p_array_loop )[0].col & 0xFFFFFFUL ) | 0x80000000UL;
								p_target_verts[index + 1].col	= (((sXboxSplatVert*)p_array_loop )[1].col & 0xFFFFFFUL ) | 0x80000000UL;
								p_target_verts[index + 2].col	= (((sXboxSplatVert*)p_array_loop )[2].col & 0xFFFFFFUL ) | 0x80000000UL;
							
								idx								= p_details->GetOldestSplat();
								index							= idx * 3;
							}
							
							p_array_loop						+= ( sizeof( sXboxSplatVert ) * 3 );
						}

						if( !subdivided_tri_added )
						{
							// No subdivided tris were added. This means we still have the large tri sitting in the list, which we don't want.
							p_details->m_lifetimes[idx]			= 0;
							plat_texture_splat_reset_poly( p_details, idx );
						}
						
						delete p_dummy_mesh;
						delete [] p_array_start;

#						if DRAW_DEBUG_LINES
						D3DXVECTOR3* p_d3dvert;
						p_d3dvert = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 ));
						Mth::Vector v0( p_d3dvert->x, p_d3dvert->y, p_d3dvert->z );
						p_d3dvert = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 ));
						Mth::Vector v1( p_d3dvert->x, p_d3dvert->y, p_d3dvert->z );
						p_d3dvert = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 ));
						Mth::Vector v2( p_d3dvert->x, p_d3dvert->y, p_d3dvert->z );
						Gfx::AddDebugLine( v0, v1, MAKE_RGB( 0, 200, 200 ), MAKE_RGB( 0, 200, 200 ), 1 );
						Gfx::AddDebugLine( v1, v2, MAKE_RGB( 0, 200, 200 ), MAKE_RGB( 0, 200, 200 ), 1 );
						Gfx::AddDebugLine( v2, v0, MAKE_RGB( 0, 200, 200 ), MAKE_RGB( 0, 200, 200 ), 1 );
#						endif // DRAW_DEBUG_LINES
					}
				}
			}
		}
		++pp_sectors;
	}
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_texture_splat_render( void )
{
	sXboxSplatInstanceDetails *p_xbox_details;

	Dbg_Assert( p_splat_details_table );

	NxXbox::set_pixel_shader( 0 );	
	NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
	
	D3DDevice_SetTextureStageState( 0, D3DTSS_BORDERCOLOR, 0x00000000UL );

	// Store the stage zero minfilter, since it may be anisotropic.
	DWORD stage_zero_minfilter;
	D3DDevice_GetTextureStageState( 0, D3DTSS_MINFILTER, &stage_zero_minfilter );
	D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );

	p_splat_details_table->IterateStart();
	sSplatInstanceDetails *p_details = p_splat_details_table->IterateNext();
	
	while( p_details )
	{
		p_xbox_details = static_cast( p_details );

		if( p_xbox_details->m_highest_active_splat >= 0 )
		{
			p_xbox_details->mp_material->Submit();
			NxXbox::EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_TRIANGLELIST, p_xbox_details->m_highest_active_splat + 1, p_xbox_details->m_verts, sizeof( sXboxSplatVert ));
		}
		
		p_details = p_splat_details_table->IterateNext();
	}

	// Restore the stage zero minfilter.
	D3DDevice_SetTextureStageState( 0, D3DTSS_MINFILTER, stage_zero_minfilter );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_shatter_initialize( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_shatter_cleanup( void )
{
	sXboxShatterInstanceDetails *p_xbox_details;

	Dbg_Assert( p_shatter_details_table );
	
	p_shatter_details_table->IterateStart();
	sShatterInstanceDetails *p_details = p_shatter_details_table->IterateNext();
	while( p_details )
	{
		p_xbox_details = static_cast( p_details );
		
		p_details = p_shatter_details_table->IterateNext();

		p_shatter_details_table->FlushItem((uint32)p_xbox_details );
		delete p_xbox_details;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void plat_shatter( CGeom *p_geom )
{
	CXboxGeom *p_xbox_geom = static_cast( p_geom );

	// For each mesh in the geom...
	for( uint32 m = 0; m < p_xbox_geom->m_num_mesh; ++m )
	{
		NxXbox::sMesh *p_mesh = p_xbox_geom->m_mesh_array[m];

		if( p_mesh->m_num_indices[0] >= 3 )
		{
			// Set the block size for this mesh.
			triSubdivideStack.SetBlockSize( p_mesh->m_vertex_stride );
			
			// Get a pointer to the renderable data.
			BYTE *p_vert_data;
			p_mesh->mp_vertex_buffer[0]->Lock( 0, 0, &p_vert_data, D3DLOCK_READONLY );
				
			// First scan through each non-degenerate tri, counting them to see how many verts we'll need.
			// We also have to figure the area of the tris here, since we need to calculate the worst case given the requirements for subdivision.
			uint32 valid_tris	= 0;
			uint32 index0;
			uint32 index1		= p_mesh->mp_index_buffer[0][0];
			uint32 index2		= p_mesh->mp_index_buffer[0][1];
			for( uint32 i = 2; i < p_mesh->m_num_indices[0]; ++i )
			{
				// Wrap the indices round.
				index0 = index1;
				index1 = index2;
				index2 = p_mesh->mp_index_buffer[0][i];

				if(( index0 != index1 ) && ( index0 != index2 ) && ( index1 != index2 ))
				{
					++valid_tris;

					D3DXVECTOR3 *p_vert0 = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index0 ));
					D3DXVECTOR3 *p_vert1 = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index1 ));
					D3DXVECTOR3 *p_vert2 = (D3DXVECTOR3*)( p_vert_data + ( p_mesh->m_vertex_stride * index2 ));
					
					// Push this tri onto the stack.
					triSubdivideStack.Push( p_vert0 );
					triSubdivideStack.Push( p_vert1 );
					triSubdivideStack.Push( p_vert2 );

					// Figure the area of this tri.
					Mth::Vector p(	p_vert1->x - p_vert0->x, p_vert1->y - p_vert0->y, p_vert1->z - p_vert0->z );
					Mth::Vector q(	p_vert2->x - p_vert0->x, p_vert2->y - p_vert0->y, p_vert2->z - p_vert0->z );
					Mth::Vector r(( p[Y] * q[Z] ) - ( q[Y] * p[Z] ), ( p[Z] * q[X] ) - ( q[Z] * p[X] ), ( p[X] * q[Y] ) - ( q[X] * p[Y] ));
					float area_squared = r.LengthSqr();
					if( area_squared > shatterAreaTest )
					{
						// We will need to subdivide - each subdivision will result in an area one quarter the previous area
						// (and thusly the square of the area will be one sixteenth the previous area).
						int num_extra_tris = 1;
						while( area_squared > shatterAreaTest )
						{
							num_extra_tris *= 4;
							area_squared *= ( 1.0f / 16.0f );
						}
					
						// This original tri will not be added...
						--valid_tris;

						// ...however, the subdivided versions will.
						valid_tris += num_extra_tris;
					}
				}
			}

			if( valid_tris == 0 )
			{
				continue;
			}
			
			// Create a tracking structure for this mesh.
			sXboxShatterInstanceDetails *p_details		= new sXboxShatterInstanceDetails( valid_tris, p_mesh );
			uint8						*p_write_vertex	= p_details->mp_vertex_buffer;
			uint32						details_index	= 0;

			Mth::Vector					spread_center	= shatterVelocity * -shatterSpreadFactor;
			float						base_speed		= shatterVelocity.Length();

			spread_center += Mth::Vector( p_mesh->m_sphere_center.x, p_mesh->m_sphere_center.y, p_mesh->m_sphere_center.z );
			
			// Add the tracking structure to the table.
			p_shatter_details_table->PutItem((uint32)p_details, p_details );
			
			// Process-subdivide the entire stack.
			uint8 *p_copy_vertex = p_write_vertex;
			while( subdivide_tri_stack( &p_write_vertex, p_mesh ));
					
			// Copy the (possibly subdivided) vertex data over.
			Dbg_Assert(((uint32)p_write_vertex - (uint32)p_copy_vertex ) <= ( valid_tris * 3 * p_mesh->m_vertex_stride ));
			while( p_copy_vertex < p_write_vertex )
			{
				Dbg_Assert( details_index < valid_tris );
				
				D3DXVECTOR3 *p_vert0 = (D3DXVECTOR3*)( p_copy_vertex + ( p_mesh->m_vertex_stride * 0 ));
				D3DXVECTOR3 *p_vert1 = (D3DXVECTOR3*)( p_copy_vertex + ( p_mesh->m_vertex_stride * 1 ));
				D3DXVECTOR3 *p_vert2 = (D3DXVECTOR3*)( p_copy_vertex + ( p_mesh->m_vertex_stride * 2 ));

				// Calculate position as the midpoint of the three vertices per poly.
				p_details->mp_positions[details_index][X] = ( p_vert0->x + p_vert1->x + p_vert2->x ) * ( 1.0f / 3.0f );
				p_details->mp_positions[details_index][Y] = ( p_vert0->y + p_vert1->y + p_vert2->y ) * ( 1.0f / 3.0f );
				p_details->mp_positions[details_index][Z] = ( p_vert0->z + p_vert1->z + p_vert2->z ) * ( 1.0f / 3.0f );

				// Calculate the vector  back from the bounding box of the object. Then use this to figure the 'spread' of the
				// shards by calculating the vector from this position to the center of each shard.
				float speed = base_speed + ( base_speed * (( shatterVelocityVariance * rand() ) / RAND_MAX ));
				p_details->mp_velocities[details_index] = ( p_details->mp_positions[details_index] - spread_center ).Normalize( speed );

				Mth::Vector axis( -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), -1.0f + ( 2.0f * (float)rand() / RAND_MAX ), -1.0f + ( 2.0f * (float)rand() / RAND_MAX ));
				axis.Normalize();
				p_details->mp_matrices[details_index].Ident();
				p_details->mp_matrices[details_index].Rotate( axis, 0.1f * ((float)rand() / RAND_MAX ));

				p_copy_vertex += ( p_mesh->m_vertex_stride * 3 );
						
				++details_index;
			}
		}
	}
}



/******************************************************************************
 *
 * 
 *****************************************************************************/
void plat_shatter_update( sShatterInstanceDetails *p_details, float framelength )
{
	sXboxShatterInstanceDetails *p_xbox_details = static_cast( p_details );
	
	BYTE *p_vert_data = p_xbox_details->mp_vertex_buffer;
	
	// Load up initial three vertex pointers.
	D3DXVECTOR3 *p_v0	= (D3DXVECTOR3*)( p_vert_data );
	D3DXVECTOR3 *p_v1	= (D3DXVECTOR3*)( p_vert_data + p_xbox_details->mp_mesh->m_vertex_stride );
	D3DXVECTOR3 *p_v2	= (D3DXVECTOR3*)( p_vert_data + ( 2 * p_xbox_details->mp_mesh->m_vertex_stride ));
	
	for( int i = 0; i < p_details->m_num_triangles; ++i )
	{
		// To move the shatter pieces:
		// 1) subtract position from each vertex
		// 2) rotate
		// 3) update position with velocity
		// 4) add new position to each vertex

		// The matrix holds 3 vectors at once.
		Mth::Matrix m;
		m[X].Set( p_v0->x - p_details->mp_positions[i][X], p_v0->y - p_details->mp_positions[i][Y], p_v0->z - p_details->mp_positions[i][Z] );
		m[Y].Set( p_v1->x - p_details->mp_positions[i][X], p_v1->y - p_details->mp_positions[i][Y], p_v1->z - p_details->mp_positions[i][Z] );
		m[Z].Set( p_v2->x - p_details->mp_positions[i][X], p_v2->y - p_details->mp_positions[i][Y], p_v2->z - p_details->mp_positions[i][Z] );
         
		m[X].Rotate( p_details->mp_matrices[i] );
		m[Y].Rotate( p_details->mp_matrices[i] );
		m[Z].Rotate( p_details->mp_matrices[i] );

		// Update the position and velocity of the shatter piece, dealing with bouncing if necessary.
		p_details->UpdateParameters( i, framelength );
      
		m[X] += p_details->mp_positions[i]; 
		m[Y] += p_details->mp_positions[i]; 
		m[Z] += p_details->mp_positions[i];

		p_v0->x = m[X][X]; p_v0->y = m[X][Y]; p_v0->z = m[X][Z];
		p_v1->x = m[Y][X]; p_v1->y = m[Y][Y]; p_v1->z = m[Y][Z];
		p_v2->x = m[Z][X]; p_v2->y = m[Z][Y]; p_v2->z = m[Z][Z];

		p_v0 = (D3DXVECTOR3*)(((BYTE*)p_v0 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
		p_v1 = (D3DXVECTOR3*)(((BYTE*)p_v1 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
		p_v2 = (D3DXVECTOR3*)(((BYTE*)p_v2 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
	}

	// Also process normals if they exist.
	if( p_xbox_details->mp_mesh->m_normal_offset > 0 )
	{
		p_v0	= (D3DXVECTOR3*)( p_vert_data + p_xbox_details->mp_mesh->m_normal_offset );
		p_v1	= (D3DXVECTOR3*)( p_vert_data + p_xbox_details->mp_mesh->m_normal_offset + p_xbox_details->mp_mesh->m_vertex_stride );
		p_v2	= (D3DXVECTOR3*)( p_vert_data + p_xbox_details->mp_mesh->m_normal_offset + ( 2 * p_xbox_details->mp_mesh->m_vertex_stride ));
	
		for( int i = 0; i < p_details->m_num_triangles; ++i )
		{
			// The matrix holds 3 vectors at once.
			Mth::Matrix m;
			m[X].Set( p_v0->x, p_v0->y, p_v0->z );
			m[Y].Set( p_v1->x, p_v1->y, p_v1->z );
			m[Z].Set( p_v2->x, p_v2->y, p_v2->z );
         
			m[X].Rotate( p_details->mp_matrices[i] );
			m[Y].Rotate( p_details->mp_matrices[i] );
			m[Z].Rotate( p_details->mp_matrices[i] );

			p_v0->x = m[X][X]; p_v0->y = m[X][Y]; p_v0->z = m[X][Z];
			p_v1->x = m[Y][X]; p_v1->y = m[Y][Y]; p_v1->z = m[Y][Z];
			p_v2->x = m[Z][X]; p_v2->y = m[Z][Y]; p_v2->z = m[Z][Z];

			p_v0 = (D3DXVECTOR3*)(((BYTE*)p_v0 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
			p_v1 = (D3DXVECTOR3*)(((BYTE*)p_v1 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
			p_v2 = (D3DXVECTOR3*)(((BYTE*)p_v2 ) + ( p_xbox_details->mp_mesh->m_vertex_stride * 3 ));
		}
	}
}



/******************************************************************************
 *
 * 
 *****************************************************************************/
void plat_shatter_render( sShatterInstanceDetails *p_details )
{
	sXboxShatterInstanceDetails *p_xbox_details = static_cast( p_details );

	p_xbox_details->mp_mesh->mp_material->Submit();

	NxXbox::set_pixel_shader( p_xbox_details->mp_mesh->m_pixel_shader );
	NxXbox::set_vertex_shader( p_xbox_details->mp_mesh->m_vertex_shader[0] );

	NxXbox::EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_TRIANGLELIST, p_xbox_details->m_num_triangles, p_xbox_details->mp_vertex_buffer, p_xbox_details->mp_mesh->m_vertex_stride );
}

	
///////////////////////////////////////////////////////////////////
//
// FOG
//
///////////////////////////////////////////////////////////////////

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFog::s_plat_enable_fog( bool enable )
{
	if( enable != (bool)NxXbox::EngineGlobals.fog_enabled )
	{
		NxXbox::EngineGlobals.fog_enabled = enable;
		D3DDevice_SetRenderState( D3DRS_FOGENABLE, NxXbox::EngineGlobals.fog_enabled );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFog::s_plat_set_fog_near_distance( float distance )
{
	NxXbox::EngineGlobals.fog_start	= -distance;

	// Test code for now.
	NxXbox::EngineGlobals.fog_end	= NxXbox::EngineGlobals.fog_start - FEET_TO_INCHES( 600.0f );

	D3DDevice_SetRenderState( D3DRS_FOGSTART,	*((DWORD*)( &NxXbox::EngineGlobals.fog_start )));
	D3DDevice_SetRenderState( D3DRS_FOGEND,		*((DWORD*)( &NxXbox::EngineGlobals.fog_end )));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFog::s_plat_set_fog_exponent( float exponent )
{
	// This is no longer a valid call.
//	if( exponent > 0.0f )
//	{
//		s_plat_enable_fog( true );
//
//		NxXbox::EngineGlobals.fog_start				= FEET_TO_INCHES( -20.0f );
//		NxXbox::EngineGlobals.fog_end				= FEET_TO_INCHES( -60.0f );
//		D3DDevice_SetRenderState( D3DRS_FOGSTART,	*((DWORD*)( &NxXbox::EngineGlobals.fog_start )));
//		D3DDevice_SetRenderState( D3DRS_FOGEND,		*((DWORD*)( &NxXbox::EngineGlobals.fog_end )));
//	}
//	else
//	{
//		s_plat_enable_fog( false );
//	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFog::s_plat_set_fog_color( void )
{
}
	


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFog::s_plat_fog_update( void )
{
}
	


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFog::s_plat_set_fog_rgba( Image::RGBA rgba )
{
	// Alpha effectively determines the fog density, with zero alpha meaning no fog.
	if( rgba.a == 0 )
	{
		s_plat_enable_fog( false );
	}
	else
	{
		s_plat_enable_fog( true );
	}

	// Calculate alpha (and clamp between 0.0 and 1.0).
	float f_alpha = (float)rgba.a / 128.0f;
	f_alpha = Mth::Min( f_alpha, 1.0f );
	f_alpha = Mth::Max( f_alpha, 0.0f );

	// Set the density register in the pixel shader constants (uses c4.r/g/b).
	NxXbox::EngineGlobals.pixel_shader_constants[16] = f_alpha;
	NxXbox::EngineGlobals.pixel_shader_constants[17] = f_alpha;
	NxXbox::EngineGlobals.pixel_shader_constants[18] = f_alpha;

	NxXbox::EngineGlobals.fog_color	= ((uint32)rgba.r << 16 ) | ((uint32)rgba.g << 8 ) | ((uint32)rgba.b );
	D3DDevice_SetRenderState( D3DRS_FOGCOLOR, NxXbox::EngineGlobals.fog_color );
}

} // Nx


================================================
FILE: Code/Gfx/XBox/p_nxnewparticle.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_NxNewParticle.cpp										**
**																			**
**	Created by:		3/25/03	-	SPG											**
**																			**
**	Description:	Xbox new parametric particle system						**
*****************************************************************************/

#include 

#include 

#include 
#include 
#include 
#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static int rand_seed;
static int rand_a	= 314159265;
static int rand_b	= 178453311;


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void seed_particle_rnd( int s, int a, int b )
{
	rand_seed		= s;
	rand_a			= a;
	rand_b			= b;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static inline int particle_rnd( int n )
{
	rand_seed	= rand_seed * rand_a + rand_b;
	rand_a		= ( rand_a ^ rand_seed ) + ( rand_seed >> 4 );
	rand_b		+= ( rand_seed >> 3 ) - 0x10101010L;
	return (int)(( rand_seed & 0xffff ) * n ) >> 16;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CParticleStream::AdvanceSeed( int num_places )
{
	// Seed the random number generator back to the current seed.
	seed_particle_rnd( m_rand_seed, m_rand_a, m_rand_b );

	// Each particle will call the random function four times.
	for( int i = 0; i < ( num_places * 4 ); i++ )
	{
		rand_seed	= rand_seed * rand_a + rand_b;
		rand_a		= ( rand_a ^ rand_seed ) + ( rand_seed >> 4 );
		rand_b		+= ( rand_seed >> 3 ) - 0x10101010L;
	}

	m_rand_seed = rand_seed;
	m_rand_a	= rand_a;
	m_rand_b	= rand_b;
}
	
	
	
inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxNewParticle::plat_render( void )
{
	CParticleStream* p_stream;
	int i;

	// Process the streams.
	if( m_params.m_EmitRate && ( !m_emitting || ( m_params.m_EmitRate != mp_newest_stream->m_rate )))
	{	
		if( m_num_streams < m_max_streams )
		{
			// Add new stream to cyclic buffer
			m_num_streams++;
			mp_newest_stream++;
			if( mp_newest_stream == mp_stream + m_max_streams )
			{
				mp_newest_stream = mp_stream;
			}

			// Initialise new stream.
			mp_newest_stream->m_rate			= m_params.m_EmitRate;
			mp_newest_stream->m_interval		= 1.0f / m_params.m_EmitRate;
			mp_newest_stream->m_oldest_age		= 0.0f;
			mp_newest_stream->m_num_particles	= 0;
			mp_newest_stream->m_rand_seed		= rand();
			mp_newest_stream->m_rand_a			= 314159265;
			mp_newest_stream->m_rand_b			= 178453311;
			m_emitting = true;
		}
		else
		{
			m_emitting = false;
		}
	}
	else
	{
		m_emitting = m_params.m_EmitRate;
	}

	if( !m_num_streams )
		return;

	// Age all streams.
	for( i = 0, p_stream = mp_oldest_stream; i < m_num_streams; ++i )
	{
		// Increase age of oldest particle.
		p_stream->m_oldest_age += 1.0f / 60.0f;

		// Step pointer within cyclic buffer.
		p_stream++;
		if( p_stream == mp_stream + m_max_streams )
		{
			p_stream = mp_stream;
		}
	}

	// Births into newest stream.
	if( m_emitting )
	{
		// How many particles so far emitted?
		mp_newest_stream->m_num_particles = (int)( mp_newest_stream->m_oldest_age * mp_newest_stream->m_rate + 1.0f );
	}

	// Deaths from oldest stream.
	if( mp_oldest_stream->m_oldest_age > m_params.m_Lifetime )
	{
		// Work out number dead.
		int particles_dead = (int)(( mp_oldest_stream->m_oldest_age - m_params.m_Lifetime ) * mp_oldest_stream->m_rate + 1.0f );

		// Remove dead particles.
		mp_oldest_stream->m_num_particles -= particles_dead;

		// Should we keep processing the oldest stream?
		if( mp_oldest_stream->m_num_particles > 0 || ( m_num_streams == 1 && m_emitting ))
		{
			// Adjust age of oldest particle.
			mp_oldest_stream->m_oldest_age -= (float)particles_dead * mp_oldest_stream->m_interval;

			// Advance seed.
			mp_oldest_stream->AdvanceSeed( particles_dead );
		}
		else
		{
			// Remove oldest stream and wrap in cyclic buffer if necessary.
			m_num_streams--;
			mp_oldest_stream++;
			if( mp_oldest_stream == mp_stream + m_max_streams )
			{
				mp_oldest_stream = mp_stream;
			}
			if( !m_num_streams )
				return;
		}
	}

	// Now render the streams. after checking the bounding sphere is visible (for no dynamic systems).
	if( !m_params.m_LocalCoord )
	{
		D3DXVECTOR3	center( m_bsphere[X], m_bsphere[Y], m_bsphere[Z] );
		if( !NxXbox::frustum_check_sphere( ¢er, m_bsphere[W] ))
		{
			return;
		}
	}

	// Swap the r and b color components in the params.
	for( i = 0; i < vNUM_BOXES; ++i )
	{
		uint8 swap = m_params.m_Color[i].r;
		m_params.m_Color[i].r	= m_params.m_Color[i].b;
		m_params.m_Color[i].b	= swap;
	}

	// Figure the distance from the camera to the system, and use a different technique accordingly.
	// Need to do this because point sprites are limited to 64 pixels in each dimension, and sometimes
	// you can get close enough to a system that a single particle will occupy more than this.
	// A more sophisticated approach would be to figure what the max particle size could be at a given distance,
	// rather than using the current hardcoded distance value of 20 feet.
	float dist_squared = Mth::DistanceSqr( Mth::Vector( m_bsphere[X], m_bsphere[Y], m_bsphere[Z], 0.0f ),
										   Mth::Vector( NxXbox::EngineGlobals.cam_position.x, NxXbox::EngineGlobals.cam_position.y, NxXbox::EngineGlobals.cam_position.z, 0.0f ));
	if( dist_squared < 57600.0f )
	{
		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Concatenate p_matrix with the emmission angle to create the direction.
		Mth::Vector up( 0.0f, 1.0f, 0.0f );

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
		Mth::Vector screen_right	= Mth::CrossProduct( at, up );
		Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

		screen_right.Normalize();
		screen_up.Normalize();

		// Submit particle material.
		mp_material->Submit();

		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( ParticleNewFlatVS );
		NxXbox::set_pixel_shader( PixelShader0 );

		// Load up the combined world->view_projection matrix.
		XGMATRIX	dest_matrix;
		XGMATRIX	projMatrix;
		XGMATRIX	viewMatrix;
			
		// Projection matrix.
		XGMatrixTranspose( &projMatrix, &NxXbox::EngineGlobals.projection_matrix );
		
		// View matrix.
		XGMatrixTranspose( &viewMatrix, &NxXbox::EngineGlobals.view_matrix );
		viewMatrix.m[3][0] = 0.0f;
		viewMatrix.m[3][1] = 0.0f;
		viewMatrix.m[3][2] = 0.0f;
		viewMatrix.m[3][3] = 1.0f;

		// Calculate composite world->view->projection matrix (simplified since world transform will be indentity).
		XGMatrixMultiply( &dest_matrix, &projMatrix, &viewMatrix );

		// Load up the combined world, camera & projection matrix.
		D3DDevice_SetVertexShaderConstantFast( 0, (void*)&dest_matrix, 4 );

		float vector_upload[8];
		vector_upload[0]	= screen_right[X];
		vector_upload[1]	= screen_right[Y];
		vector_upload[2]	= screen_right[Z];
		vector_upload[4]	= screen_up[X];
		vector_upload[5]	= screen_up[Y];
		vector_upload[6]	= screen_up[Z];
		D3DDevice_SetVertexShaderConstantFast( 4, (void*)( &vector_upload[0] ), 2 );

		static float vconstants[32]	= {	0.0f,  0.0f, 1.0f, 1.0f,		// Vert tex coords in C8 through C11
										1.0f,  0.0f, 1.0f, 1.0f,
										1.0f,  1.0f, 1.0f, 1.0f,
										0.0f,  1.0f, 1.0f, 1.0f,
									-1.0f,  1.0f, 1.0f, 1.0f,		// Vert w/h multipliers in C12 through C15
										1.0f,  1.0f, 1.0f, 1.0f,
										1.0f, -1.0f, 1.0f, 1.0f,
									-1.0f, -1.0f, 1.0f, 1.0f };
		D3DDevice_SetVertexShaderConstantFast( 8, (void*)( &vconstants[0] ), 8 );

		float stream_params[24];
		stream_params[0]	= m_s0[X];
		stream_params[1]	= m_s0[Y];
		stream_params[2]	= m_s0[Z];
		stream_params[3]	= m_s0[W];

		stream_params[4]	= m_s1[X];
		stream_params[5]	= m_s1[Y];
		stream_params[6]	= m_s1[Z];
		stream_params[7]	= m_s1[W];

		stream_params[8]	= m_s2[X];
		stream_params[9]	= m_s2[Y];
		stream_params[10]	= m_s2[Z];
		stream_params[11]	= m_s2[W];

		stream_params[12]	= m_p0[X];
		stream_params[13]	= m_p0[Y];
		stream_params[14]	= m_p0[Z];
		stream_params[15]	= m_p0[W];

		stream_params[16]	= m_p1[X];
		stream_params[17]	= m_p1[Y];
		stream_params[18]	= m_p1[Z];
		stream_params[19]	= m_p1[W];

		stream_params[20]	= m_p2[X];
		stream_params[21]	= m_p2[Y];
		stream_params[22]	= m_p2[Z];
		stream_params[23]	= m_p2[W];
		D3DDevice_SetVertexShaderConstantFast( 18, (void*)( &stream_params[0] ), 6 );

		// Construct a packet with data for each stream.
		for( i = 0, p_stream = mp_oldest_stream; i < m_num_streams; i++, p_stream++ )
		{
			Dbg_MsgAssert( p_stream->m_num_particles < 65536, ( "particle limit reached" ));

			// Wrap at end of cyclic buffer.
			if( p_stream == mp_stream + m_max_streams )
			{
				p_stream = mp_stream;
			}

			// Calculate space needed.
			DWORD dwords_per_particle	= 28;
			DWORD dword_count			= dwords_per_particle * p_stream->m_num_particles;

			// Obtain push buffer lock.
			// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
			// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
			// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
			DWORD *p_push; 
			p_push = D3DDevice_BeginPush( dword_count );

			float t				= p_stream->m_oldest_age;
			float midpoint_time = m_params.m_Lifetime * ( m_params.m_ColorMidpointPct * 0.01f );

			// Seed the random number generators for this stream.
			seed_particle_rnd( p_stream->m_rand_seed, p_stream->m_rand_a, p_stream->m_rand_b );

			for( int p = 0; p < p_stream->m_num_particles; ++p )
			{
				// Generate random vector. Each component in the range [1.0, 2.0].
				float r[4];
				r[0] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 );
				r[1] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 );
				r[2] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 );
				r[3] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 );

				float color_interpolator;
				Image::RGBA	col0, col1;

				if( m_params.m_UseMidcolor )
				{
					if( t > midpoint_time )
					{
						color_interpolator	= ( t - midpoint_time ) * ReciprocalEstimate_ASM( m_params.m_Lifetime - midpoint_time );
						col0				= m_params.m_Color[1];
						col1				= m_params.m_Color[2];
					}
					else
					{
						color_interpolator	= t * ReciprocalEstimate_ASM( midpoint_time );
						col0				= m_params.m_Color[0];
						col1				= m_params.m_Color[1];
					}
				}
				else 
				{
					color_interpolator		= t * ReciprocalEstimate_ASM( m_params.m_Lifetime );
					col0					= m_params.m_Color[0];
					col1					= m_params.m_Color[2];
				}

				// We're going to be loading constants.
				p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_TRANSFORM_CONSTANT_LOAD, 1 );

				// Specify the starting register (physical registers are offset by 96 from the D3D logical register).
				p_push[1]	= 96 + 16;

				// Specify the number of DWORDS to load. 8 DWORDS for 2 constants.
				p_push[2]	= D3DPUSH_ENCODE( D3DPUSH_SET_TRANSFORM_CONSTANT, 8 );

				// Load r vector.
				p_push[3]	= *((DWORD*)&r[0] );
				p_push[4]	= *((DWORD*)&r[1] );
				p_push[5]	= *((DWORD*)&r[2] );
				p_push[6]	= *((DWORD*)&r[3] );

				// Load interpolator values.
				p_push[7]	= *((DWORD*)&t );
				p_push[8]	= *((DWORD*)&color_interpolator );
				p_push		+= 11;

				p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
				p_push[1]	= D3DPT_QUADLIST;
				p_push		+= 2;

				// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
				// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
				p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, 12 );
				++p_push;

				// Now we can start the actual vertex data.
				p_push[0]	= *((DWORD*)&col0 );
				p_push[1]	= *((DWORD*)&col1 );
				p_push[2]	= 0x00000000UL;

				p_push[3]	= *((DWORD*)&col0 );
				p_push[4]	= *((DWORD*)&col1 );
				p_push[5]	= 0x00010001UL;

				p_push[6]	= *((DWORD*)&col0 );
				p_push[7]	= *((DWORD*)&col1 );
				p_push[8]	= 0x00020002UL;
				
				p_push[9]	= *((DWORD*)&col0 );
				p_push[10]	= *((DWORD*)&col1 );
				p_push[11]	= 0x00030003UL;

				p_push		+= 12;

				// End of vertex data for this particle.
				p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
				p_push[1] = 0;
				p_push += 2;

				// Reduce t by particle interval.
				t -= p_stream->m_interval;
			}
			D3DDevice_EndPush( p_push );
		}
	}
	else
	{
		// Submit particle material.
		mp_material->Submit();

		// Point sprites actually use texture stage 3...
		NxXbox::set_render_state( RS_UVADDRESSMODE3, 0x00000000UL );
		mp_material->mp_tex[0]->Set( 3 );

		// Set up point sprite rendering.
		D3DDevice_SetRenderState( D3DRS_POINTSPRITEENABLE,	TRUE );
		D3DDevice_SetRenderState( D3DRS_POINTSCALEENABLE,	TRUE );
//		D3DDevice_SetRenderState( D3DRS_POINTSCALE_A,		FtoDW( 0.00f ));
//		D3DDevice_SetRenderState( D3DRS_POINTSCALE_B,		FtoDW( 0.00f ));
//		D3DDevice_SetRenderState( D3DRS_POINTSCALE_C,		FtoDW( 1.00f ));

		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( ParticleNewFlatPointSpriteVS );
		NxXbox::set_pixel_shader( PixelShaderPointSprite );

		// Load up the combined world->view_projection matrix.
		XGMATRIX	dest_matrix;
		XGMATRIX	projMatrix;
		XGMATRIX	viewMatrix;
			
		// Projection matrix.
		XGMatrixTranspose( &projMatrix, &NxXbox::EngineGlobals.projection_matrix );
		
		// View matrix.
		XGMatrixTranspose( &viewMatrix, &NxXbox::EngineGlobals.view_matrix );
		viewMatrix.m[3][0] = 0.0f;
		viewMatrix.m[3][1] = 0.0f;
		viewMatrix.m[3][2] = 0.0f;
		viewMatrix.m[3][3] = 1.0f;

		// Calculate composite world->view->projection matrix (simplified since world transform will be indentity).
		XGMatrixMultiply( &dest_matrix, &projMatrix, &viewMatrix );

		// Load up the combined world, camera & projection matrix.
		D3DDevice_SetVertexShaderConstantFast( 0, (void*)&dest_matrix, 4 );

		// Load up the stream parameters.
		D3DDevice_SetVertexShaderConstantFast( 18, (void*)( &m_s0[X] ), 1 );
		D3DDevice_SetVertexShaderConstantFast( 19, (void*)( &m_s1[X] ), 1 );
		D3DDevice_SetVertexShaderConstantFast( 20, (void*)( &m_s2[X] ), 1 );

		D3DDevice_SetVertexShaderConstantFast( 21, (void*)( &m_p0[X] ), 1 );
		D3DDevice_SetVertexShaderConstantFast( 22, (void*)( &m_p1[X] ), 1 );
		D3DDevice_SetVertexShaderConstantFast( 23, (void*)( &m_p2[X] ), 1 );

		// Load up the camera position and viewport height.
		static float cam_pos_viewport_height[4];
		cam_pos_viewport_height[0]	= NxXbox::EngineGlobals.cam_position.x;
		cam_pos_viewport_height[1]	= NxXbox::EngineGlobals.cam_position.y;
		cam_pos_viewport_height[2]	= NxXbox::EngineGlobals.cam_position.z;
		cam_pos_viewport_height[3]	= NxXbox::EngineGlobals.viewport.Height * 2.0f;

		D3DDevice_SetVertexShaderConstantFast( 24, (void*)( &cam_pos_viewport_height[0] ), 1 );

		// Construct a packet with data for each stream.
		for( i = 0, p_stream = mp_oldest_stream; i < m_num_streams; i++, p_stream++ )
		{
			Dbg_MsgAssert( p_stream->m_num_particles < 65536, ( "particle limit reached" ));

			// Wrap at end of cyclic buffer.
			if( p_stream == mp_stream + m_max_streams )
			{
				p_stream = mp_stream;
			}

			// Calculate space needed.
			DWORD dwords_per_particle	= 13;
			DWORD dword_count			= dwords_per_particle * p_stream->m_num_particles;

			// Obtain push buffer lock.
			// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
			// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
			// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
			DWORD *p_push; 
			p_push = D3DDevice_BeginPush( dword_count );

			float t				= p_stream->m_oldest_age;
			float midpoint_time = m_params.m_Lifetime * ( m_params.m_ColorMidpointPct * 0.01f );

			// Seed the random number generators for this stream.
			seed_particle_rnd( p_stream->m_rand_seed, p_stream->m_rand_a, p_stream->m_rand_b );

			for( int p = 0; p < p_stream->m_num_particles; ++p )
			{
				// Generate random vector. Each component in the range [1.0, 2.0].
				float r[4];
				r[0] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 );
				r[1] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 );
				r[2] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 );
				r[3] = 1.0f + ((float)particle_rnd( 16384 ) / 16384 );

				float color_interpolator;
				Image::RGBA	col0, col1;

				if( m_params.m_UseMidcolor )
				{
					if( t > midpoint_time )
					{
						color_interpolator	= ( t - midpoint_time ) * ReciprocalEstimate_ASM( m_params.m_Lifetime - midpoint_time );
						col0				= m_params.m_Color[1];
						col1				= m_params.m_Color[2];
					}
					else
					{
						color_interpolator	= t * ReciprocalEstimate_ASM( midpoint_time );
						col0				= m_params.m_Color[0];
						col1				= m_params.m_Color[1];
					}
				}
				else 
				{
					color_interpolator		= t * ReciprocalEstimate_ASM( m_params.m_Lifetime );
					col0					= m_params.m_Color[0];
					col1					= m_params.m_Color[2];
				}

				// Signal the primitive type to follow.
				p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
				p_push[1]	= D3DPT_POINTLIST;
				p_push		+= 2;

				// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
				// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
				p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, 8 );
				++p_push;

				// Now we can start the actual vertex data.

				// Load r vector.
				p_push[0]	= *((DWORD*)&r[0] );
				p_push[1]	= *((DWORD*)&r[1] );
				p_push[2]	= *((DWORD*)&r[2] );
				p_push[3]	= *((DWORD*)&r[3] );

				// Load time and color interpolator values.
				p_push[4]	= *((DWORD*)&t );
				p_push[5]	= *((DWORD*)&color_interpolator );

				// Load colors.
				p_push[6]	= *((DWORD*)&col0 );
				p_push[7]	= *((DWORD*)&col1 );

				p_push		+= 8;

				// End of vertex data for this particle.
				p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
				p_push[1] = 0;
				p_push += 2;

				// Reduce t by particle interval.
				t -= p_stream->m_interval;
			}
			D3DDevice_EndPush( p_push );
		}

		// Restore render states.
		D3DDevice_SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );
	}

	// Swap the r and b color components back in the params.
	for( i = 0; i < vNUM_BOXES; ++i )
	{
		uint8 swap = m_params.m_Color[i].r;
		m_params.m_Color[i].r	= m_params.m_Color[i].b;
		m_params.m_Color[i].b	= swap;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxNewParticle::update_position( void )
{
	// Convert 3-point -> PVA format
	float t1 = m_params.m_Lifetime * m_params.m_MidpointPct * 0.01f;
	float t2 = m_params.m_Lifetime;
	Mth::Vector u, a_;

	Mth::Vector x0	= m_params.m_BoxPos[0];
	x0[3]			= m_params.m_Radius[0];
	Mth::Vector x1	= m_params.m_BoxPos[1];
	x1[3]			= m_params.m_Radius[1];
	Mth::Vector x2	= m_params.m_BoxPos[2];
	x2[3]			= m_params.m_Radius[2];

	if( m_params.m_UseMidpoint )
	{
		u  = ( t2 * t2 * ( x1 - x0 ) - t1 * t1 * ( x2 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
		a_ = ( t1 * ( x2 - x0 ) - t2 * ( x1 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
	}
	else
	{
		u  = ( x2 - x0 ) / t2;
		a_.Set( 0, 0, 0, 0 );
	}

	m_p0 = x0 - 1.5f * m_s0;
	m_p1 = u  - 1.5f * m_s1;
	m_p2 = a_ - 1.5f * m_s2;
	m_p0[3] = x0[3] - 1.5f * m_s0[3];
	m_p1[3] = u[3]  - 1.5f * m_s1[3];
	m_p2[3] = a_[3] - 1.5f * m_s2[3];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxNewParticle::plat_update( void )
{
	if( m_params.m_LocalCoord )
	{
		update_position();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxNewParticle::plat_build( void )
{
	// Reduce emit rate selectively to improve performance.
	m_params.m_EmitRate	= m_params.m_EmitRate * 0.5f;

	// Initialise streams.
	m_max_streams		= 5;
	m_num_streams		= 0;

	mp_stream			= new CParticleStream[m_max_streams]; 
	mp_newest_stream	= mp_stream + m_max_streams - 1;
	mp_oldest_stream	= mp_stream;
	m_emitting			= false;

	// Create a (semi-transparent) material used to render the mesh.
	mp_material			= new NxXbox::sMaterial;
	ZeroMemory( mp_material, sizeof( NxXbox::sMaterial ));

	mp_material->m_flags[0]		= MATFLAG_TRANSPARENT | MATFLAG_TEXTURED;
	mp_material->m_passes		= 1;
	mp_material->m_alpha_cutoff	= 1;
	mp_material->m_no_bfc		= true;
	mp_material->m_color[0][0]	= 0.5f;
	mp_material->m_color[0][1]	= 0.5f;
	mp_material->m_color[0][2]	= 0.5f;
	mp_material->m_color[0][3]	= m_params.m_FixedAlpha * ( 1.0f /  128.0f );
	mp_material->m_reg_alpha[0]	= NxXbox::GetBlendMode( m_params.m_BlendMode );

	// Get texture.
	Nx::CTexture*		p_texture;
	Nx::CXboxTexture*	p_xbox_texture;
	mp_material->mp_tex[0]	= NULL;
	p_texture				= Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( m_params.m_Texture );
	p_xbox_texture			= static_cast( p_texture );
	if( p_xbox_texture )
	{
		mp_material->mp_tex[0] = p_xbox_texture->GetEngineTexture();
	}

	// Convert 3-point -> PVA format.
	float t1 = m_params.m_Lifetime * m_params.m_MidpointPct * 0.01f;
	float t2 = m_params.m_Lifetime;
	Mth::Vector x0,x1,x2,u,a_;

	x0    = m_params.m_BoxDims[0];
	x0[3] = m_params.m_RadiusSpread[0];
	x1    = m_params.m_BoxDims[1];
	x1[3] = m_params.m_RadiusSpread[1];
	x2    = m_params.m_BoxDims[2];
	x2[3] = m_params.m_RadiusSpread[2];

	if( m_params.m_UseMidpoint )
	{
		u  = ( t2 * t2 * ( x1 - x0 ) - t1 * t1 * ( x2 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
		a_ = ( t1 * ( x2 - x0 ) - t2 * ( x1 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
	}
	else
	{
		u  = ( x2 - x0 ) / t2;
		a_.Set( 0.0f, 0.0f, 0.0f, 0.0f );
	}

	m_s0 = x0;
	m_s1 = u;
	m_s2 = a_;

	x0    = m_params.m_BoxPos[0];
	x0[3] = m_params.m_Radius[0];
	x1    = m_params.m_BoxPos[1];
	x1[3] = m_params.m_Radius[1];
	x2    = m_params.m_BoxPos[2];
	x2[3] = m_params.m_Radius[2];

	if( m_params.m_UseMidpoint )
	{
		u  =  ( t2 * t2 * ( x1 - x0 ) - t1 * t1 * ( x2 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
		a_ =  ( t1 * ( x2 - x0 ) - t2 * ( x1 - x0 )) / ( t1 * t2 * ( t2 - t1 ));
	}
	else
	{
		u  = ( x2 - x0 ) / t2;
		a_.Set( 0.0f, 0.0f, 0.0f, 0.0f );
	}

	m_p0	= x0 - 1.5f * m_s0;
	m_p1	= u  - 1.5f * m_s1;
	m_p2	= a_ - 1.5f * m_s2;
	m_p0[3]	= x0[3] - 1.5f * m_s0[3];
	m_p1[3]	= u[3]  - 1.5f * m_s1[3];
	m_p2[3]	= a_[3] - 1.5f * m_s2[3];

	update_position();

	// Color.
	if( m_params.m_UseMidcolor )
	{
		float q0 = 100.0f / ( m_params.m_Lifetime * m_params.m_ColorMidpointPct );
		float q1 = 100.0f / ( m_params.m_Lifetime * ( 100.0f - m_params.m_ColorMidpointPct ));

//		m_systemDmaData.m_c0[0] = ((float)m_params.m_Color[1].r - (float)m_params.m_Color[0].r) * q0;
//		m_systemDmaData.m_c0[1] = ((float)m_params.m_Color[1].g - (float)m_params.m_Color[0].g) * q0;
//		m_systemDmaData.m_c0[2] = ((float)m_params.m_Color[1].b - (float)m_params.m_Color[0].b) * q0;
//		m_systemDmaData.m_c0[3] = ((float)m_params.m_Color[1].a - (float)m_params.m_Color[0].a) * q0;

//		m_systemDmaData.m_c1[0] = (float)m_params.m_Color[1].r;
//		m_systemDmaData.m_c1[1] = (float)m_params.m_Color[1].g;
//		m_systemDmaData.m_c1[2] = (float)m_params.m_Color[1].b;
//		m_systemDmaData.m_c1[3] = (float)m_params.m_Color[1].a;

//		m_systemDmaData.m_c2[0] = ((float)m_params.m_Color[2].r - (float)m_params.m_Color[1].r) * q1;
//		m_systemDmaData.m_c2[1] = ((float)m_params.m_Color[2].g - (float)m_params.m_Color[1].g) * q1;
//		m_systemDmaData.m_c2[2] = ((float)m_params.m_Color[2].b - (float)m_params.m_Color[1].b) * q1;
//		m_systemDmaData.m_c2[3] = ((float)m_params.m_Color[2].a - (float)m_params.m_Color[1].a) * q1;
	}
	else // else suppress mid-colour
	{
		float q = 1.0f / m_params.m_Lifetime;

//		m_systemDmaData.m_c1[0] = (float)m_params.m_Color[0].r;
//		m_systemDmaData.m_c1[1] = (float)m_params.m_Color[0].g;
//		m_systemDmaData.m_c1[2] = (float)m_params.m_Color[0].b;
//		m_systemDmaData.m_c1[3] = (float)m_params.m_Color[0].a;

//		m_systemDmaData.m_c2[0] = ((float)m_params.m_Color[2].r - (float)m_params.m_Color[0].r) * q;
//		m_systemDmaData.m_c2[1] = ((float)m_params.m_Color[2].g - (float)m_params.m_Color[0].g) * q;
//		m_systemDmaData.m_c2[2] = ((float)m_params.m_Color[2].b - (float)m_params.m_Color[0].b) * q;
//		m_systemDmaData.m_c2[3] = ((float)m_params.m_Color[2].a - (float)m_params.m_Color[0].a) * q;
	}

	// Rotation matrix.
	m_rotation.Identity();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxNewParticle::plat_destroy( void )
{
	if( mp_stream )
	{
		delete [] mp_stream;
	}

	if( mp_material )
	{
		delete mp_material;
	}
}



/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx






================================================
FILE: Code/Gfx/XBox/p_nxnewparticle.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_NxNewParticle.h										**
**																			**
**	Created by:		3/24/03	-	SPG											**
**																			**
**	Description:	Xbox implementation of new parametric particle system	**
**																			**
*****************************************************************************/

#ifndef __GFX_XBOX_P_NXNEWPARTICLE_H__
#define __GFX_XBOX_P_NXNEWPARTICLE_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Nx
{

                        
/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CParticleStream
{
public:
	int						m_num_particles;
	float					m_rate;
	float					m_interval;
	float					m_oldest_age;
	uint32					m_rand_seed;
	uint32					m_rand_a;
	uint32					m_rand_b;
	void					AdvanceSeed( int num_places );
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CXboxNewParticle : public CNewParticle
{
	bool				m_emitting;
	int					m_max_streams;
	int					m_num_streams;
	CParticleStream*	mp_stream;
	CParticleStream*	mp_newest_stream;
	CParticleStream*	mp_oldest_stream;
	Mth::Matrix 		m_rotation;
	Mth::Matrix			m_new_matrix;
	NxXbox::sMaterial*	mp_material;

	Mth::Vector			m_s0;
	Mth::Vector			m_s1;
	Mth::Vector			m_s2;
	Mth::Vector			m_p0;
	Mth::Vector			m_p1;
	Mth::Vector			m_p2;

protected:
	void	plat_build( void );
	void	plat_destroy( void );
	void	plat_render( void );
	void	plat_update( void );
	void	update_position( void );
};



/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx

#endif	// __GFX_XBOX_P_NXNEWPARTICLE_H__




================================================
FILE: Code/Gfx/XBox/p_nxnewparticlemgr.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_NxNewParticleMgr.cpp									**
**																			**
**	Created by:		3/25/03	-	SPG											**
**																			**
**	Description:	Xbox-specific parametric particle system manager		**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Nx
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

CNewParticle*	CXboxNewParticleManager::plat_create_particle( void )
{
	return new CXboxNewParticle;
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx






================================================
FILE: Code/Gfx/XBox/p_nxnewparticlemgr.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5 													**
**																			**
**	Module:			Gfx			 											**
**																			**
**	File name:		p_nxnewparticlemgr.h									**
**																			**
**	Created by:		3/24/03	-	SPG											**
**																			**
**	Description:	Xbox-specific new parametric particle system manager	**
**																			**
*****************************************************************************/

#ifndef __GFX_XBOX_P_NXNEWPARTICLEMGR_H__
#define __GFX_XBOX_P_NXNEWPARTICLEMGR_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Nx
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CXboxNewParticleManager : public CNewParticleManager
{
protected:
	virtual	CNewParticle*	plat_create_particle( void );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Nx

#endif	// __GFX_XBOX_P_NXNEWPARTICLEMGR_H__




================================================
FILE: Code/Gfx/XBox/p_nxparticle.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/xbox/nx/nx_init.h"
#include "gfx/xbox/p_nxparticle.h"
#include "gfx/xbox/p_nxparticleline.h"
#include "gfx/xbox/p_nxparticleflat.h"
#include "gfx/xbox/p_nxparticleshaded.h"
#include "gfx/xbox/p_nxparticlesmooth.h"
#include "gfx/xbox/p_nxparticleglow.h"
#include "gfx/xbox/p_nxparticlestar.h"
#include "gfx/xbox/p_nxparticlesmoothstar.h"
#include "gfx/xbox/p_nxparticleribbon.h"
#include "gfx/xbox/p_nxparticlesmoothribbon.h"
#include "gfx/xbox/p_nxparticleribbontrail.h"
#include "gfx/xbox/p_nxparticleglowribbontrail.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CParticle *plat_create_particle( uint32 checksum, uint32 type_checksum, int max_particles, int max_streams, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	switch( type_checksum )
	{
		case 0x2eeb4b09:	// Line
		{
			CXboxParticleLine *p_particle = new CXboxParticleLine( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split );
			return static_cast( p_particle );
		}

		case 0xaab555bb:	// Flat
		{
			CXboxParticleFlat *p_particle = new CXboxParticleFlat( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split );
			return static_cast( p_particle );
		}

		case 0xf4d8d486:	// Shaded
		{
			CXboxParticleShaded *p_particle = new CXboxParticleShaded( checksum, max_particles, texture_checksum, blendmode_checksum, fix );
			return static_cast( p_particle );
		}
		
		case 0x8addac1f:	// Smooth
		{
			CXboxParticleSmooth *p_particle = new CXboxParticleSmooth( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split );
			return static_cast( p_particle );
		}
		
		case 0x15834eea:	// Glow
		{
			CXboxParticleGlow *p_particle = new CXboxParticleGlow( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split );
			return static_cast( p_particle );
		}

		case 0x3624a5eb:	// Star
		{
			CXboxParticleStar *p_particle = new CXboxParticleStar( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split );
			return static_cast( p_particle );
		}
		
		case 0x97cb7a9:		// SmoothStar
		{
			CXboxParticleSmoothStar *p_particle = new CXboxParticleSmoothStar( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split );
			return static_cast( p_particle );
		}

		case 0xee6fc5b:		// Ribbon
		{
			CXboxParticleRibbon *p_particle = new CXboxParticleRibbon( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
			return static_cast( p_particle );
		}

		case 0x3f109fcc:	// SmoothRibbon
		{
			CXboxParticleSmoothRibbon *p_particle = new CXboxParticleSmoothRibbon( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
			return static_cast( p_particle );
		}

		case 0xc4d5a4cb:	// RibbonTrail
		{
			CXboxParticleRibbonTrail *p_particle = new CXboxParticleRibbonTrail( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
			return static_cast( p_particle );
		}

		case 0x7ec7252d:	// GlowRibbonTrail
		{
			CXboxParticleGlowRibbonTrail *p_particle = new CXboxParticleGlowRibbonTrail( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
			return static_cast( p_particle );
		}

		case 0xdedfc057:	// NewFlat
		{
			// Just default to old flat for now.
			CXboxParticleFlat *p_particle = new CXboxParticleFlat( checksum, max_particles, texture_checksum, blendmode_checksum, fix, num_segments, split );
			return static_cast( p_particle );
		}

		default:
		{
			Dbg_MsgAssert( 0, ( "Unsupported particle type" ));
			break;
		}
	}
	return NULL;
}

} // Nx

				




================================================
FILE: Code/Gfx/XBox/p_nxparticle.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticle.h
//* OWNER:          Dave Cowling
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLE_H__
#define	__GFX_P_NX_PARTICLE_H__
    
#include "gfx/nxparticle.h"
#include "gfx/xbox/nx/particles.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx

#endif 





================================================
FILE: Code/Gfx/XBox/p_nxparticleflat.cpp
================================================
#include 
#include 
#include "gfx/xbox/p_nxparticleflat.h"

extern DWORD PixelShader0;

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleFlat::CXboxParticleFlat()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleFlat::CXboxParticleFlat( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split )
{
	m_checksum		= checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;
	
	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

	// Create the engine representation.
	mp_engine_particle = new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_FLAT, texture_checksum, blendmode_checksum, fix );

	// Default color.
	m_start_color.r = m_start_color.g = m_start_color.b = 128;
	m_start_color.a = 255;
	m_mid_color.r = m_mid_color.g = m_mid_color.b = 128;
	m_mid_color.a = 255;
	m_end_color.r = m_end_color.g = m_end_color.b = 128;
	m_end_color.a = 255;

	m_mid_time = -1.0f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleFlat::~CXboxParticleFlat()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleFlat::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleFlat::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleFlat::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxParticleFlat::plat_get_num_particle_colors( void ) { return 1; }
int CXboxParticleFlat::plat_get_num_vertex_lists( void ) { return 1; }

// Note these are r/b reversed for direct uploading to Xbox GPU.
void CXboxParticleFlat::plat_set_sr( int entry, uint8 value ) { m_start_color.b = value; }
void CXboxParticleFlat::plat_set_sg( int entry, uint8 value ) { m_start_color.g = value; }
void CXboxParticleFlat::plat_set_sb( int entry, uint8 value ) { m_start_color.r = value; }
void CXboxParticleFlat::plat_set_sa( int entry, uint8 value ) { m_start_color.a = value; }
void CXboxParticleFlat::plat_set_mr( int entry, uint8 value ) { m_mid_color.b = value; }
void CXboxParticleFlat::plat_set_mg( int entry, uint8 value ) { m_mid_color.g = value; }
void CXboxParticleFlat::plat_set_mb( int entry, uint8 value ) { m_mid_color.r = value; }
void CXboxParticleFlat::plat_set_ma( int entry, uint8 value ) { m_mid_color.a = value; }
void CXboxParticleFlat::plat_set_er( int entry, uint8 value ) { m_end_color.b = value; }
void CXboxParticleFlat::plat_set_eg( int entry, uint8 value ) { m_end_color.g = value; }
void CXboxParticleFlat::plat_set_eb( int entry, uint8 value ) { m_end_color.r = value; }
void CXboxParticleFlat::plat_set_ea( int entry, uint8 value ) { m_end_color.a = value; }


		



#if 1
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleFlat::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Concatenate p_matrix with the emmission angle to create the direction.
		Mth::Vector up( 0.0f, 1.0f, 0.0f );

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
		Mth::Vector screen_right	= Mth::CrossProduct( at, up );
		Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

		screen_right.Normalize();
		screen_up.Normalize();

		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v;

		// Calculate space needed.
		DWORD dwords_per_particle	= 32;
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( ParticleFlatVS );
		NxXbox::set_pixel_shader( PixelShader0 );
		
		// Load up the combined world->view_projection matrix.
		XGMATRIX	temp_matrix;
		XGMATRIX	dest_matrix;
		XGMATRIX	projMatrix;
		XGMATRIX	viewMatrix;
		XGMATRIX	worldMatrix;
		
		// Projection matrix.
		XGMatrixTranspose( &projMatrix, &NxXbox::EngineGlobals.projection_matrix );
	
		// View matrix.
		XGMatrixTranspose( &viewMatrix, &NxXbox::EngineGlobals.view_matrix );
		viewMatrix.m[3][0] = 0.0f;
		viewMatrix.m[3][1] = 0.0f;
		viewMatrix.m[3][2] = 0.0f;
		viewMatrix.m[3][3] = 1.0f;

		// World space transformation matrix, set to be a translation matrix corresponding to the emitter position.
		XGMatrixTranslation( &worldMatrix, m_pos[0], m_pos[1], m_pos[2] );
		XGMatrixTranspose( &worldMatrix, &worldMatrix );

		// Calculate composite world->view->projection matrix.
		XGMatrixMultiply( &temp_matrix, &viewMatrix, &worldMatrix );
		XGMatrixMultiply( &dest_matrix, &projMatrix, &temp_matrix );

		// Load up the combined world, camera & projection matrix.
		D3DDevice_SetVertexShaderConstantFast( 0, (void*)&dest_matrix, 4 );

		float vector_upload[8];
		vector_upload[0]	= screen_right[X];
		vector_upload[1]	= screen_right[Y];
		vector_upload[2]	= screen_right[Z];
		vector_upload[4]	= screen_up[X];
		vector_upload[5]	= screen_up[Y];
		vector_upload[6]	= screen_up[Z];
		D3DDevice_SetVertexShaderConstantFast( 4, (void*)( &vector_upload[0] ), 2 );

		static float vconstants[32]	= {	0.0f,  0.0f, 1.0f, 1.0f,		// Vert tex coords in C8 through C11
										1.0f,  0.0f, 1.0f, 1.0f,
										1.0f,  1.0f, 1.0f, 1.0f,
										0.0f,  1.0f, 1.0f, 1.0f,
									   -1.0f,  1.0f, 1.0f, 1.0f,		// Vert w/h multipliers in C12 through C15
										1.0f,  1.0f, 1.0f, 1.0f,
										1.0f, -1.0f, 1.0f, 1.0f,
									   -1.0f, -1.0f, 1.0f, 1.0f };
		D3DDevice_SetVertexShaderConstantFast( 8, (void*)( &vconstants[0] ), 8 );

		// Obtain push buffer lock.
		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		DWORD *p_push; 
		p_push = D3DDevice_BeginPush( dword_count );

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v			= mp_vertices;

		for( ; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
		{
			// Calculate the interpolator ( 1.0f / particle_life ).
			float terp	= p_particle->m_time * ReciprocalEstimateNR_ASM( p_particle->m_life );

			// Separate interpolator for color.
			float col_terp;

			Mth::Vector	pos( p_v[0], p_v[1], p_v[2] );
			Image::RGBA *p_col0;
			Image::RGBA *p_col1;
		
			if( m_mid_time >= 0.0f )
			{
				if( terp < m_mid_time )
				{
					p_col0		= &m_start_color;
					p_col1		= &m_mid_color;

					// Adjust interpolation for this half of the color blend.
					col_terp	= terp / m_mid_time;
				}
				else
				{
					p_col0		= &m_mid_color;
					p_col1		= &m_end_color;

					// Adjust interpolation for this half of the color blend.
					col_terp	= ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
				}
			}
			else
			{
				// No mid color specified.
				p_col0		= &m_start_color;
				p_col1		= &m_end_color;

				// Color interpoltor value is the same as the regular interpolator.
				col_terp	= terp;
			}

			// We're going to be loading constants.
			p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_TRANSFORM_CONSTANT_LOAD, 1 );

			// Specify the starting register (physical registers are offset by 96 from the D3D logical register).
			p_push[1]	= 96 + 16;

			// Specify the number of DWORDS to load. 12 DWORDS for 3 constants.
			p_push[2]	= D3DPUSH_ENCODE( D3DPUSH_SET_TRANSFORM_CONSTANT, 12 );

			// Load position.
			p_push[3]	= *((DWORD*)&pos[X] );
			p_push[4]	= *((DWORD*)&pos[Y] );
			p_push[5]	= *((DWORD*)&pos[Z] );

			// Load start and end width and height.
			p_push[7]	= *((DWORD*)&p_particle->m_sw );
			p_push[8]	= *((DWORD*)&p_particle->m_sh );
			p_push[9]	= *((DWORD*)&p_particle->m_ew );
			p_push[10]	= *((DWORD*)&p_particle->m_eh );

			// Load size and color interpolators.
			p_push[11]	= *((DWORD*)&terp );
			p_push[12]	= *((DWORD*)&col_terp );

			p_push		+= 15;

			p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
			p_push[1]	= D3DPT_QUADLIST;
			p_push		+= 2;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, 12 );
			++p_push;

			// Now we can start the actual vertex data.
			p_push[0]	= *((DWORD*)p_col0 );
			p_push[1]	= *((DWORD*)p_col1 );
			p_push[2]	= 0x00000000UL;

			p_push[3]	= *((DWORD*)p_col0 );
			p_push[4]	= *((DWORD*)p_col1 );
			p_push[5]	= 0x00010001UL;

			p_push[6]	= *((DWORD*)p_col0 );
			p_push[7]	= *((DWORD*)p_col1 );
			p_push[8]	= 0x00020002UL;
			
			p_push[9]	= *((DWORD*)p_col0 );
			p_push[10]	= *((DWORD*)p_col1 );
			p_push[11]	= 0x00030003UL;

			p_push		+= 12;

			// End of vertex data for this particle.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
			p_push[1] = 0;
			p_push += 2;
		}
		D3DDevice_EndPush( p_push );
	}

	// Deal with the Ps2 specific extensions.
	if( m_emit_rate > 0.0f )
	{
		m_emit_rate_fractional += ( m_emit_rate * ( 1.0f / 60.0f ));

		if( m_emit_rate_fractional >= 1.0f )
		{
			// This should actually deal with fractional values by accumulating them.
			emit( Ftoi_ASM( m_emit_rate_fractional ));
			m_emit_rate_fractional -= (float)Ftoi_ASM( m_emit_rate_fractional );
		}
	}
}

#else
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleFlat::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Concatenate p_matrix with the emmission angle to create the direction.
		Mth::Vector up( 0.0f, 1.0f, 0.0f );

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
		Mth::Vector screen_right	= Mth::CrossProduct( at, up );
		Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

		screen_right.Normalize();
		screen_up.Normalize();

		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
		NxXbox::set_pixel_shader( PixelShader0 );
		
		DWORD dwords_per_particle	= 24;
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Obtain push buffer lock.
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		DWORD *p_push; 
		p_push = D3DDevice_BeginPush( dword_count + ( dword_count / 2047 ) + 16 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_QUADLIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v			= mp_vertices;

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}
				
				// Calculate the interpolator ( 1.0f / particle_life ).
				float terp	= p_particle->m_time * ReciprocalEstimateNR_ASM( p_particle->m_life );
				float w		= p_particle->m_sw + (( p_particle->m_ew - p_particle->m_sw ) * terp );
				float h		= p_particle->m_sh + (( p_particle->m_eh - p_particle->m_sh ) * terp );

				// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
				Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				Mth::Vector	ss_right, ss_up;
				Mth::Vector tmp;
		
				ss_right	= screen_right * w;
				ss_up		= screen_up * h;

				Image::RGBA color;
				Image::RGBA *p_col0;
				Image::RGBA *p_col1;
		
				if( m_mid_time >= 0.0f )
				{
					if( terp < m_mid_time )
					{
						p_col0 = &m_start_color;
						p_col1 = &m_mid_color;

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = &m_mid_color;
						p_col1 = &m_end_color;

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = &m_start_color;
					p_col1 = &m_end_color;
				}

				Image::RGBA start	= *p_col0++;
				Image::RGBA end		= *p_col1++;

				// Use fixed point math to avoid _ftol2 calls.
				int f_terp	= Ftoi_ASM( terp * 4096.0f );
				color.r		= ((((int)start.r ) * 4096 ) + (((int)end.r - (int)start.r ) * f_terp )) / 4096;
				color.g		= ((((int)start.g ) * 4096 ) + (((int)end.g - (int)start.g ) * f_terp )) / 4096;
				color.b		= ((((int)start.b ) * 4096 ) + (((int)end.b - (int)start.b ) * f_terp )) / 4096;
				color.a		= ((((int)start.a ) * 4096 ) + (((int)end.a - (int)start.a ) * f_terp )) / 4096;
		
				tmp			= pos - ss_right + ss_up;
				p_push[0]	= *((DWORD*)&tmp[X] );
				p_push[1]	= *((DWORD*)&tmp[Y] );
				p_push[2]	= *((DWORD*)&tmp[Z] );
				p_push[3]	= *((DWORD*)&color );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x00000000UL;
	
				tmp			= pos + ss_right + ss_up;		
				p_push[6]	= *((DWORD*)&tmp[X] );
				p_push[7]	= *((DWORD*)&tmp[Y] );
				p_push[8]	= *((DWORD*)&tmp[Z] );
				p_push[9]	= *((DWORD*)&color );
				p_push[10]	= 0x3F800000UL;
				p_push[11]	= 0x00000000UL;

				tmp			= pos + ss_right - ss_up;		
				p_push[12]	= *((DWORD*)&tmp[X] );
				p_push[13]	= *((DWORD*)&tmp[Y] );
				p_push[14]	= *((DWORD*)&tmp[Z] );
				p_push[15]	= *((DWORD*)&color );
				p_push[16]	= 0x3F800000UL;
				p_push[17]	= 0x3F800000UL;
			
				tmp			= pos - ss_right - ss_up;		
				p_push[18]	= *((DWORD*)&tmp[X] );
				p_push[19]	= *((DWORD*)&tmp[Y] );
				p_push[20]	= *((DWORD*)&tmp[Z] );
				p_push[21]	= *((DWORD*)&color );
				p_push[22]	= 0x00000000UL;
				p_push[23]	= 0x3F800000UL;

				p_push		+= 24;

				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );
	}
}
#endif



} // Nx



================================================
FILE: Code/Gfx/XBox/p_nxparticleflat.h
================================================
#ifndef	__GFX_P_NX_PARTICLEFLAT_H__
#define	__GFX_P_NX_PARTICLEFLAT_H__
    
#include "gfx/xbox/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CXboxParticleFlat : public CParticle
{
	public:
							CXboxParticleFlat();
							CXboxParticleFlat( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split );
	virtual 				~CXboxParticleFlat();

//	NxXbox::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	float*						mp_vertices;
	uint32*						mp_colors;
	uint64						m_blend;
	NxXbox::sParticleSystem*	mp_engine_particle;
	float						m_emit_rate_fractional;		// Deals with the fractional part of m_emit_rate for the pseudo parametric systems.

	Image::RGBA					m_start_color;				// Start color for each corner.
	Image::RGBA					m_mid_color;				// Mid color for each corner.
	Image::RGBA					m_end_color;				// End color for each corner.
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 







================================================
FILE: Code/Gfx/XBox/p_nxparticleglow.cpp
================================================
#include 
#include "gfx/xbox/nx/render.h"

#include "gfx/xbox/p_nxparticleGlow.h"

extern DWORD PixelShader1;

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleGlow::CXboxParticleGlow()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleGlow::CXboxParticleGlow( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split )
{
	m_checksum		= checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;
	m_num_segments	= num_segments;
	m_split			= split;
	m_mid_time		= -1.0f;

	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

	// Create the engine representation.
	mp_engine_particle = new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_GLOW, texture_checksum, blendmode_checksum, fix, num_segments );

	// Default color.
	for ( int lp = 0; lp < 3; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 255;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 255;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 255;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleGlow::~CXboxParticleGlow()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleGlow::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleGlow::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleGlow::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxParticleGlow::plat_get_num_particle_colors( void ) { return 3; }
int CXboxParticleGlow::plat_get_num_vertex_lists( void ) { return 1; }
void CXboxParticleGlow::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CXboxParticleGlow::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CXboxParticleGlow::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CXboxParticleGlow::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CXboxParticleGlow::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CXboxParticleGlow::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CXboxParticleGlow::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CXboxParticleGlow::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CXboxParticleGlow::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CXboxParticleGlow::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CXboxParticleGlow::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CXboxParticleGlow::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }


		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleGlow::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Concatenate p_matrix with the emmission angle to create the direction.
		Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
		Mth::Vector screen_right	= Mth::CrossProduct( at, up );
		Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

		screen_right.Normalize();
		screen_up.Normalize();
	
		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v;

//		Mth::Vector		min, max;	// For dynamic bounding box calculation.

		// Obtain push buffer lock.
		DWORD *p_push; 
		DWORD dwords_per_particle	= 36 * m_num_segments;
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
		NxXbox::set_pixel_shader( PixelShader1 );
		
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		p_push = D3DDevice_BeginPush( dword_count + ( dword_count / 2047 ) + 16 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_TRIANGLELIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v			= mp_vertices;

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}

				float terp	= p_particle->m_time / p_particle->m_life;
				float w		= p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
				float h		= p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

				Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				Mth::Vector	ss_right, ss_up;
				Mth::Vector tmp0, tmp1, tmp2, tmp3;
	
				// Dynamic bounding box calculation.
//				if( lp == 0 )
//				{
//					min = pos;
//					max = pos;
//				}
//				else
//				{
//					if( pos[X] < min[X] ) min[X] = pos[X]; else if( pos[X] > max[X] ) max[X] = pos[X];
//					if( pos[Y] < min[Y] ) min[Y] = pos[Y]; else if( pos[Y] > max[Y] ) max[Y] = pos[Y];
//					if( pos[Z] < min[Z] ) min[Z] = pos[Z]; else if( pos[Z] > max[Z] ) max[Z] = pos[Z];
//				}

				ss_right	= screen_right * w;
				ss_up		= screen_up * h;
	
				Image::RGBA color[3];
				Image::RGBA *p_col0;
				Image::RGBA *p_col1;

				if ( m_mid_time >= 0.0f )
				{
					if ( terp < m_mid_time )
					{
						p_col0 = m_start_color;
						p_col1 = m_mid_color;

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = m_mid_color;
						p_col1 = m_end_color;

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = m_start_color;
					p_col1 = m_end_color;
				}

				// Swap red and blue here.
				for( int c = 0; c < 3; c++ )
				{
					Image::RGBA start	= *p_col0++;
					Image::RGBA end		= *p_col1++;

					color[c].b = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
					color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
					color[c].r = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
					color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
				}

				tmp0  = pos;
				tmp0 += ss_right * sinf( 0.0f ) * m_split;
				tmp0 += ss_up    * cosf( 0.0f ) * m_split;

				tmp2  = pos;
				tmp2 += ss_right * sinf( 0.0f );
				tmp2 += ss_up    * cosf( 0.0f );

				float radians_per_segment	= ( 2.0f * Mth::PI ) / (float)m_num_segments;

				for( int lp2 = 0; lp2 < m_num_segments; lp2++ )
				{
					tmp1  = pos;
					tmp1 += ss_right * sinf( radians_per_segment * ( lp2 + 1 )) * m_split;
					tmp1 += ss_up    * cosf( radians_per_segment * ( lp2 + 1 )) * m_split;

					tmp3  = pos;
					tmp3 += ss_right * sinf( radians_per_segment * ( lp2 + 1 ));
					tmp3 += ss_up    * cosf( radians_per_segment * ( lp2 + 1 ));

					// Triangle zero.
					p_push[0]	= *((DWORD*)&pos[X] );
					p_push[1]	= *((DWORD*)&pos[Y] );
					p_push[2]	= *((DWORD*)&pos[Z] );
					p_push[3]	= *((DWORD*)&color[0] );

					p_push[4]	= *((DWORD*)&tmp0[X] );
					p_push[5]	= *((DWORD*)&tmp0[Y] );
					p_push[6]	= *((DWORD*)&tmp0[Z] );
					p_push[7]	= *((DWORD*)&color[1] );
					
					p_push[8]	= *((DWORD*)&tmp1[X] );
					p_push[9]	= *((DWORD*)&tmp1[Y] );
					p_push[10]	= *((DWORD*)&tmp1[Z] );
					p_push[11]	= *((DWORD*)&color[1] );
				
					// Triangle one.
					p_push[12]	= *((DWORD*)&tmp0[X] );
					p_push[13]	= *((DWORD*)&tmp0[Y] );
					p_push[14]	= *((DWORD*)&tmp0[Z] );
					p_push[15]	= *((DWORD*)&color[1] );
					
					p_push[16]	= *((DWORD*)&tmp1[X] );
					p_push[17]	= *((DWORD*)&tmp1[Y] );
					p_push[18]	= *((DWORD*)&tmp1[Z] );
					p_push[19]	= *((DWORD*)&color[1] );

					p_push[20]	= *((DWORD*)&tmp2[X] );
					p_push[21]	= *((DWORD*)&tmp2[Y] );
					p_push[22]	= *((DWORD*)&tmp2[Z] );
					p_push[23]	= *((DWORD*)&color[2] );
					
					// Triangle two.
					p_push[24]	= *((DWORD*)&tmp1[X] );
					p_push[25]	= *((DWORD*)&tmp1[Y] );
					p_push[26]	= *((DWORD*)&tmp1[Z] );
					p_push[27]	= *((DWORD*)&color[1] );
					
					p_push[28]	= *((DWORD*)&tmp2[X] );
					p_push[29]	= *((DWORD*)&tmp2[Y] );
					p_push[30]	= *((DWORD*)&tmp2[Z] );
					p_push[31]	= *((DWORD*)&color[2] );

					p_push[32]	= *((DWORD*)&tmp3[X] );
					p_push[33]	= *((DWORD*)&tmp3[Y] );
					p_push[34]	= *((DWORD*)&tmp3[Z] );
					p_push[35]	= *((DWORD*)&color[2] );
					p_push		+= 36;
					
					tmp0 = tmp1;
					tmp2 = tmp3;
				}

				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );

		// Set the mesh bounding box and sphere.
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMin( min );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMax( max );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center = D3DXVECTOR3( min[X] + (( max[X] - min[X] ) * 0.5f ), min[Y] + (( max[Y] - min[Y] ) * 0.5f ), min[Z] + (( max[Z] - min[Z] ) * 0.5f ));
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius = 360.0f;

		// And the scene bounding sphere.
//		mp_engine_particle->mp_scene->m_sphere_center = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center;
//		mp_engine_particle->mp_scene->m_sphere_radius = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius;
	}
}

} // Nx





================================================
FILE: Code/Gfx/XBox/p_nxparticleglow.h
================================================
#ifndef	__GFX_P_NX_PARTICLEGlow_H__
#define	__GFX_P_NX_PARTICLEGlow_H__
    
#include "gfx/xbox/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CXboxParticleGlow : public CParticle
{
public:
							CXboxParticleGlow();
							CXboxParticleGlow( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split );
	virtual 				~CXboxParticleGlow();

	NxXbox::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	float*					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;

	NxXbox::sParticleSystem*	mp_engine_particle;
	int						m_num_segments;
	float					m_split;
	Image::RGBA				m_start_color[3];			// Start color for each corner.
	Image::RGBA				m_mid_color[3];				// Mid color for each corner.
	Image::RGBA				m_end_color[3];				// End color for each corner.
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/XBox/p_nxparticleglowribbontrail.cpp
================================================
#include 
#include "gfx/xbox/nx/render.h"

#include "gfx/xbox/p_nxparticleGlowRibbonTrail.h"

extern DWORD PixelShader1;

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CXboxParticleGlowRibbonTrail::CXboxParticleGlowRibbonTrail()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleGlowRibbonTrail::CXboxParticleGlowRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum		= checksum;
	m_max_particles	= max_particles;
	m_num_particles	= 0;
	m_mid_time		= -1.0f;
	m_num_segments	= num_segments;
	m_segment_step	= 360.0f / m_num_segments;
	m_history		= history;
	m_split			= split;

	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

	// Create the engine representation.
	mp_engine_particle = new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_GLOWRIBBONTRAIL, texture_checksum, blendmode_checksum, fix, num_segments, history );

	// Default color.
	m_start_color = new Image::RGBA[m_num_vertex_buffers + 3];
	m_mid_color = new Image::RGBA[m_num_vertex_buffers + 3];
	m_end_color = new Image::RGBA[m_num_vertex_buffers + 3];
	for ( int lp = 0; lp < ( m_num_vertex_buffers + 3 ); lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 128;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 128;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 128;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleGlowRibbonTrail::~CXboxParticleGlowRibbonTrail()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
	delete [] m_start_color;
	delete [] m_mid_color;
	delete [] m_end_color;
	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleGlowRibbonTrail::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleGlowRibbonTrail::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleGlowRibbonTrail::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxParticleGlowRibbonTrail::plat_get_num_particle_colors( void ) { return m_num_vertex_buffers + 3; }
int CXboxParticleGlowRibbonTrail::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CXboxParticleGlowRibbonTrail::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CXboxParticleGlowRibbonTrail::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CXboxParticleGlowRibbonTrail::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CXboxParticleGlowRibbonTrail::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value >> 1; }
void CXboxParticleGlowRibbonTrail::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CXboxParticleGlowRibbonTrail::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CXboxParticleGlowRibbonTrail::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CXboxParticleGlowRibbonTrail::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value >> 1; }
void CXboxParticleGlowRibbonTrail::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CXboxParticleGlowRibbonTrail::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CXboxParticleGlowRibbonTrail::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CXboxParticleGlowRibbonTrail::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value >> 1; }



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleGlowRibbonTrail::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v0, *p_v1;

		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Concatenate p_matrix with the emmission angle to create the direction.
		Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
		Mth::Vector screen_right	= Mth::CrossProduct( at, up );
		Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

		Image::RGBA color[3];
		Image::RGBA *p_col0;
		Image::RGBA *p_col1;
		
		// Obtain push buffer lock.
		DWORD *p_push; 
		DWORD dwords_per_particle	= ( 36 * m_num_segments ) + ( 24 * ( m_num_vertex_buffers - 1 ));
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
		NxXbox::set_pixel_shader( PixelShader1 );
		
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		p_push = D3DDevice_BeginPush( dword_count + 32 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_TRIANGLELIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v0		= mp_vertices[0];
		p_v1		= mp_vertices[(m_num_vertex_buffers - 1)];

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v0 += 3, p_v1 += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}
				
				// Calculate the interpolator ( 1.0f / particle_life ).
				float terp = p_particle->m_time * ReciprocalEstimateNR_ASM( p_particle->m_life );

				Mth::Vector	pos[2];
				p_v0 = &mp_vertices[0][lp*3];
				pos[0].Set( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
				p_v0 = &mp_vertices[1][lp*3];
				pos[1].Set( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
			
				Mth::Vector	part_vec = pos[1] - pos[0];
				Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
				perp_vec.Normalize();

				float w = p_particle->m_sw + (( p_particle->m_ew - p_particle->m_sw ) * terp );
				float h = p_particle->m_sh + (( p_particle->m_eh - p_particle->m_sh ) * terp );
		
				Mth::Vector tmp[4];

				if( m_mid_time >= 0.0f )
				{
					if( terp < m_mid_time )
					{
						p_col0 = &m_start_color[3];
						p_col1 = &m_mid_color[3];

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = &m_mid_color[3];
						p_col1 = &m_end_color[3];

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = &m_start_color[3];
					p_col1 = &m_end_color[3];
				}

				Image::RGBA start = *p_col0++;
				Image::RGBA end = *p_col1++;

				// Interpolate and swap red and blue here. Use fixed point math to avoid _ftol2 calls.
				int f_terp	= Ftoi_ASM( terp * 4096.0f );
				color[0].b	= ((((int)start.r ) * 4096 ) + (((int)end.r - (int)start.r ) * f_terp )) / 4096;
				color[0].g	= ((((int)start.g ) * 4096 ) + (((int)end.g - (int)start.g ) * f_terp )) / 4096;
				color[0].r	= ((((int)start.b ) * 4096 ) + (((int)end.b - (int)start.b ) * f_terp )) / 4096;
				color[0].a	= ((((int)start.a ) * 4096 ) + (((int)end.a - (int)start.a ) * f_terp )) / 4096;

				tmp[0]		= pos[0] + ( perp_vec * w * m_split );
				tmp[1]		= pos[0] - ( perp_vec * w * m_split );

				for( int c = 1; c < m_num_vertex_buffers; c++ )
				{
					start = *p_col0++;
					end = *p_col1++;

					// Interpolate and swap red and blue here. Use fixed point math to avoid _ftol2 calls.
					color[1].b	= ((((int)start.r ) * 4096 ) + (((int)end.r - (int)start.r ) * f_terp )) / 4096;
					color[1].g	= ((((int)start.g ) * 4096 ) + (((int)end.g - (int)start.g ) * f_terp )) / 4096;
					color[1].r	= ((((int)start.b ) * 4096 ) + (((int)end.b - (int)start.b ) * f_terp )) / 4096;
					color[1].a	= ((((int)start.a ) * 4096 ) + (((int)end.a - (int)start.a ) * f_terp )) / 4096;

					if ( c > 1 )
					{
						p_v0 = &mp_vertices[c][lp*3];
						pos[1].Set( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
						part_vec = pos[1] - pos[0];
						perp_vec = Mth::CrossProduct( part_vec, at );
						perp_vec.Normalize();
					}

					tmp[2]		= pos[1] + ( perp_vec * w * m_split );
					tmp[3]		= pos[1] - ( perp_vec * w * m_split );

					// First tri.
					p_push[0]	= *((DWORD*)&tmp[0][X] );
					p_push[1]	= *((DWORD*)&tmp[0][Y] );
					p_push[2]	= *((DWORD*)&tmp[0][Z] );
					p_push[3]	= *((DWORD*)&color[0] );
					p_push		+= 4;
						
					p_push[0]	= *((DWORD*)&tmp[1][X] );
					p_push[1]	= *((DWORD*)&tmp[1][Y] );
					p_push[2]	= *((DWORD*)&tmp[1][Z] );
					p_push[3]	= *((DWORD*)&color[0] );
					p_push		+= 4;
					
					p_push[0]	= *((DWORD*)&tmp[2][X] );
					p_push[1]	= *((DWORD*)&tmp[2][Y] );
					p_push[2]	= *((DWORD*)&tmp[2][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					// Second tri.
					p_push[0]	= *((DWORD*)&tmp[1][X] );
					p_push[1]	= *((DWORD*)&tmp[1][Y] );
					p_push[2]	= *((DWORD*)&tmp[1][Z] );
					p_push[3]	= *((DWORD*)&color[0] );
					p_push		+= 4;
						
					p_push[0]	= *((DWORD*)&tmp[3][X] );
					p_push[1]	= *((DWORD*)&tmp[3][Y] );
					p_push[2]	= *((DWORD*)&tmp[3][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;
						
					p_push[0]	= *((DWORD*)&tmp[2][X] );
					p_push[1]	= *((DWORD*)&tmp[2][Y] );
					p_push[2]	= *((DWORD*)&tmp[2][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					color[0] = color[1];
					pos[0] = pos[1];
					tmp[0] = tmp[2];
					tmp[1] = tmp[3];
				}

				// Now draw the glow.
				p_v0 = &mp_vertices[0][lp*3];
				pos[0].Set( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
				Mth::Vector	ss_right, ss_up;
	
				ss_right	= screen_right * w;
				ss_up		= screen_up * h;

				if( m_mid_time >= 0.0f )
				{
					if( terp < m_mid_time )
					{
						p_col0 = m_start_color;
						p_col1 = m_mid_color;

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = m_mid_color;
						p_col1 = m_end_color;
	
						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = m_start_color;
					p_col1 = m_end_color;
				}
		
				// Swap red and blue here.
				for( int c = 0; c < 3; c++ )
				{
					Image::RGBA start = *p_col0++;
					Image::RGBA end = *p_col1++;

					// Interpolate and swap red and blue here. Use fixed point math to avoid _ftol2 calls.
					color[c].b	= ((((int)start.r ) * 4096 ) + (((int)end.r - (int)start.r ) * f_terp )) / 4096;
					color[c].g	= ((((int)start.g ) * 4096 ) + (((int)end.g - (int)start.g ) * f_terp )) / 4096;
					color[c].r	= ((((int)start.b ) * 4096 ) + (((int)end.b - (int)start.b ) * f_terp )) / 4096;
					color[c].a	= ((((int)start.a ) * 4096 ) + (((int)end.a - (int)start.a ) * f_terp )) / 4096;
				}

				// We know that sin( 0 ) = 0, and cos( 0 ) = 1, so we can optimise the first iteration.
				tmp[0]  = pos[0];
				tmp[0] += ss_right * 0.0f * m_split;
				tmp[0] += ss_up    * 1.0f * m_split;

				tmp[2]  = pos[0];
				tmp[2] += ss_right * 0.0f;
				tmp[2] += ss_up    * 1.0f;

				for ( int lp2 = 0; lp2 < m_num_segments; lp2++ )
				{
					float rt	= sinf( Mth::DegToRad( m_segment_step * ( lp2 + 1 )));
					float up	= cosf( Mth::DegToRad( m_segment_step * ( lp2 + 1 )));

					tmp[1]  = pos[0];
					tmp[1] += ss_right * rt * m_split;
					tmp[1] += ss_up    * up * m_split;

					tmp[3]  = pos[0];
					tmp[3] += ss_right * rt;
					tmp[3] += ss_up    * up;

					// First tri.
					p_push[0]	= *((DWORD*)&pos[0][X] );
					p_push[1]	= *((DWORD*)&pos[0][Y] );
					p_push[2]	= *((DWORD*)&pos[0][Z] );
					p_push[3]	= *((DWORD*)&color[0] );
					p_push		+= 4;
					
					p_push[0]	= *((DWORD*)&tmp[0][X] );
					p_push[1]	= *((DWORD*)&tmp[0][Y] );
					p_push[2]	= *((DWORD*)&tmp[0][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;
					
					p_push[0]	= *((DWORD*)&tmp[1][X] );
					p_push[1]	= *((DWORD*)&tmp[1][Y] );
					p_push[2]	= *((DWORD*)&tmp[1][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					// Second tri.					
					p_push[0]	= *((DWORD*)&tmp[0][X] );
					p_push[1]	= *((DWORD*)&tmp[0][Y] );
					p_push[2]	= *((DWORD*)&tmp[0][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[1][X] );
					p_push[1]	= *((DWORD*)&tmp[1][Y] );
					p_push[2]	= *((DWORD*)&tmp[1][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[2][X] );
					p_push[1]	= *((DWORD*)&tmp[2][Y] );
					p_push[2]	= *((DWORD*)&tmp[2][Z] );
					p_push[3]	= *((DWORD*)&color[2] );
					p_push		+= 4;

					// Third tri.
					p_push[0]	= *((DWORD*)&tmp[1][X] );
					p_push[1]	= *((DWORD*)&tmp[1][Y] );
					p_push[2]	= *((DWORD*)&tmp[1][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[2][X] );
					p_push[1]	= *((DWORD*)&tmp[2][Y] );
					p_push[2]	= *((DWORD*)&tmp[2][Z] );
					p_push[3]	= *((DWORD*)&color[2] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[3][X] );
					p_push[1]	= *((DWORD*)&tmp[3][Y] );
					p_push[2]	= *((DWORD*)&tmp[3][Z] );
					p_push[3]	= *((DWORD*)&color[2] );
					p_push		+= 4;

					tmp[0] = tmp[1];
					tmp[2] = tmp[3];
				}
				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );
	}
}

} // Nx




================================================
FILE: Code/Gfx/XBox/p_nxparticleglowribbontrail.h
================================================
#ifndef	__GFX_P_NX_PARTICLEGlowRibbonTrail_H__
#define	__GFX_P_NX_PARTICLEGlowRibbonTrail_H__
    
#include "gfx/xbox/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CXboxParticleGlowRibbonTrail : public CParticle
{
public:
							CXboxParticleGlowRibbonTrail();
							CXboxParticleGlowRibbonTrail( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CXboxParticleGlowRibbonTrail();

	NxXbox::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
private:					// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
	NxXbox::sParticleSystem*	mp_engine_particle;

	int						m_num_segments;
	float					m_segment_step;				// 360.0f / m_num_segments;
	int						m_history;
	float					m_split;
	Image::RGBA*			m_start_color;				// Start color for each corner.
	Image::RGBA*			m_mid_color;				// Mid color for each corner.
	Image::RGBA*			m_end_color;				// End color for each corner.
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/XBox/p_nxparticleline.cpp
================================================
#include 
#include "gfx/xbox/nx/render.h"

#include "gfx/xbox/p_nxparticleLine.h"

extern DWORD PixelShader1;

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleLine::CXboxParticleLine()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleLine::CXboxParticleLine( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split )
{
	m_checksum		= checksum;
	m_max_particles	= max_particles;
	m_num_particles	= 0;

	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices[0] = new float[max_particles * 3];
	mp_vertices[1] = new float[max_particles * 3];		// 2nd buffer to keep history.

	// Create the engine representation.
	mp_engine_particle	= new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_LINE, texture_checksum, blendmode_checksum, fix );

	// Default color.
	for ( int lp = 0; lp < 2; lp++ )
	{
		m_start_color[lp].r = m_start_color[lp].g = m_start_color[lp].b = 128;
		m_start_color[lp].a = 255;
		m_mid_color[lp].r = m_mid_color[lp].g = m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 255;
		m_end_color[lp].r = m_end_color[lp].g = m_end_color[lp].b = 128;
		m_end_color[lp].a = 255;
	}

	m_mid_time = -1.0f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleLine::~CXboxParticleLine()
{
	delete [] mp_particle_array;
	delete [] mp_vertices[0];
	delete [] mp_vertices[1];

	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleLine::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleLine::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleLine::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxParticleLine::plat_get_num_particle_colors( void ) { return 2; }
int CXboxParticleLine::plat_get_num_vertex_lists( void ) { return 2; }
void CXboxParticleLine::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CXboxParticleLine::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CXboxParticleLine::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CXboxParticleLine::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CXboxParticleLine::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CXboxParticleLine::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CXboxParticleLine::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CXboxParticleLine::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CXboxParticleLine::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CXboxParticleLine::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CXboxParticleLine::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CXboxParticleLine::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleLine::plat_render( void )
{
	if( m_num_particles > 0 )
	{
		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v0, *p_v1;
//		Mth::Vector		min, max;	// For dynamic bounding box calculation.
	
		// Obtain push buffer lock.
		DWORD *p_push; 
		DWORD dwords_per_particle	= 8;
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
		NxXbox::set_pixel_shader( PixelShader1 );
		
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		p_push = D3DDevice_BeginPush( dword_count + 32 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_LINELIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v0		= mp_vertices[0];
		p_v1		= mp_vertices[1];

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v0 += 3, p_v1 += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}

				float terp = p_particle->m_time / p_particle->m_life;

				// Todo: Move hook to matrix/emitter code to cut down on per particle calculation.
				Mth::Vector	pos0( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
				Mth::Vector	pos1( p_v1[0] + m_pos[X], p_v1[1] + m_pos[Y], p_v1[2] + m_pos[Z] );
	
				// Dynamic bounding box calculation.
//				if( lp == 0 )
//				{
//					min = pos0;
//					max = pos0;
//				}
//				else
//				{
//					if( pos0[X] < min[X] ) min[X] = pos0[X]; else if( pos0[X] > max[X] ) max[X] = pos0[X];
//					if( pos0[Y] < min[Y] ) min[Y] = pos0[Y]; else if( pos0[Y] > max[Y] ) max[Y] = pos0[Y];
//					if( pos0[Z] < min[Z] ) min[Z] = pos0[Z]; else if( pos0[Z] > max[Z] ) max[Z] = pos0[Z];
//				}

				Image::RGBA color[2];
				Image::RGBA *p_col0[2];
				Image::RGBA *p_col1[2];
		
				if( m_mid_time >= 0.0f )
				{
					if( terp < m_mid_time )
					{
						p_col0[0] = &m_start_color[0];
						p_col1[0] = &m_mid_color[0];
						p_col0[1] = &m_start_color[1];
						p_col1[1] = &m_mid_color[1];

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0[0] = &m_mid_color[0];
						p_col1[0] = &m_end_color[0];
						p_col0[1] = &m_mid_color[1];
						p_col1[1] = &m_end_color[1];

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0[0] = &m_start_color[0];
					p_col1[0] = &m_end_color[0];
					p_col0[1] = &m_start_color[1];
					p_col1[1] = &m_end_color[1];
				}

				// Swap red and blue here.
				for( int c = 0; c < 2; ++c )
				{
					Image::RGBA start	= *( p_col0[c] );
					Image::RGBA end		= *( p_col1[c] );
					color[c].b = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
					color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
					color[c].r = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
					color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
				}
		
				p_push[0]	= *((DWORD*)&pos0[X] );
				p_push[1]	= *((DWORD*)&pos0[Y] );
				p_push[2]	= *((DWORD*)&pos0[Z] );
				p_push[3]	= *((DWORD*)&color[0] );
				p_push		+= 4;

				p_push[0]	= *((DWORD*)&pos1[X] );
				p_push[1]	= *((DWORD*)&pos1[Y] );
				p_push[2]	= *((DWORD*)&pos1[Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push		+= 4;

				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );

		// Set the mesh bounding box and sphere.
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMin( min );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMax( max );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center = D3DXVECTOR3( min[X] + (( max[X] - min[X] ) * 0.5f ), min[Y] + (( max[Y] - min[Y] ) * 0.5f ), min[Z] + (( max[Z] - min[Z] ) * 0.5f ));
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius = 360.0f;

		// And the scene bounding sphere.
//		mp_engine_particle->mp_scene->m_sphere_center = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center;
//		mp_engine_particle->mp_scene->m_sphere_radius = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius;
	}
}

} // Nx




================================================
FILE: Code/Gfx/XBox/p_nxparticleline.h
================================================
#ifndef	__GFX_P_NX_PARTICLELine_H__
#define	__GFX_P_NX_PARTICLELine_H__
    
#include "gfx/xbox/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CXboxParticleLine : public CParticle
{
public:
								CXboxParticleLine();
								CXboxParticleLine( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split );
	virtual						~CXboxParticleLine();

	NxXbox::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }
	private:					// It's all private, as it is machine specific
	void						plat_render( void );
	void						plat_get_position( int entry, int list, float * x, float * y, float * z );
	void						plat_set_position( int entry, int list, float x, float y, float z );
	void						plat_add_position( int entry, int list, float x, float y, float z );
	int							plat_get_num_vertex_lists( void );
	int							plat_get_num_particle_colors( void );

	void						plat_set_sr( int entry, uint8 value );
	void						plat_set_sg( int entry, uint8 value );
	void						plat_set_sb( int entry, uint8 value );
	void						plat_set_sa( int entry, uint8 value );
	void						plat_set_mr( int entry, uint8 value );
	void						plat_set_mg( int entry, uint8 value );
	void						plat_set_mb( int entry, uint8 value );
	void						plat_set_ma( int entry, uint8 value );
	void						plat_set_er( int entry, uint8 value );
	void						plat_set_eg( int entry, uint8 value );
	void						plat_set_eb( int entry, uint8 value );
	void						plat_set_ea( int entry, uint8 value );
		
	float*						mp_vertices[2];
	uint32*						mp_colors;
	uint64						m_blend;
	NxXbox::sParticleSystem*	mp_engine_particle;

	Image::RGBA					m_start_color[2];			// Start color for each corner.
	Image::RGBA					m_mid_color[2];				// Mid color for each corner.
	Image::RGBA					m_end_color[2];				// End color for each corner.
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/XBox/p_nxparticleribbon.cpp
================================================
#include 
#include "gfx/xbox/nx/render.h"

#include "gfx/xbox/p_nxparticleRibbon.h"

extern DWORD PixelShader1;

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleRibbon::CXboxParticleRibbon()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleRibbon::CXboxParticleRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum		= checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;
	m_mid_time		= -1.0f;

	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices = new float*[history + 1];
	for( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

	// Create the engine representation.
	mp_engine_particle = new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_RIBBON, texture_checksum, blendmode_checksum, fix, num_segments );

	// Default color.
	for ( int lp = 0; lp < 2; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 255;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 255;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 255;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleRibbon::~CXboxParticleRibbon()
{
	delete [] mp_particle_array;
	for ( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleRibbon::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleRibbon::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleRibbon::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxParticleRibbon::plat_get_num_particle_colors( void ) { return 2; }
int CXboxParticleRibbon::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CXboxParticleRibbon::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CXboxParticleRibbon::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CXboxParticleRibbon::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CXboxParticleRibbon::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CXboxParticleRibbon::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CXboxParticleRibbon::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CXboxParticleRibbon::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CXboxParticleRibbon::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CXboxParticleRibbon::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CXboxParticleRibbon::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CXboxParticleRibbon::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CXboxParticleRibbon::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleRibbon::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v0;
		float			*p_v1;
//		Mth::Vector		min, max;	// For dynamic bounding box calculation.

		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );

		Image::RGBA color[2];
		Image::RGBA *p_col0;
		Image::RGBA *p_col1;
		
		// Obtain push buffer lock.
		DWORD *p_push; 
		DWORD dwords_per_particle	= 16;
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
		NxXbox::set_pixel_shader( PixelShader1 );
		
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		p_push = D3DDevice_BeginPush( dword_count + 32 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_QUADLIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v0		= mp_vertices[0];
		p_v1		= mp_vertices[(m_num_vertex_buffers - 1)];

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v0 += 3, p_v1 += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}

				float terp = p_particle->m_time / p_particle->m_life;

				Mth::Vector	pos0( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
				Mth::Vector	pos1( p_v1[0] + m_pos[X], p_v1[1] + m_pos[Y], p_v1[2] + m_pos[Z] );

				// Dynamic bounding box calculation.
//				if( lp == 0 )
//				{
//					min = pos0;
//					max = pos0;
//				}
//				else
//				{
//					if( pos0[X] < min[X] ) min[X] = pos0[X]; else if( pos0[X] > max[X] ) max[X] = pos0[X];
//					if( pos0[Y] < min[Y] ) min[Y] = pos0[Y]; else if( pos0[Y] > max[Y] ) max[Y] = pos0[Y];
//					if( pos0[Z] < min[Z] ) min[Z] = pos0[Z]; else if( pos0[Z] > max[Z] ) max[Z] = pos0[Z];
//				}

				Mth::Vector	part_vec = pos1 - pos0;
				Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
				perp_vec.Normalize();

				float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		
				Mth::Vector tmp[4];
				tmp[0]		= pos0 + ( perp_vec * w );
				tmp[1]		= pos0 - ( perp_vec * w );
				tmp[2]		= pos1 - ( perp_vec * w );
				tmp[3]		= pos1 + ( perp_vec * w );
	
				if ( m_mid_time >= 0.0f )
				{
					if ( terp < m_mid_time )
					{
						p_col0 = m_start_color;
						p_col1 = m_mid_color;

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = m_mid_color;
						p_col1 = m_end_color;

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = m_start_color;
					p_col1 = m_end_color;
				}

				for ( int c = 0; c < 2; c++ )
				{
					Image::RGBA start = *p_col0++;
					Image::RGBA end = *p_col1++;

					// Swap red and blue here.
					color[c].b = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
					color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
					color[c].r = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
					color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
				}

				for( int v = 0; v < 4; ++v )
				{
					p_push[0]	= *((DWORD*)&tmp[v][X] );
					p_push[1]	= *((DWORD*)&tmp[v][Y] );
					p_push[2]	= *((DWORD*)&tmp[v][Z] );
					p_push[3]	= *((DWORD*)&color[v / 2] );
					p_push		+= 4;
				}
				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );

		// Set the mesh bounding box and sphere.
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMin( min );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMax( max );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center = D3DXVECTOR3( min[X] + (( max[X] - min[X] ) * 0.5f ), min[Y] + (( max[Y] - min[Y] ) * 0.5f ), min[Z] + (( max[Z] - min[Z] ) * 0.5f ));
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius = 360.0f;

		// And the scene bounding sphere.
//		mp_engine_particle->mp_scene->m_sphere_center = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center;
//		mp_engine_particle->mp_scene->m_sphere_radius = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius;
	}
}

} // Nx




================================================
FILE: Code/Gfx/XBox/p_nxparticleribbon.h
================================================
#ifndef	__GFX_P_NX_PARTICLERibbon_H__
#define	__GFX_P_NX_PARTICLERibbon_H__
    
#include "gfx/xbox/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CXboxParticleRibbon : public CParticle
{
	public:
							CXboxParticleRibbon();
							CXboxParticleRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CXboxParticleRibbon();

	NxXbox::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }

	private:				// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
	NxXbox::sParticleSystem*	mp_engine_particle;

	Image::RGBA				m_start_color[2];			// Start color for each corner.
	Image::RGBA				m_mid_color[2];				// Mid color for each corner.
	Image::RGBA				m_end_color[2];				// End color for each corner.
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/XBox/p_nxparticlesmooth.cpp
================================================
#include 
#include "gfx/xbox/nx/render.h"
#include 
#include 
#include "gfx/xbox/nx/mesh.h"
#include "gfx/xbox/p_nxparticleSmooth.h"

extern DWORD PixelShader0;

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleSmooth::CXboxParticleSmooth()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleSmooth::CXboxParticleSmooth( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split )
{
	m_checksum		= checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;
	m_mid_time		= -1.0f;

	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

	// Create the engine representation.
	mp_engine_particle = new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_SMOOTH, texture_checksum, blendmode_checksum, fix );

	// Default color.
	for ( int lp = 0; lp < 2; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 255;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 255;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 255;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleSmooth::~CXboxParticleSmooth()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmooth::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmooth::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmooth::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxParticleSmooth::plat_get_num_particle_colors( void ) { return 2; }
int CXboxParticleSmooth::plat_get_num_vertex_lists( void ) { return 1; }
void CXboxParticleSmooth::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CXboxParticleSmooth::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CXboxParticleSmooth::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CXboxParticleSmooth::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CXboxParticleSmooth::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CXboxParticleSmooth::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CXboxParticleSmooth::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CXboxParticleSmooth::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CXboxParticleSmooth::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CXboxParticleSmooth::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CXboxParticleSmooth::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CXboxParticleSmooth::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }


		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmooth::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Concatenate p_matrix with the emmission angle to create the direction.
		Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
		Mth::Vector screen_right	= Mth::CrossProduct( at, up );
		Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

		screen_right.Normalize();
		screen_up.Normalize();
	
		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v;
//		Mth::Vector		min, max;	// For dynamic bounding box calculation.

		// Obtain push buffer lock.
		DWORD *p_push; 
		DWORD dwords_per_particle	= 72;
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));
		NxXbox::set_pixel_shader( PixelShader0 );
		
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		p_push = D3DDevice_BeginPush( dword_count + 32 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_TRIANGLELIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v			= mp_vertices;

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}

				float terp	= p_particle->m_time / p_particle->m_life;
				float w		= p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
				float h		= p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

				Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				Mth::Vector	ss_right, ss_up;	//, ss_pos;
				Mth::Vector tmp[4];
	
				// Dynamic bounding box calculation.
//				if( lp == 0 )
//				{
//					min = pos;
//					max = pos;
//				}
//				else
//				{
//					if( pos[X] < min[X] ) min[X] = pos[X]; else if( pos[X] > max[X] ) max[X] = pos[X];
//					if( pos[Y] < min[Y] ) min[Y] = pos[Y]; else if( pos[Y] > max[Y] ) max[Y] = pos[Y];
//					if( pos[Z] < min[Z] ) min[Z] = pos[Z]; else if( pos[Z] > max[Z] ) max[Z] = pos[Z];
//				}

				ss_right	= screen_right * w;
				ss_up		= screen_up * h;
		
				tmp[0]		= pos - ss_right + ss_up;
				tmp[1]		= pos + ss_right + ss_up;		
				tmp[2]		= pos + ss_right - ss_up;		
				tmp[3]		= pos - ss_right - ss_up;		
	
				Image::RGBA color[2];
				Image::RGBA *p_col0;
				Image::RGBA *p_col1;
		
				if ( m_mid_time >= 0.0f )
				{
					if ( terp < m_mid_time )
					{
						p_col0 = m_start_color;
						p_col1 = m_mid_color;

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = m_mid_color;
						p_col1 = m_end_color;

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = m_start_color;
					p_col1 = m_end_color;
				}

				for ( int c = 0; c < 2; c++ )
				{
					Image::RGBA start = *p_col0++;
					Image::RGBA end = *p_col1++;

					color[c].r = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
					color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
					color[c].b = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
					color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
				}

				// First triangle.
				p_push[0]	= *((DWORD*)&pos[X] );
				p_push[1]	= *((DWORD*)&pos[Y] );
				p_push[2]	= *((DWORD*)&pos[Z] );
				p_push[3]	= *((DWORD*)&color[0] );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x00000000UL;
				p_push		+= 6;

				p_push[0]	= *((DWORD*)&tmp[0][X] );
				p_push[1]	= *((DWORD*)&tmp[0][Y] );
				p_push[2]	= *((DWORD*)&tmp[0][Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x00000000UL;
				p_push		+= 6;

				p_push[0]	= *((DWORD*)&tmp[1][X] );
				p_push[1]	= *((DWORD*)&tmp[1][Y] );
				p_push[2]	= *((DWORD*)&tmp[1][Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push[4]	= 0x3F800000UL;
				p_push[5]	= 0x00000000UL;
				p_push		+= 6;
					
				// Second triangle.
				p_push[0]	= *((DWORD*)&pos[X] );
				p_push[1]	= *((DWORD*)&pos[Y] );
				p_push[2]	= *((DWORD*)&pos[Z] );
				p_push[3]	= *((DWORD*)&color[0] );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x00000000UL;
				p_push		+= 6;

				p_push[0]	= *((DWORD*)&tmp[1][X] );
				p_push[1]	= *((DWORD*)&tmp[1][Y] );
				p_push[2]	= *((DWORD*)&tmp[1][Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push[4]	= 0x3F800000UL;
				p_push[5]	= 0x00000000UL;
				p_push		+= 6;

				p_push[0]	= *((DWORD*)&tmp[2][X] );
				p_push[1]	= *((DWORD*)&tmp[2][Y] );
				p_push[2]	= *((DWORD*)&tmp[2][Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push[4]	= 0x3F800000UL;
				p_push[5]	= 0x3F800000UL;
				p_push		+= 6;
					
				// Third triangle.
				p_push[0]	= *((DWORD*)&pos[X] );
				p_push[1]	= *((DWORD*)&pos[Y] );
				p_push[2]	= *((DWORD*)&pos[Z] );
				p_push[3]	= *((DWORD*)&color[0] );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x00000000UL;
				p_push		+= 6;

				p_push[0]	= *((DWORD*)&tmp[2][X] );
				p_push[1]	= *((DWORD*)&tmp[2][Y] );
				p_push[2]	= *((DWORD*)&tmp[2][Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push[4]	= 0x3F800000UL;
				p_push[5]	= 0x3F800000UL;
				p_push		+= 6;

				p_push[0]	= *((DWORD*)&tmp[3][X] );
				p_push[1]	= *((DWORD*)&tmp[3][Y] );
				p_push[2]	= *((DWORD*)&tmp[3][Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x3F800000UL;
				p_push		+= 6;
					
				// Fourth triangle.
				p_push[0]	= *((DWORD*)&pos[X] );
				p_push[1]	= *((DWORD*)&pos[Y] );
				p_push[2]	= *((DWORD*)&pos[Z] );
				p_push[3]	= *((DWORD*)&color[0] );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x00000000UL;
				p_push		+= 6;

				p_push[0]	= *((DWORD*)&tmp[3][X] );
				p_push[1]	= *((DWORD*)&tmp[3][Y] );
				p_push[2]	= *((DWORD*)&tmp[3][Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x3F800000UL;
				p_push		+= 6;

				p_push[0]	= *((DWORD*)&tmp[0][X] );
				p_push[1]	= *((DWORD*)&tmp[0][Y] );
				p_push[2]	= *((DWORD*)&tmp[0][Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push[4]	= 0x00000000UL;
				p_push[5]	= 0x00000000UL;
				p_push		+= 6;

				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );

		// Set the mesh bounding box and sphere.
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMin( min );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMax( max );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center = D3DXVECTOR3( min[X] + (( max[X] - min[X] ) * 0.5f ), min[Y] + (( max[Y] - min[Y] ) * 0.5f ), min[Z] + (( max[Z] - min[Z] ) * 0.5f ));
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius = 360.0f;

		// And the scene bounding sphere.
//		mp_engine_particle->mp_scene->m_sphere_center = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center;
//		mp_engine_particle->mp_scene->m_sphere_radius = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius;
	}
}

} // Nx





================================================
FILE: Code/Gfx/XBox/p_nxparticlesmooth.h
================================================
#ifndef	__GFX_P_NX_PARTICLESmooth_H__
#define	__GFX_P_NX_PARTICLESmooth_H__
    
#include "gfx/xbox/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CXboxParticleSmooth : public CParticle
{
	public:
							CXboxParticleSmooth();
							CXboxParticleSmooth( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split );
	virtual 				~CXboxParticleSmooth();

	NxXbox::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }

	private:				// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	float*					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
	NxXbox::sParticleSystem*	mp_engine_particle;

	Image::RGBA				m_start_color[2];			// Start color for each corner.
	Image::RGBA				m_mid_color[2];				// Mid color for each corner.
	Image::RGBA				m_end_color[2];				// End color for each corner.
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/XBox/p_nxparticlesmoothribbon.cpp
================================================
#include 
#include "gfx/xbox/nx/render.h"

#include "gfx/xbox/p_nxparticleSmoothRibbon.h"

extern DWORD PixelShader1;

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleSmoothRibbon::CXboxParticleSmoothRibbon()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleSmoothRibbon::CXboxParticleSmoothRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history )
{
	m_checksum		= checksum;
	m_max_particles = max_particles;
	m_num_particles = 0;
	m_mid_time		= -1.0f;

	mp_particle_array = new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices = new float*[( history + 1)];
	for ( int lp = 0; lp < ( history + 1 ); lp++ )
	{
		mp_vertices[lp] = new float[max_particles * 3];
	}
	m_num_vertex_buffers = history + 1;

	// Create the engine representation.
	mp_engine_particle = new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_SMOOTHRIBBON, texture_checksum, blendmode_checksum, fix );

	// Default color.
	for ( int lp = 0; lp < 4; lp++ )
	{
		m_start_color[lp].r = 128;
		m_start_color[lp].g = 128;
		m_start_color[lp].b = 128;
		m_start_color[lp].a = 255;
		m_mid_color[lp].r = 128;
		m_mid_color[lp].g = 128;
		m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 255;
		m_end_color[lp].r = 128;
		m_end_color[lp].g = 128;
		m_end_color[lp].b = 128;
		m_end_color[lp].a = 255;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleSmoothRibbon::~CXboxParticleSmoothRibbon()
{
	delete [] mp_particle_array;
	for( int lp = 0; lp < m_num_vertex_buffers; lp++ )
	{
		delete [] mp_vertices[lp];
	}
	delete [] mp_vertices;
	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmoothRibbon::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[list][entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmoothRibbon::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmoothRibbon::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[list][entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxParticleSmoothRibbon::plat_get_num_particle_colors( void ) { return 4; }
int CXboxParticleSmoothRibbon::plat_get_num_vertex_lists( void ) { return m_num_vertex_buffers; }
void CXboxParticleSmoothRibbon::plat_set_sr( int entry, uint8 value ) { m_start_color[entry].r = value; }
void CXboxParticleSmoothRibbon::plat_set_sg( int entry, uint8 value ) { m_start_color[entry].g = value; }
void CXboxParticleSmoothRibbon::plat_set_sb( int entry, uint8 value ) { m_start_color[entry].b = value; }
void CXboxParticleSmoothRibbon::plat_set_sa( int entry, uint8 value ) { m_start_color[entry].a = value; }
void CXboxParticleSmoothRibbon::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CXboxParticleSmoothRibbon::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CXboxParticleSmoothRibbon::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CXboxParticleSmoothRibbon::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CXboxParticleSmoothRibbon::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CXboxParticleSmoothRibbon::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CXboxParticleSmoothRibbon::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CXboxParticleSmoothRibbon::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmoothRibbon::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v0;
		float			*p_v1;
		Mth::Vector		min, max;	// For dynamic bounding box calculation.

		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );

		Image::RGBA color[4];
		Image::RGBA *p_col0;
		Image::RGBA *p_col1;
		
		// Obtain push buffer lock.
		DWORD *p_push; 
		DWORD dwords_per_particle	= 32;
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
		NxXbox::set_pixel_shader( PixelShader1 );
		
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		p_push = D3DDevice_BeginPush( dword_count + 32 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_QUADLIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v0		= mp_vertices[0];
		p_v1		= mp_vertices[(m_num_vertex_buffers - 1)];

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v0 += 3, p_v1 += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}

				float terp = p_particle->m_time / p_particle->m_life;

				Mth::Vector	pos0( p_v0[0] + m_pos[X], p_v0[1] + m_pos[Y], p_v0[2] + m_pos[Z] );
				Mth::Vector	pos1( p_v1[0] + m_pos[X], p_v1[1] + m_pos[Y], p_v1[2] + m_pos[Z] );

				// Dynamic bounding box calculation.
//				if( lp == 0 )
//				{
//					min = pos0;
//					max = pos0;
//				}
//				else
//				{
//					if( pos0[X] < min[X] ) min[X] = pos0[X]; else if( pos0[X] > max[X] ) max[X] = pos0[X];
//					if( pos0[Y] < min[Y] ) min[Y] = pos0[Y]; else if( pos0[Y] > max[Y] ) max[Y] = pos0[Y];
//					if( pos0[Z] < min[Z] ) min[Z] = pos0[Z]; else if( pos0[Z] > max[Z] ) max[Z] = pos0[Z];
//				}

				Mth::Vector	part_vec = pos1 - pos0;
				Mth::Vector perp_vec = Mth::CrossProduct( part_vec, at );
				perp_vec.Normalize();

				float w = p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
		
				Mth::Vector tmp[4];
				tmp[0]		= pos0 + ( perp_vec * w );
				tmp[1]		= pos0 - ( perp_vec * w );
				tmp[2]		= pos1 - ( perp_vec * w );
				tmp[3]		= pos1 + ( perp_vec * w );
	
				if( m_mid_time >= 0.0f )
				{
					if( terp < m_mid_time )
					{
						p_col0 = m_start_color;
						p_col1 = m_mid_color;

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = m_mid_color;
						p_col1 = m_end_color;

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = m_start_color;
					p_col1 = m_end_color;
				}

				for( int c = 0; c < 4; c++ )
				{
					Image::RGBA start = *p_col0++;
					Image::RGBA end = *p_col1++;

					// Swap red and blue here.
					color[c].b = start.r + (uint8)(( ((float)( end.r - start.r )) * terp ));
					color[c].g = start.g + (uint8)(( ((float)( end.g - start.g )) * terp ));
					color[c].r = start.b + (uint8)(( ((float)( end.b - start.b )) * terp ));
					color[c].a = start.a + (uint8)(( ((float)( end.a - start.a )) * terp ));
				}

				// First quad.
				p_push[0]	= *((DWORD*)&pos0[X] );
				p_push[1]	= *((DWORD*)&pos0[Y] );
				p_push[2]	= *((DWORD*)&pos0[Z] );
				p_push[3]	= *((DWORD*)&color[0] );
				p_push		+= 4;

				p_push[0]	= *((DWORD*)&pos1[X] );
				p_push[1]	= *((DWORD*)&pos1[Y] );
				p_push[2]	= *((DWORD*)&pos1[Z] );
				p_push[3]	= *((DWORD*)&color[3] );
				p_push		+= 4;
				
				p_push[0]	= *((DWORD*)&tmp[3][X] );
				p_push[1]	= *((DWORD*)&tmp[3][Y] );
				p_push[2]	= *((DWORD*)&tmp[3][Z] );
				p_push[3]	= *((DWORD*)&color[2] );
				p_push		+= 4;
				
				p_push[0]	= *((DWORD*)&tmp[0][X] );
				p_push[1]	= *((DWORD*)&tmp[0][Y] );
				p_push[2]	= *((DWORD*)&tmp[0][Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push		+= 4;
				
				// Second quad.
				p_push[0]	= *((DWORD*)&pos0[X] );
				p_push[1]	= *((DWORD*)&pos0[Y] );
				p_push[2]	= *((DWORD*)&pos0[Z] );
				p_push[3]	= *((DWORD*)&color[0] );
				p_push		+= 4;

				p_push[0]	= *((DWORD*)&pos1[X] );
				p_push[1]	= *((DWORD*)&pos1[Y] );
				p_push[2]	= *((DWORD*)&pos1[Z] );
				p_push[3]	= *((DWORD*)&color[3] );
				p_push		+= 4;
				
				p_push[0]	= *((DWORD*)&tmp[2][X] );
				p_push[1]	= *((DWORD*)&tmp[2][Y] );
				p_push[2]	= *((DWORD*)&tmp[2][Z] );
				p_push[3]	= *((DWORD*)&color[2] );
				p_push		+= 4;
				
				p_push[0]	= *((DWORD*)&tmp[1][X] );
				p_push[1]	= *((DWORD*)&tmp[1][Y] );
				p_push[2]	= *((DWORD*)&tmp[1][Z] );
				p_push[3]	= *((DWORD*)&color[1] );
				p_push		+= 4;

				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );

		// Set the mesh bounding box and sphere.
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMin( min );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMax( max );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center = D3DXVECTOR3( min[X] + (( max[X] - min[X] ) * 0.5f ), min[Y] + (( max[Y] - min[Y] ) * 0.5f ), min[Z] + (( max[Z] - min[Z] ) * 0.5f ));
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius = 360.0f;

		// And the scene bounding sphere.
//		mp_engine_particle->mp_scene->m_sphere_center = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center;
//		mp_engine_particle->mp_scene->m_sphere_radius = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius;
	}
}

} // Nx




================================================
FILE: Code/Gfx/XBox/p_nxparticlesmoothribbon.h
================================================
#ifndef	__GFX_P_NX_PARTICLESmoothRibbon_H__
#define	__GFX_P_NX_PARTICLESmoothRibbon_H__
    
#include "gfx/xbox/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/////////////////////////////////////////////////////////////////////////////////////
//
// Here's a machine specific implementation of the CParticle
    
class CXboxParticleSmoothRibbon : public CParticle
{
public:
							CXboxParticleSmoothRibbon();
							CXboxParticleSmoothRibbon( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
	virtual 				~CXboxParticleSmoothRibbon();

	NxXbox::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }

	private:				// It's all private, as it is machine specific
	void					plat_render( void );
	void					plat_get_position( int entry, int list, float * x, float * y, float * z );
	void					plat_set_position( int entry, int list, float x, float y, float z );
	void					plat_add_position( int entry, int list, float x, float y, float z );
	int						plat_get_num_vertex_lists( void );
	int						plat_get_num_particle_colors( void );

	void					plat_set_sr( int entry, uint8 value );
	void					plat_set_sg( int entry, uint8 value );
	void					plat_set_sb( int entry, uint8 value );
	void					plat_set_sa( int entry, uint8 value );
	void					plat_set_mr( int entry, uint8 value );
	void					plat_set_mg( int entry, uint8 value );
	void					plat_set_mb( int entry, uint8 value );
	void					plat_set_ma( int entry, uint8 value );
	void					plat_set_er( int entry, uint8 value );
	void					plat_set_eg( int entry, uint8 value );
	void					plat_set_eb( int entry, uint8 value );
	void					plat_set_ea( int entry, uint8 value );
		
	int						m_num_vertex_buffers;
	float**					mp_vertices;
	uint32*					mp_colors;
	uint64					m_blend;
	NxXbox::sParticleSystem*	mp_engine_particle;

	Image::RGBA				m_start_color[4];			// Start color for each corner.
	Image::RGBA				m_mid_color[4];				// Mid color for each corner.
	Image::RGBA				m_end_color[4];				// End color for each corner.
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 








================================================
FILE: Code/Gfx/XBox/p_nxparticlesmoothstar.cpp
================================================
#include 
#include 

#include "gfx/xbox/p_nxparticleSmoothStar.h"

extern DWORD PixelShader1;

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleSmoothStar::CXboxParticleSmoothStar()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleSmoothStar::CXboxParticleSmoothStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split )
{
	m_checksum			= checksum;
	m_max_particles		= max_particles;
	m_num_particles		= 0;
	m_num_segments		= num_segments;
	m_split				= split;

	mp_particle_array	= new CParticleEntry[max_particles];

	// Allocate vertex buffer.
	mp_vertices = new float[max_particles * 3];

	// Create the engine representation.
	mp_engine_particle = new NxXbox::sParticleSystem( max_particles, NxXbox::PARTICLE_TYPE_SMOOTHSTAR, texture_checksum, blendmode_checksum, fix, num_segments );

	// Default color.
	for ( int lp = 0; lp < 3; lp++ )
	{
		m_SmoothStart_color[lp].r = m_SmoothStart_color[lp].g = m_SmoothStart_color[lp].b = 128;
		m_SmoothStart_color[lp].a = 255;
		m_mid_color[lp].r = m_mid_color[lp].g = m_mid_color[lp].b = 128;
		m_mid_color[lp].a = 255;
		m_end_color[lp].r = m_end_color[lp].g = m_end_color[lp].b = 128;
		m_end_color[lp].a = 255;
	}

	m_mid_time = -1.0f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxParticleSmoothStar::~CXboxParticleSmoothStar()
{
	delete [] mp_particle_array;
	delete [] mp_vertices;
	delete mp_engine_particle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmoothStar::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	float* p_v = &mp_vertices[entry*3];
	*x = p_v[0];
	*y = p_v[1];
	*z = p_v[2];
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmoothStar::plat_set_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] = x;
	p_v[1] = y;
	p_v[2] = z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmoothStar::plat_add_position( int entry, int list, float x, float y, float z )
{
	float* p_v = &mp_vertices[entry*3];
	p_v[0] += x;
	p_v[1] += y;
	p_v[2] += z;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CXboxParticleSmoothStar::plat_get_num_particle_colors( void ) { return 3; }
int CXboxParticleSmoothStar::plat_get_num_vertex_lists( void ) { return 1; }
void CXboxParticleSmoothStar::plat_set_sr( int entry, uint8 value ) { m_SmoothStart_color[entry].r = value; }
void CXboxParticleSmoothStar::plat_set_sg( int entry, uint8 value ) { m_SmoothStart_color[entry].g = value; }
void CXboxParticleSmoothStar::plat_set_sb( int entry, uint8 value ) { m_SmoothStart_color[entry].b = value; }
void CXboxParticleSmoothStar::plat_set_sa( int entry, uint8 value ) { m_SmoothStart_color[entry].a = value; }
void CXboxParticleSmoothStar::plat_set_mr( int entry, uint8 value ) { m_mid_color[entry].r = value; }
void CXboxParticleSmoothStar::plat_set_mg( int entry, uint8 value ) { m_mid_color[entry].g = value; }
void CXboxParticleSmoothStar::plat_set_mb( int entry, uint8 value ) { m_mid_color[entry].b = value; }
void CXboxParticleSmoothStar::plat_set_ma( int entry, uint8 value ) { m_mid_color[entry].a = value; }
void CXboxParticleSmoothStar::plat_set_er( int entry, uint8 value ) { m_end_color[entry].r = value; }
void CXboxParticleSmoothStar::plat_set_eg( int entry, uint8 value ) { m_end_color[entry].g = value; }
void CXboxParticleSmoothStar::plat_set_eb( int entry, uint8 value ) { m_end_color[entry].b = value; }
void CXboxParticleSmoothStar::plat_set_ea( int entry, uint8 value ) { m_end_color[entry].a = value; }


		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxParticleSmoothStar::plat_render( void )
{
	// Draw the particles.
	if( m_num_particles > 0 )
	{
		// Used to figure the right and up vectors for creating screen-aligned particle quads.
		D3DXMATRIX *p_matrix = (D3DXMATRIX*)&NxXbox::EngineGlobals.view_matrix;

		// Concatenate p_matrix with the emmission angle to create the direction.
		Mth::Vector up( 0.0f, 1.0f, 0.0f, 0.0f );

		// Get the 'right' vector as the cross product of camera 'at and world 'up'.
		Mth::Vector at( p_matrix->m[0][2], p_matrix->m[1][2], p_matrix->m[2][2] );
		Mth::Vector screen_right	= Mth::CrossProduct( at, up );
		Mth::Vector screen_up		= Mth::CrossProduct( screen_right, at );

		screen_right.Normalize();
		screen_up.Normalize();
	
		int				lp;
		CParticleEntry	*p_particle;
		float			*p_v;
//		Mth::Vector		min, max;	// For dynamic bounding box calculation.

		// Obtain push buffer lock.
		DWORD *p_push; 
		DWORD dwords_per_particle	= 32 * m_num_segments;
		DWORD dword_count			= dwords_per_particle * m_num_particles;

		// Submit particle material.
		mp_engine_particle->mp_material->Submit();
		
		// Set up correct vertex and pixel shader.
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
		NxXbox::set_pixel_shader( PixelShader1 );
		
		// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
		p_push = D3DDevice_BeginPush( dword_count + 32 );

		// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
		// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
		// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
		p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1]	= D3DPT_QUADLIST;
		p_push		+= 2;

		// Set up loop variables here, since we be potentially enetering the loop more than once.
		lp			= 0;
		p_particle	= mp_particle_array;
		p_v			= mp_vertices;

		while( dword_count > 0 )
		{
			int dwords_written = 0;

			// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
			// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
			p_push[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, ( dword_count > 2047 ) ? ((int)( 2047 / dwords_per_particle )) * dwords_per_particle: dword_count );
			++p_push;
		
			for( ; lp < m_num_particles; lp++, p_particle++, p_v += 3 )
			{
				// Check to see if writing another particle will take us over the edge.
				if(( dwords_written + dwords_per_particle ) > 2047 )
				{
					break;
				}

				float terp	= p_particle->m_time / p_particle->m_life;
				float w		= p_particle->m_sw + ( ( p_particle->m_ew - p_particle->m_sw ) * terp );
				float h		= p_particle->m_sh + ( ( p_particle->m_eh - p_particle->m_sh ) * terp );

				Mth::Vector	pos( p_v[0] + m_pos[X], p_v[1] + m_pos[Y], p_v[2] + m_pos[Z] );
				Mth::Vector	ss_right, ss_up;	//, ss_pos;
				Mth::Vector tmp[4];
	
				// Dynamic bounding box calculation.
//				if( lp == 0 )
//				{
//					min = pos;
//					max = pos;
//				}
//				else
//				{
//					if( pos[X] < min[X] ) min[X] = pos[X]; else if( pos[X] > max[X] ) max[X] = pos[X];
//					if( pos[Y] < min[Y] ) min[Y] = pos[Y]; else if( pos[Y] > max[Y] ) max[Y] = pos[Y];
//					if( pos[Z] < min[Z] ) min[Z] = pos[Z]; else if( pos[Z] > max[Z] ) max[Z] = pos[Z];
//				}

				ss_right	= screen_right * w;
				ss_up		= screen_up * h;
	
				Image::RGBA color[3];
				Image::RGBA *p_col0;
				Image::RGBA *p_col1;

				if( m_mid_time >= 0.0f )
				{
					if ( terp < m_mid_time )
					{
						p_col0 = m_SmoothStart_color;
						p_col1 = m_mid_color;

						// Adjust interpolation for this half of the color blend.
						terp = terp / m_mid_time;
					}
					else
					{
						p_col0 = m_mid_color;
						p_col1 = m_end_color;

						// Adjust interpolation for this half of the color blend.
						terp = ( terp - m_mid_time ) / ( 1.0f - m_mid_time );
					}
				}
				else
				{
					// No mid color specified.
					p_col0 = m_SmoothStart_color;
					p_col1 = m_end_color;
				}

				// Swap red and blue here during interpolation.
				for ( int c = 0; c < 3; c++ )
				{
					Image::RGBA SmoothStart = *p_col0++;
					Image::RGBA end = *p_col1++;

					color[c].b = SmoothStart.r + (uint8)(( ((float)( end.r - SmoothStart.r )) * terp ));
					color[c].g = SmoothStart.g + (uint8)(( ((float)( end.g - SmoothStart.g )) * terp ));
					color[c].r = SmoothStart.b + (uint8)(( ((float)( end.b - SmoothStart.b )) * terp ));
					color[c].a = SmoothStart.a + (uint8)(( ((float)( end.a - SmoothStart.a )) * terp ));
				}

				tmp[1]  = pos;
				tmp[1] += ss_right * sinf( Mth::DegToRad( 0.0f ) ) * m_split;
				tmp[1] += ss_up    * cosf( Mth::DegToRad( 0.0f ) ) * m_split;

				for( int lp2 = 0; lp2 < m_num_segments; lp2++ )
				{
					tmp[0]  = pos;
					tmp[0] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) ) * m_split;
					tmp[0] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) ) * m_split;
			
					tmp[3]  = pos;
					tmp[3] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 2 ) ) ) ) * m_split;
					tmp[3] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 2 ) ) ) ) * m_split;
			
					tmp[2]  = pos;
					tmp[2] += ss_right * sinf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );
					tmp[2] += ss_up    * cosf( Mth::DegToRad( ( ( 360.0f / ((float)m_num_segments*2) ) * ( ( lp2 * 2 ) + 1 ) ) ) );

					// First quad.
					p_push[0]	= *((DWORD*)&tmp[0][X] );
					p_push[1]	= *((DWORD*)&tmp[0][Y] );
					p_push[2]	= *((DWORD*)&tmp[0][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&pos[X] );
					p_push[1]	= *((DWORD*)&pos[Y] );
					p_push[2]	= *((DWORD*)&pos[Z] );
					p_push[3]	= *((DWORD*)&color[0] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[3][X] );
					p_push[1]	= *((DWORD*)&tmp[3][Y] );
					p_push[2]	= *((DWORD*)&tmp[3][Z] );
					p_push[3]	= *((DWORD*)&color[2] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[2][X] );
					p_push[1]	= *((DWORD*)&tmp[2][Y] );
					p_push[2]	= *((DWORD*)&tmp[2][Z] );
					p_push[3]	= *((DWORD*)&color[2] );
					p_push		+= 4;

					// Second quad.
					p_push[0]	= *((DWORD*)&tmp[0][X] );
					p_push[1]	= *((DWORD*)&tmp[0][Y] );
					p_push[2]	= *((DWORD*)&tmp[0][Z] );
					p_push[3]	= *((DWORD*)&color[1] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&pos[X] );
					p_push[1]	= *((DWORD*)&pos[Y] );
					p_push[2]	= *((DWORD*)&pos[Z] );
					p_push[3]	= *((DWORD*)&color[0] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[1][X] );
					p_push[1]	= *((DWORD*)&tmp[1][Y] );
					p_push[2]	= *((DWORD*)&tmp[1][Z] );
					p_push[3]	= *((DWORD*)&color[2] );
					p_push		+= 4;

					p_push[0]	= *((DWORD*)&tmp[2][X] );
					p_push[1]	= *((DWORD*)&tmp[2][Y] );
					p_push[2]	= *((DWORD*)&tmp[2][Z] );
					p_push[3]	= *((DWORD*)&color[2] );
					p_push		+= 4;

					tmp[1] = tmp[3];
				}
				dwords_written	+= dwords_per_particle;
				dword_count		-= dwords_per_particle;
			}
		}

		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );

		// Set the mesh bounding box and sphere.
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMin( min );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_bbox.SetMax( max );
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center = D3DXVECTOR3( min[X] + (( max[X] - min[X] ) * 0.5f ), min[Y] + (( max[Y] - min[Y] ) * 0.5f ), min[Z] + (( max[Z] - min[Z] ) * 0.5f ));
//		mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius = 360.0f;

		// And the scene bounding sphere.
//		mp_engine_particle->mp_scene->m_sphere_center = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_center;
//		mp_engine_particle->mp_scene->m_sphere_radius = mp_engine_particle->mp_scene->m_semitransparent_meshes[0]->m_sphere_radius;
	}
}

} // Nx





================================================
FILE: Code/Gfx/XBox/p_nxparticlesmoothstar.h
================================================
#ifndef	__GFX_P_NX_PARTICLESmoothStar_H__
#define	__GFX_P_NX_PARTICLESmoothStar_H__
    
#include "gfx/xbox/p_nxparticle.h"
                   
namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CXboxParticleSmoothStar : public CParticle
{
	public:
								CXboxParticleSmoothStar();
								CXboxParticleSmoothStar( uint32 checksum, int max_particles, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split );
	virtual 					~CXboxParticleSmoothStar();

	NxXbox::sParticleSystem*	GetEngineParticle( void )	{ return mp_engine_particle; }

	private: // It's all private, as it is machine specific
	void						plat_render( void );
	void						plat_get_position( int entry, int list, float * x, float * y, float * z );
	void						plat_set_position( int entry, int list, float x, float y, float z );
	void						plat_add_position( int entry, int list, float x, float y, float z );
	int							plat_get_num_vertex_lists( void );
	int							plat_get_num_particle_colors( void );

	void						plat_set_sr( int entry, uint8 value );
	void						plat_set_sg( int entry, uint8 value );
	void						plat_set_sb( int entry, uint8 value );
	void						plat_set_sa( int entry, uint8 value );
	void						plat_set_mr( int entry, uint8 value );
	void						plat_set_mg( int entry, uint8 value );
	void						plat_set_mb( int entry, uint8 value );
	void						plat_set_ma( int entry, uint8 value );
	void						plat_set_er( int entry, uint8 value );
	void						plat_set_eg( int entry, uint8 value );
	void						plat_set_eb( int entry, uint8 value );
	void						plat_set_ea( int entry, uint8 value );
		
	float*						mp_vertices;
	uint32*						mp_colors;
	uint64						m_blend;

	NxXbox::sParticleSystem*	mp_engine_particle;
	int							m_num_segments;
	float						m_split;
	Image::RGBA					m_SmoothStart_color[3];			// SmoothStart color for each corner.
	Image::RGBA					m_mid_color[3];				// Mid color for each corner.
	Image::RGBA					m_end_color[3];				// End color for each corner.
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif 









================================================
FILE: Code/Gfx/XBox/p_nxscene.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.cpp

#include 	"gfx/xbox/p_NxScene.h"
#include 	"gfx/xbox/p_NxSector.h"
#include 	"gfx/xbox/p_NxGeom.h"

namespace Nx
{


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxScene::CXboxScene( int sector_table_size ) : CScene( sector_table_size )
{
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxScene::DestroySectorMeshes( void )
{
	// Iterate through the list of sectors, removing each one in turn, and requesting the engine to
	// remove the meshes.
	mp_sector_table->IterateStart();
	CSector* pSector = mp_sector_table->IterateNext();
	while( pSector )
	{
		// Access platform dependent data.
		CXboxSector* pXboxSector = static_cast(pSector);

		// Remove this mesh array from the engine.
//		pXboxSector->DestroyMeshArray();

		pSector = mp_sector_table->IterateNext();
	}
}



/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CScene

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxScene::plat_post_load()
{
	// Now turn the temporary mesh lists into mesh arrays.
	mp_sector_table->IterateStart();
	CSector* pSector = mp_sector_table->IterateNext();
	while( pSector )
	{
		CXboxGeom *p_xbox_geom = static_cast(pSector->GetGeom());

		p_xbox_geom->CreateMeshArray();

		// First time through we just want to count the meshes,
		p_xbox_geom->RegisterMeshArray( true );

		pSector = mp_sector_table->IterateNext();
	}

	// Now we have counted all the meshes, tell the engine to create the arrays to hold them.
	GetEngineScene()->CreateMeshArrays();
	
	// Now go through and actually add the meshes.
	mp_sector_table->IterateStart();
	pSector = mp_sector_table->IterateNext();
	while( pSector )
	{
		// Access platform dependent data.
		CXboxGeom *p_xbox_geom = static_cast(pSector->GetGeom());

		p_xbox_geom->RegisterMeshArray( false );

		pSector = mp_sector_table->IterateNext();
	}

	// Now all meshes are registered, tell the engine to sort them.
	GetEngineScene()->SortMeshes();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxScene::plat_load_textures(const char* p_name)
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxScene::plat_load_collision(const char* p_name)
{
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxScene::plat_unload_add_scene( void )
{
	// Not sure what this is supposed to do, but added for now to remove annoying stub output.
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Create an empty sector
CSector	*CXboxScene::plat_create_sector()
{
	CXboxSector *p_xbox_sector	= new CXboxSector();
	return p_xbox_sector;
}



} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/XBox/p_nxscene.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.h

#ifndef	__GFX_P_NX_SCENE_H__
#define	__GFX_P_NX_SCENE_H__

#include "Gfx/Nx.h"
#include "Gfx/xbox/nx/scene.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CScene
class	CXboxScene : public CScene
{
public:

								CXboxScene( int sector_table_size = 10 );
	NxXbox::sScene *			GetEngineScene() const						{ return mp_engine_scene; }
	void						SetEngineScene( NxXbox::sScene *p_scene )	{ mp_engine_scene = p_scene; }
	void						DestroySectorMeshes( void );

private:		// It's all private, as it is machine specific
	virtual void				plat_post_load();	
	virtual bool				plat_load_textures( const char *p_name );	// load textures 
	virtual bool				plat_load_collision( const char *p_name );	// load collision data
	virtual bool				plat_unload_add_scene( void );
	virtual	CSector	*			plat_create_sector();	 					// empty sector


	NxXbox::sScene				*mp_engine_scene;

};

} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/XBox/p_nxsector.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxSector.cpp

#include	

#include 	"gfx/xbox/p_NxSector.h"
#include 	"gfx/xbox/p_NxGeom.h"
#include 	"gfx/NxMiscFX.h"
#include 	"gfx/xbox/nx/grass.h"
#include 	"gfx/xbox/nx/billboard.h"

namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

//CXboxSector::CXboxSector() : mp_init_mesh_list( NULL ), m_mesh_array( NULL ), m_visible( 0xDEADBEEF )
CXboxSector::CXboxSector()
{
	m_pos_offset.Set( 0.0f, 0.0f, 0.0f );
}




#define MemoryRead( dst, size, num, src )	CopyMemory(( dst ), ( src ), (( num ) * ( size )));	\
											( src ) += (( num ) * ( size ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxSector::LoadFromMemory( void **pp_mem )
{
	Dbg_Assert( mp_geom );

	uint8		*p_data	= (uint8*)( *pp_mem );
	CXboxGeom	*p_geom = static_cast( mp_geom );
	
	// Read sector checksum.
	uint32 sector_checksum;
	MemoryRead( §or_checksum, sizeof( uint32 ), 1, p_data );
	SetChecksum( sector_checksum );

	// Read bone index.
	int bone_idx;
	MemoryRead( &bone_idx, sizeof( int ), 1, p_data );

	// Read sector flags.
	MemoryRead( &m_flags, sizeof( int ), 1, p_data );

	// Read number of meshes.
	MemoryRead( &p_geom->m_num_mesh, sizeof( uint ), 1, p_data );

	// Read bounding box.
	float bbox[6];
	MemoryRead( &bbox[0], sizeof( float ), 6, p_data );
	Mth::Vector	inf( bbox[0], bbox[1], bbox[2] );
	Mth::Vector	sup( bbox[3], bbox[4], bbox[5] );
	p_geom->m_bbox.Set( inf, sup );
		
	// Read bounding sphere.
	float bsphere[4];
	MemoryRead( &bsphere[0], sizeof( float ), 4, p_data );
		
	// Read billboard data if present.
	uint32		billboard_type;
	Mth::Vector	billboard_origin;
	Mth::Vector billboard_pivot_pos;
	Mth::Vector billboard_pivot_axis;
	if( m_flags & 0x00800000UL )
	{
		MemoryRead( &billboard_type,			sizeof( uint32 ), 1, p_data );
		MemoryRead( &billboard_origin[X],		sizeof( float ) * 3, 1, p_data );
		MemoryRead( &billboard_pivot_pos[X],	sizeof( float ) * 3, 1, p_data );
		MemoryRead( &billboard_pivot_axis[X],	sizeof( float ) * 3, 1, p_data );

		billboard_origin[Z]		= -billboard_origin[Z];
		billboard_pivot_pos[Z]	= -billboard_pivot_pos[Z];
	}

	// Read num vertices.
	int num_vertices;
	MemoryRead( &num_vertices, sizeof( int ), 1, p_data );
	
	// Read vertex data stride.
	int vertex_data_stride;
	MemoryRead( &vertex_data_stride, sizeof( int ), 1, p_data );

	// We want all the temporary buffer allocations to come off of the top down heap.
	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
	
	// Grab a buffer for the raw vertex data position stream, and read it.
	float* p_vertex_positions = new float[num_vertices * 3];
	MemoryRead( p_vertex_positions, sizeof( float ) * 3, num_vertices, p_data );
	
	// Grab a buffer for the raw vertex data normal stream (if present), and read it.
	float* p_vertex_normals = ( m_flags & 0x04 ) ? new float[num_vertices * 3] : NULL;
	if( p_vertex_normals )
	{
		MemoryRead( p_vertex_normals, sizeof( float ) * 3, num_vertices, p_data );
	}

	// Grab a buffer for the raw vertex data weights stream (if present), and read it.
	uint32* p_vertex_weights = ( m_flags & 0x10 ) ? new uint32[num_vertices] : NULL;
	if( p_vertex_weights )
	{
		MemoryRead( p_vertex_weights, sizeof( uint32 ), num_vertices, p_data );
	}
	
	// Grab a buffer for the raw vertex data bone indices stream (if present), and read it.
	uint16* p_vertex_bone_indices = ( m_flags & 0x10 ) ? new uint16[num_vertices * 4] : NULL;
	if( p_vertex_bone_indices )
	{
		MemoryRead( p_vertex_bone_indices, sizeof( uint16 ) * 4, num_vertices, p_data );
	}

	// Grab a buffer for the raw vertex texture coordinate stream (if present), and read it.
	int		num_tc_sets			= 0;
	float*	p_vertex_tex_coords	= NULL;
	if( m_flags & 0x01 )
	{
		MemoryRead( &num_tc_sets, sizeof( int ), 1, p_data );
		
		if( num_tc_sets > 0 )
		{
			p_vertex_tex_coords = new float[num_vertices * 2 * num_tc_sets];
			MemoryRead( p_vertex_tex_coords, sizeof( float ) * 2 * num_tc_sets, num_vertices, p_data );
		}
	}
	
	// Grab a buffer for the raw vertex colors stream (if present), and read it.
	DWORD* p_vertex_colors = ( m_flags & 0x02 ) ? new DWORD[num_vertices] : NULL;
	if( p_vertex_colors )
	{
		MemoryRead( p_vertex_colors, sizeof( DWORD ), num_vertices, p_data );
	}

	// Grab a buffer for the vertex color wibble stream (if present), and read it.
	char* p_vc_wibble_indices = ( m_flags & 0x800 ) ? new char[num_vertices] : NULL;
	if( p_vc_wibble_indices )
	{
		MemoryRead( p_vc_wibble_indices, sizeof( char ), num_vertices, p_data );
	}
	
	// Remove TopDownHeap context.
	Mem::Manager::sHandle().PopContext();

	// Preprocess verts that require cutscene scaling.
	NxXbox::ApplyMeshScaling( p_vertex_positions, num_vertices );

	for( uint m = 0; m < p_geom->m_num_mesh; ++m )
	{
		unsigned long	material_checksum;
		unsigned int	flags, num_lod_index_levels;

		NxXbox::sMesh*	p_mesh = new NxXbox::sMesh;

		// Read bounding sphere and box data.
		float		rad;
		Mth::Vector inf, sup, cen;
		MemoryRead( &cen[X], sizeof( float ), 3, p_data );
		MemoryRead( &rad, sizeof( float ), 1, p_data );
		MemoryRead( &inf[X], sizeof( float ), 3, p_data );
		MemoryRead( &sup[X], sizeof( float ), 3, p_data );

		// Read and deal with flags, including skater shadow flag.
		MemoryRead( &flags, sizeof( uint32 ), 1, p_data );
		if( flags & 0x400 )
		{
			p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_NO_SKATER_SHADOW;
		}
		if( flags & NxXbox::sMesh::MESH_FLAG_UNLIT )
		{
			p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_UNLIT;
		}

		// The material checksum for this mesh.
		MemoryRead( &material_checksum,	sizeof( unsigned long ), 1, p_data );

		// How many levels of LOD indices? Should be at least 1!
		MemoryRead( &num_lod_index_levels, sizeof( unsigned int ), 1, p_data );

		// Can have up to 8 levels of LOD indices.
		uint16*	p_indices[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
		int		num_indices[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

		for( unsigned int lod_level = 0; lod_level < num_lod_index_levels; ++lod_level )
		{
			MemoryRead( &num_indices[lod_level], sizeof( int ), 1, p_data );
		
			// Again, we want all the temporary buffer allocations to come off of the top down heap.
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
			p_indices[lod_level] = new uint16[num_indices[lod_level]];
			Mem::Manager::sHandle().PopContext();

			MemoryRead( p_indices[lod_level], sizeof( uint16 ), num_indices[lod_level], p_data );
		}

		// Set the load order of this mesh.
		p_mesh->m_load_order = m;

		// Set up the mesh.
		p_mesh->Initialize(	num_vertices,
							p_vertex_positions,
							p_vertex_normals,
							p_vertex_tex_coords,
							num_tc_sets,
							p_vertex_colors,
							num_lod_index_levels,
							num_indices,
							p_indices,
							material_checksum,
							p_geom->mp_scene->GetEngineScene(),
							p_vertex_bone_indices,
							p_vertex_weights,
							( flags & 0x800 ) ? p_vc_wibble_indices : NULL );
		
		// Set the bounding data (sphere and box) for the mesh.
		p_mesh->SetBoundingData( cen, rad, inf, sup );

		// Add the bounding data to the scene.
		p_geom->mp_scene->GetEngineScene()->m_bbox.AddPoint( inf );
		p_geom->mp_scene->GetEngineScene()->m_bbox.AddPoint( sup );

		// Set up as a billboard if required.
		if( m_flags & 0x00800000UL )
		{
			p_mesh->SetBillboardData( billboard_type, billboard_pivot_pos, billboard_pivot_axis );
			NxXbox::BillboardManager.AddEntry( p_mesh );
		}

		// Flag the mesh as being a shadow volume if applicable.
		if( m_flags & 0x200000 )
		{
			p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_SHADOW_VOLUME;
		}

		// Add the mesh to the attached CXboxGeom.
		p_geom->AddMesh( p_mesh );

		// Set the mesh bone index (mostly not applicable).
		p_mesh->SetBoneIndex( bone_idx );
	
		// Test code - if the mesh is mapped with a grass texture, add grass meshes.
		AddGrass( p_geom, p_mesh );
		
		// Done with the raw index data.
		for( int lod_level = 0; lod_level < 8; ++lod_level )
		{
			delete[] p_indices[lod_level];
		}
	}

	// Recount the number of meshes in case any have been added.
	p_geom->m_num_mesh = p_geom->mp_init_mesh_list->CountItems();
	
	// Done with the raw vertex data.
	delete[] p_vc_wibble_indices;
	delete[] p_vertex_colors;
	delete[] p_vertex_tex_coords;
	delete[] p_vertex_bone_indices;
	delete[] p_vertex_weights;
	delete[] p_vertex_normals;
	delete[] p_vertex_positions;
	
	// Set the data pointer to the new position on return.
	*pp_mem = p_data;

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxSector::LoadFromFile( void* p_file )
{
	Dbg_Assert( mp_geom );

	CXboxGeom *p_geom = static_cast( mp_geom );
	
	// Read sector checksum.
	uint32 sector_checksum;
	File::Read( §or_checksum, sizeof( uint32 ), 1, p_file );
	SetChecksum( sector_checksum );

	// Read bone index.
	int bone_idx;
	File::Read( &bone_idx, sizeof( int ), 1, p_file );

	// Read sector flags.
	File::Read( &m_flags, sizeof( int ), 1, p_file );

	// Read number of meshes.
	File::Read( &p_geom->m_num_mesh, sizeof( uint ), 1, p_file );

	// Read bounding box.
	float bbox[6];
	File::Read( &bbox[0], sizeof( float ), 6, p_file );
	Mth::Vector	inf( bbox[0], bbox[1], bbox[2] );
	Mth::Vector	sup( bbox[3], bbox[4], bbox[5] );
	p_geom->m_bbox.Set( inf, sup );
		
	// Read bounding sphere.
	float bsphere[4];
	File::Read( &bsphere[0], sizeof( float ), 4, p_file );
		
	// Read billboard data if present.
	uint32		billboard_type;
	Mth::Vector	billboard_origin;
	Mth::Vector billboard_pivot_pos;
	Mth::Vector billboard_pivot_axis;
	if( m_flags & 0x00800000UL )
	{
		File::Read( &billboard_type,			sizeof( uint32 ), 1, p_file );
		File::Read( &billboard_origin[X],		sizeof( float ) * 3, 1, p_file );
		File::Read( &billboard_pivot_pos[X],	sizeof( float ) * 3, 1, p_file );
		File::Read( &billboard_pivot_axis[X],	sizeof( float ) * 3, 1, p_file );

		billboard_origin[Z]		= -billboard_origin[Z];
		billboard_origin[W]		= 0.0f;

		billboard_pivot_pos[Z]	= -billboard_pivot_pos[Z];
		billboard_pivot_pos[W]	= 0.0f;

		billboard_pivot_axis[W]	= 0.0f;
	}

	// Read num vertices.
	int num_vertices;
	File::Read( &num_vertices, sizeof( int ), 1, p_file );
	
	// Read vertex data stride.
	int vertex_data_stride;
	File::Read( &vertex_data_stride, sizeof( int ), 1, p_file );

	// We want all the temporary buffer allocations to come off of the top down heap.
	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
	
	// Grab a buffer for the raw vertex data position stream, and read it.
	float* p_vertex_positions = new float[num_vertices * 3];
	File::Read( p_vertex_positions, sizeof( float ) * 3, num_vertices, p_file );
	
	// Grab a buffer for the raw vertex data normal stream (if present), and read it.
	float* p_vertex_normals = ( m_flags & 0x04 ) ? new float[num_vertices * 3] : NULL;
	if( p_vertex_normals )
	{
		File::Read( p_vertex_normals, sizeof( float ) * 3, num_vertices, p_file );
	}

	// Grab a buffer for the raw vertex data weights stream (if present), and read it.
	uint32* p_vertex_weights = ( m_flags & 0x10 ) ? new uint32[num_vertices] : NULL;
	if( p_vertex_weights )
	{
		File::Read( p_vertex_weights, sizeof( uint32 ), num_vertices, p_file );
	}
	
	// Grab a buffer for the raw vertex data bone indices stream (if present), and read it.
	uint16* p_vertex_bone_indices = ( m_flags & 0x10 ) ? new uint16[num_vertices * 4] : NULL;
	if( p_vertex_bone_indices )
	{
		File::Read( p_vertex_bone_indices, sizeof( uint16 ) * 4, num_vertices, p_file );
	}

	// Grab a buffer for the raw vertex texture coordinate stream (if present), and read it.
	int		num_tc_sets			= 0;
	float*	p_vertex_tex_coords	= NULL;
	if( m_flags & 0x01 )
	{
		File::Read( &num_tc_sets, sizeof( int ), 1, p_file );
		
		if( num_tc_sets > 0 )
		{
			p_vertex_tex_coords = new float[num_vertices * 2 * num_tc_sets];
			File::Read( p_vertex_tex_coords, sizeof( float ) * 2 * num_tc_sets, num_vertices, p_file );
		}
	}
	
	// Grab a buffer for the raw vertex colors stream (if present), and read it.
	DWORD* p_vertex_colors = ( m_flags & 0x02 ) ? new DWORD[num_vertices] : NULL;
	if( p_vertex_colors )
	{
		File::Read( p_vertex_colors, sizeof( DWORD ), num_vertices, p_file );
	}

	// Grab a buffer for the vertex color wibble stream (if present), and read it.
	char* p_vc_wibble_indices = ( m_flags & 0x800 ) ? new char[num_vertices] : NULL;
	if( p_vc_wibble_indices )
	{
		File::Read( p_vc_wibble_indices, sizeof( char ), num_vertices, p_file );
	}
	
	// Remove TopDownHeap context.
	Mem::Manager::sHandle().PopContext();

	for( uint m = 0; m < p_geom->m_num_mesh; ++m )
	{
		unsigned long	material_checksum;
		unsigned int	flags, num_lod_index_levels;

		NxXbox::sMesh*	p_mesh = new NxXbox::sMesh;

		// Read bounding sphere and box data.
		float		rad;
		Mth::Vector cen;
		File::Read( &cen[X], sizeof( float ), 3, p_file );
		File::Read( &rad, sizeof( float ), 1, p_file );
		File::Read( &inf[X], sizeof( float ), 3, p_file );
		File::Read( &sup[X], sizeof( float ), 3, p_file );

		// Read and deal with flags, including skater shadow flag.
		File::Read( &flags, sizeof( uint32 ), 1, p_file );
		if( flags & 0x400 )
		{
			p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_NO_SKATER_SHADOW;
		}
		if( flags & NxXbox::sMesh::MESH_FLAG_UNLIT )
		{
			p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_UNLIT;
		}

		// The material checksum for this mesh.
		File::Read( &material_checksum,	sizeof( unsigned long ), 1, p_file );

		// How many levels of LOD indices? Should be at least 1!
		File::Read( &num_lod_index_levels, sizeof( unsigned int ), 1, p_file );

		// Can have up to 8 levels of LOD indices.
		uint16*	p_indices[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
		int		num_indices[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

		for( unsigned int lod_level = 0; lod_level < num_lod_index_levels; ++lod_level )
		{
			File::Read( &num_indices[lod_level], sizeof( int ), 1, p_file );
		
			// Again, we want all the temporary buffer allocations to come off of the top down heap.
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
			p_indices[lod_level] = new uint16[num_indices[lod_level]];
			Mem::Manager::sHandle().PopContext();

			File::Read( p_indices[lod_level], sizeof( uint16 ), num_indices[lod_level], p_file );
		}

		// Set the load order of this mesh.
		p_mesh->m_load_order = m;

		// Set up the mesh.
		p_mesh->Initialize(	num_vertices,
							p_vertex_positions,
							( m_flags & 0x00800000UL ) ? NULL : p_vertex_normals,		// No normals allowed for billboards.
							p_vertex_tex_coords,
							num_tc_sets,
							p_vertex_colors,
							num_lod_index_levels,
							num_indices,
							p_indices,
							material_checksum,
							p_geom->mp_scene->GetEngineScene(),
							p_vertex_bone_indices,
							p_vertex_weights,
							( flags & 0x800 ) ? p_vc_wibble_indices : NULL );
		
		// Set the bounding data (sphere and box) for the mesh.
		p_mesh->SetBoundingData( cen, rad, inf, sup );

		// Add the bounding data to the scene.
		p_geom->mp_scene->GetEngineScene()->m_bbox.AddPoint( inf );
		p_geom->mp_scene->GetEngineScene()->m_bbox.AddPoint( sup );

		// Set up as a billboard if required.
		if( m_flags & 0x00800000UL )
		{
			p_mesh->SetBillboardData( billboard_type, billboard_pivot_pos, billboard_pivot_axis );
			NxXbox::BillboardManager.AddEntry( p_mesh );
		}

		// Flag the mesh as being a shadow volume if applicable.
		if( m_flags & 0x200000 )
		{
			p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_SHADOW_VOLUME;
		}

		// Add the mesh to the attached CXboxGeom.
		p_geom->AddMesh( p_mesh );

		// Set the mesh bone index (mostly not applicable).
		p_mesh->SetBoneIndex( bone_idx );
	
		// Test code - if the mesh is mapped with a grass texture, add grass meshes.
		AddGrass( p_geom, p_mesh );
		
		// Done with the raw index data.
		for( int lod_level = 0; lod_level < 8; ++lod_level )
		{
			delete[] p_indices[lod_level];
		}
	}

	// Recount the number of meshes in case any have been added.
	p_geom->m_num_mesh = p_geom->mp_init_mesh_list->CountItems();
	

	// Test code for creating imposters.
#	if 0
	if( p_vertex_weights == NULL )
	{
		char *p_ok_sectors[] = {	"NJ_Houses_North_09",
									"NJ_Houses_North_10" };
/*
char *p_ok_sectors[] = { "CP_telephonepole43",
								"CP_telephonepole51",
								"CP_telephonepole52",
								"NJ_TelePole_Node03",
								"NJ_TelePole_Node02",
								"NJ_TelePole_Node04",
								"NJ_Telepole_00",
								"NJ_Telepole_01",
								"NJ_Telepole_02",
								"NJ_Telepole_03",
								"NJ_Telepole_04",
								"NJ_Telepole_05",
								"NJ_Telepole_06",
								"NJ_Telepole_07",
								"NJ_Telepole_08",
								"NJ_Telepole_09",
								"NJ_Telepole_12",
								"NJ_Telepole_13",
								"NJ_Telepole_14",
								"NJ_Telepole_15",
								"NJ_Telepole_16",
								"NJ_Telepole_17",
								"NJ_Telepole_18",
								"NJ_Telepole_19",
								"NJ_Telepole_20",
								"NJ_Telepole_21",
								"NJ_Telepole_22",
								"NJ_Telepole_23",
								"NJ_Telepole_24",
								"NJ_Telepole_25",
								"NJ_Telepole_26",
								"CP_telephonepole11",
								"NJ_Telepole_27",
								"NJ_Telepole_28",
								"NJ_Telepole_29",
								"NJ_Telepole_30",
								"NJ_Telepole_31",
								"NJ_Telepole_32" };
*/

		for( int s = 0; s < ( sizeof( p_ok_sectors ) / sizeof( char* )); ++s )
		{
			uint32 checksum = Crc::GenerateCRCFromString( p_ok_sectors[s] );
			if( checksum == m_checksum )
			{
//				Nx::CEngine::sGetImposterManager()->AddGeomToImposter( checksum, p_geom );
				Nx::CEngine::sGetImposterManager()->AddGeomToImposter( 0xb88905d6UL, p_geom );
			}
		}
	}
#	endif

	// Done with the raw vertex data.
	delete[] p_vc_wibble_indices;
	delete[] p_vertex_colors;
	delete[] p_vertex_tex_coords;
	delete[] p_vertex_bone_indices;
	delete[] p_vertex_weights;
	delete[] p_vertex_normals;
	delete[] p_vertex_positions;
	
	return true;
}



/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CSector
// and we will also have a CXboxSector, CNgcSector, even a CPcSector
// maybe in the future we will have a CPS3Sector?




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxSector::plat_set_visibility(uint32 mask)
{
/*
	// Set values
	m_visible = mask;

	if( m_mesh_array )
	{
		for( uint m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[m];
			p_mesh->SetVisibility( mask );
		}
	}
*/
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxSector::plat_set_active( bool on )
{
/*
	// Set values
	m_active = on;

	if( m_mesh_array )
	{
		for( uint m = 0; m < m_num_mesh; ++m )
		{
			NxXbox::sMesh *p_mesh = m_mesh_array[m];
			p_mesh->SetActive( on );
		}
	}
*/
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxSector::plat_set_color( Image::RGBA rgba )
{
/*
	if( m_mesh_array == NULL )
	{
		return;
	}

	for( uint32 i = 0; i < m_num_mesh; ++i )
	{
		NxXbox::sMesh *p_mesh = m_mesh_array[i];
		p_mesh->m_flags |= NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE;
		p_mesh->m_material_color_override[0] = (float)rgba.r / 255.0f;
		p_mesh->m_material_color_override[1] = (float)rgba.g / 255.0f;
		p_mesh->m_material_color_override[2] = (float)rgba.b / 255.0f;
	}
*/
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxSector::plat_clear_color( void )
{
/*
	if( m_mesh_array == NULL )
	{
		return;
	}

	for( uint32 i = 0; i < m_num_mesh; ++i )
	{
		NxXbox::sMesh *p_mesh = m_mesh_array[i];
		p_mesh->m_flags &= ~NxXbox::sMesh::MESH_FLAG_MATERIAL_COLOR_OVERRIDE;
	}
*/
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxSector::plat_set_world_position( const Mth::Vector& pos )
{
/*
	Mth::Vector new_offset = pos - m_pos_offset;

	// Go through and adjust the individual meshes.
	for( uint32 i = 0; i < m_num_mesh; ++i )
	{
		NxXbox::sMesh *p_mesh = m_mesh_array[i];
		p_mesh->Move( new_offset );
	}
*/
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::CBBox &CXboxSector::plat_get_bounding_box( void ) const
{
	static Mth::CBBox dummy;
	
	//	return m_bbox;
	return dummy;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const Mth::Vector &CXboxSector::plat_get_world_position( void ) const
{
	return m_pos_offset;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxSector::plat_set_shatter( bool on )
{
	if( on && mp_geom )
	{
		Shatter( mp_geom );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CSector *CXboxSector::plat_clone( bool instance, CScene *p_dest_scene )
{
	CXboxSector *p_xbox_sector = new CXboxSector();

	/*


	// Copies over much of the standard stuff, individual stuff will be overwritten later.
	CopyMemory( p_xbox_sector, this, sizeof( CXboxSector ));

	if( instance )
	{
		Dbg_Assert( 0 );
	}
	else
	{
		// Need to create a new set of meshes. First create the mesh pointer array...
		p_xbox_sector->m_mesh_array = new NxXbox::sMesh*[m_num_mesh];

		// ...then clone the meshes themselves.
		for( uint32 i = 0; i < m_num_mesh; ++i )
		{
			p_xbox_sector->m_mesh_array[i] = m_mesh_array[i]->Clone();
		}

		// Grab a temporary workspace buffer.
		NxXbox::sScene *p_scene								= ( static_cast( p_dest_scene ))->GetEngineScene();
		NxXbox::sMesh **p_temp_opaque_mesh_buffer			= new NxXbox::sMesh*[p_scene->m_num_opaque_entries];
		NxXbox::sMesh **p_temp_semitransparent_mesh_buffer	= new NxXbox::sMesh*[p_scene->m_num_semitransparent_entries];

		// Copy meshes over into the temporary workspace buffer.
		for( int i = 0; i < p_scene->m_num_opaque_entries; ++i )
		{
			p_temp_opaque_mesh_buffer[i] = p_scene->m_opaque_meshes[i];
		}
		for( int i = 0; i < p_scene->m_num_semitransparent_entries; ++i )
		{
			p_temp_semitransparent_mesh_buffer[i] = p_scene->m_semitransparent_meshes[i];
		}

		// Delete current mesh arrays.
		delete [] p_scene->m_opaque_meshes;
		delete [] p_scene->m_semitransparent_meshes;

		// Include new meshes in count.
		p_scene->CountMeshes( p_xbox_sector->m_num_mesh, p_xbox_sector->m_mesh_array );

		// Allocate new mesh arrays.
		p_scene->CreateMeshArrays();

		// Copy old mesh data back in.
		for( int i = 0; i < p_scene->m_num_opaque_entries; ++i )
		{
			p_scene->m_opaque_meshes[i] = p_temp_opaque_mesh_buffer[i];
		}
		for( int i = 0; i < p_scene->m_num_semitransparent_entries; ++i )
		{
			p_scene->m_semitransparent_meshes[i] = p_temp_semitransparent_mesh_buffer[i];
		}

		// Add new meshes.
		p_scene->AddMeshes( p_xbox_sector->m_num_mesh, p_xbox_sector->m_mesh_array );

		// Sort the meshes.
		p_scene->SortMeshes();
	}
*/
	return p_xbox_sector;

}

} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/XBox/p_nxsector.h
================================================
#ifndef	__GFX_P_NX_SECTOR_H__
#define	__GFX_P_NX_SECTOR_H__

#include 	
#include 	
#include 	"gfx\NxSector.h"
#include 	"gfx\Image\ImageBasic.h"

#include 	"gfx\xbox\p_nxscene.h"
#include 	"gfx\xbox\nx\mesh.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Here's a machine specific implementation of the CSector
class CXboxSector : public CSector
{
	public:
									CXboxSector();

	bool							LoadFromFile( void* p_file );
	bool							LoadFromMemory( void **p_mem );

	NxXbox::sScene					*GenerateScene( void );
	
	private:						// It's all private, as it is machine specific
	virtual void					plat_set_color( Image::RGBA rgba );
	virtual void					plat_clear_color( void );
	virtual void					plat_set_visibility( uint32 mask );
	virtual void					plat_set_active( bool on );
	virtual void					plat_set_world_position( const Mth::Vector& pos );
	virtual const Mth::CBBox		&plat_get_bounding_box( void ) const;
	virtual const Mth::Vector		&plat_get_world_position( void ) const;
	virtual void					plat_set_shatter( bool on );
	virtual CSector					*plat_clone( bool instance, CScene *p_scene = NULL );

	int								m_flags;

	Mth::Vector						m_pos_offset;

	Image::RGBA						m_rgba;
};

} // Namespace Nx  			

#endif

================================================
FILE: Code/Gfx/XBox/p_nxtexman.cpp
================================================
/////////////////////////////////////////////////////////////////////////////
// p_NxTexMan.cpp - Xbox platform specific interface to CTexMan
//

#include 

#include "gfx/NxTexMan.h"
#include "gfx/xbox/p_NxTexture.h"
#include "gfx/xbox/nx/texture.h"

namespace	Nx
{


/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
// Functions


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CTexDict* CTexDictManager::s_plat_load_texture_dictionary( const char *p_tex_dict_name, bool is_level_data, uint32 texDictOffset, bool is_skin, bool forceTexDictLookup )
{
	return new CXboxTexDict( p_tex_dict_name );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CTexDict* CTexDictManager::s_plat_load_texture_dictionary( uint32 checksum, uint32 *p_data, int data_size, bool is_level_data, uint32 texDictOffset, bool is_skin, bool forceTexDictLookup )
{
	CXboxTexDict *p_dict = new CXboxTexDict( checksum );
	p_dict->LoadTextureDictionaryFromMemory( p_data );
	return p_dict;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CTexDict* CTexDictManager::s_plat_create_texture_dictionary( uint32 checksum )
{
	return new CXboxTexDict( checksum );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CTexDictManager::s_plat_unload_texture_dictionary( CTexDict* p_tex_dict )
{
	delete p_tex_dict;
	return true;
}


} 
 


================================================
FILE: Code/Gfx/XBox/p_nxtexture.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxTexture.cpp

#include 	"Gfx/Nx.h"
#include 	"Gfx/xbox/p_NxTexture.h"
#include	"sys/file/filesys.h"

namespace Nx
{

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CTexture

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxTexture::CXboxTexture() :  m_transparent( false ), mp_texture( NULL )
{
	m_num_mipmaps = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxTexture::~CXboxTexture()
{
	if( mp_texture )
	{
		delete mp_texture;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxTexture::SetEngineTexture( NxXbox::sTexture *p_texture )
{
	mp_texture	= p_texture;
	m_checksum	= p_texture->Checksum;
}




/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxTexture::plat_load_texture( const char *p_texture_name, bool sprite, bool alloc_vram )
{
	char filename[256];

	strcpy( filename, p_texture_name );
	
	// Append '.img.xbx' to the end.
	strcat( filename, ".img.xbx" );

	mp_texture = NxXbox::LoadTexture( filename );
	
	return mp_texture;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxTexture::plat_replace_texture( CTexture *p_texture )
{								 	
	CXboxTexture *p_xbox_texture = static_cast( p_texture );

	// Go through and copy the texture.
	NxXbox::sTexture *p_src	= p_xbox_texture->GetEngineTexture();
	NxXbox::sTexture *p_dst	= GetEngineTexture();

	if( p_dst->pD3DTexture )
	{
		p_dst->pD3DTexture->Release();
	}
	if( p_dst->pD3DPalette )
	{
		p_dst->pD3DPalette->Release();
	}

	D3DSURFACE_DESC	desc;
	uint32			num_levels = p_src->pD3DTexture->GetLevelCount();
	p_src->pD3DTexture->GetLevelDesc( 0, &desc );
	if( D3D_OK != D3DDevice_CreateTexture(	desc.Width,
											desc.Height,
											num_levels,
											0,
											desc.Format,
											0,
											&p_dst->pD3DTexture ))
	{
		exit( 0 );
	}

	// Create and copy the palette if present.
	if( p_src->pD3DPalette )
	{
		if( D3D_OK != D3DDevice_CreatePalette( D3DPALETTE_256, &p_dst->pD3DPalette ))
		{
			exit( 0 );
		}
		
		D3DCOLOR *p_src_palette;
		D3DCOLOR *p_dst_palette;
		p_src->pD3DPalette->Lock( &p_src_palette, D3DLOCK_READONLY );
		p_dst->pD3DPalette->Lock( &p_dst_palette, 0 );
		CopyMemory( p_dst_palette, p_src_palette, sizeof( D3DCOLOR ) * 256 );
	}
	
	for( uint32 l = 0; l < num_levels; ++l )
	{
		p_src->pD3DTexture->GetLevelDesc( l, &desc );

		D3DLOCKED_RECT src_rect, dst_rect;
		p_src->pD3DTexture->LockRect( l, &src_rect, NULL, D3DLOCK_READONLY );
		p_dst->pD3DTexture->LockRect( l, &dst_rect, NULL, 0 );

		CopyMemory( dst_rect.pBits, src_rect.pBits, desc.Size );
	}
	
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxTexture::plat_add_to_vram( void )
{								 	
	// Meaningless on Xbox, added to remove annoying debug stub output.
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxTexture::plat_remove_from_vram( void )
{								 	
	// Meaningless on Xbox, added to remove annoying debug stub output.
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint16	CXboxTexture::plat_get_width() const
{
	if( mp_texture )
		return mp_texture->ActualWidth;

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint16	CXboxTexture::plat_get_height() const
{
	if( mp_texture )
		return mp_texture->ActualHeight;

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CXboxTexture::plat_get_bitdepth() const
{
	if( mp_texture )
		return mp_texture->TexelDepth;

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8	CXboxTexture::plat_get_num_mipmaps() const
{
	return m_num_mipmaps;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CXboxTexture::plat_is_transparent() const
{
	return m_transparent;
}

////////////////////////////////////////////////////////////////////////////////////
// Here's a machine specific implementation of CTexDict

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CXboxTexDict::CXboxTexDict( uint32 checksum ) : CTexDict( checksum )
{
	// Load nothing
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CXboxTexDict::CXboxTexDict( const char *p_tex_dict_name ) : CTexDict( p_tex_dict_name, true )
{
	 LoadTextureDictionary( p_tex_dict_name );	// the derived class will does this
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxTexDict::~CXboxTexDict()
{
	UnloadTextureDictionary();				// the derived class does this
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxTexDict::LoadTextureDictionary( const char *p_tex_dict_name )
{
	// Count the number of entries in the lookup table. If it is empty, it is safe
	// to delete it and create a new, optimum sized one during the load process itself.
	if( mp_texture_lookup )
	{
		int num_items = 0;
		mp_texture_lookup->IterateStart();
		while( mp_texture_lookup->IterateNext())
			++num_items;

		if( num_items == 0 )
			mp_texture_lookup = Nx::LoadTextureFile( p_tex_dict_name, mp_texture_lookup, true );
		else
			Nx::LoadTextureFile( p_tex_dict_name, mp_texture_lookup );
	}
	else
	{
		Nx::LoadTextureFile( p_tex_dict_name, mp_texture_lookup );
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxTexDict::LoadTextureDictionaryFromMemory( void *p_mem )
{
	// Count the number of entries in the lookup table. If it is empty, it is safe
	// to delete it and create a new, optimum sized one during the load process itself.
	if( mp_texture_lookup )
	{
		int num_items = 0;
		mp_texture_lookup->IterateStart();
		while( mp_texture_lookup->IterateNext())
			++num_items;

		if( num_items == 0 )
			mp_texture_lookup =	Nx::LoadTextureFileFromMemory( &p_mem, mp_texture_lookup, true );
		else
			Nx::LoadTextureFileFromMemory( &p_mem, mp_texture_lookup );
	}
	else
	{
		Nx::LoadTextureFileFromMemory( &p_mem, mp_texture_lookup );
	}
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxTexDict::UnloadTextureDictionary( void )
{
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CTexture *CXboxTexDict::plat_load_texture( const char *p_texture_name, bool sprite, bool alloc_vram )
{
	CXboxTexture *p_texture = new CXboxTexture;
	if( !p_texture->LoadTexture( p_texture_name, sprite ))
	{
		Dbg_Error("Can't load texture %s", p_texture_name);
	}
	return p_texture;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CTexture *CXboxTexDict::plat_reload_texture( const char *p_texture_name )
{
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxTexDict::plat_unload_texture( CTexture *p_texture )
{
	delete p_texture;

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxTexDict::plat_add_texture( CTexture *p_texture )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxTexDict::plat_remove_texture( CTexture *p_texture )
{
	return false;
}



#define MemoryRead( dst, size, num, src )	CopyMemory(( dst ), ( src ), (( num ) * ( size )));	\
											( src ) += (( num ) * ( size ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Lst::HashTable* LoadTextureFileFromMemory( void **pp_mem, Lst::HashTable *p_texture_table, bool okay_to_rebuild_texture_table )
{
	uint8 *p_data = (uint8*)( *pp_mem );

	// Read the texture file version and number of textures.
	int version, num_textures;
	MemoryRead( &version, sizeof( int ), 1, p_data );
	MemoryRead( &num_textures, sizeof( int ), 1, p_data );

	// If allowed, rebuild the texture table to the optimum size, using the same heap as the original table.
	if( okay_to_rebuild_texture_table )
	{
		uint32 optimal_table_size	= num_textures * 2;
		uint32 test					= 2;
		uint32 size					= 1;
		for( ;; test <<= 1, ++size )
		{
			// Check if this iteration of table size is sufficient, or if we have hit the maximum size.
			if(( optimal_table_size <= test ) || ( size >= 12 ))
			{
				Mem::Allocator::BlockHeader*	p_bheader	= Mem::Allocator::BlockHeader::sRead( p_texture_table );
				Mem::Allocator*					p_allocater	= p_bheader->mpAlloc;

				delete p_texture_table;

				Mem::Manager::sHandle().PushContext( p_allocater );
				p_texture_table = new Lst::HashTable( size );
				Mem::Manager::sHandle().PopContext();
				break;
			}
		}
	}

	for( int t = 0; t < num_textures; ++t )
	{
		// Create the engine level texture.
		NxXbox::sTexture *p_texture = new NxXbox::sTexture;

		uint32 base_width, base_height, levels, texel_depth, palette_depth, dxt, palette_size;
		MemoryRead( &p_texture->Checksum,	sizeof( uint32 ), 1, p_data );
		MemoryRead( &base_width,			sizeof( uint32 ), 1, p_data );
		MemoryRead( &base_height,			sizeof( uint32 ), 1, p_data );
		MemoryRead( &levels,				sizeof( uint32 ), 1, p_data );
		MemoryRead( &texel_depth,			sizeof( uint32 ), 1, p_data );
		MemoryRead( &palette_depth,			sizeof( uint32 ), 1, p_data );
		MemoryRead( &dxt,					sizeof( uint32 ), 1, p_data );
		MemoryRead( &palette_size,			sizeof( uint32 ), 1, p_data );

		p_texture->BaseWidth	= (uint16)base_width;
		p_texture->BaseHeight	= (uint16)base_height;
		p_texture->Levels		= (uint8)levels;
		p_texture->TexelDepth	= (uint8)texel_depth;
		p_texture->PaletteDepth	= (uint8)palette_depth;
		p_texture->DXT			= (uint8)dxt;
		
		D3DFORMAT	texture_format;
		if( p_texture->DXT > 0 )
		{
			if(( p_texture->DXT == 1 ) || ( p_texture->DXT == 2 ))
			{
				texture_format = D3DFMT_DXT1;
			}
			else if( p_texture->DXT == 5 )
			{
				texture_format = D3DFMT_DXT5;
			}
			else
			{
				Dbg_Assert( 0 );
			}
		}
		else if( p_texture->TexelDepth == 8 )
		{
			texture_format = D3DFMT_P8;
		}
		else if( p_texture->TexelDepth == 16 )
		{
			texture_format = D3DFMT_A1R5G5B5;	// Could also be X1R5G5B5;
		}
		else if( p_texture->TexelDepth == 32 )
		{
			texture_format = D3DFMT_A8R8G8B8;
		}
		else
		{
			Dbg_Assert( 0 );
		}
		
		if( D3D_OK != D3DDevice_CreateTexture(	p_texture->BaseWidth, p_texture->BaseHeight, p_texture->Levels,	0, texture_format, 0, &p_texture->pD3DTexture ))
		{
			Dbg_Assert( 0 );
		}

		if( palette_size > 0 )
		{
			// Create and lock the palette.
			if( D3D_OK != D3DDevice_CreatePalette(	palette_size == ( 256 * sizeof( D3DCOLOR )) ? D3DPALETTE_256 : D3DPALETTE_32, &p_texture->pD3DPalette ))
			{
				Dbg_Assert( 0 );
			}
			else
			{
				D3DCOLOR* p_colors;
				if( D3D_OK != p_texture->pD3DPalette->Lock( &p_colors, 0 ))
				{
					Dbg_Assert( 0 );
				}
				else
				{
					// Read in palette data.
					MemoryRead( p_colors, palette_size, 1, p_data );
				}
			}
		}
		else
		{
			p_texture->pD3DPalette = NULL;
		}

		for( uint32 mip_level = 0; mip_level < p_texture->Levels; ++mip_level )
		{
			uint32 texture_level_data_size;
			MemoryRead( &texture_level_data_size, sizeof( uint32 ), 1, p_data );

			D3DLOCKED_RECT locked_rect;
			if( D3D_OK != p_texture->pD3DTexture->LockRect( mip_level, &locked_rect, NULL, 0 ))
			{
				Dbg_Assert( 0 );
			}
			else
			{
				MemoryRead( locked_rect.pBits, texture_level_data_size, 1, p_data );
			}
		}

		// Add this texture to the table.
		Nx::CXboxTexture *p_xbox_texture = new Nx::CXboxTexture();
		p_xbox_texture->SetEngineTexture( p_texture );
		p_texture_table->PutItem( p_texture->Checksum, p_xbox_texture );
	}
	return p_texture_table;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Lst::HashTable* LoadTextureFile( const char *Filename, Lst::HashTable *p_texture_table, bool okay_to_rebuild_texture_table )
{
	// Open the texture file.
	void *p_FH = File::Open( Filename, "rb" );
	if( !p_FH )
	{
		Dbg_Message( "Couldn't open texture file %s\n", Filename );
		return p_texture_table;
	}

	// Read the texture file version and number of textures.
	int version, num_textures;
	File::Read( &version, sizeof( int ), 1, p_FH );
	File::Read( &num_textures, sizeof( int ), 1, p_FH );

	// If allowed, rebuild the texture table to the optimum size, using the same heap as the original table.
	if( okay_to_rebuild_texture_table )
	{
		uint32 optimal_table_size	= num_textures * 2;
		uint32 test					= 2;
		uint32 size					= 1;
		for( ;; test <<= 1, ++size )
		{
			// Check if this iteration of table size is sufficient, or if we have hit the maximum size.
			if(( optimal_table_size <= test ) || ( size >= 12 ))
			{
				Mem::Allocator::BlockHeader*	p_bheader	= Mem::Allocator::BlockHeader::sRead( p_texture_table );
				Mem::Allocator*					p_allocater	= p_bheader->mpAlloc;

				delete p_texture_table;

				Mem::Manager::sHandle().PushContext( p_allocater );
				p_texture_table = new Lst::HashTable( size );
				Mem::Manager::sHandle().PopContext();
				break;
			}
		}
	}

	for( int t = 0; t < num_textures; ++t )
	{
		// Create the engine level texture.
		NxXbox::sTexture *p_texture = new NxXbox::sTexture;

		uint32 base_width, base_height, levels, texel_depth, palette_depth, dxt, palette_size;
		File::Read( &p_texture->Checksum,	sizeof( uint32 ), 1, p_FH );
		File::Read( &base_width,			sizeof( uint32 ), 1, p_FH );
		File::Read( &base_height,			sizeof( uint32 ), 1, p_FH );
		File::Read( &levels,				sizeof( uint32 ), 1, p_FH );
		File::Read( &texel_depth,			sizeof( uint32 ), 1, p_FH );
		File::Read( &palette_depth,			sizeof( uint32 ), 1, p_FH );
		File::Read( &dxt,					sizeof( uint32 ), 1, p_FH );
		File::Read( &palette_size,			sizeof( uint32 ), 1, p_FH );

		p_texture->BaseWidth	= (uint16)base_width;
		p_texture->BaseHeight	= (uint16)base_height;
		p_texture->Levels		= (uint8)levels;
		p_texture->TexelDepth	= (uint8)texel_depth;
		p_texture->PaletteDepth	= (uint8)palette_depth;
		p_texture->DXT			= (uint8)dxt;
		
		D3DFORMAT	texture_format;
		if( p_texture->DXT > 0 )
		{
			if(( p_texture->DXT == 1 ) || ( p_texture->DXT == 2 ))
			{
				texture_format = D3DFMT_DXT1;
			}
			else if( p_texture->DXT == 5 )
			{
				texture_format = D3DFMT_DXT5;
			}
			else
			{
				Dbg_Assert( 0 );
			}
		}
		else if( p_texture->TexelDepth == 8 )
		{
			texture_format = D3DFMT_P8;
		}
		else if( p_texture->TexelDepth == 16 )
		{
			texture_format = D3DFMT_A1R5G5B5;	// Could also be X1R5G5B5;
		}
		else if( p_texture->TexelDepth == 32 )
		{
			texture_format = D3DFMT_A8R8G8B8;
		}
		else
		{
			Dbg_Assert( 0 );
		}
		
		if( D3D_OK != D3DDevice_CreateTexture(	p_texture->BaseWidth, p_texture->BaseHeight, p_texture->Levels,	0, texture_format, 0, &p_texture->pD3DTexture ))
		{
			Dbg_Assert( 0 );
		}

		if( palette_size > 0 )
		{
			// Create and lock the palette.
			if( D3D_OK != D3DDevice_CreatePalette(	palette_size == ( 256 * sizeof( D3DCOLOR )) ? D3DPALETTE_256 : D3DPALETTE_32, &p_texture->pD3DPalette ))
			{
				Dbg_Assert( 0 );
			}
			else
			{
				D3DCOLOR* p_colors;
				if( D3D_OK != p_texture->pD3DPalette->Lock( &p_colors, 0 ))
				{
					Dbg_Assert( 0 );
				}
				else
				{
					// Read in palette data.
					File::Read( p_colors, palette_size, 1, p_FH );
				}
			}
		}
		else
		{
			p_texture->pD3DPalette = NULL;
		}

		for( uint32 mip_level = 0; mip_level < p_texture->Levels; ++mip_level )
		{
			uint32 texture_level_data_size;
			File::Read( &texture_level_data_size, sizeof( uint32 ), 1, p_FH );

			D3DLOCKED_RECT locked_rect;
			if( D3D_OK != p_texture->pD3DTexture->LockRect( mip_level, &locked_rect, NULL, 0 ))
			{
				Dbg_Assert( 0 );
			}
			else
			{
				File::Read( locked_rect.pBits, texture_level_data_size, 1, p_FH );
			}
		}

		// Add this texture to the table.
		Nx::CXboxTexture *p_xbox_texture = new Nx::CXboxTexture();
		p_xbox_texture->SetEngineTexture( p_texture );
		p_texture_table->PutItem( p_texture->Checksum, p_xbox_texture );
	}
	File::Close( p_FH );

	return p_texture_table;
}


} // Namespace Nx  			
				
				


================================================
FILE: Code/Gfx/XBox/p_nxtexture.h
================================================
///////////////////////////////////////////////////////////////////////////////
// p_NxScene.h

#ifndef	__GFX_P_NX_TEXTURE_H__
#define	__GFX_P_NX_TEXTURE_H__

#include 	"Gfx/NxTexture.h"
#include 	"Gfx/xbox/nx/texture.h"
#include 	"Gfx/xbox/nx/material.h"

namespace Nx
{

/////////////////////////////////////////////////////////////////////////////////////
// Private classes
//
// Machine specific implementation of the CTexture
class	CXboxTexture : public CTexture
{
public:
								CXboxTexture();
	virtual						~CXboxTexture();

	NxXbox::sTexture			*GetEngineTexture() const;
	void						SetEngineTexture( NxXbox::sTexture *p_texture );


private:		// It's all private, as it is machine specific
	virtual bool				plat_load_texture( const char *p_texture_name, bool sprite, bool alloc_vram );
	virtual bool				plat_replace_texture( CTexture *p_texture );
	virtual bool				plat_add_to_vram( void );
	virtual bool				plat_remove_from_vram( void );

	virtual uint16				plat_get_width() const;
	virtual uint16				plat_get_height() const;
	virtual uint8				plat_get_bitdepth() const;
	virtual uint8				plat_get_num_mipmaps() const;
	virtual bool				plat_is_transparent() const;

	uint8						m_num_mipmaps;
	bool						m_transparent;

	// The actual data in the engine
	NxXbox::sTexture *			mp_texture;

};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline NxXbox::sTexture	*CXboxTexture::GetEngineTexture() const
{
	return mp_texture;
}



//////////////////////////////////////////////////////////////////////////////////
// Machine specific implementation of the CMaterial
class	CXboxMaterial : public CMaterial
{
public:
								CXboxMaterial();
	virtual						~CXboxMaterial();

private:
	Image::RGBA					plat_get_rgba() const;
	void						plat_set_rgba(Image::RGBA rgba);
	void						plat_set_texture();

	Image::RGBA					m_rgba;

	// The actual data in the engine
	NxXbox::sMaterial *			mp_material;
};

//////////////////////////////////////////////////////////////////////////////////
// Machine specific implementation of the CTexDict
class	CXboxTexDict : public CTexDict
{
public:
								CXboxTexDict( uint32 checksum );		// loads nothing
								CXboxTexDict(const char *p_tex_dict_name);
	virtual						~CXboxTexDict();

	bool						LoadTextureDictionary( const char *p_tex_dict_name );
	bool						LoadTextureDictionaryFromMemory( void *p_mem );
	bool						UnloadTextureDictionary();

private:

	// Platform-specific calls
	virtual CTexture *			plat_load_texture( const char *p_texture_name, bool sprite, bool alloc_vram );
	virtual CTexture *			plat_reload_texture( const char *p_texture_name );
	virtual bool				plat_unload_texture( CTexture *p_texture );
	virtual void				plat_add_texture( CTexture *p_texture );
	virtual bool				plat_remove_texture( CTexture *p_texture );
};

Lst::HashTable*	LoadTextureFile( const char *Filename, Lst::HashTable *p_texture_table, bool okay_to_rebuild_texture_table = false );
Lst::HashTable*	LoadTextureFileFromMemory( void **pp_mem, Lst::HashTable *p_texture_table, bool okay_to_rebuild_texture_table = false );


} // Namespace Nx  			

#endif


================================================
FILE: Code/Gfx/XBox/p_nxweather.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_NxWeather.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  6/19/2003
//****************************************************************************

#include 
#include "gfx/xbox/nx/render.h"

#include 
#include 
#include "gfx/xbox/nx/mesh.h"
#include "gfx/xbox/p_nxweather.h"

#include 
#include 

#include 
#include 
#include "gfx/nx.h"

#include "gfx/xbox/nx/sprite.h"

#define FEELER_START			50000.0f
#define NUM_LINES_PER_BATCH		80
#define NUM_SPRITES_PER_BATCH	80

#define PRECISION_SHIFT			4

#define RENDER_DIST				16
#define SEQ_START				411		// Between 1 and 4095.
#define SEQ_MASK				0x0240

unsigned char grid_bytes[70*1024];

//1-3: 0x03
//1-7: 0x06
//1-15: 0x0C
//1-31: 0x14
//1-63: 0x30
//1-127: 0x60
//1-255: 0xB8
//1-511: 0x0110
//1-1023: 0x0240
//1-2047: 0x0500
//1-4095: 0x0CA0
//1-8191: 0x1B00
//1-16383: 0x3500
//1-32767: 0x6000
//1-65535: 0xB400
//0x00012000
//0x00020400
//0x00072000
//0x00090000
//0x00140000
//0x00300000
//0x00400000
//0x00D80000
//0x01200000
//0x03880000
//0x07200000
//0x09000000
//0x14000000
//0x32800000
//0x48000000
//0xA3000000


namespace Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxWeather::CXboxWeather()
{
	mp_roof_height_index	= NULL;
	mp_rain_texture			= NULL;

	m_rain_blend			= NxXbox::GetBlendMode( CRCD( 0xa86285a1, "fixadd" )) | 0x80000000UL;
	m_splash_blend			= NxXbox::GetBlendMode( CRCD( 0xa86285a1, "fixadd" )) | 0x80000000UL;
	m_snow_blend			= NxXbox::GetBlendMode( CRCD( 0xa86285a1, "fixadd" )) | 0x80000000UL;

	m_rain_rate				= 0.0f;
	m_splash_rate			= 0.0f;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxWeather::~CXboxWeather()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxWeather::plat_update_grid( void )
{
	m_system_active = false;

	// Get super sector manager.
	SSec::Manager *ss_man;
	Mth::Line line;
	ss_man = Nx::CEngine::sGetNearestSuperSectorManager( line );		// Line is ignored, 1st manager is returned.
	if ( !ss_man ) return;

	// Calculate the size of the world in cels.
	int min_x = ( ((int)ss_man->GetWorldBBox()->GetMin()[X]) / WEATHER_CEL_SIZE ) - 1;
	int max_x = ( ((int)ss_man->GetWorldBBox()->GetMax()[X]) / WEATHER_CEL_SIZE ) + 1;
	int min_z = ( ((int)ss_man->GetWorldBBox()->GetMin()[Z]) / WEATHER_CEL_SIZE ) - 1;
	int max_z = ( ((int)ss_man->GetWorldBBox()->GetMax()[Z]) / WEATHER_CEL_SIZE ) + 1;

	// Define a maximum...
	if(( max_x - min_x ) > 350 )
	{
		int wid = ( max_x - min_x );
		int excess = ( wid - 350 );
		min_x += ( excess / 2 );
		max_x -= ( excess / 2 );
	}

	if(( max_z - min_z ) > 300 )
	{
		int wid = ( max_z - min_z );
		int excess = ( wid - 300 );
		min_z += ( excess / 2 );
		max_z -= ( excess / 2 );
	}

	// This is the actual width.
	m_width = ( max_x - min_x ) + 1;
	m_height = ( max_z - min_z ) + 1;

	m_min_x = (float)( min_x * WEATHER_CEL_SIZE );
	m_min_z = (float)( min_z * WEATHER_CEL_SIZE );

	// Temporary buffer for the raw array.
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	unsigned char * p8 = new unsigned char[m_width*m_height];
	Mem::Manager::sHandle().PopContext();

	// Go through and get the height for each cel corner.
	CFeeler feeler;
	int num_heights = 0;
	int xx, zz;
	for ( zz = min_z; zz <= max_z; zz++ )
	{
		for ( xx = min_x; xx <= max_x; xx++ )
		{
			// The cel to fill.
			int cel = ( ( zz - min_z ) * m_width ) + ( xx - min_x );

			// The position to check.
			Mth::Vector pos;
			pos[X] = (float)( xx * WEATHER_CEL_SIZE );
			pos[Y] = FEELER_START;
			pos[Z] = (float)( zz * WEATHER_CEL_SIZE );
			pos[W] = 1.0f;

			feeler.SetStart( pos );
			pos[Y] = -FEELER_START;
			feeler.SetEnd( pos );

			// Get the y position.
			sint32 y;
			if ( feeler.GetCollision( false, false ) )		// No movables, nearest collision.
			{
				y = (sint32)( feeler.GetPoint()[Y] * SUB_INCH_PRECISION );
			}
			else
			{
				y = (sint32)( FEELER_START * SUB_INCH_PRECISION );
			}

			// See if a close enough y pos already exists.
			int found = -1;
			for ( int lp = 0; lp < num_heights; lp++ )
			{
				if ( abs( ( m_roof_height[lp] - y ) ) < HEIGHT_TOLERANCE )
				{
					found = lp;
					break;
				}
			}

			// Fill in the cel.
			if ( found != -1 )
			{
				// Existing height.
				p8[cel] = found;
			}
			else
			{
				// New height.
				p8[cel] = num_heights;
				m_roof_height[num_heights] = y;
				num_heights++;
			}
		}
	}

	// Work out highest height for each cel.
	for ( zz = min_z; zz <= max_z; zz++ )
	{
		for ( xx = min_x; xx <= max_x; xx++ )
		{
			// The cel to fill.
			int cel = ( ( zz - min_z ) * m_width ) + ( xx - min_x );
			int celx = ( ( zz - min_z ) * m_width ) + ( ( xx + 1 ) - min_x );
			int celz = ( ( ( zz + 1 ) - min_z ) * m_width ) + ( xx - min_x );
			int celxz = ( ( ( zz + 1 ) - min_z ) * m_width ) + ( ( xx + 1 ) - min_x );

			if ( m_roof_height[p8[celx]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celx];
			if ( m_roof_height[p8[celz]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celz];
			if ( m_roof_height[p8[celxz]] > m_roof_height[p8[cel]] ) p8[cel] = p8[celxz];
		}
	}

	// Create a sparse array.
	mp_roof_row = (sRowEntry *)grid_bytes;
	mp_roof_height_index = (unsigned char *)&mp_roof_row[m_height];

	// 0 = offset
	// 1 = width
	// 2 = index

	unsigned short index = 0;
	for ( zz = 0; zz <= m_height; zz++ )
	{
		unsigned short start = 0;
		unsigned short end = m_width - 1;

		// Scan to find the start.
		bool start_set = false;
		for ( xx = 0; xx < m_width; xx++ )
		{
			int cel = ( zz * m_width ) + xx;

			if ( m_roof_height[p8[cel]] != (sint32)( FEELER_START * SUB_INCH_PRECISION ) )
			{
				if ( !start_set )
				{
					// Set start value.
					start = xx;
					start_set = true;
				}
				else
				{
					// Set end value.
					end = xx;
				}

			}
		}

		// Copy data & set row entry.
		if ( start < end )
		{
			mp_roof_row[zz].start = start;
			mp_roof_row[zz].end = end;
			mp_roof_row[zz].index = index;
			for ( xx = start; xx <= end ; xx++ )
			{
				int cel = ( zz * m_width ) + xx;

				mp_roof_height_index[index] = p8[cel];
				index++;
			}
		}
		else
		{
			// Row doesn't exist.
			mp_roof_row[zz].start = 16384;
			mp_roof_row[zz].end = 0;
			mp_roof_row[zz].index = 0;
		}
	}

	delete p8;

	int new_size = ( m_height * 6 ) + index;

	Dbg_Message( "Grid Size: Old: %d New: %d Num Heights: %d\n", ( m_width * m_height ), new_size, num_heights );

	// Set all drip time counters to 255.
	// 255 means the drop won't be rendered.
	// Setting to anything other than 255 will mean that it will increment to 255 and stop.
	for ( int lp = 0; lp < ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ); lp++ )
	{
		m_drop_time[lp] = 255;
		m_x_offset[lp] = Mth::Rnd( 255 );
		m_z_offset[lp] = Mth::Rnd( 255 );
	}
	m_active_drops = 0;

	m_seq = SEQ_START;

	// Get the texture.
	Nx::CTexture*		p_texture;
	Nx::CXboxTexture*	p_xbox_texture;
	mp_rain_texture = NULL;

	// Set Rain Texture.
	p_texture		= Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( CRCD( 0x45c7eb0f,"splash" ));
	p_xbox_texture	= static_cast( p_texture );
	if( p_xbox_texture )
	{
		mp_rain_texture = p_xbox_texture->GetEngineTexture();
	}

	// Set Snow Texture.
	p_texture = Nx::CTexDictManager::sp_particle_tex_dict->GetTexture( CRCD( 0xc97c5a4c,"snow" ));
	p_xbox_texture = static_cast( p_texture );
	if( p_xbox_texture )
	{
		mp_snow_texture = p_xbox_texture->GetEngineTexture();
	}

	m_system_active = true;

	// Zero out the splashes.
	for( int sp = 0; sp < NUM_SPLASH_ACTIVE; sp++ )
	{
		m_splash_current_life[sp] = 0;
	}
	m_current_splash = 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxWeather::plat_process( float delta_time )
{
	if( !m_system_active ) return;

	if( m_raining )
	{
		// It's raining.
		float rate = m_rain_drops_per_frame;
		if( rate > (float)(( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_rain_frames ))
		{
			rate = (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_rain_frames );
		}

		float last = m_rain_rate;
		m_rain_rate += rate;
		int new_drops = (int)m_rain_rate - (int)last;

		for( int lp = 0; lp < new_drops; lp++ )
		{
			// If this cel is not inactive, we caught up with ourselves.
			if ( m_drop_time[m_seq] != 255 ) break;

			// Setup the drop.
			m_drop_time[m_seq] = ( 254 - m_rain_frames );
			m_x_offset[m_seq] = Mth::Rnd( 255 );
			m_z_offset[m_seq] = Mth::Rnd( 255 );
			m_active_drops++;

			// Calculate next sequence value. Notice hack to get entry 0 into the sequence.
			switch ( m_seq )
			{
				case SEQ_START:
					m_seq = 0;
					break;
				case 0:
					m_seq = SEQ_START;
					// Fall through to default case...
				default:
					if ( m_seq & 1 )
					{
						m_seq = ( m_seq >> 1 ) ^ SEQ_MASK;
					}
					else
					{
						m_seq = ( m_seq >> 1 );
					}
					break;
			}
		}
	}
	else
	{
		// It's snowing.
		float rate = m_snow_flakes_per_frame;
		if ( rate > (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_snow_frames ) ) rate = (float)( ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ) / m_snow_frames );

		float last = m_snow_rate;
		m_snow_rate += rate;
		int new_drops = (int)m_snow_rate - (int)last;

		for ( int lp = 0; lp < new_drops; lp++ )
		{
			// If this cel is not inactive, we caught up with ourselves.
			if ( m_drop_time[m_seq] != 255 ) break;

			// Setup the drop.
			m_drop_time[m_seq] = ( 254 - m_snow_frames );
			m_x_offset[m_seq] = Mth::Rnd( 255 );
			m_z_offset[m_seq] = Mth::Rnd( 255 );
			m_active_drops++;

			// Calculate next sequence value. Notice hack to get entry 0 into the sequence.
			switch ( m_seq )
			{
				case SEQ_START:
				{
					m_seq = 0;
					break;
				}
				case 0:
				{
					m_seq = SEQ_START;
					// Fall through to default case...
				}
				default:
				{
					if ( m_seq & 1 )
					{
						m_seq = ( m_seq >> 1 ) ^ SEQ_MASK;
					}
					else
					{
						m_seq = ( m_seq >> 1 );
					}
					break;
				}
			}
		}
	}
}



inline DWORD FtoDW( FLOAT f ) { return *((DWORD*)&f); }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxWeather::plat_render_snow( float skx, float skz )
{
	// Early out if no drops to draw.
	if( !m_active_drops ) return;

	// Get skater position.
	int x = (int)(( skx - m_min_x ) / WEATHER_CEL_SIZE );
	int z = (int)(( skz - m_min_z ) / WEATHER_CEL_SIZE );

	// Calculate area to render.
	int sx = x - RENDER_DIST;
	int ex = x + DROP_SIZE;	//RENDER_DIST;
	int sz = z - RENDER_DIST;
	int ez = z + DROP_SIZE;	//RENDER_DIST;

	// Clip z values.
	if( ez < 0 ) return;
	if( sz > ( m_height - 1 )) return;

	// Pointsprites use texture stage 3 for some reason...
	NxXbox::set_blend_mode( m_snow_blend );
	NxXbox::set_render_state( RS_ALPHACUTOFF, 0 );
	NxXbox::set_render_state( RS_UVADDRESSMODE3, 0x00000000UL );
	mp_snow_texture->Set( 3 );

	// Set up point sprite rendering.
	D3DDevice_SetRenderState( D3DRS_POINTSPRITEENABLE,	TRUE );
	D3DDevice_SetRenderState( D3DRS_POINTSCALEENABLE,	TRUE );
	D3DDevice_SetRenderState( D3DRS_POINTSIZE,			FtoDW( m_snow_size ));
	D3DDevice_SetRenderState( D3DRS_POINTSCALE_A,		FtoDW( 0.00f ));
	D3DDevice_SetRenderState( D3DRS_POINTSCALE_B,		FtoDW( 0.00f ));
	D3DDevice_SetRenderState( D3DRS_POINTSCALE_C,		FtoDW( 1.00f ));

	NxXbox::set_pixel_shader( PixelShaderPointSprite );
	NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );

	float minx = m_min_x;
	float minz = m_min_z;
	
	// Calculate drop height list.
	int lp;
	float y_off[256];
	for( lp = ( 254 - m_snow_frames ); lp < 256; lp++ )
	{
		y_off[lp] = m_snow_height - (((float)(( lp - 254 ) + m_snow_frames ) / (float)m_snow_frames ) * m_snow_height );
	}

	// Calculate xz offset list.
	float xz_off[256];
	for( lp = 0; lp < 256; lp++ )
	{
		xz_off[lp] = ((float)lp * (float)( WEATHER_CEL_SIZE / 2 )) / 255.0f;
	}

	// Calculate sine wave.
	float sin_off[256];

	for( lp = 0; lp < 256; lp++ )
	{
		sin_off[lp] = sinf( Mth::PI * 2.0f * ((float)lp / 256.0f )) * ( WEATHER_CEL_SIZE / 2 );
	}

	// Get Xbox format color.
	uint32 snow_color = ((uint32)m_snow_color.a << 24 ) | ((uint32)m_snow_color.r << 16 ) | ((uint32)m_snow_color.g << 8 ) | ((uint32)m_snow_color.b << 0 );

	int	flakes = 0;

	DWORD* p_push				= NULL;
	DWORD* p_push_encode_fixup	= NULL;
	uint32 dwords_written		= 0;
	uint32 total_dwords_written	= 2;

	for( int lzz = sz; lzz <= ez; lzz++ )
	{
		int zz = ( lzz < 0 ) ? 0 : ( lzz > ( m_height - 1 ) ) ? ( m_height - 1 ) : lzz;

		if( mp_roof_row[zz].start == 16384 ) continue;

		float vx = ((float)sx * (float)WEATHER_CEL_SIZE ) + minx;
		float vz = ((float)zz * (float)WEATHER_CEL_SIZE ) + minz;

		int cel = mp_roof_row[zz].index + ( sx - mp_roof_row[zz].start );

		int drop_cel_z = (( zz & ( DROP_SIZE - 1 )) << DROP_SIZE_SHIFT );

		for( int lxx = sx; lxx <= ex; lxx++, cel++ )
		{
			int xx = ( lxx > mp_roof_row[zz].start ) ? ( ( lxx < mp_roof_row[zz].end ) ? lxx : mp_roof_row[zz].end ) : mp_roof_row[zz].start;

			// Get the current drop value. Skip this one if it's inactive.
			int drop_cel = drop_cel_z + ( xx & ( DROP_SIZE - 1 ));

			vx += (float)WEATHER_CEL_SIZE;
			float vy = (float)( m_roof_height[mp_roof_height_index[cel]] >> PRECISION_SHIFT );

			for( int d = 0; d < DROP_LAYERS; d++, drop_cel += ( DROP_SIZE * DROP_SIZE ))
			{
				if( m_drop_time[drop_cel] == 255 )
					continue;

				// Create the position for rendering.
				float v0x = vx + xz_off[m_x_offset[drop_cel]] + sin_off[(m_z_offset[drop_cel]+m_drop_time[drop_cel])&255];
				float v0y = vy + y_off[m_drop_time[drop_cel]] + m_snow_size;
				float v0z = vz + xz_off[m_z_offset[drop_cel]] + sin_off[(m_x_offset[drop_cel]+m_drop_time[drop_cel])&255];

				if( flakes == 0 )
				{
					flakes = NUM_SPRITES_PER_BATCH;
				}

				// Grab the push buffer if we don't have it yet.
				if( p_push == NULL )
				{
					// Grab 32k of push buffer.	
					p_push = D3DDevice_BeginPush( 32 * 1024 / 4 );

					// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
					// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
					// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
					p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
					p_push[1]	= D3DPT_POINTLIST;
					p_push		+= 2;

					// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
					// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
					// As yet, we don't know how many dwords we will write, so save a pointer to the current push buffer location
					// so we can fix up this value at the end.
					p_push_encode_fixup = p_push;
					++p_push;
				}

				p_push[0]				= *((uint32*)&v0x );
				p_push[1]				= *((uint32*)&v0y );
				p_push[2]				= *((uint32*)&v0z );
				p_push[3]				= snow_color;

				p_push					+= 4;
				dwords_written			+= 4;
				total_dwords_written	+= 4;

				// Check for hitting the max dwords for this encode block, in which case we need to end this block
				// and start another one.
				if( dwords_written >= 2044 )
				{
					// Now we know exactly how many dwords written, fix-up our earlier entry.
					p_push_encode_fixup[0]	= D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, dwords_written );

					// Are we approaching the end of this push buffer? If so, close it out and start a new one.
					if( total_dwords_written > 6000 )
					{
						// End the push buffer.
						total_dwords_written	= 0;
						p_push[0]				= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
						p_push[1]				= 0;
						p_push					+= 2;
						D3DDevice_EndPush( p_push );

						p_push					= NULL;
					}
					else
					{
						// As yet, we don't know how many dwords we will write, so save a pointer to the current push buffer location
						// so we can fix up this value at the end.
						p_push_encode_fixup		= p_push;
						++p_push;
					}

					dwords_written = 0;
				}

				flakes--;
			}
		}
	}

	if( p_push )
	{
		// Now we know exactly how many dwords written, fix-up our earlier entry.
		p_push_encode_fixup[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, dwords_written );

		// End the push buffer.
		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );
	}

	// Restore render states.
	D3DDevice_SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxWeather::plat_render_rain( float skx, float skz )
{
	// Early out if no drops to draw.
	if ( !m_active_drops ) return;

	// Get skater position.
	int x = (int)(( skx - m_min_x ) / WEATHER_CEL_SIZE );
	int z = (int)(( skz - m_min_z ) / WEATHER_CEL_SIZE );

	// Calculate area to render.
	int sx = x - RENDER_DIST;
	int ex = x + DROP_SIZE;	//RENDER_DIST;
	int sz = z - RENDER_DIST;
	int ez = z + DROP_SIZE;	//RENDER_DIST;

	// Clip z values.
	if ( ez < 0 ) return;
	if ( sz > ( m_height - 1 ) ) return;

	// Get Xbox format top and bottom color.
	uint32 top_color = ((uint32)m_rain_top_color.a << 24 ) | ((uint32)m_rain_top_color.r << 16 ) | ((uint32)m_rain_top_color.g << 8 ) | ((uint32)m_rain_top_color.b << 0 );
	uint32 bot_color = ((uint32)m_rain_bottom_color.a << 24 ) | ((uint32)m_rain_bottom_color.r << 16 ) | ((uint32)m_rain_bottom_color.g << 8 ) | ((uint32)m_rain_bottom_color.b << 0 );

	// Deal with FIXED_BRIGHTEN mode, which doesn't work.
	if(( m_rain_blend & NxXbox::sMaterial::BLEND_MODE_MASK ) == NxXbox::vBLEND_MODE_BRIGHTEN_FIXED )
	{
		set_blend_mode( NxXbox::vBLEND_MODE_BRIGHTEN );

		top_color = ( top_color & 0x00FFFFFFUL ) | ( m_rain_blend & 0xFF000000UL );
		bot_color = ( bot_color & 0x00FFFFFFUL ) | ( m_rain_blend & 0xFF000000UL );
	}
	else
	{
		NxXbox::set_blend_mode( m_rain_blend );
	}

	NxXbox::set_render_state( RS_ALPHACUTOFF, 0 );

	NxXbox::set_pixel_shader( PixelShader5 );
	NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );

	sint32 minx		= (sint32)( m_min_x * SUB_INCH_PRECISION );
	sint32 minz		= (sint32)( m_min_z * SUB_INCH_PRECISION );
	sint32 rlength	= (sint32)( m_rain_length * SUB_INCH_PRECISION );
	
	// Calculate drop height list.
	int lp;
	sint32 y_off[256];

	for( lp = ( 254 - m_rain_frames ); lp < 256; lp++ )
	{
		y_off[lp] = (sint32)(( m_rain_height - (((float)(( lp - 254 ) + m_rain_frames ) / (float)m_rain_frames ) * m_rain_height )) * SUB_INCH_PRECISION );
	}

	// Calculate xz offset list.
	sint32 xz_off[256];
	for( lp = 0; lp < 256; lp++ )
	{
		xz_off[lp] = (sint32)((((float)lp * (float)WEATHER_CEL_SIZE * SUB_INCH_PRECISION ) / 255.0f ));
	}

	int lines = 0;

	DWORD* p_push				= NULL;
	DWORD* p_push_encode_fixup	= NULL;
	uint32 dwords_written		= 0;
	uint32 total_dwords_written	= 2;

	for ( int lzz = sz; lzz <= ez; lzz++ )
	{
		int zz = ( lzz < 0 ) ? 0 : ( lzz > ( m_height - 1 ) ) ? ( m_height - 1 ) : lzz;

		if ( mp_roof_row[zz].start == 16384 ) continue;

		sint32 vx = ( (sint32)sx << ( WEATHER_CEL_SIZE_SHIFT + PRECISION_SHIFT ) ) + minx;
		sint32 vz = ( (sint32)zz << ( WEATHER_CEL_SIZE_SHIFT + PRECISION_SHIFT ) ) + minz;

		int cel = mp_roof_row[zz].index + ( sx - mp_roof_row[zz].start );

		int drop_cel_z = ( ( zz & ( DROP_SIZE - 1 ) ) << DROP_SIZE_SHIFT );

		for ( int lxx = sx; lxx <= ex; lxx++, cel++ )
		{
			int xx = ( lxx > mp_roof_row[zz].start ) ? ( ( lxx < mp_roof_row[zz].end ) ? lxx : mp_roof_row[zz].end ) : mp_roof_row[zz].start;

			// Get the current drop value. Skip this one if it's inactive.
			int drop_cel = drop_cel_z + ( xx & ( DROP_SIZE - 1 ) );

			vx += ( WEATHER_CEL_SIZE << PRECISION_SHIFT );
			sint32 vy = m_roof_height[mp_roof_height_index[cel]];

			for ( int d = 0; d < DROP_LAYERS; d++, drop_cel += ( DROP_SIZE * DROP_SIZE ) )
			{
				if ( m_drop_time[drop_cel] == 255 )
					continue;

				// Create the position for rendering.
				float v0x = ( vx + xz_off[m_x_offset[drop_cel]] ) * ( 1.0f / (float)( 1 << PRECISION_SHIFT ));
				float v0y = ( vy + y_off[m_drop_time[drop_cel]] ) * ( 1.0f / (float)( 1 << PRECISION_SHIFT ));
				float v0z = ( vz + xz_off[m_z_offset[drop_cel]] ) * ( 1.0f / (float)( 1 << PRECISION_SHIFT ));
				float v1y = v0y + ( rlength * ( 1.0f / (float)( 1 << PRECISION_SHIFT )));

				if ( lines == 0 )
				{
					lines = NUM_LINES_PER_BATCH;
				}

				// Grab the push buffer if we don't have it yet.
				if( p_push == NULL )
				{
					// Grab 32k of push buffer.	
					p_push = D3DDevice_BeginPush( 32 * 1024 / 4 );

					// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
					// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
					// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
					p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
					p_push[1]	= D3DPT_LINELIST;
					p_push		+= 2;

					// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
					// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
					// As yet, we don't know how many dwords we will write, so save a pointer to the current push buffer location
					// so we can fix up this value at the end.
					p_push_encode_fixup = p_push;
					++p_push;
				}

				p_push[0] = *((DWORD*)&v0x);
				p_push[1] = *((DWORD*)&v0y);
				p_push[2] = *((DWORD*)&v0z);
				p_push[3] = bot_color;

				p_push[4] = *((DWORD*)&v0x);
				p_push[5] = *((DWORD*)&v1y);
				p_push[6] = *((DWORD*)&v0z);
				p_push[7] = top_color;

				p_push					+= 8;
				dwords_written			+= 8;
				total_dwords_written	+= 8;

				// Check for hitting the max dwords for this encode block, in which case we need to end this block
				// and start another one.
				if( dwords_written >= 2040 )
				{
					// Now we know exactly how many dwords written, fix-up our earlier entry.
					p_push_encode_fixup[0]		= D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, dwords_written );

					// Are we approaching the end of this push buffer? If so, close it out and start a new one.
					if( total_dwords_written > 6000 )
					{
						// End the push buffer.
						total_dwords_written	= 0;
						p_push[0]				= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
						p_push[1]				= 0;
						p_push					+= 2;
						D3DDevice_EndPush( p_push );

						p_push					= NULL;
					}
					else
					{
						// As yet, we don't know how many dwords we will write, so save a pointer to the current push buffer location
						// so we can fix up this value at the end.
						p_push_encode_fixup		= p_push;
						++p_push;
					}
					dwords_written = 0;
				}
				lines--;
			}
		}
	}

	if( p_push )
	{
		// Now we know exactly how many dwords written, fix-up our earlier entry.
		p_push_encode_fixup[0] = D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, dwords_written );

		// End the push buffer.
		p_push[0] = D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
		p_push[1] = 0;
		p_push += 2;
		D3DDevice_EndPush( p_push );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxWeather::plat_render_splashes( float skx, float skz )
{
	// Create a new splash if required.
	float last = m_splash_rate;
	m_splash_rate += m_splash_per_frame;
	int new_splashes = (int)m_splash_rate - (int)last;

	if ( new_splashes )
	{
		CFeeler feeler;
		Mth::Vector pos;

		pos[X] = NxXbox::EngineGlobals.cam_position[X];
		pos[Y] = FEELER_START;
		pos[Z] = NxXbox::EngineGlobals.cam_position[Z];
		pos[W] = 1.0f;

		Mth::Vector dir;
		dir[X] = skx - NxXbox::EngineGlobals.cam_position[X];
		dir[Y] = 0.0f;
		dir[Z] = skz - NxXbox::EngineGlobals.cam_position[Z];
		dir[W] = 1.0f;
		dir.Normalize();

		// Add distance.
		float r1 = (float)Mth::Rnd( 32768 ) / 32768.0f;
		pos		+= ( dir * ((float)WEATHER_CEL_SIZE * RENDER_DIST * r1 ));

		// Add lateral.
		Mth::Vector lat( dir[Z], 0.0f, -dir[X], 1.0f );

		float r2 = 1.0f - ( (float)Mth::Rnd( 32768 ) / 32768.0f );
		if ( m_current_splash & 1 )
		{
			pos += ( lat * ( (float)WEATHER_CEL_SIZE * RENDER_DIST * r1 * r2 ) );
		}
		else
		{
			pos += ( lat * ( (float)WEATHER_CEL_SIZE * RENDER_DIST * r1 * -r2 ) );
		}

		feeler.SetStart( pos );
		pos[Y] = -FEELER_START;
		feeler.SetEnd( pos );

		// Get the y position.
		float y;
		if ( feeler.GetCollision( false, false ) )		// No movables, nearest collision.
		{
			y = feeler.GetPoint()[Y];
		}
		else
		{
			y = FEELER_START;
		}

		m_splash_x[m_current_splash]			= pos[X];
		m_splash_y[m_current_splash]			= y + 4.0f;	// Move up slightly.
		m_splash_z[m_current_splash]			= pos[Z];
		m_splash_current_life[m_current_splash]	= m_splash_life;

		m_current_splash++;
		m_current_splash %= NUM_SPLASH_ACTIVE;
	}
	
	// Render the splashes.
	NxXbox::set_blend_mode( m_splash_blend );
	NxXbox::set_render_state( RS_ALPHACUTOFF, 0 );
	NxXbox::set_render_state( RS_UVADDRESSMODE0, 0x00000000UL );
	mp_rain_texture->Set( 0 );

	NxXbox::set_pixel_shader( PixelShader4 );
	NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2( 0 ));

	uint32 dword_count		= 24 * NUM_SPLASH_ACTIVE;
	uint32 dwords_written	= 0;
	Dbg_Assert( dword_count < 2048 );

	// Obtain push buffer lock.
	// The additional number (+5 is minimum) is to reserve enough overhead for the encoding parameters. It can safely be more, but no less.
	DWORD* p_push				= D3DDevice_BeginPush( dword_count + 16 );
	DWORD* p_push_encode_fixup	= NULL;

	// Note that p_push is returned as a pointer to write-combined memory. Writes to write-combined memory should be
	// consecutive and in increasing order. Reads should be avoided. Additionally, any CPU reads from memory or the
	// L2 cache can force expensive partial flushes of the 32-byte write-combine cache.
	p_push[0]	= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
	p_push[1]	= D3DPT_QUADLIST;
	p_push		+= 2;

	// NOTE: A maximum of 2047 DWORDs can be specified to D3DPUSH_ENCODE. If there is more than 2047 DWORDs of vertex
	// data, simply split the data into multiple D3DPUSH_ENCODE( D3DPUSH_INLINE_ARRAY ) sections.
	// As yet, we don't know how many dwords we will write, so save a pointer to the current push buffer location
	// so we can fix up this value at the end.
	p_push_encode_fixup = p_push;
	++p_push;

	for ( int spl = 0; spl < NUM_SPLASH_ACTIVE; spl++ )
	{
		if( m_splash_current_life[spl] == 0 )
			continue;

		// Interpolate color (and switch to Xbox format at the same time).
		Image::RGBA	icol;
		icol.b		= (uint8)(((int)m_splash_color.r * m_splash_current_life[spl] ) / m_splash_life );
		icol.g		= (uint8)(((int)m_splash_color.g * m_splash_current_life[spl] ) / m_splash_life );
		icol.r		= (uint8)(((int)m_splash_color.b * m_splash_current_life[spl] ) / m_splash_life );
		icol.a		= (uint8)(((int)m_splash_color.a * m_splash_current_life[spl] ) / m_splash_life );

		// Draw splashes...
		float vx0	= m_splash_x[spl] - ( m_splash_size * 0.5f );
		float vx1	= m_splash_x[spl] + ( m_splash_size * 0.5f );
		float vy	= m_splash_y[spl];
		float vz0	= m_splash_z[spl] - ( m_splash_size * 0.5f );
		float vz1	= m_splash_z[spl] + ( m_splash_size * 0.5f );

		p_push[0]	= *((DWORD*)&vx0 );
		p_push[1]	= *((DWORD*)&vy );
		p_push[2]	= *((DWORD*)&vz0 );
		p_push[3]	= *((DWORD*)&icol );
		p_push[4]	= 0x00000000UL;
		p_push[5]	= 0x00000000UL;

		p_push[6]	= *((DWORD*)&vx1 );
		p_push[7]	= *((DWORD*)&vy );
		p_push[8]	= *((DWORD*)&vz0 );
		p_push[9]	= *((DWORD*)&icol );
		p_push[10]	= 0x3F800000UL;
		p_push[11]	= 0x00000000UL;

		p_push[12]	= *((DWORD*)&vx1 );
		p_push[13]	= *((DWORD*)&vy );
		p_push[14]	= *((DWORD*)&vz1 );
		p_push[15]	= *((DWORD*)&icol );
		p_push[16]	= 0x3F800000UL;
		p_push[17]	= 0x3F800000UL;

		p_push[18]	= *((DWORD*)&vx0 );
		p_push[19]	= *((DWORD*)&vy );
		p_push[20]	= *((DWORD*)&vz1 );
		p_push[21]	= *((DWORD*)&icol );
		p_push[22]	= 0x00000000UL;
		p_push[23]	= 0x3F800000UL;

		p_push			+= 24;
		dwords_written	+= 24;
		m_splash_current_life[spl]--;
	}

	p_push_encode_fixup[0]	= D3DPUSH_ENCODE( D3DPUSH_NOINCREMENT_FLAG | D3DPUSH_INLINE_ARRAY, dwords_written );
	p_push[0]				= D3DPUSH_ENCODE( D3DPUSH_SET_BEGIN_END, 1 );
	p_push[1]				= 0;
	D3DDevice_EndPush( p_push + 2 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxWeather::plat_render( void )
{
	if ( !m_system_active ) return;

	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = pSkate->GetSkater(0);
	if (!pSkater) return;
	
	float skx = pSkater->m_pos[X];
	float skz = pSkater->m_pos[Z];

	if ( m_raining )
	{
		plat_render_splashes( skx, skz );
		plat_render_rain( skx, skz );
	}
	else
	{
		plat_render_snow( skx, skz );
	}

	// Increment rain drop/snow flake positions.
	if ( m_active_drops )
	{
		for ( int lp = 0; lp < ( DROP_SIZE * DROP_SIZE * DROP_LAYERS ); lp++ )
		{
			switch ( m_drop_time[lp] )
			{
				case 255:
					// Inactive.
					break;
				case 254:
					// About to become inactive, so decrement active drops.
					m_active_drops--;
					// Deliberately falls through...
				default:
					// Increment time.
					m_drop_time[lp]++;
					break;
			}
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxWeather::plat_set_rain_blend_mode( uint32 blendmode_checksum, int fix )
{
	m_rain_blend = NxXbox::GetBlendMode( blendmode_checksum ) | ((uint32)fix << 24 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxWeather::plat_set_splash_blend_mode( uint32 blendmode_checksum, int fix )
{
	m_splash_blend = NxXbox::GetBlendMode( blendmode_checksum ) | ((uint32)fix << 24 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxWeather::plat_set_snow_blend_mode( uint32 blendmode_checksum, int fix )
{
	m_snow_blend = NxXbox::GetBlendMode( blendmode_checksum ) | ((uint32)fix << 24 );
}

} // Nx






================================================
FILE: Code/Gfx/XBox/p_nxweather.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_NxWeather.h
//* OWNER:          Dave Cowling
//* CREATION DATE:  6/19/2003
//****************************************************************************

#ifndef	__GFX_P_NX_WEATHER_H__
#define	__GFX_P_NX_WEATHER_H__
    
#include "gfx/nxweather.h"

namespace Nx
{

#define SUB_INCH_PRECISION		16.0f
#define WEATHER_CEL_SIZE		128
#define WEATHER_CEL_SIZE_SHIFT	7
#define HEIGHT_TOLERANCE		(sint32)( 30.0f * SUB_INCH_PRECISION )
#define DROP_SIZE				16
#define DROP_SIZE_SHIFT			4
#define DROP_LAYERS				4
#define NUM_SPLASH_ACTIVE		32

typedef struct
{
	unsigned short start;
	unsigned short end;
	unsigned short index;
}
sRowEntry;

class CXboxWeather : public CWeather
{
public:
							CXboxWeather();
	virtual 				~CXboxWeather();

private:					// It's all private, as it is machine specific
	void					plat_update_grid( void );
	virtual void			plat_process( float delta_time );
	virtual void			plat_render( void );
	void					plat_set_rain_blend_mode( uint32 blendmode_checksum, int fix );
	void					plat_set_splash_blend_mode( uint32 blendmode_checksum, int fix );
	void					plat_set_snow_blend_mode( uint32 blendmode_checksum, int fix );

	void					plat_render_splashes( float skx, float skz );
	void					plat_render_rain( float skx, float skz );
	void					plat_render_snow( float skx, float skz );

	NxXbox::sTexture*		mp_rain_texture;
	NxXbox::sTexture*		mp_snow_texture;

	unsigned char *			mp_roof_height_index;
	sRowEntry *				mp_roof_row;
	sint32					m_roof_height[256];
	int						m_width;
	int						m_height;

	float					m_min_x;
	float					m_min_z;

	float					m_rain_rate;
	float					m_splash_rate;
	float					m_snow_rate;

	Mth::Vector				m_grid_base;

	unsigned char			m_drop_time[DROP_SIZE*DROP_SIZE*DROP_LAYERS];
	unsigned char			m_x_offset[DROP_SIZE*DROP_SIZE*DROP_LAYERS];
	unsigned char			m_z_offset[DROP_SIZE*DROP_SIZE*DROP_LAYERS];

	uint32					m_seq;

	uint32					m_rain_blend;
	uint32					m_splash_blend;
	uint32					m_snow_blend;

	float					m_splash_x[NUM_SPLASH_ACTIVE];
	float					m_splash_y[NUM_SPLASH_ACTIVE];
	float					m_splash_z[NUM_SPLASH_ACTIVE];
	int						m_splash_current_life[NUM_SPLASH_ACTIVE]; 
	int						m_current_splash;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // Nx


#endif



================================================
FILE: Code/Gfx/baseanimcontroller.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       BaseAnimController.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/06/2003
//****************************************************************************

#include 

#include 
#include 

#include 
#include 
							 
#include 
#include 
						  
namespace Gfx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseAnimController::CBaseAnimController( CBlendChannel* pBlendChannel ) : mp_blendChannel( pBlendChannel )
{
	m_name = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseAnimController::~CBaseAnimController()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBaseAnimController::InitFromStructure( Script::CStruct* pParams )
{
	pParams->GetChecksum( CRCD(0x40c698af,"id"), &m_name, Script::NO_ASSERT );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBaseAnimController::Update()
{
	Dbg_Assert( 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBaseAnimController::GetPose( Gfx::CPose* pResultPose )
{
	// not handled
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBaseAnimController::GetDebugInfo( Script::CStruct* p_info )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAnimFunctionResult CBaseAnimController::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	return AF_NOT_EXECUTED;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CCompositeObject* CBaseAnimController::GetObject()
{
	return mp_blendChannel->GetObject();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::CSkeleton* CBaseAnimController::GetSkeleton()
{
	Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( GetObject() );
	if ( pSkeletonComponent )
	{
		return pSkeletonComponent->GetSkeleton();
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}


================================================
FILE: Code/Gfx/baseanimcontroller.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       BaseAnimController.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/6/2003
//****************************************************************************

#ifndef __GFX_BASEANIMCONTROLLER_H__
#define __GFX_BASEANIMCONTROLLER_H__

#include 
#include 

// for procedural bones, which will be moved to another file...
#include 

namespace Obj
{
	class CCompositeObject;
}
				
namespace Script
{
	class CScript;
	class CStruct;
}

namespace Gfx
{
	class CBlendChannel;
	class CPose;
	class CSkeleton;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// (If the order of these change, then please also
// change Obj::CBaseComponent::EMemberFunctionResult)
enum EAnimFunctionResult
{
	AF_FALSE 		= 0,
	AF_TRUE  		= 1,
	AF_NOT_EXECUTED = 2
};

// maybe anim controllers should subclass from Obj::CBaseComponent
// so that they can access to the same CallMemberFunction interface?

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// TODO:  Move this to some generic animcontrollertypes.h

struct SWobbleDetails
{
    float				wobbleAmpA;
    float				wobbleAmpB;
    float				wobbleK1;
    float				wobbleK2;
    float				spazFactor;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// TODO:  Move this to some generic procanim.h

class CProceduralBone
{
public:
	CProceduralBone()
	{
		m_name = 0;
	}

public:
	uint32					m_name;
	
	bool					transEnabled;
	Mth::Vector				trans0;
	Mth::Vector				trans1;
	Mth::Vector				deltaTrans;
	Mth::Vector				currentTrans;
	
	bool					rotEnabled;
	Mth::Vector				rot0;
	Mth::Vector				rot1;
	Mth::Vector				deltaRot;
	Mth::Vector				currentRot;
	
	bool					scaleEnabled;
	Mth::Vector				scale0;
	Mth::Vector				scale1;
	Mth::Vector				deltaScale;
	Mth::Vector				currentScale;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class CBaseAnimController : public Spt::Class
{
public:
								CBaseAnimController( CBlendChannel* pBlendChannel );
	virtual 					~CBaseAnimController();

	virtual bool				GetPose( Gfx::CPose* pResultPose );
	virtual void				GetDebugInfo( Script::CStruct* p_info );
    virtual EAnimFunctionResult CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );

public:
	virtual void 				InitFromStructure( Script::CStruct* pParams );
	virtual void 				Update();

	int							GetPriority() const
	{
		return m_priority;
	}

	void						SetPriority( int priority )
	{
		m_priority = priority;
	}

	uint32						GetID() const
	{
		return m_name;
	}

protected:
	Obj::CCompositeObject*		GetObject();
	Gfx::CSkeleton*				GetSkeleton();

protected:
	CBlendChannel*				mp_blendChannel;
	Obj::CCompositeObject*		mp_object;
	int							m_priority;
	uint32						m_name;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}

#endif



================================================
FILE: Code/Gfx/bbox.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		bbox.cpp												**
**																			**
**	Created:		02/01/00	-	mjb	(actualy Matt's now)				**
**																			**
**	Description:	some functions for line/box intersection
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


namespace Gfx
{


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

bool PointInsideBox( const Mth::Vector &point, const Mth::Vector &boxMax, const Mth::Vector &boxMin )
{
	
	if ( point[ X ] > boxMax[ X ] )
		return ( false );
	if ( point[ Y ] > boxMax[ Y ] )
		return ( false );
	if ( point[ Z ] > boxMax[ Z ] )
		return ( false );
	if ( point[ X ] < boxMin[ X ] )
		return ( false );
	if ( point[ Y ] < boxMin[ Y ] )
		return ( false );
	if ( point[ Z ] < boxMin[ Z ] )
		return ( false );
	
	return ( true );
}// end of PointInsideBox( )


// Checks whether pStart-pEnd collides with the axis-aligned bounding box defined by
// pMax and pMin.
bool LineCollidesWithBox( const Mth::Vector &pStart, const Mth::Vector &pEnd, const Mth::Vector &pMax, const Mth::Vector &pMin )
{
	

	// if either point is in the box... that counts!
	if ( PointInsideBox( pStart, pMax, pMin ) ||
		PointInsideBox( pEnd, pMax, pMin ) )
	{
		return ( true );
	}

	// Trivial rejection.
	if((pStart[ Y ]>pMax[ Y ])&&( pEnd[ Y ]>pMax[ Y ])) return false;
	if((pStart[ Y ]pMax[ X ])&&( pEnd[ X ]>pMax[ X ])) return false;
	if((pStart[ X ]pMax[ Z ])&&( pEnd[ Z ]>pMax[ Z ])) return false;
	if((pStart[ Z ]pMax[ X ] && pEnd[ X ]pMin[ Y ] && zpMin[ Z ])
		{
			// It does collide!
			return true;
		}
	}

	// Check the min-x face.
	if (pStart[ X ]pMin[ X ])
	{
		// It crosses the plane of the face, so calculate the y & z coords
		// of the intersection and see if they are in the face,
		float d=pMin[ X ]-pStart[ X ];
		float y=d*dy/dx+pStart[ Y ];
		float z=d*dz/dx+pStart[ Z ];
		if (ypMin[ Y ] && zpMin[ Z ])
		{
			// It does collide!
			return true;
		}
	}

	// Check the max-y face.
	if (pStart[ Y ]>pMax[ Y ] && pEnd[ Y ]pMin[ X ] && zpMin[ Z ])
		{
			// It does collide!
			return true;
		}
	}

	// Check the min-y face.
	if (pStart[ Y ]pMin[ Y ])
	{
		// It crosses the plane of the face, so calculate the x & z coords
		// of the intersection and see if they are in the face,
		float d=pMin[ Y ]-pStart[ Y ];
		float x=d*dx/dy+pStart[ X ];
		float z=d*dz/dy+pStart[ Z ];
		if (xpMin[ X ] && zpMin[ Z ])
		{
			// It does collide!
			return true;
		}
	}

	// Check the max-z face.
	if (pStart[ Z ]>pMax[ Z ] && pEnd[ Z ]pMin[ X ] && ypMin[ Y ])
		{
			// It does collide!
			return true;
		}
	}


	// Check the min-z face.
	if (pStart[ Z ]pMin[ Z ])
	{
		// It crosses the plane of the face, so calculate the x & y coords
		// of the intersection and see if they are in the face,
		float d=pMin[ Z ]-pStart[ Z ];
		float x=d*dx/dz+pStart[ X ];
		float y=d*dy/dz+pStart[ Y ];
		if (xpMin[ X ] && ypMin[ Y ])
		{
			// It does collide!
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
} // namespace Gfx


================================================
FILE: Code/Gfx/bbox.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics  (GFX)											**
**																			**
**	File name:		gfx/bbox.h												**
**																			**
**	Created: 		06/22/00	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __GFX_BBOX_H
#define __GFX_BBOX_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/


#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#ifdef __PLAT_NGC__
#include "sys/ngc/p_camera.h"
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Gfx
{



/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/
			
/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/
bool PointInsideBox( const Mth::Vector &point, const Mth::Vector &boxMax, const Mth::Vector &boxMin );
bool LineCollidesWithBox( const Mth::Vector &pStart, const Mth::Vector &pEnd, const Mth::Vector &pMax, const Mth::Vector &pMin );


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Gfx

#endif	// __GFX_BBOX_H


================================================
FILE: Code/Gfx/blendchannel.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       BlendChannel.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/12/2002
//****************************************************************************

#include 

#include 

#include 

#include 
#include 
#include 
#include 
#include 
							 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
				  
namespace Gfx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBlendChannel::CBlendChannel( Obj::CCompositeObject* pCompositeObject )
{
	m_numControllers = 0;

	// immediately starts active when it's created
	m_status = ANIM_STATUS_ACTIVE;

	mp_object = pCompositeObject;
	Dbg_MsgAssert( mp_object, ( "Blend channel has no object" ) );
}

// how to handle conflicts between controllers?
// you should really only have either the boned anim or the wobble controller
// maybe each item should have its own local time...

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBlendChannel::~CBlendChannel()
{
	remove_controllers();

	// wipe out any old keys
	delete_custom_keys();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseAnimController* CBlendChannel::AddController( Script::CStruct* pParams )
{
	uint32 type;
	pParams->GetChecksum( CRCD(0x7321a8d6,"type"), &type, Script::ASSERT );

	CBaseAnimController* pController = NULL;

	int priority = 500;

	switch ( type )
	{
		case 0xcb4533ff:	// bonedanim
			{
				pController = new CBonedAnimController( this );
			}
			break;

		case 0xd079853e:	// ik
			{
				pController = new CIKController( this );
			}
			break;

		case 0x6d941203:	// wobble
			{
				pController = new CWobbleController( this );
			}
			break;
										 
		case 0xcfc5b380:	// pose
			{
				pController = new CPoseController( this );
			}
			break;

		case 0xfdf0436c:	// proceduralanim
			{
				pController = new CProceduralAnimController( this );
			}
			break;
		
		case 0xdf5c091a:	// fliprotate
			{
				pController = new CFlipRotateController( this );
				
				// make it be processed later
				// (so that partial anims can take effect of flipping)
				priority = 100;
			}
			break;
	
		case 0x26205db6:	// lookat
			{
				pController = new CLookAtController( this );
			}
			break;
		
		case 0x659bf355:	// partialanim
			{
				pController = new CPartialAnimController( this );
			}
			break;

		default:
			{
				Dbg_MsgAssert( 0, ( "Unrecognized controller %s", Script::FindChecksumName(type) ) );
			}
	}

	// override priority, if necessary
	pParams->GetInteger( CRCD(0x9d5923d8,"priority"), &priority, Script::NO_ASSERT );

	Dbg_MsgAssert( pController, ( "No controller" ) );
	pController->InitFromStructure( pParams );
	pController->SetPriority( priority );
	add_controller( pController, priority );

	return pController;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::add_controller( CBaseAnimController* pAnimController, int priority )
{
	Dbg_MsgAssert( m_numControllers < vMAX_CONTROLLERS, ( "Too many controllers for this channel on %s", Script::FindChecksumName( GetObject()->GetID() ) ) );	
	Dbg_MsgAssert( pAnimController, ( "No controller" ) );
		
	int i = 0;
	for ( i = 0; i < m_numControllers; i++ )
	{
		if ( priority > mp_controllers[i]->GetPriority() )
		{
			// shift all of them
			for ( int j = m_numControllers; j > i; j-- )
			{
				mp_controllers[j] = mp_controllers[j - 1];
			}
			
			break;
		}
	}

	mp_controllers[i] = pAnimController;
	m_numControllers++;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::remove_controllers()
{
	for ( int i = 0; i < m_numControllers; i++ )
	{
		delete mp_controllers[i];
		mp_controllers[i] = NULL;
	}

	m_numControllers = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::remove_controller( CBaseAnimController* pAnimController )
{
	// TODO:  Should make sure that high-level
	// function is not currently traversing the list...
	
	for ( int i = 0; i < m_numControllers; i++ )
	{
		if ( pAnimController == mp_controllers[i] )
		{
			delete mp_controllers[i];
			mp_controllers[i] = NULL;

			// shift the rest of them
			for ( int j = i; j < m_numControllers - 1; j++ )
			{
				mp_controllers[j] = mp_controllers[j + 1];
			}

			m_numControllers--;
			return;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseAnimController* CBlendChannel::get_controller_by_id( uint32 id )
{
	for ( int i = 0; i < m_numControllers; i++ )
	{
		if ( mp_controllers[i]->GetID() == id )
		{
			return mp_controllers[i];
		}
	}

	// not found
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::Update()
{
	// reset the anim complete field for this frame
	m_animComplete = false;

	if ( GetStatus() == ANIM_STATUS_DEGENERATING )
	{
		m_degenerationTime -= ( m_blendSpeed * Tmr::FrameRatio() );
		if ( m_degenerationTime <= 0.0f )
		{								    
			// This animation is no longer active.
			SetStatus( ANIM_STATUS_INACTIVE );
		}
		else if ( ( m_degenerationTime * m_degenerationTimeToBlendMultiplier ) < 0.0f )
		{
			// Check for being too small to care about.
			SetStatus( ANIM_STATUS_INACTIVE ); 
		}
	}
	else
	{
		// don't really need to do this any more, because
		// the update happens in the CBonedAnimController...
		//		CAnimChannel::Update();
	}
	
	if ( GetStatus() == ANIM_STATUS_ACTIVE )
	{
		for ( int i = 0; i < m_numControllers; i++ )
		{
			// one of these update functions should set the correct m_time for this frame
			mp_controllers[i]->Update();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBlendChannel::GetPose( Gfx::CPose* pResultPose )
{
	for ( int i = 0; i < m_numControllers; i++ )
	{
		mp_controllers[i]->GetPose( pResultPose );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBlendChannel::Degenerate( float blend_period )
{
	if ( blend_period == 0.0f )
	{
		// no point in degenerating it
		// so consider it "done"
		SetStatus( ANIM_STATUS_INACTIVE );
	}

	// only degenerate if it's active
	if( GetStatus() == ANIM_STATUS_ACTIVE )
	{
		SetStatus( ANIM_STATUS_DEGENERATING );
		m_degenerationTime = blend_period;
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CBlendChannel::GetBlendValue()
{
	EAnimStatus theStatus = GetStatus();

	switch ( theStatus )
	{
		case ANIM_STATUS_ACTIVE:
			return 1.0f;
		case ANIM_STATUS_INACTIVE:
			return 0.0f;
		case ANIM_STATUS_DEGENERATING:
			return m_degenerationTime * m_degenerationTimeToBlendMultiplier;
		default:
			Dbg_Assert( 0 );
			return 0.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::GetDebugInfo( Script::CStruct* p_info )
{
#ifdef	__DEBUG_CODE__
	CAnimChannel::GetDebugInfo( p_info );
	
	for ( int i = 0; i < m_numControllers; i++ )
	{
		mp_controllers[i]->GetDebugInfo( p_info );
	}

	p_info->AddFloat( CRCD(0x98146e11,"m_degenerationTime"), m_degenerationTime );
	p_info->AddFloat( CRCD(0x0897053d,"m_degenerationTimeToBlendMultiplier"), m_degenerationTimeToBlendMultiplier );
	
	uint32 status_checksum = 0;
	switch ( m_status )
	{
		case ANIM_STATUS_INACTIVE:
			status_checksum = CRCD(0xbc4202a3,"ANIM_STATUS_INACTIVE");
			break;
		case ANIM_STATUS_DEGENERATING:
			status_checksum = CRCD(0x5071e935,"ANIM_STATUS_DEGENERATING");
			break;
		case ANIM_STATUS_ACTIVE:
			status_checksum = CRCD(0x7009388b,"ANIM_STATUS_ACTIVE");
			break;
		default:
			Dbg_MsgAssert( 0, ( "Unknown status found in CBlendChannel::GetDebugInfo.  Was the enum changed?" ) );
			break;
	}
	p_info->AddChecksum( CRCD(0xce6cc81d,"m_status"), status_checksum );
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::Reset()
{
	CAnimChannel::Reset();

    m_status									= ANIM_STATUS_INACTIVE;

	m_degenerationTime						    = 0.0f;
	m_degenerationTimeToBlendMultiplier	        = 0.0f;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::InvalidateCache()
{
	for ( int i = 0; i < m_numControllers; i++ )
	{
		mp_controllers[i]->CallMemberFunction( CRCD(0x5f495ae0,"InvalidateCache"), NULL, NULL );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAnimStatus CBlendChannel::GetStatus() const
{
	return m_status;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::SetStatus( EAnimStatus status )
{
	m_status = status;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBlendChannel::IsActive()
{
	return ( GetStatus() != Gfx::ANIM_STATUS_INACTIVE );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBlendChannel::IsDegenerating()
{
	return ( GetStatus() == Gfx::ANIM_STATUS_DEGENERATING );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::PlaySequence( uint32 anim_name, float start_time, float end_time, EAnimLoopingType loop_type, float blend_period, float speed, bool flipped )
{
	// clear out any old custom keys, and add the new ones
	delete_custom_keys();
	add_custom_keys( anim_name );
	
	m_status = ANIM_STATUS_ACTIVE;
	
	m_blendSpeed = speed / 60.0f;

	Script::CStruct* pTempParams = NULL;
	 
	if ( loop_type == Gfx::LOOPING_WOBBLE )
	{
		pTempParams = new Script::CStruct;
		pTempParams->AddChecksum( CRCD(0x7321a8d6,"type"), CRCD(0x6d941203,"wobble") );
		this->AddController( pTempParams );
		delete pTempParams;
	}
	else
	{
		pTempParams= new Script::CStruct;
		pTempParams->AddChecksum( CRCD(0x7321a8d6,"type"), CRCD(0xcb4533ff,"bonedanim") );
		this->AddController( pTempParams );
		delete pTempParams;
	}

	pTempParams= new Script::CStruct;
	pTempParams->AddChecksum( CRCD(0x7321a8d6,"type"), CRCD(0xdf5c091a,"fliprotate") );
    pTempParams->AddInteger( "flipped", flipped );
	this->AddController( pTempParams );
	delete pTempParams;

#if 0
	// if it's the skater, then add a procedural anim controller
	if ( GetObject()->GetID() == 0 )
	{
		pTempParams = new Script::CStruct;
		pTempParams->AddChecksum( CRCD(0x7321a8d6,"type"), CRCD(0xfdf0436c,"proceduralanim") );
		this->AddController( pTempParams );
		delete pTempParams;
	}
#endif
	
#if 0
	// if it's the skater, then add a partial anim controller
	if ( GetObject()->GetID() == 0 )
	{
		pTempParams = new Script::CStruct;
		pTempParams->AddChecksum( CRCD(0x7321a8d6,"type"), CRCD(0x659bf355,"partialanim") );
		pTempParams->AddChecksum( CRCD(0x6c2bfb7f,"animname"), Script::GenerateCRC("Taunt1") );
		pTempParams->AddFloat( CRCD(0xd16b61e6,"starttime"), 0.0f );
		pTempParams->AddFloat( CRCD(0xab81bb3d,"endtime"), 1.0f );
		pTempParams->AddInteger( CRCD(0xcd8d7c1b,"looptype"), Gfx::LOOPING_CYCLE );
		pTempParams->AddFloat( CRCD(0x8f0d24ed,"blendperiod"), 0.0f );
		pTempParams->AddFloat( CRCD(0xf0d90109,"speed"), 1.0f );
		this->AddController( pTempParams );
		delete pTempParams;
	}
#endif

	pTempParams = new Script::CStruct;
	pTempParams->AddFloat( CRCD(0x4d747fa0,"wobbleTargetAlpha"), 0.0f );
	for ( int i = 0; i < m_numControllers; i++ )
	{
		mp_controllers[i]->CallMemberFunction( CRCD(0xd0209498,"setWobbleTarget"), pTempParams, NULL );
	}
	delete pTempParams;

	pTempParams = new Script::CStruct;
	pTempParams->AddChecksum( CRCD(0x6c2bfb7f,"animname"), anim_name );
	pTempParams->AddFloat( CRCD(0xd16b61e6,"starttime"), start_time );
	pTempParams->AddFloat( CRCD(0xab81bb3d,"endtime"), end_time );
	pTempParams->AddInteger( CRCD(0xcd8d7c1b,"looptype"), loop_type );
	pTempParams->AddFloat( CRCD(0x8f0d24ed,"blendperiod"), blend_period );
	pTempParams->AddFloat( CRCD(0xf0d90109,"speed"), speed );
	for ( int i = 0; i < m_numControllers; i++ )
	{
		mp_controllers[i]->CallMemberFunction( CRCD(0xaf2fae19,"PlaySequence"), pTempParams, NULL );
	}
	delete pTempParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::delete_custom_keys()
{
	m_customAnimKeyList.DestroyAllNodes();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::add_custom_keys( uint32 animName )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	if ( !pAnimationComponent )
	{
		return;
	}

	uint32 animEventTableName = pAnimationComponent->GetAnimEventTableName();
	if ( !animEventTableName )
	{
		return;
	}

	Script::CStruct* pAnimEventTable = Script::GetStructure( animEventTableName, Script::ASSERT );
	if ( pAnimEventTable )
	{
		Script::CArray* pSubArray;
		if ( pAnimEventTable->GetArray( animName, &pSubArray, Script::NO_ASSERT ) )
		{
			for ( uint32 i = 0; i < pSubArray->GetSize(); i++ )
			{
				Script::CStruct* pSubStruct;
				pSubStruct = pSubArray->GetStructure( i );
    
				int frame = 0;
				// if the frame is not specified, then look for a time value
				if ( !pSubStruct->GetInteger( CRCD(0x4a07c332,"frame"), &frame, Script::NO_ASSERT ) )
				{
					float keyTime = 0.0f;
						pSubStruct->GetFloat( CRCD(0x906b67ba,"time"), &keyTime, Script::ASSERT );
						frame = (int)(keyTime * 60.0f);
				}

                uint32 eventType;
				pSubStruct->GetChecksum( CRCD(0xc451f558,"event"), &eventType, Script::ASSERT );

				Script::CStruct* pEventParams = NULL;
				pSubStruct->GetStructure( CRCD(0x7031f10c,"params"), &pEventParams, Script::NO_ASSERT );
                
				Gfx::CCustomAnimKey* pKey = new Gfx::CEventKey( frame, eventType, pEventParams );
				pKey->SetActive( true );
				m_customAnimKeyList.AddToTail( pKey );
			}
		}													  
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::ResetCustomKeys()
{
	int customKeyCount = m_customAnimKeyList.CountItems();
	for ( int i = 0; i < customKeyCount; i++ )
	{
		Gfx::CCustomAnimKey* pKey = (Gfx::CCustomAnimKey*)m_customAnimKeyList.GetItem(i);
		pKey->SetActive( true );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBlendChannel::ProcessCustomKeys( float startTime, float endTime )
{
	// Only fire off events on the primary anim
	if ( GetStatus() == ANIM_STATUS_ACTIVE )
	{
		startTime *= 60.0f;
		endTime *= 60.0f;

		// we've just looped, so reset the keys
		if ( m_direction == ANIM_DIR_FORWARDS )
		{
			if ( startTime > endTime )
			{
				ResetCustomKeys();
				startTime = m_startTime;
			}
		}
		else
		{
			if ( endTime > startTime )
			{
				ResetCustomKeys();
				startTime = m_startTime;
			}
		}

		int customKeyCount = m_customAnimKeyList.CountItems();
		for ( int i = 0; i < customKeyCount; i++ )
		{
			Gfx::CCustomAnimKey* pKey = (Gfx::CCustomAnimKey*)m_customAnimKeyList.GetItem(i);
			if ( pKey->WithinRange( startTime, endTime ) )
			{
//				printf( "Processing key at %f (%f %f)\n", 0.0f, startTime, endTime );
				pKey->ProcessKey( GetObject() );
			}
			else
			{
//				printf( "Not processing key at %f (%f %f)\n", custom_key_time, startTime, endTime );
			}
		}
	}
	
	// TODO:  reset the custom keys when it loops?
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAnimFunctionResult CBlendChannel::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case 0x83654874:	// AddAnimController
		{
			AddController( pParams );
			return AF_TRUE;
		}
		break;

		case 0x986d274e:	// RemoveAnimController
		{
			uint32 id;
			pParams->GetChecksum( CRCD(0x40c698af,"id"), &id, Script::ASSERT );
			CBaseAnimController* pAnimController = get_controller_by_id( id );
			if ( pAnimController )
			{
				remove_controller( pAnimController );
			}
		}
		break;
	}

	for ( int i = 0; i < m_numControllers; i++ )
	{
		EAnimFunctionResult theResult = mp_controllers[i]->CallMemberFunction( Checksum, pParams, pScript );
		
		if ( theResult != AF_NOT_EXECUTED )
		{
			return theResult;
		}
	}
	
	return AF_NOT_EXECUTED;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CCompositeObject* CBlendChannel::GetObject()
{
	return mp_object;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}


================================================
FILE: Code/Gfx/blendchannel.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       BlendChannel.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  12/12/2002
//****************************************************************************

#ifndef __GFX_BLENDCHANNEL_H__
#define __GFX_BLENDCHANNEL_H__

#include 
#include 

#include 
#include 
#include 
	
namespace Object
{
	class CObject;
}
			 
namespace Script
{
	class CScript;
	class CStruct;
}

namespace Gfx
{
	class CBlendChannel;
	class CPose;
	class CBaseAnimController;

	// NOTE: if you change this enum, update the GetDebugInfo switch statement!	
	enum EAnimStatus
	{								 
		ANIM_STATUS_INACTIVE		= 0,	// No animation playing.
		ANIM_STATUS_DEGENERATING,			// Animation playing, but being faded out.
		ANIM_STATUS_ACTIVE					// Animation playing.
	};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class CBlendChannel : public CAnimChannel
{
protected:
	// TODO:  Replace with a more efficient list structure?
	enum
	{
		vMAX_CONTROLLERS = 4
	};

public:
	CBlendChannel( Obj::CCompositeObject* pCompositeObject );
	virtual	~CBlendChannel();

public:
	CBaseAnimController*		AddController( Script::CStruct* pParams );
	bool						GetPose( Gfx::CPose* pPose );
	float						GetBlendValue();
	bool						IsActive();
	bool						IsDegenerating();
	void						InvalidateCache();
    virtual EAnimFunctionResult	CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );

public:
	virtual void				Update();
	virtual void				Reset();
	virtual void				GetDebugInfo( Script::CStruct* p_info );
	virtual void        		PlaySequence( uint32 animName, float start_time, float end_time, EAnimLoopingType loop_type, float blend_period, float speed, bool flipped );

public:
	float						GetDegenerationTime() { return m_degenerationTime; }
	float						GetDegenerationTimeToBlendMultiplier() { return m_degenerationTimeToBlendMultiplier; }
	void						SetDegenerationTime( float time ) { m_degenerationTime = time; }
	void						SetDegenerationTimeToBlendMultiplier( float mult ) { m_degenerationTimeToBlendMultiplier = mult; }
	bool						Degenerate( float blendPeriod );

protected:		
	void						add_controller( CBaseAnimController* pAnimController, int priority = 0 );
	void						remove_controllers();
	void						remove_controller( CBaseAnimController* pAnimController );
	CBaseAnimController* 		get_controller_by_id( uint32 id );

protected:
	EAnimStatus					GetStatus() const;

public:
	void						SetStatus(EAnimStatus status);
	Obj::CCompositeObject*		GetObject();

protected:
	EAnimStatus					m_status;
                       
protected:
	CBaseAnimController*		mp_controllers[vMAX_CONTROLLERS];
	int							m_numControllers;
    
	float						m_blendSpeed;
	float						m_degenerationTime;
    float						m_degenerationTimeToBlendMultiplier;

	uint32						m_animScriptName;
	Obj::CCompositeObject*		mp_object;

public:
	// for doing anim events...
	void							ProcessCustomKeys(float startTime, float endTime);
	void							ResetCustomKeys();

protected:
	void 							add_custom_keys( uint32 animName );
	void							delete_custom_keys();
	Lst::Head	m_customAnimKeyList;	
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}

#endif



================================================
FILE: Code/Gfx/camera.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       camera.cpp
//* OWNER:          Mark Burton
//* CREATION DATE:  1/31/1999
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Gfx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

const float	Camera::vDEFAULT_NEARZ		= 12.0f;
const float	Camera::vDEFAULT_FARZ		= 12000.0f;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

const float Camera::vUSE_PIXEL_RATIO	= 0.0f;

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Camera::Camera ( )
{	
	//SetFOV ( Mth::DegToRad ( 60.0f ));
	SetHFOV(Nx::CViewportManager::sGetDefaultAngle());

	m_matrix.Ident();	// Since the default constructor doesn't do this

	m_near_clip = vDEFAULT_NEARZ;
	m_far_clip = vDEFAULT_FARZ;
	m_pos.Set();
	m_old_pos.Set();
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Camera::~Camera ( void )
{   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Camera::SetHFOV(float h_fov)
{
	m_h_fov = h_fov;
	//Dbg_Message("Changed camera %x to angle %f", this, m_h_fov);

	UpdateAdjustedHFOV();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Camera::UpdateAdjustedHFOV()
{
	m_adj_h_fov = Mth::RadToDeg( 2.0f * atanf( tanf( Mth::DegToRad( m_h_fov / 2.0f ) ) * Nx::CViewportManager::sGetScreenAngleFactor() ) );
	//Dbg_Message("%x: Orig angle %f; Adjusted angle %f", this, m_h_fov, m_adj_h_fov);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float Camera::GetAdjustedHFOV()
{
	// Garrett: m_adj_h_fov may not be right, so we calculate it every time we need it now.  Will try to keep it
	// up-to-date at a later date.
	UpdateAdjustedHFOV();

	return m_adj_h_fov;
#if 0
	return Mth::RadToDeg( 2.0f * atanf( tanf( Mth::DegToRad( m_h_fov / 2.0f ) ) * Nx::CViewportManager::sGetScreenAngleFactor() ) );
#endif
}

#if 0
/******************************************************************************
 *
 * Function:		Camera::SetFocalLength ( float focal_length, float h_aspect )
 *
 * Description:		Sets the camera viewplane according to focal length
 *
 * Parameters:		focal_length = focal length of lens in meters 
 *					h_aspect = horizontal aspect scale factor
 *
 * Notes:			focal_length of 0.035 is equal to a 35mm lens.
 *
 *					You cannot set horizontally or vertically flipped
 *					viewplanes diRectly with this function
 *
 *					focal_length is clamped to a minimum value of N_EPSILON
 *
 ******************************************************************************/

void Camera::SetFocalLength ( float focal_length, float h_aspect )
{
	float fov = atanf (( 18.0f / 1000.0f) / Mth::Max ( Mth::EPSILON, focal_length )) * 2.0f;
	
	SetFOV ( fov, h_aspect );
}

/******************************************************************************
 *
 * Function:		Camera::SetViewPlane ( float h_fov, float v_fov )
 *
 * Description:		Sets the camera view plane with two FOV angles 
 *
 * Parameters:		h_fov	= horizontal FOV angle in radians [-N_PI,+N_PI]
 *					v_fov	= vertical FOV angle in radians	  [_N_PI,+N_PI]
 *
 * Notes:			The FOV angles are clamped separately, i.e. if clamping
 *					occurs due to too large an angle, the aspect ratio is
 *					affected.
 *					
 ******************************************************************************/

void Camera::SetViewPlane ( float h_fov, float v_fov )
{
	

}

/******************************************************************************
 *
 * Function:		Camera::SetFOV ( float fov, float h_aspect )
 *
 * Description:		Sets camera view plane with a field-of-view angle
 *					and an aspect ratio
 *
 * Parameters:		fov		 = FOV angle in radians
 *					h_aspect = horizontal aspect ratio ( default: vp pixel ratio )
 *					
 ******************************************************************************/

void Camera::SetFOV ( float v_fov, float h_aspect )
{
	
/*
	float x,y 
	
	v_fov = Mth::Abs ( v_fov );
	v_fov = Mth::Clamp ( v_fov, Mth::EPSILON, Mth::PI - Mth::EPSILON );
	
	v_fov = tan ( v_fov / 2.0f );
	
	h_aspect = ( h_aspect == vUSE_PIXEL_RATIO ) ? m_viewport->GetPixelRatio() : h_aspect; 

	x = v_fov * h_aspect;
	y = v_fov;
  
// Mick: x and y contain the view window x and y   
*/
    

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float Camera::GetFOV ( void )
{
	// Mick: not meaningful......
	return 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float Camera::GetFocalLength ( void )
{	
	// Mick: not meaningful......
	return 0.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float Camera::GetAspectRatio ( void )
{
	// Mick: not meaningful......
	return 0.0f;

}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Camera::StoreOldPos( void )
{
	m_old_pos = GetPos();		   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Note these are returned by value, until
// we can store them explicity in the camera, and return them by reference
void Camera::SetPos(const Mth::Vector& pos)
{
	m_pos = pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector & Camera::GetPos()
{
	return  m_pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Camera::SetMatrix(const Mth::Matrix& mat)
{
	m_matrix = mat;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Matrix & Camera::GetMatrix()
{
	return m_matrix;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Camera::CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( checksum )
	{
		case 0x44ea2b6d:		// ChangeCameraFOV
			{
				float fov;
				pParams->GetFloat( NONAME, &fov, Script::ASSERT );
				//Nx::CViewportManager::sSetScreenAngle( Mth::RadToDeg( fov ) );
				SetHFOV(Mth::RadToDeg(fov));
				return true;
			}
			break;
	}

	return Obj::CObject::CallMemberFunction( checksum, pParams, pScript );
}

} // namespace Gfx


================================================
FILE: Code/Gfx/camera.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       camera.cpp
//* OWNER:          Mark Burton
//* CREATION DATE:  11/11/1999
//****************************************************************************

#ifndef __GFX_CAMERA_H
#define __GFX_CAMERA_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#ifdef __PLAT_NGC__
#include "sys/ngc/p_camera.h"
#endif		// __PLAT_NGC__
#ifndef __GEL_OBJECT_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Script
{
	class CStruct;
}

namespace Gfx
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class Camera : public Obj::CObject
{
	
public:
	static const float vUSE_PIXEL_RATIO;

	Camera( void );
	virtual ~Camera( void );

public:	
	void			SetPos(const Mth::Vector& pos);		// Set the position of the camera
	Mth::Vector	&	GetPos();							// Return the position of the camera
	void			SetMatrix(const Mth::Matrix& mat);	// Set the orientation matrix
	Mth::Matrix	&	GetMatrix();						// Return the orientation matrix


	void			SetHFOV(float h_fov);
	float			GetHFOV() const;
	void			UpdateAdjustedHFOV();
	float			GetAdjustedHFOV();

#if 0
	void			SetFOV ( float v_fov, float h_aspect = vUSE_PIXEL_RATIO );
	void			SetViewPlane ( float h_fov, float v_fov );
	void			SetFocalLength ( float focal_length, float h_aspect = vUSE_PIXEL_RATIO );

	float			GetFOV ( void );
	float			GetFocalLength ( void );
	float			GetAspectRatio ( void );
#endif

	void			SetNearFarClipPlanes ( float nearPlane, float farPlane );
	void			SetFogNearPlane ( float nearPlane );
	
	float			GetNearClipPlane() const;
	float			GetFarClipPlane() const;

	static const float	vDEFAULT_NEARZ;
	static const float	vDEFAULT_FARZ;

	void			StoreOldPos( void );
	Mth::Vector		m_old_pos;

public:
	bool			CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript ); 

protected:  	
	Mth::Vector		m_pos;								// camera position
	Mth::Matrix		m_matrix;							// orientation matrix

	float			m_h_fov;							// horizontal field of view angle (degrees)
	float			m_adj_h_fov;						// screen adjusted horizontal field of view angle (degrees)

	float			m_near_clip;						// near clip plane
	float			m_far_clip;							// far clip plane
};

/*****************************s************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float Camera::GetHFOV() const
{
	return m_h_fov;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void Camera::SetNearFarClipPlanes ( float nearPlane, float farPlane ) 
{
	Dbg_MsgAssert( farPlane > 0.0f,( "Far Plane <= 0" ));
	Dbg_MsgAssert( nearPlane > 0.0f,( "Near Plane <= 0" ));
	Dbg_MsgAssert( farPlane > nearPlane,( "Far Plane <= Near Plane" ));

	m_far_clip = farPlane;
	m_near_clip = nearPlane;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void Camera::SetFogNearPlane ( float nearPlane ) 
{
//	printf ("STUBBED:  SetFogNearPlane\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float Camera::GetNearClipPlane() const
{
	return m_near_clip;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline float Camera::GetFarClipPlane() const
{
	return m_far_clip;
}

} // namespace Gfx

#endif	// __GFX_CAMERA_H


================================================
FILE: Code/Gfx/debuggfx.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			GFX														**
**																			**
**	File name:		debuggfx.cpp											**
**																			**
**	Created by:		11/01/00	-	mjd										**
**																			**
**	Description:	Draws debug lines, other debug graphics					**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 


#include 
#include 
#include 				// Including for debugging
#include 
#include 
#include 

#include 			// for camera

#ifdef	__PLAT_NGPS__	
#include 
#include 
namespace Sys
{
	extern void			box(int x0,int y0, int x1, int y1, uint32 color);
}
#endif

#ifdef	__PLAT_XBOX__
#include 
#include 
#endif

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Gfx
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#ifdef	__PLAT_NGPS__
#define DEBUGGERY_LINE_ARRAY_SIZE		10000			// * 48 bytes = 4.8MB off the debug heap
#else
#define DEBUGGERY_LINE_ARRAY_SIZE		2048
#endif
/*****************************************************************************
**								Private Types								**
*****************************************************************************/

struct SDebuggeryLine{
	Mth::Vector			v0, v1;
	unsigned int	rgb0, rgb1;
	int				in_use;
	int				num_draw_frames;
};

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

#ifndef __PLAT_NGC__
SDebuggeryLine *DebuggeryLineArray = NULL;		//[ DEBUGGERY_LINE_ARRAY_SIZE ];
#endif		// __PLAT_NGC__

#ifdef	__DEBUG_CODE__
static bool	sActive = false;
#endif
/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

void DebuggeryLines_Draw( void );
void DebuggeryLines_CleanUp( void );

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


void DebuggeryLines_CleanUp( void )
{
#ifdef	__DEBUG_CODE__
#ifndef __PLAT_NGC__
	if (!DebuggeryLineArray)
	{
		// DOn't bother if not allocated yet
		return;
	}
	
	int i;
	
	for ( i = 0; i < DEBUGGERY_LINE_ARRAY_SIZE; i++ )
	{
		DebuggeryLineArray[ i ].in_use = 0;
	}
#endif		// __PLAT_NGC__
#endif
}

#ifndef __PLAT_NGC__
#ifdef	__DEBUG_CODE__
static bool DebuggeryLinesInitialized = 0;
#endif
#endif		// __PLAT_NGC__


void DebuggeryLines_Draw( void )
{
#ifdef	__DEBUG_CODE__


	if (!sActive)
	{
		return;
	}
	
	sActive = false;
#	if defined( __PLAT_NGPS__ )

	Mth::Vector	cam_fwd;
	
	if (Nx::CViewportManager::sGetActiveCamera())
	{
		cam_fwd = Nx::CViewportManager::sGetActiveCamera()->GetMatrix()[Z];
	}
	else
	{
		printf("WARNING: called Gfx::DebuggeryLines_Draw without a camera\n");
		return;
	}

	// can remove this when this module is properly initialized:
	int i;
	uint32	last_color = 0x33333333;	 	// the dodginess of my code astounds me!

	if ( !DebuggeryLinesInitialized )
	{
		DebuggeryLines_CleanUp( );
		DebuggeryLinesInitialized = 1;
	}

	SDebuggeryLine* p_debugline = DebuggeryLineArray;	
	for ( i = DEBUGGERY_LINE_ARRAY_SIZE; i ; i-- )
	{
		if ( p_debugline->in_use )
		{
		
			NxPs2::DMAOverflowOK = 2;
		
			if ( last_color != p_debugline->rgb0)
			{
				if (last_color != 0x33333333)
				{
					NxPs2::ChangeLineColor(0x80000000 + (0x00ffffff & p_debugline->rgb0));
				}			
				else
				{
					NxPs2::BeginLines3D(0x80000000 + (0x00ffffff & p_debugline->rgb0));
				}
				last_color = p_debugline->rgb0;
			}
		
			sActive = true;
			NxPs2::DrawLine3D(p_debugline->v0[X],
					   p_debugline->v0[Y],
					   p_debugline->v0[Z],
					   p_debugline->v1[X],
					   p_debugline->v1[Y],
					   p_debugline->v1[Z]);
			NxPs2::DrawLine3D(p_debugline->v0[X],
					   p_debugline->v0[Y],
					   p_debugline->v0[Z],
					   p_debugline->v1[X],
					   p_debugline->v1[Y],
					   p_debugline->v1[Z]);


			if ( p_debugline->in_use == 2 )
			{
					
				// Calculate and draw an arrowhead 
				// 1/4th the length, at ~30 degrees					   
				Mth::Vector	a = p_debugline->v0;
				Mth::Vector b = p_debugline->v1;
				Mth::Vector	ab = (b-a);
				ab /= 4.0f;
				Mth::Vector out;
				out = ab;
				out.Normalize();
				out = Mth::CrossProduct(out, cam_fwd);
				out *= ab.Length()/3.0f;			
				
				Mth::Vector left =  b - ab + out;
				Mth::Vector right = b - ab - out;
	
				NxPs2::DrawLine3D(left[X],left[Y],left[Z],b[X],b[Y],b[Z]);
				NxPs2::DrawLine3D(right[X],right[Y],right[Z],b[X],b[Y],b[Z]);
				NxPs2::DrawLine3D(right[X],right[Y],right[Z],left[X],left[Y],left[Z]);	 // crossbar
				// have to draw it twice for some stupid reason	(final segment of a particular color is not rendered)
				NxPs2::DrawLine3D(left[X],left[Y],left[Z],b[X],b[Y],b[Z]);
				NxPs2::DrawLine3D(right[X],right[Y],right[Z],b[X],b[Y],b[Z]);
				NxPs2::DrawLine3D(right[X],right[Y],right[Z],left[X],left[Y],left[Z]);
				
			}			   
					   
					   
			if ( p_debugline->num_draw_frames )
			{
				p_debugline->num_draw_frames--;
				if ( !p_debugline->num_draw_frames )
					p_debugline->in_use = false;
			}
		}
		p_debugline++;
	}

	if (last_color != 0x33333333)
	{
		NxPs2::EndLines3D();
	}			
#	elif defined( __PLAT_XBOX__ )
	struct sLineVert
	{
		D3DVECTOR	pos;
		D3DCOLOR	col;
	};

	static sLineVert	line_verts[DEBUGGERY_LINE_ARRAY_SIZE * 2];
	uint32				last_color = 0x33333333;
	uint32				index = 0;
	if( !DebuggeryLinesInitialized )
	{
		DebuggeryLines_CleanUp();
		DebuggeryLinesInitialized = 1;
	}
	
	for( int i = 0; i < DEBUGGERY_LINE_ARRAY_SIZE; ++i )
	{
		Dbg_Assert( index < DEBUGGERY_LINE_ARRAY_SIZE * 2 );
		if( DebuggeryLineArray[i].in_use )
		{
			if( DebuggeryLineArray[ i ].num_draw_frames )
			{
				DebuggeryLineArray[ i ].num_draw_frames--;
				if( !DebuggeryLineArray[i].num_draw_frames )
					DebuggeryLineArray[i].in_use = false;
			}
			sActive = true;
			
			// Need to switch rgba to bgra.
			uint32 rgb0 = ( DebuggeryLineArray[i].rgb0 & 0xFF00FF00UL ) | (( DebuggeryLineArray[i].rgb0 & 0x00FF0000UL ) >> 16 ) |(( DebuggeryLineArray[i].rgb0 & 0x000000FFUL ) << 16 );
			uint32 rgb1 = ( DebuggeryLineArray[i].rgb1 & 0xFF00FF00UL ) | (( DebuggeryLineArray[i].rgb1 & 0x00FF0000UL ) >> 16 ) |(( DebuggeryLineArray[i].rgb1 & 0x000000FFUL ) << 16 );
			
			line_verts[index].col	= rgb0;
			line_verts[index].pos.x	= DebuggeryLineArray[i].v0[X];
			line_verts[index].pos.y	= DebuggeryLineArray[i].v0[Y];
			line_verts[index].pos.z	= DebuggeryLineArray[i].v0[Z];
			++index;
			line_verts[index].col	= rgb1;
			line_verts[index].pos.x	= DebuggeryLineArray[i].v1[X];
			line_verts[index].pos.y	= DebuggeryLineArray[i].v1[Y];
			line_verts[index].pos.z	= DebuggeryLineArray[i].v1[Z];
			++index;
		}
	}
	
	if( index > 0 )
	{
		NxXbox::set_texture( 0, NULL );
		NxXbox::set_blend_mode( NxXbox::vBLEND_MODE_DIFFUSE );
		NxXbox::set_vertex_shader( D3DFVF_XYZ | D3DFVF_DIFFUSE );
		NxXbox::set_pixel_shader( 0 );
		HRESULT hr = NxXbox::EngineGlobals.p_Device->DrawPrimitiveUP( D3DPT_LINELIST, index / 2, line_verts, sizeof( sLineVert ));
	}
#	endif
#endif
}  // end of DebuggeryLines_Draw( )

 
// Draw a bunch of 2D rectangles that we added to the list this frame. 
// currently used for the framerate indicator.


void DebuggeryRects_Draw( float time)
{

#ifdef	__DEBUG_CODE__

	#ifdef	__PLAT_NGPS__
	#ifdef		__USE_PROFILER__
	
						  
		if (Script::GetInteger(CRCD(0xd9859988,"Display_framerate_box")) == 0)
		{
			return;
		}
						  
		if (time < 0.016667f)
		{
			return;
		}
		
		if (Config::CD())
		{
			return;
		}

		int size = (int) (time * 400); 

		
		int x = 70-size;
		int y = 200-size;
		int w = size*2;
		int h = size*2;

		
		Sys::box( x,y,x+w,y+h, 0x0000ff);
		Sys::box( x+w/4,y+h/4,x+w*3/4,y+h*3/4, 0xffffff);
	
	#endif
	#endif
#endif

}


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

// given a movement vector v0, then move all the debug lines by this amount
// (this allows us to see the lines relative to a moving object)
void AdjustDebugLines( const Mth::Vector &v0)
{
#ifdef	__DEBUG_CODE__

#ifndef __PLAT_NGC__
	if (!DebuggeryLineArray)
	{
		// DOn't bother if not allocated yet
		return;
	}

	for (int i= 0; i< DEBUGGERY_LINE_ARRAY_SIZE; i++ )
	{
		if ( DebuggeryLineArray[ i ].in_use )
		{
			DebuggeryLineArray[ i ].v0 += v0;
			DebuggeryLineArray[ i ].v1 += v0;
		}
	}

#endif		// __PLAT_NGC__
#endif
}


/*	Adds a line to our debug line buffer, to be drawn every frame
	until the buffer wraps around again...
*/						   
void AddDebugLine( const Mth::Vector &v0, const Mth::Vector &v1, int rgb0, int rgb1, int numDrawFrames )
{
#ifdef	__DEBUG_CODE__
#ifndef __PLAT_NGC__
	
    static int DebuggeryLineIndex = 0;
	
	if (!DebuggeryLineArray)
	{
		if (!Config::GotExtraMemory())
		{
			return;
		}
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
		DebuggeryLineArray  = (SDebuggeryLine *) Mem::Malloc(DEBUGGERY_LINE_ARRAY_SIZE * sizeof (SDebuggeryLine));
		Mem::Manager::sHandle().PopContext();
		DebuggeryLines_CleanUp( );	  //initialize it
		
	}
	
//	Dbg_MsgAssert((v0-v1).Length() < 1000,("Suspiciously long line...."));

	if ( !DebuggeryLinesInitialized )
	{
		DebuggeryLines_CleanUp( );
		DebuggeryLinesInitialized = 1;
	}

	if ( rgb1 == SAME_COLOR_AS_RGB0_PLEASE )
	{
		rgb1 = rgb0;
	}
	
	DebuggeryLineArray[ DebuggeryLineIndex ].v0 = v0;
	
	DebuggeryLineArray[ DebuggeryLineIndex ].v1 = v1;
													 
	DebuggeryLineArray[ DebuggeryLineIndex ].rgb0 = rgb0;
	DebuggeryLineArray[ DebuggeryLineIndex ].rgb1 = rgb1;

	DebuggeryLineArray[ DebuggeryLineIndex ].in_use = 1;
	
	DebuggeryLineArray[ DebuggeryLineIndex ].num_draw_frames = numDrawFrames;
															  
	if ( ++DebuggeryLineIndex >= DEBUGGERY_LINE_ARRAY_SIZE )
	{
		DebuggeryLineIndex = 0;
	}
	
	sActive = true;
	
#endif		// __PLAT_NGC__
#endif
} // end of AddDebugLine( )


void AddDebugArrow( const Mth::Vector &v0, const Mth::Vector &v1, int rgb0, int rgb1, int numDrawFrames )
{
#ifdef	__DEBUG_CODE__
#ifndef __PLAT_NGC__
	
    static int DebuggeryLineIndex = 0;
	
	if (!DebuggeryLineArray)
	{
		if (!Config::GotExtraMemory())
		{
			return;
		}
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
		DebuggeryLineArray  = (SDebuggeryLine *) Mem::Malloc(DEBUGGERY_LINE_ARRAY_SIZE * sizeof (SDebuggeryLine));
		Mem::Manager::sHandle().PopContext();
		DebuggeryLines_CleanUp( );	  //initialize it
		
	}
	
//	Dbg_MsgAssert((v0-v1).Length() < 1000,("Suspiciously long line...."));

	if ( !DebuggeryLinesInitialized )
	{
		DebuggeryLines_CleanUp( );
		DebuggeryLinesInitialized = 1;
	}

	if ( rgb1 == SAME_COLOR_AS_RGB0_PLEASE )
	{
		rgb1 = rgb0;
	}
	
	DebuggeryLineArray[ DebuggeryLineIndex ].v0 = v0;
	
	DebuggeryLineArray[ DebuggeryLineIndex ].v1 = v1;
													 
	DebuggeryLineArray[ DebuggeryLineIndex ].rgb0 = rgb0;
	DebuggeryLineArray[ DebuggeryLineIndex ].rgb1 = rgb1;

	DebuggeryLineArray[ DebuggeryLineIndex ].in_use = 2;
	
	DebuggeryLineArray[ DebuggeryLineIndex ].num_draw_frames = numDrawFrames;
															  
	if ( ++DebuggeryLineIndex >= DEBUGGERY_LINE_ARRAY_SIZE )
	{
		DebuggeryLineIndex = 0;
	}
	
	sActive = true;
	
#endif		// __PLAT_NGC__
#endif
} // end of AddDebugLine( )


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void AddDebugStar(Mth::Vector v0, float r, int rgb0, int numDrawFrames)
{
#ifdef	__DEBUG_CODE__
	Mth::Vector v1,v2;
	
	v1 = v0; v2 = v0; v1[X]+=r; v2[X]-=r; AddDebugLine(v1,v2,rgb0,rgb0,numDrawFrames);
	v1 = v0; v2 = v0; v1[Y]+=r; v2[Y]-=r; AddDebugLine(v1,v2,rgb0,rgb0,numDrawFrames);
	v1 = v0; v2 = v0; v1[Z]+=r; v2[Z]-=r; AddDebugLine(v1,v2,rgb0,rgb0,numDrawFrames);
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void AddDebugCircle(Mth::Vector v0, int numPoints, float r, int rgb0, int numDrawFrames)
{
#ifdef	__DEBUG_CODE__
	Mth::Vector v1, v2;

	Mth::Vector fwd( 0.0f, 0.0f, r );

	v1 = v0 + fwd;

	for ( int i = 0; i < numPoints; i++ )
	{
		fwd.RotateY( 2.0f * Mth::PI / (float)numPoints );
		v2 = v0 + fwd;

		AddDebugLine(v1,v2,rgb0,rgb0,numDrawFrames);
		
		v1 = v2;
	}
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void AddDebugBone( const Mth::Vector& p1, const Mth::Vector& p2, float red, float green, float blue )
{
#ifdef	__DEBUG_CODE__
	// GJ:  Pretty much the same as AddDebugLine, but with 
	// a more convenient interface for the skeleton code

	Gfx::AddDebugLine( p1, p2,
					   MAKE_RGB( (int)(red * 255), (int)(green * 255), (int)(blue * 255) ),
					   MAKE_RGB( (int)(red * 255), (int)(green * 255), (int)(blue * 255) ),
					   1 );
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void AddDebugBox( const Mth::Matrix& root, const Mth::Vector& pos, SBBox *pBox, Mth::Vector *pOffset, int numFrames, Mth::Vector *pRot, int rgb )
{
#ifdef	__DEBUG_CODE__
	
	if (Config::CD())
	{
		return;
	}
		
	Mth::Vector box[ 8 ];
	Mth::Matrix m;
	int i;

	m = root;
	if ( pRot )
	{
		m.RotateLocal( *pRot );
	}

	box[ 0 ] = box[ 1 ] = box[ 2 ] = box[ 3 ] = pBox->m_max;

	box[ 1 ][ X ] = pBox->m_min[ X ];
	box[ 2 ][ Y ] = pBox->m_min[ Y ];
	box[ 3 ][ Z ] = pBox->m_min[ Z ];
	
	box[ 5 ] = box[ 6 ] = box[ 7 ] = box[ 4 ] = pBox->m_min;
	
	box[ 5 ][ X ] = pBox->m_max[ X ];
	box[ 6 ][ Y ] = pBox->m_max[ Y ];
	box[ 7 ][ Z ] = pBox->m_max[ Z ];
	
	
	for ( i = 0; i < 8; i++ )
	{
//		float tempY = box[ i ][ Y ];
//		box[ i ][ Y ] = box[ i ][ Z ];
//		box[ i ][ Z ] = -tempY;
		if ( pOffset )
		{
			box[ i ] += *pOffset;
		}
		box[ i ] = m.Transform( box[ i ] );
		box[ i ] += pos;
	}

	for ( i = 1; i < 4; i++ )
	{
		Gfx::AddDebugLine( box[ 0 ], box[ i ], rgb, rgb, numFrames );
	}
	for ( i = 5; i < 8; i++ )
	{
		Gfx::AddDebugLine( box[ 4 ], box[ i ], rgb, rgb, numFrames );
	}
	// fill in the cracks:
	Gfx::AddDebugLine( box[ 1 ], box[ 6 ], rgb, rgb, numFrames );
	Gfx::AddDebugLine( box[ 1 ], box[ 7 ], rgb, rgb, numFrames );

	Gfx::AddDebugLine( box[ 2 ], box[ 5 ], rgb, rgb, numFrames );
	Gfx::AddDebugLine( box[ 2 ], box[ 7 ], rgb, rgb, numFrames );

	Gfx::AddDebugLine( box[ 3 ], box[ 5 ], rgb, rgb, numFrames );
	Gfx::AddDebugLine( box[ 3 ], box[ 6 ], rgb, rgb, numFrames );
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void DebugGfx_Draw( void )
{	
#ifdef	__DEBUG_CODE__
	DebuggeryLines_Draw( );
   
//	Mdl::FrontEnd* p_frontend = Mdl::FrontEnd::Instance();
//	if( !p_frontend->GamePaused())
	{
		DebuggeryRects_Draw(Tmr::FrameLength());	 
	}	
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void DebugGfx_CleanUp( void )
{
#ifdef	__DEBUG_CODE__
	
	
	DebuggeryLines_CleanUp( );
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx






================================================
FILE: Code/Gfx/debuggfx.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			GFX														**
**																			**
**	File name:		debuggfx.h												**
**																			**
**	Created: 		11/01/00	-	mjd										**
**																			**
*****************************************************************************/

#ifndef	__DEBUG_GFX_H
#define	__DEBUG_GFX_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/


#include 
#include 
#include 
#ifndef __GEL_OBJECT_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Gfx
{

// use this macro to create the last two params to AddDebugLine( )
#define MAKE_RGBA( r, g, b, a )	( ( ( a ) << 24 ) | ( ( b ) << 16 ) | ( ( g ) << 8 ) | ( r ) )
#define MAKE_RGB( r, g, b )		( ( ( 255 ) << 24 ) | ( ( b ) << 16 ) | ( ( g ) << 8 ) | ( r ) )
#define GET_R( rgba ) 	( ( ( rgba ) ) & 255 )
#define GET_G( rgba ) 	( ( ( rgba ) >> 8 ) & 255 )
#define GET_B( rgba ) 	( ( ( rgba ) >> 16 ) & 255 )
#define GET_A( rgba ) 	( ( ( rgba ) >> 24 ) & 255 )
										
/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

// if i put them here, would they be private any more?

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

#define SAME_COLOR_AS_RGB0_PLEASE ( 1 << 31 )

/*	Adds a line to our debug line buffer, to be drawn every frame
	until the buffer wraps around again.
*/						   
void AddDebugStar(Mth::Vector v0, float r = 12.0f, int rgb0 = MAKE_RGB( 127, 0, 127 ), int numDrawFrames = 0);

void AddDebugCircle(Mth::Vector v0, int numPoints, float r, int rgb0, int numDrawFrames);

void AdjustDebugLines( const Mth::Vector &v0);
void AddDebugLine( const Mth::Vector &v0, const Mth::Vector &v1, int rgb0 = MAKE_RGB( 127, 127, 127 ), int rgb1 = SAME_COLOR_AS_RGB0_PLEASE, int numDrawFrames = 0 );
void AddDebugArrow( const Mth::Vector &v0, const Mth::Vector &v1, int rgb0 = MAKE_RGB( 127, 127, 127 ), int rgb1 = SAME_COLOR_AS_RGB0_PLEASE, int numDrawFrames = 0 );

// For drawing skeleton
void AddDebugBone( const Mth::Vector& p1, const Mth::Vector& p2, float red = 1.0f, float green = 0.5f, float blue = 0.5f );

// For drawing bounding box
void AddDebugBox( const Mth::Matrix& root, const Mth::Vector& pos, SBBox *pBox, Mth::Vector *pOffset, int numFrames, Mth::Vector *pRot, int rgb = MAKE_RGB( 200, 0, 0 ) );

// Call every frame from the renderer.
void DebugGfx_Draw( void );

// Cleanup code for this module:
void DebugGfx_CleanUp( void );

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Gfx

#endif	// __DEBUG_GFX_H



================================================
FILE: Code/Gfx/gfxman.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics (GFX)		 									**
**																			**
**	File name:		gfxman.cpp												**
**																			**
**	Created:		07/26/99	-	mjb										**
**																			**
**	Description:	Graphics device manager									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/


#include 
#include 

#include 
#include 

#include 
#include 
#include 

#include 
#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

Dbg_DefineProject ( GfxLib, "Graphics Library" )

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

uint32 Gfx_LastVBlank = 0;

namespace Gfx
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "Gfx Manager" )

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


void		Manager::s_timer_code( const Tsk::Task< Manager >& task )
{
	
	
	
	Dbg_AssertType ( &task, Tsk::Task< Manager > );

	Manager& gfx_manager = task.GetData();

	gfx_manager.m_time += (Tmr::Time) (int) (( Tmr::FrameLength() * 60.0f ) * 
										( Tmr::vRESOLUTION / Config::FPS() ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#ifdef __PLAT_NGC__
int gDumpMem = 0;
#endif		// __PLAT_NGC__

void		Manager::s_start_render_code ( const Tsk::Hook< Manager >& hook )
{
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::s_end_render_code ( const Tsk::Hook< Manager >& hook )
{

// Note: not currently called.... just left in to show the timing stuff....
	
	Dbg_AssertType ( &hook, Tsk::Hook< Manager > );
	Manager&	gfx_manager = hook.GetData();
    
	uint64 this_vblank;
	
	do
	{
		this_vblank = Tmr::GetVblanks();
	}
	while(( this_vblank - Gfx_LastVBlank ) < gfx_manager.m_min_vblank_wait );
	Gfx_LastVBlank = this_vblank;

	Tmr::OncePerRender();   		// update the frame counter
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::start_engine( void )
{
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::stop_engine( void )
{

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/



Manager::Manager ( void )
{
	

	Mlp::Manager * mlp_man = Mlp::Manager::Instance();

	m_render_start_hook = new Tsk::Hook< Manager > ( s_start_render_code, *this );
	mlp_man->RegisterRenderStartHook ( m_render_start_hook );

	m_render_end_hook = new Tsk::Hook< Manager > ( s_end_render_code, *this );
	mlp_man->RegisterRenderEndHook ( m_render_end_hook );

	m_timer_task = new Tsk::Task< Manager > ( s_timer_code, *this );
	m_timer_task->SetMask(1<<3);
	mlp_man->AddLogicTask( *m_timer_task );
		
	m_min_vblank_wait = 0;

	m_metrics_active = false;
	m_vram_viewer_active = false;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager ( void )
{
	
	
	Dbg_AssertType ( m_render_start_hook, Tsk::Hook< Manager > );
	delete m_render_start_hook;

	Dbg_AssertType ( m_render_end_hook, Tsk::Hook< Manager > );
	delete m_render_end_hook;

	Dbg_AssertType( m_timer_task, Tsk::Task< Manager > );
	delete m_timer_task;

#ifdef __NOPT_ASSERT__
	Dbg_SetScreenAssert( false );
#endif

	stop_engine();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void	Manager::ToggleMetrics( void )
{
	
	
	m_metrics_active = !m_metrics_active;

#ifdef		__USE_PROFILER__
	if( m_metrics_active )
	{
		Sys::Profiler::sEnable();
	}
	else
	{
		Sys::Profiler::sDisable();
	}
#endif
}

#ifdef __NOPT_ASSERT__
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::AssertText ( int line, const char* text )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::AssertFlush( void )
{
// not needed...	

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#endif /* __NOPT_ASSERT__ */

} // namespace Gfx


================================================
FILE: Code/Gfx/gfxman.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics  (GFX)											**
**																			**
**	File name:		gfx/gfxman.h											**
**																			**
**	Created: 		07/26/99	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __GFX_GFXMAN_H
#define __GFX_GFXMAN_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/


#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#include 
#include 

#include 


			  
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define USE_SKIN

namespace Gfx
{

#define OBJECT_RENDER_FLAG_BASIC_ATOMIC		( 1 << 0 )
#define OBJECT_RENDER_FLAG_SKIN_ATOMIC		( 1 << 1 )
#define OBJECT_RENDER_FLAG_PARTICLE_ATOMIC	( 1 << 2 )
#define OBJECT_RENDER_FLAG_HUD_ATOMIC		( 1 << 3 )



/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/
     
class  Manager  : public Spt::Class
{
	
	
public :
		

//	static const int vDEFAULT_VIDEO_HEIGHT = SCREEN_CONV_Y( 448 );
// Mick, this only applies to the virtual viewport, so don't convert for PAL	
	static const int vDEFAULT_VIDEO_HEIGHT = ( 448 );

//	static 	Image::RGBA				sBackgroundColor; 


	void						ToggleMetrics( void );
	void						ToggleVRAMViewer( void );
	void						DumpVRAMUsage( void );
	void						SetTrivialFarZClip( bool on )	{ m_trivial_far_z_clip = on; }

//	Metrics*					GetMetrics( void );
#ifdef __NOPT_ASSERT__
	void						AssertText( int line, const char* text );
	void						AssertFlush( void );
#endif

#	ifdef __PLAT_XBOX__
	void						SetGammaNormalized( float fr, float fg, float fb );
	void						GetGammaNormalized( float *fr, float *fg, float *fb );
#	endif

	void						ScreenShot( const char *fileroot );
	void						DumpMemcardScreeenshots();
	

	void						SetMinVblankWait( int num_vblanks );

	// used by other graphics system to update displays
	// This is used instead of GetTime() because this will
	// stop incrementing when the game is paused
	Tmr::Time					GetGfxTime( void ) {return m_time; }
													
private :

								~Manager( void );
								Manager( void );
	
	static	Tsk::Hook< Manager >::Code	s_start_render_code;
	static	Tsk::Hook< Manager >::Code	s_end_render_code;
	static	Tsk::Task< Manager >::Code	s_timer_code;

	Tsk::Hook< Manager >*		m_render_start_hook;
	Tsk::Hook< Manager >*		m_render_end_hook;
	Tsk::Task< Manager >*		m_timer_task;

	void						start_engine( void );
	void						stop_engine( void );
	
//	Metrics*					m_metrics;
	bool						m_metrics_active;
	bool						m_vram_viewer_active;
	bool						m_trivial_far_z_clip;


	uint32						m_min_vblank_wait;
	Tmr::Time					m_time;	// used by other graphics system to update displays
										// This is used instead of GetTime() because this will
										// stop incrementing when the game is paused
	
	DeclareSingletonClass(Manager)

};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/
	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void	Manager::ToggleVRAMViewer( void )
{
	
	
	m_vram_viewer_active = !m_vram_viewer_active;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline	void	Manager::SetMinVblankWait( int num_vblanks )
{
	m_min_vblank_wait = num_vblanks;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx

#endif	// __GFX_GFXMAN_H


================================================
FILE: Code/Gfx/gfxutils.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       GfxUtils.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  02/01/2001
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Gfx
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   PrivateFunctions								**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v )
{
	float min, max, delta;
	min = Mth::Min3( r, g, b );
	max = Mth::Max3( r, g, b );
	*v = max;				// v
	delta = max - min;
	if( max != 0.0f )
		*s = delta / max;		// s
	else {
		// r = g = b = 0		// s = 0, v is undefined
		*s = 0.0f;
		*h = -1.0f;
		return;
	}

	// GJ:
	if (delta == 0.0f)
		return;

	if( r == max )
		*h = ( g - b ) / delta;		// between yellow & magenta
	else if( g == max )
		*h = 2.0f + ( b - r ) / delta;	// between cyan & yellow
	else
		*h = 4.0f + ( r - g ) / delta;	// between magenta & cyan
	*h *= 60.0f;				// degrees
	if( *h < 0.0f )
		*h += 360.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void HSVtoRGB( float *r, float *g, float *b, float h, float s, float v )
{
	int i;
	float f, p, q, t;
	if( s == 0.0f ) {
		// achromatic (grey)
		*r = *g = *b = v;
		return;
	}
	h /= 60.0f;			// sector 0 to 5
	i = (int)h;			// basically, the floor
	f = h - i;			// factorial part of h
	p = v * ( 1.0f - s );
	q = v * ( 1.0f - s * f );
	t = v * ( 1.0f - s * ( 1.0f - f ) );
	switch( i ) {
		case 0:
			*r = v;
			*g = t;
			*b = p;
			break;
		case 1:
			*r = q;
			*g = v;
			*b = p;
			break;
		case 2:
			*r = p;
			*g = v;
			*b = t;
			break;
		case 3:
			*r = p;
			*g = q;
			*b = v;
			break;
		case 4:
			*r = t;
			*g = p;
			*b = v;
			break;
		default:		// case 5:
			*r = v;
			*g = p;
			*b = q;
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float FRAMES_TO_TIME(int frames)
{
	return (frames / 60.0f);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int TIME_TO_FRAMES(float time)
{
	return (int)(time * 60.0f + 0.5f);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void GetModelFromFileName ( char* filename, char* pModelNameBuf )
{
	char* pModelName = NULL;
	while (*filename)
	{
		if (*filename == '\\' || *filename == '/')
		{
			pModelName = filename + 1;
		}
		filename++;
	}

	Dbg_MsgAssert ( pModelName,( "Not full path name" ));

	strcpy(pModelNameBuf, pModelName);

	// strip out extension
	for (unsigned int i = 0; i < strlen(pModelNameBuf); i++)
	{
		if (pModelNameBuf[i] == '.')
		{
			pModelNameBuf[i] = 0;
			break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool GetScaleFromParams( Mth::Vector* pScaleVector, Script::CStruct* pParams )
{
	if ( pParams->ContainsComponentNamed( CRCD(0x13b9da7b,"scale") ) )
	{
		float scaleValue;
		if ( pParams->GetFloat( CRCD(0x13b9da7b,"scale"), &scaleValue, Script::NO_ASSERT ) )
		{
			*pScaleVector = Mth::Vector( scaleValue, scaleValue, scaleValue );
			return true;
		}
		else if ( pParams->GetVector( CRCD(0x13b9da7b,"scale"), pScaleVector, Script::NO_ASSERT ) )
		{
			return true;
		}
		else
		{
			Dbg_MsgAssert( 0, ( "Scale should be either a float or a vector" ) );
		}
	}
		
	bool xFound = pParams->GetFloat( CRCD(0x7323e97c,"x"), &(*pScaleVector)[X], false );
	bool yFound = pParams->GetFloat( CRCD(0x424d9ea,"y"), &(*pScaleVector)[Y], false );
	bool zFound = pParams->GetFloat( CRCD(0x9d2d8850,"z"), &(*pScaleVector)[Z], false );

	return ( xFound || yFound || zFound );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool GetLoopingTypeFromParams( Gfx::EAnimLoopingType* pLoopingType, Script::CStruct* pParams )
{
	*pLoopingType = Gfx::LOOPING_HOLD;
	
	if ( pParams->ContainsFlag( CRCD(0x4f792e6c,"Cycle") ) )
	{
		*pLoopingType=Gfx::LOOPING_CYCLE;
	}
	else if ( pParams->ContainsFlag( CRCD(0x3153e314,"PingPong") ) )
	{
		*pLoopingType=Gfx::LOOPING_PINGPONG;
	}
	else if ( pParams->ContainsFlag( CRCD(0x6d941203,"Wobble") ) )
	{
		*pLoopingType=Gfx::LOOPING_WOBBLE;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool GetTimeFromParams( float* pStart, float* pEnd, float Current, float Duration, Script::CStruct* pParams, Script::CScript* pScript )
{
	float From = 0.0f;
	float To = Duration;

	uint32 FromChecksum=0;
	if ( pParams->GetChecksum( CRCD(0x46e55e8f,"From"), &FromChecksum ) )
	{
		switch (FromChecksum)
		{
		case 0x6086aa70: // Start
			From=0;
			break;
		case 0xff03cc4e: // End
			From=Duration;
			break;
		case 0x230ccbf4: // Current
			From=Current;
			break;
		case 0x617fe530: // Middle
			From=Duration / 2.0f;
			break;	
		default:
			Dbg_MsgAssert(0,("\n%s\nUnrecognized value '%s' for From in PlayAnim",pScript?pScript->GetScriptInfo():"???",Script::FindChecksumName(FromChecksum)));
			break;
		}
	}		
	
	uint32 ToChecksum=0;
	if (pParams->GetChecksum( CRCD(0x28782d3b,"To"), &ToChecksum) )
	{
		switch (ToChecksum)
		{
		case 0x6086aa70: // Start
			To=0;
			break;
		case 0xff03cc4e: // End
			To=Duration;
			break;
		case 0x230ccbf4: // Current
			To=Current;
			break;	
		case 0x617fe530: // Middle
			To=Duration / 2.0f;
			break;	
		default:
			Dbg_MsgAssert(0,("\n%s\nUnrecognized value '%s' for To in PlayAnim",pScript?pScript->GetScriptInfo():"???",Script::FindChecksumName(ToChecksum)));
			break;
		}
	}		
	
	// also check if From and To were specified as integers, in which case use units of 60ths.
	float FromFrames=0;
	if (pParams->GetFloat( CRCD(0x46e55e8f,"From"), &FromFrames ) )
	{
		if ( pParams->ContainsFlag( CRCD(0xd029f619,"seconds") ) )
		{
			From = FromFrames;
		}
		else
		{
			From = Gfx::FRAMES_TO_TIME((int)FromFrames);
		}
	}
		
	float ToFrames=0;
	if ( pParams->GetFloat( CRCD(0x28782d3b,"To"), &ToFrames ) )
	{
		if ( pParams->ContainsFlag( CRCD(0xd029f619,"seconds") ) )
		{
			To = ToFrames;
		}
		else
		{
			To = Gfx::FRAMES_TO_TIME((int)ToFrames);
		}
	}
	
	if ( pParams->ContainsFlag( CRCD(0xf8cfd515,"Backwards") ) )
	{
		float Temp=From;
		From=To;
		To=Temp;
	}	

	*pStart = From;
	*pEnd = To;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Str::String GetModelFileName(const char* pName, const char* pExtension)
{
	char fullName[512];

	if ( strstr( pName, "/" ) || strstr( pName, "\\" ) )
	{
		Dbg_MsgAssert( strstr( pName, "." ), ( "Filename %s is missing extension", pName ) ); 
		
		sprintf( fullName, "models\\%s", pName ); 
	}
	else
	{
		sprintf( fullName, "models\\%s\\%s%s", pName, pName, pExtension );
	}

#ifdef __NOPT_ASSERT__
	// replace all forward slashes with backslashes
	char temp[512];
	strcpy( temp, fullName );
	char* pString = temp;
	while ( *pString )
	{
		if ( *pString == '/' )
		{
			*pString = '\\';
		}
		pString++;
	}
	// look for double backslashes, which are bad
	Dbg_MsgAssert( !strstr( temp, "\\\\" ), ( "Filename %s has double backslash", temp ) ); 
#endif

	return Str::String( fullName );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void GetModelFileName(const char* pName, const char* pExtension, char* pTarget)
{
	if ( strstr( pName, "/" ) || strstr( pName, "\\" ) )
	{
		Dbg_MsgAssert( strstr( pName, "." ), ( "Filename %s is missing extension", pName ) ); 
		
		Dbg_MsgAssert( pTarget, ( "No target buffer" ) );
		sprintf( pTarget, "models\\%s", pName ); 
	}
	else
	{
		Dbg_MsgAssert( pTarget, ( "No target buffer" ) );
		sprintf( pTarget, "models\\%s\\%s%s", pName, pName, pExtension );
	}
	
#ifdef __NOPT_ASSERT__
	// replace all forward slashes with backslashes
	char temp[512];
	strcpy( temp, pTarget );
	char* pString = temp;
	while ( *pString )
	{
		if ( *pString == '/' )
		{
			*pString = '\\';
		}
		pString++;
	}
	// look for double backslashes, which are bad
	Dbg_MsgAssert( !strstr( temp, "\\\\" ), ( "Filename %s has double backslash", temp ) ); 
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx



================================================
FILE: Code/Gfx/gfxutils.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       GfxUtils.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  02/01/2001
//****************************************************************************

#ifndef __GFX_UTILS_H
#define __GFX_UTILS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 
#include 

#include 
	
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mth
{
	class Vector;
};

namespace Script
{
	class CStruct;
	class CScript;
};

namespace Gfx
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

void RGBtoHSV( float r, float g, float b, float *h, float *s, float *v );
void HSVtoRGB( float *r, float *g, float *b, float h, float s, float v );

void inlineRGBtoHSV( float r, float g, float b, float *h, float *s, float *v );
void inlineHSVtoRGB( float *r, float *g, float *b, float h, float s, float v );

// some time conversion functions
float FRAMES_TO_TIME(int frames);
int TIME_TO_FRAMES(float time);

void 		GetModelFromFileName( char* filename, char* pModelNameBuf );
bool 		GetScaleFromParams( Mth::Vector* pScaleVector, Script::CStruct* pParams );
bool 		GetLoopingTypeFromParams( Gfx::EAnimLoopingType* pLoopingType, Script::CStruct* pParams );
bool 		GetTimeFromParams( float* pStart, float* pEnd, float Current, float Duration, Script::CStruct* pParams, Script::CScript* pScript );
Str::String	GetModelFileName(const char* pName, const char* pExt);
void    	GetModelFileName(const char* pName, const char* pExt, char* pTargetBuffer);

/*****************************************************************************
**						Inline Functions									**
*****************************************************************************/
					
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void inlineRGBtoHSV( float r, float g, float b, float &h, float &s, float &v )
{
	float min, max, delta;
	min = Mth::Min3( r, g, b );
	max = Mth::Max3( r, g, b );
	v = max;				// v
	delta = max - min;
	if( max != 0.0f )
		s = delta / max;		// s
	else {
		// r = g = b = 0		// s = 0, v is undefined
		s = 0.0f;
		h = -1.0f;
		return;
	}

	// GJ:
	if (delta == 0.0f)
		return;

	if( r == max )
		h = ( g - b ) / delta;		// between yellow & magenta
	else if( g == max )
		h = 2.0f + ( b - r ) / delta;	// between cyan & yellow
	else
		h = 4.0f + ( r - g ) / delta;	// between magenta & cyan
	h *= 60.0f;				// degrees
	if( h < 0.0f )
		h += 360.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void inlineHSVtoRGB( float &r, float &g, float &b, float h, float s, float v )
{
	if( s == 0.0f ) {
		// achromatic (grey)
		r = g = b = v;
		return;
	}

	int i;
	float f, p, q, t;

	h *= (1.0f/60.0f);	// sector 0 to 5
	i = (int)h;			// basically, the floor
	f = h - i;			// factorial part of h
	p = v * ( 1.0f - s );
	q = v * ( 1.0f - s * f );
	t = v * ( 1.0f - s * ( 1.0f - f ) );
	switch( i ) {
		case 0:
			r = v;
			g = t;
			b = p;
			break;
		case 1:
			r = q;
			g = v;
			b = p;
			break;
		case 2:
			r = p;
			g = v;
			b = t;
			break;
		case 3:
			r = p;
			g = q;
			b = v;
			break;
		case 4:
			r = t;
			g = p;
			b = v;
			break;
		default:		// case 5:
			r = v;
			g = p;
			b = q;
			break;
	}
}

} // namespace Gfx

#endif // __GFX_UTILS_H

================================================
FILE: Code/Gfx/nx.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////
// NX.CPP - Platform independent interface to the platfrom specific engine code 

#include 

#include 
#include 

#include 
                               
#include 	   
#include 	   
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#ifdef __PLAT_NGC__
#include 
#endif		// __PLAT_NGC__

//#include 		// For getting list of movable objects

#include 

#ifdef	__PLAT_NGPS__
namespace NxPs2
{
	extern int geom_stats_total;
	extern int geom_stats_inactive ;
	extern int geom_stats_sky;
	extern int geom_stats_transformed;
	extern int geom_stats_skeletal;
	extern int geom_stats_camera_sphere;
	extern int geom_stats_culled;
	extern int geom_stats_leaf_culled;
	extern int geom_stats_clipcull;
	extern int geom_stats_boxcheck;
	extern int geom_stats_occludecheck;
	extern int geom_stats_occluded;
	extern int geom_stats_colored;
	extern int geom_stats_leaf;
	extern int geom_stats_wibbleUV;
	extern int geom_stats_wibbleVC;
	extern int geom_stats_sendcontext;
	extern int geom_stats_sorted;
	extern int geom_stats_shadow;

	extern bool DoLetterbox;
}
#endif

namespace	Nx
{

///////////////////////////////////////////////////////////////////
// Static definitions

CScene *						CEngine::sp_loaded_scenes[CEngine::MAX_LOADED_SCENES];
Lst::HashTable< CParticle >*	CEngine::p_particle_table = NULL;
uint32							CEngine::s_next_avail_sector_checksum = 1;	// 0 is invalid
uint32							CEngine::s_render_mode;
uint32							CEngine::s_wireframe_mode;
uint32							CEngine::s_screen_blur;
uint32							CEngine::s_debug_ignore_1;
uint32							CEngine::s_debug_ignore_0;
Obj::CGeneralManager*			CEngine::sp_moving_object_manager;
CImposterManager*				CEngine::mp_imposter_manager	= NULL;
CNewParticleManager*			CEngine::mp_particle_manager	= NULL;
CWeather*						CEngine::mp_weather = NULL;

/******************************************************************/
/*                                                                */
/* get a sector pointer, based on the checksum of the name		  */
/*                                                                */
/******************************************************************/

CSector	*		CEngine::sGetSector(uint32 sector_checksum)
{
	for (int i = 0; i < MAX_LOADED_SCENES; i++)
	{
		if (sp_loaded_scenes[i])
		{
			CSector *ret_sector = sp_loaded_scenes[i]->GetSector(sector_checksum);
			if (ret_sector) return ret_sector;
		}
	}

	// If we got here, we didn't find anything
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CEngine::sStartEngine()
{		
	for (int i = 0; i < MAX_LOADED_SCENES; i++)
	{
		Dbg_Assert(!sp_loaded_scenes[i]);	// Make sure it was deallocated
	}

	// Allocate CTexts and CWindow2Ds
	Nx::CTextMan::sAllocTextPool();
	Nx::CWindow2DManager::sAllocWindow2DPool();

	CViewportManager::sSetScreenAspect(4.0f/3.0f);
	CViewportManager::sSetDefaultAngle( Script::GetFloat("camera_fov") );
	CViewportManager::sSetScreenAngle( 0.0f);		// set to default

	CViewportManager::sInit();
	CViewportManager::sSetScreenMode(vONE_CAM);

	//sSetMaxMultipassDistance(3000.0f);

	// Allocate particle system table
	p_particle_table = new Lst::HashTable( 6 ); 
						 
	// Create the imposter manager.
	mp_imposter_manager = new CImposterManager;

	// Startup effects allocations.
	Nx::MiscFXInitialize();

	s_plat_start_engine();				

}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CEngine::sSuspendEngine()
{
	printf ("STUB: SuspendEngine\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CEngine::sResumeEngine()
{
	printf ("STUB: ResumeEngine\n");
}
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CEngine::sPreRender()
{
	s_plat_pre_render();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CEngine::sPostRender()
{
	s_plat_post_render();

	// Clear away any old cameras
	CViewportManager::sDeleteMarkedCameras();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CEngine::sRenderWorld()
{
	process_particles( Tmr::FrameLength() ); 

	ScreenFlashUpdate();
	TextureSplatUpdate();
	ShatterUpdate();
	
	CLightManager::sUpdateVCLights();
	
	s_plat_render_world();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CScene	*		CEngine::sCreateScene(const char *p_name, CTexDict *p_tex_dict,
									  bool add_super_sectors)
{
	CScene *p_created_scene = s_plat_create_scene(p_name, p_tex_dict, add_super_sectors);

	p_created_scene->SetID(Script::GenerateCRC(p_name)); 	// store the checksum of the scene name

	p_created_scene->SetTexDict(p_tex_dict);					  

	// Put in scene array
	int i;
	for (i = 0; i < MAX_LOADED_SCENES; i++)
	{
		if (!sp_loaded_scenes[i])
		{
			sp_loaded_scenes[i] = p_created_scene;
			break;
		}
	}

	Dbg_MsgAssert(i < MAX_LOADED_SCENES, ("Have more than MAX_LOADED SCENES"));

	return p_created_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CScene	*		CEngine::sLoadScene(const char *p_name, CTexDict *p_tex_dict,
									bool add_super_sectors, bool is_sky, bool is_dictionary, bool is_net)
{
	// first expand the scene file name out to include the path
	// the level directory, and the platform specific extension
	// e.g.,  "can" become levels\can\can.scn.ps2

	

	char	full_name[128];
#ifdef __PLAT_NGPS__
	sprintf(full_name,"levels\\%s\\%s%s.geom.%s",p_name,p_name,is_net?"_net":"",sGetPlatformExtension());
#else
	sprintf(full_name,"levels\\%s\\%s%s.scn.%s",  p_name,p_name,is_net?"_net":"",sGetPlatformExtension());
#endif

	Mem::PushMemProfile((char*)full_name);

	CScene *loaded_scene = s_plat_load_scene(full_name, p_tex_dict, add_super_sectors, is_sky, is_dictionary);

	loaded_scene->SetID(Script::GenerateCRC(p_name)); 	// store the checksum of the scene name

	loaded_scene->SetTexDict(p_tex_dict);					  
					  
	// Do post-load processing
	loaded_scene->PostLoad(full_name);

	// Put in scene array
	int i;
	for (i = 0; i < MAX_LOADED_SCENES; i++)
	{
		if (!sp_loaded_scenes[i])
		{
			sp_loaded_scenes[i] = loaded_scene;
			break;
		}
	}

	Dbg_MsgAssert(i < MAX_LOADED_SCENES, ("Have more than MAX_LOADED SCENES"));
	
	Mem::PopMemProfile(/*p_name*/);

	return loaded_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CScene	*		CEngine::sAddScene(const char *p_scene_name, const char *p_filename)
{
	// first expand the scene file name out to include the path
	// the level directory, and the platform specific extension
	// e.g.,  "can" become levels\can\can.scn.ps2

	char	full_name[128];
#ifdef __PLAT_NGPS__
	sprintf(full_name,"levels\\%s\\%s.geom.%s",p_filename,p_filename,sGetPlatformExtension());
#else
	sprintf(full_name,"levels\\%s\\%s.scn.%s",p_filename,p_filename,sGetPlatformExtension());
#endif

	char	texture_dict_name[128];
	sprintf(texture_dict_name,"levels\\%s\\%s.tex",p_filename,p_filename);

	CScene *p_scene = sGetScene(Script::GenerateCRC(p_scene_name));

	Dbg_MsgAssert(p_scene, ("sAddScene(): Can't find existing scene %s", p_scene_name));

	Dbg_Message("Adding to scene %s with file %s", p_scene_name, full_name);

	// Check if we need to unload old one
	if (strcmp(p_scene->GetAddSceneFilename(), full_name) == 0)
	{
		Dbg_Message("Unloading old added scene %s", full_name);
		p_scene->UnloadAddScene();

		Nx::CTexDict * p_old_tex_dict = Nx::CTexDictManager::sGetTextureDictionary(Crc::GenerateCRCFromString(texture_dict_name));
		if (p_old_tex_dict)		// See if we previously loaded this
		{
			Dbg_Message("Unloading old texture dictionary %s", texture_dict_name);
			Nx::CTexDictManager::sUnloadTextureDictionary(p_old_tex_dict);
		} else {
			Dbg_Assert(0);
		}
	}

	Nx::CTexDict * p_tex_dict = Nx::CTexDictManager::sLoadTextureDictionary(texture_dict_name,true);
	Dbg_MsgAssert(p_tex_dict, ("ERROR loading tex dict for %s\n",texture_dict_name));

	Dbg_Message("Adding to scene %s with file %s", p_scene_name, full_name);

	s_plat_add_scene(p_scene, full_name);

	// Do post-add processing
	p_scene->PostAdd(full_name, p_tex_dict);

	return p_scene;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CEngine::sToggleAddScenes()
{
	for (int i = 0; i < MAX_LOADED_SCENES; i++)
	{
		if (sp_loaded_scenes[i])
		{
			sp_loaded_scenes[i]->ToggleAddScene();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CEngine::sUnloadScene(CScene *p_scene)
{

	// Wait for the frame to finish rendering before we unload the scene
	// otherwise we might be trying to render something that is no longer there	
	// (Without this line, the engine can hang when doing a quickview, and seems
	// to be more crashy when using particle systems)
	Nx::CEngine::sFinishRendering();	
	
	// Remove Incremental Data first
	// (Doesn't work yet because the replace CSectors are lost and the texture dictionary is still around)
	p_scene->UnloadAddScene();

	// remove platform specific assets
	if (s_plat_unload_scene(p_scene))
	{
//#ifdef __PLAT_NGC__
//		CNgcScene *p_ngc_scene = (CNgcScene*)p_scene;
//		NxNgc::sScene * p_engine_scene = p_ngc_scene->GetEngineScene();
//#endif		// __PLAT_NGC__

		// delete the scene object itself. 
		delete	p_scene;

//#ifdef __PLAT_NGC__
//		// Get the engine specific scene data and pass it to the engine to delete.
//		NxNgc::DeleteScene( p_engine_scene );
//		p_ngc_scene->SetEngineScene( NULL );
//#endif		// __PLAT_NGC__

		// and remove it from the list of scenes
		for (int i = 0; i < MAX_LOADED_SCENES; i++)
		{
			if (sp_loaded_scenes[i] == p_scene)
			{
				sp_loaded_scenes[i] = NULL;
				return true;
			}
		}
	}

	Dbg_Error("Could not find CScene to unload");
	return false;
}


// (Not working yet!!!)							  
// This is useful function to be only used during development
// it attempts to reload just the .scn part of a level
// without reloading the texture dictionary (assumes it stays the same)
// and without reloading the collision (assumes you are in viewer mode, and don't need it)  				  
bool			CEngine::sQuickReloadGeometry(const char *p_scene_name)
{	
	// find the scene
	Nx::CScene	*p_scene = sGetScene(p_scene_name);
	
	if (p_scene)
	{
		CTexDict *p_tex_dict = p_scene->GetTexDict();
		bool	is_sky = p_scene->IsSky();
		
		sUnloadScene(p_scene);
		/*Nx::CScene	*p_new_scene = */ sLoadScene(p_scene_name,p_tex_dict,!is_sky,is_sky);
//		sUnloadScene(p_scene);
//		p_new_scene = sLoadScene(p_scene_name,p_tex_dict,!is_sky,is_sky);
		
		return true;
	}
	return false;
}


// Given the id of a scene, then return the CScene that has this id
// returns NULL if scene not found
CScene *		CEngine::sGetScene(uint32 id)
{
	for (int i = 0; i < MAX_LOADED_SCENES; i++)
	{
		if (sp_loaded_scenes[i] && (sp_loaded_scenes[i]->GetID() == id))
		{
			return sp_loaded_scenes[i];
		}
	}
	return NULL;
}


CScene *		CEngine::sGetScene(const char *p_name)
{
	return		sGetScene(Script::GenerateCRC(p_name));
}


// Get the last non-sky scene.  Kind of application dependent, as it assumes 
// that the "Main" scene is the last non sky scene
// but that's a pretty safe bet for what we are doing now.
// (Mick:  I'm only using this for the "Disco" cheat in THPS4)
// (Mick: now this function is used in ParseNodeArray, so I had to change this from
// using the first non-sky to the last non-sky, so now it's even dodgier....
// we need a better way of saying what is the "main" scene
CScene *  		CEngine::sGetMainScene()
{
	for (int i = MAX_LOADED_SCENES-1; i >= 0; i--)
	{
		if (sp_loaded_scenes[i] && !sp_loaded_scenes[i]->IsSky())
		{
			return sp_loaded_scenes[i];
		}
	}
	return NULL;	
}

// Get the first sky scene.
CScene *  		CEngine::sGetSkyScene()
{
	for (int i = 0; i < MAX_LOADED_SCENES; i++)
	{
		if (sp_loaded_scenes[i] && sp_loaded_scenes[i]->IsSky())
		{
			return sp_loaded_scenes[i];
		}
	}
	return NULL;	
}

  
// Mick:::::   ........  
// a temporary utility function
// unloads all loaded scenes, (level and sky)
// and also unloads the texture dictionaries
// (or at least decrements the reference counts)
bool		CEngine::sUnloadAllScenesAndTexDicts()
{
	for (int i = 0; i < MAX_LOADED_SCENES; i++)
	{
		CScene * p_scene = sp_loaded_scenes[i];
		if (p_scene)
		{
			CTexDict *p_tex_dict = p_scene->GetTexDict();

    		sUnloadScene(p_scene);
			// must unload dictionary after scene
			if (p_tex_dict)
			{
				CTexDictManager::sUnloadTextureDictionary(p_tex_dict);
			}
		}
	}

	// Remove occlusion polys.
	s_plat_remove_all_occlusion_polys();
	
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSprite *		CEngine::sCreateSprite(CWindow2D *p_window)
{
	return s_plat_create_sprite(p_window);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CEngine::sDestroySprite(CSprite *p_sprite)
{
	return s_plat_destroy_sprite(p_sprite);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTextured3dPoly *CEngine::sCreateTextured3dPoly()
{
	return s_plat_create_textured_3d_poly();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CEngine::sDestroyTextured3dPoly(CTextured3dPoly *p_poly)
{
	return s_plat_destroy_textured_3d_poly(p_poly);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTexture *CEngine::sCreateRenderTargetTexture( int width, int height, int depth, int z_depth )
{
	return s_plat_create_render_target_texture( width, height, depth, z_depth );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEngine::sProjectTextureIntoScene( Nx::CTexture *p_texture, Nx::CModel *p_model, Nx::CScene *p_scene )
{
	// usually we don't want to know the scene, so we pass in NULL
	// and let the engine figure it out
	// if we have multiple scenes, then we would need to figure this out externally

	if (!p_scene)
	{
		// find the first non-sky scene
		for (int i = 0; i < MAX_LOADED_SCENES; i++)
		{
			if (sp_loaded_scenes[i] && !sp_loaded_scenes[i]->IsSky())
			{
				p_scene = sp_loaded_scenes[i];
				break;
			}
		}
	}

	// It no longer matters if the scene is NULL.
	s_plat_project_texture_into_scene( p_texture, p_model, p_scene );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEngine::sSetProjectionTextureCamera( Nx::CTexture *p_texture, Gfx::Camera *p_camera )
{
	s_plat_set_projection_texture_camera( p_texture, p_camera );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::sStopProjectionTexture( Nx::CTexture *p_texture )
{
	s_plat_stop_projection_texture( p_texture );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::sAddOcclusionPoly( uint32 num_verts, Mth::Vector *p_vert_array, uint32 checksum )
{
	s_plat_add_occlusion_poly( num_verts, p_vert_array, checksum );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::sEnableOcclusionPoly( uint32 checksum, bool enable )
{
	s_plat_enable_occlusion_poly( checksum, enable );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEngine::sDebugCheckForHoles()
{

	Tmr::Time start_time, end_time;
	start_time = Tmr::GetTime();

	for (int i = 0; i < MAX_LOADED_SCENES; i++)
	{
		if (sp_loaded_scenes[i] && !sp_loaded_scenes[i]->IsSky())
		{
			sp_loaded_scenes[i]->DebugCheckForHoles();
		}
	}

	end_time = Tmr::GetTime();

	Dbg_Message("CheckForHoles() took %d msecs", end_time - start_time);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CModel*			CEngine::sInitModel(void)
{
	return s_plat_init_model();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CEngine::sUninitModel(CModel* pModel)
{
	// Wait for any async rendering to finish before we proceed	
#ifndef __PLAT_NGC__
	sFinishRendering();
#endif		// __PLAT_NGC__
	return s_plat_uninit_model(pModel);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGeom*			CEngine::sInitGeom()
{
	return s_plat_init_geom();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CEngine::sUninitGeom(CGeom* pGeom)
{
	// Wait for any async rendering to finish before we proceed	
#ifndef __PLAT_NGC__
	sFinishRendering();
#endif		// __PLAT_NGC__
	return s_plat_uninit_geom(pGeom);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CQuickAnim*		CEngine::sInitQuickAnim(void)
{
	return s_plat_init_quick_anim();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
			
void			CEngine::sUninitQuickAnim(CQuickAnim* pQuickAnim)
{
	s_plat_uninit_quick_anim(pQuickAnim);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMesh*			CEngine::sLoadMesh(const char* pFileName, uint32 texDictOffset, bool forceTexDictLookup, bool doShadowVolume)
{
	char baseName[512];
	strcpy( baseName, pFileName );

	char* pEnd;

	bool foundSkin = false;
	pEnd = strstr( baseName, ".skin" );
	foundSkin = ( pEnd != NULL );
	if ( foundSkin )
	{
		*pEnd = NULL;
	}

	bool foundModel = false;
	pEnd = strstr( baseName, ".mdl" );
	foundModel = ( pEnd != NULL );
	if ( foundModel )
	{
		*pEnd = NULL;
	}

	Dbg_Assert( foundSkin || foundModel );

	char meshName[512];
	Str::LowerCase( meshName );
	sprintf( meshName, "%s.%s.%s", baseName, foundSkin ? "skin" : "mdl", sGetPlatformExtension() );

	char textureName[512];
	sprintf( textureName, "%s.tex", baseName );

	// brad - special case for boardshop boards.
	// (ideally, this would be coming in from a
	// higher level, such as with the
	// "forceTexDictLookup" flag
	if ( strstr( meshName, "thps4board_01" ) )
	{
		forceTexDictLookup = true;
	}
	
	Nx::CTexDict* pTexDict = Nx::CTexDictManager::sLoadTextureDictionary(textureName, false, texDictOffset, foundSkin, forceTexDictLookup);
	Dbg_MsgAssert(pTexDict,("ERROR loading tex dict for %s\n",textureName));

	Mem::PushMemProfile((char*)meshName);	
	CMesh* p_mesh = s_plat_load_mesh(meshName, pTexDict, texDictOffset, foundSkin, doShadowVolume);
	Mem::PopMemProfile(/*(char*)meshname*/);	
	return p_mesh;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMesh*			CEngine::sLoadMesh(uint32 id, uint32* pModelData, int modelDataSize, uint8* pCASData, uint32 textureDictChecksum, uint32* pTextureData, int textureDataSize, uint32 textureDictOffset, bool isSkin, bool doShadowVolume )
{
	Dbg_MsgAssert( id != 0, ( "invalid id checksum" ) );

	bool forceTexDictLookup = false;

	Nx::CTexDict* pTexDict = Nx::CTexDictManager::sLoadTextureDictionary(textureDictChecksum, pTextureData, textureDataSize, false, textureDictOffset, isSkin, forceTexDictLookup);
	Dbg_MsgAssert(pTexDict,("ERROR loading tex dict from buffer"));

	Mem::PushMemProfile((char*)"model_data_buffer");	
	CMesh*	p_mesh = s_plat_load_mesh(id, pModelData, modelDataSize, pCASData, pTexDict, textureDictOffset, isSkin, doShadowVolume);
	Mem::PopMemProfile(/*(char*)meshname*/);	
	return p_mesh;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			CEngine::sUnloadMesh(CMesh* pMesh)
{
	CTexDict *p_tex_dict = pMesh->GetTextureDictionary();
	
	bool result = s_plat_unload_mesh(pMesh);

	CTexDictManager::sUnloadTextureDictionary( p_tex_dict );

	return result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CEngine::sSetMeshScalingParameters( SMeshScalingParameters* pParams )
{
	s_plat_set_mesh_scaling_parameters( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Set the moving object manager.  Perviously we asked the skater module for 
// this, but now we are independent of Skate, so we have to be told

void	CEngine::sSetMovableObjectManager(Obj::CGeneralManager* p_object_manager)
{
	Dbg_MsgAssert(p_object_manager,("NULL Movable Object Manager"));
	sp_moving_object_manager = p_object_manager;
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Lst::Head & CEngine::sGetMovableObjects()
{

	Dbg_MsgAssert( sp_moving_object_manager, ("NULL sp_moving_object_manager"));
	return sp_moving_object_manager->GetRefObjectList();
}

Lst::HashTable< CParticle > * CEngine::sGetParticleTable()
{
	return p_particle_table;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SSec::Manager *	CEngine::sGetNearestSuperSectorManager(Mth::Line &collision_line)
{
	// For now, just return the first manager
	for (int i = 0; i < MAX_LOADED_SCENES; i++)
	{
		if (sp_loaded_scenes[i])
		{ 
			SSec::Manager *p_man = sp_loaded_scenes[i]->GetSuperSectorManager();
			if (p_man) return p_man;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SSec::Manager *	CEngine::sGetNearestSuperSectorManager(Mth::Vector &collision_point)
{
	// For now, just return the first manager
	for (int i = 0; i < MAX_LOADED_SCENES; i++)
	{
		if (sp_loaded_scenes[i])
		{ 
			SSec::Manager *p_man = sp_loaded_scenes[i]->GetSuperSectorManager();
			if (p_man) return p_man;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32			CEngine::sGetUnusedSectorChecksum()
{
	// Make sure this checksum isn't being used
	while (sGetSector(s_next_avail_sector_checksum))
		s_next_avail_sector_checksum++;

	return s_next_avail_sector_checksum++;
}

/******************************************************************/
/*                                                                */
/* return platform specifc extension, like ".PS2"				  */
/*                                                                */
/******************************************************************/

const char *	CEngine::sGetPlatformExtension()
{
	return s_plat_get_platform_extension();
}

/******************************************************************/
// Wait for any pending asyncronous rendering to finish, so rendering
// data can be unloaded
// (Might be a wait, or a flush, depending on platform)
// This function should generally return immediatly if there is no
// pending rendering.  However, it might stall for up to a frame if
// there are things waiting to render.
// so if it is to be used in a real-time situation, then you want to
// call it as late as possible in the frame (preferably the last thing)
// so it incurs as little wait as possible
// this might be accomplished by making the "task" that uses it
// be as low priority as possible
/******************************************************************/					  

void	CEngine::sFinishRendering()
{
	s_plat_finish_rendering();
}


void				CEngine::sToggleRenderMode()
{
	s_render_mode++;
	if (s_render_mode == 4)
	{
		s_render_mode = 0;	
	}
}


void				CEngine::sSetDebugIgnore(uint32 ignore_1, uint32 ignore_0)
{
	s_debug_ignore_0 = ignore_0;
	s_debug_ignore_1 = ignore_1;
}

bool				CEngine::sIsVisible(Mth::Vector ¢er, float radius)
{
	// PATCH for multiple viewports
	// always return true, until we sort out the per-viewport visibility (which might not be needed)				   
	if (Nx::CViewportManager::sGetNumActiveViewports() > 1)
	{
		return true;
	}

	return s_plat_is_visible(center,radius);
}

void				CEngine::sSetMaxMultipassDistance(float dist)
{
	s_plat_set_max_multipass_distance(dist);
}

void				CEngine::sSetScreenBlur(uint32 amount)
{
	Dbg_MsgAssert(amount >=0 && amount <=255,("Screen blur amount out of range of 0..255 (it's %d)",amount));
	s_screen_blur = amount;
	s_plat_set_screen_blur(amount);
}

uint32 CEngine::sGetScreenBlur()
{
	uint32	amount = s_screen_blur; 
	Dbg_MsgAssert(amount >=0 && amount <=255,("s_screen_blur out of range of 0..255 (it's %d), maybe corrupted???",amount));
	return amount;	
}

int CEngine::sGetNumSoundtracks()
{
	return s_plat_get_num_soundtracks();
}

const char* CEngine::sGetSoundtrackName( int soundtrack_number )
{
	const char* p_soundtrack_name_wide = s_plat_get_soundtrack_name( soundtrack_number );
	return p_soundtrack_name_wide;
}

// Added by Ken for use by replay code.
// 
int CEngine::sWriteSectorStatusBitfield(uint32 *p_bitfield, int numUint32s)
{
	return 0;
	
	// Removed since we no longer have saving of replays to mem card.
	// Leaving this code in was causing a problem in that the passed numUint32s
	// was too small for the Skillzilla Park. Rather than increasing numUint32's
	// and hence using up more memory, it was safer just to remove the contents of this function.
	#if 0
	Dbg_MsgAssert(p_bitfield,("NULL p_bitfield"));

	int i;
	for (i=0; i *p_table=sp_loaded_scenes[i]->GetSectorList();
			Dbg_MsgAssert(p_table,("NULL p_table"));
			
			p_table->IterateStart();
			CSector *p_sector=p_table->IterateNext();
			while (p_sector)
			{
				if (p_sector->GetActiveAtReplayStart())
				{
					Dbg_MsgAssert(long_index=32)
				{
					bit_index=0;
					++long_index;
				}	

				if (p_sector->GetVisibleAtReplayStart())
				{
					Dbg_MsgAssert(long_index=32)
				{
					bit_index=0;
					++long_index;
				}	
				
				p_sector=p_table->IterateNext();
			}
		}
	}	
	
	return long_index*32+bit_index;
	#endif
}

void CEngine::sReadSectorStatusBitfield(uint32 *p_bitfield, int numUint32s)
{
	return;
	
	// Removed since we no longer have saving of replays to mem card.
	// Leaving this code in was causing a problem in that the passed numUint32s
	// was too small for the Skillzilla Park. Rather than increasing numUint32's
	// and hence using up more memory, it was safer just to remove the contents of this function.
	#if 0
	Dbg_MsgAssert(p_bitfield,("NULL p_bitfield"));

	int long_index=0;
	int bit_index=0;
	
	for (int i = 0; i *p_table=sp_loaded_scenes[i]->GetSectorList();
			Dbg_MsgAssert(p_table,("NULL p_table"));
			
			p_table->IterateStart();
			CSector *p_sector=p_table->IterateNext();
			while (p_sector)
			{
				Dbg_MsgAssert(long_indexSetActiveAtReplayStart(true);
				}	
				else
				{
					p_sector->SetActiveAtReplayStart(false);
				}	
				
				++bit_index;
				if (bit_index>=32)
				{
					bit_index=0;
					++long_index;
				}	

				Dbg_MsgAssert(long_indexSetVisibleAtReplayStart(true);
				}	
				else
				{
					p_sector->SetVisibleAtReplayStart(false);
				}	
				
				++bit_index;
				if (bit_index>=32)
				{
					bit_index=0;
					++long_index;
				}	
				
				p_sector=p_table->IterateNext();
			}
		}
	}	
	#endif
}

void CEngine::sInitReplayStartState()
{
	//printf("Calling sInitReplayStartState()\n");
	for (int i = 0; i *p_table=sp_loaded_scenes[i]->GetSectorList();
			Dbg_MsgAssert(p_table,("NULL p_table"));
			
			p_table->IterateStart();
			CSector *p_sector=p_table->IterateNext();
			while (p_sector)
			{
				p_sector->SetActiveAtReplayStart(p_sector->IsActive());
				p_sector->SetVisibleAtReplayStart(p_sector->GetVisibility()&0x01);
				// Just to be safe
				p_sector->SetStoredActive(p_sector->IsActive());
				p_sector->SetStoredVisible(p_sector->GetVisibility()&0x01);
				
				p_sector=p_table->IterateNext();
			}
		}
	}	
}

void CEngine::sPrepareSectorsForReplayPlayback(bool store_initial_state)
{
	//printf("Calling sPrepareSectorsForReplayPlayback()\n");
	for (int i = 0; i *p_table=sp_loaded_scenes[i]->GetSectorList();
			Dbg_MsgAssert(p_table,("NULL p_table"));
			
			p_table->IterateStart();
			CSector *p_sector=p_table->IterateNext();
			while (p_sector)
			{
				if (store_initial_state)
				{
					p_sector->SetStoredActive(p_sector->IsActive());
					p_sector->SetStoredVisible(p_sector->GetVisibility()&0x01);
				}	  
				  		
				p_sector->SetActive(p_sector->GetActiveAtReplayStart());
				p_sector->SetVisibility(p_sector->GetVisibleAtReplayStart()?0xff:0x00);
				
				p_sector=p_table->IterateNext();
			}
		}
	}	
}

void CEngine::sRestoreSectorsAfterReplayPlayback()
{
	//printf("Calling sRestoreSectorsAfterReplayPlayback()\n");
	for (int i = 0; i *p_table=sp_loaded_scenes[i]->GetSectorList();
			Dbg_MsgAssert(p_table,("NULL p_table"));
			
			p_table->IterateStart();
			CSector *p_sector=p_table->IterateNext();
			while (p_sector)
			{
				p_sector->SetActive(p_sector->GetStoredActive());
				p_sector->SetVisibility(p_sector->GetStoredVisible()?0xff:0x00);
				
				p_sector=p_table->IterateNext();
			}
		}
	}	
}

bool ScriptToggleAddScenes(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Just call engine function
	CEngine::sToggleAddScenes();

	return true;
}


bool ScriptSetScreenBlur(Script::CStruct *pParams, Script::CScript *pScript)
{
	int amount = 0;
	pParams->GetInteger(NONAME,&amount,false);
	CEngine::sSetScreenBlur(amount);
	return true;
}


// Given a script structure, add some metrics to it
// like number of objects, polygons, collision polygons, etc
void	CEngine::sGetMetrics(Script::CStruct * p_info)
{

	// Do main scene first, as it's more interesting
	if (sGetMainScene())
	{
		Script::CStruct *p_main = new Script::CStruct();
		sGetMainScene()->GetMetrics(p_main);
		p_info->AddStructurePointer(CRCD(0x9b10525c,"MainScene"),p_main);
	}
		
	// But also do the sky, just in case												  
	if (sGetSkyScene())
	{
		Script::CStruct *p_sky = new Script::CStruct();
		sGetSkyScene()->GetMetrics(p_sky);
		p_info->AddStructurePointer(CRCD(0x2c5ea34,"SkyScene"),p_sky);
	}

	#ifdef	__PLAT_NGPS__
	

		Script::CStruct *p_ps2 = new Script::CStruct();
		p_ps2->AddInteger(CRCD(0x3dd01ea1,"total"), NxPs2::geom_stats_total);        
		p_ps2->AddInteger(CRCD(0x742cf11f,"inactive"), NxPs2::geom_stats_inactive );    
		p_ps2->AddInteger(CRCD(0xf9d98b10,"sky"), NxPs2::geom_stats_sky);          
		p_ps2->AddInteger(CRCD(0x6d65d103,"transformed"), NxPs2::geom_stats_transformed);  
		p_ps2->AddInteger(CRCD(0x52aa1a77,"skeletal"), NxPs2::geom_stats_skeletal);     
		p_ps2->AddInteger(CRCD(0xaf792e8d,"camera_sphere"), NxPs2::geom_stats_camera_sphere);
		p_ps2->AddInteger(CRCD(0xa863e48c,"clipcull"), NxPs2::geom_stats_clipcull);     
		p_ps2->AddInteger(CRCD(0x93070c4b,"culled"), NxPs2::geom_stats_culled);     
		p_ps2->AddInteger(CRCD(0xac43724a,"leaf_culled"), NxPs2::geom_stats_leaf_culled);     
		p_ps2->AddInteger(CRCD(0x68306ffe,"boxcheck"), NxPs2::geom_stats_boxcheck);     
		p_ps2->AddInteger(CRCD(0xae81c5f6,"occludecheck"), NxPs2::geom_stats_occludecheck); 
		p_ps2->AddInteger(CRCD(0xba802ab0,"occluded"), NxPs2::geom_stats_occluded);     
		p_ps2->AddInteger(CRCD(0x1bc830f2,"colored"), NxPs2::geom_stats_colored);      
		p_ps2->AddInteger(CRCD(0x3960ff18,"leaf"), NxPs2::geom_stats_leaf);         
		p_ps2->AddInteger(CRCD(0x506a239f,"wibbleUV"), NxPs2::geom_stats_wibbleUV);     
		p_ps2->AddInteger(CRCD(0x169a94b7,"wibbleVC"), NxPs2::geom_stats_wibbleVC);     
		p_ps2->AddInteger(CRCD(0x7b471ba2,"sendcontext"), NxPs2::geom_stats_sendcontext);  
		p_ps2->AddInteger(CRCD(0x18725397,"sorted"), NxPs2::geom_stats_sorted);       
		p_ps2->AddInteger(CRCD(0x8a897dd2,"shadow"), NxPs2::geom_stats_shadow);       
		
		p_info->AddStructurePointer(CRCD(0xf2316b47,"PS2_Info"),p_ps2);

		s_plat_get_metrics(p_info);

#endif


}
					 					  


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void	CEngine::sSetLetterbox(bool letterbox)
{
	s_plat_set_letterbox( letterbox );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void	CEngine::sSetColorBufferClear(bool clear)
{
	s_plat_set_color_buffer_clear( clear );
}



} // namespace Nx






================================================
FILE: Code/Gfx/nx.h
================================================
///////////////////////////////////////////////////////////////////////////////////
// NX.H - Neversoft Engine, Rendering portion, Platform independent interface

#ifndef	__GFX_NX_H__
#define	__GFX_NX_H__

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{
	class CObject;
}

namespace Gfx
{
	class Camera;
}

namespace Script
{
	class CStruct;
	class CScript;
}

namespace Nx
{
	// forward declarations
	class CGeom;
	class CMesh;
	class CModel;
	class CQuickAnim;
	class CWindow2D;
	class CParticle;

// GJ:  The following is for doing cutscene head scaling...
// These parameters will only be active during the loading
// of the next CMesh.
struct SMeshScalingParameters
{
	char* 			pWeightIndices;
	float* 			pWeights;
	Mth::Vector* 	pBonePositions;
	Mth::Vector* 	pBoneScales;
};

////////////////////////////////////////////////////////////////////////
// Globally accessible Engine functions ar public static functions, 
// just use them like:
//
//  Nx::CSector * p_sector = Nx::CEngine::sGetSector(checksum);
//
// // NOT // I might change this later so the engine is a class, so it would become:
// // NOT // Nx::CSector * p_sector = Nx::CEngine::Instance()->GetSector(checksum);


// The engine class is all static
class CEngine
{
public:
	static CSector	*			sGetSector(uint32 sector_checksum);			// get a sector pointer, based on the checksum of the name
	static void					sStartEngine();
	static void					sSuspendEngine();
	static void					sResumeEngine();
	static void					sPreRender();
	static void					sPostRender();
	static void					sRenderWorld();
	
	static void					sFinishRendering();

	static void					sSetScreenBlur(uint32 amount);	
	static uint32				sGetScreenBlur();	
	
	static CScene	*			sCreateScene(const char *p_name, CTexDict *p_tex_dict,
											 bool add_super_sectors = true);	// creates an empty scene
	static CScene	*			sLoadScene(const char *p_name, CTexDict *p_tex_dict, bool add_super_sectors = true,
										   bool is_sky = false, bool is_dictionary = false, bool is_net=false);	// load a platform specific scene file
	static CScene	*			sAddScene(const char *p_scene_name, const char *p_filename);	// add a scene file to an existing scene
	static void					sToggleAddScenes();
	static bool					sUnloadScene(CScene *p_scene);
	static bool					sQuickReloadGeometry(const char *p_scene_name);	

	static SSec::Manager * 		sGetNearestSuperSectorManager(Mth::Line &);		// Returns the closest SuperSector Manager
	static SSec::Manager * 		sGetNearestSuperSectorManager(Mth::Vector &);	// Returns the closest SuperSector Manager

	static	const char *		sGetPlatformExtension();   					// return platform specifc extension, like ".PS2"
	
	static bool					sUnloadAllScenesAndTexDicts();				// unloads all loaded scenes and their associated texture dicts
	
	static	CScene *			sGetScene(uint32 id);		  				// get scene, given the checksum id of the name
	static	CScene *			sGetScene(const char *p_name);				// get scene, given name
	static 	CScene *  			sGetMainScene();							// get first non-sky scene
	static 	CScene *  			sGetSkyScene();								// get first sky scene

	static CSprite *			sCreateSprite(CWindow2D *p_window = NULL);
	static bool					sDestroySprite(CSprite *p_sprite);
								
	static CTextured3dPoly *	sCreateTextured3dPoly();
	static bool					sDestroyTextured3dPoly(CTextured3dPoly *p_poly);
	
											  
	static CTexture *			sCreateRenderTargetTexture( int width, int height, int depth, int z_depth );
	static void					sProjectTextureIntoScene( Nx::CTexture *p_texture, Nx::CModel *p_model, Nx::CScene *p_scene = NULL);
	static void					sSetProjectionTextureCamera( Nx::CTexture *p_texture, Gfx::Camera *p_camera );
	static void					sStopProjectionTexture( Nx::CTexture *p_texture );

	static void					sAddOcclusionPoly( uint32 num_verts, Mth::Vector *p_vert_array, uint32 checksum = 0 );
	static void					sEnableOcclusionPoly( uint32 checksum, bool enable );
	
	static CModel*				sInitModel();
	static bool					sUninitModel(CModel* pModel);

	static CGeom*				sInitGeom();
	static bool					sUninitGeom(CGeom* pGeom);

	static CQuickAnim*			sInitQuickAnim();
	static void					sUninitQuickAnim(CQuickAnim* pQuickAnim);

	static CMesh*				sLoadMesh(const char* pFileName, uint32 texDictOffset, bool forceTexDictLookup, bool doShadowVolume);
	static CMesh*				sLoadMesh(uint32 id, uint32* pModelData, int modelDataSize, uint8* pCASData, uint32 textureDictChecksum, uint32* pTextureData, int textureDataSize, uint32 textureDictOffset, bool isSkin, bool doShadowVolume );
	static bool					sUnloadMesh(CMesh* pMesh);

	static void					sSetMeshScalingParameters( SMeshScalingParameters* pParams );

	static	void				sSetMovableObjectManager(Obj::CGeneralManager* p_object_manager);
	static Lst::Head & sGetMovableObjects();					// Returns the list of all movable objects
	static 	Lst::HashTable< CParticle > * sGetParticleTable();

	static CImposterManager*	sGetImposterManager( void )	{ return mp_imposter_manager; }
	static CNewParticleManager*	sGetParticleManager( void )	{ return mp_particle_manager; }

	static uint32				sGetUnusedSectorChecksum();					// Gets a unique sector checksum for cloning

	static bool					sIsVisible(Mth::Vector ¢er, float radius);										
										
	static void					sSetMaxMultipassDistance(float dist);		// Sets distance at which multipass will stop drawing

	static void					sSetLetterbox(bool lettterbox);

	static void					sSetColorBufferClear(bool clear);				// Indicates whether a per-frame color buffer clear is required (in addition to z-buffer clear).
	static CWeather*			sGetWeather( void ) { return mp_weather; }

	// K: Added so that park editor can update the grid after geometry has changed.
	static bool					ScriptWeatherUpdateGrid( Script::CStruct* pParams, Script::CScript* pScript ) { mp_weather->UpdateGrid(); return true; }
	
	static bool					ScriptWeatherSetRainHeight( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetRainHeight( pParams, pScript ); }
	static bool					ScriptWeatherSetRainFrames( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetRainFrames( pParams, pScript ); } 
	static bool					ScriptWeatherSetRainLength( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetRainLength( pParams, pScript ); } 
	static bool					ScriptWeatherSetRainBlendMode( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetRainBlendMode( pParams, pScript ); } 
	static bool					ScriptWeatherSetRainRate( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetRainRate( pParams, pScript ); } 
	static bool					ScriptWeatherSetRainColor( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetRainColor( pParams, pScript ); }  

	static bool					ScriptWeatherSetSplashRate( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSplashRate( pParams, pScript ); } 
	static bool					ScriptWeatherSetSplashLife( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSplashLife( pParams, pScript ); } 
	static bool					ScriptWeatherSetSplashSize( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSplashSize( pParams, pScript ); } 
	static bool					ScriptWeatherSetSplashColor( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSplashColor( pParams, pScript ); } 
	static bool					ScriptWeatherSetSplashBlendMode( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSplashBlendMode( pParams, pScript ); } 

	static bool					ScriptWeatherSetSnowHeight( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSnowHeight( pParams, pScript ); }
	static bool					ScriptWeatherSetSnowFrames( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSnowFrames( pParams, pScript ); } 
	static bool					ScriptWeatherSetSnowSize( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSnowSize( pParams, pScript ); } 
	static bool					ScriptWeatherSetSnowBlendMode( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSnowBlendMode( pParams, pScript ); } 
	static bool					ScriptWeatherSetSnowRate( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSnowRate( pParams, pScript ); } 
	static bool					ScriptWeatherSetSnowColor( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSnowColor( pParams, pScript ); }  
	
	static bool					ScriptWeatherSetSnowActive( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetSnowActive( pParams, pScript ); }  
	static bool					ScriptWeatherSetRainActive( Script::CStruct* pParams, Script::CScript* pScript ) { return mp_weather->ScriptWeatherSetRainActive( pParams, pScript ); }  

	//Some Debugging functions
	static	void				sToggleRenderMode();
	static	void				sSetRenderMode(int mode) {s_render_mode = mode;}
	static	void				sSetWireframeMode(int mode) {s_wireframe_mode = mode;}
	static	void				sSetDebugIgnore(uint32 ignore_1, uint32 ignore_0);
	static	void				sDebugCheckForHoles();
	static  uint32				GetRenderMode() {return s_render_mode;}
	static  uint32				GetWireframeMode() {return s_wireframe_mode;}
	
	// Debug inspection stuff								
	static	void				sGetMetrics(Script::CStruct* p_info);

	// soundtrack stuff (hook to xbox specific funcs)
	static	int					sGetNumSoundtracks();
	static	const char*			sGetSoundtrackName( int soundtrack_number );

	// Added by Ken, for use by replay code, which needs to store the status of the sectors
	// so that they can be restored when the replay finishes.
	static	int					sWriteSectorStatusBitfield(uint32 *p_bitfield, int numUint32s);
	static	void				sReadSectorStatusBitfield(uint32 *p_bitfield, int numUint32s);
	
	static	void				sInitReplayStartState();
	static	void				sPrepareSectorsForReplayPlayback(bool store_initial_state);
	static	void				sRestoreSectorsAfterReplayPlayback();
	
	
	
	// Constants
	enum
	{
		MAX_LOADED_SCENES = 4
	};

private:
	// Platform specific function calls	
	static void					s_plat_start_engine();
	static void					s_plat_suspend_engine();
	static void					s_plat_resumeEngine();
	static void					s_plat_pre_render();
	static void					s_plat_post_render();
	static void					s_plat_render_world();
	
	static void					s_plat_set_screen_blur(uint32 amount);

	static void					s_plat_set_letterbox(bool letterbox);
	static void					s_plat_set_color_buffer_clear(bool clear);
			  
	static CScene	*			s_plat_create_scene(const char *p_name, CTexDict *p_tex_dict,
													bool add_super_sectors = true);	// creates an empty scene
	static CScene	*			s_plat_load_scene(const char *p_name, CTexDict *p_tex_dict, bool add_super_sectors,
												  bool is_sky, bool is_dictionary);		// load a platform specific scene file
	static CScene	*			s_plat_load_scene_from_memory(void *p_data, CTexDict *p_tex_dict, bool add_super_sectors,
												  bool is_sky, bool is_dictionary);		// load a platform specific scene file
	static bool					s_plat_add_scene(CScene *p_scene, const char *p_filename);
	static bool					s_plat_unload_scene(CScene *p_scene);

	static CSprite *			s_plat_create_sprite(CWindow2D *p_window);
	static bool					s_plat_destroy_sprite(CSprite *p_sprite);

	static CTextured3dPoly *	s_plat_create_textured_3d_poly();
	static bool					s_plat_destroy_textured_3d_poly(CTextured3dPoly *p_poly);

	static CTexture *			s_plat_create_render_target_texture( int width, int height, int depth, int z_depth );
	static void					s_plat_project_texture_into_scene( Nx::CTexture *p_texture, Nx::CModel *p_model, Nx::CScene *p_scene );
	static void					s_plat_set_projection_texture_camera( Nx::CTexture *p_texture, Gfx::Camera *p_camera );
	static void					s_plat_stop_projection_texture( Nx::CTexture *p_texture );

	static void					s_plat_add_occlusion_poly( uint32 num_verts, Mth::Vector *p_vert_array, uint32 checksum = 0 );
	static void					s_plat_enable_occlusion_poly( uint32 checksum, bool enable );
	static void					s_plat_remove_all_occlusion_polys( void );

	static	const char *		s_plat_get_platform_extension();

	static CModel*				s_plat_init_model();
	static bool					s_plat_uninit_model(CModel* pModel);

	static CGeom*				s_plat_init_geom();
	static bool					s_plat_uninit_geom(CGeom* pGeom);

	static CQuickAnim*			s_plat_init_quick_anim();
	static void					s_plat_uninit_quick_anim(CQuickAnim* pQuickAnim);

	static CMesh*				s_plat_load_mesh(const char* pMeshFileName, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume);
	static CMesh*				s_plat_load_mesh(uint32 id, uint32* pModelData, int modelDataSize, uint8* pCASData, Nx::CTexDict* pTexDict, uint32 texDictOffset, bool isSkin, bool doShadowVolume);
	static bool					s_plat_unload_mesh(CMesh* pMesh);
	
	static void					s_plat_set_mesh_scaling_parameters( SMeshScalingParameters* pParams );

	static bool					s_plat_is_visible(Mth::Vector& center, float radius);										

	static void					s_plat_set_max_multipass_distance(float dist);

	static	void				s_plat_finish_rendering();
		
	static 	int					s_plat_get_num_soundtracks();
	static	const char*			s_plat_get_soundtrack_name( int soundtrack_number );
	
	#ifdef	__PLAT_NGPS__
	// a debug function just for the PS2 - although we could well have it on all platforms
	static 	void 				s_plat_get_metrics(Script::CStruct * p_info);
	#endif

	// Member variables
	static CScene *				sp_loaded_scenes[MAX_LOADED_SCENES];

	static uint32				s_next_avail_sector_checksum;				// next checksum available for clones
	
	static	uint32				s_screen_blur;
	static	uint32				s_render_mode;
	static	uint32				s_wireframe_mode;
	static	uint32				s_debug_ignore_0;
	static	uint32				s_debug_ignore_1;
	
	static 	Lst::HashTable< CParticle > *p_particle_table;
	static  Obj::CGeneralManager* 		sp_moving_object_manager;

	static CImposterManager*	mp_imposter_manager;	
	static CNewParticleManager*	mp_particle_manager;
	static CWeather*			mp_weather;
};


bool ScriptToggleAddScenes(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetScreenBlur(Script::CStruct *pParams, Script::CScript *pScript);


}


#endif



================================================
FILE: Code/Gfx/nxflags.h
================================================
///////////////////////////////////////////////////////////////////////////////////////
// nxflags.h - flag definitions that used to be in nxplugin
//

#ifndef	__GFX_NXFLAGS_H
#define	__GFX_NXFLAGS_H


// Face flags
enum
{
	mFD_SKATABLE			= 0x00000001,
	mFD_NOT_SKATABLE		= 0x00000002,
	mFD_WALL_RIDABLE		= 0x00000004,
	mFD_VERT				= 0x00000008,
	mFD_NON_COLLIDABLE		= 0x00000010,
	mFD_DECAL				= 0x00000020,
	mFD_TRIGGER				= 0x00000040,
	mFD_CAMERA_COLLIDABLE	= 0x00000080,
	mFD_NO_SKATER_SHADOW	= 0x00000100,
	mFD_SKATER_SHADOW		= 0x00000200,
	mFD_NO_SKATER_SHADOW_WALL=0x00000400,
	mFD_UNDER_OK			= 0x00000800,
    mFD_INVISIBLE			= 0x00001000,
	mFD_CASFACEFLAGSEXIST   = 0x00002000,
	mFD_PASS_1_DISABLED     = 0x00004000,
	mFD_PASS_2_ENABLED      = 0x00008000,
	mFD_PASS_3_ENABLED      = 0x00010000,
	mFD_PASS_4_ENABLED      = 0x00020000,
	mFD_RENDER_SEPARATE		= 0x00040000,
	mFD_LIGHTMAPPED			= 0x00080000,
   	mFD_NON_WALL_RIDABLE	= 0x00100000,
	mFD_NON_CAMERA_COLLIDABLE = 0x00200000,
	mFD_EXPORT_COLLISION	= 0x00400000,
};

// Object flags
enum
{
	mSD_INVISIBLE			= 0x0001,	// Invisible in primary viewport
	mSD_NON_COLLIDABLE		= 0x0002,
	mSD_KILLED				= 0x0004,
	mSD_DONT_FOG			= 0x0008,
	mSD_ALWAYS_FACE			= 0x0010,
	mSD_NO_SKATER_SHADOW	= 0x0020,	// This is set at runtime for sectors with every face flagged mFD_SKATER_SHADOW.
	mSD_INVISIBLE2			= 0x0040,	// Invisible in secondary viewport (Mick)
	mSD_OCCLUDER			= 0x0080,	// Occluder (it's a single plane that hides stuff)
	mSD_CLONE				= 0x8000,	// Cloned collision object (Garrett)
};


// Material flags
enum ETerrainType
{
	// If you change any of these,
	// don't forget to change terrain.q
	// to match!
	vTERRAIN_DEFAULT, 	
	vTERRAIN_CONCSMOOTH,	
	vTERRAIN_CONCROUGH,	
	vTERRAIN_METALSMOOTH,
	vTERRAIN_METALROUGH,	
	vTERRAIN_METALCORRUGATED,	
	vTERRAIN_METALGRATING,
	vTERRAIN_METALTIN,	
	vTERRAIN_WOOD,		
	vTERRAIN_WOODMASONITE,
	vTERRAIN_WOODPLYWOOD,
	vTERRAIN_WOODFLIMSY,	
	vTERRAIN_WOODSHINGLE,
	vTERRAIN_WOODPIER,	
	vTERRAIN_BRICK,		
	vTERRAIN_TILE,		
	vTERRAIN_ASPHALT,	
	vTERRAIN_ROCK,		
	vTERRAIN_GRAVEL,		
	vTERRAIN_SIDEWALK,	
	vTERRAIN_GRASS,		
	vTERRAIN_GRASSDRIED,	
	vTERRAIN_DIRT,		
	vTERRAIN_DIRTPACKED,	
	vTERRAIN_WATER,		
	vTERRAIN_ICE,		
	vTERRAIN_SNOW,		
	vTERRAIN_SAND,		
	vTERRAIN_PLEXIGLASS,	
	vTERRAIN_FIBERGLASS,	
	vTERRAIN_CARPET,		
	vTERRAIN_CONVEYOR,	
	vTERRAIN_CHAINLINK,
	vTERRAIN_METALFUTURE,
	vTERRAIN_GENERIC1,	
	vTERRAIN_GENERIC2,
	vTERRAIN_WHEELS, // K: Used only by the skateboard wheels, as a means of identifying them so their color can be changed.	
	vTERRAIN_WETCONC,
	vTERRAIN_METALFENCE,
	vTERRAIN_GRINDTRAIN,
	vTERRAIN_GRINDROPE,
	vTERRAIN_GRINDWIRE,
	vTERRAIN_GRINDCONC,			// New as of 7/29/03
	vTERRAIN_GRINDROUNDMETALPOLE,
	vTERRAIN_GRINDCHAINLINK,
	vTERRAIN_GRINDMETAL,
	vTERRAIN_GRINDWOODRAILING,
	vTERRAIN_GRINDWOODLOG,
	vTERRAIN_GRINDWOOD,
	vTERRAIN_GRINDPLASTIC,
	vTERRAIN_GRINDELECTRICWIRE,
	vTERRAIN_GRINDCABLE,
	vTERRAIN_GRINDCHAIN,
	vTERRAIN_GRINDPLASTICBARRIER,
	vTERRAIN_GRINDNEONLIGHT,
	vTERRAIN_GRINDGLASSMONSTER,
	vTERRAIN_GRINDBANYONTREE,
	vTERRAIN_GRINDBRASSRAIL,
	vTERRAIN_GRINDCATWALK,
	vTERRAIN_GRINDTANKTURRET,

	// If anyone adds a new terrain so that vNUM_TERRAIN_TYPES > 64, let Steve G know
	vNUM_TERRAIN_TYPES
};


#endif



================================================
FILE: Code/Gfx/nxparticle.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxParticle.cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define next_random() ((((float)rand() / RAND_MAX ) * 2.0f ) - 1.0f)

//NsVector pright;
//NsVector pup;
//NsVector pat;

namespace	Nx
{
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CParticle::plat_render( void )
{
	printf ("STUB: plat_render\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CParticle::plat_get_position( int entry, int list, float * x, float * y, float * z )
{
	printf ("STUB: plat_get_position\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CParticle::plat_set_position( int entry, int list, float x, float y, float z )
{
	printf ("STUB: plat_set_position\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CParticle::plat_add_position( int entry, int list, float x, float y, float z )
{
	printf ("STUB: plat_add_position\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CParticle::plat_set_active( bool active )
{
	// PS2 & Gamecube do nothing here.
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CParticle::plat_build_path( void )
{
	printf ("STUB: plat_build_path\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CParticle::plat_get_num_particle_colors( void ) { printf ("STUB: plat_get_num_particle_colors\n"); return 0; };
int CParticle::plat_get_num_vertex_lists( void ) { printf ("STUB: plat_get_num_vertex_lists\n"); return 0; };
void CParticle::plat_set_sr( int entry, uint8 value ) { printf ("STUB: plat_set_sr\n"); }
void CParticle::plat_set_sg( int entry, uint8 value ) { printf ("STUB: plat_set_sg\n"); }
void CParticle::plat_set_sb( int entry, uint8 value ) { printf ("STUB: plat_set_sb\n"); }
void CParticle::plat_set_sa( int entry, uint8 value ) { printf ("STUB: plat_set_sa\n"); }
void CParticle::plat_set_mr( int entry, uint8 value ) { printf ("STUB: plat_set_mr\n"); }
void CParticle::plat_set_mg( int entry, uint8 value ) { printf ("STUB: plat_set_mg\n"); }
void CParticle::plat_set_mb( int entry, uint8 value ) { printf ("STUB: plat_set_mb\n"); }
void CParticle::plat_set_ma( int entry, uint8 value ) { printf ("STUB: plat_set_ma\n"); }
void CParticle::plat_set_er( int entry, uint8 value ) { printf ("STUB: plat_set_er\n"); }
void CParticle::plat_set_eg( int entry, uint8 value ) { printf ("STUB: plat_set_eg\n"); }
void CParticle::plat_set_eb( int entry, uint8 value ) { printf ("STUB: plat_set_eb\n"); }
void CParticle::plat_set_ea( int entry, uint8 value ) { printf ("STUB: plat_set_ea\n"); }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CParticleEntry::CParticleEntry()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CParticleEntry::~CParticleEntry()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CParticle::set_defaults( void )
{
#define px 0x800		//0x4a1;
#define py 0x90			//-0x117;		//0xfffffee9;
#define pz -0x3a0		//0xb96;

	mp_emit_script = NULL;
	mp_update_script = NULL;
	mp_params = NULL;
	m_life_set = false;
	m_end_set = false;


	// Temp: Set up some debug values.
	m_pos[X] = px;
	m_pos[Y] = py;
	m_pos[Z] = pz;

	m_sw = 2.0f;
	m_sh = 2.0f;
	m_ew = 2.0f;
	m_eh = 2.0f;
	
	m_life_min = 2.0f;
	m_life_max = 2.0f;

	m_emit_w = 1.0f;	//16.0f;
	m_emit_h = 1.0f;    //16.0f;
	m_emit_angle_spread = 0.5f;		//0.5f;

	m_fx = 0.0f;
	m_fy = -0.02f;
	m_fz = 0.0f;

	m_tx = 0.0f;		// Default target: straight up.
	m_ty = 1.0f;
	m_tz = 0.0f;

	m_ax = 0.0f;		// Default angles: No rotation.
	m_ay = 0.0f;
	m_az = 0.0f;

	m_speed_min = 1.0f;
	m_speed_max = 2.0f;

	m_random_angle = false;
	m_circular_emit = true;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CParticle::CParticle()
{
	set_defaults();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CParticle::CParticle( uint32 checksum )
{
	set_defaults();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CParticle::CParticle( uint32 checksum, int maxParticles )
{
	set_defaults();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CParticle::~CParticle()
{
	if ( mp_emit_script )
	{
		delete mp_emit_script;
	}
	if ( mp_update_script )
	{
		delete mp_update_script;
	}
	if ( mp_params )
	{
		delete mp_params;
	}
	CEngine::sGetParticleTable()->FlushItem( m_checksum );		// remove reference from hash table
//	printf ("\nJust Deleted 0x%x, contenets are now:\n",m_checksum);
//	CEngine::sGetParticleTable()->PrintContents();
}

void CParticle::process( float delta_time )
{
	int				lp;
	CParticleEntry*	p_particle;

	// If flagged for deletion, check if the particle system is empty and delete if it is.
	if ( m_delete_when_empty )
	{
		if ( !m_num_particles )
		{
			// removing reference is sone in the destructor
			//CEngine::sGetParticleTable()->FlushItem( m_checksum );		// remove reference from hash table
			delete this;
			return;
		}
	}

	// Don't process if paused.
	if (Mdl::FrontEnd::Instance()->GamePaused() && !Replay::RunningReplay())
	{
		return;
	}	
	if (Replay::Paused())
	{
		return;
	}	
	
	if ( GetSuspendComponent()->SkipLogic( ) )
	{
		return;
	}
	
	

	// Script updates.
	if ( mp_update_script )
	{
		mp_update_script->Update();
	}

// We can't check for off screen or not until we've run the update script
// as that might change the position (like for sparks)

	// Don't process if off screen
//	if (!Nx::CEngine::sIsVisible(m_pos,m_bounding_radius))
//	{
//		//printf ("Off - process\n");
//		Gfx::AddDebugLine(m_pos,Mth::Vector(0,0,0),0x0000ff,0x0000ff,100);	 	// where is it?
//		return;
//	}


	if ( mp_emit_script )
	{
		mp_emit_script->Update();
//		if ( mp_emit_script->Update() == Script::ESCRIPTRETURNVAL_FINISHED )
//		{
//			m_delete_when_empty = true;
//		}
	}



	// If life was not set, assume we're a screen-facing system.
	if ( !m_life_set ) return;

	// Physics update.
	for ( lp = 0, p_particle = mp_particle_array; lp < m_num_particles; lp++, p_particle++ )
	{
		// Update time/life.
		p_particle->m_time += delta_time;

		// Apply force to velocity.
		p_particle->m_vx += p_particle->m_fx;
		p_particle->m_vy += p_particle->m_fy;
		p_particle->m_vz += p_particle->m_fz;

		// Copy history.
		float x, y, z;
		for ( int lp2 = ( plat_get_num_vertex_lists() - 1 ); lp2 > 0; lp2-- )
		{
			plat_get_position( lp, lp2 - 1, &x, &y, &z );
			plat_set_position( lp, lp2, x, y, z );
		}

		// Apply velocity to position.
		plat_add_position( lp, 0, p_particle->m_vx, p_particle->m_vy, p_particle->m_vz );
	}

	// Delete old particles.
	for ( lp = 0, p_particle = mp_particle_array; lp < m_num_particles; lp++, p_particle++ )
	{
		if ( p_particle->m_time > p_particle->m_life )
		{
			// Delete old particles by copying last entry to entry that is being deleted.
			m_num_particles--;
			memcpy( p_particle, &mp_particle_array[m_num_particles], sizeof( CParticleEntry ) );

			float x, y, z;
			for ( int lp2 = 0; lp2 < plat_get_num_vertex_lists(); lp2++ )
			{
				plat_get_position( m_num_particles, lp2, &x, &y, &z );
				plat_set_position( lp, lp2, x, y, z );
				plat_set_position( m_num_particles, lp2, 1024.0f, 1024.0f, 1024.0f );
			}

			// Since we copied the last one to the current slot, decrement lp counter & particle
			// pointer so that the copied entry gets checked for deletion as well.
			p_particle--;
			lp--;
		}
	}
}

void CParticle::render( void )
{

	if ( GetSuspendComponent()->SkipRender( ) )
	{
		return;
	}

	plat_render();
}

void CParticle::emit( int count )
{
	int first, last;

	// Do nothing here if flagged for deletion.
	if ( m_delete_when_empty )
	{
		return;
	}

	// Calculate the range of array entries to emit to.
	first = m_num_particles;
	if ( ( m_num_particles + count ) <= m_max_particles )
	{
		m_num_particles += count;
	}
	else
	{
		m_num_particles = m_max_particles;
	}
	last = m_num_particles;

	// Build matrix to rotate the particle to the target.
	Mth::Matrix mt;

	mt.GetPos().Set();
	mt.GetAt().Set( m_tx, m_ty, m_tz );
	mt.GetAt().Normalize();

	Mth::Vector temp;
	temp.Set( 0.0f, 1.0f, 0.0f );
	if ( ( fabs( mt.GetAt()[Y] ) > fabs( mt.GetAt()[X] ) ) && ( fabs( mt.GetAt()[Y] ) > fabs( mt.GetAt()[Z] ) ) )
	{
		// Y Major - use Z as up.
		temp.Set( 0.0f, 0.0f, 1.0f );
	}

	mt.GetRight() = Mth::CrossProduct( temp, mt.GetAt() ); 
	mt.GetRight().Normalize();
	mt.GetUp() = Mth::CrossProduct( mt.GetRight(), mt.GetAt() ); 
	mt.GetUp().Normalize();

	// Add on rotation.
	mt.RotateX( Mth::DegToRad(m_ax) );
	mt.RotateY( Mth::DegToRad(m_ay) );
	mt.RotateZ( Mth::DegToRad(m_az) );

//	pright.set( mt.GetRight()[X], mt.GetRight()[Y], mt.GetRight()[Z] );
//	pup.set( mt.GetUp()[X], mt.GetUp()[Y], mt.GetUp()[Z] );
//	pat.set( mt.GetAt()[X], mt.GetAt()[Y], mt.GetAt()[Z] );

	// Emit particles.
	for ( int lp = first; lp < last; lp++ )
	{
		if ( m_life_set )
		{
			// Random values for this emission.
			float rx = next_random();
			float ry = next_random();

			// Calculate emission initial velocity.		
			Mth::Vector velocity;
			velocity[X] = rx * ( Mth::PI * ( m_emit_angle_spread / 4.0f ) );
			velocity[Y] = ry * ( Mth::PI * ( m_emit_angle_spread / 4.0f ) );  
			velocity[Z] = 1.0f - (( velocity[X] * velocity[X] ) + ( velocity[Y] * velocity[Y] ));
			velocity[W] = 1.0f;

			float speed_mul = ( ( m_speed_max - m_speed_min ) * ((float)rand() / RAND_MAX ) ) + m_speed_min;
			velocity[X] *= speed_mul;
			velocity[Y] *= speed_mul;
			velocity[Z] *= speed_mul;

			// Get new rx, ry to make the particles cross over one another.
			if ( m_random_angle )
			{
				rx = next_random();
				ry = next_random();
			}

			// Normalize rx, ry to make a circular emission.
			if ( m_circular_emit )
			{
				float l = 1.0f / sqrtf( ( rx * rx ) + ( ry * ry ) );
				rx = rx * ( l * (float)fabs( rx ) );		// Note: length is scaled by random factor.
				ry = ry * ( l * (float)fabs( ry ) );
			}

			// Set position around emitter width/height.
			Mth::Vector pos;
			pos[X] = m_emit_w * rx;
			pos[Y] = m_emit_h * ry;
			pos[Z] = 0.0f;
			pos[W] = 1.0f;

			// Set force.
			mp_particle_array[lp].m_fx = m_fx;
			mp_particle_array[lp].m_fy = m_fy;
			mp_particle_array[lp].m_fz = m_fz;

			// Set particle w/h
			mp_particle_array[lp].m_sw = m_sw;
			mp_particle_array[lp].m_sh = m_sh;
			mp_particle_array[lp].m_ew = m_ew;
			mp_particle_array[lp].m_eh = m_eh;

			// Set time & life.
			mp_particle_array[lp].m_time = 0.0f;
			mp_particle_array[lp].m_life = ( ( m_life_max - m_life_min ) * ((float)rand() / RAND_MAX ) ) + m_life_min;

			// Orient the particles to the target.
			Mth::Vector v;
			v = mt.Transform( velocity );
			mp_particle_array[lp].m_vx = v[X];
			mp_particle_array[lp].m_vy = v[Y];
			mp_particle_array[lp].m_vz = v[Z];
			
			v = mt.Transform( pos );

			for ( int lp2 = 0; lp2 < plat_get_num_vertex_lists(); lp2++ )
			{
				plat_set_position( lp, lp2, v[X], v[Y], v[Z] );
			}
		}
		else
		{
			// Set particle w/h
			mp_particle_array[lp].m_sw = m_sw;
			mp_particle_array[lp].m_sh = m_sh;
			mp_particle_array[lp].m_ew = m_ew;
			mp_particle_array[lp].m_eh = m_eh;

			// Set time & life.
			mp_particle_array[lp].m_time = 0.0f;
			mp_particle_array[lp].m_life = ( ( m_life_max - m_life_min ) * ((float)rand() / RAND_MAX ) ) + m_life_min;

			// Screen-facing poly. Just set to 0,0,0.
			for ( int lp2 = 0; lp2 < plat_get_num_vertex_lists(); lp2++ )
			{
				plat_set_position( lp, lp2, 0, 0, 0 );
			}
		}

	}
}

void CParticle::set_emit_script( uint32 checksum, Script::CStruct* pParams )
{
	if ( checksum )
	{
		if ( !mp_emit_script )
		{
			mp_emit_script = new Script::CScript();
			#ifdef __NOPT_ASSERT__
			mp_emit_script->SetCommentString("Created in CParticle::set_emit_script(...)");
			#endif
		}
		if ( !mp_params )
		{
			mp_params = new Script::CStruct;
		}
		mp_params->AppendStructure( pParams );
		mp_emit_script->SetScript( checksum, mp_params, this );
//		mp_emit_script->Update();
	}
}

void CParticle::set_update_script( uint32 checksum, Script::CStruct* pParams )
{
	if ( checksum )
	{
		if ( !mp_update_script )
		{
			mp_update_script = new Script::CScript();
			#ifdef __NOPT_ASSERT__
			mp_update_script->SetCommentString("Created in CParticle::set_update_script(...)");
			#endif
		}
		if ( !mp_params )
		{
			mp_params = new Script::CStruct;
		}
		mp_params->AppendStructure( pParams );
		mp_update_script->SetScript( checksum, mp_params, this );
	}
}


// Refresh a particle system after things like position have changed 

void CParticle::Refresh()
{
	plat_build_path();
}



void CParticle::SetActive( bool active )
{
	m_active = active;
	plat_set_active( active );
}

/******************************************************************/
/*   CParticle::CallMemberFunction                               */
/*   Call a member function, based on a checksum                  */
/*   This is usually the checksum of the name of the function     */
/*   but can actually be any number, as it just uses a switch     */
/*   note this is a virtual function, so the same checksum        */
/*   can do differnet things for different objects                */
/*                                                                */
/*                                                                */
/******************************************************************/

bool CParticle::CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript )
{
    
	Dbg_MsgAssert(pScript,("NULL pScript"));

	switch(Checksum)
	{
		// @script | SetPos | Sets the position of the particle system.
		// @parmopt vector | pos | world position.
		// @parmopt float | x	| x world position.
		// @parmopt float | y	| y world position.
		// @parmopt float | z	| z world position.
		case 0x99cd17b:		// SetPos
			{
				Mth::Vector pos;
				float x = 0.0f;
				float y = 0.0f;
				float z = 0.0f;
				if ( pParams->GetVector(CRCD(0x7f261953,"pos"),&pos) )
				{
					x = pos[X];
					y = pos[Y];
					z = pos[Z];
				}
				else
				{
					pParams->GetFloat(CRCD(0x7323e97c,"x"),&x);
					pParams->GetFloat(CRCD(0x424d9ea,"y"),&y);
					pParams->GetFloat(CRCD(0x9d2d8850,"z"),&z);
				}

				set_pos( x, y, z );
			}
			break;
		// @script | SetSpeedRange | Sets the min & max speed of the particles upon emission.
		// @parm float | min | minimum speed. If only min is provided, all speeds will be set to min.
		// @parmopt float | max | maximum speed.
		case 0x63274395:		// SetSpeedRange
			{
				float min = 0.0f;
				pParams->GetFloat(CRCD(0x5e84e22f,"min"),&min);
				float max = min;
				pParams->GetFloat(CRCD(0x6289dd76,"max"),&max);

				set_speed_range( min, max );
			}
			break;
		// @script | SetEmitRange | Sets the emission range (width and height).
		// @parm float | width | Width of emission area. If only width is provided, height will be set to width.
		// @parmopt float | height | Height of emission area.
		case 0x8ffdef38:		// SetEmitRange
			{
				float width = 0.0f;
				pParams->GetFloat(CRCD(0x73e5bad0,"width"),&width);
				float height = width;
				pParams->GetFloat(CRCD(0xab21af0,"height"),&height);
			
				set_emit_range( width, height );
			}
			break;
		// @script | SetAngleSpread | Sets the angle spread for particle emissions.
		// @parm float | spread | Angle spread. 0=No spread. 1=Full spread (180 degrees).
		case 0x7911855b:		// SetAngleSpread
			{
				float spread = 0.0f;
				pParams->GetFloat(CRCD(0x834a5f87,"spread"),&spread);
			
				set_emit_angle_spread( spread );
			}
		break;
	

		// @script | SetRandomAngle | Sets whether the emission angle is random or not.
		// @parm int | random | 1 means choose a random angle regardless of the emission position. 0 means use the emission position to define the emission angle.
		case 0xdb8621f7:		// SetRandomAngle
			{
				int random = 0;
				pParams->GetInteger("random",(int*)&random);
			
				set_random_angle( random > 0 );
			}
			break;
		
		// @script | SetCircularEmit | Sets whether the emission area is circular or square.
		// @parm int | circular | 1 means use a circular emission area. 0 means use a square emission area.
		case 0xe985a9a6:		// SetCircularEmit
			{
				int circular = 0;
				pParams->GetInteger("circular",(int*)&circular);
			
				set_circular_emit( circular > 0 );
			}
			break;
		
		// @script | SetForce | Sets the force acting on the particles.
		// @parmopt vector | force	| Force.
		// @parmopt float | x	| x force.
		// @parmopt float | y	| y force.
		// @parmopt float | z	| z force.
		case 0xa075ee45:		// SetForce
			{
				Mth::Vector force;
				float x = 0.0f;
				float y = 0.0f;
				float z = 0.0f;
				if ( pParams->GetVector("force",&force) )
				{
					x = force[X];
					y = force[Y];
					z = force[Z];
				}
				else
				{
					pParams->GetFloat("x",&x);
					pParams->GetFloat("y",&y);
					pParams->GetFloat("z",&z);
				}

				set_force( x, y, z );
			}
			break;
			
		// @script | SetParticleSize | Sets the size of the particles.
		// @parm float | sw | Start width of the particle. If only width is provided, height is set to width.
		// @parmopt float | sh | Start height of the particle.
		// @parm float | ew | End width of the particle. If only width is provided, height is set to width.
		// @parmopt float | eh | End height of the particle.
		case 0x2a479569:		// SetParticleSize
			{
				float sw = 0.0f;
				pParams->GetFloat("sw",&sw);
				float sh = sw;
				pParams->GetFloat("sh",&sh);
			
				set_particle_start_size( sw, sh );

				float ew = sw;
				float eh;
				if ( pParams->GetFloat("ew",&ew) )
				{
					eh = ew;
				}
				else
				{
					eh = sh;
				}
				pParams->GetFloat("eh",&eh);
			
				set_particle_end_size( ew, eh );
			}
			break;
		
		// @script | SetLife | Sets the life of the particles in seconds.
		// @parm float | min | Minimum life of the particle. 1.0 = 1 second.
		// @parmopt float | max | Maximum life of the particle. 1.0 = 1 second. If not specified, will be set to same as min.
		case 0xd3f1d333:		// SetLife
			{
				float min = 0.0f;
				pParams->GetFloat("min",&min);
				float max = min;
				pParams->GetFloat("max",&max);
			
				set_particle_life( min, max );
			}
			break;

		// @script | SetEmitTarget | Sets the target position that the particle system will emit particles towards.
		// @parmopt vector | target	| Target.
		// @parmopt float | x	| x target.
		// @parmopt float | y	| y target.
		// @parmopt float | z	| z target.
		case 0xb373fb6e:		// SetEmitTarget
			{
				Mth::Vector target;
				float x = 0.0f;
				float y = 0.0f;
				float z = 0.0f;
				if ( pParams->GetVector("target",&target) )
				{
					x = target[X];
					y = target[Y];
					z = target[Z];
				}
				else
				{
					pParams->GetFloat("x",&x);
					pParams->GetFloat("y",&y);
					pParams->GetFloat("z",&z);
				}
				
				// If it's just 0,1,0, then rotate it by the angles of the node
				if (x == 0.0f && y == 1.0f && z == 0.0f)
				{
					// not done here though....
					
				}
				else
				{
					set_emit_target( x, y, z );
				}
				

			}
			break;
		// @script | SetEmitAngle | Sets the emission angle. This is done after the target, so you can set a target as well as angles to rotate in addition to the target angle. Target defaults to 0,1,0 (straight up), so all angles rotate from there.
		// @parmopt vector | angle	| Angle (in degrees).
		// @parmopt float | x	| x angle.
		// @parmopt float | y	| y angle.
		// @parmopt float | z	| z angle.
		case 0x1cfbf078:		// SetEmitAngle
			{
				Mth::Vector angle;
				float x = 0.0f;
				float y = 0.0f;
				float z = 0.0f;
				if ( pParams->GetVector("angle",&angle) )
				{
					x = angle[X];
					y = angle[Y];
					z = angle[Z];
				}
				else
				{
					pParams->GetFloat("x",&x);
					pParams->GetFloat("y",&y);
					pParams->GetFloat("z",&z);
				}

				set_emit_angle( x, y, z );
			}
			break;
		// CreateParticleSystem colorentries=n	// default 2.
		// SetColorEntry slot=n r=n g=n b=n


		// @script | SetColor | Sets the color for the next particle emission.
		// @parmopt int | corner | | The corner to set. Omitting this paramter means set all corners.
		// @parmopt int | sr | | Start red color element to set (0-255). 128 is normal.
		// @parmopt int | sg | | Start green color element to set (0-255). 128 is normal.
		// @parmopt int | sb | | Start blue color element to set (0-255). 128 is normal.
		// @parmopt int | sa | | Start alpha color element to set (0-255). 255 is opaque, 0 is invisible.
		// @parmopt int | mr | | Mid red color element to set (0-255). 128 is normal.
		// @parmopt int | mg | | Mid green color element to set (0-255). 128 is normal.
		// @parmopt int | mb | | Mid blue color element to set (0-255). 128 is normal.
		// @parmopt int | ma | | Mid alpha color element to set (0-255). 255 is opaque, 0 is invisible.
		// @parmopt int | er | | End red color element to set (0-255). 128 is normal.
		// @parmopt int | eg | | End green color element to set (0-255). 128 is normal.
		// @parmopt int | eb | | End blue color element to set (0-255). 128 is normal.
		// @parmopt int | ea | | End alpha color element to set (0-255). 255 is opaque, 0 is invisible.
		// @parmopt int | midtime | | Sets the % through the particle's life that the mid color is reached.
		// 0.5 means halfway through. Use values from 0-1. By default, the mid color is not used. By setting
		// the mid time, the middle color will be used.
		case 0x514b2584:		// SetColor
			{
				int corner;
				int first = 0;
				int last = 0;
				if ( pParams->GetInteger("corner",&corner) )
				{
					first = last = corner;
				}
				else
				{
					first = 0;
					last = plat_get_num_particle_colors() - 1;
				}
				if ( ( first < plat_get_num_particle_colors() ) && ( last < plat_get_num_particle_colors() ) )
				{
					for ( int entry = first; entry < ( last + 1 ); entry++ )
					{
						if ( entry < plat_get_num_particle_colors() )
						{
							int elem;
							if ( pParams->GetInteger("er",&elem) )
							{
								plat_set_er( entry, elem );
								m_end_set = true;
							}
							if ( pParams->GetInteger("eg",&elem) )
							{
								plat_set_eg( entry, elem );
								m_end_set = true;
							}
							if ( pParams->GetInteger("eb",&elem) )
							{
								plat_set_eb( entry, elem );
								m_end_set = true;
							}
							if ( pParams->GetInteger("ea",&elem) )
							{
								plat_set_ea( entry, elem );
								m_end_set = true;
							}
							if ( pParams->GetInteger("mr",&elem) )
							{
								plat_set_mr( entry, elem );
							}
							if ( pParams->GetInteger("mg",&elem) )
							{
								plat_set_mg( entry, elem );
							}
							if ( pParams->GetInteger("mb",&elem) )
							{
								plat_set_mb( entry, elem );
							}
							if ( pParams->GetInteger("ma",&elem) )
							{
								plat_set_ma( entry, elem );
							}
							if ( pParams->GetInteger("sr",&elem) )
							{
								plat_set_sr( entry, elem );
								if ( !m_end_set ) plat_set_er( entry, elem ); 
							}
							if ( pParams->GetInteger("sg",&elem) )
							{
								plat_set_sg( entry, elem );
								if ( !m_end_set ) plat_set_eg( entry, elem ); 
							}
							if ( pParams->GetInteger("sb",&elem) )
							{
								plat_set_sb( entry, elem );
								if ( !m_end_set ) plat_set_eb( entry, elem ); 
							}
							if ( pParams->GetInteger("sa",&elem) )
							{
								plat_set_sa( entry, elem );
								if ( !m_end_set ) plat_set_ea( entry, elem ); 
							}
						}
					}
				}
				pParams->GetFloat("midtime",&m_mid_time);
			}
			break;
		// @script | Emit | Emits the specified number of particles.
		// @parm int | num | Number of particles to emit.
		case 0x711b1a6a:		// Emit
			{
				int num = 1;		// Defaults to 1 particle emitted.
				pParams->GetInteger("num",&num);

				emit( num );
			}
			break;
		// @script | EmitRate | Emits at the specified rate - 1=1 particle per frame.
		// @parm float | rate | The rate to emit at.
		case 0x35b65bd4:		// EmitRate
			{
				float rate = 1.0f;		// Defaults to 1 particle emitted per frame.
				pParams->GetFloat("rate",&rate);

				set_emit_rate( rate );
			}
			break;
		// @script | BuildPath | Builds a path based on the current physics data.
		case 0xa8699e0e:		// BuildPath
			{
				plat_build_path();
			}
			break;

		// @script | EmptySystem | Empties the particle system immediately (sets number of particles to 0.
		case 0x482482c6:		// EmptySystem
			{
				m_num_particles = 0;
			}
			break;

		default:
			return Obj::CMovingObject::CallMemberFunction( Checksum, pParams, pScript );
	}

	return true;
}

}



================================================
FILE: Code/Gfx/nxparticle.h
================================================
///////////////////////////////////////////////////////////////////////////////
// NxParticle.h



#ifndef	__GFX_NXPARTICLE_H__
#define	__GFX_NXPARTICLE_H__

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#ifndef __GEL_OBJECT_H
#include 
#endif
#include 



namespace Script
{

// Forward declarations
class CScript;

}

namespace Nx
{

// Forward declarations
class CParticleEntry;

//////////////////////////////////////////////////////////////////////////////
// The CParticle class is the platform independent abstract base class
// of the platform specific CParticle classes
class	CParticle : public Obj::CMovingObject
{
public:
							CParticle();
							CParticle( uint32 checksum );
							CParticle( uint32 checksum, int maxParticles );
	virtual					~CParticle();

	void					process( float delta_time );
	void					render( void );

	void					emit( int count );

	void					set_pos( float x, float y, float z )			{ m_pos[X] = x; m_pos[Y] = y; m_pos[Z] = z; }
	void					set_speed_range( float min, float max )			{ m_speed_min = min; m_speed_max = max; }
	void					set_emit_range( float width, float height )		{ m_emit_w = width; m_emit_h = height; }
	void					set_emit_angle_spread( float spread )			{ m_emit_angle_spread = spread; }
	void					set_random_angle( bool random_angle )			{ m_random_angle = random_angle; }
	void					set_circular_emit( bool circular_emit )			{ m_circular_emit = circular_emit; }
	void					set_force( float x, float y, float z )			{ m_fx = x; m_fy = y; m_fz = z; }
	void					set_particle_start_size( float w, float h )		{ m_sw = w; m_sh = h; }
	void					set_particle_end_size( float w, float h )		{ m_ew = w; m_eh = h; }
	void					set_particle_life( float min, float max )		{ m_life_min = min; m_life_max = max; m_life_set = true; }
	void					set_emit_target( float x, float y, float z )	{ m_tx = x; m_ty = y; m_tz = z; }
	void					set_emit_angle( float x, float y, float z )		{ m_ax = x; m_ay = y; m_az = z; }
	void					set_emit_rate( float rate )						{ m_emit_rate = rate; }

	void					set_checkum(uint32 checksum) {m_checksum = checksum;}

	void					delete_when_empty( void )						{ m_delete_when_empty = true; }

	bool					CallMemberFunction (uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript);	// Call a member function based on the checksum of the function name

	void					set_emit_script( uint32 checksum, Script::CStruct* pParams );
	void					set_update_script( uint32 checksum, Script::CStruct* pParams );

	void					SetActive( bool active );
	bool					IsActive()										{ return m_active; }
	
	void					SetEmitting( bool emitting )					{ m_emitting = emitting; }
	bool					IsEmitting()									{ return m_emitting; }


	void					SetPerm( bool perm )							{ m_perm = perm; }
	bool					IsPerm()										{ return m_perm; }

	void					SetNumParticles( int num )						{ m_num_particles = num; }
	int						GetNumParticles( void )							{ return m_num_particles; }

	void 					Refresh();		


protected:
	// Extensions for Mike's system.
	float					m_emit_rate;			// The rate of emission - 1.0 = 1 per frame.

	// Member variables (protected so the p-classes can access them)
	int						m_max_particles;		// Maximum size of particle array.
	int						m_num_particles;		// Current number of active particles.
		
	float					m_speed_min;			// Initial Speed setting.
	float					m_speed_max;
	float					m_emit_w, m_emit_h;		// Emitter dimensions.
	float					m_emit_angle_spread;	// Angle spread from 0 to 1.
	
	// Should be changed to a flag value...
	bool					m_random_angle;			// true means use a random angle, regardless of the emit pos.
	bool					m_circular_emit;		// true means emit in a circle.

	float					m_fx, m_fy, m_fz;		// Initial Force values. fy is gravity, will not dec to 0.
	float					m_sw, m_sh;				// Initial width & height.
	float					m_ew, m_eh;				// Initial width & height.
	float					m_life_min, m_life_max;	// Life min & max

	float					m_tx, m_ty, m_tz;		// Target position.
	float					m_ax, m_ay, m_az;		// Angle rotations.
	
	uint32					m_checksum;

	bool					m_delete_when_empty;	// When m_num_particles == 0, this system will self-delete.

	CParticleEntry*			mp_particle_array;		// Pointer to array of particles.

	Script::CScript*		mp_emit_script;
	Script::CScript*		mp_update_script;

	// parameters that will be passed to the emit/update scripts
	// generally used for passing the ID of a moving object
	Script::CStruct*		mp_params;
	bool					m_active;
	bool					m_emitting;

	float					m_mid_time;					// Time of the mid color.

	bool					m_life_set;

	bool					m_end_set;

	bool					m_perm;						// true if not deleted between levels

	// The virtual functions have a stub implementation.
private:
	virtual void			plat_render( void );
	virtual void			plat_get_position( int entry, int list, float * x, float * y, float * z );
	virtual void			plat_set_position( int entry, int list, float x, float y, float z );
	virtual void			plat_add_position( int entry, int list, float x, float y, float z );
	virtual int				plat_get_num_vertex_lists( void );
	virtual int				plat_get_num_particle_colors( void );
	virtual void			plat_set_sr( int entry, uint8 value );
	virtual void			plat_set_sg( int entry, uint8 value );
	virtual void			plat_set_sb( int entry, uint8 value );
	virtual void			plat_set_sa( int entry, uint8 value );
	virtual void			plat_set_mr( int entry, uint8 value );
	virtual void			plat_set_mg( int entry, uint8 value );
	virtual void			plat_set_mb( int entry, uint8 value );
	virtual void			plat_set_ma( int entry, uint8 value );
	virtual void			plat_set_er( int entry, uint8 value );
	virtual void			plat_set_eg( int entry, uint8 value );
	virtual void			plat_set_eb( int entry, uint8 value );
	virtual void			plat_set_ea( int entry, uint8 value );
	virtual void			plat_set_active( bool active );
	virtual void			plat_build_path( void );

	void					set_defaults( void );
};

//////////////////////////////////////////////////////////////////////////////
// Particle system entry. Does very little other than hold information. The controller does all the work.
class CParticleEntry
{
public:
							CParticleEntry();
							~CParticleEntry();

//							float					m_x, m_y, m_z;				// Current x, y, z positions.
							float					m_vx, m_vy, m_vz;			// Current velocity.
							float					m_fx, m_fy, m_fz;			// Current force.
							float					m_sw, m_sh;					// Start Width & Height.
							float					m_ew, m_eh;					// EndWidth & Height.
							float					m_time, m_life;				// Current time & life of particle.
protected:
private:
	friend CParticle;
};

}

#endif // 




================================================
FILE: Code/Gfx/nxparticlemgr.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticleMgr.cpp
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#include 
#include "gfx/nx.h"
#include "gfx/nxparticlemgr.h"

namespace Nx
{
void process_particles( float delta_time )
{
//	plat_process_particles( delta_time );
	if( CEngine::sGetParticleTable() )
	{
		CParticle *p_particle;
		CEngine::sGetParticleTable()->IterateStart();
		while(( p_particle = CEngine::sGetParticleTable()->IterateNext()))
		{
			if ( p_particle->IsActive() ) p_particle->process( delta_time );
		}
	}
}

void render_particles( void )
{
//	plat_process_particles( delta_time );
	if( CEngine::sGetParticleTable() )
	{
		CParticle *p_particle;
		CEngine::sGetParticleTable()->IterateStart();
		while(( p_particle = CEngine::sGetParticleTable()->IterateNext()))
		{
			if ( p_particle->IsActive() ) p_particle->render();
		}
	}
}

CParticle* get_particle( uint32 checksum )
{
	CParticle *p_particle = NULL;
	if( CEngine::sGetParticleTable() )
	{
		p_particle = CEngine::sGetParticleTable()->GetItem( checksum );
	}
	return p_particle;
}

CParticle* create_particle( uint32 checksum, uint32 type_checksum, int max_particles, int max_streams, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history, int perm )
{
	// Types to add:
	// 1. Flat color quad
	// 2. Gouraud quad
	// 3. n sided glow (center color + outer color)
	// 4. n sided 2 layer glow ( center color + mid color + outer color)
	// 5. n spiked star (center color + mid color + spike color)
	// 6. Lines (current color + previous color)
	// 7. Ribbons - volumetric lines made from quads (current color + previous color)

	CParticle *p_particle =  plat_create_particle( checksum, type_checksum, max_particles, max_streams, texture_checksum, blendmode_checksum, fix, num_segments, split, history );
	
	p_particle->SetPerm(perm);
	
	p_particle->SetActive( true );
	p_particle->SetEmitting( false ); 	// This is already set to false, this si just a reminder

	// Add to our list of particles.
	CEngine::sGetParticleTable()->PutItem( checksum, p_particle );
	return p_particle;
}

void destroy_particle( uint32 checksum )
{
	CParticle * p = get_particle( checksum );	 	// get item
	CEngine::sGetParticleTable()->FlushItem( checksum );		// remove reference from hash table
	delete p;										// deleted particle, will remove itself from the table
}

void destroy_particle_when_empty( uint32 checksum )
{
	CParticle * p = get_particle( checksum );	 	// get item
	p->delete_when_empty();
}

void destroy_all_particles(  )
{
	if( CEngine::sGetParticleTable() )
	{
		CParticle *p_particle;
		CEngine::sGetParticleTable()->IterateStart();
		while(( p_particle = CEngine::sGetParticleTable()->IterateNext()))
		{
			delete p_particle;
		}
	}
}

void destroy_all_temp_particles(  )
{
	if( CEngine::sGetParticleTable() )
	{
		CParticle *p_particle;
		CEngine::sGetParticleTable()->IterateStart();
		while(( p_particle = CEngine::sGetParticleTable()->IterateNext()))
		{
			if (!p_particle->IsPerm())
			{
				delete p_particle;
			}
		}
	}
}



} // Nx


================================================
FILE: Code/Gfx/nxparticlemgr.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       p_nxParticleMgr.h
//* OWNER:          Paul Robinson
//* CREATION DATE:  3/27/2002
//****************************************************************************

#ifndef	__GFX_P_NX_PARTICLE_MGR_H__
#define	__GFX_P_NX_PARTICLE_MGR_H__
    
#include "gfx/nxparticle.h"
                   
namespace Nx
{

void process_particles( float delta_time );
void render_particles( void );
CParticle* get_particle( uint32 checksum );
CParticle* create_particle( uint32 checksum, uint32 type_checksum, int max_particles, int max_streams, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history, int perm );
void	   destroy_particle( uint32 checksum );
void	   destroy_particle_when_empty( uint32 checksum );
void destroy_all_particles(  );
void destroy_all_temp_particles(  );

void plat_process_particles( float delta_time );
CParticle* plat_get_particle( uint32 checksum );
CParticle* plat_create_particle( uint32 checksum, uint32 type_checksum, int max_particles, int max_streams, uint32 texture_checksum, uint32 blendmode_checksum, int fix, int num_segments, float split, int history );
} // Nx

#endif 






================================================
FILE: Code/Gfx/nxweather.cpp
================================================
///////////////////////////////////////////////////////////////////////////////
// NxWeather.cpp

#include 
#include 
#include 
#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 

#define next_random() ((((float)rand() / RAND_MAX ) * 2.0f ) - 1.0f)

//NsVector pright;
//NsVector pup;
//NsVector pat;

namespace	Nx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWeather::plat_update_grid( void )
{
	printf ("STUB: plat_update_grid\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWeather::plat_process( float delta_time )
{
	printf ("STUB: plat_process\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CWeather::plat_render( void )
{
	printf ("STUB: plat_render\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWeather::plat_set_rain_blend_mode( uint32 blendmode_checksum, int fix )
{
	printf ("STUB: plat_set_rain_blend_mode\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWeather::plat_set_splash_blend_mode( uint32 blendmode_checksum, int fix )
{
	printf ("STUB: plat_set_splash_blend_mode\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWeather::plat_set_snow_blend_mode( uint32 blendmode_checksum, int fix )
{
	printf ("STUB: plat_set_snow_blend_mode\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CWeather::CWeather()
{
	m_rain_height = 2000.0f;
	m_rain_frames = 40;
	m_rain_length = 100.0f;
	m_rain_drops_per_frame = 0.0f;
	m_rain_top_color.r = 0x00;
	m_rain_top_color.g = 0x00;
	m_rain_top_color.b = 0x00;
	m_rain_top_color.a = 0x00;
	m_rain_bottom_color.r = 0xff;
	m_rain_bottom_color.g = 0xff;
	m_rain_bottom_color.b = 0xff;
	m_rain_bottom_color.a = 0xff;

	m_splash_per_frame = 0.0f;
	m_splash_life = 8;
	m_splash_size = 16.0f;
	m_splash_color.r = 0x80;
	m_splash_color.g = 0x80;
	m_splash_color.b = 0x80;
	m_splash_color.a = 0x80;

	m_snow_height = 500.0f;
	m_snow_frames = 254;
	m_snow_size = 4.0f;
	m_snow_flakes_per_frame = 0.0f;
	m_snow_color.r = 0x80;
	m_snow_color.g = 0x80;
	m_snow_color.b = 0x80;
	m_snow_color.a = 0x80;

	m_raining = true;

	m_system_active = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CWeather::~CWeather()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWeather::UpdateGrid( void )
{
	plat_update_grid();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWeather::Process( float delta_time )
{
	plat_process( delta_time );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWeather::Render( void )
{
	plat_render();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetRainHeight | Sets the height of the rain above the ground.
// @parm height | The height of the rain in inches - defaults to 2000 inches.
bool CWeather::ScriptWeatherSetRainHeight( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_rain_height = 2000.0f;		// Default
	pParams->GetFloat( NONAME, &m_rain_height );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetRainFrames | Sets the number of frames it takes a rain drop to fall.
// @parm frames | Number of frames for the rain to fall from RainHeight to the ground. Defaults to 40 frames.
bool CWeather::ScriptWeatherSetRainFrames( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_rain_frames = 40;		// Default
	pParams->GetInteger( NONAME, &m_rain_frames );
	if ( m_rain_frames < 0 ) m_rain_frames = 0;
	if ( m_rain_frames > 254 ) m_rain_frames = 254;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetRainLength | Sets the length of the rain drops (the rain drop is a line).
// @parm length | Length of the rain drops in inches. Defaults to 100 inches.
bool CWeather::ScriptWeatherSetRainLength( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_rain_length = 100.0f;		// Default
	pParams->GetFloat( NONAME, &m_rain_length );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetRainBlendMode | Sets the blendmode of the rain particles.
// @parm blendmode | The name of the blend mode. Type are: blend/add/sub/modulate/brighten &
// fixblend/fixadd/fixsub/fixmodulate/fixbrighten & diffuse (no blend at all). Defaults to fixadd.
// @parmopt int | Fixed alpha value. Defaults to 64. Range is 0-255. Only required if using fix blend modes.
bool CWeather::ScriptWeatherSetRainBlendMode( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 blendmode_checksum = 0;
	if ( !pParams->GetChecksum( NONAME, &blendmode_checksum ) )
	{
		return false;
	}
	int fix = 32;		// Default
	pParams->GetInteger( NONAME, &fix );

	plat_set_rain_blend_mode( blendmode_checksum, fix );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetRainRate | Sets the rate that the rain drops fall in drops per frame.
// @parm rate | The number of new drops of rain per frame. Defaults to 0 (off).
bool CWeather::ScriptWeatherSetRainRate( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_rain_drops_per_frame = 0.0f;		// Default
	pParams->GetFloat( NONAME, &m_rain_drops_per_frame );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetRainColor | Sets the color of the rain.
// @parm top | The color of the rain at the top of the droplet. Defaults to 0x00000000
// @parm bottom | The color of the rain at the bottom of the droplet. Defaults to 0xffffffff
bool CWeather::ScriptWeatherSetRainColor( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 color;

	color = 0x00000000;
	pParams->GetInteger( "top", (int*)&color ); 

	m_rain_top_color.r = (uint8)((color)&0xff);
	m_rain_top_color.g = (uint8)((color>>8)&0xff);
	m_rain_top_color.b = (uint8)((color>>16)&0xff);
	m_rain_top_color.a = (uint8)((color>>24)&0xff); 

	color = 0xffffffff;
	pParams->GetInteger( "bottom", (int*)&color ); 

	m_rain_bottom_color.r = (uint8)((color)&0xff);
	m_rain_bottom_color.g = (uint8)((color>>8)&0xff);
	m_rain_bottom_color.b = (uint8)((color>>16)&0xff);
	m_rain_bottom_color.a = (uint8)((color>>24)&0xff); 

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSplashRate | Sets the rate that the splashes appear.
// @parm rate | The number of splashes per frame. 1 is the maximum. Defaults to 0 (off).
bool CWeather::ScriptWeatherSetSplashRate( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_splash_per_frame = 0.0f;		// Default
	pParams->GetFloat( NONAME, &m_splash_per_frame );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSplashLife | Sets the number of frames a splash will stay on screen.
// @parm life | Number of frames the splash is on screen. Defaults to 8. Maximum is 32.
bool CWeather::ScriptWeatherSetSplashLife( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_splash_life = 8;		// Default
	pParams->GetInteger( NONAME, &m_splash_life );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSplashSize | Sets the size of the splashes on-screen in inches.
// @parm size | Sets the size of a splash on screen - defaults to 16 inches.
bool CWeather::ScriptWeatherSetSplashSize( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_splash_size = 16.0f;		// Default
	pParams->GetFloat( NONAME, &m_splash_size );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSplashColor | Sets the color of the splash.
// @parm color | The color of the splash. Defaults to 0x80808080.
bool CWeather::ScriptWeatherSetSplashColor( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 color;

	color = 0x80808080;
	pParams->GetInteger( NONAME, (int*)&color ); 

	m_splash_color.r = (uint8)((color)&0xff);
	m_splash_color.g = (uint8)((color>>8)&0xff);
	m_splash_color.b = (uint8)((color>>16)&0xff);
	m_splash_color.a = (uint8)((color>>24)&0xff); 

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSplashBlendMode | Sets the blendmode of the splashes.
// @parm blendmode | The name of the blend mode. Type are: blend/add/sub/modulate/brighten &
// fixblend/fixadd/fixsub/fixmodulate/fixbrighten & diffuse (no blend at all). Defaults to fixadd.
// @parmopt int | Fixed alpha value. Defaults to 64. Range is 0-255. Only required if using fix blend modes.
bool CWeather::ScriptWeatherSetSplashBlendMode( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 blendmode_checksum = 0;
	if ( !pParams->GetChecksum( NONAME, &blendmode_checksum ) )
	{
		return false;
	}
	int fix = 32;		// Default
	pParams->GetInteger( NONAME, &fix );

	plat_set_splash_blend_mode( blendmode_checksum, fix );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSnowHeight | Sets the height of the snow above the ground.
// @parm height | The height of the snow in inches - defaults to 500 inches.
bool CWeather::ScriptWeatherSetSnowHeight( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_snow_height = 500.0f;		// Default
	pParams->GetFloat( NONAME, &m_snow_height );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSnowFrames | Sets the number of frames it takes a snow flake to fall.
// @parm frames | Number of frames for the snow to fall from SnowHeight to the ground. Defaults to 254 frames.
bool CWeather::ScriptWeatherSetSnowFrames( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_snow_frames = 254;		// Default
	pParams->GetInteger( NONAME, &m_snow_frames );

	if ( m_snow_frames < 0 ) m_snow_frames = 0;
	if ( m_snow_frames > 254 ) m_snow_frames = 254;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSnowSize | Sets the size of the snow flake image.
// @parm size | Size of the snow flakes in inches. Defaults to 4 inches.
bool CWeather::ScriptWeatherSetSnowSize( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_snow_size = 4.0f;		// Default
	pParams->GetFloat( NONAME, &m_snow_size );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSnowBlendMode | Sets the blendmode of the snow flakes.
// @parm blendmode | The name of the blend mode. Type are: blend/add/sub/modulate/brighten &
// fixblend/fixadd/fixsub/fixmodulate/fixbrighten & diffuse (no blend at all). Defaults to fixadd.
// @parmopt int | Fixed alpha value. Defaults to 64. Range is 0-255. Only required if using fix blend modes.
bool CWeather::ScriptWeatherSetSnowBlendMode( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 blendmode_checksum = 0;
	if ( !pParams->GetChecksum( NONAME, &blendmode_checksum ) )
	{
		return false;
	}
	int fix = 32;		// Default
	pParams->GetInteger( NONAME, &fix );

	plat_set_snow_blend_mode( blendmode_checksum, fix );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSnowRate | Sets the rate that the snow flakes fall in flakes per frame.
// @parm rate | The number of new flakes of snow per frame. Defaults to 0 (off).
bool CWeather::ScriptWeatherSetSnowRate( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_snow_flakes_per_frame = 0.0f;		// Default
	pParams->GetFloat( NONAME, &m_snow_flakes_per_frame );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSnowColor | Sets the color of the snow.
// @parm col | The color of the snow. Defaults to 0x80808080
bool CWeather::ScriptWeatherSetSnowColor( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 color;

	color = 0x80808080;
	pParams->GetInteger( NONAME, (int*)&color ); 

	m_snow_color.r = (uint8)((color)&0xff);
	m_snow_color.g = (uint8)((color>>8)&0xff);
	m_snow_color.b = (uint8)((color>>16)&0xff);
	m_snow_color.a = (uint8)((color>>24)&0xff);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetSnowActive | Sets weather system to snow mode.
bool CWeather::ScriptWeatherSetSnowActive( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_raining = false;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | WeatherSetRainActive | Sets weather system to rain mode.
bool CWeather::ScriptWeatherSetRainActive( Script::CStruct* pParams, Script::CScript* pScript )
{
	m_raining = true;

	return true;
}

}



================================================
FILE: Code/Gfx/nxweather.h
================================================
///////////////////////////////////////////////////////////////////////////////
// NxParticle.h



#ifndef	__GFX_WEATHER_H__
#define	__GFX_WEATHER_H__

#include 
#include 
#include 
#include 
#include 

namespace Nx
{

class CWeather
{
public:
							CWeather();
	virtual					~CWeather();

	void					UpdateGrid( void );

	void					Process( float delta_time );
	void					Render( void );

	// Script functions.
	bool					ScriptWeatherSetRainHeight( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetRainFrames( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetRainLength( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetRainBlendMode( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetRainRate( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetRainColor( Script::CStruct* pParams, Script::CScript* pScript );

	bool					ScriptWeatherSetSplashRate( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetSplashLife( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetSplashSize( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetSplashColor( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetSplashBlendMode( Script::CStruct* pParams, Script::CScript* pScript );

	bool					ScriptWeatherSetSnowHeight( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetSnowFrames( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetSnowSize( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetSnowBlendMode( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetSnowRate( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetSnowColor( Script::CStruct* pParams, Script::CScript* pScript );

	bool					ScriptWeatherSetSnowActive( Script::CStruct* pParams, Script::CScript* pScript );
	bool					ScriptWeatherSetRainActive( Script::CStruct* pParams, Script::CScript* pScript );

	// The virtual functions have a stub implementation.
private:
	virtual void			plat_update_grid( void );
	virtual void			plat_process( float delta_time );
	virtual void			plat_render( void );
	virtual void			plat_set_rain_blend_mode( uint32 blendmode_checksum, int fix );
	virtual void			plat_set_splash_blend_mode( uint32 blendmode_checksum, int fix );
	virtual void			plat_set_snow_blend_mode( uint32 blendmode_checksum, int fix );

protected:
	float					m_rain_height;
	int						m_rain_frames;
	float					m_rain_length;
	float					m_rain_drops_per_frame;
	Image::RGBA				m_rain_top_color;
	Image::RGBA				m_rain_bottom_color;

	float					m_splash_per_frame;
	int						m_splash_life;
	float					m_splash_size;
	Image::RGBA				m_splash_color;

	int						m_active_drops;

	bool					m_system_active;

	float					m_snow_height;
	int						m_snow_frames;
	float					m_snow_size;
	float					m_snow_flakes_per_frame;
	Image::RGBA				m_snow_color;

	bool					m_raining;
};

}

#endif // 





================================================
FILE: Code/Gfx/shadow.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	File name:		shadow.cpp												**
**																			**
**	Description:	Shadow interface code									**
**																			**
*****************************************************************************/

#include 

#include 

#include 
#include 
#include 
#include 
#include 

namespace Gfx
{

CShadow::CShadow()
{
	m_type=vUNDEFINED_SHADOW;
}

CShadow::~CShadow()
{
}

CDetailedShadow::CDetailedShadow(Nx::CModel *p_model, Mth::Vector camera_direction, float camera_distance)
{
	m_type=vDETAILED_SHADOW;
	mp_texture = Nx::CEngine::sCreateRenderTargetTexture( 256, 256, 16, 16 );		// maybe platform dependent?
	
	// Note, engine will figure out scene for itself
	Nx::CEngine::sProjectTextureIntoScene( mp_texture, p_model );		

	m_distance = camera_distance;
	m_direction = camera_direction;
	m_direction.Normalize();
	mp_camera = new Camera();
	mp_camera->SetPos(Mth::Vector(0,0,0));
	// construct an orientation matrix for the camera
	// so it is looking in the required direction
	Mth::Matrix orient;
	orient.Ident();		 		// initialize (since it aint)
	orient[Z] = m_direction;
	orient[Z].Normalize();
	
	// X and Y are at right angles to Z  
	orient[X][X] =  orient[Z][Z];
	orient[X][Y] = -orient[Z][X];
	orient[X][Z] = -orient[Z][Y];
	
	orient[Y][X] =  orient[Z][Y];
	orient[Y][Y] = -orient[Z][Z];
	orient[Y][Z] = -orient[Z][X];
	mp_camera->SetMatrix(orient);

	// this loops through all the existing shadows,
	// and sets their shadow flags...  the model
	// also remembers its shadow state, for any
	// subsequent geoms that are added
	p_model->EnableShadow( true ); 
}

	
CDetailedShadow::~CDetailedShadow()
{	
	Nx::CEngine::sStopProjectionTexture( mp_texture );
	
	if ( mp_texture )
	{
		delete mp_texture;
	}

	delete mp_camera;
}


// set the position for the projection camera to point at
void		CDetailedShadow::UpdatePosition(Mth::Vector pos)
{
	mp_camera->SetPos(pos - m_direction * m_distance);
	Nx::CEngine::sSetProjectionTextureCamera( mp_texture, mp_camera );
}



// set the direction in which the projection camera points
void CDetailedShadow::UpdateDirection( const Mth::Vector& dir )
{
	// Store camera position.
	Mth::Vector cam_pos = mp_camera->GetPos();

	// Set new direction and normalize.	
	m_direction = dir;
	m_direction.Normalize();
	
	// Zero out camera position.
	mp_camera->SetPos( Mth::Vector( 0, 0, 0 ));

	// Construct an orientation matrix for the camera so it is looking in the required direction.
	Mth::Matrix orient;
	orient.Identity();
	orient[Z] = m_direction;
	
	// X and Y are at right angles to Z.
	orient[X][X] =  orient[Z][Z];
	orient[X][Y] = -orient[Z][X];
	orient[X][Z] = -orient[Z][Y];
	orient[Y][X] =  orient[Z][Y];
	orient[Y][Y] = -orient[Z][Z];
	orient[Y][Z] = -orient[Z][X];
	mp_camera->SetMatrix( orient );

	// Restore camera position.
	mp_camera->SetPos( cam_pos );

	// Pass camera details on to engine.
	Nx::CEngine::sSetProjectionTextureCamera( mp_texture, mp_camera );
}



CSimpleShadow::CSimpleShadow()
{
	m_type=vSIMPLE_SHADOW;
	m_scale=1.0f;
	m_offset=1.0f;	
	mp_model=NULL;
	/*
	mp_poly=Nx::CEngine::sCreateTextured3dPoly();
	Dbg_MsgAssert(mp_poly,("Could not create textured 3d poly for shadow"));
	mp_poly->SetTexture("pedshadow");
	*/
}

CSimpleShadow::~CSimpleShadow()
{
	/*
	Dbg_MsgAssert(mp_poly,("NULL mp_poly ?"));
	Nx::CEngine::sDestroyTextured3dPoly(mp_poly);
	*/
	
	if (mp_model)
	{
		Nx::CEngine::sUninitModel(mp_model);
		mp_model=NULL;
	}	
}

void CSimpleShadow::SetModel(const char *p_model_name)
{
	if (mp_model)
	{
		Nx::CEngine::sUninitModel(mp_model);
		mp_model=NULL;
	}	

	mp_model=Nx::CEngine::sInitModel();
	
	Dbg_MsgAssert(p_model_name,("NULL p_model_name"));
	
	// TODO: Change to use a geom file instead for PS2, more efficient than mdl ...
	mp_model->AddGeom(Gfx::GetModelFileName(p_model_name, ".mdl").getString(), 0, true);
}

void CSimpleShadow::UpdatePosition(Mth::Vector& parentPos, Mth::Matrix& parentMatrix, Mth::Vector normal)
{
	/*
	Dbg_MsgAssert(mp_poly,("NULL mp_poly ?"));
	float w=Script::GetFloat("shadowwidth");
	mp_poly->SetPos(pos,w,w,normal);
	*/
		
	Mth::Matrix display_matrix;
	display_matrix[Y]=normal;
	display_matrix[Z]=parentMatrix[Z];
	display_matrix[X]=Mth::CrossProduct(display_matrix[Y],display_matrix[Z]);
	
	Mth::Vector scale(m_scale,m_scale,m_scale,1.0f);
	display_matrix.Scale(scale);
	
	// Change requested by LF/Andre:  use the passed normal
	display_matrix.SetPos(parentPos+normal*m_offset);  //WAS: parentPos+parentMatrix[Y]*m_offset
 	
	//Dbg_MsgAssert(mp_model,("NULL mp_model"));
	if (mp_model)
	{
		mp_model->Render(&display_matrix,true,NULL);
	}	
}

void CSimpleShadow::Hide()
{
	//Dbg_MsgAssert(mp_model,("NULL mp_model"));
	if (mp_model)
	{
		mp_model->SetActive(false);
	}	
}

void CSimpleShadow::UnHide()
{
	//Dbg_MsgAssert(mp_model,("NULL mp_model"));
	if (mp_model)
	{
		mp_model->SetActive(true);
	}	
}

}



================================================
FILE: Code/Gfx/shadow.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2001 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Shadow  (GFX)											**
**																			**
**	File name:		gfx/shadow.h											**
**																			**
**	Created: 		01/25/01	-	dc										**
**																			**
*****************************************************************************/

#ifndef __GFX_SHADOW_H
#define __GFX_SHADOW_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

namespace Nx
{
	class CModel;
	class CTexture;
	class CTextured3dPoly;
}
							 
							 
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Gfx
{

enum EShadowType
{
	vUNDEFINED_SHADOW,
	vDETAILED_SHADOW,
	vSIMPLE_SHADOW
};

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/



class	CShadow : public Spt::Class
{
protected:
	EShadowType	m_type;
	
public:
								CShadow();
	virtual						~CShadow();
	virtual void				UpdatePosition(Mth::Vector pos) {}
	virtual void				UpdatePosition(Mth::Vector& parentPos, Mth::Matrix& parentMatrix, Mth::Vector normal) {}
	virtual void				UpdateDirection( const Mth::Vector& dir ) {}
	virtual void				Hide() {}
	virtual void				UnHide() {}
	EShadowType					GetShadowType() {return m_type;}
};

class	CSimpleShadow : public CShadow
{
	//Nx::CTextured3dPoly *mp_poly;
	Nx::CModel	*mp_model;
	float m_scale;
	float m_offset;
	
public:
								CSimpleShadow();
								~CSimpleShadow();
	void						SetScale(float scale) {m_scale=scale;}
	void						SetOffset(float offset) {m_offset=offset;}
	void						SetModel(const char *p_model_name);
	void						UpdatePosition(Mth::Vector& parentPos, Mth::Matrix& parentMatrix, Mth::Vector normal);
	void						Hide();
	void						UnHide();
};


class	Camera;				 
class	CDetailedShadow : public CShadow
{
public:
								CDetailedShadow(Nx::CModel *p_model, Mth::Vector camera_direction = Mth::Vector(0.0f,-1.0f,0.0f), float camera_distance = 72.0f);
								~CDetailedShadow();
	void    					UpdatePosition(Mth::Vector	pos);		// the position of the target
	void    					UpdateDirection( const Mth::Vector& dir );		// the direction in which the camera points

private:
	Camera*						mp_camera;
	Nx::CTexture*				mp_texture;
	Mth::Vector					m_direction;
	float						m_distance;
};
	   

} // namespace Gfx

#endif	// __GFX_SHADOW_H


================================================
FILE: Code/Gfx/stdafx.h
================================================
// You cannot conditionally compile-out stdafx.h in the PC project
// so the only recourse is to always include "stdafx.h" in both
// projects and to include this dummy version on the PS2 project


================================================
FILE: Code/Gfx/subanimcontroller.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       SubAnimController.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/06/2003
//****************************************************************************

#include 

#include 

#include 
#include 
										   
#include 
#include 
#include 
#include 
#include 
						 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
						  
namespace Gfx
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBonedAnimController::CBonedAnimController( CBlendChannel* pBlendChannel ) : CBaseAnimController( pBlendChannel )
{
	mp_quickAnim = Nx::CEngine::sInitQuickAnim();
	mp_quickAnim->Enable( false );
	
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	m_animScriptName = pAnimationComponent->GetAnimScriptName();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBonedAnimController::~CBonedAnimController()
{
	if ( mp_quickAnim )
	{
		Nx::CEngine::sUninitQuickAnim( mp_quickAnim );
		mp_quickAnim = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBonedAnimController::InitFromStructure( Script::CStruct* pParams )
{
	CBaseAnimController::InitFromStructure( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBonedAnimController::GetPose( Gfx::CPose* pResultPose )
{
	Dbg_MsgAssert( pResultPose, ( "No return data" ) );

//	int skipList[vMAX_BONES];
//	GetSkeleton()->GetSkipList( &skipList[0] );
	uint32*	p_skip_list = GetSkeleton()->GetBoneSkipList();
	uint32	skip_index	= GetSkeleton()->GetBoneSkipIndex();

	Dbg_Assert( mp_quickAnim );
//	mp_quickAnim->GetInterpolatedFrames( pResultPose->m_rotations, pResultPose->m_translations, &skipList[0], mp_blendChannel->GetCurrentAnimTime() );
	mp_quickAnim->GetInterpolatedFrames( pResultPose->m_rotations, pResultPose->m_translations, p_skip_list, skip_index, mp_blendChannel->GetCurrentAnimTime() );
	mp_quickAnim->Enable( true );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBonedAnimController::Update()
{
	Dbg_MsgAssert( mp_blendChannel->m_loopingType != Gfx::LOOPING_WOBBLE, ( "Not supposed to be wobble type" ) );
			
	float oldTime = mp_blendChannel->m_currentTime;

	mp_blendChannel->m_currentTime = mp_blendChannel->get_new_anim_time();

	mp_blendChannel->ProcessCustomKeys( oldTime, mp_blendChannel->m_currentTime );

	if ( mp_blendChannel->m_currentTime < oldTime )
	{
		mp_quickAnim->Enable( false );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAnimFunctionResult CBonedAnimController::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case 0x5f495ae0:	// invalidateCache
		{
			mp_quickAnim->Enable( false );
			
			// want the other controllers to be invalidated too
			return AF_NOT_EXECUTED;
		}
		break;

		case 0xaf2fae19:	// playsequence
		{
			uint32 anim_name;
			float start_time;
			float end_time;
			int loop_type;
			float blend_period;
			float speed;

			pParams->GetChecksum( CRCD(0x6c2bfb7f,"animname"), &anim_name, Script::ASSERT );
			pParams->GetFloat( CRCD(0xd16b61e6,"starttime"), &start_time, Script::ASSERT );
			pParams->GetFloat( CRCD(0xab81bb3d,"endtime"), &end_time, Script::ASSERT );
			pParams->GetInteger( CRCD(0xcd8d7c1b,"looptype"), &loop_type, Script::ASSERT );
			pParams->GetFloat( CRCD(0x8f0d24ed,"blendperiod"), &blend_period, Script::ASSERT );
			pParams->GetFloat( CRCD(0xf0d90109,"speed"), &speed, Script::ASSERT );
			
			mp_blendChannel->CAnimChannel::PlaySequence( anim_name, start_time, end_time, (Gfx::EAnimLoopingType)loop_type, blend_period, speed);
		
			Dbg_Assert( mp_quickAnim );
			mp_quickAnim->SetAnimAssetName( m_animScriptName + anim_name );
		}
		break;
			
		default:
			return AF_NOT_EXECUTED;
	}

	return AF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CWobbleController::CWobbleController( CBlendChannel* pBlendChannel ) : CBaseAnimController( pBlendChannel )
{
	m_wobbleTargetTime			 = 0.0f;

	m_wobbleDetails.wobbleAmpA    = 0.0f;
	m_wobbleDetails.wobbleAmpB    = 0.0f;
	m_wobbleDetails.wobbleK1      = 0.0f;
	m_wobbleDetails.wobbleK2      = 0.0f;
	m_wobbleDetails.spazFactor    = 0.0f;	
	
	mp_quickAnim = Nx::CEngine::sInitQuickAnim();
	mp_quickAnim->Enable( false );
	
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	m_animScriptName = pAnimationComponent->GetAnimScriptName();
	
	/*
	float startTime;
	float endTime;

	if ( pParams->GetFloat( "startTime", &startTime, Script::NO_ASSERT );
		&& pParams->GetFloat( "endTime", &endTime, Script::NO_ASSERT ) )
	{
		m_wobbleTargetTime = ( startTime + endTime ) / 2;
	}
	*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CWobbleController::~CWobbleController()
{
	if ( mp_quickAnim )
	{
		Nx::CEngine::sUninitQuickAnim( mp_quickAnim );
		mp_quickAnim = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWobbleController::InitFromStructure( Script::CStruct* pParams )
{
	CBaseAnimController::InitFromStructure( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CWobbleController::GetPose( Gfx::CPose* pResultPose )
{
	Dbg_MsgAssert( pResultPose, ( "No return data" ) );

//	int skipList[vMAX_BONES];
//	GetSkeleton()->GetSkipList( &skipList[0] );
	uint32*	p_skip_list = GetSkeleton()->GetBoneSkipList();
	uint32	skip_index	= GetSkeleton()->GetBoneSkipIndex();
	
	Dbg_Assert( mp_quickAnim );
//	mp_quickAnim->GetInterpolatedFrames( pResultPose->m_rotations, pResultPose->m_translations, &skipList[0], mp_blendChannel->GetCurrentAnimTime() );
	mp_quickAnim->GetInterpolatedFrames( pResultPose->m_rotations, pResultPose->m_translations, p_skip_list, skip_index, mp_blendChannel->GetCurrentAnimTime() );
	mp_quickAnim->Enable( true );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CWobbleController::get_new_wobble_time()
{
	float AmpA = m_wobbleDetails.wobbleAmpA;
	float AmpB = m_wobbleDetails.wobbleAmpB;
	float k1 = m_wobbleDetails.wobbleK1;
	float k2 = m_wobbleDetails.wobbleK2;
	float SpazFactor = m_wobbleDetails.spazFactor;

	float new_time = m_wobbleTargetTime;

	Dbg_MsgAssert( mp_blendChannel->m_startTime < mp_blendChannel->m_endTime, ("start time must be smaller than end time for LOOPING_WOBBLE"));
	float l = ( mp_blendChannel->m_endTime - mp_blendChannel->m_startTime ) / 2;
	float mid = ( mp_blendChannel->m_endTime + mp_blendChannel->m_startTime ) / 2;
	float d = Mth::Abs( new_time - mid );
	SpazFactor = 1 + ( SpazFactor - 1 ) * d / l;
	AmpA *= SpazFactor;
	AmpB *= SpazFactor;

	if ( new_time - AmpA - AmpB < mp_blendChannel->m_startTime )
	{
		new_time = mp_blendChannel->m_startTime + AmpA + AmpB;
	}	
	if ( new_time + AmpA + AmpB > mp_blendChannel->m_endTime )
	{
		new_time = mp_blendChannel->m_endTime - AmpA - AmpB;
	}	

	float t = (int)Tmr::ElapsedTime( 0 ) * k1;
	t -= 16.0f * ( ((int)t) / 16.0f );
	float amp = AmpA + AmpB * sinf( t * 2.0f * Mth::PI );

	t = (int)Tmr::ElapsedTime( 0 ) * k2;
	t -= 16.0f * ( ((int)t) / 16.0f );

	new_time += amp * sinf( t * 2.0f * Mth::PI );

	// These should never be true, but just in case.
	if (new_time < mp_blendChannel->m_startTime)
	{
		new_time = mp_blendChannel->m_startTime;
	}
	if (new_time > mp_blendChannel->m_endTime)
	{
		new_time = mp_blendChannel->m_endTime;
	}

	return new_time;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWobbleController::Update()
{
	// the looping check below is only needed until
	// we stop automatically adding the wobble
	// controller to all channels
	Dbg_MsgAssert ( mp_blendChannel->m_loopingType == Gfx::LOOPING_WOBBLE, ( "Was supposed to be wobble type %d", mp_blendChannel->m_loopingType ) );

	// remember old time, so that we know whether to use the quick anim
	float oldTime = mp_blendChannel->m_currentTime;

	mp_blendChannel->m_currentTime = get_new_wobble_time();

	// remember to use the quick anim
	if ( mp_blendChannel->m_currentTime < oldTime )
	{
		mp_quickAnim->Enable( false );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CWobbleController::GetDebugInfo( Script::CStruct* p_info )
{
#ifdef	__DEBUG_CODE__
	p_info->AddFloat( CRCD(0x359ac119,"m_wobbleTargetTime"), m_wobbleTargetTime );

	p_info->AddFloat( CRCD(0xfd266a26,"wobbleAmpA"), m_wobbleDetails.wobbleAmpA );
	p_info->AddFloat( CRCD(0x642f3b9c,"wobbleAmpB"), m_wobbleDetails.wobbleAmpB );
	p_info->AddFloat( CRCD(0x0f43fd49,"wobbleK1"), m_wobbleDetails.wobbleK1 );
	p_info->AddFloat( CRCD(0x964aacf3,"wobbleK2"), m_wobbleDetails.wobbleK2 );
	p_info->AddFloat( CRCD(0xf90b0824,"spazFactor"), m_wobbleDetails.spazFactor );
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAnimFunctionResult CWobbleController::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case 0x5f495ae0:	// invalidateCache
		{
			mp_quickAnim->Enable( false );
			
			// want the other controllers to be invalidated too
			return AF_NOT_EXECUTED;
		}
		break;

		case 0xaf2fae19:	// playsequence
		{
			uint32 anim_name;
			float start_time;
			float end_time;
			int loop_type;
			float blend_period;
			float speed;

			pParams->GetChecksum( CRCD(0x6c2bfb7f,"animname"), &anim_name, Script::ASSERT );
			pParams->GetFloat( CRCD(0xd16b61e6,"starttime"), &start_time, Script::ASSERT );
			pParams->GetFloat( CRCD(0xab81bb3d,"endtime"), &end_time, Script::ASSERT );
			pParams->GetInteger( CRCD(0xcd8d7c1b,"looptype"), &loop_type, Script::ASSERT );
			pParams->GetFloat( CRCD(0x8f0d24ed,"blendperiod"), &blend_period, Script::ASSERT );
			pParams->GetFloat( CRCD(0xf0d90109,"speed"), &speed, Script::ASSERT );
			
			mp_blendChannel->CAnimChannel::PlaySequence( anim_name, start_time, end_time, (Gfx::EAnimLoopingType)loop_type, blend_period, speed);
		
			Dbg_Assert( mp_quickAnim );
			mp_quickAnim->SetAnimAssetName( m_animScriptName + anim_name );
		}
		break;
			
		case 0xd0209498:	// setwobbletarget
		{
			float wobbleTargetAlpha;

			if ( pParams->GetFloat( CRCD(0x4d747fa0,"wobbletargetalpha"), &wobbleTargetAlpha, Script::NO_ASSERT ) )
			{
				// check bounds
				if ( wobbleTargetAlpha < 0.0f )
				{
					wobbleTargetAlpha = 0.0f;
				}

				if ( wobbleTargetAlpha > 1.0f )
				{
					wobbleTargetAlpha = 1.0f;
				}

				m_wobbleTargetTime = mp_blendChannel->m_startTime + ( mp_blendChannel->m_endTime - mp_blendChannel->m_startTime ) * wobbleTargetAlpha;
			}
		}
		break;
			
		case 0xea6d0efd:	// setwobbledetails
		{
			float wobbleAmpA;
			float wobbleAmpB;
			float wobbleK1;
			float wobbleK2;
			float spazFactor;

			if ( pParams->GetFloat( CRCD(0xfd266a26,"wobbleAmpA"), &wobbleAmpA, Script::NO_ASSERT )
				 && pParams->GetFloat( CRCD(0x642f3b9c,"wobbleAmpB"), &wobbleAmpB, Script::NO_ASSERT )
				 && pParams->GetFloat( CRCD(0x0f43fd49,"wobbleK1"), &wobbleK1, Script::NO_ASSERT )
				 && pParams->GetFloat( CRCD(0x964aacf3,"wobbleK2"), &wobbleK2, Script::NO_ASSERT )
				 && pParams->GetFloat( CRCD(0xf90b0824,"spazFactor"), &spazFactor, Script::NO_ASSERT ) )
			{
				m_wobbleDetails.wobbleAmpA = wobbleAmpA;
				m_wobbleDetails.wobbleAmpB = wobbleAmpB;
				m_wobbleDetails.wobbleK1 = wobbleK1;
				m_wobbleDetails.wobbleK2 = wobbleK2;
				m_wobbleDetails.spazFactor = spazFactor;
			}
		}
		break;

		default:
			return AF_NOT_EXECUTED;
	}

	return AF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CFlipRotateController::CFlipRotateController( CBlendChannel* pBlendChannel ) : CBaseAnimController( pBlendChannel )
{
	m_flipped = false;
	m_rotated = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CFlipRotateController::~CFlipRotateController()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFlipRotateController::InitFromStructure( Script::CStruct* pParams )
{
	CBaseAnimController::InitFromStructure( pParams );

	int flipped;
	if ( pParams->GetInteger( CRCD(0x0c7a712c,"flipped"), &flipped, Script::NO_ASSERT ) )
	{
		m_flipped = flipped;
	}

	int rotated;
	if ( pParams->GetInteger( CRCD(0x6ea3704a,"rotated"), &rotated, Script::NO_ASSERT ) )
	{
		m_rotated = rotated;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CFlipRotateController::Update()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CFlipRotateController::GetPose( Gfx::CPose* pResultPose )
{
	// rather than using the m_flipped and m_rotated flags,
	// we should use the one stored in the CAnimationComponent
	// temporarily...  this is because right now we don't want 
	// to blend across the flip (but eventually we will want to)

	CSkeleton* pSkeleton = GetSkeleton();
	Dbg_MsgAssert( pSkeleton, ( "No skeleton?" ) );

	#ifdef	__NOPT_ASSERT__
	{	// remove this scope if you actually want to use pAnimationComponent
		// in something other than the assertion
		Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
		Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	}
	#endif
	
	Obj::CSkaterFlipAndRotateComponent* pSkaterFlipAndRotateComponent = GetSkaterFlipAndRotateComponentFromObject( GetObject() );

	if ( pSkaterFlipAndRotateComponent && pSkaterFlipAndRotateComponent->IsBoardRotated() )
//	if ( m_rotated )
	{
		int idx;
		
		// name of skateboard bone in new skeleton
		idx = pSkeleton->GetBoneIndexById( CRCD(0x98971faf, "bone_board_root") );
		if ( idx != -1 )
		{	   
			Mth::Quat* pQuat = (pResultPose->m_rotations + idx);
			*pQuat = Mth::Quat(0.0f,0.0f,1.0f,0.0f) * *pQuat;
			pQuat->Normalize();
		}		
	}

	// GJ:  this remembers the flip state when the animation was
	// first launched, so that we can blend across the flip...  
	// it produces some weird effects currently in the THPS5, 
	// (such as the pelvis rotating 180 degrees), but this may 
	// be due to the way the scripts were originally written...
	// i'll take a look at it later, but hopefully this will 
	// give Left Field some of the functionality they've been
	// looking for...
	if ( m_flipped )
//	if ( pAnimationComponent->IsFlipped() )
	{
		int i;
		Gfx::CPose oldPose;
		int numBones = pSkeleton->GetNumBones();

//		oldPose = *pResultPose;
		for ( i = 0; i < numBones; i++ )
		{
			oldPose.m_rotations[i] = pResultPose->m_rotations[i];
		}	
		for ( i = 0; i < numBones; i++ )
		{
			oldPose.m_translations[i] = pResultPose->m_translations[i];
		}	

		Mth::Quat* pQuat = (pResultPose->m_rotations);
		Mth::Vector* pTrans = (pResultPose->m_translations);

		for ( i = 0; i < numBones; i++ )
		{
			int flipIndex = pSkeleton->GetFlipIndex(i);	
			if ( flipIndex != -1 )
			{
				*pQuat = *(oldPose.m_rotations+flipIndex);
				*pTrans = *(oldPose.m_translations+flipIndex);				
	   		}
			else
			{
				*pQuat = *(oldPose.m_rotations+i);
				*pTrans = *(oldPose.m_translations+i);
			}
			
			// flip all rotations and translations on Y.
			(*pQuat)[X] = -(*pQuat)[X];
			(*pQuat)[W] = -(*pQuat)[W];
			(*pTrans)[X] = -(*pTrans)[X];

			pQuat++;
			pTrans++;
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CIKController::CIKController( CBlendChannel* pBlendChannel ) : CBaseAnimController( pBlendChannel )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CIKController::~CIKController()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CIKController::InitFromStructure( Script::CStruct* pParams )
{
	CBaseAnimController::InitFromStructure( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CIKController::Update()
{
	// here we'd do things like grab the position of the bike pedals
	// from the CBikeComponent, and override the leg bones, or whatever

	// if data needs be shared among different
	// blend channels, then we might want to consider
	// storing it in a CIKComponent...  then, the
	// Update() function can grab it like so:
	// Obj::CIKComponent* pIKComponent = GetIKComponentFromObject( GetObject() );
	// if ( pIKComponent )
	// {
	//		Mth::Vector steering_column_pos = pIKComponent->GetBonePosition( "steering_column" );
	//		Mth::Vector left_handle_bar_pos	= pIKComponent->GetBonePosition( "left_handle_bar" );
	// }

	// if each blend channel needs its own set of IK data
	// then we can just do it like so:
	// Mth::Vector steering_column_pos = m_steering_column_pos;
	// Mth::Vector left_handle_bar_pos = m_left_handle_bar_pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CIKController::GetDebugInfo( Script::CStruct* p_info )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAnimFunctionResult CIKController::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return AF_NOT_EXECUTED;
	}

	return AF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CIKController::GetPose( Gfx::CPose* pResultPose )
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CProceduralAnimController::CProceduralAnimController( CBlendChannel* pBlendChannel ) : CBaseAnimController( pBlendChannel )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CProceduralAnimController::~CProceduralAnimController()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CProceduralAnimController::InitFromStructure( Script::CStruct* pParams )
{
	CBaseAnimController::InitFromStructure( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CProceduralAnimController::Update()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CProceduralAnimController::GetPose( Gfx::CPose* pResultPose )
{
	// To be consistent with THPS4, we should actually do this 
	// AFTER building the object-space transform of each bone
	// (but i'll wait until someone actually asks for this feature)
	
	// I didn't fully re-implement this functionality from THPS4,
	// since we're going to do our procedural cloth animations
	// in a different way for THPS5.  I've left this partial
	// implementation in as an example for other developers
	// to write their own controllers.

	// THPS4 had ping-ponging animations (so that we can animate
	// the shirt rising and falling)...  we can re-implement
	// this later if we end up deciding to use this controller...

	CSkeleton* pSkeleton = GetSkeleton();
	Dbg_MsgAssert( pSkeleton, ( "No skeleton?" ) );

	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	int numProceduralBones = pAnimationComponent->GetNumProceduralBones(); 
	CProceduralBone* p_current_procedural_bone = pAnimationComponent->GetProceduralBones();

	for ( int i = 0; i < numProceduralBones; i++ )
	{
		int idx = pSkeleton->GetBoneIndexById( p_current_procedural_bone->m_name );
		if ( idx != -1 )
		{
			Mth::Quat* pQuat = pResultPose->m_rotations + idx;

			if ( p_current_procedural_bone->rotEnabled )
			{
				Mth::Vector theRotVector = p_current_procedural_bone->currentRot;
				theRotVector.Scale( Mth::PI * 2.0f / 4096.0f );
				Mth::Quat theRotQuat = Mth::EulerToQuat( theRotVector );
				
				// TODO:  should I be post-rotating this?
				*pQuat = theRotQuat * *pQuat;
				
				pQuat->Normalize();
			}
		}
											  
		p_current_procedural_bone++;
    }

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneTransMin( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->trans0 = vec;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneTransMax( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->trans1 = vec;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneTransSpeed( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->deltaTrans = vec;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneTransActive( uint32 boneName, bool enabled )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->transEnabled = enabled;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneTransCurrent( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->currentTrans = vec;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneRotMin( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->rot0[X] = vec[X] * 2.0f * Mth::PI / 4096.0f;
		pProceduralBone->rot0[Y] = vec[Y] * 2.0f * Mth::PI / 4096.0f;
		pProceduralBone->rot0[Z] = vec[Z] * 2.0f * Mth::PI / 4096.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneRotMax( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->rot1[X] = vec[X] * 2.0f * Mth::PI / 4096.0f;
		pProceduralBone->rot1[Y] = vec[Y] * 2.0f * Mth::PI / 4096.0f;
		pProceduralBone->rot1[Z] = vec[Z] * 2.0f * Mth::PI / 4096.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneRotSpeed( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->deltaRot = vec;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneRotCurrent( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->currentRot = vec;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneRotActive( uint32 boneName, bool enabled )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->rotEnabled = enabled;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneScaleMin( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->scale0 = vec;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneScaleMax( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->scale1 = vec;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneScaleSpeed( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->deltaScale = vec;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneScaleCurrent( uint32 boneName, const Mth::Vector& vec )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->currentScale = vec;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CProceduralAnimController::SetProceduralBoneScaleActive( uint32 boneName, bool enabled )
{
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	Dbg_MsgAssert( pAnimationComponent, ( "No animation component?" ) );
	
	CProceduralBone* pProceduralBone = pAnimationComponent->GetProceduralBoneByName( boneName );

	if ( pProceduralBone )
	{
		pProceduralBone->scaleEnabled = enabled;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// TODO:  It's kind of backwards for the CAnimationComponent to be calling
// member functions in the CProceduralAnimController that set values
// back inside the CAnimationComponent...  This is because I haven't
// really decided whether each blend channel should have its own
// proc anim data, or whether the blend channels should share a single
// set of proc anim data.  Right now, the blend channels all share
// a single set of proc anim data (stored in the CAnimationComponent).

EAnimFunctionResult CProceduralAnimController::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
	    // @script | SetBoneTransMin | Sets the minimum translation extent for procedurally animated bone (in world units)
        // @parmopt float | x | 0 | minimum extent x
        // @parmopt float | y | 0 | minimum extent y
        // @parmopt float | z | 0 | minimum extent z
        // @parm name | bone | name of procedural bone
		case 0x02565bd1: //	SetBoneTransMin
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				this->SetProceduralBoneTransMin( boneName, vec );
			}
			break;
			
		// @script | SetBoneTransMax | Sets the maximum translation extent for procedurally animated bone (in world units)
		// @parmopt float | x | 0 | maximum extent x
		// @parmopt float | y | 0 | maximum extent y
		// @parmopt float | z | 0 | maximum extent z
		// @parm name | bone | name of procedural bone
		case 0x3e5b6488: //	SetBoneTransMax
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				this->SetProceduralBoneTransMax( boneName, vec );
			}
			break;
		
		// @script | SetBoneTransSpeed | Sets the speed at which the bone's translation will procedurally animate (in 4096 notation)
		// @parmopt int | x | 0 | speed x (2048 immediately goes from T0 to T1)
		// @parmopt int | y | 0 | speed y (2048 immediately goes from T0 to T1)
		// @parmopt int | z | 0 | speed z (2048 immediately goes from T0 to T1)
		// @parm name | bone | name of procedural bone
		case 0xc85b9ac0: //	SetBoneTransSpeed
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				Dbg_Assert( mp_blendChannel );
				this->SetProceduralBoneTransSpeed( boneName, vec );
			}
			break;
			
		// @script | SetBoneTransCurrent | Immediately sets the procedurally animated bone to a specific translation (in 4096 notation)
		// @parmopt int | x | 0 | current x (0 = T0, 1024 = halfway between T0 and T1, 2048 = T1, 3072 = halfway between T1 and T0, 4096 = T0)
		// @parmopt int | y | 0 | current y (0 = T0, 1024 = halfway between T0 and T1, 2048 = T1, 3072 = halfway between T1 and T0, 4096 = T0)
		// @parmopt int | z | 0 | current z (0 = T0, 1024 = halfway between T0 and T1, 2048 = T1, 3072 = halfway between T1 and T0, 4096 = T0)
		// @parm name | bone | name of procedural bone
		case 0x5ee9d5bd: //	SetBoneTransCurrent
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				this->SetProceduralBoneTransCurrent( boneName, vec );
			}
			break;
			
		// @script | SetBoneTransActive | Sets whether the bone should procedurally animate its translation
		// @uparm int | active (either 0 or 1)
		// @parm name | bone | name of procedural bone
		case 0x5661fb72: //	SetBoneTransActive
			{
				uint32 boneName;
				int enabled;
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				pParams->GetInteger( NONAME, &enabled, true );
				
                Dbg_Assert( mp_blendChannel );
                this->SetProceduralBoneTransActive( boneName, enabled );
			}
			break;
		
		// @script | SetBoneRotMin | Sets the minimum rotation extent for procedurally animated bone (in 4096 notation)
		// @parmopt int | x | 0 | minimum extent x (in 4096 notation)
		// @parmopt int | y | 0 | minimum extent y (in 4096 notation)
		// @parmopt int | z | 0 | minimum extent z (in 4096 notation)
		// @parm name | bone | name of procedural bone
		case 0xa47767f2: //	SetBoneRotMin
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				this->SetProceduralBoneRotMin( boneName, vec );
			}
			break;
			
		// @script | SetBoneRotMax | Sets the maximum rotation extent for procedurally animated bone (in 4096 notation)
		// @parmopt int | x | 0 | maximum extent x (in 4096 notation)
		// @parmopt int | y | 0 | maximum extent y (in 4096 notation)
		// @parmopt int | z | 0 | maximum extent z (in 4096 notation)
		case 0x987a58ab: //	SetBoneRotMax
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				this->SetProceduralBoneRotMax( boneName, vec );
			}
			break;
			
		// @script | SetBoneRotSpeed | Sets the speed at which the bone's rotation will procedurally animate (in 4096 notation)
		// @parmopt int | x | 0 | speed x (2048 immediately goes from T0 to T1)
		// @parmopt int | y | 0 | speed y (2048 immediately goes from T0 to T1)
		// @parmopt int | z | 0 | speed z (2048 immediately goes from T0 to T1)
		// @parm name | bone | name of procedural bone
		case 0x599d3707: //	SetBoneRotSpeed
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );

				this->SetProceduralBoneRotSpeed( boneName, vec );
			}
			break;
			
		// @script | SetBoneRotCurrent | Immediately sets the procedurally animated bone to a specific rotation (in 4096 notation)
		// @parmopt int | x | 0 | current x (0 = T0, 1024 = halfway between T0 and T1, 2048 = T1, 3072 = halfway between T1 and T0, 4096 = T0)
		// @parmopt int | y | 0 | current y (0 = T0, 1024 = halfway between T0 and T1, 2048 = T1, 3072 = halfway between T1 and T0, 4096 = T0)
		// @parmopt int | z | 0 | current z (0 = T0, 1024 = halfway between T0 and T1, 2048 = T1, 3072 = halfway between T1 and T0, 4096 = T0)
		// @parm name | bone | name of procedural bone
		case 0x7235daa7: //	SetBoneRotCurrent
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				this->SetProceduralBoneRotCurrent( boneName, vec );
			}
			break;
			
		// @script | SetBoneRotActive | Sets whether the bone should procedurally animate its rotation
		// @uparm int | active (either 0 or 1)
		// @parm name | bone | name of procedural bone
		case 0x53f06acc: //	SetBoneRotActive
			{		
				uint32 boneName;
				int enabled;
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				pParams->GetInteger( NONAME, &enabled, true );

                this->SetProceduralBoneRotActive( boneName, enabled );
			}
			break;
			
		// @script | SetBoneScaleMin | Sets the minimum translation extent for procedurally animated bone (in world units)
        // @parmopt float | x | 0 | minimum extent x
        // @parmopt float | y | 0 | minimum extent y
        // @parmopt float | z | 0 | minimum extent z
        // @parm name | bone | name of procedural bone
		case 0xc6889e91: //	SetBoneScaleMin
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				this->SetProceduralBoneScaleMin( boneName, vec );
			}
			break;
			
		// @script | SetBoneScaleMax | Sets the maximum translation extent for procedurally animated bone (in world units)
		// @parmopt float | x | 0 | maximum extent x
		// @parmopt float | y | 0 | maximum extent y
		// @parmopt float | z | 0 | maximum extent z
		// @parm name | bone | name of procedural bone
		case 0xfa85a1c8: //	SetBoneScaleMax
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				this->SetProceduralBoneScaleMax( boneName, vec );
			}
			break;
		
		// @script | SetBoneScaleSpeed | Sets the speed at which the bone's translation will procedurally animate (in 4096 notation)
		// @parmopt int | x | 0 | speed x (2048 immediately goes from T0 to T1)
		// @parmopt int | y | 0 | speed y (2048 immediately goes from T0 to T1)
		// @parmopt int | z | 0 | speed z (2048 immediately goes from T0 to T1)
		// @parm name | bone | name of procedural bone
		case 0xd32c2724: //	SetBoneScaleSpeed
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				this->SetProceduralBoneScaleSpeed( boneName, vec );
			}
			break;
			
		// @script | SetBoneScaleCurrent | Immediately sets the procedurally animated bone to a specific translation (in 4096 notation)
		// @parmopt int | x | 0 | current x (0 = T0, 1024 = halfway between T0 and T1, 2048 = T1, 3072 = halfway between T1 and T0, 4096 = T0)
		// @parmopt int | y | 0 | current y (0 = T0, 1024 = halfway between T0 and T1, 2048 = T1, 3072 = halfway between T1 and T0, 4096 = T0)
		// @parmopt int | z | 0 | current z (0 = T0, 1024 = halfway between T0 and T1, 2048 = T1, 3072 = halfway between T1 and T0, 4096 = T0)
		// @parm name | bone | name of procedural bone
		case 0xd12b3713: //	SetBoneScaleCurrent
			{
				Mth::Vector vec(0.0f,0.0f,0.0f);
				uint32 boneName;
				pParams->GetFloat( CRCD(0x7323e97c,"x"), &vec[X], false );
				pParams->GetFloat( CRCD(0x0424d9ea,"y"), &vec[Y], false );
				pParams->GetFloat( CRCD(0x9d2d8850,"z"), &vec[Z], false );
				pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, true );
				
				this->SetProceduralBoneScaleCurrent( boneName, vec );
			}
			break;
		
		// @script | SetBoneScaleActive | Sets whether the bone should procedurally animate its translation
		// @uparm int | active (either 0 or 1)
		// @parm name | bone | name of procedural bone
		case 0xf11daaae: //	SetBoneScaleActive
			{
				uint32 boneName;
				int enabled;
				pParams->GetChecksum( "bone", &boneName, true );
				pParams->GetInteger( NONAME, &enabled, true );
				
                this->SetProceduralBoneScaleActive( boneName, enabled );
			}
			break;
		
		default:
			return AF_NOT_EXECUTED;
	}

	return AF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPoseController::CPoseController( CBlendChannel* pBlendChannel ) : CBaseAnimController( pBlendChannel )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPoseController::~CPoseController()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPoseController::InitFromStructure( Script::CStruct* pParams )
{
	CBaseAnimController::InitFromStructure( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPoseController::Update()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPoseController::SetPose( Gfx::CPose* pPose )
{
	memcpy( &m_pose, pPose, sizeof(Gfx::CPose) );

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPoseController::GetPose( Gfx::CPose* pResultPose )
{
	// this nukes the existing pose, meaning that
	// this should be the only controller in the list...
	memcpy( pResultPose, &m_pose, sizeof(Gfx::CPose) );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CLookAtController::CLookAtController( CBlendChannel* pBlendChannel ) : CBaseAnimController( pBlendChannel )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CLookAtController::~CLookAtController()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLookAtController::InitFromStructure( Script::CStruct* pParams )
{
	CBaseAnimController::InitFromStructure( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CLookAtController::Update()
{
	Dbg_MsgAssert( 0, ( "Unimplemented function" ) );

	// GJ:  This is just a sample controller...  I was thinking
	// that this could be used for orienting the head towards
	// an object in the scene, given some constraints
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPartialAnimController::CPartialAnimController( CBlendChannel* pBlendChannel ) : CBaseAnimController( pBlendChannel )
{
	mp_quickAnim = Nx::CEngine::sInitQuickAnim();
	mp_quickAnim->Enable( false );
	
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	m_animScriptName = pAnimationComponent->GetAnimScriptName();

	mp_animChannel = new Gfx::CAnimChannel;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPartialAnimController::~CPartialAnimController()
{
	if ( mp_quickAnim )
	{
		Nx::CEngine::sUninitQuickAnim( mp_quickAnim );
		mp_quickAnim = NULL;
	}

	if ( mp_animChannel )
	{
		delete mp_animChannel;
		mp_animChannel = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPartialAnimController::InitFromStructure( Script::CStruct* pParams )
{   
	CBaseAnimController::InitFromStructure( pParams );
	
	uint32 anim_name;
	pParams->GetChecksum( CRCD(0x6c2bfb7f,"animName"), &anim_name, Script::ASSERT );
	
	float From = 0.0f;
	float To = 0.0f;
	float Current = 0.0f;
	Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( GetObject() );
	float Duration = pAnimationComponent->AnimDuration( anim_name );
	Gfx::GetTimeFromParams( &From, &To, Current, Duration, pParams, NULL );
	  
	Gfx::EAnimLoopingType loopingType;
	Gfx::GetLoopingTypeFromParams( &loopingType, pParams );

	float speed = 1.0f;
	pParams->GetFloat( CRCD(0xf0d90109,"speed"), &speed, Script::NO_ASSERT );
	
	pParams->GetFloat(CRCD(0x230ccbf4, "Current"), &Current);

	float blend_period = 0.0f;

	mp_animChannel->PlaySequence( anim_name, From, To, loopingType, blend_period, speed);
	
	if (Current != 0.0f)
	{
		mp_animChannel->AddTime(Current);
	}

	Dbg_Assert( mp_quickAnim );
	mp_quickAnim->SetAnimAssetName( m_animScriptName + anim_name );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPartialAnimController::GetPose( Gfx::CPose* pResultPose )
{
	
	#ifdef	__NOPT_ASSERT__
	{
		CSkeleton* pSkeleton = GetSkeleton();
		Dbg_MsgAssert( pSkeleton, ( "No skeleton?" ) );	
		Dbg_MsgAssert( pResultPose, ( "No return data" ) );
	}
	#endif
	
//	int skipList[vMAX_BONES];
//	GetSkeleton()->GetSkipList( &skipList[0] );
	uint32*	p_skip_list = GetSkeleton()->GetBoneSkipList();
	uint32	skip_index	= GetSkeleton()->GetBoneSkipIndex();

#if 1
	Dbg_Assert( mp_quickAnim );
	mp_quickAnim->GetInterpolatedFrames( pResultPose->m_rotations, pResultPose->m_translations, p_skip_list, skip_index, mp_animChannel->GetCurrentAnimTime() );
	mp_quickAnim->Enable( true );
#else
	Mth::Quat theRot[128];
	Mth::Vector theTrans[128];

	Dbg_Assert( mp_quickAnim );
	mp_quickAnim->GetInterpolatedFrames( theRot, theTrans, p_skip_list, skip_index, mp_animChannel->GetCurrentAnimTime() );
	mp_quickAnim->Enable( true );
	
	Script::CArray* pArray = Script::GetArray( m_boneListName, Script::ASSERT );
	for ( uint32 i = 0; i < pArray->GetSize(); i++ )
	{
		int idx = pSkeleton->GetBoneIndexById( pArray->GetChecksum(i) );
		if ( idx != -1 )
		{
			*(pResultPose->m_rotations+idx)=theRot[idx];
			*(pResultPose->m_translations+idx)=theTrans[idx];
		}
	}
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPartialAnimController::Update()
{
	// This class is used for either appending,
	// or overwriting a subset of the bones in the RT array,
	// given a CBonedAnimFrameData...
	Dbg_MsgAssert( mp_animChannel->m_loopingType != Gfx::LOOPING_WOBBLE, ( "Not supposed to be wobble type" ) );
			
	float oldTime = mp_animChannel->m_currentTime;

	mp_animChannel->m_currentTime = mp_animChannel->get_new_anim_time();

//	mp_animChannel->ProcessCustomKeys( oldTime, mp_animChannel->m_currentTime );

	if ( mp_animChannel->m_currentTime < oldTime )
	{
		mp_quickAnim->Enable( false );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAnimFunctionResult CPartialAnimController::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case 0x5f495ae0:	// InvalidateCache
		{
			mp_quickAnim->Enable( false );
			
			// want the other controllers to be invalidated too
			return AF_NOT_EXECUTED;
		}
		break;

		case 0xd63a1b81:	// GetPartialAnimParams
		{
			Dbg_Assert( pScript );
			pScript->GetParams()->AddFloat( CRCD(0xaaecb346,"partialAnimTime"), mp_animChannel->GetCurrentAnimTime() );
			pScript->GetParams()->AddChecksum( CRCD(0x659bf355,"partialAnim"), mp_animChannel->GetCurrentAnim() );
			pScript->GetParams()->AddInteger( CRCD(0x4966cc11,"partialAnimComplete"), mp_animChannel->IsAnimComplete() );

			return AF_TRUE;
		}
		break;

		case 0xbd4edd44: // SetPartialAnimSpeed
		{
			float speed;
			pParams->GetFloat( CRCD(0xf0d90109,"speed"), &speed, Script::ASSERT );			
			mp_animChannel->SetAnimSpeed( speed );

			return AF_TRUE;
		}
		break;

		case 0x6aaeb76f: // IncrementPartialAnimTime
		{
			float incVal;
			pParams->GetFloat( CRCD(0x06c9f278,"incVal"), &incVal, Script::ASSERT );			
			
			// GJ:  a way to fool the animation controller
			// into incrementing its time (frame rate
			// independent) for the viewer object

			float oldAnimSpeed = mp_animChannel->GetAnimSpeed();

			mp_animChannel->SetAnimSpeed( 0.0f );

			mp_animChannel->m_currentTime += incVal;

			mp_animChannel->Update();

			mp_animChannel->SetAnimSpeed( oldAnimSpeed );

			return AF_TRUE;
		}
		break;
			
		case 0xf5e2b871: // ReversePartialAnimDirection
		{
			mp_animChannel->ReverseDirection();

			return AF_TRUE;
		}
		break;

		default:
			return AF_NOT_EXECUTED;
	}

	return AF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}



================================================
FILE: Code/Gfx/subanimcontroller.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       SubAnimController.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  2/06/2003
//****************************************************************************

#ifndef __GFX_SUBANIMCONTROLLER_H__
#define __GFX_SUBANIMCONTROLLER_H__

#include 
#include 

#include 

#include 
#include 

// This file contains all the custom animation controllers
// which can be chained together by an animation channel
// to produce its final array of RT data for each frame.
// For example, an animated skeleton might need to:
//   (1) grab interpolated keyframe data
//   (2) flip the skeleton
//   (3) rotate the skateboard by 180 degrees
//   (4) perform IK on the leg bones to lock them to the skateboard
//   (5) apply an additional keyframed anim to only the cloth bones
//   (6) orient the head towards the closest pedestrian.
// In this case, there would be one animation channel with
// six animation controllers.

// Eventually, we'll probably want to break up this
// code into a separate file for each animation
// controller, and perhaps create a factory class so that
// BlendChannel.cpp doesn't need to be updated every time
// a new animation controller is added.
						   
namespace Script
{
	class CScript;
	class CStruct;
}

namespace Nx
{
	class CQuickAnim;
}

namespace Gfx
{
	class CBlendChannel;
	class CProceduralBone;
	class CAnimChannel;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This controller is used for getting interpolated
// keyframe data from a CBonedAnimFrameData.  It
// accesses it through a Nx::CQuickAnim, which allows
// for future optimizations.

class CBonedAnimController : public CBaseAnimController
{
public:
	CBonedAnimController( CBlendChannel* pBlendChannel );
	virtual	~CBonedAnimController();

public:
	virtual void 				InitFromStructure( Script::CStruct* pParams );
	virtual void 				Update();
    virtual EAnimFunctionResult CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual bool				GetPose( Gfx::CPose* pResultPose );
	
protected:
	Nx::CQuickAnim*				mp_quickAnim;
	uint32						m_animScriptName;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This controller is used for wobble effects (such
// as grinding and manualling).  It's very similar to
// the boned anim controller, and should probably be
// refactored to share some code at some point.

class CWobbleController : public CBaseAnimController
{
public:
	CWobbleController( CBlendChannel* pBlendChannel );
	virtual	~CWobbleController();

public:
	virtual void 				InitFromStructure( Script::CStruct* pParams );
	virtual void 				Update();
	virtual void				GetDebugInfo( Script::CStruct* p_info );
    virtual EAnimFunctionResult CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual bool				GetPose( Gfx::CPose* pResultPose );

protected:
    SWobbleDetails  			m_wobbleDetails;
    float						m_wobbleTargetTime;
	Nx::CQuickAnim*				mp_quickAnim;
	uint32						m_animScriptName;

protected:
	float						get_new_wobble_time();

public:
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This controller is used for flipping the skeleton
// and rotating the skateboard.  Truthfully, the two
// actions are completely unrelated, but they are
// so simple that it seemed like a waste to separate
// them into two controllers.  Later on, I may decide
// to add separate controllers for each.

class CFlipRotateController : public CBaseAnimController
{
public:
	CFlipRotateController( CBlendChannel* pBlendChannel );
	virtual ~CFlipRotateController();

public:
	virtual void 				InitFromStructure( Script::CStruct* pParams );
	virtual void 				Update();
	virtual bool				GetPose( Gfx::CPose* pResultPose );
	
protected:
	bool						m_flipped;
	bool						m_rotated;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This controller is a placeholder for Left Field's
// IK code.  Although THPS4 did not use IK, it would
// be easy to add once LF and Neversoft both reorganized
// their code to use this system of animation channels
// and animation controllers.

class CIKController : public CBaseAnimController
{
public:
	CIKController( CBlendChannel* pBlendChannel );
	virtual ~CIKController();

public:
	virtual void 				InitFromStructure( Script::CStruct* pParams );
	virtual void 				Update();
	virtual void				GetDebugInfo( Script::CStruct* p_info );
    virtual EAnimFunctionResult CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual bool				GetPose( Gfx::CPose* pResultPose );
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This controller is also a placeholder.  On THPS4,
// we implemented something similar in order to
// programmatically animate our cloth bones.  This
// system was inefficient and unwieldy, and we've
// decided to scrap it in order to implement 
// "partial animations", which means the animators 
// can create SKA files containing animation
// data for only the cloth bones.  Left Field can
// potentially use the CProceduralAnimController
// to do programmatic animation on the bike's tires.

class CProceduralAnimController : public CBaseAnimController
{
public:
	CProceduralAnimController( CBlendChannel* pBlendChannel );
	virtual ~CProceduralAnimController();

public:
	virtual void 				InitFromStructure( Script::CStruct* pParams );
	virtual void 				Update();    
	virtual EAnimFunctionResult CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual bool				GetPose( Gfx::CPose* pResultPose );
	
protected:
	CProceduralBone*			get_procedural_bone_by_id( uint32 boneName );
	
protected:
	void		   				SetProceduralBoneTransMin( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneTransMax( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneTransSpeed( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneTransCurrent( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneTransActive( uint32 boneName, bool enabled );
	void						SetProceduralBoneRotMin( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneRotMax( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneRotSpeed( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneRotCurrent( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneRotActive( uint32 boneName, bool enabled );
	void						SetProceduralBoneScaleMin( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneScaleMax( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneScaleSpeed( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneScaleCurrent( uint32 boneName, const Mth::Vector& vec );
	void						SetProceduralBoneScaleActive( uint32 boneName, bool enabled );
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This controller just stores a single Gfx::CPose.
// Perhaps degenerating blend channels can
// collapse their list of animation controllers
// and replace them with the CPoseController
// as a slight optimization.

class CPoseController : public CBaseAnimController
{
public:
	CPoseController( CBlendChannel* pBlendChannel );
	virtual	~CPoseController();

public:
	virtual void 				InitFromStructure( Script::CStruct* pParams );
	virtual void 				Update();
	virtual bool				GetPose( Gfx::CPose* pResultPose );

public:
	void						SetPose( Gfx::CPose* pPose );

protected:
	Gfx::CPose					m_pose;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This controller hasn't been implemented yet.
// I was thinking that it could be used to
// reorient the head towards a particular lookup
// target.

class CLookAtController : public CBaseAnimController
{
public:
	CLookAtController( CBlendChannel* pBlendChannel );
	virtual	~CLookAtController();

public:
	virtual void 				InitFromStructure( Script::CStruct* pParams );
	virtual void 				Update();
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This controller hasn't been implemented yet.
// It will be used to read a partial SKA anim 
// from a file, for animating cloth bones and
// for doing secondary anims (for example, we
// may want to apply an additional wobble animation
// to the upper body when the skater lands from
// a particular high trick)
class CPartialAnimController : public CBaseAnimController
{

public:
	CPartialAnimController( CBlendChannel* pBlendChannel );
	virtual ~CPartialAnimController();

public:
	virtual void 				InitFromStructure( Script::CStruct* pParams );
	virtual void 				Update();
	virtual bool				GetPose( Gfx::CPose* pResultPose );
	EAnimFunctionResult			CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );

public:
	Gfx::CAnimChannel*			mp_animChannel;
	Nx::CQuickAnim*				mp_quickAnim;
	uint32						m_animScriptName;
};
																			   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}

#endif


================================================
FILE: Code/Gfx/vecfont.cpp
================================================
#if 0
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics Lib (gfx)		 								**
**																			**
**	File name:		vecfont.cpp												**
**																			**
**	Created:		01/31/99	-	mjb										**
**																			**
**	Description:	Vector font class										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


namespace Gfx
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

uint32				VecFont::s_char_length[256];
VecFont::CharLine*		VecFont::s_char_maps[256];
VecFont::CharLine		VecFont::s_symbol_maps1[15][16] =
{
	 {{64,0,64,16},
      {64,32,64,128}},          /* ! */
     {{32,96,32,128},
      {96,96,96,128}},          /* " */
     {{32,0,32,128},
      {96,0,96,128},
      {0,32,128,32},
      {0,96,128,96}},           /* # */
     {{0,0,128,0},
      {128,0,128,64},
      {128,64,0,64},
      {0,64,0,128},
      {0,128,128,128},
      {64,0,64,128}},           /* $ */
     {{0,0,128,128},
      {0,112,16,128},
      {112,0,128,16}},          /* % */
     {{0,0,0,0}},               /* & - yeah,   right */
     {{64,96,64,128}},          /* ' */
     {{96,128,64,96},
      {64,96,64,32},
      {64,32,96,0}},            /* ( */
     {{32,128,64,96},
      {64,96,64,32},
      {64,32,32,0}},            /* ) */
     {{64,0,64,128},
      {0,64,128,64},
      {32,32,96,96},
      {32,96,96,32}},           /* * */
     {{64,32,64,96},
      {32,64,96,32}},           /* + */
     {{64,0,96,32}},            /* , */
     {{32,64,96,64}},           /* - */
     {{64,0,64,32}},            /* . */     
     {{0,0,128,128}}
};          /* / */

VecFont::CharLine VecFont::s_symbol_maps2[7][16] =
{
	 {{64,0,64,16},
	  {64,128,64,112}},         /* : */
     {{32,0,64,32},
      {64,128,64,112}},         /* ; */
     {{128,128,0,64},
      {0,64,128,0}},            /* < */
     {{0,32,128,32},
      {0,96,128,96}},           /* =
                                 */
     {{0,128,128,64},
      {128,64,0,0}},            /* > */
     {{0,0,0,0}},               /* ? - yeah, right */
     {{0,0,0,0}}                /* @ - yeah, right */
};              

VecFont::CharLine VecFont::s_digit_maps[10][16] =
{
	 {{0,0,128,0},
      {128,0,128,128},
      {128,128,0,128},
      {0,128,0,0}},             /* 0 */
     {{128,0,128,128}},         /* 1 */
     {{0,128,128,128},
      {128,128,128,64},
      {128,64,0,64},
      {0,64,0,0},
      {0,0,128,0}},             /* 2 */
     {{0,128,128,128},
      {128,128,128,0},
      {128,0,0,0},
      {128,64,0,64}},           /* 3 */
     {{0,128,0,64},
      {0,64,128,64},
      {128,128,128,0}},         /* 4 */
     {{128,128,0,128},
      {0,128,0,64},
      {0,64,128,64},
      {128,64,128,0},
      {128,0,0,0}},             /* 5 */
     {{128,128,0,128},
      {0,128,0,0},
      {0,0,128,0},
      {128,0,128,64},
      {128,64,0,64}},           /* 6 */
     {{0,128,128,128},
      {128,128,128,0}},         /* 7 */
     {{0,0,128,0},
      {128,0,128,128},
      {128,128,0,128},
      {0,128,0,0},
      {0,64,128,64}},           /* 8 */
     {{0,0,128,0},
      {128,0,128,128},
      {128,128,0,128},
      {0,128,0,64},      {0,64,128,64}}
}; /* 9 */

VecFont::CharLine VecFont::s_alpha_maps[26][16] =
{
	 {{0,0,64,128},
      {64,128,128,0},
      {32,64,96,64}},           /* a */
     {{0,128,0,0},
      {0,0,128,0},
      {128,0,128,64},
      {128,64,0,64}},           /* b */
     {{128,128,0,128},
      {0,128,0,0},
      {0,0,128,0}},             /* c */
     {{0,0,0,128},
      {0,128,128,64},
      {128,64,128,0},
      {128,0,0,0}},             /* d */
     {{128,128,0,128},
      {0,128,0,0},
      {0,0,128,0},
      {0,64,64,64}},            /* e */
     {{128,128,0,128},
      {0,128,0,0},
      {0,64,64,64}},            /* f */
     {{128,128,0,128},
      {0,128,0,0},
      {0,0,128,0},
      {128,0,128,64}},          /* g */
     {{0,128,0,0},
      {0,64,128,64},
      {128,128,128,0}},         /* h */
     {{64,0,64,128}},           /* i */
     {{0,0,128,0},
      {128,0,128,128}},         /* j */
     {{0,0,0,128},
      {128,0,0,64},
      {0,64,128,128}},          /* k */
     {{0,0,0,128},
      {0,0,128,0}},             /* l */
     {{0,0,0,128},
      {0,128,64,0},
      {64,0,128,128},
      {128,128,128,0}},         /* m */
     {{0,0,0,128},
      {0,128,128,128},
      {128,128,128,0}},         /* n */
     {{0,0,128,0},
      {128,0,128,128},
      {128,128,0,128},
      {0,128,0,0}},             /* o */
     {{0,0,0,128},
      {0,128,128,128},
      {128,128,128,64},
      {128,64,0,64}},           /* p */
     {{128,0,128,128},
      {128,128,0,128},
      {0,128,0,64},
      {0,64,128,64}},           /* q */
     {{0,0,0,128},
      {0,128,128,128},
      {128,128,128,64},
      {128,64,0,64},
      {0,64,128,0}},            /* r */
     {{0,0,128,0},
      {128,0,128,64},
      {128,64,0,64},
      {0,64,0,128},
      {0,128,128,128}},         /* s */
     {{0,128,128,128},
      {64,128,64,0}},           /* t */
     {{0,128,0,0},
      {0,0,128,0},
      {128,0,128,128}},         /* u */
     {{0,128,64,0},
      {64,0,128,128}},          /* v */
     {{0,128,0,0},
      {0,0,64,128},
      {64,128,128,0},
      {128,0,128,128}},         /* w */
     {{0,128,128,0},
      {0,0,128,128}},           /* x */
     {{0,0,128,0},
      {128,0,128,128},
      {0,128,0,64},
      {0,64,128,64}},           /* y */
     {{128,0,0,0},
      {0,0,128,128},      
      {128,128,0,128}}			/* z */
};

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

void VecFont::assign_line_list( uint32 target, CharLine* lineList )
{
    s_char_length[target] = 0;
    s_char_maps[target] = lineList;

    while ( lineList->startX | lineList->startY | lineList->endX | lineList->endY )
    {
        s_char_length[target]++;
        lineList++;
    }
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

VecFont::VecFont( const Image::RGBA color, float w, float h )
{
/*
        
//	RwV2dScale( &m_size, size, (RwReal)(1.0f/192.0f) );
	
	m_color = color;
	m_lineVertBuffer = NULL;
	m_lineVertBufferSize = 0;

    uint32 i;

    for (i = 0; i < 256; i++)
    {
        s_char_length[i] = 0;
    }

    for (i = 0; i < 15; i++)
    {
        assign_line_list('!'+i, s_symbol_maps1[i]);
    }

    for (i = 0; i < 10; i++)
    {
        assign_line_list('0'+i, s_digit_maps[i]);
    }

    for (i = 0; i < 7; i++)
    {
        assign_line_list(':'+i, s_symbol_maps2[i]);
    }

    for (i = 0; i < 26; i++)
    {
        assign_line_list('a'+i, s_alpha_maps[i]);
        assign_line_list('A'+i, s_alpha_maps[i]);
    }
*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#define	VECFONTSIZE  50000

void	VecFont::Print( float x, float y, const char* string )
{
	
#if 0

#ifndef __PLAT_NGC__
    uint32 i, numLines;
    RwV2d    localPos = pos;

    /* Make sure that the line vertex buffer is big enough */
    i = numLines = 0;
    while (string[i])
    {
        numLines += s_char_length[(uint32)string[i]];
        i++;
    }



    /* Will it fit? */
    if (numLines > m_lineVertBufferSize)
    {
        uint32		newSize = 2 * sizeof(RWIM2DVERTEX) * numLines;

		if (newSize < VECFONTSIZE)
		{
			numLines = VECFONTSIZE / (2 * sizeof(RWIM2DVERTEX))  + 1;
			newSize = 2 * sizeof(RWIM2DVERTEX) * numLines;
		}
		else
		{
			Dbg_MsgAssert(0,("metric vector font overflow, numLines = %d, m_lineVertBufferSize %d ", numLines, m_lineVertBufferSize));
		}
		
		printf(">>>>>>>>>>>>>>>>>>>>>>>>> newSize = %d\n",newSize);
		
        RWIM2DVERTEX*	newLineVertBuffer;

		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().RwHeap());
        if ( m_lineVertBuffer )
        {
            newLineVertBuffer = (RWIM2DVERTEX *)RwRealloc( m_lineVertBuffer, newSize );
        }
        else
        {
            newLineVertBuffer = (RWIM2DVERTEX *)RwMalloc(newSize);
        }
		Mem::Manager::sHandle().PopContext();

        if (newLineVertBuffer)
        {
            /* Got through and set all uv, recip z, screen z, etc */
            for (i = m_lineVertBufferSize*2; i < numLines*2; i++ )
            {
				RwReal	near_z = RwIm2DGetNearScreenZ();
				RwReal	recip_z = 1.0f/near_z;
                RWIM2DVERTEXSetScreenZ( &newLineVertBuffer[i], near_z );
                RWIM2DVERTEXSetRecipCameraZ( &newLineVertBuffer[i], recip_z );
                RWIM2DVERTEXSetU(&newLineVertBuffer[i], (RwReal)(0.0f), (RwReal)(1.0f));
                RWIM2DVERTEXSetV(&newLineVertBuffer[i], (RwReal)(0.0f), (RwReal)(1.0f));
                RWIM2DVERTEXSetIntRGBA(&newLineVertBuffer[i], m_color.red, m_color.green,
                                      m_color.blue, m_color.alpha);
            }
			m_lineVertBuffer = newLineVertBuffer;
            m_lineVertBufferSize = numLines;
        }
    }

    /* Will it fit now */
    if ( numLines <= m_lineVertBufferSize )
    {
        RWIM2DVERTEX*	curLineVerts = m_lineVertBuffer;

        i = 0;
        while (string[i])
        {
            uint32 	numCharLines = s_char_length[(uint32)string[i]];
            CharLine*	curLine 	 = s_char_maps[(uint32)string[i]];

            while (numCharLines--)
            {
                RwLine line;

                line.start.x = localPos.x + ((RwReal)curLine->startX * m_size.x);
                line.start.y = localPos.y + ((RwReal)(128-curLine->startY) * m_size.y);
                line.end.x = localPos.x + ((RwReal)curLine->endX * m_size.x);
                line.end.y = localPos.y + ((RwReal)(128-curLine->endY) * m_size.y);

                RWIM2DVERTEXSetScreenX(&curLineVerts[0], line.start.x);
                RWIM2DVERTEXSetScreenY(&curLineVerts[0], line.start.y);
                RWIM2DVERTEXSetScreenX(&curLineVerts[1], line.end.x);
                RWIM2DVERTEXSetScreenY(&curLineVerts[1], line.end.y);

                curLineVerts += 2;
                curLine++;
            }

            localPos.x += m_size.x * (RwReal)(192.0f);
            i++;
        }

        /* We don't need to corrupt too much render state here */
        RwRenderStateSet(rwRENDERSTATETEXTURERASTER, NULL);

        RwIm2DRenderPrimitive(rwPRIMTYPELINELIST, m_lineVertBuffer, numLines*2);
    }
#endif		// __PLAT_NGC__
#endif

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

VecFont::~VecFont( void )
{
	

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Gfx


#endif


================================================
FILE: Code/Gfx/vecfont.h
================================================
#if 0

/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		GFX (Graphics Library)									**
**																			**
**	Module:			Graphics  (GFX)											**
**																			**
**	File name:		gfx/vecfont.h											**
**																			**
**	Created: 		11/11/99	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __GFX_VECFONT_H
#define __GFX_VECFONT_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Gfx
{



/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class  VecFont  : public Spt::Class
{
	

public:
						VecFont( const Image::RGBA color, float w, float h);
						~VecFont( void );		 

	void				Print( float x, float y, const char* string );

private:


	struct CharLine
	{
		uint8			startX;
		uint8			startY;
		uint8			endX;
		uint8			endY;
	};

	void				assign_line_list( uint32 target, CharLine* lineList );

	float				m_size_x,m_size_y;
	Image::RGBA			m_color;
	
//	RWIM2DVERTEX*		m_lineVertBuffer;
//	RwUInt32			m_lineVertBufferSize;

	static uint32		s_char_length[256];
	static CharLine*	s_char_maps[256];
	static CharLine		s_symbol_maps1[15][16];
	static CharLine		s_symbol_maps2[7][16];
	static CharLine		s_digit_maps[10][16];
	static CharLine		s_alpha_maps[26][16];
};


/*****************************s************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Gfx

#endif // __GFX_VECFONT_H

#endif



================================================
FILE: Code/Sk/Components/EditorCameraComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       EditorCameraComponent.cpp
//* OWNER:          KSH
//* CREATION DATE:  26 Mar 2003
//****************************************************************************

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//#define DEBUG_COLLISION

namespace Obj
{


static bool sHitKillPoly=false;
// If a poly's node's trigger script contains any of these then it is considered a Kill poly
static uint32 spKillScripts[]=
{
	CRCC(0xdea3057b,"SK3_KillSkater"),
	CRCC(0xb1058546,"SK3_KillSkater_Water"),
	CRCC(0xb38ed6b,"SK3_KillSkater_Finish"),
	CRCC(0x1310920e,"SK3_KillSkater_Pungee"),
	CRCC(0x979a99ab,"NY_KillWater"),
	CRCD(0x7aae1282,"RU_KillSkater"),
	CRCD(0xcb1b1d9e,"RU_KillSkater2"),
	CRCD(0x84e5c703,"NY_LeavingMessage"),
	CRCD(0xf66fdf6e,"SJ_Televator"),
	CRCD(0xb21175da,"HI_KillSkater_LittleDogs"),
	CRCD(0xd9d60165,"VC_CAG_boundary"),
};

static void s_check_if_kill_poly ( CFeeler* p_feeler )
{
	if (!p_feeler->GetTrigger() || !p_feeler->GetNodeChecksum())
	{
		#ifdef DEBUG_COLLISION
		//printf("Not got trigger or not got node checksum\n");
		#endif
		return;
	}	
	
	int node = SkateScript::FindNamedNode(p_feeler->GetNodeChecksum(), false);// false means don't assert
	if (node < 0)
	{
		// Got a trigger, but no node, so assume it is a kill poly. This case arises in the park editor
		// where a kill piece such as water or lava has been placed but the node array has not been generated yet.
		//sHitKillPoly=true;
		// Note: Commented out, to fix TT8832, where the cursor could get stuck on cars.
		// The PE now allows the cursor to go anywhere, even onto kill polys.
		return;
	}
		
	Script::CArray *p_node_array = Script::GetArray(CRCD(0xc472ecc5, "NodeArray"));
	Script::CStruct *p_node = p_node_array->GetStructure(node);
	
	uint32 trigger_script=0;
	p_node->GetChecksum(CRCD(0x2ca8a299,"TriggerScript"),&trigger_script);
	
	if (trigger_script)
	{
		//printf("trigger_script=%s\n",Script::FindChecksumName(trigger_script));
		if (Script::ScriptContainsAnyOfTheNames(trigger_script,spKillScripts,sizeof(spKillScripts)/sizeof(uint32)))
		{
			sHitKillPoly=true;
		}	
	}	
}

// Checks whether the vector posA -> posB goes through any Kill polys.
// Does not check collidable polys.
static bool s_goes_through_kill_poly(const Mth::Vector &posA, const Mth::Vector &posB)
{
	sHitKillPoly=false;
	
	CFeeler feeler;
	
	feeler.m_start = posA;
	feeler.m_end = posB;

	feeler.SetIgnore(0, mFD_NON_COLLIDABLE | mFD_TRIGGER);
	feeler.SetCallback(s_check_if_kill_poly);
	//feeler.DebugLine(0,255,0);
	feeler.GetCollision();
	
	return sHitKillPoly;
}

bool CEditorCameraComponent::find_y(Mth::Vector start, Mth::Vector end, float *p_y)
{
	#ifdef DEBUG_COLLISION
	//printf("s_find_y: ");
	#endif
	
	CFeeler feeler;
	feeler.SetStart(start);
	feeler.SetEnd(end);
	// Ignore invisible polys, otherwise one can get stuck on top of the hotel in Hawaii,
	// because it is surrounded by horizontal invisible polys.
	//feeler.SetIgnore(mFD_INVISIBLE, 0);	
	
	if (feeler.GetCollision())
	{
		if (m_simple_collision)
		{
			#ifdef DEBUG_COLLISION
			printf("Simple drop down\n");
			#endif
			
			sHitKillPoly=false;
			Mth::Vector point=feeler.GetPoint();
			*p_y=point[Y];
			return true;
		}	
	
		sHitKillPoly=false;
		s_check_if_kill_poly(&feeler);
		if (sHitKillPoly)
		{
			// Can't go onto collidable Kill polys
			#ifdef DEBUG_COLLISION
			printf("Kill poly!\n");
			#endif
			return false;
		}
				
		// Check the steepness
		Mth::Vector normal=feeler.GetNormal();
		// .087=max steepness of 85 degrees
		if (normal[Y] < 0.087f/*sinf(Mth::DegToRad(90.0f-Script::GetFloat(CRCD(0x87b926b4,"MaxSteepness"))))*/)
		{
			// The ground is too steep!
			// Need this check otherwise the skater can be placed halfway down the almost vertical
			// riverbank in NJ
			#ifdef DEBUG_COLLISION
			printf("Too steep!\n");
			#endif
			return false;
		}
		
		#ifdef DEBUG_COLLISION
		//printf("Found y=%f normal[Y]=%f\n",feeler.GetPoint()[Y],normal[Y]);
		#endif
		
		Mth::Vector point=feeler.GetPoint();
		*p_y=point[Y];

		// But, before returning true, check whether there are any kill polys between the start and the
		// collision point.
		if (s_goes_through_kill_poly(start, point))
		{
			#ifdef DEBUG_COLLISION
			printf("Kill poly above found collision!\n");
			#endif
			return false;
		}
				
		return true;
	}
	
	#ifdef DEBUG_COLLISION
	printf("No collision\n");
	#endif
	
	return false;
}

bool CEditorCameraComponent::find_collision_at_offset(Mth::Vector oldCursorPos, float upOffset)
{
	Mth::Vector start=m_cursor_pos;
	start[Y]+=upOffset;
	Mth::Vector end=m_cursor_pos;
	end[Y]+=Script::GetFloat(CRCD(0xf230d521,"EditorCam_CursorCollisionDownOffset"));
	
	bool new_position_is_ok=false;
	
	float y;
	if (find_y(start,end,&y))
	{
		new_position_is_ok=true;
	}
	
	if (new_position_is_ok)
	{
		Mth::Vector from = oldCursorPos;
		Mth::Vector to = m_cursor_pos;
		to[Y]=y;
		to[Y]+=72.0f; // 6 feet above the ground
		// Make it really be horizontal to avoid steep collision check weirdness
		from[Y]=to[Y]; 
		
		if (!m_simple_collision)
		{
			// Do a horizontal collision check to see if the cursor 
			// is going to move through a kill poly
			if (s_goes_through_kill_poly(from, to))
			{
				#ifdef DEBUG_COLLISION
				printf("Goes through kill poly\n");
				#endif
				new_position_is_ok=false;
			}
		}
		
		if (!m_allow_movement_through_walls)
		{
			// Also don't allow moving through walls
			CFeeler feeler;
			feeler.SetIgnore(mFD_NON_COLLIDABLE, 0);
			feeler.SetStart(from);
			feeler.SetEnd(to);
			if (feeler.GetCollision())
			{
				#ifdef DEBUG_COLLISION
				printf("oof\n");
				#endif
				new_position_is_ok=false;
			}
		}	
	}

	if (new_position_is_ok)
	{
		m_cursor_pos[Y]=y;
		return true;
	}
	return false;	
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CEditorCameraComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CEditorCameraComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CEditorCameraComponent::CEditorCameraComponent() : CBaseComponent()
{
	SetType( CRC_EDITORCAMERA );
	
	m_camera_focus_pos.Set();
	m_cam_pos.Set();
	m_cursor_pos.Set();
	m_cursor_u.Set();
	m_cursor_v.Set();
	
	m_radius=0.0f;
	m_radius_min=0.0f;
	m_radius_max=0.0f;
	
	m_angle=0.0f;
	m_tilt_angle=0.0f;
	m_tilt_angle_min=0.0f;
	m_tilt_angle_max=0.0f;
	
	m_cursor_height=0.0f;
	m_min_height=0.0f;
	
	mp_shadow=NULL;
	mp_input_component=NULL;
	
	m_simple_collision=false;
	m_allow_movement_through_walls=false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CEditorCameraComponent::~CEditorCameraComponent()
{
	delete_shadow();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CEditorCameraComponent::InitFromStructure( Script::CStruct* pParams )
{
	// ** Add code to parse the structure, and initialize the component

	m_min_height=0.0f;
	pParams->GetFloat(CRCD(0x9cdf40cd,"min_height"),&m_min_height);
	
	m_radius_min=100.0f;
	pParams->GetFloat(CRCD(0x52eecb98,"min_radius"),&m_radius_min);
	
	m_radius_max=2000.0f;
	pParams->GetFloat(CRCD(0x53e2512c,"max_radius"),&m_radius_max);
	
	m_simple_collision=pParams->ContainsFlag(CRCD(0xa0d7fbab,"SimpleCollision"));
	m_allow_movement_through_walls=pParams->ContainsFlag(CRCD(0xf5680def,"AllowMovementThroughWalls"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CEditorCameraComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	InitFromStructure(pParams);
}

void CEditorCameraComponent::Hide( bool shouldHide )
{
	if (shouldHide)
	{
		delete_shadow();
	}	
	else
	{
		create_shadow();
	}
}

void CEditorCameraComponent::GetCursorOrientation(Mth::Vector *p_u, Mth::Vector *p_v)
{
	*p_u=m_cursor_u;
	*p_v=m_cursor_v;
}

void CEditorCameraComponent::create_shadow()
{
	if (!mp_shadow)
	{
		mp_shadow = new Gfx::CSimpleShadow;
		mp_shadow->SetScale( SHADOW_SCALE );
		mp_shadow->SetModel( "Ped_Shadow" );
	}	
}

void CEditorCameraComponent::delete_shadow()
{
	if (mp_shadow)
	{
		delete mp_shadow;
		mp_shadow=NULL;
	}	
}

void CEditorCameraComponent::update_shadow()
{
	Mth::Vector cursor_pos=m_cursor_pos;		
	cursor_pos[Y]+=m_cursor_height;

	CFeeler feeler;
	feeler.SetStart(cursor_pos);
	feeler.SetEnd(cursor_pos + Mth::Vector(0.0f,SHADOW_COLLISION_CHECK_DISTANCE,0.0f,0.0f));
	if (feeler.GetCollision())
	{
		create_shadow();
		
		Mth::Matrix mat;
		mat.Ident();

		Mth::Vector shadow_pos=feeler.GetPoint();
		Mth::Vector shadow_normal=feeler.GetNormal();
		mp_shadow->UpdatePosition(shadow_pos,
										 mat,
										 shadow_normal);
	}
	else
	{
		delete_shadow();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CEditorCameraComponent::Finalize()
{
	// Get the pointers to the other required components.
	Dbg_MsgAssert(mp_input_component==NULL,("mp_input_component not NULL ?"));
	mp_input_component = GetInputComponentFromObject(GetObject());
	Dbg_MsgAssert(mp_input_component,("CEditorCameraComponent requires parent object to have an input component!"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CEditorCameraComponent::Update()
{
	Dbg_MsgAssert(mp_input_component,("NULL mp_input_component"));
	
	// TODO: When in park editor, use different values here, cos they'll need to be
	// tweaked according to the shell geometry.
	m_tilt_angle_min=Script::GetFloat(CRCD(0x7d16d113,"EditorCam_TiltMin"));
	m_tilt_angle_max=Script::GetFloat(CRCD(0x411bee4a,"EditorCam_TiltMax"));
	
	CControlPad& control_pad = mp_input_component->GetControlPad();

	
	// Update m_angle
	float angle_vel=Script::GetFloat(CRCD(0x8242d878,"EditorCam_TurnSpeed"))*control_pad.m_scaled_rightX;
	m_angle+=angle_vel;
	while (m_angle > 2*3.141592654f)
	{
		m_angle -= 2*3.141592654f;
	}	
	while (m_angle < 0.0f)
	{
		m_angle += 2*3.141592654f;
	}	
	
	// Update m_tilt_angle
	angle_vel=-Script::GetFloat(CRCD(0x3cfa3a72,"EditorCam_TiltSpeed"))*control_pad.m_scaled_rightY;
	m_tilt_angle+=angle_vel;
	if (m_tilt_angle > m_tilt_angle_max)
	{
		m_tilt_angle = m_tilt_angle_max;
	}	
	if (m_tilt_angle < m_tilt_angle_min)
	{
		m_tilt_angle = m_tilt_angle_min;
	}	
	
	// Update m_radius
	bool zoom_in_button_pressed=false;
	bool zoom_out_button_pressed=false;
	bool raise_pressed=false;
	bool lower_pressed=false;
	switch (Config::GetHardware())
	{
		case Config::HARDWARE_XBOX:
		{
			uint32 input_mask = mp_input_component->GetInputMask();
			if (input_mask & Inp::Data::mA_BLACK)
			{
				zoom_out_button_pressed=true;
			}	
			if (input_mask & Inp::Data::mA_WHITE)
			{
				zoom_in_button_pressed=true;
			}	
			// Note: See p_siodev.cpp for the low level code that does the weird XBox buttons mappings
			if (Ed::CParkEditor::Instance()->EditingCustomPark())
			{
				// When editing a park, XBox L gives L2, XBox R gives L1
				// We want XBox L to lower, XBox R to raise
				if (input_mask & Inp::Data::mA_L1)
				{
					raise_pressed=true;
				}	
				if (input_mask & Inp::Data::mA_L2)
				{
					lower_pressed=true;
				}	
			}
			else
			{
				// When not editing a park, XBox L gives L1, XBox R gives R1
				// We want XBox L to lower, XBox R to raise
				if (input_mask & Inp::Data::mA_R1)
				{
					raise_pressed=true;
				}	
				if (input_mask & Inp::Data::mA_L1)
				{
					lower_pressed=true;
				}	
			}
			break;
		}
		case Config::HARDWARE_NGC:
		{
			uint32 input_mask = mp_input_component->GetInputMask();
			if (input_mask & Inp::Data::mA_Z)
			{
				if (input_mask & Inp::Data::mA_R1)
				{
					zoom_out_button_pressed=true;
				}
				if (input_mask & Inp::Data::mA_L1)
				{
					zoom_in_button_pressed=true;
				}
			}
			else
			{
				if (input_mask & Inp::Data::mA_R1)
				{
					lower_pressed=true;
				}
				if (input_mask & Inp::Data::mA_L1)
				{
					raise_pressed=true;
				}
			}
			break;
		}
		default:	
		{
			zoom_out_button_pressed=control_pad.m_R1.GetPressed();
			zoom_in_button_pressed=control_pad.m_R2.GetPressed();
			raise_pressed=control_pad.m_L1.GetPressed();
			lower_pressed=control_pad.m_L2.GetPressed();
			break;		
		}	
	}
	
	float radius_vel=Script::GetFloat(CRCD(0x4e58f829,"EditorCam_InOutSpeed"));
	if (zoom_out_button_pressed)
	{
	}	
	else if (zoom_in_button_pressed)
	{
		radius_vel=-radius_vel;
	}
	else
	{
		radius_vel=0.0f;
	}
		
	// Make the speed proportional to the current radius so that the camera can be zoomed
	// out to the max nice and quick.
	radius_vel*=m_radius;
	
	m_radius+=radius_vel;
	if (m_radius < m_radius_min)
	{
		m_radius = m_radius_min;
	}	
	if (m_radius > m_radius_max)
	{
		m_radius = m_radius_max;
	}	
	float zoom_factor=(m_radius-m_radius_min)/(m_radius_max-m_radius_min);
	// zoom_factor is between 0 and 1 and indicates how far out the camera is zoomed.
	// 0 = closest to cursor, 1 = furthest away.


	// Update the height
	float height_vel_min=Script::GetFloat(CRCD(0x604961f4,"EditorCam_UpDownSpeedMin"));
	float height_vel_max=Script::GetFloat(CRCD(0x5c445ead,"EditorCam_UpDownSpeedMax"));
	float height_vel=height_vel_min + zoom_factor * (height_vel_max-height_vel_min);
	
	// If very close in allow very fine movements, needed for rail placement in rail editor.
	// TODO: Fix this so that the change in speed is not so sudden, make it linear like above.
	if (m_radius < 100)
	{
		height_vel=0.2f;
	}
	
	if (raise_pressed)
	{
	}	
	else if (lower_pressed)
	{
		height_vel=-height_vel;
	}
	else
	{
		height_vel=0.0f;
	}	
	
	m_cursor_height+=height_vel;
	float max_height=Script::GetFloat(CRCD(0x61d56fa0,"EditorCam_MaxHeight"));
	if (m_cursor_height > max_height)
	{
		m_cursor_height=max_height;
	}

	// Switch off collision on any created-rail sectors whilst doing collision checks.
	// This is because the rail sectors have a vertical collidable poly, which occasionally does cause
	// collisions, making the cursor ping up into the air. Must be some sort of floating point innaccuracy
	// (or too much accuracy), probably because the collision check vector is straight down the vertical poly.
	Mdl::Skate * p_skate_mod =  Mdl::Skate::Instance();
	if (p_skate_mod->m_cur_level == CRCD(0xe8b4b836,"load_sk5ed"))
	{
		Obj::GetRailEditor()->SetSectorActiveStatus(false);
	}	

	// Make sure that the cursor cannot be raised through a collidable poly, otherwise it
	// can be raised through the stadium roof in slam city jam (TT3142)
	CFeeler feeler;
	feeler.SetStart(m_cursor_pos);
	feeler.SetEnd(m_cursor_pos + Mth::Vector(0.0f, m_cursor_height+10.0f, 0.0f));
	if (feeler.GetCollision())
	{
		m_cursor_height=feeler.GetPoint()[Y]-m_cursor_pos[Y]-10.0f;
	}
	
	// This allows the height to be less than m_min_height, but as soon as it goes
	// over m_min_height it won't be able to go below it again.
	// This is for the rail editor, where the min height needs to be limited whilst
	// still allowing snapping to a vertex.
	if (height_vel < 0.0f && m_cursor_height-height_vel+0.01 >= m_min_height)
	{
		if (m_cursor_height < m_min_height)
		{
			m_cursor_height=m_min_height;
		}
	}	
	if (m_cursor_height < 0.0f)
	{
		m_cursor_height=0.0f;
	}
		
	// Update m_cursor_pos ...

	// First move it horizontally according to the left analogue stick
	m_cursor_u.Set(cosf(m_angle),0.0f,-sinf(m_angle));
	m_cursor_v.Set(sinf(m_angle),0.0f,cosf(m_angle));
	
	float speed_min=Script::GetFloat(CRCD(0x83bde786,"EditorCam_MoveSpeedMin"));
	float speed_max=Script::GetFloat(CRCD(0xbfb0d8df,"EditorCam_MoveSpeedMax"));
	// Make the speed proportional to the zoom factor so that fine movements can be done
	// when zoomed in, whilst fast movements can be made when zoomed out.
	float speed = speed_min + zoom_factor * (speed_max-speed_min);
	
	// If very close in allow very fine movements, needed for rail placement in rail editor.
	// TODO: Fix this so that the change in speed is not so sudden, make it linear like above.
	if (m_radius < 100)
	{
		speed=1.0f;
	}
		
	float horiz=control_pad.m_leftX / 128.0f;
	float vert=control_pad.m_leftY / 128.0f;
	if (fabs(horiz) < 0.01f)
	{
		if (control_pad.m_left.GetPressed())
		{
			horiz=-0.5f;
		}	
		if (control_pad.m_right.GetPressed())
		{
			horiz=0.5f;
		}	
	}
		
	if (fabs(vert) < 0.01f)
	{
		if (control_pad.m_up.GetPressed())
		{
			vert=-0.5f;
		}	
		if (control_pad.m_down.GetPressed())
		{
			vert=0.5f;
		}	
	}	
		
	Mth::Vector vel = m_cursor_u * horiz*speed+
					  m_cursor_v * vert*speed;
	Mth::Vector old_pos=m_cursor_pos;					  
	m_cursor_pos+=vel;

	// Now do a collision check to find the y.	
	if (!find_collision_at_offset(old_pos, Script::GetFloat(CRCD(0xc7210318,"EditorCam_CursorCollisionFirstUpOffset"))))
	{
		// Do an upwards collision check to check for a ceiling above the skater, and do not
		// do the check for higher collision if there is a ceiling. This is to fix a bug where in Slam City
		// the goal cursor would pop up into the stands if hitting the teleport poly in the escalator D corridor.
		CFeeler feeler;
		feeler.SetStart(old_pos);
		Mth::Vector high=old_pos;
		high[Y]+=120.0f;
		feeler.SetEnd(high);
		if (feeler.GetCollision())
		{
			// Undo the movement so that the cursor does not move.
			m_cursor_pos=old_pos;
		}
		else if (!find_collision_at_offset(old_pos, Script::GetFloat(CRCD(0x56c2bff2,"EditorCam_CursorCollisionSecondUpOffset"))))
		{
			// Undo the movement so that the cursor does not move.
			m_cursor_pos=old_pos;
		}
		else
		{
			// Did find higher collision, which is going to cause the cursor to pop to a high place, such
			// as the roof of a building. In this case, zero the cursor height, because it seems weird to
			// have it maintain it's hover height in that case. (TT10272)
			m_cursor_height=m_min_height;
		}	
	}
		
	if (p_skate_mod->m_cur_level == CRCD(0xe8b4b836,"load_sk5ed"))
	{
		Obj::GetRailEditor()->SetSectorActiveStatus(true);
	}	

	
	// Calculate the camera position, which equals the cursor position but has a lag
	// applied so that as the cursor follows the contours of the ground it does not make
	// the camera glitch.
	m_camera_focus_pos[X]=m_cursor_pos[X];
	m_camera_focus_pos[Z]=m_cursor_pos[Z];
	m_camera_focus_pos[Y]+=(m_cursor_pos[Y]+m_cursor_height-m_camera_focus_pos[Y])*Script::GetFloat(CRCD(0x725ad02c,"EditorCam_YCatchUpFactor"));
	
	Mth::Vector tilted_v;
	tilted_v[X]=m_cursor_v[X]*cosf(m_tilt_angle);
	tilted_v[Z]=m_cursor_v[Z]*cosf(m_tilt_angle);
	tilted_v[Y]=sinf(m_tilt_angle);

	Mth::Matrix mat;
	mat[Z]=tilted_v;
	mat[X]=m_cursor_u;
	mat[Y]=Mth::CrossProduct(tilted_v,m_cursor_u);
	mat[X][W]=0.0f;
	mat[Y][W]=0.0f;
	mat[Z][W]=0.0f;
	mat[W].Set();
	GetObject()->SetMatrix(mat);
	
	Mth::Vector start=m_camera_focus_pos;
	/*
	Mth::Vector end;
	float radius=m_radius;
	while (true)
	{
		end=start+tilted_v*radius;

		if (radius > Script::GetFloat(CRCD(0xf612c474,"EditorCam_CursorCollisionEnableDistMax")))
		{
			printf("Not checksing for collision\n");
			break;
		}
		
		Mth::Vector p_rays[4];
		p_rays[0]=start;
		p_rays[1]=start-120*u+Mth::Vector(0.0f,70.0f,0.0f,0.0f);
		p_rays[2]=start+Mth::Vector(0.0f,70.0f,0.0f,0.0f);
		p_rays[3]=start+120*u+Mth::Vector(0.0f,70.0f,0.0f,0.0f);
		if (cursor_occluded(p_rays,4,end))
		{
			radius=radius*0.9f; // TODO: Hmmm, should find out the exact distance ...
		}	
		else
		{
			break;
		}	
	}

	m_cam_pos+=(end-m_cam_pos)*Script::GetFloat(CRCD(0xf151a64,"EditorCam_CameraCatchUpFactor"));
	*/
	// TODO: Fix the camera collision above, then comment out this line for it to have effect.
	m_cam_pos=start + tilted_v*m_radius;

	m_cam_pos[W]=1.0f;
	GetObject()->SetPos(m_cam_pos);
	
	
	update_shadow();
}

bool CEditorCameraComponent::cursor_occluded(Mth::Vector *p_ray_starts, int numRays, Mth::Vector end)
{
	CFeeler feeler;
	for (int i=0; iGetVector(CRCD(0xb9d31b0a,"position"),&m_cursor_pos);
			m_camera_focus_pos=m_cursor_pos;
			m_radius=700.0f;
			pParams->GetFloat(CRCD(0xc48391a5,"radius"),&m_radius);
			m_tilt_angle=0.6f;
			pParams->GetFloat(CRCD(0xe3c07609,"tilt"),&m_tilt_angle);
			m_cursor_height=0.0f;
			pParams->GetFloat(CRCD(0xb8d629ca,"cursor_height"),&m_cursor_height);
			break;

		case 0x69cb3ae1: // EditorCam_SetCursorPos
		{
			Mth::Vector pos;
			pParams->GetVector(CRCD(0xb9d31b0a,"position"),&pos,Script::ASSERT);
			m_cursor_pos=pos;
			m_cursor_height=0.0f;
			
			// Call update to do the ground collision check.
			Update();
			// That might have changed m_cursor_pos[Y], so adjust the height so that the cursor does 
			// appear at the required position.
			m_cursor_height=pos[Y]-m_cursor_pos[Y];
			// Note: If the above call to Update() followed by adjusting the height were not done, then it would not
			// be possible to snap to rail vertices, because rails are uncollidable, so on the next frame the cursor
			// would fall through the rail back to the ground.
			break;
		}
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CEditorCameraComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CEditorCameraComponent::GetDebugInfo"));
	
	p_info->AddInteger("m_active",m_active);
	p_info->AddVector("m_camera_focus_pos",m_camera_focus_pos.GetX(),m_camera_focus_pos.GetY(),m_camera_focus_pos.GetZ());
	p_info->AddVector("m_cursor_pos",m_cursor_pos.GetX(),m_cursor_pos.GetY(),m_cursor_pos.GetZ());
	p_info->AddFloat("m_cursor_height",m_cursor_height);
	p_info->AddFloat("m_radius",m_radius);
	p_info->AddFloat("m_radius_min",m_radius_min);
	p_info->AddFloat("m_radius_max",m_radius_max);
	p_info->AddFloat("m_angle",m_angle);
	p_info->AddFloat("m_tilt_angle",m_tilt_angle);
	p_info->AddFloat("m_tilt_angle_min",m_tilt_angle_min);
	p_info->AddFloat("m_tilt_angle_max",m_tilt_angle_max);

// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Sk/Components/EditorCameraComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components/
//* FILENAME:       EditorCameraComponent.h
//* OWNER:          KSH
//* CREATION DATE:  26 Mar 2003
//****************************************************************************

#ifndef __COMPONENTS_EDITORCAMERACOMPONENT_H__
#define __COMPONENTS_EDITORCAMERACOMPONENT_H__

#include 
#include 

#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_EDITORCAMERA CRCD(0xac8946e2,"EditorCamera")

//  Standard accessor macros for getting the component either from within an object, or 
//  given an object				 
#define		GetEditorCameraComponent() ((Obj::CEditorCameraComponent*)GetComponent(CRC_EDITORCAMERA))
#define		GetEditorCameraComponentFromObject(pObj) ((Obj::CEditorCameraComponent*)(pObj)->GetComponent(CRC_EDITORCAMERA))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Nx
{
	class CModel;
}

namespace Gfx
{
	class CSimpleShadow;
}
	
namespace Obj
{
class CInputComponent;

class CEditorCameraComponent : public CBaseComponent
{
	bool m_active;
	
	// The point at which the camera looks. This basically equals m_cursor_pos
	// except it smoothly catches up with m_cursor_pos so as to smooth out glitches.
	Mth::Vector m_camera_focus_pos;
	Mth::Vector m_cam_pos;
	
	// The position of the Goal model that get moves around by the controls.
	Mth::Vector m_cursor_pos;
	
	// The orthogonal horizontal vectors that define the cursor's orientation					 
	Mth::Vector m_cursor_u;
	Mth::Vector m_cursor_v;
	
	float m_radius;
	float m_radius_min;
	float m_radius_max;
	
	float m_angle;
	float m_tilt_angle;
	float m_tilt_angle_min;
	float m_tilt_angle_max;

	float m_cursor_height;
	float m_min_height;

	bool m_simple_collision;
	bool m_allow_movement_through_walls;
		
	CInputComponent *mp_input_component;
	
	Gfx::CSimpleShadow *mp_shadow;
	void create_shadow();
	void delete_shadow();
	void update_shadow();
	#define SHADOW_COLLISION_CHECK_DISTANCE -10000.0f
	#define SHADOW_SCALE 1.2f
	
	bool cursor_occluded(Mth::Vector *p_ray_starts, int numRays, Mth::Vector end);

	bool find_y(Mth::Vector start, Mth::Vector end, float *p_y);
	bool find_collision_at_offset(Mth::Vector oldCursorPos, float upOffset);
	

public:
    CEditorCameraComponent();
    virtual ~CEditorCameraComponent();

public:
	Mth::Vector&					GetCursorPos() {return m_cursor_pos;}
	void							SetCursorPos(const Mth::Vector& pos) {m_cursor_pos=pos;}
	void							GetCursorOrientation(Mth::Vector *p_u, Mth::Vector *p_v);
	float							GetCursorOrientation() {return m_angle;}
	void							SetCursorOrientation(float angle) {m_angle=angle;}
	
	void							SetCursorHeight(float height) {m_cursor_height=height;}
	float							GetCursorHeight() {return m_cursor_height;}
	void							SetCursorMinHeight(float minHeight) {m_min_height=minHeight;}
	
	void							SetSimpleCollision(bool whatever) {m_simple_collision=whatever;}
	
	virtual	void 					Finalize();
	virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
	virtual void					Hide( bool shouldHide );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
};

}

#endif // __COMPONENTS_EDITORCAMERACOMPONENT_H__


================================================
FILE: Code/Sk/Components/GoalEditorComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       GoalEditorComponent.cpp
//* OWNER:          Kendall Harrison
//* CREATION DATE:  3/21/2003
//****************************************************************************

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

//#define SIZE_TEST

namespace Obj
{

static uint32 s_convert_level(uint32 level)
{
	if (level==CRCD(0xb664035d,"Load_Sk5Ed_gameplay"))
	{
		// Map Load_Sk5Ed_gameplay back to Load_Sk5Ed 
		level=CRCD(0xe8b4b836,"Load_Sk5Ed");
	}
	
	return level;
}

// Often in the scripts the value returned by GetCurrentLevel is passed on to commands like GetEditedGoalsInfo
// However sometimes the level might be returned as Load_Sk5Ed_gameplay rather than Load_Sk5Ed, so
// this will map it back to Load_Sk5Ed
static uint32 s_get_level_checksum(Script::CStruct *p_params)
{
	uint32 level=0;
	p_params->GetChecksum(CRCD(0x651533ec,"level"),&level);
	
	level=s_convert_level(level);
			
	return level;
}

static uint32 s_get_current_level()
{
	Mdl::Skate *p_skate_module = Mdl::Skate::Instance();
	uint32 current_level=p_skate_module->m_cur_level;
	
	current_level=s_convert_level(current_level);						 
	
	return current_level;
}	
	
static const char *s_generate_letter_model_filename(uint32 goalType, int letterIndex)
{
	const char *p_model_file_name=NULL;
	switch (goalType)
	{
		case 0x54166acd: // Skate
			switch (letterIndex)
			{
				case 0:
					p_model_file_name="gameobjects\\skate\\letter_s\\letter_s.mdl";
					break;
				case 1:
					p_model_file_name="gameobjects\\skate\\letter_k\\letter_k.mdl";
					break;
				case 2:
					p_model_file_name="gameobjects\\skate\\letter_a\\letter_a.mdl";
					break;
				case 3:
					p_model_file_name="gameobjects\\skate\\letter_t\\letter_t.mdl";
					break;
				case 4:
					p_model_file_name="gameobjects\\skate\\letter_e\\letter_e.mdl";
					break;
				default:
					break;
			}		
			break;
		case 0x4ec3cfb5: // Combo
			switch (letterIndex)
			{
				case 0:
					p_model_file_name="gameobjects\\combo\\goal_combo_c\\goal_combo_c.mdl";
					break;
				case 1:
					p_model_file_name="gameobjects\\combo\\goal_combo_o\\goal_combo_o.mdl";
					break;
				case 2:
					p_model_file_name="gameobjects\\combo\\goal_combo_m\\goal_combo_m.mdl";
					break;
				case 3:
					p_model_file_name="gameobjects\\combo\\goal_combo_b\\goal_combo_b.mdl";
					break;
				case 4:
					p_model_file_name="gameobjects\\combo\\goal_combo_o\\goal_combo_o.mdl";
					break;
				default:
					break;
			}		
			break;
		default:
			break;
	}				
	return p_model_file_name;
}

static uint32 s_generate_letter_node_type_checksum(uint32 goalType, int letterIndex)
{
	uint32 checksum=0;
	switch (goalType)
	{
		case 0x54166acd: // Skate
			switch (letterIndex)
			{
				case 0:
					checksum=CRCD(0xf75425ea,"Letter_S");
					break;
				case 1:
					checksum=CRCD(0xe438bdbc,"Letter_K");
					break;
				case 2:
					checksum=CRCD(0x4ed54a2,"Letter_A");
					break;
				case 3:
					checksum=CRCD(0x6930b049,"Letter_T");
					break;
				case 4:
					checksum=CRCD(0x38090bb,"Letter_E");
					break;
				default:
					break;
			}		
			break;
		case 0x4ec3cfb5: // Combo
			switch (letterIndex)
			{
				case 0:
					checksum=CRCD(0x7bf1ba66,"Combo_C");
					break;
				case 1:
					checksum=CRCD(0x7247f64d,"Combo_O");
					break;
				case 2:
					checksum=CRCD(0x9c499761,"Combo_M");
					break;
				case 3:
					checksum=CRCD(0xcf68af0,"Combo_B");
					break;
				case 4:
					checksum=CRCD(0x7247f64d,"Combo_O");
					break;
				default:
					break;
			}		
			break;
		default:
			break;
	}				
	return checksum;
}

static uint32 s_generate_node_name_checksum(int goalIndex, int positionIndex)
{
	char p_name[100];
	p_name[0]=0;
	
	switch (positionIndex)
	{
		case 0:
			sprintf(p_name,"TRG_EditedGoal%d_Pro",goalIndex);
			break;
		case 1:
			sprintf(p_name,"TRG_EditedGoal%d_Restart",goalIndex);
			break;
		default:
			sprintf(p_name,"TRG_EditedGoal%d_Letter_%d",goalIndex,positionIndex-2);
			break;	
	}
	
	return Script::GenerateCRC(p_name);
}

static const Script::CStruct *s_get_default_goal_params(uint32 goalType)
{
	Script::CStruct *p_default_params=NULL;
	
	switch (goalType)
	{
		case 0x54166acd: // Skate
			p_default_params=Script::GetStructure(CRCD(0x3328976a,"Goal_SkateLetters_genericParams"));
			break;
		case 0x4ec3cfb5: // Combo
			p_default_params=Script::GetStructure(CRCD(0xb4f5801d,"Goal_amateurCOMBOline_genericParams"));
			break;
		case 0x6fe44c6d: // HighScore
			p_default_params=Script::GetStructure(CRCD(0xef8f345f,"Goal_HighScore_genericParams"));
			break;
		case 0xec414b76: // HighCombo
			p_default_params=Script::GetStructure(CRCD(0xb520a510,"goal_highcombo_genericParams"));
			break;
		case 0x7fe2238a: // SkateTris
		case 0x0fc6f698: // ComboSkateTris
		case 0xea7ba666: // TrickTris
			p_default_params=Script::GetStructure(CRCD(0x675816b5,"goal_tetris_genericParams"));
			break;
		case 0x61c5d092: // Gap
			p_default_params=Script::GetStructure(CRCD(0x5756a675,"Goal_Gaps_genericParams"));	
			break;
		default:
			break;
	}			
	return p_default_params;
}

static const Script::CStruct *s_get_extra_goal_params(uint32 goalType)
{
	Script::CStruct *p_extra_params=NULL;
	
	switch (goalType)
	{
		case 0x54166acd: // Skate
			p_extra_params=Script::GetStructure(CRCD(0xe41e0e04,"EditedGoal_ExtraParams_Skate"));
			break;
		case 0x4ec3cfb5: // Combo
			p_extra_params=Script::GetStructure(CRCD(0xfecbab7c,"EditedGoal_ExtraParams_Combo"));
			break;
		case 0x6fe44c6d: // HighScore
			p_extra_params=Script::GetStructure(CRCD(0x28c7b799,"EditedGoal_ExtraParams_HighScore"));
			break;
		case 0xec414b76: // HighCombo
			p_extra_params=Script::GetStructure(CRCD(0xab62b082,"EditedGoal_ExtraParams_HighCombo"));
			break;
		case 0x7fe2238a: // SkateTris
			p_extra_params=Script::GetStructure(CRCD(0x38c1d87e,"EditedGoal_ExtraParams_SkateTris"));
			break;
		case 0x0fc6f698: // ComboSkateTris
			p_extra_params=Script::GetStructure(CRCD(0x6626b0d8,"EditedGoal_ExtraParams_ComboSkateTris"));
			break;
		case 0xea7ba666: // TrickTris	
			p_extra_params=Script::GetStructure(CRCD(0xad585d92,"EditedGoal_ExtraParams_TrickTris"));
			break;
		case 0x61c5d092: // Gap
			p_extra_params=Script::GetStructure(CRCD(0x9005703a,"EditedGoal_ExtraParams_Gap"));
			break;
		default:
			break;
	}
	return p_extra_params;
}
			
void InsertGoalEditorNodes()
{
	Script::CArray *p_node_array=GetArray(CRCD(0xc472ecc5,"NodeArray"),Script::ASSERT);
	
	uint32 new_node_index=0;
	if (p_node_array->GetSize()==0)
	{
		// If it's empty, it'll need its type setting too.
		p_node_array->SetSizeAndType(MAX_POSITIONS_PER_GOAL*MAX_GOALS_PER_LEVEL,ESYMBOLTYPE_STRUCTURE);
	}
	else
	{
		new_node_index=p_node_array->GetSize();
		p_node_array->Resize(p_node_array->GetSize() + MAX_POSITIONS_PER_GOAL*MAX_GOALS_PER_LEVEL);
	}	
	
	Script::CStruct *p_new_node=NULL;
	for (int g=0; gAppendStructure(Script::GetStructure(CRCD(0x8f0f67d7,"EditedGoal_Pro_Node"),Script::ASSERT));
		p_new_node->AddChecksum(CRCD(0xa1dc81f9,"Name"),s_generate_node_name_checksum(g,0));
		p_new_node->AddInteger(CRCD(0xe50d6573,"NodeIndex"),new_node_index);
		p_node_array->SetStructure(new_node_index++,p_new_node);
		
		p_new_node=new Script::CStruct;
		p_new_node->AppendStructure(Script::GetStructure(CRCD(0xc9119673,"EditedGoal_Restart_Node"),Script::ASSERT));
		p_new_node->AddChecksum(CRCD(0xa1dc81f9,"Name"),s_generate_node_name_checksum(g,1));
		p_new_node->AddInteger(CRCD(0xe50d6573,"NodeIndex"),new_node_index);
		p_node_array->SetStructure(new_node_index++,p_new_node);
		
		for (int i=0; iAppendStructure(Script::GetStructure(CRCD(0x1aaf51ba,"EditedGoal_Letter_Node"),Script::ASSERT));
			
			p_new_node->AddChecksum(CRCD(0xa1dc81f9,"Name"),s_generate_node_name_checksum(g,i+2));
			p_new_node->AddInteger(CRCD(0xe50d6573,"NodeIndex"),new_node_index);
			
			p_node_array->SetStructure(new_node_index++,p_new_node);
		}	
	}	
}

CEditGoal::CEditGoal()
{
	Clear();
}

CEditGoal::~CEditGoal()
{
}

void CEditGoal::Clear()
{
	m_used=false;
	m_level_script=0;
	mp_goal_name[0]=0;	
	mp_goal_description[0]=0;
	mp_win_message[0]=0;
	mp_ped_name[0]=0;
	m_goal_type=0;
	
	m_num_positions_set=0;
	m_num_positions_required=0;
	m_current_position_index=0;
	m_placed_last_position=false;
	m_has_won_goal=false;
		
	m_score=100;
	m_time_limit=120;
	
	m_acceleration_interval=5;
	m_acceleration_percent=0.1f;
	m_trick_time=3000;
	m_time_to_stop_adding_tricks=5;
	m_max_tricks=11;
	m_spin=360;
	
	m_single_combo=false;
	m_combo_size=2;
	
	m_tricktris_block_size=3;
	m_tricktris_total_to_win=8;

	m_num_combo_sets=0;

	m_control_type=CRCD(0x54166acd,"skate");
	
	m_num_gaps=0;
	mp_trick_name[0]=0;
				
	for (int i=0; iAppendStructure(s_get_default_goal_params(m_goal_type));
	p_params->AppendStructure(s_get_extra_goal_params(m_goal_type));
	
	// Now change the node names to be those of the special nodes created just for edited goals.
    p_params->AddChecksum(CRCD(0x2d7e03d,"trigger_obj_id"),s_generate_node_name_checksum(m_level_goal_index,0));
    p_params->AddChecksum(CRCD(0x3494f170,"restart_node"),s_generate_node_name_checksum(m_level_goal_index,1));
	
	// Then write in other misc params.
	
	switch (m_goal_type)
	{
		case 0x54166acd: // Skate
		{
			p_params->AddChecksum(CRCD(0xba54bd5c,"S_obj_id"),s_generate_node_name_checksum(m_level_goal_index,2));
			p_params->AddChecksum(CRCD(0x150a97c2,"K_obj_id"),s_generate_node_name_checksum(m_level_goal_index,3));
			p_params->AddChecksum(CRCD(0x84ca8b0a,"A_obj_id"),s_generate_node_name_checksum(m_level_goal_index,4));
			p_params->AddChecksum(CRCD(0xb091b445,"T_obj_id"),s_generate_node_name_checksum(m_level_goal_index,5));
			p_params->AddChecksum(CRCD(0x008085f0,"E_obj_id"),s_generate_node_name_checksum(m_level_goal_index,6));
			break;
		}
			
		case 0x4ec3cfb5: // Combo
		{
			Script::CArray *p_array=new Script::CArray;
			p_array->SetSizeAndType(5,ESYMBOLTYPE_STRUCTURE);
			
			Script::CStruct *p_struct=new Script::CStruct;
			p_struct->AddChecksum(CRCD(0x99f6ccbb,"obj_id"),s_generate_node_name_checksum(m_level_goal_index,2));
			p_struct->AddString(CRCD(0xc4745838,"text"),"C");
			p_array->SetStructure(0,p_struct);

			p_struct=new Script::CStruct;
			p_struct->AddChecksum(CRCD(0x99f6ccbb,"obj_id"),s_generate_node_name_checksum(m_level_goal_index,3));
			p_struct->AddString(CRCD(0xc4745838,"text"),"O");
			p_array->SetStructure(1,p_struct);

			p_struct=new Script::CStruct;
			p_struct->AddChecksum(CRCD(0x99f6ccbb,"obj_id"),s_generate_node_name_checksum(m_level_goal_index,4));
			p_struct->AddString(CRCD(0xc4745838,"text"),"M");
			p_array->SetStructure(2,p_struct);

			p_struct=new Script::CStruct;
			p_struct->AddChecksum(CRCD(0x99f6ccbb,"obj_id"),s_generate_node_name_checksum(m_level_goal_index,5));
			p_struct->AddString(CRCD(0xc4745838,"text"),"B");
			p_array->SetStructure(3,p_struct);

			p_struct=new Script::CStruct;
			p_struct->AddChecksum(CRCD(0x99f6ccbb,"obj_id"),s_generate_node_name_checksum(m_level_goal_index,6));
			p_struct->AddString(CRCD(0xc4745838,"text"),"O");
			p_array->SetStructure(4,p_struct);
			
			p_params->AddArrayPointer(CRCD(0xb3c7f1b2,"letter_info"),p_array);
			break;
		}	
		
		default:
			break;
	}

	WriteGoalSpecificParams(p_params,WRITING_TO_GOAL_MANAGER);
	
	if (mp_goal_description[0])
	{
		p_params->AddString(CRCD(0xc5d7e6b,"goal_description"),mp_goal_description);
	}

	if (mp_win_message[0])
	{
		Script::CArray *p_default_success_cam_anims=Script::GetArray(CRCD(0x8070c110,"EditedGoal_success_cam_anims"),Script::ASSERT);
		Dbg_MsgAssert(p_default_success_cam_anims->GetSize()==1,("Expected EditedGoal_success_cam_anims to contain just one structure"));
		
		Script::CArray *p_success_cam_anims=new Script::CArray;
		Script::CopyArray(p_success_cam_anims, p_default_success_cam_anims);
		p_success_cam_anims->GetStructure(0)->AddString(CRCD(0xa7ab3b6d,"cam_anim_text"), mp_win_message);
		p_success_cam_anims->GetStructure(0)->AddChecksum(CRCD(0x531e4d28,"targetid"),s_generate_node_name_checksum(m_level_goal_index,0));
			
		p_params->AddArrayPointer(CRCD(0x27cd32e1,"success_cam_anims"), p_success_cam_anims);
	}

	p_params->AddString(CRCD(0xbfecc45b,"goal_name"),GetIDString());
	
	if (mp_goal_name[0])
	{
		p_params->AddString(CRCD(0x4bc5229d,"view_goals_text"),mp_goal_name);
	}	
		
	if (mp_ped_name[0])
	{
		p_params->AddString(CRCD(0x243b9c3b,"full_name"),mp_ped_name);
	}	
	
	p_params->AddInteger(CRCD(0x906b67ba,"time"),m_time_limit);
	p_params->AddChecksum(CRCD(0x81cff663,"control_type"),m_control_type);		
	
	
	// This is so that the goal manager can tell that this is an edited goal, and hence not save its
	// details to the mem card.
	p_params->AddChecksum(NONAME,CRCD(0x981d3ad0,"edited_goal"));
}

uint32 CEditGoal::generate_marker_object_name(int index)
{
	static char p_name[100];
	sprintf(p_name,"GoalEditorMarkerObject%d",index);
	return Script::GenerateCRC(p_name);
}	

uint32 CEditGoal::generate_goal_object_name(int index)
{
	static char p_name[100];
	
	if (index == 0)
	{
		sprintf(p_name,"TRG_EditedGoal%d_Pro",m_level_goal_index);
	}
	else if (index > 1)
	{
		sprintf(p_name,"TRG_EditedGoal%d_Letter_%d",m_level_goal_index,index-2);
	}
	else
	{
		// Position 1 is the restart, so no object exists.
		return 0;
	}	
	
	return Script::GenerateCRC(p_name);
}	

void CEditGoal::create_marker(int index)
{
	Dbg_MsgAssert(index=2,("Bad index"));
			const char *p_letter_model_filename=s_generate_letter_model_filename(m_goal_type,index-2);
			params.AddString("Model",p_letter_model_filename);
			params.AddChecksum(NONAME,CRCD(0x3bd176d9,"RotateAndHover"));
			break;
		}
		default:
			break;
	}
					
	params.AddChecksum("Name",generate_marker_object_name(index));

	params.AddVector("Pos",mp_item_positions[index].mPos[X],
						   mp_item_positions[index].mPos[Y]+mp_item_positions[index].mHeight,
						   mp_item_positions[index].mPos[Z]);
	params.AddFloat("Angle",mp_item_positions[index].mAngle * 180.0f/3.141592654f);
	
	Script::RunScript("goal_editor_create_marker_object",¶ms);
}

void CEditGoal::delete_marker(int index)
{
	Dbg_MsgAssert(indexm_cur_level == CRCD(0xe8b4b836,"load_sk5ed") )
	{
		// Don't allow the pos to be too close to the park boundary, otherwise the ped can be made to
		// be embedded in the wall.
		if (!Ed::IsWithinParkBoundaries(pos, 30.0f))
		{
			return false;
		}
	}
			
	Mth::Vector pos_with_height=pos;
	pos_with_height[Y]+=height;
	if (position_too_close_to_existing_positions(pos_with_height))
	{
		return false;
	}
	return true;
}

// Used by the GetEditedGoalsInfo script command for use when creating the menu of existing goals.
// Also used by GetCurrentEditedGoalInfo command.
// Also used for writing the goal info to the mem card.
// And used by GetDebugInfo for sending stuff to the script debugger.
void CEditGoal::WriteIntoStructure(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info"));
	
	p_info->AddChecksum(NONAME,CRCD(0x981d3ad0,"edited_goal"));
	
	p_info->AddChecksum(CRCD(0x651533ec,"level"),m_level_script);
	p_info->AddInteger(CRCD(0x60ce75ae,"level_goal_index"),m_level_goal_index);
	p_info->AddChecksum(CRCD(0x9982e501,"goal_id"),GetId());
	p_info->AddInteger(CRCD(0xc51bf6e3,"time_limit"),m_time_limit);
	p_info->AddInteger(CRCD(0x14938611,"won_goal"),m_has_won_goal);
	p_info->AddChecksum(CRCD(0x81cff663,"control_type"),m_control_type);		
	
	p_info->AddString(CRCD(0x4bc5229d,"view_goals_text"),mp_goal_name);
	p_info->AddString(CRCD(0xc5d7e6b,"goal_description"),mp_goal_description);
	p_info->AddString(CRCD(0x5de57284,"win_message"),mp_win_message);
	p_info->AddString(CRCD(0xb9426608,"ped_name"),mp_ped_name);
	p_info->AddChecksum(CRCD(0x6d11ed74,"goal_type"),m_goal_type);
	p_info->AddChecksum(CRCD(0x71fc348b,"pro_name"),s_generate_node_name_checksum(m_level_goal_index,0));
	
	WriteGoalSpecificParams(p_info);

	if (m_num_positions_set)
	{
		Script::CArray *p_position_array=new Script::CArray;
		p_position_array->SetSizeAndType(m_num_positions_set,ESYMBOLTYPE_STRUCTURE);
		for (uint i=0; iAddVector(CRCD(0x7f261953,"Pos"),mp_item_positions[i].mPos[X],
														 mp_item_positions[i].mPos[Y],
														 mp_item_positions[i].mPos[Z]);
			p_pos_info->AddFloat(CRCD(0xab21af0,"Height"),mp_item_positions[i].mHeight);
			p_pos_info->AddFloat(CRCD(0xff7ebaf6,"Angle"),mp_item_positions[i].mAngle);
			p_position_array->SetStructure(i,p_pos_info);
		}
		p_info->AddArrayPointer(CRCD(0x32df12e0,"node_positions"),p_position_array);
	}	
}

void CEditGoal::ReadFromStructure(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info"));
	
	Clear();
	m_used=true;

	int won_goal=0;
    //p_info->GetInteger(CRCD(0x14938611,"won_goal"),&won_goal);
	m_has_won_goal=won_goal;
	
	m_level_script=s_get_level_checksum(p_info);
	Dbg_MsgAssert(m_level_script,("Zero m_level_script"));
	
	p_info->GetInteger(CRCD(0x60ce75ae,"level_goal_index"),&m_level_goal_index);
	
	p_info->GetChecksum(CRCD(0x6d11ed74,"goal_type"),&m_goal_type);
	Dbg_MsgAssert(m_goal_type,("Zero m_goal_type"));
	// Calling SetType will set up m_num_positions_required
	SetType(m_goal_type);

	p_info->GetInteger(CRCD(0xcd66c8ae,"score"),&m_score);

	p_info->GetInteger(CRCD(0xc51bf6e3,"time_limit"),&m_time_limit);
	p_info->GetChecksum(CRCD(0x81cff663,"control_type"),&m_control_type);
	
	const char *p_string="";
	p_info->GetString(CRCD(0x4bc5229d,"view_goals_text"),&p_string);
	Dbg_MsgAssert(strlen(p_string)GetString(CRCD(0xc5d7e6b,"goal_description"),&p_string);
	Dbg_MsgAssert(strlen(p_string)GetString(CRCD(0x5de57284,"win_message"),&p_string);
	Dbg_MsgAssert(strlen(p_string)GetString(CRCD(0xb9426608,"ped_name"),&p_string);
	Dbg_MsgAssert(strlen(p_string)GetArray(CRCD(0x32df12e0,"node_positions"),&p_position_array))
	{
		Dbg_MsgAssert(p_position_array->GetSize()==m_num_positions_required,("Bad node_positions size for goal '%s'",mp_goal_name));
		
		m_num_positions_set=p_position_array->GetSize();
		for (uint i=0; iGetStructure(i);
			p_struct->GetVector(CRCD(0x7f261953,"Pos"),&mp_item_positions[i].mPos);
			p_struct->GetFloat(CRCD(0xab21af0,"Height"),&mp_item_positions[i].mHeight);
			p_struct->GetFloat(CRCD(0xff7ebaf6,"Angle"),&mp_item_positions[i].mAngle);
		}	
	}
		
	ReadGoalSpecificParams(p_info);
}

void CEditGoal::AddGap(uint8 gap_number)
{
	if (GotGap(gap_number))
	{
		return;
	}
		
	if (m_num_gaps>=MAX_GAPS)
	{
		printf("Warning! MAX_GAPS limit reached!\n");
		return;
	}	
	Dbg_MsgAssert(m_num_gaps gap_number)
		{
			--mp_gap_numbers[i];
		}
	}
}
	
bool CEditGoal::GotGap(uint8 gap_number)
{
	for (int i=0; iAddChecksum(CRCD(0x7be1e689,"goal_tetris_key_combos"),CRCD(0x6d641bcb,"emergency_key_combos"));
		return;
	}
		
	Script::CArray *p_predefined_key_combos=Script::GetArray(CRCD(0xba9358a5,"cag_skatetris_key_combos"),Script::ASSERT);

	int size=0;
	
	// Calculate the total size of the goal_tetris_key_combos array.
	for (int i=0; iGetStructure(mp_combo_sets[i].mSetIndex);
		Script::CArray *p_key_combos=NULL;
		p_struct->GetArray(CRCD(0x79704516,"key_combos"),&p_key_combos);
		Dbg_MsgAssert(p_key_combos,("Missing key_combos array in element %d of array cag_skatetris_key_combos",i));
		size+=p_key_combos->GetSize();
	}

	Dbg_MsgAssert(size,("Zero size for goal_tetris_key_combos array!"));

	Script::CArray *p_goal_tetris_key_combos=new Script::CArray;
	p_goal_tetris_key_combos->SetSizeAndType(size,ESYMBOLTYPE_STRUCTURE);

	int dest_index=0;
	for (int i=0; iGetStructure(mp_combo_sets[i].mSetIndex);
		Script::CArray *p_key_combos=NULL;
		p_struct->GetArray(CRCD(0x79704516,"key_combos"),&p_key_combos);
		Dbg_MsgAssert(p_key_combos,("Missing key_combos array in element %d of array cag_skatetris_key_combos",i));
		
		for (uint32 j=0; jGetSize(); ++j)
		{
			Script::CStruct *p_entry=new Script::CStruct;

			if (p_key_combos->GetType()==ESYMBOLTYPE_NAME)
			{
				p_entry->AddChecksum(CRCD(0xacfdb27a,"key_combo"),p_key_combos->GetChecksum(j));
			}
			else
			{
				p_entry->AppendStructure(p_key_combos->GetStructure(j));
			}
			
			// Overlay any extra params the user specified for this set.
			if (mp_combo_sets[i].mNumTaps)
			{
				p_entry->AddInteger(CRCD(0xa4bee6a1,"num_taps"),mp_combo_sets[i].mNumTaps);
			}
			if (mp_combo_sets[i].mRequirePerfect)
			{
				p_entry->AddChecksum(NONAME,CRCD(0x687e2c25,"require_perfect"));
			}
			//if (mp_combo_sets[i].mSpin)
			//{
			//	p_entry->AddInteger(CRCD(0x8c8abd19,"random_spin"),mp_combo_sets[i].mSpin*180);
			//}
			
			// Originally each set could have it's own spin, but it was decided that that was not
			// necessary and that instead a single spin value would be specified for the whole goal.
			if (m_spin)
			{
				// random_spin means the goal manager will actually choose a random spin up to the m_spin value.
				p_entry->AddInteger(CRCD(0x8c8abd19,"random_spin"),m_spin);
			}
				
			p_goal_tetris_key_combos->SetStructure(dest_index++,p_entry);
		}
	}
		
	p_info->AddArrayPointer(CRCD(0x7be1e689,"goal_tetris_key_combos"),p_goal_tetris_key_combos);
}

// Used for writing the mp_combo_sets array to mem card.
// Also used in GetDebugInfo
void CEditGoal::write_combo_sets(Script::CStruct *p_info)
{
	if (!m_num_combo_sets)
	{
		return;
	}
		
	Script::CArray *p_array=new Script::CArray;
	p_array->SetSizeAndType(m_num_combo_sets,ESYMBOLTYPE_STRUCTURE);
	for (int i=0; iAddInteger(CRCD(0xeb7d6498,"set_index"),mp_combo_sets[i].mSetIndex);
		if (mp_combo_sets[i].mRequirePerfect)
		{
			p_struct->AddChecksum(NONAME,CRCD(0x687e2c25,"require_perfect"));
		}
		p_struct->AddInteger(CRCD(0xedf5db70,"spin"),mp_combo_sets[i].mSpin*180);
		p_struct->AddInteger(CRCD(0xa4bee6a1,"num_taps"),mp_combo_sets[i].mNumTaps);
		
		p_array->SetStructure(i,p_struct);
	}	
	p_info->AddArrayPointer(CRCD(0x490f171,"combo_sets"),p_array);
}

void CEditGoal::read_combo_sets(Script::CStruct *p_info)
{
	Script::CArray *p_array=NULL;
	if (!p_info->GetArray(CRCD(0x490f171,"combo_sets"),&p_array))
	{
		return;
	}
		
	m_num_combo_sets=p_array->GetSize();
	Dbg_MsgAssert(m_num_combo_sets<=MAX_COMBO_SETS,("m_num_combo_sets of %d too big",m_num_combo_sets));
	
	for (int i=0; iGetStructure(i);
		
		int int_val=0;
		p_struct->GetInteger(CRCD(0xeb7d6498,"set_index"),&int_val);
		mp_combo_sets[i].mSetIndex=int_val;
		
		int_val=0;
		p_struct->GetInteger(CRCD(0xedf5db70,"spin"),&int_val);
		Dbg_MsgAssert((int_val%180)==0,("Bad spin of %d",int_val));
		mp_combo_sets[i].mSpin=int_val/180;
		
		int_val=0;
		p_struct->GetInteger(CRCD(0xa4bee6a1,"num_taps"),&int_val);
		mp_combo_sets[i].mNumTaps=int_val;
		
		mp_combo_sets[i].mRequirePerfect=p_struct->ContainsFlag(CRCD(0x687e2c25,"require_perfect"));
	}	
}

// The dest parameter determines the format in which the combo sets are written out.
// When generating params for the goal manager, the full goal_tetris_key_combos array is
// generated. When writing to mem card the chosen pre-defined set numbers are written instead.
void CEditGoal::write_skatetris_params(Script::CStruct *p_info, EWriteDest dest)
{
	p_info->AddInteger(CRCD(0x1181b29,"acceleration_interval"),m_acceleration_interval);
	p_info->AddFloat(CRCD(0x76f65c8,"acceleration_percent"),m_acceleration_percent);
	p_info->AddInteger(CRCD(0xded8540a,"trick_time"),m_trick_time);
	p_info->AddInteger(CRCD(0xa6d96603,"time_to_stop_adding_tricks"),m_time_to_stop_adding_tricks);
	p_info->AddInteger(CRCD(0x89473db7,"max_tricks"),m_max_tricks);
	
	if (dest==WRITING_TO_GOAL_MANAGER)
	{
		// When writing params out for the goal manager, the m_spin value is given to all of
		// the key combos.
		generate_key_combos_array(p_info);
	}
	else
	{
		p_info->AddInteger(CRCD(0xedf5db70,"spin"),m_spin);
		write_combo_sets(p_info);
	}
}

void CEditGoal::read_skatetris_params(Script::CStruct *p_info)
{
	p_info->GetInteger(CRCD(0x1181b29,"acceleration_interval"),&m_acceleration_interval);
	p_info->GetFloat(CRCD(0x76f65c8,"acceleration_percent"),&m_acceleration_percent);
	p_info->GetInteger(CRCD(0xded8540a,"trick_time"),&m_trick_time);
	p_info->GetInteger(CRCD(0xa6d96603,"time_to_stop_adding_tricks"),&m_time_to_stop_adding_tricks);
	p_info->GetInteger(CRCD(0x89473db7,"max_tricks"),&m_max_tricks);
	p_info->GetInteger(CRCD(0xedf5db70,"spin"),&m_spin);
	
	read_combo_sets(p_info);
}

// The dest parameter determines the format in which the combo sets are written out.
// When generating params for the goal manager, the full goal_tetris_key_combos array is
// generated. When writing to mem card the chosen pre-defined set numbers are written instead.
void CEditGoal::WriteGoalSpecificParams(Script::CStruct *p_info, EWriteDest dest)
{
	switch (m_goal_type)
	{
		case 0xea7ba666: // TrickTris
			p_info->AddChecksum(NONAME,CRCD(0xea7ba666,"tricktris"));
			p_info->AddInteger(CRCD(0x45a375c9,"tricktris_block_size"),m_tricktris_block_size);
			p_info->AddInteger(CRCD(0xbfd3a831,"tricktris_total_to_win"),m_tricktris_total_to_win);
			// Needed otherwise the goal will fail straight away for block size 13, total to win 12 (TT5933)
			m_max_tricks=m_tricktris_block_size;
			write_skatetris_params(p_info,dest);
			break;
			
		case 0x0fc6f698: // ComboSkateTris
			p_info->AddChecksum(NONAME,CRCD(0x4ec3cfb5,"combo"));
			p_info->AddInteger(CRCD(0x67dd90ca,"combo_size"),m_combo_size);
			if (m_single_combo)
			{
				p_info->AddChecksum(NONAME,CRCD(0xdf47b144,"single_combo"));
				
				// Needed otherwise the goal will fail straight away for a single combo size of 12 (TT7652)
				m_max_tricks=m_combo_size;
			}
			write_skatetris_params(p_info,dest);
			break;	
			
		case 0x7fe2238a: // SkateTris
			write_skatetris_params(p_info,dest);
			break;
			
		case 0x6fe44c6d: // HighScore
		case 0xec414b76: // HighCombo
		{
			p_info->AddInteger(CRCD(0xcd66c8ae,"score"),m_score);
			if (dest==WRITING_TO_GOAL_MANAGER)
			{
				const char *p_text=NULL;
				if (m_goal_type==CRCD(0xec414b76,"HighCombo"))
				{
					p_text=Script::GetString(CRCD(0x3693bfe4,"edited_high_combo_goal_text"));
				}
				else
				{
					p_text=Script::GetString(CRCD(0xd6efb1b5,"edited_high_score_goal_text"));
				}	
				char p_temp[100];
				sprintf(p_temp,p_text,m_score);
				p_info->AddString(CRCD(0xda441d9a,"goal_text"),p_temp);
			}
			break;
		}

		case 0x61c5d092: // Gap
		{
			p_info->AddString(CRCD(0x730844a3,"required_trick_name"),mp_trick_name);
			
			if (dest==WRITING_TO_GOAL_MANAGER)
			{
				Script::CArray *p_goal_flags=new Script::CArray;
				p_goal_flags->SetSizeAndType(m_num_gaps,ESYMBOLTYPE_NAME);
				char p_foo[20];
				for (int i=0; iSetChecksum(i,Script::GenerateCRC(p_foo));
				}	
				p_info->AddArrayPointer(CRCD(0xcc3c4cc4,"goal_flags"),p_goal_flags);

				
				Script::CArray *p_required_gaps=new Script::CArray;
				p_required_gaps->SetSizeAndType(m_num_gaps,ESYMBOLTYPE_INTEGER);
				
				for (int i=0; iSetInteger(i,mp_gap_numbers[i]);
				}	
				p_info->AddArrayPointer(CRCD(0x52d4489e,"required_gaps"),p_required_gaps);
			}
			else
			{
				if (m_num_gaps)
				{
					Script::CArray *p_gaps=new Script::CArray;
					p_gaps->SetSizeAndType(m_num_gaps,ESYMBOLTYPE_INTEGER);
					for (int i=0; iSetInteger(i,mp_gap_numbers[i]);
					}	
					p_info->AddArrayPointer(CRCD(0xd76c173e,"Gaps"),p_gaps);
				}	
			}
			break;
		}
			
		default:
			break;
	}			
}

void CEditGoal::ReadGoalSpecificParams(Script::CStruct *p_params)
{
	switch (m_goal_type)
	{
		case 0xea7ba666: // TrickTris
			p_params->GetInteger(CRCD(0x45a375c9,"tricktris_block_size"),&m_tricktris_block_size);
			p_params->GetInteger(CRCD(0xbfd3a831,"tricktris_total_to_win"),&m_tricktris_total_to_win);
			read_skatetris_params(p_params);
			break;
			
		case 0x0fc6f698: // ComboSkateTris
			p_params->GetInteger(CRCD(0x67dd90ca,"combo_size"),&m_combo_size);
			
			// Note: This only sets the m_single_combo if the flag single_combo is specified,
			// if single_combo is not specified it will leave m_single_combo the way it was.
			// Need this otherwise m_single_combo will get switched off if ReadGoalSpecificParams
			// is used to just set combo_size.
			if (p_params->ContainsFlag(CRCD(0xdf47b144,"single_combo")))
			{
				m_single_combo=true;
			}	
			
			read_skatetris_params(p_params);
			break;
			
		case 0x7fe2238a: // SkateTris
			read_skatetris_params(p_params);
			break;
			
		case 0x6fe44c6d: // HighScore
		case 0xec414b76: // HighCombo
		{
			p_params->GetInteger(CRCD(0xcd66c8ae,"score"),&m_score);
			break;
		}
			
		case 0x61c5d092: // Gap
		{
			const char *p_trick_name=NULL;
			if (p_params->GetString(CRCD(0x730844a3,"required_trick_name"),&p_trick_name))
			{
				Dbg_MsgAssert(strlen(p_trick_name)<=MAX_GAP_TRICK_NAME_CHARS,("trick_name '%s' too long",p_trick_name));
				strcpy(mp_trick_name,p_trick_name);
			}
			
			Script::CArray *p_gaps=NULL;
			p_params->GetArray(CRCD(0xd76c173e,"Gaps"),&p_gaps);
			if (p_gaps)
			{
				Dbg_MsgAssert(p_gaps->GetSize()<=MAX_GAPS,("Gaps array too big, max is %d",MAX_GAPS));
				m_num_gaps=p_gaps->GetSize();
				for (int i=0; iGetInteger(i);
				}
			}		
			break;
		}
			
		default:
			break;
	}			
}

void CEditGoal::RemoveGoalSpecificFlag(Script::CStruct *p_params)
{
	uint32 flag=0;
	p_params->GetChecksum(NONAME,&flag);
	
	switch (m_goal_type)
	{
		case 0x0fc6f698: // ComboSkateTris
			if (flag==CRCD(0xdf47b144,"single_combo"))
			{
				m_single_combo=false;
			}	
			break;
			
		default:
			break;
	}			
}
	
void CEditGoal::EditGoal()
{
//	Dbg_MsgAssert(m_num_positions_set == m_num_positions_required,("Tried to edit a goal that was not fully defined originally"));
	// Note: The index starts at 1, because the cursor will initially be at index 0, and the cursor object is
	// not a marker object.
	for (int index=1; index= m_num_positions_set)
	{
		m_num_positions_set=m_current_position_index;
		if (m_num_positions_set == m_num_positions_required)
		{
			m_current_position_index=m_num_positions_required-1;
			return true;
		}	
		
		// Make the new position default to the last position
		mp_item_positions[m_current_position_index].mPos=pos;
		mp_item_positions[m_current_position_index].mHeight=height;
		mp_item_positions[m_current_position_index].mAngle=angle;
		
		// When moving on to the first letter of the skate or combo line, add in a tweak to 
		// the height so that the letter does not appear initially half way through the ground.
		if ((m_goal_type==CRCD(0x4ec3cfb5,"Combo") || m_goal_type==CRCD(0x54166acd,"Skate")) &&  
			 m_current_position_index == 2 )
		{
			mp_item_positions[m_current_position_index].mHeight+=Script::GetFloat(CRCD(0x82a694e,"GoalEditor_LetterHeight"));
		}
	}	
		
	return true;
}

bool CEditGoal::GetPosition(Mth::Vector *p_pos, float *p_height, float *p_angle)
{
	*p_pos=mp_item_positions[m_current_position_index].mPos;
	*p_height=mp_item_positions[m_current_position_index].mHeight;
	*p_angle=mp_item_positions[m_current_position_index].mAngle;
	return true;
}

void CEditGoal::GetPosition(Mth::Vector *p_pos, int index)
{
	Dbg_MsgAssert(m_num_positions_set > index,("Called GetPedPosition with index=%d but m_num_positions_set=%d",index,m_num_positions_set));
	*p_pos=mp_item_positions[index].mPos;
	(*p_pos)[Y]+=mp_item_positions[index].mHeight;
}

CGoalPos::EType CEditGoal::GetPositionType()
{
	if (m_num_positions_set)
	{
		return mp_item_positions[m_current_position_index].mType;
	}
	return CGoalPos::PED;
}

bool CEditGoal::BackUp(const Mth::Vector& pos, float height, float angle)
{
	// Can't back up if at the first position
	if (m_current_position_index == 0)
	{
		return false;
	}

	if (!position_ok_to_place(pos,height))
	{
		return false;
	}
		
	// First, set the position as though X had been pressed, so that a marker is dropped at the current
	// position which can be got back to by pressing X again.
	Dbg_MsgAssert(m_current_position_indexAddChecksum(NONAME,CRCD(0x7f1538e0,"PedPosition"));
		Script::RunScript(CRCD(0xd066a889,"create_cag_helper_text"), p_params);
		delete p_params;
	}	

	return true;	
}

uint CEditGoal::GetCurrentLetterIndex()
{
	uint index=0;
	
	switch (m_goal_type)
	{
		case 0x4ec3cfb5: // Combo
		case 0x54166acd: // Skate
		{
			Dbg_MsgAssert(m_current_position_index>=2,("Current position is not that of a letter"));
			index=m_current_position_index-2;
			break;
		}
		default:
			Dbg_MsgAssert(0,("Called GetCurrentLetterIndex() when goal type is %s\n",Script::FindChecksumName(m_goal_type)));
			break;
	}
	
	return index;
}

void CEditGoal::SetType(uint32 type)
{
	m_goal_type=type;
	
	mp_item_positions[0].mType=CGoalPos::PED;
	mp_item_positions[0].mHeight=0.0f;
	mp_item_positions[1].mType=CGoalPos::RESTART;
	mp_item_positions[1].mHeight=0.0f;
	
	const Script::CStruct *p_default_params=s_get_default_goal_params(m_goal_type);
	
	switch (m_goal_type)
	{
		case 0x54166acd: // Skate
		{
			m_num_positions_required=7;
			
			for (int i=2; iGetInteger(CRCD(0xc51bf6e3,"time_limit"),&m_time_limit);
		p_default_params->GetInteger(CRCD(0xcd66c8ae,"score"),&m_score);
		
		const char *p_string="";
		p_default_params->GetString(CRCD(0x4bc5229d,"view_goals_text"),&p_string);
		Dbg_MsgAssert(strlen(p_string) m_time_limit)
			{
				m_time_to_stop_adding_tricks=m_time_limit;
			}
			break;
			
		default:
			break;
	}		
}

void CEditGoal::SetControlType(uint32 controlType)
{
	m_control_type=controlType;
}

void CEditGoal::AddGoalToGoalManager(bool markUnbeaten)
{
	Script::CStruct *p_params=new Script::CStruct;
	add_goal_params(p_params);
	
	Game::CGoalManager* p_goal_manager = Game::GetGoalManager();
	Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager ?"));
	p_goal_manager->AddGoal(GetId(),p_params);

	Game::CGoal *p_goal=p_goal_manager->GetGoal(GetId());
	// Mark it as seen so that it is selectable in the view-goals menu
	p_goal->SetHasSeen();		

	if (markUnbeaten)
	{
		m_has_won_goal=false;
	}
		
	if (m_has_won_goal)
	{
		p_goal->MarkBeaten();
	}	
	
	delete p_params;
}

void CEditGoal::write_position_into_node(uint index, uint32 nodeName)
{
	Dbg_MsgAssert(indexAddVector(CRCD(0x7f261953,"Pos"),	mp_item_positions[index].mPos.GetX(),
												mp_item_positions[index].mPos.GetY()+mp_item_positions[index].mHeight,
												mp_item_positions[index].mPos.GetZ());
	p_node->AddVector(CRCD(0x9d2d0915,"Angles"),0,
												mp_item_positions[index].mAngle-3.141592654f,
												0);
												
	// For letter nodes, the type and model filename need to be added.
	if (index >=2 )
	{
		p_node->AddChecksum(CRCD(0x7321a8d6,"Type"),s_generate_letter_node_type_checksum(m_goal_type,index-2));
		p_node->AddString(CRCD(0x286a8d26,"Model"),s_generate_letter_model_filename(m_goal_type,index-2));
	}	
}

void CEditGoal::WriteNodePositions()
{
	for (int i=0; i 1)
	{
		Dbg_MsgAssert(mp_item_positions[1].mType==CGoalPos::RESTART,("Strange type for goal position 1"));
		float x=mp_item_positions[1].mPos.GetX();
		float z=mp_item_positions[1].mPos.GetZ();
		
		if (x < x0 || x > x1 || z < z0 || z > z1)
		{
			return true;
		}
	}		
	return false;
}

void CEditGoal::GetDebugInfo(Script::CStruct* p_info)
{
#ifdef	__DEBUG_CODE__
	WriteIntoStructure(p_info);
#endif				 
}

#ifdef SIZE_TEST	
void CEditGoal::FillNameBuffers()
{
	int i;
	for (i=0; i( new CGoalEditorComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CGoalEditorComponent::CGoalEditorComponent() : CBaseComponent()
{
	SetType( CRC_GOALEDITOR );
	
	ClearAllExceptParkGoals();
	ClearOnlyParkGoals();
	mp_input_component=NULL;
	mp_editor_camera_component=NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CGoalEditorComponent::~CGoalEditorComponent()
{
	Script::RunScript("goal_editor_destroy_cursor");
}

// For writing to memcard
void CGoalEditorComponent::WriteIntoStructure(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info"));
		
	int goal_count=0;
	for (int i=0; iSetSizeAndType(goal_count,ESYMBOLTYPE_STRUCTURE);
	int goal_index=0;
	for (int i=0; iSetStructure(goal_index++,p_goal_info);
		}	
	}
	Dbg_MsgAssert(goal_index==goal_count,("Bad goal_index ?"));
	p_info->AddArrayPointer(CRCD(0x38dbe1d0,"Goals"),p_goals_array);
}

// This just writes in the goals for a particular level. Used when sending goal info
// across the network for network goal attack games.
void CGoalEditorComponent::WriteIntoStructure(uint32 levelScript, Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info"));
	
	int num_goals=CountGoalsForLevel(levelScript);
			
	Script::CArray *p_goals_array=new Script::CArray;
	p_goals_array->SetSizeAndType(num_goals,ESYMBOLTYPE_STRUCTURE);
	int goal_index=0;
	for (int i=0; iSetStructure(goal_index++,p_goal_info);
		}	
	}
	Dbg_MsgAssert(goal_index==num_goals,("Bad num_goals ?"));
	
	p_info->AddArrayPointer(CRCD(0x38dbe1d0,"Goals"),p_goals_array);
}

// Writes out the goals for a particular level into a uint8* buffer.
// Used when sending goal info across the network for network goal attack games.
uint32 CGoalEditorComponent::WriteToBuffer(uint32 levelScript, uint8 *p_buffer, uint32 bufferSize)
{
	Dbg_MsgAssert(p_buffer,("NULL p_buffer"));
	
	// Warning! It's feasible that we could run out of memory here ... 
	// The structure could contain around 6K of stuff.	
	Script::CStruct *p_struct=new Script::CStruct;
	WriteIntoStructure( s_convert_level( levelScript ), p_struct);
	uint32 bytes_written=Script::WriteToBuffer(p_struct, p_buffer, bufferSize);
	delete p_struct;
	
	return bytes_written;
}

uint8 *CGoalEditorComponent::ReadFromBuffer(uint32 levelScript, uint8 *p_buffer)
{
	Dbg_MsgAssert(p_buffer,("NULL p_buffer"));

	levelScript=s_convert_level(levelScript);
	
	Script::CStruct *p_struct=new Script::CStruct;
	uint8 *p_end=Script::ReadFromBuffer(p_struct, p_buffer);
	
	// Run through all the goals clearing any that exist for this level.
	for (int i=0; iGetArray(CRCD(0x38dbe1d0,"Goals"),&p_goals_array,Script::ASSERT);

	uint32 array_index=0;
	int num_goals_left_to_load=p_goals_array->GetSize();
	for (int i=0; iGetStructure(array_index++));
			mp_goals[i].SetLevel(levelScript);
			--num_goals_left_to_load;
		}
	}
	Dbg_MsgAssert(num_goals_left_to_load == 0,("Could not load all the goals"));
	
	delete p_struct;
	return p_end;
}

// For reading from memcard
void CGoalEditorComponent::ReadFromStructure(Script::CStruct *p_info, EBoolLoadingParkGoals loadingParkGoals)
{
	Dbg_MsgAssert(p_info,("NULL p_info"));

	if (loadingParkGoals)
	{
		ClearOnlyParkGoals();
	}
	else
	{
		ClearAllExceptParkGoals();
	}
	
	Script::CArray *p_goals_array=NULL;
	p_info->GetArray(CRCD(0x38dbe1d0,"Goals"),&p_goals_array);
	if (!p_goals_array)
	{
		// No Goals array means no goals.
		return;
	}
	
	uint32 array_size=p_goals_array->GetSize();

	for (uint32 array_index=0; array_indexGetStructure(array_index);
		if ( (loadingParkGoals && s_get_level_checksum(p_struct) == CRCD(0xe8b4b836,"Load_Sk5Ed")) ||
			 (!loadingParkGoals && s_get_level_checksum(p_struct) != CRCD(0xe8b4b836,"Load_Sk5Ed")))
		{
			for (int i=0; iAddInteger(CRCD(0x67e6859a,"player"),0);
		mp_input_component->InitFromStructure(p_params);
		delete p_params;
		
		refresh_cursor_object();
	}
}

Obj::CCompositeObject *CGoalEditorComponent::get_cursor()
{
	return (Obj::CCompositeObject*)Obj::CTracker::Instance()->GetObject(CRCD(0x9211b125,"GoalEditorCursor"));
}

void CGoalEditorComponent::refresh_cursor_object()
{
	const char *p_model_filename="";

	Dbg_MsgAssert(mp_current_goal,("NULL mp_current_goal"));
	switch (mp_current_goal->GetPositionType())
	{
		case CGoalPos::RESTART:
			Dbg_MsgAssert(mp_current_goal,("NULL mp_current_goal?"));
			p_model_filename="gameobjects\\p1_cursor\\p1_cursor.mdl";
			break;
		case CGoalPos::LETTER:
			Dbg_MsgAssert(mp_current_goal,("NULL mp_current_goal?"));
			p_model_filename=s_generate_letter_model_filename(mp_current_goal->GetType(),
															  mp_current_goal->GetCurrentLetterIndex());
			break;
		default:
			p_model_filename="gameobjects\\goal_cursor\\goal_cursor.mdl";
			break;
	}
	
	Script::CStruct params;
	if (mp_current_goal->GetPositionType()==CGoalPos::PED)
	{
		params.AddChecksum(CRCD(0x7321a8d6,"Type"),CRCD(0x61a741e,"Ped"));
	}
	else
	{
		params.AddChecksum(CRCD(0x7321a8d6,"Type"),0);
		params.AddString(CRCD(0x286a8d26,"Model"),p_model_filename);
	}
	
	Script::RunScript(CRCD(0x36791e3c,"goal_editor_create_cursor"),¶ms,GetObject());
	
	// Choose whether the cursor should be allowed to be moved over kill polys.
	// Only the restart position is not allowed to, to allow letters to be placed over kill polys.
	update_cursor_collision_type();
}

void CGoalEditorComponent::get_pos_from_camera_component(Mth::Vector *p_pos, float *p_height, float *p_angle)
{
	Dbg_MsgAssert(mp_editor_camera_component,("NULL mp_editor_camera_component ?"));
	*p_pos=mp_editor_camera_component->GetCursorPos();
	*p_height=mp_editor_camera_component->GetCursorHeight();
	*p_angle=mp_editor_camera_component->GetCursorOrientation();
}

void CGoalEditorComponent::update_cursor_collision_type()
{
	Dbg_MsgAssert(mp_editor_camera_component,("NULL mp_editor_camera_component ?"));
	
	// Restart positions are not allowed to go over kill polys, otherwise the skater will
	// die straight away.
	// Note that the ped position also has to not be allowed to go over kill polys, otherwise
	// as soon as it is placed the restart cursor will be over the kill poly, and hence be stuck.
	if (mp_current_goal->GetPositionType()==CGoalPos::PED ||
		mp_current_goal->GetPositionType()==CGoalPos::RESTART)
	{
		Mdl::Skate * p_skate_mod =  Mdl::Skate::Instance();
		if (p_skate_mod->m_cur_level == CRCD(0xe8b4b836,"load_sk5ed") )
		{
			// In the park editor, allow the cursor to move anywhere, otherwise the cursor
			// will get stuck if initialised on a kill poly.
			mp_editor_camera_component->SetSimpleCollision(true);
		}
		else
		{
			mp_editor_camera_component->SetSimpleCollision(false);
		}	
	}
	else
	{
		mp_editor_camera_component->SetSimpleCollision(true);
	}
}

CEditGoal *CGoalEditorComponent::find_goal(const char *p_name)
{
	uint32 current_level=s_get_current_level();

	Dbg_MsgAssert(p_name,("NULL p_name ?"));
	for (int i=0; iGetChecksum(CRCD(0x9982e501,"goal_id"),&id))
	{
		return find_goal(id);
	}
	
	const char *p_name=NULL;
	if (p_params->GetString(CRCD(0xbfecc45b,"goal_name"),&p_name))
	{
		return find_goal(p_name);
	}
	
	int index=0;
	if (p_params->GetInteger(CRCD(0x474a6a7f,"goal_index"),&index))
	{
		Dbg_MsgAssert(0,("find_goal called for an index"));
		
		uint32 current_level=s_get_current_level();
		
		for (int i=0; iGetPosition(&pos,&height,&angle);
	
	mp_editor_camera_component->SetCursorHeight(height);
	mp_editor_camera_component->SetCursorOrientation(angle);
	mp_editor_camera_component->SetCursorPos(pos);
}

void CGoalEditorComponent::update_cursor_position()
{
	Script::CStruct params;
	
	Mth::Vector cursor_pos(0.0f,0.0f,0.0f);		
	float cursor_height=0.0f;
	float cursor_angle=0.0f;
	get_pos_from_camera_component(&cursor_pos,&cursor_height,&cursor_angle);
	cursor_pos[Y]+=cursor_height;
	
	params.AddVector(CRCD(0x7f261953,"Pos"),cursor_pos[X],cursor_pos[Y],cursor_pos[Z]);
	params.AddFloat(CRCD(0xff7ebaf6,"Angle"),mp_editor_camera_component->GetCursorOrientation());
	
	Script::RunScript(CRCD(0xdea7fe56,"goal_editor_update_cursor_position"),¶ms);
}

void CGoalEditorComponent::remove_goal(CEditGoal *p_goal)
{
	Dbg_MsgAssert(p_goal,("NULL p_goal"));
	
	if (p_goal->Used())
	{
		Game::CGoalManager* p_goal_manager = Game::GetGoalManager();
		Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager ?"));
		
		if (p_goal_manager->GoalExists(p_goal->GetId()))
		{
			p_goal_manager->RemoveGoal(p_goal->GetId());
		}	
	}	
	p_goal->Clear();
	
	if (mp_current_goal==p_goal)
	{
		mp_current_goal=NULL;
	}
	if (GetNumGoals()==0)
	{
		Dbg_MsgAssert(mp_current_goal==NULL,("mp_current_goal not NULL ?"));
	}	
}

bool CGoalEditorComponent::too_close_to_another_goal_position(const Mth::Vector &pos, float height, uint32 level, int positionIndex)
{
	Mth::Vector pos_with_height=pos;
	pos_with_height[Y]+=height;
	
	float default_min_dist_allowable=Script::GetFloat(CRCD(0x71b20276,"GoalEditor_DefaultMinDistBetweenPositions"));
	
	for (int i=0; i positionIndex)
		{
			Mth::Vector p;
			mp_goals[i].GetPosition(&p, positionIndex);
			p-=pos_with_height;
			if (p.Length() < default_min_dist_allowable)
			{
				return true;
			}
		}
	}
			
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CGoalEditorComponent::Finalize()
{
	// Get the pointers to the other required components.
	
	Dbg_MsgAssert(mp_input_component==NULL,("mp_input_component not NULL ?"));
	mp_input_component = GetInputComponentFromObject(GetObject());
	Dbg_MsgAssert(mp_input_component,("CGoalEditorComponent requires parent object to have an input component!"));

	Dbg_MsgAssert(mp_editor_camera_component==NULL,("mp_editor_camera_component not NULL ?"));
	mp_editor_camera_component = GetEditorCameraComponentFromObject(GetObject());
	Dbg_MsgAssert(mp_editor_camera_component,("CGoalEditorComponent requires parent object to have an EditorCamera component!"));
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CGoalEditorComponent::Update()
{
	if (mp_current_goal)
	{
		CControlPad& control_pad = mp_input_component->GetControlPad();
		if (control_pad.m_x.GetTriggered())
		{
			control_pad.m_x.ClearTrigger();
	
			Mth::Vector cursor_pos(0.0f,0.0f,0.0f);
			float cursor_height=0.0f;
			float cursor_angle=0.0f;
			get_pos_from_camera_component(&cursor_pos,&cursor_height,&cursor_angle);
			
			if ((mp_current_goal->GetPositionType() == CGoalPos::RESTART && 
				too_close_to_another_goal_position(cursor_pos,cursor_height,mp_current_goal->GetLevel(),0)) || // 0 = ped
				(mp_current_goal->GetPositionType() == CGoalPos::PED && 
				too_close_to_another_goal_position(cursor_pos,cursor_height,mp_current_goal->GetLevel(),1))) // 1 = restart
			{
				// Can't place restart positions too close to another goal's ped position, or
				// ped positions too close to another goal's restart position.
				Script::RunScript(CRCD(0xaebc7020,"goal_editor_play_placement_fail_sound"),NULL,GetObject());
			}
			else
			{
				if (mp_current_goal->SetPosition(cursor_pos,cursor_height,cursor_angle))
				{
					Script::RunScript(CRCD(0x718b071b,"goal_editor_play_placement_success_sound"),NULL,GetObject());
					// Once a position is set, the cursor model will need to change for the next position.
					refresh_cursor_object();
					update_camera_pos();
				}
				else
				{
					Script::RunScript(CRCD(0xaebc7020,"goal_editor_play_placement_fail_sound"),NULL,GetObject());
				}
			}	
			
			if (mp_current_goal->PlacedLastPosition())
			{
				Script::RunScript(CRCD(0x89119628,"goal_editor_finished_placing_letters"),NULL,GetObject());
			}
		}	
	
		if (control_pad.m_triangle.GetTriggered())
		{
			control_pad.m_triangle.ClearTrigger();
	
			Mth::Vector cursor_pos(0.0f,0.0f,0.0f);
			float cursor_height=0.0f;
			float cursor_angle=0.0f;
			get_pos_from_camera_component(&cursor_pos,&cursor_height,&cursor_angle);
	
			Dbg_MsgAssert(mp_current_goal,("NULL mp_current_goal"));
			if ((mp_current_goal->GetPositionType() == CGoalPos::RESTART && 
				too_close_to_another_goal_position(cursor_pos,cursor_height,mp_current_goal->GetLevel(),0)) || // 0 = ped
				(mp_current_goal->GetPositionType() == CGoalPos::PED && 
				too_close_to_another_goal_position(cursor_pos,cursor_height,mp_current_goal->GetLevel(),1))) // 1 = restart
			{
				// Can't place restart positions too close to another goal's ped position, or
				// ped positions too close to another goal's restart position.
				Script::RunScript(CRCD(0xade100d6,"goal_editor_play_backup_fail_sound"),NULL,GetObject());
			}
			else
			{
				if (mp_current_goal->BackUp(cursor_pos,cursor_height,cursor_angle))
				{
					Script::RunScript(CRCD(0xfa275cc5,"goal_editor_play_backup_success_sound"),NULL,GetObject());
					refresh_cursor_object();
					update_camera_pos();
				}	
				else
				{
					Script::RunScript(CRCD(0xade100d6,"goal_editor_play_backup_fail_sound"),NULL,GetObject());
				}			
			}	
		}	
	}
	
	update_cursor_position();
}

void CGoalEditorComponent::ClearAllExceptParkGoals()
{
	for (uint i=0; iClear();
			mp_current_goal->SetUsedFlag(true);
		
			mp_current_goal->SetLevel(current_level);
			mp_current_goal->SetLevelGoalIndex(level_goal_index);
			
			refresh_cursor_object();
			return;
		}
	}
								
	Dbg_MsgAssert(0,("Exceeded CGoalEditorComponent::MAX_GOALS_TOTAL of %d",MAX_GOALS_TOTAL));
}

void CGoalEditorComponent::SetGoalType(uint32 type)
{
	Dbg_MsgAssert(mp_current_goal,("NULL mp_current_goal"));
	mp_current_goal->SetType(type);
}
	
bool CGoalEditorComponent::ThereAreGoalsOutsideArea(float x0, float z0, float x1, float z1)
{
	for (int i=0; iGetInteger(CRCD(0x3ac9ab5e,"gap_number"),&gap_number);
				p_goal->AddGap(gap_number);
			}
			break;
		}

		// @script | EditedGoalRemoveGap
		case 0x5c155a29: // EditedGoalRemoveGap
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				int gap_number=0;
				pParams->GetInteger(CRCD(0x3ac9ab5e,"gap_number"),&gap_number);
				p_goal->RemoveGap(gap_number);
			}
			break;
		}

		// @script | EditedGoalGotGap
		case 0xfb279560: // EditedGoalGotGap
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				int gap_number=0;
				pParams->GetInteger(CRCD(0x3ac9ab5e,"gap_number"),&gap_number);
				if (p_goal->GotGap(gap_number))
				{
					return CBaseComponent::MF_TRUE;
				}	
			}
			return CBaseComponent::MF_FALSE;
			break;
		}
		
		// @script | AddKeyComboSet
		case 0xcca11c78: // AddKeyComboSet
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				int set_index=0;
				pParams->GetInteger(CRCD(0xeb7d6498,"set_index"),&set_index);
				int spin=0;
				pParams->GetInteger(CRCD(0xedf5db70,"spin"),&spin);
				int num_taps=0;
				pParams->GetInteger(CRCD(0xa4bee6a1,"num_taps"),&num_taps);
				p_goal->AddKeyComboSet(set_index,
									   pParams->ContainsFlag(CRCD(0x687e2c25,"require_perfect")),
									   spin,
									   num_taps);
			}	
			break;
		}
		
		// @script | RemoveKeyComboSet
		case 0x1d30a7b6: // RemoveKeyComboSet
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				int set_index=0;
				pParams->GetInteger(CRCD(0xeb7d6498,"set_index"),&set_index);
				p_goal->RemoveKeyComboSet(set_index);
			}
			break;
		}

		// @script | GetKeyComboSet
		case 0xe5ed63b7: // GetKeyComboSet
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				int set_index=0;
				pParams->GetInteger(CRCD(0xeb7d6498,"set_index"),&set_index);
				
				SPreDefinedKeyComboSet *p_keycombo_set=p_goal->GetKeyComboSet(set_index);
				if (p_keycombo_set)
				{
					pScript->GetParams()->AddInteger(CRCD(0xedf5db70,"Spin"),p_keycombo_set->mSpin*180);
					pScript->GetParams()->AddInteger(CRCD(0xa4bee6a1,"num_taps"),p_keycombo_set->mNumTaps);
					if (p_keycombo_set->mRequirePerfect)
					{
						pScript->GetParams()->AddChecksum(NONAME,CRCD(0x687e2c25,"require_perfect"));
					}	
				}
				else
				{
					return CBaseComponent::MF_FALSE;
				}	
			}
			break;
		}
		
		// @script | RemoveGoalSpecificFlag
		case 0x3ebd2c9: // RemoveGoalSpecificFlag
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				p_goal->RemoveGoalSpecificFlag(pParams);
			}	
			break;
		}
		
		// @script | SetGoalSpecificParams
		case 0x47208c6d: // SetGoalSpecificParams
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				p_goal->ReadGoalSpecificParams(pParams);
			}	
			break;
		}

		// @script | FlagGoalAsWon
		case 0x769309e1: // FlagGoalAsWon
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				p_goal->FlagAsWon();
			}	
			break;
		}
		
		
		// @script | FindUnfinishedGoal
		case 0x88d54a10: // FindUnfinishedGoal
		{
			uint32 type=0;
			pParams->GetChecksum("type",&type);
			
			mp_current_goal=NULL;
			
			for (int i=0; iGetSkateGoalMaxSize());
			
			pScript->GetParams()->AddChecksum("goal_id",mp_current_goal->GetId());
			break;
		}	
		
		// @script | RemovedCreatedGoal |
		case 0x1f80b5ae: // RemovedCreatedGoal
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				remove_goal(p_goal);
			}	
			break;
		}

		// @script | GoalHasAllPositionsSet |
		case 0x26848221: // GoalHasAllPositionsSet
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				if (!p_goal->GotAllPositions())
				{
					return CBaseComponent::MF_FALSE;
				}	
			}	
			else
			{
				return CBaseComponent::MF_FALSE;
			}
			break;
		}
			
		// @script | SetCurrentEditorGoal |
		case 0xfdb2c514: // SetCurrentEditorGoal
		{
			mp_current_goal=find_goal(pParams);
			break;
		}
			
        // @script | SetEditorGoalType | 
		case 0x98abbd09: // SetEditorGoalType
		{
			uint32 type=0;
			pParams->GetChecksum("type",&type);
			
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				p_goal->SetType(type);
			}	
			break;
		}

        // @script | SetEditorGoalDescription | 
		case 0x66a33157: // SetEditorGoalDescription
		{
			const char *p_text="";
			pParams->GetString("text",&p_text);
			
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				p_goal->SetGoalDescription(p_text);
			}	
			break;
		}

        // @script | SetEditorGoalWinMessage | 
		case 0x7aabbc09: // SetEditorGoalWinMessage
		{
			const char *p_text="";
			pParams->GetString("text",&p_text);
			
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				p_goal->SetWinMessage(p_text);
			}	
			break;
		}

		// @script | GetCurrentEditedGoalInfo
		case 0x69b872e3: // GetCurrentEditedGoalInfo
		{
			if (mp_current_goal)
			{
				mp_current_goal->WriteIntoStructure(pScript->GetParams());
			}	
			break;
		}
			
		// @script | GetEditedGoalsInfo
		// @parmopt name | Level | If specified, it will only include goals for this level.
		// The level is specified using the name of its load script.
		case 0x96f11bd3: // GetEditedGoalsInfo
		{
			uint32 level=s_get_level_checksum(pParams);
			
			int n=CountGoalsForLevel(level);
			if (n==0)
			{
				break;
			}
				
			Script::CArray *p_info_array=new Script::CArray;
			p_info_array->SetSizeAndType(n, ESYMBOLTYPE_STRUCTURE);
			int index=0;
			
			for (int i=0; iSetStructure(index++,p_info);
				}	
			}
			Dbg_MsgAssert(index==n,("Eh?"));
			
			pScript->GetParams()->AddArrayPointer("EditedGoalsInfo",p_info_array);
			break;
		}
			
		// @script | GetNumEditedGoals	
		// @parmopt name | Level | If specified, it will only count goals for this level.
		// The level is specified using the name of its load script.
		case 0x96874358: // GetNumEditedGoals
		{
			uint32 level=s_get_level_checksum(pParams);
			
			int n=CountGoalsForLevel(level);
			if (level==0 && pParams->ContainsFlag(CRCD(0xb99f6330,"ExcludeParkEditorGoals")))
			{
				n-=CountGoalsForLevel(CRCD(0xe8b4b836,"Load_Sk5Ed"));
				Dbg_MsgAssert(n>=0,("More editor goals (%d) than total goals? (%d)",CountGoalsForLevel(CRCD(0xe8b4b836,"Load_Sk5Ed")),CountGoalsForLevel(0)));
			}
			pScript->GetParams()->AddInteger("NumGoals",n);
			break;
		}

		// @script | GetCurrentEditedGoalId
		case 0x5bd8050a: // GetCurrentEditedGoalId
		{
			pScript->GetParams()->RemoveComponent("goal_id");
			if (mp_current_goal)
			{
				pScript->GetParams()->AddChecksum("goal_id",mp_current_goal->GetId());
			}
			break;
		}

		// @script | WriteEditedGoalNodePositions
		case 0x5408ac04: // WriteEditedGoalNodePositions
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				p_goal->WriteNodePositions();
			}	
			break;
		}
		
		// @script | AddEditedGoalToGoalManager
		case 0xb525b113: // AddEditedGoalToGoalManager
		{
			CEditGoal *p_goal=find_goal(pParams);
			if (p_goal)
			{
				p_goal->AddGoalToGoalManager(pParams->ContainsFlag(CRCD(0xc6322a25,"MarkUnbeaten")));
			}	
			break;
		}

		// @script | SetEditorGoalName |
		case 0x4a569426: // SetEditorGoalName
		{
			const char *p_name="";
			if (mp_current_goal && pParams->GetString("Name",&p_name))
			{
				mp_current_goal=find_goal(pParams);
				mp_current_goal->SetGoalName(p_name);
			}	
			break;
		}

		// @script | SetEditorPedName |
		case 0x410821df: // SetEditorPedName
		{
			const char *p_name="";
			if (mp_current_goal && pParams->GetString("Name",&p_name))
			{
				mp_current_goal=find_goal(pParams);
				mp_current_goal->SetPedName(p_name);
			}	
			break;
		}

		// @script | MaxEditedGoalsReached | Returns true if there is no space for any more
		// goals, or if the max goals per level has been reached for this level.
		// @parmopt name | Level | The level, specified using its load script name.
		case 0x60288abe: // MaxEditedGoalsReached
		{
			if (GetNumGoals()==MAX_GOALS_TOTAL)
			{
				// Definitely reached the max if got MAX_GOALS_TOTAL
				return CBaseComponent::MF_TRUE;
			}
			
			// Otherwise, count up how many goals exist for this level, 
			// and compare with MAX_GOALS_PER_LEVEL
			uint32 level=s_get_level_checksum(pParams);
			Dbg_MsgAssert(level,("\n%s\nNo Level specified",pScript->GetScriptInfo()));
			
			if (CountGoalsForLevel(level) >= MAX_GOALS_PER_LEVEL)
			{
				return CBaseComponent::MF_TRUE;
			}
				
			return CBaseComponent::MF_FALSE;
			break;
		}

		// @script | EditGoal |
		case 0x831eca10: // EditGoal
			mp_current_goal=find_goal(pParams);
			if (mp_current_goal)
			{
				mp_current_goal->EditGoal();
				update_camera_pos();
				refresh_cursor_object();
			}
			break;

		// @script | NukeAllGoals	
		case 0xdba8e3fc: // NukeAllGoals
		{
			if (pParams->ContainsFlag(CRCD(0x98439808,"OnlyParkEditorGoals")))
			{
				ClearOnlyParkGoals();
			}
			else
			{
				ClearAllExceptParkGoals();
			}	
			break;
		}
			
		// @script | GetMaxGoalsPerLevel		
		case 0xd7258335: // GetMaxGoalsPerLevel
		{
			pScript->GetParams()->AddInteger(CRCD(0x8bc2e0db,"max_goals"),MAX_GOALS_PER_LEVEL);
			break;
		}

		case 0x9e5a634a: // SetGoalScore
		{
			mp_current_goal=find_goal(pParams);
			if (mp_current_goal)
			{
				int score=0;
				if (pParams->GetInteger(NONAME,&score))
				{
					mp_current_goal->SetScore(score);
				}	
			}
			break;
		}

		// @script | SetGoalTimeLimit
		case 0x7d5073f6: // SetGoalTimeLimit
		{
			mp_current_goal=find_goal(pParams);
			if (mp_current_goal)
			{
				int time=120;
				if (pParams->GetInteger(NONAME,&time))
				{
					mp_current_goal->SetTimeLimit(time);
				}	
			}
			break;
		}

		// @script | SetGoalControlType
		case 0xec316a9c: // SetGoalControlType
		{
			mp_current_goal=find_goal(pParams);
			if (mp_current_goal)
			{
				uint32 control_type=CRCD(0x54166acd,"skate");
				if (pParams->GetChecksum(NONAME,&control_type))
				{
					mp_current_goal->SetControlType(control_type);
				}	
			}
			break;
		}

		// @script | RefreshGoalCursorPosition
		case 0x20b566b1: // RefreshGoalCursorPosition
		{
			if (mp_current_goal)
			{
				Mth::Vector cursor_pos(0.0f,0.0f,0.0f);		
				float angle=0.0f;
				float height=0.0f;
				mp_current_goal->GetPosition(&cursor_pos,&height,&angle);
				
				Script::CStruct params;
				params.AddVector(CRCD(0x7f261953,"Pos"),cursor_pos[X],cursor_pos[Y]+height,cursor_pos[Z]);
				params.AddFloat(CRCD(0xff7ebaf6,"Angle"),angle);
				Script::RunScript(CRCD(0xdea7fe56,"goal_editor_update_cursor_position"),¶ms);
			}	
			break;
		}
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalEditorComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CGoalEditorComponent::GetDebugInfo"));

	int num_goals=GetNumGoals();
	p_info->AddInteger("NumGoals",num_goals);
	if (mp_current_goal)
	{
		Script::CStruct *p_struct=new Script::CStruct;
		mp_current_goal->GetDebugInfo(p_struct);
		p_info->AddStructurePointer("CurrentGoal",p_struct);
	}
	
	if (num_goals)
	{
		Script::CArray *p_array=new Script::CArray;
		p_array->SetSizeAndType(num_goals,ESYMBOLTYPE_STRUCTURE);
		int index=0;
		for (int i=0; iSetStructure(index++,p_struct);
			}	
		}
		Dbg_MsgAssert(index==num_goals,("Bad num_goals ?"));
		p_info->AddArrayPointer("Goals",p_array);
	}
		
// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

Obj::CGoalEditorComponent *GetGoalEditor()
{
	Obj::CCompositeObject *p_obj=(Obj::CCompositeObject*)Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0x81f01058,"GoalEditor"));
	Dbg_MsgAssert(p_obj,("No GoalEditor object"));
	Obj::CGoalEditorComponent *p_goal_editor=GetGoalEditorComponentFromObject(p_obj);
	Dbg_MsgAssert(p_goal_editor,("No goal editor component ???"));
	
	return p_goal_editor;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
}


================================================
FILE: Code/Sk/Components/GoalEditorComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       GoalEditorComponent.h
//* OWNER:          Kendall Harrison
//* CREATION DATE:  3/21/2003
//****************************************************************************

#ifndef __COMPONENTS_GOALEDITORCOMPONENT_H__
#define __COMPONENTS_GOALEDITORCOMPONENT_H__

#include 
#include 

#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_GOALEDITOR CRCD(0x81f01058,"GoalEditor")

//  Standard accessor macros for getting the component either from within an object, or 
//  given an object				 
#define		GetGoalEditorComponent() ((Obj::CGoalEditorComponent*)GetComponent(CRC_GOALEDITOR))
#define		GetGoalEditorComponentFromObject(pObj) ((Obj::CGoalEditorComponent*)(pObj)->GetComponent(CRC_GOALEDITOR))

namespace Script
{
    class CScript;
    class CStruct;
}

namespace Nx
{
	class CModel;
}

namespace Obj
{
enum
{
	// The number of nodes used by a goal
	MAX_POSITIONS_PER_GOAL=8,
	// Limited to 20 because a ped will need to be created for each goal
	// Note: If this value is changed, it would be a good idea to update VERSION_CREATEDGOALS
	// in memcard.q too, to prevent loading old mem card saves that may have more goals than
	// the new value allows.
	MAX_GOALS_PER_LEVEL=10,
	
	// Note: MAX_POSITIONS_PER_GOAL*MAX_GOALS_PER_LEVEL nodes will get added to the NodeArray
	
	// The total number of goals stored in the GoalEditor composite object.
	MAX_GOALS_TOTAL=50,
};

class CInputComponent;
class CEditorCameraComponent;

class CGoalPos
{
public:
	enum EType
	{
		NONE,
		PED,
		RESTART,
		LETTER,
	};
	
	EType mType;
	Mth::Vector mPos;
	float mHeight;
	float mAngle;
};	

struct SPreDefinedKeyComboSet
{
	uint8 mSetIndex; // Index into the cag_skatetris_key_combos array defined in goal_editor.q
	
	// Note: These three are not really used at the moment. They used to be settable from menus,
	// but are now all left as zero for simplicity, with only a global spin value settable for the
	// whole goal.
	// So if it stays this way, there is no need for this structure, could just have a simple array
	// of set indices instead.
	uint8 mRequirePerfect;
	uint8 mSpin; // Actually the spin/180
	uint8 mNumTaps;
};
	
class CEditGoal
{
	// Identifies which level this goal is for via the level load script name.
	uint32 m_level_script;
	
	enum
	{
		// These needs to be at least 1 bigger than the max_length value passed to
		// the create_onscreen_keyboard script.
		GOAL_NAME_BUFFER_SIZE=50,
		PED_NAME_BUFFER_SIZE=21,
		GOAL_DESCRIPTION_BUFFER_SIZE=100,
		GOAL_WIN_MESSAGE_BUFFER_SIZE=100,
	};
	char mp_goal_name[GOAL_NAME_BUFFER_SIZE];
	char mp_ped_name[PED_NAME_BUFFER_SIZE];
	char mp_goal_description[GOAL_DESCRIPTION_BUFFER_SIZE];
	char mp_win_message[GOAL_WIN_MESSAGE_BUFFER_SIZE];


	uint32 m_goal_type;
	
	CGoalPos mp_item_positions[MAX_POSITIONS_PER_GOAL];
	uint8 m_num_positions_required;
	uint8 m_num_positions_set;
	uint8 m_current_position_index;
	uint8 m_placed_last_position;
	
	uint8 m_has_won_goal;
	
	uint8 m_used; // Indicates whether this slot in the array of CEditGoals is used or not.
	
	// m_level_goal_index has a value from 0 to MAX_GOALS_PER_LEVEL-1
	// From the level goal index is also derived the goal's id string
	// which the goal manager needs when creating the goal ped arrows
	// and the goal id, which is just the checksum of the id string. 
	int m_level_goal_index;
	
	int m_time_limit;
	
	// One of skate, walk, junkercar or rallycar
	uint32 m_control_type;
	
	// The following are goal specific params.
	// Could have stored these in a CStruct instead, but that would have caused varying
	// memory requirements depending on how many goals the user had added, making it harder to test.
	
	// The score value for high-score, high-combo etc.
	int m_score;

	// Skatetris specific
	int m_acceleration_interval;
	float m_acceleration_percent;
	int m_trick_time;
	int m_time_to_stop_adding_tricks;
	int m_max_tricks;
	
	int m_spin;

	// Combo skatetris specific
	bool m_single_combo;
	int m_combo_size;

	// Tricktris specific
	int m_tricktris_block_size;
	int m_tricktris_total_to_win;
	
	enum
	{
		MAX_COMBO_SETS=10
	};	
	SPreDefinedKeyComboSet mp_combo_sets[MAX_COMBO_SETS];
	int m_num_combo_sets;
	// Used for writing/reading the above array to mem card.
	void write_combo_sets(Script::CStruct *p_info);
	void read_combo_sets(Script::CStruct *p_info);
	
	// Generates the goal_tetris_key_combos array and inserts it into p_info, for sending to
	// the goal manager.
	void generate_key_combos_array(Script::CStruct *p_info);
	
	
	enum
	{
		MAX_GAPS=10,
		MAX_GAP_TRICK_NAME_CHARS=50,
	};
	uint8 mp_gap_numbers[MAX_GAPS];
	uint8 m_num_gaps;
	char mp_trick_name[MAX_GAP_TRICK_NAME_CHARS+1];
	
	
	void write_position_into_node(uint index, uint32 nodeName);
	void add_goal_params(Script::CStruct *p_params);
	uint32 generate_marker_object_name(int index);
	uint32 generate_goal_object_name(int index);
	void create_marker(int index);
	void delete_marker(int index);
	bool position_too_close_to_existing_positions(const Mth::Vector& pos);
	bool position_ok_to_place(const Mth::Vector& pos, float height);

	void refresh_position_using_collision_check(uint positionIndex);

public:
	enum EWriteDest
	{
		NOT_WRITING_TO_GOAL_MANAGER=0,
		WRITING_TO_GOAL_MANAGER=1,
	};	
private:
	
	// The dest parameter determines the format in which the combo sets are written out.
	// When generating params for the goal manager, the full goal_tetris_key_combos array is
	// generated. When writing to mem card the chosen pre-defined set numbers are written instead,
	// so that after loading they can still be edited.
	void write_skatetris_params(Script::CStruct *p_info, EWriteDest dest);
	void read_skatetris_params(Script::CStruct *p_info);
	
public:
	CEditGoal();
	~CEditGoal();
	
	void Clear();

	#ifdef SIZE_TEST	
	// For testing, this fills the name buffers, eg mp_goal_name, mp_trick_name etc, to their
	// max size.
	void FillNameBuffers();
	uint32 GetSkateGoalMaxSize();
	#endif
	
	bool Used() {return m_used;}
	void SetUsedFlag(bool used) {m_used=used;}
	
	void SetLevelGoalIndex(uint index) {m_level_goal_index=index;}
	int GetLevelGoalIndex() {return m_level_goal_index;}
	
	uint32 GetId();
	const char *GetIDString();
	
	bool SetPosition(const Mth::Vector& pos, float height, float angle);
	bool GetPosition(Mth::Vector *p_pos, float *p_height, float *p_angle);
	CGoalPos::EType GetPositionType();
	void GetPosition(Mth::Vector *p_pos, int index);
	int GetNumPositionsSet() {return m_num_positions_set;}
	
	bool BackUp(const Mth::Vector& pos, float height, float angle);
	bool GotAllPositions();
	uint GetCurrentLetterIndex();
	bool PlacedLastPosition() {return m_placed_last_position;}
	
	void SetGoalDescription(const char *p_text);
	void SetWinMessage(const char *p_text);
	void SetGoalName(const char *p_name);
	void SetPedName(const char *p_name);
	bool PositionClashesWithExistingPositions(Mth::Vector& pos);
	
	int  GetScore() {return m_score;}
	void SetScore(int score) {m_score=score;}
	void SetTimeLimit(int timeLimit);
	void SetControlType(uint32 controlType);
	
	void EditGoal();

	void WriteIntoStructure(Script::CStruct *p_info);
	void ReadFromStructure(Script::CStruct *p_info);

	void WriteGoalSpecificParams(Script::CStruct *p_info, EWriteDest dest=NOT_WRITING_TO_GOAL_MANAGER);
	void ReadGoalSpecificParams(Script::CStruct *p_params);
	void RemoveGoalSpecificFlag(Script::CStruct *p_params);
	
	uint32 GetType() {return m_goal_type;}
	void SetType(uint32 type);
	void SetLevel(uint32 levelScript) {m_level_script=levelScript;}
	uint32 GetLevel() {return m_level_script;}
	const char *GetGoalName() {return mp_goal_name;}
	const char *GetPedName() {return mp_ped_name;}
	void FlagAsWon() {m_has_won_goal=true;}
	
	void AddKeyComboSet(int setIndex, bool requirePerfect, int spin, int numTaps);
	void RemoveKeyComboSet(int setIndex);
	SPreDefinedKeyComboSet *GetKeyComboSet(int setIndex);
	
	void AddGap(uint8 gap_number);
	void RemoveGap(uint8 gap_number);
	void RemoveGapAndReorder(uint8 gap_number);
	bool GotGap(uint8 gap_number);
	
	void AddGoalToGoalManager(bool markUnbeaten=false);
	void WriteNodePositions();
	
	bool GoalIsOutsideArea(float x0, float z0, float x1, float z1);
	void RefreshPositionsUsingCollisionCheck();
	
	void GetDebugInfo(Script::CStruct* p_info);
};

class CGoalEditorComponent : public CBaseComponent
{
	CEditGoal 	mp_goals[MAX_GOALS_TOTAL];
	CEditGoal 	*mp_current_goal;

	CInputComponent 		*mp_input_component;
	CEditorCameraComponent 	*mp_editor_camera_component;
	void get_pointers_to_required_components();
	Obj::CCompositeObject *get_cursor();
	void refresh_cursor_object();

	void get_pos_from_camera_component(Mth::Vector *p_pos, float *p_height, float *p_angle);
	void update_cursor_collision_type();
	
	CEditGoal *find_goal(const char *p_name);
	CEditGoal *find_goal(uint32 id);
	CEditGoal *find_goal(Script::CStruct *p_params);
	
	void update_camera_pos();
	void update_cursor_position();
	void remove_goal(CEditGoal *p_goal);
	bool too_close_to_another_goal_position(const Mth::Vector &pos, float height, uint32 level, int positionIndex);
	
public:
    CGoalEditorComponent();
    virtual ~CGoalEditorComponent();

public:
	void							ClearAllExceptParkGoals();
	void							ClearOnlyParkGoals();
	
	void							CleanOutUnfinishedGoals();
	void							NewGoal();
	void							SetGoalType(uint32 type);
	void							AddGoalsToGoalManager();
	void							WriteNodePositions();
	int								GetNumGoals();
	bool							ThereAreGoalsOutsideArea(float x0, float z0, float x1, float z1);
	void							DeleteGoalsOutsideArea(float x0, float z0, float x1, float z1);
	int 							CountGoalsForLevel(uint32 level);
	
	void							RefreshGoalPositionsUsingCollisionCheck();
	void							RefreshGapGoalsAfterGapRemovedFromPark(int gapNumber);
	
	
	// For reading/writing to memcard
	void							WriteIntoStructure(Script::CStruct *p_info);
	enum EBoolLoadingParkGoals
	{
		NOT_LOADING_PARK_GOALS=0,
		LOADING_PARK_GOALS=1,
	};	
	void							ReadFromStructure(Script::CStruct *p_info, EBoolLoadingParkGoals loadingParkGoals=NOT_LOADING_PARK_GOALS);
	
	void							WriteIntoStructure(uint32 levelScript, Script::CStruct *p_info);
	uint32							WriteToBuffer(uint32 levelScript, uint8 *p_buffer, uint32 bufferSize);
	uint8 *							ReadFromBuffer(uint32 levelScript, uint8 *p_buffer);
	

	virtual	void 					Finalize();
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );
	virtual void					Hide( bool shouldHide );

	static CBaseComponent*			s_create();
};

//char p[sizeof(CGoalEditorComponent)/0];

void InsertGoalEditorNodes();
Obj::CGoalEditorComponent *GetGoalEditor();

} // namespace Obj

#endif


================================================
FILE: Code/Sk/Components/ProjectileCollisionComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       ProjectileCollisionComponent.cpp
//* OWNER:          SPG
//* CREATION DATE:  7/10/03
//****************************************************************************

// The CEmptyComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy emptycomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "Empty" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#define vSKATER_RADIUS	FEET( 4 )
namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CProjectileCollisionComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CProjectileCollisionComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CProjectileCollisionComponent::CProjectileCollisionComponent() : CBaseComponent()
{
    SetType( CRC_PROJECTILECOLLISION );
	m_radius = 0.0f;
	m_owner_id = 0;
	m_death_script = 0;
	m_scale = 0;
	m_dying = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CProjectileCollisionComponent::~CProjectileCollisionComponent()
{   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CProjectileCollisionComponent::SetCollisionRadius( float radius )
{
	m_radius = radius;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CProjectileCollisionComponent::InitFromStructure( Script::CStruct* pParams )
{
	pParams->GetFloat(CRCD(0xc48391a5,"radius"), &m_radius, Script::ASSERT);
	pParams->GetFloat(CRCD(0x13b9da7b,"scale"), &m_scale, Script::ASSERT);
	pParams->GetChecksum(CRCD(0x81c39e06,"owner_id"), &m_owner_id, Script::ASSERT);
	pParams->GetChecksum(CRCD(0x6647adc3,"death_script"), &m_death_script, Script::ASSERT);

	m_vel.Set( 0, 0, 1 );
	pParams->GetVector(CRCD(0xc4c809e, "vel"), &m_vel, Script::ASSERT);
	m_vel.Normalize();
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CProjectileCollisionComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	//InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CProjectileCollisionComponent::Finalize()
{
	mp_suspend_component =  GetSuspendComponentFromObject( GetObject() );
}
	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CProjectileCollisionComponent::Hide( bool should_hide )
{
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CProjectileCollisionComponent::Update()
{
	float radii;

	radii = m_radius + vSKATER_RADIUS;
	if (!mp_suspend_component->SkipLogic() && !m_dying )
	{
		float dist;
		bool team_game;
		Mdl::Skate* skate_mod = Mdl::Skate::Instance();
		GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
		uint32 NumSkaters = skate_mod->GetNumSkaters();
		GameNet::PlayerInfo* target_player, *src_player;

		team_game = skate_mod->GetGameMode()->IsTeamGame();
		for( uint32 i = 0; i < NumSkaters; i++ )
		{
			Obj::CSkater *pSkater = skate_mod->GetSkater(i);
			Obj::CSkaterScoreComponent* p_score_comp;
			Mdl::Score* score;

			// Can't hit yourself with your own projectile
			if( pSkater->GetID() == m_owner_id )
			{
				continue;
			}

			src_player = gamenet_man->GetPlayerByObjectID( m_owner_id );
			target_player = gamenet_man->GetPlayerByObjectID( pSkater->GetID());
			
			// Allow players to get up after being knocked down
			if( target_player->IsVulnerable() == false )
			{
				continue;
			}

			p_score_comp = GetSkaterScoreComponentFromObject( pSkater );
			score = p_score_comp->GetScore();

			// Don't target dead players
			if( score->GetTotalScore() <= 0 )
			{
				continue;
			}

			if( team_game )
			{
				if( src_player->m_Team == target_player->m_Team )
				{
					uint32 enabled;

					enabled = gamenet_man->GetNetworkPreferences()->GetPreferenceChecksum( CRCD(0xe959c43a,"friendly_fire"), CRCD(0x21902065,"checksum"));
					if( enabled != CRCD(0xf81bc89b,"boolean_true"))
					{
						continue;
					}
				}
			}

			dist = (pSkater->GetPos() - GetObject()->GetPos()).Length();
			if( dist < radii )
			{
				GameNet::MsgProjectileHit hit_msg;
				Net::MsgDesc msg_desc;
				Net::Server* server;
				GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

				server = gamenet_man->GetServer();

				//Dbg_Printf( "COLLISION: Skater %d: owner: %d\n", pSkater->GetID(), m_owner_id);
				GetObject()->MarkAsDead();
				if( m_death_script != 0 )
				{
					Script::CStruct* params;
					Mth::Vector pos;

					pos = GetObject()->GetPos();
					params = new Script::CStruct;
					
					params->AddVector( CRCD(0x7f261953,"pos"), pos );
					params->AddVector( CRCD(0xc4c809e,"vel"), m_vel );
					params->AddFloat( CRCD(0x13b9da7b,"scale"), m_scale );

					Script::RunScript( m_death_script, params );
					delete params;
				}
	
				// a message explaining "You've been hit down by ID for N damage"
				hit_msg.m_Id = m_owner_id;
				hit_msg.m_Damage = get_damage_amount();
	
				/*if (CInputComponent* p_input_component = GetInputComponentFromObject(GetObject()))
				{
					p_input_component->DisableInput();
				}
				p_player->MarkAsNonCollidable();*/
				
				msg_desc.m_Id = GameNet::MSG_ID_SKATER_HIT_BY_PROJECTILE;
				msg_desc.m_Data = &hit_msg;
				msg_desc.m_Length = sizeof(GameNet::MsgProjectileHit);
				msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
				msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
				target_player->SetHitTime( Tmr::GetTime());
				server->EnqueueMessage( target_player->GetConnHandle(), &msg_desc );
	
				// basically a message explaining "You hit someone"
				hit_msg.m_Id = pSkater->GetID();
				msg_desc.m_Id = GameNet::MSG_ID_SKATER_PROJECTILE_HIT_TARGET;
				server->EnqueueMessage( src_player->GetConnHandle(), &msg_desc );	
				
				
				if(( score->GetTotalScore() - get_damage_amount() ) <= 0 )
				{
					// If this shot is killing them...
					if( score->GetTotalScore() > 0 )
					{
						if( target_player->IsLocalPlayer() == false )
						{
							Script::CStruct* params;
	
							Dbg_Printf( "*** Elimination\n" );
	
							params = new Script::CStruct;
							params->AddString( CRCD(0xa1dc81f9,"name"), pSkater->GetDisplayName());
							Script::RunScript( CRCD(0x9b043179,"announce_elimination"), params );
							delete params;
							skate_mod->HideSkater( pSkater, true );
						}
					}
					score->SetTotalScore( 0 );
				}
				else
				{
					score->SetTotalScore( score->GetTotalScore() - get_damage_amount());
				}
				
				break;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CProjectileCollisionComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CProjectileCollisionComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CProjectileCollisionComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums
	

// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int	CProjectileCollisionComponent::get_damage_amount( void )
{
	return (int) m_scale * 10;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CProjectileCollisionComponent::MarkAsDying( void )
{
	m_dying = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}


================================================
FILE: Code/Sk/Components/ProjectileCollisionComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       ProjectileCollisionComponent.h
//* OWNER:          SPG
//* CREATION DATE:  07/14/03
//****************************************************************************

#ifndef __COMPONENTS_PROJECTILECOLLISIONCOMPONENT_H__
#define __COMPONENTS_PROJECTILECOLLISIONCOMPONENT_H__

#include 
#include 

#include 
#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_PROJECTILECOLLISION CRCD(0x7767d6d7,"ProjectileCollision")

//  Standard accessor macros for getting the component either from within an object, or 
//  given an object				 
#define		GetProjectileCollisionComponent() ((Obj::CProjectileCollisionComponent*)GetComponent(CRC_PROJECTILECOLLISION))
#define		GetProjectileCollisionComponentFromObject(pObj) ((Obj::CProjectileCollisionComponent*)(pObj)->GetComponent(CRC_PROJECTILECOLLISION))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CProjectileCollisionComponent : public CBaseComponent
{
public:
    CProjectileCollisionComponent();
    virtual ~CProjectileCollisionComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void					Hide( bool should_hide );
	virtual void					Finalize();
	
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();

	void							SetCollisionRadius( float radius );
	void							MarkAsDying( void );

private:
	int								get_damage_amount( void );

	CSuspendComponent*	mp_suspend_component;

	float				m_radius;	// Radius for spherical collision detection
	float				m_scale;
	uint32				m_death_script;
	uint32				m_owner_id;
	Mth::Vector			m_vel;
	bool				m_dying;
};

}

#endif


================================================
FILE: Code/Sk/Components/RailEditorComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       RailEditorComponent.cpp
//* OWNER:          Kendall Harrison
//* CREATION DATE:  3/21/2003
//****************************************************************************

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

DefinePoolableClass(Obj::CEditedRailPoint);
DefinePoolableClass(Obj::CEditedRail);

// TODO: The m_mode member could in theory be removed, just use the tags to store it instead.
// Hence could remove the SetEditingMode and GetEditingMode functions.
// (Nice to have as much logic moved to script as possible)

namespace Obj
{
Mth::Vector ZeroVector;

#define RAIL_SECTOR_HEIGHT 2.625f

static uint32 s_rail_unique_id=1;
static int s_highlight_flash_counter=0;

// This will rotate pos-rotateCentre by degrees about Y, add the result to newCentre, and return the result.
// Needed by the clipboard when displaying the copied rails as they rotate with the cursor.
static Mth::Vector s_rotate_and_translate(Mth::Vector& pos, Mth::Vector& rotateCentre, Mth::Vector& newCentre, float degrees)
{
	Mth::Vector d=pos-rotateCentre;
	
	float rad=degrees*3.141592654f/180.0f;
	float s=sinf(rad);
	float c=cosf(rad);

	Mth::Vector rotated;
	rotated[X]=c*d[X]+s*d[Z];
	rotated[Y]=d[Y];
	rotated[Z]=c*d[Z]-s*d[X];
	
	return newCentre+rotated;
}

// SPEEDOPT: Pass false to updateSuperSectors when creating a batch of sectors, and call
// UpdateSuperSectors afterwards.
static Nx::CSector *s_clone_sector(uint32 sector_name)
{
	Nx::CScene *p_source_scene=Nx::CEngine::sGetScene("sk5ed");
	if (!p_source_scene)
	{
		return NULL;
	}
		
	Nx::CScene *p_cloned_scene=Ed::CParkManager::sInstance()->GetGenerator()->GetClonedScene();
	if (!p_cloned_scene)
	{
		return NULL;
	}
		
	uint32 new_sector_checksum = p_source_scene->CloneSector(sector_name, p_cloned_scene, false, true);
	
	// SPEEDOPT: If a batch of sectors are cloned, only do this once at the end.
	p_cloned_scene->UpdateSuperSectors();
	
	Nx::CSector *p_new_sector = Nx::CEngine::sGetSector(new_sector_checksum);

	return p_new_sector;
}


static void s_generate_end_vert_positions(Mth::Vector &pos, Mth::Vector &dir, 
										  Mth::Vector *p_geom_verts,
										  int *p_vert_indices,
										  int numIndices)
{
	// Need to copy the origin into a local var cos the source is about to be modified.
	Mth::Vector origin=p_geom_verts[p_vert_indices[0]];
	
	Mth::Vector up(0.0f,1.0f,0.0f);
	Mth::Vector u=dir;
	u.Normalize();
	
	Mth::Vector w;
	if (fabs(u[Y]) > 0.99999f)
	{
		w.Set(0.0f,0.0f,1.0f);
	}
	else
	{
		w=Mth::CrossProduct(up,u);
		w.Normalize();
	}	
	
	Mth::Vector v=Mth::CrossProduct(u,w);
	v.Normalize();
	Mth::Matrix rot;
	rot[Mth::RIGHT]=u;
	rot[Mth::UP]=v;
	rot[Mth::AT]=w;
	rot[3][3]=1.0f;
	
	for (int i=0; iGetGeom();
	Dbg_MsgAssert(p_source_geom,("NULL p_source_geom ?"));
	
	int num_render_verts=p_source_geom->GetNumRenderVerts();
	Dbg_MsgAssert(num_render_verts==num_indices*2,("Unexpected extra vertices in rail sector, expected %d, got %d",2*num_indices,num_render_verts));
	
	// SPEEDOPT: If necessary, could use a static buffer for the verts
	Mth::Vector *p_modified_render_verts=(Mth::Vector*)Mem::Malloc(num_render_verts * sizeof(Mth::Vector));
	p_source_geom->GetRenderVerts(p_modified_render_verts);

	#ifdef __NOPT_ASSERT__
	for (int i=0; iGetGeom();
		Dbg_MsgAssert(p_last_geom,("NULL p_last_geom ?"));
		
		int last_num_render_verts=p_last_geom->GetNumRenderVerts();
		// SPEEDOPT: If necessary, could use a static buffer for the verts
		Mth::Vector *p_last_verts=(Mth::Vector*)Mem::Malloc(last_num_render_verts * sizeof(Mth::Vector));
		p_last_geom->GetRenderVerts(p_last_verts);

		// Tie the end verts to the new start verts.
		for (int i=0; iSetRenderVerts(p_last_verts);

		Mem::Free(p_last_verts);
	}
	else
	{
		dir=endPos-startPos;
		s_generate_end_vert_positions(startPos, dir, p_modified_render_verts, p_end_verts_a, num_indices);
	}	
	
	dir=endPos-startPos;
	s_generate_end_vert_positions(endPos, dir, p_modified_render_verts, p_end_verts_b, num_indices);
	

	// Write the vertex coords just calculated into the cloned sector.
	Dbg_MsgAssert(p_clonedSector,("NULL p_clonedSector"));
	Nx::CGeom *p_geom=p_clonedSector->GetGeom();
	Dbg_MsgAssert(p_geom,("NULL p_geom ?"));
	Dbg_MsgAssert(p_geom->GetNumRenderVerts()==p_source_geom->GetNumRenderVerts(),("Source geom num verts mismatch"));
	p_geom->SetRenderVerts(p_modified_render_verts);

	/////////////////////////////////////////////////////////////////
	// Now update the collision verts
	/////////////////////////////////////////////////////////////////
	
	if (update_collision) // Uncomment this once the rail pieces have been reexported with correct collision
	{
		// Get the source render verts again so that we can match up the source collision verts with them, and hence get
		// the index of the modified render vert to use for each collision vert.
		Mth::Vector *p_source_render_verts=(Mth::Vector*)Mem::Malloc(num_render_verts * sizeof(Mth::Vector));
		p_source_geom->GetRenderVerts(p_source_render_verts);
		// The source render verts are in relative coords.
	
		// Get the source collision verts, which will also be in relative coords.
		Nx::CCollObjTriData *p_source_col_data=p_source_geom->GetCollTriData();
		int num_source_col_verts=p_source_col_data->GetNumVerts();

		Mth::Vector *p_collision_verts=(Mth::Vector*)Mem::Malloc(num_source_col_verts * sizeof(Mth::Vector));
		p_source_col_data->GetRawVertices(p_collision_verts);
	
		// For each of the collision verts, look for a matching coord in the source render verts,
		// and if found, write in the world coords calculated for it earlier.
		for (int i=0; iGetCollTriData();
		Dbg_MsgAssert(p_dest_col_data->GetNumVerts()==num_source_col_verts,("Bad p_dest_col_data->GetNumVerts() ?"));
		#endif

		p_clonedSector->SetRawCollVertices(p_collision_verts);
		Mem::Free(p_collision_verts);
		Mem::Free(p_source_render_verts);
	}
	
	Mem::Free(p_modified_render_verts);	
} 

// Returns true if the skater will be able to grind from a to b to c without being forced off
// due to the angle being too big.
static bool s_angle_is_ok_to_grind(Mth::Vector &a, Mth::Vector &b, Mth::Vector &c)
{
	Mth::Vector ab=b-a;
	ab[Y]=0.0f;
	ab.Normalize();
	
	Mth::Vector bc=c-b;
	bc[Y]=0.0f;
	bc.Normalize();
	
	float cosine=Mth::DotProduct(ab,bc);
	if (cosine < cosf(Mth::DegToRad(Script::GetFloat(CRCD(0x76c1da15,"Rail_Corner_Leave_Angle")))))
	{
		return false;
	}
	
	return true;	
}

static bool s_distance_is_too_long(Mth::Vector &a, Mth::Vector &b)
{
	Mth::Vector d=a-b;
	// The distance at which the dotted line will flash red to indicate the distance is too long
	return d.Length() > 4000.0f;
}

static bool s_distance_is_way_too_long(Mth::Vector &a, Mth::Vector &b)
{
	Mth::Vector d=a-b;
	// The distance at which the dotted line will disappear completely to avoid rendering problems.
	return d.Length() > 4500.0f;
}

static bool s_positions_OK(Mth::Vector &a, Mth::Vector &b)
{
	if (s_distance_is_too_long(a, b))
	{
		return false;
	}
	
	Mth::Vector diff=a-b;
	
	// Don't allow consecutive points to be placed too close, otherwise the user may accidentally
	// place points on top of each other, & it could cause weirdness in the calculations due
	// to zero vectors trying to get normalized etc.
	if (diff.Length() < Script::GetFloat(CRCD(0x8bea1c03,"RailEditorMinimumPointSeparation")))
	{
		return false;
	}
	
	// Also don't allow the rail segment to get too steep.
	diff.Normalize();
	if (fabs(diff[Y]) > sinf(Mth::DegToRad(Script::GetFloat(CRCD(0x9fd761af,"RailEditorMaxSlope")))))
	{
		return false;
	}
	
	return true;
}
										  
CEditedRailPoint::CEditedRailPoint()
{
	mPos.Set();
	mHasPost=false;
	mHighlighted=false;
	mHeightAboveGround=0.0f;
	mpNext=NULL;
	mpPrevious=NULL;
	mpClonedRailSector=NULL;
	mpPostSector=NULL;
}

CEditedRailPoint::~CEditedRailPoint()
{
	DestroyRailGeometry();
	DestroyPostGeometry();
}

// Creates the rail sector if it does not exist already, then writes the world
// positions into the vertices.
// rotateCentre, newCentre, and degrees allow the rail to be displayed rotated and translated from its original
// position. This is used when displaying a rail section on the clipboard cursor in the park editor.
void CEditedRailPoint::UpdateRailGeometry(Mth::Vector& rotateCentre, Mth::Vector& newCentre, float degrees)
{
	// Note: The last point that was placed is actually the next in the list, since they get added to the front.
	if (!mpNext)
	{
		// There is no last point, so a rail can't be created because there is nothing to join to.
		
		// TODO: Could maybe still make a small section of rail, so that the first post of a rail has
		// a bit of rail overhanging it. Would look better than having the rail stop right above the post.
		
		// Make sure any existing rail is destroyed.
		DestroyRailGeometry();
		return;
	}

	// Clone the sector if necessary
	if (!mpClonedRailSector)
	{
		mpClonedRailSector=s_clone_sector(CRCD(0x66fe99bf,"Sk5Ed_RA_Dynamic"));
	}
	
	Mth::Vector a, b, c;
	
	// Don't tie the end vertices of the two rail sections together if they meet at too great
	// an angle, cos otherwise one of the sections gets too thin.
	bool join_to_last_sector=true;
	if (mpNext->mpNext)
	{
		a=mpNext->mPos-mpNext->mpNext->mPos;
		b=mPos-mpNext->mPos;
		a.Normalize();
		b.Normalize();
		if (Mth::DotProduct(a,b) < cosf(Mth::DegToRad(Script::GetFloat(CRCD(0xb1459a78,"RailEditorMaxJoinAngle")))))
		{
			join_to_last_sector=false;
		}	

		a=s_rotate_and_translate(mpNext->mpNext->mPos, rotateCentre, newCentre, degrees);
		b=s_rotate_and_translate(mpNext->mPos, rotateCentre, newCentre, degrees);
		c=s_rotate_and_translate(mPos, rotateCentre, newCentre, degrees);
		
		s_calculate_rail_sector_vertex_coords(a, b, c,
											  mpClonedRailSector, CRCD(0x66fe99bf,"Sk5Ed_RA_Dynamic"),
											  join_to_last_sector ? mpNext->mpClonedRailSector:NULL, true);
	}
	else
	{
		Dbg_MsgAssert(mpNext->mpClonedRailSector==NULL,("Expected mpNext->mpClonedRailSector to be NULL ?"));
		
		Mth::Vector dummy;
		a=s_rotate_and_translate(mpNext->mPos, rotateCentre, newCentre, degrees);
		b=s_rotate_and_translate(mPos, rotateCentre, newCentre, degrees);
		s_calculate_rail_sector_vertex_coords(dummy, a, b, 
											  mpClonedRailSector, CRCD(0x66fe99bf,"Sk5Ed_RA_Dynamic"),
											  NULL, true);
	}											  
}

// Creates the post sector if it does not exist already, then writes the correct world
// positions into the vertices.
// This is called when the post is first created, and also gets called for all posts whenever a cell in
// the park is raised or lowered, so that the posts maintain contact with the ground.

// rotateCentre, newCentre, and degrees allow the post to be displayed rotated and translated from its original
// position. This is used when displaying a rail section on the clipboard cursor in the park editor.
void CEditedRailPoint::UpdatePostGeometry(Mth::Vector& rotateCentre, Mth::Vector& newCentre, float degrees)
{
	if (!mHasPost)
	{
		DestroyPostGeometry();
		return;
	}
	
	// Don't place a post if the rail is so close to the ground that there is not enough room for a post.
	// This often happens when snapping a rail point to the edge of some level geometry for example.	
	if (mHeightAboveGround < RAIL_SECTOR_HEIGHT)
	{
		DestroyPostGeometry();
		return;
	}
	
	// Get the coords of the source sectors vertices. These will be relative to the sectors origin,
	// which is in the middle of the post.
	Nx::CSector *p_source_sector=Nx::CEngine::sGetSector(CRCD(0x2756c52d,"Sk5Ed_RAp_Dynamic"));
	Nx::CGeom *p_source_geom=p_source_sector->GetGeom();
	Dbg_MsgAssert(p_source_geom,("NULL p_source_geom ?"));
	
	int num_verts=p_source_geom->GetNumRenderVerts();
	// SPEEDOPT: If necessary, could use a static buffer for the verts
	Mth::Vector *p_verts=(Mth::Vector*)Mem::Malloc(num_verts * sizeof(Mth::Vector));
	p_source_geom->GetRenderVerts(p_verts);

	// Find the y coords of the top and bottom
	float min_y=1000000.0f;
	float max_y=-1000000.0f;
	for (int i=0; i < num_verts; ++i)
	{
		float y=p_verts[i].GetY();
		if (y < min_y)
		{
			min_y=y;
		}
		if (y > max_y)
		{
			max_y=y;
		}	
	}
	
	// Check that max_y and min_y are the same, just opposite in sign.
	Dbg_MsgAssert(fabs(max_y-(-min_y)) < 0.01f,("Expected post geometry to have origin in centre, but min_y=%f, max_y=%f",min_y,max_y));
	Dbg_MsgAssert(max_y > 0.0f,("max_y < 0 ??  (max_y=%f)",max_y));
	
	float half_post_height=max_y;
	
	float rail_sector_height=RAIL_SECTOR_HEIGHT;
	// If the next post is at a different height, the height of the rail be a bit smaller than normal
	// due to the end face being tilted to point towards the next post.
	if (mpNext && mpPrevious)
	{
		Mth::Vector a=mpPrevious->mPos-mPos;
		Mth::Vector b=mPos-mpNext->mPos;
		// The end-face's normal will be the average of the two rails
		Mth::Vector av=(a+b)/2.0f;
		av.Normalize();
		// av[Y] is the sine of the angle, but we want the cosine
		rail_sector_height *= sqrtf(1.0f-av[Y]*av[Y]);
	}	

	Mth::Vector translated_rail_point=s_rotate_and_translate(mPos, rotateCentre, newCentre, degrees);
	
	// Translate the post so that it is centred on the rail point, then shift the y's down so that the
	// top of the post is at the bottom of the rail piece.
	// The height of the rail section needs to be added too because the rail point (mPos) is at the apex of the
	// rail section.
	float shift_down_amount=half_post_height+rail_sector_height;
	for (int i=0; i < num_verts; ++i)
	{
		p_verts[i]+=translated_rail_point;
		p_verts[i][Y]-=shift_down_amount;
	}

	// The top of the post is now in the correct position.
	// Now we need to move the bottom vertices down to ground level.
	
	// This array figured out manually, by printing out the vertex coords.
	// TODO: These indices will need to be different on Xbox and GameCube
#	ifdef __PLAT_NGC__
	int p_bottom_vert_indices[]=
	{
		0,2,4,6,		// Post bottom
		8,9,10,11		// Base plate
	};
#else
#	ifdef __PLAT_XBOX__
	int p_bottom_vert_indices[]=
	{
		8,9,10,11,		// Post bottom
		1,2,5,6			// Base plate
	};
#	else
	int p_bottom_vert_indices[]=
	{
		1,3,5,7,9,		// Post bottom
		10,11,12,13		// Base plate
	};
#	endif
#	endif
	int num_bottom_vertices=sizeof(p_bottom_vert_indices)/sizeof(int);
	Dbg_MsgAssert(num_bottom_vertices <= num_verts,("Too many bottom vertices!"));

	// The amount that the bottom vertices need to be moved down is the height, minus the
	// previous shift_down_amount cos we've already moved them by that much, minus
	// half the height of the post so that the bottom is on the ground.
	shift_down_amount=mHeightAboveGround-shift_down_amount-half_post_height;
	
	for (int i=0; imPos;
		u[Y]=0.0f;
		u.Normalize();
		float ux=u[X];
		float uz=u[Z];
		float vx=-uz;
		float vz=ux;

		for (int i=0; i < num_verts; ++i)
		{
			float dx=p_verts[i][X]-translated_rail_point[X];
			float dz=p_verts[i][Z]-translated_rail_point[Z];
			p_verts[i][X]=ux*dx+vx*dz+translated_rail_point[X];
			p_verts[i][Z]=uz*dx+vz*dz+translated_rail_point[Z];
		}			
	}

	// Clone the sector if necessary and write in the vertex coords just calculated.
	if (!mpPostSector)
	{
		mpPostSector=s_clone_sector(CRCD(0x2756c52d,"Sk5Ed_RAp_Dynamic"));
	}
	
	Nx::CGeom *p_geom=mpPostSector->GetGeom();
	Dbg_MsgAssert(p_geom,("NULL p_geom ?"));
	Dbg_MsgAssert(p_geom->GetNumRenderVerts()==p_source_geom->GetNumRenderVerts(),("Source geom num verts mismatch"));
	p_geom->SetRenderVerts(p_verts);
	Mem::Free(p_verts);	
}

void CEditedRailPoint::DestroyRailGeometry()
{
	if (mpClonedRailSector)
	{
		Nx::CScene *p_cloned_scene=Ed::CParkManager::sInstance()->GetGenerator()->GetClonedScene();
		Dbg_MsgAssert(p_cloned_scene,("Missing cloned scene!"));
		p_cloned_scene->DeleteSector(mpClonedRailSector);
		mpClonedRailSector=NULL;

		#ifdef __PLAT_NGC__
		Nx::CEngine::sFinishRendering();
		#endif

		// SPEEDOPT: If a batch of things are deleted, only do this once at the end.
		if (CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors)
		{
			p_cloned_scene->UpdateSuperSectors();
		}	
	}
}

void CEditedRailPoint::DestroyPostGeometry()
{
	if (mpPostSector)
	{
		Nx::CScene *p_cloned_scene=Ed::CParkManager::sInstance()->GetGenerator()->GetClonedScene();
		Dbg_MsgAssert(p_cloned_scene,("Missing cloned scene!"));
		p_cloned_scene->DeleteSector(mpPostSector);
		mpPostSector=NULL;
		
		#ifdef __PLAT_NGC__
		Nx::CEngine::sFinishRendering();
		#endif
		
		// SPEEDOPT: If a batch of things are deleted, only do this once at the end.
		if (CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors)
		{
			p_cloned_scene->UpdateSuperSectors();
		}	
	}
}

// Does a drop down collision check to find the height of mPos above the ground.
float CEditedRailPoint::FindGroundY()
{
	Mth::Vector off(0.0f,10000.0f,0.0f,0.0f);
	
	CFeeler feeler;
	feeler.SetStart(mPos + off );
	feeler.SetEnd(mPos - off );
	if (feeler.GetCollision())
	{
		return feeler.GetPoint().GetY();
	}
	else
	{
		return 0.0f;
	}	
}

// This will adjust mPos[Y] to be mHeightAboveGround above the ground.
// This is used to update the rails whenever the ground is raised or lowered.
// The rails will maintain the post heights if a post was on the ground
// that moved.
void CEditedRailPoint::AdjustY()
{
	if (mHasPost)
	{
		mPos[Y]=FindGroundY()+mHeightAboveGround;
	}	
}

void CEditedRailPoint::InitialiseHeight()
{
	mHeightAboveGround=mPos[Y]-FindGroundY();
}

void CEditedRailPoint::Highlight(EFlash flash, EEndPosts includeEndPosts)
{
	if (mHighlighted)
	{
		return;
	}
		
	uint32 vis=0xffffffff;
	if (flash)
	{
		int flash_rate=Script::GetInteger(CRCD(0x4551e901,"RailEditorHighlightFlashRate"));
		if (s_highlight_flash_counter < flash_rate/2)
		{
			vis=0xffffffff;
		}
		else
		{
			vis=0;
		}
			
		++s_highlight_flash_counter;
		if (s_highlight_flash_counter >= flash_rate)
		{
			s_highlight_flash_counter=0;
		}	
	}

	uint8 good_r=Script::GetInteger(CRCD(0xad28b18b,"RailEditorHighlightColourR"));
	uint8 good_g=Script::GetInteger(CRCD(0xc0f55560,"RailEditorHighlightColourG"));
	uint8 good_b=Script::GetInteger(CRCD(0xb09fa1ef,"RailEditorHighlightColourB"));

	uint8 bad_r=Script::GetInteger(CRCD(0x89b66fb9,"RailEditorBadAngleHighlightColourR"));
	uint8 bad_g=Script::GetInteger(CRCD(0xe46b8b52,"RailEditorBadAngleHighlightColourG"));
	uint8 bad_b=Script::GetInteger(CRCD(0x94017fdd,"RailEditorBadAngleHighlightColourB"));
	
	uint8 r=good_r;
	uint8 g=good_g;
	uint8 b=good_b;
		
	bool section_stretched_too_long=false;
	if (mpNext)
	{
		if (s_distance_is_too_long(mPos, mpNext->mPos))
		{
			section_stretched_too_long=true;
		}
	}		
	if (mpPrevious)
	{
		if (s_distance_is_too_long(mPos, mpPrevious->mPos))
		{
			section_stretched_too_long=true;
		}
	}		
	
	if (mpPostSector)
	{
		if (AngleIsOKToGrind() && !section_stretched_too_long)
		{
			r=good_r;
			g=good_g;
			b=good_b;
		}
		else
		{
			r=bad_r;
			g=bad_g;
			b=bad_b;
		}	
		mpPostSector->SetColor(Image::RGBA(r,g,b,0));
		mpPostSector->SetVisibility(vis);
	}	
	
	if (mpClonedRailSector)
	{
		if (mpNext)
		{
			if (mpNext->AngleIsOKToGrind() && AngleIsOKToGrind() && !section_stretched_too_long)
			{
				// The angle at the point at the other end of mpClonedRailSector is OK,
				// and so is the angle at this point, so all OK.
				r=good_r;
				g=good_g;
				b=good_b;
			}
			else
			{
				r=bad_r;
				g=bad_g;
				b=bad_b;
			}	
		}
		
		mpClonedRailSector->SetColor(Image::RGBA(r,g,b,0));
		mpClonedRailSector->SetVisibility(vis);
	}
	if (mpPrevious && mpPrevious->mpClonedRailSector)
	{
		if (mpPrevious->AngleIsOKToGrind() && AngleIsOKToGrind() && !section_stretched_too_long)
		{
			// The angle at the point at the other end of the next segment is OK,
			// and so is the angle at this point, so all OK.
			r=good_r;
			g=good_g;
			b=good_b;
		}
		else
		{
			r=bad_r;
			g=bad_g;
			b=bad_b;
		}	
		
		mpPrevious->mpClonedRailSector->SetColor(Image::RGBA(r,g,b,0));
		mpPrevious->mpClonedRailSector->SetVisibility(vis);
	}	

	if (includeEndPosts)
	{
		if (mpNext && !mpNext->mpNext && mpNext->mpPostSector)
		{
			mpNext->mpPostSector->SetColor(Image::RGBA(good_r,good_g,good_b,0));
			mpNext->mpPostSector->SetVisibility(vis);
		}
		if (mpPrevious && !mpPrevious->mpPrevious && mpPrevious->mpPostSector)
		{
			mpPrevious->mpPostSector->SetColor(Image::RGBA(good_r,good_g,good_b,0));
			mpPrevious->mpPostSector->SetVisibility(vis);
		}
	}
		
	mHighlighted=true;
}

void CEditedRailPoint::UnHighlight()
{
	if (mHighlighted)
	{
		if (mpPostSector)
		{
			mpPostSector->ClearColor();
			mpPostSector->SetVisibility(0xffffffff);
		}	
		if (mpClonedRailSector)
		{
			mpClonedRailSector->ClearColor();
			mpClonedRailSector->SetVisibility(0xffffffff);
		}
		if (mpPrevious && mpPrevious->mpClonedRailSector)
		{
			mpPrevious->mpClonedRailSector->ClearColor();
			mpPrevious->mpClonedRailSector->SetVisibility(0xffffffff);
		}	

		if (mpNext && !mpNext->mpNext && mpNext->mpPostSector)
		{
			mpNext->mpPostSector->ClearColor();
			mpNext->mpPostSector->SetVisibility(0xffffffff);
		}	
		if (mpPrevious && !mpPrevious->mpPrevious && mpPrevious->mpPostSector)
		{
			mpPrevious->mpPostSector->ClearColor();
			mpPrevious->mpPostSector->SetVisibility(0xffffffff);
		}	
		
		mHighlighted=false;
	}	
}

// Used by graffiti games
void CEditedRailPoint::SetColor(Image::RGBA rgba)
{
	if (mpPostSector)
	{
		mpPostSector->SetColor(rgba);
	}	
	if (mpClonedRailSector)
	{
		mpClonedRailSector->SetColor(rgba);
	}	
}

// Used by graffiti games
void CEditedRailPoint::ClearColor()
{
	if (mpPostSector)
	{
		mpPostSector->ClearColor();
	}	
	if (mpClonedRailSector)
	{
		mpClonedRailSector->ClearColor();
	}	
}

void CEditedRailPoint::SetSectorActiveStatus(bool active)
{
	if (mpPostSector)
	{
		mpPostSector->SetActive(active);
	}	
	if (mpClonedRailSector)
	{
		mpClonedRailSector->SetActive(active);
	}	
}

bool CEditedRailPoint::AngleIsOKToGrind()
{
	if (mpNext && mpPrevious)
	{
		return s_angle_is_ok_to_grind(mpNext->mPos, mPos, mpPrevious->mPos);
	}
	return true;	
}

void CEditedRailPoint::WriteCompressedRailPoint(SCompressedRailPoint *p_dest)
{
	Dbg_MsgAssert(p_dest,("NULL p_dest"));
	
	p_dest->mHasPost=mHasPost;
	
	// Sometimes the point may have a height that is fractionally below zero, so instead of asserting
	// clamp it to zero.
	//Dbg_MsgAssert(mHeightAboveGround >= 0.0f,("Expected mHeightAboveGround to be >= 0 ? (is %f)",mHeightAboveGround));
	if (mHeightAboveGround < 0.0f)
	{
		p_dest->mHeight=0;
	}
	else
	{
		p_dest->mHeight=(uint16)(mHeightAboveGround*16.0f);
	}	
	
	Dbg_MsgAssert(fabs(mPos[X]*8.0f)<=32767.0f,("Rail point position x=%f out of range",mPos[X]));
	Dbg_MsgAssert(fabs(mPos[Y]*8.0f)<=32767.0f,("Rail point position y=%f out of range",mPos[Y]));
	Dbg_MsgAssert(fabs(mPos[Z]*8.0f)<=32767.0f,("Rail point position z=%f out of range",mPos[Z]));
	
	p_dest->mX=(sint16)(mPos[X]*8.0f);
	p_dest->mY=(sint16)(mPos[Y]*8.0f);
	p_dest->mZ=(sint16)(mPos[Z]*8.0f);
}

CEditedRail::CEditedRail()
{
	mId=s_rail_unique_id++;
	
	mpRailPoints=NULL;
	mpNext=NULL;
	mpPrevious=NULL;
	Clear();
}

CEditedRail::~CEditedRail()
{
	Clear();
}

void CEditedRail::Clear()
{
	CEditedRailPoint *p_rail_point=mpRailPoints;
	while (p_rail_point)
	{
		CEditedRailPoint *p_next=p_rail_point->mpNext;
		delete p_rail_point;
		p_rail_point=p_next;
	}	
	mpRailPoints=NULL;
}

// Runs through all the points and adjusts the y coords so that the stored heights match the actual
// heights above the ground. This is used to keep the post heights constant when raising or lowering the
// ground in the park editor.
void CEditedRail::AdjustYs()
{
	CEditedRailPoint *p_rail_point=mpRailPoints;
	while (p_rail_point)
	{
		p_rail_point->AdjustY();
		p_rail_point=p_rail_point->mpNext;
	}	
}

void CEditedRail::InitialiseHeights()
{
	CEditedRailPoint *p_rail_point=mpRailPoints;
	while (p_rail_point)
	{
		p_rail_point->InitialiseHeight();
		p_rail_point=p_rail_point->mpNext;
	}	
}

void CEditedRail::UpdateRailGeometry(Mth::Vector& rotateCentre, Mth::Vector& newCentre, float degrees)
{
	// Find the last rail point.
	CEditedRailPoint *p_rail_point=mpRailPoints;
	CEditedRailPoint *p_last=NULL;
	while (p_rail_point)
	{
		p_last=p_rail_point;
		p_rail_point=p_rail_point->mpNext;
	}	

	// It is necessary to traverse the list backwards because UpdateRailGeometry() uses some of the vertices of the next
	// rail in the list for the end points of its rail, so the next rail needs to have been updated first.
	p_rail_point=p_last;
	while (p_rail_point)
	{
		p_rail_point->UpdateRailGeometry(rotateCentre, newCentre, degrees);
		p_rail_point=p_rail_point->mpPrevious;
	}	
}

void CEditedRail::UpdatePostGeometry(Mth::Vector& rotateCentre, Mth::Vector& newCentre, float degrees)
{
	CEditedRailPoint *p_rail_point=mpRailPoints;
	while (p_rail_point)
	{
		p_rail_point->UpdatePostGeometry(rotateCentre, newCentre, degrees);
		p_rail_point=p_rail_point->mpNext;
	}	
}

void CEditedRail::DestroyRailGeometry()
{
	CEditedRailPoint *p_rail_point=mpRailPoints;
	while (p_rail_point)
	{
		p_rail_point->DestroyRailGeometry();
		p_rail_point=p_rail_point->mpNext;
	}	
}

void CEditedRail::DestroyPostGeometry()
{
	CEditedRailPoint *p_rail_point=mpRailPoints;
	while (p_rail_point)
	{
		p_rail_point->DestroyPostGeometry();
		p_rail_point=p_rail_point->mpNext;
	}	
}

CEditedRailPoint *CEditedRail::AddPoint()
{
	if (CEditedRailPoint::SGetNumUsedItems()==MAX_EDITED_RAIL_POINTS)
	{
		// No space left on the pool to store a new point.
		return NULL;
	}
	
	CEditedRailPoint *p_new=new CEditedRailPoint;
	p_new->mpNext=mpRailPoints;
	if (mpRailPoints)
	{
		mpRailPoints->mpPrevious=p_new;
	}
	p_new->mpPrevious=NULL;
	mpRailPoints=p_new;
	
	return p_new;	
}

int CEditedRail::CountPoints()
{
	int num_points=0;
	CEditedRailPoint *p_point=mpRailPoints;
	while (p_point)
	{
		++num_points;
		p_point=p_point->mpNext;
	}
	return num_points;
}

bool CEditedRail::FindNearestRailPoint(Mth::Vector &pos,
									   Mth::Vector *p_nearest_pos, float *p_dist_squared, int *p_rail_point_index, 
									   int ignore_index)
{
	if (!mpRailPoints)
	{
		return false;
	}
		
	CEditedRailPoint *p_point=mpRailPoints;
	int rail_point_index=0;
	float min_dist_squared=100000000.0f;
	while (p_point)
	{
		if (rail_point_index != ignore_index)
		{
			Mth::Vector diff=p_point->mPos-pos;
			// Zero the y so that one does not have to raise the cursor to make high rails highlight.
			// (Make's a big difference!)
			diff[Y]=0.0f;
			float dd=diff.LengthSqr();
			
			if (dd < min_dist_squared)
			{
				min_dist_squared=dd;
				
				*p_nearest_pos=p_point->mPos;
				*p_dist_squared=min_dist_squared;
				*p_rail_point_index=rail_point_index;
			}
		}
			
		p_point=p_point->mpNext;
		++rail_point_index;
	}
	
	return true;
}

int CEditedRail::CountPointsInArea(float x0, float z0, float x1, float z1)
{
	int num_points=0;

   	CEditedRailPoint *p_point=mpRailPoints;
	while (p_point)
	{
		if (p_point->mPos[X] > x0 && p_point->mPos[X] < x1 &&
			p_point->mPos[Z] > z0 && p_point->mPos[Z] < z1)
		{
			// The point is inside the area
			++num_points;
		}
			
		p_point=p_point->mpNext;
	}	
	
	return num_points;
}

void CEditedRail::DuplicateAndAddPoint(CEditedRailPoint *p_point)
{
	Dbg_MsgAssert(p_point,("NULL p_point"));
	
	CEditedRailPoint *p_new_point=AddPoint();
	Dbg_MsgAssert(p_new_point,("NULL p_new_point"));
	
	p_new_point->mPos=p_point->mPos;
	p_new_point->mHasPost=p_point->mHasPost;
	
	p_new_point->mHeightAboveGround=p_point->mHeightAboveGround;
}

CEditedRail *CEditedRail::GenerateDuplicateRails(float x0, float z0, float x1, float z1, CEditedRail *p_head)
{
	bool inside=false;
	CEditedRailPoint *p_first_point_of_new_rail=NULL;
	
   	CEditedRailPoint *p_point=mpRailPoints;
	while (p_point)
	{
		if (p_point->mPos[X] > x0 && p_point->mPos[X] < x1 &&
			p_point->mPos[Z] > z0 && p_point->mPos[Z] < z1)
		{
			// The point is inside the area
			
			if (inside)
			{
				if (p_first_point_of_new_rail)
				{
					CEditedRail *p_new_rail=new CEditedRail;
					p_new_rail->mpNext=p_head;
					p_head=p_new_rail;
					
					p_head->DuplicateAndAddPoint(p_first_point_of_new_rail);
					p_first_point_of_new_rail=NULL;
				}	
				p_head->DuplicateAndAddPoint(p_point);
			}
			else	
			{
				// Gone from outside to inside, so it's time to make a new rail.
				// Don't actually create the rail yet though, since it may only end up containing
				// one point, & don't want to create those.
				p_first_point_of_new_rail=p_point;
			}
			
			inside=true;
		}
		else
		{
			p_first_point_of_new_rail=NULL;
			inside=false;	
		}
			
		p_point=p_point->mpNext;
	}	

	return p_head;
}

void CEditedRail::UnHighlight()
{
   	CEditedRailPoint *p_point=mpRailPoints;
	while (p_point)
	{
		p_point->UnHighlight();
		p_point=p_point->mpNext;
	}	
}

CEditedRailPoint *CEditedRail::GetRailPointFromIndex(int index)
{
   	CEditedRailPoint *p_point=mpRailPoints;
	while (index)
	{
		if (!p_point)
		{
			break;
		}	
		p_point=p_point->mpNext;
		--index;
	}	
	Dbg_MsgAssert(p_point,("Could not find rail point with index %d",index));
	
	return p_point;
}

// Deletes p_point from the rail, and returns a pointer to any fragment that gets created.
// If the max rails has been reached and deleting the point will result in a fragment,
// then it won't delete the point after all, because a new rail will not be able to be created for
// the fragment.
CEditedRailPoint *CEditedRail::DeleteRailPoint(CEditedRailPoint *p_point)
{
	if (!p_point)
	{
		return NULL;
	}
	
	// Check that p_point is in the rail ...
	#ifdef __NOPT_ASSERT__
	bool found=false;
	CEditedRailPoint *p_check=mpRailPoints;
	while (p_check)
	{
		if (p_check == p_point)
		{
			found=true;
			break;
		}
		p_check=p_check->mpNext;
	}
	Dbg_MsgAssert(found,("p_point is not in the rail !"));		
	#endif
	
	if (p_point->mpPrevious && p_point->mpNext)
	{
		// A new rail will need to be created so bail out if there is not enough
		// space to create one.
		if (CEditedRail::SGetNumUsedItems()==MAX_EDITED_RAILS)
		{
			return NULL;
		}
	}
	
	// Update this rail if removing its first point.
	if (p_point == mpRailPoints)
	{
		Dbg_MsgAssert(p_point->mpPrevious==NULL,("Expected p_point->mpPrevious==NULL"));
		mpRailPoints=p_point->mpNext;
		if (p_point->mpNext)
		{
			p_point->mpNext->mpPrevious=NULL;
		}
		delete p_point;
		return NULL;
	}

	Dbg_MsgAssert(p_point->mpPrevious,("Expected p_point->mpPrevious not to be NULL"));
	// Remove any rail geometry connecting the adjacent point to this one.
	p_point->mpPrevious->DestroyRailGeometry();
	// Terminate this rail.
	p_point->mpPrevious->mpNext=NULL;
	
	// Return the new rail fragment.
	CEditedRailPoint *p_fragment_start=p_point->mpNext;
	delete p_point;
	
	if (p_fragment_start)
	{
		p_fragment_start->mpPrevious=NULL;
	}
	return p_fragment_start;
}

bool CEditedRail::UpdateRailPointPosition(int rail_point_index, Mth::Vector &pos, EUpdateSuperSectors updateSuperSectors)
{
   	CEditedRailPoint *p_point=GetRailPointFromIndex(rail_point_index);
	
	// UpdateRailPointPosition returns false if the position is bad in that it is
	// too close to an adjacent point or causes the rail to be too steep.
	// It still writes in the position so that the rail always stays attached to
	// the cursor as the user moves it around when in grab mode.
	// However, it won't write in the position if one of the rails will be stretched too long,
	// because that causes rendering problems.
	
	bool position_is_ok=true;
	if (p_point->mpNext)
	{
		if (!s_positions_OK(pos,p_point->mpNext->mPos))
		{
			position_is_ok=false;
		}
	}
	if (p_point->mpPrevious)
	{
		if (!s_positions_OK(pos,p_point->mpPrevious->mPos))
		{
			position_is_ok=false;
		}
	}

	// Don't allow points to be placed too close to the boundary of the park (TT5464)
	if (!Ed::IsWithinParkBoundaries(pos,PARK_BOUNDARY_MARGIN))
	{
		position_is_ok=false;
	}

	bool ok_to_update=true;
	if (p_point->mpNext)
	{
		if (s_distance_is_way_too_long(pos,p_point->mpNext->mPos))
		{
			ok_to_update=false;
		}	
	}
	if (p_point->mpPrevious)
	{
		if (s_distance_is_way_too_long(pos,p_point->mpPrevious->mPos))
		{
			ok_to_update=false;
		}	
	}


	if (ok_to_update)
	{
		p_point->mPos=pos;
		p_point->InitialiseHeight();
		p_point->UpdatePostGeometry();
		p_point->UpdateRailGeometry();
		if (p_point->mpPrevious)
		{
			p_point->mpPrevious->UpdateRailGeometry();
		}	
	
		// Updating the super sectors is technically required every time the collision verts on a sector
		// are modified, as they will be by this function.
		// If UpdateSuperSectors is not done, then when attempting to delete the sector later the low-level code
		// (in nxscene) will assert.
		// However, it can be slow to call UpdateSuperSectors every frame, so when a rail point is being dragged
		// around in grab mode it is not called. It is only called when placing the new point or when backing
		// out and restoring the old position.
		if (updateSuperSectors)
		{
			Nx::CScene *p_cloned_scene=Ed::CParkManager::sInstance()->GetGenerator()->GetClonedScene();
			Dbg_MsgAssert(p_cloned_scene,("NULL p_cloned_scene ?"));
			p_cloned_scene->UpdateSuperSectors();
		}
	}
		
	return position_is_ok;
}

// Used when pasting a rail
void CEditedRail::CopyRail(CEditedRail *p_source_rail)
{
	Clear();
	
	Dbg_MsgAssert(p_source_rail,("NULL p_source_rail"));
	
	CEditedRailPoint *p_source_point=p_source_rail->mpRailPoints;
	while (p_source_point)
	{
		CEditedRailPoint *p_new_point=AddPoint();
		
		p_new_point->mPos=p_source_point->mPos;
		p_new_point->mHasPost=p_source_point->mHasPost;
		p_new_point->mHeightAboveGround=p_source_point->mHeightAboveGround;
		
		p_source_point=p_source_point->mpNext;
	}	
}

// Used when pasting a rail
void CEditedRail::RotateAndTranslate(Mth::Vector& rotateCentre, Mth::Vector& newCentre, float degrees)
{
	CEditedRailPoint *p_point=mpRailPoints;
	while (p_point)
	{
		p_point->mPos=s_rotate_and_translate(p_point->mPos, rotateCentre, newCentre, degrees);
		p_point=p_point->mpNext;
	}
}

SCompressedRailPoint *CEditedRail::WriteCompressedRailPoints(SCompressedRailPoint *p_dest)
{
	Dbg_MsgAssert(p_dest,("NULL p_dest"));

	CEditedRailPoint *p_point=mpRailPoints;
	// Skip to the end of the list of points so that it can be traversed backwards.
	// This is so that when the rails are regenerated using InitUsingCompressedRailsBuffer()
	// they get recreated with the points in the original order, and hence the rail node indices 
	// will match on the server and client. 
	while (p_point && p_point->mpNext)
	{
		p_point=p_point->mpNext;
	}
	
	while (p_point)
	{
		p_point->WriteCompressedRailPoint(p_dest++);
		p_point=p_point->mpPrevious;
	}
	return p_dest;
}

// Used by graffiti games
void CEditedRail::ModulateRailColor(int seqIndex)
{
	Script::CArray* p_graffiti_col_tab = Script::GetArray( CRCD(0x7f1ba1aa,"graffitiColors") );
	Dbg_MsgAssert( (uint) seqIndex < p_graffiti_col_tab->GetSize(), ( "graffitiColors array too small" ) );

	Script::CArray *p_entry = p_graffiti_col_tab->GetArray(seqIndex);
	
	#ifdef	__NOPT_ASSERT__
	int size = p_entry->GetSize();
	Dbg_MsgAssert(size >= 3 && size <= 4, ("wrong size %d for color array", size));
	#endif
	
	Image::RGBA color;

	color.r = p_entry->GetInteger( 0 );
	color.g = p_entry->GetInteger( 1 );
	color.b = p_entry->GetInteger( 2 );
	color.a = 128;
	
	CEditedRailPoint *p_point=mpRailPoints;
	while (p_point)
	{
		p_point->SetColor(color);
		p_point=p_point->mpNext;
	}
}

// Used by graffiti games
void CEditedRail::ClearRailColor()
{
	CEditedRailPoint *p_point=mpRailPoints;
	while (p_point)
	{
		p_point->ClearColor();
		p_point=p_point->mpNext;
	}
}

void CEditedRail::SetSectorActiveStatus(bool active)
{
	CEditedRailPoint *p_point=mpRailPoints;
	while (p_point)
	{
		p_point->SetSectorActiveStatus(active);
		p_point=p_point->mpNext;
	}
}

void CEditedRail::GetDebugInfo( Script::CStruct* p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info"));
	
	p_info->AddChecksum(CRCD(0x40c698af,"id"),mId);
	
	int num_points=CountPoints();
		
	if (num_points)
	{
		Script::CArray *p_array=new Script::CArray;
		p_array->SetSizeAndType(num_points,ESYMBOLTYPE_STRUCTURE);

		int i=0;
		CEditedRailPoint *p_point=mpRailPoints;
		while (p_point)
		{
			Script::CStruct *p_struct=new Script::CStruct;
			p_struct->AddVector(CRCD(0x7f261953,"pos"),p_point->mPos[X],p_point->mPos[Y],p_point->mPos[Z]);
			p_struct->AddFloat(CRCD(0xab21af0,"Height"),p_point->mHeightAboveGround);
			p_struct->AddInteger(CRCD(0x41a51a93,"mpClonedRailSector"),(int)p_point->mpClonedRailSector);
			p_struct->AddInteger(CRCD(0x79ffd768,"mpPostSector"),(int)p_point->mpPostSector);
			
			p_array->SetStructure(i,p_struct);
			++i;
			p_point=p_point->mpNext;
		}
		
		p_info->AddArrayPointer(CRCD(0xd84571d6,"Points"),p_array);
	}
#endif				 
}

bool CEditedRail::ThereAreRailPointsOutsideArea(float x0, float z0, float x1, float z1)
{
	Dbg_MsgAssert(x0 <= x1 && z0 <= z1,("Bad area"));
	
   	CEditedRailPoint *p_point=mpRailPoints;
	while (p_point)
	{
		if (p_point->mPos[X] < x0 || p_point->mPos[X] > x1 ||
			p_point->mPos[Z] < z0 || p_point->mPos[Z] > z1)
		{
			return true;
		}	
		p_point=p_point->mpNext;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors=true;

// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CRailEditorComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CRailEditorComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CRailEditorComponent::CRailEditorComponent() : CBaseComponent()
{
	SetType( CRC_RAILEDITOR );

	Dbg_MsgAssert(MAX_EDITED_RAILS == MAX_EDITED_RAIL_POINTS/2,("Bad MAX_EDITED_RAILS"));
	
	CEditedRailPoint::SCreatePool(MAX_EDITED_RAIL_POINTS, "CEditedRailPoint");
	CEditedRail::SCreatePool(MAX_EDITED_RAILS, "CEditedRail");

	mp_input_component=NULL;
	mp_editor_camera_component=NULL;
	mp_dotted_line_sector=NULL;
	m_dotted_line_sector_name=0;
	mp_edited_rails=NULL;

	clear_compressed_rails_buffer();
	Clear();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CRailEditorComponent::~CRailEditorComponent()
{
	Clear();
	CEditedRailPoint::SRemovePool();
	CEditedRail::SRemovePool();
}

void CRailEditorComponent::clear_compressed_rails_buffer()
{
	for (uint i=0; impNext)
	{
		p_rail=p_rail->mpNext;
	}
		
	while (p_rail)
	{
		*p_num_points_in_rail++=p_rail->CountPoints();
		p_rail_point=p_rail->WriteCompressedRailPoints(p_rail_point);	
		
		p_rail=p_rail->mpPrevious;
	}
	
	Dbg_MsgAssert((uint8*)p_rail_point <= mp_compressed_rails_buffer+sizeof(mp_compressed_rails_buffer),("p_rail_point overwrote end of buffer"));
	Dbg_MsgAssert(p_num_points_in_rail-(uint16*)(mp_compressed_rails_buffer+2)==*(uint16*)mp_compressed_rails_buffer,("Num rails mismatch"));
}

// Used when sending rail info over the net.
uint8 *CRailEditorComponent::GetCompressedRailsBuffer()
{
	generate_compressed_rails_buffer();
	return mp_compressed_rails_buffer;
}

void CRailEditorComponent::SetCompressedRailsBuffer(uint8 *p_buffer)
{
	Dbg_MsgAssert(p_buffer,("NULL p_buffer"));
	memcpy(mp_compressed_rails_buffer, p_buffer, sizeof(mp_compressed_rails_buffer));
}

void CRailEditorComponent::InitUsingCompressedRailsBuffer()
{
	Clear();
	
	uint16 num_rails=*(uint16*)mp_compressed_rails_buffer;
	uint16 *p_num_points_in_rail=(uint16*)(mp_compressed_rails_buffer+2);
	SCompressedRailPoint *p_rail_point=(SCompressedRailPoint*)(p_num_points_in_rail+MAX_EDITED_RAILS);
	
	for (uint i=0; iAddPoint();
			if (p_point)
			{
				p_point->mHasPost=p_rail_point->mHasPost;
				p_point->mHeightAboveGround=p_rail_point->mHeight / 16.0f;
				p_point->mPos[X]=p_rail_point->mX / 8.0f;
				p_point->mPos[Y]=p_rail_point->mY / 8.0f;
				p_point->mPos[Z]=p_rail_point->mZ / 8.0f;
			}
			++p_rail_point;
		}	
	}
}

void CRailEditorComponent::AdjustYs()
{
	SetSectorActiveStatus(false);

	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		p_rail->AdjustYs();
		p_rail=p_rail->mpNext;
	}

	SetSectorActiveStatus(true);
}

void CRailEditorComponent::InitialiseHeights()
{
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		p_rail->InitialiseHeights();
		p_rail=p_rail->mpNext;
	}
}

void CRailEditorComponent::UpdateRailGeometry()
{
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		p_rail->UpdateRailGeometry();
		p_rail=p_rail->mpNext;
	}
	
	Nx::CScene *p_cloned_scene=Ed::CParkManager::sInstance()->GetGenerator()->GetClonedScene();
	Dbg_MsgAssert(p_cloned_scene,("NULL p_cloned_scene ?"));
	p_cloned_scene->UpdateSuperSectors();
}

void CRailEditorComponent::UpdatePostGeometry()
{
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		p_rail->UpdatePostGeometry();
		p_rail=p_rail->mpNext;
	}
	
	Nx::CScene *p_cloned_scene=Ed::CParkManager::sInstance()->GetGenerator()->GetClonedScene();
	Dbg_MsgAssert(p_cloned_scene,("NULL p_cloned_scene ?"));
	p_cloned_scene->UpdateSuperSectors();
}

void CRailEditorComponent::DestroyRailGeometry()
{
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		p_rail->DestroyRailGeometry();
		p_rail=p_rail->mpNext;
	}
}

void CRailEditorComponent::DestroyPostGeometry()
{
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		p_rail->DestroyPostGeometry();
		p_rail=p_rail->mpNext;
	}
}

void CRailEditorComponent::RefreshGeometry()
{
	InitialiseHeights();
	UpdateRailGeometry();
	UpdatePostGeometry();
}

void CRailEditorComponent::UnHighlightAllRails()
{
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		p_rail->UnHighlight();
		p_rail=p_rail->mpNext;
	}
}

// Removes the point p_point from the rail p_rail, so p_rail may contain no points afterwards.
// May result in the creation of a new rail, if the point was not one of the end points of p_rail.
// Returns true if it did create a new rail.
bool CRailEditorComponent::DeleteRailPoint(CEditedRail *p_rail, CEditedRailPoint *p_point)
{
	Dbg_MsgAssert(p_rail,("NULL p_rail"));
	Dbg_MsgAssert(p_point,("NULL p_point"));

	CEditedRailPoint *p_fragment=p_rail->DeleteRailPoint(p_point);
	if (p_fragment)
	{
		CEditedRail *p_new_rail=NewRail();
		p_new_rail->mpRailPoints=p_fragment;
		return true;
	}	
	
	return false;
}

// Used when resizing the park, or when deleting rail points within an area using the area-select tool.
// This will remove all rail points outside of, or inside of (x0,z0) (x1,z1)
// It needs to be a member function of CRailEditorComponent rather than CEditedRail, because
// it could result in the creation of new rails.
// Returns true if it did create new rails.
bool CRailEditorComponent::ClipRail(CEditedRail *p_rail, float x0, float z0, float x1, float z1, EClipType clipType)
{
	Dbg_MsgAssert(x0 <= x1,("Need x0 <= x1"));
	Dbg_MsgAssert(z0 <= z1,("Need z0 <= z1"));

	CEditedRailPoint *p_point=p_rail->mpRailPoints;
	while (p_point)
	{
		CEditedRailPoint *p_next=p_point->mpNext;
		
		bool delete_point=false;
		
		if (p_point->mPos[X] > x0 && p_point->mPos[X] < x1 &&
			p_point->mPos[Z] > z0 && p_point->mPos[Z] < z1)
		{
			// The point is inside the area
			if (clipType==DELETE_POINTS_INSIDE)
			{
				delete_point=true;
			}	
		}
		else	
		{
			// The point is outside the area
			if (clipType==DELETE_POINTS_OUTSIDE)
			{
				delete_point=true;
			}	
		}
		
		if (delete_point)
		{
			if (DeleteRailPoint(p_rail, p_point))
			{
				// Deleting the point resulted in a new rail being created, which means that
				// p_next no longer belongs to this rail, so bail out.
				return true;
			}
		}
		
		p_point=p_next;
	}		
	
	return false;
}

void CRailEditorComponent::ClipRails(float x0, float z0, float x1, float z1, EClipType clipType)
{
	if (x0 > x1)
	{
		float t=x0;
		x0=x1;
		x1=t;
	}
	if (z0 > z1)
	{
		float t=z0;
		z0=z1;
		z1=t;
	}
		
	while (true)
	{
		bool created_new_rails=false;
		
		CEditedRail *p_rail=mp_edited_rails;
		while (p_rail)
		{
			if (ClipRail(p_rail, x0, z0, x1, z1, clipType))
			{
				created_new_rails=true;
			}	
			p_rail=p_rail->mpNext;
		}		
		
		// Repeat until no new rails were created.
		// Any new rails created by the above ClipRail calls will have been stuck on the front of
		// the list, so mp_edited_rails will be different next time around.
		if (!created_new_rails)
		{
			break;
		}
	}
	
	RemoveEmptyAndSinglePointRails();
}

bool CRailEditorComponent::ThereAreRailPointsOutsideArea(float x0, float z0, float x1, float z1)
{
	if (x0 > x1)
	{
		float t=x0;
		x0=x1;
		x1=t;
	}
	if (z0 > z1)
	{
		float t=z0;
		z0=z1;
		z1=t;
	}
		
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		if (p_rail->ThereAreRailPointsOutsideArea(x0, z0, x1, z1))
		{
			return true;
		}	
		p_rail=p_rail->mpNext;
	}		
		
	return false;	
}

// Used when copying a set of rails to the clipboard.
// Counts how many rail points will need to be created when copying the given area to the clipboard.
// This allows it to bail out before attempting to copy if there are not enough points left in the pool.
int CRailEditorComponent::CountPointsInArea(float x0, float z0, float x1, float z1)
{
	if (x0 > x1)
	{
		float t=x0;
		x0=x1;
		x1=t;
	}
	if (z0 > z1)
	{
		float t=z0;
		z0=z1;
		z1=t;
	}
		
	int num_points=0;
		
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		num_points += p_rail->CountPointsInArea(x0, z0, x1, z1);
		p_rail=p_rail->mpNext;
	}		
	
	return num_points;
}

bool CRailEditorComponent::AbleToCopyRails(float x0, float z0, float x1, float z1)
{
	return CountPointsInArea(x0,z0,x1,z1) <= GetNumFreePoints();
}

CEditedRail *CRailEditorComponent::GenerateDuplicateRails(float x0, float z0, float x1, float z1)
{
	CEditedRail *p_duplicated_rails=NULL;
	
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		p_duplicated_rails=p_rail->GenerateDuplicateRails(x0, z0, x1, z1, p_duplicated_rails);
		p_rail=p_rail->mpNext;
	}
		
	return p_duplicated_rails;
}

bool CRailEditorComponent::FindNearestRailPoint(Mth::Vector &pos, Mth::Vector *p_nearest_pos, 
												float *p_dist, 
												uint32 *p_rail_id, int *p_rail_point_index,
												uint32 ignore_rail_id, int ignore_rail_point_index)
{
	float min_dist_squared=100000000.0f;
	bool found_nearest_point=false;
	
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		Mth::Vector nearest;
		float dist;
		int rail_point_index=0;
		
		int ignore_index=-1;
		if (p_rail->mId==ignore_rail_id)
		{
			ignore_index=ignore_rail_point_index;
		}	
		
		if (p_rail->FindNearestRailPoint(pos, &nearest, &dist, &rail_point_index, ignore_index))
		{
			if (dist < min_dist_squared)
			{
				min_dist_squared=dist;
				*p_nearest_pos=nearest;
				*p_dist=dist;
				*p_rail_id=p_rail->mId;
				*p_rail_point_index=rail_point_index;
				found_nearest_point=true;
			}	
		}
		
		p_rail=p_rail->mpNext;
	}
	
	return found_nearest_point;
}

// For writing to memcard
void CRailEditorComponent::WriteIntoStructure(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info"));
	
	int num_rails=count_rails();
	
	Script::CArray *p_rails=new Script::CArray;
	p_rails->SetSizeAndType(num_rails,ESYMBOLTYPE_STRUCTURE);
	
	int index=0;
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		Script::CStruct *p_rail_info=new Script::CStruct;
		
		// Note: Don't need to write out the mId member because ReadFromBuffer will create new rails,
		// which will each get a new id on creation.
		
		int num_points=p_rail->CountPoints();
		Script::CArray *p_points=new Script::CArray;
		p_points->SetSizeAndType(num_points,ESYMBOLTYPE_STRUCTURE);
		
		int point_index=0;
		CEditedRailPoint *p_point=p_rail->mpRailPoints;
		while (p_point)
		{
			Script::CStruct *p_point_info=new Script::CStruct;
			p_point_info->AddVector(CRCD(0x7f261953,"Pos"),p_point->mPos[X],p_point->mPos[Y],p_point->mPos[Z]);
			if (p_point->mpPostSector)
			{
				p_point_info->AddChecksum(NONAME,CRCD(0xdcded772,"HasPost"));
			}	
			p_points->SetStructure(point_index,p_point_info);
			
			p_point=p_point->mpNext;
			++point_index;
		}	

		p_rail_info->AddArrayPointer(CRCD(0xd84571d6,"Points"),p_points);
		p_rails->SetStructure(index,p_rail_info);
		++index;
		
		p_rail=p_rail->mpNext;
	}
	
	p_info->AddArrayPointer(CRCD(0x244550a6,"CreatedRails"),p_rails);
}

// For reading from memcard
void CRailEditorComponent::ReadFromStructure(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info"));
	
	Clear();
	
	Script::CArray *p_rails=NULL;
	p_info->GetArray(CRCD(0x244550a6,"CreatedRails"),&p_rails);
	if (!p_rails)
	{
		return;
	}

	Dbg_MsgAssert((int)p_rails->GetSize() <= GetNumFreeRails(),("Too many rails, got %d, max is %d",p_rails->GetSize(),GetNumFreeRails()));
	
	for (uint32 i=0; iGetSize(); ++i)
	{
		Script::CStruct *p_rail_info=p_rails->GetStructure(i);
		Dbg_MsgAssert(p_rail_info,("Eh? NULL p_rail_info ?"));
		
		NewRail();
		
		Script::CArray *p_points=NULL;
		p_rail_info->GetArray(CRCD(0xd84571d6,"Points"),&p_points,Script::ASSERT);
		for (uint32 p=0; pGetSize(); ++p)
		{
			Dbg_MsgAssert(mp_current_rail,("NULL mp_current_rail ?"));
			CEditedRailPoint *p_new_point=mp_current_rail->AddPoint();
			
			Script::CStruct *p_point_info=p_points->GetStructure(p);
			p_point_info->GetVector(CRCD(0x7f261953,"pos"),&p_new_point->mPos);
			if (p_point_info->ContainsFlag(CRCD(0xdcded772,"HasPost")))
			{
				p_new_point->mHasPost=true;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CRailEditorComponent::InitFromStructure( Script::CStruct* pParams )
{
	// ** Add code to parse the structure, and initialize the component

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CRailEditorComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	InitFromStructure(pParams);
}

void CRailEditorComponent::Hide( bool shouldHide )
{
	Ed::CParkManager *p_park_manager=Ed::CParkManager::sInstance();

	if (shouldHide)
	{
		Script::RunScript(CRCD(0xec0a515,"rail_editor_destroy_cursor"));
		p_park_manager->GetGenerator()->CleanUpOutRailSet();
		UnHighlightAllRails();
		DeleteDottedLine();
	}	
	else
	{
		Script::RunScript(CRCD(0x52d1ce6f,"rail_editor_create_cursor"));

		// Create the 'out rail' set within the park generator, required for snapping to the
		// nearest rail point.
		p_park_manager->GetGenerator()->GenerateOutRailSet(p_park_manager->GetConcreteMetaList());
		
		// Make sure we're using the skater's pad
		Script::CStruct *p_params=new Script::CStruct;
		p_params->AddInteger(CRCD(0x67e6859a,"player"),0);
		mp_input_component->InitFromStructure(p_params);
		delete p_params;

		
		// This is a quick fix to a bug where button triggers are stored up whilst the
		// rail editor component is suspended. I think it's a bug in the input component, cos
		// surely it should ignore the pad whist suspended?
		CControlPad& control_pad = mp_input_component->GetControlPad();
		control_pad.Reset();
	}
}

void CRailEditorComponent::get_pos_from_camera_component(Mth::Vector *p_pos, float *p_height, float *p_angle)
{
	Dbg_MsgAssert(mp_editor_camera_component,("NULL mp_editor_camera_component ?"));
	*p_pos=mp_editor_camera_component->GetCursorPos();
	*p_height=mp_editor_camera_component->GetCursorHeight();
	*p_angle=mp_editor_camera_component->GetCursorOrientation();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CRailEditorComponent::Finalize()
{
	// Get the pointers to the other required components.
	
	Dbg_MsgAssert(mp_input_component==NULL,("mp_input_component not NULL ?"));
	mp_input_component = GetInputComponentFromObject(GetObject());
	Dbg_MsgAssert(mp_input_component,("CRailEditorComponent requires parent object to have an input component!"));

	Dbg_MsgAssert(mp_editor_camera_component==NULL,("mp_editor_camera_component not NULL ?"));
	mp_editor_camera_component = GetEditorCameraComponentFromObject(GetObject());
	Dbg_MsgAssert(mp_editor_camera_component,("CRailEditorComponent requires parent object to have an EditorCamera component!"));
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CRailEditorComponent::Update()
{
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	if (control_pad.m_x.GetTriggered())
	{
		control_pad.m_x.ClearTrigger();
		Script::RunScript(CRCD(0x415f2afa,"RailEditorX"),NULL,GetObject());
	}	

	#ifdef __PLAT_NGC__
	if (control_pad.m_square.GetTriggered())
	{
		control_pad.m_square.ClearTrigger();
		Script::RunScript(CRCD(0x3ad21c4e,"RailEditorTriangle"),NULL,GetObject());
	}	

	if (control_pad.m_triangle.GetTriggered())
	{
		control_pad.m_triangle.ClearTrigger();
		Script::RunScript(CRCD(0xd8d956c9,"RailEditorSquare"),NULL,GetObject());
	}	
	#else
	if (control_pad.m_triangle.GetTriggered())
	{
		control_pad.m_triangle.ClearTrigger();
		Script::RunScript(CRCD(0x3ad21c4e,"RailEditorTriangle"),NULL,GetObject());
	}	

	if (control_pad.m_square.GetTriggered())
	{
		control_pad.m_square.ClearTrigger();
		Script::RunScript(CRCD(0xd8d956c9,"RailEditorSquare"),NULL,GetObject());
	}	
	#endif
	
	if (control_pad.m_circle.GetTriggered())
	{
		control_pad.m_circle.ClearTrigger();
		Script::RunScript(CRCD(0xc18d5b19,"RailEditorCircle"),NULL,GetObject());
	}	

	Script::RunScript(CRCD(0x97153f0e,"RailEditorEveryFrame"),NULL,GetObject());
}

void CRailEditorComponent::Clear()
{
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		CEditedRail *p_next=p_rail->mpNext;
		delete p_rail;
		p_rail=p_next;
	}
	mp_edited_rails=NULL;
	mp_current_rail=NULL;
	m_mode=0;
	DeleteDottedLine();
}	

void CRailEditorComponent::DeleteRail(CEditedRail *p_rail)
{
	if (mp_edited_rails==p_rail)
	{
		mp_edited_rails=p_rail->mpNext;
	}	
	if (mp_current_rail==p_rail)
	{
		mp_current_rail=p_rail->mpNext;
	}	

	if (p_rail->mpNext)
	{
		p_rail->mpNext->mpPrevious=p_rail->mpPrevious;
	}
	if (p_rail->mpPrevious)
	{
		p_rail->mpPrevious->mpNext=p_rail->mpNext;
	}
	delete p_rail;
}

CEditedRail *CRailEditorComponent::NewRail()
{
	CEditedRail *p_new=new CEditedRail;
	
	p_new->mpNext=mp_edited_rails;
	p_new->mpPrevious=NULL;
	if (p_new->mpNext)
	{
		p_new->mpNext->mpPrevious=p_new;
	}
	mp_edited_rails=p_new;
	mp_current_rail=p_new;	
	
	return p_new;
}

CEditedRail *CRailEditorComponent::GetRail(uint32 id)
{
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		if (p_rail->mId==id)
		{
			return p_rail;
		}
		p_rail=p_rail->mpNext;
	}		
	return NULL;
}

void CRailEditorComponent::RemoveEmptyAndSinglePointRails()
{
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		CEditedRail *p_next=p_rail->mpNext;
		
		if (p_rail->mpRailPoints == NULL || (p_rail->mpRailPoints && p_rail->mpRailPoints->mpNext==NULL))
		{
			DeleteRail(p_rail);
		}	
		p_rail=p_next;
	}		
}

int CRailEditorComponent::count_rails()
{
	int num_rails=0;
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		++num_rails;
		p_rail=p_rail->mpNext;
	}
	return num_rails;
}

int CRailEditorComponent::GetTotalRailNodesRequired()
{
	int total_nodes=0;
	
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		CEditedRailPoint *p_point=p_rail->mpRailPoints;
		while (p_point)
		{
			++total_nodes;
			p_point=p_point->mpNext;
		}	
		p_rail=p_rail->mpNext;
	}
	
	return total_nodes;
}

void CRailEditorComponent::SetUpRailNodes(Script::CArray *p_nodeArray, int *p_nodeNum, uint32 firstID)
{
	Dbg_MsgAssert(p_nodeArray,("NULL p_nodeArray"));
	Dbg_MsgAssert(p_nodeNum,("NULL p_nodeNum"));
	
	int node_index=*p_nodeNum;
	uint32 rail_point_id=firstID;

	int rail_index=0;	
	char p_rail_name[50];
	
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		CEditedRailPoint *p_point=p_rail->mpRailPoints;
		while (p_point)
		{
			Script::CStruct *p_node=p_nodeArray->GetStructure(node_index++);
	
			p_node->AddVector(CRCD(0x7f261953,"Pos"),p_point->mPos[X],p_point->mPos[Y],p_point->mPos[Z]);
			p_node->AddVector(CRCD(0x9d2d0915,"Angles"),0.0f,0.0f,0.0f);
			
			p_node->AddChecksum(CRCD(0xa1dc81f9,"Name"),rail_point_id++);
			
			p_node->AddChecksum(CRCD(0x12b4e660,"Class"),CRCD(0x8e6b02ad,"RailNode"));
			p_node->AddChecksum(NONAME,CRCD(0x7c2552b9,"CreatedAtStart"));
			
			p_node->AddChecksum(NONAME,CRCD(0x1645b830,"TrickObject"));
			sprintf(p_rail_name,"CreatedRailCluster%d",rail_index);
			p_node->AddChecksum(CRCD(0x1a3a966b,"Cluster"),Script::GenerateCRC(p_rail_name));
			
			// hardwire to metal for the moment
			p_node->AddChecksum(CRCD(0x54cf8532,"TerrainType"),CRCD(0xa9ecf4e9,"TERRAIN_METALSMOOTH"));
			
			if (p_point->mpNext)
			{
				Script::CArray *p_links = new Script::CArray();
				p_links->SetSizeAndType(1,ESYMBOLTYPE_INTEGER);
				p_links->SetInteger(0, node_index);
				p_node->AddArrayPointer(CRCD(0x2e7d5ee7,"Links"), p_links);
			}			

			// An attempt to make graffiti mode able to tag created rails, didn't work though
			// (TT11801)
			//p_node->AddChecksum(NONAME,CRCD(0x1645b830,"TrickObject"));
			//char p_cluster_name[20];
			//sprintf(p_cluster_name,"RailCluster%d",node_index);
			//p_node->AddChecksum(CRCD(0x1a3a966b,"Cluster"),Script::GenerateCRC(p_cluster_name));
			
			p_point=p_point->mpNext;
		}	
		p_rail=p_rail->mpNext;
		++rail_index;
	}

	*p_nodeNum=node_index;
}

void CRailEditorComponent::DeleteDottedLine()
{
	if (mp_dotted_line_sector)
	{
		Nx::CScene *p_cloned_scene=Ed::CParkManager::sInstance()->GetGenerator()->GetClonedScene();
		Dbg_MsgAssert(p_cloned_scene,("Missing cloned scene!"));
		p_cloned_scene->DeleteSector(mp_dotted_line_sector);
		
		mp_dotted_line_sector=NULL;
		m_dotted_line_sector_name=0;

		#ifdef __PLAT_NGC__
		Nx::CEngine::sFinishRendering();
		#endif
		
		p_cloned_scene->UpdateSuperSectors();
	}	
}

void CRailEditorComponent::DrawDottedLine(Mth::Vector& pos)
{
	// Do nothing if there is no last position to draw from.
	if (!mp_current_rail || !mp_current_rail->mpRailPoints)
	{
		return;
	}
	CEditedRailPoint *p_last_point=mp_current_rail->mpRailPoints;
	
	
	uint32 dotted_line_sector_name=CRCD(0x5dc3c690,"Sk5Ed_RAdot_Dynamic_Green");
	if (s_distance_is_too_long(p_last_point->mPos, pos))
	{
		if (s_distance_is_way_too_long(p_last_point->mPos, pos))
		{
			// If the rail sector is stretched too far it renders all weird, so don't draw it at all.
			DeleteDottedLine();
			return;
		}	

		dotted_line_sector_name=CRCD(0x8dda17d6,"Sk5Ed_RADot_Dynamic");
	}
	else if (p_last_point->mpNext)
	{
		if (!s_angle_is_ok_to_grind(p_last_point->mpNext->mPos, p_last_point->mPos, pos))
		{
			dotted_line_sector_name=CRCD(0x8dda17d6,"Sk5Ed_RADot_Dynamic");
		}
	}		
	
	
	// Clone the sector if the dotted line does not exist or needs changing colour
	if (!mp_dotted_line_sector || dotted_line_sector_name != m_dotted_line_sector_name)
	{
		DeleteDottedLine();
		mp_dotted_line_sector=s_clone_sector(dotted_line_sector_name);
		m_dotted_line_sector_name=dotted_line_sector_name;
	}
	
	Mth::Vector dummy;
	s_calculate_rail_sector_vertex_coords(dummy, p_last_point->mPos, pos, 
										  mp_dotted_line_sector, m_dotted_line_sector_name,
										  NULL, false);

	if (dotted_line_sector_name==CRCD(0x8dda17d6,"Sk5Ed_RADot_Dynamic"))
	{
		int flash_rate=Script::GetInteger(CRCD(0xeebe4394,"RailEditorRedLineFlashRate"));
		uint32 vis=0xffffffff;
		if (s_highlight_flash_counter >= flash_rate/2)
		{
			vis=0;
		}
			
		++s_highlight_flash_counter;
		if (s_highlight_flash_counter >= flash_rate)
		{
			s_highlight_flash_counter=0;
		}	
		mp_dotted_line_sector->SetVisibility(vis);
	}
										  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CRailEditorComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case 0x8f3e0b97: // GetCursorPosition
		{
			Mth::Vector cursor_pos(0.0f,0.0f,0.0f);		
			float cursor_height=0.0f;
			float cursor_angle=0.0f;
			get_pos_from_camera_component(&cursor_pos,&cursor_height,&cursor_angle);
			cursor_pos[Y]+=cursor_height;
			
			pScript->GetParams()->AddVector(CRCD(0x7f261953,"Pos"),cursor_pos[X],cursor_pos[Y],cursor_pos[Z]);
			pScript->GetParams()->AddFloat(CRCD(0xff7ebaf6,"Angle"),mp_editor_camera_component->GetCursorOrientation()*180.0f/3.141592654f);
			break;
		}
		
		// Returns false if a new rail could not be created, due to the limit on the number of rails 
		// being reached.
		case 0xd2c98192: // NewRail
		{
			if (GetNumFreePoints()==0)
			{
				return CBaseComponent::MF_FALSE;
			}
				
			NewRail();
			break;
		}

		case 0x31f66e6b: // MaxRailsReached
		{
			if (GetNumFreePoints()==0)
			{
				return CBaseComponent::MF_TRUE;
			}
			else
			{
				return CBaseComponent::MF_FALSE;
			}	
			break;
		}
			
		case 0x5828e852: // AddNewPosition
		{
			Mth::Vector pos;
			pos.Set();
			pParams->GetVector(CRCD(0x7f261953,"pos"),&pos);
			if (mp_current_rail)
			{
				CEditedRailPoint *p_last_point=mp_current_rail->mpRailPoints;
				if (p_last_point)
				{
					// Don't allow placement of points that are too close, or segments that are too steep
					if (!s_positions_OK(p_last_point->mPos, pos))
					{
						// But return true anyway, otherwise the script will make the cursor switch out
						// of rail-layout mode.
						return CBaseComponent::MF_TRUE;
					}
				}	

				// Don't allow points to be placed too close to the boundary of the park (TT5464)
				if (!Ed::IsWithinParkBoundaries(pos,PARK_BOUNDARY_MARGIN))
				{
					if (p_last_point)
					{
						// We must be in rail-layout mode, so do not add the point but return true
						// so that the cursor stays in rail layout mode.
						return CBaseComponent::MF_TRUE;
					}
					else	
					{
						return CBaseComponent::MF_FALSE;
					}	
				}
						
				Spt::SingletonPtr p_editor;
				if (p_editor->IsParkFull())
				{
					return CBaseComponent::MF_FALSE;
				}
		
				CEditedRailPoint *p_point=mp_current_rail->AddPoint();
				if (p_point)
				{
					p_point->mPos=pos;
					p_point->mHeightAboveGround=pos[Y]-p_point->FindGroundY();
					p_point->mHasPost=pParams->ContainsFlag(CRCD(0xcb4b4880,"AddPost"));
					p_point->UpdateRailGeometry();
					p_point->UpdatePostGeometry();
					return CBaseComponent::MF_TRUE;
				}
			}
			
			return CBaseComponent::MF_FALSE;
			break;
		}
			
		case 0x9c0762c4: // ClearRailEditor
		{
			Clear();
			break;
		}
		
		case 0x1cc3a173: // SetEditingMode
		{
			pParams->GetChecksum(CRCD(0x6835b854,"mode"),&m_mode);
			
			Script::CStruct *p_params=new Script::CStruct;
			p_params->AddChecksum(CRCD(0x6835b854,"mode"),CRCD(0xffd81c08,"rail_placement"));
			Script::RunScript(CRCD(0x9db065ad,"parked_set_helper_text_mode"),p_params);
			delete p_params;
			break;
		}
		
		case 0x63dd1204: // GetEditingMode
		{
			pScript->GetParams()->AddChecksum(CRCD(0x6835b854,"mode"),m_mode);
			break;
		}	
		
		case 0xaa438d71: // DrawDottedLine
		{
			Mth::Vector pos;
			pParams->GetVector(CRCD(0x7f261953,"pos"),&pos);
			DrawDottedLine(pos);
			break;
		}

		case 0x9386dcd9: // DeleteDottedLine
		{
			DeleteDottedLine();
			break;
		}
		
		case 0x6d2eee1b: // UpdateRailPointPosition
		{
			uint32 rail_id=0;
			pParams->GetChecksum(CRCD(0xa61e7cd9,"rail_id"),&rail_id);
			
			int rail_point_index=0;
			pParams->GetInteger(CRCD(0xab3c14,"rail_point_index"),&rail_point_index);
			
			Mth::Vector pos;
			pParams->GetVector(CRCD(0x7f261953,"Pos"),&pos);

			CEditedRail *p_rail=GetRail(rail_id);
			if (p_rail)
			{
				// UpdateRailPointPosition will always write in the new position, but returns
				// false if the position is bad in that it is too close to an adjacent point
				// or causes the rail to be too steep.
				// It still writes in the position so that the rail always stays attached to
				// the cursor as the user moves it around.
				if (!p_rail->UpdateRailPointPosition(rail_point_index, pos, 
													 (CEditedRail::EUpdateSuperSectors)pParams->ContainsFlag(CRCD(0xddf9a2bb,"UpdateSuperSectors"))))
				{
					return CBaseComponent::MF_FALSE;
				}	
			}	
			break;
		}

		case 0x8798e959: // HighlightRailPoint
		{
			uint32 rail_id=0;
			pParams->GetChecksum(CRCD(0xa61e7cd9,"rail_id"),&rail_id);
			
			int rail_point_index=0;
			pParams->GetInteger(CRCD(0xab3c14,"rail_point_index"),&rail_point_index);

			CEditedRail *p_rail=GetRail(rail_id);
			if (p_rail)
			{
				CEditedRailPoint *p_point=p_rail->GetRailPointFromIndex(rail_point_index);
				p_point->Highlight((CEditedRailPoint::EFlash)pParams->ContainsFlag(CRCD(0x5031a0fc,"Flash")), 
								   (CEditedRailPoint::EEndPosts)pParams->ContainsFlag(CRCD(0x1a52e4b8,"IncludeEndPosts")));
			}					   
			break;
		}

		case 0x6716227c: // DeleteRailPoint
		{
			uint32 rail_id=0;
			pParams->GetChecksum(CRCD(0xa61e7cd9,"rail_id"),&rail_id);
			CEditedRail *p_rail=GetRail(rail_id);
			if (!p_rail)
			{
				return CBaseComponent::MF_FALSE;
			}
			
			int rail_point_index=0;
			pParams->GetInteger(CRCD(0xab3c14,"rail_point_index"),&rail_point_index);
			CEditedRailPoint *p_point=p_rail->GetRailPointFromIndex(rail_point_index);
			if (!p_point)
			{
				return CBaseComponent::MF_FALSE;
			}
			
			DeleteRailPoint(p_rail, p_point);
			RemoveEmptyAndSinglePointRails();
			break;
		}
			
		case 0xd5a87eee: // UnHighlightAllRails
		{
			UnHighlightAllRails();
			break;
		}	

		case 0xfb6a0888: // GetEditedRailInfo
		{
			uint32 rail_id=0;
			pParams->GetChecksum(CRCD(0xa61e7cd9,"rail_id"),&rail_id);
			CEditedRail *p_rail=GetRail(rail_id);
			
			if (pParams->ContainsFlag(CRCD(0xe234f2b2,"CurrentRail")))
			{
				p_rail=mp_current_rail;
			}
			
			if (p_rail)
			{
				pScript->GetParams()->AddChecksum(CRCD(0xa61e7cd9,"rail_id"),p_rail->mId);
				pScript->GetParams()->AddInteger(CRCD(0x6164266e,"num_points"),p_rail->CountPoints());
			}	
			else
			{
				return CBaseComponent::MF_FALSE;
			}	
			break;
		}
			
		case 0x353ea2a9: // DeleteRail
		{
			uint32 rail_id=0;
			pParams->GetChecksum(CRCD(0xa61e7cd9,"rail_id"),&rail_id);
			CEditedRail *p_rail=GetRail(rail_id);
			if (p_rail)
			{
				DeleteRail(p_rail);
			}
			break;
		}

		case 0xd70781c3: // DestroyEditedRailSectors
		{
			CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors=true;
			if (pParams->ContainsFlag(CRCD(0x23a11cbc,"DoNotUpdateSuperSectors")))
			{
				CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors=false;
			}	
			DestroyRailGeometry();
			DestroyPostGeometry();
			CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors=true;
			break;
		}
		
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

int CRailEditorComponent::GetNumFreePoints()
{
	return MAX_EDITED_RAIL_POINTS-CEditedRailPoint::SGetNumUsedItems();
}

int CRailEditorComponent::GetNumFreeRails()
{
	return MAX_EDITED_RAILS-CEditedRail::SGetNumUsedItems();
}

CEditedRail *CRailEditorComponent::get_rail_from_cluster_name(uint32 clusterChecksum)
{
	char p_rail_cluster_name[50];
	int rail_index=0;
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		sprintf(p_rail_cluster_name,"CreatedRailCluster%d",rail_index);
		if (clusterChecksum == Script::GenerateCRC(p_rail_cluster_name))
		{
			return p_rail;
		}	
			
		p_rail=p_rail->mpNext;
		++rail_index;
	}	
	return NULL;
}

// Used by graffiti games
void CRailEditorComponent::ModulateRailColor(uint32 clusterChecksum, int seqIndex)
{
	CEditedRail *p_rail=get_rail_from_cluster_name(clusterChecksum);
	if (p_rail)
	{
		p_rail->ModulateRailColor(seqIndex);
	}	
}

// Used by graffiti games
void CRailEditorComponent::ClearRailColor(uint32 clusterChecksum)
{
	CEditedRail *p_rail=get_rail_from_cluster_name(clusterChecksum);
	if (p_rail)
	{
		p_rail->ClearRailColor();
	}	
}

// Used for switching off collision on any created-rail sectors whilst the rail editor
// cursor is doing collision checks.
// This is because the rail sectors have a vertical collidable poly, which occasionally does cause
// collisions, making the cursor ping up into the air. Must be some sort of floating point innaccuracy
// (or too much accuracy), probably because the collision check vector is straight down the vertical poly.
void CRailEditorComponent::SetSectorActiveStatus(bool active)
{
	CEditedRail *p_rail=mp_edited_rails;
	while (p_rail)
	{
		p_rail->SetSectorActiveStatus(active);
		p_rail=p_rail->mpNext;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CRailEditorComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CRailEditorComponent::GetDebugInfo"));

	p_info->AddInteger(CRCD(0x8c75a6c1,"NumFreeRailPoints"),GetNumFreePoints());
	p_info->AddInteger(CRCD(0xe954b3d4,"NumFreeRails"),GetNumFreeRails());
	
	if (mp_current_rail)
	{
		Script::CStruct *p_struct=new Script::CStruct;
		mp_current_rail->GetDebugInfo(p_struct);
		p_info->AddStructurePointer(CRCD(0xe234f2b2,"CurrentRail"),p_struct);
	}
	else
	{
		p_info->AddChecksum(CRCD(0xe234f2b2,"CurrentRail"),CRCD(0xda3403b0,"NULL"));
	}
		
	int num_rails=count_rails();
	if (num_rails)
	{
		Script::CArray *p_array=new Script::CArray;
		p_array->SetSizeAndType(num_rails,ESYMBOLTYPE_STRUCTURE);
		
		int i=0;
		CEditedRail *p_rail=mp_edited_rails;
		while (p_rail)
		{
			Script::CStruct *p_struct=new Script::CStruct;
			p_rail->GetDebugInfo(p_struct);
			p_array->SetStructure(i,p_struct);
			++i;
			p_rail=p_rail->mpNext;
		}
		Dbg_MsgAssert(i==num_rails,("Eh ?"));
		
		p_info->AddArrayPointer(CRCD(0x7e17dfa9,"Rails"),p_array);
	}
		
	
	// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CRailEditorComponent *GetRailEditor()
{
	Obj::CCompositeObject *p_obj=(Obj::CCompositeObject*)Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0x5b509ad3,"RailEditor"));
	Dbg_MsgAssert(p_obj,("No RailEditor object"));
	Obj::CRailEditorComponent *p_rail_editor=GetRailEditorComponentFromObject(p_obj);
	Dbg_MsgAssert(p_rail_editor,("No rail editor component ???"));
	
	return p_rail_editor;
}

}




================================================
FILE: Code/Sk/Components/RailEditorComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       RailEditorComponent.h
//* OWNER:          Kendall Harrison
//* CREATION DATE:  6/19/2003
//****************************************************************************

#ifndef __COMPONENTS_RAILEDITORCOMPONENT_H__
#define __COMPONENTS_RAILEDITORCOMPONENT_H__

#include 
#include 

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

#ifndef __OBJECT_BASECOMPONENT_H__
#include 
#endif

#ifndef __GFX_IMAGE_IMAGEBASIC_H
#include 
#endif

// Replace this with the CRCD of the component you are adding
#define		CRC_RAILEDITOR CRCD(0x5b509ad3,"RailEditor")

//  Standard accessor macros for getting the component either from within an object, or 
//  given an object				 
#define		GetRailEditorComponent() ((Obj::CRailEditorComponent*)GetComponent(CRC_RAILEDITOR))
#define		GetRailEditorComponentFromObject(pObj) ((Obj::CRailEditorComponent*)(pObj)->GetComponent(CRC_RAILEDITOR))

namespace Nx
{
	class CSector;
}
	
namespace Obj
{
extern Mth::Vector ZeroVector;

enum
{
	MAX_EDITED_RAIL_POINTS=400,
	
	// Single point rails are not allowed, so the maximum possible number of rails will be half the
	// number of points, since the shortest rail will have 2 points.
	MAX_EDITED_RAILS=MAX_EDITED_RAIL_POINTS/2,
};

struct SCompressedRailPoint
{
	// These are the original float values * 8
	sint16 mX,mY,mZ;
	// This is the original float height * 16
	uint16 mHeight;
	uint16 mHasPost;
};
	
class CEditedRailPoint : public Mem::CPoolable
{
public:
	CEditedRailPoint();
	~CEditedRailPoint();

	void WriteCompressedRailPoint(SCompressedRailPoint *p_dest);
	
	void UpdateRailGeometry(Mth::Vector& rotateCentre=ZeroVector, Mth::Vector& newCentre=ZeroVector, float degrees=0.0f);
	void UpdatePostGeometry(Mth::Vector& rotateCentre=ZeroVector, Mth::Vector& newCentre=ZeroVector, float degrees=0.0f);
	void DestroyRailGeometry();
	void DestroyPostGeometry();
	
	float FindGroundY();
	void AdjustY();
	void InitialiseHeight();

	// These enums are just to avoid passing multiple bools to Highlight()
	enum EFlash
	{
		DONT_FLASH=0,
		FLASH=1,
	};
	enum EEndPosts
	{
		DONT_INCLUDE_END_POSTS=0,
		INCLUDE_END_POSTS=1,
	};	
	void Highlight(EFlash flash, EEndPosts includeEndPosts);
	void UnHighlight();

	void SetColor(Image::RGBA rgba);
	void ClearColor();

	void SetSectorActiveStatus(bool active);
	
	bool AngleIsOKToGrind();
		
	// This class needs to be kept as small as possible since a pool of them exists.	
	
	// MEMOPT: Change pos to be 3 sint16's, would save 10K for 400 points.
	// Used x(fixed)=int(9.0f*x(float)+0.5f) if x>0, x(fixed)=int(9.0f*x(float)-0.5f) if x<0
	Mth::Vector mPos;
	//sint16 m_x,m_y,m_z;
	
	// MEMOPT: Change to be bitfield of flags.
	bool mHasPost;
	bool mHighlighted;
	
	// The height is stored so that if the ground moves up or down, mPos[Y] can be recalculated
	// so as to maintain the post height.
	float mHeightAboveGround;

	
	CEditedRailPoint *mpNext;	
	// TODO: MEMOPT
	// Don't really need the list to be doubly linked
	CEditedRailPoint *mpPrevious;	


	// The rail geometry joining the last point to this point.
	// If this is the first point of the rail, mpClonedRailSector will be NULL.
	Nx::CSector *mpClonedRailSector;	

	// The post geometry joining this point to the ground. May be NULL, since the user chooses
	// whether they want a post.
	// Also, mpPostSector may be NULL even when mHasPost is true, because we don't always want 
	// the geometry for the rail to exist.
	// For example, when loading a park off the memory card in the skateshop it will set the mPos
	// and mHasPost members, but the sector pointers will remain NULL until it is time to build the park.
	Nx::CSector *mpPostSector;
};

class CEditedRail : public Mem::CPoolable
{
public:
	CEditedRail();
	~CEditedRail();

	void Clear();

	CEditedRail *mpNext;	
	CEditedRail *mpPrevious;
	
	CEditedRailPoint *mpRailPoints;
	
	uint32 mId;

	CEditedRailPoint *AddPoint();

	void AdjustYs();
	void InitialiseHeights();
	void UpdateRailGeometry(Mth::Vector& rotateCentre=ZeroVector, Mth::Vector& newCentre=ZeroVector, float degrees=0.0f);
	void UpdatePostGeometry(Mth::Vector& rotateCentre=ZeroVector, Mth::Vector& newCentre=ZeroVector, float degrees=0.0f);
	void DestroyRailGeometry();
	void DestroyPostGeometry();
	
	int CountPoints();
	bool FindNearestRailPoint(Mth::Vector &pos, 
							  Mth::Vector *p_nearest_pos, float *p_dist_squared, int *p_rail_point_index, 
							  int ignore_index=-1);
							  
	int CountPointsInArea(float x0, float z0, float x1, float z1);
	void DuplicateAndAddPoint(CEditedRailPoint *p_point);
	CEditedRail *GenerateDuplicateRails(float x0, float z0, float x1, float z1, CEditedRail *p_head);	
							  
	enum EUpdateSuperSectors
	{
		DONT_UPDATE_SUPER_SECTORS=0,
		UPDATE_SUPER_SECTORS=1
	};	
	bool UpdateRailPointPosition(int rail_point_index, Mth::Vector &pos, EUpdateSuperSectors updateSuperSectors);
	CEditedRailPoint *GetRailPointFromIndex(int index);
	void UnHighlight();

	CEditedRailPoint *DeleteRailPoint(CEditedRailPoint *p_point);
	
	void CopyRail(CEditedRail *p_source_rail);
	void RotateAndTranslate(Mth::Vector& rotateCentre, Mth::Vector& newCentre, float degrees);

	SCompressedRailPoint *WriteCompressedRailPoints(SCompressedRailPoint *p_dest);

	bool ThereAreRailPointsOutsideArea(float x0, float z0, float x1, float z1);

	// Used by graffiti games
	void ModulateRailColor(int seqIndex);
	void ClearRailColor();

	void SetSectorActiveStatus(bool active);
	
	void GetDebugInfo( Script::CStruct* p_info );
};

class CInputComponent;
class CEditorCameraComponent;

class CRailEditorComponent : public CBaseComponent
{
	CInputComponent 		*mp_input_component;
	CEditorCameraComponent 	*mp_editor_camera_component;
	void get_pointers_to_required_components();

	void get_pos_from_camera_component(Mth::Vector *p_pos, float *p_height, float *p_angle);
		
	int count_rails();
	
	CEditedRail *mp_edited_rails;
	CEditedRail *mp_current_rail;
	
	// The mode that the cursor is in, ie FreeRoaming, RailLayout or Grab
	// This is stored as a checksum rather than an enum because it will mostly
	// be scripts that are doing logic based on the mode, and this way I don't
	// have to define a bunch of script global integers to match the enum.
	uint32 m_mode;
	
	uint32 m_dotted_line_sector_name;	
	Nx::CSector *mp_dotted_line_sector;

	enum
	{
		COMPRESSED_RAILS_BUFFER_SIZE = sizeof(uint16) + // Num rails
									   MAX_EDITED_RAILS * sizeof(uint16) + // Num points in each rail
									   MAX_EDITED_RAIL_POINTS * sizeof(SCompressedRailPoint), // The rail points
	};									   
		
	uint8 mp_compressed_rails_buffer[COMPRESSED_RAILS_BUFFER_SIZE];
	void generate_compressed_rails_buffer();
	void clear_compressed_rails_buffer();
	
	// Used by graffiti games
	CEditedRail *get_rail_from_cluster_name(uint32 clusterChecksum);
	
public:
    CRailEditorComponent();
    virtual ~CRailEditorComponent();

public:
	uint8 *							GetCompressedRailsBuffer();
	int								GetCompressedRailsBufferSize() {return COMPRESSED_RAILS_BUFFER_SIZE;}
	void							SetCompressedRailsBuffer(uint8 *p_buffer);
	void							InitUsingCompressedRailsBuffer();
	
	void							Clear();
	CEditedRail *					NewRail();
	bool							NewRail(const char *p_railName);
	void							DeleteRail(CEditedRail *p_rail);
	CEditedRail *					GetRail(uint32 id);
	void							RemoveEmptyAndSinglePointRails();
	
	int								GetTotalRailNodesRequired();
	void							SetUpRailNodes(Script::CArray *p_nodeArray, int *p_nodeNum, uint32 firstID);

	void							AdjustYs();
	void							InitialiseHeights();
	void							UpdateRailGeometry();
	void							UpdatePostGeometry();
	void							DestroyRailGeometry();
	void							DestroyPostGeometry();

	void							RefreshGeometry();
	void							UnHighlightAllRails();
	
	bool							DeleteRailPoint(CEditedRail *p_rail, CEditedRailPoint *p_point);

	enum EClipType
	{
		DELETE_POINTS_OUTSIDE=0,
		DELETE_POINTS_INSIDE=1
	};
	bool							ClipRail(CEditedRail *p_rail, float x0, float z0, float x1, float z1, EClipType clipType);
	void							ClipRails(float x0, float z0, float x1, float z1, EClipType clipType);
	bool							ThereAreRailPointsOutsideArea(float x0, float z0, float x1, float z1);
	

	int								CountPointsInArea(float x0, float z0, float x1, float z1);
	bool							AbleToCopyRails(float x0, float z0, float x1, float z1);
	CEditedRail *					GenerateDuplicateRails(float x0, float z0, float x1, float z1);
									
	bool							FindNearestRailPoint(Mth::Vector &pos, Mth::Vector *p_nearest_pos, 
														 float *p_dist_squared, 
														 uint32 *p_rail_id, int *p_rail_point_index,
														 uint32 ignore_rail_index=0, int ignore_rail_point_index=-1);

	
	void							DeleteDottedLine();
	void							DrawDottedLine(Mth::Vector& pos);

	int								GetNumFreePoints();
	int								GetNumFreeRails();
	
	// Used by graffiti games
	void							ModulateRailColor(uint32 clusterChecksum, int seqIndex);
	void							ClearRailColor(uint32 clusterChecksum);

	void							SetSectorActiveStatus(bool active);
	
	// A quick hack to allow UpdateSuperSectors not to be called when loading a new park in a net game.
	// If it is called, the whole park disappears due to all the sectors having been flagged for deletion.
	static bool						sUpdateSuperSectorsAfterDeletingRailSectors;
	
	// For reading/writing to memcard
	void							WriteIntoStructure(Script::CStruct *p_info);
	void							ReadFromStructure(Script::CStruct *p_info);

	virtual	void 					Finalize();
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );
	virtual void					Hide( bool shouldHide );

	static CBaseComponent*			s_create();
};

Obj::CRailEditorComponent *GetRailEditor();


// This will print the memory used by the rail editor component, via a compiler error.
// Need to keep it within 30K, since it exists in memory permanently.
//char p[(sizeof(CRailEditorComponent)+MAX_EDITED_RAIL_POINTS*sizeof(CEditedRailPoint)+MAX_EDITED_RAILS*sizeof(CEditedRail))/0];

} // namespace Obj

#endif // #ifndef __COMPONENTS_RAILEDITORCOMPONENT_H__


================================================
FILE: Code/Sk/Components/SkaterAdjustPhysicsComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterAdjustPhysicsComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/26/3
//****************************************************************************

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

void	TrackingLine2(int type, Mth::Vector &start, Mth::Vector &end)
{
	if	(Script::GetInt(CRCD(0x19fb78fa,"output_tracking_lines")))
	{
//		Gfx::AddDebugLine(start, end, 0xffffff);
		printf ("Tracking%d %.2f,%.2f,%.2f,%.2f,%.2f,%.2f\n",type, start[X], start[Y], start[Z], end[X], end[Y], end[Z]);
	}
}

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterAdjustPhysicsComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterAdjustPhysicsComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterAdjustPhysicsComponent::CSkaterAdjustPhysicsComponent() : CBaseComponent()
{
	SetType( CRC_SKATERADJUSTPHYSICS );
	
	mp_core_physics_component = NULL;
	mp_model_component = NULL;
	mp_shadow_component = NULL;
	mp_movable_contact_component = NULL;
	
	m_uber_frigged_this_frame = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterAdjustPhysicsComponent::~CSkaterAdjustPhysicsComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterAdjustPhysicsComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterAdjustPhysicsComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterAdjustPhysicsComponent::Finalize (   )
{
	mp_core_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
	mp_model_component = GetModelComponentFromObject(GetObject());
	mp_shadow_component = GetShadowComponentFromObject(GetObject());
	mp_movable_contact_component = GetMovableContactComponentFromObject(GetObject());
	mp_state_component = GetSkaterStateComponentFromObject(GetObject());

	Dbg_Assert(mp_core_physics_component);
	Dbg_Assert(mp_model_component);
	Dbg_Assert(mp_shadow_component);
	Dbg_Assert(mp_movable_contact_component);
	Dbg_Assert(mp_state_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterAdjustPhysicsComponent::Update()
{
	// If on the ground, or moving downwards, then allow us to hit a car again		
	if (mp_core_physics_component->GetState() == GROUND || GetObject()->m_vel[Y] < 0.0f)
	{
		mp_core_physics_component->SetFlagTrue(CAN_HIT_CAR);
	}

	if (!mp_core_physics_component->GetFlag(SKITCHING))
	{
		check_inside_objects();
	}

	uber_frig();

	GetObject()->m_old_pos = GetObject()->m_pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterAdjustPhysicsComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterAdjustPhysicsComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterAdjustPhysicsComponent::GetDebugInfo"));
	
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
					
void CSkaterAdjustPhysicsComponent::uber_frig (   )
{
	// Uberfrig() will attempt to see if we are in a region with valid collision
	// if were are not, then it will attempt to move us back to a position that is valid; generally this is the old position
	
	m_uber_frigged_this_frame = false;

	if (GetObject()->m_pos != mp_core_physics_component->m_safe_pos)
	{		
		#ifdef	__NOPT_ASSERT__
		if (! (Tmr::GetRenderFrame() & 15) )
		{
			TrackingLine2(4, GetObject()->m_old_pos, GetObject()->m_pos);	  // 4 = line
		}
		#endif
		
		mp_core_physics_component->m_col_start = GetObject()->m_pos;
		mp_core_physics_component->m_col_end = GetObject()->m_pos;

		// Very minor adjustment to move origin away from vert walls
		mp_core_physics_component->m_col_start += GetObject()->m_matrix[Y] * 0.001f;
		
		mp_core_physics_component->m_col_start[Y] += 8.0f;
		mp_core_physics_component->m_col_end[Y] -= FEET(400);
			   
		if (mp_core_physics_component->get_member_feeler_collision())
		{
			// if we are in "contact" with something, and on the ground or in air, then that should be what we collide with
			// so if not, we need to leave contact, and impart the final velocity
			// ("on ground" just means skating on something, which could be the moving object
			if (!mp_core_physics_component->GetFlag(SKITCHING) && !mp_core_physics_component->GetFlag(SPINE_PHYSICS))
			{
				// but we leave it if skitching, or attempting to spin between them
				if (mp_movable_contact_component->HaveContact()
					&& (mp_core_physics_component->GetState() == GROUND || mp_core_physics_component->GetState() == AIR))
				{
					if (!mp_core_physics_component->m_feeler.IsMovableCollision() 
						|| mp_core_physics_component->m_feeler.GetMovingObject() != mp_movable_contact_component->GetContact()->GetObject())
					{
						GetObject()->m_vel += mp_movable_contact_component->GetContact()->GetObject()->GetVel();
						DUMP_VELOCITY;
						mp_movable_contact_component->LoseAnyContact();
					}
				}
			}
			
			static Mth::Vector xa, xb, xc;

			float height = GetObject()->m_pos[Y] - mp_core_physics_component->m_feeler.GetPoint()[Y];

			mp_state_component->m_height = height;
			
			// if we are below the ground, then move him up
			if (height < 0.001f)
			{
				GetObject()->m_pos[Y] = mp_core_physics_component->m_feeler.GetPoint()[Y] + 0.001f; 	// above ground by a fraction of an inch
				DUMP_POSITION;
				height = 0.0f;
			}

			if (mp_core_physics_component->GetState() != RAIL)
			{
				mp_model_component->ApplyLightingFromCollision(mp_core_physics_component->m_feeler);
			}
			
			// Store these values off for the simple shadow calculation.
			mp_shadow_component->SetShadowPos(mp_core_physics_component->m_feeler.GetPoint());
			mp_shadow_component->SetShadowNormal(mp_core_physics_component->m_feeler.GetNormal()); 
		
			if (Ed::CParkEditor::Instance()->UsingCustomPark())
			{
				// when we are in the park editor, there are other checks we might want to do
				CFeeler feeler;
				
				// ignore non-collidable, under-ok
				feeler.SetIgnore(mFD_NON_COLLIDABLE | mFD_UNDER_OK, 0);
				feeler.SetStart(GetObject()->m_pos + Mth::Vector(0.0f, 3000.0f, 0.0f, 0.0f));
				feeler.SetEnd(GetObject()->m_pos + GetObject()->m_matrix[Y]);
				if (feeler.GetCollision())
				{
					// Something above me that I'm not supposed to be under; just move the skater back to the old pos, and flip him
					
					// if only just under it, then pop me up
					if ((GetObject()->m_pos - feeler.GetPoint()).LengthSqr() < 6.0f * 6.0f)
					{
						GetObject()->m_pos = feeler.GetPoint();
						DUMP_POSITION;
					}
					else
					{
						// if we are not moving, then pop us up above the face we detected a collision with
						if ((GetObject()->m_pos - mp_core_physics_component->m_safe_pos).LengthSqr() < 1.0f * 1.0f)
						{
							GetObject()->m_pos = feeler.GetPoint();
							DUMP_POSITION;
						}
						else
						{
							GetObject()->m_pos = mp_core_physics_component->m_safe_pos;
							DUMP_POSITION;
						}
						GetObject()->m_vel = GetObject()->m_vel * (-1.0f / 2.0f);
						DUMP_VELOCITY;
						if (mp_core_physics_component->GetState() == RAIL)
						{
							mp_core_physics_component->SetState(AIR);			// Stop grinding
							GetObject()->SelfEvent(CRCD(0xafaa46ba, "OffRail"));					// regular exception
							GetObject()->m_pos[Y] += 2.0f;								// make sure we are out of the rail...
							DUMP_POSITION;
						}
					}
				}
			}
			
			m_nudge = 0;	
			
			 // Mick:  Set a safe position.  Here there was a collision, so safe
			mp_core_physics_component->m_safe_pos = GetObject()->m_pos;
			
		}
		else
		{
			// I'm going to assume that if we are on a rail, then the uberfrig is just a problem with the collision getting parallel to the wall		
			// so I can safely ignore it, as no rail should be laid out into nothingness
			// except in the park editor, dammit...
			if ((mp_core_physics_component->GetState() != RAIL && mp_core_physics_component->GetState() != LIP)
				|| Ed::CParkEditor::Instance()->UsingCustomPark())
			{
				m_uber_frigged_this_frame = true;
				
				// first nudge is zero
				static Mth::Vector uberfrig_nudges[] =
				{
					Mth::Vector(0.0f, 0.0f, 0.0f),
					Mth::Vector(0.1f, 0.1f, 0.1f),
					Mth::Vector(-0.2f, 0.0f, 0.0f),
					Mth::Vector(0.0f, 0.0f, -0.2f),
					Mth::Vector(0.2f, 0.0f, 0.0f)
				};
				
				if (m_nudge < 5)
				{
					GetObject()->m_pos = mp_core_physics_component->m_safe_pos + uberfrig_nudges[m_nudge++];
					DUMP_POSITION;
				}
				else
				{
					// just reset it, and hope for the best.  Probably we are just over a seam, and will get over it.
					m_nudge = 0;
				}
				
				GetObject()->m_vel = -GetObject()->m_vel + Mth::Vector(0.101f, 0.101f, 0.1001f);
				DUMP_VELOCITY;
				
				// special case for wallrides, lip tricks and suchlike
				// just change them to air state; bit of a patch, but prevent you getting stuck in a state													
				if (mp_core_physics_component->GetState() != GROUND && mp_core_physics_component->GetState() != AIR)
				{
					mp_core_physics_component->SetState(AIR);
				}
			}
			
			mp_state_component->m_height = 0.0f;
		}
	} 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterAdjustPhysicsComponent::check_inside_objects (   )
{
	if (mp_movable_contact_component->CheckInsideObjects(GetObject()->m_pos, mp_core_physics_component->m_safe_pos))
	{
		MESSAGE("found to be inside a moving object");
		
		// Whichever way we rcover from being inside a moving object, this abrupt change in velocity and position means any rail grinding might
		// be messed up, either with zero velocity, or being moved away from the rail; so, the best thing to do is skate off the rail

		if (mp_core_physics_component->GetState() == RAIL)
		{
			mp_core_physics_component->SetState(AIR);
			GetObject()->SelfEvent(CRCD(0xafaa46ba, "OffRail"));
		}
		else if (GetObject()->m_pos == mp_core_physics_component->m_safe_pos)
		{
			// Set velocity to zero, otherwise we can get stuck inside things as the velocity from this frame will keep pushing you back inside things
			// especially if m_safe_pos is just inside the object, when there will be no collision detected
			// If we are on a rail, let them keep the velocity as maybe we can handle it next frame
			GetObject()->GetVel().Set();
		}
	}
}

}


================================================
FILE: Code/Sk/Components/SkaterAdjustPhysicsComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterAdjustPhysicsComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/26/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERADJUSTPHYSICSCOMPONENT_H__
#define __COMPONENTS_SKATERADJUSTPHYSICSCOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERADJUSTPHYSICS CRCD(0x9f9cc949, "SkaterAdjustPhysics")

#define		GetSkaterAdjustPhysicsComponent() ((Obj::CSkaterAdjustPhysicsComponent*)GetComponent(CRC_SKATERADJUSTPHYSICS))
#define		GetSkaterAdjustPhysicsComponentFromObject(pObj) ((Obj::CSkaterAdjustPhysicsComponent*)(pObj)->GetComponent(CRC_SKATERADJUSTPHYSICS))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSkaterCorePhysicsComponent;
	class CModelComponent;
	class CShadowComponent;
	class CMovableContactComponent;
	class CSkaterStateComponent;

class CSkaterAdjustPhysicsComponent : public CBaseComponent
{
	friend CSkaterNonLocalNetLogicComponent;
	
public:
    CSkaterAdjustPhysicsComponent();
    virtual ~CSkaterAdjustPhysicsComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
	bool							UberFriggedThisFrame (  ) { return m_uber_frigged_this_frame; }
	
private:
	void							uber_frig (   );
	void							check_inside_objects (   );
	
private:
	int								m_nudge;						// counter for which nudges to attempt if we get stuck in an uberfrig

	bool							m_uber_frigged_this_frame;
	
	// peer components
	CSkaterCorePhysicsComponent*	mp_core_physics_component;
	CModelComponent*				mp_model_component;
	CShadowComponent*				mp_shadow_component;
	CMovableContactComponent*		mp_movable_contact_component;
	CSkaterStateComponent*			mp_state_component;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterBalanceTrickComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterBalanceTrickComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  328/3
//****************************************************************************

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent* CSkaterBalanceTrickComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterBalanceTrickComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterBalanceTrickComponent::CSkaterBalanceTrickComponent() : CBaseComponent()
{
	SetType( CRC_SKATERBALANCETRICK );
	
	mp_animation_component = NULL;
	
	mpBalanceParams = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterBalanceTrickComponent::~CSkaterBalanceTrickComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::InitFromStructure( Script::CStruct* pParams )
{
	mManual.Init(GetSkater());
	mGrind.Init(GetSkater());
	mLip.Init(GetSkater());
	mSkitch.Init(GetSkater());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::Finalize (   )
{
	
	mp_animation_component = GetAnimationComponentFromObject(GetObject());
	
	Dbg_Assert(mp_animation_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::Update()
{
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterBalanceTrickComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | SetWobbleDetails | gets called from the animation
		// component's PlayAnim handler (needs to come after the
		// animation has launched...)
		case CRCC(0xea6d0efd,"SetWobbleDetails"):
		{
			// Intercepts the animation component's PlayAnim member function and handle's skater specific logic
			
			// If this is a wobbling balance anim, then allow the manual or grind to programmatically wobble it.
			if (pParams->ContainsFlag(CRCD(0x6d941203, "Wobble")))
			{
				mManual.EnableWobble();
				mGrind.EnableWobble();
				mLip.EnableWobble();
				mSkitch.EnableWobble();
			}	
			
			Script::CStruct* p_wobble_params = Script::GetStructure(CRCD(0x5adb96d4, "DefaultWobbleParams"), Script::ASSERT);
			pParams->GetStructure(CRCD(0xd095379c, "WobbleParams"), &p_wobble_params);
			set_wobble_params(p_wobble_params);
			
			return CBaseComponent::MF_TRUE;
		}
		
        // @script | DoBalanceTrick | 
        // @parmopt int | Tweak | 0 | 
        // @parm name | ButtonA | 
        // @parm name | ButtonB |                       
        // @parmopt name | Type | 0 | balance trick type (NoseManual, 
        // Manual, Grind, Slide, Lip)
        // @flag DoFlipCheck | If set, the range anim will be played backwards if the skater is flipped.
        // @flag PlayRangeAnimBackwards | If set, the range anim will be played backwards. Can be
		// used in conjunction with DoFlipCheck. Ie, if both flags are set, then the anim will be
		// played forwards if the skater is flipped.
		// @flag ClearCheese | If set this will reset any previous cheese.
        // @parmopt structure | BalanceParams | | If not specified then the balance trick will use
		// the params defined by the script globals LipParams, or ManualParams etc depending on the
		// Type value. 
		case CRCC(0x7e92670c, "DoBalanceTrick"):	  
		{
			if (mpBalanceParams)
			{
				delete mpBalanceParams;
				mpBalanceParams = NULL;
			}
			Script::CStruct* p_balance_params = NULL;
			if (pParams->GetStructure(CRCD(0x647facc2, "BalanceParams"), &p_balance_params))
			{
				set_balance_trick_params(p_balance_params);
			}
			
			int Tweak = 0;
			pParams->GetInteger(CRCD(0x2bfd3d35, "Tweak"), &Tweak);
			
			uint32 ButtonA = 0;
			uint32 ButtonB = 0;
			pParams->GetChecksum(CRCD(0x4f1a0b01, "ButtonA"), &ButtonA, Script::ASSERT);
			pParams->GetChecksum(CRCD(0xd6135abb, "ButtonB"), &ButtonB, Script::ASSERT);
			
			uint32 NewBalanceTrickType = 0;
			pParams->GetChecksum(CRCD(0x7321a8d6, "Type"), &NewBalanceTrickType);

			// Do nothing if already doing the same type of balance trick.  This is so that the meter does not glitch when switching to
			// a balance trick from another balance trick where there is no StopBalanceTrick script command in between.
			if (mBalanceTrickType == NewBalanceTrickType) break;
			mBalanceTrickType = NewBalanceTrickType;
			
			bool DoFlipCheck = pParams->ContainsFlag(CRCD(0x1304e677, "DoFlipCheck"));
			bool PlayRangeAnimBackwards = pParams->ContainsFlag(CRCD(0x8fe31ed, "PlayRangeAnimBackwards"));
			
			// This needed because for the extra grind tricks (eg left triangle triangle) the
			// DoBalanceTrick command gets called twice in a row, once for the first time the grind is detected
			// by the first triangle, then again once the extra trick is detected.
			// In that case the cheese needs to be reset otherwise it thinks you've jumped and re-railed & you
			// fall off straight away.
			bool clear_cheese=pParams->ContainsFlag(CRCD(0xbbdb0f78,"ClearCheese"));
				
			switch (mBalanceTrickType)
			{
				case 0:
					Dbg_MsgAssert(false, ("\n%s\nMust specify a type in DoBalanceTrick, eg Type=NoseManual", pScript->GetScriptInfo()));
					break;
					
				case CRCC(0xac90769, "NoseManual"):
				case CRCC(0xef24413b, "Manual"):
					if (clear_cheese)
					{
						mManual.Reset();
					}	
					mManual.UpdateRecord();
					mManual.SetUp(ButtonA, ButtonB, Tweak, DoFlipCheck, PlayRangeAnimBackwards);
					break;
					
				case CRCC(0x255ed86f, "Grind"):
				case CRCC(0x8d10119d, "Slide"):
					if (clear_cheese)
					{
						mGrind.Reset();
					}	
					mGrind.UpdateRecord();
					mGrind.SetUp(ButtonA, ButtonB, Tweak, DoFlipCheck, PlayRangeAnimBackwards);
					break;
					
				case CRCC(0xa549b57b, "Lip"):
					if (clear_cheese)
					{
						mLip.Reset();
					}	
					mLip.UpdateRecord();
					mLip.SetUp(ButtonA, ButtonB, Tweak, DoFlipCheck, PlayRangeAnimBackwards);
					break;
					
				case CRCC(0x3506ce64, "Skitch"):
					if (clear_cheese)
					{
						mSkitch.Reset();
					}	
					mSkitch.UpdateRecord();
					mSkitch.SetUp(ButtonA, ButtonB, Tweak, DoFlipCheck, PlayRangeAnimBackwards);
					break;
					
				default:	
					Dbg_Assert(false);
					break;
			}		
			break;
		}
		
        // @script | AdjustBalance | Make minor adjustments, nudges, to the current balance trick, if any
        // @parmopt float | TimeAdd | 0.0 | 
        // @parmopt float | LeanAdd | 0.0 | 
        // @parmopt float | SpeedAdd | 0.0 | 
        // @parmopt float | TimeMult | 1.0 | 
        // @parmopt float | LeanMult | 1.0 | 
        // @parmopt float | SpeedMult | 1.0 | 
		case CRCC(0x7a0dcd4b, "AdjustBalance"):
		{
			if (mBalanceTrickType)
			{
				float TimeMult = 1.0f;
				float LeanMult = 1.0f;
				float SpeedMult = 1.0f;
				float TimeAdd = 0.0f;
				float LeanAdd = 0.0f;
				float SpeedAdd = 0.0f;
				pParams->GetFloat(CRCD(0x3a4e9413, "TimeAdd"), &TimeAdd);
				pParams->GetFloat(CRCD(0xb4cecc1, "LeanAdd"), &LeanAdd);
				pParams->GetFloat(CRCD(0xeb9ecabb, "SpeedAdd"), &SpeedAdd);
				pParams->GetFloat(CRCD(0x4c074698, "TimeMult"), &TimeMult);
				pParams->GetFloat(CRCD(0x24ebf718, "LeanMult"), &LeanMult);
				pParams->GetFloat(CRCD(0x94dbbd1c, "SpeedMult"), &SpeedMult);

				// Get whichever balance trick we are doing	
				CManual* pBalance;
				switch (mBalanceTrickType)
				{
					case CRCC(0x255ed86f, "Grind"):
					case CRCC(0x8d10119d, "Slide"):
						pBalance = &mGrind;
						break;
						
					case CRCC(0xa549b57b, "Lip"):
						pBalance = &mLip;
						break;
					
					case CRCC(0x3506ce64, "Skitch"):
						pBalance = &mSkitch;
						break;
						
					case CRCC(0xac90769, "NoseManual"):
					case CRCC(0xef24413b, "Manual"):
						pBalance = &mManual;
						break;
						
					default:
						pBalance = NULL;
						Dbg_Assert(false);
				}
	
				pBalance->mManualTime *= TimeMult;
				pBalance->mManualTime += TimeAdd;
				if (pBalance->mManualTime < 0.0f)
				{
					pBalance->mManualTime = 0.0f;
				}
				pBalance->mManualLean *= LeanMult;
				pBalance->mManualLean += LeanAdd * Mth::Sgn(pBalance->mManualLean);
				pBalance->mManualLeanDir *= SpeedMult;
				pBalance->mManualLeanDir += SpeedAdd  * Mth::Sgn(pBalance->mManualLeanDir);
			}
			break;
		}

        // @script | StopBalanceTrick | 
		case CRCC(0xe553a5b8, "StopBalanceTrick"):
			stop_balance_trick();
			break;
		
        // @script | StartBalanceTrick | 
		case CRCC(0x6cc475b7, "StartBalanceTrick"):
			mDoingBalanceTrick = true;
			break;
			
		case CRCC(0x78e669ab, "SwitchOffBalanceMeter"):
		{
			CSkaterScoreComponent* p_score_component = GetSkaterScoreComponentFromObject(GetObject());
			Dbg_Assert(p_score_component);
			
			p_score_component->GetScore()->SetBalanceMeter(false);
			p_score_component->GetScore()->SetManualMeter(false);
			
			mManual.SwitchOffMeters();
			mGrind.SwitchOffMeters();
			mLip.SwitchOffMeters();
			mSkitch.SwitchOffMeters();
			break;
		}
			
        // @script | SwitchOnBalanceMeter |
		case CRCC(0x73727701, "SwitchOnBalanceMeter"):
			mManual.SwitchOnMeters();
			mGrind.SwitchOnMeters();
			mLip.SwitchOnMeters();
			mSkitch.SwitchOnMeters();
			break;
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterBalanceTrickComponent::GetDebugInfo"));
	
	p_info->AddChecksum("mDoingBalanceTrick", mDoingBalanceTrick ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	p_info->AddChecksum("mBalanceTrickType", mBalanceTrickType);

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::ClearMaxTimes (   )
{
   	mManual.ClearMaxTime();
	mGrind.ClearMaxTime();
	mLip.ClearMaxTime();
	mSkitch.ClearMaxTime();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::UpdateRecord (   )
{
	mManual.UpdateRecord();		   
	mGrind.UpdateRecord();		   
	mLip.UpdateRecord();		   
	mSkitch.UpdateRecord();		   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::Reset (   )
{
	mManual.Reset();
	mGrind.Reset();
	mSkitch.Reset();
	mLip.Reset();
	
	mBalanceTrickType = 0;
	mDoingBalanceTrick = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkaterBalanceTrickComponent::ExcludeBalanceButtons ( int& numButtonsToIgnore, uint32 pButtonsToIgnore[] )
{
	// If a balance trick is being done, then this will stop the buttons being used to control the balance from
	// generating the events used to trigger tricks.
	// Otherwise, ChrisR can't do kickflips by very quickly jumping out of a grind & kickflipping by rolling
	// from X to Square.
	
	// Note, in case of problems later:
	// Now, potentially a button could be pressed just before the balance trick,
	// then released during the balance trick, and the release would not get recorded as an event.
	// So if the event buffer were analysed, it would appear that the button was still pressed.
	// However, this does not seem to cause any problems, because generally if we want to know if a button
	// is pressed right now we just read the pad. Just noting this if any problems occur later.
	switch (mBalanceTrickType)
	{
		case CRCC(0xac90769, "NoseManual"):
		case CRCC(0xef24413b, "Manual"):
			numButtonsToIgnore = 2;
			pButtonsToIgnore[0] = mManual.mButtonAChecksum;
			pButtonsToIgnore[1] = mManual.mButtonBChecksum;
			break;
			
		case CRCC(0xa549b57b, "Lip"):
			numButtonsToIgnore = 2;
			pButtonsToIgnore[0] = mLip.mButtonAChecksum;
			pButtonsToIgnore[1] = mLip.mButtonBChecksum;
			break;
			
		case CRCC(0x255ed86f, "Grind"):
		case CRCC(0x8d10119d, "Slide"):
			numButtonsToIgnore = 2;
			pButtonsToIgnore[0] = mGrind.mButtonAChecksum;
			pButtonsToIgnore[1] = mGrind.mButtonBChecksum;
			break;			
			
		case CRCC(0x3506ce64, "Skitch"):
			numButtonsToIgnore = 2;
			pButtonsToIgnore[0] = mSkitch.mButtonAChecksum;
			pButtonsToIgnore[1] = mSkitch.mButtonBChecksum;
			break;
			
		default:	
			numButtonsToIgnore = 0;
			break;
	}		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CSkaterBalanceTrickComponent::GetBalanceStat ( uint32 Checksum )
{
	// If some custom params have been set, use them.
	if (mpBalanceParams)
	{
		return GetSkater()->GetScriptedStat(Checksum, mpBalanceParams);
	}
		
	switch (mBalanceTrickType)
	{
		case CRCC(0xac90769, "NoseManual"):
		case CRCC(0xef24413b, "Manual"):
			return GetSkater()->GetScriptedStat(Checksum, Script::GetStructure(CRCD(0xb8cc8633, "ManualParams")));
			
		case CRCC(0x255ed86f, "Grind"):
		case CRCC(0x8d10119d, "Slide"):
			return GetSkater()->GetScriptedStat(Checksum, Script::GetStructure(CRCD(0x6b7fdadd, "GrindParams")));
			
		case CRCC(0xa549b57b, "Lip"):
			return GetSkater()->GetScriptedStat(Checksum, Script::GetStructure(CRCD(0xbe615d0a, "LipParams")));
		
		case CRCC(0x3506ce64, "Skitch"):
			return GetSkater()->GetScriptedStat(Checksum, Script::GetStructure(CRCD(0xfc87cc01, "SkitchParams")));
			
		default:
			Dbg_Assert(false);
			return 0.0f;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkaterBalanceTrickComponent::ClearBalanceParameters (   )
{
	if (mpBalanceParams)
	{
		delete mpBalanceParams;
		mpBalanceParams = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::stop_balance_trick (   )
{
	// Stops doing any balance trick.
	// Called when going into the lip state, and also called by the StopBalanceTrick script command.
	mManual.Stop();	
	mGrind.Stop();	
	mLip.Stop();	
	mSkitch.Stop();
		
	mBalanceTrickType = 0;
	mDoingBalanceTrick = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::set_wobble_params ( Script::CStruct *pParams )
{
	if (!GetObject()->GetScript())
	{
		GetObject()->SetScript(new Script::CScript);
	}

	Dbg_Assert(pParams);

	// Extract all the wobble details and stick them in WobbleDetails, then send that to SetWobbleDetails
	
	Script::CStruct* pStat = NULL;
	Gfx::SWobbleDetails theWobbleDetails;

	pParams->GetStructure(CRCD(0xfd266a26, "WobbleAmpA"), &pStat, Script::ASSERT);
	theWobbleDetails.wobbleAmpA = GetSkater()->GetScriptedStat(pStat);

	pParams->GetStructure(CRCD(0x642f3b9c, "WobbleAmpB"), &pStat, Script::ASSERT);
	theWobbleDetails.wobbleAmpB = GetSkater()->GetScriptedStat(pStat);

	pParams->GetStructure(CRCD(0xf43fd49, "WobbleK1"), &pStat, Script::ASSERT);
	theWobbleDetails.wobbleK1 = GetSkater()->GetScriptedStat(pStat);

	pParams->GetStructure(CRCD(0x964aacf3, "WobbleK2"), &pStat, Script::ASSERT);
	theWobbleDetails.wobbleK2 = GetSkater()->GetScriptedStat(pStat);

	pParams->GetStructure(CRCD(0xf90b0824, "SpazFactor"), &pStat, Script::ASSERT);
	theWobbleDetails.spazFactor = GetSkater()->GetScriptedStat(pStat);

	mp_animation_component->SetWobbleDetails(theWobbleDetails, true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterBalanceTrickComponent::set_balance_trick_params ( Script::CStruct *pParams )
{
	Dbg_Assert(pParams);

	if (!mpBalanceParams)
	{
		mpBalanceParams = new Script::CStruct;
	}
	mpBalanceParams->Clear();
	mpBalanceParams->AppendStructure(pParams);	
}

}


================================================
FILE: Code/Sk/Components/SkaterBalanceTrickComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterBalanceTrickComponent.h
//* OWNER:          Dan
//* CREATION DATE:  328/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERBALANCETRICKCOMPONENT_H__
#define __COMPONENTS_SKATERBALANCETRICKCOMPONENT_H__

#include 
#include 

#include 

#include 
#include 

#define		CRC_SKATERBALANCETRICK CRCD(0x589e5fb3, "SkaterBalanceTrick")

#define		GetSkaterBalanceTrickComponent() ((Obj::CSkaterBalanceTrickComponent*)GetComponent(CRC_SKATERBALANCETRICK))
#define		GetSkaterBalanceTrickComponentFromObject(pObj) ((Obj::CSkaterBalanceTrickComponent*)(pObj)->GetComponent(CRC_SKATERBALANCETRICK))

namespace Script
{
    class CScript;
    class CStruct;
}
	
namespace Mdl
{
	class Skate;
}
              
namespace Obj
{
	class CAnimationComponent;

class CSkaterBalanceTrickComponent : public CBaseComponent
{
	friend Mdl::Skate;
	friend class CSkater;
	friend class CManual;
	friend class CSkaterCam;
	friend class CSkaterCameraComponent;
	friend class CSkaterCorePhysicsComponent;
	
public:
    CSkaterBalanceTrickComponent();
    virtual ~CSkaterBalanceTrickComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
	uint32							GetBalanceTrickType (   ) { return mBalanceTrickType; }
	bool							DoingBalanceTrick (   ) { return mDoingBalanceTrick; }
	
	void							ClearMaxTimes (   );
	void							UpdateRecord (   );
	void							Reset (   );
	void							ExcludeBalanceButtons ( int& numButtonsToIgnore, uint32 pButtonsToIgnore[] );
	float							GetBalanceStat ( uint32 Checksum );
	void							ClearBalanceParameters (   );
	
private:
	void							stop_balance_trick (   );
	void							set_balance_trick_params ( Script::CStruct *pParams );
	void							set_wobble_params ( Script::CStruct *pParams );
	
private:
	uint32 							mBalanceTrickType;				// Nosemanual,manual,grind or slide (checksum of)
	bool   							mDoingBalanceTrick;				// set in script when we are doing a balance trick, but not actually into it yet
	
	Script::CStruct*				mpBalanceParams;
	
	CManual 						mManual;
	CManual 						mGrind;
	CManual 						mLip;
	CManual 						mSkitch;
	
	// peer components
	CAnimationComponent*			mp_animation_component;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterCleanupStateComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterCleanupStateComponent.cpp
//* OWNER:        	Dan
//* CREATION DATE:  3/26/3
//****************************************************************************

#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterCleanupStateComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterCleanupStateComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterCleanupStateComponent::CSkaterCleanupStateComponent() : CBaseComponent()
{
	SetType( CRC_SKATERCLEANUPSTATE );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterCleanupStateComponent::~CSkaterCleanupStateComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCleanupStateComponent::InitFromStructure( Script::CStruct* pParams )
{
	mp_state_component = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCleanupStateComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCleanupStateComponent::Finalize(   )
{
	mp_state_component = GetSkaterStateComponentFromObject(GetObject());
	
	Dbg_Assert(mp_state_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCleanupStateComponent::Update()
{
	// Make sure the sparks are off if not on a rail
	if (GetSkater()->mSparksRequireRail && mp_state_component->GetState() != RAIL)
	{
		GetSkater()->SparksOff();
	}
		
	// Dan: shouldn't need to do this; the suspend components update themselves
	// Perform LOD/culling/occluding
	// NOTE: Move to CModeldComponent::Update?  Ask Gary about this.
	// GetSuspendComponentFromObject(GetObject())->CheckModelActive();
	
	// Don't update the shadow if running a replay. The replay code will update it, using the replay dummy skater's position and matrix.
	// if (!Replay::RunningReplay())
	// {
	GetSkater()->UpdateShadow(GetObject()->m_pos, GetObject()->m_matrix);
	// }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterCleanupStateComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCleanupStateComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterCleanupStateComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}
	
}


================================================
FILE: Code/Sk/Components/SkaterCleanupStateComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterCleanupStateComponent.h
//* OWNER:        	Dan
//* CREATION DATE:  3/26/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERCLEANUPSTATECOMPONENT_H__
#define __COMPONENTS_SKATERCLEANUPSTATECOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERCLEANUPSTATE CRCD(0xd8cc3428, "SkaterCleanupState")

#define		GetSkaterCleanupStateComponent() ((Obj::CSkaterCleanupStateComponent*)GetComponent(CRC_SKATERCLEANUPSTATE))
#define		GetSkaterCleanupStateComponentFromObject(pObj) ((Obj::CSkaterCleanupStateComponent*)(pObj)->GetComponent(CRC_SKATERCLEANUPSTATE))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSkaterStateComponent;

class CSkaterCleanupStateComponent : public CBaseComponent
{
public:
    CSkaterCleanupStateComponent();
    virtual ~CSkaterCleanupStateComponent();

public:
    virtual void            		Update();
	virtual void					Finalize(   );
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
private:
	CSkaterStateComponent*			mp_state_component;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterCorePhysicsComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterCorePhysicsComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/21/3
//****************************************************************************

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 

#define	FLAGEXCEPTION(X) GetObject()->SelfEvent(X)
							
void	TrackingLine(int type, Mth::Vector &start, Mth::Vector &end)
{
	if	(Script::GetInt(CRCD(0x19fb78fa,"output_tracking_lines")))
	{
//		Gfx::AddDebugLine(start, end, 0xffffff);
		printf ("Tracking%d %.2f,%.2f,%.2f,%.2f,%.2f,%.2f\n",type, start[X], start[Y], start[Z], end[X], end[Y], end[Z]);
	}
}
 
extern bool g_CheatsEnabled;

namespace Obj
{
	Mth::Vector acid_hold;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterCorePhysicsComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterCorePhysicsComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterCorePhysicsComponent::CSkaterCorePhysicsComponent() : CBaseComponent()
{
	SetType( CRC_SKATERCOREPHYSICS );
	
	mp_input_component = NULL;
	mp_trigger_component = NULL;
	mp_sound_component = NULL;
	mp_trick_component = NULL;
	mp_rotate_component = NULL;
	mp_score_component = NULL;
	mp_balance_trick_component = NULL;
	mp_state_component = NULL;
	mp_movable_contact_component = NULL;
	mp_physics_control_component = NULL;
	mp_walk_component = NULL;
	
	mp_coll_cache = Nx::CCollCacheManager::sCreateCollCache();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterCorePhysicsComponent::~CSkaterCorePhysicsComponent()
{
	Nx::CCollCacheManager::sDestroyCollCache(mp_coll_cache);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::InitFromStructure( Script::CStruct* pParams )
{
	Dbg_MsgAssert(GetObject()->GetType() == SKATE_TYPE_SKATER, ("CSkaterCorePhysicsComponent added to non-skater composite object"));
	
	m_rolling_friction = GetPhysicsFloat(CRCD(0x78f80ec4, "Physics_Rolling_Friction"));
	
    GetPos().Set(0.0f, 0.0f, 0.0f);
	DUMP_POSITION;

	GetMatrix().Identity();
    ResetLerpingMatrix();

	m_display_normal.Set(0.0f, 1.0f, 0.0f);
	m_current_normal = m_display_normal;
	m_last_display_normal = m_display_normal;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::Finalize (   )
{
	mp_input_component = GetInputComponentFromObject(GetObject());
	mp_sound_component = GetSkaterSoundComponentFromObject(GetObject());
	mp_trigger_component = GetTriggerComponentFromObject(GetObject());
	mp_trick_component = GetTrickComponentFromObject(GetObject());
	mp_rotate_component = GetSkaterRotateComponentFromObject(GetObject());
	mp_score_component = GetSkaterScoreComponentFromObject(GetObject());
	mp_balance_trick_component = GetSkaterBalanceTrickComponentFromObject(GetObject());
	mp_state_component = GetSkaterStateComponentFromObject(GetObject());
	mp_movable_contact_component = GetMovableContactComponentFromObject(GetObject());
	mp_physics_control_component = GetSkaterPhysicsControlComponentFromObject(GetObject());
	mp_walk_component = GetWalkComponentFromObject(GetObject());
	
	Dbg_Assert(mp_input_component);
	Dbg_Assert(mp_sound_component);
	Dbg_Assert(mp_trigger_component);
	Dbg_Assert(mp_trick_component);
	Dbg_Assert(mp_rotate_component);
	Dbg_Assert(mp_score_component);
	Dbg_Assert(mp_balance_trick_component);
	Dbg_Assert(mp_movable_contact_component);
	Dbg_Assert(mp_physics_control_component);
	Dbg_Assert(mp_walk_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::Update()
{
	DUMP_POSITION;
	
	m_frame_length = Tmr::FrameLength();
	
	// bool up = GetVel()[Y] > 0.0f;

	m_landed_this_frame = false;

	m_began_frame_in_lip_state = GetState() == LIP;
	m_began_frame_in_transfer = GetFlag(SPINE_PHYSICS);
	
	handle_tensing();

	limit_speed();

	SetFlagFalse(SNAPPED_OVER_CURB);  // this flag only gets set for one frame, to fix camera snaps
	SetFlagFalse(SNAPPED);  // this flag only gets set for one frame, to fix camera snaps
	
	setup_default_collision_cache();
	
	switch (GetState())
	{
		case GROUND:
			// Mick: Remember the last ground position for calculating which side of the rail we're on later.
			// Note, we do this BEFORE we move as the movement might take us off the ground
			m_last_ground_pos = GetPos();
			do_on_ground_physics();
			maybe_skitch();
			break;
			
		case AIR:
			do_in_air_physics();
			if (GetState() == GROUND)
			{
				m_landed_this_frame = true;
			}
			break;
			
		case WALL:
			do_wallride_physics();
			break;
			
		case LIP:
			do_lip_physics();
			break;
			
		case RAIL:
			do_rail_physics();
			break;
			
		case WALLPLANT:
			do_wallplant_physics();
			break;
	}
	
	handle_post_transfer_limit_overrides();
	
	// NOTE: moved this call from after CSkaterRotateComponent::Update to before it
	maybe_stick_to_rail();
	
	update_special_friction_index();
	
	// if (up && GetVel()[Y] < 0.0f)
	// {
		// DUMPF(GetPos()[Y]);
	// }
	
	if (m_vert_air_last_frame != (GetState() == AIR && GetFlag(VERT_AIR) && !GetFlag(SPINE_PHYSICS)))
	{
		m_vert_air_last_frame = !m_vert_air_last_frame;
        GetObject()->BroadcastEvent(m_vert_air_last_frame ? CRCD(0xf225fe69, "SkaterEnterVertAir") : CRCD(0x5e27200a, "SkaterExitVertAir"));
	}
	
	#ifdef __USER_DAN__
	// Gfx::AddDebugArrow(GetPos(), GetPos() + 60.0f * GetMatrix()[Z], RED, 0, 1);
	// Gfx::AddDebugArrow(GetPos(), GetPos() + 60.0f * GetMatrix()[X], BLUE, 0, 1);
	// Gfx::AddDebugArrow(GetPos(), GetPos() + 60.0f * GetMatrix()[Y], GREEN, 0, 1);
	#endif
	
	CFeeler::sClearDefaultCache();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterCorePhysicsComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | Jump | 
        // @flag BonelessHeight | 
		case CRCC(0x584cf9e9, "Jump"):
			do_jump(pParams);
			break;
			
        // @script | Flipped | true if flipped
		case CRCC(0xc7a712c, "Flipped"):
			return GetFlag(FLIPPED) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | Switched | true if switched
		case CRCC(0x8f66b80b, "Switched"):
			return IsSwitched() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | Crouched | true if skater crouched
		case CRCC(0x4adc6c2a, "Crouched"):
			return GetFlag(TENSE) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | OnGround | true if current state is on ground
		case CRCC(0x5ea287f2, "OnGround"):
			return GetState() == GROUND ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | InAir | true if current state is in air
		case CRCC(0x3527fc07, "InAir"):
			return GetState() == AIR ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | OnWall | true if current state is on wall
		case CRCC(0xa32c1a15, "OnWall"):
			return GetState() == WALL ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | OnLip | true if current state is on lip
		case CRCC(0x5cb1fbd8, "OnLip"):
			return GetState() == LIP ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | OnRail | true if current state is on rail
		case CRCC(0xe9851e62, "OnRail"):
			return GetState() == RAIL ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | InWallplant | true if current state is in wallplant
		case CRCC(0xa64dcf8b, "InWallplant"):
			return GetState() == WALLPLANT ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;

		// @script | FirstTimeOnThisRail | true if this is the first time we grinded this rail wihout doing something else
		// like skating or wallriding
		case CRCC(0x262d42d5,"FirstTimeOnThisRail"):
			return GetFlag(NEW_RAIL) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;

		// @script | StartSkitch | Start skitch physics
		// This will send a SkitchOn exception to the object
		case CRCC(0xca6b3809, "StartSkitch"):
			start_skitch();
			break;

        // @script | Skitching | Returns True if we are skitching
		case CRCC(0x9c6a7e41, "Skitching"):
			return GetFlag(SKITCHING) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;

        // @script | StopSkitch | Stop Skitch physics
		// this wills send a SkitchOff exeception to the object
		case CRCC(0x32dcc9cf, "StopSkitch"):
			StopSkitch();
			break;
			
		// @script | CancelWallpush | Cancels the current wallpush event
		case CRCC(0x5e8f9e77, "CancelWallpush"):
			SetFlagTrue(CANCEL_WALL_PUSH);
			break;
			 
        // @script | AirTimeLessThan | true if the air time is
        // less than the specified time
        // @uparm 1.0 | time (default is milliseconds)
        // @flag seconds | time in seconds
        // @flag frames | time in frames
        case CRCC(0xc890a84, "AirTimeLessThan"):
        // @script | AirTimeGreaterThan | true if the air time is 
        // greater than the specified time
        // @uparm 1.0 | time (default is milliseconds)
        // @flag seconds | time in seconds
        // @flag frames | time in frames
		case CRCC(0xbbf2b570, "AirTimeGreaterThan"):
		{
			float t = 0;
			pParams->GetFloat(NO_NAME, &t);
	
			Tmr::Time TestTime;
			if (pParams->ContainsFlag(CRCD(0xd029f619, "seconds")) || pParams->ContainsFlag(CRCD(0x49e0ee96, "second")))
			{
				TestTime = static_cast< Tmr::Time >(t * 1000);
			}	
			else if (pParams->ContainsFlag(CRCD(0x19176c5, "frames")) || pParams->ContainsFlag(CRCD(0x4a07c332, "frame")))
			{
				TestTime = static_cast< Tmr::Time >(t * (1000 / 60));
			}
			else
			{
				TestTime = static_cast< Tmr::Time >(t);
			}

			Tmr::Time AirTime;
			if (GetState() == AIR || GetState() == WALL)
			{
				AirTime = Tmr::ElapsedTime(m_went_airborne_time);
			}
			else
			{
				AirTime = m_landed_time - m_went_airborne_time;
			}	
			
			if (Checksum == CRCD(0xc890a84, "AirTimeLessThan"))
			{
				return AirTime < TestTime ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			}
			else
			{
				return AirTime > TestTime ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			}	
		}

		// @script | GetAirTime | Puts the air time, in seconds, into a param called airtime
		case CRCC(0xde583e34, "GetAirTime"):
		{
			Tmr::Time AirTime;
			if (GetState() == AIR || GetState() == WALL)
			{
				AirTime = Tmr::ElapsedTime(m_went_airborne_time);
			}
			else
			{
				AirTime = m_landed_time - m_went_airborne_time;
			}	
			pScript->GetParams()->AddFloat(CRCD(0xad6bcdb4, "AirTime"), AirTime * (1.0f / 1000.0f));	
			break;
		}
		
		// @script | GetAirTimeLeft | Puts the amount of air time left before landing, in seconds, into a param called AirTimeLeft
		case CRCC(0x1996b797, "GetAirTimeLeft"):
		{
			pScript->GetParams()->AddFloat(CRCD(0x7e2a8993, "AirTimeLeft"),
				Mth::ClampMin(calculate_time_to_reach_height(GetPos()[Y] - mp_state_component->m_height, GetPos()[Y], GetVel()[Y]), 0.0f));
			break;
		}
						
        // @script | Braking | true if skater is braking
		case CRCC(0x1f8bbd05, "Braking"):
			return is_braking() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;

		case CRCC(0xebbcf455, "CanBrakeOff"):
			m_pressing_down_brakes = false;
			break;

		case CRCC(0xbc19e291, "CanBrakeOn"):
			m_pressing_down_brakes = true;
			break;
			
        // @script | CanKick | true if skater can kick
		case CRCC(0x2f66333, "CanKick"):
			return can_kick() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;

        // @script | CanKickOn | sets can kick to true
		case CRCC(0x68bf6c13, "CanKickOn"):
			m_force_cankick_off = false;
			break;
			
        // @script | CanKickOff | sets can kick to false
		case CRCC(0xe8deb0d7, "CanKickOff"):
			m_force_cankick_off = true;
			break;
		
        // @script | ForceAutokickOn | turns on auto kick
		case CRCC(0x34dcfc97, "ForceAutokickOn"):
			m_auto_kick = true;
			break;

        // @script | ForceAutoKickOff | turns off auto kick
		case CRCC(0x257947e, "ForceAutokickOff"):
			m_auto_kick = false;
			break;

	    // @script | AutoKickIsOff | true if auto kick is off
		case CRCC(0x1baa1d9, "AutoKickIsOff"):
			return m_auto_kick == false ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | RestoreAutokick | restores auto kick to 
        // original preferences
		case CRCC(0x9fcfdfeb, "RestoreAutokick"):
			m_auto_kick = Mdl::Skate::Instance()->mp_controller_preferences[GetSkater()->m_skater_number].AutoKickOn;
			break;
			
        // @script | DoCarPlantBoost | boost after doing car plant
		case CRCC(0x17846595, "DoCarPlantBoost"):
		{
			GetVel()[Y] = Mth::ClampMin(GetVel()[Y], 0.0f);
			GetVel()[Y] += GetPhysicsFloat(CRCD(0xcb49b3f2, "Carplant_upward_boost"));	
			
			DUMP_VELOCITY;
			
			Mth::Vector front = GetVel();
			front[Y] = 0.0f;
			if (front.LengthSqr() < 10.0f * 10.0f)
			{
				front = GetMatrix()[Z];				
				front[Y] = 0.0f;
				if (front.LengthSqr() < 0.01f * 0.01f)
				{
					front.Set(1.0f, 0.0f, 1.0f);
				}
			}
			front.Normalize();
			GetVel() += front * GetPhysicsFloat(CRCD(0xb7422173, "Carplant_forward_boost"));
			
			DUMP_VELOCITY;
			break;
		}
		
		// @script | HandleLipOllieDirection |
		case CRCC(0x259cb90d, "HandleLipOllieDirection"):
		{
			CControlPad& control_pad = mp_input_component->GetControlPad();
			
			Mth::Vector out = GetMatrix()[Z];
			out[Y] = 0.0f;
			out.Normalize();	 
			Mth::Vector along(out[Z], 0.0f, -out[X], 0.0f);
											  
			// We've jumped off a lip, so we want to give us some velocity to the left
			if (control_pad.m_right.GetPressed())
			{
				// don't allow right jumping
				return CBaseComponent::MF_FALSE;
			}
			else if (BREAK_SPINE_BUTTONS)
			{
				GetVel() += out * GetPhysicsFloat(CRCD(0x1f96c224, "Lip_side_hop_speed"));
				DUMP_VELOCITY;
				return CBaseComponent::MF_TRUE;
			} 
			else if (control_pad.m_left.GetPressed()
				&& static_cast< int >(control_pad.m_left.GetPressedTime()) > GetPhysicsInt(CRCD(0x3080e9e7, "Lip_held_jump_out_time")))
			{
				GetVel() += out * GetPhysicsFloat(CRCD(0xdc818b7e, "Lip_side_jump_speed")); 
				DUMP_VELOCITY;
				return CBaseComponent::MF_TRUE;
			} 
			if (control_pad.m_up.GetPressed()
				&& static_cast< int >(control_pad.m_up.GetPressedTime()) > GetPhysicsInt(CRCD(0x9b25142a, "Lip_held_jump_along_time")))
			{
				GetVel() -= along * GetPhysicsFloat(CRCD(0x1ab3809f, "Lip_along_jump_speed")); 
				DUMP_VELOCITY;
				return CBaseComponent::MF_TRUE;
			}
			else if (control_pad.m_down.GetPressed()
				&& static_cast< int >(control_pad.m_down.GetPressedTime()) > GetPhysicsInt(CRCD(0x9b25142a,"Lip_held_jump_along_time")))
			{
				GetVel() += along * GetPhysicsFloat(CRCD(0x1ab3809f, "Lip_along_jump_speed"));
				DUMP_VELOCITY;
				return CBaseComponent::MF_TRUE;
			} 
			else
			{
				return CBaseComponent::MF_FALSE;
			}
		}  	
			
        // @script | OrientToNormal | 
		case CRCC(0x1d1fd4f0, "OrientToNormal"):
			m_display_normal = GetMatrix()[Y];
			m_current_normal = GetMatrix()[Y];
			new_normal(m_feeler.GetNormal());
			break;
			
        // @script | SetSpeed | 
        // @uparm 0.0 | speed in inches per second, so 3000 is very very fast
		// The skater's max speed is about 1100 inches per second (depends on stats)
		// this is usually used in conjunction with Overridelimits, to provide a temporary speed boost
		case CRCC(0x383b939b, "SetSpeed"):
		{
			float Speed = 0.0f;
			pParams->GetFloat(NO_NAME, &Speed);
			float length_sqr = GetVel().LengthSqr();
			if (length_sqr < 0.001f)
			{
				GetVel() = GetMatrix()[Z];
			}
			else
			{
				GetVel() *= 1.0f / sqrtf(length_sqr);
			}
			GetVel() *= Speed;
			DUMP_VELOCITY;
			break;
		}
		
        // @script | NoSpin | sets flag, disabling spin
		case CRCC(0x54ef2b79, "NoSpin"):
			mNoSpin = true;
			SetFlagFalse(AUTOTURN);
			break;

        // @script | CanSpin | sets flag enabling spin
		case CRCC(0xe2998b9, "CanSpin"):
			mNoSpin = false;
			break;
		
        // @script | InBail | sets is_bailing flag 
		case CRCC(0x6fc5aae0, "InBail"):
		{
			SetFlagTrue(IS_BAILING);
			break;
		}
		
        // @script | NotInBail | clears is_bailing flag
		case CRCC(0xbd4303f4, "NotInBail"):
		{
			SetFlagFalse(IS_BAILING);
			break;
		}
			
        // @script | IsInBail | true if bailing (as defined by is_bailing flag)
		case CRCC(0xa901a50c, "IsInBail"):
			return GetFlag(IS_BAILING) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;

	    // @script | BailOn | turn bails on
		case CRCC(0xe0df1f0a, "BailOn"):
			m_bail = true;
			break;
			
        // @script | BailOff | turn bails off
		case CRCC(0x8c3d7864, "BailOff"):
			m_bail = false;
			break;

        // @script | BailIsOn | true if bails on
		case CRCC(0xe1d5168, "BailIsOn"):
			return m_bail ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			

        // @script | FrontTruckSparks | true if sparks should be coming from the front trucks, false if from rear
        case CRCC(0xe0760055, "FrontTruckSparks"):
			return m_front_truck_sparks ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;

        // @script | SetFrontTruckSparks | sets sparks to come from front trucks
		case CRCC(0x19d0e5fb, "SetFrontTruckSparks"):
			m_front_truck_sparks = true;
			break;

        // @script | SetRearTruckSparks | sets sparks to come from rear trucks
		case CRCC(0xb48ec756, "SetRearTruckSparks"):
			m_front_truck_sparks = false;
			break;
			
        // @script | SetState | sets the state to the specified state
        // @uparm name | state name (lip, air, ground)
		case CRCC(0x948ebf96, "SetState"):
		{
			uint32 StateChecksum = 0;
			pParams->GetChecksum(NO_NAME, &StateChecksum);
			
			if (GetState() == LIP && StateChecksum != CRCD(0xa549b57b, "Lip"))
			{
				// Trigger the lip off event.
				maybe_trip_rail_trigger(TRIGGER_LIP_OFF);
			}

			// If we are going to "LIP" via script then clear the rail node, as it's a patch to get the skater to freeze
			if (GetState() != LIP && StateChecksum == CRCD(0xa549b57b, "Lip"))
			{
				mp_rail_node = NULL;
			}
			
			// if script alters state from wallride (like for a bail), then snap upright   
			// note, this command does not support setting TO wall, if it does, we should check here
			if (GetState() == WALL)
			{
				new_normal(Mth::Vector(0.0f, 1.0f, 0.0f));
				ResetLerpingMatrix();
			}
			
			switch (StateChecksum)
			{
				case 0:
					break;
				case CRCC(0x439f4704, "Air"):
					SetState(AIR);
					break;	
				case CRCC(0x58007c97, "Ground"):
					m_last_ground_feeler.SetTrigger(0); // prevent spurious gaps
					SetState(GROUND);
					break;	
				case CRCC(0xa549b57b, "Lip"):
					SetState(LIP);
					break;
				default:
					Dbg_Assert(false);
					break;
			}		
			break;
		}

        // @script | SetRailSound | sets the rail sound to either grind or slide
        // @uparm Grind | pass either Grind or Slide
		case CRCC(0x947fccf4, "SetRailSound"):
		{
			uint32 rail_sound_checksum = 0;
			pParams->GetChecksum(NO_NAME, &rail_sound_checksum, Script::ASSERT);
			Dbg_MsgAssert(rail_sound_checksum == CRCD(0x255ed86f,"Grind") || rail_sound_checksum == CRCD(0x8d10119d, "Slide"),
				("\n%s\nBad rail sound type '%s' sent to SetRailSound", pScript->GetScriptInfo(), Script::FindChecksumName(rail_sound_checksum)));
			if (rail_sound_checksum == CRCD(0x8d10119d, "Slide"))
			{
				SetFlagTrue(RAIL_SLIDING);
			}
			else
			{
				SetFlagFalse(RAIL_SLIDING);
			}
			break;
		}

		// @script | LockVelocityDirection | When passed the flag On this will cause the skater's
		// velocity to be locked in its current direction and be unaffected by the skater's rotation.
		// Only works when on the ground however.
		// @flag On | Enable
		// @flag Off | Disable (Ie, back to normal)
		case CRCC(0xacb82c02, "LockVelocityDirection"):
			m_lock_velocity_direction = pParams->ContainsFlag(CRCD(0xf649d637, "On"));
			break;
			
        // @script | SetRollingFriction | change the rolling friction value
        // @flag Default | use the default value
        // @uparm 0.0 | friction coeff
		case CRCC(0x510f983b, "SetRollingFriction"):
			pParams->GetFloat(NO_NAME, &m_rolling_friction);
			if (pParams->ContainsFlag(CRCD(0x1ca1ff20, "Default")))
			{
				if (m_special_friction_duration == 0.0f)
				{
					m_rolling_friction = GetPhysicsFloat(CRCD(0x78f80ec4, "Physics_Rolling_Friction"));
				}
			}	
			break;
        
        // @script | SetGrindTweak | 
        // @uparm int | grind tweak
		case CRCC(0x71b993b7, "SetGrindTweak"):
			pParams->GetInteger(NO_NAME, &mGrindTweak);
			break;
   
        // @script | Ledge | true if ledge
		case CRCC(0x315d9ed4, "Ledge"):
			return mLedge ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | BadLedge | true if bad ledge
		case CRCC(0xbe371b7b, "BadLedge"):
			return mBadLedge ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | SkateInAble | Do a collision check using a horizontal line a bit below the skater, and return true if it hits something skatable.
        // @flag Left | go left to right
		// Parameters in physics.q: SkateInAble_HorizOffset and SkateInAble_DownOffset
        // @flag Lip | This will do a check to see if the other side of the spine that the
		// skater might be doing a lip trick on is skateinable.
		// The collision check is down relative to the skater, but horizontal relative to 
		// the world because of the skater's wacky orientation when doing a lip trick.
		// The direction of the collision check is towards the skater, so that it detects the
		// other side of the spine.
		// Parameters in physics.q: SkateInAble_LipHorizOffset and SkateInAble_LipDownOffset
		case CRCC(0x55934543, "SkateInAble"):
			if (pParams->ContainsFlag(CRCD(0xa549b57b, "Lip")))
			{
				float HorizOff = GetPhysicsFloat(CRCD(0xb92cbe3b, "SkateInAble_LipHorizOffset"));
				float DownOff = GetPhysicsFloat(CRCD(0xefdfe781, "SkateInAble_LipDownOffset"));
				m_col_end = GetPos();
				m_col_end[Y] -= DownOff;
				m_col_start = m_col_end - HorizOff * GetMatrix()[Y];
				if (get_member_feeler_collision())
				{
					return m_col_flag_vert ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
				}
				else
				{
					// If the first check fails, do another check straight down. This is for when the skater is doing a lip trick on a high up rail that
					// still has vert beneath it.
					HorizOff = GetPhysicsFloat(CRCD(0x108613cb, "SkateInAble_LipExtraCheckHorizOffset"));
					DownOff = GetPhysicsFloat(CRCD(0xf9dc446d, "SkateInAble_LipExtraCheckDownOffset"));
					m_col_start = GetPos() - HorizOff * GetMatrix()[Y];
					m_col_end = m_col_start;
					m_col_end[Y] -= DownOff;
					if (get_member_feeler_collision())
					{
						return m_col_flag_vert ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
					}
				}
			}
			else
			{
				float HorizOff=GetPhysicsFloat(CRCD(0xc78ebe56,"SkateInAble_HorizOffset"));
				Mth::Vector a = GetPos() + HorizOff * GetMatrix()[X];
				a[Y] -= GetPhysicsFloat(CRCD(0xfca3378c,"SkateInAble_DownOffset"));
				Mth::Vector b = a - 2.0f * HorizOff * GetMatrix()[X];
				
				if (pParams->ContainsFlag(CRCD(0x85981897,"Left")))

				{
					m_col_start = a;
					m_col_end = b;
				}
				else
				{
					m_col_start = b;
					m_col_end = a;
				}
			
				if (get_member_feeler_collision())
				{
					return m_col_flag_vert ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
				}	
			}	
			return CBaseComponent::MF_FALSE;
			
        // @script | LastSpinWas | check if last spin was frontside or backside (requires one flag)
        // @flag Backside |
        // @flag Frontside |
		case CRCC(0x6c8a1316, "LastSpinWas"):
			if (pParams->ContainsFlag(CRCD(0x47953f00, "Backside")))
			{
				if (GetFlag(FLIPPED))
				{
					return !mYAngleIncreased ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
				}
				else
				{
					return mYAngleIncreased ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
				}				
			}	
			else if (pParams->ContainsFlag(CRCD(0x996d5512, "Frontside")))
			{
				if (GetFlag(FLIPPED))
				{
					return mYAngleIncreased ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
				}
				else
				{
					return !mYAngleIncreased ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
				}	
			}	
			else
			{
				Dbg_MsgAssert(false, ("LastSpinWas requires a FrontSide or BackSide flag"));
			}
			break;
			
        // @script | LandedFromSpine | 
		case CRCC(0x448e3630, "LandedFromSpine"):
			return mLandedFromSpine ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | LandedFromVert | 
		case CRCC(0xf37ce040, "LandedFromVert"):
			return mLandedFromVert ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
   
        // @script | SetLandedFromVert | sets the mlandedfromvert flag 
        // (check with LandedFromVert)
		case CRCC(0x133e1293, "SetLandedFromVert"):
			mLandedFromVert = true;
			break;
		
        // @script | ResetLandedFromVert | clears mlandedfromvert flag
        // (check with LandedFromVert)
		case CRCC(0xf922431, "ResetLandedFromVert"):
			mLandedFromVert = false;
			break;
			
		// @script | IsInVertAir | check if VERT_AIR flag is set
		case CRCC(0xdfb8f052, "InVertAir"):
			return GetFlag(VERT_AIR) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | AllowLipNoGrind | 
		case CRCC(0xc5b4b390, "AllowLipNoGrind"):
			mAllowLipNoGrind = true;
			break;

        // @script | ClearAllLipNoGrind |
		case CRCC(0xb9eebff6, "ClearAllowLipNoGrind"):
			mAllowLipNoGrind = false;
			break;
			
        // @script | NoRailTricks | don't allow rail tricks
        // (turn on with AllowRailTricks)
		case CRCC(0xec681a59, "NoRailTricks"):
			mNoRailTricks = true;
			break;

        // @script | AllowRailTricks | turn off with NoRailTricks
		case CRCC(0x65a559a, "AllowRailTricks"):
			mNoRailTricks = false;
			break;
			
        // @script | TurnToFaceVelocity | turn to face the proper direction
		case CRCC(0x461e5b92, "TurnToFaceVelocity"):
			// Mick: Don't do it if vel is too short as skater will collapse
			if (GetVel().LengthSqr() < 0.01f * 0.01f) break;
			
			GetMatrix()[Z] = GetVel();
			GetMatrix()[Z].Normalize();						
			GetMatrix()[X] = Mth::CrossProduct(GetMatrix()[Y], GetMatrix()[Z]);
			GetMatrix()[X].Normalize();
			GetMatrix()[Y] = Mth::CrossProduct(GetMatrix()[Z], GetMatrix()[X]);
			GetMatrix()[Y].Normalize();
			
			ResetLerpingMatrix();	
			break;
		
        // @script | OverrideLimits | Overrides the limits to the maximum speed that the skater
		// can achieve.  This allows us to do temporary speed boosts whihc are much faster 
		// than regular gameplay.  
		// You can specify a time that this speed limit will last for 
        // @parm float | max | Maximum speed in inches per second (normal gameplay is around 1000, so 5000 is nice)
		// @parmopt float | max_max | max | usualy the same as above.  It's a hard cap, making it bigger that max will give
		// you a smoother limit to the speed.
		// @parmopt float | friction | 0.0000020 | air friction.  Slows you down at a rate proportional to speed. So reduce this to stay fast longer
		// @parmopt float | gravity | physics default | down_gravity. gravity that slows you down when going up a slope
		// @parmopt float | time | 10000000000000,0f | time in seconds that this effect should last for
		// @flag end | end the effect (other parameters are unneeded and ignored if end is flagged)
		case CRCC(0x90f1d8d5, "OverrideLimits"):
		{
			if (pParams->ContainsFlag(CRCD(0xff03cc4e, "end")))
			{
				m_override_limits_time = 0.0f;
			}
			else
			{
				pParams->GetFloat(CRCD(0x6289dd76, "max"), &m_override_max, Script::ASSERT);
				m_override_max_max = m_override_max;
				pParams->GetFloat(CRCD(0x73a19e3a, "max_max"), &m_override_max_max);
				m_override_limits_time = 10000000000000.0f;
				pParams->GetFloat(CRCD(0x906b67ba, "time"), &m_override_limits_time);
				m_override_air_friction = 0.0000020f;
				pParams->GetFloat(CRCD(0xedf3e7f4, "friction"), &m_override_air_friction);
				m_override_down_gravity = GetPhysicsFloat(CRCD(0x6618a713, "Physics_Ground_Gravity"));
				pParams->GetFloat(CRCD(0xa5e2da58, "gravity"), &m_override_down_gravity);
				
				if (pParams->ContainsFlag(CRCD(0xdf312928, "NoTimeLimit")))
				{
					// magic number meaning no time limit
					m_override_limits_time = -1.0f;
				}
			}
			break;
		}

        // @script | SetSpecialFriction | This command is used in the Revert script to specify a set of friction values that should be used for
		// each Revert in the combo. m_special_friction_index gets reset as soon as the combo ends, ie by the ClearPanel_Landed & ClearPanel_Bailed
	    // functions.
        // @uparm [] | array of friction values
		case CRCC(0x5e8d9b80,"SetSpecialFriction"):
		{
			Script::CArray* pArray = NULL;
			pParams->GetArray(NO_NAME, &pArray);
			Dbg_MsgAssert(pArray, ("\n%s\nSetSpecialFriction requires an array of friction values", pScript->GetScriptInfo()));
			if (m_special_friction_index >= static_cast< int >(pArray->GetSize()))
			{
				m_special_friction_index = static_cast< int >(pArray->GetSize()) - 1;
			}	
			m_rolling_friction = pArray->GetFloat(m_special_friction_index);
			if (m_special_friction_index == 0)
			{
				m_special_friction_decrement_time_stamp = Tmr::GetTime();
			}
			++m_special_friction_index;
			
			pParams->GetFloat(CRCD(0x79a07f3f, "Duration"), &m_special_friction_duration);
			break;
		}
			
		// @script | OverrideCancelGround | Overrided the Cancel_Ground flag in gaps
		// for use with reverts
        // @flag Off | turn this flag off
		case CRCC(0xb94bc0c6, "OverrideCancelGround"):
			SetFlag(OVERRIDE_CANCEL_GROUND, !pParams->ContainsFlag(CRCD(0xd443a2bc, "Off")));
			break;	
		     			
        // @script | SetExtraPush | 
        // @parmopt float | radius | 48.0 | wall push radius
        // @parmopt float | speed | 100.0 | wall push speed
        // @parmopt float | turn | 6.0 | wall rotate speed
		case CRCC(0xe47ff4b5, "SetExtraPush"):
		{
			m_wall_push_radius = 48.0f;
			m_wall_push_speed = 100.0f;
			m_wall_rotate_speed = 6.0f;
			pParams->GetFloat(CRCD(0xc48391a5, "radius"), &m_wall_push_radius);
			pParams->GetFloat(CRCD(0xf0d90109, "speed"), &m_wall_push_speed);
			pParams->GetFloat(CRCD(0xdfdfeab8, "turn"), &m_wall_rotate_speed);
			break;
		}
		
		// @script | GetMatrixNormal | places the skater's matrix normal in x, y, and z
		case CRCC(0x5144783, "GetMatrixNormal"):
		{
			pScript->GetParams()->AddFloat(CRCD(0x7323e97c, "x"), GetMatrix()[Y][X]);
			pScript->GetParams()->AddFloat(CRCD(0x0424d9ea, "y"), GetMatrix()[Y][Y]);
			pScript->GetParams()->AddFloat(CRCD(0x9d2d8850, "z"), GetMatrix()[Y][Z]);
			break;
		}
		
		// Overloading the CMotionComponent member function for skater specific logic
		case CRCC(0x8819dd8b, "Obj_MoveToNode"):
		{
			CMotionComponent* p_motion_component = GetMotionComponentFromObject(GetObject());
			Dbg_Assert(p_motion_component);
			
			// call the member function, which will set m_matrix and m_pos
			bool result = p_motion_component->CallMemberFunction( Checksum, pParams, pScript );
			
			// and copy this into m_display_matrix
			ResetLerpingMatrix();

			// set the shadow to stick to his feet, assuming we are on the ground
			// Note: These are used by the simple shadow, not the detailed one.
			CShadowComponent* p_shadow_component = GetShadowComponentFromObject(GetObject());
			Dbg_Assert(p_shadow_component);
			
			p_shadow_component->SetShadowPos( GetSkater()->m_pos );
			p_shadow_component->SetShadowNormal( GetSkater()->m_matrix[Y] );

			m_safe_pos = GetPos();			// needed for uberfrig
			GetOldPos() = GetPos();			// needed for camera, so it thinks we've stopped
			
			GetObject()->SetTeleported();

			// Force an update of the skater's camera if this is a local skater			
			if( GetSkater()->IsLocalClient())
			{
				Obj::CCompositeObject *p_obj = GetSkater()->GetCamera();
				if (p_obj)
				{
					p_obj->Update();	 // Not the best way of doing it...
				}
			}

			if (!pParams->ContainsFlag( CRCD(0xd607e2e6,"NoReset") ))
			{
				// assume we want the skater to stop...						
				m_last_ground_feeler.SetTrigger(0);  // patch to stop spurious gaps
				GetVel().Set();
				DUMP_VELOCITY;
				SetState(GROUND);
				
				// Make sure the flippedness of the skater is in a stable state
				// that reflects if he is goofy or not
                SetFlag(FLIPPED, !GetSkater()->m_isGoofy);
				
				// reset any flippedness of animation
				CSkaterFlipAndRotateComponent* p_flip_and_rotate_component = GetSkaterFlipAndRotateComponentFromObject(GetObject());
				Dbg_Assert(p_flip_and_rotate_component);
				p_flip_and_rotate_component->ApplyFlipState();
			}
			else
			{
				// the "NoReset" flag is set
				// so velocity is maintained
				// but we need to check if the orient flag is set
				// as that indicates that we want the velocity
				// to be oriented as well.
				// (if NoReset was set, then velocity will be zero)
				if ( pParams->ContainsFlag( CRCD(0x90a91232, "orient") ))
				{
					float y_vel = GetVel()[Y];	   			// y velocity is preserved.....
					GetVel()[Y] = 0.0f;
					float speed = GetVel().Length();  		// how fast are we going?
					GetVel() = GetMatrix()[Z] * speed;		// face forward, at that speed
					GetVel()[Y] = y_vel;
					DUMP_VELOCITY;
				}
			}
			return result ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}

		// Used by the create-a-goal cursor to initialise the cursor position.
		// Cannot use the current skater's position because he might have just bailed by
		// jumping into water, which would then cause the cursor to get stuck.
		case 0xf7e21884: // GetLastGroundPos
		{
			pScript->GetParams()->AddVector(CRCD(0x7f261953,"Pos"),m_last_ground_pos[X],m_last_ground_pos[Y],m_last_ground_pos[Z]);
			break;
		}
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterCorePhysicsComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::Reset (   )
{
	// In Network games, don't set the skater to 0,0,0 because he is about to be teleported to a starting position and 0,0,0 means nothing.
	if (!GameNet::Manager::Instance()->InNetGame())
	{
		/*
		GetPos().Set(0.0f, 0.0f, 0.0f);
		GetOldPos().Set(0.0f, 0.0f, 0.0f);
		m_safe_pos.Set(0.0f, 0.0f, 0.0f);
		*/
		GetOldPos() = GetPos();
		m_safe_pos = GetPos();
		DUMP_POSITION;
	}
	
	m_pre_lip_pos.Set();
	
	/*
	GetMatrix().Identity();
	ResetLerpingMatrix();
	*/
	
	m_state_change_timestamp = Tmr::GetTime();
	
	GetVel().Set(0.0f, 0.0f, 0.0f);
	DUMP_VELOCITY;
	
	SetState(GROUND);
	m_previous_state = GROUND;
	
	for (int flag = NUM_ESKATERFLAGS; flag--; )
	{
		SetFlagTrue(static_cast< ESkaterFlag >(flag));
		SetFlagFalse(static_cast< ESkaterFlag >(flag));
	}
	SetFlag(FLIPPED, !GetSkater()->m_isGoofy);
	
	mp_movable_contact_component->LoseAnyContact();
	
	// bit nasty, prevents us getting "SKATE_OFF_EDGE" triggers
	m_last_ground_feeler.SetTrigger(0);

	m_wall_push_radius = 0.0f;
	
	m_tense_time = 0;

	mWallrideTime = 0;
	
	mNoSpin = false;
	
	m_bail = false;
	
	m_force_cankick_off = false;
	m_pressing_down_brakes = true;
	
	mp_state_component->mJumpedOutOfLipTrick = false;
		
	// mp_rail_node points to previous rail grinded, which might no longer be valid
	// best to set it to NULL  (fixed crash in park editor with multiple test-plays TT#678)	
	mp_rail_node = NULL;
	
	m_override_limits_time = 0.0f;
	m_transfer_overrides_factor = 1.0f;
	
	m_last_wallplant_time_stamp = 0;
	m_last_wallpush_time_stamp = 0;
	
	m_last_jump_time_stamp = 0;
	
	m_special_friction_duration = 0.0f;
}
          
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::ReadySkateState ( bool to_ground_state, const SRailData* p_rail_data, const SAcidDropData* p_acid_drop_data )
{
	// setup the state in preparation for being in skating mode next object update
	
	// reset all flags except FLIPPED
	ResetFlags();
	
	if (to_ground_state)
	{
		SetState(GROUND);
		m_previous_state = GROUND;
	}
	else
	{
		SetState(AIR);
		m_previous_state = AIR;
		
		// no in-air orientation control after walking
		SetFlagTrue(NO_ORIENTATION_CONTROL);
		
		if (mp_walk_component->m_disallow_acid_drops)
		{
			SetFlagTrue(AIR_ACID_DROP_DISALLOWED);
		}
	}
	
	m_state_change_timestamp = Tmr::GetTime();
	
	ResetLerpingMatrix();
	
	m_display_normal = m_last_display_normal = m_current_normal = GetMatrix()[Y];
	
	GetOldPos() = m_safe_pos = GetPos();
	
	// bit nasty, prevents us getting "SKATE_OFF_EDGE" triggers
	m_last_ground_feeler.SetTrigger(0);

	m_wall_push_radius = 0.0f;
	
	m_tense_time = 0;

	mWallrideTime = 0;
	
	mNoSpin = false;
	
	// m_bail = false;
	
	m_force_cankick_off = false;
	m_pressing_down_brakes = true;
	
	mp_state_component->mJumpedOutOfLipTrick = false;
		
	m_override_limits_time = 0.0f;
	m_transfer_overrides_factor = 1.0f;
	
	m_last_wallplant_time_stamp = 0;
	m_last_wallpush_time_stamp = 0;
	
	m_last_jump_time_stamp = 0;
	
	// handle special transitions
	if (p_rail_data)
	{
		// TT8717.  If walker had a movable contact, but that contact was not the same as the object containing the relavent rail manager component,
		// the skater was asserting in do_rail_physics.  We need to lose any old contact and acquire the appropriate contact.
		mp_movable_contact_component->LoseAnyContact();
		if (p_rail_data->p_movable_contact)
		{
			mp_movable_contact_component->ObtainContact(p_rail_data->p_movable_contact);
		}
		got_rail(p_rail_data->rail_pos, p_rail_data->p_node, p_rail_data->p_rail_man, true, true);
		GetOldPos() = m_safe_pos = GetPos();
	}
	else if (p_acid_drop_data)
	{
		enter_acid_drop(*p_acid_drop_data);
	}
	
	#if 0
	const char* p_state_names [   ] =
	{
		"GROUND",
		"AIR",
		"WALL",
		"LIP",
		"RAIL",
		"WALLPLANT"
	};
	DUMPS(p_state_names[GetState()]);
	#endif
}
          
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::CleanUpSkateState (   )
{
	if (m_vert_air_last_frame)
	{
		m_vert_air_last_frame = false;
        GetObject()->BroadcastEvent(CRCD(0x5e27200a, "SkaterExitVertAir"));
	}
	
	// longer rerail delay when walking
	m_rerail_time = static_cast< Tmr::Time >(GetPhysicsFloat(CRCD(0xe4412eb7, "Rail_walk_rerail_time")));
	
	// lip time counts towards rerail delay when walking
	if (m_lip_time > m_rail_time)
	{
		m_rail_time = m_lip_time;
	}
	
	// no grinding from walking after jumping out of a lip
	if (GetState() == AIR && m_previous_state == LIP)
	{
		SetFlagFalse(CAN_RERAIL);
	}
	
	// Extra clean up so that an observing camera will act OK while walking.  Note that observing cameras always use skater camera logic, even when the
	// observed player is walking.
	mp_state_component->m_state = GROUND;
	mp_state_component->m_camera_display_normal.Set(0.0f, 1.0f, 0.0f);
	mp_state_component->m_camera_current_normal.Set(0.0f, 1.0f, 0.0f);
	mp_state_component->m_spine_vel.Set();
	mp_state_component->mJumpedOutOfLipTrick = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkaterCorePhysicsComponent::ResetFlags (   )
{
	// reset all flags except FLIPPED
	for (int flag = NUM_ESKATERFLAGS; flag--; )
	{
		ESkaterFlag skater_flag = static_cast< ESkaterFlag >(flag);
		if (skater_flag == FLIPPED) continue;
		SetFlagTrue(skater_flag);
		SetFlagFalse(skater_flag);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::SetState ( EStateType state )
{
	if (mp_state_component->m_state != state)
	{
		m_state_change_timestamp = Tmr::GetTime();
		m_previous_state = mp_state_component->m_state;
	}
	
	// if going out of lip state, and previous lip pos is valid (not zero), then move back to the previous lip position (so we drop down nicely)
	if (mp_state_component->m_state == LIP
		&& state != LIP 
		&& (m_pre_lip_pos[X] != 0.0f || m_pre_lip_pos[Y] != 0.0f || m_pre_lip_pos[Z] != 0.0f))
	{
		// *** (SetState(LIP))  kind of nasty, but we want to allow them to move through the lip
		// we leave m_old_trigger_pos alone
		GetPos() = m_pre_lip_pos;
		GetOldPos() = GetPos();
		DUMP_POSITION;
	}

	// If setting the state to anything other than lip then kill the pre_lip_pos	
	if (state != LIP)
	{
		m_pre_lip_pos.Set(0.0f, 0.0f, 0.0f);
	}

	if (state != GROUND)
	{
		// start recording graffiti tricks
		mp_trick_component->SetGraffitiTrickStarted( true );
	}

	if (mp_state_component->m_state == RAIL && state != RAIL)
	{
		SetFlagFalse(RAIL_SLIDING);
	}

	if (mp_state_component->m_state == AIR && state != AIR)
	{
		// If going from AIR to any other state, record the landing time. Required for the AirTimeLessThan and AirTimeGreaterThan functions.
		m_landed_time = Tmr::GetTime();
		
		SetFlagFalse(NO_ORIENTATION_CONTROL);
		SetFlagFalse(OLLIED_FROM_RAIL);
	}	
	else if (mp_state_component->m_state != AIR && state == AIR)
	{
		// If going to AIR, record the takeoff time, unless going from WALL to AIR.
		// (The 'unless' bit fixes TT493, where hitting the ground from doing a wall-ride was not setting a big enough airtime)
		if (mp_state_component->m_state != WALL)
		{
			m_went_airborne_time = Tmr::GetTime();
		}	

		// Reset the spin tracking stuff.
		mp_trick_component->mTallyAngles = 0.0f;
		
		// Clear the L1 and R1 triggers, since they're used for spin taps.
		mp_input_component->GetControlPad().m_L1.ClearTrigger();
		mp_input_component->GetControlPad().m_R1.ClearTrigger();
		// Just to be sure
		m_tap_turns = 0.0f;
	}   
	
	mp_state_component->m_state = state;
																			   
	mp_sound_component->SetState(state);
}
          
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::ReverseFacing (   )
{
	GetMatrix()[Z] = -GetMatrix()[Z];
	GetMatrix()[X] = -GetMatrix()[X];
	ResetLerpingMatrix();
	mRail_Backwards = !mRail_Backwards;
	
	#ifdef __NOPT_ASSERT__
	if (DebugSkaterScripts && GetObject()->GetType() == SKATE_TYPE_SKATER)
	{
		printf("%d: Rotating skater\n", (int) Tmr::GetRenderFrame());
	}
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::CollideWithOtherSkaterLost ( CCompositeObject* p_other_skater )
{
	// reset all flags except FLIPPED
	ResetFlags();

	SetState(AIR);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::limit_speed (   )
{
	float save_y = GetVel()[Y];
	GetVel()[Y] = 0.0f;
	
	float speed = GetVel().Length();
	float max_max_speed = GetSkater()->GetScriptedStat(CRCD(0xcc5f87aa, "Skater_Max_Max_Speed_Stat")); 
	float max_speed = GetSkater()->GetScriptedStat(CRCD(0x2eacddb3, "Skater_Max_Speed_Stat"));

	if (m_override_limits_time != 0.0f)
	{
		// -1.0f is a magic number causing no time limit
		if (m_override_limits_time != -1.0f)
		{
			m_override_limits_time -= m_frame_length;
			if (m_override_limits_time < 0.0f)
			{
				m_override_limits_time = 0.0f;
			}
		}
		max_max_speed = m_override_max_max;
		max_speed = m_override_max;
	}
	
	if (speed > max_max_speed)
	{
		speed = max_max_speed;
		GetVel().Normalize(speed);
		DUMP_VELOCITY;
	}
	
	if (speed > max_speed)
	{
		apply_wind_resistance(GetPhysicsFloat(CRCD(0x850eb87a, "Physics_Heavy_Air_Friction")));
	}
	
	GetVel()[Y] = save_y;
	
	// decrement the special friction counter
	if (m_special_friction_duration != 0.0f)
	{
		m_special_friction_duration -= m_frame_length;
		if (m_special_friction_duration <= 0.0f)
		{
			// reset the special friction to default
			m_special_friction_duration = 0.0f;
			m_rolling_friction = GetPhysicsFloat(CRCD(0x78f80ec4, "Physics_Rolling_Friction"));
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::apply_wind_resistance ( float friction )
{
	// air friction is proportional to the square of the velocity; this is a limiting friction, and has much more effect at high speeds
	float speed_squared = GetVel().LengthSqr();
	if (speed_squared < 0.00001f) return;
	
	Mth::Vector air_friction = GetVel();
	air_friction.Normalize(friction * 60.0f * m_frame_length * speed_squared);
	
	if (air_friction.LengthSqr() > speed_squared)
	{
		GetVel().Set();
		DUMP_VELOCITY;
	}
	else
	{
		GetVel() -= air_friction;
		DUMP_VELOCITY;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::do_on_ground_physics (  )
{		
	// clear any flags that have no meaning when on the ground
	SetFlagFalse(CAN_BREAK_VERT);
	SetFlagFalse(VERT_AIR);
	SetFlagFalse(TRACKING_VERT);
	SetFlagFalse(SPINE_PHYSICS);
	SetFlagFalse(IN_ACID_DROP);
	SetFlagFalse(IN_RECOVERY);
	SetFlagFalse(AIR_ACID_DROP_DISALLOWED);

	// ground skateing will cancel any memory of what rail we were on, so the next one seems fresh	
	mp_rail_node = NULL;
	m_last_rail_trigger_node_name = 0;
	
	// rotate velocity, so it lays in the plane we are on: (as skaters orientation can lag behind)	
	// Dave note: calling RotateToPlane() with too small |vector| causes Nan problems - fix at some point.
	if( GetVel().Length() > 0.001f )
	{
		GetVel().RotateToPlane(m_current_normal);
	}
		
	// get gravitational force 	
	Mth::Vector gravity(0.0f, GetPhysicsFloat(CRCD(0x6618a713, "Physics_Ground_Gravity")), 0.0f);

	// allow for override if we are going up the slope (we might want to set gravity to zero when rolling up a slope)
	if (m_override_limits_time != 0.0f && GetVel()[Y] > 0.0f)
	{
		gravity.Set(0.0f, m_override_down_gravity, 0.0f);
	}

	// project gravity onto the plane we are on
	gravity.ProjectToPlane(m_current_normal);
	
	// add gravity into our velocity, adjusting for time
	GetVel() += gravity * m_frame_length;
	DUMP_VELOCITY;

	// based on if we are doing a balance trick, then either handle braking/kicking, or do the balance physics
	switch (mp_balance_trick_component->mBalanceTrickType)
	{
		case 0:
			// Only do braking & speeding up when not doing a balance trick like a manual			
			if (is_trying_to_brake())
			{
				do_brake();
			}
			else
			{
				m_braking = false;
				if (can_kick())
				{
					do_kick();
				}
			}
			break;
			
		case CRCC(0xac90769, "NoseManual"):
		case CRCC(0xef24413b, "Manual"):
			m_braking = false;
			
			mp_balance_trick_component->mManual.DoManualPhysics();

			// start recording graffiti tricks
			mp_trick_component->SetGraffitiTrickStarted(true);
			
			if (Mdl::Skate::Instance()->GetCareer()->GetCheat(CRCD(0xb38341c9, "CHEAT_PERFECT_MANUAL")))
			{
				// Here, set the flag. It may seem redundant, but the above line is very likely
				// to be hacked by gameshark. They probably won't notice this one, which will
				// set the flags as if they had actually enabled the cheat -- which enables us
				// to detect that it has been turned on more easily.
				Mdl::Skate::Instance()->GetCareer()->SetGlobalFlag( Script::GetInteger(CRCD(0xb38341c9, "CHEAT_PERFECT_MANUAL")));
				mp_balance_trick_component->mManual.mManualLean = 0.0f;
				mp_balance_trick_component->mManual.mManualLeanDir = 0.0f;
				g_CheatsEnabled = true;
			}
			break;
			
		case CRCC(0x3506ce64, "Skitch"):
			mp_balance_trick_component->mSkitch.DoManualPhysics();
            if (Mdl::Skate::Instance()->GetCareer()->GetCheat(CRCD(0x69a1ce96, "CHEAT_PERFECT_SKITCH")))
			{
				// Here, set the flag. It may seem redundant, but the above line is very likely
				// to be hacked by gameshark. They probably won't notice this one, which will
				// set the flags as if they had actually enabled the cheat -- which enables us
				// to detect that it has been turned on more easily.
				Mdl::Skate::Instance()->GetCareer()->SetGlobalFlag( Script::GetInteger(CRCD(0x69a1ce96, "CHEAT_PERFECT_SKITCH")));
				mp_balance_trick_component->mSkitch.mManualLean = 0.0f;
				mp_balance_trick_component->mSkitch.mManualLeanDir = 0.0f;
				g_CheatsEnabled = true;
			}
			break;			
			
		case CRCC(0x255ed86f, "Grind"):
		case CRCC(0x8d10119d, "Slide"):
		{
			#ifdef __NOPT_ASSERT__
			CSkaterEndRunComponent* p_endrun_component = GetSkaterEndRunComponentFromObject(GetObject());
			Dbg_Assert(p_endrun_component);
            Dbg_MsgAssert(!p_endrun_component->IsEndingRun() || !p_endrun_component->RunHasEnded(), ("Grind balance trick done on ground?"));
			#endif
			break;			
		}
			
		case CRCC(0xa549b57b, "Lip"):	
		{
			#ifdef __NOPT_ASSERT__
			CSkaterEndRunComponent* p_endrun_component = GetSkaterEndRunComponentFromObject(GetObject());
			Dbg_Assert(p_endrun_component);
			Dbg_MsgAssert(!p_endrun_component->IsEndingRun() || !p_endrun_component->RunHasEnded(), ("Lip balance trick done on ground?"));
			#endif
			break;
		}
		
		default:	
		{
			Dbg_Assert(false);
			break;
		}
	}		

	// Ground friction is rolling friction + wind resistance
	// Update velocity for friction before using velocity
	handle_ground_friction();

	// if too close to a wall, then adjust velocity to move the skater away from it
	// the distance that is checked can be changed by script, and probably gets big in bails, to stop the skater clipping through walls
	push_away_from_walls();

	// Calculate how much we want to move, based on the velocity
	Mth::Vector m_movement = GetVel() * m_frame_length;

	// when skitching, we get sucked into the skitch point
	if (GetFlag(SKITCHING))
	{
		if (mp_movable_contact_component->GetContact() && mp_movable_contact_component->GetContact()->GetObject())
		{
			GetVel() = mp_movable_contact_component->GetContact()->GetObject()->GetVel() * GetPhysicsFloat(CRCD(0xdef25b34, "skitch_speed_match"));
			DUMP_VELOCITY;
		}
		m_movement.Set();
		move_to_skitch_point();
	}

	// if we are in contact with something, then factor in that "movement," but not if (we are skitching, and in not in initial movement) 
	if (mp_movable_contact_component->UpdateContact(GetPos()))  // still need to update it every frame, otherwise it gets confused
	{
		if (!GetFlag(SKITCHING))
		{
			// handle movement due to contact seperately from normal physic movement 
			
			GetPos() += mp_movable_contact_component->GetContact()->GetMovement();		 
			GetOldPos() = GetPos();	// (WITH CONTACT) frig, as movement is due to something carying us, no need to incorporate that into collision   
			m_safe_pos = GetPos();	// (WITH CONTACT) frig, as movement is due to something carying us, no need to incorporate that into collision
			DUMP_POSITION;
			
			if (mp_movable_contact_component->GetContact()->IsRotated())
			{
				GetMatrix() *= mp_movable_contact_component->GetContact()->GetRotation();
				m_lerping_display_matrix *= mp_movable_contact_component->GetContact()->GetRotation();
			}
		}
			
	}
	
	// set "move_again" flag, for while loop
	// generally this will be cleared after the first time through the loop
	// but it can be set again, indicating that out movmeent was brought short
	// by hitting a curver surface that we can track around (like a Qp)			  
	bool move_again = true;
	
	// we only let them move again once
	// so we have a flag that says if we have already done it this frame
	bool already_moved_again = false;	
	
	while (move_again)
	{
		move_again = false;
		
		Mth::Vector	start_pos = GetPos();	  		// remember where we started on this iteration
		GetPos() += m_movement;	  				// Move him
		DUMP_POSITION;
			
		// Handle collisions //////////////////////////////////////////////						
		if (!GetFlag(SKITCHING))   	// if skitching, then dont bother with forward collision
		{
 			handle_forward_collision();							
		}
		

		// Mick - removed the test for m_moving_to_skitch for now
		// as we want the cars to be able to drag the player over gaps and jumps
		// and it does not seem to look bad this way
		// the only problem might be during very abrupt cahnges in ground angle, 
		// or maybe very uneven ground		
		if (GetFlag(SKITCHING) /* && m_moving_to_skitch*/)
		{
			// moving to skitch point, so don't snap to ground, as
			// we might snap onto the vehicle
			// instead, just project the skater onto the plane of the car
			
			// Dan: We still need to update terrain, however
			m_feeler.m_start = GetPos() + 36.0f * GetMatrix()[Y];
			m_feeler.m_end = GetPos() - 200.0f * GetMatrix()[Y];
			if (get_member_feeler_collision())
			{
				set_terrain(m_feeler.GetTerrain());
			}
		}
		else
		{
			snap_to_ground();
		}
													 
		// now see how far we have moved
		// interestingly we often move more than we are supposed to
		// probably due to the "snap to ground" stuff.		
		Mth::Vector actual_movement_vector;
		actual_movement_vector = GetPos() - start_pos;
		float actual_distance_moved = actual_movement_vector.Length();
		float attempted_distance = m_movement.Length();

		if (!already_moved_again && GetState() == GROUND)
		{
			if (actual_distance_moved < (attempted_distance - 0.1f))
			{
				m_movement = GetVel();					 	
				m_movement.Normalize();			  								// get new direction of travel
				m_movement *= attempted_distance;									// at old speed
				m_movement *= 1.0f - actual_distance_moved / attempted_distance;	// scale to account for movement							
				move_again = true;
				already_moved_again = true;				
			}
		}
	}

	// The remaining "ground physics" should only be done if we are still on the ground (might have skated off it) 
	if (GetState() == GROUND)
	{
		handle_ground_rotation();			
		
		// don't let the board slide sideways	
		// K: Avoid anything that might change the velocity direction if this flag is set.
		if (!m_lock_velocity_direction)
		{
			remove_sideways_velocity(GetVel());
		}	
	
		if (!GetFlag(SKITCHING))
		{
			// check if we are too close to a wall, and pop us out and away		
			check_side_collisions();		
			
			// check if we are moving slowly and leaning, and mush us more away from a wall if so
			check_leaning_into_wall();
		}
		
		// K: This flag is to allow the skater to be rotated on the ground without affecting his
		// velocity direction, so also disable the flipping otherwise 360 rotations won't be possible.
		if (!m_lock_velocity_direction)
		{
			// might have gone upa  slope, and gravity pulled us down, so we are not skating backwards
			flip_if_skating_backwards();	
		}	
	
		// Check for jumping
		if (maybe_flag_ollie_exception())	
		{
			maybe_straight_up();
			SetFlagTrue(CAN_BREAK_VERT);
		}
	
		mp_trick_component->TrickOffObject(m_last_ground_feeler.GetNodeChecksum());
			
		m_tap_turns = 0.0f;	
	}
		
	#ifdef __NOPT_ASSERT__
	if (Script::GetInteger(CRCD(0x3ae85eef, "skater_trails")))
	{
		Gfx::AddDebugLine(GetPos() + m_current_normal, GetOldPos() + m_current_normal, GREEN, 0, 0);
	}
	#endif
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::is_trying_to_brake (   )
{								   
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	// if not autokick, and not accelerating (square pressed), and going slow, then brake	
	if (!m_auto_kick && !control_pad.m_square.GetPressed() && GetVel().Length() < 50.0f)
	{
		return true;
	}
	
	return control_pad.m_down.GetPressed() 				// Down must be pressed
		&& m_pressing_down_brakes						// and must be enabled
		&&
		(
			GetVel().Length() < 50.0f		   	// and either going really slow
			||
			(
				!control_pad.m_right.GetPressed()		// or not pressed right or left
				 &&
				!control_pad.m_left.GetPressed()
			)
			||
			(                                           // or going backwards
				Mth::DotProduct(GetVel(), GetMatrix()[Z]) < 0.0f
			)
		);				
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::do_brake (   )
{		
	// Handle braking
	
	m_braking = true;
	
	if (on_steep_slow_slope())
	{
		m_braking = false;
		return;
	}

	float speed = GetVel().Length();

	// if already really slow (or stopped), then just stop
	if (speed < GetPhysicsFloat(CRCD(0xe5d73f6f, "Physics_Brake_Acceleration")) * 2.0f * m_frame_length)
	{
			GetVel().Set();
			DUMP_VELOCITY;
	}
	else
	{
		#if 0 // old code, this check doesn't seem necessary given the above if branch; Dan
		// Mth::Vector forward = GetVel();
		// forward.Normalize();
		// forward *= -PHYSICS_BRAKE_ACCELERATION;
		// Mth::Vector old_vel = GetVel();					// remember old velocity		
		// m_vel += forward * m_frame_length;						 	// apply braking force
		// if (Mth::DotProduct(GetVel(), old_vel) < 0.0f)	// if the velocty now in other direction
		// {
			// GetVel().Set();								// then clear it to zero velocity
		// }
		#else
		Mth::Vector brake = GetVel();
		brake.Normalize(-GetPhysicsFloat(CRCD(0xe5d73f6f, "Physics_Brake_Acceleration")) * m_frame_length);
		GetVel() += brake;
		DUMP_VELOCITY;
		#endif
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::on_steep_slow_slope (   )
{
	float speed = GetVel().Length();
	if (speed < GetPhysicsFloat(CRCD(0x62a1fa03,"Skater_max_sloped_turn_speed"))
		&& m_current_normal[Y] < GetPhysicsFloat(CRCD(0xc3527ef2, "Skater_max_sloped_turn_cosine")))
	{
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::can_kick (   )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();					 
	
	if (m_force_cankick_off)
	{
		return false;
	}

	// don't allow kicking if autokick is off and square not pressed	
	if (!m_auto_kick && !control_pad.m_square.GetPressed())
	{
		return false;
	}
	
	// don't allow kicking (accelerating) if "down" is pressed
	// as we would either be braking or sharp turning  
	if (control_pad.m_down.GetPressed())
	{
		return false;
	}

	float speed = GetVel().Length();
	
	if (!GetFlag(TENSE))
	{
		if (speed > GetSkater()->GetScriptedStat(CRCD(0x4610c2e3, "Skater_Max_Standing_Kick_Speed_Stat")))
		{
			return false;
		}
	}
	else
	{
		if (speed > GetSkater()->GetScriptedStat(CRCD(0x92e0247c, "Skater_Max_Crouched_Kick_Speed_Stat")))
		{
			return false;
		}
	}
	
	Dbg_Assert(m_auto_kick || control_pad.m_square.GetPressed());
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::do_kick (   )
{
	// Get skater's forward direction
	Mth::Vector forward;
	
	// K: Avoid anything that might change the velocity direction if this flag is set.
	if (m_lock_velocity_direction)
	{
		forward = GetVel();
		
		// Make sure the skater doesn't get stuck unable to move.
		if (forward.Length() < 0.5f)
		{
			forward = GetMatrix()[Z];
		}	
		else
		{
			forward.Normalize();
		}
	}
	else
	{
		forward = GetMatrix()[Z];
	}
		
	if (GetFlag(TENSE))
	{
		forward *= GetSkater()->GetScriptedStat(CRCD(0x3d24128e, "Physics_crouching_Acceleration_stat"));
	}
	else
	{
		forward *= GetSkater()->GetScriptedStat(CRCD(0x5f9b864d, "Physics_Standing_Acceleration_stat"));
	}
		
	// apply to velocity
	GetVel() += forward * m_frame_length;
	DUMP_VELOCITY;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::handle_ground_friction (   )
{
	// Apply the various type of friction that are acting when on the ground
	
	// if no autokick, and we are not bailing, and we have default friction then don't apply any friction
	// as it sucks to slow down when you do not have autokick
	if (!m_auto_kick && !GetFlag(IS_BAILING) && m_rolling_friction == GetPhysicsFloat(CRCD(0x78f80ec4, "Physics_Rolling_Friction")))
	{
		// autokick friction
		// currently none
	}
	else
	{	
		// non-autokick friction
		handle_wind_resistance();
		handle_rolling_resistance();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::handle_wind_resistance (   )
{	
	// Wind resistance differs, based on if we are crouched or not (as when we are crouched, we have a lower profile, so less wind resistance)
	
	float crouched_friction = GetPhysicsFloat(CRCD(0xbed96eda, "Physics_Crouched_Air_Friction"));
	float standing_friction = GetPhysicsFloat(CRCD(0x1a78b6fc, "Physics_Standing_Air_Friction"));
	
	if (m_override_limits_time != 0.0f)
	{
		crouched_friction = m_override_air_friction;
		standing_friction = m_override_air_friction;
	}
		  
	if (GetFlag(TENSE))
	{
		apply_wind_resistance(crouched_friction);
	}
	else
	{
		apply_wind_resistance(standing_friction);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
		
void CSkaterCorePhysicsComponent::handle_rolling_resistance (   )
{	
	// Only have rolling resistance if we are not going slow on a steep slope.	
	if (!slide_off_slow_steep_slope())
	{
		apply_rolling_friction();
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::slide_off_slow_steep_slope (   )
{
	// If we are on very steep ground and moving slowly then do not allow the skater to brake
	if (on_steep_slow_slope())
	{
		float speed = GetVel().Length();
		
		Mth::Vector forward;
		if (speed < 0.001f)
		{
			forward = GetMatrix()[Z];
		}
		else
		{
			forward = GetVel();
			
		}
		
		Mth::Vector fall(0.0f, -1.0f, 0.0f);
		fall.ProjectToPlane(m_current_normal);	  
		
		float angle = Mth::GetAngleAbout(forward, fall, GetMatrix()[Y]);
		float rot = GetPhysicsFloat(CRCD(0x7dd5678b, "Skater_Slow_Turn_on_slopes")) * Mth::Sgn(angle) * m_frame_length;
		if (Mth::Abs(rot) > Mth::Abs(angle))
		{
			// just about done, so just turn the last bit of angle left
			rot = angle;
		}
		GetMatrix().RotateYLocal(rot);				
		m_lerping_display_matrix.RotateYLocal(rot);				
		
		return true;
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::apply_rolling_friction (   )
{
	// apply a constant braking friction; if the velocity reaches 0, then we will stop (and not change direction)  		
	// This funtion will typically be used for rolling resistance
	
	// rolling friction is a constant, and is mostly noticable at slow speeds
	// if your speed is less than that produced by the force of rolling friction, then you will abruptly stop
	Mth::Vector rolling_friction = GetVel();
	float length = rolling_friction.Length();
	
	if (length < 0.0001f)
	{
		GetVel().Set();
		DUMP_VELOCITY;
		return;
	}
	
	rolling_friction *= 60.0f * m_rolling_friction * m_frame_length / length;
	
	if (rolling_friction.LengthSqr() > length * length)
	{
		GetVel().Set();
		DUMP_VELOCITY;
	}
	else
	{
		GetVel() -= rolling_friction;
		DUMP_VELOCITY;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
		
void CSkaterCorePhysicsComponent::push_away_from_walls (   )
{
	if (!m_wall_push_radius || !m_wall_push_speed) return;
	
	m_wall_push.Set();
	m_wall_dist = 1.0f;
	
	Mth::Vector	start, end;
	
	start = GetPos() + GetMatrix()[Y] * GetPhysicsFloat(CRCD(0xb45b39e2, "Skater_side_collide_height"));
	
	end = start + GetMatrix()[X] * m_wall_push_radius;
	check_for_wall_push(start, end, 0);
	
	end = start - GetMatrix()[X] * m_wall_push_radius;
	check_for_wall_push(start, end, 1);
	
	end = start + GetMatrix()[Z] * m_wall_push_radius;
	check_for_wall_push(start, end, 2);
	
	end = start - GetMatrix()[Z] * m_wall_push_radius;
	check_for_wall_push(start, end, 3);

	if (m_wall_dist == 1.0f) return;
	
	float push_speed = m_wall_push_speed * (1.0f - m_wall_dist);
	m_wall_push.Normalize(push_speed);
	GetVel() += m_wall_push;
	GetVel().RotateToPlane(m_current_normal);
	DUMP_VELOCITY;
	
	// if facing into the wall, then rotate away from it
	if (Mth::DotProduct(GetMatrix()[Z], m_feeler.GetNormal()) < 0.0f)
	{
		Mth::Vector target = GetMatrix()[Z];
		target.RotateToPlane(m_feeler.GetNormal());
		float angle = Mth::GetAngleAbout(GetMatrix()[Z], target, GetMatrix()[Y]);
		
		float rot = m_wall_rotate_speed * Mth::Sgn(angle) * m_frame_length;
		if (Mth::Abs(rot) > Mth::Abs(angle))
		{
			// just about done, so just turn the last bit of angle left
			rot = angle;
		}
		GetMatrix().RotateYLocal(rot);				
		m_lerping_display_matrix.RotateYLocal(rot);	   
	} 		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::check_for_wall_push ( const Mth::Vector& start, const Mth::Vector& end, int index )
{
	// do not allow a push in this direction until a push in any other direction has NOT happenend for half a second
	// this should prevent the nasty flickering
	for (int i = 0; i < 4; i++)
	{
		if (i == index) continue;
		
		// get time for opposite direction
		Tmr::Time t = Tmr::ElapsedTime(m_push_time[index ^ 1]);
		
		if (t > 500)
		{
			return;
		}
	}
		
	m_col_start = start;
	m_col_end = end;
	
	if (get_member_feeler_collision())
	{
		m_wall_push += m_feeler.GetNormal();
		if (m_feeler.GetDist() < m_wall_dist)
		{
			m_wall_dist = m_feeler.GetDist();
		}
		m_push_time[index] = Tmr::GetTime();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::move_to_skitch_point (   )						
{
	
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	if (!mp_movable_contact_component->GetContact() || !mp_movable_contact_component->GetContact()->GetObject())
	{
		FLAGEXCEPTION(CRCD(0x47d44b84, "OffMeterBottom"));
		return;
	}

	CCompositeObject* p_skitch_object = mp_movable_contact_component->GetContact()->GetObject();
	
	CSkitchComponent* p_skitch_comp = GetSkitchComponentFromObject(p_skitch_object);
	Dbg_Assert(p_skitch_comp);
	
	////////////////////////////////////////////////////////
	// Do moving between the skitch nodes
	// if L1 or R1 pressed
	// Basically we just look for nodes to the left or right of us at a
	// one foot interval
	// up a a maximum of 10 feet
	// if one is found, then switch the m_node_index to that node 

	float dir = 0.0f;	   
	   
	if (control_pad.m_L1.GetTriggered())
	{		
		control_pad.m_L1.ClearTrigger();
		dir = 1.0f;
	}	

	if (control_pad.m_R1.GetTriggered())
	{		
		control_pad.m_R1.ClearTrigger();
		dir = -1.0f;
	}	

	if (control_pad.m_L2.GetTriggered())
	{		
		control_pad.m_L2.ClearTrigger();
		dir = 1.0f;
	}	

	if (control_pad.m_R2.GetTriggered())
	{		
		control_pad.m_R2.ClearTrigger();
		dir = -1.0f;
	}	

	// if moving, and either was not moving or moving in opposite direction, then we can actually try looking for a node
	if (dir != 0.0f && (!m_moving_to_skitch || dir != m_skitch_dir))
	{
		float len = 6.0f;
		float step = 6.0f;
		float max = 12.0f * 10.0f;
		while (len < max)
		{
			// get an offset to the right or left
			Mth::Vector offset = GetMatrix()[X];  // X points left, from the camera's POV			
			offset *= len * dir;
			Mth::Vector	test_pos = GetPos() + offset;
	
			// see if there is a skitch point there
			Mth::Vector dummy(0.0f, 0.0f, 0.0f);
			int index = p_skitch_comp->GetNearestSkitchPoint(&dummy, test_pos);
	
			// if there is, then switch to that point
			if (index != m_skitch_index)
			{
				m_skitch_index = index;
				m_moving_to_skitch = true;
				m_skitch_dir = dir;
				
				if (dir == 1.0f)
				{
					FLAGEXCEPTION(CRCD(0x74bf80cf, "SkitchLeft"));
				}
				else
				{
					FLAGEXCEPTION(CRCD(0x2e7474b5, "SkitchRight"));
				}				
				break;
			}	
		
			len += step;
		}
	}
	
	// 
	// end of moving between skitch nodes
	///////////////////////////////////////////////////
	
	if (!mp_movable_contact_component->GetContact() || !GetFlag(SKITCHING)) return;

	///////////////////////////////////////////////////////
	// Do the actual movement

	Mth::Vector skitch_point;
	if (!p_skitch_comp->GetIndexedSkitchPoint( &skitch_point, m_skitch_index)) return;

	// zero out W component to prevent overflows (in theory this should not be necessary)
	GetPos()[W] = 0.0f;

	Mth::Vector target = skitch_point + GetPhysicsFloat(CRCD(0x21fb182c, "skitch_offset")) * -p_skitch_object->GetMatrix()[Z];

	if (m_moving_to_skitch)
	{
		Mth::Vector con_move = mp_movable_contact_component->GetContact()->GetMovement();

		GetPos() += con_move;
		DUMP_POSITION;

		Mth::Vector dir = target - GetPos();
		float dir_length_sqr = dir.LengthSqr();
		float suck_speed = GetPhysicsFloat(CRCD(0x97496256, "skitch_suck_speed")) * m_frame_length;

		// if skater is stuck in a wall, then end the skitch when car is fifteen feet away 														 
		if (dir_length_sqr > FEET(15.0f) * FEET(15.0f))
		{
			FLAGEXCEPTION(CRCD(0x47d44b84, "OffMeterBottom"));
			return;
		}

		if (dir_length_sqr <= suck_speed * suck_speed)
		{
			// we have arrived, so no need for sucking later
			GetPos() = target;
			m_moving_to_skitch = false;
			DUMP_POSITION;
		}							   
		else
		{
			dir *= suck_speed / sqrtf(dir_length_sqr);
			GetPos() += dir;
			DUMP_POSITION;
		}
	}
	else
	{
			GetPos() = target;
			DUMP_POSITION;
	}
	
	// Copy the objects Display matrix over the skater's
	// will orient the skater the same way as the thing that is dragging it, so if the car looks solid, then so should the skater...
	GetMatrix() = p_skitch_object->GetDisplayMatrix();
	
	// we also set the display matrix, to avoid little glitches
	ResetLerpingMatrix();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
     
void CSkaterCorePhysicsComponent::handle_forward_collision (   )
{
	if (GetPos() == GetOldPos()) return;
	
	Mth::Vector	forward = GetPos() - GetOldPos();
	forward.Normalize();	
	
	Mth::Vector up_offset = GetMatrix()[Y] * GetPhysicsFloat(CRCD(0xd4205c9b, "Skater_First_Forward_Collision_Height"));
	
	m_col_start = GetOldPos() + up_offset;
	
	m_col_end = GetPos() + up_offset + forward * GetPhysicsFloat(CRCD(0x20102726, "Skater_First_Forward_Collision_Length"));

	if (!get_member_feeler_collision()) return;
	
	// we have hit something going forward
	// either it is a wall, and we need to bounce off it or it is a steep QP, and we need to stick to it.
	// (note, with the slow normal changing, then it is possible that we might even collide with the poly that we are on
	
	Mth::Vector	normal = m_feeler.GetNormal();								
							
	float dot = Mth::DotProduct(normal, m_current_normal);			

	// For fairly shallow curves, the dot between two normals will be pretty large
	// it's very important here to distinguish between a tight curve (like in a narrow QP) and a direct hit with a wall.
			   
	if (!m_col_flag_skatable || Mth::Abs(dot) < 0.01f)
	{
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_BONK, m_feeler);
		if (mp_physics_control_component->HaveBeenReset()) return;
		
		bounce_off_wall(normal);
	}
	else
	{
		// it's a qp, stick to it
		
		// Just move our contact point to the point of collision.  This is not right, as it could kill a lot of the movement for this frame
		// but should do for now (and stops you falling through)											
		GetPos() = m_feeler.GetPoint();
		
		// move it off the surface a little, so we are not IN it (which would be indeterminates as to which side)
		GetPos() += normal * 0.1f;
		DUMP_POSITION;
	}
}			

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::bounce_off_wall ( const Mth::Vector& normal )
{
	if (check_for_wallpush()) return;
	
	// Given the normal of the wall, then bounce off it, turning the skater away from the wall
	
	Mth::Vector	forward = GetPos() - GetOldPos();
	Mth::Vector movement = forward;				   		// remember how far we moved
	forward.Normalize();
	
	Mth::Vector up_offset = GetMatrix()[Y] * GetPhysicsFloat(CRCD(0xd4205c9b,"Skater_First_Forward_Collision_Height"));

	float turn_angle;
	float angle = rotate_away_from_wall(normal, turn_angle);

	float min = Mth::DegToRad(GetPhysicsFloat(CRCD(0x1483fd01, "Wall_Bounce_Dont_Slow_Angle"))); 
					 							
	if (Mth::Abs(angle) > min)
	{
		float old_speed = GetVel().Length();
		
		// The maximum value of Abs(angle) would be PI/2 (90 degrees), so scale the velocity 
		float x = Mth::Abs(angle) - min;
		x /= (Mth::PI / 2.0f) - min;		// get in the range 0 .. 1
		x = 1.0f - x; 						// invert, as we want to stop when angle is 90
		
		GetVel() *= x;
		DUMP_VELOCITY;

		// if (negative ^ flipped) then backwards flail, otherwise forward flail	

		if (old_speed > GetPhysicsFloat(CRCD(0xbe0a58a0, "Wall_Bounce_Dont_Flail_Speed")))
		{
			#ifdef	__NOPT_ASSERT__
			{
				Mth::Vector	up_offset = GetMatrix()[Y] * GetPhysicsFloat(CRCD(0xd4205c9b,"Skater_First_Forward_Collision_Height"));
				Mth::Vector start = up_offset+GetOldPos();
				Mth::Vector end = up_offset+GetPos();
				TrackingLine(1, start, end);	  // 1 = wall bounce flail
			}
			#endif
			
			if ((angle < 0.0f) ^ (GetFlag(FLIPPED)))														   
			{
				FLAGEXCEPTION(CRCD(0xb4101d70,"FlailLeft"));
			}
			else
			{
				FLAGEXCEPTION(CRCD(0x756a7535,"FlailRight"));
			}
			
			// Player's terrain isn't set to the terrain in m_feeler, as this is a wall we're bouncing off of (or chain link fence or something)...
			// Steve: we gots ta figure out how to do this...
			// Perhaps have another terrain value sent to client skaters that tell them to play a bonk sound?
			mp_sound_component->PlayBonkSound( old_speed / GetSkater()->GetScriptedStat(CRCD(0xcc5f87aa, "Skater_Max_Max_Speed_Stat")), m_feeler.GetTerrain());
		}
	}
	
	// Bit of a patch here to move the skater away from the wall
	// Not needed so much with new sideways collision checks but we keep it in for low ledges.
	// Should perhaps standardize the height so collision checks for side and front but we'd probably still have problems.
	
	GetPos() = m_feeler.GetPoint() - up_offset;
	GetPos() += normal * 6.0f;
	DUMP_POSITION;
	
	// Now the majority of cases have been taken care of; we need to see if the skater is going to get stuck in a corner.

	float old_speed = movement.Length();		 					// get how much we moved last time
	Mth::Vector next_movement = GetVel();					// get new direction of velocity
	forward = GetVel();
	forward.Normalize();
	next_movement = forward * old_speed;							// extend by same movment as last time
	
	m_col_start = GetPos() + up_offset;
	m_col_start += up_offset;
	
	m_col_end = GetPos() + up_offset + next_movement
		+ forward * GetPhysicsFloat(CRCD(0x20102726, "Skater_First_Forward_Collision_Length"));
	
	if (get_member_feeler_collision() && GetSkater()->IsLocalClient())
	{
		// Just rotating the skater will lead to another collision, so try just inverting the skater's velocity from it's original and halving it....
		
		// First reverse the rotation, and rotate 180 degrees
		GetVel().RotateY(Mth::DegToRad(180.0f) - turn_angle);
		GetMatrix().RotateYLocal(Mth::DegToRad(180.0f) - turn_angle);
		ResetLerpingMatrix();
		
		GetVel() *= 0.5f; 
		DUMP_VELOCITY;
		
		if (old_speed > GetPhysicsFloat(CRCD(0xbe0a58a0, "Wall_Bounce_Dont_Flail_Speed")))
		{
			FLAGEXCEPTION(CRCD(0xb4101d70, "FlailLeft"));
		}
	}
}			

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::snap_to_ground (   )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	float up_dot = 0.0f;
	
	// Since we really don't want to loose contact with the ground while skitching, we use a much bigger snap up dist
	// The problem will come when we get dragged down a slope.  The car will flatten out well ahead of us, so pushing us down through the slop
	// (as we are a few feet behind it) and we will be so far under the ground that our normal snap up will not be able to dig us out of it,
	// so we go in air, uberfrig, and get dragged to a random spot under the level.
	// (This would not happen if we just skitch on flat ground)																								
	
	// Dan: snap_to_ground is never called while skitching
	// if (GetFlag(SKITCHING))
	// {
		// m_col_start = GetMatrix()[Y] * GetPhysicsFloat(CRCD(0x5c0d9610,"Physics_Ground_Snap_Up_SKITCHING"));	// much above feet
	// }
	// else
	// {
		m_col_start = GetMatrix()[Y] * GetPhysicsFloat(CRCD(0xe4d79235, "Physics_Ground_Snap_Up"));	  		// bit above the feet
	// }
	
	m_col_end = GetMatrix()[Y] * -200.0f;		    // WAY! below the feet, we check distance later

	m_col_start += GetPos();
	m_col_end += GetPos();
		 
	bool sticking = false;			
	// get disatnce to ground and snap the skater to it, but only if the ground is skatable, otherwise we just go to "AIR"
	if (get_member_feeler_collision())
	{
		Mth::Vector movement = GetPos() - m_feeler.GetPoint(); 
		float drop_dist = movement.Length();
		float drop_sign = Mth::DotProduct(GetMatrix()[Y], movement); // might be approx +/- 0.00001f
		if (!m_col_flag_skatable)	
		{
			// if below the face (or very close to it), then push us away from it				
			if (drop_sign < 0.001f)
			{
				// at point of contact, and move away from surface
				GetPos() = m_feeler.GetPoint() + m_feeler.GetNormal();	
				DUMP_POSITION;
			}
		}
		else
		{
			sticking = true;
			
			// Note the two ways of calculating the angle between two faces
			// the more "accurate" method simply takes angle between the two normals
			// however, this fails to account for the direction the skater is travelling
			// when in conjunction with the "snap" up.
			// If the skater approaches a slope from the side, then he can snap up to the slope
			// however, if the angle between the ground and the face to which we are snapping up to 
			// is too great, then we transition to in-air
			// and will drop through the slope
			// the solution is to take the angle between the front vector rotated onto each face.
			
			// Firstly we check the angle between the two faces	
			Mth::Vector normal = m_feeler.GetNormal();
			
			Mth::Vector	forward = GetMatrix()[Z];
			float front_dot = Mth::DotProduct(forward,normal);

			Mth::Vector	old_forward = forward;
			forward.RotateToPlane(normal);
			old_forward.RotateToPlane(m_current_normal);

			// angle between front vectors, projected onto faces
			up_dot = Mth::DotProduct(forward, old_forward);
			
			float stick_angle_cosine;
			if (!control_pad.m_up.GetPressed())
			{
				stick_angle_cosine = cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0xef161c2a, "Ground_stick_angle"))));
			}
			else
			{
				stick_angle_cosine = cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x4138283e, "Ground_stick_angle_forward"))));
			}
			
			if (front_dot > 0.0f  && up_dot > 0.0f && up_dot < stick_angle_cosine)
			{
				sticking = false;
			}
			else
			{
				// only need to test if snap is downwards
				if (drop_sign > 0.0f)
				{
					// angle between the old plane and the new plane is either the same, or withing the set limits
					// (like, < 60 degrees, or so, see physics.q) now calculate the drop distance, based on the angle
					
#ifdef __PLAT_NGC__
					float angle = acosf(Mth::Clamp(up_dot, -1.0f, 1.0f));
#else
					float angle = acosf(up_dot);
#endif // __PLAT_NGC__

					Mth::Vector	last_move = GetPos() - GetOldPos();
					
					float max_drop = last_move.Length() * tanf(angle);
					
					float min_drop = GetPhysicsFloat(CRCD(0x899ba3d0, "Physics_Ground_Snap_Down"));				
                    // if (GetFlag(SKITCHING))
					// {
						// min_drop = GetPhysicsFloat(CRCD(0x20df7e33, "Physics_Ground_Snap_Down_Skitching"));				
					// }

					if (max_drop < min_drop)
					{
						max_drop = min_drop;
					}
					
					if (drop_dist > max_drop)
					{
						sticking = false;
					}
				}
				
				if (sticking)
				{		  
					SetFlag(LAST_POLY_WAS_VERT, m_col_flag_vert);
					new_normal(normal);
				}		 
			}																		  
		}
					
		if (sticking)
		{	
			// if there is a collision, then snap to it
			GetPos() = m_feeler.GetPoint();
			DUMP_POSITION;
			
			if (GetState() == GROUND && movement.Length() > 2.0f)
			{
				// curbs are assumed to be between parallel surfaces, so check this...
				if (up_dot > 0.99f)
				{
					SetFlag(SNAPPED_OVER_CURB, true);
				}				
			}

			// will return trivially if terrain is already set to this type...
			set_terrain(m_feeler.GetTerrain());
			
			adjust_normal();

			// check to see if we have skated onto a movable object
			check_movable_contact();

			// still on ground, so store the latest ground collision data
			// check first to see if we are about to change
			if (m_last_ground_feeler.GetSector() != m_feeler.GetSector())
			{
				// changin sectors, so check the sector we came from and the one we are going to
				mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF, m_last_ground_feeler);
				if (mp_physics_control_component->HaveBeenReset()) return;
				
				mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_ONTO, m_feeler);
				if (mp_physics_control_component->HaveBeenReset()) return;
			}

			if (!mp_trick_component->GraffitiTrickStarted())
			{
				// clear the graffiti trick buffer if we're moving to a new world sector, but haven't started our trick yet...
				mp_trick_component->SetGraffitiTrickStarted(false);
			}
			
			set_last_ground_feeler(m_feeler);
		}
	}
	
	// skated off surface into the air
	if (!sticking)
	{
		SetState(AIR);
		GetObject()->BroadcastEvent(CRCD(0xd96f01f1, "SkaterOffEdge"));
		FLAGEXCEPTION(CRCD(0x3b1001b6, "GroundGone"));
		
		maybe_straight_up();
		
		if (GetFlag(VERT_AIR))
		{
			SetFlagTrue(CAN_BREAK_VERT);

			// we want to break vert, but don't want to go into spine physics for a frame
			// so don't do this check if we are trying to break the spine
			// (we can still break vert or spine on the next frame, when in the air)
 			if (!BREAK_SPINE_BUTTONS)
			{
				maybe_break_vert();	
			}
			
			// if we did not break vert now
			// then only allow us to break vert later if we've been tapping the "up" button
			// this is indicated by us having RELEASED or Pressed in teh last few ticks
			if (static_cast< int >(control_pad.m_up.GetReleasedTime()) > GetPhysicsInt(CRCD(0x6bb5b751, "Skater_vert_active_up_time"))
				&& static_cast< int >(control_pad.m_up.GetPressedTime()) > GetPhysicsInt(CRCD(0x6bb5b751, "Skater_vert_active_up_time")))			
			{
				// "UP" was not pressed any time recently, so don't let us break late
				SetFlagFalse(CAN_BREAK_VERT);	
			}
		}
		else if (BREAK_SPINE_BUTTONS)
		{
			SAcidDropData acid_drop_data;
			if (maybe_acid_drop(true, GetPos(), GetOldPos(), GetVel(), acid_drop_data))
			{
				enter_acid_drop(acid_drop_data);
			}
		}
		
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF_EDGE, m_last_ground_feeler);
		if (mp_physics_control_component->HaveBeenReset()) return;
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::new_normal ( Mth::Vector normal )
{
	if (Ed::CParkEditor::Instance()->UsingCustomPark())
	{
		// check if this new normal will make us lean into a wall
		if (normal[Y] > 0.0f)
		{
			CFeeler	feeler(GetPos(), GetPos() + 72.0f * normal);
			if (feeler.GetCollision())
			{
				normal.Set(0.0f,1.0f,0.0f);
			}
		}
	}
	
	if (normal != m_current_normal)
	{
		m_current_normal = normal;	  										// remember this, for detecting if it changes
		m_last_display_normal = m_display_normal;				// remember start position for lerping	
		m_normal_lerp = 1.0f;												// set lerp counter
		
		GetMatrix()[Y] = normal;
		GetMatrix().OrthoNormalizeAbout(Y);									// set regular normal immediately
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::adjust_normal (   )
{
	///////////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////////
	//
	// As the skater moves over the ground, he will go from being in contact
	// with one polygon, to another.
	// The "normal" of a polygon is the vector perpendicular to the surface of
	// the polygon, and the skater is normally aligned so that the "up" vector
	// of the skater (the Y component of the orientation matrix) is the same
	// as the normal or the polgon he is in contact with.
	//
	// However, as we go from one polygon to another (in a quarterpipe, for example)
	// the normal will change abruptly, and this looks very jerky
	// So, we try to smooth this out by remembering the old normal, and interpolating 
	// towards the current display normal
	// this is done at a fixed speed, controled by the script value "Normal_Lerp_Speed" 
	// whihc is defined in phsyics.q
	//
	// m_normal_lerp represents how far off the current face normal we are, it
	// will vary from 1.0 (still at the last display normal) to 0.0 (at current normal)
	// the intermediate normal is stored in m_display_normal
	// and is also copied directly into m_lerping_display_matrix, to rotate the skater 

	// if m_normal_lerp is 0.0, then we don't need to do anything, as we should already be there
	if (m_normal_lerp != 0.0f)	   		// if lerping
	{			
		// If the last display normal is the same as the current normal, then we can't interpolate between them
		if (m_last_display_normal == m_current_normal)
		{
			m_normal_lerp = 0.0f;
		}
		else
		{
			// adjust lerp at constant speed from 1.0 to 0.0, accounting for framerate
			m_normal_lerp -= GetPhysicsFloat(CRCD(0xd8120182, "Normal_Lerp_Speed")) * m_frame_length * 60.0f;
	
			// if gone all the way, then clear lerping values and set m_display_normal to be the current face normal
			if (m_normal_lerp <= 0.0f)
			{
				m_normal_lerp = 0.0f;
				m_display_normal = m_current_normal;
			}
			else
			{
				// Still between old and current normal, so calculate intermediate normal
				m_display_normal = Mth::Lerp(m_current_normal, m_last_display_normal, m_normal_lerp);											
				m_display_normal.Normalize();
			}
		}
	}
	
	// Now update the orientation matrix.
	// We need our up (Y) vector to be this vector
	// if it changes, rotate the X and Z vectors to match
	if (m_lerping_display_matrix[Y] != m_display_normal)
	{
		// lerp the y axis
		m_lerping_display_matrix[Y] = m_display_normal;
		m_lerping_display_matrix.OrthoNormalizeAbout(Y);	
		m_lerping_display_matrix[X] = GetMatrix()[X];
		m_lerping_display_matrix[Z] = GetMatrix()[Z];		
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::check_movable_contact (   )
{
	// given m_feeler is our current contact point with the ground, then check if our mp_movable_contact_component->GetContact() information needs updating				
	
	if (GetFlag(SKITCHING)) return;
	
	if (mp_movable_contact_component->CheckForMovableContact(m_feeler))
	{
		GetVel() -= mp_movable_contact_component->GetContact()->GetObject()->GetVel();
		DUMP_VELOCITY;
	}
}				

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::maybe_straight_up (   )
{			
	// Skater has just left gound, either jumped, or skated off it
	// so we need to check if the ground we left was flagged as VERT and if it is,
	// then we need to set our skater's velocity and orientation so they go straight up
	
	if (GetFlag(LAST_POLY_WAS_VERT))	
	{
		TrackingLine(3, GetOldPos(), GetPos());	  // 3 = going vert
		
		// Get the normal to the plane we jumped from
		Mth::Vector	up_plane_normal = m_current_normal;

		m_vert_normal = m_current_normal;
		m_vert_pos = GetOldPos();
		
		// move vert pos down one inch to better track subtle changes in edge height
		m_vert_pos[Y] -= 1.0f;
											
		// clear any Y component to this plane, makes it vertical
		up_plane_normal[Y] = 0.0f;
		
		if (up_plane_normal.Length() > 0.001f)
		{
			// and re-normalize to get a unit normal to the vertical plane.
			up_plane_normal.Normalize();
			
			GetVel().RotateToPlane(up_plane_normal);
			DUMP_VELOCITY;
	
			new_normal(up_plane_normal);
			
			// Fall line is used for auto turn
			m_fall_line = GetMatrix()[Z];
			m_fall_line[Y] = -m_fall_line[Y];
				
			// offset the jumper away from the plane by an inch
			GetPos() += up_plane_normal * GetPhysicsFloat(CRCD(0x78384871, "Physics_Vert_Push_Out"));
			DUMP_POSITION;
			
			SetFlagTrue(VERT_AIR);
			SetFlagTrue(TRACKING_VERT);
			SetFlagTrue(AUTOTURN);
			m_vert_upstep = 6.0f;			
		}
		else
		{
			SetFlagFalse(VERT_AIR);
		}
	}
	else
	{
		SetFlagFalse(VERT_AIR);
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::maybe_break_vert (   )
{
	// We "Break Vert" when we are pressing forward at the start of transitioning from the ground into a "vert" state
	// however, we need to defer the test until there is no ground in that "forward" direction (which will be directly beneath the skater's feet)
	
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	if (
		(	
			control_pad.m_up.GetPressed() 
			&& static_cast< int >(control_pad.m_up.GetPressedTime()) > GetPhysicsInt(CRCD(0x9d2f8cc8, "Skater_vert_push_time"))
			&& !control_pad.m_left.GetPressed()		// must be JUST up, to avoid accidents when turning
			&& !control_pad.m_right.GetPressed()
			&& !control_pad.m_square.GetPressed()	// and don't break if trying to do a trick involving up
			&& !control_pad.m_circle.GetPressed()
		)
		||
		(
			BREAK_SPINE_BUTTONS
		)
	)
	{
		// aha... up is pressed, so we want to break the air poly

		if (!BREAK_SPINE_BUTTONS)
		{
			// normal breaking the air polygon, we just fly forward
			
			float speed = GetVel().Length() * GetPhysicsFloat(CRCD(0x13f33b41, "physics_break_air_speed_scale"));
			
			GetVel()[X] += -m_display_normal[X] * speed;
			GetVel()[Z] += -m_display_normal[Z] * speed;
			GetVel()[Y] *= GetPhysicsFloat(CRCD(0x848c5cd6, "physics_break_air_up_scale"));
			DUMP_VELOCITY;
			
			// Now, since we broke the vert poly, the way it was set up, the skater will have alrready been snapped to vertical position
			// so we need to rotate him forwads 45 degrees to componsate
			GetMatrix().RotateXLocal(Mth::DegToRad(GetPhysicsFloat(CRCD(0xd757f4bb, "Skater_Break_Vert_forward_tilt"))));				

			SetFlagFalse(VERT_AIR);	   	// just regular air, if we broke the air poly
			SetFlagFalse(TRACKING_VERT);	// and certainly not tracking the vert
			SetFlagFalse(CAN_BREAK_VERT);	// and as we broke vert, we don't want to do it again
			SetFlagFalse(AIR_ACID_DROP_DISALLOWED);	// allow acid drops once once again

			// and we want to be going in the direction of our velocity, so set front x and Z, but leave Y
			Mth::Vector vel_normal = GetVel();
			vel_normal.Normalize();
			
			GetMatrix()[Z][X] = vel_normal[X];
			GetMatrix()[Z][Z] = vel_normal[Z];
			GetMatrix()[Z].Normalize();		
			GetMatrix().OrthoNormalizeAbout(Z); 
		}
		else
		{
			if (!maybe_spine_transfer())
			{
				// cannot find a transfer target, so just break the air polygon
				GetVel()[X] += -m_display_normal[X] * 24.0f;
				GetVel()[Z] += -m_display_normal[Z] * 24.0f;
				DUMP_VELOCITY;
				GetMatrix().RotateXLocal(Mth::DegToRad(GetPhysicsFloat(CRCD(0xd757f4bb, "Skater_Break_Vert_forward_tilt"))));				
	
				SetFlagFalse(VERT_AIR);	   	// just regular air, if we broke the air poly
				SetFlagFalse(TRACKING_VERT);	// and certainly not tracking the vert
				SetFlagFalse(CAN_BREAK_VERT);	// and as we broke vert, we don't want to do it again
				SetFlagTrue(IN_RECOVERY);		// tell him to just upright himself
				
				// and we want to be going in the direction of our velocity, so set front X and Z, but leave Y
				Mth::Vector vel_normal = GetVel();
				vel_normal.Normalize();
				
				GetMatrix()[Z][X] = vel_normal[X];
				GetMatrix()[Z][Z] = vel_normal[Z];
				GetMatrix()[Z].Normalize();		
				GetMatrix().OrthoNormalizeAbout(Z); 
			
				return;
			}
		}
		
		ResetLerpingMatrix();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
                    
bool CSkaterCorePhysicsComponent::maybe_spine_transfer (   )
{
	// Break spin button is pressed, so try to break the spine
			
	// The line to check along is the skater's forward directinal vector, rotated onto the XZ plane
	// if you go straight up the wall, then this will be the same as the normal of the wall (in XY) as we previously calculated
	// however we also want to handle the cases where you approach the QP at an angle
	
	// Need to take the forward vector (Z) and rotate it "forward" 90 degrees
	// Rotate about an axis perpendicular to both the horizontal part of m_matrix[Y] and also the world up (0,1,0)
	
	Mth::Vector skater_up = GetMatrix()[Y];	// skater_up is expected to be horizontal here, as we are "vert"
	skater_up[Y] = 0.0f;
	skater_up.Normalize();
	
	// get a vector perpendicular to the plane containing m_matrix[Z] and the world up 	
	#if 0 // old code - crossing by axis alined	vector bugs me
	// Mth::Vector world_up(0.0f, 1.0f, 0.0f);
	// Mth::Vector side = Mth::CrossProduct(skater_up, world_up);
	#else
	Mth::Vector side(-skater_up[Z], 0.0f, skater_up[X], 0.0f);
	#endif
	
	// assuming we have not slowed down much, then our velocity should roughly be in the direction we took off from 
	Mth::Vector forward = -GetVel();
	forward.Normalize();
				
	Mth::Vector wall_out = forward; 							// forward facing vector
	wall_out.Rotate(side, Mth::PI / 2.0f);					// rotate fowrad 90 degrees
	
	float speed;
	float dist = 12.0f;
	float time = 1.0f;
	bool hip_transfer = false;							  
			  
	CFeeler feeler;
	
	// Here the "wall" is what we are currently skating on, anything with "wall" in the name refers to that

	Mth::Vector target;
	Mth::Vector target_normal;
	bool target_found = false;			

	// First find a point beneath our current position
	// Nice long line, higher than we can posibly jump
	feeler.m_start = GetPos() + wall_out * 0.5f;
	feeler.m_end = GetPos() + wall_out * 0.5f;
	feeler.m_end[Y] -= 4000.0f;
	
	// ignore everything that is NOT vert
	// feeler.SetIgnore(0, mFD_VERT);
	
	Mth::Vector	wall_pos;
	if (feeler.GetCollision())
	{
		wall_pos = feeler.GetPoint();

		Mth::Vector start_normal = feeler.GetNormal();
		start_normal[Y] = 0.0f;
		start_normal.Normalize();
		
		target_found = look_for_transfer_target(-wall_out, start_normal, hip_transfer, target, target_normal);
			
		if (!target_found)
		{
			Mth::Vector left_along_vert(-start_normal[Z], 0.0f, start_normal[X]);
			
			// no target was found in the forward direction, perhaps we should look slightly left or right; look in the horizontal direction which is
			// halfway between the previous search direction and the plane of the vert
			if (mp_input_component->GetControlPad().m_left.GetPressed() && !mp_input_component->GetControlPad().m_right.GetPressed())
			{
				Mth::Vector search_dir = left_along_vert + -wall_out;
				search_dir.Normalize();
				target_found = look_for_transfer_target(search_dir, start_normal, hip_transfer, target, target_normal);
			}
			else if (mp_input_component->GetControlPad().m_right.GetPressed() && !mp_input_component->GetControlPad().m_left.GetPressed())
			{
				Mth::Vector search_dir = -left_along_vert + -wall_out;
				search_dir.Normalize();
				target_found = look_for_transfer_target(search_dir, start_normal, hip_transfer, target, target_normal);
			}
		}
	}
	
	if (!target_found) return false;
	
	Mth::Vector XZ_to_target = target - wall_pos;
	XZ_to_target[Y] = 0.0f;
	dist = XZ_to_target.Length();
	
	// We are only going to allow this later if the target point is the same level
	// as the takeoff point, and we have a clear line
	// so set it to this now, so we calculate the time correctly
	target[Y] = GetPos()[Y];

	// if the two faces are not really perpendicular or if the spine is wider than
	// then we determine that we are on a "hip" and we just want to go across it without drifting left or right
	// so we want to project all our velocity straight up

	Mth::Vector	horizontal_target_normal = target_normal;
	horizontal_target_normal[Y] = 0.0f;
	horizontal_target_normal.Normalize();
	
	Mth::Vector cache_vel = GetVel();
	
	float face_dot = Mth::Abs(Mth::DotProduct(skater_up, horizontal_target_normal));															
	if (face_dot < 0.9f)
	{
		GetVel()[Y] = GetVel().Length();
		GetVel()[X] = 0.0f;
		GetVel()[Z] = 0.0f;
		DUMP_VELOCITY;
	}
	else
	{
		// if spine more than two feet wide, then also don't allow drift
		if (dist > FEET(2.0f))
		{
			GetVel()[Y] = GetVel().Length();
			GetVel()[X] = 0.0f;
			GetVel()[Z] = 0.0f;
			DUMP_VELOCITY;
		}
	}
	
	// one inch out, to ensure miss the lip
	dist += 1.0f;

	#if 0 // old transfer code
	// get angle to rotate about, being the vector perpendicular to the world up vector and the difference between the two face normals
	// (generally for a spine these normals will be opposite, however they might be up to 90 degrees or more off when doing a hip)
	
	Mth::Vector normal_diff = target_normal - skater_up;
	normal_diff[Y] = 0.0f;
	normal_diff.Normalize();
	
	m_spine_rotate_axis[X] = -normal_diff[Z];
	m_spine_rotate_axis[Y] = 0.0f;
	m_spine_rotate_axis[Z] = normal_diff[X];
	m_spine_rotate_axis[W] = 0.0f;;
	#endif
	
	// for gravity calculations, temporarily pretend we are doing spine physics, so g is constant
	SetFlagTrue(SPINE_PHYSICS);
	time = calculate_time_to_reach_height(target[Y], GetPos()[Y], GetVel()[Y]);
	SetFlagFalse(SPINE_PHYSICS);

	// subtract some frames of time, to ensure we make it
	// time -= m_frame_length * 2.0f;
	
	if (time < 0.1f)
	{
		time = 0.1f;
	}
	
	speed = dist / time;
	
	// if spine more than two foot wide, then make sure that we have enough speed to get over it
	// otherwise, just do a little pop over, and allow them to recover						  
	if (dist > 24.0f && speed * speed > GetVel().LengthSqr())
	{
		return false;
	}

	// we have found a target point, either by looking directly in front or by doing the drop-down method
	// but we don't want to go for it until there is a clear line from our current position to the target
	
	Mth::Vector	target_XZ = target;
	target_XZ[Y] = GetPos()[Y];
	
	feeler.m_start = GetPos();
	feeler.m_end = target_XZ;
	if (feeler.GetCollision())
	{
		// don't do anything.  We have a valid transfer but we can wait until we get high enough before we try for it
		return true;
	}
		
	// setup the transfer's matrix slerp
	
	Mth::Vector land_facing;
	if (!hip_transfer)
	{
		land_facing = target - GetPos();
		land_facing[Y] = -(land_facing[X] * target_normal[X] + land_facing[Z] * target_normal[Z]) / target_normal[Y];
		land_facing.Normalize();
	}
	else
	{
		Mth::Vector offset = target - GetPos();
		offset.Normalize();
		float dot = Mth::DotProduct(offset, horizontal_target_normal);
		if (dot < 0.0f)
		{
			land_facing.Set(0.0f, 1.0f, 0.0f);
		}
		else
		{
			land_facing.Set(0.0f, -1.0f, 0.0f);
		}
	}

	Mth::Matrix transfer_slerp_start = GetMatrix();

	// calculate the facing we want when we land; retain our horizontal direction and choose a vertical component which puts us parallel so the target
	// poly's plane

	// calculate goal matrix
	Mth::Matrix transfer_slerp_goal;
	transfer_slerp_goal[Z] = land_facing;
	transfer_slerp_goal[Z].ProjectToPlane(target_normal);
	transfer_slerp_goal[Z].Normalize();
	transfer_slerp_goal[Y] = target_normal;
	transfer_slerp_goal[X] = Mth::CrossProduct(transfer_slerp_goal[Y], transfer_slerp_goal[Z]);
	transfer_slerp_goal[W].Set();
	
	// store the goal facing for use in adjusting the velocity at land time
	m_transfer_goal_facing = transfer_slerp_goal[Z];

	// if the skater is entering the spine transfer with an odd facing due to rotation, we want to preserve that angle in the slerp's goal matrix

	// calculate the deviation between the skater's velocity and facing
	float angle = Mth::GetAngleAbout(GetMatrix()[Z], cache_vel, GetMatrix()[Y]);
	
	// be a bit forgiving for hip transfers, as you often have to hit left/right to trigger them, which causes rotation
	if (Mth::Abs(angle) < Mth::DegToRad(30.0f))
	{
		angle = 0.0f;
	}

	// rotate goal facing to reflect the deviation in the initial facing
	transfer_slerp_goal.RotateYLocal(-angle);

	// setup the slerp state
	m_transfer_slerper.setMatrices(&transfer_slerp_start, &transfer_slerp_goal);
	m_transfer_slerp_timer = 0.0f;
	m_transfer_slerp_duration = Mth::ClampMin(time, 0.9f); // clamp the time to stop super fast rotations
	m_transfer_slerp_previous_matrix = transfer_slerp_start;
	
	// insure that the slerp takes us over the top, and doesn't invert us
	Mth::Matrix slerp_test;
	m_transfer_slerper.getMatrix(&slerp_test, 0.5f);
	if (slerp_test[Y][Y] < 0.0f)
	{
		m_transfer_slerper.invertDirection();
	}
	
	// remember the height we are aiming for, so when we come down through this height
	// then we remove the non vert velocity (or make it very small....)
	m_transfer_target_height = target[Y];
	
	// set velocity over the wall fast enough to land on the target point																	 
	mp_state_component->m_spine_vel = (target - GetPos()) / time;		// velocity from start to target
	mp_state_component->m_spine_vel[Y] = 0.0f;															// but ignore Y, as gravity handles that...
	
	// tell the code we are doing spine physics, so we lean quicker 
	if (!hip_transfer)
	{
		GetObject()->SpawnAndRunScript(CRCD(0xa5179e9e, "SkaterAwardTransfer"));	// award a trick (might want to do it as an exception later)
	}
	else
	{
		GetObject()->SpawnAndRunScript(CRCD(0x283bb5d6, "SkaterAwardHipTransfer"));	// award a trick (might want to do it as an exception later)
	}
	
	// no late jumps during a transfer
	GetObject()->RemoveEventHandler(CRCD(0x8ffefb28, "Ollied"));
	
	SetFlagTrue(SPINE_PHYSICS);	// flag in spin physics, to do the lean forward, and also allow downcoming lip tricks
	SetFlagFalse(IN_ACID_DROP);
	SetFlagFalse(TRACKING_VERT);	// we are still vert, but not tracking the vert
	SetFlagFalse(CAN_BREAK_VERT);	// and as we "broke" vert, we don't want to do it again
	
	return true;
 }

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
                    
bool CSkaterCorePhysicsComponent::look_for_transfer_target ( const Mth::Vector& search_dir, const Mth::Vector& start_normal, bool& hip_transfer, Mth::Vector& target, Mth::Vector& target_normal )
{
	// take a bunch of steps forward until we find one		
	// This is not very good, as we have to do 80 collision checks....
	// we really need to optimize our collision detection to be able to select a set of "nearby" object
	// or to select a set that intersects a sphere, or a plane
	// (here, we could just get the set that intersects the plane)
	// this could be statically cached by the colision code, and have one set
	// or perhaps more flexibly, each "feeler" could have a set of objects
	// that it deals with (defaulting to the set of all objects)
	
	CFeeler feeler;
	
	// setup collision cache
	Nx::CCollCache* p_coll_cache = Nx::CCollCacheManager::sCreateCollCache();
	Mth::CBBox bbox;
	Mth::Vector p;
	p = GetPos() + search_dir * 10.0f;
	bbox.AddPoint(p);
	p[Y] -= 4000.0f;
	bbox.AddPoint(p);
	p = GetPos() + search_dir * 500.0f;
	bbox.AddPoint(p);
	p[Y] -= 4000.0f;
	bbox.AddPoint(p);
	p_coll_cache->Update(bbox);
	feeler.SetCache(p_coll_cache);
	
	for (float step = 10.0f; step < 500.0f; step += 6.0f)		
	{
		// First find a VERT point a bit in front of us
		// can be some distance below us 
		// allowing us to transfer from high to low pools
		// (and low to high, proving you can jump up from the low point to the high point first)
		feeler.m_start = GetPos() + search_dir * step;		// start at current height
		feeler.m_end = feeler.m_start;
		feeler.m_end[Y] -= 4000.0f;									// long way below
		
		if (feeler.GetCollision() && (feeler.GetFlags() & mFD_VERT) && is_vert_for_transfers(feeler.GetNormal()))
		{
			Mth::Vector horizontal_normal = feeler.GetNormal();
			horizontal_normal[Y] = 0.0f;
			horizontal_normal.Normalize();
			float dot = Mth::DotProduct(start_normal, horizontal_normal);
			if (dot <= 0.95f)
			{
				target = feeler.GetPoint();
				target_normal = feeler.GetNormal();
				
				hip_transfer = dot > -0.866f;
				
				// feeler.m_end[Y] += 3960.0f;
				// feeler.DebugLine(255, 100, 100, 0);
				
				Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
				
				return true;
			}
			else
			{
				// feeler.m_end[Y] += 3960.0f;
				// feeler.DebugLine(100, 255, 100, 0);
			}
		}
		else
		{
			// feeler.m_end[Y] += 3960.0f;
			// feeler.DebugLine(100, 100, 255, 0);
		}
	}
	
	Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
                    
bool CSkaterCorePhysicsComponent::maybe_acid_drop ( bool skated_off_edge, const Mth::Vector &pos, const Mth::Vector& old_pos, Mth::Vector& vel, SAcidDropData& acid_drop_data )
{
	// horizontal direction in which a drop would occur
	Mth::Vector drop_direction;
	if (mp_physics_control_component->IsSkating())
	{
		drop_direction = vel;
		drop_direction[Y] = 0.0f;
		float length = drop_direction.Length();
		if (length < 0.01f) return false;
		drop_direction *= 1.0f / length;
	}
	else
	{
		drop_direction = mp_walk_component->m_facing;
	}
	
	bool target_found = false;
	Mth::Vector target;
	
	// in order not to miss vert polys with a thin horizontal projection, we check for them starting at this frame's initial position
	Mth::Vector search_pos = old_pos;
	search_pos[Y] = Mth::Max(pos[Y], old_pos[Y]);
	
	float scan_distance = 500.0f;
	float scan_height = 0.0f;
	if (mp_physics_control_component->IsWalking())
	{
		if (mp_walk_component->m_state == CWalkComponent::WALKING_GROUND)
		{
			// and use a reduced scan distance
			scan_distance = Script::GetFloat(CRCD(0xe50a9d56, "Physics_Acid_Drop_Walking_On_Ground_Search_Distance"));

			// and look for vert polys above us
			scan_height = 200.0f;
		}
		else
		{
			// look slightly behind us for acid drops (we may be facing down a vert we're standing on)
			search_pos -= 12.0f * drop_direction;
		}
	}
	
	CFeeler feeler;
	
	// setup collision cache
	Nx::CCollCache* p_coll_cache = Nx::CCollCacheManager::sCreateCollCache();
	Mth::CBBox bbox;
	Mth::Vector p;
	p = search_pos;
	p[Y] += scan_height;
	bbox.AddPoint(p);
	p[Y] -= 4200.0f;
	bbox.AddPoint(p);
	p = search_pos + drop_direction * scan_distance;
	p[Y] += scan_height;
	bbox.AddPoint(p);
	p[Y] -= 4200.0f;
	bbox.AddPoint(p);
	p_coll_cache->Update(bbox);
	feeler.SetCache(p_coll_cache);
	
	Mth::Vector target_normal;
	Mth::Vector horizontal_target_normal;
	float distance;
	for (distance = 0.01f; distance < scan_distance; distance += 3.0f)
	{
		// look for a vert poly below us
		feeler.m_start = search_pos + distance * drop_direction;
		feeler.m_end = feeler.m_start;
		feeler.m_start[Y] += scan_height;
		feeler.m_end[Y] -= 4000.0f;
		if (feeler.GetCollision() && (feeler.GetFlags() & mFD_VERT) && is_vert_for_transfers(feeler.GetNormal()))
		{
			// the horizontal projection of the vert's normal just correspond somewhat to our direction			 
			target_normal = horizontal_target_normal = feeler.GetNormal();
			horizontal_target_normal[Y] = 0.0f;
			horizontal_target_normal.Normalize();
			
			if (mp_physics_control_component->IsWalking() && mp_walk_component->m_state == CWalkComponent::WALKING_AIR)
			{
				// special acceptance rules for walking in-air acid drops
				target_found = Mth::DotProduct(drop_direction, horizontal_target_normal) <= -0.25f
					|| Mth::DotProduct(drop_direction, horizontal_target_normal) >= 0.05f;
			}
			else
			{
				target_found = Mth::DotProduct(drop_direction, horizontal_target_normal) >= 0.05f;
			}
			
			if (target_found)
			{
				target = feeler.GetPoint();
				// feeler.m_end[Y] += 3960.0f;
				// feeler.DebugLine(255, 100, 100, 0);
				break;
			}
			else
			{
				// feeler.m_end[Y] += 3960.0f;
				// feeler.DebugLine(100, 255, 100, 0);
			}
		}
		else
		{
			// feeler.m_end[Y] += 3960.0f;
			// feeler.DebugLine(100, 100, 255, 0);
		}
		
		// use a larger incrememt at larger distances, as we have several frames yet to find these polys
		if (distance > 100.0f)
		{
			distance += 24.0f;
		}
	}
	
	if (!target_found)
	{
		// no valid acid drop target found
		Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
		return false;
	}
	
	float original_target_height = target[Y];
	
	// because our search began behind us, the horizontal offset to the target may not be forward
	Mth::Vector horizontal_offset = target - pos;
	horizontal_offset[Y] = 0.0f;
	distance = horizontal_offset.Length();
	if (Mth::DotProduct(horizontal_offset, drop_direction) < 0.0f)
	{
		distance = -distance;
	}
	drop_direction = horizontal_offset / distance;
	
	// stash a copy of velocity so we can pretend it has an adjusted value
	Mth::Vector hold_vel = vel;
	
	if (mp_physics_control_component->IsWalking())
	{
		// because when walking they are necessarily in the same direction, project our horizontal velocity in the drop direction
		vel.ProjectToNormal(drop_direction);
		vel[Y] = hold_vel[Y];
	}
	
	// calculate our effective horizontal velocity
	float initial_horiz_speed = sqrtf(vel[X] * vel[X] + vel[Z] * vel[Z]);
	
	if (mp_physics_control_component->IsWalking())
	{
		// boost our effective horizontal speed up to maximum run speed
		float horizontal_boost = mp_walk_component->get_run_speed();
		if (initial_horiz_speed < horizontal_boost)
		{
			vel[X] = horizontal_boost * GetWalkComponentFromObject(GetObject())->m_facing[X];
			vel[Z] = horizontal_boost * GetWalkComponentFromObject(GetObject())->m_facing[Z];
			initial_horiz_speed = horizontal_boost;
		}
	}
	
	// give a slight upward pop
	if (skated_off_edge)
	{
		vel[Y] = Mth::Max(vel[Y], GetPhysicsFloat(CRCD(0x95a79c32, "Physics_Acid_Drop_Pop_Speed")));
	}
	
	// but limit upward speed to something reasonable
	if (!mp_physics_control_component->IsWalking() || mp_walk_component->m_state != CWalkComponent::WALKING_GROUND)
	{
		vel[Y] = Mth::Min(vel[Y], 2.0f * GetPhysicsFloat(CRCD(0x95a79c32, "Physics_Acid_Drop_Pop_Speed")));
	}
	
	// grab the acceleration we will have during our acid drop
	SetFlagTrue(SPINE_PHYSICS);
	float acceleration = get_air_gravity();
	SetFlagFalse(SPINE_PHYSICS);
	
	// calculate what height we would have if we used our current horizontal velocity to reach the target position
	float final_height;
	if (distance > 0.0f && initial_horiz_speed > 0.0001f)
	{
		float time_to_target = distance / initial_horiz_speed;
		final_height = pos[Y] + vel[Y] * time_to_target + 0.5f * acceleration * time_to_target * time_to_target;
	}
	else
	{
		// for backwards acid drops, just act as through we are directly over the target point
		final_height = pos[Y];
	}
	
	// if we need to jump up to the target
	if (mp_physics_control_component->IsWalking() && vel[Y] > 0.0f && pos[Y] < target[Y])
	{
		// check to see if we'll ever reach that height with our effective upward velocity
		float max_height = pos[Y] + vel[Y] * vel[Y] / (-2.0f * acceleration);
		float time_to_target = Mth::Abs(distance) / initial_horiz_speed;
		float time_to_max_height = vel[Y] / -acceleration;
		if (time_to_target < time_to_max_height)
		{
			// effectively, this means that we're willing to reduce our horizontal boost in order allow more time to reach the required height
			final_height = max_height;
		}
	}
	
	// if we can't reach the target with our current velocity, ditch the acid drop
	if (final_height < target[Y])
	{
		Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
		vel = hold_vel;
		return false;
	}
	
	// calculate the air time before the acid drop would hit its true target; prevent acid drops from occuring moments before landing
	SetFlagTrue(SPINE_PHYSICS);
	float time_to_reach_target_height = calculate_time_to_reach_height(original_target_height, pos[Y], vel[Y]);
	SetFlagFalse(SPINE_PHYSICS);
	if (time_to_reach_target_height < Script::GetFloat(CRCD(0x32c20f7e, "Physics_Acid_Drop_Min_Air_Time")))
	{
		Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
		vel = hold_vel;
		return false;
	}
	
	// ensure that we have a clear shot to the target
	
	bool clear_path = false;
    
	// keep shifting our target point up until we can get a clear shot to it, or we get to an unreachable height
	while (target[Y] < final_height)
	{
		feeler.m_start = pos;
		
		// check a path constructed from two concatenated lines, with the midpoint halfway along the acid drop trajectory; this is an attempt
		// to allow most acid drop which might be disallowed by a ledge which would block a straight line
		
		// calculate the time span required to fall to the target height
		SetFlagTrue(SPINE_PHYSICS);
		float half_time_to_reach_target_height = 0.5f * calculate_time_to_reach_height(target[Y], pos[Y], vel[Y]);
		SetFlagFalse(SPINE_PHYSICS);
		
		// calculate the spine velocity which would be used for this target
		float required_speed = 0.5f * distance / half_time_to_reach_target_height;
		
		// calculate the height we will be at halfway through the acid drop
		float height_halfway = pos[Y] + vel[Y] * half_time_to_reach_target_height
			+ 0.5f * acceleration * Mth::Sqr(half_time_to_reach_target_height);
		
		// calculate the point halfway through the acid drop
		Mth::Vector halfway_point = pos;
		halfway_point[Y] = height_halfway;
		halfway_point += required_speed * half_time_to_reach_target_height * drop_direction;
		
		// check for collisions alone the two-line path
		feeler.m_end = halfway_point;
		if (!feeler.GetCollision())
		{
			// feeler.DebugLine(255, 255, 0);
			feeler.m_start = feeler.m_end;
			feeler.m_end = target;
			feeler.m_end[Y] += 1.0f;
			if (!feeler.GetCollision())
			{
				// feeler.DebugLine(255, 255, 0);
				clear_path = true;
				break;
			}
			else
			{
				// feeler.DebugLine(0, 0, 0, 0);
			}
		}
		
		// feeler.DebugLine(0, 0, 0, 0);
		
		// try a higher target point
		target[Y] += 24.0f;
	}
	
	// no clear path was found along the acid drop
	if (!clear_path)
	{
		Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
		vel = hold_vel;
		return false;
	}
	DUMP_VELOCITY;
	
	Nx::CCollCacheManager::sDestroyCollCache(p_coll_cache);
	
	acid_drop_data.target_pos = target;
	acid_drop_data.target_normal = target_normal;
	acid_drop_data.true_target_height = original_target_height;
	return true;
}

void CSkaterCorePhysicsComponent::enter_acid_drop ( const SAcidDropData& acid_drop_data )
{
	const Mth::Vector& target_pos = acid_drop_data.target_pos;
	const Mth::Vector& target_normal = acid_drop_data.target_normal;
	const float& true_target_height = acid_drop_data.true_target_height;
	
	// setup the skater state for the acid drop
	
	Mth::Vector horizontal_offset = target_pos - GetPos();
	horizontal_offset[Y] = 0.0f;
	float distance = horizontal_offset.Length();
	if (Mth::DotProduct(horizontal_offset, GetVel()) < 0.0f)
	{
		distance = -distance;
	}
	Mth::Vector drop_direction = horizontal_offset / distance;
	
	// calculate the spine speed required to reach the target given our current vertical velocity
	SetFlagTrue(SPINE_PHYSICS);
	float time_to_reach_target_height = calculate_time_to_reach_height(target_pos[Y], GetPos()[Y], GetVel()[Y]);
	float required_speed = distance / time_to_reach_target_height;
	SetFlagFalse(SPINE_PHYSICS);
	mp_state_component->m_spine_vel.Set(required_speed * drop_direction[X], 0.0f, required_speed * drop_direction[Z]);
	acid_hold = mp_state_component->m_spine_vel;
	
	// once we reach this height, the skater's horizontal velocity will be zeroed out
	m_transfer_target_height = target_pos[Y];
	
	// Gfx::AddDebugStar(target, 24.0f, RED, 0);
	
	// enter the acid drop state
	SetFlagTrue(SPINE_PHYSICS);
	SetFlagTrue(VERT_AIR);
	SetFlagTrue(IN_ACID_DROP);
	SetFlagFalse(TRACKING_VERT);
	SetFlagFalse(AUTOTURN);
	
	// zero our horizontal velocity
	GetVel()[X] = 0.0f;
	GetVel()[Z] = 0.0f;
	DUMP_VELOCITY;
	
	// setup the acid drop's matrix slerp
	
	Mth::Matrix acid_drop_slerp_start = GetMatrix();
	
	// calculate the facing we want when we land; retain our horizontal direction and choose a vertical component which puts us parallel so the target
	// poly's plane
	Mth::Vector land_facing = drop_direction;
	land_facing[Y] = -(land_facing[X] * target_normal[X] + land_facing[Z] * target_normal[Z]) / target_normal[Y];
	land_facing.Normalize();
	
	// calculate goal matrix
	Mth::Matrix acid_drop_slerp_goal;
	acid_drop_slerp_goal[Z] = land_facing;
	acid_drop_slerp_goal[Z].ProjectToPlane(target_normal);
	acid_drop_slerp_goal[Z].Normalize();
	acid_drop_slerp_goal[Y] = target_normal;
	acid_drop_slerp_goal[X] = Mth::CrossProduct(acid_drop_slerp_goal[Y], acid_drop_slerp_goal[Z]);
	acid_drop_slerp_goal[W].Set();
	
	// store the goal facing for use in adjusting the velocity at land time
	m_transfer_goal_facing = acid_drop_slerp_goal[Z];
	
	// setup a good camera matrix for the acid drop (before applying any deviation preserving adjustments)
	m_acid_drop_camera_matrix = acid_drop_slerp_goal;
	if (m_acid_drop_camera_matrix[Z][Y] > 0.0f)
	{
		m_acid_drop_camera_matrix[Z] *= -1.0f;
		m_acid_drop_camera_matrix[X] *= -1.0f;
	}
	
	// if the skater is entering the acid drop with an odd facing due to rotation, we want to preserve that angle in the slerp's goal matrix
	
	// calculate the deviation between the skater's velocity and facing
	Mth::Vector horizontal_facing = GetMatrix()[Z];
	horizontal_facing[Y] = 0.0f;
	float angle = Mth::GetAngleAbout(horizontal_facing, drop_direction, GetMatrix()[Y]);
	
	// rotate goal facing to reflect the deviation in the initial facing
	acid_drop_slerp_goal.RotateYLocal(-angle);

	// setup the slerp state
	m_transfer_slerper.setMatrices(&acid_drop_slerp_start, &acid_drop_slerp_goal);
	m_transfer_slerp_timer = 0.0f;
	m_transfer_slerp_duration = time_to_reach_target_height;
	m_transfer_slerp_previous_matrix = acid_drop_slerp_start;
	
	// trigger the appropriate script
	Script::CStruct* p_params = new Script::CStruct;
	p_params->AddFloat(CRCD(0xbb00fe40, "DropHeight"), GetPos()[Y] - true_target_height);
	GetObject()->SpawnAndRunScript(CRCD(0xc7ed5fef, "SkaterAcidDropTriggered"), -1, false, false, p_params);
	
	// no late jumps during an acid drop
	GetObject()->RemoveEventHandler(CRCD(0x8ffefb28, "Ollied"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
                    
void CSkaterCorePhysicsComponent::handle_post_transfer_limit_overrides (   )
{
	// After a transfer, if we are above our standard speed limits, we ignore those limits for a short duration.  Over the duration the speed limits
	// lerp from our starting speed down to the standard speed limits.  Only non-air time is counted towards the duration.
	
	// detect leaving an acid drop
	if (m_began_frame_in_transfer && !GetFlag(SPINE_PHYSICS) && !GetFlag(IS_BAILING))
	{
		// only override limits if we start above the limits
		float standard_max = GetSkater()->GetScriptedStat(CRCD(0x2eacddb3, "Skater_Max_Speed_Stat"));
		float speed = GetVel().Length();
		if (speed < 1.25f * standard_max) return;
		
		// setup state to ignore speed limits
		m_transfer_overrides_factor = Mth::Min(speed / standard_max, GetPhysicsFloat(CRCD(0xc6b38be0, "Physics_Transfer_Speed_Limit_Override_Max")));
		
		// large values
		m_override_max = 1e20f;
		m_override_max_max = 1e20f;
	}
	else if (m_transfer_overrides_factor == 1.0f) return;
	
	// turn on speed limit overrides
	m_override_limits_time = -1.0f;
	
	// count our timer down
	m_transfer_overrides_factor -= m_frame_length * GetPhysicsFloat(CRCD(0xf9b006aa, "Physics_Transfer_Speed_Limit_Override_Drop_Rate"));
	
	// end the ignoring of speed limits if the duration is up
	if (m_transfer_overrides_factor < 1.0f)
	{
		m_transfer_overrides_factor = 1.0f;
		m_override_limits_time = 0.0f;
		return;
	}
	
	// grab the standard speed limit
	float standard_max = GetSkater()->GetScriptedStat(CRCD(0x2eacddb3, "Skater_Max_Speed_Stat")); 
	
	// calculate the appropriate speed limit based on the time since the acid drop and the speed at the end of the acid drop
	float time_based_appropriate_max = m_transfer_overrides_factor * standard_max;
	
	// calculate a speed limit based on the current speed; thus, if we break during the ignoring of speed limits, our speed limits will turn back on
	float speed_based_appropriate_max = 1.1f * GetVel().Length();
	
	// take the lowest speed limit; never increase the speed limit
	float appropriate_max;
	if (GetState() != AIR)
	{
		appropriate_max = Mth::Min3(time_based_appropriate_max, speed_based_appropriate_max, m_override_max);
	}
	else
	{
		// in air, don't drop the limits when your current speed drops; otherwise you lose your overrides as the top of vert air
		appropriate_max = Mth::Min(time_based_appropriate_max, m_override_max);
	}
	
	// end the ignoring of speed limits if the duration is up
	if (appropriate_max < standard_max)
	{
		m_transfer_overrides_factor = 1.0f;
		m_override_limits_time = 0.0f;
		return;
	}
	
	// set the artificially high speed limit override
	m_override_max = appropriate_max;
	
	// the max max speed limit will never fall below the standard max max speed limit
	m_override_max_max = m_override_max / standard_max * GetSkater()->GetScriptedStat(CRCD(0x2eacddb3, "Skater_Max_Speed_Stat"));
	
	PERIODIC(10)
	{
		printf("Post-Transfer Speed Limit Overrides:  current / standard = %.2f\n", m_override_max / standard_max);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
                    
float CSkaterCorePhysicsComponent::get_air_gravity (   )
{
	// given that we are in the air, figure out what gravity to use	
	// based on if we are VERT or regular air and the cheat modes
	// make sure that if you use this in calculations, then your flags do not change while you expect it to be the same
	float gravity;

	if (GetFlag(VERT_AIR) || GetFlag(SPINE_PHYSICS))   // Note, spine is treated same as vert
	{							  
		// gravity = GetPhysicsFloat(CRCD(0xfaa40754, "Physics_Air_Gravity")) / GetSkater()->GetScriptedStat(CRCD(0x441c38a0, "Physics_vert_hang_Stat"));
		gravity = GetPhysicsFloat(CRCD(0xfaa40754, "Physics_Air_Gravity")) / GetPhysicsFloat(CRCD(0x441c38a0, "Physics_vert_hang_Stat"));
	}
	else
	{
		// gravity = GetPhysicsFloat(CRCD(0xfaa40754, "Physics_Air_Gravity")) / GetSkater()->GetScriptedStat(CRCD(0xc31ca696, "Physics_Air_hang_Stat"));
		gravity = GetPhysicsFloat(CRCD(0xfaa40754, "Physics_Air_Gravity")) / GetPhysicsFloat(CRCD(0xc31ca696, "Physics_Air_hang_Stat"));
	}
	
	if (Mdl::Skate::Instance()->GetCareer()->GetCheat(CRCD(0x9c8c6df1, "CHEAT_MOON")))
	{
		// Here, set the flag. It may seem redundant, but the above line is very likely
		// to be hacked by gameshark. They probably won't notice this one, which will
		// set the flags as if they had actually enabled the cheat -- which enables us
		// to detect that it has been turned on more easily.
		Mdl::Skate::Instance()->GetCareer()->SetGlobalFlag( Script::GetInteger(CRCD(0x9c8c6df1, "CHEAT_MOON")));
		gravity *= GetPhysicsFloat(CRCD(0xec128f0, "moon_gravity"));
		g_CheatsEnabled = true;
	}
	 
	return gravity;
} 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				
float CSkaterCorePhysicsComponent::calculate_time_to_reach_height ( float target_height, float pos_Y, float vel_Y )
{
	// s = ut - 1/2 * g * t^2			 (note, -g = a in the more traditional formula)
	// solve this using the quadratic equation, gives us the formula below
	// Note the sign of s is important.....
	float distance = pos_Y - target_height; 
	float velocity = vel_Y;
	float acceleration = -get_air_gravity();
	return (velocity + sqrtf(velocity * velocity + 2.0f * acceleration * distance)) / acceleration; 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::handle_ground_rotation (   )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();	

	float rot = 0.0f; 
	float speed = GetVel().Length();						
	
	if (control_pad.m_left.GetPressed())
	{
		if (!control_pad.m_down.GetPressed())
		{
			rot = GetPhysicsFloat(CRCD(0x374e056b, "Physics_Ground_Rotation"));	
		}
		else
		{
			rot = GetPhysicsFloat(CRCD(0x7933a8ef, "Physics_Ground_Sharp_Rotation")); 
			if (speed < 10.0f)
			{
				// If not moving, then we want more control over turning, so ramp up the turing speed over a second
				int pressed_time = control_pad.m_left.GetPressedTime();
				if (pressed_time < STOPPED_TURN_RAMP_TIME)
				{
					rot = rot * pressed_time / STOPPED_TURN_RAMP_TIME;
				}
			}
		}
	}
	else if (control_pad.m_right.GetPressed())
	{
		if (!control_pad.m_down.GetPressed())
		{
			rot = -GetPhysicsFloat(CRCD(0x374e056b, "Physics_Ground_Rotation"));
		}
		else
		{
			rot = -GetPhysicsFloat(CRCD(0x7933a8ef, "Physics_Ground_Sharp_Rotation")); 
			if (speed < 10.0f)
			{
				// If not moving, then we want more control over turning, so ramp up the turing speed over a second
				int pressed_time = control_pad.m_right.GetPressedTime();
				if (pressed_time < STOPPED_TURN_RAMP_TIME)
				{
					rot = rot * pressed_time / STOPPED_TURN_RAMP_TIME;
				}
			}
		}
	}

	/*
	bool do_cess = false;							
	if (CESS_SLIDE_BUTTONS)
	{
		float cess_turn_min_speed = GetPhysicsFloat(CRCD(0xae84e34a, "cess_turn_min_speed"));
		if (speed > cess_turn_min_speed)
		{
			do_cess = true;
			
			float cess_turn_cap_speed = GetPhysicsFloat(CRCD(0x8242c4fe, "cess_turn_cap_speed"));
			
			float scale = speed;
			if (scale > cess_turn_cap_speed)
			{
				scale = cess_turn_cap_speed;
			}
			scale -= cess_turn_min_speed;
			scale /= cess_turn_cap_speed - cess_turn_min_speed;
		
			rot = rot * scale * GetPhysicsFloat(CRCD(0x22834151, "cess_turn_multiplier"));
		}
	}
	*/

	if (rot == 0.0f) return;
	
	rot *= m_frame_length;
	
	mYAngleIncreased = rot > 0.0f;
	
	// K: Avoid anything that might change the velocity direction if this flag is set.
	if (!m_lock_velocity_direction /* && !do_cess*/)
	{
		GetVel().RotateY(rot);					// Note:  Need to rotate this about UP vector		
		DUMP_VELOCITY;
	}	
	
	GetMatrix().RotateYLocal(rot);				
	m_lerping_display_matrix.RotateYLocal(rot);				
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::remove_sideways_velocity ( Mth::Vector& vel )
{				
	// Remove any non-forward component of the velocity
	
	float speed = vel.Length(); 		// get size of velocity
	if (speed > 0.00001f)
	{
		// if (!USE_BIKE_PHYSICS)
		// {		
			vel *= 1.0f / speed;																// get unit vector in direction of velocity
			float direction = Mth::Sgn(Mth::DotProduct(vel, GetMatrix()[Z]));	// get fwds or backwards
			
			vel = GetMatrix()[Z];												// get forward direction
			vel *= speed * direction;													// apply all speed in this direction
			DUMP_VELOCITY;
		// }
		// else
		// {
		  // Mth::Vector old_vel = vel;
		  
		  // vel.ProjectToNormal(GetMatrix()[Z]); 								// leave forward velocity alone
		  
		  // old_vel -= GetVel();		  														// find remaining sideways velocity
		  
		  // old_vel *= 1.0f - GetPhysicsFloat(CRCD(0x53385759, "cess_Friction"));
		  // vel += old_vel;
		  // DUMP_VELOCITY;
		// }
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::check_side_collisions (   )
{
	// check for collisins left and right of the skater; if we get collisions on both sides then restore him to the original position
	
	Mth::Vector debounce = GetPos();

	float side_col = GetPhysicsFloat(CRCD(0x406b425f, "Skater_side_collide_length"));

	if (check_side(-1.0f, side_col))
	{
		if (check_side(1.0f, side_col))
		{
			GetPos() = debounce;			// two collisions, back to safety
			DUMP_POSITION;
		}
	}
	else
	{
		if (check_side(1.0f, side_col))
		{
			if (check_side(-1.0f, side_col))
			{
				GetPos() = debounce;		// two collisions, back to safety
				DUMP_POSITION;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::check_side ( float side, float side_col )
{
	#ifdef STICKY_WALLRIDES
	// Prevous checks might have put us into a wall ride, so just ignore future checks					 
	if (GetState() == WALL)
	{
		return false;
	}
	#endif
			  
	// - - - side collision detection  - - - - - - - - - - - - - - - - -

	m_col_start = GetPos() + GetMatrix()[Y] * GetPhysicsFloat(CRCD(0xb45b39e2, "Skater_side_collide_height"));
	
	float col_len = side_col;
	#ifdef STICKY_WALLRIDES
	if (GetState() == AIR)
	{
		col_len += GetPhysicsFloat(CRCD(0x1c58c8b4, "Skater_air_extra_side_col"));
	}
	#endif
	m_col_end = m_col_start + side * GetMatrix()[X] * col_len;  

	if (!get_member_feeler_collision()) return false;
	
	Mth::Vector WallFloorNormal = m_feeler.GetNormal();

	#ifdef STICKY_WALLRIDES
	if (GetState() == AIR)
	{
		if (check_for_wallride())
		{
			return true;
		}
		else
		{
			// push a bit away from the wall if in the air
			Mth::Vector to_wall = m_feeler.GetPoint();
			to_wall -= m_col_start;				// vector towards the wall
			float push_dist = to_wall.Length();					// distance to wall
			push_dist -= col_len - side_col;	   				// adjust by the extra push we gave
			if (push_dist > 0.0f)								// if closer to wall than side_col
			{
				to_wall.Normalize(push_dist);				    // then get direction to wall, scaled by dist we want to move				
				GetPos() -= to_wall / 10.0f;		    // move 1/10th of the way, for nice lerp
				DUMP_POSITION;
				
				float turn_angle;					
				// Rotate away from wall only if not rotating myself
				// otherwise velocity can be continually rotated one way while 
				// the orientation is rated the other way via the d-pad 
				
				CControlPad& control_pad = mp_input_component->GetControlPad();

				// don't rotate at all if in the air, as it changes our direction, usually not what we want
				if (GetState() != AIR
					&& !control_pad.m_R1.GetPressed()
					&& !control_pad.m_L1.GetPressed() 
					&& !control_pad.m_left.GetPressed() 
					&& !control_pad.m_right.GetPressed()) 
				{
					rotate_away_from_wall(WallFloorNormal, turn_angle, 0.2f); 	// and rotate away from the wall
				}
			}
		}
	}
	else
	#endif
	{
		float angle = Mth::DotProduct(WallFloorNormal, GetMatrix()[Y]);
		// Consider 90+-30 degrees as wall (_0_5)
		// Consider 90+-15 degrees as wall (_0_25)
		if (angle < 0.25f && angle > -0.25f)
		{
			// Undo movement
			GetPos() = m_safe_pos;
			DUMP_POSITION;

			float turn_angle;
			rotate_away_from_wall(WallFloorNormal, turn_angle, 0.2f);

			// Try moving him off the wall:
			CFeeler	feeler(m_feeler.GetPoint() + WallFloorNormal, m_feeler.GetPoint() + WallFloorNormal * side_col);	
			if (!feeler.GetCollision())
			{
				mp_trigger_component->CheckFeelerForTrigger(TRIGGER_BONK, m_feeler);
				if (mp_physics_control_component->HaveBeenReset()) return false;
				
				// Lower skater back down to the ground
				GetPos() = feeler.m_end - GetMatrix()[Y] * GetPhysicsFloat(CRCD(0xb45b39e2,"Skater_side_collide_height"));
				DUMP_POSITION;
				return true;
			}
		}
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::check_for_wallpush (   )
{
	// requires that m_feeler is a wall
	
	if (!mp_input_component->GetControlPad().m_triangle.GetPressed()) return false;
	
	// last wallpush must not have been too recently
	if (Tmr::ElapsedTime(m_last_wallpush_time_stamp) < Script::GetFloat(CRCD(0x17d543, "Physics_Disallow_Rewallpush_Duration"))) return false;
	
	// wall normal must be opposite our forward direction; just under the maximum flail angle
	if (Mth::DotProduct(GetMatrix()[Z], m_feeler.GetNormal()) >= -sinf(Mth::DegToRad(Script::GetFloat(CRCD(0x1483fd01, "Wall_Bounce_Dont_Slow_Angle")) - 1.0f))) return false;
	
	// last wallplant must not have been too recently
	if (Tmr::ElapsedTime(m_last_wallplant_time_stamp) < Script::GetFloat(CRCD(0x17d543, "Physics_Disallow_Rewallpush_Duration"))) return false;
	
	// throw a wallpush event for the scripts
	GetObject()->SelfEvent(CRCD(0x4c03635b, "WallPush"));
	
	// check to see if the wallpush has been canceled during the event
	if (GetFlag(CANCEL_WALL_PUSH))
	{
		SetFlagFalse(CANCEL_WALL_PUSH);
		return false;
	}
	
	// reverse direction of velocity perpendicular to the wall
	Mth::Vector perp_vel = Mth::DotProduct(GetVel(), m_feeler.GetNormal()) * m_feeler.GetNormal();
	GetVel() -= 2.0f * perp_vel;
	
	// damp horizontal velocity
	float speed = GetVel().Length();
	if (speed > 0.001f)
	{
		GetVel() *= Mth::Max(
			Script::GetFloat(CRCD(0xb78542c2, "Physics_Wallpush_Min_Exit_Speed")),
			speed - Script::GetFloat(CRCD(0x1112fb1c, "Physics_Wallpush_Speed_Loss"))
		) / speed;
	}
	else
	{
		GetVel() = -Script::GetFloat(CRCD(0xb78542c2, "Physics_Wallpush_Min_Exit_Speed")) * GetMatrix()[Z];
	}
	
	// project the resulting velocity into the ground's plane
	GetVel().RotateToPlane(m_current_normal);
    
	DUMP_VELOCITY;
	
	// set orientation along new velocity
	GetMatrix()[Z] = GetVel();
	GetMatrix()[Z].Normalize();
	GetMatrix()[Y] = m_current_normal;
	GetMatrix()[X] = Mth::CrossProduct(GetMatrix()[Y], GetMatrix()[Z]);
	ResetLerpingMatrix();
	
	// time stamp the wallplant
	m_last_wallpush_time_stamp = Tmr::GetTime();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::check_for_wallplant (   )
{
	// requires that m_feeler is a wall
	
	// we must be somewhat vertical
	if (GetMatrix()[Y][Y] < 0.1f) return false;
	
	// last wallplant must not have been too recently
	if (Tmr::ElapsedTime(m_last_wallplant_time_stamp) < Script::GetFloat(CRCD(0x82135dd7, "Physics_Disallow_Rewallplant_Duration"))) return false;
	
	// not when you're too near the ground
	if (mp_state_component->m_height < Script::GetFloat(CRCD(0xd5349cc6, "Physics_Min_Wallplant_Height"))) return false;
	
	// wall must be substantially vertical
	if (!(m_feeler.GetFlags() & mFD_VERT) && Mth::Abs(m_feeler.GetNormal()[Y]) > 0.1f) return false;
	
	float speed = GetVel().Length();
	if (speed < 0.01f) return false;
	Mth::Vector forward = GetVel() / speed;
		
	// horizontal wall normal must be opposite our horizontal velocity
	Mth::Vector horizontal_forward = forward;
	horizontal_forward[Y] = 0.0f;
	horizontal_forward.Normalize();
	Mth::Vector horizontal_normal = m_feeler.GetNormal();
	horizontal_normal[Y] = 0.0f;
	horizontal_normal.Normalize();
	if (Mth::DotProduct(horizontal_forward, horizontal_normal) > -sinf(Mth::DegToRad(Script::GetFloat(CRCD(0x8f79cc1c, "Physics_Wallplant_Min_Approach_Angle"))))) return false;
	
	// here we attempt to stop wallplant when in is more likely that the player is going for a grind
	if (GetVel()[Y] > 0.0f && mp_input_component->GetControlPad().m_triangle.GetPressed())
	{
		Mth::Vector wall_point = m_feeler.GetPoint();
		Mth::Vector	wall_normal = m_feeler.GetNormal();

		Mth::Vector wall_up_vel(0.0f, GetVel()[Y] * 0.15f, 0.0f);		// check 0.15 seconds ahead
		wall_up_vel.RotateToPlane(wall_normal);  

		// check at what height will be in two frames
		wall_point += wall_up_vel;		

		CFeeler feeler(wall_point + wall_normal * 6.0f, wall_point - wall_normal * 6.0f);
		if (!feeler.GetCollision())
		{
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0x3ae85eef, "skater_trails")))
			{
				feeler.DebugLine(255, 255, 0, 0);
			}
			#endif
			return false;
		}
		else
		{
			#ifdef __USER_DAN__
			if (Script::GetInteger(CRCD(0x3ae85eef, "skater_trails")))
			{
				feeler.DebugLine(255, 0, 255, 0);
			}
			#endif
		}
	}
		
	// check for wallplant trick
	// K: Modified this to take an array of triggers, so that Kurt could check out using
	// Down,X DownLeft,X or DownRight,X as a trigger.
	bool triggered=false;
	Script::CArray *p_trick_query_array=Script::GetArray(CRCD(0x5d1f84a7, "Wallplant_Trick"));
	for (uint32 i=0; iGetSize(); ++i)
	{
		Script::CStruct *p_trick_query_struct=p_trick_query_array->GetStructure(i);
		if (mp_trick_component->QueryEvents(p_trick_query_struct))
		{
			triggered=true;
			break;
		}
	}
	if (!triggered)
	{
		return false;
	}
	
	// trip wallplant triggers
	mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, m_feeler);
	if (mp_physics_control_component->HaveBeenReset()) return true;
	
	// zero vertical velocity
	GetVel()[Y] = 0.0f;
	
	// reverse velocity in the direction of the wall's normal
	Mth::Vector perp_vel = Mth::DotProduct(GetVel(), horizontal_normal) * horizontal_normal;
	GetVel() -= 2.0f * perp_vel;
	
	// damp horizontal velocity
	float horizontal_speed = sqrtf(GetVel()[X] * GetVel()[X] + GetVel()[Z] * GetVel()[Z]);
	if (horizontal_speed > 0.0001f)
	{
		GetVel()[Y] = 0.0f;
		GetVel().Normalize(Mth::Max(
			Script::GetFloat(CRCD(0x7cee396c, "Physics_Wallplant_Min_Exit_Speed")),
			horizontal_speed - Script::GetFloat(CRCD(0x9b2d4a3, "Physics_Wallplant_Speed_Loss"))
		));
	}
	else
	{
		GetVel() = -Script::GetFloat(CRCD(0x7cee396c, "Physics_Wallplant_Min_Exit_Speed")) * horizontal_forward;
	}
	
	// replace vertical velocity with a wallplant boost
	GetVel()[Y] = Script::GetFloat(CRCD(0x74957fa, "Physics_Wallplant_Vertical_Exit_Speed"));
	
	if (m_feeler.IsMovableCollision())
	{
		// if the wall is moving, we are now in contact with it
		if (!mp_movable_contact_component->HaveContact() || m_feeler.GetMovingObject() != mp_movable_contact_component->GetContact()->GetObject())
		{
			mp_movable_contact_component->LoseAnyContact();
			mp_movable_contact_component->ObtainContact(m_feeler.GetMovingObject());
		}
	}
	
	DUMP_VELOCITY;
	
	// move to just outside the wall, insuring that there is no additional collision along the line to that point
	m_feeler.m_start = m_feeler.GetPoint();
	m_feeler.m_end = m_feeler.GetPoint() + Script::GetFloat(CRCD(0x24be8f0, "Physics_Wallplant_Distance_From_Wall")) * m_feeler.GetNormal();
	if (m_feeler.GetCollision())
	{
		GetPos() = m_feeler.GetPoint() + 0.1f * m_feeler.GetNormal();
	}
	else
	{
		GetPos() = m_feeler.m_end;
	}
	DUMP_POSITION;
	
	// set orientation along new velocity
	GetMatrix()[Z] = GetVel();
	GetMatrix()[Z][Y] = 0.0f;
	GetMatrix()[Z].Normalize();
	GetMatrix()[Y].Set(0.0f, 1.0f, 0.0f);
	GetMatrix()[X] = Mth::CrossProduct(GetMatrix()[Y], GetMatrix()[Z]);
	ResetLerpingMatrix();
	
	// time stamp the wallplant
	m_last_wallplant_time_stamp = Tmr::GetTime();
	
	// throw a wallplant event for the scripts
	GetObject()->SelfEvent(CRCD(0xcf74f6b7, "WallPlant"));
	
	// trick off the object
	mp_trick_component->TrickOffObject(m_feeler.GetNodeChecksum());
	
	// turn back on orientation control in case we just came out of walking
	SetFlagFalse(NO_ORIENTATION_CONTROL);
	
	// acid drops are always allowed after a wallplant
	SetFlagFalse(AIR_ACID_DROP_DISALLOWED);
	
	// let the camera know we're snapping our position slightly
	SetFlagTrue(SNAPPED);
	
	// enter wallplant state
	SetState(WALLPLANT);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::check_for_wallride (   )
{
	// Requires that m_feeler is a valid wall
	
	if (!m_col_flag_wallable) return false;
		
	// Allow a wall-ride attempt if triangle being pressed & long enough after the last wall-ride.	
	if ((mp_trick_component->GetButtonState(CRCD(0x20689278, "Triangle")) || mp_trick_component->TriggeredInLastNMilliseconds(
			CRCD(0x20689278, "Triangle"),
			1000 * GetPhysicsFloat(CRCD(0x5d241b00, "Wall_Ride_Triangle_Window"))
		)) && static_cast< int >(Tmr::ElapsedTime(mWallrideTime)) > 1000 * GetPhysicsFloat(CRCD(0xf0c74ec2, "Wall_Ride_Delay")))
	{
		////////////////////////////////////////////////
		// check to see if we are going upwards, and within 0.15 seconds from the top of the wall (based on current up speed)
		// and we are going upwards, then don't wallride, as we will probably snap into a rail soon after
		//

		if (GetVel()[Y] > 0.0f)
		{
			Mth::Vector wall_point = m_feeler.GetPoint();
			Mth::Vector	wall_normal = m_feeler.GetNormal();
	
			Mth::Vector wall_up_vel(0.0f, GetVel()[Y] * 0.15f, 0.0f);		// check 0.15 seconds ahead
			wall_up_vel.RotateToPlane(wall_normal);  
			
			// check at what height will be in two frames
			wall_point += wall_up_vel;		
			
			CFeeler check_feeler(wall_point + wall_normal * 6.0f, wall_point - wall_normal * 6.0f);
			if (!check_feeler.GetCollision()) return false;
		}	
		
		////////////////////////////////////////////////
		mWallNormal = m_feeler.GetNormal();
		
		Mth::Vector SquashedWallNormal = mWallNormal;			 // wall normal in the XZ plane
		SquashedWallNormal[Y] = 0;
		SquashedWallNormal.Normalize();
		
		Mth::Vector SquashedVel = GetVel();			 			// velocity in the XZ plane
		SquashedVel[Y] = 0;
		SquashedVel.Normalize();
		
		// Calculate the speed "along" the wall (i.e., in the XZ plane) needed to have enough speed along the wall to make the wallride viable 
		// (otherwise we just go up and down, and end up at odd angles)
		Mth::Vector vel_along_wall = GetVel();
		vel_along_wall[Y] = 0;
		vel_along_wall.ProjectToPlane(SquashedWallNormal);
		float speed_along_wall = vel_along_wall.Length();
		
		if (speed_along_wall < GetPhysicsFloat(CRCD(0xf0636a67, "Wall_Ride_Min_Speed"))) return false;
		
		float dot = fabsf(Mth::DotProduct(SquashedVel, SquashedWallNormal));
		
		// If all angles OK then trigger a wall-ride.
		if (mWallNormal[Y] > -sinf(Mth::DegToRad(GetPhysicsFloat(CRCD(0xae23e850, "Wall_Ride_Upside_Down_Angle")))) && 
			dot < sinf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x6ac9f64d, "Wall_Ride_Max_Incident_Angle")))) && 
			GetMatrix()[Y][Y] > cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0xbfcbb7e8, "Wall_Ride_Max_Tilt")))))
		{
			if (!mp_trick_component->GraffitiTrickStarted())
			{
				// clear the graffiti trick buffer if we're moving to a new
				// world sector, but haven't started our trick yet...
				mp_trick_component->SetGraffitiTrickStarted(false);
			}

			// landed in a wall ride, check for triggers associated with this object
			// note we pass "TRIGGER_LAND_ON", perhaps not semanticaly correct but we use it for landing on rails,
			// so the meaning is the same across ground-rail-wallride
			set_last_ground_feeler(m_feeler);
			mp_trigger_component->CheckFeelerForTrigger(TRIGGER_LAND_ON, m_last_ground_feeler);
			if (mp_physics_control_component->HaveBeenReset()) return false;

			Mth::Vector	point = m_feeler.GetPoint();				   

			// check to see if we are going into a corner
			// by checking the vector from out current pos along a line parallel to our movment rotated onto the wall.
			Mth::Vector	forward_vel = GetPos() - GetOldPos();
			forward_vel.RotateToPlane(mWallNormal);
			m_col_start = GetPos();
			m_col_end = GetPos() + forward_vel * 3.0f;
			if (get_member_feeler_collision()) return false;
			
			// Record start time.
			mWallrideTime = static_cast< int >(Tmr::GetTime());
			
			// Snap position to wall.
			// we don't want to snap too close
			// previously we moved him an inch away from the wall after snapping him to the collision point.
			// However, this seemed to cause problems in corner so now I just move him back one inch along the collision line
			// which is hence guarenteed not to push him through walls.
			
			GetPos() = point;	   					// move skater
			GetPos() += mWallNormal;	// move away from surface
			DUMP_POSITION;
			
			// Rotate velocity to plane.
			GetVel().RotateToPlane(mWallNormal);
			DUMP_VELOCITY;
			
			// Mick: if we set the velocity as direction, skater will keep going up more
			GetMatrix()[Z] = GetVel();
			
			GetMatrix()[Z].Normalize();
			GetMatrix()[Y] = mWallNormal;
			GetMatrix()[X] = Mth::CrossProduct(GetMatrix()[Y], GetMatrix()[Z]);
			GetMatrix()[X].Normalize();
			ResetLerpingMatrix();

			// Decide whether left or right wall-ride.
			#if 0 // old code
			// Mth::Vector Up(0.0f, 1.0f, 0.0f);
			// Mth::Vector Cross = Mth::CrossProduct(mWallNormal, Up);
			#else
			Mth::Vector Cross(-mWallNormal[Z], 0.0f, mWallNormal[X], 0.0f);
			#endif
			
			if (Mth::DotProduct(GetVel(), Cross) < 0.0f)
			{
				// Let the script do any extra logic, like playing anims & stuff.
				FLAGEXCEPTION(CRCD(0x5de19c83, "WallRideLeft"));
			}
			else
			{
				FLAGEXCEPTION(CRCD(0x51372712, "WallRideRight"));
			}
			
			SetState(WALL);

			// Handle contact			
			check_movable_contact();
			
			return true;
		}	
		else
		{
			// Otherwise trigger the bail script.
			// FLAGEXCEPTION(CRCD(0x2ec3c7f5, "WallRideBail"));
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CSkaterCorePhysicsComponent::rotate_away_from_wall ( const Mth::Vector& normal, float &turn_angle, float lerp )
{
	// given a wall normal, then calculate the "turn_angle" to rotate the skater by and rotate the matrix and display matrix by this
	// return the angle between the skater and the wall
	// note turn_angle is passed by reference, and is altered !!!!
	
	// given m_right(dot)normal, we should be able to get a nice angle
	float dot_right_normal = Mth::DotProduct(GetMatrix()[X], normal);

	float angle = acosf(Mth::Clamp(dot_right_normal, -1.0f, 1.0f)); 	

	if (angle > Mth::PI / 2.0f)
	{
		angle -= Mth::PI;
	}
	
	// angle away from the wall
	turn_angle = angle * GetPhysicsFloat(CRCD(0xe07ee1a9, "Wall_Bounce_Angle_Multiplier")) * lerp;
	
	// Rotate the skater so he is at a slight angle to the wall, especially if we are in a right angled corner, where the skater will bounce out

	GetVel().RotateY(turn_angle);
	DUMP_VELOCITY;
	GetMatrix().RotateYLocal(turn_angle);
	m_lerping_display_matrix.RotateYLocal(turn_angle);	   
	
	return angle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::check_leaning_into_wall (   )
{
	// If we are leaning left or right, then do a collision check in that direction, and proportional to the amount of the lean.
	// If there is a collision, then push the skater away from the wall.
	
	// First determine if we actually need to be doing this
	
	// Need to be moving at a reasonable rate
	if (GetVel().Length() > GetPhysicsFloat(CRCD(0xbc33d268, "Skate_min_wall_lean_push_speed"))) return;
	
	CControlPad& control_pad = mp_input_component->GetControlPad();

	float time;
	if (control_pad.m_left.GetPressed())
	{
		time = control_pad.m_left.GetPressedTime();
	}
	else if (control_pad.m_right.GetPressed())
	{
		time = control_pad.m_right.GetPressedTime();
	}
	else
	{
		return;
	}
	time *= 1.0f / 1000.0f;

	// Calculate the length of the vector based on how long you have held down the left or right button.  
	// (Ideally it would be tied to the animation, but this is simple, and it works)	
	float min_time = GetPhysicsFloat(CRCD(0x435f0653, "Skate_wall_lean_push_time"));
	if (time > min_time)
	{
		time = min_time;
	}
	
	if (control_pad.m_right.GetPressed())
	{
		time = -time;
	}
	float length = time / min_time * GetPhysicsFloat(CRCD(0x4b8bab12, "Skate_wall_lean_push_length"));	

	
	// Now we've got the length, and in the right direction, check for a collision
	
	m_col_start = GetPos() + GetMatrix()[Y] * GetPhysicsFloat(CRCD(0xbfbbd0af, "Skate_wall_lean_push_height"));
	m_col_end = m_col_start + GetMatrix()[X] * length;
	if (get_member_feeler_collision())
	{
	
		// see how much we are into the wall
		Mth::Vector push = m_feeler.GetPoint() - m_col_end;
		
		// Only push directly out from the wall (like the upwards collision, otherwise we get pushed back)
		Mth::Vector normal = m_feeler.GetNormal();
		push.ProjectToNormal(normal);
		
		m_col_start = GetPos() + GetMatrix()[Y];
		m_col_end = m_col_start + push;
		if (!get_member_feeler_collision())
		{
			// just move him, might put him 1 inch above the ground, but regular physics should snap him down
			GetPos() = m_col_end - GetMatrix()[Y];
			DUMP_POSITION;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::flip_if_skating_backwards (   )
{	
	// if sufficient speed, and facing backwards
	if (!GetFlag(IS_BAILING)
		&& !GetFlag(SKITCHING)		// if skitching, probably just car turning a slow sharp corner
		&& !is_braking()
		&& Mth::DotProduct(GetVel(), GetMatrix()[Z]) < 0.0f
		&& GetVel().LengthSqr() > Mth::Sqr(GetPhysicsFloat(CRCD(0x2bf71eeb, "Skater_Flip_Speed")))
		)
	{
		// flip Z and X, to rotate 180 degrees about y
		GetMatrix()[Z] = -GetMatrix()[Z];
		GetMatrix()[X] = -GetMatrix()[X];
		m_lerping_display_matrix[Z] = -m_lerping_display_matrix[Z];
		m_lerping_display_matrix[X] = -m_lerping_display_matrix[X];

		// Dan: we can no longer flip mid animation
		/*
		CSkaterFlipAndRotateComponent* p_flip_and_rotate_component = GetSkaterFlipAndRotateComponentFromObject(GetObject());
		Dbg_Assert(p_flip_and_rotate_component);
		p_flip_and_rotate_component->ToggleFlipState();
		*/
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::maybe_flag_ollie_exception (   )
{
	#ifdef __NOPT_ASSERT__
	if (GetFlag(TENSE) && Script::GetInteger("TurboOllie"))
	{
		m_tense_time = GetFlagElapsedTime(TENSE);
		SetFlagFalse(TENSE);
		FLAGEXCEPTION(CRCD(0x8ffefb28, "Ollied"));
		return true;
	}
	#endif
	
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	if (GetFlag(TENSE) && !control_pad.m_x.GetPressed())
	{
		// Remember the tense time, cause it will be needed when the Jump script command executes.
		m_tense_time = GetFlagElapsedTime(TENSE);
		
		SetFlagFalse(TENSE);
		
		control_pad.m_x.ClearRelease();
		
		FLAGEXCEPTION(CRCD(0x8ffefb28, "Ollied"));
		
		return true;
	}	

	return false;
}  

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
 
void CSkaterCorePhysicsComponent::maybe_skitch (   )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();

	if (GetFlag(SKITCHING) || !SKITCH_BUTTON || GetState() != GROUND) return;
	
	// Find the nearest skitch point.
	Mth::Vector closest_pos;
	float closest_dist = GetPhysicsFloat(CRCD(0x2928f080, "Skitch_Max_Distance"));
	CCompositeObject* p_closest = NULL;
	
	for (CSkitchComponent *p_skitch_comp = static_cast< CSkitchComponent* >(CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_SKITCH));
		  p_skitch_comp;
		  p_skitch_comp = static_cast< CSkitchComponent* >(p_skitch_comp->GetNextSameType()))
	{
		if (!p_skitch_comp->CanSkitch()) continue;
		
		CCompositeObject* p_composite_object = p_skitch_comp->GetObject();
		if (p_composite_object == GetObject()) continue;
		
		Mth::Vector	skitch_point(0.0f, 0.0f, 0.0f);
		int skitch_index = p_skitch_comp->GetNearestSkitchPoint(&skitch_point, GetPos());
		if (skitch_index == -1) continue;
		
		Mth::Vector line_to = skitch_point - GetPos();
		float dist_to = line_to.Length();
		if ( dist_to < closest_dist)
		{
			closest_dist = dist_to;
			p_closest = p_composite_object;
			closest_pos	= skitch_point;
			m_skitch_index = skitch_index;
		}
	}

	if (p_closest)
	{
		// Clear any triggers, so old L1/R1 presses don't affect us.
		control_pad.m_L1.ClearTrigger();
		control_pad.m_R1.ClearTrigger();
		mp_skitch_object = p_closest;

		FLAGEXCEPTION(CRCD(0x2f184eb1, "Skitched"));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::do_in_air_physics (   )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();

	// Acceleration is re-calculated every frame
	// here it is just set to the air gravity 
	// there are generally no other forces that we would use in the air

	Mth::Vector acc(0.0f, get_air_gravity(), 0.0f);

	SetFlagFalse(LAST_POLY_WAS_VERT);
	
	// disallow acid drops before this frame's breaking of vert air
	if (GetFlag(VERT_AIR) || GetFlag(SPINE_PHYSICS) || GetFlag(IN_ACID_DROP) || GetFlag(IS_BAILING))
	{
		SetFlagTrue(AIR_ACID_DROP_DISALLOWED);
	}
	
	///////////////////////////////////////////////////////////////////////////////
	// Adjust orientation before we do any movement
	// in the air the orientation is independent of the velocity but it will affect the collision checks we do (left/right/forwards/up)

	handle_air_rotation(); 		// rotate left/right (spinning by holding left/right or L1/R1)
	
	handle_air_lean();			// lean forwards backwards (by hold up/down) also handles spine leans
	
	handle_transfer_slerping();	// during acid drops, we slerp to our required orientation
	
	if (GetFlag(IN_RECOVERY) || VERT_RECOVERY_BUTTONS)
	{
		// no control here, except for the VERT_RECOVERY_BUTTONS, above 
		handle_air_vert_recovery();	// recovering from going off the end of a vert polygon
	}
	
	// Upright if not vert, and not doing a spine transfer
	if (!GetFlag(VERT_AIR) && !GetFlag(SPINE_PHYSICS) && !mp_rotate_component->IsApplyingRotation(X) && !mp_rotate_component->IsApplyingRotation(Z))
	{
		rotate_upright();
	}		

	// end of adjusting orientation in air
	////////////////////////////////////////////////////////////////////
	
	//////////////////////////////////////////////////////////////////////////////
	// Apply special friction even in the air
	
	if (m_special_friction_duration != 0.0f)
	{
		apply_rolling_friction();
	}
	
	// end of friction
	////////////////////////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////////////////////////
	// Calculate what velocity to use
	// generally this is just the stored m_vel, but if we are doing
	// a spine transfer	we a	dd in m_spine_vel
	// (note, we also update the state of the spine transfer here, 
	// perhaps that would be better done elsewhere)
	
	Mth::Vector vel = GetVel();
	
	///////////////////////////////////////////////////////////////////////////

	if (GetFlag(SPINE_PHYSICS))
	{
		// this check is only valid if we are not in contact with a moving object		
		if (vel[Y] < 0.0f && GetPos()[Y] < m_transfer_target_height)	// just check if we have dropped below the target height		
		{
			// if (Mth::Abs(mp_state_component->m_spine_vel.Length() - 0.1f) > 1.0f)
			// {
				// Gfx::AddDebugStar(GetPos(), 24, GREEN, 0);
			// }
			mp_state_component->m_spine_vel.Normalize(0.1f); // make spine velocity very small, but still there, so camera works
		}		
		else
		{
			vel += mp_state_component->m_spine_vel;
		}
	}

	///////////////////////////////////////////////////
	// calculate contact movement
	// If the skater is in contact with an object, then his velocity (m_vel) is considered to be local to that object, so here we have to add in the
	// movement of the object from the last frame; we also account for rotation here, in the call to mp_movable_contact_component->UpdateContact

	Mth::Vector	contact_movement;
	contact_movement.Set(0.0f, 0.0f, 0.0f);

	// if we are in contact with something, then factor in that "movement"
	if (mp_movable_contact_component->UpdateContact(GetPos()))
	{
		contact_movement = mp_movable_contact_component->GetContact()->GetMovement();
	}

	// end of calculating contact movment
	/////////////////////////////////////////////////////////////////////////

	///////////////////////////////////////////////////////////////
	// Update position
	
	// Update position using correct equations of motion (S = ut + 0.5at^2)
	GetPos() += vel * m_frame_length + acc * (0.5f * m_frame_length * m_frame_length);
	DUMP_POSITION;
	
	// add in movement due to contact
	GetPos() += contact_movement;
	DUMP_POSITION;
	
	#ifdef __NOPT_ASSERT__
	if (Script::GetInteger(CRCD(0x3ae85eef, "skater_trails")))
	{
		if (GetFlag(SPINE_PHYSICS))
		{
			Gfx::AddDebugLine(GetPos(), GetOldPos(), RED, 0, 0);
		}
		else if (GetFlag(VERT_AIR))
		{
			Gfx::AddDebugLine(GetPos(), GetOldPos(), YELLOW, 0, 0);
		}
		else
		{
			Gfx::AddDebugLine(GetPos(), GetOldPos(), BLUE, 0, 0);
		}
	}
	#endif
	
	//
	// end updating position
	//////////////////////////////////////////////////////////////
	
	
	/////////////////////////////////////////////////////////////////
	// update velocity
	//
	
	// Update Velocity in air	
	GetVel() += acc * m_frame_length;
	DUMP_VELOCITY;
	
	//
	// end updating velocity
	/////////////////////////////////////////////////////////////////

	/////////////////////////////////////////////////////////////////
	// Handle trying to break VERT	
	//	
	
	if (GetFlag(CAN_BREAK_VERT))
	{
		if (GetFlag(TRACKING_VERT) && GetFlag(VERT_AIR))
		{
			// might want to check how long we've been in the air before allowing this
			// also don't want to do it if we've been doing tricks and stuff 
			
			// first of all, check if there is a collision beneath my feet
			m_col_start = GetPos() + GetMatrix()[Y] * 1.0f;		// bit above the feet
			m_col_end = GetPos() + GetMatrix()[Y] * -23.0f;
			if (!get_member_feeler_collision())
			{
				maybe_break_vert();
				
				// if we did not break vert now, then only allow us to break vert later if we've been tapping the "up" button
				// this is indicated by us having RELEASED or Pressed in the last few ticks
				if (static_cast< int >(control_pad.m_up.GetReleasedTime()) > GetPhysicsInt(CRCD(0x6bb5b751, "Skater_vert_active_up_time"))
					&& static_cast< int >(control_pad.m_up.GetPressedTime()) > GetPhysicsInt(CRCD(0x6bb5b751, "Skater_vert_active_up_time")))
				{
					// "UP" was not pressed any time recently, so don't let us break late
					SetFlagFalse(CAN_BREAK_VERT);	
				}
			}
		}	 
		   
		// Allow us to break vert polys for a while after we ge onto them
		// to make it more robust
		// Essentially lets CAN_BREAK_VERT flag expire....
		if (static_cast< int >(Tmr::ElapsedTime(GetFlagTime(CAN_BREAK_VERT))) > GetPhysicsInt(CRCD(0x26495947, "Skater_Vert_Allow_break_Time")))
		{
			SetFlagFalse(CAN_BREAK_VERT);
		}
	}
	else
	{
		// We are flagged as not being able to break vert
		// however, we still want to be able to do this at any time on the up journey
		// if we try break the spine
		if (GetFlag(VERT_AIR) && BREAK_SPINE_BUTTONS && !GetFlag(SPINE_PHYSICS) && GetVel()[Y] > 0.0f)
		{
			maybe_break_vert();
		}
	
	}
	
	//
	// End of Handling breaking Vert
	////////////////////////////////////////
	
	if (!mp_movable_contact_component->HaveContact())
	{
		
		//////////////////////////////////////////////////////////////////////
		// Now update the tracking position before we do a collision check	   
		// "Tracking" is used when we are "vert" (moving in a vertical plane through the air above a quarterpipe), to track the ground directly
		// benath us, and if the QP bends, to move the skater appropiately so he can catch vert air along bent QPs, and round corners. 		 
		
		if (GetFlag(TRACKING_VERT) && GetFlag(VERT_AIR))
		{
			m_col_start[X] = GetPos()[X];
			m_col_start[Y] = m_vert_pos[Y];
			m_col_start[Z] = GetPos()[Z];
	
			m_col_end = m_col_start;
			
			m_col_start += m_vert_normal * 30;			// Away from face
			m_col_end -= m_vert_normal * 30;			// into face
	
			// Now see if there is a collision, and track it if so
	
			// First we try an above this position
			// raising us up by m_vert_upstep
			// which starts at 6 inches, and is halved down until
			// less than half an inch, at which point we stop trying to move up
	
			bool collision;
	
			if (m_vert_upstep > 0.5f)
			{
				m_col_start[Y] += m_vert_upstep;
				m_col_end[Y] += m_vert_upstep;
				// Only check for vert polys, ignore collision info
				collision = get_member_feeler_collision(0, mFD_VERT);  
		
				// If we did not find a collision six inches above, then check back at the old height
				if (!collision)
				{
					m_col_start[Y] -= m_vert_upstep;
					m_col_end[Y] -= m_vert_upstep;	
					m_vert_upstep *= 1.0f / 2.0f;			// binary search will zoom in on the edge		
					collision = get_member_feeler_collision(0, mFD_VERT);  
				}
			}
			else
			{
				collision = get_member_feeler_collision(0, mFD_VERT);  	
			}
	
	
			if (!collision)
			{
				// there is no collision, which usually mean we have gone off the end of a qp but might mean the tracking point has drifted off the top
				// of the very polygon (it might not be exactly horizontal), so try tracking down up to 30 inches to see if we can find it again
				for (int i = 0; i < 10; i++)
				{
					m_col_start[Y] -= 3.0f;
					m_col_end[Y] -= 3.0f;
					collision = get_member_feeler_collision(0, mFD_VERT);  
					if (collision)
					{
						// need to store new m_vert_pos
						// as code below does not generally allow us to go below it
						m_vert_pos[Y] = m_feeler.GetPoint()[Y];
						break;
					}
				}
				
			}
	
	
			// Dot product in the XZ plane is the angle between them			
			float change_dot = sqrtf(
				m_vert_normal[X] * m_feeler.GetNormal()[X] + m_vert_normal[Z] * m_feeler.GetNormal()[Z]
			);
			
			// the dot check is a fix for sharp corners, nearly are right angles, TT#438
			if (collision && change_dot > 0.02f)
			{
				// let's just track it simple for now
				Mth::Vector track_point = m_feeler.GetPoint();
	
				// This is a bit of a patch
				// basically clamp the tracking point at the hihgest level it has reached, that way we can never "slip" down a slope
				// which the rest of the physics makes you do, for reasons best explained with a diagram
				if (m_vert_pos[Y] > track_point[Y])  
				{
					track_point[Y] = m_vert_pos[Y];
				}
				
				// keep vert pos updated (only used for height)
				m_vert_pos = track_point;
				
				GetPos()[X] = track_point[X];	
				GetPos()[Z] = track_point[Z];	
				m_vert_normal = m_feeler.GetNormal();
	
				// offset the jumper away from the plane
				GetPos() += m_vert_normal * (GetPhysicsFloat(CRCD(0x78384871, "Physics_Vert_Push_Out")));
				DUMP_POSITION;
				
				// The normal we might be tracking might not be vert, so need to adjust it so it is fully horizontal
				// it should never be fully horizontal (which would cause it to implode									
				Mth::Vector	flat_normal = m_vert_normal;
				flat_normal[Y] = 0.0f;
				flat_normal.Normalize();
				
				// adjust the orientation to match the plane
				new_normal(flat_normal);
				
				// now velocity
				GetVel().RotateToPlane(flat_normal);
			} 
			else
			{
				SetFlagFalse(TRACKING_VERT);
			}
		}
	}
	
	//
	// End of updating tracking
	////////////////////////////////////////////////////////////////////////////////////////	
    
	///////////////////////////////////////////////////////
	// Adjust the normal while in the air	
	//
	
	// When on an air poly, we have the usual drifting of the UP vector to smooth out changes in the normal
	// (note, we can be in VERT_AIR, but also SPINE_PHYSICS, in whcih case the orientation is controled by the "lean" routine
	// which is leaning the skater forward, so he comes down on the opposing face)
	if (GetFlag(VERT_AIR) && !GetFlag(SPINE_PHYSICS)
		&& !mp_rotate_component->IsApplyingRotation(X) && !mp_rotate_component->IsApplyingRotation(Z))
	{
		adjust_normal();
	}
	else
	{
		// otherwise, the matrix is same for display and physics
		ResetLerpingMatrix();
	}
	
	//
	// End of adjusting normal
	//////////////////////////////////////////////////////////


	///////////////////////////////////////////////////////////
	// Handle any collisions resultant from our movement
	// remember at this point m_pos is the new position
	// and we might have gone through a wall
	
	// here again, we are factoring in the contact_movement to the start position											 
	// We check if there is anything in font of us
	bool snapped_up = handle_forward_collision_in_air(GetOldPos() + contact_movement);					

	// If we are now in the WALL (Wallride) state, then just return	
	if (GetState() == WALL || GetState() == WALLPLANT)
	{
		// Maybe call DoWallPhysics() to avoid a glitch? Maybe not, cos GetOldPos() will be wrong ?
		return;
	}	
	
	// check for actual movement collision (along the full line of travel)
	// note we factor in the contact_movement here which effectivly ignores it
	// so contact movement could possibly drag us through a solid object
	// if this is a problem, then we need to add another collision check, just for the contact movement
	m_col_start = GetOldPos() + contact_movement;
	m_col_end = GetPos();
								
	if (!snapped_up && get_member_feeler_collision())
	{
		bool hit_vertical = m_feeler.GetNormal()[Y] < 0.1f;
		
		if (check_for_air_snap_up(GetOldPos() + contact_movement) && (GetVel()[Y] > 10.0f || hit_vertical) && mp_movable_contact_component->GetTimeSinceLastLostContact() > 500)
		{
		}
		else
		{
			Mth::Vector normal = m_feeler.GetNormal();
				
			// set our position (the point of contact) to be that point we just found
			GetPos() = m_feeler.GetPoint();
			
			// Move point up one inch to avoid dropping through geometry with something below it (like canada blades, and park editor stuff)
			GetPos() += normal;
			DUMP_POSITION;
			
			if (m_col_flag_skatable)
			{
				//////////////////////////////////////////////////////////////////////////
				// handle landing on the ground
				// Collided with a skatable face!! yay, let's stick to it!
				
				SetState(GROUND);
				check_movable_contact();
				
				// Used by the LandedFromVert script command.
				// landing from a spine transfer is considered to be the same as landing from vert
				if (GetFlag(VERT_AIR) || GetFlag(SPINE_PHYSICS))
				{
					// Note: Not setting it to false if the VERT_AIR flag is not set.
					// This is because sometimes Scott wants to force the mLandedFromVert to be on from in script. Don't want landing to override that.
					// mLandedFromVert never gets reset by the C-code, only by the ResetLandedFromVert script command.
					mLandedFromVert = true;
					
	 				// This flag however cannot be cleared by script
					// Added this flag for use by ClearPanel_Landed, because mLandedFromVert is false at that point even if landing from vert,
					// due to being cleared by script.
					m_true_landed_from_vert = true;
				}
				else
				{
					m_true_landed_from_vert = false;
				}
					
				mLandedFromSpine = GetFlag(SPINE_PHYSICS);

				set_terrain(m_feeler.GetTerrain());
				
				mp_sound_component->PlayLandSound(GetObject()->GetVel().Length() / GetSkater()->GetScriptedStat(CRCD(0xcc5f87aa, "Skater_Max_Max_Speed_Stat")), mp_state_component->m_terrain);

				if (!mp_trick_component->GraffitiTrickStarted())
				{
					// clear the graffiti trick buffer if we're moving to a new world sector, but haven't started our trick yet...
					mp_trick_component->SetGraffitiTrickStarted(false);
				}

				set_last_ground_feeler(m_feeler);
				mp_trigger_component->CheckFeelerForTrigger(TRIGGER_LAND_ON, m_last_ground_feeler);

				// Now, we landed, and triggered an event, which might have reset us, so we should possibly abort here if we were restarted
				if (mp_physics_control_component->HaveBeenReset())
				{
					// K: I added this here to be consistent with the above change on 7 Mar (see above comment)
					// Need it though?
					FLAGEXCEPTION(CRCD(0x532b16ef, "Landed"));
					return;
				}
				
				
				if (!m_true_landed_from_vert 			// not if coming down off vert
					&& !mLandedFromVert				// and not pretending to (like jumping out of a lip trick)
					&& GetVel()[X] == 0.0f
					&& GetVel()[Z] == 0.0f 
					&& control_pad.m_down.GetPressed())
				{
					// we had just jumped straight up, and are braking, and not on vert
					GetVel()[Y] = 0.0f;	
					DUMP_VELOCITY;
				}
				else
				{
					if (!GetFlag(SPINE_PHYSICS))
					{
						GetVel().ProjectToPlane(m_feeler.GetNormal());	   	// kill vel that is perpendicular to normal
						DUMP_VELOCITY;
					}
					else
					{
						// special landing from transfers to prevent speed loss
						
						Mth::Vector landing_vel = GetVel();
						
						// rotate all velocity to the facing direction
						GetVel().RotateToNormal(m_transfer_goal_facing);
						DUMP_VELOCITY;
						
						// now, rotate into the plane instead of projecting (actually, m_transfer_goal_facing should already be in the plane)
						GetVel().RotateToPlane(m_feeler.GetNormal());
						DUMP_VELOCITY;
						
						// test what velocity we will have once the ground physics removes sideways velocity
						Mth::Vector test_vel = GetVel();
						remove_sideways_velocity(test_vel);
						
						// if we'd be moving upwards (this can occur because sometimes an acid drop's goal facing is along a vert, not down it; and thus,
						// a slight upwards rotation means that the skater will land pointing up and immediately hop off the vert; this looks very
						// bizzare, so we prevent it here)
						if (test_vel[Y] > 0.0f)
						{
							// use a standard landing instead
							GetVel() = landing_vel;
							GetVel().ProjectToPlane(m_feeler.GetNormal());
							DUMP_VELOCITY;
						}
						
						if (GetVel().LengthSqr() < Mth::Sqr(Script::GetFloat(CRCD(0x59484878, "Physics_Acid_Drop_Min_Land_Speed"))))
						{
							GetVel().Normalize(Script::GetFloat(CRCD(0x59484878, "Physics_Acid_Drop_Min_Land_Speed")));
						}
					}
					GetVel().ZeroIfShorterThan(10.0f);
				}
				
				SetFlagFalse(VERT_AIR);
				
				m_display_normal = m_feeler.GetNormal();
				m_current_normal = m_display_normal;
				m_last_display_normal = m_display_normal;
				
				GetMatrix()[Y] = m_display_normal;
				GetMatrix().OrthoNormalizeAbout(Y);   
				ResetLerpingMatrix();

				// Flagging the exceptions needs to be the last thing done, as the script for the
				// excpetion is executed immediately, and might need some of the above values
				// Like, PitchGreaterThan requires m_last_display_matrix to be correct (as calculated above)				
				
				FLAGEXCEPTION(CRCD(0x532b16ef, "Landed"));
				
				return;
				 	
				//
				// end of handling landing
				//////////////////////////////////////////////////////////////
			}
			else
			{
				// it's a wall, bounce off it
				GetVel().RotateToPlane(normal);			
				DUMP_VELOCITY;
				GetMatrix()[Z].RotateToPlane(normal);
				GetMatrix().OrthoNormalizeAbout(Z);
				ResetLerpingMatrix();
							
				// Bit of a patach here to move the skater away from the wall
				GetPos() += normal * GetPhysicsFloat(CRCD(0x23410c14, "Skater_Min_Distance_To_Wall"));
				DUMP_POSITION;
				
				// no acid drops after an in-air collision
				SetFlagTrue(AIR_ACID_DROP_DISALLOWED);
				
			}
		}
	}
	else
	{
		// No forward collision was found, so nothing to do   
		
		// so do another check to push me away from walls, will also need a wallride check here 
		#ifdef		STICKY_WALLRIDES
		if (!GetFlag(VERT_AIR))
		{
			// check if we are too close to a wall, and pop us out and away		
			check_side_collisions();
		}
		#endif
	}
	
	// Push skater down from any roof he might now be under			
	if (handle_upward_collision_in_air())
	{
		// no acid drops after an in-air collision
		SetFlagTrue(AIR_ACID_DROP_DISALLOWED);
	}
	
	//
	// End of handling collisions
	/////////////////////////////////////////////////////////////////////////////////////////////////
	
	// If we've triggered a jump, then do the jump (note, same as jump from ground for now, might want to make it different) 	
	maybe_flag_ollie_exception();
	
	// if in standard vanilla air, check for acid drop
    if (!GetFlag(AIR_ACID_DROP_DISALLOWED) && BREAK_SPINE_BUTTONS)
	{
		bool count_as_skate_off_edge = m_went_airborne_time > m_last_jump_time_stamp
			&& Tmr::ElapsedTime(m_went_airborne_time) < 250;
		
		SAcidDropData acid_drop_data;
		if (maybe_acid_drop(count_as_skate_off_edge, GetPos(), GetOldPos(), GetVel(), acid_drop_data))
		{
			enter_acid_drop(acid_drop_data);
		}
	}
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::handle_air_rotation (   )
{
	CControlPad& control_pad = mp_input_component->GetControlPad();		 

	//////////////////////////////////////////////////////////////////////////////////
	// Part 1 - Decide on what spin to do, based on the controls

	if (mNoSpin) return;
	
	// IF doing a spine transfer, then no auto turning....	
	if (GetFlag(SPINE_PHYSICS))
	{
		SetFlagFalse(AUTOTURN);
	}
	
	int time = 0; 			
	float rot = 0.0f;
	float no_time = 0.0f;
	float ramp_time = 0.0f;

	// if (!USE_BIKE_PHYSICS)
	// {
		if (m_spin_taps)
		{
			if (control_pad.m_L1.GetTriggered())
			{
				control_pad.m_L1.ClearTrigger();
				if (!control_pad.m_R1.GetPressed())	 	// ignore it if R1 is pressed
				{
					m_tap_turns += Mth::PI;
				}
			}
			if (control_pad.m_R1.GetTriggered())
			{
				control_pad.m_R1.ClearTrigger();
				if (!control_pad.m_L1.GetPressed())	 	// ignore it if L1 is pressed
				{
					m_tap_turns -= Mth::PI;
				}
			}
		
			if (m_tap_turns != 0.0f)
			{
				time = 1;
				float turn_amount = Mth::Sgn(m_tap_turns) * GetSkater()->GetScriptedStat(CRCD(0xf7a2acc1, "Physics_air_tap_turn_speed_stat"));
				if (Mth::Abs(turn_amount * m_frame_length) > Mth::Abs(m_tap_turns))
				{
					m_tap_turns = 0.0f;
				}
				else
				{
					m_tap_turns -= turn_amount * m_frame_length;
				}	
				rot = turn_amount;
			}		
		}
		else
		{
			if (control_pad.m_L1.GetPressed() && !control_pad.m_R1.GetPressed())
			{
				rot = GetSkater()->GetScriptedStat(CRCD(0x4957db43, "Physics_air_rotation_stat"));	
				time = 1;
				if (control_pad.m_L1.GetPressedTime() > GetPhysicsFloat(CRCD(0xabdd3395, "skater_autoturn_cancel_time")))
				{
					SetFlagFalse(AUTOTURN);
				}
			}								
			
			if (control_pad.m_R1.GetPressed() && !control_pad.m_L1.GetPressed())
			{
				rot = -GetSkater()->GetScriptedStat(CRCD(0x4957db43, "Physics_air_rotation_stat"));
				time = 1;
				if (control_pad.m_R1.GetPressedTime() > GetPhysicsFloat(CRCD(0xabdd3395, "skater_autoturn_cancel_time")))
				{
					SetFlagFalse(AUTOTURN);
				}
			}								
		}
	// }
	
	// if just transitioned from walking, ignore Left/Right for rotation purposes
	if (!GetFlag(NO_ORIENTATION_CONTROL))
	{
		// if time is set, then we just ignore the Left/Right button and take no account of the spinning ramp
		if (!time)
		{
			if (control_pad.m_left.GetPressed())
			{
				rot = GetSkater()->GetScriptedStat(CRCD(0x4957db43, "Physics_air_rotation_stat"));	
				time = control_pad.m_left.GetPressedTime();
				if (time > GetPhysicsFloat(CRCD(0xabdd3395, "skater_autoturn_cancel_time")))
				{
					SetFlagFalse(AUTOTURN);
				}
			}
		
			if (control_pad.m_right.GetPressed())
			{
				rot = -GetSkater()->GetScriptedStat(CRCD(0x4957db43, "Physics_air_rotation_stat"));	
				time = control_pad.m_right.GetPressedTime();
				if (time > GetPhysicsFloat(CRCD(0xabdd3395, "skater_autoturn_cancel_time")))
				{
					SetFlagFalse(AUTOTURN);
				}
			}
			
			no_time = GetPhysicsFloat(CRCD(0xa092b2be, "Physics_Air_No_Rotate_Time"));
			ramp_time = GetPhysicsFloat(CRCD(0x5d756349, "Physics_Air_Ramp_Rotate_Time"));
		
			// if just tapped, then no leaning for a while
			if (time <= no_time)
			{
				rot = 0;
			}
		
			// if tapped enough, then ramp up to full speed over a small amount of time		
			if (time < ramp_time)
			{
				rot = rot * (time - no_time) / (ramp_time - no_time);
			}
		}
	}
	
	// if we are not rotating by hand, then might want to auto-turn	
	if (!rot && GetFlag(VERT_AIR) && GetFlag(AUTOTURN))
	{
		float angle_from_vert = acosf(Mth::Clamp(GetMatrix()[Z][Y], -1.0f, 1.0f));
		
		if (angle_from_vert < Mth::DegToRad(GetPhysicsFloat(CRCD(0x61224f2e, "skater_autoturn_vert_angle"))))
		{
			// If pointing more or less straight up, then don't auto-turn
			SetFlagFalse(AUTOTURN);				
		}
		else
		{
			float angle = Mth::GetAngleAbout(GetMatrix()[Z], m_fall_line, GetMatrix()[Y]);
							
			rot = Mth::Sgn(angle) * GetPhysicsFloat(CRCD(0x9db33213, "Skater_autoturn_speed"));
			if (Mth::Abs(rot * m_frame_length) > Mth::Abs(angle))
			{
				// just about done, so just turn the last bit of angle left
				rot = angle / m_frame_length;
				SetFlagFalse(AUTOTURN);
			}
			#if 0 // Dan: removed as this code has no effect
			time = 1;	  		// frig, we really don't want to start auto turn immediately
			ramp_time = 0;
			#endif
		}
	}
	
	// End of Part 1
	//////////////////////////////////////////////////////////////////////////////////
	
	if (rot == 0.0f) return;
	
	//////////////////////////////////////////////////////////////////////////////////
	// Part 2 - Do the actual rotation

	mYAngleIncreased = rot > 0.0f;
			
	rot *= m_frame_length;
	GetMatrix().RotateYLocal(rot);				
	m_lerping_display_matrix.RotateYLocal(rot);				
	
	// End of Part 2
	//////////////////////////////////////////////////////////////////////////////////
		
							   
	//////////////////////////////////////////////////////////////////////////////////
	// Part 3 - tracking rotation for the purposes of score (Spin multipliers)								   
	// Keep track of the total angle spun through.
	
	mp_trick_component->mTallyAngles += Mth::RadToDeg(rot);
	
	// Set the spin.
	if (GetFlag(VERT_AIR) && !GetFlag(SPINE_PHYSICS))
	{
		// If in vert air, only count the spin if it is at least 360, because getting 180 is too easy.
		if (Mth::Abs(mp_trick_component->mTallyAngles) >= 360.0f - GetPhysicsFloat(CRCD(0x50c5cc2f, "spin_count_slop")) + 0.1f)
		{
			mp_score_component->GetScore()->UpdateSpin(mp_trick_component->mTallyAngles);
		}	
	}
	else
	{
		mp_score_component->GetScore()->UpdateSpin(mp_trick_component->mTallyAngles);
	}	
	
	// End of Part 3
	//////////////////////////////////////////////////////////////////////////////////
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::handle_air_lean (   )
{						
	// Handle leaning formward and backwards in the air
												 
	//////////////////////////////////////////////////////////////////////////////////
	// Spine specific lean, should be seperated out for spine physics
	// we automatically rotate forward but don't let skaters's up vector to go past horizontal 
	
	// no leaning during transfers; we slerp instead
	if (GetFlag(SPINE_PHYSICS) || GetFlag(NO_ORIENTATION_CONTROL)) return;
	
	#if 0 // old transfer code
	if (GetFlag(SPINE_PHYSICS))
	{
		if (GetMatrix()[Y][Y] > -0.05f)	   // if y component of up vector (the Y vector) is +ve, then we are heads up
		{
			float rot = GetSkater()->GetScriptedStat(CRCD(0x110a1742, "Physics_spine_lean_stat"));
				
			GetMatrix().Rotate(m_spine_rotate_axis, -rot*m_frame_length);
		}
		return;
	}
	#endif
	
	//
	//////////////////////////////////////////////////////////////////////////////////
	
	///////////////////////////////////////////////////////////////////////////////////
	// 1 - Control Logic	
	//
	
	// don't even try to lean if in VERT_AIR		  
	if (GetFlag(VERT_AIR)) return;
	
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	// Don't want to lean if "grab" or "kick" pressed, as we could be doing some kind of grab trick
	if (control_pad.m_circle.GetPressed() || control_pad.m_square.GetPressed()) return;
	
	float rot = 0.0f;
	int time = 0;
	
	if (control_pad.m_up.GetPressed())
	{
		rot = GetSkater()->GetScriptedStat(CRCD(0xcbfdd841, "Physics_air_lean_stat"));
		time = control_pad.m_up.GetPressedTime();
	}

	if (control_pad.m_down.GetPressed())
	{
		rot = -GetSkater()->GetScriptedStat(CRCD(0xcbfdd841, "Physics_air_lean_stat"));	
		time = control_pad.m_down.GetPressedTime();
	}

	float no_time = GetPhysicsFloat(CRCD(0x5f70ef46, "Physics_Air_No_Lean_Time"));
	float ramp_time = GetPhysicsFloat(CRCD(0x12cd2d14, "Physics_Air_Ramp_Lean_Time"));

	// if just tapped, then no rotation for a while
	if (time <= no_time) return;
	
	// if tapped enough, then ramp up to full speed over a small amount of time		
	if (time < ramp_time)
	{
		rot = rot * (time - no_time) / (ramp_time - no_time);
	}

	// end of control logic
	//////////////////////////////////////////////////////////////////////////////////
	

	//////////////////////////////////////////////////////////////////////////////////
	// air lean physics
	
	if (rot == 0.0f) return;
	
	GetMatrix().RotateXLocal(rot*m_frame_length);				
	m_lerping_display_matrix.RotateXLocal(rot*m_frame_length);				
	
	// end of air lean physics
	//////////////////////////////////////////////////////////////////////////////////
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::handle_transfer_slerping (   )
{
	// during transfers, we slerp to our required orientation
	if (!GetFlag(SPINE_PHYSICS)) return;
	
	// we can't simply use the slerp's result vector at the skater's vector, because we need to allow the skater to rotate; instead, we apply
	// the change in the result vector each frame
	
	// increment the timer
	m_transfer_slerp_timer += m_frame_length;
	
	// calculate this frame's slerp result
	Mth::Matrix transfer_slerp_current_matrix;
	m_transfer_slerper.getMatrix(
		&transfer_slerp_current_matrix,
		Mth::SmoothStep(Mth::ClampMax(m_transfer_slerp_timer / m_transfer_slerp_duration, 1.0f))
	);
	
	// invert the slerp result vector from last frame
	Mth::Matrix inverse_transfer_slerp_previous_matrix = m_transfer_slerp_previous_matrix;
	inverse_transfer_slerp_previous_matrix.InvertUniform();
	
	// calculate the change between the frames
	Mth::Matrix transfer_slerp_delta_matrix = inverse_transfer_slerp_previous_matrix * transfer_slerp_current_matrix;
	
	// apply the change to the skater's matrix
	GetMatrix() *= transfer_slerp_delta_matrix;
	ResetLerpingMatrix();
	
	// store this frame's matrix
	m_transfer_slerp_previous_matrix = transfer_slerp_current_matrix;
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::handle_air_vert_recovery (   )
{						   
	// recovering from going off the end of a vert polygon	

	// don't want to recover during a transfer
	if (GetFlag(SPINE_PHYSICS)) return;

	if (GetVel()[Y] > 0.0f && mp_movable_contact_component->GetContact()) return;
	
	// right, we are in vert, but no longer tracking, so perhaps we are over flat ground?
	CFeeler	feeler;
	feeler.m_start = GetPos();
	feeler.m_end = GetPos();
	feeler.m_end[Y] -= 500.0f;
	
	// can't find ground
	if (!feeler.GetCollision()) return;

	// still over vert poly
	if (feeler.GetFlags() & mFD_VERT) return;
	
	// still over steep ground
	if (feeler.GetNormal()[Y] < 0.2f) return;

	if (GetFlag(VERT_AIR))
	{
		SetFlagFalse(VERT_AIR);
	}

	SetFlagTrue(IN_RECOVERY);

	// we want to smoothly rotate the skater in the plane formed by the up vector, and the skater's up vector
	
	// got sufficently far up 
	if (GetMatrix()[Y][Y] > 0.9f) return;
	
	float rot = GetSkater()->GetScriptedStat(CRCD(0xe7116a96,"Physics_recover_rate_stat"));	
	
	// need to rotate the Y vector in the plane formed by the XZ velocity and the (0,1,0) up vector
	// (or rather, rotate it about the vector prependicular to this
	
	// get a vector perpendicular to the plane containing m_matrix[Z] and the world up 	
	Mth::Vector forward = GetMatrix()[Y];
	forward[Y] = 0.0f;
	forward.Normalize();
	#if 0 // old code
	// Mth::Vector world_up(0.0f,1.0f,0.0f);
	// Mth::Vector side = Mth::CrossProduct(forward,world_up);
	#else
	Mth::Vector side(-forward[Z], 0.0f, forward[X], 0.0f);
	#endif
	
	GetMatrix().Rotate(side, rot * m_frame_length);
	
	ResetLerpingMatrix();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::rotate_upright (   )
{
	// Attempt to upright player (sideways) while going through air
	//
	// Cannot just check angle between mUp and world_up as that would include
	// tilting forward/backward when character is already uprighted sideways
	// (and tilting forward/backward should solely be under player control).
	// Thus, should rotate about mFront only.
	//
	// Then, basically what we want to do is:
	// 1) Rotate about mFront so that mUp is in the plane of world_up and mUp.
	// 2) Pick one of CW/CCW to rotate by, such that angle between mUp and
	//    world_up is <= 90 degrees.
	//
	// As we can be off by at most 180 degrees, rotating by 5 degrees would
	// upright us in 180/5=36 frames. For 45 degrees off (more likely), we
	// would be uprighted in 45/5=9 frames.
	//
	// Your average jump is probably around 30 frames long, so try 1 degree?
	
	// If the skater's Z vector is nearly straight up or down, then 
	// uprighting sideways will not look good 
	// (On XBox, some innacuracy in Z causes the skater to rotate oddly during "drop-in" situations)
	// so, here we test for this case, and just return if so
	if (Mth::Abs(GetMatrix()[Z][Y]) > 0.95f) return;
	
	// get a vector perpendicular to the plane containing m_matrix[Z] and the world up 	
	#if 0 // old code
	// Mth::Vector v;
	// Mth::Vector world_up;
	// world_up.Set(0.0f,1.0f,0.0f);
	// v = Mth::CrossProduct(GetMatrix()[Z],world_up);
	#else
	Mth::Vector v(-GetMatrix()[Z][Z], 0.0f, GetMatrix()[Z][X], 0.0f);
	#endif

	// Then get the dot product of the up vector with this 	
	// if up vector is in the same plane, then this will be zero
	float dot = Mth::DotProduct(GetMatrix()[Y], v);
	if (dot > 0.02f * m_frame_length * 60.0f) // prevent wobbling
	{
		float rot = Mth::DegToRad(Script::GetFloat(CRCD(0xabd57877, "skater_upright_sideways_speed")));
		GetMatrix().RotateZLocal(rot * m_frame_length);				
		ResetLerpingMatrix();
	}
	else if (dot < -0.02f * m_frame_length * 60.0f)
	{
		float rot = Mth::DegToRad(Script::GetFloat(CRCD(0xabd57877, "skater_upright_sideways_speed")));
		GetMatrix().RotateZLocal(-rot * m_frame_length);				
		ResetLerpingMatrix();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::handle_forward_collision_in_air ( const Mth::Vector &start_pos )
{
	// return true if there was a collision, but we snapped up over it
	
	Mth::Vector	forward = GetPos() - start_pos;
	forward.Normalize();	
	
	Mth::Vector up_offset = GetMatrix()[Y] * GetPhysicsFloat(CRCD(0xd4205c9b, "Skater_First_Forward_Collision_Height"));
	
	m_col_start = start_pos + up_offset;
	m_col_end = GetPos() + up_offset + forward * GetPhysicsFloat(CRCD(0x20102726, "Skater_First_Forward_Collision_Length"));
	
	if (get_member_feeler_collision())
	{
		// we have hit something going forward
		// either it is a wall, and we need to bounce off it
		// or it is a floor, and we need to stick to it.
							 
		Mth::Vector normal = m_feeler.GetNormal();

		float dot = Mth::DotProduct(normal, m_current_normal);
		if (!m_col_flag_skatable || (dot < 0.8f && normal[Y] < 0.5f))
		{

		   // at this point we have hit something that is not skatable and is fairly vertical
		   // so we are considering if we want to bounce off it
		   // one possibility is that it is a low ledge
		   //
		   // so, we check for snapping up, and if we do snap up, and hit_vertical is true, then we don't bounce off the wall
			
			/*
			// View from side
									   /
									  /	 <---------- Movement of skater
									 /
									/
				---------------+   /
							   |  /
							   | /
							   |/
							   /  <-------------  point of collision 
							  /|
							 / |
							/  |
						   /   |
						  /    +---------------	 <-------------- The ground
						 /
						/
					   /
					  /
					 /	<------------ where we end up
			*/
		   
			// Need to store m_feeler, as it is corrupted by CheckForAirSnapUp
			CFeeler tmp_feeler = m_feeler;
			
			bool hit_vertical = m_feeler.GetNormal()[Y] < 0.1f;
			
			// Since this collision had the forward vector added into it, we need to temporarily add in the forward vector again
			// so we can check for snapping up; note this will result in the skater snapping forward by the length of the forward vector (currently 10")
			// whenever we snap up in this particular check		    			
			Mth::Vector	tmp = GetPos();
			GetPos() = m_col_end - up_offset;					
			if (check_for_air_snap_up(start_pos))
			{
				// we snapped up, so if we are either moving upward or the first collision was near vertical, then we return true, allowing us to continue
				// if we were moving upwards, then we would continue into the air
				// if we were moving down, but the collision was near vert, then we do not want to bonk off the surface, and instead we let
				// the landing happen in the next frame
				if (GetVel()[Y] > 10.0f || hit_vertical) return true;
			}
			
			GetPos() = tmp;
			DUMP_POSITION;
			m_feeler = tmp_feeler;

			#ifdef		SNAP_OVER_THIN_WALLS
			// we have hit a wall, and we can't snap up over i;  see if we can just move up and go over it....

			// only do this if we are roughly vertical and going up and the wall is very verticle
			if (GetMatrix()[Y][Y] > 0.5f		   // skater is upright
			    && GetVel()[Y] > 0.0f			   // skater travelling upwards
				&& normal[Y] < 0.01f)					   // thing we hit is not far off vert
			{
				 
				float snap_Y = GetPhysicsFloat(CRCD(0x786b3272, "Physics_Air_Snap_Up"));
				tmp_feeler.m_start[Y] += snap_Y;
				tmp_feeler.m_end[Y] += snap_Y;
						   
				if (!tmp_feeler.GetCollision())
				{
					while (snap_Y > 4.0f)
					{
						// move down until we get a collision
						// so as to minimize the amount we snap up
						tmp_feeler.m_start[Y] -= 2.0f;
						tmp_feeler.m_end[Y] -= 2.0f;
						if (tmp_feeler.GetCollision()) break;
						snap_Y -= 2.0f;
					}
					GetPos()[Y] += snap_Y;
					DUMP_POSITION;
					return true;
				}
			}
			#endif
			
			// If we are on a vert poly, then just slide off the wall
			// just kill velocity perpendicular to the wall
			if (GetFlag(VERT_AIR))
			{
				GetPos() = m_feeler.GetPoint() - up_offset;
				GetPos() += normal * GetPhysicsFloat(CRCD(0x23410c14, "Skater_Min_Distance_To_Wall"));				
				DUMP_POSITION;
				
				if (!GetFlag(SPINE_PHYSICS))
				{
					GetVel().ProjectToPlane(normal);
				}
				else
				{
					GetVel().RotateToPlane(m_feeler.GetNormal());
				}
				DUMP_VELOCITY;
				return false;			
			}
			
			// maybe wall plant
			if (check_for_wallplant()) return false;
			
			// maybe wall ride
			if (check_for_wallride()) return false;
			
			// no acid drops after a in-air collision
			SetFlagTrue(AIR_ACID_DROP_DISALLOWED);
				
			mp_trigger_component->CheckFeelerForTrigger(TRIGGER_BONK, m_feeler);
			if (mp_physics_control_component->HaveBeenReset()) return false;

			// only play bonk sound for things that are near vert 												  
			if (m_feeler.GetNormal().GetY() < 0.05f)
			{
				mp_sound_component->PlayBonkSound(GetObject()->GetVel().Length() / GetSkater()->GetScriptedStat(CRCD(0xcc5f87aa, "Skater_Max_Max_Speed_Stat")), m_feeler.GetTerrain());
			}

			// it's a wall, bounce off it
			
			float vel_y = 0.0f;

			// ignore y vel if the polygon is not too much upside down 
			if (normal[Y] > -0.1f)
			{
				vel_y = GetVel()[Y];					// remember old Y vel
				GetVel()[Y] = 0.0f;					// kill Y vel
				DUMP_VELOCITY;
			}
			
			GetVel().ProjectToPlane(normal);											// project X and Z to plane of collision poly	
			GetVel() += normal * (GetVel().Length() * (1.0f / 10.0f));		// 10% tiny push away.....
			DUMP_VELOCITY;
			
			if (normal[Y] > -0.1f)
			{
				GetVel()[Y] = vel_y;					// restore old Y vel, so it's impossible to jump higher by jumping against a wall
				DUMP_VELOCITY;
			}			
			
			float Z_Y = GetMatrix()[Z][Y];
			GetMatrix()[Z][Y] = 0.0f;
			GetMatrix()[Z].RotateToPlane(normal);
			GetMatrix()[Z][Y] = Z_Y;
			GetMatrix()[Z] += (1.0f / 20.0f) * normal;
			GetMatrix()[Z].Normalize();			
			GetMatrix()[X] = Mth::CrossProduct(GetMatrix()[Y], GetMatrix()[Z]);
			GetMatrix()[X].Normalize();
			GetMatrix()[Y] = Mth::CrossProduct(GetMatrix()[Z], GetMatrix()[X]);
			GetMatrix()[Y].Normalize();
			
			// Bit of a patach here to move the skater away from the wall
			GetPos() = m_feeler.GetPoint() - up_offset;
			GetPos() += normal * GetPhysicsFloat(CRCD(0x23410c14, "Skater_Min_Distance_To_Wall"));
			DUMP_POSITION;
			
			// if the thing we have collided with is a movable object, then add in the last movement to move us away 
			if (m_feeler.IsMovableCollision())
			{
				Mth::Vector obj_vel = m_feeler.GetMovingObject()->GetVel();

				// if object is moving in same direction as face normal
				if (Mth::DotProduct(obj_vel, m_feeler.GetNormal()) > 0.0f)
				{
					// then add in the velocity, just to be on the safe side
					GetPos() += obj_vel * m_frame_length * 2.0f;		// 2.0f is a safety factor
					DUMP_POSITION;
					// and move with the thing we just hit
					GetVel() += obj_vel;					
					DUMP_VELOCITY;
				}
			}
		}
	}
	return false;
}	 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::check_for_air_snap_up ( const Mth::Vector& start_pos )
{
	////////////////////////////////////////////////////////////////////////////////////////
	// We moved from GetOldPos() to m_pos
	// and some collision was detected
	// either along this line, or along the "forward" line, which is 8 inches above
	// regardless, we want to see if the new m_pos (considered bad)
	// can be snapped upwards to a surface, where the normal is in the same direction as
	// the current movement
	// and check to see if there is a clear line to this position
	// currently in physics.q:  Physics_Air_Snap_Up = 15       
	//
	// Note, we ignore if the skater is going up or down, the calling code has to 
	// handle that (for the spcial case of hitting a wall coming down, when we can snap over it)

	// don't do it if bailing, to avoid snapping through loops
	if (GetFlag(IS_BAILING)) return false;
	
	CFeeler feeler;
	
	float up_len = GetPhysicsFloat(CRCD(0x786b3272,"Physics_Air_Snap_Up"));
	
	feeler.m_start = GetPos();
	feeler.m_start[Y] += up_len;
	feeler.m_end = GetPos();
	
	// Since the new pos might actually be ABOVE the ledge (i.e., we might have just clipped though the corner, and not be inside it)
	// then we need to check all the way down to the previous height, but only if moving upwards
	if (start_pos[Y] < GetPos()[Y])
	{
		feeler.m_end[Y] = start_pos[Y];		
	}

	// if the start pos, plus the snap up distance was above the top of the collision line, then extend the collision line up to that	
	if (start_pos[Y] + up_len > feeler.m_start[Y])
	{
		feeler.m_start[Y] = start_pos[Y] + up_len;
	}
	
	// set up the feeler, now check for collision
	if (feeler.GetCollision())
	{
		// Collision, possibly something we can snap up to
		
		// movement vector is in same direction as face normal, so we are moving away from it
		// usually this implies we are moving up, away from the horizontal face at the top of a wall, or a curb
		if (feeler.GetNormal().GetY() > 0.5f)		// not if too vertical
		{			
			// Okay, we can snap up, so all we have to do is see if the line is clear
			Mth::Vector	snap_point = feeler.GetPoint() + feeler.GetNormal();
			
			feeler.m_start = start_pos;
			feeler.m_start[Y] += up_len;
			feeler.m_end = snap_point;
			feeler.m_end[Y] += up_len;
			
			if (!feeler.GetCollision())
			{
				// No collision along upper line,so we consider it safe to move
				// note, this will generally result in passing through the corner of some geometry (generally like the top of a wall, or a curb)
				GetPos() = snap_point;	 		// set to snap point
				GetPos()[Y] += 0.1f;				// offset up a little, so we are outside the plane
				DUMP_POSITION;
				return true;								// return true, indicating we snapped up, so carry on in air
			}
			else
			{
				// was a collision when trying to move to new position, so don't move
			}
		}
		else
		{
			// too vert, so don't allow snap (probably on a QP)
		}

		return false;
	}
	else
	{
		// No collision, so don't do anything, probably deep within a wall, or off the level.
	}

	return false;	 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::handle_upward_collision_in_air (   )
{
	// return true if there was a collision, but we snapped up over it
	
	// if we are upside down, then return false
	if (GetMatrix()[Y][Y] < 0.0f) return false;
	
	float head_height = GetPhysicsFloat(CRCD(0x542cf0c7, "Skater_default_head_height"));
	
	// ignore head collisions for a duration after wallplants
    if (Tmr::ElapsedTime(m_last_wallplant_time_stamp) <= static_cast< Tmr::Time >(Script::GetInteger(CRCD(0x2757ed2c, "Physics_Ignore_Ceilings_After_Wallplant_Duration"))))
	{
		head_height = 6.0f;
	}
	
	Mth::Vector up_offset = GetMatrix()[Y] * head_height;
	
	m_col_start = GetPos();
	
	m_col_end = GetPos();
	m_col_end += up_offset;
	
	if (get_member_feeler_collision())
	{
		Mth::Vector ceiling_normal = m_feeler.GetNormal();
		
		// if it's not at least tilted a bit downwards, then ignore it and return false	   
		if (ceiling_normal[Y] > -0.1f) return false;
					  
		// get the vector we need to push down
		Mth::Vector push_down = m_feeler.GetPoint() - m_col_end;
		
		// only push down away from the ceiling	(otherwise we would push back along the line of travel, jerky)
		push_down.ProjectToNormal(ceiling_normal);

		// push down as far as we can go, so check for collisions		
		m_col_start = GetPos();
		m_col_end = GetPos() + push_down;
		if (get_member_feeler_collision())
		{
			GetPos() = m_feeler.GetPoint() + 0.001f * m_feeler.GetNormal();
			DUMP_POSITION;
		}
		else
		{
			GetPos() = m_col_end;
			DUMP_POSITION;
		}
		
		GetVel().ProjectToPlane(ceiling_normal);
		DUMP_VELOCITY;
		
		return true; 	// we have had a collision, so return true
	}
	
	return false;  // no collision found, return false
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::handle_upward_collision_in_wallride (   )
{
	m_col_start = GetPos() + 18.0f * mWallNormal;
	
	m_col_end = m_col_start;
	m_col_end[Y] += GetPhysicsFloat(CRCD(0x542cf0c7, "Skater_default_head_height"));
	
	if (get_member_feeler_collision())
	{
		Mth::Vector ceiling_normal = m_feeler.GetNormal();
		
		// if it's not at least tilted a bit downwards, then ignore it and return false	   
		if (ceiling_normal[Y] > -0.1f) return false;
					  
		// get the vector we need to push down
		Mth::Vector push_down = m_feeler.GetPoint() - m_col_end;
		
		// only push down away from the ceiling	(otherwise we would push back along the line of travel, jerky)
		push_down.ProjectToNormal(ceiling_normal);

		// push down as far as we can go, so check for collisions		
		m_col_start = GetPos();
		m_col_end = GetPos() + push_down;
		if (get_member_feeler_collision())
		{
			GetPos() = m_feeler.GetPoint() + m_feeler.GetNormal();
			DUMP_POSITION;
		}
		else
		{
			GetPos() = m_col_end;
			DUMP_POSITION;
		}
		
		GetVel().ProjectToPlane(ceiling_normal);
		DUMP_VELOCITY;
		
		return true; 	// we have had a collision, so return true
	}
	
	return false;  // no collision found, return false
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::do_wallride_physics (   )
{
	SetFlagFalse(SPINE_PHYSICS);
	SetFlagFalse(IN_ACID_DROP);
	SetFlagFalse(IN_RECOVERY);
	SetFlagFalse(AIR_ACID_DROP_DISALLOWED);

	// Wallriding will cancel any memory of what rail we were on, so the next one seems fresh	
	mp_rail_node = NULL;
	m_last_rail_trigger_node_name = 0;
	


	// Keep updating the time, cos it needs to be the time that the last wall ride finished.
	mWallrideTime = Tmr::GetTime();

	// set wallride movement to nothing
	Mth::Vector m_movement(0.0f, 0.0f, 0.0f);
	
	// if we are in contact with something, then factor in that "movement"
	if (mp_movable_contact_component->UpdateContact(GetPos()))
	{
		m_movement = mp_movable_contact_component->GetContact()->GetMovement();
		if (mp_movable_contact_component->GetContact()->IsRotated())
		{
			GetMatrix() *= mp_movable_contact_component->GetContact()->GetRotation();
			m_lerping_display_matrix *= mp_movable_contact_component->GetContact()->GetRotation();
		}
	}

	// Mick: changed to include gravity
	// note this is the way THPS2 worked
	// the physics might not be intuitive, but it works	
	// also note we are intentionally not removing sideways components of velocity, so we get to drift up a bit
	// after we hit the wall, allowing us to to the "claw" trick (Jump-WallRide-Jump-Grind)
	
	Mth::Vector acc(0.0f, GetPhysicsFloat(CRCD(0xc617caf, "Wall_Ride_Gravity")), 0.0f);
	GetPos() += GetVel() * m_frame_length + acc * (0.5f * m_frame_length * m_frame_length);
	GetPos() += m_movement;
	DUMP_POSITION;
	GetVel() += acc * m_frame_length;
	DUMP_VELOCITY;
			  
	#if 0 // old code
	// Mth::Vector Down(0.0f,-1.0f,0.0f);
	// Mth::Vector Horiz=Mth::CrossProduct(mWallNormal,Down);
	#else
	Mth::Vector Horiz(mWallNormal[Z], 0.0f, -mWallNormal[X], 0.0f);
	#endif
	Horiz.Normalize();
	
	// Down is a unit vector pointing down along the plane of the wall.
	Mth::Vector Down = Mth::CrossProduct(Horiz, mWallNormal);
	
	float Theta = GetPhysicsFloat(CRCD(0x64a48a64, "Wall_Ride_Turn_Speed"));
	
	// Check if Theta is bigger than the angle the skater's z is already making with the Down vector.
	// If Theta is bigger, don't rotate by it, otherwise the skater will turn beyond the vertical.
	float zdot = Mth::DotProduct(GetMatrix()[Z], Down);
	if (zdot > 0.68f || (zdot > 0.0f && zdot > cosf(Theta)))
	{
		// Pointing pretty much straight down, so don't turn.
	}
	else
	{
		// Choose which way to turn.
		// Need to turn one way or the other depending on whether the skater's z axis is on the left or right side of the down vector.
		// This is the same as whether the skater's x axis is pointing up or down, so just check the sign of the dot product of x with down.
		float xdot = Mth::DotProduct(GetMatrix()[X], Down);
		if (xdot <= 0.0f)
		{
			Theta = -Theta;
		}	

		// Not strictly required for NGPS, fixes a problem on NGC. However, given m_vel is used only
		// as a three-element vector, not a bad idea to set this for all platforms.
		GetVel()[W] = 1.0f;
	
		// Rotate both the skater and the skater's velocity by Theta about the wall normal axis.
		GetVel().Rotate(mWallNormal, Theta);
		DUMP_VELOCITY;

		// set skater to face in the same direction as velocity
		GetMatrix()[Z] = GetVel();
		
		GetMatrix()[Z].Normalize();
		GetMatrix()[Y] = mWallNormal;
		GetMatrix()[X] = Mth::CrossProduct(GetMatrix()[Y], GetMatrix()[Z]);
		GetMatrix()[X].Normalize();
		ResetLerpingMatrix();
	}	
	
	// Forward collision check	
	Mth::Vector forward = GetPos() - GetOldPos();
	forward.Normalize();	
	
	m_col_start = GetOldPos();
	
	m_col_end = GetPos();
	m_col_end += forward * GetPhysicsFloat(CRCD(0x20102726, "Skater_First_Forward_Collision_Length"));

	if (get_member_feeler_collision())
	{
		// we have hit something going forward
		// either it is a wall, and we need to bounce off it or it is a steep QP, and we need to stick to it.
		// (note, with the slow normal changing, then it is possible that we might even collide with the poly that we are on)
		
		Mth::Vector	normal = m_feeler.GetNormal();								
								
		float dot = Mth::DotProduct(normal, mWallNormal);			

		GetPos() = m_feeler.GetPoint() + normal;
		DUMP_POSITION;

		// For fairly shallow curves, the dot between two normals will be pretty large
		// it's very important here to distinguish between a tight curve (like in a narrow QP) and a direct hit with a wall.
		
		if (!m_col_flag_wallable || Mth::Abs(dot) < 0.01f)
		{
			if (normal[Y] > 0.9f)
			{
				// If the new poly is the ground, pop upright and set the landed exception.
				GetPos() = m_feeler.GetPoint();
				DUMP_POSITION;

				// Set every orientation type thing I can think of so as to
				// pop his orientation to that of the ground.				
				GetMatrix()[Z] = GetVel();
				GetMatrix()[Z].ProjectToPlane(normal);	 // Mick: project forward vel to ground
				GetMatrix()[Z].Normalize();
				GetMatrix()[Y] = normal;				  		
				GetMatrix()[X] = Mth::CrossProduct(GetMatrix()[Y],GetMatrix()[Z]);
				GetMatrix()[X].Normalize();
				
				ResetLerpingMatrix();
				
				GetSkaterMatrixQueriesComponentFromObject(GetObject())->ResetLatestMatrix();
				
				m_last_display_normal = GetMatrix()[Y];
				m_display_normal = GetMatrix()[Y];
				m_current_normal = GetMatrix()[Y];
				
				// check for sticking to rail first; use the "override_air" to stick to rail)
				maybe_stick_to_rail(true);
				if (GetState() != RAIL)
				{
					SetState(GROUND);
					FLAGEXCEPTION(CRCD(0x532b16ef, "Landed"));
				}				
				return;
			}
			else
			{
				new_normal(Mth::Vector(0.0f, 1.0f, 0.0f));
				
				GetPos() += 18.0f * mWallNormal;
				DUMP_POSITION;
			
				SetState(AIR);
				GetObject()->BroadcastEvent(CRCD(0xd96f01f1, "SkaterOffEdge"));
				FLAGEXCEPTION(CRCD(0x3b1001b6, "GroundGone"));


			}	
		}
		else
		{
			mWallNormal = normal;			// update the normal to the new one...			
			new_normal(mWallNormal);
            ResetLerpingMatrix();
			GetVel().RotateToPlane(mWallNormal);		// make velocity go along it
			DUMP_VELOCITY;
		}
	}
	
	if (handle_upward_collision_in_wallride())
	{
		new_normal(Mth::Vector(0.0f, 1.0f, 0.0f));
		
		GetPos() += 18.0f * mWallNormal;
		DUMP_POSITION;

		SetState(AIR);
		GetObject()->BroadcastEvent(CRCD(0xd96f01f1, "SkaterOffEdge"));
		FLAGEXCEPTION(CRCD(0x3b1001b6, "GroundGone"));
	}

	// Downwards collision check (down in direction of skater's y)
	
	m_col_start = GetMatrix()[Y] * GetPhysicsFloat(CRCD(0xe4d79235, "Physics_Ground_Snap_Up"));
	m_col_start += GetPos();
	
	m_col_end = GetMatrix()[Y] * GetPhysicsFloat(CRCD(0x438ba168, "Wall_Ride_Down_Collision_Check_Length"));
	m_col_end += GetPos();
	
	if (get_member_feeler_collision())
	{
		mWallNormal=m_feeler.GetNormal();

 		// Snap position to wall.
		GetPos() = m_feeler.GetPoint();
		// but one inch out from it
		GetPos() += mWallNormal;
		DUMP_POSITION;
			
		// Rotate velocity to plane.
		GetVel().RotateToPlane(mWallNormal);
		DUMP_VELOCITY;
			
		// Snap skater's orientation to the plane.
		new_normal(mWallNormal);
		ResetLerpingMatrix();
		
		if (!m_col_flag_wallable)
		{
			GetVel() += mWallNormal * 100.0f;		// boost away from the wall
			DUMP_VELOCITY;
			
			new_normal(Mth::Vector(0.0f, 1.0f, 0.0f));
            ResetLerpingMatrix();
			SetState(AIR);
			
			GetObject()->BroadcastEvent(CRCD(0xd96f01f1, "SkaterOffEdge"));
			FLAGEXCEPTION(CRCD(0x3b1001b6, "GroundGone"));
			mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF_EDGE, m_last_ground_feeler);
			
			if (mp_physics_control_component->HaveBeenReset()) return;
		}
		else
		{
			// handle wallride transition from one object to the next
			if (m_feeler.GetSector() != m_last_ground_feeler.GetSector())
			{
				mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF_EDGE, m_last_ground_feeler);
				if (mp_physics_control_component->HaveBeenReset()) return;
				mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_ONTO, m_last_ground_feeler);
				if (mp_physics_control_component->HaveBeenReset()) return;
			}
			if (!mp_trick_component->GraffitiTrickStarted())
			{
				// clear the graffiti trick buffer if we're moving to a new world sector, but haven't started our trick yet...
				mp_trick_component->SetGraffitiTrickStarted(false);
			}

			set_last_ground_feeler(m_feeler);
		}
	}
	else
	{
		new_normal(Mth::Vector(0.0f, 1.0f, 0.0f));
		ResetLerpingMatrix();
		GetPos() += 18.0f * mWallNormal;
		DUMP_POSITION;
		SetState(AIR);
		GetObject()->BroadcastEvent(CRCD(0xd96f01f1, "SkaterOffEdge"));
		FLAGEXCEPTION(CRCD(0x3b1001b6, "GroundGone"));
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_SKATE_OFF_EDGE, m_last_ground_feeler);
		if (mp_physics_control_component->HaveBeenReset()) return;
	}
	
	// look for head collisions
	
	
	// This was cut-and-past'd from DoOnGroundPhysics
	handle_tensing();
	
	if (maybe_flag_ollie_exception())
	{
		mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, m_last_ground_feeler);
		if (mp_physics_control_component->HaveBeenReset()) return;
		
		GetVel() += GetPhysicsFloat(CRCD(0x349d02d4, "Wall_Ride_Jump_Out_Speed")) * mWallNormal;
		GetVel()[Y] += GetPhysicsFloat(CRCD(0x5a9de35a, "Wall_Ride_Jump_Up_Speed"));
		DUMP_VELOCITY;
		
		// Pop the skater upright again immediately.
		GetMatrix()[Y].Set(0.0f, 1.0f, 0.0);
		GetMatrix().OrthoNormalizeAbout(Y);
		ResetLerpingMatrix();

		// and immediately adjust the normal, to prevent single frame snaps when walliing and the 90 degree swoop when wallride to grind
		m_normal_lerp = 0.0f;
		m_display_normal = GetMatrix()[Y];
		m_current_normal  = m_display_normal;
		m_last_display_normal  = m_display_normal;
	}
	
	// if we've already started doing a trick, then start remembering our trick chain
	// GJ:  It's possible to do a wallride while mNumTricksInCombo == 0, for some reason
	mp_trick_component->TrickOffObject(m_last_ground_feeler.GetNodeChecksum());
			
	#ifdef __NOPT_ASSERT__
	if (Script::GetInteger(CRCD(0x3ae85eef, "skater_trails")))
	{
		Gfx::AddDebugLine(GetPos() + m_current_normal, GetOldPos() + m_current_normal, PURPLE, 0, 0);
	}
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::do_wallplant_physics (   )
{
	// check if the wallplant duration is up
	if (Tmr::ElapsedTime(m_state_change_timestamp) > Script::GetFloat(CRCD(0xa06e446b, "Physics_Wallplant_Duration")))
	{
		SetState(AIR);
		return;
	}

	// Wallplanting will cancel any memory of what rail we were on, so the next one seems fresh	
	mp_rail_node = NULL;
	m_last_rail_trigger_node_name = 0;

	
	// during a wallplant, our velocity is set to the exit velocity, but we simply ignore it
	
	// the only movement comes from our moving contact; we assume that this movement does not lead to collisions
	if (mp_movable_contact_component->UpdateContact(GetPos()))
	{
		GetPos() += mp_movable_contact_component->GetContact()->GetMovement();
		DUMP_POSITION;
		if (mp_movable_contact_component->GetContact()->IsRotated())
		{
			GetMatrix() *= mp_movable_contact_component->GetContact()->GetRotation();
			m_lerping_display_matrix *= mp_movable_contact_component->GetContact()->GetRotation();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::maybe_stick_to_rail ( bool override_air )
{
	// Only alow them to grind if we are in the air, that way we will avoid nasty problem with snapping through geometry onto rails 
	if (GetState() != AIR && !override_air) return;

	// K: Don't allow a grind trick if bailing, otherwise you can late-trick into a liptrick, bail, get snapped into a grind, & hence bail again.
	if (GetFlag(IS_BAILING)) return;
		
	// K: No grinds or lip tricks are allowed if this flag is set. This is switched on and off by the NoGrind and AllowGrind script commands.
	// These are used when jumping out of a lip trick to disallow going straight into a grind.
	if (mNoRailTricks) return;

	if (!mp_input_component->GetControlPad().m_triangle.GetPressed())
	{
		// Mick: not sure why this was removed
		// SetFlagTrue(CAN_RERAIL);
		return;
	}
	
	// don't grind for a short duration after a wallplant
	if (Tmr::ElapsedTime(m_last_wallplant_time_stamp) < static_cast< Tmr::Time >(Script::GetInteger(CRCD(0x96dca7dc,"Physics_Wallplant_Disallow_Grind_Duration")))) return;

	Mth::Vector a = GetOldPos();
	Mth::Vector b = GetPos();
	
	// if we were on a rail recently, and in the park editor, then don't let us snap to rails that are very perpendicular to us for a while
	float min_dot;
	if (Ed::CParkEditor::Instance()->UsingCustomPark() && Tmr::ElapsedTime(m_rail_time) < m_rerail_time)
	{
		// Should only do this if we've recently been on a rail
		min_dot = cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x76c1da15, "Rail_Corner_Leave_Angle"))));
	}
	else
	{
		min_dot = 1.0f;
	}

	// eventually we should get the rail manager base on whatever rail we are on
	CRailManager* p_rail_man = Mdl::Skate::Instance()->GetRailManager();
		
	// first check the world rail manager (rails that do not move)
	CRailNode* pNode;		
	Mth::Vector rail_pos;
	if (p_rail_man->StickToRail(a, b, &rail_pos, &pNode, NULL, min_dot))
	{
		TrackingLine(2,GetOldPos(), GetPos());	  // 2 = stick to rail
		
		mp_movable_contact_component->LoseAnyContact();
		if (will_take_rail(pNode, p_rail_man))
		{
			got_rail(rail_pos, pNode, p_rail_man);
		}
		return;
	}
	
	// iterate through all rail manager components, starting with the first one
	for (CRailManagerComponent* p_railmanager_component = static_cast< CRailManagerComponent* >(CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_RAILMANAGER));
		p_railmanager_component;
		p_railmanager_component = static_cast< CRailManagerComponent* >(p_railmanager_component->GetNextSameType()))
	{
		CRailManager* p_rail_man = p_railmanager_component->GetRailManager();
		
		Mth::Matrix	total_mat = p_railmanager_component->UpdateRailManager();
		Mth::Matrix	inv = total_mat;
		inv.Invert();

		a[W] = 1.0f;
		b[W] = 1.0f;

		// Transform a,b into the space of the object			
		Mth::Vector local_a = inv.Transform(a);
		Mth::Vector local_b = inv.Transform(b);

		if (p_rail_man->StickToRail(local_a, local_b, &rail_pos, &pNode, NULL, min_dot))
		{
			// transform from object space to world space
			rail_pos[W] = 1.0f;
			rail_pos = total_mat.Transform(rail_pos);
			if (will_take_rail(pNode, p_rail_man))
			{
				got_rail(rail_pos, pNode, p_rail_man); 
				mp_movable_contact_component->ObtainContact(p_railmanager_component->GetObject());
				
				GetVel() -= mp_movable_contact_component->GetContact()->GetObject()->GetVel();
			}
			return;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::will_take_rail ( const CRailNode* pNode, CRailManager* p_rail_man, bool from_walk )
{
	// no grinding if we're in an acid drop which was generated from an ollie out of a grind; prevents acid-grind-acid-grind repetition
	if (GetState() == AIR && GetFlag(IN_ACID_DROP) && GetFlag(OLLIED_FROM_RAIL)
		&& (mp_rail_node == pNode || mp_rail_node == pNode->GetNextLink() || mp_rail_node == pNode->GetPrevLink()))
	{
		return false;
	}
	
	// if it's a different rail, then allow rerailing
	if (!mp_rail_node																			// if there was no last rail
		
		// Dan: removed this line to prevent occasional rerailing when you grind off the end of a car; why was it here in the first place?
		// || (mp_rail_man && mp_rail_man->IsMoving() && !mp_movable_contact_component->HaveContact())	// or last rail was movable, and we've lost contact
		
		|| mp_rail_node != pNode																// or not same segment
		&& mp_rail_node != pNode->GetNextLink()  												// or the one before
		&& mp_rail_node != pNode->GetPrevLink()) 												// of the one after
	{
		if (mp_rail_node && Ed::CParkEditor::Instance()->UsingCustomPark())
		{
			Dbg_Assert(mp_rail_man);
			
			// in park editor rail must also not share end points to be considered different
			Mth::Vector a = mp_rail_man->GetPos(mp_rail_node) - mp_rail_man->GetPos(pNode);
			Mth::Vector b = mp_rail_man->GetPos(mp_rail_node) - mp_rail_man->GetPos(pNode->GetNextLink());
			Mth::Vector c = mp_rail_man->GetPos(mp_rail_node->GetNextLink()) - mp_rail_man->GetPos(pNode);
			Mth::Vector d = mp_rail_man->GetPos(mp_rail_node->GetNextLink()) - mp_rail_man->GetPos(pNode->GetNextLink());

			if (Mth::Abs(a.Length()) > 6.0f			
				&& Mth::Abs(b.Length()) > 6.0f			
				&& Mth::Abs(c.Length()) > 6.0f			
				&& Mth::Abs(d.Length()) > 6.0f)
			{
				SetFlagTrue(CAN_RERAIL);
			}
		}
		else
		{
			SetFlagTrue(CAN_RERAIL);
		}
	}
	
    return (GetFlag(CAN_RERAIL) || Tmr::ElapsedTime(m_rail_time) > m_rerail_time)
		&& (from_walk
			|| (GetState() != RAIL 									// not already on a rail
			&& (!GetFlag(TRACKING_VERT) || GetVel()[Y] > 0.0f)));		// must be not vert, or going up 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::got_rail ( const Mth::Vector& rail_pos, const CRailNode* pNode, CRailManager* p_rail_man, bool no_lip_tricks, bool from_walk )
{
	// got a point on rail, with start node number from a particular rail manager
	// use this info to start grinding or lipping on the rail
	
	CControlPad& control_pad = mp_input_component->GetControlPad();
	
	CSkaterFlipAndRotateComponent* p_flip_and_rotate_component = GetSkaterFlipAndRotateComponentFromObject(GetObject());
	Dbg_Assert(p_flip_and_rotate_component);
	p_flip_and_rotate_component->DoAnyFlipRotateOrBoardRotateAfters();


	// Mick - landed on a rail
	// if it's a "new" rail, then tell the robot detector about it
	if (mp_rail_node != pNode)
	{
		int rail_index = p_rail_man->GetNodeIndex(pNode);		
		mp_score_component->GetScore()->UpdateRobotDetection(rail_index);
	}

	mp_rail_node = pNode;					  
	mp_rail_man = p_rail_man;
	
	// handle single node rails
	if (!pNode->GetPrevLink() && !pNode->GetNextLink())
	{
		// for a single node rail, we apply in a single frame all the effects of entering and exiting the rail state;

		/////////////////////////////////////////////////////
		// Emulate entering the rail state (with horizontal dir direction)

		// check for collision in moving from m_pos to rail_pos
		
		m_col_start = GetPos();
		m_col_end = rail_pos;
		if (get_member_feeler_collision())
		{
			// check distance from the rail to the collision point
			if ((rail_pos - m_feeler.GetPoint()).LengthSqr() > 6.0f * 6.0f) 
			{
				return;
			}
		}
		
		// check first if we are not moving much in the XY and if not, then se the XZ velocity to the matrix[Z], so we always go forward
		if (Mth::Abs(GetVel()[X]) < 0.01f && Mth::Abs(GetVel()[Z]) < 0.01f)
		{
			GetVel()[X] = GetMatrix()[Z][X];
			GetVel()[Z] = GetMatrix()[Z][Z];
			DUMP_VELOCITY;
		}
		
		if (!from_walk && GetVel()[X] * GetVel()[X] + GetVel()[Z] * GetVel()[Z] > 10.0f * 10.0f)
		{
			GetVel().RotateToPlane(Mth::Vector(0.0f, 1.0f, 0.0f));
			DUMP_VELOCITY;
		}

		// rail direction is taken to always simply be along our horizontal velocity, rotated up
		Mth::Vector dir = GetVel();
		dir[Y] = 0.0f;
		dir.Normalize();
		float angle = Mth::DegToRad(Script::GetFloat(CRCD(0xbb357ecb,"Physics_Point_Rail_Kick_Upward_Angle")));
		float c = cosf(angle);
		float s = sinf(angle);
		Mth::Vector boost_dir(c * dir[X], s, c * dir[Z]);
		
		// get the rail node name
		Script::CArray* pNodeArray = mp_rail_man->GetNodeArray();
		Script::CStruct* pNode = pNodeArray->GetStructure(mp_rail_node->GetNode());
		pNode->GetChecksum(CRCD(0xa1dc81f9, "Name"), &m_last_rail_node_name, Script::ASSERT);
		
		mp_trick_component->TrickOffObject(m_last_rail_node_name);

		// Now we want to see if the rail has a trigger, and if it does, trigger it....
		
		uint32 trigger_script = 0;
		
		// no need to call maybe_trip_rail_trigger for a single node rail
		if (pNode->GetChecksum(CRCD(0x2ca8a299, "TriggerScript"), &trigger_script))
		{
			mp_trigger_component->TripTrigger(
				TRIGGER_LAND_ON,
				m_last_rail_node_name,
				pNodeArray == Script::GetArray(CRCD(0xc472ecc5, "NodeArray")) ? NULL : pNodeArray,
				mp_movable_contact_component->HaveContact() ? mp_movable_contact_component->GetContact()->GetObject() : NULL
			);
		}

		GetPos() = rail_pos;
		DUMP_POSITION;

		// Now we'v got onto the rail, we need to:
		// 1) kill velocity perpendicular to the rail
		// 2) add a speed boost in the direction we are going.

		SetFlagFalse(VERT_AIR);
		SetFlagFalse(TRACKING_VERT);

		// if we are transitioning from wall to rail, then snap him upright		
		if (GetState() == WALL)
		{
			new_normal(Mth::Vector(0.0f, 1.0f, 0.0f));
			ResetLerpingMatrix();
		}

		SetState(RAIL);

		set_terrain(mp_rail_node->GetTerrain());
		mp_sound_component->PlayLandSound(GetObject()->GetVel().Length() / GetSkater()->GetScriptedStat(CRCD(0xcc5f87aa, "Skater_Max_Max_Speed_Stat")), mp_state_component->m_terrain);
		
		float old_y = GetVel()[Y];
		GetVel().ProjectToNormal(dir);	   							// kill perp velocity
		if (from_walk && Mth::DotProduct(GetVel(), GetMatrix()[Z]) < 0.0f)
		{
			if (GetVel().LengthSqr() < Mth::Sqr(1.1f * mp_walk_component->s_get_param(CRCD(0x896c8888, "jump_adjust_speed"))))
			{
				GetVel().Set();
				dir = GetMatrix()[Z];
				dir[Y] = 0.0f;
				dir.Normalize();
			}

		}
		GetVel()[Y] = old_y;											// except for Y

		GetVel() += dir * GetPhysicsFloat(CRCD(0xa3ef4833, "Point_Rail_Speed_Boost"));	// add speed boost			
		DUMP_VELOCITY;


		// (Mick) Set m_rail_time, otherwise there is a single frame where it is invalid
		// and this allows us to immediately re-rail and hence do the "insta-bail", since the triangle button will be held down   
		m_rail_time  = Tmr::GetTime();

		/////////////////////////////////////////////////////
		// Emulate effects of rail state (with boost_dir direction)

		SetFlagFalse(SPINE_PHYSICS);
		SetFlagFalse(IN_ACID_DROP);
		SetFlagFalse(IN_RECOVERY);
		SetFlagFalse(AIR_ACID_DROP_DISALLOWED);

		// set default rerail time	
		m_rerail_time = static_cast< Tmr::Time >(GetPhysicsFloat(CRCD(0x2b4645ad, "Rail_Minimum_Rerail_Time")));
		// and dissalow any overriding of this
		SetFlagFalse(CAN_RERAIL);		// dont allow rerailing after coming off a segment
		
		GetVel().RotateToNormal(boost_dir);
		DUMP_VELOCITY;

		/////////////////////////////////////////////////////
		// Emulate exiting the rail state

		// no need to call maybe_trip_rail_trigger for a single node rail
		if (trigger_script)
		{
			mp_trigger_component->TripTrigger(
				TRIGGER_SKATE_OFF,
				m_last_rail_node_name,
				pNodeArray == Script::GetArray(CRCD(0xc472ecc5, "NodeArray")) ? NULL : pNodeArray,
				mp_movable_contact_component->HaveContact() ? mp_movable_contact_component->GetContact()->GetObject() : NULL
			);
		}
			
		SetState(AIR);
		GetPos()[Y] += 1.0f;
		DUMP_POSITION;

		/////////////////////////////////////////////////////
		// Do extra point rail logic

		// trigger the appropriate script
		GetObject()->SelfEvent(CRCD(0xb8048f1d, "PointRail"));
		
		return;
	}
	
	///////////////////////////////////////////////////////////////////////////
	// 						Check for lip trick
	///////////////////////////////////////////////////////////////////////////
	
	float LipAllowSine = sinf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x8157c5d9, "LipAllowAngle"))));
	if (pNode->GetFlag(LIP_OVERRIDE))
	{
		LipAllowSine = sinf(Mth::DegToRad(GetPhysicsFloat(CRCD(0xf3b6f95e, "LipAllowAngle_Override"))));
	}
	float HorizSine = sinf(Mth::DegToRad(GetPhysicsFloat(CRCD(0xe2a85fbb, "LipPlayerHorizontalAngle"))));
	float VertCos = cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x61079462, "LipRampVertAngle"))));

	if ((mAllowLipNoGrind											// This flag makes lip tricks always happen, hooray!
		|| (
			GetVel()[Y] > 0.0f											// going upwards
			&& Mth::Abs(GetMatrix()[Y][Y]) < HorizSine		   			// fairly horizontal skater
			&& GetMatrix()[Z][Y] > 0.0f									// Facing fairly up in the air
			&& Mth::Abs(m_current_normal[Y]) < VertCos					// last poly we were on was near vert
			&& Mth::Abs(GetMatrix()[X][Y]) < LipAllowSine		 			// skater pointing fairly straight up
		)) && !no_lip_tricks)
	{
		// Reset a bunch of stuff.
		GetVel().Set(0.0f, 0.0f, 0.0f);
		DUMP_VELOCITY;
		SetFlagFalse(VERT_AIR);
		SetFlagFalse(TRACKING_VERT);
		SetFlagFalse(LAST_POLY_WAS_VERT);
		SetFlagFalse(CAN_BREAK_VERT);
		// SetFlagFalse(LEAN);
		
		// Make sure any other balance trick is stopped.
		mp_balance_trick_component->stop_balance_trick();
		
		m_pre_lip_pos = GetPos();
		
		// Into the lip state
		SetState(LIP);
		
		// Snap the position
		GetPos() = rail_pos;
		DUMP_POSITION;
	
		// Pop the skater horizontal
		Mth::Vector u = m_current_normal;
		u[Y] = 0.0f;
		u.Normalize();
		GetMatrix()[Y] = u;		
		
		GetMatrix()[Z].Set(0.0f, 1.0f, 0.0f);
		
		#if 0 // old code
		// GetMatrix()[X] = Mth::CrossProduct(m_matrix[Y],m_matrix[Z]);
		// GetMatrix()[X].Normalize();
		#else
		GetMatrix()[X].Set(
			-GetMatrix()[Y][Z],
			0.0f,
			GetMatrix()[Y][X],
			0.0f
		);
		#endif

		ResetLerpingMatrix();
		
		// Update the "Require_Lip" flag so lip only gaps don't need to wait on frame
		GetSkaterGapComponentFromObject(GetObject())->UpdateCancelRequire(0,REQUIRE_LIP);
		
		
		// Trigger the lip on event.
		maybe_trip_rail_trigger(TRIGGER_LIP_ON);

		mp_trick_component->mUseSpecialTrickText = false;
		
		if (!GetObject()->GetScript())
		{
			GetObject()->SetScript(new Script::CScript);
		}

		// Run the lip script.
		GetObject()->GetScript()->SetScript(CRCD(0x1647cf96, "LipTrick"), NULL, GetObject());
		GetObject()->GetScript()->Update();
	
		// Set the mDoingTrick flag so that the camera can detect that a trick is being done.
		mp_state_component->SetDoingTrick(true);

		set_terrain(mp_rail_node->GetTerrain());
	
		return;
	}
	
	// no lip trick, so we rail
	
	set_terrain(mp_rail_node->GetTerrain());
			  
	const CRailNode* pStart = mp_rail_node;
	const CRailNode* pEnd = pStart->GetNextLink();

	Mth::Vector	dir = mp_rail_man->GetPos(pEnd) - mp_rail_man->GetPos(pStart);
	dir.Normalize();
	
	// Now, we've get a rail
	
	// check for collision in moving from m_pos to rail_pos
	
	m_col_start = GetPos();
	m_col_end = rail_pos;
	if (get_member_feeler_collision())
	{
		// check distance from the rail to the collision point
		if ((rail_pos - m_feeler.GetPoint()).LengthSqr() > 6.0f * 6.0f) 
		{
			return;
		}
	}
	
	if (GetFlag(IN_ACID_DROP))
	{
		MESSAGE("GRINDING FROM ACID DROP");
		DUMPV(acid_hold);
		DUMP_VELOCITY;
		GetVel() += acid_hold;
		DUMP_VELOCITY;
	}

	// we need to check if this is the end of the rail and we are going to come off it next frame, then we don't want to get on it....

	// check first if we are not moving much in the XY and if not, then se the XZ velocity to the matrix[Z], so we always go forward
	if (GetVel()[X] == 0.0f && GetVel()[Z] == 0.0f)
	{
		GetVel()[X] = GetMatrix()[Z][X];
		GetVel()[Z] = GetMatrix()[Z][Z];
	}
	
	//////////////////////////////////////////////////////////////////			
	// Mick: Start of patch
	// (For Oil Rig type problem with steep rails)
	// if we are moving forward, then rotate velocity onto a horizontal plane
	// so we don't seem to be going backwards up steep rails when we jump onto them
	
	#if 0 // old code
	// Mth::Vector flat_vel = GetVel();
	// flat_vel[Y] = 0;
	// if (flat_vel.Length() > 10.0f)
	// {
		// GetVel().RotateToPlane(Mth::Vector(0.0f,1.0f,0.0f));
	//}
	#else
	if (!from_walk && GetVel()[X] * GetVel()[X] + GetVel()[Z] * GetVel()[Z] > 10.0f * 10.0f)
	{
		GetVel().RotateToPlane(Mth::Vector(0.0f, 1.0f, 0.0f));
		DUMP_VELOCITY;
	}
	#endif
	
	//
	// Mick: end of patch
	/////////////////////////////////////////////////////////////////////

	float dot = Mth::DotProduct(dir, GetVel());
	
	// sign is which way we are going along the rail
	float sign;
	if (dot == 0.0f)
	{
		// if the dot product can not determine the direction (pull up from hangs), choose randomly
		sign = Mth::Rnd(2) ? 1.0f : -1.0f;
	}
	else
	{
	   sign = Mth::Sgn(dot);
	}
	
	if (sign < 0.0f)
	{
		// will be going backwards along the rail
		
		// if the rail stick point is this last point, then we don't want to snap to it as we will immediatly fly off,
		// and the point might be coincident with a wall or the ground and that will cause problems
		if (!pStart->GetPrevLink() && mp_rail_man->GetPos(pStart) == rail_pos) return;
	}
	else
	{
		// same for going forward along the rail
		if (!pEnd->GetNextLink() && mp_rail_man->GetPos(pEnd) == rail_pos) return;
	}
	
	// Now we want to see if the rail has a trigger, and if it does, trigger it....
	maybe_trip_rail_trigger(TRIGGER_LAND_ON);
	
	GetPos() = rail_pos;
	DUMP_POSITION;
	
	// Now we'v got onto the rail, we need to:
	// 1) kill velocity perpendicular to the rail
	// 2) add a speed boost in the direction we are going.
	
	SetFlagFalse(VERT_AIR);
	SetFlagFalse(TRACKING_VERT);

	// if we are transitioning from wall to rail, then snap him upright		
	if (GetState() == WALL)
	{
		new_normal(Mth::Vector(0.0f, 1.0f, 0.0f));
		ResetLerpingMatrix();
	}
	
	SetState(RAIL);
	
	// don't play the rail sound when coming onto a rail from walking
	if (Tmr::ElapsedTime(mp_physics_control_component->GetStateSwitchTime()) != 0)
	{
		// play sound based on pre-rail velocity
		mp_sound_component->PlayLandSound(GetObject()->GetVel().Length() / GetSkater()->GetScriptedStat(CRCD(0xcc5f87aa, "Skater_Max_Max_Speed_Stat")), mp_state_component->m_terrain);
	}

	float old_y = GetVel()[Y];
	GetVel().ProjectToNormal(dir);	   							// kill perp velocity
	if (from_walk && Mth::DotProduct(GetVel(), GetMatrix()[Z]) < 0.0f)
	{
		if (GetVel().LengthSqr() < Mth::Sqr(1.1f * mp_walk_component->s_get_param(CRCD(0x896c8888, "jump_adjust_speed"))))
		{
			GetVel().Set();
			sign = Mth::Sgn(Mth::DotProduct(dir, GetMatrix()[Z]));
		}
		
	}
	GetVel()[Y] = old_y;											// except for Y
	
	GetVel() += dir * sign * GetPhysicsFloat(CRCD(0x457bb395, "Rail_Speed_Boost"));	// add speed boost			
	DUMP_VELOCITY;
	
	// Ken stuff ...
	bool Rail_RightOfRail = false;
	bool Rail_Parallel = false;
	mRail_Backwards = false;

	if (sign < 0.0f)
	{
		dir = -dir;
	}
	// dir is now the unit vector pointing along the rail in the direction we will be moving on the rail
		
	// Calculate which side of the rail we're on
	#if 0 // old code
	// Mth::Vector d = rail_pos - m_last_ground_pos;
	// Mth::Vector side_vel(d.GetZ(), 0.0f, -d.GetX());
	// if (Mth::DotProduct(dir, side_vel) < 0.0f)
	// {
		// Rail_RightOfRail = true;
	// }	
	#else
	float side_vel_X = rail_pos[Z] - m_last_ground_pos[Z];
	float side_vel_Z = -(rail_pos[X] - m_last_ground_pos[X]);
	if (dir[X] * side_vel_X + dir[Z] * side_vel_Z < 0.0f)
	{
		Rail_RightOfRail = true;
	}
	#endif
	
	///////////////////////////////////////////////////////////////////////////////////
	// 							   Bad ledge detection
	///////////////////////////////////////////////////////////////////////////////////
	
	// Calculate the up and down offsets for collision test.
	m_col_start.Set(0.0f, GetPhysicsFloat(CRCD(0xe4d79235, "Physics_Ground_Snap_Up")), 0.0f, 0.0f);
	m_col_end.Set(0.0f, -GetPhysicsFloat(CRCD(0x9cd7ed5c, "Rail_Bad_Ledge_Drop_Down_Dist")), 0.0f, 0.0f);
	
	// Add the rail pos, so start and end are now above and below the rail.
	m_col_start += rail_pos;
	m_col_end += rail_pos;

	// Calculate a side offset, using the rail direction rotated 90 degrees.
	Mth::Vector Off(dir[Z], 0.0f, -dir[X], 0.0f);
	Off *= GetPhysicsFloat(CRCD(0x31669752, "Rail_Bad_Ledge_Side_Dist"));

	// Add the offset and do the left collision.
	m_col_start += Off;
	m_col_end += Off;
	bool LeftCollision = get_member_feeler_collision();
	
	// Move across to the other side and do the right collision.
	m_col_start -= 2.0f * Off;
	m_col_end -= 2.0f * Off;
	bool RightCollision = get_member_feeler_collision();
	
	// Bit of logic to get whether it's a bad ledge or not.
	mBadLedge = (LeftCollision && !Rail_RightOfRail) || (RightCollision && Rail_RightOfRail);
	mLedge = LeftCollision || RightCollision;
	
	///////////////////////////////////////////////////////////////////////////////////
	
	float RightDot = Mth::DotProduct(dir, GetMatrix()[X]);
	float FrontDot = Mth::DotProduct(dir, GetMatrix()[Z]);
	
	if (Mth::Abs(RightDot) < GetPhysicsFloat(CRCD(0xd0688d4e, "Rail_Tolerance")))
	{
		// The skater's right vector is close to perpendicular to the rail, so the skater must be pointing fairly parallel to it.
		Rail_Parallel = true;
		
		// Use the front vector to determine forwards/backwards, since the front vector is fairly parallel to the rail.
		if (FrontDot < 0.0f)
		{
			mRail_Backwards = true;
		}
	}
	else
	{
		if (GetFlag(FLIPPED))
		{
			RightDot = -RightDot;
		}
			
		// The skater's right vector is fairly parallel to the rail, so use it to determine forwards/backwards.
		if (RightDot < 0.0f)
		{
			mRail_Backwards = true;
		}
	}

	mp_trick_component->mUseSpecialTrickText = false;

	if (!mp_trick_component->TriggerAnyExtraGrindTrick(
			Rail_RightOfRail,
			Rail_Parallel,
			mRail_Backwards,
			GetFlag(FLIPPED))
		)
	{
		do_grind_trick(CSkaterPad::sGetDirection(
			control_pad.m_up.GetPressed(),
			control_pad.m_down.GetPressed(),
			control_pad.m_left.GetPressed(),
			control_pad.m_right.GetPressed()
		), Rail_RightOfRail, Rail_Parallel, mRail_Backwards, GetFlag(FLIPPED));
	}	
	
	// (Mick) Set m_rail_time, otherwise there is a single frame where it is invalid
	// and this allows us to immediately re-rail and hence do the "insta-bail", since the triangle button will be held down   
	m_rail_time  = Tmr::GetTime();
}		

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::do_lip_physics (   )
{
	SetFlagFalse(SPINE_PHYSICS);
	SetFlagFalse(IN_ACID_DROP);
	SetFlagFalse(IN_RECOVERY);
	
	if (mp_movable_contact_component->UpdateContact(GetPos()))
	{
		GetPos() += mp_movable_contact_component->GetContact()->GetMovement();
		DUMP_POSITION;
		
		// this is close to correct, 'cept that but because of rotation, it's not quite right
		m_pre_lip_pos += mp_movable_contact_component->GetContact()->GetMovement();
		
		if (mp_movable_contact_component->GetContact()->IsRotated())
		{

			GetMatrix() *= mp_movable_contact_component->GetContact()->GetRotation();
			m_lerping_display_matrix *= mp_movable_contact_component->GetContact()->GetRotation();
		}
	}
	
	#ifdef __USER_DAN__
	if (Script::GetInteger(CRCD(0x1a5eab7, "rail_highlights")))
	{
		Gfx::AddDebugLine(mp_rail_man->GetPos(mp_rail_node), mp_rail_man->GetPos(mp_rail_node->GetNextLink()), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
		Gfx::AddDebugLine(mp_rail_man->GetPos(mp_rail_node) + Mth::Vector(1.0f, 0.0f, 0.0f), mp_rail_man->GetPos(mp_rail_node->GetNextLink()) + Mth::Vector(1.0f, 0.0f, 0.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
		Gfx::AddDebugLine(mp_rail_man->GetPos(mp_rail_node) + Mth::Vector(0.0f, 1.0f, 0.0f), mp_rail_man->GetPos(mp_rail_node->GetNextLink()) + Mth::Vector(0.0f, 1.0f, 0.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
		Gfx::AddDebugLine(mp_rail_man->GetPos(mp_rail_node) + Mth::Vector(0.0f, 0.0f, 1.0f), mp_rail_man->GetPos(mp_rail_node->GetNextLink()) + Mth::Vector(0.0f, 0.0f, 1.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
	}
	#endif
	
	// Update any balance control required.
	if (mp_balance_trick_component->mBalanceTrickType == CRCD(0xa549b57b, "Lip"))
	{
		if (Mdl::Skate::Instance()->GetCareer()->GetCheat(CRCD(0xcd09e062, "CHEAT_PERFECT_RAIL")))
		{
			// Here, set the flag. It may seem redundant, but the above line is very likely
			// to be hacked by gameshark. They probably won't notice this one, which will
			// set the flags as if they had actually enabled the cheat -- which enables us
			// to detect that it has been turned on more easily.
			Mdl::Skate::Instance()->GetCareer()->SetGlobalFlag( Script::GetInteger(CRCD(0xcd09e062, "CHEAT_PERFECT_RAIL")));
			mp_balance_trick_component->mLip.mManualLean = 0.0f;
			mp_balance_trick_component->mLip.mManualLeanDir = 0.0f;
			g_CheatsEnabled = true;
		}
		mp_balance_trick_component->mLip.DoManualPhysics();
	}
	
	handle_tensing();
	if (maybe_flag_ollie_exception())	
	{
		// Trigger the lip jump event.
		maybe_trip_rail_trigger(TRIGGER_LIP_JUMP);
	}

	// lip tricks are very rail-like when determining which world sector you've just tricked off of
	if (mp_rail_node)
	{
		if (mp_rail_man->GetNodeArray())
		{
			const CRailNode* pRail = mp_rail_node;
			Script::CArray* pNodeArray = Script::GetArray(CRCD(0xc472ecc5, "NodeArray"));
			Script::CStruct* pNode = pNodeArray->GetStructure(pRail->GetNode());
			pNode->GetChecksum(CRCD(0xa1dc81f9, "name"), &m_last_rail_node_name, Script::ASSERT);
			mp_trick_component->TrickOffObject(m_last_rail_node_name);
		}
	}
	
	m_lip_time = Tmr::GetTime();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::do_rail_physics (   )
{
	SetFlagFalse(SPINE_PHYSICS);
	SetFlagFalse(IN_ACID_DROP);
	SetFlagFalse(IN_RECOVERY);
	SetFlagFalse(AIR_ACID_DROP_DISALLOWED);

	// First of all we apply the movement due to contact; best to do it first, as then we will be on the rail we are moving along

	if (mp_movable_contact_component->HaveContact())
	{
		// need to update the transform of the rail manager
		// no need to do this														
		CRailManagerComponent* p_rail_manager_component = GetRailManagerComponentFromObject(mp_movable_contact_component->GetContact()->GetObject());
		Dbg_Assert(p_rail_manager_component);
		
		p_rail_manager_component->UpdateRailManager();
	
		if (mp_movable_contact_component->UpdateContact(GetPos()))
		{
			
			GetPos() += mp_movable_contact_component->GetContact()->GetMovement();
			DUMP_POSITION;
			if (mp_movable_contact_component->GetContact()->IsRotated())
			{
				GetMatrix() *= mp_movable_contact_component->GetContact()->GetRotation();
				m_lerping_display_matrix *= mp_movable_contact_component->GetContact()->GetRotation();
			}
				
		}
	}

	// set default rerail time	
	m_rerail_time = static_cast< Tmr::Time >(GetPhysicsFloat(CRCD(0x2b4645ad, "Rail_minimum_rerail_time")));
	// and dissalow any overriding of this
	SetFlagFalse(CAN_RERAIL);		// dont allow rerailing after coming off a segment

	
	if (maybe_flag_ollie_exception())
	{
		maybe_trip_rail_trigger(TRIGGER_JUMP_OFF);
		
		m_rerail_time = static_cast< Tmr::Time >(GetPhysicsFloat(CRCD(0xbf35053, "Rail_jump_rerail_time")));
		
		SetFlagTrue(OLLIED_FROM_RAIL);
		
		// when we jump off a rail, then raise him up one inch, so we don't collide with the top of a fence or something
		GetPos()[Y] += 1.0f;			// up one inch......
		DUMP_POSITION;
	}
	else
	{
		switch (mp_balance_trick_component->mBalanceTrickType)
		{
			case 0:
			case 0x0ac90769: // NoseManual	
			case 0xef24413b: // Manual
			case 0xa549b57b: // Lip	
				break;
				
			case 0x255ed86f: // Grind
			case 0x8d10119d: // Slide
				mp_balance_trick_component->mGrind.DoManualPhysics();
				if (Mdl::Skate::Instance()->GetCareer()->GetCheat(CRCD(0xcd09e062, "CHEAT_PERFECT_RAIL")))
				{
					// Here, set the flag. It may seem redundant, but the above line is very likely
					// to be hacked by gameshark. They probably won't notice this one, which will
					// set the flags as if they had actually enabled the cheat -- which enables us
					// to detect that it has been turned on more easily.
					Mdl::Skate::Instance()->GetCareer()->SetGlobalFlag( Script::GetInteger(CRCD(0xcd09e062, "CHEAT_PERFECT_RAIL")));
					mp_balance_trick_component->mGrind.mManualLean = 0.0f;
					mp_balance_trick_component->mGrind.mManualLeanDir = 0.0f;
					g_CheatsEnabled = true;
				}
				break;	
						
			default:	
				Dbg_Assert(false);
				break;
		}		
	
		// Get the rail segments

		const CRailNode* pStart = mp_rail_node;
		const CRailNode* pEnd = pStart->GetNextLink();
		
		const CRailNode* pFrom = pStart;
		const CRailNode* pOnto = NULL;
		
		#ifdef __USER_DAN__
		if (Script::GetInteger(CRCD(0x1a5eab7, "rail_highlights")))
		{
			Gfx::AddDebugLine(mp_rail_man->GetPos(pStart), mp_rail_man->GetPos(pEnd), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
			Gfx::AddDebugLine(mp_rail_man->GetPos(pStart) + Mth::Vector(1.0f, 0.0f, 0.0f), mp_rail_man->GetPos(pEnd) + Mth::Vector(1.0f, 0.0f, 0.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
			Gfx::AddDebugLine(mp_rail_man->GetPos(pStart) + Mth::Vector(0.0f, 1.0f, 0.0f), mp_rail_man->GetPos(pEnd) + Mth::Vector(0.0f, 1.0f, 0.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
			Gfx::AddDebugLine(mp_rail_man->GetPos(pStart) + Mth::Vector(0.0f, 0.0f, 1.0f), mp_rail_man->GetPos(pEnd) + Mth::Vector(0.0f, 0.0f, 1.0f), MAKE_RGB(Mth::Rnd(256), Mth::Rnd(256), Mth::Rnd(256)), 0, 1);
		}
		#endif
		
		Mth::Vector	dir = mp_rail_man->GetPos(pEnd) - mp_rail_man->GetPos(pStart);
		float segment_length = dir.Length();
		dir *= 1.0f / segment_length;

		// sign is which way we are going along the rail
		float old_sign = Mth::Sgn(Mth::DotProduct(dir, GetVel()));

		// Get gravity force 	
		Mth::Vector gravity(0.0f, GetPhysicsFloat(CRCD(0xd1f46992, "Physics_Rail_Gravity")), 0.0f);

		// Project gravity onto the line we are on
		gravity.ProjectToNormal(dir);
		GetVel() += gravity * m_frame_length;
		DUMP_VELOCITY;
		
		// sign is which way we are going along the rail
		float sign = Mth::Sgn(Mth::DotProduct(dir, GetVel()));
		
		// sign might have changed here
		// so could do the "flipping on a rail" thing
		// .................
		
		if (sign != old_sign)
		{
			// Note, we JUST flip the "Backwards" flag, as we want to stay in essentailly the same
			// pose as before, and as the sign of our velocity dotted with the rail has
			// changed, then all we need to to is tell the code we are
			// going in the opposite direction to what we were going before, and the
			// target vector will be calculated correctly.
			mRail_Backwards = !mRail_Backwards;			 		
		}
		
		// check to see if we are on the last segment of the rail
		// this logic could be folded into the logic below but it's perhps clearer to have it here
		bool last_segment = false;
		if (sign < 0.0f)
		{
			if (pStart->GetPrevLink() && pStart->GetPrevLink()->IsActive())
			{
				Mth::Vector v1, v2;
				v1 = mp_rail_man->GetPos(pEnd) - mp_rail_man->GetPos(pStart);
				v2 = mp_rail_man->GetPos(pStart) - mp_rail_man->GetPos(pStart->GetPrevLink());
				v1[Y] = 0.0f;
				v2[Y] = 0.0f;
				v1.Normalize();
				v2.Normalize();
				float dot = Mth::DotProduct(v1, v2);
				if (dot < cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x76c1da15, "Rail_Corner_Leave_Angle")))))
				{
					// there is more, but angle is too sharp
					last_segment = true;
				}
				else
				{
					pOnto = pStart->GetPrevLink();
				}
			}
			else
			{
				last_segment = true;			
			}
		}
		else
		{
			if (pEnd && pEnd->GetNextLink() && pEnd->GetNextLink()->IsActive())
			{
				Mth::Vector  v1,v2;
				v1 = mp_rail_man->GetPos(pStart) - mp_rail_man->GetPos(pEnd);
				v2 = mp_rail_man->GetPos(pEnd) - mp_rail_man->GetPos(pEnd->GetNextLink());
				v1[Y] = 0.0f;
				v2[Y] = 0.0f;
				v1.Normalize();
				v2.Normalize();
				float dot = Mth::DotProduct(v1,v2);
				if (dot < cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x76c1da15,"Rail_Corner_Leave_Angle")))))
				{
					// there is more, but angle is too sharp
					last_segment = true;
				}
				else
				{
					pOnto = pEnd;
				}
			}
			else
			{
				last_segment = true;
			}
		}

		// Now we need to see if we have gone beyond the end of the rail segment
		
		float extra_dist = 0.0f;
			
		float length_along;
		if (sign < 0.0f)
		{
			// going backwards, so it's the distance from the end of the segment
			length_along = (GetPos() - mp_rail_man->GetPos(pEnd)).Length();
		}
		else
		{
			// going forwards, so it's the distance from the start
			length_along = (GetPos() - mp_rail_man->GetPos(pStart)).Length();
		}
		
		if (length_along > segment_length + 0.1f)	// 0.1 inch, so we don't get stuck
		{
			// remember this, so we can move along next segment
			extra_dist = length_along - segment_length;
			
			// gone off the end of the segment
			if (sign < 0.0f)
			{
				if (pStart->GetPrevLink()
					&& pStart->GetPrevLink()->IsActive()
					&& Rail_ValidInEditor(mp_rail_man->GetPos(pStart), mp_rail_man->GetPos(pStart->GetPrevLink())))
				{
					if (!last_segment)
					{
						// go onto previous segment
						GetPos() = mp_rail_man->GetPos(pStart);
						DUMP_POSITION;
						mp_rail_node = pStart->GetPrevLink();
						set_terrain(mp_rail_node->GetTerrain());
						maybe_trip_rail_trigger(TRIGGER_SKATE_ONTO);
					}		   
					else
					{
						skate_off_rail(mp_rail_man->GetPos(pStart));
					} 		
				}
				else
				{
					skate_off_rail(mp_rail_man->GetPos(pStart));
				}
			}
			else
			{
				if (pEnd->GetNextLink()
					&& pEnd->IsActive()
					&& Rail_ValidInEditor(mp_rail_man->GetPos(pEnd), mp_rail_man->GetPos(pEnd->GetNextLink())))
				{
					if (!last_segment)
					{
						GetPos() = mp_rail_man->GetPos(pEnd);
						DUMP_POSITION;
						mp_rail_node = pEnd;					
						set_terrain(mp_rail_node->GetTerrain());
						maybe_trip_rail_trigger(TRIGGER_SKATE_ONTO);						
					}		   
					else
					{
						skate_off_rail(mp_rail_man->GetPos(pEnd));
					} 						
				}
				else
				{
					skate_off_rail(mp_rail_man->GetPos(pEnd));
				}
			}		
		}
		
		if (GetState() == RAIL)
		{
			// recalculate start, end, dir, as we might be on a new segment
			const CRailNode* pStart = mp_rail_node;
			const CRailNode* pEnd = pStart->GetNextLink();
			
			Mth::Vector	dir = mp_rail_man->GetPos(pEnd) - mp_rail_man->GetPos(pStart);
			dir.Normalize();

		    // sign also may have changed, now that we are auto-linking rail segments
			
			// sign is which way we are going along the rail
			float sign = Mth::Sgn(Mth::DotProduct(dir,GetVel()));

			m_rail_time = Tmr::GetTime();
								
			GetVel().RotateToNormal(dir);
			GetVel() *= sign;						   						// sign won't be on a new segment
			DUMP_VELOCITY;

			float facing_sign = mRail_Backwards ? -sign : sign;
			
			// z is forward
			Mth::Vector target_forward = dir * facing_sign;

			m_lerping_display_matrix[Z] = Mth::Lerp(m_lerping_display_matrix[Z], target_forward, 0.3f);
			m_lerping_display_matrix[Z].Normalize(); 
			
			#if 0 // old code
			// m_lerping_display_matrix[Y].Set(0.0f, 1.0f, 0.0f);
			// m_lerping_display_matrix[X] = Mth::CrossProduct(
				// m_lerping_display_matrix[Y],
				// m_lerping_display_matrix[Z]
			// );
			#else
			m_lerping_display_matrix[X].Set(
				m_lerping_display_matrix[Z][Z],
				0.0f,
				-m_lerping_display_matrix[Z][X],
				0.0f
			);
			#endif
			m_lerping_display_matrix[X].Normalize();
			
			m_lerping_display_matrix[Y] = Mth::CrossProduct(
				m_lerping_display_matrix[Z],
				m_lerping_display_matrix[X]
			);
			m_lerping_display_matrix[Y].Normalize();

			// adjust our Z value towards the new value												 
			GetMatrix()[Z] = target_forward;
			GetMatrix()[Z].Normalize(); 
			
			#if 0 // old code
			// GetMatrix()[Y].Set(0.0f, 1.0f, 0.0f);
			// GetMatrix()[X] = Mth::CrossProduct(GetMatrix()[Y],GetMatrix()[Z]);
			#else
			GetMatrix()[X].Set(
				GetMatrix()[Z][Z],
				0.0f,
				-GetMatrix()[Z][X],
				0.0f
			);
			#endif
			GetMatrix()[X].Normalize();
			
			GetMatrix()[Y] = Mth::CrossProduct(GetMatrix()[Z], GetMatrix()[X]);
			GetMatrix()[Y].Normalize();

			Mth::Vector m_movement(0.0f, 0.0f, 0.0f);
			
			// This is where we do the actual movement
			
			// if this makes us bump into the wall or the ground, then we should leave the rail
			
			Mth::Vector old_pos = GetPos();  	
			GetPos() += GetVel() * m_frame_length;			// current movement
			GetPos() += extra_dist * target_forward;						// any extra dist from previous segment
			GetPos() += m_movement;							// movement due to contact with moving object
			DUMP_POSITION;
			
			// Mick:  use "old_pos" to generate the direction
			// so it is guarenteed to be parallel to the rail
			// and m_pos might have been adjusted if we continued from
			// one rail to another (in-line) rail, like in the park editor
			Mth::Vector movement = GetPos() - old_pos;
			Mth::Vector direction = movement;
			direction.Normalize();
			
			bool always_check = false;
			if (!last_segment && Ed::CParkEditor::Instance()->UsingCustomPark())
			{
				// in the park editor we can have tight curves that end in walls, so we want to always do the forward check
				// if we are on a segment that is horizontal, and leads to another segment that is also horizontal
				Mth::Vector from = mp_rail_man->GetPos(pFrom->GetNextLink()) - mp_rail_man->GetPos(pFrom);				
				Mth::Vector onto = mp_rail_man->GetPos(pOnto->GetNextLink()) - mp_rail_man->GetPos(pOnto);		
				from.Normalize();
				onto.Normalize();
				float delta = Mth::Abs(from[Y] - onto[Y]);		
				if (delta < 0.01f)
				{
					// lines have a sufficently close Y angle
					always_check = true;
				}
			}
			
			// Only check for hitting a wall if we are on a segment of rail that has no more rail
			if (last_segment || always_check)
			{
				m_col_start = old_pos;
				m_col_end = GetPos();
				m_col_end += movement + (direction * 6.0f);
				
				// raise them up one inch, so we don't collide with the rail
				m_col_start += GetMatrix()[Y];			 
				m_col_end += GetMatrix()[Y];  
				if (get_member_feeler_collision())
				{
					// if in the park editor, then ignore collision with invisible surfaces 
					if (!Ed::CParkEditor::Instance()->UsingCustomPark() || !(m_feeler.GetFlags() & mFD_INVISIBLE))
					{
						maybe_trip_rail_trigger(TRIGGER_SKATE_OFF);
						// don't let him make this movement!!
						GetPos() = GetOldPos();
						GetPos()[Y] += 1.0f;
						DUMP_POSITION;
						
						// project velocity along the plane if we run into a wall
						GetVel().ProjectToPlane(m_feeler.GetNormal());
						DUMP_VELOCITY;
						
						SetState(AIR);			// knocked off rail, as something in front
						FLAGEXCEPTION(CRCD(0xafaa46ba, "OffRail"));
					}
				}
			}
		}	
	}			
	
	// set the normal value, so the camera is not too confused
	m_current_normal = GetMatrix()[Y];
	  
	// Add mGrindTweak to the score.
	// adjusted by the robot rail mult (1.0 to 0.1, depending on how much you've ground the rail)
	mp_score_component->GetScore()->TweakTrick(mGrindTweak * mp_score_component->GetScore()->GetRobotRailMult());

	// if we've already started doing a trick, then start remembering our trick chain
	{
		const CRailNode* pRail = mp_rail_node;
		Dbg_Assert(pRail);

		if (mp_rail_man->GetNodeArray())
		{
			Script::CArray* pNodeArray=Script::GetArray(CRCD(0xc472ecc5, "NodeArray"));
			Script::CStruct* pNode=pNodeArray->GetStructure(pRail->GetNode());
			pNode->GetChecksum(CRCD(0xa1dc81f9, "name"), &m_last_rail_node_name, true);
			mp_trick_component->TrickOffObject(m_last_rail_node_name);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::ollie_off_rail_rotate (   )
{
	float rot;
	if (mp_input_component->GetControlPad().m_left.GetPressed())
	{
		rot = Mth::DegToRad(GetPhysicsFloat(CRCD(0x38505ef3, "Rail_Jump_Angle")));
	}
	else if (mp_input_component->GetControlPad().m_right.GetPressed())
	{
		rot = -Mth::DegToRad(GetPhysicsFloat(CRCD(0x38505ef3, "Rail_Jump_Angle")));
	}
	else return;

	GetVel().RotateY(rot);		 
	GetMatrix().RotateYLocal(rot);				
	m_lerping_display_matrix.RotateYLocal(rot);				
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::skate_off_rail ( const Mth::Vector& off_point )
{
	// we have skated off a rail; either it was the end of a rail or we hit a sharp corner
	// we need to see if there is another rail in front of us that we can continue skating on

	bool really_off = true;
	
	Mth::Vector	a;
	if (Ed::CParkEditor::Instance()->UsingCustomPark())
	{
		// in the park editor, we use the last point on the rail we just took off from as the start of the line to use for looking for new rails
		// so we don't get rail segments earlier in the list
		a = off_point;
	}
	else
	{
		// go back a step
		a = GetPos() - GetVel() * m_frame_length;
		DUMP_POSITION;
	}
	
	// use current pos, as we know that is off the end
	Mth::Vector b = GetPos();
	
	Mth::Vector rail_pos;	
	CRailNode* pNode;	
	

	// we need to tilt the line down, as the rail code currently fails to find a rail if the line we check is exactly parallel with it	
	a[Y] += 6.0f;							   
	b[Y] += 6.1f;							   

	float min_dot = cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x76c1da15, "Rail_Corner_Leave_Angle"))));

	// Note: we are now passing in Rail_Side() of the current rail and velocity
	// so we can see if we switch from a rail on a left facing ledge to a rail on a right facing ledge, and try to inhibit that type of transition
	// in favour of one that retains the same side
	if (mp_rail_man->StickToRail(
			a,
			b, 
			&rail_pos,
			&pNode,
			mp_rail_node,
			min_dot,
			mp_rail_node->Side(GetVel())
		))
	{		
		// Mick, in park editor, we also disallow this if the rail is the next or the prev rail node from our current node
		if (mp_rail_node != pNode && (
				!Ed::CParkEditor::Instance()->UsingCustomPark()
				|| mp_rail_node->GetNextLink() != pNode
				&& mp_rail_node->GetPrevLink() != pNode
			))
		{
			const CRailNode* pNewStart = pNode;
			const CRailNode* pNewEnd = pNewStart->GetNextLink();	
			
			// check to see if our new position is within the two points
			Mth::Vector	to_start = mp_rail_man->GetPos(pNewStart) - GetPos();
			Mth::Vector	to_end = mp_rail_man->GetPos(pNewEnd) - GetPos();
			
			float mid_dot = Mth::DotProduct(to_start, to_end);

			// In game, the point must actualy be in the line, so mid dot will be negative
			bool ok = mid_dot < 0.0f;					
			
			// Park Editor specific rail joining			
			if (!ok && Ed::CParkEditor::Instance()->UsingCustomPark())
			{
				// in the park editor, we let the user overshoot, so he sticks to tight curves that are between two pieces
				ok = true;

				// we need to ensure that the rail segment is a good continuation of the segment that we were just on
				// Namely that one of the new start/end points is close to the end of the rail that we just came off
				// and that the direction we will be going along is within 45 degrees of the direction we were going along before. 
				// (NOT DONE HERE, as the following test is sufficent)
											 
				// first a simple elimination, if the rail is longer than our velocity projected onto it then we can not posibly have overshot it!!
				Mth::Vector	new_rail_segment = mp_rail_man->GetPos(pNewEnd) - mp_rail_man->GetPos(pNewStart);
				Mth::Vector skater_movement = b - a;
				float new_rail_length = new_rail_segment.Length();
				if (new_rail_length > skater_movement.Length())
				{
					ok = false;
				}
				
				// now we could find the shortest distance between two line segments; should be within 2 inches
				// (ALSO NOT DONE)
				
				// bit of a patch for the park editor, it's gone past the end of the rail, so set him in the middle of the rail, so we can continue nicely
				// possible glitch here, but better than falling of the rail
				// It's going to be a small rail anyway, so won't look too bad.
				if (ok && mid_dot >= 0.0f)
				{
					rail_pos = (mp_rail_man->GetPos(pNewEnd) + mp_rail_man->GetPos(pNewStart)) / 2.0f;
				}

			}

			if (ok)
			{
				Mth::Vector	newdir = mp_rail_man->GetPos(pNewEnd) - mp_rail_man->GetPos(pNewStart);
				newdir.Normalize();
				if (GetVel()[X] == 0.0f && GetVel()[Z] == 0.0f)
				{
					GetVel()[X] = GetMatrix()[Z][X];
					GetVel()[Z] = GetMatrix()[Z][Z];
					DUMP_VELOCITY;
				}

				// sign is which way we are going along the rail
				float sign = Mth::Sgn(Mth::DotProduct(newdir, GetVel()));
				GetVel().RotateToNormal(newdir);
				GetVel() *= sign;
				
				mp_rail_node = pNode;		// oh yes, this is the node
				GetPos() = rail_pos;			// move to closest position on the line
				DUMP_POSITION;
				maybe_trip_rail_trigger(TRIGGER_SKATE_ONTO);
				set_terrain(mp_rail_node->GetTerrain());
		
				really_off = false;			
			}				
			else
			{
				// new position is outside this rail
			}	
		}
		else
		{
			// its the same as this one
		}	
	}
	else
	{
		// did not find any
	}
	
	if (!really_off) return;

	maybe_trip_rail_trigger(TRIGGER_SKATE_OFF);
	SetState(AIR);
	GetPos()[Y] += 1.0f;
	DUMP_POSITION;
	FLAGEXCEPTION(CRCD(0xafaa46ba,"OffRail"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::maybe_trip_rail_trigger ( uint32 type )
{
	// given that m_rail_node is valid, then trigger any script associated with this rail node
	// will search backwards for the first rail that has a trigger script, and then execute that.
	
	if (!mp_rail_node) return;
	
	Dbg_Assert(mp_rail_man);
		 
	
	const CRailNode* pStartOfRail = mp_rail_node;																	 
	const CRailNode* pRail = mp_rail_node;																	 
	
	// no node array in rail manager indicates auto generated rails, so just return
	Script::CArray* pNodeArray = mp_rail_man->GetNodeArray();
	if (!pNodeArray) return;
	
	Script::CStruct* pNode = pNodeArray->GetStructure(mp_rail_node->GetNode());

	// find a rail node that has a "TriggerScript" in it
	uint32 value = 0;
	if (pNode->GetChecksum(CRCD(0x2ca8a299, "Triggerscript"), &value))
	{
		// we got it 
	}
	else
	{
		// did not get it, so scoot backwards until we detect a loop or we find one
		const CRailNode* p_loop_detect = pRail;	 	// start loop detect at the start
		pRail = pRail->GetPrevLink(); 				// and the first node we check is the next one
		int loop_advance = 0;
		while (pRail && pRail != pStartOfRail && pRail != p_loop_detect)
		{
			pNode = pNodeArray->GetStructure(pRail->GetNode());
			if (pNode->GetChecksum(CRCD(0x2ca8a299, "Triggerscript"), &value)) break;
			
			pRail = pRail->GetPrevLink(); 
			// The p_loop_detect pointer goes backwards at half speed, so if there is a loop
			// then pRail is guarenteed to eventually catch up with p_loop_detect
			if (loop_advance)
			{
				p_loop_detect = p_loop_detect->GetPrevLink();				
			}
			loop_advance ^= 1;
		}
	}

	if (value)
	{
		// If this is different to last time, then set flag accordingly
		uint32 new_last_rail_node_name;
		pNode->GetChecksum(CRCD(0xa1dc81f9, "name"), &new_last_rail_node_name, Script::ASSERT);
//		printf ("%s,%s\n",Script::FindChecksumName(new_last_rail_node_name), Script::FindChecksumName(m_last_rail_node_name));
		if (new_last_rail_node_name != m_last_rail_trigger_node_name)
		{
			SetFlagTrue(NEW_RAIL);
		}
		else
		{
			SetFlagFalse(NEW_RAIL);
		}
		m_last_rail_node_name = new_last_rail_node_name;
		m_last_rail_trigger_node_name = new_last_rail_node_name;
		
		
		// if we are using the default node array, then set it to NULL, so TriggerEventFromNode can use this default, which is a lot faster
		if (pNodeArray == Script::GetArray(CRCD(0xc472ecc5, "NodeArray")))
		{
			pNodeArray = NULL;
		}
		
		mp_trigger_component->TripTrigger(
			type,
			m_last_rail_node_name,
			pNodeArray,
			mp_movable_contact_component->HaveContact() ? mp_movable_contact_component->GetContact()->GetObject() : NULL
		);
	}
}		

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::handle_tensing (   )
{
	if (!GetFlag(TENSE) && mp_input_component->GetControlPad().m_x.GetPressed())
	{
		// just starting to tense, so set the flag 
		SetFlagTrue(TENSE);		
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCorePhysicsComponent::get_member_feeler_collision ( uint16 ignore_1, uint16 ignore_0 )
{
	// use m_col_start and m_col_end to set up m_feeler, then check for collision and if there is a collision, then get the various skater collision flags

	m_feeler.SetLine(m_col_start, m_col_end);
	m_feeler.SetIgnore(ignore_1, ignore_0);

	bool collision = m_feeler.GetCollision();
	
	if (!collision) return false;
	
	if (m_feeler.GetNodeChecksum() && m_feeler.GetTrigger())
	{
		// Given a node name, then find the checksum of the triggerscript in that node
		// note that this is VERY SLOW, as it has to scan the whole node array
		// it's usually only called once per frame, per skater, but might still be a good candidate for optimization
		
		// Now just clear the script, indicating it needed doing later
		m_feeler.SetScript(0);
	}

	// get any skating specific flags here
	uint16 flags = m_feeler.GetFlags();

	// get normal, as it's the kind of things we use in face flag determination				   
	Mth::Vector	normal = m_feeler.GetNormal();								
					
	// Make wallable default to off.				
	m_col_flag_wallable = false;
	
	///////////////////////////////////////////////////////////////////
	// Vert
	
	m_col_flag_vert = flags & mFD_VERT;
	
	////////////////////////////////////////////////////////////////
	// Skatable					   
	   
	if (flags & mFD_SKATABLE)
	{
		// if flaged as skatable, than that overrides all other flags
		m_col_flag_skatable = true;
	}
	else if (flags & mFD_NOT_SKATABLE)
	{
		// if flagged as non_skatable, then that overrides all other flags
		m_col_flag_skatable = false;
	}
	else if (flags & mFD_VERT)
	{			
		// if flagged as VERT, then it's the top of a QP, so it's skatable and this overrides the angle flag
		m_col_flag_skatable = true;
	}
	else if (flags & mFD_WALL_RIDABLE)
	{
		m_col_flag_skatable = false;
		m_col_flag_wallable = true;
	}
	else
	{
		// determine the skatablitlity based on the angle
		// if angle is > 5 degrees from vert, then it is skatable
		// here the y component of the normal is the cosine of the angle from vertical
								  
		if (normal[Y] < sinf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x3eede4d3,"Wall_Non_Skatable_Angle")))))
		{
			// get could possibly do another test here to see if there is a polygon beneath this meaning this is a QP
			// however, we want to be flagging the tops of all QPs
			m_col_flag_skatable = false;
		}
		else
		{
			m_col_flag_skatable = true;
		}
		
	}

	return collision;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::do_jump ( Script::CStruct *pParams )
{
	// Called by the Jump script command
	
	// play sounds before modifying the skater speed, as the sound volume and pitch are based on the pre-jump velocity
	bool play_sound = !pParams->ContainsFlag(CRCD(0xe13620a8, "no_sound"));
	
	switch (GetState())
	{
		case GROUND:
			mp_trigger_component->CheckFeelerForTrigger(TRIGGER_JUMP_OFF, m_last_ground_feeler);
			if (mp_physics_control_component->HaveBeenReset()) return;
			
			// K: Remember the last ground position for calculating which side of the rail we're on later.
			m_last_ground_pos = GetPos();
			
			if (play_sound)
			{
				mp_sound_component->PlayJumpSound(GetObject()->GetVel().Length() / GetSkater()->GetScriptedStat(CRCD(0xcc5f87aa, "Skater_Max_Max_Speed_Stat")), m_last_ground_feeler.GetTerrain());
			}	
			
			maybe_straight_up();
			SetFlagTrue(CAN_BREAK_VERT);
			break;

		case AIR:
			if (play_sound)
			{
				mp_sound_component->PlayJumpSound(GetObject()->GetVel().Length() / GetSkater()->GetScriptedStat(CRCD(0xcc5f87aa, "Skater_Max_Max_Speed_Stat")), m_last_ground_feeler.GetTerrain());
			}	
			break;

		case RAIL:
			if (play_sound)
			{
				Dbg_Assert(mp_rail_node);
				mp_sound_component->PlayJumpSound(GetObject()->GetVel().Length() / GetSkater()->GetScriptedStat(CRCD(0xcc5f87aa, "Skater_Max_Max_Speed_Stat")), mp_state_component->m_terrain);
			}	
			break;
			
		default:
			break;
	}
			  
	Dbg_Assert(pParams);
	
	int max_tense_time = GetPhysicsInt(CRCD(0xf733c097, "skater_max_tense_time"));
	m_tense_time = Mth::Min(m_tense_time, max_tense_time);
	
	float max_jump_speed;
	float min_jump_speed;
	
	bool from_vert = GetFlag(VERT_AIR) || (GetState() == GROUND && GetFlag(LAST_POLY_WAS_VERT));

	if (pParams->ContainsFlag(CRCD(0xec6b7fc7, "BonelessHeight")))
	{
		if (from_vert)
		{
			max_jump_speed = GetSkater()->GetScriptedStat(CRCD(0x39766891, "Physics_Boneless_air_Jump_Speed_stat"));
			min_jump_speed = GetSkater()->GetScriptedStat(CRCD(0x4cdb18cb, "Physics_Boneless_air_Jump_Speed_min_stat"));
		}
		else
		{
			max_jump_speed = GetSkater()->GetScriptedStat(CRCD(0x8851a76e, "Physics_Boneless_Jump_Speed_stat"));
			min_jump_speed = GetSkater()->GetScriptedStat(CRCD(0x6e8efe38, "Physics_Boneless_Jump_Speed_min_stat"));
		}
	}	
	else
	{
		if (from_vert)
		{
			max_jump_speed = GetSkater()->GetScriptedStat(CRCD(0x9f79c8ea, "Physics_air_Jump_Speed_stat"));
			min_jump_speed = GetSkater()->GetScriptedStat(CRCD(0x91b07824, "Physics_air_Jump_Speed_min_stat"));
		}
		else
		{
			max_jump_speed = GetSkater()->GetScriptedStat(CRCD(0x2ade3ad, "Physics_Jump_Speed_stat"));
			min_jump_speed = GetSkater()->GetScriptedStat(CRCD(0xc8815e43, "Physics_Jump_Speed_min_stat"));
		}
	}	
	
	float jump_speed = Mth::LinearMap(min_jump_speed, max_jump_speed, m_tense_time, 0.0f, max_tense_time);
	
	// If any speed param is specified, then use that instead. Need by Zac for when small skater is doing scripted jumps in the front end.
	pParams->GetFloat(CRCD(0xf0d90109, "speed"), &jump_speed);

	SetFlagFalse(TENSE);

	// if we have a very high downward velocity and we are landing on vert, then do not jump

	// when jumping, don't add in the current velocity if it is less than zero
	// this will give us nicer feeling jumps when 
	//  - comping down wall rides
	//  - skating down slopes
	//  - skating down the back of bumps, liks the speed bumps in CA
	//  - landing on a QP

	
	if (GetFlag(VERT_AIR) && GetVel()[Y] < 0.0f)
	{
		 	// push outward	(and upward) away from vert poly
			GetVel() += jump_speed * m_display_normal;
			DUMP_VELOCITY;
			jump_speed = 0;
			SetFlagFalse(VERT_AIR);
	}
	else
	{
		if (GetVel()[Y] < 0.0f)
		{
			GetVel()[Y] = 0.0f;
			DUMP_VELOCITY;
		}
	}
	
	GetVel()[Y] += jump_speed;
	DUMP_VELOCITY;

	// if pointing down, then jump down, as moving up will make us pass through whatever we are on	
	if (GetMatrix()[Y][Y] < -0.1f)
	{
		GetVel()[Y] -= jump_speed * 1.5f;
		DUMP_VELOCITY;
		GetPos() += GetMatrix()[Y] * 12.0f;
		DUMP_POSITION;
	}
	
	// allow side jumps when jumping off rails or when late jumping in air after skating off a rail
	if (GetState() == RAIL || (GetState() == AIR && m_previous_state == RAIL))
	{
		ollie_off_rail_rotate();
	}
	
	// don't change the state until after sound is played, as the above switch relyies on the GROUND/RAIL state...
	SetState(AIR);
	
	m_last_jump_time_stamp = Tmr::GetTime();
	
	GetObject()->BroadcastEvent(CRCD(0x8687163a, "SkaterJump"));
}			

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::start_skitch (   )
{
	// Start Skitching, given that we've queue one up
	// Note, mp_skitch_object is a smart pointer so there is a slight possibility that it might have died but we check that
	
	// Mick:  I'm using .Convert here.   I'm not sure if makes any difference,
	// but it can't hurt and should compile the same.
	if (!mp_skitch_object.Convert()) return;
	
	mp_movable_contact_component->ObtainContact(mp_skitch_object);
	SetFlagTrue(SKITCHING);
	m_moving_to_skitch = true;
	move_to_skitch_point();
	
	// Mick:  For some reason we sometimes get a NULL smart pointer here
	// so I'm checking again here, in addition to changing the code
	// to use .Convert 	
	if (!mp_skitch_object.Convert()) return;
	
	mp_skitch_object->SelfEvent(CRCD(0x35224f25, "SkitchOn"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::do_grind_trick ( uint Direction, bool Right, bool Parallel, bool Backwards, bool Regular )
{
	int SubArrayIndex = 0;
	
	// Choose which array index to use.
	switch (Direction)
	{
		case 0: // Nowt
			SubArrayIndex = 0;
			break;
		case PAD_U: // Up
			SubArrayIndex = 1;
			break;
		case PAD_D: // Down
			SubArrayIndex = 2;
			break;
		case PAD_L: // Left
			SubArrayIndex = 3;
			break;
		case PAD_R: // Right
			SubArrayIndex = 4;
			break;
		case PAD_UL: // UpLeft
			SubArrayIndex = 5;
			break;
		case PAD_UR: // UpRight
			SubArrayIndex = 6;
			break;
		case PAD_DL: // DownLeft
			SubArrayIndex = 7;
			break;
		case PAD_DR: // DownRight
			SubArrayIndex = 8;
			break;
		default:
			Dbg_MsgAssert(false, ("Bad button checksum"));
			break;
	}	
		
	// GetArray will assert if GrindTrickList not found.
	Script::CArray* pMainArray = Script::GetArray(CRCD(0x2ab3341d, "GrindTrickList"));
	Script::CArray* pSubArray = pMainArray->GetArray(SubArrayIndex);

	uint Index = 0;
	if (Right) Index |= 1;
	if (Parallel) Index |= 2;
	if (Backwards) Index |= 4;
	if (Regular) Index |= 8;

	// Initialise mGrindTweak, which should get set by a SetGrindTweak command in the script that is about to be run.
	ResetGrindTweak();
		
	CSkaterFlipAndRotateComponent* p_flip_and_rotate_component = GetSkaterFlipAndRotateComponentFromObject(GetObject());
	Dbg_Assert(p_flip_and_rotate_component);
	p_flip_and_rotate_component->DoAnyFlipRotateOrBoardRotateAfters();
	
	if (!GetObject()->GetScript())
	{
		GetObject()->SetScript(new Script::CScript);
	}
	
	GetObject()->GetScript()->SetScript(pSubArray->GetNameChecksum(Index), NULL, GetObject());
	GetObject()->GetScript()->Update();
	
	// Set the mDoingTrick flag so that the camera can detect that a trick is being done.
	mp_state_component->SetDoingTrick(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::StopSkitch (   )
{
	// If we were skitching, then stop it
	
	if (!GetFlag(SKITCHING)) return;
	
	SetFlagFalse(SKITCHING);
	if (mp_skitch_object)
	{
		mp_skitch_object->SelfEvent(CRCD(0x2739b86d, "SkitchOff"));
		mp_skitch_object = NULL;
	}
	mp_movable_contact_component->LoseAnyContact();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::setup_default_collision_cache (   )
{
	// reduced Y extent as this no longer subsumes the uberfrig
	Mth::CBBox bbox(
		GetPos() - Mth::Vector(FEET(15.0f), FEET(17.0f), FEET(15.0f), 0.0f),
		GetPos() + Mth::Vector(FEET(15.0f), FEET(8.0f), FEET(15.0f), 0.0f)
	);
	
	mp_coll_cache->Update(bbox);
	CFeeler::sSetDefaultCache(mp_coll_cache);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCorePhysicsComponent::update_special_friction_index (   )
{
	if (m_special_friction_index == 0) return;
	
	if (Tmr::ElapsedTime(m_special_friction_decrement_time_stamp) > static_cast< Tmr::Time >(1000.0f * Script::GetFloat(CRCD(0xf4813ad5, "Physics_Time_Before_Free_Revert"))))
	{
		m_special_friction_index--;
		MESSAGE("You earned a free revert!!!!");
		if (m_special_friction_index != 0)
		{
			m_special_friction_decrement_time_stamp = Tmr::GetTime();
		}
	}
}

}


================================================
FILE: Code/Sk/Components/SkaterCorePhysicsComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterCorePhysicsComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/21/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERCOREPHYSICSCOMPONENT_H__
#define __COMPONENTS_SKATERCOREPHYSICSCOMPONENT_H__

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define		CRC_SKATERCOREPHYSICS CRCD(0x5bd63b29, "SkaterCorePhysics")

#define		GetSkaterCorePhysicsComponent() ((Obj::CSkaterCorePhysicsComponent*)GetComponent(CRC_SKATERCOREPHYSICS))
#define		GetSkaterCorePhysicsComponentFromObject(pObj) ((Obj::CSkaterCorePhysicsComponent*)(pObj)->GetComponent(CRC_SKATERCOREPHYSICS))

namespace Script
{
    class CScript;
    class CStruct;
}

namespace Nx
{
	class CCollCache;
}
              
namespace Obj
{
	class CInputComponent;
	class CTriggerComponent;
	class CSkaterLoopingSoundComponent;
	class CTrickComponent;
	class CSkaterRotateComponent;
	class CSkaterScoreComponent;
	class CMovableContactComponent;
	
	struct SRailData;

class CSkaterCorePhysicsComponent : public CBaseComponent
{
	friend Mdl::Skate;
	friend CSkater;
	friend CSkaterCam;
	friend CManual;
	friend CSkaterCameraComponent;
	friend CSkaterAdjustPhysicsComponent;
	friend CSkaterFinalizePhysicsComponent;
	friend CSkaterNonLocalNetLogicComponent;
	friend CSkaterRotateComponent;
	friend CSkaterPhysicsControlComponent;
	friend CSkaterMatrixQueriesComponent;
	friend CSkaterStateHistoryComponent;
	friend CWalkComponent;
	
public:
    CSkaterCorePhysicsComponent();
    virtual ~CSkaterCorePhysicsComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
public:
	void							Reset (   );
	void							ReadySkateState ( bool to_ground_state, const SRailData* p_rail_data, const SAcidDropData* p_acid_drop_data );
	void							CleanUpSkateState (   );
	
	EStateType						GetState (   );
	void							SetState ( EStateType state );
	bool							GetFlag ( ESkaterFlag flag );
	void							SetFlag ( ESkaterFlag flag, bool state = true );
	void							SetFlagTrue ( ESkaterFlag flag );
	void							SetFlagFalse ( ESkaterFlag flag );
	void							ToggleFlag ( ESkaterFlag flag );
	Tmr::Time						GetFlagTime ( ESkaterFlag flag );
	Tmr::Time						GetFlagElapsedTime ( ESkaterFlag flag );
	void							ResetFlags (   );
	void							ResetSpecialFrictionIndex (   );
	void							ReverseFacing (   );
	bool							HaveLandedThisFrame (   );
	ETerrainType					GetTerrain (   );
	ETerrainType					GetLastGroundTerrain (   );
	uint32							GetLastNodeChecksum (   );
	bool							GetTrueLandedFromVert (   );
	void							ResetLerpingMatrix (   );
	void							StopSkitch (   );
	bool							IsSwitched (   );
	void							ResetGrindTweak (   );
	sint16							GetRailNode( void );
	
	void							CollideWithOtherSkaterLost ( CCompositeObject* p_other_skater );
	
	Mth::Vector&					GetPos (   ) { return GetObject()->m_pos; }
	Mth::Vector&					GetOldPos (   ) { return GetObject()->m_old_pos; }
	Mth::Matrix&					GetMatrix (   ) { return GetObject()->m_matrix; }
	Mth::Vector&					GetVel (   ) { return GetObject()->m_vel; }
	
private:
	void							do_on_ground_physics (   );
	void							do_in_air_physics (   );
	void							do_wallride_physics (   );
	void							do_wallplant_physics (   );
	void							do_lip_physics (   );
	void							do_rail_physics (   );
	
	void							limit_speed (   );
	bool							is_trying_to_brake (   );
	bool							is_braking (   );
	void							do_brake (   );
	bool							on_steep_slow_slope (   );
	bool							can_kick (   );
	void							do_kick (   );
	void							handle_ground_friction (   );
	void							handle_wind_resistance (   );
	void							handle_rolling_resistance (   );
	bool							slide_off_slow_steep_slope (   );
	void							apply_wind_resistance ( float friction );
	void							apply_rolling_friction (   );
	void							push_away_from_walls (   );
	void							bounce_off_wall ( const Mth::Vector& normal );
	void							check_for_wall_push ( const Mth::Vector& start, const Mth::Vector& end, int index );
	void							snap_to_ground (   );
	void							maybe_straight_up (   );
	void							maybe_break_vert (   );
	bool							maybe_spine_transfer (   );
	bool							look_for_transfer_target ( const Mth::Vector& search_dir, const Mth::Vector& start_normal, bool& hip_transfer, Mth::Vector& target, Mth::Vector& target_normal );
	bool							maybe_acid_drop ( bool skated_off_edge, const Mth::Vector &pos, const Mth::Vector& old_pos, Mth::Vector& vel, SAcidDropData& acid_drop_data );
	void							enter_acid_drop ( const SAcidDropData& acid_drop_data );
	void							handle_post_transfer_limit_overrides (   );
	void							handle_ground_rotation (   );
	void							handle_air_rotation (   );
	void							remove_sideways_velocity ( Mth::Vector& vel );
	void							check_side_collisions (   );
	bool							check_side ( float Side, float SideCol );
	void							handle_forward_collision (   );
	float							rotate_away_from_wall ( const Mth::Vector& normal, float& turn_angle, float lerp = 1.0f );
	float							get_air_gravity (   );
	void							check_leaning_into_wall (   );
	float							calculate_time_to_reach_height ( float target_height, float pos_Y, float vel_Y );
	void							flip_if_skating_backwards (   );
	bool							maybe_flag_ollie_exception (   );
	void							handle_air_lean (   );
	void							handle_transfer_slerping (   );
	void							handle_air_vert_recovery (   );
	void							rotate_upright (   );
	bool							handle_forward_collision_in_air ( const Mth::Vector& start_pos );
	bool							check_for_air_snap_up ( const Mth::Vector& start_pos );
	bool							handle_upward_collision_in_air (   );
	bool							handle_upward_collision_in_wallride (   );
	void							ollie_off_rail_rotate (   );
	void							skate_off_rail ( const Mth::Vector& off_point );
	bool							check_for_wallpush (   );
	bool							check_for_wallplant (   );
	bool							check_for_wallride (   );
	void							maybe_skitch (   );
	void							move_to_skitch_point (   );
	void							check_movable_contact (   );
	void							new_normal ( Mth::Vector normal );
	void							adjust_normal (   );
	void							maybe_trip_rail_trigger ( uint32 type );
	void							handle_tensing (   );
	void							maybe_stick_to_rail ( bool override_air = false );
	bool							will_take_rail ( const CRailNode* pNode, CRailManager* p_rail_man, bool from_walk = false );
	void							got_rail ( const Mth::Vector& rail_pos, const CRailNode* pNode, CRailManager* p_rail_man, bool no_lip_tricks = false, bool from_walk = false );
	bool							get_member_feeler_collision ( uint16 ignore_1 = mFD_NON_COLLIDABLE, uint16 ignore_0 = 0 );
	void							start_skitch (   );
	void							do_grind_trick ( uint Direction, bool Right, bool Parallel, bool Backwards, bool Regular );
	void							set_terrain ( ETerrainType terrain );
	void							set_last_ground_feeler ( CFeeler& feeler );
	void							setup_default_collision_cache (   );
	bool							is_vert_for_transfers ( const Mth::Vector& normal );
	void							update_special_friction_index (   );
	
public:
	void							do_jump ( Script::CStruct *pParams );
	
private:
	Mth::Matrix						m_lerping_display_matrix;
	Mth::Vector						m_display_normal;				// current normal the skater is oriented to
	Mth::Vector						m_current_normal;   			// normal of current polygon 
	Mth::Vector						m_last_display_normal;   		// last value of m_display_normal
	float							m_normal_lerp;					// counter 1 to 0 for amount of normal lerp
	
	float							m_frame_length;
	
	Tmr::Time						m_state_change_timestamp;		// timestamp of the latest state change
	EStateType						m_previous_state;				// the skater's state previous to the current state
	
	Mth::Vector						m_safe_pos;						// Safe world position, to which we can be moved in an emergency
	
	CFeeler							m_feeler;	   					// collision data from last collision check 
	CFeeler							m_last_ground_feeler;			// and from last time we were in valid contact with ground
	Mth::Vector						m_col_start, m_col_end;			// start and end of collision line segment
	bool							m_col_flag_skatable;			// true is last face was skatable
	bool							m_col_flag_vert;				// true if last face was flagged vert
	bool							m_col_flag_wallable;			// true if last face was flagged wall-ridable
	
	// Mth::Vector						m_movement;						// distance we want to move this frame
																	// used when moving over ground so we don't travel short distances
																	// when we hit a concave surface which causes collisions				  
	
	const CRailNode*				mp_rail_node;					// pointer to current rail node
	CRailManager*					mp_rail_man;					// pointer to manager of this rail
	Tmr::Time						m_rail_time;					// last time we were on a rail
	Tmr::Time						m_lip_time;						// last time we were on a lip
	Tmr::Time						m_rerail_time;					// time that must elapse before we can get back on the same rail
	uint32							m_last_rail_node_name;
	uint32							m_last_rail_trigger_node_name;
	bool							mLedge;                         // Whether it's a ledge. Calculated in MaybeStickToRail.
	bool							mBadLedge;                      // Whether this is a bad ledge. Calculated in MaybeStickToRail, and 
																	// used by the BadLedge script command.
	
	
	bool							m_moving_to_skitch; 			// true if in initial movment to skitch
	int								m_skitch_index;					// index of the node number on the object we are skitching
	float							m_skitch_dir;					// direction (-1.0f or 1.0f) we are moving in
	
	bool							m_landed_this_frame;			// true if we just landed this frame
	
	bool							m_began_frame_in_transfer;
	bool							m_began_frame_in_lip_state;

	Mth::Vector						m_pre_lip_pos;					// position we were at before we snapped to a lip

	Mth::Vector						m_vert_pos;						// point below us on a vert face
	Mth::Vector						m_vert_normal;					// normal of that face

	Mth::Vector						m_fall_line;					// unit vector that points "down" the plane

	float							m_vert_upstep;					// amount we try to move up each frame when tracking vert

	Mth::Vector						m_extra_ground_push;			// extra vector to apply to next "ground" movement

	Mth::Vector						m_wall_push; 					// direction to push away from wall
	float							m_wall_dist;					// shortest distance from wall
	float							m_wall_push_radius;				// radius to check
	float							m_wall_push_speed;				// speed at which to push out
	float 							m_wall_rotate_speed;			// speed at which we rotate away

	float							m_last_time;					// time at last frame
	
public:
	bool							m_auto_kick;   					// true if auto kick on
	bool							m_spin_taps;					// true when 180 spin taps are enabled.
	
protected:
	float							m_tap_turns;					// How much tap-turn rotation remains.

	bool							m_force_cankick_off;			// Set by the CanKickOff script command, so that Scott can
																	// force CanKick to return false;

	bool							m_pressing_down_brakes;			// Set/Reset by the CanBrakeOn and CanBrakeOff
																	// commands.


	float							m_rolling_friction;				// Friction, set by SetRollingFriction script command.
	int								m_special_friction_index;		// Index into an array of special friction values, set by the
																	// SetSpecialFriction command (used by reverts)
	Tmr::Time						m_special_friction_decrement_time_stamp;
	float							m_special_friction_duration;	// When m_rolling_friction is set via special friction, it must be given a duration;
																	// after which it reverts to default.  Calls to set m_rolling_friction to default
																	// are ignored during this duration.

	bool							m_braking;						// true if currently braking
	Tmr::Time						m_push_time[4];					// time of last side push

	Mth::Vector						m_last_ground_pos; 				// Set in DoJump(), & used when calculating which side of rail skater is on.
	bool							mRail_Backwards;

																	// Flags and value to override the speed limits set on the skater
	float							m_override_limits_time;			// 0.0f if not overriding, else time in seconds to override for
	float							m_override_max;					// override value for max_speed
	float							m_override_max_max;				// override value for max_max_speed
	float							m_override_air_friction;    	// override value for air friction
	float							m_override_down_gravity;		// override value for ground gravity (only applied when not going down)

	Obj::CSmtPtr	mp_skitch_object;

	bool							mNoSpin;						// Gets set and reset by NoSpin and CanSpin.
																	// Disables left-right pad control of the spin.
	bool							m_bail;							// Set by BailOn, reset by BailOff and tested using BailIsOn
	
	bool							m_front_truck_sparks;			// True if sparks come from the front trucks; false if from the rear

	bool							mLandedFromSpine;				// True if landed from a transfer.
	bool							mLandedFromVert;				// True if landed from vert. Can be set & reset by script commands.
	
	bool							m_true_landed_from_vert;		// This is the 'true' landed from vert flag, which only gets set by the C-code on landing. Scripts
																	// cannot change its value.
																	// Used by the Clearpanel_Landed C-code to determine whether or not to count an ollie as a trick.
																	// 180 Ollies are not counted as tricks if you landed from vert, only spins of at least 360 count.
																	// mLandedFromVert can't be used because the scripts have already cleared it by the time the 
																	// Clearpanel_Landed command is executed.
	
	bool							mAllowLipNoGrind;               // When this is set, then lip tricks will always happen when sticking to a rail,
																	// and grinds will not be allowed.
																	// Only gets set or reset by script commands, AllowLipNoGrind and ClearAllowLipNoGrind
	
	int								m_tense_time;                   // The time that the player was tensed for. Gets recorded by MaybeFlagOllieException,
																	// and used by the DoJump function.
	
	float							m_transfer_target_height;		// target point for transfer
	
																	// These next two needed by the AirTimeLessThan and AirTimeGreaterThan script functions.
	Tmr::Time						m_went_airborne_time;			// The time when the state change to AIR
	Tmr::Time						m_landed_time; 					// The time when the state changed from AIR to something else.
	
	bool							m_lock_velocity_direction;		// Ken: Switched on/off by the LockVelocityDirection command.
																	// This is a way to allow the skater to be able to cross a gap in a loop whilst
																	// inverted and technically being on the ground (on an invisible poly) but such that he 
																	// looks he's in the air. He will be able to be rotated, but his velocity will not turn
																	// with his rotation.
	
	Mth::Vector						mWallNormal;
	uint32							mWallrideTime; 					// The time the wall-ride was triggered. Used to stop the player triggering
																	// another one too soon after the last.

	bool							mNoRailTricks;                  // K: No grinds or lip tricks are allowed if this flag is set. This is switched on and off by
																	// the NoGrind and AllowGrind script commands. These are used when jumping out of
																	// a lip trick to disallow going straight into a grind.
	
	bool							mYAngleIncreased;				// Keeps track of whether the y angle last increased or decreased.
																	// Used by the LastSpinWas command.
	
	int								mGrindTweak;
	
	float							m_transfer_overrides_factor;	// Control the overriding of speed limits after an acid drop.
	
	Mth::Matrix						m_acid_drop_camera_matrix;
	
	Mth::SlerpInterpolator			m_transfer_slerper;				// Variables controlling the slerping of the skater's matrix during an acid drop
	float							m_transfer_slerp_timer;
	float							m_transfer_slerp_duration;
	Mth::Vector						m_transfer_goal_facing;
	Mth::Matrix						m_transfer_slerp_previous_matrix;

	Tmr::Time						m_last_wallplant_time_stamp;	// Timestamps wallplants in order to not allow a second one for a short duration.
	Tmr::Time						m_last_wallpush_time_stamp;		// Timestamps wallpushes in order to not allow a second one for a short duration.
	
	Tmr::Time						m_last_jump_time_stamp;			// Timestamps jumps
	
	bool							m_vert_air_last_frame;			// Used to broadcast vert air enter and exit events
	
	Nx::CCollCache*					mp_coll_cache;
	
private:
	// peer components
	CInputComponent*				mp_input_component;
	CTriggerComponent*				mp_trigger_component;
	CSkaterSoundComponent*			mp_sound_component;
	CTrickComponent*				mp_trick_component;
	CSkaterRotateComponent*			mp_rotate_component;
	CSkaterScoreComponent*			mp_score_component;
	CSkaterBalanceTrickComponent*	mp_balance_trick_component;
	CSkaterStateComponent*			mp_state_component;
	CMovableContactComponent*		mp_movable_contact_component;
	CSkaterPhysicsControlComponent*	mp_physics_control_component;
	CWalkComponent*					mp_walk_component;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
inline bool CSkaterCorePhysicsComponent::is_braking (   )
{
	 return m_braking;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
inline bool CSkaterCorePhysicsComponent::is_vert_for_transfers ( const Mth::Vector& normal )
{
	// cull out non-vert vert polys when looking for spine transfer and acid drop triggers; allows designers to be a little sloppier
	return Mth::Abs(normal[Y]) < 0.707f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline EStateType CSkaterCorePhysicsComponent::GetState (   )
{
	return mp_state_component->m_state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Tmr::Time CSkaterCorePhysicsComponent::GetFlagTime ( ESkaterFlag flag )
{
	return mp_state_component->m_skater_flags[flag].GetTime();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Tmr::Time CSkaterCorePhysicsComponent::GetFlagElapsedTime ( ESkaterFlag flag )
{
	return mp_state_component->m_skater_flags[flag].GetElapsedTime();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CSkaterCorePhysicsComponent::GetFlag ( ESkaterFlag flag )
{
	return mp_state_component->m_skater_flags[flag].Get(); 
} 								 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
								 
inline void CSkaterCorePhysicsComponent::SetFlag ( ESkaterFlag flag, bool state )
{
	mp_state_component->m_skater_flags[flag].Set(state);
	if (flag == RAIL_SLIDING)
	{
		mp_sound_component->SetIsRailSliding(state);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterCorePhysicsComponent::SetFlagTrue ( ESkaterFlag flag )
{
	SetFlag(flag, true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterCorePhysicsComponent::SetFlagFalse ( ESkaterFlag flag )
{
	SetFlag(flag, false);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterCorePhysicsComponent::ToggleFlag ( ESkaterFlag flag )
{
	SetFlag(flag, !GetFlag(flag));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
 								 
inline bool CSkaterCorePhysicsComponent::IsSwitched (   )
{
	return GetSkater()->m_isGoofy ? GetFlag(FLIPPED) : !GetFlag(FLIPPED);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
 								 
inline bool CSkaterCorePhysicsComponent::HaveLandedThisFrame (   )
{
	return m_landed_this_frame;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline ETerrainType CSkaterCorePhysicsComponent::GetLastGroundTerrain (   )
{
	return m_last_ground_feeler.GetTerrain();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint32 CSkaterCorePhysicsComponent::GetLastNodeChecksum (   )
{
	return m_last_ground_feeler.GetNodeChecksum();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CSkaterCorePhysicsComponent::GetTrueLandedFromVert (   )
{
	return m_true_landed_from_vert;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterCorePhysicsComponent::ResetSpecialFrictionIndex (   )
{
 	m_special_friction_index = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
 								 
inline ETerrainType CSkaterCorePhysicsComponent::GetTerrain (   )
{
	return mp_state_component->m_terrain;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline sint16	CSkaterCorePhysicsComponent::GetRailNode( void )
{
	if( mp_rail_node )
	{
		return mp_rail_node->GetNode();
	}
	else
	{
		return -1;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
 								 
inline void CSkaterCorePhysicsComponent::ResetGrindTweak (   )
{
	mGrindTweak = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
inline void CSkaterCorePhysicsComponent::ResetLerpingMatrix (   )
{
	m_lerping_display_matrix = GetObject()->m_matrix;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
 								 
inline void CSkaterCorePhysicsComponent::set_terrain ( ETerrainType terrain )
{
	mp_state_component->m_terrain = terrain;
	mp_sound_component->SetTerrain(terrain);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
 								 
inline void CSkaterCorePhysicsComponent::set_last_ground_feeler ( CFeeler& feeler )
{
	m_last_ground_feeler = feeler;
	mp_sound_component->SetLastTerrain(m_last_ground_feeler.GetTerrain());
}

}


#endif


================================================
FILE: Code/Sk/Components/SkaterEndRunComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterEndRunComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/28/3
//****************************************************************************

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#define	FLAGEXCEPTION(X) GetObject()->SelfEvent(X)

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent* CSkaterEndRunComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterEndRunComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterEndRunComponent::CSkaterEndRunComponent() : CBaseComponent()
{
	SetType( CRC_SKATERENDRUN );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterEndRunComponent::~CSkaterEndRunComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterEndRunComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterEndRunComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterEndRunComponent::Update()
{
	if (!Mdl::Skate::Instance()->IsMultiplayerGame())
	{
		Suspend(true);
		return;
	}
	
	if( static_cast< CSkater* >(GetObject())->IsLocalClient())
	{
		if (m_flags.Test(FINISHED_END_OF_RUN) && !GameNet::Manager::Instance()->HaveSentEndOfRunMessage())
		{
			GameNet::Manager::Instance()->SendEndOfRunMessage();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterEndRunComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | EndOfRunDone | 
		case CRCC(0x4c58771e, "EndOfRunDone"):
			m_flags.Set(FINISHED_END_OF_RUN);
			m_flags.Clear(IS_ENDING_RUN);
			break;
		
		case CRCC(0xb866bf9b, "RunStarted"):
			m_flags.Clear(STARTED_END_OF_RUN);
			m_flags.Clear( STARTED_GOAL_END_OF_RUN );
			m_flags.Clear(FINISHED_END_OF_RUN);
			m_flags.Clear( FINISHED_GOAL_END_OF_RUN );
			m_flags.Clear(IS_ENDING_RUN);
			m_flags.Clear(IS_ENDING_GOAL);
			break;

		case CRCC(0x962dea01, "EndOfRunStarted"):
			m_flags.Set(STARTED_END_OF_RUN);
			break;

		case CRCC(0xafc6a7,"Goal_EndOfRunStarted"):
			m_flags.Set(STARTED_GOAL_END_OF_RUN);
			break;
			
		// @script | Goal_EndOfRunDone | 
		case CRCC(0x69a9e37f, "Goal_EndOfRunDone"):
			m_flags.Set(FINISHED_GOAL_END_OF_RUN);
			m_flags.Clear(IS_ENDING_GOAL);
			break;
			
		case CRCC(0x95bdcfcd, "IsInEndOfRun"):
			return (m_flags.Test(STARTED_END_OF_RUN) && !m_flags.Test(FINISHED_END_OF_RUN)) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterEndRunComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterEndRunComponent::GetDebugInfo"));
	
	p_info->AddChecksum(CRCD(0x449233d0, "STARTED_END_OF_RUN"), m_flags.Test(STARTED_END_OF_RUN) ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	p_info->AddChecksum(CRCD(0x90259288, "FINISHED_END_OF_RUN"), m_flags.Test(FINISHED_END_OF_RUN) ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	p_info->AddChecksum(CRCD(0x0d2a8609, "IS_ENDING_RUN"), m_flags.Test(IS_ENDING_RUN) ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	p_info->AddChecksum(CRCD(0x80818017, "STARTED_GOAL_END_OF_RUN"), m_flags.Test(STARTED_GOAL_END_OF_RUN) ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	p_info->AddChecksum(CRCD(0x587092f3, "FINISHED_GOAL_END_OF_RUN"), m_flags.Test(FINISHED_GOAL_END_OF_RUN) ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	p_info->AddChecksum(CRCD(0xcc3b2295, "IS_ENDING_GOAL"), m_flags.Test(IS_ENDING_GOAL) ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterEndRunComponent::EndRun ( bool force_end )
{
	if (force_end || Mdl::Skate::Instance()->GetGameMode()->ShouldStopAtZero())
	{
		if (!m_flags.Test(IS_ENDING_RUN) && !m_flags.Test(FINISHED_END_OF_RUN))
		{
			//DumpUnwindStack( 20, 0 );
			m_flags.Set(IS_ENDING_RUN);
			if (static_cast< CSkater* >(GetObject())->IsLocalClient())
			{
				Script::RunScript(CRCD(0xf4ce3e97, "ForceEndOfRun"), NULL, GetObject());
			}
		}
	}
	else
	{
		//DumpUnwindStack( 20, 0 );
		FLAGEXCEPTION(CRCD(0x822e13a9, "RunHasEnded"));
		m_flags.Clear(FINISHED_END_OF_RUN);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterEndRunComponent::EndGoalRun ( bool force_end )
{
	m_flags.Clear(FINISHED_GOAL_END_OF_RUN);
	FLAGEXCEPTION( CRCD(0xab676175, "GoalHasEnded") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterEndRunComponent::RunHasEnded (   )
{
	return m_flags.Test(FINISHED_END_OF_RUN);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterEndRunComponent::GoalRunHasEnded (   )
{
	return m_flags.Test(FINISHED_GOAL_END_OF_RUN);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterEndRunComponent::StartedEndOfRun (   )
{
	return m_flags.Test(STARTED_END_OF_RUN);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterEndRunComponent::ClearStartedEndOfRunFlag (   )
{
	m_flags.Clear(STARTED_END_OF_RUN);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterEndRunComponent::StartedGoalEndOfRun (   )
{
	return m_flags.Test(STARTED_GOAL_END_OF_RUN);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterEndRunComponent::ClearStartedGoalEndOfRunFlag (   )
{
	m_flags.Clear(STARTED_GOAL_END_OF_RUN);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterEndRunComponent::IsEndingRun (   )
{
	return m_flags.Test(IS_ENDING_RUN);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterEndRunComponent::ClearIsEndingRun (   )
{
	m_flags.Clear(IS_ENDING_RUN);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Flags	CSkaterEndRunComponent::GetFlags( void )
{
	return m_flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSkaterEndRunComponent::SetFlags( Flags flags )
{
	m_flags = flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}


================================================
FILE: Code/Sk/Components/SkaterEndRunComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterEndRunComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/28/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERENDRUNCOMPONENT_H__
#define __COMPONENTS_SKATERENDRUNCOMPONENT_H__

#include 
#include 

#include 

#define		CRC_SKATERENDRUN CRCD(0xb1d5a3e1, "SkaterEndRun")

#define		GetSkaterEndRunComponent() ((Obj::CSkaterEndRunComponent*)GetComponent(CRC_SKATERENDRUN))
#define		GetSkaterEndRunComponentFromObject(pObj) ((Obj::CSkaterEndRunComponent*)(pObj)->GetComponent(CRC_SKATERENDRUN))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CSkaterEndRunComponent : public CBaseComponent
{
public:
    CSkaterEndRunComponent();
    virtual ~CSkaterEndRunComponent();
	
	enum EFlagType
	{
		STARTED_END_OF_RUN,
		FINISHED_END_OF_RUN,
		IS_ENDING_RUN,
		STARTED_GOAL_END_OF_RUN,
		FINISHED_GOAL_END_OF_RUN,
		IS_ENDING_GOAL
		// If adding more than 8 flags, tell Steve
	};

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							EndRun ( bool force_end = false );
	void							EndGoalRun ( bool force_end = false );
	bool							RunHasEnded (   );
	bool							GoalRunHasEnded (   );
	bool							StartedEndOfRun (   );
	void							ClearStartedEndOfRunFlag (   );
	bool							StartedGoalEndOfRun (   );
	void							ClearStartedGoalEndOfRunFlag (   );
	bool							IsEndingRun (   );
	void							ClearIsEndingRun (   );
	Flags						GetFlags( void );
	void							SetFlags( Flags flags );

	
private:
	Flags						m_flags;
	
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterFinalizePhysicsComponent.cpp
================================================
//****************************************************************************
//* MODULE:         sk/Components
//* FILENAME:       SkaterFinalizePhysicsComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/26/3
//****************************************************************************

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
				  
namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent* CSkaterFinalizePhysicsComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterFinalizePhysicsComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterFinalizePhysicsComponent::CSkaterFinalizePhysicsComponent() : CBaseComponent()
{
	SetType( CRC_SKATERFINALIZEPHYSICS );

	mp_core_physics_component = NULL;
	mp_state_component = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterFinalizePhysicsComponent::~CSkaterFinalizePhysicsComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFinalizePhysicsComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFinalizePhysicsComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFinalizePhysicsComponent::Finalize (   )
{
	mp_core_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
	mp_state_component = GetSkaterStateComponentFromObject(GetObject());
		
	Dbg_Assert(mp_core_physics_component);
	Dbg_Assert(mp_state_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFinalizePhysicsComponent::Update()
{
	// setup the state component
	
	// Logic for setting/not setting the flag for telling the camera whether to look down on the skater or not.
	if (mp_core_physics_component->m_began_frame_in_lip_state && mp_state_component->GetState() != LIP)
	{
		// This flag is sort of badly named now, it really means we changed from the lip state to something other than GROUND or LIP.
		mp_state_component->mJumpedOutOfLipTrick = false;
		if (mp_state_component->GetState() == AIR)
		{
			// we only want to set this flag if we jumped straight up; meaning the x and z velocities are close to zero
			if (Mth::Abs(GetObject()->m_vel[X]) < 1.0f && Mth::Abs(GetObject()->m_vel[Z]) < 1.0f)
			{
				mp_state_component->mJumpedOutOfLipTrick = true;
			}				
		}
	}
	
	// this flag needs clearing whenever we get out of the air
	if (mp_state_component->GetState() != AIR)
	{
		mp_state_component->mJumpedOutOfLipTrick = false;
	}
	
	mp_state_component->m_camera_display_normal = mp_core_physics_component->m_display_normal;
	mp_state_component->m_camera_current_normal = mp_core_physics_component->m_current_normal;
	
	// make sure the matrices don't get distorted
	GetObject()->m_matrix.OrthoNormalizeAbout(Y);
	mp_core_physics_component->m_lerping_display_matrix.OrthoNormalizeAbout(Y);
	
	// if any part of the matrix has collapsed, then we will neet to patch it up	
	// Note, this is a very rare occurence; probably only occurs when you hit things perfectly at right angles, so
	// you attempt to orthonormalize about an axis that that is now coincident with another axis
	// would not happen if we rotated the matrix, or used quaternions
	if (GetObject()->m_matrix.PatchOrthogonality())
	{
		mp_core_physics_component->ResetLerpingMatrix();
	}
	
	// Extract the informations from the physics object that we need for rendering
	GetObject()->SetDisplayMatrix(mp_core_physics_component->m_lerping_display_matrix);
	
	#ifdef __USER_DAN__
	// Gfx::AddDebugArrow(GetObject()->GetPos(), GetObject()->GetPos() + 60.0f * GetObject()->GetDisplayMatrix()[Z], RED, 0, 1);
	// Gfx::AddDebugArrow(GetObject()->GetPos(), GetObject()->GetPos() + 60.0f * GetObject()->GetDisplayMatrix()[X], BLUE, 0, 1);
	// Gfx::AddDebugArrow(GetObject()->GetPos(), GetObject()->GetPos() + 60.0f * GetObject()->GetDisplayMatrix()[Y], GREEN, 0, 1);
	#endif
	
	// update the sound components' state
	
	Obj::CSkaterSoundComponent *pSoundComponent = GetSkaterSoundComponentFromObject( GetObject() );
	Dbg_Assert( pSoundComponent );
	
	pSoundComponent->SetIsRailSliding( mp_state_component->GetFlag(RAIL_SLIDING) );
	pSoundComponent->SetTerrain( mp_state_component->GetTerrain() );
	
	Obj::CSkaterLoopingSoundComponent *pLoopingSoundComponent = GetSkaterLoopingSoundComponentFromObject( GetObject() );
	Dbg_Assert( pLoopingSoundComponent );
	
	float speed_fraction = sqrtf( GetObject()->GetVel()[X] * GetObject()->GetVel()[X] + GetObject()->GetVel()[Z] * GetObject()->GetVel()[Z] ) / GetSkater()->GetScriptedStat(CRCD(0xcc5f87aa, "Skater_Max_Max_Speed_Stat") );
	pLoopingSoundComponent->SetSpeedFraction( speed_fraction );
	pLoopingSoundComponent->SetIsBailing(mp_state_component->GetFlag(IS_BAILING));
	pLoopingSoundComponent->SetIsRailSliding( mp_state_component->GetFlag(RAIL_SLIDING) );
	pLoopingSoundComponent->SetTerrain( mp_state_component->GetTerrain() );
	pLoopingSoundComponent->SetState( mp_state_component->GetState() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterFinalizePhysicsComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFinalizePhysicsComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterFinalizePhysicsComponent::GetDebugInfo"));
	
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}
	
}


================================================
FILE: Code/Sk/Components/SkaterFinalizePhysicsComponent.h
================================================
//****************************************************************************
//* MODULE:         sk/Components
//* FILENAME:       SkaterFinalizePhysicsComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/26/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERFINALIZEPHYSICSCOMPONENT_H__
#define __COMPONENTS_SKATERFINALIZEPHYSICSCOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERFINALIZEPHYSICS CRCD(0x9b373e58, "SkaterFinalizePhysics")

#define		GetSkaterFinalizePhysicsComponent() ((Obj::CSkaterFinalizePhysicsComponent*)GetComponent(CRC_SKATERFINALIZEPHYSICS))
#define		GetSkaterFinalizePhysicsComponentFromObject(pObj) ((Obj::CSkaterFinalizePhysicsComponent*)(pObj)->GetComponent(CRC_SKATERFINALIZEPHYSICS))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSkaterStateComponent;

class CSkaterFinalizePhysicsComponent : public CBaseComponent
{
public:
    CSkaterFinalizePhysicsComponent();
    virtual ~CSkaterFinalizePhysicsComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
private:
	CSkaterCorePhysicsComponent* 	mp_core_physics_component;
	CSkaterStateComponent* 			mp_state_component;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterFlipAndRotateComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterFlipAndRotateComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/31/3
//****************************************************************************

/*
 * Encapsulates the dirty, dirty skater flip, skater rotate, and board rotate animation logic.
 *
 * "Please!" says CSkaterFlipAndRotateComponent, "Deprecate me, and end my torturous existence!"
 */

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{
	extern bool DebugSkaterScripts;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterFlipAndRotateComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterFlipAndRotateComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterFlipAndRotateComponent::CSkaterFlipAndRotateComponent() : CBaseComponent()
{
	SetType( CRC_SKATERFLIPANDROTATE );
	
	mp_animation_component = NULL;
	mp_model_component = NULL;
	
	m_rotate_board = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterFlipAndRotateComponent::~CSkaterFlipAndRotateComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFlipAndRotateComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFlipAndRotateComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFlipAndRotateComponent::Finalize (   )
{
	// Note: Non-local clients have a CSkaterFlipAndRotateComponent, but not a CSkaterCorePhysicsComponent.
	mp_core_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
	
	mp_model_component = GetModelComponentFromObject(GetObject());
   	mp_animation_component = GetAnimationComponentFromObject(GetObject());
	
	Dbg_Assert(mp_model_component);
	Dbg_Assert(mp_animation_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFlipAndRotateComponent::Update()
{
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterFlipAndRotateComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | Flip | flip animation
		case CRCC(0x65011baa, "Flip"):	
			ToggleFlipState();
			break;
			
        // @script | ResetSwitched | flip if we're switched
		case CRCC(0x72289a28, "ResetSwitched"):
			Dbg_Assert(mp_core_physics_component);
			if (mp_core_physics_component->IsSwitched())
			{
				ToggleFlipState();
			}
			break;
			
		// These three just set flags, which will cause the appropriate flip/rotate
		// when the next PlayAnim occurs. 
        // @script | FlipAfter | sets flag, which will cause the appropriate flip/rotate when the next PlayAnim occurs
		case CRCC(0xeba11b20, "FlipAfter"):
			mFlipAfter = true;
			break;
			
        // @script | RotateAfter | sets flag, which will cause the appropriate flip/rotate when the next PlayAnim occurs
		case CRCC(0xacac1bad, "RotateAfter"):
			mRotateAfter = true;
			break;
			
        // @script | BoardRotateAfter | sets flag, which will cause the appropriate flip/rotate when the next PlayAnim occurs
		case CRCC(0x63e93f69, "BoardRotateAfter"):
			mBoardRotateAfter = true;
			break;
			
		// @script | IsFlipAfterSet | returns mFlipAfter
		case CRCC(0xa6243daa, "IsFlipAfterSet"):
			return mFlipAfter ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
        // @script | FlipAndRotate | flip Z and X, to rotate 180 degrees about y
		case CRCC(0xd9de0c65, "FlipAndRotate"):
		{
			Dbg_Assert(mp_core_physics_component);
			mp_core_physics_component->ReverseFacing();
			ToggleFlipState();
			break;
		}
			
		// @script | BoardRotate | 
        // @flag normal | put the board back to normal (otherwise just flip)
		case CRCC(0xe0f3a644, "BoardRotate"):
		{
			GameNet::PlayerInfo* player = GameNet::Manager::Instance()->GetPlayerByObjectID(GetObject()->GetID());
			Dbg_Assert(player);
			
			Net::Client* client = GameNet::Manager::Instance()->GetClient(player->GetSkaterNumber());
			Dbg_Assert(client);

			if (pParams->ContainsFlag(CRCD(0xde7a971b, "Normal")))
			{
				// Put the board back to normal
				RotateSkateboard(GetObject()->GetID(), false, client->m_Timestamp, true);
			}
			else
			{
				// Otherwise flip it, flip it good.
				RotateSkateboard(GetObject()->GetID(), !m_rotate_board, client->m_Timestamp, true);
			}	
			break;
		}

		// @script | BoardIsRotated | 
		case CRCC(0x79ee5ccf, "BoardIsRotated"):
			return m_rotate_board ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		case CRCC(0xb1e7291, "PlayAnim"):
			// do any required flips and rotates, and then cascade the member function call
			DoAnyFlipRotateOrBoardRotateAfters();
			return CBaseComponent::MF_NOT_EXECUTED;
		
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFlipAndRotateComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterFlipAndRotateComponent::GetDebugInfo"));
	
	if (mp_core_physics_component)
	{
		p_info->AddChecksum(CRCD(0x8f66b80b, "switched"), mp_core_physics_component->IsSwitched() ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	}
	p_info->AddChecksum(CRCD(0xc7a712c, "flipped"), GetSkaterStateComponentFromObject(GetObject())->GetFlag(FLIPPED) ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	
	p_info->AddChecksum(CRCD(0xeba11b20, "FlipAfter"), mFlipAfter ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	p_info->AddChecksum(CRCD(0xacac1bad, "RotateAfter"), mRotateAfter ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	p_info->AddChecksum(CRCD(0x63e93f69, "BoardRotateAfter"), mBoardRotateAfter ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterFlipAndRotateComponent::RotateSkateboard ( uint32 objId, bool rotate, uint32 time, bool propagate )
{
	if ( propagate )
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		GameNet::PlayerInfo* player;

		player = gamenet_man->GetPlayerByObjectID( objId );
		if( player && player->IsLocalPlayer())
		{
			Net::Client* client;
			GameNet::MsgRotateSkateboard msg;
			Net::MsgDesc msg_desc;

			client = gamenet_man->GetClient( player->GetSkaterNumber());
			Dbg_Assert( client );

			msg.m_Rotate = rotate;
			msg.m_ObjId = objId;

			msg_desc.m_Data = &msg;
			msg_desc.m_Id = GameNet::MSG_ID_ROTATE_SKATEBOARD;
			msg_desc.m_Length = sizeof( GameNet::MsgRotateSkateboard );
			client->EnqueueMessageToServer(	&msg_desc );
		}
	}

	bool oldRotation = m_rotate_board;

	m_rotate_board = rotate;
	
	#ifdef __NOPT_ASSERT__
	if (DebugSkaterScripts && GetObject()->GetType() == SKATE_TYPE_SKATER)
	{
		printf("%d: Rotating board [rotated = %s]\n", (int) Tmr::GetRenderFrame(), m_rotate_board ? "true" : "false");
	}
	#endif

	return oldRotation;
}

/******************************************************************/

/*

If any of mFlipAfter, mRotateAfter or mBoardRotateAfter are set
this will flip or rotate the skater, or rotate the board, and then
reset those flags.

Quite often the skater needs to be flipped and/or rotated in order to
look correctly oriented, due to the way the animations are done.
For example, the nollie heelflip animation is actually animated backwards,
so to look correct the skater has to be flipped and rotated just for the
duration of that animation.
As soon as any sort of 'event' happens that could result in an animation
change, or requires that the orientation be correct for the physics, then
the skater needs to be flipped and rotated back.
So in the script, when running the nollie heelflip anim, Scott will flip and
rotate the skater first so that he looks correct, then set FlipAfter and RotateAfter.

It used to be that I'd check the flags only in the next call to the PlayAnim script 
member function.
However, this is often not early enough. For example, when the player lands whilst
doing a nollie heelflip, the land script will do some checks to see if the player 
is facing backwards before doing a playanim, so it will wrongly think the player
is backwards, when he isn't 'really'.

So instead I sprinkled calls to the following function whenever anything happens
that could cause a new script to be executed. In particular, before every SetScript,
and also as soon as a grind gets detected, so that the grind maths won't think the
skater is backwards when landing from a nollie heelflip.

*/

/******************************************************************/

void CSkaterFlipAndRotateComponent::DoAnyFlipRotateOrBoardRotateAfters (   )
{
	if (mFlipAfter)
	{
		ToggleFlipState();
		mFlipAfter = false;
	}
		
	if (mRotateAfter)
	{
		Dbg_Assert(mp_core_physics_component);
		mp_core_physics_component->ReverseFacing();
		mRotateAfter = false;
	}
		
	if (mBoardRotateAfter)
	{
		GameNet::PlayerInfo* player = GameNet::Manager::Instance()->GetPlayerByObjectID(GetObject()->GetID());
		Dbg_Assert(player);

		Net::Client* client = GameNet::Manager::Instance()->GetClient(player->GetSkaterNumber());
		Dbg_Assert(client);

		// Rotate the board.
		RotateSkateboard(GetObject()->GetID(), !m_rotate_board, client->m_Timestamp, true);
		mBoardRotateAfter = false;
	}	
	
	// This makes the skater display facing the correct way again.
	// The mFlipDisplayMatrix is only used when falling back in from a lip trick.
	// Since it would be bad if this flag got left on, make sure it is off whenever
	// a new anim runs. This function is a good place to put it, since it gets
	// run whenever the anim changes.
	mp_model_component->mFlipDisplayMatrix = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFlipAndRotateComponent::Reset (   )
{
	mFlipAfter = false;
	mRotateAfter = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFlipAndRotateComponent::ToggleFlipState (   )
{
	GetSkaterStateComponentFromObject(GetObject())->m_skater_flags[FLIPPED].Toggle();	
	
	#ifdef __NOPT_ASSERT__
	if (DebugSkaterScripts && GetObject()->GetType() == SKATE_TYPE_SKATER)
	{
		printf("%d: Flipping skater [flipped = %s]\n", (int) Tmr::GetRenderFrame(), mp_core_physics_component->GetFlag(FLIPPED) ? "true" : "false");
	}
	#endif
	
	// Flip the animation to the correct orientation Just setting a flag for Dave's code to handle when rendering
	ApplyFlipState();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFlipAndRotateComponent::ApplyFlipState (   )
{
	Net::Client* client = GameNet::Manager::Instance()->GetClient(GetSkater()->GetSkaterNumber());
	Dbg_Assert(client);
	
	mp_animation_component->FlipAnimation(GetObject()->GetID(), GetSkaterStateComponentFromObject(GetObject())->GetFlag(FLIPPED), client->m_Timestamp, true);
}

}


================================================
FILE: Code/Sk/Components/SkaterFlipAndRotateComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterFlipAndRotateComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/31/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERFLIPANDROTATECOMPONENT_H__
#define __COMPONENTS_SKATERFLIPANDROTATECOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERFLIPANDROTATE CRCD(0x488af07a, "SkaterFlipAndRotate")

#define		GetSkaterFlipAndRotateComponent() ((Obj::CSkaterFlipAndRotateComponent*)GetComponent(CRC_SKATERFLIPANDROTATE))
#define		GetSkaterFlipAndRotateComponentFromObject(pObj) ((Obj::CSkaterFlipAndRotateComponent*)(pObj)->GetComponent(CRC_SKATERFLIPANDROTATE))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CAnimationComponent;
	class CSkaterCorePhysicsComponent;
	class CModelComponent;

class CSkaterFlipAndRotateComponent : public CBaseComponent
{
public:
    CSkaterFlipAndRotateComponent();
    virtual ~CSkaterFlipAndRotateComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	   	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }

public:
	void							Reset (   );
	
	bool							RotateSkateboard ( uint32 objId, bool rotate, uint32 time, bool propagate );
	bool							IsBoardRotated (   ) { return m_rotate_board; }
	
	void							DoAnyFlipRotateOrBoardRotateAfters (   );
									  	
	void							ToggleFlipState (   );
	void							ApplyFlipState (   );

private:
	bool							m_rotate_board;
	
	bool							mFlipAfter;
	bool							mRotateAfter;
	bool							mBoardRotateAfter;
	
	// peer components
	CAnimationComponent*			mp_animation_component;
	CModelComponent*				mp_model_component;
	CSkaterCorePhysicsComponent*	mp_core_physics_component;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterFloatingNameComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterFloatingNameComponent.h
//* OWNER:			Dan
//* CREATION DATE:  3/13/3
//****************************************************************************

#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent* CSkaterFloatingNameComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterFloatingNameComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterFloatingNameComponent::CSkaterFloatingNameComponent() : CBaseComponent()
{
	SetType( CRC_SKATERFLOATINGNAME );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterFloatingNameComponent::~CSkaterFloatingNameComponent()
{
	Script::CStruct* pParams = pParams = new Script::CStruct;
	pParams->AddChecksum(CRCD(0x40c698af, "id"), m_screen_element_id);
	
	Script::RunScript(CRCD(0x2575b406, "destroy_object_label"), pParams);
	
	delete pParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFloatingNameComponent::InitFromStructure( Script::CStruct* pParams )
{
	Dbg_MsgAssert(GetObject()->GetType() == SKATE_TYPE_SKATER, ("CSkaterFloatingNameComponent added to non-skater composite object"));
	
	switch (GetObject()->GetID())
	{
		case 0:
			m_screen_element_id = CRCD(0xe797a186, "skater_name_0");
			break;
		case 1:
			m_screen_element_id = CRCD(0x90909110, "skater_name_1");
			break;
		case 2:
			m_screen_element_id = CRCD(0x999c0aa, "skater_name_2");
			break;
		case 3:
			m_screen_element_id = CRCD(0x7e9ef03c, "skater_name_3");
			break;
		case 4:
			m_screen_element_id = CRCD(0xe0fa659f, "skater_name_4");
			break;
		case 5:
			m_screen_element_id = CRCD(0x97fd5509, "skater_name_5");
			break;
		case 6:
			m_screen_element_id = CRCD(0xef404b3, "skater_name_6");
			break;
		case 7:
			m_screen_element_id = CRCD(0x79f33425, "skater_name_7");
			break;
		default:
			Dbg_MsgAssert(false, ("CSkaterFloatingNameComponent in CCompositeObject with ID of 8 or greater"));
			return;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFloatingNameComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFloatingNameComponent::Update()
{
	if (!GameNet::Manager::Instance()->InNetGame() || !GameNet::Manager::Instance()->ShouldDrawPlayerNames())
	{
		Suspend(true);
		return;
	}
	
	if (!GetSkater()->IsInWorld()) return;
	
	GameNet::PlayerInfo* player = GameNet::Manager::Instance()->GetPlayerByObjectID(GetObject()->GetID());
	
	float offset;
	if (GameNet::Manager::Instance()->GetCurrentlyObservedPlayer() == player)
	{
		offset = FEET(8.0f);
	}
	else
	{
		offset = FEET(10.0f);
	}

	Script::CStruct* pParams = new Script::CStruct;
	pParams->AddChecksum(CRCD(0x40c698af, "id"), m_screen_element_id);

	int color_index;
	if (Mdl::Skate::Instance()->GetGameMode()->IsTeamGame())
	{
		color_index = player->m_Team + 2;
	}
	else
	{
		color_index = GetObject()->GetID() + 2;
	}

	char text[64];
	sprintf(text, "\\c%d%s", color_index, GetSkater()->GetDisplayName());
	pParams->AddString(CRCD(0xc4745838, "text"), text);
	pParams->AddVector(CRCD(0x4b491900, "pos3D"), GetObject()->m_pos[X], GetObject()->m_pos[Y] + offset, GetObject()->m_pos[Z]);

	Front::CScreenElement *p_name_elem = Front::CScreenElementManager::Instance()->GetElement(m_screen_element_id);
	if (p_name_elem)
	{
		p_name_elem->SetProperties(pParams);
	}
	else
	{
		Script::RunScript(CRCD(0x6a060cf0, "create_object_label"), pParams);
	}
	
	delete pParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterFloatingNameComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterFloatingNameComponent::GetDebugInfo ( Script::CStruct *p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterFloatingNameComponent::GetDebugInfo"));
	
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}
	
}


================================================
FILE: Code/Sk/Components/SkaterFloatingNameComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterFloatingNameComponent.h
//* OWNER:			Dan
//* CREATION DATE:  3/13/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERFLOATINGNAMECOMPONENT_H__
#define __COMPONENTS_SKATERFLOATINGNAMECOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERFLOATINGNAME CRCD(0x125044e2, "SkaterFloatingName")

#define		GetSkaterFloatingNameComponent() ((Obj::CSkaterFloatingNameComponent*)GetComponent(CRC_SKATERFLOATINGNAME))
#define		GetSkaterFloatingNameComponentFromObject(pObj) ((Obj::CSkaterFloatingNameComponent*)(pObj)->GetComponent(CRC_SKATERFLOATINGNAME))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CSkaterFloatingNameComponent : public CBaseComponent
{
public:
    CSkaterFloatingNameComponent();
    virtual ~CSkaterFloatingNameComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
private:
	uint32							m_screen_element_id;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterGapComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterGapComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/5/3
//****************************************************************************

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 							// for event broadcasting on sucess of a gap

#include 
#include 
#include 
#include 
#include 

#include 

// #define DEBUG_GAPS

namespace Obj
{
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterGapComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterGapComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterGapComponent::CSkaterGapComponent() : CBaseComponent()
{
	SetType( CRC_SKATERGAP );
	
	mp_core_physics_component = NULL;
	mp_balance_trick_component = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterGapComponent::~CSkaterGapComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::InitFromStructure( Script::CStruct* pParams )
{
	Dbg_MsgAssert(GetObject()->GetType() == SKATE_TYPE_SKATER, ("CSkaterGapComponent added to non-skater composite object"));
	
	m_frame_count = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::Finalize (   )
{
	mp_core_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
	mp_balance_trick_component = GetSkaterBalanceTrickComponentFromObject(GetObject());
	mp_physics_control_component = GetSkaterPhysicsControlComponentFromObject(GetObject());
	mp_walk_component = GetWalkComponentFromObject(GetObject());
	
	Dbg_Assert(mp_core_physics_component);
	Dbg_Assert(mp_balance_trick_component);
	Dbg_Assert(mp_physics_control_component);
	Dbg_Assert(mp_walk_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::Update()
{
	m_frame_count++;
	
	// setup flags based on skater state
	int cancel, require;
	get_state_flags(cancel, require);
	
	UpdateCancelRequire(cancel, require);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Split out the actual updating from Update(), so we co do specific updates
// of the require/cancel flags at arbitart times
// specifically we want to update the "Lip" requirements as soon as we land on them
// and do it in isolation, so nothing else is messed up

void CSkaterGapComponent::UpdateCancelRequire(int cancel, int require)
{
	CGap* pGap = static_cast< CGap* >(m_gap_list.FirstItem());
	
	// don't bother determining the state flags if there are no active gaps
	if (!pGap) return;
	
	// update the active gaps based on the flags
	while (pGap)
	{
		CGap* pNext = static_cast< CGap* >(pGap->GetNext());
		
		// Clear any require flags that match the require mask, so if we do all that is required, then the require part of the flags field will be cleared
		pGap->m_flags &= ~require;
	
		// if any cancel flags match the cancel mask, cancel the gap
		if (pGap->m_flags & cancel)
		{
//			printf ("Cancelled by %x\n",pGap->m_flags & cancel); 
			if (pGap->m_trickscript)
			{
				// delete the gap trick
				CGapTrick *pGapTrick = static_cast< CGapTrick* >(m_gaptrick_list.FirstItem());
				while (pGapTrick)
				{
					CGapTrick * pNext = static_cast< CGapTrick* >(pGapTrick->GetNext());
	
					// only delete gaptricks if you've not been awarded them, and they exactly match the gap we're cancelling
					if (!pGapTrick->m_got
						&& pGapTrick->m_id == pGap->m_id
						&& pGapTrick->m_script == pGap->m_trickscript
						&& pGapTrick->m_node == pGap->m_node)
					{
						delete pGapTrick;
						break;
					}
	
					pGapTrick = pNext;
				}
			}
			
			#ifdef DEBUG_GAPS
			MESSAGE("DELETING GAP");
			DUMPC(pGap->m_id);
			#endif
	
			// delete the gap
			delete pGap;
		}
		pGap = pNext;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterGapComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | StartGap | Start a gap...used in conjunction with EndGap. 
        // See online doc for detailed description and examples
        // @parmopt name | GapID | Links | the name of the gap
        // @parmopt array | flags | | array of flags to use
		// @parmopt name | trickscript | | Script that is run when you land a combo that includes this gap
		// subject to the two optional conditions below
		// @parmopt checksum | KeyCombo | | Name of a key combo, like Air_SquareD, you have to do trick with this button combo
		// @parmopt string | TrickText | | This text substring, like 'kickflip' has to be in the combo
		// @nextparm | flags | flags | file | gapflags.lst | done
		// @nextparm | trickscript | script
		// @nextparm | KeyCombo | list | file | keycombo.lst | done
		// @parmopt checksum | combined_flags | | An optional way of specifying the cancel flags as a uint32 consisting
		// of the flag bit values or'd together. Currently only used when the Park Editor calls StartGap from within
		// the c-code.
		case CRCC(0x65b9ec39, "StartGap"):
			start_gap(pParams, pScript);
			break;
		
        // @script | StartGapTrick | This adds a gap trick to the skaters
        // current list
        // @parm string | TrickText | text to look for in the trick text 
        // @parm name | gapscript | script to run when you land this gap trick
		// @parmopt checksum | KeyCombo | | Name of a key combo, like Air_SquareD, you have to do trick with this button combo
		case CRCC(0xfc4f2009, "StartGapTrick"):
			start_gap_trick(pParams, pScript);
			break;
			
        // @script | EndGap | end a gap - used with StartGap 
        // See online doc for detailed description and examples
        // @parm name | GapID | gap gap_id
        // @parmopt string | text | "Unnamed Gap" | text to display
        // @parmopt int | score | | score to award for this gap
        // @flag NetEnabled | 
        // @flag Permanent | 
        // @parmopt structure | continue | | for linking gaps 
        // e.g.  continue = { GapID = SecondHalf Cancel = CANCEL_GROUND }
		// @nextparm | score | slidernum | 0 | 999999
		case CRCC(0xe5399fb2, "EndGap"):
			end_gap(pParams, pScript);
			break;
		
		// @script | CheckGapTricks | Check to see if there are any gap tricks,
		// then check to see if we did the trick yet
		// if so, then fire them off
		// note, this does not delete gaps that you did not do
		// so you would ahve to call ClearGapTricks
		// in that case
        case CRCC(0x42f80e5e, "CheckGapTricks"):
			check_gap_tricks(pParams);
			break;
		
        // @script | ClearGapTricks | clears all the gap tricks.
        // Currently, I call CheckGapTricks then ClearGapTricks
        // at any point in the skater's logic where he can land
        // a trick combo. So if you just stick a "StartGapTrick"
        // in your gap script, then it will automatically work
		case CRCC(0x196772b, "ClearGapTricks"):
			clear_gap_tricks(pParams);
			break;
		
		// @script | DuplicateTrigger | quick way of doing two way gaps 
        // see online doc for detailed description and examples
		case CRCC(0x4562e4a6, "DuplicateTrigger"):
		{
			// used in a trigger script
			// find the first node linked to this one that has
			// a trigger script, and execute that instead
			{
				int node = pScript->mNode;
				Dbg_MsgAssert(node != -1,("DuplicateTrigger in script with no node,\n%s\n",pScript->GetScriptInfo()));
				if (node != -1)	 		// Failsafe
				{
					int links = SkateScript::GetNumLinks(node);
					//dodgy_test(); printf("node %d has %d links\n",node,links);
					
					Dbg_MsgAssert(links>0,("DuplicateTrigger in node (%d) with no links,\n%s\n",node,pScript->GetScriptInfo()));
					int i;
					for (i=0;iGetChecksum(CRCD(0x2ca8a299, "TriggerScript"),&script))
						{
							// found another trigger script in the node we are linked to
							GetObject()->SpawnAndRunScript(script,pScript->mNode);
							break;		
						}						
					}
					Dbg_MsgAssert(iGetScriptInfo()));					
				}
			}
			break;
		}

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterGapComponent::GetDebugInfo"));
	
	static const uint32 p_gap_flag_checksums [   ]
	= {
		CRCD(0x9a27e74d, "CANCEL_GROUND"),				// 0x00000001
		CRCD(0xc6362354, "CANCEL_AIR"),              	// 0x00000002
		CRCD(0xcd4decee, "CANCEL_RAIL"),             	// 0x00000004
		CRCD(0x87e4e899, "CANCEL_WALL"),             	// 0x00000008
		CRCD(0x20e0d12b, "CANCEL_LIP"),              	// 0x00000010
		CRCD(0x78d5a029, "CANCEL_WALLPLANT"),			// 0x00000020
		CRCD(0x2d03dae1, "CANCEL_MANUAL"),				// 0x00000040
		CRCD(0x2a7a145a, "CANCEL_HANG"),     	    	// 0x00000080
		CRCD(0x0a65d800, "CANCEL_LADDER"),	         	// 0x00000100
		CRCD(0xa4721771, "CANCEL_SKATE"),				// 0x00000200
		CRCD(0x19807d3a, "CANCEL_WALK"),   				// 0x00000400
		CRCD(0x678677cc, "CANCEL_DRIVE"),				// 0x00000800
		CRCD(0xc6aa6589, "NO_SUCH_GAP_FLAG"),			// 0x00001000
		CRCD(0xc6aa6589, "NO_SUCH_GAP_FLAG"),			// 0x00002000
		CRCD(0xc6aa6589, "NO_SUCH_GAP_FLAG"),			// 0x00004000
		CRCD(0xc6aa6589, "NO_SUCH_GAP_FLAG"),			// 0x00008000
		CRCD(0xae0f7a14, "REQUIRE_GROUND"),				// 0x00010000
		CRCD(0xdfc901ab, "REQUIRE_AIR"),             	// 0x00020000
		CRCD(0xe056fc41, "REQUIRE_RAIL"),            	// 0x00040000
		CRCD(0xaafff836, "REQUIRE_WALL"),            	// 0x00080000
		CRCD(0x391ff3d4, "REQUIRE_LIP"),             	// 0x00100000
		CRCD(0xeae2e98f, "REQUIRE_WALLPLANT"),			// 0x00200000
		CRCD(0x192b47b8, "REQUIRE_MANUAL"),				// 0x00400000
		CRCD(0x076104f5, "REQUIRE_HANG"),	        	// 0x00800000
		CRCD(0x3e4d4559, "REQUIRE_LADDER"),	        	// 0x01000000
		CRCD(0xe236b218, "REQUIRE_SKATE"),				// 0x02000000
		CRCD(0x349b6d95, "REQUIRE_WALK"),				// 0x04000000
		CRCD(0x21c2d2a5, "REQUIRE_DRIVE"),				// 0x08000000
		CRCD(0xc6aa6589, "NO_SUCH_GAP_FLAG"),			// 0x10000000
		CRCD(0xc6aa6589, "NO_SUCH_GAP_FLAG"),			// 0x20000000
		CRCD(0xc6aa6589, "NO_SUCH_GAP_FLAG"),			// 0x40000000
		CRCD(0xc6aa6589, "NO_SUCH_GAP_FLAG")			// 0x80000000
	};
	
	int gap_count = 0;
	CGap* pGap = static_cast< CGap* >(m_gap_list.FirstItem());
	while (pGap)
	{
		gap_count++;
		pGap = static_cast< CGap* >(pGap->GetNext());
	}
	
	Script::CArray* p_gaps_array = new Script::CArray;
	p_gaps_array->SetSizeAndType(gap_count, ESYMBOLTYPE_STRUCTURE);
	
	int i = 0;
	pGap = static_cast< CGap* >(m_gap_list.FirstItem());
	while (pGap)
	{
		Script::CStruct* p_gap_struct = new Script::CStruct;
		p_gap_struct->AddChecksum(CRCD(0x40c698af, "Id"), pGap->m_id);
		
		Script::CStruct* p_gap_flags = new Script::CStruct;
		for (int n = 0; n < 32; n++)
		{
			uint32 mask = 1 << n;
			if (mask & pGap->m_flags)
			{
				p_gap_flags->AddChecksum(NO_NAME, p_gap_flag_checksums[n]);
			}
		}
		p_gap_struct->AddStructurePointer(CRCD(0xf4fabe45, "Flags"), p_gap_flags);
		
		p_gaps_array->SetStructure(i, p_gap_struct);
		i++;
		pGap = static_cast< CGap* >(pGap->GetNext());
	}
	p_info->AddArrayPointer(CRCD(0xd76c173e, "Gaps"), p_gaps_array);

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkaterGapComponent::ClearActiveGaps (   )
{
	m_gap_list.DestroyAllNodes();
	m_gaptrick_list.DestroyAllNodes();
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::ClearPendingGaps (   )
{
	Mdl::Skate::Instance()->GetGapChecklist()->ClearPendingGaps();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkaterGapComponent::AwardPendingGaps (   )
{
	Mdl::Skate::Instance()->GetGapChecklist()->AwardPendingGaps();
	
	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();
	if (!pCareer->GetGlobalFlag(405/*GOT_ALL_GAPS*/) && pCareer->GotAllGaps())
	{
		Script::SpawnScript(CRCD(0xcc74cc2e, "got_all_gaps_screen_create"));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CSkaterGapComponent::get_state_flags ( int& cancel, int& require )
{
	cancel = require = 0;
	
	if (!mp_physics_control_component->IsDriving())
	{
		if (mp_physics_control_component->IsSkating())
		{
			// skating
			
			cancel |= CANCEL_SKATE;
			require |= REQUIRE_SKATE;
			
			switch (mp_core_physics_component->GetState())
			{
				case AIR:
					cancel |= CANCEL_AIR;
					require |= REQUIRE_AIR;
					break;
					
				case GROUND:
					if (!mp_core_physics_component->GetFlag(OVERRIDE_CANCEL_GROUND)
						&& !mp_balance_trick_component->GetBalanceTrickType()
						&& !mp_balance_trick_component->DoingBalanceTrick())
						// && !mp_core_physics_component->HaveLandedThisFrame())
					{
						cancel |= CANCEL_GROUND;
						require |= REQUIRE_GROUND; 		
					}
					if (mp_balance_trick_component->GetBalanceTrickType() || mp_balance_trick_component->DoingBalanceTrick())
					{
						cancel |= CANCEL_MANUAL;
						require |= REQUIRE_MANUAL;
					}
					break;
		
				case RAIL:
					cancel |= CANCEL_RAIL;
					require |= REQUIRE_RAIL;
					break;
			
				case WALL:
					cancel |= CANCEL_WALL;
					require |= REQUIRE_WALL; 		
					break;
					
				case LIP:
					cancel |= CANCEL_LIP;
					require |= REQUIRE_LIP; 		
					break;
					
				case WALLPLANT:
					cancel |= CANCEL_WALLPLANT;
					require |= REQUIRE_WALLPLANT; 		
					break;
			}
		}
		else
		{
			// walking
			
			cancel |= CANCEL_WALK;
			require |= REQUIRE_WALK;
			
			switch (mp_walk_component->GetState())
			{
				case CWalkComponent::WALKING_AIR:
					cancel |= CANCEL_AIR;
					require |= REQUIRE_AIR;
					break;
					
				case CWalkComponent::WALKING_GROUND:
					cancel |= CANCEL_GROUND;
					require |= REQUIRE_GROUND;
					break;
					
				case CWalkComponent::WALKING_HANG:
					cancel |= CANCEL_HANG;
					require |= REQUIRE_HANG;
					break;
					
				case CWalkComponent::WALKING_LADDER:
					cancel |= CANCEL_LADDER;
					require |= REQUIRE_LADDER;
					break;
					
				default:
					break;
			}
		}
	}
	else
	{
		// driving
		
		cancel |= CANCEL_DRIVE;
		require |= REQUIRE_DRIVE;
		
		// driving gaps are a bit of a kludge right now
		
		CCompositeObject* p_vehicle = static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0x824c6a24, "PlayerVehicle")));
		Dbg_Assert(p_vehicle);
		CVehicleComponent* p_vehicle_component = GetVehicleComponentFromObject(p_vehicle);
		Dbg_Assert(p_vehicle_component);
		
		if (p_vehicle_component->IsOnGround())
		{
			cancel |= CANCEL_GROUND;
			require |= REQUIRE_GROUND;
		}
		else
		{
			cancel |= CANCEL_AIR;
			require |= REQUIRE_AIR;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::start_gap ( Script::CStruct *pParams, Script::CScript* pScript )
{
	if (mp_core_physics_component->GetFlag(IS_BAILING)) return;
						  
	uint32 gap_id = 0;
	pParams->GetChecksum(CRCD(0x3b442e26, "GapID"), &gap_id);
	if (!gap_id)
	{
		// no gap_id is assumed to indicate a "Links" gap
		Dbg_MsgAssert(pScript->mNode != -1, ("\n%s\nLink gap with no node", pScript->GetScriptInfo()));
		Dbg_MsgAssert(SkateScript::GetNumLinks(pScript->mNode), ("\n%s\nLink gap with no links", pScript->GetScriptInfo()));
	}
	
	#ifdef DEBUG_GAPS
	MESSAGE("STARTING GAP");
	DUMPC(gap_id);
	#endif
	
	// check to see if there are any other gaps from this node with this ID and delete them
	// (note that this means you can only have one "Links" gap from a single node although there can be many targets)
	CGap* pGap = static_cast< CGap* >(m_gap_list.FirstItem());
	while (pGap)
	{
		CGap* pNext = static_cast< CGap* >(pGap->GetNext());

		if (pGap->m_id == gap_id && static_cast< int >(pGap->m_node) == pScript->mNode)
		{
			delete pGap;
		}
		pGap = pNext;
	}

	// create the gap and add the info to it
	pGap = new CGap;
	m_gap_list.AddToHead(pGap);
	pGap->m_id = gap_id;
	pGap->m_node = pScript->mNode;
	pGap->m_trickTextChecksum = 0;
	
	// Take a snapshot of the skater's position & orientation so that if the gap is successfully
	// got this info can be recorded into the CGapCheck instance and used to determine a good
	// camera position for viewing the gap in the view-gaps menu.
	pGap->m_skater_start_pos=((CSkater*)GetObject())->GetCamera()->GetPos();
	pGap->m_skater_start_dir=-((CSkater*)GetObject())->GetCamera()->GetMatrix()[Mth::AT];

	// if it has a trickscript, it's a gaptrick
	uint32 trickscript;
	if (pParams->GetChecksum(CRCD(0xa26994e6, "trickscript"), &trickscript))
	{
		uint32 key_combo;
		const char* tricktext;
		Script::CArray* p_gapTricks;
		
		pGap->m_trickscript = trickscript;
		
		Dbg_Assert(mp_score);

		if (pParams->GetChecksum(CRCD(0x95e16467,"KeyCombo"), &key_combo))
		{
			pGap->m_trickChecksum = key_combo;

			int spin;
			if ( pParams->GetInteger(CRCD(0xedf5db70, "spin"), &spin))
			{
				pGap->m_requirePerfect = pParams->ContainsFlag(CRCD(0x1c39f1b9, "perfect"));
				Dbg_MsgAssert(spin % 180 == 0, ("StartGap called with a spin value of %i which is not a multiple of 180", spin));
				pGap->m_spinMult = spin / 180;
			}
			pParams->GetInteger( CRCD(0xa4bee6a1,"num_taps"), &pGap->m_numTaps, Script::NO_ASSERT );

			pGap->m_numTrickOccurrences = mp_score->GetPreviousNumberOfOccurrences( key_combo, pGap->m_spinMult, pGap->m_numTaps );
		}
		else if (pParams->GetString(CRCD(0x3eafa520, "TrickText"), &tricktext))
		{
			pGap->m_trickTextChecksum = Script::GenerateCRC(tricktext);

			int spin;
			if (pParams->GetInteger(CRCD(0xedf5db70, "spin"), &spin))
			{
				pGap->m_requirePerfect = pParams->ContainsFlag(CRCD(0x1c39f1b9, "perfect"));
				Dbg_MsgAssert(spin % 180 == 0, ("StartGap called with a spin value of %i which is not a multiple of 180", spin));
				pGap->m_spinMult = spin / 180;
			}
			
			pParams->GetInteger( CRCD(0xa4bee6a1,"num_taps"), &pGap->m_numTaps, Script::NO_ASSERT );

			pGap->m_numTrickOccurrences = mp_score->GetPreviousNumberOfOccurrencesByName( pGap->m_trickTextChecksum, pGap->m_spinMult, pGap->m_numTaps );
		}
		else if (pParams->GetArray(CRCD(0x1e26fd3e, "tricks"), &p_gapTricks))
		{
			Script::CopyArray(pGap->mp_tricks, p_gapTricks);
			pGap->m_startGapTrickCount = mp_score->GetCurrentTrickCount();
		}
		
		// create the gap trick object
		start_gap_trick(pParams, pScript);
	}

	
	pGap->m_flags = 0;
	Script::CArray* pArray = NULL;			
	pParams->GetArray(CRCD(0xf4fabe45, "flags"), &pArray);
	if (pArray)
	{
		for (uint32 i = 0; i < pArray->GetSize(); i++)
		{
			int checksum = pArray->GetChecksum(i);
			pGap->m_flags |= Script::GetInteger(checksum);	
		}
	}
	else
	{
		uint32 checksum;
		if (pParams->GetChecksum(CRCD(0xf4fabe45, "flags"), &checksum, false))
		{
			pGap->m_flags |= Script::GetInteger(checksum);
		}
	}

	// K: Added this as part of implementing ability to set cancel type for gaps in the Park Editor. (TT1969)
	// When the park editor runs the StartGap command, it generates the passed parameters in the C-code.
	// At that point, the PE already has the or'd together cancel flags. It would be tricky to try to construct
	// the array of flag checksums at that point, since it would have to do lots of compares with script globals
	// and if any new ones were added we'd have to remember to update that bit of code.
	// Easier to just support passing of the combined flags here.
	uint32 combined_flags=0;
	if (pParams->GetChecksum(CRCD(0x2760de9e,"combined_flags"),&combined_flags))
	{
		pGap->m_flags |= combined_flags;
	}
	
	uint32 car_cancel_flags=CANCEL_SKATE | CANCEL_WALK;
	
	if (pParams->ContainsFlag(CRCD(0xc442e33d, "carGap")))
	{
		// car gaps cancel when skating or walking
		pGap->m_flags |= car_cancel_flags;
	}
	else if (pParams->ContainsFlag(CRCD(0xe3ed4ade, "complexGap")))
	{
		// complex gaps must be setup completely by their designer
	}
	else
	{
		// default to gaps which cancel driving
		if (pGap->m_flags & car_cancel_flags)
		{
			// Unless we decided we did want to drive earlier
		}
		else
		{
			pGap->m_flags |= CANCEL_DRIVE;
		}	
	}
	
	// Because the car can trigger multiple trigger scripts in the same frame, we must allow the gap to be canceled immediately.  Otherwise, if one wheel
	// triggers a start gap and another an end gap in the same frame, the cancel flags of the gap will never be checked.
	if (mp_physics_control_component->IsDriving() && (pGap->m_flags & CANCEL_DRIVE)
		// Created park gaps can be so close together that we might not have a frame with which to check our cancel flags
		|| Ed::CParkEditor::Instance()->UsingCustomPark())
	{
		int cancel, require;
		get_state_flags(cancel, require);
		UpdateCancelRequire(cancel, require);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::start_gap_trick ( Script::CStruct *pParams, Script::CScript* pScript )
{
	// create the gap trick and add the info to it
	CGapTrick* pGapTrick = new CGapTrick;
	m_gaptrick_list.AddToHead(pGapTrick);
	pGapTrick->m_node = pScript->mNode;
	pGapTrick->m_got = false;
	pGapTrick->m_frame = m_frame_count;

	uint32 gap_id = 0;
	pParams->GetChecksum(CRCD(0x3b442e26, "GapId"), &gap_id);
	pGapTrick->m_id = gap_id;

	const char *p_trickString = NULL;
	pParams->GetString(CRCD(0x3eafa520, "TrickText"), &p_trickString);
	pGapTrick->m_trickString = p_trickString;
	
	pGapTrick->m_script = 0;
	pParams->GetChecksum(CRCD(0xa26994e6, "trickscript"), &pGapTrick->m_script, Script::ASSERT);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::end_gap ( Script::CStruct *pParams, Script::CScript* pScript )
{
	if (mp_core_physics_component->GetFlag(IS_BAILING)) return;
	
	uint32 gap_id = 0;
	pParams->GetChecksum(CRCD(0x3b442e26, "GapID"), &gap_id);
	
	#ifdef DEBUG_GAPS
	MESSAGE("ENDING GAP");
	DUMPC(gap_id);
	#endif
	
	Dbg_Assert(mp_score);

	// find the ending gap in our gap list
	CGap* pNext;
	for (CGap* pGap = static_cast< CGap* >(m_gap_list.FirstItem()); pGap; pGap = pNext)
	{
		pNext = static_cast< CGap* >(pGap->GetNext());
		
		if (pGap->m_id != gap_id) continue;
		
		// check if its got a gap_id, or if the gaps node is linked to this node					
		if (gap_id == 0 && !SkateScript::IsLinkedTo(pGap->m_node, pScript->mNode)) continue;
				
		// found the gap
		
		// check requirements
		if (pGap->m_flags & REQUIRE_MASK)
		{							 
			// requirements failed, so just delete the gap
			delete pGap;
			continue;
		}
		
		// requirements met

		// Get the gap name, this is what is displayed as the trick name for this gap.
		// This is not required, and will default if not there.
		const char *p_name;
		if (!pParams->GetString(CRCD(0xc4745838, "text"), &p_name))
		{
			p_name = "Unnamed Gap";
		}
		char gap_name[256];
		sprintf(gap_name, "\\c1%s\\c0", p_name);


		// check for gap trick; this MUST occur before the gap is added to the trick string
		if (pGap->m_trickscript != 0)
		{
			bool gotGapTrick = false;
			
			if (pGap->m_trickChecksum != 0 || pGap->m_trickTextChecksum != 0 || pGap->mp_tricks->GetSize() > 0)
			{
				// see if the trick has been done since the gap started
				if ( pGap->m_trickChecksum && pGap->m_numTrickOccurrences < mp_score->GetCurrentNumberOfOccurrences( pGap->m_trickChecksum, pGap->m_spinMult, pGap->m_requirePerfect, pGap->m_numTaps ) )
				{
					gotGapTrick = true;
				}
				else if ( pGap->m_trickTextChecksum && pGap->m_numTrickOccurrences < mp_score->GetCurrentNumberOfOccurrencesByName( pGap->m_trickTextChecksum, pGap->m_spinMult, pGap->m_requirePerfect, pGap->m_numTaps ) )
				{
					gotGapTrick = true;
				}
				else if ( pGap->mp_tricks->GetSize() > 0 && mp_score->GetCurrentNumberOfOccurrences( pGap->mp_tricks, pGap->m_startGapTrickCount ) > 0 )
				{
					gotGapTrick = true;
				}

				// if they get the gap trick
				if ( gotGapTrick )
				{
					// get the gap trick
					CGapTrick* pGapTrick = static_cast< CGapTrick* >(m_gaptrick_list.FirstItem());
					while ( pGapTrick )
					{
						CGapTrick* pNext = static_cast< CGapTrick* >(pGapTrick->GetNext());
						
						if (pGapTrick->m_id == pGap->m_id)
						{
							pGapTrick->GetGapTrick();
							break;
						}

						pGapTrick = pNext;
					}
				} // END if they get the gap trick
			}
			else 
			{
				// if no trick script, look for a no requirement gap trick
				CGapTrick* pGapTrick = static_cast< CGapTrick* >(m_gaptrick_list.FirstItem());
				while (pGapTrick)
				{
					CGapTrick* pNext = static_cast< CGapTrick* >(pGapTrick->GetNext());

					if (pGapTrick->m_id == pGap->m_id)
					{
						pGapTrick->GetGapTrick();
						break;
					}
					pGapTrick = pNext;
				}
			}
		} // END if trickscript != 0


		Mdl::Skate::Instance()->GetGapChecklist()->SetInfoForCamera(p_name,pGap->m_skater_start_pos,pGap->m_skater_start_dir);
		
		// Get the score for this gap.
		// If there is no score defined, then we don't award a trick.
		int score = 0;
		if (pParams->GetInteger(CRCD(0xcd66c8ae, "score"), &score))
		{
			Mdl::Score::Flags flags = Mdl::Score::vGAP;
			if (GetSkater()->IsLocalClient())
			{
				mp_score->Trigger(gap_name, score, flags);
				
				CTrickComponent* p_trick_component = GetTrickComponentFromObject(GetObject());
				Dbg_Assert(p_trick_component);
				p_trick_component->SetFirstTrickStarted(true);

				Mdl::Skate::Instance()->GetGapChecklist()->GetGapByText(p_name);  
				  
				GetObject()->SpawnAndRunScript(CRCD(0x541a2485, "DefaultGapScript"));
			}
		}
		
		//  execute the gap_script
		uint32 gap_script;
		if (pParams->GetChecksum(CRCD(0x7c9e51ab, "gapscript"), &gap_script))
		{
			GetObject()->SpawnAndRunScript(
				gap_script,
				pScript->mNode,
				pParams->ContainsFlag(CRCD(0x20209c31, "NetEnabled")),
				pParams->ContainsFlag(CRCD(0x23627fd7, "Permanent"))
			);
		}	  			  
		
		// handle for continue script parameter
		Script::CStruct * p_continue = NULL;
		if (pParams->GetStructure(CRCD(0xec1cd520, "continue"), &p_continue))
		{
			start_gap(p_continue, pScript);
		}										  

		// Broadcast an event saying we got the gap
		// won't happen very often, and broadcasing is cheap)
		// we also pass in the parameters, just in case we want to do 
		uint32	id_got = Crc::ExtendCRCWithString(gap_id,"_Success");
		Obj::CTracker::Instance()->LaunchEvent( id_got, 0xffffffff, GetObject()->GetID(), pParams, true /*, radius */  );
						
		// Script functions we might have called could have deleted this gap and maybe the next gap.
		// The safest things is to check to see if this one is still there, delete it, then reset back to the start of the list.
		CGap* pKill = static_cast< CGap* >(m_gap_list.FirstItem());
		while (pKill)
		{
			if (pKill == pGap)
			{
				delete pGap;
				break;
			}
			pKill = static_cast< CGap* >(pKill->GetNext());
		}
		
		// restart our pass through the gaps
		pNext = static_cast< CGap* >(m_gap_list.FirstItem());
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::check_gap_tricks ( Script::CStruct *pParams )
{
	// look for completed gaps and run their scripts
	CGapTrick* pGapTrick = static_cast< CGapTrick* >(m_gaptrick_list.FirstItem());
	while (pGapTrick)
	{
		CGapTrick* pNext = static_cast< CGapTrick* >(pGapTrick->GetNext());
		if (pGapTrick->m_got)
		{
			GetObject()->SpawnAndRunScript(pGapTrick->m_script, pGapTrick->m_node);
		}
		pGapTrick = pNext;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterGapComponent::clear_gap_tricks ( Script::CStruct *pParams )
{
	if (pParams->ContainsFlag(CRCD(0x97975226, "NotInSameFrame")))
	{
		// delete all gap tricks leaving those created this frame
		CGapTrick* pGapTrick = static_cast< CGapTrick* >(m_gaptrick_list.FirstItem());
		while (pGapTrick)
		{
			CGapTrick* pNext = static_cast< CGapTrick* >(pGapTrick->GetNext());
			if (pGapTrick->m_frame != m_frame_count)
			{
				delete pGapTrick;
			}
			pGapTrick = pNext;
		}
	}
	else
	{
		// delete all gap tricks
		CGapTrick* pGapTrick = static_cast< CGapTrick* >(m_gaptrick_list.FirstItem());
		while (pGapTrick)
		{
			CGapTrick* pNext = static_cast< CGapTrick* >(pGapTrick->GetNext());
			delete pGapTrick;
			pGapTrick = pNext;
		}
	}
}

}


================================================
FILE: Code/Sk/Components/SkaterGapComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterGapComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/5/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERGAPCOMPONENT_H__
#define __COMPONENTS_SKATERGAPCOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERGAP CRCD(0x11b4c67f, "SkaterGap")

#define		GetSkaterGapComponent() ((Obj::CSkaterGapComponent*)GetComponent(CRC_SKATERGAP))
#define		GetSkaterGapComponentFromObject(pObj) ((Obj::CSkaterGapComponent*)(pObj)->GetComponent(CRC_SKATERGAP))

namespace Script
{
    class CScript;
    class CStruct;
}

namespace Mdl
{
	class Score;
}
              
namespace Obj
{
	class CGap;
	class CGapTrick;
	class CSkaterCorePhysicsComponent;
	class CSkaterBalanceTrickComponent;
	class CSkaterPhysicsControlComponent;
	class CWalkComponent;

class CSkaterGapComponent : public CBaseComponent
{
public:
    CSkaterGapComponent();
    virtual ~CSkaterGapComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	void 							UpdateCancelRequire(int cancel, int require);
	
	void							SetAssociatedScore ( Mdl::Score* p_score ) { mp_score = p_score; }
	void							ClearActiveGaps (   );
	void							ClearPendingGaps (   );
	void							AwardPendingGaps (   );
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }

	static CBaseComponent*			s_create();
	
private:
	void							get_state_flags ( int& cancel, int& require );
	void							start_gap ( Script::CStruct *pParams, Script::CScript* pScript );
	void							start_gap_trick ( Script::CStruct *pParams, Script::CScript* pScript );
	void							end_gap ( Script::CStruct *pParams, Script::CScript* pScript );
	void							check_gap_tricks ( Script::CStruct *pParams );
	void							clear_gap_tricks ( Script::CStruct *pParams );
	
private:
	// list of active gaps
	Lst::Head< CGap >				m_gap_list;
	
	// list of active gapTricks
	Lst::Head< CGapTrick >			m_gaptrick_list;
	
	// count frames to allow timestamping of gap tricks
	uint32							m_frame_count;
	
	// associated score object
	Mdl::Score*						mp_score;
	
	// peer components
	CSkaterCorePhysicsComponent*	mp_core_physics_component;
	CSkaterBalanceTrickComponent*	mp_balance_trick_component;
	CSkaterPhysicsControlComponent*	mp_physics_control_component;
	CWalkComponent*					mp_walk_component;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterLocalNetLogicComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterLocalNetLogicComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/12/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterLocalNetLogicComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterLocalNetLogicComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterLocalNetLogicComponent::CSkaterLocalNetLogicComponent() : CBaseComponent()
{
	SetType( CRC_SKATERLOCALNETLOGIC );
	
	mp_state_component = NULL;
	mp_physics_component = NULL;
	m_last_update_time = 0;
	m_last_sent_terrain = -1;
	m_last_sent_flags = 0;
	m_last_sent_state = 0;
	m_last_sent_doing_trick = -1;
	m_last_sent_rail = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterLocalNetLogicComponent::~CSkaterLocalNetLogicComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLocalNetLogicComponent::InitFromStructure( Script::CStruct* pParams )
{
	Dbg_MsgAssert(GetObject()->GetType() == SKATE_TYPE_SKATER, ("CSkaterLocalNetLogicComponent added to non-skater composite object"));
	Dbg_MsgAssert(GetSkater()->IsLocalClient(), ("CSkaterLocalNetLogicComponent added to non-local skater"));
	
	m_last_update_time = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLocalNetLogicComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLocalNetLogicComponent::Finalize (   )
{
	
	mp_state_component = GetSkaterStateComponentFromObject(GetObject());
	mp_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
	
	Dbg_Assert(mp_state_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLocalNetLogicComponent::Update()
{
	if (!Mdl::Skate::Instance()->IsMultiplayerGame())
	{
		//Suspend(true);
		return;
	}
	if( GameNet::Manager::Instance()->GetLocalPlayer() == NULL )
	{
		return;
	}
		
	network_update();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterLocalNetLogicComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLocalNetLogicComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterLocalNetLogicComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}


/******************************************************************/
/* Print a graffiti steal message  								  */
/* 						  										  */
/******************************************************************/

int	CSkaterLocalNetLogicComponent::sHandleStealMessage( Net::MsgHandlerContext* context )
{
	GameNet::MsgStealMessage* p_msg = (GameNet::MsgStealMessage *) context->m_Msg;
	
	if (p_msg->m_GameId != GameNet::Manager::Instance()->GetNetworkGameId()) return Net::HANDLER_MSG_DONE;

	Obj::CSkater* pCurrSkater = (CSkater*) context->m_Data;
	Dbg_Assert( pCurrSkater );

	// For now, just exit out in these cases to avoid a crash
	Obj::CSkater* pNewSkater = Mdl::Skate::Instance()->GetSkaterById( p_msg->m_NewOwner );
	if( pNewSkater == NULL )
	{
		return Net::HANDLER_CONTINUE;
	}

	Obj::CSkater* pOldSkater = Mdl::Skate::Instance()->GetSkaterById( p_msg->m_OldOwner );
	if( pOldSkater == NULL )
	{
		return Net::HANDLER_CONTINUE;
	}

	// TODO:  Maybe send the color of this skater's graffiti tags
	
	if ( pCurrSkater->GetID() == (uint32)p_msg->m_NewOwner )
	{
		Script::CStruct* pTempStructure = new Script::CStruct;
		pTempStructure->Clear();
		pTempStructure->AddComponent( CRCD(0xa4b08520, "String0"), ESYMBOLTYPE_STRING, pOldSkater->GetDisplayName() );
		Script::RunScript( CRCD(0xb3ff911, "GraffitiStealYou"), pTempStructure, pCurrSkater );
		delete pTempStructure;
	}
	else if ( pCurrSkater->GetID() == (uint32)p_msg->m_OldOwner )
	{
		Script::CStruct* pTempStructure = new Script::CStruct;
		pTempStructure->Clear();
		pTempStructure->AddComponent( CRCD(0xa4b08520, "String0"), ESYMBOLTYPE_STRING, pNewSkater->GetDisplayName() );
		Script::RunScript( CRCD(0x4d7f6ffa, "GraffitiStealOther"), pTempStructure, pCurrSkater );
		delete pTempStructure;
	}
	else
	{
		// useless steal message encountered
		Dbg_Assert( 0 );
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	CSkaterLocalNetLogicComponent::get_update_flags()
{
	Net::Client* client;
	Mth::Vector eulers;
	short pos[3];
	short rot[3];
	char doing_trick, state, terrain, walking, driving;
	Flags< int > skater_flags;
	Flags< int > end_run_flags;
	int i;
	bool on_server, force_send;   
	int update_flags;
	sint16 rail_node;
	Obj::CSkaterEndRunComponent* p_skater_endrun_component;

	update_flags = 0;

	client = GameNet::Manager::Instance()->GetClient( GetSkater()->m_skater_number );
	Dbg_Assert( client );

	p_skater_endrun_component = GetSkaterEndRunComponentFromObject(GetSkater());

	on_server = GameNet::Manager::Instance()->OnServer();
	force_send = on_server || ( !( client->m_FrameCounter % vFORCE_SEND_INTERVAL )); 

	GetObject()->GetDisplayMatrix().GetEulers( eulers );
	
	state = (char) mp_state_component->m_state;
	terrain = (char) mp_state_component->m_terrain;
	doing_trick = (char) mp_state_component->m_doing_trick;
	walking = (char) (mp_state_component->m_physics_state == WALKING ? true : false);
	driving = (char) mp_state_component->m_driving;
	rail_node = mp_physics_component->GetRailNode();

	for( i = 0; i < NUM_ESKATERFLAGS; i++ )
	{     		
		skater_flags.Set( i, mp_state_component->m_skater_flags[( ESkaterFlag ) i ].Get() );
	}
	end_run_flags = p_skater_endrun_component->GetFlags();

	for( i = 0; i < 3; i++ )
	{
		if( i == Y )
		{
			pos[i] = (short) ( GetObject()->m_pos[i] * 4.0f );
			//pos[i] = GetObject()->m_pos[i];
		}
		else
		{
			pos[i] = (short) ( GetObject()->m_pos[i] * 2.0f );
			//pos[i] = GetObject()->m_pos[i];
		}
		if(( pos[i] != m_last_sent_pos[i] ) || force_send )
		{
			if( i == X )
			{
				update_flags |= GameNet::mUPDATE_FIELD_POS_X;
			}
			else if( i == Y )
			{
				update_flags |= GameNet::mUPDATE_FIELD_POS_Y;
			}
			else if( i == Z )
			{
				update_flags |= GameNet::mUPDATE_FIELD_POS_Z;
			}
		}
	}
    
	for( i = 0; i < 3; i++ )
	{
		rot[i] = (short) ( eulers[i] * 4096.0f );
		if(( rot[i] != m_last_sent_rot[i] ) || force_send )
		{   
			m_last_sent_rot[i] = rot[i];
			if( i == X )
			{
				update_flags |= GameNet::mUPDATE_FIELD_ROT_X;
			}
			else if( i == Y )
			{
				update_flags |= GameNet::mUPDATE_FIELD_ROT_Y;
			}
			else if( i == Z )
			{
				update_flags |= GameNet::mUPDATE_FIELD_ROT_Z;
			}
		}
	}
    
	if( ( state != m_last_sent_state ) ||
		( doing_trick != m_last_sent_doing_trick ) ||
		( terrain != m_last_sent_terrain ) ||
		( walking != m_last_sent_walking ) ||
		( driving != m_last_sent_driving ) ||
		( force_send ))
	{
			update_flags |= GameNet::mUPDATE_FIELD_STATE;
	}

    if(( skater_flags != m_last_sent_flags ) || 
	   ( end_run_flags != m_last_sent_end_run_flags ) || 
	   ( force_send ))
	{
		update_flags |= GameNet::mUPDATE_FIELD_FLAGS;
	}

	if( rail_node != m_last_sent_rail )
	{
		update_flags |= GameNet::mUPDATE_FIELD_RAIL_NODE;
	}

	return update_flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLocalNetLogicComponent::network_update ( void )
{
	// update the server with our current state so that he can relay the data to other clients
	
	Net::Client* client;
	Net::MsgMax p_msg;
	Mth::Vector eulers;
	short pos[3];
	short rot[3];
	char doing_trick, state, terrain, walking, driving;
	Flags< int > skater_flags;
	Flags< int > end_run_flags;
	int i, msg_len;
	sint16 rail_node;
	int update_flags;
	bool on_server, force_send;   
	Net::MsgDesc msg_desc;
	Net::BitStream stream;
	Obj::CSkaterEndRunComponent* p_skater_endrun_component;

	client = GameNet::Manager::Instance()->GetClient( GetSkater()->m_skater_number );
	Dbg_MsgAssert( client, ( "Could not find client: %d\n", GetSkater()->m_skater_number ));

	p_skater_endrun_component = GetSkaterEndRunComponentFromObject(GetSkater());

	on_server = GameNet::Manager::Instance()->OnServer();
	force_send = on_server || ( !( client->m_FrameCounter % vFORCE_SEND_INTERVAL )); 

	// Send object updates to narrowband servers only every other frame (except for forced-send frames)
	if( !on_server && !force_send )
	{
		Lst::Search< Net::Conn > sh;
		Net::Conn* server_conn;
		
		server_conn = client->FirstConnection( &sh );
		Dbg_Assert( server_conn );

		// Only send object updates as often as we send packets
		if(( client->m_Timestamp - m_last_update_time ) < (unsigned int) server_conn->GetSendInterval())
		{
			return;
		}
	}

	m_last_update_time = client->m_Timestamp;

	stream.SetOutputData( p_msg.m_Data, 1024 );
	
	GetObject()->GetDisplayMatrix().GetEulers( eulers );
	
	state = (char) mp_state_component->m_state;
	terrain = (char) mp_state_component->m_terrain;
	doing_trick = (char) mp_state_component->m_doing_trick;
	walking = (char) (mp_state_component->m_physics_state == WALKING ? true : false);
	driving = (char) mp_state_component->m_driving;
	rail_node = mp_physics_component->GetRailNode();

	for( i = 0; i < NUM_ESKATERFLAGS; i++ )
	{     		
		skater_flags.Set( i, mp_state_component->m_skater_flags[( ESkaterFlag ) i ].Get() );
	}
	
	end_run_flags = p_skater_endrun_component->GetFlags();

	// Write out the time for which this state info is valid
	stream.WriteValue( client->m_Timestamp, sizeof( int ) * 8 );
	update_flags = get_update_flags();
	stream.WriteValue( update_flags, 9 );

	if( on_server )
	{
		static Mth::Vector last_pos;
		Mth::Vector diff;

		diff = GetObject()->m_pos - last_pos;
		//Dbg_Printf( "Vel: %f %f %f\n", diff[X], diff[Y], diff[Z] );
		last_pos = GetObject()->m_pos;
	}

// Write out the object's position as three shorts (fixed-point)
	for( i = 0; i < 3; i++ )
	{
		if( i == Y )
		{
			pos[i] = (short) ( GetObject()->m_pos[i] * 4.0f );
			//pos[i] = GetObject()->m_pos[i];
		}
		else
		{
			pos[i] = (short) ( GetObject()->m_pos[i] * 2.0f );
			//pos[i] = GetObject()->m_pos[i];
		}
		if( i == X )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_POS_X )
			{
				stream.WriteValue( pos[i], sizeof( short ) * 8 );
				//stream.WriteFloatValue( pos[i] );
			}
		}
		else if( i == Y )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_POS_Y )
			{
				stream.WriteValue( pos[i], sizeof( short ) * 8 );
				//stream.WriteFloatValue( pos[i] );
			}
		}
		else if( i == Z )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_POS_Z )
			{
				stream.WriteValue( pos[i], sizeof( short ) * 8 );
				//stream.WriteFloatValue( pos[i] );
			}
		}

		m_last_sent_pos[i] = pos[i];
	}
    
	// Write out the object's orientation as three short euler angles (fixed-point)
	for( i = 0; i < 3; i++ )
	{
		rot[i] = (short) ( eulers[i] * 4096.0f );
		if( i == X )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_ROT_X )
			{
				stream.WriteValue( rot[i], sizeof( short ) * 8 );
			}
		}
		else if( i == Y )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_ROT_Y )
			{
				stream.WriteValue( rot[i], sizeof( short ) * 8 );
			}
		}
		else if( i == Z )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_ROT_Z )
			{
				stream.WriteValue( rot[i], sizeof( short ) * 8 );
			}
		}

		m_last_sent_rot[i] = rot[i];
	}
    
	// Write out the skater's 'state'
	// Write out the skater's 'doing trick' state
	// Write out the skater's terrain
	if( update_flags & GameNet::mUPDATE_FIELD_STATE )
	{
		char mask;

		m_last_sent_state = state;
		m_last_sent_doing_trick = doing_trick;
		m_last_sent_terrain = terrain;
		m_last_sent_walking = walking;
		m_last_sent_driving = driving;

		mask = state;
		if( doing_trick )
		{
			mask |= GameNet::mDOING_TRICK_MASK;
		}
		
		stream.WriteValue( mask, 4 );
		stream.WriteValue( terrain, 6 );
		stream.WriteValue( walking, 1 );
		stream.WriteValue( driving, 1 );
	}

    // Write out the skaters' flags
	if( update_flags & GameNet::mUPDATE_FIELD_FLAGS )
	{
		m_last_sent_flags = skater_flags;
		m_last_sent_end_run_flags = end_run_flags;
		stream.WriteValue( skater_flags, 5 );
		stream.WriteValue( end_run_flags, 3 );
	}

	if( update_flags & GameNet::mUPDATE_FIELD_RAIL_NODE )
	{
		m_last_sent_rail = rail_node;
		stream.WriteValue( rail_node, sizeof( sint16 ) * 8 );
	}

	stream.Flush();
	msg_len = stream.GetByteLength();

	msg_desc.m_Id = GameNet::MSG_ID_OBJ_UPDATE_STREAM;
	msg_desc.m_Length = msg_len;
	msg_desc.m_Data = &p_msg;
	msg_desc.m_Singular = true;
	msg_desc.m_Priority = Net::NORMAL_PRIORITY + 1;
	client->EnqueueMessageToServer( &msg_desc );
}
                                           
}

================================================
FILE: Code/Sk/Components/SkaterLocalNetLogicComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterLocalNetLogicComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/12/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERLOCALNETLOGICCOMPONENT_H__
#define __COMPONENTS_SKATERLOCALNETLOGICCOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERLOCALNETLOGIC CRCD(0x7cd3e6d5, "SkaterLocalNetLogic")

#define		GetSkaterLocalNetLogicComponent() ((Obj::CSkaterLocalNetLogicComponent*)GetComponent(CRC_SKATERLOCALNETLOGIC))
#define		GetSkaterLocalNetLogicComponentFromObject(pObj) ((Obj::CSkaterLocalNetLogicComponent*)(pObj)->GetComponent(CRC_SKATERLOCALNETLOGIC))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSkaterCorePhysicsComponent;
	class CSkaterStateComponent;

class CSkaterLocalNetLogicComponent : public CBaseComponent
{
public:
    CSkaterLocalNetLogicComponent();
    virtual ~CSkaterLocalNetLogicComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
	static int						sHandleStealMessage( Net::MsgHandlerContext* context );
	
private:
	void							network_update();
	int								get_update_flags();
	
	Tmr::Time						m_last_update_time;
	
	char							m_last_sent_terrain;
	short							m_last_sent_pos[3];
	short							m_last_sent_rot[3];
	Flags< int >					m_last_sent_flags;
	Flags< int >					m_last_sent_end_run_flags;
	char							m_last_sent_state;
	char							m_last_sent_doing_trick;
	char							m_last_sent_walking;
	char							m_last_sent_driving;
	sint16							m_last_sent_rail;
	
private:
	CSkaterStateComponent*			mp_state_component;
	CSkaterCorePhysicsComponent*	mp_physics_component;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterLoopingSoundComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterLoopingSoundComponent.cpp
//* OWNERD:			Dan
//* CREATION DATE:  2/26/3
//****************************************************************************

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent* CSkaterLoopingSoundComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterLoopingSoundComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterLoopingSoundComponent::CSkaterLoopingSoundComponent() : CBaseComponent()
{
	SetType( CRC_SKATERLOOPINGSOUND );
	
	m_is_bailing = m_is_rail_sliding = false;
	m_have_sound_info = false;
	m_update_sound_info = true;
	m_StateType = AIR;
	m_vol_mult = 1.0f;
	m_active = true;
	m_unpause = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterLoopingSoundComponent::~CSkaterLoopingSoundComponent()
{
	StopLoopingSound();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLoopingSoundComponent::InitFromStructure( Script::CStruct* pParams )
{
	m_wheelspin_pitch_step = (vSS_WHEELSPIN_MIN_PITCH - (vSS_WHEELSPIN_MIN_PITCH - vSS_WHEELSPIN_MAX_PITCH / 2.0f)) / vSS_MIN_WHEELSPIN_TIME;
	m_wheelspin_end_pitch = vSS_WHEELSPIN_MIN_PITCH - vSS_WHEELSPIN_MAX_PITCH / 2.0f;
	pParams->GetFloat( CRCD(0xf1a99b27,"volume_mult"), &m_vol_mult, Script::NO_ASSERT );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLoopingSoundComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLoopingSoundComponent::Finalize (   )
{
	mp_physics_control_component = GetSkaterPhysicsControlComponentFromObject(GetObject());
	
	// Dbg_Assert(mp_physics_control_component);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLoopingSoundComponent::Update()
{
	if (!m_active)
	{
		Suspend(true);
		return;
	}
	
	// update looping sounds
	
	Sfx::sVolume volume;
	float pitch;

	switch ( m_StateType )
	{
		case RAIL:
		{
			if ( m_is_bailing )
			{
				m_have_sound_info = false;
				break;
			}
			
			Env::ETerrainActionType table;
			if ( m_is_rail_sliding )
			{
				table = Env::vTABLE_SLIDE;
			}
			else								
			{
				table = Env::vTABLE_GRIND;
			}
			
			if (m_update_sound_info)
			{
				m_have_sound_info = Env::CTerrainManager::sGetTerrainSoundInfo(&m_sound_info, m_terrain, table);
				m_update_sound_info = false;
			}
			
			break;
		}
		
		case GROUND:
			if (m_update_sound_info)
			{
				m_have_sound_info = Env::CTerrainManager::sGetTerrainSoundInfo(&m_sound_info, m_terrain, Env::vTABLE_WHEELROLL);			
				m_update_sound_info = false;
			}

			break;
			
		case AIR:
			if (m_update_sound_info)
			{
				m_have_sound_info = true;
				m_sound_info = AIR_LOOPING_SOUND_INFO;
				m_update_sound_info = false;
			}
			
			break;
			
		default:
			break;
	} // END switch on skater state

	// if the sound has changed, turn off the old one
	if (m_looping_sound_id && (!m_have_sound_info || m_looping_sound_checksum != m_sound_info.m_soundChecksum))
	{
		Sfx::CSfxManager::Instance()->StopSound(m_looping_sound_id);
		m_looping_sound_id = 0;
	}
	
	// we have no sound to play
	if (!m_have_sound_info) return;
	
	// setup the sound's pitch and volume based on the skater's state
	
	Sfx::CSfxManager* p_sfx_manager = Sfx::CSfxManager::Instance();
	
	// adjust volume based of skater's offset from the nearest camera
	p_sfx_manager->SetVolumeFromPos(&volume, GetObject()->GetPos(), p_sfx_manager->GetDropoffDist(m_sound_info.m_soundChecksum));
	
	// if the skater is in the air and this isn't the first frame we've been playing the air looping sound
	if ( m_StateType == AIR && m_looping_sound_id )
	{
		// drop the pitch over time
		if (m_wheelspin_pitch > m_wheelspin_end_pitch)
		{
			m_wheelspin_pitch -= m_wheelspin_pitch_step * Tmr::FrameLength();
		}
		// then kill the volume
		else
		{
			volume.SetSilent();
		}
		
		pitch = m_wheelspin_pitch;
	}
	else
	{
		// adjust the volume and pitch based on the speed
		 
		if ( m_speed_fraction > 0.0f)
		{
			volume.PercentageAdjustment(Env::CTerrainManager::sGetVolPercent(&m_sound_info, 100.0f * m_speed_fraction * m_vol_mult));
			
			pitch = m_speed_fraction * (m_sound_info.m_maxPitch - m_sound_info.m_minPitch) + m_sound_info.m_minPitch;
			
			// save the current pitch incase we are in the air next frame
			m_wheelspin_pitch = pitch;
		}
		else
		{
			volume.SetSilent();
			m_wheelspin_pitch = pitch = 0.0f;
		}
	}
	
	// if the volume is zero
	if (volume.IsSilent())
	{
		// stop playing the sound
		if (m_looping_sound_id)
		{
			Sfx::CSfxManager::Instance()->StopSound(m_looping_sound_id);
			m_looping_sound_id = 0;
		}
		return;
	}
	
	// NOTE: removed until I can figure out what to do with this
	// adjust the sound based on doppler effects
	// if ( Nx::CViewportManager::sGetScreenMode( ) == 0 ) // that zero should be an enum or something...
	// {
		// sfx_manager->AdjustPitchForDoppler( &pitch, mp_physics->m_pos, mp_physics->m_old_pos, mp_physics->m_time, Nx::CViewportManager::sGetActiveCamera( 0 ) );
	// }
	
	// NOTE: removing all replay code for now
	// save pitch information for the replay code
	// m_pitch_min = sound_info.m_minPitch;
	// m_pitch_max = sound_info.m_maxPitch;
	
	// if we're not already playing a sound
	if (!m_looping_sound_id)
	{
		m_last_volume = volume;
		m_last_pitch = pitch;
		
		// start the sound
		m_looping_sound_id = p_sfx_manager->PlaySound(m_sound_info.m_soundChecksum, &volume, pitch, 0, NULL, m_sound_info.mp_soundName);
		if (!m_looping_sound_id)
		{
			return;
		}
		
		// save the checksum of the currently playing sound
		m_looping_sound_checksum = m_sound_info.m_soundChecksum;
	}
	
	// if we need to update the sound already playing; since we scale all channels equally, we can get away with only checking the first volume
	else if (m_unpause || Mth::Abs(volume.m_channels[0] - m_last_volume.m_channels[0]) > vSS_MAX_PERCENTAGE_VOLUME_CHANGE_WITHOUT_UPDATE
		|| Mth::Abs(pitch - m_last_pitch) > vSS_MAX_PITCH_CHANGE_WITHOUT_UPDATE)
	{
		m_last_volume = volume;
		m_last_pitch = pitch;
		m_unpause = false;
		
		p_sfx_manager->UpdateLoopingSound(m_looping_sound_id, &volume, pitch);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterLoopingSoundComponent::CallMemberFunction ( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		case CRCC(0xede3935f, "SkaterLoopingSound_TurnOn"):
			SetActive(true);
			break;
			
		case CRCC(0x9731e193, "SkaterLoopingSound_TurnOff"):
			SetActive(false);
			break;
			
		case CRCC(0xb1e7291, "PlayAnim"):
			// if a transition anim is interrupted, we must turn the looping sounds on here
			Dbg_MsgAssert( mp_physics_control_component, ( "Don't call PlayAnim on a non-skater" ) );
			
			Script::CStruct* p_anim_tags_struct;
			if (GetObject()->GetTags()
				&& mp_physics_control_component->IsSkating()
				&& GetObject()->GetTags()->GetStructure(CRCD(0x5db4115f, "AnimTags"), &p_anim_tags_struct)
				&& p_anim_tags_struct->ContainsFlag(CRCD(0x910d77c1, "WalkToSkateTransition")))
			{
				Suspend(false);
				m_active = true;
			}
			return CBaseComponent::MF_NOT_EXECUTED;
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLoopingSoundComponent::GetDebugInfo ( Script::CStruct *p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterLoopingSoundComponent::GetDebugInfo"));
	
	p_info->AddInteger("m_looping_sound_id", m_looping_sound_id);
	p_info->AddChecksum("m_looping_sound_checksum", m_looping_sound_checksum);

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLoopingSoundComponent::Suspend ( bool suspend )
{
	CBaseComponent::Suspend(suspend);
	
	if (suspend)
	{
		StopLoopingSound();
	}
	else
	{
		m_update_sound_info = true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLoopingSoundComponent::StopLoopingSound (   )
{
	if (m_looping_sound_id)
	{
		Sfx::CSfxManager::Instance()->StopSound(m_looping_sound_id);
		m_looping_sound_id = 0;
	}
}
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLoopingSoundComponent::SetSpeedFraction( float speed_fraction )
{
	m_speed_fraction = speed_fraction;
	if ( m_speed_fraction > 1.0f )
	{
		m_speed_fraction = 1.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterLoopingSoundComponent::SetVolumeMultiplier( float mult )
{
	Dbg_MsgAssert( mult >= 0.0f && mult <= 1.0f, ( "SetVolumeMultiplier called with bad mult value: %f", mult ) );
	m_vol_mult = mult;
}

}


================================================
FILE: Code/Sk/Components/SkaterLoopingSoundComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterLoopingSoundComponent.h
//* OWNERD:			Dan
//* CREATION DATE:  2/26/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERLOOPINGSOUNDCOMPONENT_H__
#define __COMPONENTS_SKATERLOOPINGSOUNDCOMPONENT_H__

#include 
#include 

#include 
#include 
#include 

#include 

#define		CRC_SKATERLOOPINGSOUND CRCD(0xb5f1e9d2, "SkaterLoopingSound")

#define		GetSkaterLoopingSoundComponent() ((Obj::CSkaterLoopingSoundComponent*)GetComponent(CRC_SKATERLOOPINGSOUND))
#define		GetSkaterLoopingSoundComponentFromObject(pObj) ((Obj::CSkaterLoopingSoundComponent*)(pObj)->GetComponent(CRC_SKATERLOOPINGSOUND))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSkaterPhysicsControlComponent;

	static const float vSS_MAX_PITCH_CHANGE_WITHOUT_UPDATE = 0.5f;
	static const float vSS_MAX_PERCENTAGE_VOLUME_CHANGE_WITHOUT_UPDATE = 0.5f;
	
	static const float vSS_WHEELSPIN_MIN_PITCH = 100.0f;
	static const float vSS_WHEELSPIN_MAX_PITCH = 110.0f;
	static const float vSS_MIN_WHEELSPIN_TIME = 3.0f;
	
	// looping sound used when skater is in the air
	static const Env::STerrainSoundInfo AIR_LOOPING_SOUND_INFO =
	{
		"wheels01",
		CRCD(0x24257432, "wheels01"),
		vSS_WHEELSPIN_MIN_PITCH,
		vSS_WHEELSPIN_MAX_PITCH
	};
	
class CSkaterLoopingSoundComponent : public CBaseComponent
{
public:
    CSkaterLoopingSoundComponent();
    virtual ~CSkaterLoopingSoundComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
	virtual void					Finalize (   );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
	virtual void					Suspend ( bool suspend );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							StopLoopingSound (   );

	void							UnPause();
	
	void							SetActive ( bool active );

	void							SetIsBailing( bool is_bailing );
	void							SetIsRailSliding( bool is_sliding );

	void							SetState( EStateType new_state );
	void							SetTerrain( ETerrainType terrain );

	void							SetSpeedFraction( float speed_fraction );
	void							SetVolumeMultiplier( float mult );
private:
	// the ID of the current looping sound
	uint32							m_looping_sound_id;
	
	// checksum of the current looping sound
	uint32							m_looping_sound_checksum;
	
	// holds the last frame's looping sound pitch so that can be used when the skater goes airborne
	float							m_wheelspin_pitch;
	
	// optimization variables
	float							m_last_pitch;
	Sfx::sVolume					m_last_volume;
	
	// constant characteristics
	
	// the drop in pitch of the air looping sound per second
	float m_wheelspin_end_pitch;
	
	// the final pitch of the air looping sound; when reached the sound is turned off
	float m_wheelspin_pitch_step;

	// peer components
	CSkaterPhysicsControlComponent*	mp_physics_control_component;

	ETerrainType					m_terrain;
	EStateType	m_StateType;

	Env::STerrainSoundInfo			m_sound_info;
	bool							m_have_sound_info;
	bool							m_update_sound_info;
	bool							m_unpause;

	bool							m_is_bailing;
	bool							m_is_rail_sliding;

	float							m_speed_fraction;
	float							m_vol_mult;
	
	bool							m_active;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterLoopingSoundComponent::SetActive ( bool active )
{
	if (m_active != active)
	{
		Suspend(!active);
		m_active = active;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void	CSkaterLoopingSoundComponent::UnPause()
{
	m_unpause = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterLoopingSoundComponent::SetState( EStateType state )
{
	m_update_sound_info |= (m_StateType != state);
	m_StateType = state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterLoopingSoundComponent::SetTerrain( ETerrainType terrain )
{
	m_update_sound_info |= (m_terrain != terrain);
	m_terrain = terrain;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterLoopingSoundComponent::SetIsBailing( bool is_bailing )
{
	m_is_bailing = is_bailing;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterLoopingSoundComponent::SetIsRailSliding( bool is_rail_sliding )
{
	m_update_sound_info |= (m_is_rail_sliding != is_rail_sliding);
	m_is_rail_sliding = is_rail_sliding;
}

}

#endif


================================================
FILE: Code/Sk/Components/SkaterMatrixQueriesComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterMatrixQueriesComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/12/3
//****************************************************************************

#include 
#include 

#include 
#include 
#include 
#include 

#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterMatrixQueriesComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterMatrixQueriesComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterMatrixQueriesComponent::CSkaterMatrixQueriesComponent() : CBaseComponent()
{
	SetType( CRC_SKATERMATRIXQUERIES );
	
	mp_core_physics_component = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterMatrixQueriesComponent::~CSkaterMatrixQueriesComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterMatrixQueriesComponent::InitFromStructure( Script::CStruct* pParams )
{
	Dbg_MsgAssert(GetObject()->GetType() == SKATE_TYPE_SKATER, ("CSkaterMatrixQueriesComponent added to non-skater composite object"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterMatrixQueriesComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterMatrixQueriesComponent::Finalize (   )
{
	mp_core_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
		
	Dbg_Assert(mp_core_physics_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterMatrixQueriesComponent::Update()
{
	// Store the matrix from the last frame. Used by script functions for measuring angles & stuff. Has to use the last frame's matrix
	// because the landing physics snaps the orientation to the ground, yet the land script (which executes just after) needs to measure 
	// the angles on impact to maybe trigger bails and such.
	m_latest_matrix = mp_core_physics_component->m_lerping_display_matrix;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterMatrixQueriesComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | YawBetween | yaw between the two specified angles
        // @uparm (45, 135) | angle range values
		case CRCC(0xb992f3cc, "YawBetween"):
		{
			// if (CHEAT_SNOWBOARD) 
			// {
				// return false;
			// }
			
			Script::CPair Pair;
			if (!pParams->GetPair(NO_NAME, &Pair, Script::ASSERT))
			Dbg_MsgAssert(Pair.mX < Pair.mY,("\n%s\n1st angle must be less than the 2nd angle", pScript->GetScriptInfo()));
			
			Mth::Vector a = GetSkater()->m_vel;
			a.RotateToPlane(m_latest_matrix[Y]);
			return (Mth::AngleBetweenGreaterThan(a, m_latest_matrix[Z], Pair.mX)
				&& !Mth::AngleBetweenGreaterThan(a, m_latest_matrix[Z], Pair.mY))
				? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}

        // @script | YawingLeft | true if currently yawing left
		case CRCC(0xa745c080, "YawingLeft"):
		{
			Mth::Vector a = GetSkater()->m_vel;
			a.RotateToPlane(m_latest_matrix[Y]);
			return Mth::CrossProduct(a, m_latest_matrix[Z])[Y] > 0.0f ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
			
        // @script | YawingRight | true if currently yawing right
		case CRCC(0xc8c4d2f4, "YawingRight"):
		{
			Mth::Vector a = GetSkater()->m_vel;
			a.RotateToPlane(m_latest_matrix[Y]);
			return Mth::CrossProduct(a, m_latest_matrix[Z])[Y] < 0.0f ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
			
        // @script | PitchGreaterThan | true if the pitch is greater
        // than the specified value
        // @uparm 0.0 | test angle
		case CRCC(0xa0551543, "PitchGreaterThan"):
		{
			float TestAngle = 0.0f;
			pParams->GetFloat(NO_NAME, &TestAngle, Script::ASSERT);
			
			return Mth::DotProduct(m_latest_matrix[Y], mp_core_physics_component->m_last_display_normal) < cosf(Mth::DegToRad(TestAngle))
				? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}

		case CRCC(0x5e269b2b, "AbsolutePitchGreaterThan"):
		{
			float TestAngle = 0.0f;
			pParams->GetFloat(NO_NAME, &TestAngle, Script::ASSERT);
			
			return m_latest_matrix[Y][Y] < cosf(Mth::DegToRad(TestAngle)) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
			
		/*
        // @script | PitchingForward | true if pitching forward
		case CRCC(0xdaeda59c, "PitchingForward"):
		{
			Mth::Vector b = Mth::CrossProduct(m_latest_matrix[Y], mp_core_physics_component->m_last_display_normal);
			return Mth::DotProduct(b, m_latest_matrix[X]) < 0.0f ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}

        // @script | PitchingBackward | true if pitching backward
		case CRCC(0x7dd9e92c, "PitchingBackward"):
		{
			Mth::Vector b = Mth::CrossProduct(m_latest_matrix[Y], mp_core_physics_component->m_last_display_normal);
			return Mth::DotProduct(b, m_latest_matrix[X]) > 0.0f ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		*/

        // @script | RollGreaterThan | true if the roll is greater than
        // the specified angle value
        // @uparm 0.0 | angle value
		case CRCC(0xd3313e92, "RollGreaterThan"):
		{
			float TestAngle = 0.0f;
			pParams->GetFloat(NO_NAME, &TestAngle, Script::ASSERT);
			
			Mth::Vector a = m_latest_matrix[X];
			a.RotateToPlane(mp_core_physics_component->m_last_display_normal);
			return Mth::AngleBetweenGreaterThan(a, m_latest_matrix[X], TestAngle) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}

		/*
		// @script | RollingLeft | true if rolling left
		case CRCC(0x7328c9ad, "RollingLeft"):
		{
			Mth::Vector b = Mth::CrossProduct(m_latest_matrix[Y], mp_core_physics_component->m_last_display_normal);
			return Mth::DotProduct(b, m_latest_matrix[Z]) > 0.0f ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}

        // @script | RollingRight | true if rolling right
		case CRCC(0x8dcfe388, "RollingRight"):
		{
			Mth::Vector b = Mth::CrossProduct(m_latest_matrix[Y], mp_core_physics_component->m_last_display_normal);
			return Mth::DotProduct(b, m_latest_matrix[Z]) < 0.0f ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		*/
			
		// @script | GetSlope | Puts the angle of the slope into a param called Slope.
		// Units are degrees. Zero is horizontal, positive is up, negative is down.
		// The change in slope since the last call to GetSlope is put in a parameter
		// called ChangeInSlope
		case CRCC(0x97201739, "GetSlope"):
		{
			Mth::Vector v = GetObject()->m_matrix[Z];
			v[Y] = 0.0f;
			float slope = Mth::GetAngle(GetObject()->m_matrix[Z], v);
			if (GetObject()->m_matrix[Z][Y] < 0.0f)
			{
				slope = -slope;
			}	
			pScript->GetParams()->AddFloat(CRCD(0xa733ba7a, "Slope"), slope);
			pScript->GetParams()->AddFloat(CRCD(0x21afff16, "ChangeInSlope"), slope - m_last_slope);
			m_last_slope = slope;
			break;
		}
		
		case CRCC(0x8e7833be, "GetHeading"):
		{
			float heading = Mth::RadToDeg(cosf(GetObject()->m_matrix[Z][X]));
			if (GetObject()->m_matrix[Z][Z] < 0.0f)
			{
				heading = 360.0f - heading;
			}	
			pScript->GetParams()->AddFloat(CRCD(0xfd4bc03e, "heading"), heading);
			pScript->GetParams()->AddFloat(CRCD(0x2315ef17, "cosine"), GetObject()->m_matrix[Z][X]);
			pScript->GetParams()->AddFloat(CRCD(0x26910cc0, "sine"), GetObject()->m_matrix[Z][Z]);
			break;
		}
		

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterMatrixQueriesComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterMatrixQueriesComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

}


================================================
FILE: Code/Sk/Components/SkaterMatrixQueriesComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterMatrixQueriesComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/12/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERMATRIXQUERIESCOMPONENT_H__
#define __COMPONENTS_SKATERMATRIXQUERIESCOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERMATRIXQUERIES CRCD(0xbf518e27, "SkaterMatrixQueries")

#define		GetSkaterMatrixQueriesComponent() ((Obj::CSkaterMatrixQueriesComponent*)GetComponent(CRC_SKATERMATRIXQUERIES))
#define		GetSkaterMatrixQueriesComponentFromObject(pObj) ((Obj::CSkaterMatrixQueriesComponent*)(pObj)->GetComponent(CRC_SKATERMATRIXQUERIES))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CSkaterMatrixQueriesComponent : public CBaseComponent
{
public:
    CSkaterMatrixQueriesComponent();
    virtual ~CSkaterMatrixQueriesComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
	void							ResetLatestMatrix (   ) { m_latest_matrix = GetObject()->GetMatrix(); }
	
private:
	Mth::Matrix						m_latest_matrix;
	float							m_last_slope;
	
	// peer components
	CSkaterCorePhysicsComponent*	mp_core_physics_component;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterNonLocalNetLogicComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterNonLocalNetLogicComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/11/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 
								
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterNonLocalNetLogicComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterNonLocalNetLogicComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterNonLocalNetLogicComponent::CSkaterNonLocalNetLogicComponent() : CBaseComponent()
{
	SetType( CRC_SKATERNONLOCALNETLOGIC );
	
	mp_state_history_component = NULL;
	mp_state_component = NULL;
	mp_endrun_component = NULL;
	mp_animation_component = NULL;
	mp_model_component = NULL;
	mp_flip_and_rotate_component = NULL;
	mp_shadow_component = NULL;
	m_last_pos_index = 0;
	m_interp_pos = Mth::Vector( 0, 0, 0 );
	m_old_interp_pos = Mth::Vector( 0, 0, 0 );
	m_num_mags = 0;
	m_last_anm_update_time = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterNonLocalNetLogicComponent::~CSkaterNonLocalNetLogicComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::InitFromStructure( Script::CStruct* pParams )
{
	Dbg_MsgAssert(GetObject()->GetType() == SKATE_TYPE_SKATER, ("CSkaterNonLocalNetLogicComponent added to non-skater composite object"));
	Dbg_MsgAssert(!GetSkater()->IsLocalClient(), ("CSkaterNonLocalNetLogicComponent added to non-local skater"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::Finalize (   )
{
	mp_state_history_component = GetSkaterStateHistoryComponentFromObject(GetObject());
	mp_state_component = GetSkaterStateComponentFromObject(GetObject());
	mp_endrun_component = GetSkaterEndRunComponentFromObject(GetObject());
	mp_animation_component = GetAnimationComponentFromObject(GetObject());
	mp_flip_and_rotate_component = GetSkaterFlipAndRotateComponentFromObject(GetObject());
	mp_model_component = GetModelComponentFromObject(GetObject());
	mp_shadow_component = GetShadowComponentFromObject(GetObject());
	
	Dbg_Assert(mp_state_history_component);
	Dbg_Assert(mp_state_component);
	Dbg_Assert(mp_endrun_component);
	Dbg_Assert(mp_animation_component);
	Dbg_Assert(mp_flip_and_rotate_component);
	Dbg_Assert(mp_model_component);
	Dbg_Assert(mp_shadow_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::Update()
{
	GameNet::PlayerInfo* local_player;
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();

	m_frame_length = Tmr::UncappedFrameLength();

	bool BeganFrameInLipState = mp_state_component->GetState() == LIP;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	if( mp_state_component->GetState() != AIR )
	{
		mp_state_component->m_camera_display_normal = GetObject()->GetMatrix().GetUp();
		mp_state_component->m_camera_current_normal = GetObject()->GetMatrix().GetUp();
	}

	setup_brightness_and_shadow();             
	
	m_old_extrap_pos = GetObject()->m_pos;
	GetObject()->m_old_pos = GetObject()->m_pos;
	interpolate_client_position();
	// Only extrapolate if we can collide with other players. Otherwise, interpolation looks 
	// much better.
	local_player = gamenet_man->GetLocalPlayer();
	if( !local_player->IsObserving() &&
		!local_player->IsSurveying())
	{
		if( ( GameNet::Manager::Instance()->PlayerCollisionEnabled()) ||
			( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0xbff33600,"netfirefight")) ||
			( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0x3d6d444f,"firefight")))
		{
			extrapolate_client_position();
		}
	}
	do_client_animation_update();
	
	// NOTE: Below logic is copied from CSkaterFinalizePhysicsComponent and should instead be merged.
	
	// Logic for setting/not setting the flag for telling the camera whether to look down on the skater or not.
	if (BeganFrameInLipState && mp_state_component->GetState() != LIP)
	{
		// This flag is sort of badly named now, it really means we changed from the lip state to something other than GROUND or LIP.
		mp_state_component->mJumpedOutOfLipTrick = false;
		if (mp_state_component->GetState() == AIR)
		{
			// we only want to set this flag if we jumped straight up; meaning the x and z velocities are close to zero
			if (Mth::Abs(GetObject()->m_vel[X]) < 1.0f && Mth::Abs(GetObject()->m_vel[Z]) < 1.0f)
			{
				mp_state_component->mJumpedOutOfLipTrick = true;
			}				
		}
	}
	
	// this flag needs clearing whenever we get out of the air
	if (mp_state_component->GetState() != AIR)
	{
		mp_state_component->mJumpedOutOfLipTrick = false;
	}
	
	// take the non-local client in and out of the car
	if (mp_state_component->GetDriving() && mp_state_history_component->GetCurrentVehicleControlType() != 0)
	{
		Script::CStruct* pParams = new Script::CStruct;
		pParams->AddChecksum(CRCD(0x5b24faaa, "SkaterId"), GetObject()->GetID());
		pParams->AddChecksum(CRCD(0x81cff663, "control_type"), mp_state_history_component->GetCurrentVehicleControlType());
		RunScript(CRCD(0x7853f8c4, "NonLocalClientInVehicle"), pParams);
		delete pParams;
	}
	else if (!mp_state_component->GetDriving() && m_last_driving)
	{
		Script::CStruct* pParams = new Script::CStruct;
		pParams->AddChecksum(CRCD(0x5b24faaa, "SkaterId"), GetObject()->GetID());
		RunScript(CRCD(0xf02e44f, "NonLocalClientExitVehicle"), pParams);
		delete pParams;
	}
	m_last_driving = mp_state_component->GetDriving();
	
	// update the looping sound component
	CSkaterLoopingSoundComponent* p_looping_sound_component = GetSkaterLoopingSoundComponentFromObject(GetObject());
	Dbg_Assert(p_looping_sound_component);
	if (mp_state_component->m_physics_state == SKATING)
	{
		p_looping_sound_component->SetActive(true);
		float speed_fraction = sqrtf(GetObject()->m_vel[X] * GetObject()->m_vel[X] + GetObject()->m_vel[Z] * GetObject()->m_vel[Z]) / 1000.0f;
		p_looping_sound_component->SetSpeedFraction(speed_fraction);
		p_looping_sound_component->SetState(mp_state_component->m_state);
		p_looping_sound_component->SetTerrain(mp_state_component->m_terrain);
		p_looping_sound_component->SetIsBailing(mp_state_component->GetFlag(IS_BAILING));
		p_looping_sound_component->SetIsRailSliding(mp_state_component->GetFlag(RAIL_SLIDING));
	}
	else
	{
		p_looping_sound_component->SetActive(false);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterNonLocalNetLogicComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterNonLocalNetLogicComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::Resync (   )
{
	m_client_initial_update_time = 0;
	m_last_anm_update_time = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::snap_to_ground( void  )
{
	float up_dot = 0.0f;
	Mth::Vector col_start, col_end;
	CFeeler feeler;
	
	// Since we really don't want to loose contact with the ground while skitching, we use a much bigger snap up dist
	// The problem will come when we get dragged down a slope.  The car will flatten out well ahead of us, so pushing us down through the slop
	// (as we are a few feet behind it) and we will be so far under the ground that our normal snap up will not be able to dig us out of it,
	// so we go in air, uberfrig, and get dragged to a random spot under the level.
	// (This would not happen if we just skitch on flat ground)																								
	// if (mp_state_component->m_skater_flags[SKITCHING].Get())
	// {
		// col_start = GetObject()->m_matrix[Y] * GetPhysicsFloat(CRCD(0x5c0d9610,"Physics_Ground_Snap_Up_SKITCHING"));	// much above feet
	// }
	// else
	// {
		col_start = GetObject()->m_matrix[Y] * GetPhysicsFloat(CRCD(0xe4d79235, "Physics_Ground_Snap_Up"));	  		// bit above the feet
	// }
	
	col_end = GetObject()->m_matrix[Y] * -200.0f;		    // WAY! below the feet, we check distance later

	col_start += GetObject()->m_pos;
	col_end += GetObject()->m_pos;
		 
	bool sticking = false;			
	
	feeler.SetLine( col_start, col_end );
	feeler.SetIgnore( mFD_NON_COLLIDABLE, 0 );
	// get disatnce to ground and snap the skater to it, but only if the ground is skatable, otherwise we just go to "AIR"
	if( feeler.GetCollision())
	{
		Mth::Vector movement = GetObject()->m_pos - feeler.GetPoint(); 
		uint16 flags = feeler.GetFlags();
		float drop_dist = movement.Length();
		float drop_sign = Mth::DotProduct(GetObject()->m_matrix[Y], movement); // might be approx +/- 0.00001f
		if(	(!flags & mFD_SKATABLE ) || ( flags & mFD_NOT_SKATABLE ) || (!flags & mFD_VERT) || 
			(flags & mFD_WALL_RIDABLE) ||
			(feeler.GetNormal()[Y] < sinf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x3eede4d3,"Wall_Non_Skatable_Angle"))))))
		{
			// if below the face (or very close to it), then push us away from it				
			if (drop_sign < 0.001f)
			{
				// at point of contact, and move away from surface
				GetObject()->m_pos = feeler.GetPoint() + feeler.GetNormal();	
			}
		}
		else
		{
			sticking = true;
			
			// Note the two ways of calculating the angle between two faces
			// the more "accurate" method simply takes angle between the two normals
			// however, this fails to account for the direction the skater is travelling
			// when in conjunction with the "snap" up.
			// If the skater approaches a slope from the side, then he can snap up to the slope
			// however, if the angle between the ground and the face to which we are snapping up to 
			// is too great, then we transition to in-air
			// and will drop through the slope
			// the solution is to take the angle between the front vector rotated onto each face.
			
			// Firstly we check the angle between the two faces	
			Mth::Vector normal = feeler.GetNormal();
			
			Mth::Vector	forward = GetObject()->m_matrix[Z];
			float front_dot = Mth::DotProduct(forward,normal);

			Mth::Vector	old_forward = forward;
			forward.RotateToPlane(normal);
			old_forward.RotateToPlane(m_current_normal);

			// angle between front vectors, projected onto faces
			up_dot = Mth::DotProduct(forward, old_forward);
			
			float stick_angle_cosine;
			stick_angle_cosine = cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x4138283e, "Ground_stick_angle_forward"))));
			
			if (front_dot > 0.0f  && up_dot > 0.0f && up_dot < stick_angle_cosine)
			{
				sticking = false;
			}
			else
			{
				// only need to test if snap is downwards
				if (drop_sign > 0.0f)
				{
					// angle between the old plane and the new plane is either the same, or withing the set limits
					// (like, < 60 degrees, or so, see physics.q) now calculate the drop distance, based on the angle
					
					//float max_drop = 60.0f;////last_move.Length() * tanf(angle);
#ifdef __PLAT_NGC__
					float angle = acosf(Mth::Clamp(up_dot, -1.0f, 1.0f));
#else
					float angle = acosf(up_dot);
#endif // __PLAT_NGC__

					Mth::Vector	last_move = GetObject()->m_pos - m_old_extrap_pos;
					
					float max_drop = last_move.Length() * tanf(angle);
					
					float min_drop = GetPhysicsFloat(CRCD(0x899ba3d0, "Physics_Ground_Snap_Down"));				
                    // if (mp_state_component->m_skater_flags[SKITCHING].Get())
					// {
						// min_drop = GetPhysicsFloat(CRCD(0x20df7e33, "Physics_Ground_Snap_Down_Skitching"));				
					// }

					if (max_drop < min_drop)
					{
						max_drop = min_drop;
					}
					
					if (drop_dist > max_drop)
					{
						sticking = false;
					}
				}
				
				if (sticking)
				{    
					//new_normal(normal);
					m_current_normal = normal;	  										// remember this, for detecting if it changes
		
					GetObject()->m_matrix[Y] = normal;
					GetObject()->m_matrix.OrthoNormalizeAbout(Y);									// set regular normal immediately
				}		 
			}																		  
		}
					
		if (sticking)
		{	
			// if there is a collision, then snap to it
			GetObject()->m_pos = feeler.GetPoint();
		}
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CRailNode*	CSkaterNonLocalNetLogicComponent::travel_on_rail( CRailNode* start_node, float frame_length )
{
	bool on_rail;

	Dbg_Assert( start_node );

	on_rail = true;

	CRailManager* p_rail_man = Mdl::Skate::Instance()->GetRailManager();

	const CRailNode* pStart = start_node;
	const CRailNode* pEnd = pStart->GetNextLink();
	if( pEnd == NULL )
	{
		Dbg_MsgAssert(pEnd,("NonLocal Skater on Rail node (%d) with no next link\n",start_node->GetNode()));
		return NULL;
	}
	
	//const CRailNode* pFrom = pStart;
	const CRailNode* pOnto = NULL;
	
	Mth::Vector	dir = p_rail_man->GetPos(pEnd) - p_rail_man->GetPos(pStart);
	float segment_length = dir.Length();
	dir *= (1.0f / segment_length);

	// sign is which way we are going along the rail
	//float old_sign = Mth::Sgn(Mth::DotProduct(dir, GetObject()->m_vel));

	// Get gravity force 	
	Mth::Vector gravity(0.0f, GetPhysicsFloat(CRCD(0xd1f46992, "Physics_Rail_Gravity")), 0.0f);

	// Project gravity onto the line we are on
	gravity.ProjectToNormal(dir);
	GetObject()->m_vel += gravity * frame_length;
	
	// sign is which way we are going along the rail
	float sign = Mth::Sgn(Mth::DotProduct(dir, GetObject()->m_vel));
	
	// check to see if we are on the last segment of the rail
	// this logic could be folded into the logic below but it's perhps clearer to have it here
	bool last_segment = false;
	if (sign < 0.0f)
	{
		if (pStart->GetPrevLink() && pStart->GetPrevLink()->IsActive())
		{
			Mth::Vector v1, v2;
			v1 = p_rail_man->GetPos(pEnd) - p_rail_man->GetPos(pStart);
			v2 = p_rail_man->GetPos(pStart) - p_rail_man->GetPos(pStart->GetPrevLink());
			v1[Y] = 0.0f;
			v2[Y] = 0.0f;
			v1.Normalize();
			v2.Normalize();
			float dot = Mth::DotProduct(v1, v2);
			if (dot < cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x76c1da15, "Rail_Corner_Leave_Angle")))))
			{
				// there is more, but angle is too sharp
				last_segment = true;
			}
			else
			{
				pOnto = pStart->GetPrevLink();
			}
		}
		else
		{
			last_segment = true;			
		}
	}
	else
	{
		if (pEnd && pEnd->GetNextLink() && pEnd->GetNextLink()->IsActive())
		{
			Mth::Vector  v1,v2;
			v1 = p_rail_man->GetPos(pStart) - p_rail_man->GetPos(pEnd);
			v2 = p_rail_man->GetPos(pEnd) - p_rail_man->GetPos(pEnd->GetNextLink());
			v1[Y] = 0.0f;
			v2[Y] = 0.0f;
			v1.Normalize();
			v2.Normalize();
			float dot = Mth::DotProduct(v1,v2);
			if (dot < cosf(Mth::DegToRad(GetPhysicsFloat(CRCD(0x76c1da15,"Rail_Corner_Leave_Angle")))))
			{
				// there is more, but angle is too sharp
				last_segment = true;
			}
			else
			{
				pOnto = pEnd;
			}
		}
		else
		{
			last_segment = true;
		}
	}

	// Now we need to see if we have gone beyond the end of the rail segment
	
	float extra_dist = 0.0f;
		
	float length_along;
	if (sign < 0.0f)
	{
		// going backwards, so it's the distance from the end of the segment
		length_along = (GetObject()->m_pos - p_rail_man->GetPos(pEnd)).Length();
	}
	else
	{
		// going forwards, so it's the distance from the start
		length_along = (GetObject()->m_pos - p_rail_man->GetPos(pStart)).Length();
	}
	
	if (length_along > segment_length + 0.1f)	// 0.1 inch, so we don't get stuck
	{
		// remember this, so we can move along next segment
		extra_dist = length_along - segment_length;
		
		// gone off the end of the segment
		if (sign < 0.0f)
		{
			if (pStart->GetPrevLink()
				&& pStart->GetPrevLink()->IsActive()
				&& Rail_ValidInEditor(p_rail_man->GetPos(pStart), p_rail_man->GetPos(pStart->GetPrevLink())))
			{
				if (!last_segment)
				{
					// go onto previous segment
					GetObject()->m_pos = p_rail_man->GetPos(pStart);
					start_node = (CRailNode*) pStart->GetPrevLink();
					//set_terrain(start_node->GetTerrain());
					//maybe_trip_rail_trigger(TRIGGER_SKATE_ONTO);
				}		   
				else
				{
					//skate_off_rail(p_rail_man->GetPos(pStart));
					on_rail = false;
				} 		
			}
			else
			{
				//skate_off_rail(p_rail_man->GetPos(pStart));
				on_rail = false;
			}
		}
		else
		{
			if (pEnd->GetNextLink()
				&& pEnd->IsActive()
				&& Rail_ValidInEditor(p_rail_man->GetPos(pEnd), p_rail_man->GetPos(pEnd->GetNextLink())))
			{
				if (!last_segment)
				{
					GetObject()->m_pos = p_rail_man->GetPos(pEnd);
					start_node = (CRailNode*) pEnd;					
					//set_terrain(start_node->GetTerrain());
					//maybe_trip_rail_trigger(TRIGGER_SKATE_ONTO);						
				}		   
				else
				{
					//skate_off_rail(p_rail_man->GetPos(pEnd));
					on_rail = false;
				} 						
			}
			else
			{
				//skate_off_rail(p_rail_man->GetPos(pEnd));
				on_rail = false;
			}
		}		
	}
	
	if( on_rail ) // If still on the rail     GetState() == RAIL)
	{
		// recalculate start, end, dir, as we might be on a new segment
		const CRailNode* pStart = start_node;
		const CRailNode* pEnd = pStart->GetNextLink();
		
		Mth::Vector	dir = p_rail_man->GetPos(pEnd) - p_rail_man->GetPos(pStart);
		dir.Normalize();

		// sign also may have changed, now that we are auto-linking rail segments
		
		// sign is which way we are going along the rail
		float sign = Mth::Sgn(Mth::DotProduct(dir,GetObject()->m_vel));

		//m_rail_time = Tmr::GetTime();
							
		GetObject()->m_vel.RotateToNormal(dir);
		GetObject()->m_vel *= sign;						   						// sign won't be on a new segment

		//float facing_sign = mRail_Backwards ? -sign : sign;
		float facing_sign = sign;
		
		// z is forward
		Mth::Vector target_forward = dir * facing_sign;

		//m_lerping_display_matrix[Z] = Mth::Lerp(m_lerping_display_matrix[Z], target_forward, 0.3f);
		//m_lerping_display_matrix[Z].Normalize(); 
		
		#if 0 // old code
		// m_lerping_display_matrix[Y].Set(0.0f, 1.0f, 0.0f);
		// m_lerping_display_matrix[X] = Mth::CrossProduct(
			// m_lerping_display_matrix[Y],
			// m_lerping_display_matrix[Z]
		// );
		#else
		//m_lerping_display_matrix[X].Set(
			//m_lerping_display_matrix[Z][Z],
			//0.0f,
			//-m_lerping_display_matrix[Z][X],
			//0.0f
		//);
		#endif
		//m_lerping_display_matrix[X].Normalize();
		
		//m_lerping_display_matrix[Y] = Mth::CrossProduct(
			//m_lerping_display_matrix[Z],
			//m_lerping_display_matrix[X]
		//);
		//m_lerping_display_matrix[Y].Normalize();

		// adjust our Z value towards the new value												 
		GetObject()->m_matrix[Z] = target_forward;
		GetObject()->m_matrix[Z].Normalize(); 
		
		#if 0 // old code
		// GetObject()->m_matrix[Y].Set(0.0f, 1.0f, 0.0f);
		// GetObject()->m_matrix[X] = Mth::CrossProduct(GetObject()->m_matrix[Y],GetObject()->m_matrix[Z]);
		#else
		GetObject()->m_matrix[X].Set(
			GetObject()->m_matrix[Z][Z],
			0.0f,
			-GetObject()->m_matrix[Z][X],
			0.0f
		);
		#endif
		GetObject()->m_matrix[X].Normalize();
		
		GetObject()->m_matrix[Y] = Mth::CrossProduct(GetObject()->m_matrix[Z], GetObject()->m_matrix[X]);
		GetObject()->m_matrix[Y].Normalize();

        // This is where we do the actual movement
		// if this makes us bump into the wall or the ground, then we should leave the rail
		GetObject()->m_pos += GetObject()->m_vel * frame_length;			// current movement
		GetObject()->m_pos += extra_dist * target_forward;						// any extra dist from previous segment
	}
	else
	{
		return NULL;
	}

	return start_node;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::extrapolate_rail_position( void )
{
	GameNet::Manager* gamenet_man =  GameNet::Manager::Instance();
	CRailManager* p_rail_man = Mdl::Skate::Instance()->GetRailManager();
	CRailNode* start_node;
	int i, prev_index, most_recent_index, mag_index;
	SPosEvent* p_pos_history;
	unsigned int delta_t, net_lag;
	float coeff, ratio, total_mag, total_ratio, total_coeff, vel_mag, frame_length;
	GameNet::PlayerInfo* local_player;
	Net::Client* client;
	Mth::Vector vel;

	client = gamenet_man->GetClient( 0 );

	p_pos_history = mp_state_history_component->GetPosHistory();
	most_recent_index = ( mp_state_history_component->GetNumPosUpdates() - 1 ) % CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
	prev_index = ( m_last_pos_index + ( CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS - 1 )) % CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
	
	delta_t = (int) ( m_frame_length * Tmr::vRESOLUTION );
	if( gamenet_man->OnServer())
	{
		GameNet::PlayerInfo* player;
		Net::Conn* client_conn;

		player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID());
		Dbg_Assert( player );
		client_conn = player->m_Conn;
		net_lag = client_conn->GetAveLatency();
	}
	else
	{
		Lst::Search< Net::Conn > sh;
		Net::Conn* server_conn;

		server_conn = client->FirstConnection( &sh );
		net_lag = server_conn->GetAveLatency();
	}

	// protect against dbz
	if( delta_t == 0 )
	{
		ratio = 0;
	}
	else
	{
		ratio = (float) ( net_lag - delta_t ) / (float) delta_t;
		if( ratio < 0 )
		{
			ratio = 0;
		}
	}
	
	mag_index = m_num_mags % vMAG_HISTORY_LENGTH;
	vel = m_interp_pos - m_old_interp_pos;

	coeff = 0.0f;
	local_player = gamenet_man->GetLocalPlayer();
	if( local_player )
	{
		Mth::Vector my_vel, their_vel;

		my_vel = local_player->m_Skater->GetVel();
		their_vel = vel;

		my_vel.Normalize();
		their_vel.Normalize();

		// Get the dot product of our movement vectors
		coeff = Mth::DotProduct( my_vel, their_vel );

		// This will result in a value between -1.0f and 1.0f. Adding one will make that
		// between 0 and 2
		coeff += 1.0f;

		// We want a value between 0 and 1, so divide by 2
		coeff /= 2.0f;
	}

	m_mag_history[mag_index] = ( vel.Length() / delta_t ) * 16;
	m_ratio_history[mag_index] = ratio;
	m_extrap_coeff_history[mag_index] = coeff;
	m_num_mags++;

	total_mag = 0;
	total_ratio = 0;
	total_coeff = 0;
	for( i = 0; i < vMAG_HISTORY_LENGTH; i++ )
	{
		if( i >= m_num_mags )
		{
			break;
		}
		total_mag += m_mag_history[i];
		total_ratio += m_ratio_history[i];
		total_coeff += m_extrap_coeff_history[i];
		
	}
	vel_mag = total_mag / i;
	coeff = total_coeff / i;
	ratio = total_ratio / i;
	ratio *= coeff;

	vel.Normalize();
	vel *= vel_mag;
	
	frame_length = m_frame_length * ratio;
	// Sanity check to avoid infinite loops
	if( frame_length > 1.0f )
	{
		frame_length = 1.0f;
	}
	start_node = p_rail_man->GetRailNodeByNodeNumber( p_pos_history[m_last_pos_index].RailNode );
	if( start_node )
	{
		while( frame_length > 0 )
		{
			if( frame_length > m_frame_length )
			{
				start_node = travel_on_rail( start_node, m_frame_length );
			}
			else
			{
				start_node = travel_on_rail( start_node, frame_length );
			}
			
			// If we grinded off a rail, use standard extrapolation instead.
			if( start_node == NULL )
			{
				GetObject()->m_pos = m_interp_pos;
				extrapolate_position();
				break;
			}
			frame_length -= m_frame_length;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CSkaterNonLocalNetLogicComponent::rotate_away_from_wall ( Mth::Vector normal, float &turn_angle )
{
	float lerp;

	lerp = 1.0f;
	// given a wall normal, then calculate the "turn_angle" to rotate the skater by and rotate the matrix and display matrix by this
	// return the angle between the skater and the wall
	// note turn_angle is passed by reference, and is altered !!!!
	
	// given m_right(dot)normal, we should be able to get a nice angle
	float dot_right_normal = Mth::DotProduct(GetObject()->m_matrix[X], normal);

	float angle = acosf(Mth::Clamp(dot_right_normal, -1.0f, 1.0f)); 	

	if (angle > Mth::PI / 2.0f)
	{
		angle -= Mth::PI;
	}
	
	// angle away from the wall
	turn_angle = angle * GetPhysicsFloat(CRCD(0xe07ee1a9, "Wall_Bounce_Angle_Multiplier")) * lerp;
	
	// Rotate the skater so he is at a slight angle to the wall, especially if we are in a right angled corner, where the skater will bounce out

	GetObject()->m_vel.RotateY(turn_angle);
	GetObject()->m_matrix.RotateYLocal(turn_angle);
	
	return angle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::bounce_off_wall ( const Mth::Vector& normal, const Mth::Vector& point )
{
	// Given the normal of the wall, then bounce off it, turning the skater away from the wall
	Mth::Vector col_start, col_end;
	Mth::Vector	forward = GetObject()->m_pos - m_old_extrap_pos;
	Mth::Vector movement = forward;				   		// remember how far we moved
	CFeeler feeler;

    forward.Normalize();
	
	Mth::Vector up_offset = GetObject()->m_matrix[Y] * GetPhysicsFloat(CRCD(0xd4205c9b,"Skater_First_Forward_Collision_Height"));

	float turn_angle;
	float angle = rotate_away_from_wall(normal, turn_angle);

	float min = Mth::DegToRad(GetPhysicsFloat(CRCD(0x1483fd01, "Wall_Bounce_Dont_Slow_Angle"))); 
					 							
	if (Mth::Abs(angle) > min)
	{
		//float old_speed = GetObject()->m_vel.Length();
		
		// The maximum value of Abs(angle) would be PI/2 (90 degrees), so scale the velocity 
		float x = Mth::Abs(angle) - min;
		x /= (Mth::PI / 2.0f) - min;		// get in the range 0 .. 1
		x = 1.0f - x; 						// invert, as we want to stop when angle is 90
		
		GetObject()->m_vel *= x;
	}
	
	// Bit of a patch here to move the skater away from the wall
	// Not needed so much with new sideways collision checks but we keep it in for low ledges.
	// Should perhaps standardize the height so collision checks for side and front but we'd probably still have problems.
	
	GetObject()->m_pos = point - up_offset;
	GetObject()->m_pos += normal * 6.0f;
	
	// Now the majority of cases have been taken care of; we need to see if the skater is going to get stuck in a corner.

	float old_speed = movement.Length();		 					// get how much we moved last time
	Mth::Vector next_movement = GetObject()->m_vel;					// get new direction of velocity
	forward = GetObject()->m_vel;
	forward.Normalize();
	next_movement = forward * old_speed;							// extend by same movment as last time
	
	col_start = GetObject()->m_pos + up_offset;
	col_start += up_offset;
	
	col_end = GetObject()->m_pos + up_offset + next_movement
		+ forward * GetPhysicsFloat(CRCD(0x20102726, "Skater_First_Forward_Collision_Length"));
	
	// Sanity check to make sure we're not using a line that's a whole level long
	if(( col_start - col_end ).Length() > FEET( 20 ))
	{
		return;
	}

	feeler.SetLine( col_start, col_end );
	feeler.SetIgnore( mFD_NON_COLLIDABLE, 0 );
	if( feeler.GetCollision() && GetSkater()->IsLocalClient())
	{
		// Just rotating the skater will lead to another collision, so try just inverting the skater's velocity from it's original and halving it....
		
		// First reverse the rotation, and rotate 180 degrees
		GetObject()->m_vel.RotateY(Mth::DegToRad(180.0f) - turn_angle);
		GetObject()->m_matrix.RotateYLocal(Mth::DegToRad(180.0f) - turn_angle);
		//ResetLerpingMatrix();
		
		GetObject()->m_vel *= 0.5f; 
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::extrapolate_position( void )
{
	GameNet::Manager* gamenet_man =  GameNet::Manager::Instance();
	int i, prev_index, most_recent_index, mag_index;
	SPosEvent* p_pos_history;
	unsigned int delta_t, net_lag;
	float coeff, ratio, total_mag, total_ratio, total_coeff, vel_mag;
	Mth::Vector extrap_pos;
	GameNet::PlayerInfo* local_player;
	Mth::Vector vel;
	Net::Client* client;

	client = gamenet_man->GetClient( 0 );
		
	p_pos_history = mp_state_history_component->GetPosHistory();
	most_recent_index = ( mp_state_history_component->GetNumPosUpdates() - 1 ) % CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
	prev_index = ( m_last_pos_index + ( CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS - 1 )) % CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
	
	delta_t = (int) ( m_frame_length * Tmr::vRESOLUTION );
	if( gamenet_man->OnServer())
	{
		GameNet::PlayerInfo* player;
		Net::Conn* client_conn;

		player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID());
		Dbg_Assert( player );
		client_conn = player->m_Conn;
		net_lag = client_conn->GetAveLatency();
	}
	else
	{
		Lst::Search< Net::Conn > sh;
		Net::Conn* server_conn;

		server_conn = client->FirstConnection( &sh );
		net_lag = server_conn->GetAveLatency();
	}

	// protect against dbz
	if( delta_t == 0 )
	{
		ratio = 0;
	}
	else
	{
		ratio = (float) ( net_lag - delta_t ) / (float) delta_t;
		if( ratio < 0 )
		{
			ratio = 0;
		}
	}
	
	mag_index = m_num_mags % vMAG_HISTORY_LENGTH;
	vel = m_interp_pos - m_old_interp_pos;

	coeff = 0.0f;
	local_player = gamenet_man->GetLocalPlayer();
	if( local_player && local_player->m_Skater )
	{
		Mth::Vector my_vel, their_vel;

		my_vel = local_player->m_Skater->GetVel();
		their_vel = vel;

		my_vel.Normalize();
		their_vel.Normalize();

		// Get the dot product of our movement vectors
		coeff = Mth::DotProduct( my_vel, their_vel );

		// This will result in a value between -1.0f and 1.0f. Adding one will make that
		// between 0 and 2
		coeff += 1.0f;

		// We want a value between 0 and 1, so divide by 2
		coeff /= 2.0f;
	}
	else
	{
		coeff = 0.0f;
	}

	m_mag_history[mag_index] = ( vel.Length() / delta_t ) * 16;
	m_ratio_history[mag_index] = ratio;
	m_extrap_coeff_history[mag_index] = coeff;
	m_num_mags++;

	total_mag = 0;
	total_ratio = 0;
	total_coeff = 0;
	for( i = 0; i < vMAG_HISTORY_LENGTH; i++ )
	{
		if( i >= m_num_mags )
		{
			break;
		}
		total_mag += m_mag_history[i];
		total_ratio += m_ratio_history[i];
		total_coeff += m_extrap_coeff_history[i];
		
	}
	vel_mag = total_mag / i;
	coeff = total_coeff / i;
	ratio = total_ratio / i;
	ratio *= coeff;

	vel.Normalize();
	vel *= vel_mag;
	
	extrap_pos = GetObject()->m_pos + ( vel * ratio );
	extrap_pos[Y] = GetObject()->m_pos[Y];
	GetObject()->m_pos = extrap_pos;
	
	Mth::Vector up_offset = GetObject()->m_matrix[Y] * GetPhysicsFloat(CRCD(0xd4205c9b, "Skater_First_Forward_Collision_Height"));

	Mth::Vector col_start = m_interp_pos + up_offset;
	Mth::Vector col_end = GetObject()->m_pos + up_offset;

	CFeeler feeler;

	// Sanity check to make sure we're not using a line that's a whole level long
	if(( col_start - col_end ).Length() > FEET( 10 ))
	{
		return;
	}

	feeler.SetLine( col_start, col_end );
	feeler.SetIgnore( mFD_NON_COLLIDABLE, 0 );

	if( feeler.GetCollision())
	{
		bounce_off_wall( feeler.GetNormal(), feeler.GetPoint());
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::extrapolate_client_position (   )
{
	if( mp_state_history_component->GetNumPosUpdates() >= CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS )
	{
		if( mp_state_component->GetState() == RAIL )
		{
			extrapolate_rail_position();
		}
		else
		{
			extrapolate_position();
			snap_to_ground();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::interpolate_client_position (   )
{
	GameNet::Manager* gamenet_man =  GameNet::Manager::Instance();
	Net::Client* client;
	
	SPosEvent* p_pos_history = mp_state_history_component->GetPosHistory();

	if(( client = gamenet_man->GetClient( 0 )))
	{   
		// Wait for the object update buffer to fill up
		if( mp_state_history_component->GetNumPosUpdates() >= CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS )
		{
			unsigned int cur_time;
			int i, prev_index, start_index, total_t, delta_t;
			float ratio;
			Mth::Vector delta_pos;
            
			// This will be true only if we've never performed an interpolation
			if( m_client_initial_update_time == 0 )
			{
				//Dbg_Printf( "Resync'd Skater %d\n", GetID() );
				// start off at the latest update and offset ourselves backwards in time from there
				start_index = ( mp_state_history_component->GetNumPosUpdates() - 1 ) % CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
				
				m_client_initial_update_time = client->m_Timestamp;
				// impose a lag of N frames
				m_server_initial_update_time = p_pos_history[start_index].GetTime() - Tmr::VBlanksF( vNUM_LAG_FRAMES );
				m_last_pos_index = start_index;
                
				//Dbg_Printf( "(%d) server initial time = %d\n", nonlocalnet->m_FrameCounter, (int) m_server_initial_update_time );
				//Dbg_Printf( "(%d) nonlocalnet initial time = %d\n", nonlocalnet->m_FrameCounter, (int) m_client_initial_update_time );
				
                GetObject()->SetMatrix( p_pos_history[start_index].Matrix );
				GetObject()->SetDisplayMatrix( p_pos_history[start_index].Matrix );
				GetObject()->SetPos(p_pos_history[start_index].Position);
				GetObject()->SetTeleported(); 
				GetObject()->m_vel.Set(0.0f, 0.0f, 0.0f);

				// We don't want to actually render the skaters until we have buffered
				// up their position updates and are ready to start interpolating. Now we have
				// that data, so add them to the world
				if( !GetSkater()->IsInWorld())
				{
					GameNet::PlayerInfo* player;
                    
					GetSkater()->AddToCurrentWorld();
					Script::RunScript( "NetIdle", NULL, GetObject() );
					//Hide( false );
					player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID() );
					Dbg_Assert( player );
					
                    // We're just adding them to the world.  If they were standing still,
					// UberFrig would not have been done. So let's do it here
					setup_brightness_and_shadow();
				}
			}
			else
			{   
				Mth::SlerpInterpolator slerp;

				Mth::Matrix interp_matrix;
				int most_recent_index, flag;
				GameNet::PlayerInfo* player;
				
				// Maybe a little bit overkill, but this will definitely set skaters that we can
				// see in the level to be "fully in", which triggers things like their names in
				// the score list
				player = gamenet_man->GetPlayerByObjectID( GetObject()->GetID() );
				if( !player->IsFullyIn())
				{
					player->MarkAsFullyIn();
				}

				start_index = mp_state_history_component->GetNumPosUpdates() % CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
				most_recent_index = ( mp_state_history_component->GetNumPosUpdates() - 1 ) % CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
				
				cur_time = m_server_initial_update_time + ( client->m_Timestamp - m_client_initial_update_time );

				// If we've gone past the most recent update, slow down a little to allow updates
				// to catch up
				//Dbg_Printf( "(%d) NonLocalNet Timestamp %d\n", nonlocalnet->m_FrameCounter, nonlocalnet->m_Timestamp );
				if( cur_time > p_pos_history[most_recent_index].GetTime())
				{
					// If we have some major catching up to do (usually at the beginning
					// when there are long pauses after loading) catch up in one step
					if(( cur_time - p_pos_history[most_recent_index].GetTime()) > Tmr::Seconds( 1 ))
					{
						//Dbg_Printf( "************************ (%d) Major catchup\n", client->m_FrameCounter );
						m_server_initial_update_time -= ( cur_time - p_pos_history[most_recent_index].GetTime());
					}
					else
					{
						//Dbg_Printf( "************************ (%d) (%d) Slowdown\n", GetObject()->GetID(), client->m_FrameCounter );
						m_server_initial_update_time -= Tmr::VBlanks( 1 );
					}
					
					cur_time = m_server_initial_update_time + ( client->m_Timestamp - m_client_initial_update_time );
					//Dbg_Printf( "- Slowdown\n" );
				}

				i = start_index;
				do							   
				{   
					if( cur_time <= p_pos_history[i].GetTime()) 
					{						 
						static int lag = 0;
						
						m_last_pos_index = i;
						lag = ( start_index + (CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS - i )) % CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
						
						// We probably also should adjust if we get too far behind
						if( lag >= 10 )
						{   
							//Dbg_Printf( "************************ (%d) (%d) +7 Catchup. Lag : %d\n", client->m_FrameCounter, GetObject()->GetID(), lag );
							m_server_initial_update_time += Tmr::VBlanks( 10 );
						}
						else if( lag == 0 )
						{
							//Dbg_Printf( "************************ (%d) Resync'ing skater %d\n", client->m_FrameCounter, GetObject()->GetID() );
							GetSkater()->Resync();
						}

						prev_index = ( i + ( CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS - 1 )) % CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
						
						delta_t = cur_time - p_pos_history[prev_index].GetTime();
						total_t = p_pos_history[i].GetTime() - p_pos_history[prev_index].GetTime();
						
						ratio = (float) delta_t / (float) total_t;
						
						delta_pos = p_pos_history[i].Position - p_pos_history[prev_index].Position;

						m_old_interp_pos = m_interp_pos;
						
						// Slerp between the two matrices
						Mth::Matrix t1, t2;
						Mth::Vector diff;

						diff = p_pos_history[i].Matrix[Z] - p_pos_history[prev_index].Matrix[Z];
						// Don't slerp if the matrices are way different. Just stick with the matrix
						// we have until the switch is made. 
						if( diff.Length() > 0.75f )
						{
							GetObject()->SetMatrix( p_pos_history[prev_index].Matrix );
							GetObject()->SetDisplayMatrix( p_pos_history[prev_index].Matrix );
						}
						else
						{
							t1 = p_pos_history[prev_index].Matrix;
							t2 = p_pos_history[i].Matrix;
							slerp.setMatrices( &t1, &t2 );
							slerp.getMatrix( &interp_matrix, ratio );
							
							GetObject()->SetMatrix( interp_matrix );
							GetObject()->SetDisplayMatrix( interp_matrix );
						}
						
						if( delta_pos.Length() > FEET( 50 ))
						{
							GetObject()->m_pos = p_pos_history[i].Position;
							GetObject()->m_vel = Mth::Vector( 0, 0, 0 );
							GetObject()->SetTeleported();
						}
						else
						{
							int idx, mag_index;
							Mth::Vector total_vel;

							GetObject()->m_pos = p_pos_history[prev_index].Position + ( delta_pos * ratio );
							GetObject()->m_vel = ( GetObject()->m_pos - m_old_interp_pos ) / m_frame_length; // do_nonlocalnet_position_update, calculating speed
						
							mag_index = m_num_mags % vMAG_HISTORY_LENGTH;

							// Smooth out changes in velocity
							m_vel_history[mag_index] = GetObject()->m_vel;
							for( idx = 0; idx < vMAG_HISTORY_LENGTH; idx++ )
							{
								if( idx >= m_num_mags )
								{
									break;
								}
								
								total_vel += m_vel_history[idx];
							}

							if( idx > 0 )
							{
								GetObject()->m_vel = total_vel / idx;
							}
						}

						m_interp_pos = GetObject()->m_pos;

						// Apply the state/flag data from this PosEvent
						mp_state_component->m_state = static_cast< EStateType >( p_pos_history[i].State );
						mp_state_component->m_doing_trick = p_pos_history[i].DoingTrick;
						mp_state_component->m_terrain = p_pos_history[i].Terrain;
						mp_state_component->m_physics_state = p_pos_history[i].Walking ? WALKING : SKATING;
						mp_state_component->m_driving = p_pos_history[i].Driving;
						mp_endrun_component->SetFlags(  p_pos_history[i].EndRunFlags );
						if( p_pos_history[i].EndRunFlags & CSkaterEndRunComponent::FINISHED_END_OF_RUN )
						{
							//Dbg_Printf( "*** At END OF RUN ***\n" );
						}
						
						for( flag = 0; flag < NUM_ESKATERFLAGS; flag++ )
						{
							mp_state_component->m_skater_flags[flag].Set(p_pos_history[i].SkaterFlags.Test( flag ));
						}
						if( mp_state_component->m_skater_flags[SPINE_PHYSICS].Get())
						{
							mp_state_component->m_spine_vel = GetObject()->m_vel;
						}
						else
						{
							mp_state_component->m_spine_vel.Set(0.0f, 0.0f, 0.0f);
						}

						break;
					}
					i = ( i + 1 ) % CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
				} while( i != start_index );
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterNonLocalNetLogicComponent::do_client_animation_update(   )
{
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
	Net::Client* client;

	Dbg_Assert( mp_animation_component );

	if(( client = gamenet_man->GetClient( 0 )))
	{   
		// Wait for the object update buffer to fill up
		if( mp_state_history_component->GetNumPosUpdates() >= CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS )
		{
			unsigned int cur_time;
			int i, start_index;
			SAnimEvent* event;
			int most_recent_index;
            
			start_index = mp_state_history_component->GetNumAnimUpdates() % CSkaterStateHistoryComponent::vNUM_ANIM_HISTORY_ELEMENTS;
			most_recent_index = ( mp_state_history_component->GetNumAnimUpdates() - 1 ) % CSkaterStateHistoryComponent::vNUM_ANIM_HISTORY_ELEMENTS;
			
			cur_time = m_server_initial_update_time + ( client->m_Timestamp - m_client_initial_update_time );

			i = start_index;
			bool updated = false;
			do							   
			{   
				event = &mp_state_history_component->GetAnimHistory()[i];
				//Dbg_Printf( "(%d) CurTime: %d EventTime[%d]: %d\n", client->m_FrameCounter, cur_time, event->m_MsgId, event->GetTime());
				if( ( event->GetTime() > m_last_anm_update_time ) &&
					( event->GetTime() <= cur_time ))
				{   
					updated = true;
					switch( event->m_MsgId )
					{
						case GameNet::MSG_ID_PRIM_ANIM_START:
						{
							//Dbg_Printf( "(%d) Playing primary anim: 0x%x Speed: %f Duration: %d Time:%d\n", client->m_Timestamp, event->m_Asset, event->m_Speed, event->m_Duration, event->GetTime());
							mp_animation_component->PlayPrimarySequence( event->m_Asset, false, event->m_StartTime, event->m_EndTime,
																		 (Gfx::EAnimLoopingType) event->m_LoopingType, 
																		 event->m_BlendPeriod, event->m_Speed );
	
							break;
						}
						case GameNet::MSG_ID_SET_WOBBLE_TARGET:
						{
							mp_animation_component->SetWobbleTarget( event->m_Alpha, false );
							break;
						}
						case GameNet::MSG_ID_ROTATE_SKATEBOARD:
						{
							mp_flip_and_rotate_component->RotateSkateboard( event->m_ObjId, event->m_Rotate, 0, false );
							break;
						}
						case GameNet::MSG_ID_SET_WOBBLE_DETAILS:
						{
							Gfx::SWobbleDetails wobbleDetails;
				
							wobbleDetails.wobbleAmpA = event->m_WobbleAmpA;
							wobbleDetails.wobbleAmpB = event->m_WobbleAmpB;
							wobbleDetails.wobbleK1 = event->m_WobbleK1;
							wobbleDetails.wobbleK2 = event->m_WobbleK2;
							wobbleDetails.spazFactor = event->m_SpazFactor;

							mp_animation_component->SetWobbleDetails( wobbleDetails, false );
							break;
						}
						case GameNet::MSG_ID_SET_LOOPING_TYPE:
						{
							mp_animation_component->SetLoopingType( (Gfx::EAnimLoopingType) event->m_LoopingType, false );
							break;
						}
						case GameNet::MSG_ID_SET_HIDE_ATOMIC:
						{
						   	mp_model_component->HideGeom( event->m_Asset, event->m_Hide, false );
							break;
						}
						case GameNet::MSG_ID_SET_ANIM_SPEED:
						{
							mp_animation_component->SetAnimSpeed( event->m_Speed, false );
							break;
						}
						case GameNet::MSG_ID_FLIP_ANIM:
						{
							mp_animation_component->FlipAnimation( event->m_ObjId, event->m_Flipped, 0, false );
							break;
						}
						case GameNet::MSG_ID_ROTATE_DISPLAY:
						{
							mp_model_component->m_display_rotation_offset.Set(0,30,0);
							Tmr::Time start_time=Tmr::ElapsedTime(0);
			
							if( event->m_Flags & 1 )
							{
								mp_model_component->mpDisplayRotationInfo[0].SetUp(event->m_Duration,
															   start_time,
															   event->m_StartAngle,
															   event->m_DeltaAngle,
															   event->m_SinePower,
															   event->m_HoldOnLastAngle);
							}	
							if( event->m_Flags & 2 )
							{
								mp_model_component->mpDisplayRotationInfo[1].SetUp(event->m_Duration,
															   start_time,
															   event->m_StartAngle,
															   event->m_DeltaAngle,
															   event->m_SinePower,
															   event->m_HoldOnLastAngle);
							}	
							if( event->m_Flags & 4 )
							{
								mp_model_component->mpDisplayRotationInfo[2].SetUp(event->m_Duration,
															   start_time,
															   event->m_StartAngle,
															   event->m_DeltaAngle,
															   event->m_SinePower,
															   event->m_HoldOnLastAngle);
							}	
							break;
						}
						case (char) GameNet::MSG_ID_CLEAR_ROTATE_DISPLAY:
						{
							mp_model_component->mpDisplayRotationInfo[0].Clear();
							mp_model_component->mpDisplayRotationInfo[1].Clear();
							mp_model_component->mpDisplayRotationInfo[2].Clear();
							break;
						}
						case GameNet::MSG_ID_CREATE_SPECIAL_ITEM:
						{
							CSpecialItemComponent* pSpecialItemComponent = GetSpecialItemComponentFromObject(GetObject());
							Dbg_MsgAssert( pSpecialItemComponent, ( "No special item component?" ) );

							Script::CStruct* pSpecialItemParams = Script::GetStructure( event->m_Asset, Script::ASSERT );
							CCompositeObject* pSpecialItemObject = pSpecialItemComponent->CreateSpecialItem( event->m_Index, pSpecialItemParams );
								
							Script::CStruct* pLockParams = new Script::CStruct;
							// pass along any bone or offset parameters...
							//pLockParams->AppendStructure( pParams );
							pLockParams->AddChecksum( CRCD(0xcab94088,"bone"), event->m_Bone );
							pLockParams->AddChecksum( CRCD(0x40c698af,"id"), GetObject()->GetID() );
								
							// component-based
							CLockObjComponent* pLockObjComponent = GetLockObjComponentFromObject( pSpecialItemObject );
							Dbg_MsgAssert( pLockObjComponent, ( "No lockobj component" ) );
							pLockObjComponent->InitFromStructure( pLockParams );				
												
							delete pLockParams;
						}
						break;

						case GameNet::MSG_ID_DESTROY_SPECIAL_ITEM:
						{
							CSpecialItemComponent* pSpecialItemComponent = GetSpecialItemComponentFromObject(GetObject());
                            pSpecialItemComponent->DestroySpecialItem( event->m_Index );
							break;
						}
					}
				}
				i = ( i + 1 ) % CSkaterStateHistoryComponent::vNUM_ANIM_HISTORY_ELEMENTS;
			} while( i != start_index );
			
			/*static Tmr::Time s_time = 0;

			if( !updated )
			{
				if(( Tmr::GetTime() - s_time ) > 1000 )
				{
					i = start_index;
					do							   
					{   
						event = &mp_state_history_component->GetAnimHistory()[i];
						Dbg_Printf( "(%d) CurTime: %d EventTime[%d]: %d\n", client->m_FrameCounter, cur_time, event->m_MsgId, event->GetTime());
						i = ( i + 1 ) % CSkaterStateHistoryComponent::vNUM_ANIM_HISTORY_ELEMENTS;
					} while( i != start_index );
					s_time = Tmr::GetTime();
				}
			}*/
            
			m_last_anm_update_time = cur_time;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
					
void CSkaterNonLocalNetLogicComponent::setup_brightness_and_shadow (   )
{
	// logic extracted from CAdjustComponent::uber_frig which is judged to be required by nonlocal clients
	
	CFeeler feeler;
	
	feeler.m_start = GetObject()->m_pos;
	feeler.m_end = GetObject()->m_pos;

	// Very minor adjustment to move origin away from vert walls
	feeler.m_start += GetObject()->m_matrix[Y] * 0.001f;
	
	feeler.m_start[Y] += 8.0f;
	feeler.m_end[Y] -= FEET(400);
		   
	if (!feeler.GetCollision()) return;
	
	if (mp_state_component->GetState() != RAIL && feeler.IsBrightnessAvailable())
	{
		Nx::CModelLights *p_model_lights = mp_model_component->GetModel()->GetModelLights();
		if (p_model_lights)
		{
			p_model_lights->SetBrightness(feeler.GetBrightness());
		} 
		else
		{
			// Garrett: This should move to CModel eventually
			Nx::CLightManager::sSetBrightness(feeler.GetBrightness());
		}
	}
	
	// Store these values off for the simple shadow calculation.
	mp_shadow_component->SetShadowPos(feeler.GetPoint());
	mp_shadow_component->SetShadowNormal(feeler.GetNormal()); 
}

}


================================================
FILE: Code/Sk/Components/SkaterNonLocalNetLogicComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterNonLocalNetLogicComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/11/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERNONLOCALNETLOGICCOMPONENT_H__
#define __COMPONENTS_SKATERNONLOCALNETLOGICCOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERNONLOCALNETLOGIC CRCD(0x850eef66, "SkaterNonLocalNetLogic")

#define		GetSkaterNonLocalNetLogicComponent() ((Obj::CSkaterNonLocalNetLogicComponent*)GetComponent(CRC_SKATERNONLOCALNETLOGIC))
#define		GetSkaterNonLocalNetLogicComponentFromObject(pObj) ((Obj::CSkaterNonLocalNetLogicComponent*)(pObj)->GetComponent(CRC_SKATERNONLOCALNETLOGIC))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSkaterStateHistoryComponent;
	class CSkaterCorePhysicsComponent;
	class CSkaterStateComponent;
	class CSkaterEndRunComponent;
	class CSkaterFlipAndRotateComponent;
	class CShadowComponent;
	class CRailNode;
	
enum
{
	vMAG_HISTORY_LENGTH = 20
};

class CSkaterNonLocalNetLogicComponent : public CBaseComponent
{
public:
    CSkaterNonLocalNetLogicComponent();
    virtual ~CSkaterNonLocalNetLogicComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							Resync();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
private:
	void							setup_brightness_and_shadow (   );
	void							interpolate_client_position (   );
	void							extrapolate_client_position (   );
	void							extrapolate_rail_position( void );
	CRailNode*						travel_on_rail( CRailNode* start_node, float frame_length );
	float 							rotate_away_from_wall( Mth::Vector normal, float &turn_angle );
	void 							bounce_off_wall( const Mth::Vector& normal, const Mth::Vector& point );
	void 							snap_to_ground( void  );
	void							extrapolate_position( void );
	void							do_client_animation_update (   );
	
private:
 	Tmr::Time						m_client_initial_update_time;
	//Tmr::Time						m_client_initial_anm_update_time;
	Tmr::Time						m_server_initial_update_time;
	//Tmr::Time						m_server_initial_anm_update_time;
	Tmr::Time						m_last_anm_update_time;
	int								m_last_pos_index;
	
	Mth::Vector						m_interp_pos;
	Mth::Vector						m_old_interp_pos;
	Mth::Vector						m_old_extrap_pos;
	Mth::Vector						m_current_normal;
	float							m_mag_history[vMAG_HISTORY_LENGTH];
	float							m_ratio_history[vMAG_HISTORY_LENGTH];
	float							m_extrap_coeff_history[vMAG_HISTORY_LENGTH];
	Mth::Vector						m_vel_history[vMAG_HISTORY_LENGTH];
	int								m_num_mags;
	
	float							m_frame_length;
	
	bool							m_last_driving;
	
	CSkaterStateHistoryComponent*	mp_state_history_component;
	CAnimationComponent*			mp_animation_component;
	CModelComponent*				mp_model_component;
	CSkaterStateComponent*			mp_state_component;
	CSkaterEndRunComponent*			mp_endrun_component;
	CSkaterFlipAndRotateComponent*	mp_flip_and_rotate_component;
    CShadowComponent*				mp_shadow_component;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterPhysicsControlComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterPhysicsControlComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/7/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef TESTING_GUNSLINGER
#include 
#endif

#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterPhysicsControlComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterPhysicsControlComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterPhysicsControlComponent::CSkaterPhysicsControlComponent() : CBaseComponent()
{
	SetType( CRC_SKATERPHYSICSCONTROL );
	
	mp_core_physics_component = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterPhysicsControlComponent::~CSkaterPhysicsControlComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterPhysicsControlComponent::InitFromStructure( Script::CStruct* pParams )
{
	Dbg_MsgAssert(GetObject()->GetType() == SKATE_TYPE_SKATER, ("CSkaterPhysicsControlComponent added to non-skater composite object"));
	
	m_physics_suspended = false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterPhysicsControlComponent::Finalize(  )
{
	mp_core_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
	mp_state_component = GetSkaterStateComponentFromObject(GetObject());
	
	Dbg_Assert(mp_core_physics_component);
	Dbg_Assert(mp_state_component);
	
	mp_state_component->m_physics_state = NO_STATE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterPhysicsControlComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterPhysicsControlComponent::Update()
{
	m_restarted_this_frame = false;
	
	// SkaterPhysicsControlComponent is inactive when physics is NOT suspended.  When physics is suspended SkaterPhysicsControlComponent handles
	// setting the skater's display matrix.
	if (!m_physics_suspended) return;
	
	switch (mp_state_component->m_physics_state)
	{
		case SKATING:
			GetObject()->SetDisplayMatrix(mp_core_physics_component->m_lerping_display_matrix);
			break;
		
		case WALKING:
			GetObject()->SetDisplayMatrix(GetObject()->GetMatrix());
			break;
			
		default:
			Dbg_MsgAssert(false, ("No skater physics state switched on before first skater update"));
	}
	
	// If the skater is unsuspended, the physics components will unsuspend as well.  We must check for this and resuspend
	if (!mp_core_physics_component->IsSuspended())
	{
		suspend_physics(true);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterPhysicsControlComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | PausePhysics | PausePhysics will stop all programmatic movement and rotation of the skater
		case CRCC(0x3522d612, "PausePhysics"):
			if (!m_physics_suspended)
			{
				suspend_physics(true);
			}
			break;
			
        // @script | UnPausePhysics | 
		case CRCC(0x595627c, "UnPausePhysics"):
			if (m_physics_suspended)
			{
				suspend_physics(false);
			}
			break;
			
		case CRCC(0x57bfbae8, "Walking"):
			return IsWalking() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		case CRCC(0xf2813ee5, "Skating"):
			return IsSkating() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		case CRCC(0xee584cbc, "Driving"):
			return IsDriving() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
		case CRCC(0xb32e9eae, "SetDriving"):
			mp_state_component->m_driving = true;
			break;
			
		case CRCC(0x953a7b6d, "UnsetDriving"):
			mp_state_component->m_driving = false;
			break;
			
		case CRCC(0x9569d5b1, "GetTimeSincePhysicsSwitch"):
			pScript->GetParams()->AddFloat(CRCD(0x27f48e93, "TimeSincePhysicsSwitch"), Tmr::ElapsedTime(m_physics_state_switch_time_stamp) * (1.0f / 1000.0f));
			break;
			
		case CRCC(0xa887285a, "GetPreviousPhysicsStateDuration"):
			pScript->GetParams()->AddFloat(CRCD(0x24d061ba, "PreviousPhysicsStateDuration"), m_previous_physics_state_duration * (1.0f / 1000.0f));
			break;
			
		case CRCC(0x5c038f9b, "SkaterPhysicsControl_SwitchWalkingToSkating"):
			switch_walking_to_skating();
			break;
			
		case CRCC(0x6e8e39b3, "SkaterPhysicsControl_SwitchSkatingToWalking"):
			switch_skating_to_walking();
			break;
			
		case CRCC(0x9366a509, "SetBoardMissing"):
			m_board_missing = true;
			break;
			
		case CRCC(0x4830e80, "UnsetBoardMissing"):
			m_board_missing = false;
			break;
		
		case CRCC(0xd5a9f889, "IsBoardMissing"):
			return m_board_missing ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			
#		ifdef TESTING_GUNSLINGER
		case CRCC(0x14c4f16b,"SkaterPhysicsControl_SwitchWalkingToRiding"):
			switch_walking_to_riding();
			break;

		case CRCC(0x82604c1e,"SkaterPhysicsControl_SwitchRidingToWalking"):
			switch_riding_to_walking();
			break;
#		endif

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterPhysicsControlComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterPhysicsControlComponent::GetDebugInfo"));
	
	p_info->AddChecksum("m_physics_suspended", m_physics_suspended ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	p_info->AddInteger("m_physics_state", mp_state_component->m_physics_state);

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterPhysicsControlComponent::suspend_physics ( bool suspend )
{
	m_physics_suspended = suspend;
	
	// If the skater is suspended, we don't want to unsuspend the physics components.  We only want to change the CSkaterPhysicsControlComponent state.
	if (!suspend && GetObject()->IsSuspended()) return;
    
	switch (mp_state_component->m_physics_state)
	{
		case SKATING:
			Dbg_Assert(mp_core_physics_component);
			Dbg_Assert(GetSkaterRotateComponentFromObject(GetObject()));
			Dbg_Assert(GetSkaterAdjustPhysicsComponentFromObject(GetObject()));
			Dbg_Assert(GetSkaterFinalizePhysicsComponentFromObject(GetObject()));
			
			mp_core_physics_component->Suspend(suspend);
			GetSkaterRotateComponentFromObject(GetObject())->Suspend(suspend);
			GetSkaterAdjustPhysicsComponentFromObject(GetObject())->Suspend(suspend);
			GetSkaterFinalizePhysicsComponentFromObject(GetObject())->Suspend(suspend);
			break;

		case WALKING:
			Dbg_Assert(GetWalkComponentFromObject(GetObject()));
			
			GetWalkComponentFromObject(GetObject())->Suspend(suspend);
			break;
			
		default:
			Dbg_Assert(false);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterPhysicsControlComponent::switch_walking_to_skating (   )
{
	if (mp_state_component->m_physics_state == SKATING) return;
	
	m_previous_physics_state_duration = Tmr::ElapsedTime(m_physics_state_switch_time_stamp);
	m_physics_state_switch_time_stamp = Tmr::GetTime();
	
	CWalkComponent* p_walk_component = GetWalkComponentFromObject(GetObject());
	CCompositeObject* p_skater_cam = get_skater_camera();
	CSkaterCameraComponent* p_skater_camera_component = GetSkaterCameraComponentFromObject(p_skater_cam);
	CWalkCameraComponent* p_walk_camera_component = GetWalkCameraComponentFromObject(p_skater_cam);
	
	Dbg_Assert(p_walk_component);
	Dbg_Assert(p_skater_camera_component);
	Dbg_Assert(p_walk_camera_component);
	
	// switch off walking
	
	p_walk_component->CleanUpWalkState();
	p_walk_component->Suspend(true);
	
	p_walk_camera_component->Suspend(true);
	
	// switch on skating

	mp_state_component->m_physics_state = SKATING;
	
	mp_core_physics_component->Suspend(false);
	mp_core_physics_component->ReadySkateState(p_walk_component->GetState() == CWalkComponent::WALKING_GROUND, p_walk_component->GetRailData(), p_walk_component->GetAcidDropData());
	
	GetSkaterRotateComponentFromObject(GetObject())->Suspend(false);
	GetSkaterAdjustPhysicsComponentFromObject(GetObject())->Suspend(false);
	GetSkaterFinalizePhysicsComponentFromObject(GetObject())->Suspend(false);
	
	p_skater_camera_component->Suspend(false);
	
	// exchange camera states
	SCameraState camera_state;
	p_walk_camera_component->GetCameraState(camera_state);
	p_skater_camera_component->ReadyForActivation(camera_state);
	
	// reapply the physics suspend state
	if (m_physics_suspended)
	{
		suspend_physics(m_physics_suspended);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterPhysicsControlComponent::switch_skating_to_walking (   )
{
	if (mp_state_component->m_physics_state == WALKING) return;
	
	m_previous_physics_state_duration = Tmr::ElapsedTime(m_physics_state_switch_time_stamp);
	m_physics_state_switch_time_stamp = Tmr::GetTime();
	
	CWalkComponent* p_walk_component = GetWalkComponentFromObject(GetObject());
	CCompositeObject* p_skater_cam = get_skater_camera();
	CSkaterCameraComponent* p_skater_camera_component = GetSkaterCameraComponentFromObject(p_skater_cam);
	CWalkCameraComponent* p_walk_camera_component = GetWalkCameraComponentFromObject(p_skater_cam);
	
	Dbg_Assert(p_walk_component);
	Dbg_Assert(p_skater_camera_component);
	Dbg_Assert(p_walk_camera_component);
	
	// switch off skating
	
	bool ground = mp_core_physics_component->GetState() == GROUND;
	mp_core_physics_component->CleanUpSkateState();
	mp_core_physics_component->Suspend(true);

	GetSkaterRotateComponentFromObject(GetObject())->Suspend(true);
	GetSkaterAdjustPhysicsComponentFromObject(GetObject())->Suspend(true);
	GetSkaterFinalizePhysicsComponentFromObject(GetObject())->Suspend(true);

	p_skater_camera_component->Suspend(true);
	
	// switch on walking
		
	mp_state_component->m_physics_state = WALKING;
	
	p_walk_component->Suspend(false);
	p_walk_component->ReadyWalkState(ground);
	
	p_walk_camera_component->Suspend(false);
	
	// exchange camera states
	SCameraState camera_state;
	p_skater_camera_component->GetCameraState(camera_state);
	p_walk_camera_component->ReadyForActivation(camera_state);
	
	// reapply the physics suspend state
	if (m_physics_suspended)
	{
		suspend_physics(m_physics_suspended);
	}
}


#ifdef TESTING_GUNSLINGER
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterPhysicsControlComponent::switch_walking_to_riding( void )
{
	if (mp_state_component->m_physics_state == SKATING) return;
	if (mp_state_component->m_physics_state == RIDING) return;
	
	CRiderComponent*		p_rider_component			= GetRiderComponentFromObject(GetObject());

	// Use the rider component to check that this switch is valid.
	if(	p_rider_component->ReadyRiderState( true ))
	{
		CWalkComponent*			p_walk_component			= GetWalkComponentFromObject(GetObject());
		CCompositeObject*		p_skater_cam				= get_skater_camera();
		CSkaterCameraComponent*	p_skater_camera_component	= GetSkaterCameraComponentFromObject(p_skater_cam);
		CWalkCameraComponent*	p_walk_camera_component		= GetWalkCameraComponentFromObject(p_skater_cam);
	
		Dbg_Assert(p_walk_component);
		Dbg_Assert(p_skater_camera_component);
		Dbg_Assert(p_walk_camera_component);
	
		// switch off walking
		p_walk_component->Suspend(true);
		p_walk_camera_component->Suspend(true);
	
		// switch on riding
		mp_state_component->m_physics_state = RIDING;
	
//		mp_core_physics_component->Suspend(false);
//		mp_core_physics_component->ReadySkateState(p_walk_component->GetState() == CWalkComponent::WALKING_GROUND, p_walk_component->GetRailData());
		p_rider_component->Suspend( false );
	
//		GetSkaterRotateComponentFromObject(GetObject())->Suspend(false);
//		GetSkaterAdjustPhysicsComponentFromObject(GetObject())->Suspend(false);
//		GetSkaterFinalizePhysicsComponentFromObject(GetObject())->Suspend(false);
//		GetSkaterCleanupStateComponentFromObject(GetObject())->Suspend(false);
	
//		p_skater_camera_component->Suspend(false);
	
		// exchange camera states
//		SCameraState camera_state;
//		p_walk_camera_component->GetCameraState(camera_state);
//		p_skater_camera_component->ReadyForActivation(camera_state);
	
		// reapply the physics suspend state
		if (m_physics_suspended)
		{
			suspend_physics(m_physics_suspended);
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterPhysicsControlComponent::switch_riding_to_walking( void )
{
//	if (mp_state_component->m_physics_state == WALKING) return;
	
	CWalkComponent*			p_walk_component = GetWalkComponentFromObject(GetObject());
	CRiderComponent*		p_rider_component			= GetRiderComponentFromObject(GetObject());
	CCompositeObject*		p_skater_cam = get_skater_camera();
	CSkaterCameraComponent*	p_skater_camera_component = GetSkaterCameraComponentFromObject(p_skater_cam);
	CWalkCameraComponent*	p_walk_camera_component = GetWalkCameraComponentFromObject(p_skater_cam);
	
	Dbg_Assert(p_walk_component);
	Dbg_Assert(p_skater_camera_component);
	Dbg_Assert(p_walk_camera_component);
	
	// switch off skating
	mp_core_physics_component->Suspend(true);

	GetSkaterRotateComponentFromObject(GetObject())->Suspend(true);
	GetSkaterAdjustPhysicsComponentFromObject(GetObject())->Suspend(true);
	GetSkaterFinalizePhysicsComponentFromObject(GetObject())->Suspend(true);
//	GetSkaterCleanupStateComponentFromObject(GetObject())->Suspend(true);

	p_skater_camera_component->Suspend(true);
	
	// Switch off riding.
	p_rider_component->Suspend( true );

	// switch on walking
	mp_state_component->m_physics_state = WALKING;
	
	p_walk_component->Suspend(false);
	p_walk_component->ReadyWalkState(mp_core_physics_component->GetState() == GROUND);
	p_walk_camera_component->Suspend( false );
	
	// exchange camera states
//	SCameraState camera_state;
//	p_skater_camera_component->GetCameraState( camera_state );
//	p_walk_camera_component->ReadyForActivation( camera_state );
	
	// reapply the physics suspend state
	if (m_physics_suspended)
	{
		suspend_physics(m_physics_suspended);
	}
}



#endif




}


================================================
FILE: Code/Sk/Components/SkaterPhysicsControlComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterPhysicsControlComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/7/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERPHYSICSCONTROLCOMPONENT_H__
#define __COMPONENTS_SKATERPHYSICSCONTROLCOMPONENT_H__

#include 
#include 

#include 

#include 
#include 

#define		CRC_SKATERPHYSICSCONTROL CRCD(0xd9151d4d, "SkaterPhysicsControl")

#define		GetSkaterPhysicsControlComponent() ((Obj::CSkaterPhysicsControlComponent*)GetComponent(CRC_SKATERPHYSICSCONTROL))
#define		GetSkaterPhysicsControlComponentFromObject(pObj) ((Obj::CSkaterPhysicsControlComponent*)(pObj)->GetComponent(CRC_SKATERPHYSICSCONTROL))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSkaterCorePhysicsComponent;
	class CSkaterStateComponent;
	class CRailManager;
	class CRailNode;
	
	enum ESpecialTransitionType
	{
		NO_SPECIAL_TRANSITION,
		RAIL_TRANSITION,
		ACID_DROP_TRANSITION
	};

	struct SRailData
	{
		Mth::Vector					rail_pos;
		CRailManager*				p_rail_man;
		CRailNode*					p_node;
		CCompositeObject*			p_movable_contact;
	};
	
	struct SAcidDropData
	{
		Mth::Vector					target_pos;
		Mth::Vector					target_normal;
		float						true_target_height;
	};

class CSkaterPhysicsControlComponent : public CBaseComponent
{
public:
    CSkaterPhysicsControlComponent();
    virtual ~CSkaterPhysicsControlComponent();

public:
    virtual void            		Update();
    virtual void            		Finalize();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
			 	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }

	void							SuspendPhysics ( bool suspend );
	bool							IsPhysicsSuspended (   ) { return m_physics_suspended; }
	
	bool							IsSkating (   );
	bool							IsWalking (   );
	bool							IsDriving (   );
	
	Tmr::Time						GetStateSwitchTime (   );
	Tmr::Time						GetPreviousPhysicsStateDuration (   );
	
	bool							IsBoardMissing (   ) { return m_board_missing; }
	bool							HaveBeenReset (   ) { return m_restarted_this_frame; }
	void							NotifyReset (   ) { m_restarted_this_frame = true; }
	
private:
	CCompositeObject*				get_skater_camera (   );
	void							suspend_physics ( bool suspend );
	
	void							switch_skating_to_walking (   );
	void							switch_walking_to_skating (   );

#	ifdef TESTING_GUNSLINGER
	void							switch_walking_to_riding (   );
	void							switch_riding_to_walking (   );
#	endif


private:
	bool							m_physics_suspended;
	
	Tmr::Time						m_physics_state_switch_time_stamp;
	Tmr::Time						m_previous_physics_state_duration;
	
	bool							m_board_missing;
	
	bool							m_restarted_this_frame;  		// Set if a restart occured, so we can early out
	
	// peer components
	CSkaterCorePhysicsComponent*	mp_core_physics_component;
	CSkaterStateComponent*			mp_state_component;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CSkaterPhysicsControlComponent::IsDriving (   )
{
	return mp_state_component->m_driving;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CSkaterPhysicsControlComponent::IsSkating (   )
{
	return !IsDriving() && mp_state_component->m_physics_state == SKATING;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CSkaterPhysicsControlComponent::IsWalking (   )
{
	return !IsDriving() && mp_state_component->m_physics_state == WALKING;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Tmr::Time CSkaterPhysicsControlComponent::GetStateSwitchTime (   )
{
	return m_physics_state_switch_time_stamp;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Tmr::Time CSkaterPhysicsControlComponent::GetPreviousPhysicsStateDuration (   )
{
	return m_previous_physics_state_duration;
}

/*****************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterPhysicsControlComponent::SuspendPhysics ( bool suspend )
{
	if (m_physics_suspended == suspend) return;
	
	suspend_physics(suspend);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline CCompositeObject* CSkaterPhysicsControlComponent::get_skater_camera (   )
{
	
	return static_cast< CSkater* >(GetObject())->GetCamera();
}

}

#endif


================================================
FILE: Code/Sk/Components/SkaterProximityComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SkaterProximityComponent.cpp
//* OWNER:          Mick West
//* CREATION DATE:  3/20/03
//****************************************************************************

/*

	Component for raising exceptions/events when the skater gets within a particular radius
	this used to be part of the ExceptionComponent, but as that is slated for destruction,
	I've had to seperate it out.  					

*/
					
// The CSkaterProximityComponent class is an skeletal version of a component
// It is intended that you use this as the basis for creating new
// components.  
// To create a new component called "Watch", (CWatchComponent):
//  - copy SkaterProximitycomponent.cpp/.h to watchcomponent.cpp/.h
//  - in both files, search and replace "SkaterProximity" with "Watch", preserving the case
//  - in WatchComponent.h, update the CRCD value of CRC_WATCH
//  - in CompositeObjectManager.cpp, in the CCompositeObjectManager constructor, add:
//		  	RegisterComponent(CRC_WATCH,			CWatchComponent::s_create); 
//  - and add the include of the header
//			#include  
//  - Add it to build\gel.mkf, like:
//          $(NGEL)/components/WatchComponent.cpp\
//  - Fill in the OWNER (yourself) and the CREATION DATE (today's date) in the .cpp and the .h files
//	- Insert code as needed and remove generic comments
//  - remove these comments
//  - add comments specfic to the component, explaining its usage

#include 

#include 
#include 
#include 
#include 
#include 
#include 


#include 
#include 
#include 


#define	FLAGEXCEPTION(X) GetObject()->SelfEvent(X)


namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// s_create is what is registered with the component factory 
// object, (currently the CCompositeObjectManager) 
// s_create	returns a CBaseComponent*, as it is to be used
// by factor creation schemes that do not care what type of
// component is being created
// **  after you've finished creating this component, be sure to
// **  add it to the list of registered functions in the
// **  CCompositeObjectManager constructor  

CBaseComponent* CSkaterProximityComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterProximityComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// All components set their type, which is a unique 32-bit number
// (the CRC of their name), which is used to identify the component	
CSkaterProximityComponent::CSkaterProximityComponent() : CBaseComponent()
{
	SetType( CRC_SKATERPROXIMITY );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterProximityComponent::~CSkaterProximityComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// InitFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CSkaterProximityComponent::InitFromStructure( Script::CStruct* pParams )
{
	// ** Add code to parse the structure, and initialize the component

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RefreshFromStructure is passed a Script::CStruct that contains a
// number of parameters to initialize this component
// this currently is the contents of a node
// but you can pass in anything you like.	
void CSkaterProximityComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	// Default to just calline InitFromStructure()
	// but if that does not handle it, then will need to write a specific 
	// function here. 
	// The user might only want to update a single field in the structure
	// and we don't want to be asserting becasue everything is missing 
	
	InitFromStructure(pParams);


}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The component's Update() function is called from the CCompositeObject's 
// Update() function.  That is called every game frame by the CCompositeObjectManager
// from the s_logic_code function that the CCompositeObjectManager registers
// with the task manger.
void CSkaterProximityComponent::Update()
{
	// GJ TODO:  maybe this should be made more generic
	// so that it takes a CCompositeObject*	(it would
	// make replacing the skater w/ a different player
	// object easier)

	// check skater dist exceptions...
	float distToSkaterSqr = 0.0f;
	
	if ( mInnerRadiusSqr )
	{
		distToSkaterSqr = GetDistToLocalSkaterSquared();
		//distToSkaterSqr = GetDistToNearestSkaterSquared( );
		if ( distToSkaterSqr <= mInnerRadiusSqr )
		{
			// If the local skater is in radius, then flag "SkaterInRadius"
			FLAGEXCEPTION( CRCD(0xdb7413fb,"SkaterInRadius") );
			if (!GetObject()->IsDead())
			{
				FLAGEXCEPTION( CRCD(0x5e8eb123,"AnySkaterInRadius") );
			}
		}
		else
		{
			distToSkaterSqr = GetDistToNearestSkaterSquared();	// MIGHT BE A NETWORK SKATER
			if ( distToSkaterSqr <= mInnerRadiusSqr )
			{
				FLAGEXCEPTION( CRCD(0x5e8eb123,"AnySkaterInRadius") );
			}
			distToSkaterSqr = 0.0f;		// Mick:  need to clear it, otherwise this value will be used for the
										// AvoidRadius settings, which only apply to local skaters
		}
	}
	
	if ( mOuterRadiusSqr )
	{
		if ( distToSkaterSqr == 0.0f )
		{
			distToSkaterSqr = GetDistToLocalSkaterSquared();
		}
		if ( distToSkaterSqr >= mOuterRadiusSqr )
		{
			if (!GetObject()->IsDead())
			{
				FLAGEXCEPTION( CRCD(0xa41f5336,"SkaterOutOfRadius") ); 
			}
		}
	}
	
	if ( mInnerAvoidRadiusSqr )
	{
		if ( distToSkaterSqr == 0.0f )
		{
			distToSkaterSqr = GetDistToLocalSkaterSquared();
		}
		if ( distToSkaterSqr <= mInnerAvoidRadiusSqr )
		{
			if (!GetObject()->IsDead())
			{
				FLAGEXCEPTION( CRCD(0xfaeec40f,"SkaterInAvoidRadius") );
			}
		}
	}
	
	if ( mOuterAvoidRadiusSqr )
	{
		if ( distToSkaterSqr == 0.0f )
		{
			distToSkaterSqr = GetDistToLocalSkaterSquared();
		}
		if ( distToSkaterSqr >= mOuterAvoidRadiusSqr )
		{
			if (!GetObject()->IsDead())
			{
				FLAGEXCEPTION( CRCD(0x9c5af1a0,"SkaterOutOfAvoidRadius") );
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the "Checksum" of a script command, then possibly handle it
// if it's a command that this component will handle	
CBaseComponent::EMemberFunctionResult CSkaterProximityComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | Obj_SetInnerRadius | 
        // @uparm 1.0 | inner radius
		case ( 0x82a0a873 ):	// Obj_SetInnerRadius
			if ( pParams->GetFloat( NONAME, &mInnerRadiusSqr ) )
			{
				mInnerRadiusSqr *= FEET_TO_INCHES( 1.0f );
				mInnerRadiusSqr *= mInnerRadiusSqr;
			}
			break;

        // @script | Obj_SetOuterRadius | 
        // @uparm 1.0 | outer radius
		case ( 0x8cb5d0c0 ):	// Obj_SetOuterRadius
			if ( pParams->GetFloat( NONAME, &mOuterRadiusSqr ) )
			{
				mOuterRadiusSqr *= FEET_TO_INCHES( 1.0f );
				mOuterRadiusSqr *= mOuterRadiusSqr;
			}
			break;

		// @script | Obj_SetInnerAvoidRadius | 
		// @uparm 1.0 | inner radius
		case 0x50f28d36: // Obj_SetInnerAvoidRadius
			if ( pParams->GetFloat( NONAME, &mInnerAvoidRadiusSqr ) )
			{
				mInnerAvoidRadiusSqr *= FEET_TO_INCHES( 1.0f );
				mInnerAvoidRadiusSqr *= mInnerAvoidRadiusSqr;
			}
			break;

		// @script | Obj_SetOuterAvoidRadius | 
		// @uparm 1.0 | outer radius
		case 0x2d703b94: // Obj_SetOuterAvoidRadius
			if ( pParams->GetFloat( NONAME, &mOuterAvoidRadiusSqr ) )
			{
				mOuterAvoidRadiusSqr *= FEET_TO_INCHES( 1.0f );
				mOuterAvoidRadiusSqr *= mOuterAvoidRadiusSqr;
			}
			break;



		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

	// the "default" case of the switch statement handles
	// unrecognized functions;  if we make it down here,
	// that means that the component already handled it
	// somehow
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProximityComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterProximityComponent::GetDebugInfo"));

	// Add any script components to the p_info structure,
	// and they will be displayed in the script debugger (qdebug.exe)
	// you will need to add the names to debugger_names.q, if they are not existing checksums

	/*	Example:
	p_info->AddInteger("m_never_suspend",m_never_suspend);
	p_info->AddFloat("m_suspend_distance",m_suspend_distance);
	*/
	p_info->AddFloat("mInnerRadiusSqr",mInnerRadiusSqr);
	p_info->AddFloat("mOuterRadiusSqr",mOuterRadiusSqr);
	p_info->AddFloat("mInnerAvoidRadiusSqr",mInnerAvoidRadiusSqr);
	p_info->AddFloat("mOuterAvoidRadiusSqr",mOuterAvoidRadiusSqr);

	
// we call the base component's GetDebugInfo, so we can add info from the common base component										 
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#define HUGE_DISTANCE_SQUARED ( HUGE_DISTANCE * HUGE_DISTANCE )

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CSkaterProximityComponent::GetDistToLocalSkaterSquared()
{
	CCompositeObject* pObj = Mdl::Skate::Instance()->GetLocalSkater();
	
	if ( pObj && ( pObj != GetObject() ))
	{
		return Mth::DistanceSqr( pObj->GetPos(), GetObject()->GetPos() );
	}
	
	return ( HUGE_DISTANCE_SQUARED );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CSkaterProximityComponent::GetDistToNearestSkaterSquared()
{	
	float nearest_distance = HUGE_DISTANCE_SQUARED;
	
	int num_skaters = Mdl::Skate::Instance()->GetNumSkaters();
	
	for( int i = 0; i < num_skaters; i++ )
	{
		CCompositeObject* pObj = Mdl::Skate::Instance()->GetSkater( i );
		if ( pObj && ( pObj != GetObject() ))
		{
			float this_dist = Mth::DistanceSqr( pObj->GetPos(), GetObject()->GetPos() );
			if( this_dist < nearest_distance )
			{
				nearest_distance = this_dist;
			}
		}
	}
	
	return nearest_distance;
}



	
}


================================================
FILE: Code/Sk/Components/SkaterProximityComponent.h
================================================
//****************************************************************************
//* MODULE:         Gel/Components
//* FILENAME:       SkaterProximityComponent.h
//* OWNER:          ???
//* CREATION DATE:  ??/??/??
//****************************************************************************

#ifndef __COMPONENTS_SKATERPROXIMITYCOMPONENT_H__
#define __COMPONENTS_SKATERPROXIMITYCOMPONENT_H__

#include 
#include 

#include 

// Replace this with the CRCD of the component you are adding
#define		CRC_SKATERPROXIMITY CRCD(0x27457739,"SkaterProximity")

//  Standard accessor macros for getting the component either from within an object, or 
//  given an object				 
#define		GetSkaterProximityComponent() ((Obj::CSkaterProximityComponent*)GetComponent(CRC_SkaterProximity))
#define		GetSkaterProximityComponentFromObject(pObj) ((Obj::CSkaterProximityComponent*)(pObj)->GetComponent(CRC_SkaterProximity))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CSkaterProximityComponent : public CBaseComponent
{

// Bitfield flags, so we don't trigger things twice
enum	{
	INNER=0x0001,
	OUTER=0x0002,
	INNERAVOID=0x0004,
	OUTERAVOID=0x0008,
};

public:
    CSkaterProximityComponent();
    virtual ~CSkaterProximityComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	float					GetDistToLocalSkaterSquared();
	float					GetDistToNearestSkaterSquared();

protected:
	float					mInnerRadiusSqr;
	float					mOuterRadiusSqr;

	float					mInnerAvoidRadiusSqr;
	float					mOuterAvoidRadiusSqr;
	
	uint32					m_flags;
	
	
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterRotateComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterRotateComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/6/3
//****************************************************************************

#include 
#include 
#include 

#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent* CSkaterRotateComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterRotateComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterRotateComponent::CSkaterRotateComponent() : CBaseComponent()
{
	SetType( CRC_SKATERROTATE );
	
	mp_core_physics_component = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterRotateComponent::~CSkaterRotateComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRotateComponent::InitFromStructure( Script::CStruct* pParams )
{
	for (int n = 3; n--; )
	{
		mp_rotations[n].active = false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRotateComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRotateComponent::Finalize (   )
{
	mp_core_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
	
	Dbg_Assert(mp_core_physics_component);	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRotateComponent::Update()
{
	Mth::Matrix rotation_matrix;
	
	for (int n = X; n < Z + 1; n++)
	{
		SRotation& rotation = mp_rotations[n];
		
		if (!rotation.active) continue;
		
		float amount = Tmr::FrameLength() * rotation.angle_step;
		
		rotation.angle_traversed += Mth::Abs(amount);
		
		if (rotation.angle_traversed >= rotation.angle)
		{
			rotation.active = false;
			amount -= Mth::Sgn(amount) * (rotation.angle_traversed - rotation.angle);
		}
		
		Mth::CreateRotateMatrix(rotation_matrix, n, amount);
		
		GetObject()->m_matrix = rotation_matrix * GetObject()->m_matrix;
		mp_core_physics_component->m_lerping_display_matrix = rotation_matrix * mp_core_physics_component->m_lerping_display_matrix;;
	}
	
	if (!mp_rotations[0].active && !mp_rotations[1].active && !mp_rotations[2].active)
	{
		Suspend(true);
	}
}		

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterRotateComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | Rotate | 
        // @parmopt float | Duration | 0.0 | duration time (default is ms)
        // @flag seconds | time in seconds
        // @flag frames | time in frames
        // @parmopt float | x | 0.0 | x rotation angle
        // @parmopt float | y | 0.0 | y rotation angle
        // @parmopt float | z | 0.0 | z rotation angle
        // @parmopt name | Node | | Node to point to. Currently only supported if a duration is specified.
		case CRCC(0xc876adf3, "Rotate"):
		{
			float duration;
			if (pParams->GetFloat(CRCD(0x79a07f3f, "Duration"), &duration))
			{
				Dbg_MsgAssert(duration > 0.0f, ("Zero or negative Rotate Duration"));
				
				if (pParams->ContainsFlag(CRCD(0xd029f619, "Seconds")) || pParams->ContainsFlag(CRCD(0x49e0ee96, "Second")))
				{
					// Seconds is what we want, so nothing to do
				}
				else if (pParams->ContainsFlag(CRCD(0x19176c5, "Frames")) || pParams->ContainsFlag(CRCD(0x4a07c332, "Frame")))
				{
					// Convert from frames to seconds
					duration /= 60.0f;
				}
				else
				{
					// Convert from milliseconds to seconds
					duration /= 1000.0f;
				}	

				if (pParams->GetFloat(CRCD(0x7323e97c, "x"), &mp_rotations[X].angle))
				{
					mp_rotations[X].duration = duration;
					mp_rotations[X].angle_step = Mth::DegToRad(mp_rotations[X].angle / mp_rotations[X].duration);
					mp_rotations[X].angle = Mth::Abs(Mth::DegToRad(mp_rotations[X].angle));
					mp_rotations[X].angle_traversed = 0.0f;
					mp_rotations[X].active = true;
				}	
				if (pParams->GetFloat(CRCD(0x424d9ea, "y"), &mp_rotations[Y].angle))
				{
					mp_rotations[Y].duration = duration;
					mp_rotations[Y].angle_step = Mth::DegToRad(mp_rotations[Y].angle / mp_rotations[Y].duration);
					mp_rotations[Y].angle = Mth::Abs(Mth::DegToRad(mp_rotations[Y].angle));
					mp_rotations[Y].angle_traversed = 0.0f;
					mp_rotations[Y].active = true;
				}	
				if (pParams->GetFloat(CRCD(0x9d2d8850, "z"), &mp_rotations[Z].angle))
				{
					mp_rotations[Z].duration = duration;
					mp_rotations[Z].angle_step = Mth::DegToRad(mp_rotations[Z].angle / mp_rotations[Z].duration);
					mp_rotations[Z].angle = Mth::Abs(Mth::DegToRad(mp_rotations[Z].angle));
					mp_rotations[Z].angle_traversed = 0.0f;
					mp_rotations[Z].active = true;
				}	
				
				// If a node is specified, turn to point to it instead.
				// Added by Ken for use by Brad when making the skater turn around when he goes outside the level limits.
				uint32 node_name = 0;
				if (pParams->GetChecksum(CRCD(0x7a8017ba, "Node"), &node_name))
				{
					int node = SkateScript::FindNamedNode(node_name);
					Mth::Vector node_pos;
					SkateScript::GetPosition(node, &node_pos);
				
					mp_rotations[Y].angle = Mth::RadToDeg(Mth::GetAngle(GetObject()->m_matrix, node_pos - GetObject()->m_pos));
					
					mp_rotations[Y].duration = duration;
					mp_rotations[Y].angle_step = Mth::DegToRad(mp_rotations[Y].angle / mp_rotations[Y].duration);
					mp_rotations[Y].angle = Mth::Abs(Mth::DegToRad(mp_rotations[Y].angle));
					mp_rotations[Y].angle_traversed = 0.0f;
					mp_rotations[Y].active = true;
				}
				
				Suspend(false);
			} // END if duration specified
			else
			{
				// rotate immediately
				float angle;
				if (pParams->GetFloat(CRCD(0x7323e97c, "x"), &angle))
				{
					GetObject()->m_matrix.RotateXLocal(Mth::DegToRad(angle));				
					mp_core_physics_component->ResetLerpingMatrix();
				}
				else if (pParams->GetFloat(CRCD(0x424d9ea, "y"), &angle))
				{
					GetObject()->m_matrix.RotateYLocal(Mth::DegToRad(angle));				
					mp_core_physics_component->ResetLerpingMatrix();
				}
				else if (pParams->GetFloat(CRCD(0x9d2d8850, "z"), &angle))
				{
					GetObject()->m_matrix.RotateZLocal(Mth::DegToRad(angle));
					mp_core_physics_component->ResetLerpingMatrix();
				}
				else
				{
					// if no parameters are given, rotate 180 degrees about Y
					GetObject()->m_matrix[Z].Negate();
					GetObject()->m_matrix[X].Negate();
					mp_core_physics_component->ResetLerpingMatrix();
					
					mp_core_physics_component->mRail_Backwards = !mp_core_physics_component->mRail_Backwards;
				}
			} // END else no Duration specified	
			break;
		}
		
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRotateComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterRotateComponent::GetDebugInfo"));
	
	Script::CArray* p_rotations_array = new Script::CArray;
	p_rotations_array->SetSizeAndType(3, ESYMBOLTYPE_STRUCTURE);
	for (int n = 3; n--; )
	{
		SRotation& rotation = mp_rotations[n];
		
		Script::CStruct* p_rotation_struct = new Script::CStruct;
		p_rotation_struct->AddInteger("active", rotation.active);
		p_rotation_struct->AddFloat("angle", rotation.angle);
		p_rotation_struct->AddFloat("duration", rotation.duration);
		p_rotation_struct->AddFloat("angle_step", rotation.angle_step);
		p_rotation_struct->AddFloat("angle_traversed", rotation.angle_traversed);
		
		p_rotations_array->SetStructure(n, p_rotation_struct);
	}
	p_info->AddArrayPointer("mp_rotations", p_rotations_array);

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRotateComponent::StopAllRotation (   )
{
	for (int n = 3; n--; )
	{
		mp_rotations[n].active = false;
	}
}
  
}


================================================
FILE: Code/Sk/Components/SkaterRotateComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterRotateComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/6/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERROTATECOMPONENT_H__
#define __COMPONENTS_SKATERROTATECOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERROTATE CRCD(0x52d61a0d, "SkaterRotate")

#define		GetSkaterRotateComponent() ((Obj::CSkaterRotateComponent*)GetComponent(CRC_SKATERROTATE))
#define		GetSkaterRotateComponentFromObject(pObj) ((Obj::CSkaterRotateComponent*)(pObj)->GetComponent(CRC_SKATERROTATE))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CSkaterCorePhysicsComponent;

class CSkaterRotateComponent : public CBaseComponent
{
	struct SRotation
	{
		float angle;
		float duration;
		float angle_step;
		float angle_traversed;
		bool active;
	};
	
public:
    CSkaterRotateComponent();
    virtual ~CSkaterRotateComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	bool							IsApplyingRotation ( unsigned axis );
	void							StopAllRotation (   );
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
private:
	SRotation						mp_rotations[3];
	
	// peer components
	CSkaterCorePhysicsComponent*	mp_core_physics_component;
};

inline bool CSkaterRotateComponent::IsApplyingRotation ( unsigned axis )
{
	Dbg_Assert(axis < 3);
	return mp_rotations[axis].active;
}

}

#endif


================================================
FILE: Code/Sk/Components/SkaterRunTimerComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterRunTimerComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  7/17/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterRunTimerComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterRunTimerComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterRunTimerComponent::CSkaterRunTimerComponent() : CBaseComponent()
{
	SetType( CRC_SKATERRUNTIMER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterRunTimerComponent::~CSkaterRunTimerComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRunTimerComponent::InitFromStructure( Script::CStruct* pParams )
{
	m_state = INACTIVE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRunTimerComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRunTimerComponent::Finalize()
{
	mp_walk_component = GetWalkComponentFromObject(GetObject());
	
	Dbg_Assert(mp_walk_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRunTimerComponent::Update()
{
	if (m_state == ACTIVE_RUNNING)
	{
		switch (mp_walk_component->GetState())
		{
			case CWalkComponent::WALKING_HANG:
			case CWalkComponent::WALKING_LADDER:
			case CWalkComponent::WALKING_ANIMWAIT:
				m_timer -= Script::GetFloat(CRCD(0x92fb4d09, "Hang_Run_Timer_Speed_Adjustment")) * Tmr::FrameLength();
				break;
				
			default:
				m_timer -= Tmr::FrameLength();
				break;
		}
		
		if (m_timer < 0.0f)
		{
			set_state(ACTIVE_TIME_UP);
			GetObject()->SelfEvent(CRCD(0x9e22a8e9, "RunTimerUp"));
		}
	}
	else if (m_state == ACTIVE_TIME_UP)
	{
		GetObject()->SelfEvent(CRCD(0x9e22a8e9, "RunTimerUp"));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterRunTimerComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | RunTimer_Pause | pauses the run timer
		case CRCC(0x82445966, "RunTimer_Pause"):
			pause();
			break;
		
		// @script | RunTimer_UnPause | unpauses the run timer
		case CRCC(0x956236b1, "RunTimer_UnPause"):
			unpause();
			break;
			
		// @script | RunTimer_GetFactorComplete | returns the time left of the run timer as a float between zero and one in FactorComplete
		case CRCC(0xab04cbf9, "RunTimer_GetFactorComplete"):
			pScript->GetParams()->AddFloat(CRCD(0x41f56511, "FactorComplete"), m_timer / GetSkater()->GetScriptedStat(CRCD(0xb84f532, "Physics_RunTimer_Duration")));
			break;
			
		// @script | RunTimer_GetRunTimerControllerId | returns the id of the display controller script for the run timer in RunTimerControllerId
		case CRCC(0x96d81d8e, "RunTimer_GetRunTimerControllerId"):
			pScript->GetParams()->AddChecksum(CRCD(0x39778122, "RunTimerControllerId"), get_run_timer_controller_id());
			break; 
			
		// @script | RunTimer_GetRunTimerId | returns the id of the display controller script for the run timer in RunTimerId
		case CRCC(0x81aaa6dd, "RunTimer_GetRunTimerId"):
			pScript->GetParams()->AddChecksum(CRCD(0x628f21c9, "RunTimerId"), CRCD(0x7d42821c, "the_run_timer") + GetSkater()->GetSkaterNumber());
			break; 
											  
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRunTimerComponent::GetDebugInfo(Script::CStruct *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterRunTimerComponent::GetDebugInfo"));
	
	static const uint32 states [   ] =
	{
		CRCD(0x742cf11f, "INACTIVE"),
		CRCD(0x5392ca8e, "ACTIVE_RUNNING"),
		CRCD(0x26776873, "ACTIVE_PAUSED"),
		CRCD(0xdef417fa, "ACTIVE_TIME_UP")
	};
	p_info->AddChecksum(CRCD(0x109b9260, "m_state"), states[m_state]);
	
	p_info->AddFloat(CRCD(0xd9d89e81, "m_timer"), m_timer);

	CBaseComponent::GetDebugInfo(p_info);	  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRunTimerComponent::ComboEnded (   )
{
	if (Script::FindSpawnedScriptWithID(get_run_timer_controller_id()))
	{
		CTracker::Instance()->LaunchEvent(CRCD(0x813cc576, "HideRunTimer"), get_run_timer_controller_id(), GetObject()->GetID());
	}
	
	set_state(INACTIVE);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterRunTimerComponent::unpause (   )
{
	if (m_state == INACTIVE) return;
		
	set_state(m_timer > 0.0f ? ACTIVE_RUNNING : ACTIVE_TIME_UP);
	
	if (m_unpause_count < vRT_NUM_TIME_CHUNKS)
	{
		float max_timer = (vRT_NUM_TIME_CHUNKS - m_unpause_count) * GetSkater()->GetScriptedStat(CRCD(0xb84f532, "Physics_RunTimer_Duration")) / vRT_NUM_TIME_CHUNKS;
		m_timer = Mth::Min(m_timer, max_timer);
	}
	
	m_unpause_count++;
	
	CTracker::Instance()->LaunchEvent(CRCD(0x47eec244, "ShowRunTimer"), get_run_timer_controller_id(), GetObject()->GetID());
}

}


================================================
FILE: Code/Sk/Components/SkaterRunTimerComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterRunTimerComponent.h
//* OWNER:          Dan
//* CREATION DATE:  7/17/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERRUNTIMERCOMPONENT_H__
#define __COMPONENTS_SKATERRUNTIMERCOMPONENT_H__

#include 
#include 

#include 

#include 

#include 

#define		CRC_SKATERRUNTIMER CRCD(0x8bce7e66, "SkaterRunTimer")

#define		GetSkaterRunTimerComponent() ((Obj::CSkaterRunTimerComponent*)GetComponent(CRC_SKATERRUNTIMER))
#define		GetSkaterRunTimerComponentFromObject(pObj) ((Obj::CSkaterRunTimerComponent*)(pObj)->GetComponent(CRC_SKATERRUNTIMER))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	class CWalkComponent;

class CSkaterRunTimerComponent : public CBaseComponent
{
	enum EState
	{
		INACTIVE,
		ACTIVE_RUNNING,
		ACTIVE_PAUSED,
		ACTIVE_TIME_UP
	};
	
	// number of divisions of the timer; you always lose at least one chunk of timer per runout
	enum { vRT_NUM_TIME_CHUNKS = 8 };
	
public:
    CSkaterRunTimerComponent();
    virtual ~CSkaterRunTimerComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    virtual void            		Finalize();
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
	void							ComboStarted (   );
	void							ComboEnded (   );

	static CBaseComponent*			s_create();
	
private:
	void							update_timer (   );
	void							pause (   );
	void							unpause (   );
	bool							is_timer_up (   );
	void							set_state ( EState state );
	uint32							get_run_timer_controller_id (   );
	
private:
	EState							m_state;
	
	float							m_timer;
	
	short							m_unpause_count;
	
	CWalkComponent*					mp_walk_component;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterRunTimerComponent::set_state ( EState state )
{
	m_state = state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint32 CSkaterRunTimerComponent::get_run_timer_controller_id (   )
{
	return CRCD(0x83321dea, "RunTimerController") + GetSkater()->GetSkaterNumber();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterRunTimerComponent::ComboStarted (   )
{
	m_timer = GetSkater()->GetScriptedStat(CRCD(0xb84f532, "Physics_RunTimer_Duration"));
	m_unpause_count = 0;
	
	set_state(ACTIVE_PAUSED);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterRunTimerComponent::update_timer (   )
{
	m_timer -= Tmr::FrameLength();
	
	if (m_timer < 0.0f)
	{
		set_state(ACTIVE_TIME_UP);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterRunTimerComponent::pause (   )
{
	if (m_state == INACTIVE) return;
	
	set_state(ACTIVE_PAUSED);
	
	CTracker::Instance()->LaunchEvent(CRCD(0x813cc576, "HideRunTimer"), get_run_timer_controller_id(), GetObject()->GetID());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline bool CSkaterRunTimerComponent::is_timer_up (   )
{
	Dbg_Assert(m_state != INACTIVE);
	
	return m_state == ACTIVE_TIME_UP;
}

}

#endif


================================================
FILE: Code/Sk/Components/SkaterScoreComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterScoreComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/12/3
//****************************************************************************

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

extern bool g_CheatsEnabled;

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent* CSkaterScoreComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterScoreComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterScoreComponent::CSkaterScoreComponent() : CBaseComponent()
{
	SetType( CRC_SKATERSCORE );
	mp_score = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterScoreComponent::~CSkaterScoreComponent()
{
	if (mp_score)
	{
		delete mp_score;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterScoreComponent::InitFromStructure( Script::CStruct* pParams )
{
	Dbg_MsgAssert(GetObject()->GetType() == SKATE_TYPE_SKATER, ("CSkaterScoreComponent added to non-skater composite object"));
	
	if (!mp_score)
	{
		mp_score = new Mdl::Score();
		mp_score->SetSkaterId(GetObject()->GetID());
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterScoreComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterScoreComponent::Update()
{
	// NOTE: in fact, non-local skaters may not need a score component at all
	if (!GetSkater()->IsLocalClient())
	{
		Suspend(true);
		return;
	}
	
	mp_score->Update();
	
	if (GetSkater()->m_always_special || Mdl::Skate::Instance()->GetCareer()->GetCheat(CRCD(0xeefecf1b, "CHEAT_ALWAYS_SPECIAL")))
	{
		// Here, set the flag. It may seem redundant, but the above line is very likely
		// to be hacked by gameshark. They probably won't notice this one, which will
		// set the flags as if they had actually enabled the cheat -- which enables us
		// to detect that it has been turned on more easily.
		Mdl::Skate::Instance()->GetCareer()->SetGlobalFlag( Script::GetInteger(CRCD(0xeefecf1b, "CHEAT_ALWAYS_SPECIAL")));
		mp_score->ForceSpecial();
		g_CheatsEnabled = true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterScoreComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		// @script | LastScoreLandedGreaterThan | Multiplayer-safe version 
		// of SkaterLastScoreLandedGreaterThan
		// Example: if LastScoreLandedGreaterThan 2000 --do cool stuff-- endif
		// @uparm 1 | Score (int)
		case CRCC(0xe0112aab, "LastScoreLandedGreaterThan"):
		{
			int score;
			pParams->GetInteger(NO_NAME, &score, Script::ASSERT);
			return mp_score->GetLastScoreLanded() > score ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		
		// @script | LastScoreLandedLessThan | Multiplayer-safe version 
		// of SkaterLastScoreLandedLessThan
		// Example: if LastScoreLandedLessThan 2000 --do cool stuff-- endif
		// @uparm 1 | Score (int)
		case CRCC(0x3c445a82, "LastScoreLandedLessThan"):
		{
			int score;
			pParams->GetInteger(NO_NAME, &score, Script::ASSERT);
			return mp_score->GetLastScoreLanded() < score ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		
		// @script | TotalScoreGreaterThan | Multiplayer-safe version 
		// of SkaterTotalScoreGreaterThan
		// Example: if TotalScoreGreaterThan 2000 --do cool stuff-- endif
		// @uparm 1 | Score (int)
		case CRCC(0x8bf43831, "TotalScoreGreaterThan"):
		{
			int score;
			pParams->GetInteger(NO_NAME, &score, Script::ASSERT);
			return mp_score->GetTotalScore() > score ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
			
		// @script | TotalScoreLessThan | Multiplayer-safe version 
		// of SkaterTotalScoreLessThan
		// Example: if TotalScoreLessThan 2000 --do cool stuff-- endif
		// @uparm 1 | Score (int)
		case CRCC(0xdf14bd98, "TotalScoreLessThan"):
		{
			int score;
			pParams->GetInteger(NO_NAME, &score, Script::ASSERT);
			return mp_score->GetTotalScore() < score ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
			
		// @script | CurrentScorePotGreaterThan | Multiplayer-safe version 
		// of SkaterCurrentScorePotGreaterThan
		// Example: if CurrentScorePotGreaterThan 2000 --do cool stuff-- endif
		// @uparm 1 | Score (int)
		case CRCC(0xd19bdb50, "CurrentScorePotGreaterThan"):
		{
			int score;
			pParams->GetInteger(NO_NAME, &score, Script::ASSERT);
			return mp_score->GetScorePotValue() > score ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}

		// @script | GetTotalScore | returns the total score of the skater
		// in TotalScore
		case CRCC( 0x8f2fe3d3, "GetTotalScore" ):
		{
			pScript->GetParams()->AddInteger( CRCD( 0xee5b2b48, "TotalScore" ), mp_score->GetTotalScore() );
			return CBaseComponent::MF_TRUE;
		}
			
		// @script | CurrentScorePotLessThan | Multiplayer-safe version 
		// of SkaterCurrentScorePotLessThan
		// Example: if CurrentScorePotLessThan 2000 --do cool stuff-- endif
		// @uparm 1 | Score (int)
		case CRCC(0x56645fc6, "CurrentScorePotLessThan"):
		{
			int score;
			pParams->GetInteger(NO_NAME, &score, Script::ASSERT);
			return mp_score->GetScorePotValue() < score ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
		}
		
		// @script | GetNumberOfNonGapTricks | returns number of non-gap tricks currently in the combo
		case CRCC(0xc068ea59, "GetNumberOfNonGapTricks"):
			pScript->GetParams()->AddInteger(CRCD(0xffe7e02c, "NumberOfNonGapTricks"), mp_score->GetNumberOfNonGapTricks());
			break;
		
        // @script | GotSpecial | true if special is active
		case CRCC(0x5589b902, "GotSpecial"):	
			return mp_score->GetSpecialState() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
        
		// @script | TweakTrick | 
        // @uparm 0 | tweak
		case CRCC(0xbe3e5463, "TweakTrick"):
		{
			int Tweak = 0;
			pParams->GetInteger(NO_NAME, &Tweak);
			
			mp_score->TweakTrick(Tweak);
			break;
		}
			
		// @script | IsLatestTrick | 
		case CRCC(0x2877b61a, "IsLatestTrick"):
		{
			uint32 key_combo;
			if (pParams->GetChecksum(CRCD(0x95e16467, "KeyCombo"), &key_combo))
			{
				int spin = 0;
				if (pParams->GetInteger(CRCD(0xedf5db70, "Spin"), &spin))
				{
					Dbg_MsgAssert(spin % 180 == 0, ("IsLatestTrick called with a spin value of %i which is not a multiple of 180", spin));
					spin /= 180;
				}
				
				int num_taps = 1;
				pParams->GetInteger(CRCD(0xc82cf71b, "NumTaps"), &num_taps);
				
				return mp_score->IsLatestTrick(key_combo, spin, false, num_taps) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			}
			else
			{
				const char* trick_text;
				if (pParams->GetString(CRCD(0x3eafa520, "TrickText"), &trick_text))
				{
					int spin = 0;
					if (pParams->GetInteger(CRCD(0xedf5db70, "Spin"), &spin))
					{
						Dbg_MsgAssert(spin % 180 == 0, ("IsLatestTrick called with a spin value of %i which is not a multiple of 180", spin));
						spin /= 180;
					}
					
					int num_taps = 1;
					pParams->GetInteger(CRCD(0xc82cf71b, "NumTaps"), &num_taps);
					
					return mp_score->IsLatestTrickByName(Script::GenerateCRC(trick_text), spin, false, num_taps) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
				}
				else
				{
					Dbg_MsgAssert(false, ("IsLatestTrick must have either a KeyCombo or a TrickText parameter"));
				}
			}
		}

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterScoreComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterScoreComponent::GetDebugInfo"));
	
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterScoreComponent::Reset (   )
{
	mp_score->Reset();

	mp_score->SetBalanceMeter(false);
	mp_score->SetManualMeter(false);
}

}


================================================
FILE: Code/Sk/Components/SkaterScoreComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterScoreComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/12/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERSCORECOMPONENT_H__
#define __COMPONENTS_SKATERSCORECOMPONENT_H__

#include 
#include 

#include 

#include 

#define		CRC_SKATERSCORE CRCD(0xd262802f, "SkaterScore")

#define		GetSkaterScoreComponent() ((Obj::CSkaterScoreComponent*)GetComponent(CRC_SKATERSCORE))
#define		GetSkaterScoreComponentFromObject(pObj) ((Obj::CSkaterScoreComponent*)(pObj)->GetComponent(CRC_SKATERSCORE))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CSkaterScoreComponent : public CBaseComponent
{
public:
    CSkaterScoreComponent();
    virtual ~CSkaterScoreComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	Mdl::Score*						GetScore() { return mp_score; }
	
	void							Reset (   );
	
private:
	Mdl::Score*						mp_score;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterSoundComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterSoundComponent.cpp
//* OWNER:			Dan
//* CREATION DATE:  2/27/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent* CSkaterSoundComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterSoundComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterSoundComponent::CSkaterSoundComponent() : CBaseComponent()
{
	SetType( CRC_SKATERSOUND );
	
	m_is_rail_sliding = false;
	m_max_speed = 1.0f;
	m_vol_mult = 1.0f;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterSoundComponent::~CSkaterSoundComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::InitFromStructure( Script::CStruct* pParams )
{
	float vol_mult;
	if (pParams->GetFloat( CRCD(0xf1a99b27,"volume_mult"), &vol_mult))
	{
		SetVolumeMultiplier(vol_mult);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::Update()
{
	// As a minor optimization, CSkaterSoundComponent is always suspended.
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterSoundComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | PlayJumpSound |
		case CRCC(0xe338369, "PlayJumpSound"):
		{
			switch ( m_stateType )
			{
				case GROUND:
				case AIR:
					PlayJumpSound(GetObject()->GetVel().Length() / m_max_speed);
					break;
					 
				case RAIL:
					PlayJumpSound(GetObject()->GetVel().Length() / m_max_speed);
				
				default:
					break;
			}
			break;
		}
		
        // @script | PlayLandSound | 
		case CRCC(0x2c779f22, "PlayLandSound"):
		{
			switch ( m_stateType )
			{
				case GROUND:
				case AIR:
					PlayLandSound(Mth::Abs(GetObject()->GetVel()[Y]) / m_max_speed);
					break;
					
				case RAIL:
					PlayLandSound(GetObject()->GetVel().Length() / m_max_speed);
					break;
					
				default:
					break;
			}
			break;
		}
		
		// @script | PlayBonkSound |
		case CRCC(0x0069e457, "PlayBonkSound"):
		{
			PlayBonkSound(GetObject()->GetVel().Length() / m_max_speed);
			break;
		}
		
        // @script | PlayCessSound | 
		case CRCC(0x6f5e9124, "PlayCessSound"):
		{
			PlayCessSound(GetObject()->GetVel().Length() / m_max_speed);
			break;
		}

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}

    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterSoundComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::PlayLandSound ( float speed_fraction )
{
	ETerrainType terrain;
	if ( m_stateType == GROUND || m_stateType == AIR )
	{
		terrain = m_lastTerrain;
	}
	else
	{
		terrain = m_terrain;
	}
	
	PlayLandSound(speed_fraction, terrain);
}
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::PlayLandSound ( float speed_fraction, ETerrainType terrain )
{
	Env::ETerrainActionType table;
	if ( m_stateType == RAIL )
	{
		table = m_is_rail_sliding ? Env::vTABLE_SLIDELAND : Env::vTABLE_GRINDLAND;
	}
	else
	{
		table = Env::vTABLE_LAND;
	}
	
	speed_fraction *= 100.0f;
	
	#ifdef __NOPT_ASSERT__
	if (Script::GetInteger(CRCD(0xb9ba2d27, "debug_skater_triggered_sounds")))
	{
		Dbg_Message("Playing sound [land]:   %.1f", speed_fraction);
	}
	#endif
	
	Env::CTerrainManager::sPlaySound(table, terrain, GetObject()->GetPos(), speed_fraction * m_vol_mult, speed_fraction, speed_fraction);
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::PlayJumpSound ( float speed_fraction )
{
	ETerrainType terrain;
	if ( m_stateType == GROUND || m_stateType == AIR )
	{
		terrain = m_lastTerrain;
	}
	else
	{
		terrain = m_terrain;
	}
	
	PlayJumpSound(speed_fraction, terrain);
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::PlayJumpSound ( float speed_fraction, ETerrainType terrain )
{
	PlayJumpSound(speed_fraction, terrain, m_stateType);
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::PlayJumpSound ( float speed_fraction, ETerrainType terrain, EStateType stateType )
{
	Env::ETerrainActionType table;
	if ( stateType == RAIL )
	{
		table = m_is_rail_sliding ? Env::vTABLE_SLIDEJUMP : Env::vTABLE_GRINDJUMP; 
	}
	else
	{
		table = Env::vTABLE_JUMP;
	}
	
	speed_fraction *= 100.0f;
	
	#ifdef __NOPT_ASSERT__
	if (Script::GetInteger(CRCD(0xb9ba2d27, "debug_skater_triggered_sounds")))
	{
		Dbg_Message("Playing sound [ollie]:  %.1f", speed_fraction);
	}
	#endif

	Env::CTerrainManager::sPlaySound(table, terrain, GetObject()->GetPos(), speed_fraction * m_vol_mult, speed_fraction, speed_fraction);
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::PlayBonkSound ( float speed_fraction )
{
	PlayBonkSound(speed_fraction, m_terrain);
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::PlayBonkSound ( float speed_fraction, ETerrainType terrain )
{
	speed_fraction *= 100.0f;
	
	#ifdef __NOPT_ASSERT__
	if (Script::GetInteger(CRCD(0xb9ba2d27, "debug_skater_triggered_sounds")))
	{
		Dbg_Message("Playing sound [bonk]:   %.1f", speed_fraction);
	}
	#endif

	Env::CTerrainManager::sPlaySound(Env::vTABLE_BONK, terrain, GetObject()->GetPos(), speed_fraction * m_vol_mult, speed_fraction, speed_fraction);
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::PlayCessSound ( float speed_fraction )
{
	PlayCessSound(speed_fraction, m_terrain);
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::PlayCessSound ( float speed_fraction, ETerrainType terrain )
{
	speed_fraction *= 100.0f;
	
	#ifdef __NOPT_ASSERT__
	if (Script::GetInteger(CRCD(0xb9ba2d27, "debug_skater_triggered_sounds")))
	{
		Dbg_Message("Playing sound [revert]: %.1f", speed_fraction);
	}
	#endif

	Env::CTerrainManager::sPlaySound(Env::vTABLE_CESS, terrain, GetObject()->GetPos(), speed_fraction * m_vol_mult, speed_fraction, speed_fraction);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterSoundComponent::SetVolumeMultiplier( float mult )
{
	Dbg_MsgAssert( mult >= 0.0f && mult <= 1.0f, ( "SetVolumeMultiplier called with bad mult value: %f", mult ) );
	m_vol_mult = mult;
}

}


================================================
FILE: Code/Sk/Components/SkaterSoundComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterSoundComponent.cpp
//* OWNER:			Dan
//* CREATION DATE:  2/27/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERSOUNDCOMPONENT_H__
#define __COMPONENTS_SKATERSOUNDCOMPONENT_H__

#include 
#include 

#include 

#include 

#include 

#define		CRC_SKATERSOUND CRCD(0x187574fa, "SkaterSound")

#define		GetSkaterSoundComponent() ((Obj::CSkaterSoundComponent*)GetComponent(CRC_SKATERSOUND))
#define		GetSkaterSoundComponentFromObject(pObj) ((Obj::CSkaterSoundComponent*)(pObj)->GetComponent(CRC_SKATERSOUND))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CSkaterSoundComponent : public CBaseComponent
{
public:
    CSkaterSoundComponent();
    virtual ~CSkaterSoundComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	void							PlayLandSound ( float speed_fraction );
	void							PlayJumpSound ( float speed_fraction );
	void							PlayBonkSound ( float speed_fraction );
	void							PlayCessSound ( float speed_fraction );
	
	void							PlayLandSound ( float speed_fraction, ETerrainType terrain );
	void							PlayJumpSound ( float speed_fraction, ETerrainType terrain );
	void							PlayBonkSound ( float speed_fraction, ETerrainType terrain );
	void							PlayCessSound ( float speed_fraction, ETerrainType terrain );
	
	void							PlayJumpSound ( float speed_fraction, ETerrainType terrain, EStateType stateType );
	
	void							SetTerrain ( ETerrainType terrain ) { m_terrain = terrain; }
	void							SetLastTerrain ( ETerrainType terrain ) { m_lastTerrain = terrain; }
	void							SetState ( EStateType state ) { m_stateType = state; }
	void							SetIsRailSliding ( bool is_sliding ) { m_is_rail_sliding = is_sliding; }
	
	void							SetMaxSpeed ( float max_speed ) { m_max_speed = max_speed; }
	
	void							SetVolumeMultiplier ( float mult );
private:
	EStateType						m_stateType;
	ETerrainType					m_terrain;
	ETerrainType					m_lastTerrain;

	bool							m_is_rail_sliding;
	
	float							m_max_speed;
	float							m_vol_mult;
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterStancePanelComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterStancePanelComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  2/25/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 

#include 
#include 

namespace Obj
{
	
// Component giving script control through a composite object over the input pad vibrators of the composite object's input handler.
	
// Only composite objects corresponding to local clients should be given this component.
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterStancePanelComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterStancePanelComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterStancePanelComponent::CSkaterStancePanelComponent() : CBaseComponent()
{
	SetType( CRC_SKATERSTANCEPANEL );
	
	mp_core_physics_component = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterStancePanelComponent::~CSkaterStancePanelComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStancePanelComponent::InitFromStructure( Script::CStruct* pParams )
{
	m_last_stance = -1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStancePanelComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStancePanelComponent::Finalize (   )
{
	mp_core_physics_component = GetSkaterCorePhysicsComponentFromObject(GetObject());
	
	Dbg_Assert(mp_core_physics_component);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStancePanelComponent::Update()
{
	Front::CSpriteElement* p_stance_icon = static_cast< Front::CSpriteElement* >(Front::CScreenElementManager::Instance()->GetElement(
		CRCD(0x968ae5dd, "the_boardstance_sprite") + static_cast< CSkater* >(GetObject())->GetHeapIndex()
	).Convert());
	
	Dbg_Assert(p_stance_icon);
	
	int stance = determine_stance();
	
	if (stance == m_last_stance) return;
	m_last_stance = stance;
	
	if (stance == 5)
	{
		p_stance_icon->SetAlpha(0.0f, Front::CScreenElement::FORCE_INSTANT);
	}
	else
	{
		const static uint32 sp_texture_checksums[] =
		{
            CRCD(0x33a15296, "nollie_icon"),
            CRCD(0xbe91f3b6, "fakie_icon"),
			CRCD(0x9f9d3907, "switch_icon"),
            CRCD(0xb793ef40, "sw_pressure_icon"),
            CRCD(0x4e304fa1, "pressure_icon")
		};
		
		p_stance_icon->SetTexture(sp_texture_checksums[stance]);
		p_stance_icon->SetAlpha(1.0f, Front::CScreenElement::FORCE_INSTANT);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterStancePanelComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | InNollie | true if in nollie
		case CRCC(0x1eb61dce, "InNollie"):
			return m_in_nollie ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;

        // @script | NollieOn | sets nollie to on (check with InNollie)
		case CRCC(0x41eb710b, "NollieOn"):
			if (m_in_nollie == false)
			{
				m_in_nollie = true;
				GetObject()->BroadcastEvent(CRCD(0x8157ab31, "SkaterEnterNollie"));
			}
			break;

        // @script | NollieOff | sets nollie to off (check with InNollie)
		case CRCC(0xfb9b7c9c, "NollieOff"):
			if (m_in_nollie == true)
			{
				m_in_nollie = false;
				GetObject()->BroadcastEvent(CRCD(0x3f70881a, "SkaterExitNollie"));
			}
			break;

        // @script | InPressure | true if in pressure
        case CRCC(0x9fab9d0b,"InPressure"):
			return m_in_pressure ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;

        // @script | PressureOn | sets pressure to on (check with InPressure)
		case CRCC(0xa23d710,"PressureOn"):
			if (m_in_pressure == false)
			{
				m_in_pressure = true;
				GetObject()->BroadcastEvent(CRCD(0x2e8de921,"SkaterEnterPressure"));
			}
			break;

        // @script | PressureOff | sets pressure to off (check with InPressure)
		case CRCC(0x71b57dd6,"PressureOff"):
			if (m_in_pressure == true)
			{
				m_in_pressure = false;
				GetObject()->BroadcastEvent(CRCD(0xfa9adb1d,"SkaterExitPressure"));
			}
			break;
			
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
	
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStancePanelComponent::GetDebugInfo ( Script::CStruct *p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterStancePanelComponent::GetDebugInfo"));
	
	p_info->AddInteger("stance", determine_stance());

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

}


================================================
FILE: Code/Sk/Components/SkaterStancePanelComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterStancePanelComponent.h
//* OWNER:          Dan
//* CREATION DATE:  2/25/3
//****************************************************************************

#ifndef __COMPONENTS_STANCEPANELCOMPONENT_H__
#define __COMPONENTS_STANCEPANELCOMPONENT_H__

#include 
#include 

#include 

#include 

#include 

#define		CRC_SKATERSTANCEPANEL CRCD(0x21fc3301, "SkaterStancePanel")

#define		GetSkaterStancePanelComponent() ((Obj::CSkaterStancePanelComponent*)GetComponent(CRC_SKATERSTANCEPANEL))
#define		GetSkaterStancePanelComponentFromObject(pObj) ((Obj::CSkaterStancePanelComponent*)(pObj)->GetComponent(CRC_SKATERSTANCEPANEL))
														 
namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{

class CSkaterStancePanelComponent : public CBaseComponent
{
	
public:
    CSkaterStancePanelComponent();
    virtual ~CSkaterStancePanelComponent();

public:
    virtual void            		Update (   );
    virtual void            		InitFromStructure ( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure ( Script::CStruct* pParams );
	virtual void					Finalize (   );
    
    virtual EMemberFunctionResult   CallMemberFunction ( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo ( Script::CStruct* p_info );

	static CBaseComponent*			s_create (   );
	
	void							Reset (   );
	
private:
	int								determine_stance (   );
	
private:
	int								m_last_stance;
	
	bool							m_active;
	
	bool							m_in_nollie;					// Whether in Nollie stance or not.
    bool							m_in_pressure;					// Whether in pressure stance or not.

	// peer components
	CSkaterCorePhysicsComponent*	mp_core_physics_component;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void CSkaterStancePanelComponent::Reset (   )
{
	m_last_stance = -1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline int CSkaterStancePanelComponent::determine_stance (   )
{
	// regular
    int stance = 5;
	
    // pressure
    if (m_in_pressure)
	{
        // switch pressure
		if (mp_core_physics_component->IsSwitched())
    	{
    		stance -= 2;
    	}
        else
        {
            stance -= 1;
        }
	}
	else
    {
        // switch
    	if (mp_core_physics_component->IsSwitched())
    	{
    		// fakie
            if (m_in_nollie)
        	{
                stance -= 4;
            }
            else
            {
                stance -= 3;
            }
    	}
        else
        {
            // nollie
            if (m_in_nollie)
        	{
        		stance -= 5;
        	}
        }
    }
    
    return stance;
}

}

#endif


================================================
FILE: Code/Sk/Components/SkaterStateComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterStateComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/31/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 

/*
 * Holds all skater state which is needed by both local and nonlocal clients.  This way, code external to the skater can access this information in a
 * consistent manner, without having to know which components within the skater are controling the state.
 *
 * Currently, state within the core physics component has not been moved into theis component.
 */
 
namespace Obj
{
//											Fireball										
static uint32 s_powerups[vNUM_POWERUPS] = { 0xd039432c };

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterStateComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterStateComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterStateComponent::CSkaterStateComponent() : CBaseComponent()
{
	int i;

	SetType( CRC_SKATERSTATE );
	for( i = 0; i < vNUM_POWERUPS; i++ )
	{
		m_powerups[i] = false;
	}
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterStateComponent::~CSkaterStateComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateComponent::InitFromStructure( Script::CStruct* pParams )
{
	m_state = AIR;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateComponent::Update()
{
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterStateComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
        // @script | DoingTrick | true if we're doing a trick
		case 0x58ad903f: // DoingTrick
			return DoingTrick() ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;
			
		case 0xb07ac662: // HasPowerup
		{
			uint32 type;

			pParams->GetChecksum( NONAME, &type, true );
			return HasPowerup( type ) ? CBaseComponent::MF_TRUE : CBaseComponent::MF_FALSE;
			break;
		}

		case 0xe11b7ca:	// PickedUpPowerup
		{
			uint32 type;

			pParams->GetChecksum( NONAME, &type, true );
			PickedUpPowerup( type );
			break;
		}
		
		// @script | GetTerrain | returns the number of the terrain in 'terrain'
		case CRCC(0x44ba5fce, "GetTerrain"):
			pScript->GetParams()->AddInteger(CRCD(0x3789ac4e, "terrain"), m_terrain);
			break;

		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterStateComponent::GetDebugInfo"));
	
	const uint32 p_state_checksums [   ] =
	{
		CRCD(0x58007c97, "GROUND"),
		CRCD(0x439f4704, "AIR"),
		CRCD(0xec0a1009, "WALL"),
		CRCD(0xa549b57b, "LIP"),
		CRCD(0xa6a3147e, "RAIL"),
		CRCD(0xcf74f6b7, "WALLPLANT")
	};
	
	const uint32 p_flag_checksums [ NUM_ESKATERFLAGS ] =
	{
		CRCD(0x42f41014, "TENSE"),
		CRCD(0x0c7a712c, "FLIPPED"),
		CRCD(0xb39b4f1b, "VERT_AIR"),
		CRCD(0xc6bdeafc, "TRACKING_VERT"),
		CRCD(0x7747d16a, "LAST_POLY_WAS_VERT"),
		CRCD(0x0b6c902c, "CAN_BREAK_VERT"),
		CRCD(0x1261f6a0, "CAN_RERAIL"),
		CRCD(0x2bdce1e1, "RAIL_SLIDING"),
		CRCD(0xfb2e505c, "CAN_HIT_CAR"),
		CRCD(0xb2791a2f, "AUTOTURN"),
		CRCD(0x21523880, "IS_BAILING"),
		CRCD(0xe8e7a9a1, "SPINE_PHYSICS"),
		CRCD(0x4b45106a, "IN_RECOVERY"),
		CRCD(0x9c6a7e41, "SKITCHING"),
		CRCD(0x468c28b6, "OVERRIDE_CANCEL_GROUND"),
		CRCD(0xa29e3a92, "SNAPPED_OVER_CURB"),
		CRCD(0x4424288f, "SNAPPED"),
		CRCD(0x0849fb13, "IN_ACID_DROP"),
		CRCD(0x35f996fb, "AIR_ACID_DROP_DISALLOWED"),
		CRCD(0xd1c9cb24, "CANCEL_WALL_PUSH"),
		CRCD(0x260e0844, "NO_ORIENTATION_CONTROL"),
		CRCD(0x524ea0a3, "NEW_RAIL")
	};
	
	p_info->AddChecksum(CRCD(0x109b9260, "m_state"), p_state_checksums[m_state]);
	for (int flag = 0; flag < NUM_ESKATERFLAGS; flag++)
	{
		p_info->AddChecksum(p_flag_checksums[flag], GetFlag(static_cast< ESkaterFlag >(flag)) ? CRCD(0x203b372, "true") : CRCD(0xd43297cf, "false"));
	}
	
	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateComponent::Reset (   )
{
	SetDoingTrick(false);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static int	s_get_powerup_index( uint32 type )
{
	int i;

	for( i = 0; i < vNUM_POWERUPS; i++ )
	{
		if( s_powerups[i] == type )
		{
			return i;
		}
	}

	return -1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterStateComponent::HasPowerup( uint32 type )
{
	int index;

	index = s_get_powerup_index( type );
	if( index == -1 )
	{
		return false;
	}
	
	return m_powerups[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateComponent::ClearPowerups( void )
{
	int i;

	for( i = 0; i < vNUM_POWERUPS; i++ )
	{
		m_powerups[i] = false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateComponent::PickedUpPowerup( uint32 type )
{
	int index;

	index = s_get_powerup_index( type );
	if( index == -1 )
	{
		return;
	}
	
	m_powerups[index] = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}


================================================
FILE: Code/Sk/Components/SkaterStateComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterStateComponent.h
//* OWNER:          Dan
//* CREATION DATE:  3/31/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERSTATECOMPONENT_H__
#define __COMPONENTS_SKATERSTATECOMPONENT_H__

#include 
#include 
#include 

#include 

#include 

#include 

#define		CRC_SKATERSTATE CRCD(0x43686585, "SkaterState")

#define		GetSkaterStateComponent() ((Obj::CSkaterStateComponent*)GetComponent(CRC_SKATERSTATE))
#define		GetSkaterStateComponentFromObject(pObj) ((Obj::CSkaterStateComponent*)(pObj)->GetComponent(CRC_SKATERSTATE))

namespace Script
{
    class CScript;
    class CStruct;
}
              
namespace Obj
{
	enum
	{
		vFIREBALL,
		vNUM_POWERUPS
	};
	
	enum EPhysicsStateType
	{
		NO_STATE = -1,
		SKATING,
		WALKING,
#		ifdef TESTING_GUNSLINGER
		RIDING
#		endif
	};

class CSkaterStateComponent : public CBaseComponent
{
	friend class CSkaterNonLocalNetLogicComponent;
	friend class CSkaterLocalNetLogicComponent;
	friend class CSkaterCorePhysicsComponent;
	friend class CSkaterAdjustPhysicsComponent;
	friend class CSkaterFinalizePhysicsComponent;
	friend class CSkaterFlipAndRotateComponent;
	friend class CSkaterPhysicsControlComponent;
	friend class CWalkComponent;
				  
public:
    CSkaterStateComponent();
    virtual ~CSkaterStateComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
public:
	void							Reset (   );
	
	bool							DoingTrick (   ) { return m_doing_trick; }
	bool							HasPowerup( uint32 type );
	void							ClearPowerups( void );
	void							PickedUpPowerup( uint32 type );
	void							SetDoingTrick ( bool doing_trick ) { m_doing_trick = doing_trick; }
	EStateType						GetState (   ) { return m_state; }
	bool							GetFlag ( ESkaterFlag flag ) { return m_skater_flags[flag].Get(); }
	ETerrainType					GetTerrain (   ) { return m_terrain; }
	Mth::Vector&					GetCameraDisplayNormal (   ) { return m_camera_display_normal; }
	Mth::Vector&					GetCameraCurrentNormal (   ) { return m_camera_current_normal; }
	Mth::Vector&					GetSpineVel (   ) { return m_spine_vel; }
	bool							GetJumpedOutOfLipTrick (   ) { return mJumpedOutOfLipTrick; }
	float							GetHeight (   ) { return m_height; }
	bool							GetDriving (   ) { return m_driving; }
	bool							GetPhysicsState (   ) { return m_physics_state; }
	
private:
	EStateType						m_state;
	
	EPhysicsStateType				m_physics_state;

	CTimestampedFlag				m_skater_flags [ NUM_ESKATERFLAGS ];
	
	bool							m_doing_trick;
	
	ETerrainType					m_terrain;						// current terrain type on which the skater is skating
	
	Mth::Vector						m_spine_vel;					// velocity to move over the spine
	
	bool							mJumpedOutOfLipTrick;			// Gets set when the player jumps out of a lip trick, and reset when they land.
																	// Controlled by the c-code, not script.
																	// Used by the camera.
	
	Mth::Vector						m_camera_display_normal;		// skater's up vector for the purposes of the camera
	Mth::Vector						m_camera_current_normal;		// skater's up vector for the purposes of the camera
	
	bool							m_driving;						// true if the skater is driving a car
	
	float							m_height;
	bool							m_powerups[vNUM_POWERUPS];
};

}

#endif


================================================
FILE: Code/Sk/Components/SkaterStateHistoryComponent.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterStateHistoryComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/13/3
//****************************************************************************

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CBaseComponent* CSkaterStateHistoryComponent::s_create()
{
	return static_cast< CBaseComponent* >( new CSkaterStateHistoryComponent );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterStateHistoryComponent::CSkaterStateHistoryComponent() : CBaseComponent()
{
	SetType( CRC_SKATERSTATEHISTORY );

	m_last_anm_time = 0;
	m_num_pos_updates = 0;
	m_num_anim_updates = 0;
	memset( mp_pos_history, 0, sizeof( SPosEvent ) * vNUM_POS_HISTORY_ELEMENTS );
	memset( mp_anim_history, 0, sizeof( SAnimEvent ) * vNUM_ANIM_HISTORY_ELEMENTS );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CSkaterStateHistoryComponent::~CSkaterStateHistoryComponent()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateHistoryComponent::InitFromStructure( Script::CStruct* pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateHistoryComponent::RefreshFromStructure( Script::CStruct* pParams )
{
	InitFromStructure(pParams);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateHistoryComponent::Update()
{
	Suspend(true);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBaseComponent::EMemberFunctionResult CSkaterStateHistoryComponent::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( Checksum )
	{
		default:
			return CBaseComponent::MF_NOT_EXECUTED;
	}
    return CBaseComponent::MF_TRUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateHistoryComponent::GetDebugInfo(Script::CStruct *p_info)
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert(p_info,("NULL p_info sent to CSkaterStateHistoryComponent::GetDebugInfo"));

	CBaseComponent::GetDebugInfo(p_info);	  
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterStateHistoryComponent::CheckForCrownCollision(   )
{
	Obj::CCrown* crown = GameNet::Manager::Instance()->GetCrown();
	Dbg_Assert(crown && !crown->OnKing());
	
	return (get_latest_position() - crown->GetPosition()).Length() < CCrown::vCROWN_RADIUS;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterStateHistoryComponent::CollideWithOtherSkaters( int start_index )
{
    GameNet::PlayerInfo* p_player;

	p_player = GameNet::Manager::Instance()->GetPlayerByObjectID(GetObject()->GetID());
	if( p_player == NULL )
	{
		return;
	}

	// If we've marked you as non-collidable, you are exempt from collision
	// In koth, if collision is turned off, only the king is collidable
	if (p_player->IsNonCollidable()
		|| (!GameNet::Manager::Instance()->PlayerCollisionEnabled() && !p_player->IsKing() && !p_player->HasCTFFlag())) return;
	
	CSkaterStateComponent* p_my_state_component = GetSkaterStateComponentFromObject(GetSkater());
	Dbg_Assert(p_my_state_component);

	// If this p_player is bailing or noncollidable (and he's NOT the king) he is exempt from being smacked down
	// If you're in a slap game, you can always be hit; i.e. you've just been slapped but haven't teleported yet
    bool can_fall = !p_my_state_component->GetFlag(IS_BAILING) || CFuncs::ScriptInSlapGame(NULL, NULL);
	
	// However, fallen kings are vulnerable
	if (!can_fall && !p_player->IsKing() && !p_player->HasCTFFlag()) return;
	
	bool my_driving = p_my_state_component->GetDriving();
	Mth::Vector my_pos = get_latest_position();
	Mth::Vector my_vel = get_vel();
	Mth::Vector my_dir = my_vel;
	my_dir.Normalize();

	Mth::Line my_line;
	my_line.m_start = my_pos;

	// Loop through all other skaters and check for collisions
	for (int i = start_index; i < Mdl::Skate::vMAX_SKATERS; i++)
	{
		CSkater* p_other_skater = Mdl::Skate::Instance()->GetSkater(i);

		if (!p_other_skater
			|| p_other_skater == GetSkater()) continue;
		
		CSkaterStateComponent* p_other_state_component = GetSkaterStateComponentFromObject(p_other_skater);
		Dbg_Assert(p_other_state_component);
		if (p_other_state_component->GetFlag(IS_BAILING)) continue;
		
		GameNet::PlayerInfo* p_other_player = p_other_player = GameNet::Manager::Instance()->GetPlayerByObjectID(p_other_skater->GetID());
		Dbg_Assert(p_other_player);
		
		// Non-Collidable people and kings should never win
		if (p_other_player->IsNonCollidable() || p_other_player->IsKing()) continue;

		// If both players are carrying flags, and the subject doesn't have the other guy's flag, don't let them collide
		if (p_player->HasCTFFlag() && p_other_player->HasCTFFlag() && p_player->HasWhichFlag() != p_other_player->m_Team) continue;

		// No smacking teammates
		if (Mdl::Skate::Instance()->GetGameMode()->IsTeamGame() && p_other_player->m_Team == p_player->m_Team) continue;

		bool other_driving = p_other_state_component->GetDriving();
		Mth::Vector other_pos = p_other_skater->mp_skater_state_history_component->get_latest_position();
		Mth::Vector other_vel = p_other_skater->mp_skater_state_history_component->get_vel();
		Mth::Vector other_dir = other_vel;
		other_dir.Normalize();
		
		if( other_vel.Length() > 100.0f )
		{
			continue;
		}
		
		bool collided = false;
		
		// Collision extents are based upon the "other" skater -- whether or not he's a remote skater
		
		float collide_dist = get_collision_cylinder_radius(my_driving, other_driving);
		float driving_radius_multiplier = 1.0f;
		if (my_driving)
		{
			driving_radius_multiplier += Script::GetFloat("driving_radius_boost");
		}
		if (other_driving)
		{
			driving_radius_multiplier += Script::GetFloat("driving_radius_boost");
		}
		
		my_line.m_end = my_line.m_start + my_dir * get_collision_cylinder_coeff(my_driving);
		
		Mth::Line other_line;
		other_line.m_start = other_pos;
		other_line.m_end = other_line.m_start + other_dir * p_other_skater->mp_skater_state_history_component->get_collision_cylinder_coeff(other_driving);
		
		float temp;
		Mth::Vector my_pt, other_pt;
		if (Mth::LineLineIntersect(my_line, other_line, &my_pt, &other_pt, &temp, &temp, true))
		{
			if ((my_pt - other_pt).Length() < collide_dist)
			{
				collided = true;
			}   
		}
		else
		{
			// No solution exists -- lines must be parallel. Try testing endpoint lengths
			// Note: This only really works if velocities are relatively small
			if( (other_line.m_start - my_line.m_start).Length() < collide_dist)
			{
				collided = true;
				other_pt = other_line.m_start;
				my_pt = my_line.m_start;
			}
			else if ((other_line.m_start - my_line.m_end).Length() < collide_dist)
			{
				collided = true;
				other_pt = other_line.m_start;
				my_pt = my_line.m_end;
			}
			else if ((other_line.m_end - my_line.m_start).Length() < collide_dist)
			{
				collided = true;
				other_pt = other_line.m_end;
				my_pt = my_line.m_start;
			}
			else if ((other_line.m_end - my_line.m_end).Length() < collide_dist)
			{
				collided = true;
				other_pt = other_line.m_end;
				my_pt = my_line.m_end;
			}
		}
		
		if (!collided) continue;
		
		// If the "other" p_player is going faster or if
		// the subject p_player is king or if
		// the subject is skating and we're driving, the subject loses
		if (((my_driving == other_driving && my_vel.Length() > other_vel.Length())
			 || (my_driving && !other_driving)) && !p_player->IsKing()) continue;
		
		//Dbg_Printf( "**** My Vel: %.2f, Theirs: %.2f\n", my_vel.Length(), other_vel.Length() );
		Net::Server* p_server = GameNet::Manager::Instance()->GetServer();
		Dbg_Assert(p_server);
			
		if (can_fall)
		{
			GameNet::MsgCollideLost lost_msg;
			GameNet::MsgByteInfo won_msg;
			Net::MsgDesc msg_desc;

			// basically a one-byte message explaining "You've been knocked down by ID"
			lost_msg.m_Id = p_other_skater->GetID();
			lost_msg.m_Id |= (1 << 7) * other_driving;
			lost_msg.m_Offset = my_pt - other_pt;

			if (CInputComponent* p_input_component = GetInputComponentFromObject(GetObject()))
			{
				p_input_component->DisableInput();
			}
			p_player->MarkAsNonCollidable();
			
			msg_desc.m_Id = GameNet::MSG_ID_SKATER_COLLIDE_LOST;
			msg_desc.m_Data = &lost_msg;
			msg_desc.m_Length = sizeof(GameNet::MsgCollideLost);
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
			p_server->EnqueueMessage(p_player->GetConnHandle(), &msg_desc);

			// basically a one-byte message explaining "You knocked someone down"
			won_msg.m_Data = GetObject()->GetID();
			won_msg.m_Data |= (1 << 7) * my_driving;

			msg_desc.m_Id = GameNet::MSG_ID_SKATER_COLLIDE_WON;
			msg_desc.m_Data = &won_msg;
			msg_desc.m_Length = sizeof(GameNet::MsgByteInfo);
			p_server->EnqueueMessage(p_other_player->GetConnHandle(), &msg_desc);	
			
			if (CFuncs::ScriptInSlapGame(NULL, NULL))
			{
				Dbg_Assert(p_other_skater->mp_skater_score_component);
				Mdl::Score* score = p_other_skater->mp_skater_score_component->GetScore();
				score->SetTotalScore(score->GetTotalScore() + 1);
			}
		}
		
		if (Mdl::Skate::Instance()->GetGameMode()->GetNameChecksum() == CRCD(0x6ef8fda0, "netking")
			|| Mdl::Skate::Instance()->GetGameMode()->GetNameChecksum() == CRCD(0x5d32129c, "king"))
		{
			// Don't bother switching kings if the game is over
			if (Mdl::Skate::Instance()->GetGameMode()->EndConditionsMet()) return;
			
			// If the king was just slapped, the "slapper" (hehe) is the new king
			if (!p_player->IsKing()) continue;
			
			// It is important that we mark the king immediately (rather than through a
			// network message) since we do logic based on the "current" king
			p_other_player->MarkAsKing(true);
			
			Lst::Search< GameNet::PlayerInfo > search;
			for (GameNet::PlayerInfo* p_player = GameNet::Manager::Instance()->FirstPlayerInfo(search, true);
				p_player;
				p_player = GameNet::Manager::Instance()->NextPlayerInfo(search, true))
			{
				// Already marked the king for the local p_player (above)
				if (p_player->IsLocalPlayer()) continue;
				
				GameNet::MsgByteInfo msg;
				Net::MsgDesc msg_desc;
				
				msg.m_Data = p_other_skater->GetID();
				msg_desc.m_Data = &msg;
				msg_desc.m_Id = GameNet::MSG_ID_NEW_KING;
				msg_desc.m_Length = sizeof(GameNet::MsgByteInfo);
				msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
				msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
				p_server->EnqueueMessage(p_player->GetConnHandle(), &msg_desc);
			}
		}
		else if (Mdl::Skate::Instance()->GetGameMode()->GetNameChecksum() == CRCD(0x6c5ff266, "netctf"))
		{
			GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
			// Don't bother handling this if the game is over
			//if (Mdl::Skate::Instance()->GetGameMode()->EndConditionsMet())
			if( gamenet_man->GameIsOver())
			{
				return;
			}
			
			if (!p_player->HasCTFFlag())
			{
				continue;
			}
			
			GameNet::MsgFlagMsg msg;
			Net::MsgDesc msg_desc;

			msg.m_ObjId = p_other_skater->GetID();
			msg.m_Team = p_player->HasWhichFlag();

			bool retrieve = p_player->HasWhichFlag() == p_other_player->m_Team;

			Lst::Search< GameNet::PlayerInfo > search;
			for (GameNet::PlayerInfo* send_player = GameNet::Manager::Instance()->FirstPlayerInfo(search, true);
				send_player; 
				send_player = GameNet::Manager::Instance()->NextPlayerInfo(search, true))
			{
				// If you knock down the p_player with your team's flag, you recover it. Otherwise, you steal it
				if (retrieve)
				{
					if (send_player->IsLocalPlayer())
					{
						p_other_player->RetrievedFlag();
						continue;
					}
					msg_desc.m_Data = &msg;
					msg_desc.m_Length = sizeof(GameNet::MsgFlagMsg);
					msg_desc.m_Id = GameNet::MSG_ID_RETRIEVED_FLAG;
					msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
					msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
					p_server->EnqueueMessage(send_player->GetConnHandle(), &msg_desc);
				}
				else
				{
					if (send_player->IsLocalPlayer())
					{
						p_other_player->StoleFlag(p_player->HasWhichFlag());
						continue;
					}
					msg_desc.m_Data = &msg;
					msg_desc.m_Length = sizeof(GameNet::MsgFlagMsg);
					msg_desc.m_Id = GameNet::MSG_ID_STOLE_FLAG;
					msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
					msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
					p_server->EnqueueMessage(send_player->GetConnHandle(), &msg_desc);
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int			CSkaterStateHistoryComponent::sHandleProjectileHit ( Net::MsgHandlerContext* context )
{
	CSkater* p_skater = (CSkater*) (context->m_Data);
	GameNet::MsgProjectileHit* p_msg = (GameNet::MsgProjectileHit*) (context->m_Msg);
	
	switch (context->m_MsgId)
	{
		case GameNet::MSG_ID_SKATER_PROJECTILE_HIT_TARGET:
		{
			char name[32] = "Someone";
			
			if (GameNet::PlayerInfo* p_other_player = GameNet::Manager::Instance()->GetPlayerByObjectID(p_msg->m_Id))
			{
				strncpy(name, p_other_player->m_Skater->GetDisplayName(), 32);
			}

			Script::CStruct params;
			params.AddString(CRCD(0xa4b08520, "String0"), name);
			params.AddChecksum( NONAME, CRCD(0xd039432c,"fireball") );
			p_skater->SelfEvent(CRCD(0xa1021af0, "MadeOtherSkaterBail"),¶ms);
			
			break;
		}
		
		case GameNet::MSG_ID_SKATER_HIT_BY_PROJECTILE:
		{
			char name[32] = "Someone";

			if (GameNet::PlayerInfo* p_other_player = GameNet::Manager::Instance()->GetPlayerByObjectID(p_msg->m_Id))
			{
				GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
				Mth::Vector offset = p_skater->GetPos() - p_other_player->m_Skater->GetPos();
				// Perform a "safe" normalization
				float offset_length = offset.Length();
				if (offset_length < 0.0001f)
				{
					// if skaters are super close, or coincident then pretend they are not, to avoid zero length vectors
					offset.Set(1.0f, 0.0f, 1.0f);
				}
				else
				{
					offset /= offset_length;
				}
				
				// fudge it for now
				// float speed = p_other_skater->GetVel().Length();
				float speed = 300.0f;
				p_skater->GetVel() = offset * speed * 1.5f; 
				p_skater->GetVel()[Y] = 200.0f;

				CSkaterPhysicsControlComponent* p_skater_physics_control_component = GetSkaterPhysicsControlComponentFromObject(p_skater);
				if (p_skater_physics_control_component->IsSkating())
				{
					CSkaterCorePhysicsComponent* p_skater_core_physics_component = GetSkaterCorePhysicsComponentFromObject(p_skater);
					p_skater_core_physics_component->CollideWithOtherSkaterLost(p_other_player->m_Skater);
				}
				else
				{
					CWalkComponent* p_walk_component = GetWalkComponentFromObject(p_skater);
					p_walk_component->CollideWithOtherSkaterLost(p_other_player->m_Skater);
				}

				strncpy(name, p_other_player->m_Skater->GetDisplayName(), 32);
				

				if( gamenet_man->OnServer() == false )
				{
					Mdl::Score* score = p_skater->mp_skater_score_component->GetScore();
					if(( score->GetTotalScore() - p_msg->m_Damage ) < 0 )
					{
						score->SetTotalScore( 0 );
					}
					else
					{
						score->SetTotalScore( score->GetTotalScore() - p_msg->m_Damage );
					}
				}
			}

			Script::CStruct params;
			params.AddString(CRCD(0xa4b08520, "String0"), name);
			params.AddChecksum( NONAME, CRCD(0xd039432c,"fireball") );
			p_skater->SelfEvent(CRCD(0x915e5e39, "SkaterCollideBail"),¶ms);
			break;
		}
		
		default:
			Dbg_Assert(false);
			break;
	}
		
	return Net::HANDLER_MSG_DONE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	CSkaterStateHistoryComponent::sHandleCollision( Net::MsgHandlerContext* context )
{
	CSkater* p_skater = (CSkater*) (context->m_Data);
	
	switch (context->m_MsgId)
	{
		case GameNet::MSG_ID_SKATER_COLLIDE_WON:
		{
			GameNet::MsgByteInfo* p_msg = (GameNet::MsgByteInfo*) (context->m_Msg);
			char name[32] = "Someone";
			
			bool loser_drivng = p_msg->m_Data & (1 << 7);
			p_msg->m_Data &= ~(1 << 7);
			
			if (GameNet::PlayerInfo* p_other_player = GameNet::Manager::Instance()->GetPlayerByObjectID(p_msg->m_Data))
			{
				strncpy(name, p_other_player->m_Skater->GetDisplayName(), 32);
			}

			Script::CStruct params;
			params.AddString(CRCD(0xa4b08520, "String0"), name);
			if (loser_drivng)
			{
				params.AddChecksum(NO_NAME, CRCD(0x7fd0663d, "LoserIsDriving"));
			}
			p_skater->SelfEvent(CRCD(0xa1021af0, "MadeOtherSkaterBail"),¶ms);
			
			break;
		}
		
		case GameNet::MSG_ID_SKATER_COLLIDE_LOST:
		{
			GameNet::MsgCollideLost* p_msg = (GameNet::MsgCollideLost*) (context->m_Msg);
			char name[32] = "Someone";
			
			bool winner_driving = p_msg->m_Id & (1 << 7);
			p_msg->m_Id &= ~(1 << 7);

			if (GameNet::PlayerInfo* p_other_player = GameNet::Manager::Instance()->GetPlayerByObjectID(p_msg->m_Id))
			{
				CSkaterPhysicsControlComponent* p_skater_physics_control_component = GetSkaterPhysicsControlComponentFromObject(p_skater);
				
				if (!p_skater_physics_control_component->IsDriving())
				{
					Mth::Vector offset = p_skater->GetPos() - p_other_player->m_Skater->GetPos();
					// Perform a "safe" normalization
					float offset_length = offset.Length();
					if (offset_length < 0.0001f)
					{
						// if skaters are super close, or coincident then pretend they are not, to avoid zero length vectors
						offset.Set(1.0f, 0.0f, 1.0f);
					}
					else
					{
						offset /= offset_length;
					}
					
					// fudge it for now
					// float speed = p_other_skater->GetVel().Length();
					float speed = 300.0f;
					p_skater->GetVel() = offset * speed * 1.5f; 
					p_skater->GetVel()[Y] = 200.0f;
	
					if (p_skater_physics_control_component->IsSkating())
					{
						CSkaterCorePhysicsComponent* p_skater_core_physics_component = GetSkaterCorePhysicsComponentFromObject(p_skater);
						p_skater_core_physics_component->CollideWithOtherSkaterLost(p_other_player->m_Skater);
					}
					else
					{
						CWalkComponent* p_walk_component = GetWalkComponentFromObject(p_skater);
						p_walk_component->CollideWithOtherSkaterLost(p_other_player->m_Skater);
					}
				}

				strncpy(name, p_other_player->m_Skater->GetDisplayName(), 32);
			}

			Script::CStruct params;
			params.AddString(CRCD(0xa4b08520, "String0"), name);
			params.AddVector(CRCD(0xa6f5352f, "Offset"), p_msg->m_Offset);
			if (winner_driving)
			{
				params.AddChecksum(NO_NAME, CRCD(0x2f679251, "WinnerIsDriving"));
			}
			p_skater->SelfEvent(CRCD(0x915e5e39, "SkaterCollideBail"),¶ms);
			break;
		}
		
		default:
			Dbg_Assert(false);
			break;
			
	}
		
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector	CSkaterStateHistoryComponent::get_vel (   )
{
	return GetObject()->m_vel * Tmr::UncappedFrameLength();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CSkaterStateHistoryComponent::get_latest_position(   )
{
	return GetObject()->m_pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CSkaterStateHistoryComponent::get_last_position(   )
{
    return GetObject()->m_old_pos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	CSkaterStateHistoryComponent::get_time_between_last_update(   )
{
	int time_elapsed;
	
	time_elapsed = static_cast< int >(Tmr::FrameLength());
	
	if( time_elapsed == 0 )
	{
		time_elapsed = Tmr::VBlanks( 1 );
	}
	return time_elapsed;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CSkaterStateHistoryComponent::get_collision_cylinder_coeff( bool driving )
{
    // multiplied by the velocity of the skater to give the length of a line along which to check for collision
	
	float radius;
	
	if (GetSkater()->IsLocalClient())
	{
        radius = GetPhysicsFloat( CRCD(0xd24273b, "LanServerCollCoefficient"), Script::ASSERT);
	}
	else
	{
		radius = GetPhysicsFloat( CRCD(0x25f78481, "LanClientCollCoefficient"), Script::ASSERT);
	}
	
	if (!driving)
	{
		return radius;
	}
	else
	{
        return radius * (1.0f + Script::GetFloat(CRCD(0xa49ff23, "DrivingCoefficientBoostFactor")));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CSkaterStateHistoryComponent::get_collision_cylinder_radius( bool first_driving, bool second_driving )
{
	float radius;
	
	if (GetSkater()->IsLocalClient())
	{
		if (GameNet::Manager::Instance()->InInternetMode())
		{
			radius = GetPhysicsFloat(CRCD(0xf2e35fa3, "InternetServerCollRadius"), Script::ASSERT);
		}
		else
		{
			radius = GetPhysicsFloat(CRCD(0xecb41860, "LanServerCollRadius"), Script::ASSERT);
		}
	}
	else
	{
		if (GameNet::Manager::Instance()->InInternetMode())
		{
			radius = GetPhysicsFloat(CRCD(0xdbd6d58a, "InternetClientCollRadius"), Script::ASSERT);
		}
		else
		{
			radius = GetPhysicsFloat(CRCD(0xc5819249, "LanClientCollRadius"), Script::ASSERT);
		}
	}
	
	float driving_count = 0;
	if (first_driving)
	{
		driving_count++;
	}
	if (second_driving)
	{
		driving_count++;
	}
	
	return radius * (1.0f + driving_count * GetPhysicsFloat(CRCD(0xeef4afb3, "DrivingRadiusBoostFactor")));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SPosEvent::SPosEvent( void )
{
	memset( this, 0, sizeof( SPosEvent ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	SPosEvent::GetTime( void )
{
	uint32 time;

	time = ( HiTime << 16 ) | ( LoTime );

	return time;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			SPosEvent::SetTime( uint32 time )
{
	LoTime = (unsigned short) ( time & 0xFFFF );
	HiTime = (unsigned short) ( time >> 16 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SAnimEvent::SAnimEvent( void )
{
	memset( this, 0, sizeof( SAnimEvent ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	SAnimEvent::GetTime( void )
{
	uint32 time;

	time = ( m_HiTime << 16 ) | ( m_LoTime );

	return time;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			SAnimEvent::SetTime( uint32 time )
{
	m_LoTime = (unsigned short) ( time & 0xFFFF );
	m_HiTime = (unsigned short) ( time >> 16 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32			CSkaterStateHistoryComponent::GetLatestAnimTimestamp( void )
{
	return m_last_anm_time;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			CSkaterStateHistoryComponent::SetLatestAnimTimestamp( uint32 timestamp )
{
	m_last_anm_time = timestamp;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}


================================================
FILE: Code/Sk/Components/SkaterStateHistoryComponent.h
================================================
//****************************************************************************
//* MODULE:         Sk/Components
//* FILENAME:       SkaterStateHistoryComponent.cpp
//* OWNER:          Dan
//* CREATION DATE:  3/13/3
//****************************************************************************

#ifndef __COMPONENTS_SKATERSTATEHISTORYCOMPONENT_H__
#define __COMPONENTS_SKATERSTATEHISTORYCOMPONENT_H__

#include 
#include 

#include 

#include 
#include 

#define		CRC_SKATERSTATEHISTORY CRCD(0x18223fd6, "SkaterStateHistory")

#define		GetSkaterStateHistoryComponent() ((Obj::CSkaterStateHistoryComponent*)GetComponent(CRC_SKATERSTATEHISTORY))
#define		GetSkaterStateHistoryComponentFromObject(pObj) ((Obj::CSkaterStateHistoryComponent*)(pObj)->GetComponent(CRC_SKATERSTATEHISTORY))

namespace Script
{
    class CScript;
    class CStruct;
}

namespace Net
{
	class MsgHandlerContext;
}
              
namespace Obj
{

class SPosEvent
{   
public:
	SPosEvent( void );

	uint32			GetTime( void );
	void			SetTime( uint32 time );

	short			ShortPos[3];
	Mth::Matrix		Matrix;
	Mth::Vector		Position;
	Mth::Vector		Eulers;
	Flags< int >	SkaterFlags;
	Flags< int >	EndRunFlags;
	int				State;
	char			DoingTrick;
	char			Walking;
	char			Driving;
	uint16			LoTime;
	uint16			HiTime;
	ETerrainType	Terrain;
	sint16			RailNode;
};

class SAnimEvent
{   
public:
	SAnimEvent( void );

	uint32			GetTime( void );
	void			SetTime( uint32 time );

	char			m_MsgId;
	char			m_ObjId;
	char			m_LoopingType;
	char			m_Flags;
	uint16			m_LoTime;
	uint16			m_HiTime;
	bool			m_Flipped;
	bool			m_Rotate;
	bool			m_Hide;
	float			m_Alpha;
	float			m_StartTime;
	float			m_EndTime;
	float			m_BlendPeriod;
	float 			m_Speed;
	uint32			m_Asset;
	uint32			m_Bone;
	float			m_WobbleAmpA;
	float			m_WobbleAmpB;
	float			m_WobbleK1;
	float			m_WobbleK2;
	float			m_SpazFactor;
	int 			m_Duration;
	int 			m_SinePower;
	int				m_Index;
	float 			m_StartAngle;
	float 			m_DeltaAngle;
	bool			m_HoldOnLastAngle;
};

class CSkaterStateHistoryComponent : public CBaseComponent
{
public:
	enum
	{
		vNUM_POS_HISTORY_ELEMENTS = 20,
		vNUM_ANIM_HISTORY_ELEMENTS = 20
	};
	
public:
    CSkaterStateHistoryComponent();
    virtual ~CSkaterStateHistoryComponent();

public:
    virtual void            		Update();
    virtual void            		InitFromStructure( Script::CStruct* pParams );
    virtual void            		RefreshFromStructure( Script::CStruct* pParams );
    
    virtual EMemberFunctionResult   CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	virtual void 					GetDebugInfo( Script::CStruct* p_info );

	static CBaseComponent*			s_create();
	
	CSkater*						GetSkater() { return static_cast< CSkater* >(GetObject()); }
	
	static int						sHandleCollision ( Net::MsgHandlerContext* context );
	static int						sHandleProjectileHit ( Net::MsgHandlerContext* context );
	
	SPosEvent*						GetPosHistory (   ) { return mp_pos_history; }
	SPosEvent*						GetLatestPosEvent (   ) { return &mp_pos_history[m_num_pos_updates % vNUM_POS_HISTORY_ELEMENTS]; }
	SPosEvent*						GetLastPosEvent (   ) { return &mp_pos_history[(m_num_pos_updates + ( vNUM_POS_HISTORY_ELEMENTS - 1 )) % vNUM_POS_HISTORY_ELEMENTS]; }
	void							IncrementNumPosUpdates (   ) { m_num_pos_updates++; }
	void							ResetPosHistory (   ) { m_num_pos_updates = 0; }
	int								GetNumPosUpdates (   ) { return m_num_pos_updates; }
	
	uint32							GetLatestAnimTimestamp( void );
	void							SetLatestAnimTimestamp( uint32 timestamp );
	SAnimEvent*						GetAnimHistory (   ) { return mp_anim_history; }
	SAnimEvent*						GetLatestAnimEvent (   ) { return &mp_anim_history[m_num_anim_updates % vNUM_ANIM_HISTORY_ELEMENTS]; }
	SAnimEvent*						GetLastAnimEvent (   ) { return &mp_anim_history[(m_num_anim_updates + ( vNUM_ANIM_HISTORY_ELEMENTS - 1 )) % vNUM_ANIM_HISTORY_ELEMENTS]; }
	void							IncrementNumAnimUpdates (   ) { m_num_anim_updates++; }
	void							ResetAnimHistory (   ) { m_num_anim_updates = 0; }
	int								GetNumAnimUpdates (   ) { return m_num_anim_updates; }
	
	bool							CheckForCrownCollision (   );
	void							CollideWithOtherSkaters ( int start_index );
	bool							GetCollidingPlayerAndTeam ( Script::CStruct* pParams, Script::CScript* pScript );
	
	void							SetCurrentVehicleControlType ( uint32 control_type ) { m_current_vehicle_control_type = control_type; }
	uint32							GetCurrentVehicleControlType (   ) { return m_current_vehicle_control_type; }
	
private:
	Mth::Vector						get_latest_position (   );
	Mth::Vector						get_last_position (   );
	Mth::Vector						get_vel (   );
	int								get_time_between_last_update (   );
	
	float							get_collision_cylinder_coeff ( bool driving );
	float							get_collision_cylinder_radius ( bool first_driving, bool second_driving );
	
private:
	int								m_num_pos_updates;
	SPosEvent						mp_pos_history [ vNUM_POS_HISTORY_ELEMENTS ];
	
	int								m_num_anim_updates;
	SAnimEvent						mp_anim_history [ vNUM_ANIM_HISTORY_ELEMENTS ];

        uint32							m_last_anm_time;
	// if the non-local client is driving, this control type's model will be used; set via a network message
	uint32							m_current_vehicle_control_type;
};

}

#endif


================================================
FILE: Code/Sk/Engine/RectFeeler.cpp
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Sk/Engine												**
**																			**
**	File name:		RectFeeler.cpp											**
**																			**
**	Created: 		02/14/2003	-	Dan										**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
		
#include 

#include 

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

Nx::CCollCache* CRectFeeler::sp_default_cache = NULL;

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

// BUG: getting false collisions when rectangle is flush with axes

CRectFeeler::CRectFeeler (   )
{
	init();
}

CRectFeeler::CRectFeeler ( const Mth::Vector& corner, const Mth::Vector& first_edge, const Mth::Vector& second_edge )
{
	m_corner = corner;
	m_first_edge = first_edge;
	m_second_edge = second_edge;
	
	init();
}

void CRectFeeler::init (   )
{
	mp_cache = NULL;
	m_coll_data.num_surfaces = 0;
	m_merged_coll_data.num_surfaces = 0;
	
	m_coll_data.ignore_1 = mFD_NON_COLLIDABLE;
	m_coll_data.ignore_0 = 0;
}

bool CRectFeeler::GetCollision ( bool movables )
{
	Dbg_MsgAssert(!movables, ("Collision test against movables not yet supported by CRectFeeler"));
	
	Rectangle& rect = *this;
	
	return Nx::CCollObj::sFindRectangleStaticCollision(rect, &m_coll_data, mp_cache ? mp_cache : sp_default_cache);
}

void CRectFeeler::MergeCollisionSurfaces (   )
{
	// precomputed values
	bool p_surfaces_used[Nx::MAX_NUM_2D_COLLISIONS_REPORTED];
	Mth::Vector p_line_directions[Nx::MAX_NUM_2D_COLLISIONS_REPORTED];
	for (int n = m_coll_data.num_surfaces; n--; )
	{
		p_surfaces_used[n] = false;
		
		p_line_directions[n] = m_coll_data.p_surfaces[n].ends[1].point - m_coll_data.p_surfaces[n].ends[0].point;
		p_line_directions[n].Normalize();
	}
	
	// drop duplicate surfaces
	for (int i = m_coll_data.num_surfaces; i--; )
	{
		Nx::S2DCollSurface& surface_i = m_coll_data.p_surfaces[i];
		
		if (p_surfaces_used[i]) continue;
		
		for (int j = i; j--; )
		{
			Nx::S2DCollSurface& surface_j = m_coll_data.p_surfaces[j];
			if ((very_close(surface_i.ends[0].point, surface_j.ends[0].point) && very_close(surface_i.ends[1].point, surface_j.ends[1].point))
				|| (very_close(surface_i.ends[0].point, surface_j.ends[1].point) && very_close(surface_i.ends[1].point, surface_j.ends[0].point)))
			{
				if (Mth::DotProduct(surface_i.normal, surface_j.normal) > 0.99f)
				{
					p_surfaces_used[j] = true;
					continue;
				}
			}
		}
	}
	
	// we build a merged surface list out of the collision data surface list, attempting to reduce the number of surfaces by merging those which
	// are equivalent for the purpose of collision lines; that is, merge chains of collision lines which are parallel and have equal surface normals
	
	// for each surface, first attempt to absorb as yet unvisited surfaces; then, attempt to absorb the surface into a surface previously accepted into
	// the merged surface list; if unsuccessful, add the surface to the merged surface list
	
	m_merged_coll_data.num_surfaces = 0;
	
	// loop over the collision data surfaces
	for (int proposed_idx = m_coll_data.num_surfaces; proposed_idx--; )
	{
		Nx::S2DCollSurface proposed_surface = m_coll_data.p_surfaces[proposed_idx];
		
		if (p_surfaces_used[proposed_idx]) continue;
		
		// check to absorb unused surfaces into the proposed surface
		
		bool clean_pass;
		do // loop over the unused surfaces until we pass through them cleanly, finding no absorption opportunities
		{
			clean_pass = true;
			
			for (int check_idx = proposed_idx; check_idx--; )
			{
				Nx::S2DCollSurface& check_surface = m_coll_data.p_surfaces[check_idx];
				
				// check that this line is not already used
				if (p_surfaces_used[check_idx]) continue;
				
				// check that surfaces are parallel
				if (Mth::DotProduct(proposed_surface.normal, check_surface.normal) < 0.99f) continue;
				
				// check that collision lines are parallel
				if (Mth::Abs(Mth::DotProduct(p_line_directions[proposed_idx], p_line_directions[check_idx])) < 0.99f) continue;
				
				// check that collision lines connect
				if (very_close(proposed_surface.ends[0].point, check_surface.ends[0].point))
				{
	   				proposed_surface.ends[0].point = check_surface.ends[1].point;
	   				proposed_surface.ends[0].tangent_exists = check_surface.ends[1].tangent_exists;
	   				proposed_surface.ends[0].tangent = check_surface.ends[1].tangent;
					
				}
				else if (very_close(proposed_surface.ends[0].point, check_surface.ends[1].point))
				{
	   				proposed_surface.ends[0].point = check_surface.ends[0].point;
	   				proposed_surface.ends[0].tangent_exists = check_surface.ends[0].tangent_exists;
	   				proposed_surface.ends[0].tangent = check_surface.ends[0].tangent;
				}
				else if (very_close(proposed_surface.ends[1].point, check_surface.ends[0].point))
				{
					proposed_surface.ends[1].point = check_surface.ends[1].point;
	   				proposed_surface.ends[1].tangent_exists = check_surface.ends[1].tangent_exists;
	   				proposed_surface.ends[1].tangent = check_surface.ends[1].tangent;
				}
				else if (very_close(proposed_surface.ends[1].point, check_surface.ends[1].point))
				{
					proposed_surface.ends[1].point = check_surface.ends[0].point;
	   				proposed_surface.ends[1].tangent_exists = check_surface.ends[0].tangent_exists;
	   				proposed_surface.ends[1].tangent = check_surface.ends[0].tangent;
				}
				else
				{
					continue;
				}
				
				// we've successfully absorbed check_surface into proposed_surface
				p_surfaces_used[check_idx] = true;
				
				clean_pass = false;
			} // END loop over unvisited surfaces
		} while (clean_pass = false);
		
		// accept the poropsed surface into the reduced surface list
		
		m_merged_coll_data.p_surfaces[m_merged_coll_data.num_surfaces++] = proposed_surface;
	} // END loop over proposing surfaces
}

void CRectFeeler::DebugLines ( int r, int g, int b, int num_frames ) const
{
	uint32 color = MAKE_RGB(r, g, b);
	
	Gfx::AddDebugLine(m_corner, m_corner + m_first_edge, color, color, num_frames);
	Gfx::AddDebugLine(m_corner + m_first_edge, m_corner + m_first_edge + m_second_edge, color, color, num_frames);
	Gfx::AddDebugLine(m_corner + m_first_edge + m_second_edge, m_corner + m_second_edge, color, color, num_frames);
	Gfx::AddDebugLine(m_corner + m_second_edge, m_corner, color, color, num_frames);
	
	// if we've merged the surfaces
	if (m_merged_coll_data.num_surfaces > 0)
	{
		// draw the merged collision lines
		for (int n = m_merged_coll_data.num_surfaces; n--; )
		{
			color = MAKE_RGB(r += 201, g += 99, b += 31);
			Gfx::AddDebugLine(m_merged_coll_data.p_surfaces[n].ends[0].point, m_merged_coll_data.p_surfaces[n].ends[1].point, color, color, 1);
		}
	}
	else
	{
		// otherwise, draw the bare collision lines
		for (int n = m_coll_data.num_surfaces; n--; )
		{
			color = MAKE_RGB(r += 201, g += 99, b += 31);
			Gfx::AddDebugLine(m_coll_data.p_surfaces[n].ends[0].point, m_coll_data.p_surfaces[n].ends[1].point, color, color, 1);
		}
	}
}
	



================================================
FILE: Code/Sk/Engine/RectFeeler.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Sk/Engine												**
**																			**
**	File name:		RectFeeler.h											**
**																			**
**	Created: 		02/14/2003	-	Dan										**
**																			**
*****************************************************************************/

#ifndef	__SK_ENGINE_RECTFEELER_H
#define	__SK_ENGINE_RECTFEELER_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class	CRectFeeler	: public Mth::Rectangle
{
public:
	CRectFeeler (   );
	CRectFeeler ( const Mth::Vector& corner, const Mth::Vector& first_edge, const Mth::Vector& second_edge );
	
	void							SetRectangle ( const Mth::Rectangle& rect );
	
	bool							GetCollision ( bool movables = false );
	
	int								GetNumCollisionSurfaces (   ) const { return m_coll_data.num_surfaces; }
	const Nx::S2DCollSurface&		GetCollisionSurface ( int n ) const;
	
	int								GetNumMergedCollisionSurfaces (   ) const { return m_merged_coll_data.num_surfaces; }
	const Nx::S2DCollSurface&		GetMergedCollisionSurface ( int n ) const;
	
	void							MergeCollisionSurfaces (   );
	
	void							SetIgnore ( uint16 ignore_1, uint16 ignore_0 );
	
	void							SetCache ( Nx::CCollCache* p_cache ) { mp_cache = p_cache; }
	void							ClearCache (   ) { mp_cache = NULL; }
	
	static void						sSetDefaultCache ( Nx::CCollCache* p_cache ) { sp_default_cache = p_cache; }
	static void						sClearDefaultCache (   ) { sp_default_cache = NULL; }
	
	// ReduceCollisionSurfaces (   )
	
	void							DebugLines ( int r = 255, int g = 255, int b = 255, int num_frames = 0 ) const;

private:
	void							init (   );
	
	bool							very_close ( const Mth::Vector p, const Mth::Vector q ) const;

private:
	
	Nx::S2DCollData					m_coll_data;
	Nx::S2DCollData					m_merged_coll_data;
	
	Nx::CCollCache*					mp_cache;
	
	static Nx::CCollCache*			sp_default_cache;
};

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline void							CRectFeeler::SetRectangle ( const Mth::Rectangle& rect )
{
	m_corner = rect.m_corner;
	m_first_edge = rect.m_first_edge;
	m_second_edge = rect.m_second_edge;
}

inline const Nx::S2DCollSurface&	CRectFeeler::GetCollisionSurface ( int n ) const
{
	Dbg_Assert(n < m_coll_data.num_surfaces);
	return m_coll_data.p_surfaces[n];
}

inline const Nx::S2DCollSurface&	CRectFeeler::GetMergedCollisionSurface ( int n ) const
{
	Dbg_Assert(n < m_merged_coll_data.num_surfaces);
	Dbg_Assert(!isnanf(m_merged_coll_data.p_surfaces[n].ends[0].point[W]));
	Dbg_Assert(!isnanf(m_merged_coll_data.p_surfaces[n].ends[1].point[W]));
	return m_merged_coll_data.p_surfaces[n];
}

inline void							CRectFeeler::SetIgnore ( uint16 ignore_1, uint16 ignore_0 )
{
	m_coll_data.ignore_1 = ignore_1;
	m_coll_data.ignore_0 = ignore_0;
}

inline bool							CRectFeeler::very_close ( const Mth::Vector p, const Mth::Vector q ) const
{
	return Mth::Abs(p[X] - q[X]) < 0.1f && Mth::Abs(p[Y] - q[Y]) < 0.1f && Mth::Abs(p[Z] - q[Z]) < 0.1f;
}

#endif


================================================
FILE: Code/Sk/Engine/SuperSector.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SKATE3													**
**																			**
**	Module:			SSEC					 								**
**																			**
**	File name:		SuperSector.cpp											**
**																			**
**	Created by:		01/12/01	-	spg										**
**																			**
**	Description:	Functions concerning SuperSectors						**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 


#include 					
#include 
#include 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/




namespace SSec
{





/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define	COLL_LINE_EXTENSION	0.5f

#define	vMAX_QUAL_SECTORS		1024	   	// never saw this go above 100

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

class WorldSectorNode : public Lst::Node< Nx::CSector >
{
public:
	WorldSectorNode( Nx::CSector* sector ) : Lst::Node< Nx::CSector > ( sector ) {}
};

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static	uint8	sOperationId = 0;	// operation id for sectors
//#ifdef	__PLAT_NGPS__
//const	Nx::CCollStatic**	QualCollSectors = (const Nx::CCollStatic**)0x70000000;	// use ScratchPad Ram (SPR) for speed
//#else
static	Nx::CCollStatic*	QualCollSectors[vMAX_QUAL_SECTORS];
//#endif

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

Sector::Sector( void )
{
	

	m_NumSectors = 0;
	m_NumCollSectors = 0;
	m_SectorList = NULL;
	m_CollSectorList = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Sector::~Sector( void )
{
	

	if( m_SectorList )
	{   
		delete [] m_SectorList;
	}
	if( m_CollSectorList )
	{   
		Mem::Free(m_CollSectorList);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Sector::remove_collision(Nx::CCollStatic *p_coll)
{
	for (int idx = 0; idx < m_NumCollSectors; idx++)
	{
		if (m_CollSectorList[idx] == p_coll)
		{
			// Shift the rest of the pointers over
			for (int copy_idx = idx + 1; copy_idx < m_NumCollSectors; copy_idx++)
			{
				m_CollSectorList[copy_idx - 1] = m_CollSectorList[copy_idx];
			}

			// And put a NULL on the last one (in case it isn't there, yet)
			m_CollSectorList[m_NumCollSectors - 1] = NULL;

			return true;
		}
	}

	// Never found it
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Sector::replace_collision(Nx::CCollStatic *p_coll, Nx::CCollStatic *p_replace_coll)
{
	for (int idx = 0; idx < m_NumCollSectors; idx++)
	{
		if (m_CollSectorList[idx] == p_coll)
		{
			m_CollSectorList[idx] = p_replace_coll;
			return true;
		}
	}
	
	// Never found it
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Sector::has_collision(Nx::CCollStatic *p_coll)
{
	for (int idx = 0; idx < m_NumCollSectors; idx++)
	{
		if (m_CollSectorList[idx] == p_coll)
		{
			return true;
		}
	}

	// Never found it
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8		GetCurrentSectorOperationId( void )
{
	

	return sOperationId;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8		GetNewSectorOperationId( void )
{   
	

	return ++sOperationId;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void	Manager::GenerateSuperSectors(const Mth::CBBox& world_bbox )
{
	int i, j;
	float world_width, world_depth;
	float start_x, start_z;

	Dbg_Printf( "Generating Blank SuperSectors\n" );

	m_world_bbox = world_bbox;

	world_width = world_bbox.GetMax()[X] - world_bbox.GetMin()[X];
	world_depth = world_bbox.GetMax()[Z] - world_bbox.GetMin()[Z];

	m_sector_width = world_width / NUM_PARTITIONS_X;
	m_sector_depth = world_depth / NUM_PARTITIONS_Z;

	// For each super sector, find any world sectors which intersect the ss bounding box
	// and add that ws to ss's list of ws's
	start_x = world_bbox.GetMin()[X];
		
	Mth::Vector sec_min, sec_max;
	sec_min[W] = 0.0f;		 			// to avoid assertions
	sec_max[W] = 0.0f;
	for( i = 0; i < NUM_PARTITIONS_X; i++ )
	{
		start_z = world_bbox.GetMin()[Z];

		for( j = 0; j < NUM_PARTITIONS_Z; j++ )
		{
			sec_min[X] = start_x;
			sec_min[Y] = (float) -1000000000000.0f; //world_bbox.GetMin()[Y];
			sec_min[Z] = start_z;
            
			sec_max[X] = start_x + m_sector_width;
			sec_max[Y] = (float) 1000000000000.0f; //world_bbox.GetMax()[Y];
			sec_max[Z] = start_z + m_sector_depth;

			m_super_sector_list[i][j].m_Bbox.Set(sec_min, sec_max);
			
			start_z += m_sector_depth;
		}

		start_x += m_sector_width;
	}

	m_num_sectors_x = NUM_PARTITIONS_X;
	m_num_sectors_z = NUM_PARTITIONS_Z;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::AddCollisionToSuperSectors( Nx::CCollStatic *coll, int num_coll_sectors )
{
	Lst::Head< Nx::CCollStatic > sector_list;
	Lst::Node< Nx::CCollStatic > *sector, *next;

//	for (int i=0;i<100;i++)
//	{
//		printf ("%f,%f -> %f,%f\n",
//		coll[i].GetGeometry()->GetBBox().GetMin().GetX(),
//		coll[i].GetGeometry()->GetBBox().GetMin().GetZ(),
//		coll[i].GetGeometry()->GetBBox().GetMax().GetX(),
//		coll[i].GetGeometry()->GetBBox().GetMax().GetZ());
//	}


					
	for( int i = 0; i < NUM_PARTITIONS_X; i++ )
	{
		for( int j = 0; j < NUM_PARTITIONS_Z; j++ )
		{
			// Allocate temp list on high heap
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

			for ( int idx = 0; idx < num_coll_sectors; idx++ )
			{
				// Check to see if this collision sector belongs in the SuperSector
				Dbg_Assert(coll[idx].GetGeometry());
				if (coll[idx].GetGeometry()->GetNumFaces()>0 && coll[idx].GetGeometry()->GetBBox().Intersect(m_super_sector_list[i][j].m_Bbox))
				{
					Lst::Node< Nx::CCollStatic > *node = new Lst::Node< Nx::CCollStatic > ( &(coll[idx]) );
					sector_list.AddToTail( node );
				}
			}

			Mem::Manager::sHandle().PopContext();

			if( sector_list.CountItems() > 0 )
			{
				m_super_sector_list[i][j].m_CollSectorList = (Nx::CCollStatic **) Mem::Malloc(sizeof(Nx::CCollStatic *) * sector_list.CountItems());
				m_super_sector_list[i][j].m_NumCollSectors = sector_list.CountItems();
				int k = 0;
				for( sector = sector_list.GetNext(); sector; sector = next )
				{
					next = sector->GetNext();

					m_super_sector_list[i][j].m_CollSectorList[k] = sector->GetData();
					delete sector;
					k++;
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::UpdateCollisionSuperSectors(Lst::Head &add_list,
											 Lst::Head &remove_list,
											 Lst::Head &update_list)
{
	Lst::Head< Nx::CCollStatic > sector_add_list;
	Lst::Node< Nx::CCollStatic > *sector, *next;
	Nx::CCollStatic *p_coll_sector;

	uint num_add_items = add_list.CountItems();
	uint num_remove_items = remove_list.CountItems();
	uint num_update_items = update_list.CountItems();

	//Dbg_Message("In UpdateCollisionSuperSectors with add size %d, remove size %d", num_add_items, num_remove_items);

	uint idx;
	int num_change;
	for( int i = 0; i < NUM_PARTITIONS_X; i++ )
	{
		for( int j = 0; j < NUM_PARTITIONS_Z; j++ )
		{
			SSec::Sector *p_super_sector = &(m_super_sector_list[i][j]);
			num_change = 0;

			// Allocate temp list on high heap
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

			// Check removed sectors first
			sector = remove_list.GetNext();
			for (idx = 0; idx < num_remove_items; idx++)
			{
				p_coll_sector = sector->GetData();

				// Check to see if this collision sector should be in the SuperSector
				if (p_coll_sector->GetGeometry()->GetBBox().Intersect(p_super_sector->m_Bbox))
				{
					//Dbg_Message("Removing collision %x to SuperSector (%d, %d)", p_coll_sector->GetChecksum(), i, j);
					num_change = remove_from_super_sector(p_coll_sector, p_super_sector, sector_add_list, num_change);
#if 0
				} else {
					if (p_super_sector->remove_collision(p_coll_sector))
					{
						num_change--;
						Dbg_MsgAssert(0, ("UpdateCollisionSuperSectors: Removed collision %x that shouldn't be in SuperSector (%d, %d)", 
										  p_coll_sector->GetChecksum(), i, j));
					}
#endif
				}

				sector = sector->GetNext();
			}

			// Now check added sectors
			sector = add_list.GetNext();
			for (idx = 0; idx < num_add_items; idx++)
			{
				p_coll_sector = sector->GetData();

				// Check to see if this collision sector belongs in the SuperSector
				if (p_coll_sector->GetGeometry()->GetBBox().Intersect(p_super_sector->m_Bbox))
				{
					//Dbg_Message("Adding collision %x to SuperSector (%d, %d)", p_coll_sector->GetChecksum(), i, j);
					num_change = add_to_super_sector(p_coll_sector, p_super_sector, sector_add_list, num_change);
				}

				sector = sector->GetNext();
			}
			// And check updated sectors
			sector = update_list.GetNext();
			for (idx = 0; idx < num_update_items; idx++)
			{
				p_coll_sector = sector->GetData();
				bool on_list = p_super_sector->has_collision(p_coll_sector);
				if (p_coll_sector->GetGeometry()->GetBBox().Intersect(p_super_sector->m_Bbox) != on_list)
				{
					if (on_list)
					{
						//Dbg_Message("Removing updated collision %x to SuperSector (%d, %d)", p_coll_sector->GetChecksum(), i, j);
						// Remove sector
						num_change = remove_from_super_sector(p_coll_sector, p_super_sector, sector_add_list, num_change);
					}
					else
					{
						//Dbg_Message("Adding updated collision %x to SuperSector (%d, %d)", p_coll_sector->GetChecksum(), i, j);
						// Add sector
						num_change = add_to_super_sector(p_coll_sector, p_super_sector, sector_add_list, num_change);
					}
				}

				sector = sector->GetNext();
			}

			Mem::Manager::sHandle().PopContext();

			int new_size = p_super_sector->m_NumCollSectors + num_change;

			if (num_change < 0)				// Re-alloc array down
			{
				if (!p_super_sector->m_CollSectorList ||
					!Mem::Manager::sHandle().ReallocateShrink(sizeof(Nx::CCollStatic *) * new_size, p_super_sector->m_CollSectorList))
				{
					// If we can't realloc down, then we have to use the old realloc
					p_super_sector->m_CollSectorList = (Nx::CCollStatic **) Mem::Realloc(p_super_sector->m_CollSectorList,
																						 sizeof(Nx::CCollStatic *) * new_size);
				}
			}
			else if (num_change > 0)		// Re-alloc array up and copy in sector_add_list
			{
				if (!p_super_sector->m_CollSectorList ||
				    !Mem::Manager::sHandle().ReallocateUp(sizeof(Nx::CCollStatic *) * new_size, p_super_sector->m_CollSectorList))
				{
					// If we can't realloc up, then we have to use the old realloc
					p_super_sector->m_CollSectorList = (Nx::CCollStatic **) Mem::Realloc(p_super_sector->m_CollSectorList,
																						 sizeof(Nx::CCollStatic *) * new_size);
				}

				// Copy add list in
				idx = p_super_sector->m_NumCollSectors;
				for(sector = sector_add_list.GetNext(); sector; sector = next)
				{
					next = sector->GetNext();

					p_super_sector->m_CollSectorList[idx] = sector->GetData();
					delete sector;
					idx++;
				}
				//Dbg_Message("Adding %d sectors to [%d, %d]. New size %d", num_change, i, j, new_size);
			}

			Dbg_Assert(sector_add_list.IsEmpty());

			p_super_sector->m_NumCollSectors = new_size;
		}
	}
	
	
	
	for( int i = 0; i < NUM_PARTITIONS_X; i++ )
	{
		for( int j = 0; j < NUM_PARTITIONS_Z; j++ )
		{
			SSec::Sector *p_super_sector = &(m_super_sector_list[i][j]);
			Nx::CCollStatic** pp_old = p_super_sector->m_CollSectorList;
			if (pp_old)
			{
				uint32 count = p_super_sector->m_NumCollSectors;
				if (count)
				{
					p_super_sector->m_CollSectorList = (Nx::CCollStatic**)Mem::Malloc(4*(count));
					memcpy(p_super_sector->m_CollSectorList, pp_old, 4*(count));
					Mem::Free(pp_old);
				}
				else
				{
					p_super_sector->m_CollSectorList = NULL;
					Mem::Free(pp_old);
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::add_to_super_sector(Nx::CCollStatic *p_coll, SSec::Sector *p_super_sector, Lst::Head< Nx::CCollStatic > & add_list, int num_changes)
{
	if (num_changes < 0)
	{
		// Just append to end of existing array
		int avail_idx = p_super_sector->m_NumCollSectors + num_changes;
		p_super_sector->m_CollSectorList[avail_idx] = p_coll;
	} else {
		// Append to add list
		Lst::Node< Nx::CCollStatic > *node = new Lst::Node< Nx::CCollStatic > (p_coll);
		add_list.AddToTail(node);
	}

	return ++num_changes;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::remove_from_super_sector(Nx::CCollStatic *p_coll, SSec::Sector *p_super_sector, Lst::Head< Nx::CCollStatic > & add_list, int num_changes)
{
	if (num_changes > 0)
	{
		// Move a sector off the add list, if there is one
		Lst::Node< Nx::CCollStatic > *p_sector_node;

		p_sector_node = add_list.GetNext();
		if (!p_super_sector->replace_collision(p_coll, p_sector_node->GetData()))
		{
			Dbg_MsgAssert(0, ("UpdateCollisionSuperSectors: Can't replace collision %x that should be in SuperSector", 
							  p_coll->GetChecksum()));
		}
		delete p_sector_node;
	}
	else 
	{
		// Just remove the sector
		if (!p_super_sector->remove_collision(p_coll))
		{
			Dbg_MsgAssert(0, ("UpdateCollisionSuperSectors: Can't remove collision %x that should be in SuperSector", 
							  p_coll->GetChecksum()));
		}
	}

	return --num_changes;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ClearCollisionSuperSectors()
{
	// Just delete all the arrays, since whoever called this knows they are all going away
	for( int i = 0; i < NUM_PARTITIONS_X; i++ )
	{
		for( int j = 0; j < NUM_PARTITIONS_Z; j++ )
		{
			SSec::Sector *p_super_sector = &(m_super_sector_list[i][j]);

			if( p_super_sector->m_CollSectorList )
			{   
				Mem::Free(p_super_sector->m_CollSectorList);
				p_super_sector->m_CollSectorList = NULL;
			}

			p_super_sector->m_NumCollSectors = 0;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::CCollStatic** Manager::GetIntersectingCollSectors( Mth::CBBox& bbox )
{
	int op_id = GetNewSectorOperationId();
	
	float x_min = bbox.GetMin()[X] - m_world_bbox.GetMin()[X];
	float x_max = bbox.GetMax()[X] - m_world_bbox.GetMin()[X];
	float z_min = bbox.GetMin()[Z] - m_world_bbox.GetMin()[Z];
	float z_max = bbox.GetMax()[Z] - m_world_bbox.GetMin()[Z];
	
	// extent the min and max in each direction to catch boundary conditions
	x_min -= COLL_LINE_EXTENSION;
	x_max += COLL_LINE_EXTENSION;
	z_min -= COLL_LINE_EXTENSION;
	z_max += COLL_LINE_EXTENSION;
	
	// determine the corresponding rectangle in terms of super sectors indicies
	int start_x_box = static_cast< int >(x_min / m_sector_width);
	int start_z_box = static_cast< int >(z_min / m_sector_depth);
	int end_x_box = static_cast< int >(x_max / m_sector_width);
	int end_z_box = static_cast< int >(z_max / m_sector_depth);
	
	// cap the super sector indicies
	if (start_x_box < 0)
	{
		start_x_box = 0;
	}
	else if (start_x_box >= m_num_sectors_x)
	{
		start_x_box = m_num_sectors_x - 1;
	}
	if (start_z_box < 0)
	{
		start_z_box = 0;
	}
	else if (start_z_box >= m_num_sectors_z)
	{
		start_z_box = m_num_sectors_z - 1;
	}
	if (end_x_box < 0)
	{
		end_x_box = 0;
	}
	else if (end_x_box >= m_num_sectors_x)
	{
		end_x_box = m_num_sectors_x - 1;
	}
	if (end_z_box < 0)
	{
		end_z_box = 0;
	}
	else if (end_z_box >= m_num_sectors_z)
	{
		end_z_box = m_num_sectors_z - 1;
	}
	
	// loop over the corresponding super sectors and add the sectors to the qualifying list
	int qual_sector_idx = 0;
	for (int i = start_x_box; i <= end_x_box; i++)
	{
		for (int j = start_z_box; j <= end_z_box; j++)
		{
			for (int k = 0; k < m_super_sector_list[i][j].m_NumCollSectors; k++)
			{
				Nx::CCollStatic* cs = m_super_sector_list[i][j].m_CollSectorList[k];
				
				if (cs->GetObjectFlags() & (mSD_NON_COLLIDABLE | mSD_KILLED)) continue;
				
				if (cs->GetSuperSectorID() == op_id) continue;
				
				QualCollSectors[qual_sector_idx++] = cs;
				
				cs->SetSuperSectorID(op_id);
				
				Dbg_MsgAssert(qual_sector_idx < (vMAX_QUAL_SECTORS*8/10),("Too many %d qualifying collision sectors",qual_sector_idx));						
			}
		}
	}
	
	QualCollSectors[qual_sector_idx] = NULL;
	
	return static_cast< Nx::CCollStatic** >(QualCollSectors);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::CCollStatic** Manager::GetIntersectingCollSectors( Mth::Line &line )
{
	
	
	int qual_sector_idx;
	Mth::Line test_line;
	Mth::Vector dir;
	float x_offset, z_offset;
	int start_x_box, start_z_box;
	int end_x_box, end_z_box;
	int temp,k;
	int op_id;
		
	qual_sector_idx = 0;


	// Extend the line a foot in each direction just to catch boundary conditions
	dir = Mth::Vector( line.m_end[X] - line.m_start[X],
					   line.m_end[Y] - line.m_start[Y],
					   line.m_end[Z] - line.m_start[Z] );
	dir.Normalize();
	dir.Scale( COLL_LINE_EXTENSION );
	test_line.m_start[X]	= line.m_start[X] - dir.GetX();
	test_line.m_start[Y]	= line.m_start[Y] - dir.GetY();
	test_line.m_start[Z]	= line.m_start[Z] - dir.GetZ();
	test_line.m_end[X]		= line.m_end[X] + dir.GetX();
	test_line.m_end[Y]		= line.m_end[Y] + dir.GetY();
	test_line.m_end[Z]		= line.m_end[Z] + dir.GetZ();

	// Figure out which super sector the start point is in
	x_offset = test_line.m_start[X] - m_world_bbox.GetMin()[X];
	z_offset = test_line.m_start[Z] - m_world_bbox.GetMin()[Z];

	start_x_box = (int) ( x_offset / m_sector_width );
	start_z_box = (int) ( z_offset / m_sector_depth );

//#define DISABLE_SUPER_SECTORS
#ifdef DISABLE_SUPER_SECTORS
	start_x_box = start_z_box = 0;
#endif // DISABLE_SUPER_SECTORS

	// Some sanity checks
	if( start_x_box < 0 )
	{
		start_x_box = 0;
	}
	else if( start_x_box >= m_num_sectors_x )
	{
		start_x_box = m_num_sectors_x - 1;
	}

	if( start_z_box < 0 )
	{
		start_z_box = 0;
	}
	else if( start_z_box >= m_num_sectors_z )
	{
		start_z_box = m_num_sectors_z - 1;
	}

	// Figure out which super sector the end point is in
	x_offset = test_line.m_end[X] - m_world_bbox.GetMin()[X];
	z_offset = test_line.m_end[Z] - m_world_bbox.GetMin()[Z];
		
	end_x_box = (int) ( x_offset / m_sector_width );
	end_z_box = (int) ( z_offset / m_sector_depth );

#ifdef DISABLE_SUPER_SECTORS
	end_x_box = m_num_sectors_x - 1;
	end_z_box = m_num_sectors_z - 1;
#endif // DISABLE_SUPER_SECTORS

	// Some sanity checks
	if( end_x_box < 0 )
	{
		end_x_box = 0;
	}
	else if( end_x_box >= m_num_sectors_x )
	{
		end_x_box = m_num_sectors_x - 1;
	}

	if( end_z_box < 0 )
	{
		end_z_box = 0;
	}
	else if( end_z_box >= m_num_sectors_z )
	{
		end_z_box = m_num_sectors_z - 1;
	}

	// Organize vars for two-dimensional "for" loop
	if( end_x_box < start_x_box )
	{
		temp = end_x_box;
		end_x_box = start_x_box;
		start_x_box = temp;
	}

	if( end_z_box < start_z_box )
	{
		temp = end_z_box;
		end_z_box = start_z_box;
		start_z_box = temp;
	}

	//Dbg_Message("SuperSector collision start(%d, %d) end(%d, %d)", start_x_box, start_z_box, end_x_box, end_z_box);
	//Dbg_Message("SuperSector testline start(%f, %f) end(%f, %f)", test_line.m_start[X], test_line.m_start[Z], test_line.m_end[X], test_line.m_end[Z]);
	//Dbg_Message("SuperSector world start(%f, %f) sector size(%f, %f)", m_world_bbox.inf.x, m_world_bbox.inf.z, m_sector_width, m_sector_depth);


// Optimize for the case (95%) where start and end are the same
// Just copy over the qualifying sectors pointers from one supersector
// This makes the routine about 30% fater on average.  
	if (start_x_box == end_x_box && start_z_box == end_z_box)
	{
		Sector * p_sec = &m_super_sector_list[start_x_box][start_z_box]; 
		int num = p_sec->m_NumCollSectors;
		Nx::CCollStatic** pp_cs = p_sec->m_CollSectorList; 
		Nx::CCollStatic**  pp_qual = QualCollSectors;		
//		printf ("%8d: %3d (%d,%d)\n",(int)Tmr::GetRenderFrame(),num,start_x_box, start_z_box);
		for( k = 0; k < num; k++ )
		{
			Nx::CCollStatic* cs= *pp_cs++;
			if ( !( cs->GetObjectFlags() & ( mSD_NON_COLLIDABLE | mSD_KILLED ) ) )
			{
				*pp_qual++ = cs;
			}

		}
		*pp_qual = NULL;
		return (Nx::CCollStatic**)QualCollSectors;
	}
	

//	int checked = 0;
//	int passed = 0;


	op_id = GetNewSectorOperationId();
		
	// Add world sectors from these super sectors to the qualifying list
	for( int i = start_x_box; i <= end_x_box; i++ )
	{
		for( int j = start_z_box; j <= end_z_box; j++ )
		{
			for( k = 0; k < m_super_sector_list[i][j].m_NumCollSectors; k++ )
			{
//				checked++;
				Nx::CCollStatic* cs;
				
				cs = m_super_sector_list[i][j].m_CollSectorList[k];
				
				if ( !( cs->GetObjectFlags() & ( mSD_NON_COLLIDABLE | mSD_KILLED ) ) )
				{
					// If we haven't already added it, add it
					if( cs->GetSuperSectorID() != op_id )
					{
						if (qual_sector_idx < vMAX_QUAL_SECTORS-1)
						{
//							passed++;
							QualCollSectors[qual_sector_idx++] = cs;
							// Mark it as having been added in this operation
							cs->SetSuperSectorID(op_id);
							
						}
						// normally we just return
						// but in case someone does something that involves the whole
						// world, we add this assertion...
						Dbg_MsgAssert(qual_sector_idx < (vMAX_QUAL_SECTORS*8/10),("Too many %d qualifying collision sectors (%d,%d)-(%d,%d).\n  Is a non-playable portion of the level flagged as collidable? Maybe the clouds, or the sea?",
						qual_sector_idx, start_x_box,start_z_box,end_x_box,end_z_box ));						
					}
				}
			}
		}
	}        
	
	//printf ("(%d,%d) to (%d, %d), checked %d, returning %d\n",start_x_box,start_z_box,end_x_box,end_z_box,checked, passed);                              

	QualCollSectors[qual_sector_idx] = NULL;
	return (Nx::CCollStatic**)QualCollSectors;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::CSector**	Manager::GetIntersectingWorldSectors( Mth::Line &line )
{


	Dbg_MsgAssert(0,("GetIntersectingWorldSectors is an old function"));	
	return NULL;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace SSec







================================================
FILE: Code/Sk/Engine/SuperSector.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			SSEC													**
**																			**
**	File name:		SuperSector.h											**
**																			**
**	Created: 		01/12/2001	-	spg										**
**																			**
*****************************************************************************/

#ifndef	__ENGINE_SUPERSECTOR_H
#define	__ENGINE_SUPERSECTOR_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/////////////////////////////////////////////////////////////////////////
// Forward declarations
namespace Nx
{
class	CSector;
class	CCollObj;
class	CCollStatic;
}


namespace SSec
{

// Forward declaration
class Manager;

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class Sector
{
public:
	Sector( void );
	~Sector( void );

private:
	// Remove pointer out of sector array and shift the rest.  Doesn't update m_NumCollSectors!
	bool				remove_collision(Nx::CCollStatic *p_coll);
	bool				replace_collision(Nx::CCollStatic *p_coll, Nx::CCollStatic *p_replace_coll);
	bool				has_collision(Nx::CCollStatic *p_coll);

	Mth::CBBox			m_Bbox;
	int					m_NumSectors;
	int					m_NumCollSectors;
	Nx::CSector**		m_SectorList;
	Nx::CCollStatic**	m_CollSectorList;

	friend Manager;
};


class Manager
{
public:
	enum
	{
		NUM_PARTITIONS_X = 20,
		NUM_PARTITIONS_Z = 20,
	};

	Nx::CSector**		GetIntersectingWorldSectors( Mth::Line &line );
	Nx::CCollStatic**	GetIntersectingCollSectors( Mth::Line &line );
	Nx::CCollStatic**	GetIntersectingCollSectors( Mth::CBBox &bbox );
	void				GenerateSuperSectors( const Mth::CBBox& world_bbox );
	void				AddCollisionToSuperSectors( Nx::CCollStatic *coll, int num_coll_sectors );
	void				UpdateCollisionSuperSectors(Lst::Head &add_list,
													Lst::Head &remove_list,
													Lst::Head &update_list);
	void				ClearCollisionSuperSectors();

	Mth::CBBox *		GetWorldBBox( void ) { return &m_world_bbox; }

private:
	int					add_to_super_sector(Nx::CCollStatic *p_coll, SSec::Sector *p_super_sector, Lst::Head< Nx::CCollStatic > & add_list, int num_changes);
	int					remove_from_super_sector(Nx::CCollStatic *p_coll, SSec::Sector *p_super_sector, Lst::Head< Nx::CCollStatic > & add_list, int num_changes);

	Mth::CBBox			m_world_bbox;
	float				m_sector_width;
	float 				m_sector_depth;
	int 				m_num_sectors_x;
	int 				m_num_sectors_z;
	Sector				m_super_sector_list[NUM_PARTITIONS_X][NUM_PARTITIONS_Z];
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

uint8		GetCurrentSectorOperationId( void );
uint8		GetNewSectorOperationId( void );


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace SSec

#endif	// __ENGINE_SUPERSECTOR_H

================================================
FILE: Code/Sk/Engine/contact.cpp
================================================
////////////////////////////////////////////////////////////////////////////
//
// Contact.cpp	  - Mick
//
// a contact is a point "on" a moving object
// generally on the  surface of the object
// and more generally at a position realative to it
// it is responsible for maintaining changes in
// position, due to changes on the object it is on


#include 
#include 
#include 
#include 

						
					
CContact::CContact()
{
}

CContact::CContact(Obj::CCompositeObject *p_moving_object )
{

	mp_moving_object = p_moving_object;
//	m_object_id = mp_moving_object->GetID();
	
	m_object_last_matrix = mp_moving_object->GetMatrix();
	m_object_last_pos = mp_moving_object->GetPos();
	
	m_movement.Set();
	m_rotated = false;
}


// check to see if the object exists.									
bool		CContact::ObjectExists()
{
	// With smart pointers, GetObject will just return NULL if the object has been deleted 
	return (GetObject() != NULL);
}

// call this once per frame, to calculate the movement of the contact point
bool		CContact::Update(const Mth::Vector &pos)
{											
	if (!mp_moving_object.Convert())
	{
		return false;
	}
	
	m_movement = mp_moving_object->GetPos() - m_object_last_pos;
	
	// so far we have only accounted for the movement of the object's origin
	// (mp_moving_object->GetPos()), and we need to see if the object has rotated
	// which will give us an additional movement vector

	if (m_object_last_matrix != mp_moving_object->GetMatrix())
	{
		m_rotated = true;
//		printf ("Object has rotated\n");
		// Get the position relative to the origin on the object
		Mth::Vector old_offset = pos - m_object_last_pos;
		Mth::Vector offset = old_offset;

//		printf ("Offset (%.2f,%.2f,%.2f)\n",offset[X],offset[Y],offset[Z]);
		
		// transform by the inverse of the original matrix
		Mth::Matrix back = m_object_last_matrix;		  
		// just to be safe
		back[W] = Mth::Vector(0,0,0,1);
		back[X][W] = 0.0f;
		back[Y][W] = 0.0f;
		back[Z][W] = 0.0f;
		back.InvertUniform();

		// transform to the new matrix
		m_rotation =  mp_moving_object->GetMatrix();
		// just to be safe
		m_rotation[W] = Mth::Vector(0,0,0,1);
		m_rotation[X][W] = 0.0f;
		m_rotation[Y][W] = 0.0f;
		m_rotation[Z][W] = 0.0f;
		
		m_rotation *= back;		
		
		Mth::Vector new_offset = m_rotation.Transform(offset);

//		printf ("new Offset (%.2f,%.2f,%.2f)\n",new_offset[X],new_offset[Y],new_offset[Z]);
		
		// Calculate the movment due to rotation
		Mth::Vector	rotation_movement = (new_offset - old_offset);
	
		// add it to the movement, so it is included in the skater's movement
		m_movement += rotation_movement;

		m_object_last_matrix = mp_moving_object->GetMatrix();
	
	}
	else
	{
		m_rotated = false;
	}
	
	m_object_last_pos = mp_moving_object->GetPos();

	return true;
}

												   




================================================
FILE: Code/Sk/Engine/contact.h
================================================

#ifndef	__ENGINE_CONTACT_H__
#define	__ENGINE_CONTACT_H__

#include 
#include 
#include 

class	CContact
{
public:
								CContact();
								CContact(Obj::CCompositeObject *p_moving_object);

	bool						ObjectExists();		// returns false if object has been deleted, or is "dead" or "inactive"
	bool						Update(const Mth::Vector &pos);		// Update the position, return false if object no longer exists
	const Mth::Vector		&	GetMovement() const;		// get movement vector of the contact point for this frame 	
	const Mth::Matrix		&	GetRotation() const;		// get rotation matrix for this frame 	
	bool						IsRotated() const;
						   
private:
	Obj::CSmtPtr		mp_moving_object;
	Mth::Matrix					m_object_last_matrix; 	// The object's last orientation
	Mth::Vector					m_object_last_pos;		// The object's last position

	Mth::Vector					m_movement;
	Mth::Matrix					m_rotation;
	
	bool 						m_rotated;				// set if it was rotated

public:
	Obj::CCompositeObject*		GetObject() {return mp_moving_object.Convert();}		// return the object we are in contact with


};

inline const Mth::Vector		&	CContact::GetMovement() const
{
	return	m_movement;	
}

inline bool							CContact::IsRotated() const
{
	return	m_rotated;
}

inline const Mth::Matrix		&	CContact::GetRotation() const
{
	return	m_rotation;	

}

#endif



================================================
FILE: Code/Sk/Engine/feeler.cpp
================================================
////////////////////////////////////////////////////////////////////////////
//
// Feeler.cpp	  - Mick
//
// A feeler is a 3D line segment that can check for collision with
// the world, and return information about the faces it collides with
//
// it's derived from the line type
// so operations you can do with a line
// can also be done with the feeler		  
// (See geometry.cpp for line operations)


#include 

#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 

#include 

#define PRINT_TIMES 0
						
Nx::CCollCache *		CFeeler::sp_default_cache = NULL;

					
CFeeler::CFeeler()
{
	init();
}

CFeeler::CFeeler(const Mth::Vector &start, const Mth::Vector &end)
{
	m_start = start;
	m_end = end;
	m_col_data.surface.normal.Set(1.0, 1.0, 1.0);
	init();
}

void CFeeler::init (   )
{
	// When we create a feeler
	// we use the default "ignore" settings
	// as they will be used the most
	m_col_data.ignore_1 = mFD_NON_COLLIDABLE; 		// ignore NON_COLLIDABLES
	m_col_data.ignore_0 = 0;						// don't ignore anything else
	mp_callback = NULL;
	mp_cache = NULL;

	// Now un-initialized...
	m_col_data.surface.normal.Set();
	m_col_data.surface.point.Set();
}

void CFeeler::SetIgnore(uint16 ignore_1, uint16 ignore_0)
{
	m_col_data.ignore_1 = ignore_1;
	m_col_data.ignore_0 = ignore_0;
}


void CFeeler::SetStart(const Mth::Vector &start)
{
	m_start = start;
}

void CFeeler::SetEnd(const Mth::Vector &end)
{
	m_end = end;
}

void CFeeler::SetLine(const Mth::Vector &start, const Mth::Vector &end)
{
	SetStart(start);
	SetEnd(end);
}

							  

// This callback gets called by the CLd:: collision code
// (which was in turn a callback called from elsewhere)
// and then this calls my Callback
// we should try to simplyify this chain of callbacks
// whilst still keeping platform and engine independence
static void s_feeler_collide_callback(Nx::CollData* p_col_data)
{
	CFeeler *p_feeler = (CFeeler*) p_col_data->p_callback_data;
	p_feeler->mp_callback(p_feeler); 
}

// This is the guts, the interface to the lower level collision code
bool	CFeeler::GetCollision(bool movables, bool bfar)
{
#if PRINT_TIMES
	static uint64 s_total_time = 0, s_num_collisions = 0;
	uint64 start_time = Tmr::GetTimeInUSeconds();
#endif
	
 #ifdef	__PLAT_NGPS__		
	//	snProfSetRange( -1, (void*)0, (void*)-1);
	//	snProfSetFlagValue(0x01);
 #endif
   
	
	Mth::Line is;

	// just copy over the start and the end of the line directly.
	is.m_start = m_start;
	is.m_end = m_end;

	m_col_data.p_callback_data = this;	// callback data is this current feeler

	// initialize collision info
	m_col_data.coll_found = false;
	m_col_data.trigger = false;
	m_col_data.script = 0;				// This is a checksum

	m_col_data.mp_callback_object = NULL;
						  
	void *callback = 0;					
	if (mp_callback)
	{
	   	callback = s_feeler_collide_callback;
	}

	Nx::CCollCache *p_cache = (mp_cache) ? mp_cache : sp_default_cache;

	bool	collision;
	mp_movable_collision_obj = NULL;
	m_movable_collision_id = 0;
	if (bfar)
	{
		collision = Nx::CCollObj::sFindFarStaticCollision( is, &m_col_data, callback, p_cache );
	}
	else
	{
		collision = Nx::CCollObj::sFindNearestStaticCollision( is, &m_col_data, callback, p_cache );
	}

	// Now see if we can find a closer movable
	if (movables)
	{
		if (bfar)
		{
			mp_movable_collision_obj = Nx::CCollObj::sFindFarMovableCollision( is, &m_col_data, callback, p_cache );
		}
		else
		{
			mp_movable_collision_obj = Nx::CCollObj::sFindNearestMovableCollision( is, &m_col_data, callback, p_cache );
		}
		if (mp_movable_collision_obj)
		{
			m_movable_collision_id = mp_movable_collision_obj->GetID();
		}
	}

	m_dist = m_col_data.dist;
	m_point = m_col_data.surface.point;
	m_normal = m_col_data.surface.normal;

#if 0
	if (collision)
	{
		Gfx::AddDebugLine(m_point,m_point + (m_normal * 100.0f),coll_color,10);
	}

	//static int num_print = 1;
	if (/*collision && (num_print++ % 100) ==*/ 0)
	{
		Dbg_Message("\nStart point (%f, %f, %f)", m_start[X], m_start[Y], m_start[Z]);
		Dbg_Message("End point (%f, %f, %f)", m_end[X], m_end[Y], m_end[Z]);

		Dbg_Message("Collision Distance %f", m_dist);
		Dbg_Message("Collision point (%f, %f, %f)", m_point[X], m_point[Y], m_point[Z]);
		Dbg_Message("Collision normal (%f, %f, %f)", m_normal[X], m_normal[Y], m_normal[Z]);
	}
#endif


 #ifdef	__PLAT_NGPS__		
	//	snProfSetRange( 4, (void*)NULL, (void*)-1);
 #endif		

#if PRINT_TIMES
	uint64 end_time = Tmr::GetTimeInUSeconds();
	s_total_time += end_time - start_time;

	if (++s_num_collisions >= 1000)
	{
		Dbg_Message("Feeler time %d us", s_total_time);
		s_total_time = s_num_collisions = 0;
	}
#endif

	
	return collision || (mp_movable_collision_obj != NULL);



}

//
// Just checks the movable collisions
//
bool CFeeler::GetMovableCollision(bool bfar)
{
	Mth::Line is;

	// just copy over the start and the end of the line directly.
	is.m_start = m_start;
	is.m_end = m_end;

////////////////////////////////////////////////////////////
// Clear data than might have been set on a previous call
// (change suggested by Andre at LF, 4/28/03)
	m_col_data.coll_found = false;
	m_col_data.trigger = false;
	m_col_data.script = 0;				// This is a checksum
	m_col_data.mp_callback_object = NULL;
// End of change
////////////////////////////////////////////////////////////


	m_col_data.p_callback_data = this;	// callback data is this current feeler
						  
	void *callback = 0;					
	if (mp_callback)
	{
	   	callback = s_feeler_collide_callback;
	}

	Nx::CCollCache *p_cache = (mp_cache) ? mp_cache : sp_default_cache;

	if (bfar)
	{
		mp_movable_collision_obj = Nx::CCollObj::sFindFarMovableCollision( is, &m_col_data, callback, p_cache );
	}
	else
	{
		mp_movable_collision_obj = Nx::CCollObj::sFindNearestMovableCollision( is, &m_col_data, callback, p_cache );
	}
	
	if (mp_movable_collision_obj)
	{
		m_movable_collision_id = mp_movable_collision_obj->GetID();
	}

	m_dist = m_col_data.dist;
	m_point = m_col_data.surface.point;
	m_normal = m_col_data.surface.normal;

	return mp_movable_collision_obj != NULL;
}

// Check to see that if we have collision, that we have enough data to
// calculate the brightness
bool	CFeeler::IsBrightnessAvailable()
{
	if (m_col_data.coll_found)
	{
		Dbg_MsgAssert(m_col_data.p_coll_object, ("IsBrightnessAvailable(): collision w/o a collsion object"));
		return m_col_data.p_coll_object->IsTriangleCollision();
	}

	return true;
}

// get the brightness 0.0 .. 1.0 of the point of the last
// collision, based on the RGB values of the triangle, interpolatd to
// the collision point
float	CFeeler::GetBrightness()
{
	float brightness;
	
	struct SGPoint
	{
		Mth::Vector	p;
		float		v;			// Brightness in range [0.0, 1.0].
	};

	uint32	i;
	SGPoint	point[3];

	
	// If no collision found, set default brightness.
	if( !m_col_data.coll_found )
	{
		brightness = 0.5f;
	}
	else
	{
		Nx::CCollObj*				p_object	= m_col_data.p_coll_object;
		const Nx::SCollSurface*		p_face		= &m_col_data.surface;
		uint32						face_index	= p_face->index;
		//Cld::CCollSector::SFace*	p_polygon	= p_object->mp_faces + face_index;

		// Extract contact point.
		float x = p_face->point[X];
		float y = p_face->point[Y];
		float z = p_face->point[Z];

		Dbg_MsgAssert(p_object->IsTriangleCollision(),("Not triangle collision !!!"));

		Nx::CCollObjTriData *p_tri_data=p_object->GetGeometry();
		Dbg_MsgAssert(p_tri_data,("NULL p_tri_data"));

		// Get all the vertices into a nice simple array of (x,y,z,v).
		for( i = 0; i < 3; ++i )
		{
			//uint16 *vert_idxs = p_object->GetFaceVertIndicies(face_index);

			const Mth::Vector & vertex = p_object->GetVertexPos(p_tri_data->GetFaceVertIndex(face_index,i));

			// Check that the vertices match.
			point[i].p.Set( vertex.GetX() - x, vertex.GetY() - y, vertex.GetZ() - z );

			// Here is where the conversion is made from values [0,255] to [0,1].
#ifdef __PLAT_NGC__
			point[i].v	= (float)( p_tri_data->GetVertexIntensity(face_index, i)) * ( 1.0f / 255.0f );
#else
			point[i].v	= (float)( p_tri_data->GetVertexIntensity(p_tri_data->GetFaceVertIndex(face_index, i)) ) * ( 1.0f / 255.0f );
#endif		// __PLAT_NGC__
		}

		// Calculate the intersection of the line from Point 0 through the origin, with the line 1-2.
		float		mua, mub;
		Mth::Line	line1( point[0].p, Mth::Vector( 0.0f, 0.0f, 0.0f ));
		Mth::Line	line2( point[1].p, point[2].p );
		Mth::Vector	a, b;

		Mth::LineLineIntersect( line1, line2, &a, &b, &mua, &mub, false );

		float l0, l1;
		Mth::Vector length;

		// First interpolation a = i( 1, 2 ).
		length				= a - point[1].p;
		l0					= length.Length();
		length				= point[2].p - point[1].p;
		l1					= length.Length();
		float va			= point[1].v + (( point[2].v - point[1].v ) * ( l0 / l1 ));

		// Second interpolation b = i( 0, a ).
		l0					= point[0].p.Length();
		length				= a - point[0].p;
		l1					= length.Length();
		brightness	= point[0].v + (( va - point[0].v ) * ( l0 / l1 ));
	}
	
#	ifdef __PLAT_XBOX__
	// Xbox vertex colors run on a different scale to Ps2.
	brightness *= 0.5f;
#	endif
	
	return brightness;
}


bool CFeeler::GetCollision(const Mth::Vector &start, const Mth::Vector &end, bool movables)
{
	SetLine(start,end);
	return GetCollision(movables);
}

bool CFeeler::GetMovableCollision(const Mth::Vector &start, const Mth::Vector &end)
{
	SetLine(start,end);
	return GetMovableCollision();
}

bool	CFeeler::IsMovableCollision()
{
	if (mp_movable_collision_obj.Convert())
	{
		return true;
	}

// if it was movable, and now we have no movable object
// then kill the id and the node name
// so we don't get stung by the snap to ground assuming they are still valid....
	if (m_movable_collision_id)
	{
		m_movable_collision_id = 0;
		m_col_data.node_name = 0;		// we are SO like not colliding with anything!
	}
	return false;
}

Obj::CCompositeObject *		CFeeler::GetMovingObject()	// { return mp_movable_collision_obj;}
{
	return mp_movable_collision_obj.Convert();
}

// if we are in the middle of a callback, then
// we need to look at the callback object													   
Obj::CCompositeObject *		CFeeler::GetCallbackObject() const
{
	return m_col_data.mp_callback_object;
}


void CFeeler::DebugLine(int r, int g, int b, int num_frames)
{	
	Gfx::AddDebugLine( m_start, m_end, MAKE_RGB( r, g, b ),MAKE_RGB( r,g, b ), num_frames );
}




================================================
FILE: Code/Sk/Engine/feeler.h
================================================

#ifndef	__ENGINE_FEELER_H__
#define	__ENGINE_FEELER_H__

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 

#include 
#include 
#include 
#include 
//#include 		// needed for smart pointer destructor in feeler, dammit
#include 
#include 

namespace Nx
{
	class CCollCache;
}

class CFeeler;

typedef CFeeler CLineFeeler;

typedef	void (*FEELER_CB)(CFeeler*);		// feeler callback

class	CFeeler : public Mth::Line
{
public:
	CFeeler();
	CFeeler(const Mth::Vector &start, const Mth::Vector &end);

	void 							SetIgnore(uint16 ignore_1, uint16 ignore_0);
	void 							SetLine(const Mth::Vector &start, const Mth::Vector &end);
	void 							SetStart(const Mth::Vector &start);	
	void 							SetEnd(const Mth::Vector &end);

	void							DebugLine(int r=255, int g=255, int b=255, int num_frames = 0);
	
	bool							GetCollision(const Mth::Vector &start, const Mth::Vector &end, bool movables = true);
	bool							GetCollision(bool movables = true, bool far = false);
	inline bool						GetFarCollision(bool movables = true) {return GetCollision(movables, true);};
	bool							GetMovableCollision(const Mth::Vector &start, const Mth::Vector &end);
	bool							GetMovableCollision(bool far = false);
	inline bool						GetFarMovableCollision() {return GetMovableCollision(true);};
	bool							IsBrightnessAvailable();
	float							GetBrightness();
	
	float							GetDist() const;
	const Mth::Vector			&	GetPoint() const;
	const Mth::Vector			&	GetNormal() const;
	int								GetFaceIndex() const;
	
	void 							SetCallback(FEELER_CB p_callback);
	void 							SetCallbackData(void *p_callback_data);
	void * 							GetCallbackData() const;

	void							SetCache(Nx::CCollCache *p_cache);
	void							ClearCache();

	uint16 							GetFlags() const;
	ETerrainType 					GetTerrain() const;
	bool							IsMovableCollision(); 
	Nx::CCollObj *	 				GetSector() const;		// aieee!!!!!!
	Obj::CCompositeObject *			GetMovingObject();
	Obj::CCompositeObject *			GetCallbackObject() const;
	bool 							GetTrigger() const;
	uint32 							GetNodeChecksum() const;
	uint32 							GetScript() const;
	void 							SetScript(uint32 script);
	void 							SetTrigger(bool trigger);

	static void						sSetDefaultCache(Nx::CCollCache *p_cache);
	static void						sClearDefaultCache();

	// feeler callback probably should not be public						   
	FEELER_CB			mp_callback;	   // a callback called every frame
						   
private:
	void							init (   );
	
// for now, our implmentation will just copy over the relevent things from rw		
// as needed
// keep m_col_data private, and eventually we can get rid of it	
	Nx::CollData 		m_col_data;	 			// 
	float				m_dist;
	Mth::Vector			m_point;
	Mth::Vector			m_normal;

	Obj::CSmtPtr mp_movable_collision_obj;	//  set if last collision was with a movable
	uint32 				m_movable_collision_id;		// id of this object, so we can check if it is dead.
	
	void 		*		mp_callback_data;		// 	pointer to some data the callback can use

	Nx::CCollCache *	mp_cache;

	static Nx::CCollCache *	sp_default_cache;
};

inline float	CFeeler::GetDist() const
{
	return m_dist;
}

inline const Mth::Vector &CFeeler::GetPoint() const
{
	return m_point;
}

inline const Mth::Vector &CFeeler::GetNormal() const
{
	return m_normal;
}

inline int		CFeeler::GetFaceIndex() const
{
	return m_col_data.surface.index;
}

inline uint16	CFeeler::GetFlags() const
{
	return m_col_data.flags;
}

inline ETerrainType CFeeler::GetTerrain() const
{
	return m_col_data.terrain;
}

inline Nx::CCollObj* CFeeler::GetSector() const // aieee!!!!!!
{
	return m_col_data.p_coll_object;
}

inline bool		CFeeler::GetTrigger() const
{
	return m_col_data.trigger;
}

inline uint32	CFeeler::GetNodeChecksum() const
{
	return m_col_data.node_name;
}

inline uint32	CFeeler::GetScript() const
{
	return m_col_data.script;	
}

inline void		CFeeler::SetScript(uint32 script)
{
	m_col_data.script = script; 
}

inline void		CFeeler::SetTrigger(bool trigger)
{
	m_col_data.trigger = trigger; 
}

inline void		CFeeler::SetCallback(FEELER_CB p_callback)
{
	mp_callback = p_callback;
}

inline void		CFeeler::SetCallbackData(void *p_callback_data)
{
	mp_callback_data = p_callback_data;
}

inline void *		CFeeler::GetCallbackData() const
{
	return mp_callback_data;
}

inline void		CFeeler::SetCache(Nx::CCollCache *p_cache)
{
	mp_cache = p_cache;
}

inline void		CFeeler::ClearCache()
{
	mp_cache = NULL;
}

inline void		CFeeler::sSetDefaultCache(Nx::CCollCache *p_cache)
{
	sp_default_cache = p_cache;
}

inline void		CFeeler::sClearDefaultCache()
{
	sp_default_cache = NULL;
}

#endif


================================================
FILE: Code/Sk/Engine/sounds.cpp
================================================
/*
	This is just a database for arrays of skater sounds on different terrains.
	
	For each type of sound in sounds.h (like grind, land, slide, wheel roll, etc...)
	there is an array of sounds to play, one for each possible terrain type.
	
	A terrain type of zero indicates that the sound is the default (for surfaces not
	defined in the list).
*/


#include 
#include 
#include 

#include 
#include 
#include 
#include 
						   
#include 
#include 
#include 
#include 



namespace Sk3Sfx
{



DefineSingletonClass( CSk3SfxManager, "Sk3 Sound FX" );

CSk3SfxManager::CSk3SfxManager( void )
{
	
	Reset( );
}

CSk3SfxManager::~CSk3SfxManager( void )
{
	
}

void CSk3SfxManager::Reset( void )
{
	
	int i;
	for ( i = 0; i < vNUM_SOUND_TYPES; i++ )
	{
		mNumEntries[ i ] = 0;
	}
} // end of Reset( )

// Sound FX for the level...
/*	The surface flag indicates which surface the skater is currently on (grass, cement, wood, metal)
	whichSound is the checksum from the name of the looping sound (should be loaded using LoadSound
		in the script file for each level)
	whichArray indicates whether this sound belongs in the list of wheels rolling sounds, or
		grinding sounds, etc...
*/
void CSk3SfxManager::SetSkaterSoundInfo( int surfaceFlag, uint32 whichSound, int whichArray,
	float maxPitch, float minPitch, float maxVol, float minVol )
{
	
	if (Sfx::NoSoundPlease()) return;
	
	// must initialize PInfo!
	int i;
	Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();

	if ( NULL == sfx_manager->GetWaveTableIndex( whichSound ) )
	{
		Dbg_MsgAssert( 0,( "Terrain sound not loaded! surface %d sound %s checksum %d soundType %d",
			surfaceFlag, Script::FindChecksumName( whichSound ), whichSound, whichArray ));
		return;
	}
	int numEntries = mNumEntries[ whichArray ];
	SkaterSoundInfo	*pArray = mSoundArray[ whichArray ];
	SkaterSoundInfo *pInfo = NULL;
	
	for ( i = 0; i < numEntries; i++ )
	{
		if ( pArray[ i ].surfaceFlag == surfaceFlag )
		{
			Dbg_Message( "Re-defining soundtype %d for surfaceFlag %d", whichArray, surfaceFlag );
			pInfo = &pArray[ i ];
			break;
		}
	}
	if ( !pInfo )
	{
		pInfo = &pArray[ mNumEntries[ whichArray ] ];
		mNumEntries[ whichArray ] += 1;
		Dbg_MsgAssert( mNumEntries[ whichArray ] < vMAX_NUM_ENTRIES,( "Array too small type %d.  Increase MAX_NUM_ENTRIES.", whichArray ));
	}
	
	Dbg_MsgAssert( pInfo,( "Please fire Matt immediately after kicking him in the nuts." ));
	
	// surfaceFlag of zero will be used for the default
	pInfo->surfaceFlag = surfaceFlag;
	// if soundChecksum is zero, no sound will play on this surface.
	pInfo->soundChecksum = whichSound;
	pInfo->maxPitch = maxPitch;
	pInfo->minPitch = minPitch;
	pInfo->maxVol = maxVol;
	pInfo->minVol = minVol;

} // end of SetSkaterSoundInfo( )

SkaterSoundInfo	*CSk3SfxManager::GetSkaterSoundInfo( int surfaceFlag, int whichArray )
{
	
	if (Sfx::NoSoundPlease()) return NULL;
	
	
	int numEntries = 0;
	numEntries = mNumEntries[ whichArray ];
	SkaterSoundInfo	*pArray = mSoundArray[ whichArray ];
	Dbg_MsgAssert( pArray,( "Fire Matt please." ));
	int i;
	for ( i = 0; i < numEntries; i++ )
	{
		if ( pArray[ i ].surfaceFlag == surfaceFlag )
		{
			return ( &pArray[ i ] );
		}
	}
	// couldn't find the surface flag in the table... return the default:
	for ( i = 0; i < numEntries; i++ )
	{
		if ( !pArray[ i ].surfaceFlag )
		{
			return ( &pArray[ i ] );
		}
	}

	return ( NULL );
} // end of GetSkaterSoundInfo( )

void CSk3SfxManager::PlaySound( int whichArray, int surfaceFlag, const Mth::Vector &pos, float volPercent,
								bool propogate )
{
	if (Sfx::NoSoundPlease()) return;
	

	Replay::WriteSkaterSoundEffect(whichArray,surfaceFlag,pos,volPercent);
	
	Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();
	GameNet::Manager * gamenet_manager = GameNet::Manager::Instance();
    
	SkaterSoundInfo	*pInfo = GetSkaterSoundInfo( surfaceFlag, whichArray );
	if ( !pInfo )
	{
		// no sounds are supposed to be played for this surface on this transition:
		return;
	}

	if( propogate )
	{
		Net::Client* client;
		GameNet::MsgPlaySound msg;
		Net::MsgDesc msg_desc;

		client = gamenet_manager->GetClient( 0 );
		Dbg_Assert( client );

		msg.m_WhichArray = (char) whichArray;
		msg.m_SurfaceFlag = (char) surfaceFlag;
		msg.m_Pos[0] = (short) pos[X];
		msg.m_Pos[1] = (short) pos[Y];
		msg.m_Pos[2] = (short) pos[Z];
		msg.m_VolPercent = (char) volPercent;

		msg_desc.m_Data = &msg;
		msg_desc.m_Length = sizeof( GameNet::MsgPlaySound );
		msg_desc.m_Id = GameNet::MSG_ID_PLAY_SOUND;
		client->EnqueueMessageToServer( &msg_desc );
	}

//	float volL, volR;
	Sfx::sVolume vol;
//	sfx_manager->SetVolumeFromPos( &volL, &volR, pos, sfx_manager->GetDropoffDist( pInfo->soundChecksum ) );
	sfx_manager->SetVolumeFromPos( &vol, pos, sfx_manager->GetDropoffDist( pInfo->soundChecksum ));

	// Adjust volume according to speed.
	volPercent = GetVolPercent( pInfo, volPercent );
//	volL = PERCENT( volL, volPercent );
//	volR = PERCENT( volR, volPercent );
	vol.PercentageAdjustment( volPercent );

//	sfx_manager->PlaySound( pInfo->soundChecksum, volL, volR );
	sfx_manager->PlaySound( pInfo->soundChecksum, &vol );
}

// set the volume according to the range specified by the designers...
float CSk3SfxManager::GetVolPercent( SkaterSoundInfo *pInfo, float volPercent, bool clipToMaxVol )
{
	
	Dbg_MsgAssert( pInfo,(( "Fire whoever called this function with this nonsense." )));
	if ( !( ( pInfo->minVol == 0.0f ) && ( pInfo->maxVol == 100.0f ) ) )
	{
		volPercent = ( pInfo->minVol + PERCENT( ( pInfo->maxVol - pInfo->minVol ), volPercent ) );
	}
	
	if ( clipToMaxVol )
	{
		if ( volPercent > pInfo->maxVol )
			volPercent = pInfo->maxVol;
	}
	return ( volPercent );
}

} // namespace sfx


================================================
FILE: Code/Sk/Engine/sounds.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Skate Module (SKATE) 									**
**																			**
**	File name:		sk/engine/sounds.h										**
**																			**
**	Created by:		Matt Jan. 2001											**
**																			**
**	Description:	Lists of soundFX for terrains, etc...					**
**																			**
*****************************************************************************/

#ifndef __GEL_SK_SOUNDS_H
#define __GEL_SK_SOUNDS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

namespace Sk3Sfx
{




struct SkaterSoundInfo
{
	int		surfaceFlag;
	uint32	soundChecksum;
	float		maxPitch;
	float		minPitch;
	float		maxVol;
	float		minVol;
};

/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

class  CSk3SfxManager  : public Spt::Class
{
	
	DeclareSingletonClass( CSk3SfxManager );

public:
	// these sound trigger values are also used to
	// communicate with the sounds.cpp module and scripted
	// soundfx for jumping/landing... if changed please
	// change skater_sfx.q also:
	enum
	{
		vTABLE_WHEELROLL,
		vTABLE_GRIND, // on a rail...
		vTABLE_JUMP,
		vTABLE_LAND,
		vTABLE_BONK,
		vTABLE_GRINDJUMP,
		vTABLE_GRINDLAND,
		vTABLE_SLIDE, // on a rail
		vTABLE_SLIDEJUMP,
		vTABLE_SLIDELAND,
		vTABLE_CESS,
		vNUM_SOUND_TYPES,
	};
	
	CSk3SfxManager::CSk3SfxManager( void );
	CSk3SfxManager::~CSk3SfxManager( void );

	void			Reset( void );
	void			SetSkaterSoundInfo( int surfaceFlag, uint32 whichSound,
									int whichArray, float maxPitch, float minPitch, float maxVol, float minVol );
	SkaterSoundInfo	*GetSkaterSoundInfo( int surfaceFlag, int whichArray );

	float			GetVolPercent( SkaterSoundInfo *pInfo, float volPercent, bool clipToMaxVol = false );

	void 			PlaySound( int whichArray, int surfaceFlag, const Mth::Vector &pos, float volPercent,
							   bool propogate = true );

private:
	
	enum
	{
		vMAX_NUM_ENTRIES  = 64,
	};

	int mNumEntries[ vNUM_SOUND_TYPES ];
	SkaterSoundInfo mSoundArray[ vNUM_SOUND_TYPES ][ vMAX_NUM_ENTRIES ];
};

}  // namespace Sfx

#endif


================================================
FILE: Code/Sk/GameNet/ExportMsg.h
================================================
#ifndef __ENGINE_EXPORTMSG_H__
#define	__ENGINE_EXPORTMSG_H__

#include 

namespace Net
{

#define	vSERVER_IP_VARIABLE	"VIEWER_IP"
#define vXBOX_SERVER_IP_VARIABLE "XBOX_VIEWER_IP"
#define vNGC_SERVER_IP_VARIABLE "NGC_VIEWER_IP"

enum
{
	vEXPORT_COMM_PORT = 10000,
};	

enum
{	
	vMSG_ID_QUICKVIEW		= 32,	//	32 is the first available user-defined message id
	vMSG_ID_UPDATE_MATERIAL,		//	New material properties
	vMSG_ID_REMOTE_Q,
	vMSG_ID_VIEWOBJ_LOAD_MODEL,
	vMSG_ID_VIEWOBJ_UNLOAD_MODEL,
	vMSG_ID_VIEWOBJ_SET_ANIM,
	vMSG_ID_VIEWOBJ_SET_ANIM_SPEED,
	vMSG_ID_VIEWOBJ_INCREMENT_FRAME,
	vMSG_ID_VIEWOBJ_SET_ANIM_FILE,
	vMSG_ID_VIEWOBJ_SET_CAMANIM_FILE,
	vMSG_ID_INCREMENTAL_UPDATE,
	vMSG_ID_RUN_SCRIPT_COMMAND,
	vMSG_ID_VIEWOBJ_PREVIEW_SEQUENCE,
};

class MsgViewObjLoadModel
{
public:
	char	m_ModelName[128];
	uint32	m_AnimScriptName;
	uint32	m_SkeletonName;
};

class MsgViewObjSetAnimSpeed
{
public:
	float	m_AnimSpeed;
};

class MsgViewObjSetAnim
{
public:
	uint32	m_AnimName;
};

class MsgViewObjIncrementFrame
{
public:
	bool	m_Forwards;
};				   

class MsgViewObjSetAnimFile
{
public:
	char    m_Filename[128];
	uint32  m_checksum;
};

class MsgViewObjSetCamAnimFile
{
public:
	char    m_Filename[128];
	uint32  m_checksum;
};

class MsgQuickview
{
public:
	char	m_Filename[128];
	char	m_UpdateFilename[128];
};

class MsgMaterialUpdate
{
public:
	unsigned long	MaterialChecksum;
	int				m_BlendMode;
	int				m_FixedAlpha;
	int				m_MappingMode;		// Explicit or procedural (eg. environment-mapping)	
	int				m_MinFilteringMode;	// Point/Bi-linear
	int				m_MagFilteringMode;	// Point/Bi-linear/Tri-linear
	bool			m_UVWibbleEnabled;		
	float			m_UVel;
	float			m_VVel;	
	float			m_UAmplitude;
	float			m_VAmplitude;
	float			m_UPhase;
	float			m_VPhase;
	float			m_UFrequency;
	float			m_VFrequency;
	float			m_MipMapK;
	int				m_MipMapL;	
};

}
#endif	// __ENGINE_EXPORTMSG_H__

================================================
FILE: Code/Sk/GameNet/GameHandler.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate3													**
**																			**
**	Module:			GameNet					 								**
**																			**
**	File name:		GameHandler.cpp											**
**																			**
**	Created by:		08/09/01	-	spg										**
**																			**
**	Description:	Game-Side Network Handlers								**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

#include 
#include 
#include 

#include 

#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 		   // 	 various high level network game things

#ifdef __PLAT_NGPS__
#include 
#include 
#include 
#endif

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace GameNet
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define vTIME_BEFORE_CHANGING_LEVEL		Tmr::Seconds( 3 )

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static Tmr::Time	s_time_change_level;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Handler Functions								**
*****************************************************************************/

/******************************************************************/
/* The server has told us to create a new skater and associate it */
/* with a player                                                  */
/******************************************************************/

int Manager::s_handle_new_player( Net::MsgHandlerContext* context )
{
	MsgNewPlayer* msg;
	Manager* gamenet_man;
	
	msg = (MsgNewPlayer *) context->m_Msg;
	gamenet_man = (Manager *) context->m_Data;

	NewPlayerInfo new_player;

	strcpy( new_player.Name, msg->m_Name );
	new_player.ObjID = msg->m_ObjId;
	new_player.Conn = NULL;
	new_player.Flags = msg->m_Flags;
	new_player.Team = msg->m_Team;
	new_player.Profile = msg->m_Profile;
	new_player.Rating = msg->m_Rating;
	new_player.Score = msg->m_Score;
	new_player.VehicleControlType = msg->m_VehicleControlType;

	if( new_player.Flags & PlayerInfo::mLOCAL_PLAYER )
	{
		Dbg_Message( "(%d) Got local new player with ID %d!\n", context->m_App->m_FrameCounter,
															msg->m_ObjId );
	}
	else
	{
		Dbg_Message( "(%d) Got new player with ID %d!\n", context->m_App->m_FrameCounter,
															msg->m_ObjId );
	}

	// Observers and local players do not receive their own appearance data
	if( !( new_player.Flags & ( PlayerInfo::mOBSERVER | PlayerInfo::mLOCAL_PLAYER )))
	{
		// GJ:  the appearance is transmitted inside the message,
		// so copy it to the "new_player" structure
		new_player.mpSkaterProfile->ReadFromBuffer( msg->m_AppearanceData );
	}
	
	// If this is all part of our join packet, just load players immediately instead of deferring it
	if( gamenet_man->ReadyToPlay() == false )
	{
		Dbg_Printf( "Calling DeferredNewPlayer with no wait\n" );
		gamenet_man->DeferredNewPlayer( &new_player, 0 );
	}
	else
	{
		gamenet_man->CreateNetPanelMessage( false, Script::GenerateCRC("net_message_new_player"), 
											new_player.Name );

		gamenet_man->DeferredNewPlayer( &new_player );
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_wait_n_seconds( Net::MsgHandlerContext* context )
{
	char *num_seconds = (char*) context->m_Msg;

	context->m_Conn->UpdateCommTime( Tmr::Seconds( *num_seconds ));
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_player_info_ack_req( Net::MsgHandlerContext* context )
{
	MsgIntInfo* msg;
	Manager* gamenet_man;
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	Net::MsgDesc msg_desc;
	int i;

	Dbg_Printf( "Handling PlayerInfo ack request\n" );
	// At this point, we've received the batch of player info's representing the players
	// that were in the game at the time of our join. Advance our state machine
	gamenet_man = (Manager *) context->m_Data;

	gamenet_man->SetJoinState( GameNet::vJOIN_STATE_GOT_PLAYERS );
    
	msg = (MsgIntInfo*) context->m_Msg;

	msg_desc.m_Id = MSG_ID_PLAYER_INFO_ACK;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;

	context->m_App->EnqueueMessageToServer( &msg_desc );

	if( !gamenet_man->OnServer())
	{
		for( i = 0; i < 2; i++ )
		{
			Net::Client* client;
	
			if(( client = gamenet_man->GetClient( i )))
			{
				skate_mod->AddNetworkMsgHandlers( client, i );
			}
		}
	}
                                                            
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_player_info_ack( Net::MsgHandlerContext* context )
{   
	Net::Manager * net_man = Net::Manager::Instance();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	PlayerInfo* player;
	Manager* gamenet_man;

	gamenet_man = (Manager*) context->m_Data;
	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	if( player )
	{
		// tell them what level to load
		if( !( player->m_flags.TestMask( PlayerInfo::mHAS_PLAYER_INFO )))
		{
			Lst::Search< TriggerEvent > trigger_sh;
			TriggerEvent* trigger;
			MsgStartInfo start_info_msg;
			Net::MsgDesc start_desc;
			PlayerInfo* king;
			MsgReady ready_msg;
		
			ready_msg.m_Time = Tmr::GetTime();
	
			player->m_flags.SetMask( PlayerInfo::mHAS_PLAYER_INFO );
			
			start_info_msg.m_MaxPlayers = gamenet_man->GetMaxPlayers();
			if( player->IsLocalPlayer())
			{
				gamenet_man->SetReadyToPlay(true);
			}
			else
			{
				// If the game is over, just tell the client to join in net_lobby mode
				if( gamenet_man->GameIsOver())
				{
					start_info_msg.m_GameMode = Script::GenerateCRC( "netlobby" );
					start_info_msg.m_TimeLeft = 0;
					start_info_msg.m_TimeLimit = 0;
					start_info_msg.m_TargetScore = 0;
				}
				else
				{
					start_info_msg.m_GameMode = skate_mod->GetGameMode()->GetNameChecksum();
					start_info_msg.m_TimeLeft = Tmr::InSeconds( skate_mod->GetGameMode()->GetTimeLeft());
					//start_info_msg.m_TimeLimit = gamenet_man->m_network_preferences.GetPreferenceValue( Script::GenerateCRC("time_limit"), Script::GenerateCRC("time") );
					start_info_msg.m_TimeLimit = skate_mod->GetGameMode()->GetTimeLimit();
					start_info_msg.m_TargetScore = gamenet_man->m_network_preferences.GetPreferenceValue( Script::GenerateCRC("target_score"), Script::GenerateCRC("score") );
				}
				start_info_msg.m_GameId = gamenet_man->GetNetworkGameId();
				start_info_msg.m_LevelId = gamenet_man->GetNetworkLevelId();
				start_info_msg.m_TeamMode = skate_mod->GetGameMode()->NumTeams();
				start_info_msg.m_CrownSpawnPoint = gamenet_man->m_crown_spawn_point;
				start_info_msg.m_ProSetFlags = gamenet_man->m_proset_flags;
				memcpy( start_info_msg.m_StartPoints, gamenet_man->m_skater_starting_points, 
						Mdl::Skate::vMAX_SKATERS * sizeof( int ));
				if( net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM )
				{
					start_info_msg.m_Broadband = 0;
				}
				else
				{
					start_info_msg.m_Broadband = 1;
				}

				start_desc.m_Data = &start_info_msg;
				start_desc.m_Length = sizeof( MsgStartInfo );
				start_desc.m_Id = MSG_ID_START_INFO;
				start_desc.m_Queue = Net::QUEUE_SEQUENCED;
				start_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
				gamenet_man->m_server->EnqueueMessage( 	player->GetConnHandle(), &start_desc );

				if( gamenet_man->UsingCreatedGoals())
				{
					gamenet_man->LoadGoals( gamenet_man->GetNetworkLevelId());
					gamenet_man->m_server->StreamMessage( player->GetConnHandle(), MSG_ID_GOALS_DATA, gamenet_man->GetGoalsDataSize(), 
												 gamenet_man->GetGoalsData(), "goals data", vSEQ_GROUP_PLAYER_MSGS,
												 false, true );
				}

				// tell them to execute the queue of scripts
				for( trigger = trigger_sh.FirstItem( gamenet_man->m_trigger_events ); trigger; 
						trigger = trigger_sh.NextItem())
				{
					MsgSpawnAndRunScript script_msg;
					Net::MsgDesc script_desc;
	
					script_msg.m_Node = trigger->m_Node;
					script_msg.m_ObjID = trigger->m_ObjID;
					script_msg.m_ScriptName = trigger->m_Script;
					script_msg.m_Permanent = 0;
	
					script_desc.m_Data = &script_msg;
					script_desc.m_Length = sizeof( MsgSpawnAndRunScript );
					script_desc.m_Id = MSG_ID_SPAWN_AND_RUN_SCRIPT;
					script_desc.m_Queue = Net::QUEUE_SEQUENCED;
					script_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
					gamenet_man->m_server->EnqueueMessage( 	player->GetConnHandle(), &script_desc );
				}
	
				if( player->IsLocalPlayer() == false )
				{
					skate_mod->SendCheatList( player );
				}

				// Update the observers with graffiti status
				if( player->IsObserving())
				{
					if( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netgraffiti" ))
					{
						MsgInitGraffitiState init_graffiti_state_msg;
						Net::MsgDesc obs_msg_desc;
						uint32 msg_size = skate_mod->GetTrickObjectManager()->SetInitGraffitiStateMessage( &init_graffiti_state_msg );
						Dbg_Printf( "Sending graffiti state message %d\n", msg_size );
		
						obs_msg_desc.m_Data = &init_graffiti_state_msg;
						obs_msg_desc.m_Length = msg_size;
						obs_msg_desc.m_Id = MSG_ID_OBSERVER_INIT_GRAFFITI_STATE;
						obs_msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
						obs_msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;

						gamenet_man->m_server->StreamMessage( player->GetConnHandle(), obs_msg_desc.m_Id, obs_msg_desc.m_Length, 
						   obs_msg_desc.m_Data, "TrickObj Buffer", GameNet::vSEQ_GROUP_PLAYER_MSGS );
						//gamenet_man->m_server->EnqueueMessage( 	player->GetConnHandle(), &obs_msg_desc );
                        
					}
				}
								
				// Compile the list of selected goals to send to the client
				{
					int i, num_goals;
					Game::CGoalManager* pGoalManager;
					Game::CGoal* pGoal;
					Net::MsgMax msg;
					Net::MsgDesc msg_desc;
					uint32 goal_id;
					char* data;
					 
					pGoalManager = Game::GetGoalManager();
			
					data = msg.m_Data;
					*data++ = 0;	// This indicates that they should not bring up the goal summary dialog
					num_goals = pGoalManager->GetNumGoals();
					for( i = 0; i < num_goals; i++ )
					{
						pGoal = pGoalManager->GetGoalByIndex( i );
						Dbg_Assert( pGoal );
						if( pGoal->IsSelected())
						{
							goal_id = pGoal->GetGoalId();
							memcpy( data, &goal_id, sizeof( uint32 ));
							data += sizeof( uint32 );
						}
					}
			
					// zero-terminate the list of goals
					goal_id = 0;
					memcpy( data, &goal_id, sizeof( uint32 ));
					data += sizeof( uint32 );
			
					msg_desc.m_Data = &msg;
					msg_desc.m_Length = (int) ( data - msg.m_Data );
					msg_desc.m_Id = MSG_ID_SELECT_GOALS;
					msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
					msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
					gamenet_man->m_server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
				}
				
				// Update the player with the king of the hill status
				if(( king = gamenet_man->GetKingOfTheHill()))
				{
					GameNet::MsgByteInfo msg;
					Net::MsgDesc msg_desc;
	
					Dbg_MsgAssert( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0x6ef8fda0,"netking")  ||
								   skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0x5d32129c,"king") ,
									( "King exists in non-king of the hill game" ));
	
					msg.m_Data = king->m_Skater->GetID();

					msg_desc.m_Data = &msg;
					msg_desc.m_Length = sizeof( GameNet::MsgByteInfo );
					msg_desc.m_Id = MSG_ID_NEW_KING;
					msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
					msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
					gamenet_man->m_server->EnqueueMessage(	player->GetConnHandle(), &msg_desc );
				}
			}
			
			Net::MsgDesc proceed_desc;

			proceed_desc.m_Id = MSG_ID_PROCEED_TO_PLAY;
			proceed_desc.m_Queue = Net::QUEUE_SEQUENCED;
			proceed_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
			// Send this to let the client know when he has received the full set of initial data
			gamenet_man->m_server->EnqueueMessage( 	player->GetConnHandle(), &proceed_desc );

			if( gamenet_man->InNetGame())
			{
				gamenet_man->SendFaceDataToPlayer( player );
			}
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_ready_query( Net::MsgHandlerContext* context )
{
	MsgReady* msg;
	Manager* gamenet_man;

	gamenet_man = (Manager*) context->m_Data;
	msg = (MsgReady*) context->m_Msg;
	
	gamenet_man->m_latest_ready_query = msg->m_Time;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_ready_response( Net::MsgHandlerContext* context )
{
	PlayerInfo* player;
	Manager* gamenet_man;
	MsgReady* ready_msg;

	gamenet_man = (Manager*) context->m_Data;
	ready_msg = (MsgReady* ) context->m_Msg;
	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	if( player )
	{   
		player->MarkAsReady( ready_msg->m_Time );
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Respond to a client who is looking for games to join on the LAN*/
/* Tell them info about the game                                  */
/******************************************************************/

int Manager::s_handle_find_server( Net::MsgHandlerContext* context )
{
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	Lst::Search< NewPlayerInfo > new_sh;
	NewPlayerInfo* np;
    MsgServerDescription msg;
	MsgFindServer* find_msg;
	Manager* gamenet_man;
	Net::Server* server;
	int i;
	char name[vMAX_SERVER_NAME_LEN + 1];
	const char *server_name;
	Script::CScriptStructure* pStructure;
	Prefs::Preferences* pPreferences;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();   

	//Dbg_Printf( "Got find server broadcast\n" );
	if( context->m_MsgLength != sizeof( MsgFindServer ))
	{
		return Net::HANDLER_MSG_DONE;
	}

	gamenet_man = (Manager*) context->m_Data;
	if( gamenet_man->InInternetMode())
	{
		return Net::HANDLER_MSG_DONE;
	}

	pPreferences = gamenet_man->GetNetworkPreferences();
	pStructure = pPreferences->GetPreference( Script::GenerateCRC("server_name") );
	pStructure->GetText( "ui_string", &server_name, true );
	
	find_msg = (MsgFindServer *) context->m_Msg;
    
	strncpy( name, server_name, vMAX_SERVER_NAME_LEN );
	name[ vMAX_SERVER_NAME_LEN ] = '\0';
	
	server = gamenet_man->GetServer();
	Dbg_Assert( server != NULL );

	msg.m_SkillLevel = gamenet_man->GetSkillLevel();
	msg.m_NumPlayers = gamenet_man->GetNumPlayers();
	msg.m_MaxPlayers = gamenet_man->GetMaxPlayers();
	msg.m_NumObservers = gamenet_man->GetNumObservers();
	msg.m_MaxObservers = gamenet_man->GetMaxObservers();
    
	// Handle the cases where you've just changed the limits and your current number of players/observers
	// exceeds the limits
	if( msg.m_MaxPlayers < msg.m_NumPlayers )
	{
		msg.m_MaxPlayers = msg.m_NumPlayers;
	}
	if( msg.m_MaxObservers < msg.m_NumObservers )
	{
		msg.m_MaxObservers = msg.m_NumObservers;
	}

	if( strcmp( gamenet_man->GetPassword(), "" ) == 0 )
	{
		msg.m_Password = 0;
	}
	else
	{
		msg.m_Password = 1;
	}

	if( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netlobby" ))
	{
		msg.m_GameStarted = false;
	}
	else
	{
		msg.m_GameStarted = true;
	}

	msg.m_HostMode = gamenet_man->GetHostMode();
#ifdef __PLAT_NGPS__	
	msg.m_Ranked = false;
	if( gamenet_man->InInternetMode())
	{
		if( gamenet_man->mpBuddyMan->IsLoggedIn())
		{
			msg.m_Ranked = true;
		}
	}
#endif
	
	strcpy( msg.m_Name, name );
	sprintf( msg.m_Level, "%s", gamenet_man->GetLevelName());
	sprintf( msg.m_Mode, "%s", gamenet_man->GetGameModeName());
	msg.m_Timestamp = find_msg->m_Timestamp;
#ifdef __PLAT_XBOX__
	memcpy( msg.m_Nonce, find_msg->m_Nonce, 8 );
	memcpy( &msg.m_XboxKeyId, &server->m_XboxKeyId, sizeof( XNKID ));
	memcpy( &msg.m_XboxKey, &server->m_XboxKey, sizeof( XNKEY ));
	memcpy( &msg.m_XboxAddr, &server->m_XboxAddr, sizeof( XNADDR ));
#endif // __PLAT_XBOX__

	i = 0;

	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; 
			player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		if( !player->IsObserving() || player->IsPendingPlayer())
		{
			sprintf( msg.m_PlayerNames[i], player->m_Name );
			i++;
		}
	}
    
	for( np = gamenet_man->FirstNewPlayerInfo( new_sh ); np; np = gamenet_man->NextNewPlayerInfo( new_sh ))
	{
		// Pending players count, observers don't
		if(	( np->Flags & PlayerInfo::mPENDING_PLAYER ) ||
			( np->Flags & PlayerInfo::mJUMPING_IN ) ||
			!( np->Flags & PlayerInfo::mOBSERVER ))
		{
			sprintf( msg.m_PlayerNames[i], np->Name );
			i++;
		}                 
	}
	
#ifdef __PLAT_XBOX__	
	server->SendMessageTo( Net::MSG_ID_SERVER_RESPONSE, sizeof( MsgServerDescription ), &msg,
							INADDR_BROADCAST, context->m_Conn->GetPort(), 0 );
#else
	server->SendMessageTo( Net::MSG_ID_SERVER_RESPONSE, sizeof( MsgServerDescription ), &msg,
							context->m_Conn->GetIP(), context->m_Conn->GetPort(), 0 );	
#endif		

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Got a connection request: For now, just accept it, create a    */
/* skater for the requester, and tell them to create one too      */
/******************************************************************/

int	Manager::s_handle_connection( Net::MsgHandlerContext* context )
{   
	Manager* gamenet_man;
	MsgConnectInfo* connect_msg;
	int reason;
	
	Dbg_Assert( context );

	gamenet_man = (Manager*) context->m_Data;
	connect_msg = (MsgConnectInfo*) context->m_Msg;
    
	if( context->m_Conn->IsRemote())
	{
		MsgProceed msg;
		const char *server_name;
		Script::CScriptStructure* pStructure;
		Prefs::Preferences* pPreferences;
		Net::Manager* net_man = Net::Manager::Instance();
		
		if( !gamenet_man->ok_to_join( reason, connect_msg, context->m_Conn ))
		{
			MsgEmbedded msg;
	
			msg.m_SubMsgId = reason;

			context->m_App->SendMessageTo( MSG_ID_JOIN_REFUSED, sizeof( MsgEmbedded ), &msg, 
										   context->m_Conn->GetIP(), context->m_Conn->GetPort(), 0 );
			return Net::HANDLER_HALT;
		}

		Dbg_Printf( "Sending Join Proceed Message to IP: %x Port: %d MaxPlayers: %d\n", context->m_Conn->GetIP(), context->m_Conn->GetPort(), gamenet_man->GetNumPlayers());
		msg.m_MaxPlayers = gamenet_man->GetMaxPlayers();
		if(	net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM )
		{
			msg.m_Broadband = 0;
		}
		else
		{
			msg.m_Broadband = 1;
		}
		
		if( gamenet_man->InInternetMode())
		{
			msg.m_Port = gamenet_man->GetJoinPort();
			msg.m_PrivateIP = gamenet_man->GetJoinPrivateIP();
			msg.m_PublicIP = gamenet_man->GetJoinIP();
		}
		
		pPreferences = gamenet_man->GetNetworkPreferences();
		pStructure = pPreferences->GetPreference( Script::GenerateCRC("server_name") );
		pStructure->GetText( "ui_string", &server_name, true );
	
		strncpy( msg.m_ServerName, server_name, vMAX_SERVER_NAME_LEN );
		msg.m_ServerName[ vMAX_SERVER_NAME_LEN ] = '\0';

		context->m_App->SendMessageTo( 	MSG_ID_JOIN_PROCEED, sizeof( MsgProceed), &msg,
										context->m_Conn->GetIP(), context->m_Conn->GetPort(), 0 );
	}
	else
	{
		MsgProceed msg;
		Net::MsgDesc msg_desc;

		msg.m_MaxPlayers = gamenet_man->GetMaxPlayers();
		msg_desc.m_Id = MSG_ID_JOIN_PROCEED;
		msg_desc.m_Data = &msg;
		msg_desc.m_Length = sizeof( MsgProceed );
		context->m_App->EnqueueMessage( context->m_Conn->GetHandle(), &msg_desc );
		//context->m_App->SendData();
	}
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Client wants to join a connected server						  */
/*																  */
/******************************************************************/

int	Manager::s_handle_join_request( Net::MsgHandlerContext* context )
{   
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	NewPlayerInfo new_player;
	Manager* gamenet_man;
	MsgJoinInfo* join_msg;
	int reason;
	
	Dbg_Assert( context );

	Dbg_Printf( "Got Join Request from ip %x port %d\n", context->m_Conn->GetIP(), context->m_Conn->GetPort());

	gamenet_man = (Manager*) context->m_Data;
	join_msg = (MsgJoinInfo*) context->m_Msg;

	// We'll just accept local clients
	if( context->m_Conn->IsRemote())
	{
		// Rule out redundancy
		if( context->m_PacketFlags & Net::mHANDLE_FOREIGN )
		{
			Net::Conn* conn;

			// Are we currently accepting players?
			if( context->m_App->AcceptsForeignConnections() == false )
			{
				return Net::HANDLER_MSG_DONE;
			}
			
			// Create a more permanent connection
			conn = context->m_App->NewConnection( context->m_Conn->GetIP(), context->m_Conn->GetPort());
			if( join_msg->m_Broadband )
			{
				conn->SetBandwidthType( Net::Conn::vBROADBAND );
				conn->SetSendInterval( Tmr::VBlanks( 2 ));
			}
			else
			{
				conn->SetBandwidthType( Net::Conn::vNARROWBAND );
				conn->SetSendInterval( Tmr::VBlanks( 2 ));
			}
			
			
			context->m_Conn = conn;	// the rest of the chain will use this new, valid connection
			
			if( !gamenet_man->ok_to_join( reason, join_msg, conn ))
			{
				MsgEmbedded msg;
				Net::MsgDesc msg_desc;
		
				msg.m_SubMsgId = reason;
		
				msg_desc.m_Data = &msg;
				msg_desc.m_Length = sizeof( MsgEmbedded );
				msg_desc.m_Id = MSG_ID_JOIN_REFUSED;
				context->m_App->EnqueueMessage( context->m_Conn->GetHandle(), &msg_desc );
				context->m_Conn->Invalidate();
		
				return Net::HANDLER_HALT;
			}
		}
		else
		{
			return Net::HANDLER_MSG_DONE;
		}
	}
	
	strcpy( new_player.Name, join_msg->m_Name );
	new_player.ObjID = gamenet_man->GetNextPlayerObjectId();
	new_player.Conn = context->m_Conn;
	new_player.Flags = 0;
	new_player.Profile = join_msg->m_Profile;
	new_player.Rating = join_msg->m_Rating;
	new_player.VehicleControlType = join_msg->m_VehicleControlType;
	if( join_msg->m_Observer != 0 )
	{
		new_player.Flags |= PlayerInfo::mOBSERVER;
	}
	else if(( skate_mod->GetGameMode()->GetNameChecksum() != CRCD(0x1c471c60, "netlobby") ) &&
			( gamenet_man->GameIsOver() == false ))
	{
		new_player.Flags |= ( PlayerInfo::mPENDING_PLAYER | PlayerInfo::mOBSERVER );
	}
    
	Dbg_Printf( "====================== GOT JOIN REQUEST FROM PLAYER %d\n", new_player.ObjID );
	if( context->m_Conn->IsLocal())
	{
		// GJ:  copy the contents of the skater profile from one to the other
		// it'd be nice to just use a copy constructor, but i couldn't
		// get it to work for some reason
		uint8* pTempBuffer = new uint8[vMAX_APPEARANCE_DATA_SIZE];
		skate_mod->GetProfile( new_player.ObjID )->WriteToBuffer(pTempBuffer, vMAX_APPEARANCE_DATA_SIZE);
		new_player.mpSkaterProfile->ReadFromBuffer(pTempBuffer);
		delete pTempBuffer;

		new_player.Flags = PlayerInfo::mLOCAL_PLAYER;
		gamenet_man->DeferredNewPlayer( &new_player, 0 );
	}
	else
	{
		if( !join_msg->m_Observer )
		{
			// GJ:  the appearance is transmitted inside the message,
			// so copy it to the "new_player" structure
			new_player.mpSkaterProfile->ReadFromBuffer(join_msg->m_AppearanceData);
		}
		gamenet_man->DeferredNewPlayer( &new_player );

		// Flag this new player's connection as busy so he doesn't get game-related messages
		// in the ineterum
		context->m_Conn->ClearStatus( Net::Conn::mSTATUS_READY );
		context->m_Conn->SetStatus( Net::Conn::mSTATUS_BUSY );
	}
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Client wants to enter observer mode	  						  */
/*																  */
/******************************************************************/

int	Manager::s_handle_observe( Net::MsgHandlerContext* context )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Manager* gamenet_man;
	PlayerInfo* player;
	Obj::CSkater* skater;
		
	gamenet_man = (Manager *) context->m_Data;

	Dbg_Printf( "Got observe request\n" );

	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	if( !player || player->IsObserving())
	{
		return Net::HANDLER_MSG_DONE;
	}

	// Allow the server to go into observer mode without restriction
	if( ( player->IsLocalPlayer() == false ) &&
		( gamenet_man->GetNumObservers() >= gamenet_man->GetMaxObservers()))
	{
		Net::MsgDesc msg_desc;

		Dbg_Printf( "Not enough observer spots\n" );
		msg_desc.m_Id = MSG_ID_OBSERVE_REFUSED;
		gamenet_man->m_server->EnqueueMessage( context->m_Conn->GetHandle(), &msg_desc );
	}
	else
	{
		Net::MsgDesc msg_desc;

        // Only do this player->observer conversion here for remote players.
		// For our local player, let the observe_proceed message run its course
		if( player->IsLocalPlayer() == false )
		{
			skater = player->m_Skater;
			gamenet_man->DropPlayer( player, vREASON_OBSERVING );
			skate_mod->remove_skater( skater );
			player->m_flags.SetMask( PlayerInfo::mOBSERVER );
			player->m_Skater = NULL;
			if( player->IsServerPlayer())
			{
				player->MarkAsNotServerPlayer();
				gamenet_man->ChooseNewServerPlayer();
			}
			// If we're destroying the player we're looking at, change to the next player
			if( player == gamenet_man->m_cam_player )
			{
				gamenet_man->m_cam_player = gamenet_man->GetNextPlayerToObserve();
				if( player == gamenet_man->m_cam_player )
				{
					gamenet_man->m_cam_player = NULL;
				}
				
				gamenet_man->ObservePlayer( gamenet_man->m_cam_player );
			}
			
		}

		Dbg_Printf( "Sent Proceed to Observe\n" );
		msg_desc.m_Id = MSG_ID_OBSERVE_PROCEED;
		gamenet_man->m_server->EnqueueMessage( context->m_Conn->GetHandle(), &msg_desc );
	}
    
	return Net::HANDLER_CONTINUE;
}   

/******************************************************************/
/* Got a disconnection request. Accept it at face value for now	  */
/*																  */
/******************************************************************/

int	Manager::s_handle_disconn_request( Net::MsgHandlerContext* context )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Manager* gamenet_man;
	PlayerInfo* quitter;
		
	

    gamenet_man = (Manager *) context->m_Data;
	quitter = gamenet_man->GetPlayerByConnection( context->m_Conn );
	if( quitter )
	{
		if (!quitter->IsLocalPlayer())
		{
			Obj::CSkater* quitting_skater;
			bool observing;

			quitting_skater = quitter->m_Skater;
			observing = quitter->IsObserving();

			gamenet_man->DropPlayer( quitter, vREASON_QUIT );
			if( !observing )
			{
				skate_mod->remove_skater( quitting_skater );
			}
		}
	}
	else
	{
		Lst::Node< NewPlayerInfo > *node, *next;
		NewPlayerInfo* new_player;
			
		
	
		for( node = gamenet_man->m_new_players.GetNext(); node; node = next )
		{
			next = node->GetNext();
	
			new_player = node->GetData();
			
			// 
			if( new_player->Conn == context->m_Conn )
			{
				Dbg_Printf( "Removing %s\n", new_player->Name );
				context->m_App->TerminateConnection( new_player->Conn );
				
				delete node;
				delete new_player;
				
				// Return this value to signify that we've destroyed the message (done by
				// TerminateConnection())
				return Net::HANDLER_MSG_DESTROYED;
			}
		}
		// Pay no attention to any other pending messages from ths client
		return Net::HANDLER_HALT;
	}
    
	// Return this value to signify that we've destroyed the message (done by
	// TerminateConnection())
	return Net::HANDLER_MSG_DESTROYED;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_client_proceed( Net::MsgHandlerContext* context )
{
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	Manager* gamenet_man;
	
	
	Dbg_Printf( "(%d) Got proceed instruction from server\n", context->m_App->m_FrameCounter );

	gamenet_man = (Manager *) context->m_Data;
	gamenet_man->SetReadyToPlay(true);
	
	if( !context->m_App->IsLocal())
	{
		mlp_manager->AddLogicTask( *gamenet_man->m_client_add_new_players_task );
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_observe_proceed( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
		
	Dbg_Printf( "Entering Observer Mode\n" );	

	gamenet_man = (Manager *) context->m_Data;
	ScriptExitSurveyorMode( NULL, NULL );
	gamenet_man->EnterObserverMode(); 
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_observe_refused( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	
    gamenet_man = (Manager *) context->m_Data;
	Script::CStruct* p_structure = new Script::CStruct;

	p_structure->AddChecksum( "reason", Script::GenerateCRC( "net_reason_full_observers" ));
	p_structure->AddChecksum( "just_dialog", 0 );
	Script::RunScript( "CreateJoinRefusedDialog", p_structure );
	delete p_structure;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_join_refused( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	MsgEmbedded* msg;
    
	

	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgEmbedded *) context->m_Msg;


	switch( msg->m_SubMsgId )
	{
		case JOIN_REFUSED_ID_BANNED:
			gamenet_man->m_conn_refused_reason = Script::GenerateCRC("net_reason_banned");
			break;
		case JOIN_REFUSED_ID_FULL:
			gamenet_man->m_conn_refused_reason = Script::GenerateCRC("net_reason_full");
			break;
		case JOIN_REFUSED_ID_FULL_OBSERVERS:
			gamenet_man->m_conn_refused_reason = Script::GenerateCRC("net_reason_full_observers");
			break;
		case JOIN_REFUSED_ID_PW:
		{   
			if( gamenet_man->GetJoinState() == vJOIN_STATE_TRYING_PASSWORD )
			{
				gamenet_man->m_conn_refused_reason = Script::GenerateCRC("net_reason_wrong_password");
			}
			else
			{
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());

				Script::RunScript( "CreateEnterPasswordControl" );
				/*
				dlg->GoBack();
							
				Front::MenuFactory* menu_factory = Front::MenuFactory::Instance();
				Front::MenuElement *net_menu = menu_factory->GetMenuElement(Script::GenerateCRC("net_menu"));
				Front::MenuEvent event;
				
				event.SetTypeAndTarget( Front::MenuEvent::vLINK, Script::GenerateCRC("enter_password_control") );
				net_menu->LaunchEvent( &event );
				*/

				// Temporarily disable timeout
				gamenet_man->m_join_timeout_task->Remove();

				Mem::Manager::sHandle().PopContext();
				return Net::HANDLER_CONTINUE;
			}
			
			break;
		}
		case JOIN_REFUSED_ID_IN_PROGRESS:
		{
			Dbg_Printf( "Refused: Joining a game in progress\n" );
			
			Script::RunScript( "CreateGameInProgressDialog" );
            
			gamenet_man->m_join_timeout_task->Remove();
			return Net::HANDLER_CONTINUE;
		}
		case JOIN_REFUSED_ID_VERSION:
			gamenet_man->m_conn_refused_reason = Script::GenerateCRC("net_reason_version" );
			break;
		default:
			gamenet_man->m_conn_refused_reason = Script::GenerateCRC("net_reason_default" );
			break;
	}
	
	Dbg_Printf( "(%d) Host refused join request: Reason %d\n", 	context->m_App->m_FrameCounter,
																msg->m_SubMsgId );
	
	gamenet_man->CancelJoinServer();
	gamenet_man->SetJoinState( vJOIN_STATE_REFUSED );
	
	return Net::HANDLER_HALT;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_join_proceed( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	Net::Manager * net_man = Net::Manager::Instance();
	MsgJoinInfo msg;
    Net::MsgDesc msg_desc;
	int size;
	Script::CScriptStructure* pStructure;
	Prefs::Preferences* pPreferences;
	const char* network_id;
	MsgProceed* proceed_msg;
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	bool ignore_face_data;

	gamenet_man = (Manager *) context->m_Data;
		
	proceed_msg = (MsgProceed*) context->m_Msg;
	if( context->m_Conn->IsRemote())
	{
		// Note whether or not the server is narrowband/broadband
		if( proceed_msg->m_Broadband )
		{
			context->m_Conn->SetBandwidthType( Net::Conn::vBROADBAND );
		}
		else
		{
			context->m_Conn->SetBandwidthType( Net::Conn::vNARROWBAND );
		}

		gamenet_man->m_join_ip = proceed_msg->m_PublicIP;
		gamenet_man->m_join_private_ip = proceed_msg->m_PrivateIP;
		gamenet_man->SetJoinPort( proceed_msg->m_Port );

		Dbg_Printf( "**** Setting public IP to %s\n", inet_ntoa(*(struct in_addr *) &gamenet_man->m_join_ip ));
#ifdef __PLAT_NGPS__
		if( gamenet_man->mpBuddyMan->IsLoggedIn())
		{
			char location[1024];

			gamenet_man->mpLobbyMan->SetServerName( proceed_msg->m_ServerName );
			sprintf( location, "%d:%d:%d:%s (%s)", gamenet_man->m_join_ip, gamenet_man->m_join_private_ip, gamenet_man->GetJoinPort(), 
					 gamenet_man->mpLobbyMan->GetServerName(), gamenet_man->mpLobbyMan->GetLobbyName());
			if( gamenet_man->GetJoinMode() == vJOIN_MODE_PLAY )
			{
				gamenet_man->mpBuddyMan->SetStatusAndLocation( GP_PLAYING, (char*) Script::GetString( "homie_status_playing" ), location );
			}
			else
			{
				gamenet_man->mpBuddyMan->SetStatusAndLocation( GP_PLAYING, (char*) Script::GetString( "homie_status_observing" ), location );
			}
		}
#endif

	}
	skate_mod->GetGameMode()->SetMaximumNumberOfPlayers( proceed_msg->m_MaxPlayers );

	/*Dbg_Printf( "******* Got Join Proceed from: %s, private: %s, port: %d\n", 
				inet_ntoa(*(struct in_addr *) &gamenet_man->m_join_ip ), 
				inet_ntoa(*(struct in_addr *) &gamenet_man->m_join_private_ip ),
				gamenet_man->m_join_port );*/
	
	pPreferences = gamenet_man->GetNetworkPreferences();
	pStructure = pPreferences->GetPreference( Script::GenerateCRC("network_id") );
	pStructure->GetText( "ui_string", &network_id, true );

#ifdef __PLAT_NGPS__
	msg.m_Profile = gamenet_man->mpBuddyMan->GetProfile();
	msg.m_Rating = gamenet_man->mpStatsMan->GetStats()->GetRating();
#endif
	msg.m_Observer = ( gamenet_man->GetJoinMode() == vJOIN_MODE_OBSERVE );
	msg.m_Version = vVERSION_NUMBER;
	msg.m_WillingToWait = 1;
	if(	net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM )
	{
		ignore_face_data = true;
		msg.m_Broadband = 0;
	}
	else
	{
		ignore_face_data = false;
		msg.m_Broadband = 1;
	}
			
	strcpy( msg.m_Name, network_id );
	msg.m_Password[0] = '\0';
	size = 0;
	if( !msg.m_Observer )
	{
		// GJ:  transmit the way you look (slot 0 of the
		// skater profile manager) to the server
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		Obj::CSkaterProfile* pSkaterProfile = skate_mod->GetProfile(0);
		Net::Conn *server_conn;
		Lst::Search< Net::Conn > sh;
	
		server_conn = context->m_App->FirstConnection( &sh );
		if( server_conn->GetBandwidthType() == Net::Conn::vNARROWBAND )
		{
			ignore_face_data = true;
		}

		size = pSkaterProfile->WriteToBuffer(msg.m_AppearanceData, vMAX_APPEARANCE_DATA_SIZE,
												ignore_face_data );
		Dbg_Assert( size < vMAX_APPEARANCE_DATA_SIZE );
		Dbg_Printf("\n\n******************* MsgJoinInfo (%d) appearance data size = %d %d Broadband %d\n", MSG_ID_JOIN_REQ,
				size, sizeof(MsgJoinInfo) - vMAX_APPEARANCE_DATA_SIZE + size, ignore_face_data );
	}

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof(MsgJoinInfo) - vMAX_APPEARANCE_DATA_SIZE + size;
	msg_desc.m_Id = MSG_ID_JOIN_REQ;
	if( context->m_Conn->IsRemote())
	{
		msg_desc.m_Queue = Net::QUEUE_IMPORTANT;
	}

	context->m_App->EnqueueMessageToServer( &msg_desc );
	
	/*context->m_App->StreamMessageToServer( GameNet::MSG_ID_JOIN_REQ, msg_desc.m_Length, 
					   msg_desc.m_Data, "appearance data", vSEQ_GROUP_PLAYER_MSGS, false );*/

	gamenet_man->SetJoinState( vJOIN_STATE_JOINING );

	return Net::HANDLER_MSG_DONE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_join_accepted( Net::MsgHandlerContext* context )
{
	Dbg_Message( "(%d) Got Join Accepted!\n", context->m_App->m_FrameCounter );

	// Consider all of our messages up to this point to have been received
	//context->m_Conn->AckAllMessages();
	//context->m_Conn->DestroyMessageQueues();
	if( context->m_Conn->IsRemote())
	{
		context->m_Conn->DestroyImportantMessageQueues();
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_new_king( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	MsgByteInfo* msg;
	PlayerInfo* player;

	msg = (MsgByteInfo*) context->m_Msg;

	gamenet_man = (Manager *) context->m_Data;
	
	player = gamenet_man->GetKingOfTheHill();
	if( player )
	{
		player->MarkAsKing( false );
	}
	else
	{
		NewPlayerInfo* new_player;
		Lst::Search< NewPlayerInfo > sh;

		// Maybe we just haven't officially added the previous king. Search our list of
		// queued up new players
		for( new_player = gamenet_man->FirstNewPlayerInfo( sh ); new_player;
				new_player = gamenet_man->NextNewPlayerInfo( sh ))
		{
			if( new_player->Flags & PlayerInfo::mKING_OF_THE_HILL )
			{
				new_player->Flags &= ~PlayerInfo::mKING_OF_THE_HILL;
				break;
			}
		}
	}

	player = gamenet_man->GetPlayerByObjectID( msg->m_Data );
	if( player )
	{
		player->MarkAsKing( true );
	}
	else
	{
		NewPlayerInfo* new_player;
		Lst::Search< NewPlayerInfo > sh;

		// Maybe we just haven't officially added this new king player. Search our list of
		// queued up new players
		for( new_player = gamenet_man->FirstNewPlayerInfo( sh ); new_player;
				new_player = gamenet_man->NextNewPlayerInfo( sh ))
		{
			if( new_player->ObjID == msg->m_Data )
			{
				new_player->Flags |= PlayerInfo::mKING_OF_THE_HILL;
				break;
			}
		}
	}
    
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_stole_flag( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	MsgFlagMsg* msg;
	PlayerInfo* player;

	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgFlagMsg*) context->m_Msg;

	player = gamenet_man->GetPlayerByObjectID( msg->m_ObjId );
	if( player )
	{
		player->StoleFlag( msg->m_Team );
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_took_flag( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	MsgFlagMsg* msg;
	PlayerInfo* player;

	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgFlagMsg*) context->m_Msg;

	player = gamenet_man->GetPlayerByObjectID( msg->m_ObjId );
	if( player )
	{
		player->TookFlag( msg->m_Team );
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_captured_flag( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	MsgFlagMsg* msg;
	PlayerInfo* player;

	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgFlagMsg*) context->m_Msg;

	player = gamenet_man->GetPlayerByObjectID( msg->m_ObjId );
	if( player )
	{
		player->CapturedFlag( msg->m_Team );
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_retrieved_flag( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	MsgFlagMsg* msg;
	PlayerInfo* player;

	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgFlagMsg*) context->m_Msg;

	player = gamenet_man->GetPlayerByObjectID( msg->m_ObjId );
	if( player )
	{
		player->RetrievedFlag();
	}
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* A player has restarted in koth mode. If it is the king, send	  */
/* out a message signifying that the king has lost his crown	  */
/******************************************************************/

int	Manager::s_handle_player_restarted( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	PlayerInfo* player;
	Mdl::Skate * skate_mod;
	 
	skate_mod =  Mdl::Skate::Instance();
	gamenet_man = (Manager *) context->m_Data;
	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	if( player )
	{
		player->m_flags.ClearMask( PlayerInfo::mRESTARTING );
	}

	if(	skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netctf" ))
	{
		if( player && player->HasCTFFlag())
		{
			Lst::Search< PlayerInfo > sh;
			PlayerInfo* send_player, *local_player;
			char team_str[64];
			int team;
			Script::CStruct* pParams;
			MsgFlagMsg msg;
			Net::MsgDesc msg_desc;
			
			team = player->HasWhichFlag();
			sprintf( team_str, "team_%d_name", team + 1 );
			pParams = new Script::CStruct;
	
			pParams->AddInteger( "team", team );
			pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
			Script::RunScript( "flag_returned", pParams );
	
			delete pParams;
			msg.m_Team = team;
			msg.m_ObjId = player->m_Skater->GetID();

			// If it's me, remove the message that says "return the flag to your base"
			if( player->IsLocalPlayer())
			{
				Script::RunScript( "destroy_ctf_panel_message" );
			}
			local_player = gamenet_man->GetLocalPlayer();
			if( local_player && !local_player->IsObserving())
			{
				if( local_player->m_Team == team )
				{
					Script::RunScript( "hide_ctf_arrow" );
				}
			}

			player->ClearCTFState();

			msg_desc.m_Data = &msg;
			msg_desc.m_Length = sizeof( MsgFlagMsg );
			msg_desc.m_Id = MSG_ID_PLAYER_DROPPED_FLAG;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
			for( send_player = gamenet_man->FirstPlayerInfo( sh, true ); send_player; 
					send_player = gamenet_man->NextPlayerInfo( sh, true ))
			{
				if( send_player->IsLocalPlayer())
				{
					continue;
				}
				context->m_App->EnqueueMessage( send_player->GetConnHandle(), &msg_desc );
			}
		}
	}
	else if(( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netking" )) ||
			( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "king" )))
	{
		if( player && player->IsKing())
		{
			Lst::Search< PlayerInfo > sh;
			Net::MsgDesc msg_desc;
			char point;
	
			player->MarkAsKing( false );
			gamenet_man->m_crown_spawn_point = Mth::Rnd( vNUM_CROWN_SPAWN_POINTS );
            point = (char) gamenet_man->m_crown_spawn_point;
			
			msg_desc.m_Data = &point;
			msg_desc.m_Length = sizeof( char );
			msg_desc.m_Id = MSG_ID_KING_DROPPED_CROWN;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
			for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; 
					player = gamenet_man->NextPlayerInfo( sh, true ))
			{
				context->m_App->EnqueueMessage( player->GetConnHandle(), &msg_desc );
			}
		}
	}
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* The server is notifying the client that he's in an auto-serving*/
/* server									  					  */
/******************************************************************/

int	Manager::s_handle_auto_server_notification( Net::MsgHandlerContext* context )
{
	Script::RunScript( "launch_auto_server_notification" );
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* The server is notifying the client that he is the new		  */
/* operating server	in this fcfs server		  					  */
/******************************************************************/

int	Manager::s_handle_fcfs_assignment( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	PlayerInfo* player;
	uint32 *checksum;
	Script::CStruct* pParams;
	
	gamenet_man = (Manager *) context->m_Data;

	player = gamenet_man->GetLocalPlayer();
	Dbg_Assert( player );

	checksum = (uint32*) context->m_Msg;
	player->MarkAsServerPlayer();
	pParams = new Script::CStruct;
	pParams->AddChecksum( "checksum", *checksum );
	Script::RunScript( "launch_fcfs_notification", pParams );
	delete pParams;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* The fcfs client is requesting to perform a server operation	  */
/* 								  								  */
/******************************************************************/

int	Manager::s_handle_fcfs_request( Net::MsgHandlerContext* context )
{
	PlayerInfo* player;
	Manager* gamenet_man;

	gamenet_man = (Manager *) context->m_Data;

	Dbg_Printf( "GOT FCFS REQUEST\n" );
	// First, ensure we even have an fcfs server
	player = gamenet_man->GetServerPlayer();
	if( player == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}

	// Next, make sure the requesting client matches the fcfs client
	if( player->m_Conn != context->m_Conn )
	{
		return Net::HANDLER_MSG_DONE;
	}

	Dbg_Printf( "GOT FCFS REQUEST 2\n" );
	switch( context->m_MsgId )
	{
		case MSG_ID_FCFS_START_GAME:
		{
			MsgStartGameRequest* msg;
			Prefs::Preferences* pPreferences;
			Script::CStruct* pTempStructure;
			const char* ui_string;
			int score, time, fireball_level;
	
			msg = (MsgStartGameRequest*) context->m_Msg;
			pPreferences = gamenet_man->GetNetworkPreferences();

			// First, apply the options chosen by the fcfs
			pTempStructure = new Script::CStruct;
			ui_string = gamenet_man->GetNameFromArrayEntry( "net_game_type_info", msg->m_GameMode );
			Dbg_Printf( "Got Game Mode UI String of %s\n", ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, msg->m_GameMode );
			pPreferences->SetPreference( Script::GenerateCRC( "game_type" ), pTempStructure );
			delete pTempStructure;

			pTempStructure = new Script::CStruct;
			ui_string = gamenet_man->GetNameFromArrayEntry( "skill_level_info", msg->m_SkillLevel );
			Dbg_Printf( "Got Skill Level UI String of %s\n", ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, msg->m_SkillLevel );
			pPreferences->SetPreference( Script::GenerateCRC( "skill_level" ), pTempStructure );
			delete pTempStructure;

			pTempStructure = new Script::CStruct;
			ui_string = gamenet_man->GetNameFromArrayEntry( "fireball_level_info", msg->m_FireballLevel );
			fireball_level = gamenet_man->GetIntFromArrayEntry( "fireball_level_info", msg->m_FireballLevel, 
																CRCD(0xce87e4e3,"fireball_level") );
			Dbg_Printf( "Got Fireball Level UI String of %s\n", ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, msg->m_FireballLevel );
			pPreferences->SetPreference( Script::GenerateCRC( "fireball_difficulty" ), pTempStructure );
			delete pTempStructure;

			pTempStructure = new Script::CStruct;
			ui_string = gamenet_man->GetNameFromArrayEntry( "on_off_types", msg->m_PlayerCollision );
			Dbg_Printf( "Got Player Collision UI String of %s\n", ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, msg->m_PlayerCollision );
			pPreferences->SetPreference( Script::GenerateCRC( "player_collision" ), pTempStructure );
			delete pTempStructure;

			pTempStructure = new Script::CStruct;
			ui_string = gamenet_man->GetNameFromArrayEntry( "on_off_types", msg->m_FriendlyFire );
			Dbg_Printf( "Got Friendly Fire UI String of %s\n", ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, msg->m_FriendlyFire );
			pPreferences->SetPreference( Script::GenerateCRC( "friendly_fire" ), pTempStructure );
			delete pTempStructure;

			pTempStructure = new Script::CStruct;
			ui_string = gamenet_man->GetNameFromArrayEntry( "boolean_types", msg->m_StopAtZero );
			Dbg_Printf( "Got Stop At Zero UI String of %s\n", ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, msg->m_StopAtZero );
			pPreferences->SetPreference( Script::GenerateCRC( "stop_at_zero" ), pTempStructure );
			delete pTempStructure;

			pTempStructure = new Script::CStruct;
			ui_string = gamenet_man->GetNameFromArrayEntry( "ctf_type", msg->m_CTFType );
			Dbg_Printf( "Got CTF Type UI String of %s\n", ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, msg->m_CTFType );
			pPreferences->SetPreference( Script::GenerateCRC( "ctf_game_type" ), pTempStructure );
			delete pTempStructure;

            pTempStructure = new Script::CStruct;
			ui_string = gamenet_man->GetNameFromArrayEntry( "time_limit_options", msg->m_TimeLimit );
			time = gamenet_man->GetIntFromArrayEntry( "time_limit_options", msg->m_TimeLimit, CRCD( 0x906b67ba, "time" ) );
			Dbg_Printf( "Got Time Limit UI String of %s\n", ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, msg->m_TimeLimit );
			pTempStructure->AddComponent( Script::GenerateCRC("time"), ESYMBOLTYPE_INTEGER, time );
			pPreferences->SetPreference( Script::GenerateCRC( "time_limit" ), pTempStructure );
			delete pTempStructure;

			pTempStructure = new Script::CStruct;

			score = 0;
			ui_string = gamenet_man->GetNameFromArrayEntry( "target_score_options", msg->m_TargetScore );
			score = gamenet_man->GetIntFromArrayEntry( "target_score_options", msg->m_TargetScore, CRCD( 0xcd66c8ae, "score" ));
			// Our "target score" comes from either the target score options array or the time limit options array, depending on
			// whether the game type is score challenge or koth
			if( ui_string == NULL )
			{
				ui_string = gamenet_man->GetNameFromArrayEntry( "capture_options", msg->m_TargetScore );
				score = gamenet_man->GetIntFromArrayEntry( "capture_options", msg->m_TargetScore, CRCD( 0xcd66c8ae, "score" ));
				if( ui_string == NULL )
				{
					ui_string = gamenet_man->GetNameFromArrayEntry( "time_limit_options", msg->m_TargetScore );
					score = gamenet_man->GetIntFromArrayEntry( "time_limit_options", msg->m_TargetScore, CRCD( 0x906b67ba, "time" ));
					score *= 1000;
				}
			}
			Dbg_Printf( "Got Target Score %08x UI String of %s, score %d\n", msg->m_TargetScore, ui_string, score );
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, ui_string );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, msg->m_TargetScore );
			if( score > 0 )
			{
				pTempStructure->AddComponent( Script::GenerateCRC("score"), ESYMBOLTYPE_INTEGER, score );
			}
			pPreferences->SetPreference( Script::GenerateCRC( "target_score" ), pTempStructure );

			delete pTempStructure;

			Script::RunScript( "chosen_start_game" );
			break;
		}
		case MSG_ID_FCFS_BAN_PLAYER:
		{
			MsgRemovePlayerRequest* msg;
			PlayerInfo* player, *target_player;
			Lst::Search< PlayerInfo > sh;
			int i;

			msg = (MsgRemovePlayerRequest*) context->m_Msg;

			i = 0;
			target_player = NULL;
			for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
			{
				if( msg->m_Index == i )
				{
					if( stricmp( msg->m_Name, player->m_Name ) == 0 )
					{
						target_player = player;
					}
					break;
				}
				i++;
			}

			if( target_player == NULL )
			{
				return Net::HANDLER_MSG_DONE;
			}

			if( msg->m_Ban == 1 )
			{
				Obj::CSkater* quitting_skater;
				bool observing;
	
				quitting_skater = target_player->m_Skater;
				observing = target_player->IsObserving();
				gamenet_man->DropPlayer( target_player, vREASON_BANNED );
				if( !observing )
				{
					Mdl::Skate * skate_mod = Mdl::Skate::Instance();
					skate_mod->remove_skater( quitting_skater );
				}
			}
			else
			{
				Obj::CSkater* quitting_skater;
				bool observing;
	
				quitting_skater = target_player->m_Skater;
				observing = target_player->IsObserving();
				gamenet_man->DropPlayer( target_player, vREASON_KICKED );
				if( !observing )
				{
					Mdl::Skate * skate_mod = Mdl::Skate::Instance();
					skate_mod->remove_skater( quitting_skater );
				}
			}
			break;
		}
		case MSG_ID_FCFS_CHANGE_LEVEL:
		{
			MsgIntInfo* msg;
			Script::CStruct* pStructure;

			msg = (MsgIntInfo *) context->m_Msg;
			pStructure = new Script::CStruct;
	
			pStructure->AddComponent( Script::GenerateCRC( "level" ), ESYMBOLTYPE_NAME, (uint32) msg->m_Data );
			pStructure->AddChecksum( "from_fcfs", Script::GenerateCRC( "from_fcfs" ));
			pStructure->AddChecksum( "show_warning", Script::GenerateCRC( "show_warning" ));

			Script::RunScript( "change_level", pStructure );

			delete pStructure;
			break;
		}
		case MSG_ID_FCFS_TOGGLE_PROSET:
		{
			MsgToggleProSet* msg;

			msg = (MsgToggleProSet *) context->m_Msg;
			gamenet_man->ToggleProSet( msg->m_Bit, msg->m_ParamId );
			Script::RunScript( "toggle_proset_flag", Script::GetStructure( msg->m_ParamId, Script::ASSERT ));
			Script::RunScript( "toggle_geo_nomenu", Script::GetStructure( msg->m_ParamId, Script::ASSERT ));
			break;
		}
		case MSG_ID_FCFS_TOGGLE_GOAL_SELECTION:
		{
			Game::CGoalManager* pGoalManager = Game::GetGoalManager();
			MsgIntInfo* msg;
			
			msg = (MsgIntInfo*) context->m_Msg;

			pGoalManager->ToggleGoalSelection( msg->m_Data );
			break;
		}
		case MSG_ID_FCFS_END_GAME:
		{
			Net::Server* server;
			Lst::Search< PlayerInfo > sh;
			Net::MsgDesc msg_desc;

			server = gamenet_man->GetServer();

			Script::RunScript( "fcfc_end_game_selected" );
	
			msg_desc.m_Id = MSG_ID_END_GAME;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
			for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
			{
				if( player->IsLocalPlayer())
				{
					continue;
				}
		
				server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
			}
			break;
		}

		case MSG_ID_FCFS_SET_NUM_TEAMS:
		{
			Prefs::Preferences* prefs;
			Script::CStruct* pParams, *pPrefStruct;
			MsgByteInfo* msg;
            
			msg = (MsgByteInfo*) context->m_Msg;
			Dbg_Printf( "GOT FCFS REQUEST : NUM TEAMS %d\n", msg->m_Data );
			pParams = new Script::CStruct;
			pParams->AddInteger( NONAME, msg->m_Data );

			pPrefStruct = new Script::CScriptStructure;
			switch( msg->m_Data )
			{
				case 0:
					pPrefStruct->AddString( "ui_string", "None" );
					pPrefStruct->AddChecksum( "checksum", Script::GenerateCRC( "teams_none" ));
					break;
				case 2:
					pPrefStruct->AddString( "ui_string", "2" );
					pPrefStruct->AddChecksum( "checksum", Script::GenerateCRC( "teams_two" ));
					break;
				case 3:
					pPrefStruct->AddString( "ui_string", "3" );
					pPrefStruct->AddChecksum( "checksum", Script::GenerateCRC( "teams_three" ));
					break;
				case 4:
					pPrefStruct->AddString( "ui_string", "4" );
					pPrefStruct->AddChecksum( "checksum", Script::GenerateCRC( "teams_four" ));
					break;
				default:
					Dbg_Assert( 0 );
					break;
			}
			
			prefs = gamenet_man->GetNetworkPreferences();
			prefs->SetPreference( Script::GenerateCRC("team_mode"), pPrefStruct );
			delete pPrefStruct;

			ScriptSetNumTeams( pParams, NULL );

			delete pParams;
			break;
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* The king has lost his crown. Spawn a new one in the world at   */
/* the spawn point specified									  */
/******************************************************************/

int	Manager::s_handle_dropped_crown( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	PlayerInfo* king, *other_player;
	char spawn_pt;
	Obj::CCrown* crown;
	
	gamenet_man = (Manager *) context->m_Data;
	king = gamenet_man->GetKingOfTheHill();
	memcpy( &spawn_pt, context->m_Msg, sizeof( char ));
	if( king )
	{
		// He is no longer king
		king->MarkAsKing( false );
	}
	
	if( king && king->IsLocalPlayer())
	{
		gamenet_man->CreateNetPanelMessage( false, Script::GenerateCRC("net_message_dropped_crown_you"),
											NULL, NULL, king->m_Skater );
	}
	else
	{   
		if( gamenet_man->InNetGame())
		{
			gamenet_man->CreateNetPanelMessage( false, Script::GenerateCRC("net_message_dropped_crown_other"));
		}
		else if( king && king->m_Skater )
		{   
			int other_id;

			// NOTE: This code only works for 2-player splitscreen. If we ever go to four,
			// it needs to be more sophisticated
			if( king->m_Skater->GetID() == 0 )
			{
				other_id = 1;
			}
			else
			{ 
				other_id = 0;
			}

			other_player = gamenet_man->GetPlayerByObjectID( other_id );
			if( other_player )
			{
				gamenet_man->CreateNetPanelMessage( false, Script::GenerateCRC("net_message_dropped_crown_other"),
													NULL, NULL, other_player->m_Skater );
			}
		}
	}

	crown = gamenet_man->GetCrown();
	if( crown )
	{
#if 1
		int loop_count = 0;
		bool node_found = false;
		
		// KLUDGE: keep looping through incase spawn_pt is larger than the number of crowns in the node array
		while (!node_found)
#endif
		{
#if 1
			// KLUDGE: make sure we exit this loop at some point
			loop_count++;
			if (loop_count == 50) break;
#endif

			int i;
			
			Script::CArray *pNodeArray=Script::GetArray("NodeArray");
	
			// Make sure there's a node array
			Dbg_Assert( pNodeArray != NULL );
							 
			i = 0;
			// scan through it for a crown spawn point
			while( i < (int)pNodeArray->GetSize() )
			{
				uint32	TypeChecksum;
				Mth::Vector pos;
				Script::CScriptStructure *pNode=pNodeArray->GetStructure(i);
				
				TypeChecksum = 0;
				pNode->GetChecksum("Type",&TypeChecksum);
				if( TypeChecksum == 0xaf86421b )	// checksum of "Crown"
				{
					// We want the Nth crown spawn pt
					if( spawn_pt == 0 )
					{
						SkateScript::GetPosition( pNode, &pos );
						crown->SetPosition( pos );
#if 1
						node_found = true;
#endif
						break;
					}
					spawn_pt--;
				}
				i++;
			}
		}

		crown->RemoveFromKing();
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* The player has dropped a ctf flag.  Relocate it.				  */
/*																  */
/******************************************************************/

int	Manager::s_handle_dropped_flag( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	MsgFlagMsg* msg;
	PlayerInfo* player, *local_player;

	gamenet_man = (Manager*) context->m_Data;
	msg = (MsgFlagMsg*) context->m_Msg;
	player = gamenet_man->GetPlayerByObjectID( msg->m_ObjId );
	if( player && player->HasCTFFlag())
	{
		char team_str[64];
		int team;
		Script::CStruct* pParams;
		
		team = player->HasWhichFlag();
		sprintf( team_str, "team_%d_name", team + 1 );
		pParams = new Script::CStruct;

		pParams->AddInteger( "team", team );
		pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
		Script::RunScript( "flag_returned", pParams );

		delete pParams;

		// If it's me, remove the message that says "return the flag to your base"
		if( player->IsLocalPlayer())
		{
			Script::RunScript( "destroy_ctf_panel_message" );
		}

		local_player = gamenet_man->GetLocalPlayer();
		if( local_player && !local_player->IsObserving())
		{
			if( local_player->m_Team == team )
			{
				Script::RunScript( "hide_ctf_arrow" );
			}
		}

		player->ClearCTFState();
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Streams the level data to the client    						  */
/*                                                 				  */
/******************************************************************/

int	Manager::s_handle_request_level( Net::MsgHandlerContext* context )
{
	Net::Server* server;
	MsgRequestLevel* msg;
	Manager* gamenet_man;
	PlayerInfo* player;

	gamenet_man = (Manager*) context->m_Data;
	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	msg = (MsgRequestLevel*) context->m_Msg;
		 
	server = (Net::Server*) context->m_App;
	Dbg_Printf( "************** STREAMING LEVEL DATA ***************\n" );
	
	server->StreamMessage( context->m_Conn->GetHandle(), MSG_ID_LEVEL_DATA, Ed::CParkManager::COMPRESSED_MAP_SIZE, 
						   Ed::CParkManager::sInstance()->GetCompressedMapBuffer(), "level data", vSEQ_GROUP_PLAYER_MSGS,
						   false, true );
						   
	server->StreamMessage( context->m_Conn->GetHandle(), MSG_ID_RAIL_DATA, Obj::GetRailEditor()->GetCompressedRailsBufferSize(), 
						   Obj::GetRailEditor()->GetCompressedRailsBuffer(), "rail data", vSEQ_GROUP_PLAYER_MSGS,
						   false, true );
						   
						   
	if( gamenet_man->UsingCreatedGoals())
	{
		gamenet_man->LoadGoals( msg->m_LevelId );
		server->StreamMessage( context->m_Conn->GetHandle(), MSG_ID_GOALS_DATA, gamenet_man->GetGoalsDataSize(),
							   gamenet_man->GetGoalsData(), "goals data", vSEQ_GROUP_PLAYER_MSGS, false, true );
	}

	if( msg->m_Source == MSG_ID_CHANGE_LEVEL )
	{
		GameNet::MsgReady ready_msg;
		MsgChangeLevel change_msg;
		Net::MsgDesc msg_desc;

		Dbg_Printf( "************** SENDING CHANGE LEVEL DATA ***************\n" );
			
		change_msg.m_Level = msg->m_LevelId;
		change_msg.m_ShowWarning = 0;
			
		ready_msg.m_Time = Tmr::GetTime();

		msg_desc.m_Data = &change_msg;
		msg_desc.m_Length = sizeof(MsgChangeLevel);
		msg_desc.m_Id = MSG_ID_CHANGE_LEVEL;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
		server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
	
		// Don't send them any non-important messages until they're finished loading
		msg_desc.m_Data = &ready_msg;
		msg_desc.m_Length = sizeof(MsgReady);
		msg_desc.m_Id = MSG_ID_READY_QUERY;
		server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
					
		player->MarkAsNotReady( ready_msg.m_Time );
		
		server->SendData();		// Mick, (true) because we want to send it immediatly
		
#ifdef __PLAT_NGPS__
		//server->WaitForAsyncCallsToFinish();
#endif
	} 

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Handles level data from server    						  	  */
/*                                                 				  */
/******************************************************************/

int	Manager::s_handle_level_data( Net::MsgHandlerContext* context )
{
    Dbg_Printf( "received streamed message! size %d\n", (int)context->m_MsgLength ); 
	
	Ed::CParkManager::sInstance()->SetCompressedMapBuffer((uint8*) context->m_Msg, true );

	uint32 crc;
	crc = Crc::GenerateCRCCaseSensitive((char*) Ed::CParkManager::sInstance()->GetCompressedMapBuffer(), Ed::CParkManager::COMPRESSED_MAP_SIZE );
	Dbg_Printf( "******************** CHECKSUM OF MAP : %08x\n", crc );
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Handles rail data from server    						  	  */
/*                                                 				  */
/******************************************************************/

int	Manager::s_handle_rail_data( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man = (Manager*) context->m_Data;
    
	Dbg_Printf( "received streamed message! size %d\n", (int)context->m_MsgLength ); 

	if( context->m_PacketFlags & Net::mHANDLE_CRC_MISMATCH )
	{
		Mdl::Skate* skate_mod = Mdl::Skate::Instance();
		Net::MsgDesc msg_desc;

		if( skate_mod->m_cur_level == CRCD(0x9f2bafb7,"Load_Skateshop"))
		{
			Script::CStruct* params;

			params = new Script::CStruct;
			params->AddChecksum( NONAME, CRCD(0x19eca78e,"show_timeout"));

			// Set it back to the joining state so that cancel_join_server does not
			// opt out early
			gamenet_man->m_join_state_task->Remove();
			gamenet_man->SetJoinState( vJOIN_STATE_JOINING );

			Dbg_Printf( "************** received bad rails data!! Cancelling join.\n" );
			Script::RunScript( CRCD(0x60b653db,"cancel_join_server" ), params );
			delete params;

			return Net::HANDLER_MSG_DESTROYED;
		}
		else
		{
			Dbg_Printf( "************** received bad rails data!! Re-requesting it.\n" );
			Dbg_Printf( "*** Level was 0x%x : %s\n", skate_mod->m_cur_level, Script::FindChecksumName(skate_mod->m_cur_level));

			msg_desc.m_Id = MSG_ID_REQUEST_RAILS_DATA;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	
			context->m_App->EnqueueMessageToServer( &msg_desc );

			return Net::HANDLER_MSG_DONE;
		}
	}

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	
	Obj::GetRailEditor()->SetCompressedRailsBuffer((uint8*) context->m_Msg);
    
    // InitUsingCompressedRailsBuffer will do an initial cleanup of any existing rails, which may result in
    // a call to UpdateSuperSectors (due to sectors being deleted).  
    // Trouble is, UpdateSuperSectors will cause the park to disappear from under the skater, so disable
    // it temporarily.
   
	Obj::CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors=false;   
    Obj::GetRailEditor()->InitUsingCompressedRailsBuffer();
	Obj::CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors=true;
		
	Mem::Manager::sHandle().PopContext();

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Handles goals data from server    						  	  */
/*                                                 				  */
/******************************************************************/

int Manager::s_handle_goals_data( Net::MsgHandlerContext* context )
{
	uint32 level;
	uint8* goals_data;
	Manager* gamenet_man;
	Script::CStruct* params;
	Net::MsgDesc msg_desc;
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();

	gamenet_man = (Manager *) context->m_Data;

	Dbg_Printf( "received goals data\n" );
	if( context->m_PacketFlags & Net::mHANDLE_CRC_MISMATCH )
	{
		if( skate_mod->m_cur_level == CRCD(0x9f2bafb7,"Load_Skateshop"))
		{
			Script::CStruct* params;
			Dbg_Printf( "************** received bad goals data!! Cancelling join server.\n" );

			// Set it back to the joining state so that cancel_join_server does not
			// opt out early
			gamenet_man->m_join_state_task->Remove();
			gamenet_man->SetJoinState( vJOIN_STATE_JOINING );

			params = new Script::CStruct;
			params->AddChecksum( NONAME, CRCD(0x19eca78e,"show_timeout"));
			Script::RunScript( CRCD(0x60b653db,"cancel_join_server" ), params );
			delete params;
			return Net::HANDLER_MSG_DESTROYED;
		}
		else
		{
			Dbg_Printf( "************** received bad goals data!! Re-requesting it.\n" );
			
			msg_desc.m_Id = MSG_ID_REQUEST_GOALS_DATA;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	
			context->m_App->EnqueueMessageToServer( &msg_desc );
			return Net::HANDLER_MSG_DONE;
		}
	}
	
	memcpy( &level, context->m_Msg, sizeof( uint32 ));

	//Dbg_Printf( "*** Level: 0x%x  requested level: 0x%x\n", level, Mdl::Skate::Instance()->m_requested_level );
	//skate_mod->m_requested_level = level;
	//skate_mod->m_cur_level = level;

	goals_data = (uint8*) ( context->m_Msg + sizeof( uint32 ));
	Obj::GetGoalEditor()->ReadFromBuffer( level, goals_data );

	

	params = new Script::CStruct;
	params->AddChecksum( NONAME, CRCD(0x88001327,"DoNotCreateGoalPeds"));
	Script::RunScript( CRCD(0x6f4180d0,"InitialiseCreatedGoals"), params );
	delete params;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Handles any sort of change of level. Makes sure we actually    */
/* Have that level                                                */
/******************************************************************/

int	Manager::s_handle_new_level( Net::MsgHandlerContext* context )
{
	Dbg_Printf( "***************** Handling new level!!!\n" );
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_change_level( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	MsgChangeLevel* pMsg;

	gamenet_man = (Manager *) context->m_Data;
	
	pMsg = (MsgChangeLevel*) context->m_Msg;
	
	Dbg_Printf( "********** GameNet:: Got change level\n" );
	gamenet_man->ClearTriggerEventList();
	gamenet_man->m_game_over = false;
	
	if( pMsg->m_ShowWarning )
	{
		Dbg_Printf( "******** GameNet:: IN NET GAME!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" );
		Mlp::Manager * mlp_man = Mlp::Manager::Instance();
				
		// Don't allow two quick change level commands
		if( gamenet_man->m_change_level_task->InList())
		{
			if( !gamenet_man->OnServer())
			{
				gamenet_man->m_level_id = pMsg->m_Level;
			}
			
			return Net::HANDLER_MSG_DONE;
		}

		gamenet_man->m_level_id = pMsg->m_Level;
		s_time_change_level = Tmr::GetTime() + vTIME_BEFORE_CHANGING_LEVEL;

		if( gamenet_man->OnServer())
		{                                    
			Script::CScriptStructure* pTempStructure;
			Prefs::Preferences* pPreferences;
			
			pTempStructure = new Script::CScriptStructure;
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, 
										  gamenet_man->GetLevelName( false ));
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, (int) gamenet_man->m_level_id );
			pPreferences = gamenet_man->GetNetworkPreferences();
			pPreferences->SetPreference( Script::GenerateCRC( "level"), pTempStructure );
			delete pTempStructure;

			// Allow a little extra time for the transmission of level data
			if( gamenet_man->m_level_id == CRCD(0xb664035d,"Load_Sk5Ed_gameplay"))
			{
				s_time_change_level += Tmr::Seconds( 1 );
			}
		}
			
		mlp_man->AddLogicTask( *gamenet_man->m_change_level_task );

		gamenet_man->CreateNetPanelMessage( false, Script::GenerateCRC("net_message_changing_levels"),
											gamenet_man->GetLevelName(), NULL, NULL, NULL, false, Tmr::Seconds( 5 ));
		
		Script::RunScript("hide_console_window");
		// Don't let this message pass through the Skate. We don't want to change levels just yet
		return Net::HANDLER_MSG_DONE;
	}
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_kill_flags( Net::MsgHandlerContext* context )
{
	Script::RunScript( "Kill_Team_Flags" );
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_game_info( Net::MsgHandlerContext* context )
{   
    Manager* gamenet_man;
	MsgGameInfo* msg;
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Script::CArray* mode_array;
	int i;
    
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

	msg = (MsgGameInfo*) context->m_Msg;
	gamenet_man = (Manager *) context->m_Data;

	Dbg_Printf( "Got game info\n" );

	gamenet_man->SetSkaterStartingPoints( msg->m_StartPoints );
	gamenet_man->m_crown_spawn_point = msg->m_CrownSpawnPoint;
	gamenet_man->m_waiting_for_game_to_start = false;

	// Invalidate old positions. Player will be re-added to the world when we get a
	// sufficient number of object updates
	for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
	{
		if( player->IsLocalPlayer() == false )
		{
			player->m_Skater->RemoveFromCurrentWorld();
			player->m_Skater->Resync();
		}
	}

	// set up the game mode for the client cause we're now in the lobby
	// anything that's lobby-dependent should go after this line
	skate_mod->GetGameMode()->LoadGameType( msg->m_GameMode );
	
	// Remove all currently-running goals
	skate_mod->GetGoalManager()->DeactivateAllGoals();	

	if( gamenet_man->InNetGame())
	{
		mode_array = Script::GetArray( "net_game_type_info" );
	}
	else
	{
		mode_array = Script::GetArray( "mp_game_type_info" );
	}
	
	Dbg_Assert( mode_array );

	{
		Script::CScriptStructure* pTempStructure = new Script::CScriptStructure;

		// In king of the hill, interpret time limit as a target time
		if(	( msg->m_TimeLimit == 0 ) &&
			( msg->m_GameMode != Script::GenerateCRC( "netgoalattack" )))
		{
			pTempStructure->AddComponent( Script::GenerateCRC("default_time_limit"), ESYMBOLTYPE_INTEGER, (int) 0 );
			if( ( msg->m_GameMode != CRCD(0x3d6d444f,"firefight")) &&
				( msg->m_GameMode != CRCD(0xbff33600,"netfirefight")))
			{
				Script::CArray* pArray = new Script::CArray;
				Script::CopyArray(pArray,Script::GetArray("targetScoreArray") );
				Script::CScriptStructure* pSubStruct = pArray->GetStructure(0);
				Dbg_Assert(pSubStruct);
				pSubStruct->AddComponent(Script::GenerateCRC("score"),ESYMBOLTYPE_INTEGER, msg->m_TargetScore );
				
				pTempStructure->AddComponent( Script::GenerateCRC("victory_conditions"), pArray );
			}
		}
		else
		{
			pTempStructure->AddComponent( Script::GenerateCRC("default_time_limit"), ESYMBOLTYPE_INTEGER, (int) msg->m_TimeLimit );
			
			if( msg->m_GameMode == Script::GenerateCRC( "netctf" ))
			{
				Script::CArray* pArray = new Script::CArray;
				Script::CopyArray(pArray,Script::GetArray("highestScoreArray") );
				pTempStructure->AddComponent( Script::GenerateCRC("victory_conditions"), pArray );
			}
		}

		Dbg_Printf( "***** GOT FIREBALL LEVEL OF %d\n", msg->m_FireballLevel );
		pTempStructure->AddComponent( CRCD(0xce87e4e3,"fireball_level"), ESYMBOLTYPE_INTEGER, msg->m_FireballLevel );
		pTempStructure->AddComponent( CRCD(0x48e748b5,"stop_at_zero"), ESYMBOLTYPE_INTEGER, msg->m_StopAtZero );
	    Dbg_Printf( "******************** Setting time limit to %d\n", (int)msg->m_TimeLimit );
		skate_mod->SetTimeLimit( msg->m_TimeLimit );
		skate_mod->GetGameMode()->OverrideOptions( pTempStructure );
		skate_mod->GetGameMode()->SetNumTeams( msg->m_TeamMode );
		delete pTempStructure;
	}

	Script::RunScript( "StartingNewNetGame" );
	
	// the following sets up the panel and stuff
	skate_mod->LaunchGame();
	gamenet_man->ResetPlayers();
	gamenet_man->m_game_pending = false;
	gamenet_man->m_cheating_occurred = false;
	gamenet_man->MarkReceivedFinalScores( false );
	gamenet_man->SetCurrentLeader( NULL );
	gamenet_man->SetCurrentLeadingTeam( vNO_TEAM );
	gamenet_man->SetNetworkGameId( msg->m_GameId );
	if( msg->m_GameMode == Script::GenerateCRC( "netgoalattack" ))
	{
		skate_mod->GetGoalManager()->InitializeAllSelectedGoals();
	}
	gamenet_man->m_game_over = false;
	
	// Clear all scores
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		Mdl::Score* score;
		
		player->ClearCTFState();
		player->ResetProjectileVulnerability();
		if( player->m_Skater )
		{
			Obj::CSkaterScoreComponent* p_skater_score_component = GetSkaterScoreComponentFromObject(player->m_Skater);
			Dbg_Assert(p_skater_score_component);
			score = p_skater_score_component->GetScore();
			if( score )
			{
				score->Reset();
			}
		}
	}
    
	// Clear king of the hill data
	player = gamenet_man->GetKingOfTheHill();
	if( player )
	{
		player->MarkAsKing( false );
	}
	
	for( i = 0; i < (int)mode_array->GetSize(); i++ )
	{   
		uint32 value, script;
		Script::CStruct* mode_struct;
		 
		mode_struct = mode_array->GetStructure( i );
		Dbg_Assert( mode_struct );
		
		mode_struct->GetChecksum( "checksum", &value, true );
		if( value == msg->m_GameMode )
		{
			Script::CStruct* params;
						
			params = new Script::CStruct;
			params->AddInteger( "time", msg->m_TimeLimit );
			params->AddInteger( "score", msg->m_TargetScore );
			if( msg->m_TimeLimit == 0 )
			{
				params->AddInteger( CRCD(0xf0e712d2,"unlimited_time"), 1 );
			}
			else
			{
				params->AddInteger( CRCD(0xf0e712d2,"unlimited_time"), 0 );
			}
			mode_struct->GetChecksum( "goal_script", &script, true );
			Script::RunScript( script, params );
			delete params;
			break;
		}
	}

	Mem::Manager::sHandle().PopContext();
	
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_select_goals( Net::MsgHandlerContext* context )
{
	Net::MsgMax* msg;
	Game::CGoalManager* pGoalManager;
	Game::CGoal* pGoal;
	char* data;
	uint32 goal_id;
	char show_summary;
		
	msg = (Net::MsgMax*) context->m_Msg;	 
	data = msg->m_Data;
	pGoalManager = Game::GetGoalManager();
	pGoalManager->DeselectAllGoals();
	show_summary = *data++;
	memcpy( &goal_id, data, sizeof( uint32 ));
	data+= sizeof( uint32 );
	while( goal_id )
	{
		pGoal = pGoalManager->GetGoal( goal_id );
		Dbg_Assert( pGoal );

		pGoal->UnBeatGoal();
		pGoal->Select();
		
		memcpy( &goal_id, data, sizeof( uint32 ));
		data+= sizeof( uint32 );
	}

	if( show_summary == 1 )
	{
		Script::CStruct* pParams;
		pParams = new Script::CStruct;
		pParams->AddChecksum( "goal_summary", Script::GenerateCRC( "goal_summary" ));
		#ifdef __NOPT_ASSERT__
		Script::CScript *p_script=Script::SpawnScript( "wait_and_create_view_selected_goals_menu", pParams );
		p_script->SetCommentString("Spawned from Manager::s_handle_select_goals");
		#else
		Script::SpawnScript( "wait_and_create_view_selected_goals_menu", pParams );
		#endif
		delete pParams;
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Manager::s_handle_panel_message( Net::MsgHandlerContext* context )
{
    GameNet::MsgPanelMessage *msg;
	Manager* gamenet_man;
    
	msg = (GameNet::MsgPanelMessage*) context->m_Msg;
	gamenet_man = (Manager *) context->m_Data;
	
	gamenet_man->CreateNetPanelMessage( false, msg->m_StringId, msg->m_Parm1, msg->m_Parm2, NULL, NULL, false, msg->m_Time );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_run_ended( Net::MsgHandlerContext* context )
{   
	Manager* gamenet_man;
	PlayerInfo* player;
	bool all_done;
	unsigned char game_id;

	gamenet_man = (Manager *) context->m_Data;
	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	memcpy( &game_id, context->m_Msg, sizeof( unsigned char ));
	//Dbg_Printf( "**** Got end of run from player\n" );

	if( player )
	{
		Lst::Search< PlayerInfo > sh;
		if( player->m_flags.TestMask( PlayerInfo::mRUN_ENDED ))
		{
			//Dbg_Printf( "**** Run had already ended\n" );
			return Net::HANDLER_MSG_DONE;
		}

		// Make sure this message pertains to the current game
		if( game_id != gamenet_man->GetNetworkGameId())
		{
			//Dbg_Printf( "**** Wrong game id\n" );
			return Net::HANDLER_MSG_DONE;
		}
		
		player->m_flags.SetMask( PlayerInfo::mRUN_ENDED );
		
		all_done = true;
		for( player = gamenet_man->FirstPlayerInfo( sh ); player;
				player = gamenet_man->NextPlayerInfo( sh ))
		{
			if( !player->m_flags.TestMask( PlayerInfo::mRUN_ENDED))
			{
				all_done = false;
				break;
			}
		}

		if( all_done )
		{
			Net::Server* server;
			Net::MsgDesc msg_desc;

			server = gamenet_man->GetServer();
			Dbg_Assert( server );

			msg_desc.m_Id = MSG_ID_GAME_OVER;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
			for( player = gamenet_man->FirstPlayerInfo( sh, true ); player;
				player = gamenet_man->NextPlayerInfo( sh, true ))
			{
				server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
			}
		}
		else
		{
			//Dbg_Printf( "**** Not all done id\n" );
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_game_over( Net::MsgHandlerContext* context )
{   
	Manager* gamenet_man;
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();

	gamenet_man = (Manager *) context->m_Data;
	gamenet_man->MarkGameOver();

	if( ( CFuncs::ScriptIsObserving( NULL, NULL )) &&
		( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0xec200eaa,"netgoalattack" )))
	{
		Script::RunScript( CRCD(0xfce4f72d,"create_rankings"));
	}
	
	//Script::RunScript( "set_lobby_mode" );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::s_handle_end_game( Net::MsgHandlerContext* context )
{   
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CTrickObjectManager* manager = skate_mod->GetTrickObjectManager();

	manager->DeleteAllTrickObjects();
	Script::RunScript( "create_game_ended_dialog" );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Just calculate the length of the message and pass it back to   */
/* the dispatcher                                                 */
/******************************************************************/

int	Manager::s_handle_object_update( Net::MsgHandlerContext* context )
{   
	Manager* gamenet_man;
	int update_flags;
	unsigned char obj_id, obj_id_mask;
	Net::BitStream stream;
    
	Dbg_Assert( context );
     
	gamenet_man = (Manager *) context->m_Data;
	stream.SetInputData( context->m_Msg, 1024 );
	obj_id_mask = stream.ReadUnsignedValue( sizeof( char ) * 8 );
		
	for( obj_id = 0; obj_id < Mdl::Skate::vMAX_SKATERS; obj_id++ )
	{
		int value;

		// If the bit for this player number is not set, that means there is no 
		// object update for them. Continue
		if(( obj_id_mask & ( 1 << obj_id )) == 0 )
		{
			continue;
		}

		value = stream.ReadUnsignedValue( sizeof( uint16 ) * 8 );
		//Dbg_Printf( "Length after timestamp: %d\n", stream.GetByteLength());
		
		update_flags = stream.ReadUnsignedValue( 9 );
		//Dbg_Printf( "Length after update flags: %d\n", stream.GetByteLength());
		
		if( update_flags & GameNet::mUPDATE_FIELD_POS_X )
		{   
			value = stream.ReadSignedValue( sizeof( short ) * 8 );
			//Dbg_Printf( "Length after pos X: %d\n", stream.GetByteLength());
		}
		if( update_flags & GameNet::mUPDATE_FIELD_POS_Y )
		{
			value = stream.ReadSignedValue( sizeof( short ) * 8 );
			//Dbg_Printf( "Length after pos Y: %d\n", stream.GetByteLength());
		}
		if( update_flags & GameNet::mUPDATE_FIELD_POS_Z )
		{
			value = stream.ReadSignedValue( sizeof( short ) * 8 );
			//Dbg_Printf( "Length after pos Z: %d\n", stream.GetByteLength());
		}   
				
		if( update_flags & GameNet::mUPDATE_FIELD_ROT_X )
		{
			value = stream.ReadSignedValue( sizeof( short ) * 8 );
			//Dbg_Printf( "Length after rot X: %d\n", stream.GetByteLength());
		}
		if( update_flags & GameNet::mUPDATE_FIELD_ROT_Y )
		{
			value = stream.ReadSignedValue( sizeof( short ) * 8 );
			//Dbg_Printf( "Length after rot Y: %d\n", stream.GetByteLength());
		}
		if( update_flags & GameNet::mUPDATE_FIELD_ROT_Z )
		{
			value = stream.ReadSignedValue( sizeof( short ) * 8 );
			//Dbg_Printf( "Length after rot Z: %d\n", stream.GetByteLength());
		}
        
		if( update_flags & GameNet::mUPDATE_FIELD_STATE )
		{   
			value = stream.ReadUnsignedValue( 4 );
			//Dbg_Printf( "Length after state1: %d\n", stream.GetByteLength());
			value = stream.ReadUnsignedValue( 6 );
			value = stream.ReadUnsignedValue( 1 );
			value = stream.ReadUnsignedValue( 1 );
			//Dbg_Printf( "Length after state2: %d\n", stream.GetByteLength());
		}

		if( update_flags & GameNet::mUPDATE_FIELD_FLAGS )
		{
			value = stream.ReadUnsignedValue( 8 );
			//Dbg_Printf( "Length after flags: %d\n", stream.GetByteLength());
		}

		if( update_flags & GameNet::mUPDATE_FIELD_RAIL_NODE )
		{
			value = stream.ReadSignedValue( sizeof( sint16 ) * 8 );
			//Dbg_Printf( "Length After Rail Node: %d\n", stream.GetByteLength());
		}
	}
			
	// Tell the dispatcher the size of this message because it does not know (optimization)
	context->m_MsgLength = stream.GetByteLength();
	//Dbg_Printf( "Message Length %d\n", context->m_MsgLength );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Someone has requested to change teams						  */
/*                                                  			  */
/******************************************************************/

int	Manager::s_handle_team_change_request( Net::MsgHandlerContext* context )
{
	Manager* gamenet_man;
	PlayerInfo* change_player, *player;
	MsgChangeTeam* msg;
	Net::Server* server;
	MsgChangeTeam change_msg;
	Net::MsgDesc msg_desc;
	Lst::Search< PlayerInfo > sh;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		
	gamenet_man = (Manager *) context->m_Data;
	
	msg = (MsgChangeTeam*) context->m_Msg;
	change_player = gamenet_man->GetPlayerByObjectID( msg->m_ObjID );
	if(( change_player == NULL ) ||
	   ( change_player->m_Skater == NULL ) ||
	   ( skate_mod->GetGameMode()->GetNameChecksum() != Script::GenerateCRC( "netlobby" )))	// ignore unless in lobby
	{
		return Net::HANDLER_MSG_DONE;
	}
	change_msg.m_ObjID = msg->m_ObjID;
	change_msg.m_Team = msg->m_Team;

	server = gamenet_man->GetServer();
	Dbg_Assert( server );

	msg_desc.m_Data = &change_msg;
	msg_desc.m_Length = sizeof( MsgChangeTeam );
	msg_desc.m_Id = MSG_ID_TEAM_CHANGE;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; 
				player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Someone has changed teams  									  */
/*                                                  			  */
/******************************************************************/

int	Manager::s_handle_team_change( Net::MsgHandlerContext* context )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Manager* gamenet_man;
	PlayerInfo* player, *other_player;
	Lst::Search< PlayerInfo > sh;
	MsgChangeTeam* msg;
	int num_other_players;
		
	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgChangeTeam*) context->m_Msg;
	
	player = gamenet_man->GetPlayerByObjectID( msg->m_ObjID );
	if( player )
	{
		char team_str[64];
		Script::CStruct* pParams;
		bool all_same_team;
		
		player->m_Team = msg->m_Team;
		sprintf( team_str, "team_%d_name", msg->m_Team + 1 );
		pParams = new Script::CStruct;
		if( player->IsLocalPlayer())
		{
			if( skate_mod->GetGameMode()->IsTeamGame())
			{
				pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
				Script::RunScript( "joined_team_you", pParams );
			}
		}
		else
		{
			if( skate_mod->GetGameMode()->IsTeamGame())
			{
				pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, player->m_Name );
				pParams->AddComponent( Script::GenerateCRC("String1"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
				Script::RunScript( "joined_team_other", pParams );
			}
		}
		
		delete pParams;

		all_same_team = true;
		num_other_players = 0;
		for( other_player = gamenet_man->FirstPlayerInfo( sh ); other_player; 
				other_player = gamenet_man->NextPlayerInfo( sh ))
		{
			if( other_player != player )
			{
				num_other_players++;
				if( msg->m_Team != other_player->m_Team )
				{
					all_same_team = false;
				}
			}
		}

		if( all_same_team && ( num_other_players > 0 ))
		{
			if( skate_mod->GetGameMode()->NumTeams() > 0 )
			{
				Script::RunScript( CRCD(0x16c9b0dc,"warn_all_same_team"));
			}
		}
	}
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Client beat a goalattack goal. Verify that no other teammate	  */
/* has beaten it and relay it to all clients involved  			  */
/******************************************************************/

int Manager::s_handle_beat_goal( Net::MsgHandlerContext* context )
{
	MsgBeatGoal *msg;
	Manager* gamenet_man;
	Game::CGoalManager* pGoalManager;
	Game::CGoal* pGoal;    
		
	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgBeatGoal*) context->m_Msg;
    
	Dbg_Printf( "*** Got s_handle_beat_goal: 0x%x\n", msg->m_GoalId );
	// Make sure this message pertains to the current game
	if( msg->m_GameId != gamenet_man->GetNetworkGameId())
	{
		return Net::HANDLER_MSG_DONE;
	}

	pGoalManager = Game::GetGoalManager();
	pGoal = pGoalManager->GetGoal( msg->m_GoalId );
	if( pGoal )
	{
		PlayerInfo* player;
		bool already_beaten;

		player = gamenet_man->GetPlayerByConnection( context->m_Conn );
		Dbg_Assert( player );

		already_beaten = false;
		if( player->m_Skater )
		{   
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();

			// If we're in a team game, flag the goal as completed for all team members just in case someone quits later.
			// Also, uninitialize the goal
			if( skate_mod->GetGameMode()->IsTeamGame())
			{
				PlayerInfo* team_player;
				Lst::Search< PlayerInfo > sh;

				for( team_player = gamenet_man->FirstPlayerInfo( sh ); team_player; 
						team_player = gamenet_man->NextPlayerInfo( sh ))
				{
					if( player->m_Team == team_player->m_Team )
					{
						if( pGoal->HasWonGoal( team_player->m_Skater->GetID()))
						{
							already_beaten = true;
							break;
						}
					}
				}
				
				if( !already_beaten )
				{
					pGoal->MarkBeatenBy( player->m_Skater->GetID());
					// Notify all team members of the fact that the goal was beaten
					for( team_player = gamenet_man->FirstPlayerInfo( sh ); team_player; 
							team_player = gamenet_man->NextPlayerInfo( sh ))
					{
						MsgBeatGoalRelay goal_msg;
						Net::MsgDesc msg_desc;

						goal_msg.m_GoalId = msg->m_GoalId;
						goal_msg.m_ObjId = player->m_Skater->GetID();
						
						msg_desc.m_Data = &goal_msg;
						msg_desc.m_Length = sizeof( MsgBeatGoalRelay );
						msg_desc.m_Id = MSG_ID_BEAT_GOAL;
						msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
						msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
						
						context->m_App->EnqueueMessage( team_player->GetConnHandle(), &msg_desc );
					}
				}
			}
			else
			{
				if( pGoal->HasWonGoal( player->m_Skater->GetID()) == false )
				{
					PlayerInfo* send_player;
					Lst::Search< PlayerInfo > sh;
					MsgBeatGoalRelay goal_msg;
					Net::MsgDesc msg_desc;

					pGoal->MarkBeatenBy( player->m_Skater->GetID());

					goal_msg.m_GoalId = msg->m_GoalId;
					goal_msg.m_ObjId = player->m_Skater->GetID();

					msg_desc.m_Data = &goal_msg;
					msg_desc.m_Length = sizeof( MsgBeatGoalRelay );
					msg_desc.m_Id = MSG_ID_BEAT_GOAL;
					msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
					msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
					for( send_player = gamenet_man->FirstPlayerInfo( sh ); send_player; 
							send_player = gamenet_man->NextPlayerInfo( sh ))
					{
						context->m_App->EnqueueMessage( send_player->GetConnHandle(), &msg_desc );
					}
				}
			}
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Client beat a goalattack goal						  	  	  */
/*                                                  			  */
/******************************************************************/

int Manager::s_handle_beat_goal_relay( Net::MsgHandlerContext* context )
{
	MsgBeatGoalRelay *msg;
	Manager* gamenet_man;
	Game::CGoalManager* pGoalManager;
	Game::CGoal* pGoal, *pCurrentGoal;
	int current_goal_index;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
    
	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgBeatGoalRelay*) context->m_Msg;
    
	pGoalManager = Game::GetGoalManager();
	pGoal = pGoalManager->GetGoal( msg->m_GoalId );
	pCurrentGoal = NULL;
	current_goal_index = pGoalManager->GetActiveGoal( true );
	if( current_goal_index >= 0 )
	{
		pCurrentGoal = pGoalManager->GetGoalByIndex( current_goal_index );
	}

	if( pGoal )
	{
		PlayerInfo* local_player, *player;

		player = gamenet_man->GetPlayerByObjectID( msg->m_ObjId );
		local_player = gamenet_man->GetLocalPlayer();
		Dbg_Assert( player );

		pGoal->MarkBeatenByTeam( player->m_Team );

		if( player->m_Skater )
		{
			pGoal->MarkBeatenBy( player->m_Skater->GetID());

			// If it's not us, it must be a teammate
			if( local_player == player )
			{
				int num_goals, num_beaten;
				Script::CStruct* pParams;
					
				num_goals = pGoalManager->GetNumSelectedGoals();
				num_beaten = pGoalManager->NumGoalsBeaten();
		
				if( num_goals > num_beaten )
				{
					pParams = new Script::CStruct;
					pParams->AddInteger( "NumGoalsLeft", num_goals - num_beaten );
					Script::RunScript( "goal_attack_completed_goal", pParams );
					delete pParams;
				}

				pGoal->mp_goalPed->DestroyGoalPed();
			}
			else
			{
				int num_goals, num_beaten;
				Script::CStruct* pParams;
				bool in_same_goal;
					
				in_same_goal = 	( pCurrentGoal != NULL ) && 
								( pGoal->GetRootGoalId() == pCurrentGoal->GetRootGoalId());
				if( ( skate_mod->GetGameMode()->IsTeamGame()) && 
					( local_player->m_Team == player->m_Team ))
				{
					if( in_same_goal )
					{
						if( pGoal->GetChildren()->m_relative != 0 )
						{
							Game::CGoal* pChildGoal = pGoal;	
							
							// get the root node
							Game::CGoalLink* p_child = pGoal->GetChildren();
							while ( p_child && p_child->m_relative != 0 )
							{
								pChildGoal = pGoalManager->GetGoal( p_child->m_relative );
								p_child = pChildGoal->GetChildren();
							}
						
							pChildGoal->Win();
							if( pChildGoal != pCurrentGoal )
							{
								pCurrentGoal->Deactivate();
							}
						}
						else
						{
							pGoal->Win();
						}

						pCurrentGoal->mp_goalPed->DestroyGoalPed();
					}
					else
					{
						pGoal->mp_goalPed->DestroyGoalPed();
					}
					
					pGoal->MarkBeaten();
	
					num_goals = pGoalManager->GetNumSelectedGoals();
					num_beaten = pGoalManager->NumGoalsBeaten();
			
					if( num_goals > num_beaten )
					{
						const char* p_view_goals_text = NULL;
	
						pGoal->GetViewGoalsText( &p_view_goals_text );
						pParams = new Script::CStruct;
						pParams->AddInteger( CRCD(0x684a8396, "NumGoalsLeft"), num_goals - num_beaten );
						pParams->AddString( CRCD(0x9196d920, "PlayerName"), player->m_Name );
						pParams->AddString( CRCD(0xb8a88b50, "GoalText"), p_view_goals_text );
						Script::RunScript( CRCD(0xf4748e47, "goal_attack_completed_goal_other_same_team"), pParams );
						delete pParams;
					}

					
				}
			}
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Client started a goalattack goal. 							  */
/* Relay it to all clients			  			  				  */
/******************************************************************/

int Manager::s_handle_started_goal( Net::MsgHandlerContext* context )
{
	MsgStartedGoal *msg;
	Manager* gamenet_man;
	Game::CGoalManager* pGoalManager;
	Game::CGoal* pGoal;    
		
	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgStartedGoal*) context->m_Msg;
    
	// Make sure this message pertains to the current game
	if( msg->m_GameId != gamenet_man->GetNetworkGameId())
	{
		return Net::HANDLER_MSG_DONE;
	}

	pGoalManager = Game::GetGoalManager();
	pGoal = pGoalManager->GetGoal( msg->m_GoalId );
	if( pGoal )
	{
		PlayerInfo* player;

		player = gamenet_man->GetPlayerByConnection( context->m_Conn );
		Dbg_Assert( player );

		if( player->m_Skater )
		{   
			PlayerInfo* other_player;
			Lst::Search< PlayerInfo > sh;
			MsgStartedGoalRelay goal_msg;
			Net::MsgDesc msg_desc;

			goal_msg.m_GoalId = msg->m_GoalId;
			goal_msg.m_ObjId = player->m_Skater->GetID();

			msg_desc.m_Data = &goal_msg;
			msg_desc.m_Length = sizeof( MsgStartedGoalRelay );
			msg_desc.m_Id = MSG_ID_STARTED_GOAL;
			/* Dan: used to only sent to teammates
			for( team_player = gamenet_man->FirstPlayerInfo( sh ); team_player; 
					team_player = gamenet_man->NextPlayerInfo( sh ))
			{
				if( player == team_player )
				{
					continue;
				}
				if( player->m_Team == team_player->m_Team )
				{
					context->m_App->EnqueueMessage( team_player->GetConnHandle(), &msg_desc );
				}
			}
			*/
			for( other_player = gamenet_man->FirstPlayerInfo( sh ); other_player; 
					other_player = gamenet_man->NextPlayerInfo( sh ))
			{
				if( player == other_player )
				{
					continue;
				}
				context->m_App->EnqueueMessage( other_player->GetConnHandle(), &msg_desc );
			}
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Client started a goalattack goal						  	  	  */
/*                                                  			  */
/******************************************************************/

int Manager::s_handle_started_goal_relay( Net::MsgHandlerContext* context )
{
	MsgStartedGoalRelay *msg;
	Manager* gamenet_man;
	Game::CGoalManager* pGoalManager;
	Game::CGoal* pGoal;
    
	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgStartedGoalRelay*) context->m_Msg;
    
	pGoalManager = Game::GetGoalManager();
	pGoal = pGoalManager->GetGoal( msg->m_GoalId );
	if( pGoal )
	{
		Script::CStruct* pParams;
		const char* p_view_goals_text = NULL;
		PlayerInfo* player;

		player = gamenet_man->GetPlayerByObjectID( msg->m_ObjId );
		Dbg_Assert( player );

		if (gamenet_man->GetLocalPlayer()->m_Team == player->m_Team)
		{
			pGoal->GetViewGoalsText( &p_view_goals_text );
			pParams = new Script::CStruct;
			pParams->AddString( CRCD(0x9196d920, "PlayerName"), player->m_Name );
			pParams->AddString( CRCD(0xb8a88b50, "GoalText"), p_view_goals_text );
			Script::RunScript( CRCD(0xa77758de, "goal_attack_started_goal_other_same_team"), pParams );
			delete pParams;
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Toggle this proset						  	  				  */
/*                                                  			  */
/******************************************************************/

int Manager::s_handle_toggle_proset( Net::MsgHandlerContext* context )
{
	MsgToggleProSet* msg;
	Script::CStruct* pParams;
	Manager* gamenet_man;

	msg = (MsgToggleProSet*) context->m_Msg;
	gamenet_man = (Manager *) context->m_Data;
	pParams = new Script::CStruct;

	Dbg_Printf( "********************* handle toggle proset" );

	Script::RunScript( "toggle_proset_flag", Script::GetStructure( msg->m_ParamId, Script::ASSERT ));
	Script::RunScript( "toggle_geo_nomenu", Script::GetStructure( msg->m_ParamId, Script::ASSERT ));

	if( gamenet_man->OnServer() == false )
	{
		gamenet_man->m_proset_flags.Toggle( msg->m_Bit );
	}

	delete pParams;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Someone has sent us a chat message						  	  */
/*                                                  			  */
/******************************************************************/

int Manager::s_handle_chat( Net::MsgHandlerContext* context )
{
	MsgChat* chat_msg;
	Script::CStruct* p_params;
	char final_msg[256];
	
	chat_msg = (MsgChat*) context->m_Msg;
	p_params = new Script::CStruct;	
	sprintf( final_msg, "\\c%i%s\\c0 : %s", ( chat_msg->m_ObjId + 2 ), chat_msg->m_Name, chat_msg->m_ChatMsg );
	p_params->AddString( "text", final_msg );
	Script::RunScript("create_console_message", p_params );
	delete p_params;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Server has changed the number of teams						  */
/*                                                  			  */
/******************************************************************/

int	Manager::s_handle_set_num_teams( Net::MsgHandlerContext* context )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Manager* gamenet_man;
	MsgByteInfo* msg;
	int num_teams;
    
	gamenet_man = (Manager *) context->m_Data;
	msg = (MsgByteInfo*) context->m_Msg;
	num_teams = msg->m_Data;

	if( ( num_teams > 0 ) &&
		( !skate_mod->GetGameMode()->IsTeamGame()))
	{
		skate_mod->GetGameMode()->SetNumTeams( num_teams );

		Script::RunScript( "ChooseTeamMessage" );
		if( skate_mod->GetLocalSkater())
		{
			Script::RunScript( "PrepareSkaterForMove", NULL, skate_mod->GetLocalSkater());
			skate_mod->move_to_restart_point( skate_mod->GetLocalSkater());
		}
	}
	else
	{
		skate_mod->GetGameMode()->SetNumTeams( num_teams );
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Server requests client's cheat checksum				  		  */
/*                                                  			  */
/******************************************************************/

int	Manager::s_handle_cheat_checksum_request( Net::MsgHandlerContext* context )
{
	MsgCheatChecksum* msg;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Net::MsgDesc msg_desc;

	msg = (MsgCheatChecksum*) context->m_Msg;
	msg->m_ClientChecksum = skate_mod->GetCheatChecksum();

	msg_desc.m_Id = MSG_ID_CHEAT_CHECKSUM_RESPONSE;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	msg_desc.m_Data = msg;
	msg_desc.m_Length = sizeof( MsgCheatChecksum );

	context->m_App->EnqueueMessageToServer( &msg_desc );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Server has sent a GameSpy stats challenge					  */
/*                                                  			  */
/******************************************************************/

int	Manager::s_handle_challenge( Net::MsgHandlerContext* context )
{
#ifdef __PLAT_NGPS__
	Net::MsgDesc msg_desc;
	char* challenge;
	char response[33];
	Prefs::Preferences* pPreferences;
	Manager* gamenet_man;
	
	gamenet_man = (Manager*) context->m_Data;

	pPreferences = gamenet_man->GetNetworkPreferences();
	const char* password = pPreferences->GetPreferenceString( Script::GenerateCRC( "profile_password" ), Script::GenerateCRC("ui_string") );

	challenge = context->m_Msg;
	gamenet_man->mpStatsMan->GenerateAuthResponse( challenge, (char*) password, response );

	msg_desc.m_Id = MSG_ID_CHALLENGE_RESPONSE;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	msg_desc.m_Data = response;
	msg_desc.m_Length = 33;

	context->m_App->EnqueueMessageToServer( &msg_desc );
#endif
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Client has answered the GameSpy stats challenge				  */
/*                                                  			  */
/******************************************************************/

int	Manager::s_handle_challenge_response( Net::MsgHandlerContext* context )
{
#ifdef __PLAT_NGPS__
	PlayerInfo* player;
	Manager* gamenet_man;
	
	gamenet_man = (Manager*) context->m_Data;

	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	gamenet_man->mpStatsMan->AuthorizePlayer( player->m_Skater->GetID(), context->m_Msg );
#endif

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Client has requested that we re-send level data to them		  */
/*                                                  			  */
/******************************************************************/

int Manager::s_handle_level_data_request( Net::MsgHandlerContext* context )
{
	Net::Server* server;
	Manager* gamenet_man;

	server = (Net::Server*) context->m_App;
	gamenet_man = (Manager*) context->m_Data;

	if( context->m_MsgId == MSG_ID_REQUEST_RAILS_DATA )
	{
		Dbg_Printf( "***** Client requested rails data\n" );
		server->StreamMessage( context->m_Conn->GetHandle(), MSG_ID_RAIL_DATA, Obj::GetRailEditor()->GetCompressedRailsBufferSize(), 
							   Obj::GetRailEditor()->GetCompressedRailsBuffer(), "rail data", vSEQ_GROUP_PLAYER_MSGS,
							   false, true );
							   
	}
	else if( context->m_MsgId == MSG_ID_REQUEST_GOALS_DATA )
	{
		Dbg_Printf( "***** Client requested goals data\n" );
		if( gamenet_man->UsingCreatedGoals())
		{
			server->StreamMessage( context->m_Conn->GetHandle(), MSG_ID_GOALS_DATA, gamenet_man->GetGoalsDataSize(),
								   gamenet_man->GetGoalsData(), "goals data", vSEQ_GROUP_PLAYER_MSGS, false, true );
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Client has responded to the cheat checksum request			  */
/*                                                  			  */
/******************************************************************/

int	Manager::s_handle_cheat_checksum_response( Net::MsgHandlerContext* context )
{
	MsgCheatChecksum* msg;
	PlayerInfo* player;
	Manager* gamenet_man;

	gamenet_man = (Manager*) context->m_Data;
	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	msg = (MsgCheatChecksum*) context->m_Msg;
	if(( msg->m_ServerChecksum ^ 0xDEADFACE ) != msg->m_ClientChecksum )
	{
		Dbg_Printf( "***** CHEATING! 0x%x  0x%x\n", msg->m_ServerChecksum ^ 0xDEADFACE, msg->m_ClientChecksum );
		if( gamenet_man->HasCheatingOccurred() == false )
		{
			Script::CStruct* p_params;
	
			p_params = new Script::CStruct;
			p_params->AddString( CRCD(0xa4b08520,"String0"), player->m_Name );
			Script::RunScript( CRCD(0x62ddbe0a,"notify_client_cheating"), p_params );
			delete p_params;
			
			gamenet_man->CheatingOccured();
		}
	}

	return Net::HANDLER_CONTINUE;
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_change_level_code( const Tsk::Task< Manager >& task )
{
	Manager& man = task.GetData();
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;

	if(( Tmr::GetTime() < s_time_change_level ))
	{
		return;
	}

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	skate_mod->ChangeLevel( man.m_level_id );

	// Clear the king of the hill
	if(( player = man.GetKingOfTheHill()))
	{
		player->MarkAsKing( false );
	}

	for( player = man.FirstPlayerInfo( sh, true ); player; player = man.NextPlayerInfo( sh, true ))
	{
		player->ClearCTFState();
	}

	//man.RespondToReadyQuery();
	man.m_change_level_task->Remove();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		Manager::ok_to_join( int& reason, MsgConnectInfo* connect_msg, Net::Conn* conn )
{
	char* password;
	
	if( m_server->IsConnectionBanned( conn ))
	{
		reason = JOIN_REFUSED_ID_BANNED;
		return false;
	}
	if( connect_msg->m_Observer )
	{
		if( GetNumObservers() >= GetMaxObservers())
		{
			reason = JOIN_REFUSED_ID_FULL_OBSERVERS;
			return false;
		}
	}
	else
	{
		if( GetNumPlayers() >= GetMaxPlayers())
		{
			reason = JOIN_REFUSED_ID_FULL;
			return false;
		}
	}
		
	// Version check
	if( connect_msg->m_Version != vVERSION_NUMBER )
	{
		reason = JOIN_REFUSED_ID_VERSION;
		Dbg_Printf( "Client had the wrong version. Dropping connection\n" );
		return false;
	}
		
	// Game-in-progress check
	if( !connect_msg->m_WillingToWait && !connect_msg->m_Observer )
	{
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();

		if(( skate_mod->GetGameMode()->GetNameChecksum() != Script::GenerateCRC( "netlobby" )) &&
		   ( GameIsOver() == false ))
		{
			reason = JOIN_REFUSED_ID_IN_PROGRESS;
			return false;
		}
	}

	// Check if we're password-protected. If so, compare passwords
	password = GetPassword();
	if( password[0] != '\0' )
	{
		Dbg_Printf( "Comparing passwords %s and %s\n", password, connect_msg->m_Password );
		if( strcmp( password, connect_msg->m_Password ))
		{          
			reason = JOIN_REFUSED_ID_PW;

			Dbg_Printf( "Client gave the wrong password. Dropping connection\n" );
			return false;
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet






================================================
FILE: Code/Sk/GameNet/GameMsg.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate3													**
**																			**
**	Module:			GameNet					 								**
**																			**
**	File name:		GameMsg.h												**
**																			**
**	Created by:		02/20/01	-	spg										**
**																			**
**	Description:	Game-Side Network Messages								**
**																			**
*****************************************************************************/

#ifndef __GAMENET_GAMEMSG_H
#define __GAMENET_GAMEMSG_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace GameNet
{

						

enum
{
	MSG_ID_SKATER_INPUT	= Net::MSG_ID_USER,	//	= 32 : C->S The Client's Pad Input
	MSG_ID_YOUR_PLAYER_CREATE,				//	= 33 : S->C Create local skater
	MSG_ID_PLAYER_CREATE,					//	= 34 : S->C Each player's info
	MSG_ID_OBJ_UPDATE_STREAM,				//	= 35 : S->C Stream of object updates
	MSG_ID_NEW_ALIAS,						//	= 36 : C->S Alias suggestion
	MSG_ID_READY_QUERY,						//	= 37 : S->C Are you ready to receive more data?
	MSG_ID_READY_RESPONSE,					//	= 38 : C->S or S->C Ready query response
	MSG_ID_PAUSE,							//	= 39 : S->C Pause instruction
	MSG_ID_UNPAUSE,							//	= 40 : S->C UnPause instruction
	MSG_ID_PRIM_ANIM_START,					//	= 41 : S->C Play primary animation
	MSG_ID_ROTATE_SKATEBOARD,				//	= 42 : S->C Rotate skateboard
	MSG_ID_SET_WOBBLE_TARGET,				//	= 43 : S->C Set wobble target
	MSG_ID_FLIP_ANIM,						//	= 44 : S->C Flip the animation
	MSG_ID_PLAYER_QUIT,						//	= 45 : S->C Notification of a player quitting
	MSG_ID_HOST_REQ,						//  = 46 : GameServer->MatchMaker Game Host Request
	MSG_ID_HOST_PROCEED,					//  = 47 : MatchMaker->GameServer Proceed hosting
	MSG_ID_GAMELIST_REQ,					//  = 48 : GameClient->MatchMaker Game list request
	MSG_ID_GAMELIST_RESPONSE,				//  = 49 : MatchMaker->GameClient Game list response
	MSG_ID_HOST_QUIT,						//	= 50 : S->C Server is shutting down
	MSG_ID_START_INFO,						//	= 51 : S->C Startup Game Option
	MSG_ID_GAME_INFO,						//	= 52 : S->C Game Options
	MSG_ID_PLAY_SOUND,						//	= 53 : S->C Play Sound
	MSG_ID_PLAY_LOOPING_SOUND,				// 	= 54 : S->C Play Looping Sound
	MSG_ID_SPARKS_ON,						//	= 55 : S->C Sparks on
	MSG_ID_SPARKS_OFF,						//	= 56 : S->C Sparks off
	MSG_ID_BLOOD_ON,						//	= 57 : S->C Blood on
	MSG_ID_BLOOD_OFF,						//	= 58 : S->C Blood off
	MSG_ID_SCORE,							//	= 59 : S->C General Score Msg: See SCORE_MSG_ID...
	MSG_ID_SET_WOBBLE_DETAILS,				//	= 60 : S->C Set the wobbling parameters
	MSG_ID_SET_LOOPING_TYPE,				//	= 61 : S->C Set the desired looping type
	MSG_ID_SET_HIDE_ATOMIC,					//	= 62 : S->C Hide an arbitrary atomic on the skin model
	MSG_ID_PANEL_MESSAGE,					//	= 63 : S->C Display this panel message
	MSG_ID_SCORE_UPDATE,					//	= 64 : S->C Score update for other clients
	MSG_ID_SET_ANIM_SPEED,					//	= 65 : S->C Set animation speed
	MSG_ID_PROCEED_TO_PLAY,					//	= 66 : S->C Init phase complete: Next state
	MSG_ID_SKATER_COLLIDE_LOST,				//	= 67 : S->C You were involved in a skater collision and lost
	MSG_ID_SKATER_COLLIDE_WON,				//	= 68 : S->C You were involved in a skater collision and won
	MSG_ID_BAIL_DONE,						//	= 69 : C->S Bail is done
	MSG_ID_CHANGE_LEVEL,					//	= 70 : S->C Change level
	MSG_ID_JOIN_REFUSED,					//	= 71 : S->C Server refused to allow player to join
	MSG_ID_RUN_SCRIPT,						//  = 72 : C->C Run this script
	MSG_ID_SPAWN_AND_RUN_SCRIPT,			//  = 73 : C->C Spawn and Run this script
	MSG_ID_OBSERVE,							//	= 74 : C->S A request to enter observer mode
	MSG_ID_STEAL_MESSAGE,					//	= 75 : S->C A request to display a "steal message" in graffiti
	MSG_ID_CHAT,							//	= 76 : C->C Chat message
	MSG_ID_OBSERVE_PROCEED,					//  = 77 : S->C Proceed to observe
	MSG_ID_OBSERVE_REFUSED,					//	= 78 : S->C Server says you can't observe
	MSG_ID_JOIN_PROCEED,					//	= 79 : S->C Go ahead and attempt to join
	MSG_ID_JOIN_REQ,						//  = 80 : C->S Request to join a connected server
	MSG_ID_KICKED,							//	= 81 : S->C You've been kicked
	MSG_ID_LANDED_TRICK,					//	= 82 : S->C Landed a trick
	MSG_ID_PLAYER_INFO_ACK_REQ,				//	= 83 : S->C Have you received all the player info data?
	MSG_ID_PLAYER_INFO_ACK,					//	= 84 : C->S Yes I have received all the player info data
	MSG_ID_NEW_KING,						//	= 85 : S->C Crown the new king
	MSG_ID_LEFT_OUT,						//  = 86 : S->C Sorry. We're playing without you
	MSG_ID_MOVED_TO_RESTART,				//	= 87 : C->S Client has moved to a restart
	MSG_ID_KING_DROPPED_CROWN,				//	= 88 : S->C Spawn a new crown in the world
	MSG_ID_RUN_HAS_ENDED,					//	= 89 : C->S My run has ended
	MSG_ID_GAME_OVER,						//	= 90 : S->C Game is over
	MSG_ID_OBSERVER_LOG_TRICK_OBJ,			//	= 91 : S->C Special-case observer trick object report
	MSG_ID_OBSERVER_INIT_GRAFFITI_STATE,	//	= 92 : S->C Special-case observer, to synchronize trick objects with the current graffiti state
	MSG_ID_PRINTF,							//  = 93 : S->C Printf
	MSG_ID_AUTO_SERVER_NOTIFICATION,		//	= 94 : S->C Notify the client that he's on an auto-serving server
	MSG_ID_FCFS_ASSIGNMENT,					//	= 95 : S->C Notify the client that he's the acting host
	MSG_ID_FCFS_START_GAME,					//	= 96 : C->S Request to start a game from a FCFS
	MSG_ID_FCFS_BAN_PLAYER,					//	= 97 : C->S Request to ban a player from a FCFS
	MSG_ID_FCFS_CHANGE_LEVEL,				//	= 98 : C->S Request to change levels from a FCFS
	MSG_ID_FCFS_TOGGLE_PROSET,				//	= 99 : C->S Request to toggle a pro set
	MSG_ID_FCFS_TOGGLE_GOAL_SELECTION,		//	= 100 : C->S Request to toggle a goal selection
	MSG_ID_FCFS_END_GAME,					//	= 101 : C->S Request to end the current game
	MSG_ID_FCFS_SET_NUM_TEAMS,				//	= 102 : C->S Request to set the number of teams
	MSG_ID_REQUEST_CHANGE_TEAM,				//	= 103 : C->S Request to change teams
	MSG_ID_TEAM_CHANGE,						//	= 104 : S->C Team change update
	MSG_ID_SET_NUM_TEAMS,					//	= 105 : S->C Change in the number of teams
	MSG_ID_END_GAME,						//	= 106 : S->C Server has chosen to end the game
	MSG_ID_REQUEST_LEVEL,					//	= 107 : C->S Client requests level data
	MSG_ID_LEVEL_DATA,						//	= 108 : S->C Server sending client level data
	MSG_ID_SELECT_GOALS,					//	= 109 : S->C A list of goals to complete
	MSG_ID_BEAT_GOAL,						//	= 110 : C->S I beat a goal
	MSG_ID_STARTED_GOAL,					//	= 111 : C->S I started a goal
	MSG_ID_TOGGLE_PROSET,					//	= 112 : S->C Toggle this proset
	MSG_ID_TOOK_FLAG,						//	= 113 : S->C Player took a ctf flag
	MSG_ID_CAPTURED_FLAG,					//	= 114 : S->C Player captured a ctf flag
	MSG_ID_RETRIEVED_FLAG,					//	= 115 : S->C Player retrieved a ctf flag
	MSG_ID_STOLE_FLAG,						//	= 116 : S->C Player stole a ctf flag
	MSG_ID_PLAYER_DROPPED_FLAG,				//	= 117 : S->C Player dropped a flag
	MSG_ID_ROTATE_DISPLAY,					//	= 118 : S->C Rotate the skater by this much over time
	MSG_ID_CREATE_SPECIAL_ITEM,				//	= 119 : S->C Create a special item
	MSG_ID_DESTROY_SPECIAL_ITEM,			//	= 120 : S->C Destroy a special item
	MSG_ID_JOIN_ACCEPTED,					//	= 121 : S->C You're in - completely
	MSG_ID_KILL_TEAM_FLAGS,					//	= 122 : S->C Kill team flag (i.e. no more switching teams)
	MSG_ID_WAIT_N_SECONDS,					//	= 123 : S->C Prepare for a period of N seconds of no communication
	MSG_ID_CHALLENGE,						//	= 124 : S->C GameSpy Stats Challenge
	MSG_ID_CHALLENGE_RESPONSE,				//	= 125 : C->S GameSpy Stats Challenge Response
	MSG_ID_FACE_DATA,						//	= 126 : C->C Custom face data
	MSG_ID_TOGGLE_CHEAT,					//	= 127 : S->C Toggle a cheat on or off
	MSG_ID_CHEAT_CHECKSUM_REQUEST,			//	= 128 : S->C Server requesting client's cheat checksum
	MSG_ID_CHEAT_CHECKSUM_RESPONSE,			//	= 129 : C->S Client responding to server's request
	MSG_ID_CHEAT_LIST,						//	= 130 : S->C A list of all active cheats
	MSG_ID_SPAWN_PROJECTILE,				//  = 131 : C->C Spawn a projectile
	MSG_ID_SKATER_HIT_BY_PROJECTILE,		//	= 132 : S->C You were hit by a projectile
	MSG_ID_SKATER_PROJECTILE_HIT_TARGET,	//	= 133 : S->C You hit someone with a projectile
	MSG_ID_COMBO_REPORT,					//	= 134 : C->S Reporting combo
	MSG_ID_GOALS_DATA,						//	= 135 : S->C Server sending client goals data
	MSG_ID_RAIL_DATA,						//	= 136 : S->C Server sending edited rail data
	MSG_ID_ENTER_VEHICLE,					//  = 137 : C->S Client entering a vehicle
	MSG_ID_REQUEST_GOALS_DATA,				//	= 138 : C->S Re-requesting goals data
	MSG_ID_REQUEST_RAILS_DATA,				//	= 139 : C->S Re-requesting rails data
	MSG_ID_CLEAR_ROTATE_DISPLAY,			//	= 140 : S->C Clear any model rotations
};

enum
{
	SCORE_MSG_ID_LAND,						// = 0 : S->C Tabulate final score
	SCORE_MSG_ID_LOG_TRICK_OBJECT,			// = 1 : S->C Color an object
};

enum
{
	JOIN_REFUSED_ID_FULL,					// = 0 : S->C Server Full
	JOIN_REFUSED_ID_PW,						// = 1 : S->C Wrong Password
	JOIN_REFUSED_ID_VERSION,				// = 2 : S->C Wrong version
	JOIN_REFUSED_ID_IN_PROGRESS,			// = 3 : S->C Game is in progress
	JOIN_REFUSED_ID_BANNED,					// = 4 : S->C You've been banned
	JOIN_REFUSED_ID_FULL_OBSERVERS,			// = 5 : S->C Server has no more room for observers
};

enum
{
	vSEQ_GROUP_PLAYER_MSGS = 8,				// Messages about new players & quitting players
	vSEQ_GROUP_FACE_MSGS,					// Face download messages
};

enum
{
	vMAX_CONNECTIONS			= 20,
	vMAX_PLAYERS 				= 8,
	vMAX_SERVER_NAME_LEN 		= 15,
	vMAX_PLAYER_NAME_LEN 		= 15,
	vMAX_APPEARANCE_DATA_SIZE 	= 4096,
	vMAX_PASSWORD_LEN			= 9,
	vMAX_CHAT_MSG_LEN			= 127,
	vMAX_TRICK_OBJECTS_IN_LEVEL = 256,	// actually custom parks can have more than 256 trick objects, so we'll need to address that in THPS4
};

enum
{
#ifdef __PLAT_XBOX__
	vCONNECTION_TIMEOUT = 5000,		//  5 seconds in ms
#else
	vCONNECTION_TIMEOUT = 20000,	//	30 seconds in ms
#endif
	vNOT_READY_TIMEOUT	= 60000,	//	Clients are marked "not ready" when we tell them to load
									//	levels/skaters/etc.  If that time exceeds this timeout
									//	we should disconnect them
	vJOIN_TIMEOUT		= 60000,	//  20 seconds in ms to join a server
};

enum
{
	vHOST_PORT 		= 5150,
	vJOIN_PORT		= 5151,
};

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							Messages										**
*****************************************************************************/

class MsgReady
{
public:
	unsigned int	m_Time;
};

class MsgPlayPrimaryAnim
{
public:
	//uint32			m_Time;
	uint32			m_Index;
	char			m_LoopingType;
	char			m_ObjId;
	unsigned short	m_StartTime;
	unsigned short	m_EndTime;
	unsigned short	m_BlendPeriod;
	unsigned short	m_Speed;
};

class MsgFlagMsg
{
public:
	char m_ObjId;
	char m_Team;
};

class MsgFlipAnim
{
public:
	//uint32			m_Time;
	char			m_ObjId;
	char			m_Flipped;
};

class MsgRotateSkateboard
{
public:
	//uint32			m_Time;
	char			m_ObjId;
	char			m_Rotate;
};

class MsgRotateDisplay
{
public:
	//uint32			m_Time;
	int 			m_Duration;
	int 			m_SinePower;
	short 			m_StartAngle;
	short 			m_DeltaAngle;
	char 			m_ObjId;
	bool			m_HoldOnLastAngle;
	char			m_Flags;
};

class MsgSetWobbleTarget
{
public:
	//uint32			m_Time;
	char			m_ObjId;
	unsigned char	m_Alpha;
};

class MsgSetWobbleDetails
{
public:
	enum
	{
		vWOBBLE_AMP_A,
		vWOBBLE_AMP_B,
		vWOBBLE_K1,
		vWOBBLE_K2,
		vSPAZFACTOR,

		mWOBBLE_AMPA_MASK	= 0x03,
		mWOBBLE_AMPB_MASK	= 0x04,
		mWOBBLE_K1_MASK		= 0x08,
		mWOBBLE_K2_MASK		= 0x10,
		mSPAZFACTOR_MASK	= 0x20,
	};

	//uint32			m_Time;
	char			m_WobbleDetails;
	char			m_ObjId;
};

class MsgSetLoopingType
{
public:
	//uint32			m_Time;
	char			m_ObjId;
	char			m_LoopingType;
};

class MsgSpecialItem
{
public:
	//uint32			m_Time;
	unsigned int	m_Params;
	uint32			m_Bone;
	char			m_ObjId;
	char			m_Index;
};

class MsgHideAtomic
{
public:
	//uint32			m_Time;
	uint32			m_AtomicName;
	char			m_ObjId;
	char			m_Hide;
};

class MsgSetAnimSpeed
{
	public:
		//uint32			m_Time;
		float			m_AnimSpeed;
		char			m_ObjId;
};

// Game options sent to joining players
class MsgStartInfo
{
public:
	char			m_CrownSpawnPoint;
	char			m_Broadband;	// boolean
	unsigned char	m_GameId;
	char			m_ProSetFlags;
	char			m_MaxPlayers;
	int				m_TimeLeft;
	unsigned long	m_GameMode;
	uint32			m_LevelId;
	uint32			m_TeamMode;
	
	int				m_TimeLimit;
	int				m_TargetScore;
	int				m_StartPoints[vMAX_PLAYERS];
};

class MsgInitGraffitiState
{
public:
	uint8			m_NumTrickObjects[vMAX_PLAYERS];
	uint8			m_TrickObjectStream[vMAX_TRICK_OBJECTS_IN_LEVEL];
};

class MsgGameInfo
{
public:
	unsigned long	m_GameMode;
	unsigned long	m_TimeLimit;
	unsigned long	m_TargetScore;
	int				m_FireballLevel;
	char			m_NumPlayers;
	char			m_AccumulateScore;
	char			m_DegradeScore;
	char			m_UseClock;
	char			m_StopAtZero;
	unsigned char	m_GameId;
	uint32			m_TeamMode;
	char			m_CrownSpawnPoint;
	int				m_StartPoints[vMAX_PLAYERS];
};

class MsgConnectInfo
{
public:
	int				m_Version;
	char			m_Observer;	// Does this player just want to observe?
	char			m_WillingToWait;	// Willing to sit out until the next lobby
	char			m_Password[ vMAX_PASSWORD_LEN + 1 ];
};

class MsgJoinInfo : public MsgConnectInfo
{
public:
	int				m_Profile;
	int				m_Rating;
	char			m_Broadband;	// boolean
	uint32			m_VehicleControlType;
	char			m_Name[ vMAX_PLAYER_NAME_LEN + 1 ];
	uint8			m_AppearanceData[vMAX_APPEARANCE_DATA_SIZE];	// stream information for player appearance
};

class MsgNewPlayer
{
public:
	int			m_Profile;
	int			m_Rating;
	int			m_Flags;
	int			m_Score;
	char		m_ObjId;	// object id for this player
	char		m_Team;
	uint32		m_VehicleControlType;
	char		m_Name[ vMAX_PLAYER_NAME_LEN + 1 ];
	uint8		m_AppearanceData[vMAX_APPEARANCE_DATA_SIZE]; // stream information for player appearance
};

class MsgPlayerQuit
{
public:
	char	m_ObjId;
	char	m_Reason;
};

class MsgPanelMessage
{
public:
	enum
	{
		vMAX_STRING_PARAM_LENGTH = vMAX_PLAYER_NAME_LEN,
	};

	uint32	m_PropsStructureCRC;
	uint32	m_StringId;
	int		m_Time;
	char	m_Parm1[vMAX_STRING_PARAM_LENGTH + 1];
	char	m_Parm2[vMAX_STRING_PARAM_LENGTH + 1];
};

class MsgNewAlias
{
public:
	unsigned char	m_Alias;
	char		m_ObjId;
	int			m_Expiration;
};

#ifdef __PLAT_XBOX__
class MsgFindServer : public Net::MsgTimestamp
{
public:
	BYTE m_Nonce[8]; 
};
#else
typedef Net::MsgTimestamp	MsgFindServer;
#endif

class MsgServerDescription
{
public:
#ifdef __PLAT_XBOX__
	BYTE	m_Nonce[8]; 
	XNKID	m_XboxKeyId;
	XNKEY	m_XboxKey;
	XNADDR	m_XboxAddr;
#endif
	char	m_NumPlayers;
	char	m_MaxPlayers;
	char	m_NumObservers;
	char	m_MaxObservers;
	char	m_Password;	// boolean
	char	m_GameStarted;
	char	m_HostMode;
	char	m_Ranked;
	char	m_Name[GameNet::vMAX_SERVER_NAME_LEN + 1];
	char	m_Level[32];
	char	m_Mode[32];
	char	m_SkillLevel;
	int		m_Timestamp;
	char	m_PlayerNames[vMAX_PLAYERS][ vMAX_PLAYER_NAME_LEN + 1 ];
};

class ObjectUpdate
{
public:
	short			m_ObjId;
	Mth::Vector		m_Pos;
	Mth::Matrix		m_Matrix;
};

class MsgScriptedSound
{
public:
	char			m_ObjId;
	uint32 			m_SoundChecksum;
	int 			m_Volume;
	int 			m_Pitch;
};

class MsgPlaySound
{
public:
	short 			m_Pos[3];
	char 			m_WhichArray;
	char			m_SurfaceFlag;
	char 			m_VolPercent;
	char			m_PitchPercent;
	char			m_SoundChoice;
};

class MsgObjMessage
{
public:
	char			m_ObjId;
};

class MsgPlayLoopingSound
{
public:
	char			m_ObjId;
	int 			m_WhichArray;
	int				m_SurfaceFlag;
	float 			m_VolPercent;
};

class MsgBlood : public MsgObjMessage
{
public:
	char			m_BodyPart;
	float			m_Size;
	float			m_Frequency;
	float			m_RandomRadius;
};

class MsgBloodOff : public MsgObjMessage
{
public:
	char			m_BodyPart;
};

class MsgActuator
{
public:
	char			m_ActuatorId;	// left/right
	char			m_Percent;		// between 1 and 100
	unsigned short	m_Duration;		// ms duration
};

class MsgEmbedded
{
public:
	char	m_SubMsgId;		// Sub-message
};

class MsgScoreLogTrickObject : public MsgEmbedded
{
public:
	enum
	{
		vMAX_PENDING_TRICKS = 512,	// how much data we can send across
	};
	
	unsigned char	m_GameId;
	int		m_OwnerId;
	int		m_Score;
	uint32	m_NumPendingTricks;
	uint32	m_PendingTrickBuffer[vMAX_PENDING_TRICKS];
};

class MsgObsScoreLogTrickObject
{
public:
	enum
	{
		vMAX_PENDING_TRICKS = 512,	// how much data we can send across
	};
	
	unsigned char	m_GameId;
	int		m_OwnerId;
	uint32	m_NumPendingTricks;
	uint32	m_PendingTrickBuffer[vMAX_PENDING_TRICKS];
};

class MsgScoreLanded
{
public:
	unsigned char	m_GameId;
	int		m_Score;
};

class MsgBeatGoal
{
public:
	unsigned char	m_GameId;
	uint32			m_GoalId;
};

class MsgBeatGoalRelay
{
public:
	char			m_ObjId;
	uint32			m_GoalId;
};

typedef MsgBeatGoal MsgStartedGoal;
typedef MsgBeatGoalRelay MsgStartedGoalRelay;

class MsgScoreUpdate
{
public:
	int		m_TimeLeft;		// Time left in the game
	Flags< int > m_Cheats;
	char	m_Final;		// Final scores?
	char	m_NumScores;
	char	m_ScoreData[256];
};

class MsgSpawnAndRunScript
{
public:
	uint32	m_ScriptName;
	int		m_ObjID;
	int		m_Node;
	char	m_Permanent;	// is this an event that the server should queue up for new joiners?
};

class MsgToggleProSet
{
public:
	uint32	m_ParamId;
	char	m_Bit;
};

class MsgToggleCheat
{
public:
	uint32	m_Cheat;
	char	m_On;
};

class MsgEnabledCheats
{
public:
	enum
	{
		vMAX_CHEATS = 32,
	};
	int		m_NumCheats;
	uint32	m_Cheats[vMAX_CHEATS];
};

class MsgStealMessage
{
public:
	unsigned char	m_GameId;
	int		m_NewOwner;		// new owner of the trick object
	int		m_OldOwner;		// old owner of the trick object
};

class MsgRunScript
{
public:
	enum
	{
		vMAX_SCRIPT_PARAMS_LEN = 1024
	};
	uint32	m_ScriptName;
	int		m_ObjID;
	char	m_Data[vMAX_SCRIPT_PARAMS_LEN];
};

class MsgChangeLevel
{
public:
	int		m_Level;
	char	m_ShowWarning;
};

class MsgChangeTeam
{
public:
	char	m_ObjID;
	char	m_Team;
};

class MsgByteInfo		// Generic one-byte message
{
public:
	char	m_Data;
};

class MsgIntInfo		// Generic four-byte message
{
	public:
		int		m_Data;
};

class MsgChat
{
	public:
		char	m_ObjId;
		char	m_Name[vMAX_PLAYER_NAME_LEN + 1];
		char	m_ChatMsg[vMAX_CHAT_MSG_LEN + 1];
};

class MsgRequestLevel
{
public:
	uint32		m_LevelId;
	char		m_Source;	// Was this request the result of a "StartInfo" message or a "Change Level" message
};

class MsgRemovePlayerRequest
{
public:
	char	m_Ban;	// Should we ban? If not, just kick
	char	m_Index;
	char	m_Name[vMAX_PLAYER_NAME_LEN + 1];
};

class MsgStartGameRequest
{
public:
	uint32	m_GameMode;
	uint32	m_TimeLimit;
	uint32	m_SkillLevel;
	uint32	m_FireballLevel;
	uint32	m_PlayerCollision;
	uint32	m_FriendlyFire;
	uint32	m_TeamMode;
	uint32	m_TargetScore;
	uint32	m_StopAtZero;
	uint32	m_CTFType;
};

class MsgCheatChecksum
{
public:
	uint32	m_ServerChecksum;
	uint32	m_ClientChecksum;
};

class MsgProjectile
{
public:
	uint32		m_Latency;
	Mth::Vector	m_Pos;
	Mth::Vector	m_Vel;
	uint32		m_Type;
	int			m_Radius;
	float		m_Scale;
	char		m_Id;
};

class MsgProjectileHit
{
public:
	char	m_Id;
	int		m_Damage;

};

class MsgCollideLost
{
public:
	char		m_Id;
	Mth::Vector	m_Offset;
};

class MsgEnterVehicle
{
public:
	char		m_Id;
	uint32		m_ControlType;
};

class MsgProceed
{
public:
	int			m_PublicIP;
	int			m_PrivateIP;
	int			m_Port;
	char		m_ServerName[GameNet::vMAX_SERVER_NAME_LEN + 1];
	char		m_MaxPlayers;
	char		m_Broadband;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet

#endif	// __GAMENET_GAMEMSG_H




================================================
FILE: Code/Sk/GameNet/GameNet.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet					 								**
**																			**
**	File name:		GameNet.cpp												**
**																			**
**	Created by:		02/01/01	-	spg										**
**																			**
**	Description:	Game-Side Network Functionality							**
**																			**
*****************************************************************************/

// start autoduck documentation
// @DOC gamenet
// @module gamenet | None
// @subindex Scripting Database
// @index script | gamenet
                          
/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
//#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#ifdef __PLAT_NGPS__
#include 
#include 
#include 
#include 
#endif

#ifdef __PLAT_XBOX__
#include 
#include 
#include 
#endif

#include 		   // we do various things with the skater, network play related


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

extern bool g_CheatsEnabled;

namespace GameNet
{



DefineSingletonClass( Manager, "Game Network Manager" )

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

#ifndef __PLAT_NGC__
extern	bool	 gEnteringFromMainMenu;
#endif		// __PLAT_NGC__

/*****************************************************************************
**								   Defines									**
*****************************************************************************/
#define USE_DNS	1
enum
{   
	vMAX_TOLERABLE_RESENDS = 20	// we'll allow 20 reseonds of messages to ready coonections but that's our limit
};

#define vTIME_BEFORE_STARTING_NETGAME   Tmr::Seconds( 3 )
#define vTIME_BEFORE_STARTING_GOALATTACK   Tmr::Seconds( 8 )
#define vAUTO_START_INTERVAL			Tmr::Seconds( 5 )
#define vLINK_CHECK_FREQUENCY			Tmr::Seconds( 1 )
#define vTAUNT_INTERVAL			Tmr::Seconds( 2 )
#define vMAX_FACE_DATA_SIZE		( Gfx::CFaceTexture::vTOTAL_CFACETEXTURE_SIZE )

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static Tmr::Time	s_time_to_start_game;
static Tmr::Time	s_auto_serve_start_time;
//static bool			s_waiting_for_game_to_start = false;
//static Tmr::Time	s_usage_tracking_start_time;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

Manager::Manager( void )
{
	Net::Manager * net_man = Net::Manager::Instance();
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	int i;

	

	net_man->SetMessageName( MSG_ID_SKATER_INPUT, "Skater Input" );
	net_man->SetMessageName( MSG_ID_PLAYER_CREATE, "Player Create" );
	net_man->SetMessageName( MSG_ID_JOIN_ACCEPTED, "Join Accepted" );
	net_man->SetMessageName( MSG_ID_OBJ_UPDATE_STREAM, "Object Update Stream" );
	net_man->SetMessageName( MSG_ID_NEW_ALIAS, "New Alias" );
	net_man->SetMessageName( MSG_ID_READY_QUERY, "Ready Query" );
	net_man->SetMessageName( MSG_ID_READY_RESPONSE, "Ready Response" );
	net_man->SetMessageName( MSG_ID_PAUSE, "Pause" );
	net_man->SetMessageName( MSG_ID_UNPAUSE, "Unpause" );
	net_man->SetMessageName( MSG_ID_PRIM_ANIM_START, "Primary Animation" );
	net_man->SetMessageName( MSG_ID_FLIP_ANIM, "Flip Animation" );
	net_man->SetMessageName( MSG_ID_ROTATE_SKATEBOARD, "Rotate Skateboard" );
	net_man->SetMessageName( MSG_ID_ROTATE_DISPLAY, "Rotate Display" );
	net_man->SetMessageName( MSG_ID_CLEAR_ROTATE_DISPLAY, "Clear Rotate Display" );
	net_man->SetMessageName( MSG_ID_CREATE_SPECIAL_ITEM, "Create Item" );
	net_man->SetMessageName( MSG_ID_DESTROY_SPECIAL_ITEM, "Destroy Item" );
	net_man->SetMessageName( MSG_ID_SET_WOBBLE_TARGET, "Set Wobble Target" );
	net_man->SetMessageName( MSG_ID_SET_WOBBLE_DETAILS, "Set Wobble Details" );
	net_man->SetMessageName( MSG_ID_SET_LOOPING_TYPE, "Set Looping Type" );
	net_man->SetMessageName( MSG_ID_SET_HIDE_ATOMIC, "Hide Atomic" );
	net_man->SetMessageName( MSG_ID_SET_ANIM_SPEED, "Set anim speed" );
	net_man->SetMessageName( MSG_ID_PLAYER_QUIT, "Player Quit" );	
	net_man->SetMessageName( MSG_ID_HOST_PROCEED, "Proceed Hosting" );
	net_man->SetMessageName( MSG_ID_GAMELIST_RESPONSE, "Gamelist" );
	net_man->SetMessageName( MSG_ID_HOST_QUIT, "Host Shutdown" );
	net_man->SetMessageName( MSG_ID_START_INFO, "Start Info" );
	net_man->SetMessageName( MSG_ID_GAME_INFO, "Game Info" );
	net_man->SetMessageName( MSG_ID_PLAY_SOUND, "Play Sound" );
	net_man->SetMessageName( MSG_ID_PLAY_LOOPING_SOUND, "Play Looping Sound" );
	net_man->SetMessageName( MSG_ID_SPARKS_ON, "Sparks On" );
	net_man->SetMessageName( MSG_ID_SPARKS_OFF, "Sparks Off" );
	net_man->SetMessageName( MSG_ID_BLOOD_ON, "Blood On" );
	net_man->SetMessageName( MSG_ID_BLOOD_OFF, "Blood Off" );
	net_man->SetMessageName( MSG_ID_SCORE, "Score Msg" );
	net_man->SetMessageName( MSG_ID_PANEL_MESSAGE, "Panel Message" );
	net_man->SetMessageName( MSG_ID_SCORE_UPDATE, "Score Update" );
	net_man->SetMessageName( MSG_ID_PROCEED_TO_PLAY, "Proceed To Play" );
	net_man->SetMessageName( MSG_ID_SKATER_COLLIDE_LOST, "Skater Col Lost" );
	net_man->SetMessageName( MSG_ID_SKATER_COLLIDE_WON, "Skater Col Win" );
	net_man->SetMessageName( MSG_ID_SKATER_HIT_BY_PROJECTILE, "Hit by projectile" );
	net_man->SetMessageName( MSG_ID_SKATER_PROJECTILE_HIT_TARGET, "Projectile Hit" );
	net_man->SetMessageName( MSG_ID_BAIL_DONE, "Bail done" );
	net_man->SetMessageName( MSG_ID_RUN_SCRIPT, "Run Script" );
	net_man->SetMessageName( MSG_ID_SPAWN_AND_RUN_SCRIPT, "SpawnRun Script" );
	net_man->SetMessageName( MSG_ID_OBSERVE, "Observe" );
	net_man->SetMessageName( MSG_ID_STEAL_MESSAGE, "Steal message" );
	net_man->SetMessageName( MSG_ID_CHAT, "Chat Message" );
	net_man->SetMessageName( MSG_ID_OBSERVE_PROCEED, "Proceed to Observe" );
	net_man->SetMessageName( MSG_ID_OBSERVE_REFUSED, "Observe Refused" );
	net_man->SetMessageName( MSG_ID_JOIN_PROCEED, "Proceed to Join" );
	net_man->SetMessageName( MSG_ID_JOIN_REQ, "Join Request" );
	net_man->SetMessageName( MSG_ID_KICKED, "Kick Message" );
	net_man->SetMessageName( MSG_ID_LANDED_TRICK, "Landed Trick" );
	net_man->SetMessageName( MSG_ID_PLAYER_INFO_ACK_REQ, "Player Ack Req" );
	net_man->SetMessageName( MSG_ID_PLAYER_INFO_ACK, "Player Info Ack" );
	net_man->SetMessageName( MSG_ID_NEW_KING, "New King" );
	net_man->SetMessageName( MSG_ID_LEFT_OUT, "Left Out" );
	net_man->SetMessageName( MSG_ID_MOVED_TO_RESTART, "Moved to Restart" );
	net_man->SetMessageName( MSG_ID_KING_DROPPED_CROWN, "Dropped Crown" );
	net_man->SetMessageName( MSG_ID_PLAYER_DROPPED_FLAG, "Dropped Flag" );
	net_man->SetMessageName( MSG_ID_RUN_HAS_ENDED, "Run ended" );
	net_man->SetMessageName( MSG_ID_GAME_OVER, "Game over" );
	net_man->SetMessageName( MSG_ID_OBSERVER_LOG_TRICK_OBJ, "Observer Log Trick Object" );
	net_man->SetMessageName( MSG_ID_OBSERVER_INIT_GRAFFITI_STATE, "Observer Init Graffiti State" );
	net_man->SetMessageName( MSG_ID_AUTO_SERVER_NOTIFICATION, "Auto-Server Notification" );
	net_man->SetMessageName( MSG_ID_FCFS_ASSIGNMENT, "FCFS Assignment" );
	net_man->SetMessageName( MSG_ID_FCFS_END_GAME, "FCFS End Game" );
	net_man->SetMessageName( MSG_ID_FCFS_SET_NUM_TEAMS, "FCFS Num Teams" );
	net_man->SetMessageName( MSG_ID_END_GAME, "End Game" );
	net_man->SetMessageName( MSG_ID_SELECT_GOALS, "Select Goals" );
	net_man->SetMessageName( MSG_ID_BEAT_GOAL, "Beat Goal" );
	net_man->SetMessageName( MSG_ID_STARTED_GOAL, "Started Goal" );
	net_man->SetMessageName( MSG_ID_TOGGLE_PROSET, "Toggle proset" );
	net_man->SetMessageName( MSG_ID_FCFS_TOGGLE_PROSET, "FCFS Toggle proset" );
	net_man->SetMessageName( MSG_ID_FCFS_TOGGLE_GOAL_SELECTION, "FCFS Toggle Goal" );
	net_man->SetMessageName( MSG_ID_KILL_TEAM_FLAGS, "Kill Team Flags" );
	net_man->SetMessageName( MSG_ID_WAIT_N_SECONDS, "Wait N Seconds" );
	net_man->SetMessageName( MSG_ID_CHEAT_CHECKSUM_REQUEST, "CC Request" );
	net_man->SetMessageName( MSG_ID_CHEAT_CHECKSUM_RESPONSE, "CC Response" );
    
	net_man->SetMessageFlags( MSG_ID_OBJ_UPDATE_STREAM, Net::mMSG_SIZE_UNKNOWN );
		
	for( i = 0; i < vMAX_LOCAL_CLIENTS; i++ )
	{
		m_client[i] = NULL;
		m_match_client = NULL;
	}
	
	m_server = NULL;
    
	m_metrics_on = false;
	m_scores_on = false;
	m_cheating_occurred = false;
	m_draw_player_names = true;
	m_sort_key = vSORT_KEY_NAME;
	m_host_mode = vHOST_MODE_SERVE;
	m_server_list_state = vSERVER_LIST_STATE_SHUTDOWN;
	m_next_server_list_state = vSERVER_LIST_STATE_SHUTDOWN;
    
	// initialize the network preferences
	m_network_preferences.Load( Script::GenerateCRC("default_network_preferences" ) );
	m_taunt_preferences.Load( Script::GenerateCRC("default_taunt_preferences" ) );

	m_timeout_connections_task = new Tsk::Task< Manager > ( s_timeout_connections_code, *this,
										Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_TIMEOUT_CONNECTIONS );
	m_render_metrics_task = new Tsk::Task< Manager > ( s_render_metrics_code, *this );
    m_auto_refresh_task = new Tsk::Task< Manager > ( s_auto_refresh_code, *this );
	m_auto_server_task = new Tsk::Task< Manager > ( s_auto_server_code, *this );
	m_render_scores_task = new Tsk::Task< Manager > ( s_render_scores_code, *this );
	m_client_add_new_players_task = new Tsk::Task< Manager > ( s_client_add_new_players_code, *this, 
										Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_CLIENT_ADD_NEW_PLAYERS );
	m_server_add_new_players_task = new Tsk::Task< Manager > ( s_server_add_new_players_code, *this, 
										Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_SERVER_ADD_NEW_PLAYERS );
    m_enter_chat_task = new Tsk::Task< Manager > ( s_enter_chat_code, *this );
	m_modem_state_task = new Tsk::Task< Manager > ( s_modem_state_code, *this );
	m_join_timeout_task = new Tsk::Task< Manager > ( s_join_timeout_code, *this );
	m_server_list_state_task = new Tsk::Task< Manager > ( s_server_list_state_code, *this );
	m_join_state_task = new Tsk::Task< Manager > ( s_join_state_code, *this );
	m_start_network_game_task = new Tsk::Task< Manager > ( s_start_network_game_code, *this );
	m_change_level_task = new Tsk::Task< Manager > ( s_change_level_code, *this );
	m_ctf_logic_task = new Tsk::Task< Manager > ( s_ctf_logic_code, *this );

	mlp_manager->AddLogicTask( *m_timeout_connections_task );

	m_last_modem_state = Net::vMODEM_STATE_DISCONNECTED;
	m_goals_data_size = 0;
	m_goals_level = 0;

#ifdef __PLAT_XBOX__
	m_XboxKeyRegistered = false;
	mpAuthMan = new AuthMan;
	mpBuddyMan = new BuddyMan;
	mpVoiceMan = new VoiceMan;
#endif

#ifdef __PLAT_NGPS__
	m_got_motd = false;
	mpLobbyMan = new LobbyMan;
	mpContentMan = new ContentMan;
	mpBuddyMan = new BuddyMan;
	mpStatsMan = new StatsMan;
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
#ifdef __PLAT_NGPS__
	delete mpLobbyMan;
	delete mpContentMan;
	delete mpBuddyMan;
	delete mpStatsMan;
#endif

	delete m_client_add_new_players_task;
	delete m_server_add_new_players_task;
	delete m_timeout_connections_task;
	delete m_render_metrics_task;
	delete m_enter_chat_task;
	delete m_server_list_state_task;
	delete m_join_state_task;
	delete m_start_network_game_task;
	delete m_change_level_task;
    delete m_auto_refresh_task;
#ifdef __PLAT_XBOX__
	delete mpAuthMan;
	delete mpBuddyMan;
	delete mpVoiceMan;
#endif // __PLAT_XBOX__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::free_all_players( void )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	PlayerInfo *player, *next;
	Lst::Search< PlayerInfo > sh;

	for( player = FirstPlayerInfo( sh, true ); player; player = next )
	{
		next = NextPlayerInfo( sh, true );
		// if this player has a skater
		if( player->m_Skater )
		{
			// and the skater is not on the default heap, or it's not a local player
			if ( player->m_Skater->GetHeapIndex() != 0 || !player->IsLocalPlayer() )
			{
				// then remove it (deletes the CSkater, which will unload any model)
				skate_mod->remove_skater( player->m_Skater );
			}
		}
		DestroyPlayer( player );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::free_all_pending_players( void )
{
	Lst::Node< NewPlayerInfo > *node, *next;
	NewPlayerInfo* new_info;
		
	

	Dbg_Printf( "Destroying Pending Players\n" );
	for( node = m_new_players.GetNext(); node; node = next )
	{
		next = node->GetNext();

		new_info = node->GetData();

		if( m_server )
		{
			Dbg_Printf( "Removing %s\n", new_info->Name );
			m_server->TerminateConnection( new_info->Conn );
		}
		
		delete node;
		delete new_info;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::s_observer_logic_code ( const Tsk::Task< PlayerInfo >& task )
{
	Manager * gamenet_man = Manager::Instance();

	// Dan: Proxim_Update use is now done through CProximTriggerComponents attached to the camera CCompositeObjects.  Thus, the observer's
	// camera should handle proxim updating	on its own.
	
	// PlayerInfo* current_player;
	// current_player = gamenet_man->GetCurrentlyObservedPlayer();
	// if( current_player )
	// {
		// Obj::CSkater* skater;
		// skater = local_player.m_cam->GetSkater();
	
		// if( skater )
		// {   
			// Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			// if( current_player->IsFullyIn())
			// {
				// local_player.m_cam->Update( false );
			
				// if( skate_mod->mpProximManager )
				// {   
					// if( local_player.m_cam )
					// {
						// Obj::Proxim_Update( skater, local_player.m_cam );
					// }
				// }
			// }
		// }
	// }

	if( gamenet_man->GetObserverCommand() == vOBSERVER_COMMAND_NEXT )
	{
		Dbg_Printf( "Got observer next command.....\n" );

		PlayerInfo*	player;
		
		player = gamenet_man->GetNextPlayerToObserve();
		gamenet_man->ObservePlayer( player );

		gamenet_man->SetObserverCommand( vOBSERVER_COMMAND_NONE );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_observer_input_logic_code ( const Inp::Handler < Manager >& handler )
{
    

	Manager&	man = handler.GetData();
	
	Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
	Front::CScreenElement* p_root_window = p_screen_elem_man->GetElement( Script::GenerateCRC( "root_window" ) , 
																			Front::CScreenElementManager::DONT_ASSERT );
	// If any operable menus are up, ignore input
	if( p_root_window )
	{
		Script::CStruct* pTags = new Script::CStruct();
		p_root_window->CopyTagsToScriptStruct( pTags );
		uint32 menu_state;
		pTags->GetChecksum( "menu_state", &menu_state, Script::NO_ASSERT );
		delete pTags;
		if ( menu_state == Script::GenerateCRC( "on" ) )
		{
			return;
		}
	}

	if( handler.m_Input->m_Makes & Inp::Data::mD_X )
	{
		Dbg_Printf( "Got X, inserting command....\n" );
		man.SetObserverCommand( vOBSERVER_COMMAND_NEXT );
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_auto_server_code( const Tsk::Task< Manager >& task )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Manager& man = task.GetData();

	// Give it a couple of seconds before activating
	if(( Tmr::GetTime() - s_auto_serve_start_time ) < Tmr::Seconds( 2 ))
	{
		return;
	}
	if( ( skate_mod->GetGameMode()->GetNameChecksum() != Script::GenerateCRC( "netlobby" )))
	{
		return;
	}

	if( man.GetNumPlayers() < 2 )
	{
		return;
	}

	if( man.m_waiting_for_game_to_start )
	{
		return;
	}

	if(( Tmr::GetTime() - man.m_lobby_start_time ) > vAUTO_START_INTERVAL )
	{
        Dbg_Printf( "Starting Network Game....\n" );
		Script::RunScript( "LoadPendingPlayers" );
		Script::RunScript( "StartNetworkGame" );
		man.m_waiting_for_game_to_start = true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_enter_chat_code( const Tsk::Task< Manager >& task )
{
	int num_chars;
	char makes[256];
	static Tmr::Time s_last_taunt_time = 0;
		
	num_chars = SIO::KeyboardRead( makes );

	if( num_chars > 0 )
	{
		// Space and enter bring up the chat interface
		if( ( makes[0] == SIO::vKB_ENTER ) ||
			( makes[0] == 32 ))
		{
			Script::CStruct* pParams;
			
			pParams = new Script::CStruct;
			pParams->AddChecksum( Script::GenerateCRC( "id" ), Script::GenerateCRC( "keyboard_anchor" ));
			
			// Enter and space act as "choose" only if you're not currently using the on-screen keyboard
			if( Obj::ScriptObjectExists( pParams, NULL ) == false )
			{
				pParams->Clear();
				pParams->AddChecksum( Script::GenerateCRC( "id" ), Script::GenerateCRC( "current_menu_anchor" ));
				if( Obj::ScriptObjectExists( pParams, NULL ) == false )
				{
					pParams->Clear();
					pParams->AddChecksum( Script::GenerateCRC( "id" ), Script::GenerateCRC( "dialog_box_anchor" ));
					if( Obj::ScriptObjectExists( pParams, NULL ) == false )
					{
						Script::RunScript( "enter_kb_chat" );
						SIO::KeyboardClear();
					}
				}
			}
		}
		else if( makes[0] == SIO::vKB_F1 )
		{
			if(( Tmr::GetTime() - s_last_taunt_time ) > vTAUNT_INTERVAL )
			{
				Script::CStruct* pParams;

				pParams = new Script::CStruct;
				pParams->AddChecksum( CRCD(0xb53d0e0f,"string_id"), CRCD(0xe5fd359,"props_string"));
				Script::RunScript( "SendTauntMessage", pParams );
				delete pParams;

				s_last_taunt_time = Tmr::GetTime();
			}
		}
		else if( makes[0] == SIO::vKB_F2 )
		{
			if(( Tmr::GetTime() - s_last_taunt_time ) > vTAUNT_INTERVAL )
			{
				Script::CStruct* pParams;

				pParams = new Script::CStruct;
				pParams->AddChecksum( CRCD(0xb53d0e0f,"string_id"), CRCD(0xbeea3518,"your_daddy_string"));
				Script::RunScript( "SendTauntMessage", pParams );
				delete pParams;

				s_last_taunt_time = Tmr::GetTime();
			}
		}
		else if( makes[0] == SIO::vKB_F3 )
		{
			if(( Tmr::GetTime() - s_last_taunt_time ) > vTAUNT_INTERVAL )
			{
				Script::CStruct* pParams;

				pParams = new Script::CStruct;
				pParams->AddChecksum( CRCD(0xb53d0e0f,"string_id"), CRCD(0x4525adbd,"get_some_string"));
				Script::RunScript( "SendTauntMessage", pParams );
				delete pParams;

				s_last_taunt_time = Tmr::GetTime();
			}
		}
		else if( makes[0] == SIO::vKB_F4 )
		{
			if(( Tmr::GetTime() - s_last_taunt_time ) > vTAUNT_INTERVAL )
			{
				Script::CStruct* pParams;

				pParams = new Script::CStruct;
				pParams->AddChecksum( CRCD(0xb53d0e0f,"string_id"), CRCD(0xa36dbee1,"no_way_string"));
				Script::RunScript( "SendTauntMessage", pParams );
				delete pParams;

				s_last_taunt_time = Tmr::GetTime();
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_join_state_code( const Tsk::Task< Manager >& task )
{
	Manager& man = task.GetData();
		
	switch( man.GetJoinState())
	{   
		case vJOIN_STATE_TRYING_PASSWORD:
		case vJOIN_STATE_WAITING:
		{   
			break;
		}
		case vJOIN_STATE_CONNECTING:
		{   
			Script::RunScript( "CreateConnectingDialog" );
			
			man.SetJoinState( vJOIN_STATE_WAITING );
			break;
		}
		case vJOIN_STATE_JOINING:
		{
			Net::Client* client;

			client = man.GetClient( 0 );
			if( !client->IsLocal())
			{
				Script::RunScript( "CreateJoiningDialog" );
			}
					
			man.SetJoinState( vJOIN_STATE_WAITING );
			break;
		}
		case vJOIN_STATE_JOINING_WITH_PASSWORD:
		{
			Script::RunScript( "CreateTryingPasswordDialog" );
			
			man.SetJoinState( vJOIN_STATE_TRYING_PASSWORD );
			break;
		}
		case vJOIN_STATE_REFUSED:
		{
			Script::CStruct* p_structure = new Script::CStruct;
			p_structure->AddChecksum( "reason", man.m_conn_refused_reason );

			man.m_join_state_task->Remove();

			Script::RunScript( "CreateJoinRefusedDialog", p_structure );
			delete p_structure;
			break;
		}
		case vJOIN_STATE_GOT_PLAYERS:
		{
			Lst::Search< NewPlayerInfo > sh;
			NewPlayerInfo* new_player;		
			
			// If we're observing, we need to remove our skater
			for( new_player = man.FirstNewPlayerInfo( sh ); new_player; new_player = man.NextNewPlayerInfo( sh ))
			{
				if( new_player->Flags & PlayerInfo::mLOCAL_PLAYER )
				{
					if( new_player->Flags & PlayerInfo::mOBSERVER )
					{
						Mdl::Skate * skate_mod = Mdl::Skate::Instance();

						Obj::CSkater* skater;
						skater = skate_mod->GetLocalSkater();
						Dbg_Assert( skater );
						skate_mod->remove_skater( skater );
						man.ObservePlayer( NULL );
					}
					break;
				}
			}
			
			Script::RunScript( "entered_network_game" );

			man.SetJoinState( vJOIN_STATE_WAITING_FOR_START_INFO );
			break;
		}
		case vJOIN_STATE_WAITING_FOR_START_INFO:
			break;
		case vJOIN_STATE_CONNECTED:
		{
			Script::RunScript( "dialog_box_exit" );
			man.m_join_timeout_task->Remove();
		}
		// Fall-through intentional
		case vJOIN_STATE_FINISHED:
		{
			man.m_join_state_task->Remove();
			break;
		}
		default:
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_ctf_logic_code( const Tsk::Task< Manager >& task )
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if( skate_mod->GetGameMode()->GetNameChecksum() != Script::GenerateCRC( "netctf" ))
	{
		Dbg_MsgAssert( 0, ( "Shouldn't be running CTF logic in non-ctf game" ));
		return;
	}
	
	for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
	{
		if( player->HasCTFFlag())
		{
			int team;
			uint32 obj_id;
			Obj::CMovingObject* obj;
			Obj::CMovingObject* moving_obj;

			team = player->HasWhichFlag();
			switch( team )
			{
				case vTEAM_RED:
					obj_id = Script::GenerateCRC( "TRG_CTF_Red" );
					break;
				case vTEAM_BLUE:
					obj_id = Script::GenerateCRC( "TRG_CTF_Blue" );
					break;
				case vTEAM_GREEN:
					obj_id = Script::GenerateCRC( "TRG_CTF_Green" );
					break;
			case vTEAM_YELLOW:
					obj_id = Script::GenerateCRC( "TRG_CTF_Yellow" );
					break;
				default:
					Dbg_Assert( 0 );
					obj_id = 0;
					break;
			}

			obj = (Obj::CMovingObject *) Obj::ResolveToObject( obj_id );
			if( obj == NULL )
			{
				return;
			}

			moving_obj = static_cast( obj );

			Gfx::CSkeleton* pSkeleton;
			pSkeleton = player->m_Skater->GetSkeleton();
			Dbg_Assert( pSkeleton );

			Mth::Matrix& flag_matrix = moving_obj->GetMatrix();
			pSkeleton->GetBoneMatrix( CRCD(0xddec28af,"Bone_Head"), &flag_matrix );
		
			flag_matrix.RotateXLocal( 90.0f );

			// raise the flag by 1 foot
			flag_matrix.TranslateLocal( Mth::Vector( 0.0f, FEET(1.0f), 0.0f, 0.0f ) );
		
			Mth::Vector flag_offset = flag_matrix[Mth::POS];
			flag_matrix[Mth::POS] = Mth::Vector( 0.0f, 0.0f, 0.0f, 1.0f );
		
			// for some reason, the below doesn't work as expected.
			// so instead, use the skater's root matrix for the orientation
			flag_matrix = flag_matrix * player->m_Skater->GetMatrix();
			//		crown.m_matrix = crown.mp_king->GetMatrix();

			moving_obj->m_pos = player->m_Skater->GetMatrix().Transform( flag_offset );
			moving_obj->m_pos += player->m_Skater->m_pos;

// Mick:  Removed this as it no longer does anything apart from copying over the display matrix
// if that's still needed then do it here
// Might also possibly have some problem with shadows being left behind, but that just indicates the need
// for a proper LOD system for shadows
//			moving_obj->MovingObj_Update();
// Mick:  Like this, but I don't think it's needed  		
//			moving_obj->m_display_matrix = moving_obj->m_matrix;
		
	//		Gfx::AddDebugStar( crown.m_pos, 10, 0x0000ffff, 1 );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_start_network_game_code( const Tsk::Task< Manager >& task )
{
	//Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Manager& man = task.GetData();
	static bool showing_wait_screen = false;
                                                                                             
	if( man.NumPartiallyLoadedPlayers() > 0 )
	{
		if( man.OnServer())
		{
			if( showing_wait_screen == false )
			{
				Front::CScreenElementManager* pManager = Front::CScreenElementManager::Instance();
				Front::CScreenElementPtr p_elem = pManager->GetElement( CRCD(0xf53d1d83,"current_menu_anchor"), Front::CScreenElementManager::DONT_ASSERT );
				if( !p_elem )
				{
					Script::RunScript( "CreateWaitForPlayersDialog" );
					showing_wait_screen = true;
				}
			}
		}
		else
		{
			if( showing_wait_screen == false )
			{
				man.CreateNetPanelMessage( true, Script::GenerateCRC("net_message_game_will_start"),
										   NULL, NULL, NULL, "netexceptionprops" );
				showing_wait_screen = true;
			}
		}

		return;
	}
    
	if( ScriptAllPlayersAreReady( NULL, NULL ) == false )
	{
		return;
	}

	if(( Tmr::GetTime() < s_time_to_start_game ))
	{
		return;
	}

	showing_wait_screen = false;
	man.StartNetworkGame();
	man.m_start_network_game_task->Remove();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_modem_state_code( const Tsk::Task< Manager >& task )
{
	Net::Manager * net_man = Net::Manager::Instance();
	int new_modem_state;
	Manager& man = task.GetData();

	new_modem_state = net_man->GetModemState();
	
	// Check for positive edge
	if( new_modem_state != man.m_last_modem_state )
	{
		Script::CStruct* pStructure = new Script::CStruct;
		
		switch( new_modem_state )
		{
			case Net::vMODEM_STATE_DIALING:
			{
				char dial_msg[128];

                if( net_man->GetConnectionType() == Net::vCONN_TYPE_PPPOE )
				{
					sprintf( dial_msg, "%s", Script::GetString("net_modem_state_conencting"));
				}
				else
				{
					sprintf( dial_msg, "%s %s", Script::GetString("net_modem_state_dialing"), 
				   							net_man->GetISPPhoneNumber());
				}

				pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, dial_msg );
				Script::RunScript( "create_modem_state_dialog", pStructure );
                
				Dbg_Printf( "In modem state code. Got state dialing\n" );
				break;
			}
			case Net::vMODEM_STATE_CONNECTED:
			{
				pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_modem_state_connected" ));
				Script::RunScript( "create_modem_state_dialog", pStructure );

				Dbg_Printf( "In modem state code. Got state connected\n" );
				break;
			}
			case Net::vMODEM_STATE_LOGGED_IN:
			{
				char conn_msg[128];

				Dbg_Printf( "In modem state code. Got state logged in\n" );
				if( net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM )
				{
					sprintf( conn_msg, "%s %d bps", Script::GetString("net_modem_state_logged_in"), 
										net_man->GetModemBaudRate());
				}
				else
				{
					sprintf( conn_msg, "%s", Script::GetString("net_modem_state_logged_in"));
				}
				
				pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, conn_msg );
				Script::RunScript( "create_modem_final_state_dialog", pStructure );
				break;
			}
			case Net::vMODEM_STATE_DISCONNECTING:
			{
				pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_modem_state_disconnecting" ));
				Script::RunScript( "create_modem_status_dialog", pStructure );
				

				Dbg_Printf( "In modem state code. Got state disconnecting\n" );
				break;
			}
			case Net::vMODEM_STATE_HANGING_UP:
			{
				pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_modem_state_hanging_up" ));
				Script::RunScript( "create_modem_status_dialog", pStructure );

				Dbg_Printf( "In modem state code. Got state hanging up\n" );
				break;
			}
			case Net::vMODEM_STATE_DISCONNECTED:
			{
				pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_modem_state_disconnected" ));
				Script::RunScript( "create_modem_final_state_dialog", pStructure );

				Dbg_Printf( "In modem state code. Got state disconnected\n" );
				break;
			}
			case Net::vMODEM_STATE_READY_TO_TRANSMIT:
			{
#ifdef __PLAT_NGPS__
				Dbg_Printf( "Removing modem state task\n" );
				man.RemoveModemStateTask();
				
				if( man.m_connection_success_script )
				{
					Script::RunScript( man.m_connection_success_script );
				}
#endif		// __PLAT_NGPS__
				break;
			}
			case Net::vMODEM_STATE_ERROR:
			{
#ifdef __PLAT_NGPS__
				uint32 modem_error;

				switch( net_man->GetModemError())
				{
					case SNTC_ERR_NOMODEM:
						modem_error = Script::GenerateCRC("net_modem_error_no_modem");
						break;
					case SNTC_ERR_TIMEOUT:
						modem_error = Script::GenerateCRC("net_modem_error_timeout");
						break;
					case SNTC_ERR_CONNECT:
					{
						Prefs::Preferences* prefs;
						uint32 config_type;

						prefs = man.GetNetworkPreferences();
						config_type = prefs->GetPreferenceChecksum( CRCD(0xe381f426,"config_type"), 
																	CRCD(0x21902065,"checksum") );
						if( config_type == CRCD(0x99452df9,"config_manual"))
						{
							modem_error = Script::GenerateCRC("net_modem_error_during_connect");
						}
						else
						{
							modem_error = Script::GenerateCRC("net_modem_error_during_connect_ync");
						}
						
						break;
					}
					case SNTC_ERR_BUSY:
						modem_error = Script::GenerateCRC("net_modem_error_busy");
						break;
					case SNTC_ERR_NOCARRIER:
					case SNTC_ERR_NOANSWER:
						modem_error = Script::GenerateCRC("net_modem_error_no_connect");
						break;
					case SNTC_ERR_NODIALTONE:
						modem_error = Script::GenerateCRC("net_modem_error_no_dialtone");
						break;
					default:
						modem_error = Script::GenerateCRC("net_modem_error_no_connect");
						break;
				}

				Dbg_Printf( "In modem state error: %s\n", Script::GetString( modem_error ));
				pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( modem_error ));
				Script::RunScript( "create_modem_final_state_dialog", pStructure );
#endif		// __PLAT_NGPS__
				break;
			}
			default:
				Dbg_Assert( 0 );
				break;
		}
		
		delete pStructure;
	}

	man.m_last_modem_state = new_modem_state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_client_add_new_players_code( const Tsk::Task< Manager >& task )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());
	
	Manager& man = task.GetData();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* skater;
	Lst::Node< NewPlayerInfo > *node, *next;
	NewPlayerInfo* new_player;
	Net::Conn* server_conn;
	Lst::Search< Net::Conn > sh;
	bool added_player;
	int i;
                         
	added_player = false;
	for( node = man.m_new_players.GetNext(); node; node = next )
	{
		next = node->GetNext();

		new_player = node->GetData();

		if( new_player->AllowedJoinFrame > man.m_client[0]->m_FrameCounter )
		{
			break;
		}

		// GJ:  the "new_player" structure should already
		// contain the desired appearance

		man.ClientAddNewPlayer( new_player );

		delete new_player;
		delete node;

		added_player = true;
	}

	if( added_player )
	{
        for( i = 0; i < vMAX_LOCAL_CLIENTS; i++ )
		{
			if( man.m_client[i] )
			{
				server_conn = man.m_client[i]->FirstConnection( &sh );
				if( server_conn )
				{
					server_conn->UpdateCommTime();	// update the current comm time so it doesn't time out after
													// laoding the skater
				}
			}
		}
		for( i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
		{
			skater = skate_mod->GetSkater( i );
			if( skater )
			{   
				skater->Resync();
			}
		}
        
		man.RespondToReadyQuery();
	}
	Mem::Manager::sHandle().PopContext();
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_server_add_new_players_code( const Tsk::Task< Manager >& task )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());
	Manager& man = task.GetData();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Lst::Node< NewPlayerInfo > *node, *next;
	NewPlayerInfo* new_info;
	Net::MsgDesc msg_desc;
	Net::Manager* net_man = Net::Manager::Instance();

	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	for( node = man.m_new_players.GetNext(); node; node = next )
	{
		Obj::CSkater* skater;
		MsgNewPlayer new_player_msg;
		Lst::Search< PlayerInfo > sh;
		PlayerInfo* player, *new_player;
		MsgReady ready_msg;
		bool narrowband;
				
		next = node->GetNext();

		new_info = node->GetData();
	
		if( new_info->AllowedJoinFrame > man.m_server->m_FrameCounter )
		{
			break;
		}

		narrowband = ( new_info->Conn->GetBandwidthType() == Net::Conn::vNARROWBAND ) ||
						( net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM );

		// Mick: moved onto skater info heap, to prevent fragmentation
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());
		new_player = man.NewPlayer( NULL, new_info->Conn, new_info->Flags );
		Mem::Manager::sHandle().PopContext();
		
		strcpy( new_player->m_Name, new_info->Name );
		new_player->CopyProfile( new_info->mpSkaterProfile );
		new_player->m_jump_in_frame = new_info->JumpInFrame;
		new_player->m_Profile = new_info->Profile;
		new_player->m_Rating = new_info->Rating;
		new_player->m_VehicleControlType = new_info->VehicleControlType;

		man.AddPlayerToList( new_player );
		ready_msg.m_Time = Tmr::GetTime();
        
		if( !new_player->IsLocalPlayer())
		{
			// Send the new player's create skater command in such a way that he knows it's his own skater
			new_player_msg.m_ObjId = new_info->ObjID;
			new_player_msg.m_Team = new_player->m_Team;
			new_player_msg.m_Profile = new_player->m_Profile;
			new_player_msg.m_Rating = new_player->m_Rating;
			new_player_msg.m_VehicleControlType = new_player->m_VehicleControlType;
			new_player_msg.m_Score = 0;
			strcpy( new_player_msg.m_Name, new_info->Name );
            
			new_player_msg.m_Flags = PlayerInfo::mLOCAL_PLAYER;
			if( new_player->m_flags.TestMask( PlayerInfo::mJUMPING_IN ))
			{
				new_player_msg.m_Flags |= PlayerInfo::mJUMPING_IN;
			}
			if( new_player->IsObserving())
			{
				new_player_msg.m_Flags |= PlayerInfo::mOBSERVER;
			}
			
			msg_desc.m_Data = NULL;
			msg_desc.m_Length = 0;
			msg_desc.m_Id = MSG_ID_JOIN_ACCEPTED;
			msg_desc.m_Priority = Net::HIGHEST_PRIORITY;
			man.m_server->EnqueueMessage( new_player->GetConnHandle(), &msg_desc );
		
			msg_desc.m_Data = &new_player_msg;
			msg_desc.m_Length = sizeof( MsgNewPlayer ) - vMAX_APPEARANCE_DATA_SIZE;
			msg_desc.m_Id = MSG_ID_PLAYER_CREATE;
			man.m_server->EnqueueMessage( new_player->GetConnHandle(), &msg_desc );
		}
		

		if( !new_player->IsObserving())
		{
			// Send the new player's info to existing players
			for( player = man.FirstPlayerInfo( sh, true ); player; 
					player = man.NextPlayerInfo( sh, true ))
			{
				// don't send the new player's info to the new player. We sent it separately and specially
				// Also, don't send to the server (ourselves)
				if(( new_player != player ) &&
				   ( !player->IsLocalPlayer()))
				{   
					new_player_msg.m_ObjId = new_info->ObjID;
					new_player_msg.m_Flags = 0;
					new_player_msg.m_Team = new_player->m_Team;
					new_player_msg.m_Profile = new_player->m_Profile;
					new_player_msg.m_Rating = new_player->m_Rating;
					new_player_msg.m_VehicleControlType = new_player->m_VehicleControlType;
					new_player_msg.m_Score = 0;
					strcpy( new_player_msg.m_Name, new_player->m_Name );
					Dbg_Assert( new_player->mp_SkaterProfile );
					uint32 data_size = new_player->mp_SkaterProfile->WriteToBuffer(new_player_msg.m_AppearanceData, vMAX_APPEARANCE_DATA_SIZE );

					msg_desc.m_Data = &new_player_msg;
					msg_desc.m_Length = sizeof( MsgNewPlayer ) - vMAX_APPEARANCE_DATA_SIZE + data_size;
					msg_desc.m_Id = MSG_ID_PLAYER_CREATE;
					msg_desc.m_Priority = Net::NORMAL_PRIORITY;
					msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
					msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
					man.m_server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
				}
			}
		}

		// Players that are "jumping in" already have current players' info
		// Only send them other players who jumped in at the same time
		if( new_player->m_flags.TestMask( PlayerInfo::mJUMPING_IN ))
		{
			for( player = man.FirstPlayerInfo( sh ); player; 
					player = man.NextPlayerInfo( sh ))
			{
				if( ( new_player != player ) &&
					( player->m_jump_in_frame == new_player->m_jump_in_frame ))
				{   
					new_player_msg.m_ObjId = player->m_Skater->GetID();
					new_player_msg.m_Flags = ( player->m_flags & ( PlayerInfo::mKING_OF_THE_HILL | PlayerInfo::mHAS_ANY_FLAG ));
					new_player_msg.m_Team = player->m_Team;
					new_player_msg.m_Profile = player->m_Profile;
					new_player_msg.m_Rating = player->m_Rating;
					new_player_msg.m_VehicleControlType = player->m_VehicleControlType;
					new_player_msg.m_Score = 0;
					strcpy( new_player_msg.m_Name, player->m_Name );
					Dbg_Assert( player->mp_SkaterProfile );
					uint32 data_size = player->mp_SkaterProfile->WriteToBuffer(new_player_msg.m_AppearanceData, 
																	vMAX_APPEARANCE_DATA_SIZE, narrowband );

					msg_desc.m_Data = &new_player_msg;
					msg_desc.m_Length = sizeof( MsgNewPlayer ) - vMAX_APPEARANCE_DATA_SIZE + data_size;
					msg_desc.m_Id = MSG_ID_PLAYER_CREATE;
					msg_desc.m_Priority = Net::NORMAL_PRIORITY;
					msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
					msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
					man.m_server->EnqueueMessage( new_player->GetConnHandle(), &msg_desc );
				}
			}
		}
		else
		{
			// Send the new player the info on all current players( except himself )
			for( player = man.FirstPlayerInfo( sh ); player; 
					player = man.NextPlayerInfo( sh ))
			{
				// don't send the new player's info to the new player. We sent it separately and specially
				// also only send these messages to remote clients as we've already loaded the player here
				if( ( new_player != player ) &&
					( !new_player->IsLocalPlayer()))
				{   
					new_player_msg.m_ObjId = player->m_Skater->GetID();
					new_player_msg.m_Flags = ( player->m_flags & ( PlayerInfo::mKING_OF_THE_HILL | PlayerInfo::mHAS_ANY_FLAG ));
					new_player_msg.m_Team = player->m_Team;
					new_player_msg.m_Profile = player->m_Profile;
					new_player_msg.m_Rating = player->m_Rating;
					new_player_msg.m_VehicleControlType = player->m_VehicleControlType;
					new_player_msg.m_Score = man.GetPlayerScore( player->m_Skater->GetID() );
					strcpy( new_player_msg.m_Name, player->m_Name );
					Dbg_Assert( player->mp_SkaterProfile );
					uint32 data_size = player->mp_SkaterProfile->WriteToBuffer(new_player_msg.m_AppearanceData, 
																		vMAX_APPEARANCE_DATA_SIZE, narrowband );

					msg_desc.m_Data = &new_player_msg;
					msg_desc.m_Length = sizeof( MsgNewPlayer ) - vMAX_APPEARANCE_DATA_SIZE + data_size;
					msg_desc.m_Id = MSG_ID_PLAYER_CREATE;
					msg_desc.m_Priority = Net::NORMAL_PRIORITY;
					msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
					msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
					man.m_server->EnqueueMessage( new_player->GetConnHandle(), &msg_desc );
				}
			}
			
			MsgIntInfo level_msg;
			uint32 level;

			level = (int) man.GetNetworkLevelId();
			if( !new_player->IsLocalPlayer())
			{
				if( level == CRCD(0xb664035d,"Load_Sk5Ed_gameplay"))
				{
					Ed::CParkManager::sInstance()->WriteCompressedMapBuffer();

					man.m_server->StreamMessage( new_player->GetConnHandle(), MSG_ID_LEVEL_DATA, Ed::CParkManager::COMPRESSED_MAP_SIZE, 
							   Ed::CParkManager::sInstance()->GetCompressedMapBuffer(), "level data", vSEQ_GROUP_PLAYER_MSGS,
												 false, true );
												 
					man.m_server->StreamMessage( new_player->GetConnHandle(), MSG_ID_RAIL_DATA, Obj::GetRailEditor()->GetCompressedRailsBufferSize(), 
							   Obj::GetRailEditor()->GetCompressedRailsBuffer(), "rail data", vSEQ_GROUP_PLAYER_MSGS,
												 false, true );
												 
				}
			}

			level_msg.m_Data = (int) level;
			Dbg_Printf( "Sending player info ack request\n" );

			msg_desc.m_Data = &level_msg;
			msg_desc.m_Length = sizeof( MsgIntInfo );
			msg_desc.m_Id = MSG_ID_PLAYER_INFO_ACK_REQ;
			msg_desc.m_Priority = Net::NORMAL_PRIORITY;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
			man.m_server->EnqueueMessage( new_player->GetConnHandle(), &msg_desc );
		}
		
		if( !new_player->IsObserving())
		{   
			int skater_num;

			// GJ:  the "new_info" should contain the desired
			// appearance by this point
			
			// Mick: if this is a local player in a net game
			// then it must be skater 0
			if( new_player->IsLocalPlayer() && man.InNetGame())
			{
				skater_num = 0;
			}
			else
			{
				// Otherwise, for 2P games, nonlocal net players, and P1, 
				skater_num = skate_mod->GetNumSkaters();
			}
			skater = skate_mod->add_skater( new_info->mpSkaterProfile, new_player->m_Conn->IsLocal(), 
											new_info->ObjID, skater_num );
			new_player->SetSkater( skater );
			if( !new_player->IsLocalPlayer())
			{
				skater->RemoveFromCurrentWorld();
			}

			for( player = man.FirstPlayerInfo( sh, true ); player; player = man.NextPlayerInfo( sh, true ))
			{
				if( new_player == player )
				{
					continue;
				}

				msg_desc.m_Data = &ready_msg;
				msg_desc.m_Length = sizeof( MsgReady );
				msg_desc.m_Id = MSG_ID_READY_QUERY;
				msg_desc.m_Priority = Net::NORMAL_PRIORITY;
				msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
				msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
				man.m_server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
				if( !player->IsLocalPlayer() && !player->IsObserving())
				{
					player->m_Skater->Resync();
					player->MarkAsNotReady( ready_msg.m_Time );
				}
			}
		}

		// Mark the client as "not ready" so we don't bombard him with non-important messages
		// until he has fully loaded all that he needs to load
		msg_desc.m_Data = &ready_msg;
		msg_desc.m_Length = sizeof( MsgReady );
		msg_desc.m_Id = MSG_ID_READY_QUERY;
		msg_desc.m_Priority = Net::NORMAL_PRIORITY;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
		man.m_server->EnqueueMessage( new_player->GetConnHandle(), &msg_desc );
		new_player->MarkAsNotReady( ready_msg.m_Time );
        
		delete new_info;
		delete node;
	}
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_render_metrics_code( const Tsk::Task< Manager >& task )
{
	Lst::Search< Net::Conn > sh;
	Net::Conn* conn;
	int conn_num;
	Net::Metrics* metrics_in, *metrics_out;
	char buff[ 1024 ];
	int i;

    Manager& man = task.GetData();


    for( i = 0; i < 2; i++ )
	{
		if( man.m_client[0] && !man.m_client[0]->IsLocal())
		{
			conn_num = 0;
			
	
			for( conn = man.m_client[0]->FirstConnection( &sh ); conn;
					conn = man.m_client[0]->NextConnection( &sh ))
			{
				if( conn->IsLocal())
				{
					continue;
				}
				metrics_in = conn->GetInboundMetrics();
				metrics_out = conn->GetOutboundMetrics();
				
				sprintf( buff, "(%d) Lag: %d Resends: %d Bps: %d %d", 
						 conn_num, 
						 conn->GetAveLatency(),
						 conn->GetNumResends(),
						 metrics_in->GetBytesPerSec(),
						 metrics_out->GetBytesPerSec());
					
				Script::CStruct* pParams;
				pParams = new Script::CStruct;
				pParams->AddString( "text", buff );
				Script::RunScript( "update_net_metrics", pParams );

				delete pParams;
				conn_num++;
			}
		}
		
		if( man.m_server )
		{
			conn_num = 0;
			for( conn = man.m_server->FirstConnection( &sh ); conn;
					conn = man.m_server->NextConnection( &sh ))
			{
				if( conn->IsLocal())
				{
					continue;
				}

				metrics_in = conn->GetInboundMetrics();
				metrics_out = conn->GetOutboundMetrics();
				
				sprintf( buff, "(%d) Lag: %d Resends: %d Bps: %d %d", 
						 conn_num, 
						 conn->GetAveLatency(),
						 conn->GetNumResends(),
						 metrics_in->GetBytesPerSec(),
						 metrics_out->GetBytesPerSec());

				Script::CStruct* pParams;
				pParams = new Script::CStruct;
				pParams->AddString( "text", buff );
				Script::RunScript( "update_net_metrics", pParams );

				delete pParams;
					
				conn_num++;
			}
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_timeout_connections_code( const Tsk::Task< Manager >& task )
{
	Lst::Search< PlayerInfo > sh;
	PlayerInfo *player, *next;
	Manager& man = task.GetData();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	bool time_out;
	
#ifdef __PLAT_XBOX__
	static Tmr::Time s_last_link_check = 0;
	
	if(	( man.InNetMode()) &&
		(( Tmr::GetTime() - s_last_link_check ) > vLINK_CHECK_FREQUENCY ))
	{
		if( man.InNetGame())
		{
			s_last_link_check = Tmr::GetTime();
			DWORD dwStatus = XNetGetEthernetLinkStatus();
			
        	if(( dwStatus & XNET_ETHERNET_LINK_ACTIVE ) == 0 )
			{
				Front::CScreenElementManager* pManager = Front::CScreenElementManager::Instance();
				Front::CScreenElementPtr p_elem = pManager->GetElement( Script::GenerateCRC( "link_lost_dialog_anchor" ), Front::CScreenElementManager::DONT_ASSERT );
				if( !p_elem )
				{
					Script::RunScript( "create_link_unplugged_dialog" );
				}
			}
		}
		else
		{
			s_last_link_check = Tmr::GetTime();
			DWORD dwStatus = XNetGetEthernetLinkStatus();
			
        	if(( dwStatus & XNET_ETHERNET_LINK_ACTIVE ) == 0 )
			{
				Front::CScreenElementManager* pManager = Front::CScreenElementManager::Instance();
				Front::CScreenElementPtr p_elem = pManager->GetElement( CRCD(0x3b56e746,"dialog_box_anchor"), Front::CScreenElementManager::DONT_ASSERT );
				if( !p_elem )
				{
					Script::RunScript( "create_link_unplugged_front_end_dialog" );
				}
			}
		}
	}
	
#endif	

	if( man.OnServer())
	{   
		for( player = man.FirstPlayerInfo( sh, true ); player; player = next )
		{   
			time_out = false;
			next = man.NextPlayerInfo( sh, true );
			if( player->m_Conn->IsLocal())
			{
				continue;
			}

			if( player->m_Conn->TestStatus( Net::Conn::mSTATUS_READY ))
			{   
				if( player->m_Conn->GetTimeElapsedSinceCommunication() > vCONNECTION_TIMEOUT )
				{
					time_out = true;
				}
				else
				{
					if( player->m_Conn->GetNumResends() > vMAX_TOLERABLE_RESENDS )
					{
						Obj::CSkater* skater;
						bool observing;

						skater = player->m_Skater;
						observing = player->IsObserving();
						man.DropPlayer( player, vREASON_BAD_CONNECTION );
						if( !observing)
						{
							skate_mod->remove_skater( skater );
						}
						continue;
					}
				}
			}
			else
			{
				if( player->m_Conn->GetTimeElapsedSinceCommunication() > vNOT_READY_TIMEOUT )
				{
					time_out = true;
				}
			}
			if( time_out )
			{
				Obj::CSkater* skater;
				bool observing;

#ifdef __NOPT_ASSERT__
				unsigned int cur_time = Tmr::GetTime();
				unsigned int last_time = player->m_Conn->GetLastCommTime();
				Dbg_Printf( "Gamenet : Timing client out. Current time (%d) last comm time (%d)\n",
								cur_time, last_time );
#endif				
				skater = player->m_Skater;
				observing = player->IsObserving();
				man.DropPlayer( player, vREASON_TIMEOUT );
				if( !observing)
				{
					skate_mod->remove_skater( skater );
				}
			}
		}
	}
	else
	{
		Net::Conn* server_conn;
		Lst::Search< Net::Conn > sh;
		
		if( man.m_client[0] )
		{   
            server_conn = man.m_client[0]->FirstConnection( &sh );
			if( server_conn == NULL )
			{
				return;
			}
			if(( man.GetJoinState() == vJOIN_STATE_CONNECTED ) ||
			   ( man.GetJoinState() == vJOIN_STATE_WAITING_FOR_START_INFO ) || 
			   ( man.GetJoinState() == vJOIN_STATE_GOT_PLAYERS ))
			{
				if( server_conn->GetTimeElapsedSinceCommunication() > vCONNECTION_TIMEOUT )
				{  
#ifdef __NOPT_ASSERT__
					unsigned int cur_time = Tmr::GetTime();
					unsigned int last_time = server_conn->GetLastCommTime();
#endif					
#ifdef __NOPT_ASSERT__
					Dbg_Printf( "Gamenet : Timing server out. Current time (%d) last comm time (%d)\n",
									cur_time, last_time );
#endif              
					
					if( skate_mod->m_cur_level == CRCD(0x9f2bafb7,"Load_Skateshop"))
					{						
                        // If we got partially in, cancel the join and remove partially-loaded players
						// Shut the client down so that it doesn't bombard the server ip with messages
						
						Script::RunScript( "ShowJoinTimeoutNotice" );

						//man.CancelJoinServer();
						man.m_join_state_task->Remove();
						man.SetJoinState( vJOIN_STATE_FINISHED );
					}
					else
					{
// On the xbox, don't show the lost conn dialog if the quit dialog box is up					
#ifdef __PLAT_XBOX__
						Front::CScreenElementManager* pManager = Front::CScreenElementManager::Instance();
						Front::CScreenElementPtr p_elem = pManager->GetElement( CRCD(0x4c8bf619,"quit_dialog_anchor"), Front::CScreenElementManager::DONT_ASSERT );
						if( !p_elem )
#endif
						{
							Script::RunScript( "CreateLostConnectionDialog" );
	
							man.CleanupPlayers();
							man.ClientShutdown();
						}
					}
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_join_timeout_code( const Tsk::Task< Manager >& task )
{
	Manager& man = task.GetData();

	if(( Tmr::GetTime() - man.m_join_start_time ) > vJOIN_TIMEOUT )
	{
		//Mdl::FrontEnd* front = Mdl::FrontEnd::Instance();
		
		//man.CancelJoinServer();
		//man.SetJoinState( vJOIN_STATE_FINISHED );
        
		man.m_join_state_task->Remove();
		Script::RunScript( "ShowJoinTimeoutNotice" );
	}
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

void				Manager::SetNetworkMode( NetworkMode mode )
{
	m_flags.ClearMask( mNETWORK_MODE);
	
	if( mode == vLAN_MODE )
	{
		Dbg_Printf( "************* Setting Lan Mode\n" );
		m_flags.SetMask( mLAN );
	}
	else if( mode == vINTERNET_MODE )
	{
		Dbg_Printf( "************* Setting Internet Mode\n" );
		m_flags.SetMask( mINTERNET );
	}
	else
	{
		Dbg_Printf( "************* Clearing Net Mode\n" );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				Manager::SetJoinMode( JoinMode mode )
{
	m_join_mode = mode;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

JoinMode			Manager::GetJoinMode( void )
{
	return m_join_mode;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				Manager::SetHostMode( HostMode mode )
{
	// Only accept if it is actually a change in modes
	if( mode == m_host_mode )
	{
		return;
	}
	
	m_host_mode = mode;
	
	switch( m_host_mode )
	{
		case vHOST_MODE_SERVE:
		{
			// Do nothing. This is the standard mode
			Dbg_Printf( "Got mode serve\n" );
			break;
		}
		case vHOST_MODE_AUTO_SERVE:
		{
			RequestObserverMode();
			SpawnAutoServer();

			// Should start a game and also spawn a task that starts new games
			// after X seconds in a lobby
			break;
		}
		
		case vHOST_MODE_FCFS:
		{
			RequestObserverMode();
			ChooseNewServerPlayer();

			// Should start a game and also spawn a task that starts new games
			// after X seconds in a lobby
			break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

	HostMode			Manager::GetHostMode( void )
{
	return m_host_mode;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				Manager::SetServerMode( bool on )
{
	if( on )
	{
		m_flags.SetMask( mSERVER );
	}
	else
	{
		m_flags.ClearMask( mSERVER );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				Manager::InNetMode( void )
{
	return m_flags.TestMask( mNETWORK_MODE);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				Manager::InNetGame( void )
{
	#ifdef	__NOPT_ASSERT__
	// Mick:  Pretend we are in a net game when we are actually in
	// regular single player
	// This allows designer to test net games without starting a server
	if (Script::GetInt(CRCD(0xcbc1a46,"fake_net")))
	{
		return true;
	}
	#endif
	
	
	if( !InNetMode())
	{
		return false;
	}

	if( m_server && !m_server->IsLocal())
	{
		return true;
	}

	if( m_client[0] && !m_client[0]->IsLocal())
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				Manager::InLanMode( void )
{
	return m_flags.TestMask( mLAN );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				Manager::InInternetMode( void )
{
    return m_flags.TestMask( mINTERNET );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				Manager::OnServer( void )
{
	return m_flags.TestMask( mSERVER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				Manager::OnClient( void )
{
	return m_flags.TestMask( mCLIENT );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					Manager::GetSkillLevel( void )
{
	uint32 checksum;
	Script::CStruct* pStructure;
		
	pStructure = m_network_preferences.GetPreference( Script::GenerateCRC("skill_level") );
	pStructure->GetChecksum( "Checksum", &checksum, true );
	if( checksum == Script::GenerateCRC( "num_1" ))
	{
		return vSKILL_LEVEL_1;
	}
	else if( checksum == Script::GenerateCRC( "num_2" ))
	{
		return vSKILL_LEVEL_2;
	}
	else if( checksum == Script::GenerateCRC( "num_3" ))
	{
		return vSKILL_LEVEL_3;
	}                                              
	else if( checksum == Script::GenerateCRC( "num_4" ))
	{
		return vSKILL_LEVEL_4;
	}                                              
	else if( checksum == Script::GenerateCRC( "num_5" ))
	{
		return vSKILL_LEVEL_5;
	}                                              

	return vSKILL_LEVEL_DEFAULT;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					Manager::GetMaxPlayers( void )
{
	uint32 checksum;
	int max_players = 2;
	Script::CStruct* pStructure;
		
	pStructure = m_network_preferences.GetPreference( Script::GenerateCRC("num_players") );
	pStructure->GetChecksum( "Checksum", &checksum, true );
	if( checksum == Script::GenerateCRC( "num_2" ))
	{
		max_players = 2;
	}
	else if( checksum == Script::GenerateCRC( "num_3" ))
	{
		max_players = 3;
	}
	else if( checksum == Script::GenerateCRC( "num_4" ))
	{
		max_players = 4;
	}                                              
	else if( checksum == Script::GenerateCRC( "num_5" ))
	{
		max_players = 5;
	}
	else if( checksum == Script::GenerateCRC( "num_6" ))
	{
		max_players = 6;
	}
	else if( checksum == Script::GenerateCRC( "num_7" ))
	{
		max_players = 7;
	}
	else if( checksum == Script::GenerateCRC( "num_8" ))
	{
		max_players = 8;
	}

	return max_players;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				Manager::CreateTeamFlags( int num_teams )
{
	Script::CStruct* pParams;
	
	// First destroy all team flags
	Script::RunScript( "Kill_Team_Flags" );

	// Now only create the number of flags we need
	if( num_teams == 0 )
	{
		return;
	}

	pParams = new Script::CStruct;
	pParams->AddInteger( "num_teams", num_teams );
	Script::RunScript( "Create_Team_Flags", pParams );
	delete pParams;
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					Manager::GetNumTeams( void )
{
	uint32 checksum;
	int num_teams = vMAX_TEAMS;
	Script::CStruct* pStructure;
		
	pStructure = m_network_preferences.GetPreference( Script::GenerateCRC("team_mode") );
	pStructure->GetChecksum( "Checksum", &checksum, true );
	if( checksum == Script::GenerateCRC( "teams_none" ))
	{
		num_teams = 0;
	}
	else if( checksum == Script::GenerateCRC( "teams_two" ))
	{
		num_teams = 2;
	}
	else if( checksum == Script::GenerateCRC( "teams_three" ))
	{
		num_teams = 3;
	}                                              
	else if( checksum == Script::GenerateCRC( "teams_four" ))
	{
		num_teams = 4;
	}

	return num_teams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					Manager::NumTeamMembers( int team )
{
	int num_players;
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* player;
	Lst::Search< NewPlayerInfo > new_sh;
	NewPlayerInfo* np;

	num_players = 0;

	for( player = FirstPlayerInfo( sh ); player; player = NextPlayerInfo( sh ))
	{
		if( player->m_Team == team )
		{
			num_players++;
		}
	}

	for( np = new_sh.FirstItem( m_new_players ); np; np = new_sh.NextItem())
	{
		// Pending players count, observers don't
		if(	( np->Flags & PlayerInfo::mPENDING_PLAYER ) ||
			( np->Flags & PlayerInfo::mJUMPING_IN ) ||
			!( np->Flags & PlayerInfo::mOBSERVER ))
		{
			if( np->Team == team )
			{
				num_players++;
			}
		}                 
	}

	
	return num_players;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				Manager::SetMaxPlayers( int max_players )
{
	Script::CStruct* pTempStructure;
	Prefs::Preferences* pPreferences;
	uint32 checksum;
	char check_string[32];
	char name_string[32];
	
	Dbg_Assert(( max_players >= 2 ) && ( max_players <= vMAX_PLAYERS ));
	
	sprintf( check_string, "num_%d", max_players );
	checksum = Script::GenerateCRC( check_string );

#if ENGLISH == 0
	sprintf( name_string, "%d %s", max_players, Script::GetLocalString( "netmenu_str_players" ));
#else
	sprintf( name_string, "%d Players", max_players );
#endif

	pTempStructure = new Script::CStruct;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, 
								  name_string );
	pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, (int) checksum );
	pPreferences = GetNetworkPreferences();
	pPreferences->SetPreference( Script::GenerateCRC( "num_players"), pTempStructure );
}
                                        
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					Manager::GetMaxObservers( void )
{
	uint32 checksum;
	int max_observers = 2;
	Script::CStruct* pStructure;
		
	pStructure = m_network_preferences.GetPreference( Script::GenerateCRC("num_observers"));
	pStructure->GetChecksum( "Checksum", &checksum, true );
	if( checksum == Script::GenerateCRC( "num_0" ))
	{
		max_observers = 0;
	}
	else if( checksum == Script::GenerateCRC( "num_1" ))
	{
		max_observers = 1;
	}
	else if( checksum == Script::GenerateCRC( "num_2" ))
	{
		max_observers = 2;
	}
	else if( checksum == Script::GenerateCRC( "num_3" ))
	{
		max_observers = 3;
	}
	else if( checksum == Script::GenerateCRC( "num_4" ))
	{
		max_observers = 4;
	}                                              
	else if( checksum == Script::GenerateCRC( "num_5" ))
	{
		max_observers = 5;
	}
	else if( checksum == Script::GenerateCRC( "num_6" ))
	{
		max_observers = 6;
	}
	else if( checksum == Script::GenerateCRC( "num_7" ))
	{
		max_observers = 7;
	}
	else if( checksum == Script::GenerateCRC( "num_8" ))
	{
		max_observers = 8;
	}                                              

	return max_observers;
}
                                        
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				Manager::SetMaxObservers( int max_observers )
{
	Script::CStruct* pTempStructure;
	Prefs::Preferences* pPreferences;
	uint32 checksum;
	char check_string[32];
	char name_string[32];
	
    sprintf( check_string, "num_%d", max_observers );
	checksum = Script::GenerateCRC( check_string );
	
#if ENGLISH == 0
	if( max_observers == 0 )
	{
		sprintf( name_string, Script::GetLocalString( "netmenu_str_no_observers" ));
	}
	else
	{
		if( max_observers == 1 )
		{
			sprintf( name_string, "1 %s", Script::GetLocalString( "netmenu_str_observer" ));
		}
		else
		{
			sprintf( name_string, "%d %s", max_observers, Script::GetLocalString( "netmenu_str_observers" ));
		}
	}
#else
	if( max_observers == 0 )
	{
		sprintf( name_string, "No Observers" );
	}
	else
	{
		if( max_observers == 1 )
		{
			sprintf( name_string, "1 Observer" );
		}
		else
		{
			sprintf( name_string, "%d Observers", max_observers );
		}
	}
#endif
	pTempStructure = new Script::CStruct;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, 
								  name_string );
	pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, (int) checksum );
	pPreferences = GetNetworkPreferences();
	pPreferences->SetPreference( Script::GenerateCRC( "num_observers"), pTempStructure );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*				Manager::GetPassword( void )
{
	Prefs::Preferences* pPreferences;
	Script::CStruct* pStructure;
	const char* password;
		
	pPreferences = GetNetworkPreferences();
	pStructure = pPreferences->GetPreference( Script::GenerateCRC("password") );
	pStructure->GetText( "ui_string", &password, true );

	return (char*) password;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*				Manager::GetLevelName( bool get_created_park_name )
{
	Script::CArray* pArray = Script::GetArray("level_select_menu_level_info");
	Dbg_Assert( pArray );

	for ( int i = 0; i < (int)pArray->GetSize(); i++ )
	{   
		uint32 checksum;
		Script::CStruct* pStructure = pArray->GetStructure( i );
		Dbg_Assert( pStructure );

        pStructure->GetChecksum( "level", &checksum );
		if( GetNetworkLevelId() == checksum )
		{
			const char* level_name;

			if( get_created_park_name && ( checksum == CRCD(0xb664035d,"Load_Sk5Ed_gameplay")))
			{
				return (char*) Ed::CParkManager::sInstance()->GetParkName();
			}
			pStructure->GetText( "text", &level_name, true );
			return (char *) level_name;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*				Manager::GetGameModeName( void )
{
	Script::CArray* pArray = Script::GetArray("mode_info");
	Dbg_Assert( pArray );

	for ( int i = 0; i < (int)pArray->GetSize(); i++ )
	{   
		uint32 checksum;
		uint32 game_mode_id;
		Script::CStruct* pStructure = pArray->GetStructure( i );
		Dbg_Assert( pStructure );
	
		game_mode_id = GetGameTypeFromPreferences();//skate_mod->GetGameMode()->GetNameChecksum();
		pStructure->GetChecksum( "checksum", &checksum );
		if( game_mode_id == checksum )
		{
			const char* mode_name;

			pStructure->GetText( "name", &mode_name, true );
			return (char *) mode_name;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				Manager::PlayerCollisionEnabled( void )
{
	uint32 collision_pref;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	 
	if(	( GameIsOver()) ||
		( skate_mod->GetGameMode()->GetNameChecksum() == CRCD( 0x34861a16, "freeskate" )) ||
		( skate_mod->GetGameMode()->GetNameChecksum() == CRCD( 0x9d65d0e7, "horse" )))
	{
		// Allow collision in lobbies now, if the option is on
		if( skate_mod->GetGameMode()->GetNameChecksum() != CRCD(0x1c471c60,"netlobby"))
		{
			return false;
		}
	}                

	// Option is always on in splitscreen games
	if( CFuncs::ScriptInSplitScreenGame( NULL, NULL ))
	{
		return true;
	}

	// If this option is on, we definitely collide
	collision_pref = m_network_preferences.GetPreferenceChecksum( CRCD( 0x43ee978, "player_collision"), CRCD( 0x21902065, "checksum" ));
	if( collision_pref == CRCD( 0xf81bc89b, "boolean_true" ))
	{
		return true;
	}

	// Even if it's turned off, we might still enable collision because player collision is the basis
	// of some game modes
	if( ( skate_mod->GetGameMode()->GetNameChecksum() == CRCD( 0xf9d5d933, "netslap" )) ||
		( skate_mod->GetGameMode()->GetNameChecksum() == CRCD( 0xca1f360f, "slap" )))
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ShouldDisplayTeamScores( void )
{
	uint32 collision_pref;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	 
	// Always just show players in lobbies
	if( skate_mod->GetGameMode()->GetNameChecksum() == CRCD( 0x1c471c60, "netlobby" ))
	{
		return false;
	}

	if(	skate_mod->GetGameMode()->IsTeamGame() == false )
	{
		return false;
	}                

	// If this option is on, we definitely collide
	collision_pref = m_network_preferences.GetPreferenceChecksum( CRCD( 0x92a6a8c8, "score_display"), CRCD( 0x21902065, "checksum") );
	return ( collision_pref == CRCD( 0xd071112f, "score_teams" ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Net::Server*		Manager::SpawnServer( bool local, bool secure )
{
	
	int flags;

	Net::Manager * net_man = Net::Manager::Instance();
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();

	Dbg_MsgAssert( m_server == NULL,( "Failed to spawn new server. Old server still running!\n" ));

	flags = Net::App::mBROADCAST |
			Net::App::mDYNAMIC_RESEND |
			Net::App::mACCEPT_FOREIGN_CONN;

	if( local )
	{
		flags |= Net::App::mLOCAL;
	}
	if( secure )
	{
		flags |= Net::App::mSECURE;
	}

	m_server = net_man->CreateNewAppServer( 0, "Skate4", vMAX_CONNECTIONS, 
											vHOST_PORT, inet_addr( net_man->GetLocalIP()), 
											flags );
    
	SetHostMode( vHOST_MODE_SERVE );

	if( m_server )
	{
		mlp_manager->AddLogicTask( *m_server_add_new_players_task );
		// These handlers will be removed after the server actually starts (i.e. after the lobby shuts down)
		m_server->m_Dispatcher.AddHandler( Net::MSG_ID_CONNECTION_REQ, 
											s_handle_connection, 
											Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN,
											this, Net::LOWEST_PRIORITY );
		m_server->m_Dispatcher.AddHandler( MSG_ID_JOIN_REQ,
										   s_handle_join_request,
										   Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN,
										   this );
		m_server->m_Dispatcher.AddHandler( Net::MSG_ID_FIND_SERVER, 
											s_handle_find_server, 
											Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN , this );
		m_server->m_Dispatcher.AddHandler( Net::MSG_ID_DISCONN_REQ, s_handle_disconn_request, 
										   Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_OBSERVE, s_handle_observe, 
										   Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_READY_RESPONSE, s_handle_ready_response, 
											Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_PLAYER_INFO_ACK, s_handle_player_info_ack,
										   0, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_MOVED_TO_RESTART, s_handle_player_restarted,
										   0, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_RUN_HAS_ENDED, s_handle_run_ended, 0, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_FCFS_START_GAME, s_handle_fcfs_request, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_FCFS_BAN_PLAYER, s_handle_fcfs_request, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_FCFS_CHANGE_LEVEL, s_handle_fcfs_request, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_FCFS_TOGGLE_PROSET, s_handle_fcfs_request, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_FCFS_TOGGLE_GOAL_SELECTION, s_handle_fcfs_request, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_FCFS_END_GAME, s_handle_fcfs_request, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_FCFS_SET_NUM_TEAMS, s_handle_fcfs_request, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_REQUEST_CHANGE_TEAM, s_handle_team_change_request, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_REQUEST_LEVEL, s_handle_request_level, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_BEAT_GOAL, s_handle_beat_goal, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_STARTED_GOAL, s_handle_started_goal, Net::mHANDLE_LATE, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_LEVEL_DATA, s_handle_level_data, 0, this, Net::HIGHEST_PRIORITY );
		m_server->m_Dispatcher.AddHandler( MSG_ID_RAIL_DATA, s_handle_rail_data, 0, this, Net::HIGHEST_PRIORITY );
		m_server->m_Dispatcher.AddHandler( MSG_ID_CHALLENGE_RESPONSE, s_handle_challenge_response, 0, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_CHEAT_CHECKSUM_RESPONSE, s_handle_cheat_checksum_response,
											  0, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_REQUEST_RAILS_DATA, s_handle_level_data_request, 0, this );
		m_server->m_Dispatcher.AddHandler( MSG_ID_REQUEST_GOALS_DATA, s_handle_level_data_request, 0, this );

		m_last_score_update = 0;
		m_waiting_for_game_to_start = false;
        	
		RandomizeSkaterStartingPoints();
		m_observer_input_handler = NULL;		// record and handle button inputs
	}
	else
	{
#ifdef __NOPT_ASSERT__
		int error = net_man->GetLastError();
		Dbg_Printf( "Spawn Server: Error %d\n", error );
#endif
		// Here output appropriate error message to the screen
	}

    return m_server;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Net::Client*		Manager::SpawnClient( bool broadcast, bool local, bool secure, int index )
{
	

	Net::Manager * net_man = Net::Manager::Instance();
	int flags;

	Dbg_MsgAssert( m_client[index] == NULL,( "Failed to spawn new client. Old client still running!\n" ));

	flags = 0;
	if( broadcast )
	{
		flags |= Net::App::mBROADCAST;
	}
	if( local )
	{
		flags |= Net::App::mLOCAL;
	}
	else
	{
		flags |= Net::App::mDYNAMIC_RESEND | Net::App::mACCEPT_FOREIGN_CONN;
	}
	if( secure )
	{
		flags |= Net::App::mSECURE;
	}

	m_client[index] = net_man->CreateNewAppClient( index, "Skate4", 
											vJOIN_PORT, inet_addr( net_man->GetLocalIP()),
											flags );
		
	if(( index == 0) && ( m_client[index] ))
	{  
		m_latest_ready_query = 0;
		m_proset_flags = 0;
		m_cam_player = NULL;
		SetReadyToPlay(false);
	}
	else
	{
#ifdef __NOPT_ASSERT__
		int error = net_man->GetLastError();
		Dbg_Printf( "Spawn Client: Error %d\n", error );
#endif
		// Here output appropriate error message to the screen
	}

	m_observer_input_handler = NULL;
	m_game_over = false;
	m_game_pending = false;
	return m_client[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Net::Client*		Manager::SpawnMatchClient( void )
{
	Net::Manager * net_man = Net::Manager::Instance();
	int flags;

	flags = Net::App::mBROADCAST | Net::App::mDYNAMIC_RESEND | Net::App::mACCEPT_FOREIGN_CONN | Net::App::mSECURE;

	m_match_client = net_man->CreateNewAppClient( 0, "Match Skate4", 
											vJOIN_PORT, inet_addr( net_man->GetLocalIP()),
											flags );
	m_match_client->m_Dispatcher.AddHandler( 	Net::MSG_ID_SERVER_RESPONSE, 
												s_handle_server_response, 
												Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN,
												this );

	return m_match_client;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SpawnAutoServer( void )
{
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();

	s_auto_serve_start_time = Tmr::GetTime();
	mlp_man->AddLogicTask( *m_auto_server_task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::JoinServer( bool observe_only, unsigned long ip, unsigned short port, int index )
{
    
	Net::Conn* conn;
	MsgConnectInfo msg;
	Net::MsgDesc msg_desc;
	uint32 size;
	const char* network_id;
	Script::CStruct* pStructure;
	Prefs::Preferences* pPreferences;
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	//Net::Manager * net_man = Net::Manager::Instance();
		
	Dbg_MsgAssert( m_client[index] != NULL,( "Can't join server : Client has not been spawned\n" ));

#ifndef __PLAT_NGC__
	//Dbg_Printf( "Joining server at %s %d\n", inet_ntoa( *(struct in_addr*) &ip ), port );
#endif		// __PLAT_NGC__

	pPreferences = GetNetworkPreferences();
	pStructure = pPreferences->GetPreference( Script::GenerateCRC("network_id") );
	pStructure->GetText( "ui_string", &network_id, true );

	if( m_client[index]->IsLocal())
	{
		Net::Conn* server_conn;

		Dbg_Assert( m_server );

		server_conn = m_server->NewConnection( ip, port, Net::Conn::mLOCAL );
		conn = m_client[index]->NewConnection( ip, port, Net::Conn::mLOCAL );
        m_client[index]->AliasConnections( server_conn, conn );
	}
	else
	{   
		m_client[index]->ConnectToServer( ip, port );
		conn = m_client[index]->NewConnection( ip, port );

		m_join_start_time = Tmr::GetTime();
		mlp_man->AddLogicTask( *m_join_timeout_task );
		SetJoinState( vJOIN_STATE_CONNECTING );
		mlp_man->AddLogicTask( *m_join_state_task );
		
		SetJoinIP( ip );
		SetJoinPort( port );
	}   
	
	msg.m_Observer = observe_only;
	size = 0;
	
	msg.m_Version = vVERSION_NUMBER;
	msg.m_Password[0] = '\0';
	msg.m_WillingToWait = 0;
	
	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof(MsgConnectInfo);
	msg_desc.m_Id = Net::MSG_ID_CONNECTION_REQ;

	m_client[index]->EnqueueMessageToServer( &msg_desc );
	m_client[index]->SendData();

	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_PROCEED_TO_PLAY, 
												s_handle_client_proceed, Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_OBSERVE_PROCEED, 
												s_handle_observe_proceed, Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_OBSERVE_REFUSED, 
												s_handle_observe_refused, Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_JOIN_REFUSED, 
												s_handle_join_refused, Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_JOIN_PROCEED, 
												s_handle_join_proceed, Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_READY_QUERY, s_handle_ready_query, 
												Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_READY_RESPONSE, s_handle_ready_response, 
												Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_PLAYER_INFO_ACK_REQ, s_handle_player_info_ack_req, 
												Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler(	MSG_ID_KING_DROPPED_CROWN, s_handle_dropped_crown, 0, this );
	m_client[index]->m_Dispatcher.AddHandler(	MSG_ID_PLAYER_DROPPED_FLAG, s_handle_dropped_flag, 0, this );
	m_client[index]->m_Dispatcher.AddHandler(	MSG_ID_PANEL_MESSAGE, s_handle_panel_message, 
												Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_AUTO_SERVER_NOTIFICATION, s_handle_auto_server_notification,
												Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_FCFS_ASSIGNMENT, s_handle_fcfs_assignment,
												Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_TEAM_CHANGE, s_handle_team_change,
												Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_SET_NUM_TEAMS, s_handle_set_num_teams,
												Net::mHANDLE_LATE, this );
	m_client[index]->m_Dispatcher.AddHandler( 	MSG_ID_CHAT, s_handle_chat, Net::mHANDLE_LATE );
	m_client[index]->m_Dispatcher.AddHandler(	MSG_ID_CHALLENGE, s_handle_challenge,
												Net::mHANDLE_LATE, this ); 
	
	if( index == 0 )
	{   
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_SELECT_GOALS, s_handle_select_goals, 0, this );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_GAME_INFO, s_handle_game_info, 0, this );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_KILL_TEAM_FLAGS, s_handle_kill_flags, 0, this );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_BEAT_GOAL, s_handle_beat_goal_relay, 0, this );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_STARTED_GOAL, s_handle_started_goal_relay, 0, this );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_TOGGLE_PROSET, s_handle_toggle_proset, 0, this );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_CHANGE_LEVEL, s_handle_change_level, 0, this,
												Net::HIGHEST_PRIORITY );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_CHANGE_LEVEL, s_handle_new_level, 0, this,
												Net::HIGHEST_PRIORITY );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_LEVEL_DATA, s_handle_level_data, 0, this,
												Net::HIGHEST_PRIORITY );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_RAIL_DATA, s_handle_rail_data, Net::mHANDLE_CRC_MISMATCH, this,
												Net::HIGHEST_PRIORITY );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_GOALS_DATA, s_handle_goals_data, Net::mHANDLE_CRC_MISMATCH, this,
												Net::HIGHEST_PRIORITY );
		m_client[0]->m_Dispatcher.AddHandler( MSG_ID_CHEAT_CHECKSUM_REQUEST, s_handle_cheat_checksum_request,
											  0, this );
		
		
		if( !m_client[0]->IsLocal())
		{
			m_client[index]->m_Dispatcher.AddHandler( MSG_ID_PLAYER_CREATE, s_handle_new_player, 0, this );
			m_client[index]->m_Dispatcher.AddHandler( MSG_ID_JOIN_ACCEPTED, s_handle_join_accepted, 0, this );
			
		}

		m_client[index]->m_Dispatcher.AddHandler( MSG_ID_WAIT_N_SECONDS, s_handle_wait_n_seconds, 0, this );
		m_client[index]->m_Dispatcher.AddHandler( MSG_ID_NEW_KING, s_handle_new_king, 0, this );
		m_client[index]->m_Dispatcher.AddHandler( MSG_ID_CAPTURED_FLAG, s_handle_captured_flag, 0, this );
		m_client[index]->m_Dispatcher.AddHandler( MSG_ID_STOLE_FLAG, s_handle_stole_flag, 0, this );
		m_client[index]->m_Dispatcher.AddHandler( MSG_ID_TOOK_FLAG, s_handle_took_flag, 0, this );
		m_client[index]->m_Dispatcher.AddHandler( MSG_ID_RETRIEVED_FLAG, s_handle_retrieved_flag, 0, this );
		m_client[index]->m_Dispatcher.AddHandler( MSG_ID_GAME_OVER, s_handle_game_over, 0, this );
		m_client[index]->m_Dispatcher.AddHandler( MSG_ID_END_GAME, s_handle_end_game, 0, this );
		
		// Add this low-priority object update handler just to advance the dispatcher's stream pointer.
		// Since we no longer send the size of the object update stream with the stream, we need
		// some code to return to the dispatcher the length of the message. If, because of timing,
		// we get one of these messages before our Mdl::Skate object update handler is instantiated
		// this handler will figure out the length of the message and pass it back
		m_client[index]->m_Dispatcher.AddHandler( MSG_ID_OBJ_UPDATE_STREAM, s_handle_object_update, 
												  Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this,
												  Net::HIGHEST_PRIORITY );
	}

	SetObserverCommand( vOBSERVER_COMMAND_NONE );
}
    
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ReattemptJoin( Net::App* client )
{
	MsgConnectInfo msg;
	Net::MsgDesc msg_desc;
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
		
	msg.m_Observer = ( GetJoinMode() == vJOIN_MODE_OBSERVE );
	msg.m_Version = vVERSION_NUMBER;
	msg.m_WillingToWait = 1;
	msg.m_Password[0] = '\0';
	
	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof(MsgConnectInfo);
	msg_desc.m_Id = Net::MSG_ID_CONNECTION_REQ;
	client->EnqueueMessageToServer( &msg_desc );
	client->SendData();
	
	// Reset join timeout
	m_join_start_time = Tmr::GetTime();
	mlp_man->AddLogicTask( *m_join_timeout_task );

	SetJoinState( vJOIN_STATE_JOINING );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::CancelJoinServer( void )
{
	Net::Client* client;
    Net::MsgDesc msg_desc;

	// These are "finished" states. Don't redundantly cancel our join if we've already cancelled
	if(	( GetJoinState() == vJOIN_STATE_FINISHED ) ||
		( GetJoinState() == vJOIN_STATE_CONNECTED ) ||
		( GetJoinState() == vJOIN_STATE_REFUSED ))
	{
		return;
	}

	client = GetClient( 0 );
	Dbg_Assert( client );

    // First, wait for any pending network tasks to finish before sending out final disconn message
#ifdef __PLAT_NGPS__
	//client->WaitForAsyncCallsToFinish();
#endif  		
	Dbg_Printf( "Leaving server\n" );
			
	// Send off last message, notifying server that I'm about to quit
	msg_desc.m_Id = Net::MSG_ID_DISCONN_REQ;
	client->EnqueueMessageToServer( &msg_desc );
	// Wake the network thread up to send the data
	client->SendData();
		
	// Wait for that final send to complete
#ifdef __PLAT_NGPS__
	//client->WaitForAsyncCallsToFinish();
#endif  
    
	ClientShutdown();

	m_join_timeout_task->Remove();

	// We may have made it halfway in, so free up any pending players.
	free_all_pending_players(); 

#ifdef __PLAT_NGPS__
	if( mpBuddyMan->IsLoggedIn())
	{
		mpBuddyMan->SetStatusAndLocation( GP_CHATTING, (char*) Script::GetString( "homie_status_chatting" ), 
														mpLobbyMan->GetLobbyName());
	}
#endif
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ReattemptJoinWithPassword( char* password )
{
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	Net::Manager * net_man = Net::Manager::Instance();
	Script::CStruct* pStructure;
	Prefs::Preferences* pPreferences;
	const char* network_id;
	MsgJoinInfo msg;
	Net::MsgDesc msg_desc;
	int size;
	bool ignore_face_data;

	Dbg_Printf( "************* Trying password : %s\n", password );
		
	pPreferences = GetNetworkPreferences();
	pStructure = pPreferences->GetPreference( Script::GenerateCRC("network_id") );
	pStructure->GetText( "ui_string", &network_id, true );

#ifdef __PLAT_NGPS__
	msg.m_Profile = mpBuddyMan->GetProfile();
	msg.m_Rating = mpStatsMan->GetStats()->GetRating();
#endif
	msg.m_Observer = ( GetJoinMode() == vJOIN_MODE_OBSERVE );
	msg.m_Version = vVERSION_NUMBER;
	msg.m_WillingToWait = 1;
	if(	net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM )
	{
		msg.m_Broadband = 0;
		ignore_face_data = true;
	}
	else
	{
		msg.m_Broadband = 1;
		ignore_face_data = false;
	}
	strcpy( msg.m_Name, network_id );
	strcpy( msg.m_Password, password );
	size = 0;
	if( !msg.m_Observer )
	{
		// GJ:  transmit the way you look (slot 0 of the
		// skater profile manager) to the server
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		Obj::CSkaterProfile* pSkaterProfile = skate_mod->GetProfile(0);
		Net::Conn *server_conn;
		Lst::Search< Net::Conn > sh;
	
		server_conn = m_client[0]->FirstConnection( &sh );
		if( server_conn->GetBandwidthType() == Net::Conn::vNARROWBAND )
		{
			ignore_face_data = true;
		}
	
		size = pSkaterProfile->WriteToBuffer(msg.m_AppearanceData, vMAX_APPEARANCE_DATA_SIZE,
												ignore_face_data );
		Dbg_Assert( size < vMAX_APPEARANCE_DATA_SIZE );
		Dbg_Printf("\n\n******************* MsgJoinInfo appearance data size = %d %d\n", size, sizeof(MsgJoinInfo) - vMAX_APPEARANCE_DATA_SIZE + size);
	}

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof(MsgJoinInfo) - vMAX_APPEARANCE_DATA_SIZE + size;
	msg_desc.m_Id = MSG_ID_JOIN_REQ;
	msg_desc.m_Queue = Net::QUEUE_IMPORTANT;
	m_client[0]->EnqueueMessageToServer( &msg_desc );
    
	//m_client[0]->SendData();

	// Reset join timeout
	m_join_start_time = Tmr::GetTime();
	mlp_man->AddLogicTask( *m_join_timeout_task );

	SetJoinState( vJOIN_STATE_JOINING_WITH_PASSWORD );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ServerShutdown( void )
{
	
	Net::Manager * net_man = Net::Manager::Instance();

	Dbg_Message( "Shutting Server Down\n" );

	if( m_server )
	{
#ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
		if( !m_server->IsLocal() && InInternetMode())
		{
			mpLobbyMan->StopReportingGame();
			NNFreeNegotiateList();
		}
#endif
#endif    
		net_man->DestroyApp( m_server );
		m_server_add_new_players_task->Remove();
		m_start_network_game_task->Remove();
		m_server = NULL;
	}

	m_flags.ClearMask( mSERVER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	  Manager::ClientShutdown( void )
{
	
	
	Net::Manager * net_man = Net::Manager::Instance();
	int i;

#ifdef	__PLAT_XBOX__	 
	if( m_client[0] && !m_client[0]->IsLocal())
	{
		if( m_XboxKeyRegistered )
		{
			if( XNetUnregisterKey( &m_XboxKeyId ) == 0 )
			{
				m_XboxKeyRegistered = false;
			}
		}
	}
#endif

	for( i = 0; i < vMAX_LOCAL_CLIENTS; i++ )
	{
		if( m_client[i] )
		{
			if(( i == 0 ) && !m_client[i]->IsLocal())
			{
				m_client_add_new_players_task->Remove();
			}

			net_man->DestroyApp( m_client[i] );
			m_client[i] = NULL;
		}
	}

	if( m_observer_input_handler )
	{
		delete m_observer_input_handler;
		m_observer_input_handler = NULL;
	}

	m_enter_chat_task->Remove();
	m_render_scores_task->Remove();
	m_change_level_task->Remove();

	ClearTriggerEventList();
	SetReadyToPlay(false);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::MatchClientShutdown( void )
{
	if( m_match_client )
	{
		Net::Manager * net_man = Net::Manager::Instance();

		net_man->DestroyApp( m_match_client );
		m_match_client = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::AutoServerShutdown( void )
{
	m_auto_server_task->Remove();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetJoinIP( void )
{
	return m_join_ip;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetJoinPrivateIP( void )
{
	return m_join_private_ip;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetJoinPort( void )
{
	return m_join_port;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetJoinIP( int ip )
{
	Dbg_Printf( "**** In SetJoinIP: %s\n", inet_ntoa(*(struct in_addr *) &ip ));
	m_join_ip = ip;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetJoinPrivateIP( int ip )
{
	m_join_private_ip = ip;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetJoinPort( int port )
{
	m_join_port = port;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Net::Server*  Manager::GetServer( void )
{
	return m_server;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Net::Client*   Manager::GetClient( int index )
{
	

	Dbg_Assert( index < vMAX_LOCAL_CLIENTS );
   	
	return m_client[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Net::Client*   Manager::GetMatchClient( void )
{   
	return m_match_client;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetNumPlayers( void )
{
	int num_players;
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* player;
	Lst::Search< NewPlayerInfo > new_sh;
	NewPlayerInfo* np;

	num_players = 0;

	for( player = FirstPlayerInfo( sh, true ); player; player = NextPlayerInfo( sh, true ))
	{
		if( !player->IsObserving() || player->m_flags.TestMask( PlayerInfo::mPENDING_PLAYER ))
		{
			num_players++;
		}
	}
    
	for( np = new_sh.FirstItem( m_new_players ); np; np = new_sh.NextItem())
	{
		// Pending players count, observers don't
		if(	( np->Flags & PlayerInfo::mPENDING_PLAYER ) ||
			( np->Flags & PlayerInfo::mJUMPING_IN ) ||
			!( np->Flags & PlayerInfo::mOBSERVER ))
		{
			num_players++;
		}                 
	}
	
	return num_players;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetNumObservers( void )
{
	int num_observers;
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* player;
	Lst::Search< NewPlayerInfo > new_sh;
	NewPlayerInfo* np;

	num_observers = 0;

	for( player = FirstPlayerInfo( sh, true ); player; player = NextPlayerInfo( sh, true ))
	{
		// Pending players don't count as observers
		if( ( player->IsObserving()) && 
			( !player->IsPendingPlayer()))
		{   
			// Don't count a server who is sitting out as an observer
			if(	( player->IsLocalPlayer() && 
				( GetHostMode() != vHOST_MODE_SERVE )))
			{
				continue;
			}
			num_observers++;
		}
	}
    
	for( np = new_sh.FirstItem( m_new_players ); np; np = new_sh.NextItem())
	{
		// Don't count pending players as observers
		if(( np->Flags & PlayerInfo::mOBSERVER ) &&
		   ( !( np->Flags & PlayerInfo::mPENDING_PLAYER )))
		{
			num_observers++;
		}                 
	}
	
	return num_observers;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*		Manager::NewPlayer( Obj::CSkater* skater, Net::Conn* conn, int flags )
{
	PlayerInfo* new_player;
		
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	new_player = new PlayerInfo( flags );

	new_player->m_Skater = skater;
	new_player->m_Conn = conn;
		
	// By default, count the Local player as our currently observed player
	if( new_player->IsLocalPlayer())
	{
		if( new_player->IsObserving())
		{   
			Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
			Inp::Manager * inp_manager = Inp::Manager::Instance();
			
			/*new_player->m_cam = new Obj::CSkaterCam( 0 );
			Nx::CViewportManager::sSetCamera( 0, new_player->m_cam );
			new_player->m_cam->SetMode( Obj::CSkaterCam::SKATERCAM_MODE_NORMAL_MEDIUM, 0.0f );*/
						
			Script::RunScript( "hide_panel_stuff" );
			ObservePlayer( GetNextPlayerToObserve());

			new_player->m_observer_logic_task = 
					new Tsk::Task< PlayerInfo > ( PlayerInfo::s_observer_logic_code, *new_player );
			mlp_manager->AddLogicTask( *new_player->m_observer_logic_task );
			m_observer_input_handler = new Inp::Handler< Manager > ( 0,  s_observer_input_logic_code, *this, 
															 Tsk::BaseTask::Node::vHANDLER_PRIORITY_OBSERVER_INPUT_LOGIC );
			inp_manager->AddHandler( *m_observer_input_handler );
		}
		else
		{
			m_cam_player = NULL;
		}
	}
	else
	{
		PlayerInfo* local_player;

		local_player = GetLocalPlayer();
		Dbg_MsgAssert( local_player,( "Should have local player by now" ));

		if( local_player->IsObserving())
		{
			if( m_cam_player == NULL )
			{
				ObservePlayer( new_player );
			}
		}
	}

	Dbg_Printf( "Player added : %p with conn %p\n", new_player, new_player->m_Conn );
    
	Mem::Manager::sHandle().PopContext();

	return new_player;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			Manager::SendFaceDataToServer( void )
{
	bool sent_data;
	Net::Manager* net_man = Net::Manager::Instance();

	Dbg_Assert( InNetGame());

	// Don't send face data to other players if I'm on a modem
	if(	net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM )
	{
		return false;
	}

	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( 0 );
	Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
	Gfx::CFaceTexture* pFaceTexture = pAppearance->GetFaceTexture();

	sent_data = false;
	if( pFaceTexture && pFaceTexture->IsValid())
	{
		if( OnServer())
		{
			PlayerInfo* local_player;

			local_player = GetLocalPlayer();
			
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetMiscHeap());
			local_player->m_face_data = new uint8[vMAX_FACE_DATA_SIZE + 1];
			Mem::Manager::sHandle().PopContext();

			local_player->m_face_data[0] = 0;	// player id
			pFaceTexture->WriteToBuffer( &local_player->m_face_data[1], vMAX_FACE_DATA_SIZE );
			sent_data = true;
		}
		else
		{
			Net::Conn *server_conn;
			Lst::Search< Net::Conn > sh;
		
			server_conn = GetClient(0)->FirstConnection( &sh );
			if( server_conn->GetBandwidthType() == Net::Conn::vBROADBAND )
			{
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());
				uint8* buffer = new uint8[vMAX_FACE_DATA_SIZE];
				Mem::Manager::sHandle().PopContext();
	
				pFaceTexture->WriteToBuffer( buffer, vMAX_FACE_DATA_SIZE );
				GetClient(0)->StreamMessageToServer( MSG_ID_FACE_DATA, vMAX_FACE_DATA_SIZE, buffer, "Face Data",
													 vSEQ_GROUP_FACE_MSGS, false, false );
				delete[] buffer;
				sent_data = true;
			}
		}
	}

	return sent_data;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SendFaceDataToPlayer( PlayerInfo* player )
{
	PlayerInfo* face_player;
	Lst::Search< PlayerInfo > sh;
	Net::Manager* net_man = Net::Manager::Instance();

	Dbg_Assert( OnServer());

	// Don't send face data to other players if I'm on a modem
	if(	net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM )
	{
		return;
	}

	for( face_player = FirstPlayerInfo( sh ); face_player; face_player = NextPlayerInfo( sh ))
	{
		bool has_face_data;

		if( face_player == player )
		{
			continue;
		}
		// Don't send face data to modem players for bandwidth reasons
		if( player->m_Conn->GetBandwidthType() == Net::Conn::vNARROWBAND )
		{
			continue;
		}

		has_face_data = false;
		if( face_player->GetFaceData())
		{
			has_face_data = true;
		}
		else
		{
			if( face_player->IsLocalPlayer())
			{
				has_face_data = SendFaceDataToServer();
			}
		}

		if( has_face_data == false )
		{
			continue;
		}

		
		m_server->StreamMessageToConn( player->m_Conn, MSG_ID_FACE_DATA, vMAX_FACE_DATA_SIZE + 1, face_player->GetFaceData(), "Face Data",
									   vSEQ_GROUP_FACE_MSGS, false, true );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ClientAddNewPlayer( NewPlayerInfo* new_player )
{
	Obj::CSkater* skater;
	PlayerInfo* player_info;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
    
	Dbg_Assert( !OnServer());

	skater = NULL;
    
	if( !( new_player->Flags & PlayerInfo::mOBSERVER ))
	{
		Obj::CSkaterProfile* pSkaterProfile;

		if ( CFuncs::ScriptInSplitScreenGame( NULL, NULL ) )
		{
			// in splitscreen games, we have to figure out
			// which player is currently being added (either 0 or 1)
			pSkaterProfile = skate_mod->GetProfile(new_player->ObjID);
		}
		else
		{
			// in other types of games, "your player" is always
			// in slot 0 of the local machine's skater profile manager
			pSkaterProfile = skate_mod->GetProfile(0);
		}
		if( InNetGame())
		{
			if( new_player->Flags & PlayerInfo::mLOCAL_PLAYER )
			{
				skater = skate_mod->add_skater( pSkaterProfile, true, new_player->ObjID, 0);
			}
			else
			{
				int skater_num;

				
				if( skate_mod->GetLocalSkater())
				{
					skater_num = skate_mod->GetNumSkaters();
				}
				else
				{
					skater_num = ( skate_mod->GetNumSkaters() + 1 ) % Mdl::Skate::vMAX_SKATERS;
				}

				skater = skate_mod->add_skater( new_player->mpSkaterProfile, false, new_player->ObjID,
													skater_num );
				
				if (new_player->VehicleControlType)
				{
					Obj::CSkaterStateHistoryComponent* p_skater_state_history_component = GetSkaterStateHistoryComponentFromObject(skater);
					Dbg_Assert(p_skater_state_history_component);
					
					p_skater_state_history_component->SetCurrentVehicleControlType(new_player->VehicleControlType);
				}
			}
		}
		else
		{
			skater = skate_mod->add_skater( pSkaterProfile, true, new_player->ObjID, skate_mod->GetNumSkaters() );
		}
	}

	player_info = GetPlayerByObjectID( new_player->ObjID );
	if( new_player->Flags & PlayerInfo::mJUMPING_IN )
	{   
		if( player_info == NULL )
		{
			player_info = GetLocalPlayer();
		}

		if( player_info )
		{
			DestroyPlayer( player_info );
			player_info = NULL;
		}
	}

	if( player_info == NULL )
	{
		int flags;

		flags = ( new_player->Flags & (	PlayerInfo::mLOCAL_PLAYER | PlayerInfo::mOBSERVER | PlayerInfo::mHAS_ANY_FLAG ));
										
		player_info = NewPlayer( skater, new_player->Conn, flags );
        strcpy( player_info->m_Name, new_player->Name );
		player_info->m_Team = new_player->Team;
		player_info->m_Profile = new_player->Profile;
		player_info->m_Rating = new_player->Rating;
		player_info->m_VehicleControlType = new_player->VehicleControlType;
		AddPlayerToList( player_info );
		if( new_player->Flags & PlayerInfo::mKING_OF_THE_HILL )
		{
			player_info->MarkAsKing( true );
		}
	}
	
	// We get this message when we're added to a network lobby. So go ahead and start it up
	// on our end
	if( new_player->Flags & PlayerInfo::mJUMPING_IN )
	{
		// We're jumping into a game at the lobby so start the lobby game flow.
		// Also send our face texture data to all other players.
		SendFaceDataToServer();
		skate_mod->GetTrickObjectManager()->DeleteAllTrickObjects();
		Script::RunScript( "do_backend_retry" );
	}

	if( player_info->IsLocalPlayer() == false )
	{
		Mdl::Score* score = GetSkaterScoreComponentFromObject( skater )->GetScore();

		skater->RemoveFromCurrentWorld();
		score->SetTotalScore( new_player->Score );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::DeferredNewPlayer( NewPlayerInfo* new_player, int num_frames )
{
	
	
	Lst::Node< NewPlayerInfo > *player;
	Lst::Search< NewPlayerInfo > new_sh;
	NewPlayerInfo* np;
    
	// First, make sure we don't already have this new player queued up
	for( np = new_sh.FirstItem( m_new_players ); np; np = new_sh.NextItem())
	{
		if( np->ObjID == new_player->ObjID )
		{
			return;
		}
	}
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	np = new NewPlayerInfo;

	strcpy( np->Name, new_player->Name );
    
	np->Conn = new_player->Conn;
	np->ObjID = new_player->ObjID;
	np->Flags = new_player->Flags;
	np->Team = new_player->Team;
	np->Profile = new_player->Profile;
	np->Rating = new_player->Rating;
	np->Score = new_player->Score;
	np->VehicleControlType = new_player->VehicleControlType;
	if(( np->Flags & PlayerInfo::mJUMPING_IN ) && OnServer())
	{
		np->JumpInFrame = m_server->m_FrameCounter;
	}

	// Pure observers don't have skater profile date
	if( (!( np->Flags & PlayerInfo::mOBSERVER ) || ( np->Flags & PlayerInfo::mPENDING_PLAYER )))
	{
		// GJ:  copy the contents of the skater profile from one to the other
		// it'd be nice to just use a copy constructor, but the compiler
		// doesn't seem to like it.
		uint8* pTempBuffer = new uint8[vMAX_APPEARANCE_DATA_SIZE];
		new_player->mpSkaterProfile->WriteToBuffer(pTempBuffer, vMAX_APPEARANCE_DATA_SIZE);
		np->mpSkaterProfile->ReadFromBuffer(pTempBuffer);
		delete[] pTempBuffer;
		
		// GJ 2/12/03:  the above was written sometime during THPS3,
		// and our current compiler seems to be okay now.  However,
		// i'm afraid of breaking anything by changing this now...
//		*np->mpSkaterProfile = *new_player->mpSkaterProfile;
	}
						 
	if( OnServer())
	{
        np->AllowedJoinFrame = m_server->m_FrameCounter + num_frames;

		if( !( np->Flags & PlayerInfo::mLOCAL_PLAYER ))
		{
			if( np->Flags & PlayerInfo::mOBSERVER )
			{
				uint32 msg_checksum;

				if( np->Flags & PlayerInfo::mPENDING_PLAYER )
				{
					msg_checksum = Script::GenerateCRC("net_message_join_pending");
				}
				else
				{
					msg_checksum = Script::GenerateCRC("net_message_observing");
				}
				CreateNetPanelMessage( true, msg_checksum, new_player->Name );
			}
			else
			{
				CreateNetPanelMessage( false, Script::GenerateCRC("net_message_joining"),
									   new_player->Name );
			}
		}
	}
	else
	{
		np->AllowedJoinFrame = m_client[0]->m_FrameCounter + num_frames;
	}
	
	player = new Lst::Node< NewPlayerInfo > ( np );

	m_new_players.AddToTail( player );
	
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::AddPlayerToList( PlayerInfo* player )
{
	

	Dbg_Assert( player );

    m_players.AddToTail( player );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::DestroyPlayer( PlayerInfo* player )
{
	// If we're destroying the player we're looking at, change to the next player
	if( player == m_cam_player )
	{
		PlayerInfo* local_player;

		m_cam_player = GetNextPlayerToObserve();
		if( player == m_cam_player )
		{
			m_cam_player = NULL;
		}
        
		local_player = GetLocalPlayer();
		if( local_player && local_player->m_Skater )
		{
			ObservePlayer( local_player );
		}
		else
		{
			ObservePlayer( m_cam_player );
		}
	}

	if( player->IsKing())
	{
		if( m_crown )
		{
			m_crown->RemoveFromKing();
		}
	}

	if( player->HasCTFFlag())
	{
		char team_str[64];
		int team;
		Script::CStruct* pParams;
		PlayerInfo* local_player;
		
		team = player->HasWhichFlag();
		sprintf( team_str, "team_%d_name", team + 1 );
		pParams = new Script::CStruct;

		pParams->AddInteger( "team", team );
		pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
		Script::RunScript( "flag_returned", pParams );

		delete pParams;

		local_player = GetLocalPlayer();
		if( local_player && !local_player->IsObserving())
		{
			if( local_player->m_Team == team )
			{
				Script::RunScript( "hide_ctf_arrow" );		
			}
		}
		player->ClearCTFState();
	}

	delete player;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*		Manager::GetCurrentlyObservedPlayer( void )
{
	return m_cam_player;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*		Manager::GetNextPlayerToObserve( void )
{
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* new_cam_player;

	

	new_cam_player = NULL;
	for( new_cam_player = FirstPlayerInfo( sh ); new_cam_player;
			new_cam_player = NextPlayerInfo( sh ))
	{
		// If we had no 'current' target, just return the first player
		if( m_cam_player == NULL )
		{
			break;
		}

		// Ok, we found our current cam_player. Return the next one, if there is a next one
		if( new_cam_player == m_cam_player )
		{
			new_cam_player = NextPlayerInfo( sh );
			if( new_cam_player == NULL )
			{
				// We've reached the end of the list. Return the first one
				new_cam_player = FirstPlayerInfo( sh );
			}
			break;
		}
	}

	return new_cam_player;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::ObservePlayer( PlayerInfo* player )
{   
	Obj::CSkaterCameraComponent* skater_cam;
	Obj::CWalkCameraComponent* walk_cam;
	Obj::CCompositeObject* cam_obj;
	
	cam_obj = (Obj::CCompositeObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0x967c138c,"skatercam0"));
	skater_cam = GetSkaterCameraComponentFromObject( cam_obj );
	walk_cam = GetWalkCameraComponentFromObject( cam_obj );
	
	if (!player || !player->m_Skater)
	{
		Dbg_Printf( "Attempting to observe NULL player\n" );
		
		skater_cam->SetSkater( NULL );
		walk_cam->SetSkater( NULL );
		
		m_cam_player = NULL;
		
		return;
	}
	
	Manager* gamenet_man = Manager::Instance();
	PlayerInfo* local_player;
	local_player = gamenet_man->GetLocalPlayer();
	
	if (player != local_player)
	{
		// observer camera only use the skater camera logic
		
		Dbg_Printf( "Observing player %d\n", player->m_Skater->GetID());
		skater_cam->SetSkater( player->m_Skater );
		
		walk_cam->SetSkater( NULL );
		
		skater_cam->Suspend( false );
		walk_cam->Suspend( true );
	}
	else
	{
		// restore the correct camera state for a normal skate camera
		
		Obj::CSkaterPhysicsControlComponent* physics_control_comp;
		
		physics_control_comp = GetSkaterPhysicsControlComponentFromObject( player->m_Skater );
		Dbg_MsgAssert(physics_control_comp, ("Local player has no SkaterPhysicsControlComponent"));
		
		Dbg_Printf( "Restoring standard camera to player %d\n", player->m_Skater->GetID());
		
		skater_cam->SetSkater( player->m_Skater );
		walk_cam->SetSkater( player->m_Skater );
		
		if (physics_control_comp->IsSkating())
		{
			skater_cam->Suspend( false );
			walk_cam->Suspend( true );
		}
		else
		{
			skater_cam->Suspend( true );
			walk_cam->Suspend( false );
			walk_cam->Reset();
		}
	}
	
	m_cam_player = player;

	/*if( player == NULL )
	{
		Mth::Vector rot;
		Mth::Matrix cam_matrix;
		Mth::Vector node_pos;
		int node;

		node = Obj::GetRestartNode( Script::GenerateCRC( "Player1" ), 0 );
		Script::CStruct* pNodeData = SkateScript::GetNode( node ); 
		
		cam_matrix.Ident( );	   		// Set identity before we decided if we need to rotate or not, in case we don't.
		
		SkateScript::GetPosition( node, &node_pos );
		node_pos[Y] += FEET( 5 );
		Dbg_Printf( "************************ Got Position %f %f %f\n", node_pos[X], node_pos[Y], node_pos[Z] );
		
		SkateScript::GetAngles( pNodeData, &rot );
		cam_matrix.RotateY( rot[ Y ] );
		cam_matrix[Z] = -cam_matrix[Z];
		cam_matrix[X] = -cam_matrix[X];
		Dbg_Printf( "************************ Rots X: %f Y: %f Z: %f\n", rot[X], rot[Y], rot[Z] );
		
		Dbg_Assert( local_player );
		Dbg_Assert( local_player->m_cam );

		local_player->m_cam->SetMatrix( cam_matrix );
		local_player->m_cam->SetPos( node_pos );
		local_player->m_cam->SetSkater( NULL );
		return;
	}

	// Only works in network games
	if( InNetGame())
	{   
		if( local_player && local_player->IsObserving())
		{   
			m_cam_player = player;
			local_player->m_cam->SetSkater( m_cam_player->m_Skater );
		}
	}*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::RequestObserverMode( void )
{
	PlayerInfo* local_player;
    Net::MsgDesc msg_desc;
	
	local_player = GetLocalPlayer();
	Dbg_Assert( local_player );

	if( local_player->IsObserving())
	{
		return;
	}
	
	Dbg_Printf( "Sent request to enter observer mode\n" );
	msg_desc.m_Id = MSG_ID_OBSERVE;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	m_client[0]->EnqueueMessageToServer( &msg_desc );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::ChooseNewServerPlayer( void )
{
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	Net::MsgDesc msg_desc;

	Dbg_Assert(( GetServerPlayer() == NULL ));	// Shouldn't call this function if one already exists
	Dbg_Assert( m_server );

	for( player = FirstPlayerInfo( sh ); player; player = NextPlayerInfo( sh ))
	{
		Script::CStruct* pStructure;
		uint32 checksum;

		// For now, just select the first non-server player info
		if( player->IsLocalPlayer())
		{
			continue;
		}

		// If we don't have any "fully in" players at this point, that's ok. Don't choose a server player.
		// When they are marked as being "fully in" they will be chosen as the fcfs and notified
		if( player->IsFullyIn() == false )
		{
			continue;
		}

		player->MarkAsServerPlayer();
		
		pStructure = m_network_preferences.GetPreference( Script::GenerateCRC("player_collision"));
		pStructure->GetChecksum( "Checksum", &checksum, true );
		
		msg_desc.m_Data = &checksum;
		msg_desc.m_Length = sizeof( uint32 );
		msg_desc.m_Id = MSG_ID_FCFS_ASSIGNMENT;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
		m_server->EnqueueMessage( player->GetConnHandle(), &msg_desc );

		break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::EnterObserverMode( void )
{
	PlayerInfo* local_player;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	Inp::Manager * inp_manager = Inp::Manager::Instance();
	Lst::Search< PlayerInfo > first_player;
	
	Dbg_Printf( "Entering Observer Mode\n" );
	
	local_player = GetLocalPlayer();
	Dbg_Assert( local_player );

    if( local_player->IsObserving())
	{
		return;
	}
	
	if( OnServer())
	{
		DropPlayer( local_player, vREASON_OBSERVING );
	}

	if( local_player->IsKing())
	{
		local_player->MarkAsKing( false );
		if( m_crown )
		{
			m_crown->RemoveFromKing();
		}
	}
	if( local_player->HasCTFFlag())
	{
		char team_str[64];
		int team;
		Script::CStruct* pParams;
		
		team = local_player->HasWhichFlag();
		sprintf( team_str, "team_%d_name", team + 1 );
		pParams = new Script::CStruct;

		pParams->AddInteger( "team", team );
		pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
		Script::RunScript( "flag_returned", pParams );

		delete pParams;

		Script::RunScript( "hide_ctf_arrow" );		
		local_player->ClearCTFState();
	}

	local_player->m_flags.SetMask( PlayerInfo::mOBSERVER );


	skate_mod->remove_skater( local_player->m_Skater );
	local_player->m_Skater = NULL;
	//skate_mod->HideSkater( local_player->m_Skater );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterHeap( 0 ));

	//Gfx::Camera* p_camera = &(viewer_mod->GetCamera( 0 )->camera );
	/*local_player->m_cam = new Obj::CSkaterCam( 0 );
	Dbg_Assert( local_player->m_cam );
	Nx::CViewportManager::sSetCamera( 0, local_player->m_cam );
	local_player->m_cam->SetMode( Obj::CSkaterCam::SKATERCAM_MODE_NORMAL_MEDIUM, 0.0f );*/

	ObservePlayer( GetNextPlayerToObserve());

	local_player->m_observer_logic_task = 
			new Tsk::Task< PlayerInfo > ( PlayerInfo::s_observer_logic_code, *local_player );
	mlp_manager->AddLogicTask( *local_player->m_observer_logic_task );
	m_observer_input_handler = new Inp::Handler< Manager > ( 0,  s_observer_input_logic_code, *this, 
															 Tsk::BaseTask::Node::vNORMAL_PRIORITY-1 );
	inp_manager->AddHandler( *m_observer_input_handler );
	SetObserverCommand( vOBSERVER_COMMAND_NONE );

	Script::RunScript( "ShowAllObjects" );
	Script::RunScript( "hide_panel_stuff" );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetNextPlayerObjectId( void )
{
	Lst::Search< PlayerInfo > sh;
	Lst::Search< NewPlayerInfo > new_sh;
	PlayerInfo* player;
	NewPlayerInfo* new_player;
	int i;
	bool taken;

	

	Dbg_MsgAssert( m_flags.TestMask( mSERVER ),( "Only the server should be assigning ids!\n" ));
	
	for( i = 0; i < 16; i++ )
	{
		taken = false;
		for( player = FirstPlayerInfo( sh ); player; player = NextPlayerInfo( sh ))
		{
			if( player->m_Skater->GetID() == (uint32)i )
			{
				taken = true;
				break;
			}
		}

		if( taken == false )
		{
			for( new_player = FirstNewPlayerInfo( new_sh ); new_player;
					new_player = NextNewPlayerInfo( new_sh ))
			{
				// Pure observers don't and won't have object ids
				if(( new_player->Flags & PlayerInfo::mOBSERVER ) &&
				   ( !( new_player->Flags & PlayerInfo::mPENDING_PLAYER )))
				{
					continue;
				}
				if( new_player->ObjID == i )
				{
					taken = true;
					break;
				}
			}
		}

		if( taken == false )
		{
			return i;
		}
	}

	Dbg_MsgAssert( 0,( "Ran out of player object id's. More than 16 players in game?\n" ));
	return 16;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CSkater*	Manager::GetSkaterByConnection( Net::Conn* conn )
{
	Lst::Search< PlayerInfo > sh;
	PlayerInfo *player;

	for( player = FirstPlayerInfo( sh ); player; player = NextPlayerInfo( sh ))
	{
		if( player->m_Conn == conn )
		{
			return player->m_Skater;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*		Manager::GetPlayerByConnection( Net::Conn* conn )
{
	Lst::Search< PlayerInfo > sh;
	PlayerInfo *player;

	for( player = FirstPlayerInfo( sh, true ); player; player = NextPlayerInfo( sh, true ))
	{
		if( player->m_Conn == conn )
		{
			return player;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*		Manager::GetPlayerByObjectID( unsigned short obj_id )
{
	Lst::Search< PlayerInfo > sh;
	PlayerInfo *player;

	for( player = FirstPlayerInfo( sh ); player; player = NextPlayerInfo( sh ))
	{
		if( player->m_Skater )
		{
			if( player->m_Skater->GetID() == obj_id )
			{
				return player;
			}
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::FindServersOnLAN( void )
{
	if( m_match_client )
	{
		MsgFindServer msg;
        
		msg.m_Timestamp = Tmr::GetTime();
        
#if (defined(__PLAT_NGPS__)||defined(__PLAT_NGC__))
		m_match_client->SendMessageTo( Net::MSG_ID_FIND_SERVER, sizeof( MsgFindServer ), &msg, 
									0xFFFFFFFF, vHOST_PORT, 0 );
#else
		XNetRandom( m_match_client->m_Nonce, sizeof( m_match_client->m_Nonce ));
		memcpy( msg.m_Nonce, &m_match_client->m_Nonce, sizeof( m_match_client->m_Nonce ));
		m_match_client->SendMessageTo( Net::MSG_ID_FIND_SERVER, sizeof( MsgFindServer ), &msg,
									INADDR_BROADCAST, vHOST_PORT, 0 );
#endif
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*			Manager::FirstPlayerInfo( Lst::Search< PlayerInfo > &sh, bool include_observers )
{
	PlayerInfo* player;

	player = sh.FirstItem( m_players );
	if( !include_observers && player && player->IsObserving())
	{
		player = NextPlayerInfo( sh, include_observers );
	}
	
	return player;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*			Manager::NextPlayerInfo( Lst::Search< PlayerInfo > &sh, bool include_observers )
{
	PlayerInfo* player;

	player = sh.NextItem();
	if( !include_observers && player && player->IsObserving())
	{
		player = NextPlayerInfo( sh, include_observers );
	}
	
	return player;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

NewPlayerInfo*		Manager::FirstNewPlayerInfo( Lst::Search< NewPlayerInfo > &sh )
{
	NewPlayerInfo* player;

	player = sh.FirstItem( m_new_players );
	
	return player;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

NewPlayerInfo*		Manager::NextNewPlayerInfo( Lst::Search< NewPlayerInfo > &sh )
{
	NewPlayerInfo* player;

	player = sh.NextItem();
	
	return player;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

NewPlayerInfo*		Manager::GetNewPlayerInfoByObjectID( unsigned short obj_id )
{
	NewPlayerInfo* player;
	Lst::Search< NewPlayerInfo > sh;

	for( player = FirstNewPlayerInfo( sh ); player; player = NextNewPlayerInfo( sh ))
	{
		if( player->ObjID == obj_id )
		{
			return player;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::DestroyNewPlayer( NewPlayerInfo* new_player )
{
	Lst::Node< NewPlayerInfo > *node;
	NewPlayerInfo* new_info;

	Dbg_Assert( new_player );
	Dbg_Printf( "Destroying Pending Player %d\n", new_player->ObjID );
	for( node = m_new_players.GetNext(); node; node = node->GetNext())
	{
		new_info = node->GetData();
		if( new_info == new_player )
		{
			if( m_server )
			{
				Dbg_Printf( "Removing %s\n", new_info->Name );
				m_server->TerminateConnection( new_info->Conn );
			}
			else
			{
				Dbg_Printf( "Removing %s\n", new_info->Name );
				if( new_info->Conn )
				{
					m_client[0]->TerminateConnection( new_info->Conn );
				}
			}
			
			delete node;
			delete new_info;
			break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*			Manager::GetLocalPlayer( void )
{
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;

	for( player = FirstPlayerInfo( sh, true ); player;
			player = NextPlayerInfo( sh, true ))
	{
        if( player->IsLocalPlayer())
		{
			return player;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*			Manager::GetServerPlayer( void )
{
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;

	for( player = FirstPlayerInfo( sh, true ); player;
			player = NextPlayerInfo( sh, true ))
	{
        if( player->IsServerPlayer())
		{
			return player;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*			Manager::GetKingOfTheHill( void )
{
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;

	for( player = FirstPlayerInfo( sh, true ); player;
			player = NextPlayerInfo( sh, true ))
	{
        if( player->IsKing())
		{
			return player;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32				Manager::GetNetworkLevelId( void )
{
	return m_level_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::DisconnectFromServer( void )
{
	Dbg_Message( "Disconnecting from server\n" );
    
	CleanupPlayers();
	
	ClientShutdown();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::CleanupPlayers( void )
{
	free_all_players();
	free_all_pending_players();	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::NumPartiallyLoadedPlayers( void )
{
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	int num;

	num = 0;
	for( player = FirstPlayerInfo( sh ); player; player = NextPlayerInfo( sh ))
	{
		if( player->IsFullyIn() == false )
		{
			num++;
		}
	}

	return num;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::DropPartiallyLoadedPlayers( void )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	PlayerInfo* player, *next;
	Lst::Search< PlayerInfo > sh;

	Dbg_Printf( "****** DROPPING PARTIALLY-LOADED SKATERS\n" );
	for( player = FirstPlayerInfo( sh ); player; player = next )
	{
		next = NextPlayerInfo( sh );
		if( player->IsFullyIn() == false )
		{
			Obj::CSkater* quitting_skater;
						
			quitting_skater = player->m_Skater;
						
			Dbg_Printf( "****** DROPPING PLAYER %d\n", quitting_skater->GetID());
			DropPlayer( player, vREASON_LEFT_OUT );
            skate_mod->remove_skater( quitting_skater );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::DropPlayer( PlayerInfo* info, DropReason reason )
{
	
	char player_name[vMAX_PLAYER_NAME_LEN + 1];

	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	Dbg_Assert( info );
	Dbg_Message( "Removing player %s from the game\n", info->m_Name );

    strcpy( player_name, info->m_Name );
	
	if( !info->IsObserving())
	{
		// if we're playing a game of graffiti, then free up all of the trick objects accumulated by the dropped client
		if ( skate_mod->GetGameMode()->IsTrue( "should_modulate_color" ) )
		{
			skate_mod->GetTrickObjectManager()->FreeTrickObjects( info->m_Skater->GetID() );
		}
	}

	if( OnServer())
	{
		if( !info->IsObserving())
		{
			// Inform all others of the quitter's departure
			for( player = FirstPlayerInfo( sh, true ); player;
					player = NextPlayerInfo( sh, true ))
			{
				if( !player->IsLocalPlayer())
				{
					MsgPlayerQuit msg;
					Net::MsgDesc msg_desc;
					
					msg.m_ObjId = info->m_Skater->GetID();
					msg.m_Reason = (char) reason;
			
					msg_desc.m_Data = &msg;
					msg_desc.m_Length = sizeof( MsgPlayerQuit );
					msg_desc.m_Id = MSG_ID_PLAYER_QUIT;
					msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
					msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
					m_server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
				}
			}

#ifdef __PLAT_NGPS__
			if( ( reason == vREASON_QUIT ) ||
				( reason == vREASON_TIMEOUT ) ||
				( reason == vREASON_BAD_CONNECTION ))
			{
				if( mpStatsMan->IsLoggedIn())
				{
					mpStatsMan->PlayerLeft( info->m_Skater->GetID());
				}
			}
#endif
		}

		if(	( reason == vREASON_BANNED ) ||
			( reason == vREASON_KICKED ))
		{
			m_server->SendMessageTo( MSG_ID_KICKED, 0, NULL,
									   info->m_Conn->GetIP(), info->m_Conn->GetPort(), 0 );
			if( reason == vREASON_BANNED )
			{
				m_server->BanConnection( info->m_Conn );
			}
#ifdef __PLAT_NGPS__
			//m_server->WaitForAsyncCallsToFinish();
#endif // __PLAT_NGPS__

		}
		else if( reason == vREASON_LEFT_OUT )
		{
			m_server->SendMessageTo( MSG_ID_LEFT_OUT, 0, NULL,
									   info->m_Conn->GetIP(), info->m_Conn->GetPort(), 0 );
#ifdef __PLAT_NGPS__
			//m_server->WaitForAsyncCallsToFinish();
#endif // __PLAT_NGPS__
		}
		if( reason != vREASON_OBSERVING )
		{
			if( info->m_Conn )
			{
				info->m_Conn->Invalidate();
				//Dbg_Printf( "DestroyingAllMessageData 1\n" );
				info->m_Conn->DestroyAllMessageData();
			}
		}

		uint32 reason_checksum;
		
		switch( reason )
		{
			case vREASON_QUIT:
			{
				reason_checksum = Script::GenerateCRC("net_message_player_quit");
				break;
			}
			case vREASON_TIMEOUT:
			{
				reason_checksum = Script::GenerateCRC("net_message_player_timed_out");
				break;
			}
			case vREASON_OBSERVING:
			{
				reason_checksum = Script::GenerateCRC("net_message_player_now_observing");
				break;
			}
			case vREASON_BANNED:
			{
				reason_checksum = Script::GenerateCRC("net_message_player_banned");
				break;
			}
			case vREASON_KICKED:
			{
				reason_checksum = Script::GenerateCRC("net_message_player_kicked");
				break;
			}
			case vREASON_LEFT_OUT:
			{
				reason_checksum = Script::GenerateCRC("net_message_player_left_out");
				break;
			}
			case vREASON_BAD_CONNECTION:
			{
				reason_checksum = Script::GenerateCRC("net_message_player_dropped");
				break;
			}
			default:
				reason_checksum = Script::GenerateCRC("net_message_player_timed_out");
				break;
		}

		// If the info is about the server player, no need to print a panel message.
		// Currently this would only happen if the server decided to sit out
		if( info->IsLocalPlayer() == false )
		{	   
			CreateNetPanelMessage( true, reason_checksum, player_name );
		}
		if( reason == vREASON_OBSERVING )
		{
			if( info->IsKing())
			{
				if( m_crown )
				{
					m_crown->RemoveFromKing();
				}
				info->MarkAsKing( false );
			}
			if( info->HasCTFFlag())
			{
				char team_str[64];
				int team;
				Script::CStruct* pParams;
				PlayerInfo* local_player;
				
				team = info->HasWhichFlag();
				sprintf( team_str, "team_%d_name", team + 1 );
				pParams = new Script::CStruct;
		
				pParams->AddInteger( "team", team );
				pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
				Script::RunScript( "flag_returned", pParams );
		
				delete pParams;
		
				local_player = GetLocalPlayer();
				if( local_player && !local_player->IsObserving())
				{
					if( local_player->m_Team == team )
					{
						Script::RunScript( "hide_ctf_arrow" );		
					}
				}
				info->ClearCTFState();
			}
		}
		else
		{
			bool is_server_player;
			
			is_server_player = info->IsServerPlayer();
			
			Dbg_Printf( "Destroying Player %s\n", info->m_Name );
			DestroyPlayer( info );
			if( is_server_player )
			{
				ChooseNewServerPlayer();
				if( GetServerPlayer() == NULL )
				{
					if( ( skate_mod->GetGameMode()->GetNameChecksum() != Script::GenerateCRC( "netlobby" )))
					{
						Script::RunScript( "network_end_game_selected" );
					}
				}
			}
		}

		// Now see if the leaving of this player satisfies ending conditions
		bool all_done = true;
		for( player = FirstPlayerInfo( sh ); player;
				player = NextPlayerInfo( sh ))
		{
			if( !player->m_flags.TestMask( PlayerInfo::mRUN_ENDED))
			{
				all_done = false;
				break;
			}
		}
	
		if( all_done )
		{
			Net::MsgDesc msg_desc;

			msg_desc.m_Id = MSG_ID_GAME_OVER;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
			for( player = FirstPlayerInfo( sh, true ); player;
					player = NextPlayerInfo( sh, true ))
			{
				m_server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
			}
		}
	}
	else
	{
		DestroyPlayer( info );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Prefs::Preferences* Manager::GetNetworkPreferences( void )
{
	return &m_network_preferences;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Prefs::Preferences* Manager::GetTauntPreferences( void )
{
	return &m_taunt_preferences;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char*			Manager::GetNameFromArrayEntry( char* array_name, uint32 checksum )
{
	Script::CArray* pArray;
	const char* string = NULL;
	int i;

	pArray = Script::GetArray( array_name );
	Dbg_Assert( pArray );

	for( i = 0; i < (int)pArray->GetSize(); i++ )
	{   
		uint32 value;
		Script::CStruct* pStructure;
		
		 
		pStructure = pArray->GetStructure( i );
		Dbg_Assert( pStructure );
		
		pStructure->GetChecksum( "checksum", &value, true );
		if( value == checksum )
		{
			pStructure->GetText( "name", &string, true );
		}
	}

	return string;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					Manager::GetIntFromArrayEntry( char* array_name, uint32 checksum, uint32 field_checksum )
{
	Script::CArray* pArray;
	int int_val;
	int i;

	pArray = Script::GetArray( array_name );
	Dbg_Assert( pArray );

	int_val = 0;
	for( i = 0; i < (int)pArray->GetSize(); i++ )
	{   
		uint32 value;
		Script::CStruct* pStructure;
		
		 
		pStructure = pArray->GetStructure( i );
		Dbg_Assert( pStructure );
		
		pStructure->GetChecksum( "checksum", &value, true );
		if( value == checksum )
		{
			pStructure->GetInteger( field_checksum, &int_val, true );
		}
	}

	return int_val;
}

/******************************************************************/
/* Make use of the network preferences we currently have stored	  */
/*                                                                */
/******************************************************************/

void	Manager::UsePreferences( void )
{
	Net::Manager * net_man = Net::Manager::Instance();
	Prefs::Preferences* pPreferences;
	Script::CStruct* pStructure;
	Script::CArray* pArray;
	const char* ip, *phone, *login, *password, *host, *domain;
	uint32 dev_type, ip_assignment, checksum;
	bool use_dhcp, use_authentication, use_pcmcia_card;
	int i;

	// Initialize the master server list
	for( i = 0; i < vMAX_MASTER_SERVERS; i++ )
	{
		m_master_servers[i][0] = '\0';
	}

	pPreferences = GetNetworkPreferences();

	pArray = Script::GetArray("default_master_servers");
	Dbg_Assert( pArray );

	pStructure = pPreferences->GetPreference( Script::GenerateCRC("use_default_master_servers"));
	pStructure->GetChecksum( "Checksum", &checksum, true );
	if( checksum == Script::GenerateCRC( "boolean_true" ))
	{
		for( i = 0; i < (int)pArray->GetSize(); i++ )
		{   
			Script::CStruct* pStructure = pArray->GetStructure( i );
			Dbg_Assert( pStructure );
            
			pStructure->GetText( "name", &ip, true );
			strcpy( m_master_servers[ i ], ip );
		}
	}
	else
	{
		int num_servers;

		num_servers = 0;

		// First, add the manually-entered master servers to the list, if they're not
		// 0.0.0.0
		pStructure = pPreferences->GetPreference( Script::GenerateCRC("master_server1") );
		pStructure->GetText( "ui_string", &ip, true );
		if( strcmp( ip, "0.0.0.0" ))
		{
			strcpy( m_master_servers[ num_servers ], ip );
			num_servers++;
		}
				
		pStructure = pPreferences->GetPreference( Script::GenerateCRC("master_server2") );
		pStructure->GetText( "ui_string", &ip, true );
		if( strcmp( ip, "0.0.0.0" ))
		{
			strcpy( m_master_servers[ num_servers ], ip );
			num_servers++;
		}

		// Now tack on our default servers, just in case
		for( i = 0; i < (int)pArray->GetSize(); i++ )
		{   
			Script::CStruct* pStructure = pArray->GetStructure( i );
			Dbg_Assert( pStructure );
            
			pStructure->GetText( "name", &ip, true );
			strcpy( m_master_servers[ num_servers + i ], ip );
		}
	}

	pStructure = pPreferences->GetPreference( Script::GenerateCRC("broadband_type") );
	pStructure->GetChecksum( "Checksum", &ip_assignment, true );
	use_dhcp = false;
	if( ip_assignment == Script::GenerateCRC( "ip_dhcp" ))
	{
		Dbg_Printf( "************* Supposed to use DHCP here !! ***************\n" );
		use_dhcp = true;
	}

	// Override IP settings with the viewer_ip script variable, used to launch a server for real-time communication
#ifdef __PLAT_XBOX__
	const char* viewer_ip = Script::GetString( "xbox_viewer_ip" );
#endif
#ifdef __PLAT_NGPS__
	const char* viewer_ip = Script::GetString( "viewer_ip" );
#endif
#ifdef __PLAT_NGC__
	const char* viewer_ip = Script::GetString( "ngc_viewer_ip" );
#endif
	const char* viewer_gateway = Script::GetString( "viewer_gateway" );
	if( viewer_ip && !*viewer_ip )
	{
		viewer_ip = NULL;
	}
	if( viewer_ip )
	{
		Dbg_Printf( "************* Got a viewer IP !! ***************\n" );
		Prefs::Preferences* prefs;
        Script::CScriptStructure* pTempStructure;

		use_dhcp = false;

        pTempStructure = new Script::CScriptStructure;
		pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, 
								  viewer_ip );
        prefs = GetNetworkPreferences();
		prefs->SetPreference( Script::GenerateCRC("ip_address"), pTempStructure );
		delete pTempStructure;

		if( viewer_gateway && *viewer_gateway )
		{
			pTempStructure = new Script::CScriptStructure;
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, 
									  viewer_gateway );
			prefs = GetNetworkPreferences();
			prefs->SetPreference( Script::GenerateCRC("gateway"), pTempStructure );
			delete pTempStructure;
		}
	}
		
	net_man->SetDHCP( use_dhcp );

    if( use_dhcp )
	{
		pStructure = pPreferences->GetPreference( Script::GenerateCRC("host_name") );
		pStructure->GetText( "ui_string", &host, true );
		net_man->SetHostName((char*) host );

		pStructure = pPreferences->GetPreference( Script::GenerateCRC("domain_name") );
		pStructure->GetText( "ui_string", &domain, true );
		net_man->SetDomainName((char*) domain );
	}
	else
	{
		pStructure = pPreferences->GetPreference( Script::GenerateCRC("ip_address") );
		pStructure->GetText( "ui_string", &ip, true );
		Dbg_Printf( "****************** Setting Local IP to %s ********** \n", ip );
		net_man->SetLocalIP((char*) ip );
        
		pStructure = pPreferences->GetPreference( Script::GenerateCRC("gateway") );
		pStructure->GetText( "ui_string", &ip, true );
		Dbg_Printf( "****************** Setting Gateway to %s ********** \n", ip );
		net_man->SetGateway((char*) ip );

		pStructure = pPreferences->GetPreference( Script::GenerateCRC("subnet_mask") );
		pStructure->GetText( "ui_string", &ip, true );
		Dbg_Printf( "****************** Setting Subnet to %s ********** \n", ip );
		net_man->SetSubnetMask((char*) ip );
	}

#ifdef USE_DNS
	pStructure = pPreferences->GetPreference( Script::GenerateCRC("auto_dns") );
	pStructure->GetChecksum( "Checksum", &checksum, true );
	if( checksum == Script::GenerateCRC( "boolean_true" ) && ( viewer_ip == NULL ))
	{
		Dbg_Printf( "****************** Setting DNS to 0.0.0.0 ******** \n" );
		net_man->SetDNSServer( 0, "" );
		net_man->SetDNSServer( 1, "" );
		net_man->SetDNSServer( 2, "" );
	}
	else
	{
		if( viewer_ip )
		{
			net_man->SetDNSServer( 0, "205.147.0.100" );
			net_man->SetDNSServer( 1, "205.147.0.102" );
		}
		else
		{
			pStructure = pPreferences->GetPreference( Script::GenerateCRC("dns_server") );
			pStructure->GetText( "ui_string", &ip, true );
			Dbg_Printf( "****************** Setting DNS 1 to %s ******** \n", ip );
			net_man->SetDNSServer( 0, (char*) ip );
		
			pStructure = pPreferences->GetPreference( Script::GenerateCRC("dns_server2") );
			pStructure->GetText( "ui_string", &ip, true );
			Dbg_Printf( "****************** Setting DNS 2 to %s ******** \n", ip );
			net_man->SetDNSServer( 1, (char*) ip );
		}
	}
#else
#ifdef USE_FREE_DNS
	net_man->SetDNSServer( 0, "204.80.125.130" );	// Free DNS server in LA
	net_man->SetDNSServer( 1, "204.57.55.100" );	// Free DNS server in Boston
	net_man->SetDNSServer( 2, "" );
#else
	net_man->SetDNSServer( 0, "" );	// Free DNS server in LA
	net_man->SetDNSServer( 1, "" );	// Free DNS server in Boston
	net_man->SetDNSServer( 2, "" );
#endif // USE_FREE_DNS
#endif // USE_DNS

	pStructure = pPreferences->GetPreference( Script::GenerateCRC("dialup_number") );
	pStructure->GetText( "ui_string", &phone, true );
	net_man->SetISPPhoneNumber((char*) phone );

	pStructure = pPreferences->GetPreference( Script::GenerateCRC("dialup_username") );
	pStructure->GetText( "ui_string", &login, true );
	net_man->SetISPLogin((char*) login );

	pStructure = pPreferences->GetPreference( Script::GenerateCRC("dialup_password") );
	pStructure->GetText( "ui_string", &password, true );
	net_man->SetISPPassword((char*) password );
	
	pStructure = pPreferences->GetPreference( Script::GenerateCRC("device_type") );
	pStructure->GetChecksum( "Checksum", &dev_type, true );
	const char* use_pcmcia = Script::GetString( "use_pcmcia" );
	use_pcmcia_card = use_pcmcia[0] != '\0';
	
	if(( dev_type == Script::GenerateCRC( "device_broadband_pc" )) || use_pcmcia_card ) 
	{
		if( viewer_ip )
		{
			Script::RunScript( "spoof_pcmcia_adaptor_setup" );
		}
		net_man->SetDeviceType( Net::vDEV_TYPE_PC_ETHERNET );
		net_man->SetConnectionType( Net::vCONN_TYPE_ETHERNET );
	}
	else if(( dev_type == Script::GenerateCRC( "device_broadband_usb" )) || ( viewer_ip ))
	{
		if( viewer_ip )
		{
			Script::RunScript( "spoof_usb_adaptor_setup" );
		}
		net_man->SetDeviceType( Net::vDEV_TYPE_USB_ETHERNET );
		net_man->SetConnectionType( Net::vCONN_TYPE_ETHERNET );
	}
	else if( dev_type == Script::GenerateCRC( "device_broadband_usb_pppoe" ))
	{
		net_man->SetDeviceType( Net::vDEV_TYPE_USB_ETHERNET );
		net_man->SetConnectionType( Net::vCONN_TYPE_PPPOE );
	}
	else if( dev_type == Script::GenerateCRC( "device_usb_modem" ))
	{
		net_man->SetDeviceType( Net::vDEV_TYPE_USB_MODEM );
		net_man->SetConnectionType( Net::vCONN_TYPE_MODEM );
		if( GetMaxPlayers() > 3 )
		{
			SetMaxPlayers( 3 );
		}
		SetMaxObservers( 0 );
	}
	else if( dev_type == Script::GenerateCRC( "device_sony_modem" ))
	{
		net_man->SetDeviceType( Net::vDEV_TYPE_SONY_MODEM );
		net_man->SetConnectionType( Net::vCONN_TYPE_MODEM );
		if( GetMaxPlayers() > 3 )
		{
			SetMaxPlayers( 3 );
		}
		SetMaxObservers( 0 );
	}
	else if( dev_type == Script::GenerateCRC( "device_broadband_pc_pppoe" ))
	{
		net_man->SetDeviceType( Net::vDEV_TYPE_PC_ETHERNET );
		net_man->SetConnectionType( Net::vCONN_TYPE_PPPOE );
	}
	else
	{   
		net_man->SetConnectionType( Net::vCONN_TYPE_NONE );
		net_man->SetDeviceType( Net::vDEV_TYPE_NONE );
	}

	pStructure = pPreferences->GetPreference( Script::GenerateCRC("dialup_authentication") );
	pStructure->GetChecksum( "Checksum", &checksum, true );
	use_authentication = false;
	if( checksum == Script::GenerateCRC( "boolean_true" ))
	{
		use_authentication = true;
	}
	net_man->SetDialupAuthentication( use_authentication );
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::RandomizeSkaterStartingPoints( void )
{
	int i, j, offset, max_skaters;

	// choose a random offset for these starting points
	offset = Mth::Rnd( 256 );

	if( InNetGame())
	{
		max_skaters = Mdl::Skate::vMAX_SKATERS;
	}
	else
	{
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		max_skaters = skate_mod->GetGameMode()->GetMaximumNumberOfPlayers();
		
		// PATCH  (Mick)
		// If playing a splitscreen game in the park editor
		// then set the Maximum number of players to 2
		// so we only use the 1P and 2P start points
		// oterwise, we will use all the auto-horse points
		// which end up in stupid positions
		if (Ed::CParkEditor::Instance()->UsingCustomPark() && !skate_mod->GetGameMode()->IsTrue("is_horse")) 		// is it a custom park and not horse mode?
		{
			max_skaters = 2;
			offset = 0;	   // reset the offset, so they always get used the same way
		}
		
		
	}

	for( i = 0; i < max_skaters; i++ )
	{
		m_skater_starting_points[i] = -1;
	}

	// For each skater, choose a random starting point
	for( i = 0; i < max_skaters; i++ )
	{
		bool unique;
        
		// Choose a random starting point until it's unique
		do
		{
			m_skater_starting_points[i] = Mth::Rnd( max_skaters );
			unique = true;
			for( j = 0; j < max_skaters; j++ )
			{
				// skip myself
				if( i == j )
				{
					continue;
				}

				if( m_skater_starting_points[i] == m_skater_starting_points[j] )
				{
					unique = false;
					break;
				}
			}
		} while( !unique );
	}

	// Offset these random sequential numbers by a random number so that we pick
	// four sequential numbers within a possibly larger range of starting points
	for( i = 0; i < max_skaters; i++ )
	{
		m_skater_starting_points[i] += offset;
	}

	m_crown_spawn_point = Mth::Rnd( vNUM_CROWN_SPAWN_POINTS );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetSkaterStartingPoints( int* start_points )
{
	int i;

	for( i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
	{
		m_skater_starting_points[i] = start_points[i];
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetSkaterStartingPoint( int skater_id )
{
	return m_skater_starting_points[skater_id];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::LoadPendingPlayers( void )
{
	PlayerInfo* player, *next;
	Lst::Search< PlayerInfo > sh;
	int num_new_players;

	Dbg_Assert( m_server );

	num_new_players = 0;

#ifdef __PLAT_XBOX__
	// Since XBOX has a quicker timeout, we need to tell clients that this process (loading pending players) may take much longer
	// than the timeout allows. Clients will allow the server N seconds to get back to them after receiving this message.
	// NOTE: This will only be guaranteed to work in LAN mode, because if the packet is lost, the original timeout will stand
	bool players_pending;

	players_pending = false;
	// Add all pending players to the game
	for( player = FirstPlayerInfo( sh, true ); player; player = NextPlayerInfo( sh, true ))
	{
		if( player->IsPendingPlayer())
		{
			players_pending = true;
			break;
		}
	}
	if( players_pending )
	{
		Net::MsgDesc msg_desc;
		for( player = FirstPlayerInfo( sh, true ); player; player = NextPlayerInfo( sh, true ))
		{
			if( !player->IsLocalPlayer())
			{
				char num_seconds;

				num_seconds = 15;

				msg_desc.m_Data = &num_seconds;
				msg_desc.m_Length = sizeof( char );
				msg_desc.m_Id = MSG_ID_WAIT_N_SECONDS;
				msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
				msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
				m_server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
			}
		}		
		m_server->SendData();
	}
	
#endif	

	// Add all pending players to the game
	for( player = FirstPlayerInfo( sh, true ); player; player = next )
	{
		next = NextPlayerInfo( sh, true );
		if( player->m_flags.TestMask( PlayerInfo::mPENDING_PLAYER ))
		{
			NewPlayerInfo new_player;

			Dbg_Assert( player->IsObserving());

			num_new_players++;

			strcpy( new_player.Name, player->m_Name );
			new_player.ObjID = GetNextPlayerObjectId();
			new_player.Conn = player->m_Conn;
			new_player.Flags = PlayerInfo::mJUMPING_IN;
			new_player.Profile = player->m_Profile;
			new_player.Rating = player->m_Rating;
			new_player.VehicleControlType = player->m_VehicleControlType;
			
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());
			*new_player.mpSkaterProfile = *player->mp_SkaterProfile;
			Mem::Manager::sHandle().PopContext();
            
			DestroyPlayer( player );

			DeferredNewPlayer( &new_player, ( num_new_players + 1 ) * vDEFERRED_JOIN_FRAMES );

		}
	}

	// Invalidate old positions. Player will be re-added to the world when we get a
	// sufficient number of object updates
	for( player = FirstPlayerInfo( sh ); player; player = NextPlayerInfo( sh ))
	{
		if( player->IsLocalPlayer())
		{
			continue;
		}
		
		player->m_Skater->RemoveFromCurrentWorld();
		player->m_Skater->Resync();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::StartNetworkLobby( void )
{
	int num_teams;	

	// This version of "StartNetworkLobby"
	// exits the game mode, and enters the lobby
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
    
	if( OnServer())
	{
		m_level_id = GetLevelFromPreferences();
		num_teams = GetNumTeams();
	}
	else
	{
		num_teams = skate_mod->GetGameMode()->NumTeams();
	}

	// set the game type for the server
	m_gamemode_id = Script::GenerateCRC( "netlobby" );
	skate_mod->GetGameMode()->LoadGameType( m_gamemode_id );
	skate_mod->GetGameMode()->SetNumTeams( num_teams );

	m_game_over = false;

    // launches the game for this client
	// (should really wait until the clients are ready)
	
	//Mdl::ScriptRetry( NULL, NULL );

	ResetPlayers();

	Dbg_Printf( "Starting network lobby\n" );
	
	// Clear the king of the hill
	if(( player = GetKingOfTheHill()))
	{
		player->MarkAsKing( false );
	}

	for( player = FirstPlayerInfo( sh, true ); player; player = NextPlayerInfo( sh, true ))
	{
		player->ClearCTFState();
	}

	ClearTriggerEventList();

	m_lobby_start_time = Tmr::GetTime();

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ShouldStopAtZero( void )
{
	uint32 should_stop;

	if( ( m_gamemode_id == Script::GenerateCRC( "netgoalattack" )) ||
		( m_gamemode_id == Script::GenerateCRC( "king" )) ||
		( m_gamemode_id == Script::GenerateCRC( "netking" )) ||
		( m_gamemode_id == Script::GenerateCRC( "netscorechallenge" )) ||
		( m_gamemode_id == Script::GenerateCRC( "scorechallenge" )))
	{
		return true;
	}

	if( InNetGame() == false )
	{
		return false;
	}

	if( m_gamemode_id == Script::GenerateCRC( "netctf" ))
	{
		uint32 mode;

		mode = GetNetworkPreferences()->GetPreferenceChecksum( Script::GenerateCRC("ctf_game_type"), Script::GenerateCRC("checksum") );
		if( mode != CRCD(0x938ab24b,"timed_ctf"))
		{
			return true;
		}
	}

	should_stop = GetNetworkPreferences()->GetPreferenceChecksum( Script::GenerateCRC("stop_at_zero"), Script::GenerateCRC("checksum") );
	if( should_stop == CRCD(0xf81bc89b,"boolean_true"))
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::StartNetworkGame( void )
{
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	int time_limit;
	Prefs::Preferences* pPreferences;
	Net::MsgDesc msg_desc;

	Dbg_MsgAssert( OnServer(),( "Server-only function called on client" ));

	pPreferences = NULL;
	if( InNetGame())
	{
		pPreferences = GetNetworkPreferences();
	}
	else 
	{
		pPreferences = skate_mod->GetSplitScreenPreferences();
	}

	

	Net::Server* server;
	server = GetServer();
	Dbg_MsgAssert( server,( "Server has not been started\n" ));

	// Sorry boys, you didn't quite make it in
	free_all_pending_players();

	// set the game type for the server, from the network preferences
	m_gamemode_id = GetGameTypeFromPreferences();
	//skate_mod->GetGameMode()->LoadGameType( m_gamemode_id );

	// Mick: Moved this to after the setting of the game type, so we can check for horse mode in it
	// (used to explicitly set 1P and 2P start points in 2P split screen (not horse) game)
	RandomizeSkaterStartingPoints();


	if( m_gamemode_id == Script::GenerateCRC( "horse" ))
	{
		time_limit = pPreferences->GetPreferenceValue( Script::GenerateCRC("horse_time_limit"), Script::GenerateCRC("time") );
	}
	else
	{
		time_limit = pPreferences->GetPreferenceValue( Script::GenerateCRC("time_limit"), Script::GenerateCRC("time") );
	}

	Dbg_Printf("Starting network game of type %s with time limit %d\n", Script::FindChecksumName(m_gamemode_id), time_limit);
	m_level_id = GetLevelFromPreferences();
	
	MsgGameInfo game_info_msg;
	game_info_msg.m_GameMode = m_gamemode_id;
	game_info_msg.m_FireballLevel = GetFireballLevelFromPreferences();
	Dbg_Printf( "***** FIREBALL LEVEL: %d\n", game_info_msg.m_FireballLevel );
	if( ( m_gamemode_id == CRCD(0xec200eaa,"netgoalattack")) ||
		( m_gamemode_id == CRCD(0x5d32129c,"king")) ||
		( m_gamemode_id == CRCD(0x6ef8fda0,"netking")) ||
		( m_gamemode_id == CRCD(0x1498240a,"netscorechallenge")) ||
		( m_gamemode_id == CRCD(0xf135ecb6,"scorechallenge")) ||
		( m_gamemode_id == CRCD(0x3d6d444f,"firefight")) ||
		( m_gamemode_id == CRCD(0xbff33600,"netfirefight")))
	{
		game_info_msg.m_TimeLimit = 0;
	}
	else
	{

		if( m_gamemode_id == Script::GenerateCRC( "netctf" ))
		{
			uint32 mode;

            mode = pPreferences->GetPreferenceChecksum( Script::GenerateCRC("ctf_game_type"), Script::GenerateCRC("checksum") );
			if( mode == CRCD(0x938ab24b,"timed_ctf"))
			{
				game_info_msg.m_TimeLimit = time_limit;
			}
			else
			{
				game_info_msg.m_TimeLimit = 0;
			}
		}
		else
		{
			game_info_msg.m_TimeLimit = time_limit;
		}
	}
	
	game_info_msg.m_StopAtZero = (char) ShouldStopAtZero();
	game_info_msg.m_TargetScore = pPreferences->GetPreferenceValue( Script::GenerateCRC("target_score"), Script::GenerateCRC("score") );
	Dbg_Printf( "Target score %d\n", (int)game_info_msg.m_TargetScore );
	game_info_msg.m_TeamMode = GetNumTeams();

	// Here, make sure we get a game id that does not match our last game id
	do
	{
		game_info_msg.m_GameId = 1 + (unsigned char) Mth::Rnd( 255 );
	} while( game_info_msg.m_GameId == m_last_end_of_run_game_id );

	game_info_msg.m_CrownSpawnPoint = m_crown_spawn_point;
	memcpy( game_info_msg.m_StartPoints, m_skater_starting_points, Mdl::Skate::vMAX_SKATERS * sizeof( int ));

	msg_desc.m_Data = &game_info_msg;
	msg_desc.m_Length = sizeof( MsgGameInfo );
	msg_desc.m_Id = MSG_ID_GAME_INFO;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	for( player = FirstPlayerInfo( sh, true ); player; player = NextPlayerInfo( sh, true ))
	{
		player->m_BestCombo = 0;
		player->ClearCTFState();
		player->m_flags.ClearMask( PlayerInfo::mRESTARTING );
		server->EnqueueMessage( player->GetConnHandle(), &msg_desc );

		// In splitscreen games, just send it to the first client
		if( InNetGame() == false )
		{
			break;
		}
	}

	// Clear the king of the hill
	if(( player = GetKingOfTheHill()))
	{
		player->MarkAsKing( false );
	}
	SetCurrentLeader( NULL );
	SetCurrentLeadingTeam( vNO_TEAM );
	ClearTriggerEventList();
	ResetPlayers();
	g_CheatsEnabled = false;

#ifdef __PLAT_NGPS__
	if( mpStatsMan->IsLoggedIn())
	{
		mpStatsMan->StartNewGame();
	}
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::ResetPlayers( void )
{
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;

	for( player = FirstPlayerInfo( sh, true ); player;
			player = NextPlayerInfo( sh, true ))
	{
		Mdl::Score* score;
		player->m_flags.ClearMask( PlayerInfo::mRUN_ENDED );
		if( player->m_Skater )
		{
			Obj::CSkaterScoreComponent* p_skater_score_component = GetSkaterScoreComponentFromObject(player->m_Skater);
			Dbg_Assert(p_skater_score_component);
			score = p_skater_score_component->GetScore();
			
			if( score )
			{
				score->Reset();
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::SetLevel( uint32 level_id )
{
	m_level_id = level_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetNetworkGameId( unsigned char id )
{
	m_game_id = id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

unsigned char	Manager::GetNetworkGameId( void )
{
	return m_game_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 Manager::GetGameTypeFromPreferences( void )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	Prefs::Preferences* pPreferences;
	 
	pPreferences = NULL;
	if( InNetGame())
	{
		pPreferences = GetNetworkPreferences();
	}
	else 
	{
		pPreferences = skate_mod->GetSplitScreenPreferences();
	}

	Script::CStruct* pStructure = pPreferences->GetPreference( Script::GenerateCRC("game_type") );

	Dbg_Assert( pStructure );

	uint32 gametype_id;
	pStructure->GetChecksum( "checksum", &gametype_id, true );

	return gametype_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 Manager::GetLevelFromPreferences( void )
{
	uint32 level_id;

	Prefs::Preferences* pPreferences;

	pPreferences = GetNetworkPreferences();
	Script::CStruct* pStructure = pPreferences->GetPreference( Script::GenerateCRC("level") );

	Dbg_Assert( pStructure );
    pStructure->GetChecksum( "checksum", &level_id, true );
	
	return level_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetFireballLevelFromPreferences( void )
{
	uint32 checksum;
	Script::CStruct* pStructure;
		
	pStructure = m_network_preferences.GetPreference( CRCD(0xce238f5d,"fireball_difficulty") );
	pStructure->GetChecksum( "Checksum", &checksum, true );
	if( checksum == Script::GenerateCRC( "num_1" ))
	{
		return 1;
	}
	else if( checksum == Script::GenerateCRC( "num_2" ))
	{
		return 2;
	}
	else if( checksum == Script::GenerateCRC( "num_3" ))
	{
		return 3;
	}                                              
	else if( checksum == Script::GenerateCRC( "num_4" ))
	{
		return 4;
	}                                              
	else if( checksum == Script::GenerateCRC( "num_5" ))
	{
		return 5;
	}                                              

	return 3;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ToggleMetrics( void )
{
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();

	m_metrics_on ^= 1;

	if( m_metrics_on )
	{
		mlp_manager->AddLogicTask( *m_render_metrics_task );
	}
	else
	{
		m_render_metrics_task->Remove();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ToggleScores( void )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Game::CGameMode* pGameMode;
	bool show_score;

	pGameMode = skate_mod->GetGameMode();
	show_score = pGameMode->ShouldAccumulateScore();

	m_scores_on ^= 1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::TogglePlayerNames( void )
{
	m_draw_player_names ^= 1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ShouldDrawPlayerNames( void )
{
	Prefs::Preferences* pPreferences;
	Script::CStruct* pStructure;
	uint32 checksum = 0;
		
	pPreferences = GetNetworkPreferences();
	pStructure = pPreferences->GetPreference( CRCD(0xf7dd263,"show_names") );
	pStructure->GetChecksum( CRCD(0x21902065,"checksum"), &checksum, false );
	return( checksum == CRCD(0xf81bc89b,"boolean_true") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::RemoveModemStateTask( void )
{
	m_modem_state_task->Remove();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ConnectToInternet( uint32 success_script, uint32 failure_script )
{
#ifdef __PLAT_NGPS__
	Net::Manager * net_man = Net::Manager::Instance();
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	bool was_online, is_online;

	m_connection_success_script = success_script;
	m_connection_failure_script = failure_script;
    
	Dbg_Printf( "Connecting to the internet\n" );
	was_online = net_man->IsOnline();
	if( was_online )
	{
		is_online = true;
	}
	else
	{
		m_last_modem_state = -1;
		is_online = net_man->ConnectToInternet();
	}
	
	if( is_online )
	{   
		if( m_connection_success_script > 0 )
		{
			Script::RunScript( m_connection_success_script );
		}
	}
	else if(( net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM ) ||
			( net_man->GetConnectionType() == Net::vCONN_TYPE_PPPOE ))
	{   
		mlp_man->AddLogicTask( *m_modem_state_task );
	}
	
	return is_online;
#else
#ifdef __PLAT_XBOX__
	mpAuthMan->UserLogon();
#endif		// __PLAT_XBOX__
	
	return false;
#endif		// __PLAT_NGPS__	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::CancelConnectToInternet( void )
{
	
	Net::Manager * net_man = Net::Manager::Instance();
	Manager * gamenet_man = Manager::Instance();
	
	switch( net_man->GetModemState())
	{
		case Net::vMODEM_STATE_DIALING:
		case Net::vMODEM_STATE_CONNECTED:
			gamenet_man->RemoveModemStateTask();
			gamenet_man->DisconnectFromInternet();
			break;
		case Net::vMODEM_STATE_ERROR:
#ifdef __PLAT_NGPS__
			if( net_man->GetModemError() == SNTC_ERR_NOMODEM )
			{
				Script::RunScript( gamenet_man->m_connection_failure_script );
			}
			else
			{
				gamenet_man->RemoveModemStateTask();
				gamenet_man->DisconnectFromInternet();
				break;
			}
#endif		// __PLAT_NGPS__
			gamenet_man->RemoveModemStateTask();
			break;;
		case Net::vMODEM_STATE_DISCONNECTED:
			Dbg_Printf( "Removing modem state task\n" );
			gamenet_man->RemoveModemStateTask();
			Script::RunScript( gamenet_man->m_connection_failure_script );
			break;
		case Net::vMODEM_STATE_LOGGED_IN:
		{
			net_man->SetModemState( Net::vMODEM_STATE_READY_TO_TRANSMIT );
			break;
		}
		default:
			break;

	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::DisconnectFromInternet( uint32 callback_script )
{
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	Net::Manager * net_man = Net::Manager::Instance();

	Dbg_Assert(	( net_man->GetConnectionType() == Net::vCONN_TYPE_MODEM ) ||
				( net_man->GetConnectionType() == Net::vCONN_TYPE_PPPOE ));
	   
	m_last_modem_state = -1;
	if( callback_script != 0 )
	{
        m_connection_failure_script = callback_script;
	}

	mlp_man->AddLogicTask( *m_modem_state_task );
	SetServerListState( vSERVER_LIST_STATE_SHUTDOWN );
	net_man->DisconnectFromInternet();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::CreateNetPanelMessage( bool broadcast, uint32 message_checksum, char* string_parm_1,
										char* string_parm_2, Obj::CMovingObject* object, char* properties, bool important,
										int time )
{
	if( broadcast )
	{
		PlayerInfo* player;
		Lst::Search< PlayerInfo > sh;
		MsgPanelMessage msg;
		Net::MsgDesc msg_desc;

		msg.m_Parm1[0] = '\0';
		msg.m_Parm2[0] = '\0';
		msg.m_StringId = message_checksum;
		msg.m_Time = time;
		msg.m_PropsStructureCRC = (int) Script::GenerateCRC( properties );
		if( string_parm_1 )
		{
			strcpy( msg.m_Parm1, string_parm_1 );
		}
		if( string_parm_2 )
		{
			strcpy( msg.m_Parm2, string_parm_2 );
		}

		msg_desc.m_Data = &msg;
		msg_desc.m_Length = sizeof( MsgPanelMessage );
		msg_desc.m_Id = MSG_ID_PANEL_MESSAGE;
		if( important )
		{
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
		}

		Dbg_Assert( m_server );
        for( player = FirstPlayerInfo( sh, true ); player;
				player = NextPlayerInfo( sh, true ))
		{
			m_server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
		}
	}
	else
	{   
		Script::CScriptStructure* pStructure = new Script::CScriptStructure;
		const char* text;	
		char temp_msg[256];
		char final_msg[256];
		char* format_start;

		text = Script::GetLocalString( message_checksum );
		strcpy( temp_msg, text );
		if(( format_start = strstr( temp_msg, "%s0" )))
		{
			*format_start = '\0';

			strcpy( final_msg, temp_msg );
			Dbg_Assert( string_parm_1 );	// Need a parameter for this format string
			strcat( final_msg, string_parm_1 );
			format_start += 3;	// skip the format tag
			strcat( final_msg, format_start );

			pStructure->AddComponent( Script::GenerateCRC( "Text" ), ESYMBOLTYPE_STRING, final_msg );
		}
		else
		{
			pStructure->AddComponent( Script::GenerateCRC( "Text" ), ESYMBOLTYPE_STRING, text );
		}
		
		pStructure->AddComponent( Script::GenerateCRC( "id" ), ESYMBOLTYPE_NAME, Script::GenerateCRC( "net_panel_msg" ));
		pStructure->AddInteger( Script::GenerateCRC( "msg_time" ), time );
		Script::RunScript( "create_net_panel_message", pStructure );
		
		delete pStructure;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ShouldSendScoreUpdates( void )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	Game::CGameMode* pGameMode = skate_mod->GetGameMode();
	
	// We need to send score updates in goal attack
	if( ( pGameMode->ShouldAccumulateScore() == false ) &&
		( pGameMode->ShouldTrackBestCombo() == false ) &&
		( pGameMode->GetNameChecksum() != CRCD(0xbff33600,"netfirefight")) &&
		( pGameMode->GetNameChecksum() != CRCD(0xec200eaa,"netgoalattack")) &&
		( pGameMode->GetNameChecksum() != CRCD(0x6c5ff266,"netctf")))
	{
		return false;
	}
	
	if( pGameMode->EndConditionsMet() && ( pGameMode->GetNameChecksum() != CRCD(0xbff33600,"netfirefight")))
	{
		return false;
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::SetCurrentLeader( PlayerInfo* player )
{
	m_leader = player;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo*		Manager::GetCurrentLeader( void )
{
	return m_leader;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::SetCurrentLeadingTeam( int team )
{
	m_leading_team = team;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int				Manager::GetCurrentLeadingTeam( void )
{
	return m_leading_team;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetLastScoreUpdateTime( int time )
{
	m_last_score_update = time;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Manager::GetLastScoreUpdateTime( void )
{
	return m_last_score_update;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::MarkReceivedFinalScores( bool received )
{
	m_received_final_scores = received;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::HaveReceivedFinalScores( void )
{
	return m_received_final_scores;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::AddSpawnedTriggerEvent( int node, int obj_id, uint32 script )
{
	TriggerEvent* queued_script;
	Lst::Search< TriggerEvent > sh;
	
	// Check to make sure we don't queue duplicate scripts
	for( queued_script = sh.FirstItem( m_trigger_events ); queued_script; 
			queued_script = sh.NextItem())
	{
		if( queued_script->m_Script == script )
		{
			return;
		}
	}

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	TriggerEvent* event = new TriggerEvent;

	event->m_Node = node;
	event->m_ObjID = obj_id;
	event->m_Script = script;

	m_trigger_events.AddToTail( event );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ClearTriggerEventList( void )
{
	Lst::Search< TriggerEvent > sh;
	TriggerEvent* event, *next;

	for( event = sh.FirstItem( m_trigger_events ); event; event = next )
	{
		next = sh.NextItem();
		delete event;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetProSetFlags( char flags )
{
	m_proset_flags = flags;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SendChatMessage( char* message )
{
	
	MsgChat msg;
	int size;
	PlayerInfo* player;
	Script::CStruct* p_params;
	char final_msg[256];
	Net::MsgDesc msg_desc;

    Dbg_Assert( message );
	Dbg_Assert( m_client[0] );

	// No point in printing/sending an empty message
	if( message[0] == '\0' )
	{
		return;
	}

	player = GetLocalPlayer();
	Dbg_Assert( player );
    
	strcpy( msg.m_Name, player->m_Name );
	strcpy( msg.m_ChatMsg, message );
	if( player->IsObserving() )
	{
		msg.m_ObjId = Mdl::Skate::vMAX_SKATERS; 	// Signifies "observer"
	}
	else
	{
		msg.m_ObjId = player->m_Skater->GetID();
	}

	size = sizeof( char ) + ( vMAX_PLAYER_NAME_LEN + 1 ) + strlen( message ) + 1;

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = size;
	msg_desc.m_Id = MSG_ID_CHAT;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	m_client[0]->EnqueueMessageToServer( &msg_desc );
	
	p_params = new Script::CStruct;	
	sprintf( final_msg, "%s : %s", player->m_Name, message );
	p_params->AddString( "text", final_msg );
	Script::RunScript("create_console_message", p_params );
	delete p_params;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::SetJoinState( JoinState state )
{
	m_join_state = state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

JoinState		Manager::GetJoinState( void )
{
	return m_join_state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::SetObserverCommand( ObserverCommand command )
{
	m_observer_command = command;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ObserverCommand		Manager::GetObserverCommand( void )
{
	return m_observer_command;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::LaunchQueuedScripts( void )
{
	TriggerEvent* queued_script, *next;
	Lst::Search< TriggerEvent > sh;
	Sfx::CSfxManager * pSfxManager = Sfx::CSfxManager::Instance();
	Mdl::Skate * mod = Mdl::Skate::Instance();
	Script::CScript* pScript;
    
    Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	
	mod->SetLaunchingQueuedScripts();
	// Remember the original volume, then turn sound off so scripts don't make noise
	float orig_volume = pSfxManager->GetMainVolume();
	pSfxManager->SetMainVolume( 0.0f );

    Dbg_Printf( "********************* SPAWNING QUEUED SCRIPTS ******************\n" );
	
	Script::RunScript( "PreRunQueuedScripts" );
	for( queued_script = sh.FirstItem( m_trigger_events ); queued_script; queued_script = next )
	{
		Script::EScriptReturnVal ret_val;

		next = sh.NextItem();
	
		pScript = Script::SpawnScript( queued_script->m_Script, NULL, 0, NULL, 
									   queued_script->m_Node ); // K: The 0,NULL bit means no callback script specified
		#ifdef __NOPT_ASSERT__
		pScript->SetCommentString("Spawned from Manager::LaunchQueuedScripts");
		#endif
		pScript->mpObject = mod->GetObjectManager()->GetObjectByID( queued_script->m_ObjID );; 

		// Now run script to completion
		do 
		{
			ret_val = pScript->Update();
			// Script must not get blocked, otherwise it'll hang in this loop forever.
			Dbg_MsgAssert( ret_val!=Script::ESCRIPTRETURNVAL_BLOCKED,("\n%s\nScript got blocked when being run by RunScript.",pScript->GetScriptInfo()));
		
		} while( ret_val != Script::ESCRIPTRETURNVAL_FINISHED );
		Script::KillSpawnedScript(pScript);
		delete queued_script;
	}

	Script::RunScript( "PostRunQueuedScripts" );

	Dbg_Printf( "********************* FINISHED SPAWNING QUEUED SCRIPTS ******************\n" );
	 
	// Back to normal volume
	pSfxManager->SetMainVolume( orig_volume );
	mod->ClearLaunchingQueuedScripts();

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CCrown*		Manager::GetCrown( void )
{
	return m_crown;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::CleanupObjects( void )
{
	// Nullify references to now-dead objects
	m_crown = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::SetCrownSpawnPoint( char spawn_point )
{
	m_crown_spawn_point = spawn_point;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*			Manager::GetNetThreadStack( void )
{
	return m_net_thread_stack;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::SetNetThreadId( int net_thread_id )
{
	m_net_thread_id = net_thread_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int				Manager::GetNetThreadId( void )
{
	return m_net_thread_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::SendEndOfRunMessage( void )
{
	Net::Client* client;
	Net::MsgDesc msg_desc;

	Dbg_Assert( HaveSentEndOfRunMessage() == false );

	client = m_client[0];
	Dbg_Assert( client );

	msg_desc.m_Data = &m_game_id;
	msg_desc.m_Length = sizeof( char );
	msg_desc.m_Id = MSG_ID_RUN_HAS_ENDED;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	client->EnqueueMessageToServer( &msg_desc );

	m_last_end_of_run_game_id = m_game_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			Manager::HaveSentEndOfRunMessage( void )
{
	if( m_last_end_of_run_game_id == m_game_id )
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			Manager::GameIsOver( void )
{
	return m_game_over;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::MarkGameOver( void )
{
	m_game_over = true;
}
                                          
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::ClearGameOver( void )
{
	m_game_over = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool			Manager::HasCheatingOccurred( void )
{
	return m_cheating_occurred;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::CheatingOccured( void )
{
	m_cheating_occurred = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::ClearCheating( void )
{
	m_cheating_occurred = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::RespondToReadyQuery( void )
{
	MsgReady msg;
	Net::MsgDesc msg_desc;
	int i;

	msg.m_Time = m_latest_ready_query;

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( MsgReady );
	msg_desc.m_Id = MSG_ID_READY_RESPONSE;
	msg_desc.m_Queue = Net::QUEUE_IMPORTANT;
	for( i = 0; i < 2; i++ )
	{
		if( m_client[i] )
		{
			m_client[i]->EnqueueMessageToServer( &msg_desc );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void			Manager::ToggleProSet( char bit, uint32 param_id )
{
	MsgToggleProSet msg;
	Net::MsgDesc msg_desc;
	Net::Server* server;
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;

	m_proset_flags.Toggle( bit );
	
	msg.m_Bit = bit;
	msg.m_ParamId = param_id;

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( MsgToggleProSet );
	msg_desc.m_Id = MSG_ID_TOGGLE_PROSET;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;

	server = GetServer();
	for( player = FirstPlayerInfo( sh, true ); player; player = NextPlayerInfo( sh, true ))
	{
		if( player->IsLocalPlayer())
		{
			continue;
		}
		server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		Manager::ScriptStartCTFGame(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();

	mlp_man->AddLogicTask( *gamenet_man->m_ctf_logic_task );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		Manager::ScriptEndCTFGame(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();

	gamenet_man->m_ctf_logic_task->Remove();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptTookFlag(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player, *capture_player;
	Net::Server* server;
	Lst::Search< PlayerInfo > sh;
	MsgFlagMsg msg;
	Net::MsgDesc msg_desc;
	int obj_id, team;

	pParams->GetInteger( "player", &obj_id );
	pParams->GetInteger( "flag_team", &team );
	
	capture_player = gamenet_man->GetPlayerByObjectID( obj_id );
	if( capture_player == NULL )
	{
		return true;
	}

	msg.m_ObjId = (char) obj_id;
	msg.m_Team = (char) team;
		
	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( MsgFlagMsg );
	msg_desc.m_Id = MSG_ID_TOOK_FLAG;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;

	server = gamenet_man->GetServer();
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		if( player->IsLocalPlayer())
		{
			capture_player->TookFlag( team );
			continue;
		}
		server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptCapturedFlag(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player, *capture_player;
	Net::Server* server;
	Lst::Search< PlayerInfo > sh;
	MsgFlagMsg msg;
	Net::MsgDesc msg_desc;
	int obj_id, team;

	pParams->GetInteger( "player", &obj_id );
	capture_player = gamenet_man->GetPlayerByObjectID( obj_id );
	if(( capture_player == NULL ) || ( capture_player->m_flags.TestMask( PlayerInfo::mRESTARTING )))
	{
		return true;
	}

	team = capture_player->HasWhichFlag();
	msg.m_ObjId = (char) obj_id;
	msg.m_Team = (char) team;
		
	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( MsgFlagMsg );
	msg_desc.m_Id = MSG_ID_CAPTURED_FLAG;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;

	server = gamenet_man->GetServer();
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		if( player->IsLocalPlayer())
		{
			Mdl::Score* score_obj;
			
			Obj::CSkaterScoreComponent* p_skater_score_component = GetSkaterScoreComponentFromObject(capture_player->m_Skater);
			Dbg_Assert(p_skater_score_component);
			score_obj = p_skater_score_component->GetScore();
			
			Dbg_Assert(score_obj);
			score_obj->SetTotalScore( score_obj->GetTotalScore() + 1 );

			capture_player->CapturedFlag( team );
			continue;
		}
		server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptRetrievedFlag(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player, *retrieve_player;
	Net::Server* server;
	Lst::Search< PlayerInfo > sh;
	MsgFlagMsg msg;
	Net::MsgDesc msg_desc;
	int obj_id, team;

	pParams->GetInteger( "player", &obj_id );
	pParams->GetInteger( "flag_team", &team );
	
	retrieve_player = gamenet_man->GetPlayerByObjectID( obj_id );
	if( retrieve_player == NULL )
	{
		return true;
	}

	msg.m_ObjId = (char) obj_id;
	msg.m_Team = (char) team;
		
	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( MsgFlagMsg );
	msg_desc.m_Id = MSG_ID_CAPTURED_FLAG;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;

	server = gamenet_man->GetServer();
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		if( player->IsLocalPlayer())
		{
			retrieve_player->CapturedFlag( team );
			continue;
		}
		server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptHasFlag(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player;
	int obj_id;

	pParams->GetInteger( "player", &obj_id );
	
	player = gamenet_man->GetPlayerByObjectID( obj_id );
	if( player == NULL )
	{
		return true;
	}

	return player->HasCTFFlag();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptDisplayFlagBaseWarning(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	static Tmr::Time s_last_show = 0;

	if(( Tmr::GetTime() - s_last_show ) > Tmr::Seconds( 3 ))
	{
		Script::RunScript( CRCD(0x55af7c02,"display_flag_base_warning"), NULL );
		s_last_show = Tmr::GetTime();
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptTeamFlagTaken(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	int team;

	pParams->GetInteger( "team", &team );
	
	for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
	{
		if( player->HasWhichFlag() == team )
		{
			return true;
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SpawnCrown | spawn the king of the hill crown
bool	Manager::ScriptSpawnCrown(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	// Spawn the king of the hill crown
	gamenet_man->m_crown = new Obj::CCrown;

	int i, spawn_pt;
		
	Script::CArray *pNodeArray=Script::GetArray("NodeArray");

	// if there is no node array, then there are no restart points
	if (!pNodeArray)
	{
		return false;
	}
						 
	i = 0;
	spawn_pt = gamenet_man->m_crown_spawn_point;
		
	// collect a table of all crown nodes
	int max_crown_node = 0;
	Script::CStruct *pCrownNodeTab[vNUM_CROWN_SPAWN_POINTS];
	int crown_nodes[vNUM_CROWN_SPAWN_POINTS];
	while( i < (int)pNodeArray->GetSize() )
	{
		uint32	TypeChecksum;
		Script::CStruct *pNode=pNodeArray->GetStructure(i);
		
		TypeChecksum = 0;
		pNode->GetChecksum("Type",&TypeChecksum);
		if( TypeChecksum == 0xaf86421b )	// checksum of "Crown"
		{
			Dbg_MsgAssert( max_crown_node < vNUM_CROWN_SPAWN_POINTS, ( "Too many crown spawn points" ));
			crown_nodes[max_crown_node] = i;
			pCrownNodeTab[max_crown_node++] = pNode;
		}
		i++;
	}
	Dbg_MsgAssert(max_crown_node, ("no crown nodes found"));
		
	// there may be fewer crown points than the one we want, so
	// truncate the request number if so
	spawn_pt = spawn_pt % max_crown_node;

	// now set up crown point in world
	Mth::Vector pos;
	SkateScript::GetPosition( pCrownNodeTab[spawn_pt], &pos );
    
	gamenet_man->m_crown->InitCrown( skate_mod->GetObjectManager(), pCrownNodeTab[spawn_pt] );

    PlayerInfo* king;
	//gamenet_man->m_crown->AddToWorld( viewer_mod->GetCurrentWorld(), &pos );
	
	// We may have a king "queued" up (i.e. we joined a KOTH game in progress.
	// If so, coronate the king straight away
	king = gamenet_man->GetKingOfTheHill();
	if( king )
	{
		gamenet_man->m_crown->PlaceOnKing( king->m_Skater );
	}
    
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | NetworkGamePending | 
bool Manager::ScriptNetworkGamePending(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
    
	return gamenet_man->m_game_pending;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StartNetworkGame | 
bool Manager::ScriptStartNetworkGame(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Manager * gamenet_man = Manager::Instance();
    
	if( gamenet_man->m_start_network_game_task->InList())
	{
		return true;
	}
	if( gamenet_man->m_change_level_task->InList())
	{
		return true;
	}

	gamenet_man->m_game_pending = true;
	if( gamenet_man->InNetGame())
	{ 
		if( gamenet_man->GetGameTypeFromPreferences() == Script::GenerateCRC( "netgoalattack" ))
		{
			s_time_to_start_game = Tmr::GetTime() + vTIME_BEFORE_STARTING_GOALATTACK;
			
		}
		else
		{
			s_time_to_start_game = Tmr::GetTime() + vTIME_BEFORE_STARTING_NETGAME;
		}
	}
	else
	{
		s_time_to_start_game = Tmr::GetTime();
	}
			
	mlp_man->AddLogicTask( *gamenet_man->m_start_network_game_task );
	
	if( gamenet_man->InNetGame())
	{
		if( gamenet_man->GetHostMode() == vHOST_MODE_AUTO_SERVE )
		{
			gamenet_man->CreateNetPanelMessage( true, Script::GenerateCRC("net_message_auto_starting_game"),
											 gamenet_man->GetGameModeName(), NULL, NULL, "netstatusprops", true,
												Tmr::Seconds( 3 ));    
		}
		else
		{
			gamenet_man->CreateNetPanelMessage( true, Script::GenerateCRC("net_message_starting_game"),
										 gamenet_man->GetGameModeName(), NULL, NULL, "netstatusprops", true,
											Tmr::Seconds( 3 ));
		}

		if( skate_mod->GetGameMode()->IsTeamGame())
		{
			Net::Server* server;
			PlayerInfo* player;
			Lst::Search< PlayerInfo > sh;
			Net::MsgDesc msg_desc;
		 
			server = gamenet_man->GetServer();
			Dbg_Assert( server );

			msg_desc.m_Id = MSG_ID_KILL_TEAM_FLAGS;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
			for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
			{
				server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
			}
		}
	}
	
	// Compile the list of selected goals to send to all clients at the start of goal attack games
	if( gamenet_man->InNetGame() && ( gamenet_man->GetGameTypeFromPreferences() == Script::GenerateCRC( "netgoalattack" )))
	{
		int i, num_goals;
		Game::CGoalManager* pGoalManager;
		Game::CGoal* pGoal;
		Net::MsgMax msg;
		Net::MsgDesc msg_desc;
		uint32 goal_id;
		char* data;
		Net::Server* server;
		PlayerInfo* player;
		Lst::Search< PlayerInfo > sh;
		 
		pGoalManager = Game::GetGoalManager();
		server = gamenet_man->GetServer();
		Dbg_Assert( server );

		data = msg.m_Data;
		*data++ = 1;	// This indicates that they should bring up the goal summary dialog
		num_goals = pGoalManager->GetNumGoals();
		Dbg_Printf( "*** Num Goals: %d\n" );
		for( i = 0; i < num_goals; i++ )
		{
			pGoal = pGoalManager->GetGoalByIndex( i );
			Dbg_Assert( pGoal );
			if( pGoal->IsSelected())
			{
				pGoal->UnBeatGoal();
				goal_id = pGoal->GetGoalId();
				Dbg_Printf( "*** Goals %d selected, id 0x%x\n", i, goal_id );
				memcpy( data, &goal_id, sizeof( uint32 ));
				data += sizeof( uint32 );
			}
		}

		// zero-terminate the list of goals
		goal_id = 0;
		memcpy( data, &goal_id, sizeof( uint32 ));
		data += sizeof( uint32 );

		msg_desc.m_Data = &msg;
		msg_desc.m_Length = (int) ( data - msg.m_Data );
		msg_desc.m_Id = MSG_ID_SELECT_GOALS;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
		for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
		{
			// No need to send to server
			if( player->IsLocalPlayer())
			{
				continue;
			}
			server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
		}
		
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptSendGameOverToObservers(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	Manager * gamenet_man = Manager::Instance();

	Dbg_Printf( "**** In ScriptSendGameOverToObservers\n" );
	if( gamenet_man->OnServer())
	{
		Net::Server* server;
		Net::MsgDesc msg_desc;

		server = gamenet_man->GetServer();

		msg_desc.m_Id = MSG_ID_GAME_OVER;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
		for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
		{
			if( player->IsObserving())
			{
				Dbg_Printf( "**** Sending message to observer\n" );
                server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
			}
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		Manager::ScriptEndNetworkGame(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	Manager * gamenet_man = Manager::Instance();

	if( gamenet_man->OnServer())
	{
		Net::Server* server;
		Net::MsgDesc msg_desc;

		server = gamenet_man->GetServer();

		Script::RunScript( "do_backend_retry" );
	
		msg_desc.m_Id = MSG_ID_END_GAME;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
		for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
		{
			if( player->IsLocalPlayer())
			{
				continue;
			}
	
			server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
		}
	}
	else
	{
		Net::Client* client;
		Net::MsgDesc msg_desc;

		client = gamenet_man->GetClient( 0 );

		msg_desc.m_Id = MSG_ID_FCFS_END_GAME;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
		client->EnqueueMessageToServer( &msg_desc );
	}

	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetJoinMode | 
// @uparm 1 | mode (0 for play, anything else for observe)
bool Manager::ScriptSetJoinMode(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	int mode = 0;
    
	mode = 0;
	pParams->GetInteger( NONAME, &mode );
	if( mode == 0 )
	{
		gamenet_man->SetJoinMode( vJOIN_MODE_PLAY );
	}
	else
	{
		gamenet_man->SetJoinMode( vJOIN_MODE_OBSERVE );
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetHostMode | 
// @uparm 1 | mode (0 for server, 1 for auto_serve, 2 for fcfs)
bool Manager::ScriptSetHostMode(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	int mode = 0;
    
	mode = 0;
	pParams->GetInteger( NONAME, &mode );

	gamenet_man->SetHostMode( (HostMode) mode );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptGetNumTeams(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Script::CStruct* pass_back_params;
	int num_teams;
	
	num_teams = skate_mod->GetGameMode()->NumTeams();
	pass_back_params = pScript->GetParams();
	pass_back_params->AddInteger( "num_teams", num_teams );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptGetNumPlayersOnTeam(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Script::CStruct* pass_back_params;
	int team_num = 0;
	int num_players = 0;
	
	pParams->GetInteger( "team", &team_num, Script::ASSERT );
	num_players = gamenet_man->NumTeamMembers( team_num );
	pass_back_params = pScript->GetParams();
	pass_back_params->AddInteger( "num_members", num_players );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptGetMyTeam(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Script::CStruct* pass_back_params;
	PlayerInfo *player;
	
	player = gamenet_man->GetLocalPlayer();
	if( player )
	{
		pass_back_params = pScript->GetParams();
		pass_back_params->AddInteger( "team", player->m_Team );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptSetNumTeams(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	PlayerInfo* team_player, *player;
	Lst::Search< PlayerInfo > team_sh, sh;
	MsgChangeTeam msg;
	MsgByteInfo num_teams_msg;
	Net::MsgDesc team_msg_desc;
	int num_teams = 0;
	Net::Server* server;
    
	pParams->GetInteger( NONAME, &num_teams );

	if( !gamenet_man->OnServer())
	{
		Net::Client* client;
		MsgByteInfo team_msg;
		Net::MsgDesc msg_desc;

		client = gamenet_man->GetClient( 0 );

		team_msg.m_Data = num_teams;
		Dbg_Printf( "Telling server to create %d teams\n", num_teams );

		msg_desc.m_Data = &team_msg;
		msg_desc.m_Length = sizeof( MsgByteInfo );
		msg_desc.m_Id = MSG_ID_FCFS_SET_NUM_TEAMS;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
		client->EnqueueMessageToServer( &msg_desc );
		return true;
	}

	if( ( num_teams > 0 ) &&
		( !skate_mod->GetGameMode()->IsTeamGame()))
	{
		skate_mod->GetGameMode()->SetNumTeams( num_teams );
		if( skate_mod->GetLocalSkater())
		{
			Script::RunScript( "PrepareSkaterForMove", NULL, skate_mod->GetLocalSkater());
			skate_mod->move_to_restart_point( skate_mod->GetLocalSkater());
		}
	}
	else
	{
		skate_mod->GetGameMode()->SetNumTeams( num_teams );
	}
	
	server = gamenet_man->GetServer();
	if( server == NULL )
	{
		return true;
	}

	num_teams_msg.m_Data = num_teams;

	team_msg_desc.m_Data = &num_teams_msg;
	team_msg_desc.m_Length = sizeof( MsgByteInfo );
	team_msg_desc.m_Id = MSG_ID_SET_NUM_TEAMS;
	team_msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	team_msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		if( player->IsLocalPlayer())
		{
			continue;
		}
		server->EnqueueMessage( player->GetConnHandle(), &team_msg_desc );
	}

	team_msg_desc.m_Data = &msg;
	team_msg_desc.m_Length = sizeof( MsgChangeTeam );
	team_msg_desc.m_Id = MSG_ID_TEAM_CHANGE;
	team_msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	team_msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	for( team_player = gamenet_man->FirstPlayerInfo( team_sh ); team_player; team_player = gamenet_man->NextPlayerInfo( team_sh ))
	{
		if( team_player->m_Team >= num_teams )
		{
			msg.m_Team = (unsigned char) vTEAM_RED;
			msg.m_ObjID = (unsigned char) team_player->m_Skater->GetID();

			for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
			{
				if( player->IsLocalPlayer())
				{
					continue;
				}
				server->EnqueueMessage( player->GetConnHandle(), &team_msg_desc );
			}
			team_player->m_Team = vTEAM_RED;
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptJoinTeam(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player;
	MsgChangeTeam msg;
	Net::MsgDesc msg_desc;
	Net::Client* client;
	uint32 team_checksum;
	int team;
	static Tmr::Time s_last_join_time = 0;

	player = gamenet_man->GetLocalPlayer();
	if( ( player == NULL ) || 
		( player->m_Skater == NULL ))
	{
		return false;
	}

	pParams->GetChecksum( NONAME, &team_checksum );
	if( team_checksum == Script::GenerateCRC( "blue" ))
	{
		team = vTEAM_BLUE;
	}
	else if( team_checksum == Script::GenerateCRC( "red" ))
	{
		team = vTEAM_RED;
	}
	else if( team_checksum == Script::GenerateCRC( "green" ))
	{
		team = vTEAM_GREEN;
	}
	else 
	{
		team = vTEAM_YELLOW;
	}
    
	msg.m_Team = (unsigned char) team;
	msg.m_ObjID = (unsigned char) player->m_Skater->GetID();
	client = gamenet_man->GetClient( 0 );
	if(( Tmr::GetTime() - s_last_join_time ) < 150 )
	{
		return true;
	}
	s_last_join_time = Tmr::GetTime();

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( MsgChangeTeam );
	msg_desc.m_Id = MSG_ID_REQUEST_CHANGE_TEAM;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	client->EnqueueMessageToServer( &msg_desc );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptConnectedToPeer(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_NGPS__
	Manager * gamenet_man = Manager::Instance();
	PEER peer;

	peer = gamenet_man->mpLobbyMan->GetPeer();

	return ( peer && peerIsConnected( peer ));
#else
	return false;
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptInGroupRoom(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifndef __PLAT_NGPS__
	return false;
#else
	Manager * gamenet_man = Manager::Instance();
	return gamenet_man->mpLobbyMan->InGroupRoom();
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptIsHost(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player;

	if( gamenet_man->InNetGame() == false )
	{
		return false;
	}

	if( gamenet_man->OnServer())
	{
		return( gamenet_man->GetHostMode() == vHOST_MODE_SERVE );
	}

	player = gamenet_man->GetLocalPlayer();
	if( player && player->IsServerPlayer())
	{
		return true;
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptIsAutoServing(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
		
	if( gamenet_man->InNetGame() == false )
	{
		return false;
	}

	if( gamenet_man->OnServer() == false )
	{
		return false;
		
	}

	return( gamenet_man->GetHostMode() == vHOST_MODE_AUTO_SERVE );
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptChangeLevelPending(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	
	return( gamenet_man->m_change_level_task->InList());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptDownloadMotd(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();

	gamenet_man->SetNextServerListState( vSERVER_LIST_STATE_STARTING_MOTD );
	gamenet_man->SetServerListState( vSERVER_LIST_STATE_INITIALIZE );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptAlreadyGotMotd(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_NGPS__
	Manager * gamenet_man = Manager::Instance();

	return gamenet_man->m_got_motd;
#else
	return true;
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptAlreadySignedIn(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_XBOX__
	Manager * gamenet_man = Manager::Instance();

	return gamenet_man->mpAuthMan->SignedIn();
#else
	return false;
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptSignOut(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_XBOX__
	Manager * gamenet_man = Manager::Instance();

	gamenet_man->mpAuthMan->SignOut();
#endif
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptAllPlayersAreReady(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* player;

	if( gamenet_man->OnServer())
	{
		for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
		{
			if(( player->m_Conn->GetStatus() & Net::Conn::mSTATUS_READY ) == 0 )
			{
				return false;
			}
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptJoinServerComplete(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();

	return gamenet_man->ReadyToPlay();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptJustStartedNetGame(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	if(( Tmr::GetTime() - s_time_to_start_game ) < Tmr::Seconds( 5 ))
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptEnteredNetworkGame(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
    
	mlp_manager->AddLogicTask( *gamenet_man->m_enter_chat_task );
	mlp_manager->AddLogicTask( *gamenet_man->m_render_scores_task );
	gamenet_man->m_scores_on = true;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptChooseAccount(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_XBOX__
	Manager * gamenet_man = Manager::Instance();
	int index;

	index = 0;
	pParams->GetInteger( Script::GenerateCRC("index"), &index );
	gamenet_man->mpAuthMan->SelectAccount( index );
#endif	// __PLAT_XBOX__
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptCreatePlayerOptions(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Script::CScriptStructure* pStructure;
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	int index;
	const char* name;

	index = 0;
	name = NULL;
	pParams->GetInteger( Script::GenerateCRC("index"), &index );
	pParams->GetString( Script::GenerateCRC("name"), &name );
	
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		if( index == 0 )
		{
			if( stricmp( player->m_Name, name ) == 0 )
			{
				player->m_flags.SetMask( PlayerInfo::mMARKED_PLAYER );

				pStructure = new Script::CScriptStructure;

#ifdef __PLAT_NGPS__	
				if( gamenet_man->InInternetMode())
				{
					if( ( player->m_Profile ) && 
						( gamenet_man->mpBuddyMan->IsLoggedIn()))
					{         
						pStructure->AddInteger( "profile", player->m_Profile );	
						pStructure->AddString( "nick", player->m_Name );    
				
						if( gamenet_man->mpBuddyMan->IsAlreadyMyBuddy( player->m_Profile ))
						{
							pStructure->AddChecksum( NONAME, Script::GenerateCRC( "allow_remove_homie" ));
						}
						else
						{
							pStructure->AddChecksum( NONAME, Script::GenerateCRC( "allow_add_homie" ));
						}
					}
				}
#endif
				pStructure->AddComponent( Script::GenerateCRC( "name" ), ESYMBOLTYPE_STRING, name );
	
				Script::RunScript( "create_player_options_dialog", pStructure );
	
				delete pStructure;
			}
			break;
		}
		
		index--;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#ifdef __PLAT_NGPS__
bool Manager::ScriptFillPlayerListMenu( Script::CStruct* pParams, Script::CScript* pScript )
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	Script::CStruct* p_item_params, *p_unfocus_params;
	Script::CArray* p_colors;
	int i;
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());

	i = 0;
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		if( player->IsLocalPlayer())
		{
			i++;
			continue;
		}
		
		p_colors = new Script::CArray;
		p_colors->SetSizeAndType( 4, ESYMBOLTYPE_INTEGER );

		if( ( player->m_Profile ) && 
			( gamenet_man->mpBuddyMan->IsLoggedIn()))
		{         
			if( gamenet_man->mpBuddyMan->IsAlreadyMyBuddy( player->m_Profile ))
			{
				p_colors->SetInteger( 0, 128 );
				p_colors->SetInteger( 1, 128 );
				p_colors->SetInteger( 2, 0 );
				p_colors->SetInteger( 3, 128 );
			}
			else
			{
				p_colors->SetInteger( 0, 0 );
				p_colors->SetInteger( 1, 128 );
				p_colors->SetInteger( 2, 0 );
				p_colors->SetInteger( 3, 128 );
			}
		}
		else
		{
			p_colors->SetInteger( 0, 128 );
			p_colors->SetInteger( 1, 128 );
			p_colors->SetInteger( 2, 128 );
			p_colors->SetInteger( 3, 128 );
		}
		
		p_item_params = new Script::CStruct;	
		p_item_params->AddString( "text", player->m_Name );
		p_item_params->AddChecksum( "centered", CRCD(0x2a434d05,"centered") );
		p_item_params->AddChecksum( "id", 123456 + i );
		p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("choose_selected_player"));
		
		p_unfocus_params = new Script::CStruct;
		p_unfocus_params->AddArray( "rgba", p_colors );
		p_item_params->AddArray( "rgba", p_colors );
		p_item_params->AddStructure( "unfocus_params", p_unfocus_params );
	
		// create the parameters that are passed to the X script
		Script::CStruct *p_script_params= new Script::CStruct;
		p_script_params->AddInteger( "index", i );	
		p_script_params->AddString( "name", player->m_Name );	
		p_item_params->AddStructure("pad_choose_params",p_script_params);			
	
		Script::RunScript("theme_menu_add_item",p_item_params);
		
		delete p_item_params;
		delete p_script_params;
		delete p_unfocus_params;
		delete p_colors;
		i++;
	}

	Mem::Manager::sHandle().PopContext();
	
	return true;
}
#endif
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptRemovePlayer(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Script::CScriptStructure* pStructure;
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	int index;
	const char* name;

	index = 0;
	name = NULL;
	pParams->GetInteger( Script::GenerateCRC("index"), &index );
	pParams->GetString( Script::GenerateCRC("name"), &name );
	
	for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
	{
		if( index == 0 )
		{
			if( stricmp( player->m_Name, name ) == 0 )
			{
				player->m_flags.SetMask( PlayerInfo::mMARKED_PLAYER );

				pStructure = new Script::CScriptStructure;
				pStructure->AddComponent( Script::GenerateCRC( "name" ), ESYMBOLTYPE_STRING, name );
	
				Script::RunScript( "create_kick_ban_menu", pStructure );
	
				delete pStructure;
			}
			break;
		}
		
		index--;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptCancelRemovePlayer(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;

	for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
	{
		player->m_flags.ClearMask( PlayerInfo::mMARKED_PLAYER );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptKickPlayer(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	int i;

	i = 0;
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		if( player->m_flags.TestMask( PlayerInfo::mMARKED_PLAYER ))
		{
			if( gamenet_man->OnServer())
			{
				Obj::CSkater* quitting_skater;
				bool observing;
	
				quitting_skater = player->m_Skater;
				observing = player->IsObserving();
				gamenet_man->DropPlayer( player, vREASON_KICKED );
				if( !observing )
				{
					Mdl::Skate * skate_mod = Mdl::Skate::Instance();
					skate_mod->remove_skater( quitting_skater );
				}
			}
			else
			{
				Net::Client* client;
				MsgRemovePlayerRequest msg;
				Net::MsgDesc msg_desc;

				client = gamenet_man->GetClient( 0 );
				Dbg_Assert( client );

				msg.m_Ban = 0;
				msg.m_Index = i;
				strcpy( msg.m_Name, player->m_Name );

				msg_desc.m_Data = &msg;
				msg_desc.m_Length = sizeof( MsgRemovePlayerRequest );
				msg_desc.m_Id = MSG_ID_FCFS_BAN_PLAYER;
				msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
				msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
				client->EnqueueMessageToServer( &msg_desc );
												
			}
			break;
		}
		i++;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptBanPlayer(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	int i;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());
	i = 0;
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		if( player->m_flags.TestMask( PlayerInfo::mMARKED_PLAYER ))
		{
			if( gamenet_man->OnServer())
			{
				Obj::CSkater* quitting_skater;
				bool observing;
	
				quitting_skater = player->m_Skater;
				observing = player->IsObserving();
				gamenet_man->DropPlayer( player, vREASON_BANNED );
				if( !observing )
				{
					Mdl::Skate * skate_mod = Mdl::Skate::Instance();
					skate_mod->remove_skater( quitting_skater );
				}
			}
			else
			{
				Net::Client* client;
				MsgRemovePlayerRequest msg;
				Net::MsgDesc msg_desc;

				client = gamenet_man->GetClient( 0 );
				Dbg_Assert( client );

				msg.m_Ban = 1;
				msg.m_Index = i;
				strcpy( msg.m_Name, player->m_Name );

				msg_desc.m_Data = &msg;
				msg_desc.m_Length = sizeof( MsgRemovePlayerRequest );
				msg_desc.m_Id = MSG_ID_FCFS_BAN_PLAYER;
				msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
				msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
				client->EnqueueMessageToServer( &msg_desc );
			
			}
			break;
		}
		i++;
	}

	Mem::Manager::sHandle().PopContext();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptFCFSRequestStartGame(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	MsgStartGameRequest msg;
	Net::MsgDesc msg_desc;
	Manager * gamenet_man = Manager::Instance();
	Net::Client* client;
	Prefs::Preferences* pPreferences;
	
	pPreferences = gamenet_man->GetNetworkPreferences();
	msg.m_SkillLevel = pPreferences->GetPreferenceChecksum( Script::GenerateCRC("skill_level"), Script::GenerateCRC("checksum") );
	msg.m_FireballLevel = pPreferences->GetPreferenceChecksum( Script::GenerateCRC("fireball_difficulty"), Script::GenerateCRC("checksum") );
	msg.m_GameMode = gamenet_man->GetGameTypeFromPreferences();
	msg.m_TimeLimit = pPreferences->GetPreferenceChecksum( Script::GenerateCRC("time_limit"), Script::GenerateCRC("checksum") );
	msg.m_TargetScore = 0;
	msg.m_TargetScore = pPreferences->GetPreferenceChecksum( Script::GenerateCRC("target_score"), Script::GenerateCRC("checksum") );
	Dbg_Printf( "************ SENDING TARGET SCORE OF %08x\n", msg.m_TargetScore );
	msg.m_PlayerCollision = pPreferences->GetPreferenceChecksum( Script::GenerateCRC("player_collision"), Script::GenerateCRC("checksum") );
	msg.m_FriendlyFire = pPreferences->GetPreferenceChecksum( Script::GenerateCRC("friendly_fire"), Script::GenerateCRC("checksum") );
	msg.m_StopAtZero = pPreferences->GetPreferenceChecksum( Script::GenerateCRC("stop_at_zero"), Script::GenerateCRC("checksum") );
	msg.m_CTFType = pPreferences->GetPreferenceChecksum( Script::GenerateCRC("ctf_game_type"), Script::GenerateCRC("checksum") );
	
	// FCFS functions should only happen on clients
	Dbg_Assert( !gamenet_man->OnServer());

	client = gamenet_man->GetClient( 0 );
	Dbg_Assert( client );

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( MsgStartGameRequest );
	msg_desc.m_Id = MSG_ID_FCFS_START_GAME;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	client->EnqueueMessageToServer( &msg_desc );

	gamenet_man->m_game_pending = true;
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptFCFSRequestChangeLevel(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 level;
	Manager * gamenet_man = Manager::Instance();
	MsgIntInfo msg;
	Net::MsgDesc msg_desc;
	Net::Client* client;

	// Need to limit this option. Make sure a second request doesn't go out while waiting for the first
	// to complete

	// FCFS functions should only happen on clients
	Dbg_Assert( !gamenet_man->OnServer());

	level = 0;
	pParams->GetChecksum( Script::GenerateCRC( "level" ), &level );
    msg.m_Data = (int) level;

	client = gamenet_man->GetClient( 0 );
	Dbg_Assert( client );

	if( level == CRCD(0xb664035d,"Load_Sk5Ed_gameplay"))
	{
		client->StreamMessageToServer( MSG_ID_LEVEL_DATA, Ed::CParkManager::COMPRESSED_MAP_SIZE, 
						   Ed::CParkManager::sInstance()->GetCompressedMapBuffer(), "level data", vSEQ_GROUP_PLAYER_MSGS );
						   
		client->StreamMessageToServer( MSG_ID_RAIL_DATA, Obj::GetRailEditor()->GetCompressedRailsBufferSize(), 
						   Obj::GetRailEditor()->GetCompressedRailsBuffer(), "rail data", vSEQ_GROUP_PLAYER_MSGS );
    
	}

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( MsgIntInfo );
	msg_desc.m_Id = MSG_ID_FCFS_CHANGE_LEVEL;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	client->EnqueueMessageToServer( &msg_desc );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptFoundServers(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	
	return( gamenet_man->m_servers.CountItems() > 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptCancelJoinServer(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();

	gamenet_man->CancelJoinServer();
	gamenet_man->SetJoinState( vJOIN_STATE_FINISHED );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptReattemptJoinServer(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();

	gamenet_man->ReattemptJoin( gamenet_man->GetClient( 0 ));

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptDropPendingPlayers(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();

	gamenet_man->DropPartiallyLoadedPlayers();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptToggleProSet(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	int bit;
	uint32 param_id;
	Manager * gamenet_man = Manager::Instance();

	pParams->GetInteger( "bit", &bit, Script::ASSERT );
	pParams->GetChecksum( "param_id", ¶m_id, Script::ASSERT );
    
	gamenet_man->ToggleProSet( bit, param_id );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptFCFSRequestToggleProSet(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Net::Client* client;
	MsgToggleProSet msg;
	Net::MsgDesc msg_desc;
	uint32 param_id;
	int bit;

	pParams->GetInteger( "bit", &bit, Script::ASSERT );
	pParams->GetChecksum( "param_id", ¶m_id, Script::ASSERT );

	client = gamenet_man->GetClient( 0 );
	Dbg_Assert( client );

	msg.m_Bit = bit;
	msg.m_ParamId = param_id;

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( MsgToggleProSet );
	msg_desc.m_Id = MSG_ID_FCFS_TOGGLE_PROSET;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	client->EnqueueMessageToServer( &msg_desc );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptFCFSRequestToggleGoalSelection(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    uint32 goalId;
	Manager * gamenet_man = Manager::Instance();
	Net::Client* client;
	MsgIntInfo msg;
	Net::MsgDesc msg_desc;

    pParams->GetChecksum( "name", &goalId, Script::ASSERT );
	
	client = gamenet_man->GetClient( 0 );
	Dbg_Assert( client );

	msg.m_Data = goalId;

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( MsgIntInfo );
	msg_desc.m_Id = MSG_ID_FCFS_TOGGLE_GOAL_SELECTION;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	client->EnqueueMessageToServer( &msg_desc );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptResetProSetFlags(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	int i;

	for( i = 0; i < vMAX_NUM_PROSETS; i++ )
	{
		Script::CStruct* pSetParams;
		pSetParams = new Script::CStruct;

		pSetParams->AddInteger( "flag", Script::GetInt( "FLAG_PROSET1_GEO_ON" ) + i );
		if( gamenet_man->m_proset_flags.Test( i ))
		{
			Script::RunScript( "SetFlag", pSetParams );
		}
		else
		{
			Script::RunScript( "UnSetFlag", pSetParams );
		}

		delete pSetParams;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptHasSignedDisclaimer(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Prefs::Preferences*	prefs;
	
	prefs = gamenet_man->GetNetworkPreferences();
	uint32 has_signed = prefs->GetPreferenceChecksum( Script::GenerateCRC( "signed_disclaimer" ), Script::GenerateCRC( "checksum" ));
	
	return ( has_signed == Script::GenerateCRC( "boolean_true" ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptEnterSurveyorMode(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Inp::Manager * inp_manager = Inp::Manager::Instance();
	Mlp::Manager* mlp_man = Mlp::Manager::Instance();
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* local_player;

	local_player = gamenet_man->GetLocalPlayer();
	if( local_player->IsObserving() || local_player->IsSurveying())
	{
		return false;
	}

	Dbg_Printf( "Entering surveyor mode\n" );
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	local_player->m_observer_logic_task = 
					new Tsk::Task< PlayerInfo > ( PlayerInfo::s_observer_logic_code, *local_player );
	mlp_man->AddLogicTask( *local_player->m_observer_logic_task );
	gamenet_man->m_observer_input_handler = new Inp::Handler< Manager > ( 0,  s_observer_input_logic_code, *gamenet_man, 
															 Tsk::BaseTask::Node::vHANDLER_PRIORITY_OBSERVER_INPUT_LOGIC );
	inp_manager->AddHandler( *gamenet_man->m_observer_input_handler );

	local_player->m_flags.SetMask( PlayerInfo::mSURVEYING );

	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptExitSurveyorMode(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* local_player;

	local_player = gamenet_man->GetLocalPlayer();
	if( !local_player )
	{
		return false;
	}

	Dbg_Assert( !( local_player->IsObserving()));

	if( local_player->IsSurveying() == false )
	{
		return true;
	}

	Dbg_Printf( "Exiting surveyor mode\n" );
	gamenet_man->ObservePlayer( local_player );
	delete local_player->m_observer_logic_task;
	local_player->m_observer_logic_task = NULL;
	delete gamenet_man->m_observer_input_handler;
	gamenet_man->m_observer_input_handler = NULL;

	local_player->m_flags.ClearMask( PlayerInfo::mSURVEYING );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetReadyToPlay(bool ready_to_play)
{
	if (ready_to_play == m_ready_to_play) return;
	
	m_ready_to_play = ready_to_play;
	
	// Dan: moved from the beginning of CSkater::DoServerLogic().  I'm sort of just moving a hack, instead of fixing it...
	
	// keep the skater from skating around before he it ready
	if (!Mdl::Skate::Instance()->GetGameMode()->IsTrue( "is_frontend" ))
	{
		if (m_ready_to_play)
		{
			Mdl::Skate* skate_mod = Mdl::Skate::Instance();
			uint32 NumSkaters = skate_mod->GetNumSkaters();
			for (uint32 i = 0; i < NumSkaters; ++i)
			{
			   Obj::CSkater *pSkater = skate_mod->GetSkater(i);
			   if (pSkater)
			   {
				   pSkater->UnPause();
			   }	
			}	
		}
		else
		{
			Mdl::Skate* skate_mod = Mdl::Skate::Instance();
			uint32 NumSkaters = skate_mod->GetNumSkaters();
			for (uint32 i = 0; i < NumSkaters; ++i)
			{
			   Obj::CSkater *pSkater = skate_mod->GetSkater(i);
			   if (pSkater)
			   {
				   pSkater->Pause();
			   }	
			}	
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32		Manager::GetGoalsLevel( void )
{
	return m_goals_level;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		Manager::UsingCreatedGoals( void )
{
	/*uint32 goals_pref;

	goals_pref = m_network_preferences.GetPreferenceChecksum( CRCD(0x38dbe1d0,"goals"), CRCD( 0x21902065, "checksum" ));
	if( goals_pref == CRCD(0xe9354db8,"goals_created"))
	{
		return true;
	}*/

	if( Obj::GetGoalEditor()->CountGoalsForLevel( 0 ) > 0 )
	{
		return true;
	}


	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::LoadGoals( uint32 level )
{
	m_goals_level = level;

	// Store the level as the first 4 bytes
	memcpy( m_goals_data, &level, sizeof( uint32 ));
	m_goals_data_size = Obj::GetGoalEditor()->WriteToBuffer( level, (uint8*) ( m_goals_data + sizeof( uint32 )), 
															 vMAX_GOAL_SIZE );
	m_goals_data_size += sizeof( uint32 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*		Manager::GetGoalsData( void )
{
	return m_goals_data;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int			Manager::GetGoalsDataSize( void )
{
	return m_goals_data_size;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::SetGoalsData( char* data, uint32 level, int size )
{
	Dbg_Assert( size < vMAX_GOAL_SIZE );

	memcpy( m_goals_data, data, size );
	m_goals_data_size = size;
	m_goals_level = level;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet






================================================
FILE: Code/Sk/GameNet/GameNet.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate3													**
**																			**
**	Module:			GameNet					 								**
**																			**
**	File name:		GameNet.h												**
**																			**
**	Created by:		02/01/01	-	spg										**
**																			**
**	Description:	Game-Side Network Code 									**
**																			**
*****************************************************************************/

#ifndef __GAMENET_GAMENET_H
#define __GAMENET_GAMENET_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/


#include 
#include 
#include 

#include 

#include 
#include 

#ifdef __PLAT_NGPS__
//#include 
#include 
#include 
#include 
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

	  
namespace	Obj
{
	class	CSkaterProfile;
	class	CCrown;
    class   CMovingObject;
}

namespace GameNet
{

						

enum
{
	vVERSION_NUMBER = 0x30036,	// version number. High word is the integer, lo word is the decimal fraction
};

enum
{
	vMAX_RANK			=	10,
	vMAX_GAME_CREDIT	=	500,
	vNUM_RANKED_LEVELS	=	12,
	vMAX_SCORE_CREDIT	=	100,
	vMAX_COMBO_CREDIT	=	100,
	vMAX_RATING			= (( vMAX_GAME_CREDIT ) + ( vNUM_RANKED_LEVELS * ( vMAX_SCORE_CREDIT + vMAX_COMBO_CREDIT ))),
};

enum NetworkMode
{
	vNO_NET_MODE,
	vLAN_MODE,
	vINTERNET_MODE
};

enum JoinMode
{
	vJOIN_MODE_PLAY,
	vJOIN_MODE_OBSERVE
};

enum
{
	mSERVER 	= 0x0001,
	mCLIENT 	= 0x0002,
	mINTERNET	= 0x0004,
	mLAN		= 0x0008,
	
	mNETWORK_MODE = mINTERNET | mLAN
};
						 
enum
{
	vMAX_LOCAL_CLIENTS		= 2	// two-player split screen
};

enum
{
	vSKILL_LEVEL_1,
	vSKILL_LEVEL_2,
	vSKILL_LEVEL_3,
	vSKILL_LEVEL_4,
	vSKILL_LEVEL_5,
	
	vSKILL_LEVEL_DEFAULT = vSKILL_LEVEL_3
};

enum
{
	vDEFERRED_JOIN_FRAMES	=	10
};

enum
{
	vSCORE_UPDATE_FREQUENCY	=	2000	// update the score every 2 seconds
};

enum DropReason
{
	vREASON_QUIT,
	vREASON_TIMEOUT,
	vREASON_OBSERVING,
	vREASON_BANNED,
	vREASON_KICKED,
	vREASON_BAD_CONNECTION,
	vREASON_LEFT_OUT
};

// Valid fields of object updates
enum
{
	mUPDATE_FIELD_POS_X			= 0x0001,
	mUPDATE_FIELD_POS_Y 		= 0x0002,
	mUPDATE_FIELD_POS_Z 		= 0x0004,
	mUPDATE_FIELD_ROT_X			= 0x0008,
	mUPDATE_FIELD_ROT_Y			= 0x0010,
	mUPDATE_FIELD_ROT_Z			= 0x0020,
	mUPDATE_FIELD_STATE			= 0x0040,
	mUPDATE_FIELD_FLAGS 		= 0x0080,
	mUPDATE_FIELD_RAIL_NODE		= 0x0100,
};

enum
{
	mSTATE_MASK = 0x07,
	mDOING_TRICK_MASK = 0x80,
};

enum ServerListState
{
	vSERVER_LIST_STATE_WAIT,
	vSERVER_LIST_STATE_INITIALIZE,
	vSERVER_LIST_STATE_SHUTDOWN,
	vSERVER_LIST_STATE_STARTING_MOTD,
	vSERVER_LIST_STATE_GETTING_MOTD,
	vSERVER_LIST_STATE_TRACK_USAGE,
	vSERVER_LIST_STATE_SHOW_MOTD,
	vSERVER_LIST_STATE_RETRY_MOTD,
	vSERVER_LIST_STATE_FAILED_MOTD,
	vSERVER_LIST_STATE_TRACKING_USAGE,
	vSERVER_LIST_STATE_STARTING_LOBBY_LIST,
	vSERVER_LIST_STATE_GETTING_LOBBY_LIST,
	vSERVER_LIST_STATE_GOT_LOBBY_LIST,
	vSERVER_LIST_STATE_FAILED_LOBBY_LIST,
	vSERVER_LIST_STATE_FILL_LOBBY_LIST,
};

enum JoinState
{   
	vJOIN_STATE_WAITING,
	vJOIN_STATE_CONNECTING,
	vJOIN_STATE_JOINING,
	vJOIN_STATE_JOINING_WITH_PASSWORD,
	vJOIN_STATE_TRYING_PASSWORD,
	vJOIN_STATE_GOT_PLAYERS,
	vJOIN_STATE_WAITING_FOR_START_INFO,
	vJOIN_STATE_CONNECTED,
	vJOIN_STATE_REFUSED,
	vJOIN_STATE_FINISHED
};

enum ObserverCommand
{
	vOBSERVER_COMMAND_NONE,
	vOBSERVER_COMMAND_NEXT,
};

enum HostMode
{
	vHOST_MODE_SERVE,
	vHOST_MODE_AUTO_SERVE,
	vHOST_MODE_FCFS,      
};

enum
{
	vSORT_KEY_NAME,
	vSORT_KEY_PING,
	vSORT_KEY_NUMPLAYERS,
	vSORT_KEY_MODE,
	vSORT_KEY_LEVEL,
	vSORT_KEY_SKILL,
	vNUM_SORT_KEYS
};

enum
{
	vTEAM_RED,
	vTEAM_BLUE,
	vTEAM_GREEN,
	vTEAM_YELLOW,
	vMAX_TEAMS,

	vNO_TEAM = -1
};

enum
{
	vNET_THREAD_STACK_SIZE = ( 32 * 1024 )
};

enum
{
	vNUM_CROWN_SPAWN_POINTS	= 10
};

enum
{
	vMAX_MASTER_SERVERS = 10,
	vMAX_NUM_PROSETS = 7,
	vMAX_GOAL_SIZE = 6000,
};

enum
{
	vGAMESPY_PRODUCT_ID = 10160
};

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class LobbyMan;
class ContentMan;
class BuddyMan;
class StatsMan;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  NewPlayerInfo  : public Spt::Class
{
	
	
	public:
		NewPlayerInfo();
		~NewPlayerInfo();
		
	public:
		char					Name[ vMAX_PLAYER_NAME_LEN + 1 ];
		char					ObjID;
		Net::Conn*				Conn;
		unsigned int			AllowedJoinFrame;
		Obj::CSkaterProfile*	mpSkaterProfile;
		int 					Flags;
		int						JumpInFrame;
		int						Team;
		int						Profile;
		int						Rating;
		int						Score;
		uint32					VehicleControlType;
};

class TriggerEvent : public Lst::Node< TriggerEvent >
{   
public:
		TriggerEvent( void ) 
			: Lst::Node< TriggerEvent > ( this ) {}

		int						m_Node;
		int						m_ObjID;
		uint32					m_Script;
};

class LastSentProps
{
public:

	unsigned int	m_LastSkaterUpdateTime[Mdl::Skate::vMAX_SKATERS];	// The last time of update
	char			m_LastSkaterTerrain[Mdl::Skate::vMAX_SKATERS];		// The last terrain of each skater
	char			m_LastSkaterWalking[Mdl::Skate::vMAX_SKATERS];		// The last walking of each skater
	char			m_LastSkaterDriving[Mdl::Skate::vMAX_SKATERS];		// The last driving of each skater
	short			m_LastSkaterPosUpdate[Mdl::Skate::vMAX_SKATERS][3];	// The last pos of each skater
	short			m_LastSkaterRotUpdate[Mdl::Skate::vMAX_SKATERS][3];	// The last pos of each skater
    Flags< int >	m_LastSkaterFlagsUpdate[Mdl::Skate::vMAX_SKATERS];	// The last flags 
																	   	// we sent to each skater
	Flags< int >	m_LastEndRunFlagsUpdate[Mdl::Skate::vMAX_SKATERS];	// The last end run flags 
																	   	// we sent to each skater
	char			m_LastSkaterStateUpdate[Mdl::Skate::vMAX_SKATERS];	// The last state
																		// we sent to each skater
	char			m_LastDoingTrickUpdate[Mdl::Skate::vMAX_SKATERS];	// The last "doing trick"
																		// state we sent to each skater
	char			m_LastDrivingUpdate[Mdl::Skate::vMAX_SKATERS];		// The last "driving"
																		// state we sent to each skater
	sint16			m_LastRailNodeUpdate[Mdl::Skate::vMAX_SKATERS];		// The last rail node sent to each skater
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class PlayerInfo : public Lst::Node< PlayerInfo >
{
public:
	
	friend class Manager;

	enum SkaterFlagMasks
	{
		mTENSE = nBit( Obj::TENSE ),
		// mSWITCH = nBit( Obj::SWITCH ),
		// mLEAN = nBit( Obj::LEAN ),
		// mTENSE_ON_GROUND = nBit( Obj::TENSE_ON_GROUND ),
		mFLIPPED = nBit( Obj::FLIPPED ),
		mVERT_AIR = nBit( Obj::VERT_AIR),
		mTRACKING_VERT = nBit( Obj::TRACKING_VERT ),
		mLAST_POLY_WAS_VERT = nBit( Obj::LAST_POLY_WAS_VERT ),
		mCAN_BREAK_VERT = nBit( Obj::CAN_BREAK_VERT ),
		mCAN_RERAIL = nBit( Obj::CAN_RERAIL ),
		// mSHOULD_END_RUN = nBit( Obj::SHOULD_END_RUN ),
		// mFINISHED_END_OF_RUN = nBit( Obj::FINISHED_END_OF_RUN ),
		mRAIL_SLIDING = nBit( Obj::RAIL_SLIDING ),
		mCAN_HIT_CAR = nBit( Obj::CAN_HIT_CAR ),
		mAUTOTURN = nBit( Obj::AUTOTURN ),
		mIS_BAILING = nBit( Obj::IS_BAILING ),
	};

	enum
	{
		mLOCAL_PLAYER	=	0x00000001,
		mNON_COLLIDABLE	=	0x00000002,	// For use, for example, after skater is knocked down so you don't
									// knock him down over and over
		mOBSERVER		=	0x00000004,
		mPENDING_PLAYER	=	0x00000008,	// Player is observing until the game finishes
		mJUMPING_IN		=	0x00000010,	// Transitioning from pending to playing
		mFULLY_IN		=	0x00000020,	// Player is fully in the game
		mHAS_PLAYER_INFO=	0x00000040,	// Player has fully received initial player info packet(s)
		mKING_OF_THE_HILL=	0x00000080,	// Player is king of the hill
		mRUN_ENDED		=	0x00000100,	// Player's run has ended
		mSERVER_PLAYER	=	0x00000200,	// The player acting as host
		mMARKED_PLAYER	=	0x00000400,	// The player is marked for an operation
		mHAS_RED_FLAG	=	0x00000800,	// The player has the red flag
		mHAS_BLUE_FLAG	=	0x00001000,	// The player has the blue flag
		mHAS_GREEN_FLAG	=	0x00002000,	// The player has the green flag
		mHAS_YELLOW_FLAG=	0x00004000,	// The player has the yellow flag
		mRESTARTING		=	0x00008000,	// The player is restarting
		mSURVEYING		=	0x00010000,	// The player is surveying the playing field

		mHAS_ANY_FLAG = mHAS_RED_FLAG | mHAS_BLUE_FLAG | mHAS_GREEN_FLAG | mHAS_YELLOW_FLAG,
	};
	
	PlayerInfo( int flags );
	
	void			SetSkater( Obj::CSkater* skater );
	void			CopyProfile( Obj::CSkaterProfile* pSkaterProfile );

	void			SetFaceData( uint8* face_data, int size );
	uint8*			GetFaceData( void );
	
	bool			IsLocalPlayer( void );
	bool			IsServerPlayer( void );
	bool			IsObserving( void );
	bool			IsSurveying( void );

	void			MarkAsServerPlayer( void );
	void			MarkAsNotServerPlayer( void );
	void			MarkAsReady( int time );
	void			MarkAsNotReady( int time );
	void			MarkAsRestarting( void );
	void			SetReadyQueryTime( int time );

	void			MarkAsNonCollidable( void );
	void			ClearNonCollidable( void );
	bool			IsNonCollidable( void );

	void			MarkAsFullyIn( void );
	bool			IsFullyIn( void );

	void			MarkAsKing( bool mark );
	bool			IsKing( void );

	bool			HasCTFFlag( void );
	int				HasWhichFlag( void );
	void			TookFlag( int team );
	void			StoleFlag( int team );
	void			CapturedFlag( int team );
	void			RetrievedFlag( void );
	void			ClearCTFState( void );

	bool			IsVulnerable( void );
	void			ResetProjectileVulnerability( void );
	void			SetHitTime( Tmr::Time hit_time );

	bool			IsPendingPlayer( void );

	int				GetLastObjectUpdateID( void );
	void			SetLastObjectUpdateID( int id );
	int				GetMaxObjectUpdates( void );

	int				GetConnHandle( void );
	int				GetSkaterNumber( void );

	Obj::CSkater*	m_Skater;
	Net::Conn*		m_Conn;
	int				m_Score;
	int				m_BestCombo;
	int				m_Profile;
	int				m_Rating;
	int				m_Team;
	char			m_Name[ vMAX_PLAYER_NAME_LEN + 1 ];
	uint32			m_VehicleControlType;
	Obj::CSkaterProfile*	mp_SkaterProfile;							// Appearance information
    
	LastSentProps	m_LastSentProps;

protected:
					~PlayerInfo();
private:
	static	Tsk::Task< PlayerInfo >::Code   	s_observer_logic_code;

	Flags< int >	m_flags;
	int				m_latest_ready_query;
	int				m_jump_in_frame;
	Tmr::Time		m_last_bail_time;
	Tmr::Time		m_last_hit_time;
	int				m_last_object_update_id;
	uint8*			m_face_data;

	Tsk::Task< PlayerInfo >*		m_observer_logic_task;
};

class ServerInfo : public Lst::Node< ServerInfo >
{
	public:
		ServerInfo( void );
		~ServerInfo( void );

		void	AddPlayer( char* name, int rating = 0 );
		char*	GetPlayerName( int index );
		int		GetPlayerRating( int index );
		void	ClearPlayerNames( void );

		char	m_Name[vMAX_SERVER_NAME_LEN + 1];
		int		m_Ip;
		int 	m_Port;
		int		m_Latency;
		int		m_NumPlayers;
		int		m_MaxPlayers;
		int		m_NumObservers;
		int		m_MaxObservers;
		char	m_Level[32];
		char	m_Mode[32];
		bool	m_Password;
		bool	m_GameStarted;
		char	m_SkillLevel;
		int		m_HostMode;
		bool	m_Ranked;
		bool	m_CanDirectConnect;
		bool	m_Listed;
		bool	m_InFocus;
		bool	m_HasBasicInfo;
		bool	m_HasFullInfo;
#ifdef __PLAT_NGPS__
		SBServer m_GServer;
#endif
#ifdef __PLAT_XBOX__
		XNKID		m_XboxKeyId;
		XNKEY		m_XboxKey;
		XNADDR		m_XboxAddr;
#endif // __PLAT_XBOX__

	private:
		Lst::Head< PlayerInfo > m_players;
};

class LobbyInfo : public Lst::Node< LobbyInfo >
{
	public:
		LobbyInfo( void );

		char	m_Name[64];
		int		m_GroupId;
		int		m_NumServers;
		int 	m_MaxServers;
		int		m_MinRating;
		int		m_MaxRating;
		bool	m_Full;
		bool	m_OffLimits;
		bool	m_Official;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  Manager  : public Spt::Class
{
	
public:
	void				UsePreferences( void );

//	Client and Server Access
/////////////////////////////
	Net::Server*		SpawnServer( bool local, bool secure );
	Net::Client*		SpawnClient( bool broadcast, bool local, bool secure, int index );
	Net::Client*		SpawnMatchClient( void );
	void				SpawnAutoServer( void );

	void				ServerShutdown( void );
	void				ClientShutdown( void );
	void				MatchClientShutdown( void );
	void				AutoServerShutdown( void );

	Net::Server*		GetServer( void );
	Net::Client*		GetClient( int index );
	Net::Client*		GetMatchClient( void );

	int					GetJoinIP( void );
	int					GetJoinPrivateIP( void );
	int					GetJoinPort( void );
	void				SetJoinIP( int ip );
	void				SetJoinPrivateIP( int ip );
	void				SetJoinPort( int port );

//	Player Access
/////////////////////////////
	void				LoadPendingPlayers( void );
	int					NumPartiallyLoadedPlayers( void );
	void				DropPartiallyLoadedPlayers( void );
	void				DropPlayer( PlayerInfo* info, DropReason reason );
	void				ResetPlayers( void );
	int					GetMaxPlayers( void );
	int					GetMaxObservers( void );
	void				SetMaxPlayers( int max_players );
	void				SetMaxObservers( int max_observers );
	int					GetNumPlayers( void );
	int					GetNumObservers( void );
	int					GetNumTeams( void );
	int					NumTeamMembers( int team );
	void				CreateTeamFlags( int num_teams );
	void				ClientAddNewPlayer( NewPlayerInfo* new_player );
	void				DeferredNewPlayer( NewPlayerInfo* new_player, 
										   int num_frames = vDEFERRED_JOIN_FRAMES );
	PlayerInfo*			NewPlayer( Obj::CSkater* skater, Net::Conn* conn, int flags = 0 );
	void				AddPlayerToList( PlayerInfo* player );
	void				DestroyPlayer( PlayerInfo* player );
    
	int					GetNextPlayerObjectId( void );
	Obj::CSkater*		GetSkaterByConnection( Net::Conn* conn );          
	PlayerInfo*			FirstPlayerInfo( Lst::Search< PlayerInfo > &sh, bool include_observers = false );
	PlayerInfo*			NextPlayerInfo( Lst::Search< PlayerInfo > &sh, bool include_observers = false );
	PlayerInfo*			GetPlayerByConnection( Net::Conn* conn );          
	PlayerInfo*			GetPlayerByObjectID( unsigned short obj_id );
    
	PlayerInfo*			GetLocalPlayer( void );
	PlayerInfo*			GetServerPlayer( void );
	PlayerInfo*			GetNextPlayerToObserve( void );
	PlayerInfo*			GetCurrentlyObservedPlayer( void );
	PlayerInfo*			GetKingOfTheHill( void );
	
	NewPlayerInfo*		FirstNewPlayerInfo( Lst::Search< NewPlayerInfo > &sh );
	NewPlayerInfo*		NextNewPlayerInfo( Lst::Search< NewPlayerInfo > &sh );
	NewPlayerInfo*		GetNewPlayerInfoByObjectID( unsigned short obj_id );
	void				DestroyNewPlayer( NewPlayerInfo* new_player );

	bool				SendFaceDataToServer( void );
	void				SendFaceDataToPlayer( PlayerInfo* player );

//	Modes and Options
/////////////////////////////

	char*				GetLevelName( bool get_created_park_name = true );
	char*				GetGameModeName( void );
	char*				GetPassword( void );
	int					GetSkillLevel( void );
	void				SetNetworkMode( NetworkMode mode );
    void				SetJoinMode( JoinMode mode );
	JoinMode			GetJoinMode( void );
	void				SetServerMode( bool on );
	HostMode			GetHostMode( void );
	void				SetHostMode( HostMode mode );
	bool				PlayerCollisionEnabled( void );
	bool				ShouldDisplayTeamScores( void );
	Prefs::Preferences*	GetNetworkPreferences( void );
	Prefs::Preferences*	GetTauntPreferences( void );
	uint32				GetNetworkLevelId( void );
	void				SetNetworkGameId( unsigned char id );
	unsigned char		GetNetworkGameId( void );
	const char*			GetNameFromArrayEntry( char* array_name, uint32 checksum );
	int					GetIntFromArrayEntry( char* array_name, uint32 checksum, uint32 field_checksum );
	bool				InNetMode( void );
	bool				InNetGame( void );
	bool				InLanMode( void );
	bool				InInternetMode( void );
	bool				OnServer( void );
	bool				OnClient( void );
	
	void				JoinServer( bool observe_only, unsigned long ip, unsigned short port, int index );
	void				CancelJoinServer( void );
	void				ReattemptJoin( Net::App* client );
	void				ReattemptJoinWithPassword( char* password );
	void				FindServersOnLAN( void );
	void				DisconnectFromServer( void );
	void				CleanupPlayers();
	
	void				RequestObserverMode( void );
	void				ObservePlayer( PlayerInfo* player );
	void				EnterObserverMode( void );
	
	void				ChooseNewServerPlayer( void );
	
	void				StartNetworkLobby( void );
	void				StartNetworkGame( void );
	bool				ShouldStopAtZero( void );
	
	void				ToggleProSet( char bit, uint32 param_id );
//	Script Functions
/////////////////////////////	
    
	static	bool		ScriptJoinTeam(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptSetNumTeams(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptGetNumTeams(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptGetNumPlayersOnTeam(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptGetMyTeam(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptInGroupRoom(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptConnectedToPeer(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptIsHost(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptIsAutoServing(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptChangeLevelPending(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool 		ScriptSetJoinMode(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool 		ScriptSetHostMode(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptChooseServer(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptRetrieveServerInfo(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptDescribeServer(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptFoundServers(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static 	bool		ScriptRemovePlayer(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static 	bool		ScriptAllPlayersAreReady(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptCreatePlayerOptions(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static 	bool		ScriptCancelRemovePlayer(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static 	bool		ScriptKickPlayer(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static 	bool		ScriptBanPlayer(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptStartNetworkGame(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptNetworkGamePending(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptEndNetworkGame(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptSendGameOverToObservers(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptSpawnCrown(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptStartCTFGame(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptEndCTFGame(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptTookFlag(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptCapturedFlag(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptRetrievedFlag(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptHasFlag(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptTeamFlagTaken(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptDisplayFlagBaseWarning(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptAlreadyGotMotd(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptDownloadMotd(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptAlreadySignedIn(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptSignOut(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptJoinServerComplete(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptEnteredNetworkGame(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptJustStartedNetGame(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptChooseAccount(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptCancelJoinServer(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptReattemptJoinServer(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptDropPendingPlayers(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static 	bool		ScriptToggleProSet(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static 	bool		ScriptResetProSetFlags(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptFCFSRequestStartGame(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptFCFSRequestChangeLevel(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptFCFSRequestToggleProSet(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptFCFSRequestToggleGoalSelection(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool 		ScriptHasSignedDisclaimer(Script::CScriptStructure *pParams, Script::CScript *pScript);

	static	bool		ScriptGetNumServersInLobby(Script::CScriptStructure *pParams, Script::CScript *pScript);

	static	bool		ScriptEnterSurveyorMode(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool		ScriptExitSurveyorMode(Script::CScriptStructure *pParams, Script::CScript *pScript);

	//void				RequestMatchmakerConnect( void );
	void				PostGame( void );
	void				SetLevel( uint32 level_id );
	uint32				GetLevelFromPreferences( void );
	int					GetFireballLevelFromPreferences( void );
	uint32				GetGameTypeFromPreferences( void );

	void				ToggleMetrics( void );
	void				ToggleScores( void );
	void				TogglePlayerNames( void );
	bool				ShouldDrawPlayerNames( void );

	void				CreateNetPanelMessage( 	bool broadcast, uint32 message_checksum, char* string_parm_1 = NULL,
												char* string_param_2 = NULL, Obj::CMovingObject* object = NULL, char* properties = NULL,
												bool important = false, int time = 2000 );
	bool				ReadyToPlay( void ) { return m_ready_to_play; }
	void				SetReadyToPlay( bool ready_to_play );
	void				RespondToReadyQuery( void );
		
	bool				ShouldSendScoreUpdates( void );
	void				SetLastScoreUpdateTime( int time );
	int					GetLastScoreUpdateTime( void );
	int					GetTeamScore( int team_id );
	int					GetPlayerScore( int obj_id );

	void				MarkReceivedFinalScores( bool received );
	bool				HaveReceivedFinalScores( void );

	void				SetCurrentLeader( PlayerInfo* player );
	PlayerInfo*			GetCurrentLeader( void );

	void				SetCurrentLeadingTeam( int team );
	int					GetCurrentLeadingTeam( void );

	void				AddSpawnedTriggerEvent( int node, int obj_id, uint32 script );
	void				ClearTriggerEventList( void );
	void				SetProSetFlags( char flags );
	
//	Server List Functions
/////////////////////////////	
	

	void				SetServerListState( ServerListState state );
	void				SetNextServerListState( ServerListState state );
	ServerListState		GetServerListState( void );
	ServerListState		GetNextServerListState( void );

	bool				StartServerList( void );
	void				StopServerList( void );
	void				SortPreviousKey( void );
	void				SortNextKey( void );
	void				SortServerList( void );
#ifdef __PLAT_XBOX__
	bool				ServerAlreadyInList( char* name, XNADDR* xbox_addr );
#else
	bool				ServerAlreadyInList( char* name, int ip );
#endif
	void				RefreshServerList( bool force_refresh = false );
	
	void				SetServerFocus( ServerInfo* server );
	void				ClearServerFocus( void );
	ServerInfo*			GetServerFocus( void );
	int					NumServersListed( void );
	void				AddServerToMenu( ServerInfo* server, int index );
	
	void				ClearServerList( bool refocus = true );
	void				FreeServerList( void );
	ServerInfo*			GetServerInfo( uint32 id );

	

	void				SendChatMessage( char* message );

	void				SetJoinState( JoinState state );
	JoinState			GetJoinState( void );

	void				SetObserverCommand( ObserverCommand command );
	ObserverCommand		GetObserverCommand( void );

	bool				ConnectToInternet( uint32 success_script, uint32 failure_script );
	void				DisconnectFromInternet( uint32 callback_script = 0 );
	void				RemoveModemStateTask( void );
	void				CancelConnectToInternet( void );

	void				RandomizeSkaterStartingPoints( void );
	void				SetSkaterStartingPoints( int* start_points );
	int					GetSkaterStartingPoint( int skater_id );

	void				LaunchQueuedScripts( void );

	Obj::CCrown*		GetCrown( void );
	void				SetCrownSpawnPoint( char spawn_point ); 
	void				CleanupObjects( void );

	void				SendEndOfRunMessage( void );
	bool				HaveSentEndOfRunMessage(  void );
	bool				GameIsOver( void );
	void				MarkGameOver( void );
	void				ClearGameOver( void );

	char*				GetNetThreadStack( void );
	void				SetNetThreadId( int net_thread_id );
	int					GetNetThreadId( void );

	bool				HasCheatingOccurred( void );
	void				ClearCheating( void );
	void				CheatingOccured( void );

	bool				UsingCreatedGoals( void );
	void				LoadGoals( uint32 level );
	uint32				GetGoalsLevel( void );
	char*				GetGoalsData( void );
	int					GetGoalsDataSize( void );
	void				SetGoalsData( char* data, uint32 level, int size );

	LobbyMan*			mpLobbyMan;
	ContentMan*			mpContentMan;
#ifdef __PLAT_XBOX__
	bool				m_XboxKeyRegistered;
	XNKID				m_XboxKeyId;	
	class AuthMan		*mpAuthMan;
	class BuddyMan		*mpBuddyMan;
	class VoiceMan		*mpVoiceMan;
#else
	BuddyMan* 			mpBuddyMan;
	StatsMan*			mpStatsMan;
#endif

private:
	Manager( void );
	~Manager( void );
	
	void				free_all_players( void );
	void				free_all_pending_players( void );
	bool				ok_to_join( int& reason, MsgConnectInfo* connect_msg, Net::Conn* conn );

	Net::Server*	m_server;
	Net::Client*	m_client[vMAX_LOCAL_CLIENTS];
	Net::Client* 	m_match_client;
    
	uint32			m_gamemode_id;
	uint32 			m_level_id;
	unsigned char	m_game_id;
	unsigned char	m_last_end_of_run_game_id;
	bool			m_game_over;
	bool			m_cheating_occurred;

	JoinMode		m_join_mode;
	HostMode		m_host_mode;
	
	bool			m_received_final_scores;

	bool			m_metrics_on;
	bool			m_scores_on;
	bool			m_draw_player_names;

	int				m_skater_starting_points[Mdl::Skate::vMAX_SKATERS];
	int				m_crown_spawn_point;

	Obj::CCrown* 	m_crown;

	static	Tsk::Task< Manager >::Code	s_modem_state_code;
	static	Tsk::Task< Manager >::Code 	s_timeout_connections_code;
	static	Tsk::Task< Manager >::Code 	s_render_metrics_code;
	static	Tsk::Task< Manager >::Code 	s_render_scores_code;
	static	Tsk::Task< Manager >::Code 	s_server_add_new_players_code;
	static	Tsk::Task< Manager >::Code 	s_client_add_new_players_code;
	static 	Tsk::Task< Manager >::Code	s_enter_chat_code;
	static	Tsk::Task< Manager >::Code	s_join_timeout_code;
	static 	Tsk::Task< Manager >::Code	s_server_list_state_code;
	static 	Tsk::Task< Manager >::Code	s_join_state_code;
	static	Tsk::Task< Manager >::Code	s_start_network_game_code;
	static	Tsk::Task< Manager >::Code	s_change_level_code;
    static  Tsk::Task< Manager >::Code	s_auto_refresh_code;
	static  Tsk::Task< Manager >::Code	s_auto_server_code;
	static	Tsk::Task< Manager >::Code	s_ctf_logic_code;
	static Net::MsgHandlerCode			s_handle_disconn_request;
	static Net::MsgHandlerCode			s_handle_find_server;
	static Net::MsgHandlerCode			s_handle_connection;
	static Net::MsgHandlerCode			s_handle_join_request;
	static Net::MsgHandlerCode			s_handle_join_accepted;
	static Net::MsgHandlerCode			s_handle_wait_n_seconds;
	static Net::MsgHandlerCode			s_handle_new_player;
	static Net::MsgHandlerCode			s_handle_game_info;
	static Net::MsgHandlerCode			s_handle_kill_flags;
	static Net::MsgHandlerCode			s_handle_select_goals;
	static Net::MsgHandlerCode			s_handle_change_level;
	static Net::MsgHandlerCode			s_handle_request_level;
	static Net::MsgHandlerCode			s_handle_new_level;
	static Net::MsgHandlerCode			s_handle_level_data;
	static Net::MsgHandlerCode			s_handle_rail_data;
	static Net::MsgHandlerCode			s_handle_goals_data;
	static Net::MsgHandlerCode			s_handle_client_proceed;
	static Net::MsgHandlerCode			s_handle_join_refused;
	static Net::MsgHandlerCode			s_handle_join_proceed;
	static Net::MsgHandlerCode			s_handle_observe;
	static Net::MsgHandlerCode			s_handle_server_response;
	static Net::MsgHandlerCode			s_handle_observe_proceed;
	static Net::MsgHandlerCode			s_handle_observe_refused;
	static Net::MsgHandlerCode			s_handle_ready_query;
	static Net::MsgHandlerCode			s_handle_ready_response;
	static Net::MsgHandlerCode			s_handle_player_info_ack_req;
	static Net::MsgHandlerCode			s_handle_player_info_ack;
	static Net::MsgHandlerCode			s_handle_new_king;
	static Net::MsgHandlerCode			s_handle_stole_flag;
	static Net::MsgHandlerCode			s_handle_took_flag;
	static Net::MsgHandlerCode			s_handle_captured_flag;
	static Net::MsgHandlerCode			s_handle_retrieved_flag;
	static Net::MsgHandlerCode			s_handle_player_restarted;
	static Net::MsgHandlerCode			s_handle_dropped_crown;
	static Net::MsgHandlerCode			s_handle_dropped_flag;
	static Net::MsgHandlerCode			s_handle_run_ended;
	static Net::MsgHandlerCode			s_handle_game_over;
	static Net::MsgHandlerCode			s_handle_end_game;
	static Net::MsgHandlerCode			s_handle_object_update;
	static Net::MsgHandlerCode			s_handle_panel_message;
	static Net::MsgHandlerCode			s_handle_auto_server_notification;
	static Net::MsgHandlerCode			s_handle_fcfs_assignment;
	static Net::MsgHandlerCode			s_handle_fcfs_request;
	static Net::MsgHandlerCode			s_handle_team_change_request;
	static Net::MsgHandlerCode			s_handle_team_change;
	static Net::MsgHandlerCode			s_handle_set_num_teams;
	static Net::MsgHandlerCode			s_handle_chat;
	static Net::MsgHandlerCode			s_handle_beat_goal;
	static Net::MsgHandlerCode			s_handle_beat_goal_relay;
	static Net::MsgHandlerCode			s_handle_started_goal;
	static Net::MsgHandlerCode			s_handle_started_goal_relay;
	static Net::MsgHandlerCode			s_handle_toggle_proset;
	static Net::MsgHandlerCode			s_handle_challenge;
	
	static Net::MsgHandlerCode			s_handle_challenge_response;
	static Net::MsgHandlerCode			s_handle_cheat_checksum_request;
	static Net::MsgHandlerCode			s_handle_cheat_checksum_response;
	static Net::MsgHandlerCode			s_handle_level_data_request;

	Tsk::Task< Manager >	*m_timeout_connections_task;
	Tsk::Task< Manager >	*m_render_metrics_task;
	Tsk::Task< Manager >	*m_render_scores_task;
	Tsk::Task< Manager >	*m_server_add_new_players_task;
	Tsk::Task< Manager >	*m_client_add_new_players_task;
	Tsk::Task< Manager >	*m_enter_chat_task;
	Tsk::Task< Manager >	*m_modem_state_task;
	Tsk::Task< Manager >	*m_join_timeout_task;
	Tsk::Task< Manager > 	*m_server_list_state_task;
	Tsk::Task< Manager > 	*m_join_state_task;
	Tsk::Task< Manager > 	*m_start_network_game_task;
	Tsk::Task< Manager > 	*m_change_level_task;
    Tsk::Task< Manager >	*m_auto_refresh_task;
	Tsk::Task< Manager >	*m_auto_server_task;
	Tsk::Task< Manager >	*m_ctf_logic_task;
	
	static	Inp::Handler< Manager >::Code 	s_observer_input_logic_code;      
	Inp::Handler< Manager >*	m_observer_input_handler;
	ObserverCommand			m_observer_command;


	Prefs::Preferences		m_network_preferences;
	// Ken: Taunt preferences used to be part of the network preferences, but then they were 
	// required to be saved out with the career file when saving to mem card, rather than with
	// the net settings (See TT6068) so I separated them out into a separate set of preferences.
	Prefs::Preferences		m_taunt_preferences;
    
	Lst::Head< PlayerInfo >		m_players;
	Lst::Head< NewPlayerInfo >	m_new_players;
	Lst::Head< TriggerEvent >	m_trigger_events;
	Lst::Head< ServerInfo > 	m_servers;
    Lst::Head< ServerInfo > 	m_temp_servers;
	int							m_sort_key;
	uint32						m_conn_refused_reason;
	
	//						These are currently only for clients since servers are currently
	//						the only ones sending out ready queries. Clients keep track of the latest
	//						received queries in these members
	int						m_latest_ready_query;
	bool					m_ready_to_play;
	int						m_last_score_update;
	Flags< int >			m_flags;
	Flags< char >			m_proset_flags;
	JoinState				m_join_state;
	int						m_join_ip;
	int						m_join_private_ip;
	int						m_join_port;
	int						m_last_modem_state;
	Tmr::Time				m_join_start_time;
	Tmr::Time				m_lobby_start_time;
	bool					m_game_pending;
	bool					m_waiting_for_game_to_start;

	ServerListState 		m_server_list_state;
	ServerListState 		m_next_server_list_state;
	Tmr::Time				m_server_list_wait_time;

	uint32					m_connection_success_script;
	uint32					m_connection_failure_script;

	PlayerInfo*				m_cam_player;	// the player that I'm watching with my camera
	PlayerInfo*				m_leader;
	int						m_leading_team;

	char					m_goals_data[vMAX_GOAL_SIZE];
	int						m_goals_data_size;
	uint32					m_goals_level;

#	ifndef __PLAT_XBOX__
	char					m_net_thread_stack[ vNET_THREAD_STACK_SIZE ]	__attribute__ ((aligned(16)));
#	else
#	pragma pack( 16 )
	char					m_net_thread_stack[ vNET_THREAD_STACK_SIZE ];
#	pragma pack()
#	endif // __PLAT_XBOX__
	int						m_net_thread_id;

	char					m_master_servers[vMAX_MASTER_SERVERS][16];
	
#ifdef __PLAT_NGPS__
	// Gamespy members
public:
	void					RemoveMessageOfTheDay( void );
	static bool				ScriptLoadNetConfigs(Script::CStruct *pParams, Script::CScript *pScript);
	static bool				ScriptNoNetConfigFiles(Script::CStruct *pParams, Script::CScript *pScript);
	static bool				ScriptFillNetConfigList(Script::CStruct *pParams, Script::CScript *pScript);
	static bool				ScriptChooseNetConfig(Script::CStruct *pParams, Script::CScript *pScript);
	static bool 			ScriptFillPlayerListMenu( Script::CStruct* pParams, Script::CScript* pScript );
	static bool 			ScriptPostGame( Script::CStruct* pParams, Script::CScript* pScript );
	static bool 			ScriptAuthenticateClient( Script::CStruct* pParams, Script::CScript* pScript );
	static bool 			ScriptWriteDNASBinary( Script::CStruct* pParams, Script::CScript* pScript );
	

private:
	char					m_motd[1024];	// Message of the day text
	GHTTPRequest 			m_ghttp_request;
	Tmr::Time				m_ghttp_start_time;
	bool					m_got_motd;
	//ezNetCnfCombination_t*	mp_combinations[6];
	ezNetCnfCombinationList_t m_combination_list;
	sceNetcnfifData_t		m_env_data[6];
    
	
	static void	s_create_game_callback( PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, 
											void * param );
	static void s_server_list_callback( PEER peer, PEERBool success, const char * name, SBServer server, PEERBool staging, int msg, int progress, void * param );
	static GHTTPBool MotdComplete( GHTTPRequest request, GHTTPResult result, char* buffer, int buffer_len, 
						  void* param );
	static GHTTPBool TrackUsageComplete( GHTTPRequest request, GHTTPResult result, char* buffer, int buffer_len, 
						  void* param );

	static	void			s_threaded_get_message_of_the_day( Manager* gamenet_man );
	static	void			s_threaded_track_usage( Manager* gamenet_man );
#endif

	DeclareSingletonClass( Manager );
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


class ScoreRank : public Lst::Node< ScoreRank >
{
public:
	ScoreRank( void )
		: Lst::Node< ScoreRank > ( this ) {}

	char	m_Name[32];
	bool	m_IsKing;
	bool	m_HasFlag;
	int		m_WhichFlags;
	int		m_ColorIndex;
	int		m_TotalScore;
	int		m_TeamId;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace GameNet

#endif	// __GAMENET_GAMENET_H




================================================
FILE: Code/Sk/GameNet/Lobby.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet					 								**
**																			**
**	File name:		Lobby.cpp												**
**																			**
**	Created by:		04/4/02	-	spg											**
**																			**
**	Description:	Gamespy peer lobby implementation						**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include                                        
#include 

#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace GameNet
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define vPOST_SERVER_TIMEOUT		Tmr::Seconds( 90 )
#define vPOST_SERVER_RETRY_TIME		Tmr::Seconds( 5 )
#define vGAME_VERSION				5

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

enum
{
	vGAMESPY_NO_DATA,
	vGAMESPY_QUERY_DATA,
	vGAMESPY_NAT_DATA,
};

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static Tmr::Time	s_last_refresh_time = 0;
static Tmr::Time	s_time_of_connection = 0;
static	int		s_gamespy_data_type = vGAMESPY_NO_DATA;
static	char	s_gamespy_parse_data[2048];
static	int		s_gamespy_parse_data_len = 0;
static	struct sockaddr s_gamespy_sender;
static	bool	s_got_gamespy_callback;
static	Tmr::Time	s_game_start_time;
static	Tmr::Time	s_last_post_time;
static	bool		s_notified_user_not_connected;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::add_lobby_to_menu( LobbyInfo* lobby, int index )
{
	Script::CStruct* p_item_params;
	char lobby_title[64];
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	if( lobby->m_Full )
	{
		sprintf( lobby_title, "%s - %d/%d (%s)", lobby->m_Name, lobby->m_NumServers, lobby->m_MaxServers, Script::GetString( "net_lobby_full" ));
	}
	else
	{
		sprintf( lobby_title, "%s - %d/%d", lobby->m_Name, lobby->m_NumServers, lobby->m_MaxServers );
	}

	p_item_params = new Script::CStruct;	
	p_item_params->AddString( "text", lobby_title );
	if( lobby->m_Full || lobby->m_OffLimits )
	{
		p_item_params->AddChecksum( NONAME, CRCD( 0x1d944426, "not_focusable" ) );
		p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("selected_lobby_full"));
	}
	else
	{
		p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("choose_selected_lobby"));
		p_item_params->AddChecksum( "focus_script",Script::GenerateCRC("regions_menu_set_focus"));
	}

	// create the parameters that are passed to the X script
	Script::CStruct *p_script_params= new Script::CStruct;
	p_script_params->AddInteger( "index", index );	
	
	if( !( lobby->m_Full || lobby->m_OffLimits ))
	{
		p_item_params->AddStructure("pad_choose_params",p_script_params);			
	}

	Script::RunScript("regions_menu_add_item",p_item_params);
	delete p_item_params;
	delete p_script_params;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::add_player_to_menu( LobbyPlayerInfo* player )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Script::CStruct *p_script_params;
	Script::CStruct *p_unfocus_params;
	Script::CStruct* p_item_params;
	Script::CArray* p_colors;
	int rank;
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	p_script_params = new Script::CStruct;
	p_item_params = new Script::CStruct;	
	p_colors = new Script::CArray;
	p_unfocus_params = new Script::CStruct;

	p_item_params->AddString( "text", PlayerName( player->m_Name ));
	p_colors->SetSizeAndType( 4, ESYMBOLTYPE_INTEGER );

	if( ( player->m_Profile ) && 
		( gamenet_man->mpBuddyMan->IsLoggedIn()))
	{           
		//Dbg_Printf( "Adding Player %s of %d to list\n", player->m_Name, player->m_Profile );
		
		// If it's me (or a buddy), color it differently and don't let me add myself to my own buddy list
		if( player->m_Profile == gamenet_man->mpBuddyMan->GetProfile() || gamenet_man->mpBuddyMan->IsAlreadyMyBuddy( player->m_Profile ))
		{
			if( gamenet_man->mpBuddyMan->GetProfile() == player->m_Profile )
			{
				p_colors->SetInteger( 0, 0 );
				p_colors->SetInteger( 1, 0 );
				p_colors->SetInteger( 2, 128 );
				p_colors->SetInteger( 3, 128 );

				p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("cant_add_self_to_buddy_prompt"));
			}
			else
			{
				p_colors->SetInteger( 0, 128 );
				p_colors->SetInteger( 1, 128 );
				p_colors->SetInteger( 2, 0 );
				p_colors->SetInteger( 3, 128 );

				p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("already_buddy_prompt"));
			}
		}
		else
		{
			p_colors->SetInteger( 0, 0 );
			p_colors->SetInteger( 1, 128 );
			p_colors->SetInteger( 2, 0 );
			p_colors->SetInteger( 3, 128 );

			p_script_params->AddInteger( "profile", player->m_Profile );	
			p_script_params->AddString( "nick", player->m_Name );	
			p_script_params->AddString( "net_name", PlayerName( player->m_Name ));  
			p_item_params->AddStructure( "pad_choose_params", p_script_params );

			if( gamenet_man->mpBuddyMan->NumBuddies() < vMAX_BUDDIES )
			{
				p_item_params->AddChecksum( "pad_choose_script", Script::GenerateCRC( "add_buddy_prompt" ));
			}
			else
			{
				p_item_params->AddChecksum( "pad_choose_script", Script::GenerateCRC( "cant_add_buddy_prompt_3" ));
			}
		}
	}
	else
	{
		//Dbg_Printf( "Adding Player %s, no profile\n", player->m_Name );

		p_colors->SetInteger( 0, 128 );
		p_colors->SetInteger( 1, 128 );
		p_colors->SetInteger( 2, 128 );
		p_colors->SetInteger( 3, 128 );
		
		if( gamenet_man->mpBuddyMan->IsLoggedIn())
		{
			p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("cant_add_buddy_prompt_1"));
		}
		else
		{
			p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("cant_add_buddy_prompt_2"));
		}
		
	}
	
	rank = (int) (((float) player->m_Rating / (float) vMAX_RATING ) * (float) vMAX_RANK );
	p_item_params->AddInteger( "rank", rank );
	p_item_params->AddChecksum( "id", Script::GenerateCRC( player->m_Name ));
	p_item_params->AddChecksum( "parent_menu_id", Script::GenerateCRC( "lobby_player_list_menu" ));
	p_item_params->AddFloat( "scale", 1.1f );

	p_unfocus_params->AddArray( "rgba", p_colors );
	p_item_params->AddArray( "rgba", p_colors );
	p_item_params->AddStructure( "unfocus_params", p_unfocus_params );
	//Script::RunScript("make_text_sub_menu_item", p_item_params );
	
	Script::RunScript("player_list_add_item", p_item_params );
	
	delete p_colors;
	delete p_script_params;
	delete p_item_params;
	delete p_unfocus_params;
	

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::refresh_player_list( void )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	clear_player_list();
	Script::RunScript( "destroy_lobby_user_list_children" );
	peerEnumPlayers( m_peer, GroupRoom, s_enum_players_callback, this );
	
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::clear_player_list( void )
{
	Lst::Search< LobbyPlayerInfo > sh;
	LobbyPlayerInfo* player, *next;

	for( player = sh.FirstItem( m_players ); player; player = next )
	{
		next = sh.NextItem();
		delete player;
	}

	Script::RunScript( "update_lobby_player_list" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::fill_player_list( void )
{
	Lst::Search< LobbyPlayerInfo > sh;
	LobbyPlayerInfo* player;

	Script::RunScript( "destroy_lobby_user_list_children" );

    for( player = sh.FirstItem( m_players ); player; player = sh.NextItem())
	{
		if( player->m_GotInfo )
		{
			add_player_to_menu( player );
		}
	}
	
	Script::RunScript( "update_lobby_player_list" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::add_buddy_to_menu( LobbyPlayerInfo* player )
{
	Script::CStruct* p_item_params;
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	//Dbg_Printf( "Adding %s of %d to the list\n", PlayerName( player->m_Name ), player->m_Profile );
	p_item_params = new Script::CStruct;	
	p_item_params->AddString( "text", PlayerName( player->m_Name ));
	p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("lobby_add_buddy"));
	p_item_params->AddChecksum( "parent_menu_id", Script::GenerateCRC( "lobby_buddy_list_menu" ));

	// create the parameters that are passed to the X script
	Script::CStruct *p_script_params= new Script::CStruct;
	p_script_params->AddInteger( "profile", player->m_Profile );	
	p_script_params->AddString( "nick", player->m_Name );	
	p_script_params->AddString( "net_name", PlayerName( player->m_Name ));  
	p_item_params->AddStructure( "pad_choose_params", p_script_params );

	Script::RunScript("make_text_sub_menu_item",p_item_params);

	delete p_item_params;
	delete p_script_params;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::fill_prospective_buddy_list( void )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Lst::Search< LobbyPlayerInfo > sh;
	LobbyPlayerInfo* player;

	Script::RunScript( "destroy_lobby_buddy_list_children" );

	for( player = sh.FirstItem( m_players ); player; player = sh.NextItem())
	{
		if( player->m_Profile == gamenet_man->mpBuddyMan->GetProfile())
		{
			continue;
		}

		if(	( player->m_Profile > 0 ) && 
			( gamenet_man->mpBuddyMan->IsAlreadyMyBuddy( player->m_Profile ) == false ))
		{
			add_buddy_to_menu( player );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::s_threaded_peer_connect( LobbyMan* lobby_man )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	// Register this thread with the sockets API
	sockAPIregthr();

	Prefs::Preferences* prefs;
	char lobby_name[128];
	
	prefs = gamenet_man->GetNetworkPreferences();
    const char* network_name = prefs->GetPreferenceString( Script::GenerateCRC( "network_id" ), Script::GenerateCRC( "ui_string" ));
    
	// Use our unique Gamespy profile ID as our "unique number", if one is available
	if( gamenet_man->mpBuddyMan->IsLoggedIn())
	{
		sprintf( lobby_name, "a%d_%s", gamenet_man->mpBuddyMan->GetProfile(), network_name );
	}
	else
	{
		sprintf( lobby_name, "a%d_%s", (int) Tmr::GetTime(), network_name );
	}

	lobby_man->m_connection_in_progress = true;
    peerConnect( lobby_man->m_peer, lobby_name, gamenet_man->mpBuddyMan->GetProfile(), s_nick_error_callback, s_connect_callback, lobby_man, false );

	// Deregister this thread with the sockets API
	sockAPIderegthr();
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_disconnected_callback( PEER peer, const char * reason, void * param )
{
	LobbyMan* man = (LobbyMan*) param;
	Tmr::Time elapsed_time;

	elapsed_time = Tmr::GetTime() - s_time_of_connection;
	printf( "Disconnected at %d after %d ms. Reason: %s\n", (int) Tmr::GetTime(), (int) elapsed_time, reason );

	if( man->m_connection_in_progress )
	{
		printf( "******* Connect Callback FAILED!\n" );
		Script::RunScript( "create_gamespy_connection_failure_dialog" );
		man->m_connection_in_progress = false;
	}
	else if( man->m_expecting_disconnect == false )
	{
		printf( "Wasn't expecting discon.  Attempting to stop hosting game\n" );
		Script::RunScript( "lost_connection_to_gamespy" );
	}
	else
	{
		printf( "Was expecting discon. Ignore\n" );
	}

	man->m_expecting_disconnect = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_room_message_callback( PEER peer, RoomType roomType, const char * nick, const char * message,
										MessageType messageType, void * param )
{
	Script::CStruct* p_params;
	char msg[1024];

	LobbyMan* lobby_man = (LobbyMan*) param;


	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	sprintf( msg, "%s: %s", lobby_man->PlayerName((char*) nick ), message );
	p_params = new Script::CStruct;	
	p_params->AddString( "text", msg );
	Script::RunScript("create_console_message", p_params );
	delete p_params;
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_player_message_callback( PEER peer, const char * nick, const char * message, MessageType messageType,
												void * param )
{
	//printf( "Player Message. Nick: %s Message: %s\n", nick, message );
	//LobbyMan* man = (LobbyMan*) param;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_player_proile_id_callback( PEER peer, PEERBool success,  const char * nick, int profileID, void * param )
{
	Lst::Search< LobbyPlayerInfo > sh;
	LobbyPlayerInfo* player;
	LobbyMan* lobby_man = (LobbyMan*) param;

	//Dbg_Printf( "Got a player profile id callback on %s : %d\n", nick, profileID );
	for( player = sh.FirstItem( lobby_man->m_players ); player; player = sh.NextItem())
	{
		if( stricmp( player->m_Name, nick ) == 0 )
		{
			player->m_Profile = profileID;
			break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_player_joined_callback( PEER peer, RoomType roomType, const char * nick, void * param )
{
	Script::CStruct* p_params;
	char msg[1024];
	Lst::Search< LobbyPlayerInfo > sh;
	LobbyPlayerInfo* player;
	
	LobbyMan* lobby_man = (LobbyMan*) param;

	//printf( "%s joined the room\n", nick );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	for( player = sh.FirstItem( lobby_man->m_players ); player; player = sh.NextItem())
	{
		if( stricmp( player->m_Name, nick ) == 0 )
		{
			peerGetPlayerInfoNoWait( peer, nick, NULL, &player->m_Profile );
			Mem::Manager::sHandle().PopContext();
			player->m_GotInfo = true;
			return;	// already have this player
		}
	}

	player = new LobbyPlayerInfo;
	strcpy( player->m_Name, nick );
	peerGetPlayerInfoNoWait( peer, nick, NULL, &player->m_Profile );
	player->m_GotInfo = true;

	lobby_man->m_players.AddToTail( player );

	Script::RunScript( "prepare_lobby_user_list_for_new_children" );
	lobby_man->add_player_to_menu( player );
	Script::RunScript( "update_lobby_player_list" );
		
	sprintf( msg, "%s %s", lobby_man->PlayerName((char*) nick ), Script::GetString( "lobby_status_joined" ));

	p_params = new Script::CStruct;	
	p_params->AddString( "text", msg );
    p_params->AddChecksum( "join", 0 );
	Script::RunScript("create_console_message", p_params );
	delete p_params;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_player_info_callback( PEER peer, RoomType roomType, const char * nick, unsigned int IP, int profileID,  
                                                void * param )
{
	Lst::Search< LobbyPlayerInfo > sh;
	LobbyPlayerInfo* player;
	LobbyMan* lobby_man = (LobbyMan*) param;
	
	if( nick == NULL )
	{
		return;
	}
	
	//Dbg_Printf( "Got info on %s : %d\n", nick, profileID );

	for( player = sh.FirstItem( lobby_man->m_players ); player; player = sh.NextItem())
	{
		if( stricmp( player->m_Name, nick ) == 0 )
		{
			// If we don't already have this player's profile
			if( player->m_Profile == 0 )
			{
				player->m_Profile = profileID;
			}
			player->m_GotInfo = true;
			lobby_man->add_player_to_menu( player );
            Script::RunScript( "update_lobby_player_list" );
			break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_player_left_callback( PEER peer, RoomType roomType, const char * nick, const char * reason, 
												void * param )
{
	Script::CStruct* p_params;
	char msg[1024];
	Lst::Search< LobbyPlayerInfo > sh;
	LobbyPlayerInfo* player;
	LobbyMan* lobby_man = (LobbyMan*) param;
	
	//printf( "%s left the room\n", nick );

	for( player = sh.FirstItem( lobby_man->m_players ); player; player = sh.NextItem())
	{
		if( stricmp( player->m_Name, nick ) == 0 )
		{
			p_params = new Script::CStruct;

			p_params->AddChecksum( "user_id", Script::GenerateCRC( nick ));
			Script::RunScript( "destroy_lobby_user", p_params );

			delete player;
			delete p_params;

			Script::RunScript( "update_lobby_player_list" );
			break;
		}
	}

	sprintf( msg, "%s %s", lobby_man->PlayerName((char*) nick ), Script::GetString( "lobby_status_left" ));

	p_params = new Script::CStruct;	
	p_params->AddString( "text", msg );
    p_params->AddChecksum( "left", 0 );
	Script::RunScript("create_console_message", p_params );
	delete p_params;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	LobbyMan::s_get_room_keys_callback( PEER peer, PEERBool success, RoomType roomType, 
											const char * nick, int num, char ** keys, char ** values, 
											void * param )
{
	int i;
	LobbyMan* lobby_man = (LobbyMan*) param;

	for( i = 0; i < num; i++ )
	{
		//Dbg_Printf( "Got Key: %s\n", keys[i] );
	}
	if( num > 0 )
	{
		int rank;
		Script::CStruct* p_item_params;
		Lst::Search< LobbyPlayerInfo > sh;
		LobbyPlayerInfo* player;

		if(( keys[0] != NULL )&& stricmp( keys[0], "b_rating" ) == 0 )
		{
			for( player = sh.FirstItem( lobby_man->m_players ); player; player = sh.NextItem())
			{
				if( stricmp( player->m_Name, nick ) == 0 )
				{
					Dbg_Printf( "Got rating %d for %s\n", player->m_Rating, nick );
					player->m_Rating = atoi( values[0] );
					rank = (int) (((float) player->m_Rating / (float) vMAX_RATING ) * (float) vMAX_RANK );
			
					p_item_params = new Script::CStruct;
					p_item_params->AddChecksum( "id", Script::GenerateCRC( player->m_Name ));
					p_item_params->AddInteger( "rank", rank );
					Script::RunScript( "player_list_update_rank", p_item_params );
					delete p_item_params;
				}
			}
		}
	}
	
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	LobbyMan::s_enum_players_callback( PEER peer, PEERBool success, RoomType roomType, int index, const char * nick,  // The nick of the player.
												int flags, void * param )
{
	LobbyMan* lobby_man = (LobbyMan*) param;
	Lst::Search< LobbyPlayerInfo > sh;
	LobbyPlayerInfo* player;

	if( success == false )
	{
		//printf( "Failed to enum players\n" );
		return;
	}

	//Dbg_Printf( "Enumerating players\n" );
	if( index == -1 )
	{
		// List is finished. 
		//lobby_man->fill_player_list();
        return;
	}
	
	for( player = sh.FirstItem( lobby_man->m_players ); player; player = sh.NextItem())
	{
		if( stricmp( player->m_Name, nick ) == 0 )
		{
			return;	// already have this player in our list
		}
	}
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	player = new LobbyPlayerInfo;
	strcpy( player->m_Name, nick );
	//Dbg_Printf( "Before getting player info on %s : %d\n", nick, player->m_Profile );
	if( peerGetPlayerInfoNoWait( peer, nick, NULL, &player->m_Profile ))
	{
		//Dbg_Printf( "***** Got Player Info for %s : %d\n", nick, player->m_Profile );
	}
	else
	{
		//Dbg_Printf( "***** Failed to Get Player Info for %s : %d\n", nick, player->m_Profile );
	}

	lobby_man->m_players.AddToTail( player );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_new_player_list_callback( PEER peer, RoomType roomType, void * param )
{
	LobbyMan* lobby_man = (LobbyMan*) param;
    
	//printf( "New Player List\n" );

	lobby_man->refresh_player_list();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_room_key_callback( PEER peer, RoomType roomType, const char * nick, 
									const char * key, const char * value, void * param )
{
	LobbyMan* lobby_man = (LobbyMan*) param;
	Lst::Search< LobbyPlayerInfo > sh;
	LobbyPlayerInfo* player;

	for( player = sh.FirstItem( lobby_man->m_players ); player; player = sh.NextItem())
	{
		if( stricmp( player->m_Name, nick ) == 0 )
		{
			int rank;
			Script::CStruct* p_item_params;

			player->m_Rating = atoi( value );
			rank = (int) (((float) player->m_Rating / (float) vMAX_RATING ) * (float) vMAX_RANK );

			Dbg_Printf( "Got room key for %s. Value: %d\n", nick, player->m_Rating );

			p_item_params = new Script::CStruct;
			p_item_params->AddChecksum( "id", Script::GenerateCRC( player->m_Name ));
			p_item_params->AddInteger( "rank", rank );
			Script::RunScript( "player_list_update_rank", p_item_params );
			delete p_item_params;
			break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_nat_negotiate_callback( PEER peer, int cookie, void * param )
{
    NegotiateError err;
	Manager * gamenet_man = Manager::Instance();
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	Net::Server* server;

	if( gamenet_man->mpLobbyMan->m_nat_count.InUse())
	{
		Dbg_Printf( "******* Ignoring new nat negotiation request\n" );
		return;
	}

	server = gamenet_man->GetServer();
	Dbg_Assert( server );
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	Dbg_Printf( "******* Beginning new nat negotiation\n" );
	err = NNBeginNegotiationWithSocket( server->GetSocket(), cookie, 0, s_nat_negotiate_progress,
										s_nat_negotiate_complete, (void*) cookie );
	if( err != ne_noerror )
	{
		Dbg_Printf( "******* NAT Neg Error: %d\n", err );
		NNCancel( cookie );
		NNThink();
		Mem::Manager::sHandle().PopContext();
		return;
	}
	
	if( gamenet_man->mpLobbyMan->m_nat_count.InUse() == false )
	{
		mlp_manager->AddLogicTask( *gamenet_man->mpLobbyMan->m_nat_negotiate_task );
	}

	gamenet_man->mpLobbyMan->m_nat_count.Acquire();

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void LobbyMan::s_server_key_callback( PEER peer, int key, qr2_buffer_t buffer, void * param )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	const char* server_name;
	Prefs::Preferences* pPreferences;
	Script::CScriptStructure* pStructure;
    char* password;
	
	//Dbg_Printf( "***** SERVER KEY CALLBACK\n" );
	pPreferences = gamenet_man->GetNetworkPreferences();

    switch(key)
	{
		case HOSTNAME_KEY:
			pStructure = pPreferences->GetPreference( Script::GenerateCRC("server_name") );
			pStructure->GetText( "ui_string", &server_name, true );
			//Dbg_Printf( "Server name is %s\n", server_name );
			qr2_buffer_add(buffer, server_name );
			break;
		case GAMENAME_KEY:
			qr2_buffer_add(buffer, "thps5ps2" );
			break;
		case GAMEMODE_KEY:
			qr2_buffer_add(buffer, gamenet_man->GetGameModeName());
			break;
		case HOSTPORT_KEY:
			qr2_buffer_add_int(buffer, vHOST_PORT );
			break;
		case MAPNAME_KEY:
			qr2_buffer_add(buffer, gamenet_man->GetLevelName());
			break;
		case GAMETYPE_KEY:
			qr2_buffer_add(buffer, gamenet_man->GetGameModeName());
			break;
		case TEAMPLAY_KEY:
			qr2_buffer_add_int(buffer, 0);
			break;
		case NUMPLAYERS_KEY:
			qr2_buffer_add_int(buffer, gamenet_man->GetNumPlayers());
			break;
		case MAXPLAYERS_KEY:
			qr2_buffer_add_int(buffer, gamenet_man->GetMaxPlayers());
			break;
		case PASSWORD_KEY:
			password = gamenet_man->GetPassword();
			if( password[0] == '\0' )
			{
				qr2_buffer_add_int(buffer, 0 );
			}
			else
			{
				qr2_buffer_add_int(buffer, 1 );
			}
			break;
			
		case NUMOBSERVERS_KEY:
			qr2_buffer_add_int(buffer, gamenet_man->GetNumObservers());
			break;
		case MAXOBSERVERS_KEY:
			qr2_buffer_add_int(buffer, gamenet_man->GetMaxObservers());
			break;
		case SKILLLEVEL_KEY:
			qr2_buffer_add_int(buffer, gamenet_man->GetSkillLevel());
			break;
		case STARTED_KEY:
			if( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netlobby" ))
			{
				qr2_buffer_add_int(buffer, 0 );
			}
			else
			{
				qr2_buffer_add_int(buffer, 1 );
			}
			break;
		case HOSTED_MODE_KEY:
			qr2_buffer_add_int(buffer, gamenet_man->GetHostMode());
			break;
		case RANKED_KEY:
			qr2_buffer_add_int(buffer, gamenet_man->mpBuddyMan->IsLoggedIn());
			break;
		default:
			qr2_buffer_add(buffer, "");
			break;
	}

	s_got_gamespy_callback = true;
}

void LobbyMan::s_player_key_callback( PEER peer, int key, int index, qr2_buffer_t buffer, void * param )
{
	s_got_gamespy_callback = true;

	//Dbg_Printf( "***** PLAYER KEY %d CALLBACK : for player %d\n", key, index );
	switch(key)
	{
		case RATING__KEY:
		{
			GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
			Lst::Search< PlayerInfo > sh;
			PlayerInfo* player;
			Lst::Search< NewPlayerInfo > new_sh;
			NewPlayerInfo* np;
			int i;
			
			i = 0;
			for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; 
					player = gamenet_man->NextPlayerInfo( sh, true ))
			{
				if( !player->IsObserving() || player->IsPendingPlayer())
				{
					if( i == index )
					{
						//printf( "(2) Added %s's rating: %d to list\n", player->m_Name, player->m_Rating );
						qr2_buffer_add_int(buffer, player->m_Rating );
						return;
					}
					i++;
				}
			}
		
			for( np = gamenet_man->FirstNewPlayerInfo( new_sh ); np; np = gamenet_man->NextNewPlayerInfo( new_sh ))
			{
				// Pending players count, observers don't
				if(	( np->Flags & PlayerInfo::mPENDING_PLAYER ) ||
					( np->Flags & PlayerInfo::mJUMPING_IN ) ||
					!( np->Flags & PlayerInfo::mOBSERVER ))
				{
					if( i == index )
					{
						//printf( "(2) Added %s's rating: %d to list\n", np->Name, np->Rating );
						qr2_buffer_add_int(buffer, np->Rating );
						return;
					}
					i++;
				}                 
			}
			
			//printf( "Added empty string to player list\n" );
			// If we're here, we don't have a player == index
            qr2_buffer_add(buffer, "");
			break;
		}
		case PLAYER__KEY:
		{
			GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
			Lst::Search< PlayerInfo > sh;
			PlayerInfo* player;
			Lst::Search< NewPlayerInfo > new_sh;
			NewPlayerInfo* np;
			int i;
			
			i = 0;
			for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; 
					player = gamenet_man->NextPlayerInfo( sh, true ))
			{
				if( !player->IsObserving() || player->IsPendingPlayer())
				{
					if( i == index )
					{
						//printf( "Added %s to player list\n", player->m_Name );
						qr2_buffer_add(buffer, player->m_Name);
						return;
					}
					i++;
				}
			}
		
			for( np = gamenet_man->FirstNewPlayerInfo( new_sh ); np; np = gamenet_man->NextNewPlayerInfo( new_sh ))
			{
				// Pending players count, observers don't
				if(	( np->Flags & PlayerInfo::mPENDING_PLAYER ) ||
					( np->Flags & PlayerInfo::mJUMPING_IN ) ||
					!( np->Flags & PlayerInfo::mOBSERVER ))
				{
					if( i == index )
					{
						//printf( "(2) Added %s to player list\n", player->m_Name );
						qr2_buffer_add(buffer, np->Name );
						return;
					}
					i++;
				}                 
			}
			
			//printf( "Added empty string to player list\n" );
			// If we're here, we don't have a player == index
            qr2_buffer_add(buffer, "");
			break;
		}

		case PING__KEY:
			qr2_buffer_add(buffer, "");
			break;
	}
}

void LobbyMan::s_public_address_callback( PEER peer, unsigned int ip, unsigned short port, 
											   void * param )
{
	char location[1024];
	const char *server_name;
	Script::CScriptStructure* pStructure;
	Prefs::Preferences* pPreferences;
	Manager * gamenet_man = Manager::Instance();
	BuddyMan* buddy_man;
	LobbyMan* lobby_man;

	lobby_man = (LobbyMan*) param;
	buddy_man = gamenet_man->mpBuddyMan;

	Dbg_Printf( "***** Got public address: %s : %d\n", inet_ntoa(*(struct in_addr *) &ip ), port );
	if( gamenet_man->OnServer() && lobby_man->GetPeer())
	{
		gamenet_man->SetJoinPort( port );
		gamenet_man->SetJoinIP( ip );
		gamenet_man->SetJoinPrivateIP( peerGetPrivateIP( lobby_man->GetPeer()) );
		pPreferences = gamenet_man->GetNetworkPreferences();
		pStructure = pPreferences->GetPreference( Script::GenerateCRC("server_name") );
		pStructure->GetText( "ui_string", &server_name, true );
	
		sprintf( location, "%d:%d:%d:%s (%s)", ip, peerGetPrivateIP( lobby_man->GetPeer()), port, server_name, lobby_man->GetLobbyName());
		if( buddy_man->IsLoggedIn())
		{
			buddy_man->SetStatusAndLocation( GP_PLAYING, (char*) Script::GetString( "homie_status_hosting" ), location );
		}
	}	
}

void LobbyMan::s_add_error_callback( PEER peer, qr2_error_t error, char * errorString, void * param )
{
	Dbg_Printf( "***** Got an error (%d) adding the server: %s\n", (int) error, errorString );
}

int LobbyMan::s_key_count_callback( PEER peer, qr2_key_type type, void * param )
{
	//Dbg_Printf( "*** GOT KEY COUNT CALLBACK\n" );
	switch( type )
	{
		case key_server:
		{
			//Dbg_Printf( "*** Server Key Count Request: 15\n" );
			return 16;
		}
		case key_player:
		{
			GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
			//Dbg_Printf( "*** Player Key Count Request: %d\n", gamenet_man->GetNumPlayers() );
			return gamenet_man->GetNumPlayers();
		}
		case key_team:
		default:
			return 0;
	}

	return 0;
}

void LobbyMan::s_key_list_callback( PEER peer, qr2_key_type type, qr2_keybuffer_t keyBuffer, 
									void * param )
{
	//Dbg_Printf( "***** Got a key list callback\n" );
	// register the keys we use
	switch(type)
	{
		case key_server:
			//Dbg_Printf( "***** Got a request for server keys\n" );
			
			qr2_keybuffer_add(keyBuffer, GAMEMODE_KEY);
			qr2_keybuffer_add(keyBuffer, GAMENAME_KEY);
			qr2_keybuffer_add(keyBuffer, HOSTNAME_KEY);
			qr2_keybuffer_add(keyBuffer, HOSTPORT_KEY);
			qr2_keybuffer_add(keyBuffer, MAPNAME_KEY);
			qr2_keybuffer_add(keyBuffer, GAMETYPE_KEY);
			qr2_keybuffer_add(keyBuffer, TEAMPLAY_KEY);
			qr2_keybuffer_add(keyBuffer, NUMPLAYERS_KEY);
			qr2_keybuffer_add(keyBuffer, MAXPLAYERS_KEY);
			qr2_keybuffer_add(keyBuffer, PASSWORD_KEY);
			
			qr2_keybuffer_add(keyBuffer, NUMOBSERVERS_KEY);
			qr2_keybuffer_add(keyBuffer, MAXOBSERVERS_KEY);
			qr2_keybuffer_add(keyBuffer, RANKED_KEY);
			
			qr2_keybuffer_add(keyBuffer, SKILLLEVEL_KEY);
			qr2_keybuffer_add(keyBuffer, STARTED_KEY);
			qr2_keybuffer_add(keyBuffer, HOSTED_MODE_KEY);
			break;
		case key_player:
			//Dbg_Printf( "***** Got a request for player keys\n" );
			qr2_keybuffer_add(keyBuffer, PLAYER__KEY);
			qr2_keybuffer_add(keyBuffer, RATING__KEY);
			break;
		case key_team:
			//Dbg_Printf( "***** Got a request for team keys\n" );
			// no custom team keys
			break;
		default:
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	LobbyMan::s_nick_error_callback( PEER peer, int type, const char* nick, void *param )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	if( type == PEER_IN_USE )
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		char new_nick[128];
		Prefs::Preferences* prefs;
	
		prefs = gamenet_man->GetNetworkPreferences();
		const char* network_name = prefs->GetPreferenceString( Script::GenerateCRC( "network_id" ), Script::GenerateCRC( "ui_string" ));
        
		printf( "Nick Error. Nick %s In Use\n", nick );
		sprintf( new_nick, "a%d_%s", (int) Tmr::GetTime(), network_name );
		peerRetryWithNick( peer, (const char*) new_nick );
	}
	else if( type == PEER_INVALID )
	{
		char new_nick[128];
		
		printf( "Nick Error. Nick %s is Invalid\n", nick );
        chatFixNick( new_nick, nick );
		peerRetryWithNick( peer, (const char*) new_nick );
	}

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	LobbyMan::s_connect_callback( PEER peer, PEERBool success, void* param )
{
	LobbyMan* lobby_man = (LobbyMan*) param;

	lobby_man->m_connection_in_progress = false;
	if( success )
	{
		Net::Manager * net_man = Net::Manager::Instance();

		//Mlp::Manager * mlp_man = Mlp::Manager::Instance();
		//GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		//mlp_man->AddLogicTask( *lobby_man->m_lobby_logic_task );
		
		printf( "****** Connect Callback Successful\n" );

		net_man->SetPublicIP( peerGetLocalIP( peer ));
		CFuncs::ScriptStartLobbyList( NULL, NULL );
		//gamenet_man->SetServerListState( vSERVER_LIST_STATE_STARTING_LOBBY_LIST );
		lobby_man->m_expecting_disconnect = false;
		s_time_of_connection = Tmr::GetTime();
	}
	else
	{
		printf( "******* Connect Callback FAILED!\n" );
		Script::RunScript( "create_gamespy_connection_failure_dialog" );
	}
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	LobbyMan::s_group_rooms_callback( PEER peer, PEERBool success, int groupID, SBServer server,  
									const char * name, int numWaiting, int maxWaiting, int numGames, int numPlaying,
									void* param )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	LobbyMan* lobby_man = (LobbyMan*) param;
	// Check for the terminator
	if( success )
	{
		if( groupID == 0 )
		{   
			gamenet_man->SetServerListState( vSERVER_LIST_STATE_GOT_LOBBY_LIST );
		}
		else
		{
			int max_players, official_key;
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

			LobbyInfo* lobby_info;
			lobby_info = new LobbyInfo;
			strncpy( lobby_info->m_Name, name, 63 );
			lobby_info->m_Name[63] = '\0';
			lobby_info->m_NumServers = numGames;
			lobby_info->m_GroupId = groupID;
			lobby_info->m_MaxServers = maxWaiting;
            max_players = SBServerGetIntValue( server, "maxplayers", 0 );
			if( numWaiting >= max_players )
			{
				lobby_info->m_Full = true;
			}

			lobby_info->m_MaxRating = SBServerGetIntValue( server, "maxrating", 100000 );
			lobby_info->m_MinRating = SBServerGetIntValue( server, "minrating", 0 );

			Dbg_Printf( "Got lobby: %s, max: %d min: %d, rating: %d\n", name, lobby_info->m_MaxRating, lobby_info->m_MinRating, gamenet_man->mpStatsMan->GetStats()->GetRating());

			if( gamenet_man->mpStatsMan->GetStats()->GetRating() > lobby_info->m_MaxRating )
			{
				lobby_info->m_OffLimits = true;
			}

			if( gamenet_man->mpStatsMan->GetStats()->GetRating() < lobby_info->m_MinRating )
			{
				lobby_info->m_OffLimits = true;
			}

			official_key = 0;
			official_key = SBServerGetIntValue( server, "neversoft", 0 );
			if( official_key )
			{
				lobby_info->m_Official = true;
			}
            
			//printf( "Got lobby %d: %s\n", groupID, name );
			//printf( "Max Servers of lobby %d: %d\n", lobby_info->m_GroupId, lobby_info->m_MaxServers );
			if( lobby_info->m_NumServers > lobby_info->m_MaxServers )
			{
				lobby_info->m_MaxServers = lobby_info->m_NumServers;
			}
	
			lobby_info->SetPri( -groupID );
			lobby_man->m_lobbies.AddNode( lobby_info );

			Mem::Manager::sHandle().PopContext();
		}
	}
	else
	{
		gamenet_man->SetServerListState( vSERVER_LIST_STATE_FAILED_LOBBY_LIST );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	LobbyMan::s_join_room_callback( PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, 
										void* param )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	LobbyMan* lobby_man = (LobbyMan*) param;

	
	Dbg_Assert( roomType == GroupRoom );

	if( success )
	{
		//printf( "Entered group room\n" );
		Script::RunScript( "dialog_box_exit" );
		Script::RunScript( "create_network_select_games_menu" );
		lobby_man->m_in_group_room = true;

		lobby_man->refresh_player_list();
		if( gamenet_man->mpBuddyMan->IsLoggedIn())
		{
			char rating_str[64];
			char key[64];
			const char* keys;
			const char* values;

			gamenet_man->mpBuddyMan->SetStatusAndLocation( GP_CHATTING, (char*) Script::GetString( "homie_status_chatting" ), 
																		lobby_man->GetLobbyName());
			sprintf( key, "b_rating" );
			sprintf( rating_str, "%d", gamenet_man->mpStatsMan->GetStats()->GetRating());
			keys = key;
			values = rating_str;
			peerSetRoomKeys(peer, roomType, peerGetNick( peer ), 1, &keys, &values );
			peerGetRoomKeys( peer, roomType, "*", 1, &keys, s_get_room_keys_callback, 
							 lobby_man, false );
		}
	}
	else
	{
		switch( result )
		{
			case PEERFullRoom:
				//printf( "Full Room\n" );
				break;
			case PEERInviteOnlyRoom:
				//printf( "Invite Only\n" );
				break;
			case PEERBannedFromRoom:
				//printf( "Banned from room\n" );
				break;
			case PEERBadPassword:
				//printf( "Bad Password\n" );
				break;
			case PEERAlreadyInRoom:
				//printf( "Already in Room\n" );
				break;
			case PEERNoTitleSet:
				//printf( "No Title Set\n" );
				break;
			case PEERJoinFailed:
				//printf( "Generic: Join Failed\n" );
				break;
			default:
				break;

		};
		
		Script::RunScript( "CreateJoinLobbyFailedDialog" );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	LobbyMan::s_lobby_logic_code( const Tsk::Task< LobbyMan >& task )
{
	LobbyMan& man = task.GetData();

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	peerThink( man.m_peer );
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	LobbyMan::s_nat_negotiate_think_code( const Tsk::Task< LobbyMan >& task )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
	NNThink();
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void gamespy_data_handler( char* packet_data, int len, struct sockaddr* sender )
{
	

	//Dbg_Printf( "Got gamepsy data of length %d\n", len );
	//Dbg_Printf( "Data[0] = %c\n", packet_data[0] );
	//Dbg_Printf( "Data[1] = %c\n", packet_data[1] );

	Dbg_Assert( len < 2047 );
	// backslash as the first character signifies a gamespy packet
	if( ( (unsigned char) packet_data[0] == QR_MAGIC_1 ) &&
		( (unsigned char) packet_data[1] == QR_MAGIC_2 ))
	{
		//Dbg_Printf( "************ GOT GAMESPY QUERY CALL!!!!!!!!\n" );
		memcpy( s_gamespy_parse_data, packet_data, len );
		s_gamespy_sender = *sender;
		s_gamespy_parse_data_len = len;
		s_gamespy_data_type = vGAMESPY_QUERY_DATA;
	}
	else
	{
		if( len >= NATNEG_MAGIC_LEN )
		{
			//Dbg_Printf( "Got Nat Neg data of length %d\n", len );
			if(	( (unsigned char) packet_data[0] == NN_MAGIC_0 ) &&
				( (unsigned char) packet_data[1] == NN_MAGIC_1 ) &&
				( (unsigned char) packet_data[2] == NN_MAGIC_2 ) &&
				( (unsigned char) packet_data[3] == NN_MAGIC_3 ) &&
				( (unsigned char) packet_data[4] == NN_MAGIC_4 ) &&
				( (unsigned char) packet_data[5] == NN_MAGIC_5 ))
			{
				//Dbg_Printf( "Magic number matches\n" );
                memcpy( s_gamespy_parse_data, packet_data, len );
				s_gamespy_sender = *sender;
				s_gamespy_parse_data_len = len;
				s_gamespy_data_type = vGAMESPY_NAT_DATA;
			}
		}

	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	LobbyMan::s_process_gamespy_queries_code( const Tsk::Task< LobbyMan >& task )
{
	LobbyMan& man = task.GetData();
	Tmr::Time current_time;

	current_time = Tmr::GetTime();
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	//if( s_gamespy_parse_data[0] != '\0' )
	if( s_gamespy_data_type == vGAMESPY_QUERY_DATA )
	{
		//Dbg_Printf( "***** Parsing Gamespy Packet Data...\n" ); 
		//qr_parse_query( NULL, s_gamespy_parse_data, &s_gamespy_sender );
		peerParseQuery( man.GetPeer(), s_gamespy_parse_data, s_gamespy_parse_data_len, &s_gamespy_sender );
		s_gamespy_parse_data[0] = '\0';
		s_gamespy_parse_data_len = 0;
		s_gamespy_data_type = vGAMESPY_NO_DATA;
	}
	else if( s_gamespy_data_type == vGAMESPY_NAT_DATA )
	{
		//Dbg_Printf( "Processing Nat Neg data\n" );
        NNProcessData( s_gamespy_parse_data, s_gamespy_parse_data_len, (sockaddr_in*) &s_gamespy_sender );
		s_gamespy_parse_data[0] = '\0';
		s_gamespy_parse_data_len = 0;
		s_gamespy_data_type = vGAMESPY_NO_DATA;
	}
	else if( s_got_gamespy_callback == false )
	{
		Manager * gamenet_man = Manager::Instance();

		if( gamenet_man->InNetGame() && gamenet_man->OnServer())
		{
			// After N seconds, if we have yet to be contacted by GameSpy, notify the user that his server
			// was not properly posted to GameSpy
			if( ( current_time - s_game_start_time ) > vPOST_SERVER_TIMEOUT )
			{
				
				if( s_notified_user_not_connected == false )
				{
					Script::RunScript( "CreateNotPostedDialog" );
					s_notified_user_not_connected = true;
				}
			}
			else if(( current_time - s_last_post_time ) > vPOST_SERVER_RETRY_TIME )
			{
				// Gamespy still hasn't queried us for our options. Let's tell them again
				// that our server is up
				peerStateChanged( man.GetPeer());
				s_last_post_time = current_time;
			}
		}
	}
	
	//qr_process_queries( NULL );

	Mem::Manager::sHandle().PopContext();
}

/*****************************************************************************
**							  Handler Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

LobbyMan::LobbyMan( void )
{
	m_in_group_room = false;
	m_expecting_disconnect = true;
	m_peer = NULL;

	m_lobby_logic_task = new Tsk::Task< LobbyMan > ( s_lobby_logic_code, *this );
	m_nat_negotiate_task = new Tsk::Task< LobbyMan > ( s_nat_negotiate_think_code, *this );
	m_process_gamespy_queries_task = new Tsk::Task< LobbyMan > ( s_process_gamespy_queries_code, *this );
	qr2_register_key( NUMOBSERVERS_KEY, "numobservers" );
	qr2_register_key( MAXOBSERVERS_KEY, "maxobservers" );
	qr2_register_key( SKILLLEVEL_KEY, "skilllevel" );
	qr2_register_key( STARTED_KEY, "started" );
	qr2_register_key( HOSTED_MODE_KEY, "hostmode" );
	qr2_register_key( RANKED_KEY, "ranked" );
	qr2_register_key( RATING__KEY, "rating_" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

LobbyMan::~LobbyMan( void )
{
	delete m_lobby_logic_task;
	delete m_nat_negotiate_task;
	delete m_process_gamespy_queries_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::StartReportingGame( void )
{
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	Manager * gamenet_man = Manager::Instance();
	Net::Server* server;
	PEERBool result;

	server = gamenet_man->GetServer();
	Dbg_Assert( server );
	
	server->SetForeignPacketHandler( gamespy_data_handler );
	s_gamespy_parse_data[0] = '\0';
	s_gamespy_data_type = vGAMESPY_NO_DATA;
    
	result = peerStartReportingWithSocket( m_peer, server->GetSocket(), vHOST_PORT );
	if( result == true )
	{
		peerStateChanged( m_peer );
	}

	s_game_start_time = Tmr::GetTime();
	s_last_post_time = s_game_start_time;
	s_notified_user_not_connected = false;
	s_got_gamespy_callback = false;
	mlp_manager->AddLogicTask( *m_process_gamespy_queries_task );
	m_reported_game = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::StopReportingGame( void )
{
	if( m_peer )
	{
        peerStopGame( m_peer );
		m_process_gamespy_queries_task->Remove();
	}

	m_reported_game = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		LobbyMan::ReportedGame( void )
{
	return m_reported_game;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::StartLobbyList( void )
{
	Manager * gamenet_man = Manager::Instance();
	//Thread::PerThreadStruct	net_thread_data;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

    gamenet_man->SetServerListState( vSERVER_LIST_STATE_GETTING_LOBBY_LIST );

	// Reset the server list refresh time so that the 1-second minimum rule for refreshing server
	// lists won't apply when going back and forth really quickly to/from lobby/server lists
	s_last_refresh_time = 0;

	peerListGroupRooms( m_peer, "\\maxplayers\\neversoft\\maxrating\\minrating", s_group_rooms_callback, this, false );
    
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::StopLobbyList( void )
{
	Lst::Search< LobbyInfo > sh;
	LobbyInfo* lobby, *next;
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	if( m_peer )
	{
		peerStopListingGames( m_peer );
	}

	for( lobby = sh.FirstItem( m_lobbies ); lobby; lobby = next )
	{
		next = sh.NextItem();
		delete lobby;
	}

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::FillLobbyList( void )
{
	Lst::Search< LobbyInfo > sh;
	LobbyInfo* lobby;
	int index;

	Script::RunScript( "dialog_box_exit" );
	Script::RunScript( "create_lobby_list_menu" );
	
	index = 0;
	for( lobby = sh.FirstItem( m_lobbies ); lobby; lobby = sh.NextItem())
	{
		add_lobby_to_menu( lobby, index++ );    
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

LobbyInfo*	LobbyMan::GetLobbyInfo( int index )
{
	Lst::Search< LobbyInfo > sh;
	LobbyInfo* lobby;

	for( lobby = sh.FirstItem( m_lobbies ); lobby; lobby = sh.NextItem())
	{
		if( index == 0 )
		{
			return lobby;
		}

		index--;
	}
	
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::SetLobbyId( int id )
{
	m_lobby_id = id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int			LobbyMan::GetLobbyId( void )
{
	return m_lobby_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::SetLobbyNumServers( int num_servers )
{
	m_lobby_num_servers = num_servers;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int			LobbyMan::GetLobbyNumServers( void )
{
	return m_lobby_num_servers;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::SetLobbyMaxServers( int max_servers )
{
	m_lobby_max_servers = max_servers;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int			LobbyMan::GetLobbyMaxServers( void )
{
	return m_lobby_max_servers;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::SetLobbyName( char* name )
{
	Dbg_Assert( name );

	strcpy( m_lobby_name, name );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*		LobbyMan::GetLobbyName( void )
{
	return m_lobby_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::SetServerName( char* name )
{
	strcpy( m_server_name, name );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*		LobbyMan::GetServerName( void )
{
	return m_server_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		LobbyMan::SetOfficialLobby( bool official )
{
	m_official = official;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		LobbyMan::IsLobbyOfficial( void )
{
	return m_official;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	LobbyMan::s_nat_negotiate_complete( NegotiateResult result, SOCKET gamesocket, 
												struct sockaddr_in *remoteaddr, void *userdata )
{
	Manager* gamenet_man = Manager::Instance();
	int cookie;

	cookie = (int) userdata;

	Dbg_Printf( "**** NAT Negotiation Complete: %d\n", result );
	switch( result )
	{
		case nr_success:
		{
			Dbg_Printf( "**** NAT Negotiation Complete: Success!\n" );

			if( !gamenet_man->OnServer())
			{
				bool observe_only;
				Dbg_Printf( "**** IP: %s Port: %d\n", inet_ntoa( remoteaddr->sin_addr ), ntohs( remoteaddr->sin_port ));
				observe_only = ( gamenet_man->GetJoinMode() == GameNet::vJOIN_MODE_OBSERVE );
				gamenet_man->JoinServer( observe_only, (int) remoteaddr->sin_addr.s_addr, ntohs( remoteaddr->sin_port ), 0 );
			}
			break;
		}
		case nr_deadbeatpartner:
			Dbg_Printf( "**** NAT Negotiation Complete: Deadbeat Partner\n" );
			break;
		case nr_inittimeout:
			Dbg_Printf( "**** NAT Negotiation Complete: Timeout\n" );
			break;
		case nr_unknownerror:
		default:
			Dbg_Printf( "**** NAT Negotiation Complete: Unknown Error\n" );
			break;
	}

	if( gamenet_man->OnServer())
	{
		gamenet_man->mpLobbyMan->m_nat_count.Release();
		if( gamenet_man->mpLobbyMan->m_nat_count.InUse() == false )
		{
			gamenet_man->mpLobbyMan->m_nat_negotiate_task->Remove();
		}
	}
	else
	{
		Net::Client* client;

		NNCancel( cookie );
		NNThink();
		NNFreeNegotiateList();

		client = gamenet_man->GetClient( 0 );
		client->SetForeignPacketHandler( NULL );
		gamenet_man->mpLobbyMan->m_nat_negotiate_task->Remove();
		gamenet_man->mpLobbyMan->m_process_gamespy_queries_task->Remove();

		if( result != nr_success )
		{
			Script::RunScript( "show_nat_timeout" );
		}
	}

	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	LobbyMan::s_nat_negotiate_progress( NegotiateState state, void *userdata )
{
    switch( state )
	{
		case ns_initack:
			Dbg_Printf( "**** NAT Negotiation : Init Ack State\n" );
			break;
		case ns_connectping:
			Dbg_Printf( "**** NAT Negotiation : Connect Ping State\n" );
			break;
		default:
			Dbg_Printf( "**** NAT Negotiation : Unknown State\n" );
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptCancelNatNegotiation(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager* gamenet_man = Manager::Instance();

	NNCancel( gamenet_man->mpLobbyMan->m_cookie );
	NNThink();
	NNFreeNegotiateList();
	
	gamenet_man->mpLobbyMan->m_nat_negotiate_task->Remove();
	gamenet_man->mpLobbyMan->m_process_gamespy_queries_task->Remove();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptStartNatNegotiation(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager* gamenet_man = Manager::Instance();
	LobbyMan* lobby_man;
	Mlp::Manager* mlp_manager = Mlp::Manager::Instance();
	Net::Client* client;
	const char *server_ip = NULL;
	int port = 0;
	NegotiateError err;
    
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	lobby_man = gamenet_man->mpLobbyMan;
	pParams->GetText( NONAME, &server_ip );
	pParams->GetInteger( NONAME, &port );
	pParams->GetChecksum( CRCD( 0x751f4599, "cookie" ), (uint32*) &lobby_man->m_cookie );
	
    //port = 9959;
	Dbg_Printf( "******* Starting NAT Negotiation for server : %s port %p *******", server_ip, port );
	
	client = gamenet_man->SpawnClient( false, false, true, 0 );
    peerSendNatNegotiateCookie( lobby_man->GetPeer(), inet_addr( server_ip ), 
								port, lobby_man->m_cookie );
    err = NNBeginNegotiationWithSocket( client->GetSocket(), lobby_man->m_cookie, 1, s_nat_negotiate_progress, 
										s_nat_negotiate_complete, (void*) lobby_man->m_cookie );
	if( err != ne_noerror )
	{
		Dbg_Printf( "******* NAT Neg Error: %d\n", err );
		NNFreeNegotiateList();
		Mem::Manager::sHandle().PopContext();
		return false;
	}
	Dbg_Assert( err == ne_noerror );

	Dbg_Printf( "*** Added NAT Task\n" );
	mlp_manager->AddLogicTask( *lobby_man->m_nat_negotiate_task );
	client->SetForeignPacketHandler( gamespy_data_handler );
	mlp_manager->AddLogicTask( *lobby_man->m_process_gamespy_queries_task );
	s_gamespy_parse_data[0] = '\0';

	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptRejoinLobby(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager* gamenet_man = Manager::Instance();
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	peerJoinGroupRoom( gamenet_man->mpLobbyMan->m_peer, gamenet_man->mpLobbyMan->GetLobbyId(), s_join_room_callback, gamenet_man->mpLobbyMan, false );

	Mem::Manager::sHandle().PopContext();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptChooseLobby(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	LobbyInfo* p_lobby;
	Manager* gamenet_man = Manager::Instance();
	int index = 0;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	pParams->GetInteger( Script::GenerateCRC("index"), &index );
				
	p_lobby = gamenet_man->mpLobbyMan->GetLobbyInfo( index );
	Dbg_Assert( p_lobby );
								
	//Dbg_Printf( "Choosing lobby %d\n", p_lobby->m_GroupId );
	gamenet_man->mpLobbyMan->SetLobbyId( p_lobby->m_GroupId );
	gamenet_man->mpLobbyMan->SetLobbyNumServers( p_lobby->m_NumServers );
	gamenet_man->mpLobbyMan->SetLobbyMaxServers( p_lobby->m_MaxServers );
	gamenet_man->mpLobbyMan->SetLobbyName( p_lobby->m_Name );
	gamenet_man->mpLobbyMan->SetOfficialLobby( p_lobby->m_Official );
	
    peerJoinGroupRoom( gamenet_man->mpLobbyMan->m_peer, gamenet_man->mpLobbyMan->GetLobbyId(), s_join_room_callback, gamenet_man->mpLobbyMan, false );

	// Don't need the lobby list anymore. We have recorded all its data
	gamenet_man->mpLobbyMan->StopLobbyList();

	Mem::Manager::sHandle().PopContext();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptGetNumPlayersInLobby(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager* gamenet_man = Manager::Instance();
	Script::CStruct* p_return_params;

	p_return_params = pScript->GetParams();
	p_return_params->AddInteger( "num_players", gamenet_man->mpLobbyMan->m_players.CountItems());
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptLeaveLobby(Script::CScriptStructure *pParams, Script::CScript* pScript )
{
	Manager* gamenet_man = Manager::Instance();
	LobbyMan* lobby_man;
	BuddyMan* buddy_man;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	lobby_man = gamenet_man->mpLobbyMan;
	buddy_man = gamenet_man->mpBuddyMan;

	peerLeaveRoom( lobby_man->m_peer, GroupRoom, NULL );
	lobby_man->m_in_group_room = false;

	if( buddy_man->IsLoggedIn())
	{
		if( !pParams->ContainsComponentNamed( CRCD(0xb2ab4dab,"preserve_status")))
		{
			buddy_man->SetStatusAndLocation( GP_ONLINE, 	(char*) Script::GetString( "homie_status_online" ), 
														(char*) Script::GetString( "homie_status_logging_in" ));
		}
	}

	// Maintain our association with our group room so that, if we're hosting a game, it will
	// report it to the correct group room
	peerSetGroupID( lobby_man->m_peer, lobby_man->GetLobbyId());

	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptLobbyConnect(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	PEERBool ping_rooms[NumRooms] = {false, true, false};
	PEERBool x_ping_rooms[NumRooms] = {false, false, false};
	PEER peer;
	Thread::PerThreadStruct	net_thread_data;
    
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
	
	gamenet_man->mpLobbyMan->Initialize();
	
	peer = gamenet_man->mpLobbyMan->m_peer;
	peerSetTitle( peer, 
				  "thps5ps2", 
				  "G2k8cF", 
				  "thps5ps2", 
				  "G2k8cF", 
				  vGAME_VERSION,
				  15, 
				  true,
				  ping_rooms, 
				  x_ping_rooms );

	Dbg_Printf( "Connecting to lobby 1\n" );
	net_thread_data.m_pEntry = s_threaded_peer_connect;
	net_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
	net_thread_data.m_pStackBase = gamenet_man->GetNetThreadStack();
	net_thread_data.m_iStackSize = vNET_THREAD_STACK_SIZE;
	net_thread_data.m_utid = 0x152;
	Thread::CreateThread( &net_thread_data );
	gamenet_man->SetNetThreadId( net_thread_data.m_osId );
	StartThread( gamenet_man->GetNetThreadId(), gamenet_man->mpLobbyMan );
	Dbg_Printf( "Connecting to lobby 2\n" );
	mlp_man->AddLogicTask( *gamenet_man->mpLobbyMan->m_lobby_logic_task );
	
	Mem::Manager::sHandle().PopContext();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptLobbyDisconnect(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	Dbg_Printf( "******************************* DISCONNECTING FROM PEER!!!! \n" );

	while( gamenet_man->mpLobbyMan->m_nat_count.InUse())
	{
		gamenet_man->mpLobbyMan->m_nat_count.Release();
	}

	gamenet_man->mpLobbyMan->StopLobbyList();
	gamenet_man->mpLobbyMan->clear_player_list();
	gamenet_man->mpLobbyMan->m_lobby_logic_task->Remove();
	gamenet_man->mpLobbyMan->m_nat_negotiate_task->Remove();
	gamenet_man->mpLobbyMan->m_in_group_room = false;
	gamenet_man->mpLobbyMan->m_expecting_disconnect = true;
	if( gamenet_man->mpLobbyMan->m_peer )
	{
		peerStopGame( gamenet_man->mpLobbyMan->m_peer );
		peerClearTitle( gamenet_man->mpLobbyMan->m_peer );
		peerDisconnect( gamenet_man->mpLobbyMan->m_peer );
	}
	gamenet_man->mpLobbyMan->Shutdown();
	//peerShutdown( gamenet_man->mpLobbyMan->m_peer );
	//gamenet_man->mpLobbyMan->m_peer = NULL;

	//Dbg_Printf( "******************************* DISCONNECTED FROM PEER!!!! \n" );
	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptSetQuietMode( Script::CScriptStructure* pParams, Script::CScript* pScript )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	if( pParams->ContainsFlag( "off" ))
	{
		peerSetQuietMode( gamenet_man->mpLobbyMan->m_peer, false );
	}
	else
	{
		peerSetQuietMode( gamenet_man->mpLobbyMan->m_peer, true );
	}
	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptFillPlayerList( Script::CScriptStructure* pParams, Script::CScript* pScript )
{
	Spt::SingletonPtr< GameNet
		::Manager > gamenet_man;

	gamenet_man->mpLobbyMan->fill_player_list();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptFillLobbyProspectiveBuddyList( Script::CScriptStructure* pParams, Script::CScript* pScript )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	gamenet_man->mpLobbyMan->fill_prospective_buddy_list();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptCanHostGame( Script::CScriptStructure* pParams, Script::CScript* pScript )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	if( gamenet_man->mpLobbyMan->IsLobbyOfficial())
	{
		if( gamenet_man->mpBuddyMan->IsLoggedIn())
		{
			int profile;

			profile = gamenet_man->mpBuddyMan->GetProfile();
			if(	( profile == 27747931 ) ||
				( profile == 27747977 ) ||
				( profile == 27748011 ) ||
				( profile == 27748097 ) ||
				( profile == 27748044 ) ||
				( profile == 27748142 ) ||
				( profile == 27748187 ) ||
				( profile == 27748211 ) ||
				( profile == 27748244 ) ||
				( profile == 27748273 ))
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		else
		{
			return false;
		}
	}

	return( gamenet_man->mpLobbyMan->GetLobbyNumServers() < gamenet_man->mpLobbyMan->GetLobbyMaxServers());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	LobbyMan::ScriptSendMessage( Script::CScriptStructure* pParams, Script::CScript* pScript )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	const char *p_text;
	
	if( pParams->GetText( "text", &p_text ))
	{
		if( pParams->ContainsFlag( "system_message" ))
		{
			Script::CStruct* p_params;
			char msg[1024];
		
			sprintf( msg, " %s", p_text );
			p_params = new Script::CStruct;	
			p_params->AddString( "text", msg );
			Script::RunScript("create_console_message", p_params );
			delete p_params;
		}
		else
		{
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
			peerMessageRoom( gamenet_man->mpLobbyMan->m_peer, GroupRoom, p_text, NormalMessage );
			Mem::Manager::sHandle().PopContext();
		}
	}

	return true;
}
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	LobbyMan::Initialize( void )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	PEERCallbacks callbacks;

	memset( &callbacks, 0, sizeof( PEERCallbacks ));
	callbacks.disconnected = s_disconnected_callback;
	callbacks.roomMessage = s_room_message_callback;
	callbacks.playerMessage = s_player_message_callback;
	callbacks.playerJoined = s_player_joined_callback;
	callbacks.playerLeft = s_player_left_callback;
	callbacks.playerInfo = s_player_info_callback;
	callbacks.newPlayerList = s_new_player_list_callback;
	callbacks.roomKeyChanged = s_room_key_callback;

    callbacks.qrNatNegotiateCallback = s_nat_negotiate_callback;
	callbacks.qrKeyList = s_key_list_callback;
	callbacks.qrServerKey = s_server_key_callback;
	callbacks.qrPlayerKey = s_player_key_callback;
	callbacks.qrCount = s_key_count_callback;
	callbacks.qrAddError = s_add_error_callback;
	callbacks.qrPublicAddressCallback = s_public_address_callback;
	callbacks.param = this;
	
	m_peer = peerInitialize( &callbacks );
	Dbg_Assert( m_peer );
	
	m_in_group_room = false;
	m_expecting_disconnect = true;
	m_reported_game = false;
	m_official = false;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	LobbyMan::Shutdown( void )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	m_lobby_logic_task->Remove();
	m_nat_negotiate_task->Remove();
	m_process_gamespy_queries_task->Remove();

	if( m_peer )
	{
		peerShutdown( m_peer );
	}
	m_peer = NULL;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PEER	LobbyMan::GetPeer( void )
{
	return m_peer;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


bool	LobbyMan::InGroupRoom( void )
{
	return m_in_group_room;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	LobbyMan::PlayerName( char* lobby_name )
{
	char* player_name;

	player_name = strchr( lobby_name, '_' );
	if( player_name == NULL )
	{
		return lobby_name;
	}
	return ( player_name + 1 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

LobbyPlayerInfo::LobbyPlayerInfo( void ) : Lst::Node< LobbyPlayerInfo > ( this )
{
	m_Profile = 0;
	m_Rating = 0;
	m_GotInfo = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


} // namespace GameNet






================================================
FILE: Code/Sk/GameNet/Lobby.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet													**
**																			**
**	File name:		Lobby.h													**
**																			**
**	Created:		04/04/02	- spg										**
**																			**
*****************************************************************************/

#ifndef	__LOBBY_H
#define	__LOBBY_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

namespace GameNet
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

enum
{
	NUMOBSERVERS_KEY = NUM_RESERVED_KEYS + 1,
	MAXOBSERVERS_KEY,
	SKILLLEVEL_KEY,
	STARTED_KEY,
	HOSTED_MODE_KEY,
	RANKED_KEY,
	RATING__KEY,
};
	

/*****************************************************************************
**							     Class Definitions							**
*****************************************************************************/

class LobbyPlayerInfo : public Lst::Node< LobbyPlayerInfo >
{
	public:
		LobbyPlayerInfo( void );

		char	m_Name[64];
		int		m_Profile;
		int		m_Rating;
		bool	m_GotInfo;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class LobbyMan
{
public:
				LobbyMan( void );
				~LobbyMan( void );

	void		Initialize( void );
	void		Shutdown( void );

	void		StartReportingGame( void );
	void		StopReportingGame( void );
	bool		ReportedGame( void );

	void		StartLobbyList( void );
	void		StopLobbyList( void );
	void		FillLobbyList( void );

	LobbyInfo*	GetLobbyInfo( int index );

	void		SetLobbyId( int id );
	int			GetLobbyId( void );
	void		SetLobbyNumServers( int num_servers );
	int			GetLobbyNumServers( void );
	void		SetLobbyMaxServers( int max_servers );
	int			GetLobbyMaxServers( void );
	char*		GetServerName( void );
	void		SetServerName( char* name );
	void		SetLobbyName( char* name );
	char*		GetLobbyName( void );
	void		SetOfficialLobby( bool official );
	bool		IsLobbyOfficial( void );

	PEER		GetPeer( void );
	bool		InGroupRoom( void );
	char*		PlayerName( char* lobby_name );
    
	static	bool	ScriptGetNumPlayersInLobby(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptChooseLobby(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptRejoinLobby(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptLeaveLobby(Script::CScriptStructure *pParams, Script::CScript* pScript );
	static	bool	ScriptLobbyConnect(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptLobbyDisconnect(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptSendMessage( Script::CScriptStructure* pParams, Script::CScript* pScript );
	static	bool	ScriptSetQuietMode( Script::CScriptStructure* pParams, Script::CScript* pScript );
	static	bool	ScriptFillPlayerList( Script::CScriptStructure* pParams, Script::CScript* pScript );
	static	bool	ScriptFillLobbyProspectiveBuddyList( Script::CScriptStructure* pParams, Script::CScript* pScript );
	static	bool	ScriptCanHostGame( Script::CScriptStructure* pParams, Script::CScript* pScript );
	static	bool	ScriptStartNatNegotiation(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptCancelNatNegotiation(Script::CScriptStructure *pParams, Script::CScript *pScript);

private:
	Lst::Head< LobbyInfo > 	m_lobbies;
	Lst::Head< LobbyPlayerInfo > m_players;

	int				m_lobby_id;
	int				m_lobby_max_servers;
	int				m_lobby_num_servers;
	char			m_lobby_name[128];
	char			m_server_name[128];
	bool			m_in_group_room;
	bool			m_official;
	
	bool			m_expecting_disconnect;
	bool			m_connection_in_progress;
	bool			m_reported_game;

	Spt::Ref		m_nat_count;
		
	PEER			m_peer;
	int				m_cookie;
	
	void			add_lobby_to_menu( LobbyInfo* lobby, int index );
	void			add_player_to_menu( LobbyPlayerInfo* player );
	void			add_buddy_to_menu( LobbyPlayerInfo* player );
	void			refresh_player_list( void );
	void			clear_player_list( void );
	void			fill_player_list( void );
	void			fill_prospective_buddy_list( void );

	Tsk::Task< LobbyMan >*		m_lobby_logic_task;
	Tsk::Task< LobbyMan >*		m_nat_negotiate_task;
	Tsk::Task< LobbyMan >*		m_process_gamespy_queries_task;
	static	Tsk::Task< LobbyMan >::Code   	s_lobby_logic_code;
	static	Tsk::Task< LobbyMan >::Code   	s_nat_negotiate_think_code;
	static	Tsk::Task< LobbyMan >::Code		s_process_gamespy_queries_code;

	static	void	s_enum_players_callback( PEER peer, PEERBool success, RoomType roomType, int index, const char * nick,  // The nick of the player.
												int flags, void * param );
	static 	void	s_nick_error_callback( PEER peer, int type, const char* nick, void *param );
	static	void	s_connect_callback( PEER peer, PEERBool success, void* param );

	static	void 	s_disconnected_callback( PEER peer, const char* reason, void* param );
	static	void 	s_room_message_callback( PEER peer, RoomType roomType, const char * nick, const char * message,
										MessageType messageType, void * param );
	static	void 	s_player_message_callback( PEER peer, const char * nick, const char * message, MessageType messageType,
												void * param );
	static 	void 	s_player_joined_callback( PEER peer, RoomType roomType, const char * nick, void * param );
	static	void 	s_player_info_callback( PEER peer, RoomType roomType, const char * nick, unsigned int IP, int profileID,  
                                                void * param );
	static	void	s_player_proile_id_callback( PEER peer, PEERBool success,  const char * nick, int profileID, void * param );
	static	void	s_player_left_callback( PEER peer, RoomType roomType, const char * nick, const char * reason, 
												void * param );
	static	void	s_new_player_list_callback( PEER peer, RoomType roomType, void * param );
	static	void	s_room_key_callback( PEER peer, RoomType roomType, const char * nick, 
										 const char * key, const char * value, void * param );
	static	void	s_nat_negotiate_callback( PEER peer, int cookie, void * param );
	static	void	s_key_list_callback( PEER peer, qr2_key_type type, qr2_keybuffer_t keyBuffer, 
									void * param );
	static	void	s_server_key_callback( PEER peer, int key, qr2_buffer_t buffer, void * param );
	static	void	s_player_key_callback( PEER peer, int key, int index, qr2_buffer_t buffer, void * param );
	static	int		s_key_count_callback( PEER peer, qr2_key_type type, void * param );
	static	void    s_add_error_callback( PEER peer, qr2_error_t error, char * errorString, void * param );
	static	void    s_public_address_callback( PEER peer, unsigned int ip, unsigned short port, 
											   void * param );
	static	void	s_public_address_callback( PEER peer, qr2_error_t error, char * errorString, void * param );

	static	void	s_group_rooms_callback( PEER peer, PEERBool success, int groupID, SBServer server,  
									const char * name, int numWaiting, int maxWaiting, int numGames, int numPlaying,
									void* param );
	static	void	s_join_room_callback( PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, 
										void * param );
	static	void 	s_get_room_keys_callback( PEER peer, PEERBool success, RoomType roomType, 
											const char * nick, int num, char ** keys, char ** values, 
											void * param );

	//static 	void 	s_lobby_list_callback( GServerList serverlist, int msg, void *instance, void *param1, void *param2 );
	//static	void	s_threaded_get_lobby_list( LobbyMan* lobby_man );

	static	void	s_threaded_peer_connect( LobbyMan* lobby_man );
	static	void	s_nat_negotiate_progress( NegotiateState state, void *userdata );
	static	void	s_nat_negotiate_complete( NegotiateResult result, SOCKET gamesocket, 
												struct sockaddr_in *remoteaddr, void *userdata);

};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

}	// namespace GameNet

#endif	// __LOBBY_H

================================================
FILE: Code/Sk/GameNet/Ngps/dnas.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define WAIT_VSYNC	0x1111
#define vAUTH_TIMEOUT	30

static	int	s_extra_time = 0;

//#define __DNAS_DEBUG__

static int s_get_thread_priority( void )
{
	struct ThreadParam info;
	// Fill info with zeros to be sure the following call is doing
	// something to info
	memset(&info,0,sizeof(info));
	// return value not defined in 2.0 PDF EE lib ref
	ReferThreadStatus( GetThreadId(), &info );
	return ( info.currentPriority );
}

static int netstart_dnas(int proxy_state, const char *proxy_host, u_short proxy_port, int timeout, sceDNAS2Status_t& status) 
{
	int  ret, error = 0;
	sceDNAS2TitleAuthInfo_t auth_info;
	sceDNAS2Status_t prev_status;
	sceDNAS2TimeoutInfo_t timeout_info;
	int /*fd, */proxy_state_check = 0;
	char* auth_data;
	char proxy_host_check[512];
	u_short proxy_port_check;
	unsigned char* phrase;
	//int vsync_handler_id;
	int orig_priority;

    unsigned char us_english_pass_phrase[] = { 0xfb, 0x31, 0xce, 0xf7, 0x2a, 0x57, 0x2f, 0xf4 };
	unsigned char uk_english_pass_phrase[] = { 0xea, 0x67, 0x94, 0x2c, 0x37, 0xd1, 0xfc, 0x8f };
	unsigned char spanish_pass_phrase[] = { 0xb5, 0x05, 0xcb, 0x0b, 0xe5, 0x20, 0x8e, 0x38 };
    unsigned char german_pass_phrase[] = { 0x91, 0xe7, 0x87, 0x5c, 0xc5, 0x3f, 0x70, 0x4c };
    unsigned char french_pass_phrase[] = { 0x15, 0xb3, 0x8d, 0x7f, 0x2d, 0x81, 0xbe, 0x05 };
    unsigned char italian_pass_phrase[] = { 0x88, 0xe9, 0xfa, 0x1e, 0xb4, 0x3d, 0x88, 0x3c };

	phrase = NULL;

	auth_data = new char[1024*64];

		/* Initialize ID module for DNAS */
	void *p_file;
	 
	p_file = NULL;
	if( Config::gLanguage == Config::LANGUAGE_ENGLISH )
	{
		if( Config::gDisplayType == Config::DISPLAY_TYPE_NTSC )
		{
#ifdef __DNAS_DEBUG__
			scePrintf( "Auth File: dnas/SLUS20731_client.dat\n" );
#endif
			p_file = File::Open( "dnas/SLUS20731_client.dat", "rb" );
			phrase = us_english_pass_phrase;
		}
		else
		{
#ifdef __DNAS_DEBUG__
			scePrintf( "Auth File: dnas/SLES51848_client.dat\n" );
#endif
			p_file = File::Open( "dnas/SLES51848_client.dat", "rb" );
			phrase = uk_english_pass_phrase;
		}
	}
	else if( Config::gLanguage == Config::LANGUAGE_FRENCH )
	{
#ifdef __DNAS_DEBUG__
		scePrintf( "Auth File: dnas/SLES51851_client.dat\n" );
#endif
		p_file = File::Open( "dnas/SLES51851_client.dat", "rb" );
		phrase = french_pass_phrase;
	}
	else if( Config::gLanguage == Config::LANGUAGE_GERMAN )
	{
#ifdef __DNAS_DEBUG__
		scePrintf( "Auth File: dnas/SLES51852_client.dat\n" );
#endif
		p_file = File::Open( "dnas/SLES51852_client.dat", "rb" );
		phrase = german_pass_phrase;
	}
	else if( Config::gLanguage == Config::LANGUAGE_ITALIAN )
	{
#ifdef __DNAS_DEBUG__
		scePrintf( "Auth File: dnas/SLES51853_client.dat\n" );
#endif
		p_file = File::Open( "dnas/SLES51853_client.dat", "rb" );
		phrase = italian_pass_phrase;
	}
	else if( Config::gLanguage == Config::LANGUAGE_SPANISH )
	{
#ifdef __DNAS_DEBUG__
		scePrintf( "Auth File: dnas/SLES51854_client.dat\n" );
#endif
		p_file = File::Open( "dnas/SLES51854_client.dat", "rb" );
		phrase = spanish_pass_phrase;
	}
	else
	{
#ifdef __DNAS_DEBUG__
		scePrintf( "Auth File: NONE!\n" );
#endif
	}

	Dbg_Assert( p_file != NULL );
	Dbg_Assert( phrase != NULL );

#ifdef __DNAS_DEBUG__
	scePrintf( "p_file: 0x%x  phrase: 0x%x\n", p_file, phrase );
#endif

	//fd = sceOpen("host0:../../authfile/auth.dat", SCE_RDONLY);
	Dbg_Printf( "install_dnas(1)\n" );

	File::Read( auth_data, 1, 1024 * 64, p_file );
	//sceRead(fd, auth_data, sizeof(auth_data));

	Dbg_Printf( "install_dnas(2)\n" );
	File::Close( p_file );

	orig_priority = s_get_thread_priority();
	ChangeThreadPriority( GetThreadId(), 49 );

	memset(&status, 0, sizeof(sceDNAS2Status_t));
	memset(&prev_status, 0, sizeof(sceDNAS2Status_t));
	memset(&auth_info, 0, sizeof(sceDNAS2TitleAuthInfo_t));
	if( timeout > 0 )
	{
		memset(&timeout_info, 0, sizeof(sceDNAS2TimeoutInfo_t));
		timeout_info.timeout = timeout;
		timeout_info.priority = 48;
	}
	auth_info.line_type = sceDNAS2_T_INET;
	auth_info.auth_data.ptr = auth_data;
	auth_info.auth_data.size = 1024 * 64;//sizeof(auth_data);
	auth_info.pass_phrase.ptr = phrase;
	auth_info.pass_phrase.size = sizeof(phrase);
#ifdef __DNAS_DEBUG__
	scePrintf("before sceDNAS2Init()\n");
#endif

	if(timeout > 0)
	{
		ret = sceDNAS2InitNoHDD(&auth_info, 48, 0, 0, &timeout_info);
	}
	else
	{
		ret = sceDNAS2InitNoHDD(&auth_info, 48, 0, 0, NULL);
	}
	/* NOTE */
	/* sceDNAS2InitNoHDD() with Debug flag should never be used in the final release, but for test program only */
#ifdef __DNAS_DEBUG__
	scePrintf("return value of sceDNAS2Init(): %d\n", ret);
#endif
	if(ret < 0)
	{
		sceDNAS2GetStatus( &status );
		
		error = ret;
		goto error;
	}

	/*set proxy parameter*/
	if(proxy_state)
	{
		ret = sceDNAS2SetProxy(1, proxy_host, proxy_port);
#ifdef __DNAS_DEBUG__
		scePrintf("return value of sceDNAS2SetProxy = %d\n", ret);
#endif
		if(ret < 0)
		{
			sceDNAS2GetStatus( &status );
			error = ret;
			goto error;
		}
	}

	/*get proxy parameter*/
	if(proxy_state)
	{
		ret = sceDNAS2GetProxy(&proxy_state_check, proxy_host_check, sizeof(proxy_host_check), &proxy_port_check);
#ifdef __DNAS_DEBUG__
		scePrintf("return value of sceDNASGetProxy = %d\n", ret);
#endif
		if(ret < 0)
		{
			sceDNAS2GetStatus( &status );
			error = ret;
			goto error;
		}
#ifdef __DNAS_DEBUG__
		if(proxy_state_check)
		{
			scePrintf("Proxy Info : Server = %s Port = %d\n", proxy_host_check, proxy_port_check);
		}
		else
		{
			scePrintf("Proxy Info : no use \n");
		}
#endif
	}

	ret = sceDNAS2AuthNetStart();
#ifdef __DNAS_DEBUG__
	scePrintf("ret of AuthNetStart() = %d\n", ret);
#endif
	if(ret < 0)
	{
		sceDNAS2GetStatus( &status );
		error = ret;
		goto error;
	}

	while(1)
	{
		if((ret = sceDNAS2GetStatus(&status)) < 0)
		{
#ifdef __DNAS_DEBUG__
			scePrintf("ret of sceDNAS2GetStatus() = %d\n", ret);
#endif
			error = ret;
			goto error;
		}
		if((ret = memcmp(&prev_status, &status, sizeof(sceDNAS2Status_t))) != 0)
		{
			/* status changed */
#ifdef __DNAS_DEBUG__
			scePrintf("code: %d, sub_code: %d, progress: %d\n",
				status.code, status.sub_code, status.progress);
#endif
			if(status.code == sceDNAS2_S_END)
			{
#ifdef __DNAS_DEBUG__
				scePrintf("subcode:type %d, inst_result %d\n",
					sceDNAS2Status_SubCode_TYPE(status),
					sceDNAS2Status_SubCode_INST_RESULT(status));
#endif
				memcpy(&prev_status, &status, sizeof(sceDNAS2Status_t));
				break;
			}
			else if(status.code == sceDNAS2_S_NG || status.code == sceDNAS2_S_COM_ERROR)
			{
				error = -1;
				break;
			}
		}
		memcpy(&prev_status, &status, sizeof(sceDNAS2Status_t));
	}

error:
#ifdef __DNAS_DEBUG__
	if( error < 0 )
	{
		scePrintf("*********** DNAS failed. Error code : %d**********\n", error );
	}
	else
	{
		scePrintf("*********** DNAS succeeded.!!!! *************\n");
	}
#endif
	ret = sceDNAS2Shutdown();
#ifdef __DNAS_DEBUG__
	scePrintf("ret of sceDNAS2Shutdown() is %d\n", ret);
#endif
	ChangeThreadPriority( GetThreadId(), orig_priority );
	delete [] auth_data;

	return error;
}

static	uint32	s_get_error_desc( int code )
{
	switch( code )
	{
		case sceDNAS2_SS_SERVER_BUSY:
			return CRCD(0xf9814711,"net_auth_error_server_busy");
			break;
		case sceDNAS2_SS_BEFORE_SERVICE:
			return CRCD(0x87da162f,"net_auth_error_before_service");
			break;
		case sceDNAS2_SS_OUT_OF_SERVICE:
			return CRCD(0xa7b3ce4c,"net_auth_error_out_of_service");
			break;
		case sceDNAS2_SS_END_OF_SERVICE:
			return CRCD(0x2a52e052,"net_auth_error_end_of_service");
			break;
		case sceDNAS2_SS_SESSION_TIME_OUT:
			return CRCD(0x6b58d871,"net_auth_error_time_out");
			break;
		case sceDNAS2_SS_INVALID_SERVER:
			return CRCD(0x3472f158,"net_auth_error_invalid_server");
			break;
		case sceDNAS2_SS_INTERNAL_ERROR:
			return CRCD(0x3c0c381,"net_auth_error_internal");
			break;
		case sceDNAS2_SS_EXTERNAL_ERROR:
			return CRCD(0x50617f3a,"net_auth_error_external");
			break;
		case sceDNAS2_SS_ID_NOUSE:
		case sceDNAS2_SS_ID_NOT_JOIN_TO_CAT:
			return CRCD(0xe8177760,"net_auth_error_unique");
			break;

		case sceDNAS2_SS_DL_NODATA:
		case sceDNAS2_SS_DL_BEFORE_SERVICE:
		case sceDNAS2_SS_DL_OUT_OF_SERVICE:
		case sceDNAS2_SS_DL_NOT_UPDATED:
			return CRCD(0x70295cf2,"net_auth_error_download");
			break;

		case sceDNAS2_SS_INVALID_PS2:
		case sceDNAS2_SS_INVALID_HDD_BINDING:
			return CRCD(0x448c00ef,"net_auth_error_machine");
			break;

		case sceDNAS2_SS_INVALID_MEDIA:
		case sceDNAS2_SS_INVALID_AUTHDATA:
			return CRCD(0x736c330,"net_auth_error_disc");
			break;

		case sceDNAS2_ERR_GLUE_ABORT:
		case sceDNAS2_ERR_NET_PROXY:
		case sceDNAS2_ERR_NET_SSL:
		case sceDNAS2_ERR_NET_DNS_HOST_NOT_FOUND:
		case sceDNAS2_ERR_NET_DNS_TRY_AGAIN:
		case sceDNAS2_ERR_NET_DNS_NO_RECOVERY:
		case sceDNAS2_ERR_NET_DNS_NO_DATA:
		case sceDNAS2_ERR_NET_DNS_OTHERS:
		case sceDNAS2_ERR_NET_EISCONN:
		case sceDNAS2_ERR_NET_ETIMEOUT:
		case sceDNAS2_ERR_NET_TIMEOUT:
		case sceDNAS2_ERR_NET_ECONNREFUSED:
		case sceDNAS2_ERR_NET_ENETUNREACH:
		case sceDNAS2_ERR_NET_ENOTCONN:
		case sceDNAS2_ERR_NET_ENOBUFS:
		case sceDNAS2_ERR_NET_EMFILE:
		case sceDNAS2_ERR_NET_EBADF:
		case sceDNAS2_ERR_NET_EINVAL:
		case sceDNAS2_ERR_NET_OTHERS:
			return CRCD(0x310d58d7,"net_auth_error_network");
			break;
		default:
			return CRCD(0xc7c67eb8,"net_auth_error_generic");
			break;
	}
}

static	uint32	s_get_error_footer( int code )
{
	switch( code )
	{
		case sceDNAS2_SS_BEFORE_SERVICE:
		case sceDNAS2_SS_OUT_OF_SERVICE:
		case sceDNAS2_SS_END_OF_SERVICE:
		case sceDNAS2_SS_INVALID_SERVER:
		case sceDNAS2_SS_INTERNAL_ERROR:
		case sceDNAS2_SS_EXTERNAL_ERROR:
		case sceDNAS2_SS_INVALID_PS2:
		case sceDNAS2_SS_INVALID_HDD_BINDING:
		case sceDNAS2_SS_INVALID_MEDIA:
		case sceDNAS2_SS_INVALID_AUTHDATA:
			return CRCD(0x16ff6a40,"net_auth_footer_contact");
		case sceDNAS2_SS_SERVER_BUSY:
		case sceDNAS2_SS_SESSION_TIME_OUT:
		case sceDNAS2_SS_ID_NOUSE:
		case sceDNAS2_SS_ID_NOT_JOIN_TO_CAT:
		case sceDNAS2_SS_DL_NODATA:
		case sceDNAS2_SS_DL_BEFORE_SERVICE:
		case sceDNAS2_SS_DL_OUT_OF_SERVICE:
		case sceDNAS2_SS_DL_NOT_UPDATED:
			return CRCD(0x8c671de9,"net_auth_footer_empty");
		case sceDNAS2_ERR_GLUE_ABORT:
		case sceDNAS2_ERR_NET_PROXY:
		case sceDNAS2_ERR_NET_SSL:
		case sceDNAS2_ERR_NET_DNS_HOST_NOT_FOUND:
		case sceDNAS2_ERR_NET_DNS_TRY_AGAIN:
		case sceDNAS2_ERR_NET_DNS_NO_RECOVERY:
		case sceDNAS2_ERR_NET_DNS_NO_DATA:
		case sceDNAS2_ERR_NET_DNS_OTHERS:
		case sceDNAS2_ERR_NET_EISCONN:
		case sceDNAS2_ERR_NET_ETIMEOUT:
		case sceDNAS2_ERR_NET_TIMEOUT:
		case sceDNAS2_ERR_NET_ECONNREFUSED:
		case sceDNAS2_ERR_NET_ENETUNREACH:
		case sceDNAS2_ERR_NET_ENOTCONN:
		case sceDNAS2_ERR_NET_ENOBUFS:
		case sceDNAS2_ERR_NET_EMFILE:
		case sceDNAS2_ERR_NET_EBADF:
		case sceDNAS2_ERR_NET_EINVAL:
		case sceDNAS2_ERR_NET_OTHERS:
			return CRCD(0x3a190bc4,"net_auth_footer_network");
		default:
			return CRCD(0x16ff6a40,"net_auth_footer_contact");
	}
}

static	uint32	s_get_error_footer_pal( int code )
{
	switch( code )
	{
		case sceDNAS2_SS_SERVER_BUSY:
			return CRCD(0x8c671de9,"net_auth_footer_empty");
		case sceDNAS2_SS_BEFORE_SERVICE:
		case sceDNAS2_SS_OUT_OF_SERVICE:
			return CRCD(0x99b34c96,"net_auth_footer_service_pal");
		case sceDNAS2_SS_END_OF_SERVICE:
			return CRCD(0x1a95350b,"net_auth_footer_central_pal");
		case sceDNAS2_SS_SESSION_TIME_OUT:
		case sceDNAS2_SS_INVALID_SERVER:
		case sceDNAS2_SS_INTERNAL_ERROR:
		case sceDNAS2_SS_EXTERNAL_ERROR:
			return CRCD(0x3e6e83d0,"net_auth_footer_cont_customer_pal");
		case sceDNAS2_SS_ID_NOUSE:
		case sceDNAS2_SS_ID_NOT_JOIN_TO_CAT:
		case sceDNAS2_SS_DL_NODATA:
		case sceDNAS2_SS_DL_BEFORE_SERVICE:
		case sceDNAS2_SS_DL_OUT_OF_SERVICE:
		case sceDNAS2_SS_DL_NOT_UPDATED:
			return CRCD(0x8c671de9,"net_auth_footer_empty");
		case sceDNAS2_SS_INVALID_PS2:
		case sceDNAS2_SS_INVALID_MEDIA:
		case sceDNAS2_SS_INVALID_HDD_BINDING:
			return CRCD(0xc13153cc,"net_auth_footer_customer_pal");
		case sceDNAS2_SS_INVALID_AUTHDATA:
			return CRCD(0x85cb1e0c,"net_auth_footer_clean_pal");
		case sceDNAS2_ERR_GLUE_ABORT:
		case sceDNAS2_ERR_NET_PROXY:
		case sceDNAS2_ERR_NET_SSL:
		case sceDNAS2_ERR_NET_DNS_HOST_NOT_FOUND:
		case sceDNAS2_ERR_NET_DNS_TRY_AGAIN:
		case sceDNAS2_ERR_NET_DNS_NO_RECOVERY:
		case sceDNAS2_ERR_NET_DNS_NO_DATA:
		case sceDNAS2_ERR_NET_DNS_OTHERS:
		case sceDNAS2_ERR_NET_EISCONN:
		case sceDNAS2_ERR_NET_ETIMEOUT:
		case sceDNAS2_ERR_NET_TIMEOUT:
		case sceDNAS2_ERR_NET_ECONNREFUSED:
		case sceDNAS2_ERR_NET_ENETUNREACH:
		case sceDNAS2_ERR_NET_ENOTCONN:
		case sceDNAS2_ERR_NET_ENOBUFS:
		case sceDNAS2_ERR_NET_EMFILE:
		case sceDNAS2_ERR_NET_EBADF:
		case sceDNAS2_ERR_NET_EINVAL:
		case sceDNAS2_ERR_NET_OTHERS:
			return CRCD(0x570be62b,"net_auth_footer_network_pal");
			break;
		default:
			return CRCD(0x1a95350b,"net_auth_footer_central_pal");
			break;
	}
}


extern char _dnas_start[];
extern char _dnas_size[];

bool 		GameNet::Manager::ScriptAuthenticateClient( Script::CStruct* pParams, Script::CScript* pScript )
{
	sceDNAS2Status_t status;
	int result;
	void *p_file;
	char* memory;
	
	// Hardware will be flagged as Config::HARDWARE_PS2_DEVSYSTEM if we run a 
	// .elf file.  This is probably different from what we are running on
	// the cd, so just return true, and don't actually perform the DNAS check
	// (Note, we must be running off a CD, as the check is only performed on CD burns)										  
	// If we actually BOOT the cd on the dev system, it will think it's
	// on a regular PS2, and the DNAS test will be performed
	if 	( Config::CD() && Config::GetHardware() == Config::HARDWARE_PS2_DEVSYSTEM ) 
	{
		Script::RunScript( CRCD(0xbd54c02b,"connected_to_internet") );
		s_extra_time = 0;
		return true;
	}
#ifdef __DNAS_DEBUG__	
	scePrintf( "Available Memory: %d\n", Mem::Manager::sHandle().Available());
#endif

	// Worst case, if we're out of memory because of leaks or fragmentation, just act as if
	// DNAS authentication took place and verified the authenticity of the user
	if( Mem::Manager::sHandle().Available() < (int) ( 1024 * 1024 * 1.7 ))
	{
#ifdef __DNAS_DEBUG__
		scePrintf( "Could not allocate overlay buffer. Available mem: %d. Skipping test\n", Mem::Manager::sHandle().Available());
#endif
		Dbg_MsgAssert( 0, ( "Could not allocate enough safe memory for DNAS. Tell Steve\n" ));
		Script::RunScript( CRCD(0xbd54c02b,"connected_to_internet") );
		s_extra_time = 0;
		return true;
	}

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	memory = new char[(int) ( 1024 * 1024 * 1.7 )];
	Mem::Manager::sHandle().PopContext();

#ifdef __DNAS_DEBUG__
	scePrintf( "Memory is at : 0x%x\n", memory );
	scePrintf( "DNAS Start: 0x%x  DNAS Size: %d\n", (int) _dnas_start, (int) _dnas_size );
#endif
	
	if(	((uint32) _dnas_start + (uint32) _dnas_size ) > (uint32) ( memory + (int) ( 1024 * 1024 * 1.7 )) ||
		((uint32) _dnas_start < (uint32) memory ))
	{
		// Worst case, if we're out of memory because of leaks or fragmentation, just act as if
		// DNAS authentication took place and verified the authenticity of the user

		delete [] memory;
#ifdef __DNAS_DEBUG__
		scePrintf( "DNAS didn't fit in the memory that was allocated\n" );
#endif
		Dbg_MsgAssert( 0, ( "Could not allocate enough safe memory for DNAS. Tell Steve\n" ));
		Script::RunScript( CRCD(0xbd54c02b,"connected_to_internet") );
		s_extra_time = 0;
		return true;
	}
		 
	// Clear out the destination memory
	memset( _dnas_start, 0, (int) _dnas_size );

	p_file = NULL;
	if( Config::gLanguage == Config::LANGUAGE_ENGLISH )
	{
		if( Config::gDisplayType == Config::DISPLAY_TYPE_NTSC )
		{
			p_file = File::Open( "dnas/dnas.bin", "rb" );
		}
		else
		{
			p_file = File::Open( "dnas/dnas_e.bin", "rb" );
		}
	}
	else
	{
		p_file = File::Open( "dnas/dnas_e.bin", "rb" );
	}

#ifdef __DNAS_DEBUG__
	if( p_file == NULL )
	{
		scePrintf( "Failed to open dnas/dnas(_e).bin" );
	}
#endif

	File::Read( _dnas_start, 1, (int) _dnas_size, p_file );
	File::Close( p_file );

#ifndef __PURE_HEAP__
	Dbg_MsgAssert( !Config::CD(), ( "DNAS will not work with memory debugging enabled." ));
#endif

#ifdef __USE_DNAS_DLL__
	char* rel_mem, *buff;
	void *p_file = File::Open( "dnas/dnas.rel", "rb" );
	File::Seek( p_file, 0, SEEK_END );
	int32 size = File::Tell( p_file );
	Dbg_Printf( "***************** FILE SIZE IS %d\n", size );
	File::Seek( p_file, 0, SEEK_SET );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	rel_mem = new char[size + 127];
	Mem::Manager::sHandle().PopContext();

	buff = (char*) (((int) rel_mem + 127 ) & ~127 ); // Align to 128-byte boundary

	File::Read( buff, sizeof( char ), size, p_file );
	File::Close( p_file );

	if( snDllLoaded( buff, 0 ))
	{
		Dbg_MsgAssert( 0, ( "Failed to load dnas DLL\n" ));
		return false;
	}
#endif

	if( !Config::CD())
	{
		sceCdInit(SCECdINIT);
		sceCdMmode(SCECdDVD);
		sceCdDiskReady( 0 );
	}

	memset( &status, 0, sizeof( sceDNAS2Status_t )); 
	result = netstart_dnas( 0, NULL, 0, vAUTH_TIMEOUT + s_extra_time, status );
	Dbg_Printf( "dnas result was : %d\n", result );
	//install_dnas( 0, NULL, 0, 0 );

	delete [] memory;

	if( result != 0 )
	{
		Script::CStruct* pParams;
		uint32 desc;
		uint32 footer;
		const char* err_desc;
		const char* err_footer;

		s_extra_time += 10;

		pParams = new Script::CStruct;
		if( status.code == 0 )
		{
			pParams->AddInteger( CRCD(0x88eacf67,"code"), 0 );
			pParams->AddInteger( CRCD(0xaf7c50a6,"sub_code"), 0 );
			desc = CRCD(0xc7c67eb8,"net_auth_error_generic");
			if( Config::PAL())
			{
				footer = CRCD(0xc13153cc,"net_auth_footer_customer_pal");
			}
			else
			{
				
				footer = CRCD(0x16ff6a40,"net_auth_footer_contact");
			}
		}
		else
		{
			Dbg_Printf( " Code: %d  SubCode: %d\n", status.code, status.sub_code );
			pParams->AddInteger( CRCD(0x88eacf67,"code"), status.code );
			pParams->AddInteger( CRCD(0xaf7c50a6,"sub_code"), status.sub_code );
			desc = s_get_error_desc( status.sub_code );
			if( Config::PAL())
			{
				footer = s_get_error_footer_pal( status.sub_code );
			}
			else
			{
				footer = s_get_error_footer( status.sub_code );
			}
		}
		
		err_desc = Script::GetString( desc, Script::ASSERT );
		err_footer = Script::GetString( footer, Script::ASSERT );
		Dbg_Printf( "ERROR DESC: %s\n", err_desc );
		Dbg_Printf( "ERROR FOOTER: %s\n", err_footer );
		pParams->AddString( CRCD(0xf44a53ab,"desc"), err_desc );
		pParams->AddString( CRCD(0x1dcefaac,"footer"), err_footer );
		Script::RunScript( CRCD(0xc3bb2b5a,"display_auth_error"), pParams );

		delete pParams;
		return false;
	}

	Dbg_Printf( "Success: Showing disclaimer\n" );
	Script::RunScript( CRCD(0xbd54c02b,"connected_to_internet") );
	s_extra_time = 0;
	return true;
}

bool GameNet::Manager::ScriptWriteDNASBinary( Script::CStruct* pParams, Script::CScript* pScript )
{
	void *p_file;
	static bool written = false;

	if( Config::CD())
	{
		written = true;
	}
	
	if( !written ) 
	{
		if( Config::gLanguage == Config::LANGUAGE_ENGLISH )
		{
			if( Config::gDisplayType == Config::DISPLAY_TYPE_NTSC )
			{
				p_file = File::Open( "dnas/dnas.bin", "wb" );
			}
			else
			{
				p_file = File::Open( "dnas/dnas_e.bin", "wb" );
			}
		}
		else
		{
			p_file = File::Open( "dnas/dnas_e.bin", "wb" );
		}
		File::Write( _dnas_start, 1, (int) _dnas_size, p_file );
		File::Flush( p_file );
		File::Close( p_file );

		written = true;
	}

	return true;
}




================================================
FILE: Code/Sk/GameNet/Ngps/p_buddy.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_buddy.cpp												**
**																			**
**	Created by:		02/28/02	-	SPG										**
**																			**
**	Description:	PS2 Buddy List Code 									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace GameNet
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define vBUDDY_CONNECT_TIMEOUT Tmr::Seconds( 10 )

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static 	Tmr::Time	s_connect_start_time = 0;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

void BuddyMan::s_error_callback( GPConnection* connection, void* arg, void* param )
{
    GPErrorArg* error;
	BuddyMan* buddy_man;

	error = (GPErrorArg*) arg;
	buddy_man = (BuddyMan*) param;
	
	Dbg_Printf( "******** ERROR: %d %s\n", error->errorCode, error->errorString );

	if( error->fatal )
	{
		Dbg_Printf( "******** ERROR IS FATAL\n" );
	}
	
	switch( error->errorCode )
	{
		case GP_NEWUSER_BAD_PASSWORD:
			Dbg_Printf( "Bad password\n" );
			Script::RunScript( "create_wrong_profile_password_dialog" );
			buddy_man->Disconnect();
			break;
		case GP_NEWUSER_BAD_NICK:
			Dbg_Printf( "Bad nick (already exists?) Trying to connect now\n" );
			buddy_man->m_state = vSTATE_BEGIN_LOGIN;
			break;
		case GP_ADDBUDDY_ALREADY_BUDDY:
			Script::RunScript( "failed_add_buddy_already_buddy" );
			break;
		case GP_LOGIN_CONNECTION_FAILED:
			buddy_man->Disconnect();
			Script::RunScript( "CreateBuddyLoginFailedDialog" );
			break;
		default:
			Dbg_Printf( "Not handled: %d\n", error->errorCode );
			break;
		
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void BuddyMan::s_buddy_request( GPConnection* connection, void* arg, void* param )
{
	BuddyMan* buddy_man;
	GPRecvBuddyRequestArg* request;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	
	buddy_man = (BuddyMan*) param;
	request = (GPRecvBuddyRequestArg*) arg;
	
	// Always accept
	gpAuthBuddyRequest( &buddy_man->m_gp_conn, request->profile );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void BuddyMan::s_buddy_status( GPConnection* connection, void* arg, void* param )
{
	BuddyMan* buddy_man;
	GPRecvBuddyStatusArg* status_arg;
	GPBuddyStatus status;
	Lst::Search< BuddyInfo > sh;
	BuddyInfo* buddy;
	char* join_info;
	char* location_str;
	bool handled;
	int i;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	buddy_man = (BuddyMan*) param;
	status_arg = (GPRecvBuddyStatusArg*) arg;
    
	gpGetBuddyStatus( &buddy_man->m_gp_conn, status_arg->index, &status );

	//Dbg_Printf( "Got buddy status %d for %d : %s, %s\n", status.status, status.profile, status.statusString, status.locationString );

	handled = false;
	for( buddy = sh.FirstItem( buddy_man->m_buddies ); buddy; buddy = sh.NextItem())
	{
		if( buddy->m_Profile == status.profile )
		{
			Script::CStruct* p_status_params;

			buddy->m_Status = status.status;
			
			strcpy( buddy->m_StatusString, status.statusString );
			if( buddy->m_Status == GP_PLAYING )
			{
				location_str = strchr( status.locationString, ':' );
				location_str += 1;
				location_str = strchr( location_str, ':' );
				location_str += 1;
				location_str = strchr( location_str, ':' );
				location_str += 1;
	
				strcpy( buddy->m_Location, location_str );
				join_info = strtok( status.locationString, ":" );
				Dbg_Assert( join_info != NULL );
				buddy->m_JoinIP = atoi( join_info );
				join_info = strtok( NULL, ":" );
				Dbg_Assert( join_info != NULL );
				buddy->m_JoinPrivateIP = atoi( join_info );
				join_info = strtok( NULL, ":" );
				Dbg_Assert( join_info != NULL );
				buddy->m_JoinPort = atoi( join_info );
			}
			else
			{
				strcpy( buddy->m_Location, status.locationString );
			}

			if( buddy->m_Pending )
			{
				// This was a pending addition.  Add him to our permanent list
				GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
				Prefs::Preferences*	prefs;
				Script::CArray* buddy_array, *new_array;
				Script::CStruct* new_buddy;
				Script::CStruct* params;
                      
				params = new Script::CStruct;	
				params->AddString( "net_name", buddy->m_Nick );

				Script::RunScript( "added_buddy", params );
				delete params;

				buddy->m_Pending = false;

				prefs = gamenet_man->GetNetworkPreferences();
				buddy_array = prefs->GetPreferenceArray( Script::GenerateCRC( "buddy_array" ));

				new_array = new Script::CArray;
				new_array->SetSizeAndType( buddy_array->GetSize() + 1, ESYMBOLTYPE_STRUCTURE );

				for( i = 0; i < (int) buddy_array->GetSize(); i++ )
				{
					Script::CStruct* entry, *new_entry;

					entry = buddy_array->GetStructure( i );
					new_entry = new Script::CStruct;
					*new_entry = *entry;
					new_array->SetStructure( i, new_entry );
				}

				new_buddy = new Script::CStruct;
				new_buddy->AddInteger( "profile", buddy->m_Profile );
				new_buddy->AddString( "nick", buddy->m_Nick );
				new_array->SetStructure( i, new_buddy );
				
				prefs->SetPreference( Script::GenerateCRC( "buddy_array" ), new_array );
			}

			p_status_params = new Script::CScriptStructure;
			p_status_params->AddComponent( Script::GenerateCRC( "name" ), ESYMBOLTYPE_STRING, buddy->m_Nick );
			p_status_params->AddComponent( Script::GenerateCRC( "status" ), ESYMBOLTYPE_STRING, buddy->m_StatusString );
			p_status_params->AddComponent( Script::GenerateCRC( "location" ), ESYMBOLTYPE_STRING, buddy->m_Location );
			p_status_params->AddComponent( Script::GenerateCRC( "id" ), ESYMBOLTYPE_NAME, Script::GenerateCRC( "first_lobby_buddy" ) + buddy->m_Profile );
			Script::RunScript( "update_buddy_status", p_status_params );
			delete p_status_params;

			handled = true;
			break;
		}
	}
	
	if( !handled )
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		Prefs::Preferences*	prefs;
		Script::CArray* buddy_array;
		int profile;

		prefs = gamenet_man->GetNetworkPreferences();

		buddy = new BuddyInfo;
		buddy->m_Profile = status.profile;
		buddy->m_Status = status.status;
		strcpy( buddy->m_StatusString, status.statusString );
		sprintf( buddy->m_Nick, "ProSkater" );

		if( buddy->m_Status == GP_PLAYING )
		{
			location_str = strchr( status.locationString, ':' );
			location_str += 1;
			location_str = strchr( location_str, ':' );
			location_str += 1;
			location_str = strchr( location_str, ':' );
			location_str += 1;
	
			strcpy( buddy->m_Location, location_str );
			join_info = strtok( status.locationString, ":" );
			Dbg_Assert( join_info );
			buddy->m_JoinIP = atoi( join_info );
			join_info = strtok( NULL, ":" );
			Dbg_Assert( join_info );
			buddy->m_JoinPrivateIP = atoi( join_info );
			join_info = strtok( NULL, ":" );
			Dbg_Assert( join_info );
			buddy->m_JoinPort = atoi( join_info );
		}
		else
		{
			strcpy( buddy->m_Location, status.locationString );
		}

		buddy_man->m_buddies.AddToTail( buddy );

		// We now need to find the name by which we know this buddy.  When we added them to our list
		// we added an entry into our network preferences with a matching profile ID and the player's
		// network name
		buddy_array = prefs->GetPreferenceArray( Script::GenerateCRC( "buddy_array" ));
		if( buddy_array )
		{
			Script::CStruct* entry;
			for( i = 0; i < (int) buddy_array->GetSize(); i++ )
			{
				entry = buddy_array->GetStructure( i );
				entry->GetInteger( "profile", &profile, Script::ASSERT );
				if( profile == status.profile )
				{
					const char* nick = NULL;
					entry->GetString( "nick", &nick, Script::ASSERT );

					strcpy( buddy->m_Nick, nick );
				}
			}
		}
	}

    Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void BuddyMan::s_buddy_message( GPConnection* connection, void* arg, void* param )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void BuddyMan::s_game_invite( GPConnection* connection, void* arg, void* param )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void BuddyMan::s_transfer_callback( GPConnection* connection, void* arg, void* param )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void BuddyMan::s_connect_callback( GPConnection* connection, void* arg, void* param )
{
	BuddyMan* buddy_man;
    GPConnectResponseArg* response;

	buddy_man = (BuddyMan*) param;
	response = (GPConnectResponseArg*) arg;
	switch( response->result )
	{
		case GP_NO_ERROR:
			//Dbg_Printf( "*************** CONNECTED, NO ERROR\n" );
			buddy_man->Connected( response->profile );
			break;
		case GP_MEMORY_ERROR:
			//Dbg_Printf( "*************** DIDNT CONNECT: MEMORY ERROR\n" );
			break;
		case GP_PARAMETER_ERROR:
			//Dbg_Printf( "*************** DIDNT CONNECT: PARAMETER ERROR\n" );
			break;
		case GP_NETWORK_ERROR:
			//Dbg_Printf( "*************** DIDNT CONNECT: NETWORK ERROR\n" );
			break;
		case GP_SERVER_ERROR:
			//Dbg_Printf( "*************** DIDNT CONNECT: SERVER ERROR\n" );
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::s_buddy_logic_code( const Tsk::Task< BuddyMan >& task )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	BuddyMan& man = task.GetData();
	static Tmr::Time s_last_process_time = 0;

	switch( man.m_state )
	{
		case vSTATE_WAIT:
			break;
		case vSTATE_BEGIN_LOGIN:
			man.Connect();
			break;
		case vSTATE_CREATING_PROFILE:
		case vSTATE_CONNECTING:
			if(( Tmr::GetTime() - s_connect_start_time ) > vBUDDY_CONNECT_TIMEOUT )
			{
				//Dbg_Printf( "Timing buddy server connection out\n" );
				man.Disconnect();
				Script::RunScript( "CreateBuddyLoginFailedDialog" );
			}
			else
			{
				gpProcess( &man.m_gp_conn );
			}
			break;
		case vSTATE_LOGGED_IN:
			if(( Tmr::GetTime() - s_last_process_time ) > Tmr::Seconds( 1 ))
			{
				gpProcess( &man.m_gp_conn );
				s_last_process_time = Tmr::GetTime();
			}
			break;
		default:
			Dbg_Assert( 0 );
			break;

	}

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	BuddyMan::s_threaded_buddy_connect( BuddyMan* buddy_man )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Prefs::Preferences*	prefs;
	GPResult result;
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	// Register this thread with the sockets API
	sockAPIregthr();

	prefs = gamenet_man->GetNetworkPreferences();
	const char* email = prefs->GetPreferenceString( Script::GenerateCRC( "profile_email" ), Script::GenerateCRC("ui_string") );
	const char* password = prefs->GetPreferenceString( Script::GenerateCRC( "profile_password" ), Script::GenerateCRC("ui_string") );

	Dbg_Printf( "Connecting to gp with %s : %s\n", email, password );
	result = gpConnect( &buddy_man->m_gp_conn, "proskater", email, password, GP_FIREWALL, GP_NON_BLOCKING, s_connect_callback, buddy_man );

	// Deregister this thread with the sockets API
	sockAPIderegthr();

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	BuddyMan::s_threaded_buddy_create( BuddyMan* buddy_man )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Prefs::Preferences*	prefs;
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	// Register this thread with the sockets API
	sockAPIregthr();

	prefs = gamenet_man->GetNetworkPreferences();
	const char* email = prefs->GetPreferenceString( Script::GenerateCRC( "profile_email" ), Script::GenerateCRC("ui_string") );
	const char* password = prefs->GetPreferenceString( Script::GenerateCRC( "profile_password" ), Script::GenerateCRC("ui_string") );

	//Dbg_Printf( "************ Creating profile: Proskater for account %s, password %s\n", email, password );
	gpConnectNewUser( &buddy_man->m_gp_conn, "proskater", email, password, GP_FIREWALL, GP_NON_BLOCKING, s_connect_callback, buddy_man );

	/*if( buddy_man->m_command == vCOMMAND_LOGIN )
	{
		//Dbg_Printf( "Connecting instead\n" );
		Script::RunScript( "log_in_profile" );
		buddy_man->m_command = vCOMMAND_CONNECT;
		s_connect_start_time = Tmr::GetTime();
		result = gpConnect( &buddy_man->m_gp_conn, "proskater", email, password, GP_FIREWALL, GP_NON_BLOCKING, s_connect_callback, buddy_man );
	}*/
	//Dbg_Printf( "called gpconnect\n" );

	// Deregister this thread with the sockets API
	sockAPIderegthr();

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		BuddyMan::add_player_to_menu( BuddyInfo* buddy, bool allow_join, bool allow_remove )
{
	Script::CStruct* p_item_params;
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	p_item_params = new Script::CStruct;	
	char upper_nick[1024];

	//Dbg_Printf( "****** add_player_to_menu. Allow Join %d\n", (int) allow_join );

	sprintf( upper_nick, buddy->m_Nick );

	p_item_params->AddString( "name", buddy->m_Nick );
	p_item_params->AddString( "status", buddy->m_StatusString );
	p_item_params->AddString( "location", buddy->m_Location );
	p_item_params->AddChecksum( "id", Script::GenerateCRC( "first_lobby_buddy" ) + buddy->m_Profile );
	if( allow_join || allow_remove )
	{
		// create the parameters that are passed to the X script
		Script::CStruct *p_script_params= new Script::CStruct;
		p_script_params->AddInteger( "profile", buddy->m_Profile );	
		p_script_params->AddString( "nick", strupr( upper_nick ));	
		p_script_params->AddString( "status", buddy->m_StatusString );	
		p_script_params->AddString( "location", buddy->m_Location );
		p_script_params->AddInteger( "join_ip", buddy->m_JoinIP );	
		p_script_params->AddInteger( "join_private_ip", buddy->m_JoinPrivateIP );	
		p_script_params->AddInteger( "join_port", buddy->m_JoinPort );	

		if( allow_remove )
		{
			p_script_params->AddChecksum( NONAME, Script::GenerateCRC( "allow_remove" ));	
		}

		if( allow_join && ( buddy->m_Status == GP_PLAYING ))
		{
			//Dbg_Printf( "****** GP_PLAYING\n" );
			p_script_params->AddChecksum( NONAME, Script::GenerateCRC( "allow_join" ));	
		}

		if( allow_join )
		{
			p_script_params->AddChecksum( NONAME, Script::GenerateCRC( "in_lobby" ));	
		}
		else
		{
			p_script_params->AddChecksum( "back_script", Script::GenerateCRC( "back_from_shell_buddy_options" ));   
			p_script_params->AddChecksum( "remove_script", Script::GenerateCRC( "shell_remove_buddy" ));   
		}
		p_item_params->AddStructure( "pad_choose_params", p_script_params );
		p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("present_buddy_options"));
	}
	else
	{
		p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("do_nothing"));
	}
	
	p_item_params->AddChecksum( "parent_menu_id", Script::GenerateCRC( "lobby_buddy_list_menu" ));
	if( allow_join )
	{
		p_item_params->AddFloat( "scale", 1.1f );
	}                                            

	//Script::RunScript("make_text_sub_menu_item",p_item_params);
	Script::RunScript("homie_list_add_item",p_item_params);
	delete p_item_params;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		BuddyMan::fill_buddy_list( bool clear_list, bool allow_join, bool allow_remove )
{
	Lst::Search< BuddyInfo > sh;
	BuddyInfo* buddy;

	if( clear_list )
	{
		//Dbg_Printf( "Clearing buddy list menu\n" );
		Script::RunScript( "destroy_lobby_buddy_list_children" );
	}

	for( buddy = sh.FirstItem( m_buddies ); buddy; buddy = sh.NextItem())
	{
		if( buddy->m_Pending )
		{
			continue;
		}
		add_player_to_menu( buddy, allow_join, allow_remove );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		BuddyMan::fill_prospective_buddy_list( void )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* player;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		if( player->IsLocalPlayer())
		{
			continue;
		}
		if( player->m_Profile && !IsAlreadyMyBuddy( player->m_Profile ))
		{
			Script::CStruct* p_item_params;
	
			//Dbg_Printf( "Adding %s of %d to the list\n", player->m_Name, player->m_Profile );
			p_item_params = new Script::CStruct;	
			p_item_params->AddString( "text", player->m_Name );
			p_item_params->AddChecksum( "id", player->m_Profile );
			p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("add_buddy"));
			p_item_params->AddChecksum( "parent_menu_id", Script::GenerateCRC( "lobby_buddy_list_menu" ));
		
			// create the parameters that are passed to the X script
			Script::CStruct *p_script_params= new Script::CStruct;
			p_script_params->AddInteger( "profile", player->m_Profile );	
			p_script_params->AddString( "nick", player->m_Name );	
			p_item_params->AddStructure( "pad_choose_params", p_script_params );
			p_item_params->AddFloat( "scale", 1.1f );

			Script::RunScript("make_text_sub_menu_item",p_item_params);

			delete p_item_params;
			delete p_script_params;
		}
	}

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::add_buddy( char *name, int profile )
{
	GPResult result;
	BuddyInfo* new_buddy;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	new_buddy = new BuddyInfo;
	strcpy( new_buddy->m_Nick, name );
	new_buddy->m_Profile = profile;
	new_buddy->m_Status = 0;
	new_buddy->m_Pending = true;

	m_buddies.AddToTail( new_buddy );
    
	result = gpSendBuddyRequest( &m_gp_conn, profile, "" );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::cancel_add_buddy( void )
{
	BuddyInfo* buddy;
	Lst::Search< BuddyInfo > sh;

	for( buddy = sh.FirstItem( m_buddies ); buddy; buddy = sh.NextItem())
	{
		if( buddy->m_Pending )
		{
			delete buddy;
			return;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::remove_buddy( int profile )
{
	BuddyInfo* buddy;
	Lst::Search< BuddyInfo > sh;
	Script::CStruct* p_struct;

	for( buddy = sh.FirstItem( m_buddies ); buddy; buddy = sh.NextItem())
	{
		if( buddy->m_Profile == profile )
		{
			delete buddy;
			break;
		}
	}

	gpDeleteBuddy( &m_gp_conn, profile );
                      
	p_struct = new Script::CStruct;
	p_struct->AddChecksum( "id", Script::GenerateCRC( "first_lobby_buddy" ) + profile );

	Script::RunScript( "removed_buddy", p_struct );
	delete p_struct;
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

BuddyMan::BuddyMan( void )
{
	GPResult result;
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());
	
	result = gpInitialize( &m_gp_conn, vGAMESPY_PRODUCT_ID );
	Dbg_Assert( result == GP_NO_ERROR );

	result = gpSetCallback( &m_gp_conn, GP_ERROR, s_error_callback, this );
	Dbg_Assert( result == GP_NO_ERROR );

	result = gpSetCallback( &m_gp_conn, GP_RECV_BUDDY_REQUEST, s_buddy_request, this );
	Dbg_Assert( result == GP_NO_ERROR );

	result = gpSetCallback( &m_gp_conn, GP_RECV_BUDDY_STATUS, s_buddy_status, this );
	Dbg_Assert( result == GP_NO_ERROR );

	result = gpSetCallback( &m_gp_conn, GP_RECV_BUDDY_MESSAGE, s_buddy_message, this );
	Dbg_Assert( result == GP_NO_ERROR );

	result = gpSetCallback( &m_gp_conn, GP_RECV_GAME_INVITE, s_game_invite, this );
	Dbg_Assert( result == GP_NO_ERROR );

	result = gpSetCallback( &m_gp_conn, GP_TRANSFER_CALLBACK, s_transfer_callback, this );
	Dbg_Assert( result == GP_NO_ERROR );

	m_buddy_logic_task = new Tsk::Task< BuddyMan > ( s_buddy_logic_code, *this );

	m_logged_in = false;
	m_state = vSTATE_WAIT;
	m_profile = 0;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

BuddyMan::~BuddyMan( void )
{
	delete m_buddy_logic_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::Connected( GPProfile profile )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	m_logged_in = true;
	m_profile = profile;
	m_state = vSTATE_LOGGED_IN;

	//Dbg_Printf( "******************** PROFILE IS %d\n", m_profile );
	Script::RunScript( "profile_logged_in" );
    gpSetStatus( &m_gp_conn, GP_ONLINE, (char*) Script::GetString( "homie_status_online" ), 
										(char*) Script::GetString( "homie_status_logging_in" ));

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::IsLoggedIn( void )
{
	return m_logged_in;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::Connect( void )
{   
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	Thread::PerThreadStruct	net_thread_data;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
	
	m_state = vSTATE_CONNECTING;
	s_connect_start_time = Tmr::GetTime();

	Script::RunScript( "log_in_profile" );

    net_thread_data.m_pEntry = s_threaded_buddy_connect;
	net_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
	net_thread_data.m_pStackBase = gamenet_man->GetNetThreadStack();
	net_thread_data.m_iStackSize = vNET_THREAD_STACK_SIZE;
	net_thread_data.m_utid = 0x152;
	Thread::CreateThread( &net_thread_data );
	gamenet_man->SetNetThreadId( net_thread_data.m_osId );
	StartThread( gamenet_man->GetNetThreadId(), this );

	if( !m_buddy_logic_task->InList())
	{
		mlp_man->AddLogicTask( *m_buddy_logic_task );
	}
	
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::CreateProfile( void )
{   
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	Thread::PerThreadStruct	net_thread_data;

	m_state = vSTATE_CREATING_PROFILE;
	s_connect_start_time = Tmr::GetTime();

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	
	net_thread_data.m_pEntry = s_threaded_buddy_create;
	net_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
	net_thread_data.m_pStackBase = gamenet_man->GetNetThreadStack();
	net_thread_data.m_iStackSize = vNET_THREAD_STACK_SIZE;
	net_thread_data.m_utid = 0x152;
	Thread::CreateThread( &net_thread_data );
	gamenet_man->SetNetThreadId( net_thread_data.m_osId );
	StartThread( gamenet_man->GetNetThreadId(), this );

	mlp_man->AddLogicTask( *m_buddy_logic_task );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::Disconnect( void )
{   
	BuddyInfo* buddy, *next;
	Lst::Search< BuddyInfo > sh;
	
	//Dbg_Printf( "***** Disconnecting from presence system\n" );
	gpDisconnect( &m_gp_conn );
	m_buddy_logic_task->Remove();
	m_logged_in = false;

	/*SetStatusAndLocation( GP_OFFLINE, 	(char*) Script::GetString( "homie_status_offline" ), 
										"" );*/
	for( buddy = sh.FirstItem( m_buddies ); buddy; buddy = next )
	{
		next = sh.NextItem();
		delete buddy;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		BuddyMan::GetProfile( void )
{
	return m_profile;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::IsAlreadyMyBuddy( int profile )
{
	BuddyInfo* buddy;
	Lst::Search< BuddyInfo > sh;
	
	for( buddy = sh.FirstItem( m_buddies ); buddy; buddy = sh.NextItem())
	{
		if( buddy->m_Profile == profile )
		{
			return true;
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		BuddyMan::NumBuddies( void )
{
	return m_buddies.CountItems();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::SetStatusAndLocation( int status, char* status_string, char* location )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	//Dbg_Printf( "Setting status to %d : %s , %s\n", status, status_string, location );
	gpSetStatus( &m_gp_conn, (GPEnum) status, status_string, location );
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptSetUniqueId(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Script::CStruct* new_params;
	Prefs::Preferences*	prefs;
	Tmr::Time time;
	int ms_time;
	char str_time[32];

	time = Tmr::GetTime();
	ms_time = (int) time;

	//Dbg_Printf( "*************** (Getting Time) Setting unique id to %d\n", ms_time );
	
	prefs = gamenet_man->GetNetworkPreferences();
	new_params = new Script::CStruct;
	sprintf( str_time, "%d", ms_time );
	new_params->AddString( "ui_string", str_time );
	prefs->SetPreference( Script::GenerateCRC( "unique_id" ), new_params );
	delete new_params;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptProfileLoggedIn(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	BuddyMan* buddy_man;

	buddy_man = gamenet_man->mpBuddyMan;
	return buddy_man->IsLoggedIn();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptProfileLogIn(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Prefs::Preferences*	prefs;
	
	prefs = gamenet_man->GetNetworkPreferences();
	uint32 success = prefs->GetPreferenceChecksum( Script::GenerateCRC( "profile_success" ), Script::GenerateCRC( "checksum" ));
	if( success != Script::GenerateCRC( "boolean_true" ))
	{
		return false;
	}

	gamenet_man->mpBuddyMan->Connect();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptProfileLogOff(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	BuddyMan* buddy_man;

	buddy_man = gamenet_man->mpBuddyMan;
	buddy_man->Disconnect();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptCreateProfile(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	
	gamenet_man->mpBuddyMan->CreateProfile();
    
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptFillBuddyList(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	bool clear_list, allow_join, allow_remove;

	clear_list = pParams->ContainsFlag( "clear_list" );
	allow_join = pParams->ContainsFlag( "allow_join" );
	allow_remove = pParams->ContainsFlag( "allow_remove" );

	//Dbg_Printf( "****** ScripFillBuddyList. Allow Join %d\n", (int) allow_join );
	gamenet_man->mpBuddyMan->fill_buddy_list( clear_list, allow_join, allow_remove );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptFillProspectiveBuddyList(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	
	gamenet_man->mpBuddyMan->fill_prospective_buddy_list();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptAddBuddy(Script::CStruct *pParams, Script::CScript *pScript)
{   
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	int profile;
	const char* nick;

	pParams->GetInteger( "profile", &profile, Script::ASSERT );
	pParams->GetString( "nick", &nick, Script::ASSERT );

	gamenet_man->mpBuddyMan->add_buddy((char *) nick, profile );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptCancelAddBuddy(Script::CStruct *pParams, Script::CScript *pScript)
{                                                               
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
                                                                
	gamenet_man->mpBuddyMan->cancel_add_buddy();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptRemoveBuddy(Script::CStruct *pParams, Script::CScript *pScript)
{
	int profile;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
                                                                
	pParams->GetInteger( "profile", &profile, Script::ASSERT );
	gamenet_man->mpBuddyMan->remove_buddy( profile );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptJoinBuddy(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	char location[1024];
	int join_ip, join_private_ip, join_port;
	const char* location_str;
	bool observing, local;
	Script::CStruct* p_struct;

	observing = pParams->ContainsFlag( "observe" );
	pParams->GetString( "location", &location_str, Script::ASSERT );
	pParams->GetInteger( "join_ip", &join_ip, Script::ASSERT );
	pParams->GetInteger( "join_private_ip", &join_private_ip, Script::ASSERT );
	pParams->GetInteger( "join_port", &join_port, Script::ASSERT );
	sprintf( location, "%d:%d:%d:%s", join_ip, join_private_ip, join_port, location_str );

	local = false;
	if((unsigned int) join_ip == peerGetLocalIP( gamenet_man->mpLobbyMan->GetPeer()))
	{
		join_ip = join_private_ip;
		join_port = vHOST_PORT;
		local = true;
	}
	
	if( observing )
	{
		gamenet_man->SetJoinMode( vJOIN_MODE_OBSERVE );
		gamenet_man->mpBuddyMan->SetStatusAndLocation( GP_PLAYING, (char*) Script::GetString( "homie_status_observing" ), location );
	}
	else
	{
		gamenet_man->SetJoinMode( vJOIN_MODE_PLAY );
		gamenet_man->mpBuddyMan->SetStatusAndLocation( GP_PLAYING, (char*) Script::GetString( "homie_status_playing" ), location );
	}

	char *ip_addr;
	struct in_addr addr;
	uint32 cookie;
		
	p_struct = new Script::CStruct;

	addr.s_addr = join_ip;
	ip_addr = inet_ntoa( addr );
	p_struct->AddComponent( NONAME, ESYMBOLTYPE_INTEGER, join_port );
	p_struct->AddComponent( NONAME, ESYMBOLTYPE_STRING, ip_addr );
		
	if( !local )
	{
		cookie = Mth::Rnd( 65535 );
		p_struct->AddComponent( CRCD( 0x751f4599, "cookie" ), ESYMBOLTYPE_NAME, cookie );
	}
		
	Script::RunScript( "net_chosen_join_server", p_struct );
	delete p_struct;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptHasBuddies(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	return ( gamenet_man->mpBuddyMan->NumBuddies() > 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptBuddyListFull(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	return ( gamenet_man->mpBuddyMan->NumBuddies() >= vMAX_BUDDIES );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	BuddyMan::ScriptSetLobbyStatus(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	Dbg_Assert( gamenet_man->mpBuddyMan->IsLoggedIn());
	gamenet_man->mpBuddyMan->SetStatusAndLocation( GP_CHATTING, (char*) Script::GetString( "homie_status_chatting" ), 
																gamenet_man->mpLobbyMan->GetLobbyName());

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

BuddyInfo::BuddyInfo( void )
: Lst::Node< BuddyInfo > ( this )
{
	m_Pending = false;
	m_Status = 0;
	m_Location[0] = '\0';
	sprintf( m_StatusString, "Offline" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet






================================================
FILE: Code/Sk/GameNet/Ngps/p_buddy.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_buddy.h												**
**																			**
**	Created by:		02/28/02	-	SPG										**
**																			**
**	Description:	PS2 Buddy List Code 									**
**																			**
*****************************************************************************/

#ifndef __GAMENET_NGPS_P_BUDDY_H
#define __GAMENET_NGPS_P_BUDDY_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace GameNet
{

enum
{
	vCOMMAND_NONE,
	vCOMMAND_CONNECT,
	vCOMMAND_LOGIN
};

enum
{
	vSTATE_WAIT,
	vSTATE_CREATING_PROFILE,
	vSTATE_CONNECTING,
	vSTATE_BEGIN_LOGIN,
	vSTATE_LOGGED_IN
};

enum
{
	vMAX_BUDDIES = 30
};

class BuddyInfo : public Lst::Node< BuddyInfo >
{
public:
	BuddyInfo( void );

	char	m_Nick[GP_NICK_LEN];
	int		m_Profile;
	int		m_Status;
	bool	m_Pending;
	int		m_JoinIP;
	int		m_JoinPrivateIP;
	int		m_JoinPort;
	char	m_Location[GP_LOCATION_STRING_LEN];
	char	m_StatusString[GP_STATUS_STRING_LEN];
};

class BuddyMan
{
public:
	BuddyMan( void );
	~BuddyMan( void );

	void	Connect( void );
	void	CreateProfile( void );
	void	Disconnect( void );
	void	Connected( GPProfile profile );
	bool	IsLoggedIn( void );
	bool	IsAlreadyMyBuddy( int profile );
	int		NumBuddies( void );

	int		GetProfile( void );
	void	SetStatusAndLocation( int status, char* status_string, char* location );

	static	bool		ScriptSetUniqueId(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptProfileLoggedIn(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptCreateProfile(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptProfileLogOff(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptProfileLogIn(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptFillBuddyList(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptFillProspectiveBuddyList(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptAddBuddy(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptCancelAddBuddy(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptRemoveBuddy(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptJoinBuddy(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptHasBuddies(Script::CStruct *pParams, Script::CScript *pScript);
	static 	bool		ScriptBuddyListFull(Script::CStruct *pParams, Script::CScript *pScript);
	static 	bool		ScriptSetLobbyStatus(Script::CStruct *pParams, Script::CScript *pScript);
    
private:
	GPConnection	m_gp_conn;
	GPProfile		m_profile;
	int				m_state;

	static	void s_connect_callback( GPConnection* connection, void* arg, void* param );

	static	void s_error_callback( GPConnection* connection, void* arg, void* param );
	static 	void s_buddy_request( GPConnection* connection, void* arg, void* param );
	static	void s_buddy_status( GPConnection* connection, void* arg, void* param );
	static	void s_buddy_message( GPConnection* connection, void* arg, void* param );
	static	void s_game_invite( GPConnection* connection, void* arg, void* param );
	static	void s_transfer_callback( GPConnection* connection, void* arg, void* param );

	static	void s_threaded_buddy_connect( BuddyMan* buddy_man );
	static	void s_threaded_buddy_create( BuddyMan* buddy_man );

	void		add_player_to_menu( BuddyInfo* buddy, bool allow_join, bool allow_remove );
	void		fill_buddy_list( bool clear_list, bool allow_join, bool allow_remove );
	void		fill_prospective_buddy_list( void );
	void		add_buddy( char *name, int profile );
	void		cancel_add_buddy( void );
	void		remove_buddy( int profile );

	Tsk::Task< BuddyMan >*					m_buddy_logic_task;
	static	Tsk::Task< BuddyMan >::Code   	s_buddy_logic_code;

	bool	m_logged_in;
	Lst::Head< BuddyInfo > m_buddies;
};





/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet

#endif	// __GAMENET_NGPS_P_BUDDY_H




================================================
FILE: Code/Sk/GameNet/Ngps/p_content.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet					 								**
**																			**
**	File name:		Lobby.cpp												**
**																			**
**	Created by:		04/4/02	-	spg											**
**																			**
**	Description:	Gamespy peer lobby implementation						**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

// for downloading faces
#include 
#include 
#include 
#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/
namespace GameNet
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define vHTTP_COMM_TIMEOUT	Tmr::Seconds( 25 )
#define vMAX_FACE_SIZE		( 32 * 1024 )
#define vMAX_FILE_SIZE	( 80 * 1024 )

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	ContentMan::s_progress_callback(	GHTTPRequest request,       // The request.
											GHTTPState state,           // The current state of the request.
											const char* buffer,        	// The file's bytes so far, NULL if state 0 )
		{
			content_man->m_percent_complete = (int) (((float) bytes_received / (float) total_size ) * 100.0f );
		}
	}

	content_man->m_last_comm_time = Tmr::GetTime();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

GHTTPBool ContentMan::s_dl_completed_callback(	GHTTPRequest request,       // The request.
										GHTTPResult result,         // The result (success or an error).
										char* buffer,              // The file's bytes (only valid if ghttpGetFile[Ex] was used).
										int buffer_len,              // The file's length.
										void * param                // User-data.
										)
{
	ContentMan* content_man;

	content_man = (ContentMan*) param;

	Dbg_Printf( "Got result %d\n", result );

	content_man->m_percent_complete = 100;
	if( result == GHTTPSuccess )
	{
		uint32 type;

		content_man->m_result = vCONTENT_RESULT_SUCCESS;

		type = 0;
		switch( content_man->m_vault_type ) 
		{
			case 0x5be5569f:	// parks
				type = CRCD(0x3bf882cc,"park");
				break;
			case 0x7364ea1:		// skaters
				type = CRCD(0xb010f357,"optionsandpros");
				break;
			case 0x38dbe1d0:	// goals
				type = CRCD(0x62896edf,"createdgoals");
				break;
			case 0x1e26fd3e:	// tricks
				type = CRCD(0x61a1bc57,"cat");
				break;
			default:
				Dbg_Assert( 0 );
				break;
		}
		CFuncs::SetVaultData((uint8*) buffer, type );
		
		content_man->m_error = 0;
	}
	else
	{
		content_man->m_result = vCONTENT_RESULT_FAILURE;
		content_man->m_error = result;
	}
	
	return GHTTPTrue;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

GHTTPBool ContentMan::s_face_dl_completed_callback(	GHTTPRequest request,       // The request.
													GHTTPResult result,         // The result (success or an error).
													char* buffer,              // The file's bytes (only valid if ghttpGetFile[Ex] was used).
													int buffer_len,              // The file's length.
													void * param                // User-data.
													)
{
	ContentMan* content_man;

	content_man = (ContentMan*) param;

	Dbg_Printf( "Got result %d\n", result );

	content_man->m_percent_complete = 100;
	if( result == GHTTPSuccess )
	{
		content_man->m_result = vCONTENT_RESULT_SUCCESS;
		content_man->m_error = 0;
		
		// Copy the downloaded data to the skater profile
		Mdl::Skate* skate_mod = Mdl::Skate::Instance();
		Obj::CSkaterProfile* pSkaterProfile = skate_mod->GetCurrentProfile();
		Dbg_Assert( pSkaterProfile );
		Dbg_MsgAssert( !pSkaterProfile->IsPro(), ( "Can only download face onto a custom skater.  UI must make the custom skater active before this point" ) );
		Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
		Dbg_Assert( pAppearance );
		Gfx::CFaceTexture* pFaceTexture = pAppearance->GetFaceTexture();
		Dbg_MsgAssert( pFaceTexture, ( "Appearance has no face texture" ) );
		
		// GJ:  The CFaceTexture is permanently allocated in the
		// custom skater, so there should be no need to push 
		// any heap contexts here...  if this assumption changes, 
		// then we should probably push the skt_info heap
		pFaceTexture->ReadTextureDataFromBuffer( (uint8*)buffer, 0 );
		pFaceTexture->SetValid( true );

		// rebuild the skater to display the new face
		Script::RunScript( CRCD(0x808008ea,"refresh_skater_model") );
	}
	else
	{
		content_man->m_result = vCONTENT_RESULT_FAILURE;
		content_man->m_error = result;
	}
    
	return GHTTPTrue;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

GHTTPBool ContentMan::s_ul_completed_callback(	GHTTPRequest request,       // The request.
										GHTTPResult result,         // The result (success or an error).
										char* buffer,              // The file's bytes (only valid if ghttpGetFile[Ex] was used).
										int buffer_len,              // The file's length.
										void * param                // User-data.
										)
{
	ContentMan* content_man;

	content_man = (ContentMan*) param;

	Dbg_Printf( "Got result %d\n", result );

	content_man->m_percent_complete = 100;
	if( result == GHTTPSuccess )
	{
		content_man->m_result = vCONTENT_RESULT_SUCCESS;
	}
	else
	{
		content_man->m_result = vCONTENT_RESULT_FAILURE;
	}
	
	return GHTTPTrue;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	ContentMan::s_http_transfer_code( const Tsk::Task< ContentMan > &task )
{
	ContentMan& content_man = task.GetData();

	if(( Tmr::GetTime() - content_man.m_last_comm_time ) > vHTTP_COMM_TIMEOUT )
	{
		if(	content_man.m_state != vCONTENT_STATE_IDLE )
		{
			delete [] content_man.m_buffer;
		}

		Dbg_Printf( "********* TIMING OUT!!!!\n" );
		Dbg_Assert( content_man.m_transfer_failed_script );

		ghttpCancelRequest( content_man.m_file_request );

		Script::RunScript( content_man.m_transfer_failed_script );
		content_man.m_state = vCONTENT_STATE_IDLE;
		content_man.m_http_transfer_task->Remove();

        //ghttpCleanup();
		return;
	}

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	ghttpThink();
	Mem::Manager::sHandle().PopContext();

	if( content_man.m_percent_complete < 100 )
	{
		if(( content_man.m_state == vCONTENT_STATE_DOWNLOADING ) ||
		   ( content_man.m_state == vCONTENT_STATE_DOWNLOADING_FACE ))
		{
			Script::CStruct* p_item_params;
			char pct_string[8];
		
			sprintf( pct_string, "%d%%", content_man.m_percent_complete );
					
			p_item_params = new Script::CStruct;
			p_item_params->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, pct_string );
			Dbg_Assert( content_man.m_update_progress_script );
			Script::RunScript( content_man.m_update_progress_script, p_item_params );
			delete p_item_params;
		}
	}
	else
	{
		if( ( content_man.m_state == vCONTENT_STATE_DOWNLOADING ) ||
			( content_man.m_state == vCONTENT_STATE_DOWNLOADING_FACE ))
		{
			delete [] content_man.m_buffer;
		}

		//WaitSema( content_man.m_thread_semaphore_id );
		if(( content_man.m_state == vCONTENT_STATE_DOWNLOADING ) || 
		   ( content_man.m_state == vCONTENT_STATE_DOWNLOADING_FACE ) ||
		   ( content_man.m_state == vCONTENT_STATE_UPLOADING ))
		{
			int prev_state;
			 
			prev_state = content_man.m_state;
			content_man.m_http_transfer_task->Remove();
			content_man.m_state = vCONTENT_STATE_IDLE;

			if( content_man.m_result == vCONTENT_RESULT_SUCCESS )
			{
				Script::CStruct* params;

				params = new Script::CStruct;
				params->AddChecksum( "type", content_man.m_vault_type );
				if( prev_state == vCONTENT_STATE_UPLOADING )
				{
					Script::RunScript( "LaunchUploadCompleteDialog", params );
				}
				else
				{
					Dbg_Assert( content_man.m_transfer_complete_script );
					Script::RunScript( content_man.m_transfer_complete_script, params );
				}

				delete params;
			}
			else
			{
				if( content_man.m_error == GHTTPFileNotFound )
				{
					Dbg_Assert( content_man.m_file_not_found_script );
					Script::RunScript( content_man.m_file_not_found_script );
				}
				else
				{
					Dbg_Assert( content_man.m_transfer_failed_script );
					Script::RunScript( content_man.m_transfer_failed_script );
				}
			}
			if( ( prev_state == vCONTENT_STATE_DOWNLOADING ) ||
				( prev_state == vCONTENT_STATE_DOWNLOADING_FACE ))
			{
				//delete [] content_man.m_buffer;
			}
			if( prev_state == vCONTENT_STATE_UPLOADING )
			{
				delete [] content_man.m_buffer;
			}
			
			//content_man.m_state = vCONTENT_STATE_IDLE;
			//content_man.m_http_transfer_task->Remove();
			//ghttpCleanup();
		}
		else if( content_man.m_state == vCONTENT_STATE_GETTING_DIR_LIST )
		{
			if( content_man.m_result == vCONTENT_RESULT_SUCCESS )
			{
				char* token;
				Directory* dir;

				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

				Dbg_Printf( "**** Parsing directory listing\n" );
				token = strtok( content_man.m_buffer, "\r\n" );
				if( token )
				{
					do
					{   
						Dbg_Printf( "**** Got dir %s\n", token );
						dir = new Directory;
						content_man.m_directories.AddToHead( dir );
						strcpy( dir->m_Name, token );
					} while(( token = strtok( NULL, "\r\n" )));

					content_man.m_cur_dir = content_man.m_next_dir_sh.FirstItem( content_man.m_directories );
					if( content_man.m_cur_dir->m_Uploads )
					{
						content_man.m_cur_dir = content_man.m_next_dir_sh.NextItem();
					}

					Dbg_Printf( "**** Getting subdir listing for %s\n", content_man.m_cur_dir->m_Name );

					content_man.m_state = vCONTENT_STATE_IDLE;
					//ghttpCleanup();
					delete [] content_man.m_buffer;

					//SignalSema( content_man.m_thread_semaphore_id );
					content_man.GetSubDirListing( content_man.m_cur_dir );
					Mem::Manager::sHandle().PopContext();
					return;
				}

				Mem::Manager::sHandle().PopContext();
			}
			else
			{
				content_man.m_state = vCONTENT_STATE_IDLE;
				content_man.m_http_transfer_task->Remove();
				//ghttpCleanup();
				delete [] content_man.m_buffer;

				Dbg_Assert( content_man.m_transfer_failed_script );
				Script::RunScript( content_man.m_transfer_failed_script );
			}
		}
		else if( content_man.m_state == vCONTENT_STATE_GETTING_SUB_DIR_LIST )
		{
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

			Dbg_Printf( "Got sub_dir list\n" );

			if( content_man.m_result == vCONTENT_RESULT_SUCCESS )
			{
				Content* content;
				char* line, *colon, *token;
				char fname[Content::vMAX_FILE_NAME_LEN + 1];
				char buff[128];

				line = strtok( content_man.m_buffer, "\r\n" );
				if( line )
				{
					do
					{
						content = new Content;
						content_man.m_cur_dir->m_content_list.AddToHead( content );

						token = line;
						colon = strstr( line, ":" );
						Dbg_Assert( colon );
						*colon = '\0';
						strcpy( fname, token );

						sprintf( content->m_Path, "http://www.thugonline.com/%s/%s/%s", content_man.m_vault_root, content_man.m_cur_dir->m_Name, fname );
						Dbg_Printf( "Filename: %s\n", fname );

						token = colon + 1;
						colon = strstr( token, ":" );
						Dbg_Assert( colon );
						*colon = '\0';
						strcpy( content->m_Name, token );
						Dbg_Printf( "Display name: %s\n", content->m_Name );

						token = colon + 1;
						colon = strstr( token, ":" );
						Dbg_Assert( colon );
						*colon = '\0';
						strcpy( content->m_Creator, token );
						Dbg_Printf( "Creator: %s\n", content->m_Creator );

						// Read the content size, if we're looking at parks
						if( content_man.m_vault_type == CRCD(0x5be5569f,"parks") )
						{
							token = colon + 1;
							colon = strstr( token, ":" );
							Dbg_Assert( colon );
							*colon = '\0';
							strcpy( content->m_Size, token );
							Dbg_Printf( "Size: %s\n", content->m_Size );

							token = colon + 1;
							colon = strstr( token, ":" );
							Dbg_Assert( colon );
							*colon = '\0';
							strcpy( buff, token );
							content->m_NumPieces = atoi( buff );
							Dbg_Printf( "Pieces: %d\n", content->m_NumPieces );

							token = colon + 1;
							colon = strstr( token, ":" );
							Dbg_Assert( colon );
							*colon = '\0';
							strcpy( buff, token );
							content->m_NumGaps = atoi( buff );
							Dbg_Printf( "Gaps: %d\n", content->m_NumGaps );

							token = colon + 1;
							colon = strstr( token, ":" );
							Dbg_Assert( colon );
							*colon = '\0';
							strcpy( buff, token );
							content->m_NumGoals = atoi( buff );
							Dbg_Printf( "Goals: %d\n", content->m_NumGoals );

							token = colon + 1;
							colon = strstr( token, ":" );
							Dbg_Assert( colon );
							*colon = '\0';
							strcpy( buff, token );
							content->m_Theme = atoi( buff );
							Dbg_Printf( "Theme: %d\n", content->m_Theme );

							token = colon + 1;
							colon = strstr( token, ":" );
							Dbg_Assert( colon );
							*colon = '\0';
							strcpy( buff, token );
							content->m_TODScript = atoi( buff );
							Dbg_Printf( "Script: 0x%x\n", content->m_TODScript );
						}

						// Read the content score, if we're looking at tricks
						if( content_man.m_vault_type == CRCD(0x1e26fd3e,"tricks") )
						{
							token = colon + 1;
							colon = strstr( token, ":" );
							Dbg_Assert( colon );
							*colon = '\0';
							strcpy( buff, token );
							content->m_Score = atoi( buff );
							Dbg_Printf( "Score: %d\n", content->m_Score );
						}

						// Read the content sex, if we're looking at skaters
						if( content_man.m_vault_type == CRCD(0x7364ea1,"skaters") )
						{
							token = colon + 1;
							colon = strstr( token, ":" );
							Dbg_Assert( colon );
							*colon = '\0';
							//strcpy( content->m_IsMale, token );
							strcpy( buff, token );
							content->m_IsMale = atoi( buff );
							Dbg_Printf( "IsMale: %d\n", content->m_IsMale );
						}

						// Read the content sex, if we're looking at skaters
						if( content_man.m_vault_type == CRCD(0x38dbe1d0,"goals") )
						{
							token = colon + 1;
							colon = strstr( token, ":" );
							Dbg_Assert( colon );
							*colon = '\0';
							strcpy( buff, token );
							content->m_NumGoals = atoi( buff );
							Dbg_Printf( "Goals: %d\n", content->m_NumGoals );
						}

						token = colon + 1;
						colon = strstr( token, ":" );
						Dbg_Assert( colon );
						*colon = '\0';
						strcpy( content->m_Checksum, token );
						Dbg_Printf( "Checksum: %s\n", content->m_Checksum );

						token = colon + 1;
						strcpy( content->m_Description, token );
						Dbg_Printf( "Desc: %s\n", content->m_Description );
						

						//Dbg_Printf( "////////////////////////\n" );
						//Dbg_Printf( "Name: %s\n", content->m_Name );
						//Dbg_Printf( "Creator: %s\n", content->m_Creator );
						//Dbg_Printf( "Size: %s\n", content->m_Size );
						//Dbg_Printf( "Checksum: %s\n", content->m_Checksum );
						//Dbg_Printf( "Desc: %s\n", content->m_Description );
					} while(( line = strtok( NULL, "\r\n" )));
				}
				
				content_man.m_state = vCONTENT_STATE_IDLE;
				//ghttpCleanup();
				delete [] content_man.m_buffer;

				content_man.m_cur_dir = content_man.m_next_dir_sh.NextItem();
				if(( content_man.m_cur_dir == NULL ) || ( content_man.m_cur_dir->m_Uploads ))
				{
					Script::CStruct* pParams;
					content_man.m_cur_dir = content_man.m_next_dir_sh.FirstItem( content_man.m_directories );

					// We are done. Now display the first category of files
					//Dbg_Printf( "**** Done downloading subdirectory listings\n" );
					content_man.m_http_transfer_task->Remove();
					pParams = new Script::CStruct;
					pParams->AddString( "category", content_man.m_cur_dir->m_Name );
					pParams->AddChecksum( "type", content_man.m_vault_type );
					Script::RunScript( "net_vault_menu_create", pParams );
					delete pParams;
				}
				else
				{
					content_man.GetSubDirListing( content_man.m_cur_dir );
				}
			}
            else
			{
				content_man.m_state = vCONTENT_STATE_IDLE;
				content_man.m_http_transfer_task->Remove();

				//ghttpCleanup();
				delete [] content_man.m_buffer;
				Dbg_Assert( content_man.m_transfer_failed_script );
				Script::RunScript( content_man.m_transfer_failed_script );
			}

			Mem::Manager::sHandle().PopContext();
		}
		else if( content_man.m_state == vCONTENT_STATE_GETTING_UPLOADED_LEVELS )
		{
			char* line, *colon, *token;
			Content* content;

			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
                                      
			line = strtok( content_man.m_buffer, "\r\n" );
			if( line )
			{
				do
				{
					content = new Content;
					content_man.m_cur_dir->m_content_list.AddToHead( content );

					//Dbg_Printf( "******************************************\n" );
					//Dbg_Printf( "Line: %s\n", line );
					sprintf( content->m_Path, "http://www.thugonline.com/%s/%s", content_man.m_vault_root, line );

					token = line;
					colon = strstr( line, ":" );
					Dbg_Assert( colon );
					*colon = '\0';
					strcpy( content->m_Name, token );

					token = colon + 1;
					colon = strstr( token, ":" );
					Dbg_Assert( colon );
					*colon = '\0';
					strcpy( content->m_Creator, token );

					token = colon + 1;
					colon = strstr( token, ":" );
					Dbg_Assert( colon );
					*colon = '\0';
					strcpy( content->m_Size, token );

					token = colon + 1;
					colon = strstr( token, ":" );
					Dbg_Assert( colon );
					*colon = '\0';
					strcpy( content->m_Checksum, token );

					token = colon + 1;
					strcpy( content->m_Description, token );

					/*Dbg_Printf( "////////////////////////\n" );
					Dbg_Printf( "Name: %s\n", content->m_Name );
					Dbg_Printf( "Creator: %s\n", content->m_Creator );
					Dbg_Printf( "Size: %s\n", content->m_Size );
					Dbg_Printf( "Checksum: %s\n", content->m_Checksum );
					Dbg_Printf( "Desc: %s\n", content->m_Description );
					*/
				} while(( line = strtok( NULL, "\r\n" )));
			}

			content_man.m_state = vCONTENT_STATE_IDLE;
			content_man.m_http_transfer_task->Remove();

			//ghttpCleanup();
			delete [] content_man.m_buffer;

			content_man.DownloadDirectoryListing();

			Mem::Manager::sHandle().PopContext();
		}
		
		//SignalSema( content_man.m_thread_semaphore_id );
	}
}


/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

void		ContentMan::FreeDirectoryListing( void )
{
	Directory* dir, *next;
	Lst::Search< Directory > sh;

	for( dir = sh.FirstItem( m_directories ); dir; dir = next )
	{
		next = sh.NextItem();
		delete dir;
	}
	ghttpCleanup();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		ContentMan::GetDirListing( char* path )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Thread::PerThreadStruct	net_thread_data;

	sprintf( m_download_path, path );
	//mlp_man->AddLogicTask( *m_http_transfer_task );
	m_percent_complete = 0;
	m_last_comm_time = Tmr::GetTime();

	m_transfer_complete_script = CRCD(0x89990f,"LaunchDownloadCompleteDialog");
	m_transfer_failed_script = CRCD(0x7335ab7f,"LaunchTransferFailedDialog");
	m_file_not_found_script = CRCD(0xcf040665,"LaunchGeneralFileNotFoundDialog");
	m_update_progress_script = CRCD(0x4b85ee64,"update_transfer_progress");

	Dbg_Printf( "**** Downloading path %s\n", m_download_path );

	net_thread_data.m_pEntry = s_threaded_download_directory_list;
	net_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
	net_thread_data.m_pStackBase = gamenet_man->GetNetThreadStack();
	net_thread_data.m_iStackSize = vNET_THREAD_STACK_SIZE;
	net_thread_data.m_utid = 0x152;
	Thread::CreateThread( &net_thread_data );
	gamenet_man->SetNetThreadId( net_thread_data.m_osId );

	//WaitSema( m_thread_semaphore_id );
	StartThread( gamenet_man->GetNetThreadId(), this );
	//SignalSema( m_thread_semaphore_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		ContentMan::GetSubDirListing( Directory* dir )
{
	char subdir_path[128];

	sprintf( subdir_path, "http://www.thugonline.com/%s/%s/dir_list.txt", m_vault_root, dir->m_Name );
	m_state = vCONTENT_STATE_GETTING_SUB_DIR_LIST;
	m_result = vCONTENT_RESULT_UNDETERMINED;
	GetDirListing( subdir_path );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		ContentMan::FillDirectoryListing( char* dir_text )
{
	char* token;
	Script::CStruct* p_item_params;
	Script::CStruct* p_script_params;

	//Dbg_Printf( "printing buffer\n" );
	//Dbg_Printf( "Buffer: %s\n", dir_text );

	token = strtok( dir_text, "\t" );
	if( token )
	{
		p_item_params = new Script::CStruct;
		p_item_params->AddString( "text", token );
		token = strtok( NULL, "\r\n" );
		Dbg_Assert( token );

		p_item_params->AddChecksum( "pad_choose_script", Script::GenerateCRC("download_selected_file"));

		// create the parameters that are passed to the choose script
		p_script_params= new Script::CStruct;
		p_script_params->AddString( "file", token );	
		p_item_params->AddStructure("pad_choose_params",p_script_params);			

		Script::RunScript( "make_text_sub_menu_item", p_item_params );
		delete p_item_params;
		delete p_script_params;

        while( token )
		{   
			p_item_params = new Script::CStruct;
			
			token = strtok( NULL, "\t" );
			if( token == NULL )
			{
				break;
			}

			p_item_params->AddString( "text", token );
	
			p_item_params->AddChecksum( "pad_choose_script", Script::GenerateCRC("download_selected_file"));
	
			token = strtok( NULL, "\r\n" );
			Dbg_Assert( token );

			// create the parameters that are passed to the choose script
			p_script_params= new Script::CStruct;
			p_script_params->AddString( "file", token );	
			p_item_params->AddStructure("pad_choose_params",p_script_params);			
	
			Script::RunScript( "make_text_sub_menu_item", p_item_params );
			delete p_item_params;
			delete p_script_params;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ContentMan::ContentMan( void )
{
	struct SemaParam params;

	m_state = vCONTENT_STATE_IDLE;
	m_percent_complete = 0;
	m_last_comm_time = 0;
	m_http_transfer_task = new Tsk::Task< ContentMan > ( s_http_transfer_code, *this );

	params.initCount = 1;
	params.maxCount = 10;

	m_thread_semaphore_id = CreateSema( ¶ms );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ContentMan::~ContentMan( void )
{
	DeleteSema( m_thread_semaphore_id );
	delete m_http_transfer_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		ContentMan::s_threaded_download_directory_list( ContentMan* content_man )
{
	WaitSema( content_man->m_thread_semaphore_id );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
	// Register this thread with the sockets API
	sockAPIregthr();

	content_man->m_buffer = new char[100000];
	memset( content_man->m_buffer, 0, 100000 );

	content_man->m_file_request = ghttpGetEx( 	content_man->m_download_path,
												NULL,
												content_man->m_buffer,
												100000,
												NULL,
												GHTTPFalse,
												GHTTPFalse,
												s_progress_callback,
												s_ul_completed_callback,
												content_man );
    
	//delete buffer;

	// Deregister this thread with the sockets API
	sockAPIderegthr();

	//printf( "******** EXITING DOWNLOAD DIRECTORY THREAD\n" );

	Mem::Manager::sHandle().PopContext();

	SignalSema( content_man->m_thread_semaphore_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	ContentMan::DownloadDirectoryListing( void )
{
	char path[128];
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();

	m_state = vCONTENT_STATE_GETTING_DIR_LIST;
	sprintf( path, "http://www.thugonline.com/%s/dir_list.txt", m_vault_root );
	GetDirListing( path );
	m_result = vCONTENT_RESULT_UNDETERMINED;;
	mlp_man->AddLogicTask( *m_http_transfer_task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	ContentMan::DownloadFace( char* filename )
{
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Thread::PerThreadStruct	net_thread_data;
	char path[256];
    
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	m_state = vCONTENT_STATE_DOWNLOADING_FACE;
	m_result = vCONTENT_RESULT_UNDETERMINED;
	mlp_man->AddLogicTask( *m_http_transfer_task );
	m_percent_complete = 0;
	m_last_comm_time = Tmr::GetTime();

	m_transfer_complete_script = CRCD(0x1d53d27f,"LaunchFaceDownloadCompleteDialog");
	m_transfer_failed_script = CRCD(0x7335ab7f,"LaunchTransferFailedDialog");
	m_file_not_found_script = CRCD(0x609f7535,"LaunchFileNotFoundDialog");
	m_update_progress_script = CRCD(0x4b85ee64,"update_transfer_progress");

	net_thread_data.m_pEntry = s_threaded_download_face;
	net_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
	net_thread_data.m_pStackBase = gamenet_man->GetNetThreadStack();
	net_thread_data.m_iStackSize = vNET_THREAD_STACK_SIZE;
	net_thread_data.m_utid = 0x152;
	Thread::CreateThread( &net_thread_data );
	gamenet_man->SetNetThreadId( net_thread_data.m_osId );
	
	Dbg_Printf( "In DownloadFace. Path: %s\n", filename );
	WaitSema( m_thread_semaphore_id );
	
	sprintf( path, "http://www.thugonline.com/faces/%s", filename );
	Dbg_Printf( "Downloading %s\n", path );
	StartThread( gamenet_man->GetNetThreadId(), (char*) path );
	SignalSema( m_thread_semaphore_id );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	ContentMan::ScriptDownloadFace(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	ContentMan* content_man;
	BuddyMan* buddy_man;
	char filename[128];
	const char* key;

	pParams->GetString( "string", &key, Script::ASSERT );
	sprintf( filename, "%s.img.ps2", key );
	Dbg_Printf( "Downloading %s...\n", filename );

	content_man = gamenet_man->mpContentMan;
	buddy_man = gamenet_man->mpBuddyMan;
	buddy_man->GetProfile();
	content_man->DownloadFace( filename );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	ContentMan::ScriptDownloadDirectoryList(Script::CScriptStructure *p_item_params, Script::CScript *pScript)
{
    GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	ContentMan* content_man;
	BuddyMan* buddy_man;

    content_man = gamenet_man->mpContentMan;
	buddy_man = gamenet_man->mpBuddyMan;
	p_item_params->GetChecksum( NONAME, &content_man->m_vault_type , true );
	switch( content_man->m_vault_type )
	{
		case 0x5be5569f:	// parks
			sprintf( content_man->m_vault_root, "levels" );
			break;
		case 0x7364ea1:		// skaters
			sprintf( content_man->m_vault_root, "skaters" );
			break;
		case 0x38dbe1d0:	// goals
			sprintf( content_man->m_vault_root, "goals" );
			break;
		case 0x1e26fd3e:	// tricks
			sprintf( content_man->m_vault_root, "tricks" );
			break;
		default:
			Dbg_Assert( 0 );
			break;
			
	}

	if( buddy_man->GetProfile() == 18549782 )	// Gamespy profile of our level-verifying account
	{
		Directory* dir;
		char path[128];

		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

		dir = new Directory;
		dir->m_Uploads = true;
		content_man->m_directories.AddToHead( dir );
		strcpy( dir->m_Name, "Uploads" );
		content_man->m_cur_dir = content_man->m_next_dir_sh.FirstItem( content_man->m_directories );

		content_man->m_state = vCONTENT_STATE_GETTING_UPLOADED_LEVELS;
		content_man->m_result = vCONTENT_RESULT_UNDETERMINED;
		sprintf( path, "http://www.thugonline.com/%s/uploads.txt", content_man->m_vault_root );
		content_man->GetDirListing( path );
		mlp_man->AddLogicTask( *content_man->m_http_transfer_task );
		Mem::Manager::sHandle().PopContext();
	}
	else
	{
		content_man->DownloadDirectoryListing();
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	ContentMan::ScriptFreeDirectoryListing(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	ContentMan* content_man;

	content_man = gamenet_man->mpContentMan;
	content_man->FreeDirectoryListing();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		ContentMan::s_threaded_download_file( const char* path )
{
	int max_size;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	ContentMan* content_man;

	content_man = gamenet_man->mpContentMan;
	WaitSema( content_man->m_thread_semaphore_id );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	max_size = content_man->m_buffer_size;

	content_man->m_buffer = new char[max_size];

	// Register this thread with the sockets API
	sockAPIregthr();

	//Dbg_Printf( "downloading %s, size: %d dl complete: %p\n", path, max_size, content_man->m_dl_complete );
	gamenet_man->mpContentMan->m_file_request = ghttpGetEx( path,
															NULL,
															content_man->m_buffer,
															max_size,
															NULL,
															GHTTPFalse,
															GHTTPFalse,
															s_progress_callback,
															content_man->m_dl_complete,
															content_man );
    
	// Deregister this thread with the sockets API
	sockAPIderegthr();
    
	Mem::Manager::sHandle().PopContext();
	
	SignalSema( gamenet_man->mpContentMan->m_thread_semaphore_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	ContentMan::s_threaded_download_face( const char* path )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	WaitSema( gamenet_man->mpContentMan->m_thread_semaphore_id );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	gamenet_man->mpContentMan->m_buffer = new char[vMAX_FACE_SIZE];

	// Register this thread with the sockets API
	sockAPIregthr();

	gamenet_man->mpContentMan->m_file_request = ghttpGetEx( path,
															NULL,
															gamenet_man->mpContentMan->m_buffer,
															vMAX_FACE_SIZE,
															NULL,
															GHTTPFalse,
															GHTTPFalse,
															s_progress_callback,
															s_face_dl_completed_callback,
															gamenet_man->mpContentMan );
    
	// Deregister this thread with the sockets API
	sockAPIderegthr();
    
	Mem::Manager::sHandle().PopContext();

	SignalSema( gamenet_man->mpContentMan->m_thread_semaphore_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	ContentMan::SetPercentComplete( int percent_complete )
{
	m_percent_complete = percent_complete;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	ContentMan::SetResult( int result )
{
	m_result = result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	ContentMan::SetError( int error )
{
	m_error = error;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	ContentMan::DownloadFile( DownloadContext* context )
{
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	ContentMan* content_man;
	Thread::PerThreadStruct	net_thread_data;
    
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	Dbg_Printf( "Downloading %s\n", context->m_path );
	
	content_man = gamenet_man->mpContentMan;
	content_man->m_state = vCONTENT_STATE_DOWNLOADING;
	content_man->m_result = vCONTENT_RESULT_UNDETERMINED;
	mlp_man->AddLogicTask( *content_man->m_http_transfer_task );
	content_man->m_percent_complete = 0;
	content_man->m_last_comm_time = Tmr::GetTime();
	
	content_man->m_buffer_size = context->m_max_size;
	content_man->m_transfer_failed_script = context->m_transfer_failed_script;
	content_man->m_transfer_complete_script = context->m_transfer_complete_script;
	content_man->m_update_progress_script = context->m_update_progress_script;
	content_man->m_file_not_found_script = context->m_file_not_found_script;
	content_man->m_dl_complete = context->m_dl_complete;

	net_thread_data.m_pEntry = s_threaded_download_file;
	net_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
	net_thread_data.m_pStackBase = gamenet_man->GetNetThreadStack();
	net_thread_data.m_iStackSize = vNET_THREAD_STACK_SIZE;
	net_thread_data.m_utid = 0x152;
	Thread::CreateThread( &net_thread_data );
	gamenet_man->SetNetThreadId( net_thread_data.m_osId );
	
	WaitSema( content_man->m_thread_semaphore_id );
	StartThread( gamenet_man->GetNetThreadId(), context->m_path );
	SignalSema( content_man->m_thread_semaphore_id );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	ContentMan::ScriptDownloadFile(Script::CScriptStructure *p_item_params, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	ContentMan* content_man;
	const char* path;
	DownloadContext context;
	
	p_item_params->GetString( "file", &path );

	content_man = gamenet_man->mpContentMan;
	
	context.m_transfer_complete_script = CRCD(0x89990f,"LaunchDownloadCompleteDialog");
	context.m_transfer_failed_script = CRCD(0x7335ab7f,"LaunchTransferFailedDialog");
	context.m_file_not_found_script = CRCD(0xcf040665,"LaunchGeneralFileNotFoundDialog");
	context.m_update_progress_script = CRCD(0x4b85ee64,"update_transfer_progress");
	context.m_max_size = vMAX_FILE_SIZE;
	sprintf( context.m_path, path );
	context.m_dl_complete = s_dl_completed_callback;
	content_man->DownloadFile( &context );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************

static char* s_fix_name( char* filename )
{
	char* ch = filename;
	
	while( *ch )
	{
		if(( *ch >= '0' ) && ( *ch <= '9' ))
		{
			ch++;
			continue;
		}

		if(( *ch >= 'A' ) && ( *ch <= 'Z' ))
		{
			ch++;
			continue;
		}

		if(( *ch >= 'a' ) && ( *ch <= 'z' ))
		{
			ch++;
			continue;
		}

		if(( *ch == '-' ) || ( *ch =='_' ))
		{
			ch++;
			continue;
		}

		*ch = '_';
		ch++;
	}

	return filename;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		ContentMan::s_threaded_upload_file( ContentMan* content_man )
{
	GHTTPPost post;
	char *file_buffer; 
	char filename[64], desc[1024], str[64];
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Prefs::Preferences* pPreferences;
	Script::CStruct* pStructure;
	const char* network_id;
	int filesize;
	uint32 checksum;

	pPreferences = gamenet_man->GetNetworkPreferences();
	pStructure = pPreferences->GetPreference( Script::GenerateCRC("network_id") );
	pStructure->GetText( "ui_string", &network_id, true );

	WaitSema( content_man->m_thread_semaphore_id );
	sockAPIregthr();

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
	post = ghttpNewPost();
	switch( content_man->m_vault_type )
	{
		case 0x5be5569f:	// parks
		{
			filesize = Script::CalculateBufferSize( content_man->m_upload_struct );
			file_buffer = new char[filesize];
			Script::WriteToBuffer( content_man->m_upload_struct, (uint8*) file_buffer, filesize );
			checksum = Crc::GenerateCRC( file_buffer, filesize );
		
			strcpy( filename, content_man->m_upload_filename );
			ghttpPostAddString( post, "player", network_id );
			sprintf( str, "%d", content_man->m_Width );
			ghttpPostAddString( post, "width", str );
			sprintf( str, "%d", content_man->m_Height );
			ghttpPostAddString( post, "height", str );
			sprintf( str, "%d", checksum );
			ghttpPostAddString( post, "checksum", str );
			
			sprintf( str, "%d", content_man->m_NumPieces );
			ghttpPostAddString( post, "num_pieces", str );
			sprintf( str, "%d", content_man->m_NumGaps );
			ghttpPostAddString( post, "num_gaps", str );
			sprintf( str, "%d", content_man->m_NumGoals );
			ghttpPostAddString( post, "num_goals", str );
			sprintf( str, "%d", content_man->m_Theme );
			ghttpPostAddString( post, "theme", str );
			sprintf( str, "0x%x", content_man->m_TODScript );
			ghttpPostAddString( post, "tod_script", str );
			break;
		}

		case 0x7364ea1:		// skaters
		{
			ghttpPostAddString( post, "filetype", "cas" );
			sprintf( str, "%d", content_man->m_IsMale );
			ghttpPostAddString( post, "is_male", str );
			
			filesize = Script::CalculateBufferSize( content_man->m_upload_struct );
			file_buffer = new char[filesize];
			Script::WriteToBuffer( content_man->m_upload_struct, (uint8*) file_buffer, filesize );
			checksum = Crc::GenerateCRC( file_buffer, filesize );
			strcpy( filename, content_man->m_upload_filename );
			ghttpPostAddString( post, "player", network_id );
			sprintf( str, "%d", checksum );
			ghttpPostAddString( post, "checksum", str );
				
			Dbg_Printf( "Uploading file: %s\n", filename );

			break;
		}

		case 0x38dbe1d0:	// goals
		{
			ghttpPostAddString( post, "filetype", "cag" );
			sprintf( str, "%d", content_man->m_NumGoals );
			ghttpPostAddString( post, "num_goals", str );

			filesize = Script::CalculateBufferSize( content_man->m_upload_struct );
			file_buffer = new char[filesize];
			Script::WriteToBuffer( content_man->m_upload_struct, (uint8*) file_buffer, filesize );
			checksum = Crc::GenerateCRC( file_buffer, filesize );
			strcpy( filename, content_man->m_upload_filename );
			ghttpPostAddString( post, "player", network_id );
			sprintf( str, "%d", checksum );
			ghttpPostAddString( post, "checksum", str );
			break;
		}
		case 0x1e26fd3e:	// tricks
		{
			ghttpPostAddString( post, "filetype", "cat" );
			sprintf( str, "%d", content_man->m_Score );
			ghttpPostAddString( post, "score", str );

			filesize = Script::CalculateBufferSize( content_man->m_upload_struct );
			file_buffer = new char[filesize];
			Script::WriteToBuffer( content_man->m_upload_struct, (uint8*) file_buffer, filesize );
			checksum = Crc::GenerateCRC( file_buffer, filesize );
			strcpy( filename, content_man->m_upload_filename );
			ghttpPostAddString( post, "player", network_id );
			sprintf( str, "%d", checksum );
			ghttpPostAddString( post, "checksum", str );
			break;
		}
		default:
			file_buffer = NULL;
			filesize = 0;
			Dbg_Assert( 0 );
			break;
	}
		
	content_man->m_buffer = file_buffer;
	content_man->m_buffer_size = filesize;
	ghttpPostAddFileFromMemory( post, "filename", file_buffer, filesize, filename, NULL );

	sprintf( desc, "%s", content_man->m_upload_description );
	ghttpPostAddString( post, "description", desc );

	ghttpPostAddString( post, "uniqueid", GOAGetUniqueID() );
	switch( content_man->m_vault_type )
	{
		case 0x5be5569f:	// parks
			ghttpPostAddString( post, "filetype", "cap" );
			break;
		case 0x7364ea1:		// skaters
			ghttpPostAddString( post, "filetype", "cas" );
			break;
		case 0x38dbe1d0:	// goals
			ghttpPostAddString( post, "filetype", "cag" );
			break;
		case 0x1e26fd3e:	// tricks
			ghttpPostAddString( post, "filetype", "cat" );
			break;
		default:
			Dbg_Assert( 0 );
			break;
	}
	
	// debugger log
	ghttpPostAddString( post, "dblog", "1" );

	content_man->m_file_request = ghttpPostEx( "http://www.thugonline.com/cgi-bin/upload2.pl", NULL, post, GHTTPFalse, GHTTPFalse, s_progress_callback, s_ul_completed_callback, content_man );	
	
    
    // Deregister this thread with the sockets API
	sockAPIderegthr();
	Mem::Manager::sHandle().PopContext();

	SignalSema( content_man->m_thread_semaphore_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	ContentMan::ScriptUploadFile(Script::CScriptStructure *p_item_params, Script::CScript *pScript)
{
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	ContentMan* content_man;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Thread::PerThreadStruct	net_thread_data;
	const char* desc, *filename;

	content_man = gamenet_man->mpContentMan;
	content_man->m_state = vCONTENT_STATE_UPLOADING;
	content_man->m_result = vCONTENT_RESULT_UNDETERMINED;
	mlp_man->AddLogicTask( *content_man->m_http_transfer_task );
	content_man->m_percent_complete = 0;
	content_man->m_last_comm_time = Tmr::GetTime();
	content_man->m_transfer_failed_script = CRCD(0x7335ab7f,"LaunchTransferFailedDialog");

	//Script::PrintContents( p_item_params );
	p_item_params->GetChecksum( "type", &content_man->m_vault_type , true );
	p_item_params->GetStructure( "data", &content_man->m_upload_struct );
	p_item_params->GetString( "description", &desc, Script::ASSERT );
	p_item_params->GetString( "filename", &filename, Script::ASSERT );
	strcpy( content_man->m_upload_filename, filename );
	strcpy( content_man->m_upload_description, desc );
	
	p_item_params->GetInteger( "score", &content_man->m_Score, false );
	p_item_params->GetInteger( "width", &content_man->m_Width, false );
	p_item_params->GetInteger( "length", &content_man->m_Height, false );
	p_item_params->GetInteger( "num_pieces", &content_man->m_NumPieces, false );
	p_item_params->GetInteger( "num_gaps", &content_man->m_NumGaps, false );
	p_item_params->GetInteger( "num_goals", &content_man->m_NumGoals, false );
	p_item_params->GetInteger( "theme", &content_man->m_Theme, false );
	p_item_params->GetChecksum( "tod_script", &content_man->m_TODScript, false );
	p_item_params->GetInteger( "is_male", &content_man->m_IsMale, false );
    
	net_thread_data.m_pEntry = s_threaded_upload_file;
	net_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
	net_thread_data.m_pStackBase = gamenet_man->GetNetThreadStack();
	net_thread_data.m_iStackSize = vNET_THREAD_STACK_SIZE;
	net_thread_data.m_utid = 0x152;
	Thread::CreateThread( &net_thread_data );
	gamenet_man->SetNetThreadId( net_thread_data.m_osId );

	WaitSema( content_man->m_thread_semaphore_id );
	StartThread( gamenet_man->GetNetThreadId(), content_man );
	SignalSema( content_man->m_thread_semaphore_id );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	ContentMan::ScriptFillVaultMenu(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	ContentMan* content_man;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Lst::Search< Content > sh;
	Content* content;

	content_man = gamenet_man->mpContentMan;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	for( content = sh.FirstItem( content_man->m_cur_dir->m_content_list ); content; content = sh.NextItem())
	{
		Script::CStruct* pParams, *pChooseParams;

		pParams = new Script::CStruct;

		pParams->AddString( "name", content->m_Name );
		pParams->AddString( "creator", content->m_Creator );
		pParams->AddString( "size", content->m_Size );
		pParams->AddInteger( "score", content->m_Score );
		pParams->AddInteger( "num_pieces", content->m_NumPieces );
		pParams->AddInteger( "num_gaps", content->m_NumGaps );
		pParams->AddInteger( "width", content->m_Width );
		pParams->AddInteger( "height", content->m_Height );
		pParams->AddInteger( "num_goals", content->m_NumGoals );
		pParams->AddInteger( "theme", content->m_Theme );
		pParams->AddChecksum( "tod_script", content->m_TODScript );
		pParams->AddInteger( "is_male", content->m_IsMale );
		pParams->AddString( "description", content->m_Description );

		pChooseParams = new Script::CStruct;

		pChooseParams->AddString( "file", content->m_Path );
		
		pParams->AddStructure( "pad_choose_params", pChooseParams );
		switch( content_man->m_vault_type )
		{
			case 0x5be5569f:	// parks
				Script::RunScript( "net_vault_menu_add_park", pParams );
				break;
			case 0x7364ea1:		// skaters
				Script::RunScript( "net_vault_menu_add_skater", pParams );
				break;
			case 0x38dbe1d0:	// goals
				Script::RunScript( "net_vault_menu_add_goal", pParams );
				break;
			case 0x1e26fd3e:	// tricks
				Script::RunScript( "net_vault_menu_add_trick", pParams );
				break;
			default:
				Dbg_Assert( 0 );
				break;
		}
		

		delete pParams;
		delete pChooseParams;
	}

	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	ContentMan::ScriptNextVaultCategory(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	ContentMan* content_man;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Script::CStruct* passback_params;

	content_man = gamenet_man->mpContentMan;
	content_man->m_cur_dir = content_man->m_next_dir_sh.NextItem();
	if( content_man->m_cur_dir == NULL )
	{
		content_man->m_cur_dir = content_man->m_next_dir_sh.FirstItem( content_man->m_directories );
	}

	passback_params = pScript->GetParams();
	passback_params->AddString( "category", content_man->m_cur_dir->m_Name );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	ContentMan::ScriptPrevVaultCategory(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	ContentMan* content_man;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Script::CStruct* passback_params;

	content_man = gamenet_man->mpContentMan;
	content_man->m_cur_dir = content_man->m_next_dir_sh.PrevItem();
	if( content_man->m_cur_dir == NULL )
	{
		content_man->m_cur_dir = content_man->m_next_dir_sh.LastItem( content_man->m_directories );
	}

	passback_params = pScript->GetParams();
	passback_params->AddString( "category", content_man->m_cur_dir->m_Name );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Directory::Directory( void )
: Lst::Node< Directory > ( this )
{
	m_Uploads = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Directory::~Directory( void )
{
	Content* content, *next;
	Lst::Search< Content > sh;

	for( content = sh.FirstItem( m_content_list ); content; content = next )
	{
		next = sh.NextItem();
		delete content;
	}

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Content::Content( void )
: Lst::Node< Content > ( this )
{
	m_Path[0] = '\0';
	m_Name[0] = '\0';
	m_Creator[0] = '\0';
	m_Size[0] = '\0';
	m_Checksum[0] = '\0';
	m_Description[0] = '\0';
	m_Score = 0;
	m_NumPieces = 0;
	m_NumGaps = 0;
	m_NumGoals = 0;
	m_Theme = 0;
	m_TODScript = 0;
	m_IsMale = 0;
	m_Width = 0;
	m_Height = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet


================================================
FILE: Code/Sk/GameNet/Ngps/p_content.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet													**
**																			**
**	File name:		p_content.h												**
**																			**
**	Created:		05/28/02	- spg										**
**																			**
*****************************************************************************/

#ifndef	__P_CONTENT_H
#define	__P_CONTENT_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

namespace GameNet
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

enum
{
	vCONTENT_STATE_IDLE,
	vCONTENT_STATE_GETTING_DIR_LIST,
	vCONTENT_STATE_GETTING_SUB_DIR_LIST,
	vCONTENT_STATE_DOWNLOADING,
	vCONTENT_STATE_UPLOADING,
	vCONTENT_STATE_GETTING_UPLOADED_LEVELS,
	vCONTENT_STATE_DOWNLOADING_FACE
};

enum
{
	vCONTENT_RESULT_UNDETERMINED,
	vCONTENT_RESULT_SUCCESS,
	vCONTENT_RESULT_FAILURE
};

/*****************************************************************************
**							     Class Definitions							**
*****************************************************************************/

class Content : public Lst::Node< Content >
{
public:
	enum
	{
		vMAX_FILE_NAME_LEN = 23,
		vMAX_PATH_NAME_LEN = 127,
		vMAX_CREATOR_NAME_LEN = 15,
		vMAX_DIMENSION_LEN = 15,
		vMAX_CHECKSUM_LEN = 32,
		vMAX_DESC_LEN = 255,
		vMAX_NUM_LEN = 15,
		vMAX_THEME_NAME_LEN = 23,
		vMAX_SCRIPT_NAME_LEN = 23,
		vMAX_SEX_NAME_LEN = 15,
	};

	Content( void );

	char	m_Path[vMAX_PATH_NAME_LEN + 1];
	char	m_Name[vMAX_FILE_NAME_LEN + 1];
	char	m_Creator[vMAX_CREATOR_NAME_LEN + 1];
	char	m_Size[vMAX_DIMENSION_LEN + 1];
	char	m_Checksum[vMAX_CHECKSUM_LEN + 1];
	char	m_Description[vMAX_DESC_LEN + 1];
	int		m_Score;
	int		m_Width;
	int		m_Height;
	int		m_NumPieces;
	int		m_NumGaps;
	int		m_NumGoals;
	int		m_Theme;
	uint32	m_TODScript;
	int		m_IsMale;
};

class Directory : public Lst::Node< Directory >
{
public:
	enum
	{
		vMAX_DIR_NAME_LEN = 23
	};

	Directory( void );
	~Directory( void );

	bool	m_Uploads;
	char	m_Name[vMAX_DIR_NAME_LEN + 1];
	Lst::Head< Content > 	m_content_list;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class DownloadContext
{
public:
	int						m_max_size;
	char					m_path[256];
	ghttpCompletedCallback	m_dl_complete;
	uint32					m_transfer_failed_script;
	uint32					m_transfer_complete_script;
	uint32					m_update_progress_script;
	uint32					m_file_not_found_script;

};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class ContentMan
{
public:
					ContentMan( void );
					~ContentMan( void );

	void			DownloadDirectoryListing( void );
	void			FillDirectoryListing( char* dir_text );
	void			FreeDirectoryListing( void );
	void			GetDirListing( char* path );
	void			GetSubDirListing( Directory* dir );
	void			DownloadFace( char* filename );
	void			DownloadFile( DownloadContext* context );

	void			SetPercentComplete( int percent_complete );
	void			SetResult( int result );
	void			SetError( int error );

	static	bool	ScriptDownloadFace(Script::CScriptStructure *pParams, Script::CScript *pScript);

    static	bool	ScriptDownloadDirectoryList(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptFreeDirectoryListing(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptDownloadFile(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptUploadFile(Script::CScriptStructure *pParams, Script::CScript *pScript);

	static	bool	ScriptFillVaultMenu(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptNextVaultCategory(Script::CScriptStructure *pParams, Script::CScript *pScript);
	static	bool	ScriptPrevVaultCategory(Script::CScriptStructure *pParams, Script::CScript *pScript);

private:

	Tsk::Task< ContentMan >*				m_http_transfer_task;
	static	Tsk::Task< ContentMan >::Code   s_http_transfer_code;

	static	void	s_threaded_upload_file( ContentMan* content_man );
	static	void	s_threaded_download_file( const char* path );
	static	void	s_threaded_download_face( const char* path );
	static	void	s_threaded_download_directory_list( ContentMan* content_man );

	static	void 	s_progress_callback(	GHTTPRequest request,       // The request.
											GHTTPState state,           // The current state of the request.
											const char* buffer,        	// The file's bytes so far, NULL if state m_directories;
	Lst::Search< Directory > m_next_dir_sh;
	Directory	*m_cur_dir;
	uint32		m_vault_type;
	char		m_vault_root[64];

	uint32		m_transfer_failed_script;
	uint32		m_transfer_complete_script;
	uint32		m_update_progress_script;
	uint32		m_file_not_found_script;
	ghttpCompletedCallback	m_dl_complete;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

}	// namespace GameNet

#endif	// __P_CONTENT_H

================================================
FILE: Code/Sk/GameNet/Ngps/p_ezcommon.h
================================================
/*
//  ------------------------------------------------------------------------

File: common.h

shared headers between EE and IOP projects.

//  ------------------------------------------------------------------------
*/

#ifndef	_COMMON_H
#define	_COMMON_H


/*  --------------------------------------------------------
 *  constants
 *  --------------------------------------------------------
 */

#define kModName_ezNetCnf "eznetcnf"
#define kModName_ezNetCtl "eznetctl"

/*
 *  SIF RPC identifier (aka SIFNUM)
 *  used with sceSifRegisterRpc() and sceSifBindRpc()
 */
#define kRPCId_ezNetCnf	0x75499128
#define kRPCId_ezNetCtl	0x75488909

/*
 *  SIF RPC buffer size
 */
#define kRPCBufferSize  (256 + 64)


/*
 *  SIF DMA are performed in 16-byte blocks from IOP -> EE.
 *  enforce padding in both DMA directions.
 */
#define kDMAWait        0
#define kDMANoWait      1
#define kDMABlockSize   16
#define DMA_PAD(_size)  (((_size) + (kDMABlockSize-1)) & ~(kDMABlockSize-1))


/*  --------------------------------------------------------
 *  enums
 *  --------------------------------------------------------
 */

/*
 *  eznetcnf RPC function ids
 */
typedef enum {
  eCnfGetCount = 0,
  eCnfGetCombinationList,
  eCnfGetEnvData,

  eCnfEnd = 0x1ffff           // for enum size consistency
} ezNetCnfFunctionId;

/*
 *  eznetctl RPC function ids
 */
typedef enum {
  eCtlGetEnvDataAddress = 0,
  eCtlSetConfiguration,
  eCtlSetCombination,
  eCtlWatchStatus,
  eCtlDownInterface,

  eCtlEnd = 0x1ffff           // for enum size consistency
} ezNetCtlFunctionId;

/*
 *  eznetcnf connection types (deprecated)
 */
typedef enum {
  eConnInvalid = 0,           // invalid combination of devType and ifcType
  eConnStatic,                // Ethernet (static IP)
  eConnDHCP,                  // Ethernet (DHCP)
  eConnPPPoE,                 // Ethernet (PPP over Ethernet)
  eConnPPP,                   // modem (PPP)
  eConnAOLPPP,                // modem (AOL PPP)

  eConnPadding = 0x1ffff      // for enum size consistency
} ezConnectionType;

/*
 *  network interface protocol family
 */
typedef enum {
  eInterfacePPP,      // modem PPP
  eInterfacePPPoE,    // PPP over Ethernet
  eInterfaceARP       // Ethernet (static IP and DHCP)
} ezInterfaceType;

/*
 *  netcnf file types
 */
typedef enum {
  eCombinationFile = 0,       // net???.cnf
  eInterfaceFile,             // ifc???.cnf
  eDeviceFile,                // dev???.cnf

  eFilePadding = 0x1ffff      // for enum size consistency
} ezNetCnfType;


/*  --------------------------------------------------------
 *  typedefs
 *  --------------------------------------------------------
 */

#define kMaxCombinations  10  // maximum number of Combinations
#define kStrBufferSize    256 // maximum size for any text field
#define kStrDisplaySize   32  // display size for any text field


typedef struct ezNetCnfCombination {
  int isActive;
  ezConnectionType connectionType;
  char combinationName[kStrDisplaySize];
  char ifcName[kStrDisplaySize];
  char devName[kStrDisplaySize];
} ezNetCnfCombination_t;


typedef struct ezNetCnfCombinationList {
  int listLength;
  unsigned int defaultIndex;
  unsigned int netdbOrder[kMaxCombinations];
  ezNetCnfCombination_t list[kMaxCombinations];
} ezNetCnfCombinationList_t __attribute__((aligned(64)));


typedef struct ezNetCtlStatus {
  u_int clientAddr;
  int id;
  int event;
  int state;
  char message[kStrBufferSize];
} ezNetCtlStatus_t __attribute__((aligned(64)));


/*
 *  RPC buffer type for ezNetCnfGetCount()
 */
typedef struct {
  int result;
  ezNetCnfType fileType;
  char netdbPath[kStrBufferSize];
} rpcGetCount_t;

/*
 *  RPC buffer type for ezNetCnfGetCombinationList()
 */
typedef struct {
  int result;
  u_int clientAddr;
  char netdbPath[kStrBufferSize];
} rpcGetCombinationList_t;

/*
 *  RPC buffer type for ezNetCnfGetEnvData()
 */
typedef struct {
  int result;
  u_int clientAddr;
  char netdbPath[kStrBufferSize];
  char combinationName[kStrDisplaySize];
} rpcGetEnvData_t;

#endif	// _COMMON_H



================================================
FILE: Code/Sk/GameNet/Ngps/p_ezconfig.h
================================================
/*
//  ------------------------------------------------------------------------

File: config.h

collection of global variables used for runtime configuration.

//  ------------------------------------------------------------------------
*/

#ifndef _CONFIG_H
#define _CONFIG_H


/*  --------------------------------------------------------
 *  global variables/constants
 *  --------------------------------------------------------
 *  choose between:
 *  -> declaration (GLOBAL undefined), or
 *  -> definition  (GLOBAL defined)
 *
 *  GLOBAL should be defined in exactly one file (per project).
 */

#ifdef GLOBAL
  #define EXTERN
#else
  #define EXTERN extern
#endif


    //  select one of the following NET_DB defines.
    //  note that the host0:net.db is unencoded.
EXTERN  char *g_netdb
#ifdef  GLOBAL
        = "mc0:";
//        = "mc1:";
//        = "host0:/usr/local/sce/conf/net/net.db";
//        = "host0:../conf/good/net.db";
#endif
;


    //  specify path to SCE-supplied modules
EXTERN  char *g_modRoot	
#ifdef  GLOBAL
        = "host0:/usr/local/sce/iop/modules/"
#endif
;


EXTERN const char *kConnectionTypeDescs[]
#ifdef  GLOBAL
=
{
  "invalid",
  "Ethernet, static IP",
  "Ethernet, DHCP",
  "Ethernet, PPPoE",
  "Modem, PPP",
  "Modem, AOL PPP"
}
#endif
;


/*  --------------------------------------------------------
 *  debugging information enable/disable
 *  --------------------------------------------------------
 */

#define DEBUG


    //  use dprintf() for non-essential debug messages only.
#ifdef  DEBUG
#define dprintf(_args...) \
        printf("%s> ", __FILE__); \
        printf(_args);
#else
#define dprintf(_args...)
#endif  // DEBUG


#endif  // _CONFIG_H



================================================
FILE: Code/Sk/GameNet/Ngps/p_ezmain.h
================================================
/*  
//  ------------------------------------------------------------------------

File: main.h

headers for EE library components.

//  ------------------------------------------------------------------------
*/

#ifndef _MAIN_H
#define _MAIN_H

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 


//  only present after 2.4.0
// manually define SCE_LIBRARY_VERSION if necessary.
#if (0)
#define SCE_LIBRARY_VERSION     0x2340
#else
#include 
#endif

#include "p_netcnfif.h"

#include "p_ezcommon.h"
#include "p_ezconfig.h"
//#include "util.h"
#include "p_libezcnf.h"
//#include "libezctl.h"
//#include "trc_hdd.h"


#endif  // _MAIN_H


================================================
FILE: Code/Sk/GameNet/Ngps/p_libezcnf.cpp
================================================
/*
//  ------------------------------------------------------------------------

File: libezcnf.c

//  ------------------------------------------------------------------------
*/

#include "p_ezmain.h"


static int m_rpcRunning = 0;
static sceSifClientData m_rpcCd;;
static int m_rpcBuf[kRPCBufferSize/4] __attribute__((aligned(64)));


#ifdef  DEBUG
#define ASSERT_RPC(_size)           \
  if (!m_rpcRunning) {              \
    printf("**** RPC services are not currently running\n"); \
    return -1;                      \
  }                                 \
  if (_size > kRPCBufferSize) {     \
    printf("**** RPC data size 0x%x exceeds RPC buffer size 0x%x\n", \
           _size, kRPCBufferSize);  \
    return -1;                      \
  }
#else
#define ASSERT_RPC(_size)
#endif	// DEBUG


/*  --------------------------------------------------------
 *  PUBLIC library initialization and finalization
 *  --------------------------------------------------------
 */

/*
 *  binds to eznetcnf.irx RPC services.
 *  r == 1  after RPC is bound.
 */
int
initEzNetCnf( void )
{
#ifdef  DEBUG
  int id = sceSifSearchModuleByName(kModName_ezNetCnf);
  if (id < 0) {
    printf("\"%s\" module not found (%d)\n", kModName_ezNetCnf, id);
  }
#endif

  // bind RPC
  while (1) {
    int i = 0x10000;
    if (sceSifBindRpc(&m_rpcCd, kRPCId_ezNetCnf, 0) < 0) {
      printf("could not bind to eznetcnf service\n");
    }
    if (m_rpcCd.serve != 0) break;
    while (i--)
      ;
  }

  return m_rpcRunning = 1;
}

/*
 *  exit and unload eznetcnf.irx, then unbind RPC services.
 *  r == 0  after RPC is unbound.
 */
int
exitEzNetCnf( void )
{
  if (m_rpcRunning == 1) {
    // call using non-blocking mode,
    // since eznetcnf unloads and the call cannot return.
    sceSifCallRpc(&m_rpcCd, eCnfEnd, SIF_RPCM_NOWAIT, 
                  0, 0, 0, 0, 0, 0);
    // not sure if this is necessary.
    sceSifInitRpc(0);
  }

  return m_rpcRunning = 0;
}


/*  --------------------------------------------------------
 *  PUBLIC library interfaces
 *  --------------------------------------------------------
 */

/*
 *  given the path to a netdb database, and a netcnf file type,
 *  return the number of files listed in the database. note
 *  sceNetCnfGetCount() returns 0 normally when no netdb file
 *  can be found, or even when there is no Memory Card.
 *
 *  r >  0  for valid netdb with at least 1 Combination,
 *  r <= 0  for error or no Combinations.
 */
int 
ezNetCnfGetCount(const char *netdb, ezNetCnfType type)
{
  rpcGetCount_t *rpcData = (rpcGetCount_t *)&m_rpcBuf;
  
  ASSERT_RPC(sizeof(rpcGetCount_t));
  
  // pack RPC data
  strcpy(rpcData->netdbPath, netdb);
  rpcData->fileType = type;
  
  // call RPC
  sceSifCallRpc(&m_rpcCd, eCnfGetCount, 
                0,
                &m_rpcBuf, sizeof(rpcGetCount_t),
                &m_rpcBuf, sizeof(rpcGetCount_t),
                0, 0);
  
  dprintf("ezNetCnfGetCount returned %d\n", rpcData->result);
  return rpcData->result;
}

/*
 *  given the path to a netdb database, retrieve a list of Combinations 
 *  to the memory address specified at addr. this list contains all the 
 *  information needed to present to the user.
 *
 *  r == 6  for valid Memory Card netdb with at least 1 Combination,
 *  r == 10 for valid PFS netdb with at least 1 Combination,
 *  r == N  for valid host netdb with 0 < N <= 10 Combinations,
 *  r <= 0  for error or no Combinations.
 */
int
ezNetCnfGetCombinationList(char *netdb, ezNetCnfCombinationList_t *addr)
{
  rpcGetCombinationList_t *rpcData = (rpcGetCombinationList_t *)&m_rpcBuf;

  ASSERT_RPC(sizeof(rpcGetCombinationList_t));
  
  // pack RPC data
  strcpy(rpcData->netdbPath, netdb);
  rpcData->clientAddr = (u_int)addr;
  
  // call RPC
  sceSifCallRpc(&m_rpcCd, eCnfGetCombinationList, 
                0,
                &m_rpcBuf, sizeof(rpcGetCombinationList_t),
                &m_rpcBuf, sizeof(rpcGetCombinationList_t),
                0, 0);
  
  dprintf("ezNetCnfGetCombinationList returned %d\n", rpcData->result);
  return rpcData->result;
}

/*
 *  given a previously generated list of combination configurations,
 *  return the Combination designated as the "default" in netdb.
 *
 *  r != NULL   for valid pList,
 *  r == NULL   for empty or invalid pList.
 */
ezNetCnfCombination_t *
ezNetCnfGetDefault(ezNetCnfCombinationList_t *pList)
{
  // check for valid parameters
  if (pList == NULL || pList->listLength == 0) {
    printf("ezNetCnfGetDefault: pList is empty\n");
    return NULL;
  }
  if (pList->defaultIndex >= (unsigned int) pList->listLength) {
    printf("ezNetCnfGetDefault: invalid defaultIndex (%d)\n",
           pList->defaultIndex);
    return NULL;
  }

  return &pList->list[pList->defaultIndex];
}

/*
 *  given the list of combinations and an ordinal number (1..N),
 *  return "CombinationN" -- REMEMBER to adjust zero-based indices.
 *
 *  r != NULL   for valid pList and valid number,
 *  r == NULL   for empty or invalid pList, or invalid number.
 */
ezNetCnfCombination_t *
ezNetCnfGetCombination(ezNetCnfCombinationList_t *pList, int number)
{
  // check for valid parameters
  if (pList == NULL || pList->listLength == 0) {
    printf("ezNetCnfGetCombination: pList is empty\n");
    return NULL;
  }
  if ((number < 1) || (number > pList->listLength)) {
    printf("ezNetCnfGetCombination: invalid Combination number (%d)\n",
           number);
    return NULL;
  }
    
  return &pList->list[number-1];
}

/*
 *  given the netdb path and a designated Combination, read and convert
 *  the data to a sceNetcnfifData record at the specified memory address.
 *  this data will contain all essential configuration properties.
 *
 *  r == 0  for successfully loading Combination,
 *  r <  0  for error.
 */
int
ezNetCnfGetEnvData(char *netdb, char *combinationName, sceNetcnfifData_t *addr)
{
  rpcGetEnvData_t *rpcData = (rpcGetEnvData_t *)&m_rpcBuf;

  ASSERT_RPC(sizeof(rpcGetEnvData_t));
  // pack the RPC request structure
  strcpy(rpcData->netdbPath, netdb);
  strcpy(rpcData->combinationName, combinationName);
  rpcData->clientAddr = (u_int)addr;

  // call RPC
  sceSifCallRpc(&m_rpcCd, eCnfGetEnvData, 
                0,
                &m_rpcBuf, sizeof(rpcGetEnvData_t),
                &m_rpcBuf, sizeof(rpcGetEnvData_t),
                0, 0);
  
  dprintf("ezNetCnfGetEnvData returned %d\n", rpcData->result);
  return rpcData->result;
}




================================================
FILE: Code/Sk/GameNet/Ngps/p_libezcnf.h
================================================
/*  
//  ------------------------------------------------------------------------

File: libezcnf.h

headers for libezcnf.c

//  ------------------------------------------------------------------------
*/

#ifndef _LIBEZCNF_H
#define _LIBEZCNF_H

int initEzNetCnf(void);
int exitEzNetCnf(void);

int ezNetCnfGetCount(const char *, ezNetCnfType);
int ezNetCnfGetCombinationList(char *, ezNetCnfCombinationList_t *);
ezNetCnfCombination_t *ezNetCnfGetCombination(ezNetCnfCombinationList_t *, int);
ezNetCnfCombination_t *ezNetCnfGetDefault(ezNetCnfCombinationList_t *);
int ezNetCnfGetEnvData(char *, char *, sceNetcnfifData_t *);

#endif	// _LIBEZCNF_H



================================================
FILE: Code/Sk/GameNet/Ngps/p_netcnfif.h
================================================
/* SCE CONFIDENTIAL
 "PlayStation 2" Programmer Tool Runtime Library Release 2.5
 */
/* 
 *      Netcnf Interface Library
 *
 *                         Version 1.2
 *                         Shift-JIS
 *
 *      Copyright (C) 2002 Sony Computer Entertainment Inc.
 *                        All Rights Reserved.
 *
 *                         netcnfif.h
 *
 *       Version        Date            Design      Log
 *  --------------------------------------------------------------------
 *       1.1            2002.01.28      tetsu       First version
 *       1.2            2002.02.10      tetsu       Add SCE_NETCNFIF_CHECK_ADDITIONAL_AT
 *                                                  Add sceNETCNFIF_TOO_LONG_STR
 */

#ifndef __netcnfif_common_h_
#define __netcnfif_common_h_

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/* Sifrpc p */
#define SCE_NETCNFIF_SSIZE     (4096)       /* Mf[^obt@̃TCY */
#define SCE_NETCNFIF_INTERFACE (0x80001101) /* NGXgʎq */

/* Sifrpc T[rX֐pԍ */
#define SCE_NETCNFIF_GET_COUNT           (0)   /* lbg[Nݒt@C̐擾 */
#define SCE_NETCNFIF_GET_LIST            (1)   /* lbg[Nݒt@C̃Xg擾 */
#define SCE_NETCNFIF_LOAD_ENTRY          (2)   /* lbg[Nݒt@C̓e擾 */
#define SCE_NETCNFIF_ADD_ENTRY           (3)   /* lbg[Nݒt@C̒lj */
#define SCE_NETCNFIF_EDIT_ENTRY          (4)   /* lbg[Nݒt@C̕ҏW */
#define SCE_NETCNFIF_DELETE_ENTRY        (5)   /* lbg[Nݒt@C̍폜 */
#define SCE_NETCNFIF_SET_LATEST_ENTRY    (6)   /* lbg[Nݒt@C̃XgҏW */
#define SCE_NETCNFIF_DELETE_ALL          (7)   /* Ȃ̃lbg[Nݒt@C폜 */
#define SCE_NETCNFIF_CHECK_CAPACITY      (8)   /* foCX̎ceʂ`FbN */
#define SCE_NETCNFIF_CHECK_ADDITIONAL_AT (9)   /* lj AT R}h`FbN */
#define SCE_NETCNFIF_GET_ADDR            (100) /* IOP ̎M̈(sceNetcnfifData)̃AhX擾 */
#define SCE_NETCNFIF_ALLOC_WORKAREA      (101) /* IOP ̃[NGAm */
#define SCE_NETCNFIF_FREE_WORKAREA       (102) /* IOP ̃[NGA */
#define SCE_NETCNFIF_SET_ENV             (103) /* IOP  sceNetCnfEnv ̈ sceNetcnfifData ̓eݒ */

/* G[R[h(-18 ܂ł netcnf.irx Ɠ) */
#define sceNETCNFIF_NG               (-1)   /* ̑̃G[ */
#define sceNETCNFIF_ALLOC_ERROR      (-2)   /* ̊mۂɎs */
#define sceNETCNFIF_OPEN_ERROR       (-3)   /* t@CJȂ */
#define sceNETCNFIF_READ_ERROR       (-4)   /* ǂݍ݂Ɏs */
#define sceNETCNFIF_WRITE_ERROR      (-5)   /* ݂Ɏs */
#define sceNETCNFIF_SEEK_ERROR       (-6)   /* t@CTCY擾Ɏs */
#define sceNETCNFIF_REMOVE_ERROR     (-7)   /* 폜Ɏs */
#define sceNETCNFIF_ENTRY_NOT_FOUND  (-8)   /* ݒ肪Ȃ */
#define sceNETCNFIF_INVALID_FNAME    (-9)   /* ݒǗt@C̃pXs */
#define sceNETCNFIF_INVALID_TYPE     (-10)  /* Ȃ̃lbg[Nݒt@C̎ނs */
#define sceNETCNFIF_INVALID_USR_NAME (-11)  /* ݒ薼s */
#define sceNETCNFIF_TOO_MANY_ENTRIES (-12)  /* ݒ萔ő吔ɒBĂ */
#define sceNETCNFIF_ID_ERROR         (-13)  /* ID 擾łȂ */
#define sceNETCNFIF_SYNTAX_ERROR     (-14)  /* ݒes */
#define sceNETCNFIF_MAGIC_ERROR      (-15)  /*  "PlayStation 2" ŕۑꂽݒ */
#define sceNETCNFIF_CAPACITY_ERROR   (-16)  /* foCX̎ceʂȂ */
#define sceNETCNFIF_UNKNOWN_DEVICE   (-17)  /* m̃foCXw肳Ă */
#define sceNETCNFIF_IO_ERROR         (-18)  /* IO G[ */
#define sceNETCNFIF_TOO_LONG_STR     (-19)  /* w肳ꂽ񂪒 */
#define sceNETCNFIF_NO_DATA          (-100) /* f[^ݒ肳ĂȂ */

/* Netcnf Interface ɕKvȃf[^ */
typedef struct sceNetcnfifArg{
    int data;               /* ̑̈/̑̕Ԃl */
    int f_no_decode;        /* no_decode tO */
    int type;               /* Ȃ̃lbg[Nݒt@C̎ */
    int addr;               /* EE ̎M̈̃AhX/IOP AhX̕Ԃl */
    char fname[256];        /* ݒǗt@C̃pX/lj AT R}h */
    char usr_name[256];     /* ݒ薼 */
    char new_usr_name[256]; /* Vݒ薼 */
} sceNetcnfifArg_t;

enum
{
    sceNetcnfifArg_f_no_decode_off, /* f_no_decode ݒ肵Ȃ */
    sceNetcnfifArg_f_no_decode_on   /* f_no_decode ݒ肷 */
};

enum
{
    sceNetcnfifArg_type_net, /* gݍ킹 */
    sceNetcnfifArg_type_ifc, /* ڑvoC_ݒ */
    sceNetcnfifArg_type_dev  /* ڑ@ݒ */
};

/* Ȃ̃lbg[Nݒt@C̃Xg */
typedef struct sceNetcnfifList{
    int type;           /* Ȃ̃lbg[Nݒt@C̎ */
    int stat;           /* t@CXe[^X */
    char sys_name[256]; /* t@C */
    char usr_name[256]; /* ݒ薼 */
    int p0[14];         /* \̈0 */
} sceNetcnfifList_t __attribute__((aligned(64)));

/* Ȃ̃lbg[Nݒt@Cɕۑf[^ */
typedef struct sceNetcnfifData{
    char attach_ifc[256];      /* ڑvoC_ݒt@C(net) */
    char attach_dev[256];      /* ڑ@ݒt@C(net) */
    char dhcp_host_name[256];  /* DHCPpzXg(ifc) */
    char address[256];         /* IPAhX(ifc) */
    char netmask[256];         /* lbg}XN(ifc) */
    char gateway[256];         /* ftHg[^(ifc) */
    char dns1_address[256];    /* vC}DNS(ifc) */
    char dns2_address[256];    /* ZJ_DNS(ifc) */
    char phone_numbers1[256];  /* ڑdbԍ1(ifc) */
    char phone_numbers2[256];  /* ڑdbԍ2(ifc) */
    char phone_numbers3[256];  /* ڑdbԍ3(ifc) */
    char auth_name[256];       /* [UID(ifc) */
    char auth_key[256];        /* pX[h(ifc) */
    char peer_name[256];       /* ڑ̔Fؖ(ifc) */
    char vendor[256];          /* x_(dev) */
    char product[256];         /* v_Ng(dev) */
    char chat_additional[256]; /* ljATR}h(dev) */
    char outside_number[256];  /* OMԍݒ(ԍݒ蕔)(dev) */
    char outside_delay[256];   /* OMԍݒ(xݒ蕔)(dev) */
    int ifc_type;              /* foCXC̎(ifc) */
    int mtu;                   /* MTU̐ݒ(ifc) */
    int ifc_idle_timeout;      /* ؒfݒ(ifc) */
    int dev_type;              /* foCXC̎(dev) */
    int phy_config;            /* C[Tlbgڑ@̓샂[h(dev) */
    int dialing_type;          /* _CA@(dev) */
    int dev_idle_timeout;      /* ؒfݒ(dev) */
    int p0;                    /* \̈0 */
    unsigned char dhcp;        /* DHCPgpEsgp(ifc) */
    unsigned char dns1_nego;   /* DNST[oAhX擾EȂ(ifc) */
    unsigned char dns2_nego;   /* DNST[oAhX擾EȂ(ifc) */
    unsigned char f_auth;      /* Fؕ@̎wL(ifc) */
    unsigned char auth;        /* Fؕ@(ifc) */
    unsigned char pppoe;       /* PPPoEgpEsgp(ifc) */
    unsigned char prc_nego;    /* PFClSVG[V̋֎~(ifc) */
    unsigned char acc_nego;    /* ACFClSVG[V̋֎~(ifc) */
    unsigned char accm_nego;   /* ACCMlSVG[V̋֎~(ifc) */
    unsigned char p1;          /* \̈1 */
    unsigned char p2;          /* \̈2 */
    unsigned char p3;          /* \̈3 */
    int p4[5];                 /* \̈4 */
} sceNetcnfifData_t __attribute__((aligned(64)));

enum
{
    sceNetcnfifData_type_no_set = -1, /* ݒ肵Ȃ */
    sceNetcnfifData_type_eth = 1,     /* type eth */
    sceNetcnfifData_type_ppp,         /* type ppp */
    sceNetcnfifData_type_nic          /* type nic */
};

enum
{
    sceNetcnfifData_mtu_no_set = -1,   /* ݒ肵Ȃ */
    sceNetcnfifData_mtu_default = 1454 /* mtu 1454 */
};

enum
{
    sceNetcnfifData_idle_timeout_no_set = -1 /* ݒ肵Ȃ */
};

enum
{
    sceNetcnfifData_phy_config_no_set = -1, /* ݒ肵Ȃ */
    sceNetcnfifData_phy_config_auto = 1,    /* phy_config auto */
    sceNetcnfifData_phy_config_10,          /* phy_config 10 */
    sceNetcnfifData_phy_config_10_FD,       /* phy_config 10_fd */
    sceNetcnfifData_phy_config_TX = 5,      /* phy_config tx */
    sceNetcnfifData_phy_config_TX_FD        /* phy_config tx_fd */
};

enum
{
    sceNetcnfifData_dialing_type_no_set = -1, /* ݒ肵Ȃ */
    sceNetcnfifData_dialing_type_tone = 0,    /* dialing_type tone */
    sceNetcnfifData_dialing_type_pulse        /* dialing_type pulse */
};

enum
{
    sceNetcnfifData_dhcp_no_set = 0xff, /* ݒ肵Ȃ */
    sceNetcnfifData_dhcp_no_use = 0,    /* -dhcp */
    sceNetcnfifData_dhcp_use            /* dhcp */
};

enum
{
    sceNetcnfifData_dns_nego_no_set = 0xff, /* ݒ肵Ȃ */
    sceNetcnfifData_dns_nego_on = 1         /* want.dns1_nego/want.dns2_nego */
};

enum
{
    sceNetcnfifData_f_auth_off, /* allow.auth chap/pap ݒ肵Ȃ */
    sceNetcnfifData_f_auth_on   /* allow.auth chap/pap ݒ肷 */
};

enum
{
    sceNetcnfifData_auth_chap_pap = 4 /* allow.auth chap/pap */
};

enum
{
    sceNetcnfifData_pppoe_no_set = 0xff, /* ݒ肵Ȃ */
    sceNetcnfifData_pppoe_use = 1        /* pppoe */
};

enum
{
    sceNetcnfifData_prc_nego_no_set = 0xff, /* ݒ肵Ȃ */
    sceNetcnfifData_prc_nego_off = 0        /* -want.prc_nego */
};

enum
{
    sceNetcnfifData_acc_nego_no_set = 0xff, /* ݒ肵Ȃ */
    sceNetcnfifData_acc_nego_off = 0        /* -want.acc_nego */
};

enum
{
    sceNetcnfifData_accm_nego_no_set = 0xff, /* ݒ肵Ȃ */
    sceNetcnfifData_accm_nego_off = 0        /* -want.accm_nego */
};

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /*__netcnfif_common_h_ */


================================================
FILE: Code/Sk/GameNet/Ngps/p_netconfig.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet					 								**
**																			**
**	File name:		p_netconfig.cpp											**
**																			**
**	Created by:		04/17/02	-	spg										**
**																			**
**	Description:	Netcnf wrapper											**
**																			**
*****************************************************************************/

// start autoduck documentation
// @DOC p_netconfig
// @module p_netconfig | None
// @subindex Scripting Database
// @index script | p_netconfig
                          
/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#define GLOBAL 1
#include 


#include 
#include 
#include 
#include 
#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

#define sceNETCNF_MAGIC_ERROR		(-15)

namespace GameNet
{


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

	/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void sceNetcnfifDataDump(sceNetcnfifData_t *data)
{
    Dbg_Printf("-----------------------\n");
    Dbg_Printf("attach_ifc       : \"%s\"\n", data->attach_ifc);
    Dbg_Printf("attach_dev       : \"%s\"\n", data->attach_dev);
    Dbg_Printf("dhcp_host_name   : \"%s\"\n", data->dhcp_host_name);
    Dbg_Printf("address          : \"%s\"\n", data->address);
    Dbg_Printf("netmask          : \"%s\"\n", data->netmask);
    Dbg_Printf("gateway          : \"%s\"\n", data->gateway);
    Dbg_Printf("dns1_address     : \"%s\"\n", data->dns1_address);
    Dbg_Printf("dns2_address     : \"%s\"\n", data->dns2_address);
    Dbg_Printf("phone_numbers1   : \"%s\"\n", data->phone_numbers1);
    Dbg_Printf("phone_numbers2   : \"%s\"\n", data->phone_numbers2);
    Dbg_Printf("phone_numbers3   : \"%s\"\n", data->phone_numbers3);
    Dbg_Printf("auth_name        : \"%s\"\n", data->auth_name);
    Dbg_Printf("auth_key         : \"%s\"\n", data->auth_key);
    Dbg_Printf("peer_name        : \"%s\"\n", data->peer_name);
    Dbg_Printf("vendor           : \"%s\"\n", data->vendor);
    Dbg_Printf("product          : \"%s\"\n", data->product);
    Dbg_Printf("chat_additional  : \"%s\"\n", data->chat_additional);
    Dbg_Printf("outside_number   : \"%s\"\n", data->outside_number);
    Dbg_Printf("outside_delay    : \"%s\"\n", data->outside_delay);
    Dbg_Printf("ifc_type         : \"%d\"\n", data->ifc_type);
    Dbg_Printf("mtu              : \"%d\"\n", data->mtu);
    Dbg_Printf("ifc_idle_timeout : \"%d\"\n", data->ifc_idle_timeout);
    Dbg_Printf("dev_type         : \"%d\"\n", data->dev_type);
    Dbg_Printf("phy_config       : \"%d\"\n", data->phy_config);
    Dbg_Printf("dialing_type     : \"%d\"\n", data->dialing_type);
    Dbg_Printf("dev_idle_timeout : \"%d\"\n", data->dev_idle_timeout);
    Dbg_Printf("dhcp             : \"%d\"\n", data->dhcp);
    Dbg_Printf("dns1_nego        : \"%d\"\n", data->dns1_nego);
    Dbg_Printf("dns2_nego        : \"%d\"\n", data->dns2_nego);
    Dbg_Printf("f_auth           : \"%d\"\n", data->f_auth);
    Dbg_Printf("auth             : \"%d\"\n", data->auth);
    Dbg_Printf("pppoe            : \"%d\"\n", data->pppoe);
    Dbg_Printf("prc_nego         : \"%d\"\n", data->prc_nego);
    Dbg_Printf("acc_nego         : \"%d\"\n", data->acc_nego);
    Dbg_Printf("accm_nego        : \"%d\"\n", data->accm_nego);
    Dbg_Printf("-----------------------\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*
 *  given a combination configuration, display its details.
 */
static void showConfiguration(ezNetCnfCombination_t *p_conf, sceNetcnfifData_t *p_data)
{
	if (p_conf == NULL) 
	{
		Dbg_Printf("showConfiguration: p_conf is NULL\n");
		return;
	}
  
	if (p_conf->isActive) 
	{
		Dbg_Printf("ezNetCnfCombination_t ------------\n");
		Dbg_Printf("net display name\t%s\n", p_conf->combinationName);
		Dbg_Printf("ifc display name\t%s\n", p_conf->ifcName);
		Dbg_Printf("dev display name\t%s\n", p_conf->devName);
		Dbg_Printf("type\t\t%s\n", kConnectionTypeDescs[p_conf->connectionType]);
//#if (SCE_LIBRARY_VERSION >= 0x2500)
		sceNetcnfifDataDump(p_data);
//#endif
	} 
	else 
	{
		Dbg_Printf("\tempty Combination slot\n");
	}

	Dbg_Printf("\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptLoadNetConfigs(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
    ezNetCnfCombination_t *p_combination = NULL;
	int i, err;

	Dbg_Printf( "****************** LOADING NETWORK CONFIG!!!! ************************ \n" );

	static bool once = false;
	if( !once )
	{
		SIO::LoadIRX("eznetcnf" );	// net config stuff
		initEzNetCnf();
	}

    err = ezNetCnfGetCombinationList( g_netdb, &gamenet_man->m_combination_list );
	if( err < 0 )
	{
		if( err != sceNETCNF_MAGIC_ERROR )
		{
			pScript->GetParams()->AddChecksum( NONAME, CRCD(0x492676a6,"corrupt"));
		}
		Dbg_Printf( "ezNetCnfGetCombinationList error = %d\n", err );
		return false;
	}

	for( i = 0; i < gamenet_man->m_combination_list.listLength; i++ )
	{
		Dbg_Printf("getting combo %d\n", i + 1);
		p_combination = ezNetCnfGetCombination(&gamenet_man->m_combination_list, i + 1);
		if (p_combination != NULL && p_combination->isActive && ( p_combination->connectionType != eConnInvalid )) 
		{
			Dbg_Printf("found combo, %s\n", p_combination->combinationName);
			err = ezNetCnfGetEnvData(g_netdb, p_combination->combinationName, &gamenet_man->m_env_data[i] );
			if( err < 0 ) 
			{
				Dbg_Printf("ezNetCnfGetEnvData error = %d\n", err);
			}
			showConfiguration( p_combination, &gamenet_man->m_env_data[i] );
		}
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*ezNetCnfCombination_t*	Manager::GetNetworkConfig( int index )
{
	return mp_combinations[index];
}*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*void	Manager::ExitNetworkConfig( void )
{
	Dbg_Printf( "****************** EXITING NETWORK CONFIG!!!! ************************ \n" );
	// call using non-blocking mode,
	// since eznetcnf unloads and the call cannot return.
	sceSifCallRpc(&g_cd, eFuncEnd, SIF_RPCM_NOWAIT, 
	    		0, 0, 0, 0, 0, 0);
	// not sure if this is necessary.
	sceSifInitRpc(0);
}*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptNoNetConfigFiles(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	ezNetCnfCombination_t *p_combination;

	p_combination = ezNetCnfGetDefault(&gamenet_man->m_combination_list);
	if( p_combination != NULL && p_combination->isActive ) 
	{
		return false;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::ScriptFillNetConfigList(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	ezNetCnfCombination_t *p_combination;
	Script::CStruct* p_item_params;
	Script::CStruct* p_script_params;
	int i;
	char config_title[64];
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());

	for( i = 0; i < gamenet_man->m_combination_list.listLength; i++ )
	{
		// check the Default Combination
		p_combination = ezNetCnfGetCombination(&gamenet_man->m_combination_list, i + 1);
		if( p_combination != NULL && p_combination->isActive && ( p_combination->connectionType != eConnInvalid ))
		{
			sprintf( config_title, "%d %s", i + 1, p_combination->ifcName );
		
			p_item_params = new Script::CStruct;	
			p_item_params->AddString( "text", config_title );
			p_item_params->AddChecksum( "id", Script::GenerateCRC( "first_net_cfg" ) + i );
			p_item_params->AddChecksum( "centered", CRCD(0x2a434d05,"centered") );
			p_item_params->AddFloat( "scale", 0.8f );
			p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("choose_net_config"));
			p_item_params->AddChecksum( "focus_script",CRCD(0x6100d1f9,"main_theme_focus_noscale"));
			p_item_params->AddChecksum( "unfocus_script",CRCD(0xe9602a90,"main_theme_unfocus_noscale"));
			p_item_params->AddChecksum( "parent_menu_id", Script::GenerateCRC( "network_options_load_config_vmenu" ));
		
			// create the parameters that are passed to the X script
			p_script_params= new Script::CStruct;
			p_script_params->AddInteger( "index", i + 1 );	
			p_item_params->AddStructure("pad_choose_params",p_script_params);			
		
			Script::RunScript("theme_menu_add_item",p_item_params);
			delete p_item_params;
			delete p_script_params;
		}
	}

	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		Manager::ScriptChooseNetConfig(Script::CStruct *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Prefs::Preferences* prefs;
	Script::CScriptStructure* pTempStructure;
	ezNetCnfCombination_t *p_combination;
	int index;
    sceNetcnfifData* data;
	char outside_str[64];
	char full_phone[128];
	char full_name[256];
	bool dns_set;

	prefs = gamenet_man->GetNetworkPreferences();
	p_combination = NULL;
	data = NULL;
	if( pParams->GetInteger( "index", &index, false ))
	{
		data = &gamenet_man->m_env_data[index];
		p_combination = ezNetCnfGetCombination( &gamenet_man->m_combination_list, index );
		sprintf( full_name, "%d %s", index, p_combination->ifcName );
	}
	else
	{	
		const char* name;
		int i;
		bool found;

		pParams->GetString( "name", &name, Script::ASSERT );
		found = false;
		for( i = 0; i < gamenet_man->m_combination_list.listLength; i++ )
		{
			// check the Default Combination
			p_combination = ezNetCnfGetCombination(&gamenet_man->m_combination_list, i + 1 );
			
			if(	p_combination != NULL )
			{
				sprintf( full_name, "%d %s", i + 1, p_combination->ifcName );
			
				if(	( p_combination->isActive ) && 
					( strcmp( full_name, name ) == 0 ) && 
					( p_combination->connectionType != eConnInvalid ))
				{   
					index = i;
					found = true;
					data = &gamenet_man->m_env_data[index];
					break;
				}
			}
		}

		if( !found )
		{
			pTempStructure = new Script::CScriptStructure;
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, Script::GetString( "manual_settings_str" ));
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "config_manual" ));
			prefs->SetPreference( Script::GenerateCRC("config_type"), pTempStructure );
			delete pTempStructure;

			Script::RunScript( "clear_net_options" );
			return false;
		}
	}

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, full_name );
	pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "config_sony" ));
	prefs->SetPreference( Script::GenerateCRC("config_type"), pTempStructure );
	delete pTempStructure;
	
	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, data->address );
	prefs->SetPreference( Script::GenerateCRC("ip_address"), pTempStructure );
	delete pTempStructure;

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, data->netmask );
	prefs->SetPreference( Script::GenerateCRC("subnet_mask"), pTempStructure );
	delete pTempStructure;

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, data->gateway );
	prefs->SetPreference( Script::GenerateCRC("gateway"), pTempStructure );
	delete pTempStructure;

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, data->dns1_address );
	prefs->SetPreference( Script::GenerateCRC("dns_server"), pTempStructure );
	delete pTempStructure;

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, data->dns2_address );
	prefs->SetPreference( Script::GenerateCRC("dns_server2"), pTempStructure );
	delete pTempStructure;

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, data->dhcp_host_name );
	prefs->SetPreference( Script::GenerateCRC("host_name"), pTempStructure );
	delete pTempStructure;

	sprintf( outside_str, "%s%s", data->outside_number, data->outside_delay );
	sprintf( full_phone, "%s%s", outside_str, data->phone_numbers1 );

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, full_phone );
	prefs->SetPreference( Script::GenerateCRC("dialup_number"), pTempStructure );
	delete pTempStructure;

	sprintf( full_phone, "%s%s", outside_str, data->phone_numbers2 );

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, full_phone );
	prefs->SetPreference( Script::GenerateCRC("dialup_number2"), pTempStructure );
	delete pTempStructure;

	sprintf( full_phone, "%s%s", outside_str, data->phone_numbers3 );

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, full_phone );
	prefs->SetPreference( Script::GenerateCRC("dialup_number3"), pTempStructure );
	delete pTempStructure;

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, data->auth_name );
	prefs->SetPreference( Script::GenerateCRC("dialup_username"), pTempStructure );
	delete pTempStructure;

	pTempStructure = new Script::CScriptStructure;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, data->auth_key );
	prefs->SetPreference( Script::GenerateCRC("dialup_password"), pTempStructure );
	delete pTempStructure;

	dns_set = false;
			
	if( ( strlen( data->dns1_address ) > 0 ) ||
		( strlen( data->dns2_address ) > 0 ))
	{
		dns_set = true;
	}

	switch((int) p_combination->connectionType )
	{
		case eConnStatic:
			pTempStructure = new Script::CScriptStructure;
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "Ethernet (Network Adaptor for PS2)" );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "device_broadband_pc" ));
			prefs->SetPreference( Script::GenerateCRC("device_type"), pTempStructure );
			delete pTempStructure;

			pTempStructure = new Script::CScriptStructure;
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "Static IP Address" );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "ip_static" ));
			prefs->SetPreference( Script::GenerateCRC("broadband_type"), pTempStructure );
			delete pTempStructure;

			pTempStructure = new Script::CScriptStructure;
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "No" );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "boolean_false" ));
			prefs->SetPreference( Script::GenerateCRC("auto_dns"), pTempStructure );
			delete pTempStructure;
			break;
		case eConnDHCP:
		{
			pTempStructure = new Script::CScriptStructure;
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "Ethernet (Network Adaptor for PS2)" );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "device_broadband_pc" ));
			prefs->SetPreference( Script::GenerateCRC("device_type"), pTempStructure );
			delete pTempStructure;

			pTempStructure = new Script::CScriptStructure;
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "Auto-Detect (DHCP)" );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "ip_dhcp" ));
			prefs->SetPreference( Script::GenerateCRC("broadband_type"), pTempStructure );
			delete pTempStructure;

			if( dns_set )
			{
				pTempStructure = new Script::CScriptStructure;
				pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "No" );
				pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "boolean_false" ));
				prefs->SetPreference( Script::GenerateCRC("auto_dns"), pTempStructure );
				delete pTempStructure;
			}
			else
			{
				pTempStructure = new Script::CScriptStructure;
				pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "Yes" );
				pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "boolean_true" ));
				prefs->SetPreference( Script::GenerateCRC("auto_dns"), pTempStructure );
				delete pTempStructure;
			}
			
			break;
		}
		case eConnPPPoE:
			pTempStructure = new Script::CScriptStructure;
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "Ethernet (Network Adaptor for PS2) (PPPoE)" );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "device_broadband_pc_pppoe" ));
			prefs->SetPreference( Script::GenerateCRC("device_type"), pTempStructure );
			delete pTempStructure;

			if( dns_set )
			{
				pTempStructure = new Script::CScriptStructure;
				pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "No" );
				pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "boolean_false" ));
				prefs->SetPreference( Script::GenerateCRC("auto_dns"), pTempStructure );
				delete pTempStructure;
			}
			else
			{
				pTempStructure = new Script::CScriptStructure;
				pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "Yes" );
				pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "boolean_true" ));
				prefs->SetPreference( Script::GenerateCRC("auto_dns"), pTempStructure );
				delete pTempStructure;
			}
			break;
		case eConnPPP:
		case eConnAOLPPP:
			pTempStructure = new Script::CScriptStructure;
			pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "Modem (Network Adaptor for PS2)" );
			pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "device_sony_modem" ));
			prefs->SetPreference( Script::GenerateCRC("device_type"), pTempStructure );
			delete pTempStructure;
			if( dns_set )
			{
				pTempStructure = new Script::CScriptStructure;
				pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "No" );
				pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "boolean_false" ));
				prefs->SetPreference( Script::GenerateCRC("auto_dns"), pTempStructure );
				delete pTempStructure;
			}
			else
			{
				pTempStructure = new Script::CScriptStructure;
				pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, "Yes" );
				pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, Script::GenerateCRC( "boolean_true" ));
				prefs->SetPreference( Script::GenerateCRC("auto_dns"), pTempStructure );
				delete pTempStructure;
			}
			break;
		default:
			Dbg_Printf( "************** GOT %d\n", (int) p_combination->connectionType );
			Dbg_Assert( 0 );
			break;
	}
    
	//Dbg_Printf("product          : \"%s\"\n", data->product);
    //Dbg_Printf("chat_additional  : \"%s\"\n", data->chat_additional);
    //Dbg_Printf("ifc_type         : \"%d\"\n", data->ifc_type);
    //Dbg_Printf("mtu              : \"%d\"\n", data->mtu);
    //Dbg_Printf("ifc_idle_timeout : \"%d\"\n", data->ifc_idle_timeout);
    //Dbg_Printf("dev_type         : \"%d\"\n", data->dev_type);
    //Dbg_Printf("phy_config       : \"%d\"\n", data->phy_config);
    //Dbg_Printf("dialing_type     : \"%d\"\n", data->dialing_type);
    //Dbg_Printf("dev_idle_timeout : \"%d\"\n", data->dev_idle_timeout);
    //Dbg_Printf("dhcp             : \"%d\"\n", data->dhcp);
    //Dbg_Printf("dns1_nego        : \"%d\"\n", data->dns1_nego);
    //Dbg_Printf("dns2_nego        : \"%d\"\n", data->dns2_nego);
    //Dbg_Printf("f_auth           : \"%d\"\n", data->f_auth);
    //Dbg_Printf("auth             : \"%d\"\n", data->auth);
    //Dbg_Printf("pppoe            : \"%d\"\n", data->pppoe);
    //Dbg_Printf("prc_nego         : \"%d\"\n", data->prc_nego);
    //Dbg_Printf("acc_nego         : \"%d\"\n", data->acc_nego);
    //Dbg_Printf("accm_nego        : \"%d\"\n", data->accm_nego);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet






================================================
FILE: Code/Sk/GameNet/Ngps/p_stats.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_stats.cpp												**
**																			**
**	Created by:		04/30/03	-	SPG										**
**																			**
**	Description:	PS2 Stat-tracking Code 									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

extern bool g_CheatsEnabled;

namespace GameNet
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define vSTATS_CONNECT_TIMEOUT Tmr::Seconds( 10 )
#define vSTATS_REPORT_INTERVAL Tmr::Minutes( 1 )
#define vMAX_STATS_FILE_SIZE ( 150 * 1024 );
#define vMAX_ENTRIES_PER_LEVEL 3
#define vSTATS_PLAYER_COUNT 50

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static 	Tmr::Time	s_connect_start_time = 0;
uint32	s_levels[] = { 	0xf6c822d4, 0x7276630a, 0xd7720de9, 0xee1c63cf, 0x399bd4e8, 
						0xd0f0229, 0x9a44ec8, 0x9db7727c, 0x991b5359, 
						0x73be7e94, 0xa7ff414b, 0x8ca30405 };
char	s_stats_files[vNUM_RATINGS_FILES][32] = {
	"hs_at.txt",
	"bc_at.txt",
	"hs_mo.txt",
	"bc_mo.txt",
	"ratings.txt"
};

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

class SortableStats : public Lst::Node< SortableStats >
{
public:
	SortableStats( void );

	char	m_Name[32];
	uint32	m_Level;
	int		m_Score;
	int		m_Rating;
};
/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

void	StatsMan::parse_ratings( char* buffer )
{
	StatsKeeper* keeper;
	char* token;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	keeper = &m_stats_keepers[vFILE_RATINGS];
	token = strtok( buffer, ":" );
    
	while( token )
	{
		StatsPlayer* player;

		player = new StatsPlayer;
		keeper->m_Players.AddToTail( player );
		player->m_Score = atoi( token );
		
		// Now parse, looking for the rating
		token = strtok( NULL, ":" );
		// Guard against corrupt files
		if( token == NULL )
		{
			Mem::Manager::sHandle().PopContext();
			return;
		}

		player->m_Rating = atoi( token );
		token = strtok( NULL, "\n" );
		// Guard against corrupt files
		if( token == NULL )
		{
			Mem::Manager::sHandle().PopContext();
			return;
		}

		strcpy( player->m_Name, token );
        
		// Now parse, looking for the next rating
		token = strtok( NULL, ":" );
	}

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsMan::parse_score_list( int type, char* buffer, bool read_date )
{
	StatsLevel* level;
	char* token;
	uint32 level_crc;

	Dbg_Assert( type <= vFILE_BEST_COMBO_MONTHLY );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

    if( read_date )
	{
		token = strtok( buffer, "\n" );

		strcpy( m_stats_keepers[type].m_Date, token );
		Dbg_Printf( "Date: %s\n", m_stats_keepers[type].m_Date );
		token = strtok( NULL, ":" );
	}
	else
	{
		token = strtok( buffer, ":" );
	}
	
	while( token && ( stricmp( token, "Level" ) == 0 ))
	{
		token = strtok( NULL, "\n" );
		// Guard against corrupt files
		if( token == NULL )
		{
			Mem::Manager::sHandle().PopContext();
			return;
		}
		level_crc = atoi( token );
		level = new StatsLevel;
		level->m_Level = level_crc;

		m_stats_keepers[type].m_Levels.AddToTail( level );

		// Now parse, looking for a list of players and scores
		token = strtok( NULL, ":" );
		// Guard against corrupt files
		if( token == NULL )
		{
			Mem::Manager::sHandle().PopContext();
			return;
		}

		// Read until we reach another "level" or EOF
		while( token && stricmp( token, "Level" ))
		{
			StatsPlayer* player;

			player = new StatsPlayer;

			level->m_Players.AddToTail( player );
			player->m_Score = atoi( token );
			Dbg_Printf( "Score %d  ", player->m_Score );
			token = strtok( NULL, ":" );
			// Guard against corrupt files
			if( token == NULL )
			{
				Mem::Manager::sHandle().PopContext();
				return;
			}
			player->m_Rating = atoi( token );
			Dbg_Printf( "Rating %d  ", player->m_Rating );
			token = strtok( NULL, "\n" );
			// Guard against corrupt files
			if( token == NULL )
			{
				Mem::Manager::sHandle().PopContext();
				return;
			}
			strcpy( player->m_Name, token );
			Dbg_Printf( "Name %s\n", player->m_Name );

			// Now parse, looking for the next level or player
			token = strtok( NULL, ":" );
		}
	}

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

GHTTPBool StatsMan::s_stats_file_dl_complete( GHTTPRequest request, GHTTPResult result, char* buffer,
											  int buffer_len, void * param )
{
	ContentMan* content_man;

	content_man = (ContentMan*) param;

	Dbg_Printf( "Got result %d\n", result );

	content_man->SetPercentComplete( 100 );
	if( result == GHTTPSuccess )
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		StatsMan* stats_man;

		content_man->SetResult( vCONTENT_RESULT_SUCCESS );
		content_man->SetError( 0 );
		
		stats_man = gamenet_man->mpStatsMan;
		switch( stats_man->m_cur_file_index )
		{
			case vFILE_HIGH_SCORE_ALL_TIME:
			case vFILE_BEST_COMBO_ALL_TIME:
				stats_man->parse_score_list( stats_man->m_cur_file_index, buffer, false );
				break;
			case vFILE_HIGH_SCORE_MONTHLY:
			case vFILE_BEST_COMBO_MONTHLY:
			{
				stats_man->parse_score_list( stats_man->m_cur_file_index, buffer, true );
				break;
			}
			case vFILE_RATINGS:
			{
				stats_man->parse_ratings( buffer );
				break;
			}
		}
	}
	else
	{
		content_man->SetResult( vCONTENT_RESULT_FAILURE );
		content_man->SetError( result );
	}
	
	return GHTTPTrue;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsMan::s_stats_logic_code( const Tsk::Task< StatsMan >& task )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	StatsMan& man = task.GetData();

	switch( man.m_state )
	{
		case vSTATS_STATE_WAITING:
			break;
		case vSTATS_STATE_CONNECTING:
			if(( Tmr::GetTime() - s_connect_start_time ) > vSTATS_CONNECT_TIMEOUT )
			{
				Script::RunScript( "StatsRetrievalFailedDialog" );
				man.Disconnect();
				break;
			}
			break;
		case vSTATS_STATE_CONNECTED:
			man.m_state = vSTATS_STATE_LOGGED_IN;
			man.Connected();
			break;
		case vSTATS_STATE_FAILED_LOG_IN:
			Script::RunScript( "StatsLoginFailedDialog" );
			man.Disconnect();
			break;
		case vSTATS_STATE_RETRIEVING:
			if(( Tmr::GetTime() - s_connect_start_time ) > vSTATS_CONNECT_TIMEOUT )
			{
				Script::RunScript( "StatsRetrievalFailedDialog" );
				man.Disconnect();
				break;
			}
			if( PersistThink() == 0 )
			{
				Script::RunScript( "StatsRetrievalFailedDialog" );
				man.Disconnect();
			}
			break;
		case vSTATS_STATE_LOGGED_IN:
			break;
		case vSTATS_STATE_IN_GAME:
			if(( Tmr::GetTime() - man.m_time_since_last_report ) > vSTATS_REPORT_INTERVAL )
			{
				man.ReportStats( false );
			}
			break;
	}


	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	StatsMan::s_threaded_stats_connect( StatsMan* stats_man )
{
	int result;

	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	// Register this thread with the sockets API
	sockAPIregthr();

    sprintf( gcd_gamename, "thps5ps2" );
	sprintf( gcd_secret_key, "G2k8cF" );
	Dbg_Printf( "About to connect to stats server...\n" );
	result = InitStatsConnection( GameNet::vHOST_PORT );
	switch( result )
	{
		case GE_NODNS:	// Unable to resolve stats server DNS
			gamenet_man->mpStatsMan->m_state = vSTATS_STATE_FAILED_LOG_IN;
			Dbg_Printf( "Error connecting to stats server: No DNS\n" );
			break;
		case GE_NOSOCKET: // Unable to create data socket
			gamenet_man->mpStatsMan->m_state = vSTATS_STATE_FAILED_LOG_IN;
			Dbg_Printf( "Error connecting to stats server: No Socket\n" );
			break;
		case GE_NOCONNECT: // Unable to connect to stats server
			gamenet_man->mpStatsMan->m_state = vSTATS_STATE_FAILED_LOG_IN;
			Dbg_Printf( "Error connecting to stats server: No Connect\n" );
			break;
		case GE_DATAERROR: // Unable to receive challenge from stats server, or bad challenge
			gamenet_man->mpStatsMan->m_state = vSTATS_STATE_FAILED_LOG_IN;
			Dbg_Printf( "Error connecting to stats server: Data error\n" );
			break;
		case GE_NOERROR: // Connected to stats server and ready to send data
			Dbg_Printf( "Connected to stats server\n" );
			gamenet_man->mpStatsMan->m_state = vSTATS_STATE_CONNECTED;
			break;
		default:
			Dbg_Assert( 0 );
			break;
	}

	// Deregister this thread with the sockets API
	sockAPIderegthr();

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

StatsMan::StatsMan( void )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	m_stats_logic_task = new Tsk::Task< StatsMan > ( s_stats_logic_code, *this );
	m_logged_in = false;
	m_state = vSTATS_STATE_WAITING;
	m_cur_game = NULL;
	m_need_to_retrieve_stats = true;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

StatsMan::~StatsMan( void )
{
	delete m_stats_logic_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsMan::Connected( void )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

	m_logged_in = true;
	Script::RunScript( "stats_logged_in" );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::IsLoggedIn( void )
{
	m_logged_in = ( IsStatsConnected() == 1 );

	return m_logged_in;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::NeedToRetrieveStats( void )
{
	return m_need_to_retrieve_stats;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsMan::Connect( void )
{   
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	Thread::PerThreadStruct	net_thread_data;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
	
	m_state = vSTATS_STATE_CONNECTING;
	s_connect_start_time = Tmr::GetTime();

	Script::RunScript( "stats_log_in_profile" );

    net_thread_data.m_pEntry = s_threaded_stats_connect;
	net_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
	net_thread_data.m_pStackBase = gamenet_man->GetNetThreadStack();
	net_thread_data.m_iStackSize = vNET_THREAD_STACK_SIZE;
	net_thread_data.m_utid = 0x152;
	Thread::CreateThread( &net_thread_data );
	gamenet_man->SetNetThreadId( net_thread_data.m_osId );
	StartThread( gamenet_man->GetNetThreadId(), this );

	if( !m_stats_logic_task->InList())
	{
		mlp_man->AddLogicTask( *m_stats_logic_task );
	}
	
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsMan::Disconnect( void )
{   
    CloseStatsConnection();
	m_stats_logic_task->Remove();
	m_logged_in = false;
	m_state = vSTATS_STATE_WAITING;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::ScriptStatsLoggedIn(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;

	stats_man = gamenet_man->mpStatsMan;
	return stats_man->IsLoggedIn();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::ScriptStatsLogIn(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		
	gamenet_man->mpStatsMan->Connect();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::ScriptStatsLogOff(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;

	stats_man = gamenet_man->mpStatsMan;
	stats_man->Disconnect();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::ScriptReportStats(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;
	bool final;

	final = pParams->ContainsFlag( CRCD(0x132aa339,"final"));
	stats_man = gamenet_man->mpStatsMan;
	stats_man->ReportStats( final );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsMan::s_stats_retrieval_callback( int localid, int profileid, persisttype_t type, 
											  int index, int success, char *data, int len, 
											  void *instance )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Stats* stats;

	Dbg_Printf( "********* Got stats for %d type %d index %d success %d data %s len %d\n",
				profileid, type, index, success, data, len );
	
	if( success < 1 )
	{
		return;
	}
	stats = gamenet_man->mpStatsMan->GetStats();

	// Rating
	if( index == 0 )
	{
		char* ptr;
        
		ptr = strtok( data, "\\" );
		Dbg_Printf( "Ptr: %s\n", ptr );
		if( ptr )
		{
			ptr = strtok( NULL, "\\" );
			Dbg_Printf( "(2)Ptr: %s\n", ptr );
			stats->m_rating = atoi( ptr );
			Dbg_Printf( "(3)Rating: %d\n", stats->m_rating );
		}
	}
	else
	{
		char* ptr;
        
		// Levels
		ptr = strtok( data, "\\" );
		if( ptr )
		{
			Dbg_Printf( "**** 1\n" );
			if( stricmp( ptr, "HighScore" ) == 0 )
			{
				Dbg_Printf( "**** 2\n" );
				ptr = strtok( NULL, "\\" );
				if( ptr )
				{
					stats->m_highscore[index - 1] = atoi( ptr );
					Dbg_Printf( "**** 3 %d\n", stats->m_highscore[index -1 ] );
				}

				ptr = strtok( NULL, "\\" );
			}
			
			if( stricmp( ptr, "HighCombo" ) == 0 )
			{
				Dbg_Printf( "**** 4\n" );
				ptr = strtok( NULL, "\\" );
				if( ptr )
				{
					stats->m_bestcombo[index - 1] = atoi( ptr );
					Dbg_Printf( "**** 5 %d\n", stats->m_bestcombo[index -1 ] );
				}
			}
		}

		if( index == ( vNUM_TRACKED_LEVELS - 1 ))
		{
			Script::RunScript( "stats_retrieved" );
			gamenet_man->mpStatsMan->m_state = vSTATS_STATE_LOGGED_IN;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::ScriptRetrievePersonalStats(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	int i;

	Dbg_Printf( "Requesting rating\n" );
	GetPersistDataValues( 0, gamenet_man->mpBuddyMan->GetProfile(), pd_public_ro, 0,
							  "\\Rating", s_stats_retrieval_callback, NULL );
	for( i = 0; i < vNUM_TRACKED_LEVELS; i++ )
	{
		GetPersistDataValues( 0, gamenet_man->mpBuddyMan->GetProfile(), pd_public_ro, i + 1,
								  "\\HighScore\\HighCombo", s_stats_retrieval_callback, NULL );
		
	}
	
	gamenet_man->mpStatsMan->m_state = vSTATS_STATE_RETRIEVING;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::ScriptCleanUpTopStats(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;
	Script::CArray *p_high_scores;
	int i;

	stats_man = gamenet_man->mpStatsMan;
	if( stats_man->m_need_to_retrieve_stats == true )
	{
		return true;
	}

	for( i = 0; i < vNUM_RATINGS_FILES; i++ )
	{
		stats_man->m_stats_keepers[i].Cleanup();
	}
	stats_man->m_need_to_retrieve_stats = true;
	 
	p_high_scores = Script::GetArray("high_scores_array_all_time");
	Script::CleanUpArray( p_high_scores );
	p_high_scores = Script::GetArray("best_combos_array_all_time");
	Script::CleanUpArray( p_high_scores );
	p_high_scores = Script::GetArray("high_scores_array_monthly");
	Script::CleanUpArray( p_high_scores );
	p_high_scores = Script::GetArray("best_combos_array_monthly");
	Script::CleanUpArray( p_high_scores );
	p_high_scores = Script::GetArray("top_players_array");
	Script::CleanUpArray( p_high_scores );
	p_high_scores = Script::GetArray("personal_stats_array");
	Script::CleanUpArray( p_high_scores );

	ghttpCleanup();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::ScriptGetRank(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;
	int rank, rating;
	 
	stats_man = gamenet_man->mpStatsMan;
	rating = stats_man->m_stats.GetRating();
	rank = (int) (((float) rating / (float) vMAX_RATING ) * (float) vMAX_RANK );
	pScript->GetParams()->AddInteger( CRCD(0x7786171a,"rank"), rank ); 

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::ScriptFillStatsArrays(Script::CStruct *pParams, Script::CScript *pScript)
{
	//Script::CArray *p_high_scores;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;
	uint32 array;
	 
	stats_man = gamenet_man->mpStatsMan;

	pParams->GetChecksum( CRCD(0x5ef31148,"array"), &array, true );
	Dbg_Printf( "Array 0x%x\n", array );
	switch( array )
	{
		case 0xcb95928f:	//high_scores_array_monthly
			stats_man->m_stats_keepers[vFILE_HIGH_SCORE_MONTHLY].FillSectionedMenu();
			stats_man->m_stats_keepers[vFILE_HIGH_SCORE_MONTHLY].FillMenu();
			break;
		case 0x67f1ae8a:	//best_combos_array_monthly
			stats_man->m_stats_keepers[vFILE_BEST_COMBO_MONTHLY].FillSectionedMenu();
			stats_man->m_stats_keepers[vFILE_BEST_COMBO_MONTHLY].FillMenu();
			break;
		case 0x5299291b: 	//top_players_array
			Dbg_Printf( "Filling top players menu\n" );
			stats_man->m_stats_keepers[vFILE_RATINGS].FillMenu( true );
			break;
		case 0xef20d3a1:	//high_scores_array_all_time
			stats_man->m_stats_keepers[vFILE_HIGH_SCORE_ALL_TIME].FillSectionedMenu();
			stats_man->m_stats_keepers[vFILE_HIGH_SCORE_ALL_TIME].FillMenu();
			break;
		case 0x9fe64312:	//best_combos_array_all_time
			stats_man->m_stats_keepers[vFILE_BEST_COMBO_ALL_TIME].FillSectionedMenu();
			stats_man->m_stats_keepers[vFILE_BEST_COMBO_ALL_TIME].FillMenu();
			break;
		case 0xfef84fff:	//personal_stats_array
			stats_man->m_stats.FillMenu();
			break;
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::ScriptRetrieveTopStats(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;
	DownloadContext context;
	int file_index;

	stats_man = gamenet_man->mpStatsMan;

	file_index = 0;
	if( pParams->GetInteger( CRCD(0x7360c9ef,"file"), &file_index ))
	{
		stats_man->m_cur_file_index = file_index;
	}
	else
	{
		stats_man->m_cur_file_index++;
	}
    
	sprintf( context.m_path, "http://%s/thug/%s", SSHOST, s_stats_files[stats_man->m_cur_file_index] );
	context.m_file_not_found_script = CRCD(0x1c049404,"StatsRetrievalFailed");
	context.m_transfer_failed_script = CRCD(0x1c049404,"StatsRetrievalFailed");
	context.m_update_progress_script = CRCD(0x74e0cd35,"do_nothing");
	context.m_max_size = vMAX_STATS_FILE_SIZE;

	if( stats_man->m_cur_file_index == ( vNUM_RATINGS_FILES - 1 ))
	{
		context.m_transfer_complete_script = CRCD(0x7346e8e7,"stats_retrieval_complete");
	}
	else
	{
		context.m_transfer_complete_script = CRCD(0xd932b07b,"download_more_stats");
	}
	
	context.m_dl_complete = s_stats_file_dl_complete;

	gamenet_man->mpContentMan->DownloadFile( &context );

	stats_man->m_need_to_retrieve_stats = false;

    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	StatsMan::ScriptNeedToRetrieveTopStats(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;
	
	stats_man = gamenet_man->mpStatsMan;
	return stats_man->NeedToRetrieveStats();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsMan::StartNewGame( void )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Script::CScriptStructure* pStructure;
	Prefs::Preferences* pPreferences;
	const char* server_name, *challenge;
	int score_limit, time_limit;
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* player;
	Net::Server* server;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	m_cur_game = NewGame( 1 );
	challenge = GetChallenge( m_cur_game );
	server = gamenet_man->GetServer();
	Dbg_Assert( server != NULL );

	pPreferences = gamenet_man->GetNetworkPreferences();
	pStructure = pPreferences->GetPreference( Script::GenerateCRC("server_name") );
	pStructure->GetText( "ui_string", &server_name, true );

	time_limit = pPreferences->GetPreferenceValue( Script::GenerateCRC("time_limit"), Script::GenerateCRC("time") );
	score_limit = pPreferences->GetPreferenceValue( Script::GenerateCRC("target_score"), Script::GenerateCRC("score") );

    BucketStringOp( m_cur_game, "hostname", bo_set, (char*) server_name, bl_server, 0 );
	BucketStringOp( m_cur_game, "mapname", bo_set, gamenet_man->GetLevelName(), bl_server, 0 );
	BucketStringOp( m_cur_game, "gametype", bo_set, gamenet_man->GetGameModeName(), bl_server, 0 );
	BucketIntOp( m_cur_game, "mapcrc", bo_set, gamenet_man->GetNetworkLevelId(), bl_server, 0 );
	BucketIntOp( m_cur_game, "gametypecrc", bo_set, gamenet_man->GetGameTypeFromPreferences(), bl_server, 0 );
	BucketIntOp( m_cur_game, "maxplayers", bo_set, gamenet_man->GetMaxPlayers(), bl_server, 0 );
	BucketIntOp( m_cur_game, "timelimit", bo_set, time_limit, bl_server, 0 );
	BucketIntOp( m_cur_game, "fraglimit", bo_set, score_limit, bl_server, 0 );

	for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
	{
		char key_name[64];
		Net::MsgDesc msg;
		uint32 id;

		id = player->m_Skater->GetID();

		NewPlayer( m_cur_game, id, player->m_Name );
		
		sprintf( key_name, "player" );
		BucketStringOp( m_cur_game, key_name, bo_set, player->m_Name, bl_player, id );
		sprintf( key_name, "pid" );
		BucketIntOp( m_cur_game, key_name, bo_set, player->m_Profile, bl_player, id );

		if( player->m_Profile > 0 )
		{
			msg.m_Data = (char*) challenge;
			msg.m_Length = strlen( challenge ) + 1;
			msg.m_Id = GameNet::MSG_ID_CHALLENGE;
			msg.m_Queue = Net::QUEUE_SEQUENCED;
			msg.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
	
			server->EnqueueMessage( player->GetConnHandle(), &msg );
		}
	}

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	StatsMan::ReportStats( bool final )
{
	int result, cheating_occurred;
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* player;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	if( m_cur_game == NULL )
	{
		return;
	}
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
	{
		uint32 id;
		Mdl::Score* score_obj;
				
		Obj::CSkaterScoreComponent* p_skater_score_component = GetSkaterScoreComponentFromObject( player->m_Skater );
		Dbg_Assert(p_skater_score_component);
	
		score_obj = p_skater_score_component->GetScore();

		id = player->m_Skater->GetID();
		
        BucketIntOp( m_cur_game, "score", bo_set, gamenet_man->GetPlayerScore( id ), bl_player, id );
		Dbg_Printf( "**** PLAYER %d, Best combo: %d\n", player->m_Skater->GetID(), player->m_BestCombo );
		BucketIntOp( m_cur_game, "combo", bo_set, player->m_BestCombo, bl_player, id );
	}

	// 'highscore' will really means 'cheats', but we don't want to give hackers any hints. 
	// It is meant to be misleading.
	cheating_occurred = g_CheatsEnabled | gamenet_man->HasCheatingOccurred();
	BucketIntOp( m_cur_game, "highscore", bo_set, cheating_occurred, bl_server, 0 );
    result = SendGameSnapShot( m_cur_game, NULL, (int) final );
	
	switch( result )
	{
		case GE_DATAERROR: 	// If game is NULL and the last game created by NewGame failed (because the connection was lost and disk logging is disabled)
			Dbg_Printf( "Error sending stats\n" );
			break;
		case GE_NOCONNECT: // If the connection is lost and disk logging is disabled
			Dbg_Printf( "Error sending stats : No connect\n" );
			break;
		case GE_NOERROR: //The update was sent, or disk logging is enabled and the game was logged
			Dbg_Printf( "Sent stats\n" );
			break;
	}

	if( final )
	{
		EndGame();
	}

	m_time_since_last_report = Tmr::GetTime();

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsMan::EndGame( void )
{
	m_state = vSTATS_STATE_LOGGED_IN;
	if( m_cur_game )
	{
		FreeGame( m_cur_game );
		m_cur_game = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsMan::PlayerLeft( int id )
{
	if( m_state == vSTATS_STATE_IN_GAME )
	{
		RemovePlayer( m_cur_game, id );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	StatsMan::GenerateAuthResponse( char* challenge, char* password, char* response )
{
	return GenerateAuth( challenge, password, response );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsMan::AuthorizePlayer( int id, char* response )
{
	char key_name[64];

	sprintf( key_name, "auth" );
	BucketStringOp( m_cur_game, key_name, bo_set, response, bl_player, id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Stats*	StatsMan::GetStats( void )
{
	return &m_stats;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	StatsMan::GetLevelName( uint32 level_crc )
{
	int i;
	Script::CArray* pArray = Script::GetArray("stats_level_names");
	Dbg_Assert( pArray );

	for( i = 0; i < (int)pArray->GetSize(); i++ )
	{   
		uint32 checksum;
		Script::CStruct* pStructure = pArray->GetStructure( i );
		Dbg_Assert( pStructure );

        pStructure->GetChecksum( "level", &checksum );
		if( level_crc == checksum )
		{
			const char* level_name;

			pStructure->GetText( "text", &level_name, true );
			return (char *) level_name;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Stats::Stats( void )
{
	m_rating = 0;
	memset( m_highscore, 0, sizeof( uint32 ) * vNUM_TRACKED_LEVELS );
	memset( m_bestcombo, 0, sizeof( uint32 ) * vNUM_TRACKED_LEVELS );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Stats::GetRating( void )
{
	return m_rating;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Stats::GetHighScore( uint32 level )
{
	int i;

	for( i = 0; i < vNUM_TRACKED_LEVELS; i++ )
	{
		if( level == s_levels[i] )
		{
			return m_highscore[i];
		}
	}

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Stats::GetBestCombo( uint32 level )
{
	int i;

	for( i = 0; i < vNUM_TRACKED_LEVELS; i++ )
	{
		if( level == s_levels[i] )
		{
			return m_bestcombo[i];
		}
	}

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Stats::FillMenu( void )
{
	Lst::Search< SortableStats > sh;
	SortableStats* p_stats, *next;
	Lst::Head< SortableStats > stat_list;
	int i, count, num_items;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;

	stats_man = gamenet_man->mpStatsMan;

	num_items = 0;
	for( i = 0; i < vNUM_TRACKED_LEVELS; i++ )
	{
		if( m_highscore[i] > 0 )
		{
			p_stats = new SortableStats;
			p_stats->SetPri( m_highscore[i] );
			p_stats->m_Score = m_highscore[i];
			p_stats->m_Level = s_levels[i];

			stat_list.AddNode( p_stats );
			num_items++;
		}
	}

	count = 0;
	for( p_stats = sh.FirstItem( stat_list ); p_stats; p_stats = sh.NextItem())
	{
		Script::CStruct* p_entry;
			
		p_entry = new Script::CStruct;
		p_entry->AddInteger( "score", p_stats->m_Score );
		p_entry->AddString( "level", stats_man->GetLevelName( p_stats->m_Level ));

        Script::RunScript( CRCD(0xa6111f30,"add_stat_personal_menu_item"), p_entry );
		//Script::RunScript( CRCD(0xac09e3b,"add_stat_player_menu_item"), p_entry );
	}
	for( p_stats = sh.FirstItem( stat_list ); p_stats; p_stats = next )
	{
		next = sh.NextItem();
		delete p_stats;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

StatsPlayer::StatsPlayer( void )
: Lst::Node< StatsPlayer > ( this )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

StatsLevel::StatsLevel( void )
: Lst::Node< StatsLevel > ( this )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

StatsLevel::~StatsLevel( void )
{
	Lst::Search< StatsPlayer > sh;
	StatsPlayer* player, *next;

	for( player = sh.FirstItem( m_Players ); player; player = next )
	{
		next = sh.NextItem();
		delete player;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		StatsKeeper::NumEntries( int max_entries_per_level )
{
	int num_entries;
	Lst::Search< StatsLevel > sh;
	Lst::Search< StatsPlayer > player_sh;
	StatsLevel* level;
	StatsPlayer* player;
    
	num_entries = 0;
	for( level = sh.FirstItem( m_Levels ); level; level = sh.NextItem())
	{
		num_entries += ( (int) level->m_Players.CountItems() > max_entries_per_level ) ? 
							max_entries_per_level : level->m_Players.CountItems();
	}

	for( player = player_sh.FirstItem( m_Players ); player; player = player_sh.NextItem())
	{
		num_entries++;
	}

	return num_entries;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsKeeper::FillSectionedMenu( void )
{
	int rank, count, num_items;
	Lst::Search< StatsLevel > sh;
	Lst::Search< StatsPlayer > player_sh;
	StatsLevel* level;
	StatsPlayer* player;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;
	Script::CStruct* p_header;

	stats_man = gamenet_man->mpStatsMan;
	num_items = NumEntries( vMAX_ENTRIES_PER_LEVEL );
	count = 0;
	
	Dbg_Printf( "*** Filling sectioned menu\n" );
	for( level = sh.FirstItem( m_Levels ); level; level = sh.NextItem())
	{
		int player_count;

		if( level->m_Players.CountItems() > 0)
		{
			Script::CStruct* p_level_entry;

			p_level_entry = new Script::CStruct;
			p_level_entry->AddString( "text", stats_man->GetLevelName( level->m_Level ));
			Script::RunScript( CRCD(0x1aadee21,"add_stat_header_menu_item"), p_level_entry );

			delete p_level_entry;
		}
		

		player_count = 0;
		for( player = player_sh.FirstItem( level->m_Players ); player; player = player_sh.NextItem())
		{
			Script::CStruct* p_entry;
			
			if( player_count >= vMAX_ENTRIES_PER_LEVEL )
			{
				break;
			}

			p_entry = new Script::CStruct;
			p_entry->AddInteger( "rating", player->m_Rating );
			p_entry->AddInteger( "score", player->m_Score );
			rank = (int) (((float) player->m_Rating / (float) vMAX_RATING ) * (float) vMAX_RANK );
			p_entry->AddInteger( "rank", rank );
			p_entry->AddString( "name", player->m_Name );
			p_entry->AddString( "level", stats_man->GetLevelName( level->m_Level ));

			Script::RunScript( CRCD(0x19f7bc28,"add_stat_score_menu_item"), p_entry );
			
			delete p_entry;

			player_count++;
		}
	}

    p_header = new Script::CStruct;
	p_header->AddString( "text", Script::GetLocalString( CRCD(0xc2283319,"category_all_levels")));
	Script::RunScript( CRCD(0x1aadee21,"add_stat_header_menu_item"), p_header );
	delete p_header;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsKeeper::FillMenu( bool just_ratings )
{
	int rank;
	Lst::Search< StatsLevel > sh;
	Lst::Search< StatsPlayer > player_sh;
	StatsLevel* level;
	StatsPlayer* player;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	StatsMan* stats_man;
	SortableStats* p_stats, *next;
	Lst::Head< SortableStats > stat_list;
	Lst::Search< SortableStats > stats_sh;
	int count;

	stats_man = gamenet_man->mpStatsMan;
	if( just_ratings )
	{
		for( player = player_sh.FirstItem( m_Players ); player; player = player_sh.NextItem())
		{
			p_stats = new SortableStats;
			p_stats->SetPri( player->m_Rating );
			p_stats->m_Score = player->m_Score;
			p_stats->m_Rating = player->m_Rating;
			strcpy( p_stats->m_Name, player->m_Name );
		
			stat_list.AddNode( p_stats );
		}
	}
	else
	{
		for( level = sh.FirstItem( m_Levels ); level; level = sh.NextItem())
		{
			for( player = player_sh.FirstItem( level->m_Players ); player; player = player_sh.NextItem())
			{
				p_stats = new SortableStats;
				p_stats->SetPri( player->m_Score );
				p_stats->m_Score = player->m_Score;
				p_stats->m_Level = level->m_Level;
				p_stats->m_Rating = player->m_Rating;
				strcpy( p_stats->m_Name, player->m_Name );
			
				stat_list.AddNode( p_stats );
			}
		}
	}
			
	count = 0;
	for( p_stats = stats_sh.FirstItem( stat_list ); p_stats; p_stats = stats_sh.NextItem())
	{
		Script::CStruct* p_entry;

		if(( p_stats->m_Score == 0 ) || ( count == vSTATS_PLAYER_COUNT ))
		{
			break;
		}
		p_entry = new Script::CStruct;
		p_entry->AddInteger( "rating", p_stats->m_Rating );
		p_entry->AddInteger( "score", p_stats->m_Score );
		rank = (int) (((float) p_stats->m_Rating / (float) vMAX_RATING ) * (float) vMAX_RANK );
		p_entry->AddInteger( "rank", rank );
		p_entry->AddString( "name", p_stats->m_Name );

		if( just_ratings )
		{
			Script::RunScript( CRCD(0xac09e3b,"add_stat_player_menu_item"), p_entry );
		}
		else
		{
			p_entry->AddString( "level", stats_man->GetLevelName( p_stats->m_Level ));
			Script::RunScript( CRCD(0x19f7bc28,"add_stat_score_menu_item"), p_entry );
		}
		delete p_entry;
		count++;
	}

	for( p_stats = stats_sh.FirstItem( stat_list ); p_stats; p_stats = next )
	{
		next = stats_sh.NextItem();
		delete p_stats;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	StatsKeeper::Cleanup( void )
{
	Lst::Search< StatsLevel > sh;
	Lst::Search< StatsPlayer > player_sh;
	StatsLevel* level, *next;
	StatsPlayer* player, *next_player;

	for( level = sh.FirstItem( m_Levels ); level; level = next )
	{
		next = sh.NextItem();
		delete level;
	}

	for( player = player_sh.FirstItem( m_Players ); player; player = next_player )
	{
		next_player = player_sh.NextItem();
		delete player;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SortableStats::SortableStats( void )
: Lst::Node< SortableStats > ( this )
{
	m_Score = 0;
	m_Level = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet






================================================
FILE: Code/Sk/GameNet/Ngps/p_stats.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate5													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_stats.h												**
**																			**
**	Created by:		04/30/03	-	SPG										**
**																			**
**	Description:	PS2 Stat-tracking code 									**
**																			**
*****************************************************************************/

#ifndef __GAMENET_NGPS_P_STATS_H
#define __GAMENET_NGPS_P_STATS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define vNUM_TRACKED_LEVELS	12

namespace GameNet
{
	
enum
{
	vFILE_HIGH_SCORE_ALL_TIME,
	vFILE_BEST_COMBO_ALL_TIME,
	vFILE_HIGH_SCORE_MONTHLY,
	vFILE_BEST_COMBO_MONTHLY,
	vFILE_RATINGS,
	vNUM_RATINGS_FILES
};

enum
{
	vSTATS_STATE_WAITING,
	vSTATS_STATE_CONNECTING,
	vSTATS_STATE_CONNECTED,
	vSTATS_STATE_FAILED_LOG_IN,
	vSTATS_STATE_LOGGED_IN,
	vSTATS_STATE_RETRIEVING,
	vSTATS_STATE_IN_GAME,
};


class Stats
{
	friend class StatsMan;

public:
	Stats( void );

	int		GetRating( void );
	int		GetHighScore( uint32 level );
	int		GetBestCombo( uint32 level );

	void	FillMenu( void );
private:
	int		m_rating;
	uint32	m_highscore[vNUM_TRACKED_LEVELS];
	uint32	m_bestcombo[vNUM_TRACKED_LEVELS];
};

class StatsPlayer : public Lst::Node< StatsPlayer >
{
public:
	enum
	{
		vMAX_STATS_PLAYER_NAME_LEN = 32
	};
	StatsPlayer( void );
	
	char	m_Name[vMAX_STATS_PLAYER_NAME_LEN];
	uint32	m_Rating;
	uint32	m_Score;
};

class StatsLevel : public Lst::Node< StatsLevel >
{
public:
	StatsLevel( void );
	~StatsLevel( void );

	uint32		m_Level;
	Lst::Head< StatsPlayer >	m_Players;
};

class StatsKeeper 
{
public:
	
	void	Cleanup( void );
	int		NumEntries( int max_entries_per_level );
	void	FillMenu( bool just_ratings = false );
	void	FillSectionedMenu( void );
	
	Lst::Head< StatsLevel > 	m_Levels;
	Lst::Head< StatsPlayer > 	m_Players;
	char	m_Date[32];
};

class StatsMan
{
public:
	StatsMan( void );
	~StatsMan( void );

	void	Connect( void );
	void	Disconnect( void );
	void	Connected( void );
	bool	IsLoggedIn( void );
	bool	NeedToRetrieveStats( void );
	char*	GetLevelName( uint32 level_crc );

	void	StartNewGame( void );
	void	ReportStats( bool final );
	char*	GenerateAuthResponse( char* challenge, char* password, char* response );
	void	AuthorizePlayer( int id, char* response );
	void	PlayerLeft( int id );
	void	EndGame( void );
	Stats*	GetStats( void );

	static	bool		ScriptStatsLoggedIn(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptStatsLogOff(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptStatsLogIn(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptReportStats(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptRetrievePersonalStats(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptRetrieveTopStats(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptNeedToRetrieveTopStats(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptCleanUpTopStats(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptFillStatsArrays(Script::CStruct *pParams, Script::CScript *pScript);
	static	bool		ScriptGetRank(Script::CStruct *pParams, Script::CScript *pScript);
    
private:
	static	void s_threaded_stats_connect( StatsMan* stats_man );
	static	void s_stats_retrieval_callback(int localid, int profileid, persisttype_t type, 
											int index, int success, char *data, int len, 
											void *instance );
	static	GHTTPBool s_stats_file_dl_complete(	GHTTPRequest request,       // The request.
											GHTTPResult result,         // The result (success or an error).
											char* buffer,              // The file's bytes (only valid if ghttpGetFile[Ex] was used).
											int buffer_len,              // The file's length.
											void * param                // User-data.
											);

	void	parse_score_list( int type, char* buffer, bool read_date );
	void	parse_ratings( char* buffer );

	Tsk::Task< StatsMan >*					m_stats_logic_task;
	static	Tsk::Task< StatsMan >::Code   	s_stats_logic_code;

	bool		m_logged_in;
	int			m_state;
	statsgame_t	m_cur_game;
	Tmr::Time	m_time_since_last_report;
	Stats		m_stats;

	int			m_cur_file_index;
	StatsKeeper	m_stats_keepers[vNUM_RATINGS_FILES];
	bool		m_need_to_retrieve_stats;
};





/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet

#endif	// __GAMENET_NGPS_P_STATS_H




================================================
FILE: Code/Sk/GameNet/Ngps/snglue.cpp
================================================
/****************************************************************************/
/*                                                                          */
/* Copyright SN Systems Ltd 2003                                            */
/*                                                                          */
/* File:        netglue_sneebsd.c                                           */
/* Version:     1.01                                                        */
/* Description: Netglue implementation file for SN NDK EE BSD API           */
/*                                                                          */
/* Change History:                                                          */
/* Vers Date        Author     Changes                                      */
/* 1.00 19-May-2003 D.Lowther  File Created (for use with DNAS - LN 13419)  */
/* 1.01 02-Jun-2003 D.Lowther  Fixed bug in sceNetGlueSocket() where it was */
/*                             checking for iRetval == 0, should be > 0     */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 

/****************************************************************************/
/* Stuff that should come from netglue header files but including netglue.h */
/* causes clashes with snsocket.h                                           */
/****************************************************************************/

extern "C" {

typedef long ssize_t;

typedef u_char sceNetGlueSaFamily_t;
typedef u_int  sceNetGlueSocklen_t;

#define sa_family_t sceNetGlueSaFamily_t
#define socklen_t   sceNetGlueSocklen_t

typedef struct sceNetGlueSockaddr 
{
    u_char      sa_len;
    sa_family_t sa_family;
    char        sa_data[14];
} sceNetGlueSockaddr_t;

typedef struct sceNetGlueHostent
{
    char    *h_name;
    char    **h_aliases;
    int     h_addrtype;
    int     h_length;
    char    **h_addr_list;
#define h_addr h_addr_list[0]
} sceNetGlueHostent_t;

typedef struct sceNetGlueInAddr 
{
    u_int s_addr;
} sceNetGlueInAddr_t;

/****************************************************************************/
/* The netglue error codes in the 2.7 lib ref are wrong, Sony's advice is   */
/* to use the values from ee/gcc/ee/include/sys/errno.h, but I can't        */
/* include that file in here because of clashes with the same names in      */ 
/* snsocket.h                                                               */ 
/* So the following values are copied / pasted from the above errno.h file  */
/* and I've stuck NG_ on the front of the names, they are listed in the     */
/* same order as in the 2.7 lib ref for netglue (not numerical order)       */
/****************************************************************************/

#define NG_EBADF           9 /* Invalid descriptor specified                */
#define NG_ENOMEM         12 /* Insufficient memory                         */
#define NG_EBUSY          16 /* Library not yet available (before           */
                             /* initialization completes, etc.)             */
#define NG_EINVAL         22 /* Invalid arguments specified                 */
#define NG_EPROTOTYPE    107 /* Unsupported protocol type specified         */
#define NG_EOPNOTSUPP     95 /* Invalid call for socket                     */
#define NG_EPFNOSUPPORT   96 /* Unsupported protocol family specified       */
#define NG_EAFNOSUPPORT  106 /* Specified address family is an unsupported  */
                             /* value for the socket protocol family        */
#define NG_EADDRINUSE    112 /* Tried to bind to port already bound         */
#define NG_EADDRNOTAVAIL 125 /* Invalid address specified                   */
#define NG_ENETDOWN      115 /* Interface is down                           */
#define NG_EHOSTUNREACH  118 /* Network is unreachable                      */
#define NG_ECONNABORTED  113 /* Aborted by call to sceGlueAbort()           */
#define NG_ECONNRESET    104 /* Connection was reset                        */
#define NG_EISCONN       127 /* Specified connection has already been made  */
#define NG_ENOTCONN      128 /* Specified connection does not exist         */
#define NG_ETIMEDOUT     116 /* Timeout                                     */
#define NG_ECONNREFUSED  111 /* Connection request was refused              */

/* NDK can return other error codes e.g to indicate problems with the       */
/* comms between the EE and IOP, where there isn't an obvious map from NDK  */
/* to Netglue error code, I'll use NG_UNSPECIFIED                           */ 

#define NG_UNSPECIFIED    NG_EINVAL /* Don't know what else to use          */

/****************************************************************************/
/* Netglue error codes for DNS operations from netdb.h + NG_ on front       */
/****************************************************************************/

#define NG_NETDB_INTERNAL -1 /* see errno */
#define NG_NETDB_SUCCESS  0  /* no problem */
#define NG_HOST_NOT_FOUND 1  /* Authoritative Answer Host not found */
#define NG_TRY_AGAIN      2  /* Non-Authoritative Host not found, or SERVERFAIL */
#define NG_NO_RECOVERY    3  /* Non recoverable errs, FORMERR, REFUSED, NOTIMP */
#define NG_NO_DATA        4  /* Valid name, no data record of requested type */
#define	NG_NO_ADDRESS   NG_NO_DATA /* no address, look for MX record */


/****************************************************************************/
/* Private data / types                                                     */
/****************************************************************************/

/* Absolute maximum number of EE threads which may use EE socket API */
#define ABS_MAX_THREADS      10

/* Structure to hold info for each thread using this netglue API */

typedef struct snNetGlueThreadInfo 
{
    int                 iErrno;
    int                 iHerrno;
    int                 iThreadId;
    sceNetGlueHostent_t NetGlueHostent;
    /* Following are for sceNetGlueGethostbyaddr */
    sn_uint32           *sn_h_addr_list[2]; /* one plus null terminator */
    sn_uint32           sn_h_addr;          /* above [0] points to this */
} snNetGlueThreadInfo_t;

/* Array to hold info for all the threads using this netglue API */
/* An element is 'free' if it's iThreadId is zero                */

static snNetGlueThreadInfo_t s_ThreadInfo[ABS_MAX_THREADS] =

{{ 0, 0, 0, {0,0,0,0,0}, {0,0}, 0},
 { 0, 0, 0, {0,0,0,0,0}, {0,0}, 0}, 
 { 0, 0, 0, {0,0,0,0,0}, {0,0}, 0},
 { 0, 0, 0, {0,0,0,0,0}, {0,0}, 0},
 { 0, 0, 0, {0,0,0,0,0}, {0,0}, 0},
 { 0, 0, 0, {0,0,0,0,0}, {0,0}, 0},
 { 0, 0, 0, {0,0,0,0,0}, {0,0}, 0},
 { 0, 0, 0, {0,0,0,0,0}, {0,0}, 0},
 { 0, 0, 0, {0,0,0,0,0}, {0,0}, 0},
 { 0, 0, 0, {0,0,0,0,0}, {0,0}, 0}};

/****************************************************************************/
/* Private functions                                                        */
/****************************************************************************/

/****************************************************************************/
/* Private functions - for accessing s_ThreadInfo                           */
/****************************************************************************/

static int snGetThreadInfoIndexForThreadId(int iThreadId)
{

    /* Searches s_ThreadInfo[].iThreadId for a match with iThreadId         */
    /* If match found returns index in s_ThreadInfo, else returns -1        */

    int i;

    for (i=0; i= 0)
    {
        if(iInterruptsWereEnabled != 0)
        {
            EI();
        }
        printf("Warning: snAddToThreadInfo() was already added\n");
        return iThreadInfoIndex;
    }

    /* Not already registered, so try and find a spare entry to use */
    iThreadInfoIndex = snGetThreadInfoIndexFreeEntry();

    /* If the list is full, print error message and return error */
    if (iThreadInfoIndex < 0)
    {
        if(iInterruptsWereEnabled != 0)
        {
            EI();
        }
        printf("Error: snAddToThreadInfo() list is full\n");
        return -1;
    }

    /* Store iThreadId in s_ThreadInfo */
    s_ThreadInfo[iThreadInfoIndex].iThreadId = iThreadId;
    s_ThreadInfo[iThreadInfoIndex].iErrno    = 0;
    s_ThreadInfo[iThreadInfoIndex].iHerrno   = 0;

    if(iInterruptsWereEnabled != 0)
    {
        EI();
    }
    return iThreadInfoIndex;
}

static int snRemoveFromThreadInfo(int iThreadId)
{
    /* Finds iThreadId in s_ThreadInfo and then sets it to zero             */
    /* If it didn't find iThreadId returns -1, else returns index           */

    int iInterruptsWereEnabled;
    int iThreadInfoIndex;

    /* Do this with interrupts disabled to serialise access */
    iInterruptsWereEnabled = DI();

    /* Search s_ThreadInfo for iThreadId */
    iThreadInfoIndex = snGetThreadInfoIndexForThreadId(iThreadId);
    if (iThreadInfoIndex >= 0)
    {
        /* Mark the entry where it was stored as free now */
        s_ThreadInfo[iThreadInfoIndex].iThreadId = 0;
    }

    if(iInterruptsWereEnabled != 0)
    {
        EI();
    }
    return iThreadInfoIndex;
}


/****************************************************************************/
/* Private functions - for converting / storing error codes                 */
/****************************************************************************/

static void snStoreErrno(int iErrno)
{

    /* iErrno must be a netglue error code, this function will store it     */
    /* in s_ThreadInfo[].iErrno for the entry for this ThreadId             */

    int iThreadInfoIndex;

    /* Get index in s_ThreadInfo[] for this thread */
    iThreadInfoIndex = snGetThreadInfoIndexForThreadId(GetThreadId());

    /* This shouldn't happen, but be defensive */
    if (iThreadInfoIndex < 0)
    {
        printf("Warning: snStoreErrno() thread not registered\n");
        return;
    }
    
    s_ThreadInfo[iThreadInfoIndex].iErrno = iErrno;
}

static int snConvertNdkErrToNetGlueErr(int iSnErrno)
{
    /* Converts NDK error number in iSnErrno into the closest NetGlue error */
    /* number and returns it                                                */

    int iRetval = 0;

    switch(iSnErrno)
    {
        case ENOBUFS:                  /* No memory buffers are available   */
            iRetval = NG_ENOMEM;       /* Insufficient memory               */
        break;

        case ETIMEDOUT:                /* Operation timed out               */
            iRetval = NG_ETIMEDOUT;    /* Timeout                           */
        break;

        case EISCONN:                  /* The socket is already connected   */
            iRetval = NG_EISCONN;      /* Connection has already been made  */
        break;

        case EOPNOTSUPP:               /* Operation not supported           */
            iRetval = NG_EOPNOTSUPP;   /* Invalid call for socket           */
        break;

        case ECONNABORTED:             /* The connection was aborted        */
            iRetval = NG_ECONNABORTED; /* Aborted by call to sceGlueAbort() */
        break;

        case EWOULDBLOCK:              /* Caller would block                */
            iRetval = -1;              /* Shouldn't happen in blocking mode */
        break;

        case ECONNREFUSED:             /* The connection was refused        */
            iRetval = NG_ECONNREFUSED; /* Connection request was refused    */
        break;

        case ECONNRESET:               /* The connection has been reset     */
            iRetval = NG_ECONNRESET;   /* Connection was reset              */
        break;

        case ENOTCONN:                 /* The socket is not connected       */
            iRetval = NG_ENOTCONN;     /* Connection does not exist         */
        break;

        case EALREADY:                 /* Operation is already in progress  */
            iRetval = -1;              /* Shouldn't happen in blocking mode */
        break;

        case EINVAL:                   /* Invalid parameter                 */
            iRetval = NG_EINVAL;       /* Invalid arguments specified       */
        break;

        case EMSGSIZE:                 /* Invalid message size              */
            iRetval = -1;              /* Shouldn't happen (not using fn's) */              
        break;

        case EPIPE:                    /* Cannot send any more              */
                                       /* Closest NG error code is ...      */
            iRetval = NG_ENOMEM;       /* Insufficient memory               */
        break;

        case EDESTADDRREQ:             /* Destination address is missing    */
                                       /* Closest NG error code is ...      */
            iRetval = NG_EINVAL;       /* Invalid arguments specified       */
        break;

        case ESHUTDOWN:                /* Connection has been shut down     */
                                       /* Closest NG error code is ...      */
            iRetval = NG_ENOTCONN;     /* Connection does not exist         */
        break;

        case ENOPROTOOPT:              /* Option unknown for this protocol  */
                                       /* Closest NG error code is ...      */
            iRetval = NG_EINVAL;       /* Invalid arguments specified       */
        break;

        case EHAVEOOB:                 /* Have received out-of-band data    */
            iRetval = -1;              /* Shouldn't happen                  */              
        break;

        case ENOMEM:                   /* No memory available               */
            iRetval = NG_ENOMEM;       /* Insufficient memory               */
        break;

        case EADDRNOTAVAIL:            /* Specified address not available   */
            iRetval = NG_EADDRNOTAVAIL;/* Invalid address specified         */
        break;

        case EADDRINUSE:               /* Specified address already in use  */
            iRetval = NG_EADDRINUSE;   /* Tried bind to port already bound  */
        break;

        case EAFNOSUPPORT:             /* Address family is not supported   */
            iRetval = NG_EAFNOSUPPORT; /* Address family is an unsupported  */
        break;

        case EINPROGRESS:              /* Operation is in progress          */
            iRetval = -1;              /* Shouldn't happen in blocking mode */
        break;

        case ELOWER:                   /* Unused                            */
            iRetval = -1;              /* Shouldn't happen                  */
        break;

        case EBADF:                    /* Socket descriptor is invalid      */
            iRetval = NG_EBADF;        /* Invalid descriptor specified      */
        break;

        case SN_ENOTINIT:              /* Socket API not initialised        */
                                       /* Closest NG error code is ...      */
            iRetval = NG_EBUSY;        /* Library not yet available         */
        break;

        case SN_ETHNOTREG:             /* Thread not registered with API    */
                                       /* Closest NG error code is ...      */
            iRetval = NG_EINVAL;       /* Invalid arguments specified       */
        break;

        case SN_EMAXTHREAD:            /* Max no of threads exceeded        */
                                       /* Closest NG error code is ...      */
            iRetval = NG_EINVAL;       /* Invalid arguments specified       */
        break;

        case SN_EIOPNORESP:            /* Failed to initialise IOP end      */
                                       /* Closest NG error code is ...      */
            iRetval = NG_EBUSY;        /* Library not yet available         */
        break;

        case SN_ENOMEM:                /* Not enough mem to init sock API   */
                                       /* Closest NG error code is ...      */
            iRetval = NG_ENOMEM;       /* Insufficient memory               */
        break;

        case SN_EBINDFAIL:             /* sceSifBindRpc failed              */
                                       /* Closest NG error code is ...      */
            iRetval = NG_EBUSY;        /* Library not yet available         */
        break;

        case SN_EINVTHREAD:            /* Invalid thread id                 */
                                       /* Closest NG error code is ...      */
            iRetval = NG_EINVAL;       /* Invalid arguments specified       */
        break;

        case SN_EALRDYINIT:            /* Socket API already initialised    */
            iRetval = -1;              /* Shouldn't happen                  */
        break;

        case SN_ESTKDOWN:              /* Stack has not been started        */
                                       /* Closest NG error code is ...      */
            iRetval = NG_EBUSY;        /* Library not yet available         */
        break;

        case SN_EIFSTATE:              /* Error in inet_ifstate             */
                                       /* Closest NG error code is ...      */
            iRetval = NG_EBUSY;        /* Library not yet available         */
        break;

        case SN_EDNSFAIL:              /* General failure code for DNS      */
            iRetval = -1;              /* Shouldn't get this for socket err */
        break;

        case SN_SWERROR:               /* Software error in SN sys code     */
            iRetval = -1;              /* Shouldn't happen                  */
        break;

        case SN_EDNSBUSY:              /* DNS is busy try again later       */
            iRetval = -1;              /* Shouldn't get this for socket err */
        break;

        case SN_REQSIZE:               /* RPC request is not expected size  */
            iRetval = -1;              /* Shouldn't happen                  */
        break;

        case SN_REQINV:                /* RPC request is not valid          */
            iRetval = -1;              /* Shouldn't happen                  */
        break;

        case SN_REQSYNC:               /* RPC general IOP s/w failure       */
            iRetval = -1;              /* Shouldn't happen                  */
        break;

        case SN_RPCBAD:                /* RPC fn code unknown to IOP        */
            iRetval = -1;              /* Shouldn't happen                  */
        break;

        case SN_NOTREADY:              /* Device not ready                  */
                                       /* Closest NG error code is ...      */
            iRetval = NG_ENETDOWN;     /* Interface is down                 */
        break;

        default:                       /* Unknown SN NDK error code         */
            iRetval = -1;              /* Shouldn't happen                  */
    }

    /* If iRetval is set to -1 (unexpected error) has to be worth printing  */
    /* a debug error message to indicate what the SN error code was         */

    if (iRetval == -1)
    {
        printf("Warning: snConvertNdkErrToNetGlueErr(%d) unexpected\n",iSnErrno);
        iRetval = NG_UNSPECIFIED;
    }

    return iRetval;
}

static void snGetConvStoreSocketError(int iSockId)
{
    /* This should only be called when an SN NDK function that operates on  */
    /* a socket returns -1 indicating that it failed                        */
    /*                                                                      */
    /* Get the sn error number for the socket specified by iSockId, convert */
    /* to a netglue error code and store it in s_ThreadInfo[].iErrno for    */
    /* this thread id                                                       */

    int iSnErrno;
    int iNgErrno;

    /* Get the NDK error code */
    iSnErrno = sn_errno(iSockId);

    /* Convert it to a netglue error code */
    iNgErrno = snConvertNdkErrToNetGlueErr(iSnErrno);

    /* Store it in s_ThreadInfo[].iErrno for this thread */
    snStoreErrno(iNgErrno);
}

static void snStoreHerrno(int iHerrno)
{
    /* iHerrno must be a netglue error code, this function will store it   */
    /* in s_ThreadInfo[].iErrno for the entry for this ThreadId             */

    int iThreadInfoIndex;

    /* Get index in s_ThreadInfo[] for this thread */
    iThreadInfoIndex = snGetThreadInfoIndexForThreadId(GetThreadId());

    /* This shouldn't happen, but be defensive */
    if (iThreadInfoIndex < 0)
    {
        printf("Warning: snStoreHerrno() thread not registered\n");
        return;
    }
    
    s_ThreadInfo[iThreadInfoIndex].iHerrno = iHerrno;
}

static void snGetConvStoreDnsError(void)
{
    /* This should only be called when an SN NDK function that results in   */
    /* an error that can be read by sn_h_errno()                            */
    /*                                                                      */
    /* Get the error number from sn_h_errno() and convert to a netglue      */
    /* error code and store it in s_ThreadInfo[].iHerrno for this thread id */

    int iSnHerrno;
    int iNgHerrno = 0;

    /* Get the NDK error code */
    iSnHerrno = sn_h_errno();

    /* Convert it to a netglue error code */
    switch (iSnHerrno)
    {
        /* NDK does not support the standard BSD DNS error codes:           */
        /* HOST_NOT_FOUND, TRY_AGAIN, NO_RECOVERY, NO_DATA, NO_ADDRESS, all */
        /* of these conditions result in NDK returning the following error  */

        case SN_EDNSFAIL:
            iNgHerrno = NG_HOST_NOT_FOUND; /* not sure what else I can do */
        break;

        /* Internal errors in the TCP/IP stack state, or, invalid input     */
        /* can result in the following errors:                              */
        /* SN_ESTKDOWN The TCP/IP Stack has not been started                */
        /* EINVAL name was too long or was a NULL pointer                   */

        default:
            printf("Warning: snGetConvStoreDnsError(%d) unexpected\n",iSnHerrno);
            iNgHerrno = NG_NETDB_INTERNAL;
            /* The sony comment for the above Herrno says "see errno", so I */
            /* I assume I should store something in the errno.              */
            snStoreErrno(NG_UNSPECIFIED);
    }

    /* Store the Herrno in s_ThreadInfo[].iHerrno for this thread */
    snStoreHerrno(iNgHerrno);
}

/****************************************************************************/
/* Private functions - convert sceNetGlueSockaddr_t <-> struct sockaddr     */
/****************************************************************************/

static void snConvGlueToNdkSockaddr(sceNetGlueSockaddr_t *psAddr)
{
    /* Does in place conversion                                             */
    /* from: sceNetGlueSockaddr_t                                           */
    /* to:   NDK struct sockaddr                                            */
    
    /* Do nothing if addr is NULL */
    if (psAddr == NULL)
        return;

    /* Swap the byte order for NDK sa_family (16 bits) doesn't have sa_len  */
    psAddr->sa_len    = psAddr->sa_family;
    psAddr->sa_family = 0;
}

static void snConvNdkToGlueSockaddr(sceNetGlueSockaddr_t *psAddr)
{
    /* Does in place conversion                                             */
    /* from: NDK struct sockaddr                                            */
    /* to:   sceNetGlueSockaddr_t                                           */
    
    /* Do nothing if addr is NULL */
    if (psAddr == NULL)
        return;
    
    /* Swap the byte order for NDK sa_family (16 bits) doesn't have sa_len  */
    psAddr->sa_family = psAddr->sa_len;
    psAddr->sa_len    = 16;
}

/****************************************************************************/
/* Private functions - misc                                                 */
/****************************************************************************/

static sceNetGlueHostent_t *snConvNdkToGlueHostent(struct hostent *psNdkHostent)
{
    /* convert hostent from NDK format to NetGlue format and store it in    */
    /* s_ThreadInfo[].NetGlueHostent for this thread                        */

    int iThreadInfoIndex;
    sceNetGlueHostent_t *psRetval = NULL;

    /* Get the index for this thread in s_ThreadInfo[] */
    iThreadInfoIndex = snGetThreadInfoIndexForThreadId(GetThreadId());

    if (iThreadInfoIndex < 0)
    {
        printf("Error: snConvNdkToGlueHostent() unregistered thread\n");
        return NULL;
    }

    /* Found the index, so now convert from NDK struct hostent, into        */
    /* sceNetGlueHostent_t, only difference is that the two fields          */
    /* h_addrtype and h_length are 16 bit in NDK / 32 bit in Netglue        */

    psRetval = &s_ThreadInfo[iThreadInfoIndex].NetGlueHostent;    
    memset(psRetval, 0, sizeof(*psRetval)); /* just being defensive */
    psRetval->h_addr_list = psNdkHostent->h_addr_list;
    psRetval->h_addrtype  = psNdkHostent->h_addrtype;
    psRetval->h_aliases   = psNdkHostent->h_aliases;
    psRetval->h_length    = psNdkHostent->h_length;
    psRetval->h_name      = psNdkHostent->h_name;

    return psRetval;
}

/****************************************************************************/
/* Functions exported by netglue (in same order as sony libref doc)         */
/****************************************************************************/

int *__sceNetGlueErrnoLoc(void)
{
    int iThreadInfoIndex;
    int iThreadId;

    /* Find the index for this thread in s_ThreadInfo */
    iThreadId = GetThreadId();
    iThreadInfoIndex = snGetThreadInfoIndexForThreadId(iThreadId);

    /* This shouldn't happen, but be defensive */
    if (iThreadInfoIndex < 0)
    {
        printf("Error: __sceNetGlueErrnoLoc() unregistered thread (%d)\n",iThreadId);
        return NULL;
    }
    
    return &s_ThreadInfo[iThreadInfoIndex].iErrno;
}

int *__sceNetGlueHErrnoLoc(void)
{
    int iThreadInfoIndex;
    int iThreadId;

    /* Find the index for this thread in s_ThreadInfo */
    iThreadId = GetThreadId();
    iThreadInfoIndex = snGetThreadInfoIndexForThreadId(iThreadId);

    /* This shouldn't happen, but be defensive */
    if (iThreadInfoIndex < 0)
    {
        printf("Error: __sceNetGlueHErrnoLoc() unregistered thread (%d)\n",iThreadId);
        return NULL;
    }
    
    return &s_ThreadInfo[iThreadInfoIndex].iHerrno;
}

int sceNetGlueAbort(int sockfd)
{
    /* The best approximation to this is to close the socket */
    printf("Warning sceNetGlueAbort() just does closesocket\n");
    closesocket(sockfd);
    return 0;
}

int sceNetGlueAbortResolver(int thread_id)
{
    /* Can't see a way to implement this at the moment */
    printf("Warning sceNetGlueAbortResolver() does nothing\n");
    return 0;
}

int sceNetGlueAccept
    (int s, sceNetGlueSockaddr_t *addr, sceNetGlueSocklen_t *paddrlen)
{
    int iRetval;

    /* *addr is an output so no need to convert it before calling NDK */

    /* Call the NDK equivalent */
    iRetval = accept(s, (struct sockaddr*) addr, (int*) paddrlen);

    /* NetGlue and NDK both use positive value for success */
    if (iRetval > 0)
    {
        /* convert *addr from NDK to Netglue format */
        snConvNdkToGlueSockaddr(addr);
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

int sceNetGlueBind
    (int s, const sceNetGlueSockaddr_t *addr, sceNetGlueSocklen_t addrlen)
{

    int iRetval;

    /* *addr is an input so need to convert it before calling NDK */
    /* but it's a const, so have to clone it before converting it */
    sceNetGlueSockaddr_t copyAddr;
    memcpy(©Addr, addr, sizeof(copyAddr));

    /* convert *addr from Netglue to NDK format */
    snConvGlueToNdkSockaddr(©Addr);

    /* Call the NDK equivalent */
    iRetval = bind(s, (struct sockaddr*) ©Addr, addrlen);

    /* NetGlue and NDK both use zero for success */
    if (iRetval == 0)
    {
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

int sceNetGlueConnect
    (int s, const sceNetGlueSockaddr_t *addr, sceNetGlueSocklen_t addrlen)
{
    int iRetval;

    /* *addr is an input so need to convert it before calling NDK */
    /* but it's a const, so have to clone it before converting it */
    sceNetGlueSockaddr_t copyAddr;
    memcpy(©Addr, addr, sizeof(copyAddr));

    /* convert *addr from Netglue to NDK format */
    snConvGlueToNdkSockaddr(©Addr);

    /* Call the NDK equivalent */
    iRetval = connect(s, (struct sockaddr*) ©Addr, addrlen);

    /* NetGlue and NDK both use zero for success */
    if (iRetval == 0)
    {
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

sceNetGlueHostent_t*
    sceNetGlueGethostbyaddr(const char *addr, int len, int type)
{
    /* NDK does not currently support reverse DNS lookup. Advice from Sony  */
    /* is that DNAS does not use this function (although it does appear in  */
    /* the list of symbols for the DNAS lib)                                */

    /* Without reverse DNS lookup, the best I can do is construct a         */
    /* sceNetGlueHostent_t to return that contains the addr input param and */
    /* and NULL name / alias                                                */

    int iThreadInfoIndex;
    sceNetGlueHostent_t *psRetval = NULL;
    
    iThreadInfoIndex = snGetThreadInfoIndexForThreadId(GetThreadId());

    if (iThreadInfoIndex < 0)
    {
        printf("Error: sceNetGlueGethostbyaddr() unregistered thread\n");
        /* Putting this in Herrno should make app look at Errno */
        snStoreHerrno(NG_NETDB_INTERNAL);
        snStoreErrno(NG_EINVAL);
        return NULL;
    }

    /* Get pointer to sceNetGlueHostent_t for this thread */
    psRetval = &s_ThreadInfo[iThreadInfoIndex].NetGlueHostent;

    /* Set up the sceNetGlueHostent_t that's going to be returned */
    psRetval->h_addr_list    = (char**)&s_ThreadInfo[iThreadInfoIndex].sn_h_addr_list;
    psRetval->h_addr_list[0] = (char*) &s_ThreadInfo[iThreadInfoIndex].sn_h_addr;
    psRetval->h_addr_list[1] = NULL;
    psRetval->h_addrtype     = AF_INET; /* NDK and Netglue define this as 2 */
    psRetval->h_aliases      = NULL;
    psRetval->h_length       = 4;
    psRetval->h_name         = NULL;

    /* Copy the input addr to the sn_h_addr pointed to by above struct */
    memcpy(&s_ThreadInfo[iThreadInfoIndex].sn_h_addr, addr, 4);

    return psRetval;
}

sceNetGlueHostent_t *sceNetGlueGethostbyname(const char *name)
{
    struct hostent *psResult      = NULL;
    sceNetGlueHostent_t *psRetval = NULL;

    /* Call the NDK equivalent */
    psResult = gethostbyname(name);

    /* NetGlue and NDK both use NULL for error */
    if (psResult != NULL)
    {
        /* convert hostent from NDK format to NetGlue format and store it   */
        /* in s_ThreadInfo[].NetGlueHostent for this thread                 */
        psRetval = snConvNdkToGlueHostent(psResult);
        return psRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreDnsError();

    /* NetGlue and NDK both use -1 for error */
    return NULL;
}

int sceNetGlueGetpeername
    (int s, sceNetGlueSockaddr_t *addr, sceNetGlueSocklen_t *paddrlen)
{
    int iRetval;

    /* *addr is an output so no need to convert it before calling NDK */

    /* Call the NDK equivalent */
    iRetval = getpeername(s, (struct sockaddr*) addr, (int*) paddrlen);

    /* NetGlue and NDK both use zero for success */
    if (iRetval == 0)
    {
        /* convert *addr from NDK to Netglue format */
        snConvNdkToGlueSockaddr(addr);
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

int sceNetGlueGetsockname
    (int s, sceNetGlueSockaddr_t *addr, sceNetGlueSocklen_t *paddrlen)
{
    int iRetval;

    /* *addr is an output so no need to convert it before calling NDK */

    /* Call the NDK equivalent */
    iRetval = getsockname(s, (struct sockaddr*) addr, (int*) paddrlen);

    /* NetGlue and NDK both use zero for success */
    if (iRetval == 0)
    {
        /* convert *addr from NDK to Netglue format */
        snConvNdkToGlueSockaddr(addr);
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

int sceNetGlueGetsockopt
    (int s, int level, int optname, void *optval,
     sceNetGlueSocklen_t *optlen)
{
    /* At the moment the only option supported by NetGlue is TCP_NODELAY    */
    /* which uses level = IPPROTO_TCP.                                      */
    /* Because the values defined for IPPROTO_TCP and TCP_NODELAY are the   */
    /* same in NDK and Netglue, there is no need to do any conversion here  */

    int iRetval;

    /* Call the NDK equivalent */
    iRetval = getsockopt(s, level, optname, optval, (int*) optlen);

    /* NetGlue and NDK both use zero for success */
    if (iRetval == 0)
    {
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

u_int sceNetGlueHtonl(u_int hostlong)
{
    return htonl(hostlong);
}

u_short sceNetGlueHtons(u_short hostshort)
{
    return htons(hostshort);
}

u_int sceNetGlueInetAddr(const char *cp)
{
    int iRetval;

    /* Call the NDK equivalent */
    iRetval = inet_addr(cp);

    /* NetGlue and NDK both use INADDR_NONE (all f's) for failure */
    return iRetval;
}

int sceNetGlueInetAton(const char *cp, sceNetGlueInAddr_t *addr)
{
    /* sceNetGlueInAddr_t is the same as NDK struct in_addr */

    int iRetval;

    /* Call the NDK equivalent */
    iRetval = inet_aton(cp, (struct in_addr*) addr);

    /* NetGlue and NDK both use 1 for success / 0 for failure */
    return iRetval;
}

u_int sceNetGlueInetLnaof(sceNetGlueInAddr_t in)
{
    /* to do not implemented yet - but not used by DNAS 2.7.1 */
    printf("Warning: sceNetGlueInetLnaof() not implemented\n");
    return 0;
}

sceNetGlueInAddr_t sceNetGlueInetMakeaddr(u_int net, u_int host)
{
    /* to do not implemented yet - but not used by DNAS 2.7.1 */
    sceNetGlueInAddr_t sRetval;
    memset(&sRetval, 0, sizeof(sRetval));
    printf("Warning: sceNetGlueInetMakeaddr() not implemented\n");
    return sRetval;
}

u_int sceNetGlueInetNetof(sceNetGlueInAddr_t in)
{
    /* to do not implemented yet - but not used by DNAS 2.7.1 */
    printf("Warning: sceNetGlueInetNetof() not implemented\n");
    return 0;
}

u_int sceNetGlueInetNetwork(const char *cp)
{
    /* to do not implemented yet - but not used by DNAS 2.7.1 */
    printf("Warning: sceNetGlueInetNetwork() not implemented\n");
    return INADDR_NONE;
}

char *sceNetGlueInetNtoa(sceNetGlueInAddr_t in)
{
    char *pszRetval;

    /* sceNetGlueInAddr_t is the same as NDK struct in_addr, but can't cast */
    /* non scalar in param passed to call, so copy to ndk_in                */
    struct in_addr ndk_in;
    ndk_in.s_addr = in.s_addr;

    /* Call the NDK equivalent */
    pszRetval = inet_ntoa(ndk_in);

    /* If NDK fails it returns NULL, netglue doc has no mention of failure  */
    return pszRetval;
}

int sceNetGlueListen(int s, int backlog)
{
    int iRetval;

    /* Call the NDK equivalent */
    iRetval = listen(s, backlog);

    /* NetGlue and NDK both use 0 for success */
    if (iRetval == 0)
    {
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

u_int sceNetGlueNtohl(u_int netlong)
{
    return ntohl(netlong);
}

u_short sceNetGlueNtohs(u_short netshort)
{
    return ntohs(netshort);
}

ssize_t sceNetGlueRecv(int s, void *buf, size_t len, int flags)
{
    int iRetval;

    /* 2.7.0 Netglue doc says flags "Not supported (always set to zero)     */
    /* So enforce this and print error if flags != 0                        */

    if (flags != 0)
    {
        printf("Warning: sceNetGlueRecv() called with flags non zero (%d)\n",flags);
        flags = 0;
    }

    /* Call the NDK equivalent */
    iRetval = recv(s, buf, len, flags);

    /* NetGlue and NDK both use >= 0 for success */
    if (iRetval >= 0)
    {
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

ssize_t sceNetGlueRecvfrom
    (int s, void *buf, size_t len, int flags,
     sceNetGlueSockaddr_t *addr, sceNetGlueSocklen_t *paddrlen)
{
    int iRetval;

    /* 2.7.0 Netglue doc says flags "Not supported (always set to zero)     */
    /* So enforce this and print error if flags != 0                        */

    if (flags != 0)
    {
        printf("Warning: sceNetGlueRecvfrom() called with flags non zero (%d)\n",flags);
        flags = 0;
    }

    /* *addr is an output so no need to convert it before calling NDK */

    /* Call the NDK equivalent */
    iRetval = recvfrom(s, buf, len, flags, (struct sockaddr*) addr, (int*) paddrlen);

    /* NetGlue and NDK both use >= 0 for success */
    if (iRetval >= 0)
    {
        /* convert *addr from NDK to Netglue format */
        snConvNdkToGlueSockaddr(addr);
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

ssize_t sceNetGlueSend(int s, const void *buf, size_t len, int flags)
{
    int iRetval;

    /* 2.7.0 Netglue doc says flags "Not supported (always set to zero)     */
    /* So enforce this and print error if flags != 0                        */

    if (flags != 0)
    {
        printf("Warning: sceNetGlueSend() called with flags non zero (%d)\n",flags);
        flags = 0;
    }

    /* Call the NDK equivalent */
    iRetval = send(s, buf, len, flags);

    /* NetGlue and NDK both use >= 0 for success */
    if (iRetval >= 0)
    {
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

ssize_t sceNetGlueSendto
    (int s, const void *buf, size_t len, int flags,
     const sceNetGlueSockaddr_t *addr, sceNetGlueSocklen_t addrlen)
{
    int iRetval;

    /* *addr is an input so need to convert it before calling NDK */
    /* but it's a const, so have to clone it before converting it */
    sceNetGlueSockaddr_t copyAddr;
    memcpy(©Addr, addr, sizeof(copyAddr));

    /* convert *addr from Netglue to NDK format */
    snConvGlueToNdkSockaddr(©Addr);

    /* 2.7.0 Netglue doc says flags "Not supported (always set to zero)     */
    /* So enforce this and print error if flags != 0                        */

    if (flags != 0)
    {
        printf("Warning: sceNetGlueSendto() called with flags non zero (%d)\n",flags);
        flags = 0;
    }

    /* Call the NDK equivalent */
    iRetval = sendto(s, buf, len, flags, (struct sockaddr*)©Addr, addrlen);

    /* NetGlue and NDK both use >= 0 for success */
    if (iRetval >= 0)
    {
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

int sceNetGlueSetSifMBindRpcValue
    (u_int buffersize, u_int stacksize, int priority)
{
    /* According to Sony this function is deprecated and hasn't been        */
    /* implemented in any release of netglue since 2.4                      */
    /* NDK doesn't have the run-time options to support this in any case    */

    /* Print a warning but return indicating success */
    printf("Warning: sceNetGlueSetSifMBindRpcValue() not implemented\n");

    return 0;
}

int sceNetGlueSetsockopt
    (int s, int level, int optname, const void *optval,
     sceNetGlueSocklen_t optlen)
{
    /* At the moment the only option supported by NetGlue is TCP_NODELAY    */
    /* which uses level = IPPROTO_TCP.                                      */
    /* Because the values defined for IPPROTO_TCP and TCP_NODELAY are the   */
    /* same in NDK and Netglue, there is no need to do any conversion here  */

    int iRetval;

    /* Call the NDK equivalent */
    iRetval = setsockopt(s, level, optname, optval, optlen);

    /* NetGlue and NDK both use zero for success */
    if (iRetval == 0)
    {
        return iRetval;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

int sceNetGlueShutdown(int s, int how)
{
    int iCloseResult;
    int iShutdownResult;

    /* NDK and netglue both use the same *values* for the how parameter but */
    /* use different names, like this                                       */
    /* NDK values                 Netglue values                            */
    /* #define SD_RECEIVE  0      #define SHUT_RD    0                      */
    /* #define SD_SEND     1      #define SHUT_WR    1                      */
    /* #define SD_BOTH     2      #define SHUT_RDWR  2                      */

    /* Also netglue only supports SHUT_RDWR (2.7.0 lib ref) and a call to   */
    /* this also performs the function of closesocket()                     */
    
    if (how != SD_BOTH)
    {
        printf("Warning: sceNetGlueShutdown() called with how != SHUT_RDWR (%d)\n",how);
        how = SD_BOTH;
    }

    /* Call the NDK equivalent */
    iShutdownResult = shutdown(s, how);
    iCloseResult    = closesocket(s);

    /* NetGlue and NDK both use zero for success */
    if ((iShutdownResult == 0) && (iCloseResult == 0))
    {
        return 0;
    }
    
    /* Get / convert / store error detail */
    snGetConvStoreSocketError(s);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

int sceNetGlueSocket(int family, int type, int protocol)
{
    /* NDK and netglue both use the same values and names for the various  */
    /* #defines relevant to the paramters this function is passed:         */
    /*                                                                     */
    /* family may only be AF_INET, both NDK and netglue define this as 2   */
    /*                                                                     */
    /* type may be SOCK_STREAM (NDK and netglue define this as 1)          */
    /*          or SOCK_DGRAM  (NDK and netglue define this as 2)          */
    /*          or SOCK_RAW    (NDK not supported, netglue defines it as 3)*/
    /*                                                                     */
    /* Sony advise that DNAS does not use SOCK_RAW.                        */
    /*                                                                     */
    /* protocol is not supported by netglue (always set to zero), for      */
    /* NDK, protcol is always passed in to socket() as PF_INET             */

    int iRetval;

    /* Trap any calls using type SOCK_RAW (and any other unsupported type) */

    if ((type != SOCK_STREAM) && (type != SOCK_DGRAM))
    {
        printf("Error: sceNetGlueSocket() called with type = (%d)\n",type);
        snStoreErrno(NG_EINVAL);
        return -1;
    }

    /* Call the NDK equivalent */
    iRetval = socket(family, type, PF_INET);

    /* Positive value is a good socket descriptor, return it */ 
    if (iRetval > 0) 
    { 
        return iRetval; 
    }     

    /* NDK doesn't provide a general errno, only provided per socket, but  */
    /* failed to create the socket so no way of knowing why                */

    snStoreErrno(NG_UNSPECIFIED);

    /* NetGlue and NDK both use -1 for error */
    return -1;
}

int sceNetGlueThreadInit(int thread_id)
{
    int iResult;
    int iDontCare;
    int iThisThreadId;

    iThisThreadId = GetThreadId();

    /* thread_id == 0 means this thread */
    if (thread_id == 0)
    {
        thread_id = iThisThreadId;
    }

    /* NDK requires each thread that is going to use the socket API to      */
    /* register *itself* with the NDK API by calling sockAPIregthr() i.e.   */
    /* there is currently no way for thread A to register thread B          */
    /* so trap this and report error.                                       */

    /* If this restriction turns out to be a problem in practice then it    */
    /* will be necessary for NDK to be modified so that thread A can        */
    /* register thread B e.g. add new fn sockAPIregother()                  */

    /* so trap this and report error.                                       */

    if (iThisThreadId != thread_id)
    {
        printf("Error: sceNetGlueThreadInit() can only register own thread\n");
        return -1;
    }

    /* Add this to our internal list of registered threads */
    iResult = snAddToThreadInfo(thread_id);

    /* If it failed (list is full), return now with failure */
    if (iResult < 0)
    {
        return -1;
    }
       
    /* Now register with NDK */
    iResult = sockAPIregthr();

    /* If it worked ok, return now indicating success */
    if (iResult == 0)
    {
        return 0;
    }

    /* Failed to register with NDK so remove from local list */
    iDontCare = snRemoveFromThreadInfo(thread_id);

    return -1;
}

int sceNetGlueThreadTerminate(int thread_id)
{
    int iResult1;
    int iResult2;
    int iThisThreadId;

    iThisThreadId = GetThreadId();

    /* thread_id == 0 means this thread */
    if (thread_id == 0)
    {
        thread_id = iThisThreadId;
    }

    /* Remove from local list */
    iResult1 = snRemoveFromThreadInfo(thread_id);

    /* De-register with NDK */
    iResult2 = sockAPIderegother(thread_id);

    /* If both operations successful return indicating success */
    if ((iResult1 >= 0) && (iResult2 ==0))
    {
        return 0;
    }

    return -1;
}

} // end of extern "C"



================================================
FILE: Code/Sk/GameNet/Player.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate3													**
**																			**
**	Module:			GameNet					 								**
**																			**
**	File name:		Player.cpp												**
**																			**
**	Created by:		08/09/01	-	spg										**
**																			**
**	Description:	PlayerInfo functionality								**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

#include 
#include 
#include 

#include 
#include 

#include 

#include 
#include 
#include 
#include 
//#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 		   // mostly getting score


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace GameNet
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

enum
{
	vINVULNERABILITY_PERIOD = 10,		// in frames
	vINVULNERABILITY_BAIL_SECONDS = 1,	// in seconds
	vPROJECTILE_INVULNERABILITY_INTERVAL = 5000,// in ms
};

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

int 	Manager::GetTeamScore( int team_id )
{
	PlayerInfo* player, *local_player;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Lst::Search< PlayerInfo > sh;
	Mdl::Score* score_obj;
	int score;
	bool in_goal_attack;
	 
	score = 0;

	local_player = GetLocalPlayer();
	in_goal_attack = skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netgoalattack" );
	// Next create a sorted list of the players (sorted in order of highest to lowest score)
	for( player = FirstPlayerInfo( sh ); player; player = NextPlayerInfo( sh ))
	{
		// Only show the names/scores of players that are fully in the game
		if((player->IsFullyIn() == false ) || ( player->m_Team != team_id ))
		{
			continue;
		}

		if( in_goal_attack && !local_player->IsObserving())
		{
			Game::CGoalManager* pGoalManager;
			 
			pGoalManager = Game::GetGoalManager();
			return pGoalManager->NumGoalsBeatenByTeam( team_id );
		}
		else
		{
			Obj::CSkaterScoreComponent* p_skater_score_component = GetSkaterScoreComponentFromObject(player->m_Skater);
			Dbg_Assert(p_skater_score_component);
			
			score_obj = p_skater_score_component->GetScore();
			score += score_obj->GetTotalScore();
		}
	}

	return score;
}

int 	Manager::GetPlayerScore( int obj_id )
{
	bool in_goal_attack;
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	GameNet::PlayerInfo* local_player;
	
	local_player = GetLocalPlayer();
	in_goal_attack = skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netgoalattack" );
	if( in_goal_attack && !local_player->IsObserving())
	{
		Game::CGoalManager* pGoalManager;
			 
		pGoalManager = Game::GetGoalManager();
		return pGoalManager->NumGoalsBeatenBy( obj_id );
	}
	else
	{
		Mdl::Score* score_obj;
		PlayerInfo* player;

		player = GetPlayerByObjectID( obj_id );
		Dbg_Assert( player );
		
		Obj::CSkaterScoreComponent* p_skater_score_component = GetSkaterScoreComponentFromObject(player->m_Skater);
		Dbg_Assert(p_skater_score_component);
	
		score_obj = p_skater_score_component->GetScore();
		return score_obj->GetTotalScore();
	}
}

void	Manager::s_render_scores_code( const Tsk::Task< Manager >& task )
{
	Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Script::CScriptStructure *pParams;
	Game::CGameMode* pGameMode;
	PlayerInfo* player;
	Lst::Search< PlayerInfo > sh;
	static Tmr::Time last_update = 0;
	Tmr::Time cur_time;
	Lst::Head< ScoreRank > rank_list;
	Lst::Search< ScoreRank > rank_sh;
	ScoreRank* rank, *next;
	bool show_score, king_mode;

	Manager& man = task.GetData();
    // Only draw the scores in modes in which the score accumulates
	pGameMode = skate_mod->GetGameMode();
	show_score =  skate_mod->GetGameMode()->GetNameChecksum() != CRCD( 0x1c471c60, "netlobby" );

	// in King of the hill games, "score" is actually milliseconds with the crown
	king_mode =	( skate_mod->GetGameMode()->GetNameChecksum() == CRCD( 0x6ef8fda0, "netking" )) ||
				( skate_mod->GetGameMode()->GetNameChecksum() == CRCD( 0x5d32129c, "king" ));
    
	cur_time = Tmr::GetTime();
	if(( cur_time - last_update ) >= Tmr::Seconds( 1 ))
	{
		int i, j;
		uint32 id;
		
		last_update = cur_time;

		// First clear all previous entries
		Script::RunScript( "clear_scores" );
        
		if( show_score && man.ShouldDisplayTeamScores())
		{
			ScoreRank* ranks[vMAX_TEAMS];
            
			for( j = 0; j < skate_mod->GetGameMode()->NumTeams(); j++ )
			{
				if( man.NumTeamMembers( j ) > 0 )
				{
					uint32 team_name_checksum;
					
					switch( j )
					{
					case 0:
						team_name_checksum = CRCD( 0x3224348d, "team_1_name" );
						break;
					case 1:
						team_name_checksum = CRCD( 0xb4b04623, "team_2_name" );
						break;
					case 2:
						team_name_checksum = CRCD( 0x7fec9586, "team_3_name" );
						break;
					case 3:
						team_name_checksum = CRCD( 0x62e9a53e, "team_4_name" );
						break;
					default:
						Dbg_Assert( 0 );
						team_name_checksum = 0;
						break;
					}
	
					ranks[j] = new ScoreRank;
					strcpy( ranks[j]->m_Name, Script::GetString( team_name_checksum ));
					ranks[j]->m_IsKing = false;
					ranks[j]->m_HasFlag = false;
					ranks[j]->m_WhichFlags = 0;
					ranks[j]->m_ColorIndex = j + 2;
					ranks[j]->m_TotalScore = man.GetTeamScore( j );
				}
			}

			// Next create a sorted list of the players (sorted in order of highest to lowest score)
			for( player = man.FirstPlayerInfo( sh ); player; player = man.NextPlayerInfo( sh ))
			{
				// Only show the names/scores of players that are fully in the game
				if( !( player->IsFullyIn()))
				{
					continue;
				}
				if( player->IsLocalPlayer())
				{
					char new_name[64];

					// This is my team.  Make that obvious to the players
					sprintf( new_name, "> %s", ranks[player->m_Team]->m_Name );
					strcpy( ranks[player->m_Team]->m_Name, new_name );
				}
	
				if( player->IsKing())
				{
					ranks[player->m_Team]->m_IsKing = true;
				}
				if( player->HasCTFFlag())
				{
					ranks[player->m_Team]->m_HasFlag = true;
					ranks[player->m_Team]->m_WhichFlags |= ( 1 << player->HasWhichFlag());
				}
			}

			for( j = 0; j < skate_mod->GetGameMode()->NumTeams(); j++ )
			{
				if( man.NumTeamMembers( j ) > 0 )
				{
					// Lists sort based on node's priority so set the node's priority to that of
					// the team's score and add it to the list
					ranks[j]->SetPri( ranks[j]->m_TotalScore );
					rank_list.AddNode( ranks[j] );
				}
			}
		}
		else
		{
			// Next create a sorted list of the players (sorted in order of highest to lowest score)
			for( player = man.FirstPlayerInfo( sh ); player; player = man.NextPlayerInfo( sh ))
			{
				// Only show the names/scores of players that are fully in the game
				if( !( player->IsFullyIn()))
				{
					continue;
				}
	
				rank = new ScoreRank;
				if( player->IsLocalPlayer())
				{
					// This is me.  Make that obvious.
					sprintf( rank->m_Name, "> %s", player->m_Name );
				}
				else
				{
					strcpy( rank->m_Name, player->m_Name );
				}
				
				rank->m_IsKing = player->IsKing();
				if( skate_mod->GetGameMode()->IsTeamGame())
				{
					rank->m_ColorIndex = player->m_Team + 2;
				}
				else
				{
					rank->m_ColorIndex = player->m_Skater->GetID() + 2;
				}
				if( player->HasCTFFlag())
				{
					rank->m_HasFlag = true;
					rank->m_WhichFlags |= ( 1 << player->HasWhichFlag());
				}
				
				if( show_score )
				{
					// Lists sort based on node's priority so set the node's priority to that of
					// the player's score and add him to the list
					rank->SetPri( man.GetPlayerScore( player->m_Skater->GetID()));
					rank_list.AddNode( rank );
				}
				else
				{
					// Just add the player to the list so that the order is the same regardless of score
					rank_list.AddToTail( rank );
				}
			}
		}
		

		// Now loop through the sorted list in order of highest to lowest score and print them out
		i = 0;
		for( rank = rank_sh.FirstItem( rank_list ); rank; rank = next )
		{   
			char score_str[64];
			char title[16];

			title[0]='\0';
			if( rank->m_IsKing )
			{
#if ENGLISH == 0
				sprintf( title, Script::GetLocalString( "player_str_king_abbreviation" ));
#else 
				sprintf( title, "K" );
#endif
			}
			else
			{
				if( rank->m_HasFlag )
				{
					if( rank->m_WhichFlags & 1 )
					{
						strcat( title, "\\s0" );
					}
					if( rank->m_WhichFlags & 2 )
					{
						strcat( title, "\\s1" );
					}
					if( rank->m_WhichFlags & 4 )
					{
						strcat( title, "\\s2" );
					}
					if( rank->m_WhichFlags & 8 )
					{
						strcat( title, "\\s3" );
					}
				}
				else
				{
					title[0] = '\0';
				}
			}

			next = rank_sh.NextItem();
			if( show_score )
			{
				if( king_mode )
				{
					int seconds;

					seconds = Tmr::InSeconds( rank->GetPri() );
					if( seconds >= 3600 )
					{
						sprintf( score_str, "\\c4%s\\c%d %s \\c%d %d:%.2d:%.2d", 
								   								title,
																rank->m_ColorIndex, 
																rank->m_Name,
																rank->m_ColorIndex,
																seconds / 3600,
																( seconds % 3600 ) / 60,
																seconds % 60 );
					}
					else
					{
						sprintf( score_str, "\\c4%s\\c%d %s \\c%d %d:%.2d", 
										   						title,
																rank->m_ColorIndex, 
																rank->m_Name,
																rank->m_ColorIndex,
																seconds / 60,
																seconds % 60 );
					}
					
				}
				else
				{
					sprintf( score_str, "%s\\c%d %s \\c%d %d", 	title,
																rank->m_ColorIndex, 
																rank->m_Name,
																rank->m_ColorIndex,
																rank->GetPri());
				}

			}
			else
			{
				sprintf( score_str, "\\c%d%s", 	rank->m_ColorIndex, rank->m_Name );
			}
            
			switch( i )
			{
			case 0:
				id = CRCD( 0x2b083809, "net_score_1" );
				break;
			case 1:
				id = CRCD( 0xb20169b3, "net_score_2" );
				break;
			case 2:
				id = CRCD( 0xc5065925, "net_score_3" );
				break;
			case 3:
				id = CRCD( 0x5b62cc86, "net_score_4" );
				break;
			case 4:
				id = CRCD( 0x2c65fc10, "net_score_5" );
				break;
			case 5:
				id = CRCD( 0xb56cadaa, "net_score_6" );
				break;
			case 6:
				id = CRCD( 0xc26b9d3c, "net_score_7" );
				break;
			case 7:
				id = CRCD( 0x52d480ad, "net_score_8" );
				break;
			default:
				Dbg_Assert( 0 );
				id = 0;
				break;
			}
			
			pParams = new Script::CScriptStructure;
			pParams->AddComponent( CRCD( 0xc4745838, "text" ), ESYMBOLTYPE_STRING, score_str );
			pParams->AddComponent( CRCD( 0x40c698af, "id" ), ESYMBOLTYPE_NAME, id );
			
			
			Front::CScreenElement* p_name_elem = p_screen_elem_man->GetElement( id, Front::CScreenElementManager::DONT_ASSERT );
			// If any operable menus are up, ignore input
			if( p_name_elem )
			{
				//make sure the scores are enabled
				//if(!Mdl::Skate::Instance()->GetCareer()->GetGlobalFlag(Script::GetInteger(CRCD(0xfbb66146,"NO_DISPLAY_NET_SCORES")))))
					p_name_elem->SetProperties( pParams );
			}
			Script::RunScript( "update_score", pParams );

			delete pParams;
			delete rank;
			i++;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo::PlayerInfo( int flags )
: Lst::Node< PlayerInfo > ( this )
{
	int i, j;

	sprintf( m_Name, "" );

	for( i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
	{
		m_LastSentProps.m_LastSkaterFlagsUpdate[i] = -1;
		m_LastSentProps.m_LastSkaterStateUpdate[i] = -1;
		m_LastSentProps.m_LastDoingTrickUpdate[i] = -1;
		for( j = 0; j < 3; j++ )
		{
			m_LastSentProps.m_LastSkaterPosUpdate[i][j] = -1;
			m_LastSentProps.m_LastSkaterRotUpdate[i][j] = -1;
		}
	}

	m_flags = flags;
	m_observer_logic_task = NULL;
	m_jump_in_frame = 0;
	m_last_object_update_id = vMAX_PLAYERS - 1;
	m_face_data = NULL;

	m_Skater = NULL;
	mp_SkaterProfile = NULL;
	m_last_bail_time = 0;
	m_last_hit_time = 0;
	m_Team = vTEAM_RED;
	m_Profile = 0;
	m_Rating = 0;
	m_VehicleControlType = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

PlayerInfo::~PlayerInfo( void )
{
	

	if ( mp_SkaterProfile )
		delete mp_SkaterProfile;

	if( m_observer_logic_task )
	{
		delete m_observer_logic_task;
	}

	if( m_face_data )
	{
		delete [] m_face_data;
		m_face_data = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::SetSkater( Obj::CSkater* skater )
{
	Manager * gamenet_man = Manager::Instance();
	
	m_Skater = skater;
	if( !IsLocalPlayer())
	{
		if( gamenet_man->GetCurrentlyObservedPlayer() == this )
		{
			gamenet_man->ObservePlayer( this );
		}
	}
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::SetFaceData( uint8* face_data, int size )
{
	uint8 player_id;

	Dbg_Assert( m_face_data == NULL );
	Dbg_Assert( face_data != NULL );
	Dbg_Assert( m_Skater != NULL );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetMiscHeap());

	player_id = m_Skater->GetID();
		 
	// Store the player's skater id as the first byte of the face data
	m_face_data = new uint8[size+1];
	*m_face_data = player_id;
	memcpy( m_face_data + 1, face_data, size );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The player's skater id is the first byte of the face data
uint8*	PlayerInfo::GetFaceData( void )
{
	return m_face_data;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::CopyProfile( Obj::CSkaterProfile* pSkaterProfile )
{
	

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

	Dbg_Assert( pSkaterProfile );

#if 0
	// copy it to the server's player info data
	uint8* pTempBuffer = new uint8[vMAX_APPEARANCE_DATA_SIZE];
	uint32 size = pSkaterProfile->WriteToBuffer(pTempBuffer, vMAX_APPEARANCE_DATA_SIZE);
	Dbg_Printf("Appearance data size is %d bytes\n", size);
	mp_SkaterProfile = new Obj::CSkaterProfile;
	Dbg_Assert( mp_SkaterProfile );
	mp_SkaterProfile->ReadFromBuffer(pTempBuffer);
	delete pTempBuffer;
#endif

	// now uses assignment operator to copy over the info as well
	mp_SkaterProfile = new Obj::CSkaterProfile;
	*mp_SkaterProfile = *pSkaterProfile;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	PlayerInfo::IsLocalPlayer( void )
{
	return m_flags.TestMask( mLOCAL_PLAYER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	PlayerInfo::IsServerPlayer( void )
{
	return m_flags.TestMask( mSERVER_PLAYER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	PlayerInfo::IsObserving( void )
{
	return m_flags.TestMask( mOBSERVER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	PlayerInfo::IsSurveying( void )
{
	return m_flags.TestMask( mSURVEYING );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::MarkAsRestarting( void )
{
	m_flags.SetMask( mRESTARTING );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void	PlayerInfo::MarkAsNonCollidable( void )
{
	m_last_bail_time = Tmr::GetTime();
	return m_flags.SetMask( mNON_COLLIDABLE );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::ClearNonCollidable( void )
{
	return m_flags.ClearMask( mNON_COLLIDABLE );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	PlayerInfo::IsNonCollidable( void )
{
	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	if( !m_Conn->TestStatus( Net::Conn::mSTATUS_READY ))
	{
		return true;
	}

	if( m_Skater->IsInWorld() == false )
	{
		return true;
	}

	// Make skaters invulnerable for the first 10 frames of the game
	if( m_Skater->GetStateHistory()->GetNumPosUpdates() < vINVULNERABILITY_PERIOD )
	{
		return true;
	}

	if(( Tmr::GetTime() - m_last_bail_time ) < Tmr::Seconds( vINVULNERABILITY_BAIL_SECONDS ))
	{
		return true;
	}

	// Don't collide with eliminated players in firefight
	if( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0xbff33600,"netfirefight"))
	{
		Mdl::Score* score_obj;
		Obj::CSkaterScoreComponent* p_score_component = GetSkaterScoreComponentFromObject(m_Skater);
		Dbg_Assert(p_score_component);
		
		score_obj = p_score_component->GetScore();
		if( score_obj->GetTotalScore() <= 0 )
		{
			return true;
		}
	}
	
	// If they've been in this state for more than 5 seconds, that means that
	// for one reason or another, we didn't get their "NotifyBailDone" message.
	// This will rectify things.
	if(( Tmr::GetTime() - m_last_bail_time ) > Tmr::Seconds( 5 ))
	{
		ClearNonCollidable();
		return false;
	}

	if( m_flags.TestMask( mNON_COLLIDABLE ))
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::MarkAsFullyIn( void )
{
	Manager * gamenet_man = Manager::Instance();

	m_flags.SetMask( mFULLY_IN );

	if( gamenet_man->InNetGame() && 
		gamenet_man->OnServer() &&
		!IsLocalPlayer())
	{
		switch( gamenet_man->GetHostMode())
		{
			case vHOST_MODE_SERVE:
				break;
			case vHOST_MODE_AUTO_SERVE:
			{
				Net::Server* server;
				Net::MsgDesc msg_desc;

				server = gamenet_man->GetServer();
				Dbg_Assert( server );

				msg_desc.m_Id = MSG_ID_AUTO_SERVER_NOTIFICATION;
				msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
				msg_desc.m_GroupId = vSEQ_GROUP_PLAYER_MSGS;
				// Tell the client that they're in an auto-serving server
				server->EnqueueMessage( GetConnHandle(), &msg_desc );
				break;
			}
			case vHOST_MODE_FCFS:
			{
				PlayerInfo* player;

				player = gamenet_man->GetServerPlayer();

				// If we don't currently have a player acting as the host
				// make this new player the host
				if( player == NULL )
				{
					gamenet_man->ChooseNewServerPlayer();
				}
				break;
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	PlayerInfo::IsFullyIn( void )
{
	return m_flags.TestMask( mFULLY_IN | mLOCAL_PLAYER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::MarkAsServerPlayer( void )
{
	m_flags.SetMask( mSERVER_PLAYER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::MarkAsNotServerPlayer( void )
{
	m_flags.ClearMask( mSERVER_PLAYER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	PlayerInfo::HasCTFFlag( void )
{
	return m_flags.TestMask( mHAS_ANY_FLAG );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		PlayerInfo::HasWhichFlag( void )
{
	if( m_flags.TestMask( mHAS_RED_FLAG ))
	{
		return 0;
	}
	if( m_flags.TestMask( mHAS_BLUE_FLAG ))
	{
		return 1;
	}
	if( m_flags.TestMask( mHAS_GREEN_FLAG ))
	{
		return 2;
	}
	if( m_flags.TestMask( mHAS_YELLOW_FLAG ))
	{
		return 3;
	}

	return -1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::ClearCTFState( void )
{
	m_flags.ClearMask( mHAS_ANY_FLAG );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::CapturedFlag( int team )
{   
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* local_player;
	char team_str[64];
	Script::CStruct* pParams;
		
	sprintf( team_str, "team_%d_name", team + 1 );

	local_player = gamenet_man->GetLocalPlayer();

	// Make sure they have a flag on them
	Dbg_Assert( HasCTFFlag());

	pParams = new Script::CStruct;
	
	if( IsLocalPlayer())
	{
		pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
		pParams->AddInteger( "team", team );
		Script::RunScript( "captured_flag_you", pParams );
		
	}
	else
	{
		Dbg_Printf( "***** Captured Flag : My Team %d, Flag Team %d\n", local_player->m_Team, team );
		if( ( !local_player->IsObserving()) && 
			( local_player->m_Team == team ))
		{
			Dbg_Printf( "***** Captured Flag 2\n" );
			pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, m_Name );
			pParams->AddInteger( "team", team );
			Script::RunScript( "hide_ctf_arrow" );
			Script::RunScript( "captured_your_flag", pParams );
		}
		else
		{
			pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, m_Name );
			pParams->AddComponent( Script::GenerateCRC("String1"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
			pParams->AddInteger( "team", team );
			Script::RunScript( "captured_flag_other", pParams );
		}                                                      
	}
	
	m_flags.ClearMask( mHAS_ANY_FLAG );

	delete pParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::StoleFlag( int team )
{
	// Shouldn't have any flag already
	Dbg_Assert( !HasCTFFlag());

	Dbg_Printf( "********* In StoleFlag\n" );
	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* local_player, *player;
	Lst::Search< PlayerInfo > sh;
	char team_str[64];
	Script::CStruct* pParams;
		
	sprintf( team_str, "team_%d_name", team + 1 );
	pParams = new Script::CStruct;

	local_player = gamenet_man->GetLocalPlayer();

	
	if( IsLocalPlayer())
	{
		pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
		Script::RunScript( "stole_flag_you", pParams );
		
	}
	else
	{
		Dbg_Printf( "***** FLAG STOLEN: Team %d, I had %d\n", team, local_player->HasWhichFlag() );
		if( !local_player->IsObserving() && ( local_player->HasWhichFlag() == team ))
		{

			pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, m_Name );
			pParams->AddComponent( Script::GenerateCRC("String1"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
			Script::RunScript( "stole_flag_from_you", pParams );
		}
		else
		{
			pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, m_Name );
			pParams->AddComponent( Script::GenerateCRC("String1"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
			Script::RunScript( "stole_flag_other", pParams );
		}
	}

	delete pParams;
	
	for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
	{
		if( player->HasWhichFlag() == team )
		{
			player->m_flags.ClearMask(( mHAS_RED_FLAG << team ));
			player->ClearCTFState();
			Dbg_Assert( !player->HasCTFFlag());
		}
	}

	m_flags.SetMask(( mHAS_RED_FLAG << team ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::TookFlag( int team )
{
	// Shouldn't have any flag already
	Dbg_Assert( !HasCTFFlag());

	Manager * gamenet_man = Manager::Instance();
	PlayerInfo* local_player;
	char team_str[64];
	Script::CStruct* pParams;
		
	sprintf( team_str, "team_%d_name", team + 1 );
	pParams = new Script::CStruct;

	local_player = gamenet_man->GetLocalPlayer();

    if( IsLocalPlayer())
	{
		pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
		Script::RunScript( "took_flag_you", pParams );
		
	}
	else
	{
		Script::CStruct* arrow_params;

		if(( !local_player->IsObserving()) && ( team == local_player->m_Team ))
		{
			arrow_params = new Script::CStruct;
			arrow_params->AddInteger( "team", local_player->m_Team );
			Script::RunScript( "show_ctf_arrow", arrow_params );
			delete arrow_params;

			pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, m_Name );
			Script::RunScript( "took_flag_yours", pParams );
		}
		else
		{
			pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, m_Name );
			pParams->AddComponent( Script::GenerateCRC("String1"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
			Script::RunScript( "took_flag_other", pParams );
		}
	}

	delete pParams;
	m_flags.SetMask(( mHAS_RED_FLAG << team ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::RetrievedFlag( void )
{
	Manager * gamenet_man = Manager::Instance();
	GameNet::PlayerInfo* player, *local_player;
	Lst::Search< PlayerInfo > sh;
	char team_str[64];
	Script::CStruct* pParams;
		
	sprintf( team_str, "team_%d_name", m_Team + 1 );
	pParams = new Script::CStruct;

	if( IsLocalPlayer())
	{
		pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
		pParams->AddInteger( "team", m_Team );
		Script::RunScript( "hide_ctf_arrow" );
		Script::RunScript( "retrieved_flag_you", pParams );
		
	}
	else
	{
		local_player = gamenet_man->GetLocalPlayer();
		if( local_player && !local_player->IsObserving())
		{
			if( local_player->m_Team == m_Team )
			{
				Script::RunScript( "hide_ctf_arrow" );
			}
		}
		pParams->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, m_Name );
		pParams->AddComponent( Script::GenerateCRC("String1"), ESYMBOLTYPE_STRING, Script::GetString( team_str ));
		pParams->AddInteger( "team", m_Team );
		Script::RunScript( "retrieved_flag_other", pParams );
	}

	for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
	{
		if( player->HasWhichFlag() == m_Team )
		{
			player->ClearCTFState();
			break;

		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::MarkAsKing( bool mark )
{    
	Manager * gamenet_man = Manager::Instance();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
    
	if( mark )
	{   
		PlayerInfo* former_king;

		// There can only be one king, so dethrone the last
		former_king = gamenet_man->GetKingOfTheHill();
		if( former_king )
		{
			former_king->MarkAsKing( false );
		}

		if( !m_flags.TestMask( mKING_OF_THE_HILL ))
		{
			m_flags.SetMask( mKING_OF_THE_HILL );

			if( IsLocalPlayer())
			{
				Script::CStruct* pParams;

				pParams = new Script::CStruct;
				if( m_Skater->GetHeapIndex() == 0 )
				{
					pParams->AddChecksum( "player_1", Script::GenerateCRC( "player_1"));
				}
				else
				{
					pParams->AddChecksum( "player_2", Script::GenerateCRC( "player_2"));
				}
				// If we just picked up the crown, hide the arrow
				Script::RunScript( "hide_crown_arrow", pParams );
				delete pParams;
				
				/*gamenet_man->CreateNetPanelMessage( false, Script::GenerateCRC("net_message_new_king_you"),
													NULL, NULL, m_Skater );*/
				Script::RunScript( "NewKingYou" );
				
			}
			else
			{
				Script::CStruct* pParams;

				pParams = new Script::CStruct;
				pParams->AddChecksum( "player_1", Script::GenerateCRC( "player_1"));
				// If someone else just picked up the crown, show the arrow
				Script::RunScript( "show_crown_arrow", pParams );
				
				pParams->AddString( "String0", m_Name );
				Script::RunScript( "NewKingOther", pParams );
				/*gamenet_man->CreateNetPanelMessage( false, Script::GenerateCRC( "net_message_new_king_other"),
													m_Name, NULL );*/
				delete pParams;

				
			}

			if( !gamenet_man->InNetGame())
			{
				int other_id;
				PlayerInfo* other_player;

				// NOTE: This code only works for 2-player splitscreen. If we ever go to four,
				// it needs to be more sophisticated
				if( m_Skater->GetID() == 0 )
				{
					other_id = 1;
				}
				else
				{ 
					other_id = 0;
				}
	
				other_player = gamenet_man->GetPlayerByObjectID( other_id );
				if( other_player )
				{
					Script::CStruct* pParams;

					pParams = new Script::CStruct;
					pParams->AddString( "String0", (char*)m_Skater->GetDisplayName());
					Script::RunScript( "NewKingOther", pParams );
					/*gamenet_man->CreateNetPanelMessage( false, Script::GenerateCRC("net_message_new_king_other"),
														(char*)m_Skater->GetDisplayName(), NULL, other_player->m_Skater );*/
					delete pParams;
				}
			}
			
			
			m_flags.SetMask( mKING_OF_THE_HILL );
		}
        
        Obj::CCrown* crown;
		crown = gamenet_man->GetCrown();
		if( crown )
		{
			crown->PlaceOnKing( m_Skater );
		}
	}
	else
	{
		if( m_flags.TestMask( mKING_OF_THE_HILL ))
		{
			if( IsLocalPlayer())
			{
				// If we lost the crown, show the arrow again, but only if we're still in koth mode
				if( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netking" ) ||
					skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "king" ))
				{
					Script::CStruct* pParams;

					pParams = new Script::CStruct;
					pParams->AddChecksum( "player_1", Script::GenerateCRC( "player_1"));
					pParams->AddChecksum( "player_2", Script::GenerateCRC( "player_2"));

					Script::RunScript( "show_crown_arrow", pParams );
					delete pParams;
				}
			}
			m_flags.ClearMask( mKING_OF_THE_HILL );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	PlayerInfo::IsKing( void )
{
	return m_flags.TestMask( mKING_OF_THE_HILL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::MarkAsNotReady( int time )
{
	if( m_Conn && m_Conn->IsRemote())
	{
		if( time != 0 )
		{
			m_latest_ready_query = time;
		}
		m_Conn->ClearStatus( Net::Conn::mSTATUS_READY );
		m_Conn->SetStatus( Net::Conn::mSTATUS_BUSY );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::SetReadyQueryTime( int time )
{
	m_latest_ready_query = time;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::MarkAsReady( int time )
{
	if( m_latest_ready_query == time )
	{
		if( m_Conn )
		{   
			m_Conn->ClearStatus( Net::Conn::mSTATUS_BUSY );
			m_Conn->SetStatus( Net::Conn::mSTATUS_READY );

			// Now that they're ready, we need to keep track of how many times we're resending to
			// them so that we can detect bad connections
			m_Conn->ClearNumResends();
		}
		m_latest_ready_query = 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		PlayerInfo::IsVulnerable( void )
{
	return (( Tmr::GetTime() - m_last_hit_time ) > vPROJECTILE_INVULNERABILITY_INTERVAL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		PlayerInfo::ResetProjectileVulnerability( void )
{
	m_last_hit_time = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		PlayerInfo::SetHitTime( Tmr::Time hit_time )
{
	m_last_hit_time = hit_time;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		PlayerInfo::GetConnHandle( void )
{
	if( m_Conn )
	{
		return m_Conn->GetHandle();
	}
	return Net::Conn::vHANDLE_INVALID;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		PlayerInfo::GetSkaterNumber( void )
{
	Dbg_Assert( m_Skater );

	return m_Skater->GetSkaterNumber();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		PlayerInfo::GetLastObjectUpdateID( void )
{
	return m_last_object_update_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PlayerInfo::SetLastObjectUpdateID( int id )
{
	m_last_object_update_id = id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		PlayerInfo::GetMaxObjectUpdates( void )
{
	if( m_Conn->GetBandwidthType() == Net::Conn::vBROADBAND )
	{
		return 2;
	}
	
	return 1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	PlayerInfo::IsPendingPlayer( void )
{
	return m_flags.TestMask( mPENDING_PLAYER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

NewPlayerInfo::NewPlayerInfo(void)
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

	mpSkaterProfile = new Obj::CSkaterProfile;
	JumpInFrame = 0;
	Profile = 0;
	Rating = 0;
	Score = 0;
	VehicleControlType = 0;
	
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

NewPlayerInfo::~NewPlayerInfo(void)
{
	

	Dbg_Assert( mpSkaterProfile );

	delete mpSkaterProfile;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet






================================================
FILE: Code/Sk/GameNet/ServerList.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate3													**
**																			**
**	Module:			GameNet					 								**
**																			**
**	File name:		ServerList.cpp											**
**																			**
**	Created by:		06/131/01	-	spg										**
**																			**
**	Description:	Server List functionality								**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 

#include 
//#include 

#ifdef __PLAT_NGPS__
#include 
//#include 
#include 
#include 
#include 
#include 
#endif

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

#ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
// Gamespy frame counter
//extern unsigned long goa_frame_ct;

//extern "C" 
//{
//////////////////////////////////
// Global IP addresses for gamespy
//extern char	qr_hostname[64];
//extern char ServerListHostname[64];

//}

extern void GOASetUniqueID( const char* id );

#endif
#endif

namespace GameNet
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define vMOTD_WAIT_THRESHOLD		Tmr::Seconds( 7 )
#define vTIME_BETWEEN_AUTO_REFRESH	Tmr::Seconds( 3 )
#define vTIME_BETWEEN_MANUAL_REFRESH	Tmr::Seconds( 5 )

#define	vLAN_SERVER_LIST_TIMEOUT	500
#define	vHTTP_TIMEOUT				Tmr::Seconds( 15 )

/*****************************************************************************
**								Private Types								**
*****************************************************************************/
#ifdef __PLAT_NGPS__

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static int			s_motd_retry = 0;
#endif // __PLAT_NGPS__

static Tmr::Time 	s_last_refresh_time = 0;
static bool 		s_refresh_pending = false;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

bool	gEnteringFromMainMenu;

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

#ifdef __PLAT_NGPS__
//static bool s_no_servers_dialog_box_handler( Front::EDialogBoxResult result );

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*SortKeyInfo* GetSortKey( int key_type )
{
	int i;

	for( i = 0; i < vNUM_SORT_KEYS; i++ )
	{
		if( s_sort_keys[i].KeyType == key_type )
		{
			return &s_sort_keys[i];
		}
	}

	return NULL;
}*/
#endif // __PLAT_NGPS__

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#if 0
static bool not_posted_dialog_box_handler( Front::EDialogBoxResult result )
{
    Mdl::FrontEnd* front = Mdl::FrontEnd::Instance();

	front->SetActive( false );

	return false;
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_server_list_state_code( const Tsk::Task< Manager >& task )
{
#ifdef __PLAT_NGPS__
	Manager& man = task.GetData();

	switch( man.GetServerListState())
	{   
		case vSERVER_LIST_STATE_WAIT:
			// A wait time of zero indicates we should wait for an outside force to change the state
			if( man.m_server_list_wait_time == 0 )
			{
				break;
			}
			if( Tmr::GetTime() > man.m_server_list_wait_time )
			{
				man.SetServerListState( man.GetNextServerListState());
			}
			break;

		case vSERVER_LIST_STATE_RETRY_MOTD:
		{
			//Mdl::FrontEnd* front = Mdl::FrontEnd::Instance();
			//Front::DialogBox* dlg;

			//dlg = front->GetDialogBox();
			//dlg->GoBack();

			// fall-through intentional
		}
		case vSERVER_LIST_STATE_STARTING_MOTD:
		{
			Thread::PerThreadStruct	net_thread_data;
			
			//Dbg_Printf( "Serverlist State: Starting MotD\n" );

			// Pause the music & streams before doing DNS lookup
			Pcm::PauseStream( 1 );
			
            
			// First, show the dialog box
			if( s_motd_retry == 0 )
			{
				Script::SpawnScript( "launch_motd_wait_dialog" );
			}
			else
			{
				char msg[256];
				Script::CScriptStructure* pStructure;
			
				pStructure = new Script::CScriptStructure;
				sprintf( msg, "(%d) %s", 	s_motd_retry,
											Script::GetLocalString( "net_status_retry_motd" ));
				pStructure->AddComponent( Script::GenerateCRC( "message" ), ESYMBOLTYPE_STRING, msg );
				Script::SpawnScript( "CreateMotdRetryDialog", pStructure );
				delete pStructure;
			}

			man.SetServerListState( vSERVER_LIST_STATE_WAIT );
			man.m_server_list_wait_time = 0;

			// Now spawn the thread that gets the message of the day
			net_thread_data.m_pEntry = s_threaded_get_message_of_the_day;
			net_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
			net_thread_data.m_pStackBase = man.GetNetThreadStack();
			net_thread_data.m_iStackSize = vNET_THREAD_STACK_SIZE;
			net_thread_data.m_utid = 0x151;
			Thread::CreateThread( &net_thread_data );
			man.SetNetThreadId( net_thread_data.m_osId );
			StartThread( man.GetNetThreadId(), &man );
			
			break;
		}
		case vSERVER_LIST_STATE_GETTING_MOTD:
		{
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
			if(( Tmr::GetTime() - man.m_ghttp_start_time ) > vHTTP_TIMEOUT )
			{
				ghttpCancelRequest( man.m_ghttp_request );
				man.SetServerListState( vSERVER_LIST_STATE_FAILED_MOTD );
			}
			else
			{
				ghttpThink(); 
			}
			
			Mem::Manager::sHandle().PopContext();
			break;
		}
		case vSERVER_LIST_STATE_TRACK_USAGE:
		{
			Thread::PerThreadStruct	net_thread_data;
			
			ghttpCleanup();

			//Dbg_Printf( "Serverlist State: Starting track usage\n" );

			// Pause the music & streams before doing DNS lookup
			Pcm::PauseStream( 1 );
            
			// Now spawn the thread that gets the message of the day
			net_thread_data.m_pEntry = s_threaded_track_usage;
			net_thread_data.m_iInitialPriority = vSOCKET_THREAD_PRIORITY;
			net_thread_data.m_pStackBase = man.GetNetThreadStack();
			net_thread_data.m_iStackSize = vNET_THREAD_STACK_SIZE;
			net_thread_data.m_utid = 0x151;
			Thread::CreateThread( &net_thread_data );
			man.SetNetThreadId( net_thread_data.m_osId );
			StartThread( man.GetNetThreadId(), &man );
			break;
		}
		case vSERVER_LIST_STATE_TRACKING_USAGE:
		{
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
			if(( Tmr::GetTime() - man.m_ghttp_start_time ) > vHTTP_TIMEOUT )
			{
				ghttpCancelRequest( man.m_ghttp_request );
				man.SetServerListState( vSERVER_LIST_STATE_SHOW_MOTD );
			}
			else
			{
				ghttpThink(); 
			}
			Mem::Manager::sHandle().PopContext();
			break;
		}
		case vSERVER_LIST_STATE_SHOW_MOTD:
		{
			Script::CScriptStructure* pStructure;
			
			//Dbg_Printf( "Serverlist State: Got MotD\n" );

			ghttpCleanup();

			man.RemoveMessageOfTheDay();

			pStructure = new Script::CScriptStructure;
			pStructure->AddComponent( Script::GenerateCRC( "message" ), ESYMBOLTYPE_STRING, man.m_motd );
			
			Script::RunScript( "create_motd_menu", pStructure );
			
			delete pStructure;
			
			man.SetServerListState( vSERVER_LIST_STATE_WAIT );
			man.m_server_list_wait_time = 0;
			break;
		}
		case vSERVER_LIST_STATE_FAILED_MOTD:
		{
			Dbg_Printf( "In vSERVER_LIST_STATE_FAILED_MOTD\n" );
			ghttpCleanup();
			Script::RunScript( "CreateMotdFailedDialog" );

			// NOTE: USED TO CALL THIS FROM THE "OK" DIALOG BOX HANDLER
			// gamenet_man->SetServerListState( vSERVER_LIST_STATE_STARTING_LOBBY_LIST )
						
			//man.SetServerListState( vSERVER_LIST_STATE_WAIT );
			man.SetServerListState( vSERVER_LIST_STATE_SHUTDOWN );
			man.m_server_list_wait_time = 0;
			break;
		}
		case vSERVER_LIST_STATE_STARTING_LOBBY_LIST:
		{
			// First, show the dialog box
			Script::RunScript( "CreateGettingLobbyListDialog" );

			// Free up the previous lobby list
			man.mpLobbyMan->StopLobbyList();
			
			// Now spawn the lobby list -- this function also changes the state
			man.mpLobbyMan->StartLobbyList();
			break;
		}
		case vSERVER_LIST_STATE_GETTING_LOBBY_LIST:
		{

			//Dbg_Printf( "Serverlist State: Getting Lobby List\n" );
            
            man.SetServerListState( vSERVER_LIST_STATE_WAIT );
			man.m_server_list_wait_time = 0;
			break;
		}
		case vSERVER_LIST_STATE_FAILED_LOBBY_LIST:
		{
			Script::CScriptStructure* pStructure;
			
			//Dbg_Printf( "Failed lobby list\n" );
			man.mpLobbyMan->StopLobbyList();
		
			pStructure = new Script::CScriptStructure;
			pStructure->AddComponent( Script::GenerateCRC( "message" ), ESYMBOLTYPE_STRING, Script::GetLocalString( "net_status_gamespy_no_connect" ));

			Script::RunScript( "CreateFailedLobbyListDialog", pStructure );

			delete pStructure;
					
			man.SetServerListState( vSERVER_LIST_STATE_SHUTDOWN );
			break;
		}
		case vSERVER_LIST_STATE_GOT_LOBBY_LIST:
		{
			// If we didn't get any lobbies, that either means the matchmaker's down or their
			// connection isn't valid
			if( man.mpLobbyMan->GetLobbyInfo( 0 ) == NULL )
			{
				Script::CScriptStructure* pStructure = new Script::CScriptStructure;
				
				pStructure->AddComponent( Script::GenerateCRC( "message" ), ESYMBOLTYPE_STRING, Script::GetLocalString( "net_status_gamespy_no_connect" ));

				Script::RunScript( "CreateFailedLobbyListDialog", pStructure );

				delete pStructure;
				
				man.SetServerListState( vSERVER_LIST_STATE_SHUTDOWN );
			}
			else
			{
				man.SetServerListState( vSERVER_LIST_STATE_FILL_LOBBY_LIST );
			}
			
			Pcm::PauseStream( 0 );
			break;
		}
		case vSERVER_LIST_STATE_FILL_LOBBY_LIST:
		{
			man.mpLobbyMan->FillLobbyList();
			man.SetServerListState( vSERVER_LIST_STATE_SHUTDOWN );
			break;
		}
		
		default:
			break;
	}
#endif

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::s_auto_refresh_code( const Tsk::Task< Manager >& task )
{
	Manager& man = task.GetData();
	static Tmr::Time s_refresh_time = 0;

	if(( Tmr::GetTime() - s_refresh_time ) > vTIME_BETWEEN_AUTO_REFRESH )
	{
		s_refresh_time = Tmr::GetTime();
#ifdef __PLAT_XBOX__
		man.RefreshServerList( true );
#endif
	}
	else
	{
		if( s_refresh_pending )
		{
			if(( Tmr::GetTime() - s_last_refresh_time ) > vLAN_SERVER_LIST_TIMEOUT )
			{
				Lst::Search< ServerInfo > sh;
				ServerInfo* server, *next;

				man.ClearServerList();
						
				for( server = sh.FirstItem( man.m_temp_servers ); server; server = next )
				{
					next = sh.NextItem();
					server->Remove();

					man.AddServerToMenu( server, man.m_servers.CountItems());
					man.m_servers.AddToTail( server );					
				}
				Script::RunScript( "update_lobby_server_list" );
				
				s_refresh_pending = false;
			}
		}
	}
}

/*****************************************************************************
**							  Handler Functions								**
*****************************************************************************/

/******************************************************************/
/* Respond to a client who is looking for games to join on the LAN*/
/* Tell them info about the game                                  */
/******************************************************************/

int Manager::s_handle_server_response( Net::MsgHandlerContext* context )
{
	MsgServerDescription* msg;
	ServerInfo *server_info;
	Manager* manager = (Manager*) context->m_Data;
	int i;

	//Dbg_Printf( "GOT SERVER RESPONSE!\n" );

	msg = (MsgServerDescription*) context->m_Msg;

	if( context->m_MsgLength != sizeof( MsgServerDescription ))
	{
		return Net::HANDLER_MSG_DONE;
	}
	
#ifdef	__PLAT_XBOX__
    if( manager->ServerAlreadyInList( msg->m_Name, &msg->m_XboxAddr ))
	{
		return Net::HANDLER_MSG_DONE;
	}
#else
	if( manager->ServerAlreadyInList( msg->m_Name, context->m_Conn->GetIP()))
	{
		return Net::HANDLER_MSG_DONE;
	}
#endif

	
#ifdef	__PLAT_XBOX__
	if( memcmp( msg->m_Nonce, context->m_App->m_Nonce, 8 ))
	{
		return Net::HANDLER_MSG_DONE;
	}
#endif

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	server_info = new ServerInfo;
	
	strcpy( server_info->m_Name, msg->m_Name );
	server_info->m_Ip = context->m_Conn->GetIP();
	server_info->m_Latency = Tmr::GetTime() - msg->m_Timestamp;
	strcpy( server_info->m_Level, msg->m_Level );
	strcpy( server_info->m_Mode, msg->m_Mode );
	server_info->m_MaxPlayers = msg->m_MaxPlayers;
	server_info->m_NumPlayers = msg->m_NumPlayers;
	server_info->m_MaxObservers = msg->m_MaxObservers;
	server_info->m_NumObservers = msg->m_NumObservers;
	server_info->m_Password = msg->m_Password;
	server_info->m_GameStarted = msg->m_GameStarted;
	server_info->m_HostMode = msg->m_HostMode;
	server_info->m_Ranked = msg->m_Ranked;
	server_info->m_SkillLevel = msg->m_SkillLevel;
	server_info->m_Port = vHOST_PORT;
#ifdef __PLAT_XBOX__
	memcpy( &server_info->m_XboxKeyId, &msg->m_XboxKeyId, sizeof( XNKID ));
	memcpy( &server_info->m_XboxKey, &msg->m_XboxKey, sizeof( XNKEY ));	
	memcpy( &server_info->m_XboxAddr, &msg->m_XboxAddr, sizeof( XNADDR ));	
#endif    
    
	for( i = 0; i < server_info->m_NumPlayers; i++ )
	{
		server_info->AddPlayer( msg->m_PlayerNames[i] );
	}

	//manager->AddServerToMenu( server_info, manager->m_servers.CountItems());

	//manager->m_servers.AddToTail( server_info );
	manager->m_temp_servers.AddToTail( server_info );
    
	Mem::Manager::sHandle().PopContext();

	return Net::HANDLER_CONTINUE;
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#ifdef __PLAT_NGPS__

void	Manager::s_create_game_callback( PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, 
											void * param )
{
	if( success )
	{
		//Dbg_Printf( "Joined Created Room\n" );
	}
	else
	{
		switch( result )
		{
			case PEERFullRoom:
				//Dbg_Printf( "Couldnt join created room. Full\n" );
				break;
			case PEERInviteOnlyRoom:
				//Dbg_Printf( "Couldnt join created room. Invite Only\n" );
				break;
			case PEERBannedFromRoom:
				//Dbg_Printf( "Couldnt join created room. Banned\n" );
				break;
			case PEERBadPassword:
				//Dbg_Printf( "Couldnt join created room. Bad Password\n" );
				break;
			case PEERAlreadyInRoom:
				//Dbg_Printf( "Couldnt join created room. Already in room\n" );
				break;
			case PEERNoTitleSet:
				//Dbg_Printf( "Couldnt join created room. No Title Set\n" );
				break;
			default:
				//Dbg_Printf( "Couldnt join created room. General error\n" );
				break;
		}
	}
}

#endif // __PLAT_NGPS__
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::PostGame( void )
{
#ifdef __PLAT_NGPS__
	Net::Manager * net_man = Net::Manager::Instance();
	
    Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	
	mpLobbyMan->StartReportingGame();
	if( mpBuddyMan->IsLoggedIn())
	{
		char location[1024];
		const char *server_name;
		Script::CScriptStructure* pStructure;
		Prefs::Preferences* pPreferences;

		pPreferences = GetNetworkPreferences();
		pStructure = pPreferences->GetPreference( Script::GenerateCRC("server_name") );
		pStructure->GetText( "ui_string", &server_name, true );

		if( mpLobbyMan->GetPeer())
		{
			sprintf( location, "%d:%d:%d:%s (%s)", net_man->GetPublicIP(), peerGetPrivateIP( mpLobbyMan->GetPeer()), vHOST_PORT, server_name, mpLobbyMan->GetLobbyName());
		}
		mpBuddyMan->SetStatusAndLocation( GP_PLAYING, (char*) Script::GetString( "homie_status_hosting" ), location );
	}
	Mem::Manager::sHandle().PopContext();
#endif
}


/*void Manager::RequestMatchmakerConnect( void )
{
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();

#ifdef __PLAT_NGPS__
	int result;

	m_server->SetForeignPacketHandler( gamespy_data_handler );
	s_gamespy_parse_data[0] = '\0';

	// Hardcode the IP of the master server until we get the message of the day squared away
	result = qr_init_socket( NULL, m_server->GetSocket(), "thps4ps2", "H2r8W1", basic_callback,
							 info_callback, rules_callback, players_callback, this );
	// Uncomment for media burns
	//result = qr_init_socket( NULL, m_server->GetSocket(), "thps3media", "tRKg39", basic_callback,
							 //info_callback, rules_callback, players_callback, this );
	 
	switch( result )
	{
		case 0:
			// success:
			break;
		case E_GOA_WSOCKERROR:
			Dbg_MsgAssert( 0, ( "Failed to initialize GameSpy query report toolkit due to Socket Error\n" ));
			break;
		case E_GOA_BINDERROR:
			Dbg_MsgAssert( 0, ( "Failed to initialize GameSpy query report toolkit due to Bind Error\n" ));
			break;
		case E_GOA_CONNERROR:
			Dbg_MsgAssert( 0, ( "Failed to initialize GameSpy query report toolkit due to Connection Error\n" ));
			break;
		default:
			Dbg_MsgAssert( 0, ( "Failed to initialize Gamespy query report toolkit\n" ));
			break;
	}

#endif
	
#ifdef __PLAT_NGPS__
	s_game_start_time = Tmr::GetTime();
	s_notified_user_not_connected = false;
	s_retried_game_post = false;
	gGotGamespyCallback = false;
#endif		// __PLAT_NGC__
	mlp_manager->AddLogicTask( *m_process_gamespy_queries_task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetServerFocus( ServerInfo* server )
{
	ClearServerFocus();
	server->m_InFocus = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ClearServerFocus( void )
{
	Lst::Search< ServerInfo > sh;
	ServerInfo* server;

	for( server = sh.FirstItem( m_servers ); server; server = sh.NextItem())
	{
		server->m_InFocus = false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ServerInfo*		Manager::GetServerFocus( void )
{
	Lst::Search< ServerInfo > sh;
	ServerInfo* server;

	for( server = sh.FirstItem( m_servers ); server; server = sh.NextItem())
	{
		if( server->m_InFocus )
		{
			return server;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Manager::NumServersListed( void )
{
	Lst::Search< ServerInfo > sh;
	ServerInfo* server;
	int num_listed;

	num_listed = 0;
	for( server = sh.FirstItem( m_servers ); server; server = sh.NextItem())
	{
		if( server->m_Listed )
		{
			num_listed++;
		}
	}

	return num_listed;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::AddServerToMenu( ServerInfo* server, int index )
{   
	Script::CStruct* p_item_params;
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
	uint32 server_id;
		
	// If this is our first listed server, destroy the "no servers found" message
	if( NumServersListed() == 0 )
	{
		Script::RunScript( "destroy_server_menu_children" );
	}

	Script::RunScript( "prepare_server_menu_for_new_children" );
		
	p_item_params = new Script::CStruct;	
	p_item_params->AddString( "text", server->m_Name );
	p_item_params->AddString( "mode", server->m_Mode );
	printf("The game mode is: %s\n", server->m_Mode );
#ifdef __PLAT_NGPS__
	if( InInternetMode())
	{
		server_id = (uint32) server->m_GServer;
		
	}
	else
#endif
	{
		server_id = (uint32) server;
	}
	
	p_item_params->AddChecksum( "id", server_id );
	p_item_params->AddChecksum( "parent", Script::GenerateCRC( "server_list_menu" ));
	p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("choose_selected_server"));
	p_item_params->AddChecksum( "focus_script",Script::GenerateCRC("describe_selected_server"));

	// create the parameters that are passed to the X script
	Script::CStruct *p_script_params= new Script::CStruct;
	p_script_params->AddChecksum( "id", server_id );	
	p_item_params->AddStructure("pad_choose_params",p_script_params);			
	p_item_params->AddStructure("focus_params",p_script_params);

	Script::RunScript("server_list_menu_add_item",p_item_params);
	
	server->m_Listed = true;

	delete p_item_params;
	delete p_script_params;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#ifdef __PLAT_NGPS__

GHTTPBool	Manager::MotdComplete( GHTTPRequest request, GHTTPResult result, char* buffer, int buffer_len, 
						  void* param )
{
	Manager * gamenet_man = Manager::Instance();
	bool succeeded;

	
    
	succeeded = false;
	if( result == GHTTPSuccess )
	{   
		char* token;
		char* message, *ip_ptr;
		char temp_motd[4096];
		int server_ip;

		strncpy( temp_motd, buffer, 4095 );
		temp_motd[4095] = '\0';
		//Dbg_Printf( "Got Message of the day: %s\n", temp_motd );
		
		// Checksum comes first and is delimited by a space
		token = strtok( temp_motd, " " );
		if( token )
		{
		 	uint32 checksum;

			checksum = atoi( token );
			//Dbg_Printf( "checksum is %d\n", checksum );
			ip_ptr = token + strlen( token ) + 1;	// skip the checksum
			if( checksum == Script::GenerateCRC( ip_ptr ))
			{
				//Dbg_Printf( "Matched!\n" );
				// Next token is the IP address for the gamespy servers
				ip_ptr = strtok( NULL, " " );
				if( ip_ptr )
				{
					//Dbg_Printf( "Got an IP Pointer of %s!\n", ip_ptr );
					message = strtok( NULL, "\n" );
					if( message )
					{
						//Dbg_Printf( "Got a message of %s\n", message );
						strncpy( gamenet_man->m_motd, message, 1023 );
						gamenet_man->m_motd[1023] = '\0';

						server_ip = atoi( ip_ptr );
						
                        //sprintf( ServerListHostname, "%s", inet_ntoa(*(struct in_addr*) &server_ip ));
						//sprintf( qr_hostname, "%s", inet_ntoa(*(struct in_addr*) &server_ip ));

						//Dbg_Printf( "GameSpy Server is %s\n", ServerListHostname );
						succeeded = true;
					}
				}
			}
		}

		if( succeeded )
		{   
			gamenet_man->m_got_motd = true;
			gamenet_man->SetServerListState( vSERVER_LIST_STATE_TRACK_USAGE );
		}
	}

	if( !succeeded )
	{
		Dbg_Printf( "Error Downloading motd: %d\n", result );

		// No retries in THPS4. We're just going to use our neversoft website and that's it
		/*s_motd_retry = s_motd_retry + 1;
		if( gamenet_man->m_master_servers[ s_motd_retry ][0] != '\0' )
		{
			gamenet_man->SetServerListState( vSERVER_LIST_STATE_RETRY_MOTD );
			return GHTTPTrue;
		}*/

		gamenet_man->SetServerListState( vSERVER_LIST_STATE_FAILED_MOTD );
	}

	return GHTTPTrue;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void MotdProgress( GHTTPRequest request, GHTTPState state, const char * buffer, int bufferLen,
					int bytesReceived, int totalSize, void * param )
{
	

	switch( state )
	{
		case GHTTPHostLookup:
			Dbg_Printf( "Lookin up HTTP host...\n" );
			break;
		case GHTTPConnecting:
			Dbg_Printf( "Connecting to HTTP host...\n" );
			break;
		case GHTTPSendingRequest:
			Dbg_Printf( "Sending Request to HTTP host...\n" );
			break;
		case GHTTPPosting:
			break;
		case GHTTPWaiting:
			Dbg_Printf( "Waiting for response from HTTP host...\n" );
			break;
		case GHTTPReceivingStatus:
			Dbg_Printf( "Receiving status....\n" );
			break;
		case GHTTPReceivingHeaders:
			Dbg_Printf( "Receiving headers....\n" );
			break;
		case GHTTPReceivingFile:
			Dbg_Printf( "Receiving file: %d bytes....\n", bytesReceived );
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

GHTTPBool	Manager::TrackUsageComplete( GHTTPRequest request, GHTTPResult result, char* buffer, int buffer_len, 
						  void* param )
{
	Manager * gamenet_man = Manager::Instance();

	//Dbg_Printf( "Track usage complete!!!\n" );

	gamenet_man->SetServerListState( vSERVER_LIST_STATE_SHOW_MOTD );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::s_threaded_get_message_of_the_day( Manager* gamenet_man )
{
	char motd_path[256];

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	// Register this thread with the sockets API
	sockAPIregthr();

	// Wait a bit before starting the http transfer 
	msleep( 100 );

	gamenet_man->SetServerListState( vSERVER_LIST_STATE_GETTING_MOTD );
	
	gamenet_man->m_ghttp_start_time = Tmr::GetTime();
	sprintf( motd_path, "http://www.thugonline.com/motd.txt" );
	//sprintf( motd_path, "http://www.neversoft.com/thps5/motd.txt" );
	gamenet_man->m_ghttp_request = ghttpGetEx( 	motd_path,
												NULL,
												gamenet_man->m_motd,
												1024,
												NULL,
												GHTTPFalse,
												GHTTPFalse,
												MotdProgress,
												MotdComplete,
												gamenet_man );

	// Deregister this thread with the sockets API
	sockAPIderegthr();
		
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::s_threaded_track_usage( Manager* gamenet_man )
{
	Prefs::Preferences*	prefs;
	Script::CStruct* pStructure;
	const char* unique_id = "123456789";

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());

	// Register this thread with the sockets API
	sockAPIregthr();
        
	prefs = gamenet_man->GetNetworkPreferences();
	pStructure = prefs->GetPreference( Script::GenerateCRC("unique_id"));
	pStructure->GetString( "ui_string", &unique_id );
	//GOASetUniqueID( unique_id );

	gamenet_man->m_ghttp_start_time = Tmr::GetTime();
	gamenet_man->m_ghttp_request = ptTrackUsage( 	0,	// int userID ( if not using Presence & Messaging, send zero )
													vGAMESPY_PRODUCT_ID,	// int productID,
													"1.0",	//const char * versionUniqueID,
													0,	//int distributionID,
													GHTTPFalse,
													TrackUsageComplete );	//PTBool blocking

	gamenet_man->SetServerListState( vSERVER_LIST_STATE_TRACKING_USAGE );

	// Deregister this thread with the sockets API
	sockAPIderegthr();
		
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::RemoveMessageOfTheDay( void )
{
	//ghttpCleanup();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::s_server_list_callback( PEER peer, PEERBool success, const char * name, SBServer server, PEERBool staging, 
									  int msg, int progress, void * param )
{
	Manager* man = (Manager*) param;
	if( success )
	{
		switch( msg )
		{
		
			case PEER_ADD:
			{
				ServerInfo* server_info;
				int i;
				
				Dbg_Printf( "****** ADDING SERVER %p : %s\n", server, name );
				Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());
	
				server_info = new ServerInfo;
				server_info->m_GServer = server;
				strncpy( server_info->m_Name, name, vMAX_SERVER_NAME_LEN );
				server_info->m_Name[vMAX_SERVER_NAME_LEN] = '\0';
				server_info->m_Latency = SBServerGetPing( server );

				// Check if we're behind the same NAT
                if( SBServerHasPrivateAddress( server ))
				{
					// If so, we'll have the same public IP
                    if( SBServerGetPublicInetAddress( server ) == peerGetLocalIP( peer ))
					{
						server_info->m_Ip = SBServerGetPrivateInetAddress( server );						
						server_info->m_Port = vHOST_PORT;
					}
					else
					{
						// If we're not behind the same NAT, check if they are behind
						// a promiscuous NAT or, perhaps, not behind a NAT at all.  If so
						// we can directly connect to their public IP
						if( SBServerDirectConnect( server ))
						{
							server_info->m_Ip = SBServerGetPublicInetAddress( server );
							server_info->m_Port = SBServerGetPublicQueryPort( server );
						}
						else
						{
							server_info->m_Ip = SBServerGetPublicInetAddress( server );
							server_info->m_Port = SBServerGetPublicQueryPort( server );
							server_info->m_CanDirectConnect = false;
							server_info->m_Latency = 9999;	// We can't get the ping of servers behind non-promiscuous NATs
						}
					}
				}
				else
				{
					if( SBServerDirectConnect( server ))
					{
						server_info->m_Ip = SBServerGetPublicInetAddress( server );
						server_info->m_Port = SBServerGetPublicQueryPort( server );
					}
					else
					{
						server_info->m_Ip = SBServerGetPublicInetAddress( server );
						server_info->m_Port = SBServerGetPublicQueryPort( server );
						server_info->m_CanDirectConnect = false;
						server_info->m_Latency = 9999;	// We can't get the ping of servers behind non-promiscuous NATs
					}
				}
				
				Dbg_Printf( "Percent Done: %d : Server %s ping (%d)\n", (int) progress, server_info->m_Name, server_info->m_Latency );
				strcpy( server_info->m_Level, SBServerGetStringValue( server, "mapname","..."));
				strcpy( server_info->m_Mode, SBServerGetStringValue( server, "gametype","..."));
				server_info->m_NumPlayers = SBServerGetIntValue( server, "numplayers", 1 );
				server_info->m_MaxPlayers = SBServerGetIntValue( server, "maxplayers", 8 );
				server_info->m_NumObservers = SBServerGetIntValue( server, "numobservers", 0 );
				server_info->m_MaxObservers = SBServerGetIntValue( server, "maxobservers", 1 );
				server_info->m_Password = SBServerGetBoolValue( server, "password", false );
				server_info->m_GameStarted = SBServerGetBoolValue( server, "started", false );
				server_info->m_HostMode = SBServerGetIntValue( server, "hostmode", vHOST_MODE_SERVE );
				server_info->m_Ranked = SBServerGetIntValue( server, "ranked", 0 );
				server_info->m_SkillLevel = SBServerGetIntValue( server, "skilllevel", vSKILL_LEVEL_DEFAULT );

				Dbg_Printf( "Numplayers %d\n", server_info->m_NumPlayers )
				for( i = 0; i < server_info->m_NumPlayers; i++ )
				{
					int rating;

					rating = SBServerGetPlayerIntValue( server, i, "rating", 0 );
					Dbg_Printf( "Adding player %s with rating %d\n", (char*) SBServerGetPlayerStringValue( server, i, "player","..." ), rating );
					server_info->AddPlayer((char*) SBServerGetPlayerStringValue( server, i, "player","..." ), rating );
				}
				
				man->m_servers.AddToTail( server_info );
				if( SBServerHasBasicKeys( server ))
				{
					man->AddServerToMenu( server_info, man->m_servers.CountItems() - 1 );
					Script::RunScript( "update_lobby_server_list" );
					server_info->m_HasBasicInfo = true;
				}

				Mem::Manager::sHandle().PopContext();
				break;
			}
	
			//   Remove this server from the list.  The server object for this server
			//   should not be used again after this callback returns.
			case PEER_REMOVE:
			{
				Lst::Search< ServerInfo > sh;
				ServerInfo* server_info, *next;
				Script::CStruct* pParams;
				
				Dbg_Printf( "****** REMOVING SERVER %p\n", server );

				pParams = new Script::CStruct;
				pParams->AddChecksum( "server_id", (uint32) server );
				Script::RunScript( "destroy_lobby_server", pParams );
				delete pParams;
	    
				for( server_info = sh.FirstItem( man->m_servers ); server_info; server_info = next )
				{
					next = sh.NextItem();
					if( server_info->m_GServer == server )
					{
						delete server_info;
						break;
					}
				}

				Script::RunScript( "update_lobby_server_list" );
                break;
			}

			//   Clear the list.  This has the same effect as if this was called
			//   with PEER_REMOVE for every server listed.
			case PEER_CLEAR:
			{
				Dbg_Printf( "****** CLEAR SERVER LIST\n" );

				man->ClearServerList( false );
				man->FreeServerList();

				Script::RunScript( "update_lobby_server_list" );
				break;
			}
			//   This server is already on the list, and its been updated.
			case PEER_UPDATE:
			{
				Lst::Search< ServerInfo > sh;
				ServerInfo* server_info;
				int i;
				
				if( SBServerHasFullKeys( server ))
				{
					Dbg_Printf( "****** FULLY UPDATING SERVER %p\n", server );
				}
				else
				{
					Dbg_Printf( "****** PARTIALLY UPDATING SERVER %p\n", server );
				}
				
	    
				for( server_info = sh.FirstItem( man->m_servers ); server_info; server_info = sh.NextItem())
				{
					if( server_info->m_GServer == server )
					{
						strncpy( server_info->m_Name, name, vMAX_SERVER_NAME_LEN );
						server_info->m_Name[vMAX_SERVER_NAME_LEN] = '\0';
						//server_info->m_Ip = SBServerGetPublicInetAddress( server );
						if( server_info->m_CanDirectConnect )
						{
							server_info->m_Latency = SBServerGetPing( server );
						}
						Dbg_Printf( "Percent Done: %d : Server %s ping (%d)\n", (int) progress, server_info->m_Name, SBServerGetPing(server));
						strcpy( server_info->m_Level, SBServerGetStringValue( server, "mapname","..."));
						strcpy( server_info->m_Mode, SBServerGetStringValue( server, "gametype","..."));
						server_info->m_NumPlayers = SBServerGetIntValue( server, "numplayers", 1 );
						server_info->m_MaxPlayers = SBServerGetIntValue( server, "maxplayers", 8 );
						server_info->m_NumObservers = SBServerGetIntValue( server, "numobservers", 0 );
						server_info->m_MaxObservers = SBServerGetIntValue( server, "maxobservers", 1 );
						server_info->m_Password = SBServerGetBoolValue( server, "password", false );
						server_info->m_GameStarted = SBServerGetBoolValue( server, "started", false );
						server_info->m_HostMode = SBServerGetIntValue( server, "hostmode", vHOST_MODE_SERVE );
						server_info->m_Ranked = SBServerGetIntValue( server, "ranked", 0 );
						server_info->m_SkillLevel = SBServerGetIntValue( server, "skilllevel", vSKILL_LEVEL_DEFAULT );
						//server_info->m_Port = SBServerGetPublicQueryPort( server );
						server_info->ClearPlayerNames();
						Dbg_Printf( "Game Mode: %s\n", server_info->m_Mode );
						Dbg_Printf( "Numplayers %d\n", server_info->m_NumPlayers )
						for( i = 0; i < server_info->m_NumPlayers; i++ )
						{
							int rating;

							rating = SBServerGetPlayerIntValue( server, i, "rating", 0 );
							Dbg_Printf( "Adding player %s with rating %d\n", (char*) SBServerGetPlayerStringValue( server, i, "player","..." ), rating );
							server_info->AddPlayer((char*) SBServerGetPlayerStringValue( server, i, "player","..." ), rating );
						}
						
						if( server_info->m_HasBasicInfo == false )
						{
							if( SBServerHasBasicKeys( server ))
							{
								man->AddServerToMenu( server_info, 0 );
								Script::RunScript( "update_lobby_server_list" );
								server_info->m_HasBasicInfo = true;
							}
						}

						if( server_info->m_HasFullInfo == false )
						{
							if( SBServerHasFullKeys( server ))
							{
								server_info->m_HasFullInfo = true;
								if( server_info->m_InFocus )
								{
									Script::CStruct* params;

									params = new Script::CStruct;
									params->AddChecksum( "id", (uint32) server );
									Script::RunScript( "describe_selected_server", params );
									delete params;
								}
							}
						}
						break;
					}
				}
                break;
			}
		}
	}
}

#endif 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::ScriptGetNumServersInLobby(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager * gamenet_man = Manager::Instance();
	Script::CStruct* p_return_params;

	p_return_params = pScript->GetParams();
	p_return_params->AddInteger( "num_servers", gamenet_man->m_servers.CountItems());

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Manager::StartServerList( void )
{
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();

	if( InLanMode())
	{
		Net::Client* client;

		MatchClientShutdown();

		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

		client = SpawnMatchClient();

		Mem::Manager::sHandle().PopContext();

		if( client == NULL )
		{
			return false;
		}

        if( !m_auto_refresh_task->InList())
		{
			mlp_man->AddLogicTask( *m_auto_refresh_task );
			return true;
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::StopServerList( void )
{
	

	if( InInternetMode())
	{   
#ifdef __PLAT_NGPS__
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
		Dbg_Printf( "******** STOP LISTING GAMES **********\n" );
		if( mpLobbyMan->GetPeer())
		{
			peerStopListingGames( mpLobbyMan->GetPeer());
		}
		Mem::Manager::sHandle().PopContext();
#endif
	}
	else
	{
		Net::Client* client;

		client = GetMatchClient();
		if( client )
		{
			client->m_Dispatcher.Deinit();
		}

        m_auto_refresh_task->Remove();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::FreeServerList( void )
{
	Lst::Search< ServerInfo > sh;
	ServerInfo* server, *next;
	    
	for( server = sh.FirstItem( m_temp_servers ); server; server = next )
	{
		next = sh.NextItem();
		delete server;
	}

	for( server = sh.FirstItem( m_servers ); server; server = next )
	{
		next = sh.NextItem();
		delete server;
	}

	if( InLanMode())
	{
		MatchClientShutdown();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::ClearServerList( bool refocus )
{   
	Script::CStruct* pParams;
	
	pParams = new Script::CStruct;
	if( refocus )
	{
		pParams->AddChecksum( "refocus", Script::GenerateCRC( "refocus" ));
	}
	Script::RunScript( "destroy_server_menu_children", pParams );
	Script::RunScript( "add_no_servers_found_message", pParams );
	delete pParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::RefreshServerList( bool force_refresh )
{
	//Dbg_Printf( "In Refresh server list\n" );
	if( force_refresh == false )
	{
		//Dbg_Printf( "No force\n" );
		// Don't let people refresh like crazy
		if(( Tmr::GetTime() - s_last_refresh_time ) < vTIME_BETWEEN_MANUAL_REFRESH )
		{
			//Dbg_Printf( "Not enough time\n" );
			return;
		}
	}

	s_last_refresh_time = Tmr::GetTime();
	s_refresh_pending = true;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());

	StopServerList();
	FreeServerList();
	StartServerList();

	if( InInternetMode())
	{
#ifdef __PLAT_NGPS__
		unsigned char key_list[] = {NUMPLAYERS_KEY,
									MAXPLAYERS_KEY,
									HOSTPORT_KEY,
									GAMETYPE_KEY };

		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
		
		Dbg_Printf( "******** START LISTING GAMES **********\n" );
		peerStartListingGames( mpLobbyMan->GetPeer(), key_list, sizeof(key_list), NULL, 
								s_server_list_callback, this );
		Mem::Manager::sHandle().PopContext();
#endif
	}
	else
	{   
		FindServersOnLAN();
	}
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef __PLAT_XBOX__
bool		Manager::ServerAlreadyInList( char* name, XNADDR* xbox_addr )
{
	Lst::Search< ServerInfo > sh;
	ServerInfo* server;

	for( server = sh.FirstItem( m_temp_servers ); server; server = sh.NextItem())
	{
		if(	( memcmp( &server->m_XboxAddr, xbox_addr, sizeof( XNADDR ) == 0 )) &&
			( stricmp( server->m_Name, name ) == 0 ))
		{
			return true;
		}
	}
	return false;
}

#else

bool		Manager::ServerAlreadyInList( char* name, int ip )
{
	Lst::Search< ServerInfo > sh;
	ServerInfo* server;

	for( server = sh.FirstItem( m_servers ); server; server = sh.NextItem())
	{
		if(	( server->m_Ip == ip ) &&
			( stricmp( server->m_Name, name ) == 0 ))
		{
			return true;
		}
	}

	for( server = sh.FirstItem( m_temp_servers ); server; server = sh.NextItem())
	{
		if(	( server->m_Ip == ip ) &&
			( stricmp( server->m_Name, name ) == 0 ))
		{
			return true;
		}
	}

	
	return false;
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::SortPreviousKey( void )
{
	if( m_sort_key == 0 )
	{
		m_sort_key = vNUM_SORT_KEYS - 1;
	}
	else
	{
		m_sort_key--;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::SortNextKey( void )
{
	m_sort_key = ( m_sort_key + 1 ) % vNUM_SORT_KEYS;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Manager::SortServerList( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ServerInfo*	Manager::GetServerInfo( uint32 id )
{
	Lst::Search< ServerInfo > sh;
	ServerInfo* server;

	for( server = sh.FirstItem( m_servers ); server; server = sh.NextItem())
	{
#ifdef __PLAT_NGPS__
		if( InInternetMode())
		{
			if( id == (uint32) server->m_GServer )
			{
				return server;
			}
		}
		else
#endif
		{
			if( id == (uint32) server )
		{
			return server;
		}
		}
	}
	
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ServerInfo::ServerInfo( void ) : Lst::Node< ServerInfo > ( this )
{
	m_Listed = false;
	m_InFocus = false;
	m_Latency = 0;
	sprintf( m_Level, "None" );
	sprintf( m_Mode, "None" );
	m_MaxPlayers = 8;
	m_NumPlayers = 1;
	m_MaxObservers = 1;
	m_NumObservers = 0;
	m_SkillLevel = vSKILL_LEVEL_DEFAULT;
	m_HostMode = vHOST_MODE_SERVE;
	m_Ranked = false;
	m_GameStarted = false;
	m_CanDirectConnect = true;
	m_HasBasicInfo = false;
	m_HasFullInfo = false;
#ifdef __PLAT_NGPS__
	m_GServer = NULL;
#endif
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void ServerInfo::ClearPlayerNames( void )
{
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* player, *next;
	Manager * gamenet_man = Manager::Instance();

	for( player = sh.FirstItem( m_players ); player; player = next )
	{
		next = sh.NextItem();
		gamenet_man->DestroyPlayer( player );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ServerInfo::~ServerInfo( void )
{
	ClearPlayerNames();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	ServerInfo::AddPlayer( char* name, int rating )
{   
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	PlayerInfo* player;

	if( gamenet_man->InInternetMode())
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetBottomUpHeap());
	}
	else
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().NetworkHeap());
	}
	
	player = new PlayerInfo( 0 );
	player->m_Rating = rating;
	strncpy( player->m_Name, name, vMAX_PLAYER_NAME_LEN );
	player->m_Name[ vMAX_PLAYER_NAME_LEN ] = '\0';
	m_players.AddToTail( player );
	Mem::Manager::sHandle().PopContext();
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char*	ServerInfo::GetPlayerName( int index )
{
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* player;

	for( player = sh.FirstItem( m_players ); player; player = sh.NextItem())
	{
		if( index == 0 )
		{
			return player->m_Name;
		}
		index--;
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		ServerInfo::GetPlayerRating( int index )
{
	Lst::Search< PlayerInfo > sh;
	PlayerInfo* player;

	for( player = sh.FirstItem( m_players ); player; player = sh.NextItem())
	{
		if( index == 0 )
		{
			return player->m_Rating;
		}
		index--;
	}

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetServerListState( ServerListState state )
{
	//Dbg_Printf( "Setting server list state to %d from %d\n", state, m_server_list_state );
	m_server_list_state = state;
	
	switch( m_server_list_state )
	{
		case vSERVER_LIST_STATE_INITIALIZE:
		{
			Mlp::Manager * mlp_man = Mlp::Manager::Instance();
			
			//Dbg_Printf( "Serverlist State: Initializing\n" );

			mlp_man->AddLogicTask( *m_server_list_state_task );
			SetServerListState( GetNextServerListState());
#ifdef __PLAT_NGPS__
			s_motd_retry = 0;
#endif // __PLAT_NGPS__
			break;
		}
		
		case vSERVER_LIST_STATE_SHUTDOWN:
		{
			//Dbg_Printf( "Serverlist State: Shutting down\n" );

			m_server_list_state_task->Remove();
			break;
		}
		
		default:
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Manager::SetNextServerListState( ServerListState state )
{
	m_next_server_list_state = state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ServerListState Manager::GetServerListState( void )
{
	return m_server_list_state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

ServerListState Manager::GetNextServerListState( void )
{
	return m_next_server_list_state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		Manager::ScriptRetrieveServerInfo(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Manager* gamenet_man = Manager::Instance();

	if( gamenet_man->InLanMode())
	{
		// We have the data and can display it immediately
		return true;
	}
	else
	{
#ifdef __PLAT_NGPS__
		uint32 id;
		ServerInfo* p_server;

		pParams->GetChecksum( Script::GenerateCRC("id"), &id );

		p_server = gamenet_man->GetServerInfo( id );
		Dbg_Assert( p_server );

		gamenet_man->SetServerFocus( p_server );
		if( p_server->m_HasFullInfo )
		{
			// We have the data and can display it immediately
			return true;
		}
		else
		{
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().InternetTopDownHeap());

			peerUpdateGame( gamenet_man->mpLobbyMan->GetPeer(), p_server->m_GServer, PEERTrue );

			Mem::Manager::sHandle().PopContext();

			// We have to request it from the server
			return false;
		}
#else
		return false;
#endif
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		Manager::ScriptDescribeServer(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Script::CStruct* p_item_params;
	ServerInfo* p_server;
	Manager* gamenet_man = Manager::Instance();
	int i;
	char p_string[128];
	Script::CArray* p_skill_array;
	Script::CScriptStructure* p_skill_struct;
	const char* p_skill_string;
	uint32 id;

	pParams->GetChecksum( Script::GenerateCRC("id"), &id );
				
	p_server = gamenet_man->GetServerInfo( id );
	Dbg_Assert( p_server );

	Script::RunScript( "destroy_server_desc_children" );

    Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());

	if( p_server->m_GameStarted )
	{
        // In Progress
        p_item_params = new Script::CStruct;
        sprintf( p_string, "%s:", Script::GetLocalString( "netoptions_str_game_in_progress" ) );
        p_item_params->AddString( "name", p_string );
        Script::RunScript("net_game_info_add_item", p_item_params );
    	delete p_item_params;
	}

	if( p_server->m_HostMode == vHOST_MODE_AUTO_SERVE )
	{
		// Auto Serve
        p_item_params = new Script::CStruct;
        sprintf( p_string, "%s:", Script::GetLocalString( "netoptions_str_auto_serve" ) );
        p_item_params->AddString( "name", p_string );
        Script::RunScript("net_game_info_add_item", p_item_params );
    	delete p_item_params;
	}
	else if( p_server->m_HostMode == vHOST_MODE_FCFS )
	{
		// FCFS
        p_item_params = new Script::CStruct;
        sprintf( p_string, "%s:", Script::GetLocalString( "netoptions_str_fcfs" ) );
        p_item_params->AddString( "name", p_string );
        Script::RunScript("net_game_info_add_item", p_item_params );
    	delete p_item_params;
	}

#ifdef __PLAT_NGPS__
	// Ping
    p_item_params = new Script::CStruct;
    sprintf( p_string, "%s:", Script::GetLocalString( "sort_title_ping" ) );
    p_item_params->AddString( "name", p_string );
	if( p_server->m_Latency == 9999 )
	{
		sprintf( p_string, "..." );
	}
	else
	{
		sprintf( p_string, "%d", p_server->m_Latency );
	}
    
    p_item_params->AddString( "value", p_string );
    Script::RunScript("net_game_info_add_item", p_item_params );
	delete p_item_params;
#endif

	// Mode
    p_item_params = new Script::CStruct;
    sprintf( p_string, "%s:", Script::GetLocalString( "sort_title_mode" ) );
    p_item_params->AddString( "name", p_string );
    sprintf( p_string, "%s", p_server->m_Mode );
    p_item_params->AddString( "value", p_string );
    Script::RunScript("net_game_info_add_item", p_item_params );
	delete p_item_params;
    
	// Skill Level
    p_skill_array = Script::GetArray( "skill_level_info" );
	p_skill_struct = p_skill_array->GetStructure( p_server->m_SkillLevel );
	p_skill_struct->GetText( "name", &p_skill_string );
	p_item_params = new Script::CStruct;	
    sprintf( p_string, "%s:", Script::GetLocalString( "sort_title_skill" ) );
    p_item_params->AddString( "name", p_string );
    sprintf( p_string, "%s", p_skill_string );
    p_item_params->AddString( "value", p_string );
	Script::RunScript("net_game_info_add_item", p_item_params );
	delete p_item_params;

	// Level
    p_item_params = new Script::CStruct;
    sprintf( p_string, "%s:", Script::GetLocalString( "sort_title_level" ) );
    p_item_params->AddString( "name", p_string );
    sprintf( p_string, "%s", p_server->m_Level );
    p_item_params->AddString( "value", p_string );
    Script::RunScript("net_game_info_add_item", p_item_params );
	delete p_item_params;

	// Goals
#ifndef __PLAT_XBOX__
	if( gamenet_man->InInternetMode())
	{
		p_item_params = new Script::CStruct;
		sprintf( p_string, "%s:", Script::GetLocalString( "sort_title_ranked" ) );
		p_item_params->AddString( "name", p_string );
		if( p_server->m_Ranked )
		{
			sprintf( p_string, "%s", Script::GetLocalString( "netoptions_str_yes" ));
		}
		else
		{
			sprintf( p_string, "%s", Script::GetLocalString( "netoptions_str_no" ));
		}
		
		p_item_params->AddString( "value", p_string );
		Script::RunScript("net_game_info_add_item", p_item_params );
		delete p_item_params;
	}
#endif

	// Player Ratio
    p_item_params = new Script::CStruct;
    sprintf( p_string, ": %d/%d", p_server->m_NumPlayers, p_server->m_MaxPlayers );
    p_item_params->AddString( "text", p_string );
    Script::RunScript("net_game_info_update_player_title", p_item_params );
	delete p_item_params;
    
    // List of Players
    for( i = 0; i < p_server->m_NumPlayers; i++ )
	{
		int rank;

		rank = (int) (((float) p_server->GetPlayerRating( i ) / (float) vMAX_RATING ) * (float) vMAX_RANK );
		p_item_params = new Script::CStruct;
        sprintf( p_string, "%d:", i+1 );
		p_item_params->AddInteger( "rank", rank );
        p_item_params->AddString( "name", p_string );
        sprintf( p_string, "%s", p_server->GetPlayerName( i ) );
        p_item_params->AddString( "value", p_string );
		Script::RunScript("net_game_info_add_player", p_item_params );
		delete p_item_params;
	}

	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool		Manager::ScriptChooseServer(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	ServerInfo* p_server;
	Manager* gamenet_man = Manager::Instance();
	bool server_full;
	uint32 reason_checksum;		
	uint32 id;

	pParams->GetChecksum( Script::GenerateCRC("id"), &id );
				
	gamenet_man->ClearServerFocus();

	p_server = gamenet_man->GetServerInfo( id );
	Dbg_Assert( p_server );
								
	server_full = false;
	reason_checksum = 0;
	if( gamenet_man->GetJoinMode() == vJOIN_MODE_PLAY )
	{
		if( p_server->m_NumPlayers >= p_server->m_MaxPlayers )
		{
			server_full = true;
			reason_checksum = Script::GenerateCRC("net_reason_full");
		}
	}
	else if( gamenet_man->GetJoinMode() == vJOIN_MODE_OBSERVE )
	{
		if( p_server->m_NumObservers >= p_server->m_MaxObservers )
		{
			server_full = true;
			reason_checksum = Script::GenerateCRC("net_reason_full_observers");
		}
	}

	if( server_full )
	{   
		Script::CStruct* p_structure = new Script::CStruct;
		p_structure->AddChecksum( "reason", reason_checksum );
		p_structure->AddChecksum( "just_dialog", 0 );

		gamenet_man->m_join_state_task->Remove();
		Script::RunScript( "CreateJoinRefusedDialog", p_structure );
		delete p_structure;
	}
	else
	{
		Script::CScriptStructure* p_structure = new Script::CScriptStructure;

#ifdef __PLAT_NGPS__							
        if( gamenet_man->mpBuddyMan->IsLoggedIn())
		{
			gamenet_man->mpLobbyMan->SetServerName( p_server->m_Name );
		}
#endif

#ifdef __PLAT_XBOX__							
		IN_ADDR host_addr;		
		
		// Register the session key.
		if( XNetRegisterKey( &p_server->m_XboxKeyId, 
			&p_server->m_XboxKey ) != NO_ERROR )
		{
			OutputDebugString( "**** Failed to register key\n" );
			return false;
		}
		
		gamenet_man->m_XboxKeyRegistered = true;
		// Translate the hosts XNADDR to an IN_ADDR.			
		if( XNetXnAddrToInAddr( &p_server->m_XboxAddr, &p_server->m_XboxKeyId,
				&host_addr ) != NO_ERROR )
		{
			OutputDebugString( "**** Failed to get inaddr from xnaddr\n" );
			return false;
		}
		
		memcpy( &gamenet_man->m_XboxKeyId, &p_server->m_XboxKeyId, sizeof( XNKID ));
		
		p_structure->AddComponent( Script::GenerateCRC( "Address" ), 
								ESYMBOLTYPE_INTEGER, (int) host_addr.s_addr );
		p_structure->AddComponent( Script::GenerateCRC( "Port" ), 
								ESYMBOLTYPE_INTEGER, p_server->m_Port );
#else
		char *ip_addr;
		struct in_addr addr;
		uint32 cookie;
		
		addr.s_addr = p_server->m_Ip;
		ip_addr = inet_ntoa( addr );
		if( p_server->m_CanDirectConnect == false )
		{
			cookie = Mth::Rnd( vINT32_MAX );
			p_structure->AddComponent( CRCD( 0x751f4599, "cookie" ), ESYMBOLTYPE_NAME, cookie );
		}
        p_structure->AddComponent( NONAME, ESYMBOLTYPE_INTEGER, p_server->m_Port );
		p_structure->AddComponent( NONAME, ESYMBOLTYPE_STRING, ip_addr );
#endif

		p_structure->AddInteger( "MaxPlayers", p_server->m_MaxPlayers );
		Script::RunScript( "net_chosen_join_server", p_structure );
		delete p_structure;

	}	
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#ifdef __PLAT_NGPS__
bool 		Manager::ScriptPostGame( Script::CStruct* pParams, Script::CScript* pScript )
{
	Manager* gamenet_man = Manager::Instance();

	if( gamenet_man->mpLobbyMan->ReportedGame() == false )
	{
		gamenet_man->PostGame();
	}

	return true;
}

#endif //__PLAT_NGPS__
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

LobbyInfo::LobbyInfo( void ) : Lst::Node< LobbyInfo > ( this )
{
	m_Full = false;
	m_Official = false;
	m_OffLimits = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


} // namespace GameNet






================================================
FILE: Code/Sk/GameNet/XBox/p_auth.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_auth.cpp												**
**																			**
**	Created by:		02/28/02	-	SPG										**
**																			**
**	Description:	XBox Online Authorization Code		 					**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace GameNet
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

const DWORD vMAX_MEMORY_UNITS  = 2 * XGetPortCount();
/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

void	AuthMan::s_logon_state_code( const Tsk::Task< AuthMan >& task )
{
#	if 0
	AuthMan& man = task.GetData();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	switch( man.m_state )
	{
		case vSTATE_FILL_ACCOUNT_LIST:		
			if( man.gather_user_list() == 0 )
			{
				man.m_state = vSTATE_CREATE_ACCOUNT;
			}
			else
			{
				man.fill_user_list();
				man.m_state = vSTATE_SELECT_ACCOUNT;
			}
			break;				
		case vSTATE_SELECT_ACCOUNT:
			break;			
		case vSTATE_CREATE_ACCOUNT:		
			man.create_account();
			break;
		
		case vSTATE_GET_PIN:
			Script::RunScript( "launch_pin_entry_menu" );
			man.m_pin_entry.BeginInput( man.m_chosen_account );
			man.m_state = vSTATE_WAIT;
			break;
		case vSTATE_PIN_COMPLETE:
			man.PinAttempt( man.m_pin_entry.GetPin());
			break;
		case vSTATE_PIN_CANCELLED:
			man.m_pin_entry.EndInput();
			man.m_state = vSTATE_FILL_ACCOUNT_LIST;
			break;
		case vSTATE_PIN_CLEARED:
			man.m_state = vSTATE_GET_PIN;
			man.m_pin_entry.ClearInput();
			break;
		case vSTATE_LOG_ON:
			man.logon();
			man.m_state = vSTATE_LOGGING_ON;
			break;
		case vSTATE_LOGGING_ON:		
			man.check_logon_progress();			
			break;
		case vSTATE_SUCCESS:
			Script::RunScript( "launch_xbox_online_menu" );
			man.m_signed_in = true;
			man.m_state = vSTATE_LOGGED_IN;
			gamenet_man->mpVoiceMan->Startup();
			gamenet_man->mpBuddyMan->SpawnBuddyList( man.m_chosen_account );
			break;
		case vSTATE_LOGGED_IN:
			man.pump_logon_task();
			break;
		case vSTATE_CANCEL:
		{
			Lst::DynamicTableDestroyer destroyer( &man.m_user_list );
			destroyer.DeleteTableContents();

			delete man.mp_logon_state_task;
			man.mp_logon_state_task = NULL;
			man.m_state = vSTATE_WAIT;		
			break;
		}
		case vSTATE_WAIT:
			break;
		case vSTATE_ERROR:
			man.m_state = man.m_next_state;
			switch( man.m_error )
			{
				case vERROR_WRONG_PIN:
					Script::RunScript( "launch_wrong_pin_dialog_box" );
					break;
				case vERROR_GENERAL_LOGIN:
					break;
				case vERROR_LOGIN_FAILED:
					break;
			}			
			break;
	}	
#	endif
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static DWORD	s_get_mem_unit_slot( DWORD i )
{
	return( ( i % 2 ) ? XDEVICE_BOTTOM_SLOT : XDEVICE_TOP_SLOT );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static DWORD	s_get_mem_unit_port( DWORD i )
{
	return( i / 2 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static DWORD	s_get_mem_unit_mask( DWORD i )
{
	// The XGetDevices bitmask is formatted as follows:
    //
    //      0x00000001      port 0 top slot         i = 0
    //      0x00010000      port 0 bottom slot          1
    //      0x00000002      port 1 top slot             2
    //      0x00020000      port 1 bottom slot          3
    //      0x00000004      port 2 top slot             4
    //      0x00040000      port 2 bottom slot          5
    //      0x00000008      port 3 top slot             6
    //      0x00080000      port 3 bottom slot          7

    DWORD dwMask = 1 << s_get_mem_unit_port( i );
    if( s_get_mem_unit_slot( i ) == XDEVICE_BOTTOM_SLOT )
        dwMask <<= 16;

    return( dwMask );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		AuthMan::gather_user_list( void )
{
    // Get accounts stored on the hard disk
    DWORD num_users = 0;

#	if 0
	// On input, the list must have room for XONLINE_MAX_STORED_ONLINE_USERS
    // accounts
    XONLINE_USER user_list[ XONLINE_MAX_STORED_ONLINE_USERS ];

    HRESULT hr = XOnlineGetUsers( user_list, &num_users);
    if( SUCCEEDED(hr) )
    {
        for( DWORD i = 0; i < num_users; ++i )
		{
			XONLINE_USER* new_user;
			
			new_user = new XONLINE_USER;
			*new_user = user_list[i];
            m_user_list.Add( new_user );
		}
    }
#	endif

	return num_users;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::fill_user_list( void )
{
	int i;
	
	Script::RunScript( "create_account_list_menu" );	

	for( i = 0; i < m_user_list.GetSize(); i++ )
	{
		Script::CStruct* p_item_params = new Script::CStruct;
		XONLINE_USER* user;
		
		user = &m_user_list[i];

		//p_item_params->AddString( "text", user->gamertag );
		p_item_params->AddChecksum( "id", 123456 + i );		
		p_item_params->AddChecksum( "pad_choose_script",Script::GenerateCRC("choose_selected_account"));

		// create the parameters that are passed to the X script
		Script::CStruct *p_script_params= new Script::CStruct;
		p_script_params->AddInteger( "index", i );	
		p_item_params->AddStructure("pad_choose_params",p_script_params);			

		Script::RunScript("make_text_sub_menu_item",p_item_params);
		delete p_item_params;
		delete p_script_params;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::create_account( void )
{
	Script::RunScript( "launch_no_accounts_dialog" );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::logon( void )
{	
#	if 0
	XONLINE_USER user_list[ XGetPortCount() ] = { 0 };	
	
	CopyMemory( &user_list[ 0 ], &m_user_list[ m_chosen_account ], sizeof( XONLINE_USER ));

    // Initiate the login process. XOnlineTaskContinue() is used to poll
    // the status of the login.
    HRESULT hr = XOnlineLogon( user_list, mp_services, vNUM_SERVICES, 
                               NULL, &mh_online_task );

    if( FAILED(hr) || mh_online_task == NULL )
    {
		if( mh_online_task != NULL )
		{
			hr = XOnlineTaskClose( mh_online_task );
			mh_online_task = NULL;
		}        
        m_state = vSTATE_ERROR;
		m_next_state = vSTATE_WAIT;
		m_error = vERROR_GENERAL_LOGIN;        
    }
#	endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	AuthMan::xbox_has_voice_device( void )
{
	return false;
	
//	DWORD dwConnectedMicrophones = XGetDevices( XDEVICE_TYPE_VOICE_MICROPHONE );
//	DWORD dwConnectedHeadphones = XGetDevices( XDEVICE_TYPE_VOICE_HEADPHONE );

    // Voice is available if there's at least one mike and one headphone
//	return( dwConnectedMicrophones >= 1 && dwConnectedHeadphones  >= 1 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::pump_logon_task( void )
{
#	if 0
	// Pump the task
	HRESULT hr;
	hr = XOnlineTaskContinue( mh_online_task );
	
	// Check status
	if( FAILED( hr ))
	{	
		//m_UI.SetErrorStr( L"Connection was lost. Must relogin" );
	}
#	endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::check_logon_progress( void )
{
#	if 0
	// Pump the task
	HRESULT hr;
	hr = XOnlineTaskContinue( mh_online_task );
	
	// Check status
	if ( hr != XONLINETASK_S_RUNNING )
	{	
		if ( hr != XONLINE_S_LOGON_CONNECTION_ESTABLISHED  )
        {
            XOnlineTaskClose( mh_online_task );

			m_state = vSTATE_ERROR;
			m_error = vERROR_LOGIN_FAILED;				
            return;
        }		
	
		// Check login status; partial results indicate that login itself
		// has completed.
		BOOL success = TRUE;
		BOOL service_err = FALSE;
		HRESULT hrService = S_OK;
		DWORD i = 0;
		
		// Next, check if the user was actually logged on
		PXONLINE_USER pLoggedOnUsers = XOnlineGetLogonUsers();
		Dbg_Assert( pLoggedOnUsers );

		hr = pLoggedOnUsers[ m_chosen_account ].hr;

		// Check for general errors
		if( FAILED(hr) )
		{
			XOnlineTaskClose( mh_online_task );

			m_state = vSTATE_ERROR;
			m_error = vERROR_LOGIN_FAILED;				
            return;
		}
		
		// Check for service errors
		for( i = 0; i < vNUM_SERVICES; ++i )
		{
			if( FAILED( hrService = XOnlineGetServiceInfo( mp_services[i], NULL) ))
			{
				success = FALSE;
				service_err = TRUE;
				break;
			}		
		}

		// If no errors, login was successful
		m_state = success ? vSTATE_SUCCESS : vSTATE_ERROR;

		if( !success )
		{
			if( service_err )
			{
				m_error = vERROR_SERVICE_DOWN;
				/*m_UI.SetErrorStr( L"Login failed.\n\n"
								  L"Error %x logging into service %d",
								  mp_services[i].hr, mp_services[i].dwServiceID );*/
			}
			else
			{
				m_error = vERROR_LOGIN_FAILED;
				/*m_UI.SetErrorStr( L"Login failed.\n\n"
								  L"Error %x returned by "
								  L"XOnlineTaskContinue", hr );*/					
			}
			XOnlineTaskClose( mh_online_task );
			m_state = vSTATE_ERROR;
			m_error = vERROR_LOGIN_FAILED;				
		}			
		else
		{
			DWORD player_state;

			m_service_info_list.Reset();
			for( i = 0; i < vNUM_SERVICES; ++i )
			{
				// Stored service information for UI
				XONLINE_SERVICE_INFO* service_info;			

				service_info = new XONLINE_SERVICE_INFO;                
                XOnlineGetServiceInfo( mp_services[i], service_info );
				m_service_info_list.Add( service_info );
			}

			// Notify the world of our state change
			player_state = XONLINE_FRIENDSTATE_FLAG_ONLINE;
			if( xbox_has_voice_device())
			{
				player_state |= XONLINE_FRIENDSTATE_FLAG_VOICE;
			}

			XOnlineNotificationSetState( m_chosen_account, player_state,
                                         XNKID(), 0, NULL );			
		}
	}
#	endif
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

AuthMan::AuthMan( void )
: m_user_list( 1 ), m_service_info_list( 1 ), m_signed_in( false )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::UserLogon( void )
{	
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();

	mp_logon_state_task = new Tsk::Task< AuthMan > ( s_logon_state_code, *this,
										Tsk::BaseTask::Node::vNORMAL_PRIORITY );
	mlp_man->AddLogicTask( *mp_logon_state_task );
	m_state = vSTATE_FILL_ACCOUNT_LIST;	
	m_chosen_account = 0;
	mp_services[0] = XONLINE_MATCHMAKING_SERVICE;
    mp_services[1] = XONLINE_BILLING_OFFERING_SERVICE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::CancelLogon( void )
{
	m_state = vSTATE_CANCEL;		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::SelectAccount( int index )
{
	m_chosen_account = index;

#	if 0
	DWORD pin_required = m_user_list[ m_chosen_account ].dwUserOptions & XONLINE_USER_OPTION_REQUIRE_PIN;
	if( pin_required )
	{
		m_state = vSTATE_GET_PIN;
	}
	else
	{
		m_state = vSTATE_LOG_ON;
	}
#	endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::PinAttempt( BYTE* pin )
{	
#	if 0
	if(( memcmp( pin, m_user_list[m_chosen_account].pin, XONLINE_PIN_LENGTH ) == 0 ))
	{
		m_state = vSTATE_LOG_ON;		
	}
	else
	{
		m_state = vSTATE_ERROR;
		m_next_state = vSTATE_CANCEL;
		m_error = vERROR_WRONG_PIN;
	}
#	endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::SetState( LogonState state )
{
	m_state = state;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	AuthMan::SignedIn( void )
{
	return m_signed_in;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	AuthMan::SignOut( void )
{
#	if 0
	XOnlineTaskClose( mh_online_task );
	mh_online_task = NULL;
	m_signed_in = false;
#	endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PinEntry::s_pin_entry_input_logic_code ( const Inp::Handler < PinEntry >& handler )
{	
	PinEntry&	entry = handler.GetData();

	// 'B' and 'Back' cancel the PIN entry
	if(	( handler.m_Input->m_Buttons & Inp::Data::mD_SELECT ) ||
		( handler.m_Input->m_Buttons & Inp::Data::mD_CIRCLE ))
	{
		entry.m_cancelled = true;
		return;
	}

	// 'A' and 'Start' are ignored
	if(	( handler.m_Input->m_Buttons & Inp::Data::mD_X ) ||
		( handler.m_Input->m_Buttons & Inp::Data::mD_START ))
	{
		return;
	}

	XINPUT_GAMEPAD pad = {0};	

	if( handler.m_Input->m_Buttons & Inp::Data::mD_L3 )
	{
		entry.m_cleared = true;
		return;
	}

	if( handler.m_Input->m_Buttons & Inp::Data::mD_LEFT )
	{
		pad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT;
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_RIGHT )
	{
		pad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT;
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_UP )
	{
		pad.wButtons |= XINPUT_GAMEPAD_DPAD_UP;
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_DOWN )
	{
		pad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN;
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_TRIANGLE )
	{
		pad.bAnalogButtons[XINPUT_GAMEPAD_Y] = 255;		
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_SQUARE )
	{
		pad.bAnalogButtons[XINPUT_GAMEPAD_X] = 255;		
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_L1 )
	{
		pad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] = 255;		
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_R1 )
	{
		pad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] = 255;		
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_L2 )
	{
		pad.bAnalogButtons[XINPUT_GAMEPAD_WHITE] = 255;		
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_R2 )
	{
		pad.bAnalogButtons[XINPUT_GAMEPAD_BLACK] = 255;		
	}		

	XINPUT_STATE xis;
    CopyMemory( &xis.Gamepad, &pad, sizeof( pad ));

#	if 0
    HRESULT hr = XOnlinePINDecodeInput( entry.m_pin_input, &xis, &entry.m_frame_entry );
    if( hr == S_OK )
    {
		entry.m_got_entry = true;        
    }
#	endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PinEntry::s_pin_entry_logic_code( const Tsk::Task< PinEntry >& task )
{
#	if 0
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	PinEntry& entry = task.GetData();

	if( entry.m_cleared )
	{
		gamenet_man->mp_AuthMan->SetState( AuthMan::vSTATE_PIN_CLEARED );		
	}
	else if( entry.m_cancelled )
	{		
		gamenet_man->mp_AuthMan->SetState( AuthMan::vSTATE_PIN_CANCELLED );		
	}
	else if( entry.m_got_entry )
	{
		entry.m_pin[entry.m_pin_length++] = entry.m_frame_entry;
		entry.m_got_entry = false;
		// Here update the UI with the latest pin entry

		if( entry.Complete())
		{
			entry.EndInput();
			
			// Submit this entry as a our final pin
			GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
			gamenet_man->mp_AuthMan->SetState( AuthMan::vSTATE_PIN_COMPLETE );
		}
	}
#	endif
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PinEntry::ClearInput( void )
{
	m_pin_length = 0;
	m_got_entry = false;
	m_cleared = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PinEntry::BeginInput( int controller )
{
#	if 0
	int i;

	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	Inp::Manager * inp_manager = Inp::Manager::Instance();

	mp_pin_entry_logic_task = new Tsk::Task< PinEntry > ( s_pin_entry_logic_code, *this,
										Tsk::BaseTask::Node::vNORMAL_PRIORITY );
	mlp_man->AddLogicTask( *mp_pin_entry_logic_task );
	mp_pin_entry_input_handler = new Inp::Handler< PinEntry > ( controller,  
														s_pin_entry_input_logic_code, *this, 
														Tsk::BaseTask::Node::vNORMAL_PRIORITY-1 );
	inp_manager->AddHandler( *mp_pin_entry_input_handler );	

	for( i = 0; i < XONLINE_PIN_LENGTH; i++ )
	{
		m_pin[i] = 0;
	}

	m_pin_length = 0;
	m_frame_entry = 0;
	m_cancelled = false;
	m_got_entry = false;
	m_cleared = false;

	XINPUT_STATE xis = {0};
    m_pin_input = XOnlinePINStartInput( &xis );	
#	endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	PinEntry::Complete( void )
{
//	return( m_pin_length == XONLINE_PIN_LENGTH );
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	PinEntry::EndInput( void )
{
	delete mp_pin_entry_logic_task;
	mp_pin_entry_logic_task = NULL;
	delete mp_pin_entry_input_handler;
	mp_pin_entry_input_handler = NULL;

//	XOnlinePINEndInput( m_pin_input );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

BYTE*	PinEntry::GetPin( void )
{
//	return m_pin;
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet


	



================================================
FILE: Code/Sk/GameNet/XBox/p_auth.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_auth.h												**
**																			**
**	Created by:		02/28/02	-	SPG										**
**																			**
**	Description:	XBox Online Authorization Code							**
**																			**
*****************************************************************************/

#ifndef __GAMENET_XBOX_P_AUTH_H
#define __GAMENET_XBOX_P_AUTH_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace GameNet
{

// Number of services to authenticate
const DWORD vNUM_SERVICES = 2;						
//typedef std::vector< XONLINE_USER > XBUserList;
//typedef std::vector< XONLINE_SERVICE_INFO > ServiceInfoList;

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class PinEntry
{
public:
	void	BeginInput( int controller );    
	void	EndInput( void );
	void	ClearInput( void );
	bool	Complete( void );
	BYTE*	GetPin( void );

private:
//	XPININPUTHANDLE m_pin_input;                  // PIN input handle
	BYTE		m_frame_entry;				// PIN entry this frame
//    BYTE		m_pin[ XONLINE_PIN_LENGTH ];  // Current PIN
    int			m_pin_length;                // number of buttons entered
	bool		m_got_entry;
	bool		m_cancelled;
	bool		m_cleared;
	
	static	Inp::Handler< PinEntry >::Code 	s_pin_entry_input_logic_code;      
	Inp::Handler< PinEntry >*	mp_pin_entry_input_handler;

	static	Tsk::Task< PinEntry >::Code  s_pin_entry_logic_code; 
	Tsk::Task< PinEntry >*		mp_pin_entry_logic_task;
	
};

class AuthMan
{
public:
	typedef enum
	{
		vSTATE_FILL_ACCOUNT_LIST,
		vSTATE_CREATE_ACCOUNT,
		vSTATE_SELECT_ACCOUNT,
		vSTATE_GET_PIN,
		vSTATE_PIN_COMPLETE,
		vSTATE_PIN_CLEARED,
		vSTATE_PIN_CANCELLED,
		vSTATE_LOG_ON,
		vSTATE_LOGGING_ON,		
		vSTATE_LOGGED_IN,
		vSTATE_SUCCESS,
		vSTATE_CANCEL,
		vSTATE_ERROR,
		vSTATE_WAIT,
	} LogonState;

	enum
	{
		vERROR_WRONG_PIN,
		vERROR_GENERAL_LOGIN,
		vERROR_LOGIN_FAILED,
		vERROR_SERVICE_DOWN,
	};

	AuthMan( void );

	void	UserLogon( void );
	void	CancelLogon( void );
	void	SelectAccount( int index );
	void	PinAttempt( BYTE* pin );
	void	SetState( LogonState state );
	void	SignOut( void );
	bool	SignedIn( void );
	
private:
	int		gather_user_list( void );
	void	fill_user_list( void );
	void	create_account( void );
	void	logon( void );
	void	check_logon_progress( void );
	void	pump_logon_task( void );
	bool	xbox_has_voice_device( void ); 

	Lst::DynamicTable< XONLINE_USER > m_user_list;
	LogonState	m_state;
	LogonState	m_next_state;
	int			m_error;
	bool		m_signed_in;
	int			m_chosen_account;
	PinEntry	m_pin_entry;	
	XONLINETASK_HANDLE	mh_online_task;
	DWORD		mp_services[ vNUM_SERVICES ]; // List of desired services
	Lst::DynamicTable< XONLINE_SERVICE_INFO > m_service_info_list;

	static	Tsk::Task< AuthMan >::Code  s_logon_state_code; 
	Tsk::Task< AuthMan >*		mp_logon_state_task;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet

#endif	// __GAMENET_XBOX_P_AUTH_H




================================================
FILE: Code/Sk/GameNet/XBox/p_buddy.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_buddy.cpp												**
**																			**
**	Created by:		02/28/02	-	SPG										**
**																			**
**	Description:	XBox Buddy List Code 									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace GameNet
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

void	BuddyMan::s_buddy_state_code( const Tsk::Task< BuddyMan>& task )
{
	BuddyMan& man = task.GetData();

	switch( man.m_state )
	{
		case vSTATE_UPDATE:
			man.update_buddy_list();
			break;
		case vSTATE_FILL_BUDDY_LIST:
			man.fill_buddy_list();
			man.m_state = vSTATE_WAIT;
			break;
		case vSTATE_SUCCESS:
			break;
		case vSTATE_CANCEL:
			break;
		case vSTATE_ERROR:
			break;
		case vSTATE_WAIT:
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::fill_buddy_list( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	BuddyMan::update_buddy_list( void )
{
#	if 0
	HRESULT hr = XOnlineTaskContinue( mh_friends_enum_task );
    // Enumeration failed
    if( FAILED( hr ) )
    {
        //m_UI.SetErrorStr( L"Friend enumeration failed. Error %x", hr );
        //Reset( TRUE );
        return;
    }
    
    // Update generic status
//    if( XOnlineNotificationIsPending( m_current_user, XONLINE_NOTIFICATION_TYPE_FRIENDREQUEST ))
	{
        //SetStatus( L"You have received a friend request" );
	}
//    else if( XOnlineNotificationIsPending( m_current_user, XONLINE_NOTIFICATION_TYPE_GAMEINVITE ))
	{
        //SetStatus( L"You have received a game invitation" );
	}
//    else
	{
        //SetStatus( L"Friend list refreshed" );
	}
    
	XONLINE_FRIEND	friend_list[MAX_FRIENDS];
	int	i, num_friends;

	num_friends = XOnlineFriendsGetLatest( m_current_user, MAX_FRIENDS, friend_list );
	if( num_friends != m_num_friends )
	{
		m_num_friends = num_friends;
		Lst::DynamicTableDestroyer destroyer( &m_friends_list );
		destroyer.DeleteTableContents();

		for( i = 0; i < num_friends; i++ )
		{
			XONLINE_FRIEND* new_friend;

			new_friend = new XONLINE_FRIEND;
			memcpy( new_friend, &friend_list[i], sizeof( XONLINE_FRIEND ));
			m_friends_list.Add( new_friend );
		}
	}    
#	endif
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

void	BuddyMan::SpawnBuddyList( int user_index )
{
#	if 0
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();

	m_current_user = user_index;

	// Standard init
    HRESULT hr = XOnlineFriendsStartup( NULL, &mh_friends_task );
    if( FAILED(hr) )
    {
        //m_UI.SetErrorStr( L"Friends failed to initialize. Error %x", hr );
        //Reset( TRUE );
    }
    
    // Query server for latest list of friends
    hr = XOnlineFriendsEnumerate( m_current_user, NULL, &mh_friends_enum_task );
    if( FAILED(hr) )
    {
        //m_UI.SetErrorStr( L"Friend enum failed to initialize. Error %x", hr );
        //Reset( TRUE );
    }

	mp_buddy_state_task = new Tsk::Task< BuddyMan > ( s_buddy_state_code, *this,
										Tsk::BaseTask::Node::vNORMAL_PRIORITY );
	mlp_man->AddLogicTask( *mp_buddy_state_task );	
	m_state = vSTATE_UPDATE;	
#	endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

BuddyMan::BuddyMan( void )
: m_potential_friends_list( 1 ), m_friends_list( MAX_FRIENDS ), m_num_friends( 1 )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet






================================================
FILE: Code/Sk/GameNet/XBox/p_buddy.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_buddy.h												**
**																			**
**	Created by:		02/28/02	-	SPG										**
**																			**
**	Description:	XBox Buddy List Code 									**
**																			**
*****************************************************************************/

#ifndef __GAMENET_XBOX_P_BUDDY_H
#define __GAMENET_XBOX_P_BUDDY_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace GameNet
{

class BuddyMan
{
public:
	typedef enum
	{
		vSTATE_FILL_BUDDY_LIST,
		vSTATE_UPDATE,
		vSTATE_SUCCESS,
		vSTATE_CANCEL,
		vSTATE_ERROR,
		vSTATE_WAIT,
	} BuddyState;

	BuddyMan( void );

	void	SpawnBuddyList( int user_index );

private:

	void	update_buddy_list( void );
	void	fill_buddy_list( void );

	int		m_current_user;
	int		m_num_friends;
	int		m_state;
	int		m_next_state;
	XONLINETASK_HANDLE	mh_friends_task;
	XONLINETASK_HANDLE	mh_friends_enum_task;
	Lst::DynamicTable< XONLINE_USER > m_potential_friends_list;
	Lst::DynamicTable< XONLINE_FRIEND > m_friends_list;

	static	Tsk::Task< BuddyMan >::Code  s_buddy_state_code; 
	Tsk::Task< BuddyMan >*		mp_buddy_state_task;
};


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet

#endif	// __GAMENET_XBOX_P_BUDDY_H




================================================
FILE: Code/Sk/GameNet/XBox/p_match.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_match.cpp												**
**																			**
**	Created by:		02/28/02	-	SPG										**
**																			**
**	Description:	XBox Match-Making Code									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace GameNet
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet






================================================
FILE: Code/Sk/GameNet/XBox/p_match.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_match.h												**
**																			**
**	Created by:		02/28/02	-	SPG										**
**																			**
**	Description:	XBox Matchmaking Code 									**
**																			**
*****************************************************************************/

#ifndef __GAMENET_XBOX_P_MATCH_H
#define __GAMENET_XBOX_P_MATCH_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace GameNet
{

						


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet

#endif	// __GAMENET_XBOX_P_MATCH_H




================================================
FILE: Code/Sk/GameNet/XBox/p_voice.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_voice.cpp												**
**																			**
**	Created by:		03/12/02	-	SPG										**
**																			**
**	Description:	XBox Online Voice Chat Code		 						**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace GameNet
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

VoiceMan::VoiceMan( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	VoiceMan::Startup( void )
{	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	VoiceMan::Shutdown( void )
{	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet


	



================================================
FILE: Code/Sk/GameNet/XBox/p_voice.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			GameNet			 										**
**																			**
**	File name:		p_voice.h												**
**																			**
**	Created by:		03/12/02	-	SPG										**
**																			**
**	Description:	XBox Online Voice Chat Code								**
**																			**
*****************************************************************************/

#ifndef __GAMENET_XBOX_P_VOICE_H
#define __GAMENET_XBOX_P_VOICE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace GameNet
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class VoiceMan
{
public:
	
	VoiceMan( void );

	void	Startup( void );
	void	Shutdown( void );
	
private:	
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace GameNet

#endif	// __GAMENET_XBOX_P_VOICE_H




================================================
FILE: Code/Sk/GameNet/scriptdebugger.h
================================================
#ifndef __GAMENET_SCRIPTDEBUGGER_H__
#define	__GAMENET_SCRIPTDEBUGGER_H__

#include 

namespace GameNet
{

#define	vSERVER_IP_VARIABLE	"VIEWER_IP"

enum
{
	vSCRIPT_DEBUG_PORT = 10050, // Needs to be distinct from vEXPORT_COMM_PORT or any others
};	

enum
{
	vSCRIPT_DEBUG_GROUP = 10
};

enum
{	
	//	32 is the first available user-defined message id
	vMSG_ID_DBG_PACKET							= 32, // PC -> Game
	
	vMSG_ID_DBG_CHECKSUM_NAMES_BUFFER_SIZE		= 33, // Game -> PC
	vMSG_ID_DBG_CHECKSUM_NAMES					= 34, // Game -> PC
	vMSG_ID_DBG_CSCRIPT_LIST					= 35, // Game -> PC
	vMSG_ID_DBG_WATCH_THIS						= 36, // PC -> Game
	vMSG_ID_DBG_WATCH_SCRIPT					= 37, // PC -> Game
	vMSG_ID_DBG_SCRIPT_CREATED					= 38, // Game -> PC
	vMSG_ID_DBG_SCRIPT_DIED						= 39, // Game -> PC
	vMSG_ID_DBG_CSCRIPT_INFO					= 40, // Game -> PC
	
	vMSG_ID_DBG_COMPRESSION_LOOKUP_TABLE		= 41, // Game -> PC
	vMSG_ID_DBG_BASIC_CSCRIPT_INFO				= 42, // Game -> PC
	vMSG_ID_DBG_STOP							= 43, // PC -> Game
	vMSG_ID_DBG_STEP_INTO						= 44, // PC -> Game
	vMSG_ID_DBG_STEP_OVER						= 45, // PC -> Game
	vMSG_ID_DBG_GO								= 46, // PC -> Game
	vMSG_ID_DBG_SCRIPT_UPDATE_DELAY				= 47, // PC -> Game
	vMSG_ID_DBG_CLEAR_SCRIPT_WATCHES			= 48, // PC -> Game
	vMSG_ID_DBG_REFRESH							= 49, // PC -> Game
	vMSG_ID_DBG_SEND_CSCRIPT_LIST				= 50, // PC -> Game
	
	vMSG_ID_DBG_SEND_WATCH_LIST					= 51, // PC -> Game
	vMSG_ID_DBG_WATCH_LIST						= 52, // Game -> PC
	vMSG_ID_DBG_STOP_WATCHING_THIS_INDEX		= 53, // PC -> Game
	vMSG_ID_DBG_STOP_WATCHING_THIS_CSCRIPT		= 54, // PC -> Game
	vMSG_ID_DBG_SEND_COMPOSITE_OBJECT_INFO		= 55, // PC -> Game
	vMSG_ID_DBG_COMPOSITE_OBJECT_INFO			= 56, // Game -> PC
	
	vMSG_ID_DBG_MOUSE_POSITION					= 57, // PC -> Game
	vMSG_ID_DBG_MOUSE_ON_SCREEN					= 58, // PC -> Game
	vMSG_ID_DBG_MOUSE_OFF_SCREEN				= 59, // PC -> Game
	vMSG_ID_DBG_MOUSE_LEFT_BUTTON_DOWN			= 60, // PC -> Game
	vMSG_ID_DBG_MOUSE_LEFT_BUTTON_UP			= 61, // PC -> Game
	vMSG_ID_DBG_MOUSE_RIGHT_BUTTON_DOWN			= 62, // PC -> Game
	vMSG_ID_DBG_MOUSE_RIGHT_BUTTON_UP			= 63, // PC -> Game
	
	vMSG_ID_DBG_COMPOSITE_OBJECT_LIST			= 64, // Game -> PC
	vMSG_ID_DBG_SEND_COMPOSITE_OBJECT_LIST		= 65, // PC -> Game
	vMSG_ID_DBG_SET_OBJECT_UPDATE_MODE			= 66, // PC -> Game
	vMSG_ID_DBG_VIEW_OBJECT						= 67, // PC -> Game
	
	vMSG_ID_DBG_SEND_SCRIPT_GLOBAL_INFO			= 68, // PC -> Game
	vMSG_ID_DBG_SCRIPT_GLOBAL_INFO				= 69, // Game -> PC
	
	vMSG_ID_DBG_EXCEPTION_INFO					= 70, // Game -> PC
	vMSG_ID_DBG_RUNNING_SCRIPT					= 71, // Game -> PC
};

}
#endif	// __GAMENET_SCRIPTDEBUGGER_H__

================================================
FILE: Code/Sk/Main.cpp
================================================
/*****************************************************************************
**				   															**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Main		 											**
**																			**
**	File name:		main.cpp												**
**																			**
**	Created:		a long long time ago									**
**																			**
**	Description:	Main entry code											**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 


extern int maintest(int argc, char *argv[]);
extern void test_init();
extern void test_render();

#ifdef __PLAT_NGC__
#ifdef DVDETH
#include 
#endif
#include 
#endif

#include 	
#include 
#include 
		 
#include 
#include 
#include 
#include 
#include 										   
#include 
#include 
#include 
#include 
#include 


#include 
#include 
#include 
#include 


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include  
#include  
#include  
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 

#ifdef __PLAT_NGPS__
#include 
#endif

#ifdef __PLAT_NGC__
#include 
#endif		// __PLAT_NGC__

#define MAINLOOP_MEMMARKER 16

#ifdef	__PLAT_NGPS__
#include 
#include 
extern char _code_start[];
extern char _code_end[];

extern char __floatdisf[];
extern char dptofp[];
extern char __fixunssfdi[];
extern char __udivdi3[];
#endif



#ifdef	__PLAT_NGPS__
sceDemoEndReason		demo_exit_reason = SCE_DEMO_ENDREASON_PLAYABLE_QUIT;	// default reason is that we quit (timeouts overrride this, and we can't "complete")
#define	DEBUG_FLASH(x)  //*GS_BGCOLOR = x  // 0xBBGGRR
#else
#define	DEBUG_FLASH(x)
#endif

int		inactive_time = 0;
int		inactive_countdown = 0;
int		gameplay_time = 0;



bool	skip_startup = false;

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

Dbg_DefineProject( PS2, "Test Project" )

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

extern "C"
{
#ifdef __PLAT_XBOX__
int pre_main( void );
#endif
#ifdef __PLAT_NGC__
void __init_vm(void);
#endif
#ifdef __PLAT_NGPS__
void pre_main( void );
#endif
void post_main( void );
}

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

#ifdef __PLAT_XBOX__
int pre_main( void )
#endif
#ifdef __PLAT_NGC__
void __init_vm( void )
#endif
#ifdef __PLAT_NGPS__
void pre_main( void )
#endif
{
	DEBUG_FLASH(0x07f7f7f);		// initial white


#ifdef __PLAT_NGPS__
	printf ("calling Mike test\n");
//	maintest(0, NULL); // the Mike code.....
	printf ("DONE calling Mike test\n");
#endif		// __PLAT_NGPS__

#ifdef __PLAT_NGC__
	OSInitFastCast();
	NsDisplay::init();
#endif		// __PLAT_NGC__

	Dbg::SetUp();
	Mem::Manager::sSetUp();
	
	DEBUG_FLASH(0x02050);		// brown

#ifdef __PLAT_XBOX__
	// Must return 0 here, as this is called from CRT initialization code.
	return 0;
}

#pragma data_seg( ".CRT$RIX" )
static int (*_mypreinit)(void) = pre_main;
#pragma data_seg()

#else
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void post_main (void)
{
	Mem::Manager::sCloseDown();
	Dbg::CloseDown();
}

void mat_test(Mth::Matrix A,Mth::Matrix B,Mth::Matrix C);

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


int main ( sint argc, char** argv )
{ 	
#ifdef __PLAT_NGC__
#ifdef DVDETH
	// Defaults
    u8   DvdServerAddr[4]    = { 192, 168,   0,   153 };// PC IP Address
    u16  DvdServerPort       = 9001;                  // PC TCP PORT

//    u8   ReportServerAddr[4] = { 192, 168,   0,   153 };// PC IP Address
//    u16  ReportServerPort    = 9002;                  // PC TCP PORT

    u8   GCClientAddr[4]     = { 192, 168,   5,   153 };// GC IP Adderss
    u8   Netmask[4]          = { 255, 255,   0,   0 };// Netmask
    u8   Gateway[4]          = { 192, 168,   0, 254 };// Gateway
#endif		// DVDETH


	// Add -a -pf to odemrun to enable printfs.
	bool kill = true;
	for ( int arg = 1; arg < argc; arg++ )
	{
		if ( argv[arg][0] != '-' ) continue;

		switch ( argv[arg][1] )
		{
			case 'p':
				// Kill printfs.
				if ( argv[arg][2] == 'f' )
				{
					kill = false;
				}
				break;
#ifdef DVDETH
			case 'i':
				// specify IP address.
				switch ( argv[arg][2] )
				{
					case 'p':
						{
							// PC IP address
							char num0[8];
							char num1[8];
							char num2[8];
							char num3[8];
							int c = 0;
							int d;

							// Get the numbers individually.
							d = 0;      while ( argv[arg][3+c] != '.' ) { num0[d++] = argv[arg][3+c++]; } num0[d] = '\0';
							d = 0; c++; while ( argv[arg][3+c] != '.' ) { num1[d++] = argv[arg][3+c++]; } num1[d] = '\0';
							d = 0; c++; while ( argv[arg][3+c] != '.' ) { num2[d++] = argv[arg][3+c++]; } num2[d] = '\0';
							d = 0; c++; while ( argv[arg][3+c] != '\0' ) { num3[d++] = argv[arg][3+c++]; } num3[d] = '\0';

							DvdServerAddr[0] = atoi( num0 );
							DvdServerAddr[1] = atoi( num1 );
							DvdServerAddr[2] = atoi( num2 );
							DvdServerAddr[3] = atoi( num3 );

//							ReportServerAddr[0] = atoi( num0 );
//							ReportServerAddr[1] = atoi( num1 );
//							ReportServerAddr[2] = atoi( num2 );
//							ReportServerAddr[3] = atoi( num3 );
						}
						break;
					case 'g':
						{
							// GameCube IP address
							char num0[8];
							char num1[8];
							char num2[8];
							char num3[8];
							int c = 0;
							int d;

							// Get the numbers individually.
							d = 0;      while ( argv[arg][3+c] != '.' ) { num0[d++] = argv[arg][3+c++]; } num0[d] = '\0';
							d = 0; c++; while ( argv[arg][3+c] != '.' ) { num1[d++] = argv[arg][3+c++]; } num1[d] = '\0';
							d = 0; c++; while ( argv[arg][3+c] != '.' ) { num2[d++] = argv[arg][3+c++]; } num2[d] = '\0';
							d = 0; c++; while ( argv[arg][3+c] != '\0' ) { num3[d++] = argv[arg][3+c++]; } num3[d] = '\0';

							GCClientAddr[0] = atoi( num0 );
							GCClientAddr[1] = atoi( num1 );
							GCClientAddr[2] = atoi( num2 );
							GCClientAddr[3] = atoi( num3 );
						}
						break;
					default:
						break;
				}
				break;
#endif		// DVDETH
			default:
				break;
		}
	}

#	if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
	if ( kill )
	{
		// Kill printfs
		uint32 * p;
		p = (uint32*)&OurPrintf;
		*p = 0x4e800020;		// blr
		p = (uint32*)&OSReport;
		*p = 0x4e800020;        // blr
	}
#endif	// __NOPT_FINAL__

#ifdef DVDETH

    // Initialize Network
    DVDEthInit(GCClientAddr, Netmask, Gateway);

    // Initialize DVD
    DVDLowInit(DvdServerAddr, DvdServerPort);

//    // Initialize OSReport();
//    OSReportInit(ReportServerAddr, ReportServerPort);
#endif

#endif		// __PLAT_NGC__

	// Note: printf's will not work until after Config::Init() is called.

	DEBUG_FLASH(0x007F0000);
	
	Mem::SetThreadSafe(false);	
	Config::Init(argc,argv);

#ifdef __PLAT_NGPS__
	// a hack to disable the PS2 video output as early as poss
	NxPs2::SetupPCRTC(0, SCE_GS_FIELD);
#endif		// __PLAT_NGPS__

	DEBUG_FLASH(0x00007f00);
					  
	Mem::Manager::sHandle().InitOtherHeaps();							

	DEBUG_FLASH(0x0000007f);

	#ifdef	__PLAT_NGPS__
// Need to reconcile the Config::Init with the following code
// so demo version can exist with regular version
// just switched by examining command line

	if (Config::SonyBootstrap())
	{
								   
		unsigned short language;
		unsigned short aspect;
		unsigned short playmode;
		unsigned short to_inactive;
		unsigned short to_total;
		unsigned short mediaType;
		unsigned short masterVolumeScale = 10;
		unsigned short directorySectorNum;
			
		sceDemoStart(argc,argv,&language,&aspect,&playmode,&to_inactive,&to_total,&mediaType,&masterVolumeScale,&directorySectorNum);
		
		printf("Tony Hawk's Pro Skater 4 - Demo Disk Version\n");
		printf("l %d a %d pm %d toa %d tot %d\n",language,aspect,playmode,to_inactive,to_total);
		printf("MEDIA TYPE %d\n",mediaType);
		printf("MasterVol Scale %d\n",masterVolumeScale);
		printf("My directory Sector is %d\n",directorySectorNum);
		
		inactive_time = to_inactive * 60;
		inactive_countdown = to_inactive * 60;
		gameplay_time = to_total * 60;
		
		if (Config::Bootstrap() && Config::CD())
		{
			Config::SetMasterVolume(masterVolumeScale * 10.0f);
		}
	}
	#endif		   
	
	
//	Config::SetMasterVolume(0.0f);
	 		   
	
	
	printf ("\n Start of main()\n");
	printf ("argc = %d\n",argc); 
	for (int arg = 0; arg < argc; arg++)
	{
		printf ("%2d: %s\n",arg,argv[arg]);
	}
	printf ("\n");
	
	//printf("Hardware=%s\n",Config::GetHardwareName());
	//printf("GotExtraMemory=%d\n",Config::GotExtraMemory());
	//printf("Language=%s\n",Config::GetLanguageName());
	//printf("CD=%d\n",Config::CD());
	
	if (Config::GotExtraMemory())
	{
		Mem::Manager::sSetUpDebugHeap();
	}	

	Mem::PushMemProfile("Everything");
	Mem::PushMemProfile("Initialization");

	
//	static char	profdata[8192];
//	snDebugInit();
//	snProfInit(_4KHZ, profdata, sizeof(profdata));

	if (argc == 2 && strcmp(argv[1],"demo") == 0)
	{
		skip_startup = true;
	}
	
	
	DEBUG_FLASH(0x007f007f);		// magenta
	


#ifdef	__NOPT_ASSERT__
#ifdef	__PLAT_NGPS__	
	static u_long128 profdata[2048]; // quadword aligned, can be 2K
									 // to 64K bytes

	// we now always turn on profiling if running on a TOOL (unless we are on a CD)									 
	if (Config::GetHardware() == Config::HARDWARE_PS2_DEVSYSTEM && !Config::CD())
	{
		snDebugInit();
		sceSifInitRpc(0);
		// Load the SNProfil module
		if(sceSifLoadModule("host0:/usr/local/sce/iop/modules/SNProfil.irx", 0, NULL) < 0)
		{
			printf("Can't load SNProfil module, but don't worry, that's just for programmers...\n However, you could get the latest version of usr if you want it\n");
	//		exit(-1);
		}
		else
		{
			if(snProfInit(_4KHZ, profdata, sizeof(profdata)) != 0)
			{
				printf("Profiler init failed\n"); // see SN_PRF in LIBSN.H
			}
		}	
	}	
	// rest of user code follows on from here...
#endif
#endif

	Dbg_Message ( "Begin Application" );						 
	Mth::InitialRand(107482099);

#if 0
		Mth::InitSinLookupTable();
#ifdef  __PLAT_NGPS__
// (Mick)  THis might not be needed with the new libs
													   
		// Run through all the code and replace all calls to sinf, cosf
		// with faster versions that use a lookup table.
		// Note: This must be done after the call to Mth::InitSinLookupTable(); above,
		// because that uses the original sinf to generate the lookup table.
		int *pc = (int*)_code_start;
		while (pc < (int*)_code_end)
		{

			int instruction = *pc;
			if (instruction >> 24 == 0x0c)
			{
				int code = (instruction & 0xffffff)<<2;
				if (code == (int)sinf)
				{									
					*pc=(*pc&0xff000000)| ((((uint32)Mth::Kensinf)>>2)&0xffffff);
				}
				else if (code == (int)cosf)
				{
					*pc=(*pc&0xff000000)| ((((uint32)Mth::Kencosf)>>2)&0xffffff);
				}
				else if (code == (int)acosf)
				{
					*pc=(*pc&0xff000000)| ((((uint32)Mth::Kenacosf)>>2)&0xffffff);
				}
			}
			++pc;
	   }
	   
#endif				 
#endif				 


#ifdef __NOPT_ASSERT__
#ifdef  __PLAT_NGPS__
	Mem::PushMemProfile("(debug only) Log::Init()");
	Log::Init();
	Mem::PopMemProfile();
#endif
#endif

	
	while (  true )
	{	
		Mem::Manager::sHandle().BottomUpHeap()->PushContext();
		Mem::Manager::sHandle().PushMemoryMarker(MAINLOOP_MEMMARKER);
		{
			/*
				Important Notes:
				
				Please put all module creation/registering/starting code in its proper section.
				This helps prevents bugs caused by initialization code depending on objects
				that have not been initialized.
				
				There are two stages to initialization. First, object creation (create singleton
				section). Second, running the object's v_start function (start modules section).
				Note that the latter doesn't happen when StartModule is called, but after the
				main loop is entered.
			
				It would be a bad idea to make code in any module's constructor depend on initialization 
				done in another's v_start function.
			*/
			
			// We create the symbol hash table early, as it's kind of used my Music::Init, which I think
			// is called both at startup (when the scripts are invalid), and later (when they are valid)
			Script::CreateSymbolHashTable();
								
			
			/***********************************************
			General startup section
			************************************************/
			
			Mem::PushMemProfile("Hash Item Pool Manager");
#			if( defined( __PLAT_NGC__ ) || defined( __PLAT_XBOX__ ))
			Mem::PoolManager::SSetupPool(Mem::PoolManager::vHASH_ITEM_POOL, 12500);	// Mick: increased from 5600 to 10000
#			else
			Mem::PoolManager::SSetupPool(Mem::PoolManager::vHASH_ITEM_POOL, 10000);	// Mick: increased from 5600 to 10000
#			endif // __PLAT_NGC__ || __PLAT_XBOX__
			Mem::PopMemProfile(/*"Hash Item Pool Manager"*/);

				
			// I'd prefer for this stuff to happen after the singleton section,
			// but it won't work that way
			
			Spt::SingletonPtr< File::PreMgr> pre_mgr(true);		 // Note, moved here so SkateScript::Init can use it
			   	
			/***********************************************
			Create singleton section
			************************************************/
			
			/*
				RJM:
				
				-SkateMod must come after panelMgr, because it references the panelMgr singleton
			*/
			
			Tmr::Init();
			
			Mem::PushMemProfile("File System");
			File::InstallFileSystem();               
			Mem::PopMemProfile(/*"File System"*/);

								
			DEBUG_FLASH(0x007f7f00);		// cyan
								
			Mem::PushMemProfile("System Singletons");
			Spt::SingletonPtr< Obj::CTracker >	obj_tracker( true );
			Spt::SingletonPtr< Mlp::Manager >	mlp_manager( true );
			Spt::SingletonPtr< Mdl::Manager >	mdl_manager( true );
			Spt::SingletonPtr< SIO::Manager >	sio_manager( true );
			Spt::SingletonPtr< Mc::Manager > 	mc_manager( true );
			Spt::SingletonPtr< Inp::Manager >	inp_manager( true );
			Spt::SingletonPtr< Gfx::Manager >	gfx_manager( true );
			Spt::SingletonPtr< File::CAsyncFilePoll>	async_poll( true );
			Mem::PopMemProfile(/*"System Singletons"*/);

			DEBUG_FLASH(0x00007f7f);		// yellow


			#ifdef	__PLAT_NGPS__
			NxPs2::InitialiseEngine();
			Nx::CLoadScreen::sDisplay( "loadscrn", false, false );
			//Nx::CLoadScreen::sStartLoadingBar(duration);
			#endif

			
			// NOTE:  I moved SkateScript::Init() down here, as
			// we want to display the loading screen before
			// we load any other files (so it loads as fast as possible)																  
			// SO, NO SCRIPT FUNCTIONS MAY BE USED BEFORE THIS POINT

			Mem::PushMemProfile("SkateScript::Init()");
			SkateScript::Init();	 			

			Mem::PushMemProfile("Game Singletons");
			#ifdef __NOPT_ASSERT__
			Spt::SingletonPtr< Dbg::CScriptDebugger> script_debugger(true);
			#endif
			Mem::PopMemProfile();

#ifdef __PLAT_NGC__
extern uint8 * RES_gamecube;
			Spt::SingletonPtr< Script::CScriptCache>			script_cache( true );
			Script::ParseQB( "scripts\\engine\\gamecube.qb", RES_gamecube );

			if ( ( VIGetTvFormat() == VI_NTSC ) || ( VIGetTvFormat() == VI_MPAL ) )
			{
				NsDisplay::Check480P();
			}
			else
			{
				NsDisplay::Check60Hz();
			}
#endif		// __PLAT_NGC__
			SkateScript::Preload();
			Mem::PopMemProfile();
			
			Mem::PushMemProfile("Game Singletons");
			Obj::CPathMan::Instance(); // Force creation of CPathMan singleton

#ifndef __PLAT_NGC__
			Spt::SingletonPtr< Script::CScriptCache>			script_cache( true );
#endif		// __PLAT_NGC__
			Spt::SingletonPtr< Ass::CAssMan > 					ass_manager( true );
			Spt::SingletonPtr< Sfx::CSfxManager > 				sfx_manager( true );			
			Spt::SingletonPtr< Net::Manager >					net_manager( true );	
			Spt::SingletonPtr< Obj::CCompositeObjectManager >   composite_object_manager( true );
			Spt::SingletonPtr< Mdl::Skate >						skate_mod( true );
			Spt::SingletonPtr< Mdl::FrontEnd > 					front(true);
			Spt::SingletonPtr< GameNet::Manager > 				gamenet_man( true );
			Spt::SingletonPtr< Ed::CParkEditor>					grandpas_park_editor(true);

			Spt::SingletonPtr< Front::CScreenElementManager > 	screen_elem_manager(true);
#if __USE_REPLAYS__
			Spt::SingletonPtr< Replay::Manager > 				replay_manager( true );
#endif			
			Mem::PopMemProfile(/*"Game Singletons"*/);

			DEBUG_FLASH(0x007f7f7f);		// white

			Mem::PushMemProfile("Resgistering and starting modules");

			/***********************************************
			Setup main loop tasks section
			************************************************/

			// Note, some managers/modules add their tasks in their constructors
			// however, these three are hooked up in the main loop, for some reason
			// and have to provide a global function to get a pointer to
			// their appropiate tasks
		
//			mlp_manager->AddSystemTask ( tgt_manager->GetProcessBackGroundTasks() );
			mlp_manager->AddSystemTask ( mdl_manager->GetProcessModulesTask() );   // Note: this hooks up the code that starts the modules
			mlp_manager->AddSystemTask ( sio_manager->GetProcessDevicesTask() );
			mlp_manager->AddLogicTask  ( inp_manager->GetProcessHandlersTask() );
		
			/***********************************************
			Register modules section
			************************************************/
			
			/* 
				RJM: 
				
				The FrontEnd and Panel Managers must be initialized before the Skate Mod,
				so that the game can be launched from a startup script without problems
			
			*/
			
			mdl_manager->RegisterModule ( *script_cache );
			mdl_manager->RegisterModule ( *grandpas_park_editor );
			mdl_manager->RegisterModule ( *front );
			mdl_manager->RegisterModule ( *skate_mod );
			mdl_manager->RegisterModule ( *async_poll );
			#ifdef __NOPT_ASSERT__
			mdl_manager->RegisterModule ( *script_debugger );
			#endif
			
			/***********************************************
			Start modules section
			************************************************/
			
			mdl_manager->StartModule( *script_cache );
			mdl_manager->StartModule( *front );
			mdl_manager->StartModule( *skate_mod );      
			mdl_manager->StartModule ( *grandpas_park_editor );
			mdl_manager->StartModule ( *async_poll );
			
			Mem::PopMemProfile(/*"Resgistering and starting modules"*/);
		
						
			/***********************************************
			Main loop section
			************************************************/
			
#ifdef		__USE_PROFILER__
			Sys::Profiler::sSetUp(2);
#endif			
			
			Mem::PushMemProfile("sStartEngine");
			Nx::CEngine::sStartEngine();
			Mem::PopMemProfile(/*"sStartEngine"*/);

			Dbg_Message ( "Starting main loop" );
// Note to the adventurer:
// Where does control go now? .... you may well ask
// The above "modules" have simply been flagged as wanting to start, they have not
// actually started yet.
// The code that actually runs them is called ultimately from "service_system" in mainloop.cpp
// "service_system" iterates over the "system_task_list", and calls all the tasks
// in that list.  One of those tasks is the task returned by GetProcessModulesTask()
// which is added to the task list by the line:
// 			mlp_manager->AddSystemTask ( mdl_manager->GetProcessModulesTask() );
// above.
// The GetProcessModulesTask() returns (via some indirection), Mdl::Manager::process_modules
// and this function iterates over the modules registered with the module manager
// in part it checks if (current->command == Module::vSTART), and if so, then it calls
// 					current->v_start_cb();
// which in the case of the skate module is Mdl::Skate::v_start_cb
// which does some initialization of the skate module
// and in the course of that, calls 
//	Script::RunScript("default_system_startup");
//	Script::RunScript("StartUp");	<<< but not on the CD version
//
// The "Startup" script differs per person during development, but will typically look like:
//
//	script startup
//	    autolaunch level=load_sch game=career
//	endscript
//
// which calls a bunch of commands to ultimately load the level geometry,
// parse the triggers in the "node array" 
// and drop in the skater
// and start skating.
// phew

			Mem::PopMemProfile(/*Initilization*/);
			
			
			Mem::PushMemProfile("MainLoop");
			mlp_manager->MainLoop();
			Mem::PopMemProfile(/*"MainLoop"*/);
			
// The following code is theoretical, as we actually never shut down the whole game			
// On the PS2, we use the script command "LoadExecPS2", which calls "ScriptLoadExecPS2" in cfuncs.cpp
// which allows us to load another executable.  This would either be a demo or another game
// or it would be returning to the bootstrap loader.
			
			Dbg_Message ( "Ending main loop" );
			
#ifdef		__USE_PROFILER__
			Sys::Profiler::sCloseDown();
#endif			
			mdl_manager->StopAllModules();
			
			/***********************************************
			Unregister section
			************************************************/
			
			mdl_manager->UnregisterModule ( *skate_mod );
			mdl_manager->UnregisterModule ( *front );
			mdl_manager->UnregisterModule ( *grandpas_park_editor );
			mdl_manager->UnregisterModule ( *async_poll );
			#ifdef __NOPT_ASSERT__
			mdl_manager->UnregisterModule ( *script_debugger );
			#endif
			
			/***********************************************
			General closedown
			************************************************/
			
			mlp_manager->RemoveAllDisplayTasks();			
			mlp_manager->RemoveAllLogicTasks();
			mlp_manager->RemoveAllSystemTasks();

			Dbg_Message ( "End Application" );
		}
		Tmr::DeInit();
		Mem::Manager::sHandle().PopMemoryMarker(MAINLOOP_MEMMARKER);
		Mem::Manager::sHandle().BottomUpHeap()->PopContext();
	Mth::Matrix A,B,C;
	mat_test(A,B,C);				 
	}

				 
	return 0;
}

void nob(Mth::Matrix A);

void mat_test(const Mth::Matrix A,const Mth::Matrix B,Mth::Matrix C)
{

	Mth::Matrix D;
	D = A * B;
}

void nob(Mth::Matrix A)
{
	printf ("%f",A[X][X]);
}



/*****************************************************************************
**							                    							**
*****************************************************************************/








================================================
FILE: Code/Sk/Modules/FrontEnd/FrontEnd.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			 			                              				**
**																			**
**	File name:											                    **
**																			**
**	Created by:	    rjm, 9/12/2000											**
**																			**
**	Description:									                        **
**																			**
*****************************************************************************/

// start autoduck documentation
// @DOC frontend
// @module frontend | None
// @subindex Scripting Database
// @index script | frontend
                                    

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 


#include 

#include 		   // just for pausing the skater, and for running the "run me now" script

										   
extern bool run_runmenow;

namespace Mdl
{
DefineSingletonClass( FrontEnd, "Frontend module" );


FrontEnd::FrontEnd()
{

	m_logic_task = new Tsk::Task< FrontEnd > ( FrontEnd::s_logic_code, *this, Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_FRONTEND);
	m_handle_keyboard_task = new Tsk::Task< FrontEnd > ( FrontEnd::s_handle_keyboard_code, *this, Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_HANDLE_KEYBOARD);
	
	for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		mp_input_handlers[i] = new Inp::Handler< FrontEnd > ( i, FrontEnd::s_input_logic_code, *this, Tsk::BaseTask::Node::vHANDLER_PRIORITY_FRONTEND_INPUT_LOGIC0);		
		m_pad_info[i].mState =  NOT_DOWN;
		m_pad_info[i].mMode = ACTIVE;
	}
	m_paused = false;

	m_auto_repeat_time[0] = Tmr::vRESOLUTION * 3 / 10;
	m_auto_repeat_time[1] = Tmr::vRESOLUTION * 5 / 100;
	
	
	// m_pad_info[1].mState =  NOT_DOWN;
	
	// m_pad_info[1].mMode = INACTIVE;

	m_temp_block_pad_input = false;

	m_using_auto_input = false;
	m_check_for_auto_input = true;
	m_auto_input_script_id = 0;

	for (int i = 0; i < MAX_BUTTON_EVENT_MAP_ENTRIES; i++)
	{
		m_digital_button_event_map[i].mEventType = DEAD_ENTRY;
	}

	for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
		m_device_server_map[i] = i;
}




FrontEnd::~FrontEnd()
{
	delete m_logic_task;
	
	for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		delete mp_input_handlers[i];
		mp_input_handlers[i] = NULL;
	}
}




void FrontEnd::v_start_cb ( void )
{

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
		Mlp::Manager * mlp_manager = Mlp::Manager::Instance(); 
		mlp_manager->AddLogicTask ( *m_logic_task );
		Inp::Manager * inp_manager = Inp::Manager::Instance(); 
		
		for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
		{
			Inp::Handler< FrontEnd >* p_handler = GetInputHandler( i );
			inp_manager->AddHandler( *p_handler );
		}
	Mem::Manager::sHandle().PopContext();	
}




void FrontEnd::v_stop_cb ( void )
{
	m_logic_task->Remove();
	
	for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		Inp::Handler< FrontEnd >* p_handler = GetInputHandler( i );
		p_handler->Remove();
	}
}


bool FrontEnd::PadsPluggedIn()
{
	for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		Dbg_MsgAssert(mp_input_handlers[i],("NULL mp_input_handlers[%d]",i));
		if (mp_input_handlers[i]->m_Device->IsPluggedIn())
		{
			return true;
		}
	}
	return false;		
}



void FrontEnd::AddKeyboardHandler( int max_string_length )
{
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance(); 

	m_max_kb_string_length = max_string_length;
	mlp_manager->AddLogicTask( *m_handle_keyboard_task );
}




void FrontEnd::RemoveKeyboardHandler( void )
{
	m_handle_keyboard_task->Remove();
}




void FrontEnd::SetAnalogStickActiveForMenus( bool isActive )
{
	Spt::SingletonPtr p_manager;
	p_manager->SetAnalogStickOverride(isActive);
}




///////////////////////////////////////////
// (Mick takes responsibility for this)
// This function JUST pauses the game
// this mainly consists of pausing all the tasks (for objects, cars, peds), but
// first it pauses the skate module (for music), and the sio_manager (pad vibration)
//
// The pausing of stuff is fairly comvoluted, here we:
//
// Pause the PCM music
// pause the pad vibration
// pause all messages, but allow future messages to be launched
// pause all spawned scripts, apart from the current one, and future spawned scritps 
//
// pause the skater, but still allow the reading of the pad from script 
//
// Overall, a rather mixed bag of things, which might be best refactored
// but should do for now.....
//
// Note:  is_active == true ... implies the "Front End" is active, and the "game" paused

void FrontEnd::PauseGame(bool paused)
{
	
	// if already in the correct "paused" state, then just return
	if (paused == m_paused)	
	{
		return;
	}
				
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if (paused)
	{
		// make game be paused
		skate_mod->Pause( );
		
		// Ken: Pause the controller vibrations.
		SIO::Manager * sio_man = SIO::Manager::Instance();
		sio_man->Pause();
	}
	else
	{
		// make game be unpaused
		// Ken: Un-pause the controller vibrations.
		SIO::Manager * sio_man = SIO::Manager::Instance();
		sio_man->UnPause();
	}
	
	// Pause currently spawned scripts (any script with PauseGame in it will unpause itself)		
	Script::PauseSpawnedScripts(paused);		
	
	// Pause or unpause all the skaters
	uint32 NumSkaters = skate_mod->GetNumSkaters();
	
	for (uint32 i = 0; i < NumSkaters; ++i)
	{
		Obj::CSkater *pSkater = skate_mod->GetSkater(i);
		if (pSkater) // Hmm, assert instead?
		{
			if (paused)
			{
				pSkater->Pause();
			}
			else
			{
				pSkater->UnPause();
			}
		}	
	}	
	
	// Pause all composite objects
	Obj::CCompositeObjectManager::Instance()->Pause(paused);
	

	// pause (or unpause) any screen elements in process of morphing
	Front::CScreenElementManager* p_screen_element_manager = Front::CScreenElementManager::Instance();
	p_screen_element_manager->SetPausedState(paused);
	
	m_paused = paused;

	
	if (m_paused)
	{
		// make game be paused
		Mlp::Manager * mlp_manager = Mlp::Manager::Instance(); 
		mlp_manager->SetLogicMask(Mlp::Manager::mGAME_OBJECTS | Mlp::Manager::mGFX_MANAGER);
		Tmr::StoreTimerInfo( );
	}
	else
	{
		// make game be unpaused
		Mlp::Manager * mlp_manager = Mlp::Manager::Instance(); 
		mlp_manager->SetLogicMask(Mlp::Manager::mNONE);		
//		rw_viewer->DisableMainLogic(false);
		// unpause music and soundfx:
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		skate_mod->Pause( false );
		Tmr::RecallTimerInfo( );
	}
}




void FrontEnd::AddEntriesToEventButtonMap(Script::CStruct *pParams)
{
	#ifdef __PLAT_XBOX__
	const char *p_array_name = "xbox";
	#else
	#ifdef __PLAT_NGC__
	const char *p_array_name = "gamecube";
	#else
	const char *p_array_name = "ps2";
	#endif
	#endif
	
	// This seems a very hacky way of doing things.
	// Black and white need to be in position 16 and 17, since they are button indices 16 and 17.
	#ifdef __PLAT_XBOX__
	const char *p_button_names[] =
	{
		"left_trigger_full",
		"right_trigger_full",
		"left_trigger_half",
		"right_trigger_half",
		"y",
		"b",
		"a",
		"x",
		"back",
		"left_stick_button",
		"right_stick_button",
		"null",
		"null",
		"null",
		"null",
		"null",
		"black",
		"white"
	};
	#else
	#ifdef __PLAT_NGC__
	const char *p_button_names[] =
	{
		"left_trigger_full",
		"right_trigger_full",
		"left_trigger_half",
		"right_trigger_half",
		"y",
		"x",
		"a",
		"b",
		"null",
		"z_plus_left_trigger",
		"z_plus_right_trigger",
		"null",
		"null",
		"null",
		"null",
		"null",
		"null",
		"null",
		"z"
	};
	#else
	const char *p_button_names[] =
	{
		"left_trigger2",
		"right_trigger2",
		"left_trigger1",
		"right_trigger1",
		"triangle",
		"circle",
		"x",
		"square",
		"select",
		"left_stick_button",
		"right_stick_button"
	};
	#endif
	#endif
	
	// Cleaner way of allowing varying button names array per platform.
	int num_mapped_buttons = sizeof( p_button_names ) / sizeof( char* );

	uint32 null_crc = Script::GenerateCRC("null");
	
	/*
		Indices:
		
		i: into script array
		j: into button name array
		x: into map array
	*/
	
	Script::CArray *p_map;
	if (!pParams->GetArray(p_array_name, &p_map))
		return;

	for (uint i = 0; i < p_map->GetSize(); i++)
	{
		Script::CArray *p_entry = p_map->GetArray(i);

		uint32 button_crc = p_entry->GetChecksum(0);
		uint32 event_crc = p_entry->GetChecksum(1);

		for (uint j = 0; j < (uint) num_mapped_buttons; j++)
		{
			if (button_crc == Script::GenerateCRC(p_button_names[j]) || button_crc == null_crc)
			{
				#ifdef	__NOPT_ASSERT__
				bool removing_entries = (button_crc == null_crc || event_crc == null_crc);
				#endif
				int x = 0;
				for (; x < MAX_BUTTON_EVENT_MAP_ENTRIES; x++)
				{
					if (button_crc == null_crc && m_digital_button_event_map[x].mEventType == event_crc)
					{
						// in this case, I am removing existing entry (or entries)
						m_digital_button_event_map[x].mEventType = DEAD_ENTRY;
					}
					else if (event_crc == null_crc && m_digital_button_event_map[x].mDigitalButtonIndex == j)
					{
						// in this case, I am removing existing entry (or entries)
						m_digital_button_event_map[x].mEventType = DEAD_ENTRY;
					}
					else if (m_digital_button_event_map[x].mEventType == DEAD_ENTRY)
					{
						// in this case, I am adding a new entry, so need empty slot
						m_digital_button_event_map[x].mDigitalButtonIndex = j;
						m_digital_button_event_map[x].mEventType = event_crc;
						break;
					}
				}
				Dbg_MsgAssert(x < MAX_BUTTON_EVENT_MAP_ENTRIES || removing_entries, ("out of entries, increase size of MAX_BUTTON_EVENT_MAP_ENTRIES"));
				
				break;
			}
		}
	}


	/*
		Paul:
	
		'Y' = PS2 'Triangle'. 
		'X' = PS2 'Circle'.   
		'A' = PS2 'X'.        
		'B' = PS2 'Square'.   
		'Left Trigger pressed down > halfway' = PS2 'L1'. 
		'Right Trigger pressed down > halfway' = PS2 'R1'.
		'Left Trigger' = PS2 'L2'.                        
		'Right Trigger' = PS2 'R2'.                       
		'Z' plus 'L' = PS2 'Left Stick Button'.  
		'Z' plus 'R' = PS2 'Right Stick Button'
		Z button without L & R is PS2 'Select'
	*/

	/*
		Dave:
	
		Xbox start = start
		Xbox back = select
		The 4 xbox color buttons map to the Ps2 buttons in the same location.
		Black and white buttons aren't mapped at all.
		Triggers as per Ngc.
		Left and right stick buttons map directly to Ps2 equivalents
	*/
}




void FrontEnd::s_input_logic_code ( const Inp::Handler < FrontEnd >& handler )
{	   
	Dbg_AssertType ( &handler, Inp::Handler< FrontEnd > );
    
	FrontEnd& mdl = handler.GetData();

	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
	p_tracker->LogTick();
	
	for (int count = 0;; count++)
	{
		uint32 event_type = 0; 
		
		if (count == 0)
		{
			if (mdl.m_check_for_auto_input)
			{
				mdl.m_using_auto_input = (bool) Script::GetInteger("use_auto_menu_input");
				if (mdl.m_using_auto_input)
				{
					Script::CScript *p_script = Script::SpawnScript("auto_menu_input");
					#ifdef __NOPT_ASSERT__
					p_script->SetCommentString("Spawned from FrontEnd::s_input_logic_code");
					#endif
					mdl.m_auto_input_script_id = p_script->GetUniqueId();
				}
				mdl.m_check_for_auto_input = false;
			}
			
			uint32 timed_event_type = mdl.grab_timed_event_type(handler.m_Input->m_Buttons, handler.m_Index);
			if (timed_event_type)
				event_type = timed_event_type;
		}
		
		uint32 mapped_event_type = mdl.turn_mask_into_event_type(handler.m_Input->m_Makes, count, handler.m_Index,handler.m_Input->m_Buttons);
		if (mapped_event_type)
			event_type = mapped_event_type;
		
		if (event_type && mdl.m_pad_info[handler.m_Index].mMode != INACTIVE && !mdl.m_temp_block_pad_input) 
		{
			Script::CStruct event_data;
			int device_num = handler.m_Device->GetPort();
			event_data.AddInteger("device_num", device_num );
			// printf("device_num = %i\n", device_num);

			if (mdl.m_pad_info[handler.m_Index].mMode == ACTIVE)
			{
				// get the controller this came from				
				// event_data.AddInteger("controller", handler.m_Index);
				event_data.AddInteger("controller", 0);
				// event_data.AddInteger( "controller", Mdl::FrontEnd::Instance()->GetInputHandlerMapping( device_num ) );
				// printf( "using controller %i\n", Mdl::FrontEnd::Instance()->GetInputHandlerMapping( device_num ) );
			}
			else
			{
				// printf("using 0\n");
				event_data.AddInteger("controller", 0);
			}
			
			//if (handler.m_Index)
			// printf("firing pad event %s\n", Script::FindChecksumName(event_type));
	
			if (p_tracker->LaunchEvent(event_type, Obj::CEvent::vSYSTEM_EVENT, Obj::CEvent::vSYSTEM_EVENT, &event_data))
			{
				// will get here whenever menu systems responds to one of above occurences
	
				//handler.m_Input->MaskDigitalInput(Inp::Data::mD_ALL);
				//handler.m_Input->MaskAnalogInput(Inp::Data::mA_ALL);
			}
		}

		if (event_type == 0)
			break;
	}
}




void FrontEnd::s_logic_code ( const Tsk::Task< FrontEnd >& task )
{
	Dbg_AssertType ( &task, Tsk::Task< FrontEnd > );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());

	FrontEnd&	mdl = task.GetData();

	mdl.update(mdl.m_paused);
	
	
#		ifdef __USE_PROFILER__
		Sys::CPUProfiler->PushContext( 0, 0, 128 ); 	// blue under green = screen element update
#		endif // __USE_PROFILER__
	
	
	Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
	p_screen_elem_man->Update();



#		ifdef __USE_PROFILER__
		Sys::CPUProfiler->PopContext(  ); 	
#		endif // __USE_PROFILER__
	
	Mem::Manager::sHandle().PopContext();		
}


void FrontEnd::s_handle_keyboard_code( const Tsk::Task< FrontEnd >& task )
{
	int i, num_chars;
	char makes[256];
	FrontEnd& mdl = task.GetData();
		
	num_chars = SIO::KeyboardRead( makes );

	for( i = 0; i < num_chars; i++ )
	{
		if((( makes[i] >= 32 ) && ( makes[i] <= 126 ) ) || ( makes[i] == SIO::vKB_ENTER ) || ( makes[i] == SIO::vKB_BACKSPACE ))
		{
			Script::CStruct* pParams;
			
			pParams = new Script::CStruct;
			if( makes[i] == SIO::vKB_ENTER )
			{
				pParams->AddChecksum( NONAME, Script::GenerateCRC( "got_enter" ));
			}
			else if( makes[i] == SIO::vKB_BACKSPACE )
			{
				pParams->AddChecksum( NONAME, Script::GenerateCRC( "got_backspace" ));
			}
			else
			{
				char text_string[2];

				text_string[0] = makes[i];
				text_string[1] = '\0';
				pParams->AddString( "text", text_string );
			}
			
			pParams->AddInteger( "max_length", mdl.m_max_kb_string_length );
			Script::RunScript( "handle_keyboard_input", pParams );
			delete pParams;
		}
	}

	// Don't let anyone else handle the keypresses
	SIO::KeyboardClear();
}


/*****************************************************************
*	This friendly little function gets called every frame. 'game_is_paused'
*	is true if the game is paused, or if the player is in the front end.
*
*	I will probably move this function to MainMod.cpp or some other more
*	logical place later on.                                                                
*                                                               
******************************************************************/
void FrontEnd::update(bool game_is_paused)
{	
	/*
		===========================================
		Non-front end Stuff
		===========================================
	*/

	// Turn Keybard handler on and off

	#ifdef	__PLAT_NGPS__	
	if (	game_is_paused										// paused
		|| 	GameNet::Manager::Instance()->InNetGame()			// or a network game
		|| Mdl::Skate::Instance()->m_requested_level == 0x9f2bafb7        	// (0x9f2bafb7 === Load_SkateShop) In skateshop
		|| m_handle_keyboard_task->InList() 					// if the keyboard is on-screen
		)
	{
		SIO::SetKeyboardActive(true);
	}
	else
	{
		SIO::SetKeyboardActive(false);
	}
	#endif
	

	
	// Call these functions every frame:
	Pcm::Update( );	

	Sfx::CSfxManager * sfx_manager = Sfx::CSfxManager::Instance();
	sfx_manager->Update();

	if ( !game_is_paused )
	{
		// Mick: using the default heap here....
		// as spawned scripts can fill up the front end heap in an unpredictable manner
		// like with the LA earthquake	
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());


#		ifdef __USE_PROFILER__
		Sys::CPUProfiler->PushContext( 128, 0, 0 ); 	// red under green = Positional Sounds
#		endif // __USE_PROFILER__

		sfx_manager->UpdatePositionalSounds( );

#		ifdef __USE_PROFILER__
		Sys::CPUProfiler->PopContext( ); 	// 
#		endif // __USE_PROFILER__

		Mem::Manager::sHandle().PopContext();
	}

	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());

#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PushContext( 128, 128, 128 ); 	// gray under green = spawned scripts
#	endif // __USE_PROFILER__

	// And call these functions every game frame:
	Script::UpdateSpawnedScripts();
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	skate_mod->UpdateGameFlow();

	// mick: run the special script I use for command line scripting	
	if (run_runmenow)
	{
		run_runmenow = false;
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		Obj::CSkater *pSkater = skate_mod->GetLocalSkater();						   
		Script::RunScript("RunMeNow",NULL,pSkater);
	}


#	ifdef __USE_PROFILER__
	Sys::CPUProfiler->PopContext( ); 	// 
#	endif // __USE_PROFILER__

	Mem::Manager::sHandle().PopContext();
}




uint32 FrontEnd::grab_timed_event_type(uint mask, int index)
{
    char makes[256];
	int i, num_chars;
	uint32 event_type = 0;
		
	/*
		We check for these types of pad presses first, because of the timing element
	*/
	if (mask & Inp::Data::mD_LEFT)
		event_type = Obj::CEvent::TYPE_PAD_LEFT;
	else if (mask & Inp::Data::mD_RIGHT)
		event_type = Obj::CEvent::TYPE_PAD_RIGHT;
	else if (mask & Inp::Data::mD_UP)
		event_type = Obj::CEvent::TYPE_PAD_UP;
	else if (mask & Inp::Data::mD_DOWN)
		event_type = Obj::CEvent::TYPE_PAD_DOWN;	
	
	// Let the pad take precedence over the keyboard
	if(( index == 0 ) && ( event_type == 0 ))
	{
		// First check for keyboard events, if there were any
		num_chars = SIO::KeyboardRead( makes );
	
		if( num_chars > 0 )
		{
			for( i = 0; i < num_chars; i++ )
			{
				if( makes[i] == SIO::vKB_LEFT )
				{
					event_type = Obj::CEvent::TYPE_PAD_LEFT;
					break;
				}
				else if( makes[i] == SIO::vKB_RIGHT )
				{
					event_type = Obj::CEvent::TYPE_PAD_RIGHT;
					break;
				}
				else if( makes[i] == SIO::vKB_UP )
				{
					event_type = Obj::CEvent::TYPE_PAD_UP;
					break;
				}
				else if( makes[i] == SIO::vKB_DOWN )
				{
					event_type = Obj::CEvent::TYPE_PAD_DOWN;
					break;
				}
			}
		}
	}

	if (m_pad_info[index].mState == NOT_DOWN)
	{
		if (event_type)
		{
			// The directional pad was freshly pressed, so we will send an event.
			// Begin next countdown until the pad input "counts" again.
			m_pad_info[index].mAutorepeatCountdown = m_auto_repeat_time[0];
			m_pad_info[index].mState = SLOW_REPEAT;
		}
	}
	else if (event_type)
	{ 		
		// The directional pad was held down last frame	(and this one)
		
		// update countdown	time
		Tmr::Time frame_time = (Tmr::Time) (Tmr::FrameLength() * Tmr::vRESOLUTION);
		if (frame_time < m_pad_info[index].mAutorepeatCountdown)
			m_pad_info[index].mAutorepeatCountdown -= frame_time;
		else
			m_pad_info[index].mAutorepeatCountdown = 0;

		if (m_pad_info[index].mAutorepeatCountdown == 0)
		{
			// countdown finished, time to fire event, reset countdown time
			if (m_pad_info[index].mState == SLOW_REPEAT)
				m_pad_info[index].mState = FAST_REPEAT;
			m_pad_info[index].mAutorepeatCountdown = m_auto_repeat_time[1];
		}
		else
			// don't fire pad event, countdown hasn't finished
			event_type = 0;
	}
	else
	{
		m_pad_info[index].mState = NOT_DOWN;
	}	

	if (m_using_auto_input && index == 0)
	{
		event_type = 0;

		Script::CScript *p_script = Script::GetScriptWithUniqueId(m_auto_input_script_id);
		if (p_script)
		{
			Script::UnpauseSpawnedScript(p_script);
		}
		else
		{
			m_using_auto_input = false;
		}		
	}
	
	return event_type;
}




uint32 FrontEnd::turn_mask_into_event_type(uint mask, int count, int index, uint buttons)
{
    for (int i = 0; i < MAX_BUTTON_EVENT_MAP_ENTRIES; i++)
	{
		if ((mask & (1< 0 )
		{
			for( i = 0; i < num_chars; i++ )
			{   
				// If the keyboard menu is showing, don't treat space and enter as X
				if( makes[i] == SIO::vKB_ESCAPE )
				{
					
					Script::CStruct* pParams;

					pParams= new Script::CStruct;
					pParams->AddChecksum( Script::GenerateCRC( "id" ), Script::GenerateCRC( "current_menu_anchor" ));
					if( Obj::ScriptObjectExists( pParams, NULL ) == false )
					{
						event_type = Obj::CEvent::TYPE_PAD_START;
					}
					else
					{
						event_type = Obj::CEvent::TYPE_PAD_BACK;
					}
					
					SIO::KeyboardClear();
					delete pParams;
					break;
				}
				else if(( makes[i] == SIO::vKB_ENTER ) || ( makes[i] == 32 ))
				{
					Script::CStruct* pParams;
					bool menu_up;

					menu_up = false;
					pParams = new Script::CStruct;
					pParams->AddChecksum( Script::GenerateCRC( "id" ), Script::GenerateCRC( "keyboard_anchor" ));
					
					// Enter and space act as "choose" only if you're not currently using the on-screen keyboard
					if( Obj::ScriptObjectExists( pParams, NULL ) == false )
					{
						pParams->Clear();
    					pParams->AddChecksum( Script::GenerateCRC( "id" ), Script::GenerateCRC( "actions_menu" ));
                        
                        // only allow enter in actions menu
                        if( (Obj::ScriptObjectExists( pParams, NULL ) == true) && ( makes[i] == 32 ) )
    					{
                            return 0;
                        }
                        else
                        {
                            pParams->Clear();
    						pParams->AddChecksum( Script::GenerateCRC( "id" ), Script::GenerateCRC( "current_menu_anchor" ));
    						if( Obj::ScriptObjectExists( pParams, NULL ))
    						{
    							menu_up = true;
    						}
    						else
    						{
    							pParams->Clear();
    							pParams->AddChecksum( Script::GenerateCRC( "id" ), Script::GenerateCRC( "dialog_box_anchor" ));
    							if( Obj::ScriptObjectExists( pParams, NULL ))
    							{
    								menu_up = true;
    							}
    						}
    
    						if( menu_up )
    						{
    							event_type = Obj::CEvent::TYPE_PAD_CHOOSE;
    							SIO::KeyboardClear();
    						}
                        }
					}
					delete pParams;
				}
			}
		}

		return event_type;
	}
	
    return 0;
}


void FrontEnd::UpdateInputHandlerMappings()
{
	Spt::SingletonPtr< Inp::Manager > inp_man;
	
	for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		printf("m_device_server_map[%i] = %i\n", i, m_device_server_map[i]);
		Inp::Handler< FrontEnd >* p_handler = GetInputHandler( i );
		if ( p_handler )
		{
			printf("got a handler\n");
			inp_man->ReassignHandler( *p_handler, m_device_server_map[i] );
		}
	}
}

int FrontEnd::GetInputHandlerMapping( int device_num )
{
	return m_device_server_map[device_num];
}


bool ScriptLaunchMenuScreen(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	uint32 which_menu;
	if (!pParams->GetChecksum("screen", &which_menu))
		Dbg_MsgAssert(0, ("need screen=... with LaunchMenuScreen command"));

	// XXX
	Ryan("requesting game menu from LaunchMenuScreen\n");
//	FrontEnd* frontend = FrontEnd::Instance();
//	frontend->RequestMenuScreen(which_menu);

	printf ("STUBBED: LaunchMenuScreen\n");
	
	return true;
}




bool ScriptSetMenuAutoRepeatTimes(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Script::CPair times;
	pParams->GetPair(NONAME, ×, Script::ASSERT);

	FrontEnd* p_front_end = FrontEnd::Instance();
	p_front_end->SetAutoRepeatTimes((Tmr::Time) (times.mX * (float) Tmr::vRESOLUTION),
									(Tmr::Time) (times.mY * (float) Tmr::vRESOLUTION));
	return true;
}




bool ScriptSetMenuPadMappings(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Script::CArray *p_map_array;
	pParams->GetArray(NONAME, &p_map_array, true);

	FrontEnd* p_front_end = FrontEnd::Instance();
	for (int i = 0; i < 2; i++)
	{
		uint32 flag = p_map_array->GetChecksum(i);
		FrontEnd::EControllerMode mode = FrontEnd::INACTIVE;
		if (flag == Script::GenerateCRC("inactive"))
			mode = FrontEnd::INACTIVE;
		else if (flag == Script::GenerateCRC("active"))
			mode = FrontEnd::ACTIVE;
		else if (flag == Script::GenerateCRC("use_as_first"))
			mode = FrontEnd::MAP_TO_FIRST;
		else
			Dbg_MsgAssert(0, ("unknown controller mode"));

		p_front_end->SetControllerMode(i, mode);
	}

	return true;
}




bool ScriptSetButtonEventMappings(Script::CStruct *pParams, Script::CScript *pScript)
{
	FrontEnd* p_front_end = FrontEnd::Instance();
	p_front_end->AddEntriesToEventButtonMap(pParams);
	
	if (pParams->ContainsFlag("block_menu_input"))
		p_front_end->SetControllerTempBlock(true);
	else if (pParams->ContainsFlag("unblock_menu_input"))
		p_front_end->SetControllerTempBlock(false);
	
	return true;
}


// @script bool | SetAnalogStickActiveForMenus | Turn on/off analog simulation of digital buttons,
// so you can turn them off during certain menus that use analog for other things. 
// @uparmopt 1 | on or off, 1 or 0
bool ScriptSetAnalogStickActiveForMenus(Script::CStruct *pParams, Script::CScript *pScript)
{
	int	active = 1;
	pParams->GetInteger(NONAME,&active,false);
	FrontEnd::Instance()->SetAnalogStickActiveForMenus((bool)active);
	return true;
}

} // namespace FrontEnd


================================================
FILE: Code/Sk/Modules/FrontEnd/FrontEnd.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:															        **
**																			**
**	File name:												                **
**																			**
**	Created: 		09/12/2000	-	rjm										**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __FRONTEND_H
#define __FRONTEND_H

#include 
#include 
#include 

#include 
#include 

#include 
#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Script
{
	class CStruct;
	class CScript;
}

namespace Mdl
{



/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class FrontEnd;

class FrontEnd : public  Module
{
	
	DeclareSingletonClass( FrontEnd );

public:
	enum
	{ 
		vMAIN_MENU 			= 0x6249f70e, // "main_menu"
		vGAME_MENU			= 0x69efa940, // "game_menu"
		vPARKED_MENU		= 0xab42b78b, // "parked_menu"
	};
	
	enum EControllerMode
	{
		INACTIVE,
		ACTIVE,
		MAP_TO_FIRST,
	};

	
	void 							PauseGame(bool pause);
	bool 							GamePaused() {return m_paused;}
	// This function is used to temporarily switch the paused task list on and off. You should reset it to its previous
	// state afterwards. Use SetActive() whenever you can.
	void 							SetPausedTasks(bool is_active);

	bool							PadsPluggedIn();
	void 							UseFirstPadForPausedMenu() {m_use_second_pad_for_paused_menu=false; m_using_one_pad_for_paused_menu=true;}
	void 							UseSecondPadForPausedMenu() {m_use_second_pad_for_paused_menu=true; m_using_one_pad_for_paused_menu=true;}

	void 							SetAutoRepeatTimes(Tmr::Time kickoff, Tmr::Time repeat) {m_auto_repeat_time[0] = kickoff; m_auto_repeat_time[1] = repeat;}
	void							GetAutoRepeatTimes(Tmr::Time &kickoff, Tmr::Time &repeat) {kickoff = m_auto_repeat_time[0]; repeat = m_auto_repeat_time[1];}
	void 							SetControllerMode(int controllerNum, EControllerMode mode) {m_pad_info[controllerNum].mMode = mode;}
	void 							SetControllerTempBlock(bool tempBlock) {m_temp_block_pad_input = tempBlock;}

	void							AddEntriesToEventButtonMap(Script::CStruct *pParams);

	void 							AddKeyboardHandler( int max_string_length );
	void 							RemoveKeyboardHandler( void );
	void 							SetAnalogStickActiveForMenus( bool isActive );

private:

	FrontEnd( void );
	virtual							~FrontEnd( void );

	void 							update(bool game_is_paused);
	uint32							grab_timed_event_type(uint mask, int index);
	uint32							turn_mask_into_event_type(uint mask, int count, int index, uint buttons);
	
	enum Commands 
	{
        mSELECTUP   			= nBit(0),
        mSELECTDOWN 			= nBit(1),
        mXDOWN 					= nBit(2),
		mTRIANGLEDOWN			= nBit(3),
		mSTARTDOWN				= nBit(4),
		mTOGGLE					= nBit(5),
		mSELECTLEFT				= nBit(6),
		mSELECTRIGHT			= nBit(7),
	};

	static	Tsk::Task< FrontEnd >::Code		s_logic_code;
	static	Tsk::Task< FrontEnd >::Code		s_handle_keyboard_code;
	static	Tsk::Task< FrontEnd >::Code		s_display_code;
	static	Inp::Handler< FrontEnd >::Code  s_input_logic_code;

	void							v_start_cb( void );
	void							v_stop_cb( void );
    	
	Tsk::Task< FrontEnd >*			m_logic_task;	
	Tsk::Task< FrontEnd >*			m_handle_keyboard_task;
	bool							m_use_second_pad_for_paused_menu;
	bool							m_using_one_pad_for_paused_menu;
	
   	Inp::Handler< FrontEnd >*		mp_input_handlers[SIO::vMAX_DEVICES];
public:
	// Used by ScriptAnyControllerPressed in cfuncs.cpp
	Inp::Handler< FrontEnd >*		GetInputHandler( int index ) { return mp_input_handlers[index]; }
	void							UpdateInputHandlerMappings();
	int								GetInputHandlerMapping( int device_num );

	int								m_device_server_map[SIO::vMAX_DEVICES];

private:

	bool							m_paused;
	bool							m_allowDeactivate;

	// first is "kickoff" time, second is "repeat" time
	Tmr::Time						m_auto_repeat_time[2];
	int								m_max_kb_string_length;

	enum EPadState
	{
		NOT_DOWN,
		SLOW_REPEAT,
		FAST_REPEAT,
	};
	
	struct PadInfo
	{
		EPadState					mState;
		Tmr::Time					mAutorepeatCountdown;
		EControllerMode				mMode;
	};
	
	PadInfo							m_pad_info[SIO::vMAX_DEVICES];
	bool							m_temp_block_pad_input;

	enum 
	{
		MAX_BUTTON_EVENT_MAP_ENTRIES	= 32,
		// use in mEventType
		DEAD_ENTRY						= 0,
	};
	
	struct EventMapEntry
	{
		uint						mDigitalButtonIndex;
		uint32						mEventType;
	};
	
	EventMapEntry					m_digital_button_event_map[32];
	
	bool							m_check_for_auto_input;
	bool							m_using_auto_input;
	uint32							m_auto_input_script_id;
};

bool ScriptLaunchMenuScreen(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetMenuAutoRepeatTimes(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetMenuPadMappings(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetButtonEventMappings(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetAnalogStickActiveForMenus(Script::CStruct *pParams, Script::CScript *pScript);

} // namespace FrontEnd

#endif




================================================
FILE: Code/Sk/Modules/Skate/BettingGuy.cpp
================================================
// BettingGuy.cpp

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 

#include 

#include 

namespace Game
{

const int vMINSQUAREDIST		= 1000001;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBettingGuy::CBettingGuy( Script::CStruct* pParams ) 
	: CGoal( pParams )
{
	printf("creating a CBettingGuy\n");
	Reset();
	m_shouldMove = true;
	m_currentMinigame = 0;
	m_straightWins = 0;
	m_straightLosses = 0;
	m_currentDifficulty = 1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBettingGuy::~CBettingGuy()
{
	// nothing yet
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBettingGuy::Activate()
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	CGoal* pGoal = pGoalManager->GetGoal( m_currentMinigame );
	Dbg_Assert( pGoal );

	Script::CStruct* pGoalParams = pGoal->GetParams();

	// get the bet amount
	int bet_amount;
	if ( pGoalParams->GetInteger( "bet_amount", &bet_amount, Script::NO_ASSERT ) )
	{
		m_betAmount = bet_amount;
	}

	SetActive();
	printf("activated!\n");
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBettingGuy::SetActive()
{
	// necessary?
	m_active = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBettingGuy::BetIsActive( uint32 betId )
{
	// printf("betid: %x, current: %x\n", betId, m_currentMinigame );
	return ( betId == m_currentMinigame && IsActive() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBettingGuy::IsActive()
{
	return ( m_hasOffered && !m_shouldMove );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBettingGuy::Update()
{
	// do we need to pick a new minigame?
	if ( m_shouldMove )
	{
		// printf("I need to move\n");
		MoveToNewNode();
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBettingGuy::IsExpired()
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBettingGuy::Deactivate( bool force, bool affect_tree )
{
	// no more minigame associated with this
	Reset();
	m_straightWins = 0;
	m_straightLosses = 0;
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// win function that gets called from a trickscript
// checks that the calling minigame is the current minigame
bool CBettingGuy::Win( uint32 goalId )
{
	if ( IsActive() && m_currentMinigame == goalId )
	{
		// this is the right minigame!
		return Win();
	}
	return false;
}

// win func that gets called automatically by update endbetattempt func
bool CBettingGuy::Win()
{	
	printf("CBettingGuy::Win\n");
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->AddCash( m_betAmount );
	Reset();

	RunCallbackScript( vSUCCESS );
	m_numBetsWon++;
	m_straightWins++;
	if ( m_straightWins > 3 && m_currentDifficulty < 3 )
	{
		m_currentDifficulty++;
		m_straightWins = 0;
	}

	m_straightLosses = 0;
	m_shouldMove = true;

	SetInactive();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBettingGuy::Lose()
{
	printf("CbettingGuy::Lose\n");
	RunCallbackScript( vFAIL );
	Reset();
	m_straightLosses++;
	if ( m_straightLosses > 3 && m_currentDifficulty > 1 )
	{
		m_currentDifficulty--;
		m_straightLosses = 0;
	}

	m_straightWins = 0;
	m_shouldMove = true;
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBettingGuy::Reset()
{
	m_shouldMove = false;
	// m_currentMinigame = 0;
	m_numTries = 0;
	// m_currentDifficulty = 1;
	m_hasOffered = false;
	m_inAttempt = false;
	m_currentChallenge = 0;
	m_betAmount = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBettingGuy::MoveToNewNode( bool force )
{
	CGoalManager* pGoalManager = Game::GetGoalManager();
	Dbg_Assert( pGoalManager );
	uint32 minigameId = pGoalManager->GetRandomBettingMinigame();
	if ( minigameId == 0 || minigameId == m_currentMinigame )
		return false;	
			
	uint32 node_checksum;
	Script::CStruct* p_minigame_params = pGoalManager->GetGoal( minigameId )->GetParams();
	Dbg_Assert( p_minigame_params );
	p_minigame_params->GetChecksum( "betting_guy_node", &node_checksum, Script::ASSERT );

	if ( force )
	{
		// move him!
		// printf("moving betting guy\n");

		// record his suspend state and turn off suspension
		Reset();
		m_currentMinigame = minigameId;
		SetupParams();

		uint32 betting_guy_id;
		mp_params->GetChecksum( "betting_guy_id", &betting_guy_id, Script::ASSERT );
		
//		Obj::CMovingObject* p_bet_guy = (Obj::CMovingObject*)(Obj::CMovingObject::m_hash_table.GetItem( betting_guy_id ));
		Obj::CMovingObject* p_bet_guy = (Obj::CMovingObject*)(Obj::CCompositeObjectManager::Instance()->GetObjectByID( betting_guy_id ));
		
		Dbg_Assert( p_bet_guy );

// Mick:  removed this, as suspension handled differently under new components system
//		bool suspend_state = p_bet_guy->m_never_suspend;
//		p_bet_guy->m_never_suspend = true;

		Script::CStruct* p_temp_params = new Script::CStruct();
		p_temp_params->AddChecksum( "goal_id", GetGoalId() );
		p_temp_params->AddChecksum( "new_node", node_checksum );
		Script::RunScript( "betting_guy_move_to_node", p_temp_params );
		delete p_temp_params;

		// make sure he's updated and restore his suspension state
		p_bet_guy->Update();
		
		
//		p_bet_guy->m_never_suspend = suspend_state;
	
		mp_params->AddChecksum( "current_minigame", minigameId );

		m_currentNode = node_checksum;

		return true;
	}

	// printf("got a minigameId of %x\n", minigameId);
	
	// get the local skater's position
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = skate_mod->GetLocalSkater();
	Dbg_Assert( pSkater );
	Mth::Vector skater_pos = pSkater->GetPos();
	
	// see if the skater is too close to the betting guy
	Mth::Vector current_pos;
	if ( m_currentNode )
	{
		int current_node = SkateScript::FindNamedNode( m_currentNode );
		SkateScript::GetPosition( current_node, ¤t_pos );
	
		float d = ( skater_pos - current_pos ).LengthSqr();
		// printf("square distance from the skater to the betting guy: %f\n", d);
		if ( d < vMINSQUAREDIST )
		{
			// printf("the skater is too close to the betting guy\n");
			return false;
		}
	}
	
	// try out the destination node	
	// get the node's position
	int node = SkateScript::FindNamedNode( node_checksum );
	Mth::Vector node_pos;
	SkateScript::GetPosition(node, &node_pos);
	
	// get the square of the distance between them
	float distSqr = ( skater_pos - node_pos ).LengthSqr();
	
	// make sure it isn't too close
	// printf("sqaure distance from skater to next node: %f\n", distSqr);
	if ( distSqr < vMINSQUAREDIST )
	{
		// printf("the skater is too close to the betting guy's destination node\n");
		return false;
	}
	else
	{			
		// move him!
		// printf("moving betting guy\n");
		Reset();
		m_currentMinigame = minigameId;
		SetupParams();

		Script::CStruct* p_temp_params = new Script::CStruct();
		p_temp_params->AddChecksum( "goal_id", GetGoalId() );
		p_temp_params->AddChecksum( "new_node", node_checksum );
		Script::RunScript( "betting_guy_move_to_node", p_temp_params );
		delete p_temp_params;
	
		mp_params->AddChecksum( "current_minigame", minigameId );

		m_currentNode = node_checksum;
		return true;
	}
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBettingGuy::StartBetAttempt( uint32 goalId )
{
	// this acts like a secondary activate
	if ( IsActive() && m_currentMinigame == goalId && !m_inAttempt && !m_shouldMove )
	{
		printf("starting bet attempt\n");
		m_inAttempt = true;
	}
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CBettingGuy::EndBetAttempt( uint32 goalId )
{
	// this acts like a secondary deactivate
	if ( IsActive() && m_currentMinigame == goalId && m_inAttempt && !m_shouldMove )
	{
		//printf("CBettingGuy::EndBetAttempt\n");
		// printf("step 2\n");
		m_inAttempt = false;
		
		Script::CStruct* pBetParams = GetBetParams();
		
		// check that they haven't exceeded the number of tries
		int max_attempts;
		pBetParams->GetInteger( "tries", &max_attempts, Script::ASSERT );

		// we're challenging them to break a record
		CGoalManager* pGoalManager = GetGoalManager();
		Dbg_Assert( pGoalManager );
		Script::CStruct* pMinigameParams = pGoalManager->GetGoal( m_currentMinigame )->GetParams();
		Dbg_Assert( pMinigameParams );
		if ( m_currentChallenge )
		{
			// printf("step 3\n");
			int last_attempt;
			if ( pMinigameParams->GetInteger( "last_attempt", &last_attempt, Script::NO_ASSERT ) )
			{
				printf("got a last attempt of %i\n", last_attempt);
				printf("m_currentChallenge is %i\n", m_currentChallenge);
				if ( last_attempt >= m_currentChallenge )
				{						
					// printf("you won the bet!\n");
					// beat the bet and call the minigame's bet_success script
					this->Win();
					return true;
				}
			}
		}
		// check that this isn't their last try
		m_numTries++;
		if ( m_numTries >= max_attempts )
		{
			Lose();
			return true;
		}
		// if it's not a challenge minigame, it's probably a trickspot
		// minigame.  In that case, we'll be told when the game is won
		// the the gap script.
	}
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CBettingGuy::GetBetParams()
{
	Script::CStruct* p_params = NULL;
	
	if ( m_currentMinigame )
	{		
		CGoalManager* pGoalManager = GetGoalManager();
		Dbg_Assert( pGoalManager );
		
		CGoal* pGoal = pGoalManager->GetGoal( m_currentMinigame );
		Dbg_Assert( pGoal );

		Script::CStruct* p_temp = pGoal->GetParams();
		
		switch ( m_currentDifficulty )
		{
		case 1:
			p_temp->GetStructure( "bet_easy", &p_params, Script::NO_ASSERT );
			break;
		case 2:
			p_temp->GetStructure( "bet_medium", &p_params, Script::NO_ASSERT );
			break;
		case 3:
			p_temp->GetStructure( "bet_hard", &p_params, Script::NO_ASSERT );
			break;
		default:
			Dbg_MsgAssert( 0, ( "Betting game had unknown difficulty level" ) );
		}
	}
	return p_params;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBettingGuy::OfferMade()
{
	m_hasOffered = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBettingGuy::OfferRefused()
{
	m_shouldMove = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CBettingGuy::OfferAccepted()
{
}

void CBettingGuy::SetupParams()
{
	Script::CStruct* pBetParams = GetBetParams();
	Dbg_Assert( pBetParams );
	
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	Script::CStruct* p_minigameParams = pGoalManager->GetGoal( m_currentMinigame )->GetParams();
	Dbg_Assert( p_minigameParams );
	
	pBetParams->GetInteger( CRCD(0x1d7750fa,"bet_amount"), &m_betAmount, Script::ASSERT );
	
	mp_params->RemoveComponent( CRCD(0x1d7750fa,"bet_amount") );
	mp_params->AddInteger( CRCD(0x1d7750fa,"bet_amount"), m_betAmount );
	
	mp_params->RemoveComponent( CRCD(0xf3d0c702,"current_challenge") );
	if ( pBetParams->GetInteger( CRCD(0x1931ac3b,"bet_challenge"), &m_currentChallenge, Script::NO_ASSERT ) )
	{
		// m_currentChallenge = current_challenge;
		printf("got a challenge: %i\n", m_currentChallenge);
		mp_params->AddInteger( CRCD(0xf3d0c702,"current_challenge"), m_currentChallenge );
	}

	int max_attempts;
	pBetParams->GetInteger( CRCD(0x62396931,"tries"), &max_attempts, Script::ASSERT );
	mp_params->RemoveComponent( "num_tries" );
	mp_params->AddInteger( CRCD(0x25bad847,"num_tries"), max_attempts );
	
	mp_params->RemoveComponent( CRCD(0x6922090,"bet_unit") );
	mp_params->RemoveComponent( CRCD(0xd41aac2a,"bet_action") );
	mp_params->RemoveComponent( CRCD(0xa1617634,"location") );
	// get the trick string if necessary
	if ( p_minigameParams->ContainsFlag( CRCD(0xbc167894,"trickspot") ) )
	{
		Script::CArray* p_keyCombos;
		pBetParams->GetArray( CRCD(0x79704516,"key_combos"), &p_keyCombos, Script::ASSERT );
		Dbg_MsgAssert( p_keyCombos->GetType() == ESYMBOLTYPE_NAME, ( "Betting guy key_combos array has bad type" ) );
		int key_combo_index = GetRandomIndexFromKeyCombos( p_keyCombos );
		uint32 key_combo = p_keyCombos->GetChecksum( key_combo_index );
	
		uint32 trick_name_checksum;
		// get the global trick mapping
		Mdl::Skate* skate_mod = Mdl::Skate::Instance();
		Obj::CSkaterProfile* p_SkaterProfile = skate_mod->GetCurrentProfile();
		Script::CStruct* p_trickMappings = p_SkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );
		p_trickMappings->GetChecksum( key_combo, &trick_name_checksum, Script::ASSERT );
	
		const char* trick_text;
		if ( trick_name_checksum )
		{
			Script::CStruct* p_trick_structure = Script::GetStructure( trick_name_checksum, Script::ASSERT );
			Script::CStruct* p_trick_params;
			p_trick_structure->GetStructure( CRCD(0x7031f10c,"Params"), &p_trick_params, Script::ASSERT );
			p_trick_params->GetLocalText( CRCD(0xa1dc81f9,"Name"), &trick_text, Script::ASSERT );
		}
		mp_params->AddString( CRCD(0xd41aac2a,"bet_action"), trick_text );
	
		const char* location;
		p_minigameParams->GetString( CRCD(0xa1617634,"location"), &location, Script::ASSERT );
		mp_params->AddString( CRCD(0xa1617634,"location"), location );
	}
	else 
	{
		const char* bet_action;
		// the bet action is global
		p_minigameParams->GetString( CRCD(0xd41aac2a,"bet_action"), &bet_action, Script::ASSERT );
		mp_params->AddString( CRCD(0xd41aac2a,"bet_action"), bet_action );
		const char* bet_unit;
		p_minigameParams->GetString( CRCD(0x6922090,"bet_unit"), &bet_unit, Script::ASSERT );
		mp_params->AddString( CRCD(0x6922090,"bet_unit"), bet_unit );
	}
}

}


================================================
FILE: Code/Sk/Modules/Skate/BettingGuy.h
================================================
// BettingGuy.h
#ifndef _SK_MODULES_SKATE_BETTINGGUY_H_
#define _SK_MODULES_SKATE_BETTINGGUY_H_

#include 

#include 

namespace Game
{

class CBettingGuy : public CGoal
{
public:
						CBettingGuy( Script::CStruct* pParams );
	virtual				~CBettingGuy();

	bool				IsActive();
	bool				ShouldUseTimer() { return false; }
	bool				CountAsActive() { return false; }
	bool				Activate();
	void				SetActive();
	bool				Update();
	bool				MoveToNewNode( bool force = false );
	bool				IsExpired();
	bool				Deactivate( bool force = false, bool affect_tree = true );
	bool				Win();
	bool				Win( uint32 goalId );
	bool				Lose();
	void				Reset();

	bool				EndBetAttempt( uint32 goalId );
	bool				StartBetAttempt( uint32 goalId );
	bool				BetIsActive( uint32 goalId );

	void				OfferMade();
	void				OfferRefused();
	void				OfferAccepted();

	Script::CStruct*	GetBetParams();
	void				SetupParams();

protected:
	bool				m_shouldMove;
	int					m_numBetsWon;
	uint32				m_currentMinigame;
	uint32				m_currentNode;
	int					m_currentDifficulty;
	bool				m_hasOffered;
	bool				m_inAttempt;
	int					m_numTries;
	int					m_currentChallenge;
	int					m_betAmount;
	int					m_straightLosses;
	int 				m_straightWins;
};

}	// namespace Game

#endif


================================================
FILE: Code/Sk/Modules/Skate/CATGoal.cpp
================================================
// Create-A-Trick goal!

#include 
#include 

namespace Game
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCatGoal::CCatGoal( Script::CStruct* pParams ) : CGoal( pParams )
{
	// uh...
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCatGoal::~CCatGoal()
{
	// hmm...
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CCatGoal::Activate()
{
	return CGoal::Activate();
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCatGoal::Deactivate( bool force, bool affect_tree )
{
	return CGoal::Deactivate( force, affect_tree );
}


}	// namespace game


================================================
FILE: Code/Sk/Modules/Skate/CATGoal.h
================================================
// Create-A-Trick goal!

#ifndef __SK_MODULES_SKATE_CATGOAL_H__
#define __SK_MODULES_SKATE_CATGOAL_H__

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

namespace Game
{

class CCatGoal : public CGoal
{

public:
						CCatGoal( Script::CStruct* pParams );
	virtual				~CCatGoal();

	bool				Activate();
	bool				Deactivate( bool force = false, bool affect_tree = true );
protected:
};

}

#endif // #ifndef __SK_MODULES_SKATE_FINDGAPSGOAL_H__


================================================
FILE: Code/Sk/Modules/Skate/CompetitionGoal.cpp
================================================
#include 
#include 

#include 
#include 

#include 

#include 
#include 
#include 
#include 

#include 

namespace Game
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCompetitionGoal::CCompetitionGoal( Script::CStruct* pParams )
	: CGoal( pParams )
{
	// uhh...necessary?
	m_compPaused = false;
	m_gotGold = m_gotSilver = m_gotBronze = false;
	m_rewardGiven = 0;

	m_winScoreRecord = vINT_MAX;

	m_endRunType = vENDOFRUN;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CCompetitionGoal::~CCompetitionGoal()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompetitionGoal::Expire()
{
	// don't deactivate the goal!
	RunCallbackScript( Game::vEXPIRED );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CCompetitionGoal::IsActive()
{
	return m_active;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompetitionGoal::SetActive()
{
	// printf("setting to active\n");
	m_active = true;
	m_compPaused = false;
	m_paused = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompetitionGoal::SetInactive()
{
	m_active = false;
	m_compPaused = false;
	m_paused = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CCompetitionGoal::HasWonGoal()
{
	return m_isbeat;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompetitionGoal::MarkBeaten()
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Mdl::CCompetition* pComp = skate_mod->GetCompetition();
	
	if ( pComp->PlaceIs( 1 ) )
	{
		m_gotGold = true;
		m_gotSilver = true;
		m_gotBronze = true;
	}
	else if ( pComp->PlaceIs( 2 ) )
	{
		m_gotSilver = true;
		m_gotBronze = true;
	}
	else if ( pComp->PlaceIs( 3 ) )
	{
		m_gotBronze = true;
	}

	// m_gotGold = true;
	// m_gotSilver = true;
	// m_gotBronze = true;
	m_isbeat = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CCompetitionGoal::UnBeatGoal()
{
	m_isbeat = false;
	m_gotBronze = false;
	m_gotSilver = false;
	if ( HasWonGoal() )
	{
		m_gotGold = false;
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CCompetitionGoal::Win()
{
	mp_params->AddChecksum( NONAME, CRCD( 0xc309cad1, "just_won_goal" ) );
	RunCallbackScript( Game::vDEACTIVATE );
	mp_params->RemoveFlag( CRCD( 0xc309cad1, "just_won_goal" ) );
	SetInactive();
	
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	
	if ( !gamenet_man->InNetGame() )
	{
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		Mdl::CCompetition* pComp = skate_mod->GetCompetition();
		
		int place = 999;
		if ( pComp->PlaceIs( 1 ) )
			place = 1;
		else if ( pComp->PlaceIs( 2 ) )
			place = 2;
		else if ( pComp->PlaceIs( 3 ) )
			place = 3;

		if ( place < m_winScoreRecord )
		{
			mp_params->AddInteger( "win_record", place );
			m_winScoreRecord = place;
		}
	}

#ifdef __NOPT_ASSERT__
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
#endif		// __NOPT_ASSERT__
		
	Script::CStruct* p_reward_params = new Script::CStruct();
	
	if ( !HasWonGoal() )
	{
		// don't add cash
		Script::CStruct* pTempStruct = new Script::CStruct();
		GetRewardsStructure( pTempStruct, true );
		p_reward_params->AppendStructure( pTempStruct );
		delete pTempStruct;
		UnlockRewardGoals();
	}
	else
		mp_params->RemoveComponent( "reward_params" );

	// award any cash
	// AwardCash( p_reward_params );

	// mp_params->RemoveComponent( "reward_params" );
	mp_params->AddStructure( "reward_params", p_reward_params );
	RunCallbackScript( Game::vSUCCESS );
	
	delete p_reward_params;

	// now it's safe to mark as beaten
	if ( !HasWonGoal() )
		MarkBeaten();
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompetitionGoal::AwardCash( Script::CStruct* p_reward_params )
{
#ifdef __NOPT_ASSERT__
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
#endif		// __NOPT_ASSERT__
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Mdl::CCompetition* pComp = skate_mod->GetCompetition();
	
	// figure any cash reward
	if ( pComp->PlaceIs( 1 ) || pComp->PlaceIs( 2 ) || pComp->PlaceIs( 3 ) )
	{		
		int total_reward;
		int current_reward = 0;
		if ( mp_params->GetInteger( "reward_cash", &total_reward, Script::NO_ASSERT ) )
		{			
			if ( pComp->PlaceIs( 1 ) && !m_gotGold )
			{
				current_reward = total_reward - m_rewardGiven;
				m_gotGold = true;
				m_gotSilver = true;
				m_gotBronze = true;
			}
			else if ( pComp->PlaceIs( 2 ) && !m_gotSilver )
			{
				current_reward = total_reward * 2 / 3;
				if ( m_gotBronze )
					current_reward -= total_reward / 3;

				m_gotSilver = true;
				m_gotBronze = true;				

			}
			else if ( pComp->PlaceIs( 3 ) && !m_gotBronze )
			{
				current_reward = total_reward / 3;
				m_gotBronze = true;
			}

			if ( current_reward > 0 )
			{
				// pGoalManager->AddCash( current_reward );
				// Script::RunScript( "goal_got_cash" );
				p_reward_params->AddInteger( "cash", current_reward );
				m_rewardGiven += current_reward;
			}
		}
	}   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompetitionGoal::PauseCompetition()
{
	m_compPaused = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompetitionGoal::UnPauseCompetition()
{
	m_compPaused = false;
	m_endRunCalled = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CCompetitionGoal::IsPaused()
{
	return ( m_paused || m_compPaused );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompetitionGoal::LoadSaveData( Script::CStruct* pFlags )
{
	int gotBronze;
	pFlags->GetInteger( "gotBronze", &gotBronze, Script::NO_ASSERT );
	if ( gotBronze != 0 )
		m_gotBronze = true;

	int gotSilver;
	pFlags->GetInteger( "gotSilver", &gotSilver, Script::NO_ASSERT );
	if ( gotSilver != 0 )
		m_gotBronze = true;

	int gotGold;
	pFlags->GetInteger( "gotGold", &gotGold, Script::NO_ASSERT );
	if ( gotGold != 0 )
		m_gotBronze = true;

	int reward_given;
	pFlags->GetInteger( "reward_given", &reward_given, Script::NO_ASSERT );
	m_rewardGiven = reward_given;

	int record;
	if ( pFlags->GetInteger( "win_record", &record, Script::NO_ASSERT ) )
		m_winScoreRecord = record;
	
	CGoal::LoadSaveData( pFlags );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CCompetitionGoal::GetSaveData( Script::CStruct* pFlags )
{
	CGoal::GetSaveData( pFlags );
	
	if ( m_gotBronze )
		pFlags->AddInteger( "gotBronze", 1 );
	else
		pFlags->AddInteger( "gotBronze", 0 );

	if ( m_gotSilver )
		pFlags->AddChecksum( "gotSilver", 1 );
	else
		pFlags->AddChecksum( "gotSilver", 0 );

	if ( m_gotGold )
		pFlags->AddChecksum( "gotGold", 1 );
	else
		pFlags->AddChecksum( "gotGold", 0 );

	pFlags->AddInteger( "win_record", m_winScoreRecord );
	pFlags->AddInteger( "reward_given", m_rewardGiven );
}

}


================================================
FILE: Code/Sk/Modules/Skate/CompetitionGoal.h
================================================
#ifndef __SK_MODULES_SKATE_COMPETITIONGOAL_H__
#define __SK_MODULES_SKATE_COMPETITIONGOAL_H__

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

namespace Game
{


class CCompetitionGoal : public CGoal
{

public:
					CCompetitionGoal( Script::CStruct* pParams );
	virtual			~CCompetitionGoal();

	void			Expire();
	bool			IsActive();
	void			SetActive();
	void 			SetInactive();

	void			LoadSaveData( Script::CStruct* pFlags );
	void			GetSaveData( Script::CStruct* pFlags );

	bool			Win();
	bool			HasWonGoal();
	void			MarkBeaten();
	bool			UnBeatGoal();

	void			AwardCash( Script::CStruct* p_reward_params );

	void			PauseCompetition();
	void			UnPauseCompetition();

	bool			IsPaused();
protected:

	bool			m_compPaused;
	bool			m_gotGold;
	bool			m_gotSilver;
	bool			m_gotBronze;
	int				m_rewardGiven;
};

}

#endif


================================================
FILE: Code/Sk/Modules/Skate/CreateATrick.cpp
================================================
// CreateATrick.cpp

#include 
#include 

#include 

#include 
#include 
#include 

#include 

namespace Game
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCreateATrick::CCreateATrick() 
	
{
	printf("creating a CCreateATrick\n");

    // Other Params
    Script::CStruct* p_other_params = Script::GetStructure( "Default_CAT_other_info", Script::ASSERT );
    mp_other_params = new Script::CStruct();
    mp_other_params->AppendStructure( p_other_params );
    
    // Rotations
    Script::CArray* p_rotations;
    p_rotations = Script::GetArray( "Default_CAT_rotation_info", Script::ASSERT );

    mp_rotations = new Script::CArray();
    mp_rotations->SetSizeAndType( vMAX_ROTATIONS, ESYMBOLTYPE_STRUCTURE );
    Script::CopyArray( mp_rotations, p_rotations );
    

    // Animations
    Script::CArray* p_animations;
    p_animations = Script::GetArray( "Default_CAT_animation_info", Script::ASSERT );

    mp_animations = new Script::CArray();
    mp_animations->SetSizeAndType( vMAX_ANIMATIONS, ESYMBOLTYPE_STRUCTURE );
    Script::CopyArray( mp_animations, p_animations );
    
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCreateATrick::~CCreateATrick()
{
	if ( mp_other_params )
    {
        delete mp_other_params;
    }
    if ( mp_rotations )
    {
        Script::CleanUpArray( mp_rotations );
        delete mp_rotations;
    }
    if ( mp_animations )
    {
        Script::CleanUpArray( mp_animations );
        delete mp_animations;
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetCreateATrickParams | Gets the values for rotations and animations of a particular skater
bool ScriptGetCreateATrickParams(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();

    int trick_index;

    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
	
        pParams->GetInteger( "trick_index", &trick_index, Script::ASSERT );
        Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[trick_index];
    	Dbg_Assert( pCreatedTrick );
    
        //Other params
        pScript->GetParams()->AddStructure( "other_params", pCreatedTrick->mp_other_params );
        
        //Rotation params
        pScript->GetParams()->AddArray( "rotation_info", pCreatedTrick->mp_rotations );
        
        //Animation params
        pScript->GetParams()->AddArray( "animation_info", pCreatedTrick->mp_animations );
        
        return true;
    }
    return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetCreateATrickOtherParams | Gets the values for trick name, score, etc.
bool ScriptGetCreateATrickOtherParams(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();

    int trick_index;

	Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger( "trick_index", &trick_index, Script::ASSERT );
        Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[trick_index];
    	Dbg_Assert( pCreatedTrick );
        
        //Other params
        pScript->GetParams()->AddStructure( "other_params", pCreatedTrick->mp_other_params );
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetCreateATrickRotations | Gets the values for rotations
bool ScriptGetCreateATrickRotations(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();

    int trick_index;

    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger( "trick_index", &trick_index, Script::ASSERT );
        Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[trick_index];
    	Dbg_Assert( pCreatedTrick );
    
        //Rotation params
        pScript->GetParams()->AddArray( "rotation_info", pCreatedTrick->mp_rotations );
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetCreateATrickAnimations | Gets the values for animations
bool ScriptGetCreateATrickAnimations(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();

    int trick_index;

    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger( "trick_index", &trick_index, Script::ASSERT );
        Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[trick_index];
    	Dbg_Assert( pCreatedTrick );
    
        //Animation params
        pScript->GetParams()->AddArray( "animation_info", pCreatedTrick->mp_animations );
    
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetCreateATrickParams | Sets the values for rotations and animations of a particular skater
bool ScriptSetCreateATrickParams(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();

    int trick_index;
    
    Script::CStruct *p_other_params;
    Script::CArray *p_rotation_info;
    Script::CArray *p_animation_info;

    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
    
        pParams->GetInteger( "trick_index", &trick_index, Script::ASSERT );
        //Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[trick_index];
    	//Dbg_Assert( pCreatedTrick );
        
        // make sure the skater actually has created tricks before we try to update them.
        if ( !(pSkater->m_created_trick[1]) )
        {
            printf("Skater has no created tricks!!\n");
            return false;
        }
        else
        {
            //Script::PrintContents( pSkater->m_created_trick[trick_index]->mp_other_params );
        }
        //Dbg_Assert( pSkater->m_created_trick[1] );
    
        //Params
        if(pParams->GetStructure( "other_params", &p_other_params, Script::ASSERT ))
        {
            printf("Storing param info %i\n", trick_index);
            pSkater->m_created_trick[trick_index]->mp_other_params->AppendStructure( p_other_params );
        }
        
        //Rotations
        if(pParams->GetArray( "rotation_info", &p_rotation_info, Script::ASSERT ))
        {
            printf("Storing rotation info %i\n", trick_index);
            Script::CopyArray( pSkater->m_created_trick[trick_index]->mp_rotations, p_rotation_info );
        }
    
        //Animations
        if(pParams->GetArray( "animation_info", &p_animation_info, Script::ASSERT ))
        {
            printf("Storing animation info %i\n", trick_index);
            Script::CopyArray( pSkater->m_created_trick[trick_index]->mp_animations, p_animation_info );
        }
        
            
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetCreateATrickOtherParams | Sets name, score, etc.
bool ScriptSetCreateATrickOtherParams(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();

    
    int trick_index;
    
    Script::CStruct *p_other_params;
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
	
        pParams->GetInteger( "trick_index", &trick_index, Script::ASSERT );
    
        Dbg_Assert( pSkater->m_created_trick[trick_index] );
        
        //Params
        if(pParams->GetStructure( "other_params", &p_other_params, Script::ASSERT ))
        {
            printf("Storing param info %i\n", trick_index);
            // pSkater->m_created_trick[trick_index]->mp_other_params->Clear();
            pSkater->m_created_trick[trick_index]->mp_other_params->AppendStructure( p_other_params );
            //Script::PrintContents(pSkater->m_created_trick[trick_index]->mp_other_params);
        }
    
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetCreateATrickRotations | Sets the values for rotations
bool ScriptSetCreateATrickRotations(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();

    int trick_index;
    
    Script::CArray *p_rotation_info;
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger( "trick_index", &trick_index, Script::ASSERT );
    
        Dbg_Assert( pSkater->m_created_trick[trick_index] );
        
        //Rotations
        if(pParams->GetArray( "rotation_info", &p_rotation_info, Script::ASSERT ))
        {
            printf("Storing rotation info %i\n", trick_index);
            Script::CopyArray( pSkater->m_created_trick[trick_index]->mp_rotations, p_rotation_info );
        }
    
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetCreateATrickAnimations | Sets the values for rotations
bool ScriptSetCreateATrickAnimations(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();

    int trick_index;
    
    Script::CArray *p_animation_info;
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger( "trick_index", &trick_index, Script::ASSERT );
        
        Dbg_Assert( pSkater->m_created_trick[trick_index] );
        
        //Animations
        if(pParams->GetArray( "animation_info", &p_animation_info, Script::ASSERT ))
        {
            printf("Storing animation info %i\n", trick_index);
            Script::CopyArray( pSkater->m_created_trick[trick_index]->mp_animations, p_animation_info );
        }
    
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_SetNumAnims | 
bool ScriptCAT_SetNumAnims(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    int value;

    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger(NONAME, &value, Script::ASSERT );
        pSkater->m_num_cat_animations_on = value;
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_GetNumAnims | 
bool ScriptCAT_GetNumAnims(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pScript->GetParams()->AddInteger( "num_animations_on", pSkater->m_num_cat_animations_on );
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_SetAnimsDone | 
bool ScriptCAT_SetAnimsDone(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    int value;
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger(NONAME, &value, Script::ASSERT );
        if ( value == 0 )
        {
            pSkater->m_cat_animations_done = false;
        }
        else
        {
            pSkater->m_cat_animations_done = true;
        }
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_GetAnimsDone | 
bool ScriptCAT_GetAnimsDone(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        if ( pSkater->m_cat_animations_done )
        {
            pScript->GetParams()->AddInteger( "cat_animations_done", 1 );
        }
        else
        {
            pScript->GetParams()->AddInteger( "cat_animations_done", 0 );
        }
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_SetRotsDone | 
bool ScriptCAT_SetRotsDone(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    int value;
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger(NONAME, &value, Script::ASSERT );
        if ( value == 0 )
        {
            pSkater->m_cat_rotations_done = false;
        }
        else
        {
            pSkater->m_cat_rotations_done = true;
        }
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_GetRotsDone | 
bool ScriptCAT_GetRotsDone(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        if ( pSkater->m_cat_rotations_done )
        {
            pScript->GetParams()->AddInteger( "cat_rotations_done", 1 );
        }
        else
        {
            pScript->GetParams()->AddInteger( "cat_rotations_done", 0 );
        }
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_SetBailDone | 
bool ScriptCAT_SetBailDone(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    int value;
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger(NONAME, &value, Script::ASSERT );
        if ( value == 0 )
        {
            pSkater->m_cat_bail_done = false;
        }
        else
        {
            pSkater->m_cat_bail_done = true;
        }
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_GetBailDone | 
bool ScriptCAT_GetBailDone(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        if ( pSkater->m_cat_bail_done )
        {
            pScript->GetParams()->AddInteger( "bailtimescriptdone", 1 );
        }
        else
        {
            pScript->GetParams()->AddInteger( "bailtimescriptdone", 0 );
        }
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_SetFlipSkater | 
bool ScriptCAT_SetFlipSkater(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    int value;
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger(NONAME, &value, Script::ASSERT );
        if ( value == 0 )
        {
            pSkater->m_cat_flip_skater_180 = false;
        }
        else
        {
            pSkater->m_cat_flip_skater_180 = true;
        }
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_GetFlipSkater | 
bool ScriptCAT_GetFlipSkater(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        if ( pSkater->m_cat_flip_skater_180 )
        {
            pScript->GetParams()->AddInteger( "cat_flip_skater_180", 1 );
        }
        else
        {
            pScript->GetParams()->AddInteger( "cat_flip_skater_180", 0 );
        }
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_SetHoldTime | 
bool ScriptCAT_SetHoldTime(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    float value;

    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetFloat(NONAME, &value, Script::ASSERT );
        pSkater->m_cat_hold_time = value;
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_GetHoldTime | 
bool ScriptCAT_GetHoldTime(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pScript->GetParams()->AddFloat( "cat_hold_time", pSkater->m_cat_hold_time );
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_SetTotalY | 
bool ScriptCAT_SetTotalY(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    int value;
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger(NONAME, &value, Script::ASSERT );
        pSkater->m_cat_total_y = value;
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_GetTotalY | 
bool ScriptCAT_GetTotalY(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pScript->GetParams()->AddInteger( "total_Y_angle", pSkater->m_cat_total_y );
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_SetTotalX | 
bool ScriptCAT_SetTotalX(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    int value;
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger(NONAME, &value, Script::ASSERT );
        pSkater->m_cat_total_x = value;
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_GetTotalX | 
bool ScriptCAT_GetTotalX(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pScript->GetParams()->AddInteger( "total_X_angle", pSkater->m_cat_total_x );
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_SetTotalZ | 
bool ScriptCAT_SetTotalZ(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    int value;
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pParams->GetInteger(NONAME, &value, Script::ASSERT );
        pSkater->m_cat_total_z = value;
        
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptCAT_GetTotalZ | 
bool ScriptCAT_GetTotalZ(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    
    Obj::CSkater* pSkater = pSkate->GetLocalSkater();
    if ( pSkater )
    {
        pScript->GetParams()->AddInteger( "total_Z_angle", pSkater->m_cat_total_z );
        return true;
    }
    return false;
}

 
}


================================================
FILE: Code/Sk/Modules/Skate/CreateATrick.h
================================================

// CreateATrick.h
#ifndef _SK_MODULES_SKATE_CREATEATRICK_H_
#define _SK_MODULES_SKATE_CREATEATRICK_H_

#include 

namespace Script
{
    class CStruct;
}

namespace Game
{

enum
{
    vMAX_CREATED_TRICKS=11,
    vMAX_ROTATIONS=6,
    vMAX_ANIMATIONS=6
};

class CCreateATrick
{
public:
						CCreateATrick();
	virtual				~CCreateATrick();

    //char*   m_name;
	//int     m_score;
    //bool    m_rotateafter;

    Script::CStruct *mp_other_params;
    Script::CArray *mp_rotations;
    Script::CArray *mp_animations;

};

// Get
bool ScriptGetCreateATrickParams(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptGetCreateATrickOtherParams(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptGetCreateATrickRotations(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptGetCreateATrickAnimations(Script::CScriptStructure *pParams, Script::CScript *pScript);

// Set
bool ScriptSetCreateATrickParams(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetCreateATrickOtherParams(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptSetCreateATrickRotations(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptSetCreateATrickAnimations(Script::CScriptStructure *pParams, Script::CScript *pScript);

// other scripts
bool ScriptCAT_SetNumAnims(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCAT_GetNumAnims(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptCAT_SetAnimsDone(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCAT_GetAnimsDone(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptCAT_SetRotsDone(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCAT_GetRotsDone(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptCAT_SetBailDone(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCAT_GetBailDone(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptCAT_SetFlipSkater(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCAT_GetFlipSkater(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptCAT_SetHoldTime(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCAT_GetHoldTime(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptCAT_SetTotalY(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCAT_GetTotalY(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptCAT_SetTotalX(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCAT_GetTotalX(Script::CScriptStructure *pParams, Script::CScript *pScript);

bool ScriptCAT_SetTotalZ(Script::CScriptStructure *pParams, Script::CScript *pScript);
bool ScriptCAT_GetTotalZ(Script::CScriptStructure *pParams, Script::CScript *pScript);


}	// namespace Game

#endif


================================================
FILE: Code/Sk/Modules/Skate/FilmGoal.cpp
================================================
// filming goal

#include 
#include 
#include 

#include 

#include 

#include 

#include 

#include 
#include 

namespace Game
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CFilmGoal::CFilmGoal( Script::CStruct* pParams ) : CGoal( pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CFilmGoal::~CFilmGoal()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CFilmGoal::Activate()
{
	m_timeOnCamera = 0;
	m_timeRequired = m_totalTime = 0;
	m_numShotsRequired = 0;
	m_filming = false;

	// script param for displaying time on camera...
	mp_params->AddInteger( CRCD( 0x9278ca8d, "time_on_camera" ), 0 );
	
	int time_required;
	if ( mp_params->GetInteger( CRCD(0x184e30fd,"total_time_required"), &time_required, Script::NO_ASSERT ) )
	{
		m_timeRequired = (Tmr::Time)( time_required * 1000 );
		int total_time;
		mp_params->GetInteger( CRCD(0x906b67ba,"time"), &total_time, Script::ASSERT );
		m_totalTime *= (Tmr::Time)( total_time * 1000 );
	}
	else if ( !mp_params->GetInteger( CRCD(0xc808f9fb,"total_shots_required"), &m_numShotsRequired, Script::NO_ASSERT ) )
	{
		Dbg_MsgAssert( 0, ( "Film goal %s requires either total_time_required or total_shots_required", Script::FindChecksumName( GetGoalId() ) ) );
	}	
	return CGoal::Activate();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CFilmGoal::Update()
{
	mp_params->AddInteger( CRCD(0xfc7fc309,"last_time_on_camera"), (int)m_timeOnCamera );

    if ( IsActive() && !IsPaused() )
	{
		// check if they're still looking at the target object
		bool should_win = false;
		if ( m_filming )
		{
			if ( target_object_visible() )
			{
				m_timeOnCamera += (Tmr::Time)( Tmr::FrameLength() * 1000 );
				
				// script param for displaying time on camera...
				mp_params->AddInteger( CRCD( 0x9278ca8d, "time_on_camera" ), (int)m_timeOnCamera );
			}
			// printf( "on camera: %i, required: %i\n", (int)m_timeOnCamera, (int)m_timeRequired );
	
			if ( m_timeOnCamera >= m_timeRequired )
			{
				should_win = true;
			}
			else if ( m_timeRequired - m_timeOnCamera > m_timeLeft )
			{
				// can't possibly win!
				CGoalManager* pGoalManager = GetGoalManager();
				Dbg_Assert( pGoalManager );
				return pGoalManager->LoseGoal( GetGoalId() );
			}
		}
		else if ( m_numShotsRequired && m_numShotsAchieved >= m_numShotsRequired )
		{
			should_win = true;
		}
	
		if ( should_win )
		{
			CGoalManager* pGoalManager = GetGoalManager();
			Dbg_Assert( pGoalManager );
			return pGoalManager->WinGoal( GetGoalId() );		
		}
		else
			return CGoal::Update();
	}
	else
		return CGoal::Update();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CFilmGoal::Deactivate( bool force, bool affect_tree )
{
	return CGoal::Deactivate( force, affect_tree );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFilmGoal::CheckpointHit()
{
	Dbg_MsgAssert( m_numShotsRequired, ( "GoalManager_FilmCheckpoint called on a non-checkpoint goal." ) );
	if ( target_object_visible() )
	{
		m_numShotsAchieved++;

		if ( m_numShotsAchieved > m_numShotsRequired )
		{
			CGoalManager* pGoalManager = GetGoalManager();
			Dbg_Assert( pGoalManager );
			pGoalManager->WinGoal( GetGoalId() );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// does a visibility check as well as a distance check - you can't just
// look at him from a mile away
bool CFilmGoal::target_object_visible()
{
	uint32 target_obj_id;
	mp_params->GetChecksum( CRCD( 0xd262ed09, "film_target" ), &target_obj_id, Script::ASSERT );
	Obj::CCompositeObject* pTargetObj = (Obj::CCompositeObject*)Obj::ResolveToObject( target_obj_id );
	Dbg_Assert( pTargetObj );

	bool visible = false;
	if ( pTargetObj )
	{
		Obj::CModelComponent* pTargetObjModelComponent = GetModelComponentFromObject( pTargetObj );
		Mth::Vector sphere = pTargetObjModelComponent->GetModel()->GetBoundingSphere();
		Mth::Vector position = sphere;
		position[3] = 1.0f;
		position *= pTargetObj->GetMatrix();
		position += pTargetObj->GetPos();
		//if ( ( Nx::CEngine::sIsVisible( position, sphere[3] ) ) )
		if ( ( Nx::CEngine::sIsVisible( position, 6.0f ) ) ) // TT#2555 (Mick), use a 6 inch radius sphere te ensure we can really see him
		{
			// get the cam
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			Obj::CSkater* pSkater = skate_mod->GetLocalSkater();
			Dbg_Assert( pSkater );
			Gfx::Camera* pCurrentCamera = pSkater->GetActiveCamera();

			float distance_to_target = Mth::Distance( pCurrentCamera->GetPos(), pTargetObj->GetPos() );
			int max_distance;
			mp_params->GetInteger( CRCD(0x856fab92,"max_distance_to_target"), &max_distance, Script::ASSERT );
			if ( distance_to_target < (float)max_distance )
			{
				visible = true;
			}
		}
	}
	return visible;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFilmGoal::StartFilming()
{
	Dbg_MsgAssert( m_timeRequired, ( "StartFilming called on a non-filming goal." ) );
	m_filming = true;
}

}	// namespace Game



================================================
FILE: Code/Sk/Modules/Skate/FilmGoal.h
================================================
// film goal.

#ifndef __SK_MODULES_SKATE_FILMGOAL_H__
#define __SK_MODULES_SKATE_FILMGOAL_H__

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

namespace Game
{

class CFilmGoal : public CGoal
{

public:
						CFilmGoal( Script::CStruct* pParams );
	virtual				~CFilmGoal();

	bool				Activate();
	bool				Deactivate( bool force = false, bool affect_tree = true );
	bool				Update();

	void				CheckpointHit();
	void				StartFilming();
protected:
	bool				target_object_visible();

	bool				m_filming;
	
	Tmr::Time           m_timeOnCamera;
	Tmr::Time			m_timeRequired;
	Tmr::Time			m_totalTime;

	int					m_numShotsRequired;
	int					m_numShotsAchieved;

};

}

#endif



================================================
FILE: Code/Sk/Modules/Skate/FindGapsGoal.cpp
================================================
// find x gaps
#include 
#include 

#include 

#include 
#include 

// #include 

namespace Game
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CFindGapsGoal::CFindGapsGoal( Script::CStruct* pParams ) : CGoal( pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CFindGapsGoal::~CFindGapsGoal()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CFindGapsGoal::Activate()
{
	mp_params->RemoveComponent( CRCD(0x735af2dd,"completedGaps") );
	Script::CStruct* pCompletedGaps = new Script::CStruct();
	mp_params->AddStructure( CRCD(0x735af2dd,"completedGaps"), pCompletedGaps );
	delete pCompletedGaps;

	mp_params->AddInteger( CRCD(0xf1e59083,"num_gaps_found"), 0 );
	mp_params->AddInteger( CRCD(0x129daa10,"number_collected"), 0 );

	int num_gaps_to_find;
	mp_params->GetInteger( CRCD(0xe75c4bbf,"num_gaps_to_find"), &num_gaps_to_find, Script::ASSERT );
	Dbg_MsgAssert( num_gaps_to_find > 0, ( "find gaps goal activated but num_gaps_to_find <= 0!" ) );

	return CGoal::Activate();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CFindGapsGoal::Deactivate( bool force, bool affect_tree )
{
	return CGoal::Deactivate();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CFindGapsGoal::CheckGaps()
{
	// printf("CFindGapsGoal::CheckGaps\n");
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = skate_mod->GetLocalSkater();

	Script::CStruct* pCompletedGaps;
	mp_params->GetStructure( CRCD(0x735af2dd,"completedGaps"), &pCompletedGaps, Script::ASSERT );

	int num_gaps_found;
	mp_params->GetInteger( CRCD(0xf1e59083,"num_gaps_found"), &num_gaps_found, Script::ASSERT );

	Mdl::Score* pScore = pSkater->GetScoreObject();
	int numTricks = pScore->GetCurrentTrickCount();
	bool found_gap = false;
	for ( int i = 0; i < numTricks; i++ )
	{
		if ( pScore->TrickIsGap( i ) )
		{
			uint32 gap_name = pScore->GetTrickId( i );
			if ( !pCompletedGaps->ContainsComponentNamed( gap_name ) )
			{
				pCompletedGaps->AddInteger( gap_name, 1 );
				num_gaps_found++;
				found_gap = true;
			}
		}
	}
	mp_params->AddInteger( CRCD(0xf1e59083,"num_gaps_found"), num_gaps_found );
	mp_params->AddInteger( CRCD(0x129daa10,"number_collected"), num_gaps_found );

	if ( found_gap )
	{
		Script::CStruct* pScriptParams = new Script::CStruct();
		pScriptParams->AddChecksum( CRCD(0x9982e501,"goal_id"), GetGoalId() );
		Script::RunScript( CRCD(0x851aa6b8,"goal_find_gaps_found_gap"), pScriptParams );
		delete pScriptParams;
	}
}


}	// namespace Game


================================================
FILE: Code/Sk/Modules/Skate/FindGapsGoal.h
================================================
// find x gaps

#ifndef __SK_MODULES_SKATE_FINDGAPSGOAL_H__
#define __SK_MODULES_SKATE_FINDGAPSGOAL_H__

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

namespace Game
{

class CFindGapsGoal : public CGoal
{

public:
						CFindGapsGoal( Script::CStruct* pParams );
	virtual				~CFindGapsGoal();

	bool				Activate();
	bool				Deactivate( bool force = false, bool affect_tree = true );

	void				CheckGaps();
protected:
	int					m_numGapsToFind;
};

}	// namespace game

#endif



================================================
FILE: Code/Sk/Modules/Skate/GameFlow.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Skate													**
**																			**
**	File name:		Skate/GameFlow.cpp										**
**																			**
**	Created by:		04/27/01	-	gj										**
**																			**
**	Description:	Defines various game flows								**
**																			**
*****************************************************************************/

//
/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Mdl
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGameFlow::CGameFlow( void )
{
	mp_gameFlowScript = NULL;
	mp_tester_script = NULL;
	
	m_requestedScript = 0;
	m_requestNewScript = false;
	m_paused = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGameFlow::~CGameFlow( void )
{
	if ( mp_gameFlowScript )
		delete mp_gameFlowScript;
		
	if (mp_tester_script)
	{
		delete mp_tester_script;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGameFlow::Reset( uint32 scriptChecksum )
{
	m_requestedScript = scriptChecksum;
	m_requestNewScript = true;
	m_paused = false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameFlow::Pause( bool paused )
{
	bool old = m_paused;
	m_paused = paused;
	return old;
}

void CGameFlow::SetTesterScript(uint32 testerScript, Script::CStruct *p_params)
{
	if (!mp_tester_script)
	{
		mp_tester_script=new Script::CScript;
		#ifdef __NOPT_ASSERT__
		mp_tester_script->SetCommentString("Created in CGameFlow::SetTesterScript(...)");
		#endif
	}
		
	mp_tester_script->SetScript(testerScript, p_params);
}

// Returns true if the tester script did exist.
// This is so that the corresponding script command can return true/false, and hence
// messages can say stuff like 'test script killed' or 'no tester script present' if they want.
bool CGameFlow::KillTesterScript()
{
	if (mp_tester_script)
	{
		delete mp_tester_script;
		mp_tester_script=NULL;
		return true;
	}
		
	return false;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGameFlow::Update()
{
	if (mp_tester_script)
	{
		mp_tester_script->Update();
	}	
	

	// GJ:  This keeps us from loading a new script
	// while we're still in the middle of processing the old one
	if ( m_requestNewScript )
	{
		m_requestNewScript = false;

		if ( mp_gameFlowScript )
			delete mp_gameFlowScript;

		mp_gameFlowScript = new Script::CScript;
		mp_gameFlowScript->SetScript( m_requestedScript );
		#ifdef __NOPT_ASSERT__
		mp_gameFlowScript->SetCommentString("Created in CGameFlow::Update()");
		#endif
		
#ifdef __USER_GARY__
		printf("Resetting to script %s %08x!\n", Script::FindChecksumName( m_requestedScript ), (int)mp_gameFlowScript);
#endif
	}

	// if paused, then don't continue the script
	if ( m_paused )
		return;

	if ( mp_gameFlowScript )
	{
		if ( mp_gameFlowScript->Update() == Script::ESCRIPTRETURNVAL_FINISHED )
		{
			delete mp_gameFlowScript;
			mp_gameFlowScript = NULL;
		}

	}
}

} // namespace Game



================================================
FILE: Code/Sk/Modules/Skate/GameFlow.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Skate													**
**																			**
**	File name:		Skate/GameFlow.h										**
**																			**
**	Created by:		04/27/01	-	gj										**
**																			**
**	Description:	Defines various game flows								**
**																			**
*****************************************************************************/

#ifndef __MODULES_SKATE_GAMEFLOW_H
#define __MODULES_SKATE_GAMEFLOW_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/


#ifndef __GEL_OBJECT_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mdl
{
 


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class  CGameFlow  : public Spt::Class
{
	

	public:
		CGameFlow();
		virtual						~CGameFlow();
		void						Reset( uint32 script_checksum );
		void						Update();
		bool						Pause( bool paused );

		void						SetTesterScript(uint32 testerScript, Script::CStruct *p_params);
		bool						KillTesterScript();

	protected:
		bool						m_paused;
		Script::CScript*			mp_gameFlowScript;
		bool						m_requestNewScript;
		uint32						m_requestedScript;
		
		Script::CScript*			mp_tester_script;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**						Inline Functions									**
*****************************************************************************/


} // namespace Mdl

#endif // __MODULES_SKATE_GAMEFLOW_H

================================================
FILE: Code/Sk/Modules/Skate/GameMode.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Skate													**
**																			**
**	File name:		Skate/GameMode.h										**
**																			**
**	Created by:		02/07/01	-	gj										**
**																			**
**	Description:	Defines various game modes								**
**																			**
*****************************************************************************/

// start autoduck documentation
// @DOC gamemode
// @module gamemode | None
// @subindex Scripting Database
// @index script | gamemode

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 		   // get player id
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Game
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGameMode::clear_victorycondition_list()
{
	

	Lst::Node *pNode = m_VictoryConditionList.GetNext();
	while(pNode)
	{
		Lst::Node *pNext = pNode->GetNext();
		pNode->Remove();
		delete pNode->GetData();
		delete pNode;
		pNode = pNext;
	}

	Dbg_Assert( m_VictoryConditionList.IsEmpty() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGameMode::build_victorycondition_list()
{
	

	clear_victorycondition_list();

	Script::CArray* pArray;
	if ( !m_ModeDefinition.GetArray( CRCD(0x94a24c5e,"victory_conditions"), &pArray ) )
	{
		// no victory conditions found
		return;
	}

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

	// loop through the victory conditions
	for ( uint32 i = 0; i < pArray->GetSize(); i++ )
	{
		Script::CScriptStructure* pStructure = pArray->GetStructure( i );
		Dbg_Assert( pStructure );

		CVictoryCondition* pVictoryCondition = CreateVictoryConditionInstance( pStructure );
		Dbg_Assert( pVictoryCondition );
		m_VictoryConditionList.AddToTail( new Lst::Node( pVictoryCondition ) );
	}

	Mem::Manager::sHandle().PopContext();
	// TODO: Scan for combinations that don't make sense
	// (i.e. highest score AND best combo)
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::remove_component_if_exists( const char* p_component_name, Script::CScriptStructure* p_params )
{
	if ( p_params->ContainsComponentNamed( p_component_name ) )
	{
		m_ModeDefinition.RemoveComponent( Script::GenerateCRC( p_component_name ) );
		return true;
	}
	
	return false;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGameMode::CGameMode()
{
	

	Reset();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGameMode::~CGameMode()
{
	

	clear_victorycondition_list();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Tmr::Time CGameMode::GetTimeLeft()
{

	CGoalManager* pGoalManager = Game::GetGoalManager();
	uint32	nameChecksum = ConvertNetNameChecksum(GetNameChecksum());
	CGoal* pGoal = pGoalManager->GetGoal(nameChecksum,false);

	if( !pGoal )
	{
		return 0;
	}

	return pGoal->GetTimeLeft();
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CGameMode::SetTimeLeft( Tmr::Time time_left )
{
	CGoalManager* pGoalManager = Game::GetGoalManager();
	uint32	nameChecksum = ConvertNetNameChecksum(GetNameChecksum());
	CGoal* pGoal = pGoalManager->GetGoal(nameChecksum,false);

	if( pGoal )
	{
		pGoal->SetTimer( time_left );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGameMode::EndConditionsMet()
{
	// this checks whether time has run out
	if ( ShouldUseClock() )
	{
		CGoalManager* pGoalManager = Game::GetGoalManager();

		if( pGoalManager->GetNumActiveGoals( true ) > 0 && pGoalManager->GetGoalTime() <= 0 )
		{
			return true;
		}
	}
	
	uint32 i;
	int num_terminals = 0;
	int num_completed_terminals = 0;

	// TODO:  Allow boolean conditions (&'s and |'s)

	// Look through the victory conditions also
	for ( i = 0; i < m_VictoryConditionList.CountItems(); i++ )
	{
		Lst::Node *pNode = m_VictoryConditionList.GetItem( i );

		if (pNode->GetData()->IsTerminal())
		{
			num_terminals++;
			
			if ( pNode->GetData()->ConditionComplete() )
			{
				num_completed_terminals++;
			}
		}
	}

	// If all of the terminal ones have been met,
	// then we've also met our end conditions
	if ( num_terminals && ( num_completed_terminals == num_terminals ) )
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::ShouldUseClock()
{
	int time_limit;
	
	if(	( GetNameChecksum() == CRCD(0x5d32129c,"king")) ||
		( GetNameChecksum() == CRCD(0x6ef8fda0,"netking")) ||
		( GetNameChecksum() == CRCD(0xf135ecb6,"scorechallenge")) ||
		( GetNameChecksum() == CRCD(0x1498240a,"netscorechallenge")) ||
		// K: Added check for creategoals to fix bug where end of run triggered straight away on
		// entering a level in creategoals mode.
		( GetNameChecksum() == CRCD(0xe45f0ca2,"creategoals")) ||
		( GetNameChecksum() == CRCD(0x3d6d444f,"firefight")) ||
		( GetNameChecksum() == CRCD(0xbff33600,"netfirefight")))
	{
		return false;
	}

	m_ModeDefinition.GetInteger( CRCD(0x8d38a280,"default_time_limit"), &time_limit, true );
	return ( time_limit != 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::ShouldStopAtZero()
{
	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	
	if( skate_mod->IsMultiplayerGame() == false )
	{
		return false;
	}

	if( ShouldUseClock())
	{
		int should_stop;

		m_ModeDefinition.GetInteger( CRCD(0x48e748b5,"stop_at_zero"), &should_stop, true );
		return ( should_stop == 1 );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGameMode::ShouldAccumulateScore()
{
	int should_accumulate_score;
	m_ModeDefinition.GetInteger( CRCD(0xc1f5864b,"accumulate_score"), &should_accumulate_score, true );
	return should_accumulate_score;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGameMode::ShouldTrackBestCombo()
{
	int should_track;
	if( m_ModeDefinition.GetInteger( CRCD(0xd57c16ba,"track_best_combo"), &should_track, false ) == false )
	{
		return false;
	}

	return should_track;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void
CGameMode::SetScoreAccumulation( bool should_accumulate )
{
	m_ModeDefinition.AddInteger( CRCD(0xc1f5864b,"accumulate_score"), should_accumulate );
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGameMode::SetScoreFrozen( bool is_frozen )
{
	m_ModeDefinition.AddInteger( CRCD(0x78fb9f04,"score_frozen"), is_frozen );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void
CGameMode::SetScoreDegradation( bool should_degrade )
{
	m_ModeDefinition.AddInteger( CRCD(0xca37e639,"degrade_score"), should_degrade );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void
CGameMode::SetNumTeams( int num_teams )
{
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();

	m_ModeDefinition.AddInteger( CRCD(0x2ebe6cd1,"num_teams"), num_teams );
	if( GetNameChecksum() == CRCD(0x1c471c60,"netlobby") )
	{
		gamenet_man->CreateTeamFlags( num_teams );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::ScoreFrozen()
{
	int score_is_frozen;
	m_ModeDefinition.GetInteger( CRCD(0x78fb9f04,"score_frozen"), &score_is_frozen, true );
	return ( score_is_frozen != 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGameMode::ShouldTrackTrickScore()
{
	int should_track_trick_score;
	m_ModeDefinition.GetInteger( CRCD(0x47f763ff,"track_trick_score"), &should_track_trick_score, true );
	return should_track_trick_score;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGameMode::ShouldDegradeScore()
{
	int should_degrade_score;
	m_ModeDefinition.GetInteger( CRCD(0xca37e639,"degrade_score"), &should_degrade_score, true );
	return should_degrade_score;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::ShouldRunIntroCamera()
{
	int should_run_intro_camera;
	if (Config::CD())
	{
		m_ModeDefinition.GetInteger( CRCD(0xe2355b5e,"should_run_intro_camera"), &should_run_intro_camera, true );
	}
	else
	{
		m_ModeDefinition.GetInteger( CRCD(0xea0d7454,"should_run_intro_camera_noncd"), &should_run_intro_camera, true );
	}	
	return should_run_intro_camera;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::ShouldShowRankingScreen()
{
	int should_show_ranking_screen;
	m_ModeDefinition.GetInteger( CRCD(0xf18020f1,"show_ranking_screen"), &should_show_ranking_screen, true );
	return should_show_ranking_screen;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGameMode::IsTimeLimitConfigurable()
{
	
	
	uint32 time_limit_type;
	m_ModeDefinition.GetChecksum( CRCD(0x7d5e4bc1,"time_limit_type"), &time_limit_type, true );
	Dbg_MsgAssert( time_limit_type == CRCD(0x2b75d083,"config") || time_limit_type == CRCD(0x613631cd,"fixed"),( "Unrecognized time limit type" ));
	return ( time_limit_type == CRCD(0x2b75d083,"config") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGameMode::IsTeamGame()
{
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
	return ( gamenet_man->InNetGame() && ( NumTeams() > 0 ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int
CGameMode::NumTeams()
{
	int num_teams;
	m_ModeDefinition.GetInteger( CRCD( 0x2ebe6cd1, "num_teams" ), &num_teams, true );
	return num_teams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGameMode::ShouldTimerBeep()
{
	int beeps;
	m_ModeDefinition.GetInteger( CRCD(0x35c8073,"timer_beeps"), &beeps, true );
	return beeps;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32
CGameMode::GetTimeLimit()
{
	
	int time_limit;
	m_ModeDefinition.GetInteger( CRCD(0x8d38a280,"default_time_limit"), &time_limit, true );
	return (uint32)time_limit;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32		CGameMode::GetTargetScore( void )
{
	uint32 i;
	// Look through the victory conditions also
	for ( i = 0; i < m_VictoryConditionList.CountItems(); i++ )
	{
		Lst::Node *pNode = m_VictoryConditionList.GetItem( i );

		CScoreReachedVictoryCondition* condition;

		condition = static_cast  (pNode->GetData());
		if( condition )
		{
			return condition->GetTargetScore();
		}
	}

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CGameMode::GetInitialNumberOfPlayers()
{
	

	int num_players;
	m_ModeDefinition.GetInteger( CRCD(0x6dc3d6f1,"initial_players"), &num_players, true );
	return num_players;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CGameMode::SetMaximumNumberOfPlayers( uint32 num_players )
{
	m_ModeDefinition.AddInteger( CRCD(0x4d42e61d,"max_players"), num_players );
	Dbg_Printf( "****** SETTING MAX PLAYERS TO %d\n", num_players );
//	DumpUnwindStack( 20, 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CGameMode::GetMaximumNumberOfPlayers()
{
	int num_players;
	m_ModeDefinition.GetInteger( CRCD(0x4d42e61d,"max_players"), &num_players, true );
	return num_players;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::ScreenMode CGameMode::GetScreenMode()
{
	

	uint32 checksum;

	m_ModeDefinition.GetChecksum( CRCD(0x9bab2eae,"screenmode"), &checksum, true );
	switch (checksum)
	{
		case 0x3558d8e6: // single
			return Nx::vONE_CAM;
			break;
		case 0x9d65d0e7: // horse
			return Nx::vHORSE1;
			break;
		case 0x06ab02f2: // splitscreen
		{
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			Prefs::Preferences* pPreferences;

			pPreferences = skate_mod->GetSplitScreenPreferences();
			Dbg_Assert(pPreferences);

			Script::CScriptStructure* pStructure = pPreferences->GetPreference( CRCD(0x6f82c71e,"viewport_type") );
			Dbg_Assert(pStructure);

			uint32 checksum;
			pStructure->GetChecksum( CRCD(0x21902065,"checksum"), &checksum, true );
			if ( checksum == CRCD(0x92f5b4cb,"viewport_type_vertical") )
			{
				return Nx::vSPLIT_V;
			}
			else if ( checksum == CRCD(0x7c442511,"viewport_type_horizontal") )
			{
				return Nx::vSPLIT_H;
			}
			else
			{
				Dbg_Assert( 0 );
				return Nx::vSPLIT_V;
			}
		}
		break;
		default:
			Dbg_Assert( 0 );
			return (Nx::ScreenMode) 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::IsFrontEnd()
{
	

	int is_frontend;
	m_ModeDefinition.GetInteger( CRCD( 0xa6479767, "is_frontend" ), &is_frontend, true );
	return is_frontend;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::IsParkEditor()
{
	

	int is_parkeditor;
	m_ModeDefinition.GetInteger( CRCD(0x66d9ddb9,"is_parkeditor"), &is_parkeditor, true );
	return is_parkeditor;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	CGameMode::GetFireballLevel()
{
	int level;

	level = 3;
	m_ModeDefinition.GetInteger( CRCD(0xce87e4e3,"fireball_level"), &level );
	
	return level;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::IsTrue( const char* pFieldName )
{
	

	int field_value;
	m_ModeDefinition.GetInteger( pFieldName, &field_value, true );
	return field_value;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::IsTrue( uint32 checksum )
{
	

	int field_value;
	m_ModeDefinition.GetInteger( checksum, &field_value, true );
	return field_value;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Returns the checksum of the name of the game mode definition structure
// these structures are defined in gamemode.q, an example would be "graffiti"
// however, you should NOT use these names to check for things to do
// as there might be mutliple instance of similar modes (like, graffiti and netgraffiti)
// so you should use the fields within the structure, 
// use the IsTrue methods above, like IsTrue("is_graffiti");  

uint32 CGameMode::GetNameChecksum()
{
	

	uint32 checksum = 0;
	m_ModeDefinition.GetChecksum(CRCD(0xa1dc81f9,"name"), &checksum);
	return checksum;
}

					 
// if the checksum is the name of a net game, then return the name of the regular equivalent					 
// used by the time functions, wehre we need to know what type of game it is
// and not if it's net or not
uint32 CGameMode::ConvertNetNameChecksum(uint32 nameChecksum)
{

	uint32 checksum = nameChecksum;
	
	if( nameChecksum == CRCD(0x30c2ffa3,"nettrickattack"))
	{
		checksum =  CRCD( 0xf9283ee7,"trickattack" );
	}
	else if( nameChecksum == CRCD( 0x5e2ea50c, "netgraffiti" ))
	{
		checksum = CRCD(0xc8a82b5a, "graffiti");
	}
	else if( nameChecksum == CRCD( 0xc50affd0, "netcombomambo" ))
	{
		checksum = CRCD(0x23eb3da3, "combomambo");
	}
	else if ( nameChecksum == CRCD( 0xf9d5d933, "netslap" ))
	{
		checksum = CRCD( 0xca1f360f, "slap" );
	}
	else if ( nameChecksum == CRCD(0x6ef8fda0, "netking" ) )
	{
		checksum = CRCD( 0x5d32129c,"king" );
	}
	else if( nameChecksum == CRCD(0xbff33600,"netfirefight") )
	{
		checksum = CRCD(0x3d6d444f,"firefight");
	}
	else if( nameChecksum == CRCD(0x6c5ff266,"netctf") )
	{
		checksum = CRCD(0xa5ad2b0b,"ctf");
	}
	return checksum;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int compFunc( const void *arg1, const void *arg2 )
{
	SRanking* pRanking1 = (SRanking*)arg1;
	SRanking* pRanking2 = (SRanking*)arg2;

	int score1 = pRanking1->pScore->GetTotalScore();
	int score2 = pRanking2->pScore->GetTotalScore();

	if ( score1 == score2 )
	{
		return 0;
	}
	else if ( score1 < score2 )
	{
		return 1;
	}
	else
	{
		return -1;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGameMode::CalculateRankings( SRanking* p_player_rankings )
{
	

	Dbg_AssertPtr( p_player_rankings );

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	for ( uint32 skater_num = 0; skater_num < skate_mod->GetNumSkaters(); skater_num++ )
	{
		Obj::CSkater* pSkater = skate_mod->GetSkater(skater_num);
		Dbg_AssertPtr( pSkater );
		
		Obj::CSkaterScoreComponent* pSkaterScoreComponent = GetSkaterScoreComponentFromObject(pSkater);
		Dbg_Assert( pSkaterScoreComponent );
		
		p_player_rankings[skater_num].obj_id = pSkater->GetID();
		p_player_rankings[skater_num].pScore = pSkaterScoreComponent->GetScore();
		p_player_rankings[skater_num].ranking = 0;
	}

	// sort them by score
	// TODO:  make the sorting function variable depending on the game mode
	qsort( &p_player_rankings[0], skate_mod->GetNumSkaters(), sizeof(SRanking), compFunc );

	for ( uint32 i = 0; i < skate_mod->GetNumSkaters(); i++ )
	{
		if ( (i != 0) && (p_player_rankings[i].pScore->GetTotalScore() == p_player_rankings[i-1].pScore->GetTotalScore()) )
		{
			p_player_rankings[i].ranking = p_player_rankings[i-1].ranking;
		}
		else
		{
			p_player_rankings[i].ranking = i + 1;
		}

		printf("ranking = skater=%d score=%d ranking=%d\n", p_player_rankings[i].obj_id, p_player_rankings[i].pScore->GetTotalScore(), p_player_rankings[i].ranking );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::IsWinner( uint32 skater_num )
{
	

	int completed_count[Mdl::Skate::vMAX_SKATERS];
	uint32 j;

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	for ( j = 0; j < skate_mod->GetNumSkaters(); j++ )
	{
		completed_count[j] = 0;
		
		for ( uint32 i = 0; i < m_VictoryConditionList.CountItems(); i++ )
		{
			Lst::Node *pNode = m_VictoryConditionList.GetItem( i );
			
			if ( pNode->GetData()->IsWinner( j ) )
			{
				completed_count[j]++;
			}
		}
	}

	for ( j = 0; j < skate_mod->GetNumSkaters(); j++ )
	{
		if ( skater_num != j && completed_count[skater_num] < completed_count[j] )
		{
			return false;
		}
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::Reset()
{
	clear_victorycondition_list();
	
	return LoadGameType( CRCD(0x34861a16,"freeskate") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::LoadGameType( uint32 gameTypeChecksum )
{
	GameNet::Manager* gamenet_man;
	int max_players;
	
	max_players = Mdl::Skate::vMAX_SKATERS;
	gamenet_man = GameNet::Manager::Instance();
	if( gamenet_man && gamenet_man->InNetGame())
	{
		max_players = GetMaximumNumberOfPlayers();
	}

	// Reset the current game mode first
	m_ModeDefinition.Clear();

	Script::CArray* pArray = Script::GetArray( CRCD(0xca5f7ffb,"mode_info"), Script::ASSERT );

	for ( uint32 i = 0; i < pArray->GetSize(); i++ )
	{
		Script::CScriptStructure* pStructure = pArray->GetStructure( i );
		Dbg_Assert( pStructure );

		uint32 checksum;
		pStructure->GetChecksum( CRCD(0x21902065,"checksum"), &checksum, true );
		
		if ( checksum == gameTypeChecksum )
		{

			const char* definition;
			pStructure->GetText( CRCD(0x97cfd027,"definition"), &definition, true );

			Script::CScriptStructure* pRefersToStructure = Script::GetStructure( definition, Script::ASSERT );
			m_ModeDefinition.AppendStructure( pRefersToStructure );

			// clears the existing lists, as we're about to rebuild them
			clear_victorycondition_list();
			
			// rebuilds the victory condition list based on the current game mode
			build_victorycondition_list();

			if( gamenet_man && gamenet_man->InNetGame())
			{
				SetMaximumNumberOfPlayers( max_players );
			}
			return true;
		}
	}

	Dbg_MsgAssert( 0,( "Unrecognized game type %08x %s", gameTypeChecksum, Script::FindChecksumName( gameTypeChecksum ) ));
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGameMode::OverrideOptions( Script::CScriptStructure *pParams )
{
	// first we should get rid of any named components that are duplicates
	// the trick may be not to use any named components?
	remove_component_if_exists( "time_limit_type", pParams );
	remove_component_if_exists( "victory_condition_type", pParams );
	remove_component_if_exists( "screenmode", pParams );
	
	m_ModeDefinition.AppendStructure( pParams );
	
	// clears the existing lists, as we're about to rebuild them
	clear_victorycondition_list();

	// rebuilds the victory condition list based on the current game mode
	build_victorycondition_list();

//#ifdef __USER_GARY__
#if 1
	Script::PrintContents( &m_ModeDefinition );
#endif
	

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | OverrideGameModeOptions | 
bool ScriptOverrideGameModeOptions( Script::CScriptStructure *pParams, Script::CScript *pScript )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	CGameMode* pGameMode = skate_mod->GetGameMode();
	return pGameMode->OverrideOptions( pParams );
}

} // namespace Game



================================================
FILE: Code/Sk/Modules/Skate/GameMode.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Skate													**
**																			**
**	File name:		Skate/GameMode.h										**
**																			**
**	Created by:		02/07/01	-	gj										**
**																			**
**	Description:	Defines various game modes								**
**																			**
*****************************************************************************/

#ifndef __MODULES_SKATE_GAMEMODE_H
#define __MODULES_SKATE_GAMEMODE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#include 
#include 
#include 

#ifndef	__SCRIPTING_UTILS_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mdl
{
	// forward declaration
	class Score;
}

namespace Game
{
 


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

struct SRanking
{
	int				obj_id;
	int				ranking;
	Mdl::Score*		pScore;
};

class  CGameMode  : public Spt::Class
{
	

public:
	enum
	{
		vGAMEMODE_NETLOBBY = 0x1c471c60,		// "netlobby"
	};
			
public:
	CGameMode();
	virtual						~CGameMode();

	// clears the game mode to default, which is effectively "freeskate"
	bool						Reset();

	// loads a game mode, as defined in a script
	bool						LoadGameType( uint32 game_type );
	bool						OverrideOptions( Script::CScriptStructure *pParams );
	
public:
	void						CalculateRankings( SRanking* p_player_rankings );
	void						SetScoreFrozen( bool is_frozen );
	bool						ScoreFrozen();
	void						SetScoreAccumulation( bool should_accumulate );
	void						SetScoreDegradation( bool should_degrade );
	void						SetNumTeams( int num_teams );
	bool						ShouldAccumulateScore();
	bool						ShouldTrackBestCombo();
	bool						ShouldTrackTrickScore();
	bool						ShouldDegradeScore();
	bool						ShouldUseClock();
	bool						ShouldStopAtZero();
	bool						ShouldRunIntroCamera();
	bool						ShouldShowRankingScreen();
	bool						IsTeamGame();
	int							NumTeams();
	bool						IsTimeLimitConfigurable();
	bool						ShouldTimerBeep();
	uint32						GetTimeLimit();
	uint32						GetTargetScore();
	uint32						GetInitialNumberOfPlayers();
	uint32						GetMaximumNumberOfPlayers();
	void						SetMaximumNumberOfPlayers( uint32 num_players );
	bool						EndConditionsMet();
	Tmr::Time					GetTimeLeft();
	void						SetTimeLeft( Tmr::Time time_left );
	bool						IsWinner( uint32 skater_num );
	Nx::ScreenMode				GetScreenMode();
	bool						IsFrontEnd();
	bool						IsParkEditor();

	uint32 						GetNameChecksum();
	uint32 						ConvertNetNameChecksum(uint32 nameChecksum);
	
	int							GetFireballLevel();

	// for generic tests, like "is career mode"
	bool						IsTrue( const char* pFieldName );
	bool						IsTrue( uint32 checksum );
	
	void						PrintContents()
	{
#ifdef __NOPT_ASSERT__
		Script::PrintContents(&m_ModeDefinition);
#endif	
	}

protected:
	void						clear_victorycondition_list();
	void						build_victorycondition_list();
	bool						remove_component_if_exists( const char* p_component_name, Script::CScriptStructure* p_params );
	
protected:
	Script::CScriptStructure	m_ModeDefinition;
	
	// for determining who is the winner, as well
	// as ending the game before time has run out
	Lst::Head	m_VictoryConditionList;
};


/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

bool ScriptOverrideGameModeOptions( Script::CScriptStructure *pParams, Script::CScript *pScript );

/*****************************************************************************
**						Inline Functions									**
*****************************************************************************/


} // namespace Game

#endif // __MODULES_SKATE_GAMEMODE_H

================================================
FILE: Code/Sk/Modules/Skate/Goal.cpp
================================================
// start autoduck documentation
// @DOC goal
// @module goal | None
// @subindex Scripting Database
// @index script | goal
							   
#include 

#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
								
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
					   
#include 
#include 
#include 

#include 
#include 


namespace Front
{
    extern void HideTimeTHPS4();
}

namespace Game
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGoalLink::CGoalLink()
{
	m_relative = 0;
	mp_next = NULL;
	m_beaten = false;
}

CGoalLink::~CGoalLink()
{
	// nothing yet
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGoal::CGoal( Script::CStruct* pParams ) : Lst::Node( this )
{
	mp_children = new Game::CGoalLink();
	mp_parents = new Game::CGoalLink();
	
	m_active = false;
    m_hasSeen = false;
    m_paused = false;
    m_unlimitedTime = true;
    m_isbeat = false;
	m_netIsBeat = false;
	m_beatenFlags = 0;
	m_teamBeatenFlags = 0;
	m_isselected = false;
	m_isInitialized = false;

	m_numTimesLost = 0;
	m_numTimesWon = 0;
	m_numTimesStarted = 0;

	m_deactivateOnExpire = true;

    m_timeLeft  = 0;

	m_chapter = m_stage = -1;

	mp_params = new Script::CStruct;
	mp_params->AppendStructure( pParams );

	mp_goalPed = new Game::CGoalPed( mp_params );

	mp_goalFlags = new Script::CStruct;
    
	m_numflags = 0;
    m_flagsSet = 0;

	m_elapsedTime = 0;
	m_elapsedTimeRecord = vINT32_MAX;
	m_winScoreRecord = 0;

	m_endRunType = vGOALENDOFRUN;

    // set the level number
    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
    Obj::CSkaterCareer* pCareer = skate_mod->GetCareer();
    m_levelNum = pCareer->GetLevel();
    
	CreateGoalFlags( pParams );

	if ( pParams->ContainsFlag( CRCD( 0x4ab5f14, "single_timer" ) ) )
	{
		// Dbg_MsgAssert( mp_children->m_relative, ( "single_timer flag used on a goal without children" ) );
		m_inherited_flags |= GOAL_INHERITED_FLAGS_ONE_TIMER;
	}

	if ( pParams->ContainsFlag( CRCD( 0xc6b4258f, "retry_stage" ) ) )
	{
		m_inherited_flags |= GOAL_INHERITED_FLAGS_RETRY_STAGE;
	}
    
    // TODO: make goal lock stuff work right
	if ( pParams->ContainsFlag( vUNLOCKED_BY_ANOTHER ) )
    {
        m_isLocked = true;
    }
    else if ( pParams->ContainsFlag( vPRO_GOAL ) )
    {
        m_isLocked = true;
    }
/*	else if ( pParams->ContainsFlag( vPRO_CHALLENGE ) )
	{
		m_isLocked = true;
	}
*/
    else
    {
        m_isLocked = false;
    }
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::Init()
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());

	// overwrite any params with difficulty level params
	AppendDifficultyLevelParams();

	// make sure there's a goal description so that the intro cam anims can
	// be killed and Gary will be happy
	Dbg_MsgAssert( mp_params->ContainsComponentNamed( CRCD(0xc5d7e6b,"goal_description") ), ( "No goal description defined for %s\n", Script::FindChecksumName( GetGoalId() ) ) );
	
	RunCallbackScript( vINIT );
	ReplaceTrickText();
	m_isInitialized = true;
	
	// Dan, TT12965.  If a server switched between time limit and capture limit ctf games, m_unlimitedTime was not being reset to true.  Thus, ShouldExpire
	// was returning true during the capture limit game, if the time on the previous time limit game had been allowed to run down.
	int unlimited_time = 0;
	mp_params->GetInteger( CRCD(0xf0e712d2,"unlimited_time"), &unlimited_time );
	if ( unlimited_time )
	{
		m_unlimitedTime = true;
	}
	
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::AddParent( uint32 id )
{
	// printf("adding parents\n");
	if ( mp_parents->m_relative == 0 )
	{
		mp_parents->m_relative = id;
	}
	else
	{
		Game::CGoalLink* p_parent = new Game::CGoalLink();
		p_parent->m_relative = id;
		p_parent->mp_next = mp_parents;
		mp_parents = p_parent;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::CreateGoalFlags( Script::CStruct* pParams )
{
    // set any goal flags specified in the params
    Script::CArray *pTempArray;
    mp_goalFlags->Clear();
	m_numflags = 0;
	if ( pParams->GetArray( CRCD(0xcc3c4cc4,"goal_flags"), &pTempArray ) )
    {
        for ( uint32 c = 0; c < pTempArray->GetSize(); c++)
        {
            mp_goalFlags->AddInteger( pTempArray->GetNameChecksum( c ), 0 );
            m_numflags++;
        }
    }
	mp_params->AddInteger( CRCD(0xb3790f33,"num_flags"), m_numflags );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::SetInheritableFlags( int recurse_level )
{
	// printf("SetInheritableFlags called on %s\n", Script::FindChecksumName( GetGoalId() ) );
	Dbg_MsgAssert( recurse_level < 100, ( "SetInheritableFlags recursed too much" ) );
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	CGoalLink* p_child = mp_children;
	while ( p_child && p_child->m_relative != 0 )
	{
		CGoal *pGoal = pGoalManager->GetGoal( p_child->m_relative );
		if ( pGoal )
		{
			pGoal->m_inherited_flags |= m_inherited_flags;
			pGoal->SetInheritableFlags( recurse_level + 1 );
		}
		p_child = p_child->mp_next;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::AddChild( uint32 id )
{
	// printf("adding child\n");	
	// any current children?
	if ( mp_children->m_relative == 0 )
	{
		mp_children->m_relative = id;
	}
	else
	{		
		// make sure this child isn't already in the list
		bool found = false;
		Game::CGoalLink* p_test = mp_children;
		while ( p_test && p_test->m_relative != 0 )
		{
			if ( p_test->m_relative == id )
			{
				found = true;
				break;
			}
			p_test = p_test->mp_next;
		}
		
		if ( found )
		{
			Dbg_MsgAssert( 0, ( "AddChild failed.  %s already a child of %s\n", Script::FindChecksumName( id ), Script::FindChecksumName( GetGoalId() ) ) );
		}
		else
		{
			// add new child to head of list
			Game::CGoalLink* p_child = new Game::CGoalLink();
			p_child->m_relative = id;
			p_child->mp_next = mp_children;
			mp_children = p_child;
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::ResetGoalLinks()
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	CGoalLink* p_link = mp_children;
	while ( p_link && p_link->m_relative != 0 )
	{
		p_link->m_beaten = false;
		CGoal* pGoal = pGoalManager->GetGoal( p_link->m_relative );
		if ( pGoal )
		{
			// recurse!
			pGoal->ResetGoalLinks();
		}
		
		p_link = p_link->mp_next;
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::DeleteLinks( CGoalLink* p_list )
{
	CGoalLink *p_a, *p_b;
	for ( p_a = p_list; p_a; p_a = p_b )
	{
		p_b = p_a->mp_next;
		delete p_a;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::Uninit( bool propogate, int recurse_level )
{
	Dbg_MsgAssert( recurse_level < 20, ( "CGoal::Uninit recursed too deep" ) );
	
	// uint32 trigger_obj_id = 0;

	if ( IsActive() )
	{
		Deactivate( true );
	}

	RunCallbackScript( vUNINIT );
	
	m_isInitialized = false;
	
/*	
	Script::RunScript( Script::GenerateCRC( "goal_kill_proset_geom" ), mp_params );
	mp_params->GetChecksum( CRCD( 0x2d7e03d, "trigger_obj_id" ), &trigger_obj_id );
	if ( trigger_obj_id )
	{
		Obj::CMovingObject* p_pos_obj = NULL;
		// p_pos_obj = Obj::CMovingObject::m_hash_table.GetItem( trigger_obj_id );
		p_pos_obj = (Obj::CMovingObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID( trigger_obj_id );
		if ( p_pos_obj )
		{
			delete p_pos_obj;
		}
	}
*/
	if ( propogate )
	{
		CGoalManager* pGoalManager = GetGoalManager();
		Dbg_Assert( pGoalManager );

		CGoalLink* pGoalLink = mp_children;
		while ( pGoalLink && pGoalLink->m_relative != 0 )
		{
			CGoal* pChildGoal = pGoalManager->GetGoal( pGoalLink->m_relative );
			Dbg_Assert( pChildGoal );
			pChildGoal->Uninit( propogate, recurse_level + 1 );
			pGoalLink = pGoalLink->mp_next;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CGoal::~CGoal()
{
	// does this really need to be run?
	// (you may not necessarily want to go through the
	// same destructor logic if we're clearing out the
	// goals at the end of the level)
    
    RunCallbackScript( vDESTROY );
	
	delete mp_goalPed;

	Dbg_Assert( mp_params );
	delete mp_params;    
	delete mp_goalFlags;

	// delete all children and parent trees
	DeleteLinks( mp_children );
	DeleteLinks( mp_parents );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Script::CStruct* CGoal::GetParams()
{
	return mp_params;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetTimer()
{
	int unlimited_time = 0;

	mp_params->GetInteger( CRCD(0xf0e712d2,"unlimited_time"), &unlimited_time );

	// check for unlimited time
	if ( unlimited_time == 1 )
	{
		return;
	}
	
	// don't set default time for minigames.
	if ( IsMinigame() )
	{
		if ( !mp_params->ContainsComponentNamed( CRCD(0x906b67ba,"time") ) )
			return;
	}

	// default time limit of 2 minutes
    m_timeLeft = 120;
	int time;
	if ( mp_params->GetInteger( CRCD(0x906b67ba,"time"), &time, Script::NO_ASSERT ) )
	{
		m_timeLeft = (Tmr::Time)time;
	}
/*	else
	{
		Script::CArray* pTimeLimits;
		if ( mp_params->GetArray( CRCD(0x906b67ba,"time"), &pTimeLimits, Script::NO_ASSERT ) )
		{
			CGoalManager* pGoalManager = GetGoalManager();
			Dbg_Assert( pGoalManager );
			uint32 difficulty_level = pGoalManager->GetDifficultyLevel();
			Dbg_Assert( difficulty_level > 0 && difficulty_level < pTimeLimits->GetSize() );
			m_timeLeft = (Tmr::Time)( pTimeLimits->GetInteger( difficulty_level ) );
		}
	}
*/
	// convert to milliseconds
	m_timeLeft *= 1000;
    
	// bump up timer so clock starts with expected amount of time left
	m_timeLeft += 20;
	// printf("timer set to %i\n", (int)m_timeLeft);

	m_unlimitedTime = false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetTimer( int time )
{
	m_timeLeft = (Tmr::Time)time * 1000;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	CGoal::GetTimeLeft( void )
{
	return m_timeLeft;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::AddTime( int time )
{
	// bump up time by 20ms so that it displays right
	m_timeLeft += ( (Tmr::Time)time * 1000 ) + 20;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::Activate()
{
    if ( !IsActive() )
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
		// Warning! Make sure there are no 'return's between the above pushcontext and the 
		// popcontext below ...
		
		m_elapsedTime = 0;
		
		m_numTimesStarted++;

		// printf("goal is not active - now activating\n");
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		GameNet::PlayerInfo* player;
		Lst::Search< GameNet::PlayerInfo > sh;
						
		// TODO: reset just this skater's score
		for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ) )
		{
			Obj::CSkater *p_Skater = player->m_Skater;
			Dbg_Assert( p_Skater );
			
			Obj::CSkaterScoreComponent* p_SkaterScoreComponent = GetSkaterScoreComponentFromObject(p_Skater);
			Dbg_Assert( p_SkaterScoreComponent );
			
			Mdl::Score * p_Score = ( p_SkaterScoreComponent->GetScore() );
			Dbg_Assert( p_Score );
	
			// don't reset the score when starting a minigame
			if ( !IsMinigame() )
				p_Score->Reset();
		}

		// Warning! Make sure there are no 'return's between the above pushcontext and the 
		// popcontext below ...
        
		if ( mp_parents->m_relative != 0 )
		{
			CGoalManager* pGoalManager = GetGoalManager();
			Dbg_Assert( pGoalManager );

			CGoal* pParentGoal = NULL;

			// get the root node and check the timer
			CGoalLink* p_parent = mp_parents;
			while ( p_parent && p_parent->m_relative != 0 )
			{
				pParentGoal = pGoalManager->GetGoal( p_parent->m_relative );
				p_parent = pParentGoal->GetParents();
			}
			
			if ( pParentGoal )
			{
				if ( m_inherited_flags & GOAL_INHERITED_FLAGS_ONE_TIMER )
					SetTimer( pParentGoal->GetTimeLeft() / 1000.0f );
				else
					SetTimer();

				if ( pParentGoal->IsInitialized() )
				{
					pParentGoal->Uninit();
					
					// reinitialize the goal in case uninitializing the parent
					// screwed anything up.
					if ( IsInitialized() )
					{
						Uninit();
						Init();
					}
				}
			}
			else
			{
				// Dbg_Message( "Goal supposed to use a single timer but couldn't find parent" );
				SetTimer();
			}
		}
		else
		{
			SetTimer();
		}
		
		if ( !IsInitialized() )
		{
			Init();
		}
		
		m_endRunCalled = false;

		// Warning! Make sure there are no 'return's between the above pushcontext and the 
		// popcontext below ...

		mp_params->RemoveFlag( CRCD(0x7100155d,"combo_started") );

        // activate script
		RunCallbackScript( vACTIVATE );
        
		if( gamenet_man->InNetGame() == false )
		{
			m_hasSeen = true;
		}
        
		//if ( !mp_params->ContainsFlag( "quick_start" ) )
			//PauseGoal();
		SetActive();
		Mem::Manager::sHandle().PopContext();
		
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetActive()
{
	m_active = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::Deactivate( bool force, bool affect_tree )
{
	if ( IsActive() )
	{
		// special case for special trick goal...get rid of
		// the temp key_combos array
		if ( IsSpecialGoal() )
			mp_params->RemoveComponent( CRCD(0x79704516,"key_combos") );

		Front::HideTimeTHPS4();
		
		SetInactive();
        
        ResetGoalFlags();

		ResetGoalLinks();

        RunCallbackScript( vDEACTIVATE );

		if (!mp_params->ContainsFlag( CRCD( 0x38221df4, "quick_start" ) ))
		{
			// If not retrying, then remove the testing_goal flag
			// The presence of the testing_goal flag prevents the goal from being marked as
			// beaten when it is beaten during testing.
			mp_params->RemoveFlag(CRCD(0x54d3cac1,"testing_goal"));
		}	
		
		if ( !mp_params->ContainsFlag( CRCD( 0xc309cad1, "just_won_goal" ) )
			 && !mp_params->ContainsFlag( CRCD( 0x38221df4, "quick_start" ) ) )
		{
			Script::RunScript(CRCD(0x719ae783, "ready_skater_for_early_end_current_goal"), mp_params);
			
			if ( affect_tree && mp_parents && mp_parents->m_relative )
			{
				Uninit();
	
				// find parent node and reinitialize
				CGoalManager* pGoalManager = GetGoalManager();
				Dbg_Assert( pGoalManager );
				CGoalLink* p_link = mp_parents;
				CGoal* pParentGoal = NULL;
				while ( p_link && p_link->m_relative )
				{
					pParentGoal = pGoalManager->GetGoal( p_link->m_relative );
					Dbg_Assert( pParentGoal );
					p_link = pParentGoal->GetParents();
				}
				if ( pParentGoal && !pParentGoal->HasWonGoal() )
				{
					// printf("initializing goal: %s\n", Script::FindChecksumName( pParentGoal->GetGoalId() ) );
					pParentGoal->Init();
				}
			}
		}

        return true;
    }
	return false;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetInactive()
{
	m_active = false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::Win( void )
{
	mp_params->AddInteger( CRCD(0xc22a2b72,"win_record"), 0 );
	// see if it's a leaf node
	if ( mp_children->m_relative == 0 )
	{
		mp_params->AddChecksum( NONAME, CRCD( 0xc309cad1, "just_won_goal" ) );			

		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		
		m_numTimesWon++;
		
		// look for a new record if we're not in a net game
		bool set_new_record = false;
		uint32 record_type;
		if ( !gamenet_man->InNetGame() && mp_params->GetChecksum( CRCD(0x96963902,"record_type"), &record_type, Script::NO_ASSERT ) )
		{
			// printf("record type is %s\n", Script::FindChecksumName( record_type ) );
			if ( record_type == CRCD(0xcd66c8ae,"score") )
			{
				// check for custom score record
				int custom_record;
				if ( mp_params->GetInteger( CRCD(0xfc9630ab,"custom_score_record"), &custom_record, Script::NO_ASSERT ) )
				{
					if ( custom_record > m_winScoreRecord )
					{
						m_winScoreRecord = custom_record;
						mp_params->AddInteger( CRCD(0xc22a2b72,"win_record"), m_winScoreRecord );
						set_new_record = true;
					}
				}
				else
				{
					// printf("found a score record type\n");
					Mdl::Skate * skate_mod = Mdl::Skate::Instance();
					Obj::CSkater *pSkater = skate_mod->GetLocalSkater();
					Dbg_Assert( pSkater );
					
					Obj::CSkaterScoreComponent* pSkaterScoreComponent = GetSkaterScoreComponentFromObject(pSkater);
					Dbg_Assert( pSkaterScoreComponent );
		
					Mdl::Score * pScore = ( pSkaterScoreComponent->GetScore() );
					Dbg_Assert( pScore );
		
					int total_score;
					if ( mp_params->ContainsFlag( CRCD(0xc63432a7,"high_combo") ) )
						 total_score = pScore->GetLastScoreLanded();
					else
						 total_score = pScore->GetTotalScore();

					// printf("got a total_score of %i, had a previous record of %i\n", total_score, m_winScoreRecord );
					if ( total_score > m_winScoreRecord )
					{
						m_winScoreRecord = total_score;
						mp_params->AddInteger( CRCD(0xc22a2b72,"win_record"), m_winScoreRecord );
						set_new_record = true;
					}
				}
			}
			else if ( record_type == CRCD(0x906b67ba,"time") )
			{
				// printf("got a time of %i, previous record was %i\n", (int)m_elapsedTime, (int)m_elapsedTimeRecord);
				if ( m_elapsedTime < m_elapsedTimeRecord )
				{
					m_elapsedTimeRecord = m_elapsedTime;
					mp_params->AddInteger( CRCD(0xc22a2b72,"win_record"), (int)m_elapsedTimeRecord );
					set_new_record = true;
				}
			}
		}
	#ifdef __NOPT_ASSERT__
		CGoalManager* pGoalManager = GetGoalManager();
		Dbg_Assert( pGoalManager );
	#endif		// __NOPT_ASSERT__
		
		if ( !HasWonGoal() && !gamenet_man->InNetGame() )
		{
			// printf("getting rewards struct\n");
			Script::CStruct* p_reward_params = new Script::CStruct();
			GetRewardsStructure( p_reward_params, true );
			if ( set_new_record )
				p_reward_params->AddChecksum( NONAME, CRCD(0xe7b8254f,"set_new_record") );
	
			mp_params->AddStructurePointer( CRCD(0x128b3088,"reward_params"), p_reward_params );
	
			UnlockRewardGoals();
		}
		else
			mp_params->RemoveComponent( CRCD(0x128b3088,"reward_params") );
		
		// update set_new_record flag
		if ( set_new_record )
			mp_params->AddChecksum( NONAME, CRCD(0xe7b8254f,"set_new_record") );
		else
			mp_params->RemoveFlag( CRCD(0xe7b8254f,"set_new_record") );
		
		RunCallbackScript( vSUCCESS );

		bool testing_goal=mp_params->ContainsFlag(CRCD(0x54d3cac1,"testing_goal"));
		
		if ( IsActive() )
		{
			// add a flag so the deactivate script knows that we just beat the goal
			Deactivate();
		}
	
		// The testing_goal flag is set when a created goal is being test played.
		// Don't want it to be crossed off the view goals menu in that case. (TT3366)
		if (!testing_goal)
		{
			MarkBeaten();
		}

		if ( gamenet_man->InNetGame() )
		{
			Uninit();
		}
				
		mp_params->RemoveFlag( CRCD( 0xc309cad1, "just_won_goal" ) );
	
		return true;
	}
	else
	{
		// it's a link to another goal

		// TODO: we need a way of deciding which child to pick
		// for now, we'll just take the first child
		Game::CGoalLink* p_link = mp_children;

		CGoalManager* pGoalManager = GetGoalManager();
		Dbg_Assert( pGoalManager );
		Deactivate();
		Uninit();
		CGoal* pChildGoal = pGoalManager->GetGoal( p_link->m_relative );
		pChildGoal->Init();
		pGoalManager->ActivateGoal( p_link->m_relative, false );
		
		// mark the link beaten
		p_link->m_beaten = true;

		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::Lose( void )
{
	// Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	// Obj::CSkater *p_Skater = skate_mod->GetLocalSkater();
	// Dbg_Assert( p_Skater );

	// p_Skater->ClearAllExceptions();

	if ( IsActive() )
	{
		m_numTimesLost++;
		
		RunCallbackScript( vFAIL );
        
		Deactivate( true );

		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::IsExpired()
{
	return IsExpired( false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::ShouldExpire()
{
	// returns true if the goal would expire if the skater was not in a combo
	
	// Dan: this logic needs to mimic IsExpired()
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Obj::CSkater* p_skater;
	
	p_skater = skate_mod->GetLocalSkater();
	if ( p_skater == NULL )
	{
		return true;
	}
	
#ifdef __NOPT_ASSERT__
	Obj::CSkaterEndRunComponent* p_skater_endrun_component = GetSkaterEndRunComponentFromObject(p_skater);
	Dbg_Assert(p_skater_endrun_component);
#endif		// __NOPT_ASSERT__
	
	// If no players are left, the goal should expire
	if ( gamenet_man->GetNumPlayers() == 0 )
	{
		return true;
	}
	
	// If it's an unlimited game, don't expire the goal
	if ( m_unlimitedTime )
	{
		return false;
	}
	
	if ( (int)m_timeLeft <= 0 )
	{
		// Dbg_Printf( "**** Already Expired\n" );
		return true;
	}
	
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::IsExpired( bool force_end )
{
    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Obj::CSkater* p_skater;

	p_skater = skate_mod->GetLocalSkater();
	if ( p_skater == NULL )
	{
		return true;
	}
	
	Obj::CSkaterEndRunComponent* p_skater_endrun_component = GetSkaterEndRunComponentFromObject(p_skater);
	Dbg_Assert(p_skater_endrun_component);

	// If no players are left, the goal should expire
	if ( gamenet_man->GetNumPlayers() == 0 )
	{
		return true;
	}
					
	// If it's an unlimited game, don't expire the goal
	if ( m_unlimitedTime )
	{
		return false;
	}
	
	// check if the skaters have reached end of run BEFORE
	// setting the exception, or you'll never catch it!
	bool expired = false;
	 
	switch ( m_endRunType )
	{
	case vENDOFRUN:
		expired = p_skater_endrun_component->RunHasEnded();
		break;
	case vGOALENDOFRUN:
		expired = p_skater_endrun_component->GoalRunHasEnded();
		break;
	case vNONE:
		// if we're not supposed to use end of run, we need only check
		// the time
		return ( (int)m_timeLeft <= 0 );
		break;
	default:
		Dbg_Assert( 0 );
	}

	if ( expired && (int)m_timeLeft <= 0 )
	{
		// Dbg_Printf( "**** Already Expired\n" );
		return true;
	}

	// if time is up, set the end of run exception
	if ( (int)m_timeLeft <= 0 )
	{
		// Dbg_Printf( "EXPIRING GOAL: %d\n", GetGoalId());
		// printf("&&&&&&&&&&&&&&&&& calling EndRun\n");
		switch ( m_endRunType )
		{
		case vENDOFRUN:
			m_endRunCalled = true;
			// Dbg_Printf( "**** Calling End Run\n" );
			p_skater_endrun_component->EndRun( force_end );
			break;
		case vGOALENDOFRUN:
			// printf("reached goalendofrun\n");
			m_endRunCalled = true;
			p_skater_endrun_component->EndGoalRun( force_end );
			break;
		default:
			Dbg_Assert( 0 );
		}
	}

    // printf("AtEndOfRun returned %i\n", all_expired);
	// return ( all_expired && ( (int)m_timeLeft <= 0 ) );
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::AllSkatersAtEndOfRun()
{
	// check all the goals
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();

	GameNet::PlayerInfo* player;
	Lst::Search< GameNet::PlayerInfo > sh;
	
	for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
	{
		Obj::CSkater *p_Skater = player->m_Skater;
		// int skater_num = p_Skater->GetSkaterNumber();
		Dbg_Assert( p_Skater );
		
		Obj::CSkaterEndRunComponent* p_skater_endrun_component = GetSkaterEndRunComponentFromObject(p_Skater);
		Dbg_Assert( p_skater_endrun_component );

		if( skate_mod->GetGameMode()->GetNameChecksum() == CRCD( 0x9d65d0e7, "horse" ))
		{
			if( p_Skater->IsPaused() || p_Skater->IsHidden())
			{
				continue;
			}
		}
		switch ( m_endRunType )
		{
		case vENDOFRUN:
			if ( !p_skater_endrun_component->RunHasEnded() )
			{
				return false;
			}
			break;
		case vGOALENDOFRUN:
			if ( !p_skater_endrun_component->GoalRunHasEnded() )
			{
				return false;
			}
			break;
		default:
			Dbg_Assert( 0 );
		}
	}    
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::EditParams( Script::CStruct* pParams )
{
	mp_params->AppendStructure( pParams );

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::Update( void )
{
    if ( IsActive() && !IsPaused() )
    {
        RunCallbackScript( vACTIVE );
    }
    
    // the active script may have deactivated the goal, so we check 
    // if it's active again...don't check mini games
    if ( IsActive() && !IsPaused())
	{
		if( ShouldUseTimer()  )
		{
			if ( ( (int)m_timeLeft >= 1 ) )
			{
				int old_time_left = (int)m_timeLeft;
				m_timeLeft -= (Tmr::Time)( Tmr::FrameLength() * 1000 );
				m_elapsedTime += (Tmr::Time)( Tmr::FrameLength() * 1000 );

				if ( (int)m_timeLeft < 10000 && ( (int)( old_time_left / 1000 ) > (int)( (int)m_timeLeft / 1000 ) ) )
				{
					if (Mdl::Skate::Instance()->GetGameMode()->ShouldTimerBeep())
					{
						Script::RunScript( CRCD(0xcfc7c3ad,"timer_runout_beep") );
					}
				}
			}
		}

		if ( ShouldRequireCombo() )
		{
			if ( !mp_params->ContainsFlag( CRCD( 0x7100155d, "combo_started" ) ) )
			{
				Mdl::Skate *skate_mod =  Mdl::Skate::Instance();
				Obj::CSkater* pSkater = skate_mod->GetLocalSkater();
				if ( pSkater )
				{
					Mdl::Score* pScore = pSkater->GetScoreObject();
					if ( pScore && pScore->GetScorePotValue() > 0 )
					{
						mp_params->AddChecksum( NONAME, CRCD(0x7100155d,"combo_started") );
					}
				}
			}
		}
		
		// add the amount of time left
		// mp_params->AddInteger( "time_left", m_timeLeft );        
		if ( IsExpired() )
		{
			Expire();
		}
	}
	
// haven't used the inactive yet	
/*	else
	{
		if ( !IsLocked() )
        {
            // runs its inactive script (if any)
    		RunCallbackScript( vINACTIVE );
        }
	}	
*/
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::Expire()
{
	RunCallbackScript( vEXPIRED );
	
	// TODO: fix after e3
	// this was done to make the horse and combo letter goals work right
	if ( m_deactivateOnExpire )
	{
		Deactivate();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::IsActive( void )
{
	return ( m_active == true );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::HasSeenGoal( void )
{
	return m_hasSeen;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::PauseGoal( void )
{
	m_paused = true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::UnPauseGoal( void )
{
	m_paused = false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Tmr::Time CGoal::GetTime( void )
{
	return m_timeLeft;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/*void CGoal::CreateGoalFlag( uint32 flag )
{
    mp_goalFlags->AddInteger( flag, 0 );
    m_numflags++;
}*/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::SetGoalFlag( uint32 flag, int value )
{
    if ( !IsActive() )
    {
        return false;
    }
    
	if ( mp_goalFlags->ContainsComponentNamed( flag ) )
	{
		// remember the old value
		int old_value;
		mp_goalFlags->GetInteger( flag, &old_value, Script::ASSERT );
		
		mp_goalFlags->AddInteger( flag, value );
		if ( old_value != value )
		{
			if ( value == 0 )
				m_flagsSet--;
			else
			{
				m_flagsSet++;
				if ( !mp_params->ContainsFlag( CRCD(0x524566e3,"no_midgoal_sound") ) )
					Script::RunScript( CRCD(0x5ed34deb,"goal_got_flag_sound") );
			}
			if ( mp_params->ContainsFlag( CRCD(0xfd7ae1f4,"use_hud_counter") ) )
				Script::RunScript( CRCD(0xf2e85a34,"goal_update_counter"), mp_params );

			mp_params->AddInteger( CRCD(0xcf6cd3c1,"num_flags_set"), m_flagsSet );
		}		
		// printf("setting goal flag %s, %i of %i set\n", Script::FindChecksumName( flag ), m_flagsSet, m_numflags );
		return true;
	}
    
    Dbg_MsgAssert( 0, ("SetGoalFlag couldn't find flag\n" ) );
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::GetCreatedGoalGap(int gapIndex)
{
	Script::CArray *p_required_gaps=NULL;
	if (!mp_params->GetArray(CRCD(0x52d4489e,"required_gaps"),&p_required_gaps))
	{
		return false;
	}

	const char *p_required_trick_name="";
	mp_params->GetString(CRCD(0x730844a3,"required_trick_name"),&p_required_trick_name);
	if (p_required_trick_name[0])
	{
		Mdl::Skate *p_skate_mod =  Mdl::Skate::Instance();
		Obj::CSkater *p_skater = p_skate_mod->GetLocalSkater();
		if (!p_skater)
		{
			return false;
		}	
		Mdl::Score *p_score = p_skater->GetScoreObject();
	
		uint32 trick_checksum=Script::GenerateCRC(p_required_trick_name);
		
		// Also check for fs and bs versions (TT9784)
		uint32 bs_trick_checksum=CRCD(0x7491335e,"bs ");
		bs_trick_checksum=Crc::ExtendCRCWithString(bs_trick_checksum, p_required_trick_name);
		uint32 fs_trick_checksum=CRCD(0x73989b82,"fs ");
		fs_trick_checksum=Crc::ExtendCRCWithString(fs_trick_checksum, p_required_trick_name);
		
		if (p_score->GetCurrentNumberOfOccurrencesByName(trick_checksum, 0, false) == 0 && 
			p_score->GetCurrentNumberOfOccurrencesByName(bs_trick_checksum, 0, false) == 0 &&
			p_score->GetCurrentNumberOfOccurrencesByName(fs_trick_checksum, 0, false) == 0)
		{
			return false;
		}
	}		
	
	for (uint i=0; iGetSize(); ++i)
	{
		if (gapIndex==p_required_gaps->GetInteger(i))
		{
			char p_foo[20];
			sprintf(p_foo,"got_%d",i+1);
			SetGoalFlag(Script::GenerateCRC(p_foo),1);
			return true;
		}
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::GoalFlagEquals( uint32 flag, int value )
{
	if ( mp_goalFlags->ContainsComponentNamed( flag ) )
	{
		int f;
		mp_goalFlags->GetInteger( flag, &f, Script::ASSERT );
		return ( value == f );
	}
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::ResetGoalFlags()
{
	// reset the goal flags
	Script::CComponent* p_comp = mp_goalFlags->GetNextComponent( NULL );
	while ( p_comp )
	{
		Script::CComponent* p_next = mp_goalFlags->GetNextComponent( p_comp );
		p_comp->mIntegerValue = 0;
		// SetGoalFlag( p_comp->mNameChecksum, 0 );
		
		// printf("resetting goal flag %s\n", Script::FindChecksumName( flag_name ) );
		p_comp = p_next;
	}
	m_flagsSet = 0;
	mp_params->AddInteger( CRCD( 0xcf6cd3c1, "num_flags_set" ), 0 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::AllFlagsSet()
{
//    printf("%i flags, %i set\n", m_numflags, m_flagsSet);
	int num_flags_to_win = m_numflags;
	mp_params->GetInteger( CRCD(0x1171a555,"num_flags_to_win"), &num_flags_to_win, Script::NO_ASSERT );
	
	// K: Fix to TT9785, where created gap goals would succeed straight away if no gaps were chosen,
	// causing an assert.
	if (num_flags_to_win == 0)
	{
		return false;
	}	
	return ( m_flagsSet >= num_flags_to_win );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::GoalFlagSet( uint32 flag )
{
	int flag_value;
	mp_goalFlags->GetInteger( flag, &flag_value, Script::ASSERT );
	return ( flag_value != 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::IsLocked()
{
    return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::UnlockGoal()
{
    if ( m_isLocked )
    {
        m_isLocked = false;

		// don't initialize goals automatically in net games
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		if ( gamenet_man->InNetGame() )
			return;

		// initialize goal
        Init();
        
        // call the unlock script 
        RunCallbackScript( vUNLOCK );
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::NextTourSpot()
{
    Script::CArray* p_waypoints;
    // backward compatible with the horse_spots array naming convention
	if ( !mp_params->GetArray( CRCD(0x160b3220,"tour_spots"), &p_waypoints, Script::NO_ASSERT ) )
		mp_params->GetArray( CRCD(0x92c6e839,"horse_spots"), &p_waypoints, Script::ASSERT );

    Script::CStruct* p_waypoint;
    for ( uint32 i = 0; i < p_waypoints->GetSize(); i++ )
    {
        p_waypoint = p_waypoints->GetStructure( i );

        // is this waypoint done?
        uint32 flagId;
        p_waypoint->GetChecksum( CRCD(0x2e0b1465,"flag"), &flagId, Script::ASSERT );
        if ( GoalFlagEquals( flagId, 1 ) )
            continue;
        
        // pause the goal and set the time for this tour spot
        PauseGoal();
		int time;
		p_waypoint->GetInteger( CRCD(0x906b67ba,"time"), &time, Script::ASSERT );
        // add time if necessary
		if ( mp_params->ContainsFlag( CRCD(0xfb11cb01,"cumulative_timer") ) )
		{
			AddTime( time );
		}
		else
		{
			mp_params->AddInteger( CRCD(0x906b67ba,"time"), time );
			SetTimer();
		}
            
        // add the goalId and trigger id to the params
        p_waypoint->AddChecksum( CRCD(0x9982e501,"goal_id"), GetGoalId() );

		// send a flag if this is the first spot
		if ( m_flagsSet == 0 )
			p_waypoint->AddChecksum( NONAME, CRCD( 0xb13f3ab8, "first_spot" ) );

		// send a flag if this is a restart
		if ( mp_params->ContainsFlag( CRCD(0x38221df4,"quick_start") ) )
			p_waypoint->AddChecksum( NONAME, CRCD(0x38221df4,"quick_start") );
		else
			p_waypoint->RemoveFlag( CRCD(0x38221df4,"quick_start") );

        
		#ifdef __NOPT_ASSERT__
		// backward compatible with horse goal
		Script::CScript *p_script=NULL;
		if ( mp_params->ContainsFlag( CRCD(0x9d65d0e7,"horse") ) )
			p_script=Script::SpawnScript( CRCD(0x681bc776,"goal_horse_next_spot"), p_waypoint );
		else
			p_script=Script::SpawnScript( CRCD(0x4c938553,"goal_tour_next_spot"), p_waypoint );
		p_script->SetCommentString("Spawned from CGoal::NextTourSpot()");
		#else	
		// backward compatible with horse goal
		if ( mp_params->ContainsFlag( CRCD(0x9d65d0e7,"horse") ) )
			Script::SpawnScript( CRCD(0x681bc776,"goal_horse_next_spot"), p_waypoint );
		else
			Script::SpawnScript( CRCD(0x4c938553,"goal_tour_next_spot"), p_waypoint );
		#endif			


		// remove restart flag
		p_waypoint->RemoveFlag( CRCD(0x38221df4,"quick_start") );


/*        // move the skater to the node
        uint32 reset_checksum;
        p_waypoint->GetChecksum( "id", &reset_checksum, Script::ASSERT );
        Mdl::Skate * p_skate = Mdl::Skate::Instance();
        p_skate->ResetSkaters( SkateScript::FindNamedNode( reset_checksum ) );

        uint32 waypoint_script;
        if ( p_waypoint->GetChecksum( "scr", &waypoint_script, Script::NO_ASSERT ) )
            Script::SpawnScript( waypoint_script );
*/        
        return true;

    }
    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::InitTrickObjects()
{
    // get the object id's
    Script::CArray* p_trickObjectArray = NULL;
    mp_params->GetArray( CRCD(0x2eb84a54,"trick_objects"), &p_trickObjectArray, Script::ASSERT );
    
    Mdl::Skate* skate_mod = Mdl::Skate::Instance();
    Obj::CTrickObjectManager* p_TrickObjectManager = skate_mod->GetTrickObjectManager();

    for ( uint32 i = 0; i < p_trickObjectArray->GetSize(); i++ )
    {
        // add the trick object
        uint32 goal_object;
        Script::CStruct* p_trickObject = p_trickObjectArray[i].GetStructure( i );
        p_trickObject->GetChecksum( CRCD(0x40c698af,"id"), &goal_object, Script::ASSERT );
        
        // p_TrickObjectManager->RequestLogTrick( 1, 
        p_TrickObjectManager->ModulateTrickObjectColor( goal_object, 0 );
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::GotTrickObject( uint32 clusterId )
{
	// do we care?
	if ( mp_params->ContainsFlag( CRCD( 0xf268c278, "SkateTheLine" ) ) )
	{
		// we do
		uint32 current;
		mp_params->GetChecksum( CRCD(0xb03713ee,"current_cluster"), ¤t, Script::ASSERT );
		if ( current == clusterId )
			Script::RunScript( CRCD( 0x62b12a8e, "Goal_SkateTheLine_got_node" ), mp_params );
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::IsGraffitiGoal()
{
	return ( mp_params->ContainsFlag( CRCD(0xc8a82b5a,"graffiti") ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::ShouldLogTrickObject()
{
	if ( mp_params->ContainsFlag( CRCD(0xc8a82b5a,"graffiti") ) || mp_params->ContainsFlag( CRCD(0xf268c278,"SkateTheLine") ) )
	{
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::GotGraffitiCluster( uint32 clusterId, int score )
{
    // find the cluster in our cluster array
    Script::CArray* pClusterArray;
    mp_params->GetArray( CRCD(0x191f66e9,"kill_clusters"), &pClusterArray, Script::ASSERT );

    for ( uint32 i = 0; i < pClusterArray->GetSize(); i++ )
    {
        Script::CStruct* pCluster = pClusterArray->GetStructure( i );
        Dbg_MsgAssert( pCluster, ( "got null cluster from kill_cluster array\n" ) );

        uint32 id;
        pCluster->GetChecksum( "id", &id, Script::ASSERT );
		
		int minScore;
		// get the score for this cluster, or the default score
		if ( !pCluster->GetInteger( CRCD(0xcd66c8ae,"score"), &minScore, Script::NO_ASSERT ) )
			mp_params->GetInteger( CRCD(0xcd66c8ae,"score"), &minScore, Script::ASSERT );
		
		// printf("minScore: %i, score: %i\n", minScore, score);

        if ( id == clusterId && score > minScore )
        {
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			Obj::CTrickObjectManager* p_trickObjectMan = skate_mod->GetTrickObjectManager();

			// printf("modulating color\n");
			if ( !p_trickObjectMan->ModulateTrickObjectColor( clusterId, 0 ) )
				printf("ModulateTrickObjectColor didn't work\n");

			uint32 flag;
            pCluster->GetChecksum( CRCD(0x2e0b1465,"flag"), &flag, Script::ASSERT );

            SetGoalFlag( flag, 1 );
            return;
        }
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::IsSpecialGoal()
{
    return ( mp_params->ContainsFlag( CRCD(0xb394c01c,"special") ) );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::CheckSpecialGoal()
{
    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
    Obj::CSkater *p_Skater = skate_mod->GetLocalSkater();
    Dbg_Assert( p_Skater );
	
	Obj::CSkaterScoreComponent* p_SkaterScoreComponent = GetSkaterScoreComponentFromObject(p_Skater);
	Dbg_Assert( p_SkaterScoreComponent );
    
    Mdl::Score * p_Score = ( p_SkaterScoreComponent->GetScore() );
    Dbg_Assert( p_Score );

    const char* trick_string;
    mp_params->GetString( CRCD(0x3a54cc4b,"trick_string"), &trick_string, Script::ASSERT );

	// if it's a grind, we need to check for the FS and BS version too
	uint32 trick_type;
	mp_params->GetChecksum( CRCD(0x3d929b66,"trick_type"), &trick_type, Script::ASSERT );
	if ( trick_type == CRCD( 0x255ed86f, "grind" ) )
	{
		char test_trick[128];
		// frontside
		strcpy( test_trick, "FS " );
		strcat( test_trick, trick_string );
		// printf("testing FS version - %s\n", test_trick);
		if ( p_Score->GetCurrentNumberOfOccurrencesByName( Script::GenerateCRC( test_trick ) ) )
		{
			// printf("got it\n");
			return true;
		}

		// backside
		strcpy( test_trick, "BS " );
		strcat( test_trick, trick_string );
		// printf("testing BS version - %s\n", test_trick);
		if ( p_Score->GetCurrentNumberOfOccurrencesByName( Script::GenerateCRC( test_trick ) ) )
		{
			// printf("got it\n");
			return true;
		}
	}

	// check the bare trick string
	if ( mp_params->ContainsFlag( CRCD(0x8e6014f6,"create_a_trick") ) )
	{
		// printf("testing cat version %s\n", new_string );
		if ( p_Score->GetCurrentNumberOfOccurrencesByName( Script::GenerateCRC( trick_string ) ) > 0 )
		{
			return true;
		}
	}
	else
	{
		// printf("testing normal version - %s\n", trick_string);
		if ( p_Score->GetCurrentNumberOfOccurrencesByName( Script::GenerateCRC( trick_string ) ) > 0 )
		{
			return true;
		}
	}
    return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::IsTetrisGoal()
{
    return ( mp_params->ContainsFlag( CRCD(0x4147922b,"tetris") ) );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoal::GetRandomIndexFromKeyCombos( Script::CArray* p_keyCombos )
{
	Dbg_MsgAssert( p_keyCombos->GetType() == ESYMBOLTYPE_NAME ||
				   p_keyCombos->GetType() == ESYMBOLTYPE_STRUCTURE,
				   ( "GetRandomIndexFromKeyCombos got array of the wrong type" ) );

	// get the global trick mapping
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	Obj::CSkaterProfile* p_SkaterProfile = skate_mod->GetCurrentProfile();
	Script::CStruct* p_trickMappings = p_SkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );

	// figure number of components in mapping
	uint32 size = p_keyCombos->GetSize();

	// safety first
	int max_tries = 100;
	
	uint32 trick_name_checksum = 0;
	uint32 key_combo;
	while ( !trick_name_checksum )
	{        
		int i = Mth::Rnd( size );
		if ( p_keyCombos->GetType() == ESYMBOLTYPE_NAME )
		{
			key_combo = p_keyCombos->GetChecksum( i );
		}
		else
			p_keyCombos->GetStructure( i )->GetChecksum( CRCD(0xacfdb27a,"key_combo"), &key_combo, Script::ASSERT );

		// see if this is a valid key_combo
		int cat_index;
		p_trickMappings->GetChecksum( key_combo, &trick_name_checksum, Script::NO_ASSERT );
		if ( trick_name_checksum )
		{
			return i;
		}
		else if ( p_trickMappings->GetInteger( key_combo, &cat_index, Script::NO_ASSERT ) )
		{
			return i;
		}
		// reset it to 0
		trick_name_checksum = 0;
		max_tries--;
		if ( max_tries == 0 )
		{
			// they've probably un-mapped all their keys.  check all the tricks in order
			// just to be safe
			for ( uint32 j = 0; j < size; j++ )
			{
				if ( p_keyCombos->GetType() == ESYMBOLTYPE_NAME )
					key_combo = p_keyCombos->GetChecksum( j );
				else
					p_keyCombos->GetStructure( j )->GetChecksum( CRCD(0xacfdb27a,"key_combo"), &key_combo, Script::ASSERT );
				p_trickMappings->GetChecksum( key_combo, &trick_name_checksum, Script::NO_ASSERT );
				if ( trick_name_checksum )
				{
					return i;
				}
				trick_name_checksum = 0;
			}
			// they did un-map all their keys.  Fuck 'em
			NoValidKeyCombos();
			break;
		}
	}
	return -1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::NoValidKeyCombos()
{
	Script::CStruct* p_temp_params = new Script::CStruct();
	p_temp_params->AddChecksum( CRCD(0x9982e501,"goal_id"), GetGoalId() );
	Script::RunScript( CRCD( 0xf3c20af5, "goal_no_valid_key_combos" ), p_temp_params );
	delete p_temp_params;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
uint32 CGoal::GetRootGoalId()
{
	CGoalManager* pGoalManager = GetGoalManager();
	CGoal* pParentGoal = this;	
	Dbg_Assert( pGoalManager );
	
	// get the root node
	CGoalLink* p_parent = mp_parents;
	while ( p_parent && p_parent->m_relative != 0 )
	{
		pParentGoal = pGoalManager->GetGoal( p_parent->m_relative );
		p_parent = pParentGoal->GetParents();
	}

	return pParentGoal->GetGoalId();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
uint32 CGoal::GetGoalId()
{
    uint32 goalId = 0;
    mp_params->GetChecksum( "goal_id", &goalId, Script::ASSERT );
    return goalId;
}

bool CGoal::IsEditedGoal()
{
	Dbg_MsgAssert(mp_params,("NULL mp_params"));
	return mp_params->ContainsFlag(CRCD(0x981d3ad0,"edited_goal"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::HasWonGoal()
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();	
	if ( gamenet_man->InNetGame() && 
		 ( skate_mod->GetGameMode()->GetNameChecksum() == CRCD( 0xec200eaa, "netgoalattack" )) ||
		 ( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0x1c471c60,"netlobby")))
	{
		return m_netIsBeat;
	}
	return m_isbeat;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::HasWonStage()
{
	if ( HasWonGoal() )
		return true;	
	else
	{
		CGoalLink* pLink = mp_children;
		while ( pLink )
		{
			if ( pLink->m_beaten )
				return true;
			else
				pLink = pLink->mp_next;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::IsSelected()
{
	return m_isselected;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::GotCounterObject()
{
	int number_collected = 0;
    mp_params->GetInteger( CRCD(0x129daa10,"number_collected"), &number_collected, Script::NO_ASSERT );
    // printf("alredy collected %i\n", number_collected);
    number_collected++;

    mp_params->AddInteger( CRCD(0x129daa10,"number_collected"), number_collected );

	if ( mp_params->ContainsFlag( CRCD(0xfd7ae1f4,"use_hud_counter") ) )
		Script::RunScript( CRCD(0xf2e85a34,"goal_update_counter"), mp_params );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::CounterGoalDone()
{
    int number_collected = 0;
    mp_params->GetInteger( CRCD(0x129daa10,"number_collected"), &number_collected, Script::NO_ASSERT );

    int number;
    mp_params->GetInteger( CRCD(0x696fe0ab,"number"), &number, Script::ASSERT );

    return ( number_collected >= number );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::MarkInvalid()
{
    m_invalid = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::IsInvalid()
{
    return m_invalid;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::HasProset( const char* proset_prefix )
{
    const char* p_local_proset;
    if ( mp_params->GetString( CRCD(0x955c6305,"proset_prefix"), &p_local_proset, Script::NO_ASSERT ) )
    {
        if ( stricmp( p_local_proset, proset_prefix ) == 0 )
            return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetHasSeen()
{
    m_hasSeen = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::MarkBeaten()
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	
	if( gamenet_man->InNetGame()  && skate_mod->GetGameMode()->GetNameChecksum() == CRCD( 0xec200eaa, "netgoalattack" ) )
	{
		m_netIsBeat = true;
	}
	else
	{
		m_isbeat = true;
	}
	// mark all goals in the tree beaten
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	Game::CGoalLink* p_link = mp_parents;
	while ( p_link && p_link->m_relative != 0 )
	{
		CGoal* pGoal = pGoalManager->GetGoal( p_link->m_relative );
		pGoal->MarkBeaten();
		p_link = p_link->mp_next;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::MarkBeatenBy( int id )
{
	Dbg_Printf( "**** Marking goal 0x%x as beaten\n", GetGoalId());
	m_beatenFlags.Set( id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::MarkBeatenByTeam( int team_id )
{
	m_teamBeatenFlags.Set( team_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::HasWonGoal( int id )
{
	return m_beatenFlags.Test( id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::HasTeamWonGoal( int team_id )
{
	return m_teamBeatenFlags.Test( team_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::Select()
{
	m_isselected = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::Deselect()
{
	m_isselected = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetUnlocked()
{
    m_isLocked = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetLevelNum( int levelNum )
{
    m_levelNum = levelNum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CGoal::GetLevelNum()
{
    return m_levelNum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::CheckMinigameRecord( int value )
{
	int record = 0;
	mp_params->GetInteger( CRCD(0x78ffb85c,"minigame_record"), &record, Script::NO_ASSERT );

	mp_params->AddInteger( CRCD(0x819fc1d1,"last_attempt"), value );
	if ( value > record )
	{
		mp_params->AddInteger( CRCD(0x78ffb85c,"minigame_record"), value );
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CGoal::GetMinigameRecord()
{
	int minigame_record = -1;
	mp_params->GetInteger( CRCD(0x78ffb85c,"minigame_record"), &minigame_record, Script::NO_ASSERT );
	return minigame_record;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::CanRetry()
{
	return ( !mp_params->ContainsFlag( CRCD(0xa612f12c,"no_restart") ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::IsMinigame()
{
	return ( mp_params->ContainsFlag( CRCD(0x6bae094c,"minigame") ) );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::SetStartTime()
{
	mp_params->AddInteger( CRCD(0x5fd80edc,"current_time"), 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::UpdateComboTimer()
{
	if ( !IsPaused() )
	{
		Tmr::Time current_time = 0;
		int time;
		mp_params->GetInteger( CRCD(0x5fd80edc,"current_time"), &time, Script::NO_ASSERT );
		current_time = (Tmr::Time)time;
	
		current_time +=  (Tmr::Time)( Tmr::FrameLength() * 1000 );
	
		mp_params->AddInteger( CRCD(0x5fd80edc,"current_time"), (int)current_time );

		// add normal units for display
		mp_params->AddInteger( CRCD(0x7b1fe9a4,"current_time_seconds"), (int)( current_time / 1000 ) );
		mp_params->AddInteger( CRCD(0xb4db9cb7,"current_time_fraction"), (int)( ( current_time / 10 ) % 100 ) );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetStartHeight()
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater *p_skater = skate_mod->GetLocalSkater();
	Dbg_Assert( p_skater );

	int start_height = static_cast< int >(GetSkaterStateComponentFromObject(p_skater)->GetHeight());
	mp_params->AddInteger( CRCD(0xc07d8d60,"start_height"), start_height );

	// printf("setting start height to %i\n", start_height);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::CheckHeightRecord()
{
	if ( !IsPaused() )
	{
		int record = 0;
		mp_params->GetInteger( CRCD(0x78ffb85c,"minigame_record"), &record, Script::NO_ASSERT );

		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		Obj::CSkater *p_skater = skate_mod->GetLocalSkater();
		Dbg_Assert( p_skater );

		int start_height = 10000;
		mp_params->GetInteger( CRCD(0xc07d8d60,"start_height"), &start_height, Script::NO_ASSERT );
		
		int current_height = static_cast< int >(GetSkaterStateComponentFromObject(p_skater)->GetHeight()) - start_height;

		//printf("current_height: %i\n", current_height);

		if ( current_height < 0 )
			current_height = 0;		

		int feet = (int)( current_height / 12 );
		int inches = current_height % 12;

		mp_params->AddInteger( CRCD(0x50815d42,"current_height_feet"), feet );
		mp_params->AddInteger( CRCD(0xb0c26927,"current_height_inches"), inches );
		
		if ( current_height > record )
		{
			mp_params->AddInteger( CRCD(0x78ffb85c,"minigame_record"), current_height );
			
			mp_params->AddInteger( CRCD(0x24bf3ae4,"record_feet"), feet );
			mp_params->AddInteger( CRCD(0x9b49f28d,"record_inches"), inches );
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::CheckDistanceRecord()
{
	// find the object in question
	uint32 objId;
	mp_params->GetChecksum( CRCD(0x8e18f3b3,"distance_record_object"), &objId, Script::ASSERT );

	// grab the object
	Obj::CMovingObject* pObj = NULL;
//	pObj = Obj::CMovingObject::m_hash_table.GetItem( objId );
	pObj = (Obj::CMovingObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID( objId);

	// grab the skater
	Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetLocalSkater();
	
	if ( pSkater && pObj )
	{
		float distSquared = Mth::DistanceSqr( pObj->m_pos, pSkater->m_pos );
		
		// get the height of the two
		float objHeight = pObj->m_pos.GetY();
		float skaterHeight = pSkater->m_pos.GetY();

		// get the base of the right triangle formed by the three points
		float distance = sqrtf(	distSquared - ( Mth::Sqr( skaterHeight - objHeight ) ) );
		// printf("distance = %f\n", distance);

		int feet = (int)( distance / 12 );
		int inches = (int)distance % 12;
		
		mp_params->AddInteger( CRCD(0xcb48c8e8,"current_distance_feet"), feet );
		mp_params->AddInteger( CRCD(0x644fc146,"current_distance_inches"), inches );
		
		int record = 0;
		mp_params->GetInteger( CRCD(0x78ffb85c,"minigame_record"), &record, Script::NO_ASSERT );		
		if ( distance > record )
		{
			mp_params->AddInteger( CRCD(0x78ffb85c,"minigame_record"), distance );
		
			mp_params->AddInteger( CRCD(0x24bf3ae4,"record_feet"), feet );
			mp_params->AddInteger( CRCD(0x9b49f28d,"record_inches"), inches );
			return true;
		}
	}	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetQuickStartFlag()
{
	mp_params->AddChecksum( NONAME, CRCD( 0x38221df4, "quick_start" ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::UnsetQuickStartFlag()
{
	mp_params->RemoveFlag( CRCD(0x38221df4,"quick_start") );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::IsProGoal()
{
	return ( mp_params->ContainsComponentNamed( CRCD(0xd303a1a3,"pro_goal") ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::IsNetGoal()
{
	return ( mp_params->ContainsComponentNamed( CRCD(0xd15ea00,"net") ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::IsCompetition()
{
	return ( mp_params->ContainsFlag( CRCD(0x4af5d34e,"competition") ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::IsPaused()
{
	return m_paused;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::UnBeatGoal()
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	if( gamenet_man->InNetGame() )
	{
		m_beatenFlags = 0;
		m_teamBeatenFlags = 0;
		if( m_netIsBeat )
		{
			m_netIsBeat = false;
			return true;
		}
	}
	else
	{
		if ( m_isbeat )
		{
			m_isbeat = false;
			return true;
		}
	}    
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::GetViewGoalsText( const char** p_view_goals_text )
{
	mp_params->GetText( "view_goals_text", p_view_goals_text, Script::NO_ASSERT );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::IsBettingGame()
{
	return ( mp_params->ContainsFlag( CRCD(0x8add4306,"betting_game") ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::IsBettingGuy()
{
	return ( mp_params->ContainsFlag( CRCD(0x8cfee956,"betting_guy") ) );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::EndRunCalled()
{
	return m_endRunCalled;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::ClearEndRun( Script::CScript* pScript )
{
	// printf("CGoal::ClearEndRun called\n");
	m_endRunCalled = false;

	// TODO: this should pick the right skater somehow
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* p_skater = skate_mod->GetLocalSkater();

	Obj::CSkaterEndRunComponent* p_skater_endrun_component = GetSkaterEndRunComponentFromObject(p_skater);
	Dbg_Assert(p_skater_endrun_component);

	switch ( m_endRunType )
	{
	case vENDOFRUN:
// Mick: the way these type of exception is handled has changed
// I'm not sure if it still needs to be cleared here any more
// as it should have been executed immediately
// I'm removing this line for now, as the ExceptionComponent has gone  
//		GetExceptionComponentFromObject(p_skater)->ClearException( CRCD( 0x822e13a9, "RunHasEnded" ));
		p_skater->CallMemberFunction( CRCD( 0x4c58771e, "EndOfRunDone" ), NULL, pScript );
		p_skater_endrun_component->ClearStartedEndOfRunFlag();
		break;
	case vGOALENDOFRUN:
//		GetExceptionComponentFromObject(p_skater)->ClearException( CRCD( 0xab676175, "GoalHasEnded" ) );
		p_skater->CallMemberFunction( CRCD( 0x69a9e37f, "Goal_EndOfRunDone" ), NULL, pScript );
		p_skater_endrun_component->ClearStartedGoalEndOfRunFlag();
		break;
	default:
		Dbg_Assert( 0 );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::FinishedEndOfRun()
{
	// TODO: make this pick the right skater
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* p_skater = skate_mod->GetLocalSkater();

	Obj::CSkaterEndRunComponent* p_skater_endrun_component = GetSkaterEndRunComponentFromObject(p_skater);
	Dbg_Assert(p_skater_endrun_component);

	switch ( m_endRunType )
	{
	case vENDOFRUN:
		return p_skater_endrun_component->RunHasEnded();
		break;
	case vGOALENDOFRUN:
		return p_skater_endrun_component->GoalRunHasEnded();
		break;
	default:
		Dbg_Assert( 0 );
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::StartedEndOfRun()
{
	// TODO: make this choose the right skater
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* p_skater = skate_mod->GetLocalSkater();

	Obj::CSkaterEndRunComponent* p_skater_endrun_component = GetSkaterEndRunComponentFromObject(p_skater);
	Dbg_Assert(p_skater_endrun_component);

	switch ( m_endRunType )
	{
	case vENDOFRUN:
		return p_skater_endrun_component->StartedEndOfRun();
		break;
	case vGOALENDOFRUN:
		// return p_skater->StartedGoalEndOfRun();
		return m_endRunCalled;
		break;
	default:
		Dbg_Assert( 0 );
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// void CGoal::DisableEndOfRun()
// {
//	m_shouldEndRun = false;
// }


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::AddTempSpecialTrick()
{
	// get the new special trick name and type	
	uint32 trickType;
	bool is_cat = mp_params->ContainsFlag( CRCD(0x8e6014f6,"create_a_trick") );
	
	mp_params->GetChecksum( "trick_type", &trickType, Script::ASSERT );
	uint32 trickName;	
	if ( is_cat )
	{
		int tempTrickName;
		mp_params->GetInteger( CRCD(0xb502200,"premade_cat_index"), &tempTrickName, Script::ASSERT );
		trickName = (uint32)tempTrickName;
	}
	else
		mp_params->GetChecksum( "trick_name", &trickName, Script::ASSERT );
	
	// get the local string version of the trick and store it
	if ( is_cat )
	{
		Script::CArray* pPremadeCatArray = Script::GetArray( CRCD(0xffd7913f,"Premade_CATS"), Script::ASSERT );
		Script::CStruct* pPremadeCat = pPremadeCatArray->GetStructure( trickName );
		const char* pPremadeCatName;
		pPremadeCat->GetString( CRCD(0xa1dc81f9,"name"), &pPremadeCatName, Script::ASSERT );
		mp_params->AddString( CRCD(0x3a54cc4b,"trick_string"), pPremadeCatName );
	}
	else
	{
		const char* trickName_string; 
		Script::CStruct* p_trick = Script::GetStructure( trickName, Script::ASSERT );
		Script::CStruct* p_trick_params;
		p_trick->GetStructure( "Params", &p_trick_params, Script::ASSERT );
		p_trick_params->GetLocalString( "Name", &trickName_string, Script::ASSERT );
		mp_params->AddString( "trick_string", trickName_string );
	}
	
	// get the list of valid key bindings for this trick type
	Script::CStruct* p_keyBindings = NULL;
	switch ( trickType )
	{
		case CRCC( 0x61a1bc57, "cat" ):
		case CRCC( 0x439f4704, "air" ):
			p_keyBindings = Script::GetStructure( CRCD( 0x4764231d, "goal_special_tricks_air" ), Script::ASSERT );
			break;
		case CRCC( 0xa549b57b, "lip" ):
			p_keyBindings = Script::GetStructure( CRCD( 0xa1b2d162, "goal_special_tricks_lip" ), Script::ASSERT );
			break;
		case CRCC( 0x255ed86f, "grind" ):
			p_keyBindings = Script::GetStructure( CRCD( 0xf481d0cd, "goal_special_tricks_grind" ), Script::ASSERT );
			break;
		case CRCC( 0xef24413b, "manual" ):
			p_keyBindings = Script::GetStructure( CRCD( 0xd72d5cf7, "goal_special_tricks_manual" ), Script::ASSERT );
			break;
		default:
			Dbg_MsgAssert( p_keyBindings, ( "AddTempSpecialTrick got weird trick type\n" ) );
	}

	Obj::CPlayerProfileManager* pPlayerProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	Obj::CSkaterProfile* pSkaterProfile = pPlayerProfileManager->GetCurrentProfile();
	// Script::CStruct* p_trickMapping = p_SkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );

	int numTricks = pSkaterProfile->GetNumSpecialTrickSlots();
	
	// we need to remember the key combo
	uint32 key_combo = 0;

	// search through current special tricks
	bool found_trick = false;
	for ( int i = 0; i < numTricks; i++ )
	{
		Obj::SSpecialTrickInfo trick_info = pSkaterProfile->GetSpecialTrickInfo( i );
		
		if ( !is_cat && trick_info.m_TrickName == trickName )
		{
			// printf("the special trick is already bound!\n");
			found_trick = true;
			key_combo = trick_info.m_TrickSlot;
			break;
		}
		p_keyBindings->RemoveFlag( trick_info.m_TrickSlot );
	}

	// if we didn't find the trick, grab the first available, unused key combo
	if ( !found_trick )
	{
		// printf("binding special trick\n");
		Script::CComponent* p_comp = p_keyBindings->GetNextComponent();
		Dbg_MsgAssert( p_comp, ( "Could not find an open key binding\n" ) );
		uint32 trickSlot = p_comp->mChecksum;
		Dbg_MsgAssert( trickSlot, ( "Could not find key binding in structure" ) );
		
		// give everyone a new slot
		/*int num_profiles = pPlayerProfileManager->GetNumProfileTemplates();
		for ( int i = 0; i < num_profiles; i++ )
		{
			Obj::CSkaterProfile* pTempProfile = pPlayerProfileManager->GetProfileTemplateByIndex( i );
			pTempProfile->AwardSpecialTrickSlot();
		}*/
		pSkaterProfile->AwardSpecialTrickSlot();

		key_combo = trickSlot;
		if ( is_cat )
		{
			// assign cat to trickslot
			Script::CStruct* pScriptParams = new Script::CStruct();
			pScriptParams->AddChecksum( CRCD(0xa92a2280,"trickSlot"), trickSlot );
			pScriptParams->AddInteger( CRCD(0x7f8c98fe,"index"), trickName );
			pScriptParams->AddInteger( CRCD(0x7f264be9,"special_trick_index"), numTricks );
			Script::RunScript( CRCD(0x466576c8,"load_premade_cat"), pScriptParams );
			delete pScriptParams;
		}
		else
		{
			// add new trick to new slot
			Obj::SSpecialTrickInfo trickInfo;
			trickInfo.m_TrickSlot = trickSlot;
			trickInfo.m_TrickName = trickName;
			pSkaterProfile->SetSpecialTrickInfo( numTricks, trickInfo );
		}

		// make a note of this so we can remove it later
		mp_params->AddInteger( CRCD( 0xbc678826, "should_remove_trick" ), 1 );
	}

	// testing
	// Script::PrintContents( p_SkaterProfile->GetSpecialTricksStructure() );

	// replace the key combo array
	mp_params->RemoveComponent( "key_combos" );
	Script::CArray* p_key_combos = new Script::CArray();
	p_key_combos->SetSizeAndType( 1, ESYMBOLTYPE_NAME );

	p_key_combos->SetChecksum( 0, key_combo );

	mp_params->AddArrayPointer( "key_combos", p_key_combos );
	// printf("added special trick\n");
	// Script::PrintContents( mp_params );
	ReplaceTrickText();
	
	return !found_trick;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::RemoveTempSpecialTrick()
{
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	Obj::CPlayerProfileManager* pPlayerProfileManager = skate_mod->GetPlayerProfileManager();
	Obj::CSkaterProfile* pSkaterProfile = pPlayerProfileManager->GetCurrentProfile();

	// do we need to take out the trick we added?
	int should_remove;
	mp_params->GetInteger( "should_remove_trick", &should_remove, Script::ASSERT );
	if ( should_remove == 1 )
	{
		// printf("removing special trick\n");
		// find it and take it out
		uint32 trickName;
		
		if ( mp_params->ContainsFlag( CRCD(0x8e6014f6,"create_a_trick") ) )
		{
			// int premade_cat_index;
			// mp_params->GetInteger( CRCD(0xb502200,"premade_cat_index"), &premade_cat_index, Script::ASSERT );
			// trickName = (uint32)premade_cat_index;
			
			// temp cat tricks are always on 0
			trickName = 0;
		}
		else
			mp_params->GetChecksum( CRCD( 0xef6fb249, "trick_name" ), &trickName, Script::ASSERT );

		// int numTricks = p_SkaterProfile->GetNumSpecialTrickSlots();
		// just look through all tricks
		for ( int i = 0; i < Obj::CSkaterProfile::vMAXSPECIALTRICKSLOTS; i++ )
		{
			Obj::SSpecialTrickInfo trick_info = pSkaterProfile->GetSpecialTrickInfo( i );
			// printf("m_TrickName: %x, trickName: %x\n", trick_info.m_TrickName, trickName );
			if ( trick_info.m_TrickName == trickName )
			{
				// printf("Removing special trick\n");
				Obj::SSpecialTrickInfo new_info;
				new_info.m_TrickName = CRCD( 0xf60c9090, "unassigned" );
				new_info.m_TrickSlot = CRCD( 0xf60c9090, "unassigned" );
				pSkaterProfile->SetSpecialTrickInfo( i, new_info );
				mp_params->AddInteger( CRCD( 0xbc678826, "should_remove_trick" ), 0 );
				break;
			}
		}
		
		// take a slot away from everyone
		/*int num_profiles = pPlayerProfileManager->GetNumProfileTemplates();
		for ( int i = 0; i < num_profiles; i++ )
		{
			Obj::CSkaterProfile* pTempProfile = pPlayerProfileManager->GetProfileTemplateByIndex( i );
			pTempProfile->AwardSpecialTrickSlot( -1 );
		}*/
		pSkaterProfile->AwardSpecialTrickSlot( -1 );
	}
	
	mp_params->RemoveComponent( CRCD(0x79704516,"key_combos") );

	Dbg_MsgAssert( (int)pSkaterProfile->GetNumSpecialTrickSlots() <= Script::GetInteger( CRCD(0x20297d6f,"max_number_of_special_trickslots") ), ( "You have too many trickslots!" ) );
	// Script::CStruct* pTest = pSkaterProfile->GetSpecialTricksStructure();
	// printf("done removing\n");
	// Script::PrintContents( pTest );

	// remove the display string just in case
	mp_params->RemoveComponent( "key_combo_string" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::ShouldUseTimer()
{
	int unlimited_time = 0;

	mp_params->GetInteger( CRCD(0xf0e712d2,"unlimited_time"), &unlimited_time );

	return ( unlimited_time == 0 );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::CountAsActive()
{
	return IsActive();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::LoadSaveData( Script::CStruct *pFlags )
{
	int hasBeaten = 0;
	pFlags->GetInteger( "hasBeaten", &hasBeaten, Script::ASSERT );
	if ( hasBeaten != 0 )
		MarkBeaten();
	
	int isLocked = 1;
	pFlags->GetInteger( "isLocked", &isLocked, Script::ASSERT );
	if ( isLocked == 0 )
	{
		// special case for pro challenges
/*		if ( mp_params->ContainsFlag( "pro_specific_challenge" ) && hasBeaten == 0 )
		{
			if ( mp_goalPed->ProIsCurrentSkater() )
				SetUnlocked();
		}
		else
*/
		SetUnlocked();
	}

	int hasSeen = 0;
	pFlags->GetInteger( "hasSeen", &hasSeen, Script::ASSERT );
	if ( hasSeen != 0 )
		SetHasSeen();
	
	
	// check for a completion record
	int record = 0;
	pFlags->GetInteger( CRCD( 0xc22a2b72, "win_record" ), &record, Script::ASSERT );
	mp_params->AddInteger( "win_record", record );
	uint32 record_type;
	if ( mp_params->GetChecksum( "record_type", &record_type, Script::NO_ASSERT ) )
	{
		if ( record_type == Script::GenerateCRC( "time" ) )
			m_elapsedTimeRecord = record;
		else if ( record_type == Script::GenerateCRC( "score" ) )
			m_winScoreRecord = record;
	}

	pFlags->GetInteger( "timesLost", &m_numTimesLost, Script::ASSERT );
	pFlags->GetInteger( "timesWon", &m_numTimesWon, Script::ASSERT );
	pFlags->GetInteger( "timesStarted", &m_numTimesStarted, Script::ASSERT );

	uint32 team_pro;
	pFlags->GetChecksum( CRCD(0xced8a3a4,"team_pro"), &team_pro, Script::ASSERT );
	if ( team_pro != 0 )
	{
		mp_params->AddChecksum( CRCD(0xced8a3a4,"team_pro"), team_pro );
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::GetSaveData( Script::CStruct* pFlags )
{
	pFlags->AddInteger( "levelNum", GetLevelNum() );

	int isLocked = IsLocked();
	pFlags->AddInteger( "isLocked", isLocked );

	int hasSeen = HasSeenGoal();
	pFlags->AddInteger( "hasSeen", hasSeen );

	int hasBeaten = HasWonGoal();
	pFlags->AddInteger( "hasBeaten", hasBeaten );
	
/*	int isProSpecificChallenge = 0;
	if ( mp_params->ContainsFlag( "pro_specific_challenge" ) )
		isProSpecificChallenge = 1;
	pFlags->AddInteger( "isProSpecificChallenge", isProSpecificChallenge );
*/

	int record = 0;
	uint32 record_type;
	if ( mp_params->GetChecksum( "record_type", &record_type, Script::NO_ASSERT ) )
	{
		if ( record_type == Script::GenerateCRC( "time" ) )
			record = (int)m_elapsedTimeRecord;
		else if ( record_type == Script::GenerateCRC( "score" ) )
			record = m_winScoreRecord;
	}
	pFlags->AddInteger( "win_record", record );

	// TODO: should this be removed for the release?
	pFlags->AddInteger( "timesLost", m_numTimesLost );
	pFlags->AddInteger( "timesWon", m_numTimesWon );
	pFlags->AddInteger( "timesStarted", m_numTimesStarted );

	uint32 team_pro = 0;
	mp_params->GetChecksum( CRCD(0xced8a3a4,"team_pro"), &team_pro, Script::NO_ASSERT );
	pFlags->AddChecksum( CRCD(0xced8a3a4,"team_pro"), team_pro );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// utility used by ReplaceTrickText
bool fill_trick_and_key_combo_arrays( Script::CArray* p_key_combos, Script::CArray* p_key_combo_strings, Script::CArray* p_trick_names, int premade_cat_index )
{	
	// make sure the arrays are the right size and type
	Dbg_MsgAssert( p_key_combos->GetSize() == p_key_combo_strings->GetSize(), ( "key combo string array wrong size" ) );
	Dbg_MsgAssert( p_key_combos->GetSize() == p_trick_names->GetSize(),	( "trick name array wrong size" ) );
	Dbg_MsgAssert( p_key_combo_strings->GetType() == ESYMBOLTYPE_STRING, ( "array is the wrong type" ) );
	Dbg_MsgAssert( p_trick_names->GetType() == ESYMBOLTYPE_STRING, ( "array is the wrong type" ) );
	
	Script::CStruct* p_key_combo_map = Script::GetStructure( CRCD(0x8856c817,"goal_tetris_trick_text"), Script::ASSERT );
	
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();
	Script::CStruct* pTricks = pSkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );

	// Script::PrintContents( pTricks );

	int size = p_key_combos->GetSize();
	
	for ( int i = 0; i < size; i++ )
	{
		uint32 key_combo = p_key_combos->GetChecksum( i );
		// printf("checking for key combo %s\n", Script::FindChecksumName( key_combo ) );
		uint32 trick_checksum = 0;
		bool found_a_match = false;
		int cat_trick_index = 0;
		bool found_cat = false;
		if ( pTricks->GetInteger( key_combo, &cat_trick_index, Script::NO_ASSERT ) )
		{
			// load cat info
			printf("found a cat trick - cat_trick_index = %i\n", cat_trick_index);
			found_cat = true;
			found_a_match = true;
		}
		else if ( pTricks->GetChecksum( key_combo, &trick_checksum, Script::NO_ASSERT ) )
		{
			found_a_match = true;
		}
		else
		{
			// look for a special key combo
			int numSpecials = pSkaterProfile->GetNumSpecialTrickSlots();
			// printf("checking special trick slots\n");
			for ( int j = 0; j < numSpecials; j++ )
			{
				Obj::SSpecialTrickInfo trick_info = pSkaterProfile->GetSpecialTrickInfo( j );
				// printf("trickslot: %s\n", Script::FindChecksumName( trick_info.m_TrickSlot ) );
				if ( trick_info.m_TrickSlot == key_combo )
				{
					// printf("found a match\n");
					trick_checksum = trick_info.m_TrickName;
					found_a_match = true;
					break;
				}
			}
		}

		if ( !found_a_match )
		{
			// Script::PrintContents( pTricks );
			// this key combo isn't assigned!  bail out...
			Dbg_MsgAssert( 0, ( "key combo %s not assigned!\n", Script::FindChecksumName( key_combo ) ) );
			return false;
		}

		const char* p_key_combo_string;
		p_key_combo_map->GetString( key_combo, &p_key_combo_string, Script::ASSERT );
		const char* p_trick_name;
		if ( found_cat )
		{            
			// printf("getting cat params\n");
			Obj::CSkater* pSkater = pSkate->GetLocalSkater();
			if ( pSkater )
			{
				// printf("got skater\n");
				Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[cat_trick_index];
				Dbg_Assert( pCreatedTrick );
				// Script::PrintContents( pCreatedTrick->mp_other_params );
				pCreatedTrick->mp_other_params->GetString( CRCD( 0xa1dc81f9, "name" ), &p_trick_name, Script::ASSERT );
			}
		}
		else
		{
			if ( premade_cat_index > -1 )
			{
				Script::CArray* pPremadeCats = Script::GetArray( CRCD(0xffd7913f,"Premade_CATS"), Script::ASSERT );
				Script::CStruct* pPremadeCat = pPremadeCats->GetStructure( premade_cat_index );
				pPremadeCat->GetString( CRCD(0xa1dc81f9,"name"), &p_trick_name, Script::ASSERT );
			}
			else
			{
				Script::CStruct* p_trick_struct = Script::GetStructure( trick_checksum, Script::ASSERT );
				Script::CStruct* p_trick_params;
				p_trick_struct->GetStructure( CRCD(0x7031f10c,"Params"), &p_trick_params, Script::ASSERT );
				p_trick_params->GetLocalString( CRCD(0xa1dc81f9,"Name"), &p_trick_name, Script::ASSERT );
			}
		}

		// printf("setting strings: %s\n%s\n", p_key_combo_string, p_trick_name );
		
		p_key_combo_strings->SetString( i, Script::CreateString( p_key_combo_string ) );
		p_trick_names->SetString( i, Script::CreateString( p_trick_name ) );
	}
	return true;
}

// utility used by CGoal::ReplaceTrickText...finds and replaces \t and \k with
// the trick name and key combo, respectively
void find_and_replace_trick_string( const char* p_old, char* p_out, Script::CArray* p_key, Script::CArray* p_trick, uint out_buffer_size )
{
	#ifdef	__NOPT_ASSERT__
	const char *p_out_start = p_out;
	#endif
	const char *p_in = p_old;	
	while( *p_in )
	{
		int tag_num;
		if ( *p_in == '\\' && *(p_in+1) == 't' && ( *(p_in+2) <= '9' && *(p_in+2) >= '0' ) )
		{
			// printf("found the trick tag\n");
			
			// get the index
			tag_num = *(p_in+2) - 48;
			if ( *(p_in+3) <= '9' && *(p_in+3) >= '0' )
			{
				tag_num *= 10;
				tag_num += *(p_in+3) - 48;
				
				// move ahead an extra char
				p_in++;
			}
			// printf( "found a tag num of %i\n", tag_num );
			Dbg_MsgAssert( ( tag_num > 0 ) && tag_num <= (int)p_trick->GetSize(), ( "Tag value too large" ) );
			const char* p_trick_string = p_trick->GetString( tag_num - 1 );

			Dbg_MsgAssert( p_trick_string, ( "ReplaceTrickText found a \\t tag but no key combo." ) );

			sprintf( p_out, p_trick_string );
			int length = strlen( p_trick_string );
			for ( int i = 0; i < length; i++ )
				p_out++;
			
			// move past the tag
			p_in++;
			p_in++;
			p_in++;
		}
		else if ( *p_in == '\\' && *(p_in+1) == 'k' && ( *(p_in+2) <= '9' && *(p_in+2) >= '0' ) )
		{
			// printf("found the key tag\n");
			
			// get the index
			tag_num = *(p_in+2) - 48;
			if ( *(p_in+3) <= '9' && *(p_in+3) >= '0' )
			{
				tag_num *= 10;
				tag_num += *(p_in+3) - 48;
				
				// move ahead an extra char
				p_in++;
			}
			// printf( "found a tag num of %i\n", tag_num );
			Dbg_MsgAssert( ( tag_num > 0 ) && tag_num <= (int)p_key->GetSize(), ( "Tag value too large" ) );
			const char* p_key_string = p_key->GetString( tag_num - 1 );

			Dbg_MsgAssert( p_key_string, ( "ReplaceTrickText found a \\k tag but no key combo." ) );

			sprintf( p_out, p_key_string );
			int length = strlen( p_key_string );
			for ( int i = 0; i < length; i++ )
				p_out++;
			
			// move past the tag
			p_in++;
			p_in++;
			p_in++;
		}
		else
		{
			*p_out++ = *p_in++;
		}
	}
	*p_out++ = '\0';
	Dbg_MsgAssert(strlen(p_out_start) < out_buffer_size,("String overflow with\n\n%s\n\nMake it shorter than %d chars, or get a bigger buffer",p_out_start, out_buffer_size));
}

bool CGoal::ReplaceTrickText()
{
	// printf("ReplaceTrickText %s\n", Script::FindChecksumName( GetGoalId() ) );
	// see if there's a key_combo associated with this goal
	Script::CArray* p_key_combo = NULL;
	if ( !mp_params->GetArray( "key_combos", &p_key_combo, Script::NO_ASSERT ) )
	{
		// printf("no key combos\n");
		return false;
	}
	
	// Make sure we have copies of the original text.  We want
	// to update properly if they change their trick bindings.
	if ( !mp_params->ContainsComponentNamed( "original_goal_description" ) )
	{
		const char* p_ogd;
		Script::CArray* p_ogd_array;
		if ( mp_params->GetString( "goal_description", &p_ogd, Script::NO_ASSERT ) )
			mp_params->AddString( "original_goal_description", p_ogd );
		
		else if ( mp_params->GetArray( "goal_description", &p_ogd_array, Script::ASSERT ) )
			mp_params->AddArray( "original_goal_description", p_ogd_array );
	}
	if ( !mp_params->ContainsComponentNamed( "original_view_goals_text" ) )
	{
		const char* p_ovgt;
		if ( mp_params->GetString( "view_goals_text", &p_ovgt, Script::NO_ASSERT ) )
			mp_params->AddString( "original_view_goals_text", p_ovgt );
	}
	if ( !mp_params->ContainsComponentNamed( "original_goal_text" ) )
	{
		const char* p_ogt;
		if ( mp_params->GetString( "goal_text", &p_ogt, Script::NO_ASSERT ) )
			mp_params->AddString( "original_goal_text", p_ogt );
	}
	if ( !mp_params->ContainsComponentNamed( "original_key_combo_text" ) )
	{
		const char* p_okct;
		if ( mp_params->GetString( "key_combo_text", &p_okct, Script::NO_ASSERT ) )
			mp_params->AddString( "original_key_combo_text", p_okct );
	}

	// special case for cat goal
	int premade_cat_index = -1;
	if ( mp_params->ContainsFlag( CRCD(0x8e6014f6,"create_a_trick") ) )
	{
		mp_params->GetInteger( CRCD(0xb502200,"premade_cat_index"), &premade_cat_index, Script::ASSERT );
	}

	// get the new string arrays
	Script::CArray* p_key = new Script::CArray();
	Script::CArray* p_trick = new Script::CArray();
	int size = p_key_combo->GetSize();
	p_key->SetSizeAndType( size, ESYMBOLTYPE_STRING );
	p_trick->SetSizeAndType( size, ESYMBOLTYPE_STRING );
	if ( !fill_trick_and_key_combo_arrays( p_key_combo, p_key, p_trick, premade_cat_index ) )
	{
		Script::CleanUpArray( p_key );
		Script::CleanUpArray( p_trick );
		delete p_key;
		delete p_trick;
		return false;
	}
	
	const char* p_string;
	Script::CArray* p_string_array = NULL;

	char new_string[Game::NEW_STRING_LENGTH];
	
	// replace the goal description
	if ( mp_params->GetString( "original_goal_description", &p_string, Script::NO_ASSERT ) )
	{
		find_and_replace_trick_string( p_string, new_string, p_key, p_trick, Game::NEW_STRING_LENGTH );
		mp_params->AddString( "goal_description", p_string );
	}
	else if ( mp_params->GetArray( "original_goal_description", &p_string_array, Script::ASSERT ) )
	{
		int size = p_string_array->GetSize();
		Script::CArray* p_new_string_array = new Script::CArray();
		p_new_string_array->SetSizeAndType( size, ESYMBOLTYPE_STRING );
		for ( int i = 0; i < size; i++ )
		{
			p_string = p_string_array->GetString( i );
			find_and_replace_trick_string( p_string, new_string, p_key, p_trick, Game::NEW_STRING_LENGTH );
			p_new_string_array->SetString( i, Script::CreateString( new_string ) );
		}
		mp_params->AddArray( "goal_description", p_new_string_array );
		Script::CleanUpArray( p_new_string_array );
		delete p_new_string_array;
	}

	// replace the view goals text
	if ( mp_params->GetString( "original_view_goals_text", &p_string, Script::NO_ASSERT ) )
	{
		find_and_replace_trick_string( p_string, new_string, p_key, p_trick, Game::NEW_STRING_LENGTH );
		mp_params->AddString( "view_goals_text", new_string );
	}
	
	// replace the goal text
	if ( mp_params->GetString( "original_goal_text", &p_string, Script::NO_ASSERT ) )
	{
		find_and_replace_trick_string( p_string, new_string, p_key, p_trick, Game::NEW_STRING_LENGTH );
		mp_params->AddString( "goal_text", new_string );
	}

	// replace the key combo text
	if ( mp_params->GetString( "original_key_combo_text", &p_string, Script::NO_ASSERT ) )
	{
		find_and_replace_trick_string( p_string, new_string, p_key, p_trick, Game::NEW_STRING_LENGTH );
		mp_params->AddString( "key_combo_text", new_string );
	}

	// cleanup!
	Script::CleanUpArray( p_key );
	Script::CleanUpArray( p_trick );
	delete p_key;
	delete p_trick;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CGoal::GetNumberCollected()
{
	int number_collected = 0;
	
	// check for special counter goal num
	if ( mp_params->GetInteger( CRCD(0x129daa10,"number_collected"), &number_collected, Script::NO_ASSERT ) )
		return number_collected;

	// return the number of flags set
	return m_flagsSet;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CGoal::GetNumberOfFlags()
{
	int num_flags_to_win = m_numflags;
	mp_params->GetInteger( CRCD(0x1171a555,"num_flags_to_win"), &num_flags_to_win, Script::NO_ASSERT );
	if ( num_flags_to_win != 0 )
		return num_flags_to_win;
	return -1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::ColorTrickObjects( int seqIndex, bool clear )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CTrickObjectManager* p_trickObjMan = skate_mod->GetTrickObjectManager();
	
	Script::CArray* p_clusters = NULL;
	if ( !mp_params->GetArray( "kill_clusters", &p_clusters, Script::NO_ASSERT ) )
		return;

	int numClusters = p_clusters->GetSize();
	for ( int i = 0; i < numClusters; i++ )
	{
		Script::CStruct* p_temp = p_clusters->GetStructure( i );
		uint32 cluster_id;
		p_temp->GetChecksum( "id", &cluster_id, Script::ASSERT );
		
		Obj::CTrickCluster* p_trickCluster = p_trickObjMan->GetTrickCluster( cluster_id );
		if ( p_trickCluster )
		{
			if ( clear )
			{
				// printf("clearing cluster %s\n", Script::FindChecksumName( cluster_id ) );
				Obj::CTrickCluster* p_trickCluster = p_trickObjMan->GetTrickCluster( cluster_id );
				if ( p_trickCluster )
					p_trickCluster->ClearCluster( seqIndex );
			}
			else
				p_trickObjMan->GetTrickCluster( cluster_id )->ModulateTrickObjectColor( seqIndex );
		}
		else
			Dbg_Message( "WARNING: cluster %s not found\n", Script::FindChecksumName( cluster_id ) );
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CGoal::GetNumberOfTimesGoalStarted()
{
	return m_numTimesStarted;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::GetRewardsStructure( Script::CStruct *p_structure, bool give_cash )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	
	if ( !mp_params->ContainsFlag( CRCD(0x981d3ad0,"edited_goal") ) )
		pGoalManager->AddGoalPoint();

	p_structure->AddChecksum( NONAME, CRCD( 0xc7fef529, "awarded_goal_point" ) );

	// No rewards in net games, only fanfare
	if( gamenet_man->InNetGame() == false )
	{
		// special case for special trick goal
		if ( mp_params->ContainsFlag( "reward_trickslot" ) && !mp_params->ContainsFlag( CRCD( 0x8e6014f6, "create_a_trick" ) ) )
		{
			// printf("found trickslot flag\n");
			Mdl::Skate* skate_mod = Mdl::Skate::Instance();
			Obj::CSkaterProfile* p_SkaterProfile = skate_mod->GetCurrentProfile();
			int num_slots = p_SkaterProfile->GetNumSpecialTrickSlots();
			if ( num_slots <= Script::GetInteger( CRCD(0x20297d6f,"max_number_of_special_trickslots") ) )
			{
				// printf("has less than the max number of slots\n");
				int should_remove = 0;
				mp_params->GetInteger( "should_remove_trick", &should_remove, Script::NO_ASSERT );
				if ( should_remove == 1 )
				{
					// printf("was going to remove the special trick, but don't!\n");
					mp_params->AddInteger( "should_remove_trick", 0 );
					// p_reward_params->AddChecksum( NONAME, CRCD( 0x65ca5ffc, "already_added_trickslot" ) );
					mp_params->AddChecksum( NONAME, CRCD( 0x65ca5ffc, "already_added_trickslot" ) );
					mp_params->AddInteger( CRCD(0xb45fd352,"just_added_trickslot"), 1 );
				}					
			}
		}		
		
		// ************************
		// new global reward stuff
		// ************************
		// grab the list of rewards
		Script::CStruct* p_all_rewards = Script::GetStructure( "goal_rewards", Script::ASSERT );
		Script::CStruct* p_rewards = NULL;
		p_all_rewards->GetStructure( GetGoalId(), &p_rewards, Script::NO_ASSERT );
		if ( p_rewards )
		{
			// map the reward_stat flag to an int
			if ( p_rewards->ContainsFlag( "reward_stat" ) )
			{
				p_rewards->RemoveComponent( "reward_stat" );
				p_rewards->AddInteger( "reward_stat", 1 );
			}

			p_structure->AppendStructure( p_rewards );
			
			if ( give_cash )
			{
				int reward_cash;
				if ( p_rewards->GetInteger( "reward_cash", &reward_cash, Script::NO_ASSERT ) )
					p_structure->AddInteger( "cash", reward_cash );
			}
		}
		// **************************
		// old reward stuff
		// **************************
		else
		{
			if ( give_cash )
			{
				int reward_cash;
				// give 'em cash
				if ( mp_params->GetInteger( "reward_cash", &reward_cash, Script::NO_ASSERT ) )
					p_structure->AddInteger( "cash", reward_cash );
			}

			// stat points
			int stat_points;
			if ( mp_params->ContainsFlag( "reward_stat" ) )
				p_structure->AddInteger( "reward_stat", 1 );
			else if ( mp_params->GetInteger( "reward_stat", &stat_points, Script::NO_ASSERT ) )
				p_structure->AddInteger( "reward_stat", stat_points );

		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::UnlockRewardGoals()
{
	CGoalManager* pGoalManager = GetGoalManager();
	
	// unlock a new goal
	Script::CArray* p_new_goal_array = NULL;
	uint32 new_goal_checksum = 0;

	if ( mp_params->GetChecksum( "reward_goal", &new_goal_checksum, Script::NO_ASSERT ) )
		pGoalManager->UnlockGoal( new_goal_checksum );

	// check for an array of goals to unlock
	else if ( mp_params->GetArray( "reward_goal", &p_new_goal_array, Script::NO_ASSERT ) )
	{
		int num_goals_to_unlock = p_new_goal_array->GetSize();
		for ( int i = 0; i < num_goals_to_unlock; i++ )
		{
			new_goal_checksum = p_new_goal_array->GetChecksum( i );
			if ( new_goal_checksum )
				pGoalManager->UnlockGoal( new_goal_checksum );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetEndRunType( EndRunType newEndRunType )
{
	m_endRunType = newEndRunType;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoal::SetShouldDeactivateOnExpire( bool should_deactivate )
{
	m_deactivateOnExpire = should_deactivate;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::IsHorseGoal()
{
	return mp_params->ContainsFlag( CRCD(0x9d65d0e7,"horse") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::IsFindGapsGoal()
{
    return ( mp_params->ContainsFlag( CRCD( 0xc5ec08e6, "findGaps" ) ) );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoal::ShouldRequireCombo()
{
	return mp_params->ContainsFlag( CRCD(0xf8e31760,"require_combo") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoal::AppendDifficultyLevelParams()
{
	Script::CArray* pLevelParams;
	if ( mp_params->GetArray( CRCD( 0x85104ab3, "difficulty_level_params" ), &pLevelParams, Script::NO_ASSERT ) )
	{
		CGoalManager* pGoalManager = GetGoalManager();
		Dbg_Assert( pGoalManager );
		Game::GOAL_MANAGER_DIFFICULTY_LEVEL difficulty_level = pGoalManager->GetDifficultyLevel();
		Dbg_MsgAssert( difficulty_level < (int)pLevelParams->GetSize(), ( "Difficulty params array has wrong size in goal %s", Script::FindChecksumName( GetGoalId() ) ) );
		mp_params->AppendStructure( pLevelParams->GetStructure( difficulty_level ) );
		CreateGoalFlags( mp_params );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoal::RunCallbackScript( uint32 script_name )
{
#if 1
	uint32 checksum;
	if ( mp_params->GetChecksum( script_name, &checksum, Script::NO_ASSERT ) )
	{
		Script::RunScript( checksum, mp_params );
		return true;
    }
#else
	Script::CStruct* pStruct;
	if ( mp_params->GetStructure( script_name, &pStruct, Script::NO_ASSERT ) )
	{
		// target script name is required
		uint32 checksum;
		pStruct->GetChecksum( CRCD(0xb990d003,"target"), &checksum, Script::ASSERT );

		// but not the params...
		Script::CStruct* pSubStruct = NULL;
		pStruct->GetStructure( CRCD(0x7031f10c,"params"), &pSubStruct, Script::NO_ASSERT );

		if ( pParams )
			pSubStruct->AppendStructure( pParams );
		
		Script::RunScript( checksum, pSubStruct );
		return true;
	}
#endif
	else
	{
		return false;
	}
}

}	// namespace Game


================================================
FILE: Code/Sk/Modules/Skate/Goal.h
================================================
#ifndef	__MODULES_SKATE_GOAL_H
#define	__MODULES_SKATE_GOAL_H

                           
#ifndef __CORE_DEFINES_H
    #include 
#endif

#ifndef __SYS_TIMER_H
    #include  // For Tmr::Time
#endif
							  
#include 
#include 

#include 
#include 

#include 
#include 
#include 


namespace Game
{
	enum EndRunType
	{
		vGOALENDOFRUN,
		vENDOFRUN,
		vNONE,
	};

	enum
	{
		GOAL_INHERITED_FLAGS_ONE_TIMER		= ( 1 << 1  ),
		GOAL_INHERITED_FLAGS_RETRY_STAGE	= ( 1 << 2  ),
	};
	
// *************************************************************
//			Class def
// *************************************************************

class CGoalLink
{
public:
	CGoalLink();
	~CGoalLink();
	
	uint32	   	m_relative;
	CGoalLink*	mp_next;
	bool		m_beaten;
};

class CGoal : public  Lst::Node
{
public:
	CGoal();
	CGoal(Script::CStruct* pParams);
	~CGoal();

public:
	virtual bool		IsExpired();
	bool				IsExpired( bool force_end );
	virtual bool		ShouldExpire();
	
	virtual bool		IsActive();
	virtual bool		CountAsActive(); // eg, minigames don't count
    virtual void		Expire();

	virtual void		LoadSaveData( Script::CStruct* pFlags );
	virtual void		GetSaveData( Script::CStruct* pFlags );

	void				AddChild( uint32 id );
	void				AddParent( uint32 id );
	void				ResetGoalLinks();
	void				DeleteLinks( CGoalLink* p_list );

	void                MarkInvalid();
    bool                IsInvalid(); 
	void				SetTimer();
	void				AddTime( int time );
	void				SetTimer( int time );
	int					GetTimeLeft( void );
	virtual bool		ShouldUseTimer();
	virtual bool		Activate();
	virtual void		SetActive();
	virtual bool		Deactivate( bool force = false, bool affect_tree = true );
	virtual void		SetInactive();
	virtual bool		Win();
	virtual bool		Lose();
	bool				IsProGoal();
	bool				IsNetGoal();

	bool				RunCallbackScript( uint32 script_name );
	Script::CStruct*	GetParams();
	bool				EditParams(Script::CStruct* pParams);
	virtual bool		Update();

    bool                HasSeenGoal();
    void                PauseGoal();
    void                UnPauseGoal();
	virtual bool		IsPaused();
    Tmr::Time           GetTime();
    // void                CreateGoalFlag( uint32 flag );
    bool                SetGoalFlag( uint32 flag, int value );
	bool				GetCreatedGoalGap(int gapIndex);
	
    void                ResetGoalFlags();
	void				CreateGoalFlags( Script::CStruct* pParams );
    bool                GoalFlagEquals( uint32 flag, int value );
    bool                AllFlagsSet();
	bool				GoalFlagSet( uint32 flag );
    void                Init();
	void                Uninit( bool propogate = false, int recurse_level = 0 );
    bool                IsLocked();
    void                UnlockGoal();
    uint32              GetGoalId();
	uint32              GetRootGoalId();
	bool				IsEditedGoal();
	void				GetViewGoalsText( const char** p_view_goals_text );
    virtual bool        HasWonGoal();
	virtual bool        HasWonGoal( int id );
	virtual bool        HasTeamWonGoal( int team_id );
	virtual bool		HasWonStage();
	bool				IsSelected();
	virtual bool		IsInitialized() { return m_isInitialized; }

    bool                NextTourSpot();
    void                InitTrickObjects();

	void				GotTrickObject( uint32 clusterId );
	bool				IsGraffitiGoal();
	bool				ShouldLogTrickObject();
    void                GotGraffitiCluster( uint32 clusterId, int score );
    bool                IsSpecialGoal();
    bool                IsTetrisGoal();
    bool                CheckSpecialGoal();
    
    void                GotCounterObject();
    bool                CounterGoalDone();
    bool                HasProset( const char* proset_prefix );
	bool				CheckMinigameRecord( int value );
	int					GetMinigameRecord();
	bool				IsMinigame();
	bool				IsBettingGame();
	bool				IsBettingGuy();
	virtual bool		CanRetry();
	void				SetStartTime();
	void				UpdateComboTimer();

    void                SetHasSeen();
    virtual void        MarkBeaten();
	virtual void		MarkBeatenBy( int id );
	virtual void		MarkBeatenByTeam( int team_id );
	void				Select();
	void				Deselect();
    void                SetUnlocked();
    void                SetLevelNum( int levelNum );
    int                 GetLevelNum();
	void				SetStartHeight();
	bool				CheckHeightRecord();
	bool				CheckDistanceRecord();

	void 				SetQuickStartFlag();
	void				UnsetQuickStartFlag();

	bool				IsCompetition();

	bool				AllSkatersAtEndOfRun();
	bool				EndRunCalled();
	void				ClearEndRun( Script::CScript* pScript );
	bool				FinishedEndOfRun();
	bool				StartedEndOfRun();
	void				SetEndRunType( EndRunType newEndRunType );

	virtual bool		UnBeatGoal();

	void				NoValidKeyCombos();
	int 				GetRandomIndexFromKeyCombos( Script::CArray* p_KeyCombos );

	bool				AddTempSpecialTrick();
	void				RemoveTempSpecialTrick();

	bool				ReplaceTrickText();

	int					GetNumberCollected();
	int					GetNumberOfFlags();

	void				ColorTrickObjects( int seqIndex, bool clear = false );

	int					GetNumberOfTimesGoalStarted();

	void				GetRewardsStructure( Script::CStruct *p_structure, bool give_cash = true );
	void				UnlockRewardGoals();

	void				SetShouldDeactivateOnExpire( bool should_deactivate );

	bool				IsHorseGoal();
	bool				IsFindGapsGoal();

	// family stuff	
	Game::CGoalLink*	GetChildren() { return mp_children; }
	Game::CGoalLink*	GetParents() { return mp_parents; }

	void				SetInheritableFlags( int recurse_level = 0 );

	Game::CGoalPed*		mp_goalPed;

	// the inhereted flags of any root node
	// in a family branch will be set in the goals of all offspring
	uint32				m_inherited_flags;

	// chatper/stage stuff
	void				SetChapter( int chapter ) { m_chapter = chapter; }
	void				SetStage( int stage ) { m_stage = stage; }
	int					GetChapter() { return m_chapter; }
	int					GetStage() { return m_stage; }

	bool				ShouldRequireCombo();

	void				AppendDifficultyLevelParams();
protected:	
	Script::CStruct*	mp_params;
    Script::CStruct*	mp_goalFlags;
	
	EndRunType			m_endRunType;

	Game::CGoalLink*	mp_children;
	Game::CGoalLink*	mp_parents;
	
	// members stored by goal manager on level unload
    int                 m_levelNum;
	Flags< int >		m_beatenFlags;
	Flags< int >		m_teamBeatenFlags;
	int					m_numTimesLost;
	int					m_numTimesWon;
	int					m_numTimesStarted;
	Tmr::Time			m_elapsedTimeRecord;
	int					m_winScoreRecord;
    Tmr::Time           m_timeLeft;
	int					m_ownerId;
    int                 m_numflags;
    int                 m_flagsSet;
	Tmr::Time			m_elapsedTime;
	int					m_chapter;
	int					m_stage;
    
	bool				m_isInitialized;
	bool                m_hasSeen;
    bool                m_isbeat;
	bool				m_netIsBeat;
	bool				m_isselected;
    bool                m_isLocked;
	bool				m_deactivateOnExpire;
	bool				m_endRunCalled;
	bool				m_active;
    bool                m_invalid;
    bool                m_paused;
	bool				m_isOwned;
    bool                m_unlimitedTime;
};

}	// namespace Game

#endif // __MODULES_SKATE_GOAL_H


================================================
FILE: Code/Sk/Modules/Skate/GoalManager.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Modules/Skate
//* FILENAME:       GoalManager.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  1/24/2001
//****************************************************************************

// start autoduck documentation
// @DOC goalmanager
// @module goalmanager | None
// @subindex Scripting Database
// @index script | goalmanager
							   
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include        // for tetris tricks
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 


#include 
								
#include 
#include 
#include 
#include 
#include 


#include 
#include 
#include 
#include 

#include 
#include 

namespace Game
{

	extern bool fill_trick_and_key_combo_arrays( Script::CArray* p_key_combos, Script::CArray* p_key_combo_strings, Script::CArray* p_trick_names, int premade_cat_index );
	extern void find_and_replace_trick_string( const char* p_old, char* p_out, Script::CArray* p_key, Script::CArray* p_trick, uint out_buffer_size );
	
// TODO:
// Uber time limit should be its own goal?
// Each of the skate letters can either be its own goal
// or maybe we can grab the goal state in script somehow

// maybe there's some kind of goal factory

// TODO:  Maybe the script should be spawned so that it can wait a while...
// or maybe it could run a script that spawns a script...
// or maybe a combination of the two?

// TODO:  Edit goal.  changes the state of the goal, maybe to track flags and such?
// or maybe a thing can be made of 5 subgoals?

// TODO:  Need a way of binding the goal to a specific player 

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
/*****************************************************************************
**								Public Functions							**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CGoalManager::CGoalManager()
{
    m_lastGoal = 0;
    m_graffitiMode = 0;
    m_goalPoints = 0;
	m_totalGoalPointsEarned = 0;
    m_cash = 0;
	m_numGoalsBeaten = 0;
	m_isPro = false;
	// m_proChallengesUnlocked = false;
	m_canStartGoal = true;

	m_currentChapter = 0;
	m_currentStage = 0;
	m_sponsor = 0;
	mp_team = new Script::CStruct();

    mp_goalFlags = new Script::CStruct();
	// mp_proSpecificChallenges = new Script::CStruct();
	// mp_proSpecificChallenges->AppendStructure( Script::GetStructure( "goal_pro_specific_challenges_beaten", Script::ASSERT ) );

	mp_difficulty_levels = new Script::CStruct();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CGoalManager::~CGoalManager()
{
	// delete all the goals in the list
	RemoveAllGoals();
    delete mp_goalFlags;
	// delete mp_proSpecificChallenges;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::AddGoal( uint32 goalId, Script::CStruct* pParams )
{
	// don't add non-minigames in free skate
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	bool free_skate = skate_mod->GetGameMode()->IsTrue( CRCD(0x337e7779,"is_freeskate") );
	if ( !CFuncs::ScriptInMultiplayerGame( NULL, NULL ) && free_skate && !pParams->ContainsFlag( CRCD(0x6bae094c,"minigame") ) )
		return false;

	if ( skate_mod->GetGameMode()->IsTrue( CRCD( 0x353961d7, "is_creategoals" ) ) && !pParams->ContainsFlag( CRCD( 0x981d3ad0, "edited_goal" ) ) )
		return false;

	// printf("addGoal called with goalId %x\n", goalId);
	
	Dbg_MsgAssert( !m_goalList.GetItem( goalId ), ( "duplicate goal id 0x%x (%s), please check your script", goalId, Script::FindChecksumName( goalId ) ) );
	pParams->AddChecksum( CRCD(0x9982e501,"goal_id"), goalId );
	
	// figure the type
	CGoal* pGoal = NULL;
	if ( pParams->ContainsFlag( CRCD(0xc18fef36,"career_only") ) )
	{   
		if( CFuncs::ScriptInMultiplayerGame( NULL, NULL ))
		{
			return false;
		}
	}
	if ( pParams->ContainsFlag( CRCD(0x4af5d34e,"competition") ) )
	{
		// printf("adding comp goal\n");
		pGoal = new CCompetitionGoal( pParams );
	}
	else if ( pParams->ContainsFlag( CRCD(0xd15ea00,"net") ) )
	{
		// printf("adding net goal\n");
		pGoal = new CNetGoal( pParams );
	}
	else if ( pParams->ContainsFlag( CRCD(0x25904450,"race") ) )
	{
		// printf("adding race goal\n");
		pGoal = new CRaceGoal( pParams );
	}
	else if ( pParams->ContainsFlag( CRCD(0x6bae094c,"minigame") ) )
	{
		// printf("adding minigame\n");
		pGoal = new CMinigame( pParams );
	}
	else if ( pParams->ContainsFlag( CRCD(0x8cfee956,"betting_guy") ) )
	{
		// printf("adding betting guy\n");
		pGoal = new CBettingGuy( pParams );
	}
	else if ( pParams->ContainsFlag( CRCD(0x4147922b,"tetris") ) )
	{
		// printf("adding skatetris goal\n");
		pGoal = new CSkatetrisGoal( pParams );
	}
	else if ( pParams->ContainsFlag( CRCD(0x9d65d0e7,"horse") ) )
	{
		// printf("adding horse goal\n");
		pGoal = new CHorseGoal( pParams );
	}
	else if ( pParams->ContainsFlag( CRCD( 0xc5ec08e6, "findGaps" ) ) )
	{
		pGoal = new CFindGapsGoal( pParams );
	}
	else if ( pParams->ContainsFlag( CRCD( 0x7dbb41dd, "Film" ) ) )
	{
		pGoal = new CFilmGoal( pParams );
	}
	else if ( pParams->ContainsFlag( CRCD( 0x8e6014f6, "create_a_trick" ) ) )
	{
		pGoal = new CCatGoal( pParams );
	}
	else
	{
		pGoal = new CGoal( pParams );
	}
	Dbg_Assert( pGoal );

	m_goalList.PutItem( goalId, pGoal );

	pGoal->AppendDifficultyLevelParams();
	Script::CStruct* pGoalParams = pGoal->GetParams();

	// add children
	uint32 childId;
	Script::CArray* p_children;
	if ( pGoalParams->GetArray( CRCD( 0x5e684e45, "children" ), &p_children, Script::NO_ASSERT ) )
	{
		int size = p_children->GetSize();
		Dbg_MsgAssert( size > 0, ( "0 size children array for goal %s", Script::FindChecksumName( goalId ) ) );
		Dbg_MsgAssert( p_children->GetType() == ESYMBOLTYPE_NAME, ( "children array has wrong type for goal %s", Script::FindChecksumName( goalId ) ) );
		for ( int i = 0; i < size; i++ )
		{
			FindCircularLinks( goalId, p_children->GetChecksum( i ) );
			pGoal->AddChild( p_children->GetChecksum( i ) );
		}
	}
	else if ( pGoalParams->GetChecksum( CRCD( 0xdd4cabd6, "child" ), &childId, Script::NO_ASSERT ) )
	{
		FindCircularLinks( goalId, childId );
		pGoal->AddChild( childId );
	}
	
	if ( CFuncs::ScriptInMultiplayerGame( NULL, NULL ))
	{
		// SKATE SPECIFIC
		pGoal->GetParams()->AddString( CRCD(0x944b2900,"pro"), "NetJudge" );
		pGoal->GetParams()->AddString( CRCD(0x243b9c3b,"full_name"), Script::GetString( CRCD(0xa500bc5f,"judge_full_name") ) );
		// END SKATE SPECIFIC
	}
	else
	{
		// SKATE SPECIFIC
		const char* pFirstName;
		uint32 pro_name;
		if ( pGoal->GetParams()->GetString( CRCD(0x944b2900,"pro"), &pFirstName, Script::NO_ASSERT ) )
		{
			// printf("pro string: %s\n", pFirstName );
			Script::CStruct* pLastNames = Script::GetStructure( CRCD(0x621d1828,"goal_pro_last_name_checksums") );
			if ( pLastNames->GetChecksum( Script::GenerateCRC( pFirstName ), &pro_name, Script::NO_ASSERT ) )
			{
				// printf("got a last name: %s\n", Script::FindChecksumName( pro_name ) );
				// get the current skater's name
				Mdl::Skate * skate_mod = Mdl::Skate::Instance();
				Obj::CPlayerProfileManager* pPlayerProfileManager = skate_mod->GetPlayerProfileManager();
				Obj::CSkaterProfile* pSkaterProfile = pPlayerProfileManager->GetCurrentProfile();
				uint32 current_skater = pSkaterProfile->GetSkaterNameChecksum();
				// printf("current_skater: %s\n", Script::FindChecksumName( current_skater ) );
				if ( current_skater == pro_name )
				{
					const char* p_generic_pro_first_name = Script::GetString( CRCD(0x2245b93c,"generic_pro_first_name"), Script::ASSERT );
					pGoal->GetParams()->AddString( CRCD(0x944b2900,"pro"), p_generic_pro_first_name );
					const char* p_generic_pro_full_name = Script::GetString( CRCD(0xfe6ef7b8,"generic_pro_full_name"), Script::ASSERT );
					pGoal->GetParams()->AddString( CRCD(0x243b9c3b,"full_name"), p_generic_pro_full_name );					
				}
			}
		}
		// END SKATE SPECIFIC
	}

	return ( pGoal != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGoal* CGoalManager::GetGoal( uint32 goalId, bool assert )
{
	CGoal* pGoal = m_goalList.GetItem( goalId );
	if ( assert )
		Dbg_MsgAssert( pGoal, ( "Could not find goal %s\n", Script::FindChecksumName( goalId ) ) );
	
	return pGoal;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGoal* CGoalManager::GetGoal( Script::CStruct* pParams, bool assert )
{
	uint32 goalId = GetGoalIdFromParams( pParams, assert );
	if ( goalId )
		return GetGoal( goalId, assert );
	return NULL;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGoal*	CGoalManager::GetGoalByIndex( int index )
{
	return m_goalList.GetItemByIndex( index, NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::RemoveGoal( uint32 goalId )
{
	// TODO:  Maybe add an "AssertOnFail" flag

	CGoal* pGoal = m_goalList.GetItem( goalId );
	Dbg_Assert( pGoal );
		
	pGoal->mp_goalPed->DestroyGoalPed();
	m_goalList.FlushItem( goalId );

	if (m_lastGoal == goalId)
	{
		m_lastGoal=0;
	}
		
	// The FlushItem does not delete the CGoal* itself, it only deletes its own entry for
	// it (which is a LookupItem*) so we need to delete the pGoal here.
	delete pGoal;	
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::ActivateGoal( uint32 goalId, bool dontAssert )
{
	CGoal* pGoal = GetGoal( goalId );

    bool success = false;

	if ( !pGoal )
	{
		if ( dontAssert )
			return false;			
		else
			Dbg_MsgAssert( 0, ( "ActivateGoal could not find the goal" ) );
	}
	
    if ( pGoal )
    {
		success = pGoal->Activate();
		// set the last goal
        if ( success && pGoal->CanRetry() )
		{
			// only set this for the root node
			// BB - changed!  the retry goal option now
			// always gives you the last stage.
			
			// Game::CGoalLink* p_parents = pGoal->GetParents();
			// if ( p_parents->m_relative == 0 )
			m_lastGoal = goalId;
		}
    }
	
    // move it back and forth from the inactive to the active list
	// TODO:  Maybe add an "AssertOnFail" flag
	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::DeactivateGoal( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	if ( pGoal )
	{
		pGoal->Deactivate();
	}

	// move it back and forth from the inactive to the active list
	// would be an optimization?

 	// TODO:  Maybe add an "AssertOnFail" flag

	return ( pGoal != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::ClearLastGoal( void )
{
	m_lastGoal = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::WinGoal( uint32 goalId )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	if ( pGoal )
	{
        // don't let them retry this goal
		// only if it's a leaf node
        if ( pGoal->GetChildren()->m_relative == 0 )
			m_lastGoal = 0;

		// we always want to call win, but if they
		// already beat it, we don't want to give them 
		// double credit
		if ( !pGoal->HasWonGoal() )
		{
			// Report winning leaf node goals
			if( ( pGoal->GetChildren()->m_relative == 0 ) &&
				( gamenet_man->InNetGame()))
			{
				Net::Client* client;
				Net::MsgDesc msg_desc;

				Dbg_Printf( "*** Reporting won goal\n" );
				// TODO: This should use the appropriate client for the skater completing this trick.
				// Assuming client Zero will probably cause problems in split-screen games
				client = gamenet_man->GetClient( 0 );
				Dbg_Assert( client );

				GameNet::MsgBeatGoal msg;

				msg.m_GameId = gamenet_man->GetNetworkGameId();
				msg.m_GoalId = pGoal->GetRootGoalId();
				
				Dbg_Printf( "**** Beat GOAL 0x%x  ROOT: 0x%x\n", pGoal->GetGoalId(), pGoal->GetRootGoalId());

				msg_desc.m_Data = &msg;
				msg_desc.m_Length = sizeof( GameNet::MsgBeatGoal );
				msg_desc.m_Id = GameNet::MSG_ID_BEAT_GOAL;
				msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
				msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
				client->EnqueueMessageToServer( &msg_desc );
			}
			else
			{
				// increase num goals beaten if this is a leaf node.
				if ( pGoal->GetChildren()->m_relative == 0 )
					if ( !pGoal->GetParams()->ContainsFlag( CRCD(0x981d3ad0,"edited_goal") ) )
						m_numGoalsBeaten++;
			}
			
		}
		
		// In net games, "won" goals need to be approved by the server
		// Update: Win goals locally now to close the window where time can expire waiting for server's approval
		pGoal->Win();
	}

	/*
	if( gamenet_man->InNetGame() == false )
	{
		if ( !m_isPro && ( m_numGoalsBeaten >= m_numGoalsToPro ) )
		{
			TurnPro();
		}
	}
	*/

	return ( pGoal != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::LoseGoal( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	// expire takes priority over fail
	if ( pGoal )
	{
		pGoal->Lose();
	}

	return ( pGoal != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::EditGoal( uint32 goalId, Script::CStruct* pParams )
{
	CGoal* pGoal = GetGoal( goalId );

	if ( pGoal )
	{
		pGoal->EditParams( pParams );
	}
			 
	// should edit the state of the goal here...
	
	// TODO:  Maybe add an "AssertOnFail" flag

	return ( pGoal != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::SetGoalTimer( uint32 goalId, int time )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	if ( pGoal )
	{
		if (time == -1)
		{
			pGoal->SetTimer();
		}
		else
		{
			pGoal->SetTimer(time);
		}
	}
			 
	// should edit the state of the goal here...
	
	// TODO:  Maybe add an "AssertOnFail" flag

	return ( pGoal != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::GoalIsActive( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId, false );

	if ( pGoal )
	{
		return pGoal->IsActive();
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::GetActiveGoal( bool ignore_net_goals )
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		if( ignore_net_goals && pGoal->IsNetGoal())
		{
			continue;
		}
		if ( pGoal->IsActive() )
		{
            return i;
        }
    }
    return -1;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::GetNumGoals()
{
	return m_goalList.getSize();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::GetNumActiveGoals( bool count_all )
{
	int numActiveGoals = 0;

	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		if( pGoal->IsNetGoal() && !count_all )
		{
			continue;
		}
		if ( count_all && pGoal->IsActive() )
			numActiveGoals++;
		else if ( pGoal->CountAsActive() )
			numActiveGoals++;
	}
	return numActiveGoals;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::GetNumSelectedGoals()
{
	int num_selected = 0;

	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		// this is now primarily used for determining if the timer should be on-screen
		if ( pGoal->IsSelected() )
		{
			num_selected++;
		}
	}
	return num_selected;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::RemoveAllGoals()
{
	// can't retry!
    m_lastGoal = 0;
    
	// kill the ped
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		
		pGoal->mp_goalPed->DestroyGoalPed();
	}

	// delete all the goals in the list
	Lst::LookupTableDestroyer destroyer( &m_goalList );	
	destroyer.DeleteTableContents();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::DeactivateAllGoals( bool include_net_goals )
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		if ( pGoal->IsActive() )
		{
			pGoal->Deactivate( include_net_goals );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::UninitializeGoal( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );
	pGoal->Uninit();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::UninitializeGoalTree( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );
	CGoalLink* pGoalLink = pGoal->GetParents();
	while ( pGoalLink && pGoalLink->m_relative != 0 )
	{
		pGoal = GetGoal( pGoalLink->m_relative );
		Dbg_Assert( pGoal );
		pGoalLink = pGoal->GetParents();
	}
	
	// the resulting goal should be the root node of the family tree
	pGoal->Uninit( true );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::UninitializeAllGoals()
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		pGoal->Uninit();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::DeactivateAllMinigames()
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		if ( pGoal->IsMinigame() && pGoal->IsActive() )
			pGoal->Deactivate();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::InitializeAllMinigames()
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		if( pGoal->IsMinigame())
		{
			pGoal->RunCallbackScript( vINIT );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::InitializeAllGoals()
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
			
		if( gamenet_man->InNetGame() && !pGoal->IsNetGoal())
		{
			continue;
		}

		if ( !pGoal->IsLocked() && !pGoal->HasWonGoal() )
		{
			// check if this is a child of another goal
			CGoalLink* pGoalParents = pGoal->GetParents();

			// support an always_initialize_goal flag, which
			// allows designers to have goals that aren't tied to
			// a chapter/stage
			Script::CStruct* pGoalParams = pGoal->GetParams();
			
			int goal_chapter = pGoal->GetChapter();
			int goal_stage = pGoal->GetStage();
			
			// printf("%s: %i.%i\n", Script::FindChecksumName( pGoal->GetGoalId() ), goal_chapter, goal_stage );
			if ( pGoalParents->m_relative == 0 )
			{
				// if the goal isn't listed in chapter_info.q at all,
				// the chapter and stage will default to -1.
				// Always create these goals
				if ( ( goal_chapter == m_currentChapter && goal_stage == m_currentStage ) 
					 || pGoalParams->ContainsFlag( CRCD( 0x4e863e93, "always_initialize_goal" ) )
					 || pGoalParams->ContainsFlag( CRCD( 0x981d3ad0, "edited_goal" ) ) )
				{
					pGoal->Init();
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::InitializeAllSelectedGoals()
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
			
		if ( pGoal->IsSelected())
		{       
			Script::RunScript( Script::GenerateCRC( "goal_create_proset_geom" ), pGoal->GetParams());
			pGoal->Init();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::DeselectAllGoals()
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
			
		pGoal->Deselect();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// TODO: make this smarter...it just deactivates the first
// non-minigame goal it finds
void CGoalManager::DeactivateCurrentGoal()
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		if ( pGoal->IsActive() && !pGoal->IsNetGoal() && !pGoal->IsMinigame() )
		{
			pGoal->Deactivate();
			return;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::Update()
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		if ( pGoal->IsActive() )
		{
            pGoal->Update();
        }
    }

	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

        if ( pGoal->IsInvalid() )
        {
            //Dbg_Printf("************** removing goal %p\n", pGoal->GetGoalId());
            m_goalList.FlushItem( pGoal->GetGoalId() );
            delete pGoal;
        }
/*        else 
            {
                pGoal->Update();
            }
*/
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::HasSeenGoal( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );

	if ( pGoal )
	{
		return pGoal->HasSeenGoal();
	}

	return false;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::PauseAllGoals()
{
    for ( int i = 0; i < GetNumGoals(); i++ )
    {
        uint32 key;
        CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
        Dbg_Assert( pGoal );
    
        pGoal->PauseGoal();
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::UnPauseAllGoals()
{
    for ( int i = 0; i < GetNumGoals(); i++ )
    {
        uint32 key;
        CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
        Dbg_Assert( pGoal );
    
        pGoal->UnPauseGoal();
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Tmr::Time CGoalManager::GetGoalTime()
{
    // TODO: allow some sort of goal priority 
    // right now this just finds the fist active goal
    // and returns that timer
    for (int i = 0; i < GetNumGoals(); i++) {
        uint32 key;
        CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
        Dbg_Assert( pGoal );
        
        if ( pGoal->IsActive() && pGoal->ShouldUseTimer() && !pGoal->IsExpired() )
        {
            Tmr::Time timeLeft = pGoal->GetTime();
            if ( (int)timeLeft < 0 )
                return 0;
            return timeLeft;
        }
    }
    // there are no active goals.
    return 0;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::CanRetryGoal()
{
    if ( m_lastGoal != 0 )
	{
		CGoal* pGoal = GetGoal( m_lastGoal );
		return pGoal->CanRetry();

	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::QuickStartGoal( uint32 goalId, bool dontAssert )
{
	CGoal* pGoal = GetGoal( goalId );
	if ( !pGoal )
	{
		if ( dontAssert )
			return false;
		else
			Dbg_MsgAssert( 0, ( "QuickStartGoal unable to find goal" ) );
	}
	pGoal->SetQuickStartFlag();
	bool success = pGoal->Activate();
	
	// set the last goal
	if ( success && pGoal->CanRetry() )
		m_lastGoal = goalId;

	pGoal->UnsetQuickStartFlag();
	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::RestartLastGoal() 
{
	if ( m_lastGoal != 0 )
    {
		CGoal* pGoal = GetGoal( m_lastGoal );
		Dbg_Assert( pGoal );

		// set the restart flag so the (de)activate scripts knows not to show
		// cam anims, etc.
		pGoal->SetQuickStartFlag();

		// if ( pGoal->IsActive() )
			// pGoal->Deactivate();
		DeactivateAllGoals();

		// SKATE SPECIFIC
		// kill any sk3_killscripts, as they will screw up the reset skater call
		Script::KillSpawnedScriptsThatReferTo( ( CRCD(0xdea3057b,"SK3_KillSkater") ) );
		Script::KillSpawnedScriptsThatReferTo( ( CRCD(0xb38ed6b,"SK3_Killskater_Finish") ) );
		// END SKATE SPECIFIC

		// kill any blur effect
		Script::RunScript(( CRCD(0xbda66e7d,"kill_blur") ) );
		
		// uint32 reset_checksum = 0;
        // pGoal->GetParams()->GetChecksum( "restart_node", &reset_checksum, Script::ASSERT );        
        // Mdl::Skate * p_skate = Mdl::Skate::Instance();
        // p_skate->ResetSkaters( SkateScript::FindNamedNode( reset_checksum ) );

		// clear the trigger exceptions
		if ( !pGoal->HasWonGoal() )
		{
			Script::CStruct* p_params = new Script::CStruct();
			p_params->AddChecksum( "goal_id", pGoal->GetGoalId() );
			Script::RunScript( CRCD( 0x17c2e09f, "GoalManager_ResetGoalTrigger" ), p_params );
			delete p_params;
		}
		
		pGoal->Activate();
		pGoal->UnsetQuickStartFlag();
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::RestartStage()
{
    int activeGoal = GetActiveGoal();
	if ( activeGoal == -1 )
		return;

	uint32 key;
	CGoal* pGoal = m_goalList.GetItemByIndex( activeGoal, (int*)&key );
	Dbg_Assert( pGoal );

	pGoal->SetQuickStartFlag();

	if ( pGoal->IsActive() )
	{
		pGoal->Deactivate( false, false );

		// SKATE SPECIFIC
		// kill any sk3_killscripts, as they will screw up the reset skater call
		Script::KillSpawnedScriptsThatReferTo(  CRCD(0xdea3057b,"SK3_KillSkater")  );
		Script::KillSpawnedScriptsThatReferTo( ( CRCD(0xb38ed6b,"SK3_Killskater_Finish") ) );
		// END SKATE SPECIFIC

		// kill any blur effect
		Script::RunScript( ( CRCD(0xbda66e7d,"kill_blur") ) );
		
		// uint32 reset_checksum = 0;
        // pGoal->GetParams()->GetChecksum( "restart_node", &reset_checksum, Script::ASSERT );        
        // Mdl::Skate * p_skate = Mdl::Skate::Instance();
        // p_skate->ResetSkaters( SkateScript::FindNamedNode( reset_checksum ) );

		// clear the trigger exceptions
		if ( !pGoal->HasWonGoal() )
		{
			Script::CStruct* p_params = new Script::CStruct();
			p_params->AddChecksum( "goal_id", pGoal->GetGoalId() );
			Script::RunScript( CRCD( 0x17c2e09f, "GoalManager_ResetGoalTrigger" ), p_params );
			delete p_params;
		}
		
		pGoal->Activate();
		pGoal->UnsetQuickStartFlag();
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*void CGoalManager::CreateGoalFlag( uint32 goalId, uint32 flag )
{
	CGoal* pGoal = GetGoal( goalId );
    Dbg_Assert( pGoal );
    
    pGoal->CreateGoalFlag( flag );
}*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::SetGoalFlag( uint32 goalId, uint32 flag, int value )
{
    CGoal* pGoal = GetGoal( goalId );

    if ( !pGoal )
        return false;
    if ( pGoal->SetGoalFlag( flag, value ) ) return true;
    
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::AllFlagsSet( uint32 goalId )
{
    CGoal* pGoal = GetGoal( goalId );

    Dbg_Assert( pGoal );
    return pGoal->AllFlagsSet();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::GoalFlagSet( uint32 goalId, uint32 flag )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );
	return pGoal->GoalFlagSet( flag );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CGoalManager::CreatedGapGoalIsActive()
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* p_goal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( p_goal );
		
		if (p_goal->IsActive())
		{
			Script::CStruct *p_params=p_goal->GetParams();
			Script::CArray *p_dummy=NULL;
			if (p_params->GetArray(CRCD(0x52d4489e,"required_gaps"),&p_dummy))
			{
				return true;
			}
		}	
	}	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::GetCreatedGoalGap(int gapIndex)
{
	bool return_value=false;
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* p_goal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( p_goal );
		
		return_value=p_goal->GetCreatedGoalGap(gapIndex);
	}	
	return return_value;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CGoalManager::GetGoalParams( uint32 goalId )
{
	// printf("trying to get params for goalId %x\n", goalId);
	CGoal* pGoal = GetGoal( goalId );
    Dbg_Assert( pGoal );

    return pGoal->GetParams( );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::CanStartGoal()
{
    return m_canStartGoal;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::NextRaceWaypoint( uint32 goalId )
{
    CRaceGoal* pGoal = (CRaceGoal*)GetGoal( goalId );
	Dbg_MsgAssert( pGoal, ( "Couldn't find goal: %x\n", goalId ) );

    return pGoal->NextRaceWaypoint( goalId );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::NextTourSpot( uint32 goalId )
{
    CGoal* pGoal = GetGoal( goalId );
    Dbg_Assert( pGoal );

    return pGoal->NextTourSpot();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::InitTrickObjects( uint32 goalId )
{
    CGoal* pGoal = GetGoal( goalId );
    Dbg_Assert( pGoal );

    pGoal->InitTrickObjects();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::SetGraffitiMode( int mode )
{
    m_graffitiMode = mode;

/*    // do we need to reset the scores?
    if ( mode == 0 )
    {
        Mdl::Skate* skate_mod = Mdl::Skate::Instance();
        Obj::CTrickObjectManager* p_TrickObjectManager = skate_mod->GetTrickObjectManager();
        p_TrickObjectManager->ResetAllTrickObjects();
    }
*/
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::ShouldLogTrickObject()
{
    int activeGoal = GetActiveGoal( true );
	if ( activeGoal == -1 )
		return false;

	uint32 key;
	CGoal* pGoal = m_goalList.GetItemByIndex( activeGoal, (int*)&key );
	Dbg_Assert( pGoal );
	return pGoal->ShouldLogTrickObject();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::GotTrickObject( uint32 clusterId, int score )
{
	int activeGoal = GetActiveGoal( true );
	if ( activeGoal == -1 )	// no active goal
		return;
	
	uint32 key;
	CGoal* pGoal = m_goalList.GetItemByIndex( activeGoal, (int*)&key );
	Dbg_Assert( pGoal );

	if ( pGoal->IsGraffitiGoal() )
		pGoal->GotGraffitiCluster( clusterId, score );
	else
		pGoal->GotTrickObject( clusterId );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::CheckTrickText()
{
    int activeGoal = GetActiveGoal( true );
    
    if ( activeGoal == -1 )
        return;

    uint32 key;
    CGoal* pGoal = m_goalList.GetItemByIndex( activeGoal, (int*)&key );

    Dbg_Assert( pGoal );

    Script::CStruct* p_goal_params = pGoal->GetParams();
	if ( pGoal->IsSpecialGoal() || p_goal_params->ContainsFlag( CRCD(0x8e6014f6,"create_a_trick") ) )
    {		
		// don't check special goals that are setup with gaps
		if ( !p_goal_params->ContainsFlag( "special_gap" ) )
		{
			if ( pGoal->CheckSpecialGoal() )
			{
				uint32 goalId = pGoal->GetGoalId();
				WinGoal( goalId );
			}
		}
    }
    else if ( pGoal->IsTetrisGoal() )
    {
        CSkatetrisGoal* p_temp = (CSkatetrisGoal*)pGoal;
		p_temp->CheckTetrisTricks();
    }
	else if ( pGoal->IsFindGapsGoal() )
	{
		CFindGapsGoal *p_temp = (CFindGapsGoal*)pGoal;
		p_temp->CheckGaps();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::UnlockGoal( uint32 goalId )
{
    CGoal* pGoal = GetGoal( goalId );
    Dbg_MsgAssert( pGoal, ("Couldn't find goal to unlock") );

    pGoal->UnlockGoal();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::HasWonGoal( uint32 goalId )
{
    CGoal* pGoal = GetGoal( goalId, false );

    if ( pGoal )
		return pGoal->HasWonGoal();
	else
	{
		// check goal manager params struct in case the goal was in
		// another level
		int hasWonGoal = 0;
		Script::CStruct* pGoalParams;
		if ( mp_goalFlags->GetStructure( goalId, &pGoalParams, Script::NO_ASSERT ) )
			pGoalParams->GetInteger( CRCD(0x49807745,"hasBeaten"), &hasWonGoal, Script::ASSERT );

		return ( hasWonGoal != 0 );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::GoalIsSelected( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
    Dbg_Assert( pGoal );

    return pGoal->IsSelected();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::ToggleGoalSelection( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
    Dbg_Assert( pGoal );

	if( pGoal->IsSelected())
	{
		pGoal->Deselect();
	}
	else
	{
		pGoal->Select();
	}                   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::GotCounterObject( uint32 goalId )
{
    CGoal* pGoal = GetGoal( goalId );
    Dbg_Assert( pGoal );

    if ( pGoal->IsActive() )
		pGoal->GotCounterObject();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::CounterGoalDone( uint32 goalId )
{
    CGoal* pGoal = GetGoal( goalId );
    Dbg_Assert( pGoal );

    return pGoal->CounterGoalDone();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::SpendGoalPoints( int num )
{
    m_goalPoints -= num;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::AddGoalPoint()
{
    m_goalPoints++;
	m_totalGoalPointsEarned++;
	Script::RunScript( CRCD(0x888ef05e,"GoalManager_ShowGoalPoints") );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::HasGoalPoints( int num )
{
    return ( m_goalPoints >= num );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::GetNumberOfGoalPoints()
{
    return m_goalPoints;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::GetTotalNumberOfGoalPointsEarned()
{
    return m_totalGoalPointsEarned;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::ClearGoalPoints()
{
	m_goalPoints = 0;
	m_totalGoalPointsEarned = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::AddCash( int amount )
{
    m_cash += amount;
	m_totalCash += amount;
	if ( GetNumActiveGoals() < 1 )
	{
		Script::RunScript( CRCD(0x888ef05e,"GoalManager_ShowGoalPoints") );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::SpendCash( int amount )
{
    if ( amount <= m_cash)
    {
        m_cash -= amount;
        return true;
    } 
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::GetCash( bool get_total )
{
    if ( get_total )
		return m_totalCash;
	return m_cash;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::HasBeatenGoalWithProset( const char* proset_prefix )
{
	for ( int i = 0; i < GetNumGoals(); i++ )
    {
        uint32 key;
        CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
        Dbg_Assert( pGoal );

        if ( pGoal->HasWonGoal() && pGoal->HasProset( proset_prefix ) )
        {
            return true;
        }
    }
    return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::LevelUnload()
{
	// don't do this in net games!
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	if ( gamenet_man->InNetGame() )
		return;
	
	// save goal manager params
	Script::CStruct *p_manager_params = new Script::CStruct();

	p_manager_params->AddInteger( CRCD(0x6abb6fcb,"goalPoints"), m_goalPoints );
	p_manager_params->AddInteger( CRCD(0x79ed60aa,"totalGoalPointsEarned"), m_totalGoalPointsEarned );
	p_manager_params->AddInteger( CRCD(0xf9461a46,"cash"), m_cash );
	p_manager_params->AddInteger( CRCD(0x875e89bb,"total_cash"), m_totalCash );
	// p_manager_params->AddInteger( "numGoalsToPro", m_numGoalsToPro );
	p_manager_params->AddInteger( CRCD(0x46f71d38,"numGoalsBeaten"), m_numGoalsBeaten );

	p_manager_params->AddInteger( CRCD(0xf884773c,"currentChapter"), m_currentChapter );
	p_manager_params->AddInteger( CRCD(0xaf1575eb,"currentStage"), m_currentStage );
	p_manager_params->AddChecksum( CRCD(0x7e73362b,"sponsor"), m_sponsor );
	p_manager_params->AddStructure( CRCD(0x3b1f59e0,"team"), mp_team );
	p_manager_params->AddStructure( CRCD(0xb13d98d3,"difficulty_levels"), mp_difficulty_levels );
	
/*	int proChallengesUnlocked = 0;
	if ( m_proChallengesUnlocked )
		proChallengesUnlocked = 1;
	p_manager_params->AddInteger( CRCD(0x24c226fa,"proChallengesUnlocked"), proChallengesUnlocked );
*/
	int isPro = 0;
	if ( m_isPro )
		isPro = 1;
	p_manager_params->AddInteger( CRCD(0x40801d41,"isPro"), isPro );

	// p_manager_params->AddStructure( CRCD(0xf8478579,"proSpecificChallenges"), mp_proSpecificChallenges );

	mp_goalFlags->AddStructure( CRCD(0x23d4170a,"GoalManager_Params"), p_manager_params );

	delete p_manager_params;

	for ( int i = 0; i < GetNumGoals(); i++ )
    {
        uint32 key;
        CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
        Dbg_Assert( pGoal );
		Script::CStruct* pGoalParams = pGoal->GetParams();
		
		if ( !pGoalParams->ContainsFlag( CRCD(0x3d1cab0b,"null_goal") ) && !pGoal->IsEditedGoal() ) // K: Don't add edited goals
		{
			Script::CStruct* p_temp = new Script::CStruct();
			pGoal->GetSaveData( p_temp );
			mp_goalFlags->AddStructure( pGoal->GetGoalId(), p_temp );
			delete p_temp;
		}	
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::LevelLoad()
{
    Script::CComponent* p_comp = NULL;
    p_comp = mp_goalFlags->GetNextComponent( p_comp );

    while ( p_comp )
    {
        Script::CComponent* p_next = mp_goalFlags->GetNextComponent( p_comp );

		// special goal manager params structure
		if ( p_comp->mNameChecksum == CRCD( 0x23d4170a, "GoalManager_Params" ) )
		{
			p_comp->mpStructure->GetInteger( CRCD(0x6abb6fcb,"goalPoints"), &m_goalPoints, Script::NO_ASSERT );
			p_comp->mpStructure->GetInteger( CRCD(0x79ed60aa,"totalGoalPointsEarned"), &m_totalGoalPointsEarned, Script::NO_ASSERT );
			p_comp->mpStructure->GetInteger( CRCD(0xf9461a46,"cash"), &m_cash, Script::NO_ASSERT );
			p_comp->mpStructure->GetInteger( CRCD(0x875e89bb,"total_cash"), &m_totalCash, Script::NO_ASSERT );
			// p_comp->mpStructure->GetInteger( "numGoalsToPro", &m_numGoalsToPro, Script::NO_ASSERT );
			p_comp->mpStructure->GetInteger( CRCD(0x46f71d38,"numGoalsBeaten"), &m_numGoalsBeaten, Script::NO_ASSERT );

			int auto_change_chapter_and_stage = 0;
			auto_change_chapter_and_stage = Script::GetInteger( CRCD(0x5a1d8804,"auto_change_chapter_and_stage"), Script::NO_ASSERT );
			if ( auto_change_chapter_and_stage == 0 )
			{
				p_comp->mpStructure->GetInteger( CRCD(0xf884773c,"currentChapter"), &m_currentChapter, Script::NO_ASSERT );
				p_comp->mpStructure->GetInteger( CRCD(0xaf1575eb,"currentStage"), &m_currentStage, Script::NO_ASSERT );				
			}
			p_comp->mpStructure->GetChecksum( CRCD(0x7e73362b,"sponsor"), &m_sponsor, Script::ASSERT );
			
			Script::CStruct* p_copy;
			p_comp->mpStructure->GetStructure( CRCD(0x3b1f59e0,"team"), &p_copy, Script::ASSERT );
			mp_team->Clear();
			mp_team->AppendStructure( p_copy );

			Script::CStruct* p_diff_copy;
			p_comp->mpStructure->GetStructure( CRCD(0xb13d98d3,"difficulty_levels"), &p_diff_copy, Script::ASSERT );
			mp_difficulty_levels->Clear();
			mp_difficulty_levels->AppendStructure( p_diff_copy );
			
			int isPro;
			p_comp->mpStructure->GetInteger( CRCD(0x40801d41,"isPro"), &isPro, Script::ASSERT );
			if ( isPro != 0 )
				TurnPro();
			
/*			int proChallengesUnlocked;
			p_comp->mpStructure->GetInteger( CRCD(0x24c226fa,"proChallengesUnlocked"), &proChallengesUnlocked, Script::ASSERT );
			if ( proChallengesUnlocked != 0 )
				m_proChallengesUnlocked = true;

			Script::CStruct* p_copy;
			p_comp->mpStructure->GetStructure( CRCD(0xf8478579,"proSpecificChallenges"), &p_copy, Script::ASSERT );
			mp_proSpecificChallenges->AppendStructure( p_copy );
*/
		}
		else 
		{
			CGoal* pGoal = GetGoal( p_comp->mNameChecksum, false );
			if ( pGoal )
				pGoal->LoadSaveData( p_comp->mpStructure );
		}
        
		p_comp = p_next;
    }
	
	// make sure to unlock any pro goals (they may have turned pro in another level)
	if ( m_isPro )
		TurnPro();

	// ...and unlock any pro challenges
	// if ( m_proChallengesUnlocked )
		// UnlockProSpecificChallenges();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::NumGoalsBeatenBy( int obj_id )
{
	int i, num_beaten;
	CGoal* pGoal;

	num_beaten = 0;
	for ( i = 0; i < GetNumGoals(); i++ )
    {
        uint32 key;
        pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
        Dbg_Assert( pGoal );

		if( pGoal->GetParents()->m_relative != 0 )
		{
			continue;
		}

		if( pGoal->HasWonGoal( obj_id ))
		{
			num_beaten++;
		}
	}

	return num_beaten;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::NumGoalsBeatenByTeam( int team_id )
{
	int i, num_beaten;
	CGoal* pGoal;

	num_beaten = 0;
	for ( i = 0; i < GetNumGoals(); i++ )
    {
        uint32 key;
        pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
        Dbg_Assert( pGoal );

		if( pGoal->GetParents()->m_relative != 0 )
		{
			continue;
		}
		if( pGoal->HasTeamWonGoal( team_id ))
		{
			num_beaten++;
		}
	}

	return num_beaten;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::NumGoalsBeaten()
{
	int i, num_beaten;
	CGoal* pGoal;

	num_beaten = 0;
	for ( i = 0; i < GetNumGoals(); i++ )
    {
        uint32 key;
        pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
        Dbg_Assert( pGoal );

		if( pGoal->GetParents()->m_relative != 0 )
		{
			continue;
		}
		if( pGoal->HasWonGoal())
		{
			num_beaten++;
		}
	}

	return num_beaten;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CGoalManager::NumGoalsBeatenInLevel( int levelNum, bool count_pro_specific_challenges )
{
    // store all the goal info in the goalmanager's struct
    // so we can grab it all in one place
    LevelUnload();
    
    int numbeat = 0;
    Script::CComponent* p_comp = mp_goalFlags->GetNextComponent( NULL );
    Script::CComponent* p_next = NULL;
    while ( p_comp )
    {
        // printf("checking p_comp\n");
        p_next = mp_goalFlags->GetNextComponent( p_comp );

        int p_comp_levelNum = 0;
        if ( p_comp->mpStructure->GetInteger( CRCD(0x1a4cde06,"levelNum"), &p_comp_levelNum, Script::NO_ASSERT ) )
        {
            // printf("found levelNum\n");
/*			if ( !count_pro_specific_challenges )
			{
				int isProSpecificChallenge;
				p_comp->mpStructure->GetInteger( CRCD(0x3dbe3121,"isProSpecificChallenge"), &isProSpecificChallenge, Script::ASSERT );
				if ( isProSpecificChallenge == 1 )
				{
					p_comp = p_next;
					continue;
				}
			}
*/
			int hasBeaten = 0;
			p_comp->mpStructure->GetInteger( CRCD(0x49807745,"hasBeaten"), &hasBeaten, Script::NO_ASSERT );
            if ( p_comp_levelNum == levelNum && hasBeaten )
            {
                numbeat++;
            }
        }
        p_comp = p_next;
    }
    return numbeat;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoalManager::UnlockAllGoals()
{
    m_isPro = true;
	// m_proChallengesUnlocked = true;

	for ( int i = 0; i < GetNumGoals(); i++ )
    {
        uint32 key;
        CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
        Dbg_Assert( pGoal );
        // pGoal->UnlockGoal();
		pGoal->SetHasSeen();
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::TurnPro()
{
	m_isPro = true;
	// m_proChallengesUnlocked = true;

	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		
		if ( pGoal->IsProGoal() && pGoal->IsLocked() )
		{
			pGoal->UnlockGoal();
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoalManager::SetStartTime( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->SetStartTime();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoalManager::CheckMinigameRecord( uint32 goalId, int value )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	return pGoal->CheckMinigameRecord( value );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::UpdateComboTimer( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->UpdateComboTimer();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::SetStartHeight( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->SetStartHeight();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::CheckHeightRecord( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	return pGoal->CheckHeightRecord();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::CheckDistanceRecord( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	return pGoal->CheckDistanceRecord();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::PlayGoalStartStream( Script::CStruct* pParams )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->mp_goalPed->PlayGoalStartStream( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::PlayGoalWaitStream( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->mp_goalPed->PlayGoalWaitStream();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::PlayGoalStream( Script::CStruct* pParams )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	// if we got a specific stream, just play it and quit
	uint32 stream_checksum = 0;
	pParams->GetChecksum( CRCD(0x1a4e4fb9,"vo"), &stream_checksum, Script::NO_ASSERT );
	if ( stream_checksum )
	{
		pGoal->mp_goalPed->PlayGoalStream( stream_checksum );
		return;
	}

	// check for type
	const char* type = NULL;
	if ( pParams->GetString( CRCD(0x7321a8d6,"type"), &type, Script::NO_ASSERT ) )
	{		
		pGoal->mp_goalPed->PlayGoalStream( type, pParams );
		return;
	}		
	
	// you gotta have one or the other
	if ( !type && !stream_checksum )
		Dbg_MsgAssert( 0, ("GoalManager_PlayGoalStream called without a type or vo checksum") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::StopCurrentStream( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->mp_goalPed->StopCurrentStream();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::PlayGoalWinStream( Script::CStruct* pParams )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->mp_goalPed->PlayGoalWinStream( pParams );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::PauseGoal( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->PauseGoal();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::UnPauseGoal( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->UnPauseGoal();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::PauseCompetition( uint32 goalId )
{
	Game::CCompetitionGoal* pComp = (Game::CCompetitionGoal*)GetGoal( goalId );
	Dbg_Assert( pComp );

	pComp->PauseCompetition();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::UnPauseCompetition( uint32 goalId )
{
	Game::CCompetitionGoal* pComp = (Game::CCompetitionGoal*)GetGoal( goalId );
	Dbg_Assert( pComp );

	pComp->UnPauseCompetition();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::UnBeatAllGoals()
{	
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		if ( pGoal->UnBeatGoal() && !gamenet_man->InNetGame() )
		{
			// m_numGoalsToPro++;
			m_numGoalsBeaten--;
			m_goalPoints--;
			m_totalGoalPointsEarned--;
		}
	}
	// clear the goal manager's memory of goals too
	if ( !gamenet_man->InNetGame() )
		mp_goalFlags->Clear();

	if ( GetNumActiveGoals() == 0 )
	{
		if( CFuncs::ScriptInMultiplayerGame( NULL, NULL ) == false )
		{
			Script::RunScript( CRCD(0x888ef05e,"GoalManager_ShowGoalPoints") );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::AddViewGoalsList()
{
	
	int numGoals = GetNumGoals();
	Script::CArray* p_ordered_goals = new Script::CArray();
	Script::CArray* p_unordered_goals = new Script::CArray();	
	p_ordered_goals->SetSizeAndType( numGoals, ESYMBOLTYPE_NAME );
	p_unordered_goals->SetSizeAndType( numGoals, ESYMBOLTYPE_NAME );
	
	int num_ordered_goals = 0;
	int num_unordered_goals = 0;
	
	// divide the list into goals with ordinals and goals without
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		if ( pGoal->GetParams()->ContainsComponentNamed( CRCD(0x9b048fc,"ordinal") ) )
		{
			p_ordered_goals->SetChecksum( num_ordered_goals, pGoal->GetGoalId() );
			num_ordered_goals++;
		}
		else
		{
			p_unordered_goals->SetChecksum( num_unordered_goals, pGoal->GetGoalId() );
			num_unordered_goals++;
		}			
	}

	// sort the list of goals with ordinals
	// printf("there are %i ordered goals\n", num_ordered_goals);
	for ( int i = 0; i < num_ordered_goals; i++ )
	{
		// inefficient sorting, but there's only ~20 things - brad
		for ( int j = i; j < num_ordered_goals; j++ )
		{
			uint32 temp = p_ordered_goals->GetChecksum( j );
			CGoal* p_temp_goal = GetGoal( temp );
			Dbg_Assert( p_temp_goal );
			int ordinal_check;
			p_temp_goal->GetParams()->GetInteger( CRCD(0x9b048fc,"ordinal"), &ordinal_check, Script::ASSERT );
			Dbg_MsgAssert( ordinal_check - 1 < num_ordered_goals, ( "Ordinal value %i too big.", ordinal_check ) );
			if ( ordinal_check - 1 == i )
			{
				if ( j == i )
					break;

				// swap them				
				uint32 temp2 = p_ordered_goals->GetChecksum( i );
				int double_check;
				GetGoal( temp2 )->GetParams()->GetInteger( CRCD(0x9b048fc,"ordinal"), &double_check, Script::ASSERT );
				Dbg_MsgAssert( double_check != i, ( "Found ordinal %i twice", double_check ) );

				p_ordered_goals->SetChecksum( i, temp );
				p_ordered_goals->SetChecksum( j, temp2 );
				break;
			}
		}
	}
	
	// tack on the list of unordered goals
	for ( int i = 0; i < num_unordered_goals; i++ )
		p_ordered_goals->SetChecksum( i + num_ordered_goals, p_unordered_goals->GetChecksum( i ) );
	
	// populate the menu
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 goalId = p_ordered_goals->GetChecksum( i );
		CGoal* pGoal = GetGoal( goalId );
		Dbg_Assert( pGoal );
		Script::CStruct* pGoalParams = pGoal->GetParams();

		const char* p_view_goals_text = NULL;
		pGoal->GetViewGoalsText( &p_view_goals_text );
		
		// skip goals without an associated text (eg, minigames)
		if ( !p_view_goals_text )
			continue;

		// skip pro goals if we're not pro
		if ( !IsPro() && pGoalParams->ContainsFlag( CRCD(0xd303a1a3,"pro_goal") ) )
			continue;

		// skip pro challenges if we haven't unlocked them.
		// if ( !ProSpecificChallengesUnlocked() && pGoalParams->ContainsFlag( CRCD(0xe0f96410,"pro_specific_challenge") ) )
			// continue;
		
		// skip pro specific challenges if we're not the right skater
/*		if ( !pGoal->HasWonGoal() && !IsPro() && pGoalParams->ContainsFlag( CRCD(0xe0f96410,"pro_specific_challenge") ) )
		{
			// check the current skater against the listed pro
			Script::CStruct* pLastNames = Script::GetStructure( CRCD(0x621d1828,"goal_pro_last_name_checksums"), Script::ASSERT );
			const char* pProChallengeProName;
			pGoalParams->GetString( CRCD(0x4a91eceb,"pro_challenge_pro_name"), &pProChallengeProName, Script::ASSERT );
			uint32 last_name = 0;
			pLastNames->GetChecksum( Script::GenerateCRC( pProChallengeProName ), &last_name, Script::NO_ASSERT );
			Mdl::Skate * pSkate = Mdl::Skate::Instance();
			Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();
			if ( last_name && last_name != pSkaterProfile->GetSkaterNameChecksum() )
				continue;
		}
*/
		if( pGoal->IsNetGoal())
			continue;
		
		// create the screen element
		Script::CStruct* p_elem_params = new Script::CStruct();
		uint32 id = pGoal->GetGoalId();
		
		// add the id so we can use the same struct when calling the set_events script below
		p_elem_params->AddChecksum( CRCD(0x9982e501,"goal_id"), id );
		p_elem_params->AddString( CRCD(0xc4745838,"text"), p_view_goals_text );

		// check for a win record
		uint32 record_type;
		if ( pGoal->HasWonGoal() )
		{
			int record = 0;
			char record_string[128];

			// special case for competition
			if ( pGoal->IsCompetition() )
			{
				pGoalParams->GetInteger( CRCD(0xc22a2b72,"win_record"), &record, Script::NO_ASSERT );
				sprintf( record_string, "%i", record );
				switch ( record )
				{
					case 1:
						strcat( record_string, "st place" );
						break;
					case 2:
						strcat( record_string, "nd place" );
						break;
					case 3:
						strcat( record_string, "rd place" );
						break;
					default:
						Dbg_Message( "WARNING: competition place is %i", record );
						break;
				}						
				p_elem_params->AddString( CRCD(0xbb48af8b,"win_record_string"), record_string );
			}			
			else if ( pGoalParams->GetChecksum( CRCD(0x96963902,"record_type"), &record_type, Script::NO_ASSERT ) )
			{
				// For the purposes of supporting different record types in the future,
				// I've left some repeated code within each block.
				if ( record_type == ( CRCD(0xcd66c8ae,"score") ) )
				{
					pGoalParams->GetInteger( CRCD(0xc22a2b72,"win_record"), &record, Script::NO_ASSERT );
					sprintf( record_string, "%s", Str::PrintThousands( record ) );

					p_elem_params->AddString( CRCD(0xbb48af8b,"win_record_string"), record_string );
				}
				else if ( record_type == ( CRCD(0x906b67ba,"time") ) )
				{
					int record = 0;
					pGoalParams->GetInteger( CRCD(0xc22a2b72,"win_record"), &record, Script::NO_ASSERT );
					
					// break down into minutes, seconds, fraction
					int min, sec, fraction;
					sec = record / 1000;
					min = ( sec / 60 );
					sec -= ( min * 60 );
					fraction = ( ( record % 1000 ) / 10 );
					sprintf( record_string, "%i:%02i.%02i", min, sec, fraction );

					p_elem_params->AddString( CRCD(0xbb48af8b,"win_record_string"), record_string );
				}
			}
			else
			{
				p_elem_params->AddChecksum( NONAME,  CRCD(0xc80e196,"no_record_type")  );
			}
		}

		// create the item
		Script::RunScript( CRCD(0x380aea65,"view_goals_menu_add_item"), p_elem_params );

		delete p_elem_params;
	}
	
	// cleanup!
	Script::CleanUpArray( p_ordered_goals );
	Script::CleanUpArray( p_unordered_goals );
	delete p_ordered_goals;
	delete p_unordered_goals;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::AddGoalChoices( bool selected_only )
{
	
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		const char* p_view_goals_text = NULL;
		pGoal->ReplaceTrickText();
		pGoal->GetViewGoalsText( &p_view_goals_text );
		
		// skip goals without an associated text (eg, minigames)
		if ( !p_view_goals_text )
		{
			continue;
		}

		if( pGoal->IsNetGoal())
		{
			continue;
		}

		if( pGoal->IsMinigame())
		{
			continue;
		}

        if( pGoal->GetParents()->m_relative != 0 )
		{
			continue;
		}

		if( GameNet::Manager::ScriptIsHost( NULL, NULL ))
		{
			// if( pGoal->HasSeenGoal() == false )
			// {
				// continue;
			// }
	
			// if( pGoal->IsLocked())
			// {
				// continue;
			// }
		}

		if( selected_only )
		{
			if( !pGoal->IsSelected())
			{
				
				continue;
			}
		}
		
		// create the screen element
		Script::CStruct* p_elem_params = new Script::CStruct();
		uint32 id = pGoal->GetGoalId();
		

		// add the id so we can use the same struct when calling the set_events script below
		p_elem_params->AddChecksum( CRCD(0x9982e501,"goal_id"), id );
		p_elem_params->AddChecksum( CRCD(0xc2719fb0,"parent"), CRCD( 0x4d49ac0a, "current_menu" ) );
		p_elem_params->AddString( CRCD(0xc4745838,"text"), p_view_goals_text );
		p_elem_params->AddChecksum( CRCD(0x2f6bf72d,"font"), CRCD( 0x8aba15ec, "small" ) );		
		p_elem_params->AddInteger( CRCD(0x13b9da7b,"scale"), 0 );
		
		
		// set initial color and events for this item
		if( selected_only )
		{
			Script::RunScript( CRCD(0xe704c86c,"view_selected_goals_menu_set_events"), p_elem_params);
		}
		else
		{
			Script::RunScript( CRCD(0x6790ac11,"choose_goals_menu_set_events"), p_elem_params);
		}
		// normally, script won't be updated until next frame -- we want it NOW
		// p_new_script->Update();

		delete p_elem_params;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::GoalIsLocked( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId, false );
	
	if ( pGoal )
	{
		return pGoal->IsLocked();
	}
	else
	{
		// check goal manager params struct in case the goal was in
		// another level
		int goalLocked = 0;
		Script::CStruct* pGoalParams;
		if ( mp_goalFlags->GetStructure( goalId, &pGoalParams, Script::NO_ASSERT ) )
			pGoalParams->GetInteger( CRCD(0xd3e93882,"isLocked"), &goalLocked, Script::ASSERT );

		return ( goalLocked != 0 );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGoal* CGoalManager::IsInCompetition()
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		if ( pGoal->IsCompetition() && pGoal->IsActive() )
		{
			return pGoal;
		}
	}
	return NULL;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::GetGoalAnimations( uint32 goalId, const char* type, Script::CScript* pScript )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	uint32 array_name = pGoal->mp_goalPed->GetGoalAnimations( type );
	// Dbg_Assert( p_anims_array );

	// never know
	pScript->GetParams()->RemoveComponent( CRCD(0x18653764,"pro_stuff") );
	if ( array_name )
		pScript->GetParams()->AddChecksum( CRCD(0x18653764,"pro_stuff"), array_name );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CGoalManager::GetRandomBettingMinigame()
{
	// create an index array and randomize
	int numGoals = GetNumGoals();
	int* indexes = new int[numGoals];
	for ( int i = 0; i < numGoals; i++ )
		indexes[i] = i;
	for ( int i = 0; i < numGoals * 5; i++ )
	{
		int aIndex = Mth::Rnd( numGoals );
		int bIndex = Mth::Rnd( numGoals );
		int a = indexes[aIndex];
		indexes[aIndex] = indexes[bIndex];
		indexes[bIndex] = a;
	}
	
	// look through until we find one that's suitable
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( indexes[i], (int*)&key );
		Dbg_Assert( pGoal );

		if ( pGoal->IsBettingGame() )
		{
			delete[] indexes;
			return pGoal->GetGoalId();
		}
	}
	delete[] indexes;
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::EndRunCalled( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	return pGoal->EndRunCalled();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CGoalManager::GetBettingGuyId()
{
	// find the betting guy
	int numGoals = GetNumGoals();
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		if ( pGoal->IsBettingGuy() )
		{			
			return pGoal->GetGoalId();
		}
	}
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::DeactivateMinigamesWithTimer()
{
	int numGoals = GetNumGoals();
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		if ( pGoal->IsMinigame() && pGoal->IsActive() && pGoal->ShouldUseTimer() )
			pGoal->Deactivate();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CGoalManager::GetGoalManagerParams()
{
	return mp_goalFlags;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::ReplaceTrickText( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	return pGoal->ReplaceTrickText();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::ReplaceTrickText( uint32 goalId, const char* p_old_string, char* p_new_string, uint out_buffer_size )
{
#ifdef __NOPT_ASSERT__
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );
#endif		// __NOPT_ASSERT__

	// get the key combo array
	Script::CStruct* p_goal_params = GetGoalParams( goalId );
	Script::CArray* p_key_combo;
	p_goal_params->GetArray( CRCD(0x79704516,"key_combos"), &p_key_combo, Script::ASSERT );

	// build the string arrays
	Script::CArray* p_key = new Script::CArray();
	Script::CArray* p_trick = new Script::CArray();
	int size = p_key_combo->GetSize();
	p_key->SetSizeAndType( size, ESYMBOLTYPE_STRING );
	p_trick->SetSizeAndType( size, ESYMBOLTYPE_STRING );

	int premade_cat_index = -1;
	if ( p_goal_params->ContainsFlag( CRCD(0x8e6014f6,"create_a_trick") ) )
	{
		p_goal_params->GetInteger( CRCD(0x5b077ce1,"trickName"), &premade_cat_index, Script::ASSERT );
	}
	
	if ( !fill_trick_and_key_combo_arrays( p_key_combo, p_key, p_trick, premade_cat_index ) )
	{
		Script::CleanUpArray( p_key );
		Script::CleanUpArray( p_trick );
		delete p_key;
		delete p_trick;
		return false;
	}

	find_and_replace_trick_string( p_old_string, p_new_string, p_key, p_trick, out_buffer_size );
	
	// cleanup!
	Script::CleanUpArray( p_key );
	Script::CleanUpArray( p_trick );
	delete p_key;
	delete p_trick;

	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::ReplaceAllTrickText()
{
	int numGoals = GetNumGoals();
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		if ( pGoal->IsInitialized() )
			pGoal->ReplaceTrickText();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
void CGoalManager::UnlockProSpecificChallenges()
{
	m_proChallengesUnlocked = true;
	int numGoals = GetNumGoals();
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		
		if ( pGoal->GetParams()->ContainsFlag( CRCD(0xe0f96410,"pro_specific_challenge") ) )
		{
			// only unlock it if it's beaten or we're using the right skater
			if ( pGoal->HasWonGoal() || pGoal->mp_goalPed->ProIsCurrentSkater() )
				pGoal->UnlockGoal();
		}
	}
}
*/
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool CGoalManager::ProSpecificChallengesUnlocked()
{
	return m_proChallengesUnlocked;
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::GetNumberCollected( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId, true );

	return pGoal->GetNumberCollected();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::GetNumberOfFlags( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId, true );

	return pGoal->GetNumberOfFlags();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::IsPro()
{
	return m_isPro;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::ResetGoalFlags( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );

	pGoal->ResetGoalFlags();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::ColorTrickObjects( uint32 goalId, int seqIndex, bool clear )
{
	CGoal* pGoal = GetGoal( goalId );

	pGoal->ColorTrickObjects( seqIndex, clear );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CGoalManager::GetNumberOfTimesGoalStarted( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId );

	return pGoal->GetNumberOfTimesGoalStarted();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::GoalExists( uint32 goalId )
{
	CGoal* pGoal = GetGoal( goalId, false );
	if ( pGoal )
		return true;
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CGoalManager::GetGoalAttemptInfo()
{
	int numGoals = GetNumGoals();
	int totalStarts = 0;
	Script::CStruct* attemptInfo = new Script::CStruct();

	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		if ( !pGoal->IsMinigame() && !pGoal->IsNetGoal() && pGoal->HasWonGoal() )
			totalStarts += pGoal->GetNumberOfTimesGoalStarted();
	}
	attemptInfo->AddInteger( CRCD(0xd023adbe,"totalStarts"), totalStarts );
	return attemptInfo;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::SetCanStartGoal( int value )
{
	if ( value != 0 )
		m_canStartGoal = true;
	else
		m_canStartGoal = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::CheckTrickObjects()
{
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = skate_mod->GetLocalSkater();

	Mdl::Score* pScore = pSkater->GetScoreObject();
	int score = pScore->GetLastScoreLanded();
	
	Obj::CTrickComponent* pTrickComponent = GetTrickComponentFromObject(pSkater);
	Dbg_Assert(pTrickComponent);

	Obj::CPendingTricks pendingTricks = pTrickComponent->m_pending_tricks;

	int max_tricks = pendingTricks.m_NumTrickItems;
	if ( max_tricks > pendingTricks.vMAX_PENDING_TRICKS )
		max_tricks = pendingTricks.vMAX_PENDING_TRICKS;

	for ( int i = 0; i < max_tricks; i++ )
	{
		GotTrickObject( pendingTricks.m_Checksum[i], score );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
void CGoalManager::MarkProSpecificChallengeBeaten( uint32 skater_name, int beaten )
{
	Dbg_MsgAssert( mp_proSpecificChallenges->ContainsComponentNamed( skater_name ), ("MarkProSpecificChallengeBeaten called with unknown skater") );
	mp_proSpecificChallenges->AddInteger( skater_name, beaten );
}
*/
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool CGoalManager::SkaterHasBeatenProSpecificChallenge( uint32 skater_name )
{
	// Dbg_MsgAssert( mp_proSpecificChallenges->ContainsComponentNamed( skater_name ), ("SkaterHasBeatenProSpecificChallenge called with unknown skater %s", Script::FindChecksumName( skater_name ) ) );
	if ( mp_proSpecificChallenges->ContainsComponentNamed( skater_name ) )
	{
		int beaten;
		mp_proSpecificChallenges->GetInteger( skater_name, &beaten, Script::ASSERT );
		return ( beaten != 0 );
	}
	return false;
}
*/
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::SetEndRunType( uint32 goalId, Game::EndRunType newEndRunType )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->SetEndRunType( newEndRunType );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::ShouldUseTimer()
{
	int numGoals = GetNumGoals();
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		
		if ( pGoal->IsActive() && pGoal->ShouldUseTimer() )
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::SetShouldDeactivateOnExpire( uint32 goalId, bool should_deactivate )
{
	CGoal* pGoal = GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->SetShouldDeactivateOnExpire( should_deactivate );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoalManager::Land()
{
	if ( ShouldLogTrickObject() )
		CheckTrickObjects();
	else 
	{
		int numGoals = GetNumGoals();
		for ( int i = 0; i < numGoals; i++ )
		{
			uint32 key;
			CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
			Dbg_Assert( pGoal );

			if ( pGoal->IsActive() && pGoal->IsHorseGoal() )
			{
				Game::CHorseGoal* pHorseGoal = (Game::CHorseGoal*)pGoal;
				pHorseGoal->CheckScore();
				break;
			}
			else if ( pGoal->IsActive() && pGoal->GetParams()->ContainsFlag( CRCD(0xc63432a7,"high_combo") ) )
			{
				// TODO: maybe the high combo goal should be a subclass rather than
				// having a special case here?				
				Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetLocalSkater();
				if ( pSkater )
				{
					Mdl::Score* pScore = pSkater->GetScoreObject();
					if ( pScore->GetLastScoreLanded() > 0 )
					{
						Script::CStruct* pScriptParams = new Script::CStruct();
						pScriptParams->AddChecksum( CRCD(0x9982e501,"goal_id"), pGoal->GetGoalId() );
						pScriptParams->AddInteger( CRCD(0xb9072b65,"last_score_landed"), pScore->GetLastScoreLanded() );
						Script::RunScript( CRCD(0x6b755ed8,"goal_highcombo_check_score"), pScriptParams );
						delete pScriptParams;
					}
				}
			}
			else if ( pGoal->IsActive() && pGoal->ShouldRequireCombo() && pGoal->GetParams()->ContainsFlag( CRCD(0x7100155d,"combo_started") ) )
			{
				// lose the goal next frame
				Script::CStruct* pScriptParams = new Script::CStruct();
				pScriptParams->AddChecksum( CRCD(0x9982e501,"goal_id"), pGoal->GetGoalId() );
				Script::SpawnScript( CRCD(0xfefda11,"goal_lose_next_frame"), pScriptParams );
				delete pScriptParams;
			}
			else if ( pGoal->IsActive() && pGoal->GetParams()->ContainsFlag( CRCD( 0x293b95a7, "score_goal" ) ) )
			{
				Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetLocalSkater();
				if ( pSkater )
				{
					Mdl::Score* pScore = pSkater->GetScoreObject();
					int required_score;
					pGoal->GetParams()->GetInteger( CRCD(0xcd66c8ae,"score"), &required_score, Script::ASSERT );
					if ( pScore->GetTotalScore() + pScore->GetScorePotValue() >= required_score )
					{
						pGoal->GetParams()->AddInteger( CRCD(0x3da891b6,"winning_score"), pScore->GetTotalScore() + pScore->GetScorePotValue() );
						WinGoal( pGoal->GetGoalId() );
						pGoal->GetParams()->RemoveComponent( CRCD(0x3da891b6,"winning_score") );
					}
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoalManager::Bail()
{
	int numGoals = GetNumGoals();
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );

		if ( pGoal->IsActive() && pGoal->ShouldRequireCombo() && pGoal->GetParams()->ContainsFlag( CRCD(0x7100155d,"combo_started") ) )
		{
			// lose the goal next frame
			Script::CStruct* pScriptParams = new Script::CStruct();
			pScriptParams->AddChecksum( CRCD(0x9982e501,"goal_id"), pGoal->GetGoalId() );
			Script::SpawnScript( CRCD(0xfefda11,"goal_lose_next_frame"), pScriptParams );
			delete pScriptParams;
		}
		if ( pGoal->IsActive() && pGoal->IsTetrisGoal() )
		{
			int trick_flag;
			if ( pGoal->GetParams()->GetInteger( CRCD( 0x60b827d5, "trick_flag" ), &trick_flag, Script::NO_ASSERT ) )
			{
				if ( trick_flag != 0 )
				{
					pGoal->GetParams()->AddInteger( CRCD(0x60b827d5,"trick_flag"), 0 );
				}
			}
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::AwardMinigameCash( uint32 goalId, int amount )
{
	CGoal* pGoal = GetGoal( goalId, true );
	if ( pGoal && pGoal->IsMinigame() )
	{
		CMinigame* pMinigame = (CMinigame*)pGoal;
		return pMinigame->AwardCash( amount );
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::ResetCareer()
{
	// reset all of the goal manager's memory
	mp_goalFlags->Clear();

	m_goalPoints = 0;
	m_totalGoalPointsEarned = 0;
	m_cash = 0;
	m_totalCash = 0;
	m_numGoalsBeaten = 0;
	// m_proChallengesUnlocked = false;
	m_isPro = false;
	
	// mp_proSpecificChallenges->AppendStructure( Script::GetStructure( CRCD(0xd8b84847,"goal_pro_specific_challenges_beaten"), Script::ASSERT ) );
	m_currentChapter = m_currentStage = 0;

	mp_difficulty_levels->Clear();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::AwardAllGoalCash()
{
	int numGoals = GetNumGoals();
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		
		if ( !pGoal->IsMinigame() && !pGoal->HasWonGoal() )
		{
			// printf("found an unbeaten goal\n");
			pGoal->MarkBeaten();
			Script::CStruct* pRewardParams = new Script::CStruct();
			pGoal->GetRewardsStructure( pRewardParams );
			int cash;
			if ( pRewardParams->GetInteger(	 "cash", &cash, Script::NO_ASSERT ) )
			{
				const char* p_goal_name;
				pGoal->GetParams()->GetString( CRCD(0xbfecc45b,"goal_name"), &p_goal_name, Script::ASSERT );
				// printf("adding $%i for goal %s\n", cash, p_goal_name);
				AddCash( cash );
			}
			delete pRewardParams;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::UpdateFamilyTrees()
{
	int numGoals = GetNumGoals();
	
	// update all the parent lists
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		uint32 parent_id = pGoal->GetGoalId();
		Game::CGoalLink* p_child = pGoal->GetChildren();
		
		while ( p_child && p_child->m_relative != 0 )
		{
			// printf("goal has a child\n");
			uint32 child_id = p_child->m_relative;
			// get the child
			CGoal* pChildGoal = GetGoal( child_id, false );
			Dbg_MsgAssert( pChildGoal, ( "Failed to find goal %s when binding child to parent\n", Script::FindChecksumName( child_id ) ) );

			// search the parent list and add if necessary
			Game::CGoalLink* p_parentNode = pChildGoal->GetParents();
			bool found = false;
			while ( p_parentNode && p_parentNode->m_relative != 0 )
			{
				if ( p_parentNode->m_relative == parent_id )
				{
					found = true;
					break;
				}
				p_parentNode = p_parentNode->mp_next;
			}

			if ( !found )
				pChildGoal->AddParent( parent_id );
			
			p_child = p_child->mp_next;
		}
	}

	// send inherited params down the tree from root parent
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		
		// SetInheritableFlags will automatically traverse down the tree,
		// so we only need to call it on nodes without parents - ie, the root 
		// node of the tree.
		if ( pGoal->GetParents()->m_relative == 0 )
		{
			pGoal->SetInheritableFlags();
		}
	}
	
	// debugging shit.
	for ( int i = 0; i < numGoals; i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
	
		// print out children list
		Game::CGoalLink* p_test = pGoal->GetChildren();
		bool print_newline = false;
		if ( p_test->m_relative != 0 )
			// printf("children of %s\n", Script::FindChecksumName( pGoal->GetGoalId() ) );
		while ( p_test && p_test->m_relative != 0 )
		{
			print_newline = true;
			// printf("\t%s\n", Script::FindChecksumName( p_test->m_relative ) );
			p_test = p_test->mp_next;
		}
		
		// print out the parent list
		p_test = pGoal->GetParents();
		if ( p_test->m_relative != 0 )
			// printf("parents of %s\n", Script::FindChecksumName( pGoal->GetGoalId() ) );
		while ( p_test && p_test->m_relative != 0 )
		{
			print_newline = true;
			// printf("\t%s\n", Script::FindChecksumName( p_test->m_relative ) );
			p_test = p_test->mp_next;
		}
		// if ( print_newline )
			// printf("\n");
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::FindCircularLinks( uint32 parent, uint32 child )
{
#ifdef __NOPT_ASSERT__
	CGoal* pGoal = GetGoal( child, false );
	if ( pGoal )
	{
		CGoalLink* p_link = pGoal->GetChildren();
		while ( p_link && p_link->m_relative != 0 )
		{
			if ( p_link->m_relative == parent )
				Dbg_MsgAssert( 0, ( "Circular goal link path found from %s to %s\n", Script::FindChecksumName( parent ), Script::FindChecksumName( child ) ) );
			if ( p_link->m_relative == child )
				Dbg_MsgAssert( 0, ( "Goal (%s) has itself as a child\n", Script::FindChecksumName( child ) ) );

			// recurse!
			FindCircularLinks( parent, p_link->m_relative );
			p_link = p_link->mp_next;
		}
	}
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( checksum )
	{
		case 0x3ae9211b: // IsRootNode
		{
			CGoal* pGoal = GetGoal( pParams );
			if ( pGoal )
				return ( pGoal->GetParents()->m_relative == 0 );
			return false;
			break;
		}
	
		case 0xd782f8de: // IsLeafNode
		{
			CGoal* pGoal = GetGoal( pParams );
			if ( pGoal )
				return ( pGoal->GetChildren()->m_relative == 0 );
			return false;
			break;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CGoalManager::GetGoalIdFromParams( Script::CStruct* pParams, bool assert )
{
	uint32 goalId = 0;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, assert );
	return goalId;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::CreateGoalLevelObjects( void )
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		Script::CStruct* pParams;
		Script::CArray *pGeomArray;
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		pParams = pGoal->GetParams();

		// Create the associated pieces of geometry
		if ( pParams->GetArray( CRCD(0xf4eb5c60,"create_geometry"), &pGeomArray ) )
		{
			uint32 j, geometry;
			for( j = 0; j < pGeomArray->GetSize(); j++ )
			{
				geometry = pGeomArray->GetChecksum( j );
				CFuncs::ScriptCreateFromNodeIndex( SkateScript::FindNamedNode( geometry ));
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::SetDifficultyLevel( int level )
{
	Dbg_MsgAssert( level >= 0 && level <= vMAXDIFFICULTYLEVEL, ( "SetDifficultyLevel called with level %i", level ) );
	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	uint32 gameMode = skate_mod->GetGameMode()->GetNameChecksum();
	mp_difficulty_levels->AddInteger( gameMode, level );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

GOAL_MANAGER_DIFFICULTY_LEVEL CGoalManager::GetDifficultyLevel( bool career )
{
	uint32 gameMode;
    if ( career)
    {
        gameMode = CRCD(0x4da4937b,"career");
    }
    else
    {
        Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
    	gameMode = skate_mod->GetGameMode()->GetNameChecksum();
    }
	
    int level;
	
	// if no diff level assigned for this game mode yet, assume normal
	if ( !mp_difficulty_levels->GetInteger( gameMode, &level, Script::NO_ASSERT ) )
		level = 1;
	
	GOAL_MANAGER_DIFFICULTY_LEVEL diff_level = GOAL_MANAGER_DIFFICULTY_MEDIUM;
	
    switch ( level )
	{
		case 0:
			diff_level = GOAL_MANAGER_DIFFICULTY_LOW;
			break;
		case 1:
			diff_level = GOAL_MANAGER_DIFFICULTY_MEDIUM;
			break;
		case 2:
			diff_level = GOAL_MANAGER_DIFFICULTY_HIGH;
			break;
		default:
			Dbg_MsgAssert( 0, ( "bad difficulty level %i", level ) );
			break;
	}

	return diff_level;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::CanRestartStage()
{
	printf("STUBBED\n");
	return false;
/*
	int activeGoal = GetActiveGoal();
	if ( activeGoal != -1 )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( activeGoal, (int*)&key );
		Dbg_Assert( pGoal );
	
		if ( pGoal->m_inherited_flags & GOAL_INHERITED_FLAGS_RETRY_STAGE )
		{
			return true;
		}
	}
	return false;
*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::SetGoalChaptersAndStages()
{
	// grab the chapter info array
	Script::CArray* pChapterGoalsArray = Script::GetArray( CRCD( 0xb892776f, "chapter_goals" ), Script::ASSERT );
	Dbg_Assert( pChapterGoalsArray->GetType() == ESYMBOLTYPE_ARRAY );

	int chapter_goals_array_size = pChapterGoalsArray->GetSize();
	for ( int chapter_index = 0; chapter_index < chapter_goals_array_size; chapter_index++ )
	{
		Script::CArray* pStageArray = pChapterGoalsArray->GetArray( chapter_index );
		Dbg_Assert( pStageArray->GetType() == ESYMBOLTYPE_ARRAY );
		int stage_array_size = pStageArray->GetSize();
		for ( int stage_index = 0; stage_index < stage_array_size; stage_index++ )
		{
			Script::CArray* pStage = pStageArray->GetArray( stage_index );
			Dbg_Assert( pStage->GetType() == ESYMBOLTYPE_STRUCTURE );
			int num_goals = pStage->GetSize();
			for ( int goal_index = 0; goal_index < num_goals; goal_index++ )
			{
				Script::CStruct* pGoalStruct = pStage->GetStructure( goal_index );
				uint32 goal_id;
				if ( pGoalStruct->GetChecksum( CRCD( 0x9982e501, "goal_id" ), &goal_id, Script::NO_ASSERT ) )
				{
					CGoal* pGoal = GetGoal( goal_id, false );
					if ( pGoal )
					{
						pGoal->SetChapter( chapter_index );
						pGoal->SetStage( stage_index );
					}
				}
			}
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGoalManager::AdvanceStage( bool force, uint32 just_won_goal_id )
{
	// printf("CGoalManager::AdvanceStage\n");
	// DeactivateAllGoals();
	
	// K: This was to fix an assert when winning a created goal in the park editor.
	// Fixed by just not doing any advance-stage logic when in created goals mode.
	Mdl::Skate *p_skate_mod = Mdl::Skate::Instance();
	if ( p_skate_mod->GetGameMode()->IsTrue( CRCD( 0x353961d7, "is_creategoals" ) ) )
	{
		return false;
	}	
	
	// grab the chapter info array
	Script::CArray* pChapterGoalsArray = Script::GetArray( CRCD( 0xb892776f, "chapter_goals" ), Script::ASSERT );
	Dbg_Assert( pChapterGoalsArray->GetType() == ESYMBOLTYPE_ARRAY );
	
	// grab num goals to complete array
	Script::CArray* pChapterNumGoalsToComplete = Script::GetArray( CRCD( 0x846f65f5, "CHAPTER_NUM_GOALS_TO_COMPLETE" ), Script::ASSERT );
	Dbg_Assert( pChapterNumGoalsToComplete->GetType() == ESYMBOLTYPE_ARRAY );
	
	if ( m_currentChapter >= (int)pChapterGoalsArray->GetSize() )
	{
		// they beat the game
		return false;
	}
	// printf("m_currentChapter = %i, m_currentStage = %i\n", m_currentChapter, m_currentStage);
	Dbg_MsgAssert( m_currentChapter < (int)pChapterGoalsArray->GetSize(), ( "the current chapter is %i, but the chapter goals array has size %i.", m_currentChapter, pChapterGoalsArray->GetSize() ) );
	Script::CArray* pCurrentChapter = pChapterGoalsArray->GetArray( m_currentChapter );
	Dbg_MsgAssert( m_currentStage < (int)pCurrentChapter->GetSize(), ( "The current stage is set to %i, but the chapter array only has %i stage(s).", m_currentStage, pCurrentChapter->GetSize() ) );
	Script::CArray* pCurrentStage = pCurrentChapter->GetArray( m_currentStage );

	Script::CArray* pChapterTotals = pChapterNumGoalsToComplete->GetArray( m_currentChapter );
	int stage_total_target = pChapterTotals->GetInteger( m_currentStage );
	int num_stages = pChapterTotals->GetSize();

	// resolve just won goal to parent goal, which is what's listed
	// in the chapter info array
	if ( just_won_goal_id )
	{
		// printf("just won goal %s\n", Script::FindChecksumName( just_won_goal_id ) );
		CGoal* pJustWonGoal = GetGoal( just_won_goal_id );
		CGoalLink* pJustWonParents = pJustWonGoal->GetParents();
		while ( pJustWonParents && pJustWonParents->m_relative != 0 )
		{
			just_won_goal_id = pJustWonParents->m_relative;
			pJustWonGoal = GetGoal( pJustWonParents->m_relative );
			pJustWonParents = pJustWonGoal->GetParents();
		}
	}
	
	// check the required total
	int num_goals = pCurrentStage->GetSize();
	int stage_total = 0;

	for ( int goal_index = 0; goal_index < num_goals; goal_index++ )
	{
		Script::CStruct* pGoalStruct = pCurrentStage->GetStructure( goal_index );
		uint32 goal_id;
		if ( pGoalStruct->GetChecksum( CRCD( 0x9982e501, "goal_id" ), &goal_id, Script::NO_ASSERT ) )
		{
			CGoal* pGoal = GetGoal( goal_id, false );
			
			if ( pGoal )
			{
				// resolve to parent goal id
				CGoalLink* pParents = pGoal->GetParents();
				while ( pParents && pParents->m_relative != 0 )
				{
					goal_id = pParents->m_relative;
					pGoal = GetGoal( pParents->m_relative );
					pParents = pGoal->GetParents();				
				}
			}

			if ( pGoal )
			{
				if ( ( goal_id == just_won_goal_id && !pGoal->HasWonGoal() ) || ( pGoal->HasWonGoal() ) )
				{
					stage_total++;
					// printf( "you've won %s, stage_total = %i\n", Script::FindChecksumName( goal_id ), stage_total );
				}
			}
			else
			{
				// look for goal in other level
				Script::CStruct* pTempGoalParams;
				if ( mp_goalFlags->GetStructure( goal_id, &pTempGoalParams, Script::NO_ASSERT ) )
				{
					int hasBeaten = 0;
					pTempGoalParams->GetInteger( "hasBeaten", &hasBeaten, Script::ASSERT );
					if ( hasBeaten != 0 )
					{
						// printf("you've beaten %s in another level\n", Script::FindChecksumName( p_comp->mNameChecksum ) );
						stage_total++;
					}					
				}
			}
		}
	}
	
	// printf("stage_total = %i, stage_total_target = %i\n", stage_total, stage_total_target );
	if ( stage_total >= stage_total_target || force )
	{
		// we've beaten the stage
		if ( m_currentStage + 1 == num_stages )
		{
			// we've finished the chapter
			m_currentChapter++;
			m_currentStage = 0;
		}
		else
		{
			m_currentStage++;
		}

		// printf("m_currentStage = %i\n", m_currentStage);
		// reinitialize the chapter and stage
		UninitializeAllGoals();
		InitializeAllGoals();

		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::RunLastStageScript()
{
	// DeactivateAllGoals();
	
	// don't do anything if they beat the game
	Script::CArray* pChapterGoalsArray = Script::GetArray( CRCD( 0xb892776f, "chapter_goals" ), Script::ASSERT );
	Dbg_Assert( pChapterGoalsArray->GetType() == ESYMBOLTYPE_ARRAY );
	if ( m_currentChapter >= (int)pChapterGoalsArray->GetSize() )
	{
		return;
	}

	// K: This was to fix an assert when winning a created goal in the park editor.
	// Fixed by just not doing any advance-stage logic when in created goals mode.
	Mdl::Skate *p_skate_mod = Mdl::Skate::Instance();
	if ( p_skate_mod->GetGameMode()->IsTrue( CRCD( 0x353961d7, "is_creategoals" ) ) )
	{
		return;
	}	
	
	// grab the chapter scripts arrray
	Script::CArray* pChapterScriptsArray = Script::GetArray( CRCD(0x9f00892d,"CHAPTER_COMPLETION_SCRIPTS"), Script::ASSERT );
	Dbg_Assert( pChapterScriptsArray->GetType() == ESYMBOLTYPE_ARRAY );
	
	int last_stage_index = m_currentStage;
	int last_chapter_index = m_currentChapter;

	if ( last_stage_index == 0 )
	{
		if ( last_chapter_index == 0 )
		{
			// we're on the first chapter and stage - don't do anything
			return;
		}
		last_chapter_index--;

		Dbg_MsgAssert( last_chapter_index < (int)pChapterScriptsArray->GetSize(), ( "last_chapter_index %i too big", last_chapter_index ) );

		// figure the stage index
		Script::CArray* pChapterArray = pChapterScriptsArray->GetArray( last_chapter_index );
		Dbg_Assert( pChapterArray );
		last_stage_index = pChapterArray->GetSize() - 1;
		// [ { script_name=NJ_StageSwitch_0_to_1_1 }, { script_name=NJ_StageSwitch_1_1_to_1_2 }, { script_name=NJ_StageSwitch_1_2_to_2_1 } ]
	}
	else
		last_stage_index--;

	uint32 script_name;
	Script::CArray* pChapterParamsArray = pChapterScriptsArray->GetArray( last_chapter_index );
	Dbg_Assert( pChapterParamsArray );
	Script::CStruct* pStageScriptStruct = pChapterParamsArray->GetStructure( last_stage_index );
	Dbg_Assert( pStageScriptStruct );
	if ( pStageScriptStruct->GetChecksum( CRCD(0x6166f3ad,"script_name"), &script_name, Script::NO_ASSERT ) )
	{
		Script::SpawnScript( script_name );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::SetTeamMember( uint32 pro, bool remove, bool mark_used )
{
	if ( remove )
		mp_team->RemoveComponent( pro );
	else
	{
		if ( mark_used )
			mp_team->AddInteger( pro, 1 );	
		else
			mp_team->AddInteger( pro, 0 );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::KillTeamMembers( )
{
	mp_team->Clear();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::SetTeamName( const char* pTeamName )
{
	mp_team->AddString( CRCD(0x703d7582,"team_name"), pTeamName );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalManager::HideAllGoalPeds( bool hide )
{
	for ( int i = 0; i < GetNumGoals(); i++ )
	{
		uint32 key;
		CGoal* pGoal = m_goalList.GetItemByIndex( i, (int*)&key );
		Dbg_Assert( pGoal );
		pGoal->mp_goalPed->Hide( hide );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGoalManager* GetGoalManager()
{
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	return skate_mod->GetGoalManager();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AddGoal | 
// @parm name | name | the goal id
// @parm structure | params | goal parameters
bool ScriptAddGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	Script::CStruct* pSubParams;
	pParams->GetStructure( CRCD(0x7031f10c,"params"), &pSubParams, Script::ASSERT );

	return pGoalManager->AddGoal( goalId, pSubParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_RemoveGoal | 
// @parm name | name | the goal id
bool ScriptRemoveGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	pGoalManager->RemoveGoal( goalId );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetGoal | currently does nothing at all!
// @parm name | name | the goal id
bool ScriptGetGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	#if 0
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	if ( pGoal )
	{
	}
	#endif
	
	// TODO: return it in the script components

	// flags are not allowed as params,
	// otherwise there will be a leak
	// as things get appended, and re-appended

	// activate goal->
	// should turn off the score degrader
	// and the score accumulation script 

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_EditGoal | this will append the given structure
// onto the goal's existing params structure
// @parm name | name | the goal id
// @parm structure | params | structure to append
bool ScriptEditGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	Script::CStruct* pSubParams;
	pParams->GetStructure( CRCD(0x7031f10c,"params"), &pSubParams, Script::ASSERT );

	return pGoalManager->EditGoal( goalId, pSubParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_ActivateGoal |
// @parm name | name | the goal id
// @flag dontAssert | pass this in if you don't want the goal manager 
// to assert if it can't find the specified goal (not recommended)
bool ScriptActivateGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

    // put in for testing
    bool dontAssert = false;
    if ( pParams->ContainsFlag( CRCD(0x9ac8d54f,"dontAssert") ) )
        dontAssert = true;
	
	return pGoalManager->ActivateGoal( goalId, dontAssert );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_DeactivateGoal | 
// @parm name | name | the goal id
bool ScriptDeactivateGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	return pGoalManager->DeactivateGoal( goalId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_DeactivateGoal | 
bool ScriptClearLastGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	pGoalManager->ClearLastGoal();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_WinGoal | 
// @parm name | name | the goal id
bool ScriptWinGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	#ifdef	__NOPT_ASSERT__
	bool had_object = false;
	uint32	old_id = 0;
	Obj::CObjectPtr p_obj;
	if (pScript->mpObject)
	{
		had_object = true;
		p_obj = pScript->mpObject;
		old_id = p_obj->GetID();
	}
	#endif
	
	
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	bool result = pGoalManager->WinGoal( goalId );

	#ifdef	__NOPT_ASSERT__
	if (had_object)
	{
		Dbg_MsgAssert(p_obj,("%s\nObject %s was deleted indirectly \nWhen running a script via GoalManager_WinGoal\n You probably use KILL PREFIX = ...., Maybe you could use DIE instead",pScript->GetScriptInfo(),Script::FindChecksumName(old_id)));
	}
	#endif
		
	return result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_LoseGoal | 
// @parm name | name | the goal id
bool ScriptLoseGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	#ifdef	__NOPT_ASSERT__
	bool had_object = false;
	uint32	old_id = 0;
	Obj::CObjectPtr p_obj;
	if (pScript->mpObject)
	{
		had_object = true;
		p_obj = pScript->mpObject;
		old_id = p_obj->GetID();
	}
	#endif
	
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	bool result = pGoalManager->LoseGoal( goalId );

	#ifdef	__NOPT_ASSERT__
	if (had_object)
	{
		Dbg_MsgAssert(p_obj,("%s\nObject %s was deleted indirectly when running a script via GoalManager_LoseGoal\n You probaly use KILL PREFIX = ...., Maybe you could use DIE instead",pScript->GetScriptInfo(),Script::FindChecksumName(old_id)));
	}
	#endif
	
	
	return result;
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_RemoveAllGoals | this will completely destroy
// all the goals currently in existence...make sure you call GoalManager_LevelUnload
// first if you want to remember anything about the state of the goals before
// destroying them.
bool ScriptRemoveAllGoals(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->RemoveAllGoals();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_DeactivateAllGoals | 
bool ScriptDeactivateAllGoals(Script::CStruct *pParams, Script::CScript *pScript)
{
	bool including_net_goals;
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	including_net_goals = false;
	if( pParams->ContainsComponentNamed( CRCD(0xcc365298,"force_all")))
	{
		including_net_goals = true;
	}

	pGoalManager->DeactivateAllGoals( including_net_goals );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UninitializeAllGoals |
bool ScriptUninitializeAllGoals(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->UninitializeAllGoals();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UninitializeGoal |
// @parm name | name | the goal id
bool ScriptUninitializeGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	if ( pParams->ContainsFlag( CRCD(0x687de558,"affect_tree") ) )
	{
		pGoalManager->UninitializeGoalTree( goalId );
	}
	else
	{
		pGoalManager->UninitializeGoal( goalId );
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_InitializeAllGoals | 
bool ScriptInitializeAllGoals(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->InitializeAllGoals();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_InitializeAllSelectedGoals | 
bool ScriptInitializeAllSelectedGoals(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->InitializeAllSelectedGoals();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UpdateAllGoals | This forces the goal manager to run
// an update cycle.
bool ScriptUpdateAllGoals(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->Update();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_HasActiveGoals | returns true if there is at least one
// active goal (excluding minigames and other goals that don't count towards the total)
bool ScriptHasActiveGoals(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	bool count_all = pParams->ContainsFlag( CRCD(0xa21884b5,"count_all") );

	return ( pGoalManager->GetNumActiveGoals( count_all ) > 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GoalIsActive | returns true if the specified goal
// is currently active
// @parm name | name | the goal id
bool ScriptGoalIsActive(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	return pGoalManager->GoalIsActive( goalId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetGoalTimer | resets the goal timer to it's inital
// value.
// @parm name | name | the goal id
bool ScriptSetGoalTimer(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	
	return pGoalManager->SetGoalTimer( goalId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_ZeroGoalTimer | zeros the goal timer; added to allow a hack to fix split screen horse end runs to be minimally invasive
// @parm name | name | the goal id
bool ScriptZeroGoalTimer(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	
	return pGoalManager->SetGoalTimer( goalId, 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_PauseAllGoals | 
bool ScriptPauseAllGoals(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    pGoalManager->PauseAllGoals();
    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UnPauseAllGoals | 
bool ScriptUnPauseAllGoals(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    pGoalManager->UnPauseAllGoals();
    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetGoalFlag | for setting the special goal flags
// @parm name | name | the goal id
// @uparm name | the name of the flag to set
// @uparm 1 | value to use 
bool ScriptSetGoalFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	
	uint32 flagChecksum;
	pParams->GetChecksum( NONAME, &flagChecksum, Script::ASSERT );

	int flagValue;
	pParams->GetInteger( NONAME, &flagValue, Script::ASSERT );

    bool success = pGoalManager->SetGoalFlag( goalId, flagChecksum, flagValue );

    return success;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_HasSeenGoal | returns true if the player has
// activated the specified goal at least once
// @parm name | name | the goal id
bool ScriptHasSeenGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    uint32 goalId;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

    return ( pGoalManager->HasSeenGoal( goalId ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_RestartLastGoal | this will restart the last goal
// It will fail silently if there is no goal to retry
bool ScriptRestartLastGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    pGoalManager->RestartLastGoal();

    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*// @script | GoalManager_CreateGoalFlag | 
// @parm name | name | the goal id
// @parm name | flag | the name of the flag to create
bool ScriptCreateGoalFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    uint32 name;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &name, Script::ASSERT );

    uint32 flag;
    pParams->GetChecksum( CRCD(0x2e0b1465,"flag"), &flag, Script::ASSERT ); 

    pGoalManager->CreateGoalFlag( name, flag );
    return true;
}*/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AllFlagsSet | 
// @parm name | name | the goal id
bool ScriptAllFlagsSet(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    uint32 name;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &name, Script::ASSERT );

    return pGoalManager->AllFlagsSet( name );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GoalFlagSet | true if a goal flag has been set
// @parm name | name | the goal id
// @parm name | flag | the name of the flag to check
bool ScriptGoalFlagSet(Script::CStruct *pParams, Script::CScript *pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	
	uint32 flag;
	pParams->GetChecksum( CRCD(0x2e0b1465,"flag"), &flag, Script::ASSERT );

	return pGoalManager->GoalFlagSet( goalId, flag );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_CanRetryGoal | returns true if there is a goal
// to retry
bool ScriptCanRetryGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    return pGoalManager->CanRetryGoal();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetGoalParams | this will append the goal's 
// params onto the scripts params.  The goal manager scripts use this
// extensively to avoid passing large structures back and forth.
// @parm name | name | the goal id
bool ScriptGetGoalParams(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    uint32 goalId;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
    
    pScript->GetParams()->AppendStructure( pGoalManager->GetGoalParams( goalId ) );

    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_CanStartGoal |
// @parm name | name | the goal id
bool ScriptCanStartGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    // uint32 goalId;
    // pParams->GetChecksum( "name", &goalId, Script::ASSERT );

    return pGoalManager->CanStartGoal();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_NextRaceWaypoint | 
// @parm name | name | the goal id
bool ScriptNextRaceWaypoint(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    uint32 goalId;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

    return pGoalManager->NextRaceWaypoint( goalId );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_NextTourSpot | 
// @parm name | name | the goal id
bool ScriptNextTourSpot(Script::CStruct *pParams, Script::CScript *pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    uint32 goalId;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

    return pGoalManager->NextTourSpot( goalId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_InitTrickObjects |
// @parm name | name | the goal id
bool ScriptInitTrickObjects( Script::CStruct* pParams, Script::CScript* pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );
    
    uint32 goalId;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

    pGoalManager->InitTrickObjects( goalId );

    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_CreateGoalName | 
// @parm string | goal_type | the goal type
bool ScriptCreateGoalName( Script::CStruct* pParams, Script::CScript* pScript )
{
    char goal_prefix[128];
    const char* goal_type;

    pParams->GetText( CRCD(0x6d11ed74,"goal_type"), &goal_type, Script::ASSERT );

	// Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	// Obj::CSkaterCareer* pCareer = skate_mod->GetCareer();

	// create the level prefix
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	Script::CStruct* p_level_structure = Script::GetStructure( pGoalManager->GetLevelStructureName(), Script::ASSERT );
	const char* p_level_prefix;
	p_level_structure->GetString( CRCD(0x651533ec,"level"), &p_level_prefix, Script::ASSERT );
	strcpy( goal_prefix, p_level_prefix );
	strcat( goal_prefix, "_goal_" );

/*	// figure the level
	switch ( level )
	{
	case 1:                                 // school
		strcpy( goal_prefix, "Sch_goal_" );
		break;
	case 2:                                 // san francisco
		strcpy( goal_prefix, "Sf2_goal_" );
		break;
	case 3:                                 // alcatraz
		strcpy( goal_prefix, "Alc_goal_" );
		break;
	case 4:                                 // Kona park
		strcpy( goal_prefix, "Kon_goal_" );
		break;
	case 5:                                 // junkyard
		strcpy( goal_prefix, "Jnk_goal_" );
		break;
	case 6:                                 // london
		strcpy( goal_prefix, "Lon_goal_" );
		break;
	case 7:                                 // zoo
		strcpy( goal_prefix, "Zoo_goal_" );
		break;
	case 8:									// carnival
		strcpy( goal_prefix, "Cnv_Goal_" );
		break;
	case 9:									// chicago
		strcpy( goal_prefix, "Hof_goal_" );
		break;
	case 10:								// default test level
		strcpy( goal_prefix, "Default_goal_" );
		break;
	default:
		Dbg_MsgAssert( false, ( "GetGoalName couldn't figure level number" ) );
	}
*/
    
    // create the goal name
    strcat( goal_prefix, (char*)goal_type );

    // add to pParams
    pScript->GetParams()->AddChecksum( CRCD(0x9982e501,"goal_id"), Script::GenerateCRC( goal_prefix ) );
	pScript->GetParams()->AddString( CRCD(0xbfecc45b,"goal_name"), goal_prefix );

    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetLevelPrefix | returns the current level's prefix in level_prefix
bool ScriptGetLevelPrefix( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	Script::CStruct* p_level_structure = Script::GetStructure( pGoalManager->GetLevelStructureName(), Script::ASSERT );
	const char* p_level_prefix;
	p_level_structure->GetString( CRCD(0x651533ec,"level"), &p_level_prefix, Script::ASSERT );
	
	pScript->GetParams()->AddString( CRCD(0x15f818d0,"level_prefix"), p_level_prefix);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetGraffitiMode |
// @uparmopt 1 | turn on or off with 1 or 0
bool ScriptSetGraffitiMode( Script::CStruct* pParams, Script::CScript* pScript )
{
    CGoalManager* pGoalManager = GetGoalManager();

    int mode = 1;
    pParams->GetInteger( NONAME, &mode, Script::NO_ASSERT ); 

    return pGoalManager->SetGraffitiMode( mode );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_HasWonGoal | true if you've beat this goal
// @parm name | name | the goal id
bool ScriptHasWonGoal( Script::CStruct* pParams, Script::CScript* pScript )
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    uint32 goalId;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

    return pGoalManager->HasWonGoal( goalId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGoalIsSelected(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    uint32 goalId;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

    return pGoalManager->GoalIsSelected( goalId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptToggleGoalSelection(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    uint32 goalId;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

    pGoalManager->ToggleGoalSelection( goalId );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGoalsAreSelected(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

	return ( pGoalManager->GetNumSelectedGoals() > 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GotCounterObject | 
// @parm name | name | the goal id
bool ScriptGotCounterObject( Script::CStruct* pParams, Script::CScript* pScript )
{
    uint32 goalId;
    if ( pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::NO_ASSERT ) )
    {
        CGoalManager* pGoalManager = GetGoalManager();
        pGoalManager->GotCounterObject( goalId );
        return true;
    }
    else 
    {
        // printf("didn't get goalId\n");
    }
    return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_CounterGoalDone | returns true if the counter goal
// is complete
// @parm name | name | the goal id
bool ScriptCounterGoalDone(Script::CStruct* pParams, Script::CScript* pScript)
{
    uint32 goalId;
    pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

    CGoalManager* pGoalManager = GetGoalManager();

    return pGoalManager->CounterGoalDone( goalId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AddGoalPoint | adds one goal point
bool ScriptAddGoalPoint(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->AddGoalPoint();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SpendGoalPoints | 
// @uparm 1 | number to spend
bool ScriptSpendGoalPoints(Script::CStruct* pParams, Script::CScript* pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    int num;
    pParams->GetInteger( NONAME, &num, Script::ASSERT );

    pGoalManager->SpendGoalPoints( num );
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_HasGoalPoints | 
// @uparm 1 | number to check for
bool ScriptHasGoalPoints(Script::CStruct* pParams, Script::CScript* pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    int num;
    pParams->GetInteger( NONAME, &num, Script::ASSERT );
    
    return pGoalManager->HasGoalPoints( num );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptClearGoalPoints(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    pGoalManager->ClearGoalPoints();
    
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetNumberOfGoalPoints | places the number of
// goal points in a variable named "goal_points" in the script params
bool ScriptGetNumberOfGoalPoints(Script::CStruct* pParams, Script::CScript* pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    int goal_points = 0;
	if ( pParams->ContainsFlag( CRCD(0x3dd01ea1,"total") ) )
		goal_points = pGoalManager->GetTotalNumberOfGoalPointsEarned();
	else
		goal_points = pGoalManager->GetNumberOfGoalPoints();

    pScript->GetParams()->AddInteger( CRCD(0x42c20492,"goal_points"), goal_points );
    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AddCash | gives the player more cash
// @uparm 1 | amount of cash to give
bool ScriptAddCash(Script::CStruct* pParams, Script::CScript* pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    int amount;
    pParams->GetInteger( NONAME, &amount, Script::ASSERT );

    pGoalManager->AddCash( amount );
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SpendCash | spends some of the player's cash
// @uparm 1 | amount to spend
bool ScriptSpendCash(Script::CStruct* pParams, Script::CScript* pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    int amount;
    pParams->GetInteger( NONAME, &amount, Script::ASSERT );

    return pGoalManager->SpendCash( amount );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetCash | places the current player's cash amount
// in a parameter called "cash" in the script params
bool ScriptGetCash(Script::CStruct* pParams, Script::CScript* pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

	bool get_total = pParams->ContainsFlag( CRCD(0x3dd01ea1,"total") );
    
    pScript->GetParams()->AddInteger( CRCD(0xf9461a46,"cash"), pGoalManager->GetCash( get_total ) );
    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_HasWonGoalWithProset | 
// @uparm "string" | the proset_prefix you wanna check
bool ScriptHasBeatenGoalWithProset(Script::CStruct* pParams, Script::CScript* pScript)
{
    const char* p_proset_prefix;
    pParams->GetString( NONAME, &p_proset_prefix, Script::ASSERT );

    CGoalManager* pGoalManager = GetGoalManager();
    return pGoalManager->HasBeatenGoalWithProset( p_proset_prefix );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetProsetNotPrefix | 
// @uparm "string" | the proset_prefix you wanna check
bool ScriptGetProsetNotPrefix(Script::CStruct* pParams, Script::CScript* pScript)
{
    const char* p_proset_prefix;
    pParams->GetString( NONAME, &p_proset_prefix, Script::ASSERT );

    char temp_string[128];
    strcpy( temp_string, "");

    int size = 0;    
    while ( p_proset_prefix[size + 1] )
    {
        temp_string[size] = p_proset_prefix[size];
        size++;
    }
	temp_string[size] = '\0';

    // now we're at the last char
    strcat( temp_string, "NOT_" );

    pScript->GetParams()->AddString( CRCD(0xc60ce720,"proset_not_prefix"), temp_string );
    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_LevelLoad | called when the player enters a level.
// Loads data about goals seen, completed, etc. in a previous game
bool ScriptLevelLoad(Script::CStruct* pParams, Script::CScript* pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    pGoalManager->LevelLoad();
    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_LevelUnload | called when the player leaves a level.
// Prompts the goal manager to save state data about all goals
bool ScriptLevelUnload(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    pGoalManager->LevelUnload();
    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_NumGoalsBeatenInLevel | This will return the number
// of beaten goals in the scripts params as num_beaten
// @uparm 1 | level number
// @flag count_pro_specific_challenges | false by default
bool ScriptNumGoalsBeatenInLevel(Script::CStruct* pParams, Script::CScript* pScript)
{
    int levelNum;
    pParams->GetInteger( NONAME, &levelNum, Script::ASSERT );

    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    int numbeat = pGoalManager->NumGoalsBeatenInLevel( levelNum, pParams->ContainsFlag( CRCD(0x8690a9cf,"count_pro_specific_challenges") ) );
    pScript->GetParams()->AddInteger( CRCD(0x8110dcdf,"num_beaten"), numbeat );
    return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UnlockAllGoals | Unlocks all locked goals
bool ScriptUnlockAllGoals(Script::CStruct* pParams, Script::CScript* pScript)
{
    CGoalManager* pGoalManager = GetGoalManager();
    Dbg_Assert( pGoalManager );

    pGoalManager->UnlockAllGoals();
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_CheckMinigameRecord | checks the current minigame
// value against the one stored in minigame_record in the goal params
// @parm name | name | the goal id
// @uparm 100 | the value you want to check against the current record
bool ScriptCheckMinigameRecord(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	int value;
	pParams->GetInteger( NONAME, &value, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->CheckMinigameRecord( goalId, value );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_DeactivateCurrentGoal | Deactivates the currently
// active goal (this function cannot always be trusted).
bool ScriptDeactivateCurrentGoal(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->DeactivateCurrentGoal();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetStartTime | Records the start time of a combo
// (only used for the timedcombo miingame right now).
// @parm name | name | the goal id
bool ScriptSetStartTime(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->SetStartTime( goalId );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UpdateComboTimer | Used for the timedcombo minigame
// @parm name | name | the goal id
bool ScriptUpdateComboTimer(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->UpdateComboTimer( goalId );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetStartHeight | used for the height minigame
// @parm name | name | the goal id
bool ScriptSetStartHeight(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->SetStartHeight( goalId );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_CheckHeightRecord | used for height minigame
// @parm name | name | the goal id
bool ScriptCheckHeightRecord(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->CheckHeightRecord( goalId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_CheckDistanceRecord | used for height minigame
// @parm name | name | the goal id
bool ScriptCheckDistanceRecord(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->CheckDistanceRecord( goalId );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_PlayGoalStartStream | plays the intro vo for the goal
// @parm name | name | the goal id
bool ScriptPlayGoalStartStream(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->PlayGoalStartStream( pParams );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_PlayGoalWaitStream | plays the wait vo for the goal
// @parm name | name | the goal id
bool ScriptPlayGoalWaitStream(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->PlayGoalWaitStream( goalId );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_PlayGoalWinStream | plays the success vo for the goal
// @parm name | name | the goal ID
bool ScriptPlayGoalWinStream(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->PlayGoalWinStream( pParams );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_StopCurrentStream | stops any vo being played by
// the goal trigger
// @parm name | name | the goal ID
bool ScriptStopCurrentStream(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->StopCurrentStream( goalId );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_TurnPro | instantly turns you pro, unlocks all 
// the pro goals, and runs the "you just turned pro" script
bool ScriptTurnPro(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->TurnPro();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_DeactivateAllMinigames | 
bool ScriptDeactivateAllMinigames(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->DeactivateAllMinigames();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_InitializeAllMinigames | 
bool ScriptInitializeAllMinigames(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->InitializeAllMinigames();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_PauseGoal | pauses an individual goal
// @parm name | name | the goal id
bool ScriptPauseGoal( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->PauseGoal( goalId );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UnPauseGoal | unpauses an individual goal
// @parm name | name | the goal id
bool ScriptUnPauseGoal( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->UnPauseGoal( goalId );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_PauseCompetition | Pauses a competition
// @parm name | name | the goal id
bool ScriptPauseCompetition( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->PauseCompetition( goalId );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UnPauseCompetition | The competition uses a secondary
// pause mechanism...this is for unpausing the comp between rounds
// @parm name | name | the goal id
bool ScriptUnPauseCompetition( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->UnPauseCompetition( goalId );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UnBeatAllGoals | marks all goals as unbeaten and 
// takes away any goal points you earned for beating them.  You have to 
// reload the level to re-initialize the goals, however
bool ScriptUnBeatAllGoals( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_MsgAssert( pGoalManager, ("couldn't get goal manager") );

	pGoalManager->UnBeatAllGoals();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AddViewGoalsList | adds the list of goals to the 
// view goals menu.  This is called from one place within the create_view_Goals_menu
// script.
bool ScriptAddViewGoalsList( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_MsgAssert( pGoalManager, ("couldn't get goal manager") );

	pGoalManager->AddViewGoalsList();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AddGoalChoices | Adds the list of available goals to the "Choose Goals" menu
bool ScriptAddGoalChoices( Script::CStruct* pParams, Script::CScript* pScript )
{
	bool selected_only;
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_MsgAssert( pGoalManager, ("couldn't get goal manager") );

	selected_only = pParams->ContainsFlag( CRCD(0x5685c1b7,"selected_only") );

	pGoalManager->AddGoalChoices( selected_only );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GoalIsLocked | true if the goal is locked
// @parm name | name | the goal id
bool ScriptGoalIsLocked( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->GoalIsLocked( goalId );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_IsInCompetition | returns true if the player is
// in a competition
bool ScriptIsInCompetition( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	CGoal* pGoal = pGoalManager->IsInCompetition();
	if ( pGoal )
	{
		pScript->GetParams()->AddChecksum( CRCD(0x9982e501,"goal_id"), pGoal->GetGoalId() );
		return true;
	}
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetGoalAnimations | Gets the appropriate animations
// array from script.  It wil first search for an array specific to this goal and 
// type, then for an array specific to this pro, then for the generic array
// @parm name | name | the goal id
// @parmopt string | type | | the goal state - wait, start, midgoal, or success
bool ScriptGetGoalAnimations( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	const char* type = NULL;
	pParams->GetString( CRCD(0x7321a8d6,"type"), &type, Script::NO_ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->GetGoalAnimations( goalId, type, pScript );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_PlayGoalStream | plays the appropriate stream for this
// goal/pro.  The appropriate anim arrays must be setup in script for this to work
// @parm name | name | the goal id
bool ScriptPlayGoalStream( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->PlayGoalStream( pParams );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_EndRunCalled | returns true if the goal manager
// called endrun for this goal
// @parm name | name | the goal id
bool ScriptEndRunCalled(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->EndRunCalled( goalId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_ClearEndRun | This will clear the skater's endrun exception
// as well as any others.  It's used by the combo goal, which needs to clear
// the exception if you bail.  In general, this function should not be used -
// clearing the endrun exception is not usually a good idea.
// @parm name | name | the goal id
bool ScriptClearEndRun(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	
	CGoalManager* pGoalManager = GetGoalManager();
	pGoalManager->GetGoal( goalId )->ClearEndRun( pScript );
	
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_FinishedEndOfRun | returns true if EndOfRun has
// finished
// @parm name | name | the goal id
bool ScriptFinishedEndOfRun(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->GetGoal( goalId )->FinishedEndOfRun();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_StartedEndOfRun | this returns true if EndOfRun 
// has started
// @parm name | name | the goal id
bool ScriptStartedEndOfRun(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->GetGoal( goalId )->StartedEndOfRun();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_EndBetAttempt | call this to end a bet attempt. 
// If the goal id does not match the current bet, the call will be ignored.
// @parm name | name | the goal id
bool ScriptEndBetAttempt(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 id = pGoalManager->GetBettingGuyId();
	if ( id )
	{
		CBettingGuy* pGoal = (CBettingGuy*)pGoalManager->GetGoal( id );
		Dbg_Assert( pGoal );
		return pGoal->EndBetAttempt( goalId );
	}
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_StartBetAttempt | called by any minigame that is
// also used as a betting game
// @parm name | name | the goal id
bool ScriptStartBetAttempt(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 id = pGoalManager->GetBettingGuyId();
	if ( id )
	{
		CBettingGuy* pGoal = (CBettingGuy*)pGoalManager->GetGoal( id );
		Dbg_Assert( pGoal );
		return pGoal->StartBetAttempt( goalId );
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_WinBet | calling this will win the bet.
// The goal id is used to verify that the specified bet is in fact
// the active bet.  If it's not, the goal manager will ignore the call
// @parm name | name | the goal id
bool ScriptWinBet(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 id = pGoalManager->GetBettingGuyId();
	if ( id )
	{
		CBettingGuy* pGoal = (CBettingGuy*)pGoalManager->GetGoal( id );
		Dbg_Assert( pGoal );
		pGoal->Win( goalId );
	}
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_BetOffered | called once the betting guy makes 
// an offer
bool ScriptBetOffered( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 id = pGoalManager->GetBettingGuyId();
	if ( id )
	{	
		CBettingGuy* pGoal = (CBettingGuy*)pGoalManager->GetGoal( id );
		Dbg_Assert( pGoal );
		pGoal->OfferMade();
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_BetRefused | called when the player declines a betting
// guy bet
bool ScriptBetRefused( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 id = pGoalManager->GetBettingGuyId();
	if ( id )
	{	
		CBettingGuy* pGoal = (CBettingGuy*)pGoalManager->GetGoal( id );
		Dbg_Assert( pGoal );
		pGoal->OfferRefused();
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_MoveBettingGuyNow | this will force the betting guy to
// immediately move to a new, random betting game.  This is useful when the betting guy
// is created and you don't care where the skater is (otherwise the betting guy will avoid
// moving where the skater could see him).
bool ScriptMoveBettingGuyNow( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 id = pGoalManager->GetBettingGuyId();
	if ( id )
	{
		CBettingGuy* pGoal = (CBettingGuy*)pGoalManager->GetGoal( id );
		Dbg_Assert( pGoal );
		pGoal->MoveToNewNode( true );
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_BetAccepted | called when the player accepts a bet
bool ScriptBetAccepted(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 id = pGoalManager->GetBettingGuyId();
	if ( id )
	{
		CBettingGuy* pGoal = (CBettingGuy*)pGoalManager->GetGoal( id );
		Dbg_Assert( pGoal );
		pGoal->OfferAccepted();
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_BetIsActive | returns true if the bet is currently
// active - ie, the bet guy has offered the bet and the player accepted
// @parm name | name | the goal id
bool ScriptBetIsActive(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	uint32 betting_guy_id = pGoalManager->GetBettingGuyId();
	if ( betting_guy_id )
	{
		CBettingGuy* pBetGuy = (CBettingGuy*)pGoalManager->GetGoal( betting_guy_id );
		Dbg_Assert( pBetGuy );
		return pBetGuy->BetIsActive( goalId );
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AddMinigameTime | adds the specified amount of time
// to the minigame.  
// @parm name | name | the minigame id (usually goal_id)
// @uparm 1 | the amount of time to add in seconds
bool ScriptAddMinigameTime(Script::CStruct* pParams, Script::CScript* pScript)
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );	
	
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	int amount;
	pParams->GetInteger( NONAME, &amount, Script::ASSERT );

	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	Dbg_MsgAssert( pGoal->IsMinigame(), ("This is not a minigame") );
	CMinigame* pMinigame = (CMinigame*)pGoal;
	return pMinigame->AddTime( amount );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AddTempSpecialTrick | This is used only by the 
// special goal.
// @parm name | name | the goal id
bool ScriptAddTempSpecialTrick(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	Dbg_Assert( pGoal );

	return pGoal->AddTempSpecialTrick();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_RemoveTempSpecialTrick | this is used only by the 
// special goal.
// @parm name | name | the goal id
bool ScriptRemoveTempSpecialTrick(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->RemoveTempSpecialTrick();
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetTrickFromKeyCombo | this will find the trick
// currently bound to the given key combo.  the trick name (checksum value) will
// be placed in the script params as trick_checksum.  The trick text
// will be placed in the params as trick_string.  If no trick is bound to the 
// key combo, the function will return false
// @parm name | key_combo | the key combo to search for
bool ScriptGetTrickFromKeyCombo(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 key_combo;
	pParams->GetChecksum( CRCD(0xacfdb27a,"key_combo"), &key_combo, Script::ASSERT );

	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();
	Script::CStruct* pTricks = pSkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );
	
	uint32 trick_checksum;
    int cat_num;
	if ( !pParams->ContainsFlag( CRCD(0xb394c01c,"special") ) )
    {
        if( pTricks->GetInteger( key_combo, &cat_num, Script::NO_ASSERT ) )
        {
            // printf("it's a created trick! %i\n", cat_num );
    		pScript->GetParams()->AddInteger( CRCD(0xa75b8581,"cat_num"), cat_num );
            return true;
        }
        
        if( pTricks->GetChecksum( key_combo, &trick_checksum, Script::NO_ASSERT ) )
    	{
    		if ( trick_checksum == ( CRCD(0xf60c9090,"Unassigned") ) )
               	return false;
            
    		// return the name and checksum
    		const char* p_trick_string;
    		Script::CStruct* p_trick = Script::GetStructure( trick_checksum, Script::ASSERT );
    		Script::CStruct* p_trick_params;
    		p_trick->GetStructure( CRCD(0x7031f10c,"Params"), &p_trick_params, Script::ASSERT );
    		p_trick_params->GetLocalString( CRCD(0xa1dc81f9,"Name"), &p_trick_string, Script::ASSERT );
    
    		pScript->GetParams()->AddString( CRCD(0x3a54cc4b,"trick_string"), p_trick_string );
    		pScript->GetParams()->AddChecksum( CRCD(0x4443a4da,"trick_checksum"), trick_checksum );
    
    		// look for any double tap tricks
    		uint32 extra_trick = 0;
    		if ( !p_trick_params->GetChecksum( CRCD(0x6e855102,"ExtraTricks"), &extra_trick, Script::NO_ASSERT ) )
    		{
    			// look for an array of extra tricks and grab the first one
    			Script::CArray* p_extra_tricks;
    			if ( p_trick_params->GetArray( CRCD(0x6e855102,"ExtraTricks"), &p_extra_tricks, Script::NO_ASSERT ) )
    				extra_trick = p_extra_tricks->GetChecksum( 0 );
    		}
    
    		// add any extra trick info
    		if ( extra_trick )
    		{
    			// grab this extra trick
    			Script::CArray* p_temp = Script::GetArray( extra_trick, Script::ASSERT );
    			Script::CStruct* p_extra_trick = p_temp->GetStructure( 0 );
    			Dbg_Assert( p_extra_trick );
    			Script::CStruct* p_extra_trick_params;
    			p_extra_trick->GetStructure( CRCD(0x7031f10c,"Params"), &p_extra_trick_params, Script::ASSERT );
    			const char* p_extra_trick_string;
    			p_extra_trick_params->GetString( CRCD(0xa1dc81f9,"Name"), &p_extra_trick_string, Script::ASSERT );
    			pScript->GetParams()->AddString( CRCD(0x3daaf58b,"extra_trick_string"), p_extra_trick_string );
    			pScript->GetParams()->AddChecksum( CRCD(0xd0bcfc62,"extra_trick_checksum"), extra_trick );
    		}
    		return true;
    	}
    }
	else if ( pParams->ContainsFlag( CRCD(0xb394c01c,"special") ) )
	{		
		// check the special tricks
		int numTricks = pSkaterProfile->GetNumSpecialTrickSlots();

		// search through current special tricks
		for ( int i = 0; i < numTricks; i++ )
		{
            Obj::SSpecialTrickInfo trick_info = pSkaterProfile->GetSpecialTrickInfo( i );
		
			if ( trick_info.m_TrickSlot == key_combo )
			{
				if ( trick_info.m_TrickName == ( CRCD(0xf60c9090,"Unassigned") ) )
					return false;
				
				// printf("the special trick is already bound!\n");
				if ( Script::CStruct* p_trick = Script::GetStructure( trick_info.m_TrickName ) )
                {
                    const char* p_trick_string;
                    Script::CStruct* p_trick_params;
    				p_trick->GetStructure( CRCD(0x7031f10c,"Params"), &p_trick_params, Script::ASSERT );
    				p_trick_params->GetLocalString( CRCD(0xa1dc81f9,"Name"), &p_trick_string, Script::ASSERT );
                    pScript->GetParams()->AddString( CRCD(0x3a54cc4b,"trick_string"), p_trick_string );
                }
                
                if ( trick_info.m_isCat )
                {
                    pScript->GetParams()->AddInteger( CRCD(0x4443a4da,"trick_checksum"), trick_info.m_TrickName );
                }
                else
                {
                    pScript->GetParams()->AddChecksum( CRCD(0x4443a4da,"trick_checksum"), trick_info.m_TrickName );
                }
                pScript->GetParams()->AddInteger( CRCD(0xdb92effa,"current_index"), i );
				return true;
			}
		}
	}
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UnlockGoal | unlocks the specified goal
// @parm name | name | the goal id
bool ScriptUnlockGoal(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	CGoal* pGoal = pGoalManager->GetGoal( goalId );

	pGoal->UnlockGoal();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_QuickStartGoal | starts a goal without going
// through the intro movie, etc.
// @parm name | name | the goal id
// @flag dontAssert | used by autotester
bool ScriptQuickStartGoal(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	bool dontAssert = false;
	if ( pParams->ContainsFlag( CRCD(0x9ac8d54f,"dontAssert") ) )
		dontAssert = true;

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->QuickStartGoal( goalId, dontAssert );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_InitializeGoal | initialize the specified goal
// @parm name | name | the goal id
bool ScriptInitializeGoal( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->Init();
	return true;
}
// TODO:  it would be nice to have the cfuncs call the goalmanager's
// member functions directly...

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AddTime | adds the specified amount of time to the goal
// @parm name | name | the goal id
// @uparm 10 | time to add, in seconds
bool ScriptAddTime( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	int time;
	pParams->GetInteger( NONAME, &time, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->AddTime( time );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_ReplaceTrickText | replaces the trick name and 
// key combo with the appropriate text.  The key combo should be represented 
// in the string by \k, and the text by \t
// @parmopt name | name | | the goal id
// @flag all | in place of a goal id - update all goals
bool ScriptReplaceTrickText( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 goalId;
	if ( pParams->ContainsFlag( CRCD(0xc4e78e22,"all") ) )
	{
		pGoalManager->ReplaceAllTrickText();
		return true;
	}
	else if ( pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::NO_ASSERT ) )
	{
		const char* p_old_string;
		if ( pParams->GetString( CRCD(0xc4745838,"text"), &p_old_string, Script::NO_ASSERT ) )
		{
			char new_string[NEW_STRING_LENGTH];
			if ( pGoalManager->ReplaceTrickText( goalId, p_old_string, new_string, NEW_STRING_LENGTH ) )
			{
                pScript->GetParams()->RemoveComponent( CRCD(0x3eafa520,"TrickText") );
				pScript->GetParams()->AddString( CRCD(0x3eafa520,"TrickText"), new_string );
			}
		}
		else 
			return pGoalManager->ReplaceTrickText( goalId );
	}
	else
		Dbg_MsgAssert( 0, ( "GoalManager_ReplaceTrickText requires a goal id or the 'all' flag." ) );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
// @script | GoalManager_UnlockProSpecificChallenges | called once, when you 
// first get enough goals to unlock the pro challenge
bool ScriptUnlockProSpecificChallenges( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->UnlockProSpecificChallenges();
	return true;
}
*/
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
// @script | GoalManager_ProSpecificChallengesUnlocked | true if the pro challenges 
// have been unlocked
bool ScriptProSpecificChallengesUnlocked( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->ProSpecificChallengesUnlocked();
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetNumberCollected | returns the number of objects
// collected so far in this goal.  If the goal is not a counter goal, it just returns
// the number of flags set.  The value will be stored in the number_collected var.
// @parm name | name | the goal id
bool ScriptGetNumberCollected( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	int number_collected = pGoalManager->GetNumberCollected( goalId );
	pScript->GetParams()->AddInteger( CRCD(0x129daa10,"number_collected"), number_collected );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetNumberOfFlags | returns the number of flags in this
// goal.  If there are no flags, it returns false
// @parm name | name | the goal id
bool ScriptGetNumberOfFlags( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	int number_of_flags = pGoalManager->GetNumberOfFlags( goalId );
	if ( number_of_flags != -1 )
	{
		pScript->GetParams()->AddInteger( CRCD(0xcb9d1224,"number_of_flags"), number_of_flags );
		return true;
	}
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_IsPro | returns true if you're pro
bool ScriptIsPro( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->IsPro();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_ResetGoalFlags | resets all of the goals flags
bool ScriptResetGoalFlags( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->ResetGoalFlags( goalId );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_ColorTrickObjects | colors all trick objects 
// associated with this goal
// @parm name | name | the goal id
// @flag clear | pass this in if you want to clear the color
// @parmopt int | skater | 0 | the skater num
bool ScriptColorTrickObjects( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	bool clear = pParams->ContainsFlag( CRCD(0x1a4e0ef9,"clear") );

	int skaterId = 0;
	pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skaterId, Script::NO_ASSERT );

	int seqIndex = skaterId + 1;

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->ColorTrickObjects( goalId, seqIndex, clear );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetNumberOfTimesGoalStarted | gets the total number
// of times the player has lost this goal.  The number will be stored in
// the calling scripts params as times_lost
// @parm name | name | the goal id
bool ScriptGetNumberOfTimesGoalStarted( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	int numTimesLost = pGoalManager->GetNumberOfTimesGoalStarted( goalId );
	pScript->GetParams()->AddInteger( CRCD(0xff61ba23,"times_started"), numTimesLost );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GoalExists | returns true if the goal exists
// @parm name | name | the goal id
bool ScriptGoalExists( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->GoalExists( goalId );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetGoalAttemptInfo | grabs the number of times each
// goal has been lost, and stores all the data in your times_lost_info
bool ScriptGetGoalAttemptInfo( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	pScript->GetParams()->AddStructurePointer( CRCD(0xd23ad129,"goal_attempt_info"), pGoalManager->GetGoalAttemptInfo() );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetCanStartGoal | tell the goal manager that it can
// or cannot start any new goals
// @parmopt int | | 1 | 0 to block new goals
bool ScriptSetCanStartGoal( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	int value = 1;
	pParams->GetInteger( NONAME, &value, Script::NO_ASSERT );
	pGoalManager->SetCanStartGoal( value );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetLastGoalId | returns the last unbeaten goal
// as a checksum param goal_id.  If the player hasn't played a goal yet
// or beat the last goal, the function will return false.
bool ScriptGetLastGoalId( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 goalId = pGoalManager->GetLastGoalId();
	if ( goalId )
	{
		pScript->GetParams()->AddChecksum( CRCD(0x9982e501,"goal_id"), goalId );
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_ClearTetrisTricks | clears the entire stack of
// tetris tricks.
bool ScriptClearTetrisTricks( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	Dbg_MsgAssert( pGoal->IsTetrisGoal(), ( "ClearTetrisTricks called on a non-tetris goal" ) );

	CSkatetrisGoal* pSkatetrisGoal = (CSkatetrisGoal*)pGoal;
	pSkatetrisGoal->ClearAllTricks();
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool ScriptMarkProSpecificChallengeBeaten( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 skater_name;
	pParams->GetChecksum( CRCD(0x5b8ab877,"skater"), &skater_name, Script::ASSERT );

	int beaten = 1;
	pParams->GetInteger( NONAME, &beaten, Script::NO_ASSERT );
	
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->MarkProSpecificChallengeBeaten( skater_name, beaten );
	return true;
}
*/
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool ScriptSkaterHasBeatenProSpecificChallenge( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 skater_name;
	pParams->GetChecksum( CRCD(0x5b8ab877,"skater"), &skater_name, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->SkaterHasBeatenProSpecificChallenge( skater_name );
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptAnnounceGoalStarted( Script::CStruct* pParams, Script::CScript* pScript )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Net::Client* client;
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	Dbg_Assert( pGoal != NULL );

	if( pGoal->IsNetGoal())
	{
		return true;
	}
	if( pGoal->GetParents()->m_relative != 0 )
	{
		return true;
	}
	
	client = gamenet_man->GetClient( 0 );
	Dbg_Assert( client );
	GameNet::MsgStartedGoal msg;
	GameNet::PlayerInfo* local_player;
	Net::MsgDesc msg_desc;

	local_player = gamenet_man->GetLocalPlayer();
	Dbg_Assert( local_player );
	
	msg.m_GameId = gamenet_man->GetNetworkGameId();
	msg.m_GoalId = pGoal->GetGoalId();

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( GameNet::MsgStartedGoal );
	msg_desc.m_Id = GameNet::MSG_ID_STARTED_GOAL;
	client->EnqueueMessageToServer( &msg_desc );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetEndRunType | one of the flags is required
// @parm name | name | the goal id
// @flag Goal_EndOfRun | use GoalEndOfRun
// @flag EndOfRun | use EndOfRun
// @flag none | don't use end of run
bool ScriptSetEndRunType( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	Game::EndRunType newEndRunType = Game::vENDOFRUN;
	if ( pParams->ContainsFlag( CRCD(0x54585419,"Goal_EndOfRun") ) )
		newEndRunType = Game::vGOALENDOFRUN;
	else if ( pParams->ContainsFlag( CRCD(0xe022c12c,"EndOfRun") ) )
		newEndRunType = Game::vENDOFRUN;
	else if ( pParams->ContainsFlag( CRCD(0x806fff30,"none") ) )
		newEndRunType = Game::vNONE;
	else
		Dbg_MsgAssert( 0, ( "GoalManager_SetEndRunType called without type" ) );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->SetEndRunType( goalId, newEndRunType );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetShouldDeactivateOnExpire | 
// @uparm 1 | 0 for no, anything else for yes
bool ScriptSetShouldDeactivateOnExpire( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	int should_deactivate;
	pParams->GetInteger( NONAME, &should_deactivate, Script::ASSERT );
	
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->SetShouldDeactivateOnExpire( goalId, should_deactivate != 0 );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetActiveGoalId | returns the active goal's goal_id.
// Returns false if there are no active goals
bool ScriptGetActiveGoalId( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId = 0;
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	if ( pGoalManager )
	{
		int activeGoal = pGoalManager->GetActiveGoal( true );
		if ( activeGoal == -1 )
			return false;

		CGoal* pGoal = pGoalManager->GetGoalByIndex( activeGoal );
		Dbg_Assert( pGoal );
		if ( pGoal )
		{
			goalId = pGoal->GetGoalId();
			pScript->GetParams()->AddChecksum( "goal_id", goalId );
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AwardMinigameCash | 
// @parm name | name | the goal id
// @uparm 25 | amount of cash to award
bool ScriptAwardMinigameCash( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	int amount;
	pParams->GetInteger( NONAME, &amount, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	if ( pGoalManager )
	{
		return pGoalManager->AwardMinigameCash( goalId, amount );
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_ResetCareer | 
bool ScriptResetCareer( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->ResetCareer();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptAwardAllGoalCash( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->AwardAllGoalCash();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UpdateFamilyTrees |
bool ScriptUpdateFamilyTrees( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pGoalManager->UpdateFamilyTrees();
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_IsLeafNode | returns true if the passed goal
// is a leaf node in the goal tree
// @parm name | name | the goal id
bool ScriptIsLeafNode( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->CallMemberFunction( ( CRCD(0xd782f8de,"IsLeafNode") ), pParams, pScript );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_IsRootNode | returns true if the passed
// goal is the root node of a family tree.  This is true if the goal
// is the only goal in a tree.
// @parm name | name | the goal id
bool ScriptIsRootNode( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->CallMemberFunction( ( CRCD(0x3ae9211b,"IsRootNode") ), pParams, pScript );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSuspendGoalPedLogic( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	int suspend = 1;
	pParams->GetInteger( CRCD(0xce0ca665,"suspend"), &suspend, Script::NO_ASSERT );

	pGoalManager->GetGoal( pParams )->mp_goalPed->SuspendGoalLogic( suspend != 0 );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptRememberLevelStructureName( Script::CStruct *pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 level;
	pParams->GetChecksum( CRCD(0x63b1214e,"level_structure_name"), &level, Script::ASSERT );
	pGoalManager->RememberLevelStructureName( level );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetDifficultyLevel | sets the current level
// @uparm 1 | difficulty level (starts at 0)
bool ScriptSetDifficultyLevel( Script::CStruct* pParams, Script::CScript* pScript )
{
	int level;
	pParams->GetInteger( NONAME, &level, Script::ASSERT );
	Dbg_MsgAssert( level >= 0 && level <= vMAXDIFFICULTYLEVEL, ( "GoalManager_SetDifficultyLevel called with level of %i", level ) );
	
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->SetDifficultyLevel( level );
	return ( level != 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetDifficultyLevel | returns the current
// difficulty level as an integer param called difficulty_level
bool ScriptGetDifficultyLevel( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	int level = (int)pGoalManager->GetDifficultyLevel();
	pScript->GetParams()->AddInteger( CRCD(0x3671f748,"difficulty_level"), level );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
/*
bool ScriptGetGoalParam( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );
	CGoal* pGoal = pGoalManager->GetGoal( goalId );

	uint32 type_checksum;
	Script::ESymbolType type = ESYMBOLTYPE_NONE;
	pParams->GetChecksum( CRCD(0x7321a8d6,"type"), &type_checksum, Script::ASSERT );

	// convert type
	switch ( type_checksum )
	{
		case CRCC(0x21902065,"checksum"):
			type=ESYMBOLTYPE_NAME;
			break;
		case CRCC(0x365aa16a,"float"):
			type=ESYMBOLTYPE_FLOAT;
			break;
		case CRCC(0xa5f054f4,"integer"):
			type=ESYMBOLTYPE_INTEGER;
			break;
		case CRCC(0x90fec815,"structure"):
			type=ESYMBOLTYPE_STRUCTURE;
			break;
		case CRCC(0x5ef31148,"array"):
			type=ESYMBOLTYPE_ARRAY;
			break;
		default:
			Dbg_MsgAssert( 0, ( "Uknown type %x", type ) );
			break;
	}

	uint32 name;
	pParams->GetChecksum( NONAME, &name, Script::ASSERT );

	// printf("looking for %s with type %s\n", Script::FindChecksumName( name ), Script::FindChecksumName( type ) );

	return pGoal->ExtractDifficultyLevelParam( type, name, pScript->GetParams() );
}
*/
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_RestartStage | restarts the current/last stage
bool ScriptRestartStage( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->RestartStage();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_CanRestartStage | returns true if the player
// can retry a goal stage.
bool ScriptCanRestartStage( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	return pGoalManager->CanRestartStage();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSetGoalChaptersAndStages( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->SetGoalChaptersAndStages();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_AdvanceStage | returns true if the stage/chapter
// advances, false if you haven't reached an advancement point yet.
// @flag force | force the stage to advance
// @flag just_won_goal | this flag needs to be passed in if the function is called
// from a success script, as the goal is not marked beaten until after the success
// script finishes.  Therefore, the total number of goals beaten will be one more
// than the number returned by searching through the goals.
bool ScriptAdvanceStage( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	uint32 just_won_goal_id = 0;
	pParams->GetChecksum( CRCD(0x5d82c486,"just_won_goal_id"), &just_won_goal_id, Script::NO_ASSERT );
	return pGoalManager->AdvanceStage( pParams->ContainsFlag( CRCD( 0x68977cd7, "force" ) ), just_won_goal_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetCurrentChapterAndStage | returns the 
// current chapter and stage as currentChapter and currentStage
bool ScriptGetCurrentChapterAndStage( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	int chapter = pGoalManager->GetCurrentChapter();
	int stage = pGoalManager->GetCurrentStage();
	
	// return the last chapter if they beat the game
	Script::CArray* pChapterGoalsArray = Script::GetArray( CRCD( 0xb892776f, "chapter_goals" ), Script::ASSERT );
	Dbg_Assert( pChapterGoalsArray->GetType() == ESYMBOLTYPE_ARRAY );
	if ( chapter >= (int)pChapterGoalsArray->GetSize() )
	{
		chapter = pChapterGoalsArray->GetSize() - 1;
	}
	pScript->GetParams()->AddInteger( CRCD( 0xf884773c, "CurrentChapter" ), chapter );
	pScript->GetParams()->AddInteger( CRCD( 0xaf1575eb, "CurrentStage" ), stage );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetCurrentChapterAndStage | 
// @parmopt int | chapter | current | 
// @parmopt int | stage | current |
bool ScriptSetCurrentChapterAndStage( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	int chapter = pGoalManager->GetCurrentChapter();
	int stage = pGoalManager->GetCurrentStage();
	pParams->GetInteger( CRCD(0x67e4ad1,"chapter"), &chapter, Script::NO_ASSERT );
	pParams->GetInteger( CRCD(0x3d836c96,"stage"), &stage, Script::NO_ASSERT );
	pGoalManager->SetCurrentChapter( chapter );
	pGoalManager->SetCurrentStage( stage );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// @script | GoalManager_FilmGoalCheckpoint | Call this when the filming
// target reaches a checkpoint.  This is only used for filming goals that
// require the skater to catch X of Y checkpoints, not goals which requrie
// the skater to film the target for X of Y seconds.
// @parm name | name | the goal_id
bool ScriptFilmGoalCheckpoint( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	CFilmGoal* pFilmGoal = (CFilmGoal*)pGoalManager->GetGoal( pParams, true );

	pFilmGoal->CheckpointHit();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// @script | GoalManager_StartFilming | Called when the start cam anims finish.
// @parm name | name | the goal_id
bool ScriptStartFilming( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	CFilmGoal* pFilmGoal = (CFilmGoal*)pGoalManager->GetGoal( pParams, true );

	pFilmGoal->StartFilming();
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// @script | GoalManager_GoalShouldExpire | setup for endrun exceptions
bool ScriptGoalShouldExpire( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	if ( pGoalManager )
	{
		int activeGoal = pGoalManager->GetActiveGoal();
		if ( activeGoal == -1 )
			return false;

		CGoal* pGoal = pGoalManager->GetGoalByIndex( activeGoal );
		Dbg_Assert( pGoal );
		if ( pGoal )
		{
			return pGoal->ShouldExpire();
		}
	}
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetSponsor | 
// @uparm sponsor_name | the checksum of the sponsor name
bool ScriptSetSponsor( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	uint32 sponsor;
	pParams->GetChecksum( NONAME, &sponsor, Script::ASSERT );
	pGoalManager->SetSponsor( sponsor );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetSponsor | returns sponsor as param "sponsor"
// the sponsor will be 0 if not set
bool ScriptGetSponsor( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	pScript->GetParams()->AddChecksum( CRCD(0x7e73362b,"sponsor"), pGoalManager->GetSponsor() );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GetTeam | returns team structure as param named
// team.  It also returns the current number of team members, in a param 
// named num_team_members
bool ScriptGetTeam( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	Script::CStruct* pTeam = pGoalManager->GetTeam();
	pScript->GetParams()->AddStructure( CRCD(0x3b1f59e0,"team"), pTeam );
	
	// throw in the number of team members
    Script::CComponent* p_comp = NULL;
    p_comp = pTeam->GetNextComponent( p_comp );

    int total = 0;
	while ( p_comp )
    {
        Script::CComponent* p_next = pTeam->GetNextComponent( p_comp );
		total++;
		p_comp = p_next;
	}
	pScript->GetParams()->AddInteger( CRCD(0xeb8dd330,"num_team_members"), total );
	
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetTeamMember | add or remove a team member
// @parm name | pro | name of the pro to add or remove
// @flag remove | use this flag to remove the pro - will add by default
bool ScriptSetTeamMember( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	uint32 pro;
	pParams->GetChecksum( CRCD(0x944b2900,"pro"), &pro, Script::ASSERT );
	bool remove = pParams->ContainsFlag( CRCD(0x977fe2cf,"remove") );
	bool mark_used = pParams->ContainsFlag( CRCD(0x531ff702,"mark_used") );
	Dbg_MsgAssert( !( remove && mark_used ), ( "GoalManager_SetTeamMember called with remove and mark_used flags." ) );
	pGoalManager->SetTeamMember( pro, remove, mark_used );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_KillTeamMembers | removes all team members
bool ScriptKillTeamMembers( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->KillTeamMembers( );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_SetTeamName | set the team name
// @uparm "team name" | team name
bool ScriptSetTeamName( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	const char* p_string;
	pParams->GetString( NONAME, &p_string, Script::ASSERT );
	pGoalManager->SetTeamName( p_string );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptRunLastStageScript( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	pGoalManager->RunLastStageScript();
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_GoalInitialized | returns true if the goal
// is initialized
// @parm name | name | goal id
bool ScriptGoalInitialized( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	Dbg_Assert( pGoal );

	return pGoal->IsInitialized();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_UnloadLastFam | unloads the last fam file 
// loaded for the goal ped.  If the fam has already been unloaded, it
// will return
bool ScriptUnloadLastFam( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->mp_goalPed->UnloadLastFam();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_StopLastStream | stops the last stream played 
// for a cam anim
bool ScriptStopLastSream( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 goalId;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &goalId, Script::ASSERT );

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );

	CGoal* pGoal = pGoalManager->GetGoal( goalId );
	Dbg_Assert( pGoal );

	pGoal->mp_goalPed->StopLastStream();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalManager_HideAllGoalPeds | hide/unhide all goal peds
// @uparm 0 | 0 to unhide, anything else to hide
bool ScriptHideAllGoalPeds( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	int hide;
	pParams->GetInteger( NONAME, &hide, Script::ASSERT );
	pGoalManager->HideAllGoalPeds( ( hide != 0 ) );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
} // namespace Game



================================================
FILE: Code/Sk/Modules/Skate/GoalManager.h
================================================
//****************************************************************************
//* MODULE:         Sk/Modules/Skate
//* FILENAME:       GoalManager.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  1/24/2001
//****************************************************************************

#ifndef	__MODULES_SKATE_GOALMANAGER_H
#define	__MODULES_SKATE_GOALMANAGER_H

                           
#ifndef __CORE_DEFINES_H
    #include 
#endif

#ifndef __SYS_TIMER_H
    #include  // For Tmr::Time
#endif
							  
#include 

#include 
#include 

#include 
#include 
#include 

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

namespace Script
{
	class CScript;
	class CStruct;
}

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Game
{
	// script names
	const uint32 vINIT			= 0x398b1b8b;
	const uint32 vUNINIT		= 0xd4e0306a;
	const uint32 vDESTROY		= 0x83b57984;
	const uint32 vACTIVATE		= 0x4999fb52;
	const uint32 vDEACTIVATE	= 0x01c92cac;	
	const uint32 vACTIVE		= 0xb4e103fd;
	const uint32 vINACTIVE		= 0x742cf11f;
	const uint32 vEXPIRED		= 0xd34eb52b;
	const uint32 vSUCCESS		= 0x90ff204d;
	const uint32 vFAIL			= 0x79d8d4b6;
    const uint32 vUNLOCK        = 0x951b4f10;

	const uint32 vBET_SUCCESS	= 0x782bdda1;

    // flag array name - goal_flags
    const uint32 vFLAGS         = 0xcc3c4cc4;

	// number of goal flags allowed
	const uint32 vNUMGOALFLAGS = 10;

    // number of completed goals required to unlock this goal
    const int vUNLOCKED_BY_ANOTHER  = 0x8a3a324e;   // type - unlocked_by_another goal
    const int vPRO_GOAL             = 0xd303a1a3;   // type - pro_goal
	// const int vPRO_CHALLENGE		= 0xe0f96410;	// type - pro_specific_challenge

	// number of streams to search through for goal wait vo
	const int vMAXWAITSTREAMS		= 10;
	const int vMAXWINSTREAMS		= 10;
	const int vMAXMIDGOALSTREAMS	= 10;
	const int vMAXCALLSKATERBYNAMESTREAMS = 5;

	// Size of the local buffer used for replacing trick names in goals
	const uint32	NEW_STRING_LENGTH = 512;

	const int vMAXDIFFICULTYLEVEL = 2;	
	enum GOAL_MANAGER_DIFFICULTY_LEVEL
	{
		GOAL_MANAGER_DIFFICULTY_LOW = 0,
		GOAL_MANAGER_DIFFICULTY_MEDIUM,
		GOAL_MANAGER_DIFFICULTY_HIGH,
	};
	
/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CGoalManager : public Spt::Class
{
public:
	CGoalManager();
	~CGoalManager();

public:
	bool				CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript );
	uint32				GetGoalIdFromParams( Script::CStruct* pParams, bool assert = true );
	
	bool				AddGoal( uint32 goalId, Script::CStruct* pParams );
	CGoal*				GetGoal( uint32 goalId, bool assert = true );
	CGoal*				GetGoal( Script::CStruct* pParams, bool assert = true );
	CGoal*				GetGoalByIndex( int index );
	bool				ActivateGoal( uint32 goalId, bool dontAssert );
	bool				DeactivateGoal( uint32 goalId );
	bool				WinGoal( uint32 goalId );
	bool				LoseGoal( uint32 goalId );
	bool				RemoveGoal( uint32 goalId );
    void                UnlockGoal( uint32 goalId );
    void                LevelUnload();
    void                LevelLoad();

	void				UpdateFamilyTrees();
	void				FindCircularLinks( uint32 parent, uint32 child );
	
    bool				GoalIsActive( uint32 goalId );
	bool				EditGoal( uint32 goalId, Script::CStruct* pParams );
	bool				SetGoalTimer( uint32 goalId, int time = -1 );
	int					GetNumGoals();
	int					GetNumActiveGoals( bool count_all = false );
	int					GetNumSelectedGoals();
	void				DeactivateAllGoals( bool include_net_goals = false );
	void				UninitializeGoal( uint32 goalId );
	void				UninitializeGoalTree( uint32 start_goal );
	void				UninitializeAllGoals();
	void				DeactivateAllMinigames();
	void				InitializeAllMinigames();
	void				InitializeAllGoals();
	void				InitializeAllSelectedGoals();
	void				DeselectAllGoals();
	void				RemoveAllGoals();
	void				Update();
    bool                HasSeenGoal( uint32 goalId );
    void                PauseAllGoals();
    void                UnPauseAllGoals();
    Tmr::Time           GetGoalTime();
    int                 GetActiveGoal( bool ignore_net_goals = false );
    bool                QuickStartGoal( uint32 goalId, bool dontAssert = false );
	void                RestartLastGoal();
	bool				CanRestartStage();
	void				RestartStage();
    bool                HasWonGoal( uint32 goalId );
	bool				GoalIsSelected( uint32 goalId );
	void				ToggleGoalSelection( uint32 goalId );
	void				CreateGoalLevelObjects( void );
    
    // void                CreateGoalFlag( uint32 goalId, uint32 flag );
    bool                SetGoalFlag( uint32 goalId, uint32 flag, int value );
	bool				GoalFlagSet( uint32 goalId, uint32 flag );
    bool                AllFlagsSet( uint32 goalId );
    
	bool				CreatedGapGoalIsActive(); // Added by Ken
	bool				GetCreatedGoalGap(int gapIndex); // Also added by Ken

    bool                CanRetryGoal();
    Script::CStruct*    GetGoalParams( uint32 goalId );
    bool                CanStartGoal();
    
    bool                NextRaceWaypoint( uint32 goalId );
    bool                NextTourSpot( uint32 goalId );
    void                InitTrickObjects( uint32 goalId );
	bool				ShouldLogTrickObject();
    bool                SetGraffitiMode( int mode );

	void				GotTrickObject( uint32 clusterId, int score );
    void                SetSpecialGoal( uint32 goalId, int set );
	void                CheckTrickText();
//	void                CheckTetrisTricks();
    void                GotCounterObject( uint32 goalId );
    bool                CounterGoalDone( uint32 goalId );
    void                SpendGoalPoints( int num );
    bool                HasGoalPoints( int num );
    int                 GetNumberOfGoalPoints();
	int					GetTotalNumberOfGoalPointsEarned();
	void				ClearGoalPoints();
    void                AddGoalPoint();
    void                AddCash( int amount );
    int                 GetCash( bool get_total = false );
    bool                SpendCash( int amount );
    bool                HasBeatenGoalWithProset( const char* proset_prefix );
	int                 NumGoalsBeatenBy( int obj_id );
	int                 NumGoalsBeatenByTeam( int team_id );
	int                 NumGoalsBeaten();
    int                 NumGoalsBeatenInLevel( int levelNum, bool count_pro_specific_challenges = false );
    void                UnlockAllGoals();
	void				TurnPro();
	bool				CheckMinigameRecord( uint32 goalId, int value );
	void				DeactivateMinigamesWithTimer();
	void				DeactivateCurrentGoal();
	void				SetStartTime( uint32 goalId );
	void				UpdateComboTimer( uint32 goalId );
	void				SetStartHeight( uint32 goalId );
	bool				CheckHeightRecord( uint32 goalId );
	bool				CheckDistanceRecord( uint32 goalId );

	void				PlayGoalStartStream( Script::CStruct* pParams );
	void				StopCurrentStream( uint32 goalId );
	void				PlayGoalWinStream( Script::CStruct* pParams );
	void				PlayGoalWaitStream( uint32 goalId );
	void				PlayGoalStream( Script::CStruct* pParams );

	void				GetGoalAnimations( uint32 goalId, const char* type, Script::CScript* pScript );

	void				PauseGoal( uint32 goalId );
	void				UnPauseGoal( uint32 goalId );

	void				PauseCompetition( uint32 goalId );
	void				UnPauseCompetition( uint32 goalId );

	void				UnBeatAllGoals();
	void				AddViewGoalsList();
	void				AddGoalChoices( bool selected_only );

	bool				GoalIsLocked( uint32 goalId );

	CGoal*				IsInCompetition();

	uint32				GetRandomBettingMinigame();
	bool				EndRunCalled( uint32 goalId );

	uint32				GetBettingGuyId();

	Script::CStruct*	GetGoalManagerParams();

	bool				ReplaceTrickText( uint32 goalId );
	bool				ReplaceTrickText( uint32 goalId, const char* p_old_string, char* p_new_string, uint out_buffer_size );
	void				ReplaceAllTrickText();

	// void				UnlockProSpecificChallenges();
	// bool				ProSpecificChallengesUnlocked();

	int					GetNumberCollected( uint32 goalId );
	int					GetNumberOfFlags( uint32 goalId );

	bool 				IsPro();

	void				ResetGoalFlags( uint32 goalId );

	void				ColorTrickObjects( uint32 goalId, int seqIndex, bool clear = false );

	int					GetNumberOfTimesGoalStarted( uint32 goalId );

	bool				GoalExists( uint32 goalId );

	Script::CStruct*	GetGoalAttemptInfo();

	void				SetCanStartGoal( int value );
	void				CheckTrickObjects();

	uint32				GetLastGoalId() { return m_lastGoal; }
	void				ClearLastGoal( void );

	// void				MarkProSpecificChallengeBeaten( uint32 skater_name, int beaten = 1 );
	// bool				SkaterHasBeatenProSpecificChallenge( uint32 skater_name );

	void				SetEndRunType( uint32 goalId, EndRunType newEndRunType );
	bool				ShouldUseTimer();
	void				SetShouldDeactivateOnExpire( uint32 goalId, bool should_deactivate );
	void				Land();
	void				Bail();
	bool				AwardMinigameCash( uint32 goalId, int amount );
	void				ResetCareer();
	void				AwardAllGoalCash();

	void				RememberLevelStructureName( uint32 level ) { m_levelStructureName = level; }
	uint32				GetLevelStructureName() { return m_levelStructureName; }

	void				SetDifficultyLevel( int level );
	GOAL_MANAGER_DIFFICULTY_LEVEL GetDifficultyLevel( bool career = false );

	void				SetGoalChaptersAndStages();
	bool				AdvanceStage( bool force = false, uint32 just_won_goal_id = 0 );
	int					GetCurrentChapter() { return m_currentChapter; }
	int					GetCurrentStage() { return m_currentStage; }
	void				SetCurrentChapter( int chapter ) { m_currentChapter = chapter; }
	void				SetCurrentStage( int stage ) { m_currentStage = stage; }
	void				RunLastStageScript();
	void				SetSponsor( uint32 sponsor ) { m_sponsor = sponsor; }
	uint32				GetSponsor() { return m_sponsor; }
	void				SetTeamMember( uint32 pro, bool remove = false, bool mark_used = false );
	void				KillTeamMembers( );
	void				SetTeamName( const char* pTeamName );
	Script::CStruct*	GetTeam() { return mp_team; }
	void				HideAllGoalPeds( bool hide );
protected:
	Lst::LookupTable	    m_goalList;
	Script::CStruct*            mp_goalFlags;
    
    uint32                      m_lastGoal;
	uint32						m_lastStage;
    int                         m_graffitiMode;
    int                         m_goalPoints;
	int							m_totalGoalPointsEarned;
    int                         m_cash;
	int							m_totalCash;
    
	int							m_numGoalsBeaten;
	bool 						m_isPro;
	// bool						m_proChallengesUnlocked;
	bool						m_canStartGoal;

	int							m_currentChapter;
	int							m_currentStage;

	uint32						m_sponsor;
	Script::CStruct*			mp_team;

	// Script::CStruct*			mp_proSpecificChallenges;

	uint32						m_levelStructureName;

	Script::CStruct* 			mp_difficulty_levels;
};

CGoalManager*           GetGoalManager();

bool ScriptAddGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRemoveGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEditGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptActivateGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDeactivateGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptClearLastGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptWinGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLoseGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRemoveAllGoals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDeactivateAllGoals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUninitializeAllGoals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUninitializeGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUpdateAllGoals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptInitializeAllGoals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptInitializeAllSelectedGoals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptHasActiveGoals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGoalIsActive(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetGoalTimer(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptZeroGoalTimer(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetGoalFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptHasSeenGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPauseAllGoals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnPauseAllGoals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRestartLastGoal(Script::CStruct *pParams, Script::CScript *pScript);
// bool ScriptCreateGoalFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGoalFlagSet(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAllFlagsSet(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCanRetryGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetGoalParams(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCanStartGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetCanStartGoal( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptNextRaceWaypoint(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptNextTourSpot(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptInitTrickObjects(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCreateGoalName(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptGetLevelPrefix( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetGraffitiMode(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptSetSpecialGoal(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptGetNumGoalsCompleted(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptGetNumGoalsNeeded(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptHasWonGoal(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptGoalIsSelected(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptToggleGoalSelection(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptGoalsAreSelected(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptGotCounterObject(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptCounterGoalDone(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptAddGoalPoint(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptSpendGoalPoints(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptHasGoalPoints(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptClearGoalPoints(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptGetNumberOfGoalPoints(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptAddCash(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptGetCash(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptSpendCash(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptHasBeatenGoalWithProset(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptGetProsetNotPrefix(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptLevelLoad(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptLevelUnload(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptNumGoalsBeatenInLevel(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptUnlockAllGoals(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptCheckMinigameRecord(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptDeactivateCurrentGoal(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptSetStartTime(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptUpdateComboTimer(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptSetStartHeight(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptCheckHeightRecord(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptCheckDistanceRecord(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptShowGoalPoints(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptHideGoalPoints(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptHidePoints(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptShowPoints(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptTurnPro(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptDeactivateAllMinigames(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptInitializeAllMinigames(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptPauseGoal(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptUnPauseGoal(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptPauseCompetition(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptUnPauseCompetition(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptPlayGoalStartStream(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptPlayGoalWinStream(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptStopCurrentStream(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptPlayGoalWaitStream(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptUnBeatAllGoals(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptAddViewGoalsList(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptGoalIsLocked(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptIsInCompetition(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptGetGoalAnimations(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptPlayGoalStream(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptSetEndRunType(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptEndRunCalled(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptClearEndRun(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptFinishedEndOfRun(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptStartedEndOfRun(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptEndBetAttempt(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptStartBetAttempt(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptBetOffered(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptBetRefused(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptWinBet(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptMoveBettingGuyNow(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptBetAccepted(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptBetIsActive(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptAddMinigameTime(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptAddTempSpecialTrick(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptRemoveTempSpecialTrick(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptGetTrickFromKeyCombo(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptUnlockGoal(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptQuickStartGoal( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptInitializeGoal( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGoalInitialized( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptAddGoalChoices( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptAddTime( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptReplaceTrickText( Script::CStruct* pParams, Script::CScript* pScript );

// bool ScriptUnlockProSpecificChallenges( Script::CStruct* pParams, Script::CScript* pScript );
// bool ScriptProSpecificChallengesUnlocked( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGetNumberCollected( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetNumberOfFlags( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptIsPro( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptResetGoalFlags( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptColorTrickObjects( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGetNumberOfTimesGoalStarted( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGoalExists( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGetGoalAttemptInfo( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGetLastGoalId( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptClearTetrisTricks( Script::CStruct* pParams, Script::CScript* pScript );

// bool ScriptMarkProSpecificChallengeBeaten( Script::CStruct* pParams, Script::CScript* pScript );
// bool ScriptSkaterHasBeatenProSpecificChallenge( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptAnnounceGoalStarted( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptAnnounceGoalExited( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptSetShouldDeactivateOnExpire( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGetActiveGoalId( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptAwardMinigameCash( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptResetCareer( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptAwardAllGoalCash( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptUpdateFamilyTrees( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptIsLeafNode( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptIsRootNode( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptSuspendGoalPedLogic( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptRememberLevelStructureName( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptSetDifficultyLevel( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetDifficultyLevel( Script::CStruct* pParams, Script::CScript* pScript );

// bool ScriptGetGoalParam( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptRestartStage( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptCanRestartStage( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptSetGoalChaptersAndStages( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptAdvanceStage( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetCurrentChapterAndStage( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetCurrentChapterAndStage( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptRunLastStageScript( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptFilmGoalCheckpoint( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptStartFilming( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGoalShouldExpire( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptSetSponsor( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetSponsor( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetTeam( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetTeamMember( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptKillTeamMembers( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetTeamName( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptUnloadLastFam( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptStopLastSream( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptHideAllGoalPeds( Script::CStruct* pParams, Script::CScript* pScript );
}

#endif // __MODULES_SKATE_GOALMANAGER_H



================================================
FILE: Code/Sk/Modules/Skate/GoalPed.cpp
================================================
// goal ped!

#include 
#include 

#include 

#include 
#include 
#include 

#include 
#include 

#include 

#include 

#include 
#include 
#include 
#include 
#include 

namespace Game
{

CGoalPed::CGoalPed( Script::CStruct* pParams )
{
	pParams->GetChecksum( CRCD(0x9982e501,"goal_id"), &m_goalId, Script::ASSERT );
	m_goalLogicSuspended = false;
	m_lastFam = 0;
	m_lastFamObj = 0;
	m_lastStreamId = 0;
}

CGoalPed::~CGoalPed()
{
	// nothing yet.
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/	

inline void parse_first_name( char* pFirstName, const char** ppDisplayName, int buffer_size )
{
	if ( !strstr( *ppDisplayName, " " ) )
	{		
		Dbg_MsgAssert( strlen( *ppDisplayName ) < (uint32)buffer_size, ( "buffer overflow" ) );
		strcpy( pFirstName, *ppDisplayName );
		// printf("first name has no spaces, using %s\n", pFirstName);
	}
	else
	{
		char *p_in = pFirstName;
		int length = 0;
		while ( **ppDisplayName != ' ' )
		{
			*p_in++ = **ppDisplayName;
			(*ppDisplayName)++;
			length++;
		}
		*p_in++ = '\0';
		length++;
		Dbg_MsgAssert( length < buffer_size, ( "buffer overflow" ) );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/	

const char* CGoalPed::GetProFirstName()
{	
	// get params
	Script::CStruct* pParams = GetGoalParams();
	
	// grab the pro
	const char* p_default_pro_name;
	const char* p_alternate_pro_name;
	if ( !pParams->GetText( CRCD(0x944b2900,"pro"), &p_default_pro_name ) )
	{
		// printf("no pro associated with this goal\n");
		return 0;
	}

	// check what pro the player is using
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();	
	const char* p_current_pro_name;
	p_current_pro_name = pSkaterProfile->GetDisplayName();
	// printf("the current display name is %s\n", p_current_pro_name);
	
	// only check the display name if it's a pro
	bool use_alternate = false;
	if ( pSkaterProfile->IsPro() )
	{	
		// chop off the first name
		char current_pro_first_name[128];
		parse_first_name( current_pro_first_name, &p_current_pro_name, 128 );

		Dbg_MsgAssert( strlen( current_pro_first_name ) > 0, ( "No first name found for the current pro" ) );
		// printf("the player's first name is %s\n", current_pro_first_name);
	
		// see if we should use the alternate pro
		if ( pSkaterProfile->IsPro() && ( Script::GenerateCRC( current_pro_first_name ) == Script::GenerateCRC( p_default_pro_name ) ) )
		{
			if ( pParams->GetText( CRCD(0x878ccc12,"alternate_pro"), &p_alternate_pro_name ) )
			{
				use_alternate = true;
				// printf("found an alternate name of %s\n", p_alternate_pro_name);
			}
			else
			{
				// printf("no alternate pro found for this goal\n");
				return 0;
			}
		}
	}
	
	// build the stream name
	if ( use_alternate )
		return p_alternate_pro_name;
	else
		return p_default_pro_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/	

inline void possibly_add_success_suffix( bool success, char* buffer, int buffer_length )
{
	if ( success )
	{
		Dbg_MsgAssert( (int)strlen( buffer ) + 8 < buffer_length, ( "buffer overflow" ) );
		sprintf( buffer, "%s_success", buffer );
	}
	// printf("done creating stream %s\n", buffer );
}

// returns true if it found a lip version of the stream, signifying
// that there exists a fam associated with the stream
bool CGoalPed::GetStreamChecksum( uint32* p_streamChecksum, int cam_anim_index, bool success, const char* p_speaker_name, bool last_anim )
{
	Script::CStruct* pGoalParams = GetGoalParams();

	const char* p_first_name = GetProFirstName();
	if ( !p_first_name )
		return 0;

	const char* p_goal_name;
	pGoalParams->GetText( CRCD(0xbfecc45b,"goal_name"), &p_goal_name, Script::ASSERT );
	
	// build stream name
	char p_stream_name[MAX_STREAM_NAME_LENGTH];
	Dbg_MsgAssert( strlen( p_first_name ) + strlen( p_goal_name ) + 1 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
	sprintf( p_stream_name, "%s_%s", p_first_name, p_goal_name );

	#ifdef	__NOPT_ASSERT__
	uint32 stream_name_length = (uint32)strlen( p_stream_name );
	#endif
	
	// get string representation of difficulty level
	const char* p_difficulty_level_string;
	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	GOAL_MANAGER_DIFFICULTY_LEVEL difficulty_level = pGoalManager->GetDifficultyLevel();
	switch ( difficulty_level )
	{
		case ( GOAL_MANAGER_DIFFICULTY_LOW ):
			p_difficulty_level_string = "easy";
			break;
		case ( GOAL_MANAGER_DIFFICULTY_MEDIUM ):
			p_difficulty_level_string = "medium";
			break;
		case ( GOAL_MANAGER_DIFFICULTY_HIGH ):
			p_difficulty_level_string = "hard";
			break;
		default:
			Dbg_MsgAssert( 0, ( "Unknown difficulty level" ) );
			return 0;
			break;
	}

	// build different prefix possibilities
	// format is: _____success_lip
	// where difficulty, camAnimXX, speaker, success, and lip are optional

	// figure out if we need to look for a female vo
	bool is_female_skater = false;
	if ( p_speaker_name && Script::GenerateCRC( p_speaker_name ) == CRCD(0x5b8ab877,"skater") )
	{
		Mdl::Skate * pSkate = Mdl::Skate::Instance();
		Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();	
		int male_test;
		pSkaterProfile->GetInfo()->GetInteger( CRCD(0x3f813177,"is_male"), &male_test, Script::ASSERT );
		if ( !male_test )
		{
			is_female_skater = true;
		}
	}
	
	// diff level
	char p_diff_stream_name[MAX_STREAM_NAME_LENGTH];
	Dbg_MsgAssert( stream_name_length + strlen( p_difficulty_level_string ) + 1 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
	sprintf( p_diff_stream_name, "%s_%s", p_stream_name, p_difficulty_level_string );
	#ifdef	__NOPT_ASSERT__
	uint32 diff_stream_name_length = (uint32)strlen( p_diff_stream_name );
	#endif
	
	// if we have a cam anim index...
	char p_diff_cam_stream_name[MAX_STREAM_NAME_LENGTH];
	strcpy( p_diff_cam_stream_name, "" );
	char p_cam_stream_name[MAX_STREAM_NAME_LENGTH];
	strcpy( p_cam_stream_name, "" );
	char p_cam_speaker_stream_name[MAX_STREAM_NAME_LENGTH];
	strcpy( p_cam_speaker_stream_name, "" );
	char p_diff_cam_speaker_stream_name[MAX_STREAM_NAME_LENGTH];
	strcpy( p_diff_cam_speaker_stream_name, "" );
	if ( cam_anim_index >= 0 && !last_anim )
	{	
		// diff level and cam anim
		Dbg_MsgAssert( diff_stream_name_length + 10 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
		sprintf( p_diff_cam_stream_name, "%s_camAnim%02i", p_diff_stream_name, cam_anim_index );
		possibly_add_success_suffix( success, p_diff_cam_stream_name, MAX_STREAM_NAME_LENGTH );
		
		// cam anim
		Dbg_MsgAssert( stream_name_length + 10 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
		sprintf( p_cam_stream_name, "%s_camAnim%02i", p_stream_name, cam_anim_index );
		possibly_add_success_suffix( success, p_cam_stream_name, MAX_STREAM_NAME_LENGTH );

		if ( p_speaker_name )
		{
			#ifdef	__NOPT_ASSERT__
			int speaker_name_length = strlen( p_speaker_name );
			// cam anim and speaker name
			Dbg_MsgAssert( stream_name_length + 10 + speaker_name_length + 1 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
			#endif
			
			sprintf( p_cam_speaker_stream_name, "%s_camAnim%02i_%s", p_stream_name, cam_anim_index, p_speaker_name );
			if ( is_female_skater )
			{
				Dbg_MsgAssert( strlen( p_cam_speaker_stream_name ) + 2 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
				sprintf( p_cam_speaker_stream_name, "%s_f", p_cam_speaker_stream_name );
			}
			possibly_add_success_suffix( success, p_cam_speaker_stream_name, MAX_STREAM_NAME_LENGTH );

			
			// diff level, cam anim, and speaker name
			Dbg_MsgAssert( diff_stream_name_length + 10 + speaker_name_length + 1 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
			sprintf( p_diff_cam_speaker_stream_name, "%s_camAnim%02i_%s", p_diff_stream_name, cam_anim_index, p_speaker_name );
			if ( is_female_skater )
			{
				Dbg_MsgAssert( strlen( p_diff_cam_speaker_stream_name ) + 2 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
				sprintf( p_diff_cam_speaker_stream_name, "%s_f", p_diff_cam_speaker_stream_name );
			}
			possibly_add_success_suffix( success, p_diff_cam_speaker_stream_name, MAX_STREAM_NAME_LENGTH );
		}		
	}

	char p_diff_speaker_stream_name[MAX_STREAM_NAME_LENGTH];
	strcpy( p_diff_speaker_stream_name, "" );
	char p_speaker_stream_name[MAX_STREAM_NAME_LENGTH];
	strcpy( p_speaker_stream_name, "" );
	if ( p_speaker_name )
	{
		#ifdef	__NOPT_ASSERT__
		int speaker_name_length = strlen( p_speaker_name );
		// diff level and speaker name
		Dbg_MsgAssert( diff_stream_name_length + speaker_name_length + 1 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
		#endif
		sprintf( p_diff_speaker_stream_name, "%s_%s", p_diff_stream_name, p_speaker_name );
		if ( is_female_skater )
		{
			Dbg_MsgAssert( strlen( p_diff_speaker_stream_name ) + 2 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
			sprintf( p_diff_speaker_stream_name, "%s_f", p_diff_speaker_stream_name );
		}
		possibly_add_success_suffix( success, p_diff_speaker_stream_name, MAX_STREAM_NAME_LENGTH );
	
		// speaker name
		Dbg_MsgAssert( stream_name_length + speaker_name_length + 1 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
		sprintf( p_speaker_stream_name, "%s_%s", p_stream_name, p_speaker_name );
		if ( is_female_skater )
		{
			Dbg_MsgAssert( strlen( p_speaker_stream_name ) + 2 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
			sprintf( p_speaker_stream_name, "%s_f", p_speaker_stream_name );
		}
		possibly_add_success_suffix( success, p_speaker_stream_name, MAX_STREAM_NAME_LENGTH );
	}
	
	// now that we're done using the diff stream  and stream name
	// as pieces, add the success suffix
	possibly_add_success_suffix( success, p_diff_stream_name, MAX_STREAM_NAME_LENGTH );
	possibly_add_success_suffix( success, p_stream_name, MAX_STREAM_NAME_LENGTH );
	
	// array of pointers to my streams to make the code more readable
	// 0 - p_stream_name
	// 1 - p_diff_stream_name
	// 2 - p_diff_cam_stream_name
	// 3 - p_cam_stream_name
	// 4 - p_cam_speaker_stream_name
	// 5 - p_diff_cam_speaker_stream_name
	// 6 - p_diff_speaker_stream_name
	// 7 - p_speaker_stream_name
	char* pp_stream_names[8];
	pp_stream_names[0] = p_stream_name;
	pp_stream_names[1] = p_diff_stream_name;
	pp_stream_names[2] = p_speaker_stream_name;
	pp_stream_names[3] = p_diff_speaker_stream_name;
	pp_stream_names[4] = p_diff_cam_stream_name;
	pp_stream_names[5] = p_cam_stream_name;
	pp_stream_names[6] = p_cam_speaker_stream_name;
	pp_stream_names[7] = p_diff_cam_speaker_stream_name;

	// search!
	// if there's a cam anim index defined, don't search for 
	// ones without cam anim index
	int start_index = 0;
	if ( cam_anim_index >= 0 )
	{
		start_index = 4;
	}

	// search for regular or lip version of stream
	uint32 existing_stream = 0;
	bool found_lip = false;
	for ( int i = start_index; i < 8; i++ )
	{
		// make sure there's something to check
		int stream_name_length = strlen( pp_stream_names[i] );
		if ( stream_name_length == 0 )
		{
			continue;
		}
		
		// test lip version first - it's more likely to happen
		char p_lip_stream[MAX_STREAM_NAME_LENGTH];
		Dbg_MsgAssert( (uint32)stream_name_length + 4 < MAX_STREAM_NAME_LENGTH, ( "buffer overflow" ) );
		sprintf( p_lip_stream, "%s_lip", pp_stream_names[i] );
		// printf("looking for %s\n", p_lip_stream );
		uint32 lip_stream_checksum = Script::GenerateCRC( p_lip_stream );
		if ( Pcm::StreamExists( lip_stream_checksum ) )
		{
			// printf("Existing stream: %s\n", p_lip_stream);
			existing_stream = lip_stream_checksum;
			found_lip = true;
			break;
		}
		else
		{
			uint32 stream_checksum = Script::GenerateCRC( pp_stream_names[i] );
			// printf("looking for %s\n", pp_stream_names[i]);
			if ( Pcm::StreamExists( stream_checksum ) )
			{
				// printf( "Existing stream: %s\n", pp_stream_names[i] );
				existing_stream = stream_checksum;
				break;				
			}
		}
	}
	*p_streamChecksum = existing_stream;
	return found_lip;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/	

void CGoalPed::GetPlayerFirstName( char* p_first_name, int buffer_size )
{
	// check what pro the player is using
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();	
	
	const char* p_current_pro_name;
	p_current_pro_name = pSkaterProfile->GetDisplayName();
	
	strcpy( p_first_name, "" );
	parse_first_name( p_first_name, &p_current_pro_name, buffer_size );
	Dbg_MsgAssert( strlen( p_first_name ) > 0, ( "No current player name found" ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoalPed::PlayGoalStream( uint32 stream_checksum, bool use_pos_info, uint32 speaker_obj_id, bool should_load_fam )
{
	if ( m_goalLogicSuspended )
		return;

	if ( !Pcm::StreamExists( stream_checksum ) )
		return;

	// get params
	Script::CStruct* pParams = GetGoalParams();

	// printf("PlayGoalStream: %x\n", stream_checksum );
	
	// run script and associate with trigger
	uint32 trigger_obj_id = speaker_obj_id;
	if ( !trigger_obj_id )
	{
		pParams->GetChecksum( CRCD(0x02d7e03d,"trigger_obj_id"), &trigger_obj_id, Script::ASSERT );
	}
	
	Obj::CCompositeObject* p_pos_obj = NULL;
	if ( trigger_obj_id == CRCD(0x5b8ab877,"skater") )
	{		
		Mdl::Skate* skate_mod = Mdl::Skate::Instance();
		p_pos_obj = skate_mod->GetLocalSkater();
	}
	else
	{
		//p_pos_obj = Obj::CMovingObject::m_hash_table.GetItem( trigger_obj_id );
		p_pos_obj = (Obj::CCompositeObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID( trigger_obj_id);
	}
	
	if ( p_pos_obj )
	{		
		Script::CStruct* p_tempParams = new Script::CStruct();
		p_tempParams->AddChecksum( CRCD(0x0572ea28,"stream_checksum"), stream_checksum );

		if ( !use_pos_info )
		{
			p_tempParams->AddInteger( CRCD(0xc9b0deb7,"use_pos_info"), 0 );
			
			// get the reference checksum, so that we can play anims on the correct skeleton
			// (either animload_thps5_human or animload_ped_female)
			Obj::CAnimationComponent* pAnimComponent = GetAnimationComponentFromObject( p_pos_obj );
			Dbg_Assert( pAnimComponent );
			uint32 reference_checksum = pAnimComponent->GetAnimScriptName();
	
			if ( should_load_fam )
			{
				LoadFam( stream_checksum, reference_checksum );
			}
		
			// preload the stream
			uint32 streamId = Pcm::PreLoadStream( stream_checksum, 100 );
			p_tempParams->AddChecksum( CRCD(0x8a68ab90,"streamId"), streamId );
			m_lastStreamId = streamId;
	
			// Script::KillSpawnedScriptsThatReferTo( CRCD(0x5376a011,"goal_play_stream") );
			
			// delete p_tempParams;
		
			// look for an FAM (frame amplitude) with that name
			// (needs an actual skeleton name, which we will
			// hardcode as "animload_thps5_human" for now)
			Ass::CAssMan* ass_man = Ass::CAssMan::Instance();
			if ( ass_man->GetAsset( stream_checksum + reference_checksum, false ) )
			{
				p_tempParams->AddChecksum( NONAME, CRCD(0x93516575,"play_anim") );
				m_lastFamObj = trigger_obj_id;
				
				/*Script::CStruct* pTempStruct = new Script::CStruct;
				pTempStruct->AddChecksum( CRCD(0x7321a8d6,"type"), CRCD(0x659bf355,"partialAnim") );
				pTempStruct->AddChecksum( CRCD(0x40c698af,"id"), CRCD(0xc0ba0665,"jawRotation") );
				
				// first we want to kill off any old jaw rotation anims that might be playing...
				// pAnimComponent->CallMemberFunction( CRCD(0x986d274e,"RemoveAnimController"), pTempStruct, NULL );
				// Script::RunScript( CRCD(0x986d274e,"RemoveAnimController"), pTempStruct, p_pos_obj );
																				  
				pTempStruct->AddChecksum( CRCD(0x6c2bfb7f,"animName"), stream_checksum );
				pTempStruct->AddChecksum( CRCD(0x46e55e8f,"from"), CRCD(0x6086aa70,"start") );
				pTempStruct->AddChecksum( CRCD(0x28782d3b,"to"), CRCD(0xff03cc4e,"end") );
				//pTempStruct->AddChecksum( NONAME, CRCD(0x4f792e6c,"cycle") );
				pTempStruct->AddFloat( CRCD(0xf0d90109,"speed"), 1.0f );
				// printf("adding animcontroller to %s\n", Script::FindChecksumName( trigger_obj_id ) );
				pAnimComponent->CallMemberFunction( CRCD(0x83654874,"AddAnimController"), pTempStruct, NULL );
				// Script::RunScript( CRCD(0x83654874,"AddAnimController"), pTempStruct, p_pos_obj );
				
				delete pTempStruct;*/
			}
			else
			{
				Dbg_Message( "Couldn't find FAM file '%s'...  is it preloaded?.\n", Script::FindChecksumName(stream_checksum) );
			}
		}
		Script::CScript* pSpawnedScript = Script::SpawnScript( CRCD(0x5376a011,"goal_play_stream"), p_tempParams );
		pSpawnedScript->mpObject = p_pos_obj;
		delete p_tempParams;
	}
	else
	{
		Dbg_Message( "WARNING: Could not find pro to associate with goal_play_stream" );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoalPed::PlayGoalStream( const char* type, Script::CStruct* pParams )
{
	// Garrett: This function can take over 1 ms to execute when searching for multiple streams.
	//uint64 start_time = Tmr::GetTimeInUSeconds();	

	if ( m_goalLogicSuspended )
		return;

	// check for any flags
	bool play_random = pParams->ContainsFlag( CRCD(0x2be2056a,"play_random") );
	bool call_player_by_name = pParams->ContainsFlag( CRCD(0x9f38b332,"call_player_by_name") );

	bool found_player_name_vo = false;

	if ( play_random && call_player_by_name )
	{
		Dbg_MsgAssert( 0, ("GoalManager_PlayGoalStream called with play_random and call_player_by_name flags") );
	}

	uint32 stream_checksum;
	if ( pParams->GetChecksum( CRCD(0x1a4e4fb9,"vo"), &stream_checksum, Script::NO_ASSERT ) )
	{
		// play it!
		PlayGoalStream( stream_checksum );
		//uint64 end_time = Tmr::GetTimeInUSeconds();	
		//if ((end_time - start_time) > 250)
		//	Dbg_Message("PlayGoalStream String 1 Time %d", (end_time - start_time));
		return;
	}
	// else if ( call_player_by_name )
	else if ( play_random || call_player_by_name )
	{
		// check what pro the player is using
		Mdl::Skate * pSkate = Mdl::Skate::Instance();
		Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();	

		// don't play the special vo unless they're using a pro.
		if ( pSkaterProfile->IsPro() )
		{
			//uint64 mid_time = Tmr::GetTimeInUSeconds();	

			const char* p_speaker_name = GetProFirstName();
			char p_player_name[128];
			GetPlayerFirstName( p_player_name, 128 );
			char stream_name[128];

			Script::CArray* p_stream_numbers = new Script::CArray();
			p_stream_numbers->SetSizeAndType( vMAXCALLSKATERBYNAMESTREAMS, ESYMBOLTYPE_INTEGER );
			for ( int num = 0; num < vMAXCALLSKATERBYNAMESTREAMS; num++ )
			{
				p_stream_numbers->SetInteger( num, num + 1 );   // stream numbers start at 1, not 0
			}
			for ( int i = 0; i < vMAXCALLSKATERBYNAMESTREAMS; i++ )
			{
				// grab a random index and switch with the current index
				int random_index = Mth::Rnd( vMAXCALLSKATERBYNAMESTREAMS );
				int random_value = p_stream_numbers->GetInteger( random_index );
				p_stream_numbers->SetInteger( random_index, p_stream_numbers->GetInteger( i ) );
				p_stream_numbers->SetInteger( i, random_value );
			}

			//uint64 mid2_time = Tmr::GetTimeInUSeconds();	
			// find one!
			for ( int i = 0; i < vMAXCALLSKATERBYNAMESTREAMS; i++ )
			{
				sprintf( stream_name, "%s_Far%s%02i", p_speaker_name, p_player_name, i + 1 );
				
				if ( Pcm::StreamExists( Script::GenerateCRC( stream_name ) ) )
				{
					// printf("playing targetpro vo directly: %s\n", stream_name);
					found_player_name_vo = true;
					PlayGoalStream( Script::GenerateCRC( stream_name ) );
					break;
				}				
			}
			//uint64 end_time = Tmr::GetTimeInUSeconds();	
			//if ((end_time - start_time) > 250)
			//	Dbg_Message("******** PlayGoalStream String 2 Time %d Mid %d Mid2 %d", (end_time - start_time), (mid2_time - mid_time), (end_time - mid2_time));
			
			// clean up the array
			Script::CleanUpArray( p_stream_numbers );
			delete p_stream_numbers;
		}
		// else
		if ( !found_player_name_vo )
		{
			// go ahead and play a random one
			call_player_by_name = false;
			play_random = true;
		}
	}
	
	
	if ( ( call_player_by_name || play_random ) && !found_player_name_vo )
	{
		switch ( Script::GenerateCRC( type ) )
		{
		case ( CRCC(0x82117c1a,"wait") ):
			PlayGoalWaitStream();
			break;
		// case ( CRCC(0x90ff204d,"Success") ):
			// PlayGoalWinStream();
			// break;
		case ( CRCC(0x1623b689,"Midgoal") ):
			PlayGoalMidStream();
			break;
		// case ( CRCC(0x6657a075,"Talking") ):
			// PlayGoalStartStream();
			// break;
		default:
			//Pcm::StopStreams();
			PlayGoalProStream( type, vMAXWAITSTREAMS, false );
			break;
		}
	}

	//uint64 end_time = Tmr::GetTimeInUSeconds();	
	//if ((end_time - start_time) > 250)
	//	Dbg_Message("PlayGoalStream String 3 Time %d", (end_time - start_time));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoalPed::PlayGoalStartStream( Script::CStruct* pParams )
{
	if ( m_goalLogicSuspended )
		return;

	// get goal params
	Script::CStruct* pGoalParams = GetGoalParams();
	
	// K: Added this to prevent an assert when created-goal peds try to play a non-existent stream.
	if ( pGoalParams->ContainsFlag( CRCD( 0x102a8591, "no_stream" ) ) )
	{
		return;
	}	
	
	int cam_anim_index = -1;
	pParams->GetInteger( CRCD(0x64eee2e6,"cam_anim_index"), &cam_anim_index, Script::NO_ASSERT );

	const char* p_speaker_name = NULL;
	pParams->GetString( CRCD(0x82cbe8d1,"speaker_name"), &p_speaker_name, Script::NO_ASSERT );

	bool last_anim = pParams->ContainsFlag( CRCD(0xe7f5ff8,"last_anim") );
	bool should_load_fam = false;
	uint32 stream_checksum;
	if ( GetStreamChecksum( &stream_checksum, cam_anim_index, false, p_speaker_name, last_anim ) )
	{
		// load the fam as well.
		should_load_fam = true;
	}
	
	if ( stream_checksum )
	{		
		// stop any current vo's
		StopCurrentStream();

		uint32 speaker_obj_id = 0;
		pParams->GetChecksum( CRCD(0xf0c598fa,"speaker_obj_id"), &speaker_obj_id, Script::NO_ASSERT );
		PlayGoalStream( stream_checksum, false, speaker_obj_id, should_load_fam );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalPed::LoadFam( uint32 stream_checksum, uint32 reference_checksum )
{
	UnloadLastFam();
	char p_famName[32];
	sprintf( p_famName, "fam\\%08x.fam", stream_checksum );
	// printf("loading %s\n", p_famName);
	Ass::CAssMan* ass_man =  Ass::CAssMan::Instance();
	
	uint32 old_ref_checksum = ass_man->GetReferenceChecksum();
	if ( ass_man->LoadAnim( p_famName, stream_checksum, reference_checksum, false, false ) )
	{
		// printf("loaded\n");
		m_lastFam = Script::GenerateCRC( p_famName );
	}
	ass_man->SetReferenceChecksum( old_ref_checksum );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalPed::UnloadLastFam()
{
	if ( m_lastFam )
	{		
		if ( m_lastFamObj )
		{			
			Obj::CCompositeObject* p_pos_obj = NULL;
			if ( m_lastFamObj == CRCD(0x5b8ab877,"skater") )
			{		
				// printf("getting skater\n");
				Mdl::Skate* skate_mod = Mdl::Skate::Instance();
				p_pos_obj = skate_mod->GetLocalSkater();
			}
			else
			{
				//p_pos_obj = Obj::CMovingObject::m_hash_table.GetItem( trigger_obj_id );
				// printf("getting ped\n");
				p_pos_obj = (Obj::CCompositeObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID( m_lastFamObj );
			}

			Script::StopScriptsUsingThisObject_Proper( p_pos_obj, CRCD(0x5376a011,"goal_play_stream") );
			Script::CStruct* pTempStruct = new Script::CStruct;
			pTempStruct->AddChecksum( CRCD(0x7321a8d6,"type"), CRCD(0x659bf355,"partialAnim") );
			pTempStruct->AddChecksum( CRCD(0x40c698af,"id"), CRCD(0xc0ba0665,"jawRotation") );

			// first we want to kill off any old jaw rotation anims that might be playing...		
			if ( p_pos_obj )
			{
				// printf("removing animcontroller\n");
				Obj::CAnimationComponent* pAnimComponent = GetAnimationComponentFromObject( p_pos_obj );
				Dbg_Assert( pAnimComponent );
				pAnimComponent->CallMemberFunction( CRCD(0x986d274e,"RemoveAnimController"), pTempStruct, NULL );				
			}
			delete pTempStruct;
			m_lastFamObj = 0;
		}

		// get the assman
		Ass::CAssMan * ass_man = Ass::CAssMan::Instance();

		Ass::CAsset* pAsset = ass_man->GetAssetNode( m_lastFam, false );

		if ( pAsset )
		{
			// printf("unloading fam\n");
			ass_man->DestroyReferences( pAsset );
			ass_man->UnloadAsset( pAsset );
			Script::RunScript( CRCD( 0xb3dce47f, "invalidate_anim_cache" ) );
		}
		m_lastFam = 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalPed::StopLastStream()
{
	if ( m_lastStreamId )
	{
		if ( m_lastFamObj )
		{			
			Obj::CCompositeObject* p_pos_obj = NULL;
			if ( m_lastFamObj == CRCD(0x5b8ab877,"skater") )
			{		
				Mdl::Skate* skate_mod = Mdl::Skate::Instance();
				p_pos_obj = skate_mod->GetLocalSkater();
			}
			else
			{
				p_pos_obj = (Obj::CCompositeObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID( m_lastFamObj );
			}
			Script::StopScriptsUsingThisObject_Proper( p_pos_obj, CRCD(0x5376a011,"goal_play_stream") );
		}
		Pcm::StopStreamFromID( m_lastStreamId );
		m_lastStreamId = 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalPed::PlayGoalWaitStream()
{
	if ( m_goalLogicSuspended )
		return;

	PlayGoalProStream( "Far", vMAXWAITSTREAMS, true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalPed::PlayGoalWinStream( Script::CStruct* pParams )
{
	if ( m_goalLogicSuspended )
		return;

	// get params
	const char* p_speaker_name = NULL;
	pParams->GetString( CRCD(0x82cbe8d1,"speaker_name"), &p_speaker_name, Script::NO_ASSERT );
	// int cam_anim_index = -1;
	// pParams->GetInteger( CRCD(0x64eee2e6,"cam_anim_index"), &cam_anim_index, Script:::NO_ASSERT );
	// bool last_anim = pParams->ContainsFlag( CRCD(0xe7f5ff8,"last_anim") );

	uint32 stream_checksum;
	bool should_load_fam = false;

	if ( GetStreamChecksum( &stream_checksum, -1, true, p_speaker_name, true ) )
	{
		should_load_fam = true;
	}

	uint32 speaker_obj_id = 0;
	pParams->GetChecksum( CRCD(0xf0c598fa,"speaker_obj_id"), &speaker_obj_id, Script::NO_ASSERT );
	
	if ( stream_checksum )
		PlayGoalStream( stream_checksum, false, speaker_obj_id, should_load_fam );
	// else
		// PlayGoalProStream( "Success", vMAXWINSTREAMS, false, false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalPed::PlayGoalMidStream()
{
	if ( m_goalLogicSuspended )
		return;
	PlayGoalProStream( "Midgoal", vMAXMIDGOALSTREAMS, false );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalPed::PlayGoalProStream( const char* p_stream_type, int max_number_streams, bool use_player_name, bool use_pos_info )
{
	Dbg_MsgAssert( p_stream_type, ("PlayGoalProStream called without stream type") );
	Dbg_MsgAssert( max_number_streams, ("PlayGoalProStream called without stream type") );
	
	if ( m_goalLogicSuspended )
		return;
	
	// stop any current vo's
	StopCurrentStream();
	
	// get the first name of the speaker and the player
	const char* p_speaker_name = GetProFirstName();
	if ( !p_speaker_name )
		return;
	
	// check what pro the player is using
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();	
	
	const char* p_current_pro_name;
	p_current_pro_name = pSkaterProfile->GetDisplayName();
	
	char p_player_name[128];
	parse_first_name( p_player_name, &p_current_pro_name, 128 );
	Dbg_MsgAssert( strlen( p_player_name ) > 0, ( "No player name found" ) );
	// printf("got a first name of %s\n", p_player_name);
	
	// create an array of all possible stream numbers
	int num_possible_streams = max_number_streams;

	if ( use_player_name )
		// there are x wait streams plus 20 possible streams directed specifically at the player ("Hey, tony!")
		num_possible_streams += 20;

	Script::CArray* p_stream_numbers = new Script::CArray();
	p_stream_numbers->SetSizeAndType( num_possible_streams, ESYMBOLTYPE_INTEGER );
	for ( int num = 0; num < num_possible_streams; num++ )
	{
		p_stream_numbers->SetInteger( num, num + 1 );   // stream numbers start at 1, not 0
	}
	for ( int i = 0; i < num_possible_streams; i++ )
	{
		// grab a random index and switch with the current index
		int random_index = Mth::Rnd( num_possible_streams );
		int random_value = p_stream_numbers->GetInteger( random_index );
		p_stream_numbers->SetInteger( random_index, p_stream_numbers->GetInteger( i ) );
		p_stream_numbers->SetInteger( i, random_value );
	}

	// go through until we find the first valid stream
	// uint32 current_vo_stream = 0;
	for ( int i = 0; i < num_possible_streams; i++ )
	{
		int stream_number = p_stream_numbers->GetInteger( i );
		// printf( "got a stream number of %i\n", stream_number );
		char stream_name[128];

		// check if this is the special "hey tony" type stream number
		if ( ( stream_number > max_number_streams ) )
		{
			// don't play the special vo unless they're using a pro.
			if ( pSkaterProfile->IsPro() )
			{
				// offset the stream number.
				stream_number -= max_number_streams;
				sprintf( stream_name, "%s_%s%s%02i", p_speaker_name, p_stream_type, p_player_name, stream_number );
				// printf("figured a special vo of %s\n", stream_name);
				if ( Pcm::StreamExists( Script::GenerateCRC( stream_name ) ) )
				{
					PlayGoalStream( Script::GenerateCRC( stream_name ), use_pos_info );
					// current_vo_stream = Pcm::PlayStream( Script::GenerateCRC( stream_name ), 100, 100 );
					break;
				}
			}
		}
		else
		{
			sprintf( stream_name, "%s_%s%02i", p_speaker_name, p_stream_type, stream_number );
			// printf("figured vo: %s\n", stream_name);
			if ( Pcm::StreamExists( Script::GenerateCRC( stream_name ) ) )
			{
				PlayGoalStream( Script::GenerateCRC( stream_name ), use_pos_info );
				// current_vo_stream = Pcm::PlayStream( Script::GenerateCRC( stream_name ), 100, 100 );
				break;
			}
		}
	}
	
	Script::CleanUpArray( p_stream_numbers );
	delete p_stream_numbers;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CGoalPed::StopCurrentStream()
{
	// get params
	Script::CStruct* pParams = GetGoalParams();

	uint32 current_vo_stream = 0;
	if ( pParams->GetChecksum( CRCD(0xdc14d454,"current_vo_stream"), ¤t_vo_stream, Script::NO_ASSERT ) )
	{
		pParams->RemoveComponent( CRCD(0xdc14d454,"current_vo_stream") );
		if ( current_vo_stream )
			Pcm::StopStreamFromID( current_vo_stream );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CGoalPed::ProIsCurrentSkater()
{
	// get params
	Script::CStruct* pParams = GetGoalParams();
	
	// are they using the right skater?
	Script::CStruct* p_last_names = Script::GetStructure( CRCD(0x621d1828,"goal_pro_last_name_checksums"), Script::ASSERT );
	const char* first_name_string;
	pParams->GetString( CRCD(0x4a91eceb,"pro_challenge_pro_name"), &first_name_string, Script::ASSERT );
	uint32 last_name;
	p_last_names->GetChecksum( Script::GenerateCRC( first_name_string ), &last_name, Script::ASSERT );
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CPlayerProfileManager* pProfileManager = skate_mod->GetPlayerProfileManager();
	Obj::CSkaterProfile* pSkaterProfile = pProfileManager->GetCurrentProfile();
	uint32 current_skater = pSkaterProfile->GetSkaterNameChecksum();

	return ( current_skater == last_name );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CGoalPed::GetGoalAnimations( const char* type )
{
	// get params
	Script::CStruct* pParams = GetGoalParams();
	
	uint32 array_name = 0;
	
	// check for specific goal animation
	char goal_specific_array_name[128];
	const char* p_goal_name;
	pParams->GetString( CRCD(0xbfecc45b,"goal_name"), &p_goal_name, Script::ASSERT );
	sprintf( goal_specific_array_name, "%s_%s", p_goal_name, type );
	if ( ( Script::GetArray( goal_specific_array_name, Script::NO_ASSERT ) ) )
	{
		// printf("got a specific array name of %s\n", p_goal_specific_array_name );
		array_name = Script::GenerateCRC( goal_specific_array_name );
	}
	else if ( type )
	{
		// build the name of the pro's generic anims array
		const char* p_pro_name = GetProFirstName();
		char pro_array_name[128];
		sprintf( pro_array_name, "%s_goal_%s", p_pro_name, type );

		if ( ( Script::GetArray( pro_array_name, Script::NO_ASSERT ) ) )
		{
			// pro's generic array
			// printf("got generic pro array: %s\n", pro_array_name );
			array_name = Script::GenerateCRC( pro_array_name );
		}
		else
		{
			// get the generic anim array
			char generic_array_name[128];
			sprintf( generic_array_name, "generic_pro_anims_%s", type );
			// printf("looking for array: %s\n", generic_array_name);
			if ( !Script::GetArray( generic_array_name, Script::NO_ASSERT ) )
			{
				 Dbg_MsgAssert( 0, ("Unable to find generic pro array '%s'",generic_array_name) );
			}
			array_name = Script::GenerateCRC( generic_array_name );
		}
	}
	return array_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalPed::DestroyGoalPed()
{
	// get params
	Script::CStruct* pParams = GetGoalParams();
	
	uint32 trigger_obj_id = 0;

	pParams->GetChecksum( CRCD(0x02d7e03d,"trigger_obj_id"), &trigger_obj_id );
	if( trigger_obj_id )
	{
		Obj::CCompositeObject* p_pos_obj = NULL;
		//p_pos_obj = Obj::CMovingObject::m_hash_table.GetItem( trigger_obj_id );
		p_pos_obj = (Obj::CCompositeObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID( trigger_obj_id);
		
		if( p_pos_obj )
		{
			delete p_pos_obj;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalPed::SuspendGoalLogic( bool suspend )
{
	m_goalLogicSuspended = suspend;

	if ( suspend )
	{
		Script::RunScript( CRCD(0xbf67a3cf,"goal_pro_stop_anim_scripts"), GetGoalParams() );
		StopCurrentStream();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CGoalPed::Hide( bool hide )
{
	// get params
	Script::CStruct* pParams = GetGoalParams();
	
	if ( pParams->ContainsFlag( CRCD( 0x3d1cab0b, "null_goal" ) ) )
	{
		return;
	}
	
	uint32 trigger_obj_id = 0;

	pParams->GetChecksum( CRCD(0x02d7e03d,"trigger_obj_id"), &trigger_obj_id );
	if ( trigger_obj_id )
	{
		Obj::CCompositeObject* p_pos_obj = NULL;
		//p_pos_obj = Obj::CMovingObject::m_hash_table.GetItem( trigger_obj_id );
		p_pos_obj = (Obj::CCompositeObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID( trigger_obj_id );
		
		if ( p_pos_obj )
		{
			p_pos_obj->Hide( hide );
		}

		// kill/create arrow
		Script::CStruct* pTempParams = new Script::CStruct();
		pTempParams->AddChecksum( CRCD(0x9982e501,"goal_id"), m_goalId );
		if ( hide )
			Script::RunScript( CRCD(0x6d39f2f3,"goal_ped_kill_arrow"), pTempParams );
		else
		{
			// check if this goal has been beaten...hack for mike v special goal
			CGoalManager* pGoalManager = GetGoalManager();
			Dbg_Assert( pGoalManager );
			CGoal* pGoal = pGoalManager->GetGoal( m_goalId, false );
			bool should_add_arrow = true;
			if ( pGoal && pGoal->HasWonGoal() )
				should_add_arrow = false;
			
			if ( should_add_arrow )
				Script::RunScript( CRCD(0x6ea4d309,"goal_add_ped_arrow"), pTempParams );
		}
		delete pTempParams;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CGoalPed::GetGoalParams()
{
	CGoalManager* pGoalManager = Game::GetGoalManager();
	Dbg_Assert( pGoalManager );

	return pGoalManager->GetGoalParams( m_goalId );
}

}	// namespace Game


================================================
FILE: Code/Sk/Modules/Skate/GoalPed.h
================================================
// goal ped!

#ifndef __MODULES_SKATE_GOALPED_H
#define __MODULES_SKATE_GOALPED_H

#include 
#include 

#include 
#include 
#include 

namespace Game
{

const uint32	MAX_STREAM_NAME_LENGTH = 256;
	
class CGoalPed
{
public:
	CGoalPed( Script::CStruct* pParams );
	~CGoalPed();
	
	void				DestroyGoalPed();
	
	void		 		GetPlayerFirstName( char* p_first_name, int buffer_size );
	const char*			GetProFirstName();
	bool				GetStreamChecksum( uint32* p_streamChecksum, int cam_anim_index = -1, bool success = false, const char* p_speaker_name = NULL, bool last_anim = false);
	void				LoadFam( uint32 stream_checksum, uint32 reference_checksum );
	void				UnloadLastFam();
	void				StopLastStream();
	void				PlayGoalStream( uint32 stream_checksum, bool use_pos_info = true, uint32 speaker_obj_id = 0, bool should_load_fam = false );
	void				PlayGoalStream( const char* type, Script::CStruct* pParams );
	void				PlayGoalStartStream( Script::CStruct* pParams );
	void				StopCurrentStream();
	void				PlayGoalProStream( const char* p_stream_type, int max_number_streams, bool use_player_name, bool use_pos_info = true );
	void				PlayGoalWinStream( Script::CStruct* pParams );
	void				PlayGoalWaitStream();
	void				PlayGoalMidStream();

	uint32				GetGoalAnimations( const char* type );

	bool				ProIsCurrentSkater();

	void				SuspendGoalLogic( bool suspend = true );

	Script::CStruct*	GetGoalParams();

	void				Hide( bool hide );
protected:
	uint32				m_goalId;
	bool				m_goalLogicSuspended;
	uint32				m_lastFam;
	uint32				m_lastFamObj;
	uint32				m_lastStreamId;
};


} // namespace Game

#endif // __MODULES_SKATE_GOALPED_H


================================================
FILE: Code/Sk/Modules/Skate/HorseGoal.cpp
================================================
// horse goal subclass

#include 
#include 

#include 
#include 

#include 
#include 

#include 

namespace Game
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CHorseGoal::CHorseGoal( Script::CStruct* pParams ) : CGoal( pParams )
{
	// nothing special
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CHorseGoal::~CHorseGoal()
{
	// nothing yet
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CHorseGoal::CheckScore()
{
	// get the right flag
	Script::CArray* pHorseSpots;
	mp_params->GetArray( "horse_spots", &pHorseSpots, Script::ASSERT );

	int size = pHorseSpots->GetSize();
	int i;
	int score;
	uint32 flag;
	for ( i = 0; i < size; i++ )
	{
		Script::CStruct* pSpot = pHorseSpots->GetStructure( i );
		pSpot->GetChecksum( "flag", &flag, Script::ASSERT );
		int flag_value;
		mp_goalFlags->GetInteger( flag, &flag_value, Script::ASSERT );
		
		// find the first unset flag
		if ( flag_value == 0 )
		{
			if ( !pSpot->GetInteger( "score", &score, Script::NO_ASSERT ) )
				mp_params->GetInteger( "score", &score, Script::ASSERT );
			break;
		}
	}

	// make sure they're not all set...
	if ( i == size )
		return;

	// check the score
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = skate_mod->GetLocalSkater();
	Mdl::Score* pScore = pSkater->GetScoreObject();

	int last_score_landed = pScore->GetLastScoreLanded();
	if ( last_score_landed > 0 )
	{
		uint32 goalId = GetGoalId();
		CGoalManager* pGoalManager = GetGoalManager();
		if ( last_score_landed > score )
		{			
			pGoalManager->SetGoalFlag( goalId, flag, 1 );
			pGoalManager->NextTourSpot( goalId );
		}
		else
			pGoalManager->LoseGoal( goalId );
	}
	mp_params->AddInteger( "should_check_trick", 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CHorseGoal::ShouldExpire()
{
	int should_check_trick = 0;
	mp_params->GetInteger( "should_check_trick", &should_check_trick, Script::ASSERT );

	if ( should_check_trick == 1 )
		return false;
	
	return CGoal::ShouldExpire();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CHorseGoal::IsExpired()
{
	int should_check_trick = 0;
	mp_params->GetInteger( "should_check_trick", &should_check_trick, Script::ASSERT );

	if ( should_check_trick == 1 )
		return false;
	
	return CGoal::IsExpired();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void CHorseGoal::Expire()
{
	CGoal::Expire();
}

}	// namespace Game


================================================
FILE: Code/Sk/Modules/Skate/HorseGoal.h
================================================
// horse goal subclass
#ifndef __SK_MODULES_SKATE_HORSEGOAL_H__
#define __SK_MODULES_SKATE_HORSEGOAL_H__

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

namespace Game
{


class CHorseGoal : public CGoal
{

public:
					CHorseGoal( Script::CStruct* pParams );
	virtual			~CHorseGoal();
	
	void			CheckScore();
	bool			ShouldExpire();
	bool			IsExpired();
	void			Expire();
};

}

#endif


================================================
FILE: Code/Sk/Modules/Skate/Minigame.cpp
================================================
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include        // for minigame timer
#include 
#include 

namespace Game
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CMinigame::CMinigame( Script::CStruct* pParams ) : CGoal( pParams )
{
	Script::CArray *pGeomArray;

	// Create the associated pieces of geometry
    if ( pParams->GetArray( "create_geometry", &pGeomArray ) )
    {
		uint32 i, geometry;

		for( i = 0; i < pGeomArray->GetSize(); i++ )
        {
            geometry = pGeomArray->GetChecksum( i );
			CFuncs::ScriptCreateFromNodeIndex( SkateScript::FindNamedNode( geometry ));
        }
	}
	
	m_record = 0;
	m_cashLimit = 0;
	pParams->GetInteger( "cash_limit", &m_cashLimit, Script::NO_ASSERT );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
CMinigame::~CMinigame()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CMinigame::Activate()
{
	if ( !IsActive() && ShouldUseTimer() )
	{
		CGoalManager* pGoalManager = Game::GetGoalManager();
		Dbg_Assert( pGoalManager );
		pGoalManager->DeactivateMinigamesWithTimer();
	}
	return CGoal::Activate();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CMinigame::Update()
{
	if ( IsActive() && ShouldUseTimer() )
	{
		UpdateMinigameTimer();
	}
	return CGoal::Update();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CMinigame::ShouldUseTimer()
{
	return ( mp_params->ContainsComponentNamed( "time" ) );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
// this is used for minigames that have their own timer.
void CMinigame::UpdateMinigameTimer()
{
	return;
/*	
	Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();

	Tmr::Time time_left = m_timeLeft / 1000;		
	if( time_left < 1 )
		time_left = 0;
	
	int seconds = time_left % 60;
	int minutes = time_left / 60;
		
	Front::CScreenElementPtr p_element = p_screen_elem_man->GetElement( CRCD( 0xcd9671b4, "minigame_timer") );
	if (p_element)
	{
		Dbg_MsgAssert(p_element->GetType() == CRCD(0x5200dfb6, "TextElement"), ("type is 0x%x", p_element->GetType()));
		Front::CTextElement *p_time_element = (Front::CTextElement *) p_element.Convert();
	
		char time_text[128];
		sprintf(time_text, "%2d:%.2d", minutes, seconds);
		
		// get the description if there is one
		const char* timer_description;
		if ( mp_params->GetString( CRCD( 0xe1fc4f74, "timer_description" ), &timer_description, Script::NO_ASSERT ) )
		{
			strcat( time_text, " " );
			strcat( time_text, timer_description );
		}
		p_time_element->SetText(time_text);
	}
*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CMinigame::IsExpired()
{
	return ( ShouldUseTimer() && ( (int)m_timeLeft <= 0 ) );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CMinigame::CheckRecord( int value )
{
	if ( value > m_record )
	{
		m_record = value;
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CMinigame::AddTime( int amount )
{
	if ( IsActive() )
	{
		m_timeLeft += (Tmr::Time)( amount * 1000 );
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
int CMinigame::GetRecord()
{
	mp_params->AddInteger( "minigame_record", m_record );
	return m_record;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CMinigame::CanRetry()
{
	// you cannot retry minigames - ie, they don't show up 
	// on the pause menu as "Retry last goal"
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMinigame::LoadSaveData( Script::CStruct*  pFlags )
{
	// minigame record
	int minigame_record;
	pFlags->GetInteger( "minigame_record", &minigame_record, Script::NO_ASSERT );
	if ( minigame_record != -1 )
	{
		// this will check and set the record
		CheckMinigameRecord( minigame_record );
	}

	int cash_limit;
	if ( pFlags->GetInteger( "cash_limit", &cash_limit, Script::NO_ASSERT ) )
		m_cashLimit = cash_limit;
	CGoal::LoadSaveData( pFlags );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMinigame::GetSaveData( Script::CStruct* pFlags )
{
	int minigame_record = GetMinigameRecord();
	pFlags->AddInteger( "minigame_record", minigame_record );
	pFlags->AddInteger( "cash_limit", m_cashLimit );
	CGoal::GetSaveData( pFlags );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMinigame::AwardCash( int amount )
{
	if ( m_cashLimit <= 0 )
	{
		Script::RunScript( "minigame_cash_depleted" );
		return false;
	}

	// clamp it
	if ( amount > m_cashLimit )
		amount = m_cashLimit;

	CGoalManager* pGoalManager = GetGoalManager();
	Dbg_Assert( pGoalManager );
	if ( pGoalManager )
	{
		pGoalManager->AddCash( amount );
		m_cashLimit -= amount;
		Script::CStruct* pScriptParams = new Script::CStruct();
		pScriptParams->AddInteger( "amount", amount );
		Script::RunScript( "minigame_got_cash", pScriptParams );
		delete pScriptParams;
		return true;
	}
	return false;
}

}


================================================
FILE: Code/Sk/Modules/Skate/Minigame.h
================================================
#ifndef __SK_MODULES_SKATE_MINIGAME_H__
#define __SK_MODULES_SKATE_MINIGAME_H__

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

namespace Game
{


class CMinigame : public CGoal
{

public:						
					CMinigame( Script::CStruct* pParams );
	virtual			~CMinigame();

	bool			Activate();

	void			LoadSaveData( Script::CStruct* pFlags );
	void			GetSaveData( Script::CStruct* pFlags );
	
	bool			ShouldUseTimer();
	bool			AddTime( int amount );
	bool			CountAsActive() { return false; }
	bool			IsExpired();
	bool			CheckRecord( int value );
	int				GetRecord();
	bool			CanRetry();
	void			SetStartTime();
	void			UpdateTimer();

	bool			Update();
	void			UpdateMinigameTimer();

	bool			AwardCash( int amount );

protected:
	int				m_record;
	int				m_cashLimit;
};

}

#endif


================================================
FILE: Code/Sk/Modules/Skate/NetGoal.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

namespace Game
{

CNetGoal::CNetGoal( Script::CStruct* pParams ) : CGoal( pParams )
{
	m_endRunType = vENDOFRUN;
	m_initialized = false;
}

CNetGoal::~CNetGoal()
{
	// nothing yet
}

bool CNetGoal::Update()
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if ( !m_initialized )
	{
		Init();
		m_initialized = true;
	}

	if( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netgoalattack" ))
	{
		if( GoalAttackComplete())
		{
			if( IsActive())
			{
				Expire();

				return true;
			}
		}
	}

	return CGoal::Update();
}

bool CNetGoal::Activate( void )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	gamenet_man->ClearGameOver();

	return CGoal::Activate();
}

bool CNetGoal::Deactivate( bool force, bool affect_tree )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	if( force == false )
	{
		if( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netgoalattack" ))
		{
			return false;
		}
	}

	return CGoal::Deactivate( force, affect_tree );
}

bool CNetGoal::IsExpired()
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	if( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netgoalattack" ))
	{
		return GoalAttackComplete() && AllSkatersAtEndOfRun();
	}
	else
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		GameNet::PlayerInfo* player;
		Lst::Search< GameNet::PlayerInfo > sh;
	
		// If no players are left, the goal should expire
		if( gamenet_man->GetNumPlayers() == 0 )
		{
			//Dbg_Printf( "IsExpired: 1\n" );
			return true;
		}
						
		// If it's an unlimited game, don't expire the goal
		if ( m_unlimitedTime )
		{
			//Dbg_Printf( "IsExpired: 2\n" );
			return false;
		}
		
		if( ( gamenet_man->HaveReceivedFinalScores() == false ) && 
			( gamenet_man->InNetGame()))
		{
			//Dbg_Printf( "IsExpired: 3\n" );
			return false;
		}

		// if we're not supposed to use end of run, we need only check
		// the time
		if ( m_endRunType == vNONE )
		{
			//Dbg_Printf( "Timeleft: %d\n", (int) m_timeLeft );
			return ( (int)m_timeLeft <= 0 );
		}
	
		// check if the skaters have reached end of run BEFORE
		// setting the exception, or you'll never catch it!
		if ( AllSkatersAtEndOfRun() && (int)m_timeLeft <= 0 )
		{
			//Dbg_Printf( "IsExpired: 4\n" );
			return true;
		}
	
		// reset all players' scores
		for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
		{
			Obj::CSkater *p_Skater = player->m_Skater;
			Dbg_Assert( p_Skater );

			Obj::CSkaterEndRunComponent* p_skater_endrun_component = GetSkaterEndRunComponentFromObject(p_Skater);
			Dbg_Assert( p_skater_endrun_component );

			if( p_skater_endrun_component->RunHasEnded() || p_Skater->IsPaused())
			{
				continue;
			}
			// if time is up, set the end of run exception
			// if ( m_shouldEndRun && !m_endRunCalled && (int)m_timeLeft <= 0 )
			if ( m_endRunType != vNONE && (int)m_timeLeft <= 0 )
			{
				m_endRunCalled = true;
				//Dbg_Printf( "EXPIRING GOAL: %d\n", GetGoalId());
				// printf("calling EndRun\n");
				p_skater_endrun_component->EndRun();
			}
		}

		//Dbg_Printf( "IsExpired: 6 : %d\n", gamenet_man->GameIsOver());
		return gamenet_man->GameIsOver();

	}
}

void	CNetGoal::Expire( void )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	gamenet_man->MarkGameOver();
	return CGoal::Expire();
}

}	// namespace Game


================================================
FILE: Code/Sk/Modules/Skate/NetGoal.h
================================================
#ifndef __SK_MODULES_SKATE_NETGOAL_H__
#define __SK_MODULES_SKATE_NETGOAL_H__

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

namespace Game
{


class CNetGoal : public CGoal
{

public:
					CNetGoal( Script::CStruct* pParams );
	virtual			~CNetGoal();
	
	bool			Update();
	bool			Deactivate( bool force = false, bool affect_tree = true );
	bool			Activate();
	bool			IsExpired();
	void			Expire();

protected:
	bool			m_initialized;
};

}

#endif


================================================
FILE: Code/Sk/Modules/Skate/RaceGoal.cpp
================================================
#include 
#include 

namespace Game
{


CRaceGoal::CRaceGoal( Script::CStruct* pParams ) : CGoal( pParams )
{
	// nothing yet
}

CRaceGoal::~CRaceGoal()
{
	// nope
}

bool CRaceGoal::ShouldExpire()
{
	if ( !m_unlimitedTime && (int)m_timeLeft <= 0)
	{
		return true;
	}
	return false;
}

bool CRaceGoal::IsExpired()
{
	if ( !m_unlimitedTime && (int)m_timeLeft <= 0)
	{
		CGoal::IsExpired();
		return true;
	}
	return false;
}

void CRaceGoal::Expire()
{
	RunCallbackScript( vEXPIRED );
	Deactivate();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
bool CRaceGoal::NextRaceWaypoint( uint32 goalId )
{
    if ( IsExpired() )
	{
		printf("expired!\n");
		return false;
	}
	Script::CArray* p_waypoints;
    mp_params->GetArray( "race_waypoints", &p_waypoints, Script::ASSERT );

    Script::CStruct* p_waypoint;
    for ( uint32 i = 0; i < p_waypoints->GetSize(); i++ )
    {
        p_waypoint = p_waypoints->GetStructure( i );

        // is this waypoint done?
        uint32 flagId;
        p_waypoint->GetChecksum( "flag", &flagId, Script::ASSERT );
        if ( GoalFlagEquals( flagId, 1 ) )
        {
            continue;
        }
        
		if ( m_flagsSet == 0)
		{
			m_timeLeft = 0;
			p_waypoint->AddInteger( "first_waypoint", 1 );
		}
        
		// add the goalId to the params
        p_waypoint->AddChecksum( "goal_id", goalId );

		// add quick start flag
		if ( mp_params->ContainsFlag( CRCD(0x38221df4,"quick_start") ) )
			p_waypoint->AddChecksum( NONAME, CRCD(0x38221df4,"quick_start") );
		else
			p_waypoint->RemoveFlag( CRCD(0x38221df4,"quick_start") );

		#ifdef __NOPT_ASSERT__
        Script::CScript *p_script=Script::SpawnScript( "goal_race_next_waypoint", p_waypoint);
		p_script->SetCommentString("Spawned from CRaceGoal::NextRaceWaypoint");
		#else
        Script::SpawnScript( "goal_race_next_waypoint", p_waypoint);
		#endif

		// set the timer
		if ( !m_unlimitedTime )
		{
			int time;
			if ( !p_waypoint->GetInteger( "time", &time, Script::NO_ASSERT ) )
			{
				Dbg_MsgAssert( 0, ("Each race waypoint must have a 'time' param") );
			}
			CGoal::AddTime( time );
		}

/*        uint32 waypoint_script;
        if ( p_waypoint->GetChecksum( "scr", &waypoint_script, Script::NO_ASSERT ) )
            Script::SpawnScript( waypoint_script );
*/
        return true;
    }
    return true;
}

}


================================================
FILE: Code/Sk/Modules/Skate/RaceGoal.h
================================================
#ifndef __SK_MODULES_SKATE_RACEGOAL_H__
#define __SK_MODULES_SKATE_RACEGOAL_H__

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

namespace Game
{

class CRaceGoal : public CGoal
{
	friend class CGoalManager;

public:
					CRaceGoal( Script::CStruct* pParams );
	virtual			~CRaceGoal();

	bool			ShouldExpire();
	bool			IsExpired();
	void			Expire();
	bool			NextRaceWaypoint( uint32 goalId );
};

}

#endif


================================================
FILE: Code/Sk/Modules/Skate/SkatetrisGoal.cpp
================================================
// skatetris goal
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include        // for tetris tricks
#include 
#include 
#include 

#include 
#include 

namespace Game
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkatetrisGoal::CSkatetrisGoal( Script::CStruct* pParams ) : CGoal( pParams )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkatetrisGoal::~CSkatetrisGoal()
{
//	delete[] m_tetrisTricks;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkatetrisGoal::Activate()
{
	if ( !IsActive() )
	{
		StartTetrisGoal();
		return CGoal::Activate();
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkatetrisGoal::Deactivate( bool force, bool affect_tree )
{
	// printf("CSkatetrisGoal::Deactivate\n");
	EndTetrisGoal();
	return CGoal::Deactivate( force, affect_tree );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkatetrisGoal::Win()
{
	// the win function is called after time runs out, 
	// so deactivate must always be called

	// K: TT12058, Deactivate may remove the testing_goal flag, so store it and put it back in
	// afterwards if necessary. (The flag is required by the call to CGoal::Win below)
	bool testing_goal=mp_params->ContainsFlag(CRCD(0x54d3cac1,"testing_goal"));
	
	mp_params->AddChecksum( NONAME, CRCD( 0xc309cad1, "just_won_goal" ) );
	Deactivate();
	mp_params->RemoveFlag( CRCD( 0xc309cad1, "just_won_goal" ) );
	
	if (testing_goal)
	{
		mp_params->AddChecksum(NONAME,CRCD(0x54d3cac1,"testing_goal"));
	}
		
	/*if( gamenet_man->InNetGame())
	{
		CGoalManager* pGoalManager = GetGoalManager();
        Dbg_Assert( pGoalManager );
        return pGoalManager->WinGoal( GetGoalId());
	}
	else*/
	{
		bool ret_val=CGoal::Win();
		// Make sure the testing_goal flag is definitely gone.
		mp_params->RemoveFlag(CRCD(0x54d3cac1,"testing_goal"));
		return ret_val;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkatetrisGoal::Update()
{
	int wait_to_add_tricks = 0;
	mp_params->GetInteger( CRCD(0x7824963c,"wait_to_add_tricks"), &wait_to_add_tricks, Script::NO_ASSERT );
	if ( wait_to_add_tricks )
	{
		// bail out...
		return CGoal::Update();
	}

	int minTimeLeft = 1;
	if ( !IsSingleComboSkatetris() )
	{
		if ( mp_params->GetInteger( CRCD(0xa6d96603,"time_to_stop_adding_tricks"), &minTimeLeft, Script::NO_ASSERT ) )
			minTimeLeft *= 1000;
	}
	
	// check for win conditions
	if ( IsTricktris() )
	{
		if ( IsActive() && !IsPaused() )
		{
			// tricktris is won when you've completed the pre-determined number
			// of tricks
			int tricks_completed = 0;
			mp_params->GetInteger( CRCD(0x8d958f01,"tricks_completed"), &tricks_completed, Script::NO_ASSERT );
			int total_to_win;
			mp_params->GetInteger( CRCD(0xbfd3a831,"tricktris_total_to_win"), &total_to_win, Script::ASSERT );
			// printf("tricks_completed = %i, total_to_win = %i\n", tricks_completed, total_to_win);
			if ( tricks_completed >= total_to_win )
			{
				CGoalManager* pGoalManager = GetGoalManager();
				pGoalManager->WinGoal( GetGoalId() );
				return true;			
			}
		}
	}
	else if ( IsActive() && (int)m_timeLeft < minTimeLeft && !IsPaused() && AllTricksCleared() )
	{
		// win the goal if they've cleared all tricks and there's no time to add more
		CGoalManager* pGoalManager = GetGoalManager();
		pGoalManager->WinGoal( GetGoalId() );
		return true;
	}
	
	// update faded tricks if we're active - even if the time has run out
	if ( IsActive() && !IsPaused() )
	{
		UpdateFadedTricks();
	}
	
	if ( IsTricktris() )
	{
		if ( ( m_unlimitedTime || (int)m_timeLeft > 0.0f ) && !IsPaused() )
		{
			int tricks_completed = 0;
			mp_params->GetInteger( CRCD(0x8d958f01,"tricks_completed"), &tricks_completed, Script::NO_ASSERT );
			int total_to_win;
			mp_params->GetInteger( CRCD(0xbfd3a831,"tricktris_total_to_win"), &total_to_win, Script::ASSERT );
	
			// count number of valid entries and add if necessary
			int total_in_stack = 0;
			for ( int i = 0; i < Game::vMAXTETRISTRICKS; i++ )
			{
				if ( m_tetrisTricks[i].invalid == false )
					total_in_stack++;
			}
			int desired_stack_size;
			mp_params->GetInteger( CRCD(0x45a375c9,"tricktris_block_size"), &desired_stack_size, Script::ASSERT );
			if ( total_in_stack < desired_stack_size && total_in_stack + tricks_completed < total_to_win )
			{
				int num_to_add = desired_stack_size - total_in_stack;
				if ( total_to_win - tricks_completed - total_in_stack < num_to_add )
					num_to_add = total_to_win - tricks_completed - total_in_stack;
				AddTetrisTrick( num_to_add );
			}
		}
	}
	else if ( IsActive() && ( m_unlimitedTime || (int)m_timeLeft >= minTimeLeft ) && !IsPaused() )
	{
		m_tetrisTime += (int)( Tmr::FrameLength() * 1000 );

		if ( IsSingleComboSkatetris() )
		{
			// throw in a delay so the single combo doesn't appear right away
			// during the goal intro movie
			if ( !mp_params->ContainsFlag( CRCD(0xc93da683,"single_combo_added") ) && m_tetrisTime > 50 )
			{
				int combo_size;
				mp_params->GetInteger( CRCD(0x67dd90ca,"combo_size"), &combo_size, Script::ASSERT );
				AddTetrisTrick( combo_size );
				mp_params->AddChecksum( NONAME, CRCD(0xc93da683,"single_combo_added") );
			}
		}
		else
		{
			int trickTime;
			mp_params->GetInteger( CRCD(0x648d8d2a,"adjusted_trick_time"), &trickTime, Script::ASSERT );
			if ( m_tetrisTime > trickTime )
			{
				m_tetrisTime = 0;
				if ( IsComboSkatetris() )
				{
					int combo_size;
					mp_params->GetInteger( CRCD(0x67dd90ca,"combo_size"), &combo_size, Script::ASSERT );
					AddTetrisTrick( combo_size );
				}
				else
					AddTetrisTrick();
			}
		}
	}
	return CGoal::Update();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkatetrisGoal::StartTetrisGoal()
{
    m_numTetrisTricks = 0;
    m_tetrisTime = 0;
    int original_time;
    mp_params->GetInteger( CRCD( 0xded8540a, "trick_time" ), &original_time, Script::ASSERT );
    mp_params->AddInteger( CRCD( 0x648d8d2a, "adjusted_trick_time" ), original_time );
	mp_params->AddInteger( CRCD( 0x8d958f01, "tricks_completed" ), 0 );

	// start almost out of time so a trick gets added right away
	if ( IsSingleComboSkatetris() )
		m_tetrisTime = 0;
	else
		m_tetrisTime = original_time - 200;
    
    for ( int i = 0; i < Game::vMAXTETRISTRICKS; i++ )
    {
        m_tetrisTricks[i].invalid = true;
        m_tetrisTricks[i].trickNameChecksum = 0;
		m_tetrisTricks[i].altTrickNameChecksum = 0;
		m_tetrisTricks[i].keyCombo = 0;
		m_tetrisTricks[i].spin_mult = 0;
		m_tetrisTricks[i].num_taps = 1;
		m_tetrisTricks[i].require_perfect = false;

		m_validGroups[i] = false;
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkatetrisGoal::EndTetrisGoal()
{
    m_numTetrisTricks = 0;
    m_tetrisTime = 0;
    
    for ( int i = 0; i < Game::vMAXTETRISTRICKS; i++ )
    {
        m_tetrisTricks[i].invalid = false;
        m_tetrisTricks[i].trickNameChecksum = 0;
		m_tetrisTricks[i].altTrickNameChecksum = 0;
		m_tetrisTricks[i].keyCombo = 0;
		m_tetrisTricks[i].spin_mult = 0;
		m_tetrisTricks[i].num_taps = 1;
		m_tetrisTricks[i].require_perfect = false;

		m_validGroups[i] = false;
    }

	mp_params->RemoveFlag( CRCD(0xc93da683,"single_combo_added") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkatetrisGoal::AddTetrisTrick( int num_to_add )
{
    int max_tricks;
	mp_params->GetInteger( CRCD(0x89473db7,"max_tricks"), &max_tricks, Script::ASSERT );
	
	int tricks_in_group = num_to_add;
	int possible_num_tricks = m_numTetrisTricks + tricks_in_group;
    
	bool add_combo = false;
	if ( IsComboSkatetris() || IsSingleComboSkatetris() )
		add_combo = true;
	
/*	if ( add_combo )
	{
		mp_params->GetInteger( CRCD(0x67dd90ca,"combo_size"), &tricks_in_group, Script::ASSERT );
		possible_num_tricks += tricks_in_group;
	}
	else
	{
		possible_num_tricks++;
	}
*/
	
	// check that they haven't lost
	if ( possible_num_tricks > max_tricks )
    {
        uint32 goalId = GetGoalId();
        Dbg_Assert( goalId );
        CGoalManager* pGoalManager = GetGoalManager();
        Dbg_Assert( pGoalManager );
        pGoalManager->LoseGoal( goalId );
    }
    else 
    {
		// set the group id and increment number of trick groups
		int group_id = 0;
		if ( add_combo )
		{
			for ( int i = 0; i < Game::vMAXTETRISTRICKS; i++ )
			{
				if ( !m_validGroups[i] )
				{
					group_id = i;
					m_validGroups[group_id] = true;
					break;
				}
			}
			Dbg_Assert( !( group_id == Game::vMAXTETRISTRICKS ) );
		}		
		
		// add as many tricks as we need (1 by default)
		while ( tricks_in_group > 0 )
		{
			Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
			
			// grab list of valid key combos
			Script::CArray* p_keyCombos = NULL;
			mp_params->GetArray( CRCD(0x7be1e689,"goal_tetris_key_combos"), &p_keyCombos, Script::NO_ASSERT );

			uint32 key_combo = 0;
			uint32 trick_name_checksum = 0;
			char trick_text[Game::NEW_STRING_LENGTH];
			
			// find the first empty trick
			int index = 0;

			for ( int i = 0; i < Game::vMAXTETRISTRICKS; i++ )
			{
				if ( m_tetrisTricks[i].invalid == true )
				{
					index = i;
					m_tetrisTricks[index].invalid = false;
					m_tetrisTricks[index].group_id = group_id;
					m_tetrisTricks[index].keyCombo = 0;
					m_tetrisTricks[index].trickNameChecksum = 0;
					m_tetrisTricks[index].altTrickNameChecksum = 0;
					m_tetrisTricks[index].spin_mult = 0;
					m_tetrisTricks[index].num_taps = 1;
					m_tetrisTricks[index].require_perfect = false;
					break;
				}
			}
			Dbg_Assert( !( index == Game::vMAXTETRISTRICKS ) );
			
			// check the type of the structure...this will tell us if this is a standard
			// skatetris goal (array of key combos), or a special one (array of structures
			// containing the name and text of the trick).
			const char* p_key_combo_text = NULL;
			int num_taps = 1;
			if ( !p_keyCombos )
			{
				// check for a trick array
				Script::CArray* p_tetris_tricks = NULL;
				mp_params->GetArray( CRCD(0xeb79fb49,"goal_tetris_tricks"), &p_tetris_tricks, Script::ASSERT );
				
				// grab a random trick
				// no safety checks, since we don't care if the trick is mapped
				Script::CStruct* p_trick = p_tetris_tricks->GetStructure( Mth::Rnd( p_tetris_tricks->GetSize() ) );
				const char* p_trick_display_text;
				p_trick->GetString( CRCD(0x270f56e1,"trick"), &p_trick_display_text, Script::ASSERT );
				
				if ( p_trick->ContainsFlag( CRCD( 0x255ed86f, "grind" ) ) )
				{
					// store FS and BS versions of the trick
					char grind_string[128];
					strcpy( grind_string, "FS " );
					strcat( grind_string, p_trick_display_text );
					m_tetrisTricks[index].trickNameChecksum = Script::GenerateCRC( grind_string );

					strcpy( grind_string, "BS " );
					strcat( grind_string, p_trick_display_text );
					m_tetrisTricks[index].altTrickNameChecksum = Script::GenerateCRC( grind_string );
				}
				else
				{
					m_tetrisTricks[index].trickNameChecksum = Script::GenerateCRC( p_trick_display_text );
				}

				const char* p_text;
				p_trick->GetString( CRCD(0xc4745838,"text"), &p_text, Script::ASSERT );
				strcpy( trick_text, p_text );

				if ( !p_trick->GetChecksum( CRCD(0xacfdb27a,"key_combo"), &key_combo, Script::NO_ASSERT ) )
					p_trick->GetString( CRCD(0xacfdb27a,"key_combo"), &p_key_combo_text, Script::NO_ASSERT );

				// printf("setting trick checksum: %s\n", Script::FindChecksumName( trick_name_checksum ) );
			}
			else
			{
				// see if this is a valid key_combo
				int key_combo_index = GetRandomIndexFromKeyCombos( p_keyCombos );
				if ( key_combo_index == -1 )
				{
					// couldn't find one - the goal is going to be decativated.
					return;
				}
				if ( p_keyCombos->GetType() == ESYMBOLTYPE_NAME )
				{
					key_combo = p_keyCombos->GetChecksum( key_combo_index );
				}
				else
				{
					Dbg_MsgAssert( p_keyCombos->GetType() == ESYMBOLTYPE_STRUCTURE, ( "goal_tetris_key_combos array has bad type" ) );
					Script::CStruct* p_subStruct = p_keyCombos->GetStructure( key_combo_index );
					p_subStruct->GetChecksum( CRCD(0xacfdb27a,"key_combo"), &key_combo, Script::ASSERT );
					
					int spin_mult = 0;
					if ( p_subStruct->GetInteger( CRCD(0x8c8abd19,"random_spin"), &spin_mult, Script::NO_ASSERT ) )
					{
						// make sure it's a multiple of 180
						Dbg_MsgAssert( spin_mult % 180 == 0, ( "Skatetris goal %s has bad spin_mult value %i", Script::FindChecksumName( GetGoalId() ), spin_mult ) );
						if (spin_mult < 360)
						{
							spin_mult=360;
						}
						
						// K: Make it so that spins of 180 are excluded, since they are too hard to do on vert.
						// Ie:
						// spin_mult=360 gives spins of 0, or 360
						// spin_mult=540 gives spins of 0, 360 or 540
						// spin_mult=720 gives spins of 0, 360, 540 or 720
						// etc ...
						m_tetrisTricks[index].spin_mult = Mth::Rnd(spin_mult / 180) + 1;
						if (m_tetrisTricks[index].spin_mult == 1)
						{
							m_tetrisTricks[index].spin_mult=0;
						}	
					}
					if ( p_subStruct->GetInteger( CRCD(0xedf5db70,"spin"), &spin_mult, Script::NO_ASSERT ) )
					{
						// make sure it's a multiple of 180
						Dbg_MsgAssert( spin_mult % 180 == 0, ( "Skatetris goal %s has bad spin_mult value %i", Script::FindChecksumName( GetGoalId() ), spin_mult ) );
						m_tetrisTricks[index].spin_mult = spin_mult / 180;
					}

					if ( p_subStruct->ContainsFlag( CRCD(0x1c39f1b9,"perfect") ) )
						m_tetrisTricks[index].require_perfect = true;

					if ( p_subStruct->GetInteger( CRCD(0xa4bee6a1,"num_taps"), &num_taps, Script::NO_ASSERT ) )
						m_tetrisTricks[index].num_taps = num_taps;
				}
				
				// get the global trick mapping
				Mdl::Skate* skate_mod = Mdl::Skate::Instance();
				Obj::CSkaterProfile* p_SkaterProfile = skate_mod->GetCurrentProfile();
				Script::CStruct* p_trickMappings = p_SkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );
		
				const char* p_text = NULL;
				
				// if it's not a regular assignment, grab cat info
				if ( !p_trickMappings->GetChecksum( key_combo, &trick_name_checksum, Script::NO_ASSERT ) )
				{
					int cat_index;
					p_trickMappings->GetInteger( key_combo, &cat_index, Script::ASSERT );
					Dbg_Assert( cat_index >= 0 && cat_index < vMAX_CREATED_TRICKS );
					Obj::CSkater* pSkater = skate_mod->GetLocalSkater();
					if ( pSkater )
					{
						Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[cat_index];
						pCreatedTrick->mp_other_params->GetString( CRCD( 0xa1dc81f9, "name" ), &p_text, Script::ASSERT );
						
						// mark it
						// no cat tricks have double versions!
						m_tetrisTricks[index].num_taps = 1;
					}					
				}
	
				if ( !p_text )
				{
					Script::CStruct* p_trick_structure = NULL;
					p_trick_structure = Script::GetStructure( trick_name_checksum, Script::NO_ASSERT );
					if ( !p_trick_structure )
					{
						Script::CArray* p_trick_array = Script::GetArray( trick_name_checksum, Script::ASSERT );
						p_trick_structure = p_trick_array->GetStructure( 0 );
		
						Dbg_MsgAssert( p_trick_structure, ( "Could not find trick structure" ) );
					}
					Script::CStruct* p_trick_params;
					p_trick_structure->GetStructure( CRCD(0x7031f10c,"Params"), &p_trick_params, Script::ASSERT );
					
					
					// get any multiple tap info
					while ( num_taps > 1 )
					{
						num_taps--;
	
						uint32 extra_trick;
						if ( p_trick_params->GetChecksum( CRCD(0x6e855102,"ExtraTricks"), &extra_trick, Script::NO_ASSERT ) )
						{
							Script::CArray* p_extra_trick_array = Script::GetArray( extra_trick, Script::ASSERT );
							Dbg_MsgAssert( p_extra_trick_array->GetType() == ESYMBOLTYPE_STRUCTURE, ( "ExtraTricks array %s has wrong type", Script::FindChecksumName( extra_trick ) ) );
							Script::CStruct* p_extra_trick_struct = p_extra_trick_array->GetStructure( 0 );
							p_extra_trick_struct->GetStructure( CRCD(0x7031f10c,"Params"), &p_trick_params, Script::ASSERT );
						}
						else
						{
							num_taps = 1;
							m_tetrisTricks[index].num_taps = 1;
						}
					}
					
					p_trick_params->GetLocalText( CRCD(0xa1dc81f9,"Name"), &p_text, Script::ASSERT );
				}

				// build the text string
				strcpy( trick_text, "" );

				// spin value
				if ( m_tetrisTricks[index].spin_mult != 0 )
				{
					// perfect modifier
					if ( m_tetrisTricks[index].require_perfect )
						sprintf( trick_text, "Perfect " );

					sprintf( trick_text, "\\c5%i\\c0 ", ( m_tetrisTricks[index].spin_mult * 180 ) );
				}
				// trick text
				strcat( trick_text, p_text );
				
				// store key combo
				m_tetrisTricks[index].keyCombo = key_combo;
			}
			
			// create the container for this element
			Script::CStruct *p_container_params = new Script::CStruct();
			Front::CContainerElement *p_container = new Front::CContainerElement();
			uint32 container_id = Obj::CBaseManager::vNO_OBJECT_ID;
			p_container->SetID( container_id );
			p_screen_elem_man->RegisterObject( *p_container );

			// figure the dims
			int y_dim = 20;
			if ( add_combo && tricks_in_group == 1 )
			{
				y_dim *= 2;
			}
			p_container_params->AddPair( CRCD(0x34a68574,"dims"), 100, y_dim );

			p_container->SetProperties( p_container_params );
			
			// set the parent
			Front::CScreenElement* p_vmenu = p_screen_elem_man->GetElement(CRCD(0xe35ec715,"tetris_tricks_menu"), Front::CScreenElementManager::ASSERT );
			p_vmenu->SetChildLockState( Front::CScreenElement::UNLOCK );
			p_screen_elem_man->SetParent( p_vmenu, p_container );

			delete p_container_params;						
			
			// create text name element
			Script::CStruct *p_elem_params = new Script::CStruct();
			p_elem_params->AddString( CRCD(0xc4745838,"text"), trick_text );
			p_elem_params->AddChecksum( CRCD(0x2f6bf72d,"font"), CRCD( 0xbc19c379, "newtrickfont" ) );
			p_elem_params->AddChecksum( NONAME, CRCD( 0x1d944426, "not_focusable" ) );
			uint32 id = Obj::CBaseManager::vNO_OBJECT_ID;
	
			Front::CTextElement* p_new_element = new Front::CTextElement();
			p_new_element->SetID( id );
			p_screen_elem_man->RegisterObject( *p_new_element );
			
			p_new_element->SetProperties( p_elem_params );
	
			// set the parent
			p_screen_elem_man->SetParent( p_container, p_new_element );
			
			delete p_elem_params;
			
			
			// create button combo element
			if ( p_key_combo_text || key_combo )
			{
				Script::CStruct* p_button_combos;
				if ( m_tetrisTricks[index].num_taps == 2 )
					p_button_combos = Script::GetStructure( CRCD(0xe7abfb95,"goal_tetris_trick_text_double_tap"), Script::ASSERT );
				else
					p_button_combos = Script::GetStructure( CRCD(0x8856c817,"goal_tetris_trick_text"), Script::ASSERT );

				if ( !p_key_combo_text && !p_button_combos->GetString( key_combo, &p_key_combo_text, Script::NO_ASSERT ) )
				{
					Dbg_MsgAssert( 0, ( "Could not find button text for skatetris trick" ) );
				}

				Script::CStruct *p_button_params = new Script::CStruct();
				p_button_params->AddString( CRCD(0xc4745838,"text"), p_key_combo_text );
				p_button_params->AddChecksum( CRCD(0x2f6bf72d,"font"), CRCD( 0x8aba15ec, "small" ) );
				p_button_params->AddChecksum( NONAME, CRCD( 0x1d944426, "not_focusable" ) );
				uint32 button_id = Obj::CBaseManager::vNO_OBJECT_ID;
		
				Front::CTextElement* p_button_element = new Front::CTextElement();
				p_button_element->SetID( button_id );
				p_screen_elem_man->RegisterObject( *p_button_element );
				
				p_button_element->SetProperties( p_button_params );
				
				// the button is a child of the container
				p_screen_elem_man->SetParent( p_container, p_button_element );
		
				delete p_button_params;
			}
	
			// store the id
			m_tetrisTricks[index].screenElementId = p_container->GetID();
			m_numTetrisTricks++;
	
			// run entrance animation
			Script::CStruct* p_script_params = new Script::CStruct();
			if ( !p_key_combo_text )
				p_script_params->AddChecksum( NONAME, CRCD( 0x7b716096, "no_key_combo" ) );
			uint32 script = CRCD(0x668cc9e3,"goal_tetris_add_trick");

			if ( !IsTricktris() && !IsSingleComboSkatetris() && m_numTetrisTricks > ( .75 * (float)max_tricks ) )
				script = CRCD(0x8e68afc2,"goal_tetris_add_red_trick");
	
			Script::CScript *p_new_script = Script::SpawnScript( script, p_script_params );
			#ifdef __NOPT_ASSERT__
			p_new_script->SetCommentString("Spawned from CSkatetrisGoal::AddTetrisTrick()");
			#endif
			p_new_script->mpObject = p_container;
			// normally, script won't be updated until next frame -- we want it NOW, motherfucker
			p_new_script->Update();
			delete p_script_params;
	
			// turn the elements red if you're over 75% of the way to the top,
			// but not if we're doing a single combo
			if ( !IsTricktris() && !IsSingleComboSkatetris() && m_numTetrisTricks > ( .75 * (float)max_tricks ) )
			{
				mp_params->AddInteger( CRCD(0xd02ac196,"list_is_red"), 1 );
				
				for ( int j = 0; j < Game::vMAXTETRISTRICKS; j++ )
				{
					if ( !m_tetrisTricks[j].invalid )
					{
						Front::CScreenElement* p_trick = p_screen_elem_man->GetElement( m_tetrisTricks[j].screenElementId , Front::CScreenElementManager::ASSERT );
						script = CRCD(0xeb016d1e,"goal_tetris_turn_trick_red");
						Script::CScript* p_trick_script = Script::SpawnScript( script, NULL );
						#ifdef __NOPT_ASSERT__
						p_trick_script->SetCommentString("Spawned from CSkatetrisGoal::AddTetrisTrick()");
						#endif
						p_trick_script->mpObject = p_trick;
						p_trick_script->Update();
					}
				}
			}
			tricks_in_group--;
		}
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkatetrisGoal::CheckTetrisTricks()
{
	// special case trick flag...used for an additional condition that must be
	// met before any skatetris tricks can be cleared.
	// The flag is cleared each time the tricks are checked
	int trick_flag;
	if ( mp_params->GetInteger( CRCD(0x60b827d5,"trick_flag"), &trick_flag, Script::NO_ASSERT ) )
	{
		if ( trick_flag == 0 )
			return;
		else 
			mp_params->AddInteger( CRCD(0x60b827d5,"trick_flag"), 0 );
	}
	
	bool check_combo = false;
	if ( IsSingleComboSkatetris() || IsComboSkatetris() )
		check_combo = true;

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
    Obj::CSkater *pSkater = skate_mod->GetLocalSkater();
    Dbg_Assert( pSkater );
  
    Mdl::Score * p_score = ( pSkater->GetScoreObject() );
    Dbg_Assert( p_score );
    
	// check for combos!
	if ( check_combo )
	{
		CheckTetrisCombos();
	}
	else
	{
		// check for single tricks
		bool trick_removed = false;
		for ( int i = 0; i < Game::vMAXTETRISTRICKS; i++ )
		{
			if ( !m_tetrisTricks[i].invalid )
			{
				if ( m_tetrisTricks[i].trickNameChecksum != 0 )
				{
					if ( p_score->GetCurrentNumberOfOccurrencesByName( m_tetrisTricks[i].trickNameChecksum, m_tetrisTricks[i].spin_mult, m_tetrisTricks[i].require_perfect ) )
					{
						if ( !trick_removed )
						{
							Script::RunScript( "goal_tetris_play_trick_removed_sound" );
						}
						trick_removed = true;
						RemoveTrick( i );
					}
					else if ( m_tetrisTricks[i].altTrickNameChecksum != 0 )
					{
						// check alternate trick name - usually BS version of grind trick
						if ( p_score->GetCurrentNumberOfOccurrencesByName( m_tetrisTricks[i].altTrickNameChecksum, m_tetrisTricks[i].spin_mult, m_tetrisTricks[i].require_perfect ) )
						{
							if ( !trick_removed )
							{
								Script::RunScript( "goal_tetris_play_trick_removed_sound" );
							}
							trick_removed = true;
							RemoveTrick( i );							
						}
					}
				}
				else if ( m_tetrisTricks[i].keyCombo != 0 )
				{
					// printf( "checking for a key combo %s\n", Script::FindChecksumName( m_tetrisTricks[i].keyCombo ));
					if ( p_score->GetCurrentNumberOfOccurrences( m_tetrisTricks[i].keyCombo, m_tetrisTricks[i].spin_mult, m_tetrisTricks[i].require_perfect, m_tetrisTricks[i].num_taps ) )
					{
						if ( !trick_removed )
							Script::RunScript( "goal_tetris_play_trick_removed_sound" );
						trick_removed = true;
						RemoveTrick( i );
					}
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkatetrisGoal::CheckTetrisCombos()
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
    Obj::CSkater *p_Skater = skate_mod->GetLocalSkater();
    Dbg_Assert( p_Skater );
  
    Mdl::Score * p_Score = ( p_Skater->GetScoreObject() );
    Dbg_Assert( p_Score );
    
	int group_size;
	mp_params->GetInteger( CRCD(0x67dd90ca,"combo_size"), &group_size, Script::ASSERT );
	
	for ( int group_id = 0; group_id < Game::vMAXTETRISTRICKS; group_id++ )
	{
		bool trick_removed = false;

		// skip invalid groups
		if ( !m_validGroups[group_id] )
			continue;
		
		// see how many tricks they got out of this group
		int group_total = 0;
		for ( int i = 0; i < Game::vMAXTETRISTRICKS; i++ )
		{
			if ( m_tetrisTricks[i].keyCombo != 0 )
			{
				if ( !m_tetrisTricks[i].invalid &&
					 m_tetrisTricks[i].group_id == group_id &&
					 p_Score->GetCurrentNumberOfOccurrences( m_tetrisTricks[i].keyCombo, m_tetrisTricks[i].spin_mult, m_tetrisTricks[i].require_perfect, m_tetrisTricks[i].num_taps ) > 0
				   )
					group_total++;
			}
			else if ( m_tetrisTricks[i].trickNameChecksum != 0 )
			{
				if ( !m_tetrisTricks[i].invalid &&
					 m_tetrisTricks[i].group_id == group_id &&
					 p_Score->GetCurrentNumberOfOccurrencesByName( m_tetrisTricks[i].trickNameChecksum, m_tetrisTricks[i].spin_mult, m_tetrisTricks[i].require_perfect, m_tetrisTricks[i].num_taps ) > 0
				   )
				{
					group_total++;
				}
				else if ( m_tetrisTricks[i].altTrickNameChecksum != 0 )
				{
					// check alternate version of trick - usually BS version of grind
					if ( !m_tetrisTricks[i].invalid &&
						 m_tetrisTricks[i].group_id == group_id &&
						 p_Score->GetCurrentNumberOfOccurrencesByName( m_tetrisTricks[i].altTrickNameChecksum, m_tetrisTricks[i].spin_mult, m_tetrisTricks[i].require_perfect, m_tetrisTricks[i].num_taps ) > 0
					   )
						group_total++;					
				}
			}
		}
	
		// remove this group?
		if ( group_total >= group_size )
		{
			// kill all tricks in this group
			for ( int j = 0; j < Game::vMAXTETRISTRICKS; j++ )
			{
				if ( !m_tetrisTricks[j].invalid && m_tetrisTricks[j].group_id == group_id )
				{
					if ( !trick_removed )
						Script::RunScript( CRCD(0xe4ea743a,"goal_tetris_play_trick_removed_sound") );
					trick_removed = true;

					RemoveTrick( j );
				}
			}

			// this group id is dead
			m_validGroups[group_id] = false;

			// was this a single combo goal?
			if ( IsSingleComboSkatetris() )
			{
				Game::CGoalManager* pGoalManager = Game::GetGoalManager();
				pGoalManager->WinGoal( GetGoalId() );
			}
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkatetrisGoal::RemoveTrick( int index )
{	
	int max_tricks;
	mp_params->GetInteger( CRCD(0x89473db7,"max_tricks"), &max_tricks, Script::ASSERT );

	// printf("found a completed trick at %i\n", i);
	Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
	uint32 id = m_tetrisTricks[index].screenElementId;
	Dbg_Assert( id );
	Front::CScreenElementPtr p_trick = p_screen_elem_man->GetElement( id, Front::CScreenElementManager::ASSERT );

	// kill any scripts running on this element so the animations don't conflict
	Script::CStruct* p_temp_params = new Script::CStruct();
	p_temp_params->AddChecksum( CRCD(0x40c698af,"id"), id );
	Script::RunScript( CRCD(0x235bde21,"goal_tetris_reset_trick_container"), p_temp_params );
	delete p_temp_params;
	
	uint32 script;
	
	// turn the elements back to white if you fell to under 75% full
	int list_is_red = 0;
	mp_params->GetInteger( CRCD(0xd02ac196,"list_is_red"), &list_is_red, Script::NO_ASSERT );
	if ( ( list_is_red == 1 ) && ( ( m_numTetrisTricks - 1 ) < ( .75 * (float)max_tricks ) ) )
	{
		mp_params->AddInteger( CRCD(0xd02ac196,"list_is_red"), 0 );
		for ( int j = 0; j < Game::vMAXTETRISTRICKS; j++ )
		{
			if ( !m_tetrisTricks[j].invalid )
			{
				Front::CScreenElement* p_trick = p_screen_elem_man->GetElement( m_tetrisTricks[j].screenElementId , Front::CScreenElementManager::ASSERT );
				script = CRCD(0xa39b24e0,"goal_tetris_turn_trick_white");
				Script::CScript* p_trick_script = Script::SpawnScript( script, NULL );
				#ifdef __NOPT_ASSERT__
				p_trick_script->SetCommentString("Spawned from CSkatetrisGoal::RemoveTrick()");
				#endif

				p_trick_script->mpObject = p_trick;
				p_trick_script->Update();
			}
		}
	}
	
	// remove the trick
	script = CRCD(0x23a6a456,"goal_tetris_remove_trick");
	Script::CStruct* pScriptParams = new Script::CStruct();
	
	// flag for removing tricktris tricks immediately
	if ( IsTricktris() )
		pScriptParams->AddChecksum( NONAME, CRCD(0xea7ba666,"tricktris") );
	
	Script::CScript *p_new_script = Script::SpawnScript( script, pScriptParams );

	#ifdef __NOPT_ASSERT__
	p_new_script->SetCommentString("Spawned from CSkatetrisGoal::RemoveTrick()");
	#endif
	p_new_script->mpObject = p_trick;
	p_new_script->Update();
	delete pScriptParams;
	
	// grab the number of tricks completed
	int tricks_completed = 0;
	mp_params->GetInteger( CRCD(0x8d958f01,"tricks_completed"), &tricks_completed, Script::NO_ASSERT );
	tricks_completed++;
	
	// accelerate if needed
	int acceleration_interval = 0;
	mp_params->GetInteger( CRCD(0x1181b29,"acceleration_interval"), &acceleration_interval, Script::NO_ASSERT );
	// printf("acceleration interval = %i, tricks completed = %i\n", acceleration_interval, tricks_completed );
	if ( acceleration_interval && ( tricks_completed % acceleration_interval == 0 ) )
	{
		// adjust the time between new tricks
		float acceleration_percent = 0;
		mp_params->GetFloat( CRCD(0x76f65c8,"acceleration_percent"), &acceleration_percent, Script::NO_ASSERT );
		int trick_time;
		mp_params->GetInteger( CRCD(0x648d8d2a,"adjusted_trick_time"), &trick_time, Script::ASSERT );
		trick_time = (int)( ( 1 - acceleration_percent ) * trick_time );
		mp_params->AddInteger( CRCD(0x648d8d2a,"adjusted_trick_time"), trick_time );
		// printf("setting trick time to %i\n", trick_time);
	}
	// store the number of tricks completed
	// printf("%i tricks completed\n", tricks_completed );
	mp_params->AddInteger( CRCD(0x8d958f01,"tricks_completed"), tricks_completed );
	
	// update number of tricks in menu
	m_numTetrisTricks--;
	
	// printf("setting %s trick to invalid at index %i\n", m_tetrisTricks[i].trickText, i );
	m_tetrisTricks[index].invalid = true;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkatetrisGoal::UpdateFadedTricks()
{
	// special case trick flag...used for an additional condition that must be
	// met before any skatetris tricks can be cleared.
	// The flag is cleared each time the tricks are checked
	bool found_trick_flag = false;
	int trick_flag;
	if ( mp_params->GetInteger( CRCD(0x60b827d5,"trick_flag"), &trick_flag, Script::NO_ASSERT ) )
	{
		found_trick_flag = true;
	}
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
    Obj::CSkater *pSkater = skate_mod->GetLocalSkater();
    Dbg_Assert( pSkater );
  
    Mdl::Score * pScore = ( pSkater->GetScoreObject() );
    Dbg_Assert( pScore );

	Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
	Dbg_Assert( p_screen_elem_man );

	float faded_trick_alpha = Script::GetFloat( CRCD( 0x855e6da4, "goal_tetris_faded_trick_alpha" ), Script::ASSERT );
	float unfaded_trick_alpha = Script::GetFloat( CRCD( 0xdbe5b19e, "goal_tetris_unfaded_trick_alpha" ), Script::ASSERT );

	for ( int i = 0; i < vMAXTETRISTRICKS; i++ )
	{
		if ( !m_tetrisTricks[i].invalid )
		{
			Front::CScreenElement* p_trick = p_screen_elem_man->GetElement( m_tetrisTricks[i].screenElementId , Front::CScreenElementManager::ASSERT );
			bool should_be_faded = false;
			if ( found_trick_flag && !trick_flag )
			{
				should_be_faded = false;
			}
			else if ( m_tetrisTricks[i].keyCombo != 0 )
			{
				int n = pScore->GetCurrentNumberOfOccurrences( m_tetrisTricks[i].keyCombo, m_tetrisTricks[i].spin_mult, m_tetrisTricks[i].require_perfect, m_tetrisTricks[i].num_taps );
				if ( n > 0 )
				{
					should_be_faded = true;
				}
			}
			else if ( m_tetrisTricks[i].trickNameChecksum != 0 )
			{
				int n = pScore->GetCurrentNumberOfOccurrencesByName( m_tetrisTricks[i].trickNameChecksum, m_tetrisTricks[i].spin_mult, m_tetrisTricks[i].require_perfect );
				if ( n > 0 )
				{
					should_be_faded = true;
				}
				else if ( m_tetrisTricks[i].altTrickNameChecksum != 0 )
				{
					// check alternate version of trick - usually BS version of grind
					int n = pScore->GetCurrentNumberOfOccurrencesByName( m_tetrisTricks[i].altTrickNameChecksum, m_tetrisTricks[i].spin_mult, m_tetrisTricks[i].require_perfect );
					if ( n > 0 )
					{
						should_be_faded = true;
					}
				}
			}

			if ( should_be_faded )
				p_trick->SetAlpha( faded_trick_alpha, Front::CScreenElement::FORCE_INSTANT );
			else
				p_trick->SetAlpha( unfaded_trick_alpha, Front::CScreenElement::FORCE_INSTANT );
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkatetrisGoal::Expire()
{
	// expire during normal skatetris is winning...during single combo it's losing
	if ( IsSingleComboSkatetris() || IsTricktris() )
		CGoal::Lose();
	else
	{
		if ( AllTricksCleared() )
		{
			CGoalManager* pGoalManager = GetGoalManager();
			pGoalManager->WinGoal( GetGoalId() );
		}
		else
		{
			RunCallbackScript( vEXPIRED );
	
			// TODO: fix after e3
			// this was done to make the horse and combo letter goals work right
			if ( m_endRunType != vNONE )
				Deactivate();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkatetrisGoal::AllTricksCleared()
{
    for ( int i = 0; i < Game::vMAXTETRISTRICKS; i++ )
    {
        if ( m_tetrisTricks[i].invalid == false )
			return false;
    }
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkatetrisGoal::ClearAllTricks()
{
    m_numTetrisTricks = 0;
    m_tetrisTime = 0;
    
    for ( int i = 0; i < Game::vMAXTETRISTRICKS; i++ )
    {
        m_tetrisTricks[i].invalid = true;
        m_tetrisTricks[i].trickNameChecksum = 0;
		m_tetrisTricks[i].altTrickNameChecksum = 0;
		m_tetrisTricks[i].keyCombo = 0;

		m_validGroups[i] = false;
    }

	mp_params->RemoveFlag( CRCD(0xc93da683,"single_combo_added") );

	// clear all the screen elements
	Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
	Script::CScript* pScript = NULL;
	p_screen_elem_man->DestroyElement( CRCD(0xe35ec715,"tetris_tricks_menu"), Front::CScreenElementManager::RECURSE, Front::CScreenElementManager::PRESERVE_PARENT, pScript);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkatetrisGoal::IsComboSkatetris()
{
	int combo_test = 0;
	mp_params->GetInteger( CRCD(0x4ec3cfb5,"combo"), &combo_test, Script::NO_ASSERT );
	return ( combo_test || mp_params->ContainsFlag( CRCD(0x4ec3cfb5,"combo") ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkatetrisGoal::IsSingleComboSkatetris()
{
	int single_combo_test = 0;
	mp_params->GetInteger( CRCD(0xdf47b144,"single_combo"), &single_combo_test, Script::NO_ASSERT );
	return ( single_combo_test || mp_params->ContainsFlag( CRCD(0xdf47b144,"single_combo") ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkatetrisGoal::IsTricktris()
{
	int tricktris_test = 0;
	mp_params->GetInteger( CRCD(0xea7ba666,"tricktris"), &tricktris_test, Script::NO_ASSERT );
	return ( tricktris_test || mp_params->ContainsFlag( CRCD(0xea7ba666,"tricktris") ) );
}


}		// namespace Game


================================================
FILE: Code/Sk/Modules/Skate/SkatetrisGoal.h
================================================
// skatetris subclass

#ifndef __SK_MODULES_SKATE_SKATETRISGOAL_H__
#define __SK_MODULES_SKATE_SKATETRISGOAL_H__

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 

namespace Game
{

const int vMAXTETRISTRICKS		= 30;

struct tetrisTrick 
{
	uint32 		trickNameChecksum;
	uint32		altTrickNameChecksum;
    uint32 		keyCombo;
    uint32 		screenElementId;
    bool 		invalid;
	bool		require_perfect;
	int 		group_id;
	int 		spin_mult;
	int 		num_taps;
};

class CSkatetrisGoal : public CGoal
{

public:
						CSkatetrisGoal( Script::CStruct* pParams );
	virtual				~CSkatetrisGoal();

	bool				Activate();
	bool				Deactivate( bool force = false, bool affect_tree = true );
	bool				Win();
	void				Expire();
	
	bool				Update();

	void                StartTetrisGoal();
	void                EndTetrisGoal();
	void                CheckTetrisTricks();
	void				CheckTetrisCombos();
	void                AddTetrisTrick( int num_to_add = 1 );
	void				UpdateFadedTricks();

	void				RemoveTrick( int index );

	bool				AllTricksCleared();

	void				ClearAllTricks();

	bool				IsSingleComboSkatetris();
	bool				IsComboSkatetris();
	bool				IsTricktris();
protected:

    tetrisTrick         m_tetrisTricks[vMAXTETRISTRICKS];
    int                 m_numTetrisTricks;
    int					m_tetrisTime;
	bool				m_validGroups[vMAXTETRISTRICKS];
};

}

#endif



================================================
FILE: Code/Sk/Modules/Skate/VictoryCond.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Skate													**
**																			**
**	File name:		Skate/VictoryCond.cpp									**
**																			**
**	Created by:		02/07/01	-	gj										**
**																			**
**	Description:	Defines various victory conditions for a game			**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Game
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   PrivateFunctions								**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CVictoryCondition::CVictoryCondition() //: Lst::Node< CVictoryCondition >( this )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CHighestScoreVictoryCondition::CHighestScoreVictoryCondition() : CVictoryCondition()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CHighestScoreVictoryCondition::ConditionComplete()
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CHighestScoreVictoryCondition::PrintDebugInfo()
{
//	printf( "Found CHighestScoreVictoryCondition\n" );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CHighestScoreVictoryCondition::IsTerminal()
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CHighestScoreVictoryCondition::IsWinner( uint32 skater_num )
{

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	for ( uint32 i = 0; i < skate_mod->GetNumSkaters(); i++)
	{
		if ( i != skater_num )
		{
			Mdl::Score* pTestThisScore = skate_mod->GetSkater(skater_num)->GetScoreObject();

			// score object
			Mdl::Score* pScore = skate_mod->GetSkater(i)->GetScoreObject();

			if ( pTestThisScore->GetTotalScore() < pScore->GetTotalScore() )
			{
				return false;
			}
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CScoreReachedVictoryCondition::CScoreReachedVictoryCondition( uint32 target_score ) : CVictoryCondition()
{
	m_TargetScore = target_score;

//	PrintDebugInfo();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CScoreReachedVictoryCondition::ConditionComplete()
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	if( skate_mod->GetGameMode()->IsTeamGame())
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		Lst::Search< GameNet::PlayerInfo > sh;
		GameNet::PlayerInfo* player;
		int i;
		int total_score[GameNet::vMAX_TEAMS] = {0};

		for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
		{
			Mdl::Score* pScore = player->m_Skater->GetScoreObject();

			total_score[player->m_Team] += pScore->GetTotalScore();
		}

		for( i = 0; i < GameNet::vMAX_TEAMS; i++ )
		{
			if( total_score[i] >= (int32)m_TargetScore )
			{
				return true;
			}
		}
	}
	else
	{
		for ( uint32 i = 0; i < skate_mod->GetNumSkaters(); i++)
		{
			// score object
			Mdl::Score* pScore = skate_mod->GetSkater(i)->GetScoreObject();
			
			if ( pScore->GetTotalScore() >= (int32)m_TargetScore )
			{
				return true;
			}
		}
	}
	

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CScoreReachedVictoryCondition::PrintDebugInfo()
{
	printf( "Found CScoreReachedVictoryCondition %d\n", m_TargetScore );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	CScoreReachedVictoryCondition::GetTargetScore( void )
{
	return m_TargetScore;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CScoreReachedVictoryCondition::IsTerminal()
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CScoreReachedVictoryCondition::IsWinner( uint32 skater_num )
{

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	for ( uint32 i = 0; i < skate_mod->GetNumSkaters(); i++)
	{
		if ( i != skater_num )
		{
			Mdl::Score* pTestThisScore = skate_mod->GetSkater(skater_num)->GetScoreObject();

			// score object
			Mdl::Score* pScore = skate_mod->GetSkater(i)->GetScoreObject();

			if ( pTestThisScore->GetTotalScore() < pScore->GetTotalScore() )
			{
				return false;
			}
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CGoalsCompletedVictoryCondition::CGoalsCompletedVictoryCondition( void )
: CVictoryCondition()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool GoalAttackComplete()
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Game::CGoalManager* pGoalManager;
	
	pGoalManager = Game::GetGoalManager();
	if( skate_mod->GetGameMode()->IsTeamGame())
	{
		int i, num_selected_goals;

		num_selected_goals = pGoalManager->GetNumSelectedGoals();
		for( i = 0; i < skate_mod->GetGameMode()->NumTeams(); i++ )
		{
			if( gamenet_man->GetTeamScore( i ) >= num_selected_goals )
			{
				return true;
			}
		}
	}
	else
	{
		uint32 i, j, num_goals;
		bool completed_all;
		Game::CGoal* pGoal;
		
		num_goals = pGoalManager->GetNumGoals();
		for( i = 0; i < skate_mod->GetNumSkaters(); i++ )
		{
			Obj::CSkater* skater = skate_mod->GetSkater(i);
			completed_all = true;
			for( j = 0; j < num_goals; j++ )
			{
				pGoal = pGoalManager->GetGoalByIndex( j );

				if( pGoal->GetParents()->m_relative != 0 )
				{
					continue;
				}
				if( pGoal->IsSelected())
				{
					if( pGoal->HasWonGoal( skater->GetID()) == false )
					{
						completed_all = false;
						break;
					}
				}
			}    

			if( completed_all )
			{
				return true;
			}
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGoalsCompletedVictoryCondition::ConditionComplete()
{
	return GoalAttackComplete();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGoalsCompletedVictoryCondition::IsWinner( uint32 skater_num )
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGoalsCompletedVictoryCondition::IsTerminal()
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CGoalsCompletedVictoryCondition::PrintDebugInfo()
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CBestComboVictoryCondition::CBestComboVictoryCondition( uint32 num_combos ) : CVictoryCondition()
{
	m_NumCombos = num_combos;

//	PrintDebugInfo();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CBestComboVictoryCondition::ConditionComplete()
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CBestComboVictoryCondition::PrintDebugInfo()
{
	printf( "Found CBestComboVictoryCondition %d\n", m_NumCombos );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CBestComboVictoryCondition::IsTerminal()
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool
CBestComboVictoryCondition::IsWinner( uint32 skater_num )
{

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	for ( uint32 i = 0; i < skate_mod->GetNumSkaters(); i++)
	{
		if ( i != skater_num )
		{
			Mdl::Score* pTestThisScore = skate_mod->GetSkater(skater_num)->GetScoreObject();

			// score object
			Mdl::Score* pScore = skate_mod->GetSkater(i)->GetScoreObject();

			if ( pTestThisScore->GetTotalScore() < pScore->GetTotalScore() )
			{
				return false;
			}
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CVictoryCondition*	CreateVictoryConditionInstance( Script::CScriptStructure* p_structure )
{
	

	Dbg_Assert( p_structure );

	uint32 condition_type;
	p_structure->GetChecksum( "type", &condition_type, true );

	CVictoryCondition* pVictoryCondition = NULL;
	
	switch ( condition_type )
	{
		case vVICTORYCOND_HIGHESTSCORE:
		{
			pVictoryCondition = new CHighestScoreVictoryCondition( );
		}
		break;
		case vVICTORYCOND_TARGETSCORE:
		{
			int target_score;
			p_structure->GetInteger( "score", &target_score, true );
			pVictoryCondition = new CScoreReachedVictoryCondition( (uint32)target_score );
		}
		break;
		case vVICTORYCOND_BESTCOMBO:
		{
			int num_combos;
			p_structure->GetInteger( "num_combos", &num_combos, true );
			pVictoryCondition = new CBestComboVictoryCondition( (uint32)num_combos );
		}
		break;
		case vVICTORYCOND_COMPLETEGOALS:
		{
			pVictoryCondition = new CGoalsCompletedVictoryCondition;
		}
		break;
		case vVICTORYCOND_LASTMANSTANDING:
		{
			pVictoryCondition = new CLastManStandingVictoryCondition;
		}
		break;
		default:
		{
			Dbg_MsgAssert( 0,( "Unrecognized victory condition type %08x %s", condition_type, Script::FindChecksumName( condition_type ) ));
		}
		break;
	}

	return pVictoryCondition;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CLastManStandingVictoryCondition::CLastManStandingVictoryCondition( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CLastManStandingVictoryCondition::ConditionComplete()
{
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
	GameNet::PlayerInfo* player;

	// This logic may seem strange. Basically, when ConditionComplete() returns true,
	// the local skater ends his run and he can observe other players. So, it will return
	// true when his hit points have been depleted rather than when he has won.

	player = gamenet_man->GetLocalPlayer();
	if( player && player->m_Skater )
	{
		Mdl::Score* pScore = player->m_Skater->GetScoreObject();

		if( pScore->GetTotalScore() <= 0 )
		{
			return true;
		}
	}
		
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CLastManStandingVictoryCondition::PrintDebugInfo()
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CLastManStandingVictoryCondition::IsTerminal()
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CLastManStandingVictoryCondition::IsWinner( uint32 skater_num )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	// score object
	Mdl::Score* pScore = skate_mod->GetSkater(skater_num)->GetScoreObject();
	if( pScore && ( pScore->GetTotalScore() > 0 ))
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Game



================================================
FILE: Code/Sk/Modules/Skate/VictoryCond.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Skate													**
**																			**
**	File name:		Skate/VictoryCond.h										**
**																			**
**	Created by:		02/07/01	-	gj										**
**																			**
**	Description:	Defines various victory conditions for a game			**
**																			**
*****************************************************************************/

#ifndef __MODULES_SKATE_VICTORYCOND_H
#define __MODULES_SKATE_VICTORYCOND_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Script
{
    class CStruct;
}
        
namespace Game
{
 
const uint32 vVICTORYCOND_HIGHESTSCORE = 0x8f30fd26;
const uint32 vVICTORYCOND_TARGETSCORE = 0x71cd53a2;
const uint32 vVICTORYCOND_BESTCOMBO = 0x6fad9811;
const uint32 vVICTORYCOND_COMPLETEGOALS = 0x6fc19399;	// complete_goals
const uint32 vVICTORYCOND_LASTMANSTANDING = 0xcc06880f;	// last_man_standing
   
/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CVictoryCondition : public Spt::Class
{
	

public:
	CVictoryCondition();
	virtual					~CVictoryCondition() {}
	virtual bool			IsWinner( uint32 skater_num ) { return false; }
	virtual bool			IsTerminal() = 0;
	virtual bool			ConditionComplete() = 0;
	virtual bool			PrintDebugInfo() = 0;
};

class CHighestScoreVictoryCondition : public  CVictoryCondition
{
public:
	CHighestScoreVictoryCondition();
	bool					ConditionComplete();
	bool					PrintDebugInfo();
	bool					IsTerminal();
	bool					IsWinner( uint32 skater_num );
};

class CScoreReachedVictoryCondition : public  CVictoryCondition
{
public:
	CScoreReachedVictoryCondition( uint32 target_score );
	bool					ConditionComplete();
	bool					PrintDebugInfo();
	bool					IsTerminal();
	bool					IsWinner( uint32 skater_num );
	int						GetTargetScore( void );
protected:
	uint32				m_TargetScore;
};

bool GoalAttackComplete();

class CGoalsCompletedVictoryCondition : public  CVictoryCondition
{
public:
	CGoalsCompletedVictoryCondition();
	bool					ConditionComplete();
	bool					PrintDebugInfo();
	bool					IsTerminal();
	bool					IsWinner( uint32 skater_num );
};

class CBestComboVictoryCondition : public  CVictoryCondition
{
public:
	CBestComboVictoryCondition( uint32 num_combos );
	bool					ConditionComplete();
	bool					PrintDebugInfo();
	bool					IsTerminal();
	bool					IsWinner( uint32 skater_num );
protected:
	uint32				m_NumCombos;
};

class CLastManStandingVictoryCondition : public CVictoryCondition
{
public:
	CLastManStandingVictoryCondition( void );
	bool					ConditionComplete();
	bool					PrintDebugInfo();
	bool					IsTerminal();
	bool					IsWinner( uint32 skater_num );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

CVictoryCondition*	CreateVictoryConditionInstance( Script::CStruct* p_structure );

/*****************************************************************************
**						Inline Functions									**
*****************************************************************************/
					
} // namespace Game

#endif // __MODULES_SKATE_VICTORYCOND_H

================================================
FILE: Code/Sk/Modules/Skate/competition.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate3													**
**																			**
**	Module:			skate			 										**
**																			**
**	File name:		competition.cpp											**
**																			**
**	Created by:		18/06/01	-	Mick									**
**																			**
**	Description:	handles playing single player competitions				**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 						 // for Mth::Rnd2()
#include 
#include 
#include 
#include 
#include 

#include  
#include 

#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Mdl
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

CCompetition::CCompetition( Script::CStruct* pParams )
{
	mp_params = new Script::CStruct();
}

CCompetition::~CCompetition()
{
}
	
void CCompetition::EditParams( Script::CStruct* pParams )
{
	mp_params->AppendStructure( pParams );
}

void CCompetition::ResetJudgement()
{
	m_total_score = 0.0f;
	m_bail_points = 0.0f;
}

int CLeaderBoardEntry::GetTotalScore(int round)
{
	int score1 = 0;
	int score2 = 0;
	
	for (int i=0;i score1)
		{
			score2 = score1;
			score1 = m_score[i];
		}
		else
		if (m_score[i] > score2)
		{
			score2 = m_score[i];
		}
	}
	return score1 + score2;	
}


// start the competition, and pre-calculate all
// the scores
void	   CCompetition::StartCompetition(float bronze, float silver, float gold, float bronze_score, float silver_score, float gold_score, float bail)
{
	// store the defining parameters

	m_bronze = bronze;
	m_silver = silver;
	m_gold = gold;
	m_bronze_score = bronze_score;
	m_silver_score = silver_score;
	m_gold_score = gold_score;
	m_bail = bail;
	
	
	m_end_competition = false;

	// Get the current profile
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CPlayerProfileManager*	pPlayerProfileManager=pSkate->GetPlayerProfileManager();
	Obj::CSkaterProfile* pSkaterProfile=pPlayerProfileManager->GetCurrentProfile();

	// need to use GetDisplayName() wrapper function instead of GetUIString(), bec. ui string can now theoretically be empty
	m_leader_board[0].m_name = pSkaterProfile->GetDisplayName();

	// Let's count how many pros there are, so we can pcik from them at random

	int	possible_pros = pPlayerProfileManager->GetNumProfileTemplates();	

// get an array of pros, except the one that has the same name as me
	int	pro_order[100];
	int num_pros = 0;
	for (int i = 0;iGetProfileTemplateByIndex(i);
		
//		printf ("Pro=%d, Secret = %d, name = %s",pProfile->IsPro(), pProfile->IsSecret(), pProfile->GetDisplayName());
				
		if (!pProfile->IsPro() || pProfile->IsSecret() || Script::GenerateCRC(m_leader_board[0].m_name.getString()) == Script::GenerateCRC(pProfile->GetDisplayName()))
		{
			// skip over this one
		}
		else
		{
			pro_order[num_pros++] = i;
		}
	}
	
	// shuffle them
	for (int i = 0;i= gold)
	{
		silver = gold - 0.1f;
	}
	if (bronze >= silver)
	{
		bronze = silver - 0.1f;
	}
	
	
	// go through all remaining players, and fill in the blanks	
	for (int i = 0;iGetArray( CRCD(0x1cc6f48d,"leader_board_names"), &pLeaderBoardNames, Script::NO_ASSERT ) )
		{
			Dbg_MsgAssert( pLeaderBoardNames->GetSize() == vMAX_PLAYERS - 1, ( "leader_board_names has wrong size.  Needs to be %i", vMAX_PLAYERS - 1 ) );
			m_leader_board[player].m_name = pLeaderBoardNames->GetString( i );
		}
		else
			m_leader_board[player].m_name = pPlayerProfileManager->GetProfileTemplate(pPlayerProfileManager->GetProfileTemplateChecksum(pro_order[i]))->GetDisplayName();
		
		
		// now calculate the score
		float score = 0.0f;
		if (i == 0) score = gold;
		else if (i == 1) score = silver;
		else if (i == 2) score = bronze;
		else
		{
			// scores for the player after bronze are essentially any random number less than bronze
			score = bronze - (bronze/8) + Mth::PlusOrMinus(bronze/8);
			if (score >= bronze)
			{
				score = bronze - 0.1f;
			}			
		}
		// now we have the total score, we calculate the random components that
		// will add up to that score
		// the final sore is calculated as the average of the best of two scores
		// So... calculate two scores that add up to twice the total score
		// then add a third that is less than both of these
		// and put these in random order
		// 
		float score1 = score + Mth::PlusOrMinus(2.0f);
		float score2 = score*2.0f - score1;
		float lower = score1;
		if (score2 GetString( CRCD(0xfaf19012,"first_place_name"), &p_first_place_name, Script::NO_ASSERT ) )
	{
		m_leader_board[0].m_name = p_first_place_name;
	}
	
	printf ("\nSorted\n");	
	// let up now print out the fruits of our efforts
	for (int i = 0;i 99.9f)
		m_total_score = 99.9f;

	// we calculate a "varience", which is the maximum amount
	// a score can be randomly varied
	// this ranges from 10 to 0 as the actual score goes from 0 to 99.9
													   
	float variance = ( 99.9f - m_total_score ) / 10.0f;

	// perform some initial varying by half the variance
	// to make the score seen more random 
	m_total_score += Mth::PlusOrMinus(variance/2);

	// clamp it, just to be safe	
	if (m_total_score > 99.9f)
		m_total_score = 99.9f;
	if (m_total_score < 0.0f)
		m_total_score = 0.0f;
								
	// and find the n Judges scores
	// here the final score is the average of the top three scores.
	// so we need to generate three scores that add up to the 
	// total score
	
	
	m_score[0] = m_total_score + Mth::PlusOrMinus(variance);
	if (m_score[0] > 99.85f)
		m_score[0] = 99.9f;
	if (m_score[0] < 0.0f)
		m_score[0] = 0.0f;	
	m_score[1] = m_total_score + Mth::PlusOrMinus(variance);
	if (m_score[1] > 99.85f)
		m_score[1] = 99.9f;
	if (m_score[1] < 0.0f)
		m_score[1] = 0.0f;
	m_score[2] = (m_total_score * 3.0f) - (m_score[0] + m_score[1]);
	if (m_score[2] < 0.0f)
		m_score[2] = 0.0f;
	if (m_score[2] > 99.85f)
		m_score[2] = 99.9f;
		
		
	m_score[0] = (float)( (int) (m_score[0] * 10.0f)) /10.0f + 0.0001f;
	m_score[1] = (float)( (int) (m_score[1] * 10.0f)) /10.0f + 0.0001f;
	m_score[2] = (float)( (int) (m_score[2] * 10.0f)) /10.0f + 0.0001f;
	
	m_score[0] = (int)Mth::Round(m_score[0]);
	m_score[1] = (int)Mth::Round(m_score[1]);
	m_score[2] = (int)Mth::Round(m_score[2]);

	if ( m_score[0] > 99 )
		m_score[0] = 99;
	if ( m_score[1] > 99 )
		m_score[1] = 99;
	if ( m_score[2] > 99 )
		m_score[2] = 99;
	
	// recalculate the total score
	m_total_score = ((float)(m_score[0]) + (float)(m_score[1]) + (float)(m_score[2])) / 3;
	m_total_score = (int)Mth::Round( m_total_score );
	if ( m_total_score > 99 )
		m_total_score = 99;
	// m_judge_score[m_current_round] = (int)(10.0f * m_total_score + 0.5f);
	m_judge_score[m_current_round] = (int)m_total_score;

	printf ("3 scores are %f,%f,%f, total is %f\n",m_score[0],m_score[1],m_score[2],m_total_score);

	// Stick the total score in the correct place in the leaderboard	
	for (i = 0;iIsInCompetition() )
	{
		// and let us have a look at what we come up with;
		Script::CStruct* p_run_scores = new Script::CStruct();
		// pack the scores in a carray to make them easy to deal with in script
		Script::CArray* p_run_scores_array = new Script::CArray();
		p_run_scores_array->SetSizeAndType( vNUM_JUDGES, ESYMBOLTYPE_STRUCTURE );
		printf( "\nJudgement Day\n\n" );
		for ( i = 0; i < vNUM_JUDGES; i++ )
		{
			// add an integer version of this score to the params, so it's 
			// easy to deal with in script
			Script::CStruct* p_temp = new Script::CStruct();
			p_temp->AddInteger( "score", (int)Mth::Round( m_score[i] ) );

			// add a flag to the top judges
			if ( IsTopJudge( i ) )
			{
				p_temp->AddChecksum( NONAME, CRCD( 0x7b1b959f, "top_judge" ) );
			}

			p_run_scores_array->SetStructure( i, p_temp );
			printf ("%d: %f Top=%d\n",i,m_score[i],IsTopJudge(i));
		}	
		
		p_run_scores->AddArray( "scores", p_run_scores_array );

		// add the total score
		// p_run_scores->AddInteger( "total_score", (int)Mth::Round( ( m_total_score * 10 ) ) );
		p_run_scores->AddInteger( "total_score", (int)Mth::Round( ( m_total_score ) ) );

		Script::CleanUpArray( p_run_scores_array );
		delete p_run_scores_array;
		#ifdef __NOPT_ASSERT__
		Script::CScript *p_script=Script::SpawnScript( "goal_comp_show_run_scores", p_run_scores );
		p_script->SetCommentString("Spawned from CCompetition::EndRun(...)");
		#else
		Script::SpawnScript( "goal_comp_show_run_scores", p_run_scores );
		#endif
		delete p_run_scores;
	}
}

// This function should be called every time the skater bails
void 	   CCompetition::Bail()
{
		m_bail_points += m_bail + Mth::PlusOrMinus(m_bail/2);
}

void	   CCompetition::Sort()
{
	// sort the list, based on total score 

	int round = m_current_round;									  
	// if the competition has ended (maybe early), then total up for all the rounds	
	if (CompetitionEnded())
	{
		round = 2;
	}
							 
	for (int i=0;i m_leader_board[high].GetTotalScore(round))
			{
				high = j;
			}
			// give the player the higher position if there is a tie
			else if ( m_leader_board[j].m_player_number == 0 && m_leader_board[j].GetTotalScore(round) == m_leader_board[high].GetTotalScore(round) )
			{
				high = j;
			}
		}
		if (high != i)
		{
			CLeaderBoardEntry t = m_leader_board[high];
			m_leader_board[high] = m_leader_board[i];
			m_leader_board[i] = t;
		}
	}
}
	
int 	   CCompetition::GetPosition(int player)
{
	return 0;
}

	   
char *		CCompetition::GetJudgeName(int judge)
{
	return m_judge_name[judge];
}

float		CCompetition::GetJudgeScore(int judge)
{
	return m_score[judge];
}

void CCompetition::SetupJudgeText()
{
	Game::CGoalManager* pGoalManager = Game::GetGoalManager();
	Dbg_Assert( pGoalManager );

	if ( pGoalManager->IsInCompetition() )
	{
		Script::CStruct* p_score_params = new Script::CStruct();

		Script::CArray* p_score_array = new Script::CArray();
		p_score_array->SetSizeAndType( vNUM_JUDGES , ESYMBOLTYPE_STRUCTURE );

		printf ("\nSetupJudgeText\n");	
		// let up now print out the fruits of our efforts
		for (int i = 0;iAddString( "name", m_leader_board[i].m_name.getString() );
			printf("m_current_round: %i\n", m_current_round);
			p_leader_board_entry->AddInteger( "score", m_leader_board[i].GetTotalScore(m_current_round) );

			if ( m_leader_board[i].m_player_number == 0 )
			{
				p_leader_board_entry->AddChecksum( NONAME, CRCD( 0x67e6859a, "player" ) );
			}

			p_score_array->SetStructure( i, p_leader_board_entry );
		}

		p_score_params->AddArray( "leader_board", p_score_array );
		Script::CleanUpArray( p_score_array );
		delete p_score_array;
		#ifdef __NOPT_ASSERT__
		Script::CScript *p_script=Script::SpawnScript( "goal_comp_add_leader_board", p_score_params );
		p_script->SetCommentString("Spawned from CCompetition::SetupJudgeText()");
		#else
		Script::SpawnScript( "goal_comp_add_leader_board", p_score_params );
		#endif
		delete p_score_params;
	}

/*//	int i_total = 0;
	for (int i = 0;i=1)
		{
			if (round == 2 && small == 1)
				sprintf(Script::GetScriptString(12 + line * 5 + 2),xpC21f, (float)(m_leader_board[line].m_score[1]/10.0f));
			else
				sprintf(Script::GetScriptString(12 + line * 5 + 2),pC21f, (float)(m_leader_board[line].m_score[1]/10.0f));
		}
		else
			sprintf(Script::GetScriptString(12 + line * 5 + 2), pC,"-");

		if (round >=2)
		{
			if (round == 2 && small == 2)
				sprintf(Script::GetScriptString(12 + line * 5 + 3),xpC21f, (float)(m_leader_board[line].m_score[2]/10.0f));
			else
				sprintf(Script::GetScriptString(12 + line * 5 + 3),pC21f, (float)(m_leader_board[line].m_score[2]/10.0f));
		}
		else
		{
			sprintf(Script::GetScriptString(12 + line * 5 + 3),pC, "-");				
		}
		
		sprintf(Script::GetScriptString(12 + line * 5 + 4),pC21f, (float)(m_leader_board[line].GetTotalScore(round)/10.0f));
		
	}
	
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CPlayerProfileManager*	pPlayerProfileManager=pSkate->GetPlayerProfileManager();
	Obj::CSkaterProfile* pSkaterProfile=pPlayerProfileManager->GetCurrentProfile();
	sprintf (Script::GetScriptString(12 + line *5),	pSkaterProfile->GetDisplayName());
*/	
}

// Return true if this is a top 3 judge
// note, has to handle cases where score is the same
// works by sorting the judges, and then seeing if we are in the top three 
bool CCompetition::IsTopJudge(int judge)
{
	int	judge_order[vNUM_JUDGES];
	
	for (int i=0;i		 									**
**																			**
*****************************************************************************/

#ifndef __SK_MODULES_COMPETITION_H
#define __SK_MODULES_COMPETITION_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Mdl
{

						

	enum 
	{
		vMAX_PLAYERS = 8,
		vNUM_ROUNDS = 3,
		vNUM_JUDGES = 5,
	};


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


class		CLeaderBoardEntry
{

public:
	int 						GetTotalScore(int round);	

	Str::String					m_name;
	int							m_player_number;
	int							m_score[vNUM_ROUNDS]; 	// score now stored in hundreths
	bool						visible;	
};


class  CCompetition  : public Spt::Class
{
	
	
public:	
	CCompetition( Script::CStruct* pParams = NULL );
	~CCompetition();
	
	void						StartCompetition(float bronze = 90.0f, float silver = 93.0f, float gold = 95.0f, float bonze_score=20000, float silver_score=40000,float gold_score = 60000,float bail = 1.0f);
	void						StartRun();
	void 						EndRun(int score);
	void 						Bail();
	void						Sort();
	
	int 						GetPosition(int player = 0);
	char *						GetJudgeName(int judge);
	float						GetJudgeScore(int judge);
	void						SetupJudgeText();
	bool 						IsTopJudge(int judge);
	bool 						PlaceIs(int place);
	bool 						RoundIs(int place);
	void 						EndCompetition();
	bool 						CompetitionEnded();

	void						EditParams( Script::CStruct* pParams );

private:
	void						ResetJudgement();
	
	// BB - i'm lazy, so I'm just adding a struct to hold various
	// params that i can use to change the behavior in the code
	Script::CStruct*			mp_params;
	
	int							m_current_round;

	CLeaderBoardEntry			m_leader_board[vMAX_PLAYERS];

	float 						m_bronze;		 	// score required to get bronze
	float						m_silver;
	float 						m_gold;
	float 						m_bronze_score;		// points required to get that score
	float 						m_silver_score;
	float 						m_gold_score;
	float 						m_bail;			    // points deducted for bailing	

	float						m_bail_points;			   	
	float						m_score[vNUM_JUDGES];  	// m_score is generated from m_total_score
	char *						m_judge_name[vNUM_JUDGES];  	//
	float						m_total_score;

	int							m_judge_score[vNUM_ROUNDS];

	bool						m_end_competition;
	
	int	m_place;

};

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace 

#endif		// __SK_MODULES_COMPETITION_H





================================================
FILE: Code/Sk/Modules/Skate/horse.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate3													**
**																			**
**	Module:			skate			 										**
**																			**
**	File name:		horse.cpp												**
**																			**
**	Created by:		07/17/01	-	Gary									**
**																			**
**	Description:			 									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 						 // for Mth::Rnd()
#include 

#include 
#include 
#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Mdl
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static uint32 s_horse_status[] = {
   0x0fd80eba,	// GotLetter
   0x006be86f,	// TieScore
   0xbaa12466,	// BeatScore
   0xae7d29b0,	// Ended
   0x23db4aea,	// Idle
   0xa71dd8eb,	// NoScoreSet
   0			// Terminator
};

// should match the above
enum
{
	vGOTLETTER = 0,
	vTIESCORE,
	vBEATSCORE,
	vENDED,
	vIDLE,
	vNOSCORESET,
};

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static uint32 get_num_horse_restarts()
{
	uint32 dummy_nodes[128];
	return Obj::FindQualifyingNodes( Script::GenerateCRC("horse"), &dummy_nodes[0] );
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CHorse::CHorse()
{
	strcpy( m_string, "HORSE" );

	m_currentRestartIndex = 0;
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CHorse::Init()
{
	// reset the score to beat
	m_scoreToBeat = 0;

	// setting the "current horse player"
	m_currentSkaterId = 0;
	m_nextSkaterId = 0;
	sprintf( m_nextHorseMessage, "unknown" );

	// resetting each of their accumulated horse words
	for ( int i = 0; i < Skate::vMAX_SKATERS; i++ )
	{
		m_numLetters[i] = 0;
	}

	// select a random restart point
	uint32 num_restarts = get_num_horse_restarts();
	//Dbg_MsgAssert ( num_restarts > 0, ( "Couldn't find any horse restarts" ) );
	Dbg_Printf( "-- COULDN'T FIND ANY HORSE RESTARTS --\n" );
	m_currentRestartIndex = Mth::Rnd( num_restarts );

	// maybe do some kind of handicapping?
	// or separate words for each player?
	
	// their panels need to be suspended, but that's
	// done in the panel manager code.
	// TODO:  move it to some accessor in the panel stuff
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CHorse::GetCurrentSkaterId()
{
	return m_currentSkaterId;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CHorse::StartRun()
{
	
	
	//Front::MenuFactory* menu_factory = Front::MenuFactory::Instance();

	//m_currentSkaterId = m_nextSkaterId;

	m_status = s_horse_status[vIDLE];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CHorse::EndRun()
{
	// calculate the score post-run
	// update the horse data structure so that we know that
	// the score to beat is

	sprintf( m_nextHorseMessage, "unknown" );
	
	// by default, don't change the skater
	m_nextSkaterId = m_currentSkaterId;

	int score = get_score( m_currentSkaterId );

	printf( "End run = %d\n", score );

	if ( score > m_scoreToBeat )
	{
		// current player wins the run
		m_scoreToBeat = score;

		sprintf( m_nextHorseMessage, "%s %s", Script::GetLocalString( "horse_str_setscoreis" ), Str::PrintThousands(m_scoreToBeat) );
		m_status = s_horse_status[vBEATSCORE];
	}
	else if ( score < m_scoreToBeat )
	{
		// current player loses the run
		m_scoreToBeat = 0;
		
		m_numLetters[ m_currentSkaterId ]++;

		// skip to next restart point
		SetNextRestartPoint();
		
		if ( Ended() )
		{
			// GJ:  this string is never used...  it says "Player 1 is a horse" somewhere else
			sprintf( m_nextHorseMessage, "You are a %s!", m_string );

			m_status = s_horse_status[vENDED];
		}
		else
		{
			char letter[2];
			letter[0] = *(m_string + m_numLetters[m_currentSkaterId] - 1);
			// make it uppercase
			if ( letter[0] >= 'a' && letter[0] <= 'z' )
			{
				letter[0] -= 'a';
				letter[0] += 'A';
			}
			letter[1] = 0;
			sprintf( m_nextHorseMessage, "%s %s", Script::GetLocalString( "horse_str_getstheletter" ), letter );
			m_status = s_horse_status[vGOTLETTER];
		}
	}
	else
	{
		// current player draws
		if ( score == 0 )
		{
			strcpy( m_nextHorseMessage, Script::GetLocalString( "horse_str_noscoreset" ));
			m_status = s_horse_status[vNOSCORESET];
		}
		else
		{
			strcpy( m_nextHorseMessage, Script::GetLocalString( "horse_str_youtiedthetargetscore" ));
			m_status = s_horse_status[vTIESCORE];
		}
	}

#ifdef __USER_GARY__
	printf( "Current Score:  %s - %s\n",
			GetWordForPlayer( 0 ).getString(),
			GetWordForPlayer( 1 ).getString() );
#endif
	
	//SwitchPlayers();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CHorse::SwitchPlayers()
{
	m_nextSkaterId = get_next_valid_skater_id();
	m_currentSkaterId = m_nextSkaterId;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CHorse::StatusEquals( Script::CStruct* pParams )
{
	int i = 0;
	while ( s_horse_status[i] )
	{
		if ( pParams->ContainsFlag( s_horse_status[i] ) )
		{
			return ( m_status == s_horse_status[i] );
		}
		
		i++;
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CHorse::Ended()
{
	for ( int i = 0; i < Skate::vMAX_SKATERS; i++ )
	{
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		if ( !skate_mod->GetSkaterById( i ) )
			continue;

		// if the skater exists...
		if ( m_numLetters[i] >= strlen( m_string ) )
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CHorse::SetWord( Str::String word )
{
	// validate string
	// make sure there's at least 1 letter

	Dbg_Assert( word.getString() && strlen( word.getString() ) > 0 );

	strcpy( m_string, word.getString());

	printf( "New word = %s\n", m_string );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Str::String CHorse::GetWordForPlayer( int skater_id )
{
	

	Dbg_Assert( skater_id >= 0 && skater_id < Skate::vMAX_SKATERS );
	
	char msg[256];
	
	strcpy( msg, m_string );
	for ( uint32 i = 0; i < strlen(msg); i++ )
	{
		if ( i >= m_numLetters[ skater_id ] )
			msg[i] = '-';
	}

	return msg;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char* CHorse::GetWord()
{
	return m_string;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CHorse::get_next_valid_skater_id()
{
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	Obj::CSkater *pSkater = NULL;
	int check_id = 0;

	for ( int i = 0; i < Skate::vMAX_SKATERS; i++ )
	{
		check_id = ( m_currentSkaterId + i + 1 ) % Skate::vMAX_SKATERS;
		pSkater = skate_mod->GetSkaterById( check_id );
		if ( pSkater )
			break;
	}
	
	Dbg_Assert( pSkater );

	printf( "new skater id = %d\n", check_id );

	if ( Script::GetInteger( "debug_horse" ) )
	{
		// DEBUGGING
		check_id = 0;
	}
	
	return check_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CHorse::get_score( int skater_id )
{
	

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater *pSkater = skate_mod->GetSkaterById( skater_id );
	Dbg_Assert( pSkater );

	Mdl::Score * pScore = ( pSkater->GetScoreObject() );
	Dbg_Assert( pScore );

	return pScore->GetTotalScore();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Str::String	CHorse::GetString( uint32 checksum )
{
	
	
	char msg[256];
	strcpy( msg, "Unknown" );

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater *pSkater = skate_mod->GetSkaterById( m_currentSkaterId );
	Dbg_Assert( pSkater );

	switch ( checksum )
	{
		case 0x9196d920: // playerName
		{
//			int currentPlayerNumber = pSkater->GetID() + 1;
//			sprintf( msg, "Player %d", currentPlayerNumber );
			sprintf( msg, "%s", skate_mod->GetSkaterById( pSkater->GetID() )->GetDisplayName() );
		}
		break;
		
		case 0x5078bc7c: // horsePreRun
		{
			if ( m_scoreToBeat > 0 )
			{
				sprintf( msg, "%s %s", Script::GetLocalString( "horse_str_scoretobeat" ), Str::PrintThousands(m_scoreToBeat) );
			}
			else
			{
				sprintf( msg, Script::GetLocalString( "horse_str_setscore" ) );
			}
		}
		break;
		
		case 0x2a13f850: // horsePostRun
		{
			strcpy( msg, m_nextHorseMessage );
		}
		break;

		case 0xde802177: // panelString1
		{
			sprintf( msg, "%s: %s", skate_mod->GetSkaterById( 0 )->GetDisplayName(), GetWordForPlayer( 0 ).getString() );
		}
		break;

		case 0x478970cd: // panelString2
		{
			sprintf( msg, "%s: %s", skate_mod->GetSkaterById( 1 )->GetDisplayName(), GetWordForPlayer( 1 ).getString() );
		}
		break;

		case 0x2523853d: // youAreA
		{
			bool starts_with_vowel = false;
			char first_letter = GetWord()[0];
			if ( first_letter >= 'a' && first_letter <= 'z' )
			{
				// make them upper case
				first_letter -= 'a';
				first_letter += 'A';
			}
			if ( first_letter == 'A' || first_letter == 'E' || first_letter == 'I'
				 || first_letter == 'O' || first_letter == 'U' )
			{
				starts_with_vowel = true;
			}

			sprintf( msg, "%s %s", GetString( Script::GenerateCRC("playerName") ).getString(), starts_with_vowel ?  Script::GetLocalString( "horse_str_isan" ) :  Script::GetLocalString( "horse_str_isa" ) );
		}
		break;

		case 0x5f7d73e9: // finalWord
		{
			sprintf( msg, "%s", GetWord() );
		}
		break;
	}
	
	return msg;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CHorse::GetCurrentRestartIndex( void )
{
	

	uint32 num_restarts = get_num_horse_restarts();
	if ( m_currentRestartIndex >= num_restarts )
	{
		//Dbg_MsgAssert( 0, ( "Out of range horse restart index %d %d", m_currentRestartIndex, num_restarts ) );
	}

	return m_currentRestartIndex;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CHorse::SetNextRestartPoint( void )
{
	m_currentRestartIndex++;

	if ( m_currentRestartIndex >= get_num_horse_restarts() )
	{
		m_currentRestartIndex = 0;
	}

#ifdef __USER_GARY__
	printf( "\n*** Setting next restart point %d ***\n", m_currentRestartIndex );
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mdl






================================================
FILE: Code/Sk/Modules/Skate/horse.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate3  												**
**																			**
**	Module:			sk						 								**
**																			**
**	File name:		horse.h													**
**																			**
**	Created by:		7/17/01 - Gary											**
**																			**
**	Description:			 									**
**																			**
*****************************************************************************/

#ifndef __SK_MODULES_HORSE_H
#define __SK_MODULES_HORSE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Script
{
    class CStruct;
};
                                
namespace Mdl
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

class  CHorse  : public Spt::Class
{
	

	enum
	{
		vMAX_RESTARTS = 32
	};
			
public:
	CHorse();

public:
	void						Init();
	void						SwitchPlayers();
	void						StartRun();
	void 						EndRun();
	bool						Ended();
	char*						GetWord();
	void						SetWord( Str::String word );
	Str::String					GetWordForPlayer( int skater_id );
	int							GetCurrentSkaterId();
	Str::String					GetString( uint32 checksum );
	uint32						GetCurrentRestartIndex();
	void						SetNextRestartPoint(void);
	bool						StatusEquals( Script::CStruct* pParams );

protected:
	int							get_next_valid_skater_id();
	int							get_score( int id );
	
protected:
	int							m_scoreToBeat;
	int							m_currentSkaterId;
	int							m_nextSkaterId;
	char						m_string[512];
	char						m_nextHorseMessage[512];
	uint32						m_numLetters[Skate::vMAX_SKATERS];
	uint32						m_numRestarts;
	uint32						m_currentRestartIndex;
	uint32						m_status;
};

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mdl

#endif		// __SK_MODULES_HORSE_H





================================================
FILE: Code/Sk/Modules/Skate/score.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate3													**
**																			**
**	Module:									 								**
**																			**
**	File name:																**
**																			**
**	Created by:		rjm														**
**																			**
**	Description:						 									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 

#include 		   // for control and trick stuff
#include 
#include 

#include 
#include 

#include 
#include 
#include 

#include 

#include 

namespace Front
{
	extern void SetScoreTHPS4(char* score_text, int skater_num);
}

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Mdl
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define NO_SCORE_DURING_UBER_FRIG
#define MAX_SCORE_DUE_TO_TWEAK (20000)
	
/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static const int MAX_SPIN_VALUES					= 6;
static const int SPIN_MULT_VALUES[MAX_SPIN_VALUES]	= {2, 3, 4, 5, 6, 7};
static const int MAX_DEPREC							= 5;
static const int DEPREC_VALUES[MAX_DEPREC]			= {100, 75, 50, 25, 10};


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

// given a score increment, return a value by which it should be decremented
// basically return the closest multiple of 10 below score
int	score_increment(int score)
{
	if (score >1000000) return 1000000;
	if (score >100000) return 100000;
	if (score >10000) return 10000;
	if (score >10000) return 1000;
	if (score >1000) return 100;
	if (score >100) return 100;
	if (score >10) return 10;
	return 1;	
}




Score::Score() :
	m_historyTab(16),
	m_infoTab(8)
{
	m_skaterId = 0;
	m_totalScore = 0;
	m_scorePot = 0;
	m_recentScorePot = 0;
	m_recentSpecialScorePot = 0;
	m_currentTrick = 0;
	m_currentMult = 0;
	m_currentBlockingTrick = -1;
	m_currentSpinTrick = -1;

	m_specialScore = 0;
	set_special_is_active(false);

	m_special_interpolator = 0.0f;

	m_longestCombo = 0;
	m_bestCombo = 0;
	m_bestGameCombo = 0;
	
	//mp_trickWindow = NULL;

	m_debug = (bool) Script::GetInteger("print_trick_info");

	setup_balance_meter_stuff();
}




Score::~Score()
{
	Reset();
}

int Score::get_packed_score( int start, int end )
{
	int packed_score = 0;
	int current_spin_mult = 2;		// n over 2
	for (int i = start; i < end; i++)
	{
		// if a blocking trick, use default spin multiplier
		if (m_infoTab[i].flags & vBLOCKING)
		{
			current_spin_mult = 2;
		}
		// if not a blocking trick, but the last trick was, then use the spin multiplier here
		// to apply to subsequent tricks up to next blocking one
		else if (i == 0 || (m_infoTab[i-1].flags & vBLOCKING))
		{
			current_spin_mult = spinMult(m_infoTab[i].spin_mult_index);
		}

		int deprec_mult = deprecMult(m_infoTab[i].mult_index);
		
//		if (m_debug)
//			printf("   base score %d, reduction mult %.2f, spin mult %.1f\n", 
//				   m_infoTab[i].score, (float) deprec_mult / 100, (float) current_spin_mult / 2);
		
		int score = (m_infoTab[i].score * deprec_mult * current_spin_mult) / 200;
		if (m_infoTab[i].switch_mode)
		{
			score	= score * 120 / 100;
		}
		
		packed_score += score;
	}
	return packed_score;
}

void Score::pack_trick_info_table()
{
	TrickInfo theTrickInfo;
	strcpy( theTrickInfo.trickNameText, "... + " );
	strcpy( theTrickInfo.trickTextFormatted, "...\\_+\\_" );
	theTrickInfo.score = 0;
	theTrickInfo.id = 0;
	theTrickInfo.switch_mode = 0;
	theTrickInfo.flags = 0;
	theTrickInfo.mult_index = 0;
	theTrickInfo.spin_mult_index = 0;
	theTrickInfo.spin_degrees = 0;

	int firstBlockingTrick = m_infoTab.GetSize();

	for ( int i = 0; i < m_infoTab.GetSize(); i++ )
	{
//		if ( ( i != 0 ) && ( m_infoTab[i].flags & vBLOCKING ) )
		if ( ( i != 0 ) && ( m_infoTab[i].trickNameText[0] == 0 ) )
		{
			firstBlockingTrick = i;
			break;
		}
	}
	
	// GJ:  see note below
	if ( ( firstBlockingTrick > m_currentSpinTrick )
		 || ( firstBlockingTrick > m_currentBlockingTrick ) )
	{
		return;
	}

	theTrickInfo.score = get_packed_score( 0, firstBlockingTrick );

	// shift them all...
	for ( int i = 0; i < firstBlockingTrick; i++ )
	{
		m_infoTab.Remove( 1 );

		m_currentTrick--;
		m_currentSpinTrick--;
		m_currentBlockingTrick--;

		// GJ:  these asserts will fire off if the trick list is filled
		// with more non-blocking tricks than spin tricks...	 it rarely
		// happens, except for those glitches that lets the player do
		// infinite tricks.  The if-test above will abort early,
		// allowing the calling function ( Trigger() ) to gracefully
		// handle it
		Dbg_MsgAssert( m_currentTrick > -1, ( "m_currentTrick is negative (%d)", m_currentTrick ) );
		Dbg_MsgAssert( m_currentSpinTrick > -1, ( "m_currentSpinTrick is negative (%d)", m_currentSpinTrick ) );
		Dbg_MsgAssert( m_currentBlockingTrick > -1, ( "m_currentBlockingTrick is negative (%d)", m_currentBlockingTrick ) );
	}
	
	m_infoTab[0] = theTrickInfo;
	
	// so that the packed score will be correct next time
	m_infoTab[0].flags |= vBLOCKING;

#ifdef __USER_GARY__
	Dbg_Message( "Info table has %d elements.", m_infoTab.GetSize() );
#endif
}

void Score::print_trick_info_table()
{
	for ( int i = 0; i < m_infoTab.GetSize(); i++ )
	{
		printf( "info table %d\n", i );
		printf( "\ttrickname = %s\n", m_infoTab[i].trickNameText); 
		printf( "\ttrickformatted = %s\n", m_infoTab[i].trickTextFormatted); 
		printf( "\tscore = %d\n", m_infoTab[i].score); 
		printf( "\tmult_index = %d\n", m_infoTab[i].mult_index); 
		printf( "\tspin_mult_index = %d\n", m_infoTab[i].spin_mult_index); 
		printf( "\tswitch_mode = %d\n", m_infoTab[i].switch_mode);
/*
		m_infoTab[i].id,
		m_infoTab[i].switch_mode,
		m_infoTab[i].flags,
*/
		printf( "-------------------------------------\n" );
	}
}

void Score::Update()
{
	if (m_specialScore)
	{
		// shrink special bar
		if (m_specialIsActive)
			m_specialScore = (int) ((float) m_specialScore - (float) Tmr::FrameLength() * 200.0f / Tmr::vRESOLUTION);			
		else
			m_specialScore = (int) ((float) m_specialScore - (float) Tmr::FrameLength() * 50.0f / Tmr::vRESOLUTION);			
		if (m_specialScore <= 0)
		{
			set_special_is_active(false);
			m_specialScore = 0;
		}
	}

	if (m_scorePotState == WAITING_TO_COUNT_SCORE_POT)
	{
		m_scorePotCountdown--;
		if (!m_scorePotCountdown)
		{
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );
			int index;

			index = 0;
			if( pSkater )
			{
				index = pSkater->GetHeapIndex();
			}
			TrickTextCountdown(index);
			Replay::WriteTrickTextCountdown();

			m_scorePotState = SHOW_COUNTED_SCORE_POT;
		}
	}
	else if (m_scorePotState == SHOW_COUNTED_SCORE_POT)
	{
		dispatch_score_pot_value_to_screen(m_countedScorePot, 0);
		m_countedScorePot -= score_increment(m_countedScorePot);
		if (m_countedScorePot < 0) 
		{
			m_countedScorePot = 0;
			m_scorePotState = SHOW_ACTUAL_SCORE_POT;
		}
	}
	
	#if 0
	Image::RGBA						m_special_rgba[3];
	float							m_special_interpolator;
	float							m_special_interpolator_rate;
	#endif
	
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Front::CScreenElementPtr p_special_bar;
	
	Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );
	if( pSkater )
	{
		p_special_bar = p_manager->GetElement(CRCD(0xfe1dc544,"the_special_bar_sprite") + pSkater->GetHeapIndex());
	}
	else
	{
		p_special_bar = p_manager->GetElement(CRCD(0xfe1dc544,"the_special_bar_sprite"));
	}
	// clamp scale of bar, so that it doesn't get all crazy
	float special_mult = m_specialScore / 3000.0f;
	if (special_mult > 1.0f)
		special_mult = 1.0f;
	else if (special_mult < 0.0f)
		special_mult = 0.0f;
	p_special_bar->SetScale(special_mult, p_special_bar->GetScaleY());
	Image::RGBA special_rgba = m_special_rgba[REGULAR_SPECIAL];
	if (m_specialIsActive)
	{
		float interpolate_mult = (cosf(m_special_interpolator) + 1.0f) / 2.0f;
		m_special_interpolator += m_special_interpolator_rate;
		
		special_rgba.r = (uint8) ((float) m_special_rgba[1].r + ((float) (m_special_rgba[2].r - m_special_rgba[1].r)) * interpolate_mult);
		special_rgba.g = (uint8) ((float) m_special_rgba[1].g + ((float) (m_special_rgba[2].g - m_special_rgba[1].g)) * interpolate_mult);
		special_rgba.b = (uint8) ((float) m_special_rgba[1].b + ((float) (m_special_rgba[2].b - m_special_rgba[1].b)) * interpolate_mult);
		special_rgba.a = (uint8) ((float) m_special_rgba[1].a + ((float) (m_special_rgba[2].a - m_special_rgba[1].a)) * interpolate_mult);
	}
	p_special_bar->SetRGBA(special_rgba);

	/*
	HUD::PanelMgr* panel_mgr = HUD::PanelMgr::Instance();
	HUD::Panel* panel;

	if(( panel = panel_mgr->GetPanelBySkaterId(m_skaterId, false)))
	{
		panel->SetSpecialPercent((float) m_specialScore / 3000.0f, m_specialIsActive);
	}
	*/
}




/*
	Called from CSkater constructor
*/
void Score::SetSkaterId(short skater_id) 
{
	m_skaterId = skater_id;
	//HUD::PanelMgr* panel_mgr = HUD::PanelMgr::Instance();
	//mp_trickWindow = panel_mgr->GetTrickWindow(m_skaterId);
}




void Score::copy_trick_name_with_special_spaces(char *pOut, const char *pIn)
{
	int count = 0;
	while(*pIn != '\0')
	{
		Dbg_Assert(count < TrickInfo::TEXT_BUFFER_SIZE);
		if (*pIn == ' ')
		{
			*pOut++ = '\\';
			*pOut++ = '_';
			count += 2;
		}
		else
		{
			*pOut++ = *pIn;
			count++;
		}
		pIn++;
	}
	*pOut = '\0';
}

/*
	Added by Ken. Returns the Id of the last trick added, or 0 if there are none.
	Added for use by the skater Display commmand when the AddSpin flag is specified.
	In that case, if the skater's current trick is the same as the last one in the score object,
	then instead of adding the same trick again it will add spin to the last one instead.
	Used by some flatland tricks.
	(See skater.cpp for the above code, search for | Display |)
*/
uint32 Score::GetLastTrickId()
{
	if (m_infoTab.GetSize())
	{
		return m_infoTab.Last().id;
	}
	return 0;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32 Score::GetTrickId( int trickIndex )
{
	Dbg_MsgAssert( trickIndex >= 0 && trickIndex < m_currentTrick, ( "trickIndex out of range" ) );
	return m_infoTab[trickIndex].id;
}

void Score::TrickTextPulse(int index)
{
	Script::CStruct* pParams=new Script::CStruct;
	pParams->AddChecksum( CRCD(0x33124d2e,"trick_text_container_id"), CRCD(0x1e55886e,"trick_text_container")  + index);
	pParams->AddChecksum( CRCD(0x6e7c7ba7,"the_trick_text_id"), CRCD(0x44727dae,"the_trick_text")  + index);
	pParams->AddChecksum( CRCD(0x6d02989c,"the_score_pot_text_id"), CRCD(0xf4d3a70e,"the_score_pot_text") + index);
	Script::RunScript(CRCD(0x3bd51fe5,"trick_text_pulse"), pParams);
	delete pParams;
}

void Score::TrickTextCountdown(int index)
{
	Script::CStruct* pParams=new Script::CStruct;
	pParams->AddChecksum( CRCD(0x33124d2e,"trick_text_container_id"), CRCD(0x1e55886e,"trick_text_container")  + index );
	pParams->AddChecksum( CRCD(0x6e7c7ba7,"the_trick_text_id"), ( CRCD(0x44727dae,"the_trick_text") ) + index );
	pParams->AddChecksum( CRCD(0x6d02989c,"the_score_pot_text_id"), ( CRCD(0xf4d3a70e,"the_score_pot_text") ) + index );
	Script::RunScript(CRCD(0xee9ce723,"trick_text_countdown"), pParams );
	delete pParams;
}

void Score::TrickTextLanded(int index)
{
	Script::CStruct* pParams=new Script::CStruct;
	pParams->AddChecksum( CRCD(0x33124d2e,"trick_text_container_id"), ( CRCD(0x1e55886e,"trick_text_container") ) + index );
	pParams->AddChecksum( CRCD(0x6e7c7ba7,"the_trick_text_id"), ( CRCD(0x44727dae,"the_trick_text") ) + index );
	pParams->AddChecksum( CRCD(0x6d02989c,"the_score_pot_text_id"), ( CRCD(0xf4d3a70e,"the_score_pot_text") ) + index );
	Script::RunScript(CRCD(0xc890e875,"trick_text_landed"), pParams);
	delete pParams;
}

void Score::TrickTextBail(int index)
{
	Script::CStruct* pParams=new Script::CStruct;
	pParams->AddChecksum( CRCD(0x33124d2e,"trick_text_container_id"), ( CRCD(0x1e55886e,"trick_text_container") ) + index );
	pParams->AddChecksum( CRCD(0x6e7c7ba7,"the_trick_text_id"), ( CRCD(0x44727dae,"the_trick_text") ) + index );
	pParams->AddChecksum( CRCD(0x6d02989c,"the_score_pot_text_id"), ( CRCD(0xf4d3a70e,"the_score_pot_text") ) + index );
	Script::RunScript(CRCD(0xaa7f404d,"trick_text_bail"), pParams );
	delete pParams;
}

/*
	Called when a trick is done, adding it to the trick sequence.
*/
const int TRICK_LIMIT = 250;
void Score::Trigger(char *trick_name, int base_score, Flags flags)
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	
	bool null_trick=false;
	if (!trick_name[0])
	{
		null_trick=true;
	}
		
	// once some trick/score has appeared on-screen
	// we know that the trick has started
	Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );
	Dbg_Assert( pSkater );
	Obj::CTrickComponent* pTrickComponent = GetTrickComponentFromObject(pSkater);
	Dbg_Assert( pTrickComponent );
    Obj::CStatsManagerComponent* pStatsManagerComponent = GetStatsManagerComponentFromObject(pSkater);
	Dbg_Assert( pStatsManagerComponent );
	
	// Only flag a trick as having started if a real trick is being triggered
	if( trick_name && trick_name[0] )
	{
		pTrickComponent->SetFirstTrickStarted( true );
	}
	
    bool is_switch = false; // need to make this grab the switch flag eventually!!!
    pStatsManagerComponent->SetTrick( trick_name, base_score, is_switch );

	// We place a high limit on tricks, so if they 
	// cheat with perfect baclance, and keep tricking
	// then they will not run out of memory and crash
	if (m_currentTrick > TRICK_LIMIT-1)
	{
		pack_trick_info_table();

		if (m_currentTrick > TRICK_LIMIT-1)
		{
			// GJ:  You should never be able to hit the trick limit any more

			if ( !null_trick )
			{
				printf ("Trick Limit (%d) Reached\n",TRICK_LIMIT);
				//tweak_last_valid_trick(base_score);
				m_infoTab.Last().score 	+= base_score;  // give them the score, just stop recording tricks
				m_currentMult++;						// but still keep up the multipler 
				captureScore();
			}
			return;
		}
	}
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
	
	// trick type is based on id + switch mode
	uint32 id = Script::GenerateCRC(trick_name);
	uint32 switch_mode = (flags & (vSWITCH | vNOLLIE)) >> 1;
		
	if (m_infoTab.GetSize() < TRICK_LIMIT)
	{
		m_infoTab.Add(new TrickInfo);
		m_infoTab.Last().score 				= 0;
	}
	// if we have filled up the trick limit, then we just use the last one we added
	
	m_infoTab.Last().score 				+= base_score;
	//printf("triggering trick with id %s\n",trick_name);
	m_infoTab.Last().id 				= id;
	m_infoTab.Last().switch_mode		= switch_mode;
	m_infoTab.Last().flags 				= flags;
	m_infoTab.Last().spin_mult_index 	= 0;
	m_infoTab.Last().trickNameText[0]	= 0;
	m_infoTab.Last().trickTextFormatted[0]	= 0;
	m_infoTab.Last().spin_degrees		= 0;
	
	if (!null_trick)
	{
		copy_trick_name_with_special_spaces(m_infoTab.Last().trickNameText, trick_name);
		
		if ( flags & Score::vCAT )
		{
			sprintf(m_infoTab.Last().trickTextFormatted, "\\c3%s\\c0 ", m_infoTab.Last().trickNameText);
		}
		else if ( flags & Score::vSPECIAL )
		{
			// K: If the 'special' flag is set, use color 2
			sprintf(m_infoTab.Last().trickTextFormatted, "\\c2%s\\c0 ", m_infoTab.Last().trickNameText);
		}
		else
		{
			sprintf(m_infoTab.Last().trickTextFormatted, "%s ", m_infoTab.Last().trickNameText);
		}
		
		// toss the "+" onto the end of the previous trick
		
		// K: This bit of code used to do a sprintf, but I changed it to use a strcat instead
		// to preserve any color formatting added previously, such as the special-trick color
		// added above.
		
		// This is a loop so that null tricks (tricks with no name) get skipped over.
		int index=m_infoTab.GetSize() - 2;
		while (true)
		{
			if (index<0)
			{
				break;
			}
				
			char *p_trick_text_formatted=m_infoTab[index].trickTextFormatted;
			Dbg_MsgAssert(p_trick_text_formatted,("NULL p_trick_text_formatted"));
			
			int len=strlen(p_trick_text_formatted);
			if (*p_trick_text_formatted == '.')
			{
				// skip items that begin with "..."
				// (or else you get things like
				// "... +  + blah blah")
			}
			else if (len)
			{
				// Remove any trailing space character		
				if (p_trick_text_formatted[len-1]==' ')
				{
					p_trick_text_formatted[len-1]=0;
				}	
				
				// Wack on the +
				strcat(p_trick_text_formatted,"\\_+ ");
				break;
			}
			// Keep searching backwards until a named trick is found.
			--index;
		}
	}
			
	// if the last trick was a blocking trick, make this trick the spin trick
	// (or if this is the first trick)
	if (m_currentBlockingTrick == m_currentTrick - 1)
		m_currentSpinTrick = m_currentTrick;
	if (flags & vBLOCKING)
	{
		m_currentBlockingTrick = m_currentTrick;
	}

	// set depreciation stuff
	//Game::CGameMode* pGameMode = skate_mod->GetGameMode();
	if (!(flags & vGAP) && !(flags & vNODEGRADE) /*&& pGameMode->ShouldDegradeScore()*/)
	{
		// trick is not a gap, so figure out what its depreciation index will be
		TrickHistory *pHistory = m_historyTab.GetItem(id);
		if (!pHistory)
		{
			// if no history for this trick, create one
			pHistory = new TrickHistory;
			m_historyTab.PutItem(id, pHistory);
			for (int s = 0; s < 4; s++)
			{
				pHistory->total_count[s] = 0;
				pHistory->combo_count[s] = 0;
			}
		}
		m_infoTab.Last().mult_index = pHistory->total_count[switch_mode] + (pHistory->combo_count[switch_mode]++);
	}
	else
	{
		// gaps don't depreciate
		// no depreciation used in free skate
		m_infoTab.Last().mult_index = 0;
	}
	
	m_currentTrick++;
	if (!null_trick)
	{
		#ifdef NO_SCORE_DURING_UBER_FRIG
		int previousMult = m_currentMult;
		#endif
		
		m_currentMult++;
		if (m_currentMult == 1)
		{
			Obj::CSkaterRunTimerComponent* pSkaterRunTimerComponent = GetSkaterRunTimerComponentFromObject(pSkater);
			Dbg_Assert(pSkaterRunTimerComponent);
			pSkaterRunTimerComponent->ComboStarted();
			
			pSkater->BroadcastEvent(CRCD(0x670fda8c, "SkaterEnterCombo"));
		}

		#if 1	
		// (Mick) check if this is the last of a long line of non-blocking tricks 
		// and if so, then decrement the multiplier (leaving it unchanged)
		int non_block_count = 0;   
		for (int i = m_currentTrick-1; i >0; i--)
		{
			// if a blocking trick, use default spin multiplier
			if (m_infoTab[i].flags & vBLOCKING)
			{
				break;
			}
			non_block_count++;
		}	
//		printf ("%3d: ",non_block_count);
		if (non_block_count > 10)
		{
			#ifdef	__NOPT_ASSERT__
			printf ("CHEAT PREVENTION:  Limiting non blocking combo to 10\n");
			#endif
			m_infoTab.Last().score = 0;
			m_currentMult--;
		}
		#endif
		
		#ifdef NO_SCORE_DURING_UBER_FRIG
		Obj::CSkaterAdjustPhysicsComponent* pSkaterAdjustPhysicsComponent = GetSkaterAdjustPhysicsComponentFromObject(pSkater);
		if (pSkaterAdjustPhysicsComponent && pSkaterAdjustPhysicsComponent->UberFriggedThisFrame())
		{
			m_infoTab.Last().score = 0;
			m_currentMult = previousMult;
		}
		#endif
	}
	
	if (m_debug)
	{
		printf("Adding trick %s\n", trick_name);
	}
	
	captureScore();

	dispatch_trick_sequence_to_screen();

	TrickTextPulse(pSkater->GetHeapIndex());
	Replay::WriteTrickTextPulse();	
		
	m_scorePotState = SHOW_ACTUAL_SCORE_POT;
	dispatch_score_pot_value_to_screen(m_scorePot, m_currentMult);
	
	/*
	if (mp_trickWindow)
	{
		Dbg_MsgAssert(mp_trickWindow,( "no trick window defined"));
	
		HUD::TrickWindow::TrickType trick_type = HUD::TrickWindow::vREGULAR;
		if (flags & vGAP) trick_type = HUD::TrickWindow::vGAP;	
		if (flags & vSPECIAL) trick_type = HUD::TrickWindow::vSPECIAL;	
		
		mp_trickWindow->AddTrick(trick_name, m_currentTrick, trick_type);
	}
	*/
	Mem::Manager::sHandle().PopContext();
}




/* 
	Called when spin changes. (From CSkater::HandleAirRotation()). 
*/
void Score::UpdateSpin(int spin_degrees)
{   
	// if score pot was reset, m_currentSpinTrick is no longer valid
	if (m_currentTrick == 0 || m_currentSpinTrick < 0)
		return;
	
	// If a trick is blocking, then we don't want to add any
	// more spin to it after the initial spin		
	if (m_infoTab[m_currentSpinTrick].flags & vBLOCKING)
	{
		// printf ("Trick %d is blocing, %d degs\n",m_currentSpinTrick,spin_degrees);
		return;
	}
	
	SetSpin(spin_degrees);
}

/* 
	Called right after call to Trigger(). Current spin trick might be trick just added. Also 
	called by Update() (above).
	
	Updates the spin multiplier on the current spin trick. If the skater has increased his
	spin over the highest spin last recorded (increments of 180), then save the new spin
	multiplier.
*/
void Score::SetSpin(int spin_degrees)
{
	// if score pot was reset, m_currentSpinTrick is no longer valid
	if (m_currentTrick == 0 || m_currentSpinTrick < 0)
		return;

	int spin_position = spin_degrees;
    const char *p_direction = "";

    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
    Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );
    Dbg_Assert( pSkater );

    Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( pSkater );
	bool is_flipped = pAnimationComponent->IsFlipped();

    // frontside or backside?
    if (spin_position < 0)
    { 
        spin_position = -spin_position;
        if ( is_flipped )
        {
            p_direction = "BS";
        }
        else
        {
            p_direction = "FS";
        }
    }
    else
    {
        if ( is_flipped )
        {
            p_direction = "FS";
        }
        else
        {
            p_direction = "BS";
        }
    }

    // add a little fudge factor: doesn't need to fully rotate 180 to earn 180
	spin_position += (int)Obj::GetPhysicsFloat(CRCD(0x50c5cc2f, "spin_count_slop"));	
	int spin_index = spin_position / 180;

	// TT#3426 - Clamp spins if too high, to prevent cheaters getting stuck and doing a lot of tricks	
	// Not needed as spins only give you extra multiplier up to 900 anyway
	#if 0
	if (spin_index > 11)
	{
		#ifdef	__NOPT_ASSERT__
		printf ("CHEAT WARNING, clamping spins\n");
		#endif
		spin_index = 11;
	}
	#endif
	
	// update the rotation amount of this trick
	m_infoTab[m_currentSpinTrick].spin_degrees = spin_degrees;

	// don't bother updating text if spin index isn't big enough
	if (m_infoTab[m_currentSpinTrick].spin_mult_index < spin_index)
	{
		if (m_debug)
		{
			printf("New spin value %d:\n", spin_index * 180);
		}
		
        // Not sure why this is necessary, but Ollies w/o another trick
        // have the wrong spin direction on half rotations. i.e. 180, 540, 900, etc.
        if ( m_infoTab[m_currentSpinTrick].id == CRCD(0x9b65d7b8,"Ollie") )
        {
            if ( (spin_index%2) == 1 )
            {
                if ( p_direction == "FS")
                {
                    p_direction = "BS";
                }
                else
                {
                    p_direction = "FS";
                }
            }
        }
        
		int last_trick = m_infoTab.GetSize() - 1;
		Dbg_Assert(last_trick >= 0);
		int shown_spin = spin_index * 180;
		
		const char *p_format="%d\\_%s";
		if (last_trick > m_currentSpinTrick)
		{
			if (m_infoTab[m_currentSpinTrick].flags & Score::vCAT)
			{
				p_format="\\c3%s\\_%d\\_%s\\c0\\_+ ";
			}	
			else
			{
				if (m_infoTab[m_currentSpinTrick].flags & Score::vSPECIAL)
    			{
    				p_format="\\c2%s\\_%d\\_%s\\c0\\_+ ";
    			}
                else
                {
                    p_format="%s\\_%d\\_%s\\_+ ";
                }
			}	
		}			
		else
		{
			if (m_infoTab[m_currentSpinTrick].flags & Score::vCAT)
			{
				p_format="\\c3%s\\_%d\\_%s\\c0";
			}	
			else
			{
				if (m_infoTab[m_currentSpinTrick].flags & Score::vSPECIAL)
    			{
    				p_format="\\c2%s\\_%d\\_%s\\c0";
    			}
                else
                {
                    p_format="%s\\_%d\\_%s";
                }
			}	
		}		
		
        sprintf(m_infoTab[m_currentSpinTrick].trickTextFormatted, p_format, p_direction, shown_spin, m_infoTab[m_currentSpinTrick].trickNameText);
		dispatch_trick_sequence_to_screen();
		
		/*
		if (mp_trickWindow)
		{
			mp_trickWindow->ChangeSpin(spin_index * 180, m_currentSpinTrick);
		}
		*/
		m_infoTab[m_currentSpinTrick].spin_mult_index = spin_index;
		captureScore();

        Obj::CStatsManagerComponent* pStatsManagerComponent = GetStatsManagerComponentFromObject( pSkater );
        Dbg_Assert( pStatsManagerComponent );
        pStatsManagerComponent->SetSpin( (spin_index*180) );
		
		m_scorePotState = SHOW_ACTUAL_SCORE_POT;
		dispatch_score_pot_value_to_screen(m_scorePot, m_currentMult);
		
		pSkater->BroadcastEvent(CRCD(0x68b887bb, "SkaterSpinDisplayed"));
	}
}

void Score::TweakTrick(int tweak_value )
{	
	if (m_currentTrick <= 0) return;
	// K: If the last trick was a 'Null' trick, then do not add the score.
	// This is to fix TT5718, where it was possible to do a manual, then jump out of it
	// really quickly whilst still getting some score appearing on screen, even though you
	// did not really get those points because the non-null trick name had not been displayed yet.
	// (At the start of the manual, a 'null' trick is done using SetTrickName "", simply in order
	// to do a BlockSpin, but we don't want that null trick to enable the score counter)
	if (m_infoTab[m_currentTrick-1].trickNameText[0]==0) return;
	
	#if 1	
	// (Mick) check if this is the last of a long line of non-blocking tricks 
	// and if so, then decrement the multiplier (leaving it unchanged)
	int non_block_count = 0;   
	for (int i = m_currentTrick-1; i >0; i--)
	{
		// if a blocking trick, use default spin multiplier
		if (m_infoTab[i].flags & vBLOCKING)
		{
			break;
		}
		non_block_count++;
	}	
//		printf ("%3d: ",non_block_count);
	if (non_block_count > 10)
	{
		#ifdef	__NOPT_ASSERT__
		printf ("CHEAT PREVENTION:  Limiting tweak after 10 nonblocking trick\n");
		#endif
		return;
	}
	
	if (!(m_infoTab[m_currentTrick-1].flags & vBLOCKING) && m_infoTab[m_currentTrick-1].score > MAX_SCORE_DUE_TO_TWEAK) return;
	#endif
	
	#ifdef NO_SCORE_DURING_UBER_FRIG
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = skate_mod->GetSkaterById(m_skaterId);
	Obj::CSkaterAdjustPhysicsComponent* pSkaterAdjustPhysicsComponent = GetSkaterAdjustPhysicsComponentFromObject(pSkater);
	if (pSkaterAdjustPhysicsComponent && pSkaterAdjustPhysicsComponent->UberFriggedThisFrame()) return;
	#endif
	
	m_infoTab[m_currentTrick-1].score += tweak_value;
	captureScore();
	
	dispatch_trick_sequence_to_screen();
	m_scorePotState = SHOW_ACTUAL_SCORE_POT;
	dispatch_score_pot_value_to_screen(m_scorePot, m_currentMult);
}

void Score::Land( void )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Net::Client* client;
    
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Game::CGameMode* pGameMode = skate_mod->GetGameMode();
	
	captureScore();
	
	/*
	if (mp_trickWindow)
	{
		Dbg_MsgAssert(mp_trickWindow,( "no trick window defined"));
		mp_trickWindow->Count(true);
    }
	*/
	
	// GLOOBY
	int trick_score = m_scorePot * m_currentMult;

	Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );
	Dbg_Assert( pSkater );
	Dbg_Assert( pSkater->IsLocalClient());
	// if playing graffiti, then update the colors as appropriate
	bool debug_graffiti = Script::GetInteger( "debug_graffiti" );
    Game::CGoalManager* pGoalManager = Game::GetGoalManager();
	pGoalManager->Land();
	if ( debug_graffiti || ( pGameMode->IsTrue("should_modulate_color") ) && ( trick_score > 0 ) )
	{
		// update the server		
		LogTrickObjectRequest( trick_score );
	}

	// flushes the graffiti trick buffer
	GetTrickComponentFromObject(pSkater)->SetGraffitiTrickStarted( false );
    
    if (!Script::GetInteger("NewSpecial"))	
	{
		// update special meter (when we land)
		m_specialScore += m_scorePot * m_currentMult;
		if (m_specialScore >= 3000)
		{
			set_special_is_active(true);
			m_specialScore = 3000;
		}
		if (m_specialScore < 0)
		{
			m_specialScore = 0;
		}
	}
	else
	{
		m_recentSpecialScorePot = 0;		// ensure next trick is not infuenced by this trick
	}
	
	// update combo records
	if ( m_currentMult > m_longestCombo )
	{
		// printf("length: old record - %i, new record - %i\n", m_longestCombo, m_currentMult);
		m_longestCombo = m_currentMult;
	}
	if ( GetLastScoreLanded() > m_bestCombo )
	{
		m_bestCombo = GetLastScoreLanded();
		// printf("points: old record - %i, new record - %i\n", m_bestCombo, GetLastScoreLanded());
	}

	if ( GetLastScoreLanded() > m_bestGameCombo )
	{
		if( gamenet_man->InNetGame() && ( pGameMode->GetNameChecksum() != CRCD(0x1c471c60,"netlobby")))
		{
			Net::MsgDesc msg_desc;
			Net::Client* client;
			GameNet::MsgScoreLanded msg;
	
			msg.m_Score = GetLastScoreLanded();
			msg.m_GameId = gamenet_man->GetNetworkGameId();
			
			msg_desc.m_Id = GameNet::MSG_ID_COMBO_REPORT;
			msg_desc.m_Data = &msg;
			msg_desc.m_Length = sizeof( GameNet::MsgScoreLanded );
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
	
			client = gamenet_man->GetClient( pSkater->GetSkaterNumber());
			Dbg_Assert( client );
					
			client->EnqueueMessageToServer( &msg_desc );
		}
		m_bestGameCombo = GetLastScoreLanded();
	}


	// if not degrading score, we need to clear our history
	if (!pGameMode->ShouldDegradeScore())  
	{
		// clear combo history, and total history as well, althought that really should never ahve been set.
		for (int i = 0; i < m_historyTab.getSize(); i++)
		{
			TrickHistory *pHistory = m_historyTab.GetItemByIndex(i);
			for (int s = 0; s < 4; s++)
			{
				pHistory->total_count[s] = 0;
				pHistory->combo_count[s] = 0;
			}
		}
		// and reset the rail counters between combos  
		reset_robot_detection();
	}
	else
	{
		// update trick history -- all counts from trick sequence are added to counts from session
		for (int i = 0; i < m_historyTab.getSize(); i++)
		{
			TrickHistory *pHistory = m_historyTab.GetItemByIndex(i);
			for (int s = 0; s < 4; s++)
			{
				pHistory->total_count[s] += pHistory->combo_count[s];
				pHistory->combo_count[s] = 0;
			}
		}
	}
	
	if (m_currentTrick)
	{
		m_scorePotState = WAITING_TO_COUNT_SCORE_POT;
		m_countedScorePot = m_scorePot * m_currentMult;
		m_scorePotCountdown = 150; // about 3 seconds		
		dispatch_score_pot_value_to_screen(m_scorePot * m_currentMult, 0);

		int index;
		index = pSkater->GetHeapIndex();
		TrickTextLanded(index);
		Replay::WriteTrickTextLanded();
	}
	
	resetScorePot();

	int old_score = GetTotalScore(); 

	// if we're not in graffiti mode, then update the total score
	// and panel (in graffiti mode, the client doesn't have the
	// authority to set its own scores)
	if ( !pGameMode->IsTrue(CRCD(0x11941568, "should_modulate_color")) )
	{
		if( pGameMode->ShouldTrackTrickScore())
		{
			if( ( pGameMode->ShouldAccumulateScore()) || 
				( ( pGameMode->ShouldTrackBestCombo()) &&
				  ( GetTotalScore() < trick_score )))
			{
				if( !gamenet_man->OnServer())
				{
					GameNet::MsgScoreLanded msg;
					Net::MsgDesc msg_desc;

					msg.m_GameId = gamenet_man->GetNetworkGameId();
					msg.m_Score = trick_score;
					
					msg_desc.m_Data = &msg;
					msg_desc.m_Length = sizeof( GameNet::MsgScoreLanded );
					msg_desc.m_Id = GameNet::MSG_ID_LANDED_TRICK;
					msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
					msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;

					client = gamenet_man->GetClient( pSkater->GetSkaterNumber());
					Dbg_Assert( client );
					
					client->EnqueueMessageToServer( &msg_desc );
				}

				if( pGameMode->ShouldTrackBestCombo())
				{
					SetTotalScore( trick_score );
				}
				else
				{
					SetTotalScore( m_totalScore + trick_score );
				}
			}
			else if( !pGameMode->ShouldTrackBestCombo())
			{
				// don't reset if the score is frozen
				if ( !pGameMode->ScoreFrozen() )
					SetTotalScore( trick_score );
			}
		}
	}

	int new_score = GetTotalScore();
	check_high_score(old_score, new_score);

	
	Obj::CSkaterRunTimerComponent* pSkaterRunTimerComponent = GetSkaterRunTimerComponentFromObject(pSkater);
	Dbg_Assert(pSkaterRunTimerComponent);
	pSkaterRunTimerComponent->ComboEnded();

	// XXX
//	printf("TRICK SCORE = %d\n", new_score);	
}


void Score::ForceSpecial( void )
{
	set_special_is_active(true);
	m_specialScore = 3000;
}




/*
	Called whenever skater bails. Clears combo history, resets score pot, stops special
*/
void Score::Bail( bool clearScoreText )
{    
	// update trick history -- clear counts for this combo
	for (int i = 0; i < m_historyTab.getSize(); i++)
	{
		TrickHistory *pHistory = m_historyTab.GetItemByIndex(i);
		for (int s = 0; s < 4; s++)
			pHistory->combo_count[s] = 0;
	}

	if (m_currentTrick)
	{
		m_scorePotState = SHOW_ACTUAL_SCORE_POT;
		
		if (!clearScoreText)
		{
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );
			Dbg_Assert( pSkater );
	
			dispatch_score_pot_value_to_screen(m_scorePot * m_currentMult, 0);
			
			int index;
			index = pSkater->GetHeapIndex();
			TrickTextBail(index);
		}
		else
		{
			dispatch_trick_sequence_to_screen(true);
			dispatch_score_pot_value_to_screen( 0, 0 );
		}
		
		// Replay::WriteTrickTextBail();
	}
	
	/*
	if (mp_trickWindow)
	{
		Dbg_MsgAssert(mp_trickWindow,( "no trick window defined"));
		mp_trickWindow->Collapse();
	}
	*/
	
	resetScorePot();

	m_specialScore = 0;
	set_special_is_active(false);
	
	Obj::CSkaterRunTimerComponent* pSkaterRunTimerComponent
		= GetSkaterRunTimerComponentFromObject(Mdl::Skate::Instance()->GetSkaterById( m_skaterId ));
	Dbg_Assert(pSkaterRunTimerComponent);
	pSkaterRunTimerComponent->ComboEnded();

    Game::CGoalManager* pGoalManager = Game::GetGoalManager();
	pGoalManager->Bail();
}



// completely resets score object
void Score::Reset()
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );
	//GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	//Net::Server* server; 

	printf ("Score::RESET ...............\n");

	reset_robot_detection();

	resetScorePot();

	Lst::LookupTableDestroyer destroyer(&m_historyTab);	
	destroyer.DeleteTableContents();
	
	/*
	if (mp_trickWindow)
	{
		mp_trickWindow->ResetWindow();
		mp_trickWindow->ResetRecords();
	}
	*/

	if( pSkater && pSkater->IsLocalClient())
	{
		dispatch_trick_sequence_to_screen();
		dispatch_score_pot_value_to_screen( 0, 0 );
	}
	
	if( ( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0xbff33600,"netfirefight")) ||
		( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0x3d6d444f,"firefight")))
	{
		m_totalScore = 100;
	}
	else
	{
		m_totalScore = 0;
	}
	
	m_scorePot = 0;
	m_recentScorePot = 0;
	m_recentSpecialScorePot = 0;
	m_specialScore = 0;
	m_bestGameCombo = 0;
	
	set_special_is_active(false);

	// Mick:  The next two lines clear any pending "countdown" score that might show up again after
	// we switch levels (or return to the skateshop)
	m_countedScorePot = 0;
	m_scorePotState = SHOW_ACTUAL_SCORE_POT;

	setSpecialBarColors(); // Split into seperate function so that special bar colors could be updated for themes
	
	Obj::CSkater* p_skater = Mdl::Skate::Instance()->GetSkaterById( m_skaterId );
	if (p_skater && p_skater->IsLocalClient())
	{
		Obj::CSkaterRunTimerComponent* pSkaterRunTimerComponent = GetSkaterRunTimerComponentFromObject(p_skater);
		Dbg_Assert(pSkaterRunTimerComponent);
		pSkaterRunTimerComponent->ComboEnded();
	}

	/*
	HUD::PanelMgr* panel_mgr = HUD::PanelMgr::Instance();
	HUD::Panel* panel;

	if(( panel = panel_mgr->GetPanelBySkaterId(m_skaterId, false)))
	{
		panel->SetScore(0, true);
	}
	*/
}

void Score::setSpecialBarColors()
{
    Script::CArray *p_special_bar_colors = Script::GetArray("special_bar_colors", Script::ASSERT);
	for (int i = 0; i < 3; i++)
	{
		Script::CArray *p_rgba = p_special_bar_colors->GetArray(i);

		m_special_rgba[i].r = p_rgba->GetInteger(0);
		m_special_rgba[i].g = p_rgba->GetInteger(1);
		m_special_rgba[i].b = p_rgba->GetInteger(2);
		m_special_rgba[i].a = p_rgba->GetInteger(3);
	}
	m_special_interpolator_rate = Script::GetFloat("special_bar_iterpolator_rate", Script::ASSERT);

}


/*
	Totals up score pot of present trick sequence, sends number to trick text window
*/
void Score::captureScore()
{
	// if no tricks in list, then no score to capture 
	if (m_currentTrick == 0) 
	{
		m_recentSpecialScorePot = 0;
		m_recentScorePot = 0;
		return;
	}

	if (m_debug)
		printf("Score pot:\n");
	
	// compute score pot
	m_scorePot = 0;
	int current_spin_mult = 2;		// n over 2
	for (int i = 0; i < m_currentTrick; i++)
	{
		// if a blocking trick, use default spin multiplier
		if (m_infoTab[i].flags & vBLOCKING)
		{
			current_spin_mult = 2;
		}
		// if not a blocking trick, but the last trick was, then use the spin multiplier here
		// to apply to subsequent tricks up to next blocking one
		else
		{
			if (i == 0 || (m_infoTab[i-1].flags & vBLOCKING))
			{
				current_spin_mult = spinMult(m_infoTab[i].spin_mult_index);
			}
		}

		int deprec_mult = deprecMult(m_infoTab[i].mult_index);
		
		if (m_debug)
			printf("   base score %d, reduction mult %.2f, spin mult %.1f\n", 
				   m_infoTab[i].score, (float) deprec_mult / 100, (float) current_spin_mult / 2);
		
		int score = (m_infoTab[i].score * deprec_mult * current_spin_mult) / 200;
		if (m_infoTab[i].switch_mode)
		{
			score	= score * 120 / 100;
		}
		
		m_scorePot += score;
	}
	if (m_debug)
		printf("   total %d\n", m_scorePot);
	
	/*
	if( mp_trickWindow )
	{
		mp_trickWindow->SetScore(m_scorePot);
	}
	*/

	if (Script::GetInteger("NewSpecial"))	
	{
//		printf ("Score = %d, Recent = %d\n",m_scorePot * m_currentTrick , m_recentScorePot);
		if (m_scorePot * m_currentMult > m_recentSpecialScorePot)
		{
			// update special meter (constantly, as we score)
			m_specialScore += m_scorePot * m_currentMult - m_recentSpecialScorePot;
			if (m_specialScore <0)
			{
				set_special_is_active(false);
				m_specialScore = 0;
			}
			if (m_specialScore >= 3000)
			{
				set_special_is_active(true);
				m_specialScore = 3000;
			}	
		}
	}
	m_recentScorePot = m_scorePot * m_currentMult;
	m_recentSpecialScorePot = m_scorePot * m_currentMult;
}



void Score::resetScorePot()
{
	m_scorePot = 0;
	Lst::DynamicTableDestroyer destroyer(&m_infoTab);	
	destroyer.DeleteTableContents();
	m_currentTrick = 0;
	m_currentMult = 0;
	m_currentBlockingTrick = -1;
	m_currentSpinTrick = -1;
	reset_robot_detection_combo();
	//m_recentScorePot = 0;
}




void Score::dispatch_trick_sequence_to_screen ( bool clear_text )
{
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );
	int index = 0;

	if( pSkater )
	{
		index = pSkater->GetHeapIndex();
	}
	Front::CTextBlockElement *p_text_block = (Front::CTextBlockElement *) p_manager->GetElement(Script::GenerateCRC("the_trick_text") + index ).Convert();
	if (!p_text_block) return;

	const char *pp_string_tab[TRICK_LIMIT];

	if (!clear_text)
	{
		Dbg_Assert(m_infoTab.GetSize() <= TRICK_LIMIT);
		for (int i = 0; i < m_infoTab.GetSize(); i++)
		{
			pp_string_tab[i] = m_infoTab[i].trickTextFormatted;
		}

		p_text_block->SetText(pp_string_tab, m_infoTab.GetSize());
		// Replay::WriteTrickText(pp_string_tab, m_infoTab.GetSize());
	}
	else
	{
		p_text_block->SetText(pp_string_tab, 0);
	}

	Script::RunScript("UpdateTrickText");
}




int Score::spinMult(int x)
{
	// defines how the multiplier for spins ramps up.  .5 and 1 for 180 and 360, then 2,3,4,5,... for 540,720,900,1080,... etc.
	//return (((x)GetArray("arrow_positions", &p_arrow_array);
	Dbg_Assert(p_arrow_array);
	m_numArrowPositions = p_arrow_array->GetSize();
	for (int i = 0; i < m_numArrowPositions; i++)
	{
		m_arrowPosTab[i] = *p_arrow_array->GetPair(i);
	}
	// for good measure
	m_arrowPosTab[m_numArrowPositions] = m_arrowPosTab[m_numArrowPositions-1];
	m_arrowInterval = 1.0f / ((float) m_numArrowPositions);

	Script::CArray *p_bar_pos_array = NULL;
	if( ( CFuncs::ScriptInSplitScreenGame( NULL, NULL )) && 
		( skate_mod->GetGameMode()->GetNameChecksum() != Script::GenerateCRC( "horse" )) &&
		( skate_mod->GetGameMode()->GetNameChecksum() != Script::GenerateCRC( "nethorse" )))
	{
		if( Nx::CViewportManager::sGetScreenMode() == Nx::vSPLIT_V )
		{
			p_balance_meter_struct->GetArray("bar_positions_mp_v", &p_bar_pos_array);
		}
		else
		{
			p_balance_meter_struct->GetArray("bar_positions_mp_h", &p_bar_pos_array);
		}
	}
	else
	{
		p_balance_meter_struct->GetArray("bar_positions", &p_bar_pos_array);
	}

	Dbg_Assert(p_bar_pos_array);
	for (int i = 0; i < 2; i++)
	{
		m_meterPos[i] = *p_bar_pos_array->GetPair(i);
	}
}




void Score::position_balance_meter(bool state, float value, bool isVertical)
{
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );

	Front::CSpriteElement *p_balance_meter = (Front::CSpriteElement *) p_manager->GetElement(0xa4db8a4b + pSkater->GetHeapIndex()).Convert(); // "the_balance_meter"
	Dbg_Assert(p_balance_meter);
	
	int is_shown = 0;
	p_balance_meter->GetIntegerTag(0xc22eb9a0, &is_shown); // "tag_turned_on"
	if (state != (bool) is_shown)
	{
		Script::CStruct* pParams;
		pParams = new Script::CStruct;
		pParams->AddChecksum( "id", 0xa4db8a4b + pSkater->GetHeapIndex());
		if (state)
		{   
			Script::RunScript(CRCD(0xba95da16, "show_balance_meter"), pParams );
		}
		else
		{
			Script::RunScript(CRCD(0x58783b40, "hide_balance_meter"), pParams );
		}

		delete pParams;
	}
	
	if (isVertical)
		p_balance_meter->SetChecksumTag(0x863a07e2, 0xef24413b); // "tag_mode", "manual"
	else
		p_balance_meter->SetChecksumTag(0x863a07e2, 0x530be001); // "tag_mode", "balance"
	
	// figure out position of arrow
	float abs_value = Mth::Abs(value);
	int index = (int) ( abs_value / m_arrowInterval);
	float mix =  abs_value - ((float) index) * m_arrowInterval;
	m_arrowPos.mX = m_arrowPosTab[index].mX + (m_arrowPosTab[index+1].mX - m_arrowPosTab[index].mX) * mix / m_arrowInterval;
	m_arrowPos.mY = m_arrowPosTab[index].mY + (m_arrowPosTab[index+1].mY - m_arrowPosTab[index].mY) * mix / m_arrowInterval;
	//printf("arrow at %f, %f, index %d, mix %f\n", m_arrowPos.mX, m_arrowPos.mY, index, mix);
	m_arrowRot = -value * 45.0f;
	
	Front::CSpriteElement *p_balance_arrow = (Front::CSpriteElement *) p_balance_meter->GetFirstChild();//.Convert();
	Dbg_Assert(p_balance_arrow);
	
	if (isVertical)
	{
		p_balance_meter->SetPos(m_meterPos[1].mX, m_meterPos[1].mY, Front::CScreenElement::FORCE_INSTANT);
		p_balance_meter->SetRotate(-90.0f);
		if (value >= 0.0f)
			p_balance_arrow->SetPos(p_balance_meter->GetBaseW() / 2.0f + m_arrowPos.mY, p_balance_meter->GetBaseH() / 2.0f - m_arrowPos.mX, Front::CScreenElement::FORCE_INSTANT);
		else
			p_balance_arrow->SetPos(p_balance_meter->GetBaseW() / 2.0f + m_arrowPos.mY, p_balance_meter->GetBaseH() / 2.0f + m_arrowPos.mX, Front::CScreenElement::FORCE_INSTANT);
		p_balance_arrow->SetRotate(-m_arrowRot - 90.0f);
	}
	else
	{
		p_balance_meter->SetPos(m_meterPos[0].mX, m_meterPos[0].mY, Front::CScreenElement::FORCE_INSTANT);
		p_balance_meter->SetRotate(0);
		if (value >= 0.0f)
			p_balance_arrow->SetPos(p_balance_meter->GetBaseW() / 2.0f + m_arrowPos.mX, p_balance_meter->GetBaseH() / 2.0f + m_arrowPos.mY, Front::CScreenElement::FORCE_INSTANT);
		else
			p_balance_arrow->SetPos(p_balance_meter->GetBaseW() / 2.0f - m_arrowPos.mX, p_balance_meter->GetBaseH() / 2.0f + m_arrowPos.mY, Front::CScreenElement::FORCE_INSTANT);
		p_balance_arrow->SetRotate(-m_arrowRot);
	}	
}




void Score::dispatch_score_pot_value_to_screen(int score, int multiplier)
{
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	
	Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );
	int index = 0;

	if( pSkater )
	{
		index = pSkater->GetHeapIndex();
	}
	Front::CTextElement *p_score_pot_text = (Front::CTextElement *) p_manager->GetElement(0xf4d3a70e + index ).Convert(); // "the_score_pot_text"
	Dbg_Assert(p_score_pot_text);

	char p_string[128];
	if (!score)
		strcpy(p_string, " ");
	else if (multiplier)
		sprintf(p_string, "%s X %d", Str::PrintThousands(score), multiplier);
	else
		sprintf(p_string, "%s", Str::PrintThousands(score));

	
	p_score_pot_text->SetText(p_string);
	
	Replay::WriteScorePotText(p_string);
	Script::RunScript("UpdateScorepot");
}

// Check for a high score goals being achieved	   
// for each goal that is actieved, then run the script
// on the skater that got the score
void Score::check_high_score(int old_score, int new_score)
{
	#ifdef	DEBUG_HIGH_SCORE_GOALS
	printf("Checking High Scores, score changed from %d to %d\n",old_score, new_score); 
	#endif
	
	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	// if we are not in career mode, then do not check for high score			   
	if (!skate_mod->GetGameMode()->IsTrue( CRCD(0x1ded1ea4, "is_career") ))
	{
		return;
	}

	for (int i=0;i < Mdl::Skate::vMAX_SCORE_GOALS;i++)
	{
		int score = skate_mod->GetScoreGoalScore(i);
		int goal = skate_mod->GetScoreGoalGoal(i);
		uint32 script = skate_mod->GetScoreGoalScript(i);
		if (score)		
		{
			#ifdef	DEBUG_HIGH_SCORE_GOALS
			printf("For goal %2d, score %7d, script %x\n",goal,score,script); 
			#endif
			if (old_score < score && new_score >= score)
			{
				#ifdef	DEBUG_HIGH_SCORE_GOALS
				dodgy_test(); printf("Score change works\n"); 
				#endif
				if( !skate_mod->GetCareer()->GetGoal(goal))
				{
					#ifdef	DEBUG_HIGH_SCORE_GOALS
					printf("awarding this goal, and running script"); 
					#endif
					// not got this goal yet, so give it
					skate_mod->GetCareer()->SetGoal(goal);
					
					// and run the script
					skate_mod->GetSkaterById( m_skaterId )->SpawnAndRunScript(script);					
				}
				else
				{
					#ifdef	DEBUG_HIGH_SCORE_GOALS
					printf("Already got this goal\n"); 
					#endif
				}
			}  
			else
			{
				#ifdef	DEBUG_HIGH_SCORE_GOALS
				printf("Score change did not straddel this score\n"); 
				#endif
			}
		}
	}
}

void Score::SetBalanceMeter(bool state, float value)
{
	Replay::WriteBalanceMeter(state,value);
	//Ryan("balance: %f\n", value);
	position_balance_meter(state, value, false);
}




void Score::SetManualMeter(bool state, float value)
{
	Replay::WriteManualMeter(state,value);
	
	//Ryan("balance: %f\n", value);
	position_balance_meter(state, value, true);
}




void Score::SetTotalScore( int score )
{
	m_totalScore = score;

	// update the panel
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	/*Game::CGameMode* pGameMode = */skate_mod->GetGameMode();
//	bool can_zero_score = pGameMode->IsTrue("should_modulate_color");

	//	printf("Setting total score for %d: %d %d %s\n", m_skaterId, score, GetTotalScore(), Script::FindChecksumName(pGameMode->GetNameChecksum()) );
	
	Obj::CSkater* pSkater = skate_mod->GetSkaterById( m_skaterId );
	Dbg_Assert( pSkater );
	if ( pSkater->IsLocalClient() )
	{
		//HUD::PanelMgr* panel_mgr = HUD::PanelMgr::Instance();
		//HUD::Panel* panel;
		char score_text[64];
		
		if( ( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netking" )) ||
			( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "king" )))
		{
			sprintf( score_text, "%d:%.2d", Tmr::InSeconds( m_totalScore ) / 60, Tmr::InSeconds( m_totalScore ) % 60 );
		}
		else
		{
			sprintf( score_text, "%d", m_totalScore );
		}

		Front::SetScoreTHPS4( score_text, pSkater->GetHeapIndex());

		/*
		if(( panel = panel_mgr->GetPanelBySkaterId(m_skaterId, false)))
		{
			panel->SetScore( m_totalScore, can_zero_score );
		}
		*/
	}
}




int Score::GetTotalScore()
{	
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	if( gamenet_man->OnServer())
	{
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		Game::CGameMode* pGameMode = skate_mod->GetGameMode();

		// server is also responsible for calculating graffiti scores
		bool debug_graffiti = Script::GetInteger( "debug_graffiti" );
		if ( debug_graffiti || pGameMode->IsTrue("should_modulate_color") )
		{
			return skate_mod->GetTrickObjectManager()->GetScore( m_skaterId );
		}
		else
		{
			return m_totalScore;
		}
	}
	else
	{
		// clients already knows their total scores
		return m_totalScore;
	}

}




/*
	Returns the network connection.
*/
Net::Conn* Score::GetAssociatedNetworkConnection( void )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Obj::CSkater* skater;
	GameNet::PlayerInfo* player;

	

	skater = skate_mod->GetSkaterById( m_skaterId );
	if( skater )
	{
		player = gamenet_man->GetPlayerByObjectID( skater->GetID() );
		if( player )
		{
			return player->m_Conn;
		}
	}

	return NULL;
}




void Score::LogTrickObjectRequest( int score )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::PlayerInfo* player;
	GameNet::MsgScoreLogTrickObject msg;
	Net::MsgDesc msg_desc;
	Net::Client* client;   

	player = gamenet_man->GetPlayerByObjectID( m_skaterId );
	Dbg_Assert( player );
    
	client = gamenet_man->GetClient( player->m_Skater->GetSkaterNumber());
	Dbg_MsgAssert( client, ( "Couldn't find client for skater %d", player->m_Skater->GetSkaterNumber() ) );

	msg.m_SubMsgId = GameNet::SCORE_MSG_ID_LOG_TRICK_OBJECT;
	msg.m_OwnerId = m_skaterId;
	msg.m_Score = score;
	msg.m_GameId = gamenet_man->GetNetworkGameId();

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* skater = skate_mod->GetSkaterById( m_skaterId );
	Dbg_Assert( skater );
	
	Obj::CTrickComponent* p_trick_component = GetTrickComponentFromObject(skater);
	Dbg_Assert(p_trick_component);

	// send out variable length data representing the trick chain
	uint32 max_pending_trick_buffer_size = GameNet::MsgScoreLogTrickObject::vMAX_PENDING_TRICKS * sizeof(uint32);
	uint32 actual_pending_trick_buffer_size = p_trick_component->WritePendingTricks( msg.m_PendingTrickBuffer, max_pending_trick_buffer_size );
	msg.m_NumPendingTricks = actual_pending_trick_buffer_size / sizeof( uint32 );

	printf( "Client -> server %d tricks\n", msg.m_NumPendingTricks );

	msg_desc.m_Data = &msg;
	msg_desc.m_Length = sizeof( GameNet::MsgScoreLogTrickObject ) - max_pending_trick_buffer_size + actual_pending_trick_buffer_size;
	msg_desc.m_Id = GameNet::MSG_ID_SCORE;
	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
	//client->EnqueueMessageToServer( &msg_desc );
	
	client->StreamMessageToServer( msg_desc.m_Id, msg_desc.m_Length, 
						   msg_desc.m_Data, "TrickObj Buffer", GameNet::vSEQ_GROUP_PLAYER_MSGS );

}




void Score::LogTrickObject( int skater_id, int score, uint32 num_pending_tricks, uint32* p_pending_tricks, bool propagate )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::MsgScoreLogTrickObject msg;
	GameNet::MsgObsScoreLogTrickObject obs_msg;
	Net::Server* server;
	Net::Conn* conn;
	GameNet::PlayerInfo* player;
	Lst::Search< GameNet::PlayerInfo > sh;

	if( propagate )
	{
		bool send_steal_message;

		server = gamenet_man->GetServer();
		Dbg_Assert( server );

		conn = GetAssociatedNetworkConnection();

		// keep track of whom we need to send the steal message to
		int i;
		char previous_owner_flags[GameNet::vMAX_PLAYERS];
		for ( i = 0; i < GameNet::vMAX_PLAYERS; i++ )
		{
			previous_owner_flags[i] = 0;
		}
		
		num_pending_tricks = skate_mod->GetTrickObjectManager()->RequestLogTrick( num_pending_tricks, p_pending_tricks, previous_owner_flags, skater_id, score );
		
		if ( !num_pending_tricks )
		{
			return;
		}

		for ( i = 0; i < GameNet::vMAX_PLAYERS; i++ )
		{
			if ( previous_owner_flags[i] )
			{
				// GJ:  This only sends the stolen message to the two parties involved

				GameNet::PlayerInfo* new_owner, *old_owner;
				GameNet::MsgStealMessage steal_msg;

				steal_msg.m_NewOwner = skater_id;
				steal_msg.m_OldOwner = i;
				steal_msg.m_GameId = gamenet_man->GetNetworkGameId();

				new_owner = gamenet_man->GetPlayerByObjectID(skater_id);
				old_owner = gamenet_man->GetPlayerByObjectID( i );
				Dbg_Assert( new_owner );
				
				send_steal_message = true;
				if( skate_mod->GetGameMode()->IsTeamGame())
				{
					if( old_owner && new_owner && ( old_owner->m_Team == new_owner->m_Team ))
					{
						send_steal_message = false;
					}
				}

				if( send_steal_message )
				{
					Net::MsgDesc msg_desc;

					msg_desc.m_Data = &steal_msg;
					msg_desc.m_Length = sizeof( GameNet::MsgStealMessage );
					msg_desc.m_Id = GameNet::MSG_ID_STEAL_MESSAGE;
					server->EnqueueMessage( new_owner->GetConnHandle(), &msg_desc );
	
					steal_msg.m_NewOwner = skater_id;
					steal_msg.m_OldOwner = i;
					steal_msg.m_GameId = gamenet_man->GetNetworkGameId();
	
                    //Dbg_Assert( pPlayerInfo );
	
					// For now, don't assert if the player doesn't exist anymore. Just don't do anything.
					// Eventually, Gary will fix this so that pieces of exiting players are reset
					if( old_owner )
					{
						server->EnqueueMessage( old_owner->GetConnHandle(), &msg_desc );
					}
				}
			}
		}
		
#ifdef __USER_GARY__
		printf( "Broadcasting %d tricks\n", num_pending_tricks );
#endif
		
		msg.m_SubMsgId = GameNet::SCORE_MSG_ID_LOG_TRICK_OBJECT;
		msg.m_OwnerId = skater_id;
		msg.m_Score = score;
		msg.m_NumPendingTricks = num_pending_tricks;
		msg.m_GameId = gamenet_man->GetNetworkGameId();

		uint32 max_pending_trick_buffer_size = GameNet::MsgScoreLogTrickObject::vMAX_PENDING_TRICKS * sizeof(uint32);
		uint32 actual_pending_trick_buffer_size = msg.m_NumPendingTricks * sizeof(uint32);
		memcpy( msg.m_PendingTrickBuffer, p_pending_tricks, actual_pending_trick_buffer_size );

		Net::MsgDesc score_msg_desc;

		score_msg_desc.m_Data = &msg;
		score_msg_desc.m_Length = sizeof( GameNet::MsgScoreLogTrickObject ) - max_pending_trick_buffer_size + actual_pending_trick_buffer_size;
		score_msg_desc.m_Id = GameNet::MSG_ID_SCORE;
		score_msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		score_msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
		// tell players to change their colors
		for( player = gamenet_man->FirstPlayerInfo( sh ); player; 
				player = gamenet_man->NextPlayerInfo( sh ))
		{
			server->StreamMessage( player->GetConnHandle(), score_msg_desc.m_Id, score_msg_desc.m_Length, 
						   score_msg_desc.m_Data, "TrickObj Buffer", GameNet::vSEQ_GROUP_PLAYER_MSGS );
			//server->EnqueueMessage( player->GetConnHandle(), &score_msg_desc );
		}

		obs_msg.m_OwnerId = skater_id;
		obs_msg.m_NumPendingTricks = num_pending_tricks;
		obs_msg.m_GameId = gamenet_man->GetNetworkGameId();
		max_pending_trick_buffer_size = GameNet::MsgScoreLogTrickObject::vMAX_PENDING_TRICKS * sizeof(uint32);
		actual_pending_trick_buffer_size = obs_msg.m_NumPendingTricks * sizeof(uint32);
		memcpy( obs_msg.m_PendingTrickBuffer, p_pending_tricks, actual_pending_trick_buffer_size );

		score_msg_desc.m_Data = &obs_msg;
		score_msg_desc.m_Length = sizeof( GameNet::MsgObsScoreLogTrickObject ) - max_pending_trick_buffer_size + actual_pending_trick_buffer_size;
		score_msg_desc.m_Id = GameNet::MSG_ID_OBSERVER_LOG_TRICK_OBJ;
		// tell observers to change their colors
		for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; 
				player = gamenet_man->NextPlayerInfo( sh, true ))
		{
			if( player->IsObserving())
			{
				server->StreamMessage( player->GetConnHandle(), score_msg_desc.m_Id, score_msg_desc.m_Length, 
						   score_msg_desc.m_Data, "TrickObj Buffer", GameNet::vSEQ_GROUP_PLAYER_MSGS );
				//server->EnqueueMessage( player->GetConnHandle(), &score_msg_desc );
			}
		}

		// send score updates, as something has changed
		skate_mod->SendScoreUpdates( false );
		
		// Let the server's client do the rest of the work
		if( conn->IsLocal())
		{
			return;
		}
	}

#ifdef __USER_GARY__
	printf( "Client is receiving %d tricks\n", num_pending_tricks );
#endif
	
	int seq;

	if( skate_mod->GetGameMode()->IsTeamGame())
	{
		player = gamenet_man->GetPlayerByObjectID( skater_id );
		seq = player->m_Team;
	}
	else
	{
		seq = skater_id;
	}

	// modulate the color here
	for ( uint32 i = 0; i < num_pending_tricks; i++ )
	{
		
		skate_mod->GetTrickObjectManager()->ModulateTrickObjectColor( p_pending_tricks[i], seq );
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// utilities used by the trick counting functions to resolve a key_combo
// or trick to its trick text checksum after taking into account the number
// of taps.
inline uint32 get_trickname_checksum_from_trick_and_taps( uint32 trick_checksum, int num_taps = 1 )
{
	// get the trick text and associated checksum
	Script::CStruct* p_trick = Script::GetStructure( trick_checksum, Script::ASSERT );
	Script::CStruct* p_trick_params;
	p_trick->GetStructure( CRCD( 0x7031f10c, "params" ), &p_trick_params, Script::ASSERT );

	const char* p_trick_name;

	// get any double or triple tap info
	while ( num_taps > 1 )
	{			
		num_taps--;

		Script::CArray* p_extra_trick_array;
		uint32 extra_trick;

		if ( !p_trick_params->GetChecksum( CRCD( 0x6e855102, "ExtraTricks" ), &extra_trick, Script::NO_ASSERT ) )
			Dbg_MsgAssert( 0, ( "Couldn't find ExtraTricks to get multiple tap version of trick" ) );
		
		// printf("got extra trick %s\n", Script::FindChecksumName( extra_trick ) );
		
		p_extra_trick_array = Script::GetArray( extra_trick, Script::ASSERT );
		Dbg_MsgAssert( p_extra_trick_array->GetType() == ESYMBOLTYPE_STRUCTURE, ( "Extra trick array %s had wrong type", Script::FindChecksumName( extra_trick ) ) );
		Script::CStruct* p_sub_struct = p_extra_trick_array->GetStructure( 0 );
		p_sub_struct->GetStructure( CRCD( 0x7031f10c, "params" ), &p_trick_params, Script::ASSERT );
	}
	
	p_trick_params->GetLocalString( CRCD( 0xa1dc81f9, "name" ), &p_trick_name, Script::ASSERT );
	// printf("found name %s\n", p_trick_name);
	return Script::GenerateCRC( p_trick_name );
}

inline uint32 get_trickname_checksum_from_key_combo( uint32 key_combo, int num_taps = 1 )
{
	// printf("\tget_trickname_checksum_from_key_combo( %s, %i )\n", Script::FindChecksumName( key_combo ), num_taps );
	
	// get the key mapping and trick name
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();
	Script::CStruct* pTricks = pSkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );	
	uint32 trick_checksum = 0;
	
	// see if this is a normal or special trick
	if ( !pTricks->GetChecksum( key_combo, &trick_checksum, Script::NO_ASSERT ) )
	{
		Script::CStruct* pSpecialTricks = pSkaterProfile->GetSpecialTricksStructure();
		Script::CArray* pSpecialTricksArray;
		pSpecialTricks->GetArray( NONAME, &pSpecialTricksArray, Script::ASSERT );
		int numSpecials = pSpecialTricksArray->GetSize();
		for ( int i = 0; i < numSpecials; i++ )
		{
			Script::CStruct* pSpecial = pSpecialTricksArray->GetStructure( i );
			uint32 trickSlot;
			pSpecial->GetChecksum( CRCD( 0xa92a2280, "TrickSlot" ), &trickSlot, Script::ASSERT );
			if ( trickSlot == key_combo )
			{
				pSpecial->GetChecksum( CRCD( 0x5b077ce1, "TrickName" ), &trick_checksum, Script::ASSERT );
				break;
			}
		}
	}

	if ( trick_checksum == 0 )
		return 0;

	return get_trickname_checksum_from_trick_and_taps( trick_checksum, num_taps );	
}


inline uint32 get_cat_index_from_key_combo( uint32 key_combo )
{
	// printf("\tget_trickname_checksum_from_key_combo( %s, %i )\n", Script::FindChecksumName( key_combo ), num_taps );
	
	// get the key mapping and trick name
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();
	Script::CStruct* pTricks = pSkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );	
	int cat_trick = -1;
	
	// see if this is a normal or special trick

	if ( !pTricks->GetInteger( key_combo, &cat_trick, Script::NO_ASSERT ) )
	{
		Script::CStruct* pSpecialTricks = pSkaterProfile->GetSpecialTricksStructure();
		Script::CArray* pSpecialTricksArray;
		pSpecialTricks->GetArray( NONAME, &pSpecialTricksArray, Script::ASSERT );
		int numSpecials = pSpecialTricksArray->GetSize();
		for ( int i = 0; i < numSpecials; i++ )
		{
			Script::CStruct* pSpecial = pSpecialTricksArray->GetStructure( i );
			int is_cat = 0;
			pSpecial->GetInteger( CRCD(0xb56a8816,"isCat"), &is_cat, Script::NO_ASSERT );
			if ( is_cat != 0 )
			{
				uint32 trickslot;
				pSpecial->GetChecksum( CRCD(0xa92a2280,"trickSlot"), &trickslot, Script::ASSERT );
				if ( trickslot == key_combo )
				{
					uint32 cat_trick_checksum;
					pSpecial->GetChecksum( CRCD(0x5b077ce1,"trickName"), &cat_trick_checksum, Script::ASSERT );
					cat_trick = (int)cat_trick_checksum;
					break;
				}
			}
		}
	}

	return cat_trick;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Score::VerifyTrickMatch( int info_tab_index, uint32 trick_checksum, int spin_mult, bool require_perfect )
{
	if ( info_tab_index >= m_currentTrick )
		return false;
	
	// printf("\tVerifyTrickMatch( %i, %s, %i, %i )\n", info_tab_index, Script::FindChecksumName( trick_checksum ), spin_mult, require_perfect );
	// printf("m_infoTab[%i].id = %s\n", info_tab_index, Script::FindChecksumName( m_infoTab[info_tab_index].id ) );
	if ( m_infoTab[info_tab_index].id == trick_checksum )
	{
		if ( spin_mult == 0 )
			return true;
		else 
		{
			// printf("spin_degrees for this trick = %i\n", m_infoTab[i].spin_degrees);
			if ( spin_mult == m_infoTab[info_tab_index].spin_mult_index )
			{
				if ( !require_perfect )
					return true;
				else if ( Mth::Abs( Mth::Abs(m_infoTab[info_tab_index].spin_degrees) - ( spin_mult * 180 ) ) < Script::GetInteger( CRCD( 0xfcfacab8, "perfect_landing_slop" ) ) )
					return true;
			}
		}		
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Score::CountTrickMatches( uint32 trick_checksum, int spin_mult, bool require_perfect )
{
	// printf("CountTrickMatches( %s, %i, %i )\n", Script::FindChecksumName( trick_checksum ), spin_mult, require_perfect );
	int current_spin = 1;
	
	int total = 0;

	for ( int i = 0; i < m_currentTrick; i++ )
	{
		if ( m_infoTab[i].flags & vBLOCKING )
		{
			current_spin = 1;
		}
		else if ( i == 0 || ( i >= 1 && m_infoTab[i-1].flags & vBLOCKING ) )
		{
			current_spin = m_infoTab[i].spin_mult_index;
		}
		// printf("current_spin = %i\n", current_spin );
		
		if ( m_infoTab[i].id == trick_checksum )
		{
			if ( spin_mult == 0 )
				total++;
			else 
			{
				// printf("spin_degrees for this trick = %i\n", m_infoTab[i].spin_degrees);
				if ( spin_mult == current_spin )
				{
					if ( !require_perfect )
						total++;
					else if ( Mth::Abs( Mth::Abs(m_infoTab[i].spin_degrees) - ( spin_mult * 180 ) ) < Script::GetInteger( CRCD( 0xfcfacab8, "perfect_landing_slop" ) ) )
						total++;
				}
			}		
		}
	}
	return total;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Score::CountStringMatches( const char* string )
{
	int total = 0;
    
    for ( int i = 0; i < m_currentTrick; i++ )
	{
        if ( Str::StrStr( m_infoTab[i].trickNameText, string ) )
		{
            total++;		
		}
	}
	return total;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Score::IsLatestTrickByName( uint32 trick_text_checksum, int spin_mult, bool require_perfect, int num_taps )
{
	int index = m_currentTrick;
	do
	{
		if (index <= 0) return false;
		index--;
	}
	while (m_infoTab[index].trickNameText[0] == 0);
	
	// resolve multiple tap tricks
	if (num_taps > 1)
	{
		trick_text_checksum = get_trickname_checksum_from_trick_and_taps(trick_text_checksum, num_taps);
	}
	
	return VerifyTrickMatch(index, trick_text_checksum, spin_mult, require_perfect);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Score::IsLatestTrick( uint32 key_combo, int spin_mult, bool require_perfect, int num_taps )
{
	int index = m_currentTrick;
	do
	{
		if (index <= 0) return false;
		index--;
	}
	while (m_infoTab[index].trickNameText[0] == 0);
	
	// get the key mapping and trick name
	uint32 trick_name_checksum = get_trickname_checksum_from_key_combo( key_combo, num_taps );
	if ( trick_name_checksum == 0 )
	{
		// check for cat trick		
		int cat_trick = get_cat_index_from_key_combo( key_combo );
		if ( cat_trick != -1 )
		{
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			Dbg_Assert( skate_mod );
			Obj::CSkater* pSkater = skate_mod->GetLocalSkater();
			if ( pSkater )
			{
				Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[cat_trick];
				Dbg_Assert( pCreatedTrick );
				const char* p_trick_name;
				pCreatedTrick->mp_other_params->GetString( CRCD( 0xa1dc81f9, "name" ), &p_trick_name, Script::ASSERT );
				trick_name_checksum = Script::GenerateCRC( p_trick_name );
			}
		}
		else
		{
			return false;
		}
	}
	
	return VerifyTrickMatch(index, trick_name_checksum, spin_mult, require_perfect);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Score::GetNumberOfNonGapTricks()
{
	int non_gap_trick_count = 0;
	
	for ( int i = 0; i < m_currentTrick; i++ )
	{
		if (!TrickIsGap(i))
		{
			non_gap_trick_count++;
		}
	}
	
	return non_gap_trick_count;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// find a trick by name, rather than by key combo
int Score::GetCurrentNumberOfOccurrencesByName( uint32 trickTextChecksum, int spin_mult, bool require_perfect, int num_taps )
{
	// resolve multiple tap tricks
	if ( num_taps > 1 )
	{
		trickTextChecksum = get_trickname_checksum_from_trick_and_taps( trickTextChecksum, num_taps );
	}
	
	// uint32 trick_name_checksum = Script::GenerateCRC( trick_string );
	int num = CountTrickMatches( trickTextChecksum, spin_mult, require_perfect );
/*	for ( int i = 0; i < m_currentTrick; i++ )
	{
		if ( VerifyTrickMatch( i, trickTextChecksum, spin_mult, require_perfect ) )
			num++;
	}*/
	return num;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// searches by key combo
int Score::GetCurrentNumberOfOccurrences( uint32 key_combo, int spin_mult, bool require_perfect, int num_taps )
{
	// get the key mapping and trick name
	uint32 trick_name_checksum = get_trickname_checksum_from_key_combo( key_combo, num_taps );
	if ( trick_name_checksum == 0 )
	{
		// check for cat trick		
		int cat_trick = get_cat_index_from_key_combo( key_combo );
		if ( cat_trick != -1 )
		{
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			Dbg_Assert( skate_mod );
			Obj::CSkater* pSkater = skate_mod->GetLocalSkater();
			if ( pSkater )
			{
				Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[cat_trick];
				Dbg_Assert( pCreatedTrick );
				const char* p_trick_name;
				pCreatedTrick->mp_other_params->GetString( CRCD( 0xa1dc81f9, "name" ), &p_trick_name, Script::ASSERT );
				trick_name_checksum = Script::GenerateCRC( p_trick_name );
			}
		}
		else
		{
			return 0;
		}
	}

	int num = this->CountTrickMatches( trick_name_checksum, spin_mult, require_perfect );
/*    for ( int i = 0; i < m_currentTrick; i++ )
    {
        if ( this->VerifyTrickMatch( i, trick_name_checksum, spin_mult, require_perfect) )
			num++;
    }
*/
	return num;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Score::GetPreviousNumberOfOccurrencesByName( uint32 trickTextChecksum, int spin_mult, int num_taps )
{
	// resolve multiple tap tricks
	if ( num_taps > 1 )
	{
		trickTextChecksum = get_trickname_checksum_from_trick_and_taps( trickTextChecksum, num_taps );
	}
	
	int num = 0;
    bool justSawTrick = false;
    for ( int i = 0; i < m_currentTrick; i++ )
    {
        // printf("checking %s\n", m_infoTab[i].trickNameText );
        if ( m_infoTab[i].id == trickTextChecksum )
        {
            // printf("found trick\n");
            if ( spin_mult == 0 )
			{
				num++;
				justSawTrick = true;
			}
			else if ( spin_mult == m_infoTab[i].spin_mult_index )
			{
				num++;
				justSawTrick = true;
			}
            
        }
        else 
        {
            if ( ( m_infoTab[i].flags & vGAP ) && justSawTrick )
            {
                // printf("found gap after trick\n");
            }
            else
            {
                // if ( justSawTrick ) printf("found a non-gap trick after the trick\n");
                justSawTrick = false;
            }
        }
    }
    if ( justSawTrick )
    {
        // printf("followed by gaps\n");
        num--;
    }
    // printf("found %i previous occurences\n", num );
    return num;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Score::GetPreviousNumberOfOccurrences( uint32 key_combo, int spin_mult, int num_taps )
{
	// printf ( "GetPreviousNumberOfOccurrences( %s, %i, %i )\n", Script::FindChecksumName( key_combo ), spin_mult, num_taps );
	uint32 trick_name_checksum = get_trickname_checksum_from_key_combo( key_combo, num_taps );
	
	if ( trick_name_checksum == 0 )
	{
		// check for cat trick		
		int cat_trick = get_cat_index_from_key_combo( key_combo );
		if ( cat_trick != -1 )
		{
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			Dbg_Assert( skate_mod );
			Obj::CSkater* pSkater = skate_mod->GetLocalSkater();
			if ( pSkater )
			{
				Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[cat_trick];
				Dbg_Assert( pCreatedTrick );
				const char* p_trick_name;
				pCreatedTrick->mp_other_params->GetString( CRCD( 0xa1dc81f9, "name" ), &p_trick_name, Script::ASSERT );
				trick_name_checksum = Script::GenerateCRC( p_trick_name );
			}
		}
		else
		{
			return 0;
		}
	}
	
	int num = 0;
    bool justSawTrick = false;
    for ( int i = 0; i < m_currentTrick; i++ )
    {
        // printf("checking %s\n", m_infoTab[i].id );
        if ( m_infoTab[i].id == trick_name_checksum )
        {
            // printf("found trick\n");
            if ( spin_mult == 0 )
			{
				num++;
				justSawTrick = true;
			}
			else if ( spin_mult == m_infoTab[i].spin_mult_index )
			{
				num++;
				justSawTrick = true;
			}            
        }
        else 
        {
            if ( ( m_infoTab[i].flags & vGAP ) && justSawTrick )
            {
                // printf("found gap after trick\n");
            }
            else
            {
                // if ( justSawTrick ) printf("found a non-gap trick after the trick\n");
                justSawTrick = false;
            }
        }
    }
    if ( justSawTrick )
    {
        // printf("followed by gaps\n");
        num--;
    }
    // printf("found %i previous occurences\n", num );
    return num;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// utility used by GetCurrentNumberOfOccurrences( Script::CArray* )
inline void GetTrickInfoFromTrickArray( Script::CArray* pTricks, int trick_array_index, uint32 *p_trick_name_checksum, int *p_spin, bool *p_require_perfect, int *p_num_taps )
{
	// reset spin and perfect first
	*p_spin = 0;
	*p_require_perfect = false;

	int num_taps = 1;
	uint32 key_combo;

	if ( pTricks->GetType() == ESYMBOLTYPE_STRUCTURE )
	{
		// grab the key combo and any spin or perfect modifiers from the struct
		Script::CStruct* pSubStruct = pTricks->GetStructure( trick_array_index );
		pSubStruct->GetChecksum( CRCD( 0x95e16467, "KeyCombo" ), &key_combo, Script::ASSERT );
		pSubStruct->GetInteger( CRCD( 0xa4bee6a1, "num_taps" ), &num_taps, Script::NO_ASSERT );
		pSubStruct->GetInteger( CRCD(0xa4bee6a1,"num_taps"), &*p_num_taps, Script::NO_ASSERT );
		*p_trick_name_checksum = get_trickname_checksum_from_key_combo( key_combo, num_taps );
		if ( pSubStruct->GetInteger( CRCD( 0xedf5db70, "spin" ), &*p_spin, Script::NO_ASSERT ) )
		{
			Dbg_MsgAssert( *p_spin % 180 == 0, ( "Bad spin value %i in gap tricks structure", *p_spin ) );
			*p_spin = *p_spin / 180;
			*p_require_perfect = pSubStruct->ContainsFlag( CRCD( 0x1c39f1b9, "perfect" ) );
		}
		// printf("\tlooking for key_combo %s\n", Script::FindChecksumName( key_combo ) );
	}
	else
	{
		Dbg_MsgAssert( pTricks->GetType() == ESYMBOLTYPE_NAME, ( "Tricks array has wrong type" ) );
		*p_trick_name_checksum = get_trickname_checksum_from_key_combo( pTricks->GetChecksum( trick_array_index ) );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Score::GetCurrentNumberOfOccurrences( Script::CArray* pTricks, int start_trick_index )
{
	Dbg_Message("DEPRECATED! Tell Brad if you need this.\n");
	return 0;


/*	Dbg_MsgAssert( start_trick_index <= m_currentTrick, ( "GetCurrentNumberOfOccurrences got bad start_trick_index of %i - m_currentTrick = %i", start_trick_index, m_currentTrick ) );
	
	int trick_array_size = pTricks->GetSize();
	int total = 0;
	
	// parse through the current list of tricks
	for ( int trick_array_index = 0; trick_array_index )
	{
		
	}
	for ( int i = start_trick_index; i < m_currentTrick; i++ )
	{
		// printf("\tcurrent trick = %s\n", Script::FindChecksumName( m_infoTab[i].id ) );
		int trick_array_index = 0;
		uint32 trick_name_checksum;
		int spin = 0;
		bool require_perfect = false;
		int num_taps = 0;
		GetTrickInfoFromTrickArray( pTricks, trick_array_index, &trick_name_checksum, &spin, &require_perfect, &num_taps );

		while ( VerifyTrickMatch( i + trick_array_index, trick_name_checksum, spin, require_perfect ) )
		{
			// printf("\tfound a match\n");
			// if we've matched as many tricks as are in the list, we know we've
			// found a complete match
			if ( trick_array_index == trick_array_size - 1 )
			{
				total++;
				break;
			}
			trick_array_index++;
			GetTrickInfoFromTrickArray( pTricks, trick_array_index, &trick_name_checksum, &spin, &require_perfect, &num_taps );
		}
	}
*/
	// printf("found %i occurrence(s)\n", total);
	// return total;
}

void	Score::RepositionMeters( void )
{
	setup_balance_meter_stuff();
}

void Score::ResetComboRecords()
{
	m_longestCombo = 0;
	m_bestCombo = 0;
	m_bestGameCombo = 0;
}

/******************************************************************/
/* Handle network scoring messages                                */
/*                                                                */
/******************************************************************/

int	Score::s_handle_score_message ( Net::MsgHandlerContext* context )
{
	GameNet::MsgEmbedded* p_msg = (GameNet::MsgEmbedded*) ( context->m_Msg );
	Mdl::Score* pScore = ((Obj::CSkater*) ( context->m_Data ))->GetScoreObject();
	
	switch ( p_msg->m_SubMsgId )
	{
		case GameNet::SCORE_MSG_ID_LOG_TRICK_OBJECT:
		{
			GameNet::MsgScoreLogTrickObject* log_msg = (GameNet::MsgScoreLogTrickObject*) ( context->m_Msg );
			GameNet::Manager* gamenet_man =  GameNet::Manager::Instance();

			if ( log_msg->m_GameId == gamenet_man->GetNetworkGameId())
			{
				pScore->LogTrickObject( log_msg->m_OwnerId, log_msg->m_Score, log_msg->m_NumPendingTricks, log_msg->m_PendingTrickBuffer, false );
			}
			break;
		}
	}
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Score::TrickIsGap( int trickIndex )
{
	Dbg_MsgAssert( trickIndex >= 0 && trickIndex < m_currentTrick, ( "trickIndex out of range" ) );
	return ( m_infoTab[trickIndex].flags & vGAP );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Score::set_special_is_active ( bool is_active )
{
	if (is_active != m_specialIsActive)
	{
		m_specialIsActive = is_active;
		
		Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkaterById(m_skaterId);
		if( pSkater )
		{
			pSkater->BroadcastEvent(m_specialIsActive ? CRCD(0xd0cd8081, "SkaterEnterSpecial") : CRCD(0x7ccf5ee2, "SkaterExitSpecial"));
		}
	}
}


void Score::reset_robot_detection()
{
	m_num_robot_landed = 0;
	m_num_robot_unique = 0;
	for (int i=0;i= MAX_ROBOT_NODES)
	{
		m_robot_rail_mult = 1.0f;
		return;
	}



	m_num_robot_landed++;
	m_num_robot_landed_combo++;
	if (m_robot_detection_count[node] == 0)
	{	
		m_num_robot_unique++;
		m_num_robot_first_combo++;
	}
	if (m_robot_detection_count[node] < 255)
	{
		m_robot_detection_count[node]++;
	}
	
	if (m_robot_detection_count_combo[node] == 0)
	{	
		m_num_robot_unique_combo++;
	}
	if (m_robot_detection_count_combo[node] < 255)
	{
		m_robot_detection_count_combo[node]++;
	}
	
//	GetRobotMult();

								  
	int 	kick_in = Script::GetInteger(CRCD(0x6ee127b7,"robot_kick_in_count"));							  

	if (m_robot_detection_count_combo[node] > kick_in)
	{
		int ramp = 20;
		// The first time you go over robot_kick_count, we want 1/2, everything before that will be 1.0f
		m_robot_rail_mult = 1.0f/(m_robot_detection_count_combo[node]-(kick_in)+1) * ( (float)(ramp + 1 - m_robot_detection_count[node])/(float)ramp);
		// Might go small or negative after a really long time, so clamp it
		m_robot_rail_mult = Mth::Max(0.1f, m_robot_rail_mult);	
	}	
	else
	{
		m_robot_rail_mult = 1.0f;
	}
	

}


float	Score::GetRobotMult()
{
	
	printf ("landed = %3d, unique = %3d .. ",m_num_robot_landed,m_num_robot_unique);
	printf ("combo landed = %3d, unique = %3d, first = %d\n",m_num_robot_landed_combo,m_num_robot_unique_combo,m_num_robot_first_combo);
	
//	float unique_ratio = (float)m_num_robot_unique_combo/(float)m_num_robot_landed_combo;
//	float first_ratio = (float)m_num_robot_first_combo/(float)m_num_robot_landed_combo;
//	float punish = 1.0f - 0.5f * logf(robot);
//	float reward = logf(m_num_robot_unique_combo) - logf(robot);
//	printf ("robot = %2.3f, punish = x%2.3f, reward = x%2.3f\n",robot, punish, reward);	

	int	min_robot_effect = 3;  // number of repetitions at which "robot" adjustment begins

	float sub = 0.0f;
	// Scale any effect by the size of the combo, beyond the minimun needed to take effect	
//	float robot_scale = 0.0f;
	if ((m_num_robot_landed_combo - m_num_robot_unique_combo) > min_robot_effect && m_num_robot_unique_combo < m_num_robot_landed_combo)
	{
#	if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
		float landed = m_num_robot_landed_combo - min_robot_effect;
		float dupes = m_num_robot_landed_combo - m_num_robot_unique_combo - min_robot_effect;
//		float first =  m_num_robot_first_combo;
		
//		robot_scale = 0.1f * logf(landed);   // with min of 10, 10=0, 50 =1.6, 100 = 1.95 
		// find an amount to subtract from a 1.0f multiplier, based on how repeditive this line is
//		float dupe_sub = (dupes/landed); 
//		float first_sub = 0.0f;	//0.05f * logf(landed - first); 
//		float sub = robot_scale * (dupe_sub + first_sub);
		float sub = dupes/landed;
//		printf ("(dupe = %2.3f + first = %2.3f) * scale %2.3f == sub %2.3f\n", dupe_sub, first_sub, robot_scale,  sub);
		printf ("sub = %2.3f\n",sub);
#endif		// __NOPT_FINAL__
	}
	
	float mult = 1.0f - sub;
	if (mult > 1.0f)
	{
		mult = 1.0f;
	}
	if (mult < 0.2f)
	{
		mult = 0.2f;
	}
	printf ("mult = %2.3f\n",mult);
	return mult;
}	



} // namespace HUD






================================================
FILE: Code/Sk/Modules/Skate/score.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2001 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:									 								**
**																			**
**	File name:																**
**																			**
**	Created by:		rjm, 1/23/2001											**
**																			**
**	Description:						 									**
**																			**
*****************************************************************************/

#ifndef __MODULES_SKATE_SCORE_H
#define __MODULES_SKATE_SCORE_H

#include 

#include 
#include 

#include 
#include 
#include 


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Net
{
	class	Conn;
	class	MsgHandlerContext;
}

namespace Mdl
{

						


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  Score  : public Spt::Class
{
	

public:
	
	typedef uint32 Flags;
	enum
	{
		vBLOCKING 		= nBit(1),
		vSWITCH			= nBit(2),
		vNOLLIE			= nBit(3),
		vGAP			= nBit(4),
		vSPECIAL		= nBit(5),
		vNODEGRADE		= nBit(6),
		vCAT			= nBit(7),
	};

	enum
	{
		MAX_ROBOT_NODES = 2500
	};
									Score();
									~Score();

	void 							Update();
	void 							SetSkaterId(short skater_id);

	void 							SetTotalScore( int score );
	int 							GetTotalScore();
	int 							GetLastScoreLanded() {return m_recentScorePot;}
	int 							GetScorePotValue() {return m_scorePot * m_currentMult;}  // current pot value
	bool 							GetSpecialState() {return m_specialIsActive;}
	void 							ForceSpecial( void );	

	uint32							GetLastTrickId(); // Added by Ken
	uint32							GetTrickId( int trickIndex );

	void							TrickTextPulse(int index); // Added by Ken so that replay can use it
	void							TrickTextCountdown(int index); // Added by Ken so that replay can use it
	void							TrickTextLanded(int index); // Added by Ken so that replay can use it
	void							TrickTextBail(int index); // Added by Ken so that replay can use it
	void 							Trigger(char *trick_name, int base_score, Flags flags = 0);
	void 							SetSpin(int spin_degrees );
	void 							UpdateSpin(int spin_degrees );
	void 							TweakTrick(int tweak_value );
	void 							Land( void );
	void 							Bail( bool clearScoreText = false );

	void 							Reset();

	Net::Conn* 						GetAssociatedNetworkConnection( void );
	void 							LogTrickObject( int skater_id, int score, uint32 num_pending_tricks, uint32* p_pending_tricks, bool propagate );
	void 							LogTrickObjectRequest( int score );

	void 							SetBalanceMeter(bool state, float value = 0.0f);
	void 							SetManualMeter(bool state, float value = 0.0f);
	void							RepositionMeters( void );

	int								GetNumberOfNonGapTricks();
	
	bool							IsLatestTrickByName( uint32 trick_text_checksum, int spin_mult, bool require_perfect, int num_taps );
	bool							IsLatestTrick( uint32 key_combo, int spin_mult, bool require_perfect, int num_taps );

    // counting occurrences of trick
    int                             GetPreviousNumberOfOccurrencesByName( uint32 trickTextChecksum, int spin_degrees = 0, int num_taps = 1 );
    int                             GetPreviousNumberOfOccurrences( uint32 trick_checksum, int spin_degrees = 0, int num_taps = 1 );
//	int								GetPreviousNumberOfOccurrences( Script::CArray* pTricks );
    
    int                             GetCurrentNumberOfOccurrencesByName( uint32 trickTextChecksum, int spin_degrees = 0, bool require_perfect = false, int num_taps = 1 );
    int                             GetCurrentNumberOfOccurrences( uint32 trick_checksum, int spin_degrees = 0, bool require_perfect = false, int num_taps = 1 );
	int                             GetCurrentNumberOfOccurrences( Script::CArray* pTricks, int start_trick_index = 0 );

	bool							VerifyTrickMatch( int info_tab_index, uint32 trick_checksum, int spin_degrees = 0, bool require_perfect = false );
	int								CountTrickMatches( uint32 trick_checksum, int spin_degrees = 0, bool require_perfect = false );
    int								CountStringMatches( const char* string );

	int								GetCurrentTrickCount() { return m_currentTrick; }
    int								GetCurrentMult() { return m_currentMult; }

	// track the best and longest combos
	int								GetLongestCombo() { return m_longestCombo; }
	int								GetBestCombo() { return m_bestCombo; }
	void							SetBestCombo( int best_combo ) { m_bestCombo = best_combo; }
	void							ResetComboRecords();
    void 							setSpecialBarColors();

	bool							TrickIsGap( int trickIndex );
	
	static int						s_handle_score_message ( Net::MsgHandlerContext* context );

	void							UpdateRobotDetection(int node);
	float							GetRobotMult();
	float							GetRobotRailMult() {return m_robot_rail_mult;}
	

private:

	void							reset_robot_detection();
	void							reset_robot_detection_combo();

	int								get_packed_score( int start, int end );
	void							pack_trick_info_table();
	void							print_trick_info_table();
	void 							copy_trick_name_with_special_spaces(char *pOut, const char *pIn);
	
    void 							captureScore();
	void 							resetScorePot();
	void							dispatch_trick_sequence_to_screen(bool clear_text=false);
	int 							spinMult(int x);
	int 							deprecMult(int x);

	void							setup_balance_meter_stuff();
	void							position_balance_meter(bool state, float value, bool isVertical);

	void							dispatch_score_pot_value_to_screen(int score, int multiplier);
	
	void							check_high_score(int old_score, int new_score);
	
	void							set_special_is_active ( bool is_active );
	
	short							m_skaterId;

	enum EScorePotState
	{
		SHOW_ACTUAL_SCORE_POT,
		WAITING_TO_COUNT_SCORE_POT,
		SHOW_COUNTED_SCORE_POT,
	};
	
	int								m_totalScore;
	EScorePotState					m_scorePotState;
	int 							m_scorePot;
	int 							m_countedScorePot;
	int								m_scorePotCountdown;
	int    							m_recentScorePot;
	int    							m_recentSpecialScorePot;

	int								m_longestCombo;
	int								m_bestCombo;
	int								m_bestGameCombo;

	//HUD::TrickWindow*				mp_trickWindow;

	struct TrickHistory
	{
		uint32						id;
		// there are 4 switch modes, so 4 entries
		int							total_count[4]; 	// done during session 
		int	 						combo_count[4];		// done	during combo
	};
	Lst::LookupTable	m_historyTab;
	
	struct TrickInfo
	{
		enum
		{
			TEXT_BUFFER_SIZE		= 64,
		};
		
		int							score;
		int 						mult_index;
		int 						spin_mult_index;
		int							spin_degrees;
		uint32						id;
		uint32						switch_mode;
		Flags						flags;
		char						trickNameText[TEXT_BUFFER_SIZE];
		char						trickTextFormatted[TEXT_BUFFER_SIZE];
	};
	// keeps track of tricks done in current sequence
	Lst::DynamicTable	m_infoTab;
	int								m_currentTrick;	 // Tricks cap out at 250
	int								m_currentMult;	 // but multiplier will keep going
	// Indicates most recent blocking trick
	int								m_currentBlockingTrick;
	// Most recent spin trick. Must come after a blocking trick.
	// Can be a blocking trick itself, but will be treated differently
	// if so.
	int								m_currentSpinTrick;

	int								m_specialScore;
	bool							m_specialIsActive;

	enum
	{
		REGULAR_SPECIAL	=			0,
		SPECIAL_SPECIAL
	};
	Image::RGBA						m_special_rgba[3];
	float							m_special_interpolator;
	float							m_special_interpolator_rate;

	bool 							m_debug;

	// Balance meter stuff -- here for now
	int								m_numArrowPositions;
	Script::CPair 					m_arrowPosTab[20];
	float							m_arrowInterval;
	Script::CPair					m_arrowPos;
	float							m_arrowRot;
	Script::CPair					m_meterPos[2];
	
	uint8							m_robot_detection_count[MAX_ROBOT_NODES];
	uint8							m_robot_detection_count_combo[MAX_ROBOT_NODES];
	int								m_num_robot_landed;
	int								m_num_robot_landed_combo;
	int								m_num_robot_unique;
	int								m_num_robot_first_combo;
	int								m_num_robot_unique_combo;
	float							m_robot_rail_mult;
	
};



} // namespace Mdl

#endif	// __MODULES_SKATE_SCORE_H




================================================
FILE: Code/Sk/Modules/Skate/skate.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Skate Module (SKATE) 									**
**																			**
**	File name:		modules/skate.cpp										**
**																			**
**	Created by:		06/07/2000	-	spg										**
**																			**
**	Description:	Skate Module											**
**																			**
*****************************************************************************/

// start autoduck documentation
// @DOC skate
// @module skate | None
// @subindex Scripting Database
// @index script | skate

// define this to call the movie updatign from the Skate::Mdl update, instead of the fronend up[date
#define	__MOVIES_FROM_SKATE_UPDATE__	

                           
/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
    #include 
#endif
#endif

#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef TESTING_GUNSLINGER
#include 
#endif

#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

extern bool	skip_startup;


namespace Front
{
	extern void SetTimeTHPS4(int minutes, int seconds);
    extern void HideTimeTHPS4();
}

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Mdl
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

DefineSingletonClass( Skate, "Skate Module" );

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**							   Private Classes								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

// When going into observer mode the local skater gets destroyed, and is later
// restored from his profile. The created tricks are not stored in the profile,
// so we are storing them out seperately.
static Script::CStruct *spCATData=NULL;
static Script::CStruct *spStatData=NULL;

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Skate::s_logic_code ( const Tsk::Task< Skate >& task )
{
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
    
    Dbg_AssertType ( &task, Tsk::Task< Skate > );

	Skate&	mdl = task.GetData();

	mdl.DoUpdate();

	if( gamenet_man->OnServer() && ( mdl.GetNumSkaters() > 0 ))
	{
		mdl.CheckSkaterCollisions();
	}

	if ( mdl.GetGameMode()->IsFrontEnd() )
	{
	}
	else if( ( mdl.GetGameMode()->GetNameChecksum() == CRCD(0x6ef8fda0,"netking") ) ||
			 ( mdl.GetGameMode()->GetNameChecksum() ==  CRCD(0x5d32129c,"king") ))
	{
        if( !mdl.GetGameMode()->EndConditionsMet())
		{
			GameNet::PlayerInfo* king;
			
			king = gamenet_man->GetKingOfTheHill();
			if( king && !king->m_Skater->IsPaused())
			{
				Mdl::Score* score;
				int current_score, total_score, goal_score;
	
				score = king->m_Skater->GetScoreObject();
				current_score = score->GetTotalScore();
				total_score = current_score + (int) (	( Tmr::FrameLength() * 60.0f ) * 
														( Tmr::vRESOLUTION / 60.0f ));
				score->SetTotalScore( total_score );

				goal_score = mdl.GetGameMode()->GetTargetScore();

				if( mdl.GetGameMode()->IsTeamGame())
				{
					total_score = gamenet_man->GetTeamScore( king->m_Team );
				}
				else
				{
					total_score = score->GetTotalScore();
				}

				// When within 10 seconds of victory, play a timer sound periodically
				mdl.BeepTimer( (float) ( goal_score - total_score) / 1000.0f,
							   10.0f,
							   10.0f,
							   "timer_runout_beep");
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Skate::v_start_cb ( void )
{
	// TODO:  Should assert that the top-down heap is empty
	// before the permanent prefiles are loaded
//	Script::RunScript( "DumpHeaps" );
	
	// these prefiles will sit on the top-down heap permanently
	// this is done before other stuff gets put on the top-down
	// heap
	Script::RunScript( "load_permanent_prefiles" );

	m_stat_override = 0.0f;
	   
	Mlp::Manager * mlp_manager =  Mlp::Manager::Instance(); 
                           
	Obj::Proxim_Init();						//
	Obj::CEmitterManager::sInit();

	m_logic_task->SetMask(1<<1);
												   
    // Initialise logic and display tasks
	mlp_manager->AddLogicTask ( *m_logic_task );
    
	GetGameMode()->Reset();

    Script::RunScript("init_loading_bar");
    Script::RunScript("startup_loading_screen");

	// By default we are both client and server
	//gamenet_manager->m_Flags.SetMask( GameNet::mSERVER | GameNet::mCLIENT );
    
	// Run the script for loading sounds that are permanently loaded into sound RAM...
    Script::RunScript("LoadPermSounds");

	// Run the script for loading the skater animations
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	
	Mem::PushMemProfile("Permanent Assets");
	Script::RunScript( "load_permanent_assets" );
	Mem::PopMemProfile(/*"Permanent Assets"*/);

	Mem::Manager::sHandle().PopContext();

	// initialize the pro skater profiles here
	GetPlayerProfileManager()->Init();

	// add the trick checksums used to the lookup table
	mp_trickChecksumTable->Init();

	Script::RunScript("default_system_startup");

	if ( !Config::CD() )
	{
		if ( !skip_startup )
		{
			// Run the personal startup script.
			Script::RunScript("Call_Personal_StartUp_Script");
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::v_stop_cb ( void )
{
    m_logic_task->Remove();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::HideSkater( Obj::CSkater* skater, bool should_hide )
{
	if( should_hide )
	{
		skater->Pause();
	}
	else
	{
		skater->UnPause();
	}

	skater->Hide( should_hide );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int				Skate::GetNextUnusedSkaterHeapIndex( bool for_local_skater )
{
	int start_index, heap_index, num_tested;
	bool taken;

	// Ok... kinda complicated, but it was the simplest solution I could come up with that solves the problem.
	// The issue is that we only have vMAX_SKATERS skater heaps.  And we always want to reserve skater heap 0 
	// for our local skater.  But we might be observing a game that has vMAX_SKATERS, in which case you will
	// not have a heap for the local skater at all.  But if we observe a server that has ( vMAX_SKATERS - 1 )
	// skaters and join late, we want to make sure that our skater (the last one to be loaded) gets skater 
	// heap 0
	start_index = 1;
	if( for_local_skater )
	{
		start_index = 0;
	}

	heap_index = start_index;
	for( num_tested = 0; num_tested < (int) GetGameMode()->GetMaximumNumberOfPlayers(); num_tested ++ )
	{
		taken = false;
		for( uint i = 0; i < GetNumSkaters(); i++ )
		{
			if( m_skaters.GetItem(i) != NULL )
			{
				Obj::CSkater *pSkater = m_skaters.GetItem(i)->GetData();
				if( pSkater->GetHeapIndex() == heap_index )
				{
					taken = true;
					break;
				}
			}
		}
		if( taken == false )
		{
			break;
		}

		heap_index = ( heap_index + 1 ) % GetGameMode()->GetMaximumNumberOfPlayers();
	}
	
	// We assume that these skater heaps are totally empty
	// so if something is aleft on them, then assert

#ifdef	__NOPT_ASSERT__	
	Mem::Heap *skater_heap = Mem::Manager::sHandle().SkaterHeap(heap_index);
	Dbg_MsgAssert( skater_heap != NULL, ( "Invalid skater heap : %d\n", heap_index ));
	if (skater_heap->mUsedBlocks.m_count)
	{
		printf ("Skater heap has %d used blocks still on it, it should be empty\n",skater_heap->mUsedBlocks.m_count);
#ifndef __PLAT_NGC__
		MemView_AnalyzeHeap(skater_heap);
		MemView_DumpHeap(skater_heap);
#endif		// __PLAT_NGC__
		Dbg_MsgAssert(0,("Skater heap has %d used blocks still on it, it should be empty\n",skater_heap->mUsedBlocks.m_count));
	}

	Mem::Heap *geom_heap = Mem::Manager::sHandle().SkaterGeomHeap(heap_index);
	if (geom_heap->mUsedBlocks.m_count)
	{
		printf ("SkaterGeom heap has %d used blocks still on it, it should be empty\n",geom_heap->mUsedBlocks.m_count);
#ifndef __PLAT_NGC__
		MemView_AnalyzeHeap(geom_heap);
		MemView_DumpHeap(geom_heap);
#endif		// __PLAT_NGC__
		Dbg_MsgAssert(0,("SkaterGeom heap has %d used blocks still on it, it should be empty\n",geom_heap->mUsedBlocks.m_count));
	}
	
	#endif
	

	return heap_index;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CSkater*	Skate::add_skater ( Obj::CSkaterProfile* pSkaterProfile, bool local_client, int obj_id, int player_num )
{
	Lst::Node< Obj::CSkater > *node;
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	int heap_index;
    
	// GJ:  copy the face texture (if it has one) from the local client's skater profile
	// we need to do this because the code that sends the face texture data as a separate
	// network message hasn't been implemented yet.
	//Dbg_MsgAssert( local_client, ( "Gary's kludge for face textures only works on local client" ) );
	if ( local_client )
	{
		Gfx::CModelAppearance* pModelAppearance = pSkaterProfile->GetAppearance();
		Obj::CSkaterProfile* pOriginalProfile;
		if (CFuncs::ScriptInSplitScreenGame( NULL, NULL ))
		{
			// (Mick) Kluge modified for 2 players
			// get the correct profile
			pOriginalProfile = mp_PlayerProfileManager->GetProfile( player_num );
		}
		else
		{
			// Otherwise, use the old kluge
			pOriginalProfile = mp_PlayerProfileManager->GetProfile( 0 );
		}
		
		
		Gfx::CModelAppearance* pOriginalAppearance = pOriginalProfile->GetAppearance();

		*pModelAppearance = *pOriginalAppearance;
	}
	
	// Change our ID to what the server says it should be to avoid ID collision amongs clients
	if( local_client )
	{
		Obj::CSkater* skater;

		Script::RunScript( "show_panel_stuff" );
		skater = GetLocalSkater();
		if( skater )
		{   
			// In Split-screen games, we'll have two local skaters
			if( CFuncs::ScriptInSplitScreenGame( NULL, NULL ))
			{
				if( skater->GetID() == (uint32) obj_id )
				{
					return skater;
				}
#ifdef __PLAT_NGC__
				else
				{
					File::PreMgr* pre_mgr = File::PreMgr::Instance();
					pre_mgr->LoadPre( "skaterparts.pre", false);
				}
#endif
			}
			else
			{
				// In Net games, we'll only have one, but his ID can change in order to avoid
				// ID collision over the net.  So, if someone is trying to add another local skater with a different
				// ID, assume that they just want to assign a new ID to our already-existing local skater
				if( skater->GetID() != (uint32) obj_id )
				{   
					Score* score;
	
					node = skater->GetLinkNode();
					node->SetPri( obj_id );
					node->Remove();
					m_skaters.AddUniqueSequence( node );
	
////////////////////////////////////////////////////////////////////////////////////					
// Changing the id means we have to destroy and re-create the particle systems
// as they are referenced by the id of the skater.					
					Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterHeap(skater->GetHeapIndex()));
					Script::RunScript( "DestroySkaterParticles", NULL, skater );

					skater->SetID( obj_id );
					
					Script::RunScript( "InitSkaterParticles", NULL, skater );
					Mem::Manager::sHandle().PopContext();
////////////////////////////////////////////////////////////////////////////////////					
					
					score = skater->GetScoreObject();
					score->SetSkaterId( obj_id );
				}
				
				return skater;
			}
		}
		else
		{
			Obj::CCompositeObject* cam_obj;

			// If we're about to re-create the skater, delete the skatercam as it is about to
			// be re-created.  This is to handle the case where someone goes into observer mode.
			// In this case, we destroy the skater, but leave the camera. So now we're going back
			// to the front end and it's re-creating the skater.
			cam_obj = (Obj::CCompositeObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0x967c138c,"skatercam0"));
			if( cam_obj )
			{
				cam_obj->SetLockOff();
				delete cam_obj;
			}
		}
	}
	else
	{
		// Stop vibration when loading client skaters in net games
		if ( gamenet_man->InNetGame() )
		{
			Obj::CSkater* p_skater;

			p_skater = GetLocalSkater();
			if( p_skater )
			{
				Obj::CVibrationComponent* p_vibration_component = GetVibrationComponentFromObject(p_skater);
				if (p_vibration_component)
				{
					p_vibration_component->StopAllVibration();
				}
			}
		}
	}

	if ( gamenet_man->InNetGame() )
	{
		Dbg_MsgAssert( GetNumSkaters() < GetGameMode()->GetMaximumNumberOfPlayers(),( "Trying to add too many players %d : %d", GetNumSkaters(), GetGameMode()->GetMaximumNumberOfPlayers()));
	}

    Mem::PushMemProfile("Skater"); // Could possibly replace this with the name of the skater, when we have one, so can compare in multi player

		
	Dbg_MsgAssert(( obj_id >= 0 ) && ( obj_id < vMAX_SKATERS ),( "Invalid skater object id\n" ));
	
	heap_index = GetNextUnusedSkaterHeapIndex( local_client );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterHeap(heap_index));

	Obj::CSkater* skater;

	Dbg_MsgAssert( pSkaterProfile,( "No profile specified" ));

	// Garrett: We may need to pause the music here if we get glitches on the network server
	//Pcm::PauseMusic(1);

	skater = new Obj::CSkater ( player_num, GetObjectManager(),
								local_client, obj_id, heap_index, !GetGameMode()->IsFrontEnd() );
	//Pcm::PauseMusic(0);
	skater->Init(pSkaterProfile);

	// Initialize the skater's special items
	if( !skater->IsLocalClient())
	{
		Script::RunScript( "ClientSkaterInit", NULL, skater );
	}

    // This bit of code will order our skaters in the linked list according to their object id
	// so the order will be consistent across all clients
	node = skater->GetLinkNode();
	node->SetPri( skater->GetID() );
	m_skaters.AddUniqueSequence( node );

	if ( GetGameMode()->IsFrontEnd() )
	{
		// front end doesn't have a server, so we need
		// to treat it as a special case.
		skip_to_restart_point( skater );
		Script::RunScript("reset_skateshop_skater");
	}
	
    // check if we should bind this skater to a different controller
	// printf("skaterIndex = %i, player_num = %i\n", mp_controller_preferences[player_num].skaterIndex, player_num);
	UpdateSkaterInputHandlers();

    // restore CAT's
    if( skater->IsLocalClient() && ( skater->GetSkaterNumber() == 0 ))
    {
        if (spCATData)
        {
            printf("CAT: Restoring Created Tricks\n");
            skater->LoadCATInfo(spCATData);
            delete spCATData;
    		spCATData=NULL;
        }
        
        // restore stat goal data too!
        if (spStatData)
        {
            printf("CAT: Restoring Stat Goal data\n");
            skater->LoadStatGoalInfo(spStatData);
            delete spStatData;
    		spStatData=NULL;
        }
    }

    // update trick mappings if this is the second skater in a split screen game
	if ( skater->GetID() == 0x00000001 && CFuncs::ScriptInSplitScreenGame( NULL, NULL ) )
	{
		// Obj::CPlayerProfileManager* pProfileMan = GetPlayerProfileManager();
		// Obj::CSkaterProfile* pProfile = pProfileMan->GetProfile( 1 );
		
		Obj::CTrickComponent* p_trick_component = GetTrickComponentFromObject(skater);
		Dbg_Assert(p_trick_component);
		p_trick_component->UpdateTrickMappings( pSkaterProfile );
	}

	Mem::PopMemProfile(/*"Skater"*/);
	Mem::Manager::sHandle().PopContext();

    return skater;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::GetNextStartingPointData( Mth::Vector* pos, Mth::Matrix* matrix, int obj_id )
{   
    int node;

	pos->Set( 0.0f, 0.0f, 0.0f, 0.0f );
	matrix->Ident();

	node = find_restart_node( obj_id );

    // ...and jump to it
	if (node != -1)
	{
		Mth::Vector rot;

		printf ("Got restart node %d\n",node);
		
		SkateScript::GetAngles( node, &rot );
		if ( rot[ X ] || rot[ Y ] || rot[ Z ] )
		{
			// 3DSMAX_ANGLES Mick: 3/19/03 - Changed all rotation orders to X,Y,Z
			matrix->RotateX( rot[ X ] );
			matrix->RotateY( rot[ Y ] );
			matrix->RotateZ( rot[ Z ] );
		}

		SkateScript::GetPosition( node, pos );
	}
}

/******************************************************************/
/* Find the zero-based restart node. So index 0 is Player1		  */
/*                                                                */
/******************************************************************/

int Skate::find_restart_node( int index )
{
	int node = -1;
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	// get appropriate restart point...
	if( IsMultiplayerGame())
	{
		if( GetGameMode()->IsTeamGame())
		{
			if( GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netlobby" ))
			{
				node = Obj::GetRestartNode( Script::GenerateCRC( "team" ), 0 );
			}
			else if( GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netctf" ))
			{
				GameNet::PlayerInfo* player;
				
				player = gamenet_man->GetLocalPlayer();
				if( player )
				{
					switch( player->m_Team )
					{
						case GameNet::vTEAM_RED:
							node = SkateScript::FindNamedNode( "TRG_CTF_Restart_Red" );
							break;
						case GameNet::vTEAM_BLUE:
							node = SkateScript::FindNamedNode( "TRG_CTF_Restart_Blue" );
							break;
						case GameNet::vTEAM_GREEN:
							node = SkateScript::FindNamedNode( "TRG_CTF_Restart_Green" );
							break;
						case GameNet::vTEAM_YELLOW:
							node = SkateScript::FindNamedNode( "TRG_CTF_Restart_Yellow" );
							break;
					}
				}
			}
			else
			{
				index = gamenet_man->GetSkaterStartingPoint( index );
				node = Obj::GetRestartNode( Script::GenerateCRC( "Multiplayer" ), index );
				// if we did not find one, then try the first generic restart
				if (node == -1)
				{
					printf ("\n\nWARNING - no multiplayer restart point, trying player1\n\n");
					// If no Playern, then try player1
					node = Obj::GetRestartNode( Script::GenerateCRC( "Player1" ), 0 );
				}
			}
		}
		// In the skateshop, use the Player1, Player2 restart points
		else if( GetGameMode()->IsFrontEnd())
		{
			char buff[32];

			sprintf( buff, "Player%d", index + 1 );
			node = Obj::GetRestartNode( Script::GenerateCRC( buff ), 0 );
		}
		else if ( GetGameMode()->IsTrue( "is_horse" ) )
		{
			// grab the current horse node
			node = Obj::GetRestartNode( Script::GenerateCRC( "horse" ), GetHorse()->GetCurrentRestartIndex() );
		}
		else
		{
			index = gamenet_man->GetSkaterStartingPoint( index );
			node = Obj::GetRestartNode( Script::GenerateCRC( "Multiplayer" ), index );
			// if we did not find one, then try the first generic restart
			if (node == -1)
			{
				printf ("\n\nWARNING - no multiplayer restart point, trying player1\n\n");
				// If no Playern, then try player1
				node = Obj::GetRestartNode( Script::GenerateCRC( "Player1" ), 0 );
			}
		}
	}
	else
	{
		node = Obj::GetRestartNode( Script::GenerateCRC( "Player1" ), index );
	}
		
	// If No player 1, then use generic
	if (node == -1)
	{		
		printf ("\n\nWARNING - no Player1 restart point, trying Generic\n\n");
		node = Obj::GetRestartNode( 0, 0 );
	}

	// If No player 1, then use generic
	if( node == -1 )
	{
		printf( "\n\nWARNING - No restart points found\n\n" );
	}

	return node;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::skip_to_restart_point( Obj::CSkater* skater, int node, bool walk )
{
	if (node == -1)
	{
		node = find_restart_node( skater->GetID() );
	}

	// ...and jump to it
	if (node != -1)
	{
		skater->SkipToRestart( node, walk );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::move_to_restart_point( Obj::CSkater* skater )
{
	skip_to_restart_point(skater, -1);
	
/*  Dan: MoveToRestart is deprecated.  We'll use skip instead.
	int node;
	
	node = find_restart_node( skater->GetID() );
    
	if (node != -1)
	{
		skater->MoveToRestart(node);   					   
	}
*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Skate::remove_skater( Obj::CSkater* skater )
{   	
	Dbg_Assert( skater );

    // (Mick) Now check for bailboard, and kill it
	// Bit of a patch really, but a fairly safe high level one.
	// (need a more general solution for killing model that has
	// instances...)
	
	Obj::CCompositeObject*p_bailboard = (Obj::CCompositeObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0x884ef81b, "bailboard") + skater->GetID());
	if( p_bailboard )
	{
		p_bailboard->MarkAsDead();
		Obj::CCompositeObjectManager::Instance()->FlushDeadObjects();
	}
	
	// destroy any car the non-local skater might have
	if( !skater->IsLocalClient() )
	{
		Script::CStruct* p_params = new Script::CStruct;
		p_params->AddChecksum(CRCD(0x5b24faaa, "SkaterId"), skater->GetID());
		Script::RunScript(CRCD(0x5b3fdccb, "remove_car_from_non_local_skater"), p_params);
		delete p_params;
	}

    // save off CAT params
    if( skater->IsLocalClient() && ( skater->GetSkaterNumber() == 0 ))
    {
        if (spCATData)
    	{
    		Dbg_Assert( 0 );
            delete spCATData;
    		spCATData=NULL;
    	}
        
        printf("CAT: Storing Created Trick data\n");
        spCATData=new Script::CStruct;
        skater->AddCATInfo(spCATData);

        // save stat goal info too...
        if (spStatData)
    	{
    		Dbg_Assert( 0 );
            delete spStatData;
    		spStatData=NULL;
    	}
        
        printf("CAT: Storing Stat Goal data\n");
        spStatData=new Script::CStruct;
        skater->AddStatGoalInfo(spStatData);
    }
    
	skater->GetLinkNode()->Remove();
	//Obj::Unlock( skater );
	//Obj::Destroy( skater );
	delete skater;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Skate::SkatersAreIdle()
{
	 //GameNet::Manager* gamenet_man = GameNet::Manager::Instance();

#ifdef __USER_GARY__
	// GJ:  this assert was firing off for some reason.
	// it doesn't seem to have any negative impact, but i'd
	// like to look at it later, when i've got more time
//	Dbg_Assert( GetGameMode()->EndConditionsMet() );
#endif

	bool all_idle = true;

	for ( uint32 i = 0; i < GetNumSkaters(); i++ )
	{
		Obj::CSkater* pSkater = GetSkater( i );
		Dbg_Assert( pSkater );

		if ( GetGameMode()->IsTrue( "is_horse" ) )
		{
			// only need to check the current horse skater
			if ( GetHorse()->GetCurrentSkaterId() != (int) pSkater->GetID() )
			{
				continue;
			}
		}

		Obj::CSkaterEndRunComponent* p_skater_endrun_component = GetSkaterEndRunComponentFromObject(pSkater);
		Dbg_Assert(p_skater_endrun_component);
		if ( !p_skater_endrun_component->RunHasEnded() )
		{
			all_idle = false;
			if( pSkater->IsLocalClient())
			{
				p_skater_endrun_component->EndRun();
			}                     
		}
	}
	
	// In network games, don't base it on whether skater objects are idle as this is unreliable.
	// Instead, base it on "GameIsOver()" which uses reliable transport messages
	/*if( gamenet_man->InNetGame())
	{
		return gamenet_man->GameIsOver();
	}*/

	return all_idle;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Skate::FirstTrickStarted()
{
	// you should only call this function in horse mode
	Dbg_Assert( GetGameMode()->IsTrue("is_horse") );
	
	Obj::CSkater* pSkater = GetSkaterById( GetHorse()->GetCurrentSkaterId() );
	Dbg_Assert( pSkater );
															  
	Obj::CTrickComponent* pTrickComponent = GetTrickComponentFromObject(pSkater);
	Dbg_Assert( pTrickComponent );

	if ( pTrickComponent->FirstTrickStarted() )
	{
		// if the trick has started, then end the run, yo!
		Obj::CSkaterEndRunComponent* p_skater_endrun_component = GetSkaterEndRunComponentFromObject(pSkater);
		Dbg_Assert(p_skater_endrun_component);
		p_skater_endrun_component->EndRun();
	}

	return pTrickComponent->FirstTrickStarted();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Skate::FirstTrickCompleted()
{
	// you should only call this function in horse mode
	Dbg_Assert( GetGameMode()->IsTrue("is_horse") );
	
	Obj::CSkater* pSkater = GetSkaterById( GetHorse()->GetCurrentSkaterId() );
	Dbg_Assert( pSkater );
	
	Obj::CTrickComponent* pTrickComponent = GetTrickComponentFromObject(pSkater);
	Dbg_Assert( pTrickComponent );
	
	return pTrickComponent->FirstTrickCompleted();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::PauseGameFlow( bool paused )
{
	

	Dbg_AssertPtr( mp_gameFlow );
	mp_gameFlow->Pause( paused );	
}

//////////////////////////////////////////////////////////////////////
// void Skate::BeepTimer(float time, float beep_time, float beep_speed, const char *p_script_name)
// given a time, run a script if that time is less than beep_time, and again, with increasing
// frequency.  Note use of static vars, means this is a single use fn.  Intetended for a single timer
// time = time remaining in seconds
// beep_time = time at which to start beeping
// beep_speed = relative speed at whcih beeps increase 10.0 is good
// p_script_name = script to run for each beep
//
// example:
//
//			BeepTimer(time,10.0f, 10.0f,"timer_runout_beep");			
//

void Skate::BeepTimer(float time, float beep_time, float beep_speed, const char *p_script_name)
{
	static float next_beep_time;
	if (time > 0.01f && time <= beep_time)
	{
		if (timeCheckModelActive();
	}
}
 

void Skate::DoUpdate()
{

#ifdef	__MOVIES_FROM_SKATE_UPDATE__	
	if ( !Mdl::FrontEnd::Instance()->GamePaused() && !Mdl::CViewer::sGetViewMode() )
	{
		if ( mp_movieManager->IsRolling() )
		{
			if ( mp_movieManager->GetActiveCamera() )
			{
				// hardcoded to viewport 0...  if we need the other
				// viewports to play movies then we can store it
				// in the details...
				Nx::CViewportManager::sSetCamera( 0, mp_movieManager->GetActiveCamera() );
			}

			// hides the loading screen, if any
			if ( m_lastFrameWasMovieCamera )
			{
				Script::RunScript( CRCD(0xc5c9373a,"hide_loading_screen") );
			}
			mp_movieManager->Update();
			m_lastFrameWasMovieCamera = true;
		}
		else
		{
			// the camera just finished
			if ( m_lastFrameWasMovieCamera )
			{
				Script::RunScript( CRCD(0x15674315,"restore_skater_camera") );
				m_lastFrameWasMovieCamera = false;
			}
		}
		
		// play any extra object anims that might be playing
		// (this might be phased out later)
		if ( mp_objectAnimManager->IsRolling() )
		{
			mp_objectAnimManager->Update();
		}
	}
#endif
	
	
	
	// Fills in a little structure with some stuff so that SkipLogic is really fast.
	pre_calculate_object_update_info();
	
	// called once per frame
	Script::RunScript( CRCD(0x1aef674f,"Game_Update") );

	// Mick: PATCH																		 
	// if the game is paused, then manually update the visibility of the game object
	// as we might be moving the camera around (Like in the view-goals screen)
	if ( Mdl::FrontEnd::Instance()->GamePaused())
	{
		GetObjectManager()->ProcessAllObjects(do_CheckModelActive);
	}

// 

	#if 1
	
	// Temp hack, for each viewport, run proxim-update on it, hooking it up to the appropiate camera
	
	// Perhaps a better solution would be to have a non-suspendable component
	// that is attached to the skater, and grabs the viewport/camera itself
	// But what about when the game is paused?	
	if( GetLocalSkater())
	{
		int viewports = Nx::CViewportManager::sGetNumActiveViewports();
		switch (viewports)
		{
			case 1:
				if (Nx::CViewportManager::sGetActiveCamera(0))
				{
					Proxim_Update(1<<0, GetLocalSkater(), Nx::CViewportManager::sGetActiveCamera(0)->GetPos());
				}
				else
				{
					#ifdef	__NOPT_ASSERT__
					printf ("WARNING: 1 viewport, but no active camera 0\n");
					#endif
				}
				break;
			case 2:
				if (Nx::CViewportManager::sGetCamera(0))
				{
					Proxim_Update(1<<0, Obj::CCompositeObjectManager::Instance()->GetObjectByID(0), Nx::CViewportManager::sGetCamera(0)->GetPos());
				}
				// Need to check that the second skater exists, as well as the viewport
				if (Nx::CViewportManager::sGetCamera(1) && Obj::CCompositeObjectManager::Instance()->GetObjectByID(1))
				{
					Proxim_Update(1<<1, Obj::CCompositeObjectManager::Instance()->GetObjectByID(1), Nx::CViewportManager::sGetCamera(1)->GetPos());
				}
				break;
			default:
				Dbg_MsgAssert(0,("Don't handle %d viewport proxim updating",viewports));
		}
	}
	else if (CFuncs::ScriptIsObserving(NULL, NULL))		// TT12029: Run without object in observer mode
	{
		int viewports = Nx::CViewportManager::sGetNumActiveViewports();
		switch (viewports)
		{
			case 1:
				if (Nx::CViewportManager::sGetActiveCamera(0))
				{
					Obj::Proxim_Update(1<<0, NULL, Nx::CViewportManager::sGetActiveCamera(0)->GetPos());
				}
				else
				{
					#ifdef	__NOPT_ASSERT__
					printf ("WARNING: 1 viewport, but no active camera 0\n");
					#endif
				}
				break;
			default:
				Dbg_MsgAssert(0,("Don't handle %d viewport proxim updating in observer mode",viewports));
		}
	}
	else
	{
		#ifdef	__NOPT_ASSERT__
		// printf ("WARNING: No local skater, so no Proxim updates\n");
		#endif
	}
	#endif	




	
	if ( m_levelChanged )
	{
#ifdef __DEFERRED_CLEANUP_TEST__
		CFuncs::ScriptPrintMemInfo( NULL, NULL );
#endif
		m_levelChanged = false;
	}

	// don't think i need this anymore
	if (GetGameMode()->IsFrontEnd())
	{
		// if the front end is active, then don't update
		return;
	}

	// =================== TIMER STUFF ===========================
	// display the appropriate time if necessary
	// (we don't use or show a clock in certain game modes)
	Game::CGoalManager* pGoalManager = Game::GetGoalManager();
	if ( !Replay::RunningReplay() && pGoalManager->ShouldUseTimer() )
	{
		Tmr::Time time_left = (pGoalManager->GetGoalTime() + 999) / 1000;

		// int time_left = m_time_limit - Tmr::InSeconds( front_end->GetGameTime());

		if( time_left < 1 )
		{
			time_left = 0;
		}

		int seconds = time_left % 60;
		int minutes = time_left / 60;
		
		Front::SetTimeTHPS4(minutes, seconds);
		
		//m_panelMgr->SetTime(minutes, seconds);
		
/*		if (GetGameMode()->ShouldTimerBeep())
		{
			//float time = (float)m_time_limit - front_end->GetGameTime()/1000.0f;
			float time = float(time_left);
			BeepTimer(time,10.0f, 10.0f,"timer_runout_beep");			
		}
*/
	}
	else
	{
		Front::HideTimeTHPS4();
	}

	// make sure everyone's controller is still plugged in
#ifdef __PLAT_XBOX__
	int is_changing_levels = Script::GetInteger( CRCD(0xe997db9f,"is_changing_levels"), Script::ASSERT );
	int check_controllers = Script::GetInteger( CRCD(0x77a5662c,"check_for_unplugged_controllers"), Script::ASSERT );
	if ( check_controllers && !is_changing_levels )
	{
		if ( m_first_input_received )
		{
			Front::CScreenElementManager* p_screen_elem_man = Front::CScreenElementManager::Instance();
			Front::CScreenElement* p_root_window = p_screen_elem_man->GetElement( CRCD(0x56a1eae3,"root_window")  , Front::CScreenElementManager::DONT_ASSERT );
			if ( p_root_window )
			{
				// check that the box isn't already up
				Front::CScreenElement* p_error_box = p_screen_elem_man->GetElement( CRCD(0x2edb780f,"controller_unplugged_dialog_anchor") , Front::CScreenElementManager::DONT_ASSERT );
				if ( !p_error_box )
				{
					bool controller_unplugged = false;
					for ( int i = 0; i < vMAX_SKATERS; i++ )
					{
						Obj::CSkater* pSkater = GetSkater( i );
						if ( pSkater && pSkater->IsLocalClient() )
						{
							Inp::Handler< Obj::CInputComponent >* pHandler = pSkater->mp_input_component->GetInputHandler();
							if ( !pHandler->m_Device->IsPluggedIn() )
							{
								// m_controller_unplugged_frame_count++;
								controller_unplugged = true;
								if ( m_controller_unplugged_frame_count > Script::GetInteger( CRCD(0xf7fabb50,"controller_unplugged_frame_count"), Script::ASSERT ) )
								{
									m_controller_unplugged_frame_count = 0;
									Script::CStruct* pScriptParams = new Script::CStruct();
									GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
									if ( gamenet_man->InNetGame() )
									{
										Game::CGoalManager* pGoalManager = Game::GetGoalManager();
										Dbg_Assert( pGoalManager );
										pGoalManager->DeactivateAllGoals();
										pGoalManager->UninitializeAllGoals();

										pScriptParams->AddChecksum( NONAME, CRCD(0xeaa48e14,"leaving_net_game") );
										Skate::LeaveServer();
										if ( gamenet_man->OnClient() )
										{
											gamenet_man->CleanupPlayers();
											gamenet_man->ClientShutdown();
										}
									}
	
									// get the device that's unplugged
									int device_num = pHandler->m_Device->GetPort();
									
									pScriptParams->AddInteger( CRCD(0xc9428a08,"device_num"), device_num );
									Script::RunScript( CRCD(0xf77aca62,"controller_unplugged"), pScriptParams );
									delete pScriptParams;
	
									break;
								}
							}
						}
					}
					if ( controller_unplugged )
						m_controller_unplugged_frame_count++;
					else
						m_controller_unplugged_frame_count = 0;
				}
			}
		}
	}
#endif		// __PLAT_NGC__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::SetTimeLimit( int seconds )
{
	m_time_limit = seconds;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetTimeLimit | sets the time limit
// @parmopt int | seconds | 120 | number of seconds for time limit
bool ScriptSetTimeLimit(Script::CStruct *pParams, Script::CScript *pScript) {
    int seconds = 120;

    pParams->GetInteger( "seconds", &seconds );

    Skate* skate = Skate::Instance();

    skate->SetTimeLimit( seconds );
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Skate::GetTimeLimit( void )
{
	return m_time_limit;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::UpdateGameFlow()
{
	Dbg_Assert( mp_movieManager );

#ifndef	__MOVIES_FROM_SKATE_UPDATE__	
	if ( !Mdl::FrontEnd::Instance()->GamePaused() && !Mdl::CViewer::sGetViewMode() )
	{
		if ( mp_movieManager->IsRolling() )
		{
			if ( mp_movieManager->GetActiveCamera() )
			{
				// hardcoded to viewport 0...  if we need the other
				// viewports to play movies then we can store it
				// in the details...
				Nx::CViewportManager::sSetCamera( 0, mp_movieManager->GetActiveCamera() );
			}

			// hides the loading screen, if any
			if ( m_lastFrameWasMovieCamera )
			{
				Script::RunScript( CRCD(0xc5c9373a,"hide_loading_screen") );
			}
			mp_movieManager->Update();
			m_lastFrameWasMovieCamera = true;
		}
		else
		{
			// the camera just finished
			if ( m_lastFrameWasMovieCamera )
			{
				Script::RunScript( CRCD(0x15674315,"restore_skater_camera") );
				m_lastFrameWasMovieCamera = false;
			}
		}
		
		// play any extra object anims that might be playing
		// (this might be phased out later)
		if ( mp_objectAnimManager->IsRolling() )
		{
			mp_objectAnimManager->Update();
		}
	}
#endif

	Dbg_AssertPtr( mp_gameFlow );
	mp_gameFlow->Update();
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Skate::Skate ( void )
{   
	m_first_input_received = false;
	m_controller_unplugged_frame_count = 0;
	
	m_logic_task = new Tsk::Task< Skate > ( Skate::s_logic_code, *this );

	net_setup();

	mp_PlayerProfileManager = new Obj::CPlayerProfileManager;
	mp_PlayerProfileManager->Reset();

	mp_gameFlow = new CGameFlow;
	mp_goalManager = new Game::CGoalManager;

//    mp_obj_manager = new Obj::CGeneralManager;
	// Bit of a patch, but the engine now needs to be told what is managing the moving objects		
	// When Skate.cpp is broken up, then the mp_obj_manager needs extracting into some more generic "game object" manager
	// which Nx can query, if it really needs to													
	Nx::CEngine::sSetMovableObjectManager(GetObjectManager());

    mp_gameMode = new Game::CGameMode;
	mp_career = new Obj::CSkaterCareer;				// new local careeer
	
	mp_railManager = new Obj::CRailManager;

#	ifdef TESTING_GUNSLINGER
	mp_navManager = new Obj::CNavManager;
#	endif
	
	mp_trickChecksumTable = new Obj::CTrickChecksumTable;

	mp_trickObjectManager = new Obj::CTrickObjectManager;
	mp_trickObjectManager->DeleteAllTrickObjects();

	m_recording = false;
	m_playing = false;
	m_fd = 0;

	m_gameInProgress = false;
	m_levelLoaded = false;
	SetGameType( Script::GenerateCRC("freeskate") );

	// initialize the splitscreen preferences
    mp_splitscreen_preferences = new Prefs::Preferences;
	mp_splitscreen_preferences->Load( Script::GenerateCRC("default_splitscreen_preferences" ) );

	mp_competition = new CCompetition;
	mp_horse = new CHorse;
	mp_movieManager = new Obj::CMovieManager;
	mp_objectAnimManager = new Obj::CMovieManager;

	mp_gameRecords = new Records::CGameRecords(Obj::CSkaterCareer::vMAX_CAREER_LEVELS);

	m_shadow_mode = Gfx::vSIMPLE_SHADOW;
	
	// Initialise the controller preferences.
	// These might get changed by an autoload off the memory card.
	for (int i=0; i=0 && indexm_spin_taps=state;
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::SetAutoKick(int index, bool state)
{
	Dbg_MsgAssert(index>=0 && indexm_auto_kick=state;
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::SetVibration(int index, bool state)
{
	Dbg_MsgAssert(index>=0 && indexSetActiveState(state);
		}
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CSkater* 	Skate::GetLocalSkater( void )
{
	unsigned int i;

	for( i = 0; i < GetNumSkaters(); i++ )
	{
		if( m_skaters.GetItem(i) != NULL )
		{
			Obj::CSkater *pSkater = m_skaters.GetItem(i)->GetData();
			if( pSkater->IsLocalClient())
			{
				return pSkater;
			}
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CSkater 	*Skate::GetSkater( uint num )
{
	
	if( GetNumSkaters() <= num )
	{
		return NULL;
	}   
	else 
	{
		Dbg_Assert( m_skaters.GetItem( num ) );
        return m_skaters.GetItem( num )->GetData();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CSkater *Skate::GetSkaterById( uint32 id )
{
	for (uint i = 0; i < GetNumSkaters(); i++)
	{
		if( m_skaters.GetItem(i) != NULL )
		{
			Obj::CSkater *pSkater = m_skaters.GetItem(i)->GetData();
			if (pSkater->GetID() == id)
				return pSkater;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CGeneralManager*	Skate::GetObjectManager( void )
{
//	return mp_obj_manager;
	return	Obj::CCompositeObjectManager::Instance();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CTrickObjectManager*	Skate::GetTrickObjectManager( void )
{
	return mp_trickObjectManager;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CPlayerProfileManager*	Skate::GetPlayerProfileManager( void )
{
	return mp_PlayerProfileManager;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Prefs::Preferences* Skate::GetSplitScreenPreferences( void )
{
	return mp_splitscreen_preferences;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float Skate::GetHandicap( int id )
{
	Prefs::Preferences* pPreferences = GetSplitScreenPreferences();

	Script::CStruct* pStructure = NULL;
	
	switch ( id )
	{
		case 0:
			pStructure = pPreferences->GetPreference( Script::GenerateCRC("player1_handicap") );
			break;
		case 1:
			pStructure = pPreferences->GetPreference( Script::GenerateCRC("player2_handicap") );
			break;
		default:
			Dbg_MsgAssert( 0, ( "Out of range handicap id %d", id ) );
			break;
	}

	Dbg_Assert( pStructure );

	int val = 0;
	pStructure->GetInteger( "time", &val );	// stored in the time field...  ugly!
	return val;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// The Skate::Cleanup function is essentially cleaning up
// the "Session" objects.
// It does not unload any assets, just deletes objects that were valid for the session 

void	Skate::Cleanup()
{
	uint32 i;

	printf("Skate::Cleanup() - Deleting all session specific objects\n");

// Resetting the skater is complicated
// in that we have to lock him, to make sure he is not deleted
// and then reset him manually, as we can't just delete and re-create him (yet)	
	for (i=0;iSetLockOn();					// Sets the "lock" flag, so he won't get deleted by DestroyAllObjects
			if( skater->IsLocalClient())
			{
				skater->ResetPhysics(false, true);
				// stop any movies associated with each skater
				this->GetMovieManager()->ClearMovieQueue();
			}
			skater->DeleteResources();	   // blood, gap checkslistm shodow
			//skater->SwitchOffShadow();
			skater->RemoveFromCurrentWorld();
		}
	}

// Clear the movies used for moving object anims	
	GetMovieManager()->ClearMovieQueue();
	GetObjectAnimManager()->ClearMovieQueue();

	// clear out the viewer object, if any
	Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();
	if ( pViewer )
	{
		pViewer->RemoveViewerObject();   	
	}

// Destroy all level objects.  Basically anything created with the node array
	GetObjectManager()->DestroyAllObjects();
	GetObjectManager()->FlushDeadObjects();

// For now we destory all the composite objects here as well
//	Obj::CCompositeObjectManager::Instance()->UnlockAllObjects();
//	Obj::CCompositeObjectManager::Instance()->DestroyAllObjects();
//	Obj::CCompositeObjectManager::Instance()->FlushDeadObjects();


	// Now remove the lock on the skaters' objects
	for (i=0;iSetLockOff();					// Clears the "lock" flag
			skater->AddToCurrentWorld();
		}
	}

	// The gamenet managers objects (crown, compass... )
	// have now been destroyed so Nullify references to them
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	gamenet_man->CleanupObjects();

	// Little sanity check, to make sure everyhting except the skater is gone	
//	sint pTypes[]={SKATE_TYPE_SKATER,-1};
//	GetObjectManager()->AssertIfObjectsRemainApartFrom(pTypes);

	// unlock the skaters, as they will be the only remaining objects	
	//GetObjectManager()->UnlockAllObjects();

	// Delete any leftover spawned scripts.
	// (NB, might not want to do this, if the gameflow has spawned scripts?
	// but we probably do, as most of it will be session specific scripts	
	Script::DeleteSpawnedScripts();

	// Delete all the trick objects that were created for this session
	mp_trickObjectManager->DeleteAllTrickObjects();
	
	// destroy the list of gapchecks (checklist of all gaps in the level)
	// (maybe we would keep this from session to session?)
//	m_gapcheck_list.DestroyAllNodes();
		
	// Destroy the new style particle systems....
    // or not, as they are now all destroyed by the controlling objects, or the object manager
    // so, if we did destroy them here, then the blood splat would stop working.
	Nx::destroy_all_temp_particles( );

	// Destroy rails, they get re-created from the node array
	GetRailManager()->Cleanup();
	
	// Destroy Proximity nodes, similar to rails	
	Obj::Proxim_Cleanup();

	// Destroy EmitterObjects   
	Obj::CEmitterManager::sCleanup();

	// cleanup particles
	Dbg_Printf( "Destroying particle systems..." );
	Nx::CEngine::sGetParticleManager()->Cleanup();

	// This is a debugging function to see what's
	// currently in the anim cache (at this point,
	// only a handful of skater anims should be
	// in the cache)
//	Nx::PrintAnimCache();
}


// Cleaup stuff that might have been created in the park editor test play
void Skate::CleanupForParkEditor()
{
	mp_trickObjectManager->DeleteAllTrickObjects();
	Nx::KillAllTextureSplats();
	Nx::destroy_all_temp_particles( );
	GetRailManager()->Cleanup();
	Obj::Proxim_Cleanup();
	Obj::CEmitterManager::sCleanup();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::Pause( bool pause )
{
	
	if ( pause )
	{
		 Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
		sfx_manager->PauseSounds( );
//		Pcm::PauseMusic( 1 );
//		Pcm::PauseStream( 1 );
	}
	else
	{
		// unpause music if game is active:
		if ( IsGameInProgress( ) )
		{
//			Pcm::PauseMusic( 0 );
//			Pcm::PauseStream( 0 );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::RequestGameEnd()
{
	m_gameInProgress = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Game::CGameMode *Skate::GetGameMode(void)
{
	return mp_gameMode;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Game::CGoalManager* Skate::GetGoalManager(void)
{
	return mp_goalManager;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CSkaterProfile* Skate::GetCurrentProfile( Script::CStruct *pParams )
{
	Dbg_MsgAssert( mp_PlayerProfileManager,( "No skater profile manager\n" ));

	int skaterNum;
	if ( pParams && pParams->GetInteger( "skater", &skaterNum ) )
	{
		return mp_PlayerProfileManager->GetProfile( skaterNum );
	}
	else
	{
		return mp_PlayerProfileManager->GetCurrentProfile();
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CSkaterProfile* Skate::GetProfile( int skaterNum )
{
	Dbg_MsgAssert( mp_PlayerProfileManager,( "No skater profile manager\n" ));

	return mp_PlayerProfileManager->GetProfile( skaterNum );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::SetGameType(uint32 gameType)
{
// Don't set this right away...	
//	GetGameMode()->LoadGameType( gameType );
	m_requested_game_type = gameType;
				 
// Instead, remember it, and
// commit the change when we do the "setgamestate on"
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::SetCurrentGameType()
{
    printf("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<LoadGameType( m_requested_game_type );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::OpenLevel(uint32 level_script)
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
//	CloseLevel();

	Mem::PushMemProfile("LaunchLevel");


	if( gamenet_man->InNetGame())
	{
		if( gamenet_man->OnServer())
		{
			Lst::Search< GameNet::PlayerInfo > sh;
			GameNet::PlayerInfo* player;

			for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
			{
				player->MarkAsNotReady( 0 );
			}
		}
	}


	if (level_script == Script::GenerateCRC("custom_park"))
	{
        ;
	}
	else
	{
#ifdef	__NOPT_ASSERT__
		// Make sure it exists, otherwise we crash obscurely...
		Script::CSymbolTableEntry *p_entry=Script::Resolve(level_script);
		Dbg_MsgAssert(p_entry,("Level script %x (%s) not found",level_script,Script::FindChecksumName(level_script)));
#endif
		
		Script::RunScript(level_script);
	}

	m_levelLoaded = true;
	m_cur_level = level_script;

	if( gamenet_man->InNetGame())
	{
		int i;
		for( i = 0; i < GameNet::vMAX_LOCAL_CLIENTS; i++ )
		{
			Lst::Search< Net::Conn > sh;
			Net::Conn* server_conn;
			Net::Client* client;
	
			client = gamenet_man->GetClient( i );
			if( client )
			{
				server_conn = client->FirstConnection( &sh );
				if( server_conn )
				{
					server_conn->UpdateCommTime( Tmr::Seconds( 12 ));	// update the current comm time so it doesn't time out after
																		// loading the skater
				}
			}
		}

		if( gamenet_man->OnServer())
		{
			Lst::Search< Net::Conn > sh;
			Net::Conn* conn;
			Net::Server* server;

			server = gamenet_man->GetServer();
			if( server )
			{
				for( conn = server->FirstConnection( &sh ); conn; 
						conn = server->NextConnection( &sh ))
				{
					conn->UpdateCommTime( Tmr::Seconds( 8 ));	// update the current comm time so it doesn't time out after
																// loading the skater
				}
			}
		}
		else
		{
			// If we have some queued up new players, we'll respond to the ready query
			// after we finish loading them. Otherwise, if this is just a standard change level
			// call, tell the server we're finished loading it and ready to communicate
			Lst::Search< GameNet::NewPlayerInfo > sh;

			if( gamenet_man->FirstNewPlayerInfo( sh ) == NULL )
			{
				gamenet_man->RespondToReadyQuery();
			}
		}
	}

	Mem::PopMemProfile(/*"LaunchLevel"*/);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// RestartLevel is just intended to be called when a level is restarted
// it does not actually restart the level, it just sets a few flags
// and does something with disabling the the viewer log that's only related to screenshot mode 

void Skate::RestartLevel()
{
	// clear the end run flag
	ClearEndRun();
	
	// GJ:  in a network lobby, the skater is loaded AFTER the
	// level so this return wouldn't work...  come to think
	// of it, what's it needed for?
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	if ( !GetNumSkaters() && !gamenet_man->InNetGame() )
	{
		Ryan("BAD BAD BAD\n");
		return;
	}

	if (!m_levelLoaded)
	{
		Ryan("BAD BAD BAD\n");
		return;
	}

	// the game is now on
	if ( GetGameMode()->IsFrontEnd() )
	{
		// don't let the career timer go until
		// we're actually in a real level,
		// otherwise, we'll get "Requested MenuScreen
		// not active" asserts
		m_gameInProgress = false;

	}
	else
	{
		m_gameInProgress = true;

//		 Mdl::RwViewer * rwviewer_mod =  Mdl::RwViewer::Instance();
//		rwviewer_mod->DisableMainLogic( false );		// reset
	}

	if ( gamenet_man->InNetGame() )
	{
		#if 0
		// unpause the game if we're in the network lobby
		Mdl::FrontEnd* frontend = Mdl::FrontEnd::Instance();
		frontend->SetActive(false);
		#endif
	}

	// Mick:  tell the career mode that we are just starting this level
	// so it will remember what the flags were at the start of the level	
	GetCareer()->StartLevel();
	
	// print debug info
	GetTrickObjectManager()->PrintContents();	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::ChangeLevel( uint32 level_id )
{
	 //GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	// changes level while preserving the skaters

// Mick - moved this in ScriptCleanup
//    Game::CGoalManager* p_GoalManager = Game::GetGoalManager();
//    Dbg_MsgAssert( p_GoalManager, ( "couldn't get GoalManager\n" ) );
//    p_GoalManager->LevelUnload();
//    p_GoalManager->DeactivateAllGoals();
//    p_GoalManager->RemoveAllGoals();

	printf ("-----------------------------------------Skate::ChangeLevel(%d)\n",level_id);

	//uint32 i;
	Script::CStruct* pTempStructure;

	// TODO:  is this necessary?   since we push individual heaps below
	// maybe, if there is anyhting else that might allocate stuff 
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

	// requests the new level...
	pTempStructure = new Script::CStruct;
	pTempStructure->Clear();
	pTempStructure->AddComponent( Script::GenerateCRC("level"), ESYMBOLTYPE_NAME, (int)level_id );
	Script::RunScript( "request_level", pTempStructure );
	delete pTempStructure;

	m_prev_level = m_cur_level;
	m_cur_level=level_id;
	
	// removes the skaters from the existing world
	// (skate::cleanup func used to take care of this
	// but now it's in ScriptCleanup
	/*if( !gamenet_man->InNetGame())
	{
		for ( i = 0; i < GetNumSkaters(); i++ )
		{
			Obj::CSkater* pSkater = GetSkater( i );
			pSkater->RemoveFromCurrentWorld();
		}
	}*/

	
	// cleans up everything but the skaters
	pTempStructure = new Script::CStruct;
	pTempStructure->Clear();
	// If we're going back to the skateshop, clean up any extra skater heaps we may have allocated
	if( level_id != Script::GenerateCRC( "load_skateshop" ))
	{
		pTempStructure->AddComponent( NONAME, ESYMBOLTYPE_NAME, (int)Script::GenerateCRC("preserve_skaters") );
	}
	CFuncs::ScriptCleanup( pTempStructure, NULL );
	delete pTempStructure;
	
	m_levelChanged = true;
	
	//if( gamenet_man->InNetGame())
	//{
		//Script::RunScript( "create_score_menu" );
	//}

	// restarts the gameflow
	Dbg_AssertPtr( mp_gameFlow );
	mp_gameFlow->Reset( Script::GenerateCRC( "ChangeLevelGameFlow" ) );
	
	Mem::Manager::sHandle().PopContext();
}


void Skate::ResetLevel()
{
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();

	// Cleanup existing level specific stuff
	Cleanup();

	// Re-trigger all the objects in the level										  
	CFuncs::ScriptParseNodeArray(NULL,NULL);
	
	// clear the end run flag
	ClearEndRun();

	// now that the trick object database is set,
	// we can apply any graffiti state queued up in the trick
	// object manager
	GetTrickObjectManager()->ApplyObserverGraffitiState();

	Game::CGoalManager* pGoalManager = Game::GetGoalManager();
	if( gamenet_man->InNetGame())
	{                                                         
		pGoalManager->InitializeAllMinigames();
		pGoalManager->UnBeatAllGoals();
	}
	else
	{
		pGoalManager->InitializeAllGoals();
	}

	pGoalManager->CreateGoalLevelObjects();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void Skate::ResetSkaters(int node, bool walk)
{
    // Skip skaters to restart points (will also reset their physics states)
    for (uint32 i=0;iIsLocalClient() )
        {
             skip_to_restart_point( pSkater, node, walk );			
        }

        // We Resync() the skater here, as he has just restarted
        // he also moves on the client
        // however, the client does not know he has to restart yet
        // so will still be sending old positions
        // we need to tell the clients all the restart
        // and then not take any positions from them until they acknowledge 
        // that they have carried out this restart
        // This would involved calling Resync each frame until they acknowledge having restarted
        // or we could just resync every frame for a period of time
        // however, that seems rather dodgy, as you have no way of knowing
        // how long you should resync for 
		pSkater->Resync();

        // So... how do we do that then Steve?

    }
    ClearEndRun();
}		

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::LaunchGame()
{
	 Mdl::FrontEnd * front =  Mdl::FrontEnd::Instance();
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	front->PauseGame(false);

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());

	printf ("\nLAUNCH GAME CALLED with %d skaters \n\n",GetNumSkaters());			   
			   
	//if (!GetNumSkaters())
	if( !gamenet_man->GetLocalPlayer())
	{
		Dbg_Printf( "*** NO LOCAL PLAYER\n" );
		// If we are starting a game with possibly more than two players
		// then we want to allocate 	
		Dbg_Printf( "*** Max players: %d\n", GetGameMode()->GetMaximumNumberOfPlayers());
		if (GetGameMode()->GetMaximumNumberOfPlayers() > NUM_PERM_SKATER_HEAPS)
		{
			Mem::Manager::sHandle().InitSkaterHeaps(GetGameMode()->GetMaximumNumberOfPlayers());
		}
	
		// start the game flow
		Dbg_AssertPtr( mp_gameFlow );
		mp_gameFlow->Reset( Script::GenerateCRC( "InitializeGameFlow" ) );
		mp_gameFlow->Update();
		printf( "\nLeaving Update\n" );
	}
	else
	{
		// Reset everything in the level to its initial state
		ResetLevel();

		// GJ:  we are intentionally not rerunning the intro script here
		m_gameInProgress = true;

		// start the game flow
		Dbg_AssertPtr( mp_gameFlow );
		mp_gameFlow->Reset( Script::GenerateCRC( "StandardGameFlow" ) );
		printf("Restarting game flow\n");
	}

	// Clear the king of the hill
	GameNet::PlayerInfo* player;
	Lst::Search< GameNet::PlayerInfo > sh;
	if(( player = gamenet_man->GetKingOfTheHill()))
	{
		player->MarkAsKing( false );
	}
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		player->ClearCTFState();
	}

	if( GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netlobby" ))
	{
		gamenet_man->CreateTeamFlags( GetGameMode()->NumTeams());
	}

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::CheckSkaterCollisions( void )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	Obj::CSkater* subject;
	int i;
	
	if( gamenet_man->GameIsOver())
	{
		return;
	}

	// Only perform the collision logic if player collision is enabled
	if( ( gamenet_man->PlayerCollisionEnabled() == false ) &&
		( GetGameMode()->GetNameChecksum() != CRCD(0xbff33600,"netfirefight")) &&
		( GetGameMode()->GetNameChecksum() != CRCD(0x3d6d444f,"firefight")) &&
		( GetGameMode()->GetNameChecksum() != CRCD( 0x6ef8fda0, "netking" )) &&
		( GetGameMode()->GetNameChecksum() != CRCD( 0x5d32129c, "king" )) &&
		( GetGameMode()->GetNameChecksum() != CRCD( 0x6c5ff266, "netctf" )))
	{
		return;
	}

	// Loop through all other skaters and check for collisions
	for( i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
	{
		subject = GetSkater( i );
		if(	subject == NULL )
		{
			continue;
		}
		
		subject->mp_skater_state_history_component->CollideWithOtherSkaters( 0 );
	}

	// If we're in king of the hill mode and the crown is on the ground
	// perform collision checks to see if someone snags it
	if(	( GetGameMode()->GetNameChecksum() == CRCD( 0x6ef8fda0, "netking" )) ||
		( GetGameMode()->GetNameChecksum() == CRCD( 0x5d32129c, "king" )))
	{   
		Obj::CCrown* crown;

		crown = gamenet_man->GetCrown();
		if( crown && !crown->OnKing())
		{
			for( i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
			{
				subject = GetSkater( i );
				if(	subject == NULL )
				{
					continue;
				}
				
				if( subject->mp_skater_state_history_component->CheckForCrownCollision())
				{
					GameNet::PlayerInfo* player;
					Lst::Search< GameNet::PlayerInfo > sh;
		
					player = gamenet_man->GetPlayerByObjectID( subject->GetID() );
					Dbg_Assert( player );

					// It is important that we mark the king immediately (rather than through a
					// network message) since we do logic based on the "current" king
					player->MarkAsKing( true );
					for( player = gamenet_man->FirstPlayerInfo( sh, true ); player;
							player = gamenet_man->NextPlayerInfo( sh, true ))
					{
						// Already marked the king for the local player (above)
						if( player->IsLocalPlayer())
						{
							continue;
						}
						
						GameNet::MsgByteInfo msg;
						Net::MsgDesc msg_desc;
						Net::Server* server;

						server = gamenet_man->GetServer();
						Dbg_Assert( server );
						
						msg.m_Data = subject->GetID();

						msg_desc.m_Data = &msg;
						msg_desc.m_Length = sizeof( GameNet::MsgByteInfo );
						msg_desc.m_Id = GameNet::MSG_ID_NEW_KING;
						msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
						msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
						server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
					}
					break;
				}
			}	   
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::ObserveNextSkater( void )
{
	GameNet::PlayerInfo* player;
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

    player = gamenet_man->GetNextPlayerToObserve();
	gamenet_man->ObservePlayer( player );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* Skate::GetSkaterDisplayName( int id )
{	
	for ( uint32 j = 0; j < GetNumSkaters(); j++ )
	{
		if ( (int) GetSkater(j)->GetID() == id )
		{
			return GetSkater(j)->GetDisplayName();
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Skate::ShouldBeAbsentNode( Script::CStruct* pNode )
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	Dbg_Assert( pNode );
	
	return ( pNode->ContainsFlag( 0x68910ac6 /*"AbsentInNetGames"*/ ) && 
			 ( IsMultiplayerGame() || gamenet_man->InNetMode()));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Skate::IsMultiplayerGame( void )
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	return ( gamenet_man->InNetGame() || GetGameMode()->GetInitialNumberOfPlayers() > 1 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Skate::SetShadowMode( Gfx::EShadowType shadowMode )
{
	Obj::CSkater* skater;
	Lst::Search< Obj::CSkater > sh;

	m_shadow_mode = shadowMode;

	for( skater = sh.FirstItem( m_skaters ); skater; skater = sh.NextItem())
	{
		Obj::CShadowComponent* p_shadow_component = GetShadowComponentFromObject(skater);
		Dbg_Assert(p_shadow_component);
		p_shadow_component->SwitchOnSkaterShadow();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::EShadowType Skate::GetShadowMode( void ) const
{
	return m_shadow_mode;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetGameType | Sets the type of game to be played. Examples: 
// SetGameType FreeSkate 
// SetGameType SingleSession
// @uparm name | Game type
bool ScriptSetGameType(Script::CStruct *pParams, Script::CScript *pScript)
{
	Skate* skate = Skate::Instance();
	uint32 game_type;
	pParams->GetChecksum(NONAME, &game_type, true);
	skate->SetGameType(game_type);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | TestGameType | Tests if current game type is equal to the 
// game type specified as the first parameter
// @uparm name | Game type
bool ScriptTestGameType(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 game_type;
	pParams->GetChecksum(NONAME, &game_type, true);
	
	Skate* skate = Skate::Instance();
	return (game_type == skate->GetGameMode()->GetNameChecksum());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptInTeamGame(Script::CStruct *pParams, Script::CScript *pScript)
{
	Skate* skate = Skate::Instance();

	return skate->GetGameMode()->IsTeamGame();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | TestRequestedGameType |
// This is so that I can tell what the last value sent to SetGameType
// was, since SetGameType will not set the game type straight away,
// so TestGameType will not work.
// @uparm name | Game type
bool ScriptTestRequestedGameType(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 game_type;
	pParams->GetChecksum(NONAME, &game_type, true);
	
	Skate* skate = Skate::Instance();
	return (game_type == skate->GetGameType());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ChangeLevel | 
// @parm string | level | The level to change to
bool ScriptChangeLevel(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	bool show_warning;
	uint32 level;

	pParams->GetChecksum(Script::GenerateCRC("level"), &level, true);
	if( level == Script::GenerateCRC("use_preferences") )
	{
		level = gamenet_man->GetLevelFromPreferences();
	}
		
	gamenet_man->SetLevel( level );
	show_warning = pParams->ContainsComponentNamed( "show_warning" );

#ifdef __NOPT_ASSERT__
	printf("Send message to ChangeLevel level=%s here!\n", Script::FindChecksumName(level) );
#endif

	Pcm::StopMusic();
	
	Net::Server* server;
	GameNet::MsgChangeLevel msg;
	GameNet::PlayerInfo* player;
	Lst::Search< GameNet::PlayerInfo > sh;

	server = gamenet_man->GetServer();
	Dbg_Assert( server );

	msg.m_Level = level;
	msg.m_ShowWarning = (char) show_warning;
	
	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; 
			player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		GameNet::MsgReady ready_msg;
		Net::MsgDesc msg_desc;
		
		ready_msg.m_Time = Tmr::GetTime();

		if( player->IsLocalPlayer() == false )
		{
			if( level == CRCD(0xb664035d,"Load_Sk5Ed_gameplay"))
			{
				server->StreamMessage( player->GetConnHandle(), GameNet::MSG_ID_LEVEL_DATA, Ed::CParkManager::COMPRESSED_MAP_SIZE, 
									   Ed::CParkManager::sInstance()->GetCompressedMapBuffer(), "level data", 
									   GameNet::vSEQ_GROUP_PLAYER_MSGS, false, true );
						   
				server->StreamMessage( player->GetConnHandle(), GameNet::MSG_ID_RAIL_DATA, Obj::GetRailEditor()->GetCompressedRailsBufferSize(), 
									   Obj::GetRailEditor()->GetCompressedRailsBuffer(), "rail data", 
									   GameNet::vSEQ_GROUP_PLAYER_MSGS, false, true );
						   
			}
		}

		msg_desc.m_Data = &msg;
		msg_desc.m_Length = sizeof(GameNet::MsgChangeLevel);
		msg_desc.m_Id = GameNet::MSG_ID_CHANGE_LEVEL;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
		server->EnqueueMessage( player->GetConnHandle(), &msg_desc );

		if( player->IsLocalPlayer() == false )
		{
			if( gamenet_man->UsingCreatedGoals())
			{
				gamenet_man->LoadGoals( level );
				server->StreamMessage( player->GetConnHandle(), GameNet::MSG_ID_GOALS_DATA, gamenet_man->GetGoalsDataSize(),
							   gamenet_man->GetGoalsData(), "goals data", GameNet::vSEQ_GROUP_PLAYER_MSGS, false, true );
			}
		}

		// Don't send them any non-important messages until they're finished loading
		msg_desc.m_Data = &ready_msg;
		msg_desc.m_Length = sizeof( GameNet::MsgReady );
		msg_desc.m_Id = GameNet::MSG_ID_READY_QUERY;
		server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
				
		player->SetReadyQueryTime( ready_msg.m_Time );
	}

	// In net games, the changing of levels is deferred. No need to send immediately - we can
	// wait until the enqueued message is naturally added to the outgoing stream.  In 2p mode,
	// however, the scripts expect this message to be sent and handled in a one-frame window
	// so we need to send it immediately
	if ( ! gamenet_man->InNetGame())
	{
		server->SendData();		// Mick, (true) because we want to send it immediatly
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LaunchLevel | launches currently requested level
bool ScriptLaunchLevel(Script::CStruct *pParams, Script::CScript *pScript)
{
	Skate* skate_mod = Skate::Instance();
	uint32 level = skate_mod->m_requested_level;
	skate_mod->OpenLevel(level);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RequestLevel | 
// @uparm name | level to request
bool ScriptRequestLevel(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	Skate* skate_mod = Skate::Instance();
	uint32 level;

	
//	printf ("\n%s\n",pScript->GetScriptInfo());
	
	
	if( !pParams->GetChecksum(NONAME, &level, false))
	{
		const char* level_name;
		char checksum_name[128];

		if( pParams->GetString( NONAME, &level_name, Script::ASSERT ))
		{
			sprintf( checksum_name, "load_%s", level_name );
			Dbg_Printf( "Got checksum name of %s\n", checksum_name );
			level = Script::GenerateCRC( checksum_name );
		}
	}

	if( level == Script::GenerateCRC("use_preferences") )
	{
		level = gamenet_man->GetLevelFromPreferences();
		gamenet_man->SetLevel( level );
	}

	skate_mod->m_requested_level = level;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Retry | retry current level
bool ScriptRetry(Script::CStruct *pParams, Script::CScript *pScript)
{
	Skate* skate_mod = Skate::Instance();
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	skate_mod->LaunchGame();	
	Mem::Manager::sHandle().PopContext();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LaunchGame | 
bool ScriptLaunchGame(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	Skate::Instance()->LaunchGame();	
	Mem::Manager::sHandle().PopContext();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptFillRankingScreen(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	Lst::Search< GameNet::PlayerInfo > sh;
	GameNet::PlayerInfo* player;
	Lst::Head< GameNet::ScoreRank > rank_list;
	Lst::Search< GameNet::ScoreRank > rank_sh;
	GameNet::ScoreRank* rank, *next;
	bool in_goal_attack;
	int i;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());

	in_goal_attack = skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netgoalattack" );

	if( skate_mod->GetGameMode()->IsTeamGame())
	{
		for( i = 0; i < skate_mod->GetGameMode()->NumTeams(); i++ )
		{
			if( gamenet_man->NumTeamMembers( i ) == 0 )
			{
				continue;
			}
			char team_name_str[64];
						
			rank = new GameNet::ScoreRank;
			rank->m_IsKing = false;
			rank->m_ColorIndex = i + 2;
			rank->m_TeamId = i;
			rank->m_TotalScore = gamenet_man->GetTeamScore( i );
			
			sprintf( team_name_str, "team_%d_name", i + 1 );
			sprintf( rank->m_Name, "\\c%d%s %s", rank->m_ColorIndex, Script::GetString( team_name_str ), Script::GetString( "total_str" ));
						
			rank->SetPri( gamenet_man->GetTeamScore( i ));
			rank_list.AddNode( rank );
		}

		// Now loop through the sorted list in order of highest to lowest score and print them out
		i = 0;
		for( rank = rank_sh.FirstItem( rank_list ); rank; rank = next )
		{   
			Script::CStruct* p_item_params;
			char score_str[64];
	
			next = rank_sh.NextItem();
	
			for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
			{
				char player_score_str[128];
				int player_score;
                
				if( player->m_Team != rank->m_TeamId )
				{
					continue;
				}

				if( in_goal_attack )
				{
					Game::CGoalManager* pGoalManager;
			 
					pGoalManager = Game::GetGoalManager();
					player_score = pGoalManager->NumGoalsBeatenBy( player->m_Skater->GetID());
				}
				else
				{
					player_score = gamenet_man->GetPlayerScore( player->m_Skater->GetID());
				}
				

				if( player->IsLocalPlayer())
				{
					sprintf( player_score_str, "\\c%d> %s", rank->m_ColorIndex, player->m_Name );
				}
				else
				{
					sprintf( player_score_str, "\\c%d%s", rank->m_ColorIndex, player->m_Name );
				}
				
				p_item_params = new Script::CStruct;	
				p_item_params->AddString( "text", player_score_str );
				// p_item_params->AddChecksum( "id", 123456 + i );
				p_item_params->AddChecksum( "parent", Script::GenerateCRC( "player_list_menu" ));

				Script::RunScript("player_menu_add_item", p_item_params);
				delete p_item_params;
		
				p_item_params = new Script::CStruct;	
				if(	skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netking" ) ||
					skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "king" ))
				{
					sprintf( score_str, "%.2d:%.2d", Tmr::InSeconds( player_score ) / 60, Tmr::InSeconds( player_score ) % 60 );
				}
				else
				{
					sprintf( score_str, "%d", player_score );
				}
				p_item_params->AddString( "text", score_str );
				p_item_params->AddChecksum( "font", Script::GenerateCRC( "dialog" ) );
				p_item_params->AddChecksum( "id", 234567 + i );
				p_item_params->AddChecksum( "parent", Script::GenerateCRC( "rankings_list_menu" ));
		
				Script::RunScript("score_menu_add_item",p_item_params);
				delete p_item_params;

				i++;
			}
			p_item_params = new Script::CStruct;	
			p_item_params->AddString( "text", rank->m_Name );
			// p_item_params->AddChecksum( "id", 123456 + i );
			p_item_params->AddChecksum( NONAME, Script::GenerateCRC( "team_score" ) );
			p_item_params->AddChecksum( "parent", Script::GenerateCRC( "player_list_menu" ));
	
			Script::RunScript("player_menu_add_item",p_item_params);
			delete p_item_params;
	
			p_item_params = new Script::CStruct;	
			if( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netking" ) ||
				skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "king" ))
			{
				sprintf( score_str, "\\c%d%.2d:%.2d", rank->m_ColorIndex, Tmr::InSeconds( rank->GetPri()) / 60, Tmr::InSeconds( rank->GetPri()) % 60 );
			}
			else
			{
				sprintf( score_str, "\\c%d%d", rank->m_ColorIndex, rank->GetPri());
			}
			
			p_item_params->AddString( "text", score_str );
			p_item_params->AddChecksum( "id", 234567 + i );
			p_item_params->AddChecksum( "font", Script::GenerateCRC( "dialog" ) );
			p_item_params->AddChecksum( NONAME, Script::GenerateCRC( "team_score" ) );
			p_item_params->AddChecksum( "parent", Script::GenerateCRC( "rankings_list_menu" ));
	
			Script::RunScript("score_menu_add_item",p_item_params);
			delete p_item_params;
	
			delete rank;
			i++;
		}
	}
	else
	{
		for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
		{
			int player_score;

			if( in_goal_attack )
			{
				Game::CGoalManager* pGoalManager;
		 
				pGoalManager = Game::GetGoalManager();
				player_score = pGoalManager->NumGoalsBeatenBy( player->m_Skater->GetID());
			}
			else
			{
				player_score = gamenet_man->GetPlayerScore( player->m_Skater->GetID());
			}
				
			rank = new GameNet::ScoreRank;
			strcpy( rank->m_Name, player->m_Skater->GetDisplayName() );
	
			// Lists sort based on node's priority so set the node's priority to that of
			// the player's score and add him to the list
			rank->SetPri( player_score );
			rank_list.AddNode( rank );
		}
	
		// Now loop through the sorted list in order of highest to lowest score and print them out
		i = 0;
		for( rank = rank_sh.FirstItem( rank_list ); rank; rank = next )
		{   
			Script::CStruct* p_item_params;
			char score_str[64];
	
			next = rank_sh.NextItem();
	
			//Script::RunScript( "prepare_server_menu_for_new_children" );
			
			p_item_params = new Script::CStruct;	
			p_item_params->AddString( "text", rank->m_Name );
			// p_item_params->AddChecksum( "id", 123456 + i );
			p_item_params->AddChecksum( "parent", Script::GenerateCRC( "player_list_menu" ));
	
			Script::RunScript("player_menu_add_item",p_item_params);
			delete p_item_params;
	
			p_item_params = new Script::CStruct;	
			if( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netking" ) ||
				skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "king" ))
			{
				sprintf( score_str, "%.2d:%.2d", Tmr::InSeconds( rank->GetPri()) / 60, Tmr::InSeconds( rank->GetPri()) % 60 );
			}
			else
			{
				sprintf( score_str, "%d", rank->GetPri());
			}

			p_item_params->AddString( "text", score_str );
			p_item_params->AddChecksum( "id", 234567 + i );
			p_item_params->AddChecksum( "font", Script::GenerateCRC( "dialog" ) );
			p_item_params->AddChecksum( "parent", Script::GenerateCRC( "rankings_list_menu" ));
	
			Script::RunScript("score_menu_add_item",p_item_params);
			delete p_item_params;
	
			delete rank;
			i++;
		}
	}

	Mem::Manager::sHandle().PopContext();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// K: This function used to use GetMaximumNumberOfPlayers,
// but I changed it to use p_gamenet_man->GetMaxPlayers so that only
// the required amount of memory is allocated, hence allowing bigger
// parks that allow fewer players.
// p_gamenet_man->GetMaxPlayers will return the num players as chosen from the Players menu
// when starting a network game.

// @script | InitSkaterHeaps | 
bool ScriptInitSkaterHeaps(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	
	
	int num_skater_heaps_required=1;
	
    Skate *p_skate = Skate::Instance();
	if (p_skate->GetGameType()==CRCD(0x5c8f5d66,"freeskate2p"))
	{
		num_skater_heaps_required=2;
	}
	else
	{
		GameNet::Manager * p_gamenet_man = GameNet::Manager::Instance();
		num_skater_heaps_required=p_gamenet_man->GetMaxPlayers();
	}
		
	Dbg_Printf( "\nInitializing %d Skater Heaps\n", num_skater_heaps_required);
			   
	// If we are startinga  game with possibly more than two players
	// then we want to allocate     
	if( num_skater_heaps_required > NUM_PERM_SKATER_HEAPS )
	{
		Mem::Manager::sHandle().DeleteSkaterHeaps();
		Mem::Manager::sHandle().InitSkaterHeaps(num_skater_heaps_required);
	}

	Mem::Manager::sHandle().PopContext();
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetLevel | resets the level
bool ScriptResetLevel(Script::CStruct *pParams, Script::CScript *pScript)
{
	Skate* skate_mod = Skate::Instance();

	// A hacky way of preserving the running script, since skate's cleanup will destroy all
	// spawned scripts.
	bool was_spawned = false;
	if( pScript )
	{
		was_spawned = pScript->mIsSpawned;
		pScript->mIsSpawned = false;
	}
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	skate_mod->ResetLevel();	
	Mem::Manager::sHandle().PopContext();
	
	if( pScript )
	{
		pScript->mIsSpawned = was_spawned;
	}

	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#if 0

static char * maybe_new(int n, bool new_record)
{
	char *p = Script::GetScriptString(n);
	if (new_record)
	{
		sprintf (p,"&c1");
		p+=3;
	}
	return p;	
}

// insert a single line into the script strings
void populate_line(int &index, Records::CRecord *pRecord)
{
		sprintf(maybe_new(index++,pRecord->GetNewRecord()),"%s",Str::PrintThousands(pRecord->GetValue()));
		sprintf(maybe_new(index++,pRecord->GetNewRecord()),"%d",pRecord->GetNumber());
		sprintf(maybe_new(index++,pRecord->GetNewRecord()),pRecord->GetInitials());
}

#ifndef __PLAT_NGC__
// insert a single line into the script strings
static void populate_line_secs(int &index, Records::CRecord *pRecord)
{
		#if (ENGLISH == 0)		
		sprintf(maybe_new(index++,pRecord->GetNewRecord()),"%3.2f %s",(float)pRecord->GetValue()/100.0f,Script::GetLocalString( "skate_str_secs"));
		#else		
		sprintf(maybe_new(index++,pRecord->GetNewRecord()),"%3.2f secs",(float)pRecord->GetValue()/100.0f);
		#endif
		sprintf(maybe_new(index++,pRecord->GetNewRecord()),"%d",pRecord->GetNumber());
		sprintf(maybe_new(index++,pRecord->GetNewRecord()),pRecord->GetInitials());
}


static void  populate_table(int &index, Records::CRecordTable *pRecordTable)
{

	int entries = pRecordTable->GetSize();
	for (int i = 0; i < entries; i++)
	{
		populate_line(index, pRecordTable->GetRecord(i));		
	}
}
#endif		// __PLAT_NGC__

#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Update the records with whatever we just did this session 
void Skate::UpdateRecords()
{
	m_new_record = false;
	
	int level = GetCareer()->GetLevel();

	// get the records for this level	
	Records::CLevelRecords *pRecords = GetGameRecords()->GetLevelRecords(level);

   	// find the skater, get the score landed:
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	Obj::CSkater *pSkater = skate_mod->GetLocalSkater(); 
	if( pSkater == NULL )
	{
		return;
	}          

	Mdl::Score * pScore = ( pSkater->GetScoreObject() );
	/*
	HUD::PanelMgr* panel_mgr = HUD::PanelMgr::Instance();
	HUD::Panel* pPanel;
	pPanel = panel_mgr->GetPanelBySkaterId(pSkater->GetID(), false);
	*/

	// Insert the score into the high score table if it is high enough	 
	m_new_record |=  (-1 != pRecords->GetHighScores()->MaybeNewEntry(pScore->GetTotalScore(),0));												  
	
	m_new_record |=  (-1 != pRecords->GetBestCombos()->MaybeNewEntry(pScore->GetBestCombo(),0));

	Obj::CSkaterBalanceTrickComponent* p_skater_balance_trick_component = GetSkaterBalanceTrickComponentFromObject(pSkater);
	Dbg_Assert(p_skater_balance_trick_component);
	
	int this_grind = (int)(p_skater_balance_trick_component->mGrind.GetMaxTime()*100);
	int this_manual = (int)(p_skater_balance_trick_component->mManual.GetMaxTime()*100);
	int	this_lip = (int)(p_skater_balance_trick_component->mLip.GetMaxTime()*100);
	// int this_combo = 0;
	int this_combo = pScore->GetLongestCombo();

	m_new_record |= pRecords->GetLongestGrind()->MaybeNewRecord(this_grind,0);	
	m_new_record |= pRecords->GetLongestManual()->MaybeNewRecord(this_manual,0);	
	m_new_record |= pRecords->GetLongestLipTrick()->MaybeNewRecord(this_lip,0);		
	m_new_record |= pRecords->GetLongestCombo()->MaybeNewRecord(this_combo,0);		
}

#define	MAX_COMBO_LINES  200			// more that we need...

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifndef __PLAT_NGC__
#if 0
static float text_width(const char *p_string, const char *p_font)
{
	Fnt::Drawer drawer;
	drawer.SetFont(p_font);
	drawer.SetText(p_string);
	return drawer.GetWidth();
}
#endif
#endif		// __PLAT_NGC__

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Utility function to get the info out of the level records
// and into the script strings
void Skate::GetRecordsText(int level)
{
#if 0
#ifndef __PLAT_NGC__
	

	// get the records for this level	
	Records::CLevelRecords *pRecords = GetGameRecords()->GetLevelRecords(level);

	int index = 0;
	
		// find the skater, get the score landed:
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	Obj::CSkater *pSkater = skate_mod->GetLocalSkater();
	Mdl::Score * pScore = ( pSkater->GetScoreObject() );
	/*
	HUD::PanelMgr* panel_mgr = HUD::PanelMgr::Instance();
	HUD::Panel* pPanel;
	pPanel = panel_mgr->GetPanelBySkaterId(pSkater->GetID(), false);
	Dbg_MsgAssert(pPanel,("Could not get panel for skater on records screen"));
	*/

	int this_grind = (int)(pSkater->mGrind.GetMaxTime()*100);
	int this_manual = (int)(pSkater->mManual.GetMaxTime()*100);
	int	this_lip = (int)(pSkater->mLip.GetMaxTime()*100);
	
	// get total score for this level																		
	sprintf(Script::GetScriptString(index++),"%s",Str::PrintThousands(pScore->GetTotalScore()));		 // 0
	// get highest score from high score table
	sprintf(Script::GetScriptString(index++),"%s",Str::PrintThousands(pRecords->GetHighScores()->GetRecord(0)->GetValue()));	  // 1
	
	
	#if (ENGLISH == 0)
	
	sprintf(Script::GetScriptString(index++),"%3.2f %s",(float)this_grind/100.0f,Script::GetLocalString( "skate_str_secs"));					  // 2
	sprintf(Script::GetScriptString(index++),"%3.2f %s",(float)this_manual/100.0f,Script::GetLocalString( "skate_str_secs"));					  // 3
	sprintf(Script::GetScriptString(index++),"%3.2f %s",(float)this_lip/100.0f,Script::GetLocalString( "skate_str_secs"));						  // 4
	sprintf(Script::GetScriptString(index++),"%d %s",pPanel->GetNumLongestCombo(),Script::GetLocalString( "skate_str_tricks"));			// 5
	sprintf(Script::GetScriptString(index++),Script::GetLocalString( "skate_str_points_tricks"),Str::PrintThousands(pPanel->GetScoreBestCombo()),pPanel->GetNumBestCombo());	// 6

	#else
	
	sprintf(Script::GetScriptString(index++),"%3.2f secs",(float)this_grind/100.0f);					  // 2
	sprintf(Script::GetScriptString(index++),"%3.2f secs",(float)this_manual/100.0f);					  // 3
	sprintf(Script::GetScriptString(index++),"%3.2f secs",(float)this_lip/100.0f);						  // 4
	sprintf(Script::GetScriptString(index++),"1 tricks");			// 5
	sprintf(Script::GetScriptString(index++),"2 points in 2 tricks");	// 6
	//sprintf(Script::GetScriptString(index++),"%d tricks",pPanel->GetNumLongestCombo());			// 5
	//sprintf(Script::GetScriptString(index++),"%s points in %d tricks",Str::PrintThousands(pPanel->GetScoreBestCombo()),pPanel->GetNumBestCombo());	// 6

	#endif

	// What we want is to get the last N lines of combo text, word wrapped
	// to the correct size for the font we will use on the statistics screen
	//
	// What we have is the text for the individual tricks, and the number of tricks.
	//
	// - Set up a textdrawer with the correct size and font
	// - add text to it, and to a string, until full
	// - copy string into a ScriptString, and repeat

	// find font we will be using by looking in the appropiate property 
	Script::CStruct * p_record_font_props = Script::GetStructure("statistics_font_props");
	const char *p_font;
	p_record_font_props->GetText("font",&p_font);	
	Script::CStruct * p_record_box3_props = Script::GetStructure("statistics_box_3_props");
	int	box_width;
	p_record_box3_props->GetInteger("box_w",&box_width);
	  	
	
	Str::String line_table[MAX_COMBO_LINES];
	int line = 0;
	float current_w = 0;
	int num_combos = 0;
	//int num_combos = pPanel->GetNumBestCombo();
	for (int trick =0; trick < num_combos; trick++)
	{
		const char * trick_chars = "blah";
		//const char * trick_chars = pPanel->GetStringBestCombo(trick);
		float trick_w = text_width(trick_chars, p_font);
		float new_w = current_w + trick_w;
		if (current_w > 0.0f && new_w > box_width)
		{
			line++;
			Dbg_MsgAssert(line < MAX_COMBO_LINES,("Far to many combo lines"));
			current_w = 0.0f;
		}
		if (current_w == 0.0f)
		{
			line_table[line] = trick_chars;
		}
		else
		{
			// combine the two strings into one string by sprintfing them into a temporary buffer
			char temp[1024];	  	// good job we have a huge stack!!
			sprintf (temp,"%s%s",line_table[line].getString(),trick_chars);
			Dbg_MsgAssert(strlen(temp) < 1024,("line insanely too long"));  // not taking any chances
			line_table[line] = temp;	 // this will delete the old string contents
		}
		current_w += trick_w;
	}
//	int num_lines = line+1;

	line = 0;
	while (index < 20)
	{
		sprintf(Script::GetScriptString(index++),line_table[line++].getString());		
	}

/* functions we can use
	int  	GetScoreBestCombo();
	int		GetNumBestCombo();
	const char	* GetStringBestCombo(int trick);
	int  	GetScoreLongestCombo();
	int		GetNumLongestCombo();
*/

	index = 20;

	populate_table(index, pRecords->GetHighScores());								  			// 20-34
	populate_table(index, pRecords->GetBestCombos());											// 35-49
	populate_line_secs(index,pRecords->GetLongestGrind());											// 50-52
	populate_line_secs(index,pRecords->GetLongestManual());											// 53-55
	populate_line_secs(index,pRecords->GetLongestLipTrick());										// 56-58
	populate_line(index,pRecords->GetLongestCombo());											// 59-61
#endif		// __PLAT_NGC__
#endif
}
																			 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::UpdateSkaterInputHandlers()
{	
	printf("&&&&&&&&&&&&&&&&&&&&&&&&&& UpdateSkaterInputHandlers called\n");
	
	for ( int i = 0; i < vMAX_SKATERS; i++ )
	{
		Obj::CSkater* pSkater = GetSkater( i );
		if ( pSkater && pSkater->IsLocalClient() )
		{
			printf("found a local skater - binding controller\n");
			int heap_index = pSkater->GetHeapIndex();
			Dbg_MsgAssert( heap_index >= 0 && heap_index < vMAX_SKATERS, ( "heap index %i out of range", heap_index ) );
			
			Dbg_Assert(GetInputComponentFromObject(pSkater));
			GetInputComponentFromObject(pSkater)->BindToController(m_device_server_map[heap_index]);
			
			// Dan: Pretty sure this method is wrong.
			//char skatecam[16];
			//sprintf(skatecam,"SkaterCam%d",i);
			//if (Obj::CCompositeObject *p_obj
				//= static_cast< Obj::CCompositeObject* >(Obj::CCompositeObjectManager::Instance()->GetObjectByID(Script::GenerateCRC(skatecam))))
			if (Obj::CCompositeObject *p_obj = pSkater->GetCamera())
			{
				Dbg_Assert(GetInputComponentFromObject(p_obj));
				GetInputComponentFromObject(p_obj)->BindToController(m_device_server_map[heap_index]);
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Fills in a little structure with some stuff so that SkipLogic is really fast.
void Skate::pre_calculate_object_update_info()
{
	if (Nx::CViewportManager::sGetNumActiveViewports() > 1 || ! Nx::CViewportManager::sGetActiveCamera(0))
	{
		// Nothing should be suspended if multiplayer, so leave the flag true.
		m_precalculated_object_update_info.mDoNotSuspendAnything=true;
	}
	else
	{
		m_precalculated_object_update_info.mDoNotSuspendAnything=false;
		m_precalculated_object_update_info.mActiveCameraPosition = Nx::CViewportManager::sGetActiveCamera(0)->GetPos();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Prefs::Preferences* GetPreferences( Script::CStruct* pParams, bool assert_on_fail )
{   
	uint32 checksum;
	pParams->GetChecksum( "prefs", &checksum, true );

	return GetPreferences( checksum, assert_on_fail );

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Prefs::Preferences* GetPreferences( uint32 checksum, bool assert_on_fail )
{	
	if ( checksum == Script::GenerateCRC("network") )
	{
		 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
		return gamenet_man->GetNetworkPreferences();
	}
	else if ( checksum == Script::GenerateCRC("taunt") )
	{
		 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
		return gamenet_man->GetTauntPreferences();
	}
	else if ( checksum == Script::GenerateCRC("splitscreen") )
	{
		 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
		return skate_mod->GetSplitScreenPreferences();
	}

	if ( assert_on_fail )
	{
		Dbg_MsgAssert( 0, ( "Couldn't find preferences for %s", Script::FindChecksumName(checksum) ) );
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Just a simple debugging interface function to call the main rail 
// manager's DebugRender() function
void			Rail_DebugRender()
{
	Mdl::Skate* skate_mod =  Mdl::Skate::Instance();
	if (!skate_mod->GetDrawRails())
	{
		return;
	}

	skate_mod->GetRailManager()->DebugRender();

	for (Obj::CRailManagerComponent* p_rail_manager_component = static_cast< Obj::CRailManagerComponent* >(Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_RAILMANAGER));
		p_rail_manager_component;
		p_rail_manager_component = static_cast< Obj::CRailManagerComponent* >(p_rail_manager_component->GetNextSameType()))
	{
		Obj::CCompositeObject* p_movable_object = p_rail_manager_component->GetObject();

		// form a transformation matrix
		Mth::Matrix total_mat = p_movable_object->GetMatrix();
		total_mat[X][W] = 0.0f;
		total_mat[Y][W] = 0.0f;
		total_mat[Z][W] = 0.0f;
		total_mat[W] = p_movable_object->GetPos();
		total_mat[W][W] = 1.0f;

		
		Obj::CRailManager *p_railman = p_rail_manager_component->GetRailManager();								   
		if (p_railman)
		{
			p_railman->DebugRender(&total_mat);			
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// return the current gap checklist for the level that we are in														 
Obj::CGapChecklist* Skate::GetGapChecklist()
{
	Obj::CGapChecklist* p_gap_checklist = GetCareer()->GetGapChecklist(); 
	Dbg_Assert(p_gap_checklist);
	return p_gap_checklist;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Skate::ShouldAllocateNetMiscHeap( void )
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	return ( gamenet_man->InNetGame() && ( m_cur_level != Script::GenerateCRC( "Load_Skateshop" ))) ;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Skate::ShouldAllocateInternetHeap( void )
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	if( gamenet_man->InNetMode())
	{
		return true;
	}

	// GJ:  need to temporarily remove LOAD_CAS, because 
	// the internet heap is coming in after the unloadable
	// anims, and the unloadable anims need to be the last
	// thing on the topdown heap
	if( ( m_requested_level == Script::GenerateCRC( "load_skateshop" ) ) 
		|| ( m_requested_level == Script::GenerateCRC( "load_CAS" )))
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::FirstInputReceived()
{
	m_first_input_received = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mdl


================================================
FILE: Code/Sk/Modules/Skate/skate.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			SKATE													**
**																			**
**	File name:		modules/skate/skate.h									**
**																			**
**	Created: 		06/07/2000	-	spg										**
**																			**
*****************************************************************************/

#ifndef __MODULES_SKATE_SKATE_H
#define __MODULES_SKATE_SKATE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/


#include 
#include 

#include 
#include 

#include  // For EShadowType

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

enum
{
	SKATE_TYPE_UNDEFINED 		= 0,
	SKATE_TYPE_SKATER 			= 1,
	SKATE_TYPE_PED				= 2,
	SKATE_TYPE_CAR				= 3,
	SKATE_TYPE_GAME_OBJ			= 4,
	SKATE_TYPE_BOUNCY_OBJ		= 5,
	SKATE_TYPE_CASSETTE			= 6,
	SKATE_TYPE_ANIMATING_OBJECT	= 7,
	SKATE_TYPE_CROWN			= 8,
	SKATE_TYPE_PARTICLE			= 9,
	SKATE_TYPE_REPLAY_DUMMY		= 10,
	SKATE_TYPE_COMPOSITE		= 11,		// just a temp type, to exclude it from certain checks
	
	// Note: If a new type is added here, please also add it to the switch statement in
	// the CCompositeObject::GetDebugInfo function in gel\object\compositeobject.cpp and also add
	// the name to data\scripts\debugger\debugger_names.q
	// That way the m_type member will appear as a name in the script debugger rather than as
	// an integer.
};

namespace Game
{
    class CGameMode;
	class CGoalManager;
}

namespace GameNet
{
	class PlayerInfo;
}

namespace Obj
{
    class CGapChecklist;
    class CGeneralManager;
	class CMovieManager;
    class CObject;
	class CPlayerProfileManager;
	class CProximManager;
	class CRailManager;
	class CSkater;
	class CSkaterCareer;
	class CSkaterProfile;
    class CTrickChecksumTable;
    class CTrickObjectManager;

#	ifdef TESTING_GUNSLINGER
	class CNavManager;
#	endif
};

namespace Prefs
{
	class Preferences;
};

namespace Records
{
	class CGameRecords;
};

namespace Script
{
    class CScript;
    class CStruct;
};

namespace Mdl
{
    class CCompetition;
    class CGameFlow;
    class CHorse;

// Little structure for storing information about a high score goal
// currently there are just two, the "high score" and "pro score"
// we did previously have an additional "sick score", which may return
// hence, I'm trying to be flexible here, if not totally object oriented...   
struct	SScoreGoal
{
	int				score;					// What score we try to get
	uint32			script;					// what script it triggers
	int				goal;					// what goal we get (can be -1 for none, if you want to do it in script)
};
   
// There is an array of vMAX_SKATERS of these in the Skate class.
// They get set from the controller config menu in the skateshop, and
// get saved out to the memory card. (see ScriptSaveToMemoryCard in cfuncs.cpp)
//
// Brad - the skaterIndex is used to map a controller to a skater.
// This is needed to meet an XBox requirement that whatever controller they
// use to start the game can be used to play.
struct SControllerPreferences
{
	int skaterIndex;
	bool VibrationOn;
	bool AutoKickOn;
	bool SpinTapsOn;
};

// Ken: A small structure for holding info needed by SkipLogic & MovingObj_Update
// to save them having to calculate the same things for billions of objects every frame.
// This structure gets filled in once per frame, and read by SkipLogic & MovingObj_Update
struct SPreCalculatedObjectUpdateInfo
{
	// This is set if it is a multiplayer game or if a cutscene is playing
	bool mDoNotSuspendAnything;
	
	// SkipLogic calculates the distance of the object from this.
	Mth::Vector mActiveCameraPosition;
};

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class Skate : public Module 
{

public:
	
	enum
	{
		vMAX_SKATERS 		= 8,
		vMAX_SCORE_GOALS    = 64,		// Don't really need 32, but just to cover the maximun number of goals
	};
									Skate( void );
	virtual							~Skate( void );
		
	// Cleans up game objects and optionally skaters
	void							Cleanup();
	void 							CleanupForParkEditor();

	
	void							Pause( bool pause = true );
	
	/************************************
	* skater control functions
	*************************************/
	Obj::CSkater* 					GetLocalSkater( void );
	Obj::CSkater*					GetSkater( uint num );
	Obj::CSkater*					GetSkaterById( uint32 id );
	uint32							GetNumSkaters( void );
	int								GetNextUnusedSkaterHeapIndex( bool for_local_skater );
	void		 					GetNextStartingPointData( Mth::Vector* pos, Mth::Matrix* matrix, int obj_id );
	void							HideSkater( Obj::CSkater* skater, bool should_hide = true );
	Obj::CSkater*					add_skater( Obj::CSkaterProfile* pSkaterProfile, bool local_client, int obj_id, int player_num );
	void							remove_skater( Obj::CSkater* skater );	// Remove a specific skater
	int								find_restart_node( int index );
	void							skip_to_restart_point( Obj::CSkater* skater, int node = -1, bool walk = false );
	void							move_to_restart_point( Obj::CSkater* skater );
	
	void							PauseGameFlow( bool paused );
	void							SetTimeLimit( int seconds );
	int								GetTimeLimit( void );

	void							FirstInputReceived();
	
	/************************************
	* game control functions
	*************************************/
	
public:
	// game control
	void							SetGameType(uint32 gameType);
	// Added by Ken, for use by the cassette menu so that it can tell whether it is to be
	// used for career, free skate or session.
	uint32							GetGameType() {return m_requested_game_type;}
	
	void							SetCurrentGameType();

	// returns checksum of level script name
	uint32							GetGameLevel();		  

	// informs the skate module that we want the game in progress to end
	void							RequestGameEnd();
	bool							IsGameInProgress() {return m_gameInProgress;}

	void							SetLaunchingQueuedScripts( void );
	void							ClearLaunchingQueuedScripts( void );
	bool							LaunchingQueuedScripts( void );

	// used to not load objects that were meant to be left
	// out for performance/bandwidth/gameplay reasons
	bool							IsMultiplayerGame( void );
	bool							ShouldBeAbsentNode( Script::CStruct* pNode );
	
	void							OpenLevel(uint32 level_script);
	void							RestartLevel();
	void							ResetLevel();
	void							ResetSkaters(int node = -1, bool walk = false);
	void							ChangeLevel( uint32 level_id );
	void							LaunchGame();
	void							LaunchNetworkGame();

	/************************************
	* network functions					*
	*************************************/
	void							StartServer( void );
	void							AddNetworkMsgHandlers( Net::Client* client, int index );
	void							LeaveServer( void );
	void							SendScoreUpdates( bool final = false );
	void 							ObserveNextSkater( void );
	uint32							GetCheatChecksum( void );
	void							SendCheatList( GameNet::PlayerInfo* player );

	/************************************
	* other functions
	*************************************/

	const char*						GetSkaterDisplayName( int id );
	Obj::CSkaterProfile*			GetProfile( int i );
	Obj::CSkaterProfile*			GetCurrentProfile( Script::CStruct* pParams = NULL );
	Obj::CSkaterCareer*				GetCareer(){return mp_career;} 
	Game::CGameMode*				GetGameMode();
	Game::CGoalManager*				GetGoalManager();
	Obj::CGeneralManager*			GetObjectManager( void );
	Obj::CTrickObjectManager*		GetTrickObjectManager( void );
	Obj::CPlayerProfileManager*		GetPlayerProfileManager( void );
	Obj::CTrickChecksumTable*		GetTrickChecksumTable() { return mp_trickChecksumTable; }

	Prefs::Preferences*				GetSplitScreenPreferences( void );
	float							GetHandicap( int id );

	void 							CheckSkaterCollisions( void );
	
    static Tsk::Task< Skate >::Code   	s_logic_code; 
	static Tsk::Task< Skate >::Code   	s_object_update_code; 
	static Tsk::Task< Skate >::Code   	s_score_update_code; 
	static void	s_object_update( Obj::CObject* object, void* data );
	
private:
			
	void							v_start_cb( void );
	void							v_stop_cb( void );
	
	Tsk::Task< Skate >*				m_logic_task;	
	Tsk::Task< Skate >*				m_object_update_task;	
	Tsk::Task< Skate >*				m_score_update_task;    


	Obj::CSkaterCareer*				mp_career;
	
public:
	Obj::CRailManager*	            mp_railManager;    
	Obj::CProximManager*	        mpProximManager;
	void 							UpdateGameFlow();
	Obj::CRailManager*	            GetRailManager() { return mp_railManager; }

#	ifdef TESTING_GUNSLINGER
	Obj::CNavManager*				mp_navManager;
	Obj::CNavManager*	            GetNavManager() { return mp_navManager; }
#	endif

	void			                Rail_DebugRender();

private:
	void 							DoUpdate();	// called by s_logic

	
//	Obj::CGeneralManager*			mp_obj_manager;
	Lst::Head< Obj::CSkater >		m_skaters;
	    
	bool 							m_recording;
	bool 							m_playing;
	int 							m_fd; 

	bool							m_first_input_received;
	int								m_controller_unplugged_frame_count;

	// Network msg handlers
	void							net_setup( void );
	void							net_shutdown( void );
	bool							should_send_update( GameNet::PlayerInfo* send_player, Obj::CObject* object );

	bool							m_launching_queued_scripts;
	    
	static Net::MsgHandlerCode		handle_cheats;
	static Net::MsgHandlerCode		handle_cheat_list;
	static Net::MsgHandlerCode		handle_projectile;
	static Net::MsgHandlerCode		handle_enter_vehicle_client;
	
	static Net::MsgHandlerCode		handle_anims;
	static Net::MsgHandlerCode		handle_flip_anim;
	static Net::MsgHandlerCode		handle_enter_vehicle_server;
	
	static Net::MsgHandlerCode		handle_msg_relay;
	static Net::MsgHandlerCode		handle_selective_msg_relay;
	static Net::MsgHandlerCode		handle_script_relay;
	static Net::MsgHandlerCode		handle_score_request;
	static Net::MsgHandlerCode		handle_landed_trick;
	static Net::MsgHandlerCode		handle_combo_report;
	static Net::MsgHandlerCode		handle_object_update;
	static Net::MsgHandlerCode		handle_object_update_relay;
	static Net::MsgHandlerCode		handle_player_quit;
	static Net::MsgHandlerCode		handle_disconn_accepted;
	static Net::MsgHandlerCode		handle_kicked;
	static Net::MsgHandlerCode		handle_play_sound;
	static Net::MsgHandlerCode		handle_sparks;
	static Net::MsgHandlerCode		handle_blood;
	static Net::MsgHandlerCode		handle_score_update;
	static Net::MsgHandlerCode		handle_bail_done;
	static Net::MsgHandlerCode		handle_change_level;
	static Net::MsgHandlerCode		handle_run_script;
	static Net::MsgHandlerCode		handle_spawn_run_script;
	static Net::MsgHandlerCode		handle_start_info;
	static Net::MsgHandlerCode		handle_free_trick_objects;
	static Net::MsgHandlerCode		handle_obs_log_trick_objects;
	static Net::MsgHandlerCode		handle_obs_init_graffiti_state;
	static Net::MsgHandlerCode		handle_face_data;
		
	friend bool ScriptSetGameType(Script::CStruct *pParams, Script::CScript *pScript);
	friend bool ScriptInTeamGame(Script::CStruct *pParams, Script::CScript *pScript);
	friend bool ScriptLaunchLevel(Script::CStruct *pParams, Script::CScript *pScript);
    friend bool ScriptSetTimeLimit(Script::CStruct *pParams, Script::CScript *pScript);
	
	// Exactly what it says. This should be the master flag for determining if a game is in progress.
	bool							m_gameInProgress;
	bool							m_levelLoaded;

	bool							m_endRun;

	uint32 							m_gameType;

	// needed so that we can restore the skatercamera
	// after skatercams and cutscenes...
	bool							m_lastFrameWasMovieCamera;


	// Keeps track of all the rules/options for this particular game mode
	Game::CGameMode*				mp_gameMode;

	// Keeps track of all the trick objects in the scene
	Obj::CTrickObjectManager*		mp_trickObjectManager;

	// Keeps track of all the trick names in the game
	Obj::CTrickChecksumTable*		mp_trickChecksumTable;

	// Keeps track of skater appearance, stats, etc.
	Obj::CPlayerProfileManager*		mp_PlayerProfileManager;

	// Keeps track of all the high scores and suchlike
	Records::CGameRecords*			mp_gameRecords;

	// Keeps track of splitscreen preferences
	Prefs::Preferences*				mp_splitscreen_preferences;

	uint32							m_requested_game_type;
	bool							m_levelChanged;

	SScoreGoal						m_ScoreGoal[vMAX_SCORE_GOALS];
	CCompetition*					mp_competition;
	CHorse*							mp_horse;
	Obj::CMovieManager*				mp_movieManager;
	Obj::CMovieManager*				mp_objectAnimManager;

	Gfx::EShadowType				m_shadow_mode;
	
	int								m_time_limit;

public:
	void 							BeepTimer(float time, float beep_time, float beep_speed, const char *p_script_name);

	bool							ShouldAllocateInternetHeap( void );
	bool							ShouldAllocateNetMiscHeap( void );

	uint32							m_requested_level;
	uint32							m_cur_level;
	uint32							m_prev_level;
	
	bool							m_new_record;
		
	void							GetRecordsText(int level);
	void 							UpdateRecords();

	void							SetShadowMode( Gfx::EShadowType shadowMode );
	Gfx::EShadowType				GetShadowMode( void ) const;
	
	Records::CGameRecords*			GetGameRecords() {return mp_gameRecords;}

	Obj::CGapChecklist		*		GetGapChecklist();
	
// Some handy dandy functions for accessing the high score goals 	
	int								GetScoreGoalScore(int n){return m_ScoreGoal[n].score;}
	void							SetScoreGoalScore(int n, int score){m_ScoreGoal[n].score = score;}
	uint32							GetScoreGoalScript(int n){return m_ScoreGoal[n].script;}
	void							SetScoreGoalScript(int n, uint32 script){m_ScoreGoal[n].script = script;}	
	int								GetScoreGoalGoal(int n){return m_ScoreGoal[n].goal;}
	void							SetScoreGoalGoal(int n, uint32 goal){m_ScoreGoal[n].goal = goal;}
	void							ClearScoreGoals() {for (int i =0;i

#include 
#include 

#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

//#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Mdl
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define	vFORCE_UPDATE_INTERVAL	60
#define vOBJ_UPDATE_THRESHOLD	FEET( 150 )

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

class ObjUpdateInfo
{
public:
	Net::BitStream			m_BitStream;
	int 					m_Handle;
	GameNet::PlayerInfo* 	m_Player;
};

/*****************************************************************************
**							   Private Classes								**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/* Create all network-related tasks and handlers                  */
/*                                                                */
/******************************************************************/

void	Skate::net_setup( void )
{
	// Server net tasks
	m_object_update_task = new Tsk::Task< Skate > ( Skate::s_object_update_code, *this,
										Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_OBJECT_UPDATE );
	m_score_update_task = new Tsk::Task< Skate > ( Skate::s_score_update_code, *this,
 										Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_SCORE_UPDATE );
}

/******************************************************************/
/* delete all network-related tasks and handlers                  */
/*                                                                */
/******************************************************************/

void	Skate::net_shutdown( void )
{
	// Server net tasks
	delete m_object_update_task;
	delete m_score_update_task;
}

/******************************************************************/
/* Determines whether we should send "send_player" an update 	  */
/* concerning "object"                                            */
/******************************************************************/

bool		Skate::should_send_update( GameNet::PlayerInfo* send_player, Obj::CObject* object )
{
	Obj::SPosEvent* latest_state;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::PlayerInfo* skater_player;
	Obj::CSkater* skater_obj;
	int id, index;
	
	// Dont send a client updates on his own skater. He is up to date.
	skater_player = gamenet_man->GetPlayerByObjectID( object->GetID() );
	if(	( skater_player == NULL ) ||
		( skater_player == send_player ))
	{
		return false;
	}
	
	skater_obj = skater_player->m_Skater;
	// Check if we have something to report
	if( skater_obj->GetStateHistory()->GetNumPosUpdates() == 0 )
	{
		//Dbg_Printf( "Not enough pos updates\n" );
		return false;
	}

	id = skater_obj->GetID();
	index = ( skater_obj->GetStateHistory()->GetNumPosUpdates() - 1 ) % Obj::CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
	latest_state = &skater_obj->GetStateHistory()->GetPosHistory()[index];

	// Only send an update if there's something new to tell
	if( latest_state->LoTime == send_player->m_LastSentProps.m_LastSkaterUpdateTime[id] )
	{
		//Dbg_Printf( "Nothing new to tell\n" );
		return false;
	}

	return true;
}

/******************************************************************/
/* Decide which fields need to be updated for this object		  */
/*                                                                */
/******************************************************************/

static	int		s_get_update_flags( GameNet::PlayerInfo* player, 
									Obj::SPosEvent* latest_state,
									int skater_id ) 
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	int i, update_flags;
	short pos[3];
	short rot[3];
	Flags< int > skater_flags;
	Flags< int > end_run_flags;
	Mth::Vector eulers;
	char state, doing_trick, terrain, walking, driving;
	bool forced_send;
	unsigned char id;
	sint16 rail_node;

	update_flags = 0;

	// Force an object update every N frames
	forced_send = !( gamenet_man->GetServer()->m_FrameCounter % vFORCE_UPDATE_INTERVAL ); 
	id = skater_id;

	for( i = 0; i < 3; i++ )
	{
		eulers[i] = latest_state->Eulers[i];
	}

	// Write out the object's position as three shorts (fixed-point)
	for( i = 0; i < 3; i++ )
	{
		if( i == Y )
		{
			pos[i] = (short) ( latest_state->Position[i] * 4.0f );
			//pos[i] = latest_state->Position[i];
		}
		else
		{
			pos[i] = (short) ( latest_state->Position[i] * 2.0f );
			//pos[i] = latest_state->Position[i];
		}
		if(( pos[i] != player->m_LastSentProps.m_LastSkaterPosUpdate[id][i] ) || forced_send )
		{
			if( i == X )
			{
				update_flags |= GameNet::mUPDATE_FIELD_POS_X;
			}
			else if( i == Y )
			{
				update_flags |= GameNet::mUPDATE_FIELD_POS_Y;
			}
			else if( i == Z )
			{
				update_flags |= GameNet::mUPDATE_FIELD_POS_Z;
			}
		}
	}
	
	// Write out the object's orientation as three short euler angles (fixed-point)
	for( i = 0; i < 3; i++ )
	{
		rot[i] = (short) ( eulers[i] * 4096.0f );
		if(( rot[i] != player->m_LastSentProps.m_LastSkaterRotUpdate[id][i] ) || forced_send )
		{   
			if( i == X )
			{
				update_flags |= GameNet::mUPDATE_FIELD_ROT_X;
			}
			else if( i == Y )
			{
				update_flags |= GameNet::mUPDATE_FIELD_ROT_Y;
			}
			else if( i == Z )
			{
				update_flags |= GameNet::mUPDATE_FIELD_ROT_Z;
			}
		}
	}                                          

	state = (char) latest_state->State;
	doing_trick = (char) latest_state->DoingTrick;
	terrain = (char) latest_state->Terrain;
	walking = (char) latest_state->Walking;
	driving = (char) latest_state->Driving;

	if(( state != player->m_LastSentProps.m_LastSkaterStateUpdate[id] ) ||
	   ( doing_trick != player->m_LastSentProps.m_LastDoingTrickUpdate[id] ) ||
	   ( terrain != player->m_LastSentProps.m_LastSkaterTerrain[id] ) || 
	   ( walking != player->m_LastSentProps.m_LastSkaterWalking[id] ) || 
	   ( driving != player->m_LastSentProps.m_LastSkaterDriving[id] ) || 
	   ( forced_send == true ))
	{
		update_flags |= GameNet::mUPDATE_FIELD_STATE;
	}

	skater_flags = latest_state->SkaterFlags;
	end_run_flags = latest_state->EndRunFlags;

	if(( skater_flags != player->m_LastSentProps.m_LastSkaterFlagsUpdate[id] ) || 
	   ( end_run_flags != player->m_LastSentProps.m_LastEndRunFlagsUpdate[id] ) || 
	   ( forced_send == true ))
	{
		update_flags |= GameNet::mUPDATE_FIELD_FLAGS;
	}

	rail_node = latest_state->RailNode;
	if(( rail_node != player->m_LastSentProps.m_LastRailNodeUpdate[id] ) ||
	   ( forced_send == true ))
	{
		update_flags |= GameNet::mUPDATE_FIELD_RAIL_NODE;
	}

	return update_flags;
}

/******************************************************************/
/* Place positional/state update data in the outbound stream	  */
/*                                                                */
/******************************************************************/

void		Skate::s_object_update( Obj::CObject* object, void* data )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::PlayerInfo* player, *skater_player;
	ObjUpdateInfo* update_info;
	Obj::CSkater* skater_obj;
	Mth::Vector eulers;
	short pos[3];
	short rot[3];
	unsigned char id;
	char state, doing_trick, terrain, walking, driving;
	Flags< int > skater_flags;
	Flags< int > end_run_flags;
	int update_flags;
	Obj::SPosEvent* latest_state;
	int i, index;
	unsigned short time;
	
	Dbg_Assert( data );

	update_info = (ObjUpdateInfo *) data;

	skater_player = gamenet_man->GetPlayerByObjectID( object->GetID() );
	skater_obj = skater_player->m_Skater;
	id = (char) skater_obj->GetID();
	player = update_info->m_Player;
	index = ( skater_obj->GetStateHistory()->GetNumPosUpdates() - 1 ) % Obj::CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
	latest_state = &skater_obj->GetStateHistory()->GetPosHistory()[index];
	player->m_LastSentProps.m_LastSkaterUpdateTime[id] = latest_state->GetTime();

	for( i = 0; i < 3; i++ )
	{
		eulers[i] = latest_state->Eulers[i];
	}

	time = latest_state->LoTime;
	update_info->m_BitStream.WriteValue( time, sizeof( uint16 ) * 8 );

	update_flags = s_get_update_flags( player, latest_state, id );
	update_info->m_BitStream.WriteValue( update_flags, 9 );

	//Dbg_Printf( "*** Sending Pos[%.2f %.2f %.2f]\n", latest_state->Position[X], latest_state->Position[Y], latest_state->Position[Z] );
	// Write out the object's position as three shorts (fixed-point)
	for( i = 0; i < 3; i++ )
	{
		if( i == Y )
		{
			pos[i] = (short) ( latest_state->Position[i] * 4.0f );
			//pos[i] = latest_state->Position[i];
		}
		else
		{
			pos[i] = (short) ( latest_state->Position[i] * 2.0f );
			//pos[i] = latest_state->Position[i];
		}
		
		if( i == X )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_POS_X )
			{
				update_info->m_BitStream.WriteValue( pos[i], sizeof( short ) * 8 );
				//update_info->m_BitStream.WriteFloatValue( pos[i] );
			}
		}
		else if( i == Y )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_POS_Y )
			{
				update_info->m_BitStream.WriteValue( pos[i], sizeof( short ) * 8 );
				//update_info->m_BitStream.WriteFloatValue( pos[i] );
			}
		}
		else if( i == Z )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_POS_Z )
			{
				update_info->m_BitStream.WriteValue( pos[i], sizeof( short ) * 8 );
				//update_info->m_BitStream.WriteFloatValue( pos[i] );
			}
		}
		
		player->m_LastSentProps.m_LastSkaterPosUpdate[id][i] = pos[i];
	}
	
	// Write out the object's orientation as three short euler angles (fixed-point)
	for( i = 0; i < 3; i++ )
	{
		rot[i] = (short) ( eulers[i] * 4096.0f );
		if( i == X )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_ROT_X )
			{
				update_info->m_BitStream.WriteValue( rot[i], sizeof( short ) * 8 );
			}
		}
		else if( i == Y )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_ROT_Y )
			{
				update_info->m_BitStream.WriteValue( rot[i], sizeof( short ) * 8 );
			}
		}
		else if( i == Z )
		{
			if( update_flags & GameNet::mUPDATE_FIELD_ROT_Z )
			{
				update_info->m_BitStream.WriteValue( rot[i], sizeof( short ) * 8 );
			}
		}
		
		player->m_LastSentProps.m_LastSkaterRotUpdate[id][i] = rot[i];
	}                                          

	state = (char) latest_state->State;
	doing_trick = (char) latest_state->DoingTrick;
	terrain = (char) latest_state->Terrain;
	walking = (char) latest_state->Walking;
	driving = (char) latest_state->Driving;

	// Write out the skaters' state
	// Write out the skater's "doing trick" flag
	// Write out the skater's terrain type
	if( update_flags & GameNet::mUPDATE_FIELD_STATE )
	{
		char mask;

		mask = state;
		if( doing_trick )
		{
			mask |= GameNet::mDOING_TRICK_MASK;
		}

		update_info->m_BitStream.WriteValue( mask, 4 );
		update_info->m_BitStream.WriteValue( terrain, 6 );
		update_info->m_BitStream.WriteValue( walking, 1 );
		update_info->m_BitStream.WriteValue( driving, 1 );

		player->m_LastSentProps.m_LastSkaterStateUpdate[id] = state;
		player->m_LastSentProps.m_LastDoingTrickUpdate[id] = doing_trick;
		player->m_LastSentProps.m_LastSkaterTerrain[id] = terrain;
		player->m_LastSentProps.m_LastSkaterWalking[id] = walking;
		player->m_LastSentProps.m_LastSkaterDriving[id] = driving;
	}

	// Write out the skaters' flags
	skater_flags = latest_state->SkaterFlags;
	end_run_flags = latest_state->EndRunFlags;

	if( update_flags & GameNet::mUPDATE_FIELD_FLAGS )
	{
		player->m_LastSentProps.m_LastSkaterFlagsUpdate[id] = skater_flags;
		player->m_LastSentProps.m_LastEndRunFlagsUpdate[id] = end_run_flags;
		update_info->m_BitStream.WriteValue( skater_flags, 5 );
		update_info->m_BitStream.WriteValue( end_run_flags, 3 );
	}

	// Write out the skaters' rail node
	if( update_flags & GameNet::mUPDATE_FIELD_RAIL_NODE )
	{
		player->m_LastSentProps.m_LastRailNodeUpdate[id] = latest_state->RailNode;
		update_info->m_BitStream.WriteValue( latest_state->RailNode, sizeof( sint16 ) * 8 );
	}
	
}

/******************************************************************/
/* Update Clients with objects' positional/rotational/anim data	  */
/*                                                                */
/******************************************************************/

void		Skate::s_object_update_code ( const Tsk::Task< Skate >& task )
{   
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::PlayerInfo* player;
	Lst::Search< GameNet::PlayerInfo > sh;
	Net::Server* server;
    
    Dbg_AssertType ( &task, Tsk::Task< Skate > );
	Skate& mdl = task.GetData();
	
    server = gamenet_man->GetServer();

	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = 
				gamenet_man->NextPlayerInfo( sh, true ))
	{
		Net::MsgMax update_msg;
		unsigned int length;
		ObjUpdateInfo update_info;
		GameNet::PlayerInfo* update_player;
		int i, start_id, obj_id, num_updates;
        
		// Don't send updates to the local client. His stuff is already up-to-date
		if( player->m_Conn->IsRemote())
		{
			int obj_id_mask;

			update_info.m_BitStream.SetOutputData( update_msg.m_Data, 4096 );	// skip Obj ID Mask
			update_info.m_Handle = player->m_Conn->GetHandle();
			update_info.m_Player = player;
					
			obj_id_mask = 0;

			// First decide which objects' updates we will send, then append them to the stream
			// in increasing id order 
			num_updates = 0;
			start_id = ( player->GetLastObjectUpdateID() + 1 ) % vMAX_SKATERS;
			for( i = 0; i < vMAX_SKATERS; i++ )
			{
				obj_id = ( start_id + i ) % vMAX_SKATERS;
				update_player = gamenet_man->GetPlayerByObjectID( obj_id );
				if( update_player )
				{
					if( mdl.should_send_update( player, update_player->m_Skater ))
					{
						obj_id_mask |= ( 1 << obj_id );
						if( ++num_updates >= player->GetMaxObjectUpdates())
						{
							break;
						}
					}
				}
			}

			if( num_updates == 0 )
			{
				continue;
			}

			update_info.m_BitStream.WriteValue( obj_id_mask, sizeof( char ) * 8 );
			for( i = 0; i < vMAX_SKATERS; i++ )
			{
				if( obj_id_mask & ( 1 << i ))
				{
					update_player = gamenet_man->GetPlayerByObjectID( i );

					s_object_update( update_player->m_Skater, &update_info );
					player->SetLastObjectUpdateID( i );
				}
			}

			update_info.m_BitStream.Flush();
			length = update_info.m_BitStream.GetByteLength();

			if( length > 0 )
			{   
				Net::MsgDesc msg_desc;

				msg_desc.m_Data = &update_msg;
				msg_desc.m_Length = length;
				msg_desc.m_Id = GameNet::MSG_ID_OBJ_UPDATE_STREAM;
				server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
			}
		}
	}
}

/******************************************************************/
/* Update Clients with other players' scores					  */
/*                                                                */
/******************************************************************/

void		Skate::SendScoreUpdates( bool final )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::PlayerInfo* player, *score_player;
	Lst::Search< GameNet::PlayerInfo > sh, score_sh;

    for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = 
				gamenet_man->NextPlayerInfo( sh, true ))
	{
		GameNet::MsgScoreUpdate score_msg;
		char num_scores;
		int length;
		char* data;
        
		score_msg.m_Final = (char) final;
		if( final )
		{
			score_msg.m_TimeLeft = 0;
		}
		else
		{
			score_msg.m_TimeLeft = Tmr::InSeconds( GetGameMode()->GetTimeLeft());
		}
		num_scores = 0;
		data = score_msg.m_ScoreData;
		for( score_player = gamenet_man->FirstPlayerInfo( score_sh ); score_player; score_player = 
			gamenet_man->NextPlayerInfo( score_sh ))
		{
			bool send_all_scores;

			send_all_scores = 	final ||
								( GetGameMode()->IsTrue( "should_modulate_color" )) ||
								( GetGameMode()->ShouldTrackTrickScore() == false );
			if( ( score_player != player ) || ( send_all_scores ) )
			{
				char id;
				int score;
				bool in_goal_attack;
				
				id = (char) score_player->m_Skater->GetID();
				in_goal_attack = GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netgoalattack" );

				if( in_goal_attack )
				{
					Game::CGoalManager* pGoalManager;
			 
					pGoalManager = Game::GetGoalManager();
					score = pGoalManager->NumGoalsBeatenBy( score_player->m_Skater->GetID());
				}
				else
				{
					score = gamenet_man->GetPlayerScore( score_player->m_Skater->GetID());
				}

				*data = id;
				data++;
				memcpy( data, &score, sizeof( int ));
				data += sizeof( int );

				num_scores++;
			}
		}

		if( num_scores > 0 )
		{
			Net::MsgDesc msg_desc;
            
			score_msg.m_Cheats = GetCheatChecksum();
			score_msg.m_NumScores = num_scores;
			length = 	sizeof( int ) +		// m_TimeLeft
						sizeof( int ) +		// m_Cheats
						sizeof( char ) + 	// m_Final
						sizeof( char ) +	// m_NumScores
						(( sizeof( char ) + sizeof( int )) * num_scores );

			msg_desc.m_Data = &score_msg;
			msg_desc.m_Length = length;
			msg_desc.m_Id = GameNet::MSG_ID_SCORE_UPDATE;
			// If it's the final tally or if it's reporting cheats, it HAS to get there
			if( final || ( score_msg.m_Cheats != 0 ))
			{
				msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
				msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
			}

			gamenet_man->GetServer()->EnqueueMessage( player->GetConnHandle(), &msg_desc );

			if( !player->IsLocalPlayer() && player->IsFullyIn())
			{
				GameNet::MsgCheatChecksum cheat_msg;

				cheat_msg.m_ServerChecksum = GetCheatChecksum();
				cheat_msg.m_ServerChecksum ^= 0xDEADFACE;
				msg_desc.m_Id = GameNet::MSG_ID_CHEAT_CHECKSUM_REQUEST;
				msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
				msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
				msg_desc.m_Data = &cheat_msg;
				msg_desc.m_Length = sizeof( GameNet::MsgCheatChecksum );
	
				gamenet_man->GetServer()->EnqueueMessage( player->GetConnHandle(), &msg_desc );
			}
		}
	}

	gamenet_man->SetLastScoreUpdateTime( gamenet_man->GetServer()->m_Timestamp );
}

/******************************************************************/
/* Periodic task to update Clients with other players' scores	  */
/*                                                                */
/******************************************************************/

void		Skate::s_score_update_code ( const Tsk::Task< Skate >& task )
{   
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Skate&	mdl = task.GetData();

	

	Dbg_AssertType ( &task, Tsk::Task< Skate > );

	if( gamenet_man->ShouldSendScoreUpdates() == false )
	{
		return;
	}

	if(( gamenet_man->GetServer()->m_Timestamp - gamenet_man->GetLastScoreUpdateTime()) <
			GameNet::vSCORE_UPDATE_FREQUENCY )
	{
		return;
	}

	mdl.SendScoreUpdates();
}

/*****************************************************************************
**						Server Handlers										**
*****************************************************************************/

/******************************************************************/
/* The client has requested a score event.  Decide whether or not */
/* to allow it (don't allow it if time is up, for example), and   */
/* relay the results back whenever we want to (which right now is */
/* immediately, but it doesn't have to be)						  */
/******************************************************************/

int	Skate::handle_score_request( Net::MsgHandlerContext* context )
{
	GameNet::MsgEmbedded* msg;
	GameNet::PlayerInfo* player;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
    
	msg = (GameNet::MsgEmbedded* ) context->m_Msg;
	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	if( player == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}
    
	Mdl::Score *pScore = player->m_Skater->GetScoreObject();

	switch( msg->m_SubMsgId )
	{
		case GameNet::SCORE_MSG_ID_LOG_TRICK_OBJECT:
		{
			GameNet::MsgScoreLogTrickObject* log_msg;
			
			log_msg = (GameNet::MsgScoreLogTrickObject* ) context->m_Msg;
			if( log_msg->m_GameId == gamenet_man->GetNetworkGameId())
			{
				pScore->LogTrickObject( log_msg->m_OwnerId, log_msg->m_Score, log_msg->m_NumPendingTricks, log_msg->m_PendingTrickBuffer, true );
			}
			break;
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Skater is reporting his high combo							  */
/* 						  										  */
/******************************************************************/

int Skate::handle_combo_report( Net::MsgHandlerContext* context )
{
	GameNet::PlayerInfo* player;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Skate* skate_mod;
	GameNet::MsgScoreLanded* msg;
    
	skate_mod = (Skate*) context->m_Data;

	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	if( player == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}
	msg = (GameNet::MsgScoreLanded*) context->m_Msg;

	// Ignore combo messages in freeskate
	if( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0x1c471c60,"netlobby"))
	{
		return Net::HANDLER_MSG_DONE;
	}

	// Make sure this reported score is for this game (could be late -- could be a cheat replay packet)
    if( gamenet_man->GetNetworkGameId() != msg->m_GameId )
	{
		Dbg_Printf( "Wrong game id! %d %d\n", gamenet_man->GetNetworkGameId(), msg->m_GameId );
		return Net::HANDLER_MSG_DONE;
	}

	// We should never get a score that's less than or equal to our recorded best combo for this
	// player since they are only supposed to report combo scores if they eclipse their current
	// mark.  This is most-likely cheating
	/*if( msg->m_Score <= player->m_BestCombo )
	{
		Dbg_Printf( "**** COMBO CHEAT DETECTED: Combo reported: %d , previous best: %d\n", msg->m_Score, player->m_BestCombo );
		gamenet_man->CheatingOccured();
		return Net::HANDLER_MSG_DONE;
	}*/

	//Dbg_Printf( "Player: %d  Combo: %d\n", player->m_Skater->GetID(), msg->m_Score );
	player->m_BestCombo = msg->m_Score;
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Skater landed a trick and is reporting his score		  		  */
/* 						  										  */
/******************************************************************/

int Skate::handle_landed_trick( Net::MsgHandlerContext* context )
{
	GameNet::PlayerInfo* player;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	int trick_score;
	Skate* skate_mod;
	GameNet::MsgScoreLanded* msg;
    
	skate_mod = (Skate*) context->m_Data;

	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	if( player == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}
	msg = (GameNet::MsgScoreLanded*) context->m_Msg;

	// Make sure this reported score is for this game (could be late -- could be a cheat replay packet)
    if( gamenet_man->GetNetworkGameId() != msg->m_GameId )
	{
		Dbg_Printf( "Wrong game id! %d %d\n", gamenet_man->GetNetworkGameId(), msg->m_GameId );
		return Net::HANDLER_MSG_DONE;
	}

	trick_score = msg->m_Score;
	Mdl::Score *pScore = player->m_Skater->GetScoreObject();

	if( trick_score > pScore->GetBestCombo())
	{
		pScore->SetBestCombo( trick_score );
	}
	if ( !skate_mod->GetGameMode()->IsTrue("should_modulate_color") )
	{
		if( skate_mod->GetGameMode()->ShouldTrackTrickScore())
		{
			if (skate_mod->GetGameMode()->ShouldAccumulateScore())
			{
				pScore->SetTotalScore( pScore->GetTotalScore() + trick_score );
			}
			else if( skate_mod->GetGameMode()->ShouldTrackBestCombo())
			{
				if( pScore->GetTotalScore() < trick_score )
				{
					pScore->SetTotalScore( trick_score );
				}
			}
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Skater is entering a car								  		  */
/* 						  										  */
/******************************************************************/

int Skate::handle_enter_vehicle_server( Net::MsgHandlerContext* context )
{
	GameNet::PlayerInfo* player;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Skate* skate_mod;
	GameNet::MsgEnterVehicle* msg;
    
	skate_mod = (Skate*) context->m_Data;

	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	if( player == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}
	msg = (GameNet::MsgEnterVehicle*) context->m_Msg;
	
	player->m_VehicleControlType = msg->m_ControlType;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Relay non-important msgs to all other clients		  		  */
/* 						  										  */
/******************************************************************/

int	Skate::handle_msg_relay( Net::MsgHandlerContext* context )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Lst::Search< GameNet::PlayerInfo > sh;
	GameNet::PlayerInfo* player, *orig_player;

	orig_player = gamenet_man->GetPlayerByConnection( context->m_Conn );

	// Make sure this is from a current player
	if( orig_player == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}

	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player;
			player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		Net::MsgDesc msg_desc;

		// Don't relay it back to the original client
		if( player == orig_player )
		{
			continue;
		}

		msg_desc.m_Data = context->m_Msg;
		msg_desc.m_Length = context->m_MsgLength;
		msg_desc.m_Id = context->m_MsgId;
		if( ( context->m_MsgId == GameNet::MSG_ID_BLOOD_OFF ) ||
			( context->m_MsgId == GameNet::MSG_ID_CHAT ) ||
			( context->m_MsgId == GameNet::MSG_ID_ENTER_VEHICLE ))
		{
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
		}
		else if( context->m_MsgId == GameNet::MSG_ID_SPAWN_PROJECTILE )
		{
			Skate* mod = (Skate*) context->m_Data;

			if(	( mod->GetGameMode()->GetNameChecksum() == CRCD(0x3d6d444f,"firefight") ) ||
				( mod->GetGameMode()->GetNameChecksum() == CRCD(0xbff33600,"netfirefight")))
			{
				GameNet::MsgProjectile* msg;
	
				msg = (GameNet::MsgProjectile*) context->m_Msg;
				if( ( msg->m_Scale <= 10.0f ) &&
					( msg->m_Radius <= 240 ))
				{
					msg->m_Id = orig_player->m_Skater->GetID();
					msg->m_Latency = context->m_Conn->GetAveLatency();
				}
			}
		}
			
		context->m_App->EnqueueMessage( player->GetConnHandle(), &msg_desc );
	}
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Relay script msgs to nearby clients only		  		  	  	  */
/* 						  										  */
/******************************************************************/

int	Skate::handle_selective_msg_relay( Net::MsgHandlerContext* context )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Lst::Search< GameNet::PlayerInfo > sh;
	GameNet::PlayerInfo* player, *orig_player;
	Obj::CSkater* orig_skater, *skater;

	orig_player = gamenet_man->GetPlayerByConnection( context->m_Conn );

	// Make sure this is from a current player
	if( orig_player == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}

	orig_skater = orig_player->m_Skater;
	if( orig_skater == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}

	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player;
			player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		bool send;

		send = false;
		// Don't relay it back to the original client
		if( player != orig_player )
		{
			if( player->IsObserving())
			{
				send = true;
			}
			else
			{
				skater = player->m_Skater;
				if( skater )
				{
					Mth::Vector diff;

					diff = skater->GetPos() - orig_skater->GetPos();
					if( diff.Length() < vOBJ_UPDATE_THRESHOLD )
					{
						send = true;
					}
				}
			}
		}

		if( send )
		{
			Net::MsgDesc msg_desc;

			msg_desc.m_Data = context->m_Msg;
			msg_desc.m_Length = context->m_MsgLength;
			msg_desc.m_Id = context->m_MsgId;
			context->m_App->EnqueueMessage( player->GetConnHandle(), &msg_desc );
		}
	}
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Relay script msgs to all other clients		  		  	  	  */
/* 						  										  */
/******************************************************************/

int	Skate::handle_script_relay( Net::MsgHandlerContext* context )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Lst::Search< GameNet::PlayerInfo > sh;
	GameNet::PlayerInfo* player, *orig_player;

	orig_player = gamenet_man->GetPlayerByConnection( context->m_Conn );

	// Make sure this is from a current player
	if( orig_player == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}

	for( player = gamenet_man->FirstPlayerInfo( sh, true ); player;
			player = gamenet_man->NextPlayerInfo( sh, true ))
	{
		Net::MsgDesc msg_desc;
		
		// Don't relay it back to the original client
		if( player == orig_player )
		{
			continue;
		}

		msg_desc.m_Data = context->m_Msg;
		msg_desc.m_Length = context->m_MsgLength;
		msg_desc.m_Id = context->m_MsgId;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
		context->m_App->EnqueueMessage( player->GetConnHandle(), &msg_desc );
	}

	if( context->m_MsgId == GameNet::MSG_ID_SPAWN_AND_RUN_SCRIPT )
	{
		GameNet::MsgSpawnAndRunScript* msg;

		msg = (GameNet::MsgSpawnAndRunScript *) context->m_Msg;

		// Only save the event if it changes the level in a permanent way
		if( msg->m_Permanent )
		{
			gamenet_man->AddSpawnedTriggerEvent( msg->m_Node, msg->m_ObjID, msg->m_ScriptName );
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Relay non-important msgs to all other clients		  		  */
/* 						  										  */
/******************************************************************/

int	Skate::handle_bail_done( Net::MsgHandlerContext* context )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::PlayerInfo* player;
    
	player = gamenet_man->GetPlayerByConnection( context->m_Conn );
	if( player )
	{
		player->ClearNonCollidable();
	}
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Change levels	  											  */
/* 						  										  */
/******************************************************************/

int	Skate::handle_change_level( Net::MsgHandlerContext* context )
{
	Skate* skate;
	skate = (Skate*)context->m_Data;

	GameNet::MsgChangeLevel* pMsg;
	pMsg = (GameNet::MsgChangeLevel*) context->m_Msg;
	
	skate->ChangeLevel( pMsg->m_Level );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Receive and store client skater states for relay later on	  */
/* 						  										  */
/******************************************************************/

int Skate::handle_object_update( Net::MsgHandlerContext* context )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Obj::CSkater* skater;
	GameNet::PlayerInfo* player;
	int i, index, last_index;
	unsigned int timestamp;
	char state, doing_trick, terrain, walking, driving;
	Flags< int > skater_flags;
	Flags< int > end_run_flags;
	short pos[3];
	Mth::Vector eulers;
	sint16 rail_node;
    short fixed;
	int update_flags;
	Obj::SPosEvent* latest_state, *previous_state;
	Net::BitStream stream;

    player = gamenet_man->GetPlayerByConnection( context->m_Conn );

	// Ignore late/foreign updates and update from non-ready/non-existent clients
	if( ( player == NULL ) || 
		( player->m_Conn->TestStatus( Net::Conn::mSTATUS_READY ) == false ) ||
        ( context->m_PacketFlags & ( Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN )))
	{
		int size, value, bit_data;

		size = 0;
		bit_data = 0;
		
		// Even though we don't process the data, we need to calculate the msg length
		// so that dispatcher knows how many bytes to skip
		stream.SetInputData( context->m_Msg, 1024 );

		bit_data += sizeof( int ) * 8;	// Timestamp size
		value = stream.ReadSignedValue( sizeof( int ) * 8 );
		update_flags = stream.ReadUnsignedValue( 9 );
		bit_data += 9;

		if( update_flags & GameNet::mUPDATE_FIELD_POS_X )
		{
			//bit_data += sizeof( float ) * 8;
			bit_data += sizeof( short ) * 8;
		}
		if( update_flags & GameNet::mUPDATE_FIELD_POS_Y )
		{
			//bit_data += sizeof( float ) * 8;
			bit_data += sizeof( short ) * 8;
		}
		if( update_flags & GameNet::mUPDATE_FIELD_POS_Z )
		{
			//bit_data += sizeof( float ) * 8;
			bit_data += sizeof( short ) * 8;
		}   
		if( update_flags & GameNet::mUPDATE_FIELD_ROT_X )
		{
			bit_data += sizeof( short ) * 8;
		}
		if( update_flags & GameNet::mUPDATE_FIELD_ROT_Y )
		{
			bit_data += sizeof( short ) * 8;
		}
		if( update_flags & GameNet::mUPDATE_FIELD_ROT_Z )
		{
			bit_data += sizeof( short ) * 8;
		}
		if( update_flags & GameNet::mUPDATE_FIELD_STATE )
		{   
			bit_data += 12;
		}
		if( update_flags & GameNet::mUPDATE_FIELD_FLAGS )
		{
			bit_data += 5;
			bit_data += 3;
		}              
		if( update_flags & GameNet::mUPDATE_FIELD_RAIL_NODE )
		{
			bit_data += 16;
		}

		size += (( bit_data + 7 ) >> 3 );

		context->m_MsgLength = size;
		return Net::HANDLER_CONTINUE;
	}

	skater = player->m_Skater;
	if( skater == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}
	
	index = skater->GetStateHistory()->GetNumPosUpdates() % Obj::CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
	latest_state = &skater->GetStateHistory()->GetPosHistory()[index];
	previous_state = NULL;
    
	if( skater->GetStateHistory()->GetNumPosUpdates() > 0 )
	{
		last_index = ( skater->GetStateHistory()->GetNumPosUpdates() + ( Obj::CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS - 1 )) % 
					Obj::CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
		previous_state = &skater->GetStateHistory()->GetPosHistory()[last_index];

        doing_trick = previous_state->DoingTrick;
		state = previous_state->State;
		skater_flags = previous_state->SkaterFlags;
		end_run_flags = previous_state->EndRunFlags;
		terrain = previous_state->Terrain;
		walking = previous_state->Walking;
		driving = previous_state->Driving;
		rail_node = previous_state->RailNode;
		for( i = 0; i < 3; i++ )
		{
			pos[i] = previous_state->ShortPos[i];
		}
		for( i = 0; i < 3; i++ )
		{
			eulers[i] = previous_state->Eulers[i];
		}
	}
	else
	{
		doing_trick = 0;
		state = 0;
		skater_flags = 0;
		end_run_flags = 0;
		terrain = 0;
		walking = 0;
		driving = 0;
		rail_node = -1;
		for( i = 0; i < 3; i++ )
		{
			pos[i] = 0;
		}
		for( i = 0; i < 3; i++ )
		{
			eulers[i] = 0;
		}
	}
	

	stream.SetInputData( context->m_Msg, 1024 );

	// Read in timestamp
	timestamp = stream.ReadUnsignedValue( sizeof( int ) * 8 );
	update_flags = stream.ReadUnsignedValue( 9 );
	
	// Read in fixed point position
	if( update_flags & GameNet::mUPDATE_FIELD_POS_X )
	{
		pos[X] = stream.ReadSignedValue( sizeof( short ) * 8 );
		//pos[X] = stream.ReadFloatValue();
	}
	if( update_flags & GameNet::mUPDATE_FIELD_POS_Y )
	{
		pos[Y] = stream.ReadSignedValue( sizeof( short ) * 8 );
		//pos[Y] = stream.ReadFloatValue();
	}
	if( update_flags & GameNet::mUPDATE_FIELD_POS_Z )
	{
		pos[Z] = stream.ReadSignedValue( sizeof( short ) * 8 );
		//pos[Z] = stream.ReadFloatValue();
	}   
			
	// Read in fixed point eulers
	if( update_flags & GameNet::mUPDATE_FIELD_ROT_X )
	{
		fixed = stream.ReadSignedValue( sizeof( short ) * 8 );
		eulers[X] = ((float) fixed ) / 4096.0f;
	}
	if( update_flags & GameNet::mUPDATE_FIELD_ROT_Y )
	{
		fixed = stream.ReadSignedValue( sizeof( short ) * 8 );
		eulers[Y] = ((float) fixed ) / 4096.0f;
	}
	if( update_flags & GameNet::mUPDATE_FIELD_ROT_Z )
	{
		fixed = stream.ReadSignedValue( sizeof( short ) * 8 );
		eulers[Z] = ((float) fixed ) / 4096.0f;
	}

	// Read in skater's 'state'
	if( update_flags & GameNet::mUPDATE_FIELD_STATE )
	{   
		char mask;

		mask = stream.ReadUnsignedValue( 4 );

		state = mask & GameNet::mSTATE_MASK;
		doing_trick = (( mask & GameNet::mDOING_TRICK_MASK ) != 0 );
		
		terrain = stream.ReadUnsignedValue( 6 );
		
		walking = stream.ReadUnsignedValue( 1 );
		driving = stream.ReadUnsignedValue( 1 );
	}

	if( update_flags & GameNet::mUPDATE_FIELD_FLAGS )
	{
		skater_flags = stream.ReadUnsignedValue( 5 );
		end_run_flags = stream.ReadUnsignedValue( 3 );
	}                           

	if( update_flags & GameNet::mUPDATE_FIELD_RAIL_NODE )
	{
		rail_node = stream.ReadSignedValue( sizeof( sint16 ) * 8 );
	}                           
	

	// Tell the dispatcher the size of this message because it does not know (optimization)
	context->m_MsgLength = stream.GetByteLength();

	if( ( skater->GetStateHistory()->GetNumPosUpdates() > 0 ) &&
		( timestamp < previous_state->GetTime()))
	{
		return Net::HANDLER_MSG_DONE;
	}

	// Use those euler angles to compose a matrix
	Mth::Matrix obj_matrix( eulers[X], eulers[Y], eulers[Z] );
    
	latest_state->Matrix = obj_matrix;
	latest_state->Position.Set(	((float) pos[X] ) / 2.0f,//pos[X],
				  				((float) pos[Y] ) / 4.0f,//pos[Y],
				  				((float) pos[Z] ) / 2.0f,//pos[Z],
				  				0.0f );

	latest_state->SetTime( timestamp );

	// Update short versions in the player history
	for( i = 0; i < 3; i++ )
	{
		latest_state->ShortPos[i] = pos[i];
		latest_state->Eulers[i] = eulers[i];
	}

	latest_state->DoingTrick = doing_trick;
	latest_state->SkaterFlags = skater_flags;
	latest_state->EndRunFlags = end_run_flags;
	latest_state->State = state;
	latest_state->Terrain = (ETerrainType) terrain;
	latest_state->RailNode = rail_node;
	latest_state->Walking = walking;
	latest_state->Driving = driving;

	skater->GetStateHistory()->IncrementNumPosUpdates();
	
	return Net::HANDLER_CONTINUE;
}

/*****************************************************************************
**						Client Handlers										**
*****************************************************************************/

/******************************************************************/
/* Execute a script	  									  		  */
/* 						  										  */
/******************************************************************/

int Skate::handle_run_script( Net::MsgHandlerContext* context )
{
	Skate* mod;
	GameNet::MsgRunScript* msg;
    Script::CScriptStructure *pParams;
	Obj::CMovingObject* obj;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());

	mod = (Skate *) context->m_Data;
	msg = (GameNet::MsgRunScript *) context->m_Msg;

	obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjID );

    pParams = new Script::CScriptStructure;
	Script::ReadFromBuffer(pParams,(uint8 *) msg->m_Data );

	Script::RunScript( msg->m_ScriptName, pParams, obj );

	delete pParams;

	Mem::Manager::sHandle().PopContext();

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Spawn and run a script 										  */
/* 						  										  */
/******************************************************************/

int Skate::handle_spawn_run_script( Net::MsgHandlerContext* context )
{
	Skate* mod;
	Script::CScript* pScript;
	Obj::CMovingObject* obj;
	GameNet::MsgSpawnAndRunScript* msg;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	mod = (Skate *) context->m_Data;
	msg = (GameNet::MsgSpawnAndRunScript *) context->m_Msg;

    if( gamenet_man->ReadyToPlay())
	{
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjID );
	
		pScript = Script::SpawnScript( msg->m_ScriptName, NULL, 0, NULL, msg->m_Node ); // K: The 0,NULL bit means no callback script specified
		#ifdef __NOPT_ASSERT__
		pScript->SetCommentString("Spawned from Skate::handle_spawn_run_script");
		#endif
		pScript->mpObject = obj; 

		pScript->Update(); 
	}
	else
	{
		gamenet_man->AddSpawnedTriggerEvent( msg->m_Node, msg->m_ObjID, msg->m_ScriptName );
	}
	

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Unpacks wobble details	  								  	  */
/* 						  										  */
/******************************************************************/

static float s_get_wobble_details_from_mask( char mask, int field )
{
	float value;

	value = 0;
	switch( field )
	{
		case GameNet::MsgSetWobbleDetails::vWOBBLE_AMP_A:
		{
			int field_mask;

			field_mask = mask & GameNet::MsgSetWobbleDetails::mWOBBLE_AMPA_MASK;
            if( field_mask == 0 )
			{
				value = 0.05f;
			}
			else if( field_mask == 1 )
			{
				value = 0.1f;
			}
			else if( field_mask == 2 )
			{
				value = 10.1f;
			}
			else
			{
				Dbg_Assert( 0 );
			}
			break;
		}
		case GameNet::MsgSetWobbleDetails::vWOBBLE_AMP_B:
		{
			int field_mask;

			field_mask = mask & GameNet::MsgSetWobbleDetails::mWOBBLE_AMPB_MASK;
			field_mask >>= 2;
            if( field_mask == 0 )
			{
				value = 0.04f;
			}
			else if( field_mask == 1 )
			{
				value = 10.1f;
			}
			else
			{
				Dbg_Assert( 0 );
			}
			break;
		}
		case GameNet::MsgSetWobbleDetails::vWOBBLE_K1:
		{
			int field_mask;

			field_mask = mask & GameNet::MsgSetWobbleDetails::mWOBBLE_K1_MASK;
			field_mask >>= 3;
            if( field_mask == 0 )
			{
				value = 0.0022f;
			}
			else if( field_mask == 1 )
			{
				value = 20.0f;
			}
			else
			{
				Dbg_Assert( 0 );
			}
			break;
		}
		case GameNet::MsgSetWobbleDetails::vWOBBLE_K2:
		{
			int field_mask;

			field_mask = mask & GameNet::MsgSetWobbleDetails::mWOBBLE_K2_MASK;
			field_mask >>= 4;
            if( field_mask == 0 )
			{
				value = 0.0017f;
			}
			else if( field_mask == 1 )
			{
				value = 10.0f;
			}
			else
			{
				Dbg_Assert( 0 );
			}
			break;
		}
		case GameNet::MsgSetWobbleDetails::vSPAZFACTOR:
		{
			int field_mask;

			field_mask = mask & GameNet::MsgSetWobbleDetails::mSPAZFACTOR_MASK;
			field_mask >>= 5;
            if( field_mask == 0 )
			{
				value = 1.5f;
			}
			else if( field_mask == 1 )
			{
				value = 1.0f;
			}
			else
			{
				Dbg_Assert( 0 );
			}
			break;
		}
		default:
			Dbg_Assert( 0 );
			break;
	}

	return value;
}

/******************************************************************/
/* A client has launched a projectile							  */
/* 						  										  */
/******************************************************************/

int	Skate::handle_projectile( Net::MsgHandlerContext* context )
{
	GameNet::MsgProjectile* msg;
	Script::CStruct* pParams;
	Obj::CSkater* skater;
	Skate* mod;
	Mth::Vector vel, dir, pos;
	Tmr::Time lag;
	float time;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
    
	mod = (Skate *) context->m_Data;

	msg = (GameNet::MsgProjectile*) context->m_Msg;
	skater = mod->GetSkaterById( msg->m_Id );
	if( skater == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}
	
	vel = msg->m_Vel;
	pos = msg->m_Pos;
	dir = vel;
	dir.Normalize();
	lag = msg->m_Latency;

	// If not on the server, also add the average latency from server to us
	if( gamenet_man->OnServer() == false )
	{
		Lst::Search< Net::Conn > sh;
		Net::Conn* server_conn;

		server_conn = context->m_App->FirstConnection( &sh );
		lag += server_conn->GetAveLatency();
	}

    time = (float) lag / 1000.0f;
    pos += dir * time;	// Time is in units of seconds. Velocity is in terms of feet/sec

	pParams = new Script::CStruct;
	pParams->AddChecksum( CRCD(0x81c39e06,"owner_id"), msg->m_Id );
	pParams->AddInteger( CRCD(0x7323e97c,"x"), pos[X] );
	pParams->AddInteger( CRCD(0x424d9ea,"y"), pos[Y] );
	pParams->AddInteger( CRCD(0x9d2d8850,"z"), pos[Z] );
	pParams->AddInteger( CRCD(0xbded7a76,"scaled_x"), msg->m_Vel[X] );
	pParams->AddInteger( CRCD(0xcaea4ae0,"scaled_y"), msg->m_Vel[Y] );
	pParams->AddInteger( CRCD(0x53e31b5a,"scaled_z"), msg->m_Vel[Z] );
	pParams->AddInteger( CRCD(0xc48391a5,"radius"), msg->m_Radius );
	pParams->AddFloat( CRCD(0x13b9da7b,"scale"), msg->m_Scale );
	
	Script::RunScript( CRCD(0xaa2b5552,"ClientLaunchFireball"), pParams, skater );
	delete pParams;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* A client has entered a vehicle								  */
/* 						  										  */
/******************************************************************/

int	Skate::handle_enter_vehicle_client( Net::MsgHandlerContext* context )
{
	GameNet::MsgEnterVehicle* msg;
	Obj::CSkater* skater;
	Skate* mod;
    
	mod = (Skate *) context->m_Data;

	msg = (GameNet::MsgEnterVehicle*) context->m_Msg;
	skater = mod->GetSkaterById( msg->m_Id );
	if( skater == NULL )
	{
		return Net::HANDLER_MSG_DONE;
	}
	
	Dbg_Assert(GetSkaterStateHistoryComponentFromObject(skater));
	GetSkaterStateHistoryComponentFromObject(skater)->SetCurrentVehicleControlType(msg->m_ControlType);
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* The server has sent a list of cheats that are enabled		  */
/* 						  										  */
/******************************************************************/

int	Skate::handle_cheat_list( Net::MsgHandlerContext* context )
{
	GameNet::MsgEnabledCheats* msg;
	int i;

	Dbg_Printf( "**** HANDLING CHEAT LIST\n" );
	CFuncs::ScriptClearCheats( NULL, NULL );

	msg = (GameNet::MsgEnabledCheats*) context->m_Msg;
	for( i = 0; i < msg->m_NumCheats; i++ )
	{
		Script::CStruct* pParams;

		pParams = new Script::CStruct;
		pParams->AddChecksum( CRCD(0xf649d637,"on"), 0 );
		pParams->AddChecksum( CRCD(0xae94c183,"cheat_flag"), msg->m_Cheats[i] );
		Script::RunScript( CRCD(0x3cf9e86d,"client_toggle_cheat"), pParams );

		delete pParams;
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* The server has toggled a cheat. Mimic behavior on this end	  */
/* 						  										  */
/******************************************************************/

int Skate::handle_cheats( Net::MsgHandlerContext* context )
{
	GameNet::MsgToggleCheat* msg;
	Script::CStruct* pParams;

	pParams = new Script::CStruct;
	msg = (GameNet::MsgToggleCheat*) context->m_Msg;
	
	if( msg->m_On )
	{
		pParams->AddChecksum( CRCD(0xf649d637,"on"), 0 );
	}

	pParams->AddChecksum( CRCD(0xae94c183,"cheat_flag"), msg->m_Cheat );

	Script::RunScript( CRCD(0x3cf9e86d,"client_toggle_cheat"), pParams );

	delete pParams;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Get the number of times the 16-bit value was flipped since the */
/* last time			  										  */
/******************************************************************/

int	s_get_num_flips( uint32 new_time, uint32 old_time )
{
	uint32 hi_new, hi_old;

	hi_new = new_time >> 16;
	hi_old = old_time >> 16;

	return ( hi_new - hi_old );
}

/******************************************************************/
/* Animate non-local clients	  								  */
/* 						  										  */
/******************************************************************/

int Skate::handle_anims( Net::MsgHandlerContext* context )
{
	Obj::SAnimEvent* latest_event;
	Obj::CMovingObject* obj;
	Obj::CSkater* skater;
	Skate* mod;
    
	mod = (Skate *) context->m_Data;
    if( context->m_MsgId == GameNet::MSG_ID_PRIM_ANIM_START )
	{
		GameNet::MsgPlayPrimaryAnim anim_msg;
		char* stream;
        
        stream = context->m_Msg;
        
		//memcpy( &anim_msg.m_Time, stream, sizeof( unsigned short ));
		//Dbg_Printf( "(%d) Got Primary %d\n", context->m_App->m_FrameCounter, anim_msg.m_Time );
		//stream += sizeof( int );
		memcpy( &anim_msg.m_LoopingType, stream, sizeof( char ));
		stream++;
		memcpy( &anim_msg.m_ObjId, stream, sizeof( char ));
		stream++;
		memcpy( &anim_msg.m_Index, stream, sizeof( uint32 ));
		stream += sizeof( uint32 );
		memcpy( &anim_msg.m_StartTime, stream, sizeof( unsigned short ));
		stream += sizeof( unsigned short );
		memcpy( &anim_msg.m_EndTime, stream, sizeof( unsigned short ));
		stream += sizeof( unsigned short );
		memcpy( &anim_msg.m_BlendPeriod, stream, sizeof( unsigned short ));
		stream += sizeof( unsigned short );
		memcpy( &anim_msg.m_Speed, stream, sizeof( unsigned short ));
		stream += sizeof( unsigned short );

		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( anim_msg.m_ObjId );
		if( obj )
		{
			GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
			float blend_period;

			skater = static_cast< Obj::CSkater *> ( obj );
			Dbg_MsgAssert( skater != NULL,( "Got anim for non-CSkater" ));
			
			blend_period = (float) anim_msg.m_BlendPeriod / 4096.0f;

			// Only blend skaters that we are observing
			if ( gamenet_man->InNetGame() )
			{
				GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
				GameNet::PlayerInfo* observed_player, *anim_player;

				anim_player = gamenet_man->GetPlayerByObjectID( obj->GetID() );
				observed_player = gamenet_man->GetCurrentlyObservedPlayer();
				if(( observed_player == NULL ) || ( anim_player != observed_player ))
				{
					blend_period = 0.0f;
				}
			}
			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
				latest_event->m_MsgId = GameNet::MSG_ID_PRIM_ANIM_START;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				//Dbg_Printf( "(%d) Setting anm time: %d\n", context->m_App->m_FrameCounter, latest_event->GetTime());

				latest_event->m_ObjId = anim_msg.m_ObjId;
				latest_event->m_Asset = anim_msg.m_Index;
				latest_event->m_StartTime = (float) anim_msg.m_StartTime / 4096.0f;
				latest_event->m_EndTime = (float) anim_msg.m_EndTime / 4096.0f;
				latest_event->m_BlendPeriod = blend_period;
				latest_event->m_Speed = (float) anim_msg.m_Speed / 4096.0f;
				latest_event->m_LoopingType = anim_msg.m_LoopingType;

				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_SET_WOBBLE_TARGET )
	{
		GameNet::MsgSetWobbleTarget* msg;

		msg = (GameNet::MsgSetWobbleTarget *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{   
			skater = static_cast< Obj::CSkater *> ( obj );
			Dbg_MsgAssert( skater != NULL,( "Got anim for non-CSkater" ));

			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
				latest_event->m_MsgId = GameNet::MSG_ID_SET_WOBBLE_TARGET;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				//Dbg_Printf( "(%d) Got Wobble %d\n", context->m_App->m_FrameCounter, msg->m_Time );
				latest_event->m_ObjId = msg->m_ObjId;
				latest_event->m_Alpha = (float) msg->m_Alpha / 255.0f;
				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_ROTATE_SKATEBOARD )
	{
		GameNet::MsgRotateSkateboard* msg;

		msg = (GameNet::MsgRotateSkateboard *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{
			skater = static_cast< Obj::CSkater *> ( obj );
			Dbg_MsgAssert( skater != NULL,( "Got anim for non-CSkater" ));

			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
				latest_event->m_MsgId = GameNet::MSG_ID_ROTATE_SKATEBOARD;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
                //Dbg_Printf( "(%d) Got Rotate %d\n", context->m_App->m_FrameCounter, msg->m_Time );
				latest_event->m_ObjId = msg->m_ObjId;
				latest_event->m_Rotate = msg->m_Rotate;
				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_SET_WOBBLE_DETAILS )
	{
		GameNet::MsgSetWobbleDetails* msg;

		Obj::CMovingObject* obj;

		msg = (GameNet::MsgSetWobbleDetails *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{
			skater = static_cast< Obj::CSkater *> ( obj );
			Dbg_MsgAssert( skater != NULL,( "Got anim for non-CSkater" ));

			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
				latest_event->m_MsgId = GameNet::MSG_ID_SET_WOBBLE_DETAILS;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				//Dbg_Printf( "(%d) Got details %d\n", context->m_App->m_FrameCounter, msg->m_Time );
				latest_event->m_ObjId = msg->m_ObjId;
				latest_event->m_WobbleAmpA = s_get_wobble_details_from_mask( msg->m_WobbleDetails, GameNet::MsgSetWobbleDetails::vWOBBLE_AMP_A );
				latest_event->m_WobbleAmpB = s_get_wobble_details_from_mask( msg->m_WobbleDetails, GameNet::MsgSetWobbleDetails::vWOBBLE_AMP_B );
				latest_event->m_WobbleK1 = s_get_wobble_details_from_mask( msg->m_WobbleDetails, GameNet::MsgSetWobbleDetails::vWOBBLE_K1 );
				latest_event->m_WobbleK2 = s_get_wobble_details_from_mask( msg->m_WobbleDetails, GameNet::MsgSetWobbleDetails::vWOBBLE_K2 );
				latest_event->m_SpazFactor = s_get_wobble_details_from_mask( msg->m_WobbleDetails, GameNet::MsgSetWobbleDetails::vSPAZFACTOR );

				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_SET_LOOPING_TYPE )
	{
		GameNet::MsgSetLoopingType* msg;

		Obj::CMovingObject* obj;

		msg = (GameNet::MsgSetLoopingType *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{
			skater = static_cast< Obj::CSkater *> ( obj );
			Dbg_MsgAssert( skater != NULL,( "Got anim for non-CSkater" ));

			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
                latest_event->m_MsgId = GameNet::MSG_ID_SET_LOOPING_TYPE;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				//Dbg_Printf( "(%d) Got looping %d\n", context->m_App->m_FrameCounter, msg->m_Time );
				latest_event->m_ObjId = msg->m_ObjId;
				latest_event->m_LoopingType = msg->m_LoopingType;

				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_SET_HIDE_ATOMIC )
	{
		GameNet::MsgHideAtomic* msg;

		Obj::CMovingObject* obj;

		msg = (GameNet::MsgHideAtomic *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{
			skater = static_cast< Obj::CSkater *> ( obj );
			Dbg_MsgAssert( skater != NULL,( "Got anim for non-CSkater" ));

			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
                latest_event->m_MsgId = GameNet::MSG_ID_SET_HIDE_ATOMIC;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				latest_event->m_ObjId = msg->m_ObjId;
				latest_event->m_Asset = msg->m_AtomicName;
				latest_event->m_Hide = msg->m_Hide;

				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_SET_ANIM_SPEED )
	{
		GameNet::MsgSetAnimSpeed* msg;

		Obj::CMovingObject* obj;

		msg = (GameNet::MsgSetAnimSpeed *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{
			skater = static_cast< Obj::CSkater *> ( obj );
			Dbg_MsgAssert( skater != NULL,( "Got anim for non-CSkater" ));

			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
                latest_event->m_MsgId = GameNet::MSG_ID_SET_ANIM_SPEED;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				//Dbg_Printf( "(%d) Got speed %d\n", context->m_App->m_FrameCounter, msg->m_Time );
				latest_event->m_ObjId = msg->m_ObjId;
				latest_event->m_Speed = msg->m_AnimSpeed;

				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_FLIP_ANIM )
	{
		GameNet::MsgFlipAnim* msg;

		Obj::CMovingObject* obj;
		
		msg = (GameNet::MsgFlipAnim *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{
			skater = mod->GetSkaterById( msg->m_ObjId );
			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
                latest_event->m_MsgId = GameNet::MSG_ID_FLIP_ANIM;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				//Dbg_Printf( "(%d) Got flip %d\n", context->m_App->m_FrameCounter, msg->m_Time );
				latest_event->m_Flipped = msg->m_Flipped;
				latest_event->m_ObjId = msg->m_ObjId;

				skater->GetStateHistory()->IncrementNumAnimUpdates();

				// If we got a flip message for a skater, we've now handled it. Otherwise,
				// let it fall through as it is for some other object in the game
				return Net::HANDLER_MSG_DONE;
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_ROTATE_DISPLAY )
	{
		GameNet::MsgRotateDisplay* msg;

		Obj::CMovingObject* obj;
		
		msg = (GameNet::MsgRotateDisplay *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{
			skater = mod->GetSkaterById( msg->m_ObjId );
			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
                latest_event->m_MsgId = GameNet::MSG_ID_ROTATE_DISPLAY;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				//Dbg_Printf( "(%d) Got rotate %d\n", context->m_App->m_FrameCounter, msg->m_Time );
				latest_event->m_ObjId = msg->m_ObjId;
				latest_event->m_Duration = msg->m_Duration;
				latest_event->m_SinePower = msg->m_SinePower;
				latest_event->m_StartAngle = msg->m_StartAngle;
				latest_event->m_DeltaAngle = msg->m_DeltaAngle;
				latest_event->m_HoldOnLastAngle = msg->m_HoldOnLastAngle;
				latest_event->m_Flags = msg->m_Flags;

				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_CLEAR_ROTATE_DISPLAY )
	{
		GameNet::MsgObjMessage* msg;

		Obj::CMovingObject* obj;
		
		msg = (GameNet::MsgObjMessage *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{
			skater = mod->GetSkaterById( msg->m_ObjId );
			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
                latest_event->m_MsgId = GameNet::MSG_ID_CLEAR_ROTATE_DISPLAY;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				latest_event->m_ObjId = msg->m_ObjId;

				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_CREATE_SPECIAL_ITEM )
	{    
		Obj::CMovingObject* obj;
		GameNet::MsgSpecialItem* msg;

		msg = (GameNet::MsgSpecialItem *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{
			skater = mod->GetSkaterById( msg->m_ObjId );
			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
                latest_event->m_MsgId = GameNet::MSG_ID_CREATE_SPECIAL_ITEM;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				latest_event->m_ObjId = msg->m_ObjId;
				latest_event->m_Bone = msg->m_Bone;
				latest_event->m_Index = msg->m_Index;
				latest_event->m_Asset = msg->m_Params;

				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	else if( context->m_MsgId == GameNet::MSG_ID_DESTROY_SPECIAL_ITEM )
	{
		Obj::CMovingObject* obj;
		GameNet::MsgSpecialItem* msg;

		msg = (GameNet::MsgSpecialItem *) context->m_Msg;
		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
		if( obj )
		{
			skater = mod->GetSkaterById( msg->m_ObjId );
			if( skater )
			{
				latest_event = skater->GetStateHistory()->GetLatestAnimEvent();
                latest_event->m_MsgId = GameNet::MSG_ID_DESTROY_SPECIAL_ITEM;
				latest_event->SetTime( skater->GetStateHistory()->GetLastPosEvent()->GetTime());
				latest_event->m_ObjId = msg->m_ObjId;
				latest_event->m_Index = msg->m_Index;

				skater->GetStateHistory()->IncrementNumAnimUpdates();
			}
		}
	}
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Skate::handle_flip_anim( Net::MsgHandlerContext* context ) 
{
	Skate* mod;

	mod = (Skate *) context->m_Data;
		
	GameNet::MsgFlipAnim* msg;

	Obj::CMovingObject* obj;
	msg = (GameNet::MsgFlipAnim *) context->m_Msg;
	obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
	if( obj )
	{
		Obj::CMovingObject* moving_obj;
	 
		moving_obj = static_cast< Obj::CMovingObject *> ( obj );
		Dbg_MsgAssert( moving_obj != NULL,( "Got object update for non-CMovingObj\n" ));
		
		if( moving_obj )
		{
			Obj::CAnimationComponent* pAnimationComponent = GetAnimationComponentFromObject( moving_obj );
			if ( pAnimationComponent )
			{
				pAnimationComponent->FlipAnimation( msg->m_ObjId, msg->m_Flipped, context->m_App->m_Timestamp, false );
			}
		}
	}
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Skate::handle_player_quit( Net::MsgHandlerContext* context )
{
	GameNet::PlayerInfo* player;
	Skate* mod;
	GameNet::MsgPlayerQuit* msg;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	Dbg_Printf( "Got player quit message" );

	mod = (Skate *) context->m_Data;
	msg = (GameNet::MsgPlayerQuit *) context->m_Msg; 
	player = gamenet_man->GetPlayerByObjectID( msg->m_ObjId );
	if( player )
	{   
		Obj::CSkater* quitting_skater;

		quitting_skater = player->m_Skater;
		
		Dbg_Printf( "Got player quit message 2" );
		gamenet_man->DropPlayer( player, (GameNet::DropReason) msg->m_Reason );
		mod->remove_skater( quitting_skater );
	}
	else
	{
		GameNet::NewPlayerInfo* pending_player;

		pending_player = gamenet_man->GetNewPlayerInfoByObjectID( msg->m_ObjId );
		if( pending_player )
		{
			gamenet_man->DestroyNewPlayer( pending_player );
		}
	}

	// The server may have been waiting for us to load this player.  If so, tell the server we're ready now.
	gamenet_man->RespondToReadyQuery();
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#if 0
static bool HostQuitDialogHandler( Front::EDialogBoxResult result )
{
	

	if( result == Front::DB_OK )
	{   
		Mdl::FrontEnd* front = Mdl::FrontEnd::Instance();
            
		Script::RunScript( "chosen_leave_server" );
		front->PauseGame( false );
		return false;
	}

	return true;
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Skate::handle_disconn_accepted( Net::MsgHandlerContext* context )
{
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();

	Dbg_Message( "Logging off of server\n" );

	Script::RunScript( "CreateServerQuitDialog" );
	gamenet_man->CleanupPlayers();
	gamenet_man->ClientShutdown();
 
	return Net::HANDLER_HALT;
}

/******************************************************************/
/* You've been kicked by the server								  */
/* 						  										  */
/******************************************************************/

int Skate::handle_kicked( Net::MsgHandlerContext* context )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Skate * skate_mod = Skate::Instance();

	

	Dbg_Message( "Logging off of server\n" );

	Script::DeleteSpawnedScripts();

	Script::CScriptStructure* pStructure = new Script::CScriptStructure;
	
	pStructure->AddComponent( Script::GenerateCRC( "width" ), ESYMBOLTYPE_INTEGER, 300 );
	pStructure->AddComponent( Script::GenerateCRC( "title" ), ESYMBOLTYPE_NAME, (int) Script::GenerateCRC("net_notice_msg"));
	if( context->m_MsgId == GameNet::MSG_ID_KICKED )
	{
		Script::RunScript( "CreateServerRemovedYouDialog" );
	}
	else if( context->m_MsgId == GameNet::MSG_ID_LEFT_OUT )
	{
		Script::RunScript( "CreateServerMovedOnDialog" );
	}
	
	if( skate_mod->m_requested_level == Script::GenerateCRC( "load_skateshop" ))
	{
		// If we got partially in, cancel the join and remove partially-loaded players
		// Shut the client down so that it doesn't bombard the server ip with messages
		gamenet_man->CancelJoinServer();
		gamenet_man->SetJoinState( GameNet::vJOIN_STATE_FINISHED );
	}
	else
	{
		gamenet_man->CleanupPlayers();
		gamenet_man->ClientShutdown();
	}

	delete pStructure;	
 
	return Net::HANDLER_HALT;
}

/******************************************************************/
/* Observer-specific trick object handling						  */
/* 						  										  */
/******************************************************************/

int Skate::handle_obs_log_trick_objects( Net::MsgHandlerContext* context )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::MsgObsScoreLogTrickObject* msg;
	Skate* skate_mod;

	skate_mod = (Skate*) context->m_Data;
	msg = (GameNet::MsgObsScoreLogTrickObject*) context->m_Msg;

	if( msg->m_GameId != gamenet_man->GetNetworkGameId())
	{
		return Net::HANDLER_MSG_DONE;
	}

	int seq;

	if( skate_mod->GetGameMode()->IsTeamGame())
	{
		GameNet::PlayerInfo* player;

		player = gamenet_man->GetPlayerByObjectID( msg->m_OwnerId );
		seq = player->m_Team;
	}
	else
	{
		seq = msg->m_OwnerId;
	}

	// modulate the color here
	for( uint32 i = 0; i < msg->m_NumPendingTricks; i++ )
	{
		skate_mod->GetTrickObjectManager()->ModulateTrickObjectColor( msg->m_PendingTrickBuffer[i], seq );
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* handle custom face data						  				  */
/* 						  										  */
/******************************************************************/

int Skate::handle_face_data( Net::MsgHandlerContext* context )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::PlayerInfo* sender;
	Obj::CSkater* skater;
	uint8* face_data;
	int face_data_length;
	uint8 skater_id;

	Dbg_Printf( "**** HANDLING FACE DATA****" );

    // Create a new temporary CFaceTexture.  
	Gfx::CFaceTexture* pFaceTexture = new Gfx::CFaceTexture;

	face_data = (uint8*) context->m_Msg;
	face_data_length = context->m_MsgLength;
	skater_id = 0;
	if( gamenet_man->OnServer())
	{
		sender = gamenet_man->GetPlayerByConnection( context->m_Conn );
		if( sender == NULL )
		{
			return Net::HANDLER_MSG_DONE;
		}
		
		skater = sender->m_Skater;
		if( skater == NULL ) 
		{
			return Net::HANDLER_MSG_DONE;
		}
	}
	else
	{
		skater_id = *face_data++;
		face_data_length -= sizeof( char );

		sender = gamenet_man->GetPlayerByObjectID( skater_id );
		if( sender == NULL )
		{
			return Net::HANDLER_MSG_DONE;
		}
		
		skater = sender->m_Skater;
		if( skater == NULL ) 
		{
			return Net::HANDLER_MSG_DONE;
		}
	}
	
	// Read in data from network message
	pFaceTexture->ReadFromBuffer((uint8*) face_data, face_data_length );
	pFaceTexture->SetValid( true );

	// Get the skater's model
	Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( skater ); 
	Dbg_Assert( pModelComponent );
	Nx::CModel* pModel = pModelComponent->GetModel();
	Dbg_Assert( pModel );
	
	// Apply face texture to new model (eventually these parameters 
	// might need to come through the same network message, 
	// depending on the sex of the skater) 
	const char* pSrcTexture = "CS_NSN_facemap.png";
	pModel->ApplyFaceTexture( pFaceTexture, pSrcTexture, Nx::CModel::vREPLACE_GLOBALLY );
	
	if( gamenet_man->OnServer())
	{
		GameNet::PlayerInfo* face_player, *player;
		Lst::Search< GameNet::PlayerInfo > sh;
		Net::Server* server;

		server = (Net::Server*) context->m_App;

		// need to copy face texture data somewhere
		// so that future joining players can get 
		// everyone else's face textures (unimplemented)
		face_player = gamenet_man->GetPlayerByConnection( context->m_Conn );
		face_player->SetFaceData((uint8*) context->m_Msg, context->m_MsgLength );

		for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; player = gamenet_man->NextPlayerInfo( sh, true ))
		{
			if( player->IsLocalPlayer() || ( player == face_player ))
			{
				continue;
			}

			server->StreamMessageToConn( player->m_Conn, GameNet::MSG_ID_FACE_DATA, Gfx::CFaceTexture::vTOTAL_CFACETEXTURE_SIZE + 1, face_player->GetFaceData(), "Face Data",
									   GameNet::vSEQ_GROUP_FACE_MSGS, false, true );
		}
	}
	
	delete pFaceTexture;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Observer-specific trick object handling						  */
/* for when the level starts up (observer needs to "catch up"     */
/* to everyone else's trick object state)   					  */
/******************************************************************/

int Skate::handle_obs_init_graffiti_state( Net::MsgHandlerContext* context )
{
	
	
	GameNet::MsgInitGraffitiState* msg;
	Skate* skate_mod;
	int i;

	skate_mod = (Skate*) context->m_Data;
	msg = (GameNet::MsgInitGraffitiState*) context->m_Msg;

	if ( !skate_mod->GetGameMode()->IsTrue( "should_modulate_color" ) )
	{
		printf( "No need to continue cause we're not in graffiti mode" );
		
		// no need to process the msg here unless it's graffiti mode
		return Net::HANDLER_MSG_DONE;
	}

	uint8* pStartOfStream = &msg->m_TrickObjectStream[0];

	Obj::CTrickObjectManager* pTrickObjectManager = skate_mod->GetTrickObjectManager();
	Dbg_Assert( pTrickObjectManager );

	// push heap
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());

	Script::CScriptStructure* pStructure = new Script::CScriptStructure;

	// modulate the color here
	for( i = 0; i < vMAX_SKATERS; i++ )
	{
		if ( msg->m_NumTrickObjects[i] )
		{
			Script::CArray* pArray = new Script::CArray;
			
			pArray->SetArrayType( msg->m_NumTrickObjects[i], ESYMBOLTYPE_NAME );
			
			for ( int j = 0; j < msg->m_NumTrickObjects[i]; j++ )
			{
				uint32 trick_object_checksum = pTrickObjectManager->GetUncompressedTrickObjectChecksum( *pStartOfStream );
				pArray->SetChecksum( j, trick_object_checksum );
//				pTrickObjectManager->ModulateTrickObjectColor( trick_object_checksum, i );
				pStartOfStream++;
			}

			char arrayName[32];
			sprintf( arrayName, "Skater%d", i );			
			pStructure->AddComponent( Script::GenerateCRC( arrayName ), pArray );
		}
	}

	pTrickObjectManager->SetObserverGraffitiState( pStructure );
	pTrickObjectManager->ApplyObserverGraffitiState();

	delete pStructure;

	// apply color modulation a few frames later...
	//
	//
	// This was never defined, so I commented it out. I just apply it immediately and this
	// seems to work for now
	//Script::SpawnScript( "deferred_observer_trick_modulation", pStructure, 0, NULL );

	// pop heap
	Mem::Manager::sHandle().PopContext();

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Set terrain type										  		  */
/*                           									  */
/******************************************************************/

int	Skate::handle_score_update( Net::MsgHandlerContext* context )
{
    GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
	Mdl::FrontEnd* front_end = Mdl::FrontEnd::Instance();
	GameNet::MsgScoreUpdate* msg;
	Obj::CMovingObject *obj;
	Skate* mod;
	int i, score;
	char* data;
	char id;
    
	msg = (GameNet::MsgScoreUpdate*) context->m_Msg;
	data = msg->m_ScoreData;
    mod = (Skate*) context->m_Data;

	if( !gamenet_man->OnServer())
	{
		mod->GetGameMode()->SetTimeLeft( msg->m_TimeLeft );
	}

	for( i = 0; i < msg->m_NumScores; i++ )
	{           
		id = *data;
		data++;
		memcpy( &score, data, sizeof( int ));
		data += sizeof( int );

		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( id );
		if( obj )
		{
			Obj::CSkater* skater_obj;
			
			skater_obj = static_cast< Obj::CSkater *> ( obj );
			Dbg_MsgAssert( skater_obj != NULL,( "Got score update for non-CSkater\n" ));

			if(	( mod->GetGameMode()->IsTrue( "should_modulate_color" )) ||
				( mod->GetGameMode()->ShouldTrackTrickScore() == false ))
			{
				Mdl::Score *pScore = skater_obj->GetScoreObject();

				// Check if a skater has just been eliminated in a firefight game
				if(	( mod->GetGameMode()->GetNameChecksum() == CRCD(0xbff33600,"netfirefight" )) &&
					( skater_obj->IsLocalClient() == false ))
				{
					// If this is the positive edge then hide the skater
					if(( pScore->GetTotalScore() > 0 ) && ( score == 0 ))
					{
						Script::CStruct* params;
						params = new Script::CStruct;
						params->AddString( CRCD(0xa1dc81f9,"name"), skater_obj->GetDisplayName());
						Script::RunScript( CRCD(0x9b043179,"announce_elimination"), params );
						delete params;
						mod->HideSkater( skater_obj, true );
					}
				}

				pScore->SetTotalScore( score );
			}
			else if( skater_obj && !skater_obj->IsLocalClient())
			{
				Mdl::Score *pScore = skater_obj->GetScoreObject();
				pScore->SetTotalScore( score );
			}
		}
	}

	if( msg->m_Final )
	{          
		gamenet_man->MarkReceivedFinalScores( true );
	}

	if( msg->m_Cheats != mod->GetCheatChecksum())
	{
		if( gamenet_man->HasCheatingOccurred() == false )
		{
			Script::RunScript( CRCD(0x1cedb62c,"notify_cheating"));
			gamenet_man->CheatingOccured();
		}
	}
    
	if( mod->GetGameMode()->IsTeamGame())
	{
		int num_teams, score, highest_score, leading_team;
		GameNet::PlayerInfo* my_player;

		num_teams = mod->GetGameMode()->NumTeams();
		leading_team = GameNet::vNO_TEAM;
		highest_score = 0;
		for( i = 0; i < num_teams; i++ )
		{
			score = gamenet_man->GetTeamScore( i );
			if( score > highest_score )
			{
				highest_score = score;
				leading_team = i;
			}
			else if( score == highest_score )
			{
				// A tie doesn't result in the panel message
				leading_team = GameNet::vNO_TEAM;
			}
		}

		if( leading_team != GameNet::vNO_TEAM )
		{
			my_player = gamenet_man->GetLocalPlayer();
			if( msg->m_Final )
			{
				if(( my_player->m_Skater ) && ( my_player->m_Team == leading_team ))
				{
					my_player->m_Skater->SelfEvent(CRCD(0x96935417,"WonGame"));
				}
				else
				{
					if( my_player->m_Skater )
					{
						my_player->m_Skater->SelfEvent(CRCD(0xe3ec7e9e,"LostGame"));
					}
				}
			}
			else if( ( gamenet_man->GetCurrentLeadingTeam() != leading_team ) &&
					 ( mod->GetGameMode()->IsTrue( "show_leader_messages" ) ) &&
                     ( !gamenet_man->GameIsOver()))
	
			{
				// only print the message if you're competing against someone
				if(( my_player->m_Skater ) && ( my_player->m_Team == leading_team ))
				{   
					Script::RunScript( "NewScoreLeaderYourTeam", NULL, my_player->m_Skater );
				}
				else
				{   
					char team_name[128];

					sprintf( team_name, "team_%d_name", leading_team + 1 );
					Script::CScriptStructure* pTempStructure = new Script::CScriptStructure;
					pTempStructure->Clear();
					pTempStructure->AddComponent( Script::GenerateCRC("String0"), ESYMBOLTYPE_STRING, Script::GetString( team_name ));
					Script::RunScript( "NewScoreLeaderOtherTeam", pTempStructure, my_player->m_Skater );
					delete pTempStructure;
				}
				gamenet_man->SetCurrentLeadingTeam( leading_team );
			}
		}
	}
	else
	{
		GameNet::PlayerInfo* player, *leader, *my_player;
		Lst::Search< GameNet::PlayerInfo > sh;
		int score, highest_score, num_players;

		// Do the logic that figures out if there's a new winner and provides feedback
		leader = NULL;
		highest_score = 0;
		num_players = 0;
		for( player = gamenet_man->FirstPlayerInfo( sh ); player;
				player = gamenet_man->NextPlayerInfo( sh ))
		{
			//if( player->IsLocalPlayer() && mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netgoalattack" ))
			{
				score = gamenet_man->GetPlayerScore( player->m_Skater->GetID());
			}
			//else
			//{
				//Mdl::Score *pScore = player->m_Skater->GetScoreObject();
				
				//score = pScore->GetTotalScore();
			//}
			if( score > highest_score )
			{
				highest_score = score;
				leader = player;
			}
			else if( score == highest_score )
			{
				// A tie doesn't result in the panel message
				leader = NULL;
			}
			num_players++;
		}
        
		if( leader )
		{   
			my_player = gamenet_man->GetLocalPlayer();
			if( msg->m_Final )
			{
				if( leader->IsLocalPlayer())
				{
					leader->m_Skater->SelfEvent(CRCD(0x96935417,"WonGame"));
				}
				else
				{
					if( my_player->m_Skater )
					{
						leader->m_Skater->SelfEvent( CRCD(0xe3ec7e9e,"LostGame") );
					}
				}
			}
			else if( ( gamenet_man->GetCurrentLeader() != leader ) &&
					 ( num_players > 1 ) &&
					 ( mod->GetGameMode()->IsTrue( CRCD(0xd63dfd0f,"show_leader_messages") ) ) &&
                     ( !front_end->GamePaused()))
	
			{
				// only print the message if you're competing against someone
				if( leader->IsLocalPlayer())
				{   
					Script::RunScript( CRCD(0xa1d334d0,"NewScoreLeaderYou"), NULL, leader->m_Skater );
				}
				else
				{   
					Script::CScriptStructure* pTempStructure = new Script::CScriptStructure;
					pTempStructure->Clear();
					pTempStructure->AddComponent( CRCD(0xa4b08520,"String0"), ESYMBOLTYPE_STRING, leader->m_Name );
					Script::RunScript( CRCD(0x7a4b9d14,"NewScoreLeaderOther"), pTempStructure, my_player->m_Skater );
					delete pTempStructure;
				}
				gamenet_man->SetCurrentLeader( leader );
			}
		}
	}
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Start/Stop spark effect										  */
/*                           									  */
/******************************************************************/

int	Skate::handle_sparks( Net::MsgHandlerContext* context )
{
	Skate* mod;
	GameNet::MsgObjMessage *msg;
	Obj::CMovingObject *obj;

	

	msg = (GameNet::MsgObjMessage *) context->m_Msg;
	
	mod = (Skate *) context->m_Data;
	obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
	if( obj )
	{
		Obj::CSkater* skater_obj;
		
		skater_obj = static_cast< Obj::CSkater *> ( obj );
		Dbg_MsgAssert( skater_obj != NULL,( "Got sparks message for non-CSkater\n" ));
		
		if( skater_obj )
		{   
			switch( context->m_MsgId )
			{
				case GameNet::MSG_ID_SPARKS_ON:
					skater_obj->SparksOn();
					break;
				case GameNet::MSG_ID_SPARKS_OFF:
					skater_obj->SparksOff();
					break;
				default:
					break;
			}
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Start/Stop blood effect										  */
/*                           									  */
/******************************************************************/

int	Skate::handle_blood( Net::MsgHandlerContext* context )
{
	Skate* mod;
	Obj::CMovingObject *obj;
	GameNet::MsgObjMessage* msg;

	

	mod = (Skate *) context->m_Data;
	msg = (GameNet::MsgObjMessage* ) context->m_Msg;
	obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( msg->m_ObjId );
	if( obj )
	{
		Obj::CSkater* skater_obj;

		skater_obj = static_cast< Obj::CSkater *> ( obj );
		Dbg_MsgAssert( skater_obj != NULL,( "Got blood message for non-CSkater\n" ));
		if( skater_obj )
		{   
			switch( context->m_MsgId )
			{
				case GameNet::MSG_ID_BLOOD_ON:
				{
					GameNet::MsgBlood *msg;

					msg = (GameNet::MsgBlood *) context->m_Msg;
//					skater_obj->BloodOn( msg->m_BodyPart, msg->m_Size, msg->m_Frequency, msg->m_RandomRadius );
					break;
				}
				case GameNet::MSG_ID_BLOOD_OFF:
				{
					GameNet::MsgBloodOff *msg;

					msg = (GameNet::MsgBloodOff *) context->m_Msg;
//					skater_obj->BloodOff( msg->m_BodyPart );
					break;
				}
				default:
					break;
			}
		}
	}

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Play a one-off sound given a position  						  */
/*                           									  */
/******************************************************************/

int	Skate::handle_play_sound( Net::MsgHandlerContext* context )
{
	GameNet::MsgPlaySound *msg;
	Mth::Vector pos;
	float vol, pitch, choice;

	msg = (GameNet::MsgPlaySound *) context->m_Msg;
	
	pos[X] = msg->m_Pos[0];
	pos[Y] = msg->m_Pos[1];
	pos[Z] = msg->m_Pos[2];

	vol = (float) msg->m_VolPercent;
	pitch = (float) msg->m_PitchPercent;
	choice = (float) msg->m_SoundChoice;

	Env::CTerrainManager::sPlaySound( (Env::ETerrainActionType) msg->m_WhichArray, (ETerrainType) msg->m_SurfaceFlag, pos, vol, pitch, choice, false );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Get all the game options  						  			  */
/*                           									  */
/******************************************************************/

int	Skate::handle_start_info( Net::MsgHandlerContext* context )
{   
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Skate* skate_mod;
	GameNet::MsgStartInfo* msg;
	Script::CArray* mode_array;
	int i;

	Dbg_Printf( "********************************* Got lobby info\n" );

	gamenet_man->SetJoinState( GameNet::vJOIN_STATE_CONNECTED );
	gamenet_man->FreeServerList();

	skate_mod = (Skate *) context->m_Data;
	msg = (GameNet::MsgStartInfo*) context->m_Msg;

	// Note whether or not the server is narrowband/broadband
	if( msg->m_Broadband )
	{
		context->m_Conn->SetBandwidthType( Net::Conn::vBROADBAND );
		context->m_Conn->SetSendInterval( Tmr::VBlanks( 2 ));
	}
	else
	{
		context->m_Conn->SetBandwidthType( Net::Conn::vNARROWBAND );
		context->m_Conn->SetSendInterval( Tmr::VBlanks( 2 ));
	}

	gamenet_man->SetNetworkGameId( msg->m_GameId );
	gamenet_man->SetLevel( msg->m_LevelId );
	gamenet_man->SetCrownSpawnPoint( msg->m_CrownSpawnPoint );
	gamenet_man->SetSkaterStartingPoints( msg->m_StartPoints );
	
	// set up the game mode for the client cause we're now in the lobby
	// anything that's lobby-dependent should go after this line
	Script::CScriptStructure* pTempStructure = new Script::CScriptStructure;
	pTempStructure->Clear();
	pTempStructure->AddComponent( Script::GenerateCRC("level"), ESYMBOLTYPE_NAME, (int)msg->m_LevelId );					
	Script::RunScript( "request_level", pTempStructure, NULL );
	delete pTempStructure;

	Script::RunScript( "leave_front_end" );
	Script::RunScript( "cleanup_before_loading_level" );
	
	context->m_Conn->UpdateCommTime();	// update the current comm time so it doesn't time out after
										// laoding the level

	skate_mod->GetGameMode()->SetMaximumNumberOfPlayers( msg->m_MaxPlayers );
	skate_mod->GetGameMode()->LoadGameType( msg->m_GameMode );
	//skate_mod->GetGameMode()->SetMaximumNumberOfPlayers( msg->m_MaxPlayers );
	skate_mod->LaunchGame();
	skate_mod->GetGameMode()->LoadGameType( msg->m_GameMode );
	skate_mod->GetGameMode()->SetNumTeams( msg->m_TeamMode );
	
	gamenet_man->SetProSetFlags( msg->m_ProSetFlags );
	for( i = 0; i < GameNet::vMAX_NUM_PROSETS; i++ )
	{
		if(( 1 << i ) & msg->m_ProSetFlags )
		{
			Script::CStruct* pSetParams;
			pSetParams = new Script::CStruct;
			pSetParams->AddInteger( "flag", Script::GetInt( "FLAG_PROSET1_GEO_ON" ) + i );
			Script::RunScript( "SetFlag", pSetParams );
			delete pSetParams;
		}
	}
	
    {
		Script::CScriptStructure* pTempStructure = new Script::CScriptStructure;
		
		if( msg->m_GameMode != Script::GenerateCRC("netlobby" ))
		{
			// In king of the hill, interpret time limit as a target time
			if(	( msg->m_TimeLimit == 0 ) &&
				( msg->m_GameMode != Script::GenerateCRC( "netctf" )) &&
				( msg->m_GameMode != Script::GenerateCRC( "netgoalattack" )))
			{
				if( ( msg->m_GameMode != CRCD(0x3d6d444f,"firefight")) &&
					( msg->m_GameMode != CRCD(0xbff33600,"netfirefight")))
				{
					Script::CArray* pArray = new Script::CArray;
					Script::CopyArray(pArray,Script::GetArray("targetScoreArray"));
					
					Script::CScriptStructure* pSubStruct = pArray->GetStructure(0);
					Dbg_Assert(pSubStruct);
					pSubStruct->AddComponent(Script::GenerateCRC("score"),ESYMBOLTYPE_INTEGER, msg->m_TargetScore );
					pTempStructure->AddComponent( Script::GenerateCRC("victory_conditions"), pArray );
				}
				pTempStructure->AddComponent( Script::GenerateCRC("default_time_limit"), ESYMBOLTYPE_INTEGER, (int) 0 );
				skate_mod->SetTimeLimit( 0 );
				skate_mod->GetGameMode()->OverrideOptions( pTempStructure );
			}
			else
			{
				pTempStructure->AddComponent( Script::GenerateCRC("default_time_limit"), ESYMBOLTYPE_INTEGER, (int) msg->m_TimeLimit );
				if( msg->m_GameMode == Script::GenerateCRC( "netctf" ))
				{
					Script::CArray* pArray = new Script::CArray;
					Script::CopyArray(pArray,Script::GetArray("highestScoreArray") );
					pTempStructure->AddComponent( Script::GenerateCRC("victory_conditions"), pArray );
				}
				skate_mod->SetTimeLimit( msg->m_TimeLimit );
				skate_mod->GetGameMode()->SetTimeLeft( msg->m_TimeLeft );
				skate_mod->GetGameMode()->OverrideOptions( pTempStructure );
			}
		}

		delete pTempStructure;
	}

	if( gamenet_man->InNetGame())
	{
		mode_array = Script::GetArray( "net_game_type_info" );
	}
	else
	{
		mode_array = Script::GetArray( "mp_game_type_info" );
	}
	Dbg_Assert( mode_array );

	for( i = 0; i < (int)mode_array->GetSize(); i++ )
	{   
		uint32 value, script;
		Script::CStruct* mode_struct;
		 
		mode_struct = mode_array->GetStructure( i );
		Dbg_Assert( mode_struct );
		
		mode_struct->GetChecksum( "checksum", &value, true );
		if( value == msg->m_GameMode )
		{
			Script::CStruct* params;
						
			params = new Script::CStruct;
			params->AddInteger( "time", msg->m_TimeLimit );
			params->AddInteger( "score", msg->m_TargetScore );
			if( msg->m_TimeLimit == 0 )
			{
				params->AddInteger( CRCD(0xf0e712d2,"unlimited_time"), 1 );
			}
			else
			{
				params->AddInteger( CRCD(0xf0e712d2,"unlimited_time"), 0 );
			}
			//Dbg_Printf( "Target Score : %d\n", msg->m_TargetScore );
			mode_struct->GetChecksum( "goal_script", &script, true );
			Script::RunScript( script, params );
			delete params;
			break;
		}
	}

	if( ( gamenet_man->InNetGame()) &&
		( gamenet_man->GetJoinMode() == GameNet::vJOIN_MODE_PLAY ))
	{
		if( !CFuncs::ScriptIsObserving( NULL, NULL ))
		{
			gamenet_man->SendFaceDataToServer();
		}
	}

    return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/* Parse the object update stream from the server and update the  */
/* appropriate objects                          				  */
/******************************************************************/

int Skate::handle_object_update_relay( Net::MsgHandlerContext* context )
{
	Skate* mod;
	int update_flags;
	unsigned char obj_id, obj_id_mask;
	Net::BitStream stream;
    
    Dbg_Assert( context );
     
	mod = (Skate *) context->m_Data;
	stream.SetInputData( context->m_Msg, 1024 );
	obj_id_mask = stream.ReadUnsignedValue( sizeof( char ) * 8 );
	
	for( obj_id = 0; obj_id < vMAX_SKATERS; obj_id++ )
	{
		GameNet::ObjectUpdate obj_update;
		Obj::CMovingObject* obj;
		Mth::Vector eulers;
		short pos[3];
		short fixed;
		char doing_trick, state, terrain, walking, driving;
		Flags< int > skater_flags;
		Flags< int > end_run_flags;
		sint16 rail_node;
		int i;
		unsigned short timestamp;
		 
		// If the bit for this player number is not set, that means there is no 
		// object update for them. Continue
		if(( obj_id_mask & ( 1 << obj_id )) == 0 )
		{
			continue;
		}

		//Dbg_Printf( "================ Got update for player %d\n", obj_id );

    	timestamp = stream.ReadUnsignedValue( sizeof( uint16 ) * 8 );
		update_flags = stream.ReadUnsignedValue( 9 );
		
		//Dbg_Printf( "Got Object Update For %d, Time: %d Mask %d\n", obj_id, timestamp, obj_id_mask );

		doing_trick = 0;
		state = 0;
		skater_flags = 0;
		end_run_flags = 0;
		terrain = 0;
		walking = 0;
		driving = 0;
		rail_node = -1;
        
		if( update_flags & GameNet::mUPDATE_FIELD_POS_X )
		{   
			pos[X] = stream.ReadSignedValue( sizeof( short ) * 8 );
			//pos[X] = stream.ReadFloatValue();
		}
		if( update_flags & GameNet::mUPDATE_FIELD_POS_Y )
		{
			pos[Y] = stream.ReadSignedValue( sizeof( short ) * 8 );
			//pos[Y] = stream.ReadFloatValue();
		}
		if( update_flags & GameNet::mUPDATE_FIELD_POS_Z )
		{
			pos[Z] = stream.ReadSignedValue( sizeof( short ) * 8 );
			//pos[Z] = stream.ReadFloatValue();
		}   
				
		if( update_flags & GameNet::mUPDATE_FIELD_ROT_X )
		{
			fixed = stream.ReadSignedValue( sizeof( short ) * 8 );
			eulers[X] = ((float) fixed ) / 4096.0f;
		}
		if( update_flags & GameNet::mUPDATE_FIELD_ROT_Y )
		{
			fixed = stream.ReadSignedValue( sizeof( short ) * 8 );
			eulers[Y] = ((float) fixed ) / 4096.0f;
		}
		if( update_flags & GameNet::mUPDATE_FIELD_ROT_Z )
		{
			fixed = stream.ReadSignedValue( sizeof( short ) * 8 );
			eulers[Z] = ((float) fixed ) / 4096.0f;
		}
        
		if( update_flags & GameNet::mUPDATE_FIELD_STATE )
		{   
			char mask;

			mask = stream.ReadUnsignedValue( 4 );
		
			state = mask & GameNet::mSTATE_MASK;
			doing_trick = (( mask & GameNet::mDOING_TRICK_MASK ) != 0 );
			terrain = stream.ReadUnsignedValue( 6 );
			walking = stream.ReadUnsignedValue( 1 );
			driving = stream.ReadUnsignedValue( 1 );
		}

		if( update_flags & GameNet::mUPDATE_FIELD_FLAGS )
		{
			skater_flags = stream.ReadUnsignedValue( 5 );
			end_run_flags = stream.ReadUnsignedValue( 3 );
		}

		if( update_flags & GameNet::mUPDATE_FIELD_RAIL_NODE )
		{
			rail_node = stream.ReadSignedValue( sizeof( sint16 ) * 8 );
			//Dbg_Printf( "Got rail node %d\n", rail_node );
		}

		obj = (Obj::CMovingObject *) mod->GetObjectManager()->GetObjectByID( obj_id );
		
		// Make sure that we actually have this object in our list of objects
		// Also, reject late/foreign object update messages. We still needed to do all the stream
		// parsing though because we need to return the length of the message to the dispatcher
		if( ( obj != NULL ) &&
			(( context->m_PacketFlags & ( Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN )) == 0 ))
		{
			Obj::CSkater* skater_obj;
			 
			skater_obj = static_cast< Obj::CSkater *> ( obj );
			Dbg_MsgAssert( skater_obj != NULL,( "Got object update for non-CSkater\n" ));
												   
			if( skater_obj )
			{
				Obj::SPosEvent* p_pos_history = skater_obj->GetStateHistory()->GetPosHistory();
				
				int index, last_index;
	
				if( skater_obj->GetStateHistory()->GetNumPosUpdates() > 0 )
				{
					last_index = ( skater_obj->GetStateHistory()->GetNumPosUpdates() - 1 ) % Obj::CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
				}
				else
				{
					Mth::Matrix obj_matrix;
					Mth::Vector eulers;
					last_index = 0;

					obj_matrix = skater_obj->GetMatrix();
					obj_matrix.GetEulers( eulers );
					for( i = 0; i < 3; i++ )
					{
						if( i == Y )
						{
							p_pos_history[0].ShortPos[i] = (short) ( skater_obj->m_pos[i] * 4.0f );
							//p_pos_history[0].ShortPos[i] = skater_obj->m_pos[i];
						}
						else
						{
							p_pos_history[0].ShortPos[i] = (short) ( skater_obj->m_pos[i] * 2.0f );
							//p_pos_history[0].ShortPos[i] = skater_obj->m_pos[i];
						}
						p_pos_history[0].Eulers[i] = eulers[i];
					}
				}
				
				index = skater_obj->GetStateHistory()->GetNumPosUpdates() % Obj::CSkaterStateHistoryComponent::vNUM_POS_HISTORY_ELEMENTS;
				
                if( !(  update_flags & GameNet::mUPDATE_FIELD_POS_X ))
				{
					pos[X] = p_pos_history[last_index].ShortPos[X];
				}
				if( !(  update_flags & GameNet::mUPDATE_FIELD_POS_Y ))
				{
					pos[Y] = p_pos_history[last_index].ShortPos[Y];
				}
				if( !(  update_flags & GameNet::mUPDATE_FIELD_POS_Z ))
				{
					pos[Z] = p_pos_history[last_index].ShortPos[Z];
				}
				
				for( i = 0; i < 3; i++ )
				{
					p_pos_history[index].ShortPos[i] = pos[i];
				}

				obj_update.m_Pos.Set(	((float) pos[X] ) / 2.0f,//pos[X],
										((float) pos[Y] ) / 4.0f,//pos[Y],
										((float) pos[Z] ) / 2.0f,//pos[Z],
										0.0f );
				//Dbg_Printf( "*** Got Pos[%.2f %.2f %.2f]\n", obj_update.m_Pos[X], obj_update.m_Pos[Y], obj_update.m_Pos[Z] );

				if( !(  update_flags & GameNet::mUPDATE_FIELD_ROT_X ))
				{
					eulers[X] = p_pos_history[last_index].Eulers[X];
				}
				if( !(  update_flags & GameNet::mUPDATE_FIELD_ROT_Y ))
				{
					eulers[Y] = p_pos_history[last_index].Eulers[Y];
				}
				if( !(  update_flags & GameNet::mUPDATE_FIELD_ROT_Z ))
				{
					eulers[Z] = p_pos_history[last_index].Eulers[Z];
				}

				// Update history
				for( i = 0; i < 3; i++ )
				{
					p_pos_history[index].Eulers[i] = eulers[i];
				}

				// Use those euler angles to compose a matrix
				Mth::Matrix obj_matrix( eulers[X], eulers[Y], eulers[Z] );
				obj_update.m_Matrix = obj_matrix;

                p_pos_history[index].Matrix = obj_update.m_Matrix;
				p_pos_history[index].Position = obj_update.m_Pos;
                
				Mth::Vector vel;
				vel = p_pos_history[index].Position - p_pos_history[last_index].Position;
				//Dbg_Printf( "(%d) Vel: %f %f %f\n", timestamp, vel[X], vel[Y], vel[Z] );

				if( !( update_flags & GameNet::mUPDATE_FIELD_STATE ))
				{
					state = p_pos_history[last_index].State;
					doing_trick = p_pos_history[last_index].DoingTrick;
					terrain = p_pos_history[last_index].Terrain;
					walking = p_pos_history[last_index].Walking;
					driving = p_pos_history[last_index].Driving;
				}
				if( !( update_flags & GameNet::mUPDATE_FIELD_FLAGS ))
				{
					skater_flags = p_pos_history[last_index].SkaterFlags;
					end_run_flags = p_pos_history[last_index].EndRunFlags;
				}
				if( !( update_flags & GameNet::mUPDATE_FIELD_RAIL_NODE ))
				{
					rail_node = p_pos_history[last_index].RailNode;
				}
                
				p_pos_history[index].SkaterFlags = skater_flags;
				p_pos_history[index].EndRunFlags = end_run_flags;
				p_pos_history[index].RailNode = rail_node;
				p_pos_history[index].State = state;
				p_pos_history[index].DoingTrick = doing_trick;
				p_pos_history[index].Terrain = (ETerrainType) terrain;
				p_pos_history[index].Walking = walking;
				p_pos_history[index].Driving = driving;

				// Update the lo and hi words of the timestamp
				p_pos_history[index].HiTime = p_pos_history[last_index].HiTime;

				// If the lo-word has flipped, increment the hi-word
				if( p_pos_history[last_index].LoTime > timestamp )
				{
					p_pos_history[index].HiTime++;
				}
				p_pos_history[index].LoTime = timestamp;
				//Dbg_Printf( "(%d) Got time: %d\n", context->m_App->m_FrameCounter, p_pos_history[index].GetTime());
				
				
				skater_obj->GetStateHistory()->IncrementNumPosUpdates();				
			}
		}
	}
			
	return Net::HANDLER_CONTINUE;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::StartServer( void )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance(); 
	Net::Server* server;
	bool net_game;

	net_game = false;
	// prevents fragmentation
	//CFuncs::ScriptCleanup( NULL, NULL );
	if( gamenet_man->InNetMode())
	{	
		net_game = true;
		gamenet_man->ClientShutdown();
	}
	
	server = gamenet_man->SpawnServer( !net_game, true );
	if( server == NULL )
	{
		return;
	}

	mlp_manager->AddLogicTask ( *m_object_update_task );
	mlp_manager->AddLogicTask ( *m_score_update_task );
	
	// These handlers remaio active throughout the server's life
	server->m_Dispatcher.AddHandler( GameNet::MSG_ID_SCORE, handle_score_request );
	server->m_Dispatcher.AddHandler( GameNet::MSG_ID_LANDED_TRICK, handle_landed_trick, 0, this );
	server->m_Dispatcher.AddHandler( GameNet::MSG_ID_COMBO_REPORT, handle_combo_report, 0, this );
    server->m_Dispatcher.AddHandler( GameNet::MSG_ID_CHAT, handle_msg_relay, Net::mHANDLE_LATE );
	server->m_Dispatcher.AddHandler( GameNet::MSG_ID_BAIL_DONE, handle_bail_done, Net::mHANDLE_LATE );
	server->m_Dispatcher.AddHandler( GameNet::MSG_ID_OBJ_UPDATE_STREAM, handle_object_update, 
									 Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN );

	// In Split Screen Games, Don't relay any messages
	if( !server->IsLocal())
	{
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_FACE_DATA, handle_face_data );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_SPAWN_PROJECTILE, handle_msg_relay, 0, this );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_ENTER_VEHICLE, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_ENTER_VEHICLE, handle_enter_vehicle_server );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_PRIM_ANIM_START, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_SET_WOBBLE_TARGET, handle_selective_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_ROTATE_SKATEBOARD, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_ROTATE_DISPLAY, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_CLEAR_ROTATE_DISPLAY, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_CREATE_SPECIAL_ITEM, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_DESTROY_SPECIAL_ITEM, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_FLIP_ANIM, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_SET_WOBBLE_DETAILS, handle_selective_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_SET_LOOPING_TYPE, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_SET_HIDE_ATOMIC, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_SET_ANIM_SPEED, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_PLAY_SOUND, handle_selective_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_PLAY_LOOPING_SOUND, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_BLOOD_ON, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_BLOOD_OFF, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_SPARKS_ON, handle_msg_relay );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_SPARKS_OFF, handle_msg_relay, Net::mHANDLE_LATE );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_RUN_SCRIPT, handle_script_relay, Net::mHANDLE_LATE );
		server->m_Dispatcher.AddHandler( GameNet::MSG_ID_SPAWN_AND_RUN_SCRIPT, handle_script_relay, Net::mHANDLE_LATE );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Skate::LaunchNetworkGame( void )
{
	RestartLevel();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Skate::AddNetworkMsgHandlers( Net::Client* client, int index )
{
	Obj::CSkater* skater;
	
	Dbg_Assert( client );
    
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_START_INFO, handle_start_info, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_OBJ_UPDATE_STREAM, handle_object_update_relay, 
									 Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_PRIM_ANIM_START, handle_anims, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_TOGGLE_CHEAT, handle_cheats, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SET_WOBBLE_TARGET, handle_anims, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_ROTATE_DISPLAY, handle_anims, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_CLEAR_ROTATE_DISPLAY, handle_anims, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_CREATE_SPECIAL_ITEM, handle_anims, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_DESTROY_SPECIAL_ITEM, handle_anims, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_ROTATE_SKATEBOARD, handle_anims, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_FLIP_ANIM, handle_anims, 0, this, Net::HIGHEST_PRIORITY );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_FLIP_ANIM, handle_flip_anim, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SET_WOBBLE_DETAILS, handle_anims, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SET_LOOPING_TYPE, handle_anims, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SET_HIDE_ATOMIC, handle_anims, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SET_ANIM_SPEED, handle_anims, 0, this );     
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_PLAYER_QUIT, handle_player_quit, 
									 Net::mHANDLE_LATE, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_HOST_QUIT, handle_disconn_accepted, 
									 Net::mHANDLE_LATE, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_PLAY_SOUND, handle_play_sound, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_PLAY_LOOPING_SOUND, handle_play_sound, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SPARKS_ON, handle_sparks, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SPARKS_OFF, handle_sparks, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_BLOOD_ON, handle_blood, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_BLOOD_OFF, handle_blood, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SCORE_UPDATE, handle_score_update, 
									 Net::mHANDLE_LATE, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_CHANGE_LEVEL, handle_change_level, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_RUN_SCRIPT, handle_run_script, Net::mHANDLE_LATE, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SPAWN_AND_RUN_SCRIPT, handle_spawn_run_script, Net::mHANDLE_LATE, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_KICKED, handle_kicked, Net::mHANDLE_LATE, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_LEFT_OUT, handle_kicked, Net::mHANDLE_LATE, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_OBSERVER_LOG_TRICK_OBJ, handle_obs_log_trick_objects, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_OBSERVER_INIT_GRAFFITI_STATE, handle_obs_init_graffiti_state, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_FACE_DATA, handle_face_data, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_CHEAT_LIST, handle_cheat_list, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SPAWN_PROJECTILE, handle_projectile, 0, this );
	client->m_Dispatcher.AddHandler( GameNet::MSG_ID_ENTER_VEHICLE, handle_enter_vehicle_client, 0, this );

	for( uint i = 0; i < vMAX_SKATERS; i++ )
	{
		skater = GetSkater( i );
		if( skater && skater->IsLocalClient() && ( skater->GetHeapIndex() == index ))
		{
			client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SCORE, Mdl::Score::s_handle_score_message, 0, skater );
			client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SKATER_COLLIDE_WON, Obj::CSkaterStateHistoryComponent::sHandleCollision, 0, skater );
			client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SKATER_COLLIDE_LOST, Obj::CSkaterStateHistoryComponent::sHandleCollision, 0, skater );
			client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SKATER_PROJECTILE_HIT_TARGET, Obj::CSkaterStateHistoryComponent::sHandleProjectileHit, 0, skater );
			client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SKATER_HIT_BY_PROJECTILE, Obj::CSkaterStateHistoryComponent::sHandleProjectileHit, 0, skater );
			client->m_Dispatcher.AddHandler( GameNet::MSG_ID_STEAL_MESSAGE, Obj::CSkaterLocalNetLogicComponent::sHandleStealMessage, 0, skater );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Skate::LeaveServer( void )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Net::Client* client;
    
	
    
	if( gamenet_man->OnServer())
	{   
		Net::App* server;
		Net::MsgDesc msg_desc;

		server = gamenet_man->GetServer();
		Dbg_Assert( server );

		// If we were auto serving, shut that service down
		if( gamenet_man->GetHostMode() == GameNet::vHOST_MODE_AUTO_SERVE )
		{
			gamenet_man->AutoServerShutdown();
		}

		// First, wait for any pending network tasks to finish before sending out final disconn
		// message
#ifdef __PLAT_NGPS__
		//server->WaitForAsyncCallsToFinish();
#endif  		

		// Send off last message, notifying clients that I'm about to quit
		Dbg_Printf( "Starting server shutdown\n" );
		msg_desc.m_Id = GameNet::MSG_ID_HOST_QUIT;
		msg_desc.m_Priority = Net::HIGHEST_PRIORITY;
		server->EnqueueMessage( Net::HANDLE_ID_BROADCAST, &msg_desc );
		// Wake the network thread up to send the data
		server->SendData();
		
		// Wait for that final send to complete
#ifdef __PLAT_NGPS__
		//server->WaitForAsyncCallsToFinish();
#endif  
		gamenet_man->ServerShutdown();

		m_object_update_task->Remove();
		m_score_update_task->Remove();
	}
	else
	{
		client = gamenet_man->GetClient( 0 );
		if( client )
		{
			Net::MsgDesc msg_desc;
			// First, wait for any pending network tasks to finish before sending out final disconn
			// message
#ifdef __PLAT_NGPS__
			//client->WaitForAsyncCallsToFinish();
#endif  		
			Dbg_Printf( "Leaving server\n" );
			
			msg_desc.m_Id = Net::MSG_ID_DISCONN_REQ;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
			// Send off last message, notifying server that I'm about to quit
			client->EnqueueMessageToServer( &msg_desc );
			// Wake the network thread up to send the data
			client->SendData();
		
			// Wait for that final send to complete
#ifdef __PLAT_NGPS__
			//client->WaitForAsyncCallsToFinish();
#endif  

		}
	}
    
	gamenet_man->DisconnectFromServer();
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Skate::SetLaunchingQueuedScripts( void )
{
	m_launching_queued_scripts = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Skate::ClearLaunchingQueuedScripts( void )
{
	m_launching_queued_scripts = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Skate::LaunchingQueuedScripts( void )
{
	return m_launching_queued_scripts;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Skate::SendCheatList( GameNet::PlayerInfo* player )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::MsgEnabledCheats cheat_msg;
	Net::MsgDesc msg_desc;

	cheat_msg.m_NumCheats = 0;

	if( mp_career->GetCheat( CRCD(0xeefecf1b, "CHEAT_ALWAYS_SPECIAL")))
	{
		cheat_msg.m_Cheats[cheat_msg.m_NumCheats++] = CRCD(0xeefecf1b, "CHEAT_ALWAYS_SPECIAL");
	}
	if( mp_career->GetCheat( CRCD(0xb38341c9, "CHEAT_PERFECT_MANUAL")))
	{
		cheat_msg.m_Cheats[cheat_msg.m_NumCheats++] = CRCD(0xb38341c9, "CHEAT_PERFECT_MANUAL");
	}
	if( mp_career->GetCheat( CRCD(0xcd09e062, "CHEAT_PERFECT_RAIL")))
	{
		cheat_msg.m_Cheats[cheat_msg.m_NumCheats++] = CRCD(0xcd09e062, "CHEAT_PERFECT_RAIL");
	}
	if( mp_career->GetCheat( CRCD(0x69a1ce96, "CHEAT_PERFECT_SKITCH")))
	{
		cheat_msg.m_Cheats[cheat_msg.m_NumCheats++] = CRCD(0x69a1ce96, "CHEAT_PERFECT_SKITCH");
	}
	if( mp_career->GetCheat( CRCD(0xeefecf1b, "CHEAT_ALWAYS_SPECIAL")))
	{
		cheat_msg.m_Cheats[cheat_msg.m_NumCheats++] = CRCD(0xeefecf1b, "CHEAT_ALWAYS_SPECIAL");
	}
	if( mp_career->GetCheat( CRCD(0x6b0f24bc, "CHEAT_STATS_13")))
	{
		cheat_msg.m_Cheats[cheat_msg.m_NumCheats++] = CRCD(0x6b0f24bc, "CHEAT_STATS_13");
	}
	if( mp_career->GetCheat( CRCD(0x520a66ef,"FLAG_G_EXPERT_MODE_NO_REVERTS")))
	{
		cheat_msg.m_Cheats[cheat_msg.m_NumCheats++] = CRCD(0x520a66ef,"FLAG_G_EXPERT_MODE_NO_REVERTS");
	}
	if( mp_career->GetCheat( CRCD(0xeba89179,"FLAG_G_EXPERT_MODE_NO_WALKING")))
	{
		cheat_msg.m_Cheats[cheat_msg.m_NumCheats++] = CRCD(0xeba89179,"FLAG_G_EXPERT_MODE_NO_WALKING");
	}
	if( mp_career->GetCheat( CRCD(0xc4ffc672,"FLAG_G_EXPERT_MODE_NO_MANUALS")))
	{
		cheat_msg.m_Cheats[cheat_msg.m_NumCheats++] = CRCD(0xc4ffc672,"FLAG_G_EXPERT_MODE_NO_MANUALS");
	}
	if( mp_career->GetCheat( CRCD(0xd5982173,"NO_G_DISPLAY_BALANCE")))
	{
		cheat_msg.m_Cheats[cheat_msg.m_NumCheats++] = CRCD(0xd5982173,"NO_G_DISPLAY_BALANCE");
	}

	// Send it, even if there are zero cheats. This triggers the clearing of all cheats on
	// the client side
	msg_desc.m_Id = GameNet::MSG_ID_CHEAT_LIST;
	msg_desc.m_Data = &cheat_msg;
	msg_desc.m_Length = sizeof( int ) +( sizeof( uint32 ) * cheat_msg.m_NumCheats );

	msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
	msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
	
	gamenet_man->GetServer()->EnqueueMessage( player->GetConnHandle(), &msg_desc );

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	Skate::GetCheatChecksum( void )
{
	uint32 checksum;

	checksum = 0;
	
	if( mp_career->GetCheat( CRCD(0xeefecf1b, "CHEAT_ALWAYS_SPECIAL")))
	{
		checksum |= CRCD(0xeefecf1b, "CHEAT_ALWAYS_SPECIAL");
	}
	if( mp_career->GetCheat( CRCD(0xb38341c9, "CHEAT_PERFECT_MANUAL")))
	{
		checksum |= CRCD(0xb38341c9, "CHEAT_PERFECT_MANUAL");
	}
	if( mp_career->GetCheat( CRCD(0xcd09e062, "CHEAT_PERFECT_RAIL")))
	{
		checksum |= CRCD(0xcd09e062, "CHEAT_PERFECT_RAIL");
	}
	if( mp_career->GetCheat( CRCD(0x69a1ce96, "CHEAT_PERFECT_SKITCH")))
	{
		checksum |= CRCD(0x69a1ce96, "CHEAT_PERFECT_SKITCH");
	}
	if( mp_career->GetCheat( CRCD(0xeefecf1b, "CHEAT_ALWAYS_SPECIAL")))
	{
		checksum |= CRCD(0xeefecf1b, "CHEAT_ALWAYS_SPECIAL");
	}
	if( mp_career->GetCheat( CRCD(0x6b0f24bc, "CHEAT_STATS_13")))
	{
		checksum |= CRCD(0x6b0f24bc, "CHEAT_STATS_13");
	}
	if( mp_career->GetCheat( CRCD(0x520a66ef,"FLAG_G_EXPERT_MODE_NO_REVERTS")))
	{
		checksum |= CRCD(0x520a66ef,"FLAG_G_EXPERT_MODE_NO_REVERTS");
	}
	if( mp_career->GetCheat( CRCD(0xeba89179,"FLAG_G_EXPERT_MODE_NO_WALKING")))
	{
		checksum |= CRCD(0xeba89179,"FLAG_G_EXPERT_MODE_NO_WALKING");
	}
	if( mp_career->GetCheat( CRCD(0xc4ffc672,"FLAG_G_EXPERT_MODE_NO_MANUALS")))
	{
		checksum |= CRCD(0xc4ffc672,"FLAG_G_EXPERT_MODE_NO_MANUALS");
	}
	if( mp_career->GetCheat( CRCD(0xd5982173,"NO_G_DISPLAY_BALANCE")))
	{
		checksum |= CRCD(0xd5982173,"NO_G_DISPLAY_BALANCE");
	}

	return checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mdl


================================================
FILE: Code/Sk/Modules/Viewer/Viewer.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2001 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			Viewer			 										**
**																			**
**	File name:		Viewer.cpp												**
**																			**
**	Created by:		11/28/01	-	SPG										**
**																			**
**	Description:	Viewer module		 									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 


#include 

#include 

#include 
#include 			// just to see if the select button is pressed!!!

#include 
#include 

#include 
#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

bool	run_runmenow = false;		// patch for signalling that the runnow script should be run

namespace Mdl
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

float	CViewer::s_movement_vel;	// in inches per sec
float	CViewer::s_rotate_vel;	// in degrees per sec
int		CViewer::s_view_mode=0;

// GJ:  I'm assuming that there's only going to be one CViewer at a time.
// I needed to be able to access this class from cfuncs.cpp, so I added
// this pointer.  Perhaps when Steve gets back from his surgery, we can
// decide a more appropriate interface between cfuncs.cpp and viewer.cpp.
CViewer* CViewer::sp_viewer = NULL;


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

void        CViewer::s_shift_input_logic_code ( const Inp::Handler < CViewer >& handler )
{
	Dbg_AssertType ( &handler, Inp::Handler< CViewer > );

	CViewer&   mdl = handler.GetData();

	mdl.m_shift_commands.ClearAll();

	if( handler.m_Input->m_Buttons & Inp::Data::mD_SELECT )
	{
		mdl.m_shift_commands.SetMask( handler.m_Input->m_Makes );
		if (Script::GetInt(CRCD(0xf3e055e1,"select_shift")))
		{
			// only mask the buttons if we are using select as a shift button 
			handler.m_Input->MaskDigitalInput( Inp::Data::mD_ALL && ~Inp::Data::mD_LEFT && ~Inp::Data::mD_RIGHT && ~Inp::Data::mD_UP && ~Inp::Data::mD_DOWN);
			handler.m_Input->MaskAnalogInput( Inp::Data::mA_ALL && ~Inp::Data::mA_LEFT && ~Inp::Data::mA_RIGHT && ~Inp::Data::mA_UP && ~Inp::Data::mA_DOWN && ~Inp::Data::mA_LEFT_X && ~Inp::Data::mA_LEFT_Y && ~Inp::Data::mA_RIGHT_X && ~Inp::Data::mA_RIGHT_Y );
		}
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CViewer::s_input_logic_code ( const Inp::Handler < CViewer >& handler )
{       
	CViewer&	mdl = handler.GetData();

//	printf ("s_view_mode = %d   rightx = %d\n",s_view_mode,mdl.m_right[X]);

// just return if we are not in viewer mode
	if (!s_view_mode)
	{
		return;
	}
							  
	// GJ:  no longer clearing the last frame's data
	// at the beginning of the loop...  this allows
	// the viewer object to add additional m_commands
	// to the viewer (useful for customizing controller
	// configs in different viewer modes)
//	mdl.m_commands.ClearAll();

	// translate m_Input data to module-specific commands
	mdl.m_right[X] = handler.m_Input->m_Event[Inp::Data::vA_RIGHT_X_UNCLAMPED] - 128;
	mdl.m_right[Y] = handler.m_Input->m_Event[Inp::Data::vA_RIGHT_Y_UNCLAMPED] - 128;
	mdl.m_left[X] = handler.m_Input->m_Event[Inp::Data::vA_LEFT_X_UNCLAMPED] - 128;
	mdl.m_left[Y] = handler.m_Input->m_Event[Inp::Data::vA_LEFT_Y_UNCLAMPED] - 128;


	if (Script::GetInt(CRCD(0x89bef647,"viewer_buttons_enabled")))
	{
		if( ( handler.m_Input->m_Buttons & Inp::Data::mD_R1 ) &&
			( handler.m_Input->m_Makes & Inp::Data::mD_R2 ))
		{
			if (!Config::CD())
			{
	//  Mick - took out viewport changing for non-PS2, as it messes up X-Box artists, with only one shoulder button
				#ifdef	__PLAT_NGPS__
				mdl.m_commands.SetMask( mCHANGE_SCREEN_MODE );
				#endif
			}	
		}
		else if( handler.m_Input->m_Buttons & Inp::Data::mD_R1 )
		{
			if( !( handler.m_Input->m_Buttons & Inp::Data::mD_R2 ))
			{
				mdl.m_commands.SetMask( mSTRAFE_UP );
			}
		}
		else if( handler.m_Input->m_Buttons & Inp::Data::mD_R2 )
		{
			mdl.m_commands.SetMask( mSTRAFE_DOWN );
		}
		
	//	if (!Config::CD())
		{
			if( handler.m_Input->m_Makes & Inp::Data::mD_START )
			{
				mdl.m_commands.SetMask( mRESET );
			}
			if( mdl.m_rotation_mode == vROTATE_CAMERA )
			{   
				if( handler.m_Input->m_Makes & Inp::Data::mD_TRIANGLE )
				{
					mdl.m_commands.SetMask( mVIEW_TOP );
				}
				if( handler.m_Input->m_Makes & Inp::Data::mD_SQUARE )
				{
					mdl.m_commands.SetMask( mVIEW_LEFT );
				}
				if( handler.m_Input->m_Makes & Inp::Data::mD_CIRCLE )
				{
					mdl.m_commands.SetMask( mVIEW_RIGHT );
				}
				if( handler.m_Input->m_Makes & Inp::Data::mD_X )
				{
					mdl.m_commands.SetMask( mVIEW_FRONT );
				}
			}
			else if( mdl.m_rotation_mode == vROTATE_OBJECTS )
			{
				if( handler.m_Input->m_Makes & Inp::Data::mD_X )
				{
					mdl.m_commands.SetMask( mRESET_ROTATIONS );
				}
			}
			if( handler.m_Input->m_Buttons & Inp::Data::mD_UP )
			{
				if ( handler.m_Input->m_Makes & Inp::Data::mD_UP )
				{
					mdl.m_commands.SetMask( mLIGHT_FW );
				}
			}
			if( handler.m_Input->m_Buttons & Inp::Data::mD_DOWN )
			{
				if ( handler.m_Input->m_Makes & Inp::Data::mD_DOWN )
				{
					mdl.m_commands.SetMask( mLIGHT_BK );
				}
			}
			if( handler.m_Input->m_Buttons & Inp::Data::mD_RIGHT )
			{
				mdl.m_commands.SetMask( mLIGHT_RT );
			}
			if( handler.m_Input->m_Buttons & Inp::Data::mD_LEFT )
			{
				mdl.m_commands.SetMask( mLIGHT_LT );
			}
			if( handler.m_Input->m_Makes & Inp::Data::mD_L3 )
			{
				mdl.m_commands.SetMask( mTOGGLE_ROTATE_MODE );
			}
			if ( handler.m_Input->m_Buttons & Inp::Data::mD_L1 )
			{
				if ( !( handler.m_Input->m_Buttons & Inp::Data::mD_L2 ))
				{
					mdl.m_commands.SetMask( mROLL_LEFT );
				}
			}
			else if ( handler.m_Input->m_Buttons & Inp::Data::mD_L2 )
			{
				mdl.m_commands.SetMask( mROLL_RIGHT );
			}
	
			
			if ( ! (handler.m_Input->m_Buttons & Inp::Data::mD_SELECT) )
			{
				// Patch on buttonscript calls to center the camera in various ways when in a view mode
				if ( handler.m_Input->m_Makes & Inp::Data::mD_SQUARE )
				{
					Script::RunScript("UserViewerSquare");
				}
				
				if ( handler.m_Input->m_Makes & Inp::Data::mD_CIRCLE )
				{
					Script::RunScript("UserViewerCIRCLE");
				}
				
				if ( handler.m_Input->m_Makes & Inp::Data::mD_TRIANGLE )
				{
					Script::RunScript("UserViewerTRIANGLE");
				}
				
				if ( handler.m_Input->m_Makes & Inp::Data::mD_X )
				{
					Script::RunScript("UserViewerX");
				}
			}		
			
		}	
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CViewer::s_logic_code ( const Tsk::Task< CViewer >& task )
{
	CViewer&	mdl = task.GetData();

	Dbg_AssertType ( &task, Tsk::Task< CViewer > );

	//static const    float   ldelta = 1.0f;
	//static const    float   lcolordelta = .0125f;
	static const    float   scale = ( 1.0f / ( 128.0f - Inp::vANALOGUE_TOL ));

//    Gfx::Camera* pCam = Nx::CViewportManager::sGetCamera( 0 );
//	if ( !pCam )
//	{
//		return;
//	}

	float elapsed_seconds;
	Tmr::Time elapsed_time;
	static Tmr::Time last_time = 0;

	// store the old camera pos before updating anything...
//	pCam->StoreOldPos( );

	elapsed_time = Tmr::ElapsedTime( last_time );
	elapsed_seconds = (int)elapsed_time / 1000.0f;
	last_time = last_time + elapsed_time;   

	if ( mdl.m_commands.TestMask( mTOGGLE_ROTATE_MODE ))
	{
		// Mick: Don't switch to rotating the camera around the object unless there is an object
		if ( mdl.mp_viewerObject && mdl.m_rotation_mode == vROTATE_CAMERA )
		{
			mdl.m_rotation_mode = vROTATE_OBJECTS;
		}
		else
		{
			mdl.m_rotation_mode = vROTATE_CAMERA;
		}      
	}

	mdl.m_right[Y] =
	( mdl.m_right[Y] >  Inp::vANALOGUE_TOL ) ? ( mdl.m_right[Y] - Inp::vANALOGUE_TOL ) * scale :
	( mdl.m_right[Y] < -Inp::vANALOGUE_TOL ) ? ( mdl.m_right[Y] + Inp::vANALOGUE_TOL ) * scale : 0.0f;
	mdl.m_right[X] =
	( mdl.m_right[X] >  Inp::vANALOGUE_TOL ) ? ( mdl.m_right[X] - Inp::vANALOGUE_TOL ) * scale :
	( mdl.m_right[X] < -Inp::vANALOGUE_TOL ) ? ( mdl.m_right[X] + Inp::vANALOGUE_TOL ) * scale : 0.0f;

	mdl.m_left[Y] =
	( mdl.m_left[Y] >  Inp::vANALOGUE_TOL ) ? ( mdl.m_left[Y] - Inp::vANALOGUE_TOL ) * scale :
	( mdl.m_left[Y] < -Inp::vANALOGUE_TOL ) ? ( mdl.m_left[Y] + Inp::vANALOGUE_TOL ) * scale : 0.0f;
	mdl.m_left[X] =
	( mdl.m_left[X] >  Inp::vANALOGUE_TOL ) ? ( mdl.m_left[X] - Inp::vANALOGUE_TOL ) * scale :
	( mdl.m_left[X] < -Inp::vANALOGUE_TOL ) ? ( mdl.m_left[X] + Inp::vANALOGUE_TOL ) * scale : 0.0f;

	Obj::CCompositeObject * p_obj = (Obj::CCompositeObject *) Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0xeb17151b,"viewer_cam"));
	if (p_obj)
	{
		if ( mdl.m_rotation_mode == vROTATE_CAMERA )
		{
			if (mdl.m_left[Y])
			{
				p_obj->GetMatrix().RotateLocal( Mth::Vector( 1, 0, 0 ),
							mdl.m_left[Y] * elapsed_seconds * mdl.s_rotate_vel * Mth::PI / 180.0f );
			}
			if (mdl.m_left[X])
			{
				// this rotation is *NOT* local, as the designers seem to prefer it this way.
				Mth::Matrix mat = p_obj->GetMatrix();
				mat.RotateY( -mdl.m_left[X] * elapsed_seconds * mdl.s_rotate_vel * Mth::PI / 180.0f );
				p_obj->SetMatrix( mat );
			}
		}
		else if ( mdl.m_rotation_mode == vROTATE_OBJECTS )
		{
			Dbg_Assert( mdl.mp_viewerObject );
			mdl.mp_viewerObject->SetRotation( mdl.m_left[X], mdl.m_left[Y] );
		}


	
		// Move with right analog stick:
		if (mdl.m_right[X] || mdl.m_right[Y])
		{
			Mth::Vector transVec;
			transVec[X] = mdl.m_right[X] * ( elapsed_seconds * mdl.s_movement_vel );
			transVec[Y] = 0.0f;
			transVec[Z] = mdl.m_right[Y] * ( elapsed_seconds * mdl.s_movement_vel );
			transVec[W] = 1.0f;
			s_translate_local( p_obj, transVec );
		}
	}

	if ( mdl.m_commands.TestMask( mSTRAFE_UP ))
	{
		s_translate_local( p_obj, Mth::Vector( 0, elapsed_seconds * mdl.s_movement_vel, 0 ) );
	}
	if ( mdl.m_commands.TestMask( mSTRAFE_DOWN ))
	{
		s_translate_local( p_obj, Mth::Vector( 0, -elapsed_seconds * mdl.s_movement_vel, 0 ) );
	}

	if ( !Script::GetInt( CRCD(0xd8c91578,"DisableViewerRoll"), false ) )
	{
		if ( mdl.m_commands.TestMask( mROLL_LEFT ))
		{
			p_obj->GetMatrix().RotateLocal( Mth::Vector( 0, 0, 1 ),
									  -0.5f * elapsed_seconds * mdl.s_rotate_vel * Mth::PI / 180.0f );
		}
		if ( mdl.m_commands.TestMask( mROLL_RIGHT ))
		{
			p_obj->GetMatrix().RotateLocal( Mth::Vector( 0, 0, 1 ),
									  0.5f * elapsed_seconds * mdl.s_rotate_vel * Mth::PI / 180.0f );
		}
	}
	
	p_obj->Pause(false);		// dang, it's suspended due to game being paused, so need to update manually...
							// THIS IS A PATCH - needs to be more explicity handled.....
	
	
	if ( mdl.m_commands.TestMask( mCHANGE_SCREEN_MODE ))
	{
		Nx::CViewportManager::sSetScreenMode ( (Nx::ScreenMode) (( (int) Nx::CViewportManager::sGetScreenMode() + 1 ) % Nx::vNUM_SCREEN_MODES) );
	}

	// GJ:  clear the frame's data, now that we're done with it
	// (this used to be done at the beginning of the loop)
	mdl.m_commands.ClearAll();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		CViewer::s_translate_local( Obj::CCompositeObject* p_obj, const Mth::Vector& vec )
{
	// need to build a temporary matrix,
	// because the rotation and translation
	// are stored separately in the camera
	Mth::Matrix mat = p_obj->GetMatrix();
	mat[Mth::POS] = p_obj->GetPos();
	
	mat.TranslateLocal( vec );
	
	p_obj->SetPos(mat[Mth::POS]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CScript *spawn_if_exists(uint32 script)
{
	if (Script::ScriptExists(script))
	{
		return  Script::SpawnScript(script);
	}
	return NULL;
}

void        CViewer::s_shift_logic_code ( const Tsk::Task< CViewer >& task )
{
	Dbg_AssertType ( &task, Tsk::Task< CViewer > );

	CViewer&   mdl = task.GetData();

	if (!Script::GetInt(CRCD(0xf3e055e1,"select_shift")))
	{
//		if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_SELECT ))
//		{
//			Script::RunScript(CRCD(0x60871393,"UserSelectSelect"));
//		}
	}
	else
	{
		Script::CScript *p_script=NULL;
		if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_TRIANGLE ))
		{
			p_script=spawn_if_exists(CRCD(0xcbb91022,"UserSelectTriangle"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_SQUARE ))
		{
			p_script=spawn_if_exists(CRCD(0xe69691fa,"UserSelectSquare"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_CIRCLE ))
		{
			// (launches the game)
			p_script=spawn_if_exists(CRCD(0xffc29c2a,"UserSelectCircle"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_START ))
		{
			p_script=spawn_if_exists(CRCD(0x51b0e413,"UserSelectStart"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_X ))
		{
			p_script=spawn_if_exists(CRCD(0x746d2598,"UserSelectX"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_L1 ))
		{
			p_script=spawn_if_exists(CRCD(0x81d0a13c,"UserSelectL1"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_L2 ))
		{
			p_script=spawn_if_exists(CRCD(0x18d9f086,"UserSelectL2"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_R1 ))
		{
			p_script=spawn_if_exists(CRCD(0x55919ee3,"UserSelectR1"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_R2 ))
		{
			p_script=spawn_if_exists(CRCD(0xcc98cf59,"UserSelectR2"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_UP ))
		{
			p_script=spawn_if_exists(CRCD(0x1b0b7922,"UserSelectUp"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_DOWN ))
		{
			p_script=spawn_if_exists(CRCD(0xbbc0e36a,"UserSelectDown"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_LEFT ))
		{
			p_script=spawn_if_exists(CRCD(0xdd589439,"UserSelectLeft"));
		}
		else if ( mdl.m_shift_commands.TestMask( Inp::Data::mD_RIGHT ))
		{
			p_script=spawn_if_exists(CRCD(0x7a03c488,"UserSelectRight"));
		}
		   
		if (p_script)
		{
			#ifdef __NOPT_ASSERT__
			p_script->SetCommentString("Spawned from CViewer::s_shift_logic_code()");
			#endif
		}	
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewer::s_handle_quickview( Net::MsgHandlerContext* context )
{
	Net::MsgQuickview* p_msg;
	Script::CStruct* pStructure;

	p_msg = (Net::MsgQuickview*) context->m_Msg;

	Dbg_Printf( "Got quickview request to load file %s....\n", p_msg->m_Filename );

    pStructure = new Script::CStruct;

	pStructure->AddComponent( Script::GenerateCRC( "scene" ), ESYMBOLTYPE_STRING, p_msg->m_Filename );

	Script::RunScript( "ReloadScene", pStructure );

	delete pStructure;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewer::s_handle_incremental_update( Net::MsgHandlerContext* context )
{
	Net::MsgQuickview* p_msg;
	Script::CStruct* pStructure;

	p_msg = (Net::MsgQuickview*) context->m_Msg;

	Dbg_Printf( "Got quickview request to load file %s....\n", p_msg->m_Filename );

    pStructure = new Script::CStruct;

	pStructure->AddComponent( Script::GenerateCRC( "scene" ), ESYMBOLTYPE_STRING, p_msg->m_Filename );

	pStructure->AddComponent( Script::GenerateCRC( "add" ), ESYMBOLTYPE_STRING, p_msg->m_UpdateFilename );
	Script::RunScript( "AddToScene", pStructure );

	delete pStructure;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewer::s_handle_update_material( Net::MsgHandlerContext* context )
{
	Dbg_Printf( "Got material update request....\n" );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewer::s_handle_rq( Net::MsgHandlerContext* context )
{
	char* path = context->m_Msg;

	if ((strcmp("..\\runnow.qb",path) == 0) || (strcmp("runnow.qb",path) == 0))
	{
		run_runmenow = true;
	}
	else
	{
		Dbg_Printf( "******* GOT PATH %s ********\n", path );
	}
	SkateScript::LoadQB( path );
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewer::s_handle_load_model( Net::MsgHandlerContext* context )
{        
	Dbg_Message( "Got request to load model/anim...\n" );
	
	Mdl::CViewer* pViewer = (Mdl::CViewer*) context->m_Msg;
	Dbg_Assert( pViewer );
	
	Script::CStruct* pStruct = new Script::CStruct;

	Net::MsgViewObjLoadModel* p_msg;
	p_msg = (Net::MsgViewObjLoadModel*) context->m_Msg;
	pStruct->AddString( CRCD(0xb974f2fc,"modelName"), p_msg->m_ModelName );
	pStruct->AddChecksum( CRCD(0x6c2bfb7f,"animName"), p_msg->m_AnimScriptName );
	pStruct->AddChecksum( CRCD(0x09794932,"skeletonName"), p_msg->m_SkeletonName );
	pViewer->AddViewerObject( pStruct );
	
	delete pStruct;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
		 
int CViewer::s_handle_unload_model( Net::MsgHandlerContext* context )
{
	Dbg_Message( "Got request to unload model/anim...\n" );

	Mdl::CViewer* pViewer = (Mdl::CViewer*) context->m_Msg;
	Dbg_Assert( pViewer );
	pViewer->RemoveViewerObject();

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewer::s_handle_run_script_command( Net::MsgHandlerContext* context )
{
	uint32 *p_data=(uint32*)context->m_Msg;
	
	uint32 command_name=*p_data++;
	
	Script::CStruct *p_params=new Script::CStruct;
	Script::ReadFromBuffer(p_params,(uint8*)p_data);
//	Dbg_Printf( "Running script command %p\n", command_name );
	Script::RunScript(command_name,p_params);
	delete p_params;
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewer::AddViewerObject( Script::CStruct* pParams )
{
	if (Config::GotExtraMemory())
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
	}
	else
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	}
		
	// if the viewer object exists already,
	// clear it out
	if ( mp_viewerObject )
	{
		RemoveViewerObject();
	}

	Dbg_Assert( !mp_viewerObject );
	mp_viewerObject = new Obj::CViewerObject( mp_server );
	mp_viewerObject->LoadModel( pParams );
	
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewer::RemoveViewerObject()
{
	if ( mp_viewerObject )
	{
		mp_viewerObject->UnloadModel();
		delete mp_viewerObject;
		mp_viewerObject = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CViewer::v_start_cb ( void )
{
	Inp::Manager* inp_manager = Inp::Manager::Instance();
	Mlp::Manager* mlp_manager = Mlp::Manager::Instance();

	#ifdef		__USE_PROFILER__	
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ProfilerHeap());
	#else
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
	#endif

	// Sorry Steve, but we need the shift input handler on the CD, so I'm going
	// to have to start this module all the time.
	// but on the CD, we don't initialize any of the actual view stuff
	if (!Config::CD())
	{
		
		
	
		mp_server->m_Dispatcher.AddHandler( Net::vMSG_ID_QUICKVIEW, 
											s_handle_quickview, 
											Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN );
		mp_server->m_Dispatcher.AddHandler(	Net::vMSG_ID_UPDATE_MATERIAL, 
											s_handle_update_material, 
											Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN );
		mp_server->m_Dispatcher.AddHandler(	Net::vMSG_ID_REMOTE_Q, 
											s_handle_rq, 
											Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN );
		mp_server->m_Dispatcher.AddHandler( Net::vMSG_ID_INCREMENTAL_UPDATE,
											s_handle_incremental_update,
											Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN );
		mp_server->m_Dispatcher.AddHandler( Net::vMSG_ID_VIEWOBJ_LOAD_MODEL, 
											s_handle_load_model,
											Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
		mp_server->m_Dispatcher.AddHandler( Net::vMSG_ID_VIEWOBJ_UNLOAD_MODEL, 
											s_handle_unload_model,
											Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
		mp_server->m_Dispatcher.AddHandler(	Net::vMSG_ID_RUN_SCRIPT_COMMAND, 
											s_handle_run_script_command, 
											Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
	
	
	
		Dbg_Printf( "Starting Viewer Module....\n" );
	}	

	// We also add the regular input handler and logic handler on CD
	mlp_manager->AddLogicTask( *mp_logic_task );
	inp_manager->AddHandler( *mp_input_handler );
	
	// The "shift_input" handler and logic are just for the user-select buttons
	// specifically the screenshot (Select+Square)
	// generally they are disabled. 
	// see buttonscripts.q for the scripts that get run

	printf("Adding shift input handlers\n)");	
	inp_manager->AddHandler( *mp_shift_input_handler );
	mlp_manager->AddLogicTask( *mp_shift_logic_task );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CViewer::v_stop_cb ( void )
{   
	if ( mp_viewerObject )
	{
    	delete mp_viewerObject;
		mp_viewerObject = NULL;
	}
    
	mp_input_handler->Remove();
	mp_logic_task->Remove();
	mp_shift_logic_task->Remove();
	mp_shift_input_handler->Remove();

	mp_server->m_Dispatcher.Deinit();
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

CViewer::CViewer( void )
{
	if (!Config::CD())
	{
		uint32 local_ip;

		Net::Manager * net_man = Net::Manager::Instance();
			
#ifdef __PLAT_NGC__
		SOInAddr local_addr;
		SOInetAtoN( net_man->GetLocalIP(), &local_addr );
		local_ip = local_addr.addr;
#else
		local_ip = inet_addr( net_man->GetLocalIP());
#endif
		net_man->NetworkEnvironmentSetup();
		mp_server = net_man->CreateNewAppServer( 0, "Skate4 Viewer", 4, Net::vEXPORT_COMM_PORT,
													inet_addr( net_man->GetLocalIP()), Net::App::mACCEPT_FOREIGN_CONN );
	
		Dbg_Assert( mp_server );
	}	


	int viewer_controller = Script::GetInt(CRCD(0x702247a5,"Viewer_controller"), false);

	mp_logic_task = new Tsk::Task< CViewer > ( CViewer::s_logic_code, *this );
	mp_input_handler = new Inp::Handler< CViewer > ( viewer_controller,  s_input_logic_code, *this, Tsk::BaseTask::Node::vNORMAL_PRIORITY );
	
	s_movement_vel = 400.0f;	// default movement velocity in inches per second
	s_rotate_vel = 120.0f;		// 120 degrees per second. In 3 seconds you should complete 1 revolution

	printf("Creating shift input handlers\n)");	
	mp_shift_logic_task = new Tsk::Task< CViewer > ( CViewer::s_shift_logic_code, *this );
	mp_shift_input_handler = new Inp::Handler< CViewer > ( viewer_controller,  CViewer::s_shift_input_logic_code, *this, Tsk::BaseTask::Node::vHANDLER_PRIORITY_VIEWER_SHIFT_INPUT_LOGIC );

	Dbg_Assert( !sp_viewer );
	sp_viewer = this;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CViewer::~CViewer( void )
{
	Dbg_Assert( mp_server );

	delete mp_server;
	delete mp_input_handler;
	delete mp_logic_task;
	delete mp_shift_logic_task;
	delete mp_shift_input_handler;

	Dbg_Assert( sp_viewer );
	sp_viewer = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CViewerObject* CViewer::GetViewerObject()
{
	return mp_viewerObject;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CViewer::sSetViewMode(int viewMode)
{
	s_view_mode = viewMode;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void    CViewer::sSetMovementVelocity( float in_per_sec )
{
	s_movement_vel = in_per_sec;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				 
void    CViewer::sSetRotateVelocity( float deg_per_sec )
{
	s_rotate_vel = deg_per_sec;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				 
bool	CViewer::ResetCameraToViewerObject()
{
	if ( !Script::GetInteger( CRCD(0xc965e06f,"ResetCameraToViewerObject"), Script::NO_ASSERT ) )
	{
		return true;
	}

//	Gfx::Camera* pCamera = Nx::CViewportManager::sGetCamera(0);
	Obj::CCompositeObject * p_obj = (Obj::CCompositeObject *) Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0xeb17151b,"viewer_cam"));

	if ( p_obj )
	{
		// ideally we would want to hide the level/sky when
		// switching to the object viewer, but for now,
		// just put the viewer object in the sky...

		// set the camera
		Mth::Matrix ident;
		ident.Ident();
		p_obj->SetMatrix( ident );

		// the viewer object gets placed at (0, 1000, 0),
		// so put the camera slightly offset from here
		p_obj->SetPos( Mth::Vector(0.0f, 35.0f, 100.0f) );
	}

	return true;
}			

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				 
CViewer* CViewer::sGetViewer()
{
	return sp_viewer;
}
							  
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				 
void CViewer::SetCommand( const uint32 command )
{
	m_commands.SetMask( command );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				 
} // namespace Mdl



================================================
FILE: Code/Sk/Modules/Viewer/Viewer.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2001 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Skate4													**
**																			**
**	Module:			Viewer			 										**
**																			**
**	File name:		Viewer.h												**
**																			**
**	Created by:		11/28/01	-	SPG										**
**																			**
**	Description:	Viewer module		 									**
**																			**
*****************************************************************************/

#ifndef __SK_MODULES_VIEWER_H__
#define __SK_MODULES_VIEWER_H__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{
    class CViewerObject;
    class CCompositeObject;
};

namespace Script
{
	class CStruct;
};
			  
namespace Mdl
{
/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CViewer : public Module
{
public:
	CViewer( void );
	~CViewer( void );

	static int	sGetViewMode() {return s_view_mode; }
	static void	sSetViewMode(int viewMode);
	static void	sSetMovementVelocity( float in_per_sec );
	static void	sSetRotateVelocity( float deg_per_sec );
	
	// GJ:  viewer mode stuff...
	// there's probably a better way to do this...
	static CViewer*		sGetViewer();
	bool				ResetCameraToViewerObject();
	void				AddViewerObject( Script::CStruct* pParams );
	void				RemoveViewerObject();
	Obj::CViewerObject*	GetViewerObject();
	static CViewer*		sp_viewer;
	void				SetCommand( const uint32 command );

	enum ECommands
	{
		mSTRAFE_UP          = nBit( 1 ),
		mSTRAFE_DOWN        = nBit( 2 ),
		mLIGHT_BK			= nBit( 3 ),
		mLIGHT_FW			= nBit( 4 ),
		mLIGHT_LT			= nBit( 5 ),
		mLIGHT_RT			= nBit( 6 ),
		mVIEW_FRONT         = nBit( 7 ),
		mVIEW_LEFT          = nBit( 8 ),
		mVIEW_RIGHT         = nBit( 9 ),
		mVIEW_TOP           = nBit( 10 ),
		mRESET              = nBit( 11 ),
		mCHANGE_SCREEN_MODE = nBit( 12 ),
		mTOGGLE_RENDER_MODE = nBit( 13 ),
		mTOGGLE_ROTATE_MODE	= nBit( 14 ),
		mRESET_ROTATIONS	= nBit( 15 ),
		mROLL_LEFT          = nBit( 16 ),
		mROLL_RIGHT         = nBit( 17 ),
	};

	enum ERotationMode
	{
		vROTATE_CAMERA,
		vROTATE_OBJECTS
	};

private:
	void			v_start_cb ( void );
	void			v_stop_cb ( void );
	
	static 			Net::MsgHandlerCode				s_handle_quickview;
	static 			Net::MsgHandlerCode				s_handle_incremental_update;
	static 			Net::MsgHandlerCode				s_handle_update_material;
	static 			Net::MsgHandlerCode				s_handle_rq;
	static 			Net::MsgHandlerCode				s_handle_load_model;
	static 			Net::MsgHandlerCode				s_handle_unload_model;
	static 			Net::MsgHandlerCode				s_handle_run_script_command;
	
	static			Tsk::Task< CViewer >::Code		s_logic_code;       
	static			Tsk::Task< CViewer >::Code		s_shift_logic_code;       
	
	static			Inp::Handler< CViewer >::Code	s_input_logic_code;
	static			Inp::Handler< CViewer >::Code	s_shift_input_logic_code;
	static			Inp::Handler< CViewer >::Code	s_shift_input_logic_code2;
	
	static 			void							s_translate_local( Obj::CCompositeObject* p_obj, const Mth::Vector& vec );
	

	Inp::Handler< CViewer >*	mp_input_handler;
	Inp::Handler< CViewer >*	mp_shift_input_handler;      
	Tsk::Task< CViewer >*		mp_logic_task;
	Tsk::Task< CViewer >*		mp_shift_logic_task;	

	// Input
    Mth::Vector		m_right;
    Mth::Vector		m_left;
    Flagsm_commands;
	Flags     m_shift_commands;
	ERotationMode	m_rotation_mode;
	static float	s_movement_vel;	// in inches per sec
	static float	s_rotate_vel;	// in degrees per sec
	static int		s_view_mode;	// 0 if inactive
	
	Net::Server*	mp_server;

    Obj::CViewerObject*    mp_viewerObject;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

CViewer* GetViewer();

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mdl

#endif	// __SK_MODULES_VIEWER_H__





================================================
FILE: Code/Sk/Ngps/crt0.s
================================================

/* SCEI CONFIDENTIAL
 "PlayStation 2" Programmer Tool Runtime Library Release 2.4
 */
/*
 *                      Emotion Engine Library
 *                          Version 1.10
 *                           Shift-JIS
 *
 *      Copyright (C) 1998-1999 Sony Computer Entertainment Inc.
 *                        All Rights Reserved.
 *
 *                       libkernel - crt0.s
 *                        kernel libraly
 *
 *       Version        Date            Design      Log
 *  --------------------------------------------------------------------
 *       1.10           Oct.12.1999     horikawa    renewal
 *       1.50           May.16.2000     horikawa
 */

/* Ken: Added so that I can use register names like a0 rather than $4	*/
#include "C:\usr\local\sce\iop\gcc\mipsel-scei-elfl\include\cpureg.h"

#define DS_SOUND

#ifdef __mips16
	.set nomips16	/* This file contains 32 bit assembly code. */
#endif

#define	ARG_SIZ     256 + 16*4 + 1*4

         _stack	=		_std_stack
         
	.set noat
    	.set noreorder
	.global ENTRYPOINT
	.global _start
	.ent	_start
	.text				# 0x00200000
	nop
	nop
ENTRYPOINT:
_start:
#if defined(DS_SOUND)
	/* $4 =  1 ʥ쥸Ϥ*/
   	lui	$2, %hi(_args_ptr)
   	addiu	$2, $2, %lo(_args_ptr)
    	sw	$4, ($2)
#endif
/*
 * clear .bss
 */
zerobss:
	lui	$2, %hi(_fbss)
	lui	$3, %hi(_end)
	addiu	$2, $2, %lo(_fbss)
	addiu	$3, $3, %lo(_end)
1:
	sq	$0, ($2)
	nop
	sltu	$1, $2, $3
	nop
	nop
	bne	$1, $0, 1b
	addiu	$2, $2, 16

/*
 * initialize main thread
 */
	lui	$4, %hi(_gp)
	lui	$5, %hi(_stack)
	lui	$6, %hi(_stack_size)
	lui	$7, %hi(_args)
	lui	$8, %hi(_root)
	addiu	$4, $4, %lo(_gp)
	addiu	$5, $5, %lo(_stack)
	addiu	$6, $6, %lo(_stack_size)
	addiu	$7, $7, %lo(_args)
	addiu	$8, $8, %lo(_root)
	move	$28, $4
	addiu	$3, $0, 60
	syscall
	move	$29, $2

/*
 * initialize heap area
 */
	lui	$4, %hi(_end)
	lui	$5, %hi(_heap_size)
	addiu	$4, $4, %lo(_end)
	addiu	$5, $5, %lo(_heap_size)
	addiu	$3, $0, 61
	syscall

/*
 *	Ken addition.
 *	Detect whether we are running on a ProView system by seeing whether 
 *	snputs works.
 */

	jal sceScfGetLanguage		# snputs will always fail until this is called, for some reason.
	nop
	
	la a0,TestStringForSnputs
	jal snputs
	nop

	bltz v0,notproview			# If the return value is -1, it is not a proview.
	nop

/*
 *	Now we know we're running on a ProView, so run the startup code copied from
 *	the ProView crt0.s
 */

	
/*
 * flush data cache
 */
	jal	FlushCache
	move	$4, $0

/*
 * call main program
 */
	ei

/*
 * This is a Neversoft addition, we need to call pre_main to set up the mem manager & debug stuff
 */ 
    jal pre_main
	nop
	
#if defined(DS_SOUND)	
 	lui	$2, %hi(_args_ptr)
 	addiu	$2, $2, %lo(_args_ptr)

 	lw	$3, ($2)
	
 	beq	$3, $0, _skipArgV
 	nop

 	addiu	$2, $3, 4
	b	_run	
	nop

_skipArgV:
	lui	$2, %hi(_args)
	addiu	$2, $2, %lo(_args)

#else
	lui	$2, %hi(_args)
	addiu	$2, $2, %lo(_args)
#endif

_run:

	lw	$4, ($2)
	jal	main
	addiu	$5, $2, 4

/*
 * This is a Neversoft addition, we call post_main at the end
 */ 
    jal post_main
	nop

#if defined(DS_SOUND)
	j	_root
	nop
#else
	j	Exit
	move	$4, $2
#endif

		
notproview:

/*
 * initialize System
 */
	jal	_InitSys
	nop

/*
 * flush data cache
 */
	jal	FlushCache
	move	$4, $0

/*
 * call main program
 */
	ei
			
/*
 * This is a Neversoft addition, we need to call pre_main to set up the mem manager & debug stuff
 */ 
    jal pre_main
	nop
        
	lui	$2, %hi(_args)
	addiu	$2, $2, %lo(_args)
	lw	$4, ($2)
	jal	main
	addiu	$5, $2, 4

/*
 * This is a Neversoft addition, we call post_main at the end
 */ 
    jal post_main
	nop
        
	j	Exit
	move	$4, $2
	.end	_start

/**************************************/
	.align	3
	.global	_exit
	.ent	_exit
_exit:
#if defined(DS_SOUND)
	j	_root
	nop
#else
	j	Exit			# Exit(0);
	move	$4, $0
#endif
	.end	_exit
    
	.align	3
	.ent	_root
_root:
#if defined(DS_SOUND)
/*
 *	K: If not ProView, don't do the next bit, or it will crash.
 */
	la a0,TestStringForSnputs
	jal snputs
	nop
	bltz v0,notproviewsogotoexitthread	# If the return value is -1, it is not a proview.
	nop

	lui	$2, %hi(_args_ptr)
	addiu	$2, $2, %lo(_args_ptr)
	lw	$3, ($2)
	jal	SignalSema
	lw	$4, ($3)
	
notproviewsogotoexitthread:	
#endif
	addiu	$3, $0, 35		# ExitThread();
	syscall
	.end	_root

	.bss
	.align	6
_args: .space	ARG_SIZ

#if defined(DS_SOUND)
	.data
_args_ptr:
	.space 4
#endif



/*
 * Ken: This is used by ProView detection code
 */
	.section	.rodata
TestStringForSnputs:   .string "crt0.s detected ProView ...\n"



================================================
FILE: Code/Sk/Objects/FollowOb.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Object (OBJ)											**
**																			**
**	File name:		objects/followob.h    									**
**																			**
**	Created: 		05/02/02	-	ksh										**
**																			**
*****************************************************************************/

#ifndef __OBJECTS_FOLLOWOB_H
#define __OBJECTS_FOLLOWOB_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __CORE_MATH_VECTOR_H
#include 
#endif

//#include 

//#include 

//#include 

//#include 
//#include 

//#include 

//#include 
//#include 

//#include 

//#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

#define MAX_FOLLOWOB_PATH_POINTS 64
class  CFollowOb  : public Spt::Class
{
	Mth::Vector mp_path_points[MAX_FOLLOWOB_PATH_POINTS];
	int m_num_path_points;
	int m_most_recent_path_point;
	
	uint32 m_leader_name;
	Mth::Vector m_leader_pos;
	float m_distance;
	bool m_orient_y;
	
public:
	CFollowOb();
	~CFollowOb();
	
	void	Reset();
	void	SetLeaderName(uint32 name) {m_leader_name=name;}
	void	SetDistance(float distance) {m_distance=distance;}
	
	void	GetNewPathPointFromObjectBeingFollowed();
	void	CalculatePositionAndOrientation(Mth::Vector *p_pos, Mth::Matrix *p_orientation);
	void	OrientY() {m_orient_y=true;}
	void	DoNotOrientY() {m_orient_y=false;}
};

} // namespace Obj

#endif	// __OBJECTS_FOLLOWOB_H



================================================
FILE: Code/Sk/Objects/GameObj.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       gameobj.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/02/2000
//****************************************************************************

#include 

#include 

#include 
#include 

#include   // needs some stupid special setup


#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Obj
{	

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

#define		OLD_GO_SYSTEM  0

Obj::CCompositeObject* create_game_obj( CGeneralManager* p_obj_man, Script::CStruct* pNodeData, bool is_level_obj )
{


	#if OLD_GO_SYSTEM	
	CMovingObject* pGameObj = new CMovingObject;
	pGameObj->MovingObjectCreateComponents();
	#else

// Create a composite object from this node, but don't finalize it
		Obj::CCompositeObject* pGameObj = Obj::CCompositeObjectManager::Instance()->CreateCompositeObjectFromNode(
			Script::GetArray("gameobj_composite_structure"), pNodeData, false);

//
    CLockObjComponent* p_lockObjComponent = new CLockObjComponent;
	pGameObj->AddComponent(p_lockObjComponent);

	
	#endif
	
	#if OLD_GO_SYSTEM		
	pGameObj->SetType( SKATE_TYPE_GAME_OBJ );
	Dbg_MsgAssert(pGameObj, ("Failed to create gameobj."));
	p_obj_man->RegisterObject(*pGameObj);
	SkateScript::GetPosition( pNodeData, &pGameObj->m_pos );
	pGameObj->MovingObjectInit( pNodeData, p_obj_man );
	#endif

	Script::RunScript( CRCD(0xf3b84da6,"gameobj_add_components"), pNodeData, pGameObj );

	pGameObj->Finalize();

	// need to synchronize rendered model's position to initial world position
	Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pGameObj );
	if ( pModelComponent )
	{
		pModelComponent->FinalizeModelInitialization();
	}

//	#if !OLD_GO_SYSTEM
	// This used to be in MovingObjectInit	
	// get any script associated with the object, and run it on the object
	uint32 AIScriptChecksum=0;
	if ( pNodeData->GetChecksum(CRCD(0x2ca8a299,"TriggerScript"),&AIScriptChecksum) )
	{
		Script::CScriptStructure *pScriptParams=NULL;
		pNodeData->GetStructure(CRCD(0x7031f10c,"Params"),&pScriptParams);
		pGameObj->SwitchScript( AIScriptChecksum, pScriptParams );
	}
//	#endif

	return pGameObj;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CCompositeObject* CreateGameObj( CGeneralManager* p_obj_man, Script::CStruct* pNodeData )
{   
	return create_game_obj( p_obj_man, pNodeData, false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CreateLevelObj( CGeneralManager* p_obj_man, Script::CStruct* pNodeData )
{
	if (pNodeData->ContainsFlag("Bouncy"))
	{
		Obj::CCompositeObjectManager::Instance()->CreateCompositeObjectFromNode(Script::GetArray("bouncy_composite_structure"), pNodeData);
	}
	else
	{
		create_game_obj( p_obj_man, pNodeData, true );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CreateParticleObject( CGeneralManager* p_obj_man, Script::CStruct* pNodeData )
{
	if( pNodeData->ContainsComponentNamed( "HasMotion" ))
	{
		Obj::CCompositeObjectManager::Instance()->CreateCompositeObjectFromNode(
					Script::GetArray("moving_particle_composite_structure"), pNodeData);
	}
	else
	{
		Obj::CCompositeObjectManager::Instance()->CreateCompositeObjectFromNode(
					Script::GetArray("particle_composite_structure"), pNodeData);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CreateParticleEmitter( CGeneralManager* p_obj_man, Script::CStruct* pNodeData )
{
	Dbg_MsgAssert( pNodeData, ( "Couldn't find node script." ) );

	// Run the trigger script, and this will create the game object
	// then need to find the game object	
	uint32 AIScriptChecksum=0;
	pNodeData->GetChecksum( "TriggerScript", &AIScriptChecksum, true );
	uint32 NameChecksum=0;
	pNodeData->GetChecksum( "Name", &NameChecksum, true );
	if ( AIScriptChecksum )
	{
		// Make sure we don't have an unnamed particle system already
		const uint32 PARTICLE_UNNAMED = 0x502335a1;   // Particle_UnNamed			
		Dbg_MsgAssert(!Nx::CEngine::sGetParticleTable()->GetItem(PARTICLE_UNNAMED),("Particle_UnNamed should not exist"));		
			
		// Get the parameters for the TriggerScript, and run it
		// this should create a particle system
		Script::CScriptStructure *pScriptParams=NULL;
		pNodeData->GetStructure("Params",&pScriptParams);
		Script::RunScript(AIScriptChecksum,pScriptParams);		// this will create the particle emitter
		
		// Get the newly created particle system
		Nx::CParticle *p_system = Nx::CEngine::sGetParticleTable()->GetItem(PARTICLE_UNNAMED);
		Dbg_MsgAssert(p_system,("Script did not create Particle_UnNamed!"));

		// Remove the entry from the Hash table			
		Nx::CEngine::sGetParticleTable()->FlushItem(PARTICLE_UNNAMED);

		// Add it back with the name as specifed in this node			
		Nx::CEngine::sGetParticleTable()->PutItem(NameChecksum,p_system);
																  
		p_system->set_checkum(NameChecksum);																  					   
		p_system->SetID(NameChecksum);																  					   
		   
		Mth::Vector	angles;
		Mth::Vector dir (0.0f,1.0f,0.f);
		pNodeData->GetVector("Angles",&angles);
		printf("Angles(%f,%f,%f)\n",angles[X],angles[Y],angles[Z]);
		// 3DSMAX_ANGLES Mick: 3/19/03 - Changed all rotation orders to X,Y,Z
		dir.RotateX(angles[X]);
		dir.RotateY(angles[Y]);
		dir.RotateZ(angles[Z]);
		
		p_system->set_emit_target( dir[X], dir[Y], dir[Z] );

		// Register it with the object manager.....
		// This means that the system can be refered to like a regular moving object
		p_obj_man->RegisterObject(*p_system);
			   
		p_system->MovingObjectInit( pNodeData, p_obj_man ); 	// get LOD and suspend info
		
		// Bastard crap code.... Since I set them up to be objects
		// they now run their scripts with the new component system
		// meaning the scripts run twice, dammit...												
		p_system->GetScript()->ClearScript();
		p_system->GetScript()->ClearEventHandlerTable();
	}
}


} // namespace Obj



================================================
FILE: Code/Sk/Objects/GameObj.h
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       gameobj.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/02/2000
//****************************************************************************

#ifndef __OBJECTS_GAMEOBJ_H
#define __OBJECTS_GAMEOBJ_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

namespace Script
{
	class CStruct;
}

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{
	class CCompositeObject;
	class CGeneralManager;

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

void CreateParticleEmitter( CGeneralManager* p_obj_man, Script::CStruct* pNodeData );
CCompositeObject* CreateGameObj( CGeneralManager* p_obj_man, Script::CStruct* pNodeData );
void CreateLevelObj( CGeneralManager* p_obj_man, Script::CStruct* pNodeData );
void CreateParticleObject( CGeneralManager* p_obj_man, Script::CStruct* pNodeData );

} // namespace Obj

#endif	// __OBJECTS_GAMEOBJ_H


================================================
FILE: Code/Sk/Objects/MovingObject.cpp
================================================
/*
	MovingObject.cpp
	
	Skaters, Peds, Cars are derived from CMovingObject.

	These objects are controlled through scripts.  Script commands are
	sent through CallMemberFunction( ).  See all available commands by
	looking at CMovingObject::CallMemberFunction().
*/

// start autoduck documentation
// @DOC movingobject
// @module movingobject | None
// @subindex Scripting Database
// @index script | movingobject

#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 

#include 
#include 
#include 
#include 

#include  // for AddDebugLine( )
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

namespace Obj
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovingObject::DrawBoundingBox( SBBox *pBox, Mth::Vector *pOffset, int numFrames, Mth::Vector *pRot )
{
    Gfx::AddDebugBox( m_matrix, m_pos, pBox, pOffset, numFrames, pRot);
} 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMovingObject::CMovingObject()
{
	// set up default bounding box
    m_bbox.m_max.Set(10.0f, 10.0f, 10.0f);
    m_bbox.m_min.Set(-10.0f, -10.0f, -10.0f);    
    m_bbox.centerOfGravity.Set();			// zero center of gravity......    

	// suspend component should come first, because it
	// controls whether the rest of the components
	// should be updated
	CSuspendComponent* p_suspendComponent = new CSuspendComponent;
	AddComponent(p_suspendComponent);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMovingObject::~CMovingObject( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovingObject::MovingObjectCreateComponents()
{
	CMotionComponent* p_motionComponent = new CMotionComponent;
	AddComponent(p_motionComponent);

	CSpecialItemComponent* p_specialItemComponent = new CSpecialItemComponent;
	AddComponent(p_specialItemComponent);

	CSkaterProximityComponent* p_skaterProximityComponent = new CSkaterProximityComponent;
	AddComponent( p_skaterProximityComponent );

	CSoundComponent* p_soundComponent = new CSoundComponent;
	AddComponent(p_soundComponent);
	
	CStreamComponent* p_streamComponent = new CStreamComponent;
	AddComponent(p_streamComponent);
	
    CLockObjComponent* p_lockObjComponent = new CLockObjComponent;
	AddComponent(p_lockObjComponent);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovingObject::MovingObjectInit( Script::CStruct* pNodeData, CGeneralManager* p_obj_man )
{
	// Dan: a hack for now to detect whether anyone has called CreateComponents() yet or not
	// once components are created in a consistent manner across object types, we can remove this sort of foolishness
	if (!GetMotionComponent())
	{
		MovingObjectCreateComponents();
	}
	
	Dbg_MsgAssert( GetMotionComponent(), ( "Motion component doesn't exist yet" ) );
	
	GetMotionComponent()->OrientToNode( pNodeData );
	
	//----------------------------------------
	// MOVED FROM POSOBJECT.H
	Dbg_MsgAssert( pNodeData, ( "Couldn't find node structure." ));
	
	// Get the checksum ...
	uint32	NodeNameChecksum = 0;
	pNodeData->GetChecksum(CRCD(0xa1dc81f9,"Name"),&NodeNameChecksum);
	if (NodeNameChecksum)
	{
		SetID(NodeNameChecksum);
	}	
	
	uint32 AIScriptChecksum=0;
	if ( pNodeData->GetChecksum(CRCD(0x2ca8a299,"TriggerScript"),&AIScriptChecksum) )
	{
		Script::CScriptStructure *pScriptParams=NULL;
		pNodeData->GetStructure(CRCD(0x7031f10c,"Params"),&pScriptParams);
		SwitchScript( AIScriptChecksum, pScriptParams );
	}
	//----------------------------------------
	
	GetMotionComponent()->InitFromStructure( pNodeData );
	GetSuspendComponent()->InitFromStructure( pNodeData );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::CSkeleton* CMovingObject::GetSkeleton()
{
	Obj::CSkeletonComponent* p_skeleton_component = GetSkeletonComponent();
	if ( p_skeleton_component )
	{
		return p_skeleton_component->GetSkeleton();
	}

    return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Nx::CModel* CMovingObject::GetModel()
{
	Obj::CModelComponent* p_model_component = GetModelComponent();
    if ( p_model_component )
    {
        return p_model_component->GetModel();
    }
    
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovingObject::LookAtObject_Init( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 typeChecksum;
	if ( pParams->GetChecksum( 0x7321a8d6, &typeChecksum ) ) // "type"
	{
		int type;
		switch ( typeChecksum )
		{
			case 0x88c21962: // car
				type = SKATE_TYPE_CAR;
				break;
			case 0x61a741e: // ped
				type = SKATE_TYPE_PED;
				break;
			case 0x5b8ab877: // skater
				type = SKATE_TYPE_SKATER;
				break;
			case 0x3b5737a6:  // gameobj
				type = SKATE_TYPE_GAME_OBJ;
				break;
			default:
				Dbg_MsgAssert( 0,( "\n%s\nUnknown type %s", pScript->GetScriptInfo( ), Script::FindChecksumName( typeChecksum ) ));
				return false;
				break;
		}
		CCompositeObject* pClosestObject = GetClosestObjectOfType( type );
		if ( !pClosestObject )
		{
			Dbg_Message( "\n%s\nWarning:  Looking at object that doesn't exist.", pScript->GetScriptInfo( ) );
			return false;
		}
		return GetMotionComponent()->LookAt_Init( pParams, pClosestObject->m_pos );
	}
	
	// find a named object:
	uint32 nameChecksum;
	if ( pParams->GetChecksum( 0xa1dc81f9, &nameChecksum ) ) // "name"
	{
		CCompositeObject* pNext;
		CCompositeObject* pObj;
		Lst::Search< CObject >	sh;
		Dbg_MsgAssert(mp_manager,("NULL mp_Manager in MovingObject"));
		pNext = (Obj::CCompositeObject*) sh.FirstItem( mp_manager->GetRefObjectList() );
		while ( pNext )
		{
			Dbg_AssertType( pNext, CCompositeObject );
			pObj = pNext;
			pNext = (Obj::CCompositeObject*) sh.NextItem();
			// find objects spawned from this node, return true if out of this radius...
			if ( ( pObj != this ) && ( pObj->GetID() == nameChecksum ) )
			{
				return GetMotionComponent()->LookAt_Init( pParams, pObj->m_pos );
			}
		}
		Dbg_Message( "\n%s\nWarning:  Looking at object that doesn't exist.", pScript->GetScriptInfo( ) );
		return false;
	}
	Dbg_Message( "\n%s\nWarning: No object specified for Obj_LookAtObject", pScript->GetScriptInfo( ) );
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovingObject::ObjectFromNodeWithinRange( int nodeIndex, int radiusSqr, SiteBox* pBox )
{
	uint32	nodeNameChecksum = SkateScript::GetNodeNameChecksum(nodeIndex);

// TODO:  Since there can only be one object from a node, then should be able to get it directly
	
	bool retVal = false;
	CMovingObject* pNext;
	CMovingObject* pObj;
	Lst::Search< CObject >	sh;
//	Dbg_MsgAssert(mp_manager,("NULL mp_Manager in MovingObject. Node Checksum = 0x%x (%s)",GetID(),Script::FindChecksumName(GetID())));
	Dbg_MsgAssert(mp_manager,("NULL mp_Manager in MovingObject. Node Checksum = 0x%x (%s)",GetID(),Script::FindChecksumName(GetID())));
	pNext = (Obj::CMovingObject *) sh.FirstItem( mp_manager->GetRefObjectList() );
	while ( pNext )
	{
		Dbg_AssertType( pNext, CMovingObject );
		pObj = (Obj::CMovingObject *) pNext;
		pNext = (Obj::CMovingObject *) sh.NextItem();
		
		if ( ( pObj->GetID() == nodeNameChecksum ) && ( pObj != this ) )
		{
			if ( pBox )
			{
				if ( ObjInSiteBox( pObj, pBox ) )
				{
					retVal = true;
				}
			}
			else if ( Mth::DistanceSqr( pObj->m_pos, m_pos ) < radiusSqr )
			{
				retVal = true;
			}
		}
	}
	return ( retVal );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CMovingObject::GetSiteBoxMaxRadiusSquared( SiteBox* pBox )
{
	float maxBoxDist;
	if ( pBox->maxDistInitialized )
	{
		return ( pBox->maxDistSquared );
	}
	maxBoxDist = fabsf( pBox->dist );
	if ( fabsf( pBox->height ) > maxBoxDist )
	{
		maxBoxDist = fabsf( pBox->height );
	}
	if ( fabsf( pBox->width ) > maxBoxDist )
	{
		maxBoxDist = fabsf( pBox->width );
	}

	float maxOffset;
	Mth::Vector* pOffset;
	pOffset = &pBox->bbox.centerOfGravity;
	maxOffset = fabsf( pOffset->GetZ( ) );
	if ( fabsf( pOffset->GetY( ) ) > maxOffset )
	{
		maxOffset = fabsf( pOffset->GetY( ) );
	}
	if ( fabsf( pOffset->GetX( ) ) > maxOffset )
	{
		maxOffset = fabsf( pOffset->GetX( ) );
	}

	pBox->maxDistSquared = maxOffset + maxBoxDist;
	pBox->maxDistSquared *= pBox->maxDistSquared;
	pBox->maxDistInitialized = true;
	return ( pBox->maxDistSquared );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovingObject::InitializeSiteBox( SiteBox* pBox )
{
	if ( pBox->initialized )
	{
		// already initialized!
		return;
	}
	
	pBox->matrix = m_matrix;
	if ( pBox->angle )
	{
		pBox->matrix = pBox->matrix.RotateYLocal( pBox->angle );
	}
	pBox->matrix.Invert( );
	
	SBBox* pBBox = &pBox->bbox;
	// start by setting min/max to half the dimentions:
	pBBox->m_max.Set( pBox->width / 2.0f, pBox->height / 2.0f, pBox->dist );
	pBBox->m_min = -pBBox->m_max;
	pBBox->m_min[ Z ] = 0.0f;

	// using the vector in pBox->bbox.centerOfGravity to store offset
	// sent by the player! MISNOMER:
	pBBox->m_max += pBBox->centerOfGravity;
	pBBox->m_min += pBBox->centerOfGravity;
		
	// gots ta move the site box up so that it comes from the object's
	// center of gravity (as the object's position isn't usually the center
	// of gravity):
/*	if ( mp_model )
	{
		//Mth::Vector temp3;
		//temp3 = m_pos + centerOfGravity;		
		//Gfx::AddDebugLine( m_pos, temp3 );
		pBBox->m_max += m_bbox.centerOfGravity;
		pBBox->m_min += m_bbox.centerOfGravity;
	}*/

#ifndef __PLAT_NGC__	
#ifdef __NOPT_ASSERT__	
	if ( pBox->debug )
	{
		pBBox->centerOfGravity.Set( ); // zero that out for the render...
		Mth::Vector drawOffset;
//		drawOffset.Set( 0, m_ground_offset, 0 );
		drawOffset.Set( 0, 0, 0 );
		if ( pBox->angle )
		{
			Mth::Vector angles;
			angles.Set( 0, pBox->angle, 0 );
			DrawBoundingBox( pBBox, &drawOffset, 10, &angles );
		}
		else
		{
			DrawBoundingBox( pBBox, &drawOffset, 10 );
		}
	}
#endif
#endif
	
	// Phew... now we can quickly check objects by rotating their position
	// into our world, and seeing if they're in the motherfuckin' box:
	pBox->initialized = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovingObject::ObjInSiteBox( CMovingObject* pObj, SiteBox* pBox )
{
	// K: There was a bug whereby the initial distance check below was not taking
	// into account the offset of the box.
	// The offset is stored in centerOfGravity which may get zeroed in InitializeSiteBox,
	// so store it here.
	Mth::Vector off=pBox->bbox.centerOfGravity;
	
	// initialize the site box and the BBox contained therein:
	InitializeSiteBox( pBox );

	// sorta trivial radius check and return...
	float distSquared;
	distSquared = Mth::DistanceSqr( pObj->m_pos+off, m_pos );
	float boxRadiusSquared = GetSiteBoxMaxRadiusSquared( pBox );
	if ( distSquared > boxRadiusSquared )
	{
		return false;
	}

	Mth::Vector objPos;
	objPos = pObj->m_pos;
	
	// center of the object...
	objPos[ Y ] += ( pObj->m_bbox.m_max[ Y ] - pObj->m_bbox.m_min[ Y ] ) / 2.0f;

#ifndef __PLAT_NGC__	
#ifdef __NOPT_ASSERT__
	Mth::Vector preTransform = objPos;
#endif	
#endif	

	objPos -= m_pos;
	// now we have the head and toes at the right distance from us and from each other...
	// rotate them into our world:
	objPos = pBox->matrix.Transform( objPos );
	
#ifndef __PLAT_NGC__	
#ifdef __NOPT_ASSERT__
	if ( pBox->debug )
	{
		if ( Gfx::PointInsideBox( objPos, pBox->bbox.m_max, pBox->bbox.m_min ) )
		{
			Gfx::AddDebugLine( m_pos, preTransform, MAKE_RGB( 150, 50, 50 ), MAKE_RGB( 150, 50, 50 ), 10 );
			return true;
		}
		else
		{
			Gfx::AddDebugLine( m_pos, preTransform, MAKE_RGB( 50, 50, 100 ), MAKE_RGB( 50, 50, 100 ), 10 );
			return false;
		}
	}
#endif
#endif
	return ( Gfx::PointInsideBox( objPos, pBox->bbox.m_max, pBox->bbox.m_min ) );
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovingObject::FillInSiteBox(SiteBox* p_siteBox, Script::CStruct* pParams)
{
	Dbg_MsgAssert(p_siteBox,("NULL p_siteBox"));
	Dbg_MsgAssert(pParams,("NULL pParams"));
	
	p_siteBox->initialized = false;
	p_siteBox->angle = 0;
	p_siteBox->maxDistInitialized = false;
	
	// Misnomer: used for offsetting the site box...
	// But we will want it to default to the center of gravity:
	p_siteBox->bbox.centerOfGravity = m_bbox.centerOfGravity;
	
    p_siteBox->height = p_siteBox->width = p_siteBox->dist = 0.0f;
	
	Mth::Vector offset;
	offset[W]=0.0f; //
	if ( pParams->GetVector( 0xa6f5352f, &offset ) ) // offset
	{
		offset.FeetToInches( );
		p_siteBox->bbox.centerOfGravity += offset;
	}

	if ( pParams->GetFloat( 0xff7ebaf6, &p_siteBox->angle ) ) // angle
	{
		p_siteBox->angle = DEGREES_TO_RADIANS( p_siteBox->angle );
	}

	if ( pParams->GetFloat( 0xab21af0, &p_siteBox->height ) ) // height
	{
		p_siteBox->height = FEET_TO_INCHES( p_siteBox->height );
	}
	if ( pParams->GetFloat( 0x7e832f08, &p_siteBox->dist ) ) // dist
	{
		p_siteBox->dist = FEET_TO_INCHES( p_siteBox->dist  );
	}
	if ( pParams->GetFloat( 0x73e5bad0, &p_siteBox->width ) ) // width
	{
		p_siteBox->width = FEET_TO_INCHES( p_siteBox->width );
	}

	#ifdef	__NOPT_ASSERT__	
	p_siteBox->debug = 0;

	p_siteBox->debug = pParams->ContainsFlag( 0x935ab858 ); // debug
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovingObject::ObjectWithinRect( Script::CStruct* pParams, Script::CScript* pScript )
{
	SiteBox siteBox;
	FillInSiteBox(&siteBox,pParams);
	
	return ( ObjectWithinRange( pParams, pScript, &siteBox ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovingObject::SkaterInRange( SiteBox* pBox, float radiusSqr )
{
	int numSkaters = Mdl::Skate::Instance()->GetNumSkaters( );
	
	for ( int i = 0; i < numSkaters; i++ )
	{
		CMovingObject* pObj = Mdl::Skate::Instance()->GetSkater( i );
		if ( pObj != this )
		{
			if ( pBox )
			{
				if ( ObjInSiteBox( pObj, pBox ) )
				{
					return true;
				}
			}
			else if ( Mth::DistanceSqr( pObj->m_pos, m_pos ) < radiusSqr )
			{
				return true;
			}
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovingObject::ObjTypeInRange( Script::CStruct* pParams, Script::CScript* pScript, float radiusSqr, SiteBox* pBox, uint32 typeChecksum )
{
	
	int type;
	Search objSearch;		
	CMovingObject* pObj;
	switch ( typeChecksum )
	{
		case 0x88c21962: // car
			type = SKATE_TYPE_CAR;
			break;
		case 0x61a741e: // ped
			type = SKATE_TYPE_PED;
			break;
		case 0x5b8ab877: // skater
			return ( SkaterInRange( pBox, radiusSqr ) );
			break;
		case 0x3b5737a6: // gameobj
			type = SKATE_TYPE_GAME_OBJ;
			break;
		default:
			Dbg_MsgAssert( "\n%s\nUnknown type %s",((char *)( pScript->GetScriptInfo( ), Script::FindChecksumName( typeChecksum ) )));
			return false;
			break;
	}
	Dbg_MsgAssert(mp_manager,("NULL mp_Manager in MovingObject"));
	pObj = (Obj::CMovingObject *) objSearch.FindFirstObjectOfType( mp_manager->GetRefObjectList(), type );
	while ( pObj )
	{
		if ( pObj != this )
		{
			if ( pBox )
			{
				if ( ObjInSiteBox( pObj, pBox ) )
				{
					return true;
				}
			}
			else if ( Mth::DistanceSqr( pObj->m_pos, m_pos ) < radiusSqr )
			{
				return true;
			}
		}
		pObj = (Obj::CMovingObject *) objSearch.FindNextObjectOfType( );
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovingObject::ObjectWithinRange( Script::CStruct* pParams, Script::CScript* pScript, SiteBox* pBox )
{
	float radiusSqr = 0.0f;
	
	if ( !pBox )
	{
		if( !( pParams->GetFloat( 0xc48391a5, &radiusSqr ) ) )//"radius"
		{
			Dbg_MsgAssert( 0,( "\n%s\nNo radius specified in Obj_*InRadius.", pScript->GetScriptInfo( ) ));
			return false;
		}
		radiusSqr = FEET_TO_INCHES( radiusSqr );
		radiusSqr *= radiusSqr;
	}

	Script::CArray* pArray=NULL;
	uint32 typeChecksum;
	if ( pParams->GetChecksum( 0x7321a8d6, &typeChecksum ) ) // "type"
	{
		return ( ObjTypeInRange( pParams, pScript, radiusSqr, pBox, typeChecksum ) );
	}
	else if ( pParams->GetArray( 0x7321a8d6, &pArray, Script::ASSERT ) ) // "type"
	{
		Dbg_MsgAssert( pArray->GetType( ) == ESYMBOLTYPE_NAME,( "\n%s\nObjectWithinRange: Array must be of names",pScript->GetScriptInfo()));
		uint32 i;
		for ( i = 0; i < pArray->GetSize( ); ++i )
		{
			if ( ObjTypeInRange( pParams, pScript, radiusSqr, pBox, pArray->GetChecksum( i ) ) )
			{
				return true;
			}
		}
		return false;		
	}

	// find a named object:
	uint32 nameChecksum;
	if ( pParams->GetChecksum( 0xa1dc81f9, &nameChecksum ) ) // "name"
	{
		int nodeNum = SkateScript::FindNamedNode( nameChecksum );
		return ( ObjectFromNodeWithinRange( nodeNum, radiusSqr, pBox ) );
	}
    const char* pPrefix;
	if ( pParams->GetText( 0x6c4e7971, &pPrefix ) ) // checksum 'prefix'
	{
		// Create with a prefix specified:
		uint16 numNodes = 0;
		const uint16* pMatchingNodes = SkateScript::GetPrefixedNodes( pPrefix, &numNodes );
		int i;
		for ( i = 0; i < numNodes; i++ )
		{
			if ( ObjectFromNodeWithinRange( pMatchingNodes[ i ], radiusSqr, pBox ) )
			{
				//printf("Oof old\n");
				return true;
			}
		}
		return false;
	}
	
//	Dbg_Message( "\n%s\nWarning: Unprocessed radius ( or site box ) check... unrecognized syntax. needs 'type', 'name' or 'prefix'", pScript->GetScriptInfo( ) );
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovingObject::CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript )
{   
	switch (Checksum)
	{				
		// @script | Obj_LookAtObject | 
		// Returns true if the object will rotate itself to point in the required direction. Ie, if
		// the object is already pointing the right way, the command will return false.
        // @parmopt name | lockAxis | | can lock any two axes (LOCK_X, LOCK_Y, LOCK_Z)
        // @parmopt float | time | vel/acceleration | in seconds
        // @parmopt float | speed | 360  | angular velocity in degrees per second
        // @parmopt float | acceleration | 0 | angular acceleration in degrees per second
        // per second
        // @parmopt name | name | | name of object
        // @parmopt name | type | | type of object to look at - will look at closest if 
        // there are several in the world
        case 0x33e04032:  // Obj_LookAtObject
			return LookAtObject_Init( pParams, pScript );
			break;

		case 0x37185d8a:  // Obj_AngleToNearestSkaterGreaterThan
		{
			int type = SKATE_TYPE_SKATER;
			Obj::CCompositeObject* pClosestObject = GetClosestObjectOfType( type );
			if ( !pClosestObject )
			{
				Dbg_Message( "\n%s\nWarning:  Checking angle to skater that doesn't exist.", pScript->GetScriptInfo() );
				return false;
				break;
			}

			float threshold;
			pParams->GetFloat( NONAME, &threshold, Script::ASSERT );
			Dbg_MsgAssert( threshold >= 0, ( "Negative angle passed to Obj_AngleToNearestSkaterGreaterThan" ) );
			threshold = DEGREES_TO_RADIANS( threshold );

			Mth::Vector pathHeading = pClosestObject->m_pos - m_pos;
			float test = Mth::GetAngle( m_matrix, pathHeading, 2, 1 );
			if ( fabs( test ) > threshold )
			{
				return true;
				break;
			}
			return false;
			break;
		}

        // @script | Obj_ObjectInRadius | 
        // @parmopt name | name | | use one of: name, prefix, or type
        // @parmopt name | prefix | | use one of: name, prefix, or type
        // @parmopt name | type | | use one of: name, prefix, or type
        // (type values are skater, ped, car)
        // @parm float | radius | radius in feet
		case 0x67660fe5:  // Obj_ObjectInRadius
			return ( ObjectWithinRange( pParams, pScript ) );
			break;

        // @script | Obj_ObjectInRect | 
        // @parmopt float | angle | 0 | in degrees - default is  
        // along the direction the object is facing
        // @parmopt float | height | (height of object) | 
        // @parmopt float | distance | (length of object) | in feet - 
        // offset by half of the object in the direction determined by the angle
        // @parmopt float | width | (width of object) | in feet
        // @parmopt vector | offset | | in feet - the rectangle by default will be located
        // sticking out of the XZ plane (or XY plane in Max coords ),
        // rotated by number of degrees in angle parameter
		// @flag debug | just a parameter to turn on debugging info, 
        // to quickly set up rectangles on an object.
        // @parmopt name | name | | use one of: name, prefix, or type
        // @parmopt name | prefix | | use one of: name, prefix, or type
        // @parmopt name | type | | use one of: name, prefix, or type
        // (type values are skater, ped, car)
        case 0x6520b902: // Obj_ObjectInRect
		{
			return ( ObjectWithinRect( pParams, pScript ) );
			break;
		}

        // @script | Obj_ObjectNotInRect | 
        // @parmopt float | angle | 0 | in degrees - default is  
        // along the direction the object is facing
        // @parmopt float | height | (height of object) | 
        // @parmopt float | distance | (length of object) | in feet - 
        // offset by half of the object in the direction determined by the angle
        // @parmopt float | width | (width of object) | in feet
        // @parmopt vector | offset | | in feet - the rectangle by default will be located
        // sticking out of the XZ plane (or XY plane in Max coords ),
        // rotated by number of degrees in angle parameter
        // @flag debug | just a parameter to turn on debugging info, 
        // to quickly set up rectangles on an object.
        // @parmopt name | name | | use one of: name, prefix, or type
        // @parmopt name | prefix | | use one of: name, prefix, or type
        // @parmopt name | type | | use one of: name, prefix, or type
        // (type values are skater, ped, car)
		case 0x3b6b66c3: // Obj_ObjectNotInRect
			return ( !ObjectWithinRect( pParams, pScript ) );
			break;


		// @script | Obj_SetBodyShape | Sets the body shape for this object
        // @parm structure | body_shape | a list of per-bone scales (see scaling.q for format)
		case 0x802a7600: // Obj_SetBodyShape
		{
			Script::CStruct* pBodyShapeStructure;
			pParams->GetStructure( CRCD(0x812684ef,"body_shape"), &pBodyShapeStructure, true );

			if ( GetModel() )
			{
				Gfx::CSkeleton* pSkeleton = GetSkeleton();
				Dbg_Assert( pSkeleton );
				pSkeleton->ApplyBoneScale( pBodyShapeStructure );
			
				Mth::Vector theScale( 1.0f, 1.0f, 1.0f );
				if ( Gfx::GetScaleFromParams( &theScale, pBodyShapeStructure ) )
				{
					GetModel()->SetScale( theScale );
				}
			}			
		}
		break;

		default:
			return ( CCompositeObject::CallMemberFunction( Checksum, pParams, pScript ) );
			break;
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCompositeObject* CMovingObject::GetClosestObjectOfType( int type )
{
	Search objSearch;
	CCompositeObject *pObj;		
	pObj = (CCompositeObject *) objSearch.FindFirstObjectOfType( mp_manager->GetRefObjectList(), type );
	if ( pObj == this )
	{
		if ( !( pObj = (CCompositeObject*) objSearch.FindNextObjectOfType( ) ) )
		{
			return ( NULL );
		}
	}
	if ( !( pObj ) )
	{
		return ( NULL );
	}
	float closestDist = FEET_TO_INCHES( 666.0f * 666.0f );
	float dist;
	CCompositeObject* pClosestObj = pObj;
	while ( pObj )
	{
		dist = Mth::Distance( pObj->m_pos, m_pos );
		if ( ( pObj != this ) && ( dist < closestDist ) )
		{
			closestDist = dist;
			pClosestObj = pObj;
		}
		pObj = (CCompositeObject*) objSearch.FindNextObjectOfType( );
	}
	return ( pClosestObj );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj


================================================
FILE: Code/Sk/Objects/MovingObject.h
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       MovingObject.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/25/2000
//****************************************************************************

#ifndef __OBJECTS_MOVINGOBJECT_H
#define __OBJECTS_MOVINGOBJECT_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 
#include 

#include 

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

namespace Gfx
{
	class 	CSkeleton;
	class 	CModelAppearance;
};
											  
namespace Nx
{
    class 	CModel;
	class 	CCollObj;
};

namespace Obj
{
    class 	CFollowOb;
	class	CRailManager;
	class	CObjectHookManager;
    
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

struct SiteBox
{
	Mth::Matrix     matrix;
	float           height;
	float           dist;
	float           width;
	float           angle;
	SBBox           bbox;
	bool            initialized;
	bool            maxDistInitialized;
	float           maxDistSquared;

#ifdef __NOPT_ASSERT__	
	bool            debug;
#endif
};

class CMovingObject : public CCompositeObject 
{
public :
		CMovingObject();
		virtual ~CMovingObject( void );

public:
		void					MovingObjectCreateComponents();
		void 					MovingObjectInit( Script::CStruct* pNodeData, CGeneralManager* p_obj_man );

		virtual bool			CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript );

		Gfx::CSkeleton*			GetSkeleton( void );
		Nx::CModel*				GetModel( void );
				
protected:
		void					FillInSiteBox(SiteBox *p_siteBox, Script::CStruct *pParams);
		bool					ObjInSiteBox( CMovingObject *pObj, SiteBox *pBox );

public:
//		bool					IsFlipped( void );
		void					GetHoverOrgPos( Mth::Vector *p_orgPos );
		
protected:
		bool					ObjectWithinRange( Script::CStruct *pParams, Script::CScript *pScript, SiteBox *pBox = NULL );
		bool 					ObjectWithinRect( Script::CStruct *pParams, Script::CScript *pScript );
		bool					ObjectFromNodeWithinRange( int nodeIndex, int radiusSqr, SiteBox *pBox );
		void					InitializeSiteBox( SiteBox *pBox );
		float					GetSiteBoxMaxRadiusSquared( SiteBox *pBox );
		bool					ObjTypeInRange( Script::CStruct *pParams, Script::CScript *pScript, float radiusSqr, SiteBox *pBox, uint32 typeChecksum );
		bool					SkaterInRange( SiteBox *pBox, float radiusSqr );
		
protected:
		void					DrawBoundingBox( SBBox *pBox, Mth::Vector *pOffset = NULL, int numFrames = 1, Mth::Vector *pRot = NULL );
		
		void					ToggleFlipState( void );

protected:
		// float			   		m_time;					// time since last frame
  
		// the rest of the flags stay here...
//	    int						m_general_flags;
		
		// will add rotation vector (or rotation class?) to this ASAP...

private:		
		bool					LookAtObject_Init( Script::CStruct *pParams, Script::CScript *pScript );
		void					FollowLeader_Init( Script::CStruct *pParams );

		// GJ:  MOVED THESE FROM POSOBJECT.H
public:
		// Currently used by the PauseSkaters script command, which is used to pause all the skaters whilst
		// a camera animation is running.
		virtual void 			Pause() {mPaused=true; CCompositeObject::Pause(true);}
		virtual void 			UnPause() {mPaused=false; CCompositeObject::Pause(false);}
		bool		 			IsPaused() {return mPaused;}

		SBBox					m_bbox;

	protected:
		CCompositeObject*		GetClosestObjectOfType( int type );

	public :
		// Whether the object is paused.
		bool					mPaused;
};
							  
/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_MOVINGOBJECT_H


================================================
FILE: Code/Sk/Objects/PathMan.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace Obj
{

CPathObjectTracker *CPathMan::TrackObject(CCompositeObject *p_ob, int node_number)
{
	Script::CStruct *p_node=SkateScript::GetNode(node_number);
	#ifdef __NOPT_ASSERT__
	Dbg_MsgAssert(p_node,("NULL p_node for node %d",node_number));
	#endif
	
	int path_number=0;
	if (!p_node->GetInteger("PathNum",&path_number))
	{
		Dbg_MsgAssert(0,("Node %d does not contain a PathNum",node_number));
	}
		
	Dbg_MsgAssert(path_number>=0 && path_numberGetInteger("PathNum",&path_number))
	{
		Dbg_MsgAssert(0,("Node %d does not contain a PathNum",node_number));
	}
		
	// Dbg_MsgAssert(path_number>=0 && path_numbermNodeNameChecksum));

	for (int i=0; iGetSize();
	for ( uint16 i = 0; i < array_size; ++i )
	{
		Script::CStruct *p_start_node=p_node_array->GetStructure( i );
		
		// Rail nodes are linked into a path, but don't need path numbers, so don't bother
		// adding them to save memory.
		uint32 class_type=0;
		p_start_node->GetChecksum( CRCD(0x12b4e660,"Class"), &class_type );
		if ( class_type == CRCD( 0x8e6b02ad, "RailNode" ) ||
			 class_type == CRCD(0x30c19600, "ClimbingNode") )
		{
			continue;
		}

		// make sure this isn't a pedai waypoint
		uint32 type;
		if ( class_type == CRCD( 0x4c23a77e, "Waypoint" )
			 && p_start_node->GetChecksum( CRCD( 0x7321a8d6, "type" ), &type, Script::NO_ASSERT )
			 && type == CRCD( 0xcba10ffa, "PedAI" ) )
		{
			continue;
		}
		
		int num_links=SkateScript::GetNumLinks( p_start_node );
		int node_path_number = 0;
		// Check if the node is linked to something, and has not already had a PathNum
		// given to it.
		if ( ( num_links || class_type == CRCD( 0x4c23a77e, "WayPoint" ) ) && 
			!p_start_node->ContainsComponentNamed( CRCD( 0x9c91c3ca, "PathNum" ) ) )
		{
			// printf("found a non PedAI waypoint\n");
			// The node and subsequent nodes that it is linked to require a PathNum				
			Script::CStruct *p_node=p_start_node;
			
			// The path number that is going to be written in is m_num_paths to start with,
			// but that might change if we hit a node that already has a path number. In that
			// case we'll rewind and write that number in instead.
			int path_number_to_write=m_num_paths;
			
			while (true)
			{
				if (p_node->GetInteger( CRCD( 0x9c91c3ca, "PathNum" ),&node_path_number))
				{
					// We've hit a node that has already had a PathNum written in.
					
					if (node_path_number==path_number_to_write)
					{
						// The path num matches what we started with, so must have looped
						// back to the start, so finished.
						break;
					}	
					
					// We've hit a node that has a different PathNum. So we need to rewind
					// back to where we started and write in that path number instead.
					// This happens due to the fact that the nodes could be in any order in
					// the node array, so paths can start being traversed from any point, not
					// necessarily the start.
					path_number_to_write=node_path_number;
					p_node=p_start_node;
				}
				
				// Wack in the path number
				p_node->AddInteger(0x9c91c3ca/*PathNum*/,path_number_to_write);
				
				// Check if this is a terminating node of the path, and stop if so.
				int num_links=SkateScript::GetNumLinks(p_node);
				if (num_links==0)
				{
					break;
				}	
				
				// On to the next node. Note the if the path branches, it will only
				// follow the first branch. The other branches will end up getting
				// their own path numbers. Don't know if this will be a problem yet.
				p_node=p_node_array->GetStructure(SkateScript::GetLink( p_node, 0 ));
			}
			
			// If path_number got used, increment it ready for the next path.
			if (path_number_to_write==m_num_paths)
			{
				++m_num_paths;
				Dbg_MsgAssert(m_num_pathsAllocateObjectTrackerMemory();
	return true;
}

} // namespace Obj



================================================
FILE: Code/Sk/Objects/PathMan.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Object (OBJ)											**
**																			**
**	File name:		objects/pathman.h    									**
**																			**
**	Created: 		5/08/02	-	ksh											**
**																			**
*****************************************************************************/

#ifndef __OBJECTS_PATHMAN_H
#define __OBJECTS_PATHMAN_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

#ifndef __GEL_OBJPTR_H
#include 
#endif

#include 
#include 

namespace Obj
{
class CCompositeObject;

class CPathObjectTracker
{
public:	
enum
{
	MAX_OBJECTS_PER_PATH=50
};	
private:
	CSmtPtr mpp_objects[MAX_OBJECTS_PER_PATH];
	
public:	
	CPathObjectTracker();
	~CPathObjectTracker();
	void Clear();
	void AddObjectPointer(CCompositeObject *p_ob);
	void StopTrackingObject(CCompositeObject *p_ob);

	const CSmtPtr *GetObjectList() {return mpp_objects;}
};

class CPathMan
{
public:
	static CPathMan *Instance();
	void RecursivelyAddPathInfo( int path_number_to_write, int node_number, int start_node_number, int recursion_depth = 0 );
protected:
	CPathMan();
private:
	static CPathMan *mp_instance;		
	
	#define MAX_PATHS 300
	CPathObjectTracker* mp_path_object_trackers;
	CPathObjectTracker mp_ped_object_tracker;
	int m_num_paths;
	
public:
	void AddPathInfoToNodeArray();
	void ClearPathObjectTrackers();

	void DeallocateObjectTrackerMemory();
	void AllocateObjectTrackerMemory();
	
	CPathObjectTracker *TrackObject(CCompositeObject *p_ob, int node_number);
	CPathObjectTracker *TrackPed(CCompositeObject *p_ob, int node_number);
};

//char foo[sizeof(CPathMan)/0];
bool ScriptAllocatePathManMemory( Script::CStruct* pParams, Script::CScript* pScript );

} // namespace Obj

#endif



================================================
FILE: Code/Sk/Objects/PathOb.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       PathOb.cpp
//* OWNER:          Matt Duncan
//* CREATION DATE:  11/2/2000
//****************************************************************************

/*
	Pathob.cpp
	
	This module contains path-navigation functionality.

	A moving object contains a pointer to this pathob.  When
	the moving object wants to follow a path along waypoints, it
	just initializes this pathob with the current position and tells
	the pathob where it wants to go.  Each frame, the moving object
	calls TraversePath() and sends in the distance travelled, and
	its own heading.  The pathob moves accordingly, but the point remains
	on the plane defined by the points of the path.

	The pathob is its own entity: it's essentially a point in
	space and an orientation.  When the moving object calls TraversePath(),
	the pathob just does the math and figures out where along the path
	to move to.  The moving object takes the new path-ob position, finds
	the ground above or below this position, and possibly sets its heading
	so that it's looking the correct direction down the path.

   	The pathob logic handles a simple path between two points... it also
	handles the curves from one simple path to the next (so the path along
	three waypoints, but instead of a sharp turn the pathob follows a curve
	between the two lines).

	As the pathob hits waypoints, it communicates to the moving object (in
	case the moving object needs to run a script at the waypoint).
*/

#include 

#include 

#include 
#include 
#include 
#include 
#include 

#include 

#include 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

// PATHOB flags:
#define PATHOBFLAG_LINEAR_INTERP			( 1 << 0 )
#define PATHOBFLAG_END_OF_PATH				( 1 << 1 )
#define PATHOBFLAG_SEND_HIT_WAYPOINT_MSG	( 1 << 2 )

#define MAX_RATIO_OF_HYPOTENUSE_TO_OPPOSITE_FOR_TURN_CIRCLE    6

// NOTE: if you change this enum, make sure to update the GetDebugInfo func
enum
{
	PATHOB_STATE_UNINITIALIZED,
	PATHOB_STATE_FOLLOW_LINE,
	PATHOB_STATE_FOLLOW_CURVE,
	PATHOB_STATE_LINEAR_INTERP,
	PATHOB_STATE_IDLE,
};

enum
{
	TURN_METHOD_LINEAR_INTERP,
	TURN_METHOD_CURVE,
	TURN_METHOD_INSTANT_TURN,
};

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPathOb::CPathOb( CCompositeObject* pObj )
{
	mp_object = pObj;
	m_turn_matrix.Ident();
	
	m_wp_pos_from.Set();
	m_wp_pos_next.Set();
	m_wp_pos_to.Set();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPathOb::~CPathOb( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPathOb::Busy( void )
{
	switch ( m_pathob_state )
	{
		case PATHOB_STATE_UNINITIALIZED:
		case PATHOB_STATE_IDLE:
			return false;
		default:
			return true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPathOb::SetUpNavPointLineMotion( void )
{
	// assumes we're already on the line.
	
	// pre-calculations for navigating between the waypoints...
	
	Mth::Vector vect_first_to_pos = m_nav_pos - m_wp_pos_from;
	m_dist_traveled = vect_first_to_pos.Length();
			  
	Mth::Vector vect_first_to_second = m_wp_pos_from - m_wp_pos_to;
	m_dist_to_wp = vect_first_to_second.Length();
    
    m_dist_traveled += m_extra_dist;
	m_extra_dist = 0;
	
	/* Shouldn't need this, now that curve code is independent of turn dist
    if ( m_dist_to_wp < m_current_turn_dist )
	{
		Dbg_MsgAssert( 0,( "Waypoints %d and %d too close or turn radius too large.", m_node_from, m_node_to ));
	}*/
	
	// set up the heading:
	m_turn_matrix[ Z ] = m_wp_pos_to - m_wp_pos_from;
	m_turn_matrix[ Z ].Normalize();
	
	SetUpTurnDist();
	
	return( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CPathOb::SetUpTurningCircle( void )
{
	Mth::Vector vectA, vectB;
	
	// m_nav_pos holds one point on the circle.
	// on the line from m_wp_pos_from to m_wp_pos_to
	// (at the distance of nav pos from m_wp_pos_from)
	// will be the other point on the circle.
	vectB = m_nav_pos - m_wp_pos_from;

	float actualTurnDist = vectB.Length();
						
	m_circle_arrival_pos = m_wp_pos_from;
	vectA = m_wp_pos_to - m_wp_pos_from;
	float distBetweenWps = vectA.Length();
    vectA *= ( actualTurnDist / distBetweenWps );
	m_circle_arrival_pos += vectA;

	// That's all the info from the turning circle we need, if we're doing a linear interpolation:
	if ( m_pathob_flags & PATHOBFLAG_LINEAR_INTERP )
	{
		return TURN_METHOD_LINEAR_INTERP;
	}
	
//	Dbg_MsgAssert( actualTurnDist, "Traveling too fast for turn dist... increase turn dist or decrease velocity." );
	if ( !actualTurnDist )
	{
		return TURN_METHOD_INSTANT_TURN;
	}
	
	// find the radius of the turning circle
	// the angle between the line we're on and the line to
	// the arrival pos on the next line is the same as half
	// the angle that the two radii where the circle touches
	// the waypoint lines form...  use the similar triangles
	// to get the circle radius:
	vectA = m_circle_arrival_pos - m_nav_pos;
	vectA /= 2; // vector from current pos to midpoint along current pos to arrival pos...
	float lengthPosToMidpoint = vectA.Length();

	vectB = m_nav_pos + vectA;
	vectB -= m_wp_pos_from;
	float distWPtoMP = vectB.Length();
	
	if ( ( !distWPtoMP ) | ( ( distWPtoMP * MAX_RATIO_OF_HYPOTENUSE_TO_OPPOSITE_FOR_TURN_CIRCLE ) < lengthPosToMidpoint ) )
	{
		// almost a straight line... use linear interpolation method:
		return TURN_METHOD_LINEAR_INTERP;
	}
	// to understand this, draw the triangles and derive circle radius
	// in terms of the known lengths of the similar triangles:
	m_circle_radius = lengthPosToMidpoint * actualTurnDist;
	m_circle_radius /= distWPtoMP;

	if ( !m_circle_radius )
	{
		// tiny turn radius... snap to the point and shit like that!
		return TURN_METHOD_INSTANT_TURN;
	}
	
	// find the location of the center of the circle...
	// the triangle formed by the current pos, the waypoint pos,
	// and the midpoint between the current pos and the arrival pos
	// is a similar triangle to the current pos, the waypoint pos,
	// and the center of the circle...  therefore the distance from
	// the center of the circle to the waypoint pos can be found:
	float distFromWaypointToCircleCenter = actualTurnDist * actualTurnDist;
	distFromWaypointToCircleCenter /= vectB.Length();
	// take the vector from the waypoint to the midpoint, make it this length,
	// and viola we have the circle center:
	m_circle_center = vectB;
	m_circle_center.Normalize( distFromWaypointToCircleCenter );
	m_circle_center += m_wp_pos_from;

	// test the distance from the current pos... should equal the frickin' radius:
	/*
	{
		Mth::Vector temp = m_circle_center;
		temp -= m_nav_pos;
		Dbg_Message( "dist %f radius %f", temp.Length(), m_circle_radius );
	}*/
	
	if ( m_circle_radius < m_min_turn_circle_radius )
	{
		Dbg_Message( "angle too acute to path between nodes %d and %d", m_node_from, m_node_to );
		Dbg_MsgAssert( 0,( "Turn is too tight for this object...  your waypoints are just too acute for me!" ));
	}

	// find the "rotation axis" (the y axis if we're traveling along the xz plane)
	// and set up a rotation matrix where the axis along the 1st waypoint path [ r0 ]
	// sweeps along the circle as we rotate about the 'up'[ r1 ] axis in the direction if r2...
	m_turn_matrix[ X ] = m_circle_center - m_nav_pos;
	m_turn_matrix[ Z ] = m_circle_center - m_circle_arrival_pos;
	if ( !( m_turn_matrix[ X ].Length() && m_turn_matrix[ Z ].Length() ) )
	{
		return TURN_METHOD_INSTANT_TURN;
	}
	m_turn_matrix[ X ].Normalize();
	m_turn_matrix[ Z ].Normalize();

	// while we have these two vectors, find the angle between them...
	float angCos = Mth::DotProduct( m_turn_matrix[ X ], m_turn_matrix[ Z ] );
#ifdef __PLAT_NGC__
	float	ang = angCos;
	if ( ang > 1.0f ) ang = 1.0f;
	if ( ang < -1.0f ) ang = -1.0f;
	m_arc_ang = acosf( ang );  // angles given in radians!
#else
	m_arc_ang = acosf( angCos );  // angles given in radians!
#endif		// __PLAT_NGC__
	
	// find the total circumference of the arc,
	// which is the circumference of the circle ( ang * radius )
	// multiplied by ang / ( 2 * pi ) [ the fraction of the circle we actually traverse... ]
	m_arc_dist = m_circle_radius * m_arc_ang;
	
	// continue setting up the matrix now...
	m_turn_matrix[ Y ] = Mth::CrossProduct( m_turn_matrix[ X ], m_turn_matrix[ Z ] );
	if ( !m_turn_matrix[ Y ].Length() )
	{
		return TURN_METHOD_INSTANT_TURN;
	}
	m_turn_matrix[ Y ].Normalize();
	m_turn_matrix[ Z ] = Mth::CrossProduct( m_turn_matrix[ X ], m_turn_matrix[ Y ] );
	if ( !m_turn_matrix[ Z ].Length() )
	{
		return TURN_METHOD_INSTANT_TURN;
	}
	m_turn_matrix[ Z ].Normalize();
	
    // Phew!!!  Now we can rotate this matrix along r1 and get the position on
	// the circle by taking r0 multiplied by the circle radius and adding that to
	// the center of the circle... and r2 will be the direction the car is pointed!!
	   
	// now to find the position along the arc, just get the
	// current distance traveled along the arc, divide by
	// the total circumference of the arc, multiply by the
	// angle the arc traverses to get the angle currently
	// traversed...  by rotating our curve matrix along
	// y in this fashion, we can find the position on the
	// circle!
	
	m_dist_traveled = m_extra_dist;
	m_extra_dist = 0;
	
//	Gfx::AddDebugLine( m_nav_pos, m_circle_center, MAKE_RGB( 255, 0, 0 ), MAKE_RGB( 255, 0, 0 ) );
//	Gfx::AddDebugLine( m_circle_center, m_circle_arrival_pos, MAKE_RGB( 0, 0, 255 ), MAKE_RGB( 0, 255, 0 ) );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPathOb::UpdateNavPosAlongPath( void )
{	
	Mth::Vector pointAlongPath;
	
	m_nav_pos = m_wp_pos_from;
	pointAlongPath = m_wp_pos_to - m_wp_pos_from;
	pointAlongPath *= ( m_dist_traveled / m_dist_to_wp );
	m_nav_pos += pointAlongPath;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPathOb::UpdateNavPosAlongCurve( float distTraveled )
{	
	float fractionOfTotal;
	fractionOfTotal = distTraveled / m_arc_dist;

	m_y_rot = m_arc_ang * fractionOfTotal;
	m_turn_matrix.RotateYLocal( m_y_rot );

	Mth::Vector temp;
	temp = m_turn_matrix[ Y ];
	if ( temp[ Y ] > 0.0f )
	{
		m_y_rot *= -1.0f;
	}

	m_nav_pos = m_turn_matrix[ X ] * -m_circle_radius;
	m_nav_pos += m_circle_center;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPathOb::AlignToPath( void )
{   
	m_turn_matrix[ Z ] = m_wp_pos_to - m_nav_pos;
	m_turn_matrix[ Z ].Normalize();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// If the distance between the next two waypoints is less than
// the turn dist, decrease the turn dist and shit (otherwise
// the arrival pos will be on the line but PAST the two waypoints
// rather than between them!
bool CPathOb::SetUpTurnDist( void )
{	
	Mth::Vector temp;
	float wpLength;

	if ( m_enter_turn_dist )
	{
		if ( !SkateScript::GetNumLinks( m_node_to ) )
			return false;
        SkateScript::GetPosition( SkateScript::GetLink( m_node_to, 0 ), &temp );
		temp -= m_wp_pos_to;
		// Need to optimise?  Store this length instead of recalculating it
		// when getting next waypoint!
		wpLength = temp.Length();
		if ( wpLength < m_enter_turn_dist )
		{
			m_current_turn_dist = wpLength / 2.0f;
			temp = m_wp_pos_to - m_nav_pos;
			float distToCurrentWP = temp.Length();
			if ( m_current_turn_dist > distToCurrentWP )
			{
				m_current_turn_dist = distToCurrentWP;
			}
			m_pathob_flags |= PATHOBFLAG_LINEAR_INTERP;
			return true;
		}
	}

	m_pathob_flags &= ~PATHOBFLAG_LINEAR_INTERP;

	m_current_turn_dist = m_enter_turn_dist;
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPathOb::NewPath( int nodeNum, const Mth::Vector ¤tPos )
{
	m_pathob_state = PATHOB_STATE_UNINITIALIZED;
	m_pathob_flags = 0;
	m_node_to = nodeNum;
	m_dist_traveled = m_extra_dist = 0;
	m_nav_pos = m_wp_pos_from = currentPos;
	SetUpPath();
	
	while ( Mth::Distance( m_nav_pos, m_wp_pos_to ) < 1.0f )
	{
		if ( !GetNextWaypoint() )
		{
			return;
		}
	}
	
	AlignToPath();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Set up the path according to m_node_from and m_node_to...
// Do this after getting the next waypoint in a path, or when
// beginning a new path.  m_node_from should indicate where we
// are now, m_node_to should be the node index of where we're
// going!
void CPathOb::SetUpPath( void )
{	
	// get data out of waypoints...
	Script::CScriptStructure *pNode = SkateScript::GetNode( m_node_to );
	SkateScript::GetPosition( pNode, &m_wp_pos_to );
	
	if ( !SkateScript::GetNumLinks( m_node_to ) )
	{
		// the path will end on arrival to m_node_to:
		m_pathob_flags |= PATHOBFLAG_END_OF_PATH;
	}
	else
	{
		m_pathob_flags &= ~PATHOBFLAG_END_OF_PATH;
	}

//	Gfx::AddDebugLine( m_wp_pos_from, m_wp_pos_to, MAKE_RGB( 255, 0, 0 ), MAKE_RGB( 0, 0, 255 ) );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPathOb::DrawPath()
{
	Gfx::AddDebugLine( m_wp_pos_from, m_wp_pos_to, MAKE_RGB( 255, 0, 0 ), MAKE_RGB( 0, 0, 255 ) );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPathOb::GetNextWaypoint( void )
{
	
	int numLinks;
	numLinks = SkateScript::GetNumLinks( m_node_to );
	if ( !numLinks )
	{
		return false;
	}
	m_node_from = m_node_to;
	Dbg_Assert( GetMotionComponentFromObject( mp_object ) );
	if ( GetMotionComponentFromObject(mp_object)->PickRandomWaypoint() )
	{
		m_node_to = SkateScript::GetLink( m_node_to, Mth::Rnd( numLinks ) );
	}
	else
	{
		m_node_to = SkateScript::GetLink( m_node_to, 0 );
	}
	m_wp_pos_from = m_wp_pos_to;
	
	SetUpPath();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPathOb::SetHeading( Mth::Vector *p_heading )
{
	// Gets the current heading:	
	*p_heading = m_turn_matrix[ Z ];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

float CPathOb::GetDistToWaypoint( void )
{
	switch ( m_pathob_state )
	{
		case PATHOB_STATE_UNINITIALIZED:
			//Dbg_Message( "%f line %d", HUGE_DISTANCE, __LINE__ );		
			return HUGE_DISTANCE;
			break;
		
		case PATHOB_STATE_FOLLOW_LINE:
			//Dbg_Message( "%f line %d", m_dist_to_wp - m_dist_traveled, __LINE__ );		
			return ( m_dist_to_wp - m_dist_traveled );
			break;
		
		case PATHOB_STATE_FOLLOW_CURVE:
			if ( m_dist_traveled < ( m_arc_dist / 2 ) )
			{
				//Dbg_Message( "%f line %d", ( m_arc_dist / 2 ) - m_dist_traveled, __LINE__ );		
				return ( ( m_arc_dist / 2 ) - m_dist_traveled );
			}
			else
			{
				Mth::Vector temp = m_wp_pos_to - m_wp_pos_from;
				//Dbg_Message( "%f line %d", temp.Length(), __LINE__ );		
				return temp.Length();
			}
			break;
		
		case PATHOB_STATE_LINEAR_INTERP:
			if ( m_dist_traveled < m_dist_to_wp )
			{
				//Dbg_Message( "%f line %d", m_dist_to_wp - m_dist_traveled, __LINE__ );		
				return ( m_dist_to_wp - m_dist_traveled );
			}
			else
			{
				Mth::Vector temp = m_wp_pos_to - m_wp_pos_from;
				//Dbg_Message( "%f line %d", temp.Length(), __LINE__ );		
				return temp.Length();
			}
			break;
		
		default:
			Dbg_MsgAssert( 0, ( "unhandled pathob state" ) );
			break;
	}
	
	return HUGE_DISTANCE;
}

#if FINDING_GLITCHES_IN_PATH_TRAVERSAL
// this function tracks down glitches in the path traversal...
void CPathOb::FindGlitch( void )
{	
	static float totalDist = 0;
	static int numEntries = 0;
	float thisDist = Distance( m_old_nav_pos, m_nav_pos );
	totalDist += thisDist;
	numEntries++;
	float averageDist = totalDist / numEntries;
	if ( numEntries == 100 )
	{
		numEntries = 50;
		totalDist /= 2;
	}
	if ( fabs( thisDist - averageDist ) > 4.0f )
	{
		Gfx::AddDebugLine( m_nav_pos, m_old_nav_pos, MAKE_RGB( 255, 255, 255 ), MAKE_RGB( 255, 255, 255 ) );
		Dbg_Message( "off by %f dist %f state %d prev state %d", thisDist - averageDist, thisDist, m_pathob_state, m_prev_pathob_state );
	}
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPathOb::PrepareTurn( void )
{	
	int retVal = SetUpTurningCircle();
	switch ( retVal )
	{
		case TURN_METHOD_LINEAR_INTERP:
		{
			// store the current pos in circle center:
			// the angle is too wide (too close to PI radians)...
			// just move along the two lines and gradually turn and shit like that...
			
			m_circle_center = m_nav_pos;
			Mth::Vector temp = m_nav_pos - m_wp_pos_from;
			m_dist_to_wp = temp.Length();
			m_dist_traveled = m_extra_dist;
			m_pathob_state = PATHOB_STATE_LINEAR_INTERP;
			break;
		}
		
		case TURN_METHOD_CURVE:
			m_pathob_state = PATHOB_STATE_FOLLOW_CURVE;
			break;
		
		case TURN_METHOD_INSTANT_TURN:
			// just hit the waypoint, and set up the path for the next one:
			Dbg_Assert( GetMotionComponentFromObject( mp_object ) );
			GetMotionComponentFromObject( mp_object )->HitWaypoint( m_node_from );
			m_pathob_flags &= ~PATHOBFLAG_SEND_HIT_WAYPOINT_MSG;
			m_dist_traveled = m_dist_to_wp - m_dist_traveled;
			// now move along the line...
			SetUpNavPointLineMotion();
			UpdateNavPosAlongPath();
			m_pathob_state = PATHOB_STATE_FOLLOW_LINE;
			break;

		default:
			Dbg_MsgAssert( 0,( "Unknown turn method %d", retVal ));
			break;
	}   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Function TraversePath()
// This function is called by the moving object each frame, and causes the
// pathob to move a little further down the path (by the projection of the moving
// object's forward vector along the line or curve in the path).
// Returns 'TRUE' when the end of the path is reached...
//bool CPathOb::TraversePath( bool is_being_skitched, const float dist, Mth::Vector *pHeading )
bool CPathOb::TraversePath( const float dist, Mth::Vector *pHeading )
{
	//if (is_being_skitched) printf("#");	
	
	if ( m_pathob_state == PATHOB_STATE_IDLE )
	{
		return true;
	}
	
#if FINDING_GLITCHES_IN_PATH_TRAVERSAL
	m_old_nav_pos = m_nav_pos;
#endif
	
	float distTraveled;
	if ( pHeading )
	{
		float cosHeadingToNavPath = Mth::DotProduct( *pHeading, m_turn_matrix[ Z ] );
		if ( !cosHeadingToNavPath )
		{
			//if (is_being_skitched) printf("a");	
			// can't assert on this case... car could be pointing at the sky temporarily!
			distTraveled = 1;
		}
		else
		{		
			//if (is_being_skitched) printf("b");	
			distTraveled = dist * cosHeadingToNavPath;
		}
	}
	else
	{
		//if (is_being_skitched) printf("c");	
		distTraveled = dist;
	}	
	
#if FINDING_GLITCHES_IN_PATH_TRAVERSAL
	m_prev_pathob_state = m_pathob_state;
#endif
		
	switch ( m_pathob_state )
	{
		case PATHOB_STATE_UNINITIALIZED:
			//if (is_being_skitched) printf("d");	
			SetUpNavPointLineMotion();
			m_y_rot = 0;
			m_pathob_state = PATHOB_STATE_FOLLOW_LINE;
			// intentional fall thru...
		case PATHOB_STATE_FOLLOW_LINE:
			//if (is_being_skitched) printf("e");	
			// will convert this to GetDistTraveled(), once the
			// nav point stuff is disconnected from the car stuff
			m_dist_traveled += distTraveled;
			
            if ( m_pathob_flags & PATHOBFLAG_END_OF_PATH )
			{
				// see if we're at the end of the path:
				if ( m_dist_traveled >= m_dist_to_wp )
				{
					m_node_from = m_node_to;
					m_nav_pos = m_wp_pos_from = m_wp_pos_to;
					m_pathob_state = PATHOB_STATE_IDLE;
					return true;
				}
			}
			else if ( m_dist_traveled >= ( m_dist_to_wp - m_current_turn_dist ) )
			{
				// we've reached the waypoint... save the extra!
				//m_extra_dist = m_dist_traveled - ( m_dist_to_wp - m_current_turn_dist );
				if ( !m_current_turn_dist )
				{
					m_nav_pos = m_wp_pos_to;
					Dbg_Assert( GetMotionComponentFromObject( mp_object ) );
					GetMotionComponentFromObject( mp_object )->HitWaypoint( m_node_to );
					if ( !GetNextWaypoint() )
					{
						Dbg_MsgAssert( 0,( "Tell Matt that the code got here, and that he's an idiot but you still love him." ));
						return true;
					}
					else
					{
						m_pathob_state = PATHOB_STATE_UNINITIALIZED;
					}
					break;
				}
				
                // get the circle along the turn, using the turn radius...
				// first, set the nav pos equal to the first point on the circle
				// ( the current position... )
				//m_dist_traveled -= m_extra_dist;
				UpdateNavPosAlongPath();
				
				// then get the next waypoint, which will allow us to figure out
				// the second point along the circle...
				GetNextWaypoint();
				m_pathob_flags |= PATHOBFLAG_SEND_HIT_WAYPOINT_MSG;
				PrepareTurn();
				
				break;
			}
			UpdateNavPosAlongPath();
            break;

		case PATHOB_STATE_FOLLOW_CURVE:
			// moving around the circumference of the circle now!!
			{
				//if (is_being_skitched) printf("f");	
				//Mth::Vector oldpos=m_nav_pos;
				
				// will convert this to GetDistTraveled(), once the
				// nav point stuff is disconnected from the car stuff
				m_dist_traveled += distTraveled;
				//if (is_being_skitched) printf("[dt=%.2f, %.2f]",distTraveled,m_dist_traveled);
				
				if ( ( m_pathob_flags & PATHOBFLAG_SEND_HIT_WAYPOINT_MSG ) &&
					( m_dist_traveled >= ( m_arc_dist / 2.0f ) ) )
				{
					m_pathob_flags &= ~PATHOBFLAG_SEND_HIT_WAYPOINT_MSG;
					Dbg_Assert( GetMotionComponentFromObject( mp_object ) );
					GetMotionComponentFromObject( mp_object )->HitWaypoint( m_node_from );
				}
				if ( m_dist_traveled > m_arc_dist )
				{
					
					m_extra_dist = m_dist_traveled - m_arc_dist;
					//if (is_being_skitched) printf("[mdt=%.2f][ad=%.2f][ed=%.2f]",m_dist_traveled,m_arc_dist,m_extra_dist);
					
					// Calculates m_nav_pos. Ignores m_dist_traveled and m_extra_dist
					UpdateNavPosAlongCurve( distTraveled - m_extra_dist );
					//if (is_being_skitched) printf("[cpd=%.2f]",Mth::Distance(m_nav_pos,oldpos));
					
					// now move along the line...
					
					// Ken: Skitch Glitch fix
					// If this is not zeroed there is an increase in speed for one frame when
					// going from this state to PATHOB_STATE_FOLLOW_LINE
					// Ie, the car moves too far in this transition.
					m_extra_dist=0.0f;
					
					// Recalculates m_dist_traveled using m_nav_pos, then adds m_extra_dist, 
					// then zeros m_extra_dist
					SetUpNavPointLineMotion();
					
					// Calculates m_nav_pos using m_dist_traveled
					UpdateNavPosAlongPath();
					
					m_y_rot = 0;
					m_pathob_state = PATHOB_STATE_FOLLOW_LINE;
					
					//if (is_being_skitched) printf("[%.2f]",Mth::Distance(oldpos,m_nav_pos));
					break;
				}
				// find the angular rotation (angle changes relative to
				// total arc angle as the distance changes relative to
				// the total distance...
				//if (is_being_skitched) printf("{dt=%.2f r=%.2f ad=%.2f ang=%.2f}",distTraveled,m_circle_radius,m_arc_dist,m_arc_ang);
				UpdateNavPosAlongCurve( distTraveled );
				//if (is_being_skitched) printf("[%.2f]",Mth::Distance(oldpos,m_nav_pos));
				
				//temp debuggery:
				/*{
					Mth::Vector tempVect;
					tempVect = m_turn_matrix[ 0 ] * 150;
					tempVect += m_circle_center;
					Gfx::AddDebugLine( tempVect, m_circle_center, MAKE_RGB( 255, 0, 0 ), MAKE_RGB( 0, 0, 0 ) );
					tempVect = m_turn_matrix[ 1 ] * 150;
					tempVect += m_circle_center;
					Gfx::AddDebugLine( tempVect, m_circle_center, MAKE_RGB( 0, 255, 0 ), MAKE_RGB( 0, 0, 0 ) );
					tempVect = m_turn_matrix[ 2 ] * 150;
					tempVect += m_circle_center;
					Gfx::AddDebugLine( tempVect, m_circle_center, MAKE_RGB( 0, 0, 255 ), MAKE_RGB( 0, 0, 0 ) );
				}*/
			}
			break;
		
		case PATHOB_STATE_LINEAR_INTERP:
		{
			//if (is_being_skitched) printf("g");	
			// very minimal turn...
			// move along the current line until we get to the next line...
			// gradually change the heading.
			m_dist_traveled += distTraveled;
			float fracTraveled = m_dist_traveled / m_dist_to_wp;
			
			if ( m_dist_traveled < m_dist_to_wp )
			{
				m_nav_pos = m_circle_center;
				Mth::Vector alongPath = m_wp_pos_from - m_circle_center;
				alongPath *= fracTraveled;
				m_nav_pos += alongPath;
			}
			else
			{
				m_nav_pos = m_wp_pos_from;
				Mth::Vector alongPath = m_circle_arrival_pos - m_wp_pos_from;
				alongPath *= ( fracTraveled - 1 ) / 1;
				m_nav_pos += alongPath;
			}
			
			// set the heading, just a weighted average of the two headings...
			Mth::Vector heading1, heading2;
			heading1 = m_wp_pos_from - m_circle_center;
			heading2 = m_circle_arrival_pos - m_wp_pos_from;
			// fraction traveled over both lines...
			fracTraveled /= 2.0f;
			if ( ( fracTraveled ) >= 0.5f && ( m_pathob_flags & PATHOBFLAG_SEND_HIT_WAYPOINT_MSG ) )
			{
				m_pathob_flags &= ~PATHOBFLAG_SEND_HIT_WAYPOINT_MSG;
				Dbg_Assert( GetMotionComponentFromObject( mp_object ) );
				GetMotionComponentFromObject( mp_object )->HitWaypoint( m_node_from );
			}
			if ( fracTraveled >= 1.0f )
			{
				// set all the variables back so we can traverse the next path:
				fracTraveled = 1;
				
				// Ken: Skitch Glitch fix
				//m_extra_dist = m_dist_traveled - ( 2 * m_dist_to_wp );
				m_extra_dist=0.0f;
				
				SetUpNavPointLineMotion();
				//Dbg_Message( "hmm %f\n", m_dist_traveled );
				m_pathob_state = PATHOB_STATE_FOLLOW_LINE;
				//if (is_being_skitched)
				//{
				//	printf("Going from interp back to line\n");	
				//}	
			}
			m_turn_matrix[ Z ] = ( heading1 * ( 1 - fracTraveled ) );
			// by the time we get to the arrival pos, the heading will be along the next line!
			m_turn_matrix[ Z ] += ( heading2 * ( fracTraveled ) );
			m_turn_matrix[ Z ].Normalize();
			break;
		}

		case PATHOB_STATE_IDLE:
			break;
			
		default:
			Dbg_MsgAssert( 0,( "Unknown substate!" ));
			break;
	}
	
#if FINDING_GLITCHES_IN_PATH_TRAVERSAL
	FindGlitch();
#endif
	
#if DRAW_COLOR_CODED_NAV_PATH
	int rgb;
	switch ( m_pathob_state )
	{
		case PATHOB_STATE_LINEAR_INTERP:
			rgb = MAKE_RGB( 255, 0, 0 );
			break;
			
		case PATHOB_STATE_FOLLOW_CURVE:
			rgb = MAKE_RGB( 0, 255, 0 );
			break;
			
		case PATHOB_STATE_FOLLOW_LINE:
			rgb = MAKE_RGB( 0, 0, 255 );
			break;
		
		case PATHOB_STATE_UNINITIALIZED:
			rgb = 255;
			break;
			
		default:
			rgb = MAKE_RGB( 255, 255, 255 );					  
	}
	m_old_nav_pos[ Y ] += 200;
	m_nav_pos[ Y ] += 200;
	Gfx::AddDebugLine( m_old_nav_pos, m_nav_pos, rgb, rgb );
	m_old_nav_pos[ Y ] -= 200;
	m_nav_pos[ Y ] -= 200;
#endif

	//if (is_being_skitched) printf("#");	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPathOb::GetDebugInfo( Script::CStruct* p_info )
{
#ifdef	__DEBUG_CODE__
	Dbg_MsgAssert( p_info, ( "NULL p_info passed to CPathOb::GetDebugInfo" ) );

	p_info->AddVector( "m_nav_pos", m_nav_pos );
	
	// nodes
	p_info->AddInteger( "m_node_from", m_node_from );
	p_info->AddInteger( "m_node_to", m_node_to );
	p_info->AddInteger( "m_node_next", m_node_next );
	p_info->AddStructure( "m_node_from", SkateScript::GetNode( m_node_from ) );
	p_info->AddStructure( "m_node_to", SkateScript::GetNode( m_node_to ) );
	p_info->AddStructure( "m_node_next", SkateScript::GetNode( m_node_next ) );
	
	// waypoint positions:
	p_info->AddVector( "m_wp_pos_from", m_wp_pos_from );
	p_info->AddVector( "m_wp_pos_to", m_wp_pos_to );
	p_info->AddVector( "m_wp_pos_next", m_wp_pos_next );

    // DESIGNER SETTABLE, through script file:
    p_info->AddFloat( "m_enter_turn_dist", m_enter_turn_dist );
	p_info->AddFloat( "m_min_turn_circle_radius", m_min_turn_circle_radius );
	
	// float				m_current_turn_dist;
	
	// navigational info (save runtime calculations...)
	// float				m_dist_to_wp;
	// float				m_dist_traveled;
	
	// could be private:
	// int					m_pathob_flags;

	// float				m_extra_dist;

	// for navigating around corners:
	// Mth::Vector			m_circle_center;
	// Mth::Vector			m_circle_arrival_pos; // do we really need this? no.  so fuck you!
	// use member m_turn_matrix from object structure...
//	Mth::Matrix			m_arc_matrix;	// Y axis is rotation axis, X is along radius
	// float				m_arc_ang;		// total sweep along the radius....
	// float				m_arc_dist;		// total distance along the arc...
	// float				m_circle_radius;

	uint32 pathob_state_checksum = 0;
	switch ( m_pathob_state )
	{
	case 0:
		pathob_state_checksum = CRCD( 0x16d6676f, "PATHOB_STATE_UNINITIALIZED" );
		break;
	case 1:
		pathob_state_checksum = CRCD( 0x55b5bee1, "PATHOB_STATE_FOLLOW_LINE" );
		break;
	case 2:
		pathob_state_checksum = CRCD( 0x6cc96f7, "PATHOB_STATE_FOLLOW_CURVE" );
		break;
	case 3:
		pathob_state_checksum = CRCD( 0xac01db8, "PATHOB_STATE_LINEAR_INTERP" );
		break;
	case 4:
		pathob_state_checksum = CRCD( 0x5bc8724c, "PATHOB_STATE_IDLE" );
		break;
	default:
		Dbg_MsgAssert( 0, ( "CPathOb::GetDebugInfo got bad m_pathob_state.  Was the enum changed?" ) );
		break;
	}
	p_info->AddChecksum( "m_pathob_state", pathob_state_checksum );
#endif				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj




================================================
FILE: Code/Sk/Objects/PathOb.h
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       PathOb.h
//* OWNER:          Matt Duncan
//* CREATION DATE:  11/2/2000
//****************************************************************************

#ifndef __OBJECTS_PATHOB_H
#define __OBJECTS_PATHOB_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// debugging options:
#define FINDING_GLITCHES_IN_PATH_TRAVERSAL	0
#define DRAW_COLOR_CODED_NAV_PATH			0

#define HUGE_DISTANCE		FEET_TO_INCHES( 666 * 666 )

namespace Obj
{

class CCompositeObject;											 
											 
/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class Manager;

class CPathOb : public Spt::Class
{
	
public:
	CPathOb( CCompositeObject* pObj );
	virtual ~CPathOb();

	virtual	void		GetDebugInfo( Script::CStruct* p_info );

	Mth::Vector			m_nav_pos;		//car pos is set by finding the ground under the nav pos
	CCompositeObject* 	mp_object;		// owner

	// for the curve from one path to the next...
	Mth::Matrix			m_turn_matrix;	// Y axis is rotation axis, X is along radius
	
	// waypoint indices:
	int					m_node_from;
	int					m_node_to;
	int					m_node_next;
	
	// waypoint positions:
	Mth::Vector			m_wp_pos_from;
	Mth::Vector			m_wp_pos_to;
	Mth::Vector			m_wp_pos_next;

    // DESIGNER SETTABLE, through script file:
    float				m_enter_turn_dist;				// in inches...
	float				m_min_turn_circle_radius;	// in inches
    // end DESIGNER SETTABLE variables...
	
	// varies depending on the distance between waypoints:
	float				m_current_turn_dist;
	
	// navigational info (save runtime calculations...)
	float				m_dist_to_wp;
	float				m_dist_traveled;
	
	// could be private:
	int					m_pathob_flags;

	float				m_extra_dist;

	// for navigating around corners:
	Mth::Vector			m_circle_center;
	Mth::Vector			m_circle_arrival_pos; // do we really need this? no.  so fuck you!
	// use member m_turn_matrix from object structure...
//	Mth::Matrix			m_arc_matrix;	// Y axis is rotation axis, X is along radius
	float				m_arc_ang;		// total sweep along the radius....
	float				m_arc_dist;		// total distance along the arc...
	float				m_circle_radius;

	int					m_pathob_state;
 
	bool				Busy( void );
	float				GetDistToWaypoint( void );
			   
	// Returns 'true' when path has been traversed (end of path!)
    bool				TraversePath( const float deltaDist, Mth::Vector *pHeading = NULL );
	
	void				SetHeading( Mth::Vector *p_heading );

	bool				GetNextWaypoint( void );

	// call when beginning a new path:
	void 				InitPath( int nodeNum );
	void				NewPath( int nodeNum, const Mth::Vector ¤tPos );

	float				m_y_rot;
	void				DrawPath( void );

private:

	// Set up the path according to m_node_from and m_node_to...
	// Do this after getting the next waypoint in a path, or when
	// beginning a new path.  m_node_from should indicate where we
	// are now, m_node_to should be the node index of where we're
	// going!
	void				SetUpPath( void );

	// set heading vector to correspond to new path...
	void				AlignToPath( void );

	bool 				SetUpNavPointLineMotion( );
	int 				SetUpTurningCircle( void );
	void				PrepareTurn( void );
	bool				SetUpTurnDist( void );

	void				UpdateNavPosAlongPath( void );
	void				UpdateNavPosAlongCurve( float distTraveled );

#if FINDING_GLITCHES_IN_PATH_TRAVERSAL
	// can remove if no longer debugging:
	int					m_prev_pathob_state;
	// nav pos moves smoothly between the waypoints or along the curve connecting paths...
	Mth::Vector 		m_old_nav_pos;	//for debuggery...
	// debugging function:
	void				FindGlitch( void );
#endif

};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_PATHOB_H



================================================
FILE: Code/Sk/Objects/PlayerProfileManager.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       PlayerProfileManager.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  4/29/2002
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

#include 
#include 
#include 
#include 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Obj
{

// Selects which one is the current player profile

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/
	
/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPlayerProfileManager::CPlayerProfileManager() : m_Profiles(vMAX_PROFILES)
{
	m_CurrentProfileIndex = 0;

	for ( int i = 0; i < vMAX_PLAYERS; i++ )
	{
		mp_CurrentProfile[i] = NULL;
	}

	m_LockCurrentSkaterProfileIndex = false;

	m_TemporaryProfileCount = 0;

#if 0

/*
appearance_custom_skater_worst_case_net_packet = 
{
    body = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    board = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    skater_m_head = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    skater_m_torso = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    skater_m_legs = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    skater_m_hair = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    skater_m_backpack = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    skater_m_jaw = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    skater_m_socks = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    sleeves = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    kneepads = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    elbowpads = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    shoes = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    sleeves = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    front_logo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
    back_logo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
    hat = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    helmet = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
    accessories = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
	hat_logo = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}
	helmet_logo = { desc_id=#"Wrist Bands" h=350 s=99 v=98 use_default_hsv = 1}

	// pos items...
	griptape = { desc_id=#"Wrist Bands" }
	deck_graphic = { desc_id=#"Wrist Bands" }
	cad_graphic = { desc_id=#"Wrist Bands" h=350 s=99 v=98 uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	deck_layer1 = { desc_id=#"Wrist Bands" h=350 s=99 v=98 uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	deck_layer2 = { desc_id=#"Wrist Bands" h=350 s=99 v=98 uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	deck_layer3 = { desc_id=#"Wrist Bands" h=350 s=99 v=98 uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	deck_layer4 = { desc_id=#"Wrist Bands" h=350 s=99 v=98 uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	deck_layer5 = { desc_id=#"Wrist Bands" h=350 s=99 v=98 uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
		
	head_tattoo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	left_bicep_tattoo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	left_forearm_tattoo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	right_bicep_tattoo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	right_forearm_tattoo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	chest_tattoo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	back_tattoo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	left_leg_tattoo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}
	right_leg_tattoo = { desc_id=#"Wrist Bands" uv_u=199.999999 uv_v=199.999999 uv_scale=199.999999 uv_rot=355.000000 use_default_uv = 1}

	headtop_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	Jaw_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	nose_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	head_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	torso_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	stomach_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	upper_arm_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	lower_arm_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	hands_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	upper_leg_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	lower_leg_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	feet_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	board_bone_group = { x=160 y=160 z=160 use_default_scale = 1}
	object_scaling = { x=160 y=160 z=160 use_default_scale = 1}
}
*/

	uint8 tBuf[vMAX_APPEARANCE_BUFFER_SIZE];
	Script::CStruct* pTempStructure = Script::GetStructure("appearance_custom_skater_worst_case_net_packet",Script::ASSERT);
	Obj::CSkaterProfile* pPlayerProfile = new Obj::CSkaterProfile;
	pPlayerProfile->GetAppearance()->Load( pTempStructure, false );	
	uint32 size = pPlayerProfile->WriteToBuffer(tBuf, vMAX_APPEARANCE_BUFFER_SIZE);
	Dbg_Assert( size < vMAX_PRACTICAL_APPEARANCE_DATA_SIZE );
	Dbg_Message("*********** Worst case model appearance data size = %d bytes\n", size);
	delete pTempStructure;
	delete pPlayerProfile;
	Dbg_Assert( 0 );
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPlayerProfileManager::Init()
{
	int dummy;
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

	// TODO:  this is an optimization to go down from 4-byte
	// checksums to a 1- or 2-byte index into a checksum table.
	// This is probably not a very good system, because it's not
	// scalable to downloadable content
//	Cas::InitDescChecksumTable();
	
	// initialize the pro skater profiles here
	Script::RunScript( "init_pro_skaters" );

	// the number of profiles must exceed the number of players
	Dbg_MsgAssert( m_Profiles.getSize() >= vMAX_PLAYERS, ( "Not enough profiles (must be greater than maxplayers)" ) );

	// profile #0 points to an actual profile
	mp_CurrentProfile[0] = m_Profiles.GetItemByIndex( 0, &dummy );

	// the others point to temp copies...  this is so that player
	// #1 through N can edit their appearances without affecting
	// the source profile
	for ( int i = 1; i < vMAX_PLAYERS; i++ )
	{
		mp_CurrentProfile[i] = new CSkaterProfile;
		*mp_CurrentProfile[i] = *m_Profiles.GetItemByIndex( i, &dummy );
	}

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPlayerProfileManager::~CPlayerProfileManager()
{
	uint32 tableSize = m_Profiles.getSize( );
	for ( uint32 i = 0; i < tableSize; i++ )
	{
		int key;
		CSkaterProfile* pProfile = m_Profiles.GetItemByIndex( 0, &key );
		delete pProfile;
		m_Profiles.FlushItem( key );
	}

	for ( int i = 1; i < vMAX_PLAYERS; i++ )
	{
		Dbg_Assert( mp_CurrentProfile[i] );
		delete mp_CurrentProfile[i];
		mp_CurrentProfile[i] = NULL;
	}

	for ( int i = 0; i < m_TemporaryProfileCount; i++ )
	{
		Dbg_Assert( mp_TemporaryProfiles[i] );
		delete mp_TemporaryProfiles[i];
		mp_TemporaryProfiles[i] = NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CPlayerProfileManager::GetNumProfileTemplates( void )
{
	return m_Profiles.getSize();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CPlayerProfileManager::GetProfileTemplateChecksum( int i )
{
	Dbg_MsgAssert( i >= 0 && i < m_Profiles.getSize(),( "Profile %d out of range (0-%d)", i, m_Profiles.getSize()-1 ));

	int key;
	m_Profiles.GetItemByIndex( i, &key );
	return (uint32)key;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterProfile*		CPlayerProfileManager::GetProfileTemplateByIndex( int i)
{
	Dbg_MsgAssert( i >= 0 && i < m_Profiles.getSize(),( "Profile %d out of range (0-%d)", i, m_Profiles.getSize()-1 ));

	return m_Profiles.GetItemByIndex( i );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPlayerProfileManager::SyncPlayer2()
{
	// GJ:  need to push the skaterinfoheap or else the custom skater's face
	// texture may end up on the bottom up heap (you'd only notice that
	// if you went into 2 player mode and both characters were already
	// custom skaters, because pressing LEFT to switch from a pro skater
	// to a custom skater would automatically push the skater info heap)
	// this whole process is probably worth looking at next time because
	// it's kind of kludgy, but it's too risky to fix in any other
	// way right now...
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

	int profile_index = 1;

	// get the currently selected skater for player #1
	uint32 checksum = mp_CurrentProfile[profile_index]->GetSkaterNameChecksum();

	// sync up the profile from the root profile
	*mp_CurrentProfile[profile_index] = *m_Profiles.GetItem( checksum );
	
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPlayerProfileManager::SetCurrentProfileIndex(int i)
{
	if ( m_LockCurrentSkaterProfileIndex )
	{
		// we lock it so that we make sure no one accesses
		// this during network games or splitscreen games,
		// which aren't guaranteed to return the correct
		// value
		Dbg_MsgAssert( 0, ( "Can't access current profile index! Are you playing a network game? (See Gary)" ) );
	}

	Dbg_MsgAssert( i >= 0 && i < vMAX_PLAYERS,( "Profile %d out of range (0-%d)", i, vMAX_PLAYERS-1 ));
	
	m_CurrentProfileIndex = i;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CPlayerProfileManager::GetCurrentProfileIndex()
{
	if ( m_LockCurrentSkaterProfileIndex )
	{
		// we lock it so that we make sure no one accesses
		// this during network games or splitscreen games,
		// which aren't guaranteed to return the correct
		// value
		Dbg_MsgAssert( 0, ( "Can't access current profile index! Are you playing a network game? (See Gary)" ) );
	}

	return m_CurrentProfileIndex;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPlayerProfileManager::ApplyTemplateToCurrentProfile( uint32 checksum )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());
	if ( m_CurrentProfileIndex == 0 )
	{
		mp_CurrentProfile[m_CurrentProfileIndex] = GetProfileTemplate( checksum );
	}
	else
	{
		*mp_CurrentProfile[m_CurrentProfileIndex] = *GetProfileTemplate( checksum );
	}
	Mem::Manager::sHandle().PopContext();	//Mem::Manager::sHandle().SkaterInfoHeap());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterProfile* CPlayerProfileManager::GetCurrentProfile()
{
	return mp_CurrentProfile[m_CurrentProfileIndex];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterProfile* CPlayerProfileManager::GetProfile( int i )
{
	Dbg_MsgAssert( i >= 0 && i < vMAX_PLAYERS,( "Profile %d out of range (0-%d)", i, vMAX_PLAYERS-1 ));

	return mp_CurrentProfile[i];
}

/******************************************************************/
/*                                                                
	If any of the skater careers have this flag set, it returns true.
	Used by the cassette menu when used for free skate, session etc,
	to find out what levels need to be selectable.
																  */
/******************************************************************/

/*
bool CPlayerProfileManager::GetGlobalFlag(int flag)
{
	Dbg_MsgAssert(0,("CPlayerProfileManager has been deprecated - careers no longer in profiles\n"));
	uint32 tableSize = m_Profiles.getSize( );
	for ( uint32 i = 0; i < tableSize; i++ )
	{
		int key;
		CSkaterProfile* pProfile = m_Profiles.GetItemByIndex( i, &key );
		Dbg_MsgAssert(pProfile,("NULL pProfile"));

		CSkaterCareer *pCareer=pProfile->GetCareer();
		Dbg_MsgAssert(pCareer,("NULL pCareer"));

		if (pCareer->GetGlobalFlag(flag))
		{
			return true;
		}
	}

	return false;
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterProfile* CPlayerProfileManager::GetProfileTemplate( uint32 checksum )
{
	Dbg_MsgAssert( m_Profiles.GetItem( checksum ), ( "Couldn't find profile %s", Script::FindChecksumName(checksum) ) );

	return m_Profiles.GetItem( checksum );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPlayerProfileManager::Reset()
{
	uint32 tableSize = m_Profiles.getSize( );
	for ( uint32 i = 0; i < tableSize; i++ )
	{
		int key;
		CSkaterProfile* pProfile = m_Profiles.GetItemByIndex( i, &key );
		pProfile->Reset();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPlayerProfileManager::AddAllProProfileInfo(Script::CStruct *pStuff)
{
	Dbg_MsgAssert(pStuff,("NULL pStuff"));
	
	uint32 tableSize = m_Profiles.getSize( );
	for ( uint32 i = 0; i < tableSize; i++ )
	{
		int key;
		CSkaterProfile* pProfile = m_Profiles.GetItemByIndex( i, &key );

		// Only add the pro's, not the create-a-skater.
		if (pProfile->IsPro())
		{
			pProfile->WriteIntoStructure(pStuff);
		}
	}
	
	
	Dbg_MsgAssert(mp_CurrentProfile[m_CurrentProfileIndex],("NULL mp_CurrentProfile[%d]",m_CurrentProfileIndex));
	
	uint32 CurrentSkaterNameChecksum = mp_CurrentProfile[m_CurrentProfileIndex]->GetSkaterNameChecksum();
	pStuff->AddComponent(Script::GenerateCRC("CurrentSkater"),ESYMBOLTYPE_NAME,(int)CurrentSkaterNameChecksum);
	
	Str::String DisplayName=mp_CurrentProfile[m_CurrentProfileIndex]->GetUIString("display_name");
	const char *pDisplayName=DisplayName.getString();
	pStuff->AddComponent(Script::GenerateCRC("DisplayName"),ESYMBOLTYPE_STRING,pDisplayName);
	
	/* The CAS file name is no longer needed since the cas info is now in the unified save file.
	
	const char *pFileName=mp_CurrentProfile[m_CurrentProfileIndex]->GetCASFileName();
	pStuff->AddString("CASFileName",pFileName);
	
	// The s_insert_game_save_info function in mcfuncs.cpp is also used to determine ahead of time
	// how much space a particular type of save will use, so that we can check that there is enough
	// space on the memcard.
	// However, at that point the file name for the cas save is not known, because the user has not
	// chosen it yet.
	// So add a pad string, so that whatever the current filename happens to be, the amount of
	// space used will be the same once the user has chosen a filename.
	int pad=Script::GetInteger("MAX_MEMCARD_FILENAME_LENGTH",Script::ASSERT)-strlen(pFileName);
	Dbg_MsgAssert(pad>=0 && pad<100,("Oops! '%s'",pFileName));
	char p_foo[100];
	for (int i=0; iAddString("FileNamePad",p_foo);
	*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPlayerProfileManager::LoadAllProProfileInfo(Script::CStruct *pStuff)
{
	Dbg_MsgAssert(pStuff,("NULL pStuff"));

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());
	
	uint32 tableSize = m_Profiles.getSize( );
	for ( uint32 i = 0; i < tableSize; i++ )
	{
		int key;
		CSkaterProfile* pProfile = m_Profiles.GetItemByIndex( i, &key );

		if (pProfile->IsPro())
		{
			uint32 Name=pProfile->GetSkaterNameChecksum();
			pProfile->ReadFromStructure(Name,pStuff);
		}	
	}
	
	uint32 CurrentSkaterNameChecksum=0;
	pStuff->GetChecksum("CurrentSkater",&CurrentSkaterNameChecksum);
	ApplyTemplateToCurrentProfile(CurrentSkaterNameChecksum);
	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPlayerProfileManager::AddCASProfileInfo(Script::CStruct *pStuff)
{
	Dbg_MsgAssert(pStuff,("NULL pStuff"));
	
	uint32 tableSize = m_Profiles.getSize( );
	for ( uint32 i = 0; i < tableSize; i++ )
	{
		int key;
		CSkaterProfile* pProfile = m_Profiles.GetItemByIndex( i, &key );

		if (!pProfile->IsPro())
		{
			pProfile->WriteIntoStructure(pStuff);
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPlayerProfileManager::LoadCASProfileInfo( Script::CStruct *pStuff, bool load_info )
{
	Dbg_MsgAssert(pStuff,("NULL pStuff"));

	ApplyTemplateToCurrentProfile(0xa7be964/*custom*/);
	CSkaterProfile* pProfile=GetCurrentProfile();

	uint32 Name = Script::GenerateCRC("custom");
	Script::CStruct *pSkaterInfo=NULL;
	pStuff->GetStructure(Name,&pSkaterInfo);
   
	Dbg_MsgAssert(pSkaterInfo,("Memory card data is missing info for skater '%s'",Script::FindChecksumName(Name)));
	
	// load only if there is a version number and it is current.
	int Version=0;
	if (pSkaterInfo->GetInteger("version_number",&Version))
	{
		if (Version==CSkaterProfile::vVERSION_NUMBER)
		{
			Script::CStruct *pTemp=NULL;
	
			pSkaterInfo->GetStructure("Appearance",&pTemp);
			Gfx::CModelAppearance* pAppearance=pProfile->GetAppearance();
			Dbg_MsgAssert(pAppearance,("NULL pAppearance"));
			pAppearance->Load(pTemp);
	
//			CSkaterCareer* pCareer=pProfile->GetCareer();
//			Dbg_MsgAssert(pCareer,("NULL pCareer"));
//			pCareer->ReadFromStructure(pSkaterInfo);
	
			// GJ:  maybe this function could be refactored to use
			// CSkaterProfile's ReadFromStructure() function?
			// i'm not sure why there's one set of code for the pros
			// and one for the custom skater
			Gfx::CFaceTexture* pFaceTexture = pAppearance->GetFaceTexture();
			Dbg_MsgAssert( (!pProfile->IsPro()) == (pFaceTexture!=NULL), ( "Only custom skaters should have face textures (Pro=%d, FaceTexture=%p)", pProfile->IsPro(), pFaceTexture ) )
			if ( pFaceTexture )
			{
				bool faceTextureFound = pSkaterInfo->GetStructure( CRCD(0xf0d3bc94,"FaceTexture"), &pTemp );
				if ( faceTextureFound )
				{
					// load the face texture, if it exists
					pFaceTexture->ReadFromStructure( pTemp );
					printf( "Face texture found\n" );
				}
				else
				{
					printf( "Face texture not found\n" );
				}
				pFaceTexture->SetValid( faceTextureFound );
			}
            
            // store current stats
            int air_value;
            int run_value;
            int ollie_value;
            int speed_value;
            int spin_value;
            int flip_speed_value;
            int switch_value;
            int rail_balance_value;
            int lip_balance_value;
            int manual_balance_value;
            air_value = pProfile->GetStatValue(CRCD(0x439f4704,"air"));
            run_value = pProfile->GetStatValue(CRCD(0xaf895b3f,"run"));
            ollie_value = pProfile->GetStatValue(CRCD(0x9b65d7b8,"ollie"));
            speed_value = pProfile->GetStatValue(CRCD(0xf0d90109,"speed"));
            spin_value = pProfile->GetStatValue(CRCD(0xedf5db70,"spin"));
            flip_speed_value = pProfile->GetStatValue(CRCD(0x6dcb497c,"flip_speed"));
            switch_value = pProfile->GetStatValue(CRCD(0x9016b4e7,"switch"));
            rail_balance_value = pProfile->GetStatValue(CRCD(0xf73a13e3,"rail_balance"));
            lip_balance_value = pProfile->GetStatValue(CRCD(0xae798769,"lip_balance"));
            manual_balance_value = pProfile->GetStatValue(CRCD(0xb1fc0722,"manual_balance"));

			int num_trickslots;
			num_trickslots = pProfile->GetNumSpecialTrickSlots();
			Script::CStruct* pSpecialTricks = new Script::CStruct();
			pSpecialTricks->AppendStructure( pProfile->GetSpecialTricksStructure() );
            
            
            pTemp=NULL;
            pSkaterInfo->GetStructure("Info",&pTemp);
            Script::CStruct* pInfo = pProfile->GetInfo();
            Dbg_Assert(pInfo);
            pInfo->Clear();
            pInfo->AppendStructure(pTemp);
            
            if ( !load_info )
            {
                // restore stats
                pProfile->SetPropertyValue(CRCD(0x439f4704,"air"), air_value);
                pProfile->SetPropertyValue(CRCD(0xaf895b3f,"run"), run_value);
                pProfile->SetPropertyValue(CRCD(0x9b65d7b8,"ollie"), ollie_value);
                pProfile->SetPropertyValue(CRCD(0xf0d90109,"speed"), speed_value);
                pProfile->SetPropertyValue(CRCD(0xedf5db70,"spin"), spin_value);
                pProfile->SetPropertyValue(CRCD(0x6dcb497c,"flip_speed"), flip_speed_value);
                pProfile->SetPropertyValue(CRCD(0x9016b4e7,"switch"), switch_value);
                pProfile->SetPropertyValue(CRCD(0xf73a13e3,"rail_balance"), rail_balance_value);
                pProfile->SetPropertyValue(CRCD(0xae798769,"lip_balance"), lip_balance_value);
                pProfile->SetPropertyValue(CRCD(0xb1fc0722,"manual_balance"), manual_balance_value);

				// restore trickslot number
				pProfile->SetPropertyValue( CRCD(0xac9b9eda,"max_specials"), num_trickslots );
				pProfile->GetSpecialTricksStructure()->Clear();
				pProfile->GetSpecialTricksStructure()->AppendStructure( pSpecialTricks );
            }
			delete pSpecialTricks;
            

            // First deck is always unlocked, so make sure this is the default just in case DeckFlags is not found.
			int DeckFlags=1;
			pSkaterInfo->GetInteger("DeckFlags",&DeckFlags);
			//Front::SetDeckFlags(Name,(uint32)DeckFlags);
			
			return;
		}
		Dbg_Warning("\n\nBad version number of %d in saved custom skater. Required version = %d\n\n",Version,CSkaterProfile::vVERSION_NUMBER);
		return;
	}		
	Dbg_Warning("\n\nNo version number in saved custom skater. Required version = %d\n\n",CSkaterProfile::vVERSION_NUMBER);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPlayerProfileManager::AddTemporaryProfile( uint32 profileName )
{
	Dbg_MsgAssert( m_TemporaryProfileCount < vMAX_TEMPORARY_PROFILES, ( "Too many temporary profiles (max=%d)", vMAX_TEMPORARY_PROFILES ) );

	for ( int i = 0; i < m_TemporaryProfileCount; i++ )
	{
		if ( m_TemporaryProfileChecksums[i] == profileName )
		{
			Dbg_MsgAssert( 0, ( "Temporary profile checksum %s was already used\n", Script::FindChecksumName(profileName) ) );
		}
	}

	mp_TemporaryProfiles[m_TemporaryProfileCount] = new Obj::CSkaterProfile;
	m_TemporaryProfileChecksums[m_TemporaryProfileCount] = profileName;

	m_TemporaryProfileCount++;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterProfile* CPlayerProfileManager::GetTemporaryProfile(uint32 profileName)
{
	for ( int i = 0; i < m_TemporaryProfileCount; i++ )
	{
		if ( m_TemporaryProfileChecksums[i] == profileName )
		{
			return mp_TemporaryProfiles[i];
		}
	}
	
	Dbg_MsgAssert( 0, ( "Couldn't find temporary profile with checksum %s\n", Script::FindChecksumName(profileName) ) );

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPlayerProfileManager::AddNewProfile( Script::CStruct* pParams )
{
	Dbg_Assert( pParams );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

	// profileChecksum = hawk, thomas, custom, etc.
	uint32 profileChecksum;
	pParams->GetChecksum( "name", &profileChecksum, true );
	
	// add to the lookup table
	CSkaterProfile* pPlayerProfile = new CSkaterProfile;
	pPlayerProfile->Reset( pParams );
	m_Profiles.PutItem( profileChecksum, pPlayerProfile );

#ifdef __USER_GARY__
//	uint8 tBuf[vMAX_APPEARANCE_BUFFER_SIZE];
//	uint32 size = pPlayerProfile->WriteToBuffer(tBuf, vMAX_APPEARANCE_BUFFER_SIZE);
//	Dbg_Assert( size < vMAX_PRACTICAL_APPEARANCE_DATA_SIZE );
//	Dbg_Message("*********** AddNewProfile %s appearance data size = %d bytes\n", Script::FindChecksumName(profileChecksum), size);
#endif

	Mem::Manager::sHandle().PopContext();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CPlayerProfileManager::LockCurrentSkaterProfileIndex( bool locked )
{
	// GJ:  This is purely for debugging purposes,
	// to catch any accesses to the current skater profile index.
	// When using the CModelBuilder class, we NEVER want to use
	// the current skater profile index, because it may or may
	// not be valid in a network or splitscreen game.
	// So, the CModelBuilder class will turn this lock on
	// at the beginning of the model creation process, and
	// turn it off at the end.  Any calls to GetCurrentSkaterProfileIndex
	// will assert while locked.

	m_LockCurrentSkaterProfileIndex = locked;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj



================================================
FILE: Code/Sk/Objects/PlayerProfileManager.h
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       PlayerProfileManager.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  4/29/2002
//****************************************************************************

#ifndef __OBJECTS_PLAYERPROFILEMANAGER_H
#define __OBJECTS_PLAYERPROFILEMANAGER_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
                               
#include 
                                              
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class CPlayerProfileManager : public Spt::Class
{
	enum
	{
		vMAX_PROFILES = 32,
		vMAX_PLAYERS = GameNet::vMAX_LOCAL_CLIENTS,

		// this is the biggest packet we can reasonably send
		// over a network packet (it's not really based on 
		// the max packet size, but rather the biggest packet
		// that we've seen work so far...  the max packet size
		// specified in net.h seems to be too aggressive)
		vMAX_PRACTICAL_APPEARANCE_DATA_SIZE = 1150,

		// this is just the size of a temp buffer
		vMAX_APPEARANCE_BUFFER_SIZE = 4096,

		// temporary profiles, used for remembering/restoring
		// skater profiles (like for credits)
		vMAX_TEMPORARY_PROFILES = 3,
	};
	
	public:
		CPlayerProfileManager();
		~CPlayerProfileManager();

	public:
		void					Init();

		// these deal with players (0 to numLocalClients)
		void					SetCurrentProfileIndex(int i);
		int						GetCurrentProfileIndex();
		void					ApplyTemplateToCurrentProfile(uint32 checksum);
		CSkaterProfile*			GetCurrentProfile();
		CSkaterProfile*			GetProfile(int i);
//		bool					GetGlobalFlag(int flag);
		void					SyncPlayer2();

		// these deal with templates (0 to numPros)
		CSkaterProfile*			GetProfileTemplate(uint32 checksum);
		CSkaterProfile*			GetProfileTemplateByIndex(int i);

		// for populating front end menus
		uint32					GetNumProfileTemplates();
		uint32					GetProfileTemplateChecksum(int i);
		
		void					AddAllProProfileInfo(Script::CStruct *pStuff);
		void					LoadAllProProfileInfo(Script::CStruct *pStuff);
		void					AddCASProfileInfo(Script::CStruct *pStuff);
		void					LoadCASProfileInfo(Script::CStruct *pStuff, bool load_info);
		
		void					Reset();
		bool					AddNewProfile(Script::CStruct* pParams);
		void					LockCurrentSkaterProfileIndex( bool locked );
		
		bool					AddTemporaryProfile(uint32 checksum);
		CSkaterProfile*			GetTemporaryProfile(uint32 checksum);

	protected:
		Lst::LookupTable	m_Profiles;
		CSkaterProfile*			mp_CurrentProfile[GameNet::vMAX_LOCAL_CLIENTS];
		int						m_CurrentProfileIndex;
		bool					m_LockCurrentSkaterProfileIndex;
		
		int						m_TemporaryProfileCount;
		uint32					m_TemporaryProfileChecksums[vMAX_TEMPORARY_PROFILES];
		CSkaterProfile*			mp_TemporaryProfiles[vMAX_TEMPORARY_PROFILES];
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/


} // namespace Obj

#endif	// __OBJECTS_PLAYERPROFILEMANAGER_H


================================================
FILE: Code/Sk/Objects/SkaterButton.cpp
================================================
// skaterbutton.cpp

#include	

#include	

const	int	pressure_threshold = 64;

#ifdef __NOPT_ASSERT__
void		CSkaterButton::Update(int current_pressure, bool debug) 		// Update the pad based on teh current pressure
#else
void		CSkaterButton::Update(int current_pressure) 		// Update the pad based on teh current pressure
#endif
{
	// if were are debouncing the button then update the debounce state
	// based on time and release, and ignore the button if still debouncing
	if (m_debounce_time)
	{
		// if not pressed, or debounce time expired
		if (current_pressure <= pressure_threshold)
		{
//			dodgy_test(); printf("Ending Debouncing as not pressed\n");
			m_debounce_time = 0;
		}
		else if ((float)Tmr::GetTime() > m_debounce_time)
		{
//			dodgy_test(); printf("Ending Debouncing as time expired\n");
			m_debounce_time = 0;
		}
		else
		{
//			dodgy_test(); printf("Debouncing button\n");
			return;
		}
	}

	m_pressure = current_pressure;
	if (m_pressure > pressure_threshold)
	{
		// button is currently pressed
		if (!m_pressed)
		{
			m_pressed_time = Tmr::ElapsedTime(0);
			#ifdef __NOPT_ASSERT__
			if (debug)			
			{
				printf("%d: +++ Triggered %s +++\n",(int)Tmr::GetRenderFrame(),Script::FindChecksumName(m_name_checksum));
			}
			#endif
			// but not previously pressed, so set the "triggered" flag
			m_triggered = true;
		}
		m_pressed = true;
	}
	else
	{
		// button is currently NOT pressed
		if (m_pressed)
		{
			// but WAS previously pressed, so set the "released" flag
			m_released_time = Tmr::ElapsedTime(0);
			#ifdef __NOPT_ASSERT__
			if (debug)			
			{
				printf("%d: --- Released  %s ----\n",(int)Tmr::GetRenderFrame(),Script::FindChecksumName(m_name_checksum));
			}
			#endif
			m_released = true;
		}
		m_pressed = false;
	}

}

void		CSkaterButton::SetPressed( bool pressed )		// Set the button's "Pressed" state
{
	if( pressed )
	{
		// button is currently pressed
		if( !m_pressed )
		{
			m_pressed_time = Tmr::ElapsedTime(0);
			// but not previously pressed, so set the "triggered" flag
			m_triggered = true;
		}
		m_pressed = true;
	}
	else
	{
		// button is currently NOT pressed
		if (m_pressed)
		{
			// but WAS previously pressed, so set the "released" flag
			m_released = true;
		}
		m_pressed = false;
	}
	m_pressed = pressed;
}

uint32		CSkaterButton::GetReleasedTime()					// Get time elapsed since m_released_time
{
	return	Tmr::ElapsedTime(m_released_time);
}

uint32		CSkaterButton::GetPressedTime()					// Get time elapsed since m_presssed_time
{
	return	Tmr::ElapsedTime(m_pressed_time);
}

void		CSkaterButton::ClearTrigger()						// Clear the trigger
{
	m_triggered = false;
}

void		CSkaterButton::ClearRelease()						// Clear the trigger
{
	m_released = false;
}


void 		CSkaterButton::SetName(const char *p_name)
{
	m_name_checksum = Crc::GenerateCRCFromString(p_name);
}


void		CSkaterButton::SetDebounce(float time)
{
	// Mick:  When debouncing, we ignore furthur input until the time has elapsed
	// or the button "pressed" state changes from what it was at the start of the debounce 

//	printf ("SetDebounce #########################################################\n");
//	
//	printf ("GetTime = %d, m_pressed = %d, m_pressed_time = %d, m_triggered = %d\n",
//			Tmr::ElapsedTime(0), m_pressed  , m_pressed_time , m_triggered );
	
	
	m_debounce_time = time;
}




================================================
FILE: Code/Sk/Objects/SkaterButton.h
================================================
// SkaterButton.h

#ifndef	__SK_OBJECTS_SKATERBUTTON_H__
#define	__SK_OBJECTS_SKATERBUTTON_H__

#include 
#include 
#include 


// A SkaterButton contains the state of a single pad button
// in a way that is specific to control issues
// initially this is similar to the old SPad structure in THPS-PSX
// but will probably be extended to handle analog buttons
// and storing, inspecting and flagging the history of button events 

class CSkaterButton : public Spt::Class
{


public:
#ifdef __NOPT_ASSERT__
	void		Update(int current_pressure, bool debug = false); 		// Update the button based on the current pressure
#else
	void		Update(int current_pressure); 		// Update the button based on the current pressure
#endif
	bool		GetPressed() {return m_pressed;}	// Get current "Pressed" state
	bool		GetTriggered();						// Get current "Triggered" state
	void		SetPressed( bool pressed );			// Set the button's "Pressed" state
	int			GetPressure();						// Get current pressure
	void		ClearTrigger();						// Clear the trigger
	void 		SetName(const char *p_name);		// Set name of button (mostly for dubugging)

	void		SetDebounce(float time);			// set time to debounce
	Tmr::Time	GetDebounceTime() { return static_cast< Tmr::Time >(m_debounce_time); }

	bool		GetReleased();
	void		ClearRelease();				
	
	uint32		GetPressedTime();  					// Get m_pressed_time
	uint32		GetReleasedTime();					// Get m_released_time

private:		
	
	
	bool		m_pressed;  						// true if pressed now, indicates the current state of the button
	uint32		m_pressed_time;						// time when button was last pressed
	uint32		m_released_time;					// time when button was last released
	int			m_pressure;							// current button presure;
	bool		m_triggered;	  					// true if was triggered, can be cleared
	int 		m_released;							// true if we released, can be cleared (for debouncing)
	uint32		m_name_checksum;					// Checksum of button name
	float		m_debounce_time;					// time until we we remain debounced
	//bool		m_debounce_pressed;					// state when "debounce" called
	//const char	*	mp_name;			  	 	// Pointer to name, used for debugging;
	
public:
	uint32 GetName() {return m_name_checksum;}
};

inline bool		CSkaterButton::GetTriggered()		// Get current "Triggered" state
{
	return m_triggered;
}

inline bool		CSkaterButton::GetReleased()		
{
	return m_released;
}

inline int			CSkaterButton::GetPressure()	// Get current pressure
{
	return	m_pressure;
}



#endif	__SK_OBJECTS_SKATERBUTTON_H__





================================================
FILE: Code/Sk/Objects/SkaterPad.h
================================================
// SkaterPad.h

#ifndef	__SK_OBJ_SKATERPAD_H__
#define	__SK_OBJ_SKATERPAD_H__


#include 
#include 
#include 
#include 

namespace Inp
{
	class Data;
}

// A CSkaterPad class contains the state of the pad controlling the skater 
class  CSkaterPad  : public Spt::Class
{
	
public:
	CSkaterPad();

	uint32			GetPressedMask( void );
	
	#ifdef __NOPT_ASSERT__
	void			Update ( Inp::Data* input, bool debug = false );
	#else
	void			Update ( Inp::Data* input );
	#endif
	
	void			Zero (   );
	void			Reset (   );
	
	float			GetScaledAnalogStickMagnitude ( float analog_x, float analog_y, float analog_angle  );
	float			GetScaledLeftAnalogStickMagnitude (   ) { return GetScaledAnalogStickMagnitude(m_leftX, m_leftY, m_leftAngle); }
	float			GetScaledRightAnalogStickMagnitude (   ) { return GetScaledAnalogStickMagnitude(m_rightX, m_rightY, m_rightAngle); }
	
	bool			IsLeftAnalogUpPressed();
	bool			IsLeftAnalogDownPressed();
	void			DebounceLeftAnalogUp ( float duration );
	void			DebounceLeftAnalogDown( float duration );

	CSkaterButton 	m_up;
	CSkaterButton 	m_down;
	CSkaterButton 	m_left;
	CSkaterButton 	m_right;
	CSkaterButton 	m_L1;
	CSkaterButton 	m_L2;
	CSkaterButton 	m_L3;
	CSkaterButton 	m_R1;
	CSkaterButton 	m_R2;
	CSkaterButton 	m_R3;
	CSkaterButton 	m_circle;
	CSkaterButton 	m_square;
	CSkaterButton 	m_triangle;
	CSkaterButton 	m_x;
	CSkaterButton 	m_start;
	CSkaterButton 	m_select;

	float			m_rightX;
	float			m_rightY;
	float			m_leftX;
	float			m_leftY;

	float			m_scaled_rightX;
	float			m_scaled_rightY;
	float			m_scaled_leftX;
	float			m_scaled_leftY;

	// angle and amount we are push the direction stick
	float			m_rightAngle;
	float			m_rightLength;
	float			m_leftAngle;
	float			m_leftLength;
	
	Tmr::Time		m_leftAnalogUpDebounceTime;
	Tmr::Time		m_leftAnalogDownDebounceTime;
	
	// Given the checksum of a button name, this will return that button.
	CSkaterButton*	GetButton(uint32 NameChecksum);

	// related utility functions
	
	static uint32	sGetDirection(bool Up, bool Down, bool Left, bool Right);
	static float	sGetAngleFromDPad(bool Up, bool Down, bool Left, bool Right);
};

inline bool CSkaterPad::IsLeftAnalogUpPressed (   )
{
	return m_leftAnalogUpDebounceTime == 0 && m_leftLength != 0.0f && Mth::Abs(m_leftAngle) < Mth::DegToRad(45.0f);
}

inline bool CSkaterPad::IsLeftAnalogDownPressed (   )
{
	return m_leftAnalogDownDebounceTime == 0 && m_leftLength != 0.0f && (Mth::PI - Mth::Abs(m_leftAngle)) < Mth::DegToRad(45.0f);
}

inline void CSkaterPad::DebounceLeftAnalogUp ( float duration )
{
	m_leftAnalogUpDebounceTime = Tmr::GetTime() + static_cast< Tmr::Time >(1000.0f * duration);
}

inline void CSkaterPad::DebounceLeftAnalogDown ( float duration )
{
	m_leftAnalogDownDebounceTime = Tmr::GetTime() + static_cast< Tmr::Time >(1000.0f * duration);
}

typedef CSkaterPad CControlPad;

#endif	//	__SK_OBJ_SKATERPAD_H__



================================================
FILE: Code/Sk/Objects/SkaterProfile.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       SkaterProfile.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/29/2000
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

// If possible, try to remove the dependency on these include files...
#include 
#include                          // for updatetrickmapping
#include            // for updatetrickmapping

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Obj
{

// TODO:  Add trick-config and special-trick-config classes.

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/
	
/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterProfile::CSkaterProfile(void)
{
	Reset();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterProfile::CSkaterProfile(const CSkaterProfile& skaterProfile)
{
	// use the overridden assignment operator
	*this = skaterProfile;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterProfile& CSkaterProfile::operator=(const CSkaterProfile& skaterProfile)
{
	if ( &skaterProfile == this )
	{
		return *this;
	}

	// use the overridden assignment operator
	m_Appearance = skaterProfile.m_Appearance;

	// info assignment is okay, bec. structs have an overridden assignment operator
	m_Info = skaterProfile.m_Info;

	return *this;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterProfile::Reset(Script::CStruct* pParams)
{
	// this should get called once at the beginning of the game,
	// and then whenever you want to clear it out to its initial state
	// (like creating a new pro)

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

	// destroy the face texture, if one exists
	m_Appearance.DestroyFaceTexture();
	
	m_Appearance.Init();
	
	m_Info.Clear();

	Script::CStruct* pTemp;
	
	// TODO:  The trick mapping and the specials
	// should be in their own structures, not within the
	// info...

	// at a minimum, the info structure must contain the trick_mapping structure
	pTemp = new Script::CStruct;
	m_Info.AddComponent( CRCD(0xd544aa2d,"trick_mapping"), ESYMBOLTYPE_STRUCTUREPOINTER, (int)pTemp );

	// as well as a special tricks array
	pTemp = new Script::CStruct;
	m_Info.AddComponent( CRCD(0xddbee809,"specials"), ESYMBOLTYPE_STRUCTUREPOINTER, (int)pTemp );

	Mem::Manager::sHandle().PopContext();

	if ( pParams )
	{
		//	Dbg_Message( "Alternate constructor for skater profile called\n");

		uint32 appearanceStructure;
		pParams->GetChecksum( CRCD(0x1d6f290c,"default_appearance"), &appearanceStructure, true );
		m_Appearance.Load( appearanceStructure );

		// set the trick style, whether he's a pro, etc.
		m_Info.Clear();
		m_Info.AppendStructure( pParams );

		if ( !IsPro() )
		{
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

			// create a face texture, only if it's a custom skater
			m_Appearance.CreateFaceTexture();
			
			// for debugging purposes, set up a test face
			// Gfx::CFaceTexture* pFaceTexture = m_Appearance.GetFaceTexture();
			// pFaceTexture->LoadFace( "faces\\CS_NSN_head_test_kurt" );
			
			Mem::Manager::sHandle().PopContext();
		}		

		// initialize the trick mappings to the default values, defined in protricks.q
		uint32 trickMappingChecksum;
		pParams->GetChecksum( CRCD(0xe365363e,"default_trick_mapping"), &trickMappingChecksum, Script::ASSERT );

		Script::CStruct* pGlobalTrickMappingStructure;
		pGlobalTrickMappingStructure = Script::GetStructure(trickMappingChecksum, Script::ASSERT);

		Script::CStruct* pLocalTrickMappingStructure;
		m_Info.GetStructure( CRCD(0xd544aa2d,"trick_mapping"), &pLocalTrickMappingStructure, Script::ASSERT );
		pLocalTrickMappingStructure->Clear();
		pLocalTrickMappingStructure->AppendStructure( pGlobalTrickMappingStructure );
	}
	else
	{
//		Dbg_Message( "Warning: profile was created without valid data" );
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterProfile::PartialReset(Script::CStruct* pParams)
{
    Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

	// destroy the face texture, if one exists
	m_Appearance.DestroyFaceTexture();
	
	m_Appearance.Init();
	
    Script::CStruct* pTemp;
	
    // at a minimum, the info structure must contain the trick_mapping structure
	pTemp = new Script::CStruct;
	m_Info.AddComponent( CRCD(0xd544aa2d,"trick_mapping"), ESYMBOLTYPE_STRUCTUREPOINTER, (int)pTemp );

	// as well as a special tricks array
	pTemp = new Script::CStruct;
	m_Info.AddComponent( CRCD(0xddbee809,"specials"), ESYMBOLTYPE_STRUCTUREPOINTER, (int)pTemp );

	Mem::Manager::sHandle().PopContext();

	if ( pParams )
	{
        uint32 appearanceStructure;
		pParams->GetChecksum( CRCD(0x1d6f290c,"default_appearance"), &appearanceStructure, true );
		m_Appearance.Load( appearanceStructure );

        // copy just the info I want into it
        const char* name;
        int num;
        uint32 checksum;

        pParams->GetString( CRCD(0x2ab66cb8,"display_name"), &name, Script::ASSERT );
        m_Info.AddString("display_name", name );
        pParams->GetString( CRCD(0x562e3ecd,"first_name"), &name, Script::ASSERT );
        m_Info.AddString("first_name", name );
        pParams->GetString( CRCD(0x2820e997,"file_name"), &name, Script::ASSERT );
        m_Info.AddString("file_name", name );
        pParams->GetString( CRCD(0x6c375b95,"hometown"), &name, Script::ASSERT );
        m_Info.AddString("hometown", name );

        pParams->GetInteger( CRCD(0x6f14c39c,"skater_index"), &num, Script::ASSERT );
        m_Info.AddInteger("skater_index", num );
        pParams->GetInteger( CRCD(0xd82f8ac8,"is_pro"), &num, Script::ASSERT );
        m_Info.AddInteger("is_pro", num );
        pParams->GetInteger( CRCD(0x3f813177,"is_male"), &num, Script::ASSERT );
        m_Info.AddInteger("is_male", num );
        pParams->GetInteger( CRCD(0x9f350e5,"is_head_locked"), &num, Script::ASSERT );
        m_Info.AddInteger("is_head_locked", num );
        pParams->GetInteger( CRCD(0x4564bab1,"is_locked"), &num, Script::ASSERT );
        m_Info.AddInteger("is_locked", num );
        pParams->GetInteger( CRCD(0x27eb9b9d,"is_hidden"), &num, Script::NO_ASSERT );
        m_Info.AddInteger("is_hidden", num );
        pParams->GetInteger( CRCD(0x5ecfef4d,"age"), &num, Script::ASSERT );
        m_Info.AddInteger("age", num );
        
        pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &checksum, Script::ASSERT );
        m_Info.AddChecksum("name", checksum );
        pParams->GetChecksum( CRCD(0x7d02bcc3,"stance"), &checksum, Script::ASSERT );
        m_Info.AddChecksum("stance", checksum );
        pParams->GetChecksum( CRCD(0xc15dbf86,"pushstyle"), &checksum, Script::ASSERT );
        m_Info.AddChecksum("pushstyle", checksum );
        pParams->GetChecksum( CRCD(0xdfd4043e,"trickstyle"), &checksum, Script::ASSERT );
        m_Info.AddChecksum("trickstyle", checksum );

        Script::CStruct* p_special;
        pParams->GetStructure( CRCD(0xddbee809,"specials"), &p_special, Script::ASSERT );
        m_Info.AddStructure("specials", p_special );
        
#ifdef __PLAT_NGPS__
        if ( !IsPro() )
		{
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

			// create a face texture, only if it's a custom skater
			m_Appearance.CreateFaceTexture();
			
			// for debugging purposes, set up a test face
			// Gfx::CFaceTexture* pFaceTexture = m_Appearance.GetFaceTexture();
			// pFaceTexture->LoadFace( "faces\\CS_NSN_head_test_kurt" );
			
			Mem::Manager::sHandle().PopContext();
		}
#endif

		// initialize the trick mappings to the default values, defined in protricks.q
		uint32 trickMappingChecksum;
		pParams->GetChecksum( CRCD(0xe365363e,"default_trick_mapping"), &trickMappingChecksum, Script::ASSERT );

		Script::CStruct* pGlobalTrickMappingStructure;
		pGlobalTrickMappingStructure = Script::GetStructure(trickMappingChecksum, Script::ASSERT);

		Script::CStruct* pLocalTrickMappingStructure;
		m_Info.GetStructure( CRCD(0xd544aa2d,"trick_mapping"), &pLocalTrickMappingStructure, Script::ASSERT );
		pLocalTrickMappingStructure->Clear();
		pLocalTrickMappingStructure->AppendStructure( pGlobalTrickMappingStructure );
	}
	else
	{
//		Dbg_Message( "Warning: profile was created without valid data" );
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CSkaterProfile::WriteToBuffer(uint8 *pBuffer, uint32 BufferSize, bool ignoreFaceData )
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());

	uint32 totalSize = 0;
	uint32 chunkSize = 0;

	chunkSize = m_Appearance.WriteToBuffer(pBuffer, BufferSize, ignoreFaceData);
	totalSize += chunkSize;

	// write out trick mappings
	BufferSize -= chunkSize;
	pBuffer += chunkSize;
//	compress_trick_mappings( "trick_mapping" );
	chunkSize = Script::WriteToBuffer(GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") ), pBuffer, BufferSize );
	totalSize += chunkSize;
//	decompress_trick_mappings( "trick_mapping" );

	// write out special tricks...  these could
	// theoretically be compressed, but not using
	// the same method as the regular tricks
	// (because they're in a different format)
	BufferSize -= chunkSize;
	pBuffer += chunkSize;
	Script::CStruct* pSpecialsStructure = GetSpecialTricksStructure();
	chunkSize = Script::WriteToBuffer(pSpecialsStructure, pBuffer, BufferSize);
	totalSize += chunkSize;

	// write out any extra info that needs to go across
	// (i.e. stats, name, etc.)
	// (some data, such as your default specials
	// and your default appearance, aren't needed)
	BufferSize -= chunkSize;
	pBuffer += chunkSize;
	chunkSize = write_extra_info_to_buffer(pBuffer, BufferSize);
	totalSize += chunkSize;
	
	Mem::Manager::sHandle().PopContext();

	return totalSize;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8* CSkaterProfile::ReadFromBuffer(uint8 *pBuffer)
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());

	pBuffer = m_Appearance.ReadFromBuffer(pBuffer);

	GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") )->Clear();
	pBuffer = Script::ReadFromBuffer(GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") ), pBuffer);
	
//	decompress_trick_mappings( "trick_mapping" );

	Script::CStruct* pStructure = GetSpecialTricksStructure();
	Dbg_Assert( pStructure );
	pStructure->Clear();
	pBuffer = Script::ReadFromBuffer(pStructure, pBuffer);

	// read any extra info that is needed in-game
	// (some data, such as your default specials
	// and your default appearance, aren't needed)
	pBuffer = read_extra_info_from_buffer(pBuffer);
	
	Mem::Manager::sHandle().PopContext();

	return pBuffer;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CSkaterProfile::write_extra_info_to_buffer(uint8 *pBuffer, uint32 BufferSize)
{	
	Script::CStruct* pTempStructure = new Script::CStruct;

	Dbg_Assert( pTempStructure );
	
	// for each component in info, see if it's needed in-game...
	// such as the stats, trick style, etc.
	// (most of the other information is only needed in the front end)
	Script::CComponent *pComp = m_Info.GetNextComponent();
	while ( pComp )
	{
		Script::CComponent* pNextComp = m_Info.GetNextComponent( pComp );

		switch ( pComp->mNameChecksum )
		{
			case 0xa1dc81f9:	// name
			case 0x7d02bcc3:	// stance
			case 0xc15dbf86:	// pushstyle
				pTempStructure->AddComponent( pComp->mNameChecksum, ESYMBOLTYPE_NAME, (int)pComp->mChecksum );
				break;

			case 0x2ab66cb8:	// display_name
			case 0x2820e997:	// file_name
			case 0x562e3ecd:	// first_name
				pTempStructure->AddComponent( pComp->mNameChecksum, ESYMBOLTYPE_STRING, pComp->mpString );
				break;

			case 0x439f4704:	// air
			case 0xaf895b3f:	// run
			case 0x9b65d7b8:	// ollie
			case 0xf0d90109:	// speed
			case 0xedf5db70:	// spin
			case 0x9016b4e7:	// switch
			case 0xf73a13e3:	// rail_balance
			case 0xae798769:	// lip_balance
			case 0xb1fc0722:	// manual_balance
			case 0xd82f8ac8:	// is_pro
			case 0x6dcb497c:	// flip_speed
			case 0x3f813177:	// is_male
				pTempStructure->AddComponent( pComp->mNameChecksum, ESYMBOLTYPE_INTEGER, (int)pComp->mIntegerValue );
				break;

			default:
				// doesn't need to go across
				break;
		}

		pComp = pNextComp;
	}

	uint32 totalSize = Script::WriteToBuffer(pTempStructure, pBuffer, BufferSize);
	
	delete pTempStructure;
	
	return totalSize;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8* CSkaterProfile::read_extra_info_from_buffer(uint8 *pBuffer)
{
	Script::CStruct* pTempStructure = new Script::CStruct;

	pBuffer = Script::ReadFromBuffer(pTempStructure, pBuffer);

	m_Info.AppendStructure( pTempStructure );
	
	delete pTempStructure;
	
	return pBuffer;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProfile::compress_trick_mappings( const char* pTrickMappingName )
{

	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	CTrickChecksumTable* pTrickChecksumTable = skate_mod->GetTrickChecksumTable();

	Script::CStruct* pTempStructure;
	if ( !m_Info.GetStructure( pTrickMappingName, &pTempStructure ) )
	{
		Dbg_MsgAssert( 0, ( "Trick mapping %s not found...  already compressed?", pTrickMappingName ) );
	}

	int compressed = 0;
	pTempStructure->GetInteger( CRCD(0x68a9b4e1,"compressed"), &compressed );
	if ( compressed )
	{
		Dbg_MsgAssert( 0, ( "This structure is already compressed" ) );
		return;
	}

	// take all the integers, and convert them to checksums
	Script::CComponent *pComp = pTempStructure->GetNextComponent();
	while ( pComp )
	{
		Script::CComponent* pNextComp = pTempStructure->GetNextComponent( pComp );


		if (pComp && pComp->mType==ESYMBOLTYPE_NAME && pComp->mNameChecksum != CRCD(0x68a9b4e1,"compressed") )
		{
			int compressedIndex = pTrickChecksumTable->GetIndexFromChecksum( pComp->mChecksum );
			if ( compressedIndex != -1 )
			{			
				uint32	nameChecksum = pComp->mNameChecksum; // Mick, need to store this now, as we are about to remove pComp
				pTempStructure->RemoveComponent( nameChecksum );
				pTempStructure->AddComponent( nameChecksum, ESYMBOLTYPE_INTEGER, compressedIndex );
			}
		}
		pComp = pNextComp;
	}

	// add the compressed flag
	pTempStructure->AddComponent( CRCD(0x68a9b4e1,"compressed"), ESYMBOLTYPE_INTEGER, 1 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProfile::decompress_trick_mappings( const char* pTrickMappingName )
{
	
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	CTrickChecksumTable* pTrickChecksumTable = skate_mod->GetTrickChecksumTable();
	
	Script::CStruct* pTempStructure;
	if ( !m_Info.GetStructure( pTrickMappingName, &pTempStructure ) )
	{
		Dbg_MsgAssert( 0, ( "Trick mapping %s not found...  already compressed?", pTrickMappingName ) );
	}

	int compressed = 0;
	pTempStructure->GetInteger( CRCD(0x68a9b4e1,"compressed"), &compressed );
	if ( !compressed )
	{
#ifdef __NOPT_ASSERT__
		Script::PrintContents(&m_Info);
		Script::PrintContents(pTempStructure);
#endif		
		Dbg_MsgAssert( 0, ( "This structure is not compressed" ) );
		return;
	}

	// take all the integers, and convert them to checksums
	Script::CComponent *pComp = pTempStructure->GetNextComponent();
	while ( pComp )
	{
		Script::CComponent* pNextComp = pTempStructure->GetNextComponent( pComp );

		if (pComp && pComp->mType==ESYMBOLTYPE_INTEGER && pComp->mNameChecksum != CRCD(0x68a9b4e1,"compressed") )
		{
			int uncompressedChecksum = pTrickChecksumTable->GetChecksumFromIndex( pComp->mIntegerValue );
			uint32	nameChecksum = pComp->mNameChecksum; // Mick, need to store this now, as we are about to remove pComp
			pTempStructure->RemoveComponent( nameChecksum );
			pTempStructure->AddComponent( nameChecksum, ESYMBOLTYPE_NAME, uncompressedChecksum );
		}

		pComp = pNextComp;
	}

	// reset the compressed flag
	pTempStructure->AddComponent( CRCD(0x68a9b4e1,"compressed"), ESYMBOLTYPE_INTEGER, 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProfile::WriteIntoStructure( Script::CStruct* pStuff )
{	
	Dbg_MsgAssert(pStuff,("NULL pStuff"));
	
	Script::CStruct *pSkaterInfo=new Script::CStruct;

	pSkaterInfo->AddComponent( CRCD(0x1902c8f0,"version_number"), ESYMBOLTYPE_INTEGER, CSkaterProfile::vVERSION_NUMBER );
	
	Script::CStruct *pTemp=NULL;
	
	if (!IsPro())
	{
		pTemp=new Script::CStruct;
		pTemp->AppendStructure( m_Appearance.GetStructure() );
		pSkaterInfo->AddComponent( CRCD(0x554c7d6f,"Appearance"), ESYMBOLTYPE_STRUCTUREPOINTER, (int)pTemp );
	}
		
	Gfx::CFaceTexture* pFaceTexture = m_Appearance.GetFaceTexture();
	if ( pFaceTexture && pFaceTexture->IsValid() )
	{
		pTemp=new Script::CStruct;
		pFaceTexture->WriteIntoStructure( pTemp );
		pSkaterInfo->AddComponent( CRCD(0xf0d3bc94,"FaceTexture"), ESYMBOLTYPE_STRUCTUREPOINTER, (int)pTemp );
	}

	pTemp=new Script::CStruct;
	pTemp->AppendStructure(&m_Info);
	pSkaterInfo->AddComponent( CRCD(0x3476cea8,"Info"), ESYMBOLTYPE_STRUCTUREPOINTER, (int)pTemp );

	uint32 Name=GetSkaterNameChecksum();
	//pSkaterInfo->AddComponent(CRCD(0x95388a60,"DeckFlags"),ESYMBOLTYPE_INTEGER,(int)Front::GetDeckFlags(Name));
	pSkaterInfo->AddComponent( CRCD(0x95388a60,"DeckFlags"), ESYMBOLTYPE_INTEGER, 0 );
	
	pStuff->AddComponent(Name,ESYMBOLTYPE_STRUCTUREPOINTER,(int)pSkaterInfo);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProfile::ReadFromStructure( uint32 SkaterName, Script::CStruct* pStuff )
{
	// should only get called for pros,
	// otherwise we need to implement face texture loading

	Dbg_MsgAssert(pStuff,("NULL pStuff"));
	
	Script::CStruct *pSkaterInfo=NULL;
	pStuff->GetStructure(SkaterName,&pSkaterInfo);
	// If no skater, just return. Could be no skater if a new skater name has been added, but autoloading off
	// a memory card with no info for that skater, so no assert.
	if (!pSkaterInfo)
	{
		return;
	}	
		
	Script::CStruct *pTemp=NULL;
	
	// There may not be an Appearance member, eg for the Pros.	
	if (pSkaterInfo->GetStructure( CRCD(0x554c7d6f,"Appearance"), &pTemp ))
	{
		m_Appearance.Load(pTemp);
	}	
		
	pTemp=NULL;
	pSkaterInfo->GetStructure( CRCD(0x3476cea8,"Info"), &pTemp );
	m_Info.Clear();
	m_Info.AppendStructure( pTemp);
	
	// First deck is always unlocked, so make sure this is the default just in case DeckFlags is not found.
	int DeckFlags=1;
	pSkaterInfo->GetInteger( CRCD(0x95388a60,"DeckFlags"), &DeckFlags );
	
	//Front::SetDeckFlags(SkaterName,(uint32)DeckFlags);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CSkaterProfile::GetStatValue( uint32 property )
{
	int value;
	m_Info.GetInteger( property, &value, true );
	return value;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterProfile::SetPropertyValue( uint32 property, int value )
{
	if ( value >= 0 && value <= 10 )
	{
		m_Info.AddInteger( property, value );
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterProfile::AwardStatPoint( int inc_val )
{
	int value;
	m_Info.GetInteger( CRCD(0x471fc4dd,"points_available"), &value, true );
	value+=inc_val;
	m_Info.AddComponent( CRCD(0x471fc4dd,"points_available"), ESYMBOLTYPE_INTEGER, value );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CSkaterProfile::GetNumStatPointsAvailable()
{
	int value;
	m_Info.GetInteger( CRCD(0x471fc4dd,"points_available"), &value, true );
	return value;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterProfile::AwardSpecialTrickSlot( int inc_val )
{
	int value;
	m_Info.GetInteger( CRCD(0xac9b9eda,"max_specials"), &value, true );
	value+=inc_val;
	m_Info.AddComponent( CRCD(0xac9b9eda,"max_specials"), ESYMBOLTYPE_INTEGER, value );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CSkaterProfile::GetNumSpecialTrickSlots( void )
{
	int value;
	m_Info.GetInteger( CRCD(0xac9b9eda,"max_specials"), &value, true );
	return value;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SSpecialTrickInfo CSkaterProfile::GetSpecialTrickInfo( int slot_num )
{	
	SSpecialTrickInfo theInfo;
	
	Script::CStruct* pStructure = GetSpecialTricksStructure();
	Dbg_Assert( pStructure );

	Script::CArray* pArray;
	pStructure->GetArray( NONAME, &pArray, true );
	Dbg_MsgAssert( pArray->GetSize() == vMAXSPECIALTRICKSLOTS, ( "Expected %i elements in specials array instead of %d", vMAXSPECIALTRICKSLOTS, pArray->GetSize() ) );

	if ( !(slot_num >= 0 && slot_num < (int)pArray->GetSize()) )
	{
		Script::PrintContents( &m_Info );
		Script::PrintContents( pStructure );
		Dbg_MsgAssert( 0, ( "Slot index %d is out of range (must be between 0 and %d", slot_num, vMAXSPECIALTRICKSLOTS ) );
	}
	
	Script::CStruct* pSubStructure = pArray->GetStructure( slot_num );
	pSubStructure->GetChecksum( CRCD(0x5b077ce1,"trickname"), &theInfo.m_TrickName, true );
	pSubStructure->GetChecksum( CRCD(0xa92a2280,"trickslot"), &theInfo.m_TrickSlot, true );
	int is_cat = 0;
	pSubStructure->GetInteger( CRCD(0xb56a8816,"isCat"), &is_cat, false );
	if ( is_cat != 0 )
	{
		theInfo.m_isCat = true;
	}
	
	return theInfo;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterProfile::SetSpecialTrickInfo( int slot_num, const SSpecialTrickInfo& theInfo, bool update_mappings )
{
	Script::CStruct* pStructure = GetSpecialTricksStructure();
	Dbg_Assert( pStructure );

	Script::CArray* pArray;
	pStructure->GetArray( NONAME, &pArray, true );
	Dbg_MsgAssert( pArray->GetSize() == vMAXSPECIALTRICKSLOTS, ( "Expected %i elements in specials array instead of %d", vMAXSPECIALTRICKSLOTS, pArray->GetSize() ) );

	if ( !(slot_num >= 0 && slot_num < (int)pArray->GetSize()) )
	{
		// out of range index...
		// this is possible if you press the square button while on the "Done" element
		// this isn't the best place to screen this out, but it's the safest...
		return false;
	}
	
	uint32 trickName = theInfo.m_TrickName;
	uint32 trickSlot = theInfo.m_TrickSlot;

	if ( theInfo.IsUnassigned() )
	{
		// if the incoming trick is "unassigned"
		// then clear that slot
		trickName = CRCD(0xf60c9090,"unassigned");
		trickSlot = CRCD(0xf60c9090,"unassigned");
	}
	
	Script::CStruct* pSubStructure = pArray->GetStructure( slot_num );
	pSubStructure->AddComponent( CRCD(0x5b077ce1,"trickname"), ESYMBOLTYPE_NAME, (int)trickName );
	pSubStructure->AddComponent( CRCD(0xa92a2280,"trickslot"), ESYMBOLTYPE_NAME, (int)trickSlot );

	if ( theInfo.m_isCat )
	{
		pSubStructure->AddInteger( CRCD(0xb56a8816,"isCat"), 1 );
	}
	else
		pSubStructure->RemoveComponent( CRCD(0xb56a8816,"isCat") );

	// now that a trick config has changed,
	// update the trick mappings on any existing skaters
	if ( update_mappings )
	{
        Mdl::Skate * pSkate = Mdl::Skate::Instance();
        Obj::CSkater* pSkater = pSkate->GetLocalSkater();
        if ( pSkater )
         {
            Obj::CTrickComponent* pTrickComponent = GetTrickComponentFromObject(pSkater);
    		Dbg_Assert( pTrickComponent );
    		pTrickComponent->UpdateTrickMappings( this );
        }
	}
	Script::RunScript( CRCD(0xfab5c6eb,"disable_replays") );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CSkaterProfile::GetChecksumValue( uint32 field_id )
{
	uint32 value = 0;

	switch ( field_id )
	{
		case 0xc15dbf86:	// pushstyle
		{
			m_Info.GetChecksum( CRCD(0xc15dbf86,"pushstyle"), &value, true );
		}
		break;
		case 0x7d02bcc3:	// stance
		{
			m_Info.GetChecksum( CRCD(0x7d02bcc3,"stance"), &value, true );
		}
		break;
		default:
			Dbg_MsgAssert( 0, ("Unrecognized property %s", Script::FindChecksumName(field_id) ) );
			break;
	}

	return value;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProfile::SetSkaterProperty( uint32 field_id, const char* pPropertyString )
{
	Dbg_Message("Stub:  setting skater property %s: %s", Script::FindChecksumName(field_id), pPropertyString);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Str::String	CSkaterProfile::GetUIString( const char* pFieldName )
{
	return GetUIString( Script::GenerateCRC( pFieldName ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Str::String	CSkaterProfile::GetUIString( uint32 fieldID )
{
	return "Unimplemented";
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CSkaterProfile::GetTrickMapping( uint32 TrickMappingName )
{
	// pTrickMapping must be "trick_mapping"...  specials are accessed through special trick slots
	Dbg_Assert( TrickMappingName==CRCD(0xd544aa2d,"trick_mapping") );
	
	Script::CStruct* pTrickMappingStructure;
	m_Info.GetStructure(TrickMappingName,&pTrickMappingStructure,Script::ASSERT);
	return pTrickMappingStructure;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CSkaterProfile::GetSpecialTricksStructure()
{
	Script::CStruct* pTrickMappingStructure;
	m_Info.GetStructure( CRCD(0xddbee809,"specials"), &pTrickMappingStructure, Script::ASSERT);
	return pTrickMappingStructure;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CSkaterProfile::GetSkaterNameChecksum( void )
{
	uint32 checksum;
	m_Info.GetChecksum( CRCD(0xa1dc81f9,"name"), &checksum, true );
	return checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterProfile::IsPro()
{
	int is_pro;
	m_Info.GetInteger( CRCD(0xd82f8ac8,"is_pro"), &is_pro, true );
	return is_pro;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterProfile::HeadIsLocked()
{
	int is_locked;
	m_Info.GetInteger( CRCD(0x09f350e5,"is_head_locked"), &is_locked, true );
	return is_locked;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterProfile::IsLocked()
{
	int is_locked;
	m_Info.GetInteger( CRCD(0x4564bab1,"is_locked"), &is_locked, true );
	return is_locked;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterProfile::IsSecret()
{
	return m_Info.ContainsFlag( CRCD(0xf3149ac5,"is_secret") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProfile::SetHeadIsLocked( bool is_locked )
{
	m_Info.AddComponent( CRCD(0x09f350e5,"is_head_locked"), ESYMBOLTYPE_INTEGER, (int)is_locked );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char *CSkaterProfile::GetCASFileName()
{
	const char *p_file_name="Unimplemented";
	m_Info.GetString( CRCD(0xf36c1878,"CASFileName"), &p_file_name);
	return p_file_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProfile::SetCASFileName(const char *pFileName)
{
	m_Info.AddString( CRCD(0xf36c1878,"CASFileName"), pFileName );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* CSkaterProfile::GetDisplayName()
{
	const char* pDisplayName;
	m_Info.GetText( CRCD(0x2ab66cb8,"display_name"), &pDisplayName, Script::ASSERT );
	return pDisplayName;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProfile::ResetDefaultAppearance()
{
	Dbg_Message( "Stub:  Reset appearance here" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProfile::ResetDefaultStats()
{
	Dbg_Message( "Stub:  Reset stats here" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterProfile::ResetDefaultTricks()
{
	Dbg_Message( "Stub:  Reset tricks here" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj


================================================
FILE: Code/Sk/Objects/SkaterProfile.h
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       SkaterProfile.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  11/29/2000
//****************************************************************************

#ifndef __OBJECTS_SKATERPROFILE_H
#define __OBJECTS_SKATERPROFILE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

// TODO:  Eventually, rename this CPlayerProfile,
// and subclass to get the CSkaterProfile


class CSkaterProfile : public Spt::Class
{
public:
	enum
	{
		vVERSION_NUMBER = 2,
		vMAXSPECIALTRICKSLOTS = 12,
	};

public:
	// slowly phase this first constructor out
	CSkaterProfile();							// set up default profile
	CSkaterProfile( Script::CStruct* pParams );
	CSkaterProfile( const CSkaterProfile& rhs );
	CSkaterProfile&	operator=( const CSkaterProfile& rhs );

public:
	uint32						WriteToBuffer(uint8 *pBuffer, uint32 BufferSize, 
											bool ignoreFaceData = false );
	uint8*						ReadFromBuffer(uint8 *pBuffer);
	void 						WriteIntoStructure( Script::CStruct* pStuff );
	void 						ReadFromStructure( uint32 SkaterName, Script::CStruct* pStuff );

	bool						Reset( Script::CStruct* pParams = NULL );						// reset to initial conditions
    bool						PartialReset( Script::CStruct* pParams = NULL );				// reset non story stuff
	
	// for querying whether the profile meets certain criteria (i.e. is vert skater?  is rodney mullen?)
	bool						ProfileEquals( Script::CStruct* pStructure );
	
	void						ResetDefaultAppearance();										   	
	void						ResetDefaultTricks();
	void						ResetDefaultStats();
	
public:
	// general accessors
	Gfx::CModelAppearance*		GetAppearance() {return &m_Appearance;}
	Script::CStruct*			GetInfo() {return &m_Info;}
	
	// query functions
	uint32						GetSkaterNameChecksum();
	Script::CStruct*			GetTrickMapping( uint32 TrickMappingName );
	Script::CStruct*			GetSpecialTricksStructure();
	void						SetHeadIsLocked( bool is_locked );
	bool						IsPro();
	bool						IsSecret();
	bool						HeadIsLocked();
	bool						IsLocked();
	const char*					GetCASFileName();
	void						SetCASFileName(const char *pFileName);
	
	// UI
	void						SetSkaterProperty( uint32 fieldId, const char* pPropertyName );
	const char*					GetDisplayName();
	uint32						GetChecksumValue( uint32 field_id );
	Str::String					GetUIString( uint32 field_id );
	Str::String					GetUIString( const char* field_name );
	
	// stats
	int							GetStatValue( uint32 property );
	bool						SetPropertyValue( uint32 property, int value );
	bool						AwardStatPoint( int inc_val = 1 );
	int							GetNumStatPointsAvailable();
	
	// tricks
	bool						AwardSpecialTrickSlot( int inc_val = 1 );
	uint32						GetNumSpecialTrickSlots();
	SSpecialTrickInfo			GetSpecialTrickInfo( int slot_num );
	bool						SetSpecialTrickInfo( int slot_num, const SSpecialTrickInfo& theInfo, bool update_mappings = true );

protected:
	void						compress_trick_mappings( const char* pTrickMappingName );
	void						decompress_trick_mappings( const char* pTrickMappingName );
	
protected:
	// net packet compression
	uint32						write_extra_info_to_buffer(uint8* pBuffer, uint32 BufferSize);
	uint8*						read_extra_info_from_buffer(uint8* pBuffer);

private:
	Gfx::CModelAppearance		m_Appearance;
	Script::CStruct				m_Info;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/


} // namespace Obj

#endif	// __OBJECTS_SKATERPROFILE_H

#if 0
	// UI
	void						SetSkaterProperty( uint32 fieldId, int value );
	int							GetUIValue( uint32 checksum );
	Str::String					GetUIString( uint32 checksum );
#endif

#if 0
	// temp functions
	bool						part_matches( uint32 list_name, uint32 desc_name, Script::CStruct* pIfStructure );
	bool						sponsor_is_disqualified( uint32 partChecksum, uint32 descChecksum, Script::CArray* pSponsorArray );
	bool						part_is_disqualified( uint32 list_name, uint32 desc_id );
	bool						appearance_currently_contains( Script::CStruct* pIfStructure );
#endif

#if 0
	// for setting up the skater model
	bool						PartIsDisqualified( uint32 list_name, uint32 desc_id );
	bool						ColorModulationAllowed( uint32 list_name );
	
	bool						SetCASOption(uint32 setName, const char* pDesiredName);
	bool						SetOption(uint32 setName, uint32 descId);

	void						SetHue( uint32 setName, int value );
	void						SetSaturation( uint32 setName, int value );
	void						SetValue( uint32 setName, int value );
	
	int							GetNumOptions( uint32 list_name );
	Str::String					OptionName( uint32 list_name, int i );
	uint32						OptionId( uint32 list_name, int i );
	bool						OptionSelected( uint32 list_name, int i );
	bool						OptionDisqualified( uint32 list_name, int i );
	uint32						CurrentOption( uint32 list_name );
	Str::String					GetOptionNameFromDescId( uint32 list_name, uint32 desc_id );
	void						RandomizeAppearance();
#endif



================================================
FILE: Code/Sk/Objects/SkaterTricks.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Objects (OBJ) 											**
**																			**
**	File name:		objects/SkaterTricks.h									**
**																			**
**	Created: 		10/29/01	-	gj										**
**																			**
**	Description:	Skater tricks code										**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/
	
/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SSpecialTrickInfo::SSpecialTrickInfo()
{
	m_isCat = false;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool SSpecialTrickInfo::IsUnassigned ( void ) const
{
	return m_TrickName == Script::GenerateCRC( "Unassigned" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Str::String SSpecialTrickInfo::GetTrickNameString ( void ) const
{
	if ( IsUnassigned() )
	{
		return Script::GetLocalString( "sp_str_unassigned" );
	}

	Script::CScriptStructure* pTrickStructure;
	if ( ( Script::GetStructure( m_TrickName, Script::ASSERT ) )->GetStructure( "params", &pTrickStructure ) )
	{
		// get the trick name
		const char* pTrickName;
		pTrickStructure->GetLocalText( "name", &pTrickName, true );
		return pTrickName;
	}

	return "Unknown";
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Str::String SSpecialTrickInfo::GetTrickSlotString ( void ) const
{
	if ( IsUnassigned() )
	{
		return Script::GetLocalString( "sp_str_unassigned" );
	}

	Script::CArray* pButtonArray = Script::GetArray( "SpecialCombos" );
	Dbg_Assert( pButtonArray );

	for ( int i = 0; i < (int)pButtonArray->GetSize(); i++ )
	{
		Script::CScriptStructure* pStructure = pButtonArray->GetStructure( i );
		Dbg_Assert( pStructure );

		uint32 currTrickSlot;
		pStructure->GetChecksum( "trickslot", &currTrickSlot, true );

		if ( currTrickSlot == m_TrickSlot )
		{
			const char* pTrickSlotString;
			pStructure->GetText( "desc", &pTrickSlotString, true );
			return pTrickSlotString;
		}	
	}

	return "Unknown";
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Str::String SSpecialTrickInfo::GetSlotMenuProperty ( void ) const
{
	if ( IsUnassigned() )
	{
		return "trick_menu_property";
	}
	else
	{
		return "trickslot_menu_property";
	}
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickChecksumTable::CTrickChecksumTable( void )
{
	m_NumChecksums = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickChecksumTable::Init( void )
{
	m_NumChecksums = 0;

	Script::CArray* pArray = Script::GetArray( "ConfigurableTricks" );

	for ( int i = 0; i < (int)pArray->GetSize(); i++ )
	{
		uint32 trickChecksum = pArray->GetNameChecksum( i );

		// not found
		if ( GetIndexFromChecksum( trickChecksum ) == -1 )
		{
			Dbg_MsgAssert( m_NumChecksums < vMAX_TRICK_CHECKSUMS, ("Too many trick checksums (limit = %d)", vMAX_TRICK_CHECKSUMS) );
			m_Checksum[m_NumChecksums] = trickChecksum;
			m_NumChecksums++;
		}
	}
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CTrickChecksumTable::GetChecksumFromIndex( int index )
{
		
	Dbg_Assert( index >= 0 && index < m_NumChecksums );

	return m_Checksum[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CTrickChecksumTable::GetIndexFromChecksum( uint32 checksum )
{
	for ( int i = 0; i < m_NumChecksums; i++ )
	{
		if ( checksum == m_Checksum[i] )
		{
			// found index
			return i;
		}
	}

	// not found
	return -1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj


================================================
FILE: Code/Sk/Objects/SkaterTricks.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Object (OBJ)											**
**																			**
**	File name:		objects/SkaterTricks.h									**
**																			**
**	Created: 		10/29/01	-	gj										**
**																			**
*****************************************************************************/

#ifndef __OBJECTS_SKATERTRICKS_H
#define __OBJECTS_SKATERTRICKS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class  SSpecialTrickInfo : public Spt::Class
{
	public:
		uint32	m_TrickName;
		uint32	m_TrickSlot;
		bool	m_isCat;

	public:
		SSpecialTrickInfo();
		bool			IsUnassigned() const;
		Str::String		GetTrickNameString() const;
		Str::String		GetTrickSlotString() const;
		Str::String		GetSlotMenuProperty() const;
};

class CTrickChecksumTable : public Spt::Class
{
	enum
	{
		// must be 256 or less, otherwise
		// the trick mapping database might
		// not compress to an acceptable size
		vMAX_TRICK_CHECKSUMS = 256
	};

	public:
		CTrickChecksumTable();

	public:
		void			Init( void );
		uint32			GetChecksumFromIndex( int index );
		int				GetIndexFromChecksum( uint32 checksum );

	protected:
		uint32			m_Checksum[vMAX_TRICK_CHECKSUMS];
		int				m_NumChecksums;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_SKATERTRICKS_H


================================================
FILE: Code/Sk/Objects/TrickObject.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			TrickObject (OBJ)										**
**																			**
**	File name:		TrickObject.cpp											**
**																			**
**	Created by:		03/05/01	-	gj										**
**																			**
**	Description:	trick object code										**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 		   // just needed in mygetassociatednetworkconnection

#include 

#include 

#ifdef __NOPT_ASSERT__	
//#include 
#endif

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

DefinePoolableClass(Lst::HashItem);


enum
{
	vMAX_TRICKS_TO_FREE = 128
};

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Obj
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/
   
/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickObject::CTrickObject( void ) : Lst::Node< CTrickObject >(this)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickObject::CTrickObject( uint32 name_checksum ) : Lst::Node< CTrickObject >(this)
{
	m_NameChecksum = name_checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObject::InitializeTrickObjectColor( int seqIndex )
{
	// Garrett: shouldn't need to do anything except clear color here
	// checks for the wibbling data, and creates
	// it if it doesn't already exist

	// TODO:  Should also screen out if it doesn't have any geometry
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_NameChecksum);				
	Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",m_NameChecksum,Script::FindChecksumName(m_NameChecksum)));
	p_sector->ClearColor();
	if (!Config::CD())
	{
		if ( Script::GetInt( "show_all_trick_objects", false ) )
		{
			// quick way to see all the trick objects in the scene
			ModulateTrickObjectColor( 2 );
		}
	}	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObject::ModulateTrickObjectColor( int seqIndex )
{
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_NameChecksum);				
	Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",m_NameChecksum,Script::FindChecksumName(m_NameChecksum)));

	Script::CArray* p_graffiti_col_tab = Script::GetArray( "graffitiColors" );
	Dbg_MsgAssert( (uint) seqIndex < p_graffiti_col_tab->GetSize(), ( "graffitiColors array too small" ) );

	Script::CArray *p_entry = p_graffiti_col_tab->GetArray(seqIndex);
	
	#ifdef	__NOPT_ASSERT__
	int size = p_entry->GetSize();
	Dbg_MsgAssert(size >= 3 && size <= 4, ("wrong size %d for color array", size));
	#endif
	
	Image::RGBA color;

	color.r = p_entry->GetInteger( 0 );
	color.g = p_entry->GetInteger( 1 );
	color.b = p_entry->GetInteger( 2 );
	color.a = 128;

	p_sector->SetColor(color);
	return true;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObject::ClearTrickObjectColor( int seqIndex )
{
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_NameChecksum);				
	Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",m_NameChecksum,Script::FindChecksumName(m_NameChecksum)));

	Script::CArray* p_graffiti_col_tab = Script::GetArray( "graffitiColors" );
	Dbg_MsgAssert( (uint) seqIndex < p_graffiti_col_tab->GetSize(), ( "graffitiColors array too small" ) );

	Script::CArray *p_entry = p_graffiti_col_tab->GetArray(seqIndex);
	
	#ifdef	__NOPT_ASSERT__
	int size = p_entry->GetSize();
	Dbg_MsgAssert(size >= 3 && size <= 4, ("wrong size %d for color array", size));
	#endif

	Image::RGBA color;

	color.r = p_entry->GetInteger( 0 );
	color.g = p_entry->GetInteger( 1 );
	color.b = p_entry->GetInteger( 2 );
	color.a = 128;

	Image::RGBA orig_color = p_sector->GetColor();
	if ((orig_color.r == color.r) &&
		(orig_color.g == color.g) &&
		(orig_color.b == color.b))
	{
		p_sector->ClearColor();
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickCluster::CTrickCluster( void ) : Lst::Node< CTrickCluster >(this)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickCluster::CTrickCluster( uint32 name_checksum ) : Lst::Node< CTrickCluster >(this)
{
	

	m_NameChecksum = name_checksum;

	Reset();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickCluster::~CTrickCluster( void )
{
	

	uint32 count = m_TrickObjectList.CountItems();

	// first iterate through all the items,
	// and destroy the associated objects
	for ( uint32 i = 0; i < count; i++ )
	{
		Lst::Node< CTrickObject >* pNode = m_TrickObjectList.GetItem( 0 );
		Dbg_Assert( pNode );
		CTrickObject* pObject = pNode->GetData();
		Dbg_Assert( pObject );
		delete pObject;
	}

	// now remove all the nodes from the list
	m_TrickObjectList.RemoveAllNodes();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CTrickCluster::GetScore( uint32 skater_id )
{
	return ( m_IsOwned && (skater_id == m_OwnerId) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickCluster::Reset( void )
{
	

	m_Score = 0;
	m_OwnerId = 0;
	m_IsOwned = false;	// no owner
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CTrickCluster::ModulateTrickObjectColor( int seqIndex )
{
	

	uint32 count = m_TrickObjectList.CountItems();

	for ( uint32 i = 0; i < count; i++ )
	{
		Lst::Node< CTrickObject >* pNode = m_TrickObjectList.GetItem( i );
		Dbg_Assert( pNode );
		CTrickObject* pObject = pNode->GetData();
		Dbg_Assert( pObject );
		pObject->ModulateTrickObjectColor( seqIndex );
	}

	// If playing a created park, update any created rails for this cluster.
	Mdl::Skate * p_skate_mod =  Mdl::Skate::Instance();
	if (p_skate_mod->m_cur_level == CRCD(0xb664035d,"load_sk5ed_gameplay"))
	{
		Obj::GetRailEditor()->ModulateRailColor(m_NameChecksum, seqIndex);
	}	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickObject* CTrickCluster::find_trick_object( uint32 name_checksum )
{
	

	uint32 count = m_TrickObjectList.CountItems();

	// find the appropriate trick object
	for ( uint32 i = 0; i < count; i++ )
	{
		Lst::Node< CTrickObject >* pNode = m_TrickObjectList.GetItem( i );
		Dbg_Assert( pNode );
		CTrickObject* pObject = pNode->GetData();
		Dbg_Assert( pObject );

		if ( pObject->m_NameChecksum == name_checksum )
		{
			return pObject;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickCluster::AddTrickObject( uint32 name_checksum )
{
	

	Dbg_MsgAssert( !find_trick_object( name_checksum ),( "Trick object %s is already in list", Script::FindChecksumName(name_checksum) ));
	
	CTrickObject* pObject = new CTrickObject( name_checksum );

	if ( !pObject )
	{
		return false;
	}

	if ( pObject->InitializeTrickObjectColor( name_checksum ) )
	{
//		printf( "Adding %s to trick object list\n", Script::FindChecksumName( name_checksum ) );
		m_TrickObjectList.AddToTail( pObject );
		return true;
	}
	else
	{
		// if not valid
		delete pObject;
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickObjectManager::CTrickObjectManager( void ) : m_TrickClusterList(8), m_TrickAliasList(8)
{
//	m_TrickAliasCount = 0;

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());

	mp_ObserverState = new Script::CScriptStructure;

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickObjectManager::~CTrickObjectManager( void )
{
	this->DeleteAllTrickObjects();
}

struct STrickObjectFreeInfo
{
	uint32	skater_id;			// which objects to free
	uint32	num_tricks;			// num tricks to free
	uint32	trick_buffer[vMAX_TRICKS_TO_FREE];
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickCluster::FreeCluster( uint32 skater_id )
{
	if ( this->m_IsOwned && ( this->m_OwnerId == skater_id ) )
	{
		this->m_IsOwned = false;
		this->m_Score = 0;
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickCluster::ClearCluster( int seqIndex )
{

	uint32 count = m_TrickObjectList.CountItems();

	// find the appropriate trick object
	for ( uint32 i = 0; i < count; i++ )
	{
		Lst::Node< CTrickObject >* pNode = m_TrickObjectList.GetItem( i );
		Dbg_Assert( pNode );
		CTrickObject* p_object = pNode->GetData();
		Dbg_Assert( p_object );

		if (!p_object->ClearTrickObjectColor(seqIndex))
		{
			return false;
		}
	}
	
	// If playing a created park, clear any created rail colors for this cluster.
	Mdl::Skate * p_skate_mod =  Mdl::Skate::Instance();
	if (p_skate_mod->m_cur_level == CRCD(0xb664035d,"load_sk5ed_gameplay"))
	{
		Obj::GetRailEditor()->ClearRailColor(m_NameChecksum);
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void free_trick_cluster(CTrickCluster* pCluster, void* pData)
{	
	Dbg_AssertPtr(pCluster);
	Dbg_AssertPtr(pData);

	STrickObjectFreeInfo* pFreeInfo = (STrickObjectFreeInfo*)pData;

	if ( pCluster->FreeCluster( pFreeInfo->skater_id ) )
	{
		Dbg_MsgAssert( pFreeInfo->num_tricks < vMAX_TRICKS_TO_FREE, ( "Too many trick objects to free" ) );
		pFreeInfo->trick_buffer[pFreeInfo->num_tricks] = pCluster->GetNameChecksum();		
		pFreeInfo->num_tricks++;

#ifdef __USER_GARY__
		Dbg_Message( "Freeing up %s\n", Script::FindChecksumName(pCluster->GetNameChecksum()) );
#endif
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObjectManager::FreeTrickObjects( uint32 skater_id )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
    
	// clear the observer state when you exit the game
	printf( "Clearing observer state\n" );
	mp_ObserverState->Clear();
	
    if( gamenet_man->OnServer())
	{
		STrickObjectFreeInfo theFreeInfo;
		theFreeInfo.skater_id = skater_id;
		theFreeInfo.num_tricks = 0;

		m_TrickClusterList.HandleCallback(free_trick_cluster, (void*)&theFreeInfo);

		if ( theFreeInfo.num_tricks <= 0 )
		{
			// no tricks to free
			return false;
		}

		// send score updates, as something has changed
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		skate_mod->SendScoreUpdates( false );
	}
    
	clear_trick_clusters(skater_id + 1);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CTrickObjectManager::TrickObjectExists( uint32 name_checksum ){
	// gets cluster checksum
	CTrickCluster* pCluster = get_aliased_cluster( name_checksum );
	if ( pCluster )
	{
		return pCluster->m_NameChecksum;
	}
	else
	{
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CTrickObjectManager::RequestLogTrick( uint32 num_pending_tricks, uint32* p_pending_tricks, char* p_inform_prev_owner, int skater_id, uint32 score )
{
    printf("RequestLogTrick called\n");

// GJ:  Note that this function only logs the trick;  a message
// will be sent out to the clients to actually modify the color

	uint32 i;

	Dbg_Assert( p_pending_tricks );
	Dbg_Assert( p_inform_prev_owner );

	Dbg_Assert( num_pending_tricks < 128 );
	static char s_change_mask[128];
	for ( i = 0; i < num_pending_tricks; i++ )
	{
		s_change_mask[i] = 0;
	}

	for ( i = 0; i < num_pending_tricks; i++ )
	{
		CTrickCluster* pCluster = get_aliased_cluster( p_pending_tricks[i] );

		// change only objects marked as trick objects
		if ( !pCluster )
			continue;

		// tell the goal manager
		Game::CGoalManager* pGoalManager = Game::GetGoalManager();
		Dbg_Assert( pGoalManager );
		pGoalManager->GotTrickObject( pCluster->GetNameChecksum(), score );

		// if the score beats the score
		if ( score > pCluster->m_Score )
		{
            bool owner_changed = ( pCluster->m_OwnerId != (uint32)skater_id );
			
			if ( !pCluster->m_IsOwned || owner_changed )
			{
				// that the trick object has changed states
				s_change_mask[i] = 1;
			}
			
			if ( pCluster->m_IsOwned && owner_changed )
			{
				// for tracking steal messages
				Dbg_Assert( pCluster->m_OwnerId >= 0 && pCluster->m_OwnerId < GameNet::vMAX_PLAYERS );
				p_inform_prev_owner[pCluster->m_OwnerId] = 1;
			}

			pCluster->m_OwnerId = skater_id;
			pCluster->m_Score = score;
			pCluster->m_IsOwned = true;

/*            Game::CGoalManager* p_GoalManager = Game::GetGoalManager();
            if ( p_GoalManager->IsInGraffitiGoal() )
            {
                p_GoalManager->GotGraffitiCluster( pCluster->GetNameChecksum() );
                // printf("tricked in %s\n", Script::FindChecksumName( pCluster->GetNameChecksum() ) );
            }
*/
		}
	}

	uint32 num_changed = 0;
	
	// shift them so that the changed ones are at the beginning
	for ( i = 0; i < num_pending_tricks; i++ )
	{
		if ( s_change_mask[i] )
		{
			p_pending_tricks[num_changed] = p_pending_tricks[i];
			num_changed++;
		}
	}
	
	return num_changed;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickCluster* CTrickObjectManager::GetTrickCluster( uint32 name_checksum )
{
	return find_trick_cluster( name_checksum );
}

struct SAutoTrick
{
	uint32	pPendingTricks[32];
	uint32	numPendingTricks;
};

Net::Conn* MyGetAssociatedNetworkConnection( uint32 skater_id )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Obj::CSkater* skater;
	GameNet::PlayerInfo* player;

	skater = skate_mod->GetSkaterById( skater_id );
	if( skater )
	{
		player = gamenet_man->GetPlayerByObjectID( skater->GetID() );
		if( player )
		{
			return player->m_Conn;
		}
	}

	return NULL;
}

void MyLogTrickObject( int skater_id, int score, uint32 num_pending_tricks, uint32* p_pending_tricks, bool propagate )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	GameNet::MsgScoreLogTrickObject msg;
	GameNet::MsgObsScoreLogTrickObject obs_msg;
	Net::MsgDesc msg_desc;
	Net::Server* server;
	Net::Conn* conn;
	GameNet::PlayerInfo* player;
	Lst::Search< GameNet::PlayerInfo > sh;

	if( propagate )
	{
		server = gamenet_man->GetServer();
		Dbg_Assert( server );

		conn = MyGetAssociatedNetworkConnection( skater_id );

		// keep track of whom we need to send the steal message to
		int i;
		char previous_owner_flags[GameNet::vMAX_PLAYERS];
		for ( i = 0; i < GameNet::vMAX_PLAYERS; i++ )
		{
			previous_owner_flags[i] = 0;
		}

		num_pending_tricks = skate_mod->GetTrickObjectManager()->RequestLogTrick( num_pending_tricks, p_pending_tricks, previous_owner_flags, skater_id, score );

		if ( !num_pending_tricks )
		{
			return;
		}

		for ( i = 0; i < GameNet::vMAX_PLAYERS; i++ )
		{
			if ( previous_owner_flags[i] )
			{
				// GJ:  This only sends the stolen message to the two parties involved

				GameNet::PlayerInfo* pPlayerInfo;
				GameNet::MsgStealMessage steal_msg;
				Net::MsgDesc steal_msg_desc;

				steal_msg.m_NewOwner = skater_id;
				steal_msg.m_OldOwner = i;
				steal_msg.m_GameId = gamenet_man->GetNetworkGameId();

				pPlayerInfo = gamenet_man->GetPlayerByObjectID(skater_id);
				Dbg_Assert( pPlayerInfo );

				steal_msg_desc.m_Data = &steal_msg;
				steal_msg_desc.m_Length = sizeof( GameNet::MsgStealMessage );
				steal_msg_desc.m_Id = GameNet::MSG_ID_STEAL_MESSAGE;
				server->EnqueueMessage( pPlayerInfo->GetConnHandle(), &steal_msg_desc );

				steal_msg.m_NewOwner = skater_id;
				steal_msg.m_OldOwner = i;
				steal_msg.m_GameId = gamenet_man->GetNetworkGameId();

				pPlayerInfo = gamenet_man->GetPlayerByObjectID(i);
				//Dbg_Assert( pPlayerInfo );

				// For now, don't assert if the player doesn't exist anymore. Just don't do anything.
				// Eventually, Gary will fix this so that pieces of exiting players are reset
				if( pPlayerInfo )
				{
					server->EnqueueMessage( pPlayerInfo->GetConnHandle(), &msg_desc );
				}
			}
		}

#ifdef __USER_GARY__
		printf( "Broadcasting %d tricks\n", num_pending_tricks );
#endif

		msg.m_SubMsgId = GameNet::SCORE_MSG_ID_LOG_TRICK_OBJECT;
		msg.m_OwnerId = skater_id;
		msg.m_Score = score;
		msg.m_NumPendingTricks = num_pending_tricks;
		msg.m_GameId = gamenet_man->GetNetworkGameId();

		uint32 max_pending_trick_buffer_size = GameNet::MsgScoreLogTrickObject::vMAX_PENDING_TRICKS * sizeof(uint32);
		uint32 actual_pending_trick_buffer_size = msg.m_NumPendingTricks * sizeof(uint32);
		memcpy( msg.m_PendingTrickBuffer, p_pending_tricks, actual_pending_trick_buffer_size );

		msg_desc.m_Data = &msg;
		msg_desc.m_Length = sizeof( GameNet::MsgScoreLogTrickObject ) - max_pending_trick_buffer_size + actual_pending_trick_buffer_size;
		msg_desc.m_Id = GameNet::MSG_ID_SCORE;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
		// tell players to change their colors
		for( player = gamenet_man->FirstPlayerInfo( sh ); player; 
			 player = gamenet_man->NextPlayerInfo( sh ))
		{
			//server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
			server->StreamMessage( player->GetConnHandle(), msg_desc.m_Id, msg_desc.m_Length, 
						   msg_desc.m_Data, "TrickObj Buffer", GameNet::vSEQ_GROUP_PLAYER_MSGS );
		}

		obs_msg.m_OwnerId = skater_id;
		obs_msg.m_NumPendingTricks = num_pending_tricks;
		obs_msg.m_GameId = gamenet_man->GetNetworkGameId();
		max_pending_trick_buffer_size = GameNet::MsgScoreLogTrickObject::vMAX_PENDING_TRICKS * sizeof(uint32);
		actual_pending_trick_buffer_size = obs_msg.m_NumPendingTricks * sizeof(uint32);
		memcpy( obs_msg.m_PendingTrickBuffer, p_pending_tricks, actual_pending_trick_buffer_size );

		msg_desc.m_Data = &obs_msg;
		msg_desc.m_Length = sizeof( GameNet::MsgObsScoreLogTrickObject ) - max_pending_trick_buffer_size + actual_pending_trick_buffer_size;
		msg_desc.m_Id = GameNet::MSG_ID_OBSERVER_LOG_TRICK_OBJ;
		// tell observers to change their colors
		for( player = gamenet_man->FirstPlayerInfo( sh, true ); player; 
			 player = gamenet_man->NextPlayerInfo( sh, true ))
		{
			if( player->IsObserving())
			{
				//server->EnqueueMessage( player->GetConnHandle(), &msg_desc );
				server->StreamMessage( player->GetConnHandle(), msg_desc.m_Id, msg_desc.m_Length, 
						   msg_desc.m_Data, "TrickObj Buffer", GameNet::vSEQ_GROUP_PLAYER_MSGS );
			}
		}

		// send score updates, as something has changed
		skate_mod->SendScoreUpdates( false );

		// Let the server's client do the rest of the work
		if( conn->IsLocal())
		{
			return;
		}
	}

#ifdef __USER_GARY__
	printf( "Client is receiving %d tricks\n", num_pending_tricks );
#endif

	// modulate the color here
	for ( uint32 i = 0; i < num_pending_tricks; i++ )
	{
		skate_mod->GetTrickObjectManager()->ModulateTrickObjectColor( p_pending_tricks[i], skater_id );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void trick_off_object(CTrickCluster* pCluster, void* pData)
{
	

	Dbg_AssertPtr(pCluster);

	SAutoTrick* pAutoTrick = (SAutoTrick*)pData;

	if ( pAutoTrick->numPendingTricks >= 32 )
	{
		return;
	}

	if ( !pCluster->m_IsOwned )
	{
		pAutoTrick->pPendingTricks[pAutoTrick->numPendingTricks] = pCluster->GetNameChecksum();
		pAutoTrick->numPendingTricks++;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickObjectManager::TrickOffAllObjects( uint32 skater_id )
{
	SAutoTrick theAutoTrick;
	theAutoTrick.numPendingTricks = 0;
	
	m_TrickClusterList.HandleCallback(trick_off_object, &theAutoTrick);

	if ( theAutoTrick.numPendingTricks > 0 )
	{
		MyLogTrickObject( skater_id, 1000, theAutoTrick.numPendingTricks, theAutoTrick.pPendingTricks, true );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObjectManager::ModulateTrickObjectColor( uint32 name_checksum, int skater_id )
{
	

	CTrickCluster* pCluster = get_aliased_cluster( name_checksum );
	
	// change only objects marked as trick objects
	if ( !pCluster )
	{
		// not a trick object
		return false;
	}

	// convert skater_id ->seqIndex, as it's 1-based,
	// or -1 to reset
	int seqIndex = -1;
	if ( skater_id != -1 )
	{
		seqIndex = skater_id + 1;
	}
	
	Dbg_MsgAssert( seqIndex == -1 || ( seqIndex >= 1 && seqIndex <= 8 ) ,( "Out of range seq index %d", seqIndex ));

	pCluster->ModulateTrickObjectColor( seqIndex );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickCluster* CTrickObjectManager::get_aliased_cluster( uint32 alias_checksum )
{
#if 0
	for ( uint32 i = 0; i < m_TrickAliasCount; i++ )
	{
		if ( m_TrickAliasList[i].m_AliasChecksum == alias_checksum )
		{
			return m_TrickAliasList[i].mp_TrickCluster;
		}
	}
	return NULL;
#endif
	return m_TrickAliasList.GetItem(alias_checksum, false);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CTrickCluster* CTrickObjectManager::find_trick_cluster( uint32 cluster_checksum )
{
	

#if 0
	uint32 count = m_TrickClusterList.CountItems();

	// find the appropriate trick object
	for ( uint32 i = 0; i < count; i++ )
	{
		Lst::Node< CTrickCluster >* pNode = m_TrickClusterList.GetItem( i );
		Dbg_Assert( pNode );
		CTrickCluster* pCluster = pNode->GetData();
		Dbg_Assert( pCluster );

		if ( pCluster->m_NameChecksum == cluster_checksum )
		{
			return pCluster;
		}
	}
	return NULL;
#endif
	return m_TrickClusterList.GetItem( cluster_checksum, false );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObjectManager::clear_trick_clusters( int seqIndex )
{

	m_TrickClusterList.IterateStart();
	CTrickCluster *p_cluster;
	while ((p_cluster = m_TrickClusterList.IterateNext()))
	{
		if (!p_cluster->ClearCluster(seqIndex))
		{
			return false;
		}
	}

	return true;
}

// this is useful if you want to examine the functionality
// of a single trick object without sorting through hundreds of
// other trick objects...  it represents one single piece in
// the foundry
//#define __TESTSINGLETRICKOBJECT__

#ifdef __TESTSINGLETRICKOBJECT__
	const char* p_single_cluster_name = "ParkingLotBulldozer";
	const char* p_single_object_name = "Parking_Lot_Bulldozer0";
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObjectManager::AddTrickCluster( uint32 name_checksum )
{
	


#ifdef __TESTSINGLETRICKOBJECT__
	if ( name_checksum != Script::GenerateCRC(p_single_cluster_name) )
		return false;
#endif

#ifdef __USER_GARY__
//	printf( "+++  Adding trick object %s (%x)\n", Script::FindChecksumName(name_checksum), name_checksum );
#endif

	if ( find_trick_cluster( name_checksum ) )
	{
		return false;
	}
	
	// If playing a created park, clear any created rail colors for this cluster.
	Mdl::Skate * p_skate_mod =  Mdl::Skate::Instance();
	if (p_skate_mod->m_cur_level == CRCD(0xb664035d,"load_sk5ed_gameplay"))
	{
		Obj::GetRailEditor()->ClearRailColor(name_checksum);
	}
	
	// add the cluster since it doesn't already exist
	CTrickCluster* pCluster = new CTrickCluster( name_checksum );
	Dbg_Assert( pCluster );
//	m_TrickClusterList.AddToTail( pCluster );
	m_TrickClusterList.PutItem( name_checksum, pCluster );
	m_NumTrickClusters++;
	
#if 0	
	Ed::ParkEditor* park_ed_mod = Ed::ParkEditor::Instance();
	if (!park_ed_mod->IsInitialized())
	{
		Dbg_Assert( m_NumTrickClusters <= 256 );
	}
#endif
	
	// alias the cluster to itself
	AddTrickAlias( name_checksum, name_checksum );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObjectManager::AddTrickObjectToCluster( uint32 name_checksum, uint32 cluster_checksum )
{
	
	
#ifdef __TESTSINGLETRICKOBJECT__
	if ( name_checksum != Script::GenerateCRC(p_single_object_name) )
		return false;
#endif

	CTrickCluster* pCluster = find_trick_cluster( cluster_checksum );
	Dbg_MsgAssert( pCluster,( "cluster %s not found", Script::FindChecksumName( cluster_checksum ) ));

	bool success = pCluster->AddTrickObject( name_checksum );

	// only if not clustering to self
	if ( success && ( name_checksum != cluster_checksum ) )
	{
		// bind the environment object to the cluster
		AddTrickAlias( name_checksum, cluster_checksum );
	}
	
	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObjectManager::AddTrickAlias( uint32 alias_checksum, uint32 cluster_checksum )
{
	
	
#ifdef __TESTSINGLETRICKOBJECT__
	if ( cluster_checksum != Script::GenerateCRC(p_single_cluster_name) )
		return false;
#endif
	
	CTrickCluster* pCluster = get_aliased_cluster( alias_checksum );

	if( pCluster )
	{
		Dbg_MsgAssert( 0,( "%s has already been aliased to %s cluster", Script::FindChecksumName(alias_checksum), Script::FindChecksumName(pCluster->m_NameChecksum) ));
	}

	pCluster = find_trick_cluster( cluster_checksum );

	if( pCluster == NULL )
	{
		Dbg_MsgAssert( 0,( "cluster %s not found", Script::FindChecksumName(cluster_checksum) ));
	}

	m_TrickAliasList.PutItem(alias_checksum, pCluster);
	
#if 0
	Dbg_MsgAssert( m_TrickAliasCount < vMAX_ALIASES,( "Too many aliases.  Couldn't alias %s to cluster %s", Script::FindChecksumName(alias_checksum), Script::FindChecksumName(cluster_checksum) ));
	m_TrickAliasList[m_TrickAliasCount].m_AliasChecksum = alias_checksum;
	pCluster = find_trick_cluster( cluster_checksum );
	m_TrickAliasList[m_TrickAliasCount].mp_TrickCluster = pCluster;
	Dbg_MsgAssert( pCluster,( "cluster %s not found", Script::FindChecksumName(cluster_checksum) ));
	m_TrickAliasCount++;
#endif

//	printf("Adding trick alias %s\n", Script::FindChecksumName(alias_checksum));
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void delete_trick_cluster(CTrickCluster* pCluster, void* pData)
{
	

	Dbg_AssertPtr(pCluster);

	delete pCluster;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObjectManager::DeleteAllTrickObjects( void )
{
	

#if 0
	uint32 count = m_TrickClusterList.CountItems();
	for ( uint32 i = 0; i < count; i++ )
	{
		Lst::Node< CTrickCluster >* pNode = m_TrickClusterList.GetItem( 0 );
		Dbg_Assert( pNode );
		CTrickCluster* pCluster = pNode->GetData();
		Dbg_Assert( pCluster );
		delete pCluster;
	}

	// now remove all the nodes from the list
	m_TrickClusterList.RemoveAllNodes();
#endif

	m_TrickClusterList.HandleCallback(delete_trick_cluster, NULL);
	m_TrickClusterList.FlushAllItems();
	m_NumTrickClusters = 0;
	
//	m_TrickAliasCount = 0;
	m_TrickAliasList.FlushAllItems();
	
	return true;
}

struct STrickObjectScore
{
	int		running_score;
	uint32	skater_id;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void count_score(CTrickCluster* pCluster, void* pData)
{
	

	Dbg_AssertPtr(pCluster);
	Dbg_AssertPtr(pData);
	
	STrickObjectScore* pScore = (STrickObjectScore*)pData;
	pScore->running_score += pCluster->GetScore( pScore->skater_id );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CTrickObjectManager::GetScore( uint32 skater_id )
{
	STrickObjectScore theScore;
	theScore.running_score = 0;
	theScore.skater_id = skater_id;

	m_TrickClusterList.HandleCallback(count_score, (void*)&theScore);

	return theScore.running_score;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

struct SSearchInfo
{
	uint32 checksum;
	uint8 index;
	bool found;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void find_compressed_index(CTrickCluster* pCluster, void* pData)
{
	

	Dbg_AssertPtr(pCluster);
	Dbg_AssertPtr(pData);

	SSearchInfo* pInfo = (SSearchInfo*)pData;
	
	if ( pInfo->found )
	{
		// already found...
		return;
	}
	
	if ( pInfo->checksum == pCluster->GetNameChecksum() )
	{
		pInfo->found = true;
		return;
	}

	// make sure it's less than 256
	Dbg_MsgAssert( pInfo->index < 256, ( "Too many trick aliases" ) );
	
	pInfo->index++;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void find_uncompressed_checksum(CTrickCluster* pCluster, void* pData)
{
	

	Dbg_AssertPtr(pCluster);
	Dbg_AssertPtr(pData);

	SSearchInfo* pInfo = (SSearchInfo*)pData;

	if ( pInfo->found )
	{
		// already found...
		return;
	}

	if ( pInfo->index == 0 )
	{
		pInfo->found = true;
		pInfo->checksum = pCluster->GetNameChecksum();
		return;
	}

	pInfo->index--;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8 CTrickObjectManager::GetCompressedTrickObjectIndex( uint32 name_checksum )
{
	
	
	SSearchInfo theInfo;
	theInfo.index = 0;
	theInfo.checksum = name_checksum;
	theInfo.found = false;
	
	m_TrickClusterList.HandleCallback( find_compressed_index, (void*)&theInfo );

	Dbg_Assert( theInfo.found );

	return theInfo.index;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CTrickObjectManager::GetUncompressedTrickObjectChecksum( uint8 compressed_index )
{
	
	
	SSearchInfo theInfo;
	theInfo.index = compressed_index;
	theInfo.checksum = 0;
	theInfo.found = false;

	m_TrickClusterList.HandleCallback( find_uncompressed_checksum, (void*)&theInfo );

	Dbg_Assert( theInfo.found );
	
	return theInfo.checksum;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

struct SInitGraffitiStateInfo
{
	uint32 skater_id;
	int num_trick_objects;
	GameNet::MsgInitGraffitiState* pMsg;
	Obj::CTrickObjectManager* pManager;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void init_graffiti_state(CTrickCluster* pCluster, void* pData)
{
	
	
	SInitGraffitiStateInfo* pInfo = (SInitGraffitiStateInfo*)pData;

	if ( pCluster->m_IsOwned && pCluster->m_OwnerId == pInfo->skater_id )
	{
		// TODO:  check whether it's already in the list?
		
		Dbg_MsgAssert( pInfo->num_trick_objects < GameNet::vMAX_TRICK_OBJECTS_IN_LEVEL, ( "Too many trick objects in level" ) );
		
		Obj::CTrickObjectManager* pManager = pInfo->pManager;
		pInfo->pMsg->m_TrickObjectStream[pInfo->num_trick_objects] = pManager->GetCompressedTrickObjectIndex( pCluster->GetNameChecksum() );
		pInfo->num_trick_objects++;
		pInfo->pMsg->m_NumTrickObjects[pInfo->skater_id]++;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickObjectManager::SetObserverGraffitiState( Script::CScriptStructure* pScriptStructure )
{
	mp_ObserverState->Clear();
	if (pScriptStructure)
	{
		*mp_ObserverState+=*pScriptStructure;
	}	

#ifdef __NOPT_ASSERT__
	Script::PrintContents(mp_ObserverState);
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickObjectManager::ApplyObserverGraffitiState( void )
{
	for( int i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
	{
		Script::CArray* pArray;

		char arrayName[32];
		sprintf( arrayName, "Skater%d", i );

		if ( mp_ObserverState->GetArray( arrayName, &pArray ) )
		{
			for ( int j = 0; j < (int)pArray->GetSize(); j++ )
			{			
				uint32 checksum = pArray->GetNameChecksum( j );
#ifdef __NOPT_ASSERT__				
				printf( "Modulating array %s\n", Script::FindChecksumName(checksum) );
#endif
				ModulateTrickObjectColor( checksum, i );
			}
		}
	}

	// now that we've done it, we can clear the observer state.
	mp_ObserverState->Clear();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CTrickObjectManager::SetInitGraffitiStateMessage( void* pData )
{
	

	SInitGraffitiStateInfo theInfo;
	theInfo.pMsg = (GameNet::MsgInitGraffitiState*)pData;
	theInfo.pManager = this;
	theInfo.num_trick_objects = 0;

	for ( uint32 i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
	{
		theInfo.pMsg->m_NumTrickObjects[i] = 0;
		theInfo.skater_id = i;
		m_TrickClusterList.HandleCallback( init_graffiti_state, (void*)&theInfo );
        
		printf( "Building graffiti state message %d\n", theInfo.pMsg->m_NumTrickObjects[i] );
	}
	
	return ( sizeof(uint8) * Mdl::Skate::vMAX_SKATERS ) + ( sizeof(uint8) * theInfo.num_trick_objects );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CTrickObjectManager::ResetAllTrickObjects( void )
{
	

	// TODO:
	printf("Resetting all trick objects\n");
	return false;

#if 0
	uint32 count = m_TrickClusterList.CountItems();

	// call the reset function on each trick object in the scene
	for ( uint32 i = 0; i < count; i++ )
	{
		Lst::Node< CTrickCluster >* pNode = m_TrickClusterList.GetItem( i );
		Dbg_Assert( pNode );
		CTrickCluster* pCluster = pNode->GetData();
		Dbg_Assert( pCluster );
		pCluster->Reset();
	}
#endif
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CTrickObjectManager::PrintContents( void )
{
	

	// Remove this line if you need to see the contents
	return;

	printf("**********************\n");
	printf("* TRICK CLUSTER LIST:\n");
	printf("**********************\n");

#if 0
	uint32 i;

	// call the reset function on each trick object in the scene
	for ( i = 0; i < m_TrickClusterList.CountItems(); i++ )
	{
		Lst::Node< CTrickCluster >* pNode = m_TrickClusterList.GetItem( i );
		Dbg_Assert( pNode );
		CTrickCluster* pCluster = pNode->GetData();
		Dbg_Assert( pCluster );
		printf( "Trick cluster %d of %d: %s\n", i, m_TrickClusterList.CountItems(), Script::FindChecksumName( pCluster->m_NameChecksum ) );
	}
#endif
	
#if 0
	printf("**********************\n");
	printf("* TRICK ALIAS LIST:\n");
	printf("**********************\n");

	for ( i = 0; i < m_TrickAliasCount; i++ )
	{
//		printf( "%d: %s is aliased to %s\n", i, Script::FindChecksumName(m_TrickAliasList[i].m_AliasChecksum), Script::FindChecksumName(m_TrickAliasList[i].mp_TrickCluster->m_NameChecksum) );
	}
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPendingTricks::CPendingTricks( void )
{
	m_NumTrickItems = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPendingTricks::TrickOffObject( uint32 checksum )
{
	
	
	uint32 num_trick_items = m_NumTrickItems;
	if ( num_trick_items > vMAX_PENDING_TRICKS )
		num_trick_items = vMAX_PENDING_TRICKS;

	// not a valid trick item
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	CTrickObjectManager* pTrickObjectManager = skate_mod->GetTrickObjectManager();
	Dbg_Assert( pTrickObjectManager );
	checksum = pTrickObjectManager->TrickObjectExists( checksum );
	if ( !checksum )
	{
		return false;
	}
	
	for ( uint32 i = 0; i < num_trick_items; i++ )
	{
		if ( m_Checksum[i] == checksum )
			return false;
	}

//	printf( "Adding object %s to pending tricks:\n", Script::FindChecksumName(checksum) );
	
	// circular buffer
	m_Checksum[(m_NumTrickItems++)%vMAX_PENDING_TRICKS] = checksum;
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CPendingTricks::WriteToBuffer( uint32* p_buffer, uint32 max_size )
{
	

	// TODO:  Account for the circular buffer so that the buffer
	// size can be bigger than the pending trick size
	Dbg_Assert( ( vMAX_PENDING_TRICKS * sizeof(uint32) ) <= max_size );
	
	uint32 num_trick_items = m_NumTrickItems;
	if ( num_trick_items > vMAX_PENDING_TRICKS )
		num_trick_items = vMAX_PENDING_TRICKS;

	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	CTrickObjectManager* pTrickObjectManager = skate_mod->GetTrickObjectManager();
	Dbg_Assert( pTrickObjectManager );

	uint32 size = 0;
	for ( uint32 i = 0; i < num_trick_items; i++ )
	{
#ifdef __NOPT_ASSERT__
		printf( "Looking for %s\n", Script::FindChecksumName( m_Checksum[i] ) );
#endif		
		if ( pTrickObjectManager->TrickObjectExists( m_Checksum[i] ) )
		{
			*p_buffer = m_Checksum[i];
			p_buffer++ ;

			// increment the size
			size += sizeof(uint32);
		}
		
		if ( size >= max_size )
			break;
	}
	return size;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CPendingTricks::FlushTricks( void )
{
	// player has bailed
	m_NumTrickItems = 0;

	return true;
}

} // namespace Obj




================================================
FILE: Code/Sk/Objects/TrickObject.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			TrickObject (OBJ)										**
**																			**
**	File name:		TrickObject.cpp											**
**																			**
**	Created by:		03/05/01	-	gj										**
**																			**
**	Description:	trick object code										**
**																			**
*****************************************************************************/

#ifndef __TRICK_OBJECT_H
#define __TRICK_OBJECT_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 
#include 

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

namespace Script
{
	class CStruct;
}
	
namespace Obj
{

    class CTrickObjectManager;
    class CTrickCluster;

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

// this is the interface through which we will
// modify a specific world sector's colors
class  CTrickObject : public  Lst::Node< CTrickObject > 
{
	

	public:
		friend class	CTrickCluster;

	public:
		CTrickObject( uint32 name_checksum );
		bool		InitializeTrickObjectColor( int seqIndex );
		bool		ModulateTrickObjectColor( int seqIndex );
		bool		ClearTrickObjectColor( int seqIndex );

	protected:
		uint32		m_NameChecksum;

	private:
		CTrickObject( void );
};

// this represents a group of linked trick objects
// when the score contained in the cluster is beaten
// all of the trick objects will change color
class  CTrickCluster : public  Lst::Node< CTrickCluster > 
{
	

	public:
		friend class	CTrickObjectManager;
		friend void trick_off_object( CTrickCluster*, void* );
		friend void init_graffiti_state( CTrickCluster*, void* );

	public:
		CTrickCluster( uint32 name_checksum );
		virtual			~CTrickCluster();
		int				GetScore( uint32 skater_id );
		uint32			GetNameChecksum() { return m_NameChecksum; }
		bool			AddTrickObject( uint32 name_checksum );
		bool			Reset();
		bool			ModulateTrickObjectColor( int seqIndex );
		bool			FreeCluster( uint32 skater_id );
		bool			ClearCluster( int seqIndex );

	protected:
		CTrickObject*	find_trick_object( uint32 name_checksum );

	protected:
		uint32			m_NameChecksum;
		uint32			m_Score;
		uint32			m_OwnerId;
		bool			m_IsOwned;

	// list of objects that get colored when one of the cluster get triggered
		Lst::Head< CTrickObject >		m_TrickObjectList;

	private:
		CTrickCluster( void );
};

class  CTrickObjectManager  : public Spt::Class
{
	

	public :
		CTrickObjectManager( void );
		virtual			~CTrickObjectManager( void );

	public:
		int				GetScore( uint32 skater_id );
		bool			AddTrickCluster( uint32 name_checksum );
		bool			AddTrickObjectToCluster( uint32 name_checksum, uint32 cluster_checksum );
		bool			AddTrickAlias( uint32 alias_checksum, uint32 cluster_checksum );
		bool			DeleteAllTrickObjects();	// wipes out all the trick objects
		bool			ResetAllTrickObjects();		// resets all points to 0
		void			PrintContents();
		bool			ModulateTrickObjectColor( uint32 name_checksum, int skater_id );
		uint32			RequestLogTrick( uint32 num_pending_tricks, uint32* p_pending_tricks, char* p_inform_prev_owner, int skater_id, uint32 score );
		uint32			TrickObjectExists( uint32 name_checksum );
		bool			FreeTrickObjects( uint32 skater_id );
		void			TrickOffAllObjects( uint32 skater_id );
		uint8			GetCompressedTrickObjectIndex( uint32 name_checksum );
		uint32			GetUncompressedTrickObjectChecksum( uint8 compressed_index );
		uint32			SetInitGraffitiStateMessage( void* pMsg );
		void			SetObserverGraffitiState( Script::CStruct* pScriptStructure );
		void			ApplyObserverGraffitiState( void );
		CTrickCluster*	GetTrickCluster( uint32 name_checksum );

		int 			m_NumTrickClusters;
	protected:
		CTrickCluster*	get_aliased_cluster( uint32 alias_checksum );
		CTrickCluster*	find_trick_cluster( uint32 name_checksum );
		bool			clear_trick_clusters( int seqIndex );		
		
	protected:
		// full list of clusters
		Lst::HashTable< CTrickCluster >	m_TrickClusterList;

		// list of checksums that will fire off this cluster
		Lst::HashTable< CTrickCluster >	m_TrickAliasList;

		Script::CStruct* mp_ObserverState;
};

// per-skater
class  CPendingTricks  : public Spt::Class
{
	public:
		enum
		{
			vMAX_PENDING_TRICKS = 512,		// max number of items we can trick off in a combo
			// for now this number must be less or equal to the buffer size in gamemsg.h
		};

		CPendingTricks( void );
		bool		TrickOffObject( uint32 checksum );
		uint32		WriteToBuffer( uint32* p_buffer, uint32 max_size );
		bool		FlushTricks( void );
		uint32		m_Checksum[vMAX_PENDING_TRICKS];
		uint32		m_NumTrickItems;

	protected:

};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __TRICK_OBJECT_H


================================================
FILE: Code/Sk/Objects/ViewerObj.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       ViewerObj.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  1/21/2002
//****************************************************************************

#include 
								 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

// TODO:  Remove the rest of the level when the viewer is active								   
// TODO:  Launch a primary sequence by name, pointer, or index
// TODO:  The shoulder buttons should increment the current anim (next/prev anim)
// TODO:  Don't crash if can't find the animation...
// TODO:  Add handlers so that it can launch new anims and such
// TODO:  Camera controls
// TODO:  Be able to go back to the game
// TODO:  Wireframe	mode
// TODO:  Change skater profile, in real time
// TODO:  Take LODs into account
								   
namespace Obj
{

	const float vSPEED_INCREMENT_AMOUNT = 0.25;
	const Mth::Vector vPOS_VIEWEROBJECT( 0.0f, 0.0f, 0.0f );

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewerObject::s_handle_set_anim( Net::MsgHandlerContext* context )
{
	CViewerObject* pViewerObject;
	pViewerObject = (CViewerObject*) context->m_Data;
	Dbg_Assert( pViewerObject );
	
	Net::MsgViewObjSetAnim* p_msg;
	p_msg = (Net::MsgViewObjSetAnim*) context->m_Msg;
	pViewerObject->SetAnim( p_msg->m_AnimName );
	
	Dbg_Message( "Got request to set anim %s...", Script::FindChecksumName(p_msg->m_AnimName) );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewerObject::s_handle_set_anim_speed( Net::MsgHandlerContext* context )
{
	Dbg_Message( "Got request to set anim speed..." );
	
	CViewerObject* pViewerObject;
	pViewerObject = (CViewerObject*) context->m_Data;
	Dbg_Assert( pViewerObject );

	Net::MsgViewObjSetAnimSpeed* p_msg;
	p_msg = (Net::MsgViewObjSetAnimSpeed*) context->m_Msg;
	pViewerObject->ChangeAnimSpeed( p_msg->m_AnimSpeed );
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewerObject::s_handle_increment_frame( Net::MsgHandlerContext* context )
{
	Dbg_Message( "Got request to increment frame..." );

	CViewerObject* pViewerObject;
	pViewerObject = (CViewerObject*) context->m_Data;
	Dbg_Assert( pViewerObject );
					
	Net::MsgViewObjIncrementFrame* p_msg;
	p_msg = (Net::MsgViewObjIncrementFrame*) context->m_Msg;
	pViewerObject->IncrementFrame( p_msg->m_Forwards );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewerObject::s_handle_reload_anim( Net::MsgHandlerContext* context )
{
	Dbg_Message( "Got request to reload anim..." );

	CViewerObject* pViewerObject;
	pViewerObject = (CViewerObject*) context->m_Data;
	Dbg_Assert( pViewerObject );
					
	Net::MsgViewObjSetAnimFile* p_msg;
	p_msg = (Net::MsgViewObjSetAnimFile*) context->m_Msg;
	pViewerObject->ReloadAnim( p_msg->m_Filename, p_msg->m_checksum );

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewerObject::s_handle_sequence_preview( Net::MsgHandlerContext* context )
{
	char path[256];
	char* script_name;
	CViewerObject* pViewerObject;
	pViewerObject = (CViewerObject*) context->m_Data;
	Dbg_Assert( pViewerObject );

	script_name = context->m_Msg;
	sprintf( path, "scripts\\animview\\%s.qb", script_name );
	SkateScript::LoadQB( path );
	//Script::RunScriptOnObject("RunMeNow",NULL,pViewerObject);
	Script::CScript *pNewScript=Script::SpawnScript(script_name,NULL,0,NULL);
	pNewScript->mpObject = pViewerObject;
	
	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CViewerObject::s_handle_reload_cam_anim( Net::MsgHandlerContext* context )
{
	Dbg_Message( "Got request to reload cam anim..." );

	Net::MsgViewObjSetAnimFile* p_msg;
	p_msg = (Net::MsgViewObjSetAnimFile*) context->m_Msg;
	
	Script::CStruct* pTempStruct = new Script::CStruct;
								  
	pTempStruct->AddString( NONAME, p_msg->m_Filename );
	pTempStruct->AddChecksum( NONAME, p_msg->m_checksum );

	CFuncs::ScriptReloadSkaterCamAnim( pTempStruct, NULL );

	delete pTempStruct;

	return Net::HANDLER_CONTINUE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CViewerObject::CViewerObject( Net::Server* pServer )
{
	Inp::Manager * inp_manager = Inp::Manager::Instance();
    mp_input_handler = new Inp::Handler< CViewerObject > ( 0,  s_input_logic_code, *this, Tsk::BaseTask::Node::vNORMAL_PRIORITY );
	inp_manager->AddHandler( *mp_input_handler );

    m_animSpeed = 1.0f;
	m_showPanel = true;

	m_matrix.Ident();

	m_paused = false;
	
	mp_server = pServer;

	Dbg_Assert( mp_server );

	mp_server->m_Dispatcher.AddHandler( Net::vMSG_ID_VIEWOBJ_SET_ANIM, 
									  s_handle_set_anim,
									  Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
	mp_server->m_Dispatcher.AddHandler( Net::vMSG_ID_VIEWOBJ_SET_ANIM_SPEED, 
									  s_handle_set_anim_speed,
									  Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
	mp_server->m_Dispatcher.AddHandler( Net::vMSG_ID_VIEWOBJ_INCREMENT_FRAME, 
									  s_handle_increment_frame,
									  Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
	mp_server->m_Dispatcher.AddHandler( Net::vMSG_ID_VIEWOBJ_SET_ANIM_FILE, 
									  s_handle_reload_anim,
									  Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
	mp_server->m_Dispatcher.AddHandler( Net::vMSG_ID_VIEWOBJ_SET_CAMANIM_FILE, 
									  s_handle_reload_cam_anim,
									  Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
	mp_server->m_Dispatcher.AddHandler( Net::vMSG_ID_VIEWOBJ_PREVIEW_SEQUENCE, 
									  s_handle_sequence_preview,
									  Net::mHANDLE_LATE | Net::mHANDLE_FOREIGN, this );
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CViewerObject::~CViewerObject()
{
	mp_input_handler->Remove();

    delete mp_input_handler;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void s_remove_asset( const char* pFileName )
{
	// if the file is in the asset manager, unload it so
	// that the artists can display the latest version
	// ("cas_artist" must be set to 1, or else it'll just
	// reload it from the PRE file)
	
	if ( !Script::GetInt( "cas_artist", false ) )
	{
		Dbg_Message( "Warning:  Can't reload asset %s from disk (need to define cas_artist=1)", pFileName );
		return;
	}

	// get the assman
	Ass::CAssMan * ass_man = Ass::CAssMan::Instance();

	// kills the original asset
	Ass::CAsset* pAsset = ass_man->GetAssetNode( Script::GenerateCRC( pFileName ), false );

	if ( pAsset )
	{		
		Dbg_Message( "Unloading asset %s", pFileName );
		ass_man->DestroyReferences( pAsset );
		ass_man->UnloadAsset( pAsset );
	}
	else
	{
		Dbg_Message( "Couldn't find asset %s to unload", pFileName );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::LoadModel( Script::CStruct* pNodeData )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	skate_mod->GetObjectManager()->RegisterObject( *this );
	MovingObjectCreateComponents();
	MovingObjectInit( pNodeData, skate_mod->GetObjectManager() );

	Nx::CEngine::sFinishRendering();

	if (Config::GotExtraMemory())
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
	}
	else
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	}
		
	Dbg_Assert( pNodeData );
	
	// get rid of old model, if
	// it was stored in the assman
	int skaterProfileNumber;
	const char* pModelName;
	uint32 profileName;

	for ( int i = 0; i < Obj::CModelComponent::vNUM_LODS; i++ )
	{
		char paramName[256];
		sprintf( paramName, "model%d", i ); 

		if ( pNodeData->GetText( paramName, &pModelName, false ) )
		{
			Dbg_Message( "Removing model LOD data from asset manager, in order to replace it with new data" );

			Str::String fullModelName;
			fullModelName = Gfx::GetModelFileName(pModelName, ".skin");		
			s_remove_asset( fullModelName.getString() );

			fullModelName = Gfx::GetModelFileName(pModelName, ".mdl");		
			s_remove_asset( fullModelName.getString() );
		}
	}

	if ( pNodeData->GetText( "modelName", &pModelName, false ) 
		 || pNodeData->GetText( "model", &pModelName, false ) )
	{
		Dbg_Message( "Removing model data from asset manager, in order to replace it with new data" );

		Str::String fullModelName;
		fullModelName = Gfx::GetModelFileName(pModelName, ".skin");		
		s_remove_asset( fullModelName.getString() );
		
		fullModelName = Gfx::GetModelFileName(pModelName, ".mdl");		
		s_remove_asset( fullModelName.getString() );
	}
	else if ( pNodeData->GetChecksum( "profile", &profileName, false ) )
	{
		Dbg_Message( "Warning:  can't remove old model data from asset manager...  asset not reloaded from disk." );
	}
	else if ( pNodeData->GetInteger( "skater_profile_index", &skaterProfileNumber, false ) )
	{
		Dbg_Message( "Warning:  can't remove old model data from asset manager...  asset not reloaded from disk." );

		Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( skaterProfileNumber );
		Script::CStruct* pAppearanceStruct = pSkaterProfile->GetAppearance()->GetStructure();
		Script::CStruct* pNewStruct = new Script::CStruct;
		pNewStruct->AppendStructure( pAppearanceStruct );
		pNodeData->AddStructurePointer( "profile", pNewStruct );
		// don't need to delete the structure, 
		// because it gets added permanently to the struct
	}
	else
	{
		Dbg_MsgAssert( 0, ( "Unrecognized parameters for model loading" ) );
	}

	m_pos = vPOS_VIEWEROBJECT;
	m_matrix.Ident();
	
	Script::RunScript( CRCD(0x24c71bc7,"viewerobj_add_components"), pNodeData, this );

	Finalize();

	Script::RunScript( CRCD(0xbbab79de,"viewerobj_init_model"), pNodeData, this );

	// loop the idle anim, if it exists
	CViewerObject::SetAnim( CRCD(0x23db4aea,"idle") );

	Mem::Manager::sHandle().PopContext();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::UnloadModel()
{
	Script::RunScript( "kill_viewer_object_panel" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::SetAnim( uint32 animName )
{
    if ( GetAnimationComponent() )
    {
        if ( GetAnimationComponent()->AnimExists( animName ) )
        {
            GetAnimationComponent()->PlayPrimarySequence( animName, false, 0.0f, GetAnimationComponent()->AnimDuration( animName ), Gfx::LOOPING_CYCLE);
        
/*
			Script::CStruct* pTempStruct = new Script::CStruct;
			pTempStruct->AddChecksum( "type", Script::GenerateCRC("partialAnim" ) );
			pTempStruct->AddChecksum( "AnimName", Script::GetChecksum("TestPartialAnim" ) );
			pTempStruct->AddChecksum( "from", Script::GenerateCRC("start") );
			pTempStruct->AddChecksum( "to", Script::GenerateCRC("end") );
			pTempStruct->AddChecksum( NONAME, Script::GenerateCRC("cycle") );
			pTempStruct->AddFloat( "speed", 1.0f );
			Script::RunScript( "AddAnimController", pTempStruct, this );
			delete pTempStruct;
*/
		}
        else
        {
#ifdef	__NOPT_ASSERT__
            Dbg_Message( "Unrecognized anim %s", Script::FindChecksumName(animName) );
#endif
        }
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::SetRotation( float rotZ, float rotX )
{
	m_matrix.Ident();
	m_matrix.RotateZ( rotZ );
	m_matrix.RotateX( rotX );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::ChangeAnimSpeed( float animSpeed )
{
	m_animSpeed = animSpeed;

	if ( m_animSpeed > 1.0f )
	{
		m_animSpeed = 1.0f;
	}

	if ( m_animSpeed < 0.0f )
	{
		m_animSpeed = 0.0f;
	}

    if ( GetAnimationComponent() )
    {
        GetAnimationComponent()->SetAnimSpeed( m_animSpeed, false );
		
		// update partial anims
		Script::CStruct* pTempStruct = new Script::CStruct;
		pTempStruct->AddFloat( CRCD(0xf0d90109,"speed"), m_animSpeed );
		GetAnimationComponent()->CallMemberFunction( CRCD(0xbd4edd44,"SetPartialAnimSpeed"), pTempStruct, NULL );
		delete pTempStruct;
    }

	if ( GetAnimationComponent() )
	{
		// reset the paused state...
		GetAnimationComponent()->Suspend( false );
	}

	m_paused = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::IncrementFrame( bool forwards )
{
    if ( GetAnimationComponent() )
    {
		if ( forwards )
        {
            // frame-rate independent
            GetAnimationComponent()->AddTime( 1.0f / 60.0f );
			GetAnimationComponent()->PrintStatus();
        
			Script::CStruct* pTempStruct = new Script::CStruct;
			pTempStruct->AddFloat( CRCD(0x06c9f278,"incVal"), 1.0f / 60.0f );
			GetAnimationComponent()->CallMemberFunction( CRCD(0x6aaeb76f,"IncrementPartialAnimTime"), pTempStruct, NULL );
			delete pTempStruct;
		}
        else
        {
			// reverse direction so that it loops properly
			GetAnimationComponent()->ReverseDirection( false );

			// frame-rate independent
            GetAnimationComponent()->AddTime( -1.0f / 60.0f );
            GetAnimationComponent()->PrintStatus();

			GetAnimationComponent()->ReverseDirection( false );
    		
			GetAnimationComponent()->CallMemberFunction( CRCD(0xf5e2b871,"ReversePartialAnimDirection"), NULL, NULL );
			
			Script::CStruct* pTempStruct = new Script::CStruct;
			pTempStruct->AddFloat( CRCD(0x06c9f278,"incVal"), -1.0f / 60.0f );
			GetAnimationComponent()->CallMemberFunction( CRCD(0x6aaeb76f,"IncrementPartialAnimTime"), pTempStruct, NULL );
			delete pTempStruct;
			
			GetAnimationComponent()->CallMemberFunction( CRCD(0xf5e2b871,"ReversePartialAnimDirection"), NULL, NULL );
		}
		
		// don't want to call the animation component's
		// Update() function, because that changes the
		// time...  instead, call UpdateSkeleton()
		// which syncs the skeleton to the current time
		GetAnimationComponent()->UpdateSkeleton();

		// reset the paused state...
		GetAnimationComponent()->Suspend( true );
	}

	m_paused = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::ReloadAnim( const char* pFileName, uint32 animName )
{
    if ( GetAnimationComponent() )
    {
        Dbg_Message( "Reloading anim..." );
        Dbg_Message( "Warning!  This function fragments memory.  Use it at your own risk!" );

        // get the assman
        Ass::CAssMan * ass_man = Ass::CAssMan::Instance();

        // kills the original asset
        Ass::CAsset* pAsset = ass_man->GetAssetNode( Script::GenerateCRC( pFileName ), false );
        if ( pAsset )
        {
            ass_man->DestroyReferences( pAsset );
            ass_man->UnloadAsset( pAsset );
        }
        else
        {
#ifdef __NOPT_ASSERT__
            Dbg_Message( "Couldn't find asset %s to unload", Script::FindChecksumName( animName ) );
#endif
        }

        // load anim asset here!!!!
        ass_man->LoadAnim( pFileName, animName, GetAnimationComponent()->GetAnimScriptName(), false, false );

        this->SetAnim( animName );
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::UpdateWheels()
{
	Nx::CHierarchyObject* pHierarchyObjects = GetModel()->GetHierarchy();
	Dbg_Assert( pHierarchyObjects );

	Gfx::CSkeleton* pCarSkeleton = GetSkeletonComponent()->GetSkeleton();
	Dbg_Assert( pCarSkeleton );

	Dbg_MsgAssert( GetModel()->GetNumObjectsInHierarchy()==pCarSkeleton->GetNumBones(),
				   ( "Skeleton and hierarchy doesn't match (%d hierarchy objs vs %d bones)", 
				    GetModel()->GetNumObjectsInHierarchy(), pCarSkeleton->GetNumBones() ) );

	static float s_wheelRotationX = 0.0f;
	
	// not frame rate independent (doesn't have valid m_time)
	s_wheelRotationX += ( Script::GetFloat("max_wheel_speed", Script::ASSERT) * m_animSpeed );

	// make sure it's within bounds
	while ( s_wheelRotationX > 360.0f )
	{
		s_wheelRotationX -= 360.0f;
	}

	while ( s_wheelRotationX < 0.0f )
	{
		s_wheelRotationX += 360.0f;
	}

	if ( GetModel()->GetNumObjectsInHierarchy() > 5 )
	{
		Mth::Matrix* pMatrices = pCarSkeleton->GetMatrices();
		Dbg_Assert( pMatrices );

		// initialize the setup matrix once per frame
		for ( int i = 0; i < GetModel()->GetNumObjectsInHierarchy(); i++ )
		{						
			*(pMatrices + i) = ( pHierarchyObjects + i )->GetSetupMatrix();
		}

		// apply the appropriate rotations to the wheels
		for ( int i = 2; i < 6; i++ )
		{
			(pMatrices + i)->RotateXLocal( s_wheelRotationX * 2.0f * Mth::PI / 360.0f );
		}

		// get children into object space
		for ( int i = 1; i < 6; i++ )
		{
			*(pMatrices + i) *= *(pMatrices + 0); 
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::Update()
{
	if ( GetAnimationComponent() )
    {
		if ( m_showPanel )
		{
			if ( GetAnimationComponent()->AnimExists(GetAnimationComponent()->GetCurrentSequence()) )
			{
				// pass the pertinent info to the panel:
				Script::CStruct* pTempStruct = new Script::CStruct;

				char line1[128];
				sprintf( line1, "name %s",
		#ifdef	__NOPT_ASSERT__
						 Script::FindChecksumName( GetAnimationComponent()->GetCurrentSequence() )
		#else
						 "Unknown"
		#endif				  
				  );

				pTempStruct->AddString( "line1", (const char*)&line1 );

				char line2[128];
				sprintf( line2, "speed %f", m_paused ? 0.0f : m_animSpeed );
				pTempStruct->AddString( "line2", (const char*)&line2 );

				char line3[128];
				sprintf( line3, "time %f of %f", 
						 GetAnimationComponent()->GetCurrentAnimTime(), 
						 GetAnimationComponent()->AnimDuration( GetAnimationComponent()->GetCurrentSequence() ) );
				pTempStruct->AddString( "line3", (const char*)&line3 );

				char line4[128];
				sprintf( line4, "frame %d of %d", (int)(GetAnimationComponent()->GetCurrentAnimTime() * 60.0f),
						 (int)(GetAnimationComponent()->AnimDuration( GetAnimationComponent()->GetCurrentSequence() ) * 60.0f) );
				pTempStruct->AddString( "line4", (const char*)&line4 );

				// TODO:  Print out anim size

				Script::RunScript( "draw_viewer_object_panel", pTempStruct );
				delete pTempStruct;
			}
		}
	}

	if ( GetModel()->GetNumObjectsInHierarchy() )
	{
		UpdateWheels();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::s_input_logic_code ( const Inp::Handler < CViewerObject >& handler )
{       
	CViewerObject& obj = handler.GetData();
 
	// this is the minimum amount of time
	// you need to hold down the left/right keys
	// before it will auto repeat
	int vAUTO_REPEAT_TIME = Script::GetInteger( CRCD(0xe60442d5,"ViewerAutoRepeatTime"), Script::NO_ASSERT );

	// this is how fast it auto repeats
	int vAUTO_REPEAT_SPEED = Script::GetInteger( CRCD(0x2da212e5,"ViewerAutoRepeatSpeed"), Script::NO_ASSERT );

	static int s_auto_repeat_delay = vAUTO_REPEAT_TIME;

	// GJ:  no longer clearing the last frame's data
	// at the beginning of the loop...  this allows
	// us to override the m_commands from an external
	// class (useful for doing custom controller
	// configs for different viewer modes)
//	obj.m_commands.ClearAll();

    if( handler.m_Input->m_Makes & Inp::Data::mD_TRIANGLE )
	{
        obj.m_commands.SetMask( mTOGGLE_PANEL );
	}

	if ( handler.m_Input->m_Makes & Inp::Data::mD_LEFT )
	{
		obj.m_commands.SetMask( mDECREMENT_FRAME );
		s_auto_repeat_delay = vAUTO_REPEAT_TIME;
	}
	else if ( handler.m_Input->m_Makes & Inp::Data::mD_RIGHT )
	{
		obj.m_commands.SetMask( mINCREMENT_FRAME );
		s_auto_repeat_delay = vAUTO_REPEAT_TIME;
	}
	else if ( handler.m_Input->m_Buttons & Inp::Data::mD_LEFT )
	{
		s_auto_repeat_delay--;
		if ( s_auto_repeat_delay <= 0 )
		{
			obj.m_commands.SetMask( mDECREMENT_FRAME );
			s_auto_repeat_delay = vAUTO_REPEAT_SPEED;
		}
	}
	else if ( handler.m_Input->m_Buttons & Inp::Data::mD_RIGHT )
	{
		s_auto_repeat_delay--;
		if ( s_auto_repeat_delay <= 0 )
		{
			obj.m_commands.SetMask( mINCREMENT_FRAME );
			s_auto_repeat_delay = vAUTO_REPEAT_SPEED;
		}
	}
	else
	{
		s_auto_repeat_delay = vAUTO_REPEAT_TIME;
	}
	
	// the viewer uses the global var "viewer_controls_enabled"
	// to decide whether certain viewer controls should be
	// active while in the regular viewer/light viewer.
	// rather than adding a new global var to distinguish
	// between the model viewer/light viewer, i've decided
	// to just have the viewer object handle those viewer
	// controls here
	if( handler.m_Input->m_Makes & Inp::Data::mD_L3 )
	{
		obj.m_commands.SetMask( mTOGGLE_ROTATE_MODE );
	}
	
	if ( handler.m_Input->m_Buttons & Inp::Data::mD_R1 )
	{
		if( !( handler.m_Input->m_Buttons & Inp::Data::mD_R2 ))
		{
			obj.m_commands.SetMask( mSTRAFE_UP );
		}
	}
	else if ( handler.m_Input->m_Buttons & Inp::Data::mD_R2 )
	{
		obj.m_commands.SetMask( mSTRAFE_DOWN );
	}
	
	if ( handler.m_Input->m_Makes & Inp::Data::mD_L1 )
	{
		obj.m_commands.SetMask( mINCREASE_SPEED );
	}
	else if ( handler.m_Input->m_Makes & Inp::Data::mD_L2 )
	{
		obj.m_commands.SetMask( mDECREASE_SPEED );
	}
	
	if ( !GetAnimationComponentFromObject( &obj ) )
	{
		if ( handler.m_Input->m_Buttons & Inp::Data::mD_CIRCLE )
		{
			obj.m_commands.SetMask( mROLL_LEFT );
		}
		else if ( handler.m_Input->m_Buttons & Inp::Data::mD_SQUARE )
		{
			obj.m_commands.SetMask( mROLL_RIGHT );
		}
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CViewerObject::DoGameLogic()
{
	if ( m_commands.TestMask( mTOGGLE_PANEL )	)
	{
		m_showPanel = !m_showPanel;
		
		Script::RunScript( "kill_viewer_object_panel" );
	}

	if ( m_commands.TestMask( mINCREASE_SPEED ) )
	{
		ChangeAnimSpeed( m_animSpeed + vSPEED_INCREMENT_AMOUNT );
	}

	if ( m_commands.TestMask( mDECREASE_SPEED ) )
	{
		ChangeAnimSpeed( m_animSpeed - vSPEED_INCREMENT_AMOUNT );
	}
										
	if ( m_commands.TestMask( mINCREMENT_FRAME ) )
	{
		IncrementFrame( true );
	}

	if ( m_commands.TestMask( mDECREMENT_FRAME ) )
	{
		IncrementFrame( false );
	}

	// yuck:  the following creates a cyclic dependency
	// on viewer...  should get rid of this later.
	if ( m_commands.TestMask( mTOGGLE_ROTATE_MODE ) )
	{
		Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();
		if ( pViewer )
		{
			pViewer->SetCommand( Mdl::CViewer::mTOGGLE_ROTATE_MODE );
		}
	}

	if ( m_commands.TestMask( mSTRAFE_UP ) )
	{
		Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();
		if ( pViewer )
		{
			pViewer->SetCommand( Mdl::CViewer::mSTRAFE_UP );
		}
	}
	
	if ( m_commands.TestMask( mSTRAFE_DOWN ) )
	{
		Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();
		if ( pViewer )
		{
			pViewer->SetCommand( Mdl::CViewer::mSTRAFE_DOWN );
		}
	}
	
	if ( m_commands.TestMask( mROLL_LEFT ) )
	{
		Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();
		if ( pViewer )
		{
			pViewer->SetCommand( Mdl::CViewer::mROLL_LEFT );
		}
	}
	
	if ( m_commands.TestMask( mROLL_RIGHT ) )
	{
		Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();
		if ( pViewer )
		{
			pViewer->SetCommand( Mdl::CViewer::mROLL_RIGHT );
		}
	}

	Update();
	
	// GJ:  clear the frame's data, now that we're done with it
	// (this used to be done at the beginning of the loop)
	m_commands.ClearAll();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj


================================================
FILE: Code/Sk/Objects/ViewerObj.h
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       ViewerObj.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  1/21/2002
//****************************************************************************

#ifndef __VIEWER_OBJ_H
#define __VIEWER_OBJ_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 

#include 
#ifndef __GEL_OBJECT_H
#include 
#endif

#include 

#include 

/*****************************************************************************
**							Forward Declarations						**
*****************************************************************************/

namespace Net
{
	class Server;
};

namespace Nx
{
    class CModel;
};

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{
	class CSkeletonComponent;

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

// a viewer object is similar to a moving object,
// but without the physics.  a viewer object always
// exists in the viewer, although it doesn't necessarily
// have renderable model data associated with it

class CViewerObject : public CMovingObject
{
public:

enum ECommands
{
    mLOAD_SKATER        = nBit( 1 ),
	mLOAD_PEDESTRIAN	= nBit( 2 ),
    mGOTO_NEXT_ANIM		= nBit( 3 ),
    mGOTO_PREV_ANIM		= nBit( 4 ),
    mGOTO_FIRST_ANIM	= nBit( 5 ),
    mINCREASE_SPEED 	= nBit( 6 ),
    mDECREASE_SPEED     = nBit( 7 ),
    mINCREMENT_FRAME 	= nBit( 8 ),
    mDECREMENT_FRAME    = nBit( 9 ),
    mSHOW_PANEL    		= nBit( 10 ),
    mHIDE_PANEL    		= nBit( 11 ),
    mTOGGLE_PANEL    	= nBit( 12 ),
	mSTRAFE_UP   	    = nBit( 13 ),
	mSTRAFE_DOWN        = nBit( 14 ),
	mROLL_LEFT          = nBit( 15 ),
	mROLL_RIGHT         = nBit( 16 ),
	mTOGGLE_ROTATE_MODE = nBit( 17 ),
};

public:
    CViewerObject( Net::Server* pServer );
    virtual     ~CViewerObject();
	void		SetRotation( float rotZ, float rotX );
	void        LoadModel( Script::CStruct* pParams );
	void		UnloadModel();
	void		SetAnim( uint32 animName );
	void		ReloadAnim( const char* pAnim, uint32 animName );
    
protected:
	void		DoGameLogic();
    void        Update();
	void		UpdateWheels();
    
protected:
	void		ChangeAnimSpeed( float animSpeed );
	void		IncrementFrame( bool forwards );

protected:
	static 		Net::MsgHandlerCode		s_handle_set_anim;
	static 		Net::MsgHandlerCode		s_handle_set_anim_speed;
	static 		Net::MsgHandlerCode		s_handle_increment_frame;
	static		Net::MsgHandlerCode		s_handle_reload_anim;
	static		Net::MsgHandlerCode		s_handle_reload_cam_anim;
	static		Net::MsgHandlerCode		s_handle_sequence_preview;

    static		Inp::Handler< CViewerObject >::Code 	s_input_logic_code;

    Inp::Handler< CViewerObject >*	mp_input_handler;
    Tsk::Task< CViewerObject >*     mp_logic_task;

	Flags				m_commands;

protected:
	Net::Server*					mp_server;
    float       					m_animSpeed;
	bool							m_paused;
	bool							m_showPanel;
};

} // namespace Obj

#endif	// __VIEWER_OBJ_H



================================================
FILE: Code/Sk/Objects/car.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       car.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/24/2000
//****************************************************************************

/*
	MD:  The car is just a moving object with a special ground collision 
	function, and the functionality to rotate wheels based on the car's 
	velocity and turn the wheels based on the change in the car's direction. 
*/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 				
#include 				
                         
#include 
#include 	 
#include 	 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 	 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

#define DEFAULT_CAR_TURN_DIST				20.0f
#define DEFAULT_CAR_MAX_VEL 				30.0f
#define	DEFAULT_CAR_ACCELERATION			18.0f
#define DEFAULT_CAR_HEIGHT_OFFSET  			0.0f
#define DEFAULT_CAR_DECELERATION			32.0f
#define DEFAULT_EMERGENCY_DECELERATION		20.0f
#define DEFAULT_CAR_MIN_STOP_VEL			4.0f		//mph at which the brakes overpower momentum.
#define DEFAULT_CAR_STOP_DIST_SPEED_MULT	1.0f		//stopping distance required depends on speed*this

#define MAX_CAR_SPEED 85
	
#define MAX_WHEEL_Y_ROT ( 70.0f )
#define MAX_CAR_X_ROT ( 5.0f )
#define MIN_CAR_X_ROT ( -5.0f )
#define STEP_CAR_X_ROT ( 0.125f )
#define ACCEL_TO_CAR_ROT_FACTOR ( 3.0f )

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCar::update_wheels()
{
#if 1
	// rotate based on forward speed
	GetCarPhysicsComponent()->m_wheelRotationX += ( ( Tmr::FrameRatio() ) * ( GetMotionComponent()->m_vel_z * 90.0f ) / ( MPH_TO_IPS( MAX_CAR_SPEED ) ) );
#else
	// rotate at constant rate
	GetCarPhysicsComponent()->m_wheelRotationX += ( 2.0f * Tmr::FrameRatio() );
#endif
	
	// make sure it's within bounds
	while ( GetCarPhysicsComponent()->m_wheelRotationX > 360.0f )
	{
		GetCarPhysicsComponent()->m_wheelRotationX -= 360.0f;
	}

	while ( GetCarPhysicsComponent()->m_wheelRotationX < 0.0f )
	{
		GetCarPhysicsComponent()->m_wheelRotationX += 360.0f;
	}

	if ( GetMotionComponent()->GetPathOb() )
	{
		if ( GetMotionComponent()->GetPathOb()->m_y_rot )
		{
			Mth::Vector radiusVec = GetMotionComponent()->GetPathOb()->m_nav_pos - GetMotionComponent()->GetPathOb()->m_circle_center;
			float turningRadius = radiusVec.Length();

#ifdef __PLAT_NGPS__
			float ang = asinf( GetCarPhysicsComponent()->m_distanceBetweenTires / turningRadius );
#else
			float a_ang = GetCarPhysicsComponent()->m_distanceBetweenTires / turningRadius;
			if ( a_ang > 1.0f ) a_ang = 1.0f;
			if ( a_ang < -1.0f ) a_ang = -1.0f;
			float ang = asinf( a_ang );
#endif		// __PLAT_NGPS__
			GetCarPhysicsComponent()->m_targetWheelRotationY = Mth::RadToDeg(-ang);
			if ( GetMotionComponent()->GetPathOb()->m_y_rot < 0.0f )
			{
				GetCarPhysicsComponent()->m_targetWheelRotationY = -GetCarPhysicsComponent()->m_targetWheelRotationY;
			}
		}
		else
		{
			GetCarPhysicsComponent()->m_targetWheelRotationY = 0.0f;
		}
		
	    GetCarPhysicsComponent()->m_wheelRotationY = Mth::FRunFilter( GetCarPhysicsComponent()->m_targetWheelRotationY, GetCarPhysicsComponent()->m_wheelRotationY, Tmr::FrameRatio() );
	}																				  
	else
	{
		GetCarPhysicsComponent()->m_wheelRotationY = 0.0f;
	}

	float deltaZ = GetMotionComponent()->m_vel_z - GetCarPhysicsComponent()->m_old_vel_z;

	GetCarPhysicsComponent()->m_old_vel_z = GetMotionComponent()->m_vel_z;

	if ( Script::GetInt( CRCD(0x4cf13d8f,"car_debug"), Script::NO_ASSERT ) )
	{
		GetCarPhysicsComponent()->m_minCarRot = Script::GetFloat( CRCD(0x877db8da,"accelCarRot"), Script::ASSERT );
		GetCarPhysicsComponent()->m_maxCarRot = Script::GetFloat( CRCD(0xfb3464df,"decelCarRot"), Script::ASSERT );
		GetCarPhysicsComponent()->m_stepCarRot = Script::GetFloat( CRCD(0xfd8bc8d6,"speedCarRot"), Script::ASSERT );
		GetCarPhysicsComponent()->m_accelToCarRotFactor = Script::GetFloat( CRCD(0xb2c7447b,"accelCarRotFactor"), Script::ASSERT );
//		GetCarPhysicsComponent()->m_enter_turn_dist = Script::GetFloat( "turnDist", Script::ASSERT );
	}

	// vACCELERATION_TO_ROTATION_CONSTANT = magic number 
	// that happens to look good, but it can be tweaked 
	// later if necessary (or moved into script on a 
	// per-car basis).
	GetCarPhysicsComponent()->m_targetCarRotationX = -deltaZ / GetCarPhysicsComponent()->m_accelToCarRotFactor;

	if ( GetCarPhysicsComponent()->m_targetCarRotationX > GetCarPhysicsComponent()->m_maxCarRot )
	{
		GetCarPhysicsComponent()->m_targetCarRotationX = GetCarPhysicsComponent()->m_maxCarRot;
	}
	if ( GetCarPhysicsComponent()->m_targetCarRotationX < GetCarPhysicsComponent()->m_minCarRot )
	{
		GetCarPhysicsComponent()->m_targetCarRotationX = GetCarPhysicsComponent()->m_minCarRot;
	}

    GetCarPhysicsComponent()->m_carRotationX = Mth::FRunFilter( GetCarPhysicsComponent()->m_targetCarRotationX, GetCarPhysicsComponent()->m_carRotationX, GetCarPhysicsComponent()->m_stepCarRot * Tmr::FrameRatio() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CalculateCarHierarchyMatrices( Gfx::CSkeleton* pSkeleton,
                                    Nx::CModel *p_renderedModel, 
									float carRotationX, 
									float wheelRotationX, 
									float wheelRotationY)
{
	Dbg_Assert( p_renderedModel );
	Gfx::CSkeleton* pCarSkeleton = pSkeleton;
	Dbg_Assert( pCarSkeleton );

	// TODO:  Possibly set up the setup matrix only once,
	// and then prerotate the rotation?
	
	Nx::CHierarchyObject* pHierarchyObjects = p_renderedModel->GetHierarchy();
	Mth::Matrix* pMatrices = pCarSkeleton->GetMatrices();

	float carRotationXInRadians = Mth::DegToRad( carRotationX );
	float wheelRotationYInRadians = Mth::DegToRad( wheelRotationY );
	float wheelRotationXInRadians = Mth::DegToRad( wheelRotationX );

	Nx::CHierarchyObject* pCurrentHierarchyObject = pHierarchyObjects;
	for ( int i = 0; i < pCarSkeleton->GetNumBones(); i++ )
	{
		Mth::Matrix* pMatrix = pMatrices + pCurrentHierarchyObject->GetBoneIndex();
		
		// initialize the setup matrix once per frame
		*pMatrix = pCurrentHierarchyObject->GetSetupMatrix();	

		switch ( pCurrentHierarchyObject->GetChecksum() )
		{
			// front wheels
			case 0x6e2f434e:	// car_wheel01
			case 0xf72612f4:	// car_wheel02
				
				// apply steering to the front tires
				pMatrix->RotateZLocal( wheelRotationYInRadians );

				// spin wheels
				pMatrix->RotateXLocal( wheelRotationXInRadians );

				break;

			// back wheels
			case 0x80212262:	// car_wheel03
			case 0x1e45b7c1:	// car_wheel04
				
				// spin wheels
				pMatrix->RotateXLocal( wheelRotationXInRadians );
				break;

			// middle wheels
			case 0x69428757:	// car_wheel05
			case 0xf04bd6ed:	// car_wheel06
				
				// spin wheels
				pMatrix->RotateXLocal( wheelRotationXInRadians );
				break;

			case 0x88c21962:	// car

				// rock the car back and forth
				pMatrix->RotateXLocal( carRotationXInRadians );
				break;

			case 0x92e19120:	// car_shadow
				// do nothing
				break;

			default:
				Dbg_MsgAssert( 0, ( "Unrecognized bone name %s while animating car parts (bone %d)\nCar parts MUST be named be car_wheel01, car_wheel02, car_wheel03, car_wheel04, car_wheel05, car_wheel06, car, or car_shadow", Script::FindChecksumName(pCurrentHierarchyObject->GetChecksum() ), i) );
				break;
		}

		// get children into object space
		if ( i != 0 )
		{
			*pMatrix *= *( pMatrices + pCurrentHierarchyObject->GetParentIndex() );
		}

		pCurrentHierarchyObject++;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCar::display_wheels()
{
	CalculateCarHierarchyMatrices(this->GetSkeleton(),GetModel(),GetCarPhysicsComponent()->m_carRotationX,GetCarPhysicsComponent()->m_wheelRotationX,GetCarPhysicsComponent()->m_wheelRotationY);
	
	// scale down the shadow to nothing...  must be done after all the 
	// children's matrices have been calculated...  this also assumes
	// the shadow is the first matrix...  if it's not, we'd have to
	// search for the correct matrix by name
	if ( !GetCarPhysicsComponent()->m_shadowEnabled )
	{
		Mth::Matrix mat;
		mat.Ident();
		mat.Scale(Mth::Vector(0.0f,0.0f,0.0f,1.0f));
		
		Mth::Matrix* pMatrices = GetSkeleton()->GetMatrices();
		*pMatrices = mat;
	}
}

#if 0
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCar::debug_wheels()
{
	Dbg_Assert( GetModel() );
	Gfx::CSkeleton* pCarSkeleton = this->GetSkeleton();
	Dbg_Assert( pCarSkeleton );

	// draw skeleton
	for ( int i = 0; i < 6; i++ )
	{
		Mth::Matrix objMatrix = m_matrix;
		objMatrix[Mth::POS] = m_pos;

		Mth::Matrix wsMatrix = *(pCarSkeleton->GetMatrices() + i) * objMatrix;

		Mth::Vector pos = wsMatrix[Mth::POS];
		pos[Y] += 48.0f;
		wsMatrix[Mth::POS] = Mth::Vector( 0.0f, 0.0f, 0.0f, 1.0f );
        
		// set up bounding box
		SBBox theBox;
		theBox.m_max.Set(10.0f, 10.0f, 10.0f);
		theBox.m_min.Set(-10.0f, -10.0f, -10.0f);    

		// For now, draw a bounding box
		Gfx::AddDebugBox( wsMatrix, pos, &theBox, NULL, 1, NULL ); 
	}
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCar::InitCar( CGeneralManager* p_obj_man, Script::CStruct* pNodeData )
{
	p_obj_man->RegisterObject(*this);

	MovingObjectCreateComponents();

	uint32 skeletonName = CRCD(0x88c21962,"car");
	bool use_skeletal_cars = false;

	if ( pNodeData->GetChecksum( CRCD(0x09794932,"skeletonName"), &skeletonName, false ) )
	{
		use_skeletal_cars = true;
	}

	if ( use_skeletal_cars )
	{
//		m_model_restoration_info.mSkeletonName=skeletonName;
		// component-based
		Dbg_MsgAssert( GetSkeletonComponent() == NULL, ( "Skeleton component already exists" ) );
		Script::CStruct* pSkeletonStruct = new Script::CStruct;
		pSkeletonStruct->AddChecksum( CRCD(0xb6015ea8,"component"), CRC_SKELETON );
		pSkeletonStruct->AddChecksum( CRCD(0x222756d5,"skeleton"), skeletonName );
		this->CreateComponentFromStructure(pSkeletonStruct, NULL);
		delete pSkeletonStruct;

#ifdef	__NOPT_ASSERT__
		Gfx::CSkeleton* pSkeleton = GetSkeletonComponent()->GetSkeleton();
		Dbg_Assert( pSkeleton );
		Dbg_Assert( pSkeleton->GetNumBones() > 0 );
#endif
	}
	else
	{
//		m_model_restoration_info.mSkeletonName=0;
	}	
	
	MovingObjectInit( pNodeData, p_obj_man );
	
	Obj::CModelComponent* pModelComponent = new Obj::CModelComponent;
	this->AddComponent( pModelComponent );
	pModelComponent->InitFromStructure( pNodeData );
	
	Dbg_Assert( GetModel() );

	// set up the skeleton for this car model, if any
	// needs to come after the geom is loaded (because
	// that's where the hierarchy info is)
	if ( use_skeletal_cars )
	{		
		const char* p_model_name;
		pNodeData->GetText( CRCD(0x286a8d26,"model"), &p_model_name, true );

		// sanity check on number of bones in skeleton
		Dbg_MsgAssert( GetModel()->GetNumObjectsInHierarchy()==GetSkeleton()->GetNumBones(), 
					   ( "Expected to find %d bones in the %s skeleton (found %d in %s - %s)",
						 GetSkeleton()->GetNumBones(),
						 Script::FindChecksumName( skeletonName ),
						 GetModel()->GetNumObjectsInHierarchy(),
						 Script::FindChecksumName( GetID() ),
						 p_model_name ) );
		
		GetCarPhysicsComponent()->init_car_skeleton();		
	}
	else
	{
		if ( GetModel()->GetNumObjectsInHierarchy()!=0 )
		{
			uint32 name = 0;
			pNodeData->GetChecksum( CRCD(0xa1dc81f9,"name"), &name, Script::ASSERT );
			Dbg_MsgAssert( 0, ( "Skeletal model requires a skeleton %s", Script::FindChecksumName(name) ) );
		}
	}

	GetCarPhysicsComponent()->InitFromStructure( pNodeData );

	if ( !pNodeData->ContainsFlag( CRCD(0x0bf29bc0,"NoCollision") ) )
	{
		// GJ:  collision component should be added after the model component,
		// because we want the same m_pos/m_matrix that is used for displaying
		// the model
		Dbg_MsgAssert( GetCollisionComponent() == NULL, ( "Collision component already exists" ) );
		Script::CStruct* pCollisionStruct = new Script::CStruct;
		pCollisionStruct->AddChecksum( CRCD(0xb6015ea8,"component"), CRC_COLLISION );
		pCollisionStruct->AddChecksum( CRCD(0x2d7e583b,"collisionMode"), CRCD(0x6aadf154,"geometry") );
		this->CreateComponentFromStructure(pCollisionStruct, NULL);
		delete pCollisionStruct;
	}

	// designer controlled variables:
	// set defaults, to be overridden by script values if they exist:
	GetMotionComponent()->m_max_vel = ( MPH_TO_INCHES_PER_SECOND ( DEFAULT_CAR_MAX_VEL ) );
	GetMotionComponent()->m_acceleration = FEET_TO_INCHES( DEFAULT_CAR_ACCELERATION );
	GetMotionComponent()->m_deceleration = FEET_TO_INCHES( DEFAULT_CAR_DECELERATION );
	
	// stick to ground tests against wheels, rather than origin
	// (eventually, we'll move this functionality into a custom
	// sticktoground component)
	GetMotionComponent()->m_point_stick_to_ground = false;

	GetMotionComponent()->EnsurePathobExists( this );
	GetMotionComponent()->GetPathOb()->m_enter_turn_dist = FEET_TO_INCHES( DEFAULT_CAR_TURN_DIST );
	
	// Add a NodeArrayComponent to the Car, so it will request loading of the associated node array.
	Obj::CNodeArrayComponent *p_node_array_component = new Obj::CNodeArrayComponent;
	this->AddComponent( p_node_array_component );
	p_node_array_component->InitFromStructure( pNodeData );

	if ( !pNodeData->ContainsFlag( CRCD(0x1fb9e477,"NoRail") ) )
	{
		// Add a RailManagerComponent to the Car, so it can be used for grinding etc.
		Obj::CRailManagerComponent *p_rail_manager_component = new Obj::CRailManagerComponent;
		this->AddComponent( p_rail_manager_component );
		p_rail_manager_component->InitFromStructure( pNodeData );
	}

	if ( !pNodeData->ContainsFlag( CRCD(0x59793e2b,"NoSkitch") ) )
	{
		// Add an ObjectHookManagerComponent to the Car, for use by the SkitchComponent.
		Obj::CObjectHookManagerComponent *p_object_hook_manager_component = new Obj::CObjectHookManagerComponent;
		this->AddComponent( p_object_hook_manager_component );
		p_object_hook_manager_component->InitFromStructure( pNodeData );

		// Add a SkitchComponent to the Car, so it can be used for skitching etc.
		Obj::CSkitchComponent *p_skitch_component = new Obj::CSkitchComponent;
		this->AddComponent( p_skitch_component );
		p_skitch_component->InitFromStructure( pNodeData );
	}

	// Finalize the object, saying we've added all the components
	Finalize();
	
	// need to synchronize rendered model's position to initial world position
	GetModelComponent()->FinalizeModelInitialization();

	SetProfileColor(0xc0c000);				// cyan = car
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCar::DoGameLogic()
{
	Dbg_MsgLog(("DoGameLogic for car '%s'",Script::FindChecksumName(GetID())));
	
	if ( GetSuspendComponent()->SkipLogic( ) )
	{
		return;
	}
	
	if ( IsDead() )
	{
		return;
	}

	// Don't update the wheels if we should not animate
	// (and this cunningly lets the SuspendComponment actually work, by clearing the initial_animations	
	if (GetSuspendComponent()->should_animate( ))
	{
		// check to see if it's got a hierarchy
		// if it does, then update the wheels
		Gfx::CSkeleton* pCarSkeleton = this->GetSkeleton();
		if ( pCarSkeleton )
		{
			update_wheels();
	
			display_wheels();
			
//			debug_wheels();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCar::CallMemberFunction( uint32 Checksum, Script::CScriptStructure *pParams, Script::CScript *pScript )
{   
	return CMovingObject::CallMemberFunction( Checksum, pParams, pScript );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCar::CCar ( void )
{
	m_type = SKATE_TYPE_CAR;

	Obj::CCarPhysicsComponent* pCarPhysicsComponent = new Obj::CCarPhysicsComponent;
	AddComponent( pCarPhysicsComponent );
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCar::~CCar ( void )
{   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CreateCar(CGeneralManager* p_obj_man, Script::CStruct* pNodeData)
{
	CCar* pCar = new CCar;
    Dbg_MsgAssert(pCar, ("Failed to create car."));
	
	// get position, from the node that created us:
	SkateScript::GetPosition( pNodeData, &pCar->m_pos );
    
	pCar->InitCar(p_obj_man, pNodeData);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CreateCar(CGeneralManager* p_obj_man, int nodeNumber)
{
	Script::CStruct* pNodeData = SkateScript::GetNode(nodeNumber);
	CreateCar(p_obj_man, pNodeData);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj


================================================
FILE: Code/Sk/Objects/car.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Object (OBJ)											**
**																			**
**	File name:		objects/car.h	    									**
**																			**
**	Created: 		10/15/00	-	mjd										**
**																			**
*****************************************************************************/

#ifndef __OBJECTS_CAR_H
#define __OBJECTS_CAR_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Script
{
	class CScript;
	class CStruct;
}
				 
namespace Obj
{

	class CGeneralManager;

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

void CreateCar( CGeneralManager *p_obj_man, int nodeNum );
void CreateCar( CGeneralManager* p_obj_man, Script::CStruct* pNodeData);

class CCar : public CMovingObject 
{		

public:
					CCar ( void );
	virtual			~CCar ( void );
	void			InitCar( CGeneralManager* p_obj_man, Script::CStruct* pNodeData );
	bool			CallMemberFunction( uint32 Checksum, Script::CStruct* pParams, Script::CScript* pScript );
	
protected:
	void			update_wheels();
	void			display_wheels();
	void			debug_wheels();
	void			DoGameLogic();	
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

void CalculateCarHierarchyMatrices( Gfx::CSkeleton* pSkeleton,
                                    Nx::CModel *p_renderedModel, 
									float carRotationX, 
									float m_wheelRotationX, 
									float m_wheelRotationY);

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_CAR_H


================================================
FILE: Code/Sk/Objects/crown.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate4													**
**																			**
**	Module:			CCrown			 										**
**																			**
**	File name:		Crown.cpp												**
**																			**
**	Created by:		08/06/01	-	SPG										**
**																			**
**	Description:	King of the Hill Crown Object							**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

#include 
#include 
#include 

#include 
#include 
#include 
					 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

const float CCrown::vCROWN_RADIUS = FEET( 8.0f );

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCrown::DoGameLogic()
{
	if ( m_on_king )
	{
		Gfx::CSkeleton* pSkeleton;
		pSkeleton = mp_king->GetSkeleton();
		Dbg_Assert( pSkeleton );

		pSkeleton->GetBoneMatrix( CRCD(0xddec28af,"Bone_Head"), &m_matrix );

		m_matrix.RotateXLocal( 90.0f );

		// raise the crown by 1 foot
		//m_matrix.TranslateLocal( Mth::Vector( 0.0f, FEET(1.0f), 0.0f, 0.0f ) );
		m_matrix.TranslateLocal( Mth::Vector( 0.0f, 0.0f, -FEET(1.5f), 0.0f ) );

		Mth::Vector crownOffset = m_matrix[Mth::POS];
		m_matrix[Mth::POS] = Mth::Vector( 0.0f, 0.0f, 0.0f, 1.0f );
		
// for some reason, the below doesn't work as expected.
// so instead, use the skater's root matrix for the orientation

		m_matrix = m_matrix * mp_king->GetMatrix();

//		m_matrix = mp_king->GetMatrix();

		m_pos = mp_king->GetMatrix().Transform( crownOffset );
		m_pos += mp_king->m_pos;
		
//		Gfx::AddDebugStar( m_pos, 10, 0x0000ffff, 1 );
	}

}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

CCrown::CCrown( void )
{
	SetID( Script::GenerateCRC( "crown_object" ));

#if 0	
	// GJ:  Why is the crown not registered here, instead of in InitCrown?
	p_obj_man->RegisterObject( *this );
#endif
	
    m_on_king = false;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCrown::~CCrown( void )
{	
	RemoveFromWorld();

	m_on_king = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCrown::PlaceOnKing( CSkater* king )
{   
	mp_king = king;
    
    m_on_king = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCrown::RemoveFromWorld( void )
{
	m_on_king = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CCrown::RemoveFromKing( void )
{
	m_on_king = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CCrown::OnKing( void )
{
	return m_on_king;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCrown::InitCrown( CGeneralManager* p_obj_man, Script::CStruct* pNodeData )
{   
	p_obj_man->RegisterObject( *this );
	
	MovingObjectCreateComponents();

	// Create an empty model 
	Dbg_MsgAssert( GetModelComponent() == NULL, ( "Model component already exists" ) );
	Script::CStruct* pModelStruct = new Script::CStruct;
	pModelStruct->AddChecksum( CRCD(0xb6015ea8,"component"), CRC_MODEL );
	this->CreateComponentFromStructure(pModelStruct, NULL);
	delete pModelStruct;
	
	MovingObjectInit( pNodeData, p_obj_man );
	SetID( Script::GenerateCRC( "crown_object" ));
	
	Obj::CSuspendComponent* pSuspendComponent = GetSuspendComponent();
	Dbg_MsgAssert( pSuspendComponent, ( "No suspend component?" ) );
	pSuspendComponent->m_lod_dist[0] = 1000000;
	pSuspendComponent->m_lod_dist[1] = 1000001;
	pSuspendComponent->m_lod_dist[2] = 1000002;  // Note, this is used as a magic number to turn off the high level culling
	pSuspendComponent->m_lod_dist[3] = 1000003;

	Str::String fullModelName;
//	fullModelName = Gfx::GetModelFileName("gameobjects/skate/letter_a/letter_a.mdl", ".mdl");
	fullModelName = Gfx::GetModelFileName("crown", ".mdl");
	// Model file name should look like this:  "models/testcar/testcar.mdl"
	Dbg_Printf( "Initializing Crown using file %s\n", fullModelName.getString() );
	GetModel()->AddGeom( fullModelName.getString(), 0, true );
	
	GetModel()->SetActive( true );
	SkateScript::GetPosition( pNodeData, &m_pos );
	Dbg_Printf( "Placing crown at %f %f %f\n", m_pos[X], m_pos[Y], m_pos[Z] );

	Finalize();

	GetModelComponent()->FinalizeModelInitialization();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
} // namespace Obj



================================================
FILE: Code/Sk/Objects/crown.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate3													**
**																			**
**	Module:			Crown			 										**
**																			**
**	File name:		Crown.h													**
**																			**
**	Created by:		08/06/01	-	SPG										**
**																			**
**	Description:	King of the Hill crown object							**
**																			**
*****************************************************************************/

#ifndef __OBJECTS_CROWN_H
#define __OBJECTS_CROWN_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 
#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Obj
{

class CSkater;

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class  CCrown : public CMovingObject 
{		
public:
	static const float vCROWN_RADIUS;

public:
	CCrown ( void );
	virtual	~CCrown ( void );
	
	void			InitCrown( CGeneralManager* p_obj_man, Script::CStruct* pNodeData );
	void			PlaceOnKing( CSkater* king );
	void			RemoveFromWorld( void );
	void			RemoveFromKing( void );
	Mth::Vector& 	GetPosition( void ) {return m_pos;}
	void			SetPosition( Mth::Vector& pos ) { m_pos = pos; }
	bool			OnKing( void );
	void			DoGameLogic();

protected:	
	CSkater* 		mp_king;
	uint32			m_head_bone_index;
	bool			m_on_king;
		
//	static	Tsk::Task< CCrown >::Code s_logic_code;
//	Tsk::Task< CCrown >*	mp_logic_task;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj

#endif	// __OBJECTS_CROWN_H




================================================
FILE: Code/Sk/Objects/cutscenedetails.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       cutscenedetails.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  01/13/2003
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

#include 
#include 
#include 
#include 

#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// make sure the texture dictionary offsets don't
// clash with skater's texture dictionaries
// in case we're using actual skater assets
// (such as the board) 
const int vCUTSCENE_TEXDICT_RANGE = 200;
 
// flags that are set during cifconv
#define vCUTSCENEOBJECTFLAGS_ISSKATER		(1<<31)
#define vCUTSCENEOBJECTFLAGS_ISHEAD			(1<<30)

#ifdef __PLAT_NGC__
extern bool g_in_cutscene;
#endif		// __PLAT_NGC__

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Obj
{

/*
char testWeightIndices[] = {
	30, -1, -1, // vertex 0
	30, -1, -1, // vertex 1
	30, -1, -1, // vertex 2
	30, -1, -1, // vertex 3
	30, -1, -1, // vertex 4
	30, -1, -1, // vertex 5
	30, -1, -1, // vertex 6
	30, -1, -1, // vertex 7
	30, -1, -1, // vertex 8
	30, 32, -1, // vertex 9
	30, 32, -1, // vertex 10
	30, 32, -1, // vertex 11
	30, 32, -1, // vertex 12
	30, 32, -1, // vertex 13
	30, 32, -1, // vertex 14
	30, 32, -1, // vertex 15
	30, 32, -1, // vertex 16
	30, 32, -1, // vertex 17
	30, 32, -1, // vertex 18
	30, 32, -1, // vertex 19
	30, 32, -1, // vertex 20
	30, 32, -1, // vertex 21
	30, -1, -1, // vertex 22
	30, -1, -1, // vertex 23
	30, -1, -1, // vertex 24
	30, -1, -1, // vertex 25
	30, -1, -1, // vertex 26
	30, -1, -1, // vertex 27
	30, 31, -1, // vertex 28
	30, -1, -1, // vertex 29
	30, 32, -1, // vertex 30
	30, -1, -1, // vertex 31
	30, 32, -1, // vertex 32
	30, -1, -1, // vertex 33
	30, 32, -1, // vertex 34
	30, 32, -1, // vertex 35
	30, 32, -1, // vertex 36
	30, 32, -1, // vertex 37
	30, 32, -1, // vertex 38
	30, 32, -1, // vertex 39
	30, 32, -1, // vertex 40
	30, 32, -1, // vertex 41
	30, 32, -1, // vertex 42
	30, -1, -1, // vertex 43
	30, -1, -1, // vertex 44
	30, -1, -1, // vertex 45
	30, -1, -1, // vertex 46
	30, -1, -1, // vertex 47
	30, -1, -1, // vertex 48
	30, -1, -1, // vertex 49
	30, -1, -1, // vertex 50
	30, 32, -1, // vertex 51
	30, 32, -1, // vertex 52
	30, 32, -1, // vertex 53
	30, 32, -1, // vertex 54
	30, 32, -1, // vertex 55
	30, 32, -1, // vertex 56
	30, 32, -1, // vertex 57
	30, 32, -1, // vertex 58
	30, 32, -1, // vertex 59
	30, 32, -1, // vertex 60
	30, 32, -1, // vertex 61
	30, 32, -1, // vertex 62
	30, 32, -1, // vertex 63
	30, 32, -1, // vertex 64
	30, 32, -1, // vertex 65
	30, 32, -1, // vertex 66
	30, 32, -1, // vertex 67
	30, 32, -1, // vertex 68
	30, 32, -1, // vertex 69
	30, 32, -1, // vertex 70
	30, 32, -1, // vertex 71
	30, 32, -1, // vertex 72
	30, -1, -1, // vertex 73
	30, 32, -1, // vertex 74
	30, -1, -1, // vertex 75
	30, 32, -1, // vertex 76
	30, -1, -1, // vertex 77
	30, -1, -1, // vertex 78
	30, -1, -1, // vertex 79
	30, -1, -1, // vertex 80
	30, -1, -1, // vertex 81
	30, -1, -1, // vertex 82
	30, -1, -1, // vertex 83
	30, -1, -1, // vertex 84
	30, -1, -1, // vertex 85
	30, -1, -1, // vertex 86
	30, -1, -1, // vertex 87
	30, -1, -1, // vertex 88
	30, -1, -1, // vertex 89
	30, -1, -1, // vertex 90
	30, -1, -1, // vertex 91
	30, -1, -1, // vertex 92
	30, -1, -1, // vertex 93
	30, -1, -1, // vertex 94
	30, 31, -1, // vertex 95
	30, 31, -1, // vertex 96
	30, -1, -1, // vertex 97
	30, -1, -1, // vertex 98
	30, -1, -1, // vertex 99
	30, 29, -1, // vertex 100
	30, 29, -1, // vertex 101
	30, -1, -1, // vertex 102
	30, -1, -1, // vertex 103
	30, 31, -1, // vertex 104
	30, -1, -1, // vertex 105
	30, -1, -1, // vertex 106
	30, -1, -1, // vertex 107
	30, -1, -1, // vertex 108
	30, -1, -1, // vertex 109
	30, -1, -1, // vertex 110
	30, -1, -1, // vertex 111
	30, -1, -1, // vertex 112
	30, -1, -1, // vertex 113
	30, -1, -1, // vertex 114
	29, 30, -1, // vertex 115
	30, -1, -1, // vertex 116
	29, 30, -1, // vertex 117
	29, 30, -1, // vertex 118
	30, 29, -1, // vertex 119
	30, 29, -1, // vertex 120
	30, 33, -1, // vertex 121
	30, 33, -1, // vertex 122
	33, 30, -1, // vertex 123
	33, 30, -1, // vertex 124
	33, 30, -1, // vertex 125
	33, 30, -1, // vertex 126
	33, 30, -1, // vertex 127
	33, 30, -1, // vertex 128
	33, 30, -1, // vertex 129
	33, 30, -1, // vertex 130
	33, 30, -1, // vertex 131
	33, 30, -1, // vertex 132
	33, 30, -1, // vertex 133
	33, 30, -1, // vertex 134
	30, 33, -1, // vertex 135
	33, 30, -1, // vertex 136
	33, 30, -1, // vertex 137
	33, 30, -1, // vertex 138
	33, 30, -1, // vertex 139
	33, 30, -1, // vertex 140
	30, 33, -1, // vertex 141
	33, 30, -1, // vertex 142
	30, 33, -1, // vertex 143
	33, 30, -1, // vertex 144
	30, 33, -1, // vertex 145
	30, 33, -1, // vertex 146
	30, -1, -1, // vertex 147
	30, -1, -1, // vertex 148
	30, -1, -1, // vertex 149
	30, -1, -1, // vertex 150
	30, -1, -1, // vertex 151
	30, -1, -1, // vertex 152
	30, -1, -1, // vertex 153
	30, 32, -1, // vertex 154
	30, -1, -1, // vertex 155
	30, 32, -1, // vertex 156
	30, 32, -1, // vertex 157
	30, 32, -1, // vertex 158
	30, 32, -1, // vertex 159
	30, 32, -1, // vertex 160
	30, 32, -1, // vertex 161
	30, 32, -1, // vertex 162
	30, 32, -1, // vertex 163
	30, 32, -1, // vertex 164
	30, 32, -1, // vertex 165
	30, 32, -1, // vertex 166
	30, 32, -1, // vertex 167
	30, 32, -1, // vertex 168
	30, 32, -1, // vertex 169
	30, 32, -1, // vertex 170
	30, 32, -1, // vertex 171
	30, 32, -1, // vertex 172
	30, 32, -1, // vertex 173
	30, 32, -1, // vertex 174
	30, 32, -1, // vertex 175
	30, -1, -1, // vertex 176
	30, 32, -1, // vertex 177
	30, -1, -1, // vertex 178
	30, -1, -1, // vertex 179
	30, -1, -1, // vertex 180
	30, -1, -1, // vertex 181
	30, -1, -1, // vertex 182
	30, -1, -1, // vertex 183
	30, -1, -1, // vertex 184
	30, 33, -1, // vertex 185
	30, 33, -1, // vertex 186
	33, 30, -1, // vertex 187
	30, 33, -1, // vertex 188
	33, 30, -1, // vertex 189
	30, 33, -1, // vertex 190
	33, 30, -1, // vertex 191
	33, 30, -1, // vertex 192
	33, 30, -1, // vertex 193
	33, 30, -1, // vertex 194
	33, 30, -1, // vertex 195
	30, 33, -1, // vertex 196
	33, 30, -1, // vertex 197
	30, 31, -1, // vertex 198
	30, 31, -1, // vertex 199
	30, 31, -1, // vertex 200
	30, 31, -1, // vertex 201
	30, 31, -1, // vertex 202
	30, 31, -1, // vertex 203
	30, 31, -1, // vertex 204
	30, 31, -1, // vertex 205
	30, 31, -1, // vertex 206
	30, 31, -1, // vertex 207
	30, 31, -1, // vertex 208
	30, 31, -1, // vertex 209
	30, 31, -1, // vertex 210
	30, 31, -1, // vertex 211
	31, 30, -1, // vertex 212
	31, 30, -1, // vertex 213
	31, 30, -1, // vertex 214
	31, 30, -1, // vertex 215
	30, 31, -1, // vertex 216
	30, 31, -1, // vertex 217
	30, 31, -1, // vertex 218
	30, 31, -1, // vertex 219
	30, 31, -1, // vertex 220
	30, 31, -1, // vertex 221
	30, 31, -1, // vertex 222
	30, -1, -1, // vertex 223
	30, 31, -1, // vertex 224
	30, -1, -1, // vertex 225
	30, 31, -1, // vertex 226
	30, 29, -1, // vertex 227
	30, -1, -1, // vertex 228
	30, -1, -1, // vertex 229
	30, -1, -1, // vertex 230
	30, 31, -1, // vertex 231
	30, -1, -1, // vertex 232
	30, -1, -1, // vertex 233
	30, -1, -1, // vertex 234
	30, -1, -1, // vertex 235
	30, -1, -1, // vertex 236
	30, -1, -1, // vertex 237
	30, -1, -1, // vertex 238
	30, 31, -1, // vertex 239
	30, 31, -1, // vertex 240
	30, -1, -1, // vertex 241
	30, -1, -1, // vertex 242
	30, -1, -1, // vertex 243
	30, -1, -1, // vertex 244
	30, -1, -1, // vertex 245
	30, -1, -1, // vertex 246
	30, -1, -1, // vertex 247
	30, -1, -1, // vertex 248
	30, -1, -1, // vertex 249
	30, -1, -1, // vertex 250
	30, -1, -1, // vertex 251
	30, -1, -1, // vertex 252
	30, 33, -1, // vertex 253
	30, -1, -1, // vertex 254
	30, 33, -1, // vertex 255
	30, -1, -1, // vertex 256
	30, 33, -1, // vertex 257
	30, -1, -1, // vertex 258
	33, 30, -1, // vertex 259
	30, -1, -1, // vertex 260
	33, 30, -1, // vertex 261
	30, 31, -1, // vertex 262
	30, 31, -1, // vertex 263
	30, 31, -1, // vertex 264
	30, -1, -1, // vertex 265
	30, -1, -1, // vertex 266
	30, -1, -1, // vertex 267
	30, -1, -1, // vertex 268
	30, -1, -1, // vertex 269
	30, -1, -1, // vertex 270
	30, -1, -1, // vertex 271
	30, -1, -1, // vertex 272
	30, -1, -1, // vertex 273
	30, -1, -1, // vertex 274
	30, -1, -1, // vertex 275
	30, -1, -1, // vertex 276
	30, -1, -1, // vertex 277
	30, 33, -1, // vertex 278
	30, -1, -1, // vertex 279
	30, 32, -1, // vertex 280
	30, -1, -1, // vertex 281
	30, -1, -1, // vertex 282
	30, -1, -1, // vertex 283
	30, -1, -1, // vertex 284
	30, -1, -1, // vertex 285
	30, -1, -1, // vertex 286
	30, -1, -1, // vertex 287
	30, -1, -1, // vertex 288
	30, -1, -1, // vertex 289
	30, 32, -1, // vertex 290
	30, 32, -1, // vertex 291
	30, 32, -1, // vertex 292
	30, 32, -1, // vertex 293
	30, -1, -1, // vertex 294
	30, -1, -1, // vertex 295
	30, -1, -1, // vertex 296
	30, 32, -1, // vertex 297
	30, -1, -1, // vertex 298
	30, 32, -1, // vertex 299
	30, -1, -1, // vertex 300
	30, 32, -1, // vertex 301
	30, -1, -1, // vertex 302
	30, 32, -1, // vertex 303
	30, 32, -1, // vertex 304
	30, -1, -1, // vertex 305
	30, 33, -1, // vertex 306
	30, -1, -1, // vertex 307
	30, 33, -1, // vertex 308
	30, 32, -1, // vertex 309
	30, 32, -1, // vertex 310
	30, 32, -1, // vertex 311
	30, 32, -1, // vertex 312
	30, 32, -1, // vertex 313
	30, 32, -1, // vertex 314
	30, 32, -1, // vertex 315
	30, 32, -1, // vertex 316
	30, 32, -1, // vertex 317
	30, 32, -1, // vertex 318
	30, 32, -1, // vertex 319
	30, 32, -1, // vertex 320
	30, 32, -1, // vertex 321
	30, 32, -1, // vertex 322
	30, 32, -1, // vertex 323
	30, 32, -1, // vertex 324
	30, 32, -1, // vertex 325
	30, 32, -1, // vertex 326
	30, 32, -1, // vertex 327
	30, 32, -1, // vertex 328
	30, 32, -1, // vertex 329
	30, 32, -1, // vertex 330
	30, 32, -1, // vertex 331
	30, 32, -1, // vertex 332
	30, 32, -1, // vertex 333
	30, 32, -1, // vertex 334
	30, -1, -1, // vertex 335
	30, -1, -1, // vertex 336
	30, -1, -1, // vertex 337
	30, -1, -1, // vertex 338
	30, -1, -1, // vertex 339
	30, -1, -1, // vertex 340
	30, -1, -1, // vertex 341
	30, -1, -1, // vertex 342
	30, -1, -1, // vertex 343
	30, -1, -1, // vertex 344
	30, -1, -1, // vertex 345
	30, 31, -1, // vertex 346
	30, 31, -1, // vertex 347
	30, -1, -1, // vertex 348
	30, -1, -1, // vertex 349
	30, -1, -1, // vertex 350
	30, -1, -1, // vertex 351
	30, -1, -1, // vertex 352
	30, -1, -1, // vertex 353
	30, -1, -1, // vertex 354
	30, -1, -1, // vertex 355
	30, -1, -1, // vertex 356
	30, -1, -1, // vertex 357
	30, -1, -1, // vertex 358
	30, -1, -1, // vertex 359
	30, -1, -1, // vertex 360
	30, -1, -1, // vertex 361
	30, 33, -1, // vertex 362
	30, 33, -1, // vertex 363
	33, 30, -1, // vertex 364
	30, 31, -1, // vertex 365
	30, 31, -1, // vertex 366
	30, 31, -1, // vertex 367
	30, 31, -1, // vertex 368
	30, 31, -1, // vertex 369
	31, 30, -1, // vertex 370
	30, 31, -1, // vertex 371
	30, 31, -1, // vertex 372
	30, 31, -1, // vertex 373
	30, 31, -1, // vertex 374
	30, 31, -1, // vertex 375
	30, 31, -1, // vertex 376
	30, 31, -1, // vertex 377
	30, 31, -1, // vertex 378
	30, 31, -1, // vertex 379
	30, 31, -1, // vertex 380
	30, 31, -1, // vertex 381
	30, -1, -1, // vertex 382
	30, -1, -1, // vertex 383
	30, -1, -1, // vertex 384
	30, -1, -1, // vertex 385
	30, -1, -1, // vertex 386
	30, -1, -1, // vertex 387
	30, -1, -1, // vertex 388
	30, -1, -1, // vertex 389
	30, -1, -1, // vertex 390
	29, 30, -1, // vertex 391
	30, -1, -1, // vertex 392
	30, 29, -1, // vertex 393
	30, 31, -1, // vertex 394
	30, -1, -1, // vertex 395
	30, 31, -1, // vertex 396
	30, -1, -1, // vertex 397
	30, -1, -1, // vertex 398
	30, -1, -1, // vertex 399
	30, -1, -1, // vertex 400
	30, -1, -1, // vertex 401
	30, -1, -1, // vertex 402
	30, -1, -1, // vertex 403
	30, -1, -1, // vertex 404
	30, -1, -1, // vertex 405
	30, 31, -1, // vertex 406
	30, 31, -1, // vertex 407
	30, 31, -1, // vertex 408
	33, 30, -1, // vertex 409
	30, -1, -1, // vertex 410
	33, 30, -1, // vertex 411
	30, -1, -1, // vertex 412
	30, 33, -1, // vertex 413
	30, -1, -1, // vertex 414
	30, 33, -1, // vertex 415
	30, -1, -1, // vertex 416
	30, 33, -1, // vertex 417
	30, -1, -1, // vertex 418
	30, -1, -1, // vertex 419
	30, -1, -1, // vertex 420
	30, -1, -1, // vertex 421
	30, 33, -1, // vertex 422
	30, -1, -1, // vertex 423
	30, -1, -1, // vertex 424
	30, -1, -1, // vertex 425
	30, -1, -1, // vertex 426
	30, -1, -1, // vertex 427
	30, -1, -1, // vertex 428
	30, -1, -1, // vertex 429
	30, -1, -1, // vertex 430
	30, -1, -1, // vertex 431
	30, -1, -1, // vertex 432
	30, -1, -1, // vertex 433
	30, -1, -1, // vertex 434
	30, -1, -1, // vertex 435
	30, 31, -1, // vertex 436
	30, 31, -1, // vertex 437
	30, -1, -1, // vertex 438
	30, -1, -1, // vertex 439
	30, -1, -1, // vertex 440
	30, 32, -1, // vertex 441
	30, -1, -1, // vertex 442
	30, -1, -1, // vertex 443
	30, -1, -1, // vertex 444
	30, -1, -1, // vertex 445
	30, -1, -1, // vertex 446
	30, -1, -1, // vertex 447
	30, -1, -1, // vertex 448
	30, -1, -1, // vertex 449
	30, -1, -1, // vertex 450
	30, 32, -1, // vertex 451
	30, 32, -1, // vertex 452
	30, 32, -1, // vertex 453
	30, 32, -1, // vertex 454
	30, -1, -1, // vertex 455
	30, 32, -1, // vertex 456
	30, -1, -1, // vertex 457
	30, 32, -1, // vertex 458
	30, -1, -1, // vertex 459
	30, 32, -1, // vertex 460
	30, -1, -1, // vertex 461
	30, -1, -1, // vertex 462
	30, 32, -1, // vertex 463
	30, 32, -1, // vertex 464
	30, 32, -1, // vertex 465
	30, 32, -1, // vertex 466
	30, 32, -1, // vertex 467
	30, 32, -1, // vertex 468
	30, 32, -1, // vertex 469
	30, 32, -1, // vertex 470
	30, 32, -1, // vertex 471
	30, 32, -1, // vertex 472
	30, 32, -1, // vertex 473
	30, 32, -1, // vertex 474
	30, 32, -1, // vertex 475
	30, 32, -1, // vertex 476
	30, 32, -1, // vertex 477
	30, 32, -1, // vertex 478
	30, 32, -1, // vertex 479
	30, 32, -1, // vertex 480
	30, 32, -1, // vertex 481
	30, 32, -1, // vertex 482
	30, 32, -1, // vertex 483
	30, 32, -1, // vertex 484
	30, 32, -1, // vertex 485
	30, -1, -1, // vertex 486
	30, -1, -1, // vertex 487
	30, -1, -1, // vertex 488
	30, -1, -1, // vertex 489
	30, -1, -1, // vertex 490
	30, -1, -1, // vertex 491
	30, -1, -1, // vertex 492
	30, -1, -1, // vertex 493
	30, -1, -1, // vertex 494
	30, -1, -1, // vertex 495
	30, -1, -1, // vertex 496
	30, -1, -1, // vertex 497
	30, -1, -1, // vertex 498
	30, -1, -1, // vertex 499
	30, -1, -1, // vertex 500
	29, 30, -1, // vertex 501
	30, -1, -1, // vertex 502
	30, -1, -1, // vertex 503
	30, -1, -1, // vertex 504
	30, -1, -1, // vertex 505
	30, -1, -1, // vertex 506
	30, -1, -1, // vertex 507
	30, -1, -1, // vertex 508
	30, -1, -1, // vertex 509
	30, -1, -1, // vertex 510
	30, -1, -1, // vertex 511
	30, -1, -1, // vertex 512
	30, -1, -1, // vertex 513
	30, -1, -1, // vertex 514
	30, -1, -1, // vertex 515
	30, 32, -1, // vertex 516
	30, 32, -1, // vertex 517
	30, 32, -1, // vertex 518
	30, -1, -1, // vertex 519
	30, -1, -1, // vertex 520
	30, -1, -1, // vertex 521
	30, 31, -1, // vertex 522
	30, 31, -1, // vertex 523
	30, 31, -1, // vertex 524
	30, 31, -1, // vertex 525
	30, 31, -1, // vertex 526
	30, 31, -1, // vertex 527
	30, 31, -1, // vertex 528
	30, 31, -1, // vertex 529
	30, 31, -1, // vertex 530
	30, 31, -1, // vertex 531
	30, 31, -1, // vertex 532
	30, 31, -1, // vertex 533
	30, 31, -1, // vertex 534
	30, 31, -1, // vertex 535
	30, 31, -1, // vertex 536
	30, 31, -1, // vertex 537
	30, 31, -1, // vertex 538
	30, 31, -1, // vertex 539
	30, 31, -1, // vertex 540
	30, 31, -1, // vertex 541
	30, 31, -1, // vertex 542
	30, 31, -1, // vertex 543
	30, 31, -1, // vertex 544
	30, 31, -1, // vertex 545
	30, 31, -1, // vertex 546
	30, 31, -1, // vertex 547
	30, 31, -1, // vertex 548
	30, 31, -1, // vertex 549
	30, 31, -1, // vertex 550
	30, 31, -1, // vertex 551
	30, 31, -1, // vertex 552
	31, 30, -1, // vertex 553
	30, 31, -1, // vertex 554
	31, 30, -1, // vertex 555
	31, 30, -1, // vertex 556
	30, 31, -1, // vertex 557
	30, 31, -1, // vertex 558
	30, 31, -1, // vertex 559
	30, 31, -1, // vertex 560
	30, 31, -1, // vertex 561
	31, 30, -1, // vertex 562
	30, 31, -1, // vertex 563
	30, 31, -1, // vertex 564
	30, 31, -1, // vertex 565
	30, 31, -1, // vertex 566
	30, 31, -1, // vertex 567
	30, 31, -1, // vertex 568
	31, 30, -1, // vertex 569
	31, 30, -1, // vertex 570
	30, -1, -1, // vertex 571
	30, -1, -1, // vertex 572
	30, -1, -1, // vertex 573
	30, -1, -1, // vertex 574
	30, -1, -1, // vertex 575
	30, -1, -1, // vertex 576
	30, -1, -1, // vertex 577
	30, -1, -1, // vertex 578
	30, -1, -1, // vertex 579
	30, -1, -1, // vertex 580
	30, -1, -1, // vertex 581
	30, -1, -1, // vertex 582
	30, -1, -1, // vertex 583
	30, -1, -1, // vertex 584
	30, -1, -1, // vertex 585
	30, -1, -1, // vertex 586
	30, -1, -1, // vertex 587
	30, -1, -1, // vertex 588
	30, -1, -1, // vertex 589
	30, -1, -1, // vertex 590
	30, -1, -1, // vertex 591
	30, 31, -1, // vertex 592
	30, -1, -1, // vertex 593
	30, 31, -1, // vertex 594
	30, 29, -1, // vertex 595
	30, 29, -1, // vertex 596
	30, -1, -1, // vertex 597
	29, 30, -1, // vertex 598
	30, 29, -1, // vertex 599
	30, 29, -1, // vertex 600
	30, 31, -1, // vertex 601
	30, -1, -1, // vertex 602
	30, 31, -1, // vertex 603
	33, 30, -1, // vertex 604
	33, 30, -1, // vertex 605
	33, 30, -1, // vertex 606
	33, 30, -1, // vertex 607
	33, 30, -1, // vertex 608
	30, 33, -1, // vertex 609
	30, 33, -1, // vertex 610
	30, 33, -1, // vertex 611
	30, 32, -1, // vertex 612
	30, 33, -1, // vertex 613
	30, 33, -1, // vertex 614
	33, 30, -1, // vertex 615
	33, 30, -1, // vertex 616
	33, 30, -1, // vertex 617
	33, 30, -1, // vertex 618
	33, 30, -1, // vertex 619
	33, 30, -1, // vertex 620
	33, 30, -1, // vertex 621
	33, 30, -1, // vertex 622
	33, 30, -1, // vertex 623
	33, 30, -1, // vertex 624
	33, 30, -1, // vertex 625
	30, 33, -1, // vertex 626
	30, 33, -1, // vertex 627
	30, 33, -1, // vertex 628
	30, 32, -1, // vertex 629
	30, 32, -1, // vertex 630
	30, -1, -1, // vertex 631
	30, -1, -1, // vertex 632
	30, -1, -1, // vertex 633
	30, -1, -1, // vertex 634
	30, -1, -1, // vertex 635
	30, -1, -1, // vertex 636
	30, 33, -1, // vertex 637
	30, 33, -1, // vertex 638
	30, -1, -1, // vertex 639
	30, 32, -1, // vertex 640
	30, -1, -1, // vertex 641
	30, 32, -1, // vertex 642
	30, -1, -1, // vertex 643
	30, 32, -1, // vertex 644
	30, -1, -1, // vertex 645
	30, -1, -1, // vertex 646
	30, -1, -1, // vertex 647
	30, 32, -1, // vertex 648
	30, 32, -1, // vertex 649
	30, 32, -1, // vertex 650
	30, 32, -1, // vertex 651
	30, 32, -1, // vertex 652
	30, 32, -1, // vertex 653
	30, 32, -1, // vertex 654
	30, 32, -1, // vertex 655
	30, 32, -1, // vertex 656
	30, 32, -1, // vertex 657
	30, 32, -1, // vertex 658
	30, -1, -1, // vertex 659
	30, -1, -1, // vertex 660
	30, -1, -1, // vertex 661
	33, 30, -1, // vertex 662
	33, 30, -1, // vertex 663
	33, 30, -1, // vertex 664
	33, 30, -1, // vertex 665
	33, 30, -1, // vertex 666
	33, 30, -1, // vertex 667
	33, 30, -1, // vertex 668
	33, 30, -1, // vertex 669
	33, 30, -1, // vertex 670
	33, 30, -1, // vertex 671
	33, 30, -1, // vertex 672
	33, 30, -1, // vertex 673
	33, 30, -1, // vertex 674
	33, 30, -1, // vertex 675
	30, 33, -1, // vertex 676
	30, 33, -1, // vertex 677
	33, 30, -1, // vertex 678
	33, 30, -1, // vertex 679
	33, 30, -1, // vertex 680
	33, 30, -1, // vertex 681
	33, 30, -1, // vertex 682
	30, -1, -1, // vertex 683
	30, -1, -1, // vertex 684
	30, -1, -1, // vertex 685
	30, -1, -1, // vertex 686
	30, -1, -1, // vertex 687
	30, -1, -1, // vertex 688
	30, 32, -1, // vertex 689
	30, -1, -1, // vertex 690
	30, 32, -1, // vertex 691
	30, -1, -1, // vertex 692
	30, 32, -1, // vertex 693
	30, -1, -1, // vertex 694
	33, 30, -1, // vertex 695
	30, 33, -1, // vertex 696
	30, 33, -1, // vertex 697
	30, -1, -1, // vertex 698
	30, -1, -1, // vertex 699
	30, -1, -1, // vertex 700
	30, 32, -1, // vertex 701
	30, 32, -1, // vertex 702
	30, 32, -1, // vertex 703
	30, 32, -1, // vertex 704
	30, 32, -1, // vertex 705
	30, 32, -1, // vertex 706
	30, 32, -1, // vertex 707
	30, 32, -1, // vertex 708
	30, 32, -1, // vertex 709
	30, 32, -1, // vertex 710
	30, 32, -1, // vertex 711
	30, -1, -1, // vertex 712
	30, -1, -1, // vertex 713
	30, -1, -1, // vertex 714
	30, 31, -1, // vertex 715
	30, 31, -1, // vertex 716
	30, 31, -1, // vertex 717
	30, 31, -1, // vertex 718
	30, 31, -1, // vertex 719
	30, 31, -1, // vertex 720
	30, 31, -1, // vertex 721
	30, 31, -1, // vertex 722
	30, 31, -1, // vertex 723
	30, 31, -1, // vertex 724
	30, 31, -1, // vertex 725
	30, 31, -1, // vertex 726
	30, 31, -1, // vertex 727
	30, 31, -1, // vertex 728
	30, 31, -1, // vertex 729
	30, 31, -1, // vertex 730
	30, 31, -1, // vertex 731
	30, 31, -1, // vertex 732
	30, 31, -1, // vertex 733
	30, 31, -1, // vertex 734
	30, 31, -1, // vertex 735
	30, 31, -1, // vertex 736
	30, 31, -1, // vertex 737
	30, 31, -1, // vertex 738
	30, 31, -1, // vertex 739
	30, 31, -1, // vertex 740
	30, 31, -1, // vertex 741
	30, 31, -1, // vertex 742
	30, 31, -1, // vertex 743
	30, 31, -1, // vertex 744
	30, 31, -1, // vertex 745
	30, 31, -1, // vertex 746
	30, 31, -1, // vertex 747
	30, 31, -1, // vertex 748
	30, 31, -1, // vertex 749
	30, -1, -1, // vertex 750
	30, -1, -1, // vertex 751
	30, 31, -1, // vertex 752
	30, -1, -1, // vertex 753
	30, -1, -1, // vertex 754
	30, 32, -1, // vertex 755
	30, 32, -1, // vertex 756
	30, 32, -1, // vertex 757
	30, 32, -1, // vertex 758
	30, 32, -1, // vertex 759
	30, 32, -1, // vertex 760
	30, 32, -1, // vertex 761
	30, 32, -1, // vertex 762
	30, -1, -1, // vertex 763
	30, -1, -1, // vertex 764
	30, 31, -1, // vertex 765
	30, -1, -1, // vertex 766
	30, -1, -1, // vertex 767
	30, -1, -1, // vertex 768
	30, -1, -1, // vertex 769
	30, 31, -1, // vertex 770
	30, 31, -1, // vertex 771
	30, 31, -1, // vertex 772
	30, 31, -1, // vertex 773
	30, 31, -1, // vertex 774
	30, 31, -1, // vertex 775
	30, 31, -1, // vertex 776
	30, 33, 31, // vertex 777
	30, 31, -1, // vertex 778
	30, 33, 31, // vertex 779
	30, 31, -1, // vertex 780
	30, 33, 31, // vertex 781
	30, 33, 31, // vertex 782
	30, 33, -1, // vertex 783
	30, 33, -1, // vertex 784
	30, 33, 31, // vertex 785
	30, 33, -1, // vertex 786
	30, 33, 31, // vertex 787
	30, 33, -1, // vertex 788
	30, 33, 31, // vertex 789
	33, 30, -1, // vertex 790
	30, 31, -1, // vertex 791
	30, 31, -1, // vertex 792
	30, 31, -1, // vertex 793
	30, 31, -1, // vertex 794
	30, 31, -1, // vertex 795
	30, -1, -1, // vertex 796
	30, 31, -1, // vertex 797
	30, 31, -1, // vertex 798
	30, 31, -1, // vertex 799
	30, 31, -1, // vertex 800
	30, 31, -1, // vertex 801
	30, 31, -1, // vertex 802
	30, 31, -1, // vertex 803
	30, 31, -1, // vertex 804
	30, 31, -1, // vertex 805
	30, 31, -1, // vertex 806
	30, 31, -1, // vertex 807
	30, 31, -1, // vertex 808
	30, 31, -1, // vertex 809
	30, 31, -1, // vertex 810
	30, 31, -1, // vertex 811
	30, 31, -1, // vertex 812
	30, 31, -1, // vertex 813
	30, 31, -1, // vertex 814
	30, 31, -1, // vertex 815
	30, 31, -1, // vertex 816
	30, 31, -1, // vertex 817
	30, -1, -1, // vertex 818
	30, 31, -1, // vertex 819
	30, 31, -1, // vertex 820
	30, 31, -1, // vertex 821
	30, 31, -1, // vertex 822
	30, 31, -1, // vertex 823
	30, 31, -1, // vertex 824
	30, 31, -1, // vertex 825
	30, 31, -1, // vertex 826
	30, 31, -1, // vertex 827
	30, 31, -1, // vertex 828
	30, 31, -1, // vertex 829
	30, 31, -1, // vertex 830
	30, 33, 31, // vertex 831
	30, 31, -1, // vertex 832
	30, 31, -1, // vertex 833
	30, 31, -1, // vertex 834
	30, 31, -1, // vertex 835
	30, 31, -1, // vertex 836
	30, 31, -1, // vertex 837
	30, 31, -1, // vertex 838
	30, 31, -1, // vertex 839
	30, 31, -1, // vertex 840
	30, -1, -1, // vertex 841
	30, -1, -1, // vertex 842
	30, 31, -1, // vertex 843
	30, 31, -1, // vertex 844
	30, 31, -1, // vertex 845
	30, 31, -1, // vertex 846
	30, 31, -1, // vertex 847
	33, 30, -1, // vertex 848
	30, 31, -1, // vertex 849
	30, 33, -1, // vertex 850
	30, 33, 31, // vertex 851
	30, 31, -1, // vertex 852
	30, 31, -1, // vertex 853
	30, 31, -1, // vertex 854
	30, 31, -1, // vertex 855
	30, 31, -1, // vertex 856
	30, 31, -1, // vertex 857
	30, 31, -1, // vertex 858
	30, 31, -1, // vertex 859
	30, 31, -1, // vertex 860
	30, 31, -1, // vertex 861
	30, 31, -1, // vertex 862
	30, 31, -1, // vertex 863
	30, 31, -1, // vertex 864
	30, 31, -1, // vertex 865
	30, 31, -1, // vertex 866
	30, 31, -1, // vertex 867
	30, 31, -1, // vertex 868
	30, 31, -1, // vertex 869
	30, 31, -1, // vertex 870
	30, 31, -1, // vertex 871
	30, 31, -1, // vertex 872
	30, 31, -1, // vertex 873
	30, 31, -1, // vertex 874
	30, 31, -1, // vertex 875
	30, 31, -1, // vertex 876
	30, 31, -1, // vertex 877
	30, 31, -1, // vertex 878
	30, 31, -1, // vertex 879
	30, 31, -1, // vertex 880
	30, 31, -1, // vertex 881
	30, 31, -1, // vertex 882
	30, 31, -1, // vertex 883
	30, 31, -1, // vertex 884
	30, -1, -1, // vertex 885
	30, 31, -1, // vertex 886
	30, 31, -1, // vertex 887
	30, 31, -1, // vertex 888
	30, 31, -1, // vertex 889
	30, 31, -1, // vertex 890
	30, 31, -1, // vertex 891
	30, 31, -1, // vertex 892
	30, 31, -1, // vertex 893
	30, -1, -1, // vertex 894
	30, 31, -1, // vertex 895
	30, 31, -1, // vertex 896
	30, 31, -1, // vertex 897
	30, 32, -1, // vertex 898
	30, 32, -1, // vertex 899
	30, 32, -1, // vertex 900
	30, 32, -1, // vertex 901
	30, 32, -1, // vertex 902
	30, 32, -1, // vertex 903
	30, 32, -1, // vertex 904
	30, 32, -1, // vertex 905
	30, 32, -1, // vertex 906
	30, 32, -1, // vertex 907
	30, 32, -1, // vertex 908
	30, 32, -1, // vertex 909
	30, 32, -1, // vertex 910
	30, 32, -1, // vertex 911
	30, 32, -1, // vertex 912
	30, 32, -1, // vertex 913
	30, 32, -1, // vertex 914
	30, 32, -1, // vertex 915
	30, 32, -1, // vertex 916
	30, 32, -1, // vertex 917
	30, 32, -1, // vertex 918
	30, 32, -1, // vertex 919
	30, 32, -1, // vertex 920
	30, 32, -1, // vertex 921
	30, 32, -1, // vertex 922
	30, 32, -1, // vertex 923
	30, 32, -1, // vertex 924
	30, 32, -1, // vertex 925
	30, 32, -1, // vertex 926
	30, 32, -1, // vertex 927
	30, 32, -1, // vertex 928
	30, 32, -1, // vertex 929
	30, 32, -1, // vertex 930
	30, 32, -1, // vertex 931
	30, 32, -1, // vertex 932
	30, 32, -1, // vertex 933
	30, 32, -1, // vertex 934
	30, 32, -1, // vertex 935
	30, 32, -1, // vertex 936
	30, 32, -1, // vertex 937
	30, 32, -1, // vertex 938
	30, 32, -1, // vertex 939
	30, 32, -1, // vertex 940
	30, 32, -1, // vertex 941
	30, 32, -1, // vertex 942
	30, 32, -1, // vertex 943
	30, 32, -1, // vertex 944
	30, 32, -1, // vertex 945
	30, 32, -1, // vertex 946
	30, 32, -1, // vertex 947
	30, 32, -1, // vertex 948
	30, 32, -1, // vertex 949
	30, 32, -1, // vertex 950
	30, 32, -1, // vertex 951
	30, 32, -1, // vertex 952
	30, 32, -1, // vertex 953
	30, 32, -1, // vertex 954
	30, 32, -1, // vertex 955
	30, 32, -1, // vertex 956
	30, 32, -1, // vertex 957
	30, 32, -1, // vertex 958
	30, 32, -1, // vertex 959
	30, 32, -1, // vertex 960
	30, 32, -1, // vertex 961
	30, 32, -1, // vertex 962
	30, 32, -1, // vertex 963
	30, 32, -1, // vertex 964
	30, 32, -1, // vertex 965
	30, 32, -1, // vertex 966
	30, 32, -1, // vertex 967
	30, 32, -1, // vertex 968
	30, 32, -1, // vertex 969
	30, 32, -1, // vertex 970
	30, 32, -1, // vertex 971
	30, 32, -1, // vertex 972
	30, 32, -1, // vertex 973
	30, 32, -1, // vertex 974
	30, 32, -1, // vertex 975
	30, -1, -1, // vertex 976
	30, -1, -1, // vertex 977
	30, -1, -1, // vertex 978
	30, -1, -1, // vertex 979
	30, -1, -1, // vertex 980
	30, -1, -1, // vertex 981
	30, -1, -1, // vertex 982
	30, -1, -1, // vertex 983
	30, -1, -1, // vertex 984
	30, -1, -1, // vertex 985
	30, -1, -1, // vertex 986
	30, -1, -1, // vertex 987
	30, -1, -1, // vertex 988
	30, -1, -1, // vertex 989
	30, -1, -1, // vertex 990
	30, -1, -1, // vertex 991
	30, -1, -1, // vertex 992
	30, -1, -1, // vertex 993
	30, -1, -1, // vertex 994
	30, -1, -1, // vertex 995
	30, -1, -1, // vertex 996
	30, -1, -1, // vertex 997
	30, -1, -1, // vertex 998
	30, -1, -1, // vertex 999
	30, -1, -1, // vertex 1000
	30, -1, -1, // vertex 1001
	30, -1, -1, // vertex 1002
	30, -1, -1, // vertex 1003
	30, -1, -1, // vertex 1004
	30, -1, -1, // vertex 1005
	30, -1, -1, // vertex 1006
	30, -1, -1, // vertex 1007
	30, -1, -1, // vertex 1008
	30, -1, -1, // vertex 1009
	30, -1, -1, // vertex 1010
	30, -1, -1, // vertex 1011
	30, -1, -1, // vertex 1012
	30, -1, -1, // vertex 1013
	30, -1, -1, // vertex 1014
	30, -1, -1, // vertex 1015
	30, -1, -1, // vertex 1016
	30, -1, -1, // vertex 1017
	30, -1, -1, // vertex 1018
	30, -1, -1, // vertex 1019
	30, -1, -1, // vertex 1020
	30, -1, -1, // vertex 1021
	30, -1, -1, // vertex 1022
	30, -1, -1, // vertex 1023
	30, -1, -1, // vertex 1024
	30, -1, -1, // vertex 1025
	30, -1, -1, // vertex 1026
	30, -1, -1, // vertex 1027
	30, -1, -1, // vertex 1028
	30, -1, -1, // vertex 1029
	30, -1, -1, // vertex 1030
	30, -1, -1, // vertex 1031
	30, -1, -1, // vertex 1032
	30, -1, -1, // vertex 1033
	30, -1, -1, // vertex 1034
	30, -1, -1, // vertex 1035
	30, -1, -1, // vertex 1036
	30, -1, -1, // vertex 1037
	30, -1, -1, // vertex 1038
	30, -1, -1, // vertex 1039
	30, -1, -1, // vertex 1040
	30, -1, -1, // vertex 1041
	30, -1, -1, // vertex 1042
	30, -1, -1, // vertex 1043
	30, -1, -1, // vertex 1044
	30, -1, -1, // vertex 1045
	31, -1, -1, // vertex 1046
	31, -1, -1, // vertex 1047
	31, -1, -1, // vertex 1048
	31, -1, -1, // vertex 1049
	31, -1, -1, // vertex 1050
	31, -1, -1, // vertex 1051
	31, -1, -1, // vertex 1052
	31, -1, -1, // vertex 1053
	31, -1, -1, // vertex 1054
	31, -1, -1, // vertex 1055
	31, -1, -1, // vertex 1056
	31, -1, -1, // vertex 1057
	31, -1, -1, // vertex 1058
	31, -1, -1, // vertex 1059
	31, -1, -1, // vertex 1060
	31, -1, -1, // vertex 1061
	31, -1, -1, // vertex 1062
	31, -1, -1, // vertex 1063
	31, -1, -1, // vertex 1064
	31, -1, -1, // vertex 1065
	31, -1, -1, // vertex 1066
	31, -1, -1, // vertex 1067
	31, -1, -1, // vertex 1068
	31, -1, -1, // vertex 1069
	31, -1, -1, // vertex 1070
	31, -1, -1, // vertex 1071
	31, -1, -1, // vertex 1072
	31, -1, -1, // vertex 1073
	31, -1, -1, // vertex 1074
	31, -1, -1, // vertex 1075
	31, -1, -1, // vertex 1076
	31, -1, -1, // vertex 1077
	31, -1, -1, // vertex 1078
	31, -1, -1, // vertex 1079
	31, -1, -1, // vertex 1080
	30, -1, -1, // vertex 1081
	30, -1, -1, // vertex 1082
	30, -1, -1, // vertex 1083
	30, -1, -1, // vertex 1084
	30, -1, -1, // vertex 1085
	30, -1, -1, // vertex 1086
	30, -1, -1, // vertex 1087
	30, -1, -1, // vertex 1088
	30, -1, -1, // vertex 1089
	30, -1, -1, // vertex 1090
	30, -1, -1, // vertex 1091
	30, -1, -1, // vertex 1092
	30, -1, -1, // vertex 1093
	30, -1, -1, // vertex 1094
	30, -1, -1, // vertex 1095
	30, -1, -1, // vertex 1096
	30, -1, -1, // vertex 1097
	30, -1, -1, // vertex 1098
	30, -1, -1, // vertex 1099
	30, -1, -1, // vertex 1100
	30, -1, -1, // vertex 1101
	30, -1, -1, // vertex 1102
	30, -1, -1, // vertex 1103
	31, -1, -1, // vertex 1104
	31, -1, -1, // vertex 1105
	31, -1, -1, // vertex 1106
	31, -1, -1, // vertex 1107
	31, -1, -1, // vertex 1108
	31, -1, -1, // vertex 1109
	31, -1, -1, // vertex 1110
	31, -1, -1, // vertex 1111
	31, -1, -1, // vertex 1112
	31, -1, -1, // vertex 1113
	31, -1, -1, // vertex 1114
	31, -1, -1, // vertex 1115
	31, -1, -1, // vertex 1116
	31, -1, -1, // vertex 1117
	31, -1, -1, // vertex 1118
	31, -1, -1, // vertex 1119
	31, -1, -1, // vertex 1120
	31, -1, -1, // vertex 1121
	31, -1, -1, // vertex 1122
	31, -1, -1, // vertex 1123
	31, -1, -1, // vertex 1124
	31, -1, -1, // vertex 1125
	31, -1, -1, // vertex 1126
	31, -1, -1, // vertex 1127
	31, -1, -1, // vertex 1128
	31, -1, -1, // vertex 1129
	31, -1, -1, // vertex 1130
	31, -1, -1, // vertex 1131
	31, -1, -1, // vertex 1132
	31, -1, -1, // vertex 1133
	31, -1, -1, // vertex 1134
	30, -1, -1, // vertex 1135
	30, -1, -1, // vertex 1136
	30, -1, -1, // vertex 1137
	30, -1, -1, // vertex 1138
	30, -1, -1, // vertex 1139
	30, -1, -1, // vertex 1140
	30, -1, -1, // vertex 1141
	30, -1, -1, // vertex 1142
	30, -1, -1, // vertex 1143
	30, -1, -1, // vertex 1144
	30, -1, -1, // vertex 1145
	30, -1, -1, // vertex 1146
	30, -1, -1, // vertex 1147
	30, -1, -1, // vertex 1148
	30, -1, -1, // vertex 1149
	30, -1, -1, // vertex 1150
	30, -1, -1, // vertex 1151
	30, -1, -1, // vertex 1152
	30, -1, -1, // vertex 1153
	30, -1, -1, // vertex 1154
	30, -1, -1, // vertex 1155
	30, -1, -1, // vertex 1156
	30, -1, -1, // vertex 1157
	30, -1, -1, // vertex 1158
	30, -1, -1, // vertex 1159
	30, -1, -1, // vertex 1160
	30, -1, -1, // vertex 1161
	30, -1, -1, // vertex 1162
	30, -1, -1, // vertex 1163
	30, -1, -1, // vertex 1164
	30, -1, -1, // vertex 1165
	30, -1, -1, // vertex 1166
	30, -1, -1, // vertex 1167
	30, -1, -1, // vertex 1168
	30, -1, -1, // vertex 1169
	30, -1, -1, // vertex 1170
	30, -1, -1, // vertex 1171
	30, -1, -1, // vertex 1172
	30, -1, -1, // vertex 1173
	30, -1, -1, // vertex 1174
	30, -1, -1, // vertex 1175
	30, -1, -1, // vertex 1176
	30, -1, -1, // vertex 1177
	30, -1, -1, // vertex 1178
	30, -1, -1, // vertex 1179
	30, 31, -1, // vertex 1180
	30, -1, -1, // vertex 1181
	30, -1, -1, // vertex 1182
	30, 32, -1, // vertex 1183
	30, 32, -1, // vertex 1184
	30, 32, -1, // vertex 1185
	30, 32, -1, // vertex 1186
	30, 32, -1, // vertex 1187
	30, 32, -1, // vertex 1188
	30, 32, -1, // vertex 1189
	30, 32, -1, // vertex 1190
	30, -1, -1, // vertex 1191
	30, -1, -1, // vertex 1192
	30, 31, -1, // vertex 1193
	30, -1, -1, // vertex 1194
	30, -1, -1, // vertex 1195
	30, -1, -1, // vertex 1196
	30, -1, -1, // vertex 1197
	30, 31, -1, // vertex 1198
	30, 31, -1, // vertex 1199
	30, 31, -1, // vertex 1200
	30, 31, -1, // vertex 1201
	30, 31, -1, // vertex 1202
	30, 31, -1, // vertex 1203
	30, 31, -1, // vertex 1204
	30, 33, 31, // vertex 1205
	30, 31, -1, // vertex 1206
	30, 33, 31, // vertex 1207
	30, 31, -1, // vertex 1208
	30, 33, 31, // vertex 1209
	30, 33, 31, // vertex 1210
	30, 33, -1, // vertex 1211
	30, 33, -1, // vertex 1212
	30, 33, 31, // vertex 1213
	30, 33, -1, // vertex 1214
	30, 33, 31, // vertex 1215
	30, 33, -1, // vertex 1216
	30, 33, 31, // vertex 1217
	33, 30, -1, // vertex 1218
	30, 31, -1, // vertex 1219
	30, 31, -1, // vertex 1220
	30, 31, -1, // vertex 1221
	30, 31, -1, // vertex 1222
	30, 31, -1, // vertex 1223
	30, -1, -1, // vertex 1224
	30, 31, -1, // vertex 1225
	30, 31, -1, // vertex 1226
	30, 31, -1, // vertex 1227
	30, 31, -1, // vertex 1228
	30, 31, -1, // vertex 1229
	30, 31, -1, // vertex 1230
	30, 31, -1, // vertex 1231
	30, 31, -1, // vertex 1232
	30, 31, -1, // vertex 1233
	30, 31, -1, // vertex 1234
	30, 31, -1, // vertex 1235
	30, 31, -1, // vertex 1236
	30, 31, -1, // vertex 1237
	30, 31, -1, // vertex 1238
	30, 31, -1, // vertex 1239
	30, 31, -1, // vertex 1240
	30, 31, -1, // vertex 1241
	30, 31, -1, // vertex 1242
	30, 31, -1, // vertex 1243
	30, 31, -1, // vertex 1244
	30, 31, -1, // vertex 1245
	30, -1, -1, // vertex 1246
	30, 31, -1, // vertex 1247
	30, 31, -1, // vertex 1248
	30, 31, -1, // vertex 1249
	30, 31, -1, // vertex 1250
	30, 31, -1, // vertex 1251
	30, 31, -1, // vertex 1252
	30, 31, -1, // vertex 1253
	30, 31, -1, // vertex 1254
	30, 31, -1, // vertex 1255
	30, 31, -1, // vertex 1256
	30, 31, -1, // vertex 1257
	30, 31, -1, // vertex 1258
	30, 33, 31, // vertex 1259
	30, 31, -1, // vertex 1260
	30, 31, -1, // vertex 1261
	30, 31, -1, // vertex 1262
	30, 31, -1, // vertex 1263
	30, 31, -1, // vertex 1264
	30, 31, -1, // vertex 1265
	30, 31, -1, // vertex 1266
	30, 31, -1, // vertex 1267
	30, 31, -1, // vertex 1268
	30, -1, -1, // vertex 1269
	30, -1, -1, // vertex 1270
	30, 31, -1, // vertex 1271
	30, 31, -1, // vertex 1272
	30, 31, -1, // vertex 1273
	30, 31, -1, // vertex 1274
	30, 31, -1, // vertex 1275
	33, 30, -1, // vertex 1276
	30, 31, -1, // vertex 1277
	30, 33, -1, // vertex 1278
	30, 33, 31, // vertex 1279
	30, 31, -1, // vertex 1280
	30, 31, -1, // vertex 1281
	30, 31, -1, // vertex 1282
	30, 31, -1, // vertex 1283
	30, 31, -1, // vertex 1284
	30, 31, -1, // vertex 1285
	30, 31, -1, // vertex 1286
	30, 31, -1, // vertex 1287
	30, 31, -1, // vertex 1288
	30, 31, -1, // vertex 1289
	30, 31, -1, // vertex 1290
	30, 31, -1, // vertex 1291
	30, 31, -1, // vertex 1292
	30, 31, -1, // vertex 1293
	30, 31, -1, // vertex 1294
	30, 31, -1, // vertex 1295
	30, 31, -1, // vertex 1296
	30, 31, -1, // vertex 1297
	30, 31, -1, // vertex 1298
	30, 31, -1, // vertex 1299
	30, 31, -1, // vertex 1300
	30, 31, -1, // vertex 1301
	30, 31, -1, // vertex 1302
	30, 31, -1, // vertex 1303
	30, 31, -1, // vertex 1304
	30, 31, -1, // vertex 1305
	30, 31, -1, // vertex 1306
	30, 31, -1, // vertex 1307
	30, 31, -1, // vertex 1308
	30, 31, -1, // vertex 1309
	30, 31, -1, // vertex 1310
	30, 31, -1, // vertex 1311
	30, 31, -1, // vertex 1312
	30, -1, -1, // vertex 1313
	30, 31, -1, // vertex 1314
	30, 31, -1, // vertex 1315
	30, 31, -1, // vertex 1316
	30, 31, -1, // vertex 1317
	30, 31, -1, // vertex 1318
	30, 31, -1, // vertex 1319
	30, 31, -1, // vertex 1320
	30, 31, -1, // vertex 1321
	30, -1, -1, // vertex 1322
	30, 31, -1, // vertex 1323
	30, 31, -1, // vertex 1324
	30, 31, -1, // vertex 1325
	30, 32, -1, // vertex 1326
	30, 32, -1, // vertex 1327
	30, 32, -1, // vertex 1328
	30, 32, -1, // vertex 1329
	30, 32, -1, // vertex 1330
	30, 32, -1, // vertex 1331
	30, 32, -1, // vertex 1332
	30, 32, -1, // vertex 1333
	30, 32, -1, // vertex 1334
	30, 32, -1, // vertex 1335
	30, 32, -1, // vertex 1336
	30, 32, -1, // vertex 1337
	30, 32, -1, // vertex 1338
	30, 32, -1, // vertex 1339
	30, 32, -1, // vertex 1340
	30, 32, -1, // vertex 1341
	30, 32, -1, // vertex 1342
	30, 32, -1, // vertex 1343
	30, 32, -1, // vertex 1344
	30, 32, -1, // vertex 1345
	30, 32, -1, // vertex 1346
	30, 32, -1, // vertex 1347
	30, 32, -1, // vertex 1348
	30, 32, -1, // vertex 1349
	30, 32, -1, // vertex 1350
	30, 32, -1, // vertex 1351
	30, 32, -1, // vertex 1352
	30, 32, -1, // vertex 1353
	30, 32, -1, // vertex 1354
	30, 32, -1, // vertex 1355
	30, 32, -1, // vertex 1356
	30, 32, -1, // vertex 1357
	30, 32, -1, // vertex 1358
	30, 32, -1, // vertex 1359
	30, 32, -1, // vertex 1360
	30, 32, -1, // vertex 1361
	30, 32, -1, // vertex 1362
	30, 32, -1, // vertex 1363
	30, 32, -1, // vertex 1364
	30, 32, -1, // vertex 1365
	30, 32, -1, // vertex 1366
	30, 32, -1, // vertex 1367
	30, 32, -1, // vertex 1368
	30, 32, -1, // vertex 1369
	30, 32, -1, // vertex 1370
	30, 32, -1, // vertex 1371
	30, 32, -1, // vertex 1372
	30, 32, -1, // vertex 1373
	30, 32, -1, // vertex 1374
	30, 32, -1, // vertex 1375
	30, 32, -1, // vertex 1376
	30, 32, -1, // vertex 1377
	30, 32, -1, // vertex 1378
	30, 32, -1, // vertex 1379
	30, 32, -1, // vertex 1380
	30, 32, -1, // vertex 1381
	30, 32, -1, // vertex 1382
	30, 32, -1, // vertex 1383
	30, 32, -1, // vertex 1384
	30, 32, -1, // vertex 1385
	30, 32, -1, // vertex 1386
	30, 32, -1, // vertex 1387
	30, 32, -1, // vertex 1388
	30, 32, -1, // vertex 1389
	30, 32, -1, // vertex 1390
	30, 32, -1, // vertex 1391
	30, 32, -1, // vertex 1392
	30, 32, -1, // vertex 1393
	30, 32, -1, // vertex 1394
	30, 32, -1, // vertex 1395
	30, 32, -1, // vertex 1396
	30, 32, -1, // vertex 1397
	30, 32, -1, // vertex 1398
	30, 32, -1, // vertex 1399
	30, 32, -1, // vertex 1400
	30, 32, -1, // vertex 1401
	30, 32, -1, // vertex 1402
	30, 32, -1, // vertex 1403
};

float testWeights[] = {
	1.000000f, 0.000000f, 0.000000f, // vertex 0
	1.000000f, 0.000000f, 0.000000f, // vertex 1
	1.000000f, 0.000000f, 0.000000f, // vertex 2
	1.000000f, 0.000000f, 0.000000f, // vertex 3
	1.000000f, 0.000000f, 0.000000f, // vertex 4
	1.000000f, 0.000000f, 0.000000f, // vertex 5
	1.000000f, 0.000000f, 0.000000f, // vertex 6
	1.000000f, 0.000000f, 0.000000f, // vertex 7
	1.000000f, 0.000000f, 0.000000f, // vertex 8
	0.500000f, 0.500000f, 0.000000f, // vertex 9
	0.500000f, 0.500000f, 0.000000f, // vertex 10
	0.500000f, 0.500000f, 0.000000f, // vertex 11
	0.500000f, 0.500000f, 0.000000f, // vertex 12
	0.500000f, 0.500000f, 0.000000f, // vertex 13
	0.500000f, 0.500000f, 0.000000f, // vertex 14
	0.555556f, 0.444444f, 0.000000f, // vertex 15
	0.555556f, 0.444444f, 0.000000f, // vertex 16
	0.833333f, 0.166667f, 0.000000f, // vertex 17
	0.666667f, 0.333333f, 0.000000f, // vertex 18
	0.833333f, 0.166667f, 0.000000f, // vertex 19
	0.833333f, 0.166667f, 0.000000f, // vertex 20
	0.833333f, 0.166667f, 0.000000f, // vertex 21
	1.000000f, 0.000000f, 0.000000f, // vertex 22
	1.000000f, 0.000000f, 0.000000f, // vertex 23
	1.000000f, 0.000000f, 0.000000f, // vertex 24
	1.000000f, 0.000000f, 0.000000f, // vertex 25
	1.000000f, 0.000000f, 0.000000f, // vertex 26
	1.000000f, 0.000000f, 0.000000f, // vertex 27
	0.926651f, 0.073349f, 0.000000f, // vertex 28
	1.000000f, 0.000000f, 0.000000f, // vertex 29
	0.833333f, 0.166667f, 0.000000f, // vertex 30
	1.000000f, 0.000000f, 0.000000f, // vertex 31
	0.833333f, 0.166667f, 0.000000f, // vertex 32
	1.000000f, 0.000000f, 0.000000f, // vertex 33
	0.666667f, 0.333333f, 0.000000f, // vertex 34
	0.833333f, 0.166667f, 0.000000f, // vertex 35
	0.666667f, 0.333333f, 0.000000f, // vertex 36
	0.555556f, 0.444444f, 0.000000f, // vertex 37
	0.500000f, 0.500000f, 0.000000f, // vertex 38
	0.555556f, 0.444444f, 0.000000f, // vertex 39
	0.500000f, 0.500000f, 0.000000f, // vertex 40
	0.555556f, 0.444444f, 0.000000f, // vertex 41
	0.500000f, 0.500000f, 0.000000f, // vertex 42
	1.000000f, 0.000000f, 0.000000f, // vertex 43
	1.000000f, 0.000000f, 0.000000f, // vertex 44
	1.000000f, 0.000000f, 0.000000f, // vertex 45
	1.000000f, 0.000000f, 0.000000f, // vertex 46
	1.000000f, 0.000000f, 0.000000f, // vertex 47
	1.000000f, 0.000000f, 0.000000f, // vertex 48
	1.000000f, 0.000000f, 0.000000f, // vertex 49
	1.000000f, 0.000000f, 0.000000f, // vertex 50
	0.833333f, 0.166667f, 0.000000f, // vertex 51
	0.833333f, 0.166667f, 0.000000f, // vertex 52
	0.833333f, 0.166667f, 0.000000f, // vertex 53
	0.666667f, 0.333333f, 0.000000f, // vertex 54
	0.833333f, 0.166667f, 0.000000f, // vertex 55
	0.555556f, 0.444444f, 0.000000f, // vertex 56
	0.555556f, 0.444444f, 0.000000f, // vertex 57
	0.500000f, 0.500000f, 0.000000f, // vertex 58
	0.500000f, 0.500000f, 0.000000f, // vertex 59
	0.500000f, 0.500000f, 0.000000f, // vertex 60
	0.500000f, 0.500000f, 0.000000f, // vertex 61
	0.500000f, 0.500000f, 0.000000f, // vertex 62
	0.500000f, 0.500000f, 0.000000f, // vertex 63
	0.500000f, 0.500000f, 0.000000f, // vertex 64
	0.555556f, 0.444444f, 0.000000f, // vertex 65
	0.500000f, 0.500000f, 0.000000f, // vertex 66
	0.555556f, 0.444444f, 0.000000f, // vertex 67
	0.500000f, 0.500000f, 0.000000f, // vertex 68
	0.555556f, 0.444444f, 0.000000f, // vertex 69
	0.666667f, 0.333333f, 0.000000f, // vertex 70
	0.833333f, 0.166667f, 0.000000f, // vertex 71
	0.666667f, 0.333333f, 0.000000f, // vertex 72
	1.000000f, 0.000000f, 0.000000f, // vertex 73
	0.833333f, 0.166667f, 0.000000f, // vertex 74
	1.000000f, 0.000000f, 0.000000f, // vertex 75
	0.833333f, 0.166667f, 0.000000f, // vertex 76
	1.000000f, 0.000000f, 0.000000f, // vertex 77
	1.000000f, 0.000000f, 0.000000f, // vertex 78
	1.000000f, 0.000000f, 0.000000f, // vertex 79
	1.000000f, 0.000000f, 0.000000f, // vertex 80
	1.000000f, 0.000000f, 0.000000f, // vertex 81
	1.000000f, 0.000000f, 0.000000f, // vertex 82
	1.000000f, 0.000000f, 0.000000f, // vertex 83
	1.000000f, 0.000000f, 0.000000f, // vertex 84
	1.000000f, 0.000000f, 0.000000f, // vertex 85
	1.000000f, 0.000000f, 0.000000f, // vertex 86
	1.000000f, 0.000000f, 0.000000f, // vertex 87
	1.000000f, 0.000000f, 0.000000f, // vertex 88
	1.000000f, 0.000000f, 0.000000f, // vertex 89
	1.000000f, 0.000000f, 0.000000f, // vertex 90
	1.000000f, 0.000000f, 0.000000f, // vertex 91
	1.000000f, 0.000000f, 0.000000f, // vertex 92
	1.000000f, 0.000000f, 0.000000f, // vertex 93
	1.000000f, 0.000000f, 0.000000f, // vertex 94
	0.926651f, 0.073349f, 0.000000f, // vertex 95
	0.926651f, 0.073349f, 0.000000f, // vertex 96
	1.000000f, 0.000000f, 0.000000f, // vertex 97
	1.000000f, 0.000000f, 0.000000f, // vertex 98
	1.000000f, 0.000000f, 0.000000f, // vertex 99
	0.500000f, 0.500000f, 0.000000f, // vertex 100
	0.500000f, 0.500000f, 0.000000f, // vertex 101
	1.000000f, 0.000000f, 0.000000f, // vertex 102
	1.000000f, 0.000000f, 0.000000f, // vertex 103
	0.926651f, 0.073349f, 0.000000f, // vertex 104
	1.000000f, 0.000000f, 0.000000f, // vertex 105
	1.000000f, 0.000000f, 0.000000f, // vertex 106
	1.000000f, 0.000000f, 0.000000f, // vertex 107
	1.000000f, 0.000000f, 0.000000f, // vertex 108
	1.000000f, 0.000000f, 0.000000f, // vertex 109
	1.000000f, 0.000000f, 0.000000f, // vertex 110
	1.000000f, 0.000000f, 0.000000f, // vertex 111
	1.000000f, 0.000000f, 0.000000f, // vertex 112
	1.000000f, 0.000000f, 0.000000f, // vertex 113
	1.000000f, 0.000000f, 0.000000f, // vertex 114
	0.500000f, 0.500000f, 0.000000f, // vertex 115
	1.000000f, 0.000000f, 0.000000f, // vertex 116
	0.500000f, 0.500000f, 0.000000f, // vertex 117
	0.500000f, 0.500000f, 0.000000f, // vertex 118
	0.666667f, 0.333333f, 0.000000f, // vertex 119
	0.666667f, 0.333333f, 0.000000f, // vertex 120
	0.800000f, 0.200000f, 0.000000f, // vertex 121
	0.800000f, 0.200000f, 0.000000f, // vertex 122
	0.500000f, 0.500000f, 0.000000f, // vertex 123
	0.500000f, 0.500000f, 0.000000f, // vertex 124
	0.500000f, 0.500000f, 0.000000f, // vertex 125
	0.500000f, 0.500000f, 0.000000f, // vertex 126
	0.500000f, 0.500000f, 0.000000f, // vertex 127
	0.500000f, 0.500000f, 0.000000f, // vertex 128
	0.500000f, 0.500000f, 0.000000f, // vertex 129
	0.500000f, 0.500000f, 0.000000f, // vertex 130
	0.500000f, 0.500000f, 0.000000f, // vertex 131
	0.500000f, 0.500000f, 0.000000f, // vertex 132
	0.500000f, 0.500000f, 0.000000f, // vertex 133
	0.500000f, 0.500000f, 0.000000f, // vertex 134
	0.800000f, 0.200000f, 0.000000f, // vertex 135
	0.500000f, 0.500000f, 0.000000f, // vertex 136
	0.500000f, 0.500000f, 0.000000f, // vertex 137
	0.500000f, 0.500000f, 0.000000f, // vertex 138
	0.500000f, 0.500000f, 0.000000f, // vertex 139
	0.500000f, 0.500000f, 0.000000f, // vertex 140
	0.666667f, 0.333333f, 0.000000f, // vertex 141
	0.500000f, 0.500000f, 0.000000f, // vertex 142
	0.666667f, 0.333333f, 0.000000f, // vertex 143
	0.500000f, 0.500000f, 0.000000f, // vertex 144
	0.833333f, 0.166667f, 0.000000f, // vertex 145
	0.833333f, 0.166667f, 0.000000f, // vertex 146
	1.000000f, 0.000000f, 0.000000f, // vertex 147
	1.000000f, 0.000000f, 0.000000f, // vertex 148
	1.000000f, 0.000000f, 0.000000f, // vertex 149
	1.000000f, 0.000000f, 0.000000f, // vertex 150
	1.000000f, 0.000000f, 0.000000f, // vertex 151
	1.000000f, 0.000000f, 0.000000f, // vertex 152
	1.000000f, 0.000000f, 0.000000f, // vertex 153
	0.833333f, 0.166667f, 0.000000f, // vertex 154
	1.000000f, 0.000000f, 0.000000f, // vertex 155
	0.800000f, 0.200000f, 0.000000f, // vertex 156
	0.666667f, 0.333333f, 0.000000f, // vertex 157
	0.666667f, 0.333333f, 0.000000f, // vertex 158
	0.555556f, 0.444444f, 0.000000f, // vertex 159
	0.555556f, 0.444444f, 0.000000f, // vertex 160
	0.500000f, 0.500000f, 0.000000f, // vertex 161
	0.555556f, 0.444444f, 0.000000f, // vertex 162
	0.500000f, 0.500000f, 0.000000f, // vertex 163
	0.555556f, 0.444444f, 0.000000f, // vertex 164
	0.500000f, 0.500000f, 0.000000f, // vertex 165
	0.500000f, 0.500000f, 0.000000f, // vertex 166
	0.555556f, 0.444444f, 0.000000f, // vertex 167
	0.500000f, 0.500000f, 0.000000f, // vertex 168
	0.555556f, 0.444444f, 0.000000f, // vertex 169
	0.500000f, 0.500000f, 0.000000f, // vertex 170
	0.555556f, 0.444444f, 0.000000f, // vertex 171
	0.555556f, 0.444444f, 0.000000f, // vertex 172
	0.666667f, 0.333333f, 0.000000f, // vertex 173
	0.666667f, 0.333333f, 0.000000f, // vertex 174
	0.800000f, 0.200000f, 0.000000f, // vertex 175
	1.000000f, 0.000000f, 0.000000f, // vertex 176
	0.833333f, 0.166667f, 0.000000f, // vertex 177
	1.000000f, 0.000000f, 0.000000f, // vertex 178
	1.000000f, 0.000000f, 0.000000f, // vertex 179
	1.000000f, 0.000000f, 0.000000f, // vertex 180
	1.000000f, 0.000000f, 0.000000f, // vertex 181
	1.000000f, 0.000000f, 0.000000f, // vertex 182
	1.000000f, 0.000000f, 0.000000f, // vertex 183
	1.000000f, 0.000000f, 0.000000f, // vertex 184
	0.833333f, 0.166667f, 0.000000f, // vertex 185
	0.833333f, 0.166667f, 0.000000f, // vertex 186
	0.500000f, 0.500000f, 0.000000f, // vertex 187
	0.666667f, 0.333333f, 0.000000f, // vertex 188
	0.500000f, 0.500000f, 0.000000f, // vertex 189
	0.666667f, 0.333333f, 0.000000f, // vertex 190
	0.500000f, 0.500000f, 0.000000f, // vertex 191
	0.500000f, 0.500000f, 0.000000f, // vertex 192
	0.500000f, 0.500000f, 0.000000f, // vertex 193
	0.500000f, 0.500000f, 0.000000f, // vertex 194
	0.500000f, 0.500000f, 0.000000f, // vertex 195
	0.800000f, 0.200000f, 0.000000f, // vertex 196
	0.500000f, 0.500000f, 0.000000f, // vertex 197
	0.655738f, 0.344262f, 0.000000f, // vertex 198
	0.833333f, 0.166667f, 0.000000f, // vertex 199
	0.819001f, 0.180999f, 0.000000f, // vertex 200
	0.833333f, 0.166667f, 0.000000f, // vertex 201
	0.819001f, 0.180999f, 0.000000f, // vertex 202
	0.819001f, 0.180999f, 0.000000f, // vertex 203
	0.819001f, 0.180999f, 0.000000f, // vertex 204
	0.655738f, 0.344262f, 0.000000f, // vertex 205
	0.819001f, 0.180999f, 0.000000f, // vertex 206
	0.819001f, 0.180999f, 0.000000f, // vertex 207
	0.819001f, 0.180999f, 0.000000f, // vertex 208
	0.819001f, 0.180999f, 0.000000f, // vertex 209
	0.819001f, 0.180999f, 0.000000f, // vertex 210
	0.655738f, 0.344262f, 0.000000f, // vertex 211
	0.512195f, 0.487805f, 0.000000f, // vertex 212
	0.512195f, 0.487805f, 0.000000f, // vertex 213
	0.512195f, 0.487805f, 0.000000f, // vertex 214
	0.512195f, 0.487805f, 0.000000f, // vertex 215
	0.500000f, 0.500000f, 0.000000f, // vertex 216
	0.655738f, 0.344262f, 0.000000f, // vertex 217
	0.500000f, 0.500000f, 0.000000f, // vertex 218
	0.500000f, 0.500000f, 0.000000f, // vertex 219
	0.500000f, 0.500000f, 0.000000f, // vertex 220
	0.500000f, 0.500000f, 0.000000f, // vertex 221
	0.500000f, 0.500000f, 0.000000f, // vertex 222
	1.000000f, 0.000000f, 0.000000f, // vertex 223
	0.500000f, 0.500000f, 0.000000f, // vertex 224
	1.000000f, 0.000000f, 0.000000f, // vertex 225
	0.500000f, 0.500000f, 0.000000f, // vertex 226
	0.500000f, 0.500000f, 0.000000f, // vertex 227
	1.000000f, 0.000000f, 0.000000f, // vertex 228
	1.000000f, 0.000000f, 0.000000f, // vertex 229
	1.000000f, 0.000000f, 0.000000f, // vertex 230
	0.833941f, 0.166059f, 0.000000f, // vertex 231
	1.000000f, 0.000000f, 0.000000f, // vertex 232
	1.000000f, 0.000000f, 0.000000f, // vertex 233
	1.000000f, 0.000000f, 0.000000f, // vertex 234
	1.000000f, 0.000000f, 0.000000f, // vertex 235
	1.000000f, 0.000000f, 0.000000f, // vertex 236
	1.000000f, 0.000000f, 0.000000f, // vertex 237
	1.000000f, 0.000000f, 0.000000f, // vertex 238
	0.952381f, 0.047619f, 0.000000f, // vertex 239
	0.909091f, 0.090909f, 0.000000f, // vertex 240
	1.000000f, 0.000000f, 0.000000f, // vertex 241
	1.000000f, 0.000000f, 0.000000f, // vertex 242
	1.000000f, 0.000000f, 0.000000f, // vertex 243
	1.000000f, 0.000000f, 0.000000f, // vertex 244
	1.000000f, 0.000000f, 0.000000f, // vertex 245
	1.000000f, 0.000000f, 0.000000f, // vertex 246
	1.000000f, 0.000000f, 0.000000f, // vertex 247
	1.000000f, 0.000000f, 0.000000f, // vertex 248
	1.000000f, 0.000000f, 0.000000f, // vertex 249
	1.000000f, 0.000000f, 0.000000f, // vertex 250
	1.000000f, 0.000000f, 0.000000f, // vertex 251
	1.000000f, 0.000000f, 0.000000f, // vertex 252
	0.833333f, 0.166667f, 0.000000f, // vertex 253
	1.000000f, 0.000000f, 0.000000f, // vertex 254
	0.666667f, 0.333333f, 0.000000f, // vertex 255
	1.000000f, 0.000000f, 0.000000f, // vertex 256
	0.666667f, 0.333333f, 0.000000f, // vertex 257
	1.000000f, 0.000000f, 0.000000f, // vertex 258
	0.500000f, 0.500000f, 0.000000f, // vertex 259
	1.000000f, 0.000000f, 0.000000f, // vertex 260
	0.500000f, 0.500000f, 0.000000f, // vertex 261
	0.952381f, 0.047619f, 0.000000f, // vertex 262
	0.909091f, 0.090909f, 0.000000f, // vertex 263
	0.909091f, 0.090909f, 0.000000f, // vertex 264
	1.000000f, 0.000000f, 0.000000f, // vertex 265
	1.000000f, 0.000000f, 0.000000f, // vertex 266
	1.000000f, 0.000000f, 0.000000f, // vertex 267
	1.000000f, 0.000000f, 0.000000f, // vertex 268
	1.000000f, 0.000000f, 0.000000f, // vertex 269
	1.000000f, 0.000000f, 0.000000f, // vertex 270
	1.000000f, 0.000000f, 0.000000f, // vertex 271
	1.000000f, 0.000000f, 0.000000f, // vertex 272
	1.000000f, 0.000000f, 0.000000f, // vertex 273
	1.000000f, 0.000000f, 0.000000f, // vertex 274
	1.000000f, 0.000000f, 0.000000f, // vertex 275
	1.000000f, 0.000000f, 0.000000f, // vertex 276
	1.000000f, 0.000000f, 0.000000f, // vertex 277
	0.833333f, 0.166667f, 0.000000f, // vertex 278
	1.000000f, 0.000000f, 0.000000f, // vertex 279
	0.833333f, 0.166667f, 0.000000f, // vertex 280
	1.000000f, 0.000000f, 0.000000f, // vertex 281
	1.000000f, 0.000000f, 0.000000f, // vertex 282
	1.000000f, 0.000000f, 0.000000f, // vertex 283
	1.000000f, 0.000000f, 0.000000f, // vertex 284
	1.000000f, 0.000000f, 0.000000f, // vertex 285
	1.000000f, 0.000000f, 0.000000f, // vertex 286
	1.000000f, 0.000000f, 0.000000f, // vertex 287
	1.000000f, 0.000000f, 0.000000f, // vertex 288
	1.000000f, 0.000000f, 0.000000f, // vertex 289
	0.555556f, 0.444444f, 0.000000f, // vertex 290
	0.555556f, 0.444444f, 0.000000f, // vertex 291
	0.833333f, 0.166667f, 0.000000f, // vertex 292
	0.833333f, 0.166667f, 0.000000f, // vertex 293
	1.000000f, 0.000000f, 0.000000f, // vertex 294
	1.000000f, 0.000000f, 0.000000f, // vertex 295
	1.000000f, 0.000000f, 0.000000f, // vertex 296
	0.833333f, 0.166667f, 0.000000f, // vertex 297
	1.000000f, 0.000000f, 0.000000f, // vertex 298
	0.833333f, 0.166667f, 0.000000f, // vertex 299
	1.000000f, 0.000000f, 0.000000f, // vertex 300
	0.833333f, 0.166667f, 0.000000f, // vertex 301
	1.000000f, 0.000000f, 0.000000f, // vertex 302
	0.833333f, 0.166667f, 0.000000f, // vertex 303
	0.666667f, 0.333333f, 0.000000f, // vertex 304
	1.000000f, 0.000000f, 0.000000f, // vertex 305
	0.833333f, 0.166667f, 0.000000f, // vertex 306
	1.000000f, 0.000000f, 0.000000f, // vertex 307
	0.833333f, 0.166667f, 0.000000f, // vertex 308
	0.833333f, 0.166667f, 0.000000f, // vertex 309
	0.833333f, 0.166667f, 0.000000f, // vertex 310
	0.714286f, 0.285714f, 0.000000f, // vertex 311
	0.833333f, 0.166667f, 0.000000f, // vertex 312
	0.714286f, 0.285714f, 0.000000f, // vertex 313
	0.833333f, 0.166667f, 0.000000f, // vertex 314
	0.714286f, 0.285714f, 0.000000f, // vertex 315
	0.833333f, 0.166667f, 0.000000f, // vertex 316
	0.800000f, 0.200000f, 0.000000f, // vertex 317
	0.555556f, 0.444444f, 0.000000f, // vertex 318
	0.666667f, 0.333333f, 0.000000f, // vertex 319
	0.666667f, 0.333333f, 0.000000f, // vertex 320
	0.500000f, 0.500000f, 0.000000f, // vertex 321
	0.500000f, 0.500000f, 0.000000f, // vertex 322
	0.555556f, 0.444444f, 0.000000f, // vertex 323
	0.555556f, 0.444444f, 0.000000f, // vertex 324
	0.666667f, 0.333333f, 0.000000f, // vertex 325
	0.833333f, 0.166667f, 0.000000f, // vertex 326
	0.666667f, 0.333333f, 0.000000f, // vertex 327
	0.833333f, 0.166667f, 0.000000f, // vertex 328
	0.833333f, 0.166667f, 0.000000f, // vertex 329
	0.500000f, 0.500000f, 0.000000f, // vertex 330
	0.500000f, 0.500000f, 0.000000f, // vertex 331
	0.555556f, 0.444444f, 0.000000f, // vertex 332
	0.500000f, 0.500000f, 0.000000f, // vertex 333
	0.500000f, 0.500000f, 0.000000f, // vertex 334
	1.000000f, 0.000000f, 0.000000f, // vertex 335
	1.000000f, 0.000000f, 0.000000f, // vertex 336
	1.000000f, 0.000000f, 0.000000f, // vertex 337
	1.000000f, 0.000000f, 0.000000f, // vertex 338
	1.000000f, 0.000000f, 0.000000f, // vertex 339
	1.000000f, 0.000000f, 0.000000f, // vertex 340
	1.000000f, 0.000000f, 0.000000f, // vertex 341
	1.000000f, 0.000000f, 0.000000f, // vertex 342
	1.000000f, 0.000000f, 0.000000f, // vertex 343
	1.000000f, 0.000000f, 0.000000f, // vertex 344
	1.000000f, 0.000000f, 0.000000f, // vertex 345
	0.926651f, 0.073349f, 0.000000f, // vertex 346
	0.833941f, 0.166059f, 0.000000f, // vertex 347
	1.000000f, 0.000000f, 0.000000f, // vertex 348
	1.000000f, 0.000000f, 0.000000f, // vertex 349
	1.000000f, 0.000000f, 0.000000f, // vertex 350
	1.000000f, 0.000000f, 0.000000f, // vertex 351
	1.000000f, 0.000000f, 0.000000f, // vertex 352
	1.000000f, 0.000000f, 0.000000f, // vertex 353
	1.000000f, 0.000000f, 0.000000f, // vertex 354
	1.000000f, 0.000000f, 0.000000f, // vertex 355
	1.000000f, 0.000000f, 0.000000f, // vertex 356
	1.000000f, 0.000000f, 0.000000f, // vertex 357
	1.000000f, 0.000000f, 0.000000f, // vertex 358
	1.000000f, 0.000000f, 0.000000f, // vertex 359
	1.000000f, 0.000000f, 0.000000f, // vertex 360
	1.000000f, 0.000000f, 0.000000f, // vertex 361
	0.800000f, 0.200000f, 0.000000f, // vertex 362
	0.800000f, 0.200000f, 0.000000f, // vertex 363
	0.500000f, 0.500000f, 0.000000f, // vertex 364
	0.655738f, 0.344262f, 0.000000f, // vertex 365
	0.819001f, 0.180999f, 0.000000f, // vertex 366
	0.833333f, 0.166667f, 0.000000f, // vertex 367
	0.833333f, 0.166667f, 0.000000f, // vertex 368
	0.500000f, 0.500000f, 0.000000f, // vertex 369
	0.512195f, 0.487805f, 0.000000f, // vertex 370
	0.500000f, 0.500000f, 0.000000f, // vertex 371
	0.500000f, 0.500000f, 0.000000f, // vertex 372
	0.500000f, 0.500000f, 0.000000f, // vertex 373
	0.655738f, 0.344262f, 0.000000f, // vertex 374
	0.500000f, 0.500000f, 0.000000f, // vertex 375
	0.500000f, 0.500000f, 0.000000f, // vertex 376
	0.500000f, 0.500000f, 0.000000f, // vertex 377
	0.500000f, 0.500000f, 0.000000f, // vertex 378
	0.500000f, 0.500000f, 0.000000f, // vertex 379
	0.500000f, 0.500000f, 0.000000f, // vertex 380
	0.500000f, 0.500000f, 0.000000f, // vertex 381
	1.000000f, 0.000000f, 0.000000f, // vertex 382
	1.000000f, 0.000000f, 0.000000f, // vertex 383
	1.000000f, 0.000000f, 0.000000f, // vertex 384
	1.000000f, 0.000000f, 0.000000f, // vertex 385
	1.000000f, 0.000000f, 0.000000f, // vertex 386
	1.000000f, 0.000000f, 0.000000f, // vertex 387
	1.000000f, 0.000000f, 0.000000f, // vertex 388
	1.000000f, 0.000000f, 0.000000f, // vertex 389
	1.000000f, 0.000000f, 0.000000f, // vertex 390
	0.500000f, 0.500000f, 0.000000f, // vertex 391
	1.000000f, 0.000000f, 0.000000f, // vertex 392
	0.666667f, 0.333333f, 0.000000f, // vertex 393
	0.500000f, 0.500000f, 0.000000f, // vertex 394
	1.000000f, 0.000000f, 0.000000f, // vertex 395
	0.500000f, 0.500000f, 0.000000f, // vertex 396
	1.000000f, 0.000000f, 0.000000f, // vertex 397
	1.000000f, 0.000000f, 0.000000f, // vertex 398
	1.000000f, 0.000000f, 0.000000f, // vertex 399
	1.000000f, 0.000000f, 0.000000f, // vertex 400
	1.000000f, 0.000000f, 0.000000f, // vertex 401
	1.000000f, 0.000000f, 0.000000f, // vertex 402
	1.000000f, 0.000000f, 0.000000f, // vertex 403
	1.000000f, 0.000000f, 0.000000f, // vertex 404
	1.000000f, 0.000000f, 0.000000f, // vertex 405
	0.909091f, 0.090909f, 0.000000f, // vertex 406
	0.952381f, 0.047619f, 0.000000f, // vertex 407
	0.952381f, 0.047619f, 0.000000f, // vertex 408
	0.500000f, 0.500000f, 0.000000f, // vertex 409
	1.000000f, 0.000000f, 0.000000f, // vertex 410
	0.500000f, 0.500000f, 0.000000f, // vertex 411
	1.000000f, 0.000000f, 0.000000f, // vertex 412
	0.666667f, 0.333333f, 0.000000f, // vertex 413
	1.000000f, 0.000000f, 0.000000f, // vertex 414
	0.666667f, 0.333333f, 0.000000f, // vertex 415
	1.000000f, 0.000000f, 0.000000f, // vertex 416
	0.833333f, 0.166667f, 0.000000f, // vertex 417
	1.000000f, 0.000000f, 0.000000f, // vertex 418
	1.000000f, 0.000000f, 0.000000f, // vertex 419
	1.000000f, 0.000000f, 0.000000f, // vertex 420
	1.000000f, 0.000000f, 0.000000f, // vertex 421
	0.833333f, 0.166667f, 0.000000f, // vertex 422
	1.000000f, 0.000000f, 0.000000f, // vertex 423
	1.000000f, 0.000000f, 0.000000f, // vertex 424
	1.000000f, 0.000000f, 0.000000f, // vertex 425
	1.000000f, 0.000000f, 0.000000f, // vertex 426
	1.000000f, 0.000000f, 0.000000f, // vertex 427
	1.000000f, 0.000000f, 0.000000f, // vertex 428
	1.000000f, 0.000000f, 0.000000f, // vertex 429
	1.000000f, 0.000000f, 0.000000f, // vertex 430
	1.000000f, 0.000000f, 0.000000f, // vertex 431
	1.000000f, 0.000000f, 0.000000f, // vertex 432
	1.000000f, 0.000000f, 0.000000f, // vertex 433
	1.000000f, 0.000000f, 0.000000f, // vertex 434
	1.000000f, 0.000000f, 0.000000f, // vertex 435
	0.909091f, 0.090909f, 0.000000f, // vertex 436
	0.909091f, 0.090909f, 0.000000f, // vertex 437
	1.000000f, 0.000000f, 0.000000f, // vertex 438
	1.000000f, 0.000000f, 0.000000f, // vertex 439
	1.000000f, 0.000000f, 0.000000f, // vertex 440
	0.833333f, 0.166667f, 0.000000f, // vertex 441
	1.000000f, 0.000000f, 0.000000f, // vertex 442
	1.000000f, 0.000000f, 0.000000f, // vertex 443
	1.000000f, 0.000000f, 0.000000f, // vertex 444
	1.000000f, 0.000000f, 0.000000f, // vertex 445
	1.000000f, 0.000000f, 0.000000f, // vertex 446
	1.000000f, 0.000000f, 0.000000f, // vertex 447
	1.000000f, 0.000000f, 0.000000f, // vertex 448
	1.000000f, 0.000000f, 0.000000f, // vertex 449
	1.000000f, 0.000000f, 0.000000f, // vertex 450
	0.833333f, 0.166667f, 0.000000f, // vertex 451
	0.833333f, 0.166667f, 0.000000f, // vertex 452
	0.555556f, 0.444444f, 0.000000f, // vertex 453
	0.555556f, 0.444444f, 0.000000f, // vertex 454
	1.000000f, 0.000000f, 0.000000f, // vertex 455
	0.833333f, 0.166667f, 0.000000f, // vertex 456
	1.000000f, 0.000000f, 0.000000f, // vertex 457
	0.833333f, 0.166667f, 0.000000f, // vertex 458
	1.000000f, 0.000000f, 0.000000f, // vertex 459
	0.833333f, 0.166667f, 0.000000f, // vertex 460
	1.000000f, 0.000000f, 0.000000f, // vertex 461
	1.000000f, 0.000000f, 0.000000f, // vertex 462
	0.666667f, 0.333333f, 0.000000f, // vertex 463
	0.833333f, 0.166667f, 0.000000f, // vertex 464
	0.800000f, 0.200000f, 0.000000f, // vertex 465
	0.833333f, 0.166667f, 0.000000f, // vertex 466
	0.714286f, 0.285714f, 0.000000f, // vertex 467
	0.833333f, 0.166667f, 0.000000f, // vertex 468
	0.714286f, 0.285714f, 0.000000f, // vertex 469
	0.833333f, 0.166667f, 0.000000f, // vertex 470
	0.714286f, 0.285714f, 0.000000f, // vertex 471
	0.833333f, 0.166667f, 0.000000f, // vertex 472
	0.833333f, 0.166667f, 0.000000f, // vertex 473
	0.666667f, 0.333333f, 0.000000f, // vertex 474
	0.833333f, 0.166667f, 0.000000f, // vertex 475
	0.666667f, 0.333333f, 0.000000f, // vertex 476
	0.555556f, 0.444444f, 0.000000f, // vertex 477
	0.555556f, 0.444444f, 0.000000f, // vertex 478
	0.500000f, 0.500000f, 0.000000f, // vertex 479
	0.500000f, 0.500000f, 0.000000f, // vertex 480
	0.500000f, 0.500000f, 0.000000f, // vertex 481
	0.500000f, 0.500000f, 0.000000f, // vertex 482
	0.555556f, 0.444444f, 0.000000f, // vertex 483
	0.500000f, 0.500000f, 0.000000f, // vertex 484
	0.500000f, 0.500000f, 0.000000f, // vertex 485
	1.000000f, 0.000000f, 0.000000f, // vertex 486
	1.000000f, 0.000000f, 0.000000f, // vertex 487
	1.000000f, 0.000000f, 0.000000f, // vertex 488
	1.000000f, 0.000000f, 0.000000f, // vertex 489
	1.000000f, 0.000000f, 0.000000f, // vertex 490
	1.000000f, 0.000000f, 0.000000f, // vertex 491
	1.000000f, 0.000000f, 0.000000f, // vertex 492
	1.000000f, 0.000000f, 0.000000f, // vertex 493
	1.000000f, 0.000000f, 0.000000f, // vertex 494
	1.000000f, 0.000000f, 0.000000f, // vertex 495
	1.000000f, 0.000000f, 0.000000f, // vertex 496
	1.000000f, 0.000000f, 0.000000f, // vertex 497
	1.000000f, 0.000000f, 0.000000f, // vertex 498
	1.000000f, 0.000000f, 0.000000f, // vertex 499
	1.000000f, 0.000000f, 0.000000f, // vertex 500
	0.500000f, 0.500000f, 0.000000f, // vertex 501
	1.000000f, 0.000000f, 0.000000f, // vertex 502
	1.000000f, 0.000000f, 0.000000f, // vertex 503
	1.000000f, 0.000000f, 0.000000f, // vertex 504
	1.000000f, 0.000000f, 0.000000f, // vertex 505
	1.000000f, 0.000000f, 0.000000f, // vertex 506
	1.000000f, 0.000000f, 0.000000f, // vertex 507
	1.000000f, 0.000000f, 0.000000f, // vertex 508
	1.000000f, 0.000000f, 0.000000f, // vertex 509
	1.000000f, 0.000000f, 0.000000f, // vertex 510
	1.000000f, 0.000000f, 0.000000f, // vertex 511
	1.000000f, 0.000000f, 0.000000f, // vertex 512
	1.000000f, 0.000000f, 0.000000f, // vertex 513
	1.000000f, 0.000000f, 0.000000f, // vertex 514
	1.000000f, 0.000000f, 0.000000f, // vertex 515
	0.555556f, 0.444444f, 0.000000f, // vertex 516
	0.666667f, 0.333333f, 0.000000f, // vertex 517
	0.666667f, 0.333333f, 0.000000f, // vertex 518
	1.000000f, 0.000000f, 0.000000f, // vertex 519
	1.000000f, 0.000000f, 0.000000f, // vertex 520
	1.000000f, 0.000000f, 0.000000f, // vertex 521
	0.833333f, 0.166667f, 0.000000f, // vertex 522
	0.833333f, 0.166667f, 0.000000f, // vertex 523
	0.833333f, 0.166667f, 0.000000f, // vertex 524
	0.833333f, 0.166667f, 0.000000f, // vertex 525
	0.833333f, 0.166667f, 0.000000f, // vertex 526
	0.833333f, 0.166667f, 0.000000f, // vertex 527
	0.833333f, 0.166667f, 0.000000f, // vertex 528
	0.769231f, 0.230769f, 0.000000f, // vertex 529
	0.769231f, 0.230769f, 0.000000f, // vertex 530
	0.769231f, 0.230769f, 0.000000f, // vertex 531
	0.500000f, 0.500000f, 0.000000f, // vertex 532
	0.769231f, 0.230769f, 0.000000f, // vertex 533
	0.500000f, 0.500000f, 0.000000f, // vertex 534
	0.833333f, 0.166667f, 0.000000f, // vertex 535
	0.500000f, 0.500000f, 0.000000f, // vertex 536
	0.833333f, 0.166667f, 0.000000f, // vertex 537
	0.655738f, 0.344262f, 0.000000f, // vertex 538
	0.833333f, 0.166667f, 0.000000f, // vertex 539
	0.900901f, 0.099099f, 0.000000f, // vertex 540
	0.900901f, 0.099099f, 0.000000f, // vertex 541
	0.833333f, 0.166667f, 0.000000f, // vertex 542
	0.900901f, 0.099099f, 0.000000f, // vertex 543
	0.833333f, 0.166667f, 0.000000f, // vertex 544
	0.655738f, 0.344262f, 0.000000f, // vertex 545
	0.833333f, 0.166667f, 0.000000f, // vertex 546
	0.900901f, 0.099099f, 0.000000f, // vertex 547
	0.833333f, 0.166667f, 0.000000f, // vertex 548
	0.833333f, 0.166667f, 0.000000f, // vertex 549
	0.833333f, 0.166667f, 0.000000f, // vertex 550
	0.655738f, 0.344262f, 0.000000f, // vertex 551
	0.500000f, 0.500000f, 0.000000f, // vertex 552
	0.512195f, 0.487805f, 0.000000f, // vertex 553
	0.500000f, 0.500000f, 0.000000f, // vertex 554
	0.512195f, 0.487805f, 0.000000f, // vertex 555
	0.512195f, 0.487805f, 0.000000f, // vertex 556
	0.833333f, 0.166667f, 0.000000f, // vertex 557
	0.833333f, 0.166667f, 0.000000f, // vertex 558
	0.900901f, 0.099099f, 0.000000f, // vertex 559
	0.655738f, 0.344262f, 0.000000f, // vertex 560
	0.655738f, 0.344262f, 0.000000f, // vertex 561
	0.512195f, 0.487805f, 0.000000f, // vertex 562
	0.500000f, 0.500000f, 0.000000f, // vertex 563
	0.500000f, 0.500000f, 0.000000f, // vertex 564
	0.500000f, 0.500000f, 0.000000f, // vertex 565
	0.500000f, 0.500000f, 0.000000f, // vertex 566
	0.655738f, 0.344262f, 0.000000f, // vertex 567
	0.500000f, 0.500000f, 0.000000f, // vertex 568
	0.512195f, 0.487805f, 0.000000f, // vertex 569
	0.512195f, 0.487805f, 0.000000f, // vertex 570
	1.000000f, 0.000000f, 0.000000f, // vertex 571
	1.000000f, 0.000000f, 0.000000f, // vertex 572
	1.000000f, 0.000000f, 0.000000f, // vertex 573
	1.000000f, 0.000000f, 0.000000f, // vertex 574
	1.000000f, 0.000000f, 0.000000f, // vertex 575
	1.000000f, 0.000000f, 0.000000f, // vertex 576
	1.000000f, 0.000000f, 0.000000f, // vertex 577
	1.000000f, 0.000000f, 0.000000f, // vertex 578
	1.000000f, 0.000000f, 0.000000f, // vertex 579
	1.000000f, 0.000000f, 0.000000f, // vertex 580
	1.000000f, 0.000000f, 0.000000f, // vertex 581
	1.000000f, 0.000000f, 0.000000f, // vertex 582
	1.000000f, 0.000000f, 0.000000f, // vertex 583
	1.000000f, 0.000000f, 0.000000f, // vertex 584
	1.000000f, 0.000000f, 0.000000f, // vertex 585
	1.000000f, 0.000000f, 0.000000f, // vertex 586
	1.000000f, 0.000000f, 0.000000f, // vertex 587
	1.000000f, 0.000000f, 0.000000f, // vertex 588
	1.000000f, 0.000000f, 0.000000f, // vertex 589
	1.000000f, 0.000000f, 0.000000f, // vertex 590
	1.000000f, 0.000000f, 0.000000f, // vertex 591
	0.500000f, 0.500000f, 0.000000f, // vertex 592
	1.000000f, 0.000000f, 0.000000f, // vertex 593
	0.500000f, 0.500000f, 0.000000f, // vertex 594
	0.500000f, 0.500000f, 0.000000f, // vertex 595
	0.500000f, 0.500000f, 0.000000f, // vertex 596
	1.000000f, 0.000000f, 0.000000f, // vertex 597
	0.500000f, 0.500000f, 0.000000f, // vertex 598
	0.666667f, 0.333333f, 0.000000f, // vertex 599
	0.666667f, 0.333333f, 0.000000f, // vertex 600
	0.500000f, 0.500000f, 0.000000f, // vertex 601
	1.000000f, 0.000000f, 0.000000f, // vertex 602
	0.500000f, 0.500000f, 0.000000f, // vertex 603
	0.500000f, 0.500000f, 0.000000f, // vertex 604
	0.500000f, 0.500000f, 0.000000f, // vertex 605
	0.500000f, 0.500000f, 0.000000f, // vertex 606
	0.500000f, 0.500000f, 0.000000f, // vertex 607
	0.500000f, 0.500000f, 0.000000f, // vertex 608
	0.833333f, 0.166667f, 0.000000f, // vertex 609
	0.833333f, 0.166667f, 0.000000f, // vertex 610
	0.714286f, 0.285714f, 0.000000f, // vertex 611
	0.833333f, 0.166667f, 0.000000f, // vertex 612
	0.714286f, 0.285714f, 0.000000f, // vertex 613
	0.714286f, 0.285714f, 0.000000f, // vertex 614
	0.500000f, 0.500000f, 0.000000f, // vertex 615
	0.500000f, 0.500000f, 0.000000f, // vertex 616
	0.500000f, 0.500000f, 0.000000f, // vertex 617
	0.500000f, 0.500000f, 0.000000f, // vertex 618
	0.500000f, 0.500000f, 0.000000f, // vertex 619
	0.500000f, 0.500000f, 0.000000f, // vertex 620
	0.500000f, 0.500000f, 0.000000f, // vertex 621
	0.500000f, 0.500000f, 0.000000f, // vertex 622
	0.500000f, 0.500000f, 0.000000f, // vertex 623
	0.500000f, 0.500000f, 0.000000f, // vertex 624
	0.500000f, 0.500000f, 0.000000f, // vertex 625
	0.833333f, 0.166667f, 0.000000f, // vertex 626
	0.714286f, 0.285714f, 0.000000f, // vertex 627
	0.833333f, 0.166667f, 0.000000f, // vertex 628
	0.833333f, 0.166667f, 0.000000f, // vertex 629
	0.833333f, 0.166667f, 0.000000f, // vertex 630
	1.000000f, 0.000000f, 0.000000f, // vertex 631
	1.000000f, 0.000000f, 0.000000f, // vertex 632
	1.000000f, 0.000000f, 0.000000f, // vertex 633
	1.000000f, 0.000000f, 0.000000f, // vertex 634
	1.000000f, 0.000000f, 0.000000f, // vertex 635
	1.000000f, 0.000000f, 0.000000f, // vertex 636
	0.833333f, 0.166667f, 0.000000f, // vertex 637
	0.833333f, 0.166667f, 0.000000f, // vertex 638
	1.000000f, 0.000000f, 0.000000f, // vertex 639
	0.833333f, 0.166667f, 0.000000f, // vertex 640
	1.000000f, 0.000000f, 0.000000f, // vertex 641
	0.833333f, 0.166667f, 0.000000f, // vertex 642
	1.000000f, 0.000000f, 0.000000f, // vertex 643
	0.833333f, 0.166667f, 0.000000f, // vertex 644
	1.000000f, 0.000000f, 0.000000f, // vertex 645
	1.000000f, 0.000000f, 0.000000f, // vertex 646
	1.000000f, 0.000000f, 0.000000f, // vertex 647
	0.833333f, 0.166667f, 0.000000f, // vertex 648
	0.833333f, 0.166667f, 0.000000f, // vertex 649
	0.555556f, 0.444444f, 0.000000f, // vertex 650
	0.833333f, 0.166667f, 0.000000f, // vertex 651
	0.555556f, 0.444444f, 0.000000f, // vertex 652
	0.500000f, 0.500000f, 0.000000f, // vertex 653
	0.555556f, 0.444444f, 0.000000f, // vertex 654
	0.666667f, 0.333333f, 0.000000f, // vertex 655
	0.833333f, 0.166667f, 0.000000f, // vertex 656
	0.833333f, 0.166667f, 0.000000f, // vertex 657
	0.666667f, 0.333333f, 0.000000f, // vertex 658
	1.000000f, 0.000000f, 0.000000f, // vertex 659
	1.000000f, 0.000000f, 0.000000f, // vertex 660
	1.000000f, 0.000000f, 0.000000f, // vertex 661
	0.500000f, 0.500000f, 0.000000f, // vertex 662
	0.500000f, 0.500000f, 0.000000f, // vertex 663
	0.500000f, 0.500000f, 0.000000f, // vertex 664
	0.500000f, 0.500000f, 0.000000f, // vertex 665
	0.500000f, 0.500000f, 0.000000f, // vertex 666
	0.500000f, 0.500000f, 0.000000f, // vertex 667
	0.500000f, 0.500000f, 0.000000f, // vertex 668
	0.500000f, 0.500000f, 0.000000f, // vertex 669
	0.500000f, 0.500000f, 0.000000f, // vertex 670
	0.500000f, 0.500000f, 0.000000f, // vertex 671
	0.500000f, 0.500000f, 0.000000f, // vertex 672
	0.500000f, 0.500000f, 0.000000f, // vertex 673
	0.500000f, 0.500000f, 0.000000f, // vertex 674
	0.500000f, 0.500000f, 0.000000f, // vertex 675
	0.714286f, 0.285714f, 0.000000f, // vertex 676
	0.714286f, 0.285714f, 0.000000f, // vertex 677
	0.500000f, 0.500000f, 0.000000f, // vertex 678
	0.500000f, 0.500000f, 0.000000f, // vertex 679
	0.500000f, 0.500000f, 0.000000f, // vertex 680
	0.500000f, 0.500000f, 0.000000f, // vertex 681
	0.500000f, 0.500000f, 0.000000f, // vertex 682
	1.000000f, 0.000000f, 0.000000f, // vertex 683
	1.000000f, 0.000000f, 0.000000f, // vertex 684
	1.000000f, 0.000000f, 0.000000f, // vertex 685
	1.000000f, 0.000000f, 0.000000f, // vertex 686
	1.000000f, 0.000000f, 0.000000f, // vertex 687
	1.000000f, 0.000000f, 0.000000f, // vertex 688
	0.833333f, 0.166667f, 0.000000f, // vertex 689
	1.000000f, 0.000000f, 0.000000f, // vertex 690
	0.833333f, 0.166667f, 0.000000f, // vertex 691
	1.000000f, 0.000000f, 0.000000f, // vertex 692
	0.833333f, 0.166667f, 0.000000f, // vertex 693
	1.000000f, 0.000000f, 0.000000f, // vertex 694
	0.500000f, 0.500000f, 0.000000f, // vertex 695
	0.714286f, 0.285714f, 0.000000f, // vertex 696
	0.833333f, 0.166667f, 0.000000f, // vertex 697
	1.000000f, 0.000000f, 0.000000f, // vertex 698
	1.000000f, 0.000000f, 0.000000f, // vertex 699
	1.000000f, 0.000000f, 0.000000f, // vertex 700
	0.555556f, 0.444444f, 0.000000f, // vertex 701
	0.833333f, 0.166667f, 0.000000f, // vertex 702
	0.555556f, 0.444444f, 0.000000f, // vertex 703
	0.833333f, 0.166667f, 0.000000f, // vertex 704
	0.833333f, 0.166667f, 0.000000f, // vertex 705
	0.555556f, 0.444444f, 0.000000f, // vertex 706
	0.500000f, 0.500000f, 0.000000f, // vertex 707
	0.666667f, 0.333333f, 0.000000f, // vertex 708
	0.666667f, 0.333333f, 0.000000f, // vertex 709
	0.833333f, 0.166667f, 0.000000f, // vertex 710
	0.833333f, 0.166667f, 0.000000f, // vertex 711
	1.000000f, 0.000000f, 0.000000f, // vertex 712
	1.000000f, 0.000000f, 0.000000f, // vertex 713
	1.000000f, 0.000000f, 0.000000f, // vertex 714
	0.655738f, 0.344262f, 0.000000f, // vertex 715
	0.833333f, 0.166667f, 0.000000f, // vertex 716
	0.900901f, 0.099099f, 0.000000f, // vertex 717
	0.900901f, 0.099099f, 0.000000f, // vertex 718
	0.833333f, 0.166667f, 0.000000f, // vertex 719
	0.500000f, 0.500000f, 0.000000f, // vertex 720
	0.833333f, 0.166667f, 0.000000f, // vertex 721
	0.500000f, 0.500000f, 0.000000f, // vertex 722
	0.769231f, 0.230769f, 0.000000f, // vertex 723
	0.500000f, 0.500000f, 0.000000f, // vertex 724
	0.833333f, 0.166667f, 0.000000f, // vertex 725
	0.833333f, 0.166667f, 0.000000f, // vertex 726
	0.833333f, 0.166667f, 0.000000f, // vertex 727
	0.833333f, 0.166667f, 0.000000f, // vertex 728
	0.833333f, 0.166667f, 0.000000f, // vertex 729
	0.833333f, 0.166667f, 0.000000f, // vertex 730
	0.833333f, 0.166667f, 0.000000f, // vertex 731
	0.769231f, 0.230769f, 0.000000f, // vertex 732
	0.769231f, 0.230769f, 0.000000f, // vertex 733
	0.769231f, 0.230769f, 0.000000f, // vertex 734
	0.769231f, 0.230769f, 0.000000f, // vertex 735
	0.769231f, 0.230769f, 0.000000f, // vertex 736
	0.833333f, 0.166667f, 0.000000f, // vertex 737
	0.500000f, 0.500000f, 0.000000f, // vertex 738
	0.500000f, 0.500000f, 0.000000f, // vertex 739
	0.500000f, 0.500000f, 0.000000f, // vertex 740
	0.500000f, 0.500000f, 0.000000f, // vertex 741
	0.769231f, 0.230769f, 0.000000f, // vertex 742
	0.833333f, 0.166667f, 0.000000f, // vertex 743
	0.769231f, 0.230769f, 0.000000f, // vertex 744
	0.833333f, 0.166667f, 0.000000f, // vertex 745
	0.833333f, 0.166667f, 0.000000f, // vertex 746
	0.833333f, 0.166667f, 0.000000f, // vertex 747
	0.833333f, 0.166667f, 0.000000f, // vertex 748
	0.833333f, 0.166667f, 0.000000f, // vertex 749
	1.000000f, 0.000000f, 0.000000f, // vertex 750
	1.000000f, 0.000000f, 0.000000f, // vertex 751
	0.909091f, 0.090909f, 0.000000f, // vertex 752
	1.000000f, 0.000000f, 0.000000f, // vertex 753
	1.000000f, 0.000000f, 0.000000f, // vertex 754
	0.833333f, 0.166667f, 0.000000f, // vertex 755
	0.833333f, 0.166667f, 0.000000f, // vertex 756
	0.666667f, 0.333333f, 0.000000f, // vertex 757
	0.666667f, 0.333333f, 0.000000f, // vertex 758
	0.666667f, 0.333333f, 0.000000f, // vertex 759
	0.666667f, 0.333333f, 0.000000f, // vertex 760
	0.833333f, 0.166667f, 0.000000f, // vertex 761
	0.833333f, 0.166667f, 0.000000f, // vertex 762
	1.000000f, 0.000000f, 0.000000f, // vertex 763
	1.000000f, 0.000000f, 0.000000f, // vertex 764
	0.909091f, 0.090909f, 0.000000f, // vertex 765
	1.000000f, 0.000000f, 0.000000f, // vertex 766
	1.000000f, 0.000000f, 0.000000f, // vertex 767
	1.000000f, 0.000000f, 0.000000f, // vertex 768
	1.000000f, 0.000000f, 0.000000f, // vertex 769
	0.500000f, 0.500000f, 0.000000f, // vertex 770
	0.833333f, 0.166667f, 0.000000f, // vertex 771
	0.500000f, 0.500000f, 0.000000f, // vertex 772
	0.833333f, 0.166667f, 0.000000f, // vertex 773
	0.714286f, 0.285714f, 0.000000f, // vertex 774
	0.900901f, 0.099099f, 0.000000f, // vertex 775
	0.655738f, 0.344262f, 0.000000f, // vertex 776
	0.772187f, 0.154437f, 0.073375f, // vertex 777
	0.819001f, 0.180999f, 0.000000f, // vertex 778
	0.772187f, 0.154437f, 0.073375f, // vertex 779
	0.819001f, 0.180999f, 0.000000f, // vertex 780
	0.772187f, 0.154437f, 0.073375f, // vertex 781
	0.772187f, 0.154437f, 0.073375f, // vertex 782
	0.800000f, 0.200000f, 0.000000f, // vertex 783
	0.800000f, 0.200000f, 0.000000f, // vertex 784
	0.772187f, 0.154437f, 0.073375f, // vertex 785
	0.800000f, 0.200000f, 0.000000f, // vertex 786
	0.772187f, 0.154437f, 0.073375f, // vertex 787
	0.800000f, 0.200000f, 0.000000f, // vertex 788
	0.772187f, 0.154437f, 0.073375f, // vertex 789
	0.500000f, 0.500000f, 0.000000f, // vertex 790
	0.900901f, 0.099099f, 0.000000f, // vertex 791
	0.952381f, 0.047619f, 0.000000f, // vertex 792
	0.833333f, 0.166667f, 0.000000f, // vertex 793
	0.909091f, 0.090909f, 0.000000f, // vertex 794
	0.909091f, 0.090909f, 0.000000f, // vertex 795
	1.000000f, 0.000000f, 0.000000f, // vertex 796
	0.500000f, 0.500000f, 0.000000f, // vertex 797
	0.500000f, 0.500000f, 0.000000f, // vertex 798
	0.500000f, 0.500000f, 0.000000f, // vertex 799
	0.500000f, 0.500000f, 0.000000f, // vertex 800
	0.500000f, 0.500000f, 0.000000f, // vertex 801
	0.500000f, 0.500000f, 0.000000f, // vertex 802
	0.500000f, 0.500000f, 0.000000f, // vertex 803
	0.500000f, 0.500000f, 0.000000f, // vertex 804
	0.500000f, 0.500000f, 0.000000f, // vertex 805
	0.500000f, 0.500000f, 0.000000f, // vertex 806
	0.500000f, 0.500000f, 0.000000f, // vertex 807
	0.500000f, 0.500000f, 0.000000f, // vertex 808
	0.500000f, 0.500000f, 0.000000f, // vertex 809
	0.655738f, 0.344262f, 0.000000f, // vertex 810
	0.714286f, 0.285714f, 0.000000f, // vertex 811
	0.500000f, 0.500000f, 0.000000f, // vertex 812
	0.500000f, 0.500000f, 0.000000f, // vertex 813
	0.500000f, 0.500000f, 0.000000f, // vertex 814
	0.500000f, 0.500000f, 0.000000f, // vertex 815
	0.500000f, 0.500000f, 0.000000f, // vertex 816
	0.500000f, 0.500000f, 0.000000f, // vertex 817
	1.000000f, 0.000000f, 0.000000f, // vertex 818
	0.500000f, 0.500000f, 0.000000f, // vertex 819
	0.500000f, 0.500000f, 0.000000f, // vertex 820
	0.500000f, 0.500000f, 0.000000f, // vertex 821
	0.500000f, 0.500000f, 0.000000f, // vertex 822
	0.500000f, 0.500000f, 0.000000f, // vertex 823
	0.500000f, 0.500000f, 0.000000f, // vertex 824
	0.500000f, 0.500000f, 0.000000f, // vertex 825
	0.500000f, 0.500000f, 0.000000f, // vertex 826
	0.500000f, 0.500000f, 0.000000f, // vertex 827
	0.500000f, 0.500000f, 0.000000f, // vertex 828
	0.500000f, 0.500000f, 0.000000f, // vertex 829
	0.819001f, 0.180999f, 0.000000f, // vertex 830
	0.772187f, 0.154437f, 0.073375f, // vertex 831
	0.819001f, 0.180999f, 0.000000f, // vertex 832
	0.819001f, 0.180999f, 0.000000f, // vertex 833
	0.655738f, 0.344262f, 0.000000f, // vertex 834
	0.900901f, 0.099099f, 0.000000f, // vertex 835
	0.714286f, 0.285714f, 0.000000f, // vertex 836
	0.833333f, 0.166667f, 0.000000f, // vertex 837
	0.500000f, 0.500000f, 0.000000f, // vertex 838
	0.833333f, 0.166667f, 0.000000f, // vertex 839
	0.500000f, 0.500000f, 0.000000f, // vertex 840
	1.000000f, 0.000000f, 0.000000f, // vertex 841
	1.000000f, 0.000000f, 0.000000f, // vertex 842
	0.909091f, 0.090909f, 0.000000f, // vertex 843
	0.909091f, 0.090909f, 0.000000f, // vertex 844
	0.833333f, 0.166667f, 0.000000f, // vertex 845
	0.952381f, 0.047619f, 0.000000f, // vertex 846
	0.900901f, 0.099099f, 0.000000f, // vertex 847
	0.500000f, 0.500000f, 0.000000f, // vertex 848
	0.819001f, 0.180999f, 0.000000f, // vertex 849
	0.800000f, 0.200000f, 0.000000f, // vertex 850
	0.772187f, 0.154437f, 0.073375f, // vertex 851
	0.500000f, 0.500000f, 0.000000f, // vertex 852
	0.655738f, 0.344262f, 0.000000f, // vertex 853
	0.500000f, 0.500000f, 0.000000f, // vertex 854
	0.714286f, 0.285714f, 0.000000f, // vertex 855
	0.500000f, 0.500000f, 0.000000f, // vertex 856
	0.500000f, 0.500000f, 0.000000f, // vertex 857
	0.500000f, 0.500000f, 0.000000f, // vertex 858
	0.500000f, 0.500000f, 0.000000f, // vertex 859
	0.500000f, 0.500000f, 0.000000f, // vertex 860
	0.500000f, 0.500000f, 0.000000f, // vertex 861
	0.500000f, 0.500000f, 0.000000f, // vertex 862
	0.500000f, 0.500000f, 0.000000f, // vertex 863
	0.500000f, 0.500000f, 0.000000f, // vertex 864
	0.500000f, 0.500000f, 0.000000f, // vertex 865
	0.500000f, 0.500000f, 0.000000f, // vertex 866
	0.500000f, 0.500000f, 0.000000f, // vertex 867
	0.500000f, 0.500000f, 0.000000f, // vertex 868
	0.500000f, 0.500000f, 0.000000f, // vertex 869
	0.500000f, 0.500000f, 0.000000f, // vertex 870
	0.500000f, 0.500000f, 0.000000f, // vertex 871
	0.500000f, 0.500000f, 0.000000f, // vertex 872
	0.500000f, 0.500000f, 0.000000f, // vertex 873
	0.500000f, 0.500000f, 0.000000f, // vertex 874
	0.500000f, 0.500000f, 0.000000f, // vertex 875
	0.500000f, 0.500000f, 0.000000f, // vertex 876
	0.500000f, 0.500000f, 0.000000f, // vertex 877
	0.500000f, 0.500000f, 0.000000f, // vertex 878
	0.500000f, 0.500000f, 0.000000f, // vertex 879
	0.500000f, 0.500000f, 0.000000f, // vertex 880
	0.500000f, 0.500000f, 0.000000f, // vertex 881
	0.500000f, 0.500000f, 0.000000f, // vertex 882
	0.714286f, 0.285714f, 0.000000f, // vertex 883
	0.500000f, 0.500000f, 0.000000f, // vertex 884
	1.000000f, 0.000000f, 0.000000f, // vertex 885
	0.909091f, 0.090909f, 0.000000f, // vertex 886
	0.833333f, 0.166667f, 0.000000f, // vertex 887
	0.833333f, 0.166667f, 0.000000f, // vertex 888
	0.500000f, 0.500000f, 0.000000f, // vertex 889
	0.500000f, 0.500000f, 0.000000f, // vertex 890
	0.500000f, 0.500000f, 0.000000f, // vertex 891
	0.500000f, 0.500000f, 0.000000f, // vertex 892
	0.500000f, 0.500000f, 0.000000f, // vertex 893
	1.000000f, 0.000000f, 0.000000f, // vertex 894
	0.833333f, 0.166667f, 0.000000f, // vertex 895
	0.909091f, 0.090909f, 0.000000f, // vertex 896
	0.833333f, 0.166667f, 0.000000f, // vertex 897
	0.666667f, 0.333333f, 0.000000f, // vertex 898
	0.800000f, 0.200000f, 0.000000f, // vertex 899
	0.666667f, 0.333333f, 0.000000f, // vertex 900
	0.714286f, 0.285714f, 0.000000f, // vertex 901
	0.666667f, 0.333333f, 0.000000f, // vertex 902
	0.714286f, 0.285714f, 0.000000f, // vertex 903
	0.666667f, 0.333333f, 0.000000f, // vertex 904
	0.714286f, 0.285714f, 0.000000f, // vertex 905
	0.714286f, 0.285714f, 0.000000f, // vertex 906
	0.666667f, 0.333333f, 0.000000f, // vertex 907
	0.555556f, 0.444444f, 0.000000f, // vertex 908
	0.666667f, 0.333333f, 0.000000f, // vertex 909
	0.555556f, 0.444444f, 0.000000f, // vertex 910
	0.666667f, 0.333333f, 0.000000f, // vertex 911
	0.555556f, 0.444444f, 0.000000f, // vertex 912
	0.666667f, 0.333333f, 0.000000f, // vertex 913
	0.666667f, 0.333333f, 0.000000f, // vertex 914
	0.714286f, 0.285714f, 0.000000f, // vertex 915
	0.714286f, 0.285714f, 0.000000f, // vertex 916
	0.800000f, 0.200000f, 0.000000f, // vertex 917
	0.714286f, 0.285714f, 0.000000f, // vertex 918
	0.666667f, 0.333333f, 0.000000f, // vertex 919
	0.666667f, 0.333333f, 0.000000f, // vertex 920
	0.555556f, 0.444444f, 0.000000f, // vertex 921
	0.555556f, 0.444444f, 0.000000f, // vertex 922
	0.800000f, 0.200000f, 0.000000f, // vertex 923
	0.666667f, 0.333333f, 0.000000f, // vertex 924
	0.666667f, 0.333333f, 0.000000f, // vertex 925
	0.555556f, 0.444444f, 0.000000f, // vertex 926
	0.555556f, 0.444444f, 0.000000f, // vertex 927
	0.500000f, 0.500000f, 0.000000f, // vertex 928
	0.500000f, 0.500000f, 0.000000f, // vertex 929
	0.500000f, 0.500000f, 0.000000f, // vertex 930
	0.500000f, 0.500000f, 0.000000f, // vertex 931
	0.500000f, 0.500000f, 0.000000f, // vertex 932
	0.500000f, 0.500000f, 0.000000f, // vertex 933
	0.500000f, 0.500000f, 0.000000f, // vertex 934
	0.500000f, 0.500000f, 0.000000f, // vertex 935
	0.500000f, 0.500000f, 0.000000f, // vertex 936
	0.500000f, 0.500000f, 0.000000f, // vertex 937
	0.500000f, 0.500000f, 0.000000f, // vertex 938
	0.500000f, 0.500000f, 0.000000f, // vertex 939
	0.500000f, 0.500000f, 0.000000f, // vertex 940
	0.500000f, 0.500000f, 0.000000f, // vertex 941
	0.500000f, 0.500000f, 0.000000f, // vertex 942
	0.500000f, 0.500000f, 0.000000f, // vertex 943
	0.500000f, 0.500000f, 0.000000f, // vertex 944
	0.500000f, 0.500000f, 0.000000f, // vertex 945
	0.500000f, 0.500000f, 0.000000f, // vertex 946
	0.500000f, 0.500000f, 0.000000f, // vertex 947
	0.500000f, 0.500000f, 0.000000f, // vertex 948
	0.500000f, 0.500000f, 0.000000f, // vertex 949
	0.500000f, 0.500000f, 0.000000f, // vertex 950
	0.500000f, 0.500000f, 0.000000f, // vertex 951
	0.500000f, 0.500000f, 0.000000f, // vertex 952
	0.500000f, 0.500000f, 0.000000f, // vertex 953
	0.500000f, 0.500000f, 0.000000f, // vertex 954
	0.500000f, 0.500000f, 0.000000f, // vertex 955
	0.500000f, 0.500000f, 0.000000f, // vertex 956
	0.500000f, 0.500000f, 0.000000f, // vertex 957
	0.500000f, 0.500000f, 0.000000f, // vertex 958
	0.500000f, 0.500000f, 0.000000f, // vertex 959
	0.500000f, 0.500000f, 0.000000f, // vertex 960
	0.500000f, 0.500000f, 0.000000f, // vertex 961
	0.500000f, 0.500000f, 0.000000f, // vertex 962
	0.500000f, 0.500000f, 0.000000f, // vertex 963
	0.500000f, 0.500000f, 0.000000f, // vertex 964
	0.500000f, 0.500000f, 0.000000f, // vertex 965
	0.500000f, 0.500000f, 0.000000f, // vertex 966
	0.500000f, 0.500000f, 0.000000f, // vertex 967
	0.500000f, 0.500000f, 0.000000f, // vertex 968
	0.500000f, 0.500000f, 0.000000f, // vertex 969
	0.500000f, 0.500000f, 0.000000f, // vertex 970
	0.500000f, 0.500000f, 0.000000f, // vertex 971
	0.500000f, 0.500000f, 0.000000f, // vertex 972
	0.500000f, 0.500000f, 0.000000f, // vertex 973
	0.500000f, 0.500000f, 0.000000f, // vertex 974
	0.500000f, 0.500000f, 0.000000f, // vertex 975
	1.000000f, 0.000000f, 0.000000f, // vertex 976
	1.000000f, 0.000000f, 0.000000f, // vertex 977
	1.000000f, 0.000000f, 0.000000f, // vertex 978
	1.000000f, 0.000000f, 0.000000f, // vertex 979
	1.000000f, 0.000000f, 0.000000f, // vertex 980
	1.000000f, 0.000000f, 0.000000f, // vertex 981
	1.000000f, 0.000000f, 0.000000f, // vertex 982
	1.000000f, 0.000000f, 0.000000f, // vertex 983
	1.000000f, 0.000000f, 0.000000f, // vertex 984
	1.000000f, 0.000000f, 0.000000f, // vertex 985
	1.000000f, 0.000000f, 0.000000f, // vertex 986
	1.000000f, 0.000000f, 0.000000f, // vertex 987
	1.000000f, 0.000000f, 0.000000f, // vertex 988
	1.000000f, 0.000000f, 0.000000f, // vertex 989
	1.000000f, 0.000000f, 0.000000f, // vertex 990
	1.000000f, 0.000000f, 0.000000f, // vertex 991
	1.000000f, 0.000000f, 0.000000f, // vertex 992
	1.000000f, 0.000000f, 0.000000f, // vertex 993
	1.000000f, 0.000000f, 0.000000f, // vertex 994
	1.000000f, 0.000000f, 0.000000f, // vertex 995
	1.000000f, 0.000000f, 0.000000f, // vertex 996
	1.000000f, 0.000000f, 0.000000f, // vertex 997
	1.000000f, 0.000000f, 0.000000f, // vertex 998
	1.000000f, 0.000000f, 0.000000f, // vertex 999
	1.000000f, 0.000000f, 0.000000f, // vertex 1000
	1.000000f, 0.000000f, 0.000000f, // vertex 1001
	1.000000f, 0.000000f, 0.000000f, // vertex 1002
	1.000000f, 0.000000f, 0.000000f, // vertex 1003
	1.000000f, 0.000000f, 0.000000f, // vertex 1004
	1.000000f, 0.000000f, 0.000000f, // vertex 1005
	1.000000f, 0.000000f, 0.000000f, // vertex 1006
	1.000000f, 0.000000f, 0.000000f, // vertex 1007
	1.000000f, 0.000000f, 0.000000f, // vertex 1008
	1.000000f, 0.000000f, 0.000000f, // vertex 1009
	1.000000f, 0.000000f, 0.000000f, // vertex 1010
	1.000000f, 0.000000f, 0.000000f, // vertex 1011
	1.000000f, 0.000000f, 0.000000f, // vertex 1012
	1.000000f, 0.000000f, 0.000000f, // vertex 1013
	1.000000f, 0.000000f, 0.000000f, // vertex 1014
	1.000000f, 0.000000f, 0.000000f, // vertex 1015
	1.000000f, 0.000000f, 0.000000f, // vertex 1016
	1.000000f, 0.000000f, 0.000000f, // vertex 1017
	1.000000f, 0.000000f, 0.000000f, // vertex 1018
	1.000000f, 0.000000f, 0.000000f, // vertex 1019
	1.000000f, 0.000000f, 0.000000f, // vertex 1020
	1.000000f, 0.000000f, 0.000000f, // vertex 1021
	1.000000f, 0.000000f, 0.000000f, // vertex 1022
	1.000000f, 0.000000f, 0.000000f, // vertex 1023
	1.000000f, 0.000000f, 0.000000f, // vertex 1024
	1.000000f, 0.000000f, 0.000000f, // vertex 1025
	1.000000f, 0.000000f, 0.000000f, // vertex 1026
	1.000000f, 0.000000f, 0.000000f, // vertex 1027
	1.000000f, 0.000000f, 0.000000f, // vertex 1028
	1.000000f, 0.000000f, 0.000000f, // vertex 1029
	1.000000f, 0.000000f, 0.000000f, // vertex 1030
	1.000000f, 0.000000f, 0.000000f, // vertex 1031
	1.000000f, 0.000000f, 0.000000f, // vertex 1032
	1.000000f, 0.000000f, 0.000000f, // vertex 1033
	1.000000f, 0.000000f, 0.000000f, // vertex 1034
	1.000000f, 0.000000f, 0.000000f, // vertex 1035
	1.000000f, 0.000000f, 0.000000f, // vertex 1036
	1.000000f, 0.000000f, 0.000000f, // vertex 1037
	1.000000f, 0.000000f, 0.000000f, // vertex 1038
	1.000000f, 0.000000f, 0.000000f, // vertex 1039
	1.000000f, 0.000000f, 0.000000f, // vertex 1040
	1.000000f, 0.000000f, 0.000000f, // vertex 1041
	1.000000f, 0.000000f, 0.000000f, // vertex 1042
	1.000000f, 0.000000f, 0.000000f, // vertex 1043
	1.000000f, 0.000000f, 0.000000f, // vertex 1044
	1.000000f, 0.000000f, 0.000000f, // vertex 1045
	1.000000f, 0.000000f, 0.000000f, // vertex 1046
	1.000000f, 0.000000f, 0.000000f, // vertex 1047
	1.000000f, 0.000000f, 0.000000f, // vertex 1048
	1.000000f, 0.000000f, 0.000000f, // vertex 1049
	1.000000f, 0.000000f, 0.000000f, // vertex 1050
	1.000000f, 0.000000f, 0.000000f, // vertex 1051
	1.000000f, 0.000000f, 0.000000f, // vertex 1052
	1.000000f, 0.000000f, 0.000000f, // vertex 1053
	1.000000f, 0.000000f, 0.000000f, // vertex 1054
	1.000000f, 0.000000f, 0.000000f, // vertex 1055
	1.000000f, 0.000000f, 0.000000f, // vertex 1056
	1.000000f, 0.000000f, 0.000000f, // vertex 1057
	1.000000f, 0.000000f, 0.000000f, // vertex 1058
	1.000000f, 0.000000f, 0.000000f, // vertex 1059
	1.000000f, 0.000000f, 0.000000f, // vertex 1060
	1.000000f, 0.000000f, 0.000000f, // vertex 1061
	1.000000f, 0.000000f, 0.000000f, // vertex 1062
	1.000000f, 0.000000f, 0.000000f, // vertex 1063
	1.000000f, 0.000000f, 0.000000f, // vertex 1064
	1.000000f, 0.000000f, 0.000000f, // vertex 1065
	1.000000f, 0.000000f, 0.000000f, // vertex 1066
	1.000000f, 0.000000f, 0.000000f, // vertex 1067
	1.000000f, 0.000000f, 0.000000f, // vertex 1068
	1.000000f, 0.000000f, 0.000000f, // vertex 1069
	1.000000f, 0.000000f, 0.000000f, // vertex 1070
	1.000000f, 0.000000f, 0.000000f, // vertex 1071
	1.000000f, 0.000000f, 0.000000f, // vertex 1072
	1.000000f, 0.000000f, 0.000000f, // vertex 1073
	1.000000f, 0.000000f, 0.000000f, // vertex 1074
	1.000000f, 0.000000f, 0.000000f, // vertex 1075
	1.000000f, 0.000000f, 0.000000f, // vertex 1076
	1.000000f, 0.000000f, 0.000000f, // vertex 1077
	1.000000f, 0.000000f, 0.000000f, // vertex 1078
	1.000000f, 0.000000f, 0.000000f, // vertex 1079
	1.000000f, 0.000000f, 0.000000f, // vertex 1080
	1.000000f, 0.000000f, 0.000000f, // vertex 1081
	1.000000f, 0.000000f, 0.000000f, // vertex 1082
	1.000000f, 0.000000f, 0.000000f, // vertex 1083
	1.000000f, 0.000000f, 0.000000f, // vertex 1084
	1.000000f, 0.000000f, 0.000000f, // vertex 1085
	1.000000f, 0.000000f, 0.000000f, // vertex 1086
	1.000000f, 0.000000f, 0.000000f, // vertex 1087
	1.000000f, 0.000000f, 0.000000f, // vertex 1088
	1.000000f, 0.000000f, 0.000000f, // vertex 1089
	1.000000f, 0.000000f, 0.000000f, // vertex 1090
	1.000000f, 0.000000f, 0.000000f, // vertex 1091
	1.000000f, 0.000000f, 0.000000f, // vertex 1092
	1.000000f, 0.000000f, 0.000000f, // vertex 1093
	1.000000f, 0.000000f, 0.000000f, // vertex 1094
	1.000000f, 0.000000f, 0.000000f, // vertex 1095
	1.000000f, 0.000000f, 0.000000f, // vertex 1096
	1.000000f, 0.000000f, 0.000000f, // vertex 1097
	1.000000f, 0.000000f, 0.000000f, // vertex 1098
	1.000000f, 0.000000f, 0.000000f, // vertex 1099
	1.000000f, 0.000000f, 0.000000f, // vertex 1100
	1.000000f, 0.000000f, 0.000000f, // vertex 1101
	1.000000f, 0.000000f, 0.000000f, // vertex 1102
	1.000000f, 0.000000f, 0.000000f, // vertex 1103
	1.000000f, 0.000000f, 0.000000f, // vertex 1104
	1.000000f, 0.000000f, 0.000000f, // vertex 1105
	1.000000f, 0.000000f, 0.000000f, // vertex 1106
	1.000000f, 0.000000f, 0.000000f, // vertex 1107
	1.000000f, 0.000000f, 0.000000f, // vertex 1108
	1.000000f, 0.000000f, 0.000000f, // vertex 1109
	1.000000f, 0.000000f, 0.000000f, // vertex 1110
	1.000000f, 0.000000f, 0.000000f, // vertex 1111
	1.000000f, 0.000000f, 0.000000f, // vertex 1112
	1.000000f, 0.000000f, 0.000000f, // vertex 1113
	1.000000f, 0.000000f, 0.000000f, // vertex 1114
	1.000000f, 0.000000f, 0.000000f, // vertex 1115
	1.000000f, 0.000000f, 0.000000f, // vertex 1116
	1.000000f, 0.000000f, 0.000000f, // vertex 1117
	1.000000f, 0.000000f, 0.000000f, // vertex 1118
	1.000000f, 0.000000f, 0.000000f, // vertex 1119
	1.000000f, 0.000000f, 0.000000f, // vertex 1120
	1.000000f, 0.000000f, 0.000000f, // vertex 1121
	1.000000f, 0.000000f, 0.000000f, // vertex 1122
	1.000000f, 0.000000f, 0.000000f, // vertex 1123
	1.000000f, 0.000000f, 0.000000f, // vertex 1124
	1.000000f, 0.000000f, 0.000000f, // vertex 1125
	1.000000f, 0.000000f, 0.000000f, // vertex 1126
	1.000000f, 0.000000f, 0.000000f, // vertex 1127
	1.000000f, 0.000000f, 0.000000f, // vertex 1128
	1.000000f, 0.000000f, 0.000000f, // vertex 1129
	1.000000f, 0.000000f, 0.000000f, // vertex 1130
	1.000000f, 0.000000f, 0.000000f, // vertex 1131
	1.000000f, 0.000000f, 0.000000f, // vertex 1132
	1.000000f, 0.000000f, 0.000000f, // vertex 1133
	1.000000f, 0.000000f, 0.000000f, // vertex 1134
	1.000000f, 0.000000f, 0.000000f, // vertex 1135
	1.000000f, 0.000000f, 0.000000f, // vertex 1136
	1.000000f, 0.000000f, 0.000000f, // vertex 1137
	1.000000f, 0.000000f, 0.000000f, // vertex 1138
	1.000000f, 0.000000f, 0.000000f, // vertex 1139
	1.000000f, 0.000000f, 0.000000f, // vertex 1140
	1.000000f, 0.000000f, 0.000000f, // vertex 1141
	1.000000f, 0.000000f, 0.000000f, // vertex 1142
	1.000000f, 0.000000f, 0.000000f, // vertex 1143
	1.000000f, 0.000000f, 0.000000f, // vertex 1144
	1.000000f, 0.000000f, 0.000000f, // vertex 1145
	1.000000f, 0.000000f, 0.000000f, // vertex 1146
	1.000000f, 0.000000f, 0.000000f, // vertex 1147
	1.000000f, 0.000000f, 0.000000f, // vertex 1148
	1.000000f, 0.000000f, 0.000000f, // vertex 1149
	1.000000f, 0.000000f, 0.000000f, // vertex 1150
	1.000000f, 0.000000f, 0.000000f, // vertex 1151
	1.000000f, 0.000000f, 0.000000f, // vertex 1152
	1.000000f, 0.000000f, 0.000000f, // vertex 1153
	1.000000f, 0.000000f, 0.000000f, // vertex 1154
	1.000000f, 0.000000f, 0.000000f, // vertex 1155
	1.000000f, 0.000000f, 0.000000f, // vertex 1156
	1.000000f, 0.000000f, 0.000000f, // vertex 1157
	1.000000f, 0.000000f, 0.000000f, // vertex 1158
	1.000000f, 0.000000f, 0.000000f, // vertex 1159
	1.000000f, 0.000000f, 0.000000f, // vertex 1160
	1.000000f, 0.000000f, 0.000000f, // vertex 1161
	1.000000f, 0.000000f, 0.000000f, // vertex 1162
	1.000000f, 0.000000f, 0.000000f, // vertex 1163
	1.000000f, 0.000000f, 0.000000f, // vertex 1164
	1.000000f, 0.000000f, 0.000000f, // vertex 1165
	1.000000f, 0.000000f, 0.000000f, // vertex 1166
	1.000000f, 0.000000f, 0.000000f, // vertex 1167
	1.000000f, 0.000000f, 0.000000f, // vertex 1168
	1.000000f, 0.000000f, 0.000000f, // vertex 1169
	1.000000f, 0.000000f, 0.000000f, // vertex 1170
	1.000000f, 0.000000f, 0.000000f, // vertex 1171
	1.000000f, 0.000000f, 0.000000f, // vertex 1172
	1.000000f, 0.000000f, 0.000000f, // vertex 1173
	1.000000f, 0.000000f, 0.000000f, // vertex 1174
	1.000000f, 0.000000f, 0.000000f, // vertex 1175
	1.000000f, 0.000000f, 0.000000f, // vertex 1176
	1.000000f, 0.000000f, 0.000000f, // vertex 1177
	1.000000f, 0.000000f, 0.000000f, // vertex 1178
	1.000000f, 0.000000f, 0.000000f, // vertex 1179
	0.909091f, 0.090909f, 0.000000f, // vertex 1180
	1.000000f, 0.000000f, 0.000000f, // vertex 1181
	1.000000f, 0.000000f, 0.000000f, // vertex 1182
	0.833333f, 0.166667f, 0.000000f, // vertex 1183
	0.833333f, 0.166667f, 0.000000f, // vertex 1184
	0.666667f, 0.333333f, 0.000000f, // vertex 1185
	0.666667f, 0.333333f, 0.000000f, // vertex 1186
	0.666667f, 0.333333f, 0.000000f, // vertex 1187
	0.666667f, 0.333333f, 0.000000f, // vertex 1188
	0.833333f, 0.166667f, 0.000000f, // vertex 1189
	0.833333f, 0.166667f, 0.000000f, // vertex 1190
	1.000000f, 0.000000f, 0.000000f, // vertex 1191
	1.000000f, 0.000000f, 0.000000f, // vertex 1192
	0.909091f, 0.090909f, 0.000000f, // vertex 1193
	1.000000f, 0.000000f, 0.000000f, // vertex 1194
	1.000000f, 0.000000f, 0.000000f, // vertex 1195
	1.000000f, 0.000000f, 0.000000f, // vertex 1196
	1.000000f, 0.000000f, 0.000000f, // vertex 1197
	0.500000f, 0.500000f, 0.000000f, // vertex 1198
	0.833333f, 0.166667f, 0.000000f, // vertex 1199
	0.500000f, 0.500000f, 0.000000f, // vertex 1200
	0.833333f, 0.166667f, 0.000000f, // vertex 1201
	0.714286f, 0.285714f, 0.000000f, // vertex 1202
	0.900901f, 0.099099f, 0.000000f, // vertex 1203
	0.655738f, 0.344262f, 0.000000f, // vertex 1204
	0.772187f, 0.154437f, 0.073375f, // vertex 1205
	0.819001f, 0.180999f, 0.000000f, // vertex 1206
	0.772187f, 0.154437f, 0.073375f, // vertex 1207
	0.819001f, 0.180999f, 0.000000f, // vertex 1208
	0.772187f, 0.154437f, 0.073375f, // vertex 1209
	0.772187f, 0.154437f, 0.073375f, // vertex 1210
	0.800000f, 0.200000f, 0.000000f, // vertex 1211
	0.800000f, 0.200000f, 0.000000f, // vertex 1212
	0.772187f, 0.154437f, 0.073375f, // vertex 1213
	0.800000f, 0.200000f, 0.000000f, // vertex 1214
	0.772187f, 0.154437f, 0.073375f, // vertex 1215
	0.800000f, 0.200000f, 0.000000f, // vertex 1216
	0.772187f, 0.154437f, 0.073375f, // vertex 1217
	0.500000f, 0.500000f, 0.000000f, // vertex 1218
	0.900901f, 0.099099f, 0.000000f, // vertex 1219
	0.952381f, 0.047619f, 0.000000f, // vertex 1220
	0.833333f, 0.166667f, 0.000000f, // vertex 1221
	0.909091f, 0.090909f, 0.000000f, // vertex 1222
	0.909091f, 0.090909f, 0.000000f, // vertex 1223
	1.000000f, 0.000000f, 0.000000f, // vertex 1224
	0.500000f, 0.500000f, 0.000000f, // vertex 1225
	0.500000f, 0.500000f, 0.000000f, // vertex 1226
	0.500000f, 0.500000f, 0.000000f, // vertex 1227
	0.500000f, 0.500000f, 0.000000f, // vertex 1228
	0.500000f, 0.500000f, 0.000000f, // vertex 1229
	0.500000f, 0.500000f, 0.000000f, // vertex 1230
	0.500000f, 0.500000f, 0.000000f, // vertex 1231
	0.500000f, 0.500000f, 0.000000f, // vertex 1232
	0.500000f, 0.500000f, 0.000000f, // vertex 1233
	0.500000f, 0.500000f, 0.000000f, // vertex 1234
	0.500000f, 0.500000f, 0.000000f, // vertex 1235
	0.500000f, 0.500000f, 0.000000f, // vertex 1236
	0.500000f, 0.500000f, 0.000000f, // vertex 1237
	0.655738f, 0.344262f, 0.000000f, // vertex 1238
	0.714286f, 0.285714f, 0.000000f, // vertex 1239
	0.500000f, 0.500000f, 0.000000f, // vertex 1240
	0.500000f, 0.500000f, 0.000000f, // vertex 1241
	0.500000f, 0.500000f, 0.000000f, // vertex 1242
	0.500000f, 0.500000f, 0.000000f, // vertex 1243
	0.500000f, 0.500000f, 0.000000f, // vertex 1244
	0.500000f, 0.500000f, 0.000000f, // vertex 1245
	1.000000f, 0.000000f, 0.000000f, // vertex 1246
	0.500000f, 0.500000f, 0.000000f, // vertex 1247
	0.500000f, 0.500000f, 0.000000f, // vertex 1248
	0.500000f, 0.500000f, 0.000000f, // vertex 1249
	0.500000f, 0.500000f, 0.000000f, // vertex 1250
	0.500000f, 0.500000f, 0.000000f, // vertex 1251
	0.500000f, 0.500000f, 0.000000f, // vertex 1252
	0.500000f, 0.500000f, 0.000000f, // vertex 1253
	0.500000f, 0.500000f, 0.000000f, // vertex 1254
	0.500000f, 0.500000f, 0.000000f, // vertex 1255
	0.500000f, 0.500000f, 0.000000f, // vertex 1256
	0.500000f, 0.500000f, 0.000000f, // vertex 1257
	0.819001f, 0.180999f, 0.000000f, // vertex 1258
	0.772187f, 0.154437f, 0.073375f, // vertex 1259
	0.819001f, 0.180999f, 0.000000f, // vertex 1260
	0.819001f, 0.180999f, 0.000000f, // vertex 1261
	0.655738f, 0.344262f, 0.000000f, // vertex 1262
	0.900901f, 0.099099f, 0.000000f, // vertex 1263
	0.714286f, 0.285714f, 0.000000f, // vertex 1264
	0.833333f, 0.166667f, 0.000000f, // vertex 1265
	0.500000f, 0.500000f, 0.000000f, // vertex 1266
	0.833333f, 0.166667f, 0.000000f, // vertex 1267
	0.500000f, 0.500000f, 0.000000f, // vertex 1268
	1.000000f, 0.000000f, 0.000000f, // vertex 1269
	1.000000f, 0.000000f, 0.000000f, // vertex 1270
	0.909091f, 0.090909f, 0.000000f, // vertex 1271
	0.909091f, 0.090909f, 0.000000f, // vertex 1272
	0.833333f, 0.166667f, 0.000000f, // vertex 1273
	0.952381f, 0.047619f, 0.000000f, // vertex 1274
	0.900901f, 0.099099f, 0.000000f, // vertex 1275
	0.500000f, 0.500000f, 0.000000f, // vertex 1276
	0.819001f, 0.180999f, 0.000000f, // vertex 1277
	0.800000f, 0.200000f, 0.000000f, // vertex 1278
	0.772187f, 0.154437f, 0.073375f, // vertex 1279
	0.500000f, 0.500000f, 0.000000f, // vertex 1280
	0.655738f, 0.344262f, 0.000000f, // vertex 1281
	0.500000f, 0.500000f, 0.000000f, // vertex 1282
	0.714286f, 0.285714f, 0.000000f, // vertex 1283
	0.500000f, 0.500000f, 0.000000f, // vertex 1284
	0.500000f, 0.500000f, 0.000000f, // vertex 1285
	0.500000f, 0.500000f, 0.000000f, // vertex 1286
	0.500000f, 0.500000f, 0.000000f, // vertex 1287
	0.500000f, 0.500000f, 0.000000f, // vertex 1288
	0.500000f, 0.500000f, 0.000000f, // vertex 1289
	0.500000f, 0.500000f, 0.000000f, // vertex 1290
	0.500000f, 0.500000f, 0.000000f, // vertex 1291
	0.500000f, 0.500000f, 0.000000f, // vertex 1292
	0.500000f, 0.500000f, 0.000000f, // vertex 1293
	0.500000f, 0.500000f, 0.000000f, // vertex 1294
	0.500000f, 0.500000f, 0.000000f, // vertex 1295
	0.500000f, 0.500000f, 0.000000f, // vertex 1296
	0.500000f, 0.500000f, 0.000000f, // vertex 1297
	0.500000f, 0.500000f, 0.000000f, // vertex 1298
	0.500000f, 0.500000f, 0.000000f, // vertex 1299
	0.500000f, 0.500000f, 0.000000f, // vertex 1300
	0.500000f, 0.500000f, 0.000000f, // vertex 1301
	0.500000f, 0.500000f, 0.000000f, // vertex 1302
	0.500000f, 0.500000f, 0.000000f, // vertex 1303
	0.500000f, 0.500000f, 0.000000f, // vertex 1304
	0.500000f, 0.500000f, 0.000000f, // vertex 1305
	0.500000f, 0.500000f, 0.000000f, // vertex 1306
	0.500000f, 0.500000f, 0.000000f, // vertex 1307
	0.500000f, 0.500000f, 0.000000f, // vertex 1308
	0.500000f, 0.500000f, 0.000000f, // vertex 1309
	0.500000f, 0.500000f, 0.000000f, // vertex 1310
	0.714286f, 0.285714f, 0.000000f, // vertex 1311
	0.500000f, 0.500000f, 0.000000f, // vertex 1312
	1.000000f, 0.000000f, 0.000000f, // vertex 1313
	0.909091f, 0.090909f, 0.000000f, // vertex 1314
	0.833333f, 0.166667f, 0.000000f, // vertex 1315
	0.833333f, 0.166667f, 0.000000f, // vertex 1316
	0.500000f, 0.500000f, 0.000000f, // vertex 1317
	0.500000f, 0.500000f, 0.000000f, // vertex 1318
	0.500000f, 0.500000f, 0.000000f, // vertex 1319
	0.500000f, 0.500000f, 0.000000f, // vertex 1320
	0.500000f, 0.500000f, 0.000000f, // vertex 1321
	1.000000f, 0.000000f, 0.000000f, // vertex 1322
	0.833333f, 0.166667f, 0.000000f, // vertex 1323
	0.909091f, 0.090909f, 0.000000f, // vertex 1324
	0.833333f, 0.166667f, 0.000000f, // vertex 1325
	0.666667f, 0.333333f, 0.000000f, // vertex 1326
	0.800000f, 0.200000f, 0.000000f, // vertex 1327
	0.666667f, 0.333333f, 0.000000f, // vertex 1328
	0.714286f, 0.285714f, 0.000000f, // vertex 1329
	0.666667f, 0.333333f, 0.000000f, // vertex 1330
	0.714286f, 0.285714f, 0.000000f, // vertex 1331
	0.666667f, 0.333333f, 0.000000f, // vertex 1332
	0.714286f, 0.285714f, 0.000000f, // vertex 1333
	0.714286f, 0.285714f, 0.000000f, // vertex 1334
	0.666667f, 0.333333f, 0.000000f, // vertex 1335
	0.555556f, 0.444444f, 0.000000f, // vertex 1336
	0.666667f, 0.333333f, 0.000000f, // vertex 1337
	0.555556f, 0.444444f, 0.000000f, // vertex 1338
	0.666667f, 0.333333f, 0.000000f, // vertex 1339
	0.555556f, 0.444444f, 0.000000f, // vertex 1340
	0.666667f, 0.333333f, 0.000000f, // vertex 1341
	0.666667f, 0.333333f, 0.000000f, // vertex 1342
	0.714286f, 0.285714f, 0.000000f, // vertex 1343
	0.714286f, 0.285714f, 0.000000f, // vertex 1344
	0.800000f, 0.200000f, 0.000000f, // vertex 1345
	0.714286f, 0.285714f, 0.000000f, // vertex 1346
	0.666667f, 0.333333f, 0.000000f, // vertex 1347
	0.666667f, 0.333333f, 0.000000f, // vertex 1348
	0.555556f, 0.444444f, 0.000000f, // vertex 1349
	0.555556f, 0.444444f, 0.000000f, // vertex 1350
	0.800000f, 0.200000f, 0.000000f, // vertex 1351
	0.666667f, 0.333333f, 0.000000f, // vertex 1352
	0.666667f, 0.333333f, 0.000000f, // vertex 1353
	0.555556f, 0.444444f, 0.000000f, // vertex 1354
	0.555556f, 0.444444f, 0.000000f, // vertex 1355
	0.500000f, 0.500000f, 0.000000f, // vertex 1356
	0.500000f, 0.500000f, 0.000000f, // vertex 1357
	0.500000f, 0.500000f, 0.000000f, // vertex 1358
	0.500000f, 0.500000f, 0.000000f, // vertex 1359
	0.500000f, 0.500000f, 0.000000f, // vertex 1360
	0.500000f, 0.500000f, 0.000000f, // vertex 1361
	0.500000f, 0.500000f, 0.000000f, // vertex 1362
	0.500000f, 0.500000f, 0.000000f, // vertex 1363
	0.500000f, 0.500000f, 0.000000f, // vertex 1364
	0.500000f, 0.500000f, 0.000000f, // vertex 1365
	0.500000f, 0.500000f, 0.000000f, // vertex 1366
	0.500000f, 0.500000f, 0.000000f, // vertex 1367
	0.500000f, 0.500000f, 0.000000f, // vertex 1368
	0.500000f, 0.500000f, 0.000000f, // vertex 1369
	0.500000f, 0.500000f, 0.000000f, // vertex 1370
	0.500000f, 0.500000f, 0.000000f, // vertex 1371
	0.500000f, 0.500000f, 0.000000f, // vertex 1372
	0.500000f, 0.500000f, 0.000000f, // vertex 1373
	0.500000f, 0.500000f, 0.000000f, // vertex 1374
	0.500000f, 0.500000f, 0.000000f, // vertex 1375
	0.500000f, 0.500000f, 0.000000f, // vertex 1376
	0.500000f, 0.500000f, 0.000000f, // vertex 1377
	0.500000f, 0.500000f, 0.000000f, // vertex 1378
	0.500000f, 0.500000f, 0.000000f, // vertex 1379
	0.500000f, 0.500000f, 0.000000f, // vertex 1380
	0.500000f, 0.500000f, 0.000000f, // vertex 1381
	0.500000f, 0.500000f, 0.000000f, // vertex 1382
	0.500000f, 0.500000f, 0.000000f, // vertex 1383
	0.500000f, 0.500000f, 0.000000f, // vertex 1384
	0.500000f, 0.500000f, 0.000000f, // vertex 1385
	0.500000f, 0.500000f, 0.000000f, // vertex 1386
	0.500000f, 0.500000f, 0.000000f, // vertex 1387
	0.500000f, 0.500000f, 0.000000f, // vertex 1388
	0.500000f, 0.500000f, 0.000000f, // vertex 1389
	0.500000f, 0.500000f, 0.000000f, // vertex 1390
	0.500000f, 0.500000f, 0.000000f, // vertex 1391
	0.500000f, 0.500000f, 0.000000f, // vertex 1392
	0.500000f, 0.500000f, 0.000000f, // vertex 1393
	0.500000f, 0.500000f, 0.000000f, // vertex 1394
	0.500000f, 0.500000f, 0.000000f, // vertex 1395
	0.500000f, 0.500000f, 0.000000f, // vertex 1396
	0.500000f, 0.500000f, 0.000000f, // vertex 1397
	0.500000f, 0.500000f, 0.000000f, // vertex 1398
	0.500000f, 0.500000f, 0.000000f, // vertex 1399
	0.500000f, 0.500000f, 0.000000f, // vertex 1400
	0.500000f, 0.500000f, 0.000000f, // vertex 1401
	0.500000f, 0.500000f, 0.000000f, // vertex 1402
	0.500000f, 0.500000f, 0.000000f, // vertex 1403                                                          
};
*/

Mth::Vector testBonePositions[] = {
	Mth::Vector( 0.000000f, 0.000000f, 0.000000f, 1.000000f ),
	Mth::Vector( 0.003876f, 3.697956f, 1.117333f, 1.000000f ),
	Mth::Vector( 0.003872f, 4.297436f, 2.893996f, 1.000000f ),
	Mth::Vector( 0.003868f, 9.568745f, 1.807596f, 1.000000f ),
	Mth::Vector( 0.003863f, 7.351619f, 5.444900f, 1.000000f ),
};											    

Mth::Vector testBoneScales[] = {
	Mth::Vector( 1.000000f, 1.000000f, 1.000000f, 1.000000f ),
	Mth::Vector( 1.000000f, 1.000000f, 1.000000f, 1.000000f ),
	Mth::Vector( 1.000000f, 1.000000f, 1.000000f, 1.000000f ),
	Mth::Vector( 1.000000f, 1.000000f, 1.000000f, 1.000000f ),
	Mth::Vector( 1.000000f, 1.000000f, 1.000000f, 1.000000f ),
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCutsceneData::CCutsceneData()
{
	mp_fileLibrary = NULL;

	mp_cameraQuickAnim = NULL;
	mp_objectQuickAnim = NULL;

	m_numObjects = 0;
	m_numAssets = 0;

	m_audioStarted = false;
	m_currentTime = 0.0f;

	m_original_params_set = false;

	Pcm::StopMusic();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCutsceneData::~CCutsceneData()
{
	if ( mp_fileLibrary )
	{
		delete mp_fileLibrary;
		mp_fileLibrary = NULL;
	}

	if ( mp_cameraQuickAnim )
	{
		delete mp_cameraQuickAnim;
		mp_cameraQuickAnim = NULL;
	}

	if ( mp_objectQuickAnim )
	{
		delete mp_objectQuickAnim;
		mp_objectQuickAnim = NULL;
	}
	
	for ( int i = 0; i < m_numBonedAnims; i++ )
	{											 
		SCutsceneAnimInfo* pAnimInfo = &m_bonedAnims[i];
	
		Dbg_MsgAssert( pAnimInfo->mp_bonedQuickAnim, ( "No quick anim to delete" ) );
		delete pAnimInfo->mp_bonedQuickAnim;
		pAnimInfo->mp_bonedQuickAnim = NULL;
	}

	for ( int i = 0; i < m_numObjects; i++ )
	{
// 		Don't want to mark 'em as dead, because the assets
//		contained in these dummy objects will be obliterated
//		very soon...  we don't want any dummy objects hanging
//		around without valid assets...
//		mp_object[i]->MarkAsDead();
		
		SCutsceneObjectInfo* pObjectInfo = &m_objects[i];
		
		Dbg_MsgAssert( pObjectInfo->mp_object, ( "No object to delete" ) );
		
		Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pObjectInfo->mp_object );
		Dbg_Assert( pModelComponent );
		
		if ( pObjectInfo->m_doPlayerSubstitution )
		{
			Dbg_Assert( pModelComponent->HasRefObject() );
			// put back the skater's model component,
			// if we changed the bounding sphere...
			uint32 refObjectName = pModelComponent->GetRefObjectName();
			Obj::CCompositeObject* pRefObject = (Obj::CCompositeObject*)Obj::ResolveToObject( refObjectName );
			Dbg_Assert( pRefObject );
			Obj::CModelComponent* pRefModelComponent = GetModelComponentFromObject( pRefObject );
			Dbg_Assert( pRefModelComponent );
			Nx::CModel* pRefModel = pRefModelComponent->GetModel();
			pRefModel->SetBoundingSphere( m_original_player_bounding_sphere );
			pRefModel->Hide( m_original_hidden );
			pRefModel->EnableScaling( m_original_scale_enabled );
			
			// send the object back to where it came from...
			// (we had to change it for the duration of the cutscene
			// in case the object was affected by a FakeLight)
			pRefObject->SetPos( m_original_pos );
		}

		delete pObjectInfo->mp_object;
		pObjectInfo->mp_object = NULL;
	}
	
	for ( int i = 0; i < m_numAssets; i++ )
	{
		// remove them from the assman
		// (do i need to make sure the objects are completely dead first?)
		Ass::CAssMan* pAssMan = Ass::CAssMan::Instance();
		Ass::CAsset* pAsset = pAssMan->GetAssetNode( m_assetName[i], true );
		pAssMan->UnloadAsset( pAsset );
		m_assetName[i] = NULL;
		
		// TODO:  Need to handle anims slightly
		// if we use references...  fortunately,
		// we're not using references right now
	}

	// stop the stream (may not need this, unless we abort)
	Pcm::StopMusic();
	
	Script::RunScript( CRCD(0x7216d8da,"CutsceneKillObjects") );

	Script::UnloadQB( CRCD(0xa1408e6e,"cutscenefrommemory.qb") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneData::OverridesCamera()
{
	return LoadFinished() && m_audioStarted;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector get_scale( Script::CStruct* pAppearance, uint32 bone_group_name )
{
	Script::CStruct* pVirtualDescStruct = NULL;

	if ( pAppearance->GetStructure( bone_group_name, &pVirtualDescStruct, Script::NO_ASSERT ) )
	{
		int use_default_scale = 1;
		pVirtualDescStruct->GetInteger( CRCD(0x5a96985d,"use_default_scale"), &use_default_scale, Script::NO_ASSERT );
		
		if ( !use_default_scale )
		{
			int x, y, z;
			pVirtualDescStruct->GetInteger( CRCD(0x7323e97c,"x"), &x, Script::ASSERT );
			pVirtualDescStruct->GetInteger( CRCD(0x0424d9ea,"y"), &y, Script::ASSERT );
			pVirtualDescStruct->GetInteger( CRCD(0x9d2d8850,"z"), &z, Script::ASSERT );
			return Mth::Vector( ((float)x)/100.0f, ((float)y)/100.0f, ((float)z)/100.0f, 1.0f ); 
		}
		else 
		{
			// use_default_scale parameter
			return Mth::Vector( 1.0f, 1.0f, 1.0f, 1.0f );
		}
	}
	else
	{
		// part not found
		return Mth::Vector( 1.0f, 1.0f, 1.0f, 1.0f );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneData::load_models()
{
	Dbg_MsgAssert( mp_fileLibrary, ( "No file library?!?" ) );

	int numFiles = mp_fileLibrary->GetNumFiles();
	for ( int i = 0; i < numFiles; i++ )
	{
		const File::SFileInfo* pFileInfo = mp_fileLibrary->GetFileInfo( i );

#ifdef __PLAT_NGC__
		uint32* pData = NULL;
		switch ( pFileInfo->fileExtensionChecksum )
		{
			case 0x5ac14717:	// CIF
			case 0x1512808d:	// TEX
			case 0xffc529f4:	// CAS
			case 0x2cd4107d:	// WGT
				break;
			default:
				pData = mp_fileLibrary->GetFileData( i );
				break;
		}
#else
		uint32* pData = mp_fileLibrary->GetFileData( i );
#endif		// __PLAT_NGC__
			
		// read each file one by one, and do something different based on the file type
		switch ( pFileInfo->fileExtensionChecksum )
		{
			case 0xeab51346:	// SKA
				add_boned_anim( pFileInfo->fileNameChecksum, pData, pFileInfo->fileSize );
				mp_fileLibrary->ClearFile( pFileInfo->fileNameChecksum, pFileInfo->fileExtensionChecksum );				
				break;
		
			case 0x2e4bf21b:	// OBA
			{
				Dbg_MsgAssert( !mp_objectQuickAnim, ( "Cutscene file should have exactly 1 object anim\n" ) );
				add_anim( CRCD(0x3b423a24,"cutscene_oba"), pData, pFileInfo->fileSize );
				mp_objectQuickAnim = new Nx::CQuickAnim;
				mp_objectQuickAnim->SetAnimAssetName( CRCD(0x3b423a24,"cutscene_oba") );
				mp_objectQuickAnim->ResetCustomKeys();
//				m_objectAnimController.PlaySequence( 0, 0.0f, mp_objectAnimData->GetDuration(), Gfx::LOOPING_HOLD, 0.0f, 1.0f );
				mp_fileLibrary->ClearFile( pFileInfo->fileNameChecksum, pFileInfo->fileExtensionChecksum );				
			}
			break;
		
			case 0x05ca1497:	// CAM
			{
				// this presumes that there's only one cutscene_cam asset at a time,
				// which may not be true if we're queueing up multiple cutscenes
				// in this case, we'd need to run an init function before each
				// cutscene is played, not in the cutscene's InitFromStructure,
				// which gets called at the time the cutscene gets added to the list...
				add_anim( CRCD(0x10c3dca8,"cutscene_cam"), pData, pFileInfo->fileSize );
				Dbg_MsgAssert( !mp_cameraQuickAnim, ( "Cutscene file should have exactly 1 Camera\n" ) );
				mp_cameraQuickAnim = new Nx::CQuickAnim;
				mp_cameraQuickAnim->SetAnimAssetName( CRCD(0x10c3dca8,"cutscene_cam") );
				mp_cameraQuickAnim->ResetCustomKeys();
//				m_cameraController.PlaySequence( 0, 0.0f, mp_cameraData->GetDuration(), Gfx::LOOPING_HOLD, 0.0f, 1.0f );
				mp_fileLibrary->ClearFile( pFileInfo->fileNameChecksum, pFileInfo->fileExtensionChecksum );				
			}
			break;
			
			case 0x2bbea5c3:	// QB
			{
				Script::LoadQBFromMemory( "cutscenefrommemory.qb", (uint8*)pData, Script::ASSERT_IF_DUPLICATE_SYMBOLS );
				mp_fileLibrary->ClearFile( pFileInfo->fileNameChecksum, pFileInfo->fileExtensionChecksum );
			}
			break;

			case 0xe7308c1f:	// GEOM
			case 0xfd8697e1:	// SKIN
			case 0x0524fd4e:	// MDL
			{
				char model_asset_filename[512];
				sprintf( model_asset_filename, "%08x", pFileInfo->fileNameChecksum + pFileInfo->fileExtensionChecksum );
				uint32 model_asset_name = pFileInfo->fileNameChecksum + CRCD(0xfd8697e1,"SKIN");// pFileInfo->fileExtensionChecksum;
//				Dbg_Message( "Adding model asset %08x", model_asset_name );
						
				Ass::SCutsceneModelDataInfo theInfo;
				theInfo.modelChecksum = model_asset_name;
				theInfo.pModelData = pData;
				theInfo.modelDataSize = pFileInfo->fileSize;
				theInfo.pTextureData = mp_fileLibrary->GetFileData( pFileInfo->fileNameChecksum, CRCD(0x1512808d,"TEX"), true );
				const File::SFileInfo* pTextureFileInfo;
				pTextureFileInfo = mp_fileLibrary->GetFileInfo( pFileInfo->fileNameChecksum, CRCD(0x1512808d,"TEX"), true );
				Dbg_Assert( pTextureFileInfo );
				theInfo.textureDataSize = pTextureFileInfo->fileSize;
					
				// CAS data isn't always required;  only for certain SKIN files
				theInfo.pCASData = (uint8*)mp_fileLibrary->GetFileData( pFileInfo->fileNameChecksum, CRCD(0xffc529f4,"CAS"), false );;

				theInfo.isSkin = pFileInfo->fileExtensionChecksum == CRCD(0xfd8697e1,"SKIN");		
				theInfo.texDictChecksum = pTextureFileInfo->fileNameChecksum + pTextureFileInfo->fileExtensionChecksum;
				
				// TODO:  Should be true if this is the skater model...
				theInfo.doShadowVolume = false;	

				// GJ:  make sure the texture dictionary offset doesn't clash with skaters
				// there must be a cleaner way to do this...  there must!
				if ( pFileInfo->fileExtensionChecksum == CRCD(0xe7308c1f,"GEOM") )
				{
					// geoms shouldn't specify tex dict offset, because
					// non-zero tex dict offsets will try to load themselves
					// up as a MDL, rather than as a GEOM
					theInfo.texDictOffset = 0;
				}
				else
				{
					theInfo.texDictOffset = vCUTSCENE_TEXDICT_RANGE + i;
				}

				// set up some default parameters
				bool permanent = false;
				int group = 0;

				// these file types get added to the asset manager
				// (if they don't already exist)...  remember to remove
				// them when the cutscene is done!
				Ass::CAssMan* pAssMan = Ass::CAssMan::Instance();

				// by default, load the asset...  the cutscene heads
				// go through an additional piece of code to figure out
				// whether they will actually be needed during the cutscene
				bool should_load_asset = true;

				// used for cutscene head scaling
				uint8* pWeightMapData = (uint8*)mp_fileLibrary->GetFileData( pFileInfo->fileNameChecksum, CRCD(0x2cd4107d,"WGT"), false );
				if ( pWeightMapData )
				{
					Nx::SMeshScalingParameters theParams;
					
#ifdef __NOPT_ASSERT__
					if ( Script::GetInteger( CRCD(0xf28e1301,"CutsceneScaleTest"), Script::NO_ASSERT ) )
					{
						Script::CVector* pVec = NULL;
						pVec = Script::GetVector( CRCD(0x513a55f6,"CutsceneScaleTestNeck") );
						testBoneScales[0] = Mth::Vector( pVec->mX, pVec->mY, pVec->mZ, 1.0f );
						pVec = Script::GetVector( CRCD(0xd6d87539,"CutsceneScaleTestHead") );
						testBoneScales[1] = Mth::Vector( pVec->mX, pVec->mY, pVec->mZ, 1.0f );
						pVec = Script::GetVector( CRCD(0x9dea75ff,"CutsceneScaleTestJaw") );
						testBoneScales[2] = Mth::Vector( pVec->mX, pVec->mY, pVec->mZ, 1.0f );
						pVec = Script::GetVector( CRCD(0x0767f02b,"CutsceneScaleTestHat") );
						testBoneScales[3] = Mth::Vector( pVec->mX, pVec->mY, pVec->mZ, 1.0f );
						pVec = Script::GetVector( CRCD(0xba314cf8,"CutsceneScaleTestBrow") );
						testBoneScales[4] = Mth::Vector( pVec->mX, pVec->mY, pVec->mZ, 1.0f );
					}
					else
#endif
					{
						// get it from the profile
						Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( 0 );
						Gfx::CModelAppearance* pCASModelAppearance = pSkaterProfile->GetAppearance();
						Script::CStruct* pCASModelStruct = pCASModelAppearance->GetStructure();

						testBoneScales[0] = get_scale( pCASModelStruct, CRCD(0xa28fab7f,"head_bone_group") );
						testBoneScales[1] = get_scale( pCASModelStruct, CRCD(0xa28fab7f,"head_bone_group") );
						testBoneScales[2] = get_scale( pCASModelStruct, CRCD(0x0442d3c1,"jaw_bone_group") );
						testBoneScales[3] = get_scale( pCASModelStruct, CRCD(0xb90f08d0,"headtop_bone_group") );
						testBoneScales[4] = get_scale( pCASModelStruct, CRCD(0x1dbcfae8,"nose_bone_group") );
					}

					// skip past version number
					pWeightMapData += sizeof(int); 

					int numVerts = *(int*)pWeightMapData;
					pWeightMapData += sizeof(int);

	    			theParams.pWeights = (float*)pWeightMapData;
					theParams.pWeightIndices = (char*)(pWeightMapData + (numVerts * 3 * sizeof(float)));
					
					//theParams.pWeights = &testWeights[0];
					//theParams.pWeightIndices = &testWeightIndices[0];
					theParams.pBonePositions = &testBonePositions[0];
					theParams.pBoneScales = &testBoneScales[0];

					// it's a cutscene head, so only load the model if it's the head that 
					// we're going to need in the cutscene...  the 4 cutscene heads are always
					// ordered male/nonfacemapped, male/facemapped, female/nonfacemapped, 
					// female/facemapped, so we should be able to figure out which head
					// is which depending on what other files exist...
					int is_male_head = ( mp_fileLibrary->GetFileInfo( pFileInfo->fileNameChecksum + 2, CRCD(0xfd8697e1,"SKIN"), false ) != NULL );
					int is_facemapped_head;
					if ( is_male_head )
					{
						is_facemapped_head = ( mp_fileLibrary->GetFileInfo( pFileInfo->fileNameChecksum - 1, CRCD(0xfd8697e1,"SKIN"), false ) != NULL );
					}
					else
					{
						is_facemapped_head = ( mp_fileLibrary->GetFileInfo( pFileInfo->fileNameChecksum + 1, CRCD(0xfd8697e1,"SKIN"), false ) == NULL );
					}

					int is_male_skater;
					int is_facemapped_skater;
					Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( 0 );
					Gfx::CModelAppearance* pCASModelAppearance = pSkaterProfile->GetAppearance();
					Gfx::CFaceTexture* pCASFaceTexture = pCASModelAppearance->GetFaceTexture();
					is_facemapped_skater = ( pCASFaceTexture && pCASFaceTexture->IsValid() );					
					Script::CStruct* pInfo = pSkaterProfile->GetInfo();
					pInfo->GetInteger( CRCD(0x3f813177,"is_male"), &is_male_skater, Script::ASSERT );

					// only load the correct head from the 4 cutscene heads
					should_load_asset = ( ( is_facemapped_head == is_facemapped_skater ) && ( is_male_head == is_male_skater) );
				
					if ( should_load_asset )
					{
						// only want to set the parameters if we're sure we're going to be loading the asset...
						Nx::CEngine::sSetMeshScalingParameters( &theParams );
					}
				}

				if ( should_load_asset )
				{
					pAssMan->LoadAssetFromStream( model_asset_name, 
												  Ass::ASSET_SKIN, 
												  (uint32*)&theInfo, 
												  sizeof(Ass::SCutsceneModelDataInfo), 
												  permanent,
												  group);

					// keep track of which assets are loaded, 
					// so that we know to remove it in the destructor...
					Dbg_MsgAssert( m_numAssets < vMAX_CUTSCENE_ASSETS, ( "Too many assets (increase vMAX_CUTSCENE_ASSETS > %d)", vMAX_CUTSCENE_ASSETS ) );
					m_assetName[m_numAssets] = model_asset_name;
					m_numAssets++;
				}
				
				mp_fileLibrary->ClearFile( pFileInfo->fileNameChecksum, pFileInfo->fileExtensionChecksum );
				mp_fileLibrary->ClearFile( pFileInfo->fileNameChecksum, CRCD(0x1512808d,"TEX") );
				mp_fileLibrary->ClearFile( pFileInfo->fileNameChecksum, CRCD(0xffc529f4,"CAS") );
				mp_fileLibrary->ClearFile( pFileInfo->fileNameChecksum, CRCD(0x2cd4107d,"WGT") );
		  }
			break;
/*
			case 0xedd8d75f:	// SKE
				{
					char skeleton_asset_filename[512];
					sprintf( skeleton_asset_filename, "%08x", pFileInfo->fileNameChecksum + pFileInfo->fileExtensionChecksum );
					uint32 skeleton_asset_name = pFileInfo->fileNameChecksum + pFileInfo->fileExtensionChecksum;
					Dbg_Message( "Adding asset %08x", skeleton_asset_name );
						
					// set up some default parameters
					bool permanent = false;
					int group = 0;
					 
					// these file types get added to the asset manager
					// (if they don't already exist)...  remember to remove
					// them when the cutscene is done!
					Ass::CAssMan* pAssMan = Ass::CAssMan::Instance();
					pAssMan->LoadAssetFromStream( skeleton_asset_name, 
												  Ass::ASSET_SKELETON, 
												  (uint32*)pData, 
												  pFileInfo->fileSize, 
												  permanent,
												  group);

					// keep track of which assets are loaded, 
					// so that we know to remove it in the destructor...
					Dbg_MsgAssert( m_numAssets < vMAX_CUTSCENE_ASSETS, ( "Too many assets (increase vMAX_CUTSCENE_ASSETS > %d)", vMAX_CUTSCENE_ASSETS ) );
					m_assetName[m_numAssets] = skeleton_asset_name;
					m_numAssets++;
				}
				break;
*/

			case 0x5ac14717:	// CIF
				// do nothing;  cif packet is handled during a separate step
				break;

			case 0x1512808d:	// TEX
				// do nothing;  loading of texture dictionaries is handled
				// automatically by the loading of models/skins
				break;

			case 0xffc529f4:	// CAS
				// do nothing;  loading of CAS flags are handled
				// automatically by the loading of models/skins
				break;

			case 0x2cd4107d:	// WGT
				// do nothing;  loading of mesh scaling weight maps are handled
				// automatically by the loading of models/skins
				break;
			
			default:
				// file type ignored on this pass
				break;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneData::init_objects()
{
	// now create the objects once the assets have been loaded
	uint32* pCIFData = mp_fileLibrary->GetFileData( 0, CRCD(0x5ac14717,"CIF"), true );
	Dbg_Assert( pCIFData );
	create_objects( pCIFData );
	mp_fileLibrary->ClearFile( 0, CRCD(0x5ac14717,"CIF") );

#ifdef __NOPT_ASSERT__
	/*
	can't do this test any more, because we're clearing out unused data
	
	// check for valid skeleton, should be moved to the exporter
	for ( int i = 0; i < m_numObjects; i++ )
	{
		SCutsceneObjectInfo* pObjectInfo = &m_objects[i];
		
		if ( pObjectInfo->m_doPlayerSubstitution )
		{
			// no checks
		}
		else if ( pObjectInfo->m_skeletonName == 0 )
		{
			// if the object has no skeleton, make sure that this is an MDL, and not a SKIN
			if ( mp_fileLibrary->GetFileData( pObjectInfo->m_objectName, Crc::GenerateCRCFromString("SKIN"), false ) )
			{
				Dbg_MsgAssert( 0, ( "No skeleton specified for object %08x, even though it was listed as a SKIN file", pObjectInfo->m_objectName ) );
			}
		}
		else
		{
			if ( !mp_fileLibrary->GetFileData( pObjectInfo->m_objectName, Crc::GenerateCRCFromString("SKIN"), false ) )
			{
				Dbg_MsgAssert( 0, ( "Skeleton specified for object %08x, even though it was not listed as a SKIN file", pObjectInfo->m_objectName ) );
			}
		}
	}
	*/
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneData::PostLoad( bool assertOnFail )
{
	Dbg_MsgAssert( mp_fileLibrary, ( "No file library?!?" ) );

	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().CutsceneBottomUpHeap() );

	load_models();
	init_objects();
	
	Mem::Manager::sHandle().PopContext();

	delete mp_fileLibrary;
	mp_fileLibrary = NULL;

	//-------------------------------------------------------------
	// There's a blue glitch that appears for a few frames while
	// the cutscene manager waits for the stream to be preloaded.
	// The following loop will ensure that the stream is ready
	// to go before the video starts...

#ifndef __PLAT_NGC__
	while ( !m_audioStarted )
	{
		Pcm::Update();
		
		if ( Pcm::PreLoadMusicStreamDone() )
		{
			// TODO:  Fill in with appropriate music volume parameter?
			Pcm::StartPreLoadedMusicStream();

			m_audioStarted = true;
			break;
		}

		Tmr::VSync();
	}
#endif		// __PLAT_NGC__
	//-------------------------------------------------------------
	
	return true;
}
			   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneData::add_anim( uint32 animAssetName, uint32* pData, int fileSize )
{
	// set up some default parameters
	bool permanent = false;
	int group = 0;
	uint32 anim_asset_name = animAssetName;
					 
	// these file types get added to the asset manager
	// (if they don't already exist)...  remember to remove
	// them when the cutscene is done!
	Ass::CAssMan* pAssMan = Ass::CAssMan::Instance();
	pAssMan->LoadAssetFromStream( anim_asset_name, 
								  Ass::ASSET_ANIM, 
								  pData,
								  fileSize,
								  permanent,
								  group);

	// keep track of which assets are loaded, 
	// so that we know to remove it in the destructor...
	Dbg_MsgAssert( m_numAssets < vMAX_CUTSCENE_ASSETS, ( "Too many assets (increase vMAX_CUTSCENE_ASSETS > %d)", vMAX_CUTSCENE_ASSETS ) );
	m_assetName[m_numAssets] = anim_asset_name;
	m_numAssets++;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneData::add_boned_anim( uint32 animName, uint32* pData, int fileSize )
{
	Dbg_MsgAssert( m_numBonedAnims < vMAX_CUTSCENE_ANIMS, ( "Too many anims in cutscene (increase vMAX_CUTSCENE_ANIMS > %d).", vMAX_CUTSCENE_ANIMS ) );

	SCutsceneAnimInfo* pAnimInfo = &m_bonedAnims[m_numBonedAnims];

	Dbg_MsgAssert( !get_anim_info( animName ), ( "Anim already exists %s", Script::FindChecksumName(animName) ) );

	add_anim( animName, pData, fileSize );

#ifdef __PLAT_NGC__
	int size = sizeof( Nx::CQuickAnim );
	int mem_available;
	bool need_to_pop = false;
	if ( g_in_cutscene )
	{
		Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
		mem_available = Mem::Manager::sHandle().Available();
		if ( size < ( mem_available - ( 40 * 1024 ) ) )
		{
			need_to_pop = true;
		}
		else
		{
			Mem::Manager::sHandle().PopContext();
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
			mem_available = Mem::Manager::sHandle().Available();
			if ( size < ( mem_available - ( 5 * 1024 ) ) )
			{
				need_to_pop = true;
			}
			else
			{
				Mem::Manager::sHandle().PopContext();
				Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ScriptHeap() );
				mem_available = Mem::Manager::sHandle().Available();
				if ( size < ( mem_available - ( 40 * 1024 ) ) )
				{
					need_to_pop = true;
				}
				else
				{
					Mem::Manager::sHandle().PopContext();
				}
			}
		}
	}
#endif	// __PLAT_NGC__

	Nx::CQuickAnim* pBonedQuickAnim = new Nx::CQuickAnim;

#ifdef __PLAT_NGC__
	if ( need_to_pop )
	{
		Mem::Manager::sHandle().PopContext();
	}
#endif	// __PLAT_NGC__

	pBonedQuickAnim->SetAnimAssetName( animName );
	pAnimInfo->mp_bonedQuickAnim = pBonedQuickAnim;
	
	pAnimInfo->m_animName = animName;

//	Dbg_Message( "Boned anim 0x%08x has %d bones", animName, pBonedQuickAnim->GetNumBones() );

	m_numBonedAnims++;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SCutsceneObjectInfo* CCutsceneData::get_object_info( uint32 objectName )
{
	for ( int i = 0; i < m_numObjects; i++ )
	{
		if ( m_objects[i].m_objectName == objectName )
		{
			return &m_objects[i];
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

SCutsceneAnimInfo* CCutsceneData::get_anim_info( uint32 animName )
{
	for ( int i = 0; i < m_numBonedAnims; i++ )
	{
		if ( m_bonedAnims[i].m_animName == animName )
		{
			return &m_bonedAnims[i];
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Obj::CCompositeObject* create_cutscene_dummy_object( Script::CStruct* pNodeData )
{  
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	CGeneralManager* p_obj_man = skate_mod->GetObjectManager();

	Obj::CCompositeObject* pDummyObject = new CCompositeObject;
	
	pDummyObject->SetType( SKATE_TYPE_COMPOSITE );
	
	p_obj_man->RegisterObject(*pDummyObject);
	
	Script::RunScript( CRCD(0x1f29c5a5,"cutsceneobj_add_components"), pNodeData, pDummyObject );
	
	pDummyObject->Finalize();

	pDummyObject->m_pos = Mth::Vector( 0.0f, 0.0f, 0.0f, 1.0f );

	return pDummyObject;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void apply_cas_scaling( Obj::CCompositeObject* pObject, Gfx::CSkeleton* pSourceSkeleton )
{
	Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( pObject );

	if ( pSkeletonComponent )
	{
		Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pObject );
		if ( pModelComponent->HasRefObject() )
		{
			Gfx::CSkeleton* pSkeleton = pSkeletonComponent->GetSkeleton();
			Dbg_Assert( pSkeleton );

			pSkeleton->CopyBoneScale( pSourceSkeleton );
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneData::create_objects( uint32* pData )
{
	// skip the version number
	pData++;

	// get the number of objects
	int numObjects = *pData;
	pData++;

	Dbg_Message("Found %d objects in the CIF file", numObjects);

	for ( int i = 0; i < numObjects; i++ )
	{
//		Dbg_Message( "Adding cutscene object #%d:", i );
		
		// remember the objectName
		uint32 objectName = *pData;
//		Dbg_Message( "objectName = %08x", *pData );
		pData++;

		// get the model name
		uint32 modelName = *pData;
		uint32 modelAssetName;
//		Dbg_Message( "modelName = %08x", *pData );
		modelAssetName = *pData + Script::GenerateCRC("SKIN");
//		Dbg_Message( "munged modelAssetName = %08x", modelAssetName );
		pData++;

		// get the skeleton name
		uint32 skeletonName;
		skeletonName = *pData;
		pData++;

		// get the anim name (the associated SKA file
		// which should be found somewhere in this CUT file)
		uint32 animName;
		animName = *pData;
		pData++;

		// get any extra flags
		uint32 flags;
		flags = *pData;
		pData++;

		Script::CStruct* pNodeData = new Script::CStruct;
										
		// This substitutes the	given model with the current player model
		// TODO:  Eventually phase out the name "skater"
		bool do_player_substitution = ( modelName == CRCD(0x5b8ab877,"skater") || modelName == CRCD(0x67e6859a,"player") );
		if ( do_player_substitution )
		{
			uint32 refObjectName = 0;
			pNodeData->AddChecksum( CRCD(0x153a84de,"refObjectName"), refObjectName );
		}
		else
		{
			// if it's not a skater model, then check for correct skeleton/anims
			if ( !mp_fileLibrary->FileExists( modelName, CRCD(0xfd8697e1,"SKIN") ) )
			{
//				Dbg_MsgAssert( skeletonName == 0, ( "Non-skinned model %08x has skeleton name", objectName ) );
				skeletonName = 0;
//				Dbg_MsgAssert( animName == 0, ( "Non-skinned model %08x has anim name", objectName ) );
				animName = 0;
			}
			else
			{
				Dbg_MsgAssert( skeletonName != 0, ( "Skinned model %08x has no skeleton name", objectName ) );
				Dbg_MsgAssert( animName != 0, ( "Skinned model %08x has no anim name", objectName ) );
			}

			// if it's the skater's head model and the player has a face texture,
			// then we need to swap in the eyeball-less head model...
			bool is_head_model = flags & vCUTSCENEOBJECTFLAGS_ISHEAD;
			bool is_skater_model = flags & vCUTSCENEOBJECTFLAGS_ISSKATER;
			if ( is_head_model && is_skater_model )
			{
				Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( 0 );
				Gfx::CModelAppearance* pCASModelAppearance = pSkaterProfile->GetAppearance();
				Gfx::CFaceTexture* pCASFaceTexture = pCASModelAppearance->GetFaceTexture();
				if ( pCASFaceTexture && pCASFaceTexture->IsValid() )
				{
					Dbg_MsgAssert( mp_fileLibrary->FileExists( modelName + 1, CRCD(0xfd8697e1,"SKIN") ), ( "Couldn't find eyeball-less head model" ) );

					// eyeball-less head model checksum = regular head model checksum + 1
					modelAssetName += 1;
				}

				// Load up the correct head, based on the skater's sex
				Script::CStruct* pInfo = pSkaterProfile->GetInfo();
				int is_male = 0;
				pInfo->GetInteger( CRCD(0x3f813177,"is_male"), &is_male, Script::ASSERT );
				if ( !is_male )
				{
					// female eyeball = male eyeball + 2
					// female eyeball-less = male eyeball-less + 2
					modelAssetName += 2;
				}

				// this will allow changing facial hair color (needed only for males)
				pNodeData->AddInteger( CRCD(0x92b43e79,"multicolor"), is_male );
			}
			
			pNodeData->AddChecksum( CRCD(0x86bd5b8f,"assetName"), modelAssetName );
		}

		// if you've got the special-case skull head,
		// then we don't want to subsitute the hi-res skeleton
		{
			bool is_head_model = flags & vCUTSCENEOBJECTFLAGS_ISHEAD;
			bool is_skater_model = flags & vCUTSCENEOBJECTFLAGS_ISSKATER;
			if ( is_head_model && is_skater_model )
			{
				Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( 0 );
				Gfx::CModelAppearance* pCASModelAppearance = pSkaterProfile->GetAppearance();

				Script::CStruct* pHeadStruct = NULL;
				pHeadStruct = pCASModelAppearance->GetActualDescStructure( CRCD(0x650fab6d,"skater_m_head") );
				if ( !pHeadStruct )
				{
					pHeadStruct = pCASModelAppearance->GetActualDescStructure( CRCD(0x0fc85bae,"skater_f_head") );
				}
				if ( pHeadStruct && pHeadStruct->ContainsFlag( CRCD(0xc4c5b2cc,"NoCutsceneHead") ) )
				{
					Script::RunScript( CRCD(0xf37d9f53,"UnhideLoResHeads") );
					delete pNodeData;
					continue;
				}

				// another special case for the paper bag...
				Script::CStruct* pHatStruct = NULL;
				pHatStruct = pCASModelAppearance->GetActualDescStructure( CRCD(0x6df453b6,"hat") );
				if ( pHatStruct && pHatStruct->ContainsFlag( CRCD(0xc4c5b2cc,"NoCutsceneHead") ) )
				{
					Script::RunScript( CRCD(0xf37d9f53,"UnhideLoResHeads") );
					delete pNodeData;
					continue;
				}
#if 0				
				Script::RunScript( CRCD(0xf37d9f53,"UnhideLoResHeads") );
				delete pNodeData;
				continue;
#endif
			}
		}

//		Dbg_Message( "skeletonName = %08x", skeletonName );
//		Dbg_Message( "animName = %08x", animName );
		
		// TODO:  if the skeleton asset were included
		// in the CUT file, then we wouldn't need the
		// artists to fill in this field manually:	
		if ( skeletonName )
		{
			pNodeData->AddChecksum( CRCD(0x09794932,"skeletonName"), skeletonName );

			// this disables the "bone skip list" optimization,
			// which would otherwise make the cutscene object's
			// hands disappear
			pNodeData->AddInteger( CRCD(0xd3982061,"max_bone_skip_lod"), 0 );
		}

		// shouldn't have an animation component, because
		// we want the cutscene details to fill up the skeleton
		// with the appropriate data (we don't want the
		// animation component's update() function to get
		// called once per frame and wipe out the cutscene
		// details' changes
		//pNodeData->AddChecksum( CRCD(0x6c2bfb7f,"animName"), anim_name );

		SCutsceneObjectInfo* pObjectInfo = &m_objects[m_numObjects];

		Dbg_MsgAssert( m_numObjects < vMAX_CUTSCENE_OBJECTS, ( "Too many objects in cutscene (increase vMAX_CUTSCENE_OBJECTS > %d).", vMAX_CUTSCENE_OBJECTS ) );
		pObjectInfo->mp_object = create_cutscene_dummy_object( pNodeData ); 

		// this	prevents the skateboard from clipping out
		// when it gets far enough away from the skater
		// (like in the NJ pool cutscene) 
		Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pObjectInfo->mp_object );
		if ( pModelComponent )
		{
			Nx::CModel* pModel = pModelComponent->GetModel();
			Dbg_Assert( pModel );
			pModel->SetBoundingSphere( Mth::Vector(0.0f,0.0f,0.0f,50000.0f) );

	//		printf( "pModel has %d bones, transforms = %p\n", pModel->GetNumBones(), pModel->GetBoneTransforms() );

			if ( pModelComponent->HasRefObject() )
			{
				uint32 refObjectName = pModelComponent->GetRefObjectName();
				Obj::CCompositeObject* pRefObject = (Obj::CCompositeObject*)Obj::ResolveToObject( refObjectName );
				Dbg_Assert( pRefObject );
				Obj::CModelComponent* pRefModelComponent = GetModelComponentFromObject( pRefObject );
				Dbg_Assert( pRefModelComponent );
				Nx::CModel* pRefModel = pRefModelComponent->GetModel();
				
				// "m_original_params_set" ensures that we don't try
				// to write the same data out multiple times if there
				// are multiple create-a-skaters (which happens in the
				// final.cut cutscene)
				if ( !m_original_params_set )
				{
					m_original_player_bounding_sphere = pRefModel->GetBoundingSphere();
					m_original_hidden = pRefModel->IsHidden();
					m_original_scale_enabled = pRefModel->IsScalingEnabled();
					m_original_pos = pRefObject->GetPos();
					m_original_params_set = true;
				}
				
				pRefModel->SetBoundingSphere( Mth::Vector(0.0f,0.0f,0.0f,50000.0f) );
				pRefModel->Hide( false );
				pRefModel->EnableScaling( false );

				pRefModelComponent->UpdateBrightness();
			}

			// don't run the normal update() function,
			// or else it will screw up the double-buffer
			pModelComponent->Suspend(true);
		}

		pObjectInfo->m_objectName = objectName;

		// set the id, so that we can run scripts on it
		Dbg_MsgAssert( !Obj::ResolveToObject(objectName), ( "Object with name %s already exists", Script::FindChecksumName(objectName) ) );
		pObjectInfo->mp_object->SetID( objectName );

		// for debugging...
		pObjectInfo->m_skeletonName = skeletonName;
		pObjectInfo->m_doPlayerSubstitution = do_player_substitution;
		pObjectInfo->m_flags = flags;

		pObjectInfo->mp_animInfo = NULL;
		pObjectInfo->mp_parentObject = NULL;
		pObjectInfo->m_obaIndex = -1;

		// the animation doesn't necessarily exist for all the objects
		if ( animName )
		{
			// enforce that the anim with that name exists,
			pObjectInfo->mp_animInfo = get_anim_info( animName );
			Dbg_MsgAssert( pObjectInfo->mp_animInfo, ( "Couldn't find SKA with id 0x%08x in LIB file", animName ) );
		}
		
//		Dbg_Message( "---------------" );

		m_numObjects++;

		// done with temp struct
		delete pNodeData;
	}

	// apply cas scaling to the appropriate parts
	for ( int i = 0; i < m_numObjects; i++ )
	{
		SCutsceneObjectInfo* pObjectInfo = &m_objects[i];

		if ( pObjectInfo->m_doPlayerSubstitution )
		{
			Obj::CModelComponent* pModelComponent = GetModelComponentFromObject(pObjectInfo->mp_object);
			Dbg_Assert( pModelComponent );
			
			uint32 refObjectName = pModelComponent->GetRefObjectName();
			Obj::CCompositeObject* pRefObject = (Obj::CCompositeObject*)Obj::ResolveToObject( refObjectName );
			Dbg_Assert( pRefObject );
			
			// body
			Obj::CSkeletonComponent* pRefSkeletonComponent = GetSkeletonComponentFromObject(pRefObject);
			Dbg_Assert( pRefSkeletonComponent );
			apply_cas_scaling( pObjectInfo->mp_object, pRefSkeletonComponent->GetSkeleton() );
		}
		
		SCutsceneObjectInfo* pParentObjectInfo = get_object_info( pObjectInfo->m_objectName - 1 );
		if ( pParentObjectInfo && pParentObjectInfo->m_doPlayerSubstitution )
		{
			Obj::CModelComponent* pModelComponent = GetModelComponentFromObject(pParentObjectInfo->mp_object);
			Dbg_Assert( pModelComponent );
			
			uint32 refObjectName = pModelComponent->GetRefObjectName();
			Obj::CCompositeObject* pRefObject = (Obj::CCompositeObject*)Obj::ResolveToObject( refObjectName );
			Dbg_Assert( pRefObject );
			
			// head
			Obj::CSkeletonComponent* pRefSkeletonComponent = GetSkeletonComponentFromObject(pRefObject);
			Dbg_Assert( pRefSkeletonComponent );
			apply_cas_scaling( pObjectInfo->mp_object, pRefSkeletonComponent->GetSkeleton() );
		}
	}

	// do basic poly removal on all objects
	// w/ some extra poly removal on the player's head
	for ( int i = 0; i < m_numObjects; i++ )
	{
		SCutsceneObjectInfo* pObjectInfo = &m_objects[i];

		Obj::CModelComponent* pModelComponent = GetModelComponentFromObject(pObjectInfo->mp_object);
		if ( pModelComponent )
		{
			Nx::CModel* pModel = pModelComponent->GetModel();

			uint32 polyRemovalMask = pModel->GetPolyRemovalMask();

			SCutsceneObjectInfo* pParentObjectInfo = get_object_info( pObjectInfo->m_objectName - 1 );
			if ( pParentObjectInfo && pParentObjectInfo->m_doPlayerSubstitution )
			{
				// treat the player's head as a special case
				// because we need to grab the poly removal
				// mask from the player's body

				Obj::CModelComponent* pModelComponent = GetModelComponentFromObject(pParentObjectInfo->mp_object);
				Dbg_Assert( pModelComponent );

				uint32 refObjectName = pModelComponent->GetRefObjectName();
				Obj::CCompositeObject* pRefObject = (Obj::CCompositeObject*)Obj::ResolveToObject( refObjectName );
				Dbg_Assert( pRefObject );

				Obj::CModelComponent* pRefModelComponent = GetModelComponentFromObject(pRefObject);
				Dbg_Assert( pRefModelComponent );

				Nx::CModel* pRefModel = pRefModelComponent->GetModel();
				Dbg_Assert( pRefModel );

				// head
				polyRemovalMask |= pRefModel->GetPolyRemovalMask();
			}

			pModel->HidePolys( polyRemovalMask );
		}
	}

	// relink parents if necessary
	for ( int i = 0; i < m_numObjects; i++ )
	{
		SCutsceneObjectInfo* pObjectInfo = &m_objects[i];

		// checks for parents, if any...  based on naming convention
		SCutsceneObjectInfo* pParentObjectInfo = get_object_info( pObjectInfo->m_objectName - 1 );
		if ( pParentObjectInfo )
		{
			pObjectInfo->mp_parentObject = pParentObjectInfo->mp_object;

			if ( pParentObjectInfo->m_doPlayerSubstitution )
			{
				// it's the hires create-a-skater head,
				// so need to do some extra texture	replacement on it
				Obj::CModelComponent* pCASModelComponent = GetModelComponentFromObject(pObjectInfo->mp_object);
				Dbg_Assert( pCASModelComponent );
				
				Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( 0 );
				Gfx::CModelAppearance* pCASModelAppearance = pSkaterProfile->GetAppearance();

				pCASModelComponent->InitModelFromProfile(pCASModelAppearance,false,0,CRCD(0x8fb01036,"replace_cutscene_skater_from_appearance"));
			
				// apply color from the skater appearance
				// (we can't do this in replace_cutscene_skater_from_appearance
				// because it might have an object color to set, which you can
				// only do if you have the correct geom name)
				Script::CStruct* pHeadStruct = NULL;

				pCASModelAppearance->PrintContents();

				pHeadStruct = pCASModelAppearance->GetVirtualDescStructure( CRCD(0x650fab6d,"skater_m_head") );
				
				if ( !pHeadStruct )
				{
					pHeadStruct = pCASModelAppearance->GetVirtualDescStructure( CRCD(0x0fc85bae,"skater_f_head") );
				}
				
				if ( pHeadStruct )
				{
					Nx::CModel* pCASModel = pCASModelComponent->GetModel();
					Dbg_Assert( pCASModel );

					int use_default_hsv = 1;
					pHeadStruct->GetInteger( CRCD(0x97dbdde6,"use_default_hsv"), &use_default_hsv );
					if ( !use_default_hsv )
					{
						int h, s, v;
						if ( pHeadStruct->GetInteger( CRCD(0x6e94f918,"h"), &h, false )
							 && pHeadStruct->GetInteger( CRCD(0xe4f130f4,"s"), &s, false )
							 && pHeadStruct->GetInteger( CRCD(0x949bc47b,"v"), &v, false ) )
						{
							Script::CStruct* pTempStruct = new Script::CStruct;
							pTempStruct->AddChecksum( CRCD(0xb6f08f39,"part"), 0 );
							pTempStruct->AddChecksum( CRCD(0x83418a6a,"material"), CRCD(0xaa2206ef,"cashead_head") );
							pTempStruct->AddInteger( CRCD(0x318f2bdb,"pass"), 0 );
							pCASModel->SetColor( pTempStruct, (float)h, (float)s / 100.0f, (float)v / 100.0f );
							delete pTempStruct;
						}
					}
				}
			}
		}
		
		Dbg_Assert( mp_objectQuickAnim );
		for ( int j = 0; j < mp_objectQuickAnim->GetNumBones(); j++ )
		{
			uint32 obaObjectName = mp_objectQuickAnim->GetBoneName(j);
			if ( pObjectInfo->m_objectName == obaObjectName )
			{
				pObjectInfo->m_obaIndex	= j;
				break;
			}
		}
	}


	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

char* get_cut_name( const char* p_fileName )
{
	static char cutsceneName[256];

	for ( int i = strlen(p_fileName); i >= 0; i-- )
	{
		if ( p_fileName[i] == '\\' || p_fileName[i] == '/' )
		{
			strcpy( cutsceneName, &p_fileName[i+1] );
			break;
		}
	}

	// strip off extension, if one exists
	char* pExt = strstr( cutsceneName, "." );
	if ( pExt )
	{
		*pExt = NULL;
	}

	return cutsceneName;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneData::Load( const char* p_fileName, bool assertOnFail, bool async_load )
{   
	// now that the async code is working, 
	// there's no reason to load it synchronously
	async_load = true;

	// GJ:  Disabled async load to remove
	// the glitch when starting cutscenes
	async_load = false;
	
	// ...  but turn off async for platforms that
	// don't support it	anyway
	if ( !File::CAsyncFileLoader::sAsyncSupported() )
	{
		async_load = false;
	}
	
	// based on the name of the CUT file, 
	// we can build the name of the stream associated with it
	char* pCutsceneName = get_cut_name( p_fileName );

	//-------------------------------------------------------------
	// Load up the correct stream, based on the skater's sex
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( 0 );
	Script::CStruct* pInfo = pSkaterProfile->GetInfo();
	int is_male = 0;
	pInfo->GetInteger( CRCD(0x3f813177,"is_male"), &is_male, Script::ASSERT );
	
	char streamName[256];
	if ( is_male )
	{
		sprintf( streamName, "%s_Male", pCutsceneName );
	}
	else
	{
		sprintf( streamName, "%s_Female", pCutsceneName );
	}
	
	uint32 streamNameChecksum;
	streamNameChecksum = Crc::GenerateCRCFromString(streamName);

#ifndef __PLAT_NGC__
	if ( !Pcm::PreLoadMusicStream( streamNameChecksum ) )
	{
		Dbg_Message( "Couldn't start stream...  stream %s not found?", streamName );
		m_audioStarted = true;
	}
#endif		// __PLAT_NGC__
	//-------------------------------------------------------------

	//-------------------------------------------------------------
	// Create a cutscene heap here...  it will only be used for the 
	// cutscene, and will prevent fragmentation on the bottom up heap
	// by things that might get allocated during the cutscene,
	// such as proxim nodes.
	Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap() );
		
	// reserve about 100K for any allocations that need to go on the 
	// bottom up heap (such as proxim nodes).  Also need to reserve
	// some room on the topdown heap for texture dictionary loading
	// on the PS2
	int mem_available = Mem::Manager::sHandle().Available();
#	if defined( __PLAT_NGC__ )
#ifdef DVDETH
	mem_available -= 6 * 1024 * 1024;
#else
#ifdef __NOPT_ASSERT__
	mem_available = 3 * 1024 * 1024;
#else

# define toupper(c) ( ( (c) >= 'a' ) && ( (c) <= 'z' ) ) ? (c) += ( 'A' - 'a' ) : (c)
	char name[64];
	char * ps = (char *)p_fileName;
	char * pd = name;
	while ( *ps != '\0' )
	{
		*pd = toupper( *ps );
		ps++;
		pd++;
	}

	if ( strstr( name, "FL_03" ) )
	{
		mem_available -= 200 * 1024;
		OSReport( "******* Reducing by 200k\n" );
	}
	else
	{
		if ( strstr( name, "NY_01V" ) )
		{
			mem_available -= 100 * 1024;
			OSReport( "******* Reducing by 80k\n" );
		}
		else
		{
			if ( strstr( name, "NY_02" ) )
			{
				mem_available -= 80 * 1024;
				OSReport( "******* Reducing by 80k\n" );
			}
			else
			{
				mem_available -= 120 * 1024;
				OSReport( "******* Reducing by 120k\n" );
			}
		}
	}
#endif		// __NOPT_ASSERT__ 
#endif		// DVDETH
#	elif defined( __PLAT_XBOX__ )
	// Reserve more for Xbox since audio streams will be allocated from bottom up heap.
	mem_available -= 512 * 1024;
#	else
	mem_available -= 200 * 1024;
#	endif		// __PLAT_NGC__

#ifdef __PLAT_NGC__
		if ( mem_available < ( 150 * 1024 ) )
		{
			// If heap is too small, allocate a 200k heap from the theme region.
			Mem::Manager::sHandle().PopContext();
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
			mem_available = 150 * 1024;
		}
#endif

	Mem::Manager::sHandle().InitCutsceneHeap( mem_available );
	Mem::Manager::sHandle().PopContext(); 
	//-------------------------------------------------------------
#	if defined( __PLAT_NGC__ )
	g_in_cutscene = true;
#	endif		// __PLAT_NGC__

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneBottomUpHeap());	
	Dbg_MsgAssert( !mp_fileLibrary, ( "File library already exists" ) );
	mp_fileLibrary = new File::CFileLibrary;
	Dbg_MsgAssert( mp_fileLibrary, ( "Couldn't create file library %s", p_fileName ) );
	Mem::Manager::sHandle().PopContext();

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneTopDownHeap());	

	bool success = mp_fileLibrary->Load( p_fileName, assertOnFail, async_load );
	
	if ( !success )
	{
		Dbg_MsgAssert( 0, ( "Couldn't load file library %s", p_fileName ) );
	}
	
	Mem::Manager::sHandle().PopContext();

	if ( !async_load )
	{
		PostLoad( assertOnFail );
		m_dataLoaded = true;
	}

#ifdef __PLAT_NGC__
	if ( !Pcm::PreLoadMusicStream( streamNameChecksum ) )
	{
		Dbg_Message( "Couldn't start stream...  stream %s not found?", streamName );
		m_audioStarted = true;
	}

	while ( !m_audioStarted )
	{
		Pcm::Update();
		
		if ( Pcm::PreLoadMusicStreamDone() )
		{
			// TODO:  Fill in with appropriate music volume parameter?
			Pcm::StartPreLoadedMusicStream();

			m_audioStarted = true;
			break;
		}

		Tmr::VSync();
	}
#endif		// __PLAT_NGC__

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneData::LoadFinished()
{
	if ( m_dataLoaded )
	{
		// already finished before this...
		return true;
	}

	// check to see whether the file has finished loading
	if ( !mp_fileLibrary->LoadFinished() )
	{
		Dbg_Message( "Waiting for async load to finish..." );
		return false;
	}
	else
	{
		PostLoad( true );
		m_dataLoaded = true;
		return true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
										    
void CCutsceneData::update_video_playback( Gfx::Camera* pCamera, Script::CStruct* pStruct )
{
	if ( !m_videoStarted )
	{
		// need to pass in the fadeintime
//		Script::SpawnScript( CRCD(0x55665639,"FadeInCutscene"), pStruct );
		m_videoStarted = true;
		
		Script::RunScript( CRCD(0xe794a164,"CutsceneStartVideo") );

		// initialize the camera (don't really need to do this,
		// since the camera animation will override it)
		Dbg_Assert( pCamera );
//		Mth::Vector& camPos = pCamera->GetPos();
//		Mth::Matrix& camMatrix = pCamera->GetMatrix();
//		camPos = Mth::Vector(0.0f,0.0f,0.0f,1.0f);
//		camMatrix.Ident();
	
		// reset the camera to some default FOV,
		// or else it will use the last FOV used
		float fov_in_degrees = Script::GetFloat( CRCD(0x99529205, "camera_fov") );
		pCamera->SetHFOV(fov_in_degrees);

		m_oldTime = 0.0f;
		m_initialVBlanks = Tmr::GetVblanks();
	}

	// update universal cutscene time
	// (shouldn't use the animcontroller's update function
	// because it caps the frame length...  although
	// there's no real reason that it needs to... 
	// maybe we can have the animcontroller use the
	// uncapped version as well...  it's only capped
	// for things like the physics so that objects
	// don't travel too far in a single frame...)
	m_oldTime = m_currentTime;
	
#if 0
	// here's some debug code for debugging cutscene glitches...
	// this will loop through 1 second's worth of cutscene data
	// at half speed.  it was handy for tracking down 
	// SK5:TT10351 - "pop" in camera animation when going to
	// a moving camera cut
	static float cutscene_test_time_offset = 0.0f;
	cutscene_test_time_offset += ( 1/120.0f );
	if (cutscene_test_time_offset >= 1.0f )
	{
		cutscene_test_time_offset -= 1.0f;
	}

	m_currentTime = Script::GetFloat( "cutscene_test_time" ) + cutscene_test_time_offset;//(float)( Tmr::GetVblanks() - m_initialVBlanks ) / Config::FPS();
#else
	m_currentTime = (float)( Tmr::GetVblanks() - m_initialVBlanks ) / Config::FPS();
#endif

	// if the CUT file is really small, then chances are
	// the Makes will still be set from choosing the
	// item on the menu...  so, the following makes
	// sure that you don't try to skip it before the
	// cutscene has played for at least 1 frame...
	if ( m_videoStarted && ( m_currentTime != 0.0f ) )
	{
		Mdl::FrontEnd* pFront = Mdl::FrontEnd::Instance();
		
		for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
		{
			Inp::Handler< Mdl::FrontEnd >* pHandler = pFront->GetInputHandler( i );
			if ( pHandler->m_Device->IsPluggedIn() && pHandler->m_Device->HasValidControlData() && pHandler->m_Input->m_Makes & Inp::Data::mD_X )
			{
				// GJ FIX FOR TT12703:  "If the player disconnects the 
				// controller during any cut scene in the game, the player
				// can reconnect the controller into any of the other 3
				// controller ports and bypass the cut scene by pressing
				// the A button. Since story mode is a one player game, the
				// player should not be able to use multiple ports at the
				// same time."
				if ( i == Mdl::Skate::Instance()->m_device_server_map[0] )
				{
					// skip to the end of the movie...
					m_currentTime = mp_cameraQuickAnim->GetDuration();
					m_videoAborted = true;
				}
				else
				{
					Dbg_Message( "Can only abort cutscenes from first controller" );
				}
			}
		}
	}

	update_camera( pCamera );

	// boned anims should come before moving objects,
	// because some moving objects depend on the 
	// current position of the neck bone
	update_boned_anims();	

	update_moving_objects();

	update_extra_cutscene_objects();

/*
	// display moving objects here
	// (need to do it after the object anims AND
	// the skeletal anims have been updated for
	// this frame)
	for ( int i = 0; i < m_numObjects; i++ )
	{
		if ( m_objects[i].mp_animInfo )
		{
			Obj::CCompositeObject* pCompositeObject = m_objects[i].mp_object;	
			Dbg_Assert( pCompositeObject );

			Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( pCompositeObject );
			if ( pSkeletonComponent )
			{
				Gfx::CSkeleton* pSkeleton = pSkeletonComponent->GetSkeleton();
				Dbg_Assert( pSkeleton );
	
				// if we're going to grab the display matrix, need to grab it
				// *after* the object anims have been updated
				Mth::Matrix theMat = pCompositeObject->GetDisplayMatrix();
				theMat[Mth::POS] = pCompositeObject->GetPos();
				pSkeleton->Display( &theMat, 1.0f, 0.0f, 0.0f );
			}
		}
	}
*/
	if ( Script::GetInteger( CRCD(0x5ecc0073,"debug_cutscenes") ) )
	{
		// pass the pertinent info to the panel:
		Script::CStruct* pTempStruct = new Script::CStruct;

//			char line1[128];
//			sprintf( line1, "camera = %f", m_cameraController.GetCurrentAnimTime() );
//			pTempStruct->AddString( "line1", (const char*)&line1 );

		char line1[128];
		sprintf( line1, "time = %f", m_currentTime );
		pTempStruct->AddString( "line1", (const char*)&line1 );

		Script::RunScript( CRCD(0xd9157174,"draw_cutscene_panel"), pTempStruct );

		delete pTempStruct;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneData::update_extra_cutscene_objects()
{
	// GJ:  the cutscene manager updates the camera position after
	// the object manager updates the extra peds in the scene,
	// meaning that the extra peds will still be using the
	// previous frame's camera position to calculate whether it
	// should be visible.  effectively, this causes a 1-frame glitch
	// whenever there's a new camera cut and a ped appears for the
	// first time on-screen.  to patch this, i will loop through
	// the list of all the extra peds for the cutscene and re-apply
	// their model positions.
	
	Script::CArray* pArray = Script::GetArray( CRCD(0x67f08671,"CutsceneObjectNames"), Script::NO_ASSERT );
	if ( pArray )
	{
		for ( uint32 i = 0; i < pArray->GetSize(); i++ )
		{
			uint32 objectName = pArray->GetChecksum( i );
			Obj::CCompositeObject* pObject = (Obj::CCompositeObject*)Obj::ResolveToObject( objectName );
			if ( pObject )
			{
				Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pObject );
				if ( pModelComponent && pModelComponent->GetModel() )
				{
					pModelComponent->GetModel()->SetActive( true );
					pModelComponent->Update();
				}
			}
		}
	}

	/*
	// repeat for the skater
	Obj::CCompositeObject* pObject = (Obj::CCompositeObject*)Obj::ResolveToObject( 0 );
	if ( pObject )
	{
		Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pObject );
		if ( pModelComponent && pModelComponent->GetModel() )
		{
			pModelComponent->GetModel()->SetActive( true );
//			pModelComponent->Update();
		}
	}
	*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneData::Update( Gfx::Camera* pCamera, Script::CStruct* pStruct )
{
	if ( !m_audioStarted )
	{
		// TODO:  Maybe have some timeout, so that if it 
		// can't find the stream, it won't wait forever...

		if ( Pcm::PreLoadMusicStreamDone() )
		{
			// TODO:  Fill in with appropriate music volume parameter
			Pcm::StartPreLoadedMusicStream();

			m_audioStarted = true;
			
			// GJ:  This seemed to cause sync problems
			// when the CUT file is really small (quick
			// to load), and the streams aren't ready
			// to go yet...  need to look into it more

			// now that the music stream has started,
			// we should start updating the video
			// on this frame
			update_video_playback( pCamera, pStruct );
		}
		else
		{
			Dbg_Message( "Waiting for streams to preload to finish..." );
		}
	}
	else
	{
		update_video_playback( pCamera, pStruct );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneData::IsAnimComplete()
{
	// we don't call the Update() function any more
	// (because Update() uses the capped frame rate)
	//return m_cameraController.IsAnimComplete();

	return m_currentTime >= mp_cameraQuickAnim->GetDuration(); 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneData::update_camera( Gfx::Camera* pCamera )
{
	Dbg_Assert( pCamera );

	// Not sure if this is still needed for the Doppler effect code
	// Maybe we can add the functionality automatically to the Gfx::Camera?
	pCamera->StoreOldPos();

	Mth::Vector& camPos = pCamera->GetPos();
	Mth::Matrix& camMatrix = pCamera->GetMatrix();
	
	Mth::Quat theQuat;
	Mth::Vector theTrans;

	get_current_frame( &theQuat, &theTrans );
		
	// update the camera position
	camPos = theTrans;

	// update the camera orientation
	Mth::Vector nullTrans;
	nullTrans.Set(0.0f,0.0f,0.0f,1.0f);		// Mick: added separate initialization
	Mth::QuatVecToMatrix( &theQuat, &nullTrans, &camMatrix );

	if ( !m_videoAborted )
	{
		// any allocations during the cutscene should go on the
		// cutscene heap (such as pedestrians).  at the end
		// of the cutscene, we require that the cutscene heap
		// is empty before it is deleted...
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneBottomUpHeap());	

		// process any new keys since last frame...
		// do this AFTER the camera is updated for
		// the frame, because a custom key might
		// override the normal value (this is a fix
		// for the double-cut problem, which
		// tries to interpolate between the end
		// of one camera and the beginning of the
		// next...  what we really want is to 
		// keep on the end of the previous camera)
		mp_cameraQuickAnim->ProcessCustomKeys( m_oldTime, m_currentTime, pCamera );
		
		Mem::Manager::sHandle().PopContext();
	}

//	printf( "Done for frame\n" );

//	camPos.PrintContents();
//	camMatrix.PrintContents();
}

// TODO:  Check for overflow of temp arrays
static Mth::Quat s_temp_quats[96];
static Mth::Vector s_temp_vectors[96];

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneData::update_boned_anims()
{
	for ( int i = 0; i < m_numObjects; i++ )
	{
		if ( m_objects[i].mp_animInfo )
		{
			Obj::CCompositeObject* pCompositeObject = m_objects[i].mp_object;	
			Dbg_Assert( pCompositeObject );

			Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( pCompositeObject );
			if ( pSkeletonComponent )
			{
				Gfx::CSkeleton* pSkeleton = pSkeletonComponent->GetSkeleton();
				Dbg_Assert( pSkeleton );

				Nx::CQuickAnim* pBonedQuickAnim = m_objects[i].mp_animInfo->mp_bonedQuickAnim;

				// turn off the bone skipping, because we're
				// not going through the normal animation code
				pSkeleton->SetBoneSkipDistance( 0.0f );

				pBonedQuickAnim->GetInterpolatedFrames( &s_temp_quats[0], &s_temp_vectors[0], pSkeleton->GetBoneSkipList(), pSkeleton->GetBoneSkipIndex(), m_currentTime );

				if ( m_objects[i].m_doPlayerSubstitution && pSkeleton->GetNumBones() == 55 )
				{					
					int bone_index = pSkeleton->GetBoneIndexById( CRCD(0x7268c230,"bone_jaw") );
					
					// GJ:  the hi-res jaw bone and the low-res jaw bone aren't in the
					// exact same position, which is required for the skinning to
					// work properly (otherwise, it would create a gap between the
					// head and body models.)  ideally on the next project, we will
					// change the exporter so that it puts the correct low-res jaw
					// position into the body's SKA, and the correct hi-res jaw into
					// the head's SKA.  For now, since we only have access to the
					// correct hi-res jaw bone position, we need to explicitly
					// offset the position of the low-res jaw bone here.  the offset
					// was derived by subtracting the low-res jaw bone bone-space 
					// translation (-0.000004, -1.776663, 0.599479) from the hi-res
					// jaw's bone-space translation (0, -1.57249, 0.08276)
					s_temp_vectors[bone_index] += Mth::Vector(0.000000f, -1.776663f-(-1.57249f), 0.599479f-0.08276f, 1.000000f);

//					s_temp_vectors[bone_index] = Mth::Vector(0.000000f, -2.531250f, 1.406250f, 1.000000f);
				}

				pSkeleton->Update( &s_temp_quats[0], &s_temp_vectors[0] );
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void update_model_component( Obj::CCompositeObject* pCompositeObject )
{
	CModelComponent* pModelComponent = GetModelComponentFromObject( pCompositeObject );
	if ( pModelComponent )
	{
		// if it's hidden, there's no reason to update it...
		// (as a side benefit, this also allows us to have multiple
		// objects referencing the same skater, such as in final.cut...
		// otherwise, each object would try to update the same model's 
		// position, and only the last object processed would actually
		// look correct)
		Nx::CModel* pModel = pModelComponent->GetModel();
		Dbg_Assert( pModel );
		if ( pModel->IsHidden() )
		{
			return;
		}

		if ( pModelComponent->HasRefObject() )
		{
			uint32 refObjectName = pModelComponent->GetRefObjectName();
			Obj::CCompositeObject* pRefObject = (Obj::CCompositeObject*)Obj::ResolveToObject( refObjectName );
			Dbg_Assert( pRefObject );
			Obj::CModelComponent* pRefModelComponent = GetModelComponentFromObject( pRefObject );
			Dbg_Assert( pRefModelComponent );
			Nx::CModel* pRefModel = pRefModelComponent->GetModel();
			   
			// turn off skater brightness, so that it matches the other objects...
			pRefModelComponent->GetModel()->GetModelLights()->SetBrightness( 0.0f );

			// update brightness in case there's any time of day changes
			pRefModelComponent->UpdateBrightness();
			
			bool should_animate = true;
			Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( pCompositeObject );
			Dbg_Assert( pSkeletonComponent );
			Mth::Matrix theMatrix = pCompositeObject->GetDisplayMatrix();
			theMatrix[Mth::POS] = pCompositeObject->GetPos();
			pRefModel->Render( &theMatrix, !should_animate, pSkeletonComponent->GetSkeleton() );
			pRefModel->SetBoneMatrixData( pSkeletonComponent->GetSkeleton() );

			// TODO:  Should do some check that the object's skeleton matches up with the ref object's models.
		
			// Need to update the fake skater's position with the
			// real skater's position, or else the FakeLights will
			// be set incorrectly (FakeLights works by taking the 
			// closest light to the m_pos that is pointed to by the
			// model lights)
			pRefObject->SetPos( pCompositeObject->GetPos() );
		}
		else
		{
			// update brightness in case there's any time of day changes
			pModelComponent->UpdateBrightness();
			
			// remember:
			// if there's no model specified, then it will draw
			// a red box.  the compositeobject's update() loop
			// will draw a red box with identity parameters, and then
			// this will draw a second one.  so don't freak out
			// when you see more boxes than you are expecting
			pModelComponent->Update();

			// GJ:  If you call the Update() function explicitly,
			// the model's double-buffer will be messed up...
			// in this case, however, we have suspended the model
			// component upon creation, so it doesn't get wonky
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneData::update_moving_objects()
{
	if ( !mp_objectQuickAnim )
	{
		// No OBA data found in CUT file
		return;
	}

	mp_objectQuickAnim->GetInterpolatedHiResFrames( &s_temp_quats[0], &s_temp_vectors[0], m_currentTime );

	// update all the OBA bones that don't have an object associated with it
	for ( int i = 0; i < mp_objectQuickAnim->GetNumBones(); i++ )
	{
		uint32 boneName = mp_objectQuickAnim->GetBoneName(i);
		
		bool objectExists = false;

		for ( int j = m_numObjects - 1; j >= 0; j-- )
		{
			SCutsceneObjectInfo* pInfo = &m_objects[j];
			if ( pInfo->m_objectName == boneName )
			{
				objectExists = true;
			}
		}

		if ( !objectExists )
		{
			Obj::CCompositeObject* pCompositeObject = (Obj::CCompositeObject*)Obj::ResolveToObject( boneName );
			if ( pCompositeObject )
			{
				// update the object's orientation
				Mth::Matrix theMatrix;

				Mth::Vector nullTrans;
				nullTrans.Set(0.0f,0.0f,0.0f,1.0f);		// Mick: added separate initialization
				Mth::QuatVecToMatrix( &s_temp_quats[i], &nullTrans, &theMatrix );

				//theMatrix.Ident();
				pCompositeObject->SetMatrix( theMatrix );
				pCompositeObject->SetDisplayMatrix( theMatrix );

				// update the object's position
				pCompositeObject->m_pos = s_temp_vectors[i];
			}
		}
	}


	// need to go backwards because the head is dependent
	// on the body's matrices being set up correctly
	// for that frame...
	for ( int i = m_numObjects - 1; i >= 0; i-- )
	{
		SCutsceneObjectInfo* pInfo = &m_objects[i];

		Obj::CCompositeObject* pCompositeObject = pInfo->mp_object;

		if ( pInfo->mp_parentObject )
		{
			// we handle the hi-res heads specially...  if we
			// were to use the OBA data, the hi-res head wouldn't
			// match up exactly with the body, possibly due to
			// accumulated round-off error as we move down the
			// parent-child hierarchy

			Obj::CCompositeObject* pParentObject = pInfo->mp_parentObject;
			Dbg_Assert( pParentObject );

			// if it's a neck bone, then use the previous item to link it
			Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( pParentObject );
			Dbg_Assert( pSkeletonComponent );
			Gfx::CSkeleton* pSkeleton = pSkeletonComponent->GetSkeleton();
			Dbg_Assert( pSkeleton );
			Mth::Vector neckPos;
			bool success = pSkeleton->GetBonePosition( CRCD(0x5a0e0860,"bone_neck"), &neckPos );
			if ( !success )
			{
				Dbg_MsgAssert( 0, ( "Couldn't find the neck bone" ) );
			}

			pCompositeObject->m_pos = pParentObject->m_pos + ( neckPos * pParentObject->GetDisplayMatrix() );
			pCompositeObject->m_pos[W] = 1.0f;

			Mth::Matrix bone_matrix;
			success = pSkeleton->GetBoneMatrix( CRCD(0x5a0e0860,"bone_neck"), &bone_matrix );
			if ( !success )
			{
				Dbg_MsgAssert( 0, ( "Couldn't find the neck bone" ) );
			}

			// build the object's matrix
			Mth::Matrix object_matrix = pParentObject->GetDisplayMatrix();
			object_matrix[W] = pParentObject->GetPos();
			object_matrix[X][W] = 0.0f;
			object_matrix[Y][W] = 0.0f;
			object_matrix[Z][W] = 0.0f;
			
			// rotate by 90:  doesn't seem to be needed
			// any more, perhaps due to an exporter bug
			// being fixed
			//Mth::Vector temp;
			//temp = bone_matrix[Y];
			//bone_matrix[Y] = bone_matrix[Z];
			//bone_matrix[Z] = -temp;

			// map the bone to world space
			bone_matrix *= object_matrix; 
			bone_matrix[W] = Mth::Vector(0.0f,0.0f,0.0f,1.0f);

			pCompositeObject->SetMatrix( bone_matrix );
			pCompositeObject->SetDisplayMatrix( bone_matrix );

			// need to update the component manually,
			// because it is probably out of range for
			// the suspension logic
			update_model_component( pCompositeObject );
		}
		else if ( pInfo->m_obaIndex != -1 )
		{
			// if the object is directly referenced in the OBA file,
			// then use the position and rotation specified in the OBA.

			// update the object's orientation
			Mth::Matrix theMatrix;

			Mth::Vector nullTrans;
			nullTrans.Set(0.0f,0.0f,0.0f,1.0f);		// Mick: added separate initialization
			Mth::QuatVecToMatrix( &s_temp_quats[pInfo->m_obaIndex], &nullTrans, &theMatrix );

			// GJ:  this used to be a kludge to fix objects
			// being rotated incorrectly by 90 degrees.  it
			// was probably due to an exporter bug, which
			// seems to have been fixed.  in any case,
			// it doesn't make sense why we would need
			// to rotate it in code, so i will comment
			// this out permanently and we can fix the
			// bug in the exporter if it ever arises again.
			if ( pInfo->m_skeletonName == 0 )
			{
				// but we still need to do it for MDL files
				// because it doesn't have SKA data that
				// automatically rotates it
				// theMatrix.RotateXLocal( Mth::DegToRad(90.0f) );
			}

			//theMatrix.Ident();
			pCompositeObject->SetMatrix( theMatrix );
			pCompositeObject->SetDisplayMatrix( theMatrix );

			// update the object's position
			pCompositeObject->m_pos = s_temp_vectors[pInfo->m_obaIndex];

//			Dbg_Message( "Object %d:   ", i );
//	 		pCompositeObject->m_pos.PrintContents();
//			pCompositeObject->GetDisplayMatrix().PrintContents();

			// need to update the component manually,
			// because it is probably out of range for
			// the suspension logic
			update_model_component( pCompositeObject );
		}

//		if ( pCompositeObject->GetID() == Script::GenerateCRC( "Skin_CAS2" ) )
//		{
//			printf( "Setting %s at time %d"\n, Script::FindChecksumName(pCompositeObject->GetID()), (int)(m_currentTime/60.0f+0.5f) );
//			pCompositeObject->GetPos().PrintContents();
//		}

#if 0
		// print out world-space position of board for adam:
		{
			Obj::CSkeletonComponent* pSkeletonComponent = GetSkeletonComponentFromObject( pCompositeObject );
			if ( pSkeletonComponent )
			{
				Gfx::CSkeleton* pSkeleton = pSkeletonComponent->GetSkeleton();
				Mth::Vector theVec;
				if ( pSkeleton->GetBonePosition( Script::GenerateCRC("bone_board_root"), &theVec ) )
				{
					printf( "Position of %s at time %f:\n\t", Script::FindChecksumName(pCompositeObject->GetID()), m_currentTime );
					pCompositeObject->GetPos().PrintContents();
					
					printf( "Position of %s's board at time %f:\n\t", Script::FindChecksumName(pCompositeObject->GetID()), m_currentTime );
					theVec = pCompositeObject->GetPos() + ( theVec * pCompositeObject->GetDisplayMatrix() );
					theVec.PrintContents();
             					
					printf( "--------------------------------\n" );
				}
			}
		}
#endif	  

		if ( Script::GetInteger( CRCD(0x5ecc0073,"debug_cutscenes"), Script::NO_ASSERT ) )
		{
			// set up bounding box
			SBBox theBox;
			theBox.m_max.Set(1.0f, 10.0f, 1.0f);
			theBox.m_min.Set(-1.0f, -1.0f, -1.0f);    

			// For now, draw a bounding box
			Gfx::AddDebugBox( pCompositeObject->GetDisplayMatrix(), pCompositeObject->GetPos(), &theBox, NULL, 1, NULL ); 
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneData::get_current_frame(Mth::Quat* pQuat, Mth::Vector* pTrans)
{
	Dbg_Assert( pQuat );
	Dbg_Assert( pTrans );

	// with cutscenes, i don't actually want to get 
	// the interpolated frames, i actually want the closest frame
	// this is for camera positions that are held on a certain
	// frame for a period of time, and then jumps somewhere else
	// in this case we would want either the old pose or the new pose,
	// not something in the middle

	// this means we will not want compression on it

	// grab the frame from the animation controller
	Dbg_MsgAssert( mp_cameraQuickAnim, ( "Camera data has not been initialized for this cutscene" ) );
	Dbg_Assert( mp_cameraQuickAnim->GetNumBones() == 1 );
	mp_cameraQuickAnim->GetInterpolatedHiResFrames( pQuat, pTrans, m_currentTime );
}

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCutsceneDetails::CCutsceneDetails()
{				  
	mp_cutsceneAsset = NULL;
	m_cutsceneAssetName = 0;

	mp_cutsceneStruct = new Script::CStruct;

	m_numHiddenObjects = 0;
	mp_hiddenObjects = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CCutsceneDetails::~CCutsceneDetails()
{
	if ( mp_cutsceneStruct )
	{
		delete mp_cutsceneStruct;
	}

	if ( mp_cutsceneAsset )
	{
		Ass::CAssMan* pAssMan = Ass::CAssMan::Instance();
		Ass::CAsset* pAsset = pAssMan->GetAssetNode( m_cutsceneAssetName, true );
		pAssMan->UnloadAsset( pAsset );
		mp_cutsceneAsset = NULL;
	}

	if ( mp_hiddenObjects )
	{
		delete[] mp_hiddenObjects;
		mp_hiddenObjects = NULL;
	}
	
	// restore the dynamic light modulation factor
	Nx::CLightManager::sSetAmbientLightModulationFactor( m_oldAmbientLightModulationFactor );
	Nx::CLightManager::sSetDiffuseLightModulationFactor( 0, m_oldDiffuseLightModulationFactor[0] );
	Nx::CLightManager::sSetDiffuseLightModulationFactor( 1, m_oldDiffuseLightModulationFactor[1] );

	// don't need to refresh the skater, because
	// it will be applied on the next frame anyway
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneDetails::InitFromStructure( Script::CStruct* pParams )
{
	bool success = false;

	/// need to create a temp copy of the params...  this is because
	// the PreCutscene script destroys the screen element that launched
	// the cutscene, meaning that afterwards the data inside pParams 
	// will become corrupt...
	Script::CStruct* pTempParams = new Script::CStruct;
	pTempParams->AppendStructure( pParams );

	this->SetParams( pTempParams );

	// TODO:  Handle async loads of cutscene
	bool async = false;
//	bool dont_assert = false;

	// TODO:  Need to account for the possibility that the same
	// cutscene will be loaded twice

	// TODO:  Push the correct heap

	// turn off the modulation for the duration of the cutscene
	m_oldAmbientLightModulationFactor = Nx::CLightManager::sGetAmbientLightModulationFactor();
	m_oldDiffuseLightModulationFactor[0] = Nx::CLightManager::sGetDiffuseLightModulationFactor(0);
	m_oldDiffuseLightModulationFactor[1] = Nx::CLightManager::sGetDiffuseLightModulationFactor(1);

	Nx::CLightManager::sSetAmbientLightModulationFactor( 0.0f );
	Nx::CLightManager::sSetDiffuseLightModulationFactor( 0, 0.0f );
	Nx::CLightManager::sSetDiffuseLightModulationFactor( 1, 0.0f );

	// force the fake lights to allocate its memory now
	// (otherwise, the next call to RenderWorld() would
	// alloc it which would end up fragmenting memory)
	Nx::CLightManager::sUpdateVCLights();

	// also need to turn it off on the skater to refresh it
	// (but that will be done if there's a ref model)

	const char* pCutsceneFileName;
	pTempParams->GetText( CRCD(0xa1dc81f9,"name"), &pCutsceneFileName, Script::ASSERT );
	
	Ass::CAssMan* pAssMan = Ass::CAssMan::Instance();
	if ( pAssMan->GetAsset( pCutsceneFileName, false ) )
	{
		Dbg_Message( "Cutscene %s is already playing!", pCutsceneFileName );
		goto init_failed;
	}
	else
	{
		char* pCutName = get_cut_name( pCutsceneFileName );
		char structName[256];
		strcpy( structName, pCutName );
		strcat( structName, "_params" );

		Script::CStruct* pCutParams = Script::GetStructure(structName, Script::NO_ASSERT);
		if ( pCutParams )
		{
			mp_cutsceneStruct->AppendStructure( pCutParams );
		}

		// first look at "dont_unload_anims" flag in cutscene struct
		int should_unload_anims = !mp_cutsceneStruct->ContainsFlag( CRCD(0xbafa9f1c,"dont_unload_anims") );
		int should_reload_anims = !mp_cutsceneStruct->ContainsFlag( CRCD(0xbafa9f1c,"dont_unload_anims") );

		if ( pParams->ContainsFlag( CRCD(0x7ddde00e,"from_cutscene_menu") ) )
		{
			// if we're coming from the cutscene menu, we never want to unload anims
			should_unload_anims = true;
			should_reload_anims = true;
		}

		mp_cutsceneStruct->AddInteger( CRCD(0xcdf2b13e,"unload_anims"), should_unload_anims );
		mp_cutsceneStruct->AddInteger( CRCD(0x9a4bdff5,"reload_anims"), should_reload_anims );

		// by default, unload the goals
		mp_cutsceneStruct->AddInteger( CRCD(0xed064302,"unload_goals"), 1 );
		mp_cutsceneStruct->AddInteger( CRCD(0xbabf2dc9,"reload_goals"), 1 );

		// "unload_anims" and "reload_anims" can be overridden from playcutscene line
		mp_cutsceneStruct->AppendStructure( pTempParams );

		// hide all moving objects here...
		// (do it before normal hide/unhide scripts happen)
		hide_moving_objects();

		Script::RunScript( CRCD(0xe1ce826a,"PreCutscene"), mp_cutsceneStruct );

		// run the script after the cutscene, so
		// that we can unpause the objects and such...
		char startScriptName[256];
		strcpy( startScriptName, pCutName );
		strcat( startScriptName, "_begin" );
		char endScriptName[256];
		strcpy( endScriptName, pCutName );
		strcat( endScriptName, "_end" );
		m_startScriptName = Crc::GenerateCRCFromString( startScriptName );
		m_endScriptName = Crc::GenerateCRCFromString( endScriptName );

		// GJ:  "use_pip" flag would be handy here!

		mp_cutsceneAsset = (CCutsceneData*)pAssMan->LoadAsset( pCutsceneFileName, async, false, false, 0 );

		// couldn't get cutscene
		Dbg_MsgAssert( mp_cutsceneAsset, ( "Couldn't create cutscene data %s", pCutsceneFileName ) );

		m_cutsceneAssetName = Script::GenerateCRC( pCutsceneFileName );
    }

	success = true;

init_failed:
	delete pTempParams;
	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneDetails::Cleanup()
{
#ifdef __PLAT_NGC__
	g_in_cutscene = false;
#endif		// __PLAT_NGC__

	// kill the cutscene panel, if it exists
	Script::RunScript( CRCD(0xe7bc7efd,"kill_cutscene_panel") );

	// need to delete the cutscene asset,
	// so that there will be enough room for all
	// the anims reloaded in "PostCutscene"
	if ( mp_cutsceneAsset )
	{
		Ass::CAssMan* pAssMan = Ass::CAssMan::Instance();
		Ass::CAsset* pAsset = pAssMan->GetAssetNode( m_cutsceneAssetName, true );
		pAssMan->UnloadAsset( pAsset );
		mp_cutsceneAsset = NULL;
	}

	if ( m_endScriptName )
	{
		Script::RunScript( m_endScriptName );
	}
		
	// kill special effects, in case the artist forgot to do it 
	// in the individual cutscene end scripts... (need to do this
	// before the cutscene heap is destroyed)
	Script::RunScript( CRCD(0xf49a8c9c,"kill_cutscene_camera_hud") );

	// Clear any currently running FakeLights commands, in case the player
	// aborted while they were still taking effect during the cutscene
	// (in which case the fake lights would have gone on the cutscene heap)
	Nx::CLightManager::sClearCurrentFakeLightsCommand();

	// destroy the cutscene heap (at this point, any allocations made during 
	// the cutscene should have been freed up)
	Mem::Manager::sHandle().DeleteCutsceneHeap();

	Script::RunScript( CRCD(0xbca1791c,"PostCutscene"), mp_cutsceneStruct );

	// unhide moving objects, based on whether they were hidden...
	unhide_moving_objects();

	CMovieDetails::Cleanup();

	Script::RunScript( CRCD(0x15674315,"restore_skater_camera") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneDetails::Update()
{
	if ( !mp_cutsceneAsset )
	{
		Dbg_Message( "No cutscene asset!" );
		return;
	}

	if ( mp_cutsceneAsset->LoadFinished() )
	{
		if ( !m_startScriptAlreadyRun && m_startScriptName )
		{
			Script::RunScript( m_startScriptName );
			m_startScriptAlreadyRun = true;
		}

		mp_cutsceneAsset->Update( mp_camera, mp_cutsceneStruct );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneDetails::HasMovieStarted()
{
	if ( !mp_cutsceneAsset )
	{
		Dbg_MsgAssert( 0, ( "No cutscene asset!" ) );
		return false;
	}

	return mp_cutsceneAsset->HasMovieStarted();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneDetails::ResetCustomKeys()
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneDetails::IsComplete()
{
	if ( m_aborted )
	{
		return true;
	}

	if ( m_holdOnLastFrame )
	{
		return false;
	}

	if ( mp_cutsceneAsset )
	{
		return mp_cutsceneAsset->LoadFinished() && mp_cutsceneAsset->IsAnimComplete();
	}
		
	return false;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneDetails::IsHeld()
{
	return false;
}
									  
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CCutsceneDetails::OverridesCamera()
{
	if ( mp_cutsceneAsset )
	{
		return mp_cutsceneAsset->OverridesCamera();
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneDetails::hide_moving_objects()
{
	// hides the goal peds
	Script::RunScript( CRCD(0x722d9ca7,"cutscene_hide_objects") );

	// first count the number of objects, so we can allocate the proper number of hide flags
	int numObjects = 0;
	Obj::CBaseComponent *p_component = Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRCD(0x286a8d26,"model") );
	while( p_component )
	{
		Obj::CModelComponent* pModelComponent = ((Obj::CModelComponent*)p_component);
		Nx::CModel* pModel = pModelComponent->GetModel();
		if ( p_component->GetObject()->GetID() == 0 )
		{
			// skip the skater...
		}
		else if ( !pModel->IsHidden() )
		{
			numObjects++;
		}
		p_component = p_component->GetNextSameType();		
	}
	Dbg_MsgAssert( mp_hiddenObjects	== NULL, ( "hidden objects array already exists?" ) );

	if ( !numObjects)
		return;


	mp_hiddenObjects = new uint32[numObjects];
	m_numHiddenObjects = 0;
	
	// now actually go through and hide them
	p_component = Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRCD(0x286a8d26,"model") );
	while( p_component )
	{
		Obj::CModelComponent* pModelComponent = ((Obj::CModelComponent*)p_component);
		Nx::CModel* pModel = pModelComponent->GetModel();
		if ( p_component->GetObject()->GetID() == 0 )
		{
			// skip the skater...
		}
		else if ( !pModel->IsHidden() )
		{
			// if this fires off, that would be really weird because we just counted the exact
			// number we need a few moments ago...
			Dbg_MsgAssert( m_numHiddenObjects < numObjects, ( "Too many objects in scene to hide?" ) );

//			printf( "Found model to hide (%s)\n", Script::FindChecksumName(p_component->GetObject()->GetID()) );
			mp_hiddenObjects[m_numHiddenObjects] = p_component->GetObject()->GetID();
			pModel->Hide( true );
			m_numHiddenObjects++;
		}

		p_component = p_component->GetNextSameType();		
	}

	printf( "Hid %d objects\n", m_numHiddenObjects );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CCutsceneDetails::unhide_moving_objects()
{
	if ( mp_hiddenObjects )
	{
		for ( int i = 0; i < m_numHiddenObjects; i++ )
		{
			Obj::CCompositeObject* pObject = (Obj::CCompositeObject*)Obj::ResolveToObject( mp_hiddenObjects[i] );
			if ( pObject )
			{
				Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pObject );
				Nx::CModel* pModel = pModelComponent->GetModel();
				pModel->Hide( false );
			}
			else
			{
				// it's possible that they won't exist anymore
				// because of the call to uninitialize all goals...
				//Dbg_MsgAssert( pObject, ( "Couldn't find object %s to unhide?", Script::FindChecksumName(mp_hiddenObjects[i]) ) );
			}
		}

		delete[] mp_hiddenObjects;
		mp_hiddenObjects = NULL;
	}

	m_numHiddenObjects = 0;
	
	// unhides the goal peds
	Script::RunScript( CRCD(0x94d0d528,"cutscene_unhide_objects") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj


================================================
FILE: Code/Sk/Objects/cutscenedetails.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       cutscenedetails.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  01/13/2003
//****************************************************************************

#ifndef __OBJECTS_CUTSCENEDETAILS_H
#define __OBJECTS_CUTSCENEDETAILS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 							   

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

namespace File
{
	class CFileLibrary;
}
		 
namespace Nx
{
	class CQuickAnim;
}

namespace Mth
{
	class Matrix;
	class Quat;
	class Vector;
}
				   
namespace Script
{
	class CScript;
	class CStruct;
}

namespace Obj
{

	class CCutsceneData;
	class CCompositeObject;

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

struct SCutsceneAnimInfo
{
	uint32						m_animName;
	Nx::CQuickAnim*				mp_bonedQuickAnim;
};

struct SCutsceneObjectInfo
{
	uint32						m_objectName;
	uint32						m_flags;
	uint32						m_skeletonName;
	bool						m_doPlayerSubstitution;
	SCutsceneAnimInfo*			mp_animInfo;
	Obj::CCompositeObject*		mp_object;
	Obj::CCompositeObject*		mp_parentObject;
	int							m_obaIndex;			// -1 if not valid
};

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class CCutsceneData	: public Spt::Class
{
protected:
	enum
	{
		vMAX_CUTSCENE_ANIMS = 64,
		vMAX_CUTSCENE_OBJECTS = 64,
		vMAX_CUTSCENE_ASSETS = 64
	};

public:
	CCutsceneData();
	virtual ~CCutsceneData();

public:
    bool    			   		Load(const char* p_fileName, bool assertOnFail, bool async);
    bool    			   		PostLoad(bool assertOnFail);
	bool						LoadFinished();
	void						Update( Gfx::Camera* pCamera, Script::CStruct* pStruct );
	bool						IsAnimComplete();
	bool						OverridesCamera();
	bool						HasMovieStarted()
	{
		return m_videoStarted;
	}

protected:
	void						get_current_frame( Mth::Quat* pQuat, Mth::Vector* pTrans );
	void						update_camera( Gfx::Camera* pCamera );
	void						update_moving_objects();
	void						update_extra_cutscene_objects();
	void						update_boned_anims();	
	void 						add_boned_anim( uint32 objectName, uint32* pData, int fileSize );
	void						add_anim( uint32 animName, uint32* pData, int fileSize );
	void 						add_object( Script::CStruct* pParams );
	bool						create_objects( uint32* pData );
	SCutsceneObjectInfo*		get_object_info( uint32 objectName );
	SCutsceneAnimInfo*			get_anim_info( uint32 animName );
	void						update_video_playback( Gfx::Camera* pCamera, Script::CStruct* pStruct );

protected:
	void						init_objects();
	void						load_models();

protected:
	// need this pointer only during async loads
	File::CFileLibrary* 		mp_fileLibrary;
	bool						m_dataLoaded;
	
	Nx::CQuickAnim*				mp_cameraQuickAnim;
	Nx::CQuickAnim*				mp_objectQuickAnim;
	
	Mth::Vector					m_original_player_bounding_sphere;
	bool						m_original_hidden;
	bool						m_original_scale_enabled;
	Mth::Vector					m_original_pos;

	// this is used so that if there are multiple create-a-skaters,
	// they don't all try to overwrite the same m_original_*
	// fields...
	bool						m_original_params_set;

	SCutsceneObjectInfo			m_objects[vMAX_CUTSCENE_OBJECTS];
	int							m_numObjects;

	SCutsceneAnimInfo			m_bonedAnims[vMAX_CUTSCENE_ANIMS];
	int							m_numBonedAnims;

	uint32						m_assetName[vMAX_CUTSCENE_ASSETS];
	int							m_numAssets;
	
	bool						m_audioStarted;
	bool						m_videoStarted;
	bool						m_videoAborted;

	float						m_oldTime;
	float						m_currentTime;
	uint64						m_initialVBlanks;
};

class CCutsceneDetails : public CMovieDetails
{
public:
	CCutsceneDetails();
	virtual			~CCutsceneDetails();

public:
	virtual bool 				InitFromStructure(Script::CStruct *pParams);
	virtual void 				Update();
	virtual bool 				ResetCustomKeys();
	virtual bool 				IsComplete();
	virtual bool				IsHeld();
	virtual bool				OverridesCamera();
	virtual void				Cleanup();
	virtual bool				HasMovieStarted();
	
protected:
	void						hide_moving_objects();
	void						unhide_moving_objects();

protected:
	CCutsceneData*				mp_cutsceneAsset;
	uint32						m_cutsceneAssetName;
	uint32						m_startScriptName;
	bool						m_startScriptAlreadyRun;
	uint32						m_endScriptName;
	Script::CStruct*			mp_cutsceneStruct;

	float						m_oldAmbientLightModulationFactor;
	float						m_oldDiffuseLightModulationFactor[2];
	
	uint32*						mp_hiddenObjects;
	int							m_numHiddenObjects;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_CUTSCENEDETAILS_H



================================================
FILE: Code/Sk/Objects/emitter.cpp
================================================
///////////////////////////////////////////////////////
// Emitter.cpp

		  
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 		   // used to get position of the skater's camera

//#define	DEBUG_EMITTER

namespace Obj
{

Lst::HashTable *	CEmitterManager::sp_emitter_lookup = NULL;

void CEmitterManager::sInit()
{
	if (sp_emitter_lookup)
	{
		sCleanup();
	}
	else
	{
		sp_emitter_lookup = new Lst::HashTable(4);	
	}
}

void CEmitterManager::sCleanup()
{
	if (sp_emitter_lookup)
	{
		sp_emitter_lookup->IterateStart();
		CEmitterObject *pNode = sp_emitter_lookup->IterateNext();
		while (pNode)
		{
			CEmitterObject *pNext = sp_emitter_lookup->IterateNext();
			uint32 checksum = pNode->m_name;   
			delete pNode;
			sp_emitter_lookup->FlushItem(checksum);			
			pNode = pNext;
	   	}

//		delete sp_emitter_lookup;
//		sp_emitter_lookup = NULL;
	}
}

void CEmitterManager::sAddEmitter(int node,bool active)
{
							
						
//	printf ("It's a Emitter (%d)\n",node);
	
	// create a new EmitterObject
	CEmitterObject *pEmitterObject = new CEmitterObject();

	pEmitterObject->m_node = node;

	pEmitterObject->m_active = active;  // created at start or not?
	
	pEmitterObject->mp_sector = NULL;
	pEmitterObject->m_shape_type = CEmitterObject::vSHAPE_SPHERE;  

	// Get pointer to the Emitter data	
	Script::CScriptStructure *pNode = SkateScript::GetNode(node);
	
	// Get name
	if (pNode->GetChecksum("name",&pEmitterObject->m_name))
	{
#ifdef	DEBUG_EMITTER
		printf ("Emitter: Got Name %s\n",Script::FindChecksumName(pEmitterObject->m_name));
#endif	
	}
	else
	{
		Dbg_MsgAssert(0,("No name in proxim node %d\n",node));
	}

	// Add to hash table
	sp_emitter_lookup->PutItem(pEmitterObject->m_name, pEmitterObject);

	// Get shape type
	uint32 shape_checksum;
	if (pNode->GetChecksum("type", &shape_checksum))
	{
		pEmitterObject->m_shape_type = CEmitterObject::sGetShapeFromChecksum(shape_checksum);
	}

	// Try to find geometry
	pEmitterObject->InitGeometry(Nx::CEngine::sGetMainScene()->GetSector(pEmitterObject->m_name));

	SkateScript::GetPosition(pNode,&pEmitterObject->m_pos);
#ifdef	DEBUG_EMITTER				
		Dbg_Message("Emitter: Pos = (%f, %f, %f)", pEmitterObject->m_pos[X], pEmitterObject->m_pos[Y], pEmitterObject->m_pos[Z]);
#endif	

	pNode->GetFloat (0xc48391a5 /*radius*/, &pEmitterObject->m_radius);  
	Dbg_MsgAssert(pEmitterObject->mp_sector || pEmitterObject->m_radius > 0.0f,( "Radius %f too small in proxim node %d\n",pEmitterObject->m_radius,node));	
#ifdef	DEBUG_EMITTER
	printf ("Emitter:  Radius = %f\n",pEmitterObject->m_radius);
#endif	
	pEmitterObject->m_radius_squared = pEmitterObject->m_radius * pEmitterObject->m_radius;	

	if (pNode->GetChecksum(0x2ca8a299,&pEmitterObject->m_script))		// Checksum of TriggerScript
	{
#ifdef	DEBUG_EMITTER
		printf ("Emitter: Got Script %s\n",Script::FindChecksumName(pEmitterObject->m_script));
#endif	
	}
	else
	{
		//Dbg_MsgAssert(0,("No triggerscript in proxim node %d\n",node));
	}

	
	//pEmitterObject->m_outside_flags = 0;			// say all inside for now....
}

void CEmitterManager::sAddedAll()
{
	
//	printf("Added all Emitters\n");		 
		 
	Dbg_Assert(sp_emitter_lookup);

	sp_emitter_lookup->IterateStart();
	CEmitterObject *pEmitterObject = sp_emitter_lookup->IterateNext();

	while (pEmitterObject)
	{
		// do any final per-node processing here
		pEmitterObject = sp_emitter_lookup->IterateNext();
	}				 
				 	
}			   

CEmitterObject * CEmitterManager::sGetEmitter(uint32 checksum)
{
	Dbg_Assert(sp_emitter_lookup);

	CEmitterObject *pEmitter = sp_emitter_lookup->GetItem(checksum);

	if (!pEmitter)
	{
		Dbg_Message("Warning: Can't find emitter %s", Script::FindChecksumName(checksum));
	}

	return pEmitter;
}

void CEmitterManager::sSetEmitterActive( int node, int active )
{
	Dbg_Assert(sp_emitter_lookup);

	sp_emitter_lookup->IterateStart();
	CEmitterObject *pEmitterObject = sp_emitter_lookup->IterateNext();
	while (pEmitterObject)
	{
		if (pEmitterObject->m_node == node)
		{
			pEmitterObject->m_active = active;
			return;
		}
		pEmitterObject = sp_emitter_lookup->IterateNext();
	}				 
}

#if 0
// Update all the proximity nodes				
// for a certain skater
// checking for proximity either with the skater or the camera					
void CEmitterManager::sUpdateEmitters(CSkater *pSkater, Gfx::Camera* camera)
{
	Dbg_Assert(sp_emitter_lookup);

	sp_emitter_lookup->IterateStart();
	CEmitterObject *pEmitterObject = sp_emitter_lookup->IterateNext();

    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if( skate_mod->mpProximManager == NULL )
	{
		return;
	}
	
	int	skater_num = 0;						// 0 for now, should be viewport index

	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	if (!gamenet_man->InNetGame())
	{
		if (skate_mod->GetSkater(1) == pSkater)
		{
			skater_num = 1;
		}
	}

	int	skater_mask = 1 << skater_num;		// the bitmask for this skater

//	Mth::Vector	skater_pos = pSkater->m_pos;	
//	Mth::Vector	skater_old_pos = pSkater->m_old_pos;	

	// Aieeeeee!!!
	// to get the camera's position, we have to dig down to a low level
	// the CSkater::GetSkaterCam returns a *CSkaterCam
	// CSkaterCam::GetCamera returns a 	reference to Gfx::Camera
	// Gfx::Camera::GetCamera returns a *NsCamera (pointer)
	// so we pass that to RwCameraGetFrame, so we can get the position of the camera...
	// Matt added m_pos to CSkaterCam and removed the code below to find the camera:
	
//	Gfx::Camera* 	p_camera = pSkater->GetSkaterCam()->mpCamera ;
	Mth::Vector	camera_pos;
	// Local clients can get the camera's position directly from the skater cam
	// but observers need to get it from the camera that was passed in since
	// the skater they're following doesn't have a skatercam

// Mick - camera passed in is the active camera
//	if( pSkater->IsLocalClient())
//	{
//		camera_pos = pSkater->GetSkaterCam()->GetPos();
//	}
//	else
//	{
		camera_pos = camera->GetPos();
//	}
	
	while (pEmitterObject)
	{
	
	//Mth::Vector to_skater = skater_pos - pEmitterObject->m_pos;
	// Bitfield per viewport....
			
//		printf ("Proxim: Update,  Script %s\n",Script::FindChecksumName(pEmitterObject->m_script));	

		// find the squared dist from this node:
		
		// This code would crash with observers since the skater they were following had no cam
		//Mth::Vector		node_to_camera = pSkater->GetSkaterCam( )->m_pos - pEmitterObject->m_pos;
        
		bool now_inside = pEmitterObject->CheckInside(camera_pos);

		if ( skater_mask & pEmitterObject->m_outside_flags)
		{
			// skater is currently outside, check if moved to inside
			if (now_inside)
			{
			
				pEmitterObject->m_outside_flags &= ~skater_mask;
				pSkater->m_inside = true;
				
#ifdef	DEBUG_EMITTER				
				printf ("+++++ NOW INSIDE, running script %s \n",Script::FindChecksumName(pEmitterObject->m_script));
#endif				
				pSkater->SpawnAndRunScript(pEmitterObject->m_script,pEmitterObject->m_node );
			}			
		}
		else
		{
			// skater is currently inside, check if moved outside
			if (!now_inside)
			{
				pEmitterObject->m_outside_flags |= skater_mask;				
				pSkater->m_inside = false;
	#ifdef	DEBUG_EMITTER				
				printf ("----- NOW OUTSIDE, running script %s \n",Script::FindChecksumName(pEmitterObject->m_script));
	#endif				
					
				pSkater->SpawnAndRunScript(pEmitterObject->m_script,pEmitterObject->m_node );
			}			
		}		
		pEmitterObject = sp_emitter_lookup->IterateNext();
	}				 
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CEmitterObject::EShape CEmitterObject::sGetShapeFromChecksum(uint32 checksum)
{
	switch (checksum)
	{
	case 0xaa069978: // Sphere
		return vSHAPE_SPHERE;

	case 0xe460abde: // BoundingBox
		return vSHAPE_BBOX;

	case 0x3f1e5a7: // NonAxisAlignedBoundingBox
		return vSHAPE_NON_AA_BBOX;

	default:
		Dbg_MsgAssert(0, ("Unknown shape checksum %x\n", checksum));
		return vSHAPE_SPHERE;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CEmitterObject::InitGeometry(Nx::CSector *p_sector)
{
	mp_sector = p_sector;

	if (!mp_sector)
	{
		// No Geometry
		Dbg_MsgAssert(m_shape_type == CEmitterObject::vSHAPE_SPHERE, ("EmitterObject with shape %d has no geometry.", m_shape_type));
		return;
	}

#ifdef	DEBUG_EMITTER				
	Dbg_Message("Found Geometry for EmitterObject %s at address %x", Script::FindChecksumName(m_name), mp_sector);
#endif

	switch (m_shape_type)
	{
	case CEmitterObject::vSHAPE_SPHERE:
		Dbg_MsgAssert(0, ("EmitterObject with sphere shape has geometry."));
		break;

	case CEmitterObject::vSHAPE_BBOX:
		m_bbox = mp_sector->GetBoundingBox();
#ifdef	DEBUG_EMITTER				
		Dbg_Message("Bounding box of geometry (%f, %f, %f) - (%f, %f, %f)",
					m_bbox.GetMin()[X], m_bbox.GetMin()[Y], m_bbox.GetMin()[Z],
					m_bbox.GetMax()[X], m_bbox.GetMax()[Y], m_bbox.GetMax()[Z]);
#endif	
		break;

	case CEmitterObject::vSHAPE_NON_AA_BBOX:
		Dbg_MsgAssert(0, ("EmitterObject with non axis aligned bounding box shape not supported yet."));
		break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CEmitterObject::CheckInside(const Mth::Vector &object_pos) const
{
	Mth::Vector		node_to_object = object_pos - m_pos;
	float			dist_squared = node_to_object.LengthSqr();	   

	bool inside = false;

	switch (m_shape_type)
	{
	case vSHAPE_SPHERE:
		inside = (dist_squared <= m_radius_squared);
		break;

	case vSHAPE_BBOX:
		inside = m_bbox.Within(object_pos);
#ifdef	DEBUG_EMITTER
		m_bbox.DebugRender(0xff0000ff);
#endif //	DEBUG_EMITTER
		break;

	default:
		Dbg_MsgAssert(0, ("Can't handle EmitterObject type %d", m_shape_type));
		break;
	}

	return inside;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector 		CEmitterObject::GetClosestPoint(const Mth::Vector &object_pos) const
{
	// Return original position if within the object
	if (CheckInside(object_pos))
	{
		return object_pos;
	}

	Mth::Vector intersect_point;

	switch (m_shape_type)
	{
	case vSHAPE_SPHERE:
		{
			// Same for either within or outside sphere
			Mth::Vector direction = object_pos - m_pos;
			direction.Normalize();
			direction *= m_radius;

			intersect_point = m_pos + direction;
		}
		break;

	case vSHAPE_BBOX:
		m_bbox.GetClosestIntersectPoint(object_pos, intersect_point);
		break;

	default:
		Dbg_MsgAssert(0, ("Can't handle EmitterObject type %d", m_shape_type));
		break;
	}

#ifdef	DEBUG_EMITTER
	Gfx::AddDebugStar(intersect_point);
#endif //	DEBUG_EMITTER

	return intersect_point;
}

/*
{
	// Node 572 (Mick Test)
	Position = (-4060.666992,131.999329,-4604.379395)
	Angles = (0.000000,-3.141593,0.000000)
	Name = TRG_Micks_test
	Class = ProximNode
	Type = skater
    radius = 1200
	TriggerScript = xxx_test
}
*/


}
			   



================================================
FILE: Code/Sk/Objects/emitter.h
================================================
//////////////////////////////////////////
// Emitter.h

#ifndef __OBJECTS_Emitter_H
#define __OBJECTS_Emitter_H

#include 
#include 

namespace Nx
{
	class	CSector;
}

namespace Gfx
{
	class	Camera;
}

namespace Script
{
	class	CScript;
}

namespace Obj
{

	class	CSkater;
	class CEmitterManager;

class CEmitterObject : public Spt::Class
{
public:
	enum EShape
	{
		vSHAPE_BBOX = 0,		// Axis-aligned bounding box
		vSHAPE_NON_AA_BBOX,		// Non-axis-aligned bounding box
		vSHAPE_SPHERE,
	};

	///////////
	// Functions
	void				InitGeometry(Nx::CSector *p_sector);
	bool				CheckInside(const Mth::Vector &object_pos) const;
	Mth::Vector 		GetClosestPoint(const Mth::Vector &object_pos) const;

	// Checksum conversion functions
	static EShape		sGetShapeFromChecksum(uint32 checksum);

protected:
	///////////
	// Variables
	int			m_node;				// Number of the node in the trigger array
	int 		m_link;				// link number
	uint32		m_name;				// checksum of name of the node in the trigger array
	Mth::Vector m_pos;				// position of the node
	Mth::Vector m_angles;			// angles of the node
	bool 		m_active;			// Emitter segment starting at this node: active or not?

	//int			m_outside_flags;	// bitmask of flags, 0 = inside the radius, 1 = outside

	EShape 		m_shape_type;

	// Sphere data
	float		m_radius;
	float		m_radius_squared;

	// Geometry Data
	Nx::CSector *mp_sector;

	// BBox data
	Mth::CBBox	m_bbox;
	
	float		m_last_distance_squared;
	
	uint32 		m_script;

	//CEmitterObject *m_pNext;


	// friends
	friend CEmitterManager;
};

 

class CEmitterManager
{
public:
	static void				sInit();
	static void				sCleanup();
	static void				sAddEmitter(int node, bool active);
	static void				sAddedAll();
	static CEmitterObject * sGetEmitter(uint32 checksum);

	// activate/deactivate Emitter:
	static void				sSetEmitterActive(int node, int active);

	//static void				sUpdateEmitters(CSkater *pSkater, Gfx::Camera* camera);

protected:
	static Lst::HashTable *	sp_emitter_lookup;	
};



} 

			
#endif			// #ifndef __OBJECTS_Emitter_H
			




================================================
FILE: Code/Sk/Objects/followob.cpp
================================================
#include 
#include 
#include 
#include 

namespace Obj
{

CFollowOb::CFollowOb()
{
	Reset();
}

CFollowOb::~CFollowOb()
{
}

void CFollowOb::Reset()
{
	m_num_path_points=0;
	m_most_recent_path_point=0;
	m_leader_name=0;
	m_leader_pos.Set();
	m_distance=0.0f;
	m_orient_y=false;
}

// Adds a new position taken from the leader object, if that position is suitably interesting.
// (Ie, it is not too close to the last position)
// FOLLOW_RESOLUTION should be kept as big as possible. That way, the array of positions does
// not need to be so big, and calculating the position will be faster because it won't have to
// step over so many segments.
// If it is too big though the object won't follow the leader's path so accurately and 
// will start cutting corners noticeably.
#define FOLLOW_RESOLUTION 40.0f
void CFollowOb::GetNewPathPointFromObjectBeingFollowed()
{
	//CMovingObject *p_obj=CMovingObject::m_hash_table.GetItem(m_leader_name);
	CMovingObject *p_obj=(CMovingObject*)Obj::ResolveToObject(m_leader_name);
	
	if (!p_obj)
	{
		// Can't find the object, oh well.
		return;
	}	

	m_leader_pos=p_obj->m_pos;
	
	if (m_num_path_points==0)
	{
		// Insert the first point.
		m_most_recent_path_point=0;
		mp_path_points[0]=m_leader_pos;
		++m_num_path_points;
		return;
	}	
	
	if (Mth::DistanceSqr(mp_path_points[m_most_recent_path_point],m_leader_pos) > 
		FOLLOW_RESOLUTION*FOLLOW_RESOLUTION)
	{
		// The new point is sufficiently far from the last, so insert it into the cyclic buffer.
		++m_most_recent_path_point;
		if (m_most_recent_path_point>=MAX_FOLLOWOB_PATH_POINTS)
		{
			m_most_recent_path_point=0;
		}
		
		mp_path_points[m_most_recent_path_point]=m_leader_pos;
		
		// m_num_path_points increases till it hits MAX_FOLLOWOB_PATH_POINTS then stays there.
		if (m_num_path_points seg_length)
		{
			dist_remaining-=seg_length;
			seg_start=seg_end;
			if (!num_points_left)
			{
				break;
			}	
			--i;
			if (i < 0)
			{
				i=MAX_FOLLOWOB_PATH_POINTS-1;
			}
			seg_end=mp_path_points[i];	
			--num_points_left;
		}
		else
		{
			break;
		}
	}
	
	if (seg_length>0.0f)
	{
		Mth::Vector seg_dir=seg_vec/-seg_length;
		
		*p_pos=seg_start-seg_dir*dist_remaining;
		
		if (m_orient_y)
		{
			// Instead of setting z to seg_dir directly, use some of the previous value
			// so that the orientation gradually moves towards the new dir rather than snapping.
			(*p_orientation)[Z]=10.0f*(*p_orientation)[Z]+seg_dir;
			(*p_orientation)[Z].Normalize();
			
			Mth::Vector Up(0.0f,1.0f,0.0f);
			(*p_orientation)[X]=Mth::CrossProduct(Up,(*p_orientation)[Z]);
			(*p_orientation)[X].Normalize();
			(*p_orientation)[Y]=Mth::CrossProduct((*p_orientation)[Z],(*p_orientation)[X]);
		}
		else
		{
			(*p_orientation)[Z]=10.0f*(*p_orientation)[Z]+seg_dir;
			(*p_orientation)[Z][Y]=0;
			(*p_orientation)[Z].Normalize();
			(*p_orientation)[Y].Set(0.0f,1.0f,0.0f);
			(*p_orientation)[X]=Mth::CrossProduct((*p_orientation)[Y],(*p_orientation)[Z]);
		}
	}	
}

}



================================================
FILE: Code/Sk/Objects/gap.cpp
================================================
//////////////////////////////////////////////////////////
// Gap.cpp
//
// Functions for CGap and CGapMan
//
// A "Gap" in the game is a pair of events that cause something to happen
// Typically this is jumping across something, such as a gap between two buildings
// but might also be grinding a rail for a certain distance
// or doing something specific, like doing a kickflip over a barrel.
//
// The aim of the gap system is to provide lots of flexibility in setting up
// these pairs of events, and also to allow the user to set gaps quickly and simply
//
// Gaps are set up via script commands
// so they can easily be inserted in trigger scripts
// the simplest might look like:
/*

script TRG_Obj010
	StartGap GapId = JumpOverLog Cancel = [CANCEL_GROUND CANCEL_RAIL CANCEL_WALLRIDE] Expire = 20
endscript


script TRG_Obj012
	EndGap GapId = JumpOverLog Score = 100 Text = "You jumped over a log" Script = ScreenFlash01
endscript

Using the "StartGap" command, you can set up as many gaps as you like, starting from
a particular event.

For that gap, you specify what cancels the gap, if nothing is specified, then this should default
to CANCEL_GROUND.  These conditions are checked every frame

All gaps are cancelled by a bail (perhaps unless we have CANCEL_BAIL_NOT set????)

We can also have a specific "CancelGap"  command, for fuller control.

The optional "Expire" parameter says when the gap expires.  By default gaps do not expire				   

The "EndGap" command will check to see if the gap with the specified GapID had been started
(and not cancelled, or expired), and will then Execute the gap, having an optional Score,
text (defaults to "Gap") and an optional script, which is executed.

If there is no score, then it will not count as a trick, and you just get the script executing.





*/


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

namespace Obj
{

/*
	a CGapCheck is a single entry representing a gap with a unique name
	these are stored in a list: Mdl::Skate::Instance()->m_gapcheck_list
	which is re-created individually for each level whan the level is loaded
	
	This list is used to create the "View Gaps" menu (from gamemenu.q, script launch_gap_menu)
	
	The gap list is updated in game with CGapCheck::GetGap() whena  gap is got
	
	TODO -> add LandPendingGaps to "solidlfy" gaps.  The gap should be flagged
	as "pending" when you "get" it (with an EndGap).  But it should only
	be really flagged when you sucessfully land the combo
	and it should be cleared if you bail while doing the gap 


*/	

// Constructors are needed for types derived from the list node class
// so the "this" pointer can be passed down, so it ends up
// in the data member

CGapCheck::CGapCheck(): Lst::Node< CGapCheck > ( this )
{	
	m_got = 0;
	m_got_pending = 0;
	m_valid = true;			// newly created gaps are always valid
}

int				CGapCheck::Got()
{
	return m_got;
}

void				CGapCheck::GetGap()
{
	m_got+= m_got_pending;
	m_got_pending = 0;
}

void				CGapCheck::GetGapPending()
{
	m_got_pending += 1;
}

void				CGapCheck::UnGetGapPending()
{
	m_got_pending = 0;
}

int				CGapCheck::GotGapPending()
{
	 return m_got_pending;
}


void				CGapCheck::UnGetGap()
{
	m_got = false;
}

const char	*			CGapCheck::GetText()
{
	return m_name.getString();
}

void				CGapCheck::SetText(const char *p)
{
	m_name = p;
}

void CGapCheck::SetInfoForCamera(Mth::Vector pos, Mth::Vector dir)
{
	m_skater_start_pos=pos;
	m_skater_start_dir=dir;
}

void				CGapCheck::SetCount(int count)
{
	m_got = count;
}

void 				CGapCheck::SetValid(bool valid)
{
	m_valid = valid;
}

void				CGapCheck::Reset()
{
	m_got = 0;
	m_got_pending = 0;
	m_valid = true;			// newly created gaps are always valid
}


///////////////////////////////////////////////////////////////////////

CGap::CGap(): Lst::Node< CGap > ( this )
{
    m_trickChecksum = 0;
    m_numTrickOccurrences = 0;
	m_spinMult = 0;
	m_requirePerfect = false;
	mp_tricks = new Script::CArray();
	m_startGapTrickCount = 0;
	m_numTaps = 0;
}

CGap::~CGap()
{
	if ( mp_tricks )
		Script::CleanUpArray( mp_tricks );
	delete mp_tricks;
}

CGapTrick::CGapTrick(): Lst::Node< CGapTrick > ( this )
{
}

void CGapTrick::GetGapTrick()
{
    m_got = true;
}


// delete all gapchecks
// used when we load a level						  
// K: Also used when choosing test play in the park editor, just before adding all the park editor
// gaps.
void	CGapChecklist::FlushGapChecks()
{
	CGapCheck *pOther = (CGapCheck*)m_gapcheck_list.FirstItem();
	while (pOther != NULL)
	{
		delete pOther;
		pOther = (CGapCheck*)m_gapcheck_list.FirstItem();
	}
	m_valid = false;
}

// Given a name and a count, then add a GapCheck to the list
// if it is already in the list, then just set it valid (leaving the count)						   
// (that is so when we re-scan the level's gaps after loading a level, we can remove any
// that do not exist in the level any more)
void	CGapChecklist::AddGapCheck(const char *p_name, int count, int score)
{
	
	m_valid = true;	 							// the list becomes valid when at least one gapcheck is in it
	
	CGapCheck *pOther = (CGapCheck*)m_gapcheck_list.FirstItem();
	CGapCheck *pAppend = NULL;				// last item we find that is lower than this
	while (pOther)						 		
	{
		if (!strcmp(pOther->GetText(),p_name) && pOther->GetScore() == score)   // if it matches an existing gap in both text and score
		{
			pOther->SetValid(true);				// then set that one valid (as we might be pruning later)
			return;								// then break out
		}		  
		
		if (pOther->GetScore() < score)
		{
			pAppend = pOther;
		}
		else
		{
			if (pOther->GetScore() == score && Str::Compare(pOther->GetText(),p_name)<0)
			{
				pAppend = pOther;
			}
		}
		  		
		pOther = (CGapCheck*)pOther->GetNext();
	}

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap()); 
	
	CGapCheck* pGapCheck = new CGapCheck;		// create new gap		
	pGapCheck->UnGetGap();						// say not got it yet

	pGapCheck->SetText(p_name);			   		// set the text for it
	pGapCheck->SetCount(count);			   		// and the count
	pGapCheck->SetScore(score);			   		// and the count
	if (pAppend)
	{
		// Append after the last node that is smaller or equal to than this
		pAppend->Append( pGapCheck );
	}
	else
	{
		// add to the ehad of the list if it's smaller than everything
		m_gapcheck_list.AddToHead( pGapCheck );	 
	}
	
	Mem::Manager::sHandle().PopContext();
}

																					  
void CGapChecklist::got_gap(Script::CStruct *pScriptStruct, const uint8 *pPC)
{
	int score=0;
	pScriptStruct->GetInteger(CRCD(0xcd66c8ae,"score"),&score);
	
	const char *p_name="";
	pScriptStruct->GetText(CRCD(0xc4745838,"text"),&p_name);

	// only add gaps with some text, and a score
	if (score && p_name[0])
	{
		AddGapCheck(p_name,0,score);
	}	
	else
	{
		if (Script::GetInteger("PrintGaps"))
		{
			printf ("(Name '%s' Score %d - not in checklist)\n",p_name,score); // we don't add gaps with no name or score
		}
	}
}


static CGapChecklist * p_current;

// slightly dodgy callback needed to call memmber function
static void Gap_GotGap(Script::CStruct *pScriptStruct, const uint8 *pPC)
{
	p_current->got_gap(pScriptStruct,pPC);		   
}


CGapChecklist::CGapChecklist()
{
	m_valid = false;
}


//  Find all the gaps in the level
// note, we scan regardless, so during development, we 
// can add gaps to an already saved game
// (might want to also remove gaps if they have been deleted)
void CGapChecklist::FindGaps()
{	
	Dbg_Message("Looking for gaps ...\n");
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap()); 
	
	p_current = this;	  			// store curent 'this' pointer for the callback
	m_valid = true;					// say that we have looked for gaps in this level
	
	// Maybe - scan all gaps, and flag them as not found
	
	
	if (Script::GetInteger("PrintGaps"))
	{
		printf("\nGAP LIST\n--------------------------------------\n");
	}

	SkateScript::ScanNodeScripts(CRCD(0x2ca8a299,"TriggerScript"), CRCD(0xe5399fb2,"EndGap"), Gap_GotGap);	
	
	// Maybe - and then re-scan them here, and delete them if not flagged by got_gap
	
	Mem::Manager::sHandle().PopContext(); 
	Dbg_Message("Finished looking for gaps.\n");
}

// K: Given a gap name, this will return its index withing the gap checklist, or -1 if not found.
// Used by the end_gap function in skatergapcomponent when a created gap goal is active.
int CGapChecklist::GetGapChecklistIndex(const char *p_name)
{
	int index=0;
	CGapCheck *p_search =  (CGapCheck*) m_gapcheck_list.FirstItem();
	while (p_search)						 		
	{
		if (!strcmp(p_search->GetText(),p_name))
		{
			return index;
		}	
		p_search = (CGapCheck*)p_search->GetNext();
		++index;
	}
	return -1;
}

void CGapChecklist::SetInfoForCamera(const char *p_name, Mth::Vector pos, Mth::Vector dir)
{
	CGapCheck *p_search =  (CGapCheck*) m_gapcheck_list.FirstItem();
	while (p_search)						 		
	{
		if (!strcmp(p_search->GetText(),p_name))
		{
			p_search->SetInfoForCamera(pos,dir);
			break;
		}	
		p_search = (CGapCheck*)p_search->GetNext();
	}
}

void CGapChecklist::GetGapByText(const char *p_text)
{
	CGapCheck *pOther =  (CGapCheck*) m_gapcheck_list.FirstItem();
	while (pOther)						 		
	{
		if (!strcmp(pOther->GetText(),p_text))   // if it matches and existing gap 
		{
			pOther->GetGapPending();	// flag it as pending
		
			break;								// then break out
		}	
		else
		{
		}
		pOther = (CGapCheck*)pOther->GetNext();
	}
}

void CGapChecklist::AwardPendingGaps()
{
	int gap_index=0;
	CGapCheck *pOther =  (CGapCheck*) m_gapcheck_list.FirstItem();
	char ran_got_gap_for_first_time = 0;

	while (pOther)						 		
	{
		if(pOther->GotGapPending())
		{
			// K: The gap is being awarded, so let the goal manager know that the gap has
			// just been got in case a create-gap-goal is active.
			Game::CGoalManager* p_goal_manager = Game::GetGoalManager();
			Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager ?"));
			if (p_goal_manager->CreatedGapGoalIsActive())
			{
				// Let the goal manager know that this gap has just been gone through so that
				// it can tick it off it's list of required gaps, if it is one of them.
				p_goal_manager->GetCreatedGoalGap(gap_index);
			}
			
			// The code below is commented out so multiple new gap messages can be displayed
			if ( pOther->Got() == 0 )//&& !ran_got_gap_for_first_time )
			{
				++ran_got_gap_for_first_time;
				
				// this is the first time we've gotten the gap, so give them a message
				Script::CStruct* pScriptParams = new Script::CStruct();

				if( ran_got_gap_for_first_time > 1)
					pScriptParams->AddInteger( Script::GenerateCRC("multiple_new_gaps"), ( int ) ran_got_gap_for_first_time  );

				pScriptParams->AddString( CRCD(0xc69ce365,"gap_text"), pOther->GetText() );
				Script::RunScript( CRCD(0x318eaf64,"got_gap_for_first_time"), pScriptParams );
				delete pScriptParams;
			}
			
			pOther->GetGap();
		}
		pOther->UnGetGapPending();			
		pOther = (CGapCheck*)pOther->GetNext();
		++gap_index;
	}
}

void CGapChecklist::AwardAllGaps()
{

	CGapCheck *pOther =  (CGapCheck*) m_gapcheck_list.FirstItem();

	while (pOther)						 		
	{  	
		pOther->SetCount( pOther->Got() + 1 );			
		pOther = (CGapCheck*)pOther->GetNext();
	}
}

void CGapChecklist::ClearPendingGaps()
{
	CGapCheck *pOther =  (CGapCheck*) m_gapcheck_list.FirstItem();
	while (pOther)						 		
	{
		pOther->UnGetGapPending();			
		pOther = (CGapCheck*)pOther->GetNext();
	}
}

int	CGapChecklist::NumGaps()
{
	return m_gapcheck_list.CountItems();
}


bool CGapChecklist::GotAllGaps()
{
	CGapCheck *pOther =  (CGapCheck*) m_gapcheck_list.FirstItem();
	while (pOther)						 		
	{
		if ( pOther->Got() == 0 )
			return false;
		pOther = (CGapCheck*)pOther->GetNext();
	}
	return true;
}

void CGapChecklist::Reset()
{
	CGapCheck *pOther =  (CGapCheck*) m_gapcheck_list.FirstItem();
	while (pOther)						 		
	{
		pOther->Reset();
		pOther = (CGapCheck*)pOther->GetNext();
	}
}

bool CGapChecklist::ScriptGetLevelGapTotals( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGapCheck *pGap =  (CGapCheck*) m_gapcheck_list.FirstItem();
	
	int total_gaps = 0;
	int total_got = 0;
	while (pGap)						 		
	{
		CGapCheck* pNext = (CGapCheck*)pGap->GetNext();
		total_gaps++;
	
		if ( pGap->Got() )
			total_got++;

		pGap = (CGapCheck*)pNext;
	}
	pScript->GetParams()->AddInteger( "num_gaps", total_gaps );
	pScript->GetParams()->AddInteger( "num_collected_gaps", total_got );
	return true;
}


bool CGapChecklist::ScriptAddGapsToMenu( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 choose_script=CRCD(0xf7e902c,"nullscript");
	pParams->GetChecksum(CRCD(0xa7d7c610,"choose_script"),&choose_script);


	CGapCheck *pGap =  (CGapCheck*) m_gapcheck_list.FirstItem();
	
	Script::CStruct* p_gap_params;
	int entry = 0;
	while (pGap)						 		
	{
		p_gap_params = new Script::CStruct;

		// check if this is the first or last gap
		CGapCheck* pNext = (CGapCheck*)pGap->GetNext();
		if ( entry == 0 )
			p_gap_params->AddChecksum( NONAME, CRCD( 0x171665d5, "first_item" ) );
		else if ( !pNext )
			p_gap_params->AddChecksum( NONAME, CRCD( 0x76cf1ebd, "last_item" ) );

		p_gap_params->AddInteger( "gap_score", pGap->GetScore() );
		p_gap_params->AddString( "gap_name", pGap->GetText() );
		p_gap_params->AddInteger( "times", pGap->Got() );

		p_gap_params->AddInteger( "gap_number", entry );
		
		Mth::Vector v=pGap->GetSkaterStartPos();
		p_gap_params->AddVector(CRCD(0x26a4e85a,"skater_start_pos"),v[X],v[Y],v[Z]);
		v=pGap->GetSkaterStartDir();
		p_gap_params->AddVector(CRCD(0x1cd674e6,"skater_start_dir"),v[X],v[Y],v[Z]);
		
		p_gap_params->AddChecksum(CRCD(0xa7d7c610,"choose_script"),choose_script);
		
		Script::RunScript( "gap_menu_add_item", p_gap_params );
		delete p_gap_params;

		pGap = (CGapCheck*)pNext;
		entry++;
	}

	if (entry == 0)
	{
		return false;
	}
	return true;
}

bool CGapChecklist::ScriptCreateGapList( Script::CStruct* pParams, Script::CScript* pScript )
{
	CGapCheck *p_gap =  (CGapCheck*) m_gapcheck_list.FirstItem();

	int num_gaps=0;
	while (p_gap)						 		
	{
		p_gap = (CGapCheck*)p_gap->GetNext();
		++num_gaps;
	}	
	pScript->GetParams()->AddInteger(CRCD(0x7f5b3d4d,"array_size"),num_gaps);
	
	if (!num_gaps)
	{
		pScript->GetParams()->AddInteger(CRCD(0x6a8a0061,"num_gaps_got"),0);
		return false;
	}
		
	Script::CArray *p_gap_list=new Script::CArray;
	p_gap_list->SetSizeAndType(num_gaps,ESYMBOLTYPE_STRUCTURE);
	
	p_gap =  (CGapCheck*) m_gapcheck_list.FirstItem();
	
	int gap_number = 0;
	int num_gaps_got=0;
	while (p_gap)						 		
	{
		if (p_gap->Got())
		{
			++num_gaps_got;
		}
			
		Script::CStruct* p_gap_params = new Script::CStruct;

		p_gap_params->AddInteger( "gap_score", p_gap->GetScore() );
		p_gap_params->AddString( "gap_name", p_gap->GetText() );
		p_gap_params->AddInteger( "times", p_gap->Got() );

		p_gap_params->AddInteger( "gap_number", gap_number );
		
		Mth::Vector v=p_gap->GetSkaterStartPos();
		p_gap_params->AddVector(CRCD(0x26a4e85a,"skater_start_pos"),v[X],v[Y],v[Z]);
		v=p_gap->GetSkaterStartDir();
		p_gap_params->AddVector(CRCD(0x1cd674e6,"skater_start_dir"),v[X],v[Y],v[Z]);
		
		p_gap_list->SetStructure(gap_number,p_gap_params);
		p_gap = (CGapCheck*)p_gap->GetNext();
		++gap_number;
	}

	pScript->GetParams()->AddArrayPointer(CRCD(0x737449de,"GapList"),p_gap_list);
	pScript->GetParams()->AddInteger(CRCD(0x6a8a0061,"num_gaps_got"),num_gaps_got);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AddgapsToMenu | adds all gaps to the menu
bool ScriptAddGapsToMenu( Script::CStruct* pParams, Script::CScript* pScript )
{
	
	bool result = 	Mdl::Skate::Instance()->GetGapChecklist()->ScriptAddGapsToMenu(pParams,pScript); 
	
	
	return result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CreateGapList | Similar to AddGapsToMenu, but instead creates an array of structures called GapList
// and adds it to the calling script's parameters.
bool ScriptCreateGapList( Script::CStruct* pParams, Script::CScript* pScript )
{
	return Mdl::Skate::Instance()->GetGapChecklist()->ScriptCreateGapList(pParams,pScript); 
}

// @script | GetLevelGapTotals | returns the total number of gaps and the number
// of gaps that have been completed in the level (num_gaps, num_completed_gaps)
// @parm int | level | the level number
bool ScriptGetLevelGapTotals( Script::CStruct* pParams, Script::CScript* pScript )
{
	int level;
	pParams->GetInteger( "level", &level, Script::ASSERT );
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkaterCareer* pCareer = skate_mod->GetCareer();
	
	bool result = pCareer->GetGapChecklist( level )->ScriptGetLevelGapTotals( pParams, pScript );
	return result;
}

// @script | GiveAllGaps | "Gets" all the gaps in level or current level if none is specified
// @parm int | level | the level number | optional
bool ScriptGiveAllGaps( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkaterCareer* pCareer = skate_mod->GetCareer();

	int level;
	if(!pParams->GetInteger( "level", &level, Script::NO_ASSERT ))
		level = pCareer->GetLevel();
	
	pCareer->GetGapChecklist( level )->AwardAllGaps();
	return true;
}


CGapCheck *CGapChecklist::get_indexed_gapcheck(int gap_index)
{

	CGapCheck *pGap =  (CGapCheck*) m_gapcheck_list.FirstItem();
	int 	entry = 0;
	while (pGap)						 		
	{
		if (entry == gap_index)
		{
			return pGap;		
		}
		pGap = (CGapCheck*)pGap->GetNext();
		entry++;
	}
	Dbg_MsgAssert(0,("Gap index %ds invalid\n",gap_index));	
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CGapChecklist::ScriptGotGap( Script::CStruct* pParams, Script::CScript* pScript )
{
	int	gap_index = 0;
	pParams->GetInteger(NONAME,&gap_index,true);		 
	return get_indexed_gapcheck(gap_index)->Got();
}

const char *	CGapChecklist::GetGapText(int gap)
{
	return get_indexed_gapcheck(gap)->GetText();
}

int	CGapChecklist::GetGapCount(int gap)
{
	return get_indexed_gapcheck(gap)->Got();
}

int	CGapChecklist::GetGapScore(int gap)
{
	return get_indexed_gapcheck(gap)->GetScore();
}

void CGapChecklist::GetSkaterStartPosAndDir(int gap, Mth::Vector *p_skaterStartPos, Mth::Vector *p_skaterStartDir)
{
	CGapCheck *p_gap_check=get_indexed_gapcheck(gap);
	
	*p_skaterStartPos=p_gap_check->GetSkaterStartPos();
	*p_skaterStartDir=p_gap_check->GetSkaterStartDir();
}

// @script | GotGap | returns true if we have got the indicated gap
// this really only makes sense in the context of the gap menu (see AddGapsToMenu)
bool ScriptGotGap( Script::CStruct* pParams, Script::CScript* pScript )
{
	return Mdl::Skate::Instance()->GetGapChecklist()->ScriptGotGap(pParams,pScript); 
}


 
}	// end of Ojb namespace




================================================
FILE: Code/Sk/Objects/gap.h
================================================
////////////////////////////////////////////////////////////
// gap.h

#ifndef	__GAP_H__
#define	__GAP_H__

#define	__USE_MJB_LIST__
	  
#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __CORE_MATH_H
#include 
#endif

#include 
#include 

#include 

namespace Script
{
	class CStruct;
	class CScript;
}

namespace Obj
{

#define	CANCEL_GROUND				0x00000001
#define	CANCEL_AIR              	0x00000002
#define	CANCEL_RAIL             	0x00000004
#define	CANCEL_WALL             	0x00000008
#define	CANCEL_LIP              	0x00000010
#define	CANCEL_WALLPLANT			0x00000020
#define	CANCEL_MANUAL				0x00000040
#define CANCEL_HANG     	    	0x00000080
#define CANCEL_LADDER	         	0x00000100
#define CANCEL_SKATE				0x00000200
#define CANCEL_WALK					0x00000400
#define CANCEL_DRIVE				0x00000800

#define	CANCEL_MASK					0x0000FFFF

#define	REQUIRE_GROUND				0x00010000
#define	REQUIRE_AIR             	0x00020000
#define	REQUIRE_RAIL            	0x00040000
#define	REQUIRE_WALL            	0x00080000
#define	REQUIRE_LIP             	0x00100000
#define	REQUIRE_WALLPLANT			0x00200000
#define	REQUIRE_MANUAL				0x00400000
#define REQUIRE_HANG	        	0x00800000
#define REQUIRE_LADDER	        	0x01000000
#define REQUIRE_SKATE				0x02000000
#define REQUIRE_WALK				0x04000000
#define REQUIRE_DRIVE				0x08000000
                                
#define	REQUIRE_MASK				0xFFFF0000


class CGapCheck : public Lst::Node< CGapCheck >
{
public:
						CGapCheck();
	int					Got();
	void				GetGap();
	
	void				GetGapPending();
	void				UnGetGapPending();
	int					GotGapPending();
	
	void				SetInfoForCamera(Mth::Vector pos, Mth::Vector dir);
	Mth::Vector&		GetSkaterStartPos() {return m_skater_start_pos;}
	Mth::Vector&		GetSkaterStartDir() {return m_skater_start_dir;}
	
	
	void				UnGetGap();
	const char	*		GetText();
	void				SetText(const char *p);
	void				SetCount(int count);
	void				SetScore(int score) {m_score = score;}
	int					GetScore() {return m_score;};
	void 				SetValid(bool valid);

	void				Reset();
private:
	Mth::Vector			m_skater_start_pos;
	Mth::Vector			m_skater_start_dir;
	
	int					m_got_pending;			// number of times we have got this gap, but the landing of the combo is still pending
	int					m_got;					// number of times we have got this gap
	int					m_score;				// score for this gap
	bool				m_valid;				// flag used when parsing gaps to remove any that no longer exist (from an old save)
	Str::String			m_name;
};



// CGapChecklist contains a list of gaps for the level
// these gaps (CGap) count how many times they have been got,
// and so this list is used to display the in-game gap checklist
// there is one of these for each level in an array in CSkaterCareer
// 
class CGapChecklist 
{

public:
										CGapChecklist();
	void 								FindGaps();
	void 								GetGapByText(const char *p_text);
	int									GetGapChecklistIndex(const char *p_name);
	void								SetInfoForCamera(const char *p_name, Mth::Vector pos, Mth::Vector dir);
	void 								AwardPendingGaps(); 
	void 								AwardAllGaps();
	void 								ClearPendingGaps();
	bool 								ScriptAddGapsToMenu( Script::CStruct* pParams, Script::CScript* pScript );
	bool								ScriptCreateGapList( Script::CStruct* pParams, Script::CScript* pScript );
	
	bool								ScriptGotGap( Script::CStruct* pParams, Script::CScript* pScript );
	bool								ScriptGetLevelGapTotals( Script::CStruct* pParams, Script::CScript* pScript );
	
	bool								IsValid() {return m_valid;}
	int									NumGaps();

	const char *						GetGapText(int gap);
	int									GetGapCount(int gap);
	int									GetGapScore(int gap);
	void								GetSkaterStartPosAndDir(int gap, Mth::Vector *p_skaterStartPos, Mth::Vector *p_skaterStartDir);

	void								AddGapCheck(const char *p_name, int count, int score);
	void								FlushGapChecks();

	bool								GotAllGaps();
	void								Reset();

// semi-private, dammit...	
	void 								got_gap(Script::CStruct *pScriptStruct, const uint8 *pPC);
private:

	Obj::CGapCheck *					get_indexed_gapcheck(int gap_index);
	bool								m_valid;			// true if we'd parsed the scripts for this level
	Lst::Head< Obj::CGapCheck >			m_gapcheck_list;	// list of unique gaps in level, or NULL if none
};

// CGap is a gap we are in the middle of
class CGap : public Lst::Node< CGap >
{
	public:	
		CGap();
		~CGap();
	public:
		uint32			m_id;					// checksum of the gap's ID
		uint32			m_flags;				// bit fields saying what cancles it
		uint32			m_node;					// source node
        uint32          m_trickChecksum;
        int             m_numTrickOccurrences;
        uint32          m_trickscript;
		uint32			m_trickTextChecksum;
		int				m_spinMult;
		bool			m_requirePerfect;
		int				m_startGapTrickCount;
		int				m_numTaps;

		// K: The position & direction the skater is facing when the StartGap command was executed.
		// Used to determine a good camera position when showing the gap in the view-gaps menu.
		Mth::Vector		m_skater_start_pos;
		Mth::Vector		m_skater_start_dir;
				
		// these arrays are used for arrays of tricks that must
		// be completed in order to get the gap.
		Script::CArray* mp_tricks;
};

// CGaptrick contains list if gaps we are in the middle of, that contain tricks
class CGapTrick : public Lst::Node< CGapTrick >
{
public:
	CGapTrick();
    void                GetGapTrick();
public:
    Str::String         m_trickString;
    uint32  			m_id;                   // matches the gap's ID
	uint32	 			m_node;					// node this came from
	uint32	 			m_script;				// script to run when sucessful
    bool                m_got;
	uint32				m_frame;				// Logic frame number on which this was triggered
};

// Some gap utility functions
bool ScriptAddGapsToMenu( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptCreateGapList( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetLevelGapTotals( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGiveAllGaps( Script::CStruct* pParams, Script::CScript* pScript );

}  // end namespace obj














#endif


================================================
FILE: Code/Sk/Objects/manual.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Objects (OBJ) 											**
**																			**
**	File name:		manual.cpp												**
**																			**
**	Created:		01/31/01	-	ksh										**
**																			**
**	Description:	Skater object code										**
**																			**
*****************************************************************************/

//#define		__DEBUG_CHEESE__

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 


#include 

#include 
#include 

#include 

#include 

#include  

#include 
#include 
#include 
#include 

#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Obj
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

CManual::CManual()
{
	Reset();
}

CManual::~CManual()
{
	
}

void CManual::Init ( CCompositeObject *pSkater )
{
	mpSkater = pSkater;
	Dbg_Assert(mpSkater);
	
	mpSkaterBalanceTrickComponent = GetSkaterBalanceTrickComponentFromObject(mpSkater);
	Dbg_Assert(mpSkaterBalanceTrickComponent);
	
	mpSkaterScoreComponent = GetSkaterScoreComponentFromObject(mpSkater);
	Dbg_Assert(mpSkaterScoreComponent);
	
	mpInputComponent = GetInputComponentFromObject(mpSkater);
	Dbg_Assert(mpInputComponent);
}

void CManual::Reset()
{

#ifdef		__DEBUG_CHEESE__
	printf ("%d:   Resetting cheese to 0 in CManual::Reset\n",__LINE__);
#endif	
	mManualCheese=0.0f;
	mActualManualTime=0.0f;
	mManualTime=0.0f;
	mManualLean=0.0f;
	mManualLeanDir=0.0f;
	mManualReleased=false;
	mOldRailNode=-1;
	mWobbleEnabled=false;
}

// a utility function to scale a linear value based on 
// the framerate
// m_time will be 1/60 when running at 60fps
// and 1/50 when running at 50fps (for pal)
// So, this 60.0f is correct for PAL
// as the adjustment is done to m_time					
float CManual::scale_with_frame_length ( float frame_length, float f )
{
	return f * frame_length * 60.0f;	  // Do NOT change this in PAL mode, leave it at 60
}

// clear the record time for this level, only call this at start of a session
void CManual::ClearMaxTime()
{
	
	mMaxTime=0.0f;
}


void CManual::SwitchOffMeters()
{
	
	mNeverShowMeters=true;
}

void CManual::SwitchOnMeters()
{
	
	mNeverShowMeters=false;
}

// Switched off the meters and sets the buttons to zero so that calling DoManualPhysics will
// have no effect.
void CManual::Stop()
{
	
	// Switch off the meters.
	if (mpSkaterScoreComponent->GetScore())
	{
		mpSkaterScoreComponent->GetScore()->SetBalanceMeter(false);
		mpSkaterScoreComponent->GetScore()->SetManualMeter(false);
	} 
	mButtonAChecksum=0;
	mButtonBChecksum=0;
}


// Check to see if this is a new record				 
void CManual::UpdateRecord()
{
    // Update the longest grind/manual/lip
	if (mActualManualTime>mMaxTime)
	{
		mMaxTime=mActualManualTime;
	}
	mActualManualTime = 0.0f;
}



void CManual::SetUp(uint32 ButtonAChecksum, uint32 ButtonBChecksum, int Tweak, bool DoFlipCheck, bool PlayRangeAnimBackwards)
{
	
	mActualManualTime = 0.0f;
	mManualReleased=false;
	mButtonAChecksum=ButtonAChecksum;
	mButtonBChecksum=ButtonBChecksum;
	mTweak=Tweak;
	mDoFlipCheck=DoFlipCheck;
	mPlayRangeAnimBackwards=PlayRangeAnimBackwards;

	Dbg_MsgAssert(mpSkater,("NULL mpSkater"));

	// Check for whether the skater has re-railed onto the same rail
	// or a different one.
	if (mpSkaterBalanceTrickComponent->mBalanceTrickType==0x255ed86f || // Grind
		mpSkaterBalanceTrickComponent->mBalanceTrickType==0x8d10119d) // Slide
	{
		const CRailNode* p_rail_node = GetSkaterCorePhysicsComponentFromObject(mpSkater)->mp_rail_node;
		
		Dbg_MsgAssert(p_rail_node,("What, NULL mp_rail_node ??"));
		
		if (mOldRailNode==-1)
		{
			// This is a fresh rail.
		}
		else
		{
		
			if (p_rail_node->ProbablyOnSameRailAs(mOldRailNode))
			{
				// Jumped and landed on the same rail without ending
				// the combo.
				// Add some time to make this more difficult.
				mManualTime+=mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0xfbec5b6c, "Same_Grind_Add_Time"));
				#ifdef		__DEBUG_CHEESE__
					printf ("%d:  %.2f:  leaving cheese, probably on same rail\n",__LINE__,mManualCheese);
				#endif					
			}
			else
			{
				// It's a different rail from the last one, ie jumped from a rail to
				// another rail without ending the combo.
				// Reward the player by subtracting a bit of time to make it easier.
				mManualTime-=mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0xc5e1c8eb,"New_Grind_Sub_Time"));
				if (mManualTime<0.0f)
				{
					mManualTime=0.0f;
				}	
				// And clear the cheese
				// as it's not cheesey to change rails
				// so we don't to penalize the player for doing so
				#ifdef		__DEBUG_CHEESE__
					printf ("%d:   Resetting cheese to 0 as it's a new grind\n",__LINE__);
				#endif	

				mManualCheese = 0.0f;
			}	
		}	
		mOldRailNode=p_rail_node->GetNode();
		
		Mdl::Score *pScore=mpSkaterScoreComponent->GetScore();
		float robot = pScore->GetRobotRailMult();
		if ( robot < 0.5f)	   // <0.5 means any time you are on the same rail twice in a combo
		{
//			printf ("SHOULD ADJUST BALANCE HERE, AS ON A ROBOT LINE\n");
			// Need to add something to the Manual Time
			mManualTime += Script::GetFloat(CRCD(0xf54ca12f,"robot_rail_add_time") )* 0.5f / robot;
			
			// And give him  percentage nudge away from 0
			// (actually, an absolute value would be better, more consistent
			// perhaps not even based on the amount, just trigger 20% whenever  
			// you land on something robotesque
			mManualLean += Script::GetFloat(CRCD(0x30990680,"robot_rail_nudge")) * Mth::Sgn(mManualLeanDir) * 0.5f / robot;
//			printf ("Time = %.2f,  Lean = %.2f, dir = %.2f\n",mManualTime, mManualLean, mManualLeanDir);
		}
		
		
	}

	float repeat_min = mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0x8f4db5d3,"Repeat_Min"));
	if ( Mth::Abs(mManualLeanDir) != 0.0f)
	{
		// first multiplied by the reat multipler
		mManualLeanDir = mManualLeanDir * mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0x93dd6a10,"Repeat_Multiplier"));		
				
		// use the sign of previous balance
		if ( Mth::Abs(mManualLeanDir) < repeat_min)
		{
			//  if too small, then set to minimum
			mManualLeanDir = repeat_min * Mth::Sgn(mManualLeanDir);
		}	
		else
		{
		}
	}
	else
	{
		// No previous balance
		// so just set to default minimum
		mManualLeanDir = repeat_min;
		// Adjust the sign of the lean to point in the direction of a bail.
		switch (mpSkaterBalanceTrickComponent->mBalanceTrickType)
		{
			case 0xef24413b: // Manual
				// Manuals keep positive sign.
				break;
			case 0x0ac90769: // NoseManual	
				// Nosemanuals fall the other way.
				mManualLeanDir=-mManualLeanDir;
				break;
			default:
				// If in a grind, choose a random side to fall towards.
				// Hmm, should choose differently for a lip?
				if (Mth::Rnd(2))
				{
					mManualLeanDir=-mManualLeanDir;
				}
				break;
		}		

	}
	
	
	// multiply this initial direction by some value based on the time
//	mManualLean = Mth::Abs(mManualLean) * Mth::Sgn(mManualLeanDir);		// possibly alter lean to face the bail direction

	mManualLean *= mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0x6ce10ea4,"Lean_Repeat_Multiplier"));

	// add cheese after lean multiply (otherwise, it can be ineffective)																				 
	#ifdef		__DEBUG_CHEESE__
		printf ("%d:  %.2f + %.2f = %.2f:  Adding cheese to lean \n",__LINE__,mManualLean, mManualCheese * Mth::Sgn(mManualLean),mManualLean += mManualCheese * Mth::Sgn(mManualLean));
	#endif					

	mManualLean += mManualCheese * Mth::Sgn(mManualLean);				// add in the cheese	to the lean
	
	float BailAngle=mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0xf81ab0e9,"Lean_Bail_Angle"));
	if (Mth::Abs(mManualLean) > BailAngle)
	{
		#ifdef		__DEBUG_CHEESE__
			printf ("%d:  %.2f:  Cheese caused an instant bail!!! \n",__LINE__,mManualCheese);
		#endif					
		// The cheese has caused an instant bail, so in order that the meter does become
		// visible, make the lean a wee bit less that the bail angle.
		mManualLean=BailAngle*0.9f;
		mManualLean = Mth::Abs(mManualLean) * Mth::Sgn(mManualLeanDir);
		mManualLeanDir = Mth::Sgn(mManualLeanDir) * BailAngle;  // ensure a bail next frame
	}

	
	// Reset the cheese.
	mManualCheese=mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0x1231ae3c,"Cheese"));

	#ifdef		__DEBUG_CHEESE__
		printf ("%d:  %.2f:  Cheese reset to initial scripted value \n",__LINE__,mManualCheese);
	#endif					

	
	// The programmatic wobble applied to the animation is off initially, so that it does not get applied
	// to the init anim.
	mWobbleEnabled=false;
	
	mStartTime=Tmr::ElapsedTime(0);
}

// Gets called by the EnableWobble skater script command.
void CManual::EnableWobble()
{
	
	mWobbleEnabled=true;
}

//static float MaxTime=0;
void CManual::DoManualPhysics()
{
	float frame_length = Tmr::FrameLength();
	
	// Do nothing if no buttons are specified.
	if (!mButtonAChecksum || !mButtonBChecksum)
	{
		return;
	}
		
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Net::Server* server;
	GameNet::PlayerInfo* player;
	Net::Conn* skater_conn;
	
	Dbg_MsgAssert(mpSkater,("NULL mpSkater"));
	
	skater_conn = NULL;
	if(( server = gamenet_man->GetServer()))
	{
		if(( player = gamenet_man->GetPlayerByObjectID( mpSkater->GetID() )))
		{
			skater_conn = player->m_Conn;
		}
	}
	
	// decrement the cheese value, which is added to the lean angle when we do the next manual
	// this is to prevent "cheesing" the manuals (by rapidly doing several manuals that only
	// last a frame or two, and don't give the balancing physics any time to take effect).

	if (mManualCheese>0.01f)
	{
		mManualCheese -= scale_with_frame_length(frame_length, mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0x1231ae3c,"Cheese"))/mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0xa4362662,"CheeseFrames")));
		if (mManualCheese < 0.0f)
			mManualCheese = 0.0f;
		#ifdef		__DEBUG_CHEESE__
			printf ("%d:  %.2f:  Cheese decremented\n",__LINE__,mManualCheese);
		#endif					

	}
//	printf(Cheese=%f\n",mManualCheese);

	// Add in the tweak to the score.
	Mdl::Score *pScore=mpSkaterScoreComponent->GetScore();
	//pScore->TweakTrickRequest(mTweak);
	pScore->TweakTrick(mTweak);

	mActualManualTime+=frame_length;
	mManualTime+=frame_length;

    Obj::CStatsManagerComponent* pStatsManagerComponent = GetStatsManagerComponentFromObject(mpSkater);
	Dbg_Assert( pStatsManagerComponent );
    
    pStatsManagerComponent->CheckTimedRecord( mpSkaterBalanceTrickComponent->mBalanceTrickType, mActualManualTime, false );
	
	float ManualLeanGravity=mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0x6851748,"Lean_Gravity_Stat"));
	float ManualInstableRate=mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0xa9fef5ab,"Instable_Rate"));
	float ManualBase = mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0xb6a634f3,"Instable_base")); 
	float ManualInstability = ManualBase + (mManualTime * ManualInstableRate);

	
	// the more you lean, the more you lean
//	mManualLean += scale_with_frame_length(mManualLean/ManualLeanGravity * (0.5f + mManualTime/2)/ManualInstableRate);
//	mManualLean += scale_with_frame_length(mManualLeanDir * (0.5f + mManualTime/2)/ManualInstableRate);	 // speed of turn
	
	mManualLean += scale_with_frame_length(frame_length, mManualLean * ManualLeanGravity * ManualInstability);
	mManualLean += scale_with_frame_length(frame_length, mManualLeanDir * ManualInstability);	 	


	CSkaterButton *pButt=mpInputComponent->GetControlPad().GetButton(mButtonAChecksum);
	bool APressed=pButt->GetPressed();
	
	pButt=mpInputComponent->GetControlPad().GetButton(mButtonBChecksum);
	bool BPressed=pButt->GetPressed();

	// The released flag stays false until they let go of the buttons. This is so that if the player
	// was holding one of the control buttons at the instant the balance trick started, as they probably
	// will be if doing a manual, then the button won't have any effect on the balance, so as not to
	// unbalance them straight away.
	if (!APressed && !BPressed)
	{
		mManualReleased = true;
	}
	
	
	uint32   safe_period = (uint32)Script::GetInt(CRCD(0x8cc8f176,"BalanceSafeButtonPeriod"));
	uint32	 elapsed_time = Tmr::ElapsedTime(mStartTime);

	// If the buttons have not technically been released, but have been held for longer than a certain
	// amount of time, then assume that the player does want the button to have an effect after all.
	if (!mManualReleased && elapsed_time>(uint32)Script::GetInt(CRCD(0x7da1621,"BalanceIgnoreButtonPeriod")))
	{
		mManualReleased = true;
	}
	
	
	if (APressed && mManualReleased)
	{
		// this ramping up of the accleration for a few hundred miliseconds
		// will prevent us from overcompensating
		// due to the button being held down
		// and will actually make it easier to "Stick" the mManualLeanDir
		// the ramping is only in effect if you press in the direction opposite 
		// from what you need to get more upright
		// so it prevents you from becoming more unbalanced
		float mult = 1.0f;
		if (mManualLean < 0.0f && elapsed_time < safe_period)
		{
			mult =  elapsed_time / safe_period; 
		}
		
		mManualLeanDir -= scale_with_frame_length(frame_length, mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0x61ad5596,"Lean_Acc"))) * mult;
	}
	else if (BPressed && mManualReleased)
	{
		float mult = 1.0f;
		if (mManualLean > 0.0f && elapsed_time < safe_period)
		{
			mult =  elapsed_time / safe_period; 
		}
		mManualLeanDir += scale_with_frame_length(frame_length, mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0x61ad5596,"Lean_Acc"))) * mult;
	}
	else
	{
		if (Mth::Abs(mManualLeanDir) < mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0xef27e8ff,"Lean_Min_Speed")))
		{
			int r=(int)mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0x32eb3308,"Lean_Rnd_Speed"));
			r|=1;
//			mManualLeanDir = Mth::Rnd(r)-r/2;
			mManualLeanDir = Mth::Rnd(r)*Mth::Sgn(mManualLeanDir);
		}
		else
		{
			// always add some randomness to it, to make sure we don't ever get stuck
			mManualLeanDir += (Mth::Rnd(50))/100.0f * Mth::Sgn(mManualLeanDir);
		}
	}

	
	/*
	HUD::PanelMgr* panelMgr = HUD::PanelMgr::Instance();
	HUD::Panel *pPanel=panelMgr->GetPanelBySkaterId(mpSkater->GetID());
	
	char pBuf[100];
	sprintf(pBuf,"Record %.2f",mMaxTime);
	panelMgr->SendConsoleMessage(pBuf, 2);
	sprintf(pBuf,"Time %.2f",mActualManualTime);
	panelMgr->SendConsoleMessage(pBuf, 3);
	*/

	if (!mpSkater->GetScript())
	{
		mpSkater->SetScript(new Script::CScript);
	}

	if (Mth::Abs(mManualLean)>mpSkaterBalanceTrickComponent->GetBalanceStat(CRCD(0xf81ab0e9,"Lean_Bail_Angle")))
	{
		if (mManualLean>0)
		{
			mpSkater->SelfEvent(CRCD(0xd77b6ff9,"OffMeterTop"));
		}
		else	
		{
			mpSkater->SelfEvent(CRCD(0x47d44b84,"OffMeterBottom"));
		}

		mManualLean=0;
		mpSkaterBalanceTrickComponent->mBalanceTrickType=0;
	}	

	if (mWobbleEnabled)
	{
		bool flip=mPlayRangeAnimBackwards;
		if (mDoFlipCheck && GetSkaterCorePhysicsComponentFromObject(mpSkater)->GetFlag(FLIPPED))
		{
			flip=!flip;
		}	
		// Update the anim frame.
		Obj::CAnimationComponent* pAnimComponent = GetAnimationComponentFromObject(mpSkater);
        if ( pAnimComponent )
        {
            if (flip)
            {
                pAnimComponent->SetWobbleTarget((-mManualLean+4096)/8192, true);
            }
            else
            {
                pAnimComponent->SetWobbleTarget((mManualLean+4096)/8192, true);
            }		
        }
	}	


	// Update the balance meter.
	GameNet::MsgByteInfo msg;
	float final_val = -mManualLean/4096;
	
	if( server && skater_conn )
	{
		msg.m_Data = (char) ( final_val * 127.0f );
		/*server->EnqueueMessage( skater_conn->GetHandle(), GameNet::MSG_ID_SET_MANUAL_METER, 
								sizeof(GameNet::MsgByteInfo), &msg );*/
	}
	
	// Need to also check we are still doing a balance trick, since exceptions
	// that we fired above (offmetertop/bottom) might have canceled it
	if (!mNeverShowMeters && mpSkaterBalanceTrickComponent->mBalanceTrickType)
	{
		if ((mButtonAChecksum==0xbc6b118f/*Up*/ || mButtonAChecksum==0xe3006fc4/*Down*/) &&
			(mButtonBChecksum==0xbc6b118f/*Up*/ || mButtonBChecksum==0xe3006fc4/*Down*/))
		{
			if (mpSkaterScoreComponent->GetScore())
			{
				mpSkaterScoreComponent->GetScore()->SetManualMeter( true, final_val );
			}
		}
		else
		{
			if (mpSkaterScoreComponent->GetScore())
			{
				mpSkaterScoreComponent->GetScore()->SetBalanceMeter( true, final_val );
			}
		}
	}	 
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj




================================================
FILE: Code/Sk/Objects/manual.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Object (OBJ)											**
**																			**
**	File name:		objects/manual.h										**
**																			**
**	Created: 		01/31/01	-	ksh										**
**																			**
*****************************************************************************/

#ifndef __OBJECTS_MANUAL_H
#define __OBJECTS_MANUAL_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/



#ifndef __CORE_DEFINES_H
#include 
#endif

#include 

#include 

#include 
#include 
#include 

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/
namespace Obj
{
	class CSkaterBalanceTrickComponent;
	class CSkaterScoreComponent;
	class CInputComponent;
	
/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class  CManual  : public Spt::Class
{
	
public:								// for debugging

	CCompositeObject *mpSkater;
	CSkaterBalanceTrickComponent* mpSkaterBalanceTrickComponent;
	CSkaterScoreComponent* mpSkaterScoreComponent;
	CInputComponent* mpInputComponent;
	
	Mth::Vector mManualPos;
	float mActualManualTime;
	float mManualTime;
	float mMaxTime;
	
	float mManualLean;
	float mManualLeanDir;
	float mManualCheese;
	bool mManualReleased;
	
	uint32 mStartTime;
	uint32 mButtonAChecksum;
	uint32 mButtonBChecksum;
	
	int mTweak;
	
	int mOldRailNode;
	
	bool mWobbleEnabled;
	bool mNeverShowMeters;

	// If true, then the animation will be moved in the opposite direction
	// when the skater is flipped.
	// This is required when the animation moves from side to side as in
	// the case of most grinds, but not for anims that move forward and
	// back, such as manuals.
	bool mDoFlipCheck;
	
	// If set, the range anim will be played backwards. Can be used in conjunction with
	// mDoFlipCheck.
	bool mPlayRangeAnimBackwards;
		
public:
	CManual();
	virtual ~CManual();
	
	void Init ( CCompositeObject *pSkater );

	void ResetCheese() {mManualCheese=0.0f;}	
	void Reset();
	void ClearMaxTime();
	void SwitchOffMeters();
	void SwitchOnMeters();
	void Stop();
	void UpdateRecord();

	float	GetMaxTime() {return mMaxTime;}
	
	void SetUp(uint32 ButtonAChecksum, uint32 ButtonBChecksum, int Tweak, bool DoFlipCheck, bool PlayRangeAnimBackwards);
	void EnableWobble();
	void DoManualPhysics();
	
private:
	inline float scale_with_frame_length ( float frame_length, float f );
	
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_MANUAL_H


================================================
FILE: Code/Sk/Objects/moviecam.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       moviemanager.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  02/07/2002
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 

#include 

// TODO:  Remove dependency on CSkater class...
#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CMovieManager::get_movie_count()
{
	return m_movieDetailsList.CountItems();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMovieDetails* CMovieManager::get_movie_details( int index )
{
	if ( get_movie_count() == 0 )
	{
		return NULL;
	}

	return (CMovieDetails*)m_movieDetailsList.GetItem( index );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMovieDetails* CMovieManager::get_movie_details_by_name( uint32 name )
{
	for ( int i = 0; i < get_movie_count(); i++ )
	{
		CMovieDetails* pDetails = get_movie_details( i );
		Dbg_Assert( pDetails );
		if ( pDetails->GetName() == name )
		{
			return pDetails;
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovieManager::setup_last_movie_camera( CMovieDetails* pDetails )
{
	// Check if this is an overridding camera
	if ( !( pDetails && pDetails->OverridesCamera() ) )
	{
		return;
	}

	// Don't set up if the game is paused or in the skateshop
	if ( pDetails->NeedsCameraTransition() &&
		 !Mdl::FrontEnd::Instance()->GamePaused() &&
		 (Mdl::Skate::Instance()->m_cur_level != CRCD(0x9f2bafb7,"Load_Skateshop") ) )
	{
		m_last_movie_camera = *(pDetails->GetCamera());
		m_last_movie_frame = Tmr::GetRenderFrame() + 1;		// End it one frame later so there aren't any glitches
		m_use_last_movie_camera = true;
	}
	else
	{
//		Script::RunScript( CRCD( 0x15674315, "Restore_skater_camera" ) );  // Temp patch to get the skater camera back at end of cutscenes
	}
}

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMovieManager::CMovieManager()
{
	m_use_last_movie_camera = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMovieManager::~CMovieManager()
{
	ClearMovieQueue();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::Camera* CMovieManager::GetActiveCamera()
{
	CMovieDetails* pDetails = get_movie_details( 0 );
	if ( pDetails && pDetails->OverridesCamera() )
	{
		return pDetails->GetCamera();
	}
	else if ( m_use_last_movie_camera )
	{
		return &m_last_movie_camera;
	}
	else
	{
		return NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovieManager::ClearMovieQueue( void )
{
	if ( !m_movieDetailsList.IsEmpty() )
	{
		setup_last_movie_camera( get_movie_details( 0 ) );
		//Script::RunScript( CRCD( 0x15674315, "Restore_skater_camera" ) );  // Temp patch to get the skater camera back at end of cutscenes
	}
	m_movieDetailsList.DestroyAllNodes();
	//Dbg_Message("Removing all movies\n");
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovieManager::CleanupMovie( CMovieDetails* pDetails )
{
	Dbg_MsgAssert( pDetails, ( "No details?" ) );

	// remember the exit script, for use
	// after the movie has been removed
	// from the list
	uint32 exitScriptName = pDetails->GetExitScriptName();
	Script::CStruct* pTempExitParams = new Script::CStruct;
	pTempExitParams->AppendStructure( pDetails->GetExitParams() );
										  
	// Check if last movie
	if (get_movie_count() == 1)
	{
		setup_last_movie_camera(pDetails);
	}

	pDetails->Cleanup();
	pDetails->Remove();
	delete pDetails;
	printf( "Details have been removed %ld\n", Tmr::GetRenderFrame() );
	
	// GJ:  run the exit script AFTER we delete it
	// need to do it after the details are deleted
	// in case the exit script affects the camera
	// somehow (would cause a glitch otherwise)
	if ( exitScriptName != 0 )
	{
		Script::RunScript( exitScriptName, pTempExitParams, NULL );
	}

	delete pTempExitParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::AbortCurrentMovie( bool only_if_skippable )
{
	bool aborted = false;
	
	if ( !only_if_skippable )
	{
		CMovieDetails* pDetails = get_movie_details( 0 );
		if ( pDetails )
		{
			CleanupMovie( pDetails );
			
			aborted = true;
		}
	}
	else
	{
		// delete as many skippable movies as there are...
		CMovieDetails* pDetails = get_movie_details( 0 );
		while ( pDetails && pDetails->IsSkippable() )
		{
			CleanupMovie( pDetails );
			
			pDetails = get_movie_details( 0 );
			aborted = true;
		}
	}

	// now that a new movie is about to begin,
	// reset its custom keys
	CMovieDetails* pDetails = get_movie_details( 0 );
	if ( pDetails )
	{
		pDetails->ResetCustomKeys();
	}

	// restore skater cam
	// Nx::CViewportManager::sSetCamera( 0, Mdl::Skate::Instance()->GetSkater(0)->GetCamera() );

	// if there are no more movies, then restore the camera
//	if (aborted)  <-- causes one frame glitch with queued up cameras
	if ( !aborted && (get_movie_count() == 0) )
	{
//		Script::RunScript(CRCD(0x15674315, "Restore_skater_camera"));		// Temp pathc to get the skater camera back at end of cutscenes
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::SetMovieSkippable( uint32 name, bool skippable )
{
	if ( m_movieDetailsList.CountItems() == 0 )
	{
		// no movies in the queue
		return false;
	}

	if ( name == 0 )
	{
		// no name specified, so set the current movie
		CMovieDetails* pDetails = get_movie_details( 0 );
		Dbg_Assert( pDetails );
		pDetails->SetSkippable( skippable );
	}
	else
	{
		// a name was specified, so only do this movie
		CMovieDetails* pDetails = get_movie_details_by_name( name );
		if ( pDetails )
		{
			pDetails->SetSkippable( skippable );
		}
		else
		{
			Dbg_MsgAssert( 0, ( "Movie to skip (%s) not found\n", Script::FindChecksumName(name) ) );
		}
	}

	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::SetMoviePauseMode( uint32 name, bool pause_mode )
{
	if ( m_movieDetailsList.CountItems() == 0 )
	{
		// no movies in the queue
		return false;
	}

	if ( name == 0 )
	{
		// no name specified, so set the current movie
		CMovieDetails* pDetails = get_movie_details( 0 );
		Dbg_Assert( pDetails );
		pDetails->SetPauseMode( pause_mode );
	}
	else
	{
		// a name was specified, so only do this movie
		CMovieDetails* pDetails = get_movie_details_by_name( name );
		if ( pDetails )
		{
			pDetails->SetPauseMode( pause_mode );
		}
		else
		{
			Dbg_MsgAssert( 0, ( "Movie to set pause mode (%s) not found\n", Script::FindChecksumName(name) ) );
		}
	}
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::IsMovieComplete( uint32 name )
{
	if ( get_movie_count() == 0 )
	{
		return true;
	}

	if ( name != 0 )
	{
		if ( get_movie_details_by_name( name ) )
		{
			// the movie still exists in the queue,
			// so it's obviously not done yet
			return false;
		}
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::IsMovieHeld( uint32 name )
{
	if ( m_movieDetailsList.CountItems() == 0 )
	{
		return false;
	}

	if ( name != 0 )
	{
		CMovieDetails* pDetails = get_movie_details_by_name( name );
		
		if ( pDetails )
		{
			return pDetails->IsHeld();
		}
		else
		{
			// movie not found in list!
			return false;
		}
	}
	else
	{
		CMovieDetails* pDetails = get_movie_details( 0 );
		Dbg_Assert( pDetails );
		return pDetails->IsHeld();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::RemoveMovieFromQueue( uint32 name )
{
	CMovieDetails* pDetails = get_movie_details_by_name( name );

	if ( pDetails )
	{
		if ( pDetails == get_movie_details( 0 ) )
		{
			// first movie?  then just abort current
			AbortCurrentMovie( false );
			return true;
		}
		else
		{
			Dbg_Message( "Removing %s from movie queue", Script::FindChecksumName(name) );
			pDetails->Remove();
			delete pDetails;
			return true;
		}
	}
	
	//Dbg_Message( "RemoveMovieFromQueue:  Movie %s not found.", Script::FindChecksumName(name) );
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::AddMovieToQueue( uint32 name, Script::CStruct* pParams, uint32 movieType )
{
	CMovieDetails* pDetails = NULL;

    bool old_last_movie_camera = m_use_last_movie_camera;

	// GJ:  a new camera has been queued up, so don't need
	// to do anything special to fix the 1-frame glitch
	// (otherwise, some of the cutscenes will have the
	// wrong camera for the first frame...)  must be done before
	// the cutscenedetails constructor, which updates the model component,
	// which contains a check for "IsRolling"
	m_use_last_movie_camera = false;

	switch ( movieType )
	{
		case 0xf5012f52: // cutscene
			
			// check for multiple movies in the queue
			if ( get_movie_count() != 0 )
			{
				for ( int i = 0; i < get_movie_count(); i++ )
				{
					CMovieDetails* pDetails = get_movie_details( i );
					if ( pDetails->GetName() == CRCD(0xf5012f52,"cutscene") )
					{
						// GJ:  theoretically, we should be able to just clear the movie queue
						// to get rid of the extra cutscene, but i suspect that there will be
						// problems with cleanup, and that it would be too risky to do
						// that so close to the end of the project...
						Dbg_MsgAssert( 0, ( "Cutscenes should not be queued (only 1 cutscene at a time, please)" ) );
					}
				}
				// if we've currently got skatercamanims in the queue,
				// then trash them...  (due to the convoluted level
				// loading process, it's possible for a skatercamanim
				// to be launched on the same frame as a cutscene;
				// in this case, we want the cutscene to take
				// precedence)
				ClearMovieQueue();
								
				// we should set this to false, since
				// we know we're about to add a new movie
				// in a moment anyway.  (otherwise, modelcomponent
				// will assert because it doesn't expect the
				// cutscene camera to be active...
				m_use_last_movie_camera = false;
			}

			{
				// fix 1-frame glitch with a loading screen here...
				char* pFileName = NULL;
				bool freeze = true;
				bool blank = false;
				Nx::CLoadScreen::sDisplay( pFileName, freeze, blank );
			}										  

			pDetails = new CCutsceneDetails;
			Dbg_Message( "Trying to create new cutscene" );
			break;
		case 0x12743edb: // objectanim
			pDetails = new CObjectAnimDetails;
			Dbg_Message( "Trying to create new object anim" );
			break;
		case 0x66b7dd11: // skatercamanim
			pDetails = new CSkaterCamDetails;
			Dbg_Message( "Trying to create new skater cam anim" );
			break;
		default:
			Dbg_MsgAssert( 0, ( "Unrecognized movie type %s", Script::FindChecksumName(movieType) ) );
			break;
	}

	Dbg_Assert( pDetails );

	pDetails->SetName( name );

	if ( !pDetails->InitFromStructure( pParams ) )
	{
		// failed
		delete pDetails;

		// restore the old "last movie camera" flag
		m_use_last_movie_camera = old_last_movie_camera;
		
		return false;
	}

	m_movieDetailsList.AddToTail( pDetails );
	//Dbg_Message("Adding a movie on frame %ld.  Count now %d\n", Tmr::GetRenderFrame(), get_movie_count());

	// GJ:  if it's the first movie in the queue,
	// then set the correct camera (to prevent
	// one frame glitch between movies)
	if ( get_movie_count() == 1 )
	{
		Nx::CViewportManager::sSetCamera( 0, this->GetActiveCamera() );
	}

	// TODO:  Support the following parameters
//	pDetails->SetTarget( Obj::MovingObject* pObject );	
//	pDetails->SetFocusSkater( pParams->ContainsFlag("focus_skater") );
//	pDetails->SetFocusTarget( pParams->ContainsFlag("focus_target") );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovieManager::ApplyLastCamera()
{
   	Nx::CViewportManager::sSetCamera( 0, &m_last_movie_camera );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::Update()
{
	// Clear out old camera if we advanced a frame
	if (m_use_last_movie_camera && (m_last_movie_frame <= Tmr::GetRenderFrame()))
	{
		//Dbg_Message("Turning off camera on frame %d", Tmr::GetRenderFrame());
		m_use_last_movie_camera = false;

		if ( get_movie_count() == 0 )
		{
//			Script::RunScript(CRCD(0x15674315, "Restore_skater_camera"));		// Temp pathc to get the skater camera back at end of cutscenes
		}
	}

	CMovieDetails* pDetails = get_movie_details( 0 );

	// there are no movies in the queue,
	// so return "no movies" code
	// (the camera manager should then immediately
	// pass control back to the skater cam)
	if ( !pDetails )
	{
		return false;
	}

#if 0
	printf( "Movie manager update %d:\n", get_movie_count() );
	for ( int i = 0; i < get_movie_count(); i++ )
	{
		printf( "\tmovie name = %s\n", Script::FindChecksumName(get_movie_details(i)->GetName()) );
	}
#endif
	
	if ( Mdl::FrontEnd::Instance()->GamePaused() && ( Mdl::CViewer::sGetViewMode() == 0 ) )
	{
		// printf("game is paused in normal view mode\n");
		if ( !pDetails->ShouldPauseMovie() )
		{
			pDetails->Update();
		}
	}
	else
	{
		// printf("view mode is not normal\n");
		pDetails->Update();
	}
		
	// if the animation is done,
	// the remove it from the list
	if ( pDetails->IsComplete() )
	{
		// TODO:  run callback function if necessary?

		AbortCurrentMovie( false );
	}

	return ( get_movie_count() != 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CMovieManager::GetMovieParams( uint32 name )
{
	if ( m_movieDetailsList.CountItems() == 0 )
	{
		// no movies in the queue
		return NULL;
	}

	if ( name == 0 )
	{
		// no name specified, so set the current movie
		CMovieDetails* pDetails = get_movie_details( 0 );
		Dbg_Assert( pDetails );
		if ( pDetails )
			return pDetails->GetParams();
	}
	else
	{
		// a name was specified, so only do this movie
		CMovieDetails* pDetails = get_movie_details_by_name( name );
		if ( pDetails )
			return pDetails->GetParams();
	}
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	CMovieDetails* pDetails = get_movie_details( 0 );
	Dbg_MsgAssert( pDetails, ("No current movie?") );
	bool success = pDetails->CallMemberFunction( checksum, pParams, pScript );
	if ( success )
	{
		return success;
	}

	return Obj::CObject::CallMemberFunction( checksum, pParams, pScript );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::OverridesCamera()
{
	CMovieDetails* pDetails = get_movie_details( 0 );

	if ( pDetails )
	{
		return pDetails->OverridesCamera();
	}

	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CMovieManager::GetCurrentMovieName()
{
	CMovieDetails* pDetails = get_movie_details( 0 );

	if ( pDetails )
	{
		return pDetails->GetName();
	}

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::HasMovieStarted()
{
	CMovieDetails* pDetails = get_movie_details( 0 );

	if ( pDetails )
	{
		return pDetails->HasMovieStarted();
	}

	Dbg_MsgAssert( 0, ( "Shouldn't call this without knowing there's a movie queued %d", get_movie_count() ) );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieManager::IsMovieQueued()
{
	return ( get_movie_count() != 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj


================================================
FILE: Code/Sk/Objects/moviecam.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       moviemanager.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  02/07/2002
//****************************************************************************

#ifndef __OBJECTS_MOVIEMANAGER_H
#define __OBJECTS_MOVIEMANAGER_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

#include 

#include 

#include 

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

namespace Script
{
	class CScript;
	class CStruct;
}

namespace Obj
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

// CMovieManager
class CMovieManager : public Obj::CObject
{
	public:
	CMovieManager();
	virtual			~CMovieManager();

public:
	virtual bool				Update();
	void						ClearMovieQueue();
	bool						AddMovieToQueue( uint32 name, Script::CStruct* pParams, uint32 movieType );
	bool						RemoveMovieFromQueue( uint32 name );
	void						ApplyLastCamera();
	bool						SetMovieSkippable( uint32 name, bool skippable );
	bool						SetMoviePauseMode( uint32 name, bool pause_mode );
	Script::CStruct*			GetMovieParams( uint32 name );
	bool						AbortCurrentMovie( bool only_if_skippable );
	bool						IsMovieComplete( uint32 name );
	bool						IsMovieHeld( uint32 name );
	bool						IsRolling() { return ( get_movie_count() != 0 ); }// || m_use_last_movie_camera;}
	bool						OverridesCamera();
	Gfx::Camera*				GetActiveCamera();
	uint32						GetCurrentMovieName();
	void						CleanupMovie( CMovieDetails* pMovieDetails );
	bool						HasMovieStarted();
	bool						IsMovieQueued();

public:
	bool						CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript ); 

protected:
	int							get_movie_count();
	CMovieDetails*				get_movie_details( int index );
	CMovieDetails*				get_movie_details_by_name( uint32 name );
	void						update_movie_frame();
	void						setup_last_movie_camera( CMovieDetails* pDetails );

protected:
	Lst::Head	m_movieDetailsList;

	// Currently, when we switch movie cameras, we delete the old one before adding
	// a new one.  Because the new one isn't added until a frame later, we need the
	// ability to have a smooth transition.  So we keep the old camera around for
	// an extra frame.
	// In the future, this code should go away and the cameras should be switched
	// at the same time.
	Gfx::Camera					m_last_movie_camera;
	uint32						m_last_movie_frame;
	bool						m_use_last_movie_camera;
};
	
/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_MOVIEMANAGER_H



================================================
FILE: Code/Sk/Objects/moviedetails.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       moviedetails.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  06/17/2002
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
								
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 

#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMovieDetails::CMovieDetails() : Lst::Node( this )
{
	mp_exitParams = NULL;

	// defaults to not skippable
	m_skippable = false;
	m_holdOnLastFrame = false;

	m_allowPause = false;

	mp_camera = new Gfx::Camera;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CMovieDetails::~CMovieDetails()
{
	if ( mp_exitParams )
	{
		delete mp_exitParams;
	}
   
	if ( mp_camera )
	{
		if (!Nx::CViewportManager::sMarkCameraForDeletion(mp_camera))
		{
			delete mp_camera;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovieDetails::SetParams( Script::CStruct* pParams )
{
	Dbg_Assert( pParams );

	Dbg_MsgAssert( m_exitScript == 0, ( "exit script was already assigned to %s", Script::FindChecksumName( m_name ) ) );

	if ( !mp_exitParams )
	{
		mp_exitParams = new Script::CStruct;
	}

	mp_exitParams->Clear();

	if ( pParams->GetChecksum( CRCD(0x1a005041,"exitScript"), &m_exitScript, Script::NO_ASSERT ) )
	{
		Script::CStruct* pSubParams = NULL;
		if ( pParams->GetStructure( CRCD(0x894fd988,"exitParams"), &pSubParams, Script::NO_ASSERT ) )
		{
			mp_exitParams->AppendStructure( pSubParams );
		}
	}

	int skippable = 0;
	pParams->GetInteger( CRCD(0x8dde72c0,"skippable"), &skippable, Script::NO_ASSERT );
	m_skippable = skippable;

	// if we want the movie to continue to hold on the last frame (i.e. don't go on to the next anim)
	m_holdOnLastFrame = pParams->ContainsFlag( CRCD(0xeb300c29,"play_hold") );

	int allow_pause = 0;
	pParams->GetInteger( CRCD(0xb8ca12c5,"allow_pause"), &allow_pause, Script::NO_ASSERT );
	m_allowPause = ( allow_pause != 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* CMovieDetails::GetParams()
{
	Script::CStruct* pReturnParams = new Script::CStruct();
	pReturnParams->AddInteger( CRCD(0x8dde72c0,"skippable"), m_skippable );
	pReturnParams->AddInteger( CRCD(0xb8ca12c5,"allow_pause"), m_allowPause );
	return pReturnParams;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CMovieDetails::Cleanup()
{
	// run the exit script

//	if ( m_exitScript != 0 )
	{
//		Dbg_Message( "Running exit script (%s)\n", Script::FindChecksumName(m_exitScript) );

//		Script::RunScript( m_exitScript, mp_exitParams, NULL );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieDetails::Abort( bool only_if_skippable )
{
	if ( !only_if_skippable )
	{
		m_aborted = true;
	}
	if ( m_skippable )
	{
		m_aborted = true;
	}

	return m_aborted;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieDetails::SetSkippable( bool skippable )
{
	m_skippable = skippable;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieDetails::SetPauseMode( bool pause_mode )
{
	m_shouldPause = pause_mode;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieDetails::SetName( uint32 name )
{
	m_name = name;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32 CMovieDetails::GetName()
{
	return m_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CMovieDetails::CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CObjectAnimDetails::CObjectAnimDetails() : CMovieDetails()
{
//	Gfx::CBonedAnimFrameData*	mp_frameData;
	
	mp_frameData = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CObjectAnimDetails::~CObjectAnimDetails()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CObjectAnimDetails::InitFromStructure( Script::CStruct* pParams )
{
	this->SetParams( pParams );

	if ( !pParams->ContainsFlag( CRCD(0xee721991,"virtual_cam") ) )
	{
		Ass::CAssMan* ass_man = Ass::CAssMan::Instance();
		Gfx::CBonedAnimFrameData* pData = (Gfx::CBonedAnimFrameData*)ass_man->GetAsset( this->GetName(), false );
		if ( !pData )
		{
			Dbg_Message( "Warning: couldn't find movie with name %s", Script::FindChecksumName( this->GetName() ) );
			return false;
		}
		
		this->set_frame_data( pData, 
							pParams->ContainsFlag( CRCD(0x5ea0e211,"loop") ) ? Gfx::LOOPING_CYCLE : Gfx::LOOPING_HOLD,
							pParams->ContainsFlag( CRCD(0xf8cfd515,"backwards") ) );
	}
	else
	{
		int numFrames = 0;
		pParams->GetInteger( CRCD(0x019176c5,"frames"), &numFrames, Script::ASSERT );
		this->set_movie_length( numFrames / 60.0f );
	}

	// by default, pause movie when game is paused 
	this->SetPauseMode( true );
	   
	// now that the new movie is about to start,
	// reset all of its custom keys
	this->ResetCustomKeys();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CObjectAnimDetails::Update()
{
	update_moving_objects();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CObjectAnimDetails::set_frame_data( Gfx::CBonedAnimFrameData* pFrameData, Gfx::EAnimLoopingType loopingType, bool reverse )
{
	Dbg_Assert( pFrameData );

	mp_frameData = pFrameData;

	if ( !reverse )
	{
		m_animController.PlaySequence( 0, 0.0f, pFrameData->GetDuration(), loopingType, 0.0f, 1.0f );
	}
	else
	{
// 26Mar03 JCB - When playing backwards, start on the final frame, not beyond the end of the anim.
//
//		m_animController.PlaySequence( 0, pFrameData->GetDuration(), 0.0f, loopingType, 0.0f, 1.0f );
		m_animController.PlaySequence( 0, pFrameData->GetDuration() - (1.0f/60.0f), 0.0f, loopingType, 0.0f, 1.0f );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CObjectAnimDetails::set_movie_length( float duration )
{
	mp_frameData = NULL;

	m_animController.PlaySequence( 0, 0.0f, duration, Gfx::LOOPING_HOLD, 0.0f, 1.0f );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CObjectAnimDetails::ResetCustomKeys()
{
	// eventually, this will go in the decompressed version of the
	// frame data, rather than the frame data itself...
	if ( mp_frameData )
	{
		mp_frameData->ResetCustomKeys();
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CObjectAnimDetails::process_custom_keys(float startTime, float endTime, bool end_time_inclusive)
{
	// This code currently assumes that you're not going to pause the
	// camera.  If so, then you run the risk of calling these user keys
	// more than once...

	// TODO:  Handle this gracefully!

	if ( mp_frameData )
	{
		Dbg_Assert( mp_camera );

		// It depends on the direction also...
		mp_frameData->ProcessCustomKeys( startTime, endTime, mp_camera, end_time_inclusive );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CObjectAnimDetails::update_moving_objects()
{
	float oldTime = m_animController.GetCurrentAnimTime();

	m_animController.Update();
	
	// Print out some useful information...
	//if ( Script::GetInt( CRCD(0x2f510f45,"moviecam_debug"), false ) )
	{
		//Dbg_Message( "Moving object anim %s @ %f seconds (%d frames)", Script::FindChecksumName(m_name), m_animController.GetCurrentAnimTime(), (int)(m_animController.GetCurrentAnimTime() * 60.0f) );
	}

	// process associated keys since last frame
	process_custom_keys( oldTime, m_animController.GetCurrentAnimTime(), false );

	Mth::Quat* pQuat = new Mth::Quat[mp_frameData->GetNumBones()];
	Mth::Vector* pVector = new Mth::Vector[mp_frameData->GetNumBones()];
	mp_frameData->GetInterpolatedCameraFrames( pQuat, pVector, m_animController.GetCurrentAnimTime() );

	// get the position of each bone
	for ( int i = 0; i < mp_frameData->GetNumBones(); i++ )
	{
		// grab the frame from the animation controller
		Dbg_Assert( mp_frameData );
		
		uint32 objName;
		objName = mp_frameData->GetBoneName( i );

		Obj::CObject* pObject = Obj::ResolveToObject( objName );
		Dbg_MsgAssert( pObject, ( "Can't find lookup target %s", Script::FindChecksumName( objName ) ) );
		if ( pObject )
		{
			Obj::CCompositeObject* pCompositeObject = (Obj::CCompositeObject*)pObject;

			// update the object's orientation
			Mth::Matrix theMatrix;
			
			Mth::Vector nullTrans;
			nullTrans.Set(0.0f,0.0f,0.0f,1.0f);		// Mick: added separate initialization
			Mth::QuatVecToMatrix( pQuat + i, &nullTrans, &theMatrix );
			
			pCompositeObject->SetMatrix( theMatrix );
			pCompositeObject->SetDisplayMatrix( theMatrix );

			// update the object's position
			pCompositeObject->m_pos = pVector[i];

			if ( Script::GetInt( CRCD(0x2f510f45,"moviecam_debug") ) )
			{
				printf( "Object %s @ time %f:  ", Script::FindChecksumName(objName), m_animController.GetCurrentAnimTime() );
				pCompositeObject->m_pos.PrintContents();
				pCompositeObject->m_matrix.PrintContents();
			}
		
			// if we want to change the position/rotation,
			// then we also need to update the model component
			CModelComponent* pModelComponent = GetModelComponentFromObject( pCompositeObject );
			if ( pModelComponent )
			{
				pModelComponent->Update();
			}
		}
		else
		{
			printf( "object animation:  couldn't find object %s\n", Script::FindChecksumName(objName) );
		}
	}
	
	delete[] pQuat;
	delete[] pVector;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CObjectAnimDetails::IsComplete()
{
	if ( m_aborted )
	{
		return true;
	}

	if ( m_holdOnLastFrame )
	{
		return false;
	}

	return m_animController.IsAnimComplete();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CObjectAnimDetails::IsHeld()
{
	/* Added by Ken. Used to determine when camera anims used in	  */
	/* the skate shop front-end have got to their last frame, so that */
	/* scripts can trigger things to happen such as the video menu    */
	/* making the video cassettes pan out to the foreground.          */
	
	return ( m_holdOnLastFrame && m_animController.IsAnimComplete() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CObjectAnimDetails::OverridesCamera()
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterCamDetails::CSkaterCamDetails() : CMovieDetails()
{
	mp_frameData = NULL;
	
	// defaults to no target
	m_hasTarget = false;
	m_targetID = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterCamDetails::~CSkaterCamDetails()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCamDetails::InitFromStructure( Script::CStruct* pParams )
{
	this->SetParams( pParams );

	if ( !pParams->ContainsFlag( CRCD(0xee721991,"virtual_cam") ) )
	{
		Ass::CAssMan* ass_man = Ass::CAssMan::Instance();
		Gfx::CBonedAnimFrameData* pData = (Gfx::CBonedAnimFrameData*)ass_man->GetAsset( this->GetName(), false );
		if ( !pData )
		{
			Dbg_Message( "Warning: couldn't find movie with name %s", Script::FindChecksumName( this->GetName() ) );
			return false;
		}
		
		this->set_frame_data( pData, 
							pParams->ContainsFlag( CRCD(0x5ea0e211,"loop") ) ? Gfx::LOOPING_CYCLE : Gfx::LOOPING_HOLD,
							pParams->ContainsFlag( CRCD(0xf8cfd515,"backwards") ) );
	}
	else
	{
		int numFrames = 0;
		pParams->GetInteger( CRCD(0x019176c5,"frames"), &numFrames, Script::ASSERT );
		this->set_movie_length( numFrames / 60.0f );
	}

	// by default, pause movie when game is paused 
	this->SetPauseMode( true );
	   
	// look for target parameters
	this->set_target_params( pParams );
	
	// now that the new movie is about to start,
	// reset all of its custom keys
	this->ResetCustomKeys();

	update_camera();

	// process initial keys, end time inclusive...
	// this to fix an FOV glitch on the first frame
	process_custom_keys( m_animController.GetCurrentAnimTime(), m_animController.GetCurrentAnimTime(), true );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCamDetails::Update()
{
	update_camera_time();

	update_camera();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCamDetails::set_target_params( Script::CStruct* pParams )
{
	Dbg_Assert( pParams );

	m_hasTarget = pParams->GetChecksum( CRCD(0x531e4d28,"targetID"), &m_targetID, Script::NO_ASSERT );
	
	if ( m_hasTarget )
	{
		// target offset, if any
		m_targetOffset.Set(0.0f,0.0f,0.0f);
		pParams->GetVector( CRCD(0x906642bf,"targetOffset"), &m_targetOffset, Script::NO_ASSERT );
		m_hasTargetOffset = ( m_targetOffset[X] != 0.0f || m_targetOffset[Y] != 0.0f || m_targetOffset[Z] != 0.0f );

		// position offset, if any
		m_positionOffset.Set(0.0f,0.0f,0.0f);
		m_hasPositionOffset = pParams->GetVector( CRCD(0xa00d3d6e,"positionOffset"), &m_positionOffset, Script::NO_ASSERT );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCamDetails::clear_target_params()
{
	m_hasTarget = false;
	m_targetID = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCamDetails::set_frame_data( Gfx::CBonedAnimFrameData* pFrameData, Gfx::EAnimLoopingType loopingType, bool reverse )
{
	Dbg_Assert( pFrameData );

	mp_frameData = pFrameData;

	if ( !reverse )
	{
		m_animController.PlaySequence( 0, 0.0f, pFrameData->GetDuration(), loopingType, 0.0f, 1.0f );
	}
	else
	{
// 26Mar03 JCB - When playing backwards, start on the final frame, not beyond the end of the anim.
//
//		m_animController.PlaySequence( 0, pFrameData->GetDuration(), 0.0f, loopingType, 0.0f, 1.0f );
		m_animController.PlaySequence( 0, pFrameData->GetDuration() - (1.0f/60.0f), 0.0f, loopingType, 0.0f, 1.0f );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCamDetails::set_movie_length( float duration )
{
	mp_frameData = NULL;

	m_animController.PlaySequence( 0, 0.0f, duration, Gfx::LOOPING_HOLD, 0.0f, 1.0f );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCamDetails::ResetCustomKeys()
{
	// eventually, this will go in the decompressed version of the
	// frame data, rather than the frame data itself...
	if ( mp_frameData )
	{
		mp_frameData->ResetCustomKeys();
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCamDetails::get_current_frame(Mth::Quat* pQuat, Mth::Vector* pTrans)
{
	Dbg_Assert( pQuat );
	Dbg_Assert( pTrans );

	// grab the frame from the animation controller
	Dbg_Assert( mp_frameData );
	Dbg_Assert( mp_frameData->GetNumBones() == 1 );
	mp_frameData->GetInterpolatedCameraFrames( pQuat, pTrans, m_animController.GetCurrentAnimTime() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCamDetails::process_custom_keys(float startTime, float endTime, bool end_time_inclusive)
{
	// This code currently assumes that you're not going to pause the
	// camera.  If so, then you run the risk of calling these user keys
	// more than once...

	// TODO:  Handle this gracefully!

	if ( mp_frameData )
	{
		Dbg_Assert( mp_camera );

		// It depends on the direction also...
		mp_frameData->ProcessCustomKeys( startTime, endTime, mp_camera, end_time_inclusive );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCamDetails::update_camera_time()
{
	float oldTime = m_animController.GetCurrentAnimTime();

	m_animController.Update();

	// Not sure if this is still needed for the sound code
	// Maybe we can add the functionality automatically to the Gfx::Camera?
	mp_camera->StoreOldPos();
	
	// process associated keys since last frame
	process_custom_keys( oldTime, m_animController.GetCurrentAnimTime(), false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCamDetails::update_camera()
{
	Dbg_Assert( mp_camera );

	Mth::Vector& camPos = mp_camera->GetPos();
	Mth::Matrix& camMatrix = mp_camera->GetMatrix();
	
	Mdl::FrontEnd* pFront = Mdl::FrontEnd::Instance();

	for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		if ( pFront->GetInputHandler( i )->m_Input->m_Makes & Inp::Data::mD_X )
		{
			// abort if skippable
			Abort( true );
		}
	}
	
	if ( mp_frameData )
	{
		Mth::Quat theQuat;
		Mth::Vector theTrans;

		get_current_frame( &theQuat, &theTrans );
		
		// update the camera position
		camPos = theTrans;

		// update the camera orientation
		Mth::Vector nullTrans;
		nullTrans.Set(0.0f,0.0f,0.0f,1.0f);		// Mick: added separate initialization
		Mth::QuatVecToMatrix( &theQuat, &nullTrans, &camMatrix );
	}
	else
	{
		// clear out the cam matrix to the identity
		// if there's no associated animation.
		// the assumption is that the following m_hasTarget
		// code block will overrride it with the correct data...
		camPos = Mth::Vector( 0.0f, 0.0f, 0.0f );
		camMatrix.Ident();
		
		// if there's no frame data, then we should
		// reset the camera to some default FOV,
		// or else it will use the last FOV used
		float fov_in_degrees = Script::GetFloat( CRCD(0x99529205, "camera_fov") );
		mp_camera->SetHFOV(fov_in_degrees);
	}
	
	if ( m_hasTarget )
	{
		if ( m_targetID != CRCD(0xc588eebc,"world") )
		{
			// calculate the orientation from the target
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			Obj::CGeneralManager* pObjectManager = skate_mod->GetObjectManager();
			Dbg_Assert( pObjectManager );
			Obj::CObject* pObject = pObjectManager->GetObjectByID( m_targetID );
			Dbg_MsgAssert( pObject, ( "Can't find lookup target %s", Script::FindChecksumName(m_targetID) ) );
			if ( pObject )
			{
				//Dbg_MsgAssert( static_cast( pObject ), ( "This function only works on moving objects." ) ); 
				Obj::CCompositeObject* pCompositeObject = (Obj::CCompositeObject*)pObject;
	
				// override it with the moving object's position
				Mth::Vector pos = pCompositeObject->GetPos();
				
				// patch for positions that don't have 1.0 in the final column...
				// not sure why this would be the case, but it seems to happen
				// with the skater...  (possibly a result of skater's mp_physics
				// disappearing)
				pos[W] = 1.0f;
	
				// not properly working yet...
				if ( m_hasTargetOffset )
				{    
					Mth::Matrix mat = pCompositeObject->GetMatrix();
					mat[Mth::POS] = pos;				                                                         
					mat.TranslateLocal( m_targetOffset );
					pos = mat[Mth::POS];
				}
	
				if ( m_hasPositionOffset )
				{
					camPos = pos;
	
					Mth::Matrix mat = pCompositeObject->GetMatrix();
					mat[Mth::POS] = pos;
					mat.TranslateLocal( m_positionOffset );
					camPos = mat[Mth::POS];
				}
	
				// set rotation from the camera's position (always up)
				Mth::Vector atVec = camPos - pos;
				atVec.Normalize();
				
				camMatrix.Ident();
				camMatrix[Mth::AT]		= atVec;
				camMatrix[Mth::RIGHT]	= Mth::CrossProduct( camMatrix[Mth::UP], camMatrix[Mth::AT] );
				camMatrix[Mth::RIGHT].Normalize();
				camMatrix[Mth::UP]		= Mth::CrossProduct( camMatrix[Mth::AT], camMatrix[Mth::RIGHT] );
			}
		}
		else
		{
			// World coordinates are a lot simpler
			Mth::Vector pos = m_targetOffset;
			camPos = m_positionOffset;
			Mth::Vector atVec = camPos - pos;
			atVec[W] = 1.0f;
			atVec.Normalize();
			camMatrix.Ident();
			camMatrix[Mth::AT]		= atVec;
			camMatrix[Mth::RIGHT]	= Mth::CrossProduct( camMatrix[Mth::UP], camMatrix[Mth::AT] );
			camMatrix[Mth::RIGHT].Normalize();
			camMatrix[Mth::UP]		= Mth::CrossProduct( camMatrix[Mth::AT], camMatrix[Mth::RIGHT] );
		}		
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCamDetails::CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript )
{
	switch ( checksum )
	{
		case 0xa8d5a188:		// SetTargetObject
			{
				Dbg_Message( "*** Calling SetTargetObject" );
				
				set_target_params( pParams );
				
				return true;
			}
		case 0xd7241a6c:		// ClearTargetObject
			{
				Dbg_Message( "*** Calling ClearTargetObject" );
				
				clear_target_params();
				
				return true;
			}
			break;
	}

	return CMovieDetails::CallMemberFunction( checksum, pParams, pScript );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCamDetails::IsComplete()
{
	if ( m_aborted )
	{
		return true;
	}

	if ( m_holdOnLastFrame )
	{
		return false;
	}

	return m_animController.IsAnimComplete();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCamDetails::IsHeld()
{
	/* Added by Ken. Used to determine when camera anims used in	  */
	/* the skate shop front-end have got to their last frame, so that */
	/* scripts can trigger things to happen such as the video menu    */
	/* making the video cassettes pan out to the foreground.          */
	
	return ( m_holdOnLastFrame && m_animController.IsAnimComplete() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCamDetails::OverridesCamera()
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj


================================================
FILE: Code/Sk/Objects/moviedetails.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       moviedetails.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  06/17/2002
//****************************************************************************

#ifndef __OBJECTS_MOVIEDETAILS_H
#define __OBJECTS_MOVIEDETAILS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 
#include 

#include 						 

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

namespace Gfx
{
	class Camera;
	class CBonedAnimFrameData;
}

namespace Mth
{
	class Matrix;
	class Quat;
	class Vector;
}
				   
namespace Script
{
	class CScript;
	class CStruct;
}

namespace Obj
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

// CMovieDetails
class CMovieDetails : public Lst::Node
{
public:
	CMovieDetails();
	virtual 				~CMovieDetails();

public:
	virtual bool			InitFromStructure( Script::CStruct* pParams ) = 0;
	virtual void			Update() = 0;
	virtual	bool			ResetCustomKeys() = 0;
	virtual bool			CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript ); 
	virtual bool			IsComplete() = 0;
	virtual bool			IsHeld() = 0;
	virtual bool			OverridesCamera() = 0;
	virtual void			Cleanup();
	virtual bool			HasMovieStarted() = 0;
	virtual bool			NeedsCameraTransition() { return false; }		// Only set to true on classes that need the
																			// old camera to stay around an extra frame
												  
public:
	void			    	SetParams( Script::CStruct* pParams );
	Script::CStruct* 		GetParams();
	bool					SetSkippable( bool skippable );
	bool					IsSkippable() { return m_skippable; }
	bool					SetName( uint32 name );
	uint32					GetName();
	bool					Abort( bool only_if_skippable );

	bool					ShouldPauseMovie() { return m_shouldPause; }
	bool					SetPauseMode( bool pause_mode );

	Gfx::Camera*			GetCamera() { return mp_camera; }
	
	uint32					GetExitScriptName() const
	{
		return m_exitScript;
	}
	Script::CStruct*		GetExitParams()
	{
		return mp_exitParams;
	}

protected:
	Gfx::Camera*			mp_camera;
	bool					m_holdOnLastFrame;
	bool					m_skippable;
	bool					m_aborted;
	bool					m_shouldPause;
	bool					m_allowPause;
	uint32					m_name;

	// exit parameters
	uint32					m_exitScript;
	Script::CStruct*		mp_exitParams;
};

// this can probably be split up into
// a target version and a non-target version
class CSkaterCamDetails : public CMovieDetails
{
public:
	CSkaterCamDetails();
	~CSkaterCamDetails();

public:
	virtual bool 	InitFromStructure(Script::CStruct *pParams);
	virtual void 	Update();
	virtual bool 	ResetCustomKeys();
	virtual bool	CallMemberFunction( uint32 checksum, Script::CStruct* pParams, Script::CScript* pScript ); 
	virtual bool 	IsComplete();
	virtual bool	IsHeld();
	virtual bool	OverridesCamera();
	virtual bool	HasMovieStarted()
	{
		return true;
	}
	virtual bool	NeedsCameraTransition() { return true; }		// Uses hack to work around glitches where there isn't a camera

protected:
	void			set_target_params( Script::CStruct* pParams );
	void			clear_target_params();
	void			set_frame_data( Gfx::CBonedAnimFrameData* pFrameData, Gfx::EAnimLoopingType loopingType, bool reverse );
	void			set_movie_length( float duration );
	void			get_current_frame( Mth::Quat* pQuat, Mth::Vector* pTrans );
	bool			process_custom_keys( float startTime, float endTime, bool end_time_inclusive );
	void			update_camera();
	void			update_camera_time();

protected:
	// target parameters
	bool			m_hasTarget;
	uint32			m_targetID;
	Mth::Vector		m_targetOffset;
	Mth::Vector		m_positionOffset;
	bool			m_hasTargetOffset;
	bool			m_hasPositionOffset;
	
	Gfx::CBonedAnimFrameData*	mp_frameData;
	
	// time-keeper
	Gfx::CAnimChannel		m_animController;
};					

class CObjectAnimDetails : public CMovieDetails
{
public:
	CObjectAnimDetails();
	virtual 		~CObjectAnimDetails();

public:
	virtual bool	InitFromStructure( Script::CStruct* pParams );
	virtual void	Update();
	virtual bool	ResetCustomKeys();
	virtual bool	IsComplete();
	virtual bool	IsHeld();
	virtual bool	OverridesCamera();
	virtual bool	HasMovieStarted()
	{
		return true;
	}
	
protected:
	void			set_frame_data( Gfx::CBonedAnimFrameData* pFrameData, Gfx::EAnimLoopingType loopingType, bool reverse );
	void			set_movie_length( float duration );
	bool			process_custom_keys( float startTime, float endTime, bool end_time_inclusive );
	void			update_moving_objects();

protected:
	Gfx::CBonedAnimFrameData*	mp_frameData;
	
	// time-keeper
	Gfx::CAnimChannel		m_animController;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_MOVIEDETAILS_H



================================================
FILE: Code/Sk/Objects/navigation.cpp
================================================
///////////////////////////////////////////////////////
// rail.cpp

#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
		  

namespace Obj
{









/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sAStarOpenList
{
	CNavNode*	m_open_list[512];
	int			m_num_open;

				sAStarOpenList( void );
				~sAStarOpenList( void );

	bool		IsEmpty( void )				{ return m_num_open == 0; }
	void		Clear();
	void		Empty();
	void		Push( CNavNode* p_node );
	CNavNode*	Pop( void );
	void		SortOnTotalCost();
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sAStarClosedList
{
	CNavNode*	m_closed_list[512];
	int			m_num_closed;

				sAStarClosedList( void );
				~sAStarClosedList( void );

	bool		IsEmpty( void )				{ return m_num_closed == 0; }
	void		Clear();
	void		Empty( void );
	void		Add( CNavNode* p_node );
	void		Remove( CNavNode* p_node );
};



static sAStarOpenList	openList;
static sAStarClosedList	closedList;



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sAStarNode::Reset( void )
{
	m_flags	= 0;
	m_cost_from_start	= 0.0f;
	m_cost_to_goal		= 0.0f;
	m_total_cost		= 0.0f;
	mp_parent			= NULL;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool normal_sensitive_collision( CFeeler& feeler, float normal_y_sensitivity )
{
	// Safety check.
	int iteration = 0;

	// The feeler should already have been set up with the start and end points.
	while( feeler.GetCollision())
	{
		// If this collision is with a normal that passes ouyr sensitivity test, consider it valid.
		if( Mth::Abs( feeler.GetNormal()[Y] ) < normal_y_sensitivity )
		{
			return true;
		}

		// Otherwise move the start location on by 0.1 inch and continue.
		feeler.m_start += ( feeler.m_end - feeler.m_start ).Normalize( 0.1f );

		++iteration;
		if( iteration > 8 )
		{
			Dbg_Message( "Multiple iterations in normal_sensitive_collision()" );

			// Err on the side of caution.
			return true;
		}
	}
	return false;
}




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float path_cost_estimate( Mth::Vector& pos_a, Mth::Vector& pos_b )
{
	// Currently just use the distance between them. No consideration of terrain type.
	return Mth::Distance( pos_a, pos_b );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float traverse_cost( CNavNode* p_node_a, CNavNode* p_node_b )
{
	// Currently just use the distance between them. No consideration of terrain type.
	return Mth::Distance( p_node_a->GetPos(), p_node_b->GetPos());
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int sort_on_total_cost( const void *p1, const void *p2 )
{
	CNavNode*	p_node1	= *((CNavNode**)p1 );
	CNavNode*	p_node2	= *((CNavNode**)p2 );
	
	return ( p_node1->GetAStarNode()->m_total_cost < p_node2->GetAStarNode()->m_total_cost ) ? 1 : (( p_node1->GetAStarNode()->m_total_cost > p_node2->GetAStarNode()->m_total_cost ) ? -1 : 0 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNavNode* get_nearest_visible_node( Mth::Vector& pos, Mth::Vector& heading )
{
	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	Obj::CNavManager* p_nav_man = skate_mod->GetNavManager();

	float		best_dp		= -0.707106f;	// cosine of 135 degrees
	CNavNode*	p_best_node	= NULL;

	int num_nodes = p_nav_man->GetNumNodes();
	for( int i = 0; i < num_nodes; ++i )
	{
		CNavNode*	p_node = p_nav_man->GetNavNodeByIndex( i );

		// Discard nodes that are not in the required direction.
		Mth::Vector node_pos	= p_node->GetPos() + Mth::Vector( 0.0f, 60.0f, 0.0f, 0.0f );
		Mth::Vector pos_to_node	= node_pos - pos;
		pos_to_node[Y]			= 0.0f;
		pos_to_node[W]			= 0.0f;
		pos_to_node.Normalize();
		float dp				= Mth::DotProduct( pos_to_node, heading );

		// Do we already have a better node.
		if( dp < best_dp )
		{
			continue;
		}

		// Discard nodes that are not visible.
		CFeeler feeler;
		feeler.m_start	= pos;
		feeler.m_end	= node_pos;
		if( normal_sensitive_collision( feeler, 0.2f ))
		{
			continue;
		}

		best_dp		= dp;
		p_best_node	= p_node;
	}
	return p_best_node;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNavNode* CalculateNodePath( Mth::Vector pos, Mth::Vector target_pos )
{
	// Clear open and closed nodes.
	openList.Clear();
	closedList.Clear();

	// Find nearest visible node from current location, preferably in direction currently facing.
	Mth::Vector heading = target_pos - pos;
	heading[Y] = 0.0f;
	heading[W] = 0.0f;
	heading.Normalize();
	CNavNode* p_nearest_visible = get_nearest_visible_node( pos, heading );
	if( p_nearest_visible == NULL )
	{
		return false;
	}

	// Initialise the A* section of the node.
	p_nearest_visible->GetAStarNode()->m_cost_from_start	= path_cost_estimate( pos, p_nearest_visible->GetPos());
	p_nearest_visible->GetAStarNode()->m_cost_to_goal		= path_cost_estimate( p_nearest_visible->GetPos(), target_pos );
	p_nearest_visible->GetAStarNode()->m_total_cost			= p_nearest_visible->GetAStarNode()->m_cost_from_start + p_nearest_visible->GetAStarNode()->m_cost_to_goal;
	p_nearest_visible->GetAStarNode()->mp_parent			= NULL;

	// Push this node on to the open list.
	openList.Push( p_nearest_visible );

	while( !openList.IsEmpty())
	{
		// Pop lowest total cost node from open list.
		CNavNode* p_this_node = openList.Pop();

		// If goal visible from this node, we're done.
		Mth::Vector raised_node_pos = p_this_node->GetPos() + Mth::Vector( 0.0f, 60.0f, 0.0f, 0.0f );
		CFeeler feeler;
		feeler.m_start	= raised_node_pos;
		feeler.m_end	= target_pos;
		if( !normal_sensitive_collision( feeler, 0.2f ))
		{
			// Build list of nodes.

			// Empty lists.
			openList.Empty();
			closedList.Empty();

			return p_this_node;
		}

		// For each node linked to this node...
		for( int l = 0; l < p_this_node->GetNumLinks(); ++l )
		{
			CNavNode*	p_new_node	= p_this_node->GetLink( l );
			float		new_cost	= p_this_node->GetAStarNode()->m_cost_from_start + traverse_cost( p_this_node, p_new_node );

			// Ignore this node if it is already in a list and provides no improvement.
			if(( p_new_node->GetAStarNode()->InOpen() || p_new_node->GetAStarNode()->InClosed()) &&
				 p_new_node->GetAStarNode()->m_cost_from_start <= new_cost )
			{
				continue;
			}

			// Store the new or improved information.
			p_new_node->GetAStarNode()->mp_parent			= p_this_node;
			p_new_node->GetAStarNode()->m_cost_from_start	= new_cost;
			p_new_node->GetAStarNode()->m_cost_to_goal		= path_cost_estimate( p_new_node->GetPos(), target_pos );
			p_new_node->GetAStarNode()->m_total_cost		= p_new_node->GetAStarNode()->m_cost_from_start + p_new_node->GetAStarNode()->m_cost_to_goal;

			// Remove new node from closed list if it's in there.
			if( p_new_node->GetAStarNode()->InClosed())
			{
				closedList.Remove( p_new_node );
			}

			// Push new node onto open list (if it's not already in there).
			// This will re-sort the open list.
			if( !p_new_node->GetAStarNode()->InOpen())
			{
				openList.Push( p_new_node );
			}
		}

		// Add node onto the closed list.
		closedList.Add( p_this_node );
	}

	// Empty lists.
	openList.Empty();
	closedList.Empty();

	// If we get here we have failed to find any valid path.
	return NULL;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sAStarOpenList::sAStarOpenList( void )
{
	Clear();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sAStarOpenList::~sAStarOpenList( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sAStarOpenList::Clear( void )
{
	memset( m_open_list, 0, sizeof( m_open_list ));
	m_num_open = 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sAStarOpenList::Empty( void )
{
	while( !IsEmpty())
	{
		Pop();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sAStarOpenList::Push( CNavNode* p_node )
{
	Dbg_Assert( m_num_open < 512 );

	m_open_list[m_num_open++] = p_node;

	// Flag this node as now being in the open list.
	p_node->GetAStarNode()->m_flags |= sAStarNode::IN_OPEN;

	SortOnTotalCost();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNavNode* sAStarOpenList::Pop( void )
{
	Dbg_Assert( m_num_open > 0 );

	// Assumes always sorted, so just return lowest cost node.
	--m_num_open;
	CNavNode* p_return		= m_open_list[m_num_open];
	m_open_list[m_num_open]	= NULL;

	// Flag this node as no longer being in the open list.
	p_return->GetAStarNode()->m_flags &= ~sAStarNode::IN_OPEN;

	return p_return;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sAStarOpenList::SortOnTotalCost( void )
{
	if( m_num_open > 1 )
	{
		qsort( m_open_list, m_num_open, sizeof( CNavNode* ), sort_on_total_cost );
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sAStarClosedList::sAStarClosedList( void )
{
	Clear();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
sAStarClosedList::~sAStarClosedList( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sAStarClosedList::Clear( void )
{
	memset( m_closed_list, 0, sizeof( m_closed_list ));
	m_num_closed = 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sAStarClosedList::Empty( void )
{
	if( !IsEmpty())
	{
		for( int i = 0; i < 512; ++i )
		{
			if( m_closed_list[i] )
			{
				Remove( m_closed_list[i] );
			}
		}
		Dbg_Assert( IsEmpty());
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sAStarClosedList::Add( CNavNode* p_node )
{
	for( int i = 0; i < 512; ++i )
	{
		if( m_closed_list[i] == NULL )
		{
			m_closed_list[i] = p_node;
			++m_num_closed;
			p_node->GetAStarNode()->m_flags |= sAStarNode::IN_CLOSED;
			return;
		}
	}
	Dbg_Assert( 0 );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void sAStarClosedList::Remove( CNavNode* p_node )
{
	for( int i = 0; i < 512; ++i )
	{
		if( m_closed_list[i] == p_node )
		{
			m_closed_list[i] = NULL;
			--m_num_closed;
			p_node->GetAStarNode()->m_flags &= ~sAStarNode::IN_CLOSED;
			return;
		}
	}
	Dbg_Assert( 0 );
}




































/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNavManager::CNavManager()
{
	mp_nodes		= NULL;
	m_num_nodes		= 0;
	mp_node_array	= NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CNavManager::~CNavManager()
{	 
	Cleanup();  
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNavManager::Cleanup()
{
	m_num_nodes = 0;
	
	Mem::Free( mp_nodes );
	mp_nodes		= NULL;
	mp_node_array	= NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNavManager::AddNavNode( int node_number, Script::CStruct* p_node_struct )
{
	CNavNode* pNavNode = &mp_nodes[m_current_node];
	
	uint32 class_checksum;
	p_node_struct->GetChecksum( CRCD( 0x12b4e660, "class" ), &class_checksum );
	
	uint32 type_checksum;
	p_node_struct->GetChecksum( CRCD( 0x7321a8d6, "type" ), &type_checksum );

	// No links as yet.
	pNavNode->m_num_links = 0;
	for( int i = 0;i < MAX_NAV_LINKS; ++i )
	{
		pNavNode->mp_links[i] = NULL;
	}

	// The node_number is use primarily for calculating links.
	pNavNode->m_node		= node_number;
	
	// Reset the A* node.
	pNavNode->m_astar_node.Reset();
	
//	pNavNode->m_flags	= 0;
//	pNavNode->SetActive( p_node_struct->ContainsFlag( 0x7c2552b9 /*"CreatedAtStart"*/ ));	// Created at start or not?

	// Set the position from the node structure.
	SkateScript::GetPosition( p_node_struct, &pNavNode->m_pos );

	int links = SkateScript::GetNumLinks( p_node_struct );
	if( links )
	{
		Dbg_MsgAssert( links <= MAX_NAV_LINKS, ( "Nav node %d has %d linkes, max is %d", node_number, links, MAX_NAV_LINKS ));

		pNavNode->m_num_links = links;
		for( int i = 0; i < links; ++i )
		{
			// For now we just set the link to be the node number in the node array.
			pNavNode->mp_links[i] = (CNavNode*)SkateScript::GetLink( p_node_struct, i );
		}
	}	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CNavManager::AddNavsFromNodeArray( Script::CArray *p_nodearray )
{
	Dbg_MsgAssert( !mp_node_array, ( "Setting two node arrays in rail manager" ));
	mp_node_array = p_nodearray;
	
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();

	uint32 i;	

	Dbg_MsgAssert( m_num_nodes == 0, ("Can only addd nodes once, already %d there\n", m_num_nodes ));

	// First iterate over the node array, and count the number of nodes needed.
	m_num_nodes = 0;										  
	for( i = 0; i < p_nodearray->GetSize(); ++i )
	{
		Script::CStruct* p_node_struct = p_nodearray->GetStructure( i );
		Dbg_MsgAssert( p_node_struct, ( "Error getting node from node array for navigation" ));
		if( !skate_mod->ShouldBeAbsentNode( p_node_struct ))
		{
			uint32 class_checksum = 0;		
			p_node_struct->GetChecksum( CRCD( 0x12b4e660, "Class" ), &class_checksum );
			if( class_checksum == CRCD( 0x4c23a77e, "Waypoint" ))
			{
				uint32 type_checksum = 0;		
				p_node_struct->GetChecksum( CRCD( 0x7321a8d6, "Type" ), &type_checksum );
				if( type_checksum == CRCD( 0x9784feb5, "HorseNav" ))
				{
					++m_num_nodes;
				}
			}
		}
	}

	// No nodes found, so just return
	if( m_num_nodes == 0 )
	{
		return;
	}
	
	mp_nodes = (CNavNode*)Mem::Malloc( m_num_nodes * sizeof( CNavNode ));

	// Iterate over the nodes and add them to the array.
	m_current_node = 0;	
	
	for( i = 0; i < p_nodearray->GetSize(); ++i )
	{
		Script::CStruct *p_node_struct = p_nodearray->GetStructure( i );
		Dbg_MsgAssert( p_node_struct, ("Error getting node from node array for navigation" ));

		if( !skate_mod->ShouldBeAbsentNode( p_node_struct ))
		{
			uint32 class_checksum = 0;		
			p_node_struct->GetChecksum( CRCD( 0x12b4e660, "Class" ), &class_checksum );
			if( class_checksum == CRCD( 0x4c23a77e, "Waypoint" ))
			{
				uint32 type_checksum = 0;		
				p_node_struct->GetChecksum( CRCD( 0x7321a8d6, "Type" ), &type_checksum );
				if( type_checksum == CRCD( 0x9784feb5, "HorseNav" ))
				{
					AddNavNode( i, p_node_struct );
					++m_current_node;
				}
			}
		}
	}
	
	// We are creating a table of all nodes, and the pointer to the CNavNode for that node, so we can do a reverse lookup.
	int num_nodes = p_nodearray->GetSize();
	CNavNode **pp_navnodes = (CNavNode**)Mem::Malloc( num_nodes * sizeof( CNavNode* ));
	for( int i = 0; i < num_nodes; ++i )
	{
		pp_navnodes[i] = NULL;
	}

	// Now fill it in.
	CNavNode* p_node;
	for( int node = 0; node < m_num_nodes; ++node )
	{
		p_node = &mp_nodes[node];
		pp_navnodes[p_node->GetNode()] = p_node;
	}

	// Now go through all the nodes.
	for( int node = 0; node < m_num_nodes; ++node )
	{
		p_node = &mp_nodes[node];
		for( int i = 0; i < p_node->m_num_links; ++i )
		{
			int index = (int)p_node->mp_links[i];

			Dbg_MsgAssert( index < num_nodes, ( "Node %d, Nav link node (%d) out of range (0 .. %d). Bad Node array?", p_node->m_node, index, num_nodes ));
			Dbg_MsgAssert( pp_navnodes[index], ( "NavNode %d linked to something (node %d) that is not a NavNode", p_node->m_node, index ));

			p_node->mp_links[i] = pp_navnodes[index];
		}
	}

	// Don't need the temporary array anymore.
	Mem::Free( pp_navnodes );
}



Mth::Vector	CNavManager::GetPos( const CNavNode* p_node ) const
{
	Dbg_Assert( p_node );
	return p_node->GetPos();
}


}
			   



================================================
FILE: Code/Sk/Objects/navigation.h
================================================
//////////////////////////////////////////
// navigation.h

#ifndef __OBJECTS_NAVIGATION_H
#define __OBJECTS_NAVIGATION_H
                        
#include 
#include 
#include 
                           
namespace Script
{
	class	CArray;
	class	CStruct;
}

#define	MAX_NAV_LINKS 12

namespace Obj
{

class	CNavManager;
//class	CWalkComponent;
//struct	SHangNavData;
//struct	SLadderNavData;

//enum ENavNodeFlag
//{	
//	LIP_OVERRIDE,		// always do a lip trick on this
//	DEFAULT_LINE,
//	ACTIVE,
//	CLIMBING,
//	LADDER
//};



// Forward declaration.
class CNavNode;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
struct sAStarNode
{
	enum EAStarNodeFlag
	{
		IN_OPEN		= 0x01,
		IN_CLOSED	= 0x02
	};

	uint32			m_flags;
	float			m_cost_from_start;
	float			m_cost_to_goal; 
	float			m_total_cost; 
	CNavNode*		mp_parent;


	bool			InOpen( void )		{ return m_flags & IN_OPEN; }
	bool			InClosed( void )	{ return m_flags & IN_CLOSED; }
	void			Reset();
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CNavNode : public Spt::Class
{
	friend class		CNavManager;

public:						  
	
	const sint16		GetNode() const				{ return m_node; }
	sAStarNode*			GetAStarNode( void )		{ return &m_astar_node; }
	Mth::Vector&		GetPos( void )				{ return m_pos; }
	int					GetNumLinks( void )			{ return m_num_links; }
	CNavNode*			GetLink( int l )			{ return mp_links[l]; }

protected:

	Mth::Vector			m_pos;						// Position of the node.
	CNavNode*			mp_links[MAX_NAV_LINKS];
	sint16				m_num_links;
	sint16				m_node;						// Number of the node in the trigger array.
	sAStarNode			m_astar_node;				// Used during the A* pathfinding routine.

private:
	const Mth::Vector&	GetPos() const;
};



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
class CNavManager : public Spt::Class
{

public:
						CNavManager();
						~CNavManager();	
	
	void 				Cleanup();
									 
	void				AddNavsFromNodeArray( Script::CArray * p_node_array );
	void 				AddNavNode( int node_number, Script::CStruct *p_node_struct );
	
	int					GetNumNodes()									{ return m_num_nodes; }
	Script::CArray*		GetNodeArray()									{ return mp_node_array; }
	void				SetNodeArray( Script::CArray* p_nodeArray )		{ mp_node_array = p_nodeArray; }

	CNavNode*			GetNavNodeByIndex( int index )					{ return mp_nodes + index; }
	CNavNode*			GetNavNodeByNodeNumber( int node_num );
	
	Mth::Vector			GetPos( const CNavNode *p_node ) const;	 

private:

	CNavNode*	  		mp_nodes;				// pointer to array of nodes
	Script::CArray*		mp_node_array;
	int					m_num_nodes;
	int					m_current_node;
};
 


CNavNode* CalculateNodePath( Mth::Vector pos, Mth::Vector target_pos );


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
inline const Mth::Vector& CNavNode::GetPos() const
{
	return m_pos;
}





} 

			
#endif			// #ifndef __OBJECTS_RAIL_H
			




================================================
FILE: Code/Sk/Objects/objecthook.cpp
================================================
///////////////////////////////////////////////////////
// ObjectHook.cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#ifdef	__PLAT_NGPS__	
#include 
#endif




namespace Obj
{


CObjectHookManager::CObjectHookManager()
{
	mp_first_node = NULL;
	m_is_transformed = false;
	mp_node_array = NULL;
	
}

CObjectHookManager::~CObjectHookManager()
{	 
	Cleanup();  
}

void CObjectHookManager::Cleanup()
{
	while (mp_first_node)
	{
		CObjectHookNode *pNext = mp_first_node->m_pNext;
		delete mp_first_node;
		mp_first_node = pNext;			
	}    
	m_is_transformed = false;	
	// Unhook from node array
	mp_node_array = NULL;
}


void CObjectHookManager::AddObjectHookNode(int node_number, Script::CStruct *p_node_struct)
{

	// create a new ObjectHook node										   
	CObjectHookNode *pObjectHookNode = new CObjectHookNode();
	// Link into the head of the ObjectHook manager	
	// Note, this must be done before we attempt to link up with other nodes								   
	pObjectHookNode->m_pNext = mp_first_node; 							  
	mp_first_node = pObjectHookNode;

	pObjectHookNode->m_node = node_number;			// The node_number is use primarily for calculating links
	pObjectHookNode->m_active = p_node_struct->ContainsFlag( 0x7c2552b9 /*"CreatedAtStart"*/ );	// created at start or not?
	pObjectHookNode->m_flags = 0;						// all flags off by default

	SkateScript::GetPosition(p_node_struct,&pObjectHookNode->m_pos);

}

void CObjectHookManager::UpdateTransform(Mth::Matrix& transform)
{

	m_is_transformed = true;
	m_transform = transform;
}



void	CObjectHookManager::DebugRender(Mth::Matrix *p_transform)
{
	#ifdef	__PLAT_NGPS__

	Mth::Vector up(0.0f,1.0f,0.0f);	

	uint32	rgb = 0x0000ff;
	
	uint32 current = 12345678;

	
	
	CObjectHookNode *pObjectHookNode;
	pObjectHookNode = mp_first_node;
	int count = 0;
	// then draw the node positions as litle red lines	
	pObjectHookNode = mp_first_node;
	while (pObjectHookNode)
	{
		count++;
		if (pObjectHookNode->m_active)
		{
			rgb = 0xff0000;
		}
		else
		{
			rgb = 0x00ff00;
		}
	
		// check for context changes
		if (current != rgb)
		{
			if ( current != 12345678)
			{
				NxPs2::EndLines3D();
			}
			
			current = rgb;
			NxPs2::BeginLines3D(0x80000000 + (0x00ffffff & rgb));
		}
	
		Mth::Vector v0, v1;
		v0 = pObjectHookNode->m_pos;
		v1 = v0 + up * 24.0f;
		v0[W] = 1.0f;		// Make homgeneous
		v1[W] = 1.0f;

		if (p_transform)
		{
			v0 = p_transform->Transform(v0);
			v1 = p_transform->Transform(v1);
		}

		NxPs2::DrawLine3D(v0[X],v0[Y],v0[Z],v1[X],v1[Y],v1[Z]);
		
		pObjectHookNode = pObjectHookNode->m_pNext;
		
	}

	
	
	// only if we actually drew some
	if ( current != 12345678)
	{
		NxPs2::EndLines3D();
	}


	#endif

}




void	CObjectHookManager::AddObjectHooksFromNodeArray(Script::CArray * p_nodearray)
{
	Dbg_MsgAssert(!mp_node_array,("Setting two node arrays in ObjectHook manager"));
	mp_node_array = p_nodearray;
	
	
	uint32 i;	 
	for (i=0; iGetSize(); ++i)
	{
		Script::CStruct *p_node_struct=p_nodearray->GetStructure(i);
		Dbg_MsgAssert(p_node_struct,("Error getting node from node array for ObjectHook generation"));

		uint32 class_checksum = 0;		
		p_node_struct->GetChecksum( 0x12b4e660 /*"Class"*/, &class_checksum );
		if (class_checksum == 0xd18719d5)		  // checksum 'ObjectHook'
		{
			AddObjectHookNode( i, p_node_struct);
		}
	}
}


const Mth::Vector&	CObjectHookNode::GetPos() const
{
	return m_pos;
}


Mth::Vector	CObjectHookManager::GetPos(const CObjectHookNode *p_node) 	const
{
	if (!m_is_transformed)
	{
		return p_node->GetPos();
	}
	else
	{
		Mth::Vector v = m_transform.Transform(p_node->GetPos());
		return v;
	}
}


}
			   



================================================
FILE: Code/Sk/Objects/objecthook.h
================================================
//////////////////////////////////////////
// ObjectHook.h

#ifndef __OBJECTS_OBJECTHOOK_H
#define __OBJECTS_OBJECTHOOK_H

namespace Script
{
	class	CArray;
	class	CStruct;
}



namespace Obj
{

class	CObjectHookManager;

enum EObjectHookNodeFlag
{
	GRAB_OVERRIDE = 0x01,
};

class CObjectHookNode : public Spt::Class
{
	friend class  CObjectHookManager;

public:						  
	const sint16			GetNode() const 	  		{return m_node;}
	void					SetActive(bool active)		{m_active = active;}
	const bool				IsActive() const				{return m_active;}

	void					SetFlag(EObjectHookNodeFlag flag) {m_flags |= flag;}
	void					ClearFlag(EObjectHookNodeFlag flag) {m_flags &= ~flag;}
	bool					GetFlag(EObjectHookNodeFlag flag) const {return m_flags & flag;}

	CObjectHookNode *		GetNext(){return m_pNext;}

protected:

	Mth::Vector m_pos;			// position of the node
	CObjectHookNode *m_pNext;			// Pointer to next node in master list	 	(or NULL if none)
	sint16 m_node;					// Number of the node in the trigger array
	char m_active;				// ObjectHook segment starting at this node: active or not?
	char m_flags;				// flags for ObjectHook segment

// the position getting functions are private
// so you can only access them through the ObjectHook manager
// (as you need to know if it's transformed or not)
private:
	const Mth::Vector&	GetPos() const;	 
						  
};



class CObjectHookManager : public Spt::Class
{

public:
					CObjectHookManager();
				   ~CObjectHookManager();	

	void 			Cleanup();
								 
	void			AddObjectHooksFromNodeArray(Script::CArray * p_node_array);
	void 			AddObjectHookNode(int node_number, Script::CStruct *p_node_struct);
	void 			AddedAll();
	void			UpdateTransform(Mth::Matrix& transform);
	
	void 			SetActive( int node, int active, bool wholeObjectHook );
	bool 			IsActive( int node );
	bool			IsMoving(){return m_is_transformed;}
	
	void			DebugRender(Mth::Matrix *p_transform = NULL);
	void			RemoveOverlapping();
	
	CObjectHookNode*		GetFirstNode(){return mp_first_node;}
	
	Script::CArray *	GetNodeArray(){return mp_node_array;}
	void				SetNodeArray(Script::CArray *p_nodeArray){mp_node_array = p_nodeArray;}

// accessor functions for the ObjectHook node
// we only access their positions via the ObjectHook manager
// as only the ObjectHook manager knows the transform of the object to
// which they are attached
// These nodes are "instances", in that the same node might be used in
// more than one place
// so these functions only make sense in conjunction with the ObjectHook_manager
	Mth::Vector	GetPos(const CObjectHookNode *p_node) 	const;	 

private:
	CObjectHookNode*  			mp_first_node;	
	bool						m_is_transformed;
	Mth::Matrix					m_transform;
	Script::CArray *			mp_node_array;

};


} 

			
#endif			// #ifndef __OBJECTS_OBJECTHOOK_H
			




================================================
FILE: Code/Sk/Objects/ped.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       ped.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/15/2000
//****************************************************************************

/*
	MD:  Derived from CMovingObject, the ped just adds some functionality
	for jumping out of the way of the skater.  That's all!  Other than
	that, the ped inherits all the abilities of the moving object class,
	such as having an animating model, that can follow paths, or play
	sounds, etc...
*/

/*
	GJ:  The above comment used to be true, but now peds are defined as
	having skinned models and animations, running extra scripts to shut
	off extra bones, etc.  Some of this stuff should be moved up to
	the CMovingObject level (in particular, the skinned model/anim stuff)
*/

// start autoduck documentation
// @DOC ped
// @module ped | None
// @subindex Scripting Database
// @index script | ped
    
/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

const float DEFAULT_PED_TURN_DIST = 2.0f;
const float DEFAULT_PED_MAX_VEL = 2.5f;
const float DEFAULT_PED_ACCELERATION = 4.0f;
const float DEFAULT_PED_DECELERATION = 1.0f;
const float DEFAULT_PED_STOP_DIST = 2.0f;		// stopping distance from wp

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CreatePed(CGeneralManager* p_obj_man, Script::CStruct* pNodeData)
{
	CMovingObject* pPed = new CMovingObject;
	
	pPed->MovingObjectCreateComponents();

	pPed->SetType( SKATE_TYPE_PED );

	Dbg_MsgAssert(pPed, ("Failed to create pedestrian."));
	p_obj_man->RegisterObject(*pPed);
	
	// GJ:  This should come before the model gets initialized
	// get position, from the node that created us:
	SkateScript::GetPosition( pNodeData, &pPed->m_pos );
		
	pPed->MovingObjectInit( pNodeData, p_obj_man );

	Script::RunScript( CRCD(0xf3eec45e,"ped_add_components"), pNodeData, pPed );
	// Finalize the components, after everything has been added	
	pPed->Finalize();
	Script::RunScript( CRCD(0x955a2767,"ped_init_model"), pNodeData, pPed );

	// designer controlled variables:
	// set defaults, to be overridden by script values if they exist:
	Obj::CMotionComponent* pMotionComponent = GetMotionComponentFromObject( pPed );
	
	pMotionComponent->m_max_vel = ( MPH_TO_INCHES_PER_SECOND ( DEFAULT_PED_MAX_VEL ) );
	pMotionComponent->m_acceleration = FEET_TO_INCHES( DEFAULT_PED_ACCELERATION );
	pMotionComponent->m_deceleration = FEET_TO_INCHES( DEFAULT_PED_DECELERATION );
	pMotionComponent->m_stop_dist = FEET_TO_INCHES( DEFAULT_PED_STOP_DIST );
	pMotionComponent->EnsurePathobExists( pPed );
	pMotionComponent->GetPathOb()->m_enter_turn_dist = FEET_TO_INCHES( DEFAULT_PED_TURN_DIST );

	// don't let his pitch get messed up when going up hills...
	pMotionComponent->m_movingobj_flags |= MOVINGOBJ_FLAG_NO_PITCH_PLEASE;

	// need to synchronize rendered model's position to initial world position
	GetModelComponentFromObject( pPed )->FinalizeModelInitialization();

	// now that the skeleton has been updated at least once,
	// we can turn off the extra bones that don't animate:	
	Script::RunScript( CRCD(0x48ca2d26,"ped_disable_bones"), NULL, pPed );
	pPed->SetProfileColor(0xc000c0);				// magenta = ped

	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj



================================================
FILE: Code/Sk/Objects/ped.h
================================================
//****************************************************************************
//* MODULE:         Sk/Objects
//* FILENAME:       ped.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  10/15/2000
//****************************************************************************

#ifndef __OBJECTS_PED_H
#define __OBJECTS_PED_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Script
{
	class CStruct;
}
				   
namespace Obj
{

	class CGeneralManager;

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

	void CreatePed(CGeneralManager* p_obj_man, Script::CStruct* pNodeData);

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_PED_H


================================================
FILE: Code/Sk/Objects/proxim.cpp
================================================
///////////////////////////////////////////////////////
// Proxim.cpp

		  
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 		   // used to get position of the skater's camera
#include 

//#define	DEBUG_PROXIM

namespace Obj
{

void Proxim_Init()
{
    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	
	if (skate_mod->mpProximManager)
	{
		Proxim_Cleanup();
	}
	skate_mod->mpProximManager = new CProximManager();
	skate_mod->mpProximManager->m_pFirstNode = NULL;
}

void Proxim_Cleanup()
{
    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	if (skate_mod->mpProximManager)
	{
		CProximNode *pNode = skate_mod->mpProximManager->m_pFirstNode;	   
		while (pNode)
		{
			CProximNode *pNext = pNode->m_pNext;
			delete pNode;
			pNode = pNext;			
		}

		delete skate_mod->mpProximManager;
		skate_mod->mpProximManager = NULL;
	}
}

#define CHECKSUM_TERRAIN_TYPE	0x54cf8532  // "terrainType"

void Proxim_AddNode(int node,bool active)
{
//	printf ("It's a Proxim (%d)\n",node);
    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Dbg_Assert(skate_mod->mpProximManager);
	
	// create a new Proxim node										   
	CProximNode *pProximNode = new CProximNode();

	// Link into the head of the Proxim manager									   
	pProximNode->m_pNext = skate_mod->mpProximManager->m_pFirstNode; 							  
	skate_mod->mpProximManager->m_pFirstNode = pProximNode;

	pProximNode->m_node = node;

	pProximNode->m_active = active;  // created at start or not?
	
	pProximNode->mp_sector = NULL;
	pProximNode->m_shape = CProximNode::vSHAPE_SPHERE;  

	// Get pointer to the Proxim data	
	Script::CScriptStructure *pNode = SkateScript::GetNode(node);
	
	// Get name
	if (pNode->GetChecksum("name",&pProximNode->m_name))
	{
#ifdef	DEBUG_PROXIM
		printf ("Proxim: Got Name %s\n",Script::FindChecksumName(pProximNode->m_name));
#endif	
	}
	else
	{
		Dbg_MsgAssert(0,("No name in proxim node %d\n",node));
	}

	// Get shape
	uint32 shape_checksum;
	if (pNode->GetChecksum("shape", &shape_checksum))
	{
		pProximNode->m_shape = CProximNode::sGetShapeFromChecksum(shape_checksum);
	}

	// Try to find geometry
	pProximNode->InitGeometry(Nx::CEngine::sGetMainScene()->GetSector(pProximNode->m_name));

	SkateScript::GetPosition(pNode,&pProximNode->m_pos);
#ifdef	DEBUG_PROXIM				
		Dbg_Message("Proxim: Pos = (%f, %f, %f)", pProximNode->m_pos[X], pProximNode->m_pos[Y], pProximNode->m_pos[Z]);
#endif	

	pProximNode->m_type = 0;  
	pNode->GetInteger( 0x7321a8d6 /* type */, &pProximNode->m_type );
#ifdef	DEBUG_PROXIM
	printf ("Proxim:  Type = %d\n",pProximNode->m_type);
#endif
	pNode->GetFloat (0xc48391a5 /*radius*/, &pProximNode->m_radius);  
	Dbg_MsgAssert(pProximNode->mp_sector || pProximNode->m_radius > 0.0f,( "Radius %f too small in proxim node %d\n",pProximNode->m_radius,node));	
#ifdef	DEBUG_PROXIM
	printf ("Proxim:  Radius = %f\n",pProximNode->m_radius);
#endif	
	pProximNode->m_radius_squared = pProximNode->m_radius * pProximNode->m_radius;	

	if (pNode->GetChecksum(0x2ca8a299,&pProximNode->m_script))		// Checksum of TriggerScript
	{
#ifdef	DEBUG_PROXIM
		printf ("Proxim: Got Script %s\n",Script::FindChecksumName(pProximNode->m_script));
#endif	
	}
	else
	{
		Dbg_MsgAssert(0,("No triggerscript in proxim node %d\n",node));
	}

	
	pProximNode->m_proxim_object = 0;	
	pProximNode->m_active_objects = 0;	
	pProximNode->m_active_scripts = 0;	
	if (pNode->ContainsFlag(CRCD(0x5113f19c,"ProximObject")))
	{
		// for proxim objects, it's best if we start "outside" the objects
		// so if we are actually inside, then the event will trigger
		pProximNode->m_outside_flags = 0xffffffff;			// say all outside
		pNode->GetChecksum("Name",&pProximNode->m_proxim_object);	
	}
	else
	{
		// for non-proxim objects, it's best if we start "inside" the node
		// in case something is depending on the outside script being called first
		// to keep things shut off.  We'll do this by starting with it active
		// and then setting it to inactive.
		pProximNode->m_outside_flags = 0;			// say all inside for now....
		pProximNode->m_active = true;

		// Set the script active flag for each skater that exists
		Obj::CObject *p_obj;
		int object_id = 0;
		while ((p_obj  = Obj::CCompositeObjectManager::Instance()->GetObjectByID(object_id)) && (object_id < 32))
		{
			Obj::CSkater *p_skater = (Obj::CSkater *) p_obj;
			if (p_skater->IsLocalClient())
			{
				pProximNode->m_active_scripts |= 1 << object_id;
			}
			object_id++;
		}

		pProximNode->SetActive(false);
		pProximNode->SetActive(active);
	}
				
}

void Proxim_AddedAll()
{
	
//	printf("Added all Proxims\n");		 
		 
    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Dbg_Assert(skate_mod->mpProximManager);
	CProximNode *pProximNode = skate_mod->mpProximManager->m_pFirstNode;
	while (pProximNode)
	{
		// do any final per-node processing here
		pProximNode = pProximNode->m_pNext;
	}				 
				 	
}			   

void Proxim_SetActive( int node, int active )
{
    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	
	if (!skate_mod->mpProximManager) return;
	
	CProximNode *pProximNode = skate_mod->mpProximManager->m_pFirstNode;
	while (pProximNode)
	{
		if (pProximNode->m_node == node)
		{
			pProximNode->SetActive(active);
			return;
		}
		pProximNode = pProximNode->m_pNext;
	}				 
}

CProximNode * CProximManager::sGetNode(uint32 checksum)
{
    Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Dbg_Assert(skate_mod->mpProximManager);
	CProximNode *pProximNode = skate_mod->mpProximManager->m_pFirstNode;
	while (pProximNode)
	{
		if (pProximNode->m_name == checksum)
		{
			return pProximNode;
		}
		pProximNode = pProximNode->m_pNext;
	}				 

	return NULL;
}

// Update all the proximity nodes				
// for a certain skater
// checking for proximity either with the skater or the camera					
void Proxim_Update ( uint32 trigger_mask, CObject* p_script_target, const Mth::Vector& pos )
{
#if 0		// TT9254: Too many things were having problems with this.  The proxim node scripts themselves
			// will have to do this.
	// Don't update if the game is paused
	if( Mdl::FrontEnd::Instance()->GamePaused() && !(Script::GetInteger(CRCD(0x45e46afd,"goal_editor_placement_mode"))) )
	{
		return;
	}

#if 0
	// Don't update if the game is playing a CamAnim
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	if ( skate_mod->GetMovieManager()->IsRolling() )
	{
		return;
	}
#endif
#endif

	CProximManager* proxim_manager = Mdl::Skate::Instance()->mpProximManager;
	
	if (!proxim_manager) return;
	
	CProximNode *pProximNode = proxim_manager->m_pFirstNode;

	while (pProximNode)
	{
		if (!pProximNode->m_active)
		{
			pProximNode = pProximNode->m_pNext;
			continue;
		}
			
		bool now_inside = pProximNode->CheckInside(pos);

		if ( trigger_mask & pProximNode->m_outside_flags)
		{
			// trigger is currently outside, check if moved to inside
			if (now_inside)
			{
			
				pProximNode->m_outside_flags &= ~trigger_mask;
				
				if ( ! pProximNode->m_proxim_object )
				{
#ifdef	DEBUG_PROXIM				
				printf ("+++++ NOW INSIDE, mask = %d running script %s \n",trigger_mask, Script::FindChecksumName(pProximNode->m_script));
#endif				
					proxim_manager->m_inside_flag = true;
					proxim_manager->m_inside_flag_valid = true;
					
					if (p_script_target)
					{
						p_script_target->SpawnAndRunScript(pProximNode->m_script,pProximNode->m_node );
						Dbg_MsgAssert(p_script_target->GetID() < 32, ("Object ID %d is greater than 31", pProximNode->m_proxim_object));
						pProximNode->m_active_scripts |= 1 << p_script_target->GetID();
					}
					else
					{
						//Script::SpawnScript(pProximNode->m_script, NULL, NO_NAME, NULL, pProximNode->m_node );
						Script::RunScript(pProximNode->m_script);
					}

					proxim_manager->m_inside_flag_valid = false;
				}
				else if (p_script_target)
				{
					// Need to create an object with the id pProximNode->m_proxim_object

#ifdef	DEBUG_PROXIM				
				printf ("+++++ NOW INSIDE, creating object %s running script %s \n",Script::FindChecksumName(pProximNode->m_proxim_object),Script::FindChecksumName(pProximNode->m_script));
#endif				
					
					Obj::CCompositeObject* pObj = Obj::CCompositeObjectManager::Instance()->CreateCompositeObject();
					pObj->SetID(pProximNode->m_proxim_object + p_script_target->GetID());  // Mick mangle the proxim object ID with the player's ID
					pObj->m_pos = pProximNode->m_pos  ;

					Dbg_MsgAssert(p_script_target->GetID() < 32, ("Object ID %d is greater than 31", pProximNode->m_proxim_object));
					pProximNode->m_active_objects |= 1 << p_script_target->GetID();

					Script::CStruct component_params;	// ProximNode params
					if (pProximNode->HasGeometry())
					{
						// Only add if we want the geometry used
						component_params.AddChecksum("ProximNode", pProximNode->m_name);
					}
					pObj->CreateComponentsFromArray(Script::GetArray("proximobj_composite_structure"), &component_params);   
				 	pObj->Finalize();
   
					Script::CStruct params;	// dummy empty params
					pObj->SwitchScript(pProximNode->m_script,¶ms);
				}
			}			
		}
		else
		{
			// skater is currently inside, check if moved outside
			if (!now_inside)
			{
				pProximNode->m_outside_flags |= trigger_mask;				
				if ( ! pProximNode->m_proxim_object )
				{
	#ifdef	DEBUG_PROXIM				
					printf ("----- NOW OUTSIDE, mask = %d running script %s \n",trigger_mask,Script::FindChecksumName(pProximNode->m_script));
	#endif				
					proxim_manager->m_inside_flag = false;
					proxim_manager->m_inside_flag_valid = true;

					if (p_script_target)
					{
						p_script_target->SpawnAndRunScript(pProximNode->m_script,pProximNode->m_node );
						pProximNode->m_active_scripts &= ~(1 << p_script_target->GetID());
					}
					else
					{
						//Script::SpawnScript(pProximNode->m_script, NULL, NO_NAME, NULL, pProximNode->m_node );
						Script::RunScript(pProximNode->m_script);
					}

					proxim_manager->m_inside_flag_valid = false;
				}									
				else if (p_script_target)
				{
					// Need to create an object with the id pProximNode->m_proxim_object

#ifdef	DEBUG_PROXIM				
					printf ("+++++ NOW OUTSIDE, killing %s running script %s \n",Script::FindChecksumName(pProximNode->m_proxim_object),Script::FindChecksumName(pProximNode->m_script));
#endif				
					Obj::CObject *p_obj  = Obj::CCompositeObjectManager::Instance()->GetObjectByID(pProximNode->m_proxim_object  + p_script_target->GetID());
					if (p_obj)
					{
						p_obj->MarkAsDead();					
					}
					pProximNode->m_active_objects &= ~(1 << p_script_target->GetID());
				}
			}			
		}		
		pProximNode = pProximNode->m_pNext;
	}				 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CProximNode::EShape CProximNode::sGetShapeFromChecksum(uint32 checksum)
{
	switch (checksum)
	{
	case 0xaa069978: // Sphere
		return vSHAPE_SPHERE;

	case 0xe460abde: // BoundingBox
		return vSHAPE_BBOX;

	case 0x3f1e5a7: // NonAxisAlignedBoundingBox
		return vSHAPE_NON_AA_BBOX;

	case 0x64fba415: // Cylinder
		return vSHAPE_CYLINDER;

	case 0x6aadf154: // Geometry
		return vSHAPE_GEOMETRY;

	default:
		Dbg_MsgAssert(0, ("Unknown shape checksum %x\n", checksum));
		return vSHAPE_SPHERE;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CProximNode::SetActive(bool active)
{
	// Check for changes
	if (m_active != active)
	{
		if (m_active)
		{
			// Turning off node, so treat like were going outside
			if ( m_proxim_object )
			{
				// Delete all created objects
#ifdef	DEBUG_PROXIM			    
				printf ("----- SETTING ProximNode INACTIVE, killing %s running script %s \n",Script::FindChecksumName(m_proxim_object),Script::FindChecksumName(m_script));
#endif				
				int object_id = 0;
				while (m_active_objects)
				{
					if (m_active_objects & 0x1)	// Check if this ID is turned on
					{
						Obj::CObject *p_obj  = Obj::CCompositeObjectManager::Instance()->GetObjectByID(m_proxim_object + object_id);
						if (p_obj)
						{
							p_obj->MarkAsDead();					
						}
					}

					// Advance ID
					m_active_objects = m_active_objects >> 1;
					object_id++;
				}
			}									
			else
			{
#ifdef	DEBUG_PROXIM				
				printf ("----- SETTING ProximNode INACTIVE, running script %s \n",Script::FindChecksumName(m_script));
#endif				
				CProximManager* proxim_manager = Mdl::Skate::Instance()->mpProximManager;

				if (proxim_manager)
				{
					int object_id = 0;
					while (m_active_scripts)
					{
						if (m_active_scripts & 0x1)	// Check if this ID is turned on
						{
							Obj::CObject *p_obj  = Obj::CCompositeObjectManager::Instance()->GetObjectByID(object_id);
							Dbg_MsgAssert(p_obj, ("Can't find object to play script on"));
							proxim_manager->m_inside_flag = false;

							proxim_manager->m_inside_flag_valid = true;
							p_obj->SpawnAndRunScript( m_script, m_node );
							proxim_manager->m_inside_flag_valid = false;
						}

						// Advance ID
						m_active_scripts = m_active_scripts >> 1;
						object_id++;
					}
				}
			}
		}

		// Make everything outside
		m_outside_flags = 0xffffffff;

		m_active = active;
	}

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CProximNode::InitGeometry(Nx::CSector *p_sector)
{
	mp_sector = p_sector;

	if (!mp_sector)
	{
		// No Geometry
		Dbg_MsgAssert(m_shape == CProximNode::vSHAPE_SPHERE, ("ProximNode %s with shape %d has no geometry.", Script::FindChecksumName(m_name), m_shape));
		return;
	}

#ifdef	DEBUG_PROXIM				
	Dbg_Message("Found Geometry for Proxim node %s at address %x", Script::FindChecksumName(m_name), mp_sector);
#endif

	switch (m_shape)
	{
	case CProximNode::vSHAPE_SPHERE:
		Dbg_MsgAssert(0, ("ProximNode with sphere shape has geometry."));
		break;

	case CProximNode::vSHAPE_BBOX:
		m_bbox = mp_sector->GetBoundingBox();
#ifdef	DEBUG_PROXIM				
		Dbg_Message("Bounding box of geometry (%f, %f, %f) - (%f, %f, %f)",
					m_bbox.GetMin()[X], m_bbox.GetMin()[Y], m_bbox.GetMin()[Z],
					m_bbox.GetMax()[X], m_bbox.GetMax()[Y], m_bbox.GetMax()[Z]);
#endif	
		break;

	case CProximNode::vSHAPE_NON_AA_BBOX:
		Dbg_MsgAssert(0, ("ProximNode with non axis aligned bounding box shape not supported yet."));
		break;

	case CProximNode::vSHAPE_CYLINDER:
		Dbg_MsgAssert(0, ("ProximNode with cylinder shape not supported yet."));
		break;
	case CProximNode::vSHAPE_GEOMETRY:
		Dbg_MsgAssert(0, ("ProximNode with geometry shape not supported yet."));
		break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool				CProximNode::HasGeometry() const
{
	return mp_sector != NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CProximNode::CheckInside(const Mth::Vector &object_pos) const
{
	Mth::Vector		node_to_object = object_pos - m_pos;
	float			dist_squared = node_to_object.LengthSqr();	   

	bool inside = false;

	switch (m_shape)
	{
	case vSHAPE_SPHERE:
		inside = (dist_squared <= m_radius_squared);
		break;

	case vSHAPE_BBOX:
		inside = m_bbox.Within(object_pos);
#ifdef	DEBUG_PROXIM
		m_bbox.DebugRender(0xff0000ff);
		{
			Mth::Vector closest_point;
			m_bbox.GetClosestIntersectPoint(object_pos, closest_point);
//			Gfx::AddDebugStar(closest_point);
		}
#endif //	DEBUG_PROXIM
		break;

	default:
		Dbg_MsgAssert(0, ("Can't handle ProximNode type %d", m_shape));
	}

	return inside;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector 		CProximNode::GetClosestIntersectionPoint(const Mth::Vector &object_pos) const
{
	Mth::Vector intersect_point;

	switch (m_shape)
	{
	case vSHAPE_SPHERE:
		{
			// Same for either within or outside sphere
			Mth::Vector direction = object_pos - m_pos;
			direction.Normalize();
			direction *= m_radius;

			intersect_point = m_pos + direction;
		}
		break;

	case vSHAPE_BBOX:
		m_bbox.GetClosestIntersectPoint(object_pos, intersect_point);
		break;

	default:
		Dbg_MsgAssert(0, ("Can't handle ProximNode type %d", m_shape));
		break;
	}

#ifdef	DEBUG_PROXIM
//	Gfx::AddDebugStar(intersect_point);
#endif //	DEBUG_PROXIM

	return intersect_point;
}

/*
{
	// Node 572 (Mick Test)
	Position = (-4060.666992,131.999329,-4604.379395)
	Angles = (0.000000,-3.141593,0.000000)
	Name = TRG_Micks_test
	Class = ProximNode
	Type = skater
    radius = 1200
	TriggerScript = xxx_test
}
*/


}
			   



================================================
FILE: Code/Sk/Objects/proxim.h
================================================
//////////////////////////////////////////
// Proxim.h

#ifndef __OBJECTS_Proxim_H
#define __OBJECTS_Proxim_H

#include 

#include 

namespace Nx
{
	class	CSector;
}

namespace Script
{
	class	CScript;
}

namespace Obj
{

	class	CSkater;

class CProximNode : public Spt::Class
{
public:
	enum EShape
	{
		vSHAPE_SPHERE = 0,
		vSHAPE_BBOX,			// Axis-aligned bounding box
		vSHAPE_NON_AA_BBOX,		// Non-axis-aligned bounding box
		vSHAPE_CYLINDER,		// Cylinder
		vSHAPE_GEOMETRY,		// Geometry data
	};

	///////////
	// Functions
	void		SetActive(bool active);
	void		InitGeometry(Nx::CSector *p_sector);
	bool		HasGeometry() const;
	bool		CheckInside(const Mth::Vector &object_pos) const;
	Mth::Vector GetClosestIntersectionPoint(const Mth::Vector &object_pos) const;

	// Checksum conversion functions
	static EShape sGetShapeFromChecksum(uint32 checksum);

	///////////
	// Variables
	int			m_node;				// Number of the node in the trigger array
	int 		m_link;				// link number
	uint32		m_name;				// checksum of name of the node in the trigger array
	Mth::Vector m_pos;				// position of the node
	Mth::Vector m_angles;			// angles of the node
	bool 		m_active;			// Proxim segment starting at this node: active or not?

	uint32 		m_proxim_object;	// checksum of name, if this is a ProximObject
	uint32		m_active_objects;	// bit number corresponds to originating object ID
	uint32		m_active_scripts;	// bit number corresponds to originating object ID

	int			m_outside_flags;	// bitmask of flags, 0 = inside the radius, 1 = outside

	int 		m_type;
	EShape 		m_shape;

	// Sphere data
	float		m_radius;
	float		m_radius_squared;

	// Geometry Data
	Nx::CSector *mp_sector;

	// BBox data
	Mth::CBBox	m_bbox;
	
	float		m_last_distance_squared;
	
	uint32 		m_script;

	CProximNode *m_pNext;

};

 

void Proxim_AddNode(int node, bool active);
void Proxim_Init();
void Proxim_Cleanup();
void Proxim_AddedAll();
// activate/deactivate Proxim:
void Proxim_SetActive( int node, int active);
void Proxim_Update ( uint32 trigger_mask, CObject* p_script_target, const Mth::Vector& pos );

class CProximManager : public Spt::Class
{
private:
	static const int vMAX_NUM_PROXIM_TRIGGERS = 8 * sizeof(uint32);
	
public:
	CProximManager() { m_inside_flag_valid = false; }

	static CProximNode * sGetNode(uint32 checksum);

	CProximNode*  m_pFirstNode;	
		
	uint32 GetProximTriggerMask ( int viewport );
	
	bool IsInsideFlagValid (   ) { return m_inside_flag_valid; }
	bool IsInside (   ) { return m_inside_flag; }
	
private:
	bool m_inside_flag;
	bool m_inside_flag_valid;
	
	friend void Proxim_Update ( uint32 trigger_mask, CObject* p_script_target, const Mth::Vector& pos );
	friend CProximNode;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline uint32 CProximManager::GetProximTriggerMask ( int viewport )
{
	return 1 << viewport;
}

} 
			
#endif // #ifndef __OBJECTS_Proxim_H
			




================================================
FILE: Code/Sk/Objects/rail.cpp
================================================
///////////////////////////////////////////////////////
// rail.cpp

#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
		  
// REMOVE
#include 
		  
#ifdef	__PLAT_NGPS__	
#include 			// for camera
#include 
#include 
#endif

//#define		DEBUG_EDITOR_RAILS

namespace Obj
{


void Rail_ComputeBB(Mth::Vector &pos1, Mth::Vector &pos2, Mth::Vector &bb_min, Mth::Vector &bb_max)
{
	float x0 = pos1.GetX();
	float x1 = pos2.GetX();
	float min_x, max_x;
	if (x0 < x1)
	{
		min_x = x0; max_x = x1;
	}
	else
	{
		min_x = x1; max_x = x0;
	}

	float y0 = pos1.GetY();
	float y1 = pos2.GetY();
	float min_y, max_y;
	if (y0 < y1)
	{
		min_y = y0; max_y = y1;
	}
	else
	{
		min_y = y1; max_y = y0;
	}

	float z0 = pos1.GetZ();
	float z1 = pos2.GetZ();
	float min_z, max_z;
	if (z0 < z1)
	{
		min_z = z0; max_z = z1;
	}
	else
	{
		min_z = z1; max_z = z0;
	}

	bb_min.Set(min_x, min_y, min_z);
	bb_max.Set(max_x, max_y, max_z);
}

CRailManager::CRailManager()
{
//	mp_first_node = NULL;
	mp_nodes = NULL;
	mp_links = NULL;
	m_num_nodes = 0;
	m_is_transformed = false;
	mp_node_array = NULL;
	
}

CRailManager::~CRailManager()
{	 
	Cleanup();  
}

void CRailManager::Cleanup()
{
//	while (mp_first_node)
//	{
//		CRailNode *pNext = mp_first_node->m_pNext;
//		delete mp_first_node;
//		mp_first_node = pNext;			
//	}    
	
	m_num_nodes = 0;
	
	Mem::Free(mp_nodes);
	mp_nodes = NULL;
	
	
	m_is_transformed = false;	
	// Unhook from node array
	mp_node_array = NULL;
}


#define CHECKSUM_TERRAIN_TYPE	0x54cf8532  // "terrainType"

// Given two rail nodes that should be linked in a rail segment
// then link them up as well as we are able
// given that we only have a NEXT and a PREV pointer
// so we give precedence to links to nodes that
// are flagged "DefaultLine"

void CRailManager::NewLink(CRailNode *p_from, CRailNode *p_to)
{
	// one of our links links to this node
	// so we need to establish next and prev links
	// or if they are established, then possibly override them			
	if (!p_from->m_pNextLink)
	{
		// this is the first time we are linking to something
		p_from->m_pNextLink = p_to;
		Rail_ComputeBB(p_from->m_pos, p_to->m_pos, p_from->m_BBMin, p_from->m_BBMax);
	}
	else
	{
	
		// already linked to something, so only override it
		// if the new node is not DEFAULT_LINE
		if (p_to->GetFlag(DEFAULT_LINE))
		{
			// This new link is DEFAULT_LINE
			// so, as long as the previous link was not, we can override it
			Dbg_MsgAssert(!p_from->m_pNextLink->GetFlag(DEFAULT_LINE),
						  ("Node %d links to both %d and %d and both are DEFAULT_LINE",
						   p_from->m_node,p_from->m_pNextLink->m_node,p_to->m_node));
			// Overriding an existing NEXT link
			p_from->m_pNextLink = p_to;
			Rail_ComputeBB(p_from->m_pos, p_to->m_pos, p_from->m_BBMin, p_from->m_BBMax);
		}
		else
		{
			// The new link is not DEFAULT_LINE
			// if the old one was, then leave it
			// if neither is, then leave it, and maybe print a warning?
			
		}
	}
	
	// Now handle the PREV link back from p_to to p_from
	
	if (!p_to->m_pPrevLink)
	{
		p_to->m_pPrevLink = p_from;
	}
	else
	{

		// something already linked to p_from via prev from p_to
		if (p_from->GetFlag(DEFAULT_LINE))
		{
			// this is the default line, so make sure the old PREV node was not
			Dbg_MsgAssert(!p_to->m_pPrevLink->GetFlag(DEFAULT_LINE),
						  ("Node %d linked from both %d and %d and both are DEFAULT_LINE",
						   p_to->m_node,p_to->m_pPrevLink->m_node,p_from->m_node));
			
			p_to->m_pPrevLink = p_from;
			// Note, we don't re-calculate the bounding box here
			// as that only applies to NEXT links
		}
	}
}


void CRailManager::AddRailNode(int node_number, Script::CStruct *p_node_struct)
{

							
	// create a new rail node										   
//	CRailNode *pRailNode = new CRailNode();
	// Link into the head of the rail manager	
	// Note, this must be done before we attempt to link up with other nodes								   
//	pRailNode->m_pNext = mp_first_node; 							  
//	mp_first_node = pRailNode;
	
	CRailNode *pRailNode = &mp_nodes[m_current_node];
	CRailLinks *pLinkNode = &mp_links[m_current_node++];
	
	uint32 class_checksum;
	p_node_struct->GetChecksum(CRCD(0x12b4e660, "class"), &class_checksum);
	bool climbing = class_checksum == CRCD(0x30c19600, "ClimbingNode");
	
	uint32 type_checksum;
	p_node_struct->GetChecksum(CRCD(0x7321a8d6, "type"), &type_checksum);

	pRailNode->m_pNextLink = NULL;
	pRailNode->m_pPrevLink = NULL;
	for (int i=0;im_link[i] = -1;						// say it's not linked to anything, initially
	}
	pRailNode->m_node = node_number;			// The node_number is use primarily for calculating links
	pRailNode->m_flags = 0;						// all flags off by default
	pRailNode->SetActive(p_node_struct->ContainsFlag( 0x7c2552b9 /*"CreatedAtStart"*/ ));	// created at start or not?
	
    // set the position from the node structure
	SkateScript::GetPosition(p_node_struct,&pRailNode->m_pos);

	if (p_node_struct->ContainsFlag( 0xf4c67f0f /*"LipOverride"*/))
	{
		pRailNode->m_flags.Set(LIP_OVERRIDE);
	}

	if (p_node_struct->ContainsFlag(  0x4de721de/*"DefaultLine"*/))
	{
		pRailNode->m_flags.Set(DEFAULT_LINE);
	}
	
	if (climbing)
	{
		pRailNode->m_flags.Set(ONLY_CLIMBING);
		pRailNode->m_flags.Set(LADDER, type_checksum == CRCD(0xc84243da, "Ladder"));
	}
	
	pRailNode->m_flags.Set(NO_HANG_WITH_RIGHT_ALONG_RAIL, p_node_struct->ContainsFlag(CRCD(0xf6dd21ca, "HangLeft")));
	pRailNode->m_flags.Set(NO_HANG_WITH_LEFT_ALONG_RAIL, p_node_struct->ContainsFlag(CRCD(0x5e9ce29b, "HangRight")));
	pRailNode->m_flags.Set(NO_CLIMBING, p_node_struct->ContainsFlag(CRCD(0xf22bfc6d, "NoClimbing")));
    
	if (climbing)
	{
		// set the orientation from the node structure
		Mth::Vector angles;
		SkateScript::GetAngles(p_node_struct, &angles);
		Mth::Matrix matrix;
		pRailNode->m_orientation = matrix.SetFromAngles(angles);
	}

	// no terrain types for climbing nodes
	if (!climbing)
	{
		pRailNode->m_terrain_type = 0;  // default terrain type...
		uint32 terrain_checksum;
		int terrain = 0;
		p_node_struct->GetChecksum( CHECKSUM_TERRAIN_TYPE, &terrain_checksum, Script::ASSERT );
		terrain = Env::CTerrainManager::sGetTerrainFromChecksum(terrain_checksum);
		Dbg_MsgAssert(terrain >= 0 && terrain < 256,("Rail node %d, terrain type %d too big\n",node_number,terrain));
		pRailNode->m_terrain_type = (uint8) terrain ;
	}


	int links = SkateScript::GetNumLinks(p_node_struct);
	if (links)
	{
		Dbg_MsgAssert(links <= MAX_RAIL_LINKS,("Rail node %d has %d linkes, max is %d",node_number,links,MAX_RAIL_LINKS));
		for (int i=0;im_link[i] = SkateScript::GetLink(p_node_struct,i);
		}
	}	
	
	Rail_ComputeBB(pRailNode->m_pos, pRailNode->m_pos, pRailNode->m_BBMin, pRailNode->m_BBMax);

// Linking is now donw after they are all loaded
// Making it O(n) rather than O(n^2)
/*	
	// try going through all the nodes to find one linked to this one
	// and to find the target node, if it exists
	CRailNode *p_link_node = pRailNode->m_pNext;
	while (p_link_node)
	{
		// Check links from the new node to all the existing nodes
		for (int i=0;im_link[i] == p_link_node->m_node)
			{
				NewLink(pRailNode,p_link_node);
			}
		}
		
		// check links from the existing nodes to the new node													  
		for (int i=0;im_link[i] == pRailNode->m_node)
			{
				NewLink(p_link_node,pRailNode);
			}
		}
	   	p_link_node = p_link_node->m_pNext;		
	}
*/	
}

// returns a positive number that is the amount of an axis
// required to totally encompass the 1D line segments a-b and c-d
static inline float span_4(float a, float b, float c, float d)
{
	float min = a;
	if (b < min) min = b;
	if (c < min) min = c;
	if (d < min) min = d;
	float max = a;
	if (b > max) max = b;
	if (c > max) max = c;
	if (d > max) max = d;
	return max-min;	
}

															 
// returns the amount two 1D line segments overlap
// works by adding together the length of both lines
// and then subtracting the "span" of the the min and max points of the lines															 
static inline float overlap(float a, float b, float c, float d)
{
	return  Mth::Abs(b-a) + Mth::Abs(d-c) - span_4(a,b,c,d);
}

static inline bool nearly_the_same(float a, float b, float c, float d, float nearly = 1.0f)
{
	return ( Mth::Abs(a-b) < nearly
		 && Mth::Abs(b-c) < nearly
		 && Mth::Abs(c-d) < nearly);
}	

void	CRailManager::RemoveOverlapping()
{
	printf ("Starting overlapping rail removal\n");
	
	int	removed =0;
//	CRailNode *pRailNode = mp_first_node;
//	while (pRailNode)
//	{
	for (int node=0;nodem_pNextLink)	   	// it's a segment
		{

			Mth::Vector	start = pRailNode->m_pos;
			Mth::Vector	end = pRailNode->m_pNextLink->m_pos;

			Mth::Vector dir = end-start;
			dir.Normalize();

			// we expand the bounding box, as they might not really be that close			
			Mth::Vector bb_min = pRailNode->m_BBMin;
			bb_min[X] -= 16.0f;
			bb_min[Y] -= 16.0f;
			bb_min[Z] -= 16.0f;
			Mth::Vector bb_max = pRailNode->m_BBMax;
			bb_max[X] += 16.0f;
			bb_max[Y] += 16.0f;
			bb_max[Z] += 16.0f;
			
			
			int check_node = node+1;
//			CRailNode *pCheckNode = pRailNode->m_pNext;
			while (check_nodeIsActive())
			{
				CRailNode *pCheckNode = &mp_nodes[check_node];
				if (pCheckNode->m_pNextLink)	   	// it's a segment
				{
				   	// first check to see if bounding boxes overlap
					  
					if (!(pCheckNode->m_BBMin.GetX() > bb_max.GetX()))
					if (!(pCheckNode->m_BBMax.GetX() < bb_min.GetX()))
					if (!(pCheckNode->m_BBMin.GetZ() > bb_max.GetZ()))
					if (!(pCheckNode->m_BBMax.GetZ() < bb_min.GetZ()))
					if (!(pCheckNode->m_BBMin.GetY() > bb_max.GetY()))
					if (!(pCheckNode->m_BBMax.GetY() < bb_min.GetY()))
					{					
					
						// bounding boxes overlap 						
						// check to see if rails are roughly parallel
						Mth::Vector check_start = pCheckNode->m_pos;
						Mth::Vector check_end = pCheckNode->m_pNextLink->m_pos;
						Mth::Vector check_dir = check_end - check_start;

						check_dir.Normalize();						
						float dot = Mth::DotProduct(dir, check_dir);
//						printf ("Bounding Box Overlap, dot = %f\n",dot);
						
						if (dot < -0.99f || dot > 0.99f)
						{
							// lines are roughly parallel
							// so check if they overlap significantly in at least one axis
							// and/or are very close in one other axis
							// we only check X and Z
							// as we don't have any vertical rails in the park
							// editor
							// (note, I'm prematurely optimizing here...)
							
							int overlaps = 0;
							int close =  0;
							
							// now check the distance between the lines
							// which is the components of start -> check_start
							// that is perpendicular to start -> end
							const float significant = 6.0f;	// six inches is significant, I feel...
							Mth::Vector	s_cs = check_start - start;
							Mth::Vector	s_e = end - start;
							s_e.Normalize();
							Mth::Vector perp = s_cs;
							perp.ProjectToPlane(s_e); 
							if ( perp.Length() significant)
									overlaps++;
								if (overlap(start[Z],end[Z],check_start[Z],check_end[Z]) > significant)
									overlaps++;
								if (nearly_the_same(start[X],end[X],check_start[X],check_end[X],6.0f))
									close++;
								if (nearly_the_same(start[Z],end[Z],check_start[Z],check_end[Z],6.0f))
									close++;
//								printf ("dot close, overlaps = %d, close = %d\n",overlaps,close);
								if (overlaps + close >= 2)
								{
									// it's a duplicate
//									printf("Removing duplicate rail !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
									
									// add a magenta line for rails that have been removed
									#ifdef		DEBUG_EDITOR_RAILS
									pRailNode->m_pos += Mth::Vector(0.0f,2.0f,0.0f);
									pRailNode->m_pNextLink->m_pos += Mth::Vector(0.0f,2.0f,0.0f);
									Gfx::AddDebugLine( pRailNode->m_pos, pRailNode->m_pNextLink->m_pos, MAKE_RGB( 255, 0, 255 ),MAKE_RGB( 255,0, 255 ) );
									pRailNode->m_pos -= Mth::Vector(0.0f,2.0f,0.0f);
									pRailNode->m_pNextLink->m_pos -= Mth::Vector(0.0f,2.0f,0.0f);
									#endif
									
									removed ++;
									// Remove the one that is lower
									if (pRailNode->m_pos[Y] < pCheckNode->m_pos[Y])
									{
										pRailNode->SetActive(false);
									}
									else
									{
										pCheckNode->SetActive(false);
									}
									break;
								}
							}
						}
					}
				}
				check_node++;
//				pCheckNode = pCheckNode->m_pNext;
			}				
		}
//		pRailNode = pRailNode->m_pNext;
	}		 
	printf ("Done overlapping rail removal, removed %d\n", removed);
}


void CRailManager::AddedAll()
{
	
//	printf("Added all rails\n");		 

	#if 1
	if (Ed::CParkEditor::Instance()->UsingCustomPark()) 		// is it a custom park???
	{
		// yes, so remove overlapping rails
		RemoveOverlapping();	
	}	 
	#endif

	
}			   

void CRailManager::SetActive( int node, int active, bool wholeRail )
{
	
//	CRailNode *pRailNode = mp_first_node;
//	while (pRailNode)
	for (int check_node = 0;check_nodem_node == node)
		{
			if ( wholeRail )
			{
				while ( pRailNode )
				{
					pRailNode->SetActive(active);
					pRailNode = pRailNode->m_pNextLink;
				}
				return;
			}
			pRailNode->SetActive(active);
			return;
		}
//		pRailNode = pRailNode->m_pNext;
	}				 
}

bool CRailManager::IsActive( int node )
{
//	CRailNode *pRailNode = mp_first_node;
//	while (pRailNode)
	for (int check_node = 0;check_nodem_node == node)
		{
			return ( pRailNode->GetActive() );
		}
//		pRailNode = pRailNode->m_pNext;
	}
	return ( false );
}


// A node in the node array has moved (due to realtime editing)
// so find that node here, and update the position
// and update bounding boxes as needed 
//

void CRailManager::MoveNode( int node, Mth::Vector &pos )
{
	for (int check_node = 0;check_nodem_node == node)
		{
			// Set The position
			pRailNode->m_pos = pos;
			
			// calculate the bounding box of any segment this node starts
			// (there can be only one)
			if (pRailNode->GetNextLink())
			{
				Mth::Vector from_pos = pRailNode->m_pos; 
				Mth::Vector to_pos = pRailNode->GetNextLink()->m_pos;
				Rail_ComputeBB(from_pos ,to_pos , pRailNode->m_BBMin, pRailNode->m_BBMax);				
			}
			
			// Check to see if there are any nodes leading to this one
			// (this initial check is only for speed)
			if (pRailNode->GetPrevLink())
			{
				// There is one, but there might be more that one
				// the only way to know, is to go through all the nodes and see if any points to this one
				// 
				for (int from_node = 0;from_nodeGetNextLink() == pRailNode)
					{
						Rail_ComputeBB(p_from->m_pos, pRailNode->m_pos, p_from->m_BBMin, p_from->m_BBMax);
					}
				}
			}
			// There is only going to be one mathcing node, so we can now return		
			return;
		} // end if (pRailNode->m_node == node)
	}				 
}




// Given a velocity, and a rail node
// then see which side of the rail (left or right)
// has no collision
// returns -1 if left, 0 if neither or both, and +1 if right
// Used in the park editor to give preference to those
// rails that keep the same sideness as the prior rail we were on
int	CRailNode::Side(const Mth::Vector &vel) const
{
	Mth::Vector Start = m_pos;
	Mth::Vector End = 	m_pNextLink->m_pos;

	CFeeler	feeler;

 // Find a point "Mid", which is 1/4 of the way along the rail   
 // and lowered six inches
	Mth::Vector	Mid = (Start + End) / 2.0f;			// Half way along
	Mid = Start + ((Mid - Start) / 2.0f);		    // quarter of the way along	
	Mid[Y] -= 6.0f;									// lowered a bit

// Find a vector "Side" which is horizontal, perpendicular to the rail, and 4 inches long	
	Mth::Vector	Side = End - Start;	   			// vector along the rail
	Side[Y] = 0.0f;								// horizontal
	Side.Normalize(4.0f);						// this is half the width of the thickest rail.  Was 4.0, changed for Tokyo mega funbox
	float temp = Side[X];			 				// make perpendicular to rail
	Side[X] = Side[Z];
	Side[Z] = -temp;


	bool	left = false;
	bool	right = false;
	
// if collision above me, left to right
	feeler.SetLine(Mid + Side, Mid-Side);	
	if (feeler.GetCollision())
	{
		left = true;
	}

// if collision above me, right to left
	feeler.FlipDirection();
	if (feeler.GetCollision())
	{
		right = true;
	}
	else
	{
	}
	
	int side = 0;
	if (left && !right)
	{
		side = -1;
	}
	else
	{
		if (right && ! left)
		{
			side = 1;
		}
	}
	
	// flip is we are going the opposite direction on the rail
	if ( side && (Mth::DotProduct(vel,End-Start) < 0.0f) )
	{
		side = -side;
	}
	
	return side;	
}


void CRailManager::UpdateTransform(Mth::Matrix& transform)
{

	m_is_transformed = true;
	m_transform = transform;
}


bool CRailManager::CheckForLadderRail ( const Mth::Vector& pos, float max_horizontal_snap_distance, float max_vertical_snap_distance, bool up, CWalkComponent* p_walk_component, SLadderRailData& rail_data, const CRailNode** pp_rail_node )
{
    float max_horizontal_snap_distance_sqr = Mth::Sqr(max_horizontal_snap_distance);
    
    for (int check_node_idx = m_num_nodes; check_node_idx--; )
    {
        CRailNode* p_node = &mp_nodes[check_node_idx];
        
		if (!p_node->GetFlag(LADDER) || !p_node->GetActive()) continue;
        
        // quick oriented bounding box check
        if (Mth::Abs(p_node->m_pos[X] - pos[X]) > max_horizontal_snap_distance) continue;
        if (Mth::Abs(p_node->m_pos[Z] - pos[Z]) > max_horizontal_snap_distance) continue;
        if (Mth::Abs(p_node->m_pos[Y] - pos[Y]) > max_vertical_snap_distance) continue;
        
        // actual distance check
        Mth::Vector offset = p_node->m_pos - pos;
        if (offset[X] * offset[X] + offset[Z] * offset[Z] > max_horizontal_snap_distance_sqr) continue;
        
        // find the ladder's partiner node
        const CRailNode* p_partiner_node = p_node->GetNextLink() ? p_node->GetNextLink() : p_node->GetPrevLink();
        Dbg_Assert(p_partiner_node);
        
		if (up)
		{
			if (!p_walk_component->FilterLadderUpRail(p_node->m_pos, p_partiner_node->m_pos, GetMatrix(p_node), rail_data)) continue;
		}
		else
		{
			if (!p_walk_component->FilterLadderDownRail(p_partiner_node->m_pos, p_node->m_pos, GetMatrix(p_partiner_node), rail_data)) continue;
		}
        
        // for now we do no comparison between ladders; they should be sparce enough that we can just take the first ladder rail we find
		
		// return the bottom node of the ladder
		if (up)
		{
			*pp_rail_node = p_node;
		}
		else
		{
			*pp_rail_node = p_partiner_node;
		}
        return true;
    }
    
    return false;
}

bool CRailManager::CheckForAirGrabLadderRail ( const Mth::Vector& start_pos, const Mth::Vector& end_pos, CWalkComponent* p_walk_component, SLadderRailData& rail_data, const CRailNode** pp_rail_node )
{
	*pp_rail_node = NULL;
	
	Mth::Line movement;
	movement.m_start = start_pos;
	movement.m_end = end_pos;
	
	// find bounding box for movement
	Mth::Vector bb_min, bb_max;
	Rail_ComputeBB(movement.m_start, movement.m_end, bb_min, bb_max);
	float snap_dist_sqr = Script::GetFloat(CRCD(0x30ce7f2c, "Climb_Max_Snap"));
	bb_min.Set(bb_min.GetX() - snap_dist_sqr, bb_min.GetY() - snap_dist_sqr, bb_min.GetZ() - snap_dist_sqr);	
	bb_max.Set(bb_max.GetX() + snap_dist_sqr, bb_max.GetY() + snap_dist_sqr, bb_max.GetZ() + snap_dist_sqr);	
	snap_dist_sqr *= snap_dist_sqr;
	
	float closest_dist_sqr = 10000000.0f * 10000000.0f;

	for (int check_node = 0; check_node < m_num_nodes; check_node++)
	{
		CRailNode* pRailNode = &mp_nodes[check_node];
		if (!pRailNode->GetFlag(LADDER) || !pRailNode->GetActive() || !pRailNode->m_pNextLink) continue;
		
		// First do bounding box test, before time-intensive LineLineIntersect test 
		if (bb_min.GetX() > pRailNode->m_BBMax.GetX()) continue;
		if (bb_max.GetX() < pRailNode->m_BBMin.GetX()) continue;
		if (bb_min.GetZ() > pRailNode->m_BBMax.GetZ()) continue;
		if (bb_max.GetZ() < pRailNode->m_BBMin.GetZ()) continue;
		if (bb_min.GetY() > pRailNode->m_BBMax.GetY()) continue;
		if (bb_max.GetY() < pRailNode->m_BBMin.GetY()) continue;
		
		// we have a rail segment with a BB match
		// so see if we are close to it 
		Mth::Line segment;
		segment.m_start = pRailNode->m_pos;
		segment.m_end = pRailNode->m_pNextLink->m_pos;
		Mth::Vector p1, p2;
		float f1,f2;
		if (!Mth::LineLineIntersect(movement, segment, &p1, &p2, &f1, &f2)) continue;
		
		Mth::Vector	to_rail = p2 - p1;
		float dist_sqr = to_rail.LengthSqr();
		
		if (dist_sqr > snap_dist_sqr || dist_sqr > closest_dist_sqr) continue;
		
		SLadderRailData this_data;
		if (pRailNode->m_pos[Y] < pRailNode->m_pNextLink->m_pos[Y])
		{
			if (!p_walk_component->FilterLadderAirGrabRail(pRailNode->m_pos, pRailNode->m_pNextLink->m_pos, GetMatrix(pRailNode), p2, f2, this_data)) continue;
			*pp_rail_node = pRailNode;
		}
		else
		{
			if (!p_walk_component->FilterLadderAirGrabRail(pRailNode->m_pNextLink->m_pos, pRailNode->m_pos, GetMatrix(pRailNode->m_pNextLink), p2, 1.0f - f2, this_data)) continue;
			*pp_rail_node = pRailNode->m_pNextLink;
		}
		
		closest_dist_sqr = dist_sqr;
		rail_data = this_data;
	}

	return *pp_rail_node;
}

/*
// check for rails near but perpendicular to check_line
bool CRailManager::CheckForHopToHangRail ( const Mth::Vector& check_line_start, float check_line_height, const Mth::Vector& facing, float max_forward_reach, float max_backward_reach, CWalkComponent* p_walk_component, SHangRailData& rail_data, const CRailNode** pp_rail_node )
{
	// this logic assumes Y is up, which might not be true if we're checking a movable rail manager
	// for now, no hopping to hang from movable rail managers which are not nicely oriented
	if (Mth::Abs(facing[Y]) > 0.1f) return false;
	
	// The check box is aligned with the Y axis and the facing, is check_line_height tall,
	// and has a max_foward_reach + max_backward_reach by vCHECK_BOX_WIDTH foot print in the horizontal plane.
	
	*pp_rail_node = NULL;
	float best_facing_distance = 0.0f;
	float best_height = 0.0f;
	
	float check_line_X_start = check_line_start[X] - max_backward_reach * facing[X];
	float check_line_X_delta = (max_backward_reach + max_forward_reach) * facing[X];
	float check_line_Z_start = check_line_start[Z] - max_backward_reach * facing[Z];
	float check_line_Z_delta = (max_backward_reach + max_forward_reach) * facing[Z];
	
	for (int check_node_idx = m_num_nodes; check_node_idx--; )
	{
		CRailNode* p_node = &mp_nodes[check_node_idx];
		
		if (p_node->GetFlag(LADDER) || p_node->GetFlag(NO_CLIMBING) || !p_node->GetActive() || !p_node->m_pNextLink) continue;
		
		// determine the clostest point on the rail in the horizontal plane
		float s;
		if (!Nx::CCollObj::s2DLineLineCollision(
			p_node->m_pos[X],
			p_node->m_pos[Z],
			p_node->m_pNextLink->m_pos[X] - p_node->m_pos[X],
			p_node->m_pNextLink->m_pos[Z] - p_node->m_pos[Z],
			check_line_X_start,
			check_line_Z_start,
			check_line_X_delta,
			check_line_Z_delta,
			&s
		)) continue;
		Mth::Vector closest_point = p_node->m_pos + s * (p_node->m_pNextLink->m_pos - p_node->m_pos);
		
		// calculate the offset vector from the bottom of the check line to the closest point
		Mth::Vector offset_to_rail = closest_point - check_line_start;
		
		// calculate distance along our facing to the closest point
		float facing_distance = offset_to_rail[X] * facing[X] + offset_to_rail[Z] * facing[Z];
		
		// check if the closest point is within reach
		if (facing_distance > max_forward_reach) continue;
		if (facing_distance < -max_backward_reach) continue;
		
		// check to see if the closest point is within the allowed height range
		if (closest_point[Y] < check_line_start[Y]) continue;
		if (closest_point[Y] > check_line_start[Y] + check_line_height) continue;
		
		SHangRailData this_data;
		if (!p_walk_component->FilterHangRail(GetPos(p_node), GetPos(p_node->m_pNextLink), LocalToWorldTransform(closest_point), s, this_data, false)) continue;
		
		if (*pp_rail_node)
		{
			// logic determining if this rail is better
			
			// take rail in front over rail behind
			if (best_facing_distance >= 0.0f && facing_distance < 0.0f) continue;
			if (!(facing_distance >= 0.0f && best_facing_distance < 0.0f))
			{
				// otherwise, take lowest rail
				if (offset_to_rail[Y] > best_height) continue;
				if (offset_to_rail[Y] == best_height)
				{
					// otherwise, take the closest rail horizontally
					if (Mth::Abs(facing_distance) > Mth::Abs(best_facing_distance)) continue;
				}
			}
		}
		
		// this is the best (or first) rail so far
		*pp_rail_node = p_node;
		best_facing_distance = facing_distance;
		best_height = offset_to_rail[Y];
		rail_data = this_data;
		
		Gfx::AddDebugStar(closest_point,  36.0f,  MAKE_RGB(255, 0, 0), 1);
	}
	
	return *pp_rail_node;
}
*/

bool CRailManager::CheckForHangRail ( const Mth::Vector& start_pos, const Mth::Vector& end_pos, const Mth::Vector& facing, CWalkComponent* p_walk_component, SHangRailData& rail_data, float snap_distance )
{
	bool rail_found = false;
	
	Mth::Line movement;
	movement.m_start = start_pos;
	movement.m_end = end_pos;
	
	// find bounding box for movement
	Mth::Vector bb_min, bb_max;
	Rail_ComputeBB(movement.m_start, movement.m_end, bb_min, bb_max);
	float snap_dist_sqr = snap_distance;
	bb_min.Set(bb_min.GetX() - snap_dist_sqr, bb_min.GetY() - snap_dist_sqr, bb_min.GetZ() - snap_dist_sqr);	
	bb_max.Set(bb_max.GetX() + snap_dist_sqr, bb_max.GetY() + snap_dist_sqr, bb_max.GetZ() + snap_dist_sqr);	
	snap_dist_sqr *= snap_dist_sqr;
	
	float closest_dist_sqr = 10000000.0f * 10000000.0f;

	for (int check_node = 0; check_node < m_num_nodes; check_node++)
	{
		CRailNode* pRailNode = &mp_nodes[check_node];
		if (pRailNode->GetFlag(LADDER) || pRailNode->GetFlag(NO_CLIMBING) || !pRailNode->GetActive() || !pRailNode->m_pNextLink) continue;
		
		// First do bounding box test, before time-intensive LineLineIntersect test 
		if (bb_min.GetX() > pRailNode->m_BBMax.GetX()) continue;
		if (bb_max.GetX() < pRailNode->m_BBMin.GetX()) continue;
		if (bb_min.GetZ() > pRailNode->m_BBMax.GetZ()) continue;
		if (bb_max.GetZ() < pRailNode->m_BBMin.GetZ()) continue;
		if (bb_min.GetY() > pRailNode->m_BBMax.GetY()) continue;
		if (bb_max.GetY() < pRailNode->m_BBMin.GetY()) continue;
		
		// we have a rail segment with a BB match
		// so see if we are close to it 
		Mth::Line segment;
		segment.m_start = pRailNode->m_pos;
		segment.m_end = pRailNode->m_pNextLink->m_pos;
		Mth::Vector p1, p2;
		float f1,f2;
		if (!Mth::LineLineIntersect(movement, segment, &p1, &p2, &f1, &f2)) continue;
		
		Mth::Vector	to_rail = p2 - p1;
		if (to_rail.LengthSqr() > snap_dist_sqr) continue;
		
		// count rails in front of us as three times closer
		float dot = Mth::DotProduct(facing, to_rail);
		if (dot > 0.0f)
		{
			to_rail -= (2.0f / 3.0f * dot) * facing;
		}
		float dist_sqr = to_rail.LengthSqr();
		
		if (dist_sqr > closest_dist_sqr) continue;
		
		if (!Rail_ValidInEditor(GetPos(pRailNode), GetPos(pRailNode->m_pNextLink))) continue;
		
		SHangRailData this_data;
		this_data.p_rail_node = pRailNode;
		if (!p_walk_component->FilterHangRail(GetPos(pRailNode), GetPos(pRailNode->m_pNextLink), LocalToWorldTransform(p2), LocalToWorldTransform(p1), f2, this_data, false)) continue;
		
		closest_dist_sqr = dist_sqr;
		rail_data = this_data;
		rail_found = true;
	}
	
	return rail_found;
}

bool CRailManager::RailNodesAreCoincident ( const CRailNode* p_node_a, const CRailNode* p_node_b )
{
	const float epsilon = 1.0f;
	
	if (Mth::Abs(p_node_a->m_pos[X] - p_node_b->m_pos[X]) > epsilon) return false;
	if (Mth::Abs(p_node_a->m_pos[Y] - p_node_b->m_pos[Y]) > epsilon) return false;
	if (Mth::Abs(p_node_a->m_pos[Z] - p_node_b->m_pos[Z]) > epsilon) return false;
	return true;
}

// look for another rail node within a few inches of the given node's position
bool CRailManager::CheckForCoincidentRailNode ( const CRailNode* p_node, uint32 ignore_mask, const CRailNode** pp_next_node )
{
	for (int check_node = 0; check_nodem_pNextLink && !pRailNode->m_pPrevLink) continue;
		
		if (pRailNode == p_node) continue;
		
		if (pRailNode->m_pNextLink && pRailNode->IsActive() && !pRailNode->m_flags.TestMask(ignore_mask))
		{
			// MESSAGE("found potential coincident");
			if (Rail_ValidInEditor(GetPos(pRailNode), GetPos(pRailNode->m_pNextLink)))
			{
				*pp_next_node = pRailNode;
				// MESSAGE("found good coincident");
				return true;
			}
			else
			{
				// MESSAGE("not valid coincident");
			}
		}
		
		if (pRailNode->m_pPrevLink && pRailNode->m_pPrevLink->IsActive() && !pRailNode->m_pPrevLink->m_flags.TestMask(ignore_mask))
		{
			// MESSAGE("found potential coincident");
			if (Rail_ValidInEditor(GetPos(pRailNode->m_pPrevLink), GetPos(pRailNode)))
			{
				*pp_next_node = pRailNode->m_pPrevLink;
				// MESSAGE("found good coincident");
				return true;
			}
			else
			{
				// MESSAGE("not valid coincident");
			}
		}
	}
	
	return false;
}

// see if we can stick to a rail	
bool CRailManager::StickToRail(const Mth::Vector &pos1, const Mth::Vector &pos2, Mth::Vector *p_point, CRailNode **pp_rail_node, const CRailNode *p_ignore_node, float min_dot, int side)
{
	
	// Go through all the rail segments, and find the shortest distance to line
	// and there is your rail
	
	Mth::Line  movement;
	movement.m_start = pos1;
	movement.m_end = pos2;
	
	// find bounding box for skater
	Mth::Vector bb_min, bb_max;
	Rail_ComputeBB(movement.m_start, movement.m_end, bb_min, bb_max);
	float snap_dist = Script::GetFloat(CRCD(0xf840753e, "Rail_Max_Snap"));
	bb_min.Set(bb_min.GetX() - snap_dist, bb_min.GetY() - snap_dist, bb_min.GetZ() - snap_dist);	
	bb_max.Set(bb_max.GetX() + snap_dist, bb_max.GetY() + snap_dist, bb_max.GetZ() + snap_dist);	
	
	float		closest_dist = 10000000.0f;
	CRailNode   * p_closest_rail = NULL;
	Mth::Vector		closest_point; 

	bool	found = false;
						

//	CRailNode *pRailNode = mp_first_node;
//	while (pRailNode)
	for (int check_node = 0;check_nodeGetFlag(ONLY_CLIMBING) && pRailNode != p_ignore_node && pRailNode->GetActive())
		{
			if (pRailNode->m_pNextLink)
			{
				// First do bounding box test, before time-intensive LineLineIntersect test 
				
				// *** IMPORTANT ***
				// There should be indentations for each 'if', but I left them out for readability
				if (!(bb_min.GetX() > pRailNode->m_BBMax.GetX()))
				if (!(bb_max.GetX() < pRailNode->m_BBMin.GetX()))
				if (!(bb_min.GetZ() > pRailNode->m_BBMax.GetZ()))
				if (!(bb_max.GetZ() < pRailNode->m_BBMin.GetZ()))
				if (!(bb_min.GetY() > pRailNode->m_BBMax.GetY()))
				if (!(bb_max.GetY() < pRailNode->m_BBMin.GetY()))
				{
					// we have a rail segment with a BB match
					// so see if we are close to it 
					Mth::Line segment;
					segment.m_start = pRailNode->m_pos;
					segment.m_end = pRailNode->m_pNextLink->m_pos;
	
					if (Rail_ValidInEditor(pRailNode->m_pos,pRailNode->m_pNextLink->m_pos))
					{
						Mth::Vector p1,p2;
						float  f1,f2;
						if (Mth::LineLineIntersect(movement, segment, &p1, &p2, &f1, &f2))
						{
		
							Mth::Vector	to_rail = p2-p1;
							float 	dist = to_rail.Length();
							float	old_dist = dist; 
							
							// calculate the dot product of the
							// the movement and the rail in the XZ plane
							Mth::Vector  v1,v2;
							v1 = segment.m_end - segment.m_start;
							v2 = pos2 - pos1;
							v1[Y] = 0.0f;
							v2[Y] = 0.0f;
							v1.Normalize();
							v2.Normalize();
							float dot = Mth::Abs(Mth::DotProduct(v1,v2));
	
							// if now v2 (the skater's movement vector) is all zero
							// and the dot is 0.0f
							// then that means we were going precisely straght up
							// so we set the dot to 1,
							// as normally (if we we slightly left or right)
							// we would be going along the rail at the top of the pipe
							// in the XZ plane (albeit slowly)
							if (v2[X] == 0.0f && v2[Z] == 0.0f && dot == 0.0f)
							{
								dot = 1.0f;
							}
	
	
							dist = dist * (0.122f + 1.0f-dot);		// was (50 + (4096-dist)) on PS1						
							old_dist = old_dist * (2.0f - dot);		// was (8192-dot) on PS1 
							
	//						printf ("dot=%.2f, dist=%.2f, old_dist=%.2f, min_dot=%.2f\n",dot,dist,old_dist,min_dot);
	
							if (side)
							{
								Mth::Vector vel = pos2-pos1;
								if (pRailNode->Side(vel) != side)
								{
									dist *= 2.0f;
	//								printf ("side change, dist doubled to %.2f\n",dist);
								}
							}
							
		
							if (dist < closest_dist)
							{						
								bool close = true;
								// if we have a maximum dot, then check we don't go over it
								if (min_dot != 1.0f)
								{
									if (Mth::Abs(dot) < min_dot)
									{
										close = false; 	 
									}
								}							
								if (close)
								{
									if (old_dist > snap_dist)
									{
										// there is a good rail, but too far away
										// so kill any rail we've found so far
										// and make this the new target
										closest_dist = dist;
										found = false; 
									}
									else
									{
										// good rail, and close enough
										closest_dist = dist;
										closest_point = p2;
										p_closest_rail = pRailNode;	  
										found = true; 
									}							
								}
							}						  	
						} // end if (bb_test && Mth::LineLineIntersect(movement, segment, &p1, &p2, &f1, &f2))
					} // end if (Rail_ValidInEditor(pRailNode->m_pos,pRailNode->m_pNextLink->m_pos))
				} // end whole big bounding box test
			} // end if (pRailNode->m_pNextLink)
			else if (!pRailNode->m_pPrevLink && !pRailNode->m_pNextLink)
			{
				// special logic for a single node rail
				if (!(bb_min.GetX() > pRailNode->m_BBMax.GetX()))
				if (!(bb_max.GetX() < pRailNode->m_BBMin.GetX()))
				if (!(bb_min.GetZ() > pRailNode->m_BBMax.GetZ()))
				if (!(bb_max.GetZ() < pRailNode->m_BBMin.GetZ()))
				if (!(bb_min.GetY() > pRailNode->m_BBMax.GetY()))
				if (!(bb_max.GetY() < pRailNode->m_BBMin.GetY()))
				{
					// calculate the distance from the movement to the rail point 
					Mth::Vector offset = pRailNode->m_pos - pos1;
					Mth::Vector movement_direction = (pos2 - pos1).Normalize();
					offset -= Mth::DotProduct(offset, movement_direction) * movement_direction;
					float distance = offset.Length();
					
					if (distance > snap_dist) continue;
					
					// single node rails count as twice the distance when looking for the closest rail
					if (closest_dist < 2.0f * distance) continue;
					
					closest_dist = 2.0f * distance;
					closest_point = pRailNode->m_pos;
					p_closest_rail = pRailNode;
					found = true;
				}
			}
		} // end if (active && etc)
		
		//pRailNode = pRailNode->m_pNext;
	}				 

	if (found)
	{
		// note, the line from pos1 to closest_point will not reflect the line segment found above
		// as the line segment will actually start somewhere between pos1 and pos2, and not always on pos1
//		if ( closest_dist > Script::GetFloat("Rail_Max_Snap"))
//		{
//			found = false;
//		}
//		else
		{
			*p_point = closest_point;
			*pp_rail_node = p_closest_rail;
		}
	}
	return found;	
}

// Added by Ken.
// Returns true if the passed Node is on the same rail as the rail node.
// Note, with the "merging" rails, where two nodes can link to
// a new node, then this is not guarenteed to work
//  
// Given that the rails can now form a "loop with a tail"
// we now detect loops by traversign the list with two pointers
// one moving at half the speed of the other

bool CRailNode::ProbablyOnSameRailAs(int SearchNode) const
{		
	

	// First check if this node is the required node.
	if (m_node==SearchNode)
	{
		// Well that was easy.
		return true;
	}	
	
	
	// MICK:  Modified to return true only if on the same rail segment
	
	return false;


	

	const CRailNode *p_trailing = this;
	bool		advance_trailing = false;
	
	// Scan forwards.
	CRailNode *pNode=m_pNextLink;
	while (pNode)
	{
		if (pNode->m_node==SearchNode)
		{
			// Found it.
			return true;
		}	
		if (pNode == p_trailing)
		{
			// We've got back where we started without finding
			// the node, so return false.
			return false;
		}	
		pNode=pNode->m_pNextLink;
		
		// Advance the trailing node every other time
		// we advance the search node
		if (advance_trailing)
		{
			p_trailing = p_trailing->m_pNextLink;
		}
		advance_trailing = !advance_trailing;
	}

	p_trailing = this;
	advance_trailing = false;
		
	// Didn't find anything that way, so now scan backwards.
	pNode=m_pPrevLink;
	while (pNode)
	{
		if (pNode->m_node==SearchNode)
		{
			// Found it.
			return true;
		}	
		if (pNode == p_trailing)
		{
			// We've got back where we started without finding
			// the node, so return false.
			return false;
		}	
		pNode=pNode->m_pPrevLink;
		// Advance the trailing node every other time
		// we advance the search node
		if (advance_trailing)
		{
			p_trailing = p_trailing->m_pPrevLink;
		}
		advance_trailing = !advance_trailing;

	}
	
	return false;
}


// use in-game collision checks to determine if a rail is valid
bool  Rail_ValidInEditor(Mth::Vector Start, Mth::Vector End)
{

// MAYBE TODO:  a rail should only need disqualifying if it is along the edge of a cell
// (or even more accurately, a meta-piece
// so could maybe check for that before we try to disqualify it. 
	

	if (!Ed::CParkEditor::Instance()->UsingCustomPark()) 		// is it a custom park???
	{
		#ifdef		DEBUG_EDITOR_RAILS
		printf ("not in editor\n");
		#endif
		// if not a custom park, then everything is valid.
		return true;
	}

	CFeeler		feeler;

// Find a point "Mid", which is 1/4 of the way along the rail   
	Mth::Vector	Mid = (Start + End) / 2.0f;			// Half way along
	Mid = Start + ((Mid - Start) / 2.0f);		    // quarter of the way along	
	Mid[Y] += 6.0f;									// raised up a bit

// Find a vector "Side" which is horizontal, perpendicular to the rail, and 4 inches long	
	Mth::Vector	Side = End - Start;	   			// vector along the rail
	Side[Y] = 0.0f;								// horizontal
	Side.Normalize(7.0f);						// this is half the width of the thickest rail.  Was 4.0, changed for Tokyo mega funbox
	float temp = Side[X];			 				// make perpendicular to rail
	Side[X] = Side[Z];
	Side[Z] = -temp;

												   
	
// if collision above me, left to right, then invalid
	feeler.SetLine(Mid + Side, Mid-Side);	
	if (feeler.GetCollision())
	{
		#ifdef		DEBUG_EDITOR_RAILS
		printf ("l-r above collision, invalid\n");
		feeler.DebugLine(255,0,0);
		#endif
		return false;
	}
	else
	{
		#ifdef		DEBUG_EDITOR_RAILS
		feeler.DebugLine();
		#endif
	}

// if collision above me, right to left, then invalid 	
	feeler.FlipDirection();
	if (feeler.GetCollision())
	{
		#ifdef		DEBUG_EDITOR_RAILS
		printf ("r-l above collision, invalid\n");
		feeler.DebugLine(255,0,0);
		#endif
		return false;
	}
	else
	{
		#ifdef		DEBUG_EDITOR_RAILS
		feeler.DebugLine();
		#endif
	}

#if 1

// Get a vector "Down", which is a line straight down 7 inches
	Mth::Vector	DeepBelow(0.0f,-12.0f,0.0f);	 		// six inches below the rail
	
	float	left_height 		= 0.0f;
	float 	right_height 		= 0.0f;

// find reltive height of slope (to rail) on left side
	feeler.SetLine(Mid + Side, Mid + Side + DeepBelow);				  
	if (feeler.GetCollision())
	{
		#ifdef		DEBUG_EDITOR_RAILS
		feeler.DebugLine(0,255,0);	   	// green = possible bad one side
		#endif
		left_height = feeler.GetPoint().GetY() - (Mid[Y] -6.0f);
	}
	else
	{
		return true;
	}

	feeler.SetLine(Mid - Side, Mid - Side + DeepBelow);				  
	if (feeler.GetCollision())
	{
		#ifdef		DEBUG_EDITOR_RAILS
		feeler.DebugLine(0,255,0);	   	// green = possible bad one side
		#endif
		right_height = feeler.GetPoint().GetY() - (Mid[Y]-6.0f);
	}
	else
	{
		return true;
	}
	
	
//	printf("Left = %f, Right = %f\n",left_height, right_height);
	
	if (left_height > -1.0f && right_height > -1.0f)
	{
		// both faces tilt upwards, or are roughtly horizontal
		// so return false
		return false;
	}

	// Make it so left is higher than right for subsequent tests	
	if (left_height < right_height)
	{
		float t = left_height;
		left_height = right_height;
		right_height = t;
	}
	
	// check for steep left side
	if (left_height > 1.0f)
	{
		// sloped down right side, so return
		if (right_height > -2.0f)
		{
//			printf ("Sloped, false\n");
			return false;
		}
	}


#else

// Get a vector "Down", which is a line straight down 7 inches
	Mth::Vector	DownBelow(0.0f,-7.0f,0.0f);	 		// inch below the rail
	Mth::Vector	DownAbove(0.0f,-4.0f,0.0f);			// 2 inch above the rail


	// we do fource collision checks, two on each side of the rail
	// one to a height that goes below the rail
	// and the other that goes to a height above it
	// if two or more of these return a collision
	// then the rail is invalid
	// the majority of bad rails will be eliminated with two checks

	int cols = 0;
	feeler.SetLine(Mid + Side, Mid + Side + DownBelow);				  
	if (feeler.GetCollision())
	{
		cols++;
		#ifdef		DEBUG_EDITOR_RAILS
		feeler.DebugLine(0,255,0);	   	// green = possible bad one side
		printf ("first side collision (greeen), checking other side.....\n");
		#endif
	}
	
	feeler.SetLine(Mid-Side, Mid-Side+DownBelow);				  
	if (feeler.GetCollision())
	{
		cols++;
		#ifdef		DEBUG_EDITOR_RAILS
		feeler.DebugLine(0,255,0);	   	// green = possible bad one side
		printf ("second side collision (greeen), checking other side.....\n");
		#endif
		if (cols > 1)
		{
			return false;
		}	
	}
	
	feeler.SetLine(Mid+Side, Mid+Side+DownAbove);				  
	if (feeler.GetCollision())
	{
		cols++;
		#ifdef		DEBUG_EDITOR_RAILS
		feeler.DebugLine(0,255,0);	   	// green = possible bad one side
		printf ("third side collision (greeen), checking other side.....\n");
		#endif
		if (cols > 1)
		{
			return false;
		}	
	}
	
	feeler.SetLine(Mid-Side, Mid-Side+DownAbove);				  
	if (feeler.GetCollision())
	{
		cols++;
		#ifdef		DEBUG_EDITOR_RAILS
		feeler.DebugLine(0,255,0);	   	// green = possible bad one side
		printf ("forth side collision (greeen)\n");
		#endif
		if (cols > 1)
		{
			return false;
		}	
	}
#endif			
		
// Not found two collisions
// so we can return true, indicating this rail is valid in the park editor
//	printf ("everything ok, valid\n");
	return true;
}



void	CRailManager::DebugRender(Mth::Matrix *p_transform)
{
#ifdef	__DEBUG_CODE__
	
	#ifdef	__PLAT_NGPS__	
	
	NxPs2::DMAOverflowOK = 2;
						   
	
	Mth::Vector	cam_fwd;
	
	if (Nx::CViewportManager::sGetActiveCamera())
	{
		cam_fwd = Nx::CViewportManager::sGetActiveCamera()->GetMatrix()[Z];
	}
	else
	{
		printf("WARNING: called CRailManager::DebugRender without a camera\n");
		return;
	}

	
	
	bool	draw_arrows = Script::GetInt( CRCD(0xc57f95d7,"rail_arrows"), false );

	Mth::Vector up(0.0f,1.0f,0.0f);	

	uint32	rgb = 0x0000ff;
	
	uint32 current = 12345678;

	
	
//	CRailNode *pRailNode;
//	pRailNode = mp_first_node;
	int count = 0;
	for (int check_node = 0;check_nodem_pNextLink;
		
		int terrain = pRailNode->GetTerrain();
			
		if (pNext)
		{
		
			switch (terrain)
			{
			
				case vTERRAIN_CONCSMOOTH:
				case vTERRAIN_CONCROUGH:	
					rgb = 0x0000ff;			// red = concrete
					break;
				case vTERRAIN_METALSMOOTH:
				case vTERRAIN_METALROUGH:	
				case vTERRAIN_METALCORRUGATED:	
				case vTERRAIN_METALGRATING:
				case vTERRAIN_METALTIN: 
					rgb = 0xff0000;			// blue = metal
					break;
				 
				case vTERRAIN_WOOD:		
				case vTERRAIN_WOODMASONITE:
				case vTERRAIN_WOODPLYWOOD:
				case vTERRAIN_WOODFLIMSY:	
				case vTERRAIN_WOODSHINGLE:
				case vTERRAIN_WOODPIER:	
					rgb = 0x00ff00;			// green = wood
					break;
				default:
					rgb = 0xffffff;			// white = everything else 
					break;
			}
			
			if (pRailNode->m_flags.Test(LADDER))
			{
				rgb = 0xffff00;
			}
		

			// check for context changes
			if (current != rgb)
			{
				if ( current != 12345678)
				{
					NxPs2::EndLines3D();
				}
				
				current = rgb;
				NxPs2::BeginLines3D(0x80000000 + (0x00ffffff & rgb));
			}
		
			Mth::Vector v0, v1;
			v0 = pRailNode->m_pos/* + up*/;	  	// the +up is the lift them off the surface, so they render better
			v1 = pNext->m_pos/* + up*/;
			v0[W] = 1.0f;		// Make homgeneous
			v1[W] = 1.0f;

			if (p_transform)
			{
				v0 = p_transform->Transform(v0);
				v1 = p_transform->Transform(v1);
			}

			if (pRailNode->GetTerrain() || ((Tmr::GetTime() % 100) > 50))
			{
				NxPs2::DrawLine3D(v0[X],v0[Y],v0[Z],v1[X],v1[Y],v1[Z]);
				NxPs2::DrawLine3D(v0[X],v0[Y],v0[Z],v1[X],v1[Y],v1[Z]);
			
				if (draw_arrows)
				{
					// Calculate and draw an arrowhead 
					// 1/4th the length, at ~30 degrees					   
					Mth::Vector	a = v0;
					Mth::Vector b = v1;
					Mth::Vector	ab = (b-a);
					ab /= 4.0f;
					Mth::Vector out;
					out = ab;
					out.Normalize();
					out = Mth::CrossProduct(out, cam_fwd);
					out *= ab.Length()/3.0f;			
					
					Mth::Vector left =  b - ab + out;
					Mth::Vector right = b - ab - out;
		
					NxPs2::DrawLine3D(left[X],left[Y],left[Z],b[X],b[Y],b[Z]);
					NxPs2::DrawLine3D(right[X],right[Y],right[Z],b[X],b[Y],b[Z]);
					NxPs2::DrawLine3D(right[X],right[Y],right[Z],left[X],left[Y],left[Z]);	 // crossbar
					// have to draw it twice for some stupid reason	(final segment of a particular color is not rendered)
					NxPs2::DrawLine3D(left[X],left[Y],left[Z],b[X],b[Y],b[Z]);
					NxPs2::DrawLine3D(right[X],right[Y],right[Z],b[X],b[Y],b[Z]);
					NxPs2::DrawLine3D(right[X],right[Y],right[Z],left[X],left[Y],left[Z]);
				}		
			
			
			
			}
		}
		
//		pRailNode = pRailNode->m_pNext;
		
	}
	

	// then draw the node positions as litle red lines	
//	pRailNode = mp_first_node;
	count = 0;
	for (int check_node = 0;check_nodeGetActive())
		{
			rgb = 0xff0000;		 		// blue for active
		}
		else
		{
			rgb = 0x00ff00;				// green for inactive
		}
		
		if (!pRailNode->GetPrevLink())
		{
			rgb |= 0x0000ff;			// blue+red = cyan = no prev node (or green+red = yellow for no prev inactive)
		}
	
		// check for context changes
		if (current != rgb)
		{
			if ( current != 12345678)
			{
				NxPs2::EndLines3D();
			}
			
			current = rgb;
			NxPs2::BeginLines3D(0x80000000 + (0x00ffffff & rgb));
		}
	
		Mth::Vector v0, v1;
		v0 = pRailNode->m_pos;
		v1 = v0 + up * 24.0f;
		v0[W] = 1.0f;		// Make homgeneous
		v1[W] = 1.0f;

		if (p_transform)
		{
			v0 = p_transform->Transform(v0);
			v1 = p_transform->Transform(v1);
		}

		if (pRailNode->GetTerrain() || ((Tmr::GetTime() % 100) > 50))
		{
			NxPs2::DrawLine3D(v0[X],v0[Y],v0[Z],v1[X],v1[Y],v1[Z]);
			NxPs2::DrawLine3D(v0[X],v0[Y],v0[Z],v1[X],v1[Y],v1[Z]);
		}
//		pRailNode = pRailNode->m_pNext;
		
	}

	
	
	// only if we actually drew some
	if ( current != 12345678)
	{
		NxPs2::EndLines3D();
	}


	#endif
	#endif

}




void	CRailManager::AddRailsFromNodeArray(Script::CArray * p_nodearray)
{
	Dbg_MsgAssert(!mp_node_array,("Setting two node arrays in rail manager"));
	mp_node_array = p_nodearray;
	
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();

	
	uint32 i;	
	

	Dbg_MsgAssert(m_num_nodes == 0,("Can only addd nodes once, already %d there\n",m_num_nodes));										  
	m_num_nodes = 0;										  
										  
	// First iterate over the node array, and count the number of nodes needed


	for (i=0; iGetSize(); ++i)
	{
		Script::CStruct *p_node_struct=p_nodearray->GetStructure(i);
		Dbg_MsgAssert(p_node_struct,("Error getting node from node array for rail generation"));
		if ( !skate_mod->ShouldBeAbsentNode( p_node_struct ) )
		{
			uint32 class_checksum = 0;		
			p_node_struct->GetChecksum( 0x12b4e660 /*"Class"*/, &class_checksum );
			if (class_checksum == CRCD(0x8e6b02ad, "railnode") || class_checksum == CRCD(0x30c19600, "ClimbingNode"))
			{
				m_num_nodes++;										  
			}
			
			// if (class_checksum == CRCD(0x16d1e502, "climbnode"))
			// {
				// printf("Found climb node!\n");
			// }
		}
	}


	// No nodes found, so just return
	if (!m_num_nodes)
	{
		return;
	}
	
	mp_nodes = (CRailNode*)Mem::Malloc(m_num_nodes * sizeof(CRailNode));
	mp_links = (CRailLinks*)Mem::Malloc(m_num_nodes * sizeof(CRailLinks));

	// iterate over the nodes and add them to the array	
	m_current_node = 0;	
	
	 
	for (i=0; iGetSize(); ++i)
	{
		Script::CStruct *p_node_struct=p_nodearray->GetStructure(i);
		Dbg_MsgAssert(p_node_struct,("Error getting node from node array for rail generation"));

		if ( !skate_mod->ShouldBeAbsentNode( p_node_struct ) )
		{
	
	
			uint32 class_checksum = 0;		
			p_node_struct->GetChecksum( 0x12b4e660 /*"Class"*/, &class_checksum );
			if (class_checksum == CRCD(0x8e6b02ad, "railnode") || class_checksum == CRCD(0x30c19600, "ClimbingNode"))
			{
				
	
				AddRailNode( i, p_node_struct);
	
				bool is_trick_object = p_node_struct->ContainsComponentNamed( "TrickObject" );
	
	#if 1
				bool debug_graffiti = Script::GetInt( "create_all_trick_objects", false );
				if ( debug_graffiti )
					is_trick_object = true;
	#endif
	
				if ( is_trick_object )
				{
					// get the node name
					uint32 checksumName;
					p_node_struct->GetChecksum( "name", &checksumName, true );
	
					// must have a cluster associated with it
					uint32 clusterName = checksumName;
	#if 0
					// automatically link all rail nodes to the TestCluster for now
					clusterName = Script::GenerateCRC("TestCluster");
	#else
					p_node_struct->GetChecksum( "Cluster", &clusterName, true );
	#endif
					skate_mod->GetTrickObjectManager()->AddTrickCluster( clusterName );
	
					// bind the name of the rail node to the cluster name
					skate_mod->GetTrickObjectManager()->AddTrickAlias( checksumName, clusterName );
				}
			}
		}
	}


	
	int num_nodes  = p_nodearray->GetSize();   
	
	// we are creating a table of all nodes, and the pointer to the CRailNode
	// for that node, so we can do a reverse lookup
	
	CRailNode **pp_railnodes = (CRailNode **) Mem::Malloc(num_nodes * sizeof(CRailNode*));
	for (int i=0;iGetNode()] = p_node;
//		p_node = p_node->m_pNext;
	}

	// now go through all the node	
//	p_node = mp_first_node;
	for (int node=0;nodem_node, mp_links[node].m_link[i], num_nodes));
				Dbg_MsgAssert(pp_railnodes[mp_links[node].m_link[i]], ("RailNode %d linked to something (node %d) that is not a RailNode",
												p_node->m_node, mp_links[node].m_link[i]));
				NewLink(p_node,pp_railnodes[mp_links[node].m_link[i]]);
			}
		}
//		p_node = p_node->m_pNext;
	}
	Mem::Free(pp_railnodes);
	
	Mem::Free(mp_links);
	mp_links = NULL;
	
	//printf ("THERE ARE %d rails\n",m_num_nodes);
	
}

Mth::Vector CRailManager::LocalToWorldTransform ( const Mth::Vector& vector ) const
{
	if (!m_is_transformed)
	{
		return vector;
	}
	else
	{
		return m_transform.Transform(vector);
	}
}

Mth::Vector	CRailManager::GetPos(const CRailNode *p_node) 	const
{
	Dbg_Assert(p_node);
	
	if (!m_is_transformed)
	{
		return p_node->GetPos();
	}
	else
	{
		Mth::Vector v = m_transform.Transform(p_node->GetPos());
		return v;
	}
}

Mth::Matrix	CRailManager::GetMatrix(const CRailNode *p_node) 	const
{
	Dbg_Assert(p_node);
	
	if (!m_is_transformed)
	{
		return Mth::Matrix(p_node->GetOrientation());
	}
	else
	{
		Mth::Matrix m = m_transform * Mth::Matrix(p_node->GetOrientation());
		return m;
	}
}


Mth::Vector	CRailManager::GetBBMin(const CRailNode *p_node) const
{
	if (!m_is_transformed)
	{
		return p_node->GetBBMin();
	}
	else
	{
		Mth::Vector v = m_transform.Transform(p_node->GetBBMin());
		return v;
	}
}

Mth::Vector	CRailManager::GetBBMax(const CRailNode *p_node) const
{
	if (!m_is_transformed)
	{
		return p_node->GetBBMax();
	}
	else
	{
		Mth::Vector v = m_transform.Transform(p_node->GetBBMax());
		return v;
	}
}

// Auto-generation of rails

// a binning data structure similar to a hashtable which will allow us to quickly find nearby rail endpoints
// differs from the standard hashtable in that we will be able to add an element multiple times
// its algorithm is just as inefficient as the standard hashtable

struct SAutoRail;
class BinTable;

class BinItem
{
	friend class BinTable;

private:
	BinItem (   )
		: mp_value(NULL), mp_next(NULL)
		{   }

	SAutoRail* mp_value;
	BinItem* mp_next;
};

class BinTable
{
public:
	BinTable ( uint32 numBits );
	~BinTable (   );

	bool PutItem ( const uint32 key, SAutoRail* item );
	bool PutItemTwice ( const uint32 first_key, const uint32 second_key, SAutoRail* item );
	SAutoRail* GetFirstItem ( const uint32 key );
	SAutoRail* GetNextItem ( const uint32 key, SAutoRail* item );
	void FlushAllItems (   );

	int GetSize (   ) { return m_size; }

protected:
	uint32 m_numBits;
	BinItem* mp_table;
	int m_size;

private:
	uint32 key_to_bin ( const uint32 key )
		{ return key & ((1 << m_numBits) - 1); }
};

BinTable::BinTable ( uint32 numBits )
	: m_numBits(numBits), mp_table(new BinItem[1 << numBits]), m_size(0)
	{   }

BinTable::~BinTable (   )
{
	Dbg_AssertPtr(mp_table);

	FlushAllItems();

	delete [] mp_table;
	mp_table = NULL;
}

bool BinTable::PutItem ( const uint32 key, SAutoRail* item )
{    
    Dbg_AssertPtr(item);

	Dbg_AssertPtr(mp_table);

	Dbg_MsgAssert(key || item, ("Both key and item are 0 (NULL) in bin table"));
	
	Dbg_MsgAssert(item, ("NULL item added to bin table"));

	BinItem *pEntry = &mp_table[key_to_bin(key)];
	if (pEntry->mp_value)
	{
#ifndef __PLAT_WN32__
		BinItem *pNew = new (Mem::PoolManager::SCreateItem(Mem::PoolManager::vHASH_ITEM_POOL)) BinItem();
#else
		BinItem *pNew = new BinItem;
#endif
		pNew->mp_value = item;
		pNew->mp_next = pEntry->mp_next;
		pEntry->mp_next = pNew;
	}
	else
	{
		pEntry->mp_value = item;
	}

	m_size++;	
	return true;
}

// this feature is needed to insure a rail is never added twice to the same bin; if it were, it would become
// impossible to traverse the bin
bool BinTable::PutItemTwice ( const uint32 first_key, const uint32 second_key, SAutoRail* item )
{
	PutItem(first_key, item);
	if (key_to_bin(first_key) != key_to_bin(second_key))
		PutItem(second_key, item);
	return true;
}

SAutoRail* BinTable::GetFirstItem ( const uint32 key )
{
    Dbg_AssertPtr(mp_table);

	BinItem* pEntry = &mp_table[key_to_bin(key)];

	while (pEntry)
	{
		if (pEntry->mp_value)
		{
			return pEntry->mp_value;
		}	
		pEntry = pEntry->mp_next;
	}

	return NULL;
}

SAutoRail* BinTable::GetNextItem ( const uint32 key, SAutoRail* p_item )
{
    Dbg_AssertPtr(mp_table);

	BinItem* pEntry = &mp_table[key_to_bin(key)];

	while (pEntry)
	{
		if (pEntry->mp_value == p_item)
		{
			break;
		}	
		pEntry = pEntry->mp_next;
	}
	if (!pEntry)
	{
		return NULL;
	}	

	pEntry = pEntry->mp_next;
	while (pEntry)
	{
		if (pEntry->mp_value)
		{
			return pEntry->mp_value;
		}	
		pEntry = pEntry->mp_next;
	}

	return NULL;
}

void BinTable::FlushAllItems (   )
{
	Dbg_AssertPtr(mp_table);
	if (!mp_table)
		return;

	BinItem* pMainEntry = mp_table;
	uint32 hashTableSize = 1 << m_numBits;
	for (uint32 i = 0; i < hashTableSize; ++i)
	{
		BinItem* pLinkedEntry = pMainEntry->mp_next;
		while (pLinkedEntry)
		{
			BinItem* pNext = pLinkedEntry->mp_next;
#ifndef __PLAT_WN32__
			Mem::PoolManager::SFreeItem(Mem::PoolManager::vHASH_ITEM_POOL, pLinkedEntry);
#else
			delete pLinkedEntry;
#endif
			pLinkedEntry = pNext;
		}
		++pMainEntry;
	}	
	m_size = 0;
}

// memory managment defines; control temporary memory usage

// maximum number of edges in a sector
#define MAX_NUM_EDGES					(5000)
// maximum number of rails
#define	MAX_NUM_RAILS					(30000)
// maximum number of railsets
#define MAX_NUM_RAILSETS 				(6000)
// size of the rail endpoint bintable
#define ENDPOINT_BINTABLE_SIZE_BITS		(12) // 4096 entries

// default rail generation parameter values
// when changing these, don't forget to CHANGE THE DOCUMENTATION of the default script parameter values in cfuncs.cpp

// angle below which edges do not generate rails
#define MIN_RAIL_EDGE_ANGLE				(30.0f)
// maximum rail assent
// NOTE: can't sync with max skate slop because not a explicitly defined physics value (I think)
#define MAX_RAIL_ANGLE_OF_ASSENT		(45.0f)
// minimum length of a independent rail or a railset
#define MIN_RAILSET_LENGTH				(36.0f)
// minimum length of a rail, whether in a railset or not
#define MIN_EDGE_LENGTH					(0.0f)
// distance increment along rails at which collision detection is done
#define FEELER_INCREMENT				(36.0f)
// maximum heigh of a curb before it will get a rail 
// NOTE: sync with low-curb skate height from physics engine? i think that's 8.0f but I don't really know what I'm talking about
#define MAX_LOW_CURB_HEIGHT				(8.0f)
// angle of assent above face of low-curb feelers
#define LOW_CURB_FEELER_ANGLE_OF_ASSENT	(30.0f)
// maximum angle between two connected rails in a railset
#define MAX_CORNER_IN_RAILSET			(50.0f)
// height of vertical feeler
#define VERTICAL_FEELER_LENGTH			(5.0f * 12.0f)
// distance above rail at which crossbar feeler is used
#define CROSSBAR_FEELER_ELEVATION		(12.0f)
// half length of crossbar feeler
#define CROSSBAR_FEELER_LENGTH			(1.0f * 12.0f)
// farthest distance between an auto-generated rail and an old rail for which the auto-generated rail will be culled
#define FARTHEST_DEGENERATE_RAIL		(12.0f)
// angle between an old and and auto-generated rail below which they may be considered degenerate
#define MAX_DEGENERATE_RAIL_ANGLE		(20.0f)
// distance below which verticies are concidered equal
#define CONNECTION_SLOP					(0.1f)


// feeler usage bits
#define VERTICAL_FEELER_BIT				(1 << 0)
#define CROSSBAR_FEELER_BIT				(1 << 1)
#define LOW_CURB_FEELER_BIT				(1 << 2)
#define ALL_FEELERS_ON					(-1)

struct SEdge {
	// has a match for this edge been found?
	bool matched;

	// edge is from a vert poly; maybe I should just save all the face flag bits
	bool vert;

	// edge endpoints
	Mth::Vector p0, p1;

	// edge's face's unit normal
	Mth::Vector	normal;

	// edge's unit perp vector along face
	Mth::Vector perp;
};

struct SAutoRailEndpoint {
	// endpoint position
	Mth::Vector p;

	// connection at this end; -1 is no connection
	int connection;
};

struct SAutoRail {
	// rail endpoints
	SAutoRailEndpoint endpoints[2];

	// unit vector along rail
	Mth::Vector	para;

	// railset id; -1 for solo rail
	int railset;

	// rail length
	float length;

	// if found to be too sort or once added to the rail nodes, a rail is disabled
	bool disabled;
};

// dereference endpoints
enum { START = 0, END };

struct SRailSet {
	// length of rail set
	float length;
};

// collect the autorail state into a single structure to ease transportation
struct SAutoRailGeneratorState {
	SEdge* p_edges;
	SAutoRail* p_rails;
	SRailSet* p_railsets;

	// hash of rail endpoints; most rails are in hash twice, keyed off endpoint's X*Y rounded to nearest inch
	BinTable* p_rail_endpoint_list;

	int	num_rails;	
	int num_active_rails;
	int num_railsets;
};

// collect all the algorithm's input parameters into a structure
// see default values for explaination of meanings
struct SAutoRailGeneratorParameters {
	// input parameters
	float min_rail_edge_angle;
	float max_rail_angle_of_assent;
	float min_railset_length;
	float min_edge_length;
	float feeler_increment;
	float max_low_curb_height;
	float curb_feeler_angle_of_assent;
	float max_corner_in_railset;
	float vertical_feeler_length;
	float crossbar_feeler_elevation;
	float half_crossbar_feeler_length;
	float farthest_degenerate_rail;
	float max_degenerate_rail_angle;
	float connection_slop;
	int feeler_usage;

	// dependent parameters
	float sin_max_rail_angle_of_assent;
	float low_curb_feeler_length;
	float cos_min_rail_edge_angle;
	float cos_max_corner_in_railset;
	float sin_curb_feeler_angle_of_assent;
	float cos_curb_feeler_angle_of_assent;
	float cos_max_degenerate_rail_angle;

	// additional flags
	bool remove_old_rails;
};

inline bool very_close ( Mth::Vector a, Mth::Vector b, const SAutoRailGeneratorParameters& arp )
{
	return(Mth::Abs(a[X]-b[X]) < arp.connection_slop	
		&& Mth::Abs(a[Y]-b[Y]) < arp.connection_slop
		&& Mth::Abs(a[Z]-b[Z]) < arp.connection_slop
	);
}

inline int generate_endpoint_key ( Mth::Vector& end_point )
{
	return static_cast((end_point[X] * end_point[Z]) * (1.0f / 6.0f));
}

// checks for duplicate rails and then adds rails to rail array and endpoint bintable
inline bool add_rail ( const Mth::Vector& pa, const Mth::Vector& pb, SAutoRailGeneratorState& arg, const SAutoRailGeneratorParameters& arp )
{
	// cull out duplicate rails
	// NOTE: what is causing these? some levels do not have them; perhaps sloppy geometry
	for (int n = arg.num_rails; n--; )
	{
		// if this is a duplicate, bail
		if (very_close(arg.p_rails[n].endpoints[START].p, pa, arp) && very_close(arg.p_rails[n].endpoints[END].p, pb, arp)
			|| very_close(arg.p_rails[n].endpoints[START].p, pb, arp) && very_close(arg.p_rails[n].endpoints[END].p, pa, arp))
		{
			return true;
		}
	}

	// if we get to here, we've decided this is a valid rail
	
	if (arg.num_rails + 1 == MAX_NUM_RAILS) return false;

	// add rail to rail array

	arg.p_rails[arg.num_rails].endpoints[START].p = pa;
	arg.p_rails[arg.num_rails].endpoints[END].p = pb;
	arg.p_rails[arg.num_rails].endpoints[START].connection = -1;
	arg.p_rails[arg.num_rails].endpoints[END].connection = -1;
	arg.p_rails[arg.num_rails].railset = -1;
	arg.p_rails[arg.num_rails].disabled = false;

	arg.p_rails[arg.num_rails].para = pb - pa;
	arg.p_rails[arg.num_rails].length = arg.p_rails[arg.num_rails].para.Length();
	arg.p_rails[arg.num_rails].para.Normalize();

	// add rail to endpoint bintable
	arg.p_rail_endpoint_list->PutItemTwice(
		generate_endpoint_key(arg.p_rails[arg.num_rails].endpoints[START].p),
		generate_endpoint_key(arg.p_rails[arg.num_rails].endpoints[END].p),
		&arg.p_rails[arg.num_rails]
	);

	arg.num_rails++;
	
	return true;
}

// handles nearby collision detection
// use feelers to insure that this is a good rail; if the full rail doesn't work, attempt to snip it up into smaller
// valid rails
inline bool consider_rail ( const Mth::Vector& pa, const Mth::Vector& pb, int matched_edge, const Mth::Vector& para, const Mth::Vector& normal,
	const Mth::Vector& perp, float edge_length, SAutoRailGeneratorState& arg, SAutoRailGeneratorParameters& arp )
{
	// four feelers are used at each step along the edge
	// 1, 2) short feelers up from the edge's two faces
	// 3) longer feeler straight up from the edge
	// 4) medium feeler crossbar on feeler 3

	// there are full_step_length + 2 feeler points along an edge
	// 0 corresponds the the edge's start point
	// n corresponds to a points n steps in from the start point
	// full_step_length + 1 corresponds to the edge's end point
	int full_step_length = static_cast(edge_length / arp.feeler_increment);

	int start_step = 0;
	Mth::Vector r0 = pa;
	// attempt to create rail snippets until we've used up the rail
	do
	{
		// start a new rail snippet
		int end_step = start_step;
		Mth::Vector r1 = r0;

		// attempt to extent the rail snippet along the edge
		do
		{
			bool fail = false;

			// check for nearby collidables

			CFeeler feeler;
            
			// first side feeler
			// angled up from face in perp-normal plane starting a fraction of an inch from edge
			if ((arp.feeler_usage & LOW_CURB_FEELER_BIT))
			{
				Mth::Vector feeler_direction = arp.sin_curb_feeler_angle_of_assent * normal + arp.cos_curb_feeler_angle_of_assent * perp;

				feeler.m_start = r1 + 0.1f * feeler_direction;
				feeler.m_end = r1 + arp.low_curb_feeler_length * feeler_direction;

				fail = feeler.GetCollision(false);
			}

			// second side feeler
			// angled up from face in perp-normal plane starting a fraction of an inch from edge
			if (!fail && (arp.feeler_usage & LOW_CURB_FEELER_BIT))
			{
				Mth::Vector feeler_direction = arp.sin_curb_feeler_angle_of_assent * arg.p_edges[matched_edge].normal
					+ arp.cos_curb_feeler_angle_of_assent * arg.p_edges[matched_edge].perp;

				feeler.m_start = r1 + 0.2f * feeler_direction;
				feeler.m_end = r1 + arp.low_curb_feeler_length * feeler_direction;

				fail = feeler.GetCollision(false);
			}

			// vertical feeler
			// straight up starting a fraction of an inch above the edge
			if (!fail && (arp.feeler_usage & VERTICAL_FEELER_BIT))
			{
				feeler.m_start = r1;
				feeler.m_start[Y] += 0.2f;

				feeler.m_end = r1;
				feeler.m_end[Y] += arp.vertical_feeler_length;
			}

			// crossbar feeler
			// some height above rail, flush with XZ plane, perp to edge
			if (!fail && (arp.feeler_usage & CROSSBAR_FEELER_BIT))
			{
				Mth::Vector bar;

				bar[X] = para[Z];
				bar[Y] = 0.0f;
				bar[Z] = -para[X];
				bar.Normalize();

				feeler.m_start = r1;
				feeler.m_start[Y] += arp.crossbar_feeler_elevation;
				feeler.m_start += arp.half_crossbar_feeler_length * bar;

				feeler.m_end = r1;
				feeler.m_end[Y] += arp.crossbar_feeler_elevation;
				feeler.m_end += -arp.half_crossbar_feeler_length * bar;

				fail = feeler.GetCollision(false);
			}

			// if there is collidables, the extention attempt failed
			if (fail) break;

			// there are no nearby collidables
			// so we'll attempt to stretch the snippet one step or to the end of the edge
			end_step++;

			// if we're beyond the end of the edge, we're done
			if (end_step == full_step_length + 2) break;

			if (end_step == full_step_length + 1)
			{
				r1 = pb;
			}
			else
			{
				r1 = pa + end_step * arp.feeler_increment * para;
			}
		} while (true);

		// restore r1 to the last value it had before the failed extention
		end_step--;
		if (end_step == full_step_length + 1)
			r1 = pb;
		else
			r1 = pa + end_step * arp.feeler_increment * para;

		// the snippet is now as long as it can be

		// if the snippet is long enough
		if (end_step - start_step > 0 && (r0 - r1).LengthSqr() >= (arp.min_edge_length * arp.min_edge_length))
		{
			// use it
			if (!add_rail(r0, r1, arg, arp)) return false;
		}

		// jump to next start point
		start_step = end_step + 2;
		r0 = pa + start_step * arp.feeler_increment * para;

	} while (start_step < full_step_length + 1);

	return true;
}

CRailNode* CRailManager::GetRailNodeByNodeNumber( int node_num )
{
	int i;

	if( mp_nodes )
	{
		for( i = 0; i < m_num_nodes; i++ )
		{
			if( mp_nodes[i].GetNode() == node_num )
			{
				return &mp_nodes[i];
			}
		}
	}

	return NULL;
}

// knowns issues or ideas:
// snap rail endpoints - do we want to snap the endpoints of railset together; if so, definitely make the slop parameter adjustable
// vertical feeler - the vertical feeler is causing random snipping up of rails in odd places
// don't destory old rails - instead, when considering a rail, check for nearly parallel rails and don't add a new rail if you find one; bin old rails first
// cross-section edges finding - look for potential rails by joining up across sectors; time consuming; edge endpoints won't match, so we have to look for overlapping edges instead of just looking for matching endpoints
void CRailManager::AutoGenerateRails ( Script::CStruct* pParams )
{
// 27K
#ifdef	__DEBUG_CODE__
	printf("-----------------------\n");

	// let's use the Debug heap, since we might need a lot of memory
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());

	// auto-generator's state
	SAutoRailGeneratorState arg;

	arg.num_rails = 0;	
	arg.num_railsets = 0;
	arg.num_active_rails = 0;

	arg.p_edges = new SEdge[MAX_NUM_EDGES];
	arg.p_rails = new SAutoRail[MAX_NUM_RAILS];
	arg.p_railsets = new SRailSet[MAX_NUM_RAILSETS];

	arg.p_rail_endpoint_list = new BinTable(ENDPOINT_BINTABLE_SIZE_BITS);

	// auto-generator's modifiable parameters
	SAutoRailGeneratorParameters arp;

	// set parameters to defaults
	arp.min_rail_edge_angle = MIN_RAIL_EDGE_ANGLE;
	arp.max_rail_angle_of_assent = MAX_RAIL_ANGLE_OF_ASSENT;
	arp.min_railset_length = MIN_RAILSET_LENGTH;
	arp.min_edge_length = MIN_EDGE_LENGTH;
	arp.feeler_increment = FEELER_INCREMENT;
	arp.max_low_curb_height = MAX_LOW_CURB_HEIGHT;
	arp.curb_feeler_angle_of_assent = LOW_CURB_FEELER_ANGLE_OF_ASSENT;
	arp.max_corner_in_railset = MAX_CORNER_IN_RAILSET;
	arp.vertical_feeler_length = VERTICAL_FEELER_LENGTH;
	arp.crossbar_feeler_elevation = CROSSBAR_FEELER_ELEVATION;
	arp.half_crossbar_feeler_length = CROSSBAR_FEELER_LENGTH;
	arp.farthest_degenerate_rail = FARTHEST_DEGENERATE_RAIL;
	arp.max_degenerate_rail_angle = MAX_DEGENERATE_RAIL_ANGLE;
	arp.connection_slop = CONNECTION_SLOP;

	// look for overrides in script parameters
	pParams->GetFloat("min_rail_edge_angle", &arp.min_rail_edge_angle, Script::NO_ASSERT);
	pParams->GetFloat("max_rail_angle_of_assent", &arp.max_rail_angle_of_assent, Script::NO_ASSERT);
	pParams->GetFloat("min_railset_length", &arp.min_railset_length, Script::NO_ASSERT);
	pParams->GetFloat("min_edge_length", &arp.min_edge_length, Script::NO_ASSERT);
	pParams->GetFloat("feeler_increment", &arp.feeler_increment, Script::NO_ASSERT);
	pParams->GetFloat("max_low_curb_height", &arp.max_low_curb_height, Script::NO_ASSERT);
	pParams->GetFloat("curb_feeler_angle_of_assent", &arp.curb_feeler_angle_of_assent, Script::NO_ASSERT);
	pParams->GetFloat("max_corner_in_railset", &arp.max_corner_in_railset, Script::NO_ASSERT);
	pParams->GetFloat("vertical_feeler_length", &arp.vertical_feeler_length, Script::NO_ASSERT);
	pParams->GetFloat("crossbar_feeler_elevation", &arp.crossbar_feeler_elevation, Script::NO_ASSERT);
	pParams->GetFloat("crossbar_feeler_length", &arp.half_crossbar_feeler_length, Script::NO_ASSERT);
	pParams->GetFloat("farthest_degenerate_rail", &arp.farthest_degenerate_rail, Script::NO_ASSERT);
	pParams->GetFloat("max_degenerate_rail_angle", &arp.max_degenerate_rail_angle, Script::NO_ASSERT);
	pParams->GetFloat("connection_slop", &arp.connection_slop, Script::NO_ASSERT);

	arp.half_crossbar_feeler_length /= 2.0f;
	arp.low_curb_feeler_length = arp.max_low_curb_height / cosf(DEGREES_TO_RADIANS(arp.curb_feeler_angle_of_assent));
	arp.sin_max_rail_angle_of_assent = sinf(DEGREES_TO_RADIANS(arp.max_rail_angle_of_assent));
	arp.cos_min_rail_edge_angle = cosf(DEGREES_TO_RADIANS(arp.min_rail_edge_angle));
	arp.cos_max_corner_in_railset = cosf(DEGREES_TO_RADIANS(arp.max_corner_in_railset));
	arp.sin_curb_feeler_angle_of_assent = sinf(DEGREES_TO_RADIANS(arp.curb_feeler_angle_of_assent));
	arp.cos_curb_feeler_angle_of_assent = cosf(DEGREES_TO_RADIANS(arp.curb_feeler_angle_of_assent));
	arp.cos_max_degenerate_rail_angle = cosf(DEGREES_TO_RADIANS(arp.max_degenerate_rail_angle));

	// turn on all feelers by default
	arp.feeler_usage = ALL_FEELERS_ON;

	// check for overrides in script parameters
	if (pParams->ContainsFlag("no_vertical_feeler"))
		arp.feeler_usage &= ~VERTICAL_FEELER_BIT;
	if (pParams->ContainsFlag("no_crossbar_feeler"))
		arp.feeler_usage &= ~CROSSBAR_FEELER_BIT;
	if (pParams->ContainsFlag("no_low_curb_feeler"))
		arp.feeler_usage &= ~LOW_CURB_FEELER_BIT;
	if (pParams->ContainsFlag("no_feelers"))
		arp.feeler_usage = 0;

	// check additional flags
	arp.remove_old_rails = false;
	if (pParams->ContainsFlag("overwrite"))
	{
		arp.remove_old_rails = true;
	}

	// dump parameter state

	printf("min_rail_edge_angle = %f\n", arp.min_rail_edge_angle);
	printf("max_rail_angle_of_assent = %f\n", arp.max_rail_angle_of_assent);
	printf("min_railset_length = %f\n", arp.min_railset_length);
	printf("min_edge_length = %f\n", arp.min_edge_length);
	printf("connection_slop = %f\n", arp.connection_slop);
	printf("feeler_increment = %f\n", arp.feeler_increment);
	printf("max_low_curb_height = %f\n", arp.max_low_curb_height);
	printf("curb_feeler_angle_of_assent = %f\n", arp.curb_feeler_angle_of_assent);
	printf("max_corner_in_railset = %f\n", arp.max_corner_in_railset);
	printf("vertical_feeler_length = %f\n", arp.vertical_feeler_length);
	printf("crossbar_feeler_length = %f\n", 2.0f * arp.half_crossbar_feeler_length);
	printf("crossbar_feeler_elevation = %f\n", arp.crossbar_feeler_elevation);

	if (!arp.remove_old_rails)
	{
		printf("farthest_degenerate_rail = %f\n", arp.farthest_degenerate_rail);
		printf("max_degenerate_rail_angle = %f\n", arp.max_degenerate_rail_angle);
	}

	if (!(arp.feeler_usage & VERTICAL_FEELER_BIT))
		printf("vertical feeler deactivated\n");
	if (!(arp.feeler_usage & CROSSBAR_FEELER_BIT))
		printf("crossbar feeler deactivated\n");
	if (!(arp.feeler_usage & LOW_CURB_FEELER_BIT))
		printf("low-curb feeler deactivated\n");
	if (arp.feeler_usage == ALL_FEELERS_ON)
		printf("all feelers active\n");

	if (arp.remove_old_rails)
	{
		printf("removing existing rails\n");
	}
	else
	{
		printf("retaining existing rails\n");
	}

	printf("generating rails...\n");

	Nx::CScene *p_scene = Nx::CEngine::sGetMainScene();
	Lst::HashTable* p_sector_list = p_scene->GetSectorList();
	if (!p_sector_list)
		return;

	// loop through every sector in the scene
	// NOTE: to do a better job, we may need to attempt to match edges between sectors
	// if so, we'd have to deal with the issue of not being able to expect edge vertices to match up
	p_sector_list->IterateStart();	
	Nx::CSector *p_sector = p_sector_list->IterateNext();		
	while(p_sector)
	{
		Nx::CCollObjTriData *p_coll_obj = p_sector->GetCollSector()->GetGeometry();
		if (!p_coll_obj || !p_sector->IsActive() || !p_sector->IsCollidable())
		{
			p_sector = p_sector_list->IterateNext();		
			continue;
		}

		// we have the collision data, now build a list of edges
		// based solely on positions of verts

		int	num_edges = 0;

		Mth::Vector p[3];
		
		// loop though the faces of the sector
		int num_faces = p_coll_obj->GetNumFaces();
		for (int face = 0; face < num_faces; face++)
		{
			// get the normal													 
			Mth::Vector normal = p_coll_obj->GetFaceNormal(face);
			
			// Get the three vertex indicies for this face
			// and the vertex position into the above arrays
			int v[3];
            for (int i = 0; i < 3; i++)			
			{
				v[i] = p_coll_obj->GetFaceVertIndex(face,i);
				p[i] = p_coll_obj->GetRawVertexPos(v[i]);
					
			}
			// iterate over the edges (0->1, 1->2, 2->0)
			for (int a = 0; a < 3; a++)	   		// 'a' is the first point on the edge
			{				
				int b = (a + 1) % 3;			// 'b' is the second point on the edge
				int c = (b + 1) % 3;			// 'c' is the opposite point on the triangle
				// we are considering the edge a->b

				// generate a unit vector along the edge from a to b, from start to end
				Mth::Vector	para = (p[b] - p[a]);
				float edge_length = para.Length();
				if (edge_length != 0.0f) para /= edge_length; // normalize by hand since we have the length

				// cull certain edges at this point; we don't cull faces at this point in
				// order to better match edges

				// cull very short edges
				if (edge_length < arp.min_edge_length) continue;

				// use edges only if less than some angle from horizontal
				if (Mth::Abs(para[Y]) > arp.sin_max_rail_angle_of_assent) continue;

				// generate a unit vector along the triangle face perpendicular to the edge
				Mth::Vector perp = (p[c] - p[a]);
				perp -= Mth::DotProduct(perp, para) * para;
				perp.Normalize();

				// check to see if we already have this edge's match; bail if we find a match
				bool new_edge = true;
				for (int edge = 0; edge < num_edges && new_edge; edge++)
				{
					if (arg.p_edges[edge].matched) continue;
				
					// check for matched edge 
					if (very_close(arg.p_edges[edge].p0, p[a], arp) && very_close(arg.p_edges[edge].p1, p[b], arp)
						|| 	very_close(arg.p_edges[edge].p0, p[b], arp) && very_close(arg.p_edges[edge].p1, p[a], arp))
					{
						new_edge = false;
						arg.p_edges[edge].matched = true;
						
						// we've found a match, let's see if the edge is a good rail
						{
							// ignore upside down faces			
							// NOTE: we could probably come up with a better heuristic using both edges' normals
							if (normal[Y] < -0.707 || arg.p_edges[edge].normal[Y] < -0.707) break;

							// ignore face if non-collidable
							if (p_coll_obj->GetFaceFlags(face) & mFD_NON_COLLIDABLE) break;
							
							// exclude roughly coplanar polygons
							if (Mth::DotProduct(normal, arg.p_edges[edge].normal) > arp.cos_min_rail_edge_angle) break;
														
							// cull out interior edges; that is, if sum of the normals points along the sum of the perps
							if (Mth::DotProduct(perp + arg.p_edges[edge].perp, normal + arg.p_edges[edge].normal) > 0.0f) break;

							// we don't want the edges of verts to generate rails, only the tops; so cull non-horizontal vert rails
							if ((arg.p_edges[edge].vert || (p_coll_obj->GetFaceFlags(face) & mFD_VERT)) && Mth::Abs(para[Y]) > 0.1f) break;

							// if we haven't removed the old rails
							if (!arp.remove_old_rails)
							{
								// loop over all old rails and check for degeneracy
								int check_node = 0;
								for (check_node = 0; check_node < m_num_nodes; check_node++)
								{
									CRailNode *pRailNode = &mp_nodes[check_node];
	
									if (!pRailNode->m_pNextLink) continue;
	
									// is the new rail within a loose bounding box of the old rail
									if (p[a][X] < pRailNode->m_BBMin[X] - arp.farthest_degenerate_rail) continue;
									if (p[a][Y] < pRailNode->m_BBMin[Y] - arp.farthest_degenerate_rail) continue;
									if (p[a][Z] < pRailNode->m_BBMin[Z] - arp.farthest_degenerate_rail) continue;
									if (p[a][X] > pRailNode->m_BBMax[X] + arp.farthest_degenerate_rail) continue;
									if (p[a][Y] > pRailNode->m_BBMax[Y] + arp.farthest_degenerate_rail) continue;
									if (p[a][Z] > pRailNode->m_BBMax[Z] + arp.farthest_degenerate_rail) continue;
									if (p[b][X] < pRailNode->m_BBMin[X] - arp.farthest_degenerate_rail) continue;
									if (p[b][Y] < pRailNode->m_BBMin[Y] - arp.farthest_degenerate_rail) continue;
									if (p[b][Z] < pRailNode->m_BBMin[Z] - arp.farthest_degenerate_rail) continue;
									if (p[b][X] > pRailNode->m_BBMax[X] + arp.farthest_degenerate_rail) continue;
									if (p[b][Y] > pRailNode->m_BBMax[Y] + arp.farthest_degenerate_rail) continue;
									if (p[b][Z] > pRailNode->m_BBMax[Z] + arp.farthest_degenerate_rail) continue;
	
									// is the new rail parallel to the old rail
									Mth::Vector old_para = pRailNode->m_pNextLink->m_pos - pRailNode->m_pos;
									old_para.Normalize();
									if (Mth::Abs(Mth::DotProduct(para, old_para)) < arp.cos_max_degenerate_rail_angle) continue;
	
									// if the perpendicular distance from the start of the new rail to the start of the old rail is small, it's degenerate
									perp = p[a] - pRailNode->m_pos;
									perp -= Mth::DotProduct(para, perp) * para;
									if (perp.LengthSqr() < (arp.farthest_degenerate_rail * arp.farthest_degenerate_rail)) break;
	
									// if the perpendicular distance from the end of the new rail to the start of the old rail is small, it's degenerate
									perp = p[b] - pRailNode->m_pos;
									perp -= Mth::DotProduct(para, perp) * para;
									if (perp.LengthSqr() < (arp.farthest_degenerate_rail * arp.farthest_degenerate_rail)) break;
	
									// if the perpendicular distance from the start of the new rail to the end of the old rail is small, it's degenerate
									perp = p[a] - pRailNode->m_pNextLink->m_pos;
									perp -= Mth::DotProduct(para, perp) * para;
									if (perp.LengthSqr() < (arp.farthest_degenerate_rail * arp.farthest_degenerate_rail)) break;
	
									// if the perpendicular distance from the end of the new rail to the end of the old rail is small, it's degenerate
									perp = p[b] - pRailNode->m_pNextLink->m_pos;
									perp -= Mth::DotProduct(para, perp) * para;
									if (perp.LengthSqr() < (arp.farthest_degenerate_rail * arp.farthest_degenerate_rail)) break;
								}

								// if we broke from the loop, cull the rail due to degeneracy
								if (check_node != m_num_nodes) break;
							}

							// use feelers to test for good rails within this edge
							if (!consider_rail(p[a], p[b], edge, para, normal, perp, edge_length, arg, arp))
							{
								printf("failed: more than %d rails\n", MAX_NUM_RAILS);
								goto ABORT;
							}

							
						} // END valid rail test block
					} // END if (this edge matches)
				} // END loop over previous edges looking for match
				
				// if we didn't find a match
				if (new_edge)
				{
					if (num_edges + 1 < MAX_NUM_EDGES)
					{
						// save the new edge
						arg.p_edges[num_edges].p0 = p[a];
						arg.p_edges[num_edges].p1 = p[b];
						arg.p_edges[num_edges].normal = normal;
						arg.p_edges[num_edges].perp = perp;
						arg.p_edges[num_edges].matched = false;
						arg.p_edges[num_edges].vert = p_coll_obj->GetFaceFlags(face) & mFD_VERT;

						num_edges++;
					}
					else
					{
						printf("failed: more that %d edges in an object\n", MAX_NUM_EDGES);
						goto ABORT;	// sorry dijkstra ...
					}
				} // END if (new_edge)
			} // END for (int a=0;a<3;a++) (iterating over 3 edges in a face)
		} // END for (int face = 0; face < num_faces; face++) (iterate over faces in a collision sector)

		p_sector = p_sector_list->IterateNext();
	} // END loop over sectors

	printf("building railsets...\n");

	// we need to group in the individual rails into continuous rail sets, so we can cull short solo rails and short railsets
	// note that the connectivity is not perfect in the sense that it can make loops but not loops with tails

	// loop through rail set
	for (int r = 0; r < arg.num_rails; r++)
	{
		SAutoRail& rail = arg.p_rails[r];

		// loop over rail's endpoints
		for (int endpoint = START; endpoint <= END; endpoint++)
		{
			// if the endpoint is connected
			if (rail.endpoints[endpoint].connection != -1) continue;

			// now we'll check for connections

			int rail_key = generate_endpoint_key(rail.endpoints[endpoint].p);
	
			// loop over all rails in same endpoint bin as rail's endpoint's bin
			// we will check only these rails for connections
			for (SAutoRail* next_rail = arg.p_rail_endpoint_list->GetFirstItem(rail_key);
				next_rail;
				next_rail = arg.p_rail_endpoint_list->GetNextItem(rail_key, next_rail))
			{
				SAutoRail& match_rail = *next_rail;

				// we'll always find ourself
				if (&match_rail == &rail) continue;
	
				// check only rails with unconnected endpoints
				if (match_rail.endpoints[START].connection != -1 && match_rail.endpoints[END].connection != -1) continue;

				// rails connect only if the angle between them is small; this should be adjusted or adjustable
				float dot = Mth::DotProduct(rail.para, match_rail.para);
				if (Mth::Abs(dot) < arp.cos_max_corner_in_railset) continue;

				bool reverse_connection = false;

				// see if the start of the match rail is connected to our endpoint
				if (match_rail.endpoints[START].connection == -1
					&& very_close(rail.endpoints[endpoint].p, match_rail.endpoints[START].p, arp))
				{
					// rails connect only for obtuse angles
					if (endpoint == START)
					{
						reverse_connection = true;
						if (dot > 0.0f) continue;
					}
					else
					{
						if (dot < 0.0f) continue;
					}

					rail.endpoints[endpoint].connection = &match_rail - arg.p_rails;
					match_rail.endpoints[START].connection = r;
				}

				// else see if the end of the match rail is connected to our endpoint
				else if (match_rail.endpoints[END].connection == -1
					&& very_close(rail.endpoints[endpoint].p, match_rail.endpoints[END].p, arp))
				{
					// rails connect only for obtuse angles
					if (endpoint == END)
					{
						reverse_connection = true;
						if (dot > 0.0f) continue;
					}
					else
					{
						if (dot < 0.0f) continue;
					}
                    
					rail.endpoints[endpoint].connection = &match_rail - arg.p_rails;
					match_rail.endpoints[END].connection = r;
				} // END ifelse determining connectivity

				// if this endpoint of the rail is not found to be connected, continue
				if (rail.endpoints[endpoint].connection == -1) continue;

				// otherwise, we'll add the rail to a railset

				// if both rails are not in railset
				if (match_rail.railset == -1 && rail.railset == -1)
				{
					if (arg.num_railsets == MAX_NUM_RAILSETS)
					{
						printf("failed: more that %d railsets\n", MAX_NUM_RAILSETS);
						goto ABORT;
					}

					// setup a new railset
					rail.railset = arg.num_railsets;
					arg.p_railsets[arg.num_railsets].length = rail.length;
					arg.num_railsets++;

					// add match_rail to rail's new railset
					match_rail.railset = rail.railset;
					arg.p_railsets[match_rail.railset].length += match_rail.length;

					// swap the rail's endpoints if the connection is reverse
					if (reverse_connection)
					{
						SAutoRailEndpoint temp = rail.endpoints[START];
						rail.endpoints[START] = rail.endpoints[END];
						rail.endpoints[END] = temp;
					}

				// if only match_rail has a railset
				}
				else if (match_rail.railset != -1 && rail.railset == -1)
				{
					// add rail to match_rail's railset
					rail.railset = match_rail.railset;
					arg.p_railsets[rail.railset].length += rail.length;

					// swap the rail's endpoints if the connection is reverse
					if (reverse_connection)
					{
						SAutoRailEndpoint temp = rail.endpoints[START];
						rail.endpoints[START] = rail.endpoints[END];
						rail.endpoints[END] = temp;
					}

				// if only rail has a railset
				}
				else if (match_rail.railset == -1 && rail.railset != -1)
				{
					// add match_rail to rail's railset
					match_rail.railset = rail.railset;
					arg.p_railsets[match_rail.railset].length += rail.length;

					// swap the match rail's endpoints if the connection is reverse
					if (reverse_connection)
					{
						SAutoRailEndpoint temp = match_rail.endpoints[START];
						match_rail.endpoints[START] = match_rail.endpoints[END];
						match_rail.endpoints[END] = temp;
					}

				// if both rails have multrails
				}
				else
				{
					// join railsets
					// NOTE: this currently waists a railset; more flexible railset data structure would fix this;
					// can i use STL? if geometry is ordered in any reasonable fashion, waist should be minimal

					// match_rail's railset survives
					if (match_rail.railset != rail.railset)
					{
						match_rail.length += arg.p_railsets[rail.railset].length;
						for (int n = arg.num_rails; n--; )
						{
							if (arg.p_rails[n].railset == rail.railset) {
								arg.p_rails[n].railset = match_rail.railset;
							}
						}
						rail.railset = match_rail.railset;
					}

					// swap all of the rail's railset's endpoints if the connection is reverse
					// if we're closing a ring, the connection should never be reversed; so that shouldn't be a worry
					if (reverse_connection)
					{
						// traverse in the opposite direction of the connection
						int traversal_direction = endpoint ^ 1;

						// traverse the rail's railset
						for (int s = r; s != -1; s = arg.p_rails[s].endpoints[traversal_direction].connection)
						{
							SAutoRailEndpoint temp = arg.p_rails[s].endpoints[START];
							arg.p_rails[s].endpoints[START] = arg.p_rails[s].endpoints[END];
							arg.p_rails[s].endpoints[END] = temp;
						}
					}
				} // END ifelse block determining rail and match_rail's previous connectivity
			} // END loop over potential match rails

		} // END loop over endpoints
	} // END loop over all rails

	printf("culling short rails...\n");

	// now we have rails grouped into railsets; we can cull rails based on their length or the length of their railset
	for (int r = 0; r < arg.num_rails; r++)
	{
		SAutoRail& rail = arg.p_rails[r];

		// solo rails
		if (rail.railset == -1)
		{
			// cull short solo rails
			if (rail.length < arp.min_railset_length)
			{
				rail.disabled = true;
				continue;
			}
		}
		// railsets
		else
		{
			// cull short railsets
			if (arg.p_railsets[rail.railset].length < arp.min_railset_length)
			{
				rail.disabled = true;
				continue;
			}
		}

		arg.num_active_rails++;
	} // END loop over rails

	if (!arp.remove_old_rails && arg.num_rails == 0)
	{
		printf("failed: no rails found\n");
		goto ABORT;
	}

	printf("creating rail nodes...\n");

	// block to scope the final-rail-list generation variables
	{
		// we need to count the number of nodes required for the new rails
		// new_num_nodes = (num rails) + (num rails with no end connection)	+ (num old nodes)
		int new_num_nodes = arg.num_active_rails;
		for (int r = arg.num_rails; r--; )
		{
			if (!arg.p_rails[r].disabled && arg.p_rails[r].endpoints[END].connection == -1)
			{
				new_num_nodes++;
			}
		}
		if (!arp.remove_old_rails)
		{
			new_num_nodes += m_num_nodes;
		}
	
		// and create the array for the rails	
		CRailNode* p_new_nodes = (CRailNode*)Mem::Malloc(new_num_nodes * sizeof(CRailNode));
	
		// add the old rails to the new data structure
		int next_node = 0;
		if (!arp.remove_old_rails)
		{
			// loop over the old nodes
			for (; next_node < m_num_nodes; next_node++)
			{
				CRailNode* pOldRailNode = &mp_nodes[next_node];
				CRailNode* pRailNode = &p_new_nodes[next_node];

				pRailNode->m_node = next_node;

				// calculate connectivity using pointer offsets
				pRailNode->m_pNextLink = (pOldRailNode->m_pNextLink
										  ? pOldRailNode->m_pNextLink - mp_nodes + p_new_nodes
										  : NULL);
				pRailNode->m_pPrevLink = (pOldRailNode->m_pPrevLink
										  ? pOldRailNode->m_pPrevLink - mp_nodes + p_new_nodes
										  : NULL);

				// copy in their state
				pRailNode->m_flags = pOldRailNode->m_flags;
				pRailNode->m_pos = pOldRailNode->m_pos;
				pRailNode->m_terrain_type = pOldRailNode->m_terrain_type; // debug colors for now pOldRailNode->m_terrain_type;
				pRailNode->m_BBMin = pOldRailNode->m_BBMin;
				pRailNode->m_BBMax = pOldRailNode->m_BBMax;
			} // END loop over old nodes
		} // END if retaining old rails
	
		// iterate over the nodes and add the new rails to the array; each time we hit a railset, we move to the head, then traverse the set, adding the
		// rails; not the most optimal algorithm but simple; we skip previously added rails and we move through the array
		for (int r = 0; r < arg.num_rails; r++)
		{
			// because we add whole railsets at once, we may already have added any given rail
			if (arg.p_rails[r].disabled) continue;

			// traverse to start of this rail's railset watching for a loop
			int s = r;
			while (arg.p_rails[s].endpoints[START].connection != -1)
			{
				s = arg.p_rails[s].endpoints[START].connection;
				if (s == r) break;
			}

			// traverse the railset, adding nodes as we go
			int starting_rail = s;
			CRailNode* p_starting_node = &p_new_nodes[next_node];
			int last_s;
			do {
				CRailNode* pRailNode = &p_new_nodes[next_node];
				pRailNode->m_node = next_node;

				if (arg.p_rails[s].endpoints[START].connection != -1)
				{
					pRailNode->m_pPrevLink = &p_new_nodes[next_node - 1];
				}
				else
				{
					pRailNode->m_pPrevLink = NULL;
				}

				// check for a loop
				if (arg.p_rails[s].endpoints[END].connection != starting_rail)
				{
					pRailNode->m_pNextLink = &p_new_nodes[next_node + 1];
				}
				else
				{
					pRailNode->m_pNextLink = p_starting_node;
				}

				pRailNode->m_flags = 0;
				pRailNode->SetActive(true);
				pRailNode->m_pos = arg.p_rails[s].endpoints[START].p;
				if (arg.p_rails[s].railset == -1)
					pRailNode->m_terrain_type = vTERRAIN_CONCSMOOTH; // red
				else
					pRailNode->m_terrain_type = vTERRAIN_METALSMOOTH; // blue
				Rail_ComputeBB(arg.p_rails[s].endpoints[START].p, arg.p_rails[s].endpoints[END].p, pRailNode->m_BBMin, pRailNode->m_BBMax);

				// mark as having been added
				arg.p_rails[s].disabled = true;
				next_node++;

				last_s = s;
				s = arg.p_rails[s].endpoints[END].connection;
			} while (s != -1 && s != starting_rail);

			// if not a loop
			if (s != starting_rail)
			{
				// add extra ending node of railset

				CRailNode* pRailNode = &p_new_nodes[next_node];
				pRailNode->m_node = next_node;
	
				pRailNode->m_pPrevLink = &p_new_nodes[next_node - 1];
				pRailNode->m_pNextLink = NULL;

				pRailNode->m_pos = arg.p_rails[last_s].endpoints[END].p;
	
				next_node++;
			}
		} // END final-rail-list generation scope
	
		// first reset the manager (this)
		// Note this invalidates mp_node_array (setting it to NULL)
		// so I had to patch up a couple of placed in skater.cpp
		// that were using the rail manager's node array
		Cleanup();
	
		mp_nodes = p_new_nodes;
 		m_num_nodes = new_num_nodes;
	
		printf("complete.\n");

	}

ABORT:

	delete	arg.p_rail_endpoint_list;

	delete	[] arg.p_railsets;	
	delete	[] arg.p_edges;
	delete  [] arg.p_rails;
	
	Mem::Manager::sHandle().PopContext();
	
	printf("-----------------------\n");
#endif	
}

}
			   



================================================
FILE: Code/Sk/Objects/rail.h
================================================
//////////////////////////////////////////
// rail.h

#ifndef __OBJECTS_RAIL_H
#define __OBJECTS_RAIL_H
                        
#include 
#include 
#include 
                           
namespace Script
{
	class	CArray;
	class	CStruct;
}

#define	MAX_RAIL_LINKS 4


namespace Obj
{

class	CRailManager;
class	CWalkComponent;
struct	SHangRailData;
struct	SLadderRailData;

enum ERailNodeFlag
{	
	LIP_OVERRIDE,		// always do a lip trick on this
	DEFAULT_LINE,
	ACTIVE,
	NO_CLIMBING,
	ONLY_CLIMBING,
	LADDER,
	NO_HANG_WITH_RIGHT_ALONG_RAIL,
	NO_HANG_WITH_LEFT_ALONG_RAIL
};


// seperating out a parallel structure of CRailLinks, as only used 
// when rails are first parsed
class	CRailLinks
{
	friend class CRailManager;
	sint16 m_link[MAX_RAIL_LINKS];	// link numbers	
};

class CRailNode : public Spt::Class
{
	friend class  CRailManager;

public:						  
	bool 			ProbablyOnSameRailAs(int SearchNode) const;
	int				Side(const Mth::Vector &vel) const;
	
	const CRailNode	*	GetNextLink() const				{return m_pNextLink;}	
	const CRailNode	*	GetPrevLink() const				{return m_pPrevLink;}	
	const sint16			GetNode() const 	  				{return m_node;}
//	const sint16			GetLink() const	  				{return m_link;}
	const ETerrainType	GetTerrain() const	  			{return (ETerrainType) m_terrain_type;}
	
	void				SetActive(bool active)		{if (active) SetFlag(ACTIVE); else ClearFlag(ACTIVE);}
	const bool			GetActive() const					{return GetFlag(ACTIVE);}
	const bool			IsActive() const					{return GetActive();}

	void			SetFlag(ERailNodeFlag flag) {m_flags.Set(flag);}
	void			ClearFlag(ERailNodeFlag flag) {m_flags.Clear(flag);}
	bool			GetFlag(ERailNodeFlag flag) const {return m_flags.Test(flag);}

protected:

// MEMOPT:  In theory this could all fit in 3x16 = 48 bytes, instead it is 64, becaute the Mth::Vectors are 16 byte aligned
// possibly could define a Mth::Vector3 that uses 12 bytes, with appropiate copy operators.

	Mth::Vector m_BBMin;		// bounding box for this node and the subsequent one
	Mth::Vector m_BBMax;		// (or just a zero-sized box, if not applicable)
	Mth::Vector m_pos;			// position of the node

    Mth::Quat m_orientation;    // orientation of the rail node; only set for climbing nodes
	
	CRailNode *m_pNextLink;		// Pointer to next Node	in rail(or NULL if none)
	CRailNode *m_pPrevLink;		// Pointer to previous Node	(or NULL if none)

	Flags< ERailNodeFlag > m_flags; // flags for rail segment
	
	sint16 m_node;				// Number of the node in the trigger array	
	uint8 m_terrain_type;		// play the correct grind sound on the rail...
	
    

// the position getting functions are private
// so you can only access them through the rail manager
// (as you need to know if it's transformed or not)
private:
	const Mth::Vector&	GetPos() const;	 
	const Mth::Quat&	GetOrientation() const;	 
	const Mth::Vector&	GetBBMin() const;
	const Mth::Vector&	GetBBMax() const;
						  
};



class CRailManager : public Spt::Class
{

public:
					CRailManager();
				   ~CRailManager();	

	
	void 			Cleanup();
								 
	void			AddRailsFromNodeArray(Script::CArray * p_node_array);
	void 			AddRailNode(int node_number, Script::CStruct *p_node_struct);
	void 			AddedAll();
	
	void			UpdateTransform(Mth::Matrix& transform);
    bool			CheckForLadderRail ( const Mth::Vector& pos, float max_horizontal_snap_distance, float max_vertical_snap_distance, bool up, CWalkComponent* p_walk_component, SLadderRailData& rail_data, const CRailNode** pp_rail_node );
	bool			CheckForAirGrabLadderRail ( const Mth::Vector& start_pos, const Mth::Vector& end_pos, CWalkComponent* p_walk_component, SLadderRailData& rail_data, const CRailNode** pp_rail_node );
	// bool			CheckForHopToHangRail ( const Mth::Vector& check_line_start, float check_line_height, const Mth::Vector& facing, float max_forward_reach, float max_backward_reach, CWalkComponent* p_walk_component, SHangRailData& rail_data, const CRailNode** pp_rail_node );
	bool			CheckForHangRail ( const Mth::Vector& start_pos, const Mth::Vector& end_pos, const Mth::Vector& facing, CWalkComponent* p_walk_component, SHangRailData& rail_data, float snap_distance );
	bool			RailNodesAreCoincident ( const CRailNode* p_node_a, const CRailNode* p_node_b );
	bool			CheckForCoincidentRailNode ( const CRailNode* p_node, uint32 ignore_mask, const CRailNode** pp_next_node );
    bool 			StickToRail(const Mth::Vector &pos1, const Mth::Vector &pos2, Mth::Vector *p_point, Obj::CRailNode **pp_rail_node, const Obj::CRailNode *p_ignore_node = NULL, float min_dot = 1.0f, int side = 0);
	
	void 			SetActive( int node, int active, bool wholeRail );
	bool 			IsActive( int node );
	bool			IsMoving(){return m_is_transformed;}

	void 			MoveNode( int node, Mth::Vector &pos );

	
	void			DebugRender(Mth::Matrix *p_transform = NULL);
	void			RemoveOverlapping();
	
	int				GetNumNodes(){return m_num_nodes;}
	
	Script::CArray *	GetNodeArray(){return mp_node_array;}
	void				SetNodeArray(Script::CArray *p_nodeArray){mp_node_array = p_nodeArray;}

	void			NewLink(CRailNode *p_from, CRailNode *p_to);

	void			AutoGenerateRails(Script::CStruct* pParams);

	CRailNode*		GetRailNodeByNodeNumber( int node_num );
	
	int				GetNodeIndex(const CRailNode *p_node) {return (((int)(p_node)-(int(mp_nodes)))/sizeof(CRailNode));}
	
// accessor functions for the rail node
// we only access their positions via the rail manager
// as only the rail manager knows the transform of the object to
// which they are attached
// These nodes are "instances", in that the same node might be used in
// more than one place
// so these functions only make sense in conjunction with the rail_manager
	Mth::Vector LocalToWorldTransform ( const Mth::Vector& vector ) const;
	Mth::Vector	GetPos(const CRailNode *p_node) 	const;	 
    Mth::Matrix GetMatrix(const CRailNode *p_node) 	const;
	Mth::Vector	GetBBMin(const CRailNode *p_node)   const;
	Mth::Vector	GetBBMax(const CRailNode *p_node)   const;


private:
//	CRailNode*  		mp_first_node;			// was a pointer to a list of nodes
	Mth::Matrix			m_transform;
	CRailNode*  		mp_nodes;				// pointer to array of nodes
	CRailLinks*			mp_links;				// pointer to temp array of links
	Script::CArray *	mp_node_array;
	int					m_num_nodes;
	int					m_current_node;
	bool				m_is_transformed;
	
};



// Global rail type functions that don't need a manager

bool  			Rail_ValidInEditor(Mth::Vector Start, Mth::Vector End);
 
inline const Mth::Vector&	CRailNode::GetPos() const
{
	return m_pos;
}

inline const Mth::Quat&	CRailNode::GetOrientation() const
{
	return m_orientation;
}

inline const Mth::Vector&	CRailNode::GetBBMin() const
{
	return m_BBMin;
}

inline const Mth::Vector&	CRailNode::GetBBMax()  const
{
	return m_BBMax;
}

} 

			
#endif			// #ifndef __OBJECTS_RAIL_H
			




================================================
FILE: Code/Sk/Objects/records.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Objects	(Record)										**
**																			**
**	File name:		objects/Record.cpp										**
**																			**
**	Created: 		7/11/01	- Mick											**
**																			**
**	Description:	Record Handling, high score tables and suchlike			**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include  
#include  
#include  

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Records
{
	

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

		enum {
					vNumHighScores = 5,
					vNumBestCombos = 5,
			 };

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/




/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CInitials::Set(const char *initials) // K: Had to add a const so I could pass it a const got from GetText
{
	

	m_initials[0] = initials[0];
	m_initials[1] = initials[1];
	m_initials[2] = initials[2];
	m_initials[3] = 0;

}

char * CInitials::Get(void)
{
	

	return m_initials;			// note, returning a pointer

}



CRecord::CRecord()
{
	
	
	mp_initials = new CInitials();
	
	Set("XXX",1000,0); 			// set a default.  Will not hurt anything
	
}

CRecord::~CRecord()
{
	
	
	delete mp_initials;
}


// SetInitials will copy in the first three digits of the string   
void	CRecord::SetInitials(char *initials)
{
	
	mp_initials->Set(initials);

}

void	CRecord::SetValue(int value)
{
	
	m_value = value;

}

void	CRecord::SetNumber(int number)
{
	
	m_number = number;

}

void	CRecord::SetNewRecord(bool new_record)
{
	
	m_new_record = new_record;

}


void	CRecord::Set(char *initials, int value, int number)
{
	
	
	SetInitials(initials);
	SetValue(value);
	SetNumber(number);
}

void	CRecord::Set(CRecord *pRecord)
{
	
	
	SetInitials(pRecord->GetInitials());
	SetValue(pRecord->GetValue());
	SetNumber(pRecord->GetNumber());
}


char *	CRecord::GetInitials(void)	// note, returns a pointer to the initials, not the initials itself
{
	
	return mp_initials->Get();
}

int	   CRecord::GetValue(void)
{
	
	
	return m_value;
}

int	   CRecord::GetNumber(void)
{
	
	
	return m_number;

}

bool	   CRecord::GetNewRecord(void)
{
	
	
	return m_new_record;

}


bool  CRecord::BeatRecord(int value)		
{
	
	
	return value > GetValue();
}

bool  CRecord::MaybeNewRecord(int value, int number)		
{
	
	
	if (BeatRecord(value))
	{
		SetValue(value);
		SetNumber(number);
		SetNewRecord(true);
		return true;
	}
	else
	{
		SetNewRecord(false);
		return false;
	}
}


// if this was flagged as a new record, then set it to these initials
void 	CRecord::UpdateInitials(CInitials* pInitials)
{
	if (GetNewRecord())
	{
//		SetNewRecord(false);
		mp_initials->Set(pInitials->Get());
	}
}



void CRecord::WriteIntoStructure(Script::CStruct *pIn)
{
	

	Dbg_MsgAssert(pIn,("NULL pIn sent to CRecord::WriteIntoStructure"));

	pIn->AddComponent(Script::GenerateCRC("Value"),ESYMBOLTYPE_INTEGER,m_value);
	pIn->AddComponent(Script::GenerateCRC("Number"),ESYMBOLTYPE_INTEGER,m_number);
	Dbg_MsgAssert(mp_initials,("NULL mp_initials"));
	pIn->AddComponent(Script::GenerateCRC("Initials"),ESYMBOLTYPE_STRING,mp_initials->Get());
}

void CRecord::ReadFromStructure(Script::CStruct *pIn)
{
	
	Dbg_MsgAssert(pIn,("NULL pIn sent to CRecord::ReadFromStructure"));
	
	if (!pIn->GetInteger("Value",&m_value))
	{
		Dbg_MsgAssert(0,("Structure is missing Value parameter"));
	}	
	if (!pIn->GetInteger("Number",&m_number))
	{
		Dbg_MsgAssert(0,("Structure is missing Number parameter"));
	}	
	
	const char *pInitials="XXX";
	if (!pIn->GetText("Initials",&pInitials))
	{
		Dbg_MsgAssert(0,("Structure is missing Initials parameter"));
	}
	Dbg_MsgAssert(mp_initials,("NULL mp_initials"));
	mp_initials->Set(pInitials);
}



///////////////////////////////////////////////////////////////////////////////
// CRecordTable, a table of records, like a high score table
CRecordTable::CRecordTable(int num_records)
{
	
	
	m_numRecords = num_records;
	mp_records = new CRecord[m_numRecords];
}
			
CRecordTable::~CRecordTable()
{
	

	delete [] mp_records;
	
}

CRecord*   CRecordTable::GetRecord(int line)
{
	
	
	Dbg_MsgAssert(line < m_numRecords,("Bad Line %d in recordtable",line));
	return &mp_records[line];
}

int			CRecordTable::GetSize(void)
{
	
	
	return m_numRecords;	
}


void 	CRecordTable::UpdateInitials(CInitials* pInitials)
{
	for (int line= 0; line < m_numRecords; line++)
	{
		mp_records[line].UpdateInitials(pInitials);
	}	
}

// given a new "value", then insert it into the table in the position
// that matches this value, and move other entries down (losing the final entry)
// returns the entery number (fisr tline is 0)
// or -1 if it does not make the cut.
int			CRecordTable::MaybeNewEntry(int value, int number)
{
	

	int line;
	
	for (line= 0; line < m_numRecords; line++)
	{
		mp_records[line].SetNewRecord(false);
	}

	for (line= 0; line < m_numRecords; line++)
	{
		if (value > mp_records[line].GetValue())
		{
			// alright! this value belongs at "line"
			// so maove down any that are below this line
			// and insert this one
			for (int dest = m_numRecords - 1; dest > line; dest--)
			{
				mp_records[dest].Set(&mp_records[dest-1]);
			}
			mp_records[line].SetValue(value);	
			mp_records[line].SetNumber(number);	
			mp_records[line].SetInitials("XXX");	
			mp_records[line].SetNewRecord(true);
			return line;		
		}
	}

	return -1;
}


			
void 		CRecordTable::ReadFromStructure(Script::CStruct *pIn)
{
	

	Dbg_MsgAssert(pIn, ("NULL pIn sent to CRecordTable::ReadFromStructure"));
	Script::CArray *pArray=NULL;
	pIn->GetArray("RecordTable",&pArray);
	Dbg_MsgAssert(pArray,("Structure is missing RecordTable parameter"));
	
	if (m_numRecords!=(int)pArray->GetSize())
	{
		Dbg_Warning("m_numRecords mismatch: m_numRecords=%d, but num records in structure=%d",m_numRecords,pArray->GetSize());
		return;
	}
	Dbg_MsgAssert(mp_records,("NULL mp_records"));
		
	for (int i=0; iGetStructure(i));
	}	
}
			
void 		CRecordTable::WriteIntoStructure(Script::CStruct *pIn)
{
	

	Dbg_MsgAssert(pIn, ("NULL pIn sent to CRecordTable::WriteIntoStructure"));
	
	Script::CArray *pArray=new Script::CArray;
	pArray->SetArrayType(m_numRecords,ESYMBOLTYPE_STRUCTURE);

	Dbg_MsgAssert(mp_records,("NULL mp_records"));
	for (int i=0; iSetStructure(i,pStruct);
	}

	pIn->AddComponent(Script::GenerateCRC("RecordTable"),ESYMBOLTYPE_ARRAY,(int)pArray);
}
			


///////////////////////////////////////////////////////////////////////////////
// CLevelRecords - Records for a particualr level (Skate3 specific)
CLevelRecords::CLevelRecords(/*int numHighScores, int numBestCombos*/)
{
	
	
	
   	mp_highScores = new CRecordTable(vNumHighScores);
	
   	mp_bestCombos = new CRecordTable(vNumBestCombos);
   	mp_longestCombo = new CRecord;
	mp_longestCombo->SetValue(0);
   	mp_longestGrind = new CRecord;
	mp_longestGrind->SetValue(0);
   	mp_longestLipTrick = new CRecord;
	mp_longestLipTrick->SetValue(0);
   	mp_longestManual = new CRecord;
	mp_longestManual->SetValue(0);
	
	
}
			
CLevelRecords::~CLevelRecords()
{
	
	delete mp_longestManual;
	delete mp_longestLipTrick;
	delete mp_longestGrind;
	delete mp_longestCombo;
	delete mp_bestCombos;
	delete mp_highScores;
}


void		CLevelRecords::UpdateInitials(CInitials* pInitials)
{
	mp_longestManual->UpdateInitials(pInitials);
	mp_longestLipTrick->UpdateInitials(pInitials);
	mp_longestGrind->UpdateInitials(pInitials);
	mp_longestCombo->UpdateInitials(pInitials);
	mp_bestCombos->UpdateInitials(pInitials);
	mp_highScores->UpdateInitials(pInitials);
	
}
 


			
void		CLevelRecords::ReadFromStructure(Script::CStruct *pIn)
{
	

	Dbg_MsgAssert(pIn,("NULL pIn sent to CLevelRecords::ReadFromStructure"));

	Script::CStruct *pStruct=NULL;
	
	pIn->GetStructure("HighScores",&pStruct);
	Dbg_MsgAssert(pStruct,("Structure is missing HighScores"));
   	Dbg_MsgAssert(mp_highScores,("NULL mp_highScores"));
	mp_highScores->ReadFromStructure(pStruct);

	pIn->GetStructure("BestCombos",&pStruct);
	Dbg_MsgAssert(pStruct,("Structure is missing BestCombos"));
   	Dbg_MsgAssert(mp_bestCombos,("NULL mp_bestCombos"));
	mp_bestCombos->ReadFromStructure(pStruct);

	pIn->GetStructure("LongestCombo",&pStruct);
	Dbg_MsgAssert(pStruct,("Structure is missing LongestCombo"));
   	Dbg_MsgAssert(mp_longestCombo,("NULL mp_longestCombo"));
	mp_longestCombo->ReadFromStructure(pStruct);

	pIn->GetStructure("LongestGrind",&pStruct);
	Dbg_MsgAssert(pStruct,("Structure is missing LongestGrind"));
   	Dbg_MsgAssert(mp_longestGrind,("NULL mp_longestGrind"));
	mp_longestGrind->ReadFromStructure(pStruct);

	pIn->GetStructure("LongestLipTrick",&pStruct);
	Dbg_MsgAssert(pStruct,("Structure is missing LongestLipTrick"));
   	Dbg_MsgAssert(mp_longestLipTrick,("NULL mp_longestLipTrick"));
	mp_longestLipTrick->ReadFromStructure(pStruct);

	pIn->GetStructure("LongestManual",&pStruct);
	Dbg_MsgAssert(pStruct,("Structure is missing LongestManual"));
   	Dbg_MsgAssert(mp_longestManual,("NULL mp_longestManual"));
	mp_longestManual->ReadFromStructure(pStruct);
}
			
void		CLevelRecords::WriteIntoStructure(Script::CStruct *pIn)
{
	
	
	Dbg_MsgAssert(pIn,("NULL pIn sent to CLevelRecords::WriteIntoStructure"));
	

	Script::CStruct *pStruct=new Script::CStruct;
   	Dbg_MsgAssert(mp_highScores,("NULL mp_highScores"));
	mp_highScores->WriteIntoStructure(pStruct);
	pIn->AddComponent(Script::GenerateCRC("HighScores"),ESYMBOLTYPE_STRUCTUREPOINTER,(int)pStruct);
	
	pStruct=new Script::CStruct;
   	Dbg_MsgAssert(mp_bestCombos,("NULL mp_bestCombos"));
	mp_bestCombos->WriteIntoStructure(pStruct);
	pIn->AddComponent(Script::GenerateCRC("BestCombos"),ESYMBOLTYPE_STRUCTUREPOINTER,(int)pStruct);
	
	pStruct=new Script::CStruct;
   	Dbg_MsgAssert(mp_longestCombo,("NULL mp_longestCombo"));
	mp_longestCombo->WriteIntoStructure(pStruct);
	pIn->AddComponent(Script::GenerateCRC("LongestCombo"),ESYMBOLTYPE_STRUCTUREPOINTER,(int)pStruct);
	
	pStruct=new Script::CStruct;
   	Dbg_MsgAssert(mp_longestGrind,("NULL mp_longestGrind"));
	mp_longestGrind->WriteIntoStructure(pStruct);
	pIn->AddComponent(Script::GenerateCRC("LongestGrind"),ESYMBOLTYPE_STRUCTUREPOINTER,(int)pStruct);
	
	pStruct=new Script::CStruct;
   	Dbg_MsgAssert(mp_longestLipTrick,("NULL mp_longestLipTrick"));
	mp_longestLipTrick->WriteIntoStructure(pStruct);
	pIn->AddComponent(Script::GenerateCRC("LongestLipTrick"),ESYMBOLTYPE_STRUCTUREPOINTER,(int)pStruct);
   	
	pStruct=new Script::CStruct;
	Dbg_MsgAssert(mp_longestManual,("NULL mp_longestManual"));
	mp_longestManual->WriteIntoStructure(pStruct);
	pIn->AddComponent(Script::GenerateCRC("LongestManual"),ESYMBOLTYPE_STRUCTUREPOINTER,(int)pStruct);
}
			


///////////////////////////////////////////////////////////////////////////////
// CGameRecords - Records for the whole game

CGameRecords::CGameRecords(int numLevels /*, int numHighScores, int numBestCombos*/)
{
	
	m_numLevels = numLevels;

	// VC++ complains about constructing an array with parameters
	// so I (Mick) changed it to use and enum for those values,
	// as they are not likely to change anyway  
	mp_levelRecords = new CLevelRecords[numLevels]; /*(numHighScores,numBestCombos)*/
	mp_defaultInitials = new CInitials;
	mp_defaultInitials->Set("ABC");
}
			
CGameRecords::~CGameRecords()
{
	
	delete [] mp_levelRecords;
	delete mp_defaultInitials;
	
}


CInitials * CGameRecords::GetDefaultInitials(void)
{
	

	return mp_defaultInitials;
}

void CGameRecords::SetDefaultInitials(const char *p_initials)
{
	

	mp_defaultInitials->Set( p_initials );
}



CLevelRecords * CGameRecords::GetLevelRecords(int level)
{
	
	Dbg_MsgAssert(level>=0 && level <= m_numLevels,("bad level number %d (max %d)\n",level,m_numLevels));
	
	return &mp_levelRecords[level];
}

			
void 		CGameRecords::ReadFromStructure(Script::CStruct *pIn)
{
	

	Dbg_MsgAssert(pIn, ("NULL pIn sent to CGameRecords::ReadFromStructure"));	

	Script::CArray *pArray=NULL;
	pIn->GetArray("GameRecords",&pArray);
	Dbg_MsgAssert(pArray,("Structure is missing GameRecords parameter"));
	
	if (m_numLevels!=(int)pArray->GetSize())
	{
		Dbg_Warning("m_numLevels mismatch: m_numLevels=%d, but num levels in structure=%d",m_numLevels,pArray->GetSize());
		return;
	}
	Dbg_MsgAssert(mp_levelRecords,("NULL mp_levelRecords"));
		
	for (int i=0; iGetStructure(i));
	}	
	
	const char *pDefaultInitials="ABC";
	pIn->GetText("DefaultInitials",&pDefaultInitials);
	Dbg_MsgAssert(mp_defaultInitials,("NULL mp_defaultInitials"));
	mp_defaultInitials->Set(pDefaultInitials);
	
}
			
void 		CGameRecords::WriteIntoStructure(Script::CStruct *pIn)
{
	
	Dbg_MsgAssert(pIn, ("NULL pIn sent to CGameRecords::WriteIntoStructure"));	
	
	Script::CArray *pArray=new Script::CArray;
	pArray->SetArrayType(m_numLevels,ESYMBOLTYPE_STRUCTURE);

	Dbg_MsgAssert(mp_levelRecords,("NULL mp_levelRecords"));
	for (int i=0; iSetStructure(i,pStruct);
	}

	pIn->AddComponent(Script::GenerateCRC("GameRecords"),ESYMBOLTYPE_ARRAY,(int)pArray);
	pIn->AddComponent(Script::GenerateCRC("DefaultInitials"),ESYMBOLTYPE_STRING,mp_defaultInitials->Get());
}
			






} // namespace Records




================================================
FILE: Code/Sk/Objects/records.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Object (OBJ)											**
**																			**
**	File name:		objects/records.h									   	**
**																			**
**	Created: 		11/7/01 - Mick 											**
**																			**
*****************************************************************************/
// Notes:
// as a bit of an experiment, I'm writing the whole structure using 
// dynamic containers,
// so each data type is more independent of the others
// but the constructor (and destructor) of the higher level types
// will have to create (and destroy) the dynamic instances of the others
//
// this gives us benefits, namely:
//   No cross includes needed between header files if we split into seperate classes
//   Number of elements in arrays (for high score tables) can be decided at run time.
//
// But has drawback:
//   Uses more memory, as each element is allocated individually
//   might be slower?
//   more code?  Dunno, that's why I'm experimenting....


#ifndef __OBJECTS_RECORDS_H
#define __OBJECTS_RECORDS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif


namespace Script
{
	class CStruct;
};

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Records
{

/*****************************************************************************
**							Forward Declarations						**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class  CInitials : public Spt::Class
{
	

	public:
		char *	Get(void);
		void	Set(const char *initials);				
	
	private:
		char 	m_initials[4];
		
};
				
class  CRecord  : public Spt::Class
{
	


	public:
								CRecord();
								~CRecord();
		
		// for memory card loading/saving
		void 						ReadFromStructure(Script::CStruct *pIn);
		void 						WriteIntoStructure(Script::CStruct *pIn);

		void						Set(char *initials, int value, int number);
		void						Set(CRecord *pRecord);
		
		void						SetInitials(char *initials);
		void						SetValue(int value);
		void						SetNumber(int number);
		void						SetNewRecord(bool new_record);
		
		char 	*					GetInitials(void);	// note, returns a pointer to the initials, not the initials itself
		int							GetValue(void);
		int							GetNumber(void);
		bool						GetNewRecord(void);	

		// Return true if we beat the record		
		bool						BeatRecord(int value);		

		// set the record and return true if we beat it
		bool						MaybeNewRecord(int value, int number);
		void 						UpdateInitials(CInitials* pInitials);
		


	private:
		CInitials		*			mp_initials;		// Initials
		int							m_value;		 	// 
		int							m_number;			// pro skater number (or could be used for anything)
		bool						m_new_record;		// set if it's a new record
};


class  CRecordTable  : public Spt::Class
{
	public:
									CRecordTable(int num_records);
									~CRecordTable();
		CRecord*   					GetRecord(int line);
		int		   					GetSize(void);
		int							MaybeNewEntry(int value, int number);
		// for memory card loading/saving
		void 						ReadFromStructure(Script::CStruct *pIn);
		void 						WriteIntoStructure(Script::CStruct *pIn);
		void 						UpdateInitials(CInitials* pInitials);

	private:		
		int							m_numRecords;		// Number of records
		CRecord		*				mp_records;			// pointer to records					
};


// CLevelRecords - Records for a particualr level
// Include table of high scores, and combo scores
// and individual records for other things like longest grind 
class  CLevelRecords  : public Spt::Class
{
	public:
									CLevelRecords(/*int numHighScores, int numBestCombos*/);
									~CLevelRecords();
		
		void 						UpdateInitials(CInitials* pInitials);
		
		// for memory card loading/saving
		void 						ReadFromStructure(Script::CStruct *pIn);
		void 						WriteIntoStructure(Script::CStruct *pIn);
	
	private:
		CRecordTable		*		mp_highScores;
		CRecordTable		*		mp_bestCombos;
		CRecord				*		mp_longestGrind;
		CRecord				*		mp_longestManual;
		CRecord				*		mp_longestLipTrick;
		CRecord				*		mp_longestCombo;	
	public:
		CRecordTable		*		GetHighScores()      {return mp_highScores;}        
		CRecordTable		*		GetBestCombos()      {return mp_bestCombos;}        
		CRecord				*		GetLongestGrind()    {return mp_longestGrind;}      
		CRecord				*		GetLongestManual()   {return mp_longestManual;}     
		CRecord				*		GetLongestLipTrick() {return mp_longestLipTrick;}   
		CRecord				*		GetLongestCombo()	 {return mp_longestCombo;}      
				
			
};

// Records for the whole game
// stores a single CLevelRecords for each level, in an array
// also stores the default initials used by the game records
								  
class CGameRecords : public Spt::Class
{
	public:
									CGameRecords(int numLevels /*, int numHighScores, int numBestCombos*/);
									~CGameRecords();
									
		CInitials 			* 		GetDefaultInitials(void);
		void				 		SetDefaultInitials(const char *p_initials);
		
		CLevelRecords 		*	 	GetLevelRecords(int level);
									
		// for memory card loading/saving
		void 						ReadFromStructure(Script::CStruct *pIn);
		void 						WriteIntoStructure(Script::CStruct *pIn);
	
	private:
		int							m_numLevels;
		CInitials			*		mp_defaultInitials;
		CLevelRecords		*		mp_levelRecords;
};



/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Records

#endif	// __OBJECTS_RECORDS_H



================================================
FILE: Code/Sk/Objects/restart.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:													**
**																			**
**	Module:						 								**
**																			**
**	File name:		restart.cpp											**
**																			**
**	Created by:		00/00/00	-	initials								**
**																			**
**	Description:			 									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Obj
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

// Return tru is this node is a restart node
bool IsRestart(Script::CScriptStructure *pNode)
{
	uint32	ClassChecksum;
	pNode->GetChecksum("Class",&ClassChecksum);
	if (ClassChecksum == 0x1806ddf8)					// checksum of "Restart"
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool IsRestart(int node)
{
	// get the node array	
	Script::CArray *pNodeArray=Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
	Script::CScriptStructure *pNode=pNodeArray->GetStructure(node);
	return	IsRestart(pNode);
}

int FindQualifyingNodes( uint32 type, uint32 *node_buffer, int first = -1 )
{
	int num_qualifiers;
	int i;

	num_qualifiers = 0;
	i = 0;
	
	// get the node array	
	Script::CArray *pNodeArray=Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));

	// if there is no node array, then there are no restart points
	if (!pNodeArray)
	{
		return 0;
	}
						 
	if( first >= 0 )
	{
		i = first + 1;
	}
	// scan through it from the start point
	while( i < (int)pNodeArray->GetSize() )
	{
		Script::CScriptStructure *pNode=pNodeArray->GetStructure(i);
		if (IsRestart(pNode))					// checksum of "Restart"
		{   
			Script::CArray *pArray;
			int j;

			// The caller wants all restart points
			if (type == 0)
			{
				node_buffer[num_qualifiers++] = i;
				i++;
				continue;
			}

			// Loop through supported game types, gathering matches
			if (pNode->GetArray("restart_types", &pArray))
			{
				for (j = 0; j < (int)pArray->GetSize(); j++)
				{
					uint32 game_type;
						
					game_type = pArray->GetNameChecksum( j );
					if( game_type == type )
					{
						node_buffer[num_qualifiers++] = i;
					}
				}
			}
		}		
		i++;	
	}	

	return num_qualifiers;
}


// given the checksum of a type of restart
// then get the node index  
// if "first" is specified then resturn the first on after this								  
// if "type"  is zero, then it will be ignored, and will return restarts of any type.
// returns -1 if no restarts matching "type" are found.
int GetRestartNode(uint32 type, int index, int first = -1)
{
	// decide where to start, based on the "first" parameter
	int num_qualifiers;
	uint32 qualifying_nodes[256];
	
	num_qualifiers = FindQualifyingNodes( type, qualifying_nodes, first );
	if( num_qualifiers == 0 )
	{
		return -1;
	}
	
	#ifdef	__NOPT_ASSERT__
	if (num_qualifiers > 1 && (type == CRCD(0xbae0e4da,"multiplayer") || type == CRCD(0x9d65d0e7,"horse")))
	{
		// Scan through the nodes we've found, and check that there are not any
		// that are too close to each other (< 6 feet)
		bool ok = true;
		for (int i=0;iGetChecksum("Name",&i_name_checksum);
					uint32	j_name_checksum;
					SkateScript::GetNode(qualifying_nodes[j])->GetChecksum("Name",&j_name_checksum);
					printf ("%2.2f"" %s to %s \n",
					 dist,
					 Script::FindChecksumName(i_name_checksum),
					 Script::FindChecksumName(j_name_checksum)
					 );
				}
			}
		}
		if (!ok) printf ("\n\n\n");
		Dbg_MsgAssert(ok, ("Restart nodes of type %s too close, see above for list\n",Script::FindChecksumName(type)));
	}
	#endif	
	
	//Ryan("index is %d, num_qualifiers %d\n", index, num_qualifiers);
	
	// If we've found matches, return the 
	if( num_qualifiers > 0 )
	{
		if( index >= num_qualifiers )
		{
			return qualifying_nodes[index % num_qualifiers];
		}
		else
		{
			return qualifying_nodes[index];
		}
	}
	return -1;
}
				
				
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj






================================================
FILE: Code/Sk/Objects/restart.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate3													**
**																			**
**	Module:			obj						 								**
**																			**
**	File name:		tmeplate.h  											**
**																			**
**	Created by:		00/00/00	-	mick									**
**																			**
*****************************************************************************/

#ifndef __SK_OBJECTS_RESTART_H
#define __SK_OBJECTS_RESTART_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Obj
{

						


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

bool IsRestart(Script::CScriptStructure pNode);
bool IsRestart(int node);
int GetRestartNode(uint32 type, int index, int first = -1);
int FindQualifyingNodes( uint32 type, uint32 *node_buffer, int first = -1 );


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj

#endif	// __DIR_SUBDIR_FILE_H




================================================
FILE: Code/Sk/Objects/skater.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**							    											**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Objects (OBJ) 											**
**																			**
**	File name:		skater.cpp												**
**																			**
**	Created:		01/25/00												**
**																			**
**	Description:	Skater													**
**															
****************************************************************************/
  								 									  
// start autoduck documentation
// @DOC skater        																	  
// @module skater | None
// @subindex Scripting Database
// @index script | skater

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#ifdef TESTING_GUNSLINGER
#include 
#include 
#endif


#include 
#include 
#include 				// for debug lines
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 						// for sGetMovableObjects
#include 

                                   
#include  // for SKATE_TYPE_*
#include 
#include 	  	// for CCompetition
#include 
#include    // for special goal
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 

#include 

#include 
#include 

#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef __PLAT_NGC__
#include "gfx/ngc/nx/nx_init.h"
#endif		// __PLAT_NGC__


#define	FLAGEXCEPTION(X)	SelfEvent(X)


////////////////////////////////////////////////////////////////////////

inline void	dodgy_test()
{
//		 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
//		if (skate_mod->GetSkater(0))
//		{
//			skate_mod->GetSkater(0)->DodgyTest();
//		}
}

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

extern uint64	Gfx_LastVBlank;

namespace Gfx
{
	void DebuggeryLines_CleanUp( void );		// just for debugging
}

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

// All the CSkater code sits in the Obj namespace, along with other objects,
// like peds and cars
namespace Obj
{

bool DebugSkaterScripts=false;			// Set to true to dump out a lot of info about the skater's script
										// generally you would set it with the script command SkaterDebugOn/Off

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/////////////////////////////////////////////////////////////////////////////
// Physics patching, so physics vars can come from different structs
// or globals, in a nice relaxed manner

Script::CStruct * get_physics_struct()
{
	// if (Script::GetInteger(CRCD(0x9e86d1c1,"use_bike_physics")))
	// {
		// return Script::GetStructure(CRCD(0x9d728121,"bike_physics"));
	// }
	// else if (Script::GetInteger(CRCD(0x9ae06a83,"use_walk_physics")))
	// {
		// return Script::GetStructure(CRCD(0x8cbc89dd,"skater_physics"));
	// }
	// else
	// {
		return Script::GetStructure(CRCD(0x8cbc89dd,"skater_physics"));
	// }
}
			  
float GetPhysicsFloat(uint32 id, Script::EAssertType assert)
{

	Script::CStruct	*p_struct = get_physics_struct();	
	float x;
	if (p_struct->GetFloat(id,&x))
	{
		return x;
	}

	return Script::GetFloat(id,assert);
}

int GetPhysicsInt(uint32 id, Script::EAssertType assert)
{
	
	Script::CStruct	*p_struct = get_physics_struct();	
	int x;
	if (p_struct->GetInteger(id,&x))
	{
		return x;
	}

	return Script::GetInteger(id,assert);
}

// End of physics patching
/////////////////////////////////////////////////////////////////////////////


// This Test generally not called
// can stick code in here that will be called from dodgy_test
// whcih gets called from every printf
// generaly you'd have printfs enabled via something like DEBUG_POSITION
void 	CSkater::DodgyTest()
{
		CFeeler feeler;
		feeler.SetEnd(m_pos);
		feeler.SetStart(m_pos + Mth::Vector(0.0f,100.0f,0.0f,0.0f));
		if (feeler.GetCollision())
		{
#	if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
			float above = feeler.GetPoint()[Y] - m_pos[Y];
			printf(" collision %.2f above me, normal = (%.2f,%.2f,%.2f)\n",above,feeler.GetNormal()[X],feeler.GetNormal()[Y],feeler.GetNormal()[Z]);
#endif		// __NOPT_FINAL__
		}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// K: This is a factored out code snippet that is used quite a bit.
// It get's a pointer to this skater's score object and asserts if it's NULL.
Mdl::Score *CSkater::GetScoreObject()
{
	Dbg_Assert(mp_skater_score_component);
	return mp_skater_score_component->GetScore();
}	

// Just move the skater to the correct position and orientation for
// this restart point
// used to set intial position of other skaters on clients
void CSkater::MoveToRestart(int node)
{
	Dbg_Assert(mp_skater_core_physics_component);
	
	m_matrix.Ident();
	Script::CStruct *pNode = NULL;
	if (node >= 0)
	{
		Script::CArray *pNodeArray=Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
		pNode=pNodeArray->GetStructure(node);
		Dbg_MsgAssert(pNode,( "null restart"));
		SkateScript::GetPosition( node, &m_pos );
		#ifdef	CHEAT_RUBBER
		m_rubber_pos = m_pos;
		m_rubber_vel.Set();
		#endif
		
		m_old_pos = m_pos;
		mp_skater_core_physics_component->m_safe_pos = m_pos;
		
		SetTeleported();

		Mth::Vector angles;
		SkateScript::GetAngles(pNode, &angles);
		m_matrix.SetFromAngles(angles);
		
		mp_skater_core_physics_component->ResetLerpingMatrix();
		mp_skater_core_physics_component->m_display_normal = mp_skater_core_physics_component->m_last_display_normal = mp_skater_core_physics_component->m_current_normal = m_matrix[Y];
		
		#ifdef		DEBUG_DISPLAY_MATRIX
		dodgy_test(); printf("%d: Setting display_matrix[Y][Y] to %f, [X][X] to %f\n",__LINE__,mp_skater_core_physics_component->m_lerping_display_matrix[Y][Y],mp_skater_core_physics_component->m_lerping_display_matrix[X][X]);
		#endif
	
		// set the shadow to stick to his feet, assuming we are on the ground
		// Note: These are used by the simple shadow, not the detailed one.
		Dbg_Assert(mp_shadow_component)
		mp_shadow_component->SetShadowPos( m_pos );
		mp_shadow_component->SetShadowNormal( m_matrix[Y] );
	
		// update for the camera
		m_display_matrix = mp_skater_core_physics_component->m_lerping_display_matrix;
	}
}

void CSkater::SkipToRestart(int node, bool walk)
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

#	ifdef	DEBUG_TRIGGERS			
	dodgy_test(); printf("SkipToRestart(%d)\n",node);
#	endif

    GetSkaterPhysicsControlComponent()->NotifyReset();
	
	if( m_local_client )
	{
		// The Skater might have been in the middle of a move that disabled his input, so re-enable it
		// and also enable the camera control
		mp_input_component->EnableInput();
		// Also player should ot be paused
		UnPause();

        if (mp_stats_manager_component)
        {
            mp_stats_manager_component->m_restarted_this_frame = true;
        }

        /*		
        // If the player had locked the camera in lookaround, clear it back.
		if ( mp_skaterCamera )
		{
			mp_skaterCamera->ClearLookaroundLock();
		}
		*/
		//printf ("STUBBED: Skater.cpp line %d - not clearing lookaroundlock\n",__LINE__);

		// Reset the skater back to a collidable state
		if( gamenet_man->InNetGame())
		{   
			CFuncs::ScriptNotifyBailDone( NULL, NULL );
			mp_skater_core_physics_component->SetFlagFalse( IS_BAILING );
		}
	}

    if (mp_vibration_component)
	{
		mp_vibration_component->Reset();
	}

	if (node != -1)
	{
		MoveToRestart(node);
	}
	       
	ResetPhysics(walk);

	dodgy_test(); //printf("Setting animation flipped to %d in SkipToRestart\n",mp_skater_core_physics_component->GetFlag(FLIPPED));
	
	if (IsLocalClient())
	{
		// reset any flippedness of animation
		GetSkaterFlipAndRotateComponent()->ApplyFlipState();
	}

	// Only local skaters should run the script
	if( m_local_client )
	{
		if (!m_viewing_mode)
		{
			UpdateCameras( true );
			
			Obj::CCompositeObject *p_obj = GetCamera();
			if (p_obj)
			{
				p_obj->Update();	 // Not the best way of doing it...
			}

			
		}
		if (node != -1)
		{
			Script::CStruct *pNode = NULL;	
			Script::CArray *pNodeArray=Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
			pNode=pNodeArray->GetStructure(node);
			// Now run any TriggerScript associated with this node
			uint32 script;
			if (pNode->GetChecksum(0x2ca8a299,&script))
			{
	#			ifdef	DEBUG_TRIGGERS			
				dodgy_test(); printf("%d: Restart Node Script triggered, script name %s, node %d\n",(int)Tmr::GetVblanks(),(char*)Script::FindChecksumName(script),node);
	#			endif			
				SpawnAndRunScript(script,node);
			}
		}	  	
	}
			
	SetTeleported();

	if(	( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0x6c5ff266,"netctf") ) ||
		( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0x6ef8fda0,"netking") ) ||
		( skate_mod->GetGameMode()->GetNameChecksum() == CRCD(0x5d32129c,"king") ))
	{
		Net::Client* client;
		GameNet::PlayerInfo* player;
		Net::MsgDesc msg_desc;

		client = gamenet_man->GetClient( m_skater_number );
		Dbg_Assert( client );
		player = gamenet_man->GetPlayerByObjectID( GetID());
		Dbg_Assert( player );
		player->MarkAsRestarting();

		msg_desc.m_Id = GameNet::MSG_ID_MOVED_TO_RESTART;
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
		client->EnqueueMessageToServer( &msg_desc );
	}

}

//////////////////////////////////////////////////////////////////////////
// START of "Stat" code


float	CSkater::GetStat(EStat stat)
{
	

	float value;			   

	float	override;
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	override = skate_mod->GetStatOverride();
	
	if (override != 0.0f)
	{
		value = override;
	}
	else
	{
		if (m_stat[stat] == -1.0f)
		{
			value =  GetPhysicsFloat(CRCD(0x6357d57c,"Skater_Default_Stats"));
		}
		else
		{
			value =  m_stat[stat];
		}
	}

	// In a network game, always set stats to 10	
//	if( GameNet::Manager::Instance()->InNetGame() )
//	{
//		value = 10.0f;
//	}

	if( GameNet::Manager::Instance()->InNetGame())
	{
		GameNet::PlayerInfo* player;

		player = GameNet::Manager::Instance()->GetPlayerByObjectID( GetID());
		if( player )
		{
			if( player->IsKing())
			{
				value = 1.0f;
			}
			else if( player->HasCTFFlag())
			{
				value = 6.0f;
			}
			else
			{
				value = 10.0f;
			}
		}
		else
		{
			value = 10.0f;
		}
	}
	else if( CFuncs::ScriptInSplitScreenGame( NULL, NULL ))
	{
		GameNet::PlayerInfo* player;

		player = GameNet::Manager::Instance()->GetPlayerByObjectID( GetID());
		if( player )
		{
			if( player->IsKing())
			{
				value = 1.0f;
			}
		}
	}

	if( GameNet::Manager::Instance()->InNetGame())
	{
		GameNet::PlayerInfo* player;

		player = GameNet::Manager::Instance()->GetPlayerByObjectID( GetID());
		if( player )
		{
			if( player->IsKing())
			{
				value = 1.0f;
			}
			else if( player->HasCTFFlag())
			{
				value = 6.0f;
			}
			else
			{
				value = 10.0f;
			}
		}
		else
		{
			value = 10.0f;
		}
	}
	else if( CFuncs::ScriptInSplitScreenGame( NULL, NULL ))
	{
		GameNet::PlayerInfo* player;

		player = GameNet::Manager::Instance()->GetPlayerByObjectID( GetID());
		if( player )
		{
			if( player->IsKing())
			{
				value = 1.0f;
			}
		}
	}

	if (CHEAT_STATS_13)
	{
		value = 15.0f;
	}

	Dbg_Assert(mp_skater_score_component);
	if (mp_skater_score_component->GetScore()->GetSpecialState())
	{
		value += 3.0f;
	}

	return value;
	
}


void	CSkater::SetStat(EStat stat, float value)
{

	// factor in the handicap if any...
	if ( CFuncs::ScriptInSplitScreenGame( NULL, NULL ) )
	{
		 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
		value += skate_mod->GetHandicap( m_id );

		// clamp it to the min, max values
		if ( value > 10.0f ) value = 10.0f;
		if ( value < 0.0f ) value = 0.0f;
	}
	
	m_stat[stat] = value;
}


// Gets a stat value given a pointer to the stat structure.
float	CSkater::GetScriptedStat(Script::CStruct *pSS)
{
	
	Script::CPair range;
	Script::CPair switch_range;
	int stat;
	float limit;

    Dbg_MsgAssert(pSS,("NULL pSS sent to GetScriptedStat"));
	
	if (!pSS->GetPair(NONAME,&range))
	{
		Dbg_MsgAssert(0,("range pair not found in structure."));		
	}

	float stat_value;
	if (!pSS->GetInteger(NONAME,&stat))
	{
		stat_value = 10.0f;	   		// default to 10 if no stats
	}
	else
	{
		stat_value = GetStat((CSkater::EStat)stat);
	}
	
	float lower = range.mX;
	float upper = range.mY;  
	float len = upper - lower;
	float value = lower + (len * stat_value / 10);


	if (mp_skater_core_physics_component->IsSwitched())		// if skating in switch stance
	{
		if (pSS->GetPair(0x9016b4e7,&switch_range))	   		// switch
		{
			float	switch_stat = GetStat(STATS_SWITCH);   	// returns 0..13
			float 	mult = switch_range.mX + (switch_range.mY - switch_range.mX) * switch_stat/10.0f;
			
			if (mult <0.0f) mult = 0.0f;
			if (mult >1.0f) mult = 1.0f;		// this ensures we are never Better in switch

			value *= mult;			
			
		}
	}
	
	Game::GOAL_MANAGER_DIFFICULTY_LEVEL level = Mdl::Skate::Instance()->GetGoalManager()->GetDifficultyLevel();
	if (level != Game::GOAL_MANAGER_DIFFICULTY_MEDIUM)
	{
		Script::CPair diff;	
		if (pSS->GetPair(CRCD(0xba8fb854,"diff"),&diff))
		{
			if (level == Game::GOAL_MANAGER_DIFFICULTY_LOW)
			{
				value *= diff.mX;
			}
			else if (level == Game::GOAL_MANAGER_DIFFICULTY_HIGH)
			{
				value *= diff.mY;
			}
		}
	}	
	
	// if there is a limit, then clamp the value to it
	// accounting for the direction of the range, this may be a high limit
	// or a low limit, but applies to the value for higher values of stat_value.
	// so if lower > upper, then it's a low limit
	if (pSS->GetFloat(0x8069179f/*"limit"*/,&limit))
	{
		if (lower < upper)
		{
			if (value > limit)
			{
				value = limit;
			}
		}
		else
		{
			if (value < limit)
			{
				value = limit;
			}
		}
	}
	else
	{
		// there is no limit, so we do nothing.
	}
	
	return value;
}

// Gets a stat value given the name of the stat, where the stat is defined as a global structure.
float	CSkater::GetScriptedStat(const uint32 checksum)
{
	
   // Get the structure that contains the range, stat and limit
	Script::CStruct *pSS = NULL; 
	Script::CStruct *pPhysics; 
	// Try to get it from the skater/biker physics structure first
	pPhysics = get_physics_struct();
	if (! pPhysics->GetStructure(checksum,&pSS))
	{
		pSS= Script::GetStructure(checksum);
	}
	
	Dbg_MsgAssert(pSS, ("State %s not found", Script::FindChecksumName(checksum)));

	// These asserts are also in the other GetScriptedStat,
	// but included here too so that it can print the name of the structure.
	#ifdef __NOPT_ASSERT__
	Script::CPair range;
	if (!pSS->GetPair(NONAME,&range))
	{
		Dbg_MsgAssert(0,("range pair not found in %s.",Script::FindChecksumName(checksum)));		
	}

	int stat;
	if (!pSS->GetInteger(NONAME,&stat))
	{
		Dbg_MsgAssert(0,("stat not found in %s.",Script::FindChecksumName(checksum)));		
	}
	#endif
		
	return GetScriptedStat(pSS);
}

// Gets a stat value given the name of the stat, where the stat is defined as a global structure.


// Gets a stat value given the name of the stat within a structure pSetOfStats.
// This is used in manual.cpp.
// Different sets of control & wobble stats are stored in structures in physics.q
float CSkater::GetScriptedStat(uint32 Checksum, Script::CStruct *pSetOfStats)
{
	Dbg_MsgAssert(pSetOfStats,("NULL pSetOfStats."));
	Script::CStruct *pSS=NULL;
		pSetOfStats->GetStructure(Checksum,&pSS);
	Dbg_MsgAssert(pSS,("Could not find stat called %s in pSetOfStats",Script::FindChecksumName(Checksum)));
	
	return GetScriptedStat(pSS);
}


// END of "Stat" code
/////////////////////////////////////////////////////////////////////////////

void CSkater::MoveToRandomRestart( void )
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	int node, retry;
	
	

	// Try to go to a different restart point than last time.  But still, limit
	// the number of retries just in case there is only one restart node in the level
	retry = 0;
	do
	{
		node = skate_mod->find_restart_node( Mth::Rnd( Mdl::Skate::vMAX_SKATERS ));
		retry++;
	} while(( node == m_last_restart_node ) && ( retry < 20 ));

	m_last_restart_node = node;

	Dbg_Printf( "In MoveToRandomRestart: Node %d\n", node );
    
	if (node != -1)
	{
		SkipToRestart(node);   					   
	}
}

void CSkater::UpdateCameras( bool instantly )
{
//	printf ("%d: SUTUBBBEDDDDDDDDDDDDDDDDDDDDDDDD  UpdateCameras(%d)\n",__LINE__,instantly);	
	if (instantly)
	{
		SetTeleported( false );
	}
}

void CSkater::UpdateCamera( bool instantly )
{
//	printf ("%d: SUTUBBBEDDDDDDDDDDDDDDDDDDDDDDDD  UpdateCamera(%d)\n",__LINE__,instantly);	
	if (instantly)
	{
		SetTeleported( false );
	}
}

Gfx::Camera* CSkater::GetActiveCamera( void )
{

	return Nx::CViewportManager::sGetActiveCamera(m_skater_number);
}
	
CCompositeObject* CSkater::GetCamera()
{
	uint32 camera_name = CRCD(0x967c138c,"SkaterCam0");
	if (m_skater_number == 1)
	{
		camera_name = CRCD(0xe17b231a,"SkaterCam1");
	}
	return static_cast< CCompositeObject* >(CCompositeObjectManager::Instance()->GetObjectByID(camera_name));
}

void CSkater::SetViewMode( EViewMode view_mode )
{
	
	 Mdl::FrontEnd* front = Mdl::FrontEnd::Instance();
	//HUD::PanelMgr* panel_mgr = HUD::PanelMgr::Instance();

	if( m_viewing_mode == view_mode )
	{
		return;
	}
	

	// don't want to do it to skaters that don't have this component
	// (which would be the nonlocal skaters)	
	if (!GetSkaterPhysicsControlComponent())
	{
		return;
	}
	
	m_viewing_mode = view_mode;
			  
	switch ( m_viewing_mode )
	{
		// Normal skaing around with camera
		case GAMEPLAY_SKATER_ACTIVE:
		{
			// Regular gameplay, panel and console on
			if (GetPhysicsInt(CRCD(0x9816e1e3,"ScreenShotMode")))
			{
				dodgy_test(); printf("UnPausing game\n");
				front->PauseGame(false);
			}
		}
		break;

		// select+X pressed once, skater is frozen										  
		case VIEWER_SKATER_PAUSED:
		{
			if (GetPhysicsInt(CRCD(0x9816e1e3,"ScreenShotMode")))
			{
				dodgy_test(); printf("Freezing game\n");
				front->PauseGame(true);
			}
			else
			{
				GetSkaterPhysicsControlComponent()->SuspendPhysics(true);
			}
		}
		break;
		
		// select+X pressed again, skater skates around					 
		case VIEWER_SKATER_ACTIVE:
		{
			// pause the  game, turn off console
			if (GetPhysicsInt(CRCD(0x9816e1e3,"ScreenShotMode")))
			{
				// Dan: should we skip VIEWER_SKATER_ACTIVE then ScreenShotMode is true?
			}
			else
			{
				GetSkaterPhysicsControlComponent()->SuspendPhysics(false);
			}
		}
		break;
		
		
		default:
		Dbg_Assert( 0 );
	}
}

void CSkater::Pause()
{
	if (!mPaused && m_local_client )
	{
		MARK;
		
		// Pause the base object
		CMovingObject::Pause();

		// Switch off the meters. They will come back on by themselves when the skater is unpaused
		// and his logic is called again.
		Dbg_Assert(mp_skater_score_component)	   // Might not have a panel in the front end
		
		// NOTE:  These two lines make the balance meter vanish in "Screenshot" mode
		// But are needed for regular game pausing
		mp_skater_score_component->GetScore()->SetBalanceMeter(false);
		mp_skater_score_component->GetScore()->SetManualMeter(false);
		
		Script::RunScript(CRCD(0x1277e3fd,"pause_run_timer"), NULL);
		
		// Pause the input device (such as vibrations)
		if (mp_input_component)
		{
			mp_input_component->PauseDevice();
		}
        
		// Pause the game sounds
		Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
		sfx_manager->PauseSounds();
	}
}

void CSkater::UnPause()
{
	if (mPaused && m_local_client)
	{
		MARK;
		
		CMovingObject::UnPause();
		
		if (mp_input_component)
		{
			mp_input_component->UnPauseDevice();
		}

		CSkaterLoopingSoundComponent *p_sound_loop_component = GetSkaterLoopingSoundComponent();
		if (p_sound_loop_component)
		{
			p_sound_loop_component->UnPause();
		}
	}	
}

// Ken: Factored this out so that the replay code can call it too.
void CSkater::UpdateShadow(const Mth::Vector& pos, const Mth::Matrix& matrix)
{
#ifndef __PLAT_NGPS__
	static Mth::Vector ground_dir( 0.8f, -0.8f, 0.3f );
	static Mth::Vector air_dir( 0.0f, -1.0f, 0.0f );
	
	// If lights are active, set the ground direction to be that of the primary light.
	if( GetModel())
	{
		Nx::CModelLights *p_lights = GetModel()->GetModelLights();
		if( p_lights )
		{
			ground_dir = p_lights->GetLightDirection( 0 ) * -1.0f;

			if( ground_dir[Y] > -0.65f )
			{
				// Lighting direction is too shallow, leading to overly extended shadows. Limit direction.
				// In the new vector, we know we want the [Y] component to be -0.65, so it follows that we
				// want ( [X]^2 + [Z]^2 ) to be ( 1.0 - ( -0.65 ^2 )), or 0.5775. First, figure out the
				// current value of ( [X]^2 + [Z]^2 ).
				float xz_squared	= ( ground_dir[X] * ground_dir[X] ) + ( ground_dir[Z] * ground_dir[Z] );
				float multiple		= sqrtf( 0.5775f / xz_squared );
				ground_dir[X]		= ground_dir[X] * multiple;
				ground_dir[Y]		= -0.65f;
				ground_dir[Z]		= ground_dir[Z] * multiple;
			}
		}
	}
	
	bool ground = true;
	if( mp_skater_physics_control_component && ( mp_skater_physics_control_component->IsSkating()))
	{
		ground = mp_skater_core_physics_component->GetState() == GROUND;
	}
	else if( mp_walk_component )
	{
		ground = mp_walk_component->GetState() != CWalkComponent::WALKING_AIR;
	}
	
	// Interpolate between the two shadow directions based on Skater state.
	if( ground )
	{
		m_air_timer = ( m_air_timer > 0 ) ? ( m_air_timer - 1 ) : 0;
	}
	else
	{
		m_air_timer = ( m_air_timer < 16 ) ? ( m_air_timer + 1 ) : 16;
	}

	Mth::Vector dir = Mth::Lerp(ground_dir, air_dir, m_air_timer * (1.0f / 16.0f));
	// Mth::Vector dir = ground_dir + (( air_dir - ground_dir ) * ((float)m_air_timer / 16.0f ));
	mp_shadow_component->SetShadowDirection( dir );
#ifdef __PLAT_NGC__
	NxNgc::EngineGlobals.skater_shadow_dir.x = dir[X];
	NxNgc::EngineGlobals.skater_shadow_dir.y = dir[Y];
	NxNgc::EngineGlobals.skater_shadow_dir.z = dir[Z];
	NxNgc::EngineGlobals.skater_height = GetSkaterStateComponent()->GetHeight();

#endif		// __PLAT_NGC__ 

#endif

	/*
	GJ:  The following has been moved to CShadowComponent::Update()
	
	Mth::Vector shadow_target_pos = pos + ( matrix.GetUp() * 36.0f );

	if ( pShadowComponent->mp_shadow )
	{
		if (pShadowComponent->mp_shadow->GetShadowType()==Gfx::vSIMPLE_SHADOW)
		{
			pShadowComponent->mp_shadow->UpdatePosition(m_shadow_pos,m_matrix,pShadowComponent->m_shadow_normal);
		}
		else
		{
			pShadowComponent->mp_shadow->UpdatePosition(shadow_target_pos); // at this point m_pos is the same as m_pos
		}
	}
	*/
}

// K: Added for use by replay code so that it can playback pad vibrations
SIO::Device *CSkater::GetDevice()
{
	// Stubbed for now
	// Dbg_MsgAssert(m_input_handler,("NULL m_input_handler"));
	// Dbg_MsgAssert(m_input_handler->m_Device,("NULL m_input_handler->m_Device ?"));
	// return m_input_handler->m_Device;
	return NULL;
}

int			CSkater::GetHeapIndex( void )
{
	return m_heap_index;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

CSkater::CSkater ( int player_num, CBaseManager* p_obj_manager, bool local_client, int obj_id,
						int heap_num, bool should_tristrip ) : m_link_node( this ), m_local_client( local_client )
{
	m_type = SKATE_TYPE_SKATER;
	
	m_in_world = false;
	m_heap_index = heap_num;
	m_id = obj_id;

	// Mick:  Set up the Skater and Skater2 aliases	
	// Note we use heap_num rather than player_num, as player_num is unreliable in network games
	if (heap_num == 0)
	{
		Obj::CTracker::Instance()->AddAlias(CRCD(0x5b8ab877,"Skater"),this);		
	}
	if (heap_num == 1)
	{
		Obj::CTracker::Instance()->AddAlias(CRCD(0x6ed3fa7,"Skater2"),this);				
	}
	
	// low priority (but not lower than cameras) ensures that moving objects we may skate on or
	// cars we may drive will be updated before the skater each frame
	m_node.SetPri(-500);
	p_obj_manager->RegisterObject(*this);
	
	m_skater_number = player_num;
	
	DUMPI(m_skater_number);
	DUMPI(m_id);

	SetProfileColor(0x00c0c0);				// yellow = skater
	
	mp_animation_component=NULL;
	mp_model_component=NULL;
	mp_shadow_component=NULL;
	mp_trick_component=NULL;
	mp_vibration_component=NULL;
	mp_input_component=NULL;
	mp_skater_sound_component=NULL;
	mp_skater_gap_component=NULL;
	mp_skater_score_component=NULL;
	mp_skater_core_physics_component=NULL;
    mp_stats_manager_component=NULL;
	mp_walk_component=NULL;
	mp_skater_physics_control_component=NULL;

	// create the following components before CMovingObject creates components
	
	Script::CStruct* component_struct = new Script::CStruct;
	
	component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERSTATE);
	CreateComponentFromStructure(component_struct);
																  
	if (m_local_client)
	{
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_INPUT);
		CreateComponentFromStructure(component_struct);
		mp_input_component = GetInputComponent();
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERSCORE);
		CreateComponentFromStructure(component_struct);
		mp_skater_score_component = GetSkaterScoreComponent();

		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERMATRIXQUERIES);
		CreateComponentFromStructure(component_struct);

		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_TRICK);
		CreateComponentFromStructure(component_struct);
		mp_trick_component = GetTrickComponent();
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERPHYSICSCONTROL);
		CreateComponentFromStructure(component_struct);
		mp_skater_physics_control_component=GetSkaterPhysicsControlComponent();

		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERCOREPHYSICS);
		CreateComponentFromStructure(component_struct);
		mp_skater_core_physics_component = GetSkaterCorePhysicsComponent();

		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERROTATE);
		CreateComponentFromStructure(component_struct);

		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERGAP);
		CreateComponentFromStructure(component_struct);
		mp_skater_gap_component = GetSkaterGapComponent();
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERADJUSTPHYSICS);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_TRIGGER);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERFINALIZEPHYSICS);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERCLEANUPSTATE);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_WALK);
		CreateComponentFromStructure(component_struct);
		mp_walk_component = GetWalkComponent();
		
#		ifdef TESTING_GUNSLINGER
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_RIDER);
		CreateComponentFromStructure(component_struct);

		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_WEAPON);
		CreateComponentFromStructure(component_struct);
#		endif

		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERLOCALNETLOGIC);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERENDRUN);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERBALANCETRICK);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERLOOPINGSOUND);
		CreateComponentFromStructure(component_struct);

        component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_STATSMANAGER);
		CreateComponentFromStructure(component_struct);
        mp_stats_manager_component = GetStatsManagerComponent();
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_MOVABLECONTACT);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERRUNTIMER);
		CreateComponentFromStructure(component_struct);
	}
	else
	{
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERSCORE);
		CreateComponentFromStructure(component_struct);
		mp_skater_score_component = GetSkaterScoreComponent();

		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERNONLOCALNETLOGIC);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERCLEANUPSTATE);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERFLOATINGNAME);
		CreateComponentFromStructure(component_struct);

		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERENDRUN);
		CreateComponentFromStructure(component_struct);
		
		component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERLOOPINGSOUND);
		CreateComponentFromStructure(component_struct);
	}
	
	component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERSTATEHISTORY);
	CreateComponentFromStructure(component_struct);
	mp_skater_state_history_component = GetSkaterStateHistoryComponent();
	
	component_struct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_SKATERFLIPANDROTATE);
	CreateComponentFromStructure(component_struct);
									
    delete component_struct;
}

void CSkater::Construct ( Obj::CSkaterProfile* pSkaterProfile)
{
	// only skater 0 should update the camera in the
	// skateshop (otherwise, reloading skater 1 would
	// cause a momentary flicker)
	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	bool should_create_cam = ( !skate_mod->GetGameMode()->IsFrontEnd() ) || ( m_skater_number == 0 );

	if( m_local_client && should_create_cam )
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());

		uint32	name = CRCD(0x967c138c,"SkaterCam0");
		if (m_skater_number == 1)
		{
			name = CRCD(0xe17b231a,"SkaterCam1");
		}
		
			   
		CCompositeObject *p_cam_object = (CCompositeObject*)CCompositeObjectManager::Instance()->GetObjectByID(name);
		if (p_cam_object)
		{
			// Camera already exists, was being used as an observer camera,
			// so just attache it back to this skater
			GetSkaterCameraComponentFromObject(p_cam_object)->SetSkater( this );
			GetWalkCameraComponentFromObject(p_cam_object)->SetSkater( this );
		}
		else
		
		{
			// Add the parameters for the skater camera component		
			// so it can get me as the target
			Script::CStruct * p_component_params = new Script::CStruct;
			p_component_params->AddChecksum(CRCD(0x4c48da58,"CameraTarget"), GetID());
			// p_component_params->AddChecksum(CRCD(0x1313e3c,"ProximTriggerTarget"), GetID());
			p_component_params->AddChecksum(CRCD(0xa1dc81f9,"name"),name);
	
			p_cam_object = CCompositeObjectManager::Instance()->CreateCompositeObjectFromNode(
													Script::GetArray("skatercam_composite_structure"),p_component_params);
			delete	p_component_params;
		}		
		
		GetWalkComponent()->SetAssociatedCamera(p_cam_object);
		CFuncs::SetActiveCamera(name, m_skater_number, false);
		
#if 0 
// proximtrigger components are deprecated  	
		// point the viewer camera's proximity trigger at the skater
		// if it exists, which it might not, if the viewer is not active (Like for the XBox)
		CObject* p_viewer_cam = CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0xeb17151b,"viewer_cam"));
		if (p_viewer_cam)
		{
			GetProximTriggerComponentFromObject(static_cast< CCompositeObject* >(p_viewer_cam))->SetScriptTarget(this);
		}
#endif		
		
		// Lock the object so it won't get deleted.
		p_cam_object->SetLockOn();
		
			// Set default mode, so "behind" etc, is set tosomething sensible 
		GetSkaterCameraComponentFromObject(p_cam_object)->SetMode(CSkaterCameraComponent::SKATERCAM_MODE_NORMAL_MEDIUM, 0.0f);

		/*
		CCameraComponent *p_cam_component				= new CCameraComponent();
		CSkaterCameraComponent *p_skatercam_component	= new CSkaterCameraComponent();

		// We want the CameraComponent processed *after* the SkaterCameraComponent.
		p_cam_object->AddComponent( p_skatercam_component );
		p_cam_object->AddComponent( p_cam_component );

		p_skatercam_component->InitFromStructure( NULL );
		p_cam_component->InitFromStructure( NULL );

		p_skatercam_component->SetSkater( this );
		*/
		
		Mem::Manager::sHandle().PopContext();
	}

	// Set up the AI script.
	Dbg_MsgAssert(mp_script==NULL,("mp_script not NULL?"));

	mp_script=new Script::CScript;


// SkaterInit used to be run there --------------> *	
	
	Script::CStruct* pSkaterInfoParams = Script::GetStructure(CRCD(0x962e5cf7,"skater_asset_info"));

	uint32 skeleton_name = CRCD(0x5a9d2a0a,"human");
	if (pSkaterInfoParams)
	{
		pSkaterInfoParams->GetChecksum("skater_skeleton",&skeleton_name,Script::NO_ASSERT);
	}
    
//--------------------------------------------------------
	// components need to be created in a specific
	// order...  the model needs to come as late as
	// possible, so that all the other components can
	// play around with its display matrix.  (maybe 
	// should have some kind of priority system for 
	// components)?
	
	MovingObjectCreateComponents();

	Dbg_MsgAssert( !GetAnimationComponent(), ( "Animation component already exists" ) );
	Script::CStruct* pAnimationStruct = new Script::CStruct;
	pAnimationStruct->AddChecksum( CRCD(0xb6015ea8,"component"), CRC_ANIMATION );
	pAnimationStruct->AddChecksum( CRCD(0x5b8c6dc2,"AnimEventTableName"), CRCD(0x3fec31a6,"SkaterAnimEventTable") );
	CreateComponentFromStructure(pAnimationStruct, NULL);
    delete pAnimationStruct;
	mp_animation_component = GetAnimationComponent();
		
	Dbg_MsgAssert( !GetSkeletonComponent(), ( "Skeleton component already exists" ) );
	Script::CStruct* pSkeletonStruct = new Script::CStruct;
	pSkeletonStruct->AddChecksum( CRCD(0xb6015ea8,"component"), CRC_SKELETON );
    pSkeletonStruct->AddChecksum( CRCD(0x222756d5,"skeleton"), skeleton_name );
    pSkeletonStruct->AddInteger( CRCD(0xd3982061,"max_bone_skip_lod"), 0 );
	CreateComponentFromStructure(pSkeletonStruct, NULL);
    delete pSkeletonStruct;
	
	#if 0
	// Dan: testing
	Script::CStruct* pStruct = new Script::CStruct;
	pStruct->AddChecksum(CRCD(0xb6015ea8,"component"), CRC_RIBBON);
    pStruct->AddChecksum(CRCD(0xcab94088,"bone"), CRCD(0x7ee14cfe,"Bone_Wrist_L"));
    pStruct->AddInteger(CRCD(0x69feef91,"num_links"), 15);
    pStruct->AddFloat(CRCD(0x9f4625c2,"link_length"), 5.0f);
    pStruct->AddChecksum(CRCD(0x99a9b716,"color"), MAKE_RGB(0, 0, 255));
	CreateComponentFromStructure(pStruct, NULL);
    delete pStruct;
	#endif
	
//--------------------------------------------------------
 	
	uint32 anim_script_name = CRCD(0x501949bd,"animload_human");
	if (pSkaterInfoParams)
	{
		pSkaterInfoParams->GetChecksum(CRCD(0x8b7488e,"skater_anims"),&anim_script_name,Script::NO_ASSERT);
	}
	mp_animation_component->SetAnims( anim_script_name );
	mp_animation_component->EnableBlending( true );
	
	// Create an empty model 
	Dbg_MsgAssert( !GetModelComponent(), ( "Model component already exists" ) );
	Script::CStruct* pModelStruct = new Script::CStruct;
	pModelStruct->AddChecksum( NONAME, CRCD(0x10079f2d,"UseModelLights") );
	
	// Enables the shadow volume on any meshes that get added in the future
	int shadowVolumeEnabled = 0;
	if ( m_local_client )
	{
		shadowVolumeEnabled = true;
	}
	pModelStruct->AddInteger( CRCD(0x2a1f92c0,"ShadowVolume"), shadowVolumeEnabled ); 
	
	pModelStruct->AddChecksum( CRCD(0xb6015ea8,"component"), CRC_MODEL );
	CreateComponentFromStructure(pModelStruct, NULL);
	delete pModelStruct;
	mp_model_component = GetModelComponent();
	
	// create the model
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterGeomHeap(m_heap_index));
	
	Ass::CAssMan * ass_man =  Ass::CAssMan::Instance();
	bool defaultPermanent = ass_man->GetDefaultPermanent();
	ass_man->SetDefaultPermanent( false );
	
	// load up the model from a profile
	mp_model_component->InitModelFromProfile( pSkaterProfile->GetAppearance(), false, m_heap_index );
	
	ass_man->SetDefaultPermanent( defaultPermanent );
	
	Mem::Manager::sHandle().PopContext();

	// Create Model lights
	// (This has been moved to the CModelComponent)
//	GetModel()->CreateModelLights();
//	Nx::CModelLights *p_lights = GetModel()->GetModelLights();
//	p_lights->SetPositionPointer(&m_pos);

	// now that the model has been created,
	// create a shadow component for the skater
	Dbg_MsgAssert( !GetShadowComponent(), ( "Shadow component already exists" ) );
	Script::CStruct* pShadowStruct = new Script::CStruct;
	pShadowStruct->AddChecksum( CRCD(0xb6015ea8,"component"), CRC_SHADOW );
    pShadowStruct->AddChecksum( CRCD(0x9ac24b18,"shadowType"), (CRCD(0x76a54cd1,"detailed")) );
	CreateComponentFromStructure(pShadowStruct, NULL);
    delete pShadowStruct;
	mp_shadow_component = GetShadowComponent();

	if ( m_local_client )
	{
		Dbg_MsgAssert( !GetVibrationComponent(), ( "Vibration component already exists" ) );
		Script::CStruct* p_vibration_struct = new Script::CStruct;
		p_vibration_struct->AddChecksum( CRCD(0xb6015ea8,"component"), CRC_VIBRATION );
		CreateComponentFromStructure(p_vibration_struct, NULL);
		delete p_vibration_struct;
		mp_vibration_component = GetVibrationComponent();

		Dbg_MsgAssert( !GetSkaterStancePanelComponent(), ( "StancePanel component already exists" ) );
		Script::CStruct* p_stancepanel_struct = new Script::CStruct;
		p_stancepanel_struct->AddChecksum( CRCD(0xb6015ea8,"component"), CRC_SKATERSTANCEPANEL );
		CreateComponentFromStructure(p_stancepanel_struct, NULL);
		delete p_stancepanel_struct;
	}
	else
	{
		mp_vibration_component = NULL;
	}
	
	if ( m_local_client )
	{
		Dbg_MsgAssert( !GetSkaterSoundComponent(), ( "SkaterSound component already exists" ) );
		Script::CStruct* p_skater_sound_struct = new Script::CStruct;
		p_skater_sound_struct->AddChecksum( CRCD(0xb6015ea8,"component"), CRC_SKATERSOUND );
		CreateComponentFromStructure(p_skater_sound_struct, NULL);
		delete p_skater_sound_struct;
		mp_skater_sound_component = GetSkaterSoundComponent();
	}
	
	// Finalize Components
	Finalize();									
	
	// Set up the controller preferences.
	Dbg_MsgAssert(m_skater_number>=0 && m_skater_numberm_auto_kick = skate_mod->mp_controller_preferences[m_skater_number].AutoKickOn;
		mp_skater_core_physics_component->m_spin_taps = skate_mod->mp_controller_preferences[m_skater_number].SpinTapsOn;
		mp_vibration_component->SetActiveState(skate_mod->mp_controller_preferences[m_skater_number].VibrationOn);
	}


	// Mick:  Run the SkaterInit Script
	// this used to be done before all the components are created, but it makes more sense to do it after
	// and the old way boke after I moved the "SetSkaterCamOverride" into SkaterCameraComponent
	if ( m_local_client )
	{
		// Start running the ground AI.
		#ifdef __NOPT_ASSERT__
		mp_script->SetCommentString("Created in CSkater constructor, assigned to CSkater::mp_script");
		#endif
		mp_script->SetScript("SkaterInit",NULL,this);
	}

	
	UpdateSkaterInfo( pSkaterProfile );

	UpdateStats( pSkaterProfile );
	
	if ( m_local_client )
	{
		GetSkaterSoundComponent()->SetMaxSpeed(GetScriptedStat(CRCD(0xcc5f87aa,"Skater_Max_Max_Speed_Stat")));
	}
	

	/*
	// K: For the debug info above the skater.
	mp_drawer=new Fnt::Drawer();
	mp_drawer->SetRenderState(Image::Drawer::vRS_ZBUFFER | Image::Drawer::vRS_ALPHABLENDING);
	mp_drawer->SetFont("small.fnt");
	mp_drawer->SetJust(Image::vJUST_CENTER_X,Image::vJUST_CENTER_Y);
	mp_drawer->SetShadowState(true,0,0);
	*/

	if (mp_trick_component)
	{
		// K: Create and fill in the trick mappings structure.
		mp_trick_component->UpdateTrickMappings( pSkaterProfile );

		// Initialise the event buffer.
		mp_trick_component->ClearEventBuffer();
		
		// Initialise the trick queue
		mp_trick_component->ClearTrickQueue();
		mp_trick_component->ClearManualTrick();
		mp_trick_component->ClearExtraGrindTrick();

		mp_trick_component->SetAssociatedScore(mp_skater_score_component->GetScore());
	}
	if (mp_skater_gap_component)
	{
		mp_skater_gap_component->SetAssociatedScore(mp_skater_score_component->GetScore());
	}
	
	mSparksRequireRail=true;


	Obj::CCollisionComponent* p_collision_component = GetCollisionComponent();
	if ( p_collision_component )
	{
		if (p_collision_component->GetCollision())
		{
			// Garrett: Make skater non-collidable for now
			p_collision_component->GetCollision()->SetObjectFlags(mSD_NON_COLLIDABLE);
		}
	}
	
	// Init collision cache for CFeelers
	if (m_local_client)
	{
		// mp_collision_cache = Nx::CCollCacheManager::sCreateCollCache();
	}
	else
	{
		// mp_collision_cache = NULL;
	}

	// make sure it doesn't flash at the origin for a brief moment,
	mp_model_component->FinalizeModelInitialization();

	// this needs to happen after the m_id has been assigned
	Script::RunScript( CRCD(0xae5438af,"InitSkaterParticles"), NULL, this );

	// safety-check to make sure he doesn't start out in the blair witch position
	// (because of our animation LOD-ing system)
	// (must be done before the models get initialized)
    mp_animation_component->PlaySequence(CRCD(0x1ca1ff20,"default"));
    
    if (m_local_client)
	{
		// add created tricks
		for (int i=0; i < Game::vMAX_CREATED_TRICKS; i++)
		{
			m_created_trick[i] = new Game::CCreateATrick;
		}
        Script::RunScript( CRCD(0xbdc1c89,"spawn_add_premade_cats_to_skater"), NULL, this );
        
		// setup the correct physics state
		CCompositeObject::CallMemberFunction(CRCD(0x5c038f9b,"SkaterPhysicsControl_SwitchWalkingToSkating"), NULL, NULL);
	}
}

void CSkater::Init(Obj::CSkaterProfile* pSkaterProfile)
{
	Construct(pSkaterProfile);

	GameNet::Manager * gamenet_manager =  GameNet::Manager::Instance();

// Note, both tasks are registered as LOW_PRIORITY, so they come after everything else
// this ensures the movement of the skater is in sync with objects the skater stands on
	
	if( m_local_client && CFuncs::ScriptInMultiplayerGame( NULL, NULL ))
	{
		Net::App* client;
	
		client = gamenet_manager->GetClient( m_skater_number );
		Dbg_Assert( client );
		   
		client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SCORE, Mdl::Score::s_handle_score_message, 0, this );
		client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SKATER_COLLIDE_WON, CSkaterStateHistoryComponent::sHandleCollision, 0, this );
		client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SKATER_COLLIDE_LOST, CSkaterStateHistoryComponent::sHandleCollision, 0, this );
		client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SKATER_PROJECTILE_HIT_TARGET, CSkaterStateHistoryComponent::sHandleProjectileHit, 0, this );
		client->m_Dispatcher.AddHandler( GameNet::MSG_ID_SKATER_HIT_BY_PROJECTILE, CSkaterStateHistoryComponent::sHandleProjectileHit, 0, this );
		client->m_Dispatcher.AddHandler( GameNet::MSG_ID_STEAL_MESSAGE, CSkaterLocalNetLogicComponent::sHandleStealMessage, 0, this );
	}
		
	// moved to CSkaterLocalNetLogicComponent
	// m_last_update_time = 0;
    
	Obj::CSuspendComponent* pSuspendComponent = GetSuspendComponent();
	if ( pSuspendComponent )
	{
		pSuspendComponent->m_lod_dist[0] = 3000;	// was 500, but in multi-player, we generally want to see subtle animations animating
													// and in single player, you should always be close to the camera anyway
		pSuspendComponent->m_lod_dist[1] = 5000; 	// we pretty much always want to see the other player, even at a great distance
		pSuspendComponent->m_lod_dist[2] = 10000000;
		pSuspendComponent->m_lod_dist[3] = 10000000;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkater::UpdateStats( CSkaterProfile* pSkaterProfile )
{		
	// GJ:  apply the stats contained in the skater profile;  we'll need
	// need to override them after this function, if we're applying cheats
	
	this->SetStat( STATS_AIR, pSkaterProfile->GetStatValue((CRCD(0x439f4704,"air"))) );
	this->SetStat( STATS_RUN, pSkaterProfile->GetStatValue((CRCD(0xaf895b3f,"run"))) );
	this->SetStat( STATS_OLLIE, pSkaterProfile->GetStatValue((CRCD(0x9b65d7b8,"ollie"))) );
	this->SetStat( STATS_SPEED, pSkaterProfile->GetStatValue((CRCD(0xf0d90109,"speed"))) );
	this->SetStat( STATS_SPIN, pSkaterProfile->GetStatValue((CRCD(0xedf5db70,"spin"))) );
	this->SetStat( STATS_FLIPSPEED, pSkaterProfile->GetStatValue((CRCD(0x6dcb497c,"flip_speed"))) );
	this->SetStat( STATS_SWITCH, pSkaterProfile->GetStatValue((CRCD(0x9016b4e7,"switch"))) );
	this->SetStat( STATS_RAILBALANCE, pSkaterProfile->GetStatValue((CRCD(0xf73a13e3,"rail_balance"))) );
	this->SetStat( STATS_LIPBALANCE, pSkaterProfile->GetStatValue((CRCD(0xae798769,"lip_balance"))) );
	this->SetStat( STATS_MANUAL, pSkaterProfile->GetStatValue((CRCD(0xb1fc0722,"manual_balance"))) );

#ifdef __USER_GARY__
	dodgy_test(); printf("Air    %2.2f\n",GetStat(STATS_AIR));		
	dodgy_test(); printf("Run    %2.2f\n",GetStat(STATS_RUN));		
	dodgy_test(); printf("Ollie  %2.2f\n",GetStat(STATS_OLLIE));		
	dodgy_test(); printf("Speed  %2.2f\n",GetStat(STATS_SPEED));		
	dodgy_test(); printf("Spin   %2.2f\n",GetStat(STATS_SPIN));		
	dodgy_test(); printf("Flip   %2.2f\n",GetStat(STATS_FLIPSPEED));		
	dodgy_test(); printf("Switch %2.2f\n",GetStat(STATS_SWITCH));		
	dodgy_test(); printf("Rail   %2.2f\n",GetStat(STATS_RAILBALANCE));		
	dodgy_test(); printf("Lip    %2.2f\n",GetStat(STATS_LIPBALANCE));		
	dodgy_test(); printf("Manual %2.2f\n",GetStat(STATS_MANUAL));
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkater::UpdateSkaterInfo( Obj::CSkaterProfile* pSkaterProfile )
{
	// get some info from the skater profile
	// while we still have access to it...
	Dbg_MsgAssert( pSkaterProfile,( "No profile supplied" ));

	m_pushStyle = pSkaterProfile->GetChecksumValue( (CRCD(0xc15dbf86,"pushstyle")) );
	m_isGoofy = pSkaterProfile->GetChecksumValue( (CRCD(0x7d02bcc3,"stance")) ) == ( CRCD(0x287fcd4e,"goofy") );
	m_isPro = pSkaterProfile->IsPro();
	m_displayName = pSkaterProfile->GetDisplayName();
	
	// needed for playing correct streams
	pSkaterProfile->GetInfo()->GetInteger( CRCD(0x3f813177,"is_male"), &m_isMale, Script::ASSERT );

	const char* pFirstName;
	pSkaterProfile->GetInfo()->GetText( CRCD(0x562e3ecd,"first_name"), &pFirstName, Script::ASSERT );
	Dbg_MsgAssert( strlen( pFirstName) < MAX_LEN_FIRST_NAME,
				   ( "First name %s is too long (max = %d chars)", m_firstName, MAX_LEN_FIRST_NAME ) );
	strcpy( m_firstName, pFirstName );
	
	m_skaterNameChecksum = pSkaterProfile->GetSkaterNameChecksum();
	
	if (IsLocalClient())
	{
		mp_skater_core_physics_component->SetFlag(FLIPPED, !m_isGoofy);
		GetSkaterFlipAndRotateComponent()->ApplyFlipState();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkater::~CSkater ( void )
{ 
// Taken care of by the shadow component's destructor
//	Obj::CShadowComponent* pShadowComponent = GetShadowComponent();
//	if ( pShadowComponent )
//	{
//		pShadowComponent->SwitchOffShadow();
//	}

	// if (mp_collision_cache)
	// {
		// Nx::CCollCacheManager::sDestroyCollCache(mp_collision_cache);
	// }
	
	// We must unpause the controller before we destruct ourselves.  Otherwise, the
	// paused/unpaused toggle will become out of sync with any future skater which
	// uses this controller.
	if (mPaused && m_local_client && mp_input_component)
	{
		mp_input_component->GetDevice()->UnPause();
		mp_input_component->GetDevice()->StopAllVibrationIncludingSaved();
	}

	DeleteResources();	   

	Script::RunScript( CRCD(0xf429e0ac,"DestroySkaterParticles"), NULL, this );

	for (int i=0; i < Game::vMAX_CREATED_TRICKS; i++)
    {
        delete m_created_trick[i];
    }

	// (Mick) If there is a camera still associated with this skater, then delete it
	// this will be either SkaterCam0 or SkaterCam1, and only on local clients 	
	// (Steve) only delete player 2's camera. For now, at least, player 1's camera
	// is persistent because it is used in observer mode
	if (IsLocalClient() && ( m_skater_number == 1 ))
	{
		CCompositeObject *p_cam = GetCamera();
		if (p_cam)
		{
			//delete p_cam;
			p_cam->MarkAsDead();
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Reset skater state (physics, score, et al.), such as when you're retrying
void CSkater::Reset()
{
	// MARK;
	
	Dbg_Assert(mp_skater_score_component);
	mp_skater_score_component->Reset();
	
	// clear max times (records) for the various balance tricks
	Dbg_Assert(GetSkaterBalanceTrickComponent());
	GetSkaterBalanceTrickComponent()->ClearMaxTimes();	
	
	ResetAnimation();
	
	Dbg_Assert(mp_skater_core_physics_component);
	int i;
	for ( i = 0; i < NUM_ESKATERFLAGS; i++ )
	{
		mp_skater_core_physics_component->SetFlag( ( ESkaterFlag )i, true );
		mp_skater_core_physics_component->SetFlag( ( ESkaterFlag )i, false );
	}

	// Reset the pad:
	Dbg_Assert(mp_input_component);
	mp_input_component->GetControlPad().Reset();
}


// DeleteResources will delete things the skater has allocated that
// we do not want to stick around when we do a cleanup of everything else
// we should really split the skater into temporary and permanent parts
void CSkater::DeleteResources(void)
{
	if (IsLocalClient())
	{
		mp_skater_gap_component->ClearActiveGaps();
		
		GetSkaterBalanceTrickComponent()->ClearBalanceParameters();
	}
	
	// we don't want to carry over special items
	// from one level to another (they would
	// fragment memory anyway)
	
	Obj::CSpecialItemComponent* pSpecialItemComponent = GetSpecialItemComponent();
	Dbg_Assert(pSpecialItemComponent);
	pSpecialItemComponent->DestroyAllSpecialItems();

	// need to get rid of any animations
	// that the skater is currently playing,
	// or else there will be CBlendChannels
	// fragmenting the bottom-up heap.
	mp_animation_component->Reset();
	
	
#ifdef	__SCRIPT_EVENT_TABLE__		
	if (mp_script)
	{
		delete mp_script;
		mp_script = NULL;
		
	}
#else
	// Clear the event receiver associated with this objects
	// They will get set up again next time we add or remove one
	mp_event_handler_table->unregister_all(this);
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void CSkater::ResetStates()
{
	Dbg_Assert( IsLocalClient());

	GetSkaterPhysicsControlComponent()->SuspendPhysics(false);
	
	GetSkaterFlipAndRotateComponent()->Reset();
	
//	#ifdef __PLAT_NGC__
//	mDoDeferredRotateAfter=false;
//	mDeferredFlipAndRotate=false;
//	#endif

	// Ken stuff, reset other misc stuff such as the event buffer, trick queue, blaa blaa
	
//	GetExceptionComponent()->ClearExceptions();

	Dbg_Assert( mp_trick_component );
	mp_trick_component->ClearQueueTricksArrays();
	mp_trick_component->ClearManualTrick();
	mp_trick_component->ClearExtraGrindTrick();
	mp_trick_component->ClearEventBuffer();
	mp_trick_component->ClearTrickQueue();
	mp_trick_component->ClearMiscellaneous();
	
	GetSkaterStateComponent()->Reset();

	// If set this will make the call to UpdateModel() use the display matrix flipped.
	// Used when running the lip-trick out-anim, to stop it appearing flipped until
	// the anim has finished.
	mp_model_component->mFlipDisplayMatrix=false;
	
	// Make sure the meters are off.
	Dbg_Assert( mp_skater_score_component )
	mp_skater_score_component->GetScore()->SetBalanceMeter(false);
	mp_skater_score_component->GetScore()->SetManualMeter(false);
	
	// Reset the panel
//	Mdl::Score *pScore=GetScoreObject();
//	pScore->BailRequest();	// effectively cancels the current score pot

	// Reset doing-trick flag so that he doesn't bail on the drop-in if he died whilst
	// doing a trick.
	
	if (mp_vibration_component)
	{
		mp_vibration_component->StopAllVibration();
	}	

	// set the shadow to stick to his feet, assuming we are on the ground
	// Note: These are used by the simple shadow, not the detailed one.
	mp_shadow_component->SetShadowPos( m_pos );
	mp_shadow_component->SetShadowNormal( m_matrix[Y] );

	mSparksRequireRail=true;
	
	GetSkaterStancePanelComponent()->Reset();
}

void CSkater::ResetAnimation()
{
	Dbg_Assert( IsLocalClient());
    
	// Put the board orientation back to normal
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	Net::Client* client;

	client = gamenet_man->GetClient( GetHeapIndex());
	Dbg_Assert( client );

	CSkaterFlipAndRotateComponent* p_flip_and_rotate_component = GetSkaterFlipAndRotateComponent();
	p_flip_and_rotate_component->RotateSkateboard(GetID(), false, client->m_Timestamp, IsLocalClient());
	p_flip_and_rotate_component->ApplyFlipState();
	
	// Make sure the board is visible
	mp_model_component->HideGeom( CRCD(0xa7a9d4b8,"board"), false, IsLocalClient() );
	
	/*
	// Make sure all atomics are visible
	// (GJ:  Not really sure why the previous board unhiding is still necessary)
	pModelComponent->HideGeom((CRCD(0xc4e78e22,"all")), false, IsLocalClient());
	
	// unhiding all the atomics will conflict with the "invisible" cheat	
	*/
	  
	// getting into certain physics states
	// will reset the animation, so we want to 
	// make sure we've always got some animation
	// to be playing...
	mp_animation_component->Reset();

//	GJ:  don't start playing a new animation, because
//  it's possible that the animations have been unloaded
//	mp_animation_component->PlaySequence( CRCD(0x1ca1ff20,"default") );
//	mp_animation_component->SetLoopingType( Gfx::LOOPING_CYCLE, true );
}

void CSkater::ResetInitScript(bool walk, bool in_cleanup)
{
// Mick: Finally update the "SkaterInit" script, so it does any flag clearing that 
// is needed by the game
// we do this now, otherwise some other script could issue a MakeSkaterGoto, and clear this script
// so it would never get run.
	if ( !mp_script )
	{
		mp_script = new Script::CScript;
	}
	
	Script::CStruct* p_params = NULL;
	if (walk || in_cleanup)
	{
		p_params = new Script::CStruct;
		if (walk)
		{
			p_params->AddChecksum(NO_NAME, CRCD(0x726e85aa,"Walk"));
		}
		if (in_cleanup)
		{
			p_params->AddChecksum(NO_NAME, CRCD(0x7b738580,"InCleanup"));
		}
	}
	
	mp_script->SetScript(CRCD(0xe740f458,"SkaterInit"), p_params, this);
	if( IsLocalClient())
	{
		mp_script->Update();
	}
	if (p_params)
	{
		delete p_params;
	}
}

// Reset physics flags, position, velocity, etc...
void CSkater::ResetPhysics(bool walk, bool in_cleanup)
{
	// MARK;
	Dbg_Assert( IsLocalClient());

	// Massive function to reset all skater's members so that
	// replay becomes deterministicalistic
	// ResetEverything( );
	
	// switch back to walking
	if (mp_skater_physics_control_component->IsWalking())
	{
		Script::CStruct* p_params = new Script::CStruct;
		p_params->AddChecksum(CRCD(0x91d0d784, "NewScript"), CRCD(0x1dde4804, "OnGroundAi"));
		JumpToScript(CRCD(0x93a4e402, "Switch_OnGroundAI"), p_params);
		delete p_params;
	}
	
	// make sure the OnExitRun script gets run before DeleteResources clears the script
	if (mp_script && mp_script->GetOnExitScriptChecksum())
	{
		uint32 on_exit_script_name_checksum = mp_script->GetOnExitScriptChecksum();
		mp_script->SetOnExitScriptChecksum(0);
		mp_script->Interrupt(on_exit_script_name_checksum);
	}
	
	mp_skater_core_physics_component->Reset();

	GetSkaterCameraComponentFromObject(GetCamera())->ResetMode();

	ResetStates();
	DeleteResources();
	ResetAnimation();		  
	ResetInitScript(walk, in_cleanup);
	
	GetSkaterEndRunComponent()->ClearIsEndingRun();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkater::SkaterEquals( Script::CStruct* pStructure )
{
	// TODO:  Maybe stick this data inside the skater's tags,
	// so that we can create new fields without having to
	// update this function?

	Dbg_MsgAssert( pStructure,( "No structure supplied" ));

	if ( pStructure->ContainsFlag( CRCD(0xd82f8ac8,"is_pro") ) )
	{
		if ( !m_isPro )
			return false;
	}

	if ( pStructure->ContainsFlag( CRCD(0x5a3264bb,"is_custom") ) )
	{
		if ( m_isPro )
			return false;
	}

	if ( pStructure->ContainsFlag( CRCD(0x3f813177,"is_male") ) )
	{
		if ( !m_isMale )
			return false;
	}
	
	if ( pStructure->ContainsFlag( CRCD(0xe5c2f84c,"is_female") ) )
	{
		if ( m_isMale )
			return false;
	}
	
	uint32 is_named;
	if ( pStructure->GetChecksum( CRCD(0xc2e3d008,"is_named"), &is_named ) )
	{
		if ( is_named != m_skaterNameChecksum )
			return false;
	}

	uint32 stance;
	if ( pStructure->GetChecksum( CRCD(0x7d02bcc3,"stance"), &stance ) )
	{
		uint32 myStance = ( m_isGoofy ? CRCD(0x287fcd4e,"goofy") : CRCD(0xb58efc2b,"regular") );
		if ( stance != myStance )
			return false;
	}

	// if we've gotten to the end of the function,
	// that means that all of the criteria matched
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkater::CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript )
{
	
	Dbg_MsgAssert(pScript,("NULL pScript"));
	
	return _function0( Checksum, pParams, pScript );
}

bool CSkater::_function0( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript )
{
	
	switch (Checksum)
	{
        // @script | InLocalSkater | returns true if the skater is a local client
		case 0x7015bc33: // IsLocalSkater
		{
			return IsLocalClient();
		}
		
        // @script | MoveToRandomRestart | moves the skater to a random
        // restart node
		case 0xbf450bab: // MoveToRandomRestart
		{
			MoveToRandomRestart();
			break;
		}

        // @script | SkaterIsNamed | true if skater name matches specified name
        // @uparm name | skater name
		case 0x1c5612ee: // SkaterIsNamed
		{
			uint32 checksum;
			pParams->GetChecksum( NONAME, &checksum, Script::ASSERT );
			return ( checksum == m_skaterNameChecksum );
		}
		break;
		
		// @script | GetCameraId | sets the skater's camera id to CameraId
		case CRCC(0x97c8285a,"GetCameraId"):
		{
			uint32 camera_name = CRCD(0x967c138c,"SkaterCam0");
			if (m_skater_number == 1)
			{
				camera_name = CRCD(0xe17b231a,"SkaterCam1");
			}
			pScript->GetParams()->AddChecksum(CRCD(0x7a039889,"CameraId"), camera_name);
			break;
		}

        // @script | RemoveSkaterFromWorld | 
		case 0xbce85494:	// RemoveSkaterFromWorld
		{
			 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

			skate_mod->HideSkater( this, true );
			return true;
		}
		break;

        // @script | AddSkaterToWorld | 
		case 0x0c09c1b7:	// AddSkaterToWorld
		{
			 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

			skate_mod->HideSkater( this, false );
			//AddToCurrentWorld();
			return true;
		}
		break;

		#if 0
		case 0x7fa39d17:	// ResetLookAround
		{
			Dbg_Message("Skater:ResetLookAround does not work");
			if (GetSkaterCameraComponent())
			{
				GetSkaterCameraComponent()->ResetLookAround();	
			}
			return true;
		}
		break;
		#endif
		
		case 0x72aecfc0:	// ResetRigidBodyCollisionRadiusBoost
			ResetRigidBodyCollisionRadiusBoost();
			break;

		default:
			return _function1( Checksum, pParams, pScript );
			break;
	}
	return true;
}
			
bool CSkater::_function1( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript )
{
	
	
	switch (Checksum)
	{
		/////////////////////////////////////////////////////////////////////////////////////////////
        // @script | KillSkater | Killskater will reset the skater
        // to a node, and set his speed to 0. If this command is
        // run from a node, and that node is linked to a restart
        // node, then the skater will skip to that restart node,
        // and the script in that restart node will be executed
		// @parmopt name | Node | | name of node to jump to
		// @parmopt name | prefix | | restart at nearest node matching this prefix
		// @flag RestartWalking | restart the skater in walk physics mode
		case 0x1b572abf: // KillSkater 
		{
			// default just clear skater skater
			// and reset to origin		
			
			bool walk = pParams->ContainsComponentNamed(CRCD(0xd6f06bf6,"RestartWalking"));
			
			// If there is a "node" parameter, then skip to that 
			// Mick, optionally add a "prefix" parameter, and skip to the nearest node that matches
			// this prefix (will need a new node funtion:
			//  int	GetNearestNodeByPrefix(const char *p_prefix, const Mth::Vector &pos)

			const char *p_prefix = NULL;
			uint32	prefix_name = 0;
			if (!pParams->GetChecksum(CRCD(0x6c4e7971,"prefix"),&prefix_name))
			{
				if (pParams->GetString(CRCD(0x6c4e7971,"prefix"),&p_prefix))
				{
					prefix_name = Crc::GenerateCRCFromString(p_prefix);
				}
			}
			if (prefix_name)
			{
				int	target_node = SkateScript::GetNearestNodeByPrefix(prefix_name,m_pos);
				Dbg_MsgAssert(target_node != -1,("%s\nNo restart node found with prefix 0x%x, %s",pScript->GetScriptInfo(),prefix_name, p_prefix));
				SkipToRestart(target_node, walk);  
				break;
			}
			
			
			uint32 node_name=0;
			if (pParams->GetChecksum(CRCD(0x7a8017ba,"Node"),&node_name))
			{
				int target_node = SkateScript::FindNamedNode(node_name);
				Dbg_MsgAssert(target_node != -1,("%\n Tried to kill skater to non-existant node",pScript->GetScriptInfo()));
				SkipToRestart(target_node, walk); 
				break;
			}
			else
			{
				
				int node = pScript->mNode;
				Dbg_MsgAssert(node !=  -1,( "%s\nKillSkater called from non node script with no 'node' parameter",pScript->GetScriptInfo()));			
				{
					// Otherwise, check the links to see if we are linked to a restart point
				
					int links = SkateScript::GetNumLinks(node);
					int link;
					for (link = 0;linkUsingCustomPark()) 		// is it a custom park???
						{
							Mdl::Skate* skate_mod = Mdl::Skate::Instance();
							skate_mod->skip_to_restart_point( this, -1, walk );	// just skip to standard restart node			
						}
						else
						#endif
						{
							Dbg_MsgAssert(0,( "%s\nKillSkater called, but node %d not linked to restart",pScript->GetScriptInfo(),node));			
						}
					}
				}
			}			
			break;
		}
		
        // @script | SparksOn | turn sparks on
        // @flag RailNotRequired | 
		case 0xe62f94a2: // SparksOn
			Replay::WriteSparksOn();
			Script::RunScript( CRCD(0xaf4475de,"sparks_on"), pParams, this );
			mSparksRequireRail=true;
			mSparksOn=true;
			if (pParams->ContainsFlag("RailNotRequired"))
			{
				mSparksRequireRail=false;
			}	
			break;

        // @script | SparksOff | turn sparks off
		case 0x5436a335: // SparksOff
			Replay::WriteSparksOff();
			Script::RunScript( CRCD(0x0dccf5c3,"sparks_off"), pParams, this );
			mSparksOn=false;
			mSparksRequireRail=true;
			break;

        // @script | BloodOff | turn blood off
        // @parm string | bone | bone name
		case 0xc67c18d7: // BloodOff
		{
			const char* p_bone_name;
			if (!pParams->GetText( CRCD(0xcab94088,"bone"), &p_bone_name ))
			{
				Dbg_MsgAssert(0,("%s\nBloodOff requires a bone name"));
				return 0;
			}
			
			// nothing doing......

            break;
		}
   
        // @script | Die | can't be called on the player
		case 0xc6870028:// "Die"
			Dbg_MsgAssert( 0,( "\n%s\nDie can't be called on the player.\nIs somebody putting 'Die' in a trigger script?", pScript->GetScriptInfo( ) ));
			break;

        // @script | ShouldMongo | true if should mongo
		case 0x3e8eb713:	// ShouldMongo
		{
			if( !IsLocalClient())
			{
				return false;
			}
			switch ( m_pushStyle )
			{
				case vALWAYS_MONGO:
					return true;
					break;
				case vMONGO_WHEN_SWITCH:
					return ( mp_skater_core_physics_component->IsSwitched() );
					break;
				case vNEVER_MONGO:
				default:
					return false;
					break;
			}
		}
			break;

        // @script | SetCustomRestart |  Skater command to set the current position as the "custom restart point" 
		// and also to query or clear the status of this restart, based on:
        // @flag set | set the restart
		// @flag clear | clear the restart
		// if no flags are passed, then it will return true if restart point is valid 
		case 0x42832375:			// SetCustomRestart
		{
			if (pParams->ContainsFlag(CRCD(0x19ebda23,"set")))
			{
				m_restart_valid = true;
				m_restart_pos = m_pos;
				m_restart_matrix = m_matrix;			
				m_restart_walking = mp_skater_physics_control_component->IsWalking();
			}
			else if (pParams->ContainsFlag(CRCD(0x1a4e0ef9,"clear")))
			{
				m_restart_valid = false;
				break;
			}
			else
			{
				return m_restart_valid;
			}
			
			//break;
		}
		// Note: Intentional fallthrough!  We want setting the point to have the same effect as skipping to it
		
        // @script | SkipToCustomRestart |  Skater command to jump to previously set custom restart 		
		case 0x5a3c19e9:		// SkipToCustomRestart
		{
			SkipToRestart(-1, m_restart_walking);
			m_pos = m_restart_pos;
			DUMP_SPOSITION
			#ifdef	DEBUG_POSITION
			dodgy_test(); printf("%d: Position = (%.2f,%.2f,%.2f)\n",__LINE__,m_pos[X],m_pos[Y],m_pos[Z]);
			#endif
			m_matrix = m_restart_matrix;
			mp_skater_core_physics_component->m_safe_pos = m_pos;				// for uberfrig
			m_old_pos = m_pos;				// ensure we don't get any nasty triggerings....
			SetTeleported();
			
			mp_skater_core_physics_component->ResetLerpingMatrix();
			#ifdef		DEBUG_DISPLAY_MATRIX
			dodgy_test(); printf("%d: Setting display_matrix[Y][Y] to %f, [X][X] to %f\n",__LINE__,mp_skater_core_physics_component->m_lerping_display_matrix[Y][Y],mp_skater_core_physics_component->m_lerping_display_matrix[X][X]);
			#endif
			mp_skater_core_physics_component->m_display_normal = mp_skater_core_physics_component->m_last_display_normal = mp_skater_core_physics_component->m_current_normal = m_matrix[Y];
			// set the shadow to stick to his feet, assuming we are on the ground
			// Note: These are used by the simple shadow, not the detailed one.
			mp_shadow_component->SetShadowPos( m_pos );
			mp_shadow_component->SetShadowNormal( m_matrix[Y] );
			break;
		}

        // @script | GetStat |  Get a stat value in the float stat_value, so it can be used in a script expression
		// @parm int | Stat | index of stat in stat array, see 
		case 0x7915aa31:			// GetStat
		{
			int stat;
			pParams->GetInteger(CRCD(0xdf4700de,"Stat"),&stat,true);
			pScript->GetParams()->AddFloat(CRCD(0x8eaf7add,"stat_value"),GetStat((EStat)stat));
			break;
		}

        // @script | GetScriptedStat |  Get a stat value in the float stat_value
		// parameters are as used in physics.q
		// and you would generally pass in a structure, like Skater_Max_Speed_Stat
		// rather than the individual values
		case 0x9abe8a21:			// GetScriptedStat
		{
//			Script::PrintContents(pParams);
			Script::CStruct *p_struct = NULL;
			pParams->GetStructure(NONAME,&p_struct,true);
			pScript->GetParams()->AddFloat(CRCD(0x8eaf7add,"stat_value"),GetScriptedStat(p_struct));
			break;
		}
		
		// @script | GetSkaterNumber | Gets the skater number, and puts it into a parameter called
		// SkaterNumber
		case 0xf4c52f72: // GetSkaterNumber 
		{
			pScript->GetParams()->AddInteger(CRCD(0xf3cf5755,"SkaterNumber"),m_skater_number);
			break;
		}
		
		case 0xd38e8b6c: // PlaceBeforeCamera
			{
				if ( GetActiveCamera() )
				{
					Mth::Vector	&cam_pos = GetActiveCamera()->GetPos();
					Mth::Matrix	&cam_mat = GetActiveCamera()->GetMatrix();


					m_matrix.Ident();
					m_pos = cam_pos;
					m_pos[Y] += FEET( 0 );
					m_pos -= cam_mat[Z] * FEET( 12 );
					DUMP_SPOSITION
					m_old_pos = m_pos;			// MoveToRestart 
					mp_skater_core_physics_component->m_safe_pos = m_pos;			// MoveToRestart  
					m_matrix = cam_mat;
					m_matrix[Z] = -m_matrix[Z];
					m_matrix[X] = -m_matrix[X];
					mp_skater_core_physics_component->ResetLerpingMatrix();
					#ifdef		DEBUG_DISPLAY_MATRIX
					dodgy_test(); printf("%d: Setting display_matrix[Y][Y] to %f, [X][X] to %f\n",__LINE__,mp_skater_core_physics_component->m_lerping_display_matrix[Y][Y],mp_skater_core_physics_component->m_lerping_display_matrix[X][X]);
					#endif
					mp_skater_core_physics_component->m_display_normal = mp_skater_core_physics_component->m_last_display_normal = mp_skater_core_physics_component->m_current_normal = m_matrix[Y];
					// set the shadow to stick to his feet, assuming we are on the ground
					// Note: These are used by the simple shadow, not the detailed one.
					mp_shadow_component->SetShadowPos( m_pos );
					mp_shadow_component->SetShadowNormal( m_matrix[Y] );
					// update these for the camera
					m_vel = -cam_mat[Z];  
					m_vel[Y] = 0;  
					m_vel.Normalize(10);  
					UpdateCamera(true);	  	// re-snap the camera to the correct position
					
					#ifdef	DEBUG_VELOCITY
					dodgy_test(); printf("%d: Velocity = (%.2f,%.2f,%.2f) (%f)\n",__LINE__,m_vel[X],m_vel[Y],m_vel[Z],m_vel.Length());
					#endif
				}
			}
			break;

  			Dbg_MsgAssert(0,("Forgot a break?"));		
		
		default:
			return CMovingObject::CallMemberFunction( Checksum, pParams, pScript );
			break;
	}
	return true;
}

// End of CallMemberFunction

void CSkater::SparksOn()
{
	Dbg_Assert( 0 );

	// GJ:  It would be nice to keep all the spark logic in
	// script, but it requires some skater state info in C
	// (like the mSparksRequireRail).  Eventually, we should
	// move this all to script, instead of calling the following
	// script from C code.

	Script::RunScript( CRCD(0xaf4475de,"sparks_on"), NULL, this );
	mSparksOn = true;
}

void CSkater::SparksOff()
{
	// GJ:  It would be nice to keep all the spark logic in
	// script, but it requires some skater state info in C
	// (like the mSparksRequireRail).  Eventually, we should
	// move this all to script, instead of calling the following
	// script from C code.
	if ( mSparksOn )
	{
		Replay::WriteSparksOff();
		Script::RunScript( CRCD(0x430efc31,"TurnOffSkaterSparks"), NULL, this );
		mSparksOn=false;
	}
}
	
// Used by the MakeSkaterGoto script function in cfuncs.cpp
// Causes the skater to jump to the specified script straight away.
void CSkater::JumpToScript(uint32 ScriptChecksum, Script::CStruct *pParams)
{
	Dbg_Assert(IsLocalClient());
		
//	Dbg_MsgAssert(mp_script,("NULL mp_script"));
	if ( !mp_script )
	{
		mp_script = new Script::CScript;
	}

	GetSkaterFlipAndRotateComponent()->DoAnyFlipRotateOrBoardRotateAfters(); // <- See huge comment above definition of this function.
	mp_script->SetScript(ScriptChecksum,pParams,this);
	mp_script->Update();
}
	
void CSkater::JumpToScript(const char *pScriptName, Script::CStruct *pParams)
{
	
	JumpToScript(Script::GenerateCRC(pScriptName),pParams);
}

Mth::Matrix&				CSkater::GetDisplayMatrix( void )
{
	return mp_skater_core_physics_component->m_lerping_display_matrix;
}

void						CSkater::SetDisplayMatrix( Mth::Matrix& matrix )
{
	mp_skater_core_physics_component->m_lerping_display_matrix = matrix;
}

Lst::Node< CSkater >*		CSkater::GetLinkNode( void )
{
	return &m_link_node;
}

void CSkater::Resync( void )
{
	if (!m_local_client)
	{
		GetSkaterNonLocalNetLogicComponent()->Resync();
	}
	
	mp_skater_state_history_component->ResetPosHistory();
	//mp_skater_state_history_component->ResetAnimHistory();
	//mp_skater_state_history_component->SetLatestAnimTimestamp( 0 );
	
}

void CSkater::RemoveFromCurrentWorld( void )
{
	if (IsLocalClient())
	{
		GetSkaterLoopingSoundComponent()->StopLoopingSound();
	}
	
	mp_shadow_component->SwitchOffShadow();
    
	Hide( true );
	m_in_world = false;
}

void	CSkater::AddToCurrentWorld ( void )
{
	mp_shadow_component->SwitchOnSkaterShadow();
	Hide( false );
	m_in_world = true;
}

bool	CSkater::IsInWorld( void )
{
	return m_in_world;
}

const char* CSkater::GetDisplayName()
{
	// set the player's display name for rankings screen,
	// "You got bitch-slapped by X" messages, etc.
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	if( gamenet_man->InNetGame() )
	{
		GameNet::PlayerInfo* pInfo = gamenet_man->GetPlayerByObjectID( m_id );
		Dbg_Assert( pInfo );
		// (Mick) don't assign it to m_displayName, as that can cause memory allocations
		// and fragments the heap
		// ensure you set the appropiate context if you want to do this.
		//m_displayName =  pInfo->m_Name;
		return pInfo->m_Name;
	}
	
	return m_displayName.getString();
}

void CSkater::AddCATInfo(Script::CStruct *pStuff)
{
	Dbg_MsgAssert(pStuff,("NULL pStuff"));
	
    Script::CArray *p_tricks=new Script::CArray;
    p_tricks->SetSizeAndType(Game::vMAX_CREATED_TRICKS,ESYMBOLTYPE_STRUCTURE);
    for (int i=0; iAddStructure(CRCD(0x26a7cadf,"other"),m_created_trick[i]->mp_other_params);
        p_new_trick_struct->AddArray(CRCD(0x6b19fc8f,"animations"),m_created_trick[i]->mp_animations);
        p_new_trick_struct->AddArray(CRCD(0x2e628ee6,"rotations"),m_created_trick[i]->mp_rotations);

        //Script::PrintContents( p_new_trick_struct );

        p_tricks->SetStructure(i,p_new_trick_struct);
    }
    pStuff->AddArrayPointer(CRCD(0x1e26fd3e,"Tricks"),p_tricks);
}

void CSkater::LoadCATInfo(Script::CStruct *pStuff)
{
    printf("LoadCATInfo was called\n");
	Dbg_MsgAssert(pStuff,("NULL pStuff"));

    Script::CArray *pCATInfo=NULL;
    pStuff->GetArray(CRCD(0x1e26fd3e,"tricks"),&pCATInfo);

    Dbg_Assert( m_created_trick[1] );

    for (int i=0; iGetStructure(i);
        
        // other params
        Script::CStruct *p_other_params=NULL;
        pTemp->GetStructure(CRCD(0x26a7cadf,"other"),&p_other_params,Script::ASSERT);
        m_created_trick[i]->mp_other_params->AppendStructure( p_other_params );

        // animations
        Script::CArray *p_animation_info=NULL;
        pTemp->GetArray(CRCD(0x6b19fc8f,"animations"),&p_animation_info,Script::ASSERT);
        Script::CopyArray( m_created_trick[i]->mp_animations, p_animation_info );
        
        // rotations
        Script::CArray *p_rotation_info=NULL;
        pTemp->GetArray(CRCD(0x2e628ee6,"rotations"),&p_rotation_info,Script::ASSERT);
        Script::CopyArray( m_created_trick[i]->mp_rotations, p_rotation_info );

        //Script::PrintContents( m_created_trick[i]->mp_other_params );
        //Script::PrintContents( m_created_trick[i]->mp_rotations );
    }

    Script::RunScript(CRCD(0x33fe5817,"kill_load_premade_cats"), NULL);
}

void CSkater::AddStatGoalInfo(Script::CStruct *pStuff)
{
    Dbg_MsgAssert(pStuff,("NULL pStuff"));
	
    Script::CArray *p_stat_goals=new Script::CArray;
    p_stat_goals->SetSizeAndType(Obj::NUM_STATS_GOALS,ESYMBOLTYPE_INTEGER);
    
    for (int i=0; iSetInteger(i,mp_stats_manager_component->StatGoalGetStatus(i));
    }
    pStuff->AddArrayPointer(CRCD(0x44b66a9f,"StatGoals"),p_stat_goals);
}

void CSkater::LoadStatGoalInfo(Script::CStruct *pStuff)
{
    Dbg_MsgAssert(pStuff,("NULL pStuff"));

    // Reset Stats Goals and set to proper difficulty
    Script::CStruct* p_params = NULL;
    mp_stats_manager_component->RefreshFromStructure(p_params);

    Script::CArray *pStatGoalInfo=NULL;
    pStuff->GetArray(CRCD(0x44b66a9f,"StatGoals"),&pStatGoalInfo);

    for (int i=0; iStatGoalSetStatus(i,pStatGoalInfo->GetInteger(i));
    }
}

void CSkater::AddChapterStatusInfo(Script::CStruct *pStuff)
{
    Dbg_MsgAssert(pStuff,("NULL pStuff"));
	
    pStuff->AddArray(CRCD(0x8e8ee0c0,"ChapterStatus"),Script::GetArray( CRCD(0xdfa17d68,"CHAPTER_STATUS"), Script::ASSERT ));
}

void CSkater::LoadChapterStatusInfo(Script::CStruct *pStuff)
{
    Dbg_MsgAssert(pStuff,("NULL pStuff"));

	uint32 Name = Script::GenerateCRC("ChapterStatus");
    Script::CArray *pChapterStatusInfo=NULL;
    pStuff->GetArray(Name,&pChapterStatusInfo);

    Script::CopyArray(Script::GetArray( CRCD(0xdfa17d68,"CHAPTER_STATUS"), Script::ASSERT ), pChapterStatusInfo);
}

} // namespace Obj


================================================
FILE: Code/Sk/Objects/skater.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Object (OBJ)											**
**																			**
**	File name:		objects/skater.h										**
**																			**
**	Created: 		01/25/99	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __OBJECTS_SKATER_H
#define __OBJECTS_SKATER_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 
#include 
#include 
// #include 

// #include 

// #include 
// #include 
#include 
#include 
// #include 

// #include 

#include 
// #include 

#include 
#include 

#include 

#define		SNAP_OVER_THIN_WALLS  	// snap over thin walls (like rails)
#define		STICKY_WALLRIDES		 // attempt to snap sideways to wallrides

// temp debug macros
#define MESSAGE(a) { printf("M:%s:%i: %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPI(a) { printf("D:%s:%i: " #a " = %i\n", __FILE__ + 15, __LINE__, a); }
#define DUMPB(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a ? "true" : "false"); }
#define DUMPF(a) { printf("D:%s:%i: " #a " = %g\n", __FILE__ + 15, __LINE__, a); }
#define DUMPE(a) { printf("D:%s:%i: " #a " = %e\n", __FILE__ + 15, __LINE__, a); }
#define DUMPS(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, a); }
#define DUMPP(a) { printf("D:%s:%i: " #a " = %p\n", __FILE__ + 15, __LINE__, a); }
#define DUMPC(a) { printf("D:%s:%i: " #a " = %s\n", __FILE__ + 15, __LINE__, Script::FindChecksumName(a)); }
#define DUMPV(a) { printf("D:%s:%i: " #a " = %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z]); }
#define DUMP4(a) { printf("D:%s:%i: " #a " = %g, %g, %g, %g\n", __FILE__ + 15, __LINE__, (a)[X], (a)[Y], (a)[Z], (a)[W]); }
#define DUMPM(a) { DUMP4(a[X]); DUMP4(a[Y]); DUMP4(a[Z]); DUMP4(a[W]); }
#define MARK { printf("K:%s:%i: %s\n", __FILE__ + 15, __LINE__, __PRETTY_FUNCTION__); }
#define PERIODIC(n) for (static int p__ = 0; (p__ = ++p__ % (n)) == 0; )

#define RED MAKE_RGB(255, 0, 0)
#define GREEN MAKE_RGB(0, 255, 0)
#define BLUE MAKE_RGB(0, 0, 255)
#define YELLOW MAKE_RGB(200, 200, 0)
#define PURPLE MAKE_RGB(200, 0, 200)
#define CYAN MAKE_RGB(0, 200, 200)

// skater specific debug defines
#ifdef __NOPT_ASSERT__
#define HEADER { printf("%i:%s:%i: ", Tmr::GetRenderFrame(), __FILE__ + 15, __LINE__); }
#define DUMP_VELOCITY { if (Script::GetInteger(CRCD(0x71f0478a, "debug_skater_vel"))) { HEADER; printf("m_vel = %g, %g, %g (%g)\n", GetObject()->m_vel[X], GetObject()->m_vel[Y], GetObject()->m_vel[Z], GetObject()->m_vel.Length()); } }
#define DUMP_POSITION { if (Script::GetInteger(CRCD(0x029ade47, "debug_skater_pos"))) { HEADER; printf("m_pos = %g, %g, %g\n", GetObject()->m_pos[X], GetObject()->m_pos[Y], GetObject()->m_pos[Z]); } }
#define DUMP_SPOSITION { if (Script::GetInteger(CRCD(0x029ade47, "debug_skater_pos"))) { HEADER; printf("m_pos = %g, %g, %g\n", m_pos[X], m_pos[Y], m_pos[Z]); } }
#else
#define DUMP_VELOCITY {   }
#define DUMP_POSITION {   }
#define DUMP_SPOSITION {   }
#endif

// if uncommented here, uncomment in skaterpad.cpp too								   
//#define		PS2_SIMULATING_XBOX

#ifdef	__PLAT_NGPS__
	#ifdef	PS2_SIMULATING_XBOX
		#define	BREAK_SPINE_BUTTONS (control_pad.m_L2.GetPressed() && control_pad.m_R2.GetPressed())
		#define	WALK_SPINE_BUTTONS (control_pad.m_L2.GetPressed() && control_pad.m_R2.GetPressed())
	#else	
		#define	BREAK_SPINE_BUTTONS (control_pad.m_L2.GetPressed() || control_pad.m_R2.GetPressed())
		#define	WALK_SPINE_BUTTONS (control_pad.m_R2.GetPressed())
	#endif
#endif

#ifdef	__PLAT_XBOX__
#define	BREAK_SPINE_BUTTONS (control_pad.m_L2.GetPressed() && control_pad.m_R2.GetPressed())
#define	WALK_SPINE_BUTTONS (control_pad.m_L2.GetPressed() && control_pad.m_R2.GetPressed())
#endif

#ifdef	__PLAT_NGC__
#define	BREAK_SPINE_BUTTONS (control_pad.m_L1.GetPressed() && control_pad.m_R1.GetPressed())
#define	WALK_SPINE_BUTTONS (control_pad.m_L1.GetPressed() && control_pad.m_R1.GetPressed())
#endif


#define	VERT_RECOVERY_BUTTONS (BREAK_SPINE_BUTTONS)

// #define	USE_BIKE_PHYSICS (Script::GetInteger(CRCD(0x9e86d1c1,"use_bike_physics")))
// #define	USE_WALK_PHYSICS (Script::GetInteger(CRCD(0x9ae06a83,"use_walk_physics")))
// #define	CESS_SLIDE_BUTTONS (control_pad.m_R1.GetPressed() && USE_BIKE_PHYSICS)

#define	SKITCH_BUTTON (control_pad.m_up.GetPressed() && control_pad.m_up.GetPressedTime()>(uint32)GetPhysicsInt(CRCD(0x19b2c752,"Skitch_Hold_Time")))

// These are defined here, so they can easily be switched from	
// script flags to some more general cheat flags
//#define		CHEAT_SNOWBOARD		(control_pad.m_R1.GetPressed()) //   (GetPhysicsInt("CheatSnowBoard",false) || GetCheat(GetPhysicsInt(0x9bc667b5)))  //CHEAT_SNOWBOARD
#define		CHEAT_SNOWBOARD		0	//(GetPhysicsInt("CheatSnowBoard",false) || GetCheat(GetPhysicsInt(0x9bc667b5)))  //CHEAT_SNOWBOARD
#define		CHEAT_MATRIX		(Mdl::Skate::Instance()->GetCareer()->GetPhysicsInt(CRCD(0xc41d8a34,"CheatMatrix"),false)  || GetCheat(GetPhysicsInt(0xf9514c9e)))	  // CHEAT_MATRIX
#define		CHEAT_FIRST_PERSON	0//(GetPhysicsInt("CheatFirstPerson",false)  || GetCheat(GetPhysicsInt(0xca459091))) // CHEAT_FIRST_PERSON
#define		CHEAT_PERFECT_RAIL	 (Mdl::Skate::Instance()->GetCareer()->GetCheat(GetPhysicsInt(0xcd09e062)))	  // CHEAT_PERFECT_RAIL
#define		CHEAT_PERFECT_MANUAL (Mdl::Skate::Instance()->GetCareer()->GetCheat(GetPhysicsInt(0xb38341c9)))	  // CHEAT_PERFECT_MANUAL
#define		CHEAT_PERFECT_SKITCH (Mdl::Skate::Instance()->GetCareer()->GetCheat(GetPhysicsInt(0x69a1ce96)))	  // CHEAT_PERFECT_SKITCH

#define		CHEAT_STATS_13   (Mdl::Skate::Instance()->GetCareer()->GetCheat(GetPhysicsInt(0x6b0f24bc)))  
#define		CHEAT_KID        0//(GetCheat(GetPhysicsInt(0x406e5a16))) 
#define		CHEAT_SIM        (Mdl::Skate::Instance()->GetCareer()->GetCheat(GetPhysicsInt(0x2b87107a)))  
#define		CHEAT_MOON       (Mdl::Skate::Instance()->GetCareer()->GetCheat(GetPhysicsInt(0x9c8c6df1)))  

#define		CHEAT_SLOMO		(Mdl::Skate::Instance()->GetCareer()->GetCheat(GetPhysicsInt(0xf163e7e0)))		// CHEAT_SLOMO
#define		CHEAT_DISCO		(Mdl::Skate::Instance()->GetCareer()->GetCheat(GetPhysicsInt(0x9fc22bda)))		// CHEAT_DISCO

////////////////////////////////////////////////////////////////////////
// The following #defines generally enable a bunch of prints
// that track the changes in value or state of a particular 
// aspect of the physics.
// Some of them will also display debugging lines
//


//#define	DEBUG_WALKING
//#define	DEBUG_ACC_LINES
//#define	DEBUG_INSIDE_OBJECT
//#define	DEBUG_SKITCHING
//#define	DEBUG_BOUNCE_OFF_WALL
//#define	DEBUG_WALLRIDE
//#define	DEBUG_VELOCITY
//#define	DEBUG_POSITION
//#define	DEBUG_SPINE
//#define	SHOW_MOVEMENT_LINES
//#define	DEBUG_UBERFRIG
//#define	DEBUG_DISPLAY_MATRIX
//#define	DEBUG_MATRIX
//#define	DEBUG_COL_FLAGS 
//#define	DEBUG_RAILS
//#define	DEBUG_TERRAIN
//#define	DEBUG_AIR_SNAP_UP
//#define	DEBUG_SHOW_ALL_COLLISION_LINES
//#define	DEBUG_SHOW_SIDE_PUSH_LINES
//#define	DEBUG_TRIGGERS
//#define	DEBUG_GAPS
//#define	DEBUG_STATES
//#define	DEBUG_VERT
//#define	DEBUG_RECOVER
//#define	DEBUG_BRAKE
//#define	DEBUG_FRICTION
//#define	DEBUG_HIGH_SCORE_GOALS
					   
#define		STOPPED_TURN_RAMP_TIME		600		// ms ramp time for turning when stopped

/*****************************************************************************
**						   Forward Declarations								**
*****************************************************************************/
	
// These are for network games
#define vNUM_LAG_FRAMES	1.0f
#define vFORCE_SEND_INTERVAL	15	// every 15 frames, send a full network object update

class	CContact;

namespace Script
{
	class CComponent;
}
	
namespace Mdl
{
	class Skate;
}

namespace Nx
{
	class CCollCache;
}

namespace Gfx
{
	class	Camera;
}

namespace Obj
{
	class 	CRailNode;							   
	class 	CSkaterProfile;
	class	CMovieManager;

	class	CTrickBufferComponent;
	
	class	CAnimationComponent;
	class	CModelComponent;
	class	CShadowComponent;
	class	CTrickComponent;
	class	CTriggerComponent;
	class	CInputComponent;
	class	CSkaterCameraComponent;
	class	CVibrationComponent;
	class	CSkaterSoundComponent;
	class	CSkaterGapComponent;
	class	CSkaterRotateComponent;
	class	CSkaterScoreComponent;
	class	CSkaterStateHistoryComponent;
	class	CSkaterCorePhysicsComponent;
	class	CSkaterPhysicsControlComponent;
	class	CWalkComponent;
    class	CStatsManagerComponent;
	
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

enum EViewMode
{
	GAMEPLAY_SKATER_ACTIVE,
	VIEWER_SKATER_PAUSED,
	VIEWER_SKATER_ACTIVE,
	NUM_VIEW_MODES
};

#define MAX_TRICK_NAME_CHARS 50

// Used for storing any info needed to restore the skater after it is
// hidden during a replay.
// Doesn't contain much at the moment.
class CSkaterModelRestorationInfo
{
public:
	//bool mShadowOn;
	Mth::Vector mPreReplayPos;
	Mth::Matrix mPreReplayDisplayMatrix;
	uint32 mAtomicStates;
};

class  CSkater : public  CMovingObject 
{
	friend class Mdl::Skate;

public:

	void DodgyTest();

	enum	EPushStyle
	{
		vNEVER_MONGO = 0x8b966093,			// "never_mongo"
		vALWAYS_MONGO = 0x16c6df0b,			// "always_mongo"
		vMONGO_WHEN_SWITCH = 0x0cdba4f9		// "mongo_when_switch"
	};

	enum	EStat
	{
		STATS_AIR = 0,
		STATS_RUN,
		STATS_OLLIE,
		STATS_SPEED,
		STATS_SPIN,
		STATS_FLIPSPEED,
		STATS_SWITCH,
		STATS_RAILBALANCE,
		STATS_LIPBALANCE,
		STATS_MANUAL,	
			   
		NUM_ESTATS
	};

///////////////////////////////////////////////////////////////////////////////////////////////	
// Player specific	
public:
	void						UpdateStats( Obj::CSkaterProfile* pSkaterProfile );
	void 						UpdateSkaterInfo( Obj::CSkaterProfile* pSkaterProfile );
	float						GetStat(EStat stat);
	void						SetStat(EStat stat, float value);
	float 						GetStattedValue(const char *pName, EStat stat);
	float 				  	  	GetScriptedStat(Script::CStruct *pSS);
	float						GetScriptedStat(const uint32 checksum);
	float 						GetScriptedStat(uint32 Checksum, Script::CStruct *pSetOfStats);
	// float						GetHeight();
							   
	// Here's some data that needs to be stored
	// from the skater profile, in case various
	// scripts ask for it
	uint32	m_pushStyle;
	bool	m_isGoofy;
	bool	m_isPro;
	int		m_isMale;

	enum
	{
		MAX_LEN_FIRST_NAME = 64
	};

	char	m_firstName[MAX_LEN_FIRST_NAME];

	// Name of the skater, eg Hawk, Caballero, etc.
	// Grabbed from the skater profile.
	uint32	m_skaterNameChecksum;
	Str::String	m_displayName;

protected:
	
	CSkaterModelRestorationInfo m_model_restoration_info;
	
public :
	CSkater ( int player_num, CBaseManager* p_obj_man, bool local_client, int obj_id, int heap_num, bool should_tristrip);
	virtual						~CSkater ( void );

	// void HideForReplayPlayback();
	// void RestoreAfterReplayPlayback();

///////////////////////////////////////////////////////////////////////////
// Sub Games
	
public:
	bool						m_always_special;

///////////////////////////////////////////////////////////////////////////////////////////////
// Kind of redundant	
public:
	void						RemoveFromCurrentWorld( void );
	void						AddToCurrentWorld ( void );
	bool						IsInWorld( void );
	
///////////////////////////////////////////////////////////////////////////////////////////
// fundamental  to both skater and physics	
public:
	virtual void 				Pause();
	virtual void 				UnPause();
	virtual bool				CallMemberFunction( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript ); 
	virtual void 				Init(Obj::CSkaterProfile* pSkaterProfile);

protected :
	void 						Construct(Obj::CSkaterProfile* pSkaterProfile);
	bool _function0( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript );
	bool _function1( uint32 Checksum, Script::CStruct *pParams, Script::CScript *pScript );

///////////////////////////////////////////////////////////////////////////////////
// Network

public:
	void	Resync( void );
protected:

	int							m_last_restart_node;

////////////////////////////////////////////////////////////////////////////////////////////
// Rendering related
public:
	Mth::Matrix&				GetDisplayMatrix( void );
	void						SetDisplayMatrix( Mth::Matrix& matrix );
	
protected:
	// True if sparks will be automatically switched off when
	// the skater comes off a rail.
	bool mSparksRequireRail;
	bool mSparksOn;
	void SparksOff();
	void SparksOn();

	// The skater's shadow will move beneath him (if directional) when he is in the air.
	int			m_air_timer;
	
//////////////////////////////////////////////////////////////////////////////////////////////
// high level accessors	
public:
	int							GetHeapIndex( void );

////////////////////////////////////////////////////////////////////////////////////////////////
// Level gameplay related
public:
	void						MoveToRandomRestart( void );
	virtual void				Reset();
	virtual void				SkipToRestart(int node, bool walk=false);
	virtual void				MoveToRestart(int node);

// should be private:	
	// Dan: I'd like to remove this, but it's still used by CEmiter, which is skater specific and does not
	// currently use ProximTriggerComponents
	bool						m_inside;		// flag set up by proxim node triggering


protected:


////////////////////////////////////////////////////////////////////////////////////////////////
// Misc
public:
	Lst::Node< CSkater >*		GetLinkNode( void );

//////////////////////////////////////////////////////////////////////////////
// Tricks 
public:	
    // CreateATrick 
    Game::CCreateATrick* m_created_trick[Game::vMAX_CREATED_TRICKS];
    void					AddCATInfo(Script::CStruct *pStuff);
    void					LoadCATInfo(Script::CStruct *pStuff);
    int                     m_num_cat_animations_on;
    bool                    m_cat_bail_done;
    bool                    m_cat_flip_skater_180;
    bool                    m_cat_animations_done;
    bool                    m_cat_rotations_done;
    float                   m_cat_hold_time;
    int                     m_cat_total_x;
    int                     m_cat_total_y;
    int                     m_cat_total_z;
    // Stat Goals
    void					AddStatGoalInfo(Script::CStruct *pStuff);
    void					LoadStatGoalInfo(Script::CStruct *pStuff);
    // Chapter Status
    void					AddChapterStatusInfo(Script::CStruct *pStuff);
    void					LoadChapterStatusInfo(Script::CStruct *pStuff);
	
public:
////////////////////////////////////////////////////////////////////////////////
// script

	void	JumpToScript(uint32 ScriptChecksum, Script::CStruct *pParams=NULL);
	void	JumpToScript(const char *pScriptName, Script::CStruct *pParams=NULL);

protected :
	
	void UpdateCamera(bool instantly = false);
	void UpdateCameras( bool instantly );
	
public:
	
	void DeleteResources(void);
	void ResetPhysics(bool walk=false,bool in_cleanup=false);
	virtual void ResetStates();
	virtual void ResetAnimation();		  
	virtual void ResetInitScript(bool walk, bool in_cleanup);

protected:
	// Moved this from the CMovingObject class
	// since we're trying to remove functionality
	// from the moving object class, and because
	// the skater was the only thing that was
	// calling it...
	void MovingObj_Reset( void );

public:	
	void UpdateShadow(const Mth::Vector& pos, const Mth::Matrix& matrix);
	// K: Added for use by replay code so that it can playback pad vibrations
	SIO::Device *GetDevice();

protected:

	Lst::Node< CSkater >		m_link_node;			// linked list node for list of skaters

	// pointers to oft used components of the skater
	CAnimationComponent*			mp_animation_component;
	CModelComponent*				mp_model_component;
	CShadowComponent*				mp_shadow_component;
	CTrickComponent*				mp_trick_component;
	CVibrationComponent*			mp_vibration_component;
	CTriggerComponent*				mp_trigger_component;
	CInputComponent*				mp_input_component;
	CSkaterSoundComponent*			mp_skater_sound_component;
	CSkaterGapComponent*			mp_skater_gap_component;
	CSkaterRotateComponent*			mp_skater_rotate_component;
	CSkaterScoreComponent*			mp_skater_score_component;
	CSkaterStateHistoryComponent*	mp_skater_state_history_component;
	CSkaterCorePhysicsComponent*	mp_skater_core_physics_component;
	CSkaterPhysicsControlComponent* mp_skater_physics_control_component;
	CWalkComponent*					mp_walk_component;
    CStatsManagerComponent*			mp_stats_manager_component;
	
public:
	// allow external code rapid access to these components
	CSkaterStateHistoryComponent*	GetStateHistory (   )
	{
		Dbg_Assert(mp_skater_state_history_component);
		return mp_skater_state_history_component;
	}
	
protected:
	
	float						m_stat[NUM_ESTATS];	 	// all the stats

	int							m_heap_index;			// Which skater heap is this skater using
											 
	bool						m_local_client;			// Is this cient being controlled by my PS2

	
	int							m_skater_number;		// 0 or 1 (maybe 2 or 3) 	  

	int							m_viewing_mode;					// 0 = normal, 1 = frozen, move cam, 2 = move, freeze cam
	
public:	
	int							GetViewingMode (   ) { return m_viewing_mode; }
	
protected:	
	// is the skater in the world?
	bool						m_in_world;
	
	// For custom restart point
	
	Mth::Vector					m_restart_pos;
	Mth::Matrix					m_restart_matrix;
	bool						m_restart_walking;
	bool						m_restart_valid;
	
public:	

	const char*	GetDisplayName();
	void		SetAlwaysSpecial(bool on) {m_always_special = on;}
	void		ToggleAlwaysSpecial() {m_always_special = !m_always_special;}

	// this used to be in the skater profile, but I changed it
	// because it was confusing when called from multiplayer games.
	bool		SkaterEquals( Script::CStruct* pParams );

public:
	// Returns a pointer to this skater's score object.
	Mdl::Score *GetScoreObject();
	bool	IsLocalClient() {return m_local_client;}

	int		GetSkaterNumber() {return m_skater_number;}
	
	Gfx::Camera *GetActiveCamera( void );
	
	CCompositeObject* CSkater::GetCamera (    );

	void    SetViewMode( EViewMode view_mode );
	int		GetViewMode() { return m_viewing_mode; }
	
	// hack to allow the collision radius to be adjusted while in a car
	float	m_rigid_body_collision_radius_boost;
	void	SetRigidBodyCollisionRadiusBoost ( float rigid_body_collision_radius_boost ) { m_rigid_body_collision_radius_boost = rigid_body_collision_radius_boost; }
	void	ResetRigidBodyCollisionRadiusBoost (   ) { m_rigid_body_collision_radius_boost = 0.0f; }
	float	GetRigidBodyCollisionRadiusBoost (   ) { return m_rigid_body_collision_radius_boost; }

	friend class CSkaterCam;
	
	friend class CSkaterCameraComponent;
	friend class CTrickComponent;
	friend class CSkaterLoopingSoundComponent;
	friend class CSkaterSoundComponent;
	friend class CSkaterGapComponent;
	friend class CSkaterRotateComponent;
	friend class CSkaterLocalNetLogicComponent;
	friend class CSkaterNonLocalNetLogicComponent;
	friend class CSkaterScoreComponent;
	friend class CSkaterMatrixQueriesComponent;
	friend class CSkaterStateHistoryComponent;
	friend class CSkaterPhysicsControlComponent;
	friend class CSkaterCorePhysicsComponent;
	friend class CSkaterAdjustPhysicsComponent;
	friend class CSkaterFinalizePhysicsComponent;
	friend class CSkaterCleanupStateComponent;
	friend class CSkaterFlipAndRotateComponent;
	
private:
	// skater physics variables beyond those inherited from CCompositeObject
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/
extern bool DebugSkaterScripts;

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

int GetPhysicsInt(const char *p_name, Script::EAssertType assert = Script::NO_ASSERT);
int GetPhysicsInt(uint32 id, Script::EAssertType assert = Script::NO_ASSERT);
float GetPhysicsFloat(const char *p_name, Script::EAssertType assert = Script::NO_ASSERT);
float GetPhysicsFloat(uint32 id, Script::EAssertType assert = Script::NO_ASSERT);

} // namespace Obj

#endif	// __OBJECTS_SKATER_H


================================================
FILE: Code/Sk/Objects/skatercam.cpp
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       skatercam.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/05/2001
//****************************************************************************

/*

(Mick) 
Problem:

If you stop, and then jump in place while holding "down", you will lean backwards while 
you jump.  This will not really be noticable, but when you land, the camera will seem to 
snap backwards for a single frame

the problem was m_display_matrix is lagging behind
so I have patched it by setting skater_up to mpSkate->m_display_normal when on the ground
this fixes it for the skater glitching, however the world around him is still glitching
as the angle still changes abruptly

one fix might be to somehow smoothly interpolate this angle , which might also help 
with any fix we do for skating off ledges

I need to clean up and comment this code, so it can be modified without fear.....


*/


//#define	DEBUG_CAMERA

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/
		
		
static void _xrotlocal ( Mth::Matrix& m, float a )
{
	m.RotateXLocal( a );
}

namespace Obj
{

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

	const float LEAN_TO_SKATERCAM_LEAN	= (( Mth::PI / 6.0f ) / -4096.0f );
	const float	SKATERCAM_INTERPOLATOR_TIMESTEP = ( 1.0f / 60.0f );

	const float VERT_AIR_LANDED_TIME	= 10.0f / 60.0f;
	const float TILT_MAX				= 0.34907f;						// 20 degees.
	const float TILT_MIN				= -0.34907f;					// -20 degees.
	const float TILT_INCREMENT			= 2.0f * ( TILT_MAX / 60.0f );	// 40 degrees/second.
	const float TILT_DECREMENT			= 2.0f * ( TILT_MAX / 60.0f );	// 40 degrees/second.
	const float TILT_RESTORE			= 8.0f * ( TILT_MAX / 60.0f );	// 160 degrees/second.

	// This is the value that mAbove should tend to when the behind value is closer than mBehind.
	const float PERFECT_ABOVE = 3.0f;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCam::s_input_logic_code( const Inp::Handler < CSkaterCam >& handler )
{    
    Dbg_AssertType( &handler, Inp::Handler< CSkaterCam > );
	
	bool skip_button;
	bool right_button;
	int right_x;
	int right_y;

    CSkaterCam&	obj = handler.GetData();
	
	if ( obj.mpSkater && !obj.mpSkater->IsLocalClient( ) )
	{
		obj.mSkipButton = handler.m_Input->m_Makes & ( Inp::Data::mD_X );
		obj.mRightButton = handler.m_Input->m_Makes & ( Inp::Data::mD_R3 );
		obj.mRightX = handler.m_Input->m_Event[Inp::Data::vA_RIGHT_X] - 128;
		obj.mRightY = handler.m_Input->m_Event[Inp::Data::vA_RIGHT_Y] - 128;
	}
	skip_button = obj.mSkipButton;
	right_button = obj.mRightButton;
	right_x = obj.mRightX;
	right_y = obj.mRightY;

	if (obj.m_input_enabled)
	{
		if( right_button )
		{
			obj.mLookaroundLock = !obj.mLookaroundLock;
		}
	
		if( !obj.mLookaroundLock && !obj.mLookaroundOverride )
		{
			// We want this to map to (PI/2, ~-2PI/10). Switched this 7/10/01 to be opposite up/down to previously.
			float target = ((float)(( right_y < 0 ) ? right_y : ( 0.4f * right_y ))) * ( -1.4f / 128.0f );
			obj.mLookaroundTilt += ( target - obj.mLookaroundTilt ) * 0.0625f;
	
			// We want this to map to (-~PI, ~PI).
			target = ((float)right_x ) * ( 3.0f / 128.0f );
			obj.mLookaroundHeading	+= ( target - obj.mLookaroundHeading ) * 0.0625f;
		}
	
/*		Hey -- Let's not mask these since I need to read them in the player's s_input_logic function
		for replay stuff.  Thanks.  If it breaks something please tell me.  Love Matt
		// Mask off analog right stick input.
		handler.m_Input->MaskDigitalInput( Inp::Data::mD_R3 );
		handler.m_Input->MaskAnalogInput( Inp::Data::mA_RIGHT_X );
		handler.m_Input->MaskAnalogInput( Inp::Data::mA_RIGHT_Y );
*/
	}
}

/*****************************************************************************
**								Public Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterCam::CSkaterCam( int player )
{
	mMode	= SKATERCAM_MODE_UNDEFINED;

#ifdef	DEBUG_CAMERA
	printf ("+++ SkaterCam Created ..........................\n");				  
#endif
				  
	// Create the SLERP interpolator here.
	mpSlerp = new Mth::SlerpInterpolator;

	// Set current zoom and LERP rates.
	mCurrentZoom		= 1.0f;
	mZoomLerp			= 0.0625f;
	mLastDot			= 1.0f;

	mLerpXZ			= 0.25f;
	mLerpY			= 0.5f;
	mVertAirLerpXZ	= 1.0f;
	mVertAirLerpY	= 1.0f;
	mGrindLerp		= 0.1f;

	mLookaroundTilt		= 0.0f;
	mLookaroundHeading	= 0.0f;
	mLookaroundZoom		= 1.0f;

	Inp::Manager * inp_manager = Inp::Manager::Instance();

	// Register input task.
	m_input_handler			= new Inp::Handler< CSkaterCam >( player, s_input_logic_code, *this, Tsk::BaseTask::Node::vNORMAL_PRIORITY + 1 );
//	m_input_handler_special	= new Inp::Handler< CSkaterCam >( player, s_input_logic_code_special, *this, Tsk::BaseTask::Node::vNORMAL_PRIORITY + 1 );

	m_input_enabled = true;
	inp_manager->AddHandler( *m_input_handler );

    // clear the frame matrix
    // so that future slerps make sense
    Mth::Matrix& frame_matrix = Gfx::Camera::GetMatrix();
    frame_matrix.Ident();

	mLastActualRight 	= frame_matrix[X];
	mLastActualUp 		= frame_matrix[Y];
	mLastActualAt 		= frame_matrix[Z];
	
	Dbg_MsgAssert(false, ("CSkaterCam is not supported."));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterCam::~CSkaterCam( void )
{
#ifdef	DEBUG_CAMERA
	printf ("--- SkaterCam Destroyed ..........................\n");				  
#endif

	if( mpSlerp )
	{
		delete mpSlerp;
	}

	#if 0
	// for cutscene camera

	if( mpCopy )
	{
		Mem::Free( mpCopy );
		mpCopy = NULL;
	}
	#endif

	// Unregister input task.
	if( m_input_handler )
	{
		if( m_input_handler->InList() )
		{
			m_input_handler->Remove();
		}
		delete m_input_handler;
	}
/*	
	if( m_input_handler_special )
	{
		if( m_input_handler_special->InList() )
		{
			m_input_handler_special->Remove();
		}
		delete m_input_handler_special;
	}
*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCam::EnableInputHandler( bool enable )
{
	m_input_enabled = enable;

/*
	if( enable )
	{
		// Remove the special handler and add the standard handler.
		if( m_input_handler_special->InList() )
		{
			m_input_handler_special->Remove();
		}
		if( !m_input_handler->InList() )
		{
			Inp::Manager * inp_manager = Inp::Manager::Instance();
			inp_manager->AddHandler( *m_input_handler );
		}
	}
	else
	{
		// Remove the standard handler and add the special handler.
		if( m_input_handler->InList() )
		{
			m_input_handler->Remove();
		}
		if( !m_input_handler_special->InList() )
		{
			Inp::Manager * inp_manager = Inp::Manager::Instance();
			inp_manager->AddHandler( *m_input_handler_special );
		}

	}
*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCam::ResetLookAround( void )
{
	mLookaroundTilt		= 0.0f;
	mLookaroundHeading	= 0.0f;

	Update( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// toggle between replay modes:
void CSkaterCam::SwitchReplayMode( bool increment )  // true to increment, false to decrement
{
	
	int mode = ( int )mMode;
	if ( increment )
	{
		mode++;
	}
	else
	{
		mode--;
	}
	if ( mode < SKATERCAM_FIRST_REPLAY_MODE )
	{
		mode = SKATERCAM_LAST_REPLAY_MODE;
	}
	else if ( mode > SKATERCAM_LAST_REPLAY_MODE )
	{
		mode = SKATERCAM_FIRST_REPLAY_MODE;
	}
	SetMode( ( ESkaterCamMode )mode, 0.0f );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCam::SetMode( ESkaterCamMode mode, float time )
{
	

#if 0	// Garrett: This doesn't work because it doesn't check to see if the screen mode has changed
	if( mode == mMode )
	{
		// Nothing to do here.
		return;
	}
#endif

	Dbg_MsgAssert( mode < SKATERCAM_NUM_MODES,( "Bad mode" ));

	// Obtain details for this mode.
	float h_fov, behind, above, tilt, origin_offset, zoom_lerp;
	const char* p_name;

	// Obtain a pointer to the array entry for the desired mode. The array is now dependent
	// on which game mode is currently being played, 1 player, 2 player vert, or 2 player horiz.
	Nx::ScreenMode screen_mode = Nx::CViewportManager::sGetScreenMode();

	Script::CArray* p_array;

	switch( screen_mode )
	{
		case Nx::vSPLIT_V:
		{
			p_array = Script::GetArray( "Skater_Camera_2P_Vert_Array" );
			break;
		}

		case Nx::vSPLIT_H:
		{
			p_array = Script::GetArray( "Skater_Camera_2P_Horiz_Array" );
			break;
		}

		default:
		{			
			p_array = Script::GetArray( "Skater_Camera_Array" );
			break;
		}
	}


	// Extra check here, since the array in physics.q may change.
	Dbg_MsgAssert( mode < (int)p_array->GetSize(), ( "Bad mode" ));

	uint32 cs = p_array->GetNameChecksum( mode );
	Script::CScriptStructure* p_struct = Script::GetStructure( cs );

	mMode = mode;

	mLipTrickTilt = 0.0f;

	// Read the values from the array.
	p_struct->GetFloat( "horiz_fov",				&h_fov );
	p_struct->GetFloat( "behind",					&behind );
	p_struct->GetFloat( "above",					&above );
	p_struct->GetFloat( "tilt",						&tilt );
	p_struct->GetFloat( "origin_offset",			&origin_offset );
	p_struct->GetFloat( "lip_trick_tilt",			&mLipTrickTilt );
	p_struct->GetFloat( "lip_trick_above",			&mLipTrickAbove );

	// Get SLERP values.
	p_struct->GetFloat( "slerp",					&mSlerp );
	p_struct->GetFloat( "vert_air_slerp",			&mVertAirSlerp );
	p_struct->GetFloat( "vert_air_landed_slerp",	&mVertAirLandedSlerp );

	// Get LERP values.
	p_struct->GetFloat( "lerp_xz",					&mLerpXZ );
	p_struct->GetFloat( "lerp_y",					&mLerpY );
	p_struct->GetFloat( "vert_air_lerp_xz",			&mVertAirLerpXZ );
	p_struct->GetFloat( "vert_air_lerp_y",			&mVertAirLerpY );
	p_struct->GetFloat( "grind_lerp",				&mGrindLerp );

	// Get zoom values.
	mGrindZoom			= 1.0f;
	mLipTrickZoom		= 1.0f;
	mBigAirTrickZoom	= 1.0f;

	p_struct->GetFloat( "zoom_lerp",			&zoom_lerp );
	p_struct->GetFloat( "grind_zoom",			&mGrindZoom );
	p_struct->GetFloat( "big_air_trick_zoom",	&mBigAirTrickZoom );
	p_struct->GetFloat( "lip_trick_zoom",		&mLipTrickZoom );
	mZoomLerp			= zoom_lerp;

	// Get camera style name.
	p_struct->GetText( "name", &p_name );

	// Send the new camera mode to console line 1.
	//HUD::PanelMgr* panelMgr = HUD::PanelMgr::Instance();
	//panelMgr->SendConsoleMessage( p_name, 1 );

	// Need to calculate interpolate timers here.
	mHorizFOV		= h_fov;
	mTilt			= tilt;
	mOriginOffset	= FEET( origin_offset );		// Convert to inches.

	// Set up interpolators for those values that require it.
	if( time > 0.0f )
	{
		behind						= FEET( behind );				// Convert to inches.
		above						= FEET( above );				// Convert to inches.
		mBehindInterpolatorTime		= time;
		mBehindInterpolatorDelta	= (( behind - mBehind ) * ( SKATERCAM_INTERPOLATOR_TIMESTEP / time ));
		mAboveInterpolatorTime		= time;
		mAboveInterpolatorDelta		= (( above - mAbove ) * ( SKATERCAM_INTERPOLATOR_TIMESTEP / time ));
	}
	else
	{
		mBehind						= FEET( behind );
		mAbove						= FEET( above );
		mBehindInterpolatorTime		= 0.0f;
		mAboveInterpolatorTime		= 0.0f;
	}

	// Set focal length immediately.
	if( this )
	{
		Nx::ScreenMode	mode = Nx::CViewportManager::sGetScreenMode();

		switch( mode )
		{
			case Nx::vSPLIT_V:
			{
#ifdef __PLAT_NGC__
				//Gfx::Camera::SetFocalLength( Script::GetFloat("Skater_Cam_Horiz_FOV"), Nx::vBASE_ASPECT_RATIO );
				Gfx::Camera::SetHFOV(Script::GetFloat("Skater_Cam_Horiz_FOV") / 2.0f);
#else
				//Gfx::Camera::SetFocalLength( mFocalLength, Nx::vBASE_ASPECT_RATIO * 0.5f );
				Gfx::Camera::SetHFOV(mHorizFOV / 2.0f);
#endif // __PLAT_NGC__
				break;
			}

			case Nx::vSPLIT_H:
			{
				//Gfx::Camera::SetFocalLength( mFocalLength * 2.0f, Nx::vBASE_ASPECT_RATIO * 2.0f );
				Gfx::Camera::SetHFOV(mHorizFOV);
				break;
			}

			default:
			{			
				//Gfx::Camera::SetFocalLength( mFocalLength, Nx::vBASE_ASPECT_RATIO );
				Gfx::Camera::SetHFOV(mHorizFOV);
				break;
			}
		}
	}

	switch ( mode )
	{
		case ( SKATERCAM_MODE_REPLAY_FRONT ):
		case ( SKATERCAM_MODE_REPLAY_FRONT_ZOOM ):
			mLookaroundHeadingStartingPoint = Mth::PI;
			break;
		case ( SKATERCAM_MODE_REPLAY_LEFT ):
		case ( SKATERCAM_MODE_REPLAY_LEFT_ZOOM ):
			mLookaroundHeadingStartingPoint = Mth::PI / 2.0f;
			break;
		case ( SKATERCAM_MODE_REPLAY_BEHIND ):
		case ( SKATERCAM_MODE_REPLAY_BEHIND_ZOOM ):
			mLookaroundHeadingStartingPoint = 0;
			break;
		case ( SKATERCAM_MODE_REPLAY_RIGHT ):
		case ( SKATERCAM_MODE_REPLAY_RIGHT_ZOOM ):
			mLookaroundHeadingStartingPoint = -( Mth::PI / 2.0f );
			break;
		default:
			mLookaroundHeadingStartingPoint = 0;
			break;
	}   
}

/*******************************************************************************************************
logic for getTimeAdjustedSlerp()

If the camera is lagging a distance D behind the target, and the target is moving with velocity V, and the lerp is set to L.

Then when the camera is stable, it will be moving at the same rate as the target (V), and since the distance it will move is equal to the lerp multiplied by the distance from the target (which will be the old distance plus the new movement of the target V)

Then:

L*(D+V) = V 

L*D + L*V = V
D = V * (1 - L) / L

Assuming in the above T = 1, then if we have a variable T, the speed of the skater will be T*V, yet we want the distance (Dt) moved to remain unchanged (D), so for a time adjusted lerp Lt

Dt = T * V * (1-Lt)/Lt

Since D = Dt

V * (1 - L) / L = T * V * (1-Lt)/Lt

V cancels out, and we get

Lt = TL / (1 - L + TL)

Sanity check,  

if L is 0.25, and T is 1, then Lt = 1*0.25 / (1 - 0.25 + 1*0.25)  = 0.25
if L is 0.25, and T is 2, then Lt = 2*0.25 / (1 - 0.25 + 2*0.25)  = 0.5 / 1.25 = 0.40
if L is 0.25, and T is 5, then Lt = 5*0.25 / (1 - 0.25 + 5*0.25)  = 1.25 / 2 = 0.625

Sounds about right.

*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static float getTimeAdjustedSlerp( float slerp, float delta )
{
#if	1
	// Mick's method, tries to guarantee that the distance from the target point
	// will never alter at constant velocity, with changing frame rate
	// Lt = TL / (1 - L + TL)
	
	float t = delta * 60.0f;
	float Lt =  t * slerp / (1.0f - slerp + t * slerp);
	return Lt;	
	
#else
	// Dave's method. simulates the effect of lerp over multiple frames
	// curiously seems to be within 2% of the above method.  

	float result	= 0.0f;
	float remainder	= 1.0f;

	while( true )
	{
		if( delta >= ( 1.0f / 60.0f ))
		{
			delta -= ( 1.0f / 60.0f );

			float extra = slerp * remainder;
			remainder -= extra;
			result += extra;
		}
		else
		{
			// Partial fragment, or nothing at all?
			if( delta > 0.0f )
			{
				// The slerp this time will be reduced.
				slerp *= delta / ( 1.0f / 60.0f );

				float extra = slerp * remainder;
				remainder -= extra;
				result += extra;
			}
//			printf ("Result %f, Lt %f, diff = %f\n",result,Lt,result-Lt);
			return result;
		}
	}
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCam::SetLookaround( float heading, float tilt, float time, float zoom )
{
	mLookaroundOverride			= true;

	mLookaroundHeadingTarget	= heading;
	mLookaroundTiltTarget		= tilt;

	// Calculate delta - the amount to move in 1 second.
	if( time > 0.0f )
	{
		mLookaroundHeadingDelta		= ( heading - mLookaroundHeading ) / time;
		mLookaroundTiltDelta		= ( tilt - mLookaroundTilt ) / time;
	}

	mLookaroundDeltaTimer		= time;
	mLookaroundZoom				= zoom;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCam::ClearLookaround( void )
{
	mLookaroundOverride = false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CSkaterCam::SetShake( float duration, float vert_amp, float horiz_amp, float vert_vel, float horiz_vel )
{
	mShakeDuration			= duration;
	mShakeInitialDuration	= duration;
	mShakeMaxVertAmp		= vert_amp;
	mShakeMaxHorizAmp		= horiz_amp;
	mShakeVertVel			= vert_vel;
	mShakeHorizVel			= horiz_vel;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Added by Ken. 
bool CSkaterCam::UseVertCam( void )
{   
	// Use vert cam when in the lip state.
	if( mpSkaterStateComponent->GetState() == LIP )
	{
#ifdef	DEBUG_CAMERA
		printf ("SkaterCam Using Vert Cam as LIP\n");
#endif
		return true;
	}	
		
	if( mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT )  && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS))
	{
#ifdef	DEBUG_CAMERA
		printf ("SkaterCam Using Vert Cam as VERT_AIR\n");
#endif
		return true;
	}
	
	if( mpSkaterStateComponent->GetJumpedOutOfLipTrick())
	{
#ifdef	DEBUG_CAMERA
		printf ("SkaterCam Using Vert Cam as Jumped out of lip trick\n");
#endif
		return true;
	}

#ifdef	DEBUG_CAMERA
		printf ("Skatecam NOT using vert cam\n");
#endif


	return false;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
					 
void CSkaterCam::addShake( Mth::Vector& cam, Mth::Matrix& frame )
{
	float damping		= mShakeDuration / mShakeInitialDuration;
	float vert_angle	= mShakeDuration * mShakeVertVel * Mth::PI * 2.0f;
	float horiz_angle	= mShakeDuration * mShakeHorizVel * Mth::PI * 2.0f;
	float vert_offset	= mShakeMaxVertAmp * sinf( vert_angle ) * damping;
	float horiz_offset	= mShakeMaxHorizAmp * sinf( horiz_angle ) * damping;

	cam += frame[X] * horiz_offset;
	cam += frame[Y] * vert_offset;

	mShakeDuration		-= Tmr::FrameLength();
	if( mShakeDuration < 0.0f )
		mShakeDuration = 0.0f;
}

// updates the camera position
// returns the dist from the camera to the focus point 					 
float CSkaterCam::Update( bool instantly )
{
	
	if( instantly )
	{
		mInstantCount = 3;
	}

	if( mInstantCount > 0 )
	{
		--mInstantCount;
		instantly = true;
	}

	// Length of last frame in seconds.
	float delta_time = Tmr::FrameLength();

	if (mpSkater && mpSkater->mPaused)	  	// Mick: early out of ithe skater is paused, just allow scripted cameras
	{
		//return 100000.0f;
	}

	// Update any values we are currently interpolating.
	UpdateInterpolators();

		// Would have used a SetPos() function, but the camera update in rwviewer.cpp is spread out all over the place,
		// so it's not easy to do. We'll just store the old position before updating anything...
		Gfx::Camera::StoreOldPos();

		Mth::Matrix& frame_matrix	= Gfx::Camera::GetMatrix();

		frame_matrix[X]   		= mLastActualRight;
		frame_matrix[Y]			= mLastActualUp;
		frame_matrix[Z]			= mLastActualAt;

		Mth::Vector	current_at_vector( frame_matrix[Z][X], frame_matrix[Z][Y], frame_matrix[Z][Z] ); 

		// Obtain skater state.
		EStateType	state			= mpSkaterStateComponent->GetState();
		Mth::Matrix target;
		if( state == WALL )
		{
#ifdef	DEBUG_CAMERA
		printf ("SkaterCam Using mLastPreWallSkaterMatrix, as wallriding\n");
#endif
			target						= mLastPreWallSkaterMatrix;
		}
		else
		{
#ifdef	DEBUG_CAMERA
		printf ("SkaterCam Using GetCameraDisplayMatrix\n");
#endif
			target						= mpSkater->GetDisplayMatrix();
			mLastPreWallSkaterMatrix	= target;
		}

		// Lerp towards the required lean.
		float cam_lean = 0.0f;
		if( state == RAIL )
		{		
			// Need to translate the lean value [-4096, 4096] into a reasonable camera lean angle.
			cam_lean = GetSkaterBalanceTrickComponentFromObject(mpSkater)->mGrind.mManualLean * LEAN_TO_SKATERCAM_LEAN;
		}

		if( cam_lean != mLean )
		{
			if( instantly )
			{
				mLean = cam_lean;
			}
			else
			{
				mLean += (( cam_lean - mLean ) * mGrindLerp );
			}
		}
		

		Mth::Vector skater_up;
					
		if (state != GROUND)
		{
			skater_up	= target[Y];
		}
		else
		{
			skater_up	= mpSkaterPhysicsComponent->m_display_normal;
		}

//		Gfx::AddDebugLine( p_skater->m_pos, ( target[X] * 72.0f ) + p_skater->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 4 );
//		Gfx::AddDebugLine( p_skater->m_pos, ( target[Y] * 72.0f ) + p_skater->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 4 );
//		Gfx::AddDebugLine( p_skater->m_pos, ( target[Z] * 72.0f ) + p_skater->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 );
		

		// Use vel for forward facing, if moving in xz plane.
		Mth::Vector vel_xz( mpSkater->GetVel().GetX(), 0.0f, mpSkater->GetVel().GetZ());

		bool use_vert_cam = UseVertCam();

		// use velocity if physics not paused, and if we are moving fast enough, or if we are doing vert cam
		// or if we are doing spine physics
		if( /* !mpSkater->m_physics_paused && */ (vel_xz.Length() > 0.1f || use_vert_cam  || mpSkaterStateComponent->GetFlag( SPINE_PHYSICS )))
		{
			if( use_vert_cam )
			{
				// If it's vert, then set target direction to near straight down.
				target[Z].Set( 0.0f, -1.0f, 0.0f );

				// Zero lookaround when in vert air (and not in lookaround locked or override mode, or doing a lip trick).
				if( !mLookaroundLock && !mLookaroundOverride && ( state != LIP ))
				{
					mLookaroundTilt		= 0.0f;
					mLookaroundHeading	= 0.0f;
				}
			}
			else
			{
				
			
				target[Z]		= mpSkater->GetVel();
				#if 1	// for use with non-straight spine transfer physics
				if (mpSkaterStateComponent->GetFlag( SPINE_PHYSICS ))
				{
					// just use the up velocity, plus the velocity over the spine
					// will be straight down when coing down the other side
					target[Z][X] = 0.0f;
					target[Z][Z] = 0.0f;
					target[Z] += mpSkaterStateComponent->GetSpineVel();				
				}
				#endif
				target[Z].Normalize();

//				printf ("Using Velocity (%f, %f, %f)\n", target[Z][X],target[Z][Y], target[Z][Z]);

//				Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 128, 128, 128 ), MAKE_RGB( 128, 128, 128 ), 4 );

				// Flatten out the velocity component perpendicular to the ground. However, if the skater is in the air, the
				// m_last_normal will contain the last ground normal, which we don't want, so set it to be 'up'.
				float			dot;
				Mth::Vector		normal;

				if( state == LIP )
				{
					
				
					// Patch for spine physics
					// only reduce component perpendicular to plane by 60%
					// so we still get some up/down camera movmeent 
					// only do this when moving down though, otherwise it looks poo
					if (mpSkaterStateComponent->GetFlag(SPINE_PHYSICS) && target[Z][Y] < 0.0f)
					{
//						normal	= mpSkaterPhysicsComponent->m_current_normal;
						normal.Set( 0.0f, 1.0f, 0.0f );
						//dot		= (( target[Z].GetX() * normal.GetX() ) + ( target[Z].GetY() * normal.GetY() ) + ( target[Z].GetZ() * normal.GetZ() ));
						//dot		= dot * 0.8f;
						dot =  0.7f * Mth::DotProduct(target[Z],normal);	// change this to 0.8 to look down when coming down other side of a spine
					}
					else
					{
						normal.Set( 0.0f, 1.0f, 0.0f );
						dot		= target[Z].GetY();
					}
					// Set world up as the target vector.
					target[Y].Set( 0.0f, 1.0f, 0.0f );
				}
				else if( state == WALL )
				{
					normal.Set( 0.0f, 1.0f, 0.0f );
					dot		= target[Z].GetY();
				}
				else
				{
					normal	= mpSkaterPhysicsComponent->m_current_normal;
					//dot		= (( target[Z].GetX() * normal.GetX() ) + ( target[Z].GetY() * normal.GetY() ) + ( target[Z].GetZ() * normal.GetZ() ));
					//dot		= dot * 0.8f;
					dot =  0.8f * Mth::DotProduct(target[Z],normal);
				}

				//target[Z].Set(	target[Z].GetX() - ( dot * normal.GetX() ), target[Z].GetY() - ( dot * normal.GetY() ), target[Z].GetZ() - ( dot * normal.GetZ() ));
				target[Z] -= normal * dot;
				
				
				target[Z].Normalize();

//				Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 256, 256, 256 ), MAKE_RGB( 256, 256, 256 ), 4 );
			}

			// We need to orthonormalize in a specific order, as when the velocity is going backwards, then 
			// the X orient will be wrong, and so plonk the skater upside down.
			target[X]			= Mth::CrossProduct( target[Y], target[Z] );
			target[X].Normalize();
			target[Y]			= Mth::CrossProduct( target[Z], target[X] );
			target[Y].Normalize();

//			Gfx::AddDebugLine( mpSkater->m_pos, ( target[X] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 4 );
//			Gfx::AddDebugLine( mpSkater->m_pos, ( target[Y] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 4 );
//			Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 );
		}    							
							
		// Tilt further if in regular air (going off a big jump, for example), or doing a lip trick.
		if( state == LIP )
		{
			mTiltAddition += TILT_INCREMENT * Tmr::FrameRatio();
			if( mTiltAddition > TILT_MAX )
			{
				mTiltAddition = TILT_MAX;
			}				
		}
		else if( mTiltAddition > 0.0f )
		{
			mTiltAddition -= TILT_RESTORE * Tmr::FrameRatio();
			if( mTiltAddition < 0.0f )
			{
				mTiltAddition = 0.0f;
			}				
		}
		else if( mTiltAddition < 0.0f )
		{
			mTiltAddition += TILT_RESTORE * Tmr::FrameRatio();
			if( mTiltAddition > 0.0f )
			{
				mTiltAddition = 0.0f;
			}				
		}

		// PJR: Needed to do this to fix a compiler crash on NGC. Hopefully SN will
		// fix this & I can revert this hack...
		target.RotateYLocal( mLookaroundHeading + mLookaroundHeadingStartingPoint );

		// Now tilt the matrix down a bit.
		if( state == LIP )
		{
//			target.RotateYLocal( 0.8f );
//			target.RotateXLocal( mLipTrickTilt + mTiltAddition );
			_xrotlocal ( target, mLipTrickTilt + mTiltAddition );
		}					
		else if( !(mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT )  && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS) ))
		{
//			target.RotateXLocal( mTilt + mTiltAddition );
			_xrotlocal ( target, mTilt + mTiltAddition );
		}					

		// Now adjust for 'lookaround'.
//		target.RotateXLocal( mLookaroundTilt );
		_xrotlocal ( target, mLookaroundTilt );

		static float lip_rotate = 0.0f;

		if( state == LIP )
		{
			lip_rotate = 1.5707963f;

			if( mLipSideDecided == 0 )
			{
				target.RotateY( lip_rotate );

				// Do collision check.
				CFeeler feeler;

				Mth::Vector horiz_z( target[Z][X], 0.0f, target[Z][Z] );
				horiz_z.Normalize();
				Mth::Vector a = mpSkater->m_pos - ( target[Z] * 3.0f );
				Mth::Vector b = a - ( horiz_z * 72.0f );

				feeler.SetIgnore(0, mFD_CAMERA_COLLIDABLE);
				bool collision = feeler.GetCollision(a, b, true);
				if( !collision )
				{
					// Side 1 is fine.
					mLipSideDecided = 1;
				}
				else
				{
					float dist = feeler.GetDist();
					target.RotateY( -lip_rotate * 2.0f );
					horiz_z.Set( target[Z][X], 0.0f, target[Z][Z] );
					horiz_z.Normalize();
					a = mpSkater->m_pos - ( target[Z] * 3.0f );
					b = a - ( horiz_z * 72.0f );

					collision = feeler.GetCollision(a, b, true);
					if( !collision )
					{
						// Side 2 is fine.
						mLipSideDecided = 2;
					}
					else
					{
						if( feeler.GetDist() < dist )
						{
							// Side 2 is better than side 1.
							mLipSideDecided = 2;
						}
						else
						{
							// Side 1 is better than side 2.
							target.RotateY( lip_rotate * 2.0f );
							mLipSideDecided = 1;
						}
					}
				}
			}
			else
			{
				if( mLipSideDecided == 1 )
				{
					target.RotateY( lip_rotate );
				}
				else
				{
					target.RotateY( -lip_rotate );
				}
			}
		}
		else
		{
			mLipSideDecided = 0;
		}					

		// Set skater flag to indicate when the liptrick camera has spun to the opposite side.
		if( mpSkater && ( mLipSideDecided == 2 ))
		{
			mpSkater->mScriptFlags |= ( 1 << Script::GetInteger( 0x16b8e4cb /* "FLAG_SKATER_LIPTRICK_CAM_REVERSED" */ ));
		}
		else
		{
			mpSkater->mScriptFlags &= ~( 1 << Script::GetInteger( 0x16b8e4cb /* "FLAG_SKATER_LIPTRICK_CAM_REVERSED" */ ));
		}
		
//		Gfx::AddDebugLine( mpSkater->m_pos, ( target[X] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 4 );
//		Gfx::AddDebugLine( mpSkater->m_pos, ( target[Y] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 128, 0 ), MAKE_RGB( 0, 128, 0 ), 4 );
//		Gfx::AddDebugLine( mpSkater->m_pos, ( target[Z] * 72.0f ) + mpSkater->m_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 );

		// Why doing this, when setting below?
		Mth::Matrix t_matrix(0.0f, 0.0f, 0.0f);
		
		// Since camera points in -Z, but player in +Z, we must negate the X and Z axes
        t_matrix[X][X]		= -target[0][0];
		t_matrix[X][Y]		= -target[0][1];
		t_matrix[X][Z]		= -target[0][2];
		t_matrix[X][W]		= 0.0f;
	
		t_matrix[Y][X]		= target[1][0];
		t_matrix[Y][Y]		= target[1][1];
		t_matrix[Y][Z]		= target[1][2];
		t_matrix[Y][W]		= 0.0f;
	
		t_matrix[Z][X]		= -target[2][0];
		t_matrix[Z][Y]		= -target[2][1];
		t_matrix[Z][Z]		= -target[2][2];
		t_matrix[Z][W]		= 0.0f;
			
        t_matrix[W][X]		= 0.0f;
		t_matrix[W][Y]		= 0.0f;
		t_matrix[W][Z]		= 0.0f;
		t_matrix[W][W]		= 1.0f;
        
		frame_matrix[W][X]	= 0.0f;
		frame_matrix[W][Y]	= 0.0f;
		frame_matrix[W][Z]	= 0.0f;
		frame_matrix[W][W]	= 1.0f;


		// if we want an instant update, then just move it
		if (instantly)
		{
			frame_matrix = t_matrix;
		}


		float dotx = Mth::DotProduct( t_matrix[X], frame_matrix[X] );
		float doty = Mth::DotProduct( t_matrix[Y], frame_matrix[Y] );
		float dotz = Mth::DotProduct( t_matrix[Z], frame_matrix[Z] );

#define CAMERA_SLERP_STOP 0.9999f
		
		if( dotx > CAMERA_SLERP_STOP && doty > CAMERA_SLERP_STOP && dotz > CAMERA_SLERP_STOP )
		{
			// Do nothing, camera has reached the target so we just leave the frame matrix exactly where it is.
//		    frame_matrix = t_matrix;
        }
		else
		{
			// Initialise the SLERP interpolator, with the current matrix as the start, and the ideal
			// matrix as the end (target).

            // GARY:  is this right?  i assume p_matrix = &t_matrix
			mpSlerp->setMatrices( &frame_matrix, &t_matrix );

			// Copy flags and shit (why copying all this when most of the values will be overwritten?)
			Mth::Matrix stored_frame;
            stored_frame = frame_matrix;
            frame_matrix = t_matrix;			

			// Continue to slerp towards the target, updating frame_matrix with the slerped values.
			if( !(mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS)))
			{
				// If the skater has just landed from big air, use the big air land slerp value.
				if(( mpSkaterStateComponent->GetState() == GROUND ) && ( mVertAirLandedTimer >= delta_time ))
				{
					mVertAirLandedTimer -= delta_time;
					mpSlerp->getMatrix( &frame_matrix, getTimeAdjustedSlerp( mVertAirLandedSlerp, delta_time ));
				}
				else
				{
					// Clear the vert air landed timer.
					mVertAirLandedTimer = 0.0f;

					mpSlerp->getMatrix( &frame_matrix, getTimeAdjustedSlerp( mSlerp, delta_time ));

					// At this point we can check to see the angle that the camera's 'at' vector moved through this frame.
					// If this angle is too large, we need to limit it, regardless of the slerp. (This is primarily to avoid
					// the nasty jerk when transitioning rails etc.
					float this_dot = ( current_at_vector.GetX() * frame_matrix[Mth::AT][X] ) 
                        + ( current_at_vector.GetY() * frame_matrix[Mth::AT][Y] ) 
                        + ( current_at_vector.GetZ() * frame_matrix[Mth::AT][Z] ); 

					// Dot values will be in the range [0,1], with 1 being no change.
					if( this_dot < ( mLastDot * 0.9998f ))
					{
						// The angular change this frame is too big. Need to recalculate with an adjusted slerp value.
						float new_slerp = mSlerp * ( this_dot / ( mLastDot * 0.9998f ));
						mpSlerp->setMatrices( &stored_frame, &t_matrix );
						mpSlerp->getMatrix( &frame_matrix, getTimeAdjustedSlerp( new_slerp, delta_time ));

						this_dot = mLastDot * 0.9998f;
					}
                    
					mLastDot = this_dot;
				}
			}
			else
			{
				mpSlerp->getMatrix( &frame_matrix, getTimeAdjustedSlerp( mVertAirSlerp, delta_time ));

				// Set the vert air landed timer to the max, since the skater is in vert air.
				mVertAirLandedTimer = VERT_AIR_LANDED_TIME;
			}						
		}		

		// At this point, frame_matrix is valid to store.
		mLastActualRight	= frame_matrix[X];
		mLastActualUp		= frame_matrix[Y];
		mLastActualAt		= frame_matrix[Z];

		// Set camera position to be the same as the skater.
		Mth::Vector	cam_pos = GetTripodPos( instantly );

		// If in the air doing a trick, we slowly reduce the behind value to 'zoom' in on the skater.
		float above, behind;
		if (instantly)
		{
			float temp = mZoomLerp;
			mZoomLerp = 1.0f;					// fully lerp instantly
			CalculateZoom( &above, &behind );
			mZoomLerp = temp;
		}
		else
		{
			CalculateZoom( &above, &behind );
		}



		

//        printf( "above = %f, behind %f\n", above, behind );
		
		//Dbg_Message("Camera before: pos (%5.1f,%5.1f,%5.1f)", cam_pos[X], cam_pos[Y], cam_pos[Z]);

		// Note we use the camera's at vector, as this will keep the skater in the middle of the frame.
		//cam_pos -= frame_matrix[Z] * behind;
		cam_pos += frame_matrix[Z] * behind;
		
		// Move camera along the Skater's up vector.
//		cam_pos += mpSkater->GetMatrix()[Y] * above;
		cam_pos += skater_up * above;

		Mth::Vector	actual_focus_pos = mpSkater->m_pos + ( skater_up * above );


//		Gfx::AddDebugLine( mpSkater->m_pos, actual_focus_pos, MAKE_RGB( 0, 0, 128 ), MAKE_RGB( 0, 0, 128 ), 4 );
													  
//		Dbg_Message("Matrix Z  (%5.4f,%5.4f,%5.4f)", frame_matrix[Z][X], frame_matrix[Z][Y], frame_matrix[Z][Z]);
//		Dbg_Message("Matrix UP (%5.4f,%5.4f,%5.4f)", skater_up[X], skater_up[Y], skater_up[Z]);
//		Dbg_Message("Above %f Behind %f", above, behind);
//		Dbg_Message("focus pos (%5.1f,%5.1f,%5.1f)", actual_focus_pos[X], actual_focus_pos[Y], actual_focus_pos[Z]);
//		Dbg_Message("Camera after: pos (%5.1f,%5.1f,%5.1f)", cam_pos[X], cam_pos[Y], cam_pos[Z]);

		// Reorient the camera to look directly at the skater. We don't want to look at the interpolated position,
		// since this can lead to nasty jerkiness, especially on rails etc.
		

		target[Z].Set( actual_focus_pos.GetX() - cam_pos.GetX(), actual_focus_pos.GetY() - cam_pos.GetY(), actual_focus_pos.GetZ() - cam_pos.GetZ());
		target[Z].Normalize();

		// Read back the Y from the current matrix.
		target[Y][0]	= frame_matrix[Y][X];
		target[Y][1]	= frame_matrix[Y][Y];
		target[Y][2]	= frame_matrix[Y][Z];

		// Generate new orthonormal X and Y axes.
		target[X]		= Mth::CrossProduct( target[Y], target[Z] );
		target[X].Normalize();
		target[Y]		= Mth::CrossProduct( target[Z], target[X] );
		target[Y].Normalize();

		// Here is where lean may safely be applied without moving the focus position on screen.
		if( mLean != 0.0f )
		{
			target[Y].Rotate( target[Z], mLean );
			target[X].Rotate( target[Z], mLean );
		}

		// Write back into camera matrix.
		// Since camera points in -Z, but player in +Z, we must negate the X and Z axes
        frame_matrix[X][X]		= -target[0][0];
        frame_matrix[X][Y]		= -target[0][1];
        frame_matrix[X][Z]		= -target[0][2];
        frame_matrix[X][W]		= 0.0f;

        frame_matrix[Y][X]		= target[1][0];
        frame_matrix[Y][Y]		= target[1][1];
        frame_matrix[Y][Z]		= target[1][2];
        frame_matrix[Y][W]		= 0.0f;

        frame_matrix[Z][X]		= -target[2][0];
        frame_matrix[Z][Y]		= -target[2][1];
        frame_matrix[Z][Z]		= -target[2][2];
        frame_matrix[Z][W]		= 0.0f;

		// Update the position if there is a shake active.
		if(	mShakeDuration > 0.0f )
		{
			addShake( cam_pos, frame_matrix );
		}
		
		// Now do collision detection.
		Mth::Vector at_pos = cam_pos - ((Mth::Vector)( frame_matrix[Z] )) * behind;

		// camera collision detection
		
		if (mpSkaterPhysicsComponent->mp_movable_contact_component->GetContact() && state == LIP)
		{
			// no camera collision if doing a lipt trick on a moving object
			//printf ("Lip trick on moving object\n");
			
		}
		else
		{
			CFeeler feeler;
			// Ignore faces with mFD_NON_COLLIDABLE set, and ignore faces with mFD_CAMERA_COLLIDABLE not set.
			feeler.SetIgnore(0, mFD_CAMERA_COLLIDABLE);
			bool collision = feeler.GetCollision(at_pos, cam_pos, true);
			if( collision )
			{
				
				//Gfx::AddDebugLine( at_pos, cam_pos, MAKE_RGB( 128, 0, 0 ), MAKE_RGB( 128, 0, 0 ), 2000 );
	
				// Limits the camera to getting nearer than 11.9 inches to the focus point.
				float distance = feeler.GetDist();
				if(( behind * distance * 0.9f ) < 11.9f )
				{
					distance = 11.9f / ( behind * 0.9f );
				}
				
	
				cam_pos = at_pos + (( cam_pos - at_pos ) * ( distance * 0.9f ));
			}
	
#			ifdef __PLAT_NGPS__
			// Now do two additional checks 1 foot to either side of the camera.
			Mth::Vector	left( frame_matrix[X][X], 0.0f, frame_matrix[X][Z], 0.0f );
			left.Normalize( 8.0f );
			left += cam_pos;
	
			collision = feeler.GetCollision(cam_pos, left, true);
			if( collision )
			{
				left		   -= feeler.GetPoint();
				cam_pos		   -= left;
			}
			else
			{
				Mth::Vector	right( -frame_matrix[X][X], 0.0f, -frame_matrix[X][Z], 0.0f );
				right.Normalize( 8.0f );
				right += cam_pos;
	
				collision = feeler.GetCollision(cam_pos, right, true);
				if( collision )
				{
					right		   -= feeler.GetPoint();
					cam_pos		   -= right;
				}
			}
	
			if(collision )
			{
				// Re-orient the camera again.
				target[Z].Set( actual_focus_pos.GetX() - cam_pos.GetX(), actual_focus_pos.GetY() - cam_pos.GetY(), actual_focus_pos.GetZ() - cam_pos.GetZ());
				target[Z].Normalize();
	
				// Read back the Y from the current matrix.
	//			target[Y][0]	= p_frame_matrix->up.x;
	//			target[Y][1]	= p_frame_matrix->up.y;
	//			target[Y][2]	= p_frame_matrix->up.z;
	
				// Generate new orthonormal X and Y axes.
				target[X]		= Mth::CrossProduct( target[Y], target[Z] );
				target[X].Normalize();
				target[Y]		= Mth::CrossProduct( target[Z], target[X] );
				target[Y].Normalize();
	
				// Write back into camera matrix.
				frame_matrix[X][X]	= -target[0][0];
				frame_matrix[X][Y]	= -target[0][1];
				frame_matrix[X][Z]	= -target[0][2];
				frame_matrix[Y][X]	= target[1][0];
				frame_matrix[Y][Y]	= target[1][1];
				frame_matrix[Y][Z]	= target[1][2];
				frame_matrix[Z][X]	= -target[2][0];
				frame_matrix[Z][Y]  = -target[2][1];
				frame_matrix[Z][Z]	= -target[2][2];
			}
#			endif
		}

		cam_pos[W] = 1.0f;
		//Dbg_MsgAssert(cam_pos[W] == 1.0f, ("cam_pos W is %f, not 1.0", cam_pos[W]));
		Gfx::Camera::SetPos(cam_pos);

		// store the position of the camera in CSkaterCam also (used for proximity nodes)
		m_pos = cam_pos;
		Mth::Vector		to_target = at_pos - cam_pos;
				
				
//		Dbg_Message("Camera Final: pos (%5.1f,%5.1f,%5.1f)", cam_pos[X], cam_pos[Y], cam_pos[Z]);

		
		
		return to_target.Length();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Mth::Vector CSkaterCam::GetTripodPos( bool instantly )
{
	float		lerp_xz;
	float		lerp_y;
	float		delta		= Tmr::FrameLength();
	Mth::Vector now			= mpSkater->m_pos;

	// The tripod pos is always *tending* towards the skater position, but the rate at which it does so is
	// definable, to allow for some lag, which improves the feeling of speed.
	if( mLerpReductionTimer > 0.0f )
	{
		mLerpReductionTimer -= delta;
		if( mLerpReductionTimer > 0.0f )
		{
			mLerpReductionMult += ( mLerpReductionDelta * delta );
		}
		else
		{
			mLerpReductionMult	= 1.0f;
			mLerpReductionTimer	= 0.0f;
		}
	}
	else
	{
		mLerpReductionMult	= 1.0f;
	}

	if(( mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS)))
	{
		#ifdef	DEBUG_CAMERA
		printf ("Skatercam using mVertAirLerp\n");
		#endif

		lerp_xz	= getTimeAdjustedSlerp( mVertAirLerpXZ * mLerpReductionMult, delta );
		lerp_y	= getTimeAdjustedSlerp( mVertAirLerpY * mLerpReductionMult, delta );
	}
	else
	{
		lerp_xz	= getTimeAdjustedSlerp( mLerpXZ * mLerpReductionMult, delta );
		lerp_y	= getTimeAdjustedSlerp( mLerpY * mLerpReductionMult, delta );

		// Added the following check (Dave 7/16/02) to help eliminate the camera rattle when snapping on/off a curb.
		// The flag is set for one frame only.
		if( mpSkaterStateComponent->GetFlag( SNAPPED_OVER_CURB ))
		{
			lerp_y = 1.0f;
		}
	}

	if( instantly )
	{
		mLastTripodPos = now;
	}
	else
	{
		mLastTripodPos.Set( mLastTripodPos.GetX() + (( now.GetX() - mLastTripodPos.GetX() ) * lerp_xz ),
							mLastTripodPos.GetY() + (( now.GetY() - mLastTripodPos.GetY() ) * lerp_y ),
							mLastTripodPos.GetZ() + (( now.GetZ() - mLastTripodPos.GetZ() ) * lerp_xz ));
	}

	return mLastTripodPos;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCam::CalculateZoom( float* p_above, float* p_behind )
{
	mTargetZoom = 1.0f;

	// Deal with zoom for Big Air. Set the flag only when a trick is started.
	if( !mBigAirTrickZoomActive && (mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS)) && mpSkaterStateComponent->DoingTrick() )
	{
		mBigAirTrickZoomActive = true;
	}
	else if( !(mpSkaterStateComponent->GetFlag( VERT_AIR ) && !mpSkaterStateComponent->GetFlag( CAN_BREAK_VERT ) && !mpSkaterStateComponent->GetFlag(SPINE_PHYSICS)))
	{
		// Must have landed.
		mBigAirTrickZoomActive = false;
	}

	if( mBigAirTrickZoomActive )
	{
		mTargetZoom = mBigAirTrickZoom;
	}
	else
	{
		EStateType state = mpSkaterStateComponent->GetState();

		// If the skater is grinding, zoom also.
		if( state == RAIL )
		{
			mTargetZoom = mGrindZoom;
		}
		else if( state == LIP )
		{
			mTargetZoom = mLipTrickZoom;
		}
		else if( state == LIP )
		{
			mTargetZoom = mLipTrickZoom;
		}
	}

	
	// If lookaround override is set, factor in the lookaround override zoom.
	if( mLookaroundOverride && ( mLookaroundZoom != 1.0f ))
	{
		mCurrentZoom = mTargetZoom;
		mCurrentZoom *= mLookaroundZoom;
	}
	else
	{
		mCurrentZoom += (( mTargetZoom - mCurrentZoom ) * mZoomLerp );
	}
	
	*p_behind = mBehind * mCurrentZoom;


	// Behind is also shortened when the lookaround camera is tilting upwards.
	if( mLookaroundTilt < 0.0f )
	{
		float max_tilt = 3.14f * 0.2f;
		*p_behind = *p_behind * ( 0.4f + ( 0.6f * (( max_tilt + mLookaroundTilt ) / max_tilt )));
	}


	// Use lip_trick_above when doing a lip trick.
	float above_val = mAbove;
	if( mpSkaterStateComponent->GetState() == LIP )
	{
		above_val = mLipTrickAbove;
	}
	

	// Figure above tending towards the perfect above, if zoom is < 1.0.
	if( mCurrentZoom < 1.0f )
	{
		*p_above = PERFECT_ABOVE + (( above_val - PERFECT_ABOVE ) * mCurrentZoom );
	}
	else
	{
		*p_above = above_val;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCam::UpdateInterpolators( void )
{
	float delta_time = Tmr::FrameLength();

	if( mLookaroundOverride )
	{
		if( mLookaroundDeltaTimer > 0.0f )
		{
			if( mLookaroundDeltaTimer > delta_time )
			{
				mLookaroundHeading		+= mLookaroundHeadingDelta * delta_time;
				mLookaroundTilt			+= mLookaroundTiltDelta * delta_time;
				mLookaroundDeltaTimer	-= delta_time;
			}
			else
			{
				mLookaroundHeading		= mLookaroundHeadingTarget;
				mLookaroundTilt			= mLookaroundTiltTarget;
				mLookaroundDeltaTimer	= 0.0f;
			}
		}
	}

	if( mBehindInterpolatorTime > 0.0f )
	{
		mBehindInterpolatorTime -= SKATERCAM_INTERPOLATOR_TIMESTEP;
		mBehind += mBehindInterpolatorDelta;		
	}
	else
	{
		mBehindInterpolatorTime = 0.0f;
	}

	if( mAboveInterpolatorTime > 0.0f )
	{
		mAboveInterpolatorTime -= SKATERCAM_INTERPOLATOR_TIMESTEP;
		mAbove += mAboveInterpolatorDelta;		
	}
	else
	{
		mAboveInterpolatorTime = 0.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCam::SetLerpReductionTimer( float time )
{
	if( time > 0.0f )
	{
		mLerpReductionTimer	= time;
		mLerpReductionDelta	= 1.0f / time;
		mLerpReductionMult	= 0.0f;
	}
	else
	{
		mLerpReductionTimer	= 0.0f;
		mLerpReductionDelta	= 0.0f;
		mLerpReductionMult	= 1.0f;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCam::SetSkater( CSkater* skater )
{
	mpSkater = skater;
	Dbg_Assert(mpSkater);
	
	mpSkaterPhysicsComponent = GetSkaterCorePhysicsComponentFromObject(skater);
	Dbg_Assert(mpSkaterPhysicsComponent);
	
	mpSkaterStateComponent = GetSkaterStateComponentFromObject(skater);
	Dbg_Assert(mpSkaterStateComponent);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkater* CSkaterCam::GetSkater( void )
{
	return mpSkater;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCam::BindToController( int controller )
{
	// swap
	Spt::SingletonPtr< Inp::Manager > inp_manager;
	inp_manager->ReassignHandler( *m_input_handler, controller );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Obj


================================================
FILE: Code/Sk/Objects/skatercam.h
================================================
//****************************************************************************
//* MODULE:         Gfx
//* FILENAME:       skatercam.cpp
//* OWNER:          Dave Cowling
//* CREATION DATE:  02/05/2001
//****************************************************************************

#ifndef __OBJECTS_SKATERCAM_H
#define __OBJECTS_SKATERCAM_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
    #include 
#endif

#include 
#include 
#include 

#include 

/*****************************************************************************
**							Forward Declarations							**
*****************************************************************************/

namespace Mth
{
    class SlerpInterpolator;
};

#if 0
namespace NxPlugin
{
	class CameraHeader;
};
#endif
                   
namespace Obj
{
    class CSkater;
    class CSkaterCorePhysicsComponent;
    class CSkaterStateComponent;

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class CSkaterCam : public Gfx::Camera
{
public:
	enum ESkaterCamMode
	{
		SKATERCAM_MODE_UNDEFINED		= 0,
		SKATERCAM_MODE_FIRST_VALID		= 1,
		SKATERCAM_MODE_NORMAL_NEAR		= 1,
		SKATERCAM_MODE_NORMAL_MEDIUM,
		SKATERCAM_MODE_NORMAL_FAR,
		SKATERCAM_MODE_NORMAL_MEDIUM_LTG,
        SKATERCAM_NUM_NORMAL_MODES,
		SKATERCAM_FIRST_REPLAY_MODE = SKATERCAM_NUM_NORMAL_MODES,
		SKATERCAM_MODE_REPLAY_FRONT = SKATERCAM_NUM_NORMAL_MODES,
		SKATERCAM_MODE_REPLAY_FRONT_ZOOM,
        SKATERCAM_MODE_REPLAY_LEFT,
		SKATERCAM_MODE_REPLAY_LEFT_ZOOM,
		SKATERCAM_MODE_REPLAY_BEHIND,
		SKATERCAM_MODE_REPLAY_BEHIND_ZOOM,
		SKATERCAM_MODE_REPLAY_RIGHT,
		SKATERCAM_MODE_REPLAY_RIGHT_ZOOM,
		SKATERCAM_LAST_REPLAY_MODE = SKATERCAM_MODE_REPLAY_RIGHT_ZOOM,
		SKATERCAM_NUM_MODES,
	};

public:

					CSkaterCam( int player );
					~CSkaterCam( void );
	bool			UseVertCam();
	float			Update( bool instantly = false );
	bool			IsAnimFinished( void );
	bool			IsAnimHeld( void );
	void			SetAnimSkippable( bool skippable );
	void			SwitchReplayMode( bool increment );
	void			SetMode( ESkaterCamMode mode, float time = 1.0f );
	int				GetMode( void )								{ return (int)mMode; }
	void			SetLookaround( float heading, float tilt, float time, float zoom = 1.0f );
	void			ClearLookaround( void );
	void			ClearLookaroundLock( void )							{ mLookaroundLock = false; }
	void			SetShake( float duration, float vert_amp, float horiz_amp, float vert_vel, float horiz_vel );
	void			EnableInputHandler( bool enable );
	void			SetSkater( CSkater* skater );
	void			SetLerpReductionTimer( float time );
	CSkater*		GetSkater( void );
	void 			ResetLookAround( void );

	void			BindToController( int controller );

	// needed for proxim.cpp (each camera stores its position 
	// after writing that position to render camera)
//	Mth::Vector		m_pos;

	bool			mSkipButton;
	bool			mRightButton;
	int				mRightX;
	int				mRightY;
	ESkaterCamMode	mMode;
	
private:

	Mth::Vector		GetTripodPos( bool instantly = false );
	void			UpdateInterpolators( void );
	void			CalculateZoom( float* p_above, float* p_behind );
	void			addShake( Mth::Vector& cam, Mth::Matrix& frame );

	Mth::SlerpInterpolator*	mpSlerp;

	CSkater*		mpSkater;
	CSkaterCorePhysicsComponent*	mpSkaterPhysicsComponent;
	CSkaterStateComponent*	mpSkaterStateComponent;

	float			mLastDot;
	Mth::Vector		mLastTripodPos;

	static						Inp::Handler< CSkaterCam >::Code s_input_logic_code;
	Inp::Handler< CSkaterCam >*	m_input_handler;
//	static						Inp::Handler< CSkaterCam >::Code s_input_logic_code_special;
//	Inp::Handler< CSkaterCam >*	m_input_handler_special;

	bool			m_input_enabled;
	
	int				mInstantCount;				// Used to maintain the 'instantly' flag during update for several frames.

	float			mHorizFOV;

	Mth::Vector		mLastActualRight;			// Used because the frame is adjusted after focus LERPING.
	Mth::Vector		mLastActualUp;
	Mth::Vector		mLastActualAt;

	Mth::Matrix		mLastPreWallSkaterMatrix;	// Used because we don't want to use the skater's matrix during wall rides.

	float			mBehind;
	float			mBehindInterpolatorTime;
	float			mBehindInterpolatorDelta;

	float			mAbove;
	float			mAboveInterpolatorTime;
	float			mAboveInterpolatorDelta;

	bool			mBigAirTrickZoomActive;
	float			mCurrentZoom;
	float			mTargetZoom;
	float			mZoomLerp;				// LERP rate for zoom.
	float			mBigAirTrickZoom;		// Target zoom for big air trick.
	float			mGrindZoom;				// Target zoom for grind.
	float			mLipTrickZoom;			// Target zoom for lip trick.
	int				mLipSideDecided;

	float			mVertAirLandedTimer;	// Defines how long the vert air landed slerp gets applied.

	float			mLerpReductionTimer;
	float			mLerpReductionDelta;
	float			mLerpReductionMult;

	bool			mLookaroundLock;
	bool			mLookaroundOverride;	// For when the designer is scripting the lookaround values.
	float			mLookaroundZoom;		// Allows designers to adjust the zoom when overrideing the lookaround values.
	float			mLookaroundTilt;
	float			mLookaroundHeading;
	float			mLookaroundHeadingStartingPoint;
	float			mLookaroundTiltTarget;
	float			mLookaroundHeadingTarget;
	float			mLookaroundTiltDelta;
	float			mLookaroundHeadingDelta;
	float			mLookaroundDeltaTimer;

	float			mTilt;
	float			mTiltAddition;
	float			mLipTrickTilt;
	float			mLipTrickAbove;
	float			mSlerp;
	float			mVertAirSlerp;
	float			mVertAirLandedSlerp;
	float			mOriginOffset;

	float			mShakeDuration;			// Shake duration in seconds (0.0 means no shake).
	float			mShakeInitialDuration;
	float			mShakeMaxVertAmp;
	float			mShakeMaxHorizAmp;
	float			mShakeVertVel;
	float			mShakeHorizVel;
	
	float			mLean;					// For lean during grind imbalance.

	float			mLerpXZ;
	float			mLerpY;
	float			mVertAirLerpXZ;
	float			mVertAirLerpY;
	float			mGrindLerp;
};
	
/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_SKATERCAM_H


================================================
FILE: Code/Sk/Objects/skatercareer.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Objects (OBJ) 											**
**																			**
**	File name:		objects/SkaterCareer.cpp								**
**																			**
**	Created: 		21/5/01 - Mick (Based on SkaterApperance.cpp by gj)		**
**																			**
**	Description:	Skater career code 										**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


bool g_CheatsEnabled = false;

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Obj
{


static bool						s_is_competition[CSkaterCareer::vMAX_CAREER_LEVELS];
	
	
	

int CountOnes(uint32 v)
{
	

	int NumOnes=0;
	for (int i=0; i<32; ++i)
	{
		if (v&1)
		{
			++NumOnes;
		}
		v>>=1;
	}		
	return NumOnes;
}

int CountOnes(uint64 v)
{
	

	int NumOnes=0;
	for (int i=0; i<64; ++i)
	{
		if (v&1)
		{
			++NumOnes;
		}
		v>>=1;
	}		
	return NumOnes;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CSkaterCareer::CSkaterCareer()
{
   
	for (unsigned int i = 0; iReset();
		}
	}
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCareer::Reset()
{
	
	
	Init(false);
	
	return true;
}


/******************************************************************/
// Return the gap checklist, either for the current level
// of for the level specified
					 
					 
Obj::CGapChecklist	*		CSkaterCareer::GetGapChecklist(int level)
{
	if (level == -1)
	{
		level = m_current_level;
	}

	Dbg_MsgAssert(level >= 0 && level < (int)vMAX_CAREER_LEVELS,("bad level number (%d) in  GetGapChecklist",level));
	return mp_gap_checklist[level];
}

bool CSkaterCareer::GotAllGaps()
{
	//Rulon: Made this function more robust to handle the changing level arrangement.
	Script::CArray *p_array = Script::GetArray(CRCD(0xeb1a4bc9,"levels_with_gaps"),Script::ASSERT);
	uint32 array_size = p_array->GetSize();

	for ( uint32 i = 0; i < vMAX_CAREER_LEVELS; ++i )
	{
		// Skip up levels we dont care about
		uint32 j = 0;
		for ( j = 0; j < array_size; ++j) 
		{
			uint32 check = p_array->GetChecksum(j);
			uint32 level_num = Script::GetInt(check ,Script::ASSERT);
			if( i == level_num )
			{
				//printf("level %i\n", i);
				break;
			}
		}
		
		if (j == array_size)
			continue;

		Obj::CGapChecklist* pGapChecklist = GetGapChecklist( i );
		
		// If this level should be taken into account but is unvisited
		if ( !this->HasVisitedLevel( i ) )
		{
			 //printf("haven't been to level %i\n", i);
			return false;
		}

		if ( !pGapChecklist->GotAllGaps() )
		{
			//printf("didn't get all the gaps in level %i\n", i);
			return false;
		}
		 //printf("got all gaps in level %i\n", i);
	}
	  return true;
	
}
					 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSkaterCareer::StartLevel(int level)
{
	

	if (level == -1)
	{
		level = m_current_level;
	}
	else
	{	
		m_current_level = level;
	}

	this->MarkLevelVisited( m_current_level );

	Dbg_MsgAssert(m_current_level < (int)vMAX_CAREER_LEVELS,( "level number %d too big",m_current_level)); 
	m_start_goal_flags[0]	= m_goal_flags[level][0];
	m_start_goal_flags[1]	= m_goal_flags[level][1];
	m_start_level_flags[0]	= m_level_flags[level][0];
	m_start_level_flags[1]	= m_level_flags[level][1];
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int CSkaterCareer::GetLevel()
{
	
	return m_current_level;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterCareer::MarkLevelVisited( int level_num )
{
	Dbg_MsgAssert( level_num >= 0 && level_num < (int)vMAX_CAREER_LEVELS, ( "MarkLevelVisited called with bad level num %i", level_num ) );
	m_level_visited[level_num] = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CSkaterCareer::HasVisitedLevel( int level_num )
{
	Dbg_MsgAssert( level_num >= 0 && level_num < (int)vMAX_CAREER_LEVELS, ( "MarkLevelVisited called with bad level num %i", level_num ) );
	return m_level_visited[level_num];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSkaterCareer::SetGoal(int goal, int level)
{
	

	Dbg_MsgAssert(goal >= 0 && goal <256, ("goal %d out of range"));
	
	if (level == -1)
	{
		level = m_current_level;
	}
	
	m_goal_flags[level][goal>>5] |= (1<<(goal&31));
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSkaterCareer::UnSetGoal(int goal, int level)
{
	
	
	Dbg_MsgAssert(goal >= 0 && goal <256, ("goal %d out of range"));
	
	
	if (level == -1)
	{
		level = m_current_level;
	}
	
	m_goal_flags[level][goal>>5] &= ~(1<<(goal&31));
	m_start_goal_flags[goal>>5]  &= ~(1<<(goal&31));
	
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSkaterCareer::GetGoal(int goal, int level)
{
	
	Dbg_MsgAssert(goal >= 0 && goal <256, ("goal %d out of range"));
	if (level == -1)
	{
		level = m_current_level;
	}
	
	if (m_goal_flags[level][goal>>5] & (1<<(goal&31)))
	{
		return true;
	}
	
	return false;
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSkaterCareer::JustGotGoal(int goal)
{
	
	Dbg_MsgAssert(goal >= 0 && goal <256, ("goal %d out of range"));

	// if start goal flag is different from the current one, then we must
	// have just got the goal	
	if ((m_goal_flags[m_current_level][goal>>5] ^ m_start_goal_flags[goal>>5]) & (1<<(goal&31)) )
	{
		return true;
	}
	
	return false;

}

int	CSkaterCareer::CountGoalsCompleted(int level)
{
	
	if (level == -1)
	{
		level = m_current_level;
	}
	return CountOnes(m_goal_flags[level][0]) +  CountOnes(m_goal_flags[level][1]);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	CSkaterCareer::CountTotalGoalsCompleted()
{
	
	int Goals=0;
	for (uint i=0; i 0) ? 1:0;		// count one per level
		}
	}
	return Medals;
}

// Returns 0 if no medal was won.
// If a medal was won, it returns 1+the best-medal number, where the medal number is whatever it is defined to be
// in goal_scripts.q
int CSkaterCareer::GetBestMedal(int Level)
{
	
	
	Dbg_MsgAssert(Level>=0 && Level<(int)vMAX_CAREER_LEVELS,("Bad Level of %d sent to GetBestMedal",Level));
	
	int Gold=Script::GetInteger("GOAL_GOLD");
	int Silver=Script::GetInteger("GOAL_SILVER");
	int Bronze=Script::GetInteger("GOAL_BRONZE");
	
	Dbg_MsgAssert(s_is_competition[Level],("Level %d is not a competition level",Level));
	
	if (m_goal_flags[Level][Gold>>5] & (1<>5] & (1<>5] & (1<InNetGame()) return false;
	return g_CheatsEnabled;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSkaterCareer::SetGlobalFlag(int flag)
{
	
	Dbg_MsgAssert(flag<512,("Flag %d is over 255",flag));
	m_global_flags[flag>>5] |= (1<<(flag&31));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSkaterCareer::UnSetGlobalFlag(int flag)
{
	
	Dbg_MsgAssert(flag<512,("Flag %d is over 255",flag));
	m_global_flags[flag>>5] &= ~(1<<(flag&31));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSkaterCareer::GetGlobalFlag(int flag)
{
	
	Dbg_MsgAssert(flag<512,("Flag %d is over 255",flag));
	if (m_global_flags[flag>>5] & (1<<(flag&31)))
	{
		return true;
	}
	
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSkaterCareer::SetFlag(int flag, int level)
{
	
	Dbg_MsgAssert(flag<256,("Flag %d is over 255",flag));
	if (level == -1)
	{
		level = m_current_level;
	}
	m_level_flags[level][flag>>5] |= (1<<(flag&31));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CSkaterCareer::UnSetFlag(int flag, int level)
{
	
	Dbg_MsgAssert(flag<256,("Flag %d is over 255",flag));
	if (level == -1)
	{
		level = m_current_level;
	}
	m_level_flags[level][flag>>5] &= ~(1<<(flag&31));
	m_start_level_flags[flag>>5]  &= ~(1<<(flag&31));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSkaterCareer::GetFlag(int flag, int level)
{
	
	Dbg_MsgAssert(flag<256,("Flag %d is over 255",flag));
	if (level == -1)
	{
		level = m_current_level;
	}
	
	if (m_level_flags[level][flag>>5] & (1<<(flag&31)))
	{
		return true;
	}
	
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	CSkaterCareer::JustGotFlag(int flag)
{
	
	Dbg_MsgAssert(flag<256,("Flag %d is over 255",flag));
	// if start goal flag is different from the current one, then we must
	// have just got the goal	
	if ((m_level_flags[m_current_level][flag>>5] ^ m_start_level_flags[flag>>5]) & (1<<(flag&31)) )
	{
		return true;
	}
	
	return false;
}


void CSkaterCareer::WriteIntoStructure(Script::CScriptStructure *pIn)
{
	
	// Create an array for holding the goal flags
	Script::CArray *pGoalFlags=new Script::CArray;
	pGoalFlags->SetSizeAndType(8*vMAX_CAREER_LEVELS, ESYMBOLTYPE_INTEGER);

	// Wack 'em all in
	for (unsigned int i = 0; iSetInteger(i*8+j,m_goal_flags[i][j]);
		}
	}	
	
	// Create an array for holding the level flags
	Script::CArray *pLevelFlags=new Script::CArray;
	pLevelFlags->SetSizeAndType(8*vMAX_CAREER_LEVELS, ESYMBOLTYPE_INTEGER);
	
	// Wack 'em all in
	for (unsigned int i = 0; iSetInteger(i*8+j,m_level_flags[i][j]);
		}
	}	

	// Create an array for holding the global flags
	Script::CArray *pGlobalFlags=new Script::CArray;
	pGlobalFlags->SetSizeAndType(vMAX_GLOBAL_FLAGS, ESYMBOLTYPE_INTEGER);
	
	// Wack 'em all in
	for (unsigned int i = 0; iSetInteger(i,m_global_flags[i]);
	}

#if 1
	// Create an array for holding the gap checklists
	
	Script::CArray *pGapChecklists=new Script::CArray;
	pGapChecklists->SetSizeAndType(vMAX_CAREER_LEVELS, ESYMBOLTYPE_STRUCTURE);
	
	for (unsigned int i = 0; iAddInteger(CRCD(0x651533ec,"level"),i);
		if (!p_checklist->IsValid())
		{
			p_gap_struct->AddInteger(CRCD(0x73f8ca00,"valid"),0);
		}
		else
		{
			p_gap_struct->AddInteger(CRCD(0x73f8ca00,"valid"),1);
			int num_gaps = p_checklist->NumGaps();
			
			Script::CArray *p_gaps_array=new Script::CArray;
			p_gaps_array->SetSizeAndType(num_gaps, ESYMBOLTYPE_STRUCTURE);

			for (int gap=0; gapAddString(CRCD(0x699fcfc0,"GapName"),p_checklist->GetGapText(gap));
				p_struct->AddInteger(CRCD(0x25eb70db,"GapCount"),p_checklist->GetGapCount(gap));
				p_struct->AddInteger(CRCD(0x92ab03e8,"GapScore"),p_checklist->GetGapScore(gap));
				
				Mth::Vector skater_start_pos;
				Mth::Vector skater_start_dir;
				skater_start_pos.Set();
				skater_start_dir.Set();
				if (p_checklist->GetGapCount(gap))
				{
					p_checklist->GetSkaterStartPosAndDir(gap,&skater_start_pos,&skater_start_dir);
				}				
				// The vectors are always added to keep the save structure size as constant as possible.
				p_struct->AddVector(CRCD(0xa4605544,"SkaterStartPos"),skater_start_pos[X],skater_start_pos[Y],skater_start_pos[Z]);
				p_struct->AddVector(CRCD(0x9e12c9f8,"SkaterStartDir"),skater_start_dir[X],skater_start_dir[Y],skater_start_dir[Z]);
				
				p_gaps_array->SetStructure(gap,p_struct);
			}
						
			p_gap_struct->AddArrayPointer(CRCD(0xd76c173e,"Gaps"),p_gaps_array);
		}
		pGapChecklists->SetStructure(i,p_gap_struct);
	}
#endif

	Script::CArray* pVisitedLevels = new Script::CArray();
	pVisitedLevels->SetSizeAndType( vMAX_CAREER_LEVELS, ESYMBOLTYPE_INTEGER );
	for ( unsigned int i = 0; i < vMAX_CAREER_LEVELS; ++i )
		pVisitedLevels->SetInteger( i, m_level_visited[i] );

	// Insert the above arrays into a new structure.
	Script::CScriptStructure *pTemp=new Script::CScriptStructure;
	pTemp->AddComponent(Script::GenerateCRC("GoalFlags"),ESYMBOLTYPE_ARRAY,(int)pGoalFlags);
	pTemp->AddComponent(Script::GenerateCRC("LevelFlags"),ESYMBOLTYPE_ARRAY,(int)pLevelFlags);
	pTemp->AddComponent(Script::GenerateCRC("GlobalFlags"),ESYMBOLTYPE_ARRAY,(int)pGlobalFlags);
	
#if 1	
	pTemp->AddComponent(Script::GenerateCRC("GapChecklists"),ESYMBOLTYPE_ARRAY,(int)pGapChecklists);
#endif
	
	pTemp->AddArrayPointer( "VisitedLevels", pVisitedLevels );

	// K: Added these for Zac so that when autoloading on startup it can automatically go
	// straight to the last level played
	Mdl::Skate * p_skate_mod =  Mdl::Skate::Instance();
	pTemp->AddChecksum(CRCD(0xe3335d2f,"LastLevelLoadScript"),p_skate_mod->m_cur_level);
	pTemp->AddChecksum(CRCD(0x2cc06f5e,"LastGameMode"),p_skate_mod->GetGameMode()->GetNameChecksum());
    pTemp->AddInteger("current_theme",Script::GetInteger("stored_theme_prefix"));
					 

	// Thrust the new structure into the passed structure.
	Dbg_MsgAssert(pIn,("NULL pIn"));
	pIn->AddComponent(Script::GenerateCRC("Career"),ESYMBOLTYPE_STRUCTUREPOINTER,(int)pTemp);
	
	// Note: The above arrays and structure are not deleted here, because pointers to them have
	// been given to the passed structure, so it will delete them when it gets deleted.

//	Script::PrintContents(pTemp);
}

void CSkaterCareer::ReadFromStructure(Script::CScriptStructure *pIn)
{
	
	Dbg_MsgAssert(pIn,("NULL pIn"));

	Script::CScriptStructure *pCareer=NULL;
	pIn->GetStructure("Career",&pCareer);
	Dbg_MsgAssert(pCareer,("Missing Career structure in structure passed to CSkaterCareer::ReadFromStructure"));
	
	Script::CArray *pGoalFlags=NULL;
	pCareer->GetArray("GoalFlags",&pGoalFlags);
	Dbg_MsgAssert(pGoalFlags,("Missing GoalFlags array in Career structure"));
	Dbg_MsgAssert(pGoalFlags->GetSize()==8*vMAX_CAREER_LEVELS,("Bad size of %d for goal flags array, expected %d",pGoalFlags->GetSize(),vMAX_CAREER_LEVELS));

	Script::CArray *pLevelFlags=NULL;
	pCareer->GetArray("LevelFlags",&pLevelFlags);
	Dbg_MsgAssert(pLevelFlags,("Missing LevelFlags array in Career structure"));
	Dbg_MsgAssert(pLevelFlags->GetSize()==8*vMAX_CAREER_LEVELS,("Bad size of %d for level flags array, expected %d",pGoalFlags->GetSize(),vMAX_CAREER_LEVELS));

	for (unsigned int i = 0; iGetInteger(i*8+j);
		}
		
		for (j = 0;j<8;j++)
		{
			m_level_flags[i][j]=pLevelFlags->GetInteger(i*8+j);
		}
	}	
	
	Script::CArray *pGlobalFlags=NULL;
	pCareer->GetArray("GlobalFlags",&pGlobalFlags);
	Dbg_MsgAssert(pGlobalFlags,("Missing GlobalFlags array in Career structure"));
	Dbg_MsgAssert(pGlobalFlags->GetSize()==vMAX_GLOBAL_FLAGS,("Bad size of %d for global flags array, expected %d",pGlobalFlags->GetSize(),vMAX_GLOBAL_FLAGS));
	for (unsigned int i = 0; iGetInteger(i);
	}
	
	Script::CArray *pGapChecklists = NULL;
	pCareer->GetArray("GapChecklists",&pGapChecklists);	
	//Dbg_MsgAssert(pGapChecklists,("Missing Gap checklist in Career structure"));
	Dbg_MsgAssert(pGapChecklists->GetSize()==vMAX_CAREER_LEVELS,("Bad size of %d for gap checklist array, expected %d",pGapChecklists->GetSize()==vMAX_CAREER_LEVELS));
	if (pGapChecklists)
	{
		for (unsigned int i = 0; iFlushGapChecks();
			Script::CStruct *p_gap_struct = pGapChecklists->GetStructure(i);
			int level = 0;
			int valid = 0;
			p_gap_struct->GetInteger("level",&level,true);
			p_gap_struct->GetInteger("valid",&valid,true);
			if (valid)
			{
				Script::CArray *p_gaps=NULL;
				p_gap_struct->GetArray(CRCD(0xd76c173e,"Gaps"),&p_gaps);
				Dbg_MsgAssert(p_gaps,("Missing gaps array"));
				
				int num_gaps = p_gaps->GetSize();
				for (int gap = 0; gapGetStructure(gap);
					
					const char *p_name = NULL;
					int count = 0;
					int score = 0;
					p_struct->GetString(CRCD(0x699fcfc0,"GapName"),&p_name);
					p_struct->GetInteger(CRCD(0x25eb70db,"GapCount"),&count);
					p_struct->GetInteger(CRCD(0x92ab03e8,"GapScore"),&score);
					p_checklist->AddGapCheck(p_name,count,score);  
					
					if (count)
					{
						Mth::Vector skater_start_pos;
						Mth::Vector skater_start_dir;
						p_struct->GetVector(CRCD(0xa4605544,"SkaterStartPos"),&skater_start_pos);
						p_struct->GetVector(CRCD(0x9e12c9f8,"SkaterStartDir"),&skater_start_dir);
						
						p_checklist->SetInfoForCamera(p_name,skater_start_pos,skater_start_dir);
					}
					
					//printf ("%d: %s (%d)\n",level,p_name,count);				
				}
			}
		}
	}

	Script::CArray* pVisitedLevels = NULL;
	pCareer->GetArray( "VisitedLevels", &pVisitedLevels, Script::NO_ASSERT );
	if ( pVisitedLevels )
	{
#ifdef __NOPT_ASSERT__
		int size = pVisitedLevels->GetSize();
		Dbg_MsgAssert( size <= (int)vMAX_CAREER_LEVELS, ( "wrong array size" ) );
#endif		// __NOPT_ASSERT__
		for ( int i = 0; i < (int)vMAX_CAREER_LEVELS; i++ )
			m_level_visited[i] = pVisitedLevels->GetInteger( i );
	}
}

} // namespace Obj



================================================
FILE: Code/Sk/Objects/skatercareer.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Object (OBJ)											**
**																			**
**	File name:		objects/SkaterCareer.h								   	**
**																			**
**	Created: 		21/5/01 - Mick (Based on SkaterApperance.h by gj)		**
**																			**
*****************************************************************************/

#ifndef __OBJECTS_SKATERCAREER_H
#define __OBJECTS_SKATERCAREER_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
    #include 
#endif

namespace Script
{
	class CStruct;
}	


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{

/*****************************************************************************
**							Forward Declarations						**
*****************************************************************************/

class	CGapChecklist;

/*****************************************************************************
**							   Class Definitions							**
*****************************************************************************/

class  CSkaterCareer  : public Spt::Class
{
	

	public:
		enum
		{
			vVERSION_NUMBER = 0x00000001,
			vMAX_CAREER_LEVELS = 21, 		// changes in this are handled automatically, so don't change the version number
			vMAX_GLOBAL_FLAGS = 16,
			vMAGIC_NUMBER = 0x84751129,		// magic terminator, for integrity check
			vNUM_FLAG_BITS = 64,			// used for assertions
		};
	
		CSkaterCareer();
		virtual						~CSkaterCareer();

	public:
		// for memory card loading/saving
		uint32						WriteToBuffer(uint8* pBuffer, uint32 bufferSize);
		uint8*						ReadFromBuffer(uint8* pBuffer);
		void 						ReadFromStructure(Script::CStruct *pIn);
		void 						WriteIntoStructure(Script::CStruct *pIn);

		// Initialize the career to "Nothing Done Yet" 
		bool						Init( bool init_gaps = true );

		// Reset's the career to "Nothing Done Yet"
		bool						Reset( void );


		// Start the level, setting the default level number
		// and remembering which goals and flags were set at the start of this level
		void						StartLevel(int level = -1);
		
		// Access function for Level. Added by Ken, used by CareerLevelIs script command.
		int							GetLevel();

		// marks a level visited
		void						MarkLevelVisited( int level_num );
		bool						HasVisitedLevel( int level_num );

		// Access functions for goals and flags
		void						SetGoal(int goal, int level = -1);
		void						UnSetGoal(int goal, int level = -1);
		bool						GetGoal(int goal, int level = -1);
		int							CountGoalsCompleted(int level = -1);
		bool						JustGotGoal(int goal);
		
		void						SetFlag(int flag, int level = -1);
		void						UnSetFlag(int flag, int level = -1);
		bool						GetFlag(int flag, int level = -1);
		
		bool						GetCheat(uint32 cheat_checksum);
		
		void						SetGlobalFlag(int flag);
		void						UnSetGlobalFlag(int flag);
		bool						GetGlobalFlag(int flag);

		bool						JustGotFlag(int flag);
		
		
		int							CountTotalGoalsCompleted();
		int							CountMedals();
		int 						GetBestMedal(int Level);
		
		Obj::CGapChecklist	*		GetGapChecklist(int level = -1);
		bool						GotAllGaps();


	public:
		
	protected:
		uint32						m_goal_flags[vMAX_CAREER_LEVELS][8];
		uint32						m_level_flags[vMAX_CAREER_LEVELS][8];
		uint32						m_start_goal_flags[8];
		uint32						m_start_level_flags[8];
		
		uint32						m_global_flags[vMAX_GLOBAL_FLAGS];		// 8x32 = 256 global flags
		
		int							m_current_level;
		
		Obj::CGapChecklist		*   mp_gap_checklist[vMAX_CAREER_LEVELS];
		bool						m_level_visited[vMAX_CAREER_LEVELS];
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/



/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Obj

#endif	// __OBJECTS_SKATERCAREER_H


================================================
FILE: Code/Sk/Objects/skaterflags.h
================================================
// Skaterflags.h

#ifndef	__SK_OBJECTS_SKATERFLAGS_H__	
#define	__SK_OBJECTS_SKATERFLAGS_H__	

namespace Obj
{
	// Flags that can be toggled on and off, and also store how long since last toggle
	// This is used as an index into CSkater::m_flags
	enum	ESkaterFlag
	{
		TENSE = 0, 					// tensing for a jump, usually X pressed											 0
		// SWITCH,					// skating switch																	 1		NOTE: unused?
		// LEAN,					// leaning L/R (might prohibit us doing other things								 2		NOTE: unused?
		// TENSE_ON_GROUND,			// we were on the ground, and the tense flag was set								 3		NOTE: unused?
		FLIPPED,					// animation is flipped																 4
		VERT_AIR,					// True if we are going to vert air													 5
		TRACKING_VERT,				// True if we are tracking a vert surface below us 									 6
		LAST_POLY_WAS_VERT,			// True if the last polygon we skated on was vert									 7
		CAN_BREAK_VERT,				// True if we can break the vert poly we are on										 8
		CAN_RERAIL,					// can get back on the rail again, ahead of rerail time								 9
		// STARTED_END_OF_RUN,		// true if the skater has started the "end of run" script							 10
		// FINISHED_END_OF_RUN,		// True if the skater has returned to his idle state after the "end of run" script	 11
		// STARTED_GOAL_END_OF_RUN,	// True if the goal_endofrun script has started
		// FINISHED_GOAL_END_OF_RUN,
		RAIL_SLIDING,				// True if skater is railsliding													 12
		CAN_HIT_CAR,				// True is we can hit a car.  Set when on ground, or have negative y vel			 13
		AUTOTURN,					// True if we can autoturn															 14
		IS_BAILING,					// True if skater is bailing...														 15
		// IS_ENDING_RUN,			// True is skater is in the process of ending his run								 16
		// IS_ENDING_GOAL,			// True if we're ending a goal
		SPINE_PHYSICS,				// True is skater is going over a spine												 17
		IN_RECOVERY,				// True if recovering from going off vert											 18
		SKITCHING,					// True if we are being towed by a car												 19
		OVERRIDE_CANCEL_GROUND,		// True if we want to ignore the "CANCEL_GROUND" flag for gaps						 20
		SNAPPED_OVER_CURB,			// True if we snapped up or down a curb this frame (for use by camera)				 21
		SNAPPED,					// True if we snapped slightly this frame (for use by camera)
		IN_ACID_DROP,				// True if we are in an acid drop
		AIR_ACID_DROP_DISALLOWED,	// True if in-air acid drops are not currently allowed
		CANCEL_WALL_PUSH,			// True if the current wallpush event is canceled via script
		NO_ORIENTATION_CONTROL,		// True if spins and leans are turned off due to switching from walking
		NEW_RAIL,					// True if the rail we land on is a "new" rail
		OLLIED_FROM_RAIL,			// True if entered current air via an ollie out of a rail
		// NOTE: If anyone adds any more flags so that NUM_ESKATERFLAGS > 32, let Steve G know

		NUM_ESKATERFLAGS
	};
	
	enum EStateType
	{
		GROUND,
		AIR,
		WALL,
		LIP,
		RAIL,
		WALLPLANT
	};
}
	
#endif



================================================
FILE: Code/Sk/Objects/skaterpad.cpp
================================================
// SkaterPad.cpp

#include 
#include 
#include 

//#define		PS2_SIMULATING_XBOX

// The constructor of a skater pad just sets all the names
CSkaterPad::CSkaterPad()
{
	// Note: These names must match those used when defining event queries in .q files.
	// (Otherwise the queries won't work)
	m_up.SetName("UP");
	m_down.SetName("DOWN");
	m_left.SetName("LEFT");
	m_right.SetName("RIGHT");
	m_square.SetName("SQUARE");
	m_circle.SetName("CIRCLE");
	m_triangle.SetName("TRIANGLE");
	m_x.SetName("X");
	m_L1.SetName("L1");
	m_L2.SetName("L2");
	m_L3.SetName("L3");
	m_R1.SetName("R1");
	m_R2.SetName("R2");
	m_R3.SetName("R3");
	m_start.SetName("START");
	m_select.SetName("SELECT");
	
	m_leftAnalogUpDebounceTime = 0;
	m_leftAnalogDownDebounceTime = 0;
}

#ifdef __NOPT_ASSERT__
void CSkaterPad::Update ( Inp::Data* input, bool debug )
#else
void CSkaterPad::Update ( Inp::Data* input )
#endif
{
	#ifdef __NOPT_ASSERT__
	
	m_up.Update(input->m_Event[Inp::Data::vA_UP] ? 255 : 0, debug);
	m_down.Update(input->m_Event[Inp::Data::vA_DOWN] ? 255 : 0, debug);
	m_left.Update(input->m_Event[Inp::Data::vA_LEFT] ? 255 : 0, debug);
	m_right.Update(input->m_Event[Inp::Data::vA_RIGHT] ? 255 : 0, debug);

	m_square.Update(input->m_Event[Inp::Data::vA_SQUARE] ? 255 : 0, debug);
	m_circle.Update(input->m_Event[Inp::Data::vA_CIRCLE] ? 255 : 0, debug);
	m_x.Update(input->m_Event[Inp::Data::vA_X] ? 255 : 0, debug);
	m_triangle.Update(input->m_Event[Inp::Data::vA_TRIANGLE] ? 255 : 0, debug);

	m_L1.Update(input->m_Event[Inp::Data::vA_L1] ? 255 : 0, debug);
	m_R1.Update(input->m_Event[Inp::Data::vA_R1] ? 255 : 0, debug);

	#ifndef	PS2_SIMULATING_XBOX
	m_L2.Update(input->m_Event[Inp::Data::vA_L2] ? 255 : 0, debug);
	m_R2.Update(input->m_Event[Inp::Data::vA_R2] ? 255 : 0, debug);
	#else	// update both 1 and 2 from L1/R1, to simulate the X-Box
	m_L2.Update(input->m_Event[Inp::Data::vA_L1] ? 255 : 0, debug);
	m_R2.Update(input->m_Event[Inp::Data::vA_R1] ? 255 : 0, debug);
	#endif
	
	m_L3.Update(input->m_Buttons & Inp::Data::mD_L3 ? 255 : 0, debug);
	m_R3.Update(input->m_Buttons & Inp::Data::mD_R3 ? 255 : 0, debug);
	
	
	m_select.Update(input->m_Buttons & Inp::Data::mD_SELECT ? 255 : 0, debug);
	
	#else
	
	m_up.Update(input->m_Event[Inp::Data::vA_UP] ? 255 : 0);
	m_down.Update(input->m_Event[Inp::Data::vA_DOWN] ? 255 : 0);
	m_left.Update(input->m_Event[Inp::Data::vA_LEFT] ? 255 : 0);
	m_right.Update(input->m_Event[Inp::Data::vA_RIGHT] ? 255 : 0);

	m_square.Update(input->m_Event[Inp::Data::vA_SQUARE] ? 255 : 0);
	m_circle.Update(input->m_Event[Inp::Data::vA_CIRCLE] ? 255 : 0);
	m_x.Update(input->m_Event[Inp::Data::vA_X] ? 255 : 0);
	m_triangle.Update(input->m_Event[Inp::Data::vA_TRIANGLE] ? 255 : 0);

	m_L1.Update(input->m_Event[Inp::Data::vA_L1] ? 255 : 0);
	m_R1.Update(input->m_Event[Inp::Data::vA_R1] ? 255 : 0);

	#ifndef	PS2_SIMULATING_XBOX
	m_L2.Update(input->m_Event[Inp::Data::vA_L2] ? 255 : 0);
	m_R2.Update(input->m_Event[Inp::Data::vA_R2] ? 255 : 0);
	#else	// update both 1 and 2 from L1/R1, to simulate the X-Box
	m_L2.Update(input->m_Event[Inp::Data::vA_L1] ? 255 : 0);
	m_R2.Update(input->m_Event[Inp::Data::vA_R1] ? 255 : 0);
	#endif
	
	m_L3.Update(input->m_Buttons & Inp::Data::mD_L3 ? 255 : 0);
	m_R3.Update(input->m_Buttons & Inp::Data::mD_R3 ? 255 : 0);

	m_select.Update(input->m_Buttons & Inp::Data::mD_SELECT ? 255 : 0);
	
	#endif
	
	int in;
	
	in = input->m_Event[Inp::Data::vA_RIGHT_X] - 128;
	if (in == 0)
	{
		m_scaled_rightX = 0.0f;
	}
	else
	{
		if (in == -128)
		{
			in = -127;
		}
		m_scaled_rightX = (in + (in > 0 ? -Inp::vANALOGUE_TOL : Inp::vANALOGUE_TOL)) / (127.0f - Inp::vANALOGUE_TOL);
	}
	
	in = input->m_Event[Inp::Data::vA_RIGHT_Y] - 128;
	if (in == 0)
	{
		m_scaled_rightY = 0.0f;
	}
	else
	{
		if (in == -128)
		{
			in = -127;
		}
		m_scaled_rightY = (in + (in > 0 ? -Inp::vANALOGUE_TOL : Inp::vANALOGUE_TOL)) / (127.0f - Inp::vANALOGUE_TOL);
	}
	
	in = input->m_Event[Inp::Data::vA_LEFT_X] - 128;
	if (in == 0)
	{
		m_scaled_leftX = 0.0f;
	}
	else
	{
		if (in == -128)
		{
			in = -127;
		}
		m_scaled_leftX = (in + (in > 0 ? -Inp::vANALOGUE_TOL : Inp::vANALOGUE_TOL)) / (127.0f - Inp::vANALOGUE_TOL);
	}

	in = input->m_Event[Inp::Data::vA_LEFT_Y] - 128;
	if (in == 0)
	{
		m_scaled_leftY = 0.0f;
	}
	else
	{
		if (in == -128)
		{
			in = -127;
		}
		m_scaled_leftY = (in + (in > 0 ? -Inp::vANALOGUE_TOL : Inp::vANALOGUE_TOL)) / (127.0f - Inp::vANALOGUE_TOL);
	}
	
	m_rightX = input->m_Event[Inp::Data::vA_RIGHT_X_UNCLAMPED] - 128;
	m_rightY = input->m_Event[Inp::Data::vA_RIGHT_Y_UNCLAMPED] - 128;
	m_leftX = input->m_Event[Inp::Data::vA_LEFT_X_UNCLAMPED] - 128;
	m_leftY = input->m_Event[Inp::Data::vA_LEFT_Y_UNCLAMPED] - 128;
	
	// Calculate the direction, and the amount

	m_rightLength = sqrtf(m_rightX * m_rightX + m_rightY * m_rightY);
	m_rightAngle = atan2f(m_rightX, -m_rightY);

	m_leftLength = sqrtf(m_leftX * m_leftX + m_leftY * m_leftY);
	if (m_leftLength > 0.001f)
	{
		m_leftAngle = atan2f(m_leftX, -m_leftY);
	}
	else
	{
		// if left analog stick not pressed, then get it from the D-Pad
		bool Up;
		bool Down;
		bool Left;
		bool Right;
		Up = input->m_Event[Inp::Data::vA_UP];
		Down = input->m_Event[Inp::Data::vA_DOWN];
		Left = input->m_Event[Inp::Data::vA_LEFT];
		Right = input->m_Event[Inp::Data::vA_RIGHT];

		if (Up || Down || Left || Right)
		{
			m_leftAngle = sGetAngleFromDPad(Up, Down, Left, Right);
			m_leftLength = 127.0f;
		}
		else
		{
			m_leftAngle = 0.0f;
			m_leftLength = 0.0f;				
		}
	}
	
	if (m_leftAnalogUpDebounceTime)
	{
		// if not pressed, or debounce time expired
		if (m_leftLength == 0.0f || !(Mth::Abs(m_leftAngle) < Mth::DegToRad(45.0f)))
		{
			m_leftAnalogUpDebounceTime = 0;
		}
		else if ((float)Tmr::GetTime() > m_leftAnalogUpDebounceTime)
		{
			m_leftAnalogUpDebounceTime = 0;
		}
	}
	
	if (m_leftAnalogDownDebounceTime)
	{
		// if not pressed, or debounce time expired
		if (m_leftLength == 0.0f || !((Mth::PI - Mth::Abs(m_leftAngle)) < Mth::DegToRad(45.0f)))
		{
			m_leftAnalogDownDebounceTime = 0;
		}
		else if ((float)Tmr::GetTime() > m_leftAnalogDownDebounceTime)
		{
			m_leftAnalogDownDebounceTime = 0;
		}
	}
}

CSkaterButton *CSkaterPad::GetButton(uint32 NameChecksum)
{
	
	// Maybe optimize using a hash table later for speed.
	if (m_up.GetName()==NameChecksum) return &m_up;
	else if (m_down.GetName()==NameChecksum) return &m_down;
	else if (m_left.GetName()==NameChecksum) return &m_left;
	else if (m_right.GetName()==NameChecksum) return &m_right;
	else if (m_square.GetName()==NameChecksum) return &m_square;
	else if (m_circle.GetName()==NameChecksum) return &m_circle;
	else if (m_triangle.GetName()==NameChecksum) return &m_triangle;
	else if (m_x.GetName()==NameChecksum) return &m_x;
	else if (m_L1.GetName()==NameChecksum) return &m_L1;
	else if (m_L2.GetName()==NameChecksum) return &m_L2;
	else if (m_L3.GetName()==NameChecksum) return &m_L3;
	else if (m_R1.GetName()==NameChecksum) return &m_R1;
	else if (m_R2.GetName()==NameChecksum) return &m_R2;
	else if (m_R3.GetName()==NameChecksum) return &m_R3;
	else if (m_start.GetName()==NameChecksum) return &m_start;
	else if (m_select.GetName()==NameChecksum) return &m_select;
	else 
	{
		Dbg_MsgAssert(0,("Bad checksum '%x' sent to GetButton",NameChecksum));
	}
	return NULL;
}	
																			
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
																			
float	CSkaterPad::GetScaledAnalogStickMagnitude ( float analog_x, float analog_y, float analog_angle )
{
	float x = analog_x / (analog_x > 0.0f ? 127.0f : 128.0f);
	float y = analog_y / (analog_y > 0.0f ? 127.0f : 128.0f);
	
	#ifndef __PLAT_NGC__
	
	float angle;
	if (analog_angle > (-Mth::PI / 4.0f) && analog_angle < (Mth::PI / 4.0f))
	{
		// top quadrant
		angle = analog_angle;
	}
	else if (analog_angle > (Mth::PI / 4.0f) && analog_angle < (3.0f * Mth::PI / 4.0f))
	{
		// right quadrant
		angle = analog_angle - (Mth::PI / 2.0f);
	}
	else if (Mth::Abs(analog_angle) > (3.0f * Mth::PI / 4.0f))
	{
		// bottom quadrant
		angle = Mth::Abs(analog_angle) - Mth::PI;
	}
	else
	{
		// left quadrant
		angle = analog_angle + (Mth::PI / 2.0f);
	}
	
	return sqrtf(x * x + y * y) * Mth::Abs(cosf(angle));
	
	#else
	
	return sqrtf(x * x + y * y);
	
	#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint32	CSkaterPad::GetPressedMask( void )
{
	uint32 mask;

	mask = 0;

	if( m_up.GetPressed())
	{
		mask |= Inp::Data::mA_UP;
	}
	if( m_down.GetPressed())
	{
		mask |= Inp::Data::mA_DOWN;
	}
	if( m_left.GetPressed())
	{
		mask |= Inp::Data::mA_LEFT;
	}
	if( m_right.GetPressed())
	{
		mask |= Inp::Data::mA_RIGHT;
	}
	if( m_L1.GetPressed())
	{
		mask |= Inp::Data::mA_L1;
	}
	if( m_L2.GetPressed())
	{
		mask |= Inp::Data::mA_L2;
	}
	if( m_R1.GetPressed())
	{
		mask |= Inp::Data::mA_R1;
	}
	if( m_R2.GetPressed())
	{
		mask |= Inp::Data::mA_R2;
	}
	if( m_circle.GetPressed())
	{
		mask |= Inp::Data::mA_CIRCLE;
	}
	if( m_square.GetPressed())
	{
		mask |= Inp::Data::mA_SQUARE;
	}
	if( m_triangle.GetPressed())
	{
		mask |= Inp::Data::mA_TRIANGLE;
	}
	if( m_x.GetPressed())
	{
		mask |= Inp::Data::mA_X;
	}
		
	return mask;
}
																			
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the pressed state of the four cardinal directions
// then return a sensible value including diagonals
// given that we might be pressing opposing directions (Up+Down)
// and we still want to get some sensible response
// Here Up+Down is mapped to PAD_D
// all mapping where there is some conflict needing resolution are
// marked with an asterix
uint32 CSkaterPad::sGetDirection(bool Up, bool Down, bool Left, bool Right)
{
	int Dir = Up + (Down << 1) + (Left << 2) + (Right << 3);

	static uint DirectionMap[16] =
	{
		// RLDU
		0,				// 0000
		PAD_U,		// 0001
		PAD_D,		// 0010
		PAD_D,		// 0011	 *
		PAD_L,		// 0100
		PAD_UL,		// 0101
		PAD_DL,		// 0110
		PAD_DL,		// 0111	 *
		PAD_R,		// 1000
		PAD_UR,		// 1001
		PAD_DR,		// 1010
		PAD_DR,		// 1011	 *
		PAD_R,		// 1100	 *
		PAD_UR,		// 1101	 *
		PAD_DR,		// 1110	 *
		PAD_DR,		// 1111	 *
	};

	return DirectionMap[Dir];
}
																			
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#define		PAD_ANGLE_U		( 0.0f )
#define		PAD_ANGLE_UR	( Mth::PI / 4.0f)
#define		PAD_ANGLE_R     ( Mth::PI / 2.0f) 
#define		PAD_ANGLE_DR    ( Mth::PI * 3.0f / 4.0f)
#define		PAD_ANGLE_D		( Mth::PI )
#define		PAD_ANGLE_DL	( - Mth::PI * 3.0f / 4.0f) 
#define		PAD_ANGLE_L		( - Mth::PI / 2.0f ) 
#define		PAD_ANGLE_UL	( - Mth::PI / 4.0f) 

float CSkaterPad::sGetAngleFromDPad(bool Up, bool Down, bool Left, bool Right)
{
	int Dir = Up + (Down << 1) + (Left << 2) + (Right << 3);

	static float DirectionMap[16] =
	{
		// RLDU
		0.0f,				// 0000
		PAD_ANGLE_U,		// 0001
		PAD_ANGLE_D,		// 0010
		PAD_ANGLE_D,		// 0011	 *
		PAD_ANGLE_L,		// 0100
		PAD_ANGLE_UL,		// 0101
		PAD_ANGLE_DL,		// 0110
		PAD_ANGLE_DL,		// 0111	 *
		PAD_ANGLE_R,		// 1000
		PAD_ANGLE_UR,		// 1001
		PAD_ANGLE_DR,		// 1010
		PAD_ANGLE_DR,		// 1011	 *
		PAD_ANGLE_R,		// 1100	 *
		PAD_ANGLE_UR,		// 1101	 *
		PAD_ANGLE_DR,		// 1110	 *
		PAD_ANGLE_DR,		// 1111	 *
	};

	return DirectionMap[Dir];
}
																			
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
																			
void CSkaterPad::Zero (   )
{
	m_up.Update( 0 );
	m_down.Update( 0 );
	m_left.Update( 0 );
	m_right.Update( 0 );
	m_L1.Update( 0 );
	m_L2.Update( 0 );
	m_L3.Update( 0 );
	m_R1.Update( 0 );
	m_R2.Update( 0 );
	m_R3.Update( 0 );
	m_square.Update( 0 );
	m_circle.Update( 0 );
	m_x.Update( 0 );
	m_triangle.Update( 0 );
	m_start.Update( 0 );
	m_select.Update( 0 );
	
	m_rightX = 0.0f;
	m_rightY = 0.0f;
	m_leftX = 0.0f;
	m_leftY = 0.0f;

	m_scaled_rightX = 0.0f;
	m_scaled_rightY = 0.0f;
	m_scaled_leftX = 0.0f;
	m_scaled_leftY = 0.0f;

	m_rightAngle = 0.0f;
	m_rightLength = 0.0f;
	m_leftAngle = 0.0f;
	m_leftLength = 0.0f;
}
																			
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CSkaterPad::Reset (   )
{
	Zero();
	
	m_up.ClearTrigger( );
	m_up.ClearRelease( );
	m_down.ClearTrigger( );
	m_down.ClearRelease( );
	m_left.ClearTrigger( );
	m_left.ClearRelease( );
	m_right.ClearTrigger( );
	m_right.ClearRelease( );
	m_L1.ClearTrigger( );
	m_L1.ClearRelease( );
	m_L2.ClearTrigger( );
	m_L2.ClearRelease( );
	m_L3.ClearTrigger( );
	m_L3.ClearRelease( );
	m_R1.ClearTrigger( );
	m_R1.ClearRelease( );
	m_R2.ClearTrigger( );
	m_R2.ClearTrigger( );
	m_R3.ClearRelease( );
	m_R3.ClearRelease( );
	m_square.ClearTrigger( );
	m_square.ClearRelease( );
	m_circle.ClearTrigger( );
	m_circle.ClearRelease( );
	m_x.ClearTrigger( );
	m_x.ClearRelease( );
	m_triangle.ClearTrigger( );
	m_triangle.ClearRelease( );
	m_start.ClearTrigger( );
	m_start.ClearRelease( );
	m_select.ClearTrigger( );
	m_select.ClearRelease( );
}


================================================
FILE: Code/Sk/ParkEditor/EdRail.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:					      			 								**
**																			**
**	File name:																**
**																			**
**	Created by:		rjm										                **
**																			**
**	Description:															**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

DefinePoolableClass(Ed::RailPoint)
DefinePoolableClass(Ed::RailString)

namespace Ed
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

RailPoint::RailPoint() :
	m_pos(0.0f, 0.0f, 0.0f, 0.0f)
{
	mp_next = NULL;
}




RailPoint::~RailPoint() 
{
}




RailString::RailString()
{
	mp_pointList = NULL;
	m_numPoints = 0;
	mp_next = NULL;

	m_isLoop = false;
}




RailString::~RailString()
{
}




void RailString::AddPoint(RailPoint *pPoint)
{
	if (!mp_pointList)
		mp_pointList = pPoint;
	else
		mp_lastPoint->mp_next = pPoint;
	mp_lastPoint = pPoint;
	m_numPoints++;
}




void RailString::Destroy()
{
	RailPoint *pPoint = mp_pointList;
	while(pPoint)
	{
		RailPoint *pNext = pPoint->mp_next;
		mp_parentSet->DestroyRailPoint(pPoint);
		pPoint = pNext;
	}
	mp_pointList = NULL;
	mp_lastPoint = NULL;
	m_numPoints = 0;
}




int	RailString::CountLinkedPoints()
{
	

	if (m_isLoop)
		return Count();

	int count = 0;
	for (RailPoint *pPoint = mp_pointList; pPoint != NULL; pPoint = pPoint->mp_next)
		count++;

	Dbg_MsgAssert(count > 1, ("invalid number of linked rail points in a rail (%d) need 2 or more",count));
	return count - 1;
}




void RailString::CopyOffsetAndRot(RailString *pSource, Mth::Vector &pos, int rot, uint32 piece_id)
{
	RailPoint *pSourcePt = pSource->GetList();

	while(pSourcePt)
	{
		RailPoint *pOut = mp_parentSet->CreateRailPoint();
		float x = 0.0f, z = 0.0f;
		switch(rot)
		{
			case 0:
				x =  pSourcePt->m_pos.GetX();
				z = pSourcePt->m_pos.GetZ();
// GJ:  No longer need to negate the Z component, now that we're using new-style "Pos" vectors!
//				z = -pSourcePt->m_pos.GetZ();
				break;
			case 1:
				x = pSourcePt->m_pos.GetZ();
				z = -pSourcePt->m_pos.GetX();
// GJ:  No longer need to negate the Z component, now that we're using new-style "Pos" vectors!
//				z = pSourcePt->m_pos.GetX();
				break;
			case 2:
				x = -pSourcePt->m_pos.GetX();
				z = -pSourcePt->m_pos.GetZ();
// GJ:  No longer need to negate the Z component, now that we're using new-style "Pos" vectors!
//				z = pSourcePt->m_pos.GetZ();
				break;
			case 3:
				x =  -pSourcePt->m_pos.GetZ();
				z =  pSourcePt->m_pos.GetX();
// GJ:  No longer need to negate the Z component, now that we're using new-style "Pos" vectors!
//				z =  -pSourcePt->m_pos.GetX();
				break;
		}
		float y = pSourcePt->m_pos.GetY() +	pos.GetY();
		x += pos.GetX();
		z += pos.GetZ();
// GJ:  No longer need to negate the Z component, now that we're using new-style "Pos" vectors!
		pOut->m_pos.Set(x, y, z);
//		pOut->m_pos.Set(x, y, -z);
		pOut->m_type = pSourcePt->m_type;
		pOut->m_objectId = piece_id;

		AddPoint(pOut);

		pSourcePt = pSourcePt->GetNext();
	}

	m_id = pSource->m_id;
	m_isLoop = pSource->m_isLoop;
}




RailSet::RailSet()
{
	mp_stringList = NULL;
	m_numStrings = 0;

	mp_pointAllocator = NULL;
	mp_stringAllocator = NULL;
}




RailSet::~RailSet()
{
}




void RailSet::AddString(RailString *pString)
{
	if (!mp_stringList)
		mp_stringList = pString;
	else
		mp_lastString->mp_next = pString;
	mp_lastString = pString;
	m_numStrings++;
}




void RailSet::Destroy()
{
	RailString *pString = mp_stringList;
	while(pString)
	{
		RailString *pNext = pString->mp_next;
		pString->Destroy();
		DestroyRailString(pString);
		pString = pNext;
	}
	mp_stringList = NULL;
	mp_lastString = NULL;
	m_numStrings = 0;
}




int RailSet::CountPoints()
{
	int num_points = 0;
	RailString *pString = mp_stringList;
	while (pString)
	{
		num_points += pString->Count();
		pString = pString->GetNext();
	}

	return num_points;
}




RailString *RailSet::GetString(uint32 id, int requested_num)
{
	RailString *pString = mp_stringList;
	int num = 0;
	while(pString)
	{
		if (pString->m_id == id)
		{
			if (num == requested_num)
				return pString;
			else
				num++;
		}
		pString = pString->GetNext();
	}
	return NULL;
}




void RailSet::SetupAllocators(int num_points, int num_strings, bool in_set)
{
	if (in_set)
		strcpy(m_setName, "In set");
	else
		strcpy(m_setName, "Out set");
	char point_name[64];
	sprintf(point_name, "%s RailPoint", m_setName);
	char string_name[64];
	sprintf(string_name, "%s RailString", m_setName);
	
	Dbg_MsgAssert(mp_pointAllocator==NULL,("mp_pointAllocator not NULL"));
	mp_pointAllocator = new Mem::CCompactPool(sizeof(RailPoint), num_points, point_name);
	Dbg_MsgAssert(mp_stringAllocator==NULL,("mp_stringAllocator not NULL"));
	mp_stringAllocator = new Mem::CCompactPool(sizeof(RailString), num_strings, string_name);
}




void RailSet::FreeAllocators()
{
	if (mp_stringAllocator)
	{
		delete mp_stringAllocator;
		mp_stringAllocator=NULL;
	}
	if (mp_pointAllocator)
	{
		delete mp_pointAllocator;
		mp_pointAllocator=NULL;
	}	
}




RailPoint *RailSet::CreateRailPoint()
{
	
	Dbg_Assert(mp_pointAllocator);

	RailPoint::SAttachPool(mp_pointAllocator);
	RailPoint *pNew = new RailPoint;
	RailPoint::SRemovePool();
	return pNew;
}




RailString *RailSet::CreateRailString()
{
	
	Dbg_Assert(mp_stringAllocator);

	RailString::SAttachPool(mp_stringAllocator);
	RailString *pString = new RailString;
	RailString::SRemovePool();
	pString->mp_parentSet = this;
	return pString;
}




void RailSet::DestroyRailPoint(RailPoint *pPoint)
{
	
	Dbg_Assert(mp_pointAllocator);

	RailPoint::SAttachPool(mp_pointAllocator);
	delete pPoint;
	RailPoint::SRemovePool();
}




void RailSet::DestroyRailString(RailString *pString)
{
	
	Dbg_Assert(mp_stringAllocator);

	RailString::SAttachPool(mp_stringAllocator);
	delete pString;
	RailString::SRemovePool();
}




} // namespace Ed






================================================
FILE: Code/Sk/ParkEditor/EdRail.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		            											**
**																			**
**	Module:			              			 								**
**																			**
**	File name:		                    									**
**																			**
**	Created by:     rjm        				    			                **
**																			**
**	Description:					                                        **
**																			**
*****************************************************************************/

#ifndef __SK_PARKEDITOR_EDRAIL_H
#define __SK_PARKEDITOR_EDRAIL_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Ed
{

						


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class  RailPoint  : public Mem::CPoolable
{
	

public:
//	enum RailType
//	{
//		vMETAL,
//		vWOOD,
//	};

	typedef		uint32		RailType;
	
	RailPoint();
	~RailPoint();

	RailPoint *								GetNext() {return mp_next;}

	Mth::Vector								m_pos;
	RailType								m_type;
	// id of associated Piece (and therefore trick object)
	uint32									m_objectId;
	RailPoint *								mp_next;
};




class RailSet;
class  RailString  : public Mem::CPoolable
{
	

public:
	RailString();
	~RailString();

	void									AddPoint(RailPoint *pPoint);
	void									Destroy();
	RailPoint *								GetList() {return mp_pointList;}
	RailString *							GetNext() {return mp_next;}
	int										Count() {return m_numPoints;}
	int										CountLinkedPoints();

	void									CopyOffsetAndRot(RailString *pString, Mth::Vector &pos, int rot, uint32 piece_id);
	
	uint8									m_isLoop;
	uint32									m_id; // of associated piece
	
	RailPoint *								mp_pointList;
	RailPoint *								mp_lastPoint;
	uint16									m_numPoints;
	RailString *							mp_next;

	RailSet *								mp_parentSet;
};




class  RailSet  : public Spt::Class
{
	

public:
	RailSet();
	~RailSet();

	void									AddString(RailString *pString);
	void									Destroy();
	RailString *							GetList() {return mp_stringList;}
	int										Count() {return m_numStrings;}
	int										CountPoints();

	RailString *							GetString(uint32 id, int num);
	
	void									SetupAllocators(int num_points, int num_strings, bool in_set);
	void									FreeAllocators();
	RailPoint *								CreateRailPoint();
	RailString *							CreateRailString();
	void									DestroyRailPoint(RailPoint *pPoint);
	void									DestroyRailString(RailString *pString);

protected:	
	RailString *							mp_stringList;
	RailString *							mp_lastString;
	uint16									m_numStrings;

	Mem::CCompactPool *						mp_pointAllocator;
	Mem::CCompactPool *						mp_stringAllocator;

	char									m_setName[32];
};




/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


} // namespace Ed

#endif	// __SK_PARKEDITOR_EDRAIL_H




================================================
FILE: Code/Sk/ParkEditor/LoadPath.txt
================================================
**************************************************
Level Loading Pathway for THPS3
Updated 7/17/2001
**************************************************

=========================================================
*** REGULAR LEVEL ***
change_level level=Load_LevelName (script)
-setgametype freeskate
-request_level <...>
	-RequestLevel  -- sets skate_mod->m_requested_level
-ScriptChangeLevel()
	-does some network stuff, or might apply to single player
-Return to regular game flow, update function (below) does more later


=========================================================
*** CUSTOM PARK, PLAYED AS REGULAR ***
change_level level=custom_park (script, launched from menu)
-setgametype freeskate
-request_level <...>
	-RequestLevel  -- sets skate_mod->m_requested_level
-ScriptChangeLevel()
	-does some network stuff, or might apply to single player
-Return to regular game flow, update function (below) does more later


=========================================================
*** GOING INTO PARK EDITOR ***
AutoLaunch level=custom_park game=parkeditor
-SetServerMode on (command)
-request_level level=
	-RequestLevel  -- sets skate_mod->m_requested_level
-SetGameType 
-launchmenuscreen screen=parked_menu
-Return to regular game flow, update function (below) does more later


=========================================================
*** UPDATE FUNCTION, ALL ***
-CallMemberFunction, request==GameFlow_RequestLevel
	-*** REGULAR LEVEL ***
		-LoadLevel (script, see details below)
	-*** PARK EDITOR ***
		-DoParkGeneration (script, see below)


=========================================================
*** REGULAR LEVEL ***
LoadLevel (script)
-SetServerMode (command)
-PreLevelLoad (script, see details below)
-LaunchLevel (command, see details below)
-PostLevelLoad
-ResetCamera


=========================================================
*** PARK EDITOR ***
DoParkGeneration (script)
-PreLevelLoad (script, see details below)
-LaunchLevel (command, see details below)
-PostLevelLoad
-ResetCamera
-Do color, velocity, fogging stuff normally found in Load_LevName script


=========================================================
-PreLevelLoad (script)
	-Cleanup
	-Stopping, pausing music
	-SetArenaSize 1
		-call to RwResourcesSetArenaSize()
	-Sounds
	-SetLevelExists
		-rwviewer::SetLevelExists()
	-SetupBasicLights
	-DisplayLoadingScreen


=========================================================
LaunchLevel (command)
-Skate::OpenLevel() 
	-**** REGULAR LEVEL ***
		-runs level script, e.g. Load_Can
			-loadlevelgeometry (command)
				-rwviewer::RequestLoad() (see details below)
			-loadnodearray (command)
				-Script::RemoveOldTriggerScripts(), DeleteSpawnedScripts()
				-Script::LoadQB()
				-ScriptParseNodeArray()
			-LoadTerrain (loads sounds)
			-SetRenderModeVU
			-Set colors, velocity, fog and other level attributes
			-Call level startup, e.g. Can_startup
	-*** PARK EDITOR ***
		-ParkEditor::commandGeneratePark()
			-ParkEditor::Initialize() [need ParkEditor::mp_compressedMap to be set up at this point]
				-Map::InitializeWorld()
					-World::CreateParkHeap()
						-rwviewer::RequestLoad("sk3ed.bsp") (see details below)
						-Create new heap for generated pieces
					-World::PreparePieceSet()
						-disconnect world sectors (source piece set) from just-loaded RpWorld
						-Script::RemoveOldTriggerScripts(), DeleteSpawnedScripts()
						-Script::LoadQB()
						-Sk3Ed_Startup (script)
				-Map::Initialize()
					-Map::readMapFromBuf()
						-Clone world sectors and link to them from the RpWorld
				-Map::GenerateWorld()
					-Generate super sectors
					-Obj::Proxim_Init()
					-Obj::Rail_Init()


=========================================================
// Probably safe for going into editor

rwviewer::RequestLoad() 
	-world_deinit()
	-sky_deinit()
	-world_init()
		-set m_level_exists
		-set m_dff_light_active
		-scene_load()
			-load bits texture dictionary
			-Gfx::SetImageDirectoryFromFileName()
			-Gfx::TexDictionaryLoad(), set it up
			-RwStreamOpen(), stream in world
			-GenerateSuperSectors()
			-load models from DFF file, gather textures from clumps, add clumps to AssMan
			-Pipeline::Chooser::FinishLoadingWorld()
			-Upload texture dictionary
		-Add cameras to world
		-run SetupViewerLights?
		-Mip K value stuff (NGPS)
		-Add lights
		-Get world sector render callback function
		-Set up particle system





=========================================================
*** LEAVING CUSTOM PARK ***

chosen_leave_server
-ParkEditor::Cleanup()
	-Map::Destroy()
	-Map::DestroyWorld()
-ScriptLeaveServer()
	-Skate::LeaveServer()
-SetNetworkMode




================================================
FILE: Code/Sk/ParkEditor2/EdMap.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

DefinePoolableClass(Ed::CMapListNode);

#ifdef __PLAT_NGC__
#define _16(a) (((a>>8)&0x00ff)|((a<<8)&0xff00))
#define _32(a) (((a>>24)&0x000000ff)|((a>>8)&0x0000ff00)|((a<<8)&0x00ff0000)|((a<<24)&0xff000000)) 
#else
#define _16(a) a
#define _32(a) a
#endif		// __PLAT_NGC__

#if 1
#ifdef	__PLAT_NGPS__
#define	__USE_EXTERNAL_BUFFER__
#include 
#include 
#define	__EXTERNAL_BUFFER__ NxPs2::dma::pRuntimeBuffer
#define	 ACQUIRE_BUFFER Nx::CEngine::sFinishRendering();
#endif
#endif

namespace Ed
{




DefineSingletonClass(CParkManager, "Park Manager");



CParkManager *CParkManager::sp_instance = NULL;

GridDims::GridDims(uint8 x, sint8 y, uint8 z, uint8 w, uint8 h, uint8 l)
{
	m_dims[0] = x;
	m_dims[1] = (uint8) y;
	m_dims[2] = z;
	m_dims[3] = w;
	m_dims[4] = h;
	m_dims[5] = l;
}




uint8 &GridDims::operator [](sint i)
{
	Dbg_Assert(i < 6);
	return m_dims[i];
}




// Effectively gives the area an H of infinity
void GridDims::MakeInfinitelyHigh()
{
	m_dims[4] = 127 - (sint8) m_dims[1];
}




// Effectively gives the area an H of infinity
void GridDims::MakeInfiniteOnY()
{
	m_dims[1] = (uint8) -100;
	m_dims[4] = 200;
}




void GridDims::PrintContents() const
{
	printf("pos=(%d,%d,%d) dims=(%d,%d,%d)\n", 
		   m_dims[0], (sint8) m_dims[1], m_dims[2],
		   m_dims[3], m_dims[4], m_dims[5]);
}




CMetaPiece::CMetaPiece()
{
	m_flags = EFlags(0);
	mp_additional_desc_tab = NULL;
	m_total_entries = 0;
	m_num_used_entries = 0;
}




CMetaPiece::~CMetaPiece()
{
	if (mp_additional_desc_tab)
		delete mp_additional_desc_tab;
}




CConcreteMetaPiece *CMetaPiece::CastToConcreteMeta()
{
	Dbg_MsgAssert(this == NULL || (m_flags & mCONCRETE_META), ("not a concrete metapiece"));
	return (CConcreteMetaPiece *) this;
}




CAbstractMetaPiece *CMetaPiece::CastToAbstractMeta()
{
	Dbg_MsgAssert(this == NULL || (m_flags & mABSTRACT_META), ("not an abstract metapiece"));
	return (CAbstractMetaPiece *) this;
}

bool CMetaPiece::IsRestartOrFlag()
{
	switch (m_name_checksum)
	{
		case CRCC(0xdc09a6a4,"Sk3Ed_RS_1p"):
		case CRCC(0xca07b4fc,"Sk3Ed_RS_Mp"):
		case CRCC(0x3a784d4c,"Sk3Ed_Rs_Ho"):
		case CRCC(0x4e2efd2a,"Sk3Ed_Rs_KOTH"):
		case CRCC(0x613c7766,"Sk4Ed_Team_Yellow"):
		case CRCC(0xec82433c,"Sk4Ed_Team_Green"):
		case CRCC(0xd7eb62b5,"Sk4Ed_Team_Red"):
		case CRCC(0x8a1576b6,"Sk4Ed_Team_Blue"):
			return true;
			break;
			
		default:
			break;
	}		
	
	return false;
}

bool CMetaPiece::IsCompletelyWithinArea(const GridDims &area)
{
	if (m_cell_area.GetX() >= area.GetX() && m_cell_area.GetX()+m_cell_area.GetW() <= area.GetX()+area.GetW() &&
		m_cell_area.GetZ() >= area.GetZ() && m_cell_area.GetZ()+m_cell_area.GetL() <= area.GetZ()+area.GetL())
	{
		return true;
	}
	return false;	
}

/*
	Use to set rotation of abstract metapiece for testing purposes (in GetMetaPiecesAt()), or
	to set rotation of concrete metapiece before adding to park.
*/
void CMetaPiece::SetRot(Mth::ERot90 newRot)
{
	Dbg_MsgAssert(!(m_flags & CMetaPiece::mIN_PARK), ("metapiece already in park -- can't change rotation"));
	
	// just want to change relative to current rotation
	Mth::ERot90 rot = Mth::ERot90((newRot + 4 - m_rot) & 3);
	m_rot = newRot;
	if (rot & 1)
	{
		// switch W and L
		m_cell_area.SetWHL(m_cell_area.GetL(), m_cell_area.GetH(), m_cell_area.GetW());
	}
	
	SMetaDescriptor temp_new_first[USUAL_NUM_DESCRIPTORS];
	
	// Create a new table, which will soon replace the old one
	SMetaDescriptor *p_meta_tab = NULL;
	if (mp_additional_desc_tab)
	{
		Mem::Manager::sHandle().PushContext(CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap());
		p_meta_tab = new SMetaDescriptor[m_total_entries-USUAL_NUM_DESCRIPTORS];
		Mem::Manager::sHandle().PopContext();
	}
	for (uint i = 0; i < m_num_used_entries; i++)
	{
		// "dims" contains position of upper-left, after rotation
		SMetaDescriptor &out = (i < USUAL_NUM_DESCRIPTORS) ? temp_new_first[i] : p_meta_tab[i-USUAL_NUM_DESCRIPTORS];
		SMetaDescriptor &in = (i < USUAL_NUM_DESCRIPTORS) ? m_first_desc_tab[i] : mp_additional_desc_tab[i-USUAL_NUM_DESCRIPTORS];
		out = in;

		// dimensions with current rotation (not new)
		// OOG!
		GridDims in_dims(0, 0, 0);
		if (m_flags & mABSTRACT_META)
		{
			CAbstractMetaPiece *p_meta = CParkManager::sInstance()->GetAbstractMeta(in.mPieceName);
			in_dims = p_meta->GetArea();
			if ((in.mRot & 1))
			{
				// for a concrete metapiece, the retrieved cell dimensions will already reflect
				// the current rotation, so no need to enter this block
				in_dims.SetWHL(in_dims.GetL(), in_dims.GetH(), in_dims.GetW()); // switch W & L
			}
		}
		else
		{
			in.mpPiece->GetCellDims(&in_dims);
		}

		switch(rot)
		{
			case Mth::ROT_0:
				out.mX = in.mX;
				out.mZ = in.mZ;
				break;
			case Mth::ROT_90:
				out.mX = in.mZ;
				out.mZ = m_cell_area.GetL() - in.mX - in_dims.GetW();
				break;
			case Mth::ROT_180:
				out.mX = m_cell_area.GetW() - in.mX - in_dims.GetW();
				out.mZ = m_cell_area.GetL() - in.mZ - in_dims.GetL();
				break;
			case Mth::ROT_270:
				out.mX = m_cell_area.GetW() - in.mZ - in_dims.GetL();
				out.mZ = in.mX;
				break;
			default:
				Dbg_MsgAssert(0, ("ahhh-oooop?"));
				break;
		}		
		
		out.mY = in.mY;
		out.mRot = Mth::ERot90((in.mRot + rot) & 3);
		
		// this will also copy in.mPieceName, if this is an abstract metapiece
		out.mpPiece = in.mpPiece;
	} // end for

	if (mp_additional_desc_tab)
		delete mp_additional_desc_tab;
	mp_additional_desc_tab = p_meta_tab;
	for (int i = 0; i < USUAL_NUM_DESCRIPTORS; i++)
	{
		m_first_desc_tab[i] = temp_new_first[i];
		//printf("desc at (%d,%d,%d)\n", m_first_desc_tab[i].mX, m_first_desc_tab[i].mY, m_first_desc_tab[i].mZ);
	}
}




// returns postion of piece's center, relative to center of metapiece
Mth::Vector CMetaPiece::GetRelativePosOfContainedPiece(int index)
{
	// center_pos in world coordinates
	Mth::Vector center_pos;
	center_pos[X] = (float) m_cell_area.GetW() * CParkGenerator::CELL_WIDTH / 2.0f;
	center_pos[Y] = 0;
	center_pos[Z] = (float) m_cell_area.GetL() * CParkGenerator::CELL_LENGTH / 2.0f;

	SMetaDescriptor &desc = get_desc_at_index(index);	
	CPiece *p_piece = GetContainedPiece(index);
	Mth::Vector piece_pos(desc.mX * CParkGenerator::CELL_WIDTH + p_piece->GetDimsWithRot(Mth::ERot90(desc.mRot)).GetX() / 2.0f,
						  desc.mY * CParkGenerator::CELL_HEIGHT,
						  desc.mZ * CParkGenerator::CELL_LENGTH + p_piece->GetDimsWithRot(Mth::ERot90(desc.mRot)).GetZ() / 2.0f);
	return piece_pos - center_pos;
}




void CMetaPiece::BuildElement3dSectorsArray(Script::CArray *pArray)
{
	pArray->SetSizeAndType(m_num_used_entries, ESYMBOLTYPE_STRUCTURE);
	for (uint i = 0; i < m_num_used_entries; i++)
	{
		SMetaDescriptor &desc = get_desc_at_index(i);	
		
		Script::CStruct *p_struct = new Script::CStruct();
		p_struct->AddChecksum(NONAME, desc.mPieceName);
		Mth::Vector pos = GetRelativePosOfContainedPiece(i);
		p_struct->AddVector(NONAME, pos.GetX(), pos.GetY(), pos.GetZ());
		
		pArray->SetStructure(i, p_struct);
	}
}




/*
	Creates	new table of SMetaDescriptors. Might copy old one.
*/
void CMetaPiece::initialize_desc_table(int numEntries)
{
	SMetaDescriptor *p_new_tab = NULL;
	if (numEntries > USUAL_NUM_DESCRIPTORS) 
	{
		Mem::Manager::sHandle().PushContext(CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap());
		p_new_tab = new SMetaDescriptor[numEntries-USUAL_NUM_DESCRIPTORS];
		Mem::Manager::sHandle().PopContext();
	}

	// Copy old table, if any. Only copied if there are to be at least as many entries as
	// there were before.
	if (p_new_tab && mp_additional_desc_tab && m_total_entries <= (uint) numEntries)
	{
		for (uint i = USUAL_NUM_DESCRIPTORS; i < m_total_entries; i++)
			p_new_tab[i-USUAL_NUM_DESCRIPTORS] = mp_additional_desc_tab[i-USUAL_NUM_DESCRIPTORS];
	}
	
	if (mp_additional_desc_tab)
		delete mp_additional_desc_tab;
	mp_additional_desc_tab = p_new_tab; 
	m_total_entries = numEntries;
}
								



/*
	Returns specified descriptor.
*/
SMetaDescriptor &CMetaPiece::get_desc_at_index(int i)
{
	if (i < USUAL_NUM_DESCRIPTORS) return m_first_desc_tab[i];

	Dbg_MsgAssert(mp_additional_desc_tab && (uint) i < m_num_used_entries, ("descriptor entry doesn't exist"));
	return mp_additional_desc_tab[i-USUAL_NUM_DESCRIPTORS];
}




CConcreteMetaPiece::CConcreteMetaPiece()
{
	m_flags = EFlags(m_flags | mCONCRETE_META);
	m_rot = 0;
}




int	CConcreteMetaPiece::CountContainedPieces()
{
	return m_num_used_entries;
}




CPiece *CConcreteMetaPiece::GetContainedPiece(int index)
{
	if ((uint) index >= m_num_used_entries || index < 0)
		return NULL;

	return get_desc_at_index(index).mpPiece;
}




/*
	Used for the cursor piece, allowing any position and rotation, not just cell-aligned with 90 degree
	increments. Doesn't affect "hard" position, rotation.
*/
void CConcreteMetaPiece::SetSoftRot(Mth::Vector pos, float rot)
{
	//Ryan("moving metapiece to (%.2f,%.2f,%.2f)\n", pos[X], pos[Y], pos[Z]);
	
	Mth::Vector center(m_cell_area.GetW() * CParkGenerator::CELL_WIDTH / 2.0f,
					   0.0f,
					   m_cell_area.GetL() * CParkGenerator::CELL_LENGTH / 2.0f);
	
	for (uint i = 0; i < m_num_used_entries; i++)
	{
		SMetaDescriptor &desc = get_desc_at_index(i);
		
		// BAKU
		Mth::Vector piece_dims = desc.mpPiece->GetDims();
		Mth::Vector piece_pos(desc.mX * CParkGenerator::CELL_WIDTH + piece_dims[X] / 2.0f - center[X],
							  desc.mY * CParkGenerator::CELL_HEIGHT - center[Y],
							  desc.mZ * CParkGenerator::CELL_LENGTH + piece_dims[Z] / 2.0f - center[Z]);

		Mth::Matrix rot_mat;
		rot_mat.Ident();
		rot_mat.SetPos(piece_pos);
		rot_mat.RotateY(Mth::DegToRad(rot));
		rot_mat.Translate(pos);

		//Ryan("    sub-piece position (%d) (%.2f,%.2f,%.2f)\n", desc.mY, rot_mat[Mth::POS][X], rot_mat[Mth::POS][Y], rot_mat[Mth::POS][Z]);
		
		desc.mpPiece->CastToCClonedPiece()->SetDesiredPos(rot_mat[Mth::POS], CClonedPiece::CHANGE_SECTOR);		
		//desc.mpPiece->CastToCClonedPiece()->SetDesiredRot(Mth::ROT_0, CClonedPiece::CHANGE_SECTOR);
		
		float soft_rot = 90.0f * (float) desc.mRot + rot;
		desc.mpPiece->CastToCClonedPiece()->SetSoftRot(soft_rot);
	}
}


// Turn on or off the highlight effect for this concrete meta piece
// which just turns the effect on or off for the cloned pieces that make it up 
void CConcreteMetaPiece::Highlight(bool on, bool gapHighlight)
{
	for (uint i = 0; i < m_num_used_entries; i++)
	{
		SMetaDescriptor &desc = get_desc_at_index(i);
		if (desc.mpPiece->GetFlags() & CPiece::mCLONED_PIECE)
		{
			desc.mpPiece->CastToCClonedPiece()->Highlight(on, gapHighlight);
		}	
	}
}



void CConcreteMetaPiece::SetVisibility(bool visible)
{
	for (uint i = 0; i < m_num_used_entries; i++)
	{
		SMetaDescriptor &desc = get_desc_at_index(i);
		desc.mpPiece->CastToCClonedPiece()->SetActive(visible);
	}
}




/*
	Use after calls to add_piece() have been made. Once metapiece has been locked, it can be added to 
	the world or used as a cursor
*/
void CConcreteMetaPiece::lock()
{
	// work out dimensions of metapiece from contained pieces
	Mth::Vector min_pos, max_pos;
	get_max_min_world_coords(max_pos, min_pos);
	
	Mth::Vector my_dims;
	my_dims.Set(max_pos.GetX() - min_pos.GetX(), max_pos.GetY() - min_pos.GetY(), max_pos.GetZ() - min_pos.GetZ());

	m_cell_area.SetWHL((uint8) ((my_dims.GetX() + 2.0f) / CParkGenerator::CELL_WIDTH),
					   (uint8) ((my_dims.GetY() + 2.0f) / CParkGenerator::CELL_HEIGHT),
					   (uint8) ((my_dims.GetZ() + 2.0f) / CParkGenerator::CELL_LENGTH));

	if (m_cell_area[H] < 1) m_cell_area[H] = 1;
	
	//Dbg_Assert(m_cell_area.mW > 0 && m_cell_area.mL > 0);

	// reposition all contained pieces relative to center bottom
	for (uint i = 0; i < m_num_used_entries; i++)
	{
		CClonedPiece *p_piece = get_desc_at_index(i).mpPiece->CastToCClonedPiece();
		
		//Mth::Vector piece_dims = p_piece->GetDims();
		p_piece->m_pos[X] = p_piece->m_pos.GetX() - min_pos.GetX() - my_dims.GetX() / 2.0f;
		p_piece->m_pos[Z] = p_piece->m_pos.GetZ() - min_pos.GetZ() - my_dims.GetZ() / 2.0f;
		p_piece->m_pos[Y] = p_piece->m_pos.GetY(); // - min_pos.GetY();
	}
}




/*
	Called by lock()
*/
void CConcreteMetaPiece::get_max_min_world_coords(Mth::Vector &max, Mth::Vector &min)
{
	min.Set(100000.0f, 100000.0f, 100000.0f, 0.0f);
	max.Set(-100000.0f, -100000.0f, -100000.0f, 0.0f);

	for (uint i = 0; i < m_num_used_entries; i++)
	{
		CClonedPiece *p_piece = get_desc_at_index(i).mpPiece->CastToCClonedPiece();
		
		Mth::Vector piece_dims = p_piece->GetDims();
		Mth::Vector piece_pos = p_piece->GetPos();

		if (piece_pos.GetX() - piece_dims.GetX() / 2.0f < min.GetX())
			min[X] = piece_pos.GetX() - piece_dims.GetX() / 2.0f;
		if (piece_pos.GetX() + piece_dims.GetX() / 2.0f > max.GetX())
			max[X] = piece_pos.GetX() + piece_dims.GetX() / 2.0f;
		
		if (piece_pos.GetZ() - piece_dims.GetZ() / 2.0f < min.GetZ())
			min[Z] = piece_pos.GetZ() - piece_dims.GetZ() / 2.0f;
		if (piece_pos.GetZ() + piece_dims.GetZ() / 2.0f > max.GetZ())
			max[Z] = piece_pos.GetZ() + piece_dims.GetZ() / 2.0f;
		
		if (piece_pos.GetY() < min.GetY())
			min[Y] = piece_pos.GetY();
		if (piece_pos.GetY() + piece_dims.GetY() > max.GetY())
			max[Y] = piece_pos.GetY() + piece_dims.GetY();
	}

	//Dbg_Assert(max[X] - min[X] > 1.0f && max[Z] - min[Z] > 1.0f);
}




/* 
	Determines if overlap between specified area of park and this metapiece.
	
	If excludeRisers set, then we ignore overlaps with contained CPieces that are risers.
*/
bool CConcreteMetaPiece::test_for_conflict(GridDims area, bool excludeRisers)
{
	// for now, we simply see if this metapiece overlaps with the area
	// later, we may add code that tests against contained pieces

	Dbg_Assert(m_cell_area.GetW() > 0 && m_cell_area.GetL() > 0 && m_cell_area.GetH() > 0);
	Dbg_MsgAssert(area.GetW() > 0 && area.GetL() > 0 && area.GetH() > 0, ("invalid dimensions (%d,%d,%d)", 
																		  area.GetW(), area.GetW(), area.GetL()));

	// do quick test
	
	if (m_cell_area.GetX() + m_cell_area.GetW() <= area.GetX())
		return false;
	if (m_cell_area.GetX() >= area.GetX() + area.GetW())
		return false;
	if (m_cell_area.GetY() + m_cell_area.GetH() <= area.GetY())
		return false;
	if (m_cell_area.GetY() >= area.GetY() + area.GetH())
		return false;
	if (m_cell_area.GetZ() + m_cell_area.GetL() <= area.GetZ())
		return false;
	if (m_cell_area.GetZ() >= area.GetZ() + area.GetL())
		return false;

	bool contains_risers = false;
   	if (excludeRisers)
	{
		for (uint i = 0; i < m_num_used_entries; i++)
		{
			SMetaDescriptor &desc = get_desc_at_index(i);
			if (desc.mRiser) contains_risers = true;
		}
	}
	// '|| if !excludeRisers' is implicit
	if (!contains_risers) return true;
	
	for (uint i = 0; i < m_num_used_entries; i++)
	{
		SMetaDescriptor &desc = get_desc_at_index(i);
		CClonedPiece *p_piece = GetContainedPiece(i)->CastToCClonedPiece();
		GridDims piece_area;
		p_piece->GetCellDims(&piece_area);
		piece_area[X] = m_cell_area.GetX() + desc.mX;
		piece_area[Y] = m_cell_area.GetY() + desc.mY;
		piece_area[Z] = m_cell_area.GetZ() + desc.mZ;
	
		if (!desc.mRiser &&
			piece_area.GetX() + piece_area.GetW() > area.GetX() &&
			piece_area.GetY() + piece_area.GetH() > area.GetY()	&&
			piece_area.GetZ() + piece_area.GetL() > area.GetZ() &&
			piece_area.GetX() < area.GetX() + area.GetW() &&
			piece_area.GetY() < area.GetY() + area.GetH() &&
			piece_area.GetZ() < area.GetZ() + area.GetL())
		{
			return true;
		}
	}
		
	return false;
}




/*
	See comments with original declaration.
	
	It is fine if pPiece has been rotated already.
*/
void CConcreteMetaPiece::add_piece(CPiece *pPiece, GridDims *pPos, Mth::ERot90 rot, bool isRiser)
{
	Dbg_Assert(pPiece);
	Dbg_MsgAssert(!(pPiece->GetFlags() & CPiece::mIN_WORLD), ("piece already in world"));
	
	CClonedPiece *p_cloned = pPiece->CastToCClonedPiece();
	#ifdef __NOPT_ASSERT__
	if (m_num_used_entries > 0)
	{
		if (get_desc_at_index(0).mpPiece->GetFlags() & CPiece::mSOFT_PIECE)
		{
			Dbg_MsgAssert(pPiece->GetFlags() & CPiece::mSOFT_PIECE, ("must be a soft piece"));
		}
		else
		{
			Dbg_MsgAssert(!(pPiece->GetFlags() & CPiece::mSOFT_PIECE), ("must be a hard piece"));
		}
	}
	#endif

	Dbg_MsgAssert(m_num_used_entries < m_total_entries, ("out of entries"));
	SMetaDescriptor &desc = get_desc_at_index(m_num_used_entries++);

	// Set position relative to northwest bottom of piece. For now.
	Mth::Vector piece_dims = p_cloned->GetDims();
	Mth::Vector desired_pos;
	desired_pos.Set(pPos->GetX() * CParkGenerator::CELL_WIDTH + piece_dims.GetX() / 2.0f,
						  pPos->GetY() * CParkGenerator::CELL_HEIGHT, // + piece_dims.GetY() / 2.0f,
						  pPos->GetZ() * CParkGenerator::CELL_LENGTH + piece_dims.GetZ() / 2.0f);
	p_cloned->SetDesiredPos(desired_pos, CClonedPiece::MARKER_ONLY);

	desc.mpPiece = p_cloned;
	desc.mX = pPos->GetX();
	desc.mY = pPos->GetY();
	desc.mZ = pPos->GetZ();
	desc.mRot = rot;
	desc.mRiser = isRiser ? 1 : 0;

	/*
	if (isRiser)
	{
		printf("hoogaboog 2 (%d,%d,%d), desc.mRiser is %d, desc.mRiser at 0x%x, index %d\n", 
			   desc.mX, desc.mY, desc.mZ, desc.mRiser, &desc.mRiser, m_num_used_entries-1);
	}
	*/
}




CAbstractMetaPiece::CAbstractMetaPiece()
{
	m_flags = EFlags(m_flags | mABSTRACT_META);
}




CPiece *CAbstractMetaPiece::GetContainedPiece(int index)
{
	if ((uint) index >= m_num_used_entries || index < 0)
		return NULL;

	CPiece *p_piece = CParkManager::sInstance()->GetGenerator()->GetMasterPiece(get_desc_at_index(index).mPieceName);
	Dbg_Assert(p_piece);
	return p_piece;
}




/*
	See comments with original declaration. It is fine if pPiece has been rotated already.
	
	Automatically changes stored cell WHL dimensions of metapiece to include new addition.
*/
void CAbstractMetaPiece::add_piece(CPiece *pPiece, GridDims *pPos, Mth::ERot90 rot, bool isRiser)
{
	Dbg_Assert(pPiece);
	CSourcePiece *p_source = NULL;
	if (pPiece->GetFlags() & CPiece::mSOURCE_PIECE)
		p_source = pPiece->CastToCSourcePiece();
	if (!p_source)
		Dbg_MsgAssert(0, ("not supported yet"));

	Dbg_MsgAssert(m_num_used_entries < m_total_entries, ("out of entries"));
	SMetaDescriptor &desc = get_desc_at_index(m_num_used_entries++);

	if (p_source)
	{
		desc.mX = pPos->GetX();
		desc.mY = pPos->GetY();
		desc.mZ = pPos->GetZ();
		desc.mRot = rot;
		desc.mRiser = (uint8) isRiser;
		desc.mPieceName = p_source->GetType();

		int highest_x = pPos->GetX() + pPos->GetW();
		int highest_y = pPos->GetY() + pPos->GetH();
		int highest_z = pPos->GetZ() + pPos->GetL();

		uint8 current_w = m_cell_area.GetW();
		uint8 current_h = m_cell_area.GetH();
		uint8 current_l = m_cell_area.GetL();
		
		if (highest_x > current_w)
			current_w = highest_x;
		if (highest_y > current_h)
			current_h = highest_y;
		if (highest_z > current_l)
			current_l = highest_z;

		m_cell_area.SetWHL(current_w, current_h, current_l);
		if (m_cell_area[H] < 1) m_cell_area[H] = 1;
	}
	
	/*
	if (isRiser)
	{
		printf("hoogaboog 1 (%d,%d,%d), index %d\n", desc.mX, desc.mY, desc.mZ, m_num_used_entries-1);
	}
	*/
}




void CAbstractMetaPiece::add_piece_dumb(CAbstractMetaPiece *pMeta, GridDims *pPos, Mth::ERot90 rot, bool isRiser)
{
	Dbg_MsgAssert(m_num_used_entries < m_total_entries, ("out of entries"));
	SMetaDescriptor &desc = get_desc_at_index(m_num_used_entries++);

	if (pMeta)
	{
		desc.mX = pPos->GetX();
		desc.mY = pPos->GetY();
		desc.mZ = pPos->GetZ();
		desc.mRot = rot;
		desc.mRiser = (uint8) isRiser;
		desc.mPieceName = pMeta->GetNameChecksum();

		int highest_x = pPos->GetX() + pPos->GetW();
		int highest_y = pPos->GetY() + pPos->GetH();
		int highest_z = pPos->GetZ() + pPos->GetL();

		uint8 current_w = m_cell_area.GetW();
		uint8 current_h = m_cell_area.GetH();
		uint8 current_l = m_cell_area.GetL();
		
		if (highest_x > current_w)
			current_w = highest_x;
		if (highest_y > current_h)
			current_h = highest_y;
		if (highest_z > current_l)
			current_l = highest_z;

		m_cell_area.SetWHL(current_w, current_h, current_l);
	}
	
	/*
	if (isRiser)
	{
		printf("hoogaboog 1 (%d,%d,%d), index %d\n", desc.mX, desc.mY, desc.mZ, m_num_used_entries-1);
	}
	*/
}




SMetaDescriptor CAbstractMetaPiece::get_desc_with_expansion(int index)
{
	#if 1
	int count = 0;
	for (uint j = 0; j < m_num_used_entries; j++)
	{
		SMetaDescriptor &outer_desc = get_desc_at_index(j);

		CAbstractMetaPiece *p_contained_meta = CParkManager::sInstance()->GetAbstractMeta(outer_desc.mPieceName);
		if (p_contained_meta->m_flags & CMetaPiece::mSINGULAR)
		{
			if (count == index)
				return outer_desc;
			count++;
		}
		else
		{
			for (uint i = 0; i < p_contained_meta->m_num_used_entries; i++)
			{
				SMetaDescriptor &inner_desc = p_contained_meta->get_desc_at_index(i);
				if (count == index)
				{
					SMetaDescriptor ret_desc = inner_desc;
					ret_desc.mPieceName = inner_desc.mPieceName;
					ret_desc.mRot = inner_desc.mRot + outer_desc.mRot;
					
					GridDims outer_dims = GetArea();

					GridDims inner_dims = p_contained_meta->GetArea();
					
					ret_desc.mX = outer_desc.mX;
					ret_desc.mZ = outer_desc.mZ;
					ret_desc.mY = outer_desc.mY + inner_desc.mY;
					
					switch(outer_desc.mRot)
					{
						case Mth::ROT_0:
							ret_desc.mX += inner_desc.mX;
							ret_desc.mZ += inner_desc.mZ;
							break;
						case Mth::ROT_90:
							ret_desc.mX += inner_desc.mZ;
							ret_desc.mZ += outer_dims.GetW() - inner_desc.mX - inner_dims.GetW();
							break;
						case Mth::ROT_180:
							ret_desc.mX += outer_dims.GetW() - inner_desc.mX - inner_dims.GetW();
							ret_desc.mZ += outer_dims.GetL() - inner_desc.mZ - inner_dims.GetL();
							break;
						case Mth::ROT_270:
							ret_desc.mX += outer_dims.GetL() - inner_desc.mZ - inner_dims.GetL();
							ret_desc.mZ += inner_desc.mX;
							break;
					}
					
					return ret_desc;
				}
				count++;
			}
		}
	}
	Dbg_MsgAssert(0, ("what the bloody 'ell?"));
	SMetaDescriptor dumb;
	return dumb;
	#else
	return get_desc_at_index(index);
	#endif
}




int CAbstractMetaPiece::count_descriptors_expanded()
{
	#if 1
	int count = 0;
	for (uint j = 0; j < m_num_used_entries; j++)
	{
		SMetaDescriptor &outer_desc = get_desc_at_index(j);

		CAbstractMetaPiece *p_contained_meta = CParkManager::sInstance()->GetAbstractMeta(outer_desc.mPieceName);
		Dbg_MsgAssert(p_contained_meta, ("couldn't find metapiece %s", Script::FindChecksumName(outer_desc.mPieceName)));
		if (p_contained_meta->m_flags & CMetaPiece::mSINGULAR)
		{
			count++;
		}
		else
		{
			for (uint i = 0; i < p_contained_meta->m_num_used_entries; i++)
			{
				count++;
			}
		}
	}
	return count;
	#else
	return m_num_used_entries;
	#endif
}




CMetaPiece *CMapListNode::GetMeta()
{
	Dbg_Assert(this);
	Dbg_MsgAssert(((uint32)this) != 0x03030303,("Bad CMapListNode pointer ! (0x03030303)"));
	Dbg_MsgAssert(((uint32)this) != 0x55555555,("Bad CMapListNode pointer ! (0x55555555)"));
	Dbg_Assert(mp_meta);
	return mp_meta;
}




void CMapListNode::DestroyList()
{
	CMapListNode *p_node = this;
	while (p_node)
	{
		CMapListNode *p_next_node = p_node->mp_next;
		delete p_node;
		p_node = p_next_node;
	}
}




void CMapListNode::VerifyIntegrity()
{
	CMapListNode *p_outer_node = this;
	while (p_outer_node)
	{
		CMapListNode *p_inner_node = p_outer_node->mp_next;
		while (p_inner_node)
		{
			Dbg_Assert(p_inner_node->GetMeta() != p_outer_node->GetMeta());
			p_inner_node = p_inner_node->mp_next;
		}
		p_outer_node = p_outer_node->mp_next;
	}
}




CMapListTemp::CMapListTemp(CMapListNode *p_list)
{
	mp_list = p_list;
}




CMapListTemp::~CMapListTemp()
{
	if (mp_list)
		mp_list->DestroyList();
}




CMapListNode *CMapListTemp::GetList()
{
	return mp_list;
}




void CMapListTemp::PrintContents()
{
	printf("contents of map list:\n");
	CMapListNode *p_node = mp_list;
	while(p_node)
	{
		printf("   address=%p area=", p_node->GetMeta());
		p_node->GetMeta()->GetArea().PrintContents();
		p_node = p_node->GetNext();
	}
}




CParkManager::CParkManager()
{
	mp_generator = new CParkGenerator();
	
	mp_concrete_metapiece_list = NULL;
	mp_abstract_metapiece_list = NULL;
	m_num_concrete_metapieces = 0;
	m_num_general_concrete_metapieces = 0;
	m_dma_piece_count = 0;
	m_num_abstract_metapieces = 0;

	m_floor_height_map = NULL;
	
	for (int i = 0; i < FLOOR_MAX_WIDTH; i++)
		for (int j = 0; j < FLOOR_MAX_LENGTH; j++)
			mp_bucket_list[i][j] = NULL;

	m_state_on = false;

	sp_instance = this;

	mp_compressed_map_buffer = new uint8[COMPRESSED_MAP_SIZE];
	m_compressed_map_flags = mNO_FLAGS;
	m_park_is_valid = false;

	mp_gap_manager = NULL;
	
	for (int i = 0; i < NUM_RISER_INFO_SLOTS; i++)
	{
		char name0[128];
		sprintf(name0, "floor_wall_block%d", i+1);
		m_riser_piece_checksum[i][0] = Script::GenerateCRC(name0);
		
		char name1[128];
		sprintf(name1, "wall_block%d", i+1);
		m_riser_piece_checksum[i][1] = Script::GenerateCRC(name1);
		
		char name2[128];
		sprintf(name2, "floor_block%d", i+1);
		m_riser_piece_checksum[i][2] = Script::GenerateCRC(name2);
	}

	m_theme = 0;
	
	// Mick: Create a compressed map buffer, equivalent to an empty park								
	fake_compressed_map_buffer();

	mp_column_slide_list = NULL;	
	
	mp_park_name[0]=0;
	
	#ifdef USE_BUILD_LIST
	m_build_list_size=0;
	mp_build_list_entry=NULL;
	#endif
}




CParkManager::~CParkManager()
{
	#ifdef USE_BUILD_LIST
	if (mp_build_list_entry)
	{
		#ifndef	__USE_EXTERNAL_BUFFER__
		Mem::Free(mp_build_list_entry);
		#endif
		mp_build_list_entry=NULL;
	}	
	#endif
	
	delete mp_generator;
	delete mp_compressed_map_buffer;

	sp_instance = NULL;
}




/*
	Boots up park editor.
	
	-Loads up files needed
	-Initializes buffers
	-Initializes CParkGenerator module
	-Calls Rebuild(), which:
		-Clears map
		-Generates riser geometry
	-Creates piece set database
*/
void CParkManager::Initialize()
{
	ParkEd("CParkManager::Initialize()");
	
	if (m_state_on)
		return;
	m_state_on = true;


	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	Mem::CPoolable::SCreatePool(16000, "CMapListNode");
	Mem::CPoolable::SPrintInfo();

	Mem::CPoolable::SCreatePool(MAX_CLIPBOARD_METAS, "CClipboardEntry");
	Mem::CPoolable::SCreatePool(MAX_CLIPBOARDS, "CClipboard");
	Mem::Manager::sHandle().PopContext();

	mp_generator->InitializeMasterPieces(MAX_WIDTH, 16, MAX_LENGTH, GetTheme());
	mp_generator->ReadInRailInfo();

	create_abstract_metapieces();

	setup_road_mask();
	
	Dbg_MsgAssert(!m_floor_height_map, ("bad monkey!!!"));
	m_floor_height_map = new FloorHeightEntry*[FLOOR_MAX_WIDTH];
	for (int i = 0; i < FLOOR_MAX_WIDTH; i++)
	{
		m_floor_height_map[i] = new FloorHeightEntry[FLOOR_MAX_LENGTH];		
		for (int j = 0; j < FLOOR_MAX_LENGTH; j++)
		{
			m_floor_height_map[i][j].mMarkAsSlid = false;
			m_floor_height_map[i][j].mSlideAmount = 0;
		}
	}


	setup_default_dimensions();
		
	mp_gap_manager = new CGapManager(this);
	
	RebuildWholePark(false);

	Script::CArray *p_set_array = Script::GetArray("Ed_piece_sets", Script::ASSERT);
	m_total_sets = p_set_array->GetSize();
	Dbg_Assert(m_total_sets <= MAX_SETS);

	Script::CArray *p_piece_array = Script::GetArray("Ed_standard_metapieces", Script::ASSERT);
	int last_piece_index=p_piece_array->GetSize();
	int last_set_index=-1;
	
	for (int s = m_total_sets-1; s >=0 ; --s)
	{
		Script::CStruct *p_set = p_set_array->GetStructure(s);
		
		m_palette_set[s].mpName = NULL;
		p_set->GetText("name", &m_palette_set[s].mpName, Script::ASSERT);
		m_palette_set[s].mNameCrc = Script::GenerateCRC(m_palette_set[s].mpName);
		m_palette_set[s].mSelectedEntry = 0;
		
		m_palette_set[s].mTotalEntries=0;

		uint32 first_checksum;
		if (p_set->GetChecksum("first", &first_checksum))
		{
			int first_index=0;
			if (!GetAbstractMeta(first_checksum, &first_index))
			{
				Dbg_MsgAssert(0, ("couldn't find first-in-set entry"));
			}

			// make sure index in previous first-in_set_entry is less than this one
			if (last_set_index>=0)
			{
				Dbg_MsgAssert(first_index <
							  last_piece_index,
							  ("sets %s and %s are out of order in EdPieces2.q", 
							   m_palette_set[s].mpName,
							   m_palette_set[last_set_index].mpName));
			}
			else
			{
				Dbg_MsgAssert(first_index <
							  last_piece_index,
							  ("set %s has a bad first piece in EdPieces2.q", 
							   m_palette_set[s].mpName));
			}
			m_palette_set[s].mTotalEntries=last_piece_index-first_index;
			last_piece_index=first_index;
			last_set_index=s;
			
			// Now that we know the number of entries, create the set.
			int entry=first_index;
			for (int i=0; iGetNameChecksum();
				
				Script::CStruct *p_piece_entry = p_piece_array->GetStructure(entry);
				if (!p_piece_entry->GetLocalString("text_name", &m_palette_set[s].mEntryTab[i].mpName))
				{
					m_palette_set[s].mEntryTab[i].mpName = NULL;
				}
			
				++entry;
			}
		}

		m_palette_set[s].mIsClipboardSet=false;
		if (p_set->ContainsFlag(CRCD(0x45c6c158,"clipboard_set")))
		{
			m_palette_set[s].mIsClipboardSet=true;
			m_palette_set[s].mTotalEntries=MAX_CLIPBOARDS;
			
			const char *p_clipboard_title=Script::GetString(CRCD(0x4e677220,"ClipboardTitle"));
			
			for (int i=0; iContainsFlag("hidden");
		#ifdef __PLAT_NGC__
		if (p_set->ContainsFlag("no_gamecube"))
			m_palette_set[s].mVisible = false;
		#endif
	}
}

float CParkManager::GetClipboardProportionUsed()
{
	float proportion_clipboard_entries=((float)Mem::CPoolable::SGetNumUsedItems())/Mem::CPoolable::SGetTotalItems();
	float proportion_clipboard_slots=((float)Mem::CPoolable::SGetNumUsedItems())/Mem::CPoolable::SGetTotalItems();

	if (proportion_clipboard_entries > proportion_clipboard_slots)
	{
		return proportion_clipboard_entries;
	}
	return proportion_clipboard_slots;
}
	
// This is just a public version of the private function
// needed so the parks can be saved from within the park editor
// This just updates the contents of the map buffer that will be used
// by the enxt two public functions
void CParkManager::WriteCompressedMapBuffer()
{
	if (m_state_on)
		write_compressed_map_buffer();	
}

uint8* CParkManager::GetCompressedMapBuffer(bool markSaved)
{
	/*
		The compressed map buffer is valid if:
		
		-The park manager is active AND (in sync with built park OR newer than built park) OR
		-The park manager is not active
	*/
	
	Dbg_MsgAssert((m_compressed_map_flags & mIS_VALID), ("compressed map buffer not valid")); 
	if (m_state_on)
	{
		Dbg_MsgAssert((m_compressed_map_flags & mIN_SYNC_WITH_PARK) || (m_compressed_map_flags & mIS_NEWER_THAN_PARK),
					  ("park has changed, compressed map buffer out of sync"));
	}

#ifdef __NOPT_ASSERT__
	CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer;
	Dbg_Printf( "************ NUM METAS: %d\n", p_header->mNumMetas );
#endif		// __NOPT_ASSERT__

	if (markSaved)
		m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mNOT_SAVED_LOCAL);
	
	return mp_compressed_map_buffer;
}




void	CParkManager::SetCompressedMapBuffer( uint8* src_buffer, bool markNotSaved )
{
	ParkEd("SetCompressedMapBuffer()");
	
	
	memcpy( mp_compressed_map_buffer, src_buffer, COMPRESSED_MAP_SIZE );
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIS_VALID);
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIS_NEWER_THAN_PARK);

	if (markNotSaved)
		m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mNOT_SAVED_LOCAL);

#ifdef __NOPT_ASSERT__
	CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer;
	//uint32 checksum=Crc::GenerateCRCCaseSensitive((const char*)mp_compressed_map_buffer+4,COMPRESSED_MAP_SIZE-4);
	//Dbg_MsgAssert(checksum==p_header->mChecksum,("Park editor buffer checksum mismatch !"));
	
	Dbg_Printf( "************ NUM METAS: %d\n", p_header->mNumMetas );
#endif		// __NOPT_ASSERT__
}




bool CParkManager::IsMapSavedLocally()
{
	if ((!(m_compressed_map_flags & mIN_SYNC_WITH_PARK) && !(m_compressed_map_flags & mIS_NEWER_THAN_PARK)) || 
		(m_compressed_map_flags & mNOT_SAVED_LOCAL))
		return false;
	return true;
}


void CParkManager::WriteIntoStructure(Script::CStruct *p_struct)
{
	WriteCompressedMapBuffer();		// Ensure map buffer is correct
	uint32	*p_map_buffer = (uint32*) GetCompressedMapBuffer(true);
	int		size = COMPRESSED_MAP_SIZE ;

	// create the array
	Script::CArray *p_map=new Script::CArray;
	p_map->SetArrayType(size/4,ESYMBOLTYPE_INTEGER);
	// copy the park editor into it
	for (int i=0;iSetInteger(i,p_map_buffer[i]);
	}
	
	// append it to p_struct
	p_struct->AddArrayPointer(CRCD(0x337c5289,"Park_editor_map"),p_map);

	// Now also add in any created goals, so that the goals do not have to saved out to a separate file.
	Script::CStruct *p_goals_struct=new Script::CStruct;
	
	Obj::CGoalEditorComponent *p_goal_editor=Obj::GetGoalEditor();
	Dbg_MsgAssert(p_goal_editor,("No goal editor component ???"));
	p_goal_editor->WriteIntoStructure(CRCD(0xe8b4b836,"Load_Sk5Ed"),p_goals_struct);
	
	
	p_struct->AddStructurePointer(CRCD(0xd8eb825e,"Park_editor_goals"),p_goals_struct);

	// Add in the created rails.			
	Obj::GetRailEditor()->WriteIntoStructure(p_struct);

	p_struct->AddInteger(CRCD(0xb7e39b53,"MaxPlayers"),GetGenerator()->GetMaxPlayers());
}

#ifdef __PLAT_NGC__
void CParkManager::ReadFromStructure(Script::CStruct *p_struct, bool do_post_mem_card_load, bool preMadePark)
#else
void CParkManager::ReadFromStructure(Script::CStruct *p_struct, bool do_post_mem_card_load)
#endif
{
	Obj::CGoalEditorComponent *p_goal_editor=Obj::GetGoalEditor();
	Dbg_MsgAssert(p_goal_editor,("No goal editor component ???"));
	
	p_goal_editor->ClearOnlyParkGoals();
	
	// Read in any created goals.
	Script::CStruct *p_goals_struct=NULL;
	if (p_struct->GetStructure(CRCD(0xd8eb825e,"Park_editor_goals"),&p_goals_struct))
	{
		p_goal_editor->ReadFromStructure(p_goals_struct,Obj::CGoalEditorComponent::LOADING_PARK_GOALS);
	}

	// Read in the created rails
	Spt::SingletonPtr p_park_ed;
	if (!p_park_ed->EditingCustomPark())
	{
		// Do not call UpdateSuperSectors after deleting any existing rails if playing a park,
		// otherwise the entire park will disappear from under the skater due to all the sectors
		// having been flagged for deletion.
		Obj::CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors=false;
	}	
	Obj::GetRailEditor()->ReadFromStructure(p_struct);
	Obj::CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors=true;
	
	int max_players=2;
	p_struct->GetInteger(CRCD(0xb7e39b53,"MaxPlayers"),&max_players);
	Ed::CParkManager::Instance()->GetGenerator()->SetMaxPlayers(max_players);
				
	int old_theme = GetTheme();
	WriteCompressedMapBuffer();
	uint32	*p_map_buffer = (uint32*) GetCompressedMapBuffer();
	int		size = Ed::CParkManager::COMPRESSED_MAP_SIZE ;
	// get the array
	Script::CArray *p_map=NULL;
	if (p_struct->GetArray("Park_editor_map",&p_map,true))
	{
		// copy the park editor map from structure to buffer
		for (int i=0;iGetInteger(i);
				p_map_buffer[i] = ((v>>24) | (((v>>16)&0xff)<<8) | (((v>>8)&0xff)<<16) | ((v&0xff)<<24));
			}	
			else
			{
				p_map_buffer[i] = p_map->GetInteger(i);
			}		
			#else
			p_map_buffer[i] = p_map->GetInteger(i);
			#endif
		}

		#ifdef __PLAT_NGC__
		if (preMadePark)
		{
			// If loading a pre-made park, it will need the map buffer endianness swapped since
			// it was saved from the PS2.
			SwapMapBufferEndianness();
		}	
		#endif
		
		if (do_post_mem_card_load)
		{
			Spt::SingletonPtr p_park_ed;
			p_park_ed->PostMemoryCardLoad((uint8*) p_map_buffer, old_theme);
		}	
	}
}

int CParkManager::GetTheme()
{
	if (m_compressed_map_flags & mIS_VALID)
	{
		int theme = (((CompressedMapHeader *) mp_compressed_map_buffer)->mTheme);
		Dbg_MsgAssert(theme >= 0 && theme <= MAX_THEMES, ("nonsense theme %d", theme));
		return theme;
	}

	return 0;
}




void CParkManager::SetTheme(int theme)
{
	Dbg_MsgAssert(theme >= 0 && theme < MAX_THEMES , ("nonsense theme %d", theme));
	m_theme = theme;
	write_compressed_map_buffer();
}




const char *CParkManager::GetParkName()
{
	if (mp_compressed_map_buffer && ((CompressedMapHeader *) mp_compressed_map_buffer)->mVersion == (VERSION))
		return ((CompressedMapHeader *) mp_compressed_map_buffer)->mParkName;
	else
		return NULL;
}

uint32 CParkManager::GetParkChecksum()
{
	if (mp_compressed_map_buffer && ((CompressedMapHeader *) mp_compressed_map_buffer)->mVersion == (VERSION))
		return ((CompressedMapHeader *) mp_compressed_map_buffer)->mChecksum;
	else
		return 0;
}

int	CParkManager::GetCompressedParkWidth()
{
	if (mp_compressed_map_buffer && ((CompressedMapHeader *) mp_compressed_map_buffer)->mVersion == (VERSION))
		return ((CompressedMapHeader *) mp_compressed_map_buffer)->mW;
	else
		return 0;
}

int	CParkManager::GetCompressedParkLength()
{
	if (mp_compressed_map_buffer && ((CompressedMapHeader *) mp_compressed_map_buffer)->mVersion == (VERSION))
		return ((CompressedMapHeader *) mp_compressed_map_buffer)->mL;
	else
		return 0;
}


void CParkManager::SetParkName(const char *pName)
{
	if (!mp_compressed_map_buffer || ((CompressedMapHeader *) mp_compressed_map_buffer)->mVersion != (VERSION)) return;
	Dbg_Assert(pName);
	Dbg_MsgAssert(strlen(pName) < 64, ("park name too long"));
	strcpy(((CompressedMapHeader *) mp_compressed_map_buffer)->mParkName, pName);
	strcpy(mp_park_name, pName);
}




void CParkManager::AccessDisk(bool save, int fileSlot)
{
	char fullname[64];
	sprintf(fullname, "CustomParks\\custom%d.prk", fileSlot);

	if (save)
	{
//		Ryan("I am saving park %s\n", fullname);
//		printf("I am saving park %s\n", fullname);
		
		Script::CStruct *p_struct=new Script::CStruct;
		WriteIntoStructure(p_struct);
		uint32 size=Script::CalculateBufferSize(p_struct);
		uint8 *p_buffer=(uint8*)Mem::Malloc(size);
		Script::WriteToBuffer(p_struct,p_buffer,size);
		
		void *fp = File::Open(fullname, "wb");
		Dbg_MsgAssert(fp, ("failed to open file %s for write", fullname));
		File::Write(p_buffer, size, 1, fp);
		File::Close(fp);
		
		Mem::Free(p_buffer);
		delete p_struct;
	}
	else
	{
//		Ryan("I am loading park %s\n", fullname);
//		printf("I am loading park %s\n", fullname);
		void *fp = File::Open(fullname, "rb");
		Dbg_MsgAssert(fp, ("failed to open file %s for read", fullname));
		
		int size=File::GetFileSize(fp);

		uint8 *p_buffer=(uint8*)Mem::Malloc(size);
		
		File::Read(p_buffer, size, 1, fp);
		File::Close(fp);
		
		Script::CStruct *p_struct=new Script::CStruct;
		Script::ReadFromBuffer(p_struct,p_buffer);
		Mem::Free(p_buffer);

		#ifdef __PLAT_NGC__
		ReadFromStructure(p_struct,false,true);
		#else
		ReadFromStructure(p_struct,false);
		#endif
		
		delete p_struct;
				
		m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | (mIS_NEWER_THAN_PARK + mIS_VALID));
		m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIN_SYNC_WITH_PARK);
		m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mNOT_SAVED_LOCAL);
	}	
}




/*
	Destroys everything, concrete metas, abstract metas, floor map.
*/
// K: Added type so that DESTROY_ONLY_PIECES can be passed when this function is used to clean
// up the park editor heap for deletion when playing a park.
void CParkManager::Destroy(CParkGenerator::EDestroyType type)
{
	ParkEd("CParkManager::Destroy(), m_compressed_map_flags=%08x", m_compressed_map_flags);
	
	if (!m_state_on)
		return;
	
	if (!(m_compressed_map_flags & mIN_SYNC_WITH_PARK) && !(m_compressed_map_flags & mIS_NEWER_THAN_PARK))
	{
		write_compressed_map_buffer();
	}
	
	Dbg_Assert(mp_gap_manager);
	mp_gap_manager->RemoveAllGaps();
	
	// remove all abstract metapieces
	CMapListNode *p_node = NULL;
	while((p_node = mp_abstract_metapiece_list))
	{
		CMetaPiece *p_meta = p_node->GetMeta();
		remove_metapiece_from_node_list(p_meta, &mp_abstract_metapiece_list);
		delete p_meta;
	}
	m_num_abstract_metapieces = 0;
	
	destroy_concrete_metapieces(type);
	
	mp_generator->UnloadMasterPieces();
	mp_generator->DestroyRailInfo();   		// info from node array (mostly rails)
	
	Dbg_MsgAssert(m_floor_height_map, ("bad bad monkey!!!"));
	for (int i = 0; i < FLOOR_MAX_WIDTH; i++)
		delete [] m_floor_height_map[i];
	delete [] m_floor_height_map;
	m_floor_height_map = NULL;
	
	m_state_on = false;
	// since park is destroyed, buffer is out of sync and newer than park
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIN_SYNC_WITH_PARK);
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIS_NEWER_THAN_PARK);
	m_park_is_valid = false;
	
	Dbg_Assert(mp_gap_manager);
	delete mp_gap_manager;
	mp_gap_manager = NULL;
	
	if (mp_column_slide_list)
		mp_column_slide_list->DestroyList();
	mp_column_slide_list = NULL;
	
	Mem::CPoolable::SRemovePool();	
	Mem::CPoolable::SRemovePool();	
	Mem::CPoolable::SRemovePool();
}


void CParkManager::RebuildWholePark(bool clearPark)
{
	ParkEd("CParkManager::RebuildWholePark() 0x%x", m_compressed_map_flags);

	setup_lighting();  // ensure light arrays are correct for current theme
	
	Dbg_Assert(m_state_on);
	
	if (!(m_compressed_map_flags & mIN_SYNC_WITH_PARK) && 
		!(m_compressed_map_flags & mIS_NEWER_THAN_PARK) && 
		m_park_is_valid)
	{
		//Ryan("writing compressed map buffer\n");
		write_compressed_map_buffer();
	}
	
	Dbg_Printf( "************* Rebuilding whole park : %d\n", (int) clearPark );
	destroy_concrete_metapieces(CParkGenerator::DESTROY_PIECES_AND_SECTORS);
	mp_generator->GenerateCollisionInfo(false);

	if (clearPark || !(m_compressed_map_flags & mIS_VALID))
	{
		//Ryan("clearing park\n");
		Dbg_MsgAssert(m_floor_height_map, ("bad bad monkey!!!"));
		
		for (int j = 0; j < FLOOR_MAX_LENGTH; j++)
		{
			for (int i = 0; i < FLOOR_MAX_WIDTH; i++)
			{
				int x = i;
				int z = j;
				m_floor_height_map[x][z].mHeight = 0; //Mth::Rnd(4);
				m_floor_height_map[x][z].mEnclosedFloor = 0;
				m_floor_height_map[x][z].mMarkAsSlid = false;
				m_floor_height_map[i][j].mSlideAmount = 0; 
			}
		}
		
		write_compressed_map_buffer();
		SetParkName("unnamed park");
		m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mNOT_SAVED_LOCAL);
		clearPark = true;
	}	
	else
	{
		// use compressed map
		read_from_compressed_map_buffer();
	}
	
	RebuildInnerShell(m_park_near_bounds);
	generate_floor_pieces_from_height_map(clearPark, false);	
	
	mp_generator->GenerateCollisionInfo();
//	mp_generator->GenerateNodeInfo(mp_concrete_metapiece_list);

	m_park_is_valid = true;

	if (clearPark)
	{
		m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIN_SYNC_WITH_PARK);
	}
	
	// K: Now that the park geometry has been built and collision info generated, 
	// update the created rails.
	Obj::GetRailEditor()->RefreshGeometry();
}




/*
	-Might clear map
	-Generates riser geometry (from height map)

	The only place we call this function with firstTestMemory true is from CCursor::ChangeFloorHeight()
*/
bool CParkManager::RebuildFloor(bool firstTestMemory)
{
	ParkEd("CParkManager::RebuildFloor()");
	
	Dbg_Assert(m_state_on);
	
	//Dbg_Printf( "************* Rebuilding floor\n" );

	if (firstTestMemory)
	{
		if (!generate_floor_pieces_from_height_map(false, true))
			return false;
	}

	generate_floor_pieces_from_height_map(false, false);
	
	//CConcreteMetaPiece *p_out = CreateConcreteMeta(GetAbstractMeta(Script::GenerateCRC("gloopy")));
	//GridDims dims(m_park_near_bounds.GetX(), 0, m_park_near_bounds.GetZ());
	//AddMetaPieceToPark(dims, p_out);
	
	mp_generator->GenerateCollisionInfo();

	return true;
	
}


void CParkManager::RebuildNodeArray()
{
	ParkEd("CParkManager::RebuildNodeArray()");


	// clear any pre-exisiting automatic restarts
	// so we have slots free to add new ones	
	mp_generator->ClearAutomaticRestarts();

 
	// add default restarts
	// (they won't be added if player-placed ones already exist)
	// there are 18 points
	// 1p, 2p, 4 flags, 6 horse, 6 KOTH
	for (int p = 0; p < 18; p++)
	{
		CParkGenerator::RestartType restart_type = CParkGenerator::vONE_PLAYER;
		
		GridDims restart_area;
		restart_area = GetParkNearBounds();		 // returns the rectangle enclosed by the fence
		restart_area.SetXYZ(restart_area.GetX()+restart_area.GetW()/2, 0, restart_area.GetZ()+restart_area.GetL()/2);	// sets xyz to the midpoint
		restart_area.SetWHL(1,1,1);		// needed for when we get the floor height
						

		bool	find_ground = true;			// most restarts need to find the ground, except copied horse points
						
		int need = 1;		// Most restarts only need one.  Horse/Multi being the current exception						
		if (p == 1)
		{
			restart_type = CParkGenerator::vMULTIPLAYER;
			restart_area.SetXYZ(restart_area.GetX()+1, 0, restart_area.GetZ());	
		}

		if (p == 2)
		{
			restart_type = CParkGenerator::vRED_FLAG;
			restart_area.SetXYZ(restart_area.GetX()-2, 0, restart_area.GetZ()+2);	
		}

		if (p == 3)
		{
			restart_type = CParkGenerator::vGREEN_FLAG;
			restart_area.SetXYZ(restart_area.GetX()+0, 0, restart_area.GetZ()+2);	
		}

		if (p == 4)
		{
			restart_type = CParkGenerator::vBLUE_FLAG;
			restart_area.SetXYZ(restart_area.GetX()+2, 0, restart_area.GetZ()+2);	
		}

		if (p == 5)
		{
			restart_type = CParkGenerator::vYELLOW_FLAG;
			restart_area.SetXYZ(restart_area.GetX()+4, 0, restart_area.GetZ()+2);	
		}

		// all the dummy horse/multi restart points go in a line at Z-2		
		if (p >= 6 && p <=11)				 	// 6,7,8,9,10,11
		{
			need = 6;			
			restart_type = CParkGenerator::vHORSE;
			
			#if 0
			// Code to make horse points be evenly distributed between 1p/2p nodes
			// but it's not usable, as the players will start at the same point
			// either all multi points need to be different, of the code needs
			// to check in 2P (and multi) for same node positions
			// either are too risky to implement right now 			
			static GridDims horse_points[6];    // needs to be static, so I can include it in this conditional
			GridDims  dims;
			if (p==6)
			{
				// about to try the first horse point
				// so fill the array with appropriate values
				int num_actual_horse = mp_generator->NumRestartsOfType(restart_type);
//				printf ("There are %d actual horse points\n",num_actual_horse);
				if (num_actual_horse == 0)
				{
//					printf ("No horse points, using 1p/2p\n");
					// not even got one, so fill the array with 1p,2p,1p,2p,1p,2p
					for (int i = 0;i<6;i+=2)
					{
						dims = mp_generator->GetRestartDims(CParkGenerator::vONE_PLAYER,0);
						horse_points[i] = restart_area;
						horse_points[i].SetXYZ(dims.GetX(),0,dims.GetZ());
						dims = mp_generator->GetRestartDims(CParkGenerator::vMULTIPLAYER,0);
						horse_points[i+1] = restart_area;
						horse_points[i+1].SetXYZ(dims.GetX(),0,dims.GetZ());
					}					 
				}
				else
				{
					// we have one or more real horse points, so find out where they are
					// and copy those values for any automatic horse points, if needed
					for (int i = 0;i<6;i++)
					{
						if (i < num_actual_horse)
						{
							// copy the n initial values into the array
							horse_points[i] = mp_generator->GetRestartDims(CParkGenerator::vHORSE,i);
						}
						else
						{
							// and duplicate these through the rest of the array
							//  so we will get an even distribution
							//  n = 1  gives [ 1 1 1 1 1 1 ]
							//  n = 2  gives [ 1 2 1 2 1 2 ]
							//  n = 3  gives [ 1 2 3 1 2 3 ]
							//  n = 4  gives [ 1 2 3 4 1 2 ]
							//  n = 5  gives [ 1 2 3 4 5 1 ]
							//  n = 6  gives [ 1 2 3 4 5 6 ]
							horse_points[i] = horse_points[i % num_actual_horse];
						}
//						printf ("Horse %d: (%d,%d,%d)\n",horse_points[i].GetX(),horse_points[i].GetY(),horse_points[i].GetX());
					}					 					
				}
			}
			
			// copy the appropiate value from the array
			restart_area = horse_points[p-6];
			find_ground = false;			// Don't find the ground when copying existing restarts, as we will be raised up by 1p/2p/horse pieces
			//printf ("Using Restart Area(%d,%d,%d)\n",restart_area.GetX(), restart_area.GetY(), restart_area.GetZ());

															 
			#else			
			restart_area.SetXYZ(restart_area.GetX()-3 + (p-6), 0, restart_area.GetZ()-2);	
			//printf ("Using Restart Area(%d,%d,%d)\n",restart_area.GetX(), restart_area.GetY(), restart_area.GetZ());
			#endif
		
		}
		
		bool	auto_copy = false;
		
		if ( p >= 12 && p <= 17 )  					// 12,13,14,15,16,17
		{
			auto_copy = true;						// copy any existing restart point position, if there is one
			need = 6;
			restart_type = CParkGenerator::vKING_OF_HILL;
			restart_area.SetXYZ(restart_area.GetX()-3 + (p-12), 0, restart_area.GetZ()-3);	 
		}

		if (mp_generator->NotEnoughRestartsOfType(restart_type, need))
		{
			if (find_ground)
			{
				restart_area.SetXYZ(restart_area.GetX(),GetFloorHeight(restart_area),restart_area.GetZ());
				CMapListTemp meta_list = GetMetaPiecesAt(restart_area);
				if (!meta_list.IsEmpty())
				{
					restart_area[Y] = restart_area.GetY() + meta_list.GetList()->GetConcreteMeta()->GetArea().GetH();
				}
			}

			Mth::Vector restart_pos = GridCoordinatesToWorld(restart_area);
			restart_pos[X] = restart_pos[X] + CParkGenerator::CELL_WIDTH / 2.0f;
			restart_pos[Z] = restart_pos[Z] + CParkGenerator::CELL_LENGTH / 2.0f;
			
			// Fix to TT12221, where the flags would be floating if over a pool.
			CFeeler feeler;
			feeler.SetStart(restart_pos+Mth::Vector(0.0f, 10000.0f, 0.0f));
			feeler.SetEnd(restart_pos+Mth::Vector(0.0f, -1000.0f, 0.0f));
			if (feeler.GetCollision())
			{
				restart_pos[Y]=feeler.GetPoint().GetY();
			}	

//			printf("restart point %d at: cell=(%d,%d,%d) world=(%.4f,%.4f,%.4f)\n", p, 
//				 restart_area.GetX(), restart_area.GetY(), restart_area.GetZ(),
//				 restart_pos[X], restart_pos[Y], restart_pos[Z]);

			mp_generator->AddRestart(restart_pos, 0, restart_area, restart_type, true, auto_copy);
		}
	} // end for p

	mp_generator->GenerateNodeInfo(mp_concrete_metapiece_list);	
}


uint32 CParkManager::GetShellSceneID()
{
	char p_shell_name[40];
	if (GetTheme())
	{
		sprintf(p_shell_name,"sk5ed%d_shell",GetTheme()+1);
	}
	else
	{
		strcpy(p_shell_name,"sk5ed_shell");
	}
	return Script::GenerateCRC(p_shell_name);
}

void CParkManager::RebuildInnerShell(GridDims newNearBounds)
{
	ParkEd("CParkManager::RebuildInnerShell()");
	
	Dbg_Printf( "************* Rebuilding inner shell\n" );

	#if 1
	ParkEd("old shell bounds: (%d,%d,%d)-(%d,%d,%d)\n", 
		 m_park_near_bounds.GetX(), m_park_near_bounds.GetY(), m_park_near_bounds.GetZ(),
		 m_park_near_bounds.GetW(), m_park_near_bounds.GetH(), m_park_near_bounds.GetL());
	#endif
	m_park_near_bounds = newNearBounds;
	#if 1
	ParkEd("new shell bounds: (%d,%d,%d)-(%d,%d,%d)\n", 
		 newNearBounds.GetX(), newNearBounds.GetY(), newNearBounds.GetZ(),
		 newNearBounds.GetW(), newNearBounds.GetH(), newNearBounds.GetL());
	#endif
	
	// west
	m_road_mask_tab[0][X] = m_park_near_bounds.GetX() - 1;
	m_road_mask_tab[0][Z] = m_park_near_bounds.GetZ() - 1;
	m_road_mask_tab[0][W] = 1;
	m_road_mask_tab[0][L] = m_park_near_bounds.GetL() + 2;
	
	// east
	m_road_mask_tab[1][X] = m_park_near_bounds.GetX() + m_park_near_bounds.GetW();
	m_road_mask_tab[1][Z] = m_park_near_bounds.GetZ() - 1;
	m_road_mask_tab[1][W] = 1;
	m_road_mask_tab[1][L] = m_park_near_bounds.GetL() + 2;
	
	// north
	m_road_mask_tab[2][X] = m_park_near_bounds.GetX() - 1;
	m_road_mask_tab[2][Z] = m_park_near_bounds.GetZ() - 1;
	m_road_mask_tab[2][W] = m_park_near_bounds.GetW() + 2;
	m_road_mask_tab[2][L] = 1;
	
	// south
	m_road_mask_tab[3][X] = m_park_near_bounds.GetX() - 1;
	m_road_mask_tab[3][Z] = m_park_near_bounds.GetZ() + m_park_near_bounds.GetL();
	m_road_mask_tab[3][W] = m_park_near_bounds.GetW() + 2;
	m_road_mask_tab[3][L] = 1;
	
	//#if DEBUG_THIS_DAMN_THING
	//int destroy_type2_count = 0;
	//#endif
		
	// destroy all existing inner shell pieces
	// also: kill everything that's outside the near bounds
	CMapListNode *p_node = mp_concrete_metapiece_list;
	while(p_node)
	{
		CMapListNode *p_next_node = p_node->GetNext();
		GridDims meta_area = p_node->GetConcreteMeta()->GetArea();
		
		if (p_node->GetConcreteMeta()->IsInnerShell())
		{
			DestroyConcreteMeta(p_node->GetConcreteMeta(), mDONT_DESTROY_PIECES_ABOVE);
		}
		#if 1
		else if ( !p_node->GetConcreteMeta()->IsRiser() &&  
			p_node->GetConcreteMeta()->IsInPark() &&
			(meta_area.GetX() < m_park_near_bounds.GetX() ||
			meta_area.GetZ() < m_park_near_bounds.GetZ() ||
			meta_area.GetX() + meta_area.GetW() > m_park_near_bounds.GetX() + m_park_near_bounds.GetW() ||
			meta_area.GetZ() + meta_area.GetL() > m_park_near_bounds.GetZ() + m_park_near_bounds.GetL()))
		{
			DestroyConcreteMeta(p_node->GetConcreteMeta(), mDONT_DESTROY_PIECES_ABOVE);
			//#if DEBUG_THIS_DAMN_THING
			//destroy_type2_count++;
			//#endif
		}
		#endif

		p_node = p_next_node;
	}
	
	uint32 fence_meta_name_crc = Script::GenerateCRC("Sk4Ed_Fence_20x20");
	CAbstractMetaPiece *p_abstract_fence_meta = GetAbstractMeta(fence_meta_name_crc);
	
	for (int x = m_park_near_bounds.GetX(); x < m_park_near_bounds.GetX() + m_park_near_bounds.GetW(); x += 2)
	{
		GridDims road_test_area;
		road_test_area.SetWHL(2, 1, 1);

		// north
		GridDims pos(x, 0, m_park_near_bounds.GetZ() - 1);
		road_test_area.SetXYZ(pos.GetX(), 0, pos.GetZ() + 1);
		if (!IsOccupiedByRoad(road_test_area))
		{
			CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_abstract_fence_meta);
			p_meta->SetRot(Mth::ROT_0);
			p_meta->MarkAsInnerShell();
			AddMetaPieceToPark(pos, p_meta);
		}
	
		// south
		pos.SetXYZ(x, 0, m_park_near_bounds.GetZ() + m_park_near_bounds.GetL());
		road_test_area.SetXYZ(pos.GetX(), 0, pos.GetZ() - 1);
		if (!IsOccupiedByRoad(road_test_area))
		{
			CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_abstract_fence_meta);
			p_meta->SetRot(Mth::ROT_180);
			p_meta->MarkAsInnerShell();
			AddMetaPieceToPark(pos, p_meta);
		}
	}

	for (int z = m_park_near_bounds.GetZ(); z < m_park_near_bounds.GetZ() + m_park_near_bounds.GetL(); z += 2)
	{
		GridDims road_test_area;
		road_test_area.SetWHL(1, 1, 2);

		// west
		GridDims pos(m_park_near_bounds.GetX() - 1, 0, z);
		road_test_area.SetXYZ(pos.GetX() + 1, 0, pos.GetZ());
		if (!IsOccupiedByRoad(road_test_area))
		{
			CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_abstract_fence_meta);
			p_meta->SetRot(Mth::ROT_90);
			p_meta->MarkAsInnerShell();
			AddMetaPieceToPark(pos, p_meta);
		}
	
		// east
		pos.SetXYZ(m_park_near_bounds.GetX() + m_park_near_bounds.GetW(), 0, z);
		road_test_area.SetXYZ(pos.GetX() - 1, 0, pos.GetZ());
		if (!IsOccupiedByRoad(road_test_area))
		{
			CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_abstract_fence_meta);
			p_meta->SetRot(Mth::ROT_270);
			p_meta->MarkAsInnerShell();
			AddMetaPieceToPark(pos, p_meta);
		}
	}

	//ParkEd("destroyed %d non-riser pieces\n", destroy_type2_count); 

	for (int x = 0; x < MAX_WIDTH; x++)
	{
		for (int z = 0; z < MAX_LENGTH; z++)
		{
			if (x < m_park_near_bounds.GetX() ||
				z < m_park_near_bounds.GetZ() ||
				x >= m_park_near_bounds.GetX() + m_park_near_bounds.GetW() ||
				z >= m_park_near_bounds.GetZ() + m_park_near_bounds.GetL())
			{
				m_floor_height_map[x][z].mHeight = 0;
			}
		}
	}

	mp_generator->RemoveOuterShellPieces(GetTheme());
	mp_generator->GenerateCollisionInfo();

//	mp_generator->GenerateNodeInfo(mp_concrete_metapiece_list);
	ParkEd("end RebuildInnerShell()\n");
}




/*
	Retrieves the abstract meta with the specified name checksum. Returns its index.
*/
CAbstractMetaPiece *CParkManager::GetAbstractMeta(uint32 type, int *pRetIndex)
{
	Dbg_Assert(m_state_on);
	
	CMapListNode *p_node = mp_abstract_metapiece_list;
	int index = m_num_abstract_metapieces-1;
	while(p_node)
	{
		CAbstractMetaPiece *p_meta = p_node->GetAbstractMeta();
		Dbg_Assert(p_meta);
		if (p_meta->m_name_checksum == type)
		{
			if (pRetIndex)
				*pRetIndex = index;
			return p_meta;
		}

		index--;
		p_node = p_node->GetNext();
	}

	return NULL;
}




/*
	Retrieves the abstract meta with the specified index.
*/
CAbstractMetaPiece *CParkManager::GetAbstractMetaByIndex(int index)
{
	Dbg_Assert(m_state_on);
	
	CMapListNode *p_node = mp_abstract_metapiece_list;
	int i = m_num_abstract_metapieces-1;
	while(p_node)
	{
		CAbstractMetaPiece *p_meta = p_node->GetAbstractMeta();
		Dbg_Assert(p_meta);
		if (i == index)
			return p_meta;

		i--;
		p_node = p_node->GetNext();
	}

	Dbg_MsgAssert(0, ("index %d out of range", i));
	return NULL;	
}




/*
	Creates a concrete copy of the specified abstract meta.
*/
CConcreteMetaPiece *CParkManager::CreateConcreteMeta(CAbstractMetaPiece *pSource, bool isSoft)
{
	Dbg_Assert(m_state_on);
	Dbg_Assert(pSource);
	
	Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap());
	CConcreteMetaPiece *p_meta = new CConcreteMetaPiece();
	Mem::Manager::sHandle().PopContext();
	
	int num_entries = pSource->count_descriptors_expanded();
	p_meta->initialize_desc_table(num_entries);
	p_meta->m_name_checksum = pSource->m_name_checksum;
	
	CPiece::EFlags clone_flags = (isSoft) ? CPiece::mSOFT_PIECE : CPiece::mNO_FLAGS;
	
	if (pSource->IsRiser())
	{
		p_meta->set_flag(CMetaPiece::mIS_RISER);
		clone_flags	= CPiece::EFlags(clone_flags | CPiece::mFLOOR);
	}
	if (pSource->NoRails())
	{
		p_meta->set_flag(CMetaPiece::mNO_RAILS);
		clone_flags	= CPiece::EFlags(clone_flags | CPiece::mNO_RAILS);
	}
	if (pSource->m_flags & CMetaPiece::mCONTAINS_RISERS)
	{
		p_meta->set_flag(CMetaPiece::mCONTAINS_RISERS);
	}

	// pick through source piece, for each descriptor entry, make new cloned piece
	for (int i = 0; i < num_entries; i++)
	{
		SMetaDescriptor desc = pSource->get_desc_with_expansion(i);
		
		CClonedPiece *p_piece = 
			mp_generator->ClonePiece(CParkManager::sInstance()->GetGenerator()->GetMasterPiece(desc.mPieceName), clone_flags);
		p_piece->SetDesiredRot(Mth::ERot90(desc.mRot), CClonedPiece::MARKER_ONLY);
		GridDims dims(desc.mX, desc.mY, desc.mZ);
		p_meta->add_piece(p_piece, &dims, Mth::ERot90(desc.mRot), (desc.mRiser != 0));

		#if 0
		// Ryan: this call is unnecessary
		if (!isSoft)
		{
			mp_generator->CalculateLighting(p_piece);
		}
		#endif

		#if 0
		if (pSource->GetNameChecksum() == Script::GenerateCRC("Low_Short_Deck"))
		{
			printf("   rotation: %d\n", desc.mRot);
		}
		#endif
	}

	p_meta->lock();
	   
	#if 1
	#ifdef __NOPT_ASSERT__
	GridDims test_area = p_meta->GetArea();
	Dbg_MsgAssert(test_area.GetW() > 0 && test_area.GetH() > 0 && test_area.GetL() > 0, 
				  ("metapiece %s has invalid dimensions (%d,%d,%d)", Script::FindChecksumName(p_meta->GetNameChecksum()),
				   test_area.GetW(), test_area.GetH(), test_area.GetL()));
	
	#if 0
	if (1 || p_meta->GetNameChecksum() == Script::GenerateCRC("Sk4Ed_Fence_10x10"))
		printf("OY! metapiece %s has dimensions (%d,%d,%d)\n", Script::FindChecksumName(p_meta->GetNameChecksum()),
				   test_area.GetW(), test_area.GetH(), test_area.GetL());
	#endif

	#endif
	#endif
	
	Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap());
	CMapListNode *p_node = new CMapListNode();
	Mem::Manager::sHandle().PopContext();
	p_node->mp_meta = p_meta;

	#if 0	
	p_node->mp_next = mp_concrete_metapiece_list;
	mp_concrete_metapiece_list = p_node;
	#else
	p_node->mp_next = NULL;		
	if (!mp_concrete_metapiece_list)
	{
		mp_concrete_metapiece_list = p_node;
	}
	else
	{
		CMapListNode *p_add = mp_concrete_metapiece_list;
		while( p_add->mp_next)
		{
			p_add = p_add->mp_next;
		}
		p_add->mp_next = p_node;
	}
	#endif
	
	m_num_concrete_metapieces++;

	return p_meta;
}




void CParkManager::DestroyConcreteMeta(CConcreteMetaPiece *pMeta, EDestroyFlags flags)
{
	Dbg_Assert(m_state_on);
	if (pMeta->m_flags & CMetaPiece::mLOCK_AGAINST_DELETE) return;
	Dbg_MsgAssert(!(pMeta->m_flags & CMetaPiece::mLOCK_AGAINST_DELETE), ("Metapiece is locked, can't deleted"));

	if (CCursor::sInstance(false))
		CCursor::sInstance()->InformOfMetapieceDeletion(pMeta);
	
	// remove any gaps attached to this piece
	RemoveGap(pMeta);

	if (!pMeta->IsInnerShell() && pMeta->IsInPark())
	{
		decrement_meta_count(pMeta->GetNameChecksum());
		if (!pMeta->IsRiser())
		{
			m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~(mIN_SYNC_WITH_PARK | mIS_NEWER_THAN_PARK));
		}	
	}
	
	// destroy all metas that are ABOVE this one
	if (!pMeta->IsRiser() && (flags & mDESTROY_PIECES_ABOVE))
	{
		GridDims test_area = pMeta->GetArea();
		test_area[Y] = test_area.GetY() + test_area.GetH();
		test_area.MakeInfinitelyHigh();
		// XXX
		Ryan("die now: ");
		//test_area.PrintContents();
		DestroyMetasInArea(test_area, EDestroyFlags(mEXCLUDE_RISERS	| mDESTROY_PIECES_ABOVE));
	}
	
	apply_meta_contained_risers_to_floor(pMeta, false);
	
	debucketify_metapiece(pMeta);
	remove_metapiece_from_node_list(pMeta, &mp_concrete_metapiece_list);
	
	#if 0
	printf("concrete metapiece %s destroyed: cell dims (%d,%d,%d)-(%d,%d,%d), flags=0x%x\n",
		   Script::FindChecksumName(pMeta->GetNameChecksum()),
		   pMeta->m_cell_area.GetX(), pMeta->m_cell_area.GetY(), pMeta->m_cell_area.GetZ(), 
		   pMeta->m_cell_area.GetW(), pMeta->m_cell_area.GetH(), pMeta->m_cell_area.GetL(),
		   pMeta->m_flags);
	#endif

										 
	CParkGenerator::RestartType restart_type = IsRestartPiece(pMeta->GetNameChecksum());		
	if (restart_type != CParkGenerator::vEMPTY)
	{
		mp_generator->RemoveRestart(pMeta->GetArea(),restart_type);
	}
	
	// take out metapiece
	for (uint i = 0; i < pMeta->m_num_used_entries; i++)
	{
		CClonedPiece *p_piece = pMeta->get_desc_at_index(i).mpPiece->CastToCClonedPiece();
		Dbg_Assert(p_piece);
		mp_generator->DestroyClonedPiece(p_piece);
	}
	
	delete pMeta;
	m_num_concrete_metapieces--;
}


void CParkManager::HighlightMetasInArea(GridDims area, bool on)
{
	CMapListTemp list = GetAreaMetaPieces(area);
	CMapListNode *p_entry = list.GetList();

	while(p_entry)
	{
		CMetaPiece *p_meta=p_entry->GetMeta();
		if (p_meta)
		{
			if (p_meta->IsConcrete() && p_meta->IsCompletelyWithinArea(area))
			{
				p_entry->GetConcreteMeta()->Highlight(on);
			}	
		}	
		p_entry = p_entry->GetNext();
	}
}

/*
GridDims CParkManager::FindBoundingArea(GridDims area)
{
	int min_x=1000000;	
	int min_z=1000000;	
	int max_x=-1000000;
	int max_z=-1000000;
	
	CMapListTemp list = GetAreaMetaPieces(area);
	CMapListNode *p_entry = list.GetList();

	while(p_entry)
	{
		CMetaPiece *p_meta=p_entry->GetMeta();
		if (p_meta && p_meta->IsConcrete())
		{
			GridDims meta_area = p_entry->GetConcreteMeta()->GetArea();
			int x1=meta_area.GetX();
			int z1=meta_area.GetZ();
			int x2=x1+meta_area.GetW()-1;
			int z2=z1+meta_area.GetL()-1;
			
			if (x1 < min_x) min_x=x1;
			if (x2 > max_x) max_x=x2;
			if (z1 < min_z) min_z=z1;
			if (z2 > max_z) max_z=z2;
		}	
		p_entry = p_entry->GetNext();
	}
	
	GridDims bounding_area=area;
	if (min_x <= max_x && min_z <= max_z)
	{
		bounding_area.SetXYZ(min_x, 0, min_z);
		bounding_area.SetWHL(max_x-min_x+1, 1, max_z-min_z+1);
	}
	return bounding_area;
}
*/

// K: Does the same as CCursor::AttemptRemove() but for any area
bool CParkManager::DestroyMetasInArea(GridDims area, bool doNotDestroyRestartsOrFlags, bool onlyIfWithinArea)
{
	bool destroyed_something=false;
	CMapListTemp metas_at_pos = GetAreaMetaPieces(area);
	CMapListNode *p_entry = metas_at_pos.GetList();
	while (p_entry)
	{
		CConcreteMetaPiece *p_meta = p_entry->GetConcreteMeta();
		if (p_meta->IsRiser() || 
			(doNotDestroyRestartsOrFlags && p_meta->IsRestartOrFlag()) ||
			(onlyIfWithinArea && !p_meta->IsCompletelyWithinArea(area)))
		{
		}
		else
		{
			DestroyConcreteMeta(p_meta, CParkManager::mDESTROY_PIECES_ABOVE);
			destroyed_something=true;
		}	
		
		p_entry = p_entry->GetNext();
	}

	// Also destroy any rail points in the area.
	Mth::Vector corner_pos;
	Mth::Vector area_dims;
	corner_pos=Ed::CParkManager::Instance()->GridCoordinatesToWorld(area,&area_dims);
	Obj::GetRailEditor()->ClipRails(corner_pos[X], corner_pos[Z],
									corner_pos[X]+area_dims[X], corner_pos[Z]+area_dims[Z],
									Obj::CRailEditorComponent::DELETE_POINTS_INSIDE);
	
	if (destroyed_something)
	{
		RebuildFloor();
		return true;
	}
		
	return false;
}


/*
	Destroys concrete metapieces in specified area, according to flags.
	
	Flags:
	-mDESTROY_PIECES_ABOVE: if set, whenever a meta is destroyed, recursively
		destroy all metas above it
		
	Return value:
	upper 16 bits: number metas killed
	lower 16 bits: number pieces killed
*/
uint32 CParkManager::DestroyMetasInArea(GridDims area, EDestroyFlags flags)
{
	uint16 metas_killed = 0;
	uint16 pieces_killed = 0;
	CMapListNode *p_list = GetMetaPiecesAt(area, Mth::ROT_0, NULL, (flags & mEXCLUDE_RISERS));
	CMapListNode *p_node = p_list;
	while (p_node)
	{
		if (!(flags & mEXCLUDE_PIECES_MARKED_AS_SLID) || !(p_node->GetConcreteMeta()->m_flags & CMetaPiece::mMARK_AS_SLID))
		{
			metas_killed++;
			pieces_killed += p_node->GetConcreteMeta()->m_num_used_entries;
			DestroyConcreteMeta(p_node->GetConcreteMeta(), flags);
		}
		p_node = p_node->GetNext();
	}
	p_list->DestroyList();

	return ((metas_killed<<16) | pieces_killed);
}

int CParkManager::get_dma_usage(uint32 pieceChecksum)
{
	Script::CStruct *p_dma_usage=Script::GetStructure(CRCD(0x9547da8d,"DMA_Usage"),Script::ASSERT);
	int usage=1;
	p_dma_usage->GetInteger(pieceChecksum, &usage);
	return usage;
}	

void CParkManager::increment_meta_count(uint32 pieceChecksum)
{
	++m_num_general_concrete_metapieces;
	m_dma_piece_count += get_dma_usage(pieceChecksum);
	
	//printf("IncrementMetaCount '%s', %d   DMA=%d\n",Script::FindChecksumName(pieceChecksum),m_num_general_concrete_metapieces,m_dma_piece_count);
}

void CParkManager::decrement_meta_count(uint32 pieceChecksum)
{
	--m_num_general_concrete_metapieces;
	m_dma_piece_count -= get_dma_usage(pieceChecksum);
	
	//printf("DecrementMetaCount '%s' %d   DMA=%d\n",Script::FindChecksumName(pieceChecksum),m_num_general_concrete_metapieces,m_dma_piece_count);
	Dbg_MsgAssert(m_dma_piece_count >= 0,("Negative m_dma_piece_count of %d\n",m_dma_piece_count));
}

/*
	Position is in cell coordinates, relative to northwest corner of 256X256 grid.
	Y coordinate is relative to "ground level", can be + or -.

	Rotation is 0-3.
*/
void CParkManager::AddMetaPieceToPark(GridDims pos, CConcreteMetaPiece *pMetaPiece)
{
	Dbg_Assert(m_state_on);
	
	Dbg_MsgAssert(!(pMetaPiece->m_flags & CMetaPiece::mIN_PARK), ("metapiece already in park"));
	pMetaPiece->set_flag(CMetaPiece::mIN_PARK);

	pMetaPiece->m_cell_area.SetXYZ(pos.GetX(), pos.GetY(), pos.GetZ());

	// center_pos in world coordinates
	Mth::Vector center_pos;
	center_pos[X] = ((float) pos.GetX() + (float) pMetaPiece->m_cell_area.GetW() / 2.0f) * CParkGenerator::CELL_WIDTH;
	center_pos[X] -= (CParkGenerator::CELL_WIDTH * (float) CParkManager::FLOOR_MAX_WIDTH) / 2.0f;
	center_pos[Z] = ((float) pos.GetZ() + (float) pMetaPiece->m_cell_area.GetL() / 2.0f) * CParkGenerator::CELL_LENGTH;
	center_pos[Z] -= (CParkGenerator::CELL_LENGTH * (float) CParkManager::FLOOR_MAX_LENGTH) / 2.0f;
	center_pos[Y] = pos.GetY() * CParkGenerator::CELL_HEIGHT;

	// XXX
	#if 0
	Dbg_Printf("adding piece to park: (%d,%d,%d), (%.2f,%.2f,%.2f)\n", 
		 pos.GetX(), pos.GetY(), pos.GetZ(), 
		 center_pos[X], center_pos[Y], center_pos[Z]);
	#endif
	
	for (uint i = 0; i < pMetaPiece->m_num_used_entries; i++)
	{
		Mth::Vector rel_pos = pMetaPiece->GetRelativePosOfContainedPiece(i);
		CClonedPiece *p_piece = pMetaPiece->GetContainedPiece(i)->CastToCClonedPiece();
		
		// reposition piece from metapiece-relative coordinates to world-relative
		Mth::Vector abs_pos;
		abs_pos[X] = center_pos[X] + rel_pos[X]; 
		abs_pos[Z] = center_pos[Z] + rel_pos[Z]; 
		abs_pos[Y] = center_pos[Y] + rel_pos[Y];
		
		p_piece->SetDesiredPos(abs_pos, CClonedPiece::CHANGE_SECTOR);
		p_piece->SetDesiredRot(Mth::ERot90((p_piece->GetRot() + pMetaPiece->m_rot) & 3), CClonedPiece::CHANGE_SECTOR);
		mp_generator->AddClonedPieceToWorld(p_piece);
		mp_generator->CalculateLighting(p_piece);
	}

	apply_meta_contained_risers_to_floor(pMetaPiece, true);		
	bucketify_metapiece(pMetaPiece);

	// If it's a restart point, then add that to the list of restart points
	CParkGenerator::RestartType restart_type = IsRestartPiece(pMetaPiece->GetNameChecksum());		
	if (restart_type != CParkGenerator::vEMPTY)
	{
		mp_generator->AddRestart(center_pos,pMetaPiece->m_rot,pos,restart_type,false);
	}

	if (!pMetaPiece->IsInnerShell())
	{
		if (!pMetaPiece->IsRiser())
		{
			m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~(mIN_SYNC_WITH_PARK | mIS_NEWER_THAN_PARK));
		}
		increment_meta_count(pMetaPiece->GetNameChecksum());
	}
	
}




/*
	"Moves" a metapiece from one position to another. Really, the old metapiece is destroyed, and a new copy created,
	so be careful to replace any pointers to old one.
*/
CConcreteMetaPiece *CParkManager::RelocateMetaPiece(CConcreteMetaPiece *pMeta, int changeX, int changeY, int changeZ)
{
	GridDims area = pMeta->GetArea();
	uint32 name = pMeta->GetNameChecksum();
	Mth::ERot90 rot = pMeta->GetRot();
	CMetaPiece::EFlags old_flags = pMeta->m_flags;

	// get gap associated with metapiece
	int gap_half;
	bool gap_exists = false;
	CGapManager::GapDescriptor *p_gap_desc = GetGapDescriptor(area, &gap_half);
	CGapManager::GapDescriptor new_gap_desc;
	if (p_gap_desc)
	{
		gap_exists = true;
		new_gap_desc = *p_gap_desc;
		
		// remove it
		RemoveGap(*p_gap_desc);

		// shift gap descriptor to new position
		new_gap_desc.loc[gap_half][X] = new_gap_desc.loc[gap_half].GetX() + changeX;
		new_gap_desc.loc[gap_half][Y] = new_gap_desc.loc[gap_half].GetY() + changeY;
		new_gap_desc.loc[gap_half][Z] = new_gap_desc.loc[gap_half].GetZ() + changeZ;
	}
	
	DestroyConcreteMeta(pMeta, mDONT_DESTROY_PIECES_ABOVE);
	
	area[X] += changeX;
	area[Y] = area.GetY() + changeY;
	area[Z] += changeZ;

	CConcreteMetaPiece *p_new_meta = CreateConcreteMeta(GetAbstractMeta(name), false);
	p_new_meta->SetRot(rot);
	p_new_meta->m_flags = old_flags;
	p_new_meta->clear_flag(CMetaPiece::mIN_PARK);
	AddMetaPieceToPark(area, p_new_meta);

	if (gap_exists)
	{
		AddGap(new_gap_desc);
	}

	return p_new_meta;
}




/*
	Returns list of concrete metapieces in specified area.
	
	Modes:
	pPiece == NULL, grab all pieces in area specified by 'dims', excluding risers and riser-containing metas if desired.
	
	pPiece != NULL, does pretty much the same thing right now.
*/

CMapListNode *CParkManager::GetMetaPiecesAt(GridDims dims, Mth::ERot90 rot, CAbstractMetaPiece *pPiece, bool excludeRisers)
{
	Dbg_Assert(m_state_on);
	
	if (!pPiece)
	{
		CMapListNode *p_out_list = NULL;
		
		// need to be scanning an area bigger than 0
		//if (dims.mW == 0) dims.mW++;
		//if (dims.mL == 0) dims.mL++;
		
		// no piece supplied, just scan through and grab all metapieces
		int end_x = dims.GetX() + dims.GetW();
		for (int x = dims.GetX(); x < end_x; x++)
		{
			int end_z = dims.GetZ() + dims.GetL();
			for (int z = dims.GetZ(); z < end_z; z++)
			{
				CMapListNode *p_node = mp_bucket_list[x][z];
				
				while(p_node)
				{
					//Ryan("boogah %d\n", x);
					if ((!excludeRisers || !(p_node->GetMeta()->IsRiser())) &&
						p_node->GetConcreteMeta()->test_for_conflict(dims, excludeRisers))
					{
						add_metapiece_to_node_list(p_node->GetMeta(), &p_out_list);
					}
					
					p_node = p_node->GetNext();
				}
			}
		}
		
		p_out_list->VerifyIntegrity();
		return p_out_list;
	}
	else
	{
		Dbg_MsgAssert(pPiece->GetRot() == Mth::ROT_0, ("abstract metapiece needs 0 rotation"));
		
		// build a descriptor table rotated and shifted to correct world coordinates
		
		// rotate the abstract meta piece to the desired rotation
		pPiece->SetRot(rot);
		dims[W] = pPiece->m_cell_area.GetW();
		dims[H] = pPiece->m_cell_area.GetH();
		dims[L] = pPiece->m_cell_area.GetL();

		CMapListNode *p_out_list = NULL;
		
		// grab list of all concrete metas
		CMapListTemp metas_in_park = GetMetaPiecesAt(dims);

		// for each contained piece
		for (uint i = 0; i < pPiece->m_num_used_entries; i++)
		{
			SMetaDescriptor &desc = pPiece->get_desc_at_index(i);
			GridDims test_area(desc.mX, desc.mY, desc.mZ);
			test_area[X] += dims.GetX();
			test_area[Y] += dims.GetY();
			test_area[Z] += dims.GetZ();

			CMapListNode *p_in_park_entry = metas_in_park.GetList();
			while (p_in_park_entry)
			{
				if (p_in_park_entry->GetConcreteMeta()->test_for_conflict(test_area, false))
					add_metapiece_to_node_list(p_in_park_entry->GetMeta(), &p_out_list);

				p_in_park_entry = p_in_park_entry->GetNext();
			}
		}
		
		// return abstract meta to a zero rotation
		pPiece->SetRot(Mth::ROT_0);
		p_out_list->VerifyIntegrity();
		return p_out_list;
	}
	
	return NULL;
}

// This is just a slightly faster version of GetMetaPiecesAt, which gets all
CMapListNode *CParkManager::GetAreaMetaPieces(GridDims dims)
{
	Dbg_Assert(m_state_on);

	CMapListNode *p_out_list = NULL;
	
	int end_x = dims.GetX() + dims.GetW();
	for (int x = dims.GetX(); x < end_x; x++)
	{
		int end_z = dims.GetZ() + dims.GetL();
		for (int z = dims.GetZ(); z < end_z; z++)
		{
			CMapListNode *p_node = mp_bucket_list[x][z];
			while(p_node)
			{
				add_metapiece_to_node_list(p_node->GetMeta(), &p_out_list);
				p_node = p_node->GetNext();
			}
		}
	}
		
	return p_out_list;
}

// K: These two functions used by CClipboard::CopySelectionToClipboard
CMapListNode *CParkManager::GetMapListNode(int x, int z)
{
	return mp_bucket_list[x][z];
}

int CParkManager::GetFloorHeight(int x, int z)
{
	return m_floor_height_map[x][z].mHeight;
}
	
/*
	Used to determine if resizing park will destroy existing metas.
*/
bool CParkManager::AreMetasOutsideBounds(GridDims new_dims)
{
	//Ryan("New Park Bounds are: (%d,%d,%d)-(%d,%d,%d)\n", 
	//	 new_dims.GetX(), new_dims.GetY(), new_dims.GetZ(),
	//	 new_dims.GetW(), new_dims.GetH(), new_dims.GetL());
	CMapListNode *p_node = mp_concrete_metapiece_list;
	while(p_node)
	{
		CConcreteMetaPiece *p_meta = p_node->GetConcreteMeta();
		GridDims meta_area = p_meta->GetArea();
		//Ryan("Checking if meta in bounds: is_riser=%d, inner_shell=%d (%d,%d,%d)-(%d,%d,%d)\n", 
		//	 p_meta->IsRiser(), p_meta->IsInnerShell(), 
		//	 meta_area.GetX(), meta_area.GetY(), meta_area.GetZ(),
		//	 meta_area.GetW(), meta_area.GetH(), meta_area.GetL());
		if ((meta_area.GetX() < new_dims.GetX() ||
			meta_area.GetZ() < new_dims.GetZ() ||
			meta_area.GetX() + meta_area.GetW() > new_dims.GetX() + new_dims.GetW() ||
			meta_area.GetZ() + meta_area.GetL() > new_dims.GetZ() + new_dims.GetL()) &&
			!p_meta->IsRiser() &&
			!p_meta->IsInnerShell() &&
			(p_meta->m_flags & CMetaPiece::mIN_PARK))
			return true;
		p_node = p_node->GetNext();
	}

	return false;
}




/*
	Used to determine if resizing park will destroy existing metas.
*/
bool CParkManager::EnoughMemoryToResize(GridDims new_dims)
{
	int current_num_tiles = m_park_near_bounds.GetW() * m_park_near_bounds.GetL();
	int new_num_tiles = new_dims.GetW() * new_dims.GetL();

	if (new_num_tiles - current_num_tiles > GetGenerator()->GetResourceSize("max_dma_pieces") - GetDMAPieceCount())
	{
		return false;
	}
		
	int park_add_amount = (new_num_tiles - current_num_tiles) * mp_generator->GetResourceSize("floor_piece_size_park");
	int main_add_amount = (new_num_tiles - current_num_tiles) * mp_generator->GetResourceSize("floor_piece_size_main");

	CParkGenerator::MemUsageInfo usage_info = mp_generator->GetResourceUsageInfo();
	if (usage_info.mParkHeapFree < park_add_amount) 
	{
		Ryan("not enough memory for resize on park heap, need %d, have %d\n", park_add_amount, usage_info.mParkHeapFree);
		return false;
	}
	if (usage_info.mMainHeapFree < main_add_amount) 
	{
		Ryan("not enough memory for resize on main heap, need %d, have %d\n", main_add_amount, usage_info.mMainHeapFree);
		return false;
	}
	
	#ifdef __PLAT_NGC__
	// NGC has problems resizing if the top down heap is low due to UpdateSuperSectors requiring some temporary space.
	int required_top_down_heap=TOP_DOWN_REQUIRED_MARGIN;
	
	// If increasing the size of the park, then in the worse case the TD heap may reduce by main_add_amount,
	// if none of the fragments got used.
	// We want there to be more than TOP_DOWN_REQUIRED_MARGIN after increasing the size, so that the user
	// will be able to reduce the size afterwards, so add in main_add_amount to the requirement.
	if (main_add_amount > 0)
	{
		required_top_down_heap += main_add_amount;
	}	
	
	Mem::Heap *p_top_down_heap = Mem::Manager::sHandle().TopDownHeap();
	// Check if there is enough TD heap at the moment
	if (p_top_down_heap->mp_region->MemAvailable() < required_top_down_heap)
	{
		return false;
	}
	#endif
	
	return true;
}




/*
	Returns max floor height within specified
*/
int	CParkManager::GetFloorHeight(GridDims dims, bool *pRetUniformHeight)
{
	Dbg_Assert(m_state_on);
	Dbg_MsgAssert(m_floor_height_map, ("bad bad monkey!!!"));
	
	int max_height = -10000;
	
	if (pRetUniformHeight)
		*pRetUniformHeight = true;
	int corner_height = m_floor_height_map[dims.GetX()][dims.GetZ()].mHeight;
	
	for (int x = dims.GetX(); x < dims.GetX() + dims.GetW(); x++)
	{
		for (int z = dims.GetZ(); z < dims.GetZ() + dims.GetL(); z++)
		{
			Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH,
						  ("out of bounds (%d,%d)", x, z));
			if (pRetUniformHeight && m_floor_height_map[x][z].mHeight != corner_height)
				*pRetUniformHeight = false;
			if (m_floor_height_map[x][z].mHeight > max_height)
				max_height = m_floor_height_map[x][z].mHeight;
		}
	}
	
	return max_height;
}




#if 0
/*
	Changes the floor height stored in the height map, A subsequent call to
	generate_floor_pieces_from_height_map() will make the geometry.
*/
void CParkManager::SetFloorHeight(GridDims area, int newHeight, bool justDropHighest)
{
	for (int x = area.GetX(); x < area.GetX() + area.GetW(); x++)
	{
		for (int z = area.GetZ(); z < area.GetZ() + area.GetL(); z++)
		{
			Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH,
						  ("out of bounds (%d,%d)", x, z));
			
			GridDims test_area(x, 0, z, 1, 1, 1);
			if (IsOccupiedByRoad(test_area))
			{
				m_floor_height_map[x][z].mHeight = 0;
			}
			if (justDropHighest)
			{
				if (m_floor_height_map[x][z].mHeight > newHeight)
					m_floor_height_map[x][z].mHeight = newHeight;
			}
			else
				m_floor_height_map[x][z].mHeight = newHeight;
		}
	}
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~(mIN_SYNC_WITH_PARK | mIS_NEWER_THAN_PARK));
}
#endif


void CParkManager::ResetFloorHeights(GridDims area)
{
	// First move all the pieces in the area such that their y's are zero.
	CMapListTemp metas_at_pos = GetAreaMetaPieces(area);
	CMapListNode *p_entry = metas_at_pos.GetList();
	while (p_entry)
	{
		CConcreteMetaPiece *p_meta = p_entry->GetConcreteMeta();
		if (!p_meta->IsRiser())
		{
			GridDims meta_area=p_meta->GetArea();
			RelocateMetaPiece(p_meta,0,-meta_area.GetY(),0);
			
			// Set the floor heights under the piece to zero. Need to do this as well
			// as zeroing the heights for the whole area, in case the piece sticks out
			// of the area.
			for (int x = meta_area.GetX(); x < meta_area.GetX() + meta_area.GetW(); x++)
			{
				for (int z = meta_area.GetZ(); z < meta_area.GetZ() + meta_area.GetL(); z++)
				{
					Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH,
								  ("out of bounds (%d,%d)", x, z));
					m_floor_height_map[x][z].mHeight=0;
				}
			}		
		}
		
		p_entry = p_entry->GetNext();
	}

	// Then set all the floor heights to zero
	for (int x = area.GetX(); x < area.GetX() + area.GetW(); x++)
	{
		for (int z = area.GetZ(); z < area.GetZ() + area.GetL(); z++)
		{
			Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH,
						  ("out of bounds (%d,%d)", x, z));
			m_floor_height_map[x][z].mHeight=0;
		}
	}		

	// K: Do I need this?
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~(mIN_SYNC_WITH_PARK | mIS_NEWER_THAN_PARK));
	
	// Then rebuild the floor so that the above changes take effect.
	RebuildFloor();
}	


/*
	A "column" is a 3D region defined by the XZ rectangle of 'area'. Its bottom is at height 'y'. All the 
	metapieces within the column will change y position by the amount specified. Floor heights within the
	column will change as well.
	
	Returns true if success.
*/
bool CParkManager::SlideColumn(GridDims area, int bottom, int top, bool up, bool uneven)
{
	ESlideColumnFlags flags = ESlideColumnFlags(mFIRST_RECURSE);
	if (up) flags = ESlideColumnFlags(flags | mUP);
	if (uneven) flags = ESlideColumnFlags(flags | mUNEVEN);
	
	/* Remove bookkeeping left from last SlideColumn */
	finish_slide_column();
	if (mp_column_slide_list)
		mp_column_slide_list->DestroyList();
	mp_column_slide_list = NULL;
	m_num_metas_killed_in_slide = 0;
	m_num_pieces_killed_in_slide = 0;
	
	#if 1
	if (uneven && intersection_with_riser_containing_metas(area))
		return false;
	#endif
	
	if (!slide_column_part_one(area, bottom, top, flags, &mp_column_slide_list))
	{
		fix_slide_column_part_one_failure();
		return false;
	}
	slide_column_part_two(mp_column_slide_list);
	
	if (!up)
		kill_pieces_in_layer_under_area(area);
	
	return true;
}





/*
	Undoes the results of a successful SlideColumn()
*/
void CParkManager::UndoSlideColumn()
{
	fix_slide_column_part_one_failure();
	
	CMapListNode *p_node = mp_column_slide_list;
	while(p_node)
	{
		p_node->mp_meta = RelocateMetaPiece(p_node->GetConcreteMeta(), 0, -p_node->mSlideAmount, 0);
		p_node = p_node->GetNext();
	}
}

// K: This is copied from CCursor::ChangeFloorHeight
// Modified to take a GridDims parameter so that it can be used by the clipboard pasting code.
bool CParkManager::ChangeFloorHeight(GridDims dims, int dir)
{
	/*
		Uneven floor:
			If going up, raise to height of topmost
			If going down, only lower ones on same level
	*/

	bool uniform_height;
	int height = GetFloorHeight(dims, &uniform_height);

	/*
		Now, 
	*/

	bool success = true;
	if (SlideColumn(dims, dims.GetY(), height, (dir > 0), !uniform_height))
	{
		//Ryan("Slide column, dir = %d, dims=", dir);
		//dims.PrintContents();

		// K: This was required in the CCursor version of ChangeFloorHeight, can't
		// call it here though cos it is a member function of CCursor ...
		//change_cell_pos(0, 0);
		
		Spt::SingletonPtr p_editor;
		if (RebuildFloor(p_editor->IsParkFull()))
		{
			HighlightMetasWithGaps(false, NULL);
		}
		else
		{
			// out of memory, so reverse slide
			UndoSlideColumn();
			RebuildFloor();
			success = false;
		}
	}
	else
		success = false;

	return success;
}



/*
	Converts a cell based XYZ coordinate to its world coordinate equivalent.
*/
Mth::Vector	CParkManager::GridCoordinatesToWorld(const GridDims &cellDims, Mth::Vector *pRetDims)
{
	Mth::Vector world_pos(((int) cellDims.GetX() - (int) CParkManager::FLOOR_MAX_WIDTH / 2) * CParkGenerator::CELL_WIDTH,
		(int) cellDims.GetY() * CParkGenerator::CELL_HEIGHT,
		((int) cellDims.GetZ() - (int) CParkManager::FLOOR_MAX_LENGTH / 2) * CParkGenerator::CELL_LENGTH);

	if (pRetDims)
	{
		(*pRetDims)[X] = cellDims.GetW() * CParkGenerator::CELL_WIDTH;
		(*pRetDims)[Y] = cellDims.GetH() * CParkGenerator::CELL_HEIGHT;
		(*pRetDims)[Z] = cellDims.GetL() * CParkGenerator::CELL_LENGTH;
		#if 0
		printf("dims: (%d,%d,%d)-(%d,%d,%d)\n", 
			   cellDims.GetW(), cellDims.GetH(), cellDims.GetL(),
			   (int) cellDims.GetX() - (int) CParkManager::FLOOR_MAX_WIDTH / 2,
			   (int) cellDims.GetY(),
			   (int) cellDims.GetZ() - (int) CParkManager::FLOOR_MAX_LENGTH / 2);
		#endif
	}
	
	return world_pos;
}




CParkManager *CParkManager::sInstance()
{
	Dbg_Assert(sp_instance);
	return sp_instance;
}




/*
	The manager keeps track of a piece database that matches the selection set the user sees at the top of the screen.
*/
CParkManager::CPieceSet &CParkManager::GetPieceSet(int setNumber, int *pMenuSetNumber)
{
	Dbg_Assert(setNumber >= 0 && setNumber < MAX_SETS);
	
	int menu_set_number = 0;
	for (int s = 0; s < setNumber; s++)
	{
		if (m_palette_set[s].mVisible)
			menu_set_number++;
	}
	
	if (pMenuSetNumber)
		*pMenuSetNumber = menu_set_number;

	return m_palette_set[setNumber];
}




/*
	y coordinates, dimensions of 'area' not used.
*/
bool CParkManager::IsOccupiedByRoad(GridDims area)
{
	for (uint i = 0; i < m_road_mask_tab_size; i++)
	{
		bool overlap = true;
		if (area.GetX() + area.GetW() <= m_road_mask_tab[i].GetX()) overlap = false;
		else if (area.GetX() >= m_road_mask_tab[i].GetX() + m_road_mask_tab[i].GetW()) overlap = false;
		else if (area.GetZ() + area.GetL() <= m_road_mask_tab[i].GetZ()) overlap = false;
		else if (area.GetZ() >= m_road_mask_tab[i].GetZ() + m_road_mask_tab[i].GetL()) overlap = false;

		if (overlap) return true;
	}
	return false;
}

bool CParkManager::CanPlacePiecesIn(GridDims cellDims, bool ignoreFloorHeight)
{
	Spt::SingletonPtr p_editor;
	if (p_editor->IsParkFull())
	{
		ParkEd("p_editor->IsParkFull()");
		return false;
	}	

	// Check if there are any pieces in the area already.
	// Not using GetMetaPiecesAt because it does not detect pieces in lowered cells for some reason.
	CMapListTemp metas_at_pos = GetAreaMetaPieces(cellDims);
	CMapListNode *p_entry = metas_at_pos.GetList();
	while (p_entry)
	{
		CConcreteMetaPiece *p_meta=p_entry->GetConcreteMeta();
		if (!p_meta->IsRiser())
		{
			return false;
		}
			
		p_entry = p_entry->GetNext();
	}
	
	if (!ignoreFloorHeight)
	{
		bool uniform_floor;
		int floor_height = GetFloorHeight(cellDims, &uniform_floor);
		if (floor_height != cellDims.GetY() || !uniform_floor)
		{
			ParkEd("floor_height != cellDims.GetY() || !uniform_floor");
			return false;
		}	
	}
		
	if (IsOccupiedByRoad(cellDims))
	{
		ParkEd("IsOccupiedByRoad(cellDims)");
		return false;
	}	
	
	return true;
}


// pHalf returns the half number associated with the piece, if any gap found
CGapManager::GapDescriptor *CParkManager::GetGapDescriptor(GridDims &area, int *pHalf)
{
	area.SetWHL(1,1,1);
	CMapListTemp meta_list = GetMetaPiecesAt(area);
	if (meta_list.IsEmpty())
		return NULL;
	CConcreteMetaPiece *p_meta = meta_list.GetList()->GetConcreteMeta();

	int tab_index;
	Dbg_Assert(mp_gap_manager);
	if (!mp_gap_manager->IsGapAttached(p_meta, &tab_index))
		return NULL;
	
	CGapManager::GapDescriptor *pDesc = mp_gap_manager->GetGapDescriptor(tab_index, pHalf);
	Dbg_MsgAssert(pDesc, ("couldn't find gap descriptor"));
	return pDesc;
}




/*
	Call from:
	
	read_from_compressed_map_buffer(), CParkEditor, gap adjustment code, gap name changer
	
	-see if meta at cursor loc
	-if user working on gap already, fail
	-if no prexisiting, remove gap-in-progress
	-build descriptor
	-add gap
*/
bool CParkManager::AddGap(CGapManager::GapDescriptor &descriptor)
{
	Dbg_Assert(mp_gap_manager);
	int gap_tab_index = mp_gap_manager->GetFreeGapIndex();
	if (gap_tab_index == -1) return false;

	for (int i = 0; i < descriptor.numCompleteHalves; i++)
	{
		// fetch piece associated with this gap half
		descriptor.loc[i].SetWHL(1,1,1);
		CMapListTemp meta_list = GetMetaPiecesAt(descriptor.loc[i]);
		if (meta_list.IsEmpty())
			return false;
		Dbg_MsgAssert(!meta_list.IsEmpty(), ("am expecting a metapiece here"));
		CConcreteMetaPiece *p_meta = meta_list.GetList()->GetConcreteMeta();
		Dbg_MsgAssert(p_meta, ("no pieces found at (%d,%d,%d)", descriptor.loc[i].GetX(), descriptor.loc[i].GetY(), descriptor.loc[i].GetZ()));
		Dbg_MsgAssert(!mp_gap_manager->IsGapAttached(p_meta, NULL), ("gap is already attached"));
		p_meta->set_flag(CMetaPiece::mGAP_ATTACHED);
		
		descriptor.loc[i] = p_meta->GetArea();
		
		#if 0
		Ryan("  Placing gap half %d, loc=(%d,%d,%d-%d,%d,%d), rot=%d, left=%d, right=%d\n", i,
			 descriptor.loc[i].GetX(), descriptor.loc[i].GetY(), descriptor.loc[i].GetZ(), 
			 descriptor.loc[i].GetW(), descriptor.loc[i].GetH(), descriptor.loc[i].GetL(),
			 descriptor.rot[i], descriptor.leftExtent[i], descriptor.rightExtent[i]);
		#endif
		
		GridDims area = p_meta->GetArea();
		
		#if 1
		// work out gap positioning
		Mth::Vector gap_pos;
		float gap_length = 0.0f;
		int gap_rot = 0;
		switch(descriptor.rot[i])
		{
			case 0:	// east
				gap_length = (area.GetL() + descriptor.leftExtent[i] + descriptor.rightExtent[i]) * CParkGenerator::CELL_LENGTH;
				gap_pos[X] = (area.GetX() + area.GetW()) * CParkGenerator::CELL_WIDTH - CParkGenerator::CELL_WIDTH / 2.0f - 1.0f;
				gap_pos[Z] = (area.GetZ() - descriptor.rightExtent[i]) * CParkGenerator::CELL_LENGTH + gap_length / 2.0f - CParkGenerator::CELL_LENGTH;
				gap_pos[Y] = area.GetY() * CParkGenerator::CELL_HEIGHT;
				gap_rot = 1;
				break;
			case 1:	// north
				gap_length = (area.GetW() + descriptor.leftExtent[i] + descriptor.rightExtent[i]) * CParkGenerator::CELL_WIDTH;
				gap_pos[X] = (area.GetX() - descriptor.rightExtent[i]) * CParkGenerator::CELL_WIDTH + gap_length / 2.0f - CParkGenerator::CELL_WIDTH;
				gap_pos[Z] = area.GetZ() * CParkGenerator::CELL_LENGTH - CParkGenerator::CELL_LENGTH / 2.0f + 1.0f;
				gap_pos[Y] = area.GetY() * CParkGenerator::CELL_HEIGHT;
				gap_rot = 0;
				break;
			case 2:	// west
				gap_length = (area.GetL() + descriptor.leftExtent[i] + descriptor.rightExtent[i]) * CParkGenerator::CELL_LENGTH;
				gap_pos[X] = area.GetX() * CParkGenerator::CELL_WIDTH - CParkGenerator::CELL_WIDTH / 2.0f + 1.0f;
				gap_pos[Z] = (area.GetZ() - descriptor.leftExtent[i]) * CParkGenerator::CELL_LENGTH + gap_length / 2.0f - CParkGenerator::CELL_LENGTH;
				gap_pos[Y] = area.GetY() * CParkGenerator::CELL_HEIGHT;
				gap_rot = 1;
				break;
			case 3:	// south
				gap_length = (area.GetW() + descriptor.leftExtent[i] + descriptor.rightExtent[i]) * CParkGenerator::CELL_WIDTH;
				gap_pos[X] = (area.GetX() - descriptor.leftExtent[i]) * CParkGenerator::CELL_WIDTH + gap_length / 2.0f - CParkGenerator::CELL_WIDTH;
				gap_pos[Z] = (area.GetZ() + area.GetL()) * CParkGenerator::CELL_LENGTH - CParkGenerator::CELL_LENGTH / 2.0f - 1.0f;
				gap_pos[Y] = area.GetY() * CParkGenerator::CELL_HEIGHT;
				gap_rot = 0;
				break;
			default:
				Dbg_MsgAssert(0, ("not a valid gap rotation"));
				break;
		}
		#endif
		
		gap_pos[X] -= MAX_WIDTH * CParkGenerator::CELL_WIDTH / 2.0f;
		gap_pos[Z] -= MAX_LENGTH * CParkGenerator::CELL_LENGTH / 2.0f;

		CClonedPiece *p_gap_piece = mp_generator->CreateGapPiece(gap_pos, gap_length, gap_rot);
		p_gap_piece->SetScaleX(gap_length / CParkGenerator::CELL_WIDTH);
		
		// create gap
		if (i == 0)
			mp_gap_manager->StartGap(p_gap_piece, p_meta, gap_tab_index);
		else	
			mp_gap_manager->EndGap(p_gap_piece, p_meta, gap_tab_index);
	}	

	descriptor.tabIndex = gap_tab_index;
	mp_gap_manager->SetGapInfo(gap_tab_index, descriptor);

	return true;
}



/*
	Called from: change cursor, cleanup, gap placement, gap adjustment, name change
*/
void CParkManager::RemoveGap(CGapManager::GapDescriptor &descriptor)
{
	#if 1
	// XXX
	for (int half = 0; half < 2; half++)
		Ryan("   removing gap half %d (%d,%d,%d),(%d,%d,%d) left=%d right=%d, index=%d\n", half,
			 descriptor.loc[half].GetX(), descriptor.loc[half].GetY(), descriptor.loc[half].GetZ(),
			 descriptor.loc[half].GetW(), descriptor.loc[half].GetH(), descriptor.loc[half].GetL(),
			 descriptor.leftExtent[half], descriptor.rightExtent[half],
				descriptor.tabIndex);
	#endif
	
	CConcreteMetaPiece *p_meta[2];
	for (int half = 0; half < descriptor.numCompleteHalves; half++)
	{
		CMapListTemp meta_list = GetMetaPiecesAt(descriptor.loc[half]);
		Dbg_MsgAssert(!meta_list.IsEmpty(), ("no pieces found at (%d,%d,%d)", descriptor.loc[half].GetX(), descriptor.loc[half].GetY(), descriptor.loc[half].GetZ()));
		
		#if 1
		if (!meta_list.IsSingular())
		{
//			printf("failed singularity, half %d\n", half);
			meta_list.PrintContents();
		}
		#endif
		
		// go through list, find metapiece matching location above
		// there is only SUPPOSED to be one metapiece in list, but sometimes there are more
		// and I have no idea why, so fuck it
		CMapListNode *p_node = meta_list.GetList();
		while(p_node)
		{
			p_meta[half] = p_node->GetConcreteMeta();
			GridDims area = p_meta[half]->GetArea();

			if (area.GetX() == descriptor.loc[half].GetX() &&
				area.GetY() == descriptor.loc[half].GetY() &&
				area.GetZ() == descriptor.loc[half].GetZ())
				break;

			p_node = p_node->GetNext();
		}

		//p_meta[half] = meta_list.GetList()->GetConcreteMeta();
		p_meta[half]->Highlight(false);
		p_meta[half]->clear_flag(CMetaPiece::mGAP_ATTACHED);
	}

	int tab_index;
	Dbg_Assert(mp_gap_manager);
	if (mp_gap_manager->IsGapAttached(p_meta[0], &tab_index))
	{
		mp_gap_manager->RemoveGap(tab_index);
	}

	descriptor.tabIndex = -1;
	// Reset the cancel flags to the default, to fix TT9493
	descriptor.mCancelFlags=Script::GetInteger(CRCD(0x9a27e74d,"Cancel_Ground"));
}




void CParkManager::RemoveGap(CConcreteMetaPiece *pMeta)
{
	int tab_index;
	Dbg_Assert(mp_gap_manager);
	if (!mp_gap_manager->IsGapAttached(pMeta, &tab_index))
		return;
	
	int half;
	CGapManager::GapDescriptor *pDesc = mp_gap_manager->GetGapDescriptor(tab_index, &half);
	Dbg_MsgAssert(pDesc, ("couldn't find gap descriptor"));
	RemoveGap(*pDesc);
}




void CParkManager::HighlightMetasWithGaps(bool highlightOn, CGapManager::GapDescriptor *pDesc)
{
	CMapListNode *p_node = mp_concrete_metapiece_list;
	while (p_node)
	{
		if (p_node->GetConcreteMeta()->m_flags & CMetaPiece::mGAP_ATTACHED)
			p_node->GetConcreteMeta()->Highlight(highlightOn, true);
		p_node = p_node->GetNext();
	}

	mp_generator->SetGapPiecesVisible(false);
	
	if (highlightOn && pDesc && pDesc->tabIndex >= 0)
	{
		mp_gap_manager->MakeGapWireframe(pDesc->tabIndex);
	}
	else if (!highlightOn)
	{
		mp_generator->SetGapPiecesVisible(false);
	}
}

// Added for use by s_generate_summary_info in mcfunc.cpp
void CParkManager::GetSummaryInfoFromBuffer(uint8 *p_buffer, int *p_numGaps, int *p_numMetas, uint16 *p_theme, uint32 *p_todScript, int *p_width, int *p_length)
{
	CompressedMapHeader *p_header=(CompressedMapHeader*)p_buffer;
	*p_width=p_header->mW;
	*p_length=p_header->mL;
	*p_numGaps=p_header->mNumGaps;
	*p_numMetas=p_header->mNumMetas;
	*p_theme=p_header->mTheme;
	if (p_header->mTODScript)
	{
		*p_todScript=p_header->mTODScript;
	}
	else
	{
		*p_todScript=CRCD(0x1ca1ff20,"default");
	}	
}


/*
	Creates a "stack" of risers within the specified vertical column. If !simpleBuild, will keep exisiting
	riser metapieces if they do the job.
	
	'top' refers to cell position of top of topmost riser
*/
void CParkManager::output_riser_stack(BuildFloorParams ¶ms)
{
	Dbg_MsgAssert(m_floor_height_map, ("bad bad monkey!!!"));
	
	Dbg_MsgAssert(params.top >= -16 && params.top <= 15, ("faulty floor height %d, at (%d,%d)", params.top, params.x, params.z));
	
	int lowest_neighbor = 10000;
	
	// work out lowest neighbor
	if (params.x > m_park_far_bounds.GetX())
	{
		if (m_floor_height_map[params.x-1][params.z].mHeight < lowest_neighbor)
			lowest_neighbor = m_floor_height_map[params.x-1][params.z].mHeight;
	}
	else
		lowest_neighbor = (lowest_neighbor > 0) ? 0 : lowest_neighbor;

	if (params.x < m_park_far_bounds.GetX() + m_park_far_bounds.GetW() - 1)
	{
		if (m_floor_height_map[params.x+1][params.z].mHeight < lowest_neighbor)
			lowest_neighbor = m_floor_height_map[params.x+1][params.z].mHeight;
	}
	else
		lowest_neighbor = (lowest_neighbor > 0) ? 0 : lowest_neighbor;

	if (params.z > m_park_far_bounds.GetZ())
	{
		if (m_floor_height_map[params.x][params.z-1].mHeight < lowest_neighbor)
			lowest_neighbor = m_floor_height_map[params.x][params.z-1].mHeight;
	}
	else
		lowest_neighbor = (lowest_neighbor > 0) ? 0 : lowest_neighbor;

	if (params.z < m_park_far_bounds.GetZ() + m_park_far_bounds.GetL() - 1)
	{
		if (m_floor_height_map[params.x][params.z+1].mHeight < lowest_neighbor)
			lowest_neighbor = m_floor_height_map[params.x][params.z+1].mHeight;
	}
	else
		lowest_neighbor = (lowest_neighbor > 0) ? 0 : lowest_neighbor;

	EFloorFlags flags = mHAS_BOTTOM;
	GridDims road_test_area(params.x, 0, params.z, 1, 1, 1);
	if (IsOccupiedByRoad(road_test_area))
	{
		flags = EFloorFlags(flags | mNO_COVER);
		params.top = 0;
	}
	
	if (lowest_neighbor < params.top)
	{
		params.bottom = lowest_neighbor;
	}
	else
	{
		params.bottom = params.top-1;
		flags = EFloorFlags(flags & ~mHAS_BOTTOM);
	}

	params.flags = flags;
	for (int y = params.bottom; y < params.top; y++)
	{
		if (y == params.top-1 && !(flags & mNO_COVER))
			params.flags = EFloorFlags(params.flags | mHAS_TOP);
		
		if (y == -1)
		{
			if (y <= lowest_neighbor)
				params.heightSlotOffset = 3; // group 4 -- bottom level, just below surface
			else
				params.heightSlotOffset = 2; // group 3 -- not bottom level, just below surface
		}
		else if (y < -1)
		{
			if (y <= lowest_neighbor)
				params.heightSlotOffset = 0; // group 1 -- bottom level, not touching surface
			else
				params.heightSlotOffset = 1; // group 2 -- not bottom level, not touching surface
		}
		else
		{
			if (y == params.top-1)
			{
				if (y == 0)
					params.heightSlotOffset = 4; // group 5 -- top level risers sitting on surface
				else
					params.heightSlotOffset = 7; // group 8 -- risers sitting above surface, top level
			}
			else if (y == 0)
				params.heightSlotOffset = 5; // group 6 -- risers sitting on surface, but not top level
			else
				params.heightSlotOffset = 6; // group 7 -- risers sitting above surface, not top level
		}		
		
		params.y = y;
		if (params.simpleBuild)
			build_add_floor_piece_simple(params); 
		else
			build_add_floor_piece(params); 
	}
}




/*
	Creates a riser metapiece, if one is needed.
*/
void CParkManager::build_add_floor_piece(BuildFloorParams ¶ms)
{
	uint32 type = TYPE_EMPTY_BLOCK;
	if (params.flags & mHAS_BOTTOM)
	{
		if (params.flags & mHAS_TOP)
			type = m_riser_piece_checksum[params.heightSlotOffset][0];
		else
			type = m_riser_piece_checksum[params.heightSlotOffset][1];
	}
	else if (params.flags & mHAS_TOP)
	{
		type = m_riser_piece_checksum[params.heightSlotOffset][2];
	}
	else
		return;
	
	// see if floor already included in metapiece at this position
	int floor_height = params.y + 1;
	Dbg_MsgAssert(floor_height >= -16 && floor_height <= 15, ("faulty floor height %d, at (%d,%d,%d)", 
															  floor_height, params.x, params.y, params.z));
	uint32 floor_bit = (1<<(floor_height+16));
	if (m_floor_height_map[params.x][params.z].mEnclosedFloor & floor_bit)
	{
		#if 0
		printf("preexisting riser at %d (%d,%d)!\n", floor_height, params.x, params.z);
		#endif
		return;
	}
	
	// see if floor piece is already there and if it has desired type
	GridDims test_dims(params.x, params.y, params.z, 1, 1, 1);
	CMapListNode *p_node = GetMetaPiecesAt(test_dims);
	if (p_node)
	{
		if (p_node->GetMeta()->m_name_checksum == type)
		{
			/*
			if (type == TYPE_FLOOR_BLOCK && 0)
				printf("preserving floor block at %d,%d,%d\n", 
					   p_node->GetMeta()->GetArea().GetX(),
					   p_node->GetMeta()->GetArea().GetY(),
					   p_node->GetMeta()->GetArea().GetZ());
			*/
			
			// undo mark for kill
			p_node->GetMeta()->clear_flag(CMetaPiece::mMARK_FOR_KILL);
			p_node->DestroyList();
			return;
		}
		else
			p_node->DestroyList();
	}
	
	if (!params.dryRun)
	{
		#ifdef USE_BUILD_LIST
		enum
		{
			MAX_BUILD_LIST_SIZE=10000,
		};
		Dbg_MsgAssert(m_build_list_sizem_num_used_entries;
	}
	params.addCount++;
}




/*
	Creates a riser metapiece, period.
*/
void CParkManager::build_add_floor_piece_simple(BuildFloorParams ¶ms)
{
	uint32 type = TYPE_EMPTY_BLOCK;
	if (params.flags & mHAS_BOTTOM)
	{
		if (params.flags & mHAS_TOP)
			type = m_riser_piece_checksum[params.heightSlotOffset][0];
		else
			type = m_riser_piece_checksum[params.heightSlotOffset][1];
	}
	else if (params.flags & mHAS_TOP)
	{
		type = m_riser_piece_checksum[params.heightSlotOffset][2];
	}
	else
		return;
	
	
	CConcreteMetaPiece *p_out = CreateConcreteMeta(GetAbstractMeta(type));
	GridDims pos(params.x, params.y, params.z);
	AddMetaPieceToPark(pos, p_out);
	params.addCount++;
}




/*
	Some metapieces contain risers.
*/
void CParkManager::apply_meta_contained_risers_to_floor(CConcreteMetaPiece *pMetaPiece, bool add)
{
	for (uint i = 0; i < pMetaPiece->m_num_used_entries; i++)
	{
		CClonedPiece *p_piece = pMetaPiece->GetContainedPiece(i)->CastToCClonedPiece();
		
		// see if this piece affects floor height
		SMetaDescriptor &desc = pMetaPiece->get_desc_at_index(i);
		if (desc.mRiser)
		{
			GridDims riser_area;
			p_piece->GetCellDims(&riser_area);
			riser_area[X] = pMetaPiece->m_cell_area.GetX() + desc.mX;
			riser_area[Y] = pMetaPiece->m_cell_area.GetY() + desc.mY;
			riser_area[Z] = pMetaPiece->m_cell_area.GetZ() + desc.mZ;
	
			int start_x = riser_area.GetX();
			int end_x = start_x + riser_area.GetW();
			int start_z = riser_area.GetZ();
			int end_z = start_z + riser_area.GetL();
			#if 0
			printf("raising floor at: (%d,%d,%d)+(%d,%d,%d), desc.mRiser is %d at 0x%x, index %d\n",
				   pMetaPiece->m_cell_area.GetX(), pMetaPiece->m_cell_area.GetY(), pMetaPiece->m_cell_area.GetZ(),
				   desc.mX, desc.mY, desc.mZ, desc.mRiser, &desc.mRiser, i);
			#endif
			for (int x = start_x; x < end_x; x++)
			{
				for (int z = start_z; z < end_z; z++)
				{
					Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH,
								  ("out of bounds (%d,%d)", x, z));
					if (add)
					{
						if (m_floor_height_map[x][z].mHeight < riser_area.GetY() + riser_area.GetH())
							m_floor_height_map[x][z].mHeight = riser_area.GetY() + riser_area.GetH();
						if (m_floor_height_map[x][z].mHeight > 15)
							m_floor_height_map[x][z].mHeight = 15;
						if (m_floor_height_map[x][z].mHeight < -16)
							m_floor_height_map[x][z].mHeight = -16;
					}
					else
					{
						// Are there non-riser metapieces above this one?
						GridDims area_above = riser_area;
						area_above[Y] = riser_area.GetY() + riser_area.GetH();
						area_above.MakeInfinitelyHigh();
						CMapListTemp meta_list = GetMetaPiecesAt(area_above, Mth::ROT_0, NULL, true);
						if (meta_list.IsEmpty())
						{
							if (m_floor_height_map[x][z].mHeight > riser_area.GetY())
								m_floor_height_map[x][z].mHeight = riser_area.GetY();
						}
					}

					for (int y = riser_area.GetY(); y < riser_area.GetY() + riser_area.GetH(); y++)
					{
						if (add)
							m_floor_height_map[x][z].mEnclosedFloor |= (1<<(y+16+1));
						else
							m_floor_height_map[x][z].mEnclosedFloor &= ~(1<<(y+16+1));
					}
					
					#if 0
					printf("  floor change: (%d,%d) to %d, flags 0x%x, bottom %d, top %d\n", 
						   x, z, m_floor_height_map[x][z].mHeight, m_floor_height_map[x][z].mEnclosedFloor,
						   riser_area.GetY(), riser_area.GetY() + riser_area.GetH());
					#endif
				}
			}
		}
	}
}




bool CParkManager::intersection_with_riser_containing_metas(GridDims area)
{
	area.MakeInfiniteOnY();
	
	CMapListTemp list = GetMetaPiecesAt(area, Mth::ROT_0, NULL, true);
	CMapListNode *p_node = list.GetList();
	while (p_node)
	{
		if (p_node->GetConcreteMeta()->m_flags & CMetaPiece::mCONTAINS_RISERS)
			return true;
		p_node = p_node->GetNext();
	}

	return false;
}




/*
	Change map height. Add metapieces in column to move list. Repeat this for every metapiece in move list. No
	metapiece will end up in move list twice, though.
	
	Good LUCK figuring this out. :)
*/
bool CParkManager::slide_column_part_one(GridDims area, int bottom, int top, ESlideColumnFlags flags, CMapListNode **ppMoveList)
{
	GridDims test_area = area;
	test_area[Y] = (uint8) bottom;
	if (flags & mFIRST_RECURSE)
		test_area.MakeInfiniteOnY();
	else
		test_area.MakeInfinitelyHigh();

	for (int x = test_area.GetX(); x < test_area.GetX() + test_area.GetW(); x++)
	{
		for (int z = test_area.GetZ(); z < test_area.GetZ() + test_area.GetL(); z++)
		{
			Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH,
						  ("out of bounds (%d,%d)", x, z));
			if (!m_floor_height_map[x][z].mMarkAsSlid)
			{
				int change_amount = 0;
				if (flags & mUNEVEN)
				{
					if (flags & mUP)
						change_amount = top - m_floor_height_map[x][z].mHeight;
					else
						change_amount = (m_floor_height_map[x][z].mHeight == top) ? -1 : 0;
				}
				else
				{
					if (flags & mUP)
						change_amount = 1;
					else
						change_amount = -1;
				}
				
				m_floor_height_map[x][z].mHeight += change_amount;
				m_floor_height_map[x][z].mSlideAmount = change_amount;

				if (m_floor_height_map[x][z].mHeight < -16 || m_floor_height_map[x][z].mHeight > 15)
				{
					return false;
				}
			}
			m_floor_height_map[x][z].mMarkAsSlid = true;
		}
	}

	CMapListTemp list = GetMetaPiecesAt(test_area, Mth::ROT_0, NULL, true);
	CMapListNode *p_node = list.GetList();
	while (p_node)
	{
		CConcreteMetaPiece *p_meta = p_node->GetConcreteMeta();
		GridDims meta_area = p_meta->GetArea();
		
		if (!p_meta->IsRiser() && !(p_meta->m_flags & CMetaPiece::mMARK_AS_SLID))
		{
			/*
				Are we actually going to move this meta?
				
				Even surface:
				-yes
				
				Uneven
				-up: only if lower than top
				-down
			*/
			
			p_meta->set_flag(CMetaPiece::mMARK_AS_SLID);
			Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap());
			CMapListNode *p_new_node = new CMapListNode();
			Mem::Manager::sHandle().PopContext();
			p_new_node->mp_next = *ppMoveList;
			p_new_node->mp_meta = p_meta;			
			*ppMoveList = p_new_node;

			#if 0
			Ryan("rising meta at: ");
			p_meta->GetArea().PrintContents();
			#endif
			
			p_new_node->mSlideAmount = 0;
			if (flags & mUNEVEN)
			{
				if (flags & mUP)
					p_new_node->mSlideAmount = top - meta_area.GetY();
				else
					p_new_node->mSlideAmount = (meta_area.GetY() == top) ? -1 : 0;
			}
			else
			{
				if (flags & mUP)
					p_new_node->mSlideAmount = 1;
				else
					p_new_node->mSlideAmount = -1;
			}
			
			ESlideColumnFlags recurse_flags = ESlideColumnFlags(flags & ~mFIRST_RECURSE);
			if (!slide_column_part_one(meta_area, meta_area.GetY(), top, recurse_flags, ppMoveList))
				return false;
		}
		p_node = p_node->GetNext();
	}

	return true;
}




void CParkManager::fix_slide_column_part_one_failure()
{
	for (int x = 0; x < FLOOR_MAX_WIDTH; x++)
	{
		for (int z = 0; z < FLOOR_MAX_LENGTH; z++)
		{
			if (m_floor_height_map[x][z].mMarkAsSlid)
			{
				m_floor_height_map[x][z].mMarkAsSlid = false;
				m_floor_height_map[x][z].mHeight -= m_floor_height_map[x][z].mSlideAmount;
			}
			if (m_floor_height_map[x][z].mHeight > 15)
				m_floor_height_map[x][z].mHeight = 15;
			if (m_floor_height_map[x][z].mHeight < -16)
				m_floor_height_map[x][z].mHeight = -16;
		}
	}
}




/*
	Go through move list, relocate each metapiece
*/
void CParkManager::slide_column_part_two(CMapListNode *pMoveList)
{
	//Mem::Heap *p_top_down_heap = Mem::Manager::sHandle().TopDownHeap();
	
	CMapListNode *p_node = pMoveList;
	while (p_node)
	{
		CConcreteMetaPiece *p_meta = p_node->GetConcreteMeta();
		
		if (p_node->mSlideAmount < 0)
		{
			kill_pieces_in_layer_under_area(p_meta->GetArea());
		}

		p_node->mp_meta = RelocateMetaPiece(p_meta, 0, p_node->mSlideAmount, 0);

		//if (p_top_down_heap->mp_region->MemAvailable() < 200000)
		//{
		//	GetGenerator()->GetClonedScene()->UpdateSuperSectors();
		//}	
		
		p_node = p_node->GetNext();
	}	
}




void CParkManager::kill_pieces_in_layer_under_area(GridDims area)
{
	GridDims kill_area = area;
	kill_area[Y] = area.GetY() - 1;
	kill_area[H] = 1;

	#if 0
	printf("killing metas in area: (%d,%d,%d)-(%d,%d,%d)\n",
		   kill_area.GetX(), kill_area.GetY(), kill_area.GetZ(),
		   kill_area.GetW(), kill_area.GetH(), kill_area.GetL());
	#endif

	uint32 killed_result = DestroyMetasInArea(kill_area, mEXCLUDE_PIECES_MARKED_AS_SLID);
	m_num_metas_killed_in_slide += killed_result>>16;
	m_num_pieces_killed_in_slide += killed_result & 0xFFFF;
}




/*
	Undo 'slid' markers in floor map array. Turn off MARK_AS_SLID flags on metapieces.
*/
void CParkManager::finish_slide_column()
{
	for (int x = m_park_far_bounds.GetX(); x < m_park_far_bounds.GetX() + m_park_far_bounds.GetW(); x++)
	{
		for (int z = m_park_far_bounds.GetZ(); z < m_park_far_bounds.GetZ() + m_park_far_bounds.GetL(); z++)
		{
			Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH,
						  ("out of bounds (%d,%d)", x, z));
			m_floor_height_map[x][z].mMarkAsSlid = false;
			m_floor_height_map[x][z].mSlideAmount = 0;
		}
	}

	CMapListNode *p_node = mp_concrete_metapiece_list;
	while(p_node)
	{
		p_node->GetConcreteMeta()->clear_flag(CMetaPiece::mMARK_AS_SLID);
		p_node = p_node->GetNext();
	}
}

#ifdef USE_BUILD_LIST
void CParkManager::create_metas_in_build_list()
{
	for (int i=0; iGetMeta();
		if (p_meta->IsRiser())
		{
			p_meta->set_flag(CMetaPiece::mMARK_FOR_KILL);
			mark_count++;
		}
		p_node = p_node->GetNext();
	}
	mark_count += m_num_metas_killed_in_slide;

	#ifdef USE_BUILD_LIST
	m_build_list_size=0;
	Dbg_MsgAssert(mp_build_list_entry==NULL,("Non-NULL mp_build_list_entry"));
	#endif

	
	Dbg_MsgAssert(m_floor_height_map, ("bad bad monkey!!!"));
	BuildFloorParams riser_build_params;
	riser_build_params.simpleBuild = simpleBuild;
	riser_build_params.dryRun = dryRun;
	riser_build_params.addCount = 0; // counts all risers added by the output_riser_stack() calls
	riser_build_params.addCount2 = 0;
	for (int x = m_park_near_bounds.GetX() - 1; x < m_park_near_bounds.GetX() + m_park_near_bounds.GetW() + 1; x++)
	{
		for (int z = m_park_near_bounds.GetZ() - 1; z < m_park_near_bounds.GetZ() + m_park_near_bounds.GetL() + 1; z++)
		{
			Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH,
						  ("out of bounds (%d,%d)", x, z));
			riser_build_params.x = x;
			riser_build_params.z = z;
			riser_build_params.top = m_floor_height_map[x][z].mHeight;
			riser_build_params.bottom = m_park_far_bounds.GetY();
			output_riser_stack(riser_build_params);
		}

		#ifdef __PLAT_XBOX__
		// On the XBox the large amount of CPU time taken up by rebuilding the park causes a glitch when
		// wma music is playing. So make sure Pcm::Update gets called often whilst in this inner loop.
		DirectSoundDoWork();
		Pcm::Update();
		#endif // __PLAT_XBOX__
		#ifdef __PLAT_NGC__
		// Same for the NGC ...
		Pcm::Update();
		#endif // __PLAT_NGC__
	}

	// remove all marked floor piece
	int kill_count = 0; // counts risers killed
	int kill_count2 = 0; // counts contained pieces
	int left_count = 0;	// counts risers not killed plus those added
	p_node = mp_concrete_metapiece_list;
	while(p_node)
	{
		CMapListNode *p_nextNode = p_node->GetNext();		// get next node now, as DestroyConcretMeta, below, can delete current node
		CMetaPiece *p_meta = p_node->GetMeta();
		if (p_meta->m_flags & CMetaPiece::mMARK_FOR_KILL)
		{
			kill_count++;
			kill_count2 += p_meta->m_num_used_entries;
			if (!dryRun)
			{
				//p_node->GetConcreteMeta()->GetArea().PrintContents();
				DestroyConcreteMeta(p_node->GetConcreteMeta(), mDONT_DESTROY_PIECES_ABOVE);
			}
		}
		else if (p_meta->IsRiser())
			left_count++;
		p_node = p_nextNode;
	}
	kill_count += m_num_metas_killed_in_slide;
	kill_count2 += m_num_pieces_killed_in_slide;
	

	#ifdef USE_BUILD_LIST
	mp_generator->GetClonedScene()->UpdateSuperSectors();	
	if (!dryRun)
	{
		left_count+=m_build_list_size;
		create_metas_in_build_list();
	}
	Dbg_MsgAssert(mp_build_list_entry==NULL,("Non-NULL mp_build_list_entry"));
	#endif
		
	// risers preexisting + risers added - risers killed == risers left
	Dbg_Assert(dryRun || riser_build_params.addCount == left_count + kill_count - mark_count);
	
	//printf("riser_build_params.addCount = %d kill_count = %d, kill_count2 = %d\n",riser_build_params.addCount,kill_count,kill_count2);
	
	//if (dryRun)
	//	printf("DRYRUN: ");
	//printf("marked %d pieces, killed %d, not killed %d, left %d, added %d\n", 
	//	   mark_count, kill_count, mark_count - kill_count, left_count, riser_build_params.addCount);
	//printf("addCount2 = %d, killCount2 = %d\n", riser_build_params.addCount2, kill_count2);

	if (dryRun && (riser_build_params.addCount > kill_count || riser_build_params.addCount2 > kill_count2))
	{
		return false;
	}
	
	//if (dryRun && (riser_build_params.addCount - kill_count > 200))
	//{
	//	// Don't allow height changes that will result in too many pieces being created, to prevent
	//	// running out of memory.
	//	return false;
	//}

	return true;
}




/*
	Create new list if none
*/
void CParkManager::add_metapiece_to_node_list(CMetaPiece *pMetaToAdd, CMapListNode **ppList)
{
	CMapListNode *p_test_node = *ppList;
	while(p_test_node)
	{
		if (p_test_node->GetMeta() == pMetaToAdd) return; // it's in there
		p_test_node = p_test_node->GetNext();
	}

	// not in list already, so add it
	Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap());
	CMapListNode *p_new_node = new CMapListNode();
	Mem::Manager::sHandle().PopContext();
	p_new_node->mp_meta = pMetaToAdd;
	p_new_node->mp_next = *ppList;
	*ppList = p_new_node;
}




/*
	Opposite of above
*/
void CParkManager::remove_metapiece_from_node_list(CMetaPiece *pMetaToRemove, CMapListNode **ppList)
{
	CMapListNode *p_node = *ppList;
	CMapListNode *p_prev = NULL;
	while(p_node)
	{
		if (p_node->GetMeta() == pMetaToRemove)
		{
			if (p_prev)
				p_prev->mp_next = p_node->GetNext();
			else
				*ppList = p_node->GetNext();

			delete p_node;
			return;
		}
		
		p_prev = p_node;
		p_node = p_node->GetNext();
	}
}




void CParkManager::bucketify_metapiece(CConcreteMetaPiece *pMeta)
{
	for (int x = 0; x < pMeta->m_cell_area.GetW(); x++)
	{
		for (int z = 0; z < pMeta->m_cell_area.GetL(); z++)
		{
			Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap());
			CMapListNode *p_new_entry = new CMapListNode();
			Mem::Manager::sHandle().PopContext();
			p_new_entry->mp_meta = pMeta;
			
			p_new_entry->mp_next = mp_bucket_list[x + pMeta->m_cell_area.GetX()][z + pMeta->m_cell_area.GetZ()];
			mp_bucket_list[x + pMeta->m_cell_area.GetX()][z + pMeta->m_cell_area.GetZ()] = p_new_entry;
		}
	}
}




void CParkManager::debucketify_metapiece(CConcreteMetaPiece *pMeta)
{
	for (int x = 0; x < pMeta->m_cell_area.GetW(); x++)
	{
		for (int z = 0; z < pMeta->m_cell_area.GetL(); z++)
		{
			CMapListNode *p_prev = NULL;
			CMapListNode *p_entry = mp_bucket_list[x + pMeta->m_cell_area.GetX()][z + pMeta->m_cell_area.GetZ()];
			while(p_entry)
			{
				// piece should occur only once per list
				
				if (p_entry->mp_meta == pMeta)
				{
					if (p_prev)
						p_prev->mp_next = p_entry->mp_next;
					else
						mp_bucket_list[x + pMeta->m_cell_area.GetX()][z + pMeta->m_cell_area.GetZ()] = p_entry->mp_next;
					delete p_entry;
					break;
				}
	
				p_prev = p_entry;
				p_entry = p_entry->mp_next;
			}
		} // end for z
	} // end for x
}




void CParkManager::create_abstract_metapieces()
{
	ParkEd("CParkManager::create_abstract_metapieces()");
	
	/*
		This add_later stuff is so we can create singular metapieces that weren't defined in 
		Ed_standard_metapieces. Every CPiece that exists must have a corresponding singular
		metapiece, at least an abstract one.
	*/
	uint32 add_later_tab[1000];
	uint add_later_count = 0;
	
	Script::CArray *p_metapiece_array = Script::GetArray("Ed_standard_metapieces", Script::ASSERT);
	
	// this first loop makes sure that metapieces named in EdPieces2.q actually exist
	for (uint i = 0; i < p_metapiece_array->GetSize(); i++)
	{
		Script::CStruct *p_entry = p_metapiece_array->GetStructure(i);
		
		// see if singular or multiple metapiece
		uint32 single_crc = 0;
		if (p_entry->GetChecksum("single", &single_crc))
		{
			mp_generator->GetMasterPiece(single_crc, false);
		}		
	} // end for


	// now, build abstract metapieces from Ed_standard_metapieces array
	for (uint i = 0; i < p_metapiece_array->GetSize(); i++)
	{
		Script::CStruct *p_entry = p_metapiece_array->GetStructure(i);
		
		Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap());
		CAbstractMetaPiece *p_meta = new CAbstractMetaPiece();
		Mem::Manager::sHandle().PopContext();
		
		// see if singular or multiple metapiece
		uint32 single_crc = 0;
		Script::CArray *p_multiple_array = NULL;
		if (p_entry->GetChecksum("single", &single_crc))
		{
			p_meta->initialize_desc_table(1);
			
			CSourcePiece *p_source_piece = mp_generator->GetMasterPiece(single_crc, true);
			
			GridDims area(0, 0, 0);
			p_source_piece->GetCellDims(&area);
			Script::CArray *p_pos_array = NULL;
			if (p_entry->GetArray("pos", &p_pos_array))
			{
				area.SetXYZ(p_pos_array->GetInteger(0), p_pos_array->GetInteger(1), p_pos_array->GetInteger(2));
			}

			p_meta->add_piece(p_source_piece, &area);

			// if just a singular meta, use the name checksum already associated with single piece
			p_meta->m_name_checksum = p_source_piece->GetType();
			Dbg_Assert(add_later_count < 1000);
			add_later_tab[add_later_count++] = p_meta->m_name_checksum;			
			p_meta->set_flag(CMetaPiece::mSINGULAR);
			
			// Finally, Check to see if it is a special type
			// and add it to the table of special types: 
			
			uint32 special_type;
			if (p_entry->GetChecksum(CRCD(0xf176a9fd,"special_type"), &special_type))
			{

				// gap piece detection here ----- need to reinstate this when we do gaps				
				if (special_type == CRCD(0x2813ab8a,"gap_placement"))
					p_meta->set_flag(CMetaPiece::mGAP_PLACEMENT);
				if (special_type == CRCD(0xfc26b6f4,"area_selection"))
					p_meta->set_flag(CMetaPiece::mAREA_SELECTION);
				if (special_type == CRCD(0xffd81c08,"rail_placement"))
					p_meta->set_flag(CMetaPiece::mRAIL_PLACEMENT);
				
				if (special_type == CRCD(0x48fa9c7b,"restart_1"))
					SetRestartTypeId(CParkGenerator::vONE_PLAYER, single_crc);
				if (special_type == CRCD(0x419183b2,"restart_multi"))
					SetRestartTypeId(CParkGenerator::vMULTIPLAYER, single_crc);
				if (special_type == CRCD(0xe69aefaf,"restart_horse"))
					SetRestartTypeId(CParkGenerator::vHORSE, single_crc);
				if (special_type == CRCD(0xa091ce25,"king_of_hill"))
					SetRestartTypeId(CParkGenerator::vKING_OF_HILL, single_crc);
				
				if (special_type == CRCD(0x74b0f4e9,"red_flag"))
				{
					SetRestartTypeId(CParkGenerator::vRED_FLAG, single_crc);
					p_meta->set_flag(CMetaPiece::mNOT_ON_GC);
				}
				if (special_type == CRCD(0xc893acf8,"green_flag"))
				{
					SetRestartTypeId(CParkGenerator::vGREEN_FLAG, single_crc);
					p_meta->set_flag(CMetaPiece::mNOT_ON_GC);
				}
				if (special_type == CRCD(0x40bdfa7e,"blue_flag"))
				{
					SetRestartTypeId(CParkGenerator::vBLUE_FLAG, single_crc);
					p_meta->set_flag(CMetaPiece::mNOT_ON_GC);
				}
				if (special_type == CRCD(0x3ae047fc,"yellow_flag"))
				{
					SetRestartTypeId(CParkGenerator::vYELLOW_FLAG, single_crc);
					p_meta->set_flag(CMetaPiece::mNOT_ON_GC);
				}
					
			}
		}
		else if (p_entry->GetArray("multiple", &p_multiple_array))
		{
			bool contains_risers = false;
			p_meta->initialize_desc_table(p_multiple_array->GetSize());
			
			for (uint j = 0; j < p_multiple_array->GetSize(); j++)
			{
				Script::CStruct *p_name_pos = p_multiple_array->GetStructure(j);
				
				Script::CComponent *p_name_crc_comp = p_name_pos->GetNextComponent(NULL);
				Dbg_Assert(p_name_crc_comp->mType == ESYMBOLTYPE_NAME);				
				uint32 name_crc = p_name_crc_comp->mChecksum;

				Script::CArray *p_pos_array = NULL;
				p_name_pos->GetArray(NONAME, &p_pos_array, Script::ASSERT);
				
				CSourcePiece *p_source_piece = mp_generator->GetMasterPiece(name_crc, true);
				Dbg_Assert(add_later_count < 1000);
				add_later_tab[add_later_count++] = name_crc;
				
				GridDims area;
				p_source_piece->GetCellDims(&area);
				area.SetXYZ(p_pos_array->GetInteger(0), p_pos_array->GetInteger(1), p_pos_array->GetInteger(2));
	
				Mth::ERot90 rot = Mth::ROT_0;
				if (p_pos_array->GetSize() > 3)
					rot = (Mth::ERot90) p_pos_array->GetInteger(3);
				
				if (p_name_pos->ContainsFlag(CRCD(0xcd75a3f,"riser")))
					contains_risers = true;
				p_meta->add_piece(p_source_piece, &area, rot, p_name_pos->ContainsFlag(CRCD(0xcd75a3f,"riser")));
			} // end for

			if (contains_risers)
				p_meta->set_flag(CMetaPiece::mCONTAINS_RISERS);
		}
		else if (p_entry->GetArray("ack", &p_multiple_array))
		{
			p_meta->initialize_desc_table(p_multiple_array->GetSize());
			
			for (uint j = 0; j < p_multiple_array->GetSize(); j++)
			{
				Script::CStruct *p_name_pos = p_multiple_array->GetStructure(j);
				
				Script::CComponent *p_name_crc_comp = p_name_pos->GetNextComponent(NULL);
				Dbg_Assert(p_name_crc_comp->mType == ESYMBOLTYPE_NAME);				
				uint32 name_crc = p_name_crc_comp->mChecksum;

				Script::CArray *p_pos_array = NULL;
				p_name_pos->GetArray(NONAME, &p_pos_array, Script::ASSERT);
				
				CAbstractMetaPiece *p_source_meta = GetAbstractMeta(name_crc);
				
				GridDims area = p_source_meta->GetArea();
				area.SetXYZ(p_pos_array->GetInteger(0), p_pos_array->GetInteger(1), p_pos_array->GetInteger(2));
	
				Mth::ERot90 rot = Mth::ROT_0;
				if (p_pos_array->GetSize() > 3)
					rot = (Mth::ERot90) p_pos_array->GetInteger(3);
				
				p_meta->add_piece_dumb(p_source_meta, &area, rot, p_name_pos->ContainsFlag(CRCD(0xcd75a3f,"riser")));
			} // end for
		}
		else
		{
			Dbg_MsgAssert(0, ("screwy entry %d in Ed_standard_metapieces", i));
		}
		
		#if 1
		#ifdef __NOPT_ASSERT__
		GridDims test_area = p_meta->GetArea();
		Dbg_MsgAssert(test_area.GetW() > 0 && test_area.GetH() > 0 && test_area.GetL() > 0, 
					  ("metapiece %s has invalid dimensions (%d,%d,%d)", Script::FindChecksumName(p_meta->GetNameChecksum()),
					   test_area.GetW(), test_area.GetH(), test_area.GetL()));
		#if 0
		if (p_meta->GetNameChecksum() == Script::GenerateCRC("Sk4Ed_Fence_10x10"))
			printf("OY! metapiece %s has dimensions (%d,%d,%d)\n", Script::FindChecksumName(p_meta->GetNameChecksum()),
					   test_area.GetW(), test_area.GetH(), test_area.GetL());
		#endif

		#endif		
		#endif
		
		// the name provided in EdPieces.q, if any, takes precedence
		if (p_entry->GetChecksum("name", &p_meta->m_name_checksum))
		{
			// this metapiece has been renamed, so we must treat it as a non-singular piece
			p_meta->clear_flag(CMetaPiece::mSINGULAR);
		}
		// make sure no piece with checksum currently exists
		Dbg_MsgAssert(!GetAbstractMeta(p_meta->m_name_checksum), ("name checksum already used for metapiece"));

		if (p_entry->ContainsFlag("is_riser"))
			p_meta->set_flag(CMetaPiece::mIS_RISER);
		if (p_entry->ContainsFlag("no_rails"))
		{
#			ifdef __NOPT_ASSERT__
			Ryan("No rails for %s\n", Script::FindChecksumName(p_meta->m_name_checksum));
#			endif
			p_meta->set_flag(CMetaPiece::mNO_RAILS);
		}
		
		Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap());
		CMapListNode *p_node = new CMapListNode();
		Mem::Manager::sHandle().PopContext();
		p_node->mp_meta = p_meta;
		p_node->mp_next = mp_abstract_metapiece_list;
		mp_abstract_metapiece_list = p_node;
		m_num_abstract_metapieces++;
	} // end for

	
	for (uint i = 0; i < add_later_count; i++)
	{
		if (GetAbstractMeta(add_later_tab[i]))
			continue;
		
		Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap());
		CAbstractMetaPiece *p_meta = new CAbstractMetaPiece();
		Mem::Manager::sHandle().PopContext();
		
		p_meta->initialize_desc_table(1);
		CSourcePiece *p_source_piece = mp_generator->GetMasterPiece(add_later_tab[i], true);

		GridDims area(0, 0, 0);
		p_source_piece->GetCellDims(&area);
		p_meta->add_piece(p_source_piece, &area);

		p_meta->m_name_checksum = add_later_tab[i];
		p_meta->set_flag(CMetaPiece::mSINGULAR);

		Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap());
		CMapListNode *p_node = new CMapListNode();
		Mem::Manager::sHandle().PopContext();
		p_node->mp_meta = p_meta;
		p_node->mp_next = mp_abstract_metapiece_list;
		mp_abstract_metapiece_list = p_node;
		m_num_abstract_metapieces++;
	}
}




void CParkManager::SetRestartTypeId(CParkGenerator::RestartType type, uint32 id)
{

	// Mick	
//	printf ("SetRestartTypeId(%d,%x)\n",type,id);	
	if (type > CParkGenerator::vEMPTY && type < CParkGenerator::vNUM_RESTART_TYPES)
	{
		m_restartPieceIdTab[type] = id;		
	}

/*	
	switch(type)
	{
		case CParkGenerator::vONE_PLAYER:
//			printf ("Found vONE_PLAYER\n");
			m_restartPieceIdTab[0] = id;
			break;
		case CParkGenerator::vMULTIPLAYER:
//			printf ("Found vMULTIPLAYER\n");
			m_restartPieceIdTab[1] = id;
			break;
		case CParkGenerator::vHORSE:
//			printf ("Found vHORSE\n");
			m_restartPieceIdTab[2] = id;
			break;
		case CParkGenerator::vKING_OF_HILL:
//			printf ("Found vKING_OF_HILL\n");
			m_restartPieceIdTab[3] = id;
			break;
		default:
			break;
	
	}
*/
}




// returns the type of restart piece this is, or vEMPTY is it is not a restart piece
// (vEMPTY is 0, so the return value can be treated as true/false)
CParkGenerator::RestartType CParkManager::IsRestartPiece(uint32 id)
{
	for (int type = CParkGenerator::vONE_PLAYER; type < CParkGenerator::vNUM_RESTART_TYPES; type++)
	{
		if (id == m_restartPieceIdTab[type])
		{
			return (CParkGenerator::RestartType) type;
		}
	}
	return CParkGenerator::vEMPTY;

/*
	if (id == m_restartPieceIdTab[0])
		return CParkGenerator::vONE_PLAYER;
	if (id == m_restartPieceIdTab[1])
		return CParkGenerator::vMULTIPLAYER;
	if (id == m_restartPieceIdTab[2])
		return CParkGenerator::vHORSE;
	if (id == m_restartPieceIdTab[3])
		return CParkGenerator::vKING_OF_HILL;
	return CParkGenerator::vEMPTY;
*/
}

/*
Commented out cos I just discovered Destroy() does pretty much the same thing!

// K: This is for freeing up as much memory as possible when playing a park, so that
// the number of players can be increased. Each player requires 830K.
void CParkManager::FreeUpMemoryForPlayingPark()
{
	// Delete everything in the park editor heap, so that it can be removed, freeing
	// up 900K.
	
	// First delete the cursor.
	Ed::CParkEditor::Instance()->DeleteCursor();
	
	printf("After deleting cursor ...\n");	
	printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems());
	MemView_AnalyzeHeap(mp_generator->GetParkEditorHeap());

	mp_generator->DeleteSourceAndClonedPieces();
	
	// Delete the concrete metapieces
	CMapListNode *p_node = mp_concrete_metapiece_list;
	while(p_node)
	{
		CMapListNode *p_next_node = p_node->GetNext();
		delete p_node->GetConcreteMeta();
		delete p_node;
		p_node = p_next_node;
	}
	m_num_concrete_metapieces=0;
	mp_concrete_metapiece_list=NULL;

	printf("After deleting CParkManager::mp_concrete_metapiece_list ...\n");	
	printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems());
	MemView_AnalyzeHeap(Ed::CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap());

	// Free up the abstract metapieces.	
	p_node = mp_abstract_metapiece_list;
	while(p_node)
	{
		CMapListNode *p_next_node = p_node->GetNext();
		delete p_node->GetAbstractMeta();
		delete p_node;
		p_node = p_next_node;
	}
	m_num_abstract_metapieces=0;
	mp_abstract_metapiece_list=NULL;


	printf("After deleting CParkManager::mp_abstract_metapiece_list ...\n");	
	printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems());
	MemView_AnalyzeHeap(Ed::CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap());


	// The park editor heap is now totally empty!
	// So delete it.
	mp_generator->DeleteParkEditorHeap();
	
	
	
	printf("After deleting stuff ...\n");	
	printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems());
	MemView_AnalyzeHeap(Ed::CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap());
}
*/

// K: Added type so that DESTROY_ONLY_PIECES can be passed when this function is used to clean
// up the park editor heap for deletion when playing a park.
void CParkManager::destroy_concrete_metapieces(CParkGenerator::EDestroyType type)
{
	ParkEd("CParkManager::destroy_concrete_metapieces()");

	Dbg_Assert(mp_gap_manager);
	mp_gap_manager->RemoveAllGaps();
	
	// Commented these out because destroy_concrete_metapieces gets called via 
	// ScriptFreeUpMemoryForPlayingPark, which must not delete the edited rails.
	// (Fix to TT2125)
	//Obj::GetRailEditor()->DestroyRailGeometry();
	//Obj::GetRailEditor()->DestroyPostGeometry();
	
	// remove all concrete metapieces, except locked ones
	CMapListNode *p_node = mp_concrete_metapiece_list;
	while(p_node)
	{
		CMapListNode *p_next_node = p_node->GetNext();
		DestroyConcreteMeta(p_node->GetConcreteMeta(), mDONT_DESTROY_PIECES_ABOVE);
		p_node = p_next_node;
	}
	
	mp_generator->DestroyAllClonedPieces(type); // if any are left
	
	mp_generator->KillRestarts();		// This should have been taken care of by destrying the actual concrete metas, but just to be safe....
}


#ifdef __PLAT_NGC__
void CParkManager::SwapMapBufferEndianness()
{
	CompressedMapHeader *p_header = (CompressedMapHeader *)mp_compressed_map_buffer;
	
	p_header->mChecksum=_32(p_header->mChecksum);
	p_header->mSizeInBytes=_16(p_header->mSizeInBytes);
	p_header->mVersion=_16(p_header->mVersion);
	p_header->mTheme=_16(p_header->mTheme);
	p_header->mParkSize=_16(p_header->mParkSize);
	p_header->mNumMetas=_16(p_header->mNumMetas);
	p_header->mNumGaps=_16(p_header->mNumGaps);
	p_header->mTODScript=_32(p_header->mTODScript);
	
	uint32 *p_metas=(uint32*)((uint8*)(p_header+1) + FLOOR_MAX_WIDTH * FLOOR_MAX_LENGTH);
	for (int i=0; imNumMetas; ++i)
	{
		p_metas[0]=_32(p_metas[0]);
		p_metas[1]=_32(p_metas[1]);
		p_metas+=2;
	}
	
	CompressedGap *p_gap=(CompressedGap*)p_metas;
	for (int i=0; imNumGaps; ++i)
	{
		p_gap->score=_16(p_gap->score);
		p_gap->mCancelFlags=_32(p_gap->mCancelFlags);
		++p_gap;
	}
}
#endif

/*
Park editor map file format:

Header
-size
-version
-theme
-X, Z, W, L
-number of metas

Floor height array, each entry:
-height
-texture
(included risers taken care of by placed metapieces)

Meta entries:
-10 bits: index
-8 bits: X
-8 bits: Z
-6 bits: padding
-2 bits: rot
-5 bits: Y
-1 bit: padding
(total: 40)

Gap info

User-defined stamps
-number contained
	-source meta
	-8 bits: X
	-8 bits: Z
	-6 bits: Y
	-2 bits: rot
*/

void CParkManager::write_compressed_map_buffer()
{
	Dbg_Assert(m_state_on);
	ParkEd("CParkManager::write_compressed_map_buffer()");	
	//DumpUnwindStack( 20, 0 );
	
	if (m_compressed_map_flags & mIS_NEWER_THAN_PARK)
		// we just loaded this park, so don't touch it
		return;
	
	for (int i=0; iGetSize();
	
	CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer;
	p_header->mVersion = (VERSION);
	p_header->mTheme = (m_theme);
	p_header->mParkSize = (0);
	p_header->mX = m_park_near_bounds.GetX();
	p_header->mZ = m_park_near_bounds.GetZ();
	p_header->mW = m_park_near_bounds.GetW();
	p_header->mL = m_park_near_bounds.GetL();
	p_header->mTODScript = Ed::CParkEditor::Instance()->GetTimeOfDayScript();
	p_header->mMaxPlayers=GetGenerator()->GetMaxPlayers();
	Dbg_MsgAssert(strlen(mp_park_name) < 64,("Park name '%s' too long",mp_park_name));
	strcpy(p_header->mParkName,mp_park_name);

	CompressedFloorEntry *p_floor_entry = (CompressedFloorEntry *) (p_header + 1);		
	for (int i = 0; i < FLOOR_MAX_WIDTH; i++)
	{
		for (int j = 0; j < FLOOR_MAX_LENGTH; j++)
		{
			p_floor_entry->mHeight = (sint8) m_floor_height_map[i][j].mHeight;
			p_floor_entry++;
		}
	}

	uint8 *p_meta_entry = (uint8 *) p_floor_entry;

	int num_metas_outputted = 0;
	CMapListNode *p_meta_node = mp_concrete_metapiece_list;
	while(p_meta_node)
	{
		CConcreteMetaPiece *p_meta = p_meta_node->GetConcreteMeta();
		if (!p_meta->IsRiser() && !p_meta->IsInnerShell() && p_meta->IsInPark())
		{
			uint32 *p_meta_entry_first = (uint32 *) p_meta_entry;
			uint32 *p_meta_entry_second = (uint32 *) (p_meta_entry + 4);

			GridDims meta_area = p_meta->GetArea();
			
			uint32 metapiece_index = 0;
			uint32 metapiece_name_checksum = p_meta->GetNameChecksum();
			for (; metapiece_index < save_map_array_size; metapiece_index++)
			{	
				if (p_save_map_array->GetChecksum(metapiece_index) == metapiece_name_checksum)
					break;
			}
			
			if (metapiece_index < save_map_array_size)
			{			
				*p_meta_entry_first = metapiece_index & 1023;
				*p_meta_entry_first |= meta_area.GetX()	<< 10;
				*p_meta_entry_first |= meta_area.GetZ()	<< 18;
	
				*p_meta_entry_second = p_meta->GetRot() & 3;
				*p_meta_entry_second |= ((meta_area.GetY() + 16) & 31) << 2;
				
				*p_meta_entry_first = (*p_meta_entry_first);
				*p_meta_entry_second = (*p_meta_entry_second);
				//Ryan("compressed piece: 0x%x 0x%x\n", *p_meta_entry_first, *p_meta_entry_second);
				
				p_meta_entry += 8;
				num_metas_outputted++;
			}
		}

		p_meta_node = p_meta_node->GetNext();
	}

	int buffer_used_size = (uint32) p_meta_entry - (uint32) p_header;
	Dbg_MsgAssert(buffer_used_size <= COMPRESSED_MAP_SIZE, ("compressed map buffer (%d) needs to be bigger than (%d)",COMPRESSED_MAP_SIZE,buffer_used_size));
	p_header->mNumMetas = (num_metas_outputted);	
	
	/* Gap Section */

	// count number of gaps
	CGapManager::GapDescriptor *p_desc_tab[CGapManager::vMAX_GAPS];
	int num_gaps = 0;
	int gap;
	for (gap = 0; gap < CGapManager::vMAX_GAPS; gap++)
	{
		int half = 0;
		CGapManager::GapDescriptor *pDesc = mp_gap_manager->GetGapDescriptor(gap, &half);
		if (pDesc && half == 0 && pDesc->numCompleteHalves == 2)
			p_desc_tab[num_gaps++] = pDesc;
	}
	p_header->mNumGaps = (num_gaps);
	
	// write gaps
	CompressedGap *p_out_gap = (CompressedGap *) p_meta_entry;
	for (gap = 0; gap < num_gaps; gap++)
	{
		p_out_gap->rot = 0;
		for (int half = 0; half < 2; half++)
		{
			p_out_gap->x[half] = p_desc_tab[gap]->loc[half].GetX(); 
			p_out_gap->y[half] = p_desc_tab[gap]->loc[half].GetY() + (MAX_HEIGHT>>1); 
			p_out_gap->z[half] = p_desc_tab[gap]->loc[half].GetZ(); 
			p_out_gap->rot |= p_desc_tab[gap]->rot[half]<<(half*4);
			
			p_out_gap->extent[half] = (uint8) ((p_desc_tab[gap]->leftExtent[half] & 15) << 4);
			p_out_gap->extent[half] |= (uint8) (p_desc_tab[gap]->rightExtent[half] & 15);
			//Ryan("adding gap at (%d,%d,%d)\n", p_out_gap->x[half], p_out_gap->y[half], p_out_gap->z[half]);
		}
		Dbg_MsgAssert(strlen(p_desc_tab[gap]->text) <= 31, ("too many characters in gap name %s", p_desc_tab[gap]->text));
		strcpy(p_out_gap->text, p_desc_tab[gap]->text);
		p_out_gap->score = ((uint16) p_desc_tab[gap]->score);
		
		p_out_gap->mCancelFlags=p_desc_tab[gap]->mCancelFlags;
		
		p_out_gap++;
	}
  
	buffer_used_size = (uint32) p_out_gap - (uint32) p_header;
	Dbg_MsgAssert(buffer_used_size <= COMPRESSED_MAP_SIZE, ("compressed map buffer (%d) needs to be bigger than (%d)",COMPRESSED_MAP_SIZE,buffer_used_size));
	p_header->mSizeInBytes = buffer_used_size;
	
	/* Flag section */

	if (!(m_compressed_map_flags & mIN_SYNC_WITH_PARK) && !(m_compressed_map_flags & mIS_NEWER_THAN_PARK))
		m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mNOT_SAVED_LOCAL);
	
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | (mIN_SYNC_WITH_PARK + mIS_VALID));
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIS_NEWER_THAN_PARK);


	p_header->mChecksum = (Crc::GenerateCRCCaseSensitive((const char *) mp_compressed_map_buffer + 4, COMPRESSED_MAP_SIZE-4));
	
	// XXX
	ParkEd("end CParkManager::write_compressed_map_buffer()");	
}


// Create a minimal compressed map buffer
void CParkManager::fake_compressed_map_buffer()
{
	ParkEd("CParkManager::fake_compressed_map_buffer()");	
	
//	Script::CArray *p_save_map_array = Script::GetArray("Ed_Save_Map", Script::ASSERT);
//	uint save_map_array_size = p_save_map_array->GetSize();
	
	setup_default_dimensions();
	
	CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer;
	p_header->mVersion = (VERSION);
	p_header->mTheme = (m_theme);
	p_header->mParkSize = (0);
	p_header->mX = m_park_near_bounds.GetX();
	p_header->mZ = m_park_near_bounds.GetZ();
	p_header->mW = m_park_near_bounds.GetW();
	p_header->mL = m_park_near_bounds.GetL();
	
	Ed::CParkEditor *p_editor=Ed::CParkEditor::Instance();
	if (p_editor)
	{
		p_header->mTODScript = p_editor->GetTimeOfDayScript();
	}
	else
	{
		p_header->mTODScript=0;
	}	
	
	p_header->mMaxPlayers=2;
	strcpy(p_header->mParkName, "unnamed park");

	CompressedFloorEntry *p_floor_entry = (CompressedFloorEntry *) (p_header + 1);		
	for (int i = 0; i < FLOOR_MAX_WIDTH; i++)
	{
		for (int j = 0; j < FLOOR_MAX_LENGTH; j++)
		{
			p_floor_entry->mHeight = 0;		// (sint8) m_floor_height_map[i][j].mHeight;
			p_floor_entry++;
		}
	}

	uint8 *p_meta_entry = (uint8 *) p_floor_entry;
	int num_metas_outputted = 0;
	int buffer_used_size = (uint32) p_meta_entry - (uint32) p_header;
	p_header->mNumMetas = num_metas_outputted;	
	p_header->mChecksum = Crc::GenerateCRCCaseSensitive((const char *) mp_compressed_map_buffer + 4, buffer_used_size - 4);
	int num_gaps = 0;
	p_header->mNumGaps = num_gaps;
	
	// write gaps
	CompressedGap *p_out_gap = (CompressedGap *) p_meta_entry;
	buffer_used_size = (uint32) p_out_gap - (uint32) p_header;
	Dbg_MsgAssert(buffer_used_size <= COMPRESSED_MAP_SIZE, ("compressed map buffer (%d) needs to be bigger than (%d)",COMPRESSED_MAP_SIZE,buffer_used_size));
	p_header->mSizeInBytes = buffer_used_size;
	
	/* Flag section */

	if (!(m_compressed_map_flags & mIN_SYNC_WITH_PARK) && !(m_compressed_map_flags & mIS_NEWER_THAN_PARK))
		m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mNOT_SAVED_LOCAL);
	
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | (mIN_SYNC_WITH_PARK + mIS_VALID));
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIS_NEWER_THAN_PARK);
	
	// XXX
	ParkEd("end CParkManager::fake_compressed_map_buffer()");	
}




void CParkManager::read_from_compressed_map_buffer()
{
	Dbg_Assert(m_state_on);
	// XXX
	ParkEd("CParkManager::read_from_compressed_map_buffer()");	
	
	Script::CArray *p_save_map_array = Script::GetArray("Ed_Save_Map", Script::ASSERT);
	uint32 dead_piece_checksum = Script::GenerateCRC("Sk4Ed_Dead");
	
	Dbg_MsgAssert(m_compressed_map_flags & mIS_VALID, ("compressed map is invalid"));
	
	CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer;
	// make sure latest version
	if (p_header->mVersion != VERSION)
	{
		read_from_compressed_map_buffer_old();
		GetGenerator()->SetMaxPlayers(1);
		return;
	}

	m_park_near_bounds[X] = p_header->mX;
	m_park_near_bounds[Z] = p_header->mZ;
	m_park_near_bounds[W] = p_header->mW;
	m_park_near_bounds[L] = p_header->mL;
	m_theme = p_header->mTheme;
	Ed::CParkEditor::Instance()->SetTimeOfDayScript(p_header->mTODScript);
	#ifdef __PLAT_NGC__
	p_header->mMaxPlayers=2;
	#endif
	GetGenerator()->SetMaxPlayers(p_header->mMaxPlayers);
	// SG: Was setting to the incorrect number (8 when it should have been 2). I think
	// it is redundant anyway as max players is set elsewhere already
	//GameNet::Manager::Instance()->SetMaxPlayers(p_header->mMaxPlayers);
	
	CompressedFloorEntry *p_floor_entry = (CompressedFloorEntry *) (p_header + 1);		
	for (int i = 0; i < FLOOR_MAX_WIDTH; i++)
	{
		for (int j = 0; j < FLOOR_MAX_LENGTH; j++)
		{
			m_floor_height_map[i][j].mHeight = (int) p_floor_entry->mHeight;
			m_floor_height_map[i][j].mEnclosedFloor = 0;
			p_floor_entry++;
		}
	}

	uint8 *p_meta_entry = (uint8 *) p_floor_entry;
	Dbg_Printf( "****** NUM METAS WHILE READING FROM BUFFER : %d\n", p_header->mNumMetas );
	for (int i = 0; i < p_header->mNumMetas; i++)
	{
		uint32 *p_meta_entry_first = (uint32 *) p_meta_entry;
		uint32 *p_meta_entry_second = (uint32 *) (p_meta_entry + 4);
		uint32 meta1 = *p_meta_entry_first;
		uint32 meta2 = *p_meta_entry_second;
		
		//Ryan("compressed piece: 0x%x 0x%x\n", *p_meta_entry_first, *p_meta_entry_second);
		
		uint32 metapiece_index = meta1 & 1023;
		uint32 metapiece_checksum = p_save_map_array->GetChecksum(metapiece_index);

		if (metapiece_checksum != dead_piece_checksum)
		{
			CAbstractMetaPiece *p_source_meta = GetAbstractMeta(metapiece_checksum);
			Dbg_MsgAssert(p_source_meta, ("couldn't find abstract meta %s", Script::FindChecksumName(metapiece_checksum)));
			#ifdef __PLAT_NGC__
			if (!(p_source_meta->m_flags & CMetaPiece::mNOT_ON_GC))			
			#endif
			{			
				CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_source_meta);
				GridDims pos;
				pos[X] = (meta1 >> 10) & 255;
				pos[Z] = (meta1 >> 18) & 255;
				pos[Y] = ((meta2 >> 2) & 31) - 16;
				int rot = meta2 & 3;
				p_meta->SetRot(Mth::ERot90(rot));
				AddMetaPieceToPark(pos, p_meta);
			}
		}
		
		p_meta_entry += 8;
	}	
	
	/* Gap section */
	
	CompressedGap *p_in_gap = (CompressedGap *) p_meta_entry;
	for (int g = 0; g < p_header->mNumGaps; g++)
	{
		CGapManager::GapDescriptor desc;
		
		for (int half = 0; half < 2; half++)
		{
			desc.loc[half][X] = p_in_gap->x[half]; 
			desc.loc[half][Y] = p_in_gap->y[half] - (MAX_HEIGHT>>1); 
			desc.loc[half][Z] = p_in_gap->z[half]; 
			desc.rot[half] = (p_in_gap->rot>>(half*4)) & 15;
			
			desc.leftExtent[half] = (p_in_gap->extent[half]>>4) & 15;
			desc.rightExtent[half] = (p_in_gap->extent[half]) & 15;
			//Ryan("adding gap at (%d,%d,%d)\n", p_in_gap->x[half], p_in_gap->y[half], p_in_gap->z[half]);
		}
		Dbg_Assert(strlen(p_in_gap->text) < 32);
		strcpy(desc.text, p_in_gap->text);
		desc.score = (int) p_in_gap->score;
		desc.numCompleteHalves = 2;
		desc.mCancelFlags=p_in_gap->mCancelFlags;
		
		AddGap(desc);
		
		p_in_gap++;
	}
	
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIN_SYNC_WITH_PARK);
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIS_NEWER_THAN_PARK);
	
	// XXX
	ParkEd("end CParkManager::read_from_compressed_map_buffer()");	
}



void CParkManager::read_from_compressed_map_buffer_old()
{
	Dbg_Assert(m_state_on);
	// XXX
	ParkEd("CParkManager::read_from_compressed_map_buffer()");	
	
	Script::CArray *p_save_map_array = Script::GetArray("Ed_Save_Map", Script::ASSERT);
	uint32 dead_piece_checksum = Script::GenerateCRC("Sk4Ed_Dead");
	
	Dbg_MsgAssert(m_compressed_map_flags & mIS_VALID, ("compressed map is invalid"));
	
	CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer;

	// make sure proper version
	Dbg_MsgAssert((p_header->mVersion) == VERSION-1, ("can't read park with this version #: %d", (p_header->mVersion)));
	
	// XXX
//	for (int zzz = 0; zzz < 16; zzz++)
//		printf("OUT OF DATE PARK FORMAT -- SAVE NOW. OUT OF DATE PARK FORMAT -- SAVE NOW. OUT OF DATE PARK FORMAT -- SAVE NOW.\n");

	m_park_near_bounds[X] = p_header->mX;
	m_park_near_bounds[Z] = p_header->mZ;
	m_park_near_bounds[W] = p_header->mW;
	m_park_near_bounds[L] = p_header->mL;
	m_theme = (p_header->mTheme);
	Ed::CParkEditor::Instance()->SetTimeOfDayScript(p_header->mTODScript);
	
	GetGenerator()->SetMaxPlayers(p_header->mMaxPlayers);
	// SG: Was setting to the incorrect number (8 when it should have been 2). I think
	// it is redundant anyway as max players is set elsewhere already
	//GameNet::Manager::Instance()->SetMaxPlayers(p_header->mMaxPlayers);
	
	CompressedFloorEntry *p_floor_entry = (CompressedFloorEntry *) (p_header + 1);		
	for (int i = 0; i < FLOOR_MAX_WIDTH; i++)
	{
		for (int j = 0; j < FLOOR_MAX_LENGTH; j++)
		{
			m_floor_height_map[i][j].mHeight = (int) p_floor_entry->mHeight;
			m_floor_height_map[i][j].mEnclosedFloor = 0;
			p_floor_entry++;
		}
	}

	uint8 *p_meta_entry = (uint8 *) p_floor_entry;
	Dbg_Printf( "****** NUM METAS WHILE READING FROM BUFFER : %d\n", p_header->mNumMetas );
	for (int i = 0; i < (p_header->mNumMetas); i++)
	{
		uint32 *p_meta_entry_first = (uint32 *) p_meta_entry;
		uint32 *p_meta_entry_second = (uint32 *) (p_meta_entry + 4);
		uint32 meta1 = (*p_meta_entry_first);
		uint32 meta2 = (*p_meta_entry_second);
		
		//Ryan("compressed piece: 0x%x 0x%x\n", *p_meta_entry_first, *p_meta_entry_second);
		
		uint32 metapiece_index = meta1 & 1023;
		uint32 metapiece_checksum = p_save_map_array->GetChecksum(metapiece_index);

		if (metapiece_checksum != dead_piece_checksum)
		{
			CAbstractMetaPiece *p_source_meta = GetAbstractMeta(metapiece_checksum);
			Dbg_MsgAssert(p_source_meta, ("couldn't find abstract meta %s", Script::FindChecksumName(metapiece_checksum)));
			#ifdef __PLAT_NGC__
			if (!(p_source_meta->m_flags & CMetaPiece::mNOT_ON_GC))			
			#endif
			{			
				CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_source_meta);
				GridDims pos;
				pos[X] = (meta1 >> 10) & 255;
				pos[Z] = (meta1 >> 18) & 255;
				pos[Y] = ((meta2 >> 2) & 31) - 16;
				int rot = meta2 & 3;
				p_meta->SetRot(Mth::ERot90(rot));
				AddMetaPieceToPark(pos, p_meta);
			}
		}
		
		p_meta_entry += 8;
	}	
	
	// Gap section
	
	CompressedGapOld *p_in_gap = (CompressedGapOld *) p_meta_entry;
	for (int g = 0; g < (p_header->mNumGaps); g++)
	{
		CGapManager::GapDescriptor desc;
		
		for (int half = 0; half < 2; half++)
		{
			desc.loc[half][X] = p_in_gap->x[half]; 
			desc.loc[half][Y] = p_in_gap->y[half] - (MAX_HEIGHT>>1); 
			desc.loc[half][Z] = p_in_gap->z[half]; 
			desc.rot[half] = (p_in_gap->rot>>(half*4)) & 15;
			
			desc.leftExtent[half] = (p_in_gap->extent[half]>>4) & 15;
			desc.rightExtent[half] = (p_in_gap->extent[half]) & 15;
			//Ryan("adding gap at (%d,%d,%d)\n", p_in_gap->x[half], p_in_gap->y[half], p_in_gap->z[half]);
		}
		Dbg_Assert(strlen(p_in_gap->text) < 32);
		strcpy(desc.text, p_in_gap->text);
		desc.score = (int) (p_in_gap->score);
		desc.numCompleteHalves = 2;
		
		AddGap(desc);
		
		p_in_gap++;
	}
	
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIN_SYNC_WITH_PARK);
	m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIS_NEWER_THAN_PARK);
	
	// XXX
	ParkEd("end CParkManager::read_from_compressed_map_buffer_old()");	
}

void	CParkManager::setup_default_dimensions()
{
	int default_inner_dim = Script::GetInt("ed_default_inner_dim", Script::ASSERT);
	Dbg_Assert(default_inner_dim <= MAX_WIDTH && default_inner_dim <= MAX_LENGTH);

	m_park_far_bounds.SetXYZ(1, -4, 1);
	m_park_far_bounds.SetWHL(MAX_WIDTH, 8, MAX_LENGTH);
	m_park_near_bounds.SetXYZ(1 + (MAX_WIDTH - default_inner_dim) / 2, -4, 1 + (MAX_LENGTH - default_inner_dim) / 2);
	m_park_near_bounds.SetWHL(default_inner_dim, 8, default_inner_dim);
}



void CParkManager::setup_road_mask()
{
	/*
		The first four entries in m_road_mask_tab will be set up in RebuildInnerShell()
	*/
	
	Script::CArray *p_road_mask_array = Script::GetArray("Ed_Roadmask", Script::ASSERT);
	m_road_mask_tab_size = p_road_mask_array->GetSize();
	for (uint i = 4; i < 4 + m_road_mask_tab_size; i++)
	{
		Dbg_Assert(i < NUM_ROAD_PIECES);
		Script::CArray *p_entry = p_road_mask_array->GetArray(i);

		m_road_mask_tab[i][X] = p_entry->GetInt(0);
		m_road_mask_tab[i][Z] = p_entry->GetInt(1);
		m_road_mask_tab[i][W] = p_entry->GetInt(2);
		m_road_mask_tab[i][L] = p_entry->GetInt(3);
		m_road_mask_tab[i][Y] = 0;
		m_road_mask_tab[i].MakeInfinitelyHigh();
	}

	m_road_mask_tab_size += 4;
}


void CParkManager::setup_lighting()
{
	
	
	Script::CArray *pThemeArray = Script::GetArray("Editor_Light_Info");
//	Script::CArray *pSizeArray = pThemeArray->GetArray(m_currentThemeIndex);
//	Script::CScriptStructure *pLightInfo = pSizeArray->GetStructure(m_currentShellSizeIndex);
	Script::CArray *pSizeArray = pThemeArray->GetArray(0);
	Script::CScriptStructure *pLightInfo = pSizeArray->GetStructure(0);

	float amb_const;
	pLightInfo->GetFloat("ambient_const", &amb_const, true);
	Mth::Vector ambient_rgb;
	pLightInfo->GetVector("ambient_rgb", &ambient_rgb, true);
	float falloff_const;
	pLightInfo->GetFloat("falloff_const", &falloff_const, true);
	Mth::Vector falloff_rgb;
	pLightInfo->GetVector("falloff_rgb", &falloff_rgb, true);
	float cursor_ambience;
	pLightInfo->GetFloat("cursor_ambience", &cursor_ambience, true);
	
	
	Script::CArray *pLightArray;
	pLightInfo->GetArray("pos", &pLightArray, true);

	mp_generator->SetLightProps(pLightArray->GetSize(), 
							amb_const * ambient_rgb.GetX(), amb_const * ambient_rgb.GetY(), amb_const * ambient_rgb.GetZ(), 
							falloff_const * falloff_rgb.GetX(), falloff_const * falloff_rgb.GetY(), falloff_const * falloff_rgb.GetZ(),
							cursor_ambience);

	for (int i = 0; i < (int)pLightArray->GetSize(); i++)
	{
		Script::CVector *pInPos = pLightArray->GetVector(i);
		Mth::Vector outPos;
		outPos.Set(pInPos->mX * CParkGenerator::CELL_WIDTH,
				   pInPos->mY * CParkGenerator::CELL_HEIGHT,
				   pInPos->mZ * CParkGenerator::CELL_LENGTH);
		mp_generator->SetLight(outPos, i);
	}
}




}


================================================
FILE: Code/Sk/ParkEditor2/EdMap.h
================================================
#ifndef __SK_PARKEDITOR2_EDMAP_H
#define __SK_PARKEDITOR2_EDMAP_H

#include 
#include 

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

//#ifdef	__PLAT_NGPS__
#define USE_BUILD_LIST
//#endif


namespace Script
{
	class CStruct;
	class CScript;
}


namespace Ed
{

class CPiece;
class CSourcePiece;
class CClonedPiece;
class CParkGenerator;
class CConcreteMetaPiece;
class CAbstractMetaPiece;

/*
	Describes how to incorporate a CPiece or hardcoded metapiece into a CMetaPiece.
	
	The position is relative to the NW corner of the piece, in its current rotation.
	
	Likewise, the rotation value includes the rotation of the whole metapiece.
*/
struct SMetaDescriptor
{
	uint8						mX;
	uint8						mY;
	uint8						mZ;
	uint8						mRot;
	uint8						mRiser; // true if a riser
	
	/* 
		The piece pointer is active within a concrete metapiece, the name in an abstract one.
		In the latter case, the name refers to a metapiece.
	*/
	union
	{
		uint32					mPieceName;
		CPiece *				mpPiece;
	};
};




/*
	What is a metapiece?
	
	-Wraps a group of cloned pieces that are in the park (or about to be)
	
	-Wraps a group of cloned pieces that make up the cursor. Contains extra position,
		rotation info.
	
	-Wraps a group of cloned pieces that make up a menu item. Refers to a 3D element.
		
	-Wraps a descriptor table that tells how to build a full metapiece.
	
	Rules:
	-If metapiece is in world, positions of contained pieces are in world
	-Otherwise, positions are relative to center bottom of metapiece
	
	Other notes:
	-An abstract user-created metapiece can contain hard-coded metapieces
	-A concrete metapiece cannot
*/
class CMetaPiece
{
	friend class CParkManager;

public:

	enum EFlags
	{
		mCONCRETE_META			= (1<<0),
		mABSTRACT_META			= (1<<1),

		mIN_PARK				= (1<<2),

		mIS_RISER				= (1<<3),
		mMARK_FOR_KILL			= (1<<4),
		mMARK_AS_SLID			= (1<<5),

		mUSER_CREATED			= (1<<6),
		mSINGULAR				= (1<<7),

		mINNER_SHELL			= (1<<8),

		mLOCK_AGAINST_DELETE	= (1<<9),
		mNO_RAILS				= (1<<10),

		mGAP_PLACEMENT			= (1<<11),
		mGAP_ATTACHED			= (1<<12),

		mNOT_ON_GC				= (1<<13), // this metapiece does not appear on Gamecube

		mCONTAINS_RISERS		= (1<<14),
		mAREA_SELECTION			= (1<<15),
		
		mRAIL_PLACEMENT			= (1<<16),
	};

	CConcreteMetaPiece *		CastToConcreteMeta();
	CAbstractMetaPiece *		CastToAbstractMeta();

	bool						IsRestartOrFlag();
	bool						IsCompletelyWithinArea(const GridDims &area);
	
	
	virtual CPiece *			GetContainedPiece(int index) = 0;

	void						SetRot(Mth::ERot90 rot);
	Mth::Vector 				GetRelativePosOfContainedPiece(int index);
	void 						BuildElement3dSectorsArray(Script::CArray *pArray);

	/*
		Returns cell dimensions occupied by metapiece (post-rotation). Coordinates in GridDims structure
		are not valid in an abstract metapiece, only in a concrete one that's in the park.
	*/
	const GridDims &			GetArea() {return m_cell_area;}
	uint32						GetNameChecksum() {return m_name_checksum;}
	// Returns rotation of whole metapiece
	Mth::ERot90					GetRot() {return Mth::ERot90(m_rot);}

	bool						IsInPark() {return m_flags & mIN_PARK;}
	// Returns true if the WHOLE metapiece is a riser
	bool 						IsRiser() {return m_flags & mIS_RISER;}
	bool						IsInnerShell() {return m_flags & mINNER_SHELL;}
	bool						IsSingular() {return m_flags & mSINGULAR;}
	bool 						NoRails() {return m_flags & mNO_RAILS;}
	bool						IsGapPlacement() {return m_flags & mGAP_PLACEMENT;}
	bool						IsAreaSelection() {return m_flags & mAREA_SELECTION;}
	bool						IsRailPlacement() {return m_flags & mRAIL_PLACEMENT;}
	bool 						IsConcrete() {return m_flags & mCONCRETE_META;}

	void						MarkAsInnerShell() {m_flags = EFlags(m_flags | mINNER_SHELL);}
	void						MarkLocked() {m_flags = EFlags(m_flags | mLOCK_AGAINST_DELETE);}
	void						MarkUnlocked() {m_flags = EFlags(m_flags & ~mLOCK_AGAINST_DELETE);}
	

protected:
								CMetaPiece();
	virtual						~CMetaPiece();

	void						initialize_desc_table(int numEntries);
	SMetaDescriptor &			get_desc_at_index(int i);

	/*
		Adds a CPiece to the metapiece. If the metapiece is abstract, the CPiece must be a CSourcePiece. If the metapiece is
		concrete, the CPiece must be a CClonedPiece. 
		
		pPos specifies the position of the pPiece relative to the northwest corner of the metapiece.
		
		isRiser set if pPiece is meant to act as a riser.
	*/
	virtual	void				add_piece(CPiece *pPiece, GridDims *pPos, Mth::ERot90 rot = Mth::ROT_0, bool isRiser = false) = 0;
	
	void						set_flag(EFlags flag) {m_flags = EFlags(m_flags | flag);}
	void						clear_flag(EFlags flag) {m_flags = EFlags(m_flags & ~flag);}
	
	// takes into account current rotation
	GridDims					m_cell_area;			//  6 ( 8) + 2
									
	EFlags 						m_flags;   				// 	4 (only uses 17 bits, grr)
	int							m_rot;					//  4 (only uses  2 bits, grr)   +4 (if combined)
	
	/* 
		-See comment above SMetaDescriptor declaration for details on what descriptors are
		-Most metapieces will only contain one piece, so mp_additional_desc_tab is only used if there are more
	*/

	enum
	{
		USUAL_NUM_DESCRIPTORS =	2
	};
	
	SMetaDescriptor 			m_first_desc_tab[USUAL_NUM_DESCRIPTORS];  //  9 (12), + 9 (12)	  + 6
	SMetaDescriptor *			mp_additional_desc_tab;	  				  //  4	
	uint						m_total_entries;						  //  2 (4) 			  + 2
	uint 						m_num_used_entries;						  //  2 (4) 			  +2
	
	uint32						m_name_checksum; 						  //  4
};

// Total 													



class CConcreteMetaPiece : public CMetaPiece
{
	friend class CParkManager;

public:
	
	int							CountContainedPieces();
	CPiece *					GetContainedPiece(int index);

	void						SetSoftRot(Mth::Vector pos, float rot);

	void						Highlight(bool on, bool gapHighlight = false);
	void						SetVisibility(bool visible);

protected:
	
								CConcreteMetaPiece();
	
	void						lock();
	void						get_max_min_world_coords(Mth::Vector &max, Mth::Vector &min);
	
	bool 						test_for_conflict(GridDims area, bool excludeRisers);
	
	// see comments with original declaration
	virtual void				add_piece(CPiece *pPiece, GridDims *pPos, Mth::ERot90 rot = Mth::ROT_0, bool isRiser = false);
};




class CAbstractMetaPiece : public CMetaPiece
{
	friend class CParkManager;

public:
protected:
								CAbstractMetaPiece();

	CPiece *					GetContainedPiece(int index);
	
	// see comments with original declaration
	virtual void				add_piece(CPiece *pPiece, GridDims *pPos, Mth::ERot90 rot = Mth::ROT_0, bool isRiser = false);
	void 						add_piece_dumb(CAbstractMetaPiece *pMeta, GridDims *pPos, Mth::ERot90 rot, bool isRiser);

	SMetaDescriptor 			get_desc_with_expansion(int index);
	int 						count_descriptors_expanded();
};




class CMapListNode : public Mem::CPoolable
{
	friend class CParkManager;
	friend class CCursor;

public:
	CMetaPiece *				GetMeta();
	CConcreteMetaPiece *		GetConcreteMeta() {return GetMeta()->CastToConcreteMeta();}
	CAbstractMetaPiece *		GetAbstractMeta() {return GetMeta()->CastToAbstractMeta();}
	CMapListNode *				GetNext() {return mp_next;}
	void						DestroyList();
	void						VerifyIntegrity();

	int							mSlideAmount;

private:
	CMetaPiece *				mp_meta;
	CMapListNode *				mp_next;
};




class CMapListTemp
{
public:
	CMapListTemp(CMapListNode *p_list);
	~CMapListTemp();
	
	bool						IsEmpty() {return (mp_list == NULL);}
	bool						IsSingular() {return (mp_list->GetNext() == NULL);}
	CMapListNode *				GetList();
	void						PrintContents();

private:

	CMapListNode *				mp_list;
};




class CParkManager
{
	DeclareSingletonClass( CParkManager );

public:

	enum
	{
		MAX_WIDTH =				56,
		MAX_LENGTH =			56,
		MAX_HEIGHT =			16,
		/* 
			These next two account for the border around the whole park. Riser pieces must be added when the user
			digs a hole at the edge of the park.
		*/
		FLOOR_MAX_WIDTH =		MAX_WIDTH + 2,
		FLOOR_MAX_LENGTH =		MAX_LENGTH + 2,

		COMPRESSED_MAP_SIZE		= 15000,		// (Mick) it can acutally be bigger than this, but I've made it this size so network code won't crash
	};

								CParkManager();
								~CParkManager();

	/* ======== General Functions ======== */

	CParkGenerator *			GetGenerator() {return mp_generator;}								
	void						Initialize();
	bool						IsInitialized() {return m_state_on;}
	void						AccessDisk(bool save, int fileSlot);
	void						Destroy(CParkGenerator::EDestroyType type);

	float						GetClipboardProportionUsed();
	void						WriteCompressedMapBuffer();
	uint8*						GetCompressedMapBuffer(bool markSaved = false);
	void						SetCompressedMapBuffer(uint8* src_buffer, bool markNotSaved = false);
	bool						IsMapSavedLocally();
	void						WriteIntoStructure(Script::CStruct *p_struct);
	#ifdef __PLAT_NGC__
	void 						ReadFromStructure(Script::CStruct *p_struct, bool do_post_mem_card_load=true, bool preMadePark=false);
	#else
	void						ReadFromStructure(Script::CStruct *p_struct, bool do_post_mem_card_load=true);
	#endif
	
	int							GetTheme();
	void						SetTheme(int theme);
	uint32						GetShellSceneID();
	const char *				GetParkName();
	void						SetParkName(const char *pName);
	uint32						GetParkChecksum();
	int							GetCompressedParkWidth();
	int							GetCompressedParkLength();

	/* ======== Rebuild functions ======== */

	void						RebuildWholePark(bool clearPark);	
	bool						RebuildFloor(bool firstTestMemory = false);
	void						RebuildNodeArray();
	void						RebuildInnerShell(GridDims newNearBounds);
	
	/* ======== Abstract metapiece functions ======== */
	
	CAbstractMetaPiece *		GetAbstractMeta(uint32 type, int *pRetIndex = NULL);
	CAbstractMetaPiece *		GetAbstractMetaByIndex(int index);
	
	/* ======== Concrete metapiece functions ======== */
	
	enum EDestroyFlags
	{
		mDONT_DESTROY_PIECES_ABOVE		= 0,
		mDESTROY_PIECES_ABOVE			= (1<<0),
		mEXCLUDE_RISERS					= (1<<1),
		mEXCLUDE_PIECES_MARKED_AS_SLID	= (1<<2),
	};
	
	CConcreteMetaPiece *		CreateConcreteMeta(CAbstractMetaPiece *pSource, bool isSoft = false);
	void						DestroyConcreteMeta(CConcreteMetaPiece *pMeta, EDestroyFlags flags);
	uint32						DestroyMetasInArea(GridDims area, EDestroyFlags flags);
	bool						DestroyMetasInArea(GridDims area, bool doNotDestroyRestartsOrFlags=false, bool onlyIfWithinArea=false);
	void						HighlightMetasInArea(GridDims area, bool on);
	//GridDims					FindBoundingArea(GridDims area);
	void						AddMetaPieceToPark(GridDims pos, CConcreteMetaPiece *pPiece);
	CConcreteMetaPiece *		RelocateMetaPiece(CConcreteMetaPiece *pMeta, int changeX, int changeY, int changeZ);
	CMapListNode *				GetMetaPiecesAt(GridDims dims, Mth::ERot90 rot = Mth::ROT_0, CAbstractMetaPiece *pPiece = NULL, bool excludeRisers = false);
	CMapListNode *				GetAreaMetaPieces(GridDims dims);
	CMapListNode *				GetMapListNode(int x, int z);
	CMapListNode *				GetConcreteMetaList() {return mp_concrete_metapiece_list;}
	int 						GetFloorHeight(int x, int z);
	
	
	bool 						AreMetasOutsideBounds(GridDims new_dims);
	bool 						EnoughMemoryToResize(GridDims new_dims);

	int 						CountGeneralMetas() {return m_num_general_concrete_metapieces;}
	int							GetDMAPieceCount() {return m_dma_piece_count;}
	
	
	/* ======== Floor height functions ======== */
	
	int							GetFloorHeight(GridDims dims, bool *pRetUniformHeight = NULL);
	#if 0
	void						SetFloorHeight(GridDims area, int newHeight, bool justDropHighest);
	#endif
	void						ResetFloorHeights(GridDims area);
	
	bool						SlideColumn(GridDims area, int bottom, int top, bool up, bool uneven);
	void						UndoSlideColumn();
	bool						ChangeFloorHeight(GridDims dims, int dir);

	/* ======== Coordinate info functions ======== */
	
	const GridDims &			GetParkNearBounds() {return m_park_near_bounds;}
	const GridDims &			GetParkFarBounds() {return m_park_far_bounds;}
	Mth::Vector					GridCoordinatesToWorld(const GridDims &cellDims, Mth::Vector *pRetDims = NULL);

	static CParkManager *		sInstance();
	
	/* ======== Piece set (group, category) functions ======== */
	
	class CPieceEntry
	{
	public:
		uint32									mNameCrc;
		const char *							mpName;
	};
	
	class CPieceSet
	{
	public:
		enum 
		{
			MAX_ENTRIES =						40,
		};
		
		uint32									mNameCrc;
		const char *							mpName;
		CPieceEntry 							mEntryTab[MAX_ENTRIES];
		int										mTotalEntries;
		int										mSelectedEntry;
		char									mVisible;
		char									mIsClipboardSet;
	};

	enum
	{
		MAX_SETS =								40,
	};

	CPieceSet &					GetPieceSet(int setNumber, int *pMenuSetNumber = NULL);
	int							GetTotalNumPieceSets() {return m_total_sets;}

	/* ======== Road mask functions ======== */
	
	bool						IsOccupiedByRoad(GridDims area);
	bool						CanPlacePiecesIn(GridDims cellDims, bool ignoreFloorHeight=false);


	/* ======== Restart node functions ===== */

	void						SetRestartTypeId(CParkGenerator::RestartType type, uint32 id);
	CParkGenerator::RestartType	IsRestartPiece(uint32 id);

	/* ======== Gap functions ===== */
	
	CGapManager::GapDescriptor *GetGapDescriptor(GridDims &area, int *pHalf);
	bool 						AddGap(CGapManager::GapDescriptor &descriptor);
	void 						RemoveGap(CGapManager::GapDescriptor &descriptor);
	void						RemoveGap(CConcreteMetaPiece *pMeta);
	void						HighlightMetasWithGaps(bool highlightOn, CGapManager::GapDescriptor *pDesc);

	//void						FreeUpMemoryForPlayingPark();
	
	void						GetSummaryInfoFromBuffer(uint8 *p_buffer, int *p_numGaps, int *p_numMetas, uint16 *p_theme, uint32 *p_todScript, int *p_width, int *p_length);
	
	#ifdef __PLAT_NGC__
	void						SwapMapBufferEndianness();
	#endif
	
protected:

	/* ======== Key members ======== */
	
	CParkGenerator *			mp_generator;
	bool						m_state_on;
	bool						m_park_is_valid;

	/* ======== Riser/floor related functions ======== */
	
	enum EFloorFlags
	{
		NONE =					0,
		mHAS_BOTTOM =			(1<<0),
		mHAS_TOP =				(1<<1),
		mNO_COVER =				(1<<2),
	};
	
	struct BuildFloorParams
	{
		/* for build_add_floor_piece() */
		int 					x, y, z;
		EFloorFlags 			flags;
		int 					heightSlotOffset;
		/* for output_riser_stack() */
		int						bottom;
		int						top;
		bool					simpleBuild;
		bool					dryRun;
		int						addCount; 	// counts metapieces
		int						addCount2;	// counts contained pieces
	};

	void						output_riser_stack(BuildFloorParams ¶ms);
	void						build_add_floor_piece(BuildFloorParams ¶ms);
	void						build_add_floor_piece_simple(BuildFloorParams ¶ms);
	void						apply_meta_contained_risers_to_floor(CConcreteMetaPiece *pMetaPiece, bool add);
	
	CMapListNode *				mp_column_slide_list;
	int							m_num_metas_killed_in_slide;
	int							m_num_pieces_killed_in_slide;
		
	enum ESlideColumnFlags
	{
		mUNEVEN =				(1<<0),
		mUP	=					(1<<1),
		mFIRST_RECURSE =		(1<<2),
	};
	bool 						intersection_with_riser_containing_metas(GridDims area);
	bool						slide_column_part_one(GridDims area, int bottom, int top, ESlideColumnFlags flags, CMapListNode **ppMoveList);
	void						fix_slide_column_part_one_failure();
	void						slide_column_part_two(CMapListNode *pMoveList);
	void						kill_pieces_in_layer_under_area(GridDims area);
	void						finish_slide_column();
	bool						generate_floor_pieces_from_height_map(bool simpleBuild, bool dryRun);

	void						increment_meta_count(uint32 pieceChecksum);
	void						decrement_meta_count(uint32 pieceChecksum);
	int							get_dma_usage(uint32 pieceChecksum);

	enum
	{
		NUM_RISER_INFO_SLOTS = 	8,
	};
	
	uint32						m_riser_piece_checksum[NUM_RISER_INFO_SLOTS][3];

	#ifdef USE_BUILD_LIST
	struct SBuildListEntry
	{
		uint32 mType;
		GridDims mPos;
	};	
	int							m_build_list_size;
	SBuildListEntry				*mp_build_list_entry;
	void						create_metas_in_build_list();
	#endif
	
	/* ======== metapiece management stuff ======== */
	
	CMapListNode *				mp_concrete_metapiece_list;
	CMapListNode *				mp_abstract_metapiece_list;
	int 						m_num_concrete_metapieces;
	int							m_num_general_concrete_metapieces; // concrete metapieces not including riser, shell metapieces. Must be in park.
	int 						m_num_abstract_metapieces;
	// The DMA piece count counts pieces weighted according to their contribution to DMA usage.
	// Some pieces contribute more than 1, eg the lava piece, and any piece that has wibbling.
	// Needed so that the gauge can take into account DMA usage and not allow DMA overflow.
	int							m_dma_piece_count;
	CMapListNode * 				mp_bucket_list[FLOOR_MAX_WIDTH][FLOOR_MAX_LENGTH];

	void 						add_metapiece_to_node_list(CMetaPiece *pPieceToAdd, CMapListNode **ppList);
	void 						remove_metapiece_from_node_list(CMetaPiece *pPieceToRemove, CMapListNode **ppList);
	void						bucketify_metapiece(CConcreteMetaPiece *pPiece);
	void						debucketify_metapiece(CConcreteMetaPiece *pPiece);
		
	void						create_abstract_metapieces();
	void						destroy_concrete_metapieces(CParkGenerator::EDestroyType type);

	/* ======== Floor map/park bounds stuff ======== */
	
	int							m_theme;
	
	/*
		Within the 256X256 "super map"
		
		X and Z are positive -- must subtract 128 to get true world cell coordinates
	*/
	GridDims					m_park_near_bounds;
	GridDims					m_park_far_bounds;

	enum
	{
		TYPE_EMPTY_BLOCK		= 0,
		TYPE_FLOOR_BLOCK		= 0xaec511eb,
		TYPE_FLOOR_WALL_BLOCK	= 0x829cfc8d,
		TYPE_WALL_BLOCK			= 0x304bd5f7,
	};
	
	struct FloorHeightEntry
	{
		int						mHeight;
		/*
			Bit 0 = -16Y, 31 = 15Y
			
			Each bit set true of corresponding riser is part of metapiece at that position.
		*/
		uint32					mEnclosedFloor;
		bool					mMarkAsSlid;
		int						mSlideAmount;
	};

	FloorHeightEntry **			m_floor_height_map;

	/* ======== piece set stuff ======== */
	
	CPieceSet					m_palette_set[MAX_SETS];
	int							m_total_sets;

	/* ======== map buffer access stuff ======== */
	
	struct CompressedMapHeader
	{
		uint32					mChecksum; // CRC of every byte in file except these 4
		uint16					mSizeInBytes;
		uint16					mVersion;
		uint16					mTheme;
		uint16					mParkSize;
		uint8 					mX;
		uint8					mZ;
		uint8					mW;
		uint8					mL;
		uint16					mNumMetas;
		uint16					mNumGaps;
		uint8					mMaxPlayers;
		uint32					mTODScript;
		char					mParkName[64];
	};

	struct CompressedMapHeaderOld
	{
		uint32					mChecksum; // CRC of every byte in file except these 4
		uint16					mSizeInBytes;
		uint16					mVersion;
		uint16					mTheme;
		uint16					mParkSize;
		uint8 					mX;
		uint8					mZ;
		uint8					mW;
		uint8					mL;
		uint16					mNumMetas;
		uint16					mNumGaps;
		char					mParkName[64];
	};

	struct CompressedFloorEntry
	{
		sint8					mHeight;
	};

	// K: To allow loading of older parks that do not have gap cancel-flag info 
	struct CompressedGapOld
	{
		uint8					x[2];
		uint8					y[2];
		uint8					z[2];
		// bits 0-3 left extent, 4-7 right
		uint8					extent[2];
		// bits 0-3 first rot, 4-7 second
		uint8					rot;
		uint8					flags;
		char					text[32];
		uint16					score;
	};
	
	struct CompressedGap
	{
		uint8					x[2];
		uint8					y[2];
		uint8					z[2];
		// bits 0-3 left extent, 4-7 right
		uint8					extent[2];
		// bits 0-3 first rot, 4-7 second
		uint8					rot;
		uint8					flags;
		char					text[32];
		uint16					score;
		
		uint32					mCancelFlags;
	};
	
	enum
	{   
		VERSION					= 20004,
	};
	uint8 *						mp_compressed_map_buffer;
	
	enum ECompressedMapFlags
	{
		mNO_FLAGS				= 0,
		mIN_SYNC_WITH_PARK		= (1<<0),	// cleared if park hasn't been built yet
		mIS_VALID				= (1<<1),
		mIS_NEWER_THAN_PARK		= (1<<2),	// also set if park hasn't been built yet

		mNOT_SAVED_LOCAL		= (1<<3),
	};
	ECompressedMapFlags			m_compressed_map_flags;

	void						fake_compressed_map_buffer();
	void						write_compressed_map_buffer();
	void						read_from_compressed_map_buffer();
	void 						read_from_compressed_map_buffer_old();

	void						setup_default_dimensions();
	
	char						mp_park_name[64];
	
	/* ======== Road mask section ======== */

	enum
	{
		NUM_ROAD_PIECES			= 8,
	};
	GridDims					m_road_mask_tab[NUM_ROAD_PIECES];
	uint						m_road_mask_tab_size;

	void						setup_road_mask();

	/* ======== Restart Node section ======= */

	uint32						m_restartPieceIdTab[CParkGenerator::vNUM_RESTART_TYPES];	 // Table of checksums of the names of restart pieces


	/* ======== Lights Section ============= */
	void						setup_lighting();


	/* ======== Gap Section ============= */
	
	CGapManager	*				mp_gap_manager;
	
	static CParkManager *		sp_instance;
};




}

#endif // __SK_PARKEDITOR2_EDMAP_H



================================================
FILE: Code/Sk/ParkEditor2/GapManager.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


namespace Ed
{




CGapManager *CGapManager::sp_instance = NULL;


GapInfo::GapInfo()
{
	mp_gapPiece[0] 		= NULL;
	mp_gapPiece[1] 		= NULL;
	mp_regularMeta[0] 	= NULL;
	mp_regularMeta[1] 	= NULL;
	
	m_descriptor.mCancelFlags=0;
}




CGapManager::CGapManager(CParkManager *pManager)
{
	m_currentId = 2000;

	for (int i = 0; i < vMAX_GAPS; i++)
		mp_gapInfoTab[i] = NULL;

	mp_manager = pManager;
	SetInstance();
}




CGapManager::~CGapManager()
{
	// XXX
	Ryan("in ~CGapManager()\n");
	RemoveAllGaps();
	// XXX
	Ryan("leaving ~CGapManager()\n");
	sp_instance = NULL;
}

CGapManager& CGapManager::operator=( const CGapManager& rhs )
{
	m_currentId = rhs.m_currentId;
	
	for (int i = 0; i < vMAX_GAPS; i++)
	{
		if (rhs.mp_gapInfoTab[i])
		{
			GapInfo *p_new = new GapInfo;
			
			p_new->m_id[0] = rhs.mp_gapInfoTab[i]->m_id[0];
			p_new->m_id[1] = rhs.mp_gapInfoTab[i]->m_id[1];
			p_new->mp_gapPiece[0] = NULL;
			p_new->mp_gapPiece[1] = NULL;
			p_new->mp_regularMeta[0] = NULL;
			p_new->mp_regularMeta[1] = NULL;
			Dbg_MsgAssert(strlen(rhs.mp_gapInfoTab[i]->mp_text) < 128,("Bad source gap text"));
			strcpy(p_new->mp_text, rhs.mp_gapInfoTab[i]->mp_text);
			p_new->m_descriptor = rhs.mp_gapInfoTab[i]->m_descriptor;
			
			mp_gapInfoTab[i] = p_new;
		}
		else
		{
			mp_gapInfoTab[i]=NULL;
		}	
		
		m_startOfGap[i] = rhs.m_startOfGap[i];
	}
	
	mp_manager=NULL;
	
	return *this;
}



int CGapManager::GetFreeGapIndex()
{
	for (int i = 0; i < vMAX_GAPS; i++)
		if (!mp_gapInfoTab[i])
			return i;
	return -1;
}




void CGapManager::StartGap(CClonedPiece *pGapPiece, CConcreteMetaPiece *pRegularMeta, int tab_index)
{
	
	Dbg_Assert(pGapPiece);
	Dbg_Assert(pRegularMeta);

	//Ryan("Starting gap 0x%x\n", m_currentId);
	
	// find empty slot
	// BAD MEM
	mp_gapInfoTab[tab_index] = new GapInfo();
	mp_gapInfoTab[tab_index]->m_id[0] = m_currentId++;
	mp_gapInfoTab[tab_index]->mp_gapPiece[0] = pGapPiece;
	//mp_gapInfoTab[tab_index]->mp_gapPiece[0]->MarkAsGapInProgress();
	mp_gapInfoTab[tab_index]->mp_regularMeta[0] = pRegularMeta;
	mp_gapInfoTab[tab_index]->m_descriptor.tabIndex = tab_index;
	m_startOfGap[tab_index] = true;
}




void CGapManager::EndGap(CClonedPiece *pGapPiece, CConcreteMetaPiece *pRegularMeta, int tab_index)
{
	
	Dbg_Assert(pGapPiece);
	Dbg_Assert(pRegularMeta);
	Dbg_MsgAssert(tab_index >= 0 && tab_index < vMAX_GAPS, ("invalid tab_index"));

	GapInfo *pStartInfo = mp_gapInfoTab[tab_index];
	Dbg_Assert(pStartInfo);
	
	//Ryan("Ending gap 0x%x\n", m_currentId);
	
	// find empty slot
	for (int i = 0; i < vMAX_GAPS; i++)
		if (!mp_gapInfoTab[i])
		{
			mp_gapInfoTab[i] = pStartInfo;
			mp_gapInfoTab[i]->m_id[1] = m_currentId++;
			mp_gapInfoTab[i]->mp_gapPiece[1] = pGapPiece;
			//mp_gapInfoTab[i]->mp_gapPiece[0]->UnmarkAsGapInProgress();
			//mp_gapInfoTab[i]->mp_gapPiece[1]->UnmarkAsGapInProgress();
			mp_gapInfoTab[i]->mp_regularMeta[1] = pRegularMeta;
			mp_gapInfoTab[i]->m_descriptor.tabIndex = tab_index;
			m_startOfGap[i] = false;
			return;
		}

	Dbg_MsgAssert(0, ("out of gap slots"));
	return;
}

// K: Called when CParkEditor::SetState switches to test play or regular play.
// This is necessary to make the gaps appear in the view-gaps menu, which lists those
// in the skater career.
void CGapManager::RegisterGapsWithSkaterCareer()
{
	Obj::CGapChecklist	*p_gap_checklist=Mdl::Skate::Instance()->GetCareer()->GetGapChecklist();
	if (!p_gap_checklist)
	{
		return;
	}

	p_gap_checklist->FlushGapChecks();

	for (int i = 0; i < vMAX_GAPS; i++)
	{
		if (mp_gapInfoTab[i])
		{
			p_gap_checklist->AddGapCheck(mp_gapInfoTab[i]->m_descriptor.text, 0,
										 mp_gapInfoTab[i]->m_descriptor.score);
		}
	}	
}

void CGapManager::SetGapInfo(int tab_index, GapDescriptor &descriptor)
{
	
	Dbg_MsgAssert(tab_index >= 0 && tab_index < vMAX_GAPS, ("invalid tab_index"));
	Dbg_Assert(mp_gapInfoTab[tab_index]);
	Dbg_Assert(descriptor.tabIndex >= 0 && descriptor.tabIndex < vMAX_GAPS);

	sprintf(mp_gapInfoTab[tab_index]->mp_text, "%s", descriptor.text);
	mp_gapInfoTab[tab_index]->m_descriptor = descriptor;

	RegisterGapsWithSkaterCareer();
	//mp_gapInfoTab[tab_index]->mp_gapPiece[0]->MakeTriggerable(true);
	//mp_gapInfoTab[tab_index]->mp_gapPiece[1]->MakeTriggerable(true);
}




bool CGapManager::IsGapAttached(const CConcreteMetaPiece *pRegularMeta, int *pTabIndex)
{
	for (int i = 0; i < vMAX_GAPS; i++)
		if (mp_gapInfoTab[i])
		{
			int half_num = (m_startOfGap[i]) ? 0 : 1;
			#if 0
			if (i == 16 || i == 17)
				Ryan("           testing gap index %d, pMeta[0]=%x, pMeta[1]=%x, half=%d, test_meta=%p\n", 
					 i,
					 mp_gapInfoTab[i]->mp_regularMeta[0],
					 mp_gapInfoTab[i]->mp_regularMeta[1], 
					 half_num, pRegularMeta);  
			#endif
			if (mp_gapInfoTab[i]->mp_regularMeta[half_num] == pRegularMeta)
			{
				#if 0
				Ryan("     found gap at table index %d, position=(%d,%d,%d), half=%d, pMeta=%p\n", 
					 i, 
					 mp_gapInfoTab[i]->m_descriptor.loc[half_num].GetX(), 
					 mp_gapInfoTab[i]->m_descriptor.loc[half_num].GetY(),
					 mp_gapInfoTab[i]->m_descriptor.loc[half_num].GetZ(),
					 half_num,
					 pRegularMeta);
				#endif
				if (pTabIndex)
					*pTabIndex = i;
				return true;
			}
		}
	return false;
}




void CGapManager::RemoveGap(int tab_index, bool unregisterGapFromGoalEditor)
{
	
	Dbg_MsgAssert(tab_index >= 0 && tab_index < vMAX_GAPS, ("invalid tab_index"));

	GapInfo *pRemoveInfo = mp_gapInfoTab[tab_index];
	Dbg_Assert(pRemoveInfo);
	
	
	for (int gp = 0; gp < 2; gp++)
	{
		if (pRemoveInfo->mp_gapPiece[gp])
			mp_manager->GetGenerator()->DestroyClonedPiece(pRemoveInfo->mp_gapPiece[gp]);
	}

	// Note: Need to check mp_manager cos it could be NULL in the case of this being
	// the CParkEditor's mp_play_mode_gap_manager. (Fixes TT12087)
	if (unregisterGapFromGoalEditor)
	{
		// Determine the gap number of the gap as stored in the career gap check list	
		int gap_number=Mdl::Skate::Instance()->GetGapChecklist()->GetGapChecklistIndex(pRemoveInfo->m_descriptor.text);
		if (gap_number >= 0)
		{
			Obj::GetGoalEditor()->RefreshGapGoalsAfterGapRemovedFromPark(gap_number);
		}	
	}
		
	for (int i = 0; i < vMAX_GAPS; i++)
	{
		if (mp_gapInfoTab[i] == pRemoveInfo)
		{
			mp_gapInfoTab[i] = NULL;
		}
	}
	delete pRemoveInfo;
}




void CGapManager::RemoveAllGaps()
{
	for (int i = 0; i < vMAX_GAPS; i++)
	{
		if (mp_gapInfoTab[i])
		{
			// Normally, when a gap piece is deleted such as when editing a park, it gets unregistered
			// from the goal editor.
			// However, when cleaning up the park, we don't want to do this.
			// The false means do not unregister the gap from the goal editor.
			RemoveGap(i, false);
		}
	}	
}




uint32 CGapManager::GetGapTriggerScript(CClonedPiece *pGapPiece)
{
	for (int i = 0; i < vMAX_GAPS; i++)
		if (mp_gapInfoTab[i])
		{
			int half_num = (m_startOfGap[i]) ? 0 : 1;
			if (mp_gapInfoTab[i]->mp_gapPiece[half_num] == pGapPiece)
			{
				if (i&1  && Script::GetInt("PrintGaps",false))
				{
					printf ("Editor Gap %d points: %s\n",mp_gapInfoTab[i]->m_descriptor.score, mp_gapInfoTab[i]->m_descriptor.text);  
				}
				
				char gap_trigger_script[48];
				sprintf(gap_trigger_script, "EditorTrgGap%d", i);
				return Script::GenerateCRC(gap_trigger_script);
			}
		}

	Dbg_MsgAssert(0, ("no gap trigger script"));
	return 0;
}



	
CGapManager::GapDescriptor *CGapManager::GetGapDescriptor(int tab_index, int *pHalfNum)
{
	
	if (!mp_gapInfoTab[tab_index])
		return NULL;
	
	*pHalfNum = (m_startOfGap[tab_index]) ? 0 : 1;
	strcpy(mp_gapInfoTab[tab_index]->m_descriptor.text, mp_gapInfoTab[tab_index]->mp_text);
	return &mp_gapInfoTab[tab_index]->m_descriptor;
}




void CGapManager::MakeGapWireframe(int tab_index)
{
	
	Dbg_MsgAssert(tab_index >= 0 && tab_index < vMAX_GAPS, ("invalid tab_index %d", tab_index));

	if (!mp_gapInfoTab[tab_index])
		return;

	if (mp_gapInfoTab[tab_index]->mp_gapPiece[0])
		mp_gapInfoTab[tab_index]->mp_gapPiece[0]->SetVisibility(true);
	if (mp_gapInfoTab[tab_index]->mp_gapPiece[1])
		mp_gapInfoTab[tab_index]->mp_gapPiece[1]->SetVisibility(true);
}




void CGapManager::LaunchGap(int tab_index, Script::CScript *pScript)
{
#if 1	
	Dbg_MsgAssert(tab_index >= 0 && tab_index < vMAX_GAPS, ("invalid tab_index"));
	Dbg_Assert(mp_gapInfoTab[tab_index]);
	Dbg_Assert(pScript);
	Dbg_Assert(pScript->mpObject);
		
	for (int i = 0; i < 2; i++)
	{
		int half_num = (m_startOfGap[tab_index]) ? i : 1 - i;

		Script::CStruct *pGapParms = new Script::CStruct();
		pGapParms->AddChecksum(CRCD(0x3b442e26,"GapID"), mp_gapInfoTab[tab_index]->m_id[half_num]);
		pGapParms->AddInteger(CRCD(0xcd66c8ae,"score"), mp_gapInfoTab[tab_index]->m_descriptor.score);
		pGapParms->AddString(CRCD(0xc4745838,"text"), mp_gapInfoTab[tab_index]->mp_text);
		//pGapParms->AddComponent(Script::GenerateCRC("text"), ESYMBOLTYPE_STRING, "boogah");
		
		if (i == 0)
		{
			uint32 cancel_flags=mp_gapInfoTab[tab_index]->m_descriptor.mCancelFlags;
			if (!cancel_flags)
			{
				// If no flags are set for some reason, use the default of just CANCEL_GROUND
				cancel_flags=Script::GetInteger(CRCD(0x9a27e74d,"CANCEL_GROUND"));
			}
			pGapParms->AddChecksum(CRCD(0x2760de9e,"combined_flags"),cancel_flags);
		
			pScript->mpObject->CallMemberFunction(Script::GenerateCRC("StartGap"), pGapParms, pScript);
		}	
		else
		{
			pScript->mpObject->CallMemberFunction(Script::GenerateCRC("EndGap"), pGapParms, pScript);
		}
		
		delete pGapParms;	
	}
#endif
}




CGapManager *CGapManager::sInstance()
{
	Dbg_Assert(sp_instance);
	return sp_instance;
}




}







================================================
FILE: Code/Sk/ParkEditor2/GapManager.h
================================================
#ifndef __SK_PARKEDITOR2_GAPMANAGER_H
#define __SK_PARKEDITOR2_GAPMANAGER_H

#include 

namespace Script
{
	class CStruct;
	class CScript;
}


namespace Ed
{

class CGapManager;
class GapInfo;
class CParkManager;

class  CGapManager
{
	
	
public:

	struct GapDescriptor
	{
		GridDims				loc[2];
		int						rot[2];
		int 					leftExtent[2];
		int						rightExtent[2];
		char 					text[32];
		int						score;
		int						numCompleteHalves; // will be 0, 1, or 2
		int						tabIndex; // set to -1 if not in table
		
		// An 'or' of the CANCEL_ values as defined in skutils.q, eg CANCEL_GROUND
		uint32					mCancelFlags;
	};
	
	// actually max gap HALVES, divide by two to get gaps
	static const int			vMAX_GAPS = 32;

								CGapManager(CParkManager *pManager);
								~CGapManager();

	int 						GetFreeGapIndex();
	void						StartGap(CClonedPiece *pGapPiece, CConcreteMetaPiece *pRegularMeta, int tab_index);
	void						EndGap(CClonedPiece *pGapPiece, CConcreteMetaPiece *pRegularMeta, int tab_index);
	void						SetGapInfo(int tab_index, GapDescriptor &descriptor);
	bool						IsGapAttached(const CConcreteMetaPiece *pRegularMeta, int *pTabIndex);
	void						RemoveGap(int tab_index, bool unregisterGapFromGoalEditor=true);
	void						RemoveAllGaps();
	void						RegisterGapsWithSkaterCareer();

	uint32						GetGapTriggerScript(CClonedPiece *pGapPiece);
	GapDescriptor *				GetGapDescriptor(int tab_index, int *pHalfNum);
	void						MakeGapWireframe(int tab_index);
	
	void						LaunchGap(int tab_index, Script::CScript *pScript);

	static CGapManager *		sInstance();
	void						SetInstance() {sp_instance=this;}

	static CGapManager *		sInstanceNoAssert() {return sp_instance;}

	CGapManager& operator=( const CGapManager& rhs );
	
private:

	uint32						m_currentId;

	GapInfo *					mp_gapInfoTab[vMAX_GAPS];
	bool						m_startOfGap[vMAX_GAPS];

	CParkManager				*mp_manager;
	
	static CGapManager *		sp_instance;
};




class  GapInfo  : public Spt::Class
{
	
	friend class CGapManager;

private:

	GapInfo();

	uint32						m_id[2];
	CClonedPiece *				mp_gapPiece[2];
	CConcreteMetaPiece *		mp_regularMeta[2];
	char						mp_text[128];

	CGapManager::GapDescriptor	m_descriptor;
};




}

#endif



================================================
FILE: Code/Sk/ParkEditor2/ParkEd.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 

//#define DONT_USE_CURSOR_META

namespace Ed
{




const float CParkEditor::vMAX_CAM_DIST				= 1500.0f;
const float CParkEditor::vMIN_CAM_DIST				= 500.0f;
const float CParkEditor::vCAM_DIST_INC				= 16.0f;
const float CParkEditor::vCAM_TARGET_ELEVATION		= 300.0f;

DefineSingletonClass( CParkEditor, "Park editor module" );




CParkEditor::CParkEditor() :
	mp_park_manager(true)
{
	m_logic_task 	= new Tsk::Task< CParkEditor > ( CParkEditor::s_logic_code, *this );
	m_display_task 	= new Tsk::Task< CParkEditor > ( CParkEditor::s_display_code, *this, Tsk::BaseTask::Node::vDISPLAY_TASK_PRIORITY_CPARKEDITOR_DISPLAY );
	m_input_handler = new Inp::Handler< CParkEditor > ( 0,  CParkEditor::s_input_logic_code, *this);

	m_movement_vel = 480.0f; //8.0f;		// default movement velocity in inches per second
	m_rotate_vel = 120.0f;		// 120 degrees per second. In 3 seconds you should complete 1 revolution

	mp_camera = NULL;
	m_camDist = 1400.0f;
	m_camAngle = 0.0f; //225.0f;
	m_camAngleVert = 10.0f;

	m_cursor_state = vMOVEMENT;
	m_state = vINACTIVE;
	m_paused = false;

	mp_cursor = NULL;
	mp_menu_manager = NULL;
	
	m_pct_resources_used = 1.0f;
	m_last_main_heap_free=0;
	
	m_tod_script=0;

	mp_play_mode_gap_manager=NULL;

	m_allow_defrag=false;
		
	RegisterWithTracker(NULL);
}




CParkEditor::~CParkEditor()
{
}




void CParkEditor::Initialize(bool editMode)
{
	ParkEd("CParkEditor::Initialize()");
	//printf("CParkEditor::Initialize\n");
	
	mp_park_manager->Initialize();
	
	if (editMode && !mp_cursor)
	{
		mp_cursor = new CCursor();
		mp_menu_manager = new CUpperMenuManager();
	}
}




void CParkEditor::Rebuild(bool justRebuildFloor, bool clearMap, bool makeCursor)
{
	ParkEd("CParkEditor::Rebuild(justRebuildFloor = %d, clearMap = %d)", justRebuildFloor, clearMap);
	
	if (justRebuildFloor)
	{
		mp_park_manager->RebuildFloor();
	}
	else
	{
		if (mp_cursor)
			delete mp_cursor;
		mp_cursor = NULL;
		mp_park_manager->RebuildWholePark(clearMap);
	}	   
	
	if (makeCursor && !mp_cursor)
	{
		mp_cursor = new CCursor();
	}
	
	// Fix to TT1437, where the 1P restart piece was able to be placed outside the map if X pressed really quickly
	// just after nuking a park.
	if (mp_cursor)
	{
		mp_cursor->ForceInBounds();
	}
		
	// Regenerate the node array when the park is rebuilt (usually only happens when we are going to play the level)
	mp_park_manager->RebuildNodeArray();
	
	// and re register the gaps so that any old gaps are removed from the career.
	CGapManager::sInstance()->RegisterGapsWithSkaterCareer();			
}




void CParkEditor::SetState(EEditorState desiredState)
{
	ParkEd("CParkEditor::SetState(desiredState = %d)", desiredState);
	
	if (desiredState != vKEEP_SAME_STATE)
	{
		m_state = desiredState;
	}	
	
	// this prevents menu input from placing a piece
	m_commands.ClearAll();
	if (mp_cursor)
		mp_cursor->SetVisibility(m_state == vEDITING);

	if (desiredState == vTEST_PLAY || desiredState == vREGULAR_PLAY)
	{
	   	// XXX
		Ryan("$$$MakeEditorStuffVisible(false)\n");
		MakeEditorStuffVisible(false); 
		
		CGapManager::sInstance()->RegisterGapsWithSkaterCareer();	
	}
	else
	{
		// XXX
		Ryan("$$$MakeEditorStuffVisible(true)\n");
		MakeEditorStuffVisible(true); 	
	}

	Spt::SingletonPtr p_front_end;	
	if (desiredState == vEDITING)
	{
		Mdl::Skate::Instance()->CleanupForParkEditor();
	
		p_front_end->SetAutoRepeatTimes(320, 100);
		mp_park_manager->HighlightMetasWithGaps(false, NULL);
	}
	else
	{
		// same as above, but what the hell
		p_front_end->SetAutoRepeatTimes(320, 50);
	}
	update_analog_stick_menu_control_state();
}




void CParkEditor::MakeEditorStuffVisible(bool visible)
{
	ParkEd("MakeEditorStuffVisible(visible = %d)", visible);
	
	// it would be nice to sort out the encapsulation sometime...	
	Ed::CParkManager::Instance()->GetGenerator()->HighlightAllPieces(false);
	Ed::CParkManager::Instance()->GetGenerator()->SetGapPiecesVisible(visible);
	Ed::CParkManager::Instance()->GetGenerator()->SetRestartPiecesVisible(visible);
	
	if (visible)
	{
		Script::RunScript("pause_trick_text");
		Script::RunScript("GoalManager_HidePoints");
		Script::RunScript("GoalManager_HideGoalPoints");
		Script::RunScript("SK4_Hide_Death_Message");
	}
	else
	{
		Script::RunScript("unpause_trick_text");
		Script::RunScript("GoalManager_ShowPoints");
		Script::RunScript("GoalManager_ShowGoalPoints");
	}
}




void CParkEditor::AccessDisk(bool save, int fileSlot, bool blockRebuild)
{
	ParkEd("CParkEditor::AccessDisk()");
	
	if (save)
	{
		CParkManager::sInstance()->AccessDisk(true, fileSlot);
	}
	else
	{
		int old_theme = CParkManager::sInstance()->GetTheme();
		
		if (m_state != vINACTIVE && !blockRebuild)
		{
			// need to destroy cursor geometry now, otherwise code will think park has changed
			Dbg_Assert(mp_cursor);
			mp_cursor->DestroyGeometry();
		}
		if (mp_cursor)
		{
			mp_cursor->DestroyAnyClipboardCursors();
		}	
		
		CParkManager::sInstance()->AccessDisk(false, fileSlot);
		if (m_state != vINACTIVE && !blockRebuild)
		{
			if (CParkManager::sInstance()->GetTheme() != old_theme)
			{
				Script::CStruct params;
				params.AddChecksum("level", CRCD(0xe8b4b836,"Load_Sk5Ed"));
				
				Script::RunScript("change_level", ¶ms);
			}
			else
			{
				CParkManager::sInstance()->RebuildWholePark(false);
				mp_cursor->ChangePieceInSet(0);
				mp_park_manager->HighlightMetasWithGaps(false, NULL);
			}
		}
	}
}




void CParkEditor::PostMemoryCardLoad(uint8 * p_map_buffer, int oldTheme)
{
	bool needs_rebuild = (m_state == vEDITING || m_state == vTEST_PLAY);
	
	if (needs_rebuild)
	{
		// need to destroy cursor geometry now, otherwise code will think park has changed
		Dbg_Assert(mp_cursor);
		mp_cursor->DestroyGeometry();
	}
	// PATCH:  Copy buffer back over itself to set the flags
	Ed::CParkManager::Instance()->SetCompressedMapBuffer(p_map_buffer);
	if (needs_rebuild)
	{
		// XXX
		Ryan("old theme %d, new theme %d\n", oldTheme, CParkManager::sInstance()->GetTheme());
		if (CParkManager::sInstance()->GetTheme() != oldTheme)
		{
			Script::CStruct params;
			params.AddChecksum("level", CRCD(0xe8b4b836,"Load_Sk5Ed"));

			Script::RunScript("change_level", ¶ms);
		}
		else
		{
			CParkManager::sInstance()->RebuildWholePark(false);
			mp_cursor->ChangePieceInSet(0);
			mp_park_manager->HighlightMetasWithGaps(false, NULL);
		}
	}
}


// K: Added to allow cleanup of the park editor heap during play
void CParkEditor::DeleteCursor()
{
	if (mp_cursor)
	{
		delete mp_cursor;
		mp_cursor=NULL;
	}
}

// Creates a copy of the gap manager on the script heap.
// CGapManager::sInstance() will still return the original gap manager afterwards however.
// This function is needed because to free up memory before playing a park, everything on the
// park editor heap is deleted and then the heap is deleted, and the original gap manager is on that heap.
// So a copy is made for play mode, since the gap manager is needed when a gap fires.
void CParkEditor::CreatePlayModeGapManager()
{
	Dbg_MsgAssert(mp_play_mode_gap_manager==NULL,("Expected mp_play_mode_gap_manager to be NULL"));
	Ed::CGapManager *p_original_gap_manager = CGapManager::sInstance();
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());
	mp_play_mode_gap_manager=new Ed::CGapManager(NULL);
	
	// Use the overloaded assignement operator to make the copy.
	*mp_play_mode_gap_manager = *p_original_gap_manager;
	
	Mem::Manager::sHandle().PopContext();
	
	
	// The constructor of CGapManager sets the sp_instance pointer, so set it back to the original so that
	// things don't get confused.
	p_original_gap_manager->SetInstance();
}

void CParkEditor::SwitchToPlayModeGapManager()
{
	Dbg_MsgAssert(CGapManager::sInstanceNoAssert() == NULL,("Expected CGapManager::sInstance() to be NULL"));
	Dbg_MsgAssert(mp_play_mode_gap_manager,("NULL mp_play_mode_gap_manager"));
	mp_play_mode_gap_manager->SetInstance();
}	

void CParkEditor::DeletePlayModeGapManager()
{
	if (mp_play_mode_gap_manager)
	{
		delete mp_play_mode_gap_manager;
		mp_play_mode_gap_manager=NULL;
	}
	Dbg_MsgAssert(CGapManager::sInstanceNoAssert() == NULL,("Expected CGapManager::sInstance() to be NULL"));
}

void CParkEditor::PlayModeGapManagerChecks()
{
	//printf("CGapManager::sInstance = 0x%08x\n",CGapManager::sInstanceNoAssert());
	//printf("mp_play_mode_gap_manager = 0x%08x\n",mp_play_mode_gap_manager);
}

void CParkEditor::Cleanup()
{
	ParkEd("CParkEditor::Cleanup()");

	m_state = vINACTIVE;
	if (mp_cursor)
	{
		delete mp_cursor;
		mp_cursor = NULL;
	}
	if (mp_menu_manager)
	{
		delete mp_menu_manager;
		mp_menu_manager = NULL;
	}
	
	mp_park_manager->Destroy(CParkGenerator::DESTROY_PIECES_AND_SECTORS);
	
	Obj::CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors = false;
	Obj::GetRailEditor()->DestroyRailGeometry();
	Obj::GetRailEditor()->DestroyPostGeometry();
	Obj::CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors = true;

	printf("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ DEATH OR GLORY 2 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
}

void CParkEditor::SetAppropriateCamera()
{
	if (mp_cursor && mp_cursor->InRailPlacementMode())
	{
		CFuncs::SetActiveCamera(CRCD(0x5b509ad3,"raileditor"), 0, false);
	}
	else
	{
		CFuncs::SetActiveCamera(CRCD(0x64716bee,"parked_cam"), 0, false);
	}	
}

void CParkEditor::do_piece_select_commands()
{
	if (m_commands.TestMask(mPREV_SET))
	{
		mp_cursor->ChangeSet(-1); 
		turn_on_or_update_piece_menu();
		Obj::CTracker* p_tracker = Obj::CTracker::Instance();
		p_tracker->LaunchEvent(Script::GenerateCRC("parked_menu_up"), Script::GenerateCRC("set_menu_container"));
		//mp_menu_manager->SetSourceMeta(mp_cursor->GetChecksum());
	}
	if (m_commands.TestMask(mNEXT_SET))
	{
		mp_cursor->ChangeSet(1); 
		turn_on_or_update_piece_menu();
		Obj::CTracker* p_tracker = Obj::CTracker::Instance();
		p_tracker->LaunchEvent(Script::GenerateCRC("parked_menu_down"), Script::GenerateCRC("set_menu_container"));
		//mp_menu_manager->SetSourceMeta(mp_cursor->GetChecksum());
	}
	if (m_commands.TestMask(mPREV_PIECE))
	{
		mp_cursor->ChangePieceInSet(-1); 
		turn_on_or_update_piece_menu();
		Obj::CTracker* p_tracker = Obj::CTracker::Instance();
		p_tracker->LaunchEvent(Script::GenerateCRC("parked_menu_left"), Script::GenerateCRC("piece_menu_container"));
		//mp_menu_manager->SetSourceMeta(mp_cursor->GetChecksum());
	}
	if (m_commands.TestMask(mNEXT_PIECE))
	{
		mp_cursor->ChangePieceInSet(1); 
		turn_on_or_update_piece_menu();
		Obj::CTracker* p_tracker = Obj::CTracker::Instance();
		p_tracker->LaunchEvent(Script::GenerateCRC("parked_menu_right"), Script::GenerateCRC("piece_menu_container"));
		//mp_menu_manager->SetSourceMeta(mp_cursor->GetChecksum());
	}
}

void CParkEditor::regular_command_logic()
{
	Dbg_MsgAssert(mp_cursor,("NULL mp_cursor"));

	Mth::Matrix cam_matrix;
	cam_matrix.Ident();
	cam_matrix.SetPos(Mth::Vector(0.0f, 0.0f, m_camDist, 1.0f));
	cam_matrix.RotateX(Mth::DegToRad(-m_camAngleVert));
	cam_matrix.RotateY(Mth::DegToRad(m_camAngle));
	cam_matrix.Translate(Mth::Vector(mp_cursor->m_pos[X], vCAM_TARGET_ELEVATION, mp_cursor->m_pos[Z], 0.0f));
	
	Obj::CCompositeObject * p_obj = (Obj::CCompositeObject *) Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0x64716bee,"parked_cam"));
	if (p_obj)
	{
		//printf("Setting cam pos\n");
		//CFuncs::SetActiveCamera(CRCD(0x64716bee,"parked_cam"), 0, false);
		
		//Obj::CCameraComponent * p_cam_comp = GetCameraComponentFromObject(p_obj); 			
		p_obj->SetPos(cam_matrix[Mth::POS]);
		p_obj->SetMatrix(cam_matrix);
	}


	if (!m_paused)
	{
		// XXX
		//Ryan("enraged monkey\n");

		// We unhighlight the meta-pieces intersecting the cursor before
		// we change or move it.
		if (!mp_cursor->InAreaSelectMode())
		{
			mp_cursor->HighlightIntersectingMetas(false); 
		}	
		
		Tmr::Time current_time = Tmr::GetTime();
		float elapsed_seconds = (int) (current_time - m_last_time) / 1000.0f;	
		m_last_time = current_time;
		
		static const	float	scale = ( 1.0f / ( 128.0f - Inp::vANALOGUE_TOL ));
		
		m_leftStick.m_Y =
			( m_leftStick.m_Y >  Inp::vANALOGUE_TOL ) ? ( m_leftStick.m_Y - Inp::vANALOGUE_TOL ) * scale :
			( m_leftStick.m_Y < -Inp::vANALOGUE_TOL ) ? ( m_leftStick.m_Y + Inp::vANALOGUE_TOL ) * scale : 0.0f;
		m_leftStick.m_X =
			( m_leftStick.m_X >  Inp::vANALOGUE_TOL ) ? ( m_leftStick.m_X - Inp::vANALOGUE_TOL ) * scale :
			( m_leftStick.m_X < -Inp::vANALOGUE_TOL ) ? ( m_leftStick.m_X + Inp::vANALOGUE_TOL ) * scale : 0.0f;
			
		float rel_shift_x = m_leftStick.m_X * elapsed_seconds * m_movement_vel;
		float rel_shift_z = m_leftStick.m_Y * elapsed_seconds * m_movement_vel;
		float angle_rad = m_camAngle * Mth::PI / 180.0f;
		float shift_x = cosf(angle_rad) * rel_shift_x + sinf(angle_rad) * rel_shift_z;
		float shift_z = cosf(angle_rad) * rel_shift_z - sinf(angle_rad) * rel_shift_x;
	
		m_rightStick.m_Y =
			( m_rightStick.m_Y >  Inp::vANALOGUE_TOL ) ? ( m_rightStick.m_Y - Inp::vANALOGUE_TOL ) * scale :
			( m_rightStick.m_Y < -Inp::vANALOGUE_TOL ) ? ( m_rightStick.m_Y + Inp::vANALOGUE_TOL ) * scale : 0.0f;
		m_rightStick.m_X =
			( m_rightStick.m_X >  Inp::vANALOGUE_TOL ) ? ( m_rightStick.m_X - Inp::vANALOGUE_TOL ) * scale :
			( m_rightStick.m_X < -Inp::vANALOGUE_TOL ) ? ( m_rightStick.m_X + Inp::vANALOGUE_TOL ) * scale : 0.0f;
	
#ifdef __PLAT_NGC__DAN
		if ( !( m_commands.TestMask(mZOOM_CAMERA_IN) ) )
#endif		// __PLAT_NGC__
		{
			m_camAngle += m_rightStick.m_X * elapsed_seconds * m_rotate_vel;	
		}
		if (m_camAngle < 0) m_camAngle += 360.0f;
		else if (m_camAngle >= 360.0) m_camAngle -= 360.0f;
#ifdef __PLAT_NGC__DAN
		if ( !( m_commands.TestMask(mZOOM_CAMERA_IN) ) )
#endif		// __PLAT_NGC__
		{
			m_camAngleVert -= m_rightStick.m_Y * elapsed_seconds * m_rotate_vel;	
		}
		if (m_camAngleVert < 10.0f) m_camAngleVert = 10.0f;
		else if (m_camAngleVert > 90.0) m_camAngleVert = 90.0f;

		/*
			PlayEdPlaceSound for placing items (might have wrong sound hooked up right now)
			PlayEdEraseSound  for removing items
			PlayRaiseGroundSound for raising
			PlayLowerGroundSound for lowering
			PlayRotatePieceSound for rotating both directions
		*/
		
		int rot_inc = 0;
		if (m_commands.TestMask(mROTATE_CW))
		{
			Script::RunScript("PlayRotatePieceSound");
			if (mp_cursor->InGapMode())
				mp_cursor->AdjustGap(-1, 0, 0);
			else
				rot_inc = -1;
		}
		if (m_commands.TestMask(mROTATE_CCW))
		{
			Script::RunScript("PlayRotatePieceSound");
			if (mp_cursor->InGapMode())
				mp_cursor->AdjustGap(-1, 0, 0);
			else
				rot_inc = 1;
		}
		if (m_commands.TestMask(mPLACE_PIECE))
		{
			if (mp_cursor->InGapMode())
			{
				bool success = mp_cursor->AttemptGap();
				if ((success || mp_cursor->IsSittingOnGap()) && !mp_cursor->HalfFinishedGap())
				{
					Script::RunScript("PlayEdPlaceSound");
					// XXX
					Ryan("the hell, you say\n");
					Script::RunScript("parked_setup_gap_menu");
				}
				else
					Script::RunScript("PlayEdBuzzSound");
					
				m_allow_defrag=true;
			}
			else if (mp_cursor->InAreaSelectMode())
			{
				switch (mp_cursor->AttemptAreaSelect())
				{
				case 1:
					// Maybe have a different sound for the first corner ?
					Script::RunScript("PlayEdPlaceSound");
					break;
				case 2:
					Script::RunScript("PlayEdPlaceSound");
					Script::RunScript("parked_setup_area_select_menu");
					break;
				default:
					Script::RunScript("PlayEdBuzzSound");
					break;
				}
				// Note: Not setting m_allow_defrag here, because otherwise it may defrag
				// as soon as the user places one of the area corners, which becomes a pain
				// especially if trying to select a set of pieces to delete to free up memory.
			}
			else if (mp_cursor->InPasteMode())
			{
				mp_cursor->PasteCurrentClipboard();
				m_allow_defrag=true;
			}
			else	
			{
				if (mp_cursor->AttemptStamp())
					Script::RunScript("PlayEdPlaceSound");
				else
					Script::RunScript("PlayEdBuzzSound");
				m_allow_defrag=true;
			}
		}
		if (m_commands.TestMask(mREMOVE_PIECE))
		{
			if (mp_cursor->InGapMode())
			{
				if (mp_cursor->AttemptRemoveGap())
				{
					Script::RunScript("PlayEdEraseSound");
				}
				else
					Script::RunScript("PlayEdBuzzSound");
			}
			else if (mp_cursor->InAreaSelectMode())
			{
				mp_cursor->ClearAreaSelection();
				m_allow_defrag=true;
			}
			else	
			{
				if (mp_cursor->AttemptRemove())
				{
					Script::RunScript("PlayEdEraseSound");
				}
				else
				{
					Script::RunScript("PlayEdBuzzSound");
				}
				m_allow_defrag=true;
			}
			// and re register the gaps so that any old gaps are removed from the career.
			CGapManager::sInstance()->RegisterGapsWithSkaterCareer();			
		}
		
		do_piece_select_commands();
		
		if (m_commands.TestMask(mRAISE_FLOOR))
		{
			if (!mp_cursor->InGapMode())
			{
				if (mp_cursor->ChangeFloorHeight(1))
					Script::RunScript("PlayRaiseGroundSound");
				else
					Script::RunScript("PlayEdBuzzSound");
			}
			m_allow_defrag=true;
		}
		if (m_commands.TestMask(mLOWER_FLOOR))
		{
			if (!mp_cursor->InGapMode())
			{
				if (mp_cursor->ChangeFloorHeight(-1))
					Script::RunScript("PlayLowerGroundSound");
				else
					Script::RunScript("PlayEdBuzzSound");
			}
			m_allow_defrag=true;
		}

#ifdef __PLAT_NGC__DAN
		if ( m_commands.TestMask(mZOOM_CAMERA_IN) )
		{
			// Use y component of stick to zoom camera in or out.
			m_camDist += vCAM_DIST_INC * m_rightStick.m_Y;
			if (m_camDist > vMAX_CAM_DIST)
				m_camDist = vMAX_CAM_DIST;
			if (m_camDist < vMIN_CAM_DIST)
				m_camDist = vMIN_CAM_DIST;
		}
#else
		if (m_commands.TestMask(mZOOM_CAMERA_OUT) && !mp_cursor->InGapMode())
		{
			m_camDist += vCAM_DIST_INC;
			if (m_camDist > vMAX_CAM_DIST)
				m_camDist = vMAX_CAM_DIST;
		}
		if (m_commands.TestMask(mZOOM_CAMERA_IN) && !mp_cursor->InGapMode())
		{
			m_camDist -= vCAM_DIST_INC;
			if (m_camDist < vMIN_CAM_DIST)
				m_camDist = vMIN_CAM_DIST;
		}
#endif		// __PLAT_NGC__
		
		if (m_commands.TestMask(mINCREASE_GAP_LEFT))
		{
			if (mp_cursor->InGapMode())
				mp_cursor->AdjustGap(0, 1, 0);
		}
		if (m_commands.TestMask(mINCREASE_GAP_RIGHT))
		{
			if (mp_cursor->InGapMode())
				mp_cursor->AdjustGap(0, 0, 1);
		}
		if (m_commands.TestMask(mDECREASE_GAP_LEFT))
		{
			if (mp_cursor->InGapMode())
				mp_cursor->AdjustGap(0, -1, 0);
		}
		if (m_commands.TestMask(mDECREASE_GAP_RIGHT))
		{
			if (mp_cursor->InGapMode())
				mp_cursor->AdjustGap(0, 0, -1);
		}


		// If an operation has occurred that has modified collidable geometry,
		// run through all the existing created-rails and update their posts so that they
		// maintain ground contact.
		if (m_commands.TestMask(mPLACE_PIECE) ||
			m_commands.TestMask(mREMOVE_PIECE) ||
			m_commands.TestMask(mRAISE_FLOOR) ||
			m_commands.TestMask(mLOWER_FLOOR))
		{
			Obj::CRailEditorComponent *p_rail_editor=Obj::GetRailEditor();
			p_rail_editor->AdjustYs();
			p_rail_editor->UpdateRailGeometry();
			p_rail_editor->UpdatePostGeometry();
			
			Obj::GetGoalEditor()->RefreshGoalPositionsUsingCollisionCheck();
		}
		
		mp_cursor->Update(shift_x, shift_z, rot_inc);
		//printf("cursor position: (%.2f,%.2f)\n", mp_cursor->m_target_pos[X], mp_cursor->m_target_pos[Z]);		
		
		if (mp_cursor->InAreaSelectMode())
		{
			mp_cursor->RefreshSelectionArea();
		}
		else if (mp_cursor->InRailPlacementMode())
		{
			mp_cursor->DestroyGeometry();
		}
		else
		{
			// Highlight the pieces intersecting the cursor after it has changed or moved
			mp_cursor->HighlightIntersectingMetas(true); 
		}	
	}	
}

void CParkEditor::rail_placement_logic()
{
	Dbg_MsgAssert(mp_cursor,("NULL mp_cursor"));

	if (!m_paused)
	{
		do_piece_select_commands();
	}
}
	
void CParkEditor::Update()
{
	if (!m_paused)
	{
		if (m_state == vEDITING)
		{
			Script::CStruct params;
			params.AddChecksum(NONAME, Script::GenerateCRC("hide"));
			CFuncs::ScriptPauseSkaters(¶ms, NULL);
		}
		else if (m_state == vTEST_PLAY)
		{
			CFuncs::ScriptUnPauseSkaters(NULL, NULL);
		}
	}

	if (m_state == vEDITING && mp_cursor)
	{
		if (!m_paused)
		{
			if (mp_cursor->InRailPlacementMode())
			{
				if (Obj::GetRailEditor()->IsSuspended())
				{
					Script::RunScript(CRCD(0x2c3b17ea,"SwitchOnRailEditor"));
				}
				
				rail_placement_logic();
				
				if (!mp_cursor->InRailPlacementMode())
				{
					Script::RunScript(CRCD(0x32428a49,"SwitchOffRailEditor"));
				}
			}
			else
			{
				if (!Obj::GetRailEditor()->IsSuspended())
				{
					Script::RunScript(CRCD(0x32428a49,"SwitchOffRailEditor"));
				}
				regular_command_logic();
				if (mp_cursor->InRailPlacementMode())
				{
					Script::RunScript(CRCD(0x2c3b17ea,"SwitchOnRailEditor"));
				}
			}	
		}

		if (!m_paused)		
		{
			CParkGenerator *p_generator = mp_park_manager->GetGenerator();
			CParkGenerator::MemUsageInfo usage_info = p_generator->GetResourceUsageInfo();
	
			#ifdef	__NOPT_ASSERT__
			if (Script::GetInteger(CRCD(0xcc55a87b,"show_parked_main_heap_free")))
			{
				printf("usage_info.mMainHeapFree = %d   usage_info.mParkHeapFree=%d\n",usage_info.mMainHeapFree,usage_info.mParkHeapFree);
			}	
			#endif
			

			if (usage_info.mMainHeapFree >= 0)
			{
				m_pct_resources_used=1.0f-((float)usage_info.mMainHeapFree)/((float)p_generator->GetResourceSize("main_heap_base"));
			}
			else
			{
				#ifdef	__NOPT_ASSERT__
				printf("Memory overflow! Gone over the end of the gauge by %d bytes\n",-usage_info.mMainHeapFree);
				#endif
				m_pct_resources_used=1.0f;
			}	

			Mem::Heap *p_bottom_up_heap = Mem::Manager::sHandle().BottomUpHeap();
			Mem::Heap *p_top_down_heap = Mem::Manager::sHandle().TopDownHeap();
			#ifdef	__NOPT_ASSERT__
			if (Script::GetInteger(CRCD(0xe2ca7dfc,"show_actual_heap_status")))
			{
				printf("Bottom up = %d  Top down = %d\n",p_bottom_up_heap->mFreeMem.m_count,p_top_down_heap->mp_region->MemAvailable());
			}	
			#endif
			
			// top_down_pct is a metric based on the amount of top down heap available. The reason for the 2.0 is to
			// make the top_down_pct be >= 1.0 if there is less than TOP_DOWN_REQUIRED_MARGIN available, and < 1.0
			// if there is more than that available.
			float top_down_pct=2.0f-(((float)p_top_down_heap->mp_region->MemAvailable()) / ((float)TOP_DOWN_REQUIRED_MARGIN));
			if (top_down_pct > m_pct_resources_used)
			{
				m_pct_resources_used = top_down_pct;
			}	


			float park_heap_pct=1.0f-((float)usage_info.mParkHeapFree)/((float)p_generator->GetResourceSize("park_heap_base"));
			if (park_heap_pct > m_pct_resources_used)
			{
				m_pct_resources_used=park_heap_pct;
			}	

			m_last_main_heap_free=usage_info.mMainHeapFree;

			//printf("2: m_pct_resources_used = %f\n",m_pct_resources_used);
			
			//float rail_point_pct = (float) usage_info.mTotalRailPoints / (float) (p_generator->GetResourceSize("out_railpoint_pool") - 25);
			int component_use_est = usage_info.mTotalClonedPieces * 7 + usage_info.mTotalRailPoints * 9 + usage_info.mTotalLinkedRailPoints;
			int base_component_use = p_generator->GetResourceSize("component_use_base");
			float component_pct = (float) (component_use_est - base_component_use) / (float) (p_generator->GetResourceSize("max_components") - base_component_use);
			if (m_pct_resources_used < component_pct)
				m_pct_resources_used = component_pct;
			//printf("component_use_est=%d\n",component_use_est);
			//ParkEd("3: m_pct_resources_used=%f",m_pct_resources_used);
			
			int vector_use_est = usage_info.mTotalClonedPieces * 2 + usage_info.mTotalRailPoints * 2;
			int base_vector_use = p_generator->GetResourceSize("vector_use_base");
			float vector_pct = (float) (vector_use_est - base_vector_use) / (float) (p_generator->GetResourceSize("max_vectors") - base_vector_use);
			if (m_pct_resources_used < vector_pct)
				m_pct_resources_used = vector_pct;
			//printf("vector_use_est=%d\n",vector_use_est);	
			//ParkEd("4: m_pct_resources_used=%f",m_pct_resources_used);
			//printf("3: m_pct_resources_used = %f\n",m_pct_resources_used);

			float pieces_pct=(float)mp_park_manager->GetDMAPieceCount() / (float)p_generator->GetResourceSize("max_dma_pieces");
			if (pieces_pct > m_pct_resources_used)
			{
				m_pct_resources_used = pieces_pct;
			}
			
			//printf("4: m_pct_resources_used = %f IsParkFull=%d\n",m_pct_resources_used,IsParkFull());
			
			float shown_pct_resources_used = m_pct_resources_used;
			if (shown_pct_resources_used < 0.0f) shown_pct_resources_used = 0.0f;
			else if (shown_pct_resources_used > 1.0f) shown_pct_resources_used = 1.0f;



			bool do_defragment=false;
			
			// Don't defrag if in rail placement mode, cos the resulting mode switch causes an assert.
			if (mp_cursor->InRailPlacementMode())
			{
				m_allow_defrag=false;
			}
			// m_allow_defrag is a flag that gets set by any operation that may have increased
			// memory usage, such as placing a piece, or raising the ground.
			// It is required to prevent an infinite loop of defrags which could occur if defrags
			// were allowed every frame.
			
			if (shown_pct_resources_used >= 1.0f && p_top_down_heap->mp_region->MemAvailable() < TOP_DOWN_REQUIRED_MARGIN)
			{
				// We're in trouble, there may not be enough top down heap for UpdateSuperSectors
				// to execute. This can happen if trying to raise a large chunk of ground.
				if (p_bottom_up_heap->mFreeMem.m_count + p_top_down_heap->mp_region->MemAvailable() > (TOP_DOWN_REQUIRED_MARGIN + TOP_DOWN_REQUIRED_MARGIN_LEEWAY))
				{
					// ... but there would be more than enough if all the fragments could be utilised,
					// so do a defragment to see if that helps.
					if (m_allow_defrag)
					{
						do_defragment=true;
					}	
				}
			}	

			
			// The old logic from THPS4, which would only defrag if this flag was set.
			/*
			if (usage_info.mIsFragmented)
			{
				if (m_allow_defrag)
				{
					do_defragment=true;
				}	
			}
			*/

			m_allow_defrag=false;
			
			if (do_defragment)
			{
				#ifdef __PLAT_NGC__
				// The regular defragment script often does not fully cure the fragmentation, which
				// is a problem on the NGC because of its lower memory. So do a full reload instead.
				Script::SpawnScript(CRCD(0x57c05a9a,"reload_park"));
				#else
					// XXX
					Ryan("defragging, memory free (largest block) is %d\n", usage_info.mMainHeapFree);
					SetPaused(true);
					// run a script to defragment memory
					#ifdef __NOPT_ASSERT__
					Script::CScript *p_script=Script::SpawnScript("parked_defragment_memory");
					p_script->SetCommentString("Spawned by CParkEditor::Update()");
					#else
					Script::SpawnScript("parked_defragment_memory");
					#endif
				#endif
				return;
			}



			// percent_bar_colored_part
			Front::CScreenElementManager* p_elem_man = Front::CScreenElementManager::Instance();
			Front::CScreenElement *p_pct_bar = p_elem_man->GetElement(CRCD(0xcd9c729a, "percent_bar_colored_part"), Front::CScreenElementManager::DONT_ASSERT).Convert();
			if (p_pct_bar)
				p_pct_bar->SetScale(shown_pct_resources_used, 1.0f, false, Front::CScreenElement::FORCE_INSTANT);

			if (Script::GetInteger(CRCD(0x7a9c1acd,"parked_show_memory_stats")))
			{
				Script::RunScript(CRCD(0xb935948d,"CreateMemStatsScreenElements"));
				
				Front::CScreenElementManager* p_elem_man = Front::CScreenElementManager::Instance();
				Front::CTextElement *p_park_free_elem = (Front::CTextElement *) p_elem_man->GetElement(CRCD(0xa5a33eb9, "parked_mem_stats_park_free"), Front::CScreenElementManager::ASSERT).Convert();
				char park_free_text[128];
				sprintf(park_free_text, "park heap free:\\c2 %s", Str::PrintThousands(usage_info.mParkHeapFree));
				p_park_free_elem->SetText(park_free_text);
	
				Front::CTextElement *p_main_free_elem = (Front::CTextElement *) p_elem_man->GetElement(CRCD(0xcd38c218, "parked_mem_stats_main_free"), Front::CScreenElementManager::ASSERT).Convert();
				char main_free_text[128];
				if (usage_info.mIsFragmented)
					sprintf(main_free_text, "main heap free:\\c2 %s \\c1FRAG", Str::PrintThousands(usage_info.mMainHeapFree));
				else
					sprintf(main_free_text, "main heap free:\\c2 %s", Str::PrintThousands(usage_info.mMainHeapFree));
				p_main_free_elem->SetText(main_free_text);
	
				Front::CTextElement *p_last_op_elem = (Front::CTextElement *) p_elem_man->GetElement(CRCD(0x9dd4532f, "parked_mem_stats_last_op"), Front::CScreenElementManager::ASSERT).Convert();
				char last_op_text[128];
				sprintf(last_op_text, "last op size:\\c2 %d", usage_info.mMainHeapUsed - usage_info.mLastMainUsed);
				p_last_op_elem->SetText(last_op_text);
	
				Front::CTextElement *p_other_elem = (Front::CTextElement *) p_elem_man->GetElement(CRCD(0xb42704a3, "parked_mem_stats_other"), Front::CScreenElementManager::ASSERT).Convert();
				char other_text[128];
				sprintf(other_text, "components free:\\c2 %d %d", 
						p_generator->GetResourceSize("max_components") - component_use_est - base_component_use,
						p_generator->GetResourceSize("max_vectors") - vector_use_est - base_vector_use);
				p_other_elem->SetText(other_text);
	
				Front::CTextElement *p_pct_elem = (Front::CTextElement *) p_elem_man->GetElement(CRCD(0x65f2394f, "parked_mem_stats_pct"), Front::CScreenElementManager::ASSERT).Convert();
				char pct_text[128];
				sprintf(pct_text, "pct:\\c3 %.2f", m_pct_resources_used * 100.0f);
				p_pct_elem->SetText(pct_text);
	
				Front::CTextElement *p_pieces_remaining_elem = (Front::CTextElement *) p_elem_man->GetElement(CRCD(0x3da56dac, "parked_mem_pieces_remaining"), Front::CScreenElementManager::ASSERT).Convert();
				char pieces_remaining_text[128];
				sprintf(pieces_remaining_text, "pieces remaining:\\c2 %d", p_generator->GetResourceSize("max_dma_pieces") - mp_park_manager->GetDMAPieceCount());
				p_pieces_remaining_elem->SetText(pieces_remaining_text);

				Front::CTextElement *p_main_used_elem = (Front::CTextElement *) p_elem_man->GetElement(CRCD(0xf95160c8, "parked_mem_stats_main_used"), Front::CScreenElementManager::ASSERT).Convert();
				char main_used_text[128];
				sprintf(main_used_text, "main heap used:\\c2 %s", Str::PrintThousands(usage_info.mMainHeapUsed));
				p_main_used_elem->SetText(main_used_text);
			}
			else
			{
				Script::RunScript(CRCD(0x7a449180,"DestroyMemStatsScreenElements"));
			}	

			// Update the clipboard usage bar.			
			/*
			Front::CScreenElement *p_clipboard_percent_bar = p_elem_man->GetElement(CRCD(0xf07100ce,"clipboard_percent_bar_colored_part"), Front::CScreenElementManager::DONT_ASSERT).Convert();
			if (p_clipboard_percent_bar)
			{
				p_clipboard_percent_bar->SetScale(mp_park_manager->GetClipboardProportionUsed(), 1.0f, false, Front::CScreenElement::FORCE_INSTANT);
			}
			*/
		} // end if (!m_paused)
	}
	else
	{
		m_commands.ClearAll();
	}  
}

void CParkEditor::SwitchMenuPieceToMostRecentClipboard()
{
	CCursor::sInstance()->SwitchMenuPieceToMostRecentClipboard();
	
	turn_on_or_update_piece_menu();
	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
	p_tracker->LaunchEvent(Script::GenerateCRC("parked_menu_up"), Script::GenerateCRC("set_menu_container"));
}

// Returns true if there is enough memory to copy or paste an area of the given size.
bool CParkEditor::RoomToCopyOrPaste(int w, int l)
{
	#ifdef __PLAT_NGC__
	Mem::Heap *p_top_down_heap = Mem::Manager::sHandle().TopDownHeap();
	if (p_top_down_heap->mp_region->MemAvailable() < TOP_DOWN_REQUIRED_MARGIN)
	{
		return false;
	}	
	#endif
	
	// Assumes that in the worst case 1 cell uses up 40K, for example when a bunch of fountain pieces are raised
	// to the maximum height.
	return w * l * 40000 < m_last_main_heap_free;
}

void CParkEditor::SetPaused(bool paused)
{
	// XXX
	Ryan("some fucker has made my pause state = %d\n", paused);
	m_paused = paused;

	if (mp_menu_manager)
	{
		if (paused)
		{
			Script::RunScript("parked_kill_wait_message");
			mp_menu_manager->Disable();
		}
		else
		{
			mp_menu_manager->Enable();
			turn_on_or_update_piece_menu();
		}
	}

	if (mp_cursor)
	{
		if (mp_cursor->InRailPlacementMode())
		{
			mp_cursor->SetVisibility(false);
		}
		else
		{
			if (paused)
			{
				mp_cursor->SetVisibility(false);
			}		
			else
			{
				mp_cursor->SetVisibility(true);
			}		
		}	
		mp_cursor->RefreshHelperText();
	}	

	update_analog_stick_menu_control_state();
}




void CParkEditor::turn_on_or_update_piece_menu()
{
	int menu_set_number;
	int set_num = mp_cursor->GetSelectedSet(&menu_set_number);
	// XXX
	Ryan("piece set number is %d\n", set_num);
	Dbg_Assert(mp_menu_manager);
	mp_menu_manager->SetPieceSet(&mp_park_manager->GetPieceSet(set_num), set_num, menu_set_number);
	
	if (mp_cursor)
	{
		mp_cursor->RefreshHelperText();
	}	
}




void CParkEditor::update_analog_stick_menu_control_state()
{
	Spt::SingletonPtr p_front_end;	
	if (m_state == vEDITING && !m_paused)
	{
		p_front_end->SetAnalogStickActiveForMenus(false);
	}
	else
		p_front_end->SetAnalogStickActiveForMenus(true);
}




void CParkEditor::pass_event_to_listener(Obj::CEvent *pEvent)
{
	if (m_state != vEDITING || !mp_cursor || m_paused)
		return;
	
	uint32 type = pEvent->GetType();
	uint32 target = pEvent->GetTarget();

	if (target != CRCD(0x56a1eae3, "root_window"))
		return;

#ifdef __PLAT_NGC__
	if ( m_commands.TestMask( mINCREASE_GAP_RIGHT ) ) return;
	if ( m_commands.TestMask( mDECREASE_GAP_RIGHT ) ) return;
	if ( m_commands.TestMask( mINCREASE_GAP_LEFT ) ) return;
	if ( m_commands.TestMask( mDECREASE_GAP_LEFT ) ) return;
#endif		// __PLAT_NGC__

	switch(type)
	{
		case Obj::CEvent::TYPE_PAD_UP:
			m_commands.SetMask( mPREV_SET );
			break;
		case Obj::CEvent::TYPE_PAD_DOWN:
			m_commands.SetMask( mNEXT_SET );
			break;
		case Obj::CEvent::TYPE_PAD_LEFT:
			m_commands.SetMask( mPREV_PIECE );
			break;
		case Obj::CEvent::TYPE_PAD_RIGHT:
			m_commands.SetMask( mNEXT_PIECE );
			break;
		default:
			break;
	}
	//Ryan("I got an event!!\n");
}




void CParkEditor::v_start_cb()
{
	
	Ryan("CParkEditor2::v_start_cb\n");

	Mlp::Manager * mlp_manager = Mlp::Manager::Instance(); 
	mlp_manager->AddLogicTask ( *m_logic_task );
	Inp::Manager * inp_manager = Inp::Manager::Instance(); 
	inp_manager->AddHandler( *m_input_handler );
	mlp_manager->AddDisplayTask ( *m_display_task );
}




void CParkEditor::v_stop_cb()
{
	Ryan("CParkEditor2::v_stop_cb\n");

	m_logic_task->Remove();
	m_input_handler->Remove();
}




void CParkEditor::s_input_logic_code ( const Inp::Handler < CParkEditor >& handler )
{
	CParkEditor &mdl = handler.GetData();
	// first clear last frame's data
	mdl.m_commands.ClearAll();

	//if (!mdl.mp_map || mdl.GameGoingOrOutsideEditor() || mdl.InMenu() || !mdl.m_initialized) return;
	
	// translate m_Input data to module-specific commands
	mdl.m_rightStick.m_X = handler.m_Input->m_Event[Inp::Data::vA_RIGHT_X] - 128;
	mdl.m_rightStick.m_Y = handler.m_Input->m_Event[Inp::Data::vA_RIGHT_Y] - 128;
	mdl.m_leftStick.m_X = handler.m_Input->m_Event[Inp::Data::vA_LEFT_X] - 128;
	mdl.m_leftStick.m_Y = handler.m_Input->m_Event[Inp::Data::vA_LEFT_Y] - 128;

#ifdef __PLAT_NGC__
	if( handler.m_Input->m_Makes & Inp::Data::mD_TRIANGLE )
	{
		mdl.m_commands.SetMask( mROTATE_CCW );
	}
#else
	if( handler.m_Input->m_Makes & Inp::Data::mD_SQUARE )
	{
		mdl.m_commands.SetMask( mROTATE_CCW );
	}
#endif		// __PLAT_NGC__
	if( handler.m_Input->m_Makes & Inp::Data::mD_CIRCLE )
	{
		mdl.m_commands.SetMask( mROTATE_CW );
	}
	if( handler.m_Input->m_Makes & Inp::Data::mD_X )
	{
		mdl.m_commands.SetMask( mPLACE_PIECE );
	}
#ifdef __PLAT_NGC__
	if( handler.m_Input->m_Makes & Inp::Data::mD_SQUARE )
	{
		mdl.m_commands.SetMask( mREMOVE_PIECE );
	}
#else
	if( handler.m_Input->m_Makes & Inp::Data::mD_TRIANGLE )
	{
		mdl.m_commands.SetMask( mREMOVE_PIECE );
	}
#endif		// __PLAT_NGC__
#ifdef __PLAT_NGC__
	if( !(handler.m_Input->m_Buttons & Inp::Data::mD_Z) )
	{
		if( handler.m_Input->m_Makes & Inp::Data::mD_L1 )
		{
			mdl.m_commands.SetMask( mRAISE_FLOOR );
		}
		if( handler.m_Input->m_Makes & Inp::Data::mD_R1 )
		{
			mdl.m_commands.SetMask( mLOWER_FLOOR );
		}
	}
#else
	if( handler.m_Input->m_Makes & Inp::Data::mD_L1 )
	{
		mdl.m_commands.SetMask( mRAISE_FLOOR );
	}
	if( handler.m_Input->m_Makes & Inp::Data::mD_L2 )
	{
		mdl.m_commands.SetMask( mLOWER_FLOOR );
	}
#endif		// __PLAT_NGC__
#ifdef __PLAT_NGC__
	if( ( handler.m_Input->m_Makes & Inp::Data::mD_UP ) && ( handler.m_Input->m_Buttons & Inp::Data::mD_R1 ) )
	{
		mdl.m_commands.SetMask( mINCREASE_GAP_RIGHT );
	}
	if( ( handler.m_Input->m_Makes & Inp::Data::mD_DOWN ) && ( handler.m_Input->m_Buttons & Inp::Data::mD_R1 ) )
	{
		mdl.m_commands.SetMask( mDECREASE_GAP_RIGHT );
	}
	if( ( handler.m_Input->m_Makes & Inp::Data::mD_UP ) && ( handler.m_Input->m_Buttons & Inp::Data::mD_L1 ) )
	{
		mdl.m_commands.SetMask( mINCREASE_GAP_LEFT );
	}
	if( ( handler.m_Input->m_Makes & Inp::Data::mD_DOWN ) && ( handler.m_Input->m_Buttons & Inp::Data::mD_L1 ) )
	{
		mdl.m_commands.SetMask( mDECREASE_GAP_LEFT );
	}
#else
	#ifdef __PLAT_XBOX__
		if( handler.m_Input->m_Makes & Inp::Data::mD_L1 )
		{
			mdl.m_commands.SetMask( mINCREASE_GAP_RIGHT );
		}
		if( handler.m_Input->m_Makes & Inp::Data::mD_L2 )
		{
			mdl.m_commands.SetMask( mDECREASE_GAP_RIGHT );
		}
		if( handler.m_Input->m_Makes & Inp::Data::mD_BLACK )
		{
			mdl.m_commands.SetMask( mINCREASE_GAP_LEFT );
		}
		if( handler.m_Input->m_Makes & Inp::Data::mD_WHITE )
		{
			mdl.m_commands.SetMask( mDECREASE_GAP_LEFT );
		}
	#else
		if( handler.m_Input->m_Makes & Inp::Data::mD_R1 )
		{
			mdl.m_commands.SetMask( mINCREASE_GAP_RIGHT );
		}
		if( handler.m_Input->m_Makes & Inp::Data::mD_R2 )
		{
			mdl.m_commands.SetMask( mDECREASE_GAP_RIGHT );
		}
		if( handler.m_Input->m_Makes & Inp::Data::mD_L1 )
		{
			mdl.m_commands.SetMask( mINCREASE_GAP_LEFT );
		}
		if( handler.m_Input->m_Makes & Inp::Data::mD_L2 )
		{
			mdl.m_commands.SetMask( mDECREASE_GAP_LEFT );
		}
	#endif
	
#endif		// __PLAT_NGC__
#ifdef __PLAT_NGC__
	if( handler.m_Input->m_Buttons & Inp::Data::mD_Z )
	{
		if( handler.m_Input->m_Buttons & Inp::Data::mD_L1 )
		{
			mdl.m_commands.SetMask( mZOOM_CAMERA_IN );
		}
		if( handler.m_Input->m_Buttons & Inp::Data::mD_R1 )
		{
			mdl.m_commands.SetMask( mZOOM_CAMERA_OUT );
		}
	}
#else
#ifdef __PLAT_XBOX__
	if( handler.m_Input->m_Buttons & Inp::Data::mD_BLACK )
	{
		mdl.m_commands.SetMask( mZOOM_CAMERA_OUT );
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_WHITE )
	{
		mdl.m_commands.SetMask( mZOOM_CAMERA_IN );
	}
#else
	if( handler.m_Input->m_Buttons & Inp::Data::mD_R1 )
	{
		mdl.m_commands.SetMask( mZOOM_CAMERA_OUT );
	}
	if( handler.m_Input->m_Buttons & Inp::Data::mD_R2 )
	{
		mdl.m_commands.SetMask( mZOOM_CAMERA_IN );
	}
#endif		// __PLAT_XBOX__	
#endif		// __PLAT_NGC__
}




void CParkEditor::s_logic_code ( const Tsk::Task< CParkEditor >& task )
{
	

	CParkEditor &mdl = task.GetData();
	//if (mdl.mp_commandParams)
	//	mdl.runCommand();	
	//if (!mdl.mp_map || mdl.GameGoingOrOutsideEditor() || !mdl.m_initialized) return;

	/*
	// put skater up in air HACK
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	Obj::CSkater *pSkater = skate_mod->GetLocalSkater();
	if (pSkater)
		pSkater->m_pos.Set(4000.0f, 4000.0f, 0.0f);
	*/
	
	Tmr::Time elapsed_time;
	static Tmr::Time last_time = 0;
	
	elapsed_time = Tmr::ElapsedTime( last_time );
	last_time = last_time + elapsed_time;   
	
	mdl.Update();
}




void CParkEditor::s_display_code ( const Tsk::Task< CParkEditor >& task )
{
	//CParkEditor &mdl = task.GetData();
	//if (!mdl.mp_map || mdl.GameGoingOrOutsideEditor() || !mdl.m_initialized) return;
	
	//mdl.Draw();
}

void CParkEditor::BindParkEditorToController( int controller )
{
	Spt::SingletonPtr< Inp::Manager > inp_manager;
	inp_manager->ReassignHandler( *m_input_handler, controller );
}

Mth::Vector& CParkEditor::GetCursorPos()
{
	Dbg_MsgAssert(mp_cursor,("NULL mp_cursor"));
	return mp_cursor->m_pos;
}

void CParkEditor::DestroyClipboardsWhichAreTooBigToFit()
{
	if (mp_cursor)
	{
		mp_cursor->DestroyClipboardsWhichAreTooBigToFit();
	}	
}

void CParkEditor::SetTimeOfDayScript(uint32 tod_script)
{
	m_tod_script=tod_script;
}	

CCursor *CCursor::sp_instance = NULL;
const float CCursor::MOTION_PCT_INC = .15f;
const float CCursor::ROT_DEG_INC = 15.0f;




CCursor::CCursor() :
	m_cell_dims(0, 0, 0),
	m_target_pos(0.0f, 0.0f, 0.0f),
	m_pos(0.0f, 0.0f, 0.0f),
	m_hard_rot(Mth::ROT_0),
	m_target_rot(0.0f),
	m_rot(0.0f),
	mp_meta(NULL)
{
	m_num_corners_selected=0;
	m_initialised_highlight=false;
	
	mp_clipboards=NULL;
	mp_current_clipboard=NULL;
	
	m_hard_rot = Mth::ROT_0;

	m_motion_pct = 1.0f;

	m_selected_set = 0;
	m_menu_set_number = 0;
	
	m_current_gap.numCompleteHalves = 0;
	m_current_gap.mCancelFlags=0;
	
	m_mode = NONE;
	
	CParkManager::CPieceSet &piece_set = m_manager->GetPieceSet(m_selected_set, &m_menu_set_number);
	set_source_meta(piece_set.mEntryTab[piece_set.mSelectedEntry].mNameCrc);

	m_gap_suffix_counter=1;
	
	sp_instance = this;
}

CCursor::~CCursor()
{
	#ifndef DONT_USE_CURSOR_META
	if (mp_meta)
	{
		mp_meta->MarkUnlocked();
		m_manager->DestroyConcreteMeta(mp_meta, CParkManager::mDONT_DESTROY_PIECES_ABOVE);
	}
	
	#endif

	destroy_clipboards();
	
	sp_instance = NULL;
}

// K: Added to test something
void CCursor::DeleteMeta()
{
	if (mp_meta)
	{
		delete mp_meta;
		mp_meta=NULL;
	}
}
		
void CCursor::destroy_clipboards()
{
	CClipboard *p_clipboard=mp_clipboards;
	while (p_clipboard)
	{
		CClipboard *p_next=p_clipboard->mpNext;
		delete p_clipboard;
		p_clipboard=p_next;
	}	
	mp_current_clipboard=NULL;
}

// Called when the park is resized.
void CCursor::DestroyClipboardsWhichAreTooBigToFit()
{
	CClipboard *p_last=NULL;
	CClipboard *p_clipboard=mp_clipboards;
	while (p_clipboard)
	{
		CClipboard *p_next=p_clipboard->mpNext;
		
		GridDims area;
		p_clipboard->GetArea(this,&area);

		if (area.GetW() > m_manager->GetParkNearBounds().GetW() ||
			area.GetW() > m_manager->GetParkNearBounds().GetL() ||
			area.GetL() > m_manager->GetParkNearBounds().GetW() ||
			area.GetL() > m_manager->GetParkNearBounds().GetL())
		{
			if (p_last)
			{
				p_last->mpNext=p_next;
			}	
			if (p_clipboard==mp_clipboards)
			{
				mp_clipboards=p_next;
			}
				
			delete p_clipboard;
			p_clipboard=p_next;
		}
		else
		{
			p_last=p_clipboard;
			p_clipboard=p_next;
		}	
	}	
	
	mp_current_clipboard=mp_clipboards;

	// Refresh since mp_current_clipboard has changed.
	ChangePieceInSet(0);
}

void CCursor::DestroyGeometry()
{
	if (mp_meta)
	{
		mp_meta->MarkUnlocked();
		m_manager->DestroyConcreteMeta(mp_meta, CParkManager::mDONT_DESTROY_PIECES_ABOVE);
		mp_meta = NULL;
	}
}

void CCursor::DestroyAnyClipboardCursors()
{
	CClipboard *p_clipboard=mp_clipboards;
	while (p_clipboard)
	{
		p_clipboard->DestroyMetas();
		p_clipboard=p_clipboard->mpNext;
	}	
}

void CCursor::Update(float shiftX, float shiftZ, int rotInc)
{
	/*
		==================================================
		ROTATION SECTION
		==================================================
	*/
	
	if (rotInc > 0)
	{
		m_hard_rot = Mth::ERot90((m_hard_rot + 1) & 3);
		m_target_rot += 90.0f;
		if (m_target_rot > 360.0f)
		{
			m_target_rot -= 360.0f;
			m_rot -= 360.0f;
		}
		m_cell_dims.SetWHL(m_cell_dims.GetL(), m_cell_dims.GetH(), m_cell_dims.GetW());
	}
	else if (rotInc < 0)
	{
		m_hard_rot = Mth::ERot90((m_hard_rot - 1) & 3);
		m_target_rot -= 90.0f;
		if (m_target_rot < 0.0f)
		{
			m_target_rot += 360.0f;
			m_rot += 360.0f;
		}
		m_cell_dims.SetWHL(m_cell_dims.GetL(), m_cell_dims.GetH(), m_cell_dims.GetW());
	}

	/*
		==================================================
		CELL POSITIONING SECTION
		==================================================
	*/
	
	const float near_zero = .0001f;
	//const float tolerance = .5f;
	
	float shift_x_squared = shiftX * shiftX;
	float shift_z_squared = shiftZ * shiftZ;

	bool actual_shift = (shift_x_squared > near_zero * near_zero || shift_z_squared > near_zero * near_zero);
	
	// can't shift to new cell if cursor currently in motion
	if (actual_shift && m_motion_pct <= 0.0f)
	{
		if (shift_z_squared > 5.8284f * shift_x_squared)
		{
			if (shiftZ < 0.0f)
				change_cell_pos(0, -1);
			else
				change_cell_pos(0, 1);
			m_motion_pct = 1.0f;
		}
		else if (shift_x_squared > 5.8284f * shift_z_squared)
		{
			if (shiftX < 0.0f)
				change_cell_pos(-1, 0);
			else
				change_cell_pos(1, 0);
			m_motion_pct = 1.0f;
		}
		else
		{
			if (shiftZ < 0.0f)
				change_cell_pos(0, -1);
			else
				change_cell_pos(0, 1);
	
			if (shiftX < 0.0f)
				change_cell_pos(-1, 0);
			else
				change_cell_pos(1, 0);
			m_motion_pct = 1.0f;
		}
	}
	else
	{
		change_cell_pos(0, 0);
	}
	
	#if 0
	printf("cell (%d,%d), shown (%.2f,%.2f)\n", 
		   m_cell_dims.GetX(), m_cell_dims.GetZ(), 
		   m_pos[X], m_pos[Z]);
	#endif
	
	/*
		==================================================
		ANIMATION SECTION
		==================================================
	*/

	//m_cell_dims.PrintContents();
	
	// derive m_target_pos from m_cell_dims
	
	Mth::Vector world_dims;
	m_target_pos = m_manager->GridCoordinatesToWorld(m_cell_dims, &world_dims);
	m_target_pos[X] += world_dims[X] / 2.0f;
	m_target_pos[Z] += world_dims[Z] / 2.0f;

	if (m_motion_pct > 0.0f)
	{
		m_pos[X] += (m_target_pos[X] - m_pos[X]) * MOTION_PCT_INC / m_motion_pct;
		m_pos[Z] += (m_target_pos[Z] - m_pos[Z]) * MOTION_PCT_INC / m_motion_pct;
		m_pos[Y] = m_target_pos[Y];
		m_motion_pct -= MOTION_PCT_INC;
		if (m_motion_pct < 0.0f) m_motion_pct = 0.0f;
	}
	else
	{
		m_pos = m_target_pos;
		m_motion_pct = 0.0f;
	}
	
	if (m_rot < m_target_rot)
	{
		m_rot += ROT_DEG_INC;
		if (m_rot > m_target_rot)
			m_rot = m_target_rot;
	}
	else if (m_rot > m_target_rot)
	{
		m_rot -= ROT_DEG_INC;
		if (m_rot < m_target_rot)
			m_rot = m_target_rot;
	}

	/*	
	Script::CStruct *p_params=new Script::CStruct;
	p_params->AddVector(CRCD(0x7f261953,"pos"),m_pos[X],m_pos[Y],m_pos[Z]);
	Script::RunScript(CRCD(0x9796fc9f,"ParkEdCursorPos"),p_params);
	delete p_params;
	*/
	
	#ifndef DONT_USE_CURSOR_META
	// Mick:  TT#3804
	// Adding the vector fixes Z-Fighting between cursor and identical piece
	if (m_mode==PASTE)
	{
		if (mp_current_clipboard)
		{
			//printf("m_clipboard_y=%d\n",m_clipboard_y);
			mp_current_clipboard->ShowMetas(m_pos + Mth::Vector(-1.5f,+1.5f,-1.5f), m_rot, m_clipboard_y);
		}	
	}
	else
	{
		DestroyAnyClipboardCursors();
		if (m_mode != RAIL_PLACEMENT)
		{
			Dbg_Assert(mp_meta);
			mp_meta->SetSoftRot(m_pos + Mth::Vector(-1.5f,+1.5f,-1.5f), m_rot);		  // Mick: Changed to move up, so water does not vanish on GC
		}	
	}	
	#endif
}



bool CCursor::AttemptStamp()
{
	Dbg_Assert(mp_meta);
	ParkEd("CCursor::AttemptStamp()");
	
	if (!m_manager->CanPlacePiecesIn(m_cell_dims))
	{
		return false;
	}
	
	// If we are trying to put down a restart point then we need to see if there are 
	// any free slots avaiable	
	CParkGenerator::RestartType restart_type = m_manager->IsRestartPiece(mp_meta->GetNameChecksum());
	if (restart_type != CParkGenerator::vEMPTY)
	{
		//printf("Attempting to stamp a restart piece type %d\n", restart_type);			
		if (! m_manager->GetGenerator()->FreeRestartExists(restart_type))
		{
			// no free slots for this restart point
			printf ("No free slots exist for this type of restart point\n");
			return false;
		}
	}
		
	#ifndef DONT_USE_CURSOR_META
	CAbstractMetaPiece *p_abstract = m_manager->GetAbstractMeta(mp_meta->GetNameChecksum());
	Dbg_MsgAssert(p_abstract, ("couldn't find abstract piece %s", Script::FindChecksumName(mp_meta->GetNameChecksum())));
	CConcreteMetaPiece *p_concrete = m_manager->CreateConcreteMeta(p_abstract);
	p_concrete->SetRot(m_hard_rot);
	m_manager->AddMetaPieceToPark(m_cell_dims, p_concrete);
	#endif
	
	m_manager->RebuildFloor();
	
	return true;
}

bool CCursor::AttemptRemove()
{
	ParkEd("CCursor::AttemptRemove()");
	
	GridDims area;
	if (m_mode==PASTE)
	{
		if (mp_current_clipboard)
		{
			mp_current_clipboard->GetArea(this,&area);
		}	
		else
		{
			return false;
		}	
	}
	else
	{
		area=m_cell_dims;
	}	
	
	return m_manager->DestroyMetasInArea(area);
}




/*
	A callback called by CParkManager::DestroyConcreteMeta()
*/
void CCursor::InformOfMetapieceDeletion(CConcreteMetaPiece *pMeta)
{
	int gap_half;
	GridDims meta_area = pMeta->GetArea();
	CGapManager::GapDescriptor *p_gap_desc = m_manager->GetGapDescriptor(meta_area, &gap_half);
	if (p_gap_desc && is_gap_descriptor_same_as_current(p_gap_desc))
		m_current_gap.numCompleteHalves = 0;
}




bool CCursor::AttemptGap()
{
	Spt::SingletonPtr p_editor;
	if (p_editor->IsParkFull())
		return false;
	
	CMapListTemp meta_list = m_manager->GetMetaPiecesAt(m_cell_dims);
	if (meta_list.IsEmpty()) 
	{
		// XXX
		Ryan("no gap for you!!!!\n");
		return false;
	}
	CConcreteMetaPiece *p_meta = meta_list.GetList()->GetConcreteMeta();
	// XXX
	Ryan("attempting gap at: ");
	m_cell_dims.PrintContents();
	
	int half;
	CGapManager::GapDescriptor *pDesc = m_manager->GetGapDescriptor(m_cell_dims, &half);
	if (pDesc)
	{
		// gap already attached at this location
		if (m_current_gap.numCompleteHalves == 1)
		{
			// user already working on gap, so fail
			return false;
		}
	}
	else if (p_meta)
	{
		// metapiece found at this location without gap attached
		if (m_current_gap.numCompleteHalves == 1)
			// remove gap half placed so far. It'll get rebuilt later.
			m_manager->RemoveGap(m_current_gap);
		else
			// make sure not 2
			m_current_gap.numCompleteHalves = 0;

		int active_gap_half = m_current_gap.numCompleteHalves;
		
		GridDims gap_area = p_meta->GetArea();
		
		// start gap 
		m_current_gap.loc[active_gap_half] = gap_area;
		m_current_gap.rot[active_gap_half] = 0;
		m_current_gap.leftExtent[active_gap_half] = 0;
		m_current_gap.rightExtent[active_gap_half] = 0;
		m_current_gap.score = 100;
		m_current_gap.numCompleteHalves = active_gap_half + 1;
		m_current_gap.tabIndex = -1;
	
		if (active_gap_half == 0)
		{
			// new gap, so give default name
			if (m_gap_suffix_counter > 1)
			{
				// K: Make sure the default names are different otherwise when registered in the 
				// CSkaterCareer it will not get added cos it will think it is the same gap
				// as the last.
				sprintf(m_current_gap.text, "gap%d",m_gap_suffix_counter);
			}
			else
			{
				strcpy(m_current_gap.text, "gap");
			}
			++m_gap_suffix_counter;
		}
		
		if (!m_manager->AddGap(m_current_gap))
		{
			m_current_gap.numCompleteHalves = 0;
			return false;
		}
		m_manager->RebuildFloor();
		if (m_current_gap.numCompleteHalves > 0)
			m_manager->HighlightMetasWithGaps(true, &m_current_gap);

		return true;
	}	
	
	return false;
}




bool CCursor::AttemptRemoveGap()
{
	if (m_current_gap.numCompleteHalves == 1)
	{
		// remove gap being worked on
		m_manager->RemoveGap(m_current_gap);
		m_current_gap.numCompleteHalves = 0;
		m_manager->RebuildFloor();
		return true;
	}

	int half;
	CGapManager::GapDescriptor *pDesc = m_manager->GetGapDescriptor(m_cell_dims, &half);
	if (pDesc)
	{
		// remove gap at this position
		m_manager->RemoveGap(*pDesc);
		m_manager->RebuildFloor();
		m_manager->HighlightMetasWithGaps(true, NULL);
		return true;
	}

	return false;
}




bool CCursor::AdjustGap(int rot, int leftChange, int rightChange)
{
	// grab gap at this location
	int half;
	CGapManager::GapDescriptor *p_desc = m_manager->GetGapDescriptor(m_cell_dims, &half);
	if (!p_desc)
	{
		// no gap, so leave
		return false;
	}

	if (!is_gap_descriptor_same_as_current(p_desc))
		// we are currently working on a gap and it's not the same as the one at this location, so fail
		return false;
	
	CGapManager::GapDescriptor desc_temp = *p_desc;
	bool needs_change = false;

	if (rot != 0)
	{
		desc_temp.rot[half] -= rot;
		desc_temp.leftExtent[half] = 0;
		desc_temp.rightExtent[half] = 0;
		needs_change = true;
	}

	if (leftChange != 0)
	{
		desc_temp.leftExtent[half] += leftChange;
		if (desc_temp.leftExtent[half] < 0)
			desc_temp.leftExtent[half] = 0;
		else if (desc_temp.leftExtent[half] > 6)
			desc_temp.leftExtent[half] = 6;
		needs_change = true;
	}
	
	if (rightChange != 0)
	{
		desc_temp.rightExtent[half] += rightChange;
		if (desc_temp.rightExtent[half] < 0)
			desc_temp.rightExtent[half] = 0;
		if (desc_temp.rightExtent[half] > 6)
			desc_temp.rightExtent[half] = 6;
		needs_change = true;
	}

	if (needs_change)
	{
		m_manager->RemoveGap(*p_desc);
		desc_temp.rot[half] &= 3;

		GridDims park_bounds = m_manager->GetParkNearBounds();
		
		// make sure gap boundaries are within map
		if (desc_temp.rot[half] == 0)
		{
			if (desc_temp.loc[half].GetZ() - desc_temp.rightExtent[half] < park_bounds.GetZ())
				desc_temp.rightExtent[half]--;
			if (desc_temp.loc[half].GetZ() + desc_temp.loc[half].GetL() + desc_temp.leftExtent[half] > park_bounds.GetZ() + park_bounds.GetL())
				desc_temp.leftExtent[half]--;
		}
		else if (desc_temp.rot[half] == 2)
		{
			if (desc_temp.loc[half].GetZ() - desc_temp.leftExtent[half] < park_bounds.GetZ())
				desc_temp.leftExtent[half]--;
			if (desc_temp.loc[half].GetZ() + desc_temp.loc[half].GetL() + desc_temp.rightExtent[half] > park_bounds.GetZ() + park_bounds.GetL())
				desc_temp.rightExtent[half]--;
		}
		else if (desc_temp.rot[half] == 1)
		{
			if (desc_temp.loc[half].GetX() - desc_temp.rightExtent[half] < park_bounds.GetX())
				desc_temp.rightExtent[half]--;
			if (desc_temp.loc[half].GetX() + desc_temp.loc[half].GetW() + desc_temp.leftExtent[half] > park_bounds.GetX() + park_bounds.GetW())
				desc_temp.leftExtent[half]--;
		}
		else if (desc_temp.rot[half] == 3)
		{
			if (desc_temp.loc[half].GetX() - desc_temp.leftExtent[half] < park_bounds.GetX())
				desc_temp.leftExtent[half]--;
			if (desc_temp.loc[half].GetX() + desc_temp.loc[half].GetW() + desc_temp.rightExtent[half] > park_bounds.GetX() + park_bounds.GetW())
				desc_temp.rightExtent[half]--;
		}

		// XXX
		for (half = 0; half < 2; half++)
			Ryan("gap details for half %d (%d,%d,%d),(%d,%d,%d) left=%d right=%d\n", half,
				 desc_temp.loc[half].GetX(), desc_temp.loc[half].GetY(), desc_temp.loc[half].GetZ(),
				 desc_temp.loc[half].GetW(), desc_temp.loc[half].GetH(), desc_temp.loc[half].GetL(),
				 desc_temp.leftExtent[half], desc_temp.rightExtent[half]);

		m_manager->AddGap(desc_temp);
		m_manager->RebuildFloor();
		
		if (m_current_gap.numCompleteHalves == 1)
		{
			m_current_gap = desc_temp;
		}
	}

	return true;
}




void CCursor::SetGapNameAndScore(const char *pGapName, int score)
{
	if (pGapName)
	{
		Dbg_MsgAssert(strlen(pGapName) <= 31, ("too many characters in gap name"));
		if (*pGapName == '\0')
			strcpy(m_current_gap.text, "x");
		else
			strcpy(m_current_gap.text, pGapName);
	}
	if (score >= 0)
	{
		m_current_gap.score = score;
	}
	Dbg_Assert(m_current_gap.tabIndex >= 0);
	CGapManager::sInstance()->SetGapInfo(m_current_gap.tabIndex, m_current_gap);
}

void CCursor::SetGapCancelFlags(Script::CStruct *p_cancelFlags)
{
	if (!p_cancelFlags)
	{
		return;
	}
	
	uint32 cancel_flags=0;
	Script::CComponent *p_comp=p_cancelFlags->GetNextComponent();
	while (p_comp)
	{
		Dbg_MsgAssert(p_comp->mNameChecksum==0 && p_comp->mType==ESYMBOLTYPE_NAME,("Gap cancel flag structure must contain only names"));
		cancel_flags |= Script::GetInteger(p_comp->mChecksum);
		p_comp=p_cancelFlags->GetNextComponent(p_comp);
	}	
	
	m_current_gap.mCancelFlags=cancel_flags;
	
	Dbg_Assert(m_current_gap.tabIndex >= 0);
	CGapManager::sInstance()->SetGapInfo(m_current_gap.tabIndex, m_current_gap);
}


const char *CCursor::GetGapName()
{
	return m_current_gap.text;
}


int CCursor::AttemptAreaSelect()
{
	Dbg_MsgAssert(m_mode==AREA_SELECT,("Called AttemptAreaSelect() with m_mode!=AREA_SELECT"));
	Dbg_MsgAssert(m_num_corners_selected<=2,("Bad m_num_corners_selected of %d",m_num_corners_selected));

	if (m_num_corners_selected==0)
	{
		mp_area_corners[0]=m_cell_dims;
		mp_area_corners[1]=m_cell_dims;
		m_num_corners_selected=1;
		ForceSelectionAreaHighlightRefresh();
	}
	else if (m_num_corners_selected==1)
	{
		GridDims area;
		GetAreaSelectDims(&area);
		if (area.GetW() <= CLIPBOARD_MAX_W && area.GetL() <= CLIPBOARD_MAX_L)
		{
			m_num_corners_selected=2;
		}	
	}
	
	return m_num_corners_selected;
}

void CCursor::ClearAreaSelection()
{
	GridDims area;
	GetAreaSelectDims(&area);
	
	m_manager->HighlightMetasInArea(area,false);
	
	m_num_corners_selected=0;
	m_initialised_highlight=false;
}

void CCursor::ContinueAreaSelection()
{
	m_num_corners_selected=1;
	// Need to force it to re-highlight the selection area next time around
	// because when quitting the paused menu something resets all the highlights.
	ForceSelectionAreaHighlightRefresh();
}

void CCursor::DeleteSelectedPieces()
{
	if (!m_num_corners_selected)
	{
		return;
	}
		
	GridDims dims;
	GetAreaSelectDims(&dims);

	// First true means do not destroy any flags or restarts
	// Second true means only destroy if completely within the area.
	m_manager->DestroyMetasInArea(dims, true, true);

	ContinueAreaSelection();
}

void CCursor::ResetSelectedHeights()
{
	if (!m_num_corners_selected)
	{
		return;
	}
		
	GridDims dims;
	GetAreaSelectDims(&dims);

	m_manager->ResetFloorHeights(dims);
	
	ContinueAreaSelection();
}
	
void CCursor::get_selected_area_coords(uint8 *p_x1, uint8 *p_z1, uint8 *p_x2, uint8 *p_z2)
{
	uint8 x1=mp_area_corners[0].GetX();
	uint8 z1=mp_area_corners[0].GetZ();
	uint8 x2=mp_area_corners[1].GetX();
	uint8 z2=mp_area_corners[1].GetZ();
	
	if (x2 < x1)
	{
		uint8 temp=x1;
		x1=x2;
		x2=temp;
	}

	if (z2 < z1)
	{
		uint8 temp=z1;
		z1=z2;
		z2=temp;
	}
	
	*p_x1=x1;
	*p_z1=z1;
	*p_x2=x2;
	*p_z2=z2;
}

void CCursor::GetAreaSelectDims(GridDims *p_dims)
{
	if (m_num_corners_selected==0)
	{
		p_dims->SetXYZ(0,0,0);
		p_dims->SetWHL(0,0,0);
		return;
	}	
	
	uint8 x1,z1,x2,z2;
	get_selected_area_coords(&x1,&z1,&x2,&z2);
	
	p_dims->SetXYZ(x1,0,z1);
	p_dims->SetWHL(x2-x1+1,1,z2-z1+1);
}

bool CCursor::DestroyMetasInCurrentArea()
{
	GridDims area;
	GetAreaSelectDims(&area);

	return CParkManager::sInstance()->DestroyMetasInArea(area);
}

void CCursor::DeleteOldestClipboard()
{
	// Find p_last, the last clipboard in the list.
	CClipboard *p_clip=mp_clipboards;
	CClipboard *p_last=NULL;
	CClipboard *p_next_to_last=NULL;
	while (p_clip)
	{
		p_next_to_last=p_last;
		p_last=p_clip;
		
		p_clip=p_clip->mpNext;
	}
	if (!p_last)
	{
		// Cannot do anything because there are no clipboards to delete
		return;
	}
		
	delete p_last;
	
	if (p_next_to_last)
	{
		p_next_to_last->mpNext=NULL;
	}
	else
	{
		mp_clipboards=NULL;
	}
}

bool CCursor::SelectionAreaTooBigToCopy()
{
	GridDims area;
	GetAreaSelectDims(&area);

	if (area.GetW() > CLIPBOARD_MAX_W || area.GetL() > CLIPBOARD_MAX_L)
	{
		return true;
	}
	
	Spt::SingletonPtr p_editor;
	if (!p_editor->RoomToCopyOrPaste(area.GetW(), area.GetL()))
	{
		return true;
	}	
	
	return false;
}
		
bool CCursor::CopySelectionToClipboard()
{
	if (SelectionAreaTooBigToCopy())
	{
		return false;
	}
		
	GridDims area;
	GetAreaSelectDims(&area);
	
	// TODO TODO TODO
	// If not enough rail points to copy the rails, check to see if any of the existing clipboards contain
	// rails. If so, delete enough of them to free up enough points. If still not enough points, bail out.
	// If enough, carry on with the next loop.
	Mth::Vector corner_pos;
	Mth::Vector area_dims;
	corner_pos=Ed::CParkManager::Instance()->GridCoordinatesToWorld(area,&area_dims);
	int num_rail_points_needed=Obj::GetRailEditor()->CountPointsInArea(corner_pos[X], corner_pos[Z], 
																	   corner_pos[X]+area_dims[X], corner_pos[Z]+area_dims[Z]);
	if (num_rail_points_needed > Obj::GetRailEditor()->GetNumFreePoints())
	{
		return false;
	}	



	if (GetNumClipboards()==MAX_CLIPBOARDS)
	{
		// All the CClipboard's in the pool have already been allocated.
		
		// Find the oldest clipboard and delete it.
		DeleteOldestClipboard();
		
		// That will have freed up space for one more CClipboard
		// so the new CClipboard below should not assert.
		// If it does then something strange is going on, like MAX_CLIPBOARDS is 0 or something.
	}
		
	CClipboard *p_clipboard=new CClipboard;

	
	
	// CopySelectionToClipboard will use up CClipboardEntry's, of which there are only
	// a certain number allocated in a pool.
	// So CopySelectionToClipboard may fail, even though the CClipboard pool may not be full.
	
	// Keep deleting old clipboards until CopySelectionToClipboard succeeds.
	// Note that the above newly allocated p_clipboard has not been put in the list
	// yet so it will not get deleted by accident here.
	while (!p_clipboard->CopySelectionToClipboard(area))
	{
		if (!mp_clipboards)
		{
			// Run out of clipboards to delete! If this happens then CopySelectionToClipboard
			// must be failing because the total number of pieces the user wants to copy 
			// exceeds the total number of CClipboardEntry's available in the pool, which
			// is defined by MAX_CLIPBOARD_METAS in clipboard.h
			break;
		}			
		DeleteOldestClipboard();
	}	
	
	if (p_clipboard->IsEmpty())
	{
		// Nothing got copied into the clipboard, which means either the user tried to copy a 
		// blank area or the above attempts at copying failed due to lack of memory.
		
		delete p_clipboard;
		p_clipboard=NULL;
		
		return false;
	}	
	
	// Add the new clipboard to the list.
	p_clipboard->mpNext=mp_clipboards;
	mp_clipboards=p_clipboard;
	return true;
}	

int CCursor::GetNumClipboards()
{
	int n=0;
	CClipboard *p_clipboard=mp_clipboards;
	while (p_clipboard)
	{
		++n;
		p_clipboard=p_clipboard->mpNext;
	}
	return n;	
}

void CCursor::PasteCurrentClipboard()
{
	if (mp_current_clipboard)
	{
		mp_current_clipboard->Paste(this);
	}
}

CClipboard *CCursor::get_clipboard(int index)
{
	CClipboard *p_clipboard=mp_clipboards;
	
	for (int i=0; impNext;
		}
		else
		{
			break;
		}	
	}
	
	return p_clipboard;
}

void CCursor::set_current_clipboard(int index)
{
	DestroyAnyClipboardCursors();		
	mp_current_clipboard=get_clipboard(index);
	
	/*
	if (mp_current_clipboard)
	{
		m_cell_dims.SetWHL(mp_current_clipboard->GetWidth(),1,mp_current_clipboard->GetLength());
		if (m_hard_rot & 1)
		{
			m_cell_dims.SetWHL(m_cell_dims.GetL(), m_cell_dims.GetH(), m_cell_dims.GetW());
		}
	}	
	*/
}

void CCursor::remove_clipboard(int index)
{
	CClipboard *p_remove=get_clipboard(index);
	if (!p_remove)
	{
		return;
	}
	
	CClipboard *p_search=mp_clipboards;
	CClipboard *p_last=NULL;
	while (p_search)
	{
		if (p_search==p_remove)
		{
			if (p_last)
			{
				p_last->mpNext=p_remove->mpNext;
			}
			else
			{
				mp_clipboards=p_remove->mpNext;
			}
			delete p_remove;
			break;
		}
				
		p_last=p_search;
		p_search=p_search->mpNext;
	}	
}

void CCursor::RefreshSelectionArea()
{
	switch (m_num_corners_selected)
	{
	case 0:
		break;
	case 1:
		if (!m_initialised_highlight)
		{
			GridDims area;
			GetAreaSelectDims(&area);
			
			m_manager->HighlightMetasInArea(area,true);
			
			m_initialised_highlight=true;
		}	
		else
		{
			if (m_cell_dims.GetX() != mp_area_corners[1].GetX() ||
				m_cell_dims.GetZ() != mp_area_corners[1].GetZ())
			{
				GridDims area;
				GetAreaSelectDims(&area);

				m_manager->HighlightMetasInArea(area,false);

				mp_area_corners[1]=m_cell_dims;

				GetAreaSelectDims(&area);

				m_manager->HighlightMetasInArea(area,true);
			}
		}
		break;
	default:
		break;		
	}
}

// Find all the pieces intersecting the cursor
// and turn the highlight effect on or off
// (In THPS3, the highlight was a wireframe.  Here I think we are going to try coloring it red)
void CCursor::HighlightIntersectingMetas(bool on)
{
	if (m_mode==PASTE)
	{
		if (mp_current_clipboard)
		{
			mp_current_clipboard->HighlightIntersectingMetas(this,on);
			return;
		}	
	}
	
	// Get the list of (concrete) meta pieces that intersect the
	// dimension of the cursor's m_cell_dims
	CMapListTemp metas_at_pos = m_manager->GetMetaPiecesAt(m_cell_dims);
	if (metas_at_pos.IsEmpty())
	{
		// Nothing intersecting, so do nothing and return
		return;
	}

	// Iterate over the list of metapieces we just found, and highlight each one
	// (Whcih will actually highlight the pieces
	CMapListNode *p_entry = metas_at_pos.GetList();
	while(p_entry)
	{
		p_entry->GetConcreteMeta()->Highlight(on);		
		p_entry = p_entry->GetNext();
	}
}

void CCursor::SetVisibility(bool visible)
{
	//Dbg_Assert(mp_meta);
	if (mp_meta)
	{
		mp_meta->SetVisibility(visible);
	}	
	
	if (!visible)
	{
		if (mp_current_clipboard)
		{
			mp_current_clipboard->DestroyMetas();
		}
	}		
	
	m_initialised_highlight=false;
}



void CCursor::ChangePieceInSet(int dir)
{
	CParkManager::CPieceSet &piece_set = m_manager->GetPieceSet(m_selected_set, &m_menu_set_number);
	int selected_entry = piece_set.mSelectedEntry;
	selected_entry += dir;
	if (selected_entry < 0)
		selected_entry = piece_set.mTotalEntries - 1;
	else if (selected_entry >= piece_set.mTotalEntries)
		selected_entry = 0;
	piece_set.mSelectedEntry = selected_entry;

	if (piece_set.mIsClipboardSet)
	{
		set_current_clipboard(piece_set.mSelectedEntry);
	}
	else
	{
		set_source_meta(piece_set.mEntryTab[selected_entry].mNameCrc);
	}	
}

void CCursor::ChangeSet(int dir)
{
	for (int count = m_manager->GetTotalNumPieceSets(); count > 0; count--)
	{
		m_selected_set += dir;
		if (m_selected_set < 0)
			m_selected_set = m_manager->GetTotalNumPieceSets() - 1;
		if (m_selected_set >= m_manager->GetTotalNumPieceSets())
			m_selected_set = 0;
		//printf("set is: %s\n", m_palette_set[m_selected_set].mpName);
		if (m_manager->GetPieceSet(m_selected_set, &m_menu_set_number).mVisible) break;
	}
	CParkManager::CPieceSet &piece_set = m_manager->GetPieceSet(m_selected_set, &m_menu_set_number);
	if (piece_set.mIsClipboardSet)
	{
		set_current_clipboard(piece_set.mSelectedEntry);
		// Destroy the regular cursor meta
		DestroyGeometry();
		
		
		change_mode(PASTE);
	}
	else
	{
		set_source_meta(piece_set.mEntryTab[piece_set.mSelectedEntry].mNameCrc);
	}	
}

void CCursor::SwitchMenuPieceToMostRecentClipboard()
{
	if (!GetNumClipboards())
	{
		return;
	}
		
	int num_sets=m_manager->GetTotalNumPieceSets();
	CParkManager::CPieceSet *p_piece_set=NULL;
	
	for (int i=0; iGetPieceSet(i, &m_menu_set_number);
		if (piece_set.mIsClipboardSet)
		{
			m_selected_set=i;
			p_piece_set=&piece_set;
			break;
		}
	}
	Dbg_MsgAssert(p_piece_set,("Clipboard piece set not found?"));
	
	p_piece_set->mSelectedEntry=0;
	set_current_clipboard(p_piece_set->mSelectedEntry);
	// Destroy the regular cursor meta
	DestroyGeometry();
	change_mode(PASTE);
	
	// Fix to TT14349: Crash bug if copying tree pieces at the edge of the largest map by highlighting starting at the 
	// lower right corner of the set of trees.
	// Caused by the cursor being out of bounds for a frame, hence causing it to read outside the map array.
	ForceInBounds();
}

bool CCursor::ChangeFloorHeight(GridDims dims, int height, int dir, bool uniformHeight)
{
	//printf("CCursor::ChangeFloorHeight: ");
	
	/*
		Uneven floor:
			If going up, raise to height of topmost
			If going down, only lower ones on same level
	*/

	//printf("height=%d uniform_height=%d\n",height,uniformHeight);
	/*
		Now, 
	*/

	if (dims.GetW() > CLIPBOARD_MAX_W || dims.GetL() > CLIPBOARD_MAX_L)
	{
		return false;
	}
	
	//Spt::SingletonPtr p_editor;
	//if (p_editor->IsParkFull())
	//{
	//	return false;
	//}
		
	bool success = true;
	
		
	if (m_manager->SlideColumn(dims, dims.GetY(), height, (dir > 0), !uniformHeight))
	{
		//Ryan("Slide column, dir = %d, dims=", dir);
		//dims.PrintContents();
		
		change_cell_pos(0, 0);
		Spt::SingletonPtr p_editor;
		if (m_manager->RebuildFloor(p_editor->IsParkFull()))
		{
			m_manager->HighlightMetasWithGaps(false, NULL);
		}
		else
		{
			// out of memory, so reverse slide
			m_manager->UndoSlideColumn();
			m_manager->RebuildFloor();
			success = false;
		}
	}
	else
		success = false;

	return success;
}

bool CCursor::ChangeFloorHeight(int dir)
{
	GridDims dims;
	switch (m_mode)
	{
	case AREA_SELECT:
		if (m_num_corners_selected)
		{
			GetAreaSelectDims(&dims);
		}
		else
		{
			dims=m_cell_dims;
		}	
		break;
	case PASTE:
		if (mp_current_clipboard)
		{
			mp_current_clipboard->GetArea(this,&dims);
		}
		break;
	default:
		dims=m_cell_dims;
		break;
	}	

	// If the dims exceeds the area of the park, then do not raise or lower.
	// This can happen if the park is been resized to its smallest possible size and then a big pool
	// piece selected. If the raise was allowed to go ahead, it would result in the fence raising too. (TT11825)
	GridDims park_bounds = CParkManager::sInstance()->GetParkNearBounds();	
	if (dims.GetW() > park_bounds.GetW() || dims.GetL() > park_bounds.GetL())
	{
		return false;
	}	
	
	
	bool uniform_height;
	int height = m_manager->GetFloorHeight(dims, &uniform_height);

	// If pasting, destroy the clipboard metas, otherwise the dry run that ChangeFloorHeight
	// does to determine whether it is safe to raise or lower will incorrectly think it
	// is safe when it is not. I think the function generate_floor_pieces_from_height_map
	// gets confused by the presence of the clipboard metas. (TT7495 & TT6152)
	if (m_mode==PASTE)
	{
		if (mp_current_clipboard)
		{
			mp_current_clipboard->DestroyMetas();
		}
	}		
	bool ret_val=ChangeFloorHeight(dims,height,dir,uniform_height);
	
	if (m_mode==AREA_SELECT)
	{
		ForceSelectionAreaHighlightRefresh();
	}
	return ret_val;	
}




bool CCursor::IsSittingOnGap()
{
	int half;
	CGapManager::GapDescriptor *pDesc = m_manager->GetGapDescriptor(m_cell_dims, &half);
	if (pDesc)
		return true;
	return false;
}




CCursor *CCursor::sInstance(bool assert)
{
	Dbg_Assert(!assert || sp_instance);
	return sp_instance;
}




void CCursor::ForceInBounds()
{
	if (m_cell_dims[X] < m_manager->GetParkNearBounds().GetX())
	{
		m_cell_dims[X] = m_manager->GetParkNearBounds().GetX();
	}
	if (m_cell_dims[X] + m_cell_dims[W] > m_manager->GetParkNearBounds().GetX() + m_manager->GetParkNearBounds().GetW())
	{
		m_cell_dims[X] = m_manager->GetParkNearBounds().GetX() + m_manager->GetParkNearBounds().GetW() - m_cell_dims[W];
	}
	if (m_cell_dims[Z] < m_manager->GetParkNearBounds().GetZ())
	{
		m_cell_dims[Z] = m_manager->GetParkNearBounds().GetZ();
	}
	if (m_cell_dims[Z] + m_cell_dims[L] > m_manager->GetParkNearBounds().GetZ() + m_manager->GetParkNearBounds().GetL())
	{
		m_cell_dims[Z] = m_manager->GetParkNearBounds().GetZ() + m_manager->GetParkNearBounds().GetL() - m_cell_dims[L];
	}
	
	if (m_mode==PASTE)
	{
		if (mp_current_clipboard)
		{
			GridDims area;
			mp_current_clipboard->GetArea(this,&area);
						
			int dx=0;
			int park_x_left=m_manager->GetParkNearBounds().GetX();
			int park_x_right=m_manager->GetParkNearBounds().GetX()+m_manager->GetParkNearBounds().GetW()-1;
			sint8 x_left=area.GetX();
			if (x_left < park_x_left)
			{
				dx=park_x_left-x_left;
			}
			sint8 x_right=area.GetX();
			x_right+=area.GetW()-1;	
			if (x_right > park_x_right)
			{
				dx=park_x_right-x_right;
			}	
			m_cell_dims[X]+=dx;
			
			int dz=0;
			int park_z_top=m_manager->GetParkNearBounds().GetZ();
			int park_z_bottom=m_manager->GetParkNearBounds().GetZ()+m_manager->GetParkNearBounds().GetL()-1;
			
			sint8 z_top=area.GetZ();
			if (z_top < park_z_top)
			{
				dz=park_z_top-z_top;
			}
			sint8 z_bottom=area.GetZ();
			z_bottom+=area.GetL()-1;	
			if (z_bottom > park_z_bottom)
			{
				dz=park_z_bottom-z_bottom;
			}	
			
			m_cell_dims[Z]+=dz;
		}	
	}
	
}




void CCursor::change_cell_pos(int incX, int incZ)
{
	//printf("change_cell_pos:\n");
	//printf("Before:");
	//m_cell_dims.PrintContents();
	
	ForceInBounds();
	GridDims old_pos = m_cell_dims;
	
	m_cell_dims[X] += incX;
	m_cell_dims[Z] += incZ;

	ForceInBounds();

	if (m_mode==PASTE)
	{
		if (mp_current_clipboard)
		{
			m_clipboard_y=mp_current_clipboard->FindMaxFloorHeight(this);
			m_cell_dims[Y]=m_clipboard_y;
		}	
		else
		{
			m_cell_dims[Y]=0;
		}	
	}
	else
	{
		int current_floor_height = m_manager->GetFloorHeight(m_cell_dims);
		int floor_height_old_pos = m_manager->GetFloorHeight(old_pos);
		m_cell_dims[Y] = (current_floor_height > floor_height_old_pos) ? current_floor_height : floor_height_old_pos;
	}
		
	//Ryan("cursor at %d,%d\n", m_cell_dims[X], m_cell_dims[Z]);	 

	//printf("After:");
	//m_cell_dims.PrintContents();
	
	#if 0
	printf("park bounds: %d,%d,%d,%d, cell_dims: %d,%d\n", 
		   m_manager->GetParkNearBounds().GetX(), m_manager->GetParkNearBounds().GetZ(),
		   m_manager->GetParkNearBounds().GetW(), m_manager->GetParkNearBounds().GetL(),
		   m_cell_dims[W], m_cell_dims[L]);
	#endif
	
	if (m_mode == GAP || m_mode == GAP_ADJUST)
	{
		ECursorMode new_mode = GAP;
		
		// see if cursor is over a metapiece with attached gap
		CMapListTemp meta_list = m_manager->GetMetaPiecesAt(m_cell_dims);
		if (!meta_list.IsEmpty())
		{
			int half;
			GridDims area = meta_list.GetList()->GetConcreteMeta()->GetArea();
			CGapManager::GapDescriptor *p_desc = m_manager->GetGapDescriptor(area, &half);
			if (p_desc)
			{
				if (m_current_gap.numCompleteHalves != 1)
				{
					m_current_gap = *p_desc;
				}
				new_mode = GAP_ADJUST;
			}
		}
		
		change_mode(new_mode);
		
		if (m_current_gap.numCompleteHalves > 0)
			m_manager->HighlightMetasWithGaps(true, &m_current_gap);
		else
			m_manager->HighlightMetasWithGaps(true, NULL);
	}
}
	



void CCursor::set_source_meta(uint32 nameChecksum)
{
	#ifndef DONT_USE_CURSOR_META
	if (mp_meta)
	{
		mp_meta->MarkUnlocked();
		m_manager->DestroyConcreteMeta(mp_meta, CParkManager::mDONT_DESTROY_PIECES_ABOVE);
		mp_meta = NULL;
	}
	
	CAbstractMetaPiece *p_source_meta = m_manager->GetAbstractMeta(nameChecksum);
	Dbg_MsgAssert(p_source_meta, ("couldn't find metapiece named %s", Script::FindChecksumName(nameChecksum)));
	
	if (p_source_meta->IsRailPlacement())
	{
		m_cell_dims.SetWHL(1, 1, 1);	
	}
	else
	{
		mp_meta = m_manager->CreateConcreteMeta(p_source_meta, true);
		mp_meta->MarkLocked();
		m_cell_dims.SetWHL(mp_meta->GetArea().GetW(), mp_meta->GetArea().GetH(), mp_meta->GetArea().GetL());
		if (m_hard_rot & 1)
			m_cell_dims.SetWHL(m_cell_dims.GetL(), m_cell_dims.GetH(), m_cell_dims.GetW());
	}
	#else
	m_cell_dims.SetWHL(1, 1, 1);	
	#endif

	ClearAreaSelection();

	if (p_source_meta->IsGapPlacement())
	{
		change_mode(GAP);
		//m_manager->HighlightMetasWithGaps(true, NULL);
	}
	else if (p_source_meta->IsAreaSelection())
	{
		change_mode(AREA_SELECT);
		m_manager->HighlightMetasWithGaps(false, NULL);
	}
	else if (p_source_meta->IsRailPlacement())
	{
		change_mode(RAIL_PLACEMENT);
		m_manager->HighlightMetasWithGaps(false, NULL);
	}
	else
	{
		change_mode(REGULAR);
		m_manager->HighlightMetasWithGaps(false, NULL);
	}
		
	//m_manager->AddMetaPieceToPark(GridDims(0, 8, 0), mp_meta);

	//printf("selected set %d, entry %d/%d\n", m_selected_set, m_palette_set[m_selected_set].mSelectedEntry, m_palette_set[m_selected_set].mTotalEntries);
}

void CCursor::change_mode(ECursorMode newMode)
{
	if (m_mode != newMode)
	{
		m_mode = newMode;
		RefreshHelperText();
	}
}




void CCursor::RefreshHelperText()
{
	Script::CStruct params;
	if (m_mode == GAP_ADJUST)
		params.AddChecksum(CRCD(0x6835b854,"mode"), CRCD(0xe75a3a17,"gap_adjust"));
	else if (m_mode == GAP)
		params.AddChecksum(CRCD(0x6835b854,"mode"), CRCD(0x780a5cf3,"gap_regular"));
	else if (m_mode == RAIL_PLACEMENT)
		params.AddChecksum(CRCD(0x6835b854,"mode"), CRCD(0xffd81c08,"rail_placement"));
	else
		params.AddChecksum(CRCD(0x6835b854,"mode"), CRCD(0xb58efc2b,"regular"));
	Script::RunScript("parked_set_helper_text_mode", ¶ms);
}




bool CCursor::is_gap_descriptor_same_as_current(CGapManager::GapDescriptor *pDesc)
{
	if (m_current_gap.numCompleteHalves == 1 && 
		(pDesc->loc[0].GetX() != m_current_gap.loc[0].GetX() ||
		 pDesc->loc[0].GetY() != m_current_gap.loc[0].GetY() ||
		 pDesc->loc[0].GetZ() != m_current_gap.loc[0].GetZ()))
	{
		// we are currently working on a gap and it's not the same as the one at this location, so fail
		return false;
	}

	return true;
}
	



CUpperMenuManager::CUpperMenuManager()
{
	//Rulon: Commented this out to prevent menu set items from being added twice
	//Enable();

	mp_current_set = NULL;
	m_current_entry_in_set = 0;
	m_current_set_index = 0;
}




CUpperMenuManager::~CUpperMenuManager()
{
	Disable();
}




void CUpperMenuManager::SetSourceMeta(uint32 nameChecksum)
{
	//Script::RunScript("parked_make_piece_menu");
	
	CAbstractMetaPiece *p_source_meta = m_manager->GetAbstractMeta(nameChecksum);
	Dbg_MsgAssert(p_source_meta, ("couldn't find metapiece named %s", Script::FindChecksumName(nameChecksum)));
	
	Script::CStruct params;
	params.AddChecksum("metapiece_id", nameChecksum);
	if (p_source_meta->IsSingular())
	{
		params.AddChecksum("sector", nameChecksum);
		Script::RunScript("parked_make_piece_menu_item", ¶ms);			
	}
	else
	{
		Script::CArray sectors_array;
		p_source_meta->BuildElement3dSectorsArray(§ors_array);			
		params.AddArray("sectors", §ors_array);
		Script::RunScript("parked_make_piece_menu_item", ¶ms);			
		Script::CleanUpArray(§ors_array);
	}
}




void CUpperMenuManager::SetPieceSet(CParkManager::CPieceSet *pSet, int set_number, int menuSetNumber)
{
	ParkEd("CUpperMenuManager::SetPieceSet(pSet = %p, set_number = %d)", pSet, set_number);
	
	Dbg_Assert(pSet);
	
	bool is_clipboard_set=false;
	int num_clipboards=0;
	if (pSet->mIsClipboardSet)
	{
		is_clipboard_set=true;
		num_clipboards=CCursor::sInstance()->GetNumClipboards();
		CCursor::sInstance()->ClearAreaSelection();
	}

	Obj::CTracker* p_tracker = Obj::CTracker::Instance();
	if (mp_current_set)
	{
		//uint32 name_crc = pSet->mEntryTab[m_current_entry_in_set].mNameCrc;
		uint32 name_crc = mp_current_set->mEntryTab[m_current_entry_in_set].mNameCrc;
		
		ParkEd("unfocusing item in piece menu %s", Script::FindChecksumName(name_crc));
		if (p_tracker->GetObject(name_crc))
			p_tracker->LaunchEvent(Obj::CEvent::TYPE_UNFOCUS, name_crc);
	}
	
	ParkEd("current set is %p\n", mp_current_set);
	if (pSet != mp_current_set)
	{
		//printf("Script::RunScript(\"parked_make_piece_menu\");");
		Script::RunScript("parked_make_piece_menu");
		
		for (int i = 0; i < pSet->mTotalEntries; i++)
		{
			uint32 name_checksum = pSet->mEntryTab[i].mNameCrc;
			
			if (is_clipboard_set)
			{
				Script::CStruct params;
				params.AddChecksum("metapiece_id", name_checksum);
				if (iGetAbstractMeta(name_checksum);
				Dbg_MsgAssert(p_source_meta, ("couldn't find metapiece named %s", Script::FindChecksumName(name_checksum)));
				
				Script::CStruct params;
				params.AddChecksum("metapiece_id", name_checksum);
				if (p_source_meta->IsSingular())
				{
					params.AddChecksum("sector", name_checksum);
					Script::RunScript("parked_make_piece_menu_item", ¶ms);			
				}
				else
				{
					Script::CArray sectors_array;
					p_source_meta->BuildElement3dSectorsArray(§ors_array);			
					params.AddArray("sectors", §ors_array);
					Script::RunScript("parked_make_piece_menu_item", ¶ms);			
					Script::CleanUpArray(§ors_array);
				}
			}	
		}

		Script::RunScript("parked_make_set_menu");
		
		Script::CStruct params;
		params.AddInteger("set_number", menuSetNumber);
		params.AddInteger("last_set_number", m_current_set_index);
		Script::RunScript("parked_lock_piece_and_set_menus", ¶ms);
		
		mp_current_set = pSet;
		m_current_set_index = menuSetNumber;
	}

	// m_current_entry_in_set
	
	//Dbg_Assert(m_current_entry_in_set >= 0 && m_current_entry_in_set < 
	
	ParkEd("focusing item in piece menu %s", Script::FindChecksumName(pSet->mEntryTab[pSet->mSelectedEntry].mNameCrc));
	if (p_tracker->GetObject(pSet->mEntryTab[pSet->mSelectedEntry].mNameCrc))
		p_tracker->LaunchEvent(Obj::CEvent::TYPE_FOCUS, pSet->mEntryTab[pSet->mSelectedEntry].mNameCrc);
	m_current_entry_in_set = pSet->mSelectedEntry;

	/* Make the slider reflect the selection window */
	
	int first_in_window = m_current_entry_in_set - 3;
	int last_in_window = m_current_entry_in_set + 3;
	if (pSet->mTotalEntries <= 7)
	{
		first_in_window = 0;
		last_in_window = pSet->mTotalEntries - 1;
	}
	else if (first_in_window < 0)
	{
		last_in_window += -first_in_window;
		first_in_window = 0;
	}
	else if (last_in_window >= pSet->mTotalEntries)
	{
		first_in_window += pSet->mTotalEntries - last_in_window - 1;
		last_in_window = pSet->mTotalEntries - 1;
	}
	
	Front::CScreenElementManager* p_element_manager = Front::CScreenElementManager::Instance();
	Front::CScreenElementPtr p_slider = p_element_manager->GetElement(Script::GenerateCRC("piece_slider_orange"), Front::CScreenElementManager::ASSERT);
	float slider_x, slider_y;
	p_slider->GetLocalPos(&slider_x, &slider_y);
	float slider_start = 375.0f * first_in_window / pSet->mTotalEntries;
	float slider_end = 375.0f * (last_in_window + 1) / pSet->mTotalEntries;
	p_slider->SetPos(slider_start, slider_y, Front::CScreenElement::FORCE_INSTANT);
	p_slider->SetScale((slider_end - slider_start) / 4.0f, 1.0f, false, Front::CScreenElement::FORCE_INSTANT);

	Front::CScreenElementPtr p_name_text = p_element_manager->GetElement(Script::GenerateCRC("piece_menu_name_text"), Front::CScreenElementManager::ASSERT);
	if (pSet->mEntryTab[m_current_entry_in_set].mpName)
		((Front::CTextElement *) p_name_text.Convert())->SetText(pSet->mEntryTab[m_current_entry_in_set].mpName);
	else
		((Front::CTextElement *) p_name_text.Convert())->SetText("---");
}




void CUpperMenuManager::Enable()
{
	ParkEd("CUpperMenuManager::Enable()");
	//printf("CUpperMenuManager::Enable()");
	
	Script::RunScript("parked_make_piece_menu");			
	Script::RunScript("parked_make_set_menu");			
	
	for (int i = 0; i < m_manager->GetTotalNumPieceSets(); i++)
	{
		CParkManager::CPieceSet &set = m_manager->GetPieceSet(i);
		if (set.mVisible)
		{
			Script::CStruct params;
			params.AddString("set_name", set.mpName);
			Script::RunScript("parked_make_set_menu_item", ¶ms);
		}
	}

	// will force menu to be rebuilt in SetPieceSet()
	mp_current_set = NULL;
}




void CUpperMenuManager::Disable()
{
	ParkEd("CUpperMenuManager::Disable()");
	
	Script::RunScript("parked_destroy_piece_menu");
	Script::RunScript("parked_destroy_set_menu");
	mp_current_set= NULL;
}

bool ScriptSetParkEditorTimeOfDay(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 tod_script=0;
	pParams->GetChecksum(CRCD(0x4c72ed98,"tod_script"),&tod_script);
	Ed::CParkEditor::Instance()->SetTimeOfDayScript(tod_script);
	return true;
}

bool ScriptGetParkEditorTimeOfDayScript(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 tod_script=Ed::CParkEditor::Instance()->GetTimeOfDayScript();
	if (tod_script)
	{
		pScript->GetParams()->AddChecksum(CRCD(0x4c72ed98,"tod_script"),tod_script);
		return true;
	}	
	return false;
}

// Added so that when choosing to create a goal, the node array can be initialised, otherwise the
// cursor will not be able to detect kill polys (unless a test play had been done)
bool ScriptRebuildParkNodeArray(Script::CStruct *pParams, Script::CScript *pScript)
{
	Ed::CParkManager::sInstance()->RebuildNodeArray();	
	return true;
}
	
bool ScriptFreeUpMemoryForPlayingPark(Script::CStruct *pParams, Script::CScript *pScript)
{
	// The following will free up everything on the park editor heap, then delete the
	// heap itself.

	Ed::CParkEditor::Instance()->CreatePlayModeGapManager();
	
	Ed::CParkEditor::Instance()->DeleteCursor();
	// Note: Only destroying pieces, not the cloned sectors, otherwise the whole park
	// will disappear from beneath the skater.
	Ed::CParkManager::sInstance()->Destroy(Ed::CParkGenerator::DESTROY_ONLY_PIECES);
	
	Ed::CParkEditor::Instance()->SwitchToPlayModeGapManager();
	
	return true;
}

bool ScriptCalibrateMemoryGauge(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Old
	return true;
}

bool ScriptGetParkEditorCursorPos(Script::CStruct *pParams, Script::CScript *pScript)
{
	CParkEditor* p_editor = CParkEditor::Instance();
	Mth::Vector pos=p_editor->GetCursorPos();
	pScript->GetParams()->AddVector(CRCD(0x7f261953,"pos"),pos[X],pos[Y],pos[Z]);
	return true;
}

bool ScriptSwitchToParkEditorCamera(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Create the park editor camera if it does not exist, then switch to it.
	Obj::CCompositeObject * p_obj = (Obj::CCompositeObject *) Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0x64716bee,"parked_cam"));
	if (!p_obj)
	{
		Script::CStruct * p_cam_params = new Script::CStruct;
		p_cam_params->AddChecksum("name",CRCD(0x64716bee,"parked_cam"));
		
		Obj::CCompositeObjectManager::Instance()->CreateCompositeObjectFromNode(
											Script::GetArray("parkedcam_composite_structure"),p_cam_params);
		
		delete	p_cam_params;
	}
	
	CParkEditor::Instance()->SetAppropriateCamera();	

	return true;
}
	
/*
	Parameters:
	
	state
	command
	
	States:
	-edit
		Command:
		-initialize
		-clear whole park
		-regenerate from compressed map
		-build floor
	-test_play
	-off
	
	Will
*/
bool ScriptSetParkEditorState(Script::CStruct *pParams, Script::CScript *pScript)
{
	ParkEd("CCursor::ScriptSetParkEditorState()");
	#if DEBUG_THIS_DAMN_THING
	Script::PrintContents(pParams, 4);
	#endif
	
	CParkEditor* p_editor = CParkEditor::Instance();
	
	CParkEditor::EEditorState new_state = CParkEditor::vKEEP_SAME_STATE;
	pParams->GetChecksum("state", (uint32 *) &new_state);
	Dbg_MsgAssert(new_state == CParkEditor::vEDITING ||
				  new_state == CParkEditor::vINACTIVE ||
				  new_state == CParkEditor::vREGULAR_PLAY ||
				  new_state == CParkEditor::vTEST_PLAY, 
				  ("invalid park editor state"));

	CParkEditor::EEditorCommand command = CParkEditor::vNO_COMMAND;
	pParams->GetChecksum("command", (uint32 *) &command);
	Dbg_MsgAssert(command == CParkEditor::vINITIALIZE ||
				  command == CParkEditor::vCLEAR ||
				  command == CParkEditor::vREGENERATE_FROM_MAP ||
				  command == CParkEditor::vBUILD_FLOOR ||
				  command == CParkEditor::vNO_COMMAND,
				  ("invalid park editor command"));
	
	if (new_state == CParkEditor::vINACTIVE)
	{
		p_editor->SetState(new_state);					
		p_editor->Cleanup();
		p_editor->SetPaused(false);
	}
	else
	{
		if (command == CParkEditor::vINITIALIZE)
		{
			p_editor->Initialize((new_state == CParkEditor::vEDITING));
		}
		else
		{
			if (command == CParkEditor::vCLEAR)
				p_editor->Rebuild(false, true, (new_state == CParkEditor::vEDITING));
			else if (command == CParkEditor::vREGENERATE_FROM_MAP)
				p_editor->Rebuild(false, false, (new_state == CParkEditor::vEDITING));
			else if (command == CParkEditor::vBUILD_FLOOR)
				p_editor->Rebuild(true, false, (new_state == CParkEditor::vEDITING));			
		}
		p_editor->SetState(new_state);					
	}

	return true;
}




bool ScriptSetParkEditorPauseMode(Script::CStruct *pParams, Script::CScript *pScript)
{
	ParkEd("CCursor::ScriptSetParkEditorPauseMode()");
	#if DEBUG_THIS_DAMN_THING
	Script::PrintContents(pParams, 4);
	#endif
	
	CParkEditor* p_editor = CParkEditor::Instance();
	
	if (pParams->ContainsFlag("pause"))
		p_editor->SetPaused(true);
	else if (pParams->ContainsFlag("unpause"))
		p_editor->SetPaused(false);
	
	return true;
}




bool ScriptCustomParkMode(Script::CStruct *pParams, Script::CScript *pScript)
{
	CParkEditor* p_editor = CParkEditor::Instance();
	if (pParams->ContainsFlag("editing"))
	{
		return p_editor->EditingCustomPark();
	}
	else if (pParams->ContainsFlag("testing"))
	{
		return p_editor->TestingCustomPark();
	}
	else if (pParams->ContainsFlag("just_using"))
	{
		return p_editor->UsingCustomPark();
	}
	
	Dbg_MsgAssert(0, ("need a parameter -- 'editing' or 'just_using'"));
	return false;
}

bool ScriptSetParkEditorMaxPlayers(Script::CStruct *pParams, Script::CScript *pScript)
{
	int max_players=1;
	pParams->GetInteger(NONAME,&max_players);
	Dbg_MsgAssert(max_players>=1 && max_players<=8,("\n%s\nBad value of %d sent to SetParkEditorMaxPlayers",pScript->GetScriptInfo(),max_players));
	
	CParkManager::Instance()->GetGenerator()->SetMaxPlayers(max_players);

	return true;
}	

bool ScriptGetParkEditorMaxPlayers(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AddInteger("MaxPlayers",CParkManager::Instance()->GetGenerator()->GetMaxPlayers());
	return true;
}	

bool ScriptGetParkEditorMaxPlayersPossible(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AddInteger("MaxPlayersPossible",CParkManager::Instance()->GetGenerator()->GetMaxPlayersPossible());
	return true;
}	


// a helper function used by ScriptCanCleanlyResizePark(), ScriptResizePark()
GridDims ComputeResizedDims(Script::CStruct *pParams)
{
	// these are the current bounds, may be replaced with new info
	GridDims new_bounds = CParkManager::sInstance()->GetParkNearBounds();	
	
	// assume current w,l unless new parms passed in
	int w = new_bounds.GetW();
	int l = new_bounds.GetL();
	pParams->GetInteger("w", &w);
	pParams->GetInteger("l", &l);

	// these diffs may be replaced below
	int w_diff = w - (int) new_bounds.GetW();
	int l_diff = l - (int) new_bounds.GetL();

	if (pParams->GetInteger("inc_w", &w_diff))
		w = new_bounds.GetW() + w_diff;
	if (pParams->GetInteger("inc_l", &l_diff))
		l = new_bounds.GetL() + l_diff;
	
	new_bounds[X] = new_bounds[X] - w_diff / 2;
	new_bounds[Z] = new_bounds[Z] - l_diff / 2;
	new_bounds[W] = w;
	new_bounds[L] = l;

	
	return new_bounds;
}

// Prevents rail points or goal peds being placed too close to the edge of the park.
bool IsWithinParkBoundaries(Mth::Vector pos, float margin)
{
	GridDims park_bounds = CParkManager::sInstance()->GetParkNearBounds();	
	Mth::Vector corner_pos;
	Mth::Vector area_dims;
	corner_pos=Ed::CParkManager::Instance()->GridCoordinatesToWorld(park_bounds,&area_dims);

	if (pos[X] > corner_pos[X]+margin && pos[X] < corner_pos[X]+area_dims[X]-margin &&
		pos[Z] > corner_pos[Z]+margin && pos[Z] < corner_pos[Z]+area_dims[Z]-margin)
	{
		return true;
	}
	
	return false;
}		


bool ScriptCanCleanlyResizePark(Script::CStruct *pParams, Script::CScript *pScript)
{
	GridDims new_bounds;
	
	Script::CStruct *p_new_bounds=NULL;
	if (pParams->GetStructure(CRCD(0xb4f88771,"NewBounds"),&p_new_bounds))
	{
		int x,z,w,l;
		p_new_bounds->GetInteger(CRCD(0x7323e97c,"x"),&x);
		p_new_bounds->GetInteger(CRCD(0x9d2d8850,"z"),&z);
		p_new_bounds->GetInteger(CRCD(0xe39cf4ed,"w"),&w);
		p_new_bounds->GetInteger(CRCD(0x69f93d01,"l"),&l);
		new_bounds.SetXYZ(x,0,z);
		new_bounds.SetWHL(w,1,l);
		
		bool can_resize=true;
		if (CParkManager::sInstance()->AreMetasOutsideBounds(new_bounds))
		{
			can_resize=false;
		}
		
		Mth::Vector corner_pos;
		Mth::Vector area_dims;
		corner_pos=Ed::CParkManager::Instance()->GridCoordinatesToWorld(new_bounds,&area_dims);
		if (Obj::GetRailEditor()->ThereAreRailPointsOutsideArea(corner_pos[X]+PARK_BOUNDARY_MARGIN, 
																corner_pos[Z]+PARK_BOUNDARY_MARGIN,
																corner_pos[X]+area_dims[X]-PARK_BOUNDARY_MARGIN, 
																corner_pos[Z]+area_dims[Z]-PARK_BOUNDARY_MARGIN))
		{
			can_resize=false;
		}

		if (Obj::GetGoalEditor()->ThereAreGoalsOutsideArea(corner_pos[X], corner_pos[Z], corner_pos[X]+area_dims[X], corner_pos[Z]+area_dims[Z]))
		{
			can_resize=false;
		}
		
		return can_resize;	
	}

	return false;
}


void ResizePark(GridDims newBounds)
{
	CParkManager::sInstance()->RebuildInnerShell(newBounds);
	CParkManager::sInstance()->RebuildFloor();

	Mth::Vector corner_pos;
	Mth::Vector area_dims;
	corner_pos=Ed::CParkManager::Instance()->GridCoordinatesToWorld(newBounds,&area_dims);
	Obj::GetRailEditor()->ClipRails(corner_pos[X]+PARK_BOUNDARY_MARGIN, corner_pos[Z]+PARK_BOUNDARY_MARGIN,
									corner_pos[X]+area_dims[X]-PARK_BOUNDARY_MARGIN, corner_pos[Z]+area_dims[Z]-PARK_BOUNDARY_MARGIN,
									Obj::CRailEditorComponent::DELETE_POINTS_OUTSIDE);
	Obj::GetGoalEditor()->DeleteGoalsOutsideArea(corner_pos[X], corner_pos[Z], corner_pos[X]+area_dims[X], corner_pos[Z]+area_dims[Z]);
	
	CParkEditor::Instance()->DestroyClipboardsWhichAreTooBigToFit();
}

bool ScriptResizePark(Script::CStruct *pParams, Script::CScript *pScript)
{
	static GridDims stored_dims;
	
	GridDims new_bounds;
	
	Script::CStruct *p_new_bounds=NULL;
	if (pParams->GetStructure(CRCD(0xb4f88771,"NewBounds"),&p_new_bounds))
	{
		int x,z,w,l;
		p_new_bounds->GetInteger(CRCD(0x7323e97c,"x"),&x);
		p_new_bounds->GetInteger(CRCD(0x9d2d8850,"z"),&z);
		p_new_bounds->GetInteger(CRCD(0xe39cf4ed,"w"),&w);
		p_new_bounds->GetInteger(CRCD(0x69f93d01,"l"),&l);
		new_bounds.SetXYZ(x,0,z);
		new_bounds.SetWHL(w,1,l);
	}
	else
	{
		new_bounds = ComputeResizedDims(pParams);
	}
	
	
	if (pParams->ContainsFlag("delayed"))
	{
		stored_dims = new_bounds;
	}
	else 
	{
		if (pParams->ContainsFlag("use_stored"))
		{
			new_bounds = stored_dims;
		}
		if (!CParkManager::sInstance()->EnoughMemoryToResize(new_bounds))
		{
			Script::RunScript("parked_show_out_of_memory_message");
			return false;
		}
				
		ResizePark(new_bounds);
		// Refresh the rail geometry so that UpdateSuperSectors gets called on them.
		//Obj::GetRailEditor()->RefreshGeometry();
	}
	
	return true;
}


bool ScriptGetCurrentParkBounds(Script::CStruct *pParams, Script::CScript *pScript)
{
	GridDims bounds = CParkManager::sInstance()->GetParkNearBounds();	
	pScript->GetParams()->AddInteger(CRCD(0x7323e97c,"x"),bounds.GetX());
	pScript->GetParams()->AddInteger(CRCD(0x9d2d8850,"z"),bounds.GetZ());
	pScript->GetParams()->AddInteger(CRCD(0xe39cf4ed,"w"),bounds.GetW());
	pScript->GetParams()->AddInteger(CRCD(0x69f93d01,"l"),bounds.GetL());
	return true;
}	

bool ScriptCanChangeParkDimension(Script::CStruct *pParams, Script::CScript *pScript)
{
	GridDims current_bounds = CParkManager::sInstance()->GetParkNearBounds();
	GridDims max_bounds = CParkManager::sInstance()->GetParkFarBounds();
	
	Script::CArray *p_dim_array = NULL;
	if (pParams->GetArray(NONAME, &p_dim_array))
	{
		int w = p_dim_array->GetInt(0);
		int l = p_dim_array->GetInt(1);
		if (w != current_bounds.GetW() || l != current_bounds.GetL())
			return true;
	}
	else
	{
		// otherwise, decrease
		bool test_for_increase = pParams->ContainsFlag("up");
	
		if (pParams->ContainsFlag("width"))
		{
			if (test_for_increase && current_bounds.GetW() < max_bounds.GetW()) return true;
			else if (!test_for_increase && current_bounds.GetW() > 16) return true;
		}
		else
		{
			if (test_for_increase && current_bounds.GetL() < max_bounds.GetL()) return true;
			else if (!test_for_increase && current_bounds.GetL() > 16) return true;
		}
	}
		
	return false;
}




bool ScriptSaveParkToDisk(Script::CStruct *pParams, Script::CScript *pScript)
{
	int slot = 0;
	pParams->GetInteger("slot", &slot, Script::ASSERT);
	// XXX
	Ryan("saving park at slot %d\n", slot);
	CParkEditor* p_editor = CParkEditor::Instance();
	p_editor->AccessDisk(true, slot, false);
	return true;
}




bool ScriptLoadParkFromDisk(Script::CStruct *pParams, Script::CScript *pScript)
{
	int slot = 0;
	pParams->GetInteger("slot", &slot, Script::ASSERT);

	bool block_rebuild = pParams->ContainsFlag("block_rebuild");

	// XXX
	Ryan("loading park in slot %d\n", slot);
	CParkEditor* p_editor = CParkEditor::Instance();
	p_editor->AccessDisk(false, slot, block_rebuild);
	return true;
}




bool ScriptIsParkUnsaved(Script::CStruct *pParams, Script::CScript *pScript)
{
	return !CParkManager::sInstance()->IsMapSavedLocally();
}




bool ScriptFireCustomParkGap(Script::CStruct *pParams, Script::CScript *pScript)
{
	int gap_index;
	pParams->GetInteger("gap_index", &gap_index, true);

	// XXX
	Ryan("ScriptFireCustomParkGap() %d\n", gap_index);
	
	CGapManager::sInstance()->LaunchGap(gap_index, pScript);
	return true;
}

bool ScriptSetEditedParkGapInfo(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *p_name = NULL;
	pParams->GetString("name", &p_name);
	int score = -1;
	pParams->GetInteger("score", &score);
	
	CCursor::sInstance()->SetGapNameAndScore(p_name, score);

	Script::CStruct *p_cancel_flags=NULL;
	pParams->GetStructure(CRCD(0x49ec3f9,"cancel_flags"),&p_cancel_flags);
	CCursor::sInstance()->SetGapCancelFlags(p_cancel_flags);
	return true;
}




bool ScriptGetEditedParkGapName(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AddString("name", CCursor::sInstance()->GetGapName());

	return true;
}

bool ScriptParkEditorSelectionAreaTooBigToCopy(Script::CStruct *pParams, Script::CScript *pScript)
{
	return CCursor::sInstance()->SelectionAreaTooBigToCopy();
}

bool ScriptCopyParkEditorSelectionToClipboard(Script::CStruct *pParams, Script::CScript *pScript)
{
	bool success=CCursor::sInstance()->CopySelectionToClipboard();
	CCursor::sInstance()->ClearAreaSelection();	
	return success;
}

bool ScriptSwitchParkEditorMenuPieceToMostRecentClipboard(Script::CStruct *pParams, Script::CScript *pScript)
{
	CParkEditor::Instance()->SwitchMenuPieceToMostRecentClipboard();
	return true;
}

bool ScriptCutParkEditorAreaSelection(Script::CStruct *pParams, Script::CScript *pScript)
{
	bool success=CCursor::sInstance()->CopySelectionToClipboard();
	if (success)
	{
		CCursor::sInstance()->DeleteSelectedPieces();
		CCursor::sInstance()->ResetSelectedHeights();
	}
	CCursor::sInstance()->ClearAreaSelection();	
	return success;
}

bool ScriptParkEditorAreaSelectionDeletePieces(Script::CStruct *pParams, Script::CScript *pScript)
{
	CCursor::sInstance()->DeleteSelectedPieces();
	return true;
}

bool ScriptContinueParkEditorAreaSelection(Script::CStruct *pParams, Script::CScript *pScript)
{
	CCursor::sInstance()->ContinueAreaSelection();
	return true;
}
	
bool ScriptGetEditorTheme(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AddInteger(CRCD(0x688a18f7,"theme"), CParkManager::sInstance()->GetTheme());
	return true;
}

bool ScriptSetEditorTheme(Script::CStruct *pParams, Script::CScript *pScript)
{
	int theme=0;
	pParams->GetInteger(CRCD(0x688a18f7,"theme"),&theme);
	CParkManager::sInstance()->SetTheme(theme);
	return true;
}

bool ScriptGetEditorMaxThemes(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AddInteger(CRCD(0x7ddc0da8,"max_themes"), MAX_THEMES);
	return true;
}

bool ScriptGetCustomParkName(Script::CStruct *pParams, Script::CScript *pScript)
{
	int truncate_char_num = -1;
	pParams->GetInteger(NONAME, &truncate_char_num);
	
	const char *p_name = CParkManager::sInstance()->GetParkName();
	
	if (truncate_char_num == -1)
	{
		if (p_name)
			pScript->GetParams()->AddString("name", p_name);
		else
			pScript->GetParams()->AddString("name", "unnamed park");
	}
	else
	{
		char out_name[64];
		for (int i = 0; i < truncate_char_num; i++)
			out_name[i] = p_name[i];
		out_name[truncate_char_num] = '\0';
		pScript->GetParams()->AddString("name", out_name);
	}
	return true;
}




bool ScriptSetCustomParkName(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *p_name = NULL;
	pParams->GetString("name", &p_name, Script::ASSERT);
	CParkManager::sInstance()->SetParkName(p_name);
	return true;
}


// @script | IsCustomPark | returns true if this is a custom park
bool ScriptIsCustomPark(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	return Ed::CParkEditor::Instance()->UsingCustomPark();
}



bool ScriptBindParkEditorToController( Script::CStruct* pParams, Script::CScript* pScript )
{
	int controller;
	pParams->GetInteger( NONAME, &controller, Script::ASSERT );
	CParkEditor* p_editor = CParkEditor::Instance();
	p_editor->BindParkEditorToController( controller );
	return true;
}

#ifdef __PLAT_NGC__
// Required in order that the park rebuild when defragmenting during resizing does not
// reset the map heights.
// (Used in the script parked_defragment_memory_for_resize in sk5ed_scripts)
bool ScriptWriteCompressedMapBuffer(Script::CStruct *pParams, Script::CScript *pScript)
{
	CParkManager::sInstance()->WriteCompressedMapBuffer();
	return true;
}

bool ScriptRequiresDefragment(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mem::Heap *p_top_down_heap = Mem::Manager::sHandle().TopDownHeap();
	if (p_top_down_heap->mp_region->MemAvailable() < TOP_DOWN_REQUIRED_MARGIN)
	{
		return true;
	}
	
        Mem::Heap *p_main_heap = Mem::Manager::sHandle().BottomUpHeap();
        if (p_main_heap->mFreeMem.m_count > 200000)
	{
		return true;
	}

	return false;
}

bool ScriptTopDownHeapTooLow(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mem::Heap *p_top_down_heap = Mem::Manager::sHandle().TopDownHeap();
	if (p_top_down_heap->mp_region->MemAvailable() < TOP_DOWN_REQUIRED_MARGIN)
	{
		return true;
	}
	return false;
}	

#endif


}


================================================
FILE: Code/Sk/ParkEditor2/ParkEd.h
================================================
#ifndef __SK_PARKEDITOR2_PARKED_H
#define __SK_PARKEDITOR2_PARKED_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 

// At least this much is required on the top down heap during editing to ensure that
// UpdateSuperSectors will have enough space to execute when raising large areas of ground.
#ifdef __PLAT_NGC__
#define TOP_DOWN_REQUIRED_MARGIN 1000000
#define TOP_DOWN_REQUIRED_MARGIN_LEEWAY 100000
#else
#define TOP_DOWN_REQUIRED_MARGIN 1000000
// A defrag will only occur if freeing up the fragments will make the top down heap have this much
// more than the above margin. Could probably make this zero, just included to make sure it doesn't
// try to defrag too much.
#define TOP_DOWN_REQUIRED_MARGIN_LEEWAY 2000
#endif


class Coord
{
public:
	float m_X;
	float m_Y;
};


namespace Ed
{

enum
{
	MAX_THEMES=5
};

class CCursor;
class CUpperMenuManager;
class CClipboard;



class CParkEditor : public Mdl::Module, public Obj::CEventListener 
{
	
	DeclareSingletonClass( CParkEditor );
	
	CParkEditor();
	~CParkEditor();

public:

	enum EEditorState
	{
		vINACTIVE								= 0xd443a2bc,	// "off"
		vEDITING								= 0x7eca21e5,	// "edit"
		vTEST_PLAY								= 0xa40f0b8a,	// "test_play"
		vREGULAR_PLAY							= 0xcd3682c1,	// "regular_play"
		
		// use this as parameter only
		vKEEP_SAME_STATE						= 0,
	};

	enum EEditorCommand
	{
		vINITIALIZE								= 0x27f2117d,	// "initialize"
		vCLEAR									= 0x1a4e0ef9,	// "clear"
		vREGENERATE_FROM_MAP					= 0x2a907cbc,	// "regenerate_from_map"
		vBUILD_FLOOR							= 0x1625eda,	// "build_floor"

		vNO_COMMAND								= 0,
	};

	void										Initialize(bool edit_mode);
	void 										Rebuild(bool justRebuildFloor, bool clearMap, bool makeCursor);
	void										SetState(EEditorState desiredState);
	void										AccessDisk(bool save, int slot, bool blockRebuild);
	void 										PostMemoryCardLoad(uint8 * p_map_buffer, int oldTheme);
	void										Cleanup();
	
	void										SetAppropriateCamera();
	
	// K: Added to allow cleanup of the park editor heap during play
	void										DeleteCursor();

	void										Update();

	void										SwitchMenuPieceToMostRecentClipboard();
	
	void										SetPaused(bool paused);
	void 										MakeEditorStuffVisible( bool visible);
	
	bool										UsingCustomPark() {return m_state != vINACTIVE;}
	// returns true if actually editing right now
	bool										EditingCustomPark() {return m_state == vEDITING;}
	bool										TestingCustomPark() {return m_state == vTEST_PLAY;}

	// returns true if park is full
	bool	   									IsParkFull() {return m_pct_resources_used >= 1.0f;} 								
	bool										RoomToCopyOrPaste(int w, int l);

	void										BindParkEditorToController( int controller );
	Mth::Vector&								GetCursorPos();
	
	void										DestroyClipboardsWhichAreTooBigToFit();
	
	void										SetTimeOfDayScript(uint32 tod_script);
	uint32										GetTimeOfDayScript() {return m_tod_script;}
	
	void										CreatePlayModeGapManager();
	void										SwitchToPlayModeGapManager();
	void										DeletePlayModeGapManager();
	void										PlayModeGapManagerChecks();
	
private:	

	void										do_piece_select_commands();
	void										regular_command_logic();
	void										rail_placement_logic();
	
	int											m_last_main_heap_free;
	
	// A flag which is set whenever an operation is done that could increase memory usage.
	// This permits a defrag to be done if required. The flag is reset every frame.
	// The flag is required to prevent an infinite loop of defrags.
	bool 										m_allow_defrag;
	
	void										turn_on_or_update_piece_menu();
	void										update_analog_stick_menu_control_state();
	
	void										pass_event_to_listener(Obj::CEvent *pEvent);

	enum Commands
	{
		// some are aliases
		
		mROTATE_CW         	= nBit( 1 ),
		mROTATE_CCW        	= nBit( 2 ),
		mPREV_PIECE        	= nBit( 3 ),
		mNEXT_PIECE        	= nBit( 4 ),
		mPLACE_PIECE       	= nBit( 5 ),
		mREMOVE_PIECE      	= nBit( 6 ),
		mRAISE_FLOOR      	= nBit( 7 ),
		mLOWER_FLOOR      	= nBit( 8 ),
		mPREV_SET        	= nBit( 9 ),
		mNEXT_SET        	= nBit( 10 ),
		mZOOM_CAMERA_OUT	= nBit( 13 ),
		mZOOM_CAMERA_IN		= nBit( 14 ),

		mROTATE_GAP_CW		= nBit( 1 ),
		mROTATE_GAP_CCW		= nBit( 2 ),
		mPLACE_GAP       	= nBit( 5 ),
		mREMOVE_GAP       	= nBit( 6 ),
#ifdef __PLAT_NGC__
		mINCREASE_GAP_LEFT	= nBit( 0 ),
		mDECREASE_GAP_LEFT	= nBit( 15 ),
		mINCREASE_GAP_RIGHT	= nBit( 11 ),
		mDECREASE_GAP_RIGHT	= nBit( 12 ),
#else
		mINCREASE_GAP_LEFT	= nBit( 15 ),
		mDECREASE_GAP_LEFT	= nBit( 16 ),
		mINCREASE_GAP_RIGHT	= nBit( 11 ),
		mDECREASE_GAP_RIGHT	= nBit( 12 ),
#endif		// __PLAT_NGC__
	};

	static const float				vMAX_CAM_DIST;
	static const float				vMIN_CAM_DIST;
	static const float				vCAM_DIST_INC;
	static const float				vCAM_TARGET_ELEVATION;
	
	static	Tsk::Task< CParkEditor >::Code		s_logic_code;
	static	Tsk::Task< CParkEditor >::Code		s_display_code;
	static	Inp::Handler< CParkEditor >::Code  	s_input_logic_code;

	Tsk::Task< CParkEditor >*					m_logic_task;	
    Tsk::Task< CParkEditor >*					m_display_task;
    Inp::Handler< CParkEditor >*				m_input_handler;
	
	float										m_movement_vel;	// in inches per sec
	float										m_rotate_vel;	// in degrees per sec
    
	Coord                      			m_rightStick;
    Coord                      			m_leftStick;
    Flags                 			m_commands;
	
	Gfx::Camera*								mp_camera;
	float										m_camAngle;
	float										m_camAngleVert;
	float										m_camDist;
	
	enum CursorState
	{
												vMOVEMENT,
												vGAP_MOVE,
												vGAP_ADJUST,
	};
	
	CursorState									m_cursor_state;
	
	void										v_start_cb( void );
	void										v_stop_cb( void );
    	
	Spt::SingletonPtr				mp_park_manager;

	Mth::Vector									m_cursor_pos;
	CCursor *									mp_cursor;
	CUpperMenuManager *							mp_menu_manager;

	Tmr::Time									m_last_time;

	EEditorState								m_state;
	bool										m_paused;
	
	float										m_pct_resources_used;
	
	// This is the name of the time-of-day script which gets passed to the
	// script_change_tod script in the parameter tod_action.
	// (see timeofday.q for the above scripts)
	// The time of day gets set in Sk5Ed_Startup and in parked_test_play (both in sk5ed_scripts.q)
	// and in GameFlow_StartRun
	uint32										m_tod_script;
	
	CGapManager *								mp_play_mode_gap_manager;
	/* Debugging */
};




class CCursor
{
	friend class CParkEditor;

public:												
												CCursor();
												~CCursor();

	void										DeleteMeta();
	void 										DestroyGeometry();
	void										DestroyAnyClipboardCursors();
	void										Update(float shiftX, float shiftZ, int rotInc);
	bool										AttemptStamp();
	bool										AttemptRemove();
	void 										InformOfMetapieceDeletion(CConcreteMetaPiece *pMeta);
	bool										AttemptGap();
	bool										AttemptRemoveGap();
	bool 										AdjustGap(int rot, int leftChange, int rightChange);
	void										SetGapNameAndScore(const char *pGapName, int score);
	void										SetGapCancelFlags(Script::CStruct *p_cancelFlags);
	
	const char *								GetGapName();

	int											AttemptAreaSelect();
	void										ClearAreaSelection();
	void										DeleteSelectedPieces();
	void										ResetSelectedHeights();
	void										ContinueAreaSelection();
	void										GetAreaSelectDims(GridDims *p_dims);
	bool										DestroyMetasInCurrentArea();
	void										RefreshSelectionArea();
	void										ForceSelectionAreaHighlightRefresh() {m_initialised_highlight=false;}
	
	void										ChangePieceInSet(int dir);
	void										ChangeSet(int dir);
	void										SwitchMenuPieceToMostRecentClipboard();
	
	bool										ChangeFloorHeight(GridDims dims, int height, int dir, bool uniformHeight);
	bool										ChangeFloorHeight(int dir);
	void 										HighlightIntersectingMetas(bool on);
	void										SetVisibility(bool visible);

	int											GetSelectedSet(int *pMenuSetNumber) {*pMenuSetNumber = m_menu_set_number; return m_selected_set;}
	uint32										GetChecksum() {return mp_meta->GetNameChecksum();}

	enum ECursorMode
	{
		NONE,
		REGULAR,
		GAP,
		GAP_ADJUST,
		AREA_SELECT,
		PASTE,
		RAIL_PLACEMENT,
	};

	ECursorMode									GetCursorMode() {return m_mode;}
	bool										InGapMode() {return (m_mode == GAP || m_mode == GAP_ADJUST);}
	bool										InAreaSelectMode() {return m_mode==AREA_SELECT;}
	bool										InRailPlacementMode() {return m_mode==RAIL_PLACEMENT;}
	bool										InPasteMode() {return m_mode==PASTE;}
	Mth::ERot90									GetRotation() {return m_hard_rot;}
	const GridDims &							GetPosition() {return m_cell_dims;}
	bool										SelectionAreaTooBigToCopy();
	bool										CopySelectionToClipboard();
	void										DeleteOldestClipboard();
	
	void										PasteCurrentClipboard();
	int											GetNumClipboards();
	int											GetClipboardY() {return m_clipboard_y;}
		
	bool										IsSittingOnGap();
	bool										HalfFinishedGap() {return m_current_gap.numCompleteHalves == 1;}

	// K: Made this public so that it can be called from CParkEditor::Rebuild
	void										ForceInBounds();

	void										DestroyClipboardsWhichAreTooBigToFit();

	static	CCursor *							sInstance(bool assert = true);

protected:

	void										destroy_clipboards();
	void										get_selected_area_coords(uint8 *p_x1, uint8 *p_z1, uint8 *p_x2, uint8 *p_z2);
	
	void										change_cell_pos(int incX, int incZ);
	void										set_source_meta(uint32 nameChecksum);
	void										change_mode(ECursorMode newMode);
	void										RefreshHelperText();

	bool										is_gap_descriptor_same_as_current(CGapManager::GapDescriptor *pDesc);
	
	static const float							MOTION_PCT_INC;
	static const float							ROT_DEG_INC;
	
	Spt::SingletonPtr				m_manager;

	GridDims									m_cell_dims;
	
	float										m_motion_pct;
	float										m_motion_pct_inc;
	
	Mth::Vector									m_target_pos;
	Mth::Vector									m_pos;
	bool										m_am_moving;

	Mth::ERot90									m_hard_rot;
	float										m_target_rot;
	float										m_rot; // rotation on y, in degrees


	CConcreteMetaPiece *						mp_meta;
	
	// Clipboard stuff
	uint8										m_num_corners_selected; // 0,1 or 2
	GridDims									mp_area_corners[2];
	bool										m_initialised_highlight;
	
	// List of clipboards
	CClipboard *								mp_clipboards;
	// This is the clip that will get placed when in paste mode.
	CClipboard *								mp_current_clipboard;
	CClipboard *								get_clipboard(int index);
	void										set_current_clipboard(int index);
	void										remove_clipboard(int index);
	// The y that must be added to the y of each clipboard entry such that
	// they align with the ground as best as possible.
	int											m_clipboard_y;
	

	
	
	int											m_selected_set;
	int											m_menu_set_number;

	ECursorMode									m_mode;

	/* Gap Stuff */

	CGapManager::GapDescriptor					m_current_gap;
	uint32										m_gap_suffix_counter;
	
	static CCursor *							sp_instance;
};




class CUpperMenuManager
{
public:
												CUpperMenuManager();
												~CUpperMenuManager();

	void 										SetSourceMeta(uint32 nameChecksum);
	void										SetPieceSet(CParkManager::CPieceSet *pSet, int set_number, int menuSetNumber);
	void										Enable();
	void										Disable();

protected:

	Spt::SingletonPtr				m_manager;
	CParkManager::CPieceSet *					mp_current_set;
	int											m_current_set_index;
	int											m_current_entry_in_set;
};


#define PARK_BOUNDARY_MARGIN 10.0f
bool IsWithinParkBoundaries(Mth::Vector pos, float margin);


bool ScriptSetParkEditorTimeOfDay(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetParkEditorTimeOfDayScript(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRebuildParkNodeArray(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptFreeUpMemoryForPlayingPark(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCalibrateMemoryGauge(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetParkEditorCursorPos(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSwitchToParkEditorCamera(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetParkEditorState(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetParkEditorPauseMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCustomParkMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetParkEditorMaxPlayers(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetParkEditorMaxPlayers(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetParkEditorMaxPlayersPossible(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCanCleanlyResizePark(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptResizePark(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetCurrentParkBounds(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCanChangeParkDimension(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSaveParkToDisk(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLoadParkFromDisk(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptIsParkUnsaved(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptFireCustomParkGap(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetEditedParkGapInfo(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetEditedParkGapName(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptParkEditorSelectionAreaTooBigToCopy(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCopyParkEditorSelectionToClipboard(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSwitchParkEditorMenuPieceToMostRecentClipboard(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCutParkEditorAreaSelection(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptParkEditorAreaSelectionDeletePieces(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptContinueParkEditorAreaSelection(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetEditorTheme(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetEditorTheme(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetEditorMaxThemes(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptGetCustomParkName(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetCustomParkName(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptBindParkEditorToController(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptIsCustomPark(Script::CStruct *pParams, Script::CScript *pScript);
#ifdef __PLAT_NGC__
bool ScriptWriteCompressedMapBuffer(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRequiresDefragment(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptTopDownHeapTooLow(Script::CStruct *pParams, Script::CScript *pScript);
#endif


}
#endif


================================================
FILE: Code/Sk/ParkEditor2/ParkGen.cpp
================================================
#include 
#include 
#include 			// Mick:  Added as we need to iterate over the ConcreteMetaPieces
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 	   // Needed to get the size of the replay buffer, for adjusting available memory

#include 
#include 


#include 
#include 
#include 
#include 
#include 

//#define	DEBUG_RESTARTS

#ifdef __PLAT_NGC__
#include 
#endif		// __PLAT_NGC__

#ifdef __PLAT_NGPS__
#define PRINT_RAW_RESULTS 0

#if PRINT_RAW_RESULTS
#include 
#include 
#include 
#include 


namespace Nx
{
	extern void find_geom_leaves( NxPs2::CGeomNode *p_node, NxPs2::CGeomNode **leaf_array, int & num_nodes);
}
#endif // PRINT_RAW_RESULTS

#endif


namespace Ed
{



const float CParkGenerator::CELL_WIDTH = 120.0f;
const float CParkGenerator::CELL_HEIGHT = 48.0f;
const float CParkGenerator::CELL_LENGTH = 120.0f;


// the first range of ids are used for regular objects and object nodes
// the second for rail nodes
const uint32 CParkGenerator::vFIRST_ID_FOR_OBJECTS 			= 2000;
const uint32 CParkGenerator::vMAX_ID_FOR_OBJECTS 			= 2000000;
const uint32 CParkGenerator::vFIRST_ID_FOR_RAILS 			= 2000001;
const uint32 CParkGenerator::vMAX_ID_FOR_RAILS 				= 4000000;
const uint32 CParkGenerator::vFIRST_ID_FOR_CREATED_RAILS	= 4000001;
const uint32 CParkGenerator::vMAX_ID_FOR_CREATED_RAILS		= 6000000;
const uint32 CParkGenerator::vFIRST_ID_FOR_RESTARTS 		= 6000001;



CPiece::CPiece()
{
	m_flags = EFlags(0);

	//mp_sector = NULL;
	m_sector_checksum = 0;
	
	mp_next_in_list = NULL;
}




CPiece::~CPiece()
{
}




void CPiece::GetCellDims(GridDims *pDims)
{
	Mth::Vector dims = GetDims();
	pDims->SetWHL((uint8) ((dims.GetX() + 2.0f) / CParkGenerator::CELL_WIDTH),
				  (uint8) ((dims.GetY() + 2.0f) / CParkGenerator::CELL_HEIGHT),
				  (uint8) ((dims.GetZ() + 2.0f) / CParkGenerator::CELL_LENGTH));
	
	/*
	if (m_sector_checksum == Script::GenerateCRC("Sk3Ed_MTra_02"))
	{
		Ryan("Sk3Ed_MTra_02 cells dims are (%d,%d,%d), world (%.4f,%.4f,%.4f)\n", 
			 pDims->GetW(), pDims->GetH(), pDims->GetL(),
			 dims.GetX(), dims.GetY(), dims.GetZ());
	}
	*/
}




CSourcePiece *CPiece::CastToCSourcePiece()
{
	Dbg_MsgAssert(this == NULL || (m_flags & mSOURCE_PIECE), ("not a source piece!"));
	return (CSourcePiece *) this;
}




CClonedPiece *CPiece::CastToCClonedPiece()
{
	Dbg_MsgAssert(this == NULL || (m_flags & mCLONED_PIECE), ("not a cloned piece!"));
	return (CClonedPiece *) this;
}




Mth::Vector	CPiece::GetDimsWithRot(Mth::ERot90 rot)
{
	CSourcePiece *p_source_piece;
	if (m_flags & mSOURCE_PIECE)
		p_source_piece = CastToCSourcePiece();
	else
		p_source_piece = CastToCClonedPiece()->GetSourcePiece();

	Dbg_Assert(p_source_piece);

	Mth::Vector result;
	if (rot & 1)
	{
		result[X] = p_source_piece->GetDims().GetZ();
		result[Z] = p_source_piece->GetDims().GetX();
	}
	else
	{
		result[X] = p_source_piece->GetDims().GetX();
		result[Z] = p_source_piece->GetDims().GetZ();
	}
	result[Y] = p_source_piece->GetDims().GetY();

	return result;
}




CSourcePiece::CSourcePiece()
{
	set_flag(CPiece::mSOURCE_PIECE);
	m_domain = mREGULAR;
	m_triggerScriptId = 0;
}




CSourcePiece::~CSourcePiece()
{
	//printf("killed CSourcePiece\n");
}




uint32 CSourcePiece::GetType()
{
	return m_sector_checksum;
}




CClonedPiece::CClonedPiece()
{
	m_id = 0;
	set_flag(CPiece::mCLONED_PIECE);
	mp_source_piece = NULL;
}




uint32 CClonedPiece::GetID()
{
	return m_id;
}




/*
	Instant function. Piece arrives at new location right away.
*/
void CClonedPiece::SetDesiredPos(const Mth::Vector & pos, ESectorEffect sectorEffect)
{
	//Dbg_MsgAssert(!(m_flags & CPiece::mIN_WORLD), ("can't change position, piece already in world"));
	m_pos = pos;

	if (sectorEffect == CHANGE_SECTOR)
	{
		#if 0
		//printf("cloned piece at (%.2f,%.2f,%.2f), dims=(%.2f,%.2f,%.2f)\n", 
		//if (mp_source_piece->GetType() == Script::GenerateCRC("Sk3Ed_Rd1b_10x10x4"))
//			printf("   cloned piece at (%.3f,%.3f,%.3f), dims=(%.3f,%.3f,%.3f)\n", 
//				   pos.GetX(), pos.GetY(), pos.GetZ(),
//				   mp_source_piece->m_dims.GetX(), mp_source_piece->m_dims.GetY(), mp_source_piece->m_dims.GetZ());
		#endif

		/*
		if (pos.GetX() == -1140.0f && pos.GetZ() == -1140.0f)
			printf("    add (%f,%f,%f), sector 0x%x\n", 
				   pos.GetX(), pos.GetY(), pos.GetZ(), m_sector_checksum);
		*/
		
		Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
		Dbg_Assert(p_sector);
	
		p_sector->SetWorldPosition(pos);
	}
}




/*
	Instant function. Piece arrives at new rotation right away.
*/
void CClonedPiece::SetDesiredRot(Mth::ERot90 rot, ESectorEffect sectorEffect)
{
	//Dbg_MsgAssert(!(m_flags & CPiece::mIN_WORLD), ("can't change rotation, piece already in world"));
	m_rot = rot;

	if (sectorEffect == CHANGE_SECTOR)
	{
		Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
		Dbg_Assert(p_sector);

		//Dbg_Message("Rotating piece with to %d", (int) rot * 90);
		p_sector->SetYRotation(rot);
	}
}




void CClonedPiece::SetSoftRot(float rot)
{
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
	Dbg_Assert(p_sector);

	Nx::CGeom *p_geom = p_sector->GetGeom();
	if (p_geom)
	{
		Mth::Matrix rot_mat;
		rot_mat.Ident();
		rot_mat.RotateY(Mth::DegToRad(rot));
		p_geom->SetOrientation(rot_mat);
	}
}




void CClonedPiece::SetScaleX(float scaleX)
{
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
	Dbg_Assert(p_sector);
	Mth::Vector scale_vect;
	if (m_rot & 1)
	{
		scale_vect[X] = 1.0f;
		scale_vect[Z] = scaleX;
	}
	else
	{
		scale_vect[X] = scaleX;
		scale_vect[Z] = 1.0f;
	}
	scale_vect[Y] = 1.0f;
	p_sector->SetScale(scale_vect);
}




// Turn on or off the highlight effect for the geometry that makes up a cloned piece
void CClonedPiece::Highlight(bool on, bool makePurple)
{
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
	if (!p_sector)
	{
		return;
	}	
	//Dbg_Assert(p_sector);

	Nx::CGeom *p_geom = p_sector->GetGeom();
	if (p_geom)
	{
		// This effect is somewhat arbitary.   I just want to get SOME effect
		if (on)
		{
			if (makePurple)
				p_geom->SetColor(Image::RGBA(160,100,160,0));
			else
				p_geom->SetColor(Image::RGBA(160,100,100,0));
			//Ryan("set color for sector=%p\n", p_sector);
		}
		else
		{
			// ClearColor flags the geom as being NOT COLORED, rather than setting the color to neutral.
			p_geom->ClearColor();		
			//Ryan("clear color for sector=%p\n", p_sector);
		}
	}
}




void CClonedPiece::SetActive(bool active)
{
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
	Dbg_Assert(p_sector);
	p_sector->SetActive(active);
}




void CClonedPiece::SetVisibility(bool visible)
{
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
	Dbg_Assert(p_sector);
	uint32 mask = visible ? 0xFF : 0;
	p_sector->SetVisibility(mask);
}




Mth::Vector	CClonedPiece::GetDims()
{
	Dbg_Assert(mp_source_piece);

	Mth::Vector result;
	if (m_rot & 1)
	{
		result[X] = mp_source_piece->m_dims.GetZ();
		result[Z] = mp_source_piece->m_dims.GetX();
	}
	else
	{
		result[X] = mp_source_piece->m_dims.GetX();
		result[Z] = mp_source_piece->m_dims.GetZ();
	}
	result[Y] = mp_source_piece->m_dims.GetY();

	return result;
}




CParkGenerator::CParkGenerator()
{
	m_flags = EFlags(0);
	mp_cloned_piece_list = NULL;
	mp_source_piece_list = NULL;

	mp_cloned_scene = NULL;
	mp_source_scene = NULL;
	m_num_cloned_pieces = 0;
	m_num_source_pieces = 0;

	m_next_id = 32;

	mp_mem_region = NULL;
	mp_mem_heap = NULL;

	m_mem_usage_info.mMainHeapFree = 0;
	m_mem_usage_info.mMainHeapUsed = 0;
	m_mem_usage_info.mParkHeapFree = 0;
	m_mem_usage_info.mParkHeapUsed = 0;
	m_mem_usage_info.mLastMainUsed = 0;
	
	m_total_rail_points = 0;
	m_total_rail_linked_points = 0;
	
	m_max_players=2;
}




CParkGenerator::~CParkGenerator()
{
	// Clean up scenes (do this last)
	if (mp_cloned_scene)
	{
		delete mp_cloned_scene;
	}
	if (mp_source_scene)
	{
		delete mp_source_scene;
	}
}

int	CParkGenerator::GetResourceSize(char *name)
{
	Script::CStruct *p_resource_struct = Script::GetStructure("Ed_Resources_Info");
	int size;

	if (strcmp(name, "main_heap_base") == 0)
	{
		Script::CArray *p_values;
		p_resource_struct->GetArray(name, &p_values);
		#ifdef __PLAT_NGPS__
		size = p_values->GetInteger(0);
		#else
			#ifdef __PLAT_NGC__
			size = 4654800+((2 * 1024 * 1024)-900000);	//p_values->GetInteger(1);
			#else
			size = p_values->GetInteger(2);
			#endif
		#endif
	}
	else if(strcmp(name, "main_heap_pad") == 0)
	{
		Script::CArray *p_values;
		p_resource_struct->GetArray(name, &p_values);
#		if defined( __PLAT_NGPS__ )
		size = p_values->GetInteger(0);
#		elif defined( __PLAT_NGC__ )
		size = -2294188;	//p_values->GetInteger(1);
#		else
		size = p_values->GetInteger(2);
#		endif

	}
	else if (strcmp(name, "theme_pad") == 0)
	{
		int theme=CParkManager::sInstance()->GetTheme();
		#ifdef __PLAT_NGC__
		int theme_size[5] =
		{
			0,
			179296,
			822784,
			482944,
			444544
		};
		size = theme_size[theme];
		#else
			#ifdef __PLAT_XBOX__
				int theme_size[5] =
				{
					0,
					253728,
					564688,
					161344,
					441856
				};
				size = theme_size[theme];
			#else		
				Script::CArray *p_values;
				p_resource_struct->GetArray(name, &p_values);
				size = p_values->GetInteger(theme);
			#endif
		#endif		// __PLAT_NGC__
	}
	#ifdef __PLAT_NGC__
	else if(strcmp(name, "floor_piece_size_main") == 0)
	{
		size = 1000;
	}
	#endif
	else
	{
		if (!p_resource_struct->GetInteger(name, &size))
		{
			Dbg_MsgAssert(0, ("couldn't get resource size '%s'", name));
			return 0;
		}
	}

	return size;
}




Mem::Heap *CParkGenerator::GetParkEditorHeap()
{
	Dbg_MsgAssert(mp_mem_heap, ("no park editor heap"));
	return mp_mem_heap;
}



void CParkGenerator::SetMaxPlayers(int maxPlayers)
{
	Dbg_MsgAssert(maxPlayers>1 && maxPlayers<=GameNet::vMAX_PLAYERS,("Bad maxPlayers of %d",maxPlayers));
	m_max_players=maxPlayers;
}	

CParkGenerator::MemUsageInfo CParkGenerator::GetResourceUsageInfo(bool printInfo)
{
	Dbg_Assert(mp_mem_heap);
	Dbg_Assert(this);
	
	//int base_park_heap = 0;
	//int max_base_park_heap = 0;
	
	//int padding_size = GetResourceSize("park_heap_pad");
	//int used_park_heap = mp_mem_heap->mUsedMem.m_count;
	//int free_park_heap = mp_mem_heap->mFreeMem.m_count + mp_mem_region->MemAvailable();


	// allowed to be used, that is
	//int allowed_park_heap_mem = free_park_heap + used_park_heap - padding_size;
	//float park_heap_pct = (float) (used_park_heap - base_park_heap) / (float) (allowed_park_heap_mem - max_base_park_heap);
	//float actual_park_heap_pct = (float) (used_park_heap) / (float) (allowed_park_heap_mem);
	/*
	if (largest_free_block < padding_size)
	{
		// there is no guarantee we can safetly fit the next piece
		// placed, so make the memory meter full
		park_heap_pct = actual_park_heap_pct = 1.0001f;
	}
	*/

	Mem::Heap *p_main_heap = Mem::Manager::sHandle().BottomUpHeap();
	Mem::Region *p_main_region = p_main_heap->mp_region;
	Dbg_Assert(p_main_region);
	if (m_mem_usage_info.mMainHeapUsed != p_main_heap->mUsedMem.m_count)
	{
		m_mem_usage_info.mLastMainUsed = m_mem_usage_info.mMainHeapUsed;
	}
	
	// Mick, the replay buffer is allocated in the park editor
	// but we want to ignore it in our calculations
	// so we just subtract it from the main_heap_pad																			 
#ifdef __PLAT_NGC__
	int main_padding_size = GetResourceSize("main_heap_pad");
#else
#if __USE_REPLAYS__
	int main_padding_size = GetResourceSize("main_heap_pad") - Replay::GetBufferSize();
#else	
	int main_padding_size = GetResourceSize("main_heap_pad");
#endif
#endif
	
	main_padding_size+=(m_max_players-1)*(SKATER_HEAP_SIZE+SKATER_GEOM_HEAP_SIZE);
	
	int total_mem_available=p_main_heap->mFreeMem.m_count + p_main_region->MemAvailable();
	if (main_padding_size > total_mem_available)
	{
		//Dbg_MsgAssert(0,("main_padding_size > total_mem_available !! (%d > %d)",main_padding_size,total_mem_available));
		#ifdef	__NOPT_ASSERT__
		//printf("main_padding_size > total_mem_available !! (%d > %d)\n",main_padding_size,total_mem_available);
		#endif
		//ParkEd("main_padding_size > total_mem_available !! (%d > %d)\n",main_padding_size,total_mem_available);
	}
		
	m_mem_usage_info.mMainHeapUsed = p_main_heap->mUsedMem.m_count;
	m_mem_usage_info.mMainHeapFree = p_main_heap->mFreeMem.m_count + p_main_region->MemAvailable() - main_padding_size;
	
	
	//printf("p_main_heap->mFreeMem.m_count=%d\np_main_region->MemAvailable()=%d\nmain_padding_size=%d\n",p_main_heap->mFreeMem.m_count,p_main_region->MemAvailable(),main_padding_size);
	int park_padding_size = GetResourceSize("park_heap_pad");
	m_mem_usage_info.mParkHeapUsed = mp_mem_heap->mUsedMem.m_count;
	m_mem_usage_info.mParkHeapFree = mp_mem_heap->mFreeMem.m_count + mp_mem_region->MemAvailable() - park_padding_size;

	if (p_main_heap->LargestFreeBlock() < m_mem_usage_info.mMainHeapFree && 	// heap definitely fragmented
		p_main_heap->LargestFreeBlock() < main_padding_size && 					// no block big enough for single piece
		m_mem_usage_info.mMainHeapFree >= main_padding_size) 					// there is enough memory for single piece
	{
		// XXX
		Ryan("===================== DEFRAG ===========================\n");
		Ryan("largest: %d, free: %d, padding %d\n", 
			 p_main_heap->LargestFreeBlock(), m_mem_usage_info.mMainHeapFree, main_padding_size);
		Ryan("========================================================\n");
		m_mem_usage_info.mIsFragmented = true;
		int false_free_amt = m_mem_usage_info.mMainHeapFree - p_main_heap->LargestFreeBlock();
		m_mem_usage_info.mMainHeapFree -= false_free_amt;
		m_mem_usage_info.mMainHeapUsed += false_free_amt;
	}
	else
		m_mem_usage_info.mIsFragmented = false;
		
		
	#if 0
	if ( p_main_heap->mFreeMem.m_count > 200000)
	{
		m_mem_usage_info.mIsFragmented = true;	
	}
	#endif

	m_mem_usage_info.mTotalClonedPieces = m_num_cloned_pieces;
	m_mem_usage_info.mTotalRailPoints = m_total_rail_points;
	m_mem_usage_info.mTotalLinkedRailPoints = m_total_rail_linked_points;


	// subtract theme specific padding
	if (CParkManager::sInstance()->GetTheme() > 0)
	{
		m_mem_usage_info.mMainHeapFree -= GetResourceSize("theme_pad");
	}

	
	if (printInfo)
	{
		Ryan("=====================================================\nParkGen Usage Stats:\n\n");
		Ryan("largest free block: %d\n", mp_mem_heap->LargestFreeBlock());
		Ryan("used park heap: %d\n", m_mem_usage_info.mParkHeapUsed);
		Ryan("free park heap: %d\n", m_mem_usage_info.mParkHeapFree);
		Ryan("main heap used: %d\n", m_mem_usage_info.mMainHeapUsed);
		Ryan("main heap available: %d\n", m_mem_usage_info.mMainHeapFree);
	}
	
	return m_mem_usage_info;
	
}

int	CParkGenerator::GetMaxPlayersPossible()
{
	CParkGenerator::MemUsageInfo usage_info = GetResourceUsageInfo();
	// GetResourceUsageInfo has already subtracted the memory used by the currently set max players,
	// so add that back in so that the max max can be calculated.
	usage_info.mMainHeapFree += (m_max_players-1)*(SKATER_HEAP_SIZE+SKATER_GEOM_HEAP_SIZE);
	
	// The +1 is because at least 1 skater is assumed.
	return 	usage_info.mMainHeapFree/(SKATER_HEAP_SIZE+SKATER_GEOM_HEAP_SIZE)+1;
}



/*
	Given checksum of piece name, returns pointer to source piece.
*/
CSourcePiece *CParkGenerator::GetMasterPiece(uint32 pieceType, bool assert)
{
	CPiece *p_piece = mp_source_piece_list;
	while(p_piece)
	{
		if (p_piece->CastToCSourcePiece()->m_sector_checksum == pieceType)
			return p_piece->CastToCSourcePiece();
		p_piece = p_piece->mp_next_in_list;
	}
	
	if (assert)
	{
		Dbg_MsgAssert(0, ("couldn't not find master piece %s", Script::FindChecksumName(pieceType)));
	}
	else
		printf("couldn't not find master piece %s\n", Script::FindChecksumName(pieceType));

	return NULL;
}




CSourcePiece *CParkGenerator::GetNextMasterPiece(CSourcePiece *pLast)
{	
	if (!pLast)
		return mp_source_piece_list->CastToCSourcePiece();
	else
		return pLast->mp_next_in_list->CastToCSourcePiece();
}




/*
	Makes a copy of a source piece. The copy can then be added to a metapiece, or to the world.

	A soft piece can be smoothly rotated, like a piece used in a cursor.
*/
CClonedPiece *CParkGenerator::ClonePiece(CPiece *pMasterPiece, CPiece::EFlags flags)
{
	// make sure flags make sense
	flags = CPiece::EFlags(flags & (CPiece::mSOFT_PIECE | CPiece::mFLOOR | CPiece::mNO_RAILS));
	
	Dbg_Assert(pMasterPiece);

	CSourcePiece *pSource;
	if (pMasterPiece->m_flags & CPiece::mSOURCE_PIECE)
		pSource = pMasterPiece->CastToCSourcePiece();
	else
		pSource = pMasterPiece->CastToCClonedPiece()->mp_source_piece;

	Dbg_Assert(pSource);
	Dbg_Assert(mp_source_scene);
	Dbg_Assert(mp_cloned_scene);

	// putting cloned sectors on main heap seems best right now
	//Mem::Manager::sHandle().PushContext(mp_mem_heap);
	uint32 new_sector_checksum;
	if (flags & CPiece::mSOFT_PIECE)
	{
		// create fresh soft piece (hmm, that sounds kind of disgusting)
		new_sector_checksum = mp_source_scene->CloneSector(pSource->m_sector_checksum, mp_cloned_scene, true, false); // no instance for now (should be instance)
		//Dbg_Message("************************* New soft checksum %x", new_sector_checksum);
	}
	else
	{
		// create fresh hard piece (no, this sounds worse!)
		new_sector_checksum = mp_source_scene->CloneSector(pSource->m_sector_checksum, mp_cloned_scene, false, true); // no instance
		
		if (!(flags & CPiece::mNO_RAILS))
		{
			m_total_rail_points += pSource->m_num_rail_points;
			m_total_rail_linked_points += pSource->m_num_linked_rail_points;
		}
	}
	//Mem::Manager::sHandle().PopContext();

	// Create cloned piece, will soon live in world or as animated piece
	Mem::Manager::sHandle().PushContext(mp_mem_heap);
	CClonedPiece *p_cloned_piece = new CClonedPiece();
	Mem::Manager::sHandle().PopContext();
	p_cloned_piece->m_sector_checksum = new_sector_checksum;
	p_cloned_piece->m_id = m_next_id++;
	p_cloned_piece->mp_source_piece = pSource;
	p_cloned_piece->SetDesiredRot(Mth::ERot90(0), CClonedPiece::MARKER_ONLY);
	
	m_num_cloned_pieces++;

	p_cloned_piece->set_flag(flags);

	set_flag(mSECTORS_GENERATED);
	
	return p_cloned_piece;
}




/*
	Must be called to finalized cloned piece's presence in world.
*/
void CParkGenerator::AddClonedPieceToWorld(CClonedPiece *pPiece)
{
	Dbg_MsgAssert(!(pPiece->GetFlags() & CPiece::mIN_WORLD), ("piece already in world"));
	pPiece->set_flag(CPiece::mIN_WORLD);

	pPiece->mp_next_in_list = mp_cloned_piece_list;
	mp_cloned_piece_list = pPiece;
	
	set_flag(mPIECES_IN_WORLD);
}




void CParkGenerator::DestroyClonedPiece(CClonedPiece *pPiece)
{
	destroy_piece_impl(pPiece, DESTROY_PIECES_AND_SECTORS);
}


#ifdef __PLAT_NGPS__
#if PRINT_RAW_RESULTS
void print_ps2_data(NxPs2::CGeomNode *p_node)
{
	uint8 *p_dma = p_node->GetDma();

	int num_verts = NxPs2::dma::GetNumVertices(p_dma);
	if( num_verts >= 3 )
	{
		bool short_xyz = (NxPs2::dma::GetBitLengthXYZ(p_dma) == 16);
		Mth::Vector mesh_center = p_node->GetBoundingSphere();

		// Get DMA arrays
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
		sint32 *p_vert_array = new sint32[4 * num_verts];
		sint32 *p_uv_array = new sint32[2 * num_verts];
		uint32 *p_rgb_array = new uint32[num_verts];
		Mem::Manager::sHandle().PopContext();

		// Copy DMA data
		NxPs2::dma::ExtractXYZs(p_dma, (uint8 *) p_vert_array);
		NxPs2::dma::ExtractSTs(p_dma, (uint8 *) p_uv_array);
		NxPs2::dma::ExtractRGBAs(p_dma, (uint8 *) p_rgb_array);

		Mth::Vector v0;
		float u, v;
		Image::RGBA col;

		Dbg_Message("Number of verts: %d", num_verts);

		for (int idx = 0; idx < num_verts; idx++)
		{
			if (short_xyz)
			{
				NxPs2::dma::ConvertXYZToFloat(v0, &(p_vert_array[idx * 4]), mesh_center);
			} else {
				NxPs2::dma::ConvertXYZToFloat(v0, &(p_vert_array[idx * 4]));
			}
			NxPs2::dma::ConvertSTToFloat(u, v, &(p_uv_array[idx * 2]));
			col = *((Image::RGBA *) &(p_rgb_array[idx]));

			Dbg_Message("Vert %d", idx);
			Dbg_Message("Raw Pos (%x, %x, %x, %x)", p_vert_array[idx * 4 + 0], p_vert_array[idx * 4 + 1], p_vert_array[idx * 4 + 2], p_vert_array[idx * 4 + 3]);
			Dbg_Message("Pos (%f, %f, %f, %f)", v0[X], v0[Y], v0[Z], v0[W]);
			Dbg_Message("UV (%f, %f)", u, v);
			Dbg_Message("Color (%d, %d, %d, %d)", col.r, col.g, col.b, col.a);

		}

		// Free DMA buffers
		delete [] p_vert_array;
		delete [] p_uv_array;
		delete [] p_rgb_array;
	}
}
#endif // PRINT_RAW_RESULTS
#endif


/*
	Creates the set of source pieces. The data will have been loaded by this point.
*/
void CParkGenerator::InitializeMasterPieces(int parkW, int parkH, int parkL, int theme)
{
	if (flag_on(mMASTER_STUFF_LOADED))
		UnloadMasterPieces();
	
	set_flag(mMASTER_STUFF_LOADED);

	// When we reload the master piece, we reset the id of the generate pieces
	// so that net games will be syncronized
	m_next_id = 32;

	// set up park editor heap
	Dbg_MsgAssert(!mp_mem_region && !mp_mem_heap,(	"park editor heap already exists"));
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	#ifdef __PLAT_NGC__
	// Added 200K to the TOP_DOWN_REQUIRED_MARGIN for safety, and cancelled that by removing
	// 200K from the park editor heap. (Normally 900K)
	mp_mem_region = new Mem::AllocRegion(700000);	
	#else
	mp_mem_region = new Mem::AllocRegion(GetResourceSize("heap"));	
	#endif
	Mem::Manager::sHandle().PopContext();
	Ryan("  allocated park editor region at %p\n", mp_mem_region);
	mp_mem_heap = Mem::Manager::sHandle().CreateHeap( mp_mem_region, Mem::Allocator::vBOTTOM_UP, "Park Editor" );

	// fetch scene
	const char *p_scene_name="sk5ed";
		
	mp_source_scene = Nx::CEngine::sGetScene(p_scene_name);
	
	// set up all the source pieces
	Dbg_MsgAssert(mp_source_scene, ("couldn't get scene %s", p_scene_name));
	Lst::HashTable *p_sector_list = mp_source_scene->GetSectorList();
	Nx::CSector *p_sector;

//	printf("Setting up park editor source pieces:\n");
	p_sector_list->IterateStart();
	while ((p_sector = p_sector_list->IterateNext()))
	{
		Mem::Manager::sHandle().PushContext(mp_mem_heap);
		CSourcePiece *p_new_piece = new CSourcePiece();
		Mem::Manager::sHandle().PopContext();
		p_new_piece->mp_next_in_list = mp_source_piece_list;
		mp_source_piece_list = p_new_piece;

		//printf ("Piece with checksum 0x%x\n",p_sector->GetChecksum());
#ifdef __PLAT_NGPS__
#if PRINT_RAW_RESULTS
		if (p_sector->GetChecksum() == Crc::GenerateCRCFromString("Sk3Ed_F_10x10"))
		{
			Dbg_Message("******* Found the target piece");
			Mth::CBBox piece_bbox(p_sector->GetBoundingBox());
			Dbg_Message("Bounding Box (%.8f, %.8f, %.8f) - (%.8f, %.8f, %.8f)",
						piece_bbox.GetMin()[X], piece_bbox.GetMin()[Y], piece_bbox.GetMin()[Z],
						piece_bbox.GetMax()[X], piece_bbox.GetMax()[Y], piece_bbox.GetMax()[Z]);

			Nx::CPs2Geom *p_geom = static_cast(p_sector->GetGeom());
			NxPs2::CGeomNode *p_geom_node = p_geom->GetEngineObject();
			Dbg_Assert(p_geom_node);

			// Put all the meshes in an array
			int num_meshes = 0;
			NxPs2::CGeomNode *mesh_array[10];
			Nx::find_geom_leaves(p_geom_node, mesh_array, num_meshes);
			Dbg_Assert(num_meshes == 1);
			p_geom_node = mesh_array[0];

			print_ps2_data(p_geom_node);
		}
#endif
#endif
		p_new_piece->m_sector_checksum = p_sector->GetChecksum();

		// Init dimensions
		Mth::CBBox piece_bbox(p_sector->GetBoundingBox());
		
		int cell_w = (int) ((piece_bbox.GetMax()[X] - piece_bbox.GetMin()[X] + 2.0f) / CELL_WIDTH);
		if (cell_w < 1) cell_w = 1;
		p_new_piece->m_dims[X] = cell_w * CELL_WIDTH;
		
		int cell_l = (int) ((piece_bbox.GetMax()[Z] - piece_bbox.GetMin()[Z] + 2.0f) / CELL_LENGTH);
		if (cell_l < 1) cell_l = 1;
		p_new_piece->m_dims[Z] = cell_l * CELL_LENGTH;
		
		p_new_piece->m_dims[Y] = (piece_bbox.GetMax()[Y] - piece_bbox.GetMin()[Y]);

		// piece cannot have any dimension less the that of a park editor cell
		if (p_new_piece->m_dims[Y] < CParkGenerator::CELL_HEIGHT && p_new_piece->m_dims[Y] > 0.0f)
			p_new_piece->m_dims[Y] = CParkGenerator::CELL_HEIGHT;
		
		m_num_source_pieces++;
		
		#if 0
		//printf("   %s\n      [%f,%f,%f]-[%f,%f,%f]\n", Script::FindChecksumName(p_new_piece->m_sector_checksum),
		//	   piece_bbox.GetMin()[X], piece_bbox.GetMin()[Y], piece_bbox.GetMin()[Z],
		//	   piece_bbox.GetMax()[X], piece_bbox.GetMax()[Y], piece_bbox.GetMax()[Z]);
		printf("      %s [%.2f,%.2f,%.2f]\n", Script::FindChecksumName(p_new_piece->m_sector_checksum),
			   p_new_piece->m_dims[X], p_new_piece->m_dims[Y], p_new_piece->m_dims[Z]);
		#endif
	}

	mp_cloned_scene = Nx::CEngine::sCreateScene("cloned", mp_source_scene->GetTexDict(), true);

#ifdef __PLAT_NGC__
	// Need to copy scene data here, so stuff added to the cloned scene has geometry pools.
	Nx::CNgcScene *p_Ngc_scene = static_cast( mp_cloned_scene );
	Nx::CNgcScene *p_source_scene = static_cast( mp_source_scene );
	NxNgc::sScene *p_scene = p_Ngc_scene->GetEngineScene();
	memcpy( p_scene, p_source_scene->GetEngineScene(), sizeof( NxNgc::sScene ) );
	p_scene->m_num_meshes			= 0;		// No meshes as yet.
	p_scene->m_num_filled_meshes	= 0;
	p_scene->mpp_mesh_list			= NULL;

	p_scene->mp_hierarchyObjects	= NULL;
	p_scene->m_numHierarchyObjects	= 0;

	p_scene->mp_hierarchyObjects	= NULL;
	p_scene->m_numHierarchyObjects	= 0;

	// Make it renderable, and make sure it doesn't try to double-delete the scene data.
	p_scene->m_is_dictionary		= false;
	p_scene->m_flags				= SCENE_FLAG_CLONED_GEOM;
#endif		// __PLAT_NGC__

	Mth::CBBox b_box;
	Mth::Vector vmin(-((float) parkW * CELL_WIDTH) / 2.0f, -((float) parkH * CELL_HEIGHT) / 2.0f, -((float) parkL * CELL_LENGTH) / 2.0f);
	b_box.AddPoint(vmin);
	Mth::Vector vmax(((float) parkW * CELL_WIDTH) / 2.0f, ((float) parkH * CELL_HEIGHT) / 2.0f, ((float) parkL * CELL_LENGTH) / 2.0f);
	b_box.AddPoint(vmax);
	mp_cloned_scene->CreateCollision(b_box);

	KillRestarts();
}

/*
// K: Added to allow cleanup of the park editor heap during play
void CParkGenerator::DeleteSourceAndClonedPieces()
{
	CPiece *p_piece = mp_source_piece_list;
	while (p_piece)
	{
		CPiece *p_next = p_piece->mp_next_in_list;
		delete p_piece;
		p_piece = p_next;
	}
	mp_source_piece_list = NULL;
	m_num_source_pieces=0;

	printf("After deleting CParkGenerator::mp_source_piece_list ...\n");	
	printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems());
	MemView_AnalyzeHeap(Ed::CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap());
	
	p_piece = mp_cloned_piece_list;
	while(p_piece)
	{
		CPiece *p_next = p_piece->mp_next_in_list;
		delete p_piece;
		p_piece = p_next;
	}
	m_num_cloned_pieces=0;
	mp_cloned_piece_list=NULL;
	
	printf("After deleting CParkGenerator::mp_cloned_piece_list ...\n");	
	printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems());
	MemView_AnalyzeHeap(Ed::CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap());
}
*/

/*
	Destroys the set of source pieces.
*/
void CParkGenerator::UnloadMasterPieces()
{
	if (flag_on(mPIECES_IN_WORLD))
		DestroyAllClonedPieces(DESTROY_PIECES_AND_SECTORS);

	// GARRETT: not sure if you need to do anything here
	
	CPiece *p_piece = mp_source_piece_list;
	while (p_piece)
	{
		CPiece *p_next = p_piece->mp_next_in_list;
		delete p_piece;
		p_piece = p_next;
	}
	mp_source_piece_list = NULL;
	m_num_source_pieces--;
	//m_num_source_pieces=0;
	
	clear_flag(mMASTER_STUFF_LOADED);
	
	Mem::Manager::sHandle().RemoveHeap(mp_mem_heap);
	delete mp_mem_region;
	mp_mem_heap = NULL;
	mp_mem_region = NULL;
}

/*
void CParkGenerator::DeleteParkEditorHeap()
{
	if (mp_mem_heap)
	{
		Mem::Manager::sHandle().RemoveHeap(mp_mem_heap);
		delete mp_mem_region;
		mp_mem_heap = NULL;
		mp_mem_region = NULL;
	}	
}
*/

/* 
	Should be called after adding/removing sectors
*/
void CParkGenerator::PostGenerate()
{
	Dbg_MsgAssert(flag_on(mPIECES_IN_WORLD) && flag_on(mSECTORS_GENERATED), ("no pieces or sectors available"));
}




/*
	Should be called after adding a group of cloned pieces to world.
*/
void CParkGenerator::GenerateCollisionInfo(bool assert)
{
	// if this flag IS on, then we need to call UpdateSuperSectors() to free the memory
	if (flag_on(mSECTOR_MEMORY_NEEDS_FREE))
	{
	}
	else if (!assert)
	{
		if (!flag_on(mPIECES_IN_WORLD) || !flag_on(mSECTORS_GENERATED))
			return;
	}
	else
		Dbg_MsgAssert(flag_on(mPIECES_IN_WORLD) && flag_on(mSECTORS_GENERATED), ("no pieces or sectors available"));
	
	// GARRETT
	Dbg_Assert(mp_cloned_scene);
	mp_cloned_scene->UpdateSuperSectors();
	//Dbg_Message("Adding collision to scene %x", mp_cloned_scene->GetID());
	
	// XXX
	//printf("there are %d source pieces, %d cloned pieces\n", m_num_source_pieces, m_num_cloned_pieces);
	
	set_flag(mCOLLISION_INFO_GENERATED);
	clear_flag(mSECTOR_MEMORY_NEEDS_FREE);
}


void CParkGenerator::RemoveOuterShellPieces(int theme)
{
	uint32 shell_id=CParkManager::Instance()->GetShellSceneID();
	
	Nx::CScene *p_scene = Nx::CEngine::sGetScene(shell_id);
	if (p_scene)
	{
		// Turn everything on simply by using a huge bounding box
		Mth::CBBox a(Mth::Vector(-200000,-200000,-200000),Mth::Vector(200000,200000,200000));
		p_scene->SetActiveInBox(a,true,0.0f);
		
																	 
		// Calculate the bounding box of the park up to the edges 
 		GridDims  fence;
		fence = CParkManager::Instance()->GetParkNearBounds();		 					// returns the rectangle enclosed by the fence
		Mth::Vector start = CParkManager::Instance()->GridCoordinatesToWorld(fence);		// top left cornder
		
		fence.SetXYZ(fence.GetX()+fence.GetW()-1,fence.GetY(),fence.GetZ()+fence.GetL()-1);
		Mth::Vector end = CParkManager::Instance()->GridCoordinatesToWorld(fence);		// bot right corner, in one square

		// offset into the park by half a piece		
		start[X] += CParkGenerator::CELL_WIDTH/2;
		start[Z] += CParkGenerator::CELL_LENGTH/2;
		end[X] += CParkGenerator::CELL_WIDTH/2;
		end[Z] += CParkGenerator::CELL_LENGTH/2;
		
		// The Y of the world box calculated above is not correct.... so just make it bigh enough to encompass everything
		start[Y] = -100000;
		end[Y]   =  100000;
		Mth::CBBox b(start,end);
		// Note: the above bounding box is based on the centers of cells
		// so it misses a half cell boundry around the inside of the fence
		// however, this is actually close to what I want than the true bounding box
		// as I want to provide a little slack so we don't accidentally kill 
		// things that just come inside the fence by an inch or two.
		// (remember the shell collision is not active) 
		
		
		// and turn off everything that intersects the bounding box we just calculated
		p_scene->SetActiveInBox(b,false,0.0f);
		
	}
	else
	{
		Dbg_MsgAssert(0, ("Where is my scene?! %s", Script::FindChecksumName(shell_id)));
	}
}

void CParkGenerator::CleanUpOutRailSet()
{
	m_out_rail_set.Destroy();
	m_out_rail_set.FreeAllocators();
}

// K: Factored this code out of GenerateNodeInfo because it is required by FindNearestRailPoint
void CParkGenerator::GenerateOutRailSet(CMapListNode * p_concrete_metapiece_list)
{
	CleanUpOutRailSet();
	
	Mdl::Skate * p_skate_mod =  Mdl::Skate::Instance();
	if (p_skate_mod->m_cur_level == CRCD(0xe8b4b836,"load_sk5ed"))
	{
		// K: Not using the top down heap whilst in the editor to avoid running out of memory, 
		// since this causes a 192K spike in memory usage when doing a test play.
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
	}
	else	
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	}
	m_out_rail_set.SetupAllocators(GetResourceSize("out_railpoint_pool"), GetResourceSize("out_railstring_pool"), false);
	Mem::Manager::sHandle().PopContext();

	// Need to iterate over the pieces here, meaning we'd iterate over the meta pieces
	// and then find the pieces they are composed of.....
	// should be similar to rebuilding the park (in terms of iterating over the pieces)
	
	// We have been passed in the list of concrete metapiece
	// (the pieces that have actually been placed in the level
	CMapListNode *p_node = p_concrete_metapiece_list;
	while(p_node)
	{
		CConcreteMetaPiece *p_meta = p_node->GetConcreteMeta();

		if (p_meta->IsInPark() && !p_meta->NoRails())
		{
			// Iterate over the pieces in the metapiece
			int pieces = p_meta->CountContainedPieces();
			for (int i=0;iGetContainedPiece(i);
				CClonedPiece *pPiece = p_base_piece->CastToCClonedPiece();	    
	
	//			if (!(pPiece->m_type & CPiece::vNO_RAILS))
				{
					// go through all rail strings associated with source object
					for (int n = 0; ;n++)
					{
						uint32 piece_checksum = pPiece->mp_source_piece->m_sector_checksum;
						RailString *pSrcString = m_in_rail_set.GetString(piece_checksum, n);
						
						if (pSrcString)
						{
//							printf("creating rail string %d for %s, meta=%s\n", 
//								   n, Script::FindChecksumName(piece_checksum), Script::FindChecksumName(p_meta->GetNameChecksum()));
							RailString *pOutString = m_out_rail_set.CreateRailString();
							uint32	trickob_id = pPiece->GetID();
							if (p_meta->IsRiser())
							{
								// objects that are JUST risers cannot be trickobs
								trickob_id = 0;
							}
							pOutString->CopyOffsetAndRot(pSrcString, pPiece->m_pos, pPiece->m_rot, trickob_id);
							m_out_rail_set.AddString(pOutString);
						}
						else
						{
							//printf ("No rails here for %x: %s\n", piece_checksum, Script::FindChecksumName(piece_checksum));
							break;
						}
					}		
				} // end if (!(pPiece->m_type & Piece::vNO_RAILS))
			}
		}
		p_node = p_node->GetNext();
	}
}

/*
	For rail generation, etc.
*/
void CParkGenerator::GenerateNodeInfo(CMapListNode * p_concrete_metapiece_list)
{
	Dbg_MsgAssert(flag_on(mPIECES_IN_WORLD) && flag_on(mSECTORS_GENERATED), ("no pieces or sectors available"));
	
	int		total_nodes = 0;
	
	
	/*
	========================================================
	Begin Rail Generation
	========================================================
	*/

	printf("Making rail objects\n");

	GenerateOutRailSet(p_concrete_metapiece_list);	
	
	total_nodes	+= m_out_rail_set.CountPoints();
	
	
	
	/*
	========================================================
	End Rail Generation
	========================================================
	*/


	#if 1	


	printf("Generating node array\n");
	
	// count up regular object nodes
	int object_nodes = 0;
	CPiece *p_piece = mp_cloned_piece_list;
	while (p_piece)
	{
		if (!(p_piece->m_flags & CPiece::mFLOOR))
			object_nodes++;
		p_piece = p_piece->mp_next_in_list;
	}
	// count up object nodes
	total_nodes += object_nodes;
	
	#endif
	

	#if 1
	/*
	========================================================
	Do Some Restart Stuff
	========================================================
	*/
	
	// count up restart nodes
	for (int res = 0; res < vNUM_RESTARTS; res++)
	{
		if (m_restart_tab[res].type != vEMPTY)
			total_nodes++;
		// one player restarts are repeated as multiplayer
		if (m_restart_tab[res].type == vONE_PLAYER)
			total_nodes++;
		// HORSE restarts are repeated as multiplayer
		if (m_restart_tab[res].type == vHORSE)
			total_nodes++;
		// flag restart nodes will generate 3 extra nodes in addition to a restart
		if (
			m_restart_tab[res].type == vRED_FLAG
			|| m_restart_tab[res].type == vGREEN_FLAG
			|| m_restart_tab[res].type == vBLUE_FLAG
			|| m_restart_tab[res].type == vYELLOW_FLAG
		)
		{
		    total_nodes += 3; 
		}
			
	}

	#endif
	
	// Add in the number of nodes needed for the created rails.	
	int num_nodes_required=Obj::GetRailEditor()->GetTotalRailNodesRequired();
	Dbg_MsgAssert((uint32)num_nodes_required <= vMAX_ID_FOR_CREATED_RAILS-vFIRST_ID_FOR_CREATED_RAILS+1,("Too many created-rail nodes"));
	total_nodes+=num_nodes_required;
	
	
	/*
	========================================================
	Actually Create Node Array
	========================================================
	*/


	SkateScript::CreateNodeArray(total_nodes, false);

	Script::CArray *pNodeArray = Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
	int current_node_num = 0;
	#if 1
	set_up_object_nodes(pNodeArray, ¤t_node_num);
	#endif
	
	set_up_rail_nodes(pNodeArray, ¤t_node_num);
	
	Obj::GetRailEditor()->SetUpRailNodes(pNodeArray, ¤t_node_num, vFIRST_ID_FOR_CREATED_RAILS);
	
	#if 1
	set_up_restart_nodes(pNodeArray, ¤t_node_num);
	#endif
	
	CleanUpOutRailSet();

	
	printf("Calling CreateNodeNameHashTable()\n");
	SkateScript::CreateNodeNameHashTable();
	printf("Calling ScriptParseNodeArray()\n");
	CFuncs::ScriptParseNodeArray(NULL, NULL);

//	m_node_array_is_set_up = true;

	
	set_flag(mNODE_INFO_GENERATED);
}




/*
	Can use 'type' parameter only to remove only CPieces, but not underlying sector. This is useful
	for freeing up memory no longer needed once park is generated.
*/
void CParkGenerator::DestroyAllClonedPieces(EDestroyType type)
{
	if (type == DESTROY_PIECES_AND_SECTORS)
	{
		Obj::GetRailEditor()->DestroyRailGeometry();
		Obj::GetRailEditor()->DestroyPostGeometry();
	}	
	
	CPiece *p_entry = mp_cloned_piece_list;
	while(p_entry)
	{
		CPiece *p_next_entry = p_entry->mp_next_in_list;
		destroy_piece_impl(p_entry->CastToCClonedPiece(), type);
		p_entry = p_next_entry;
	}
	
	if (type == DESTROY_PIECES_AND_SECTORS)
		clear_flag(mSECTORS_GENERATED);
	clear_flag(mPIECES_IN_WORLD);

	if (type == DESTROY_PIECES_AND_SECTORS)
	{
		// CHUNKY
		Dbg_Assert(mp_cloned_scene);
		mp_cloned_scene->ClearSuperSectors();
	}	
}




void	CParkGenerator::HighlightAllPieces(bool highlight)
{
	CPiece *p_entry = mp_cloned_piece_list;
	while(p_entry)
	{
		CPiece *p_next_entry = p_entry->mp_next_in_list;
		p_entry->CastToCClonedPiece()->Highlight(highlight);
		p_entry = p_next_entry;
	}
}




void	CParkGenerator::SetGapPiecesVisible(bool visible)
{
#if 1	
	// Nothing doing here yet, eventually will turn off the wireframe pieces
	CPiece *p_entry = mp_cloned_piece_list;
	while(p_entry)
	{
		CPiece *p_next_entry = p_entry->mp_next_in_list;
		if (p_entry->CastToCClonedPiece()->GetFlags() & CPiece::mGAP)
		{
			//Dbg_Assert(0);
			p_entry->CastToCClonedPiece()->SetVisibility(visible);
		}
		p_entry = p_next_entry;
	}
#endif
}




void	CParkGenerator::SetRestartPiecesVisible(bool visible)
{
	Ryan("the stars go in and the stars go out %d\n", visible);
	CPiece *p_entry = mp_cloned_piece_list;
	while(p_entry)
	{
		CPiece *p_next_entry = p_entry->mp_next_in_list;
		uint32 id = p_entry->CastToCClonedPiece()->mp_source_piece->GetType();
		if (CParkManager::Instance()->IsRestartPiece(id))
		{
			p_entry->CastToCClonedPiece()->SetActive(visible);	
		}
		p_entry = p_next_entry;
	}
}




void CParkGenerator::SetLightProps(int num_lights, 
						  float amb_const_r, float amb_const_g, float amb_const_b, 
						  float falloff_const_r, float falloff_const_g, float falloff_const_b,
						  float cursor_ambience)
{
	
	Dbg_MsgAssert(num_lights <= vMAX_LIGHTS, ("too many lights"));
	m_numLights = num_lights;
	
	m_ambientLightIntensity.r = amb_const_r;
	m_ambientLightIntensity.g = amb_const_g;
	m_ambientLightIntensity.b = amb_const_b;
	m_falloffLightIntensity.r = falloff_const_r;
	m_falloffLightIntensity.g = falloff_const_g;
	m_falloffLightIntensity.b = falloff_const_b;

	m_cursorAmbience = cursor_ambience;
}




void CParkGenerator::SetLight(Mth::Vector &light_pos, int light_num)
{
	
	Dbg_MsgAssert(light_num < m_numLights, ("out of light slots"));

	m_lightTab[light_num] = light_pos;
}

											   
// Given the id of a piece in the world, apply the lights to it											   
void CParkGenerator::CalculateLighting(CClonedPiece *p_piece)
{	
	Nx::CSector *p_sector= Nx::CEngine::sGetSector(p_piece->m_sector_checksum);
	Dbg_MsgAssert(p_sector,("Trying to Calculate lighting on non-existant sector"))
	Nx::CGeom 	*p_geom = p_sector->GetGeom();
	Dbg_MsgAssert(p_geom,("sector does not have geometry"))
	
	Nx::CSector *p_source_sector= Nx::CEngine::sGetSector(p_piece->GetSourcePiece()->GetType());
	Dbg_MsgAssert(p_source_sector,("Trying to Calculate lighting on sector with no source sector"))
	Nx::CGeom 	*p_source_geom = p_source_sector->GetGeom();
	Dbg_MsgAssert(p_source_geom,("source sector does not have geometry"))

	//
	// Do renderable geometry
	int verts = p_geom->GetNumRenderVerts();
	Dbg_MsgAssert(verts == p_source_geom->GetNumRenderVerts(),("source and clone have differing no of verts (%d, %d)\n",verts,p_source_geom->GetNumRenderVerts()));

	if (verts)
	{
		Mth::Vector	*p_verts = new Mth::Vector[verts];
		Image::RGBA	*p_colors = new Image::RGBA[verts];
		p_geom->GetRenderVerts(p_verts);
		p_source_geom->GetRenderColors(p_colors);
	
		Image::RGBA *p_color = p_colors;
		Mth::Vector *p_vert = p_verts;
		for (int i = 0; i < verts; i++)
		{
			CalculateVertexLighting(*p_vert, *p_color);

			p_color++;
			p_vert++;
		} // end for

		// Set the colors on the actual new geom, not on the source...		
		p_geom->SetRenderColors(p_colors);
		
		delete [] p_verts;
		delete [] p_colors;
	} // end if
	else
	{
		// debuggery
		//p_geom->SetColor(Image::RGBA(0,0,100,0));
	}


	//
	// Do collision geometry
	Nx::CCollObjTriData *p_coll_tri_data = p_geom->GetCollTriData();
	Nx::CCollObjTriData *p_source_coll_tri_data = p_source_geom->GetCollTriData();

	Dbg_MsgAssert(p_coll_tri_data, ("sector does not have collision"));
	Dbg_MsgAssert(p_source_coll_tri_data, ("source sector does not have collision"));

#ifdef __PLAT_NGC__
	int faces = p_coll_tri_data->GetNumFaces();
	Dbg_MsgAssert(faces == p_source_coll_tri_data->GetNumFaces(), ("source and clone have differing no of collision faces (%d, %d)\n",verts,p_source_coll_tri_data->GetNumFaces()));

	Image::RGBA color;
	unsigned char intensity;
	unsigned int avg_color;
	for (int f = 0; f < faces; f++)
	{
		for ( int c = 0; c < 3; c++ )
		{
			intensity = p_source_coll_tri_data->GetVertexIntensity(f, c);

			color.r = intensity;
			color.g = intensity;
			color.b = intensity;
			color.a = 255;

			CalculateVertexLighting(p_coll_tri_data->GetRawVertexPos(p_coll_tri_data->GetFaceVertIndex(f, c)), color);
			avg_color = (color.r + color.g + color.b) / 3;
			p_coll_tri_data->SetVertexIntensity(f, c, avg_color);
		}
	}
#else
	verts = p_coll_tri_data->GetNumVerts();
	Dbg_MsgAssert(verts == p_source_coll_tri_data->GetNumVerts(), ("source and clone have differing no of collision verts (%d, %d)\n",verts,p_source_coll_tri_data->GetNumVerts()));

	Image::RGBA color;
	unsigned char intensity;
	unsigned int avg_color;
	for (int i = 0; i < verts; i++)
	{
		intensity = p_source_coll_tri_data->GetVertexIntensity(i);

		color.r = intensity;
		color.g = intensity;
		color.b = intensity;
		color.a = 255;

		CalculateVertexLighting(p_coll_tri_data->GetRawVertexPos(i), color);

		avg_color = (color.r + color.g + color.b) / 3;
		p_coll_tri_data->SetVertexIntensity(i, avg_color);

	}
#endif // __PLAT_NGC__
}


// apply lights to a vert											   
void CParkGenerator::CalculateVertexLighting(const Mth::Vector & vert, Image::RGBA & color)
{
	LightIntensity i_amb;

	i_amb.r = (float) color.r 	* m_ambientLightIntensity.r;
	i_amb.g = (float) color.g 	* m_ambientLightIntensity.g;
	i_amb.b = (float) color.b 	* m_ambientLightIntensity.b;

	float i_spot_light = 0.0f;
	Mth::Vector *pLightTabEntry = m_lightTab;
	for (int l = 0; l < m_numLights; l++)
	{
		float dist_x = vert.GetX() - pLightTabEntry->GetX();
		//float dist_y = vert.GetY() - pLightTabEntry->GetY();
		float dist_y = 400;	// Mick:  Patched to ignore height, otherwise we have burningly bright spots on high walls
							// you can decrease this number make the lighhing more intense
							// (note, this means no verticle variation in brightness in the park editor
		float dist_z = vert.GetZ() - pLightTabEntry->GetZ();

		float dist_squared = dist_x * dist_x + dist_y * dist_y + dist_z * dist_z;
		i_spot_light += 1.0f / dist_squared;

		pLightTabEntry++;
	}
	i_spot_light *= 255.0f;

	i_amb.r += i_spot_light * m_falloffLightIntensity.r;
	if (i_amb.r > 255.0)
		color.r = 255;
	else
		color.r = (uint8) (i_amb.r);

	i_amb.g += i_spot_light * m_falloffLightIntensity.g;
	if (i_amb.g > 255.0)
		color.g = 255;
	else 
		color.g = (uint8) (i_amb.g);

	i_amb.b += i_spot_light * m_falloffLightIntensity.b;
	if (i_amb.b > 255.0)
		color.b = 255;
	else
		color.b	= (uint8) (i_amb.b);
}


/*
	See comments in DestroyAllClonedPieces() for 'destroyType'
*/
void CParkGenerator::destroy_piece_impl(CClonedPiece *pPiece, EDestroyType destroyType)
{
	// XXX
	if (pPiece->m_flags & CPiece::mGAP)
		Ryan("@@@ destroying gap piece 0x%x\n", pPiece);

	if (!(pPiece->m_flags & CClonedPiece::mSOFT_PIECE))
	{
		// remove from list	first
		CPiece *p_entry = mp_cloned_piece_list;
		CPiece *p_prev = NULL;
		while(p_entry)
		{
			if (p_entry == pPiece)
			{
				if (p_prev)
					p_prev->mp_next_in_list = p_entry->mp_next_in_list;
				else
					mp_cloned_piece_list = p_entry->mp_next_in_list;
				break;
			}
			
			p_prev = p_entry;
			p_entry = p_entry->mp_next_in_list;
		}
		Dbg_MsgAssert(p_entry, ("piece not found"));
		
		if (!(pPiece->m_flags & CPiece::mNO_RAILS))
		{
			m_total_rail_points -= pPiece->mp_source_piece->m_num_rail_points;
			m_total_rail_linked_points -= pPiece->mp_source_piece->m_num_linked_rail_points;
		}
	}

	// now actually destroy piece
	if (destroyType == DESTROY_PIECES_AND_SECTORS)
	{
		#if 0
		printf("    Gott mit uns (%f,%f,%f)\n", 
			   pPiece->GetPos().GetX(), pPiece->GetPos().GetY(), pPiece->GetPos().GetZ());
		#endif
		// destroy underlying sector
		mp_cloned_scene->DeleteSector(pPiece->m_sector_checksum);
		
		#ifdef __PLAT_NGC__
		//printf("Called DeleteSector\n");
		Nx::CEngine::sFinishRendering();
		#endif
	}
	
	m_num_cloned_pieces--;
	
	delete pPiece;

	if (!mp_cloned_piece_list)
		clear_flag(mPIECES_IN_WORLD);
	set_flag(mSECTOR_MEMORY_NEEDS_FREE);
}



// Read in the node array containing the rail information
// and parse this into the park editor format....

// called from CParkManager::Initialize()
void CParkGenerator::ReadInRailInfo()
{
	const uint32 crc_cluster 		= 0x1a3a966b;
    const uint32 crc_class 			= 0x12b4e660;
	const uint32 crc_links 			= 0x2e7d5ee7;

	printf ("Starting ReadInRailInfo()\n");

// Mick - Not sure where this should be set up
	m_in_rail_set.SetupAllocators(GetResourceSize("in_railpoint_pool"), GetResourceSize("in_railstring_pool"), true);

	
	// Ken: This function has gone now, since the new ability to unload a qb has made it redundant.
	//Script::RemoveOldTriggerScripts();
	
	// Remove any spawned scripts that might have been left over from the previous level.
	Script::DeleteSpawnedScripts();	
		
	// Load the QB
//	SkateScript::LoadQB(PIECE_SET_QB[m_currentThemeNumber]);
//	SkateScript::LoadQB("levels\\sk4ed\\sk4ed.qb");			  // assume it has already been loaded (NOTE: If not, then you will have problems with PRE files, as the .QBs do not go in the pre file)
	// Ensure it has a node array in it
	Script::CArray *pNodeArray=Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
	Dbg_MsgAssert(pNodeArray,("Park editor node array not found"));
		
	// this will load up sounds
	Script::RunScript("LoadTerrain");
	
	Ryan("\n*** Reading in rail info ***\n");
	m_in_rail_set.Destroy();
	// BAD MEM
	m_processed_node_tab = new int[256];
	m_processed_node_tab_entries = 0;
	m_temp_node_tab = new TempNodeInfo[2048];

	
	int index = 0;
	for ( ; index < (int)pNodeArray->GetSize(); index++)
	{
		Script::CScriptStructure *pStruct = pNodeArray->GetStructure(index);
	
		m_temp_node_tab[index].classCrc = 0;
		pStruct->GetChecksum(crc_class, &m_temp_node_tab[index].classCrc);
		m_temp_node_tab[index].cluster = 0;
		pStruct->GetChecksum(crc_cluster, &m_temp_node_tab[index].cluster);
		m_temp_node_tab[index].pLinkArray = NULL;
		pStruct->GetArray(crc_links, &m_temp_node_tab[index].pLinkArray);
	}

	
	const bool debug_rail_read = true;
	
	// XXX
	Ryan("number of node entries is %d\n", pNodeArray->GetSize());
	
	index = 0;
	// loop through all LevelGeometry objects in node array.
	// for each one, locate associated rail nodes (if any) and create RailString.
	while(1)
	{
		// get cluster checksum of LevelGeometry object, will be used to
		// look up rail nodes
		uint32 cluster_crc = scan_for_cluster(pNodeArray, index);
		if (index >= (int)pNodeArray->GetSize())
		{
			printf ("No more clusters, exiting\n"); 
			break;
		}
		
//		printf("source piece %s for cluster \n", Script::FindChecksumName(cluster_crc));		
		
		//debug_rail_read = (cluster_crc == 0x21997899);
#ifdef __NOPT_ASSERT__
		CPiece *pMasterPiece = GetMasterPiece(cluster_crc);
		
		CSourcePiece *pSourcePiece = pMasterPiece->CastToCSourcePiece();
		
		Dbg_MsgAssert(pSourcePiece, ("no source piece %s found", Script::FindChecksumName(cluster_crc)));		
#endif		// __NOPT_ASSERT__
		if (debug_rail_read)
			Ryan("found cluster %s at index %d\n", Script::FindChecksumName(cluster_crc), index-1);

		Mth::Vector v;
		RailPoint::RailType type;
			   
		// find all rail strings associated with current LevelGeometry object
		// m_processed_node_tab[] keeps track of strings already processed
		m_processed_node_tab_entries = 0;
		while (1)
		{
			bool string_is_loop = false;
			
			// try to locate a rail node that matches the cluster and has no links
			// if it fails to find one, it will try to return one that matches the cluster and does have links
			int last_node_in_set = scan_for_rail_node(pNodeArray, cluster_crc, -1, &v, &string_is_loop, &type);
			if (last_node_in_set == -1)
				break;

			int current_node = last_node_in_set;
			
			RailString *pCurrentString = m_in_rail_set.CreateRailString();
			pCurrentString->m_id = cluster_crc;
			pCurrentString->m_isLoop = string_is_loop;

			RailPoint *pPoint = m_in_rail_set.CreateRailPoint();
			//pPoint->m_pos.Set(v.GetX() - pSourcePiece->m_pos.GetX(), v.GetY() - pSourcePiece->m_pos.GetY(), v.GetZ() + pSourcePiece->m_pos.GetZ());
			pPoint->m_pos.Set(v.GetX(), v.GetY(), v.GetZ());
			pPoint->m_type = type;
			if (debug_rail_read)
				Ryan("* [%.1f,%.1f,%.1f]", pPoint->m_pos.GetX(), pPoint->m_pos.GetY(), pPoint->m_pos.GetZ());
			pCurrentString->AddPoint(pPoint);
			
			// go through all points in the string (after the first)
			while (1)
			{
				current_node = scan_for_rail_node(pNodeArray, cluster_crc, current_node, &v, &string_is_loop, &type);
				if (current_node == -1)
				{
					if (debug_rail_read && pCurrentString->m_isLoop)
						Ryan(" (loop)");
					break;
				}

				
				pPoint = m_in_rail_set.CreateRailPoint();
				//pPoint->m_pos.Set(v.GetX() - pSourcePiece->m_pos.GetX(), v.GetY() - pSourcePiece->m_pos.GetY(), v.GetZ() + pSourcePiece->m_pos.GetZ());
				pPoint->m_pos.Set(v.GetX(), v.GetY(), v.GetZ());
				pPoint->m_type = type;
				if (debug_rail_read)
					Ryan("-[%.1f,%.1f,%.1f]", pPoint->m_pos.GetX(), pPoint->m_pos.GetY(), pPoint->m_pos.GetZ());
				pCurrentString->AddPoint(pPoint);
			}

			if (debug_rail_read)
				Ryan("\n");
			m_in_rail_set.AddString(pCurrentString);
		} // end while
	} // end while

	delete m_processed_node_tab;
	delete m_temp_node_tab;

	scan_in_trigger_info(pNodeArray);
//
//	m_nodeArrayIsSetup = true;
//	DestroyCollisionDataAndNodeArray();

	// now that rail info is set up, it's safe to do this next block of code
	CPiece *p_piece = mp_source_piece_list;
	while(p_piece)
	{
		CSourcePiece *p_source_piece = p_piece->CastToCSourcePiece();

		p_source_piece->m_num_rail_points = 0;
		p_source_piece->m_num_linked_rail_points = 0;
		int string_num = 0;
		while(1)
		{
			RailString *p_rail_string = m_in_rail_set.GetString(p_source_piece->GetType(), string_num++);
			if (!p_rail_string)
				break;
			p_source_piece->m_num_rail_points += p_rail_string->Count();
			p_source_piece->m_num_linked_rail_points += p_rail_string->CountLinkedPoints();
		}

		p_piece = p_piece->mp_next_in_list;
	}
}




// called from CParkManager::Destroy()
void CParkGenerator::DestroyRailInfo()
{
	m_in_rail_set.Destroy();
	m_in_rail_set.FreeAllocators();
}







// returns checksum of first found cluster, increases index appropriately
// called from ReadInRailInfo()
uint32 CParkGenerator::scan_for_cluster(Script::CArray *pNodeArray, int &index)
{
    const uint32 crc_class 			= 0x12b4e660;
    const uint32 crc_levelgeometry = 0xbf4d3536;		//0xdabd3086;
	const uint32 crc_name 			= 0xa1dc81f9;

	int total_entries = pNodeArray->GetSize();

	/*
		Given a node like...
		
		{
			// Node 471
			Position = (-0.002991,1200.000000,0.000854)
			Angles = (0.000000,0.000000,0.000000)
			Name = Sk3Ed_Shell_Col_16x16_01
			Class = levelgeometry
			CreatedAtStart
		}
		
		... make sure it's level geometry, then find the value
		of 'Name'
	*/
	while (index < total_entries)
	{
		Script::CStruct *pStruct = pNodeArray->GetStructure(index);
		
		uint32 node_class = 0;
		pStruct->GetChecksum(crc_class, &node_class);
		
		if (node_class == crc_levelgeometry)
		{
			uint32 name;
			if (pStruct->GetChecksum(crc_name, &name))
			{
				index++;
				return name;
			}			
		}

		index++;
	}

	return 0;
}




/* 
	returns number of node
	3 ways of calling:
		-link set: find rail node with that link
		-cluster set, link -1: find rail node with that cluster, but no link
		-cluster set, link -2: find rail node with that cluster, can have a link
	
	Called from ReadInRailInfo()
*/
int CParkGenerator::scan_for_rail_node(Script::CArray *pNodeArray, uint32 cluster, int link, Mth::Vector *pVector, bool *pHasLinks, RailPoint::RailType *pType)
{
	
	
//	const uint32 crc_position 		= 0xb9d31b0a;
	//const uint32 crc_links 			= 0x2e7d5ee7;
	//const uint32 crc_cluster 		= 0x1a3a966b;
	const uint32 crc_terraintype	= 0x54cf8532;
//	const uint32 crc_terrain_wood	= 0x39075ea5;
	
    //const uint32 crc_class 			= 0x12b4e660;
    const uint32 crc_railnode 		= 0x8e6b02ad;

	int found_node_with_link_index = -1;
	int found_node_without_link_index = -1;
	
	/*
		A little optimization:
	
		We first call this function with link<0, so at that time
		store the index of the first node belonging to the requested
		cluster.
		
		Several following calls to this function will start the node
		search from this point
	*/
	
	static int first_node_in_cluster;
	if (link < 0)
		first_node_in_cluster = 0;

	int total_entries = pNodeArray->GetSize();
	for (int n = first_node_in_cluster; n < total_entries; n++)
	{
		//Script::CScriptStructure *pStruct = pNodeArray->GetStructure(n);
	
		uint32 node_class = m_temp_node_tab[n].classCrc;
		if (node_class == crc_railnode)
		{
			// find link
			int found_link = -1;
			Script::CArray *pLinkArray = m_temp_node_tab[n].pLinkArray;
			if (pLinkArray)
			{
				Dbg_MsgAssert(pLinkArray->GetSize() == 1, ("Rail node %d has more than one link (%d)",n,pLinkArray->GetSize()));
				found_link = pLinkArray->GetInt(0);
			}
			
			uint32 found_cluster = m_temp_node_tab[n].cluster;
			//Ryan("   cluster is %s, link is %d\n", Script::FindChecksumName(found_cluster), found_link);

			if (link >= 0)
			{
				if (found_link == link)
				{
					found_node_with_link_index = n;
					for (int i = 0; i < m_processed_node_tab_entries; i++)
					{
						if (m_processed_node_tab[i] == found_node_with_link_index)
							// we've already processed this node
							found_node_with_link_index = -1;
					}
					if (found_node_with_link_index != -1)
						// in this case, we have all the info we need, so exit loop
						break;
				}
			}
			else if (cluster == found_cluster)
			{
				if (!first_node_in_cluster)
					first_node_in_cluster = n;
				
				if (found_link == -1)
				{
					found_node_without_link_index = n;
					for (int i = 0; i < m_processed_node_tab_entries; i++)
					{
						if (m_processed_node_tab[i] == found_node_without_link_index)
							// we've already processed this node
							found_node_without_link_index = -1;
					}
					if (found_node_without_link_index != -1)
						// this is the node we're looking for, so exit loop
						break;
				}
				else if (found_node_with_link_index == -1) // && found_link >= 0
				{
					found_node_with_link_index = n;
					for (int i = 0; i < m_processed_node_tab_entries; i++)
					{
						if (m_processed_node_tab[i] == found_node_with_link_index)
							// we've already processed this node
							found_node_with_link_index = -1;
					}
					// even if not already processed, this node is secondary priority, 
					// so we will not be exiting the loop
				}
			} // end else if
		} // if (node_class == crc_railnode)
	}
		
	// the node without a link has greater priority
	int match_index = -1;
	if (found_node_with_link_index != -1)
	{
		match_index = found_node_with_link_index;
		*pHasLinks = true;
	}
	if (found_node_without_link_index != -1)
	{
		match_index = found_node_without_link_index;	
		*pHasLinks = false;
	}
	if (match_index == -1) return -1;

	// store this node so we won't use it twice
	m_processed_node_tab[m_processed_node_tab_entries++] = match_index;
	// fetch position info (will be returned to caller)
	Script::CScriptStructure *pFound = pNodeArray->GetStructure(match_index);
	
	pFound->GetVector(CRCD(0x7f261953,"Pos"), pVector, true);

// GJ:  The following was incorrect, because the Z component should have been negated,
// however, the function that was supposed to use this data should have negated it again,
// but didn't, meaning that the 2 bugs ended up cancelling each other out.
// The new-style 'pos' vector is already correct and needs no negation.
//	pFound->GetVector(CRCD(0xb9d31b0a,"Position"), pVector, false);
	
	uint32 terrain_type; // terrain_wood
	pFound->GetChecksum(crc_terraintype, &terrain_type, true);
	
//	if (terrain_type == crc_terrain_wood)
//		*pType = RailPoint::vWOOD;
//	else
//		*pType = RailPoint::vMETAL;

	*pType =  terrain_type; 

	return match_index;
}




// 	Should be called from ReadInRailInfo(), but isn't
void CParkGenerator::scan_in_trigger_info(Script::CArray *pNodeArray)
{
	//const uint32 crc_position 		= 0xb9d31b0a;
    const uint32 crc_class 			= 0x12b4e660;
    const uint32 crc_levelgeometry = 0xbf4d3536;		//0xdabd3086;
	const uint32 crc_name 			= 0xa1dc81f9;
    const uint32 crc_triggerscript 	= 0x2ca8a299;

	for (uint32 n = 0; n < pNodeArray->GetSize(); n++)
	{
		Script::CScriptStructure *pStruct = pNodeArray->GetStructure(n);
		
		uint32 node_class = 0;
		pStruct->GetChecksum(crc_class, &node_class);
		
		if (node_class == crc_levelgeometry)
		{
			uint32 piece_name = 0;
			pStruct->GetChecksum(crc_name, &piece_name);
			uint32 trigger_name = 0;
			pStruct->GetChecksum(crc_triggerscript, &trigger_name);
			
			if (piece_name && trigger_name)
			{
				// XXX
				Ryan("Setting up trigger script: piece=%s script=%s\n", Script::FindChecksumName(piece_name), Script::FindChecksumName(trigger_name));
				CPiece *pPiece = GetMasterPiece(piece_name, true);
				CSourcePiece *pSourcePiece = pPiece->CastToCSourcePiece();
				pSourcePiece->m_triggerScriptId = trigger_name;
			}
		}
	}
	Ryan("done with all rail reading\n");
}




void CParkGenerator::set_up_object_nodes(Script::CArray *pNodeArray, int *pNodeNum)
{
	

	Ryan("set_up_object_nodes: %d/%d\n", Mem::CPoolable::SGetNumUsedItems(), Mem::CPoolable::SGetTotalItems());
	Ryan("CVector use: %d/%d\n", Mem::CPoolable::SGetNumUsedItems(), Mem::CPoolable::SGetTotalItems());

#if 1
//	uint32 crc_position 			= Script::GenerateCRC("position");
	uint32 crc_angles 				= Script::GenerateCRC("angles");
	uint32 crc_name 				= Script::GenerateCRC("name");
	uint32 crc_class 				= Script::GenerateCRC("class");
	uint32 crc_levelgeometry 		= Script::GenerateCRC("LevelGeometry");
	uint32 crc_createdatstart 		= Script::GenerateCRC("CreatedAtStart");
	uint32 crc_triggerscript 		= Script::GenerateCRC("TriggerScript");
	uint32 crc_trickobject 			= Script::GenerateCRC("TrickObject");
	uint32 crc_cluster 				= Script::GenerateCRC("Cluster");
	
	// 7
	CPiece *p_piece = mp_cloned_piece_list;
	while (p_piece)
	{
		CClonedPiece *p_cloned = p_piece->CastToCClonedPiece();
		if (!(p_cloned->GetFlags() & CPiece::mFLOOR) && 
			!(p_cloned->GetFlags() & CPiece::mRESTART))
		{
			
			Script::CScriptStructure *pNode = pNodeArray->GetStructure(*pNodeNum);
			Mth::Vector pos = p_cloned->GetPos();
			pNode->AddComponent(CRCD(0x7f261953,"pos"), pos.GetX(), pos.GetY(), pos.GetZ());
// GJ:  The following was incorrect, and the Z component should have been negated,
// but no one ever really used it, so we never noticed that it was a problem...
// The new-style 'pos' vector is already correct and needs no negation.
//			pNode->AddComponent(CRCD(0xb9d31b0a,"position"), pos.GetX(), pos.GetY(), pos.GetZ());
			pNode->AddComponent(crc_angles, 0.0f, 0.0f, 0.0f);
			// hopefully safe
			// XXX
			Ryan("id is 0x%x\n", p_cloned->m_id);
			pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, (int) p_cloned->m_sector_checksum);
			pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, (int) crc_levelgeometry);
			pNode->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int) crc_createdatstart);
			
																				  
			pNode->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int) crc_trickobject);
// Mick:  use the cloned pieces ID, but EOR with with 0xffffffff so it's different to the original name
			pNode->AddComponent(crc_cluster, ESYMBOLTYPE_NAME, (int) p_cloned->GetID() ^ 0xffffffff);

			
			// trigger script
			if (p_cloned->GetFlags() & CPiece::mGAP)
			{
				Dbg_Assert(CGapManager::sInstance());
				pNode->AddComponent(crc_triggerscript, ESYMBOLTYPE_NAME, (int) CGapManager::sInstance()->GetGapTriggerScript(p_cloned));
			}
			else if (p_cloned->GetSourcePiece()->m_triggerScriptId)
				pNode->AddComponent(crc_triggerscript, ESYMBOLTYPE_NAME, (int) p_cloned->GetSourcePiece()->m_triggerScriptId);
			
			(*pNodeNum)++;
		}
		p_piece = p_piece->mp_next_in_list;
	}
#endif
}

// Used by the rail editor for snapping to the nearest rail point.
bool CParkGenerator::FindNearestRailPoint(Mth::Vector &pos, Mth::Vector *p_nearest_pos, float *p_dist_squared)
{
	float min_dist_squared=100000000.0f;
	bool found_point=false;
	RailString *p_string = m_out_rail_set.GetList();
	
	while (p_string)
	{
		RailPoint *p_point = p_string->GetList();
		
		while (p_point)
		{
			Mth::Vector diff=p_point->m_pos-pos;
			float dd=diff.LengthSqr();
			
			if (dd < min_dist_squared)
			{
				min_dist_squared=dd;
				
				*p_nearest_pos=p_point->m_pos;
				*p_dist_squared=min_dist_squared;
				found_point=true;
			}
			
			p_point = p_point->GetNext();
		}
			
		p_string = p_string->GetNext();
	}

	return found_point;
}

// called from GenerateNodeInfo()
void CParkGenerator::set_up_rail_nodes(Script::CArray *pNodeArray, int *pNodeNum)
{

	Ryan("set_up_rail_nodes: %d/%d\n", Mem::CPoolable::SGetNumUsedItems(), Mem::CPoolable::SGetTotalItems());

#if 1	
	
	const bool debug_rail_out = false;
	
	uint32 crc_angles 			= Script::GenerateCRC("angles");
	uint32 crc_name 			= Script::GenerateCRC("name");
	uint32 crc_class 			= Script::GenerateCRC("class");
	uint32 crc_railnode 		= Script::GenerateCRC("railnode");
	uint32 crc_createdatstart 	= Script::GenerateCRC("CreatedAtStart");
	uint32 crc_links 			= Script::GenerateCRC("links");
	
//	uint32 crc_type				= Script::GenerateCRC("type");
//	uint32 crc_metal			= Script::GenerateCRC("metal");
//	uint32 crc_wood				= Script::GenerateCRC("wood");
	uint32 crc_terraintype		= Script::GenerateCRC("terraintype");
//	uint32 crc_terrain_wood		= Script::GenerateCRC("terrain_wood");
//	uint32 crc_terrain_metal	= Script::GenerateCRC("terrain_metal");
	
	uint32 crc_trickobject 		= Script::GenerateCRC("TrickObject");
	uint32 crc_cluster 			= Script::GenerateCRC("Cluster");
	
	/*
	Position = (-1619.978027,-336.000031,-6870.965332)
	Angles = (0.000000,-3.141589,0.000000)
	Name = TRG_Conc_Park_Rail0
	Class = RailNode
	Type = Concrete
	CreatedAtStart
	TerrainType = TERRAIN_CONCSMOOTH
	TrickObject Cluster = ParkingLotSpine1
	TriggerScript = CanTRG_Conc_Park_Rail0Script
	Links=[1]
	*/
	
	int num_points = m_out_rail_set.CountPoints();
	if (!num_points) return;

	RailString *pString = m_out_rail_set.GetList();
	RailPoint *pPoint = pString->GetList();
	
	if (debug_rail_out)
		Ryan("String %s: ", Script::FindChecksumName(pString->m_id));	
	int first_node_in_loop = *pNodeNum;
	
	// 10
	uint32 rail_point_id = vFIRST_ID_FOR_RAILS;
	int stop_node_num = *pNodeNum + num_points;
	for (; *pNodeNum < stop_node_num; (*pNodeNum)++)
	{
		Dbg_Assert(pPoint);
		
		Script::CStruct *pNode = pNodeArray->GetStructure(*pNodeNum);
		pNode->AddComponent(CRCD(0x7f261953,"pos"), pPoint->m_pos.GetX(), pPoint->m_pos.GetY(), pPoint->m_pos.GetZ());
//		pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), pPoint->m_pos.GetX(), pPoint->m_pos.GetY(), pPoint->m_pos.GetZ());
		if (debug_rail_out)
			Ryan("[%.1f,%.1f,%.1f]", pPoint->m_pos.GetX(), pPoint->m_pos.GetY(), pPoint->m_pos.GetZ());
		pNode->AddComponent(crc_angles, 0.0f, 0.0f, 0.0f);
		Dbg_MsgAssert(rail_point_id < vMAX_ID_FOR_RAILS, ("out of rail ids"));
		pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, (int) rail_point_id++);
		pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, (int) crc_railnode);
		pNode->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int) crc_createdatstart);

/*	
		uint32 type, terrain_type;

		
		if (pPoint->m_type == RailPoint::vWOOD)
		{
			type = crc_wood;
			terrain_type = crc_terrain_wood;
		}
		else
		{
			type = crc_metal;
			terrain_type = crc_terrain_metal;
		}
*/		

// Don't need type?		
//		pNode->AddComponent(crc_type, ESYMBOLTYPE_NAME, (int) type);
		
		pNode->AddComponent(crc_terraintype, ESYMBOLTYPE_NAME, (int) pPoint->m_type);
		
		// Mick: m_objectId will be zero if this rail is not part of a trickob
		if (pPoint->m_objectId)
		{
			pNode->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int) crc_trickobject);
			pNode->AddComponent(crc_cluster, ESYMBOLTYPE_NAME, (int) pPoint->m_objectId ^ 0xffffffff);
		}
		
		if (pPoint->GetNext() != NULL || pString->m_isLoop)
		{
			int link_num = *pNodeNum + 1;
			if (pPoint->GetNext() == NULL && pString->m_isLoop)
				link_num = first_node_in_loop;
			
			Script::CArray *pLinks = new Script::CArray();
			pLinks->SetSizeAndType(1,ESYMBOLTYPE_INTEGER);
			pLinks->SetInteger(0, link_num);
			pNode->AddComponent(crc_links, pLinks);
			delete pLinks;
		}

		pPoint = pPoint->GetNext();
		if (pPoint)
		{
			if (debug_rail_out)
				Ryan("-");
		}
		else
		{
			if (debug_rail_out)
			{
				if (pString->m_isLoop) Ryan(" (loop)");
				Ryan("\n");
			}
			pString = pString->GetNext();
			if (pString)
			{
				if (debug_rail_out)
					Ryan("String %s: ", Script::FindChecksumName(pString->m_id));
				pPoint = pString->GetList();
				first_node_in_loop = *pNodeNum + 1;
			}
		}
	}
	
#endif	
}




// called from GenerateNodeInfo()
void CParkGenerator::set_up_restart_nodes(Script::CArray *pNodeArray, int *pNodeNum)
{
	uint32 crc_angles 			= Script::GenerateCRC("angles");
	uint32 crc_name 			= Script::GenerateCRC("name");
	uint32 crc_class 			= Script::GenerateCRC("class");
	uint32 crc_type 			= Script::GenerateCRC("type");
	uint32 crc_restart 			= Script::GenerateCRC("restart");
	uint32 crc_genericnode		= Script::GenerateCRC("GenericNode");
	uint32 crc_createdatstart 	= Script::GenerateCRC("CreatedAtStart");
	uint32 crc_restartname 		= Script::GenerateCRC("RestartName");
	uint32 crc_restart_types 	= Script::GenerateCRC("restart_types");
	uint32 crc_model		 	= Script::GenerateCRC("model");
	uint32 crc_triggerscript 	= Script::GenerateCRC("TriggerScript");
	
	uint32 restart_id = vFIRST_ID_FOR_RESTARTS;
	
	for (int i = 0; i < vNUM_RESTARTS; i++)
	{
		if (m_restart_tab[i].type != vEMPTY)
		{
			//Ryan("yo, restart at (%.2f,%.2f,%.2f)\n", m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), m_restart_tab[i].pos.GetZ());
			
			// if restart is ONE_PLAYER or HORSE type, it will also be repeated 
			// as a multiplayer
			int repeat_num = 1;
			if (m_restart_tab[i].type == vHORSE)
				repeat_num = 2;
			if (m_restart_tab[i].type == vONE_PLAYER)
				repeat_num = 2;				

			for (int n = 0; n < repeat_num; n++)
			{
				/*
				{
					// Node 1965
					Position = (-476.991364,-70.263161,-5703.516602)
					Angles = (0.000000,0.000000,0.000000)
					Name = TRG_Crown01
					Class = GenericNode
					Type = Crown
					CreatedAtStart
				}
				{
					// Node 2912
					Position = (2940.254639,-1368.771729,-753.127625)
					Angles = (0.000000,2.687807,0.000000)
					Name = TRG_CTF_Restart_Red
					Class = Restart
					Type = CTF
					CreatedAtStart
					CollisionMode = BoundingBox
					RestartName = "Team: CTF"
					restart_types = [ CTF ]
				}
				
				*/
				
				
				int restart_name = restart_id++;		// use generic name for most restarts
				uint32 type_checksum = 0;
				if (m_restart_tab[i].type == vONE_PLAYER)
				{
					type_checksum = Script::GenerateCRC("Player1");
				}
				if (m_restart_tab[i].type == vHORSE)
				{
					type_checksum = Script::GenerateCRC("Horse");
				}
				if (m_restart_tab[i].type == vMULTIPLAYER || n == 1)
				{	
					type_checksum = Script::GenerateCRC("Multiplayer");
				}
				if (m_restart_tab[i].type == vKING_OF_HILL)
				{
					type_checksum = Script::GenerateCRC("Crown");
				}
				if (
					m_restart_tab[i].type == vRED_FLAG
					|| m_restart_tab[i].type == vGREEN_FLAG
					|| m_restart_tab[i].type == vBLUE_FLAG
					|| m_restart_tab[i].type == vYELLOW_FLAG
					)
				{
					type_checksum = Script::GenerateCRC("CTF");
					switch (m_restart_tab[i].type)
					{
						case vRED_FLAG:
							restart_name = Script::GenerateCRC("TRG_CTF_Restart_Red");
							break;
						case vGREEN_FLAG:
							restart_name = Script::GenerateCRC("TRG_CTF_Restart_Green");
							break;
						case vBLUE_FLAG:
							restart_name = Script::GenerateCRC("TRG_CTF_Restart_Blue");
							break;
						case vYELLOW_FLAG:
							restart_name = Script::GenerateCRC("TRG_CTF_Restart_Yellow");
							break;
						default:
							Dbg_MsgAssert(0,("Impossible!"));
							break;
					}
				}
				
				if (type_checksum != 0)
				{
	
					Script::CScriptStructure *pNode = pNodeArray->GetStructure(*pNodeNum);
					if (m_restart_tab[i].type == vKING_OF_HILL)
					{
						pNode->AddComponent(CRCD(0x7f261953,"pos"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY() + 50.0f, m_restart_tab[i].pos.GetZ());
//						pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY() + 50.0f, -m_restart_tab[i].pos.GetZ());
						pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, (int) crc_genericnode);
					}
					else
					{
						pNode->AddComponent(CRCD(0x7f261953,"pos"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY() + 50.0f, m_restart_tab[i].pos.GetZ());
//						pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), -m_restart_tab[i].pos.GetZ());
						pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, (int) crc_restart);
					}
					pNode->AddComponent(crc_angles, 0.0f, Mth::PI * 0.5f * ((float) m_restart_tab[i].dir + 2), 0.0f);
					pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, (int) restart_name);
					pNode->AddComponent(crc_type, ESYMBOLTYPE_NAME, (int) type_checksum);
					pNode->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int) crc_createdatstart);
					if (m_restart_tab[i].type != vKING_OF_HILL)
					{
						char *p_name = "Multi Restart";
						if (n == 0)
						{
							switch (m_restart_tab[i].type)
							{
								case vONE_PLAYER:
									p_name = "Player 1 Restart";
									break;
								case vMULTIPLAYER:
									p_name = "Player 2 Restart";
									break;
								case vHORSE:
									p_name = "Horse Restart";
									break;
								case vRED_FLAG:
								case vGREEN_FLAG:
								case vBLUE_FLAG:
								case vYELLOW_FLAG:
									p_name = "Team: CTF";
									break;
								default:
									break;
									 
							}
						}						
						pNode->AddComponent(crc_restartname, ESYMBOLTYPE_STRING, p_name);
					
						Script::CArray *pRestartTypes = new Script::CArray();
						pRestartTypes->SetSizeAndType(1, ESYMBOLTYPE_NAME);
						pRestartTypes->SetChecksum(0, type_checksum);
						pNode->AddComponent(crc_restart_types, pRestartTypes);
						delete pRestartTypes;
					}
					//Script::PrintContents(pNode);								   
					(*pNodeNum)++;
				}
				
				// Flags have the 3 additional nodes for the team flags, bases, CTF flag
				
				if (
					m_restart_tab[i].type == vRED_FLAG
					|| m_restart_tab[i].type == vGREEN_FLAG
					|| m_restart_tab[i].type == vBLUE_FLAG
					|| m_restart_tab[i].type == vYELLOW_FLAG
					)
				{
					// Not a normal restart point, so must be a flag
					// so need to generate the flag, the CTF base and the CTF restart all in the same place

				/*					
				// team flag is like:
				{
					// Node 2678
					Position = (864.618835,-408.157806,-3771.989014)
					Angles = (0.000000,2.268928,0.000000)
					Name = TRG_Flag_Red
					Class = GameObject
					Type = Flag_Red
					Model = "gameobjects\flags\flag_red\flag_red.mdl"
					SuspendDistance = 0
					lod_dist1 = 800
					lod_dist2 = 801
					TriggerScript = TRG_Flag_RedScript
				}
				
				then pretty much the same for CTF flags, just a different name
				
				{
					// Node 3360
					Position = (4060.947998,408.885132,1926.812012)
					Angles = (0.000000,0.000000,0.000000)
					Name = TRG_CTF_Blue						<<<<<<<<<<<<<<   Different name
					Class = GameObject
					Type = Flag_Blue
					Model = "gameobjects\flags\flag_blue\flag_blue.mdl"
					SuspendDistance = 0
					lod_dist1 = 800
					lod_dist2 = 801
					TriggerScript = TRG_CTF_BlueScript		<<<<<<<<<<<<<<  script can actually stay the same
				}
				
				and then the base
				{
					// Node 3252
					Position = (-2448.872314,-373.527405,5749.742188)
					Angles = (0.000000,-2.792527,0.000000)
					Name = TRG_CTF_Yellow_Base		 <<<<<<<<<<<<<<<<<<<<<<<<<<<<  different name
					Class = GameObject
					Type = Flag_Yellow_Base			   <<<<<<<<<<<<<<<<<<<<<<<<<<<  different type
					Model = "gameobjects\flags\flag_yellow_base\flag_yellow_base.mdl"   <<<<<< differnt model
					SuspendDistance = 0
					lod_dist1 = 800
					lod_dist2 = 801
													<<<<<<<<<<<<<<<  no script
				}


				
				*/	
				
	
					Script::CScriptStructure *pNode = pNodeArray->GetStructure(*pNodeNum);

					const	char *color = NULL;					
					if (m_restart_tab[i].type == vRED_FLAG) color = "red";
					if (m_restart_tab[i].type == vGREEN_FLAG) color = "green";
					if (m_restart_tab[i].type == vBLUE_FLAG) color = "blue";
					if (m_restart_tab[i].type == vYELLOW_FLAG) color = "yellow";

					char name_string[256];
					char type_string[256];
					char model_string[256];
					char triggerscript_string[256];
					sprintf(type_string,"Flag_%s",color);
					sprintf(model_string,"gameobjects\\flags\\flag_%s\\flag_%s.mdl",color,color);
					sprintf(triggerscript_string,"TRG_Flag_%sScript_Parked",color);
					
					sprintf(name_string,"TRG_Flag_%s",color);
					pNode->AddComponent(CRCD(0x7f261953,"pos"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), m_restart_tab[i].pos.GetZ());
//					pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), -m_restart_tab[i].pos.GetZ());
					pNode->AddComponent(crc_angles, 0.0f, Mth::PI * 0.5f * ((float) m_restart_tab[i].dir + 2), 0.0f);
					pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, Script::GenerateCRC(name_string));
					pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, Script::GenerateCRC("GameObject"));
					pNode->AddComponent(crc_type, ESYMBOLTYPE_NAME, Script::GenerateCRC(type_string));
					pNode->AddComponent(crc_model, ESYMBOLTYPE_STRING, model_string);
					pNode->AddComponent(crc_triggerscript, ESYMBOLTYPE_NAME, Script::GenerateCRC(triggerscript_string));

					pNode->AddInteger("nodeIndex", *pNodeNum);
					pNode->AddInteger("SuspendDistance", 0);
					pNode->AddInteger("lod_dist1", 800);
					pNode->AddInteger("lod_dist2", 801);
//					Script::PrintContents(pNode);								   
					(*pNodeNum)++;				
					
					// then repeat exactly for the CTF node, with different name
					
					pNode = pNodeArray->GetStructure(*pNodeNum);
					sprintf(name_string,"TRG_CTF_%s",color);
					pNode->AddComponent(CRCD(0x7f261953,"pos"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), m_restart_tab[i].pos.GetZ());
//					pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), -m_restart_tab[i].pos.GetZ());
					pNode->AddComponent(crc_angles, 0.0f, Mth::PI * 0.5f * ((float) m_restart_tab[i].dir + 2), 0.0f);
					pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, Script::GenerateCRC(name_string));
					pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, Script::GenerateCRC("GameObject"));
					pNode->AddComponent(crc_type, ESYMBOLTYPE_NAME, Script::GenerateCRC(type_string));
					pNode->AddComponent(crc_model, ESYMBOLTYPE_STRING, model_string);
					pNode->AddComponent(crc_triggerscript, ESYMBOLTYPE_NAME, Script::GenerateCRC(triggerscript_string));

					pNode->AddInteger("nodeIndex", *pNodeNum);
					pNode->AddInteger("SuspendDistance", 0);
					pNode->AddInteger("lod_dist1", 800);
					pNode->AddInteger("lod_dist2", 801);
//					Script::PrintContents(pNode);								   
					(*pNodeNum)++;				

					// different for base (note, no triggerscript)
					pNode = pNodeArray->GetStructure(*pNodeNum);
					sprintf(name_string,"TRG_CTF_%s_Base",color);
					sprintf(type_string,"Flag_%s_Base",color);
					sprintf(model_string,"gameobjects\\flags\\flag_%s_base\\flag_%s_base.mdl",color,color);
					
					pNode->AddComponent(CRCD(0x7f261953,"pos"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), m_restart_tab[i].pos.GetZ());
//					pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), -m_restart_tab[i].pos.GetZ());
					pNode->AddComponent(crc_angles, 0.0f, Mth::PI * 0.5f * ((float) m_restart_tab[i].dir + 2), 0.0f);
					pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, Script::GenerateCRC(name_string));
					pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, Script::GenerateCRC("GameObject"));
					pNode->AddComponent(crc_type, ESYMBOLTYPE_NAME, Script::GenerateCRC(type_string));
					pNode->AddComponent(crc_model, ESYMBOLTYPE_STRING, model_string);

					pNode->AddInteger("nodeIndex", *pNodeNum);
					pNode->AddInteger("SuspendDistance", 0);
					pNode->AddInteger("lod_dist1", 800);
					pNode->AddInteger("lod_dist2", 801);
//					Script::PrintContents(pNode);								   
					(*pNodeNum)++;				
				}
			} // end for n
		} // end if
	}

/*	
	Position = (2456.617188,-218.955109,-6663.688477)
	Angles = (0.741766,-1.570794,-0.000002)
	Name = TRG_Restart_Parking_Lot04
	Class = Restart
	Type = Player1
	CreatedAtStart
	RestartName = "P1: Restart"
	restart_types =
	[
	Player1
	]
*/
}


////////////////////////////////////////////////////////////////////////////
// Restart stuff

/* 
	Should be called from 
	-CParkManager::RebuildNodeArray() (was in Map::GenerateWorld())
	-AddMetaPieceToPark::AddMetaPieceToPark() (was in Map::AddPiece())
*/
// Add a restart point if space
// Non-automatic restart points will overwrite automatic restart points
// (but not if they find an empty slot first)
bool CParkGenerator::AddRestart(Mth::Vector &pos, int dir, GridDims &dims, RestartType type, bool automatic, bool auto_copy)
{
	int first_slot = 0, last_slot = 0;
	get_restart_slots(type, &first_slot, &last_slot);

	Mth::Vector	use_pos = pos;
	GridDims use_dims = dims;

	// When "auto_copy" is set
	// If this is an automatic point, then copy the position
	// from the last non-automatic point we find
	if (automatic && auto_copy)
	{
		for (int i = first_slot; i <= last_slot; i++)
		{
			if (m_restart_tab[i].type == type && !m_restart_tab[i].automatic)
			{
				use_pos = m_restart_tab[i].pos;				
				use_dims = m_restart_tab[i].dims;				
				#ifdef	DEBUG_RESTARTS
				printf ("%d: restart copying position from slot %d",__LINE__,i);
				#endif
			}
		}
	}
	
	
	
	for (int i = first_slot; i <= last_slot; i++)
	{
		
		// add the restart to any empty slot, or overwrite an "automatic" slot if this is not automatic 
		if (m_restart_tab[i].type == vEMPTY || (m_restart_tab[i].automatic && !automatic))		
		{
			m_restart_tab[i].pos = use_pos;
			m_restart_tab[i].dir = dir;
			m_restart_tab[i].dims = use_dims;
			m_restart_tab[i].type = type;
			m_restart_tab[i].automatic = automatic;
			#ifdef	DEBUG_RESTARTS
			printf ("%d: restart (%.2f,%.2f,%.2f) set in slot %d  (type = %d, auto = %d\n)",__LINE__,use_pos[X],use_pos[Y],use_pos[Z],i,type,automatic);
			#endif
			return true;
		}		
	}
	
	return false;
}


int CParkGenerator::NumRestartsOfType(RestartType type)
{
	int first_slot = 0, last_slot = 0;
	get_restart_slots(type, &first_slot, &last_slot);

	int got = 0;									// count of number of actual restarts
	for (int i = first_slot; i <= last_slot; i++)
	{
		if (m_restart_tab[i].type != vEMPTY)		// got some kind of restart
		{
				got++; 								// counting an actual restart
		}
	}
	return got;
}


// should be called from CParkManager::RebuildNodeArray() (was in Map::GenerateWorld())
// returns true if there are not enough restart points (ie, true if we need more)
// Note:  we include "automatic" restart points now.
bool CParkGenerator::NotEnoughRestartsOfType(RestartType type, int need)
{
	return NumRestartsOfType(type) < need;		  // if got less than we need, return true, (we need more)
}


Mth::Vector	 CParkGenerator::GetRestartPos(RestartType type, int index)
{

	int first_slot = 0, last_slot = 0;
	get_restart_slots(type, &first_slot, &last_slot);

	for (int i = first_slot; i <= last_slot; i++)
	{
		if (m_restart_tab[i].type != vEMPTY)		// got some kind of restart
		{
			index--;								
			if (index < 0)
			{
				return m_restart_tab[i].pos;
			}
		}
	}
		
	Dbg_MsgAssert(0,("could not find restart pos"));	
	return Mth::Vector(0,0,0);

}

GridDims	 CParkGenerator::GetRestartDims(RestartType type, int index)
{

	int first_slot = 0, last_slot = 0;
	get_restart_slots(type, &first_slot, &last_slot);

	for (int i = first_slot; i <= last_slot; i++)
	{
		if (m_restart_tab[i].type != vEMPTY)		// got some kind of restart
		{
			index--;								
			if (index < 0)
			{
				return m_restart_tab[i].dims;
			}
		}
	}
		
	Dbg_MsgAssert(0,("could not find restart dims"));	
	return	GridDims();

}



// called by various restart functions in this class
void CParkGenerator::get_restart_slots(RestartType type, int *pFirstSlot, int *pLastSlot)
{
	switch (type)
	{
		case vONE_PLAYER:
			*pFirstSlot = 1;
			*pLastSlot = 1;
			break;
		case vMULTIPLAYER:
			*pFirstSlot = 0;
			*pLastSlot = 0;
			break;
		case vHORSE:
			*pFirstSlot = 2;
			*pLastSlot = 7;
			break;
		case vKING_OF_HILL:
			*pFirstSlot = 8;
			*pLastSlot = 13;
			break;
		case vRED_FLAG:	
			*pFirstSlot = 14;
			*pLastSlot = 14;
			break;
		case vGREEN_FLAG:	
			*pFirstSlot = 15;
			*pLastSlot = 15;
			break;
		case vBLUE_FLAG:	
			*pFirstSlot = 16;
			*pLastSlot = 16;
			break;
		case vYELLOW_FLAG:	
			*pFirstSlot = 17;
			*pLastSlot = 17;
			break;
		default:
			Dbg_MsgAssert(0,("Unhandled restart type %d",type));
			break;
	}
}




// should be called by CParkManager::DestroyConcreteMeta() (was Map::RemovePiece())
void CParkGenerator::RemoveRestart(const GridDims &dims, RestartType type)
{
	int first_slot = 0, last_slot = 0;
	get_restart_slots(type, &first_slot, &last_slot);
	
	for (int i = first_slot; i <= last_slot; i++)
	{
//		if (m_restart_tab[i].area.x == area.x &&
//			m_restart_tab[i].area.y == area.y &&
//			m_restart_tab[i].area.z == area.z)
		if (
			m_restart_tab[i].dims.GetX() == dims.GetX()
			&& m_restart_tab[i].dims.GetY() == dims.GetY()
			&& m_restart_tab[i].dims.GetZ() == dims.GetZ()
			)
		{
			m_restart_tab[i].type = vEMPTY;
		}
	}
}




// should be called from CCursor::AttemptStamp() (was Cursor::AttemptPlacePiece())
bool CParkGenerator::FreeRestartExists(RestartType type)
{
	int first_slot = 0, last_slot = 0;
	get_restart_slots(type, &first_slot, &last_slot);
	
	for (int i = first_slot; i <= last_slot; i++)
		if (m_restart_tab[i].type == vEMPTY || m_restart_tab[i].automatic)
		{
//			m_restart_tab[i].type = vEMPTY;
			return true;
		}
	return false;
}




/* 
	should be called from:
	-CParkGenerator::InitializeMasterPieces() (was World::CreateParkHeap())
	-CParkGenerator::DestroyAllPieces() or CParkManager::destroy_concrete_metapieces() (was World::DestroyAllPieces())
*/
void CParkGenerator::KillRestarts()
{
	#ifdef	DEBUG_RESTARTS
	printf ("%d: KillRestarts called, all restarts set to vEMPTY",__LINE__);
	#endif

	for (int i = 0; i < vNUM_RESTARTS; i++)
		m_restart_tab[i].type = vEMPTY;
}


void CParkGenerator::ClearAutomaticRestarts()
{

	#ifdef	DEBUG_RESTARTS
	printf ("%d: ClearAutomaticRestarts called, all automatic restarts set to vEMPTY",__LINE__);
	#endif
	
	for (int i = 0; i < vNUM_RESTARTS; i++)
	{
		if (m_restart_tab[i].automatic)
		{
			m_restart_tab[i].type = vEMPTY;
		}
	}
}




/*
	Left edge of gap is at position pos. Bottom edge sticks out in direction
	indicated by rot, for length units.
*/
CClonedPiece *CParkGenerator::CreateGapPiece(Mth::Vector &pos, float length, int rot)
{
	// maken der gapen piece
	CClonedPiece *p_gap_piece = ClonePiece(GetMasterPiece(Script::GenerateCRC("Sk4Ed_Gap_10x10"), true), CPiece::mNO_FLAGS);
	p_gap_piece->SetDesiredPos(pos, CClonedPiece::CHANGE_SECTOR);
	p_gap_piece->SetDesiredRot(Mth::ERot90(rot), CClonedPiece::CHANGE_SECTOR);
	p_gap_piece->set_flag(CPiece::mGAP);
	AddClonedPieceToWorld(p_gap_piece);
	// XXX
	Ryan("@@@ made gap piece 0x%x\n", p_gap_piece);
	return p_gap_piece;
}




}




================================================
FILE: Code/Sk/ParkEditor2/ParkGen.h
================================================
#ifndef __SK_PARKEDITOR2_PARKGEN_H
#define __SK_PARKEDITOR2_PARKGEN_H

#include 
#include 

#define DEBUG_THIS_DAMN_THING 0

#if defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ )
	inline void ParkEd(const char* A ...) {};
#else

	#if DEBUG_THIS_DAMN_THING
		#define ParkEd(A...)  printf("PARKED: "); printf(##A); printf("\n") 		
	#else
		#define ParkEd(A...)
	#endif
#endif

namespace Image
{
	struct RGBA;
}

namespace Nx
{
	class CScene;
	class CSector;
}

namespace Script
{
	class	CArray;
}

namespace Ed
{

	class CSourcePiece;
	class CClonedPiece;
	class CMapListNode;

struct MapArea		 	// Mick - OLD
{
	int x, y, z;
	int w, h, l;
};


enum
{
	H = 4,
	L = 5
};




/*
	Describes a 3D box in cell coordinates. Sometimes only the position part or only the area part is used.
*/
class GridDims
{
public:
								GridDims(uint8 x = 0, sint8 y = 0, uint8 z = 0, uint8 w = 0, uint8 h = 0, uint8 l = 0);

	void						SetXYZ(uint8 x, sint8 y, uint8 z) {m_dims[0] = x; m_dims[1] = (uint8) y; m_dims[2] = z;}
	void						SetWHL(uint8 w, uint8 h, uint8 l) {m_dims[3] = w; m_dims[4] = h; m_dims[5] = l;}

	uint8 &						operator[](sint i);
	
	uint8						GetX() const {return m_dims[0];}
	sint8						GetY() const {return (sint8) m_dims[1];}
	uint8						GetZ() const {return m_dims[2];}
	uint8						GetW() const {return m_dims[3];}
	uint8						GetH() const {return m_dims[4];}
	uint8						GetL() const {return m_dims[5];}

	void						MakeInfinitelyHigh();
	void						MakeInfiniteOnY();

	void						PrintContents() const;

private:

	uint8						m_dims[6];
};


/*
	Roles of CPiece:
	-wraps object placed in park
	-wraps fluid object contained in world (cursor)
	
	Class hierarchy:
	-CPiece
		-CSourcePiece (dims)
		-CClonedPiece (pos, rot, pointer to master)
		
	Rotations:				  
	-Hard: underlying verts are flipped around directly, stored dims changed to match
	-Soft: matrix sent down directly, not stored with piece
	
	Relationship with metapiece:
	-pieces contained by metapiece must be of the same type, all hard pieces or all soft pieces
*/
class CPiece
{
	friend class CParkGenerator;
	friend class CMetaPiece;
	friend class CConcreteMetaPiece;

public:

	enum EFlags
	{
		mNO_FLAGS =					0,

		// only one of following can be set
		mSOURCE_PIECE =				(1<<0),
		mCLONED_PIECE =				(1<<1),
		
		// Applied to a cloned piece. Set if an animated piece, like part of cursor
		mSOFT_PIECE =				(1<<2),
		
		// set if piece is being displayed
		mIN_WORLD =					(1<<3),

		mSHELL = 					(1<<4),
		mFLOOR = 					(1<<5),
		mGAP = 						(1<<6),
		mRESTART =					(1<<7),
		mNO_RAILS =					(1<<8),
	};

	EFlags							GetFlags() {return m_flags;}
	virtual Mth::Vector				GetDims() = 0;
	void							GetCellDims(GridDims *pDims);

	CSourcePiece *					CastToCSourcePiece();
	CClonedPiece *					CastToCClonedPiece();

	Mth::Vector						GetDimsWithRot(Mth::ERot90 rot);

protected:

	/*
		Hard rotation: flipping of verts
		
		Soft: rotation of underlying piece, matrix is set for CSector directly.
	*/									
	
									CPiece();
	virtual							~CPiece();

	void							set_flag(EFlags flag) {m_flags = EFlags(m_flags | flag);}
	void							clear_flag(EFlags flag) {m_flags = EFlags(m_flags & ~flag);}

	union
	{
		uint32						m_sector_checksum;		// probably will refer to CSectors this way
		// 							mp_other_thing // for soft pieces
	};

	EFlags							m_flags;

	CPiece *						mp_next_in_list;
	//CPiece *						mp_next_in_metapiece;
};




class CSourcePiece : public CPiece
{
	friend class CParkGenerator;
	friend class CMetaPiece;
	friend class CClonedPiece;

public:
	/*
		pieces in different domains can coexist at same GRID coordinates
	*/
	enum EDomain
	{
		mREGULAR				= (1<<0),
		mOFFSET_RAIL			= (1<<1),
		mGAP					= (1<<2),
	};

	uint32							GetType();
	Mth::Vector						GetDims() {return m_dims;}

protected:
									CSourcePiece();
									~CSourcePiece();

	Mth::Vector						m_dims;
	EDomain							m_domain;	
	uint32							m_triggerScriptId;  // Checksum of trigger script associated with this node (for stuff like chainlink fence SFX)

	int								m_num_rail_points;
	int								m_num_linked_rail_points;
};




class CClonedPiece : public CPiece
{
	friend class CParkGenerator;
	friend class CMetaPiece;
	friend class CConcreteMetaPiece;

public:
	uint32							GetID();
	
	enum ESectorEffect
	{
		MARKER_ONLY,
		CHANGE_SECTOR,
	};
	void							SetDesiredPos(const Mth::Vector& pos, ESectorEffect sectorEffect);
	void							SetDesiredRot(Mth::ERot90 rot, ESectorEffect sectorEffect);
	void							SetSoftRot(float rot); // in degrees
	void							SetScaleX(float scaleX);
	void							Highlight(bool on, bool makePurple = false);
	void							SetActive(bool active);
	void							SetVisibility(bool visible);
	CSourcePiece *					GetSourcePiece() {return mp_source_piece;}
	Mth::Vector						GetPos() {return m_pos;}
	Mth::ERot90						GetRot() {return m_rot;}
	Mth::Vector						GetDims();
	uint32							GetType() {return mp_source_piece->GetType();}

protected:
									CClonedPiece();

	uint32							m_id;
	CSourcePiece *					mp_source_piece;

	Mth::Vector						m_pos; // center, bottom
	Mth::ERot90						m_rot; // in units of 90 degrees, CCW
};




class CParkGenerator
{

public:

	static const float 			CELL_WIDTH;
	static const float 			CELL_HEIGHT;
	static const float 			CELL_LENGTH;


	static const uint32				vFIRST_ID_FOR_OBJECTS;
	static const uint32				vMAX_ID_FOR_OBJECTS;
	static const uint32				vFIRST_ID_FOR_RAILS;
	static const uint32				vMAX_ID_FOR_RAILS;
	static const uint32				vFIRST_ID_FOR_CREATED_RAILS;
	static const uint32				vMAX_ID_FOR_CREATED_RAILS;
	static const uint32				vFIRST_ID_FOR_RESTARTS;

	
	enum EDestroyType
	{
		DESTROY_PIECES_AND_SECTORS,
		DESTROY_ONLY_PIECES,
	};

	enum RestartType
	{
		vEMPTY,
		vONE_PLAYER,
		vMULTIPLAYER,
		vHORSE,
		vKING_OF_HILL,
		vRED_FLAG,
		vGREEN_FLAG,
		vBLUE_FLAG,
		vYELLOW_FLAG,
		
		
		vNUM_RESTART_TYPES,
	};


									CParkGenerator();
									~CParkGenerator();

	int								GetResourceSize(char *name);
	Mem::Heap *						GetParkEditorHeap();
	
	struct MemUsageInfo
	{
		int							mParkHeapUsed;
		int							mParkHeapFree; // after padding is deducted
		int							mMainHeapUsed;
		int							mMainHeapFree; // after padding is deducted
		
		int							mLastMainUsed;
		bool						mIsFragmented;

		int							mTotalRailPoints;
		int							mTotalLinkedRailPoints;
		int							mTotalClonedPieces;
	};
	MemUsageInfo					GetResourceUsageInfo(bool printInfo = false);
	void							SetMaxPlayers(int maxPlayers);
	int 							GetMaxPlayers() {return m_max_players;}
	int 							GetMaxPlayersPossible();
		
	CSourcePiece *					GetMasterPiece(uint32 pieceType, bool assert = false);
	CSourcePiece *					GetNextMasterPiece(CSourcePiece *pLast);
	CClonedPiece *					ClonePiece(CPiece *pMasterPiece, CPiece::EFlags flags);
	void							AddClonedPieceToWorld(CClonedPiece *pPiece);
	void							DestroyClonedPiece(CClonedPiece *pPiece);


	void							InitializeMasterPieces(int parkW, int parkH, int parkL, int theme);
	
	// K: Added to allow cleanup of the park editor heap during play
	//void							DeleteSourceAndClonedPieces();
	//void							DeleteParkEditorHeap();
	
	void							UnloadMasterPieces();
	
	void							PostGenerate();
	void							GenerateCollisionInfo(bool assert = true);
	void 							RemoveOuterShellPieces(int theme);
	void							GenerateNodeInfo(CMapListNode * p_concrete_metapiece_list);
	void 							ReadInRailInfo();
	void							DestroyRailInfo();

	void							HighlightAllPieces(bool highlight);
	void							SetGapPiecesVisible(bool visible);
	void							SetRestartPiecesVisible(bool visible);


	void 							SetLightProps(int num_lights, 
												  float amb_const_r, float amb_const_g, float amb_const_b, 
												  float falloff_const_r, float falloff_const_g, float falloff_const_b,
												  float cursor_ambience);
	void 							SetLight(Mth::Vector &light_pos, int light_num);
	void 							CalculateLighting(CClonedPiece *p_piece);
	void 							CalculateVertexLighting(const Mth::Vector & vert, Image::RGBA & color);
	
	
	void							DestroyAllClonedPieces(EDestroyType type);


	int								NumRestartsOfType(RestartType type);
	bool							NotEnoughRestartsOfType(RestartType type, int need);
	Mth::Vector	 					GetRestartPos(RestartType type, int index);
	GridDims	 					GetRestartDims(RestartType type, int index);
	bool							AddRestart(Mth::Vector &pos, int dir, GridDims &dims, RestartType type, bool automatic, bool auto_copy=false);
	void							RemoveRestart(const GridDims &dims, RestartType type);
	bool 							FreeRestartExists(RestartType type);
	void							KillRestarts();	
	void							ClearAutomaticRestarts();

	
	CClonedPiece *					CreateGapPiece(Mth::Vector &pos, float length, int rot);

	Nx::CScene *					GetClonedScene() {return mp_cloned_scene;}

	void							CleanUpOutRailSet();
	void							GenerateOutRailSet(CMapListNode * p_concrete_metapiece_list);
	bool							FindNearestRailPoint(Mth::Vector &pos, Mth::Vector *p_nearest_pos, float *p_dist_squared);
	
private:

	enum EFlags
	{
		mMASTER_STUFF_LOADED		= (1<<1),
		mSECTORS_GENERATED			= (1<<2),
		mPIECES_IN_WORLD			= (1<<3),
		mCOLLISION_INFO_GENERATED	= (1<<4),
		mNODE_INFO_GENERATED		= (1<<5),
		// set when we destroy a piece
		mSECTOR_MEMORY_NEEDS_FREE	= (1<<6),
	};

	void 							destroy_piece_impl(CClonedPiece *pPiece, EDestroyType destroyType);
	bool							flag_on(EFlags flag) {return ((m_flags & flag) != 0);}
	void							set_flag(EFlags flag) {m_flags = EFlags(m_flags | flag);}
	void							clear_flag(EFlags flag) {m_flags = EFlags(m_flags & ~flag);}
	
	uint32 							scan_for_cluster(Script::CArray *pNodeArray, int &index);
	int 							scan_for_rail_node(Script::CArray *pNodeArray, uint32 cluster, int link, Mth::Vector *pVector, bool *pHasLinks, RailPoint::RailType *pType);
	void							scan_in_trigger_info(Script::CArray *pNodeArray);
	void 							set_up_object_nodes(Script::CArray *pNodeArray, int *pNodeNum);
	void							set_up_rail_nodes(Script::CArray *pNodeArray, int *pNodeNum);
	void							set_up_restart_nodes(Script::CArray *pNodeArray, int *pNodeNum);

	
	/*
	Things
	
		-loading master geometry, loading master rails (one operation)
		-generating world from compressed map
		-collision setup
		-rail setup
		-cleanup world (maybe just destroys pieces)
	*/
	
	EFlags							m_flags;

	CPiece *						mp_cloned_piece_list;
	CPiece *						mp_source_piece_list;
	int								m_num_cloned_pieces;
	int								m_num_source_pieces;

	Nx::CScene *					mp_source_scene;				// Scene used as a manager of pieces
	Nx::CScene *					mp_cloned_scene;

	uint32							m_next_id;
	
	Mem::Region*					mp_mem_region;
	Mem::Heap*						mp_mem_heap;

	int								m_max_players;
		
//////////////////////////////////////////////////////////////////////////
// rail and node stuff patched in by Mick

	RailSet							m_in_rail_set;
	RailSet							m_out_rail_set;
	// in world, at editing time
	int								m_total_rail_points;
	int								m_total_rail_linked_points;

	struct TempNodeInfo
	{
		uint32						classCrc;
		uint32						cluster;
		Script::CArray *			pLinkArray;
	};

	TempNodeInfo *					m_temp_node_tab;
	int	*							m_processed_node_tab;
	int								m_processed_node_tab_entries;

	/*
		Restart stuff
	*/



	void 				get_restart_slots(RestartType type, int *pFirstSlot, int *pLastSlot);


	struct RestartInfo
	{
		Mth::Vector					pos;
		int							dir;
		GridDims					dims;
		RestartType					type;
		bool						automatic;
	};
	static const int				vNUM_RESTARTS = 18;
	
	RestartInfo						m_restart_tab[vNUM_RESTARTS];

	/*
		Light info
	*/
	
	struct LightIntensity
	{
		float r;
		float g;
		float b;
	};
	
	static const int				vMAX_LIGHTS = 16;
	
	int								m_numLights;
	LightIntensity					m_ambientLightIntensity;
	LightIntensity					m_falloffLightIntensity;
	Mth::Vector						m_lightTab[vMAX_LIGHTS];
	float							m_cursorAmbience;

	
	MemUsageInfo					m_mem_usage_info;
};




} // end namespace


#endif


================================================
FILE: Code/Sk/ParkEditor2/clipboard.cpp
================================================
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 

DefinePoolableClass(Ed::CClipboardEntry)
DefinePoolableClass(Ed::CClipboard)

namespace Ed
{

static void s_rotate_map_vector(int *p_x, int *p_z, Mth::ERot90 rotation)
{
	int in_x=*p_x;
	int in_z=*p_z;
	switch (rotation)
	{
		case Mth::ROT_0:
			break;
		case Mth::ROT_90:
			*p_x=in_z;
			*p_z=-in_x;
			break;
		case Mth::ROT_180:
			*p_x=-in_x;
			*p_z=-in_z;
			break;
		case Mth::ROT_270:
			*p_x=-in_z;
			*p_z=in_x;
			break;
		default:
			Dbg_MsgAssert(0, ("Wacky rotation"));
			break;
	}		
}

CClipboardEntry::CClipboardEntry()
{
	mpMeta=NULL;
	mpConcreteMeta=NULL;
	mX=mY=mZ=0;
	mWidth=0;
	mLength=0;
	m_rot=Mth::ROT_0;
	mpNext=NULL;
}

CClipboardEntry::~CClipboardEntry()
{
	DestroyMeta();
}

void CClipboardEntry::DestroyMeta()
{
	if (mpConcreteMeta)
	{
		mpConcreteMeta->MarkUnlocked();
		CParkManager::sInstance()->DestroyConcreteMeta(mpConcreteMeta, CParkManager::mDONT_DESTROY_PIECES_ABOVE);
		mpConcreteMeta=NULL;
	}
}

// This function takes the vector from the centre to the piece, rotates it according to 
// the cursor's current rotation, then adds that vector to the cursor's position and places the result in
// p_mapCoords
void CClipboardEntry::CalculateMapCoords(GridDims *p_mapCoords, uint8 centre_x, uint8 centre_z, CCursor *p_cursor)
{
	GridDims meta_area;
	if (mpMeta)
	{
		meta_area=mpMeta->GetArea();
	}
	else
	{
		// If there is no meta then the clipboard entry is defining a height for one cell, so
		// just set the w and l to 1.
		meta_area.SetWHL(1,1,1);
	}
		
	Mth::ERot90 total_rotation=Mth::ERot90((p_cursor->GetRotation()+m_rot)&3);
	switch ( total_rotation )
	{
		case Mth::ROT_0:
		case Mth::ROT_180:
			p_mapCoords->SetWHL(meta_area.GetW(),1,meta_area.GetL());
			break;
		case Mth::ROT_90:
		case Mth::ROT_270:
			p_mapCoords->SetWHL(meta_area.GetL(),1,meta_area.GetW());
			break;
		default:
			Dbg_MsgAssert(0, ("Wacky rotation"));
			break;
	}		
	
	// Get the vector from the centre of the selection area to the piece.
	int vector_x=mX-centre_x;
	int vector_z=mZ-centre_z;
	
	// Rotate the vector according to the cursor's current rotation
	s_rotate_map_vector(&vector_x, &vector_z, p_cursor->GetRotation());

	// A frig is needed after rotation because the piece always appears to be
	// displayed with the coords referring to the top-left corner, even when the
	// piece is rotated.
	switch ( p_cursor->GetRotation() )
	{
		case Mth::ROT_0:
			break;
		case Mth::ROT_90:
			vector_z-=mWidth-1;
			break;
		case Mth::ROT_180:
			vector_x-=mWidth-1;
			vector_z-=mLength-1;
			break;	
		case Mth::ROT_270:
			vector_x-=mLength-1;
			break;
		default:
			Dbg_MsgAssert(0, ("Wacky rotation"));
			break;
	}		

	// Get the new map coords of the meta such that it is positioned relative to the cursor.
	GridDims cursor_position=p_cursor->GetPosition();
	p_mapCoords->SetXYZ(cursor_position.GetX()+vector_x, mY, cursor_position.GetZ()+vector_z);
}

bool CClipboardEntry::CanPaste(uint8 centre_x, uint8 centre_z, CCursor *p_cursor)
{
	GridDims map_coords;
	CalculateMapCoords(&map_coords,centre_x,centre_z,p_cursor);
	// Make sure the y is set to zero because the GetMetaPiecesAt function that gets called in
	// CanPlacePiecesIn will think there is something in the cell if the y is -1
	map_coords.SetXYZ(map_coords.GetX(),0,map_coords.GetZ());
	
	CParkManager *p_manager=CParkManager::sInstance();
	return p_manager->CanPlacePiecesIn(map_coords, true); // The true means ignore floor height problems
}

// centre_x,centre_z are the map coords of the centre of the original selection area.
// The coords of the entry (mX,mZ) are also in map coords.
void CClipboardEntry::Paste(uint8 centre_x, int raiseAmount, uint8 centre_z, CCursor *p_cursor)
{
	if (mpMeta && mpMeta->IsRiser())
	{
		return;
	}

	GridDims map_coords;
	CalculateMapCoords(&map_coords,centre_x,centre_z,p_cursor);
	
	int new_y=mY+raiseAmount;
	if (new_y >= CParkManager::MAX_HEIGHT)
	{
		new_y=CParkManager::MAX_HEIGHT-1;
	}

	map_coords.SetXYZ(map_coords.GetX(),new_y,map_coords.GetZ());
		
	CParkManager *p_manager=CParkManager::sInstance();
	
	// Adjust the floor height to match that of the piece
	int count=0;	
	while (true)
	{
		bool uniform_height=false;
		int current_height = p_manager->GetFloorHeight(map_coords, &uniform_height);
		
		if (current_height > new_y)
		{
			break;
		}	
		
		if (count>40)
		{
			Dbg_MsgAssert(0,("Too many recursions of ChangeFloorHeight in CClipboardEntry::Paste"));
			break;
		}	
			
		if (uniform_height && current_height==new_y)
		{
			break;
		}
		
		p_manager->ChangeFloorHeight(map_coords,1); // 1 is the step amount, 1 upwards
		++count;
	}		

	if (mpMeta)
	{
		// Place the piece	
		CConcreteMetaPiece *p_concrete = p_manager->CreateConcreteMeta(mpMeta);
		p_concrete->SetRot(Mth::ERot90((m_rot+p_cursor->GetRotation())&3));
		p_manager->AddMetaPieceToPark(map_coords, p_concrete);
	}	
}

int CClipboardEntry::GetFloorHeight(uint8 centre_x, uint8 centre_z, CCursor *p_cursor)
{
	GridDims map_coords;
	CalculateMapCoords(&map_coords,centre_x,centre_z,p_cursor);
	
	bool uniform_height;
	return CParkManager::sInstance()->GetFloorHeight(map_coords, &uniform_height);
}

void CClipboardEntry::HighlightIntersectingMetas(uint8 centre_x, uint8 centre_z, CCursor *p_cursor, bool on)
{
	GridDims map_coords;
	CalculateMapCoords(&map_coords,centre_x,centre_z,p_cursor);
	// Make sure the y is set to zero because the GetMetaPiecesAt function that gets called in
	// CanPlacePiecesIn will think there is something in the cell if the y is -1
	map_coords.SetXYZ(map_coords.GetX(),0,map_coords.GetZ());
	
	CParkManager *p_manager=CParkManager::sInstance();
	
	CMapListTemp metas_at_pos = p_manager->GetAreaMetaPieces(map_coords);
	if (metas_at_pos.IsEmpty())
	{
		return;
	}
	
	// Iterate over the list of metapieces we just found, and highlight each one
	CMapListNode *p_entry = metas_at_pos.GetList();
	while(p_entry)
	{
		CConcreteMetaPiece *p_meta=p_entry->GetConcreteMeta();
		if (!p_meta->IsRiser())
		{
			p_meta->Highlight(on);		
		}
			
		p_entry = p_entry->GetNext();
	}
}

// This gets called when the CCursor is in paste mode, and needs to display the clipboard contents at
// the cursor position.
// pos and rot are the world position and rotation in degrees of the cursor.
// They may not be cell aligned, and the rotation may not be a multiple of 90 degrees.
// This function will position the meta such that the centre of the clipboard selection is at pos.
void CClipboardEntry::ShowMeta(uint8 centre_x, int raiseAmount, uint8 centre_z, Mth::Vector pos, float rot)
{
	if (!mpMeta)
	{
		// No meta to display, meaning the clipboard entry is defining a cell height
		return;
	}

	// Get the vector (in cell units) from the centre of the selection area to the piece.
	float vector_x=mX-centre_x;
	float vector_z=mZ-centre_z;

	// Calculate the vector, in world coords, from the centre of the selection to the centre of the piece.	
	Mth::Vector p(0.0f,0.0f,0.0f);
	p[X]=vector_x*CParkGenerator::CELL_WIDTH+(mWidth-1)*CParkGenerator::CELL_WIDTH/2.0f;
	p[Z]=vector_z*CParkGenerator::CELL_LENGTH+(mLength-1)*CParkGenerator::CELL_LENGTH/2.0f;

	// Rotate by rot degrees	
	float rad=rot*3.141592654f/180.0f;
	float s=sinf(rad);
	float c=cosf(rad);

	Mth::Vector rotated;
	rotated[X]=c*p[X]+s*p[Z];
	rotated[Y]=0.0f;
	rotated[Z]=c*p[Z]-s*p[X];
	
	// Add the passed pos to get the final world position of the piece.
	Mth::Vector world_pos=pos+rotated;

	// Add in the cell height
	world_pos[Y]+=mY*CParkGenerator::CELL_HEIGHT;
	if (mpMeta->IsRiser())
	{
		world_pos[Y]-=CParkGenerator::CELL_HEIGHT;
	}	
	
	// Create the concrete meta if it does not exist already.
	if (!mpConcreteMeta)
	{
		Dbg_MsgAssert(mpMeta,("NULL mpMeta ?"));
		mpConcreteMeta=CParkManager::sInstance()->CreateConcreteMeta(mpMeta,true);
		mpConcreteMeta->MarkLocked();
	}	
	
	// Position the piece, adding in the pieces local rotation value.
	mpConcreteMeta->SetSoftRot(world_pos,rot+90.0f*m_rot);
}

bool CClipboardEntry::CreateGapFillerPieces()
{
	if (!mpMeta)
	{
		//printf("y=%d\n",mY);
		return true;
	}

	//printf("y=%d Meta=%s\n",mY,Script::FindChecksumName(mpMeta->GetNameChecksum()));

	switch (mpMeta->GetNameChecksum())
	{
	case CRCC(0x66a8fedb,"floor_block1"):
	case CRCC(0xffa1af61,"floor_block2"):
	case CRCC(0x88a69ff7,"floor_block3"):
	case CRCC(0x16c20a54,"floor_block4"):
	case CRCC(0x61c53ac2,"floor_block5"):
	case CRCC(0xf8cc6b78,"floor_block6"):
	case CRCC(0x8fcb5bee,"floor_block7"):
	case CRCC(0x1f74467f,"floor_block8"):
		break;
	default:
		return true;
		break;
	}		

	//printf("y=%d Meta=%s\n",mY,Script::FindChecksumName(mpMeta->GetNameChecksum()));

	bool ran_out_of_memory=false;
	CClipboardEntry *p_new_list=NULL;
	CClipboardEntry *p_last_in_list=NULL;
	for (int y=mY; y>0; --y)
	{
		if (Mem::CPoolable::SGetNumUsedItems()==
			Mem::CPoolable::SGetTotalItems())
		{
			// Not enough space to create a new CClipboardEntry !
			ran_out_of_memory=true;
			break;
		}	
				
		CClipboardEntry *p_new_entry=new CClipboardEntry;
		if (!p_last_in_list)
		{
			p_last_in_list=p_new_entry;
		}	

		if (y==mY)
		{
			p_new_entry->mpMeta = CParkManager::sInstance()->GetAbstractMeta(CRCD(0x2c5b0277,"floor_wall_block3"));
		}
		else
		{
			p_new_entry->mpMeta = CParkManager::sInstance()->GetAbstractMeta(CRCD(0x72372c50,"wall_block1"));
		}
			
		p_new_entry->mX=mX;
		p_new_entry->mY=y;
		p_new_entry->mZ=mZ;
		p_new_entry->mWidth=1;
		p_new_entry->mLength=1;
		p_new_entry->m_rot=m_rot;
		
		// Insert into the list.
		p_new_entry->mpNext=p_new_list;
		p_new_list=p_new_entry;
	}
	
	if (p_new_list)
	{
		Dbg_MsgAssert(p_last_in_list,("Eh?"));
		p_last_in_list->mpNext=mpNext;
		mpNext=p_new_list;
	}
		
	if (ran_out_of_memory)
	{
		return false;
	}
		
	return true;
}
	
CClipboard::CClipboard()
{
	mp_entries=NULL;
	m_min_x=m_min_z=255;
	m_max_x=m_max_z=0;
	
	mpNext=NULL;
	m_area.SetXYZ(0,0,0);
	m_area.SetWHL(0,0,0);
	
	mp_rails=NULL;
}

CClipboard::~CClipboard()
{
	delete_entries();
}

void CClipboard::delete_entries()
{
	CClipboardEntry *p_entry=mp_entries;
	while (p_entry)
	{
		CClipboardEntry *p_next=p_entry->mpNext;
		delete p_entry;
		p_entry=p_next;
	}	
	mp_entries=NULL;
	
	m_min_x=m_min_z=255;
	m_max_x=m_max_z=0;
	
	m_area.SetXYZ(0,0,0);
	m_area.SetWHL(0,0,0);
	
	// Call UpdateSuperSectors to refresh the collision before deleting the rails otherwise the 
	// collision code will assert when the rail sectors are deleted. (TT6874)
	// UpdateSuperSectors isn;t normally called on the rails displayed on the cursor because
	// they don't need collision.
	Nx::CScene *p_cloned_scene=Ed::CParkManager::sInstance()->GetGenerator()->GetClonedScene();
	p_cloned_scene->UpdateSuperSectors();

	Obj::CEditedRail *p_rail=mp_rails;
	while (p_rail)
	{
		Obj::CEditedRail *p_next=p_rail->mpNext;
		delete p_rail;
		p_rail=p_next;
	}
	mp_rails=NULL;
}

void CClipboard::update_extents(uint8 x, uint8 z)
{
	if (x < m_min_x)
	{
		m_min_x=x;
	}
	if (x > m_max_x)
	{
		m_max_x=x;
	}
	if (z < m_min_z)
	{
		m_min_z=z;
	}
	if (z > m_max_z)
	{
		m_max_z=z;
	}
}

// Returns false if it fails due to running out of space on the CClipboardEntry pool
bool CClipboard::AddMeta(CConcreteMetaPiece *p_meta, int raiseAmount)
{
	Dbg_MsgAssert(p_meta,("NULL p_meta"));

	GridDims dims=p_meta->GetArea();
	dims.SetXYZ(dims.GetX(),dims.GetY()+raiseAmount,dims.GetZ());
	//printf("AddMeta y=%d raiseAmount=%d, \t%s Riser=%d\n",dims.GetY(),raiseAmount,Script::FindChecksumName(p_meta->GetNameChecksum()),p_meta->IsRiser());
	
	if (p_meta->IsRiser())
	{
		dims.SetXYZ(dims.GetX(),dims.GetY()+1,dims.GetZ());
		
		//printf("Riser: %d,%d  %d\n",dims.GetX(),dims.GetZ(),dims.GetY());
		
		if (dims.GetY())
		{
			if (!AddHeight(dims.GetX(),dims.GetY(),dims.GetZ()))
			{
				return false;
			}	
		}	
		// Zero height riser pieces (ie, floor pieces) still get added so that they
		// are displayed on the cursor.
	}	

	CAbstractMetaPiece *p_abstract_meta=CParkManager::sInstance()->GetAbstractMeta(p_meta->GetNameChecksum());
	
	// Don't add meta if it is in the list already
	CClipboardEntry *p_entry=mp_entries;
	while (p_entry)
	{
		if (p_entry->mpMeta==p_abstract_meta &&
			p_entry->mX==dims.GetX() &&
			p_entry->mY==dims.GetY() &&
			p_entry->mZ==dims.GetZ() &&
			p_entry->mWidth==dims.GetW() &&
			p_entry->mLength==dims.GetL() &&
			p_entry->m_rot==p_meta->GetRot())
		{
			return true;
		}
		p_entry=p_entry->mpNext;
	}	

	if (Mem::CPoolable::SGetNumUsedItems()==
		Mem::CPoolable::SGetTotalItems())
	{
		// Not enough space to create a new CClipboardEntry !
		return false;
	}	
			
	CClipboardEntry *p_new_entry=new CClipboardEntry;

	// Store a pointer to the abstract meta so that a copy can be made when the time comes to paste.	
	// Mustn't store a pointer to the concrete meta because the user may delete that piece later.
	p_new_entry->mpMeta = p_abstract_meta;

	// Store the grid coords of the concrete meta because the abstract meta's x,y,z will be zero.
	p_new_entry->mX=dims.GetX();
	p_new_entry->mY=dims.GetY();
	p_new_entry->mZ=dims.GetZ();
	// Also store the width and length, needed for calculating the world pos of the centre when
	// displaying the piece on the cursor.
	// Note that the width and height will already take into account any rotation that the concrete meta has.
	p_new_entry->mWidth=dims.GetW();
	p_new_entry->mLength=dims.GetL();

	// Update the min & max x & z for the clipboard selection for calculation of the centre later.
	update_extents(dims.GetX(),					dims.GetZ());
	update_extents(dims.GetX()+dims.GetW()-1,	dims.GetZ()+dims.GetL()-1);
	
	p_new_entry->m_rot=p_meta->GetRot();
	
	// Insert into the list.
	p_new_entry->mpNext=mp_entries;
	mp_entries=p_new_entry;
	return true;
}

// Returns false if it fails due to running out of space on the CClipboardEntry pool
bool CClipboard::AddHeight(int x, int y, int z)
{
	// If there is a piece covering this position already then do not add
	// a new height, to prevent unnecessary column slides.
	CClipboardEntry *p_entry=mp_entries;
	while (p_entry)
	{
		if (p_entry->mpMeta && !p_entry->mpMeta->IsRiser() &&
			p_entry->mX <= x && x < p_entry->mX+p_entry->mWidth &&
			p_entry->mZ <= z && z < p_entry->mZ+p_entry->mLength)
		{
			return true;
		}
		p_entry=p_entry->mpNext;
	}	
	
	// If there is a height defined for x,z already then just update it's y
	// if the passed y is bigger.
	p_entry=mp_entries;
	while (p_entry)
	{
		if (p_entry->mpMeta==NULL &&
			p_entry->mX <= x && x < p_entry->mX+p_entry->mWidth &&
			p_entry->mZ <= z && z < p_entry->mZ+p_entry->mLength)
		{
			if (y >= p_entry->mY)
			{
				p_entry->mY=y;
			}
			return true;
		}
		p_entry=p_entry->mpNext;
	}	
	
	if (Mem::CPoolable::SGetNumUsedItems()==
		Mem::CPoolable::SGetTotalItems())
	{
		// Not enough space to create a new CClipboardEntry !
		return false;
	}	

	CClipboardEntry *p_new_entry=new CClipboardEntry;

	p_new_entry->mpMeta = NULL;

	p_new_entry->mX=x;
	p_new_entry->mY=y;
	p_new_entry->mZ=z;
	p_new_entry->mWidth=1;
	p_new_entry->mLength=1;

	// Update the min & max x & z for the clipboard selection for calculation of the centre later.
	update_extents(x,z);
	
	// Insert into the list.
	p_new_entry->mpNext=mp_entries;
	mp_entries=p_new_entry;
	return true;
}

// Creates clipboard entries for all the things in the passed area.
// A clipboard entry will be created if there is a piece in a cell, or if
// the cell has a non-zero height.
// Returns false if it fails due to running out of space on the CClipboardEntry pool.
// If that happens, it will delete any CClipboardEntry's that it already allocated.
bool CClipboard::CopySelectionToClipboard(GridDims area)
{
	m_area=area;
	
	CParkManager *p_manager=CParkManager::sInstance();

	// First, figure out how much the selection needs to be raised up so that
	// all the pieces are above ground level.
	int most_negative_y=0;	
	for (int x = area.GetX(); x < area.GetX() + area.GetW(); x++)
	{
		for (int z = area.GetZ(); z < area.GetZ() + area.GetL(); z++)
		{
			CMapListNode *p_entry=p_manager->GetMapListNode(x,z);
			while (p_entry)
			{
				CConcreteMetaPiece *p_meta=p_entry->GetConcreteMeta();
				
				//printf("Meta name = %s\n",Script::FindChecksumName(p_meta->GetNameChecksum()));
				int y=p_meta->GetArea().GetY();
				switch (p_meta->GetNameChecksum())
				{
				case CRCC(0x66a8fedb,"floor_block1"):
				case CRCC(0xffa1af61,"floor_block2"):
				case CRCC(0x88a69ff7,"floor_block3"):
				case CRCC(0x16c20a54,"floor_block4"):
				case CRCC(0x61c53ac2,"floor_block5"):
				case CRCC(0xf8cc6b78,"floor_block6"):
				case CRCC(0x8fcb5bee,"floor_block7"):
				case CRCC(0x1f74467f,"floor_block8"):
					// With floor blocks, the ground position is 1 unit higher up than
					// their stored y, because their stored y is the y of their base.
					++y;
					break;
				default:
					break;
				}		
				if (y < most_negative_y)
				{
					most_negative_y=y;
				}

				//printf("y=%d  %s\n",p_meta->GetArea().GetY(),Script::FindChecksumName(p_meta->GetNameChecksum()));
				
				p_entry = p_entry->GetNext();
			}
		}
	}
	
	
	int raise_amount=-most_negative_y;
	
	
	for (int x = area.GetX(); x < area.GetX() + area.GetW(); x++)
	{
		for (int z = area.GetZ(); z < area.GetZ() + area.GetL(); z++)
		{
			CMapListNode *p_entry=p_manager->GetMapListNode(x,z);
			while (p_entry)
			{
				CConcreteMetaPiece *p_meta=p_entry->GetConcreteMeta();
				if (p_meta->IsRestartOrFlag())
				{
					// Don't copy restarts or flags (TT7793)
				}
				else if (!p_meta->IsCompletelyWithinArea(area))
				{
				}
				else
				{
					if (!AddMeta(p_meta,raise_amount))
					{
						// Delete the existing CClipboardEntry's so that another attempt
						// at calling CopySelectionToClipboard can be made later after the
						// calling code has freed up some memory.
						delete_entries();
						return false;
					}
				}		
				
				p_entry = p_entry->GetNext();
			}
		}
	}	

	// Now one last ugly bit. Raising the selection may have moved some simple ground pieces
	// to above the ground, in which case they won't have pieces filling the gap, which looks odd.
	// So create these pieces if needed.
	CClipboardEntry *p_entry=mp_entries;
	while (p_entry)
	{
		if (!p_entry->CreateGapFillerPieces())
		{
			// Could not create the pieces, due to low memory.
			delete_entries();
			return false;
		}	
		p_entry=p_entry->mpNext;
	}


	Dbg_MsgAssert(mp_rails==NULL,("Expected mp_rails=NULL"));
	Mth::Vector corner_pos;
	Mth::Vector area_dims;
	corner_pos=Ed::CParkManager::Instance()->GridCoordinatesToWorld(area,&area_dims);
	mp_rails=Obj::GetRailEditor()->GenerateDuplicateRails(corner_pos[X], corner_pos[Z], 
														  corner_pos[X]+area_dims[X], corner_pos[Z]+area_dims[Z]);
		
	return true;
}

bool CClipboard::IsEmpty()
{
	if (mp_entries)
	{
		return false;
	}
	if (mp_rails)
	{
		return false;
	}	
	return true;
}
		
// Pastes the contents of the clipboard into the position that the cursor is at,
// using the cursor's rotation value.
bool CClipboard::Paste(CCursor *p_cursor)
{
	Spt::SingletonPtr p_editor;
	if (!p_editor->RoomToCopyOrPaste(m_max_x-m_min_x+1, m_max_z-m_min_z+1))
	{
		return false;
	}	

	// Get the map coords of the centre cell of the set of meta's
	uint8 centre_x=(m_min_x+m_max_x)/2;
	uint8 centre_z=(m_min_z+m_max_z)/2;
	
	// Bail out if any entry cannot be pasted
	CClipboardEntry *p_entry=mp_entries;
	while (p_entry)
	{
		if (!p_entry->CanPaste(centre_x,centre_z,p_cursor))
		{
			return false;
		}	
		p_entry=p_entry->mpNext;
	}	

	// Bail out if there are not enough free rail points to create the edited rails
	int num_rail_points_to_paste=0;
	Obj::CEditedRail *p_rail=mp_rails;
	while (p_rail)
	{
		num_rail_points_to_paste += p_rail->CountPoints();
		p_rail=p_rail->mpNext;
	}
	if (num_rail_points_to_paste > Obj::GetRailEditor()->GetNumFreePoints() )
	{
		return false;
	}	

	
	// OK to proceed, so paste the entries
	p_entry=mp_entries;
	while (p_entry)
	{
		p_entry->Paste(centre_x,p_cursor->GetClipboardY(),centre_z,p_cursor);
		p_entry=p_entry->mpNext;
	}	
	
	// and paste the rails
	Mth::Vector clipboard_pos=GetClipboardWorldPos();
	Mth::Vector cursor_pos=p_editor->GetCursorPos();
	
	p_rail=mp_rails;
	while (p_rail)
	{
		Obj::CEditedRail *p_new_rail=Obj::GetRailEditor()->NewRail();
		p_new_rail->CopyRail(p_rail);
		p_new_rail->RotateAndTranslate(clipboard_pos, cursor_pos, p_cursor->GetRotation()*90.0f);
		
		p_new_rail->UpdateRailGeometry();
		p_new_rail->UpdatePostGeometry();
		
		p_rail=p_rail->mpNext;
	}
		
	return true;
}


int CClipboard::GetFloorHeight(CCursor *p_cursor)
{
	// Get the map coords of the centre cell of the set of meta's
	uint8 centre_x=(m_min_x+m_max_x)/2;
	uint8 centre_z=(m_min_z+m_max_z)/2;

	int max_height=-1000;	
	CClipboardEntry *p_entry=mp_entries;
	while (p_entry)
	{
		int height=p_entry->GetFloorHeight(centre_x,centre_z,p_cursor);
		if (height > max_height)
		{
			max_height=height;
		}	
		p_entry=p_entry->mpNext;
	}	
	
	if (max_height==-1000)
	{
		return 0;
	}
	return max_height;
}

int CClipboard::FindMaxFloorHeight(CCursor *p_cursor)
{
	// Get the map coords of the centre cell of the set of meta's
	uint8 centre_x=(m_min_x+m_max_x)/2;
	uint8 centre_z=(m_min_z+m_max_z)/2;

	int max_floor_height=-1000;	
	CClipboardEntry *p_entry=mp_entries;
	while (p_entry)
	{
		int height=p_entry->GetFloorHeight(centre_x,centre_z,p_cursor);
		if (height > max_floor_height)
		{
			max_floor_height=height;
		}	
			
		p_entry=p_entry->mpNext;
	}	
	
	if (max_floor_height != -1000)
	{
		return max_floor_height;
	}
	return 0;	
}

void CClipboard::HighlightIntersectingMetas(CCursor *p_cursor, bool on)
{
	// Get the map coords of the centre cell of the set of meta's
	uint8 centre_x=(m_min_x+m_max_x)/2;
	uint8 centre_z=(m_min_z+m_max_z)/2;
	
	CClipboardEntry *p_entry=mp_entries;
	while (p_entry)
	{
		p_entry->HighlightIntersectingMetas(centre_x,centre_z,p_cursor,on);
		p_entry=p_entry->mpNext;
	}	
}

int CClipboard::GetWidth()
{
	return m_max_x-m_min_x+1;
}

int CClipboard::GetLength()
{
	return m_max_z-m_min_z+1;
}

void CClipboard::GetArea(CCursor *p_cursor, GridDims *p_area)
{
	// Get the map coords of the centre cell of the set of meta's
	uint8 centre_x=(m_min_x+m_max_x)/2;
	uint8 centre_z=(m_min_z+m_max_z)/2;
	
	int idx=m_min_x-centre_x;
	int idz=m_min_z-centre_z;
	int dx=0;
	int dz=0;
	
	switch ( p_cursor->GetRotation() )
	{
		case Mth::ROT_0:
			dx=idx;
			dz=idz;
			p_area->SetWHL(m_area.GetW(),1,m_area.GetL());
			break;
		case Mth::ROT_270:
			dx=-idz;
			dz=idx;
			p_area->SetWHL(m_area.GetL(),1,m_area.GetW());
			// Shift dx,dz so that they refer to the top-left point again
			dx=dx-m_area.GetL()+1;
			break;
		case Mth::ROT_180:
			dx=-idx;
			dz=-idz;
			p_area->SetWHL(m_area.GetW(),1,m_area.GetL());
			// Shift dx,dz so that they refer to the top-left point again
			dx=dx-m_area.GetW()+1;
			dz=dz-m_area.GetL()+1;
			break;
		case Mth::ROT_90:
			dx=idz;
			dz=-idx;
			p_area->SetWHL(m_area.GetL(),1,m_area.GetW());
			// Shift dx,dz so that they refer to the top-left point again
			dz=dz-m_area.GetW()+1;
			break;
		default:
			break;
	}		

	GridDims cursor_position=p_cursor->GetPosition();
	p_area->SetXYZ(cursor_position.GetX()+dx,0,cursor_position.GetZ()+dz);
}

Mth::Vector CClipboard::GetClipboardWorldPos()
{
	// Get the map coords of the centre cell of the set of meta's
	uint8 centre_x=(m_min_x+m_max_x)/2;
	uint8 centre_z=(m_min_z+m_max_z)/2;
	
	GridDims centre_cell;
	centre_cell.SetXYZ(centre_x,m_area.GetY(),centre_z);
	centre_cell.SetWHL(1,1,1);
	
	Mth::Vector corner_pos;
	Mth::Vector area_dims;
	corner_pos=Ed::CParkManager::Instance()->GridCoordinatesToWorld(centre_cell,&area_dims);
	// corner_pos is the world pos of the top left corner of the centre cell
	
	Mth::Vector clipboard_pos=corner_pos;
	clipboard_pos[X]+=area_dims[X]/2.0f;
	clipboard_pos[Z]+=area_dims[Z]/2.0f;
	// clipboard_pos is the world pos of the centre of the cell
	
	return clipboard_pos;
}

// This gets called when the CCursor is in paste mode, and needs to display the clipboard contents at
// the cursor position.
void CClipboard::ShowMetas(Mth::Vector pos, float rot, int clipboardY)
{
	// Get the map coords of the centre cell of the set of meta's
	uint8 centre_x=(m_min_x+m_max_x)/2;
	uint8 centre_z=(m_min_z+m_max_z)/2;
	
	CClipboardEntry *p_entry=mp_entries;
	while (p_entry)
	{
		p_entry->ShowMeta(centre_x,clipboardY,centre_z,pos,rot);
		p_entry=p_entry->mpNext;
	}	
	
	
	Mth::Vector clipboard_pos=GetClipboardWorldPos();
	
	Obj::CEditedRail *p_rail=mp_rails;
	while (p_rail)
	{
		p_rail->UpdateRailGeometry(clipboard_pos,pos,rot);
		p_rail->UpdatePostGeometry(clipboard_pos,pos,rot);
		p_rail=p_rail->mpNext;
	}	
}

void CClipboard::DestroyMetas()
{
	CClipboardEntry *p_entry=mp_entries;
	while (p_entry)
	{
		p_entry->DestroyMeta();
		p_entry=p_entry->mpNext;
	}	

	Obj::CEditedRail *p_rail=mp_rails;
	while (p_rail)
	{
		p_rail->DestroyPostGeometry();
		p_rail->DestroyRailGeometry();
		p_rail=p_rail->mpNext;
	}	
}

} // namespace Ed



================================================
FILE: Code/Sk/ParkEditor2/clipboard.h
================================================
#ifndef __SK_PARKEDITOR2_CLIPBOARD_H
#define __SK_PARKEDITOR2_CLIPBOARD_H

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __SYS_MEM_POOLABLE_H
#include 
#endif

#ifndef __SK_PARKEDITOR2_PARKGEN_H
#include 
#endif

namespace Obj
{
	class CEditedRail;
}
	
namespace Ed
{

class CAbstractMetaPiece;

enum
{
	MAX_CLIPBOARD_METAS=1000,
	MAX_CLIPBOARDS=6,
	CLIPBOARD_MAX_W=10,
	CLIPBOARD_MAX_L=10,
};

class CClipboardEntry : public Mem::CPoolable
{
public:
	CClipboardEntry();
	~CClipboardEntry();

	void CalculateMapCoords(GridDims *p_mapCoords, uint8 centre_x, uint8 centre_z, CCursor *p_cursor);
	bool CanPaste(uint8 centre_x, uint8 centre_z, CCursor *p_cursor);
	void Paste(uint8 centre_x, int raiseAmount, uint8 centre_z, CCursor *p_cursor);
	int	 GetFloorHeight(uint8 centre_x, uint8 centre_z, CCursor *p_cursor);
	void HighlightIntersectingMetas(uint8 centre_x, uint8 centre_z, CCursor *p_cursor, bool on);
	void ShowMeta(uint8 centre_x, int raiseAmount, uint8 centre_z, Mth::Vector pos, float rot);
	void DestroyMeta();
	bool CreateGapFillerPieces();
	
	CAbstractMetaPiece *mpMeta;
	uint8 mX;
	sint8 mY;
	uint8 mZ;
	Mth::ERot90 m_rot;
	uint8 mWidth;
	uint8 mLength;
	
	// Used when displaying the cursor
	CConcreteMetaPiece *mpConcreteMeta;
	
	CClipboardEntry *mpNext;
};

class CClipboard : public Mem::CPoolable
{
	GridDims m_area;
	
	uint8 m_min_x;
	uint8 m_min_z;
	uint8 m_max_x;
	uint8 m_max_z;
	void update_extents(uint8 x, uint8 z);
	
	CClipboardEntry *mp_entries;
	void delete_entries();
	
	Obj::CEditedRail *mp_rails;
	
public:
	CClipboard();
	~CClipboard();

	bool AddMeta(CConcreteMetaPiece *p_meta, int raiseAmount);
	bool AddHeight(int x, int y, int z);
	bool CopySelectionToClipboard(GridDims area);
	bool IsEmpty();
	bool Paste(CCursor *p_cursor);
	int	 GetFloorHeight(CCursor *p_cursor);
	int	 FindMaxFloorHeight(CCursor *p_cursor);
	void HighlightIntersectingMetas(CCursor *p_cursor, bool on);
	void ShowMetas(Mth::Vector pos, float rot, int clipboardY);
	void DestroyMetas();
	void GetArea(CCursor *p_cursor, GridDims *p_area);
	Mth::Vector GetClipboardWorldPos();

	int GetWidth();
	int GetLength();
	
	CClipboard *mpNext;	
};

}

#endif




================================================
FILE: Code/Sk/Scripting/cfuncs.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Scripting
//* FILENAME:       CFuncs.cpp
//* OWNER:          Kendall Harrison
//* CREATION DATE:  9/14/2000
//****************************************************************************

// start autoduck documentation
// @DOC cfuncs
// @module cfuncs | None
// @subindex Scripting Database
// @index script | cfuncs

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 
//#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef __PLAT_NGPS__
//    #include 
//    #include 
    #include 
	//#include 
	#include 
#endif

#ifdef __PLAT_NGC__
#include 
#include 
#endif

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include 
#include 
#include 
#include 
#include 
#include 
 
#include  
#include  

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef TESTING_GUNSLINGER
#include 
#endif

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include 
//#include 
#include 
#include 
#include 

#include 

#include 

#ifdef __PLAT_XBOX__
#include 
#endif

#include 

bool DumpFunctionTimesTrigger=false;

void MemViewToggle();

namespace Script
{
	extern void DumpLastStructs();
}

namespace Gfx
{
	void DebuggeryLines_CleanUp( void );		// just for debugging
}

#ifdef	__PLAT_NGPS__
namespace NxPs2
{
#ifdef	__NOPT_ASSERT__
extern uint32		gPassMask1; 		// 1<<6 | 1<<7  (0x40, 0x80)
extern uint32		gPassMask0; 		// 1<<6 | 1<<7  (0x40, 0x80)
#endif 
}
#endif // __PLAT_NGPS__


// This is the level specific qb file.
#if 0
static uint32 s_level_specific_qb=0;
#else
// extended to have more than one
#define	MAX_LEVEL_QBS  2
static uint32 s_level_specific_qbs[MAX_LEVEL_QBS];
#endif


///////////////////////////////////////////////////////////////////
// some temp debugging functions
#define	DUMP_LEN	1024
static void * dump_handle;	
static char p_buffer[1024];
static char *p_pos;

int dumping_printfs = 0;		

bool dump_open(const char *name)
{	
	dump_handle = File::Open( name, "wb");		
	p_pos = p_buffer;

	if (dump_handle != NULL)
	{
		dumping_printfs = 1;
	}

	return ( dump_handle != NULL );
}

void dump_flush()
{
	int len = p_pos - p_buffer;
	if (len)
	{
		File::Write( p_buffer, len, 1, dump_handle );     		
		p_pos = p_buffer;
	}
}

// print to file, replacing lf with cr/lf, so I can read them in notepad
void dump_printf(char *p)
{
	int len = strlen(p);
	if ((int)(p_pos + len) > (int)(p_buffer + DUMP_LEN-16))
	{
		dump_flush();
	}
	
	for (int i= 0; i*	s_network_test_task;
static Tsk::Task< int >*		s_second_controller_check_task = NULL;

// GJ:  viewer mode stuff...  will eventually be moved to CViewer class, hopefully.
static int s_view_mode = 0;
static uint32	s_last_broadcast_cheat = 0;
														
/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*
static void		test_network_task( const Tsk::Task< Tmr::Time >& task )
{
	// Wait a second before testing so the user can see the status box
	if(( Tmr::GetTime() - s_start_network_test_time ) < Tmr::Seconds( 1 ))
	{
		return;
	}
	
	Net::Manager * net_man =  Net::Manager::Instance();
	bool properly_setup;
	 
	properly_setup = net_man->NetworkEnvironmentSetup();
	if( !properly_setup )
	{
		if( net_man->GetLastError() == Net::vRES_ERROR_DEVICE_NOT_HOT )
		{
			Script::CStruct* pStructure;
			
			pStructure = new Script::CStruct;
			
			pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_not_connected" ));
			Script::RunScript( "create_net_startup_error_dialog", pStructure );

			delete pStructure;
		}
		else if( ( net_man->GetLastError() == Net::vRES_ERROR_DEVICE_NOT_CONNECTED ) ||
				 ( net_man->GetLastError() == Net::vRES_ERROR_UNKNOWN_DEVICE ))
		{
			Script::CStruct* pStructure;
			
			pStructure = new Script::CStruct;
			
			pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_not_detected" ));
			Script::RunScript( "create_net_startup_error_dialog", pStructure );

			delete pStructure;
		}
		else if( net_man->GetLastError() == Net::vRES_ERROR_DHCP )
		{
			Script::CStruct* pStructure;
			
			pStructure = new Script::CStruct;
			
			pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_dhcp_error" ));
			Script::RunScript( "create_net_startup_error_dialog", pStructure );

			delete pStructure;
		}
		else if( net_man->GetLastError() == Net::vRES_ERROR_DEVICE_CHANGED )
		{
			Script::CStruct* pStructure;
			
			pStructure = new Script::CStruct;
			
			pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_changed_device" ));
			Script::RunScript( "create_net_startup_error_dialog", pStructure );

			delete pStructure;
		}
		else
		{
			Script::CStruct* pStructure;
			
			pStructure = new Script::CStruct;
			
			pStructure->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_general_error" ));
			Script::RunScript( "create_net_startup_error_dialog", pStructure );

			delete pStructure;
		}
	}
	else
	{   
		Script::CStruct* pParams;

		Script::RunScript( "dialog_box_exit" );
		//Script::RunScript( "launch_network_select_menu" );
		
        pParams = new Script::CStruct;

		pParams->AddChecksum( "change_gamemode", Script::GenerateCRC( "change_gamemode_net" ));
		Script::RunScript( "launch_select_skater_menu", pParams );
		
		delete pParams;
	}

	delete s_network_test_task;
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static	void	s_second_controller_check_code( const Tsk::Task< int > &task )
{
	// Get the second controller.
	 SIO::Manager * pSioMan =  SIO::Manager::Instance();
	SIO::Device *pFirstController=pSioMan->GetDeviceByIndex(0);
	SIO::Device *pSecondController=pSioMan->GetDeviceByIndex(1);
	
#ifdef __PLAT_XBOX__
	int num_plugged_devices;
	SIO::Device *pThirdController=pSioMan->GetDeviceByIndex(2);
	SIO::Device *pFourthController=pSioMan->GetDeviceByIndex(3);
	
	num_plugged_devices = 0;
	if( pFirstController->IsPluggedIn())
	{
		num_plugged_devices++;
	}
	if( pSecondController->IsPluggedIn())
	{
		num_plugged_devices++;
	}
	if( pThirdController->IsPluggedIn())
	{
		num_plugged_devices++;
	}
	if( pFourthController->IsPluggedIn())
	{
		num_plugged_devices++;
	}

	if( num_plugged_devices >= 2 )
	{
		Script::RunScript( "enable_two_player_option" );
	}	
	else
	{
		Script::RunScript( "disable_two_player_option" );
	}         
#else
	if (pSecondController->IsPluggedIn() && pFirstController->IsPluggedIn())
	{
		Script::RunScript( "enable_two_player_option" );
	}	
	else
	{
		Script::RunScript( "disable_two_player_option" );
	}         
#endif
#ifdef __PLAT_XBOX__
	DWORD result;
	static Tmr::Time s_last_link_test;
	
	if(( Tmr::GetTime() - s_last_link_test ) >= Tmr::Seconds( 1 ))
	{
		result = XNetGetEthernetLinkStatus();
        if(( result & XNET_ETHERNET_LINK_ACTIVE ) == 0 )
		{
			Script::RunScript( "disable_system_link_option" );
		}
		else
		{
			Script::RunScript( "enable_system_link_option" );
		}
		s_last_link_test = Tmr::GetTime();
	}
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*
static bool SetupNetworkDeviceDialogHandler( Front::EDialogBoxResult Result )
{
	
	switch (Result)
	{
		case Front::DB_YES:
		{
			Mdl::FrontEnd* front = Mdl::FrontEnd::Instance();
			Front::MenuFactory* menu_factory = Front::MenuFactory::Instance();
			Front::DialogBox* dlg;
			 
			dlg = front->GetDialogBox();
			dlg->GoBack();
					
			Front::MenuEvent event;
			event.SetTypeAndTarget( Front::MenuEvent::vLINK, Script::GenerateCRC("net_network_setup_menu") );
			menu_factory->LaunchEvent(&event);
			
			return false;
		}
		default:
			break;
	}		

	return true;
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

bool ScriptDummyCommand(Script::CStruct *pParams, Script::CScript *pScript)
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// C++ version of the script command	
bool SetActiveCamera(uint32 id, int viewport, bool move_to_current)
{
	Obj::CCompositeObject *p_obj = (Obj::CCompositeObject *)Obj::CCompositeObjectManager::Instance()->GetObjectByID(id);
	if (!p_obj)
	{
		return false;
	}
	
	Obj::CCameraComponent * p_cam_comp = GetCameraComponentFromObject(p_obj); 
	
	if (p_cam_comp)
	{
		// Move this camera to the current camera position.  Useful for seamlessly 
		// transitioning between cameras
		if (move_to_current)
		{
			Dbg_MsgAssert(Nx::CViewportManager::sGetActiveCamera(viewport), ("No active camera for viewport %d\n",viewport));
			p_obj->m_matrix = Nx::CViewportManager::sGetActiveCamera(viewport)->GetMatrix();
			p_obj->m_pos = Nx::CViewportManager::sGetActiveCamera(viewport)->GetPos();			
			p_cam_comp->Update();  // bit dodgy...  force the camera to update itself from the parent object
		}
		
		Nx::CViewportManager::sSetCamera( viewport, p_cam_comp->GetCamera() );
	}
	else
	{
		Dbg_Message("WARNING:  Trying to set camera from object with no camera component");
	}

#if 0
// this is not needed any more, as the proxim trigger component has been deprecated in favour 
// of using whatever camera directly	
	// deactivate all proxim trigger components with this viewport on other objects with cameras and activate any proxim trigger on the new camera object
	Obj::CProximTriggerComponent* p_prox_comp
		= static_cast< Obj::CProximTriggerComponent* >(Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_PROXIMTRIGGER));
	while (p_prox_comp)
	{
		Obj::CCameraComponent* p_peer_cam_comp = GetCameraComponentFromObject(p_prox_comp->GetObject());
		
		if (p_peer_cam_comp)
		{
			if (p_peer_cam_comp == p_cam_comp)
			{
				p_prox_comp->SetActive();
				p_prox_comp->SetViewport(viewport);
			}
			else
			{
				if (p_prox_comp->GetViewport() == viewport)
				{
					p_prox_comp->SetInactive();
				}
			}
		}
		
		p_prox_comp = static_cast< Obj::CProximTriggerComponent* >(p_prox_comp->GetNextSameType());
	}

	// K: If a different camera is switched to when the game is paused it could be that the geometry
	// it is looking at was switched off by the previous camera. So refresh the proxim component
	// to make sure it switches back on.
	// (This fixes a bug when scrolling through the view-gaps menu, where geometry was switching off)
	p_prox_comp=GetProximTriggerComponentFromObject(p_obj);
	if (p_prox_comp)
	{
		p_prox_comp->ProximUpdate();
	}	
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/* Waits for n CScript::Update calls.                             */
/******************************************************************/

// @script | Wait | Waits for a period of time, where the time can be specified in various units. 
// For example:  Wait 1200       By default, the units are milliseconds, so 
// this will wait for 1.2 seconds 
// Wait 2.1 seconds     This will wait for 2.1 seconds. 
// Wait 60 frames       This will wait for 60 sixtieths of a second, regardless of the framerate. 
// Wait 60 gameframes       This will wait for exactly 60 logic calls, 
// so the actual time it takes will depend on the current framerate. 
// In all the above, the units do not have to be plural, so one could type 
// Wait 1 second instead of Wait 1 seconds
// @parm float |  | number of units to wait
// @flag seconds | measure time in seconds
// @flag frames | measure time in frames
// @flag gameframes | measure time in gameframes 
bool ScriptWait(Script::CStruct *pParams, Script::CScript *pScript)
{
	float Period=0.0f;
	pParams->GetFloat(NONAME,&Period);
	
	if (pParams->ContainsFlag(0xd029f619/*"seconds"*/) || pParams->ContainsFlag(0x49e0ee96/*"second"*/))
	{
		pScript->WaitTime((int)(Period*1000.0f));
		return true;
	}	

	if (pParams->ContainsFlag(0xfc37df05/*"gameframe"*/) || pParams->ContainsFlag(0xb99ae3d6/*"gameframes"*/) || pParams->ContainsFlag(0xdcd4ce73/*"game"*/))
	{
		pScript->Wait((int)Period);
		return true;
	}	

	if (pParams->ContainsFlag(0x4a07c332/*"frame"*/) || pParams->ContainsFlag(0x19176c5/*"frames"*/))
	{
		pScript->WaitTime((int)(Period*1000.0f/60.0f)); // This should NOT be changed for PAL, as Tmr::GetTime accounts for PAL
		return true;
	}		

	if (pParams->ContainsFlag(0xecaa3345/*"one_per_frame"*/))
	{
		pScript->WaitOnePerFrame((int)Period);
		return true;
	}	
	

	
	pScript->WaitTime((int)Period);
	return true;
}

/******************************************************************/
/*                                                                */
/*  Added for testing if statements, may be useful anyway though  */
/******************************************************************/

// @script | IsZero | added for testing if statements.  may be useful
bool ScriptIsZero(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	
	int Val=0;
	pParams->GetInteger(NONAME,&Val);
	if (Val==0)
	{
		return true;
	}
	else
	{
		return false;
	}		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | CastToInteger | If a parameter with the passed name exists and is a float,
// this will cast it down to an integer.
// So for example, if x=3.141, after calling CastToInteger x, x will be 3.
// @uparmopt name | The name of the parameter to be cast down
bool ScriptCastToInteger(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 param_name=0;
	if (!pParams->GetChecksum(NONAME,¶m_name))
	{
		return false;
	}
	
	float v=0.0f;
	if (!pScript->GetParams()->GetFloat(param_name,&v))
	{
		return false;
	}

	// Need to remove the parameter first, otherwise we will end up with both an integer
	// and a float parameter with the same name. Components are only automatically replaced if
	// the new component has the same name and type.
	pScript->GetParams()->RemoveComponent(param_name);
	pScript->GetParams()->AddInteger(param_name,(int)v);
	return true;
}

// @script bool | StringToInteger | If a parameter with the passed name exists and is a string,
// this will convert it to a integer.
// So for example, if x="123", after calling StringToInteger x, x will be 123.
// @uparmopt name | The name of the string parameter to be converted.
bool ScriptStringToInteger(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 param_name=0;
	if (!pParams->GetChecksum(NONAME,¶m_name))
	{
		return false;
	}
	
	const char *p_string="";
	if (!pScript->GetParams()->GetString(param_name,&p_string))
	{
		return false;
	}

	// Need to remove the parameter first, otherwise we will end up with both an integer
	// and a string parameter with the same name. Components are only automatically replaced if
	// the new component has the same name and type.
	pScript->GetParams()->RemoveComponent(param_name);
	
	int v=0;
	int len=strlen(p_string);
	const char *p_ch=p_string;
	for (int i=0; i='0' && *p_ch<='9',("\n%s\nNon-numeric char in '%s'",pScript->GetScriptInfo(),p_string));
		v=v*10+*p_ch++-'0';
	}
	
	pScript->GetParams()->AddInteger(param_name,(int)v);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | IntegerEquals | Returns true if the two values equal
// @parmopt int | a | 0 | First value
// @parmopt int | b | 0 | Second value
bool ScriptIntegerEquals(Script::CStruct *pParams, Script::CScript *pScript)
{
	int a = 0;
	pParams->GetInteger( CRCD(0x174841bc,"a"), &a );
	
	int b = 0;
	pParams->GetInteger( CRCD(0x8e411006,"b"), &b );
	
	return ( a == b );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | ChecksumEquals | Returns true if two checksums are equal
// @parmopt name | a | 0 (checksum value) | First name
// @parmopt name | b | 0 (checksum value) | Second name
bool ScriptChecksumEquals(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	uint32 a=0;
	pParams->GetChecksum("a",&a);
	uint32 b=0;
	pParams->GetChecksum("b",&b);

	return a==b;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | StringEquals | Returns true if two strings are equal
// @parm string | a | First string
// @parm string | b | Secnond string
bool ScriptStringEquals(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	const char* a;
	pParams->GetText("a",&a,true);
	const char* b;
	pParams->GetText("b",&b,true);

	return Script::GenerateCRC(a)==Script::GenerateCRC(b);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | ArrayContains | Returns true if the array contains an
// element equal to the variable. 
// Example:  ArrayContains array=some_int_array contains=4 
// ArrayContains array=some_string_array contains="some_string"
// @parm array | array | The array to search
// @parm | contains | The element to search for.  Must match array type
bool ScriptArrayContains(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	Script::CArray* pArray;
	pParams->GetArray( "array", &pArray, true );

	uint8 array_type = pArray->GetType();

	for ( uint32 i = 0; i < pArray->GetSize(); i++ )
	{
		switch ( array_type )
		{
			case ESYMBOLTYPE_INTEGER:
			{
				int desiredInteger;
				pParams->GetInteger( "contains", &desiredInteger, true );
				if ( desiredInteger == pArray->GetInt( i ) )
					return true;
				break;
			}
			case ESYMBOLTYPE_NAME:
			{
				uint32 desiredChecksum;
				pParams->GetChecksum( "contains", &desiredChecksum, true );
				if ( desiredChecksum == pArray->GetNameChecksum( i ) )
					return true;
				break;
			}
			case ESYMBOLTYPE_STRING:
			{
				const char* pDesiredString;
				pParams->GetText( "contains", &pDesiredString, true );
				// Changed to use stricmp instead of GenerateCRC because GenerateCRC sees \ and / as
				// being the same character.
				if (stricmp(pDesiredString, pArray->GetString( i )) == 0)
					return true;
				break;
			}
			default:
				Dbg_MsgAssert( 0, ( "This data type is not yet supported by ArrayContains" ) );			
				break;
		}
	}

	// item was not found
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetArraySize | returns the number of elements in the specified structure
// @parm | array_size | return value for array size
bool ScriptGetArraySize(Script::CStruct *pParams, Script::CScript *pScript)
{
	Script::CArray* pArray;
	
	pParams->GetArray( NONAME, &pArray, Script::ASSERT );

	// Add support for higher dimensional arrays
	uint32 index = 0;
	if(pParams->GetInteger(CRCD(0xba453b9,"index1"),(int*)&index, Script::NO_ASSERT))
	{
		pArray = pArray->GetArray(index);

		if(pParams->GetInteger(CRCD(0x92ad0203,"index2"),(int*)&index, Script::NO_ASSERT))
		{
			pArray = pArray->GetArray(index);

			if(pParams->GetInteger(CRCD(0xe5aa3295,"index3"),(int*)&index, Script::NO_ASSERT))
			{
				pArray = pArray->GetArray(index);
			}
		}
	}

	pScript->GetParams()->AddInteger( "array_size", pArray->GetSize() );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetArrayElement | Changes the value of an element of an array.
// @parm name | ArrayName | The name of the array in the current script's parameters whose
// value is to be changed.
// @parm int | Index | The index of the array element whose value is to be changed.
// @parm int | NewValue | The new value for the element. The type of this value must match the
// type of the array, so it is not necessarily an integer. If the type does not match that of
// the array it will assert.
bool ScriptSetArrayElement(Script::CStruct *pParams, Script::CScript *pScript)
{
	// TODO: Most of this code should really be in the CArray class. Need to go over that
	// class to make it easier to manipulate arrays safely. (resize, etc)
	
	// Get a pointer to the CArray that is to be modified.
	// It will be in the script's parameters.
	uint32 array_name=0;
	pParams->GetChecksum("ArrayName",&array_name,Script::ASSERT);
	Script::CArray *p_array=NULL;
	pScript->GetParams()->GetArray(array_name,&p_array,Script::ASSERT);
	
	// Get the index of the element to be changed.
	uint32 index=0;
	pParams->GetInteger("Index",(int*)&index,Script::ASSERT);

	// Find the new value which is to be written into the array.	
	Script::CComponent *p_new_value=pParams->FindNamedComponentRecurse(CRCD(0x4b40630d,"NewValue"));
	Dbg_MsgAssert(p_new_value,("\n%s\nMissing NewValue parameter in call to SetArrayElement",pScript->GetScriptInfo()));
	// Resolve the componentn in case the new value is referring to some global.
	Script::ResolveNameComponent(p_new_value);
	
	// Now make a copy of the component. This is because it may contain a pointer, eg a pointer to
	// a struct, vector, string etc, and that entity may only exist temporarily.
	// CopyComponent will create a new pointer which will be safe to delete later.
	Script::CComponent *p_copy=new Script::CComponent;
	Script::CopyComponent(p_copy,p_new_value);

	// Check that the type of the new value is consistent with the type of the array.
	if (p_array->GetType()==ESYMBOLTYPE_FLOAT && 
		p_copy->mType==ESYMBOLTYPE_INTEGER)
	{
		// It's OK to write an integer value into an array of floats so don't assert in this case.
	}
	else
	{
		Dbg_MsgAssert(p_copy->mType==p_array->GetType(),("Array type of %s does not match the NewValue type of %s in call to SetArrayElement",Script::GetTypeName(p_array->GetType()),Script::GetTypeName(p_copy->mType)));
	}
	
	// Write the new value into the array.
	switch (p_array->GetType())
	{
	case ESYMBOLTYPE_INTEGER:
		p_array->SetInteger(index,p_copy->mIntegerValue);
		break;
    case ESYMBOLTYPE_FLOAT:
		if (p_copy->mType==ESYMBOLTYPE_FLOAT)
		{
			p_array->SetFloat(index,p_copy->mFloatValue);
		}
		else
		{
			Dbg_MsgAssert(p_copy->mType==ESYMBOLTYPE_INTEGER,("Eh ?"));
			p_array->SetFloat(index,(float)p_copy->mIntegerValue);
		}
		break;
    case ESYMBOLTYPE_NAME:
		p_array->SetChecksum(index,p_copy->mChecksum);
		break;
	// Now for the types that are referenced by a pointer.
	// In each of these cases, first the existing pointer in the array gets deleted.
	// The the pointer to the array of pointers is got, and the new pointer written in.
	// Cannot use the regular SetString, SetVector etc functions because they all assert if
	// the existing pointer is not NULL, to catch memory leaks.
    case ESYMBOLTYPE_STRING:
	{
		Script::DeleteString(p_array->GetString(index));
		char **pp_strings=(char**)p_array->GetArrayPointer();
		// Need to do a bounds check since not using SetString
		Dbg_MsgAssert(indexGetSize(),("Bad index of %d sent to SetArrayElement, array size is %d",index,p_array->GetSize()));
		pp_strings[index]=p_copy->mpString;
		break;
	}	
    case ESYMBOLTYPE_LOCALSTRING:
	{
		Script::DeleteString(p_array->GetString(index));
		char **pp_strings=(char**)p_array->GetArrayPointer();
		Dbg_MsgAssert(indexGetSize(),("Bad index of %d sent to SetArrayElement, array size is %d",index,p_array->GetSize()));
		pp_strings[index]=p_copy->mpLocalString;
		break;
	}	
    case ESYMBOLTYPE_PAIR:
	{
		delete p_array->GetPair(index);
		Script::CPair **pp_pairs=(Script::CPair**)p_array->GetArrayPointer();
		Dbg_MsgAssert(indexGetSize(),("Bad index of %d sent to SetArrayElement, array size is %d",index,p_array->GetSize()));
		pp_pairs[index]=p_copy->mpPair;
		break;
	}	
    case ESYMBOLTYPE_VECTOR:
	{
		delete p_array->GetVector(index);
		Script::CVector **pp_vectors=(Script::CVector**)p_array->GetArrayPointer();
		Dbg_MsgAssert(indexGetSize(),("Bad index of %d sent to SetArrayElement, array size is %d",index,p_array->GetSize()));
		pp_vectors[index]=p_copy->mpVector;
		break;
	}	
    case ESYMBOLTYPE_STRUCTURE:
	{
		delete p_array->GetStructure(index);
		Script::CStruct **pp_structs=(Script::CStruct**)p_array->GetArrayPointer();
		Dbg_MsgAssert(indexGetSize(),("Bad index of %d sent to SetArrayElement, array size is %d",index,p_array->GetSize()));
		pp_structs[index]=p_copy->mpStructure;
		break;
	}	
    case ESYMBOLTYPE_ARRAY:
	{
		Script::CArray *p_old_array=p_array->GetArray(index);
		Script::CleanUpArray(p_old_array);
		delete p_old_array;
		Script::CArray **pp_arrays=(Script::CArray**)p_array->GetArrayPointer();
		Dbg_MsgAssert(indexGetSize(),("Bad index of %d sent to SetArrayElement, array size is %d",index,p_array->GetSize()));
		pp_arrays[index]=p_copy->mpArray;
		break;
	}	
	default:
		Dbg_MsgAssert(0,("Unsupported array type of %s in SetArrayElement",Script::GetTypeName(p_array->GetType())));
		break;		
	}
	
	// Now delete the temporary component, but set any pointer in it to NULL first since the
	// pointer has been given to the array.
	p_copy->mUnion=0;
	delete p_copy;
	
	return true;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Get2DarrayData | 
bool ScriptGet2DArrayData(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 array_name=0;
	pParams->GetChecksum(CRCD(0x8f61d602,"ArrayName"),&array_name,Script::ASSERT);
	Script::CArray *p_array1=NULL;
	if(!pScript->GetParams()->GetArray(array_name,&p_array1,Script::NO_ASSERT))
	{
		p_array1 = Script::GetArray( array_name,Script::ASSERT);
	}

	// Get the index of the element
	uint32 index1=0;
	pParams->GetInteger(CRCD(0xba453b9,"Index1"),(int*)&index1,Script::ASSERT);

	// Get the index of the element
	uint32 index2=0;
	pParams->GetInteger(CRCD(0x92ad0203,"Index2"),(int*)&index2,Script::ASSERT);


	Dbg_MsgAssert(index1GetSize(),("Bad index of %d sent to Get2DArrayData, array size is %d",index1,p_array1->GetSize()));
	Script::CArray *p_array2=p_array1->GetArray(index1);

	Dbg_MsgAssert(index2GetSize(),("Bad index of %d sent to Get2DArrayData, array size is %d",index2,p_array2->GetSize()));

	switch (p_array2->GetType())
	{
	case ESYMBOLTYPE_INTEGER:
		pScript->GetParams()->AddInteger( CRCD(0x6820459a,"val"), p_array2->GetInteger(index2) );
		break;
    case ESYMBOLTYPE_FLOAT:
		pScript->GetParams()->AddFloat( CRCD(0x6820459a,"val"), p_array2->GetFloat(index2) );
		break;
    case ESYMBOLTYPE_NAME:
		pScript->GetParams()->AddChecksum( CRCD(0x6820459a,"val"), p_array2->GetChecksum(index2) );
		break;
    case ESYMBOLTYPE_STRING:
	{
		pScript->GetParams()->AddString( CRCD(0x6820459a,"val"), p_array2->GetString(index2) );
		break;
	}	
    case ESYMBOLTYPE_LOCALSTRING:
	{
		pScript->GetParams()->AddString( CRCD(0x6820459a,"val"), p_array2->GetString(index2) );
		break;
	}	
    case ESYMBOLTYPE_PAIR:
	{	
		Script::CPair *p_pair = p_array2->GetPair(index2);
		pScript->GetParams()->AddPair( CRCD(0x6820459a,"val"), p_pair->mX, p_pair->mY );
		break;
	}	
    case ESYMBOLTYPE_VECTOR:
	{
		Script::CVector *p_vector = p_array2->GetVector(index2);
		
		pScript->GetParams()->AddVector( CRCD(0x6820459a,"val"), p_vector->mX, p_vector->mY, p_vector->mZ );
		break;
	}	
    case ESYMBOLTYPE_STRUCTURE:
	{
		pScript->GetParams()->AddStructure( CRCD(0x6820459a,"val"), p_array2->GetStructure(index2) );
		break;
	}	
    case ESYMBOLTYPE_ARRAY:
	{
		pScript->GetParams()->AddArray( CRCD(0x6820459a,"val"), p_array2->GetArray(index2) );
		break;
	}	
	default:
		Dbg_MsgAssert(0,("Unsupported array type of %s",Script::GetTypeName(p_array2->GetType())));
		break;		
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Get3DarrayData | 
bool ScriptGet3DArrayData(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 array_name=0;
	pParams->GetChecksum(CRCD(0x8f61d602,"ArrayName"),&array_name,Script::ASSERT);
	Script::CArray *p_array1=NULL;
	if(!pScript->GetParams()->GetArray(array_name,&p_array1,Script::NO_ASSERT))
	{
		p_array1 = Script::GetArray( array_name,Script::ASSERT);
	}

	// Get the index of the element
	uint32 index1=0;
	pParams->GetInteger(CRCD(0xba453b9,"Index1"),(int*)&index1,Script::ASSERT);

	// Get the index of the element
	uint32 index2=0;
	pParams->GetInteger(CRCD(0x92ad0203,"Index2"),(int*)&index2,Script::ASSERT);

	// Get the index of the element
	uint32 index3=0;
	pParams->GetInteger(CRCD(0xe5aa3295,"Index3"),(int*)&index3,Script::ASSERT);

	Dbg_MsgAssert(index1GetSize(),("Bad index of %d sent to Get3DArrayData, array size is %d",index1,p_array1->GetSize()));
	Script::CArray *p_array2=p_array1->GetArray(index1);

	Dbg_MsgAssert(index2GetSize(),("Bad index of %d sent to Get3DArrayData, array size is %d",index2,p_array2->GetSize()));
	Script::CArray *p_array3=p_array2->GetArray(index2);

	Dbg_MsgAssert(index3GetSize(),("Bad index of %d sent to Get3DArrayData, array size is %d",index3,p_array3->GetSize()));
	

	switch (p_array3->GetType())
	{
	case ESYMBOLTYPE_INTEGER:
		pScript->GetParams()->AddInteger( CRCD(0x6820459a,"val"), p_array3->GetInteger(index3) );
		break;
    case ESYMBOLTYPE_FLOAT:
		pScript->GetParams()->AddFloat(CRCD(0x6820459a,"val"), p_array3->GetFloat(index3) );
		break;
    case ESYMBOLTYPE_NAME:
		pScript->GetParams()->AddChecksum( CRCD(0x6820459a,"val"), p_array3->GetChecksum(index3) );
		break;
    case ESYMBOLTYPE_STRING:
	{
		pScript->GetParams()->AddString( CRCD(0x6820459a,"val"), p_array3->GetString(index3) );
		break;
	}	
    case ESYMBOLTYPE_LOCALSTRING:
	{
		pScript->GetParams()->AddString( CRCD(0x6820459a,"val"), p_array3->GetString(index3) );
		break;
	}	
    case ESYMBOLTYPE_PAIR:
	{	
		Script::CPair *p_pair = p_array3->GetPair(index3);
		pScript->GetParams()->AddPair( CRCD(0x6820459a,"val"), p_pair->mX, p_pair->mY );
		break;
	}	
    case ESYMBOLTYPE_VECTOR:
	{
		Script::CVector *p_vector = p_array3->GetVector(index3);
		
		pScript->GetParams()->AddVector( CRCD(0x6820459a,"val"), p_vector->mX, p_vector->mY, p_vector->mZ );
		break;
	}	
    case ESYMBOLTYPE_STRUCTURE:
	{
		pScript->GetParams()->AddStructure( CRCD(0x6820459a,"val"), p_array3->GetStructure(index3) );
		break;
	}	
    case ESYMBOLTYPE_ARRAY:
	{
		pScript->GetParams()->AddArray( CRCD(0x6820459a,"val"), p_array3->GetArray(index3) );
		break;
	}	
	default:
		Dbg_MsgAssert(0,("Unsupported array type of %s",Script::GetTypeName(p_array3->GetType())));
		break;		
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetNDarrayData | 
bool ScriptGetNDArrayData(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 array_name=0;
	pParams->GetChecksum(CRCD(0x8f61d602,"ArrayName"),&array_name,Script::ASSERT);
	Script::CArray *p_array=NULL;
	if(!pScript->GetParams()->GetArray(array_name,&p_array,Script::NO_ASSERT))
	{
		p_array = Script::GetArray( array_name,Script::ASSERT);
	}

	uint32 indices_name=CRCD(0xffc8e484,"Indices");
	//pParams->GetChecksum("Indices",&indices_name,Script::ASSERT);
	Script::CArray *p_indices=NULL;
	pScript->GetParams()->GetArray(indices_name,&p_indices,Script::ASSERT);

	uint32 i = 0;
	uint32 index=0;
	for(; i < p_indices->GetSize()-1; ++i)
	{
		// Get the index of the element
		index = p_indices->GetInteger( i );
		
		p_array = p_array->GetArray(index);
	}
	index = p_indices->GetInteger( i );
	
	switch (p_array->GetType())
	{
	case ESYMBOLTYPE_INTEGER:
		pScript->GetParams()->AddInteger( CRCD(0x6820459a,"val"), p_array->GetInteger(index) );
		break;
    case ESYMBOLTYPE_FLOAT:
		pScript->GetParams()->AddFloat( CRCD(0x6820459a,"val"), p_array->GetFloat(index) );
		break;
    case ESYMBOLTYPE_NAME:
		pScript->GetParams()->AddChecksum( CRCD(0x6820459a,"val"), p_array->GetChecksum(index) );
		break;
    case ESYMBOLTYPE_STRING:
	{
		pScript->GetParams()->AddString( CRCD(0x6820459a,"val"), p_array->GetString(index) );
		break;
	}	
    case ESYMBOLTYPE_LOCALSTRING:
	{
		pScript->GetParams()->AddString( CRCD(0x6820459a,"val"), p_array->GetString(index) );
		break;
	}	
    case ESYMBOLTYPE_PAIR:
	{	
		Script::CPair *p_pair = p_array->GetPair(index);
		pScript->GetParams()->AddPair( CRCD(0x6820459a,"val"), p_pair->mX, p_pair->mY );
		break;
	}	
    case ESYMBOLTYPE_VECTOR:
	{
		Script::CVector *p_vector = p_array->GetVector(index);
		
		pScript->GetParams()->AddVector( CRCD(0x6820459a,"val"), p_vector->mX, p_vector->mY, p_vector->mZ );
		break;
	}	
    case ESYMBOLTYPE_STRUCTURE:
	{
		pScript->GetParams()->AddStructure( CRCD(0x6820459a,"val"), p_array->GetStructure(index) );
		break;
	}	
    case ESYMBOLTYPE_ARRAY:
	{
		pScript->GetParams()->AddArray( CRCD(0x6820459a,"val"), p_array->GetArray(index) );
		break;
	}	
	default:
		Dbg_MsgAssert(0,("Unsupported array type of %s",Script::GetTypeName(p_array->GetType())));
		break;		
	}
	return true;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AddParams | appends the given structure to the list of params
// @uparmopt {} | structure to append to params
bool ScriptAddParams(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AppendStructure(pParams);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RemoveComponent | removes a component from the script's list of params
// @uparm name | name of component to remove
bool ScriptRemoveComponent(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 name;
	if ( !pParams->GetChecksum("name", &name, Script::NO_ASSERT) )
	{
		pParams->GetChecksum(NONAME, &name, Script::ASSERT);
	}

	pScript->GetParams()->RemoveComponent(name);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static uint32 s_get_bottom_up_free(bool includeFrag)
{
	Mem::Manager& mem_man = Mem::Manager::sHandle();
	Mem::Heap* p_heap = mem_man.GetHeap( CRCD(0xc7800b0,"BottomUpHeap") );
	Mem::Region* p_region = p_heap->ParentRegion();

	if (includeFrag)
	{
		return p_heap->mFreeMem.m_count + p_region->MemAvailable();
	}
	return p_region->MemAvailable();
}

// @script | SetScriptString | 
bool ScriptSetScriptString(Script::CStruct *pParams, Script::CScript *pScript)
{
	int index=0;
	pParams->GetInteger(CRCD(0x7f8c98fe,"index"),&index);
	
	const char *p_string="";
	pParams->GetString(CRCD(0x61414d56,"string"),&p_string);
	
	Script::SetScriptString(index,p_string);
	return true;
}

// @script | GetScriptString | 
bool ScriptGetScriptString(Script::CStruct *pParams, Script::CScript *pScript)
{
	int index=0;
	pParams->GetInteger(CRCD(0x7f8c98fe,"index"),&index);
	
	pScript->GetParams()->AddString(CRCD(0x61414d56,"string"),Script::GetScriptString(index));
	return true;
}
	
// @script | KenTest1 | old test of ken's
bool ScriptKenTest1(Script::CStruct *pParams, Script::CScript *pScript)
{
	static bool s_got_main_heap_free_edit=false;
	static uint32 s_main_heap_free_edit=0;
	
	if (pParams->ContainsFlag(CRCD(0x1a4e0ef9,"Clear")))
	{
		s_got_main_heap_free_edit=false;
		s_main_heap_free_edit=0;
	}	
	
	if (!s_got_main_heap_free_edit)
	{
		s_main_heap_free_edit = s_get_bottom_up_free(true);
		printf("Stored s_main_heap_free_edit\n");
		s_got_main_heap_free_edit=true;
		return true;
	}	
	
#	if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
	uint32 main_heap_free_play = s_get_bottom_up_free(false);
	
	printf("main_heap_pad = %d\n", s_main_heap_free_edit - main_heap_free_play + 200000);
#endif		// __NOPT_FINAL__
	
	return true;
}

// Used when creating default file names in the files menu.
// It appends the passed suffix, but if the result will be a string longer than MaxChars
// then it will move the suffix back so that the total number of chars is never more than
// MaxChars.
bool ScriptAppendSuffix(Script::CStruct *pParams, Script::CScript *pScript)
{
	enum
	{
		BUFFER_SIZE=100,
	};	
	char p_new_text[BUFFER_SIZE];
	
	const char *p_text="";
	pParams->GetString(CRCD(0xc4745838,"Text"), &p_text);
	int original_text_length=strlen(p_text);
	Dbg_MsgAssert(original_text_lengthGetInteger(CRCD(0xa6931595,"MaxChars"), &max_chars);
	Dbg_MsgAssert(max_charsGetInteger(CRCD(0x4a4f7821,"suffix"),&suffix);
	
	char p_suffix[20];
	sprintf(p_suffix, " %d",suffix);
	int suffix_length=strlen(p_suffix);
	Dbg_MsgAssert(suffix_length max_chars)
	{
		p_new_text[max_chars-suffix_length]=0;
	}
	strcat(p_new_text, p_suffix);
	

	uint32 new_text_name=0;
	pParams->GetChecksum(CRCD(0x50060694,"NewTextName"),&new_text_name);
	
	pScript->GetParams()->AddString(new_text_name, p_new_text);
	return true;
}

// @script | FindNearestRailPoint | 
// Used by the rail editor for snapping the cursor to the nearest rail point.
// Note that for this to work CParkGenerator::GenerateOutRailSet needs to have been called at some point first.
// If it were not called, this would only find the nearest edited-rail point.
bool ScriptFindNearestRailPoint(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mth::Vector pos;
	pParams->GetVector(CRCD(0x7f261953,"Pos"),&pos,Script::ASSERT);
	
	float min_dist=150.0f;
	pParams->GetFloat(CRCD(0x213395f1,"MinDist"),&min_dist);
	float min_dist2=min_dist*min_dist;

	uint32 ignore_rail_id=0;
	pParams->GetChecksum(CRCD(0x288a6efe,"IgnoreRailId"),&ignore_rail_id);

	int ignore_rail_point_index=-1;
	pParams->GetInteger(CRCD(0xe4d305a7,"IgnoreRailPointIndex"),&ignore_rail_point_index);


	// Find the nearest edited-rail point	
	Mth::Vector edited_rail_nearest_point;
	float edited_rail_nearest_dist2=100000000.0f;
	uint32 rail_id=0;
	int rail_point_index=0;
	Obj::GetRailEditor()->FindNearestRailPoint(pos,
											   &edited_rail_nearest_point, &edited_rail_nearest_dist2,
											   &rail_id,&rail_point_index,
											   ignore_rail_id, ignore_rail_point_index);
	
	// Then find the nearest rail point on the park editor level geometry.
	// CParkGenerator::GenerateOutRailSet needs to have been called at some point first for this to
	// find anything.
	// Not calling GenerateOutRailSet here because it may take a while to execute.
	Mth::Vector level_nearest_point;
	float level_nearest_dist2=100000000.0f;
	if (pParams->ContainsFlag(CRCD(0x9c247a94,"CheckLevelGeometry")))
	{
		Ed::CParkManager::sInstance()->GetGenerator()->FindNearestRailPoint(pos,&level_nearest_point,&level_nearest_dist2);
	}	
	
	if (edited_rail_nearest_dist2 < min_dist2 ||
		level_nearest_dist2 < min_dist2)
	{
		Mth::Vector nearest_point;
		if (edited_rail_nearest_dist2 < level_nearest_dist2)
		{
			nearest_point=edited_rail_nearest_point;
			
			pScript->GetParams()->AddChecksum(CRCD(0xa61e7cd9,"rail_id"),rail_id);
			pScript->GetParams()->AddInteger(CRCD(0xab3c14,"rail_point_index"),rail_point_index);
		}
		else
		{
			nearest_point=level_nearest_point;
		}	
		pScript->GetParams()->AddVector(CRCD(0xdf970a05,"NearestPos"),nearest_point[X],nearest_point[Y],nearest_point[Z]);
		return true;
	}
	
	return false;
}

bool ScriptGetTime(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_NGPS__
/*
	sceCdCLOCK rtc;
	sceScfGetLocalTimefromRTC(&rtc);

	pScript->GetParams()->AddInteger("Hours",rtc.hour);	
	pScript->GetParams()->AddInteger("Minutes",rtc.minute);	
	pScript->GetParams()->AddInteger("Seconds",rtc.second);	
*/	
#endif
	return true;
}

bool ScriptGetDate(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_NGPS__
/*
	sceCdCLOCK rtc;
	sceScfGetLocalTimefromRTC(&rtc);

	pScript->GetParams()->AddInteger("Day",rtc.day);	
	pScript->GetParams()->AddInteger("Month",rtc.month);	
	pScript->GetParams()->AddInteger("Year",rtc.year);	
*/	
#endif
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetObNearestScreenCoord(Script::CStruct *pParams, Script::CScript *pScript)
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptRandomize(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mth::InitialRand(Tmr::ElapsedTime(0));
	return true;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | KenTest2 | old test of Ken's
bool ScriptKenTest2(Script::CStruct *pParams, Script::CScript *pScript)
{
	//Ed::CParkEditor::Instance()->PlayModeGapManagerChecks();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static Tmr::Time sStartTime=0;
bool ScriptResetTimer(Script::CStruct *pParams, Script::CScript *pScript)
{
	sStartTime=Tmr::ElapsedTime(0);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptTimeGreaterThan(Script::CStruct *pParams, Script::CScript *pScript)
{
	float t=0.0f;
	pParams->GetFloat(NONAME,&t);
	
	if (Tmr::ElapsedTime(sStartTime)>t*1000.0f)
	{
		return true;
	}
	return false;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetStartTime(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AddInteger(CRCD(0xd16b61e6, "StartTime"), Tmr::GetTime());
	return true;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetElapsedTime(Script::CStruct *pParams, Script::CScript *pScript)
{
	int start_time;
	pParams->GetInteger(CRCD(0xd16b61e6, "StartTime"), &start_time);
	pScript->GetParams()->AddInteger(CRCD(0x3eb3566b, "ElapsedTime"), Tmr::ElapsedTime(start_time));
	return true;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RemoveParameter | Removes the parameter with the given name. If no such parameter
// exists, it will do nothing.
// @uparmopt name | The name of the parameter to be removed
bool ScriptRemoveParameter(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 name=0;
	pParams->GetChecksum(NONAME,&name);
	
	#ifdef __NOPT_ASSERT__
	const char *p_foo=NULL;
	Dbg_MsgAssert(!pParams->GetString(NONAME,&p_foo),("\n%s\nRemoveParameter requires a name, not a string",pScript->GetScriptInfo()));
	#endif
	
	CStruct *p_params=pScript->GetParams();
	Dbg_MsgAssert(p_params,("NULL p_params ??"));
	p_params->RemoveComponent(name);
	p_params->RemoveFlag(name);
	return true;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetRandomValue | Chooses a random number in a specified range. The returned value can 
// be selected to be either an integer or a floating point value.
// @parmopt name | name | 0 | The name to be given to the random value parameter. For example, 
// name=foo will make the random number be assigned to a parameter called foo.
// @parmopt float | a | 0.0 | The first value in the range.
// @parmopt float | b | 0.0 | The second value in the range. The first value does not have to be smaller than the second.
// @parmopt int | Resolution | 1000 | The number of possible floating point values that could be returned.
// For example, if the range is a=1, b=2 and the resolution is set to 3, it will return either 1, 1.5 or 2
// @flag Integer | Return an integer value in the given range, inclusive of the end values.
// For example, if the range is a=1, b=3 it will return either 1, 2 or 3. Note that a and b need to be 
// integers instead of floats in this case. 
bool ScriptGetRandomValue(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 param_name=0;
	pParams->GetChecksum(CRCD(0xa1dc81f9,"Name"),¶m_name);
		
	if (pParams->ContainsFlag(CRCD(0xa5f054f4,"Integer")))
	{
		int a=0;
		int b=0;
		pParams->GetInteger(CRCD(0x174841bc,"a"),&a);
		pParams->GetInteger(CRCD(0x8e411006,"b"),&b);
		if (a>b)
		{
			int t=a;
			a=b;
			b=t;
		}	
		pScript->GetParams()->AddInteger(param_name,a+Mth::Rnd(b-a+1));
	}
	else
	{
		int resolution=1000;
		pParams->GetInteger(CRCD(0x22cf075,"Resolution"),&resolution);
		Dbg_MsgAssert(resolution>=2,("Resolution needs to be at least 2"));

		float a=0.0f;
		float b=0.0f;
		pParams->GetFloat(CRCD(0x174841bc,"a"),&a);
		pParams->GetFloat(CRCD(0x8e411006,"b"),&b);
		
		pScript->GetParams()->AddFloat(param_name,a+Mth::Rnd(resolution)*(b-a)/(resolution-1));
	}
		
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetConfig | Allows various things in the config module to be changed at runtime.
// @parmopt name | Language | | Can be one of English, French or German
// @flag CD | Use CD for file loading
// @flag NotCD | Use host for file loading
// @flag GotExtraMemory | Switch on use of extra memory
// @flag NoExtraMemory | Switch off use of extra memory
// @flag Pal | Be in PAL mode
// @flag NTSC | Be in NTSC mode
// @parmopt int | FrameRate | | Frame rate to use, 50 or 60
bool ScriptSetConfig(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 language_checksum=0;
	pParams->GetChecksum(CRCD(0x2b248e4a,"Language"),&language_checksum);
	
	switch (language_checksum)
	{
	case 0xd37cfdff: // English
		Config::gLanguage=Config::LANGUAGE_ENGLISH;
		break;
	case 0x508a31a1: // French
		Config::gLanguage=Config::LANGUAGE_FRENCH;
		break;
	case 0x5dcbbf5e: // German
		Config::gLanguage=Config::LANGUAGE_GERMAN;
		break;
	case 0xcbe70acb: // Spanish
		Config::gLanguage=Config::LANGUAGE_FRENCH;
		break;
	case 0xa8469630: // Italian
		Config::gLanguage=Config::LANGUAGE_GERMAN;
		break;
	default:
		break;
	}
	
	if (pParams->ContainsFlag(CRCD(0xba297025,"cd")))
	{
		if (!Config::gCD)
		{
			File::InstallFileSystem();
		}
		Config::gCD=true;
	}	
	if (pParams->ContainsFlag(CRCD(0x2b6d7b32,"notcd")))
	{
		if (Config::gCD)
		{
			File::UninstallFileSystem();
		}
		Config::gCD=false;
	}	
	
	if (pParams->ContainsFlag(CRCD(0x38d23fd4,"GotExtraMemory")))
	{
		Config::gGotExtraMemory=true;
	}
	if (pParams->ContainsFlag(CRCD(0xaf11b50f,"NoExtraMemory")))		
	{
		Config::gGotExtraMemory=false;
	}
	
	if (pParams->ContainsFlag(CRCD(0x6cad3928,"Pal")))		
	{
		Config::gDisplayType=Config::DISPLAY_TYPE_PAL;
	}
	if (pParams->ContainsFlag(CRCD(0x86137a88,"NTSC")))		
	{
		Config::gDisplayType=Config::DISPLAY_TYPE_NTSC;
	}
	
	int frame_rate=60;
	if (pParams->GetInteger(CRCD(0xee2bc65f,"FrameRate"),&frame_rate))
	{
		Config::gFPS=frame_rate;
	}
	
	return true;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PrintConfig | Prints the current system configuration, whether it is CD, PAL, etc.
bool ScriptPrintConfig(Script::CStruct *pParams, Script::CScript *pScript)
{
	printf("\n");
	printf("System Configuration:\n\n");
	printf("Hardware       = %s\n",Config::GetHardwareName());
	printf("Display Type   = %s, %d fps\n",Config::GetDisplayTypeName(),Config::FPS());
	printf("Language       = %s\n",Config::GetLanguageName());
	printf("Territory      = %s\n",Config::GetTerritoryName());
	printf("CD             = %s\n",Config::CD()?"True":"False");
	printf("GotExtraMemory = %s\n",Config::GotExtraMemory()?"True":"False");
	printf("\n");
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | German | Returns true if the current language is German
bool ScriptGerman(Script::CStruct *pParams, Script::CScript *pScript)
{
	return Config::GetLanguage()==Config::LANGUAGE_GERMAN;
}

// @script | French | Returns true if the current language is French
bool ScriptFrench(Script::CStruct *pParams, Script::CScript *pScript)
{
	return Config::GetLanguage()==Config::LANGUAGE_FRENCH;
}

// @script | Spanish | Returns true if the current language is Spanish
bool ScriptSpanish(Script::CStruct *pParams, Script::CScript *pScript)
{
	return Config::GetLanguage()==Config::LANGUAGE_SPANISH;
}

// @script | Italian | Returns true if the current language is Italian
bool ScriptItalian(Script::CStruct *pParams, Script::CScript *pScript)
{
	return Config::GetLanguage()==Config::LANGUAGE_ITALIAN;
}

// @script | English | Returns true if the current language is English
bool ScriptEnglish(Script::CStruct *pParams, Script::CScript *pScript)
{
	return Config::GetLanguage()==Config::LANGUAGE_ENGLISH;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
#ifdef		__USE_PROFILER__			
static Tmr::CPUCycles sStopwatchStartTime=0;
#endif

bool ScriptResetStopwatch(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef		__USE_PROFILER__			
	sStopwatchStartTime=Tmr::GetTimeInCPUCycles();
#endif	
	return true;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptPrintStopwatchTime(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef		__USE_PROFILER__			
	Tmr::CPUCycles diff=Tmr::GetTimeInCPUCycles()-sStopwatchStartTime; 
	printf("Stopwatch time = %.3f seconds\n",(float)diff/150000000.0f);
#endif	
	return true;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CustomSkaterFilenameDefined | Doesn't appear to be supported
bool ScriptCustomSkaterFilenameDefined(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	 Mdl::Skate * pSkate =  Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile=pSkate->GetCurrentProfile();
	Dbg_MsgAssert(pSkaterProfile,("NULL pSkaterProfile"));
	
	if (strcmp(pSkaterProfile->GetCASFileName(),"Unimplemented")==0)
	{
		return false;
	}
	else
	{
		return true;
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetCustomSkaterFilename | Gets the filename for the current CAS
bool ScriptGetCustomSkaterFilename(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Mdl::Skate * pSkate =  Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile=pSkate->GetCurrentProfile();
	Dbg_MsgAssert(pSkaterProfile,("NULL pSkaterProfile"));
	
	pScript->GetParams()->AddString(CRCD(0xf36c1878,"CASFileName"),pSkaterProfile->GetCASFileName());
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetCustomSkaterFilename | Sets the filename for the current CAS
bool ScriptSetCustomSkaterFilename(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mdl::Skate * pSkate =  Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile=pSkate->GetCurrentProfile();
	Dbg_MsgAssert(pSkaterProfile,("NULL pSkaterProfile"));

	const char *p_file_name="Unimplemented";
	pParams->GetString(NONAME,&p_file_name);
	pSkaterProfile->SetCASFileName(p_file_name);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EditingPark | Returns true of we're in the park editor
bool ScriptEditingPark(Script::CStruct *pParams, Script::CScript *pScript)
{
#if 0
	Ed::ParkEditor * pParkEd =  Ed::ParkEditor::Instance();
	if (pParkEd->IsInitialized() && !pParkEd->GameGoingOrOutsideEditor())
	{
		// We're in the park editor, editing a park.
		return true;
	}
#endif
		
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#define PARK_NAME_BUF_SIZE 128 
static char spParkName[PARK_NAME_BUF_SIZE]={0};

// @script | GetParkName | Gets the current park name
bool ScriptGetParkName(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AddString(CRCD(0xcc23bddb,"ParkName"),spParkName);
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetParkName | Sets the current park name
bool ScriptSetParkName(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *p_name="";
	pParams->GetString(NONAME,&p_name);
	Dbg_MsgAssert(strlen(p_name)m_themeWasAutoSwitched;
#endif
	return false;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static bool MenuIsShown(uint32 id)
{
/*
	 Front::MenuFactory * pMenuFactory =  Front::MenuFactory::Instance();
	Front::MenuElement *pMenuElement=pMenuFactory->GetMenuElement(id,false); // false means don't assert

	if (pMenuElement && pMenuElement->m_isShown)
		return true;
	else
		return false;
*/

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MenuIsShown | Checks array of names (pArray) is shown
bool ScriptMenuIsShown(Script::CStruct *pParams, Script::CScript *pScript)
{
	// If there is an array of names, check them.		
	Script::CArray *pArray=NULL;
	if (pParams->GetArray(NONAME,&pArray))
	{
		Dbg_MsgAssert(pArray,("Eh? NULL pArray?"));
		for (uint32 i=0; iGetSize(); ++i)
		{
			if (MenuIsShown(pArray->GetNameChecksum(i)))
			{
				return true;
			}
		}
	}
	
	uint32 id=0;
	if (pParams->GetChecksum(NONAME,&id))
	{
		if (MenuIsShown(id))
		{
			return true;
		}	
	}
			
	return false;			
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static bool MenuIsSelected(uint32 id)
{
	
	/*
	 Front::MenuFactory * pMenuFactory =  Front::MenuFactory::Instance();
	Front::MenuElement *pMenuElement=pMenuFactory->GetMenuElement(id,false); // false means don't assert

	if (pMenuElement && pMenuElement->m_isSelected)
		return true;
	else
		return false;
	*/
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ForEachIn | For each member of the array, run the specified script
// @parm name | Do | The name of the script to run
// @uparm [] | The array on which to run
// @parmopt structure | Params | | any extra parameters that need to be
// merged onto each of those in the array
bool ScriptForEachIn(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 ScriptChecksum=0;
	pParams->GetChecksum(CRCD(0x62ba3f6a,"Do"),&ScriptChecksum);
	Dbg_MsgAssert(ScriptChecksum,("\n%s\nScript name missing in call to ForEachIn\n(eg, ForEachIn AnArray Do=AScript)",pScript->GetScriptInfo()));
	
	Script::CArray *pArray=NULL;
	pParams->GetArray(NONAME,&pArray);
	Dbg_MsgAssert(pArray,("\n%s\nMissing array parameter in call to ForEachIn",pScript->GetScriptInfo()));
	Dbg_MsgAssert(pArray->GetSize()==0 || pArray->GetType()==ESYMBOLTYPE_STRUCTURE,("\n%s\nForEachIn only supports arrays of structures at the moment",pScript->GetScriptInfo()));
	
	
	// Get any extra parameters that need to be merged onto each of those in the array.
	Script::CStruct *pExtraParams=NULL;
	pParams->GetStructure(CRCD(0x7031f10c,"params"),&pExtraParams);
	
	if (pExtraParams)
	{
		// Used for holding the merged parameters.
		Script::CStruct *pTotalParams=new Script::CStruct;
		
		for (uint32 i=0; iGetSize(); ++i)
		{
			pTotalParams->Clear();
			
			CStruct *p_array_struct=pArray->GetStructure(i);
			
			if (p_array_struct)
			{
				*pTotalParams+=*p_array_struct;
			}	
			
			if (pExtraParams)
			{
				*pTotalParams+=*pExtraParams;
			}	
	
			Script::RunScript(ScriptChecksum,pTotalParams,pScript->mpObject);
		}
		
		// Delete the temporary structure.
		delete pTotalParams;
	}
	else
	{
		for (uint32 i=0; iGetSize(); ++i)
		{
			Script::RunScript(ScriptChecksum,pArray->GetStructure(i),pScript->mpObject);
		}
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SizeOf | 
// Puts the size (num elements in) of the passed array into a parameter called ArraySize
// @uparm [] | The array of which we're finding the size
bool ScriptSizeOf(Script::CStruct *pParams, Script::CScript *pScript)
{
	Script::CArray *p_array=NULL;
	pParams->GetArray(NONAME,&p_array);
	
	int size=0;
	if (p_array)
	{
		size=p_array->GetSize();
	}	
	pScript->GetParams()->AddComponent(Script::GenerateCRC("ArraySize"),ESYMBOLTYPE_INTEGER,size);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetElement | Retrieves the array element pointed to by "Index"
// and places it in the param called "Element"
// @uparm [] | the array
// @parmopt int | Index | 0 | The index value of the array element
bool ScriptGetElement(Script::CStruct *pParams, Script::CScript *pScript)
{
	Script::CArray *p_array=NULL;
	pParams->GetArray(NONAME,&p_array);

	int index=0;
	pParams->GetInteger("Index",&index);

	pScript->GetParams()->RemoveComponent(0xbebfa1c6/*Element*/);	
	
	if (p_array)
	{
		if (index<0 || index>=(int)p_array->GetSize())
		{
		   #ifdef __NOPT_ASSERT__
		   printf("\n%s\nWarning ! Index %d out of range. Array has %d elements\n",pScript->GetScriptInfo(),index,p_array->GetSize());
		   #endif
		   return false;
		}
		
		switch (p_array->GetType())
		{
		case ESYMBOLTYPE_STRUCTURE:
		{
			Script::CStruct *p_new=new Script::CStruct;
			p_new->AppendStructure(p_array->GetStructure(index));
			pScript->GetParams()->AddComponent(0xbebfa1c6/*Element*/,ESYMBOLTYPE_STRUCTUREPOINTER,(int)p_new);
			break;
		}	
		case ESYMBOLTYPE_ARRAY:
			pScript->GetParams()->AddComponent(0xbebfa1c6/*Element*/,p_array->GetArray(index));
			break;
		case ESYMBOLTYPE_INTEGER:
			pScript->GetParams()->AddComponent(0xbebfa1c6/*Element*/,ESYMBOLTYPE_INTEGER,p_array->GetInt(index));
			break;
			
		default:
			printf("GetElement currently only supports arrays of structures, arrays or integers ... \n(Requested type=%d)\n",p_array->GetType());
			break;
		}		
	}
	return true;
}
		   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetNextArrayElement | Increments the value in Index and uses it to retrieve that
// element from the array, and puts it in Element
// If no Index parameter exists, it will retrieve the first element of the array, and 
// create Index and set it to zero.
// If Index goes off the end of the array, it will remove the Index parameter and
// any existing Element parameter, and return false.
// Hence calling GetNextArrayElement repeatedly will cycle through the array elements.
// @uparm [] | the array
bool ScriptGetNextArrayElement(Script::CStruct *pParams, Script::CScript *pScript)
{
	Script::CArray *p_array=NULL;
	pParams->GetArray(NONAME,&p_array);

	int index=-1;
	pScript->GetParams()->GetInteger(CRCD(0x7f8c98fe,"Index"),&index);

	pScript->GetParams()->RemoveComponent(0x7f8c98fe/*Index*/);
	pScript->GetParams()->RemoveComponent(0xbebfa1c6/*Element*/);
	
	if (!p_array)
	{
		return false;
	}
	
	int size=p_array->GetSize();
	
	if (index<0)
	{
		// No index parameter existed, so set index to the first element of the array.
		index=0;
		
		if (size==0)
		{
			// The array is empty though, so add nothing to the params.
			return false;
		}	
	}
	else
	{
		// An index existed already, so increment it.
		++index;
		if (index>=size)
		{
			// Run out of array elements!
			return false;
		}
	}

	pScript->GetParams()->AddInteger(CRCD(0x7f8c98fe,"Index"),index);

	switch (p_array->GetType())
	{
	case ESYMBOLTYPE_STRUCTURE:
		pScript->GetParams()->AddStructure(0xbebfa1c6/*Element*/,p_array->GetStructure(index));
		break;
	case ESYMBOLTYPE_ARRAY:
		pScript->GetParams()->AddArray(0xbebfa1c6/*Element*/,p_array->GetArray(index));
		break;
	case ESYMBOLTYPE_INTEGER:
		pScript->GetParams()->AddInteger(0xbebfa1c6/*Element*/,p_array->GetInteger(index));
		break;
	case ESYMBOLTYPE_FLOAT:
		pScript->GetParams()->AddFloat(0xbebfa1c6/*Element*/,p_array->GetFloat(index));
		break;
	case ESYMBOLTYPE_NAME:
		pScript->GetParams()->AddChecksum(0xbebfa1c6/*Element*/,p_array->GetChecksum(index));
		break;
	case ESYMBOLTYPE_STRING:
		pScript->GetParams()->AddString(0xbebfa1c6/*Element*/,p_array->GetString(index));
		break;
	case ESYMBOLTYPE_LOCALSTRING:
		pScript->GetParams()->AddLocalString(0xbebfa1c6/*Element*/,p_array->GetLocalString(index));
		break;
	default:
		printf("GetNextArrayElement does not support arrays of type '%s'\n",GetTypeName(p_array->GetType()));
		break;
	}		
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetRandomArrayElement | Chooses a random element of the passed array, and
// puts it in Element. It will put the index of the chosen element into a parameter called Index.
// If the array is empty, it will remove any existing Element and Index parameter and return false.
// @uparm [] | the array
bool ScriptGetRandomArrayElement(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->RemoveComponent(CRCD(0xbebfa1c6,"Element"));
	pScript->GetParams()->RemoveComponent(CRCD(0x7f8c98fe,"index"));

	Script::CArray *p_array=NULL;
	pParams->GetArray(NONAME,&p_array);

	if (!p_array)
	{
		return false;
	}
	
	int size=p_array->GetSize();
	if (size==0)
	{
		return false;
	}	

	int index=Mth::Rnd(size);
	// Wack in the index too cos it is handy to know.
	pScript->GetParams()->AddInteger(CRCD(0x7f8c98fe,"index"),index);	
		
	switch (p_array->GetType())
	{
	case ESYMBOLTYPE_STRUCTURE:
		pScript->GetParams()->AddStructure(CRCD(0xbebfa1c6,"Element"),p_array->GetStructure(index));
		break;
	case ESYMBOLTYPE_ARRAY:
		pScript->GetParams()->AddArray(CRCD(0xbebfa1c6,"Element"),p_array->GetArray(index));
		break;
	case ESYMBOLTYPE_INTEGER:
		pScript->GetParams()->AddInteger(CRCD(0xbebfa1c6,"Element"),p_array->GetInteger(index));
		break;
	case ESYMBOLTYPE_FLOAT:
		pScript->GetParams()->AddFloat(CRCD(0xbebfa1c6,"Element"),p_array->GetFloat(index));
		break;
	case ESYMBOLTYPE_NAME:
		pScript->GetParams()->AddChecksum(CRCD(0xbebfa1c6,"Element"),p_array->GetChecksum(index));
		break;
	default:
		printf("GetRandomArrayElement does not support arrays of type '%s'\n",GetTypeName(p_array->GetType()));
		break;
	}		
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PermuteArray | Creates a new array equal to the passed array with its elements
// randomly permuted. The new array is put into a parameter named using the passed NewArrayName.
// @parm array | Array | The source array
// @parm name | NewArrayName | The name to be given to the new array parameter.
// @flag MakeNewFirstDifferFromOldLast | Makes the first element of the new array definitely
// be different from the old last element. This is so that the old permutation can be
// concatenated with the new and no consecutive elements will be the same.
// Could be useful for playing songs in a random order with no repeats.
// @flag PermuteSource | If this flag is specified then it will be the source array itself
// that will get permuted, no new array will get created.
// So the NewArrayName parameter is not required in this case, it will be ignored.
bool ScriptPermuteArray(Script::CStruct *pParams, Script::CScript *pScript)
{
	Script::CArray *p_source_array=NULL;
	pParams->GetArray("Array",&p_source_array);
	
	if (!p_source_array)
	{
		#ifdef __NOPT_ASSERT__
		printf("\n%s\nPermuteArray requires an Array parameter\n",pScript->GetScriptInfo());
		#endif
		return false;
	}

	CArray *p_array_to_be_permuted=p_source_array;
	bool create_new_array=!pParams->ContainsFlag("PermuteSource");
	
	uint32 new_array_name=0;
	CArray *p_new_array=NULL;
	
	if (create_new_array)
	{
		pParams->GetChecksum("NewArrayName",&new_array_name);
		if (!new_array_name)
		{
			#ifdef __NOPT_ASSERT__
			printf("\n%s\nPermuteArray requires a NewArrayName parameter\n",pScript->GetScriptInfo());
			#endif
			return false;
		}	
		
		p_new_array=new CArray;
		CopyArray(p_new_array,p_source_array);
		p_array_to_be_permuted=p_new_array;
	}	
	
	
	
	int size=p_array_to_be_permuted->GetSize();
	if (size)
	{
		uint32 *p_array_data=p_array_to_be_permuted->GetArrayPointer();
		Dbg_MsgAssert(p_array_data,("NULL p_array_data ?"));
		
		int num_swaps=size*10;
		
		uint32 old_last=p_array_data[size-1];
		
		for (int i=0; iContainsFlag("MakeNewFirstDifferFromOldLast") && size>1 && p_array_data[0]==old_last)
		{
			int a=0;
			int b=1+Mth::Rnd(size-1);
			
			uint32 temp=p_array_data[a];
			p_array_data[a]=p_array_data[b];
			p_array_data[b]=temp;
		}
	}
	
	if (create_new_array)
	{
		pScript->GetParams()->AddArrayPointer(new_array_name,p_new_array);
	}
		
	return true;
}
	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetGammaValues( Script::CStruct *pParams, Script::CScript *pScript )
{
#	if defined( __PLAT_XBOX__ )
	 Gfx::Manager * gfx_man =  Gfx::Manager::Instance();
	float gamma_r;
	float gamma_g;
	float gamma_b;
	gfx_man->GetGammaNormalized( &gamma_r, &gamma_g, &gamma_b );

	pScript->GetParams()->RemoveComponent( "red" );
	pScript->GetParams()->RemoveComponent( "green" );
	pScript->GetParams()->RemoveComponent( "blue" );

	pScript->GetParams()->AddInteger( "red", ( (int)( gamma_r * 100.0f ) ));
	pScript->GetParams()->AddInteger( "green", ( (int)( gamma_g * 100.0f ) ));
	pScript->GetParams()->AddInteger( "blue", ( (int)( gamma_b * 100.0f ) ));
#	endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ApplyChangeGamma | This gets called whenever one of 
// the color values in the gamma menu changes.
// Called from script in gamemenu.q
bool ScriptApplyChangeGamma( Script::CStruct *pParams, Script::CScript *pScript )
{
	
#	if defined( __PLAT_XBOX__ )	
	// get the current values
	 Gfx::Manager * gfx_man =  Gfx::Manager::Instance();
	float gamma_r;
	float gamma_g;
	float gamma_b;
	gfx_man->GetGammaNormalized( &gamma_r, &gamma_g, &gamma_b );

	uint32 color;
	pParams->GetChecksum( "color", &color, Script::ASSERT );

	float change;
	pParams->GetFloat( "change", &change, Script::ASSERT );
	switch ( color )
	{
	case CRCC( 0x59ea070, "red" ):
		if (( change > 0.0f && gamma_r < 1.0f ) || ( change < 0.0f && gamma_r > 0.0f ))
			gamma_r += change;
		break;
	case CRCC( 0x2f6511de, "green" ):
		if (( change > 0.0f && gamma_g < 1.0f ) || ( change < 0.0f && gamma_g > 0.0f ))
			gamma_g += change;
		break;
	case CRCC( 0x61c9354b, "blue" ):
		if (( change > 0.0f && gamma_b < 1.0f ) || ( change < 0.0f && gamma_b > 0.0f ))
			gamma_b += change;
		break;
	default:
		Dbg_MsgAssert( 0, ("ApplyChangeGamma called on unknown color") );
	}
	
	gfx_man->SetGammaNormalized( gamma_r, gamma_g, gamma_b );
	
/*	// grab any new values from the params
	int r, g, b;
	if ( pParams->GetInteger( "red", &r, Script::NO_ASSERT ) )
		gamma_r = (float)(r) / 100.0f;
	if ( pParams->GetInteger( "green", &g, Script::NO_ASSERT ) )
		gamma_g = (float)(g) / 100.0f;
	if ( pParams->GetInteger( "blue", &b, Script::NO_ASSERT ) )
		gamma_b = (float)(b) / 100.0f;
*/

#	endif
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GotParam | Checks if the script was passed the specified parameter
// @uparm name | The name of the param to check for
bool ScriptGotParam(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	uint32 FlagChecksum=0;
	pParams->GetChecksum(NONAME,&FlagChecksum);
	Dbg_MsgAssert(FlagChecksum,("GotParam command requires a flag name"));
	
	// Get the script's parameters, and see if they contain the aforementioned flag therein.
	Script::CStruct *pScriptParams=pScript->GetParams();
	Dbg_MsgAssert(pScriptParams,("NULL pScriptParams"));
	return pScriptParams->ContainsComponentNamed(FlagChecksum);
}

Tmr::Time gButtonDebounceTime[PAD_NUMBUTTONS];  // PAD_NUMBUTTONS defined in skater.h for some reason (instead of some controller header)

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ControllerDeboune |
// For example: 
// Debounce X time = 2.0 
// This will ignore the X button until either the time has elapsed,
// or the X button has been released and subsequently re-pressed. 
// This prevents the press of X from carrying over from one screen to 
// the next, and removes the need to have a delay, whilst still allowing 
// the user to actively X past things. 
// @uparm name | the button
// @parmopt float | time | 1.0 | 
bool ScriptControllerDebounce( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	uint32 Checksum = 0;
	float time=1.0f;
	pParams->GetFloat(0x906b67ba,&time);	   // "time"
	if ( pParams->GetChecksum( NONAME, &Checksum ) )
	{
		int Button = Inp::GetButtonIndex(Checksum);	
		gButtonDebounceTime[Button] = (Tmr::Time)(Tmr::GetTime() + (time * 1000));
	}
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void ResetDebounce( void )
{
	
	int i;
	for ( i = 0; i < PAD_NUMBUTTONS; i++ )
	{
		gButtonDebounceTime[ i ] = 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CheckButton(Inp::Handler< Mdl::FrontEnd >* pHandler, uint32 button)
{
	

	if (!pHandler)
	{
		return false;
	}	
	
	if (!pHandler->m_Input)
	{
		return false;
	}	
	
	int whichButton = Inp::GetButtonIndex( button );
	if ( gButtonDebounceTime[ whichButton ] > Tmr::GetTime( ) )
	{
		return ( false );
	}
	switch ( button )
	{
		case ( 0xbc6b118f ): // Up
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_UP] )
			{
				return ( true );
			}
			break;
		case ( 0xe3006fc4 ): // Down
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_DOWN] )
			{
				return ( true );
			}
			break;
		case ( 0x85981897 ): // Left
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_LEFT] )
			{
				return ( true );
			}
			break;
		case ( 0x4b358aeb ): // Right
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_RIGHT] )
			{
				return ( true );
			}
			break;
		case ( 0xb7231a95 ): // UpLeft
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_UP] )
			{
				if ( pHandler->m_Input->m_Event[Inp::Data::vA_LEFT] )
				{
					return ( true );
				}
			}
			break;
		case ( 0xa50950c5 ): // UpRight
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_UP] )
			{
				if ( pHandler->m_Input->m_Event[Inp::Data::vA_RIGHT] )
				{
					return ( true );
				}
			}
			break;
		case ( 0xd8847efa ): // DownLeft
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_DOWN] )
			{
				if ( pHandler->m_Input->m_Event[Inp::Data::vA_LEFT] )
				{
					return ( true );
				}
			}
			break;
		case ( 0x786b8b68 ): // DownRight
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_DOWN] )
			{
				if ( pHandler->m_Input->m_Event[Inp::Data::vA_RIGHT] )
				{
					return ( true );
				}
			}
			break;
		case ( 0x2b489a86 ): // Circle
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_CIRCLE] )
			{
				return ( true );
			}
			break;
		case ( 0x321c9756 ): // Square
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_SQUARE] )
			{
				return ( true );
			}
			break;
		case ( 0x7323e97c ): // X
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_X] )
			{
				return ( true );
			}
			break;
		case ( 0x20689278 ): // Triangle
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_TRIANGLE] )
			{
				return ( true );
			}
			break;
		case ( 0x26b0c991 ): // L1
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_L1] )
			{
				return ( true );
			}
			break;
		case ( 0xbfb9982b ): // L2
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_L2] )
			{
				return ( true );
			}
			break;
		case ( 0xf2f1f64e ): // R1
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_R1] )
			{
				return ( true );
			}
			break;
		case ( 0x6bf8a7f4 ): // R2
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_R2] )
			{
				return ( true );
			}
			break;
        case ( 0x767a45d7 ): // black
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_BLACK] )
			{
				return ( true );
			}
			break;
        case ( 0xbd30325b ): // white
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_WHITE] )
			{
				return ( true );
			}
			break;
        case ( 0x9d2d8850 ): // Z
			if ( pHandler->m_Input->m_Event[Inp::Data::vA_Z] )
			{
				return ( true );
			}
			break;

		default:
			break;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ControllerPressed | returns true if the specified button is pressed
// @uparm name | the button to check
// @uparmopt 0 | controller index
bool ScriptControllerPressed(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mdl::FrontEnd* pFront = Mdl::FrontEnd::Instance();

	int controller;
	uint32 button = 0;
	if ( pParams->GetChecksum( NONAME, &button ) )
	{
		if ( pParams->GetInteger( NONAME, &controller, Script::NO_ASSERT ) )
		{
			Dbg_MsgAssert( controller < SIO::vMAX_DEVICES && controller >= 0, ( "ControllerPressed called with bad controller index" ) );
			if ( CheckButton( pFront->GetInputHandler( controller ), button ) )
				return true;
			else
				return false;
		}
		else
		{
			for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
			{
				if ( CheckButton( pFront->GetInputHandler( i ), button ) )
					return true;
			}
			return false;
		}
	}
	return false;
}

// @script | GetAnalogueInfo | Reads the analogue sticks, ans returns the floats LeftX, LeftY, RightX and RightY.
// These each range from -1 to 1.
// @parmopt int | Controller | 0 | Controller index
bool ScriptGetAnalogueInfo(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mdl::FrontEnd* pFront = Mdl::FrontEnd::Instance();

	int controller=0;
	pParams->GetInteger( CRCD(0xb30d9965,"Controller"), &controller, Script::NO_ASSERT );
	
	Dbg_MsgAssert( controller < SIO::vMAX_DEVICES && controller >= 0, ( "ControllerPressed called with bad controller index" ) );
	Inp::Handler< Mdl::FrontEnd > *p_handler=pFront->GetInputHandler( controller );
	CControlPad pad;
	pad.Update(p_handler->m_Input);
	
	pScript->GetParams()->AddFloat(CRCD(0x303067f1,"LeftX"),pad.m_scaled_leftX);
	pScript->GetParams()->AddFloat(CRCD(0x47375767,"LeftY"),pad.m_scaled_leftY);
	pScript->GetParams()->AddFloat(CRCD(0x694df774,"RightX"),pad.m_scaled_rightX);
	pScript->GetParams()->AddFloat(CRCD(0x1e4ac7e2,"RightY"),pad.m_scaled_rightY);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSfxVolume | SetSfxVolume [100] 
// Param: volumeLevel 
// range: 0 to 100 (the main mixing volume, affecting all soundfx) 
// default: 100  
// Notes: the volume is set to 100 on startup, and can only be modified with this command
// @uparmopt 100.0 | volume
bool ScriptSetVolume( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	float Volume = 100.0f;
    
	pParams->GetFloat( NONAME, &Volume );

	 Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
	sfx_manager->SetMainVolume( Volume );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetMusicVolume | 
// @uparmopt 100.0 | volume level
bool ScriptSetMusicVolume( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	float Volume = 100.0f;
    
	pParams->GetFloat( NONAME, &Volume );

	Pcm::SetVolume( Volume );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetMusicStreamVolume | 
// @uparmopt 100.0 | volume level
bool ScriptSetMusicStreamVolume( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	float Volume = 100.0f;
    
	pParams->GetFloat( NONAME, &Volume );

	Pcm::SetMusicStreamVolume( Volume );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StopMusic | 
bool ScriptStopMusic( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	Pcm::StopMusic( );
	return ( true );
}

#define CHECKSUM_FADE	0xb5aa125f // fade

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PauseMusic | 
bool ScriptPauseMusic( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	int pausePlease = 1;
	pParams->GetInteger( NONAME, &pausePlease );
	Pcm::PauseMusic( pausePlease );
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PauseStream | 
// @uparm 1 | 0 to unpause, 1 to pause
bool ScriptPauseStream( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	int pausePlease = 1;
	pParams->GetInteger( NONAME, &pausePlease );
	Pcm::PauseStream( pausePlease );
	return ( true );
}
		   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadMusicHeader | parameter is name of header/wad file (minus extension) 
// Do this once on startup. 
// Example:  LoadMusicHeader "music\music"
// @uparm "string" | name of header/wad file
bool ScriptLoadMusicHeader( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	const char *pName = NULL;
	pParams->GetText( NONAME, &pName );
	if ( pName )
	{
		// Mick:  Moved to BottomUpHeap, as it is a lot bigger now, and only loaded at startup
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
		bool done =  ( Pcm::LoadMusicHeader( pName ) );
		Mem::Manager::sHandle().PopContext();
		return done;
		
	}
	return ( false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadStreamHeader | 
// @uparm "string" | name of header/wad file (minus extension)
bool ScriptLoadStreamHeader( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	const char *pName = NULL;
	pParams->GetText( NONAME, &pName );
	if ( pName )
	{
		// Mick:  Moved to BottomUpHeap, as it is a lot bigger now, and only loaded at startup
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
		bool done = ( Pcm::LoadStreamHeader( pName ) );
		Mem::Manager::sHandle().PopContext();
		return done;
	}
	return ( false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StreamIsAvailable | only available in the skateshop
// @parm int | | which stream 
bool ScriptStreamIsAvailable( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	if ( !skate_mod->GetGameMode()->IsFrontEnd() )
	{
		Dbg_MsgAssert( 0, ( "StreamIsAvailable script function is only valid in the skateshop." ) );
	}
	int whichStream;
	if ( pParams->GetInteger( NONAME, &whichStream ) )
	{
		if ( whichStream >= Pcm::GetNumStreamsAvailable( ) )
		{
			Dbg_MsgAssert( 0, ( "\n%s\nAsking for StreamIsAvailable on stream %d, past valid range ( 0 to %d ).", pScript->GetScriptInfo( ),  whichStream, Pcm::GetNumStreamsAvailable( ) - 1 ) );
			return ( false );
		}
		return ( Pcm::StreamAvailable( whichStream ) );
	}
	else
	{
		return ( Pcm::StreamAvailable( ) );
	}
}

#define CHECKSUM_FLAG_PERM	0x389129e4	// "FLAG_PERM"

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AddMusicTrack | adds track to list - plays randomly during game 
// Note: The songs that go into the data/music folder (and are added to the
// CD in them 'music' directory) as well as the ambient tracks in data/ambient
// (and in 'ambient' directory on CD) should have a filename of 8 characters
// or less, as otherwise the names get converted upon building a CD
// @uparmopt "string" | filename in quotes ( .wav file in data/music ) 
// @uparmopt name | filename 
// @flag flag_perm | to add the song to the permanent soundtrack list on startup...
// otherwise track is a level-specific ambient type. 
bool ScriptAddMusicTrack( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	const char *pTrackName = NULL;
	const char *pTrackTitle = NULL;
	// default to ambient tracks...
	pParams->GetText( NONAME, &pTrackName );
	pParams->GetText( "TrackTitle", &pTrackTitle );
	int whichList = Pcm::TRACKLIST_LEVEL_SPECIFIC;
	if ( pParams->ContainsFlag( CHECKSUM_FLAG_PERM ) )
	{
		whichList = Pcm::TRACKLIST_PERM;
	}
	
	if ( pTrackName )
	{
		Pcm::AddTrackToPlaylist( pTrackName, whichList, pTrackTitle );
	}
	return ( true );
}


// @script | ChangeTrackState | Disables or enables one of the permanent music tracks.
// @uparmopt 0 | The index of the track
// @flag Off | Disable the specified track. If omitted, it will enable the track.
bool ScriptChangeTrackState(Script::CStruct *pParams, Script::CScript *pScript)
{
	int track_num=0;
	pParams->GetInteger(NONAME,&track_num);
	
	Pcm::SetTrackForbiddenStatus(track_num,pParams->ContainsFlag("Off"),Pcm::TRACKLIST_PERM);
	return true;
}
	
// @script | TrackEnabled | Returns true if the specified track in the permanent track list is enabled.
// @uparmopt 0 | The index of the track
bool ScriptTrackEnabled(Script::CStruct *pParams, Script::CScript *pScript)
{
	int track_num=0;
	pParams->GetInteger(NONAME,&track_num);

	if (Pcm::GetNumTracks(Pcm::TRACKLIST_PERM) == 0)
	{
		return false;
	}	
	
	Dbg_MsgAssert( track_num>=0 && track_num < Pcm::GetNumTracks(Pcm::TRACKLIST_PERM),( "\n%s\nBad track number of %d sent to TrackEnabled, num tracks = %d", pScript->GetScriptInfo(), track_num, Pcm::GetNumTracks(Pcm::TRACKLIST_PERM) ));
	
	uint64 list1,list2;
    Pcm::GetPlaylist(&list1, &list2);
    
    if (track_num<64)
    {
        if (list1 & (((uint64)1) << track_num))
        {
            return false;
        }
    }        
    else
    {
        if (list2 & (((uint64)1) << (track_num-64)))
        {
            return false;
        }
    }
	
	return true;	
}

bool ScriptMusicIsPaused(Script::CStruct *pParams, Script::CScript *pScript)
{
	return Pcm::MusicIsPaused();
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ClearMusicTrackList | 
// @flag FLAG_PERM | optionally add FLAG_PERM to clear 
// the permanent list, otherwise clear level specific list
bool ScriptClearMusicTrackList( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	int whichList = Pcm::TRACKLIST_LEVEL_SPECIFIC;
	if ( pParams->ContainsFlag( CHECKSUM_FLAG_PERM ) )
	{
		whichList = Pcm::TRACKLIST_PERM;
	}
	Pcm::ClearPlaylist( whichList );
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SkipMusicTrack | 
// Play next track in the list ( or if in random mode, pick another random track )
bool ScriptSkipMusicTrack( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	Pcm::SkipMusicTrack( );
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetMusicMode |
// Play music or ambient sounds?
// @uparm 1 | 1 for music on, 0 for music off/ ambience on
bool ScriptSetMusicMode( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	int musicOn = 1;  // 1 for music on, 0 for music off/ambience on
	pParams->GetInteger( NONAME, &musicOn );
	Pcm::SetActiveTrackList( musicOn ? Pcm::TRACKLIST_PERM : Pcm::TRACKLIST_LEVEL_SPECIFIC );
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetRandomMode | 
// Play tracks in Random mode or play tracks in order.
bool ScriptSetRandomMode( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	int randomModeOn = 1;
	pParams->GetInteger( NONAME, &randomModeOn );
	Pcm::SetRandomMode( randomModeOn );
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetMusicLooping | 
// Loops playing track.
// @uparm 1 | 1 enables looping, 0 disables looping
bool ScriptSetMusicLooping( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	int loopingModeOn = 1;
	pParams->GetInteger( NONAME, &loopingModeOn );
	Pcm::SetLoopingMode( loopingModeOn );
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetCurrentTrack | 
// Gets the index of the currently playing music track.
bool ScriptGetCurrentTrack( Script::CStruct *pParams, Script::CScript *pScript )
{
    int track = Pcm::GetCurrentTrack();
    pScript->GetParams()->AddInteger( "current_track", track );
    return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PlayMovie | Play a movie from data\movies on PC, or \movies on CD
// @uparm "string" | movie name
bool ScriptPlayMovie( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	const char *pMovieName;


	if ( pParams->GetText( NONAME, &pMovieName, true ) )
	{
		Flx::PlayMovie( pMovieName );
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnloadAnim | 
// @parm name | descChecksum | asset name
// @parmopt name | refChecksum | 0 | skeleton name
bool ScriptUnloadAnim( Script::CStruct *pParams, Script::CScript *pScript )
{
	// get the assman
	Ass::CAssMan * ass_man = Ass::CAssMan::Instance();

	const char* pAnimName = NULL;
	pParams->GetText( "name", &pAnimName, Script::ASSERT );

	Ass::CAsset* pAsset = ass_man->GetAssetNode( Script::GenerateCRC(pAnimName), false );

	if ( pAsset )
	{
		ass_man->DestroyReferences( pAsset );
		ass_man->UnloadAsset( pAsset );
		
//		Dbg_Message( "Unloaded asset %s", 
//					 pAnimName );
	}
	else
	{
//		Dbg_Message( "Couldn't find asset %s to unload", 
//					 pAnimName );
	}


#if 0
	// can't do it this way, because it 
	// deletes the reference,
	// not the actual asset itself...

	uint32 animName = 0;
	uint32 refChecksum = 0;

	pParams->GetChecksum( "descChecksum", &animName, Script::ASSERT );

	//printf("Called UnloadAnim on %s\n",Script::FindChecksumName(animName));
	
	if ( !pParams->GetChecksum( "refChecksum", &refChecksum, Script::NO_ASSERT ) )
	{
		refChecksum = ass_man->GetReferenceChecksum();
	}

	// make sure the reference checksum is not 0,
	// if a reference checksum was not specified	
	Dbg_MsgAssert( refChecksum != 0, ( "No ref checksum" ) );

	// kills the original asset
	Ass::CAsset* pAsset = ass_man->GetAssetNode( refChecksum + animName, false );
	if ( pAsset )
	{
		ass_man->DestroyReferences( pAsset );
		ass_man->UnloadAsset( pAsset );
#ifdef __NOPT_ASSERT__
		Dbg_Message( "Unloaded asset %s %s", 
					 Script::FindChecksumName( refChecksum ),
					 Script::FindChecksumName( animName ) );
#endif
	}
	else
	{
#ifdef __NOPT_ASSERT__
		Dbg_Message( "Couldn't find asset %s %s to unload", 
					 Script::FindChecksumName( refChecksum ),
					 Script::FindChecksumName( animName ) );
#endif
	}
#endif

   return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadAsset | 
// @parm string | name | asset name
bool ScriptLoadAsset( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Ass::CAssMan * ass_man =  Ass::CAssMan::Instance();

	const char* p_asset_filename;
	if ( pParams->GetText( (const char*)"name", &p_asset_filename, false )
		 || pParams->GetText( NONAME, &p_asset_filename, true ) )
	{
		// either (name="XXX") or ("XXX") is required
	}

#ifdef __USER_GARY__
//	Dbg_Message( "*** Loading asset %s, perm=%d\n", p_asset_filename, ass_man->GetDefaultPermanent() );
#endif

//	if( !ass_man->LoadOrGetAsset( p_asset_filename, false, ass_man->GetDefaultPermanent() ) )
	if( !ass_man->LoadOrGetAsset( p_asset_filename, false, false, ass_man->GetDefaultPermanent(), 0, NULL, pParams ) )
	{
		if ( Script::GetInteger( CRCD(0x25dc7904,"AssertOnMissingAssets") ) )
		{
			Dbg_MsgAssert( false,( "Could not load asset %s", p_asset_filename ));
		}
		else
		{
			return true;
		}
	}																			 

	uint32 descChecksum = 0;
	if ( pParams->GetChecksum( (const char*)"desc", &descChecksum, false )
		 || pParams->GetChecksum( NONAME, &descChecksum, false ) )
	{
		// either (desc=nnn) or (nnn) is required
		ass_man->AddRef(p_asset_filename, descChecksum, 0);	 
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadAnim | loads the specified animation
// @parm string | name | the file name of the animation
// @parm string | desc | description
// @flag async | load animation asynchronously
// returns true if a reference is generated.  false if a reference already exists
bool ScriptLoadAnim( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Ass::CAssMan * ass_man =  Ass::CAssMan::Instance();

	// an anim is just like an asset, except it has a reference added to it
		
	const char* p_asset_filename;
	uint32 descChecksum = 0;
	pParams->GetText( CRCD(0xa1dc81f9,"name"), &p_asset_filename );

	pParams->GetChecksum( CRCD(0x4d20d794,"descChecksum"), &descChecksum, true );

	bool async = false;
	if( pParams->ContainsFlag( CRCD(0x90e07c79,"async") ))
	{
		async = true;
	}

	bool use_pip = false;
	if( pParams->ContainsFlag( CRCD(0x23028e29,"use_pip") ))
	{
		use_pip = true;
	}

	return ass_man->LoadAnim( p_asset_filename, descChecksum, ass_man->GetReferenceChecksum(), async, use_pip );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadSkeleton | loads the specified skeleton
// @parm string | name | the file name of the skeleton
// @parm string | desc | description
bool ScriptLoadSkeleton( Script::CStruct *pParams, Script::CScript *pScript )
{
	Ass::CAssMan* ass_man =  Ass::CAssMan::Instance();

	const char* nameBuf;
	pParams->GetText( (const char*)"name", &nameBuf, Script::ASSERT );

	uint32 skeletonName;
	pParams->GetChecksum( NONAME, &skeletonName, Script::ASSERT );
	
//    Gfx::CSkeletonData* pSkeletonData = (Gfx::CSkeletonData*)ass_man->LoadOrGetAsset(nameBuf, false, ass_man->GetDefaultPermanent());
    Gfx::CSkeletonData* pSkeletonData = (Gfx::CSkeletonData*)ass_man->LoadOrGetAsset(nameBuf, false, false, ass_man->GetDefaultPermanent(), 0);
	if ( !pSkeletonData )
	{
		Dbg_MsgAssert(0,("Failed to load skeleton %s",nameBuf));
	}

	// Now add the reference to it, if one was requested
	// this gets combined with the "reference checksum"
	if (!ass_man->AssetLoaded(skeletonName))
	{
		ass_man->AddRef(nameBuf, skeletonName, 0);	 
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptAssManSetReferenceChecksum( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Ass::CAssMan * ass_man =  Ass::CAssMan::Instance();

	uint32 checksum;
	if ( !pParams->GetChecksum( NONAME, &checksum ) )
	{
		pParams->GetInteger( NONAME, (int*)&checksum, true );
	}

	ass_man->SetReferenceChecksum( checksum );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptAssManSetDefaultPermanent( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Ass::CAssMan * ass_man =  Ass::CAssMan::Instance();

	int permanent;
	pParams->GetInteger( NONAME, &permanent, true );

	ass_man->SetDefaultPermanent( permanent );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#define CHECKSUM_PITCH		0xd8604126	// "pitch"
#define CHECKSUM_DROPOFF	0xff2020ec	// "dropoff"
#define CHECKSUM_VOLUME		0xf6a36814	// "vol"

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadSound | 
// @uparm "string" | The name of the sound file
// @parmopt float | pitch | 100.0 | 
// @parmopt float | volume | 100.0 | 
// @parmopt float | dropoff | 0.0 | 
// @flag PosUpdateWithDoppler | positional update with doppler
// @flag NoReverb | reverb effect disabled for this sound
bool ScriptLoadSound( Script::CStruct *pParams, Script::CScript *pScript )
{
	const char *pSfxName;
	if ( pParams->GetText( NONAME, &pSfxName, true ) )
	{
		float volume = 100.0f;
		float pitch = 100.0f;
		float dropoff = 0.0f;
		int flags = 0;
		pParams->GetFloat( CHECKSUM_PITCH, &pitch );
		if ( pParams->GetFloat( CHECKSUM_DROPOFF, &dropoff ) )
		{
			dropoff = FEET_TO_INCHES( dropoff );
		}
		pParams->GetFloat( CHECKSUM_VOLUME, &volume );
		
		if ( pParams->ContainsFlag( CHECKSUM_FLAG_PERM ) )
		{
			flags |= SFX_FLAG_LOAD_PERM;
		}
		if ( pParams->ContainsFlag( 0xcf15c120 ) ) //"PosUpdateWithDoppler"
		{
			flags |= SFX_FLAG_POSITIONAL_UPDATE_WITH_DOPPLER;
		}
		else if ( pParams->ContainsFlag( CRCD(0x8b87176f,"NoReverb") ) )
		{
			flags |= SFX_FLAG_NO_REVERB;
		}
		//else if ( pParams->ContainsFlag( 0x14f9c4fe ) ) // "PosUpdate"
		//{
		//	flags |= SFX_FLAG_POSITIONAL_UPDATE;
		//}
			
		 Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
		if ( !sfx_manager->LoadSound( pSfxName, flags, dropoff, pitch, volume ) )
		{
//			printf( "sound %s couldn't load\n", pSfxName );
		}
	}
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PlayStream | 
// @uparm name | stream name
// @parmopt float | volume | 100.0 | volume of both left and right channel in percent (overrides volL and volR)
// @parmopt float | volL | 100.0 | volume of left channel
// @parmopt float | volR | 100.0 | volume of right channel
// @parmopt float | pitch | 100.0 | pitch value
// @parmopt int | priority | 50 | priority; higher priority streams can stop lower priority streams.
// @parmopt name | id | 0 | control ID (used instead of stream name for script control purposes)
// @parmopt int | lipsync | 0 | set to 1 to load lipsync data
bool ScriptPlayStream( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	uint32 streamNameChecksum = 0;
	if ( pParams->GetChecksum( NONAME, &streamNameChecksum ) )
	{
		float volume = 100.0f;
		float volL = 100.0f;
		float volR = 100.0f;
		float pitch = 100.0f;
		int priority = STREAM_DEFAULT_PRIORITY;
		uint32 id = 0;
		int lipsync = 0;
		pParams->GetFloat( "volL", &volL );
		pParams->GetFloat( "volR", &volR );
		if (pParams->GetFloat( CHECKSUM_VOLUME, &volume ))
		{
			volL = volume;
			volR = volume;
		}
		pParams->GetFloat( CHECKSUM_PITCH, &pitch );
		pParams->GetInteger( 0x9d5923d8, &priority ); // priority
		pParams->GetChecksum( 0x40c698af, &id ); // id
		pParams->GetInteger( CRCD(0xf5c3922b,"lipsync") , &lipsync );

		if (lipsync == 1)
		{
			bool loaded = Pcm::CStreamFrameAmpManager::sLoadFrameAmp(streamNameChecksum);
			if (!loaded)
			{
				Dbg_MsgAssert( 0,( "Couldn't load lipsync data for stream %s", Script::FindChecksumName(streamNameChecksum)));
				return false;
			}
		}

		Sfx::sVolume	vol( Sfx::VOLUME_TYPE_BASIC_2_CHANNEL );
		vol.SetChannelVolume( 0, volL );
		vol.SetChannelVolume( 1, volR );
		Pcm::PlayStream( streamNameChecksum, &vol, pitch, priority, id );

		return ( true );
	}
	Dbg_MsgAssert( 0,( "\n%s\nMust specify stream name.", pScript->GetScriptInfo( ) ));
	return ( false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StopStream | stops current stream from playing
// @uparm name | stream name (or control ID, if one was supplied with PlayStream)
bool ScriptStopStream( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	uint32 id;
	if (pParams->GetChecksum( NONAME, &id ))
	{
		Pcm::StopStreamFromID( id );
	}
	else
	{
		Pcm::StopStreams( -1 );		// Stop all streams
	}

	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsStreamPlaying | Returns true if the named stream is currently playing
// @uparm name | stream name (or control ID, if one was supplied with PlayStream)
bool ScriptIsStreamPlaying( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 id = 0;
    
	if (pParams->GetChecksum( NONAME, &id ))
	{
		return Pcm::StreamPlaying(id);
	}
	else
	{
		Dbg_MsgAssert(0, ("No stream specified"));
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetStreamParams | Set the volume or pitch of a playing stream
// @uparm name | stream name (or control ID, if one was supplied with PlayStream)
// @parmopt float | vol | 100.0 | volume of both left and right channel (overrides volL and volR)
// @parmopt float | volL | 100.0 | volume of left channel
// @parmopt float | volR | 100.0 | volume of right channel
// @parmopt float | pitch | 100.0 | pitch
bool ScriptSetStreamParams( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 id = 0;
    
	if (pParams->GetChecksum( NONAME, &id ))
	{
		float Volume;
		float VolL;
		float VolR;
		float Pitch;

		bool adjust_vol;

		adjust_vol = pParams->GetFloat( CRCD(0x5e285a66,"VolL"), &VolL );
		adjust_vol = pParams->GetFloat( CRCD(0xa4276705,"VolR"), &VolR ) || adjust_vol;
		if (pParams->GetFloat( CRCD(0xf6a36814,"Vol"), &Volume ))
		{
			VolL = Volume;
			VolR = Volume;
			adjust_vol = true;
		}

		bool result = true;

		if (adjust_vol)
		{
			Sfx::sVolume vol;
			vol.SetSilent();
			vol.SetChannelVolume( 0, VolL );
			vol.SetChannelVolume( 1, VolR );

			result = Pcm::SetStreamVolumeFromID(id, &vol);
		}

		if (pParams->GetFloat( CRCD(0xd8604126,"Pitch"), &Pitch ))
		{
			result = Pcm::SetStreamPitchFromID(id, Pitch) && result;
		}

		if (!result)
		{
			Dbg_MsgAssert(0, ("Can't find stream %s", Script::FindChecksumName(id)));
		}

		return result;
	}
	else
	{
		Dbg_MsgAssert(0, ("No stream specified"));
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PlayTrack | 
// @uparm "string" | filename in quotes (no extension, file should be a 
// .wav file in data/music)
// @uparmopt 0 | The index of the track. For use if the name is not known.
// @parmopt int | loop | 0 | continuously play the track if set to non-zero
bool ScriptPlayTrack( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	const char *songName = NULL;
	char p_calculated_track_name[MAX_TRACKNAME_STRING_LENGTH];
    int track_num=999;
	int loop = 0;
	
    if (!pParams->GetText( NONAME, &songName ))
	{
        pParams->GetInteger(NONAME,&track_num);
        songName=Pcm::GetTrackName(track_num,Pcm::TRACKLIST_PERM);
        
		Dbg_MsgAssert(strlen("MUSIC\\VAG\\SONGS\\")+strlen(songName)GetInteger(CRCD(0x5ea0e211,"loop"), &loop);

    if ( songName )
	{
		printf("PlayTrack songName ===================== %s\n", songName);
        Pcm::StopMusic( );
        Pcm::PlayTrack( songName, loop );
        Pcm::SetCurrentTrack( track_num );
	}
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PlayMusicStream | Plays a stereo stream through the music channel
// @uparm name | music stream name (in music.wad file)
// @parmopt float | volume | -1.0 | volume in percent; negative value uses music volume
bool ScriptPlayMusicStream( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	uint32 streamNameChecksum = 0;
	if ( pParams->GetChecksum( NONAME, &streamNameChecksum ) )
	{
		float volume = -1.0f;
		pParams->GetFloat( CHECKSUM_VOLUME, &volume );
		Pcm::PlayMusicStream( streamNameChecksum, volume );
		return ( true );
	}
	Dbg_MsgAssert( 0,( "\n%s\nMust specify stream name.", pScript->GetScriptInfo( ) ));
	return ( false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadStreamFrameAmp | Loads stream frame amplitude data for lipsync
// @uparm name | stream name
bool ScriptLoadStreamFrameAmp( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 streamNameChecksum = 0;
	if ( pParams->GetChecksum( NONAME, &streamNameChecksum ) )
	{
		return Pcm::CStreamFrameAmpManager::sLoadFrameAmp(streamNameChecksum);
	}
	Dbg_MsgAssert( 0,( "\n%s\nMust specify stream name.", pScript->GetScriptInfo( ) ));
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | FreeStreamFrameAmp | Frees stream frame amplitude data for lipsync
// @uparm name | stream name
bool ScriptFreeStreamFrameAmp( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 streamNameChecksum = 0;
	if ( pParams->GetChecksum( NONAME, &streamNameChecksum ) )
	{
		return Pcm::CStreamFrameAmpManager::sFreeFrameAmp(streamNameChecksum);
	}
	Dbg_MsgAssert( 0,( "\n%s\nMust specify stream name.", pScript->GetScriptInfo( ) ));
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PlaySound | 
// @uparm name | sound name
// @parmopt float | vol | 100.0 | volume of both left and right channel (overrides volL and volR)
// @parmopt float | volL | 100.0 | volume of left channel
// @parmopt float | volR | 100.0 | volume of right channel
// @parmopt float | pitch | 100.0 | 
// @parmopt name | id | 0 | control ID (used instead of sound name for script control purposes)
bool ScriptPlaySound(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 SoundChecksum = 0;
	uint32 id = 0;
	float Volume = 100.0f;
	float VolL = 100.0f;
	float VolR = 100.0f;
	float Pitch = 100.0f;
    
	pParams->GetChecksum( NONAME, &SoundChecksum );
	pParams->GetFloat( "VolL", &VolL );
	pParams->GetFloat( "VolR", &VolR );
	if (pParams->GetFloat( "Vol", &Volume ))
	{
		VolL = Volume;
		VolR = Volume;
	}
	pParams->GetFloat( "Pitch", &Pitch );
	pParams->GetChecksum( CRCD(0x40c698af,"id"), &id );

	Dbg_MsgAssert( SoundChecksum,( "PlaySound requires the name of the sound" ));
	
	Sfx::sVolume vol;
	vol.SetSilent();
	vol.SetChannelVolume( 0, VolL );
	vol.SetChannelVolume( 1, VolR );
	
	Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
	if( !sfx_manager->PlaySound( SoundChecksum, &vol, Pitch, id ))
	{
#ifdef __NOPT_ASSERT__
		printf( "failed to play sound %s", Script::FindChecksumName( SoundChecksum ) );
#endif	
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StopSound | 
// @uparm name | sound name (or control ID, if one was supplied with PlaySound)
bool ScriptStopSound( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 id;
	if (pParams->GetChecksum( NONAME, &id ))
	{
		Sfx::CSfxManager* sfx_manager = Sfx::CSfxManager::Instance();
		bool result = sfx_manager->StopSound(id);

		if (!result)
		{
			Dbg_Message( "Couldn't find sound %s to stop", Script::FindChecksumName( id ) );
		}

		return result;
	}
	else
	{
		Dbg_MsgAssert(0, ("No sound specified"));
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StopAllSounds | 
bool ScriptStopAllSounds(Script::CStruct *pParams, Script::CScript *pScript)
{
	Sfx::CSfxManager* sfx_manager = Sfx::CSfxManager::Instance();
    sfx_manager->StopAllSounds();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSoundParams | Set the volume and pitch of a playing sound
// @uparm name | sound name (or control ID, if one was supplied with PlaySound)
// @parmopt float | vol | 100.0 | volume of both left and right channel (overrides volL and volR)
// @parmopt float | volL | 100.0 | volume of left channel
// @parmopt float | volR | 100.0 | volume of right channel
// @parmopt float | pitch | 100.0 | pitch of sound
bool ScriptSetSoundParams( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 id = 0;
    
	if (pParams->GetChecksum( NONAME, &id ))
	{
		float Volume;
		float VolL = 100.0f;
		float VolR = 100.0f;
		float Pitch = 100.0f;

		bool adjust_vol;

		adjust_vol = pParams->GetFloat( CRCD(0x5e285a66,"VolL"), &VolL );
		adjust_vol = pParams->GetFloat( CRCD(0xa4276705,"VolR"), &VolR ) || adjust_vol;
		if (pParams->GetFloat( CRCD(0xf6a36814,"Vol"), &Volume ))
		{
			VolL = Volume;
			VolR = Volume;
			adjust_vol = true;
		}

		if (pParams->GetFloat( CRCD(0xd8604126,"Pitch"), &Pitch ) && !adjust_vol)
		{
			Dbg_MsgAssert(0, ("Must set volume when setting pitch"));

		}

		Sfx::sVolume vol;
		vol.SetSilent();
		vol.SetChannelVolume( 0, VolL );
		vol.SetChannelVolume( 1, VolR );

		Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
		if( !sfx_manager->SetSoundParams( id, &vol, Pitch ))
		{
	#ifdef __NOPT_ASSERT__
			Dbg_MsgAssert(0, ("Can't find sound %s", Script::FindChecksumName(id)));
	#endif // __NOPT_ASSERT__
		}
	}
	else
	{
		Dbg_MsgAssert(0, ("No sound specified"));
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsSoundPlaying | Returns true if the named sound is currently playing
// @uparm name | sound name (or control ID, if one was supplied with PlaySound)
bool ScriptIsSoundPlaying( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 id = 0;
    
	if (pParams->GetChecksum( NONAME, &id ))
	{
		Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
		return sfx_manager->SoundIsPlaying(id);
	}
	else
	{
		Dbg_MsgAssert(0, ("No sound specified"));
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSfxReverb | 1st param: reverbLevel 
// default 0 (will turn reverb off) 
// range: 0 to 100 percent 
// 2nd param: mode (see possible values in reverb.q) 
// default is 0 (first value in reverb.q)  
// Notes: 2nd param (mode) only matters if 1st param (reverbLevel) is 
// above 0. On a script Cleanup command, the global reverb is turned off.
// @uparmopt 0.0 | Reverb (cannot be greater than 100)
// @uparmopt name | reverb mode
bool ScriptSetReverb( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	float reverb = 0.0f;
	int reverbMode = 0;

	// if the parameter isn't found, it should default to zero!	
	pParams->GetFloat( NONAME, &reverb );
	if ( reverb > 100.0f )
	{
		reverb = 100.0f;
		Dbg_Message( "Reverb greater than 100.  Clipping." );
	}
	
	// if the parameter isn't found, it should default to zero!	
	pParams->GetInteger( CRCD(0x6835b854,"mode"), &reverbMode );
	
	 Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
	sfx_manager->SetReverb( reverb, reverbMode, pParams->ContainsFlag( "instant" ) );
	
	return ( true );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSfxDropoff | SetSfxDropoff [100] 
// Param: dropoff distance (in feet) 
// range: anything above zero 
// default is 100  
// Notes: only affects positional soundfx. Also, the global
// dropoff distance is set back to 100 (or whatever you want me
// to set it to) on a script Cleanup command. That way, if somebody 
// forgets to call SetSfxDropoff at the beginning of their level, it 
// will always be this default instead of staying at whatever level
// the previously loaded level had it set to
// @uparm 1.0 | Dropoff distance
bool ScriptSetDropoff( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	float dropoffDist = DEFAULT_DROPOFF_DIST;

	pParams->GetFloat( NONAME, &dropoffDist );
	if ( dropoffDist <= 0.0f )
	{
		Dbg_MsgAssert( 0.0f,( "Can't have dropoff zero or less." ));
		return ( false );
	}
	
	 Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
	sfx_manager->SetDefaultDropoffDist( FEET_TO_INCHES( dropoffDist ) );
	
	return ( true );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/



void	set_all_colors(Nx::CScene * p_scene, Image::RGBA rgb, uint32 light_group)
{
	Lst::HashTable< Nx::CSector > * p_sector_list = p_scene->GetSectorList();
	

	
	if (p_sector_list)
	{
		p_sector_list->IterateStart();	
		Nx::CSector *p_sector = p_sector_list->IterateNext();		
		if (light_group)
		{
			while(p_sector)
			{
				if (p_sector->GetLightGroup() == light_group)
				{
					p_sector->SetColor(rgb);
				}
				p_sector = p_sector_list->IterateNext();
			}
		}
		else
		{
			while(p_sector)
			{
				p_sector->SetColor(rgb);
				p_sector = p_sector_list->IterateNext();
			}
		}
	}
}


// Given a value, a target and a range, then scale the distance from the target by the amount
// so, 
float compress_value(float value, float target, float amount)
{
	float v =  target + (value-target) * amount;
	if (v<0.0f) v = 0.0f;
	if (v>255.0f) v = 255.0f;
	return v;
}

void	compress_all_vertex_colors(Nx::CScene * p_scene, float target, float amount, uint32 light_group)
{
	Lst::HashTable< Nx::CSector > * p_sector_list = p_scene->GetSectorList();
	
	Image::RGBA rgb;
	
	
	if (p_sector_list)
	{
		p_sector_list->IterateStart();	
		Nx::CSector *p_sector = p_sector_list->IterateNext();		
		while(p_sector)
		{

			Nx::CGeom 	*p_geom = p_sector->GetGeom();
//			Dbg_MsgAssert(p_geom,("sector does not have geometry"))
			
			if (p_geom && (!light_group || light_group == p_sector->GetLightGroup()))
			{
				//
				// Do renderable geometry
				int verts = p_geom->GetNumRenderVerts();
			
				if (verts)
				{
	//				Mth::Vector	*p_verts = new Mth::Vector[verts];
					Image::RGBA	*p_colors = new Image::RGBA[verts];
	//				p_geom->GetRenderVerts(p_verts);
					//p_source_geom->GetRenderColors(p_colors);
					
					// Note: getting the original render colors will allocate lots of memory
					// to store all the origianl colors the firs tiem it is called
					p_geom->GetOrigRenderColors(p_colors);
				
					Image::RGBA *p_color = p_colors;
					if (target != 0.0 || amount != 0.0)
					{
						for (int i = 0; i < verts; i++)
						{
							rgb.a = p_color->a; 
							rgb.r =  (uint8) compress_value((float) p_color->r,target,amount);						
							rgb.g =  (uint8) compress_value((float) p_color->g,target,amount);						
							rgb.b =  (uint8) compress_value((float) p_color->b,target,amount);						
							*p_color = rgb;		//(*p_color & 0xff000000) | color;
							p_color++;
						} // end for
					}
			
					// Set the colors on the actual new geom, not on the source...		
					p_geom->SetRenderColors(p_colors);
					
	//				delete [] p_verts;
					delete [] p_colors;
				} // end if
				else
				{
					// debuggery
					//p_geom->SetColor(Image::RGBA(0,0,100,0));
				}
			}
			p_sector = p_sector_list->IterateNext();
		}
	}
}



void	nudge_vertex_colors(Nx::CScene * p_scene, float amount)
{
	Lst::HashTable< Nx::CSector > * p_sector_list = p_scene->GetSectorList();
	
	Image::RGBA rgb;
	
	if (p_sector_list)
	{
		p_sector_list->IterateStart();	
		Nx::CSector *p_sector = p_sector_list->IterateNext();		
		while(p_sector)
		{

			Nx::CGeom 	*p_geom = p_sector->GetGeom();
//			Dbg_MsgAssert(p_geom,("sector does not have geometry"))
			
			if (p_geom)
			{
				//
				// Do renderable geometry
				int verts = p_geom->GetNumRenderVerts();
			
				if (verts)
				{
					Mth::Vector	*p_verts = new Mth::Vector[verts];
					Image::RGBA	*p_colors = new Image::RGBA[verts];
					p_geom->GetRenderVerts(p_verts);
					//p_source_geom->GetRenderColors(p_colors);
					
					// Note: getting the original render colors will allocate lots of memory
					// to store all the origianl colors the firs tiem it is called
					p_geom->GetOrigRenderColors(p_colors);
				
					Image::RGBA *p_color = p_colors;
					Mth::Vector *p_vert = p_verts;
					for (int i = 0; i < verts; i++)
					{
			//			CalculateVertexLighting(*p_vert, *p_color);
						
						rgb.a = p_color->a; 

						Mth::Vector col;
						col.Set(p_color->r, p_color->g, p_color->b);
					
											 
						// create a pseudo random number based on the vertex position
						// so non-common verts in the same position will get the same result
						// which is the only way you can get a smooth result							 
						int i_random =  (int)((*p_vert)[X]*16.0f) * (int)((*p_vert)[Y]*16.0f) ^ (int)((*p_vert)[Z]*16.0f);
						float r1 = (float)(i_random & 0x7fff) / 32768.0f;
						
						float	nudge = 1.0f + ( 2.0f * r1 * amount ) - amount;
						
						col *= 	nudge;
						if (col[X] < 0.0f) col[X] = 0.0f;
						if (col[X] > 255.0f) col[X] = 255.0f;
						if (col[Y] < 0.0f) col[Y] = 0.0f;
						if (col[Y] > 255.0f) col[Y] = 255.0f;
						if (col[Z] < 0.0f) col[Z] = 0.0f;
						if (col[Z] > 255.0f) col[Z] = 255.0f;
						
						rgb.r = (uint8)col[X];
						rgb.g = (uint8)col[Y];
						rgb.b = (uint8)col[Z];
						 
						*p_color = rgb;		//(*p_color & 0xff000000) | color;
						//*(uint32*)p_color = Mth::Rnd(32767);		//(*p_color & 0xff000000) | color;
			
						p_color++;
						p_vert++;
					} // end for
			
					// Set the colors on the actual new geom, not on the source...		
					p_geom->SetRenderColors(p_colors);
					
					delete [] p_verts;
					delete [] p_colors;
				} // end if
				else
				{
					// debuggery
					//p_geom->SetColor(Image::RGBA(0,0,100,0));
				}
			}
			p_sector = p_sector_list->IterateNext();
		}
	}
}




bool ScriptSetSceneColor( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	uint32 color;
	
	pParams->GetInteger( "color", (int*)&color, TRUE );
	uint32 sky = color;
	pParams->GetInteger( "sky", (int*)&sky );
	uint32 light_group =0;
	pParams->GetChecksum( "lightgroup", (uint32*)&light_group );	
	Nx::CScene	*p_scene = Nx::CEngine::sGetMainScene();
	
	Image::RGBA rgb;
	rgb.r = (uint8)((color)&0xff);
	rgb.g = (uint8)((color>>8)&0xff);
	rgb.b = (uint8)((color>>16)&0xff);
	rgb.a = 0x80;
	
	Image::RGBA sky_rgb;
	sky_rgb.r = (uint8)((sky)&0xff);
	sky_rgb.g = (uint8)((sky>>8)&0xff);
	sky_rgb.b = (uint8)((sky>>16)&0xff);
	sky_rgb.a = 0x80;
	
	
	if (p_scene)
	{
		// For main scene, just set the root color
		#ifdef	__PLAT_NGPS__

		// if doing a global or outdoor change to some color
		// then reset all the colors first		
		// to reset things that were set by individual SetObjectColor  commands
		// like the Chris's best line goal in NJ
		if 	(color != 0x808080 && (light_group == CRCD(0xe3714dc1,"outdoor") || light_group == 0))
		{
			Image::RGBA clear_rgb(0x80,0x80,0x80,0x80);
			set_all_colors(p_scene, clear_rgb, light_group);						
		}
		
		// if not the outdoor or default group, or if we are turing lighting off with 0x808080
		// then set it on all nodes
		// otherwise we just set the root color
		if ((light_group != CRCD(0xe3714dc1,"outdoor") && light_group != 0 ) || color == 0x808080 
			/*|| p_scene->GetID() ==CRCD(0xefc68238,"cloned")*/)
		#endif
		{
			set_all_colors(p_scene, rgb, light_group);
		}
		
		// Set the root color of the scene.  A PS2 specific optimization
		// which can be ignored by other platforms.		
		if (light_group == CRCD(0xe3714dc1,"outdoor") || light_group == 0)
		{
			p_scene->SetMajorityColor(rgb);
		}
	}
	
	// K: If the park editor shell scene exists, update its colours too.
	p_scene=Nx::CEngine::sGetScene(Ed::CParkManager::Instance()->GetShellSceneID());
	if (p_scene)
	{
		// Note, cut and paste from above
		#ifdef	__PLAT_NGPS__
		// if not the outdoor or default group, or if we are turing lighting off with 0x808080
		// then set it on all nodes
		// otherwise we just set the root color
		if ((light_group != CRCD(0xe3714dc1,"outdoor") && light_group != 0 ) || color == 0x808080)
		#endif
		{
			set_all_colors(p_scene, rgb, light_group);
		}
		
		// Set the root color of the scene.  A PS2 specific optimization
		// which can be ignored by other platforms.		
		if (light_group == CRCD(0xe3714dc1,"outdoor") || light_group == 0)
		{
			p_scene->SetMajorityColor(rgb);
		}
	}

	
	p_scene = Nx::CEngine::sGetSkyScene();
	
	if (p_scene)
	{
		set_all_colors(p_scene, sky_rgb, light_group);
	}
	
	// we also need to set the color of all the level objects
	// assume they are all "outdoors"
	if (light_group == CRCD(0xe3714dc1,"outdoor") || light_group == 0)
	{
		Obj::CModelComponent *p_component = static_cast( Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRC_MODEL ));
		while( p_component )
		{
			p_component->ApplySceneLighting(rgb);
			p_component = static_cast( p_component->GetNextSameType());
		}
	}	
	
	return true;
}





/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool ScriptCompressVC( Script::CStruct *pParams, Script::CScript *pScript )
{

	float target = 0.0f;
	pParams->GetFloat( "target", (float*)&target, TRUE );
	float amount = 0.0f;
	pParams->GetFloat( "percent", (float*)&amount );
	amount = (100.0f - amount)/100.0f;
	uint32 light_group =0;
	pParams->GetChecksum( "lightgroup", (uint32*)&light_group );	

	Nx::CScene	*p_scene = Nx::CEngine::sGetMainScene();
	if (p_scene)
	{
		compress_all_vertex_colors(p_scene, target, amount, light_group);
	}
	p_scene = Nx::CEngine::sGetSkyScene();
	if (p_scene)
	{
		compress_all_vertex_colors(p_scene, target, amount, light_group);
	}
	return true;
}

// @script bool | FakeLights | Adjust lighting for a node-based SceneLight.
// @parm name | id | The name of the node, if only one needs adjusting.
// @parm name | Prefix | The prefix of a set of nodes, if lots need adjusting.
// @parmopt integer | Time | 0 | The time period, in frames, over which the execution of the command
// will be spread so as not to use up too much CPU time in one frame.
// @parmopt float | inner_radius | none | The new radius of the light in inches
// @parmopt float | outer_radius | none | The new radius of the light in inches
// @parmopt float | percent | none | The new intensity percentage
// @parmopt integer | red | none | The new red component of the light color (also green, blue)
bool ScriptFakeLights( Script::CStruct *pParams, Script::CScript *pScript )
{
	// If an id is supplied, we want to apply the effect to just one light.
	uint32 id = 0; 
	if( pParams->GetChecksum( CRCD(0x40c698af,"id"), &id ))
	{
		Nx::CLightManager::sFakeLight(id,pParams);
		return true;
	}

	// Get the period in frames over which the command will execute.
	int time_period=0;
	pParams->GetInteger(CRCD(0x906b67ba,"time"),&time_period);
	
	// K: Added this bit to allow a set of nodes with a given prefix to be adjusted.
	uint32 prefix=0;	
    if ( pParams->GetChecksumOrStringChecksum( CRCD(0x6c4e7971,"Prefix"), &prefix ) )
	{
		uint16 num_nodes = 0;
		const uint16 *p_nodes = SkateScript::GetPrefixedNodes( prefix, &num_nodes );
		
		Nx::CLightManager::sFakeLights(p_nodes,num_nodes,time_period,pParams);
		return true;
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool ScriptNudgeVC( Script::CStruct *pParams, Script::CScript *pScript )
{

	float amount = 0.0f;
	pParams->GetFloat( "percent", (float*)&amount, true );
	amount = (amount)/100.0f;
	Nx::CScene	*p_scene = Nx::CEngine::sGetMainScene();
	if (p_scene)
	{
		nudge_vertex_colors(p_scene,amount);
	}
	return true;
}

// Center the viewer camera on the scene
// (Previously this would center the current camera on the scene, which had unintended results)
void	center_camera_on_scene(Nx::CScene * p_scene, float scale, float x_rot, float y_rot, float z_rot, uint32 id=0)
{

	// get a bounding box of the world, and center the camera on that	
	Mth::CBBox bbox;
	
	Lst::HashTable< Nx::CSector > * p_sector_list = p_scene->GetSectorList();
	if (p_sector_list)
	{
		p_sector_list->IterateStart();	
		Nx::CSector *p_sector = p_sector_list->IterateNext();		
		while(p_sector)
		{

			if ((!id || id == p_sector->GetChecksum()) && p_sector->IsActive())
			{
				
				Nx::CGeom 	*p_geom = p_sector->GetGeom();
				if (p_geom)
				{
					//
					// Do renderable geometry
					int verts = p_geom->GetNumRenderVerts();
					if (verts)
					{
						Mth::Vector	*p_verts = new Mth::Vector[verts];
						p_geom->GetRenderVerts(p_verts);
						Mth::Vector *p_vert = p_verts;
						for (int i = 0; i < verts; i++)
						{
							bbox.AddPoint(*p_vert);
							//printf ("%f,%f,%f,%f\n",p_vert[0][X],p_vert[0][Y],p_vert[0][Z],p_vert[0][W]);
//							if (i>0)
//							{
//								Gfx::AddDebugLine(p_vert[0],p_vert[-1],0xffffff,100);
//							}
							p_vert++;
						} // end for
				
						delete [] p_verts;
					} // end if
				}
			}
			p_sector = p_sector_list->IterateNext();
		}
	}


	
	
	// Move the camera to the object, and move it back by the width of the object (min 100 ft)
//    Gfx::Camera* pCam = Nx::CViewportManager::sGetCamera( 0 );

// Mick:  Now we attempt to get the viewer camera object

	Obj::CCompositeObject *p_obj = (Obj::CCompositeObject *)Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0xeb17151b,"viewer_cam"));
	if (!p_obj)
	{
		return;
	}
	
								 
	Dbg_MsgAssert(GetCameraComponentFromObject(p_obj),("Viewer Camera Object Missing camera component"));
	
	
	Mth::Vector min = bbox.GetMin();
	Mth::Vector max = bbox.GetMax();
	Mth::Vector mid = (min + max)/2.0f;
	float	diagonal = (max - min).Length();
	if (diagonal < 1000)
	{
		diagonal = 1000;
	}


	diagonal *= scale;

	// Note we are moving the parent object, the camera component takes its pos from this
	p_obj->GetMatrix().Ident();
	p_obj->GetMatrix().RotateX(Mth::DegToRad(x_rot));
	p_obj->GetMatrix().RotateY(Mth::DegToRad(y_rot));
	p_obj->GetMatrix().RotateZ(Mth::DegToRad(z_rot));

	// printf ("diagonal = %f, mid = [%f,%f,%f]\nbox = [%f,%f,%f] - [%f,%f,%f]\n",diagonal,mid[X],mid[Y],mid[Z],min[X],min[Y],min[Z],max[X],max[Y],max[Z]  );	

	p_obj->SetPos(mid + scale * diagonal * p_obj->GetMatrix().GetAt());

	
}


// @script | CenterCamera |  Center the camera on the rendered geometry, or an individual object
// @parmopt float | scale | 0.9 | 
// @parmopt float | X | -45 | Angle to rotate about X 
// @parmopt float | Y | 45 | Angle to rotate about Y
// @parmopt float | Z | 0 | Angle to rotate about X
// @parmopt	checksum | id | 0 | checksum of and individual object to center on
// Notes:  The Camera will be centered on the center of the bounding box
// of all exported geometry.  It will be moved back by the size of the diagonal
// of the bounding box, and rotated by the X,Y,Z angles

bool ScriptCenterCamera( Script::CStruct *pParams, Script::CScript *pScript )
{
	// This is only interend to be used in the fly around modes
	// so kill it during menus

	if (!Mdl::CViewer::sGetViewMode())
	{
		return false;
	}


	float scale = 0.9f;
	pParams->GetFloat( "scale", &scale );
	float X = -45.0f;;
	pParams->GetFloat( "x", &X );
	float Y = 45.0f;
	pParams->GetFloat( "y", &Y );
	float Z = 0.0f;
	pParams->GetFloat( "z", &Z );
	uint32 id = 0; 
	pParams->GetChecksum( "id", &id );




	Nx::CScene	*p_scene = Nx::CEngine::sGetMainScene();
	if (p_scene)
	{
		center_camera_on_scene(p_scene, scale, X, Y, Z, id);
	}
	return true;
}





#define CHECKSUM_MAXPITCH			0xfa3e14c5	// maxpitch
#define CHECKSUM_MINPITCH			0x1c5ebb24	// minpitch
#define CHECKSUM_MAXVOL				0x0693daaf	// maxvol
#define CHECKSUM_MINVOL				0x4391992d	// minvol
#define CHECKSUM_TERRAIN			0x3789ac4e  // terrain
#define CHECKSUM_TABLE				0x09d670b9  // table

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadTerrainSounds | Loads all the associated sounds for a terrain type of a level.
// @parm name | terrain | Name of terrain

bool ScriptLoadTerrainSounds( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 terrain_checksum;

	if (!pParams->GetChecksum(CHECKSUM_TERRAIN, &terrain_checksum))
	{
		printf("Expected terrain checksum\n");
		return false;
	}

	ETerrainType terrain = Env::CTerrainManager::sGetTerrainFromChecksum(terrain_checksum);
	Env::CTerrainManager::sLoadTerrainSounds(terrain);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Goto | jumps to the specified script
// @Uparm name | The name of the script 
// @parmopt structure | Params | | parameter list to pass to the new script
bool ScriptGoto(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	uint32 ScriptChecksum=0;
	pParams->GetChecksum(NONAME,&ScriptChecksum);
	Dbg_MsgAssert(ScriptChecksum,("Goto command requires a script name"));
	
	Script::CStruct *pArgs=NULL;
	pParams->GetStructure(CRCD(0x7031f10c,"Params"),&pArgs);
	pScript->SetScript(ScriptChecksum,pArgs,pScript->mpObject);
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GotoPreserveParams | This has the same effect as goto, 
// except all the parameters that were passed to the current script 
// will get passed to the new script
// @Uparm name | The name of the script to goto
// @parmopt structure | Params | | additional parameters to pass to script
bool ScriptGotoPreserveParams(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	uint32 ScriptChecksum=0;
	pParams->GetChecksum(NONAME,&ScriptChecksum);
	Dbg_MsgAssert(ScriptChecksum,("GotoPreserveParams command requires a script name"));
	
	// Extract the old script parameters.
	// Note: Need to do it this way rather than pass pScript->GetParams() to pScript->SetScript,
	// because SetScript will clear the pScript's params before using the passed params, but
	// since the passed params ARE the pScript's params, this will mean they will get zeroed.
	Script::CStruct *pTemp=new Script::CStruct;
	pTemp->AppendStructure(pScript->GetParams());
	
	// If any more params are specified, merge them on too.
	Script::CStruct *pMoreParams=NULL;
	pParams->GetStructure(CRCD(0x7031f10c,"Params"),&pMoreParams);
	pTemp->AppendStructure(pMoreParams);
	
	
	pScript->SetScript(ScriptChecksum,pTemp,pScript->mpObject);
	delete pTemp;
	
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GotoRandomScript | This will abort the current script
// and jump to a random one of the scripts specified in an array. 
// For example: 
// GotoRandomScript [ScriptA ScriptB ScriptC]
// @uparm [] | Array of scripts to choose from

bool ScriptGotoRandomScript(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	
	Script::CArray *pArray=NULL;
	pParams->GetArray(NONAME,&pArray);
	if (pArray && pArray->GetSize())
	{
		switch (pArray->GetType())
		{
			case ESYMBOLTYPE_NAME:
				pScript->SetScript(pArray->GetNameChecksum(Mth::Rnd(pArray->GetSize())),NULL,pScript->mpObject);				
				break;
			default:
				Dbg_MsgAssert(0,("GotoRandomScript requires an array of script names."));
				break;
		}		
	}
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PrintStruct | Prints the contents of the passed structure
// @uparm structure | The structure to print
bool ScriptPrintStruct(Script::CStruct *pParams, Script::CScript *pScript)
{
	Script::PrintContents(pParams);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static CComponent sTempComponent;

static int sFormatText(CStruct *p_dest_struct, CStruct *p_format)
{
	#define FORMATTED_TEXT_MAX_LEN 1000
	static char s_printf_buffer[FORMATTED_TEXT_MAX_LEN+1];

	Dbg_MsgAssert(p_format,("NULL p_format"));
	
	uint32 string_name_checksum=0;
	p_format->GetChecksum(CRCD(0xff0db407,"TextName"),&string_name_checksum);
	
	// If passed a ChecksumName, the checksum of the formatted text will
	// be calculated and put into a parameter with that name.
	uint32 checksum_name_checksum=0;
	p_format->GetChecksum(CRCD(0x76fa7a8f,"ChecksumName"),&checksum_name_checksum);

	int result = 1;
	
	const char *p_format_text="";
	if (!p_format->GetString(NONAME,&p_format_text))
	{
		printf("Expected text format parameters to contain a format string\n");
		return 0;
	}	
	
	char *p_dest=s_printf_buffer;
	const char *p_scan=p_format_text;
	int space_left=FORMATTED_TEXT_MAX_LEN;
	while (*p_scan)
	{
		Dbg_MsgAssert(space_left,("Overflowed formatted text buffer"));

		if (p_scan[0]=='\\' && p_scan[1]=='%')
		{
			// Make \% equate to %, so that we can have % chars in the string.
			++p_scan;
			*p_dest++=*p_scan++;
			--space_left;
		}	
		else if (*p_scan=='%')
		{
			++p_scan; // Skip over the %
			
			// Find the component in p_format which has the same name as the name following
			// the % sign.
			#define PARAM_NAME_MAX_LEN 50
			char p_param_name[PARAM_NAME_MAX_LEN+1];
			int c=0;
			char *p_param_name_dest=p_param_name;

			if (*p_scan=='%')
			{
				// Two percents mean that what follows is a name longer than one char.
				++p_scan;
				
				while ((*p_scan>='a' && *p_scan<='z') ||
					   (*p_scan>='A' && *p_scan<='Z') ||
					   (*p_scan>='0' && *p_scan<='9') ||
					   *p_scan=='_')
				{
					Dbg_MsgAssert(cFindNamedComponentRecurse(GenerateCRC(p_param_name));
			
			if (!p_unresolved_comp)
			{
				printf("Missing text formatting parameter '%s'\n",p_param_name);
				return 0;
			}	

			// p_unresolved_comp may have type Name, which may resolve to some global. So use
			// the ResolveNameComponent to resolve it.
			// Using sTempComponent because ResolveNameComponent may modify the component by
			// perhaps setting it's mpStructure or mpArray to equal that of some global symbol.
			// It would be bad to modify the contents of p_unresolved_comp because it is
			// in the passed structure, which will get cleaned up later, resulting in the deletion
			// of the global symbol's pointer, causing horrible crashes.
			sTempComponent.mType=p_unresolved_comp->mType;
			sTempComponent.mUnion=p_unresolved_comp->mUnion;
			ResolveNameComponent(&sTempComponent);
			
			char p_temp[100];
			switch (sTempComponent.mType)
			{
				case ESYMBOLTYPE_INTEGER:
				{
					if (p_format->ContainsFlag(CRCD(0xf138085f,"UseCommas")))
					{
						strcpy(p_temp,Str::PrintThousands(sTempComponent.mIntegerValue));
					}
					else
					{
						sprintf(p_temp,"%d",sTempComponent.mIntegerValue);
					}	
					int len=strlen(p_temp);
					Dbg_MsgAssert(len<=space_left,("Overflowed formatted text buffer"));
					strcpy(p_dest,p_temp);
					p_dest+=len;
					space_left-=len;
					break;
				}	
				case ESYMBOLTYPE_FLOAT:
				{
					int decimal_places=3;
					char format_string[16];
					p_format->GetInteger(CRCD(0xd8e2e09f,"DecimalPlaces"),&decimal_places);
					Dbg_MsgAssert(decimal_places>=0 && decimal_places<10,("decimal_places must be less than 10"));
					sprintf( format_string, "%%.%df", decimal_places );
					sprintf(p_temp,format_string,sTempComponent.mFloatValue);
					int len=strlen(p_temp);
					Dbg_MsgAssert(len<=space_left,("Overflowed formatted text buffer"));
					strcpy(p_dest,p_temp);
					p_dest+=len;
					space_left-=len;
					break;
				}	
				case ESYMBOLTYPE_PAIR:
				{
					CPair *p_pair=sTempComponent.mpPair;
					Dbg_MsgAssert(p_pair,("NULL p_pair"));
					
					sprintf(p_temp,"(%.3f,%.3f)",p_pair->mX,p_pair->mY);
					int len=strlen(p_temp);
					Dbg_MsgAssert(len<=space_left,("Overflowed formatted text buffer"));
					strcpy(p_dest,p_temp);
					p_dest+=len;
					space_left-=len;
					break;
				}	
				case ESYMBOLTYPE_VECTOR:
				{
					CVector *p_vector=sTempComponent.mpVector;
					Dbg_MsgAssert(p_vector,("NULL p_vector"));
					
					sprintf(p_temp,"(%.3f,%.3f,%.3f)",p_vector->mX,p_vector->mY,p_vector->mZ);
					int len=strlen(p_temp);
					Dbg_MsgAssert(len<=space_left,("Overflowed formatted text buffer"));
					strcpy(p_dest,p_temp);
					p_dest+=len;
					space_left-=len;
					break;
				}	
				case ESYMBOLTYPE_NAME:
				{
					// we set the return result to 2, so we can assert in ScriptFormatText
					// so we can see where in the script we are
					result = 2;
					sprintf(p_temp,"%s",FindChecksumName(sTempComponent.mChecksum));
					int len=strlen(p_temp);
					Dbg_MsgAssert(len<=space_left,("Overflowed formatted text buffer"));
					strcpy(p_dest,p_temp);
					p_dest+=len;
					space_left-=len;
					break;
				}	
				case ESYMBOLTYPE_STRING:
				case ESYMBOLTYPE_LOCALSTRING:
				{
					const char *p_source="";
					if (sTempComponent.mType==ESYMBOLTYPE_STRING)
					{
						p_source=sTempComponent.mpString;
					}	
					else
					{
						p_source=sTempComponent.mpLocalString;
					}	
					
					while (*p_source)
					{
						Dbg_MsgAssert(space_left,("Overflowed formatted text buffer"));
						*p_dest++=*p_source++;
						--space_left;
					}	
					break;
				}	
				default:
				{
					Dbg_MsgAssert(0,("Error when formatting '%s'\n'%s' has data type '%s' which is not supported yet.\n",p_format_text,p_param_name,Script::GetTypeName(sTempComponent.mType)));
					break;
				}	
			}	
		}
		else
		{
			*p_dest++=*p_scan++;
			--space_left;
		}	
	}
	*p_dest=0;

	Dbg_MsgAssert(p_dest_struct,("NULL p_dest_struct"));
	if (string_name_checksum)
	{
		p_dest_struct->AddString(string_name_checksum,s_printf_buffer);
	}	
	if (checksum_name_checksum)
	{
		p_dest_struct->AddChecksum(checksum_name_checksum,Script::GenerateCRC(s_printf_buffer));
	}	
	return result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | FormatText | Fills in any % characters in the passed format string with the passed
// parameters, and assigns the new string to a parameter with the given name.
// For example:
// FormatText TextName=Line1 "Hello %f %b" f="Mum" b=23
// will create the string "Hello Mum 23" and assign it to Line1
// The name following the % must consist of a single character. This is to allow formatting to
// be used to append strings, eg "%sBlaa"
// If two % signs are used, then the name that follows may consist of more than one char,
// eg "%%foo blaa"
//
// Currently supported data types are integer, float, string, local string, vector and pair.
// If any of the parameters are missing it will give an error message and will not create the
// final string. It will not assert though.
//
// @parmopt name | TextName | | The name to be given to the new formatted string
// @parmopt name | ChecksumName | | If this is specified, the checksum of the formatted text
// will be calculated and added using ChecksumName as its name.
// @uparm string | The source format string
// @flag UseCommas | This will make it print any integer using commas to separate thousands for clarity.
bool ScriptFormatText(Script::CStruct *pParams, Script::CScript *pScript)
{
	if (sFormatText(pScript->GetParams(),pParams) == 2)
	{
	#ifdef	__NOPT_ASSERT__
//		Dbg_MsgAssert(0,("%s\nCannot use checksum names in FormatText",pScript->GetScriptInfo()));
		printf ("%s\n### WARNING ### - Cannot use checksum names in FormatText",pScript->GetScriptInfo());
	#endif
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool s_get_string_from_params(char* p_buffer, Script::CStruct *pParams, Script::CScript* pScript)
{
	Dbg_Assert( p_buffer );

	const char *p_text="Default printf text";
	if (pParams->GetText(NONAME,&p_text))
	{
		CStruct *p_format=new CStruct;
		p_format->AddChecksum(GenerateCRC("TextName"),GenerateCRC("PrintfText"));
		p_format->AppendStructure(pParams);
		
		if (sFormatText(p_format,p_format))
		{
			p_format->GetString("PrintfText",&p_text);
		}
		else
		{
			Dbg_Warning("\n%s\nError formatting text for printf",pScript->GetScriptInfo());
			p_text="";
		}

		sprintf(p_buffer, "%s\n",p_text);
		
		delete p_format;   		// p_format actually contains the string we are printing, so delete it AFTER the printf
		
		return true;
	}

	int IntVal=0;
	if (pParams->GetInteger(NONAME,&IntVal))
	{
		sprintf(p_buffer, "%d\n",IntVal);

		return true;
	}
		
	#ifdef __NOPT_ASSERT__
	uint32 Checksum=0;    
	if (pParams->GetChecksum(NONAME,&Checksum))
	{
		sprintf(p_buffer, "[Checksum] %s\n",Script::FindChecksumName(Checksum));

		return true;
	}
		
	sprintf(p_buffer, "Empty printf at %s", pScript->GetScriptInfo());
	#endif
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Printf | Printf "Default printf text" Prints the passed 
// text onto the EE0 output on the target manager.
// Printf can will also print the checksum name of any passed checksum, for example:
// printf blaa    // Will print: [Checksum] blaa
// @uparmopt "text" | some text to print
// @uparmopt 1 | an int to print
// @uparmopt name | checksum to print
// @flag UseCommas | This will make it print any integer using commas to separate thousands for clarity.
bool ScriptPrintf(Script::CStruct *pParams, Script::CScript *pScript)
{
	char buffer[1024];

	s_get_string_from_params(buffer, pParams, pScript);
	
	printf( buffer );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptAssert |
bool ScriptScriptAssert(Script::CStruct *pParams, Script::CScript *pScript)
{
	char buffer[1024];

	s_get_string_from_params(buffer, pParams, pScript);
	
	Dbg_MsgAssert( 0, ( "%s\nSCRIPT ASSERT:  %s", pScript->GetScriptInfo(), buffer ) );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PrintScriptInfo |
bool ScriptPrintScriptInfo(Script::CStruct *pParams, Script::CScript *pScript)
{
	#ifdef	__NOPT_ASSERT__
	printf("+++ScriptInfo+++++++++++++++++++++++++\n");
	
	// firsts prints out the associated string,
	// using the "printf" script function
	ScriptPrintf(pParams, pScript);
	
	// then it asserts
	printf("%s",pScript->GetScriptInfo());
	
	printf("++++++++++++++++++++++++++++++++++++++\n");
	#endif	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetHackFlag | Sets special hackflag
// @uparmopt 1 | integer flag (1 or 0)
static bool HackFlag=false;
bool ScriptSetHackFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	int v=1;
	pParams->GetInteger(NONAME,&v);
	HackFlag=v?true:false;
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | HackFlagIsSet | Returns current value of hackflag
bool ScriptHackFlagIsSet(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	return HackFlag;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PreloadModel | Preloads model
// Example: PreloadModel name="ModelName" or name="levels/skateshop/thps4board_01/thps4board_01.mdl"
// @parm string | name | The model name
bool ScriptPreloadModel(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char* pModelName;
	pParams->GetText( "name", &pModelName, Script::ASSERT );

	// now preload any programmatically-loaded peds
	Nx::CModel* pDummy = Nx::CEngine::sInitModel();
	Dbg_Assert( pDummy );
	
	bool forceTexDictLookup = pParams->ContainsFlag( CRCD(0x6c37fdc7,"AllowReplaceTex") );

	Str::String fullModelName;
	fullModelName = Gfx::GetModelFileName(pModelName, ".mdl");
	pDummy->AddGeom( fullModelName.getString(), 0, true, 0, forceTexDictLookup );
		
	Nx::CEngine::sUninitModel( pDummy );
	
	return true;
}
		   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*
// @script | LoadLevelGeometry | Loads a bsp file. Assumes the current 
// directory is \skate3\data, so a valid string would be "levels\aus\aus.bsp". 
// Can also have an optional "Sky="xxx.bsp" parameter for loading the 
// sky dome with a level file. The Level and Sky parameters are optional, 
// but you must have at least one of them (So you can load just a sky, 
// or just a level, if you want).
// @parmopt string | Sky | | .bsp file for sky dome
// @parmopt string | Level | | .bsp file for level
// @parmopt string | Pre_set | | load assets from PRE file
bool ScriptLoadLevelGeometry(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	 File::PreMgr * pre_mgr =  File::PreMgr::Instance();
	
	const char*	p_level = NULL;
	const char*	p_sky 	= NULL;

	pParams->GetText( "Level",&p_level );
	pParams->GetText( "Sky", &p_sky );

	Nx::CTexDict *p_tex_dict, *p_sky_tex_dict;
	Nx::CScene *p_scene, *p_sky_scene;

	if (p_sky)
	{
		p_sky_tex_dict = Nx::CTexDictManager::sLoadTextureDictionary(p_sky);
		Dbg_Assert(p_sky_tex_dict);
		p_sky_scene = Nx::CEngine::sLoadScene(p_sky, p_sky_tex_dict, false, true);	// mark as sky that doesn't go in SuperSectors
		Dbg_Assert(p_sky_scene);
	}
	
	if (p_level)
	{
		//p_tex_dict = Nx::CEngine::sLoadTextures(p_level);
		p_tex_dict = Nx::CTexDictManager::sLoadTextureDictionary(p_level);
		Dbg_Assert(p_tex_dict);
		p_scene = Nx::CEngine::sLoadScene(p_level, p_tex_dict);
		Dbg_Assert(p_scene);
		p_scene->LoadCollision(p_level);
	}
	
	return true;
}

*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptUnloadAllLevelGeometry(Script::CStruct *pParams, Script::CScript *pScript)
{
	Nx::CEngine::sUnloadAllScenesAndTexDicts();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptLoadScene(Script::CStruct *pParams, Script::CScript *pScript)
{
	bool is_dictionary = pParams->ContainsFlag(CRCD(0x3d559e7d,"is_dictionary"));  		// piece dictionary, like the park editor
	bool is_sky = pParams->ContainsFlag(CRCD(0xb5bd28d8,"is_sky"));						// a sky dome
	bool is_net = pParams->ContainsFlag(CRCD(0x417149c8,"is_net"));						// the _net version
	bool add_super_sectors = ! pParams->ContainsFlag(CRCD(0x8943dbd8,"no_supersectors"));	// optionally ignore supersectors
	
	const char *p_scene_name = NULL;
	pParams->GetText( CRCD(0x26861025,"scene"),&p_scene_name,true );
	Dbg_MsgLog(("Loading Scene %s ...",p_scene_name));
	printf ("Loading Scene %s ...",p_scene_name);
	

	// First load the texture dictionary
	char	texture_dict_name[128];
	sprintf(texture_dict_name,"levels\\%s\\%s%s.tex",p_scene_name,p_scene_name,is_net?"_net":"");


	
	#ifdef	__NOPT_ASSERT__
	const char *p_fallback = "nj_sky";
	// Some debug code to load the NJ sky if default_sky does not exists
	if (stricmp(texture_dict_name,"levels\\default_sky\\default_sky.tex")==0)
	{
		printf ("TRYING TO LAOD DEFUALT SKY\n");
		printf ("TRYING TO LAOD DEFUALT SKY\n");
		printf ("TRYING TO LAOD DEFUALT SKY\n");
		printf ("TRYING TO LAOD DEFUALT SKY\n");
		// it's the default sky, so check if it exists
		if (!File::Exist(texture_dict_name))
		{
			printf ("NO EXIST\n");
		
			p_scene_name = p_fallback;			
			sprintf(texture_dict_name,"levels\\%s\\%s.tex",p_scene_name,p_scene_name);
		}
	}
	#endif
	
	
	Nx::CTexDict * p_tex_dict = Nx::CTexDictManager::sLoadTextureDictionary(texture_dict_name,true);
	Dbg_MsgAssert(p_tex_dict,("ERROR loading tex dict for %s\n",texture_dict_name));

	// a level ending in _sky is automatically a sky dome	
	if (Str::StrStr(p_scene_name,"_sky"))
	{
		is_sky = true;
	}
	
	// Skys always have no supersectors
	if (is_sky)
	{
		add_super_sectors = false;		
	}
	
	// and dictionaries always have no supersectors
	if (is_dictionary)
	{
		add_super_sectors = false;		
	}
	
	#ifdef	__NOPT_ASSERT__
	Nx::CScene * p_scene = 
	#endif
	Nx::CEngine::sLoadScene(p_scene_name, p_tex_dict, add_super_sectors,is_sky,is_dictionary,is_net);	// mark as sky that doesn't go in SuperSectors
	Dbg_MsgAssert(p_scene,("ERROR loading scene for %s\n",p_scene_name));
	printf ("... done\n");
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptAddScene(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *p_scene_name = NULL;
	const char *p_add_name = NULL;
	pParams->GetText( "scene",&p_scene_name );
	pParams->GetText( "add",&p_add_name );
	if (p_scene_name && p_add_name)
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());

		#ifdef	__NOPT_ASSERT__
		Nx::CScene * p_scene =
		#endif
		Nx::CEngine::sAddScene(p_scene_name, p_add_name);
		Dbg_MsgAssert(p_scene,("ERROR adding scene for %s\n",p_scene_name));

		Mem::Manager::sHandle().PopContext();
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// we have a seperate script command for loading collision
// so we can control the loading of pre files
bool ScriptLoadCollision(Script::CStruct *pParams, Script::CScript *pScript)
{

	const char *p_scene_name = NULL;
	pParams->GetText( "scene",&p_scene_name );
	bool is_net = pParams->ContainsFlag(CRCD(0x417149c8,"is_net"));						// the _net version
	if (p_scene_name)
	{
		Nx::CScene * p_scene = Nx::CEngine::sGetScene(p_scene_name);
		Dbg_MsgAssert(p_scene,("Trying to load collision for scene: %s\n",p_scene_name));
		Dbg_MsgLog(("Trying to load collision for scene: %s\n",p_scene_name));
		p_scene->LoadCollision(p_scene_name, is_net);	
	}
	return true;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptAddCollision(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *p_scene_name = NULL;
	const char *p_add_name = NULL;
	pParams->GetText( "scene",&p_scene_name );
	pParams->GetText( "add",&p_add_name );
	if (p_scene_name && p_add_name)
	{
		Nx::CScene * p_scene = Nx::CEngine::sGetScene(p_scene_name);
		Dbg_MsgAssert(p_scene,("Trying to add collision file %s to scene: %s\n", p_add_name, p_scene_name));
		p_scene->AddCollision(p_add_name);
	}
	return true;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptUnloadScene(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *p_scene_name = NULL;
	bool matching_names;
	pParams->GetText( "scene",&p_scene_name );

	matching_names = false;
	if (p_scene_name)
	{
		Nx::CScene *p_scene = Nx::CEngine::sGetScene(p_scene_name);
		if (p_scene)
		{
			matching_names = true;
		}
		else
		{
			p_scene = Nx::CEngine::sGetMainScene();
		}

		if (p_scene)
		{
			Nx::CTexDict *p_tex_dict = p_scene->GetTexDict();

			Nx::CEngine::sUnloadScene(p_scene);
			// must unload dictionary after scene
			if (p_tex_dict)
			{
				Nx::CTexDictManager::sUnloadTextureDictionary(p_tex_dict);
			}
		}
	}

	return matching_names;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptQuickReload(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *p_scene_name = NULL;
	pParams->GetText( "scene",&p_scene_name );
	if (p_scene_name)
	{
		Nx::CEngine::sQuickReloadGeometry(p_scene_name);
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptCreateFromNode( Script::CStruct *pNode )
{	
//	Dbg_MsgLog(("CreateFromNodeIndex nodeIndex=%d",nodeIndex));
	
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	Obj::CRailManager *p_rail_man = skate_mod->GetRailManager();
	Dbg_MsgAssert(p_rail_man,("Missing rail manager"));					 
					 
//	Script::CArray *pNodeArray = Script::GetArray( CRCD(0xc472ecc5,"NodeArray") );
//	Dbg_MsgAssert( pNodeArray,( "No NodeArray found" ));

	// We still need the node index for some things (rails, proxim)
	// nut now we get it from the node
	int nodeIndex = 0;
	pNode->GetInteger(CRCD(0xe50d6573,"NodeIndex"), &nodeIndex);

//	Script::CStruct *pNode=pNodeArray->GetStructure( nodeIndex );
	Dbg_MsgAssert( pNode,( "NULL pNode" ));

	// If this is a net game, don't load objects that were meant to be left out for 
	// performance/bandwidth/gameplay reasons
	if ( skate_mod->ShouldBeAbsentNode( pNode ) )
	{
		return true;
	}

#ifdef __NOPT_ASSERT__
	Mem::Allocator* pCurrentHeapContext = Mem::Manager::sHandle().GetContextAllocator();
	if ( !( pCurrentHeapContext == Mem::Manager::sHandle().CutsceneBottomUpHeap()
		 || pCurrentHeapContext == Mem::Manager::sHandle().BottomUpHeap() ) )
	{
		// GJ:  this function used to push the bottom up heap context explicitly,
		// but it was causing problems with the cutscene code, which
		// wants to put stuff on the cutscene heap.  however, there's
		// no reason to push the bottom up heap, since the bottom up heap
		// was always the default context anyway...  i think.
		Dbg_MsgAssert( 0, ( "Was expecting bottomupheap context" ) );
	}
#endif

	/*
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	*/

	uint32 ClassChecksum = 0;
	pNode->GetChecksum( 0x12b4e660 /*"Class"*/, &ClassChecksum );

	// check for obsolete trigger objects first
	if ( ClassChecksum == 0xe594f0a2 )	// trigger
	{
		uint32 checksumName = 0;
		if ( pNode->GetChecksum( 0xa1dc81f9, &checksumName ) ) // checksum 'name'
		{
#ifdef __NOPT_ASSERT__
			printf("WARNING:  Trigger objects are obsolete %s\n", Script::FindChecksumName(checksumName) );
#endif		
		}
	}

	bool success = true;

	switch (ClassChecksum)
	{
		case 0xe47f1b79: //vehicle
			Dbg_MsgLog(("Vehicle"));
			Mem::PushMemProfile("Vehicles");
			Obj::CreateCar( skate_mod->GetObjectManager(), pNode );
			Mem::PopMemProfile();
			break;
	
		case 0xa0dfac98:  //pedestrian
		case 0x061a741e:  //ped
			{
				Dbg_MsgLog(("Pedestrian"));
				Mem::PushMemProfile("Pedestrians");
				Obj::CreatePed( skate_mod->GetObjectManager(), pNode );
				Mem::PopMemProfile();
			}
			break;

		case 0xef59c100:  //gameobject
			{
				Dbg_MsgLog(("gameobject"));
				Mem::PushMemProfile("Game Objects");
				Obj::CreateGameObj( skate_mod->GetObjectManager(), pNode );
				Mem::PopMemProfile();
			}
			break;
			
		case 0x19b1e241: // ParticleEmitter
			{
				Dbg_MsgLog(("ParticleEmitter"));
				Mem::PushMemProfile("Particle Systems");
				Obj::CreateParticleEmitter( skate_mod->GetObjectManager(), pNode );
				Mem::PopMemProfile();
			}
			break;
		case 0x9e7d469e: // ParticleObject
			{
				Dbg_MsgLog(("ParticleObject"));
				Mem::PushMemProfile("Particle Systems");
				Obj::CreateParticleObject( skate_mod->GetObjectManager(), pNode );
				Mem::PopMemProfile();
			}
			break;

		case 0xb7b3bd86:  // LevelObject
			{
				Dbg_MsgLog(("LevelObject"));
				// create a level object here
				Mem::PushMemProfile("LevelObjects");
				Obj::CreateLevelObj( skate_mod->GetObjectManager(), pNode );
				Mem::PopMemProfile();
				// Note, we used to turn off the original sector here
				// this is now done to all LevelObjects in ParseNodeArray
			}
			break;

//		case 0xe594f0a2:  // Trigger (geometry containing triggers...)
		case 0xbf4d3536:  // LevelGeometry
		{
			Dbg_MsgLog(("LevelGeometry"));
			uint32 checksumName = 0;
			if ( pNode->GetChecksum( 0xa1dc81f9, &checksumName ) ) // checksum 'name'
			{
//				Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksumName);
				// We only want to get sectors from the main scene, as that is what the node array applies to															
				Nx::CScene * p_main_scene = Nx::CEngine::sGetMainScene();
				Nx::CSector *p_sector = p_main_scene->GetSector(checksumName);
				if (p_sector)
				{
					Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",checksumName,Script::FindChecksumName(checksumName)));
					p_sector->SetVisibility(0xffffffff);
					p_sector->SetActive(true);
					p_sector->SetCollidable(true);

					// If this node is an occluder, let the occlusion code know that this occluder is now enabled.
					if( pNode->ContainsFlag( 0x4e549fe1 /*"Occluder"*/ ))
					{
						Nx::CEngine::sEnableOcclusionPoly( checksumName, true );
					}
				}
				else
				{
					printf(" WARNING: sGetSector(0x%x) returned NULL (%s)\n",checksumName,Script::FindChecksumName(checksumName));
				}
				//Bsp::ClearWorldSectorFlag( checksumName, mSD_KILLED | mSD_NON_COLLIDABLE | mSD_INVISIBLE  | mSD_INVISIBLE2);
			}
			break;
 		}

		case 0x8e6b02ad:  // railnode
			Dbg_MsgLog(("railnode"));
			p_rail_man->SetActive( nodeIndex, true, pNode->ContainsFlag( "FLAG_WHOLE_RAIL" ) );
			break;
		case CRCC(0x30c19600, "ClimbingNode"):
			Dbg_MsgLog(("climbingnode"));
			p_rail_man->SetActive( nodeIndex, true, pNode->ContainsFlag( "FLAG_WHOLE_RAIL" ) );
			break;
		case 0x8470f2e:  // proximnode
			Dbg_MsgLog(("proximnode"));
			Obj::Proxim_SetActive( nodeIndex, true );
			break;			
		case 0xd64dc7c2:  // emitterobject
			Dbg_MsgLog(("emitterobject"));
			Obj::CEmitterManager::sSetEmitterActive( nodeIndex, true );
			break;			
		default:
			Dbg_MsgLog(("default"));
			success = false;
			break;
	}
//	Mem::Manager::sHandle().PopContext();
	return success;
}


bool ScriptCreateFromNodeIndex( int nodeIndex )
{	
	return ScriptCreateFromNode(SkateScript::GetNode(nodeIndex));
}


struct MaybeKillObjectInfo
{
	uint32 nodeNum;
	Obj::CObject *forbiddenObject;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void MaybeKillObject( Obj::CObject *pOb, void *pVoidData )
{
	
	Dbg_MsgAssert(pOb,("NULL pOb"));
	Dbg_MsgAssert(pVoidData,("NULL pVoidData"));

	MaybeKillObjectInfo *pInfo;
	pInfo = ( MaybeKillObjectInfo * )pVoidData;
	
	uint32 nodeNum = pInfo->nodeNum;
	uint32	nodeName = SkateScript::GetNodeNameChecksum(nodeNum);
	
	if ( pOb->GetID() == nodeName )
	{
		Dbg_MsgAssert( pOb != pInfo->forbiddenObject,( "Can't issue a kill command from within the script of the object you're killing:  Use Die command." ));
		pOb->DestroyIfUnlocked( );
//		pOb->MarkAsDead();
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptKillFromNodeIndex( int nodeIndex, Script::CScript *pScript )
{			
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
			
	Obj::CRailManager *p_rail_man = skate_mod->GetRailManager();
	Dbg_MsgAssert(p_rail_man,("Missing rail manager"));					 
					     
	Script::CArray *pNodeArray = Script::GetArray( CRCD(0xc472ecc5,"NodeArray") );
	Dbg_MsgAssert( pNodeArray,( "No NodeArray found" ));

	Script::CStruct *pNode=pNodeArray->GetStructure( nodeIndex );
	Dbg_MsgAssert( pNode,( "NULL pNode" ));

	uint32 ClassChecksum = 0;
	pNode->GetChecksum( 0x12b4e660 /*"Class"*/, &ClassChecksum );


									
	switch (ClassChecksum)
	{
		case 0xe47f1b79: // vechicle
		case 0xa0dfac98: // pedestrian
		case 0x061a741e: // ped
		case 0xef59c100: // gameobject
		case 0x19b1e241: // ParticleEmitter
		case 0x9e7d469e: // ParticleObject
		case 0xb7b3bd86: // LevelObject
		case 0x5b8ab877: // skater
		{
			MaybeKillObjectInfo info;
			info.forbiddenObject = pScript->mpObject;
			info.nodeNum = nodeIndex;
			
			skate_mod->GetObjectManager()->ProcessAllObjects( MaybeKillObject, &info );
			break;
		}

		case CRCC(0x30c19600, "ClimbingNode"):
			p_rail_man->SetActive( nodeIndex, false, pNode->ContainsFlag( "FLAG_WHOLE_RAIL" ) );
			break;		

		case 0x8e6b02ad:  // railnode
			p_rail_man->SetActive( nodeIndex, false, pNode->ContainsFlag( "FLAG_WHOLE_RAIL" ) );
			break;		

		case 0x8470f2e:  // proximnode
			Obj::Proxim_SetActive( nodeIndex, false );
			break;			

		case 0xd64dc7c2:  // emitterobject
			Obj::CEmitterManager::sSetEmitterActive( nodeIndex, false );
			break;			

//		case 0xe594f0a2:  // Trigger (geometry containing triggers...)
		case 0xbf4d3536:  // LevelGeometry
		{
			uint32 checksumName = 0;
			if ( pNode->GetChecksum( 0xa1dc81f9, &checksumName ) ) // checksum 'name'
			{
//				Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksumName);				
				// We only want to get sectors from the main scene, as that is what the node array applies to															
				Nx::CScene * p_main_scene = Nx::CEngine::sGetMainScene();
				Nx::CSector *p_sector = p_main_scene->GetSector(checksumName);
				Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",checksumName,Script::FindChecksumName(checksumName)));
				p_sector->SetActive(false);
				//Bsp::SetWorldSectorFlag(  checksumName, mSD_KILLED );

				// If this node is an occluder, let the occlusion code know that this occluder is no longer enabled.
				if( pNode->ContainsFlag( 0x4e549fe1 /*"Occluder"*/ ))
				{
					Dbg_MsgAssert(!ScriptInSplitScreenGame(NULL, NULL),
								  ("Can't turn occlusion polys on or off during a 2 player game"));
					Nx::CEngine::sEnableOcclusionPoly( checksumName, false );
				}
			}
			break;
 		}

		default:
			return ( false );
			break;
	}
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptKillFromNodeNameChecksum( uint32 nodeNameChecksum, Script::CScript *pScript )
{

	return ( ScriptKillFromNodeIndex( SkateScript::FindNamedNode( nodeNameChecksum ), pScript ) );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool DoNodeActionFromNodeNum( int nodeNum, int action, Script::CScript *pScript, Script::CStruct *pParams )
{
    GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

// In net games, check if the node is flagged to be absent
// and just ignore the command it fo	
	if( gamenet_man->InNetMode())
	{
		// Rather inefficient.  We should get away from refering to nodes by index
		if (skate_mod->ShouldBeAbsentNode( SkateScript::GetNode(nodeNum) ) )
		{
			// just ignore the action
			return false;
		}
	}

	// note rather dodgy way of determining the viewport of a script
	// if it has an object, and tht object is the second skater, 
	// and we have two viewports
	// use viewport 1, otherwise default to viewport 0	
	int viewport_number = 0;
	if (pScript->mpObject && !gamenet_man->InNetGame() && Nx::CViewportManager::sGetNumActiveViewports() == 2)
	{
		Obj::CSkater *pSkater = skate_mod->GetSkater(1);
		if (pScript->mpObject == pSkater )		
		{
			viewport_number = 1;
		}
	}
	
	
    switch ( action )
	{
		case ( NODE_ACTION_CREATE ):
			return ( ScriptCreateFromNodeIndex( nodeNum ) );
		case ( NODE_ACTION_KILL ):
			return ( ScriptKillFromNodeIndex( nodeNum, pScript ) );
		case ( NODE_ACTION_SET_VISIBLE ):
			return ( ScriptSetVisibilityFromNodeIndex( nodeNum, false, viewport_number ) );
		case ( NODE_ACTION_SET_INVISIBLE ):
			return ( ScriptSetVisibilityFromNodeIndex( nodeNum, true, viewport_number ) );
		case ( NODE_ACTION_SET_COLOR ):
			return ( ScriptSetColorFromNodeIndex( nodeNum, pParams ) );
		case NODE_ACTION_SHATTER:
		{   
			ScriptSetVisibilityFromNodeIndex( nodeNum, false, viewport_number );
			return ScriptShatterFromNodeIndex( nodeNum );
		}
		case NODE_ACTION_CHECK_IF_ALIVE:
		{
			return ( ScriptCheckExistenceFromNodeIndex( nodeNum ) );
		}
		default:
			Dbg_MsgAssert( 0,( "Fire Matt." ));
			return ( false );
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Used to perform actions to nodes by prefix, name checksum, or links... General version
// (applicable to nodes that create Environmental Objects, Rails, and all types of CMovingObjects.
bool DoNodeAction( Script::CStruct *pParams, Script::CScript *pScript, int action )
{
	
	uint32 nameChecksum;
	uint32 prefixChecksum;
	//const char *pPrefix;
	int i;
	bool useCurrentLinks = false;
	if ( pParams->ContainsFlag( 0x2e7d5ee7 ) || // checksum 'links'
		( useCurrentLinks = pParams->ContainsFlag( 0xbceb479a ) ) ) // checksum 'currentlinks'
	{
		Dbg_MsgAssert(0,("%s:  DoNodeAction with links or currentlinks is deprecated",pScript->GetScriptInfo()));
	
		/*
		int numLinks;
		Script::CStruct *pNode = NULL;
		if ( pParams->GetChecksumOrStringChecksum( 0xa1dc81f9, &nameChecksum ) ) // checksum 'name'
		{
			if ( useCurrentLinks )
			{
				Dbg_Message( "\n%s\nWarning:  The 'currentLinks' flag only works in an object script on the calling object.", pScript->GetScriptInfo( ) );
			}
			pNode = SkateScript::GetNode( SkateScript::FindNamedNode( nameChecksum ) );
			Dbg_MsgAssert( pNode,( "\n%s\nNo node named %s found", pScript->GetScriptInfo( ), Script::FindChecksumName( nameChecksum ) ));
		}
		else
		{
			if ( pScript->mNode != -1 )
			{
				pNode = SkateScript::GetNode( pScript->mNode );
				Dbg_MsgAssert( pNode,( "\n%s\nnode %d not found", pScript->GetScriptInfo( ), pScript->mNode ));
			}
			else
			{
				if ( pScript->mpObject )
				{
					if ( useCurrentLinks )
					{
						pNode = SkateScript::GetNode( ((Obj::CMovingObject *) pScript->mpObject.Convert())->m_current_node );
						Dbg_MsgAssert( pNode,( "\n%s\n object's node %d is invalid", pScript->GetScriptInfo( ), ((Obj::CMovingObject *) pScript->mpObject.Convert())->m_start_node ));
					}
					else
					{
						pNode = SkateScript::GetNode( ((Obj::CMovingObject *) pScript->mpObject.Convert())->m_start_node );
						Dbg_MsgAssert( pNode,( "\n%s\n object's node %d is invalid", pScript->GetScriptInfo( ), ((Obj::CMovingObject *) pScript->mpObject.Convert())->m_start_node ));
					}
				}
				else
				{
					Dbg_MsgAssert( 0,( "\n%s\nNo valid nodes to be found anywherest, dumass.", pScript->GetScriptInfo( ) ));
					return ( false );
				}
			}
		}
		Dbg_MsgAssert( pNode,( "What the fuck?  How did this motherfuckin' happen?" ));
		numLinks = SkateScript::GetNumLinks( pNode );
		for ( i = 0; i < numLinks; i++ )
		{
			if ( DoNodeActionFromNodeNum( SkateScript::GetLink( pNode, i ), action, pScript ) )
			{
				if ( action == NODE_ACTION_CHECK_IF_ALIVE )
				{
					return ( true );
				}
			}
		}
		*/

		return ( true );
	}
    //if ( pParams->GetText( 0x6c4e7971, &pPrefix ) ) // checksum 'prefix'
    if ( pParams->GetChecksumOrStringChecksum( 0x6c4e7971, &prefixChecksum ) ) // checksum 'prefix'
	{
		// Create with a prefix specified:
		uint16 numNodes = 0;
	  //  const uint16 *pMatchingNodes = SkateScript::GetPrefixedNodes( pPrefix, &numNodes );
		const uint16 *pMatchingNodes = SkateScript::GetPrefixedNodes( prefixChecksum, &numNodes );
		for ( i = 0; i < numNodes; i++ )
		{
			if ( DoNodeActionFromNodeNum( pMatchingNodes[ i ], action, pScript, pParams ) )
			{
				if ( action == NODE_ACTION_CHECK_IF_ALIVE )
				{
					return ( true );
				}
			}
		}
		return ( true );
	}
	if ( pParams->GetChecksumOrStringChecksum( 0xa1dc81f9, &nameChecksum ) ) // checksum 'name'
	{
		// Create with a name specified:
		return ( DoNodeActionFromNodeNum( SkateScript::FindNamedNode( nameChecksum ), action, pScript, pParams ) );
	}

	// if they're calling "Invisible" with no parameters, intending to turn 
	// visibility off on an object from within the object's script, they should
	// be calling "Obj_Invisible" (or Obj_Visible, or Die, create not applicable
	// since the object would have to have  already been created to be calling
	// something.)
	Dbg_Message( "\n%s\nSyntax not recognized... object script may need to call Obj_ version. (Ask Matt).", pScript->GetScriptInfo( ) );
	return ( false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptNodeExists( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 node_checksum=0;
	pParams->GetChecksum(NONAME,&node_checksum,true);
	
	return SkateScript::NodeExists(node_checksum);
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptCreateFromStructure( Script::CStruct *pParams, Script::CScript *pScript )
{
	return ScriptCreateFromNode( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Create | creates an object node
// @flag links | pass in to perform action to nodes by links
// @flag currentlinks | pass in to perform action to nodes by links
// @parmopt name | name | | The name of the node
// @parmopt name | prefix | | Create with a prefix specified
bool ScriptCreate( Script::CStruct *pParams, Script::CScript *pScript )
{

 #ifdef	__PLAT_NGPS__		
//		snProfSetRange( -1, (void*)0, (void*)-1);
//		snProfSetFlagValue(0x01);
 #endif


	// Flush dead objects before we create any new ones
	ScriptFlushDeadObjects(NULL,NULL);	
	
	bool result = ( DoNodeAction( pParams, pScript, NODE_ACTION_CREATE ) );

 #ifdef	__PLAT_NGPS__		
//		snProfSetRange( 4, (void*)NULL, (void*)-1);
 #endif		
 
	return result;

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetObjectColor | color the object a particular color
// @parm name | name | The name of the piece of level geometry to change color
// @parm int | color | The color to set the object to (as a hexadecimal ABGR value)
bool ScriptSetObjectColor( Script::CStruct *pParams, Script::CScript *pScript )
{

	bool result = ( DoNodeAction( pParams, pScript, NODE_ACTION_SET_COLOR ) );
	
	return result;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Kill | kills an object node
// @flag links | pass in to perform action to nodes by links
// @flag currentlinks | pass in to perform action to nodes by links
// @parmopt name | name | | The name of the node
// @parmopt name | prefix | | kill with a prefix specified
bool ScriptKill( Script::CStruct *pParams, Script::CScript *pScript )
{

	return ( DoNodeAction( pParams, pScript, NODE_ACTION_KILL ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Visible | make object visible
// @flag links | pass in to perform action to nodes by links
// @flag currentlinks | pass in to perform action to nodes by links
// @parmopt name | name | | The name of the node
// @parmopt name | prefix | | act on objects with specified prefix
bool ScriptVisible( Script::CStruct *pParams, Script::CScript *pScript )
{
	return ( DoNodeAction( pParams, pScript, NODE_ACTION_SET_VISIBLE ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Invisible | make object invisible
// @flag links | pass in to perform action to nodes by links
// @flag currentlinks | pass in to perform action to nodes by links
// @parmopt name | name | | The name of the node
// @parmopt name | prefix | | act on objects with specified prefix
bool ScriptInvisible( Script::CStruct *pParams, Script::CScript *pScript )
{
	return ( DoNodeAction( pParams, pScript, NODE_ACTION_SET_INVISIBLE ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Shatter | Shatter an object
// @parmopt float | area | 288.0 | the subdivision area above which
// polys will be recursively subdivided. Be careful with this
// parameter - making it too small will cause the buffer to
// overflow and assert. The units are square inches 
// The default value may change
// @parmopt float | variance | 0.0 | the amount the velocity
// of each shard can be (randomly) above the supplied velocity. 
// So a value of 1.0 would mean a shard could travel at twice the supplied 
// velocity  The default value may change.
// @parmopt float | spread | 1.0 | determines the degree to which the
// shards will fly apart. The way I figure this is to take the center
// of the bounding box for the object, and then move backwards (spread
// * velocity). This position is then used to figure the start velocity
// for each shard. So the lower the spread value, the less the start
// point moves back, and the more the shards will fly apart. (I can
// draw a diagram if this isn't clear). 
// The default value may change
// @parmopt float | life | 4.0 | how long the shatter atomic will exist for
// (seconds)  The default value may change
// @parmopt | bounce | -10,000 | Default value may change
// @parmopt | scale | 1.0 | Scaling factor for the shards
// @flag use_skater_vel | Use skater velocity to determine
// if object should shatter
// @parm | vel_x | default velocity of shard on x-axis (in inches per second)
// @parm | vel_y | default velocity of shard on y-axis (in inches per second) 
// @parm | vel_z | default velocity of shard on z-axis (in inches per second)
bool ScriptShatter( Script::CStruct *pParams, Script::CScript *pScript )
{
	// Need to read some params from the script here.
	float		area		= 0.0f;
	float		variance	= 0.0f;
	float		spread		= 0.0f;
	float		life		= 0.0f;
	float		bounce		= 0.0f;
	float		bounce_amp	= 0.0f;
	float		scale		= 1.0f;
	Mth::Vector	velocity;

	if( pParams->ContainsFlag( 0x9f98989e /*"use_skater_vel"*/ ))
	{
		pParams->GetFloat( "scale", &scale );

		Obj::CSkater* p_skater = static_cast ( pScript->mpObject.Convert() );
		Dbg_Assert( p_skater );

		velocity = p_skater->GetVel() * scale;
	}
	else
	{
		// Have to supply vel_x, y, and z.
		pParams->GetFloat( "vel_x", &velocity[X], true );
		pParams->GetFloat( "vel_y", &velocity[Y], true );
		pParams->GetFloat( "vel_z", &velocity[Z], true );
		velocity[W] = 0.0f;	// To prevent assert on using unitialized vectors 
	}

	pParams->GetFloat( "area",			&area );
	pParams->GetFloat( "variance",		&variance );
	pParams->GetFloat( "spread",		&spread );
	pParams->GetFloat( "life",			&life );
	pParams->GetFloat( "bounce",		&bounce );
	pParams->GetFloat( "bounce_amp",	&bounce_amp );

	//////////////////////////////////////////////////////////////////////////////////////////
	// Mick 09/18/02:  PATCH
	// setting the shatter area too low (generally to 1000) generates an excessive amount
	// of polygons in the form of immediate mode primitive, which eat up the DMA buffer on the PS2
	// So I'm going to limit the area to 2000 in the general case, and to 5000 in multi player games
	float min_area = 2000.0f;				// single player limit
	if (Mdl::Skate::Instance()->IsMultiplayerGame())
	{
		min_area = 5000.0f;
	}
	
	if (area < min_area)
	{
		#ifdef	__NOPT_ASSERT__
		printf("WARNING:  area %.1f too small for THPS4 PS2 engine, clamping to %.1f\n",area,min_area);
		#endif
		area = min_area;
	}
	// END PATCH
	//////////////////////////////////////////////////////////////////////////////////////////

	// Set up values for this shatter.
	Nx::ShatterSetParams( velocity, area, variance, spread, life, bounce, bounce_amp );

	return DoNodeAction( pParams, pScript, NODE_ACTION_SHATTER );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	CleanupBeforeParseNodeArray()
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	Obj::Proxim_Init();					
	Obj::CEmitterManager::sInit();
	skate_mod->GetRailManager()->Cleanup();		// probably not needed, as will be done in ScriptCleanup
	skate_mod->GetTrickObjectManager()->DeleteAllTrickObjects();

	// Scene lights are created during the node array parse, so clear them up here ready.
	Nx::CLightManager::sClearSceneLights();
	Nx::CLightManager::sClearVCLights();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ParseNodeArray | Parses the loaded node array 
bool ScriptParseNodeArray( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mem::PushMemProfile("ParseNodeArray");

	CleanupBeforeParseNodeArray();	

	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	// Scan through the node array creating all things that need to be created.
	Script::CArray *pNodeArray=Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
	Dbg_MsgAssert(pNodeArray,("No NodeArray found in ParseNodeArray"));

	// Rails are now added seperately
	Mem::PushMemProfile("Rail Nodes");
	Obj::CRailManager *p_rail_man = skate_mod->GetRailManager();
	Dbg_MsgAssert(p_rail_man,("Missing rail manager"));					 
	p_rail_man->AddRailsFromNodeArray(pNodeArray );					 									  
	Mem::PopMemProfile();
	
	// Nav nodes are added separately.
#	ifdef TESTING_GUNSLINGER
	Mem::PushMemProfile( "Nav Nodes" );
	Obj::CNavManager* p_nav_man = skate_mod->GetNavManager();
	Dbg_MsgAssert( p_nav_man, ( "Missing Nav Manager" ));
	p_nav_man->AddNavsFromNodeArray( pNodeArray );
	Mem::PopMemProfile();
#	endif

	// We only want to get sectors from the main scene, as that is what the node array applies to															
	Nx::CScene * p_main_scene = Nx::CEngine::sGetMainScene();
									  
	bool	fail = false;
	uint32 i;	 
	for (i=0; iGetSize(); ++i)
	{
		Script::CStruct *pNode=pNodeArray->GetStructure(i);
		Dbg_MsgAssert(pNode,("NULL pNode"));

		uint32 ClassChecksum = 0;
		pNode->GetChecksum( 0x12b4e660 /*"Class"*/, &ClassChecksum );


		// First the nodes that need some special processing
		// for the "net game" state
		switch (ClassChecksum)
		{						   
			// Note - Rails are no longer added here, but in the AddRailsFromNodeArray, above
			
			case 0xb7b3bd86:  // LevelObject
			{
				// A "LevelObject" is like "LevelGeometry", in that it is a 3DStudio Max object
				// which is part of the scene (like, a wall or a house)
				// However, it's going to be movable, so it has to exist in "local" coordinate
				// space, centered around the origin.
				// When it is exported, the original geometry is just tranlated to the origin, and left there
				// later we will create a clone of this object, which will be be movable instance
				// but the original is left alone. So, we always turn off the original instance
				// of a LevelObject, before we create any instances. The creation is done via
				// ScriptCreateFromNodeIndex, either below (if createdAtStart), 
				// or via a script command at some indeterminate time in the future.
				uint32 checksumName = 0;
				pNode->GetChecksum( 0xa1dc81f9 /*Name*/, &checksumName);
//				Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksumName);
				Nx::CSector *p_sector = p_main_scene->GetSector(checksumName);
				if (p_sector)
				{
						p_sector->SetActive(false);
				}
				

				break;
			}
			
			case 0x8e6b02ad:  // railnode
			{
				pNode->RemoveComponent(CRCD(0x9d2d0915,"angles"));
				pNode->RemoveComponent(CRCD(0x7321a8d6,"type")); 	// Type is no longer used, as "TerrainType" suffices
				break;
			}

			case CRCC(0x30c19600, "ClimbingNode"):
			{
				break;
			}
			
			case 0xbf4d3536:  // LevelGeometry
			{
				
				////////////////////////////////////////////////////////////////////////////////////
				// Test:  if it's a level object, then remove the angles, collisionmode and position
				pNode->RemoveComponent(CRCD(0x2d7e583b,"collisionmode"));
//				pNode->RemoveComponent(CRCD(0xb9d31b0a,"position"));
//				pNode->RemoveComponent(CRCD(0x9d2d0915,"angles"));
				////////////////////////////////////////////////////////////////////////////////////

				
				// first get the sector indicated by this level geometry
				uint32 checksumName = 0;
				pNode->GetChecksum( 0xa1dc81f9 /*Name*/, &checksumName);
				if (checksumName == 0)
				{
					Script::PrintContents(pNode);
					printf("FAILED BECAUSE Node %d is LevelGeometry, but has no Name=....\n",i);
					fail = true;
				}
				//Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksumName);
				Nx::CSector *p_sector = p_main_scene->GetSector(checksumName);
				
				//Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",checksumName,Script::FindChecksumName(checksumName)));
				
				if (p_sector)
				{
					 
					// The park editor takes care of it's own visibility								
					// 

					   
					if (Ed::CParkEditor::Instance()->UsingCustomPark()) 		// is it a custom park???
					{
						if ( !( pNode->ContainsFlag( 0x7c2552b9/*"CreatedAtStart"*/  ) ) || skate_mod->ShouldBeAbsentNode( pNode ) )
						{
							// if this assertion fires, you are probably try to extend the park
							// editor to have some sectors be not "createdatstart"
							// which is not currently supported, as the park editor sets up it;s
							// own visibility and "active" state for things like:
							//   - restart nodes
							//   - the cursor
							//   - gap pieces
							// so you would need to re-work it so they were correctly flagged 
							// as no created at start, and then remove this whole "UsingCustomPark" special case
							// This is also done in the logic around the call to ScriptCreateFromNodeIndex, below, in this function.
							Dbg_MsgAssert(0,("Park ed levelgeom not set created at start"));
						}					
					}
					else
					{
						// The sector will already be active and visible, since no flags will be set...
						// but if we're not created at start, set the non-collide and invisible flags:
						if ( !( pNode->ContainsFlag( 0x7c2552b9/*"CreatedAtStart"*/  ) ) || skate_mod->ShouldBeAbsentNode( pNode )
							 )
						{
							p_sector->SetActive(false);
						}
						else
						{
							// Mick: as ParseNodeArray can get called multiple times
							// on the same node array
							// we need to clear any KILLED and INVIVIBLE flags
							// that might have been set in the previous session
							p_sector->SetVisibility(0xffffffff);
							p_sector->SetActive(true);
							p_sector->SetCollidable(true);
						}
					}
					
					// If it's an occluder, then we need to flag it as such
					if (  pNode->ContainsFlag( 0x4e549fe1/*"Occluder"*/   )	)
					{
//						printf ("Occluding node %d, <%s>\n",i,Script::FindChecksumName(checksumName));
						// This object is an occluder
						// so need to flag it as so
						p_sector->SetOccluder(true);				
						
						// For now, we make all occluders invisble, so we can see through them
						//printf ("WARNING:   Occluders are being made invisible!!!!!!!\n");											
						//printf ("WARNING:   Occluders are being made invisible!!!!!!!\n");											
						//printf ("WARNING:   Occluders are being made invisible!!!!!!!\n");											
						p_sector->SetActive(false);

						// If this occluder is not created at start, disable it.
						if( !pNode->ContainsFlag( 0x7c2552b9 /*"CreatedAtStart"*/  ))
						{
							Nx::CEngine::sEnableOcclusionPoly( checksumName, false );
						}
					}
					
					// Set the light group (usually will be "outdoor")
					uint32	light_group = CRCD(0xe3714dc1,"outdoor");			 // default to OutDoors
					pNode->GetChecksum(CRCD(0x7e4813a7,"lightgroup"),&light_group);
					p_sector->SetLightGroup(light_group);
				}
				else
				{
					// Only fail in non-multiplayer games
					// as it's quite valid for a sector to not exist in a network game
					// as they are stripped out at SceneConv time 
					if (!skate_mod->IsMultiplayerGame())
					{
						//printf("sGetSector(0x%x) returned NULL (%s)",checksumName,Script::FindChecksumName(checksumName));
						printf("FAILED BECAUSE: Cannot find level geometry or collision for node %d \nMaybe there are spaces in or after the name of the Max object (%s)?\nOr maybe the .QN file does not match the .SCN file checked into Perforce?\n"
									   ,i,Script::FindChecksumName(checksumName));
						fail = true;
					}
				}
	
				bool is_trick_object = pNode->ContainsComponentNamed( "TrickObject" );
	
				if (!Config::CD())
				{
					bool debug_graffiti = Script::GetInt( "create_all_trick_objects", false );
					if ( debug_graffiti )
						is_trick_object = true;
				}		

				// Only add trick objects is they are not flagged as absentinnetgames
				if ( !skate_mod->ShouldBeAbsentNode( pNode ) && is_trick_object )
				{
					Mem::PushMemProfile("Trick Object Clusters");
					// get the node name
					uint32 checksumName;
					pNode->GetChecksum( "name", &checksumName, true );
	
					uint32 clusterName = checksumName;
					pNode->GetChecksum( "Cluster", &clusterName, false );
					skate_mod->GetTrickObjectManager()->AddTrickCluster( clusterName );
						
					// add the environment object to this cluster
					skate_mod->GetTrickObjectManager()->AddTrickObjectToCluster( checksumName, clusterName );
					Mem::PopMemProfile();
				}
				break;
			}
			default:
				break;
		}	
		
		// The following nodes are capable of being ignored in net games		
		
		if ( skate_mod->ShouldBeAbsentNode( pNode ) )
		{
			// Don't load, as it's a net game
		}	
		else 
		{
		
			if (ClassChecksum == 0xbf4d3536 && Ed::CParkEditor::Instance()->UsingCustomPark())  // LevelGeometry
			{
				// Level geometry in park editor is ignored, see above.
			}
			else
			{
				
				if (ClassChecksum == 0x8470f2e) // checksum 'ProximNode'
				{
					Mem::PushMemProfile("Proximity Nodes");
					Obj::Proxim_AddNode( i, pNode->ContainsFlag( 0x7c2552b9/*"CreatedAtStart"*/ ) );
					Mem::PopMemProfile();
				}
				
				if (ClassChecksum == 0xd64dc7c2) // checksum 'EmitterObject'
				{
					Mem::PushMemProfile("Emitter Objects");
					Obj::CEmitterManager::sAddEmitter( i, pNode->ContainsFlag( 0x7c2552b9/*"CreatedAtStart"*/ ) );
					Mem::PopMemProfile();
				}

				if ( pNode->ContainsFlag( 0x7c2552b9 /*"CreatedAtStart"*/ ) )
				{
					ScriptCreateFromNodeIndex( i );
				}
			}
		}				
	}	
	
	Dbg_MsgAssert(!fail, ("ParseNodeArray FAILED, see above for reasons labled FAILED BECAUSE \n"));
	
	Obj::Proxim_AddedAll();
	p_rail_man->AddedAll();
	Mdl::Skate::Instance()->GetGapChecklist()->FindGaps();

	/*
	// STUPID HACK
	Ed::ParkEditor* park_ed = Ed::ParkEditor::Instance();
	if (park_ed->IsInitialized() && park_ed->GameGoingOrOutsideEditor())
		park_ed->MakeStuffInvisibleForGameplay();
	*/
	
// Mick:
// Level lights need to be created AFTER the node array is parsed, so that the 
// lightgroups are set on the level geometry
// otherwise geometry nodes that occur after this one will default to "outdoor"	

	for (i=0; iGetSize(); ++i)
	{
		Script::CStruct *pNode=pNodeArray->GetStructure(i);
		Dbg_MsgAssert(pNode,("NULL pNode"));

		uint32 ClassChecksum = 0;
		pNode->GetChecksum( 0x12b4e660 /*"Class"*/, &ClassChecksum );


		// First the nodes that need some special processing
		// for the "net game" state
		if (ClassChecksum == CRCD(0xa0e52802,"LevelLight"))
		{						   
			// Will need to check a parameter here to see if the light is flagged to affect objects.
			bool affects_objects	= !pNode->ContainsFlag( CRCD( 0xe1509e4f, "ExcludeSkater" ));
			bool affects_geometry	= !pNode->ContainsFlag( CRCD( 0x6f050e18, "ExcludeLevel" ));

			// Get the name checksum of this node, since this will be used as the link between the node and the Scene Light.
			uint32 name_checksum = SkateScript::GetNodeNameChecksum( i );

			Mth::Vector pos;
			SkateScript::GetPosition( pNode, &pos );

			// Default values.
			float intensity		= 100.0f;
			float inner_radius	= 300.0f;
			float outer_radius	= 300.0f;

			pNode->GetFloat( CRCD( 0x2689291c, "Brightness" ), &intensity );	
			pNode->GetFloat( CRCD( 0x8d2c287f, "InnerRadius" ), &inner_radius );
			pNode->GetFloat( CRCD( 0x833950cc, "OuterRadius" ), &outer_radius );

			if( !pNode->ContainsFlag( CRCD( 0x7c2552b9, "CreatedAtStart" )))
			{
				// This light is not supposed to be active at startup.
			}

			// Lights default to pure white in the absence of any color information.
			Image::RGBA col( 0xFF, 0xFF, 0xFF, 0x00 );

			Script::CArray *p_array;
			pNode->GetArray( CRCD( 0x99a9b716, "Color" ), &p_array );
			if( p_array )
			{
				col.r = p_array->GetInteger( 0 );
				col.g = p_array->GetInteger( 1 );
				col.b = p_array->GetInteger( 2 );
			}

			if( affects_objects )
			{
				Nx::CSceneLight *p_new_light = Nx::CLightManager::sAddSceneLight();
				if( p_new_light )
				{
					p_new_light->SetNameChecksum( name_checksum );
					p_new_light->SetLightPosition( pos );

					// Set the color, radius and intensity.
					p_new_light->SetLightIntensity( intensity * 0.01f );
					p_new_light->SetLightColor( col );
					p_new_light->SetLightRadius( outer_radius );
				}
				else
				{
					Dbg_MsgAssert(0,("Too many Scene lights"));
				}
			}

			if( affects_geometry )
			{
				Nx::CLightManager::sAddVCLight(name_checksum,pos,intensity,col,outer_radius);
			}
		}
	}	
	

	Mem::PopMemProfile(/*"ParseNodeArray"*/);

	return true;
}	

static char LastNodeArrayLoaded[128];

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadNodeArray | Will load node array from the file specified 
// @uparm "string" | File name to load
bool ScriptLoadNodeArray(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mem::PushMemProfile("LoadNodeArray");
	
	// Remove any spawned scripts that might have been left over from the previous level.
	Script::DeleteSpawnedScripts();
		
	
	const char *pFileName=NULL;
	pParams->GetText(NONAME,&pFileName);
	Dbg_MsgAssert(pFileName,("LoadNodeArray requires a file name."));

	strcpy(LastNodeArrayLoaded,pFileName);

	// Load the QB
	SkateScript::LoadQB(pFileName);
	
	// Ensure it has a node array in it
	Script::CArray* pNodeArray=Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
	Dbg_MsgAssert(pNodeArray,("No NodeArray found in %s",pFileName));

	for ( uint32 i = 0; i < pNodeArray->GetSize(); i++ )
	{
		Script::CStruct* pStructure = pNodeArray->GetStructure(i);
		pStructure->AddInteger("nodeIndex", i);
	}

	if (pParams->ContainsFlag("park_editor"))
	{
		// PARKED4:
		// Nothing needs doing here, we don't preload any models in the park editor
	}
	else
	{
		// not a park editor QB, parse as normal
//		ScriptPreloadModels( NULL, NULL );
	}
	Mem::PopMemProfile(/*"LoadNodeArray"*/);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ReLoadNodeArray | Will reload the last node array you loaded, 
// without parsing it, and without deleting anything. 
// Note, this will load directly from the .qb, so there is NO NEED to rebuild the .pre files. 
// Using this command by itself could be used if you are just altering 
// stuff like trigger scripts.  However, as it does not respawn anything, 
// you have the potential to mess things up.  Give it a go though, you can 
// always just do a Select+Circle to make sure. 
bool ScriptReLoadNodeArray(Script::CStruct *pParams, Script::CScript *pScript)
{	
	
	// We try to use the Debug heap, as we assume we will be fragmenting memory like a jackass
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
	
	const char *pFileName=LastNodeArrayLoaded;
	// Load the QB
	SkateScript::LoadQB(pFileName);
	
	// Ensure it has a node array in it
	Script::CArray *pNodeArray=Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
	
	Dbg_MsgAssert(pNodeArray,("No NodeArray found in %s",pFileName));
		
	// renumber node indices
	for ( uint32 i = 0; i < pNodeArray->GetSize(); i++ )
	{
		Script::CStruct* pStructure = pNodeArray->GetStructure(i);
		pStructure->AddInteger("nodeIndex", i);
	}
	
	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetBackgroundColor | Sets the background color. Each component 
// is in the range 0-255.  The alpha value currently is not used. 
// @parmopt int | r | 0 | Red component
// @parmopt int | g | 0 | Green component
// @parmopt int | b | 0 | Blue component
// @parmopt int | alpha | 0 | Alpha value
bool ScriptSetBackgroundColor(Script::CStruct *pParams, Script::CScript *pScript)
{
	    
	int Red=0;
	int Green=0;
	int Blue=0;
	int Alpha=0;
	pParams->GetInteger("r",&Red);
	pParams->GetInteger("g",&Green);
	pParams->GetInteger("b",&Blue);
	pParams->GetInteger("alpha",&Alpha);
    
/*	
	gfx_man->sBackgroundColor.r 	= Red;
	gfx_man->sBackgroundColor.g = Green;
	gfx_man->sBackgroundColor.b 	= Blue;
	gfx_man->sBackgroundColor.a = Alpha;   
*/
	printf ("STUBBED ScriptSetBackgroundColor\n");	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetBSPAmbientColor | Sets the ambient color for the BSP 
// (The environment, or the non-moving objects). 
// NOTE:  This should be set to 0,0,0, as any other setting will result 
// in much slowdown.  However, you can set it temporarily if you 
// want to try out effects quickly. 
// @parmopt int | r | 0 | Red component
// @parmopt int | g | 0 | Green component
// @parmopt int | b | 0 | Blue component
// @parmopt int | alpha | 0 | Alpha value
bool ScriptSetBSPAmbientColor(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_Message( "SetBSPAmbientColor is obsolete" );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetDFFAmbientColor | Sets the ambient color for the DFFs 
// (The skater, cars and suchlike.).
// @parmopt int | r | 0 | Red component
// @parmopt int | g | 0 | Green component
// @parmopt int | b | 0 | Blue component
// @parmopt int | alpha | 0 | Alpha value
bool ScriptSetDFFAmbientColor(Script::CStruct *pParams, Script::CScript *pScript)
{			
	Dbg_Message( "SetDFFAmbientColor is obsolete" );
	return true;
}
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetDFFDirectColor | Sets the directional light color 
// for the DFFs (The skater, cars and suchlike.).   (Note:  temporary, 
// until we get a better lighting setup). 
// @parmopt int | r | 0 | Red component
// @parmopt int | g | 0 | Green component
// @parmopt int | b | 0 | Blue component
// @parmopt int | alpha | 0 | Alpha value
bool ScriptSetDFFDirectColor(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_Message( "SetDFFDirectColor is obsolete" );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetDynamicLightModulationFactor | A value of 0.0 means no 
// effect (light/dark polys have no effect). 
// A value of 1.0 means the maximum possible effect from light/dark polys 
// These values will stay set until changed, so designers will want to
// put these in their level startup scripts. 
// @parm float | value | 
// @parmopt flag | ambient | | 
// @parmopt bool | directional | | 
bool ScriptSetDynamicLightModulationFactor( Script::CStruct* pParams, Script::CScript* pScript )
{
	

	bool	ok;
	int		index	= 0;
	float	value	= 0.0f;
	if( pParams->GetFloat( "value", &value ))
	{
		if( pParams->ContainsFlag( "ambient" ))
		{
			Nx::CLightManager::sSetAmbientLightModulationFactor(value);
		}
		else
		{
			ok = pParams->GetInteger( "directional", &index );
			Nx::CLightManager::sSetDiffuseLightModulationFactor(index, value);
		}
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetClippingDistances | Sets the clipping distances in inches. 
// Near should generally be left at 12, Far should be whatever is 
// appropriate for your level. Example: 
// SetClippingDistances Near=12 Far=8000  
// @parm float | Near |
// @parm float | Far | 
bool ScriptSetClippingDistances(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	float Near=Gfx::Camera::vDEFAULT_FARZ;
	float Far=Gfx::Camera::vDEFAULT_NEARZ;
	pParams->GetFloat("Near",&Near);
	pParams->GetFloat("Far",&Far);

	// This is a bit of a patch,
	// cycle over the two possible cameras, and set the clipping dist
	for (int cam = 0; cam < 2; cam ++)
	{
		
		Gfx::Camera *p_cam = Nx::CViewportManager::sGetCamera(cam);
		if (p_cam)
		{
			p_cam->SetNearFarClipPlanes(Near,Far);
		}
	}
	//Gfx::Camera::SetNearFarClipPlanes(Near,Far);


	// Also Gamecube uses different near and far z clip planes, and has a fog far plane independent of the
	// camera far plane. So indicate here what the fog far plane for Gamecube should be.
//#	ifdef __PLAT_NGC__
//	NsRender::setFogFar( Far );
//#	endif // __PLAT_NGC__



	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetTrivialFarZClip | Sets the Z clipping distance 
// SetTrivialFarZClip on        --- to turn it on
// SetTrivialFarZClip off       --- to turn it off 
// @flag on | Pass this in to set the trivial Z clipping
// distance to true or false
bool ScriptSetTrivialFarZClip(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	bool on = false;

	if( pParams->ContainsFlag( "on" ))
	{
		on = true;
	}

	 Gfx::Manager * gfx_manager =  Gfx::Manager::Instance();
	gfx_manager->SetTrivialFarZClip( on );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EnableFog | 
bool ScriptEnableFog( Script::CStruct *pParams,
					  Script::CScript *pScript )
{
	Nx::CFog::sEnableFog(true);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DisableFog | 
bool ScriptDisableFog( Script::CStruct *pParams,
					  Script::CScript *pScript )
{
	Nx::CFog::sEnableFog(false);

	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetFogDistance | Sets the "near" distance (in inches) 
// at which fogging will begin.  (Polys closer than the near distance
// will not be fogged.  Beyond the near distance, they will become
// increasingly fogged until they reach maximum fogging at the far
// clip distance -- see SetClippingDistances).
// @parm float | distance | distance in inches
bool ScriptSetFogDistance( Script::CStruct *pParams,
						   Script::CScript *pScript )
{
	
	float fog_dist = 0.0f;
	pParams->GetFloat( "distance", &fog_dist );

	Nx::CFog::sSetFogNearDistance(fog_dist);

	// Garrett: Not sure if this is needed anymore, but leaving it there
	// in case some other platforms need it.
	// This is a bit of a patch,
	// cycle over the two possible cameras, and set the clipping dist
	for ( int cam = 0; cam < 2; cam++ )
	{
		Gfx::Camera *p_cam = Nx::CViewportManager::sGetCamera(cam);
		if ( NULL != p_cam )
		{
			p_cam->SetFogNearPlane( fog_dist );
		}
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetFogExponent | Sets the fog exponent.  The exponent
// value determines the "spread" of the fog, or how far out you
// must go before you reach maximum fog.  This component of fog
// will probably change in the near future.
// @parm float | exponent | fog exponent
bool ScriptSetFogExponent( Script::CStruct *pParams,
						   Script::CScript *pScript )
{
	float exponent;
	if (!pParams->GetFloat( "exponent", &exponent ))
	{
		Dbg_MsgAssert(0, ("Can't find parameter 'exponent'"));
	}

	Nx::CFog::sSetFogExponent(exponent);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetFogColor | Sets the color that will be used for
// atmospheric fogging.  Color components are in the range 0-255. 
// The alpha component is in the range of 0-128.
// @parmopt int | r | 255 | red component
// @parmopt int | g | 255 | green component
// @parmopt int | b | 255 | blue component
// @parmopt int | a | 128 | alpha component (opacity of fog)
bool ScriptSetFogColor( Script::CStruct *pParams,
						Script::CScript *pScript )
{
	
	int red = 255;
	int green = 255;
	int blue = 255;
	int alpha = 128;
	pParams->GetInteger( "r", &red );
	pParams->GetInteger( "g", &green );
	pParams->GetInteger( "b", &blue );
	pParams->GetInteger( "a", &alpha );

	Image::RGBA rgba(red, green, blue, alpha);

	Nx::CFog::sSetFogRGBA( rgba );
		
	return true;
}

// @script | SetUVWibbleParams | Sets the UV wibble parameters of a sector
// @parm name | sector | name of sector
// @parm float | u_vel   | u velocity
// @parm float | u_amp   | u amplitude
// @parm float | u_freq  | u frequency
// @parm float | u_phase | u phase
// @parm float | v_vel   | v velocity
// @parm float | v_amp   | v amplitude
// @parm float | v_freq  | v frequency
// @parm float | v_phase | v phase
bool ScriptSetUVWibbleParams(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Get sector checksum
	uint32 checksum;
	if (!pParams->GetChecksum("sector", &checksum))
	{
		Dbg_MsgAssert(0, ("Can't find parameter sector"));
	}

	// Find sector
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksum);
	Dbg_MsgAssert(p_sector, ("Can't find sector %x in world", checksum));

	// Extract all the parameters
    float u_velocity;
    float u_amplitude;
    float u_frequency;
    float u_phase;
    float v_velocity;
    float v_amplitude;
    float v_frequency;
    float v_phase;
	bool found_all;

	found_all  = pParams->GetFloat("u_vel", &u_velocity);
	found_all &= pParams->GetFloat("u_amp", &u_amplitude);
	found_all &= pParams->GetFloat("u_freq", &u_frequency);
	found_all &= pParams->GetFloat("u_phase", &u_phase);
	found_all &= pParams->GetFloat("v_vel", &v_velocity);
	found_all &= pParams->GetFloat("v_amp", &v_amplitude);
	found_all &= pParams->GetFloat("v_freq", &v_frequency);
	found_all &= pParams->GetFloat("v_phase", &v_phase);

	Dbg_MsgAssert(found_all, ("Missing one or more of the wibble parameters.  Must fill them all out."));

	p_sector->SetUVWibbleParams(u_velocity, u_amplitude, u_frequency, u_phase, v_velocity, v_amplitude, v_frequency, v_phase);

	return true;
}

// @script | EnableExplicitUVWibble | Enables explicit setting of the UV wibble offsets.
// To set the offsets, call SetUVWibbleOffsets.
// @parm name | sector | name of sector
bool ScriptEnableExplicitUVWibble(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Get sector checksum
	uint32 checksum;
	if (!pParams->GetChecksum("sector", &checksum))
	{
		Dbg_MsgAssert(0, ("Can't find parameter sector"));
	}

	// Find sector
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksum);
	Dbg_MsgAssert(p_sector, ("Can't find sector %x in world", checksum));

	p_sector->UseExplicitUVWibble(true);

	return true;
}

// @script | DisableExplicitUVWibble | Disables explicit setting of the UV wibble offsets.
// @parm name | sector | name of sector
bool ScriptDisableExplicitUVWibble(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Get sector checksum
	uint32 checksum;
	if (!pParams->GetChecksum("sector", &checksum))
	{
		Dbg_MsgAssert(0, ("Can't find parameter sector"));
	}

	// Find sector
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksum);
	Dbg_MsgAssert(p_sector, ("Can't find sector %x in world", checksum));

	p_sector->UseExplicitUVWibble(false);

	return true;
}

// @script | SetUVWibbleOffsets | Sets the UV wibble offsets explicitly
// @parm name | sector | name of sector
// @parm float | u_off | u offset
// @parm float | v_off | v offset
bool ScriptSetUVWibbleOffsets(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Get sector checksum
	uint32 checksum;
	if (!pParams->GetChecksum("sector", &checksum))
	{
		Dbg_MsgAssert(0, ("Can't find parameter sector"));
	}

	// Find sector
	Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksum);
	Dbg_MsgAssert(p_sector, ("Can't find sector %x in world", checksum));

	// Extract all the parameters
    float u_offset;
    float v_offset;
	bool found_all;

	found_all  = pParams->GetFloat("u_off", &u_offset);
	found_all &= pParams->GetFloat("v_off", &v_offset);

	Dbg_MsgAssert(found_all, ("Missing one or more of the wibble offset parameters.  Must fill them all out."));

	p_sector->SetUVWibbleOffsets(u_offset, v_offset);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetPreferenceString(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Script::CStruct* pStructure;
	Prefs::Preferences* pPreferences;
	uint32 field_id, pref_type;
	const char* ui_string;
	CStruct* pass_back_params;
    
	pass_back_params = pScript->GetParams();

	pParams->GetChecksum( NO_NAME, &field_id );
	pParams->GetChecksum( "pref_type", &pref_type );

	pPreferences = Mdl::GetPreferences(pref_type); // This will assert if it cannot find the prefs
	
	pStructure = pPreferences->GetPreference( field_id );
	Dbg_MsgAssert(pStructure, ("\n%s\nCan't get preference 0x%x (%s)",pScript->GetScriptInfo(),field_id, Script::FindChecksumName(field_id)));
	
	//pParams->AddStructure( "preference", pStructure );
	pStructure->GetString( "ui_string", &ui_string );
	pass_back_params->AddString( "ui_string", ui_string );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetPreferencePassword(Script::CStruct *pParams, Script::CScript *pScript)
{
	Script::CStruct* pStructure;
	Prefs::Preferences* pPreferences;
	uint32 field_id, pref_type;
	const char* ui_string;
	CStruct* pass_back_params;
	static char	s_password_string[32];
	uint32 i;
    
	pass_back_params = pScript->GetParams();

	pParams->GetChecksum( NO_NAME, &field_id );
	pParams->GetChecksum( "pref_type", &pref_type );

	pPreferences = Mdl::GetPreferences(pref_type); // This will assert if it cannot find the prefs

	pStructure = pPreferences->GetPreference( field_id );
	//pParams->AddStructure( "preference", pStructure );
	pStructure->GetString( "ui_string", &ui_string );
	for( i = 0; i < strlen( ui_string ); i++ )
	{
		s_password_string[i] = '*';
	}
	
	Dbg_Assert( i < 32 );
	s_password_string[i] = '\0';

	pass_back_params->AddString( "password_string", s_password_string );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetPreferenceChecksum(Script::CStruct *pParams, Script::CScript *pScript)
{
	Script::CStruct* pStructure;
	Prefs::Preferences* pPreferences;
	uint32 field_id, checksum, pref_type;
	CStruct* pass_back_params;
    
	pass_back_params = pScript->GetParams();

	pParams->GetChecksum( NO_NAME, &field_id );
	pParams->GetChecksum( "pref_type", &pref_type );

	pPreferences = Mdl::GetPreferences(pref_type); // This will assert if it cannot find the prefs

	pStructure = pPreferences->GetPreference( field_id );
	//pParams->AddStructure( "preference", pStructure );
	pStructure->GetChecksum( "checksum", &checksum );
	pass_back_params->AddChecksum( "checksum", checksum );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetPreference | sets the prefs to those specified in
// the params structure
// @parm name | field | the field id
// @parm structure | params | 
bool ScriptSetPreference(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	// gets either network or splitscreen prefs, as appropriate
	Prefs::Preferences* pPreferences = Mdl::GetPreferences( pParams );
	
	uint32 field_id;
	pParams->GetChecksum( "field", &field_id, true );

	Script::CStruct* pSubParams;
	pParams->GetStructure( "params", &pSubParams, true );
	pPreferences->SetPreference( field_id, pSubParams );

#ifdef __NOPT_ASSERT__
	Script::PrintContents(pSubParams);
#endif
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PreferenceEquals | returns true if the actual preference value
// for the given field equals the test value
// @parm name | field | the field id
// @parm name | equals | test value
bool ScriptPreferenceEquals(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	// gets either network or splitscreen prefs, as appropriate
	Prefs::Preferences* pPreferences = Mdl::GetPreferences( pParams );

	uint32 field_id;
	pParams->GetChecksum("field", &field_id, true);

	uint32 test_value;
	pParams->GetChecksum( "equals", &test_value, true );

	Script::CStruct* pStructure = pPreferences->GetPreference( field_id );
	Dbg_Assert(pStructure);

	uint32 actual_value;
	pStructure->GetChecksum( "checksum", &actual_value, true );

	return ( test_value == actual_value );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetCamera | 
bool ScriptResetCamera(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	// This is a bit of a patch,
	// cycle over the two possible cameras, and reset them
	for (int cam = 0; cam < 2; cam ++)
	{
		
		Gfx::Camera *p_cam = Nx::CViewportManager::sGetCamera(cam);
		if (p_cam)
		{
			printf ("TODO: ScriptResetCamera not implemented\n");
		}
	}
	//Gfx::Camera::SetNearFarClipPlanes(Near,Far);
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetVolumeFromValue | sets the volume according to the slider
// @uparm sfxvol | the volume to set (either sfxvol or cdvol)
// @parm name | id | the slider id used to change the volume
bool ScriptSetVolumeFromValue( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	/*
	uint32 whichParam;
	pParams->GetChecksum( NONAME, &whichParam, true );

	uint32 slider_id;
	pParams->GetChecksum( "id", &slider_id, true );

	Front::MenuFactory* menu_factory = Front::MenuFactory::Instance();
	Front::SliderMenuElement* pSliderElement = static_cast( menu_factory->GetMenuElement( slider_id, true ) );
	Dbg_Assert( pSliderElement );

	switch ( whichParam )
	{
		case 0x8d6f6554: // "sfxvol"
		{
			 Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
			sfx_manager->SetMainVolume( ( float )( 10 * pSliderElement->GetValue( ) ) );
		}
		break;

		case 0xa5162bd2: // "cdvol"
		{
			// still set the main music volume even if in the front end!!!
			Pcm::SetVolume( ( float )( 10 * pSliderElement->GetValue( ) ) );

			// make sure the music is playing and shit...
			Pcm::PauseMusic( false );
		}
		break;

		default:
		{
			// unrecognized sound parameter
			Dbg_MsgAssert( 0, ( "Unrecognized sound parameter" ) );
		}
		break;
	}
	*/
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetValueFromVolume | 
// @uparm sfxvol | the volume you're interested in (either sfxvol, cdvol, or cutvol)
bool ScriptGetValueFromVolume( Script::CStruct *pParams, Script::CScript *pScript )
{
	

	uint32 whichParam;
	pParams->GetChecksum( NONAME, &whichParam, true );

	switch ( whichParam )
	{
		case 0x8d6f6554: // "sfxvol"
		{
			 Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
			pScript->GetParams()->AddComponent( Script::GenerateCRC( "value" ), ESYMBOLTYPE_INTEGER, ( (int) sfx_manager->GetMainVolume() ) / 10 );
		}
		break;
		
		case 0xa5162bd2: // "cdvol"
		{
			pScript->GetParams()->AddComponent( Script::GenerateCRC( "value" ), ESYMBOLTYPE_INTEGER, ( (int) Pcm::GetVolume() ) / 10 );
		}
		break;

		case 0xe32f3525: // "cutvol"
		{
			pScript->GetParams()->AddComponent( Script::GenerateCRC( "value" ), ESYMBOLTYPE_INTEGER, ( (int) Pcm::GetMusicStreamVolume() ) / 10 );
		}
		break;

		default:
		{
			// unrecognized sound parameter
			Dbg_MsgAssert( 0, ( "Unrecognized sound parameter" ) );
		}
		break;
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSliderValue | 
// @parm name | id | the slider id
// @parm int | value | value to assign to slider
bool ScriptSetSliderValue( Script::CStruct *pParams, Script::CScript *pScript )
{
	

	/*
	uint32 slider_id;
	pParams->GetChecksum( "id", &slider_id, true );

	int value;
	pParams->GetInteger( "value", &value, true );
	
	Front::MenuFactory* menu_factory = Front::MenuFactory::Instance();
	Front::SliderMenuElement* pSliderElement = static_cast( menu_factory->GetMenuElement( slider_id, true ) );
	Dbg_Assert( pSliderElement );
	pSliderElement->SetValue( value );
	*/

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetIconTexture | 
// @parm name | id | menuelement
// @parm string | texture | the texture name
bool ScriptSetIconTexture(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	/*
	uint32 id;
	pParams->GetChecksum("id", &id, true);
	
	Front::MenuFactory* menu_factory = Front::MenuFactory::Instance();
	Front::IconMenuElement* pIconElement = static_cast( menu_factory->GetMenuElement( id, true ) );

	if ( pIconElement )
	{
		const char* textureName;
		pParams->GetText("texture", &textureName, true);
		pIconElement->SetTexture( textureName );
	}
	*/

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


// @script | SetMovementVelocity | Sets the movement (translational) 
// speed of the camera in the viewer. The number is in terms of inches per second. 
// @parmopt float | vel | 400 | velocity in inches per second
bool ScriptSetMovementVelocity( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	float vel = 400;
	pParams->GetFloat( NONAME, &vel );

	Mdl::CViewer::sSetMovementVelocity( vel );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetRotateVelocity | Sets the rotational speed of the 
// camera in the viewer. The number is in terms of degrees per second. 
// @parmopt float | vel | 120 | Rotational speed in degrees per second
bool ScriptSetRotateVelocity( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	float vel = 120;
	pParams->GetFloat( NONAME, &vel );

	Mdl::CViewer::sSetRotateVelocity( vel );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | OnReload | 
// This is a script function that doesn't do anything, it is just used to indicate what should
// happen if the script gets reloaded at runtime. It needs to be the first 
bool ScriptOnReload(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetEngine | resets the engine
bool ScriptResetEngine(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	 Mlp::Manager * mlp_manager =  Mlp::Manager::Instance();
	mlp_manager->QuitLoop();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleMetrics | Toggle metrics - use before ToggleMemMetrics
bool ScriptToggleMetrics(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	 Gfx::Manager * gfx_manager =  Gfx::Manager::Instance();
	gfx_manager->ToggleMetrics();
    //Script::RunScript( "show_poly_count" );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleNetMetrics | 
bool ScriptToggleNetMetrics(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	 GameNet::Manager * gamenet_manager =  GameNet::Manager::Instance();
	gamenet_manager->ToggleMetrics();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleVRAMViewer | Toggle VRAM viewer
bool ScriptToggleVRAMViewer(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	 Gfx::Manager * gfx_manager =  Gfx::Manager::Instance();
	gfx_manager->ToggleVRAMViewer();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DumpVRAMUsage | Spits out a file detailing VRAM usage 
// separately for all four contexts
bool ScriptDumpVRAMUsage(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	 Gfx::Manager * gfx_manager =  Gfx::Manager::Instance();
	gfx_manager->DumpVRAMUsage();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleLightViewer | Toggle the light viewer
bool ScriptToggleLightViewer(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_Message( "ToggleLightViewer is obsolete" );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | debugrendermode | currently unsupported
bool ScriptSetDebugRenderMode(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_Message( "debugrendermode is obsolete" );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | debugtoggletextureupload | currently unsupported
bool ScriptToggleTextureUpload(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_Message( "debugtoggletextureupload is obsolete" );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | debugtoggletexturedraw | currently unsupported
bool ScriptToggleTextureDraw(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_Message( "debugtoggletexturedraw is obsolete" );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | debugtogglepointdraw | currently unsupported
bool ScriptTogglePointDraw(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_Message( "debugtogglepointdraw is obsolete" );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | debugrendertest1 | old function?  returns true if mbr_nsDebugTest1
// (check cfuncs.cpp) is set
bool ScriptRenderTest1(Script::CStruct *pParams, Script::CScript *pScript)
{
	
#ifdef __PLAT_NGPS__
//	mbr_nsDebugTest1=(mbr_nsDebugTest1)?0:1;
#endif
	return true;
}
// @script | debugrendertest2 | old function?  returns true if mbr_nsDebugTest2
// (check cfuncs.cpp) is set
bool ScriptRenderTest2(Script::CStruct *pParams, Script::CScript *pScript)
{

#ifdef __PLAT_NGPS__
//	mbr_nsDebugTest2=(mbr_nsDebugTest2)?0:1;
#endif
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetViewMode | Debugging function to toggle the camera mode,
// switching between normal, free with fixed skater, and free with moving skater
// usually triggers with Select+X (See buttonscripts.q)
bool ScriptSetViewMode(Script::CStruct *pParams, Script::CScript *pScript)
{
	int viewMode;
	pParams->GetInteger( NONAME, &viewMode, Script::ASSERT );

	Dbg_Assert( viewMode >= 0 && viewMode < Obj::NUM_VIEW_MODES );

	s_view_mode = viewMode;
	Mdl::CViewer::sSetViewMode( s_view_mode );
	// Handle toggling to different cameras based on view mode
	
	switch (s_view_mode )
	{
		case Obj::GAMEPLAY_SKATER_ACTIVE:
			SetActiveCamera(CRCD(0x967c138c,"skatercam0"),0, false);			
			break;
		default:
			SetActiveCamera(CRCD(0xeb17151b,"viewer_cam"),0, true);						
			break;		
	}
	
		
	Obj::CSkater* pSkater;
	for ( int i = 0; i < 8; i++ )
	{
		 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
		pSkater = skate_mod->GetSkater(i);						   
		if ( pSkater )			// Skater might not exist
		{
			pSkater->SetViewMode( (Obj::EViewMode)s_view_mode );
		}
	}
	
	// clear out the viewer object, if any
	Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();
	if ( pViewer )
	{
		pViewer->RemoveViewerObject();   	
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleMetricItem | ToggleMetricItem will turn on or off 
// individual items in the metric display.
// The item parameter is one of: 
// METRIC_TIME 
// METRIC_ARENAUSAGE             
// METRIC_TOTALPOLYS 
// METRIC_POLYSPROC 
// METRIC_VERTS 
// METRIC_RESOURCEALLOCS 
// METRIC_TEXTUREUPLOADS 
// METRIC_VU1 
// METRIC_DMA1 
// METRIC_DMA2 
// METRIC_VBLANKS 
// METRIC_DRAWTIME 
// METRIC_IHANDLERTIME 
// METRIC_SKYCACHE 
// METRIC_VIDEOMODE 
// METRIC_VRAMUSAGE 
// METRIC_MEMUSED 
// METRIC_MEMFREE 
// METRIC_REGIONINFO 
// @parmopt int | item | 0 | 
bool ScriptToggleMetricItem( Script::CStruct *pParams, Script::CScript *pScript )
{

	
/*
	uint32 metricFlags = 0;
	pParams->GetInteger( "item", ( int * )&metricFlags );

	 Gfx::Manager * gfx_manager =  Gfx::Manager::Instance();

	Gfx::Metrics* pMetrics = gfx_manager->GetMetrics();
	pMetrics->ToggleMetricItem( metricFlags );
*/
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleMemMetrics | This will replace the PS2 specific
// metrics with a list of the all the memory heaps, together with
// the amount of fragmentation, and the amount of free memory. Note, 
// you have to use ToggleMetrics to actually see the metrics before
// using ToggleMemMetrics 
// See the online doc for a full explanation of the various heaps
bool ScriptToggleMemMetrics( Script::CStruct *pParams, Script::CScript *pScript )
{
	/* 
	 Gfx::Manager * gfx_manager =  Gfx::Manager::Instance();
	Gfx::Metrics* pMetrics = gfx_manager->GetMetrics();
	pMetrics->ToggleMemMetrics();
	*/
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// @script | KillAllTextureSplats | Destroys all exisiting texture splats
bool ScriptKillAllTextureSplats( Script::CStruct *pParams, Script::CScript *pScript )
{
	Nx::KillAllTextureSplats();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ProximCleanup | Destroys all proxim nodes
bool ScriptProximCleanup(Script::CStruct *pParams, Script::CScript *pScript)
{
	// TT12597: Clear out the proxim node pointers in the stream and sound components
	// new fast way, just go directly to the components, if any
	Obj::CSoundComponent* p_sound_component = static_cast( Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRC_SOUND ));
	while( p_sound_component )
	{
		p_sound_component->ClearProximNode();
		
		p_sound_component = static_cast( p_sound_component->GetNextSameType());
	}

	Obj::CStreamComponent* p_stream_component = static_cast( Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRC_STREAM ));
	while( p_stream_component )
	{
		p_stream_component->ClearProximNode();
		
		p_stream_component = static_cast( p_stream_component->GetNextSameType());
	}

	Obj::Proxim_Cleanup();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Cleanup | Destroys current world, texture dictionaries, objects and railnodes.
// @flag preserve_skaters | Set this to preserve the skaters
bool ScriptCleanup(Script::CStruct *pParams, Script::CScript *pScript)
{
	//	DumpUnwindStack( 20, 0 );
	// Wait for any async rendering to finish before we proceed	
	Nx::CEngine::sFinishRendering();
 	#ifdef	__PLAT_NGPS__
	NxPs2::AllocateExtraDMA(false);
	#endif

	Replay::DeallocateReplayMemory();
	
// goalmanager stuff moved here from Skate::ChangeLevel
    Game::CGoalManager* p_GoalManager = Game::GetGoalManager();
    Dbg_MsgAssert( p_GoalManager, ( "couldn't get GoalManager\n" ) );
    
    p_GoalManager->DeactivateAllGoals();
    p_GoalManager->RemoveAllGoals();

	// PARKED4:
	Ed::CParkEditor* p_park_editor = Ed::CParkEditor::Instance();
	p_park_editor->Cleanup();
	p_park_editor->DeletePlayModeGapManager();
	
	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();	
	bool preserve;
	bool was_spawned;

	Dbg_Printf( "Performing Cleanup\n" );

	Gfx::DebuggeryLines_CleanUp( );	// so we remove debug lines between levels

	preserve = false;
	if( pParams )
	{
		preserve = pParams->ContainsFlag("preserve_skaters");
	}	

// ********************************************************************************************
// ********************************************************************************************
// ********************************************************************************************
	// A hacky way of preserving the running script, since skate's cleanup will destroy all
	// spawned scripts.
	was_spawned = false;
	if( pScript )
	{
		was_spawned = pScript->mIsSpawned;
		pScript->mIsSpawned = false;
	}
	// Delete's session objects managed by the "Skate::" code module	
	skate_mod->Cleanup( );   
	if( pScript )
	{
		pScript->mIsSpawned = was_spawned;
	}
	
	// Destroy other Session Objects
// These can be recreated, no assets are unloaded

	//skate_mod->GetGoalManager()->RemoveAllGoals();	

	Dbg_Printf( "Destroying imposters..." );
	Nx::CEngine::sGetImposterManager()->Cleanup();

	Dbg_Printf( "Destroying misc fx..." );
	Nx::MiscFXCleanup();

	Dbg_Printf( "Clearing node-name hash table ..." );
	SkateScript::ClearNodeNameHashTable();

	// Turn off fogging...let the next level turn it back on if it wants it
	Nx::CFog::sEnableFog(false);

	Script::KillStoppedScripts();


	Dbg_Printf( "Cleaning up terrain soundFX manager." );
	Env::CTerrainManager::sReset();
	Dbg_Printf( "Done.\n" );

				   
// ********************************************************************************************
// ********************************************************************************************
// ********************************************************************************************
// Actual unloading of assets starts here 
	
// unload sond effects (just resets the SRAM pointer)				   
	Dbg_Printf( "Cleaning up soundFX manager." );
	 Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
	sfx_manager->CleanUp( );		// Sfx manager uses a very primitive memory management system ... check it out..
	Dbg_Printf( "Done.\n" );

// Unload object models and animations													 
	printf ("unloading assman tables ...........\n");
	 Ass::CAssMan * ass_manager =  Ass::CAssMan::Instance();
	ass_manager->UnloadAllTables( );
	printf ("done unloading assman ...........\n");

// ********************************************************************************************
// ********************************************************************************************
// ********************************************************************************************
	Dbg_Printf( "Unloading NodeArray .QB (level .qn file)" );
	Script::CSymbolTableEntry *p_sym=Script::LookUpSymbol(CRCD(0xc472ecc5,"NodeArray"));
	if (p_sym)
	{
		SkateScript::UnloadQB(p_sym->mSourceFileNameChecksum);
	}	
	
// ********************************************************************************************
// ********************************************************************************************
// ********************************************************************************************
// Unload the level specific qb.
	// Note: It's ok if s_level_specific_qb is 0, UnloadQB just won't do anything if it can't find it.
	#if 0
	SkateScript::UnloadQB(s_level_specific_qb);
	s_level_specific_qb=0;
	#else
	for (int qb=0;qbGetLocalSkater())
		{
			Obj::CAnimationComponent* pAnimComponent = GetAnimationComponentFromObject( skate_mod->GetLocalSkater() );
			pAnimComponent->SetAnimArray( Script::GenerateCRC("animload_human") );
		}
		Mem::Manager::sHandle().PopContext();
		*/
	
	}
	else	
	{
		printf("Clean up without preserve skaters\n");
		//skate_mod->UnloadSkaters();
		// as we're leaving a level,
		// clean up any additional heaps we might have allocated for the other skaters...
		Mem::Manager::sHandle().DeleteSkaterHeaps();
	}


	// Unload any pip pre files that are no longer being used (eg, collision)
	const char *p_pre_name=Pip::GetNextLoadedPre();
	while (p_pre_name)
	{
		const char *p_next=Pip::GetNextLoadedPre(p_pre_name);
		if (!Pip::PreFileIsInUse(p_pre_name))
		{
			Pip::UnloadPre(p_pre_name);
		}
		p_pre_name=p_next;
	}	
		
	// unload the skater parts pre file if it is currently loaded
	File::PreMgr* pre_mgr = File::PreMgr::Instance();
	pre_mgr->UnloadPre( "skaterparts.pre", true );			
	
	// Clear away any old cameras
	Nx::CViewportManager::sDeleteMarkedCameras();

	// Clear any currently running FakeLights commands.
	Nx::CLightManager::sClearCurrentFakeLightsCommand();

	// Clear away any non-permanent heaps
	Nx::FlushParticleTextures(false);
	
	
	// cleanup pathman memory
	Obj::CPathMan* pPathMan = Obj::CPathMan::Instance();
	pPathMan->DeallocateObjectTrackerMemory();

	if( skate_mod->ShouldAllocateNetMiscHeap())
	{
		Dbg_Printf( "****** LEVEL: 0x%x  0x%x 0x%x\n", skate_mod->m_requested_level, skate_mod->m_cur_level, skate_mod->m_prev_level );
		Mem::Manager::sHandle().InitNetMiscHeap();
	}
	else
	{
		Mem::Manager::sHandle().DeleteNetMiscHeap();
	}


	Mem::Manager& mem_man = Mem::Manager::sHandle();
	
	// destroy the custom heaps, if they exist
	// (TODO:  Move this to script, since it really
	// only applies to the skateshop)
	mem_man.DeleteNamedHeap( Script::GenerateCRC("preview"), false );

	#ifdef	__NOPT_ASSERT__
	// sanity checks for fragmentation
	Mem::Heap* heap = mem_man.BottomUpHeap();
	int fragmentation = heap->mFreeMem.m_count;
	
	#if 1	
	if (fragmentation > 10000)
	{
		// not guaranteed to do anything useful.... might crash the debugger (Mick)
		printf ("Dumping Fragments.....\n");
		MemView_DumpFragments(heap);
		printf ("Done Dumping Fragments.....\n");
	}
	#endif
	
	Dbg_MsgAssert(fragmentation < 10000, ("Excessive bottom up fragmentation (%d) after cleanup",fragmentation)); 
	
#ifndef __PLAT_NGC__
	heap = mem_man.TopDownHeap();
	fragmentation = heap->mFreeMem.m_count;
	if (fragmentation > 10000)
	{
		// not guaranteed to do anything useful.... might crash the debugger (Mick)
		printf ("Dumping Fragments.....\n");
		MemView_DumpFragments(heap);
		printf ("Done Dumping Fragments.....\n");
	}
	
	Dbg_MsgAssert(fragmentation < 10000, ("Excessive top down fragmentation (%d) after cleanup",fragmentation)); 
#endif		// __PLAT_NGC__
	#endif

#	if defined( __PLAT_XBOX__ ) && defined( __NOPT_ASSERT__ )
#	define AddStr(a,b)	( pstrOut += wsprintf( pstrOut, a, b ))
#	define KB			( 1024 )
	MEMORYSTATUS stat;
	char strOut[1024], *pstrOut;

    // Get the memory status.
    GlobalMemoryStatus( &stat );

    // Setup the output string.
    pstrOut = strOut;
//	AddStr( "%4d total kb of virtual memory.\n", stat.dwTotalVirtual / KB );
//	AddStr( "%4d  free kb of virtual memory.\n", stat.dwAvailVirtual / KB );
    AddStr( "%4d total kb of physical memory.\n", stat.dwTotalPhys / KB );
    AddStr( "%4d  free kb of physical memory.\n", stat.dwAvailPhys / KB );
//	AddStr( "%4d  percent of memory is in use.\n", stat.dwMemoryLoad );
    OutputDebugString( strOut );
#	endif
	
#	ifdef __PLAT_NGPS__
	if( skate_mod->ShouldAllocateInternetHeap())
	{
		Mem::Manager::sHandle().InitInternetHeap();
	}
	else
	{
		Mem::Manager::sHandle().DeleteInternetHeap();
	}
#	endif // __PLAT_NGPS__

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadQB | Loads the specified QB file.
// @uparm "data\scripts\ken.qb" | File name of the qb to load
// @uparmopt LevelSpecific | Whether this is the level specific script file
bool ScriptLoadQB(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *p_file_name=NULL;
	pParams->GetString(NONAME,&p_file_name);
	Dbg_MsgAssert(p_file_name,("LoadQB requires a file name."));

	if (pParams->ContainsFlag("LevelSpecific"))
	{
		#if 0
		if (s_level_specific_qb)
		{
			// Only one level specific qb can exist currently, so make sure
			// that the old one is removed, just in case someone did two
			// LoadQB's in a row.
			SkateScript::UnloadQB(s_level_specific_qb);
		}	
		s_level_specific_qb=Crc::GenerateCRCFromString(p_file_name);
		#else
		// Now allowing multiple level specific QBs, so 
		// just look for a free slot
		int qb;
		for (qb=0;qbGetString(NONAME,&p_file_name);
	Dbg_MsgAssert(p_file_name,("LoadQB requires a file name."));

	// Unload the QB
	uint32 qb_checksum=Crc::GenerateCRCFromString(p_file_name);
	SkateScript::UnloadQB(qb_checksum);

	// If it was the level specific qb, which is normally removed automatically
	// by ScriptCleanup above.
	// Since they removed it manually, reset s_level_specific_qb so that it is consistent.
	#if 0	
	if (qb_checksum==s_level_specific_qb)
	{
		s_level_specific_qb=0;
	}
	#else
	int qb;
	for (qb=0;qbGetInteger(CRCD(0x6835b854,"mode"),&mode);
	Nx::CEngine::sSetRenderMode(mode);	

	return true;
}

// @script | SetWireframeMode | 
bool ScriptSetWireframeMode(Script::CStruct *pParams, Script::CScript *pScript)
{
	int mode = 0;
	pParams->GetInteger(CRCD(0x6835b854,"mode"),&mode);
	Nx::CEngine::sSetWireframeMode(mode);	

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DebugRenderIgnore | 
// @parmopt int | ignore_1 | 0 | bitmask of face flags where we ignore this face if bit is 1
// @parmopt int | ignore_0 | 0 | bitmask of face flags where we ignore this face if bit is 0
bool ScriptDebugRenderIgnore(Script::CStruct *pParams, Script::CScript *pScript)
{

	int ignore_0 = 0;	
	int ignore_1 = 0;
	pParams->GetInteger("ignore_1",&ignore_1);
	pParams->GetInteger("ignore_0",&ignore_0);
						  
	Nx::CEngine::sSetDebugIgnore(ignore_1,ignore_0);	

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetVRAMPackContext | Currently, there are 4 possible 
// contexts, 0 through 3, with 0 being the default. There is a script 
// command SetVRAMPackContext which takes a number between 0 and 3. 
// All packing handled by the intelligent packer will then only pack 
// in blocks with a matching context.
// @uparmopt 0 | context number (int)
bool ScriptSetVRAMPackContext(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	int context = 0;
	pParams->GetInteger( NONAME, &context );

#	ifdef __PLAT_NGPS__
//	RpSkyNXSetVRAMPackContext( (uint32)context );
#	endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScreenShot | save a screenshot with specified filename
// @parmopt string | filename | screen | place to save screenshot
bool ScriptScreenShot(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *fileName;
	bool ret;

	ret = pParams->GetText( "filename", &fileName );
	if ( !ret )
		fileName = "screen";

	 Gfx::Manager * gfx_manager =  Gfx::Manager::Instance();
	gfx_manager->ScreenShot( fileName );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// @script | DumpShots | dump memory card screenshots to PC
bool ScriptDumpShots(Script::CStruct *pParams, Script::CScript *pScript)
{
	#ifdef	__PLAT_NGPS__
	Gfx::Manager * gfx_manager =  Gfx::Manager::Instance();
	gfx_manager->DumpMemcardScreeenshots( );
	#endif

	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IfDebugOn | returns true if debug on
bool ScriptIfDebugOn(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Dbg_MsgAssert(pScript,("NULL pScript ?"));
	pScript->SwitchOnIfDebugging();
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IfDebugOff | returns true if debug off
bool ScriptIfDebugOff(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Dbg_MsgAssert(pScript,("NULL pScript ?"));
	pScript->SwitchOffIfDebugging();
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CD | returns true if CD present
bool ScriptCD(Script::CStruct *pParams, Script::CScript *pScript)
{
	return Config::CD();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | NotCD | returns true if CD not present
bool ScriptNotCD(Script::CStruct *pParams, Script::CScript *pScript)
{
	return !Config::CD();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Bootstrap | returns true if this is a bootstrap demo
bool ScriptBootstrap(Script::CStruct *pParams, Script::CScript *pScript)
{
	return Config::Bootstrap();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CasArtist | returns true if cas_artist int is set anywhere
bool ScriptCasArtist(Script::CStruct *pParams, Script::CScript *pScript)
{
	bool is_cas_artist = Script::GetInt( "cas_artist", false );

	return is_cas_artist;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | NotCasArtist | returns true if cas_artist int is not set
bool ScriptNotCasArtist(Script::CStruct *pParams, Script::CScript *pScript)
{
	return !ScriptCasArtist( pParams, pScript );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Gunslinger | returns true if a Gunslinger build
bool ScriptGunslinger(Script::CStruct *pParams, Script::CScript *pScript)
{
#	ifdef TESTING_GUNSLINGER
	return true;
#	else
	return false;
#	endif
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

enum
{
	ACTION_SET_FLAG,
	ACTION_CLEAR_FLAG,
	ACTION_QUERY_FLAG,
	ACTION_SET_OBJECT_FLAG,    // different than the script flags.
	ACTION_CLEAR_OBJECT_FLAG,  // different than the script flags.
	ACTION_FLAG_EXCEPTION_BY_CHECKSUM,
	ACTION_SEE_IF_OBJECT_EXISTS,
};

struct SActionData
{
	int Flag;
	int Action;
	bool ReturnValue;
	bool ObjectFound;
};

struct SActionData_NameChecksum
{
	uint32 ObjectNameChecksum;
	SActionData actionData;
};

struct SActionData_NodeIndex
{
	int NodeIndex;
	SActionData actionData;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void DoAction( Obj::CCompositeObject* pOb, SActionData* pData )
{
	pData->ObjectFound = true;
	switch ( pData->Action )
	{
		case ( ACTION_SET_FLAG ):
			pOb->mScriptFlags |= ( 1 << pData->Flag );
			pData->ReturnValue = true;
			break;
		case ( ACTION_CLEAR_FLAG ):
			pOb->mScriptFlags &= ~( 1 << pData->Flag );
			pData->ReturnValue = true;
			break;
		case ( ACTION_QUERY_FLAG ):
			if ( pOb->mScriptFlags & ( 1 << pData->Flag ) )
			{
				pData->ReturnValue = true;
			}
			break;
		case ( ACTION_SET_OBJECT_FLAG ):
			pOb->SetFlags(((Obj::CObject *) pOb)->GetFlags() | ( pData->Flag ));	 // Note:  Flags are passed as a bitfield
			break;
		case ( ACTION_CLEAR_OBJECT_FLAG ):
			pOb->SetFlags(((Obj::CObject *) pOb)->GetFlags() & ~( pData->Flag ));
			break;
		case ( ACTION_FLAG_EXCEPTION_BY_CHECKSUM ):
			{
				pOb->SelfEvent((pData->Flag));
				pData->ReturnValue = true;
			}
			break;
		case ( ACTION_SEE_IF_OBJECT_EXISTS ):
			break;
		default:
			Dbg_MsgAssert( 0,( "Unknown action %d", pData->Action ));
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void Action_ByNameChecksum( Obj::CObject* pOb, void* pVoidData )
{
	
	Dbg_MsgAssert(pOb,("NULL pOb"));
	Dbg_MsgAssert(pVoidData,("NULL pVoidData"));
	
	SActionData_NameChecksum *pData=(SActionData_NameChecksum*)pVoidData;

	// Mick - find objects based either on their node name (soon to go away)
	// or by their ID (which is set to the node name for composite objects)	
	if ( pOb->GetID() == pData->ObjectNameChecksum )
	{
		DoAction( (Obj::CCompositeObject*)pOb, &pData->actionData );
	}
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void Action_ByNodeIndex(Obj::CObject* pOb, void* pVoidData)
{
	
	Dbg_MsgAssert(pOb,("NULL pOb"));
	Dbg_MsgAssert(pVoidData,("NULL pVoidData"));
	
	SActionData_NodeIndex *pData=(SActionData_NodeIndex*)pVoidData;

	uint32	nodeName = SkateScript::GetNodeNameChecksum(pData->NodeIndex);
	
	if (pOb->GetID() == nodeName )
	{
		DoAction( (Obj::CCompositeObject*)pOb, &pData->actionData );
	}	
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ClearFlag | clears a flag 
// @uparm 1 | the flag to clear
// @parmopt name | name | | the name of the object
// @parmopt string | prefix | | prefix value
bool ScriptClearFlag(Script::CStruct *pParams, Script::CScript *pScript )
{
	
	return ( ScriptDoAction( pParams, pScript, ACTION_CLEAR_FLAG ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SendFlag | Sets a specified flag in a named object. All
// objects have a set of 32 flags. For example: 
// SendFlag 14 name =TRG_Car03 ; no quotes 
// This will search all the objects until it finds the one called
// TRG_Car03, and will then set its flag 14. The flag value must
// be between 0 and 31 inclusive. To aid readability, names should
// be used for the flags, and there is a file flags.q where the
// definitions should be put
// @uparm 1 | the flag to set
// @parmopt name | name | | the name of the object
// @parmopt string | prefix | | prefix value
bool ScriptSendFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	return ( ScriptDoAction( pParams, pScript, ACTION_SET_FLAG ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | QueryFlag | you can query a flag on an object (or several
// objects if using links parameter or prefix = "Something_" parameter
// instead of name = Something_Unique). In the case of one object,
// QueryFlag will return true or false depending on if the flag is set
// or not. In the case of several objects, the function will return true
// if any of the objects specified have that flag set, false otherwise
// @uparm 1 | the flag to query
// @parmopt name | name | | the name of the object
// @parmopt string | prefix | | prefix value
bool ScriptQueryFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	return ( ScriptDoAction( pParams, pScript, ACTION_QUERY_FLAG ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SendException | use the SendException command in the same
// way you would use the SendFlag command:  you can specify name=whatever_object
// or links or prefix="whatever_" (just like with SendFlag). 
// @uparm Exception | the flag 
// @parmopt name | name | | object name
// @parmopt string | prefix | | prefix value
bool ScriptFlagException(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	return ( ScriptDoAction( pParams, pScript, ACTION_FLAG_EXCEPTION_BY_CHECKSUM ) );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsAlive | Takes the same arguments as SendFlag etc... Returns
// true if the object currently exists
// @parm name | name | object name
bool ScriptCheckIfAlive( Script::CStruct *pParams, Script::CScript *pScript )
{

	return ( DoNodeAction( pParams, pScript, NODE_ACTION_CHECK_IF_ALIVE ) );
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void SetUpActionData( SActionData *pData, int flag, int action )
{
	
	Dbg_MsgAssert( pData,( "Fire matt the idiot" ));
	pData->Flag = flag;
	pData->Action = action;
	pData->ObjectFound = false;
	pData->ReturnValue = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Used to perform actions to nodes by prefix, name checksum, or links -- but this version
// only operates on the nodes that create CMovingObjects... For the general version (applicable
// to nodes that create Environmental Objects, Rails, etc... AS WELL as CMovingObjects, see
// DoNodeAction( )
bool ScriptDoAction(Script::CStruct *pParams, Script::CScript *pScript, int action )
{
	
	int flag = 0;
	if (action == ACTION_FLAG_EXCEPTION_BY_CHECKSUM)
	{
	#ifdef __NOPT_ASSERT__
		bool GotFlag=
	#endif	
		pParams->GetChecksum(NONAME,(uint32*)&flag);
		Dbg_MsgAssert(GotFlag,("\n%s\nSendException  require a chechecksum name to be specified.\n(NOT AN INTEGER)",pScript->GetScriptInfo()));
	}
	else
	{
	#ifdef __NOPT_ASSERT__
		bool GotFlag=
	#endif	
		pParams->GetInteger(NONAME,&flag);
		Dbg_MsgAssert(GotFlag,("\n%s\nFlag commands require a flag to be specified.\n(Either an integer or a name defined to be an integer)",pScript->GetScriptInfo()));
		Dbg_MsgAssert(flag>=0 && flag<32,("\n%s\nBad flag value of %d, value must be between 0 and 31",pScript->GetScriptInfo(),flag));
	
	}
	uint32 nameChecksum;
	const char *pPrefix;
	int i;
	
	
	bool useCurrentLinks = false;
	if ( pParams->ContainsFlag( 0x2e7d5ee7 ) || // checksum 'links'
		( useCurrentLinks = pParams->ContainsFlag( 0xbceb479a ) ) ) // checksum 'currentlinks'
	{
	
		Dbg_MsgAssert(0,("\n%s\n'links' and 'currentlinks' are deprecated\n",pScript->GetScriptInfo()));
	}
	
	if ( pParams->GetChecksum( 0xa1dc81f9, &nameChecksum) )  // 'name'
	{
		SActionData_NameChecksum Data;
		Data.ObjectNameChecksum = nameChecksum;
		SetUpActionData( &Data.actionData, flag, action );
		 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
		skate_mod->GetObjectManager()->ProcessAllObjects(Action_ByNameChecksum,&Data);
		Dbg_MsgAssert( Data.actionData.ObjectFound,( "\n%s\nDidn't find object specified '%s'.", pScript->GetScriptInfo( ), Script::FindChecksumName( nameChecksum ) ) );
		return ( Data.actionData.ReturnValue );
	}
    if ( pParams->GetText( 0x6c4e7971, &pPrefix ) ) // checksum 'prefix'
	{
		// Create with a prefix specified:
		uint16 numNodes = 0;
		const uint16 *pMatchingNodes = SkateScript::GetPrefixedNodes( pPrefix, &numNodes );
		 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
		SActionData_NodeIndex Data;
		SetUpActionData( &Data.actionData, flag, action );
		for ( i = 0; i < numNodes; i++ )
		{
			Data.NodeIndex = pMatchingNodes[ i ];
			skate_mod->GetObjectManager()->ProcessAllObjects(Action_ByNodeIndex,&Data);
		}
		Dbg_MsgAssert( Data.actionData.ObjectFound,( "\n%s\nDidn't find any objects with prefix %s.", pScript->GetScriptInfo( ), pPrefix ));
		return ( Data.actionData.ReturnValue );
	}
#if 0  // put in if ever necessary:
	// set the flag on ourself:
	if ( pScript->mpObject )
	{
		switch ( action )
		{
			case ( ACTION_SET_FLAG ):
				pScript->mpObject->mScriptFlags |= ( 1 << flag );
				return ( true );
			case ( ACTION_CLEAR_FLAG ):
                pScript->mpObject->mScriptFlags &= ~( 1 << flag );
				return ( true );
			case ( ACTION_QUERY_FLAG ):
				return ( pScript->mpObject->mScriptFlags & ( 1 << flag );
			default:
				Dbg_MsgAssert( 0,( "Unknown flag action %d", action ));
				break;
		}
	}
	else
	{
		Dbg_MsgAssert( 0,( "\n%s\nNeed to specify an object, or call this command on a script associated with an object.", pScript->GetScriptInfo( ) ));
	}
#endif
	Dbg_MsgAssert( 0,( "\n%s\nMust specify a name, links, or prefix please.", pScript->GetScriptInfo( ) ));
	return ( false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSetVisibilityFromNodeIndex( int nodeIndex, bool invisible, int viewport_number )
{	
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	Script::CArray *pNodeArray = Script::GetArray( CRCD(0xc472ecc5,"NodeArray") );
	Dbg_MsgAssert( pNodeArray,( "No NodeArray found" ));

	Script::CStruct *pNode=pNodeArray->GetStructure( nodeIndex );
	Dbg_MsgAssert( pNode,( "NULL pNode" ));

	// If this is a net game, don't show/hide objects that were meant to be left out for 
	// performance/bandwidth/gameplay reasons
	if ( skate_mod->ShouldBeAbsentNode( pNode ) )
	{
		return true;
	}

	uint32 ClassChecksum = 0;
	pNode->GetChecksum( 0x12b4e660 /*"Class"*/, &ClassChecksum );
	
	switch (ClassChecksum)
	{
		case 0xe47f1b79: // vehicle
		case 0xa0dfac98: // pedestrian
		case 0x61a741e:  // ped
		case 0xef59c100: // gameobject
		case 0x5b8ab877: // skater
		case 0x19b1e241: // ParticleEmitter	  
		case 0x9e7d469e: // ParticleObject  
		{
			SActionData_NodeIndex Data;
			Data.NodeIndex = nodeIndex;
			if ( invisible )
			{
				SetUpActionData( &Data.actionData, Obj::CMovingObject::vINVISIBLE, ACTION_SET_OBJECT_FLAG );
			}
			else
			{
				SetUpActionData( &Data.actionData, Obj::CMovingObject::vINVISIBLE, ACTION_CLEAR_OBJECT_FLAG );
			}
			Data.NodeIndex = nodeIndex;
			skate_mod->GetObjectManager()->ProcessAllObjects(Action_ByNodeIndex,&Data);
			if ( !Data.actionData.ObjectFound )
			{
				Dbg_Message( "Vis/Invis:  Object from node %d not found.", nodeIndex );
			}
			break;
		}
		case 0xb7b3bd86:  // LevelObject
		case 0xbf4d3536:  // LevelGeometry
		{
			uint32 checksumName = 0;
			//Dbg_MsgAssert( ( "No world in viewer module..." ));
			
			if ( pNode->GetChecksum( 0xa1dc81f9, &checksumName ) ) // checksum 'name'
			{
//				Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksumName);
				// We only want to get sectors from the main scene, as that is what the node array applies to															
				Nx::CScene * p_main_scene = Nx::CEngine::sGetMainScene();
				Nx::CSector *p_sector = p_main_scene->GetSector(checksumName);
				Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",checksumName,Script::FindChecksumName(checksumName)));

				uint32 orig_flag = p_sector->GetVisibility();
				uint32 flag = 0x1; 
				if (viewport_number == 1)
				{
					flag = 0x2; 
				}
				if ( invisible )
				{
					p_sector->SetVisibility(orig_flag & ~flag);		// clear flags
					//Bsp::SetWorldSectorFlag(  checksumName, flag );
				}
				else
				{
					p_sector->SetVisibility(orig_flag | flag);		// set flags
					//Bsp::ClearWorldSectorFlag(  checksumName, flag );
				}
			}
			break;
 		}

//		case 0xe594f0a2:  // Trigger (geometry containing triggers...)
		case 0x8e6b02ad:  // railnode
		case CRCC(0x30c19600, "ClimbingNode"):
		default:
			return ( false );
			break;
	}
	return ( true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptCheckExistenceFromNodeIndex( int nodeIndex )
{	
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	Script::CArray *pNodeArray = Script::GetArray( CRCD(0xc472ecc5,"NodeArray") );
	Dbg_MsgAssert( pNodeArray,( "No NodeArray found" ));

	Script::CStruct *pNode=pNodeArray->GetStructure( nodeIndex );
	Dbg_MsgAssert( pNode,( "NULL pNode" ));

	uint32 ClassChecksum = 0;
	pNode->GetChecksum( 0x12b4e660 /*"Class"*/, &ClassChecksum );
	
	switch (ClassChecksum)
	{
		case 0xe47f1b79: // vehicle
		case 0xa0dfac98: // pedestrian
		case 0x61a741e:  // ped
		case 0xef59c100: // gameobject
		case 0x5b8ab877: // skater
		case 0xb7b3bd86: // LevelObject
		case 0x19b1e241: // ParticleEmitter
		case 0x9e7d469e: // ParticleObject
		
		{
			SActionData_NodeIndex Data;
			Data.NodeIndex = nodeIndex;
			SetUpActionData( &Data.actionData, 0, ACTION_SEE_IF_OBJECT_EXISTS );
			Data.NodeIndex = nodeIndex;
			skate_mod->GetObjectManager()->ProcessAllObjects(Action_ByNodeIndex,&Data);
			if ( !Data.actionData.ObjectFound )
			{
				return ( false );
			}
			return ( true );
			break;
		}
		case 0xbf4d3536:  // LevelGeometry
		{
			uint32 checksumName = 0;
			//Dbg_MsgAssert( ( "No world in viewer module..." ));
			if ( pNode->GetChecksum( 0xa1dc81f9, &checksumName ) ) // checksum 'name'
			{
//				Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksumName);				
				// We only want to get sectors from the main scene, as that is what the node array applies to															
				Nx::CScene * p_main_scene = Nx::CEngine::sGetMainScene();
				Nx::CSector *p_sector = p_main_scene->GetSector(checksumName);
				Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",checksumName,Script::FindChecksumName(checksumName)));
				return (p_sector->IsActive());
				//return ( Bsp::WorldSectorIsAlive( checksumName ) );
			}
			break;
 		}

		case 0xe594f0a2:  // Trigger (geometry containing triggers...)
			break;
			
		case CRCC(0x30c19600, "ClimbingNode"):
		case 0x8e6b02ad:  // railnode
			return ( skate_mod->GetRailManager()->IsActive( nodeIndex ) );
			break;
			
		default:
			return ( false );
			break;
	}
	return ( true );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSetColorFromNodeIndex( int nodeIndex, Script::CStruct *pParams )
{	

	uint32 color = 0x808080;
	
	pParams->GetInteger( CRCD(0x99a9b716,"color"), (int*)&color, TRUE );
	
	Image::RGBA rgb;
	rgb.r = (uint8)((color)&0xff);
	rgb.g = (uint8)((color>>8)&0xff);
	rgb.b = (uint8)((color>>16)&0xff);
	rgb.a = 0x80;

	Script::CArray *pNodeArray = Script::GetArray( CRCD(0xc472ecc5,"NodeArray") );
	Dbg_MsgAssert( pNodeArray,( "No NodeArray found" ));

	Script::CStruct *pNode=pNodeArray->GetStructure( nodeIndex );
	Dbg_MsgAssert( pNode,( "NULL pNode" ));

	uint32 ClassChecksum = 0;
	pNode->GetChecksum( 0x12b4e660 /*"Class"*/, &ClassChecksum );
	
	switch (ClassChecksum)
	{
		case 0xbf4d3536:  // LevelGeometry
		{
			uint32 checksumName = 0;
			//Dbg_MsgAssert( ( "No world in viewer module..." ));
			if ( pNode->GetChecksum( 0xa1dc81f9, &checksumName ) ) // checksum 'name'
			{
				Nx::CScene * p_main_scene = Nx::CEngine::sGetMainScene();
				Nx::CSector *p_sector = p_main_scene->GetSector(checksumName);
				Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",checksumName,Script::FindChecksumName(checksumName)));
				p_sector->SetColor(rgb);
			
			}
			break;
 		}
		default:
			return ( false );
			break;
	}
	return ( true );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptShatterFromNodeIndex( int nodeIndex )
{
	
	
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

// Mick:  If pre-running scripts in a net game, we might shatter a lot of glass
// which might run out of memory
// so don't actually shatter the sector if that's what we are doing

	if( skate_mod->LaunchingQueuedScripts())
	{
		return true;
	}

	Script::CArray *pNodeArray = Script::GetArray( CRCD(0xc472ecc5,"NodeArray") );
	Dbg_MsgAssert( pNodeArray,( "No NodeArray found" ));

	Script::CStruct *pNode=pNodeArray->GetStructure( nodeIndex );
	Dbg_MsgAssert( pNode,( "NULL pNode" ));

	// If this is a net game, don't show/hide objects that were meant to be left out for 
	// performance/bandwidth/gameplay reasons
	if ( skate_mod->ShouldBeAbsentNode( pNode ) )
	{
		return true;
	}

	uint32 ClassChecksum = 0;
	pNode->GetChecksum( 0x12b4e660 /*"Class"*/, &ClassChecksum );

	switch( ClassChecksum )
	{
		case 0xb7b3bd86:  // LevelObject
		case 0xbf4d3536:  // LevelGeometry
		{
			//Dbg_MsgAssert( p_world,( "No world in viewer module..." ));

			uint32 checksumName = 0;
			if( pNode->GetChecksum( 0xa1dc81f9, &checksumName )) // checksum 'name'
			{
//				Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksumName);				
				// We only want to get sectors from the main scene, as that is what the node array applies to															
				Nx::CScene * p_main_scene = Nx::CEngine::sGetMainScene();
				Nx::CSector *p_sector = p_main_scene->GetSector(checksumName);
				Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",checksumName,Script::FindChecksumName(checksumName)));
				p_sector->SetShatter(true);
				//Bsp::ShatterWorldSector(  checksumName );
			}
			break;
 		}

		default:
		{
			return false;
			break;
		}
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MakeSkaterGoto |
// This will cause the main skater script to jump to the
// specified script. So a script triggered from a trigger poly
// can use this to force the main skater script to jump to a script.
// It will choose the skater that triggered the poly in that case. 
// 
// A script not associated with an object can also use this, in which
// case it will set the script of skater 0. 
// 
// The main skater script itself could also use it, but there would be
// no point since it can use Goto. 
// 
// This should be used with care, since making the skater jump to a
// script when he's in the middle of doing something could confuse him.
// Eg, forcing him to jump to OnGroundAI when he's on a rail will make
// an assert go off.
// @uparm name | Script name
// @parmopt structure | params | | parameters to be passed to new script
bool ScriptMakeSkaterGoto(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	
	Script::CStruct *pParamsToPass=NULL;
	pParams->GetStructure(CRCD(0x7031f10c,"Params"),&pParamsToPass);
	
	uint32 ScriptChecksum=0;
	pParams->GetChecksum(NONAME,&ScriptChecksum);
	Dbg_MsgAssert(ScriptChecksum,("\n%s\nMakeSkaterGoto requires a script name, eg MakeSkaterGoto Blaa",pScript->GetScriptInfo()));

	Obj::CSkater *pSkater=NULL;
	if (pScript && pScript->mpObject && pScript->mpObject->GetType()==SKATE_TYPE_SKATER)	
	{
		// If the script using this has an object associated with it, and that object
		// is a skater, then use that skater.
		// This will happen in the case of a trigger script using this command.
		pSkater=(Obj::CSkater*)pScript->mpObject.Convert();
	}
	else
	{
		 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
		int Skater=0;
		if( pParams->GetInteger("Skater",&Skater))
		{
			pSkater = skate_mod->GetSkater(Skater);
		}
		else
		{
			pSkater = skate_mod->GetLocalSkater();
		}
	}
		
	if (pSkater && pSkater->IsLocalClient())
	{
		pSkater->JumpToScript(ScriptChecksum,pParamsToPass);
	}
		
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MakeSkaterGotoSub | interrupts whatever script the skater is doing,
// calls new script, and then returns to the original script.
// @uparm name | script to goto
// @parmopt structure | Params | | parameters to pass to new script
// @parmopt int | Skater | | 
bool ScriptMakeSkaterGosub(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Script::CStruct *pParamsToPass=NULL;
	pParams->GetStructure(CRCD(0x7031f10c,"Params"),&pParamsToPass);
	
	uint32 ScriptChecksum=0;
	pParams->GetChecksum(NONAME,&ScriptChecksum);
	Dbg_MsgAssert(ScriptChecksum,("\n%s\nMakeSkaterGosub requires a script name, eg MakeSkaterGosub Blaa",pScript->GetScriptInfo()));

	Obj::CSkater *pSkater=NULL;
	if (pScript && pScript->mpObject && pScript->mpObject->GetType()==SKATE_TYPE_SKATER)	
	{
		// If the script using this has an object associated with it, and that object
		// is a skater, then use that skater.
		// This will happen in the case of a trigger script using this command.
		pSkater=(Obj::CSkater*)pScript->mpObject.Convert();
	}
	else
	{
		 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
		int Skater=0;
		pParams->GetInteger("Skater",&Skater);
		pSkater = skate_mod->GetSkater(Skater);						   
	}
		
	if (pSkater)
	{
		pSkater->CallScript(ScriptChecksum,pParamsToPass);
	}
		
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SpawnScript | This will create & run a new script which will
// run -in parallel- until it finishes, when it will die. The new script
// is not associated with any object. The calling script is not affected
// in any way. A typical use of a spawned script would be to play a sound,
// pause, play another sound, etc, for example when an object dies. This
// way the object itself can die straight away
// @uparm name | the name of the script to spawn
// @parmopt structure | Params | | Parameter structure to pass to new script
// @parmopt name | Id | | an id to assign to the spawned script, so it can 
// be killed by KillSpawnedScript
// @flag NotSessionSpecific | This will cause the script to not get deleted when the current
// level (session) ends.
bool ScriptSpawnScript(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 ScriptChecksum=0;
	pParams->GetChecksum(NONAME,&ScriptChecksum);
	Dbg_MsgAssert(ScriptChecksum,("\n%s\nMissing script name in SpawnScript command.",pScript->GetScriptInfo()));
	bool net_enabled, permanent;
	Script::CStruct *pScriptParams=NULL;
	pParams->GetStructure(CRCD(0x7031f10c,"Params"),&pScriptParams);
	
	// The spawned script can optionally be given an Id, so that it can be deleted
	// by KillSpawnedScript.
	uint32 Id=0;
	pParams->GetChecksum("Id",&Id);

	uint32 CallbackScript=0;
	pParams->GetChecksum("Callback",&CallbackScript);
	Script::CStruct *pCallbackParams=NULL;
	pParams->GetStructure("CallbackParams",&pCallbackParams);
		
	//net_enabled = 0;
	//permanent = 0;
	//pParams->GetInteger( "NetEnabled", &net_enabled );
	//pParams->GetInteger( "Permanent", &permanent );
	net_enabled = pParams->ContainsFlag( "NetEnabled" );
	permanent = pParams->ContainsFlag( "Permanent" );
	
	int not_session_specific=0;
	pParams->GetInteger( "NotSessionSpecific", ¬_session_specific);
	
	// copy the parent's node
	#ifdef __NOPT_ASSERT__
	Script::CScript *p_script=Script::SpawnScript(ScriptChecksum,pScriptParams,CallbackScript,pCallbackParams,
													pScript->mNode,
													Id,
													net_enabled,
													permanent,
													not_session_specific); 	
	p_script->SetCommentString("Spawned by script command SpawnScript");
	p_script->SetOriginatingScriptInfo(pScript->GetCurrentLineNumber(),pScript->mScriptChecksum);
	#else
	Script::SpawnScript(ScriptChecksum,pScriptParams,CallbackScript,pCallbackParams,
						pScript->mNode,
						Id,
						net_enabled,
						permanent,
						not_session_specific); 	
	#endif	
	return true;
}	


			
// @script | SpawnSound | This will create & run a new script which will
// run -in parallel- until it finishes, when it will die. The new script
// is not associated with any object. The calling script is not affected
// in any way. A typical use of a spawned script would be to play a sound,
// pause, play another sound, etc, for example when an object dies. This
// way the object itself can die straight away
//
// Note, this is the same as SpawnScript, except if the current script is
// attached to an object, then we do the same as Obj_SpawnScript
// So we get attached to the object
//
// @uparm name | the name of the script to spawn
// @parmopt structure | Params | | Parameter structure to pass to new script
// @parmopt name | Id | | an id to assign to the spawned script, so it can 
// be killed by KillSpawnedScript
bool ScriptSpawnSound(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	if (pScript->mpObject)
	{
		uint32 scriptChecksum;
		if ( pParams->GetChecksum( NONAME, &scriptChecksum ) )
		{
			// The spawned script can optionally be given an Id, so that it can be deleted
			// by KillSpawnedScript.
			uint32 Id=0;
			// keep the same ID as the parent if not specified...
			Id = Script::FindSpawnedScriptID(pScript);
			pParams->GetChecksum("Id",&Id);
			Script::CScriptStructure *pScriptParams = NULL;
			pParams->GetStructure( "Params", &pScriptParams );
			#ifdef __NOPT_ASSERT__	
			Script::CScript *p_script=pScript->mpObject->SpawnScriptPlease( scriptChecksum, pScriptParams, Id );
			p_script->SetCommentString("Created by SpawnSound");
			p_script->SetOriginatingScriptInfo(pScript->GetCurrentLineNumber(),pScript->mScriptChecksum);
			#else
			pScript->mpObject->SpawnScriptPlease( scriptChecksum, pScriptParams, Id );
			#endif
		}
	}
	else
	{
		return ScriptSpawnScript(pParams,pScript);		
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SpawnSkaterScript | This will create & run a new script
// on the skater which will run in parallel until it finishes, when it
// will die. The calling script is not affected in any way. 
// @uparm name | the name of the script to call
// @parmopt structure | Params | | parameter list to pass to new script
// @parmopt name | Id | | an id to assign to the new script so it can 
// be killed by KillSpawnedScript
bool ScriptSpawnSkaterScript(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	// Mostly the same as the code for ScriptSpawnScript
	uint32 ScriptChecksum=0;
	pParams->GetChecksum(NONAME,&ScriptChecksum);
	Dbg_MsgAssert(ScriptChecksum,("\n%s\nMissing script name in SpawnSkaterScript command.",pScript->GetScriptInfo()));
	
	Script::CStruct *pScriptParams=NULL;
	pParams->GetStructure(CRCD(0x7031f10c,"Params"),&pScriptParams);
	
	// The spawned script can optionally be given an Id, so that it can be deleted
	// by KillSpawnedScript.
	uint32 Id=0;
	pParams->GetChecksum("Id",&Id);

	uint32 CallbackScript=0;
	pParams->GetChecksum("Callback",&CallbackScript);
	Script::CStruct *pCallbackParams=NULL;
	pParams->GetStructure("CallbackParams",&pCallbackParams);
	
	Script::CScript *pNewScript=Script::SpawnScript(ScriptChecksum,pScriptParams,CallbackScript,pCallbackParams,pScript->mNode,Id); 	// copy the parent's node
	#ifdef __NOPT_ASSERT__
	pNewScript->SetCommentString("Spawned by script command SpawnSkaterScript");
	pNewScript->SetOriginatingScriptInfo(pScript->GetCurrentLineNumber(),pScript->mScriptChecksum);
	#endif	
	
	// Now set the object pointer to be the skater.
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	Obj::CSkater *pSkater = skate_mod->GetLocalSkater();						   
	
	pNewScript->mpObject=pSkater;
	
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | KillSpawnedScript | can be passed a script name or an id
// @parmopt name | Name | | the name of the script
// @parmopt name | Id | | the id of the script
bool ScriptKillSpawnedScript(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	uint32 ScriptChecksum=0;
	if (pParams->GetChecksum("Name",&ScriptChecksum))
	{
		// Got a script name, so kill all spawned scripts that ran that script.
		Script::KillSpawnedScriptsThatReferTo(ScriptChecksum);
		return true;
	}

	uint32 Id=0;										   
	if (pParams->GetChecksum("Id",&Id))
	{
		// They specified an Id, so kill all spawned scripts with this Id.
		Script::KillSpawnedScriptsWithId(Id);
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PauseSkaters | This will cause all skaters to freeze.
// They will continue where they left off when UnPauseSkaters is called. 
// Note that this command will not pause any spawned scripts associated
// with the skaters, because otherwise it will pause the camera animation
// script too, which is where the command will probably be used most
bool ScriptPauseSkaters( Script::CStruct *pParams, Script::CScript *pScript )
{
	Replay::WritePauseSkater();
	
	bool hide = pParams->ContainsFlag("hide");	
	
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	uint32 NumSkaters=skate_mod->GetNumSkaters();
	for (uint32 i=0; iGetSkater(i);
		if (pSkater && pSkater->IsLocalClient()) // Hmm, assert instead?
		{
			pSkater->Pause();
			pSkater->Hide(hide);
		}	
	}	
	
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnPauseSkaters | opposite effect of PauseSkaters
bool ScriptUnPauseSkaters( Script::CStruct *pParams, Script::CScript *pScript )
{
	Replay::WriteUnPauseSkater();
	
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	uint32 NumSkaters=skate_mod->GetNumSkaters();
	for (uint32 i=0; iGetSkater(i);
		if (pSkater)
		{
			pSkater->UnPause();
			pSkater->Hide(false);
		}	
	}	
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PauseSkater | this pauses an individual skater
// @uparm 0 | skater id
bool ScriptPauseSkater( Script::CStruct *pParams, Script::CScript *pScript )
{
	bool hide = pParams->ContainsFlag("hide");	

	int skater_id;
	pParams->GetInteger( NONAME, &skater_id, true );
	
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	Obj::CSkater *pSkater = skate_mod->GetSkater(skater_id);
	if (pSkater)
	{
		pSkater->Pause();
		pSkater->Hide(hide);
	}	

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnPauseSkater | Unpauses an individual skater
// @uparm 0 | skater id
bool ScriptUnPauseSkater( Script::CStruct *pParams, Script::CScript *pScript )
{
	

	int skater_id;
	pParams->GetInteger( NONAME, &skater_id, true );

	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	Obj::CSkater *pSkater = skate_mod->GetSkater(skater_id);
	if (pSkater)
	{
		pSkater->UnPause();
		pSkater->Hide(false);
	}	

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PauseGame | Similar to "PauseSkaters", it will pause
// the skaters, and everything else in the world, yet still allow
// you to launch messages and spawn new scripts
bool ScriptPauseGame( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Mdl::FrontEnd * front =  Mdl::FrontEnd::Instance();
	front->PauseGame(true);
	// if the script was a spawned script
	// then we want to unpause it, so we can keep running after the game has paused
	Script::UnpauseSpawnedScript(pScript);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnPauseGame | 
bool ScriptUnPauseGame( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Mdl::FrontEnd * front =  Mdl::FrontEnd::Instance();
	front->PauseGame(false);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsGamePaused | 
bool ScriptIsGamePaused( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Mdl::FrontEnd * front =  Mdl::FrontEnd::Instance();
	if( front->GamePaused() )
    {
        return true;
    }
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PauseObjects |
bool ScriptPauseObjects(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Pause all composite objects
	Obj::CCompositeObjectManager::Instance()->Pause(true);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnpauseObjects |
bool ScriptUnPauseObjects(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Unpause all composite objects
	Obj::CCompositeObjectManager::Instance()->Pause(false);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PauseSpawnedScripts |
bool ScriptPauseSpawnedScripts(Script::CStruct *pParams, Script::CScript *pScript)
{
	Script::PauseSpawnedScripts(true);		

	if ( pScript->mIsSpawned )
	{
		Dbg_MsgAssert( 0, ( "Can't pause spawned scripts from a spawned script" ) );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnPauseSpawnedScripts |
bool ScriptUnPauseSpawnedScripts(Script::CStruct *pParams, Script::CScript *pScript)
{
	Script::PauseSpawnedScripts(false);		

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetClock | 
bool ScriptResetClock( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	int time_limit;

	// Clients instead get their time limit from the server
	if( gamenet_man->OnServer())
	{
		time_limit = skate_mod->GetGameMode()->GetTimeLimit();
		skate_mod->SetTimeLimit( time_limit );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PadsPluggedIn | Returns true if at least one pad is plugged in.
bool ScriptPadsPluggedIn( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Mdl::FrontEnd * p_front =  Mdl::FrontEnd::Instance();
	 return p_front->PadsPluggedIn();
}	 

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DoFlash | Doflash just draws a big poly over the viewport
// and then modulates the color and alpha of that poly over time. There
// is no support for Blendmodes yet. If you are doing a player specific 
// flash remember to call it from a skater script so that it works in 2
// player splitscreen
// @parmopt float | duration | 0.0 | 
// @parmopt string | texture | | texture name
// @parmopt float | z | 0.0 | z
// @flag fullscreen |
// @flag behind_panel |
// @flag additive |
// @flag subtractive |
// @parmopt int | skater | | for specifying skater when not in skater script
// @flag ignore_pause |
// @parm int | start_r | start red index
// @parm int | start_g | start green index
// @parm int | start_b | start blue index
// @parm int | start_a | start alpha value
// @parm int | end_r | end red index
// @parm int | end_g | end green index
// @parm int | end_b | end blue index
// @parm int | end_a | end alpha value
bool ScriptDoFlash( Script::CStruct *pParams, Script::CScript *pScript )
{
	

	float		duration		= 0.0f;
	float		z				= 0.0f;
	uint32		flags			= 0;
	bool		fullscreen		= false;
	int			skater_num		= -1;
	const char*	p_texture_name	= NULL;

	pParams->GetFloat( "duration", &duration );
	
	if( duration > 0.0f )
	{
		pParams->GetText( "texture", &p_texture_name );

		pParams->GetFloat( "z", &z );

//		if( pParams->ContainsFlag( 0xdf6436d2 /*"fullscreen"*/ ))
//		{
//			flags |= Flash::FLAG_FULLSCREEN;
//			fullscreen = true;
//		}	
		if( pParams->ContainsFlag( 0x70ffc649 /*"behind_panel"*/ ))
		{
			flags |= Nx::SCREEN_FLASH_FLAG_BEHIND_PANEL;
		}	
		if( pParams->ContainsFlag( 0x19c43cf6 /*"additive"*/ ))
		{
			flags |= Nx::SCREEN_FLASH_FLAG_ADDITIVE;
		}	
		else if( pParams->ContainsFlag( 0x387c9ed6 /*"subtractive"*/ ))
		{
			flags |= Nx::SCREEN_FLASH_FLAG_SUBTRACTIVE;
		}	

		if( !fullscreen )
		{
			pParams->GetInteger( "skater", &skater_num );
			if( skater_num == -1 )
			{
				// No skater parameter provided, so assume running from a skater script.
				Obj::CSkater* p_skater = static_cast ( pScript->mpObject.Convert() );
				Dbg_Assert( p_skater );
				skater_num = p_skater->GetSkaterNumber();
			}
		}

		if( pParams->ContainsFlag( 0xdf6436d2 /*"ignore_pause"*/ ))
		{
			flags |= Nx::SCREEN_FLASH_FLAG_IGNORE_PAUSE;
		}	

		int val = 0;
		Image::RGBA from, to;

		pParams->GetInteger( "start_r",	&val );
		from.r = val;
		pParams->GetInteger( "start_g",	&val );
		from.g = val;
		pParams->GetInteger( "start_b",	&val );
		from.b = val;
		pParams->GetInteger( "start_a",	&val );
		from.a = val;

		pParams->GetInteger( "end_r",	&val );
		to.r = val;
		pParams->GetInteger( "end_g",	&val );
		to.g = val;
		pParams->GetInteger( "end_b",	&val );
		to.b = val;
		pParams->GetInteger( "end_a",	&val );
		to.a = val;

		if( fullscreen )
		{
		}
		else
		{
			Nx::AddScreenFlash( skater_num, from, to, duration, z, flags, p_texture_name );

			// Get the panel for player.
			//HUD::PanelMgr* panel_mgr = HUD::PanelMgr::Instance();
			//HUD::Panel* p_panel = panel_mgr->GetPanelBySkaterId( skater_num );
			//p_panel->AddFlash( from, to, duration, z, flags, p_texture_name );
		}
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StartServer | Starts a server using the IP stored in
// the script variable LocalIP, which you also have to define. You
// can also define a script variable called ServerName to name your
// server (i.e. ServerName = MyServer with a max of 15 characters
bool ScriptStartServer(Script::CStruct *pParams, Script::CScript *pScript )
{
	
	
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	// set the appropriate memory context
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());

	skate_mod->StartServer();

	if (gamenet_man->InInternetMode())
	{
		// defer loading the level/starting the game
		// until we have connected to the matchmaker
		//gamenet_man->RequestMatchmakerConnect();
		
		// SG: 9-17-02. Post the game AFTER we're in our first level
		//gamenet_man->PostGame();
	}
	else if (gamenet_man->InLanMode())
	{
		// if we're in LAN mode, start the game immediately
	}
		
	// pop the memory context
	Mem::Manager::sHandle().PopContext();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LeaveServer | 
bool ScriptLeaveServer(Script::CStruct *pParams, Script::CScript *pScript )
{   
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	skate_mod->LeaveServer();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | FindServers | broadcast out to the local subnet for servers
bool ScriptFindServers(Script::CStruct *pParams, Script::CScript *pScript )
{
	// Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	
	//skate_mod->FindServers();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | JoinServer | Join the server in session at the given IP address and port
// @uparm "string" | ip address
// @uparm 1 | port
bool ScriptJoinServer(Script::CStruct *pParams, Script::CScript *pScript )
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
    
	if( gamenet_man->OnServer())
	{
		uint32 i;

		// If playing split screen, create N clients (one for each viewport
		// otherwise, just create one client
//		gamenet_man->SpawnClient( false, true, 0 );

		uint32 numPlayers = skate_mod->GetGameMode()->GetInitialNumberOfPlayers();

		for( i = 0; i < numPlayers; i++ )
		{   
			Net::Client* client;

			client = gamenet_man->SpawnClient( false, true, true, i );
			
			//skate_mod->JoinServer( false, 0, 0, true, i );
			gamenet_man->JoinServer( false, 0, 0, i );
			skate_mod->AddNetworkMsgHandlers( client, i );
			if( gamenet_man->InNetGame())
			{
				Script::RunScript( "entered_network_game" );
			}
		}
	}
	else
	{
		/*int max_players;
		pParams->GetInteger( Script::GenerateCRC( "MaxPlayers" ), (int *) &max_players );
		skate_mod->GetGameMode()->SetMaximumNumberOfPlayers( max_players );

		Dbg_Printf( "**** SETTING MAX PLAYERS TO %d\n", max_players );
		Dbg_Assert( skate_mod->GetGameMode()->GetMaximumNumberOfPlayers() == (uint32) max_players );*/

#ifdef __PLAT_XBOX__
		IN_ADDR host_addr;
		int port = 0;
		bool observe_only;

		pParams->GetInteger( Script::GenerateCRC( "Address" ), (int *) &host_addr.s_addr );
		pParams->GetInteger( Script::GenerateCRC( "Port" ), &port );
		observe_only = ( gamenet_man->GetJoinMode() == GameNet::vJOIN_MODE_OBSERVE );
	
		if( port != 0 )
		{
			gamenet_man->SpawnClient( false, false, true, 0 );
			gamenet_man->JoinServer( observe_only, (unsigned long) host_addr.s_addr, port, 0 );
		}
#else
		const char *server_ip = NULL;
		int port = 0;
		bool observe_only;
		
		pParams->GetText( NONAME, &server_ip );
		pParams->GetInteger( NONAME, &port );
		observe_only = ( gamenet_man->GetJoinMode() == GameNet::vJOIN_MODE_OBSERVE );
	
		if( server_ip && ( port != 0 ))
		{
			gamenet_man->SpawnClient( false, false, true, 0 );
			gamenet_man->JoinServer( observe_only, inet_addr( server_ip ), port, 0 );
		}
#endif
	}
    
	Mem::Manager::sHandle().PopContext();
    	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetNetworkMode | sets the network mode
// @uparm 0 | mode
bool ScriptSetNetworkMode( Script::CStruct *pParams, Script::CScript *pScript )
{
	int mode = 0;
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	mode = 0;
	pParams->GetInteger( NONAME, &mode );
	if( mode == 0 )
	{
		gamenet_man->SetNetworkMode( GameNet::vNO_NET_MODE );
	}
	else if( mode == 1 )
	{
		gamenet_man->SetNetworkMode( GameNet::vLAN_MODE );
	}
	else if( mode == 2 )
	{
		gamenet_man->SetNetworkMode( GameNet::vINTERNET_MODE );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MemViewToggle | 
bool ScriptMemViewToggle( Script::CStruct *pParams, Script::CScript *pScript )
{
	MemViewToggle();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ProfileTasks | profiles task for the number of frames specified
// @uparm 1 | number of frames
bool ScriptProfileTasks( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Mlp::Manager * mlp_manager =  Mlp::Manager::Instance();
	int Val=1;
	pParams->GetInteger(NONAME,&Val);	
	printf ("\n\nProfiling task for %d frames:\n",Val);
	mlp_manager->ProfileTasks(Val);
	
	return true;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UseNetworkPreferences | 
bool ScriptUseNetworkPreferences( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->UsePreferences();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptTestNetSetup( Script::CStruct *pParams, Script::CScript *pScript )
{
	Net::Manager * net_man =  Net::Manager::Instance();
	bool properly_setup;
	 
	properly_setup = net_man->NetworkEnvironmentSetup();
	if( !properly_setup )
	{
		if( net_man->GetLastError() == Net::vRES_ERROR_DEVICE_NOT_HOT )
		{
			pParams->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_not_connected" ));
			Script::RunScript( "create_net_startup_error_dialog", pParams );
		}
		else if( net_man->GetLastError() == Net::vRES_ERROR_DEVICE_NOT_CONNECTED )
		{
			pParams->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_device_error" ));
			Script::RunScript( "create_net_startup_error_dialog", pParams );
		}
		else if( net_man->GetLastError() == Net::vRES_ERROR_UNKNOWN_DEVICE )
		{
			pParams->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_not_detected" ));
			Script::RunScript( "create_net_startup_error_dialog", pParams );
		}
		else if( net_man->GetLastError() == Net::vRES_ERROR_DHCP )
		{
			pParams->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_dhcp_error" ));
			Script::RunScript( "create_net_startup_error_dialog", pParams );
		}
		else if( net_man->GetLastError() == Net::vRES_ERROR_DEVICE_CHANGED )
		{
			pParams->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_changed_device" ));
			Script::RunScript( "create_net_startup_error_dialog", pParams );
		}
		else
		{
			pParams->AddComponent( Script::GenerateCRC( "text" ), ESYMBOLTYPE_STRING, Script::GetString( "net_error_general_error" ));
			Script::RunScript( "create_net_startup_error_dialog", pParams );
		}
	}
	else
	{   
		uint32 success_script;

		pParams->GetChecksum( "success_script", &success_script, true );
		Dbg_Printf( "***** SUCCESS! RUNNING SCRIPT %p\n", success_script );
		Script::RunScript( success_script, pParams );
		
		/*Script::CStruct* pParams;

		Script::RunScript( "dialog_box_exit" );
		//Script::RunScript( "launch_network_select_menu" );
		
        pParams = new Script::CStruct;

		pParams->AddChecksum( "change_gamemode", Script::GenerateCRC( "change_gamemode_net" ));
		Script::RunScript( "launch_select_skater_menu", pParams );
		
		delete pParams;*/
	}

	return properly_setup;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptNeedToTestNetSetup( Script::CStruct *pParams, Script::CScript *pScript )
{
	Net::Manager * net_man =  Net::Manager::Instance();
	
	return net_man->NeedToTestNetworkEnvironment();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptCanChangeDevices( Script::CStruct *pParams, Script::CScript *pScript )
{
	Net::Manager * net_man =  Net::Manager::Instance();

	return net_man->CanChangeDevices();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ConnectToInternet | 
bool ScriptConnectToInternet( Script::CStruct *pParams, Script::CScript *pScript )
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	uint32 success, failure;

	success = 0;
	failure = 0;

	pParams->GetChecksum( CRCD(0x90ff204d,"success"), &success );
	pParams->GetChecksum( CRCD(0xde64fc3e,"failure"), &failure );

	return gamenet_man->ConnectToInternet( success, failure );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptCancelConnectToInternet( Script::CStruct* pParams, Script::CScript* pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->CancelConnectToInternet();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptCancelLogon( Script::CStruct *pParams, Script::CScript *pScript )
{
#ifdef __PLAT_XBOX__
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	gamenet_man->mpAuthMan->CancelLogon();
	return true;
#else
	return true;
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptIsOnline( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Net::Manager * net_man =  Net::Manager::Instance();

	return net_man->IsOnline();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DisconnectFromInternet | 
bool ScriptDisconnectFromInternet( Script::CStruct *pParams, Script::CScript *pScript )
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	uint32 callback_script = 0;

	pParams->GetChecksum( CRCD(0x86068bd9,"callback"), &callback_script );
	gamenet_man->DisconnectFromInternet( callback_script );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StopAllScripts | This will cause all scripts in existence
// to stop (including the one calling this function). This function may
// be useful later when doing cleanup type stuff. Note that though this
// command will stop all scripts in existence at the moment it is called,
// it won't prevent new scripts being run by the C-code
bool ScriptStopAllScripts(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Script::StopAllScripts();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetMenuElementText | sets the specified menu element
// @uparmopt "Blaa" | text to use for menu element - "Blaa" is the actual default
bool ScriptSetMenuElementText(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	/*
	uint32 Id=0;
	pParams->GetChecksum("Id",&Id);
	Dbg_MsgAssert(Id,("SetMenuElementText requires an Id"));
	
	const char *pText="Blaa";
	pParams->GetText(NONAME,&pText);
	
	Front::MenuEvent event;
	event.SetTypeAndTarget(Front::MenuEvent::vSETCONTENTS,Id);
	Script::CStruct *pData = event.GetData();		
	pData->AddComponent(Script::GenerateCRC("string"), ESYMBOLTYPE_STRING, pText);
	 Front::MenuFactory * pMenuFactory =  Front::MenuFactory::Instance();
	pMenuFactory->LaunchEvent(&event);
	*/
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static bool FirstTimeThisIsCalled_Flag=true;
// @script | FirstTimeThisIsCalled | returns true the first time this is called
bool ScriptFirstTimeThisIsCalled(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	if (FirstTimeThisIsCalled_Flag)
	{
		FirstTimeThisIsCalled_Flag=false;
		return true;
	}
	return false;		
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EnableActuators | 
// @uparm 1 | 1 for enable - 0 for disable.  1 is the default
bool	ScriptEnableActuators(Script::CStruct *pParams, Script::CScript *pScript)
{	
	int on = 1;	
	pParams->GetInteger(NONAME,&on);
	Inp::Manager * input_man =  Inp::Manager::Instance();
	Mdl::Skate * pSkate =  Mdl::Skate::Instance();
	for (int i=0; i< SIO::vMAX_DEVICES; ++i)
	{
		if ( pSkate->mp_controller_preferences[i].VibrationOn )
		{
			if (on)
			{
				input_man->EnableActuator(i);
			}
			else
			{
				input_man->DisableActuator(i);
			}
		}
	}

	return true;
}
					  

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | InNetGame | returns true if we're in a net game
bool ScriptInNetGame(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	 
	return gamenet_man->InNetGame();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsObserving | You can now use the "IsObserving" script
// command to perform logic (ex. hide objects) if the player is
// Observing a network game.
bool ScriptIsObserving( Script::CStruct *pParams, Script::CScript *pScript )
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	GameNet::PlayerInfo* player;
	GameNet::NewPlayerInfo* new_player;
	Lst::Search< GameNet::NewPlayerInfo > sh;

	if(( player = gamenet_man->GetLocalPlayer()))
	{
		return player->IsObserving();
	}
		
	// If we're observing, we need to remove our skater
	for( new_player = gamenet_man->FirstNewPlayerInfo( sh ); new_player; new_player = gamenet_man->NextNewPlayerInfo( sh ))
	{
		if( new_player->Flags & GameNet::PlayerInfo::mLOCAL_PLAYER )
		{
			if( new_player->Flags & GameNet::PlayerInfo::mOBSERVER )
			{
				return true;
			}
		}
	}

	return( gamenet_man->GetJoinMode() == GameNet::vJOIN_MODE_OBSERVE );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SkatersAreReady | true if all skaters connected to the server
// are done loading
bool ScriptSkatersAreReady(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	
	// poll the server to find out whether the skaters are done loading
	if( gamenet_man->ReadyToPlay() && 
		( ( skate_mod->GetNumSkaters() > 0 ) ||
		  ( ScriptIsObserving( NULL, NULL ))))
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSlomo | sets slomo to specified value
// @uparmopt 1.0 | value 
bool ScriptSetSlomo(Script::CStruct *pParams, Script::CScript *pScript)
{
	float slomo = 1.0f;
	pParams->GetFloat(NONAME,&slomo);
	Tmr::SetSlomo(slomo);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetArenaSize | Obsolete function
bool ScriptSetArenaSize(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_Message( "SetArenaSize is obsolete" );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetParticleSysVisibility | no longer supported.
bool ScriptSetParticleSysVisibility( Script::CStruct *pParams, Script::CScript *pScript )
{
	printf ("STUBBED: ScriptSetParticleSysVisibility\n");
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | TogglePlayerNames | 
bool ScriptTogglePlayerNames( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->TogglePlayerNames();
	if( !gamenet_man->ShouldDrawPlayerNames())
	{
		Script::RunScript( "destroy_all_player_names" );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/*************************                          *****************************************/

// @script | SetCurrentGameType | sets the current game type
bool ScriptSetCurrentGameType( Script::CStruct *pParams, Script::CScript *pScript )
{

	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	skate_mod->SetCurrentGameType();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DumpNetMessageStats | displays all net message stats
bool ScriptDumpNetMessageStats( Script::CStruct *pParams, Script::CScript *pScript )
{
	Net::Conn* conn;
	Lst::Search< Net::Conn > sh;
	Net::App* app;
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
#ifdef __NOPT_ASSERT__
	 Net::Manager * net_man =  Net::Manager::Instance();
#endif		// __NOPT_ASSERT__
	int i, j, total_size, total_num, size, num;
	Net::Metrics* metrics;

	app = gamenet_man->GetServer();
	if( app == NULL )
	{
		app = gamenet_man->GetClient( 0 );
	}

	if( app == NULL )
	{
		return true;
	}

	i = 0;
    for( conn = app->FirstConnection( &sh ); conn; conn = app->NextConnection( &sh ))
	{
		metrics = conn->GetInboundMetrics();
		Dbg_Printf( "Conn %d inbound stats:\n", i );
		total_size = 0;
		total_num = 0;
		for( j = 0; j < 256; j++ )
		{
			total_size += metrics->GetTotalMessageData( j );
			total_num += metrics->GetTotalNumMessagesOfId( j );
		}

		// Guard against dbz
		if( total_size == 0 )
		{
			total_size = 1;
		}

		for( j = 0; j < 256; j++ )
		{
			size = metrics->GetTotalMessageData( j );
			num = metrics->GetTotalNumMessagesOfId( j );

			if( num > 0 )
			{
#				ifdef __NOPT_ASSERT__
				Dbg_Printf( "[%d-%s] Num: %d Size: %d Pct: %02f\n", j, net_man->GetMessageName( j ),
						num, size, (float) size/(float) total_size );
#				endif // __NOPT_ASSERT__
			}
		}

		metrics = conn->GetOutboundMetrics();
		Dbg_Printf( "Conn %d outbound stats:\n", i );
		total_size = 0;
		total_num = 0;
		for( j = 0; j < 256; j++ )
		{
			total_size += metrics->GetTotalMessageData( j );
			total_num += metrics->GetTotalNumMessagesOfId( j );
		}

		// Guard against dbz
		if( total_size == 0 )
		{
			total_size = 1;
		}

		for( j = 0; j < 256; j++ )
		{
			size = metrics->GetTotalMessageData( j );
			num = metrics->GetTotalNumMessagesOfId( j );

			if( num > 0 )
			{
#				ifdef __NOPT_ASSERT__
				Dbg_Printf( "[%d-%s] Num: %d Size: %d Pct: %02f\n", j, net_man->GetMessageName( j ),
						num, size, (float) size/(float) total_size );
#				endif // __NOPT_ASSERT__
			}
		}
		i++;
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetServerMode | sets the server mode
// @flag off | 
bool  ScriptSetServerMode(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	bool off = pParams->ContainsFlag("off");
    
	gamenet_man->SetServerMode( !off );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | NotifyBailDone | 
bool ScriptNotifyBailDone( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	Net::Client* client;	

	Obj::CSkater *pSkater=NULL;
	if( pScript && pScript->mpObject && pScript->mpObject->GetType() == SKATE_TYPE_SKATER )	
	{
		// If the script using this has an object associated with it, and that object
		// is a skater, then use that skater.
		pSkater= (Obj::CSkater *) pScript->mpObject.Convert();
	}

	if( pSkater && pSkater->IsLocalClient())
	{
		client = gamenet_man->GetClient( pSkater->GetSkaterNumber());
		if( client )
		{
			Net::MsgDesc msg_desc;

			msg_desc.m_Id = GameNet::MSG_ID_BAIL_DONE;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
			client->EnqueueMessageToServer( &msg_desc );
		}
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DisplayLoadingScreen | shows the loading screen 
// @uparm "string" | name of the screen to display
// @uparmopt time | the amount of time to display loading bar (default is 0, meaning no loading bar)
// @flag Freeze | just freeze current screen
// @flag Blank | clear screen to black
bool ScriptDisplayLoadingScreen( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	const char	*pScreen = "images\\xxxxx";	
	pParams->GetText(NONAME,&pScreen);
	
	Nx::CLoadScreen::sDisplay( (char*) pScreen, pParams->ContainsFlag(CRCD(0xb96e0be5,"Freeze")),pParams->ContainsFlag(CRCD(0xc3d43b9a,"blank")));

	float duration;
	if (pParams->GetFloat(NONAME,&duration))
	{
		Nx::CLoadScreen::sStartLoadingBar(duration);
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | HideLoadingScreen | hides the loading screen 
bool ScriptHideLoadingScreen( Script::CStruct *pParams, Script::CScript *pScript )
{
	//LoadScreen::Hide();
	Nx::CLoadScreen::sHide();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EnterObserverMode | changes to observer mode (for net game)
bool ScriptEnterObserverMode(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->RequestObserverMode();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ObserveNextSkater | 
bool ScriptObserveNextSkater( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	skate_mod->ObserveNextSkater();

    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AllowPause | 
// @flag off | do not allow pause
bool ScriptAllowPause(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	bool on = true;
	if (pParams->ContainsFlag("off"))
	{
		on = false;
	}		

	printf ("WARNING: ScriptAllowPause is STUBBED\n");	
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RefreshServerList | gets a new server list 
bool ScriptRefreshServerList(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->RefreshServerList( pParams->ContainsFlag( "force_refresh" ));
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StartServerList | 
bool ScriptStartServerList(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->StartServerList();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptStartLobbyList( Script::CStruct* pParams, Script::CScript* pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	if(	gamenet_man->GetServerListState() == GameNet::vSERVER_LIST_STATE_SHUTDOWN )
	{
		gamenet_man->SetNextServerListState( GameNet::vSERVER_LIST_STATE_STARTING_LOBBY_LIST );
		gamenet_man->SetServerListState( GameNet::vSERVER_LIST_STATE_INITIALIZE );
	}
	else
	{
		gamenet_man->SetServerListState( GameNet::vSERVER_LIST_STATE_STARTING_LOBBY_LIST );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StopServerList | 
bool ScriptStopServerList(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->StopServerList();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | FreeServerList | 
bool ScriptFreeServerList(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->FreeServerList();
	
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PauseGameFlow | suspends gameflow - should only be called from
// gameflow.q
bool ScriptPauseGameFlow(Script::CStruct *pParams, Script::CScript *pScript)
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	skate_mod->PauseGameFlow( true );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnPauseGameFlow | 
bool ScriptUnpauseGameFlow(Script::CStruct *pParams, Script::CScript *pScript)
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	skate_mod->PauseGameFlow( false );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | InFrontEnd | returns true if we are in the front end
bool ScriptInFrontEnd(Script::CStruct *pParams, Script::CScript *pScript)
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	return skate_mod->GetGameMode()->IsFrontEnd();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | InSplitScreenGame | returns true if we are in a split screen game
bool ScriptInSplitScreenGame(Script::CStruct *pParams, Script::CScript *pScript)
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	return skate_mod->IsMultiplayerGame() && !gamenet_man->InNetGame() && !skate_mod->GetGameMode()->IsFrontEnd();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | InMultiPlayerGame | 
bool ScriptInMultiplayerGame(Script::CStruct *pParams, Script::CScript *pScript)
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	
	return skate_mod->IsMultiplayerGame();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetFireballLevel(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	int level;
	 
	Script::CStruct* params = pScript->GetParams();

 	level = skate_mod->GetGameMode()->GetFireballLevel();
	params->AddInteger( CRCD(0x651533ec,"level"), level );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GameModeEquals | returns true if the current game mode matches
// the specified game mode
// @uparm name | game mode to check. More than one may be specified, eg GameModeEquals is_singlesession is_creategoals
bool ScriptGameModeEquals(Script::CStruct *pParams, Script::CScript *pScript)
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	 
 	CComponent *pComp=pParams->GetNextComponent();
	while (pComp)
	{
		if (pComp->mNameChecksum==0 && pComp->mType==ESYMBOLTYPE_NAME)
		{
			if (skate_mod->GetGameMode()->IsTrue(pComp->mChecksum))
			{
				return true;
			}
		}
		pComp=pParams->GetNextComponent(pComp);
	}	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | OnServer | returns true if we are on a server
bool ScriptOnServer(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	return gamenet_man->OnServer();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetCurrentLevel | returns level=load_foun  (or whatever)
// so you can use it with LoadLevel, without going through LoadRequestedLevel
bool ScriptGetCurrentLevel(Script::CStruct *pParams, Script::CScript *pScript)
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	pScript->GetParams()->AddComponent( Script::GenerateCRC("level"), ESYMBOLTYPE_NAME, (int)skate_mod->m_requested_level );
	
	Game::CGoalManager* p_goal_manager = Game::GetGoalManager();
	Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager ?"));
	pScript->GetParams()->AddChecksum(CRCD(0x16a0b364,"level_structure"),p_goal_manager->GetLevelStructureName());
	
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RestartLevel | RestartLevel is just intended to be called
// when a level is restarted it does not actually restart the level,
// it just sets a few flags and does something with disabling the the
// viewer log that's only related to screenshot mode 
bool ScriptRestartLevel(Script::CStruct *pParams, Script::CScript *pScript)
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	skate_mod->RestartLevel();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleScores | toggles game scores
bool ScriptToggleScores(Script::CStruct *pParams, Script::CScript *pScript)
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->ToggleScores();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | XTriggered | 
bool ScriptXTriggered(Script::CStruct *pParams, Script::CScript *pScript)
{
    Dbg_MsgAssert( 0, ( "Obsolete function" ) );
    return false;

//	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
//	return skate_mod->XTriggered();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UsePad | sets the specified skater to use the pad
// @uparmopt 0 | skater id (default is 0)
bool ScriptUsePad(Script::CStruct *pParams, Script::CScript *pScript)
{
    Dbg_MsgAssert( 0, ( "Obsolete function" ) );
    return false;
	
/*
    int skaterId = 0;
	pParams->GetInteger( NONAME, &skaterId );

	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	skate_mod->UsePad( skaterId );
	return true;
*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsTrue | Checks to see whether some global constant equals
// 1 or 0.  (If it's not defined anywhere, then it returns 0.) 
// Usage: 
// if IsTrue run_viewer 
//    printf "I am in viewer mode" 
// else 
//    printf "I am in skateshop mode" 
// endif 
// if IsTrue test_balls 
// where test_balls is defined in "yourname".q. 
//    printf "I am testing my balls here" 
// endif 
// @uparm name | global constant to check
bool ScriptIsTrue(Script::CStruct *pParams, Script::CScript *pScript)
{
	int Integer=0;
	pParams->GetInteger( NONAME, &Integer, false );
	
	return Integer;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GameFlow | 
// @uparm name | script name
bool ScriptGameFlow(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 ScriptChecksum=0;
	pParams->GetChecksum(NONAME,&ScriptChecksum);
	Dbg_MsgAssert(ScriptChecksum,("\n%s\nGameFlow requires a script name, eg Gameflow Blaa",pScript->GetScriptInfo()));

	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	Dbg_AssertPtr( skate_mod->mp_gameFlow );
	skate_mod->mp_gameFlow->Reset( ScriptChecksum );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptLastBroadcastedCheatWas(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 cheat_flag;

	pParams->GetChecksum(CRCD(0xae94c183,"cheat_flag"), &cheat_flag, true );

	return( s_last_broadcast_cheat == cheat_flag );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptClearCheats(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	Obj::CSkaterCareer* career;

	career = skate_mod->GetCareer();
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x13d5b5db,"CHEAT_ON_1")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x8adce461,"CHEAT_ON_2")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0xfddbd4f7,"CHEAT_ON_3")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x63bf4154,"CHEAT_ON_4")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x14b871c2,"CHEAT_ON_5")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x8db12078,"CHEAT_ON_6")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0xfab610ee,"CHEAT_ON_7")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x6a090d7f,"CHEAT_ON_8")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x1d0e3de9,"CHEAT_ON_9")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x37cbee45,"CHEAT_ON_10")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x40ccded3,"CHEAT_ON_11")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0xd9c58f69,"CHEAT_ON_12")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0xaec2bfff,"CHEAT_ON_13")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x30a62a5c,"CHEAT_ON_14")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x47a11aca,"CHEAT_ON_15")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0xdea84b70,"CHEAT_ON_16")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0xa9af7be6,"CHEAT_ON_17")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x39106677,"CHEAT_ON_18")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x4e1756e1,"CHEAT_ON_19")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x1ce6bd86,"CHEAT_ON_20")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x6be18d10,"CHEAT_ON_21")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0xf2e8dcaa,"CHEAT_ON_22")));
	career->UnSetGlobalFlag( Script::GetInteger(CRCD(0x85efec3c,"CHEAT_ON_23")));

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptBroadcastCheat(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	Lst::Search< Net::Conn > sh;
	Net::Conn* conn;
	Net::Server* server;
	uint32 cheat_flag;
	char on;

	pParams->GetChecksum(CRCD(0xae94c183,"cheat_flag"), &cheat_flag, true );
	Dbg_Assert( gamenet_man->OnServer());
	Dbg_Assert( gamenet_man->InNetGame());
	if( pParams->ContainsComponentNamed(CRCD(0xf649d637,"on")))
	{
		on = 1;
	}
	else
	{
		on = 0;
	}

	// Intentionally sprinkled in this function so that hackers have a more-difficult time to 
	// nullify it
	s_last_broadcast_cheat = cheat_flag;
	server = gamenet_man->GetServer();
	for( conn = server->FirstConnection( &sh ); conn; conn = server->NextConnection( &sh ))
	{
		if( conn->IsRemote())
		{
			Net::MsgDesc msg_desc;
			GameNet::MsgToggleCheat msg;
	
			msg.m_Cheat = cheat_flag;
			msg.m_On = on;
	
			msg_desc.m_Id = GameNet::MSG_ID_TOGGLE_CHEAT;
			msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
			msg_desc.m_Data = &msg;
			msg_desc.m_Length = sizeof( GameNet::MsgToggleCheat );
			msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
		
			server->EnqueueMessage( conn->GetHandle(), &msg_desc );
			s_last_broadcast_cheat = cheat_flag;
		}
	}

	s_last_broadcast_cheat = cheat_flag;
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptCheatAllowed(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 cheat_flag;

	pParams->GetChecksum(CRCD(0xae94c183,"cheat_flag"), &cheat_flag, true );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | JoinWithPassword | joins the server with the specified password
// @parm string | string | password string
bool ScriptJoinWithPassword(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
    
	const char* p_string;
	pParams->GetText( "string", &p_string, true );

    gamenet_man->ReattemptJoinWithPassword((char*) p_string );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SendChatMessage | 
// @parm string | string | the message to send
bool ScriptSendChatMessage(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	const char* p_string = NULL;
	pParams->GetText( "string", &p_string );
	if( p_string == NULL )
	{
		return false;
	}

#if ( ENGLISH == 0 )
	if ( ( p_string != NULL ) && ( strlen( p_string ) > 0 ) && ( stricmp( p_string, Script::GetLocalString( "kc_str_empty" ) ) ) )
#else
	if ( ( p_string != NULL ) && ( strlen( p_string ) > 0 ) && ( stricmp( p_string, "--EMPTY--" ) ) )
#endif
	{
		 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
		gamenet_man->SendChatMessage( (char*) p_string );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | InSlapGame | true if we are in a slap or netslap game
bool ScriptInSlapGame( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	if( ( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netslap" )) ||
		(skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "slap" )))
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetScreenMode | Sets the way the screen will be split up into separate
// viewports in the game. If there is more than one viewport, and multiple skaters
// are active, then each skater will get his own viewport
// @uparm One_Camera | the mode to use - one of the following: 
// One_Camera 
// Split_Vertical 
// Split_Horizontal 
// Split_Quarters
bool ScriptSetScreenMode(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	Nx::ScriptSetScreenMode( pParams, pScript );

	Obj::CSkater* pSkater;
	Mdl::Score* pScore;
	for( uint i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
	{
		pSkater = skate_mod->GetSkater(i);						   
		if ( pSkater )			// Skater might not exist
		{
			pScore = pSkater->GetScoreObject();
			pScore->RepositionMeters();
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetScreenModeFromGameMode | sets the appropriate screen mode based on the game mode
bool ScriptSetScreenModeFromGameMode( Script::CStruct *pParams, Script::CScript *pScript )
{           
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	Nx::ScreenMode requested_mode, current_mode;

	Obj::CSkater* pSkater;
	Mdl::Score* pScore;
	for( uint i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
	{
		pSkater = skate_mod->GetSkater(i);						   
		if ( pSkater && pSkater->IsLocalClient())			// Skater might not exist
		{
			printf ("cfuncs %d: SUTUBBBEDDDDDDDDDDDDDDDDDDDDDDDD\n",__LINE__);
			
			/*							
			Gfx::Camera *p_camera = pSkater->GetSkaterCam();
			if (p_camera)
			{
				Nx::CViewportManager::sSetCameraAllScreenModes(pSkater->GetHeapIndex(), p_camera);
			}
			*/
		}
	}
	
	// set up the appropriate screen mode
	requested_mode = skate_mod->GetGameMode()->GetScreenMode();
	current_mode = Nx::CViewportManager::sGetScreenMode();
	if(! ((	( requested_mode == Nx::vSPLIT_V ) &&
			( current_mode == Nx::vSPLIT_H )) ||
			(( requested_mode == Nx::vSPLIT_H ) &&
			( current_mode == Nx::vSPLIT_V ))))
	{
		Nx::CViewportManager::sSetScreenMode( requested_mode );
	}

	for( uint i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
	{
		pSkater = skate_mod->GetSkater(i);						   
		if ( pSkater && pSkater->IsLocalClient())			// Skater might not exist
		{
			pScore = pSkater->GetScoreObject();
			pScore->RepositionMeters();
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadPendingPlayers | 
bool ScriptLoadPendingPlayers( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->LoadPendingPlayers();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LaunchQueuedScripts | 
bool ScriptLaunchQueuedScripts( Script::CStruct *pParams, Script::CScript *pScript )
{
	GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->LaunchQueuedScripts();

	return true;
}

//////////////////////////////////////////////////////////////////////////////////////
// extend the current parameters to include:  string = "WOW"

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetInitialsString | gets the initials string for the player
bool ScriptGetInitialsString( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mdl::Skate * pSkate =  Mdl::Skate::Instance();
	Records::CGameRecords *pGameRecords=pSkate->GetGameRecords();
	
	pScript->GetParams()->AddComponent( Script::GenerateCRC( "string" ), ESYMBOLTYPE_STRING, pGameRecords->GetDefaultInitials()->Get() );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetInitialsString | sets the initials string for the player
// @parm string | string | initials string
bool ScriptSetInitialsString( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	 Mdl::Skate * pSkate =  Mdl::Skate::Instance();
	Records::CGameRecords *pGameRecords=pSkate->GetGameRecords();	
	const char *pInitials = "ERR";
	pParams->GetText("string",&pInitials);
	pGameRecords->SetDefaultInitials(pInitials);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AttachToSkater | (DEPRECATED) Make the current script be running on the skater 
// used for the gameflow, where we are not always sure if there is a skater there 
// use this in place of SpawnSkaterScript in the gameflow 
// if "AttachToSkater End" is called then will detatch it 
// must be in mathcing pairs 
// otherwise the gameflow gets messed up 
// when the skater is deleted (as the gameflow gets stopped and deleted) 
// @flag End | 
bool	ScriptAttachToSkater(  Script::CStruct *pParams, Script::CScript *pScript )
{

	Dbg_MsgAssert(0,("ScriptAttachToSkater should not be called, as it is old and dangerous\n"));	
	
	if (pParams->ContainsFlag("End"))
	{
		pScript->mpObject = NULL; 
	}
	else
	{
		 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
		Obj::CSkater *pSkater = skate_mod->GetLocalSkater();
		Dbg_MsgAssert(pSkater,("AttachToSkater called before skater exists"));
		pScript->mpObject = pSkater; 
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | TryCheatString | 
// @parm string | string | cheat string to try
bool	ScriptTryCheatString(  Script::CStruct *pParams, Script::CScript *pScript )
{
	
	char  b[128];
	const char *p;
	pParams->GetText("string",&p,true);
	if (strlen(p) < 6)
	{
		Script::RunScript("cheat_playsound_bad");
		return false;
	}
	
	sprintf(b,"12345678901234567890"); 		// just in case.....
	sprintf(b,p);
	
	// (Mick)									  
	// We want to generate a 64 bit CRC
	// so we take 3 checksums, starting from 3 different places
	// and add the first one to the second two
	// and use those two numbers as a 64 bit checksum
	// The idea here is to create a number, from which is is difficult to
	// go back to the original string
	// (to do this, you need a fairlry long string, otherwise, you could just try them all
	// now we do 1 second worth of PS2 precessing the generate the key,
	// so even on a computer 1000 times as fast, it would take ~30 years to try all six letter keys
	// So, barring a flaw in my algorithm, should be pretty safe even with short keys 
	
	uint32 check0 = Script::GenerateCRC(p);	  // get checksum
	
	uint32 check1 = 0;	
	for (int i = 0;i< 100000;i++)			  // a hundred thousand calls, to mess with Ken
	{
		sprintf (b,"%d",i+check0);			  // print a different number at the start
		b[strlen(b)]='X';					  // link it to the rest of the string
		char *p1 = b;
		while (*p1)
		{
			check1 += 1023 * (*p1++);				  // and just add up the bytes, oh, how sweet...
		}
		check0 = Script::GenerateCRC(b);	  // oh, I slay myself...
	}
	uint32 check2 = Script::GenerateCRC(&p[strlen(p)/3]);	
	uint32 check3 = Script::GenerateCRC(b);	   // use the final string, so no correlation with check2
	
//	printf ("%x,%x,%x\n",check1,check2,check3);	 // three pretty dang random looking numbers

	check2 ^= check1;							 // munched into two
	check3 ^= check1;							 // (hopefully not a fatal mistake)
	
//	printf ("%x,%x,%x\n",check1,check2,check3);
	// Print it out, so we can cut and paste
	printf ("\n{c1=%d c2=%d CheatScript=cheat_xxx },  ; %s\n\n",check2, check3,p); 
	// then search through the cheat array to see if we have a match


	#ifdef	__PLAT_XBOX__				 	
	Script::CArray *pCheatArray = Script::GetArray( "Cheat_Array_Xbox" );
	#endif
	#ifdef	__PLAT_NGC__				 	
	Script::CArray *pCheatArray = Script::GetArray( "Cheat_Array_Gamecube" );
	#endif
	#ifdef	__PLAT_NGPS__				 	
	Script::CArray *pCheatArray = Script::GetArray( "Cheat_Array_PS2" );
	#endif
	
	Dbg_MsgAssert( pCheatArray,( "No Cheat_Array found" ));
	int cheats = pCheatArray->GetSize();
	bool cheated = false;
	for (int cheat = 0;cheatGetStructure(cheat);
		Dbg_MsgAssert( pStruct,( "Cheat Array messed up" ));
		uint32 c1,c2;
		uint32 cheat_script;
		pStruct->GetInteger("c1",(int*)&c1,true);
		pStruct->GetInteger("c2",(int*)&c2,true);
		pStruct->GetChecksum("cheatscript",&cheat_script,true);
		if (c1 == check2 && c2 == check3)
		{
			Script::RunScript("cheat_playsound_good");
			Script::RunScript(cheat_script);
			cheated = true;
		}
	}
	
	if (!cheated)
	{
			Script::RunScript("cheat_playsound_bad");
	}


								
	return false;								
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LevelIs | true if the level matches the specified level
// @uparm name | level name
bool ScriptLevelIs(  Script::CStruct *pParams, Script::CScript *pScript )
{
	

	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	
	// GJ:  this may not work in park editor mode
	uint32 level_name;
	pParams->GetChecksum( NONAME, &level_name, true );
	
	return ( skate_mod->m_cur_level == level_name );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StartNetworkLobby | 
bool ScriptStartNetworkLobby( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	gamenet_man->StartNetworkLobby();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ObserversAllowed | 
bool ScriptObserversAllowed( Script::CStruct *pParams, Script::CScript *pScript )
{
	 Net::Manager * net_man =  Net::Manager::Instance();
	
	// Modem games cannot host observers
	return( net_man->GetConnectionType() != Net::vCONN_TYPE_MODEM );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | NumPlayersAllowed | 
// @uparm name | number of players allowed
bool ScriptNumPlayersAllowed( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	
	uint32 checksum = 0;
	 Net::Manager * net_man =  Net::Manager::Instance();

	// Non-Modem games have no max player restriction
	if( net_man->GetConnectionType() != Net::vCONN_TYPE_MODEM )
	{
		return true;
	}
    
	pParams->GetChecksum( NONAME, &checksum );
	if( checksum == Script::GenerateCRC( "num_4" ))
	{
		return false;
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AutoDNS | true if auto DNS
bool ScriptAutoDNS( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	Script::CStruct* pStructure;
	Prefs::Preferences* pPreferences;
	uint32 auto_dns_checksum;
	 
	pPreferences = gamenet_man->GetNetworkPreferences();

	pStructure = pPreferences->GetPreference( Script::GenerateCRC("auto_dns") );
	pStructure->GetChecksum( "Checksum", &auto_dns_checksum, true );
	return ( auto_dns_checksum == Script::GenerateCRC( "boolean_true" ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UsingDefaultMasterServers | true if using the default master
// servers
bool ScriptUsingDefaultMasterServers( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	Script::CStruct* pStructure;
	Prefs::Preferences* pPreferences;
	uint32 default_servers;
	 
	pPreferences = gamenet_man->GetNetworkPreferences();

	pStructure = pPreferences->GetPreference( Script::GenerateCRC("use_default_master_servers") );
	pStructure->GetChecksum( "Checksum", &default_servers, true );
	return ( default_servers == Script::GenerateCRC( "boolean_true" ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UsingDHCP | true if using DHCP
bool ScriptUsingDHCP( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	Script::CStruct* pStructure;
	Prefs::Preferences* pPreferences;
	uint32 ip_assignment;
	 
	pPreferences = gamenet_man->GetNetworkPreferences();

	pStructure = pPreferences->GetPreference( Script::GenerateCRC("broadband_type") );
	pStructure->GetChecksum( "Checksum", &ip_assignment, true );
	return ( ip_assignment == Script::GenerateCRC( "ip_dhcp" ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | InInternetMode | 
bool ScriptInInternetMode( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();

	return gamenet_man->InInternetMode();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EnteringNetGame | true if we're in a network game but not 
// read to play
bool ScriptEnteringNetGame( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

	 return(( gamenet_man->InNetGame()) &&
			( skate_mod->m_prev_level == Script::GenerateCRC( "Load_Skateshop" )));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DeviceChosen | true if a device type has been chosen
bool ScriptDeviceChosen( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	uint32 checksum;
	Prefs::Preferences* pPreferences;
	Script::CStruct* pStructure;

	pPreferences = gamenet_man->GetNetworkPreferences();
	Dbg_Assert( pPreferences );

	pStructure = pPreferences->GetPreference( Script::GenerateCRC("device_type") );
	pStructure->GetChecksum( "checksum", &checksum, true );

	return ( checksum != Script::GenerateCRC("device_none"));
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GameIsOver | true if game is over
bool ScriptGameIsOver( Script::CStruct *pParams, Script::CScript *pScript )
{
	 GameNet::Manager * gamenet_man =  GameNet::Manager::Instance();
	
	return gamenet_man->GameIsOver();
}

static char 	s_level_name[64];

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetLevelName | gets the level name into the above static, so it can be used for the DumpHeaps memory profile dump 
bool ScriptSetLevelName( Script::CStruct *pParams, Script::CScript *pScript )
{
	const char* pName;
	pParams->GetText( NONAME, &pName, true );
	sprintf(s_level_name,pName);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetPS2 | For testing the reset button handler when
// the Sony Network adapter is loaded
bool ScriptResetPS2( Script::CStruct *pParams, Script::CScript *pScript )
{

#	ifdef __PLAT_NGPS__
	int stat;

	Dbg_Printf( "Resetting PS2\n" );
	
	while( sceDevctl("dev9x:", DDIOC_OFF, NULL, 0, NULL, 0 ) < 0 );
	// PS2 power off
	while( !sceCdPowerOff( &stat ) || stat );
#	endif // __PLAT_NGPS__

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetHD | For testing the reset button handler when
// the Sony Network adapter is loaded
bool ScriptResetHD( Script::CStruct *pParams, Script::CScript *pScript )
{
	Dbg_Printf( "Resetting PS2\n" );

#	ifdef __PLAT_NGPS__
	int stat;
	sceDevctl("pfs:", PDIOC_CLOSEALL, NULL, 0, NULL, 0);
	while (sceDevctl("hdd:", HDIOC_DEV9OFF, NULL, 0, NULL, 0) < 0);
	while (sceDevctl("dev9x:", DDIOC_OFF, NULL, 0, NULL, 0) < 0);
    while (!sceCdPowerOff(&stat) || stat);
#	endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PAL | returns true if we're in PAL mode
bool ScriptPAL( Script::CStruct *pParams, Script::CScript *pScript )
{
	
#ifdef	PAL
	return true;
#else
	return false;
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Change | changes the symbol to the new value specified.  will do nothing
// if called on the following: a symbol that doesn't already exist, a script symbol, c-function
// or member function symbols 
// should really only be called on strings, floats, and ints 
// example: Change Foo=7
// @uparm name | the name of the symbol to change
// @uparm value | the value to assign to the symbol (make sure type is correct)
// @flag Resolve | If specified, resolve the right hand side if it is a name of a global, so as to lose
// the reference to that global. Ie, Change a=b Resolve where a=6 and b=3.141 will change a to be 3.141,
// and it will remain so even if b is then changed to something else.
bool ScriptChangeSymbolValue(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Extract the first component from pParams, which is used to define the name of the
	// symbol to change and the new value to give to it.
	CComponent *pComp=pParams->GetNextComponent();
	Dbg_MsgAssert(pComp,("\n%s\nChange function requires a symbol name and new value, eg Change Foo=7",pScript->GetScriptInfo()));
	Dbg_MsgAssert(pComp->mNameChecksum,("\n%s\nChange function requires a symbol name and new value, eg Change Foo=7",pScript->GetScriptInfo()));
	if (!pComp)
	{
		return false;
	}
		
	// Do a few checks on the old symbol.
	CSymbolTableEntry *pOld=Script::LookUpSymbol(pComp->mNameChecksum);
	
	// Require that the old symbol must exist, otherwise continuing will create the symbol and it will never get deleted,
	// causing fragmentation & memory leaks etc.
	if (!pOld)
	{
		Dbg_MsgAssert(0,("\n%s\nTried to change the value of the non-existent symbol '%s'",pScript->GetScriptInfo(),FindChecksumName(pComp->mNameChecksum)));
		return false;
	}	
	// Don't allow script symbols to be changed. If this were allowed then all existing CScripts that were running that
	// script would have to get halted to avoid their PC's becoming invalid, which would require some extra code
	// and seems kind of a silly thing to want to do anyway.
	if (pOld->mType==ESYMBOLTYPE_QSCRIPT)
	{
		Dbg_MsgAssert(0,("\n%s\nTried to change the value of the script '%s'",pScript->GetScriptInfo(),FindChecksumName(pComp->mNameChecksum)));
		return false;
	}	
	// Don't allow c-function or member function symbols to be changed, cos once changed they'll never be able to be put back.
	if (pOld->mType==ESYMBOLTYPE_CFUNCTION)
	{
		Dbg_MsgAssert(0,("\n%s\nTried to change the value of the c-function '%s'",pScript->GetScriptInfo(),FindChecksumName(pComp->mNameChecksum)));
		return false;
	}	
	if (pOld->mType==ESYMBOLTYPE_MEMBERFUNCTION)
	{
		Dbg_MsgAssert(0,("\n%s\nTried to change the value of the member function '%s'",pScript->GetScriptInfo(),FindChecksumName(pComp->mNameChecksum)));
		return false;
	}	
	
	// Hmmm, in theory other types could be changed, but they shouldn't be cos it'll cause fragmentation.
	switch (pOld->mType)
	{
		case ESYMBOLTYPE_INTEGER:
        case ESYMBOLTYPE_FLOAT:
        case ESYMBOLTYPE_NAME:
			break;
		default:
			Dbg_MsgAssert(0,("\n%s\nCannot change the value of '%s' because it has type '%s', and reallocating it will cause memory fragmentation. So there.",pScript->GetScriptInfo(),FindChecksumName(pComp->mNameChecksum),GetTypeName(pOld->mType)));
			break;
	}	
	
	// Remove the old symbol.
	Script::CleanUpAndRemoveSymbol(pOld);

	// Create it afresh.
	CSymbolTableEntry *pNew=Script::CreateNewSymbolEntry(pComp->mNameChecksum); 

	CComponent temp;
	temp.mType=pComp->mType;
	temp.mUnion=pComp->mUnion;

	// If they are changing a value to some name, then support the option of resolving that name,
	// in case it might be a global.
	// For example, suppose a=6, and b=3.141
	// 'Change a=b' will change a from 6 to the checksum b. This means it will preserve the reference to b,
	// so in the C code if a GetFloat("a") is done it will return 3.141, but if b is changed to 28.3, 
	// GetFloat("a") will now return 28.3, because it preserves the reference to b.
	// However, 'Change a=b Resolve' will change a to be the float 3.141. Even if b is now changed, a has
	// lost the reference to b and will stay as 3.141.
	if (temp.mType==ESYMBOLTYPE_NAME)
	{
		if (pParams->ContainsFlag(CRCD(0x991a7bc3,"Resolve")))
		{
			CSymbolTableEntry *p_global=Script::Resolve(temp.mChecksum);
			if (p_global)
			{
				temp.mType=p_global->mType;
				temp.mUnion=p_global->mUnion;
			}
		}
	}
	
	// Copy the new value into the symbol just created.
	switch (temp.mType)
	{
		case ESYMBOLTYPE_INTEGER:
            pNew->mType=ESYMBOLTYPE_INTEGER;
            pNew->mIntegerValue=temp.mIntegerValue;
            break;

        case ESYMBOLTYPE_FLOAT:
            pNew->mType=ESYMBOLTYPE_FLOAT;
            pNew->mFloatValue=temp.mFloatValue;
            break;

        case ESYMBOLTYPE_NAME:
            pNew->mType=ESYMBOLTYPE_NAME;
            pNew->mChecksum=temp.mChecksum;
            break;
			
        default:
            Dbg_MsgAssert(0,("\n%s\nChange function does not support component type '%s'",pScript->GetScriptInfo(),GetTypeName(temp.mType)));
            break;
	}

	// This is to prevent an assert in the CComponent destructor
	temp.mUnion=0;
	
	return true;									   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DumpScripts | Prints out the names of all the currently existing scripts.
bool ScriptDumpScripts( Script::CStruct *pParams, Script::CScript *pScript )
{
	bool just_stack = pParams->ContainsFlag("just_stack");
	
	if (!just_stack)
		Script::DumpScripts();
	
	// XXX
	#ifdef	__NOPT_ASSERT__
	printf("Script stack is: %s\n", pScript->GetScriptInfo());
	#endif
	
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | TimeUp | true if time is up
bool ScriptTimeUp( Script::CStruct *pParams, Script::CScript *pScript )
{
     Mdl::Skate * skate_mod =  Mdl::Skate::Instance();

    return skate_mod->GetGameMode()->ShouldUseClock() && ( skate_mod->GetGameMode()->GetTimeLeft() <= 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LaunchViewer | 
bool ScriptLaunchViewer( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::Manager * mdl_manager =  Mdl::Manager::Instance();
	Mdl::CViewer* viewer_mod;
	static bool initialized = false;
	
	if( initialized )
	{
		return true;
	}

	viewer_mod = new Mdl::CViewer;

	mdl_manager->RegisterModule ( *viewer_mod );
	mdl_manager->StartModule( *viewer_mod );
	initialized = true;
	
	
	// Add the parameters for the skater camera component		
	// so it can get me as the target
	Script::CStruct * p_cam_params = new Script::CStruct;
	p_cam_params->AddChecksum("name",CRCD(0xeb17151b,"viewer_cam"));
	
	// Also create the camera components here.
	Obj::CCompositeObject *p_cam_object = Obj::CCompositeObjectManager::Instance()->CreateCompositeObjectFromNode(
										Script::GetArray("viewercam_composite_structure"),p_cam_params);
	
	delete	p_cam_params;
	p_cam_object->SetLockOn();		// Lock it so it does not get deleted between levels


	return true;
}

// @script | LaunchViewer | 
bool ScriptLaunchScriptDebugger( Script::CStruct* pParams, Script::CScript* pScript )
{
	#ifdef __NOPT_ASSERT__
	static bool initialized = false;
	
	if( initialized )
	{
		return true;
	}

	Mdl::Manager::Instance()->StartModule( *Dbg::CScriptDebugger::Instance() );
	
	initialized = true;
	#endif
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetViewerModel | 
bool ScriptSetViewerModel( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();

	s_view_mode = (int)Obj::VIEWER_SKATER_PAUSED;
	
	// sync the viewer up to cfuncs.cpp's version of s_view_mode
	pViewer->sSetViewMode( s_view_mode );
	// Handle toggling to different cameras based on view mode
	
	switch (s_view_mode )
	{
		case Obj::GAMEPLAY_SKATER_ACTIVE:
			SetActiveCamera(CRCD(0x967c138c,"skatercam0"),0, false);			
			break;
		default:
			SetActiveCamera(CRCD(0xeb17151b,"viewer_cam"),0, true);						
			break;		
	}


	Obj::CSkater* pSkater;
	for ( int i = 0; i < 8; i++ )
	{
		 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
		pSkater = skate_mod->GetSkater(i);						   
		if ( pSkater )			// Skater might not exist
		{
			pSkater->SetViewMode( (Obj::EViewMode)s_view_mode );
		}
	}
	
	if ( pViewer )
	{
		pViewer->ResetCameraToViewerObject();
		
		Script::PrintContents(pParams);

		pViewer->AddViewerObject(pParams);

		/*
		Obj::CViewerObject* pViewerObject = pViewer->GetViewerObject();
		if ( pViewerObject )
		{
			pViewerObject->UnloadModel();
			
			pViewerObject->LoadModel( pParams );
		}
		*/
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetViewerAnim | 
bool ScriptSetViewerAnim( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();

	if ( pViewer )
	{
		uint32 animName;
		pParams->GetChecksum( NONAME, &animName, true );

		Obj::CViewerObject* pViewerObject = pViewer->GetViewerObject();
		if ( pViewerObject )
		{
			pViewerObject->SetAnim( animName );
		}
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptSetViewerLODDist | 
bool ScriptSetViewerLODDist( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();

	if ( pViewer )
	{
		Script::CPair thePair;
		if ( !pParams->GetPair( NONAME, &thePair, Script::NO_ASSERT ) )
		{
			Dbg_Message( "Looking for pair in the format:  SetViewerLODDist (1,500.0f)" );
			return false;
		}

		Obj::CViewerObject* pViewerObject = pViewer->GetViewerObject();
		if ( pViewerObject )
		{
			GetModelComponentFromObject( pViewerObject )->SetModelLODDistance( (int)thePair.mX, thePair.mY );
		}
	}

	return true;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

bool ScriptGetViewerObjectID( Script::CStruct* pParams, Script::CScript* pScript )
{
	// clear out the viewer object, if any
	Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();
	if ( pViewer )
	{
		Obj::CCompositeObject* pViewerObject = pViewer->GetViewerObject();
		pScript->GetParams()->AddChecksum( CRCD(0x0830ecaf,"objID"), pViewerObject->GetID() );
		pScript->GetParams()->AddChecksum( CRCD(0x52280066,"viewerObjectId"), pViewerObject->GetID() );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptReloadViewerAnim( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::CViewer* pViewer = Mdl::CViewer::sGetViewer();

	if ( pViewer )
	{
		Obj::CViewerObject* pViewerObject = pViewer->GetViewerObject();
		if ( pViewerObject )
		{
			uint32 animName;
			const char* fileName;

			if ( pParams->GetChecksum( NONAME, &animName )
				 && pParams->GetText( NONAME, &fileName ) )
			{
				pViewerObject->ReloadAnim( fileName, animName );
			}
			else
			{
				Dbg_Message( "Need 2 parameters to viewer anim:  filename string, and anim checksum" );
			}
		}
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AddRestartsToMenu | adds all restart points to the menu
bool ScriptAddRestartsToMenu( Script::CStruct* pParams, Script::CScript* pScript )
{
	
	Script::CStruct* p_restart_params;
	int 	node = -1;
	int 	entry = 0;
	do
	{
		node = Obj::GetRestartNode(0, 0, node);
		if (node != -1)
		{
			Script::CArray *pNodeArray=Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
			Script::CScriptStructure *pNode=pNodeArray->GetStructure(node);
			const char *pName;
			if (!pNode->GetText("RestartName",&pName))
			{
				pName = "Unnamed restart";
			}
			p_restart_params = new Script::CStruct;	
			p_restart_params->AddString("text",pName);
			p_restart_params->AddChecksum("id",123456 + entry);
			p_restart_params->AddChecksum("no_bg",CRCD(0x9f67b2c8,"no_bg"));
			p_restart_params->AddChecksum("centered",CRCD(0x2a434d05,"centered"));
			p_restart_params->AddChecksum("pad_choose_script",Script::GenerateCRC("skip_to_selected_restart"));
			
			// create the parameters that are passed to the X script
			Script::CStruct *p_script_params= new Script::CStruct;
			p_script_params->AddInteger("node_number",node);	
			p_restart_params->AddStructure("pad_choose_params",p_script_params);			

			/*if (!Script::GetInt("SimpleRestarts",false))
			{
				p_restart_params->AddChecksum("focus_script",Script::GenerateCRC("preview_restart"));
				// also pass it too the Focus script
				p_restart_params->AddStructure("focus_params",p_script_params);						
			}*/
			
            // scale the restarts
            float initial_scale;
            pParams->GetFloat( "initial_scale", &initial_scale, Script::ASSERT );
            p_restart_params->AddFloat( "scale", initial_scale );
            //p_restart_params->AddChecksum( "unfocus_script", Script::GenerateCRC( "scale_down_restart" ) );
			
			Script::RunScript("theme_menu_add_item",p_restart_params);
			delete p_restart_params;
			delete p_script_params;

			entry++;
		}
	} while (node != -1);

	if (entry == 0)
	{
			p_restart_params = new Script::CStruct;	
			p_restart_params->AddString("text","No restarts in level");
			p_restart_params->AddChecksum("id",123456 + entry);
			p_restart_params->AddChecksum("no_bg",CRCD(0x9f67b2c8,"no_bg"));
			p_restart_params->AddChecksum("centered",CRCD(0x2a434d05,"centered"));
			p_restart_params->AddChecksum("pad_choose_script",Script::GenerateCRC("skip_to_selected_restart"));
			Script::RunScript("theme_menu_add_item",p_restart_params);
			delete p_restart_params;
	}

	
	
	
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptAddWarpPointsToMenu( Script::CStruct* pParams, Script::CScript* pScript ) {
    Script::CStruct* p_restart_params;
    
    // grab array of warp points
    Script::CArray *p_Array = NULL;

    pParams->GetArray( "nodes", &p_Array );
    Dbg_MsgAssert(p_Array, ("AddWarpPointsToMenu requires an array of nodes"));
    // better only be names
    Dbg_MsgAssert(p_Array->GetType()==ESYMBOLTYPE_NAME,("\n%s\nnodes: Array must be of names",pScript->GetScriptInfo()));
    
    
    // add nodes to menu    
    for (uint32 i=0; iGetSize(); ++i) {
		// Obj_MoveToNode name =  Orient NoReset
        
        p_restart_params = new Script::CStruct;
        
        Script::CScriptStructure *pNode = SkateScript::GetNode( SkateScript::FindNamedNode( p_Array->GetChecksum( i ) ) );
        const char *pName;
        if (!pNode->GetText("RestartName",&pName))
        {
            pName = "Unnamed restart";
        }        
        
        p_restart_params->AddString( "text", pName );
        p_restart_params->AddChecksum("pad_choose_script",Script::GenerateCRC("WarpSkater"));

        // create the parameters that are passed to the script
        Script::CStruct *p_script_params = new Script::CStruct;
        p_script_params->AddChecksum( "nodename", p_Array->GetChecksum( i ) );
        p_restart_params->AddStructure( "pad_choose_params", p_script_params );

        Script::RunScript( "make_text_sub_menu_item", p_restart_params );
        delete p_restart_params;

    }
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptRunScriptOnObject | allows you to access Obj_ type functions, from a global script
bool ScriptRunScriptOnObject( Script::CStruct* pParams, Script::CScript* pScript )
{
	// searches for object by name

	uint32 obj_id;
	pParams->GetChecksum("id", &obj_id, true);
    
	uint32 scriptName;
	pParams->GetChecksum(NONAME, &scriptName, true);

	Script::CStruct* pSubParams = NULL;
	pParams->GetStructure(CRCD(0x7031f10c,"params"), &pSubParams);

	pScript->GetParams()->RemoveComponent(CRCD(0xa2b033fd,"UniqueID"));	
	
	 // this returns all objects, not just game objects and peds
	Obj::CObject* pObject = Obj::ResolveToObject( obj_id );

	if ( pObject )
	{
		/*
		Script::CScript *pNewScript=Script::SpawnScript(scriptName,pSubParams,0,NULL,scriptName);
		#ifdef __NOPT_ASSERT__
		pNewScript->SetCommentString("Spawned by script command RunScriptOnObject");
		pNewScript->SetOriginatingScriptInfo(pScript->GetCurrentLineNumber(),pScript->mScriptChecksum);
		#endif	
		
		pNewScript->mpObject = pObject;
		
		pScript->GetParams()->AddChecksum("UniqueID",pNewScript->GetUniqueId());
//		Script::RunScript( scriptName, pSubParams, obj_and_id.pObj );

	*/
	
		if (pObject->GetScript())
		{
			// (Mick) If this object has a script, but it's got a NULL mpObject
			// that means that script has been cleared in the current game loop
			// but not got around to being deleted.
			// it's quite safe to re-use it, we just need to set the object on it
			if (pObject->GetScript()->mpObject == NULL)
			{
				pObject->GetScript()->mpObject = pObject;
			}
		
			Dbg_MsgAssert(pObject->GetScript()->mpObject == pObject,("%s\nscript object %p not same as object %p",pScript->GetScriptInfo(),pObject->GetScript()->mpObject.Convert(),pObject));
		}
		// (Mick) Instead of spawning a script, call it on the main script
		// Which allows us to set exceptions and event handlers
		// It's also a lot quicker
		// (Might be problem with parameter conflicts?)
		pObject->CallScript(scriptName, pSubParams);
		
		
		return true;
	
		
	}
	else
	{
		#ifdef	__NOPT_ASSERT__
		printf( "Warning: Couldn't find object %s on which to run script %s\n", Script::FindChecksumName(obj_id), Script::FindChecksumName(scriptName) );
		#endif
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RunScriptOnComponentType | 
bool ScriptRunScriptOnComponentType( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 componentType;
	pParams->GetChecksum( CRCD(0xb6015ea8,"component"), &componentType, Script::ASSERT );

	uint32 target;
	pParams->GetChecksum( CRCD(0xb990d003,"target"), &target, Script::ASSERT );

	Script::CStruct* pSubParams = NULL;
	pParams->GetStructure( CRCD(0x7031f10c,"params"), &pSubParams, Script::NO_ASSERT );

	// new fast way, just go directly to the components, if any
	Obj::CBaseComponent *p_component = Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( componentType );
	while( p_component )
	{
		p_component->CallMemberFunction( target, pSubParams, pScript );
		
		p_component = p_component->GetNextSameType();		
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | Debounce | This will ignore the specified button
// until either the time has elapsed, or the button has been
// released and subsequently re-pressed. 
// For X, this prevents the press of X from carrying over from one
// screen to the next, and removes the need to have a delay,
// whilst still allowing the user to actively X past things
// @uparm X | button name
// @parmopt float | time | 1.0 | time to wait (in seconds)
// @parmopt int | clear | 0 | if 1, then clear the current pressed and triggered states
	
bool ScriptDebounce( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 ButtonChecksum=0;
	pParams->GetChecksum(NONAME,&ButtonChecksum);
	if (!ButtonChecksum)
	{
		return true;
	}	
	
	float time=1.0f;
	pParams->GetFloat(CRCD(0x906b67ba, "time"),&time);

	int	clear = 0;
	pParams->GetInteger(CRCD(0x1a4e0ef9, "clear"),&clear);
	
	Obj::CInputComponent* p_input_component
		= static_cast< Obj::CInputComponent* >(Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType(CRC_INPUT));
	while (p_input_component)
	{
		p_input_component->Debounce(ButtonChecksum, time, clear);
		p_input_component = static_cast< Obj::CInputComponent* >(p_input_component->GetNextSameType());
	}
		
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetStatOverride | Set a global stat override for debugging
// set it to 0 to use normal stats
bool ScriptSetStatOverride( Script::CStruct* pParams, Script::CScript* pScript )
{
	float	value = 0.0f;
	pParams->GetFloat(NONAME,&value);
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	skate_mod->SetStatOverride(value);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleRails | Toggle the debug display of rails on and off
bool ScriptToggleRails( Script::CStruct* pParams, Script::CScript* pScript )
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	skate_mod->SetDrawRails(!skate_mod->GetDrawRails());
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleRigidBodyDebug | Toggle the debug display of rigidbody characteristics
bool ScriptToggleRigidBodyDebug( Script::CStruct* pParams, Script::CScript* pScript )
{
	Obj::CRigidBodyComponent::sToggleDrawRigidBodyDebugLines(); 
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CheckForHoles | check the scene file for holes in geometry, and visually display them
bool ScriptCheckForHoles( Script::CStruct* pParams, Script::CScript* pScript )
{
	Nx::CEngine::sDebugCheckForHoles();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | OnXbox |
bool ScriptOnXbox( Script::CStruct* pParams, Script::CScript* pScript )
{
#ifdef __PLAT_XBOX__
	return true;
#else
	return false;
#endif	// __PLAT_XBOX__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GotoXboxDashboard |
bool ScriptGotoXboxDashboard( Script::CStruct* pParams, Script::CScript* pScript )
{
#	ifdef __PLAT_XBOX__
	LD_LAUNCH_DASHBOARD ld;
    ZeroMemory( &ld, sizeof(ld) );

	// In the case where we are rebooting to the memory management section of the dashboard,
	// this value should contain the total number of blocks needed.
	uint32 total_blocks_needed = 0;

	if( pParams->ContainsFlag( 0x1592cbca /*"memory"*/ ))
	{
		pParams->GetInteger(CRCD(0xb204db86,"total_blocks_needed"),(int*)&total_blocks_needed);
		
		ld.dwReason		= XLD_LAUNCH_DASHBOARD_MEMORY;
		ld.dwParameter1	= 'U';
		ld.dwParameter2	= total_blocks_needed;
	}
	else
	{
		ld.dwReason = XLD_LAUNCH_DASHBOARD_MAIN_MENU;
	}

    XLaunchNewImage( NULL, PLAUNCH_DATA( &ld ) );
#	endif	// __PLAT_XBOX__
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSystemLinkEnabled( Script::CStruct* pParams, Script::CScript* pScript )
{
#ifdef __PLAT_XBOX__
	DWORD result;
	
	result = XNetGetEthernetLinkStatus();
	if(( result & XNET_ETHERNET_LINK_ACTIVE ) == 0 )
	{
		return false;
	}
#endif	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Particle system functions.
// @script | CreateParticleSystem | Creates a particle system.
// @parm name | name | the name of the particle system. (Should be Particle_UnNamed if created from a node)
// @parmopt int | max | 256 | maximum number of particles this system can display.
// @parmopt name | texture | | the name of the texture to apply. No name means use flat shaded.
// @parmopt name | emitscript | | the name of the script to call for emission.
// @parmopt name | updatescript | | the name of the script to call to update position etc.
// @parmopt name | blendmode | diffuse | The blendmode: blend/add/sub/modulate/brighten & 
// fixblend/fixadd/fixsub/fixmodulate/fixbrighten & diffuse (no blend at all). Defaults to diffuse.
// @parmopt int | fix | 0 | Fixed alpha value. 128 is normal. Range is 0-255.
// @parmopt name | params | | Sets a parameter block for use with the created particle scripts.
// @parmopt name | type | flat | Sets the type of particle (flat/shaded/smooth/glow). Defaults to flat.
// @parmopt int | segments | 8 | The number of segments in a segmented particle, such as 'Glow'.
// @parmopt float | split | 0.5 | Sets the split point in a segmented particle. 0 = split at center. 1 means split at outer edge.
// @parmopt int | history | 1 | Number of history lists to keep. Minimum is 1 when using trail particle types.
// @parmopt int | perm | 0 | set to 1 if particle system stays between levels (like the skaters)
// update script to grab bone/node positions when attaching.
//
// 
bool ScriptCreateParticleSystem( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 name = 0;
	pParams->GetChecksum("Name",&name);

	int max = 256;
	pParams->GetInteger("Max",&max);

	int max_streams = 2;
	pParams->GetInteger("MaxStreams",&max_streams);

	uint32 texture = 0;
	pParams->GetChecksum("Texture",&texture);

	uint32 blendmode = 0x515e298e;		// Defaults to diffuse.
	pParams->GetChecksum("blendmode",&blendmode);
	
	uint32 type = 0xaab555bb;		// Defaults to flat.
	pParams->GetChecksum("type",&type);
	
	int fix = 0;
	pParams->GetInteger("fix",&fix);

	int segments = 8;
	pParams->GetInteger("segments",&segments);

	float split = 0.5f;
	pParams->GetFloat("split",&split);

	int history = 1;
	pParams->GetInteger("history",&history);

	int perm = 0;
	pParams->GetInteger("perm",&perm);

	// Create a particle. Will be internally added to the process list, so we don't need to do
	// anything after it has been created.
	Nx::CParticle * p_particle = Nx::create_particle( name, type, max, max_streams, texture, blendmode, fix, segments, split, history, perm );
	
	Script::CStruct* pSubParams = NULL;
	pParams->GetStructure( "params", &pSubParams, Script::NO_ASSERT );

	uint32 emitscript = 0;
	if ( pParams->GetChecksum("emitscript",&emitscript) )
	{
		p_particle->set_emit_script( emitscript, pSubParams );
	}

								
//	p_particle->SetActive( false );
//								
//	uint32 active = 0;
//	if ( pParams->GetChecksum("active",&active) )
//	{
//		p_particle->SetActive( active );
//	}
	
	uint32 updatescript = 0;
	if ( pParams->GetChecksum("updatescript",&updatescript) )
	{
		p_particle->set_update_script( updatescript, pSubParams );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetScript | Sets the emission script.
// @parm name | name | the name of the particle system.
// @parmopt name | emitscript | | the name of the script to call for emission.
// @parmopt name | updatescript | | the name of the script to call to update position etc.
// @parmopt name | params | | Sets a parameter block for use with the created particle scripts.
bool ScriptSetScript( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 name = 0;
	pParams->GetChecksum("Name",&name);

	Script::CStruct* pSubParams = NULL;
	pParams->GetStructure( "params", &pSubParams, Script::NO_ASSERT );

	Nx::CParticle * p_particle = Nx::get_particle( name );
	Dbg_MsgAssert( p_particle, ( "Couldn't find the requested particle system." ) );

	uint32 emitscript = 0;
	if ( pParams->GetChecksum("emitscript",&emitscript) )
	{
		p_particle->set_emit_script( emitscript, pSubParams );
	}
	
	uint32 updatescript = 0;
	if ( pParams->GetChecksum("updatescript",&updatescript) )
	{
		p_particle->set_update_script( updatescript, pSubParams );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DestroyParticleSystem | Destroys a particle system.
// @parm name | name | the name of the particle system.
// @parmopt int | ifempty | 0 | Set this to 1 if you want the particle system to be destroyed only when empty.
bool ScriptDestroyParticleSystem( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 name = 0;
	pParams->GetChecksum("Name",&name);

	int ifempty = 0;
	pParams->GetInteger("IfEmpty",&ifempty);

	if ( ifempty )
	{
		Nx::destroy_particle_when_empty( name );
	}
	else
	{
		Nx::destroy_particle( name );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EmptyParticleSystem | Empties the particle system immediately (sets number of particles to 0. 
// @parm name | name | the name of the particle system.
bool ScriptEmptyParticleSystem( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 name = 0;
	pParams->GetChecksum("Name",&name);

	Nx::CParticle * p_particle = Nx::get_particle( name );
	Dbg_MsgAssert( p_particle, ( "Couldn't find the requested particle system." ) );

	p_particle->SetNumParticles( 0 );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ParticleExists | Returns whether the specified particle system exists
// @parm name | name | the name of the particle system.
bool ScriptParticleExists( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 name = 0;
	if ( !pParams->GetChecksum("Name",&name ) )
	{
		pParams->GetChecksum(NONAME,&name,Script::ASSERT);
	}

	Nx::CParticle * p_particle = Nx::get_particle( name );

	if ( p_particle )
	{
		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StructureContains | true if the structure contains an element
// or flag with the specified name
// @parm structure | structure | the structure to check
// @uparm name | the named param to look for
bool ScriptStructureContains( Script::CStruct* pParams, Script::CScript* pScript )
{
	Script::CStruct* p_struct;
	if ( pParams->GetStructure( "structure", &p_struct, Script::NO_ASSERT ) )
	{	
		// printf("got a structure\n");
		uint32 name;
		pParams->GetChecksum( NONAME, &name, Script::NO_ASSERT );
		Dbg_MsgAssert( name, ( "StructureContains called without a param name" ) );
		// printf("checking for a name of %x\n", name );
		if ( p_struct->ContainsComponentNamed( name ) )
		{
			return true;
		}
		else
			return p_struct->ContainsFlag( name );
	}
	// printf("didn't get a structure\n");
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetBonePosition | allows you to access Obj_ type functions, from a global script
bool ScriptGetBonePosition( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mth::Vector bonePos;
	bool success = false;
	
	// searches for object by m_id
	uint32 obj_id;
	pParams->GetChecksum( CRCD(0x40c698af,"id"), &obj_id, Script::ASSERT );
	
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();	
	Obj::CGeneralManager* pObjectManager = skate_mod->GetObjectManager();
	Dbg_Assert( pObjectManager );

	Obj::CObject* pObject = pObjectManager->GetObjectByID( obj_id );
	if ( pObject )
	{
		Dbg_MsgAssert( static_cast( pObject ), ( "This function only works on moving objects." ) ); 
		Obj::CMovingObject* pMovingObject = (Obj::CMovingObject*)pObject;

		uint32 boneName;
		pParams->GetChecksum( CRCD(0xcab94088,"bone"), &boneName, Script::ASSERT );
		
		Obj::CSkeletonComponent* p_skeleton_component = GetSkeletonComponentFromObject(pMovingObject);
		Dbg_Assert(p_skeleton_component);
		success = p_skeleton_component->GetBoneWorldPosition( boneName, &bonePos );
	}
	
	// make sure we have somewhere to return the data
	Dbg_Assert( pScript && pScript->GetParams() );
	pScript->GetParams()->AddFloat( CRCD(0x7323e97c,"x"), bonePos[X] );
	pScript->GetParams()->AddFloat( CRCD(0x424d9ea,"y"), bonePos[Y] );
	pScript->GetParams()->AddFloat( CRCD(0x9d2d8850,"z"), bonePos[Z] );

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ShouldEmitParticles | tests whether the specified particle system is active
bool ScriptShouldEmitParticles( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 name;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &name, Script::ASSERT );

	Nx::CParticle * p_particle = Nx::get_particle( name );

	if ( p_particle )
	{
		return p_particle->IsEmitting();
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ParticlesOn | turns on the specified particle system
bool ScriptParticlesOn( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 name;
	pParams->GetChecksum( "name", &name, Script::ASSERT );

	Nx::CParticle * p_particle = Nx::get_particle( name );

	if ( p_particle )
	{
		p_particle->SetEmitting( true );
	}
	else
	{
//		Dbg_MsgAssert( 0, ( "Couldn't find particle system to turn on" ) );
	}

	return ( p_particle != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ParticlesOff | turns off the specified particle system
bool ScriptParticlesOff( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 name;
	pParams->GetChecksum( "name", &name, Script::ASSERT );

	Nx::CParticle * p_particle = Nx::get_particle( name );

	if ( p_particle )
	{
		p_particle->SetEmitting( false );
	}
	else
	{
//		Dbg_MsgAssert( 0, ( "Couldn't find particle system to turn off" ) );
	}

	return ( p_particle != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MangleChecksums | adds 2 checksums together and returns the result in "mangled_id"
// @parm name | a | First checksum
// @parm name | b | Second checksum
// @parm name | mangled_id | The returned value for the mangled checksum
bool ScriptMangleChecksums( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 a;
	if ( !pParams->GetChecksum( CRCD(0x174841bc,"a"), &a, Script::NO_ASSERT ) )
	{
		pParams->GetInteger( CRCD(0x174841bc,"a"), (int*)&a, Script::ASSERT );
	}
	
	uint32 b;
	if ( !pParams->GetChecksum( CRCD(0x8e411006,"b"), &b, Script::NO_ASSERT ) )
	{
		pParams->GetInteger( CRCD(0x8e411006,"b"), (int*)&b, Script::ASSERT );
	}
	
	uint32 mangled_id = a + b;
	Dbg_Assert( pScript && pScript->GetParams() );
	pScript->GetParams()->AddChecksum( CRCD(0xfba40626,"mangled_id"), mangled_id );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AppendSuffixToChecksum | Appends a string suffix to a checksum and returns the result in "appended_id"
// @parm name | Base | The base checksum
// @parm string | SuffixString | The suffix string
bool ScriptAppendSuffixToChecksum( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 base_checksum;
	pParams->GetChecksum(CRCD(0x3f4b019e, "Base"), &base_checksum, Script::ASSERT);
	
	const char* suffix_string;
	pParams->GetString(CRCD(0x94542eea, "SuffixString"), &suffix_string, Script::ASSERT);
	
	pScript->GetParams()->AddChecksum(CRCD(0xafb911c6, "appended_id"), Crc::ExtendCRCWithString(base_checksum, suffix_string));
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RotateVector | rotates a vector by the specified amount
// @parm float | x | X value of the vector to be rotated (also, the return value)
// @parm float | y | Y value of the vector to be rotated (also, the return value)
// @parm float | z | Z value of the vector to be rotated (also, the return value)
// @parm float | rx | Amount to rotate on X, in degrees
// @parm float | ry | Amount to rotate on Y, in degrees
// @parm float | rz | Amount to rotate on Z, in degrees
bool ScriptRotateVector( Script::CStruct* pParams, Script::CScript* pScript )
{
	// get original vector:
	Mth::Vector vec;
	pParams->GetFloat( "x", &vec[X], Script::ASSERT );
	pParams->GetFloat( "y", &vec[Y], Script::ASSERT );
	pParams->GetFloat( "z", &vec[Z], Script::ASSERT );
	vec[W] = 1.0f;

	// get amount to rotate (in degrees)
	float rx = 0.0f;
	float ry = 0.0f;
	float rz = 0.0f;
	pParams->GetFloat( "rx", &rx, Script::NO_ASSERT );
	pParams->GetFloat( "ry", &ry, Script::NO_ASSERT );
	pParams->GetFloat( "rz", &rz, Script::NO_ASSERT );

	// rotate the vector
	Mth::Matrix mat;
	mat.Ident();
	mat.RotateXLocal( Mth::DegToRad(rx) );
	mat.RotateYLocal( Mth::DegToRad(ry) );
	mat.RotateZLocal( Mth::DegToRad(rz) );
	vec = mat.Transform( vec );

	// return the vector
	pScript->GetParams()->AddFloat( "x", vec[X] );
	pScript->GetParams()->AddFloat( "y", vec[Y] );
	pScript->GetParams()->AddFloat( "z", vec[Z] );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsPS2 | Returns true if the current hardware is PS2 (proview/devkit/regular).
bool ScriptIsPS2( Script::CStruct* pParams, Script::CScript* pScript )
{
	if (( Config::GetHardware() == Config::HARDWARE_PS2 ) ||
		( Config::GetHardware() == Config::HARDWARE_PS2_PROVIEW ) ||
		( Config::GetHardware() == Config::HARDWARE_PS2_DEVSYSTEM ) )
	{
		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsNGC | Returns true if the current hardware is GameCube.
bool ScriptIsNGC( Script::CStruct* pParams, Script::CScript* pScript )
{
	if ( ( Config::GetHardware() == Config::HARDWARE_NGC ) )
	{
		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsXBOX | Returns true if the current hardware is Xbox.
bool ScriptIsXBOX( Script::CStruct* pParams, Script::CScript* pScript )
{
	if ( ( Config::GetHardware() == Config::HARDWARE_XBOX ) )
	{
		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsWin32 | Returns true if the current hardware is Win32.
bool ScriptIsWIN32( Script::CStruct* pParams, Script::CScript* pScript )
{
	if ( ( Config::GetHardware() == Config::HARDWARE_WIN32 ) )
	{
		return true;
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetPlatform | Puts the name of the current platform as a checksum into a parameter
// called Platform. The value will be either ps2, xbox, ngc or win32
// Handy for use in script switch statements, rather than having to have nested if's
bool ScriptGetPlatform( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 platform=0;
	
	switch (Config::GetHardware())
	{
	case Config::HARDWARE_PS2:
	case Config::HARDWARE_PS2_PROVIEW:
	case Config::HARDWARE_PS2_DEVSYSTEM:
		platform=0x988a3508; // ps2
		break;
	case Config::HARDWARE_NGC:
		platform=0xbcf00d45; // ngc
		break;
	case Config::HARDWARE_XBOX:
		platform=0x87d839b8; // xbox
		break;
	case Config::HARDWARE_WIN32:
		platform=0x4af45e2e; // win32
		break;
	default:
		break;
	}
	
	pScript->GetParams()->AddChecksum("Platform",platform);		
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptIsPal( Script::CStruct* pParams, Script::CScript* pScript )
{
	return Config::PAL();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PushMemProfile | push an arbitarily named memory profile (Mick's use only)
bool ScriptPushMemProfile( Script::CStruct* pParams, Script::CScript* pScript )
{
	
	const char* pContext;
	pParams->GetText( NONAME, &pContext, true );
	Mem::PushMemProfile((char*)pContext);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PopMemProfile | pop a mem profile that was pushed (Mick's use only)
bool ScriptPopMemProfile( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mem::PopMemProfile();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | TogglePass | Toggle which passes are displayed
bool ScriptTogglePass( Script::CStruct* pParams, Script::CScript* pScript )
{
	#ifdef	__PLAT_NGPS__
	#ifdef	__NOPT_ASSERT__
	static 	int pass = 0;
	pass++;

	// Let us put a pass value in that will override the toggling value  
	pParams->GetInteger(CRCD(0x318f2bdb,"Pass"),&pass);
	
	if (pass == 5)
	{
		pass = 0;
	}
	if (pass)
	{
		NxPs2::gPassMask1 = 0xc0; 			// 1<<6 | 1<<7  (0x40, 0x80)
		NxPs2::gPassMask0 = (pass-1)<<6;		
	}
	else
	{
		NxPs2::gPassMask1 = 0;
		NxPs2::gPassMask0 = 0;
	}
	#endif
	#endif
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetScreen | Sets default screen aspect ratio and camera
// @parm float | Aspect | aspect ratio, usually 16/9 (1.777777777) or 4/3 (1.3333333333)
// @parm float | Angle | screen angle in degrees usally 72, or 80 for 16:9 
// @parm int | Letterbox | 0 for non-letterbox, 1 for letterboxed.  Normally used with 4/3 aspect 
bool	ScriptSetScreen( Script::CStruct* pParams, Script::CScript* pScript )
{
	float	Aspect;
	float	Angle;
	
	if (pParams->GetFloat("Aspect",&Aspect))
	{
		Nx::CViewportManager::sSetScreenAspect(Aspect);
	}
	
	if (pParams->GetFloat("Angle",&Angle))
	{
		Nx::CViewportManager::sSetScreenAngle(Angle);
	}
	
	int letterbox;
	if (pParams->GetInteger("Letterbox",&letterbox))
	{
		Nx::CEngine::sSetLetterbox(letterbox);
	}
	
	return true;	   
	   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetUpperCaseString( Script::CStruct* pParams, Script::CScript* pScript )
{
	const char* initial_string;
	pParams->GetString( NONAME, &initial_string, Script::ASSERT );

	char new_string[128];
	strcpy( new_string, initial_string );

	Str::UpperCase( new_string );
	pScript->GetParams()->AddString( "UpperCaseString", new_string );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetGameMode( Script::CStruct* pParams, Script::CScript* pScript )
{
	 Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
	uint32 gameMode = skate_mod->GetGameMode()->GetNameChecksum();

	pScript->GetParams()->AddChecksum( "GameMode", gameMode );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptStartKeyboardHandler( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::FrontEnd* front = Mdl::FrontEnd::Instance();
	int max_length;

	pParams->GetInteger( "max_length", &max_length, true );
	front->AddKeyboardHandler( max_length );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptEnableKeyboard( Script::CStruct* pParams, Script::CScript* pScript )
{
#ifdef __PLAT_NGPS__
	SIO::EnableKeyboard( true );
#endif
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptDisableKeyboard( Script::CStruct* pParams, Script::CScript* pScript )
{
#ifdef __PLAT_NGPS__
	SIO::EnableKeyboard( false );
#endif
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptStopKeyboardHandler( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::FrontEnd* front = Mdl::FrontEnd::Instance();

	front->RemoveKeyboardHandler();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CreateIndexArray | creates an array of ints, where each element
// is equal to its own index
// @uparm 1 | size of array
bool ScriptCreateIndexArray( Script::CStruct* pParams, Script::CScript* pScript )
{
	int size;
	pParams->GetInteger( NONAME, &size, Script::ASSERT );

	if ( size < 1 )
		Dbg_MsgAssert( 0, ( "CreateIndexArray called with size less than 1" ) );

	Script::CArray *p_index_array = new Script::CArray();
	p_index_array->SetSizeAndType( size, ESYMBOLTYPE_INTEGER );

	for ( int i = 0; i < size; i++ )
		p_index_array->SetInteger( i, i );

	pScript->GetParams()->AddArray( "index_array", p_index_array );
	Script::CleanUpArray( p_index_array );
	delete p_index_array;
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetRescaledTargetValue | 
// @parm float | min |  
// @parm float | max | 
// @parm float | target | 
// @parm float | result | 
bool ScriptGetRescaledTargetValue( Script::CStruct* pParams, Script::CScript* pScript )
{
	float min;
	float max;
	float target;
	float result;

	pParams->GetFloat( "min", &min, true );
	pParams->GetFloat( "max", &max, true );
	pParams->GetFloat( "target", &target, true );

	result = ( target - min ) / ( max - min );

	if ( pParams->ContainsFlag( "sin_curve" ) )
	{
		result = asinf( result ) * 4096.0f / ( 2.0f * Mth::PI );
	
		if ( pParams->ContainsFlag( "backwards" ) )
		{
			result = 2048.0f - result;
		}
	}

//	Dbg_Message( "min = %f max = %f target = %f result = %f", min, max, target, result );

	// this finds the scaled value between 0.0 and 1.0
	pScript->GetParams()->AddFloat( "result", result );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MenuIsSelected | Checks names in pArray to find selected
bool ScriptMenuIsSelected(Script::CStruct *pParams, Script::CScript *pScript)
{
	// If there is an array of names, check them.		
	Script::CArray *pArray=NULL;
	if (pParams->GetArray(NONAME,&pArray))
	{
		Dbg_MsgAssert(pArray,("Eh? NULL pArray?"));
		for (uint32 i=0; iGetSize(); ++i)
		{
			if (MenuIsSelected(pArray->GetNameChecksum(i)))
			{
				return true;
			}
		}
	}
	
	uint32 id=0;
	if (pParams->GetChecksum(NONAME,&id))
	{
		if (MenuIsSelected(id))
		{
			return true;
		}	
	}
			
	return false;			
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetNodeName | Gets the node name of the object running this script, and puts
// it into a parameter called NodeName.
// If there is no such object, it will not add NodeName and will return false.
bool ScriptGetNodeName(Script::CStruct *pParams, Script::CScript *pScript)
{
	if (pScript->mpObject)
	{
		Obj::CMovingObject *p_pos_obj = static_cast(pScript->mpObject.Convert());
		if (p_pos_obj)
		{
			if (p_pos_obj->GetID())
			{
				pScript->GetParams()->AddChecksum("NodeName",p_pos_obj->GetID());
				return true;
			}
		}
	}			

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetTesterScript | Sets and starts running the tester script. This is a script which
// will never die and will always be having its update function called every frame.
// Used for auto testing levels.
// SetTesterScript may be called more than once. Any new call will stop the current tester script
// and start the new one.
// If the tester script hits an endscript it still won't die, it will just stick on the endscript
// and not do anything. (This uses negligible processing time)
// @uparm name | The name of the script to run.
bool ScriptSetTesterScript(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 tester_script=0;
	CStruct *p_params=NULL;
	
	pParams->GetChecksum(NONAME,&tester_script);
	pParams->GetStructure(CRCD(0x7031f10c,"params"),&p_params);
	
	 Mdl::Skate * pSkate =  Mdl::Skate::Instance();

	Dbg_MsgAssert(pSkate->mp_gameFlow,("NULL pSkate->mp_gameFlow ??"));
	pSkate->mp_gameFlow->SetTesterScript(tester_script, p_params);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | KillTesterScript | Kills any tester script that is running. 
// Returns true if a tester script was running before, false otherwise.
bool ScriptKillTesterScript(Script::CStruct *pParams, Script::CScript *pScript)
{
	 Mdl::Skate * pSkate =  Mdl::Skate::Instance();

	Dbg_MsgAssert(pSkate->mp_gameFlow,("NULL pSkate->mp_gameFlow ??"));
	return pSkate->mp_gameFlow->KillTesterScript();
}									 

// Memory manager related functions

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MemPushContext | 
// @uparm 1 | value
bool ScriptMemPushContext( Script::CStruct *pParams, Script::CScript *pScript )
{
//	MemViewToggle();
	
	// 0 is the special case name
	int Val=0;			   			// default to 0
	if ( pParams->GetInteger(NONAME,&Val,false) )
	{
		if ( Val == 0 )
		{
			// is the bottom up heap
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
		}
		else
		{
			Dbg_MsgAssert( 0, ( "Must specify heap name (instead of number) for MemPushContext (ex: BottomUpHeap)" ) );
		}
	}
	else
	{
		// otherwise, look it up by name
		uint32 whichHeap;
		pParams->GetChecksum( NONAME, &whichHeap, true );
		Mem::Manager& mem_man = Mem::Manager::sHandle();
		Mem::Heap* pHeap = mem_man.GetHeap( whichHeap );
		Dbg_Assert( pHeap );

		Mem::Manager::sHandle().PushContext( pHeap );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MemPopContext |
bool ScriptMemPopContext( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mem::Manager::sHandle().PopContext();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MemInitHeap |
// @parm name | name | the name of heap to create
// @parm int | size | the size of heap to create
bool ScriptMemInitHeap(Script::CStruct *pParams, Script::CScript *pScript)
{	
	const char* pHeapName;
	pParams->GetText( "name", &pHeapName, true );

	int heapSize;
	pParams->GetInteger( "size", &heapSize, true );

	uint32 heapName;
	heapName = Script::GenerateCRC( pHeapName );

	Mem::Manager& mem_man = Mem::Manager::sHandle();
	Dbg_MsgAssert( !mem_man.NamedHeap(heapName, false), ( "named heap %s already exists", pHeapName ) )
	mem_man.InitNamedHeap( heapName, heapSize, pHeapName );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MemDeleteHeap |
// @parm name | name | the name of heap to delete
bool ScriptMemDeleteHeap(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char* pHeapName;
	pParams->GetText( "name", &pHeapName, true );

	Mem::Manager& mem_man = Mem::Manager::sHandle();	
	return mem_man.DeleteNamedHeap( Script::GenerateCRC( pHeapName ), false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptMemThreadSafe( Script::CStruct *pParams, Script::CScript *pScript )
{
	if (pParams->ContainsFlag( Script::GenerateCRC( "off" )))
	{
		Mem::SetThreadSafe( false );
	}
	else
	{
		Mem::SetThreadSafe( true );
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AnalyzeHeap | analyzes specified heap
// @uparmopt BottomUpHeap | heap to analyze
bool ScriptAnalyzeHeap( Script::CStruct *pParams, Script::CScript *pScript )
{
	// GJ:  I am planning to refactor this!
	Mem::Manager& mem_man = Mem::Manager::sHandle();
	Mem::Heap* pHeap = NULL;
	uint32 whichHeap = Script::GenerateCRC("BottomUpHeap");
	if ( pParams )
	{
		pParams->GetChecksum( NONAME, &whichHeap );
	}

	pHeap = mem_man.GetHeap( whichHeap );

#ifndef __PLAT_NGC__
	if ( pHeap )
	{
		MemView_AnalyzeHeap( pHeap );
	}
#endif		// __PLAT_NGC__

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PrintMemInfo | displays mem info about specified heap
// @uparmopt BottomUpHeap | which heap to print
bool ScriptPrintMemInfo( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mem::Manager& mem_man = Mem::Manager::sHandle();
	Mem::Heap* pHeap = NULL;
	char buf[64];
	uint32 whichHeap = Script::GenerateCRC("BottomUpHeap");
	if ( pParams )
	{
		pParams->GetChecksum( NONAME, &whichHeap );
	}
	
	pHeap = mem_man.GetHeap( whichHeap );
	strcpy( buf, mem_man.GetHeapName( whichHeap ) );

	if ( pHeap )
	{
		printf(  "\n" );

		printf( "%s:\n", buf );
		printf( "Used %dK (%d) Peak %dK (%d)\n",
				pHeap->mUsedMem.m_count / 1024,
				pHeap->mUsedBlocks.m_count,
				pHeap->mUsedMem.m_peak / 1024,
				pHeap->mUsedBlocks.m_peak );
		printf( "Frag %dK (%d) Peak %dK (%d)\n",
				pHeap->mFreeMem.m_count / 1024,
				pHeap->mFreeBlocks.m_count,
				pHeap->mFreeMem.m_peak / 1024,
				pHeap->mFreeBlocks.m_peak );

		printf(  "\n" );
	}
	
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DisplayFreeMem | displays free mem info of specified heap
bool ScriptDisplayFreeMem( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mem::Manager& mem_man = Mem::Manager::sHandle();

	Mem::Heap* heap;
   
	Script::CStruct* params;

	params = new Script::CStruct;
	for (heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
	{		
			Mem::Region* region = heap->ParentRegion();			
			
			params->Clear();
			
			params->AddChecksum( "id", Script::GenerateCRC( heap->GetName() ) );
			params->AddInteger( "free_mem", region->MemAvailable() );
			params->AddInteger( "min_free_mem", region->MinMemAvailable() );

			Script::RunScript( "UpdateDisplayFreeMemory", params );
			
	}

	delete params;
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DumpHeaps | prints out heap contents
// @flag ExactValues | Prints the exact heap sizes in bytes.
bool ScriptDumpHeaps( Script::CStruct *pParams, Script::CScript *pScript )
{
#	if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )

	Script::DumpLastStructs();

	Mem::Manager& mem_man = Mem::Manager::sHandle();

#	ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
	char buf[512];
#endif		// __PLAT_NGC__
#	endif

	Mem::Heap* heap;

#	ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
	dump_open("blah.txt");
#endif		// __PLAT_NGC__
#	endif

	if (pParams->ContainsFlag(CRCD(0xd65b9ac7,"ExactValues")))
	{
		printf("Name              Used    Frag    Free    Min Blocks\n");
		printf("--------------- ------ ------- ------- ------ ------\n");
		for (heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
		{		
				Mem::Region* region = heap->ParentRegion();			
				printf( "%12s: %8d %7d %7d %7d  %5d \n",
						heap->GetName(),
						heap->mUsedMem.m_count ,
						heap->mFreeMem.m_count ,
						region->MemAvailable() ,
						region->MinMemAvailable() ,
						heap->mUsedBlocks.m_count
						);										
		}
	}	
	else
	{
		printf("Name            Used  Frag  Free   Min  Blocks\n");
		printf("--------------- ----- ----- ---- ------ ------\n");
		for (heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
		{		
				Mem::Region* region = heap->ParentRegion();			
				printf( "%12s: %5dK %4dK %4dK %4dK  %5d \n",
						heap->GetName(),
						heap->mUsedMem.m_count / 1024,
						heap->mFreeMem.m_count / 1024,
						region->MemAvailable() / 1024,
						region->MinMemAvailable() / 1024,
						heap->mUsedBlocks.m_count
						);										
		}
	}

#	ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
	dump_close();
#endif		// __PLAT_NGC__
#	endif
	
	Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
	printf( "\nSound mem free %d K  (%d bytes)\n", 
		 sfx_manager->MemAvailable() / 1024, sfx_manager->MemAvailable() );

#	ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
	char fileName[256];
	int i = 0;
	while ( TRUE )
	{
		// The filename is formed from the level name a number, and the free memory
		// you should sort them by DATE, and not by filename
		// 
		sprintf( fileName, "dumps\\%s_dump_%02d_%s.txt",
						   s_level_name,
//						   __nDATE__,
					//	   __nTIME__,
						   i,
						   Str::PrintThousands((mem_man.BottomUpHeap()->ParentRegion()->MemAvailable())) );

		printf ("Filename = <%s>, %d chars\n",fileName,strlen(fileName));
						   
		if ( FALSE == File::Exist( fileName ))
			break;

		i++;
	}
#endif		// __PLAT_NGC__
#	endif // __PLAT_XBOX__

#	ifndef __PLAT_XBOX__
#ifndef __PLAT_NGC__
	if( dump_open(fileName))
	{
		sprintf(buf,"Name            Used  Frag  Free   Blocks\n");
		dump_printf(buf);
		sprintf(buf,"--------------- ----- ----- ------ ------\n");
		dump_printf(buf);
		for (heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
		{		
				Mem::Region* region = heap->ParentRegion();			
				sprintf(buf, "%12s: %5dK %4dK %4dK   %5d \n",
						heap->GetName(),
						heap->mUsedMem.m_count / 1024,
						heap->mFreeMem.m_count / 1024,
						region->MemAvailable() / 1024,
						heap->mUsedBlocks.m_count
						);										
				dump_printf(buf);
		}
		sprintf( buf, "\nSound mem free %d K  (%d bytes)\n\n\n", 
			 sfx_manager->MemAvailable() / 1024, sfx_manager->MemAvailable() );
		dump_printf(buf);
	
		sprintf(buf,"Summary\n----------\n\n");
		dump_printf(buf);
		Mem::DumpMemProfile(3);
		sprintf(buf,"\n\nDetails\n----------\n\n");
		dump_printf(buf);
		Mem::DumpMemProfile(100);

		dump_close();
	}
#endif		// __PLAT_NGC__
#	endif //__PLAT_XBOX__

	printf("\nScript pool usage:\n");
	printf("CStruct:           %5d of %5d, max %5d\n",CStruct::SGetNumUsedItems(),CStruct::SGetTotalItems(),CStruct::SGetMaxUsedItems());
	printf("CComponent:        %5d of %5d, max %5d\n",CComponent::SGetNumUsedItems(),CComponent::SGetTotalItems(),CComponent::SGetMaxUsedItems());
	printf("CSymbolTableEntry: %5d of %5d, max %5d\n",CSymbolTableEntry::SGetNumUsedItems(),CSymbolTableEntry::SGetTotalItems(),CSymbolTableEntry::SGetMaxUsedItems());
	printf("CVector:           %5d of %5d, max %5d\n",CVector::SGetNumUsedItems(),CVector::SGetTotalItems(),CVector::SGetMaxUsedItems());
	printf("CPair:             %5d of %5d, max %5d\n",CPair::SGetNumUsedItems(),CPair::SGetTotalItems(),CPair::SGetMaxUsedItems());
	printf("CArray:            %5d of %5d, max %5d\n",CArray::SGetNumUsedItems(),CArray::SGetTotalItems(),CArray::SGetMaxUsedItems());
	printf("CScript:           %5d of %5d, max %5d\n",CScript::SGetNumUsedItems(),CScript::SGetTotalItems(),CScript::SGetMaxUsedItems());
	Mem::CCompactPool *p_hash = Mem::PoolManager::SGetPool(Mem::PoolManager::vHASH_ITEM_POOL);
	printf("CHashItem:         %5d of %5d, max %5d\n",p_hash->GetNumUsedItems(),p_hash->GetTotalItems(),p_hash->GetMaxUsedItems());
	printf("\n(HashItems are init in main.cpp, others are in init.cpp)\n");
#endif		// __NOPT_FINAL__

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DumpFragments | prints out heap fragments
bool ScriptDumpFragments( Script::CStruct *pParams, Script::CScript *pScript )
{
	int maxFragmentation = 10000;
	pParams->GetInteger( NONAME, &maxFragmentation, Script::NO_ASSERT );

	if ( pParams->ContainsFlag( "K" ) )
	{
		maxFragmentation *= 1024;
	}

#ifdef __NOPT_ASSERT__
	Mem::Manager& mem_man = Mem::Manager::sHandle();
	
	// sanity checks for fragmentation
	Mem::Heap* heap = mem_man.BottomUpHeap();
	int fragmentation = heap->mFreeMem.m_count;
	
	if (fragmentation > maxFragmentation)
	{
		// not guaranteed to do anything useful.... might crash the debugger (Mick)
		printf ("Dumping Fragments.....\n");
		MemView_DumpFragments(heap);
		printf ("Done Dumping Fragments.....\n");
	}
	
	Dbg_MsgAssert(fragmentation < maxFragmentation, ("Excessive bottom up fragmentation (%d) after cleanup (max=%d)",fragmentation,maxFragmentation)); 
	
	heap = mem_man.TopDownHeap();
	fragmentation = heap->mFreeMem.m_count;
	if (fragmentation > maxFragmentation)
	{
		// not guaranteed to do anything useful.... might crash the debugger (Mick)
		printf ("Dumping Fragments.....\n");
		MemView_DumpFragments(heap);
		printf ("Done Dumping Fragments.....\n");
	}
	
	Dbg_MsgAssert(fragmentation < maxFragmentation, ("Excessive top down fragmentation (%d) after cleanup (max=%d)",fragmentation,maxFragmentation)); 
#endif

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptClearStruct( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 structName;
	pParams->GetChecksum( "struct", &structName, Script::ASSERT );

	Script::CStruct* pStruct = Script::GetStructure( structName, Script::ASSERT );
	pStruct->Clear();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptAppendStruct( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 structName;
	pParams->GetChecksum( "struct", &structName, Script::ASSERT );

	uint32 fieldName;
	pParams->GetChecksum( "field", &fieldName, Script::ASSERT );
	
	Script::CStruct* pAppendParams;
	pParams->GetStructure( "params", &pAppendParams, Script::ASSERT );

	Script::CStruct* pSubStruct = new Script::CStruct;
	pSubStruct->AppendStructure( pAppendParams );
	
	Script::CStruct* pStruct = Script::GetStructure( structName, Script::ASSERT );
	pStruct->AddStructurePointer( fieldName, pSubStruct ); 

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptScriptExists( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 script_name_checksum=0;
	pParams->GetChecksum(NONAME,&script_name_checksum);

	CSymbolTableEntry *p_entry=Resolve(script_name_checksum);
	if (p_entry && p_entry->mType==ESYMBOLTYPE_QSCRIPT)
	{
		return true;
	}
	return false;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSpawnSecondControllerCheck( Script::CStruct* pParams, Script::CScript* pScript )
{
	 Mlp::Manager * mlp_manager =  Mlp::Manager::Instance();
	int not_used;

	if( s_second_controller_check_task != NULL )
	{
		return true;
	}

	not_used = 0;
	s_second_controller_check_task = new Tsk::Task< int > ( s_second_controller_check_code, not_used );
	mlp_manager->AddLogicTask ( *s_second_controller_check_task );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptStopSecondControllerCheck( Script::CStruct* pParams, Script::CScript* pScript )
{
	Dbg_Assert( s_second_controller_check_task != NULL );

	delete s_second_controller_check_task;
	s_second_controller_check_task = NULL;

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LoadExecPS2 | 
// @parm string | elf | elf file to load
// if this fails on a TOOL turn off SN profiling in main.cpp
// and check the media type in prepare_to_exit
bool ScriptLoadExecPS2( Script::CStruct *pParams, Script::CScript *pScript )
{

#ifdef __PLAT_NGPS__
	const char* p_string;
	pParams->GetText( "elf", &p_string, true );
    Sys::LoadExec( p_string );
#endif // __PLAT_NGPS__
	
    return true;		// actually will never get here}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*
- TODO - factor out the common functionality of ExitDemo into LoadExecPS2

the LoadExecPS2() references lists some common gotchas:

The total size of the character strings for the filename and all of the
arguments must not exceed 256 bytes. 

Note the following points before calling LoadExecPS2(). 
* Terminate or delete all threads other than the thread that will 
  execute LoadExecPS2(). 
* Cancel all callbacks and interrupt handlers. 
* Execute scePadEnd(). 
* Execute sceSifExitCmd().
*/

bool ScriptExitDemo( Script::CStruct*, Script::CScript* )
{
#ifdef __PLAT_NGPS__
    Sys::ExitDemo();
#endif

	return true;		// actually will never get here
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptIsArray( Script::CStruct* pParams, Script::CScript* pScript )
{
	Script::CArray* pTest = NULL;
	if ( pParams->GetArray( NONAME, &pTest, Script::NO_ASSERT ) )
		return true;
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UseUserSoundtrack | xbox only - uses a certain soundtrack
// @uparm 1 | the soundtrack number
bool ScriptUseUserSoundtrack( Script::CStruct* pParams, Script::CScript* pScript )
{
	Dbg_MsgAssert( Config::GetHardware() == Config::HARDWARE_XBOX, ( "UseUserSoundtrack should only be called on the XBox" ) );
	
	int soundtrack;
	pParams->GetInteger( NONAME, &soundtrack, Script::ASSERT );
	Pcm::UseUserSoundtrack( soundtrack );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UseStandardSoundtrack | xbox only - use the standard soundtrack
bool ScriptUseStandardSoundtrack( Script::CStruct* pParams, Script::CScript* pScript )
{
	Dbg_MsgAssert( Config::GetHardware() == Config::HARDWARE_XBOX, ( "UseStandardSoundtrack should only be called on XBox" ) );
	Pcm::UseStandardSoundtrack();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | DisableReset | ngc only - disable the reset button. If the user presses reset when disabled, a screen will be displayed until it is enabled, and it will reset.
bool ScriptDisableReset( Script::CStruct* pParams, Script::CScript* pScript )
{
#ifdef __PLAT_NGC__
	//printf("DisableReset ...\n");
	NxNgc::EngineGlobals.disableReset = true;
#endif		// __PLAT_NGC__ 

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EnableReset | ngc only - enable the reset button. If reset was pressed while disabled, it will now reset.
bool ScriptEnableReset( Script::CStruct* pParams, Script::CScript* pScript )
{
#ifdef __PLAT_NGC__
	//printf("WARNING! EnableReset ...\n");

	NxNgc::EngineGlobals.disableReset = false;
#endif		// __PLAT_NGC__ 

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetToIPL | ngc only - resets to IPL screen.
bool ScriptResetToIPL( Script::CStruct* pParams, Script::CScript* pScript )
{
#ifdef __PLAT_NGC__
	NxNgc::EngineGlobals.resetToIPL = true;
#endif		// __PLAT_NGC__ 

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptInitAnimCompressTable( Script::CStruct* pParams, Script::CScript* pScript )
{
	const char* pFileName;
	pParams->GetText( NONAME, &pFileName, Script::ASSERT );

	uint32 compressTableName;
	pParams->GetChecksum( NONAME, &compressTableName, Script::ASSERT );

	char filename[256];
	sprintf( filename, "%s", pFileName );//, Nx::CEngine::sGetPlatformExtension() );

	switch ( compressTableName )
	{
		case 0xc6a56fe3:	// q48
		{
			Gfx::InitQ48Table( filename );
		}
		break;
		
		case 0xc06ead08:	// t48
		{
			Gfx::InitT48Table( filename );
		}
		break;

		default:
			Dbg_MsgAssert( 0, ( "Unrecognized compress table name %s", Script::FindChecksumName(compressTableName) ) );
			break;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Test_Composite is a test array of structures
// that are the components of the composite object, and the parameters passed to it
/*
test_composite = [
    {
        component = model
        model = "gameobjects\skate\letter_a\letter_a.mdl"
    }
    
    {
        component = exceptions
    }
    
    {
        component = bouncyphysics
        bounce = 2
    }
   {
    components = [model, exceptions, explodingphysics]
    model = "gameobjects\skate\letter_a\letter_a.mdl"
    bounce = 2
   
} 
]
*/
						
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptCreateCompositeObject( Script::CStruct* pParams, Script::CScript* pScript )
{
	// Required array of structures containing "component="
	Script::CArray*	p_array = NULL;
	pParams->GetArray(CRCD(0x11b70a02,"components"),&p_array,Script::ASSERT);

	// Optional extra structure of parameters that override any provided in the array	
	Script::CStruct* p_struct = NULL;
	pParams->GetStructure(CRCD(0x7031f10c,"params"),&p_struct);
	
	Obj::CCompositeObjectManager::Instance()->CreateCompositeObjectFromNode(p_array, p_struct);
	
	return true;
}	  

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the id of an object, and a structure, 
bool ScriptRefreshObject( Script::CStruct* pParams, Script::CScript* pScript )
{


	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AutoRail |
// auto-generates rails based of the level's collidable geometry
// @parmopt float | min_rail_edge_angle | 30.0 |
// rails are created only on edges as least as sharp as this angle
// @parmopt float | max_rail_angle_of_assent | 45.0 |
// rails are created only on edges less steep than this angle
// @parmopt float | min_railset_length | 36.0 |
// autorail attempts to join contiguous edges together in order to determine the effective length of a curved rail;
// once this is done, rails shorter than this length in inches are dropped
// @parmopt float | min_edge_length | 0.0 |
// edges below this length in inches are not considered for rail creation, even if they could be part of a curved rail
// @parmopt float | max_corner_in_railset | 50.0 |
// maximum angle between two edges for them to be connected as a curved rail
// @parmopt float | connection_slop | 0.1 |
// slop distance allowed between rail endpoints below which those rails will be snapped togeather
// @parmopt float | farthest_degenerate_rail | 12.0 |
// auto-generated rails	within this distance in inches of an old rail will not be created
// @parmopt float | max_degenerate_rail_angle | 20.0 |
// if the angle between an auto-generated rail and a nearly old rail is below this angle, the auto-generated rail will not be created
// @parmopt float | max_low_curb_height | 8.0 |
// curbs below this height in inches are not given rails; controls the low-curb collision feeler length
// @parmopt float | vertical_feeler_length | 60.0 |
// length in inches of the vertical collision feeler
// @parmopt float | crossbar_feeler_length | 12.0 |
// length in inches of the crossbar collision feeler
// @parmopt float | crossbar_feeler_elevation | 12.0 |
// height in inches of the crossbar collision feeler
// @parmopt float | curb_feeler_angle_of_assent | 30.0	|
// low-curb collision feeler radiates from the rail at this angle above the bordering faces
// @parmopt float | feeler_increment | 36.0 |
// collision feelers are used at each step of this distance in inches along the rail
// @flag no_feelers |
// turns off the checking of the volume around rails by collision feelers
// otherwise this checking is done in order to insure that a rail is not cramped or on a low curb
// @flag no_vertical_feeler |
// turns off the collision feeler which radiates upward from the rail
// @flag no_crossbar_feeler |
// turns off the collision feeler which lies just above the rail and runs perpendicular to it
// @flag no_low_curb_feeler |
// turns off the collision feelers which radiate from the rail just above the bordering faces;
// these collision feelers are used to insure rails are not created on low curbs
// @flag overwrite |
// deletes old rails before auto-generating rails
bool ScriptAutoRail( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::Skate::Instance()->GetRailManager()->AutoGenerateRails(pParams);
	return true;			  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptInside(Script::CStruct* pParams, Script::CScript* pScript)
{
	Dbg_MsgAssert(Mdl::Skate::Instance()->mpProximManager->IsInsideFlagValid(), ("Inside called outside of a proxim trigger script or within a proxim trigger script with a ProximObject"));
    return Mdl::Skate::Instance()->mpProximManager->IsInside(); 
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSetSpecialBarColors(Script::CStruct* pParams, Script::CScript* pScript)
{
    int skater_num;
    pParams->GetInteger( "skater", &skater_num, Script::ASSERT );
    
    Mdl::Skate * pSkate = Mdl::Skate::Instance();
    Obj::CSkater* pSkater = pSkate->GetSkater( skater_num );
    Mdl::Score *pScore = pSkater->GetScoreObject();

    pScore->setSpecialBarColors();
    return true;
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	ScriptGetMetrics(Script::CStruct* pParams, Script::CScript* pScript)
{
// get various metrics into a structure for display
// You can run this on a spawned script to see in the viewer

	Script::CStruct *p_metrics = new Script::CStruct();
	Nx::CEngine::sGetMetrics(p_metrics);
	
	Script::CStruct *p_script_params = pScript->GetParams(); 				   
	p_script_params->AddStructure("Metrics",p_metrics);
	
	return true;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	ScriptMoveNode(Script::CStruct* pParams, Script::CScript* pScript)
{
   	// Given a name and a position, move the node to that position
	// This can only be applied to one node at once, as it's an absolute position
	// So we just do everything here.

	uint32	name;															
	if ( ! pParams->GetChecksum(CRCD(0xa1dc81f9,"name"),&name) )
	{
		printf ("Missing Name\n");
		return false;
	}

	int node_num = SkateScript::FindNamedNode( name, false );
	if( node_num == -1 )
	{
		Dbg_Printf( "MoveNode failed. Could not find node %p in the scene\n", name );
		return false;
	}
	Script::CStruct *pNode = SkateScript::GetNode( node_num );
	
	Mth::Vector new_pos, old_pos;
	
  	if ( !pParams->GetVector(CRCD(0xb9d31b0a,"position"),&new_pos)
		 && !pParams->GetVector(CRCD(0x7f261953,"pos"),&new_pos) )
	{
		printf("Missing position parameter in MoveNode\n");
		return false;
	}

	if ( !pNode->GetVector(CRCD(0xb9d31b0a,"position"),&old_pos)
		 && !pNode->GetVector(CRCD(0x7f261953,"pos"),&old_pos) )
	{
		printf("node is missing position\n");
		return false;
	}
		
	// Move the original node 
	// GJ:  i'm pretty sure that the following is incorrect,
	// and that the Z component needs to be negated, but since we don't
	// actually reference the "position" component after the object has
	// been created, the bug has slipped through the cracks...  we're
	// switching over to the new-style "pos" vector anyway
//	pNode->AddVector(CRCD(0xb9d31b0a,"position"),new_pos[X],new_pos[Y],new_pos[Z]);
	pNode->AddVector(CRCD(0x7f261953,"pos"),new_pos[X],new_pos[Y],new_pos[Z]);

	// Now look for the node type, and handle the mode for each type of thing

	Obj::CCompositeObject * p_object = (Obj::CCompositeObject *) Obj::CCompositeObjectManager::Instance()->GetObjectByID(name);

	if (p_object)
	{
		// Might also have to set the pos of the components
		// as they can store internal absolute positions
		// and the pos of the object is only used for display
		p_object->SetPos(new_pos);
	}

	uint32 ClassChecksum = 0;
	pNode->GetChecksum( 0x12b4e660 /*"Class"*/, &ClassChecksum );
	switch (ClassChecksum)
	{
		case 0xe47f1b79: //vehicle
			break;
	
		case 0xa0dfac98:  //pedestrian
		case 0x61a741e:  // ped
//			Obj::CreatePed( skate_mod->GetObjectManager(), nodeIndex );
			break;

		case 0xef59c100:  //gameobject
			break;
			
		case 0x19b1e241: // ParticleEmitter		
			{
				Nx::CParticle * p_particle = Nx::get_particle( name );
				if (p_particle)
				{
					p_particle->Refresh();
				}
			}
			break;

		case 0xb7b3bd86:  // LevelObject
			{
			}
			break;

		case 0xbf4d3536:  // LevelGeometry
		{
			/*Dbg_MsgLog(("LevelGeometry"));
			uint32 checksumName = 0;
			if ( pNode->GetChecksum( 0xa1dc81f9, &checksumName ) ) // checksum 'name'
			{
//				Nx::CSector *p_sector = Nx::CEngine::sGetSector(checksumName);
				// We only want to get sectors from the main scene, as that is what the node array applies to															
				Nx::CScene * p_main_scene = Nx::CEngine::sGetMainScene();
				Nx::CSector *p_sector = p_main_scene->GetSector(checksumName);
				if (p_sector)
				{
					Dbg_MsgAssert(p_sector,("sGetSector(0x%x) returned NULL (%s)",checksumName,Script::FindChecksumName(checksumName)));
					p_sector->SetWorldPosition(new_pos);
				}
				else
				{
					printf(" WARNING: sGetSector(0x%x) returned NULL (%s)\n",checksumName,Script::FindChecksumName(checksumName));
				}
				//Bsp::ClearWorldSectorFlag( checksumName, mSD_KILLED | mSD_NON_COLLIDABLE | mSD_INVISIBLE  | mSD_INVISIBLE2);
			}*/
			break;
 		}

		case CRCC(0x30c19600, "ClimbingNode"):
		case 0x8e6b02ad:  // railnode
			Mdl::Skate::Instance()->GetRailManager()->MoveNode( node_num, new_pos );
			break;			
		case 0x8470f2e:  // proximnode
//			Obj::Proxim_SetActive( nodeIndex, true );
			break;			
		default:
			return ( false );
			break;
	}
	
	return true;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
					 
// @script | SetActiveCamera |
// Sets the active camera to be the camera component of this object
// @parmopt checksum | id | 0 | 
// @parmopt integer | viewport | 0 |
// @flag move |	  move the new camera to the position of the old camera
bool	ScriptSetActiveCamera(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32	id=0;
	pParams->GetChecksum(CRCD(0x40c698af,"id"),&id);
	int	viewport=0;
	pParams->GetInteger(CRCD(0x9fd29151,"viewport"),&viewport);
	bool move = pParams->ContainsFlag(CRCD(0x10c1c887,"move"));

	return SetActiveCamera(id,viewport, move); 	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSin(Script::CStruct* pParams, Script::CScript* pScript)
{
    float x,sin;
    pParams->GetFloat( NONAME , &x, Script::ASSERT );
    
    sin = sinf(Mth::DegToRad(x));

    Script::CStruct *p_script_params = pScript->GetParams(); 				   
	p_script_params->AddFloat("sin",sin);
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptCos(Script::CStruct* pParams, Script::CScript* pScript)
{
    float x,cos;
    pParams->GetFloat( NONAME , &x, Script::ASSERT );
    
    cos = cosf(Mth::DegToRad(x));

    Script::CStruct *p_script_params = pScript->GetParams(); 				   
	p_script_params->AddFloat("cos",cos);
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptTan(Script::CStruct* pParams, Script::CScript* pScript)
{
    float x,tan;
    pParams->GetFloat( NONAME , &x, Script::ASSERT );
    
    tan = tanf(Mth::DegToRad(x));

    Script::CStruct *p_script_params = pScript->GetParams(); 				   
	p_script_params->AddFloat("tan",tan);
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptASin(Script::CStruct* pParams, Script::CScript* pScript)
{
    float x,asin;
    pParams->GetFloat( NONAME , &x, Script::ASSERT );
    
    asin = asinf(x);
    asin = Mth::RadToDeg(asin);

    Script::CStruct *p_script_params = pScript->GetParams(); 				   
	p_script_params->AddFloat("asin",asin);
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptACos(Script::CStruct* pParams, Script::CScript* pScript)
{
    float x,acos;
    pParams->GetFloat( NONAME , &x, Script::ASSERT );
    
    acos = acosf(x);
    acos = Mth::RadToDeg(acos);

    Script::CStruct *p_script_params = pScript->GetParams(); 				   
	p_script_params->AddFloat("acos",acos);
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptATan(Script::CStruct* pParams, Script::CScript* pScript)
{
    float x,atan;
    pParams->GetFloat( NONAME , &x, Script::ASSERT );
    
    atan = atanf(x);
    atan = Mth::RadToDeg(atan);

    Script::CStruct *p_script_params = pScript->GetParams(); 				   
	p_script_params->AddFloat("atan",atan);
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Load tracking info from a file, and display it as immediate mode lines
bool ScriptShowTracking(Script::CStruct* pParams, Script::CScript* pScript)
{
	Gfx::DebugGfx_CleanUp();
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());

	bool do_bonks = pParams->ContainsFlag(CRCD(0xb426bf5a,"bonks"));														   
	bool do_grinds = pParams->ContainsFlag(CRCD(0x14240297,"grinds"));														   
	bool do_verts = pParams->ContainsFlag(CRCD(0x969d3af6,"verts"));														   
	bool do_lines = pParams->ContainsFlag(CRCD(0xb0fe7369,"lines"));					

	// if nothing specificed, then do everything												  
	if (!do_bonks && !do_grinds && !do_verts && !do_lines)
	{
		do_bonks = do_grinds = do_verts	= do_lines = true;
	}
														   
	const char *p_name="";
	pParams->GetString(NONAME,&p_name);
	if (p_name[1] == ':')
	{
		// skip over any 'c:' the user might be specifying
		// hopefully they don't try to load things off q:, or something
		printf ("WARNING: stripped drive name off file\n");
		p_name+=2;
	}
	int size=0;
	void * p_file;
	if (File::Exist(p_name))
	{
		p_file = File::LoadAlloc(p_name,&size);
	
		printf ("Loaded %s, size = %d, at %p\n",p_name,size,p_file);
	
		Mem::Manager::sHandle().PopContext();
	}
	else
	{
		printf ("Can't find file: %s\n",p_name);
		return false;
	}
	
	int bonks = 0;
	int grinds = 0;				 
	int verts = 0;				 
	int lines = 0;				 
	char *p = (char*)p_file;
	while (p < ((char*)p_file+size))
	{
		if (
			    p[0] == 0x0a
			&&	p[1] == 'T'
			&&	p[2] == 'r'
			&&	p[3] == 'a'
			&&	p[4] == 'c'
			&&	p[5] == 'k'
			&&	p[6] == 'i'
			&&	p[7] == 'n'
			&&	p[8] == 'g'
			)
		{
			int type = p[9] - '0';
			p += 11;
			float f[6];
			for (int i=0;i<6;i++)
			{
				f[i] = (float)atof(p);
				while (*p=='-' || *p=='.' || (*p>='0' && *p<='9'))
				{
					p++;
				}
				p++;  // skip the comma (or half the EOL)
			}
			p--;	// back up!
//			printf ("%d: %f,%f,%f - %f,%f,%f,%f\n",type,f[0],f[1],f[2],f[3],f[4],f[5]);

			Mth::Vector start = Mth::Vector(f[0],f[1],f[2]); 
			Mth::Vector end = Mth::Vector(f[3],f[4],f[5]);
			
			uint32 color = 0xff00ff;
			switch (type)
			{
				case 1:
					bonks++;
					if (!do_bonks) goto sorry;
					color = 0xffffff;	  // white = Bonk
					start -= (end-start)*10;
					break;
				case 2:
					grinds++;
					if (!do_grinds) goto sorry;
					color = 0x0000ff;	  // Red = Grind
					//start -= (end-start)*2;
					break;
				case 3: 
					verts++;
					if (!do_verts) goto sorry;
					color = 0xffff00;	  // Cyan = Vert
					start -= (end-start)*2;
					break;
				case 4: 
					lines++;
					if (!do_lines) goto sorry;
					color = 0x008000;	  // Green = Line
					start -= (end-start)*4;
					break;
				default:
					color = 0xffff;
			}
			Gfx::AddDebugArrow(start, end,color,color,0);
sorry:		
		;	
		}
	
	
		p++;
	}
	
	printf ("Bonks = %d,  Grinds = %d, verts= %d, lines = %d\n",bonks, grinds, verts, lines);
	
	Mem::Free(p_file);	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsGrind | 
bool ScriptIsGrind(Script::CStruct* pParams, Script::CScript* pScript)
{
	return pParams->ContainsFlag(CRCD(0x255ed86f, "Grind"));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetColorBufferClear | 
// @parmopt integer | clear | 0 |
bool ScriptSetColorBufferClear(Script::CStruct* pParams, Script::CScript* pScript)
{
	int	clear = 0;
	pParams->GetInteger( CRCD( 0x1a4e0ef9, "clear" ), &clear );

	Nx::CEngine::sSetColorBufferClear( clear > 0 );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ShowCamOffset |
// @parm name | Name | The name of the node we want to get the offset from
// Printfs the cam offset in EE0
bool ScriptShowCamOffset(Script::CStruct* pParams, Script::CScript* pScript)
{
	#ifdef	__NOPT_ASSERT__
	// Given a node name
	// then calculate a good targetOffset and positionOffset
	// so we can mode the camera into that position later

	// Some common code to either relative, or world specific
	Mth::Matrix cam_matrix = Nx::CViewportManager::sGetActiveCamera()->GetMatrix();
	Mth::Vector cam_pos = Nx::CViewportManager::sGetActiveCamera()->GetPos();			

	// get a line segment extending fromthe camera forward (note -z)
	Mth::Line cam_fwd;
	cam_fwd.m_start = cam_pos;
	cam_fwd.m_end = cam_pos - cam_matrix[Z] * 10000;

	
	uint32 name = 0;
	if (pParams->GetChecksum(CRCD(0xa1dc81f9,"name"),&name))
	{
		
		if (name != CRCD(0xc588eebc,"world"))
		{
			// get the node from the node array, and get the position
			int node = SkateScript::FindNamedNode(name);
			if (node != -1)
			{
				Script::CStruct *pNode = SkateScript::GetNode(node);
				Mth::Vector	pos;
				pNode->GetVector(CRCD(0x7f261953,"pos"),&pos);
	
	
				Mth::Matrix cam_matrix = Nx::CViewportManager::sGetActiveCamera()->GetMatrix();
				Mth::Vector cam_pos = Nx::CViewportManager::sGetActiveCamera()->GetPos();			
	
	
				// get a line segment extending fromthe camera forward (note -z)
				Mth::Line cam_fwd;
				cam_fwd.m_start = cam_pos;
				cam_fwd.m_end = cam_pos - cam_matrix[Z] * 10000;
				
				Mth::Line node_up;
				node_up.m_start = pos;
				node_up.m_end = pos + Mth::Vector(0,10000,0);
	
				Mth::Vector pa, pb;			
				float mua,mub;
				if (Mth::LineLineIntersect( cam_fwd, node_up, &pa, &pb, &mua, &mub, false ))
				{
					Gfx::AddDebugArrow(pos, pa,0xff0000,0,100);			
	//				Gfx::AddDebugArrow(pa, pb ,0xff00  ,0,100);			
				}
				else
				{
					printf ("Can't calculate intersection, move camera a bit\n");
				}			
				
				//Need to find the offset from this point to the line of sight of the camera
				// we have  cam_pos = position of camera
				//          pos     = position of node
				//          pa      = position of the focus
				
				Mth::Vector target_offset = pa-pos;
				Mth::Vector position_offset = cam_pos - pa;
				
				/*
				printf ("\nUnrotated - Use if focussing relative to the ground\n");																									 
				printf ("		targetOffset=(%.1f, %.1f, %.1f)\n",target_offset[X],target_offset[Y],target_offset[Z]);
				printf ("		positionOffset=(%.1f, %.1f, %.1f)\n",position_offset[X],position_offset[Y],position_offset[Z]);
				*/
				
				// Now we have to transform it by the inverse fo the matrix representing the orientation fo the node
				Obj::CCompositeObject *p_obj = (Obj::CCompositeObject *)Obj::CCompositeObjectManager::Instance()->GetObjectByID(name);
				if (!p_obj)
				{
					printf ("Can't find the oject this node represents\n");
					return false;
				}
				// Get the matrix
				Mth::Matrix obj_mat = p_obj->GetMatrix();
				// Invert it, as we want to do the opposite of what we will do with the actual values
				obj_mat.Invert();
				// apply this, to get the values
				target_offset = obj_mat.TransformAsPos(target_offset);
				position_offset = obj_mat.TransformAsPos(position_offset);
				
//				printf ("\nRotated (Use if focussing relative to the object's orientation)\n");																									 
				printf ("		targetid=%s\n",Script::FindChecksumName(name)); 
				printf ("		targetOffset=(%.1f, %.1f, %.1f)\n",target_offset[X],target_offset[Y],target_offset[Z]);
				printf ("		positionOffset=(%.1f, %.1f, %.1f)\n",position_offset[X],position_offset[Y],position_offset[Z]);
	
				return true;
			}			
		}
	}
	
	// Looks like we want world coordinates, so print up targetOffset as a point 100 feet in front of the camera
	// and positionoffset as the actual point of the camera
	printf ("		targetid=world\n"); 
	printf ("		targetOffset=(%.1f, %.1f, %.1f)\n",cam_fwd.m_end[X],cam_fwd.m_end[Y],cam_fwd.m_end[Z]);
	printf ("		positionOffset=(%.1f, %.1f, %.1f)\n",cam_pos[X],cam_pos[Y],cam_pos[Z]);
	
	
	
	#endif
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


// @script | SetEventHandler | register this script to listen for the given event and gosub to the given script in response to the event
// @parm name | Ex | the name of the event listened for
// @parm script | Scr | the script to be run in response to the event
// @parmopt name | Group | the name of this event listeners group; used to identify which listeners to clear when clearing listener groups
// @parmopt structure | Params | this structure is added to the parameter list of the script
// @parmopt flag | exception | set if this causes an exception, rather than a goto
bool ScriptSetEventHandler(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 ex = 0;
	pParams->GetChecksum(CRCD(0xf8728bec,"Ex"), &ex, true);
	uint32 scr = 0;
	pParams->GetChecksum(CRCD(0xa6d2d890,"Scr"), &scr, true);
	uint32 group = CRCD(0x1ca1ff20,"default");
	pParams->GetChecksum(CRCD(0x923fbb3a,"group"), &group);	
	bool exception = pParams->ContainsFlag(CRCD(0x80367192,"exception"));
	Script::CStruct *pExtraParams=NULL;
	pParams->GetStructure(CRCD(0x7031f10c,"params"),&pExtraParams);
	pScript->SetEventHandler(ex,scr,group,exception,pExtraParams);	
	return true;
}

bool ScriptClearEventHandler(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 type;
	pParams->GetChecksum(NO_NAME, &type, Script::ASSERT);
	pScript->RemoveEventHandler(type);
	return true;
}
			
bool ScriptClearEventHandlerGroup(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 group = Obj::CEventHandlerTable::vDEFAULT_GROUP;
	pParams->GetChecksum(NO_NAME, &group);
	pScript->RemoveEventHandlerGroup(group);
	return true;
}
		
// @script | OnExceptionRun | run the specified script on exception
// @uparm name | script name to run
// can be called without a parameter to clear it
bool ScriptOnExceptionRun(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 OnExceptionScriptChecksum = 0;
	pParams->GetChecksum( NONAME, &OnExceptionScriptChecksum);
	pScript->SetOnExceptionScriptChecksum(OnExceptionScriptChecksum);
	return true;
}

bool ScriptOnExitRun(Script::CStruct* pParams, Script::CScript* pScript)
{
	uint32 OnExitScriptChecksum = 0;
	pParams->GetChecksum( NONAME, &OnExitScriptChecksum);
	pScript->SetOnExitScriptChecksum(OnExitScriptChecksum);
	return true;
}


// @script | Block | Stop execution of this script, allowing to still be woken up by events and exceptions
bool ScriptBlock(Script::CStruct* pParams, Script::CScript* pScript)
{
	pScript->Block();
	return true;
}

// @script | PrintEventHandlerTable | Prints the event handler table of the current script
bool ScriptPrintEventHandlerTable ( Script::CStruct* pParams, Script::CScript* pScript )
{
	pScript->PrintEventHandlerTable();
	return true;
}


// @script | AllocateSplitScreenDMA | (PS2 only) allocated extra memory for DMA usage
bool ScriptAllocateSplitScreenDMA ( Script::CStruct* pParams, Script::CScript* pScript )
{
	#ifdef	__PLAT_NGPS__
	
	Nx::CEngine::sFinishRendering();
 	NxPs2::AllocateExtraDMA(true);

	#endif
	
	return true;
}



// @script | AddSkaterEarly | Create and load a skater, so when we autolaunch a level, it's there.  DEBUG ONLY
bool ScriptAddSkaterEarly( Script::CStruct* pParams, Script::CScript* pScript )
{

	if (Mdl::Skate::Instance()->GetNumSkaters() == 0)
	{
		Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile(0);
		Mdl::Skate::Instance()->add_skater( pSkaterProfile, true, 0, 0);
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptPreLoadStreamDone( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 streamId;
	pParams->GetChecksum( NONAME, &streamId, Script::ASSERT );
	return Pcm::PreLoadStreamDone( streamId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptStartPreLoadedStream( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 streamId;
	pParams->GetChecksum( CRCD(0x8a68ab90,"streamId"), &streamId, Script::ASSERT );
	float vol = 100;
	pParams->GetFloat( CRCD(0x46653221,"volume"), &vol, Script::NO_ASSERT );
	Sfx::sVolume* p_volume = new Sfx::sVolume();
	p_volume->SetChannelVolume( 0, vol );
	p_volume->SetChannelVolume( 1, vol );
	bool rc = Pcm::StartPreLoadedStream( streamId, p_volume );
	delete p_volume;
	return rc;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptFinishRendering( Script::CStruct* pParams, Script::CScript* pScript )
{
	Nx::CEngine::sFinishRendering();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace CFuncs


================================================
FILE: Code/Sk/Scripting/cfuncs.h
================================================
//****************************************************************************
//* MODULE:         Sk/Scripting
//* FILENAME:       CFuncs.h
//* OWNER:          Kendall Harrison
//* CREATION DATE:  9/14/2000
//****************************************************************************

#ifndef	__SCRIPTING_CFUNCS_H
#define	__SCRIPTING_CFUNCS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

// for now, include skfuncs.h, so that
// it won't break the existing CPP files
// that include cfuncs.h
//#include 

namespace Script
{
	class CStruct;
	class CScript;
};
	
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace CFuncs
{



/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

enum {
	TYPE_VUTESTOBJECT = 0x8daad03d,
	VUTESTOBJECT = 0xae96a38,
};


bool	SetActiveCamera(uint32 id, int viewport, bool move_to_current);


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

bool ScriptDummyCommand(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptAllocateSplitScreenDMA ( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptWait(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptBlock(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPrintEventHandlerTable(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptFormatText(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPrintf(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptScriptAssert(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPrintScriptInfo(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetHackFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptHackFlagIsSet(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPrintStruct(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnloadAllLevelGeometry(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLoadScene(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLoadCollision(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAddScene(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAddCollision(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnloadScene(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptQuickReload(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLoadNodeArray(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptReLoadNodeArray(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptParseNodeArray(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetBackgroundColor(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetBSPAmbientColor(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetDFFAmbientColor(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetDFFDirectColor(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetDynamicLightModulationFactor( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetClippingDistances(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetTrivialFarZClip(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEnableFog(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDisableFog(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetFogDistance(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetFogExponent(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetFogColor(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetUVWibbleParams(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEnableExplicitUVWibble(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDisableExplicitUVWibble(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetUVWibbleOffsets(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetWorldSize( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetMovementVelocity( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetRotateVelocity( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptOnReload(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptResetEngine(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleMetrics(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleVRAMViewer(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetVRAMPackContext(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDumpVRAMUsage(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleLightViewer(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCleanup(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptProximCleanup(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLoadQB(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnloadQB(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetDebugRenderMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleTextureUpload(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleTextureDraw(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptTogglePointDraw(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRenderTest1(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRenderTest2(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptIsZero(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCastToInteger(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptStringToInteger(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptIntegerEquals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptChecksumEquals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptStringEquals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptArrayContains(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetArraySize(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetArrayElement(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAddParams(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGet3DArrayData(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGet2DArrayData(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetNDArrayData(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRemoveComponent(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetScriptString(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetScriptString(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptKenTest1(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptKenTest2(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAppendSuffix(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptFindNearestRailPoint(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetTime(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetDate(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetObNearestScreenCoord(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRandomize(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptResetTimer(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptTimeGreaterThan(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetStartTime(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetElapsedTime(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRemoveParameter(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetRandomValue(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetConfig(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPrintConfig(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGerman(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptFrench(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSpanish(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptItalian(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetNodeName(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetTesterScript(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptKillTesterScript(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptResetStopwatch(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPrintStopwatchTime(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCustomSkaterFilenameDefined(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetCustomSkaterFilename(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetCustomSkaterFilename(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEditingPark(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetParkName(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetParkName(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptParkEditorThemeWasSwitched(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptMenuIsShown(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptMenuIsSelected(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptForEachIn(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSizeOf(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetElement(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetNextArrayElement(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetRandomArrayElement(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPermuteArray(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptApplyChangeGamma( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGetGammaValues( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGotParam(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPreloadModel(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGoto(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGotoPreserveParams(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGotoRandomScript(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleRenderMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetRenderMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetWireframeMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDebugRenderIgnore(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptScreenShot(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDumpShots(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleMetricItem( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptToggleMemMetrics( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptKillAllTextureSplats( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPlayMovie( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLoadAsset( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLoadAnim( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLoadSkeleton( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptUnloadAnim( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptAssManSetReferenceChecksum( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptAssManSetDefaultPermanent( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLoadSound( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPlaySound( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptStopSound( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptStopAllSounds( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetSoundParams( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptIsSoundPlaying( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPlayStream( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptStopStream( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetStreamParams( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptIsStreamPlaying( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetReverb( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLoadTerrainSounds( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetObjectColor( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetSceneColor( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCompressVC( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptNudgeVC( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptFakeLights( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCenterCamera( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetDropoff( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetVolume( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetMusicVolume( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetMusicStreamVolume( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLoadMusicHeader( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLoadStreamHeader( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptStreamIsAvailable( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPlayTrack( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPlayMusicStream( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSkipMusicTrack( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetRandomMode( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGetCurrentTrack( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetMusicMode( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetMusicLooping( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptStopMusic( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPauseMusic( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPauseStream( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLoadStreamFrameAmp( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptFreeStreamFrameAmp( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptAddMusicTrack( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptChangeTrackState(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptTrackEnabled(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptMusicIsPaused(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptClearMusicTrackList( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptIfDebugOn(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptIfDebugOff(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptBootstrap(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCD(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptNotCD(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCasArtist(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptNotCasArtist(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGunslinger(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDoAction(Script::CStruct *pParams, Script::CScript *pScript, int action );
bool ScriptSendFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptClearFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptQueryFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCheckIfAlive( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCheckExistenceFromNodeIndex( int nodeIndex );
bool ScriptFlagException(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptMakeSkaterGoto(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptMakeSkaterGosub(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSpawnSound(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSpawnScript(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSpawnSkaterScript(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptKillSpawnedScript(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPauseSkaters( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptUnPauseSkaters( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPauseSkater( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptUnPauseSkater( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPauseGame( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptUnPauseGame( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptIsGamePaused( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPauseObjects( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptUnPauseObjects( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPauseSpawnedScripts( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptUnPauseSpawnedScripts( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptResetClock( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPadsPluggedIn( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptDoFlash( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetViewMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptStartServer(Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSpawnCrown(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSpawnCompass(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLeaveServer(Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptFindServers(Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptJoinServer(Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetNetworkMode( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetServerMode( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCreateFromNodeIndex( int nodeIndex );
bool ScriptKillFromNodeIndex( int nodeIndex, Script::CScript *pScript );
bool ScriptSetColorFromNodeIndex( int nodeIndex, Script::CStruct *pParams );
bool ScriptShatterFromNodeIndex( int nodeIndex );
bool ScriptSetVisibilityFromNodeIndex( int nodeIndex, bool invisible, int viewport_number = 0 );
bool ScriptNodeExists( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCreate( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCreateFromStructure( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptKill( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptVisible( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptInvisible( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptShatter( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptResetCamera(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetValueFromVolume(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetSliderValue(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetVolumeFromValue(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetIconTexture(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptMemViewToggle(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPreferenceEquals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetPreference(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetPreferenceString(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetPreferencePassword(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetPreferenceChecksum(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptMemPushContext(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptMemPopContext(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptProfileTasks( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptUseNetworkPreferences( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCanChangeDevices( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptNeedToTestNetSetup( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptTestNetSetup( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptConnectToInternet( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCancelConnectToInternet( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCancelLogon( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptDisconnectFromInternet( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptIsOnline( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptStopAllScripts( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetMenuElementText(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptFirstTimeThisIsCalled(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEnableActuators(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptInNetGame(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleNetMetrics(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetSlomo(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetArenaSize(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetParticleSysVisibility( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptTogglePlayerNames( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetCurrentGameType( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptDumpNetMessageStats( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptNotifyBailDone( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptDisplayLoadingScreen( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptHideLoadingScreen( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptObserveNextSkater( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPrintMemInfo( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptDisplayFreeMem( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptAnalyzeHeap( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptMemThreadSafe( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptIsSingleSession(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEnterObserverMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAllowPause(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRefreshServerList(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptStartServerList(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptStopServerList(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptFreeServerList(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPauseGameFlow(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnpauseGameFlow(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptInFrontEnd(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetFrontEndInactive(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptInSplitScreenGame(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGameModeEquals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetFireballLevel(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptOnServer(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetCurrentLevel(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRestartLevel(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleScores(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptIsTrue(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetLevelLoadScript(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptXTriggered(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUsePad(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptInMultiplayerGame(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleShadowType(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptGameFlow(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptControllerPressed(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetAnalogueInfo(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptControllerDebounce( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptClearCheats(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptBroadcastCheat(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLastBroadcastedCheatWas(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCheatAllowed(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptJoinWithPassword(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSendChatMessage(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptInSlapGame( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetScreenModeFromGameMode( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetScreenMode( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptLoadPendingPlayers( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptLaunchQueuedScripts( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptIsObserving( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSkatersAreReady(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptGetInitialsString( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetInitialsString( Script::CStruct *pParams, Script::CScript *pScript );
bool	ScriptAttachToSkater(  Script::CStruct *pParams, Script::CScript *pScript );
bool	ScriptTryCheatString(  Script::CStruct *pParams, Script::CScript *pScript );
bool	ScriptLevelIs(  Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptStartNetworkLobby( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptObserversAllowed( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptNumPlayersAllowed( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptAutoDNS( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptUsingDefaultMasterServers( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptUsingDHCP( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptInInternetMode( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptEnteringNetGame( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptDeviceChosen( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLoadExecPS2( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptExitDemo( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptGameIsOver( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptSetLevelName( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptDumpHeaps( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptDumpFragments( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptResetPS2( Script::CStruct *pParams, Script::CScript *pScript ); 
bool ScriptResetHD( Script::CStruct *pParams, Script::CScript *pScript ); 
bool ScriptPAL( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptEnglish( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptTimeUp( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptChangeSymbolValue(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDumpScripts( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptResetPS2( Script::CStruct *pParams, Script::CScript *pScript ); 
bool ScriptResetHD( Script::CStruct *pParams, Script::CScript *pScript ); 
bool ScriptPAL( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptEnglish( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptTimeUp( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptLaunchViewer( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptLaunchScriptDebugger( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetViewerModel( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetViewerAnim( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetViewerLODDist( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetViewerObjectID( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptReloadViewerAnim( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptInitHealth( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptResetScore( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptAddRestartsToMenu( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptAddWarpPointsToMenu( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptRunScriptOnObject( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptRunScriptOnComponentType( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptTestName( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptDebounce( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetStatOverride( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptToggleRails( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptToggleRigidBodyDebug( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptCheckForHoles( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptStartLobbyList( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptChatConnect( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptChatDisconnect( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptFillPlayerListMenu( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptOnXbox( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGotoXboxDashboard( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSystemLinkEnabled( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptCreateParticleSystem( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetScript( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptDestroyParticleSystem( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptEmptyParticleSystem( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptParticleExists( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptStructureContains( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetBonePosition( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptMangleChecksums( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptAppendSuffixToChecksum( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptShouldEmitParticles( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptParticlesOn( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptParticlesOff( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptRotateVector( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptIsPS2( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptIsXBOX( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptIsNGC( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptIsWIN32( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetPlatform( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptIsPal( Script::CStruct* pParams, Script::CScript* pScript );


bool ScriptPushMemProfile( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptPopMemProfile( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptTogglePass( Script::CStruct* pParams, Script::CScript* pScript );

bool	ScriptSetScreen( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGetUpperCaseString( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGetGameMode( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptStartKeyboardHandler( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptStopKeyboardHandler( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptEnableKeyboard( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptDisableKeyboard( Script::CStruct* pParams, Script::CScript* pScript );
	 
bool ScriptCreateIndexArray( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetRescaledTargetValue( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptMemInitHeap( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptMemDeleteHeap( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptClearStruct( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptAppendStruct( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptScriptExists( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptSpawnSecondControllerCheck( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptStopSecondControllerCheck( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptIsArray( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptUseUserSoundtrack( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptUseStandardSoundtrack( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptDisableReset( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptEnableReset( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptResetToIPL( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptInitAnimCompressTable( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptCreateCompositeObject( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptAutoRail( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptInside( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptSetSpecialBarColors( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetMetrics( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptMoveNode( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetActiveCamera( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptSetColorBufferClear( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetEventHandler( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptClearEventHandler(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptClearEventHandlerGroup(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptOnExceptionRun(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptOnExitRun(Script::CStruct* pParams, Script::CScript* pScript);



bool ScriptSin( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptCos( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptTan( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptASin( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptACos( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptATan( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptShowTracking(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptIsGrind(Script::CStruct* pParams, Script::CScript* pScript);
bool ScriptShowCamOffset(Script::CStruct* pParams, Script::CScript* pScript);

bool ScriptAddSkaterEarly( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptPreLoadStreamDone( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptStartPreLoadedStream( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptFinishRendering( Script::CStruct* pParams, Script::CScript* pScript );

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace CFuncs

#endif	// __SCRIPTING_CFUNCS_H



================================================
FILE: Code/Sk/Scripting/ftables.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Scripting                  								**
**																			**
**	File name:		Scripting/ftables.cpp          							**
**																			**
**	Created by:		09/15/00	-	ksh										**
**																			**
**	Description:	Lists of C-functions & class member functions callable 	**
**                  from scripts.                                           **
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 

#include 

#include 
#ifdef __PLAT_NGPS__
#include 
#include 
#include 
#include 
#endif

#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 



#ifdef __PLAT_NGC__
#include 
#endif		// __PLAT_NGC__
			  
/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Script
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

// A list of all the C functions callable from scripts.
// The name in quotes must be the same as the way it appears in q scripts. (case insensitive)
SCFunction CFunctionLookupTable[]=
{
    {"Wait",				CFuncs::ScriptWait},
	{"WaitForEvent",		Obj::ScriptWaitForEvent},
	{"Block",				CFuncs::ScriptBlock},

    // Prints the contents of the passed structure.
    {"PrintStruct",         CFuncs::ScriptPrintStruct},
	
	// Prints the script's event handler table.
    {"PrintEventHandlerTable",	CFuncs::ScriptPrintEventHandlerTable},
    
    {"AllocateSplitScreenDMA",	CFuncs::ScriptAllocateSplitScreenDMA},
										
										
    // Printf "Default printf text"
    {"Printf",				CFuncs::ScriptPrintf},
	{"ScriptAssert",		CFuncs::ScriptScriptAssert},
	{"PrintScriptInfo",		CFuncs::ScriptPrintScriptInfo},
	{"FormatText",			CFuncs::ScriptFormatText},
	
	{"SetHackFlag",			CFuncs::ScriptSetHackFlag},
	{"HackFlagIsSet",		CFuncs::ScriptHackFlagIsSet},
	{"SetCurrentSkaterProfile",	CFuncs::ScriptSetCurrentSkaterProfile},
	{"CurrentSkaterProfileIs",	CFuncs::ScriptCurrentSkaterProfileIs},
	{"AddSkaterProfile",		CFuncs::ScriptAddSkaterProfile},
	{"AddTemporaryProfile",		CFuncs::ScriptAddTemporaryProfile},
	{"RememberTemporaryAppearance",CFuncs::ScriptRememberTemporaryAppearance},
	{"RestoreTemporaryAppearance",	CFuncs::ScriptRestoreTemporaryAppearance},
	{"SyncPlayer2Profile",	CFuncs::ScriptSyncPlayer2Profile},
	
	{"UnloadAllLevelGeometry",	CFuncs::ScriptUnloadAllLevelGeometry},
	
    // LoadLevelGeometry "blaa"
	{"QuickReload",			CFuncs::ScriptQuickReload},
	{"UnloadScene",			CFuncs::ScriptUnloadScene},
	{"LoadScene",			CFuncs::ScriptLoadScene},
	{"LoadCollision",		CFuncs::ScriptLoadCollision},
	{"AddScene",			CFuncs::ScriptAddScene},
	{"AddCollision",		CFuncs::ScriptAddCollision},
	{"ToggleAddScenes",		Nx::ScriptToggleAddScenes},
	{"SetScreenBlur",		Nx::ScriptSetScreenBlur},
    // LoadNodeArray "blaa.qb"
    {"LoadNodeArray",		CFuncs::ScriptLoadNodeArray},
    {"ReLoadNodeArray",		CFuncs::ScriptReLoadNodeArray},
	{"ParseNodeArray",		CFuncs::ScriptParseNodeArray},
    
    // SetBackgroundColor r=0 g=0 b=0 alpha=0
    {"SetBackgroundColor",  CFuncs::ScriptSetBackgroundColor},
    
    // Won't do anything yet ...   
    // SetAmbientColor r=0 g=0 b=0 alpha=0                                         
//    {"SetBSPAmbientColor",  CFuncs::ScriptSetBSPAmbientColor},
//    {"SetDFFAmbientColor",  CFuncs::ScriptSetDFFAmbientColor},
//    {"SetDFFDirectColor",  CFuncs::ScriptSetDFFDirectColor},
//	{"SetDFFDirectRotation", Mdl::ScriptSetDirectionalLightDirection},
//	{"SetDFFDirectActive", Mdl::ScriptSetDirectionalLightActive},
	
	{"SetDynamicLightModulationFactor", CFuncs::ScriptSetDynamicLightModulationFactor},
    
    // Sets the clipping distances.
    // SetClippingDistances Near=100 Far=1000
    {"SetClippingDistances",    CFuncs::ScriptSetClippingDistances},
	{"SetTrivialFarZClip",		CFuncs::ScriptSetTrivialFarZClip},
    
	// Sets the default world size (for viewing dffs)
    // SetWorldSize Size=100
//    {"SetWorldSize",    CFuncs::ScriptSetWorldSize},

    // Sets the movement( translational ) velocity for the camera in the viewer
	{"SetMovementVelocity", CFuncs::ScriptSetMovementVelocity },
	// Sets the rotational velocity for the camera in the viewer
	{"SetRotateVelocity", CFuncs::ScriptSetRotateVelocity},

	{"SkaterLastScoreLandedGreaterThan", CFuncs::ScriptLastScoreLandedGreaterThan},
	{"SkaterLastScoreLandedLessThan", CFuncs::ScriptLastScoreLandedLessThan},
	{"SkaterTotalScoreGreaterThan", CFuncs::ScriptTotalScoreGreaterThan},
	{"AnySkaterTotalScoreAtLeast", CFuncs::ScriptAnyTotalScoreAtLeast},
	{"OnlyOneSkaterLeft", CFuncs::ScriptOnlyOneSkaterLeft},
	{"SkaterTotalScoreLessThan", CFuncs::ScriptTotalScoreLessThan},
	{"SkaterCurrentScorePotGreaterThan", CFuncs::ScriptCurrentScorePotGreaterThan},
	{"SkaterCurrentScorePotLessThan", CFuncs::ScriptCurrentScorePotLessThan},
	{"SkaterGetScoreInfo", CFuncs::ScriptSkaterGetScoreInfo},

	{"SkaterSpeedGreaterThan", CFuncs::ScriptSkaterSpeedGreaterThan},
	{"SkaterSpeedLessThan", CFuncs::ScriptSkaterSpeedLessThan},

	{"ToggleAlwaysSpecial", CFuncs::ScriptToggleAlwaysSpecial},

	{"Debounce",			CFuncs::ScriptDebounce},
	{"SetStatOverride",		CFuncs::ScriptSetStatOverride},
	{"ToggleRails",			CFuncs::ScriptToggleRails},
	{"ToggleRigidBodyDebug", CFuncs::ScriptToggleRigidBodyDebug},
	{"CheckForHoles",		CFuncs::ScriptCheckForHoles},
	
	{"OnReload",			CFuncs::ScriptOnReload},
	{"ResetEngine",			CFuncs::ScriptResetEngine},
	{"ResetSkaters",		CFuncs::ScriptResetSkaters},
	{"ToggleMetrics",		CFuncs::ScriptToggleMetrics},
	{"ToggleVRAMViewer",	CFuncs::ScriptToggleVRAMViewer},
	{"ToggleLightViewer",	CFuncs::ScriptToggleLightViewer},
	{"DumpVRAMUsage",		CFuncs::ScriptDumpVRAMUsage},
	{"SetVRAMPackContext",	CFuncs::ScriptSetVRAMPackContext},
	{"Cleanup",	    		CFuncs::ScriptCleanup},
	{"ProximCleanup",  		CFuncs::ScriptProximCleanup},
	{"LoadQB",				CFuncs::ScriptLoadQB},
	{"UnloadQB",			CFuncs::ScriptUnloadQB},
	{"DebugRenderIgnore",	CFuncs::ScriptDebugRenderIgnore},
	{"debugrendermode",		CFuncs::ScriptSetDebugRenderMode},
	{"debugtoggletextureupload",		CFuncs::ScriptToggleTextureUpload},
	{"debugtoggletexturedraw",		CFuncs::ScriptToggleTextureDraw},
	{"debugtogglepointdraw",		CFuncs::ScriptTogglePointDraw},
	{"debugrendertest1",			CFuncs::ScriptRenderTest1},
	{"debugrendertest2",			CFuncs::ScriptRenderTest2},

    {"IsZero",					CFuncs::ScriptIsZero},
	{"CastToInteger",			CFuncs::ScriptCastToInteger},
	{"StringToInteger",			CFuncs::ScriptStringToInteger},
	{"IntegerEquals",			CFuncs::ScriptIntegerEquals},
	{"ChecksumEquals",			CFuncs::ScriptChecksumEquals},
	{"StringEquals",			CFuncs::ScriptStringEquals},
	{"ArrayContains",			CFuncs::ScriptArrayContains},
	{"GetArraySize",			CFuncs::ScriptGetArraySize},
	{"SetArrayElement",			CFuncs::ScriptSetArrayElement},
	{"Get3DArrayData",			CFuncs::ScriptGet3DArrayData},
	{"Get2DArrayData",			CFuncs::ScriptGet2DArrayData},
	{"GetNDArrayData",			CFuncs::ScriptGetNDArrayData},
	{"AddParams",				CFuncs::ScriptAddParams},
	{"RemoveComponent",			CFuncs::ScriptRemoveComponent},
	{"SetScriptString",			CFuncs::ScriptSetScriptString},
	{"GetScriptString",			CFuncs::ScriptGetScriptString},
    {"KenTest1",				CFuncs::ScriptKenTest1},
    {"KenTest2",				CFuncs::ScriptKenTest2},
	{"AppendSuffix",			CFuncs::ScriptAppendSuffix},
	{"FindNearestRailPoint",	CFuncs::ScriptFindNearestRailPoint},
	{"GetTime",					CFuncs::ScriptGetTime},
	{"GetDate",					CFuncs::ScriptGetDate},
	{"GetObNearestScreenCoord",	CFuncs::ScriptGetObNearestScreenCoord},
	{"Randomize",				CFuncs::ScriptRandomize},
	{"RunningReplay",			Replay::ScriptRunningReplay},
	{"PauseReplay",				Replay::ScriptPauseReplay},
	{"UnPauseReplay",			Replay::ScriptUnPauseReplay},
	{"DeleteDummies",			Replay::ScriptDeleteDummies},
	{"ShowGameObjects",			Replay::ScriptShowGameObjects},
	{"HideGameObjects",			Replay::ScriptHideGameObjects},
	{"PlaybackReplay",			Replay::ScriptPlaybackReplay},
	{"ReplayRecordSimpleScriptCall",	Replay::ScriptReplayRecordSimpleScriptCall},
	{"RecordPanelMessage",		Replay::ScriptRecordPanelMessage},
	{"SwitchToReplayRecordMode",Replay::ScriptSwitchToReplayRecordMode},
	{"SwitchToReplayIdleMode",	Replay::ScriptSwitchToReplayIdleMode},
	{"AllocateReplayMemory",	Replay::ScriptAllocateReplayMemory},
	{"DeallocateReplayMemory",	Replay::ScriptDeallocateReplayMemory},
	{"StartRecordingAfresh",				Replay::ScriptStartRecordingAfresh},
	{"RememberLevelStructureNameForReplays",	Replay::ScriptRememberLevelStructureNameForReplays},
	{"GetReplayLevelStructureName",	Replay::ScriptGetReplayLevelStructureName},
	{"ClearTrickAndScoreText",		Replay::ScriptClearTrickAndScoreText},
	{"NeedToLoadReplayBuffer",	CFuncs::ScriptNeedToLoadReplayBuffer},
	{"LoadReplayData",			CFuncs::ScriptLoadReplayData},
	{"ResetTimer",				CFuncs::ScriptResetTimer},
	{"TimeGreaterThan",			CFuncs::ScriptTimeGreaterThan},
	{"GetStartTime",			CFuncs::ScriptGetStartTime},
	{"GetElapsedTime",          CFuncs::ScriptGetElapsedTime},
	{"RemoveParameter",			CFuncs::ScriptRemoveParameter},
	{"GetRandomValue",			CFuncs::ScriptGetRandomValue},
	{"SetConfig",				CFuncs::ScriptSetConfig},
	{"PrintConfig",				CFuncs::ScriptPrintConfig},
	{"German",					CFuncs::ScriptGerman},
	{"French",					CFuncs::ScriptFrench},
	{"Spanish",					CFuncs::ScriptSpanish},
	{"Italian",					CFuncs::ScriptItalian},
	{"GetNodeName",				CFuncs::ScriptGetNodeName},
	{"SetTesterScript",			CFuncs::ScriptSetTesterScript},
	{"KillTesterScript",		CFuncs::ScriptKillTesterScript},
	{"ResetStopwatch",			CFuncs::ScriptResetStopwatch},
	{"PrintStopwatchTime",		CFuncs::ScriptPrintStopwatchTime},
	{"CustomSkaterFilenameDefined",CFuncs::ScriptCustomSkaterFilenameDefined},
	{"GetCustomSkaterFilename",	CFuncs::ScriptGetCustomSkaterFilename},
	{"SetCustomSkaterFilename",	CFuncs::ScriptSetCustomSkaterFilename},
	{"CurrentSkaterIsPro",		CFuncs::ScriptCurrentSkaterIsPro},
	{"EditingPark",				CFuncs::ScriptEditingPark},
	{"GetParkName",				CFuncs::ScriptGetParkName},
	{"SetParkName",				CFuncs::ScriptSetParkName},
	{"ParkEditorThemeWasSwitched",CFuncs::ScriptParkEditorThemeWasSwitched},
	{"GetGoalsCompleted",		CFuncs::ScriptGetGoalsCompleted},
	{"GetNextLevelRequirements",CFuncs::ScriptGetNextLevelRequirements},
	{"MenuIsShown",				CFuncs::ScriptMenuIsShown},
	{"MenuIsSelected",			CFuncs::ScriptMenuIsSelected},

	{"GetMostRecentSave",		CFuncs::ScriptGetMostRecentSave},
	{"GetMemCardSpaceAvailable",CFuncs::ScriptGetMemCardSpaceAvailable},
	{"GetMemCardSpaceRequired",	CFuncs::ScriptGetMemCardSpaceRequired},
	{"MemCardFileExists",		CFuncs::ScriptMemCardFileExists},
	{"DeleteMemCardFile",		CFuncs::ScriptDeleteMemCardFile},
	{"FormatCard",				CFuncs::ScriptFormatCard},
	{"CardIsInSlot",			CFuncs::ScriptCardIsInSlot},
	{"SectorSizeOK",			CFuncs::ScriptSectorSizeOK},
	{"CardIsDamaged",			CFuncs::ScriptCardIsDamaged},
	{"CardIsForeign",			CFuncs::ScriptCardIsForeign},
	{"BadDevice",				CFuncs::ScriptBadDevice},
	{"GetSaveInfo",				CFuncs::ScriptGetSaveInfo},
	{"CreateTemporaryMemCardPools",CFuncs::ScriptCreateTemporaryMemCardPools},
	{"RemoveTemporaryMemCardPools",CFuncs::ScriptRemoveTemporaryMemCardPools},
	{"SwitchToTempPoolsIfTheyExist",CFuncs::ScriptSwitchToTempPoolsIfTheyExist},
	{"SwitchToRegularPools",	CFuncs::ScriptSwitchToRegularPools},
	{"CardIsFormatted",			CFuncs::ScriptCardIsFormatted},
	{"SaveFailedDueToInsufficientSpace",CFuncs::ScriptSaveFailedDueToInsufficientSpace},
	{"GetSummaryInfo",			CFuncs::ScriptGetSummaryInfo},
	{"SaveToMemoryCard",		CFuncs::ScriptSaveToMemoryCard},
	{"LoadFromMemoryCard",		CFuncs::ScriptLoadFromMemoryCard},
	{"LoadedCustomSkater",		CFuncs::ScriptLoadedCustomSkater},
	{"SetSectionsToApplyWhenLoading",CFuncs::ScriptSetSectionsToApplyWhenLoading},
	{"GetMemCardDataForUpload",	CFuncs::ScriptGetMemCardDataForUpload},
	{"ClearMemCardDataForUpload",CFuncs::ScriptClearMemCardDataForUpload},
	{"GetMaxTHPS4FilesAllowed",	CFuncs::ScriptGetMaxTHPS4FilesAllowed},
	{"GetMemCardDirectoryListing",CFuncs::ScriptGetMemCardDirectoryListing},
	{"ForEachIn",				CFuncs::ScriptForEachIn},
	{"SizeOf",					CFuncs::ScriptSizeOf},
	{"GetElement",				CFuncs::ScriptGetElement},
	{"GetNextArrayElement",		CFuncs::ScriptGetNextArrayElement},
	{"GetRandomArrayElement",	CFuncs::ScriptGetRandomArrayElement},
	{"PermuteArray",			CFuncs::ScriptPermuteArray},
	{"CreateIndexArray",		CFuncs::ScriptCreateIndexArray},
	{"GetGammaValues",			CFuncs::ScriptGetGammaValues},
	{"ApplyChangeGamma",		CFuncs::ScriptApplyChangeGamma},
	{"GotParam",				CFuncs::ScriptGotParam},
	{"Goto",					CFuncs::ScriptGoto},
	{"GotoPreserveParams",		CFuncs::ScriptGotoPreserveParams},
	{"GotoRandomScript",		CFuncs::ScriptGotoRandomScript},

	{"PreloadModel", CFuncs::ScriptPreloadModel},

	{"LaunchPanelMessage", 		CFuncs::ScriptDummyCommand},
	{"LaunchLocalPanelMessage", CFuncs::ScriptDummyCommand},
	{"KillMessages", 			CFuncs::ScriptDummyCommand},
	{"ChangeLocalMessage", 		CFuncs::ScriptDummyCommand},
	{"LaunchConsoleMessage", 	CFuncs::ScriptDummyCommand},
	//{"FireMenuEvent",			Front::ScriptFireMenuEvent},

	{"SetViewMode",				CFuncs::ScriptSetViewMode},
	
	{"ToggleRenderMode",		CFuncs::ScriptToggleRenderMode},
	{"SetRenderMode",			CFuncs::ScriptSetRenderMode},
	{"SetWireframeMode",		CFuncs::ScriptSetWireframeMode},
	{"ScreenShot",				CFuncs::ScriptScreenShot},
	{"DumpShots",				CFuncs::ScriptDumpShots},
	//{"SetStat",					CFuncs::ScriptSetStat},

	{"LoadFont", 				Nx::ScriptLoadFont},
	{"UnloadFont",				Nx::ScriptUnloadFont},

	{"LoadTexture", 			Nx::ScriptLoadTexture},
	{"UnloadTexture",			Nx::ScriptUnloadTexture},
	{"AddTextureToVram", 		Nx::ScriptAddTextureToVram},
	{"RemoveTextureFromVram",	Nx::ScriptRemoveTextureFromVram},
	{"LoadFaceTextureFromProfile", Nx::ScriptLoadFaceTextureFromProfile},
	{"Generate32BitImage",		Nx::ScriptGenerate32BitImage},
	{"OffsetTexture",			Nx::ScriptOffsetTexture},
    {"AdjustTextureRegion",		Nx::ScriptAdjustTextureRegion},
	{"PullTextureToEdge",		Nx::ScriptPullTextureToEdge},
	{"PushTextureToPoint",		Nx::ScriptPushTextureToPoint},
	{"AdjustTextureBrightness",	Nx::ScriptAdjustTextureBrightness},
	{"AdjustTextureHSV",		Nx::ScriptAdjustTextureHSV},
	{"CopyTexture",				Nx::ScriptCopyTexture},
	{"CombineTextures",			Nx::ScriptCombineTextures},
	{"LoadParticleTexture",		Nx::ScriptLoadParticleTexture},
	{"UnloadParticleTexture",	Nx::ScriptUnloadParticleTexture},
	{"LoadSFPTexture",			Nx::ScriptLoadSFPTexture},
	{"UnloadSFPTexture",		Nx::ScriptUnloadSFPTexture},
	{"DumpTextures",			Nx::ScriptDumpTextures},

	{"SetModelFaceTexturePoints", Nx::ScriptSetModelFaceTexturePoints},
	{"SetFaceMassageTextureOverlay", Nx::ScriptSetFaceMassageTextureOverlay},
	{"MassageFaceTexture",		Nx::ScriptMassageFaceTexture},
	{"AdjustFaceTextureToModel", Nx::ScriptAdjustFaceTextureToModel},
	{"CombineFaceTextureWithOverlay", Nx::ScriptCombineFaceTextureWithOverlay},

	{"SetLightAmbientColor",  	Nx::ScriptSetLightAmbientColor},
	{"SetLightDirection",		Nx::ScriptSetLightDirection},
	{"SetLightDiffuseColor",	Nx::ScriptSetLightDiffuseColor},
	{"PushWorldLights",			Nx::ScriptPushWorldLights},
	{"PopWorldLights",			Nx::ScriptPopWorldLights},

	{"SetColorBufferClear",		CFuncs::ScriptSetColorBufferClear},

	{"SetEventHandler",			CFuncs::ScriptSetEventHandler},	   
	{"ClearEventHandler",		CFuncs::ScriptClearEventHandler},
	{"ClearEventHandlerGroup",	CFuncs::ScriptClearEventHandlerGroup},
	{"OnExceptionRun",			CFuncs::ScriptOnExceptionRun},
	{"OnExitRun",				CFuncs::ScriptOnExitRun},
	
	{"SetUVWibbleParams",  		CFuncs::ScriptSetUVWibbleParams},
	{"EnableExplicitUVWibble",	CFuncs::ScriptEnableExplicitUVWibble},
	{"DisableExplicitUVWibble",	CFuncs::ScriptDisableExplicitUVWibble},
	{"SetUVWibbleOffsets",		CFuncs::ScriptSetUVWibbleOffsets},

	{"ToggleMetricItem",		CFuncs::ScriptToggleMetricItem},
	{"ToggleMemMetrics",		CFuncs::ScriptToggleMemMetrics},

	{"KillAllTextureSplats",	CFuncs::ScriptKillAllTextureSplats},
	
	{"PreloadModels",			CFuncs::ScriptPreloadModels},
	{"PreloadPedestrians",		CFuncs::ScriptPreloadPedestrians},
	{"PreselectRandomPedestrians",		CFuncs::ScriptPreselectRandomPedestrians},
	{"ReplaceCarTextures",		CFuncs::ScriptReplaceCarTextures},

	{"LoadSound",				CFuncs::ScriptLoadSound},
    // PlaySound "SoundName" Vol=100 Pitch=100
	{"PlaySound",				CFuncs::ScriptPlaySound},
	{"StopSound",				CFuncs::ScriptStopSound},
	{"StopAllSounds",			CFuncs::ScriptStopAllSounds},
	{"SetSoundParams",			CFuncs::ScriptSetSoundParams},
	{"IsSoundPlaying",			CFuncs::ScriptIsSoundPlaying},
	// 0-100 for zero to 100percent reverb level...
	{"SetSfxReverb",   			CFuncs::ScriptSetReverb},
	{"SetSfxVolume",   			CFuncs::ScriptSetVolume},
	{"SetSfxDropoff",			CFuncs::ScriptSetDropoff},
	{"LoadTerrainSounds",		CFuncs::ScriptLoadTerrainSounds},
	{"SetObjectColor",			CFuncs::ScriptSetObjectColor},
	{"SetSceneColor",			CFuncs::ScriptSetSceneColor},
	{"CompressVC",				CFuncs::ScriptCompressVC},
	{"NudgeVC",					CFuncs::ScriptNudgeVC},
	{"FakeLights",				CFuncs::ScriptFakeLights},
	{"CenterCamera",			CFuncs::ScriptCenterCamera},
	// music tracks commands:
	{"SetMusicVolume",			CFuncs::ScriptSetMusicVolume},
	{"SetMusicStreamVolume",	CFuncs::ScriptSetMusicStreamVolume},
	{"PlayTrack",				CFuncs::ScriptPlayTrack},
	{"PlayMusicStream",			CFuncs::ScriptPlayMusicStream},
	{"SkipMusicTrack", 			CFuncs::ScriptSkipMusicTrack},
	{"PauseMusic",				CFuncs::ScriptPauseMusic},
	{"StopMusic",				CFuncs::ScriptStopMusic},
	{"SetMusicMode",			CFuncs::ScriptSetMusicMode},
	{"SetRandomMode",			CFuncs::ScriptSetRandomMode},
	{"SetMusicLooping",			CFuncs::ScriptSetMusicLooping},
	{"LoadMusicHeader",			CFuncs::ScriptLoadMusicHeader},
	{"LoadStreamHeader",		CFuncs::ScriptLoadStreamHeader},
	{"StreamIsAvailable",		CFuncs::ScriptStreamIsAvailable}, // only can be used in the skateshop!
	// tracks added to the tracklist get played randomly during the game...
	{"AddMusicTrack",			CFuncs::ScriptAddMusicTrack},
	{"ChangeTrackState",		CFuncs::ScriptChangeTrackState},
    {"GetCurrentTrack",		    CFuncs::ScriptGetCurrentTrack},
	{"TrackEnabled",			CFuncs::ScriptTrackEnabled},
	{"MusicIsPaused",			CFuncs::ScriptMusicIsPaused},
	{"ClearMusicTrackList",		CFuncs::ScriptClearMusicTrackList},
	{"PlayStream",				CFuncs::ScriptPlayStream},
	{"StopStream",				CFuncs::ScriptStopStream},
	{"PauseStream",				CFuncs::ScriptPauseStream},
	{"SetStreamParams",			CFuncs::ScriptSetStreamParams},
	{"IsStreamPlaying",			CFuncs::ScriptIsStreamPlaying},
	{"LoadStreamFrameAmp",		CFuncs::ScriptLoadStreamFrameAmp},
	{"FreeStreamFrameAmp",		CFuncs::ScriptFreeStreamFrameAmp},
	{"PlayMovie",				CFuncs::ScriptPlayMovie},
	
	{"SetGameType",				Mdl::ScriptSetGameType},
	{"InTeamGame",				Mdl::ScriptInTeamGame},
	{"SetNumTeams",				GameNet::Manager::ScriptSetNumTeams},
	{"GetNumTeams",				GameNet::Manager::ScriptGetNumTeams},
	{"GetNumPlayersOnTeam",		GameNet::Manager::ScriptGetNumPlayersOnTeam},
	{"GetMyTeam",				GameNet::Manager::ScriptGetMyTeam},
	{"JoinTeam",				GameNet::Manager::ScriptJoinTeam},
	{"GetCollidingPlayerAndTeam", CFuncs::ScriptGetCollidingPlayerAndTeam},
    {"LobbyCheckKeyboard",      CFuncs::ScriptLobbyCheckKeyboard},
	{"TestGameType",			Mdl::ScriptTestGameType},
	{"TestRequestedGameType",	Mdl::ScriptTestRequestedGameType},
	{"ChangeLevel",				Mdl::ScriptChangeLevel},
	{"LaunchLevel",				Mdl::ScriptLaunchLevel},
	{"RequestLevel",			Mdl::ScriptRequestLevel},
	{"Retry",					Mdl::ScriptRetry},
	{"LaunchGame",				Mdl::ScriptLaunchGame},
	{"ResetLevel",				Mdl::ScriptResetLevel},
	{"FillRankingScreen",		Mdl::ScriptFillRankingScreen},
	{"InitSkaterHeaps",			Mdl::ScriptInitSkaterHeaps},

	{"NodeExists",				CFuncs::ScriptNodeExists},
	{"Create",					CFuncs::ScriptCreate},
	{"CreateFromStructure",		CFuncs::ScriptCreateFromStructure},
	{"Kill",					CFuncs::ScriptKill},
	{"Visible",					CFuncs::ScriptVisible},
	{"Invisible",				CFuncs::ScriptInvisible},
	{"Shatter",					CFuncs::ScriptShatter},

	{"ToggleSkaterCamMode",				CFuncs::ScriptToggleSkaterCamMode},
	{"SetSkaterCamLerpReductionTimer",	CFuncs::ScriptSetSkaterCamLerpReductionTimer},
	
	{"PlaySkaterCamAnim",				CFuncs::ScriptPlaySkaterCamAnim},
	{"SetSkaterCamAnimSkippable",		CFuncs::ScriptSetSkaterCamAnimSkippable},
	{"SetSkaterCamAnimShouldPause",		CFuncs::ScriptSetSkaterCamAnimShouldPause},
	{"SkaterCamAnimFinished",			CFuncs::ScriptSkaterCamAnimFinished},
	{"SkaterCamAnimHeld",				CFuncs::ScriptSkaterCamAnimHeld},
	{"KillSkaterCamAnim",				CFuncs::ScriptKillSkaterCamAnim},
	{"GetSkaterCamAnimParams",			CFuncs::ScriptGetSkaterCamAnimParams},
	{"GetCurrentSkaterCamAnimName",		CFuncs::ScriptGetCurrentSkaterCamAnimName},

	{"PlayMovingObjectAnim",		   	CFuncs::ScriptPlayMovingObjectAnim},
	{"SetMovingObjectAnimSkippable",	CFuncs::ScriptSetMovingObjectAnimSkippable},
	{"SetMovingObjectAnimShouldPause",	CFuncs::ScriptSetMovingObjectAnimShouldPause},
	{"MovingObjectAnimFinished",		CFuncs::ScriptMovingObjectAnimFinished},
	{"MovingObjectAnimHeld",			CFuncs::ScriptMovingObjectAnimHeld},
	{"KillMovingObjectAnim",			CFuncs::ScriptKillMovingObjectAnim},

	{"PlayCutscene",		   	CFuncs::ScriptPlayCutscene},
	{"IsMovieQueued",		   	CFuncs::ScriptIsMovieQueued},
	{"HasMovieStarted",		   	CFuncs::ScriptHasMovieStarted},
	
	// (most of the cutscene functions are actually the same
	// as the movingobjectanim-style functions)
	{"SetCutsceneSkippable",	CFuncs::ScriptSetSkaterCamAnimSkippable},
	{"SetCutsceneShouldPause",	CFuncs::ScriptSetSkaterCamAnimShouldPause},
	{"CutsceneFinished",		CFuncs::ScriptSkaterCamAnimFinished},
	{"CutsceneHeld",			CFuncs::ScriptSkaterCamAnimHeld},
	{"KillCutscene",			CFuncs::ScriptKillSkaterCamAnim},

	{"SkaterDebugOn",			CFuncs::ScriptSkaterDebugOn},
	{"SkaterDebugOff",			CFuncs::ScriptSkaterDebugOff},
	{"IfDebugOn",				CFuncs::ScriptIfDebugOn},
	{"IfDebugOff",				CFuncs::ScriptIfDebugOff},
	{"Bootstrap",				CFuncs::ScriptBootstrap},
	{"CD",						CFuncs::ScriptCD},
	{"NotCD",					CFuncs::ScriptNotCD},
	{"Gunslinger",				CFuncs::ScriptGunslinger},
	{"CasArtist",				CFuncs::ScriptCasArtist},
	{"NotCasArtist",			CFuncs::ScriptNotCasArtist},
	{"SendFlag",				CFuncs::ScriptSendFlag},
	{"ClearFlag",				CFuncs::ScriptClearFlag},
	{"QueryFlag",				CFuncs::ScriptQueryFlag},
	{"SendException",			CFuncs::ScriptFlagException},
	{"IsAlive",					CFuncs::ScriptCheckIfAlive},

	{"MakeSkaterGoto",			CFuncs::ScriptMakeSkaterGoto},
	{"MakeSkaterGosub",			CFuncs::ScriptMakeSkaterGosub},

	{"LaunchMenuScreen",		Mdl::ScriptLaunchMenuScreen},
	{"UseOnePadInFrontEnd",		CFuncs::ScriptDummyCommand},
	{"SetMenuAutoRepeatTimes",	Mdl::ScriptSetMenuAutoRepeatTimes},
	{"SetMenuPadMappings",		Mdl::ScriptSetMenuPadMappings},
	{"SetButtonEventMappings",	Mdl::ScriptSetButtonEventMappings},
	{"SetAnalogStickActiveForMenus",	Mdl::ScriptSetAnalogStickActiveForMenus},

	{"ControllerPressed",		CFuncs::ScriptControllerPressed},
	{"ControllerDebounce",		CFuncs::ScriptControllerDebounce},
	{"GetAnalogueInfo",			CFuncs::ScriptGetAnalogueInfo},

	{"SetScreenMode",			CFuncs::ScriptSetScreenMode},
	{"ScriptGetScreenMode",		Nx::ScriptGetScreenMode},
	{"SetScreenModeFromGameMode",	CFuncs::ScriptSetScreenModeFromGameMode},
	{"Set2DIn3DSpaceParams",		Nx::ScriptSet2DIn3DSpaceParams},
	

	{"SpawnScript",				CFuncs::ScriptSpawnScript},
	{"SpawnSound",				CFuncs::ScriptSpawnSound},
	{"SpawnSkaterScript",		CFuncs::ScriptSpawnSkaterScript},
	{"KillSpawnedScript",		CFuncs::ScriptKillSpawnedScript},
	{"PauseSkaters",			CFuncs::ScriptPauseSkaters},
	{"UnPauseSkaters",			CFuncs::ScriptUnPauseSkaters},
	{"PauseSkater",				CFuncs::ScriptPauseSkater},
	{"UnPauseSkater",			CFuncs::ScriptUnPauseSkater},

	{"PauseGame",				CFuncs::ScriptPauseGame},
	{"UnPauseGame",				CFuncs::ScriptUnPauseGame},
    {"GameIsPaused",			CFuncs::ScriptIsGamePaused},

	{"PauseObjects",			CFuncs::ScriptPauseObjects},
	{"UnPauseObjects",			CFuncs::ScriptUnPauseObjects},

	{"PauseSpawnedScripts",		CFuncs::ScriptPauseSpawnedScripts},
	{"UnPauseSpawnedScripts",	CFuncs::ScriptUnPauseSpawnedScripts},

	// clock related functions
	{"ResetClock",				CFuncs::ScriptResetClock},
	{"PadsPluggedIn",			CFuncs::ScriptPadsPluggedIn},
	{"DoFlash",					CFuncs::ScriptDoFlash},

	{"InPreFile",				File::ScriptInPreFile},
	{"LoadPreFile",				File::ScriptLoadPreFile},
	{"UnloadPreFile",			File::ScriptUnloadPreFile},
	{"IsLoadPreFinished",		File::ScriptIsLoadPreFinished},
	{"AllLoadPreFinished",		File::ScriptAllLoadPreFinished},
	{"WaitLoadPre",				File::ScriptWaitLoadPre},
	{"WaitAllLoadPre",			File::ScriptWaitAllLoadPre},
	{"LoadPipPre",				Pip::ScriptLoadPipPre},
	{"UnLoadPipPre",			Pip::ScriptUnloadPipPre},
	{"DumpPipPreStatus",		Pip::ScriptDumpPipPreStatus},
	{"StartServer",				CFuncs::ScriptStartServer},
	{"SpawnCrown",				GameNet::Manager::ScriptSpawnCrown},
	{"StartCTFGame",			GameNet::Manager::ScriptStartCTFGame},
	{"EndCTFGame",				GameNet::Manager::ScriptEndCTFGame},
	{"PlayerTookFlag",			GameNet::Manager::ScriptTookFlag},
	{"PlayerCapturedFlag",		GameNet::Manager::ScriptCapturedFlag},
	{"PlayerRetrievedFlag",		GameNet::Manager::ScriptRetrievedFlag},
	{"PlayerHasFlag",			GameNet::Manager::ScriptHasFlag},
    {"TeamFlagTaken",			GameNet::Manager::ScriptTeamFlagTaken},
	{"DisplayFlagBaseWarning",	GameNet::Manager::ScriptDisplayFlagBaseWarning},
	{"FindServers",				CFuncs::ScriptFindServers},
	{"FoundServers",			GameNet::Manager::ScriptFoundServers},
	{"JoinServer",				CFuncs::ScriptJoinServer},
	{"LeaveServer",				CFuncs::ScriptLeaveServer},
	{"SetNetworkMode",			CFuncs::ScriptSetNetworkMode},
	{"SetServerMode",			CFuncs::ScriptSetServerMode},
	{"StartNetworkGame",		GameNet::Manager::ScriptStartNetworkGame},
	{"NetworkGamePending",		GameNet::Manager::ScriptNetworkGamePending},
	{"EndNetworkGame", 			GameNet::Manager::ScriptEndNetworkGame},
	{"EnteredNetworkGame",		GameNet::Manager::ScriptEnteredNetworkGame},
	{"JustStartedNetGame",		GameNet::Manager::ScriptJustStartedNetGame},
	{"OverrideGameModeOptions",	Game::ScriptOverrideGameModeOptions},

	{"ResetCamera",				CFuncs::ScriptResetCamera},
	{"GetValueFromVolume",		CFuncs::ScriptGetValueFromVolume},
	{"SetSliderValue",			CFuncs::ScriptSetSliderValue},
	{"SetVolumeFromValue",		CFuncs::ScriptSetVolumeFromValue},
	
	{"ResetDefaultAppearance",	CFuncs::ScriptResetDefaultAppearance},
	{"ResetDefaultTricks",		CFuncs::ScriptResetDefaultTricks},
	{"ResetDefaultStats",		CFuncs::ScriptResetDefaultStats},
	{"RandomizeAppearance",		CFuncs::ScriptRandomizeAppearance},
	{"PrintCurrentAppearance",	CFuncs::ScriptPrintCurrentAppearance},
	{"SetNeversoftSkater",		CFuncs::ScriptSetNeversoftSkater},
	{"CurrentProfileIsLocked",	CFuncs::ScriptCurrentProfileIsLocked},
	{"SetIconTexture",			CFuncs::ScriptSetIconTexture},
	
	{"MemViewToggle",           CFuncs::ScriptMemViewToggle},

    {"EnableFog",				CFuncs::ScriptEnableFog},
    {"DisableFog",				CFuncs::ScriptDisableFog},
    {"SetFogDistance",			CFuncs::ScriptSetFogDistance},
    {"SetFogExponent",			CFuncs::ScriptSetFogExponent},
	{"SetFogColor",				CFuncs::ScriptSetFogColor},
	
	{"Change",					CFuncs::ScriptChangeSymbolValue},
	{"PreferenceEquals",		CFuncs::ScriptPreferenceEquals},
	{"SetUIFromPreferences",	CFuncs::ScriptSetUIFromPreferences},
	{"SetUIFromSkaterProfile",	CFuncs::ScriptSetUIFromSkaterProfile},
	{"SetPreference",			CFuncs::ScriptSetPreference},
	{"SetPreferencesFromUI",	CFuncs::ScriptSetPreferencesFromUI},
	{"GetPreferenceString",		CFuncs::ScriptGetPreferenceString},
	{"GetPreferencePassword",	CFuncs::ScriptGetPreferencePassword},
	{"GetPreferenceChecksum",	CFuncs::ScriptGetPreferenceChecksum},
	
	{"MemPushContext",			CFuncs::ScriptMemPushContext},
	{"MemPopContext",			CFuncs::ScriptMemPopContext},
	{"ProfileTasks",			CFuncs::ScriptProfileTasks},
	{"UseNetworkPreferences",	CFuncs::ScriptUseNetworkPreferences},
	{"CanChangeDevices",		CFuncs::ScriptCanChangeDevices},
	{"NeedToTestNetSetup",		CFuncs::ScriptNeedToTestNetSetup},
	{"TestNetSetup",			CFuncs::ScriptTestNetSetup},
	{"StopAllScripts",			CFuncs::ScriptStopAllScripts},
	{"SetMenuElementText",		CFuncs::ScriptSetMenuElementText},
	{"FirstTimeThisIsCalled",	CFuncs::ScriptFirstTimeThisIsCalled},
	{"VibrationIsOn",			CFuncs::ScriptVibrationIsOn},
	{"VibrationOff",			CFuncs::ScriptVibrationOff},
	{"VibrationOn",				CFuncs::ScriptVibrationOn},
	{"VibrateController",		CFuncs::ScriptVibrateController},
	{"EnableActuators",			CFuncs::ScriptEnableActuators},
	{"AutoKickIsOn",			CFuncs::ScriptAutoKickIsOn},
	{"AutoKickOn",				CFuncs::ScriptAutoKickOn},
	{"AutoKickOff",				CFuncs::ScriptAutoKickOff},
	{"SpinTapsAreOn",			CFuncs::ScriptSpinTapsAreOn},
	{"SpinTapsOn",				CFuncs::ScriptSpinTapsOn},
	{"SpinTapsOff",				CFuncs::ScriptSpinTapsOff},
	{"IsOnline",				CFuncs::ScriptIsOnline},
	{"ConnectToInternet",		CFuncs::ScriptConnectToInternet},
	{"CancelConnectToInternet",	CFuncs::ScriptCancelConnectToInternet},
	{"CancelLogon",				CFuncs::ScriptCancelLogon},
	{"DisconnectFromInternet",	CFuncs::ScriptDisconnectFromInternet},
	{"InNetGame",				CFuncs::ScriptInNetGame},
	{"DumpScripts",				CFuncs::ScriptDumpScripts},
	{"SetSlomo",				CFuncs::ScriptSetSlomo},
	{"SetArenaSize",			CFuncs::ScriptSetArenaSize},
	{"ToggleNetMetrics",		CFuncs::ScriptToggleNetMetrics },
	{"GetCurrentProDisplayInfo",CFuncs::ScriptGetCurrentProDisplayInfo},
	{"SetPlayerAppearance",		CFuncs::ScriptSetPlayerAppearance},
	{"SetPlayerFaceTexture",	CFuncs::ScriptSetPlayerFaceTexture},
    {"SetPlayerFaceOverlayTexture",	CFuncs::ScriptSetPlayerFaceOverlayTexture},
	{"ClearPlayerFaceTexture",	CFuncs::ScriptClearPlayerFaceTexture},
	{"GetPlayerFacePoints",		CFuncs::ScriptGetPlayerFacePoints},
	{"SetPlayerFacePoints",		CFuncs::ScriptSetPlayerFacePoints},
	{"PlayerFaceIsValid",		CFuncs::ScriptPlayerFaceIsValid},
	{"SelectCurrentSkater",		CFuncs::ScriptSelectCurrentSkater},
	{"SetParticleSysVisibility",CFuncs::ScriptSetParticleSysVisibility},
	{"TogglePlayerNames",		CFuncs::ScriptTogglePlayerNames},
	{"SetCurrentGameType",		CFuncs::ScriptSetCurrentGameType},
	{"DumpNetMessageStats",		CFuncs::ScriptDumpNetMessageStats},
	{"NotifyBailDone",			CFuncs::ScriptNotifyBailDone},
	{"ClearPowerups",			CFuncs::ScriptClearPowerups},
	{"BroadcastProjectile",		CFuncs::ScriptBroadcastProjectile},
	{"BroadcastEnterVehicle",	CFuncs::ScriptBroadcastEnterVehicle},

	// Loading screen
	{"DisplayLoadingScreen",	CFuncs::ScriptDisplayLoadingScreen},
	{"HideLoadingScreen",		CFuncs::ScriptHideLoadingScreen},

	// Loading bar of loading screen
	{"SetLoadingBarPos",		Nx::ScriptSetLoadingBarPos},
	{"SetLoadingBarSize",		Nx::ScriptSetLoadingBarSize},
	{"SetLoadingBarStartColor",	Nx::ScriptSetLoadingBarStartColor},
	{"SetLoadingBarEndColor",	Nx::ScriptSetLoadingBarEndColor},
	{"SetLoadingBarBorder",		Nx::ScriptSetLoadingBarBorder},
	{"SetLoadingBarBorderColor",Nx::ScriptSetLoadingBarBorderColor},

	// park editor, ya munks
	{"SetParkEditorTimeOfDay",	Ed::ScriptSetParkEditorTimeOfDay},
	{"GetParkEditorTimeOfDayScript",Ed::ScriptGetParkEditorTimeOfDayScript},
	{"RebuildParkNodeArray",	Ed::ScriptRebuildParkNodeArray},
	{"FreeUpMemoryForPlayingPark",Ed::ScriptFreeUpMemoryForPlayingPark},
	{"CalibrateMemoryGauge",	Ed::ScriptCalibrateMemoryGauge},
	{"GetParkEditorCursorPos",	Ed::ScriptGetParkEditorCursorPos},
	{"SwitchToParkEditorCamera",Ed::ScriptSwitchToParkEditorCamera},
	{"SetParkEditorState",		Ed::ScriptSetParkEditorState},
	{"SetParkEditorPauseMode",	Ed::ScriptSetParkEditorPauseMode},
	{"SetParkEditorMaxPlayers",	Ed::ScriptSetParkEditorMaxPlayers},
	{"GetParkEditorMaxPlayers",	Ed::ScriptGetParkEditorMaxPlayers},
	{"GetParkEditorMaxPlayersPossible",Ed::ScriptGetParkEditorMaxPlayersPossible},
	{"CustomParkMode",			Ed::ScriptCustomParkMode},
	{"CanCleanlyResizePark",	Ed::ScriptCanCleanlyResizePark},
	#ifdef __PLAT_NGC__
	{"WriteCompressedMapBuffer",Ed::ScriptWriteCompressedMapBuffer},
	{"RequiresDefragment",		Ed::ScriptRequiresDefragment},
	{"TopDownHeapTooLow",		Ed::ScriptTopDownHeapTooLow},
	#endif
	{"ResizePark",				Ed::ScriptResizePark},
	{"GetCurrentParkBounds",	Ed::ScriptGetCurrentParkBounds},
	{"CanChangeParkDimension", 	Ed::ScriptCanChangeParkDimension},
	{"SaveParkToDisk",			Ed::ScriptSaveParkToDisk},
	{"LoadParkFromDisk",		Ed::ScriptLoadParkFromDisk},
	{"IsParkUnsaved",			Ed::ScriptIsParkUnsaved},
	{"FireCustomParkGap",		Ed::ScriptFireCustomParkGap},
	{"SetEditedParkGapInfo",	Ed::ScriptSetEditedParkGapInfo},
	{"GetEditedParkGapName",	Ed::ScriptGetEditedParkGapName},
	{"ParkEditorSelectionAreaTooBigToCopy",Ed::ScriptParkEditorSelectionAreaTooBigToCopy},
	{"CopyParkEditorSelectionToClipboard",Ed::ScriptCopyParkEditorSelectionToClipboard},
	{"SwitchParkEditorMenuPieceToMostRecentClipboard",Ed::ScriptSwitchParkEditorMenuPieceToMostRecentClipboard},
	{"CutParkEditorAreaSelection",Ed::ScriptCutParkEditorAreaSelection},
	{"ParkEditorAreaSelectionDeletePieces",Ed::ScriptParkEditorAreaSelectionDeletePieces},
	{"ContinueParkEditorAreaSelection",	Ed::ScriptContinueParkEditorAreaSelection},
	{"GetEditorTheme",			Ed::ScriptGetEditorTheme},
	{"SetEditorTheme",			Ed::ScriptSetEditorTheme},
	{"GetEditorMaxThemes",		Ed::ScriptGetEditorMaxThemes},
	{"GetCustomParkName",		Ed::ScriptGetCustomParkName},
	{"SetCustomParkName",		Ed::ScriptSetCustomParkName},
	{"BindParkEditorToController",	Ed::ScriptBindParkEditorToController},
	{"ParkEditorCommand",		CFuncs::ScriptDummyCommand},
	{"ParkEditorGetGapName",	CFuncs::ScriptDummyCommand},
	{"ParkEditorSetGapName",	CFuncs::ScriptDummyCommand},
	{"IsCustomPark",			Ed::ScriptIsCustomPark},
	{"IsUnsavedPark",			CFuncs::ScriptDummyCommand},
	
	{"ObserveNextSkater",		CFuncs::ScriptObserveNextSkater},

	// Prints the current memory usage of the bottom up heap
	{"PrintMemInfo",			CFuncs::ScriptPrintMemInfo},
	{"DisplayFreeMem",      	CFuncs::ScriptDisplayFreeMem},
	{"AnalyzeHeap",				CFuncs::ScriptAnalyzeHeap},
	{"SetMemThreadSafe",		CFuncs::ScriptMemThreadSafe},
	
	{"CareerStartLevel",		CFuncs::ScriptCareerStartLevel},
	{"CareerLevelIs",			CFuncs::ScriptCareerLevelIs},
	{"CareerReset",				CFuncs::ScriptCareerReset},
	{"SetGoal",               	CFuncs::ScriptSetGoal},        
	{"UnSetGoal",               CFuncs::ScriptUnSetGoal},        
	{"GetGoal",               	CFuncs::ScriptGetGoal},        
	{"JustGotGoal",           	CFuncs::ScriptJustGotGoal},    
	{"SetFlag",               	CFuncs::ScriptSetFlag},        
	{"UnSetFlag",               CFuncs::ScriptUnSetFlag},        
	{"GetFlag",               	CFuncs::ScriptGetFlag},        
	{"JustGotFlag",           	CFuncs::ScriptJustGotFlag},
	{"SetScoreGoal",			CFuncs::ScriptSetScoreGoal},			
	{"ClearScoreGoals",			CFuncs::ScriptClearScoreGoals},			

	{"SetGlobalFlag",           CFuncs::ScriptSetGlobalFlag},        
	{"UnSetGlobalFlag",         CFuncs::ScriptUnSetGlobalFlag},        
	{"GetGlobalFlag",           CFuncs::ScriptGetGlobalFlag},        
	
	// Career-related functions...  these refer to skater 0,
	// so don't try to use these functions from a trick script.
	{"ProfileEquals",			CFuncs::ScriptProfileEquals},
	{"IsCareerMode",			CFuncs::ScriptIsCareerMode},
	{"EndRun",					CFuncs::ScriptEndRun},
	{"AllowPause",				CFuncs::ScriptAllowPause},

	{"EnterObserverMode",		CFuncs::ScriptEnterObserverMode},
	{"EnterSurveyorMode",		GameNet::Manager::ScriptEnterSurveyorMode},
	{"ExitSurveyorMode",		GameNet::Manager::ScriptExitSurveyorMode},
	
	{"RefreshServerList",		CFuncs::ScriptRefreshServerList},
	{"SetJoinMode",				GameNet::Manager::ScriptSetJoinMode},
	{"SetHostMode",				GameNet::Manager::ScriptSetHostMode},
	{"IsHost",					GameNet::Manager::ScriptIsHost},
	{"IsAutoServing",			GameNet::Manager::ScriptIsAutoServing},
	{"ChangeLevelPending",		GameNet::Manager::ScriptChangeLevelPending},
	{"ToggleProSet",			GameNet::Manager::ScriptToggleProSet},
	{"ResetProSetFlags",		GameNet::Manager::ScriptResetProSetFlags},
		
	// Some gameflow-related tests
	{"PauseGameFlow",			CFuncs::ScriptPauseGameFlow},
	{"UnpauseGameFlow",			CFuncs::ScriptUnpauseGameFlow},
	{"InFrontEnd",				CFuncs::ScriptInFrontEnd},
	{"GetFireballLevel",		CFuncs::ScriptGetFireballLevel},
	{"InSplitScreenGame",		CFuncs::ScriptInSplitScreenGame},
	{"GameModeEquals",			CFuncs::ScriptGameModeEquals},
	{"OnServer",				CFuncs::ScriptOnServer},
	{"ShouldEndRun",			CFuncs::ScriptShouldEndRun},
	{"InitializeSkaters",		CFuncs::ScriptInitializeSkaters},
	{"SkatersAreReady",			CFuncs::ScriptSkatersAreReady},
	{"RestartLevel",			CFuncs::ScriptRestartLevel},
	{"EndRunSelected",			CFuncs::ScriptEndRunSelected},
	{"AllSkatersAreIdle",		CFuncs::ScriptAllSkatersAreIdle},
	{"FirstTrickStarted",		CFuncs::ScriptFirstTrickStarted},
	{"FirstTrickCompleted",		CFuncs::ScriptFirstTrickCompleted},
	{"CalculateFinalScores",	CFuncs::ScriptCalculateFinalScores},
	{"ReinsertSkaters",			CFuncs::ScriptReinsertSkaters},
	{"UnhookSkaters",			CFuncs::ScriptUnhookSkaters},
	{"ToggleScores",			CFuncs::ScriptToggleScores},
	{"ApplySplitScreenOptions",	CFuncs::ScriptApplySplitScreenOptions},
	{"ApplyToSkaterProfile",	CFuncs::ScriptApplyToSkaterProfile},
	{"RefreshSkaterColors",		CFuncs::ScriptRefreshSkaterColors},
	{"RefreshSkaterScale",		CFuncs::ScriptRefreshSkaterScale},
	{"RefreshSkaterVisibility", CFuncs::ScriptRefreshSkaterVisibility},
	{"RefreshSkaterUV", 		CFuncs::ScriptRefreshSkaterUV},
	{"IsTrue",					CFuncs::ScriptIsTrue},
	{"InitSkaterModel",			CFuncs::ScriptInitSkaterModel},
	{"RefreshSkaterModel",		CFuncs::ScriptRefreshSkaterModel},
	{"EditPlayerAppearance",	CFuncs::ScriptEditPlayerAppearance},
	{"GetCurrentSkaterProfileIndex", CFuncs::ScriptGetCurrentSkaterProfileIndex},
    {"GetCustomSkaterName",     CFuncs::ScriptGetCustomSkaterName},

	{"GetCurrentLevel",			CFuncs::ScriptGetCurrentLevel},

	{"StartCompetition",		CFuncs::ScriptStartCompetition},
	{"EndCompetition",			CFuncs::ScriptEndCompetition},
	{"CompetitionEnded",		CFuncs::ScriptCompetitionEnded},
	{"StartCompetitionRun",		CFuncs::ScriptStartCompetitionRun},
	{"EndCompetitionRun",		CFuncs::ScriptEndCompetitionRun},

	{"StartHorse",				CFuncs::ScriptStartHorse},
	{"EndHorse",				CFuncs::ScriptEndHorse},
	{"HorseEnded",				CFuncs::ScriptHorseEnded},
	{"HorseStatusEquals",		CFuncs::ScriptHorseStatusEquals},
	{"StartHorseRun",			CFuncs::ScriptStartHorseRun},
	{"EndHorseRun",				CFuncs::ScriptEndHorseRun},
	{"SwitchHorsePlayers",		CFuncs::ScriptSwitchHorsePlayers},
	{"GetHorseString",			CFuncs::ScriptGetHorseString},
	{"IsCurrentHorseSkater",	CFuncs::ScriptIsCurrentHorseSkater},
	{"ApplyToHorsePanelString", CFuncs::ScriptApplyToHorsePanelString},
	
	{"XTriggered",				CFuncs::ScriptXTriggered},
	{"UsePad",					CFuncs::ScriptUsePad},

	{"InMultiplayerGame",		CFuncs::ScriptInMultiplayerGame},	
	{"IsTopJudge",				CFuncs::ScriptIsTopJudge},
	{"PlaceIs",					CFuncs::ScriptPlaceIs},
	{"RoundIs",					CFuncs::ScriptRoundIs},
	
	{"GameFlow",				CFuncs::ScriptGameFlow},	
	{"AwardStatPoint",			CFuncs::ScriptAwardStatPoint},
	{"AwardSpecialTrickSlot",	CFuncs::ScriptAwardSpecialTrickSlot},
	{"UpdateSkaterStats",		CFuncs::ScriptUpdateSkaterStats},
	
	{"GoalsGreaterThan",        CFuncs::ScriptGoalsGreaterThan},    
	{"GoalsEqualTo",            CFuncs::ScriptGoalsEqualTo},
	{"MedalsGreaterThan",       CFuncs::ScriptMedalsGreaterThan},   
	{"MedalsEqualTo",           CFuncs::ScriptMedalsEqualTo},       
	{"StartServerList",			CFuncs::ScriptStartServerList},
	{"StartLobbyList",			CFuncs::ScriptStartLobbyList},
	{"NumServersInLobby",		GameNet::Manager::ScriptGetNumServersInLobby},
#ifdef __PLAT_NGPS__
	{"NumPlayersInLobby",		GameNet::LobbyMan::ScriptGetNumPlayersInLobby},
	{"RejoinLobby",				GameNet::LobbyMan::ScriptRejoinLobby},
	{"ChooseLobby",				GameNet::LobbyMan::ScriptChooseLobby},
	{"LeaveLobby",				GameNet::LobbyMan::ScriptLeaveLobby},
	{"LobbyConnect",			GameNet::LobbyMan::ScriptLobbyConnect},
	{"LobbyDisconnect",			GameNet::LobbyMan::ScriptLobbyDisconnect},
	{"FillLobbyProspectiveBuddyList",	GameNet::LobbyMan::ScriptFillLobbyProspectiveBuddyList},
	{"StartNatNegotiation",		GameNet::LobbyMan::ScriptStartNatNegotiation},
	{"CancelNatNegotiation",	GameNet::LobbyMan::ScriptCancelNatNegotiation},
	
	{"CanHostGame",             GameNet::LobbyMan::ScriptCanHostGame},
	{"SendMessage",				GameNet::LobbyMan::ScriptSendMessage},
	{"SetQuietMode",			GameNet::LobbyMan::ScriptSetQuietMode},
	{"FillPlayerList",			GameNet::LobbyMan::ScriptFillPlayerList},
	{"LoadNetConfigs",			GameNet::Manager::ScriptLoadNetConfigs},
	{"NoNetConfigFiles",		GameNet::Manager::ScriptNoNetConfigFiles},
	{"FillNetConfigList",		GameNet::Manager::ScriptFillNetConfigList},
	{"ChooseNetConfig",			GameNet::Manager::ScriptChooseNetConfig},
	{"FillPlayerListMenu",		GameNet::Manager::ScriptFillPlayerListMenu},
	{"DownloadFace",			GameNet::ContentMan::ScriptDownloadFace},
	{"UploadFile",				GameNet::ContentMan::ScriptUploadFile},
	{"DownloadFile",			GameNet::ContentMan::ScriptDownloadFile},
	{"DownloadDirectoryList",	GameNet::ContentMan::ScriptDownloadDirectoryList},
	{"FreeDirectoryListing",	GameNet::ContentMan::ScriptFreeDirectoryListing},
	{"FillVaultMenu",			GameNet::ContentMan::ScriptFillVaultMenu},
	{"NextVaultCategory",		GameNet::ContentMan::ScriptNextVaultCategory},
	{"PrevVaultCategory",		GameNet::ContentMan::ScriptPrevVaultCategory},
	{"StatsLoggedIn",			GameNet::StatsMan::ScriptStatsLoggedIn},
	{"StatsLogIn",				GameNet::StatsMan::ScriptStatsLogIn},
	{"StatsLogOff",				GameNet::StatsMan::ScriptStatsLogOff},
	{"ReportStats",				GameNet::StatsMan::ScriptReportStats},
	{"RetrievePersonalStats",	GameNet::StatsMan::ScriptRetrievePersonalStats},
	{"RetrieveTopStats",		GameNet::StatsMan::ScriptRetrieveTopStats},
	{"NeedToRetrieveTopStats",	GameNet::StatsMan::ScriptNeedToRetrieveTopStats},
	{"FillStatsArrays",			GameNet::StatsMan::ScriptFillStatsArrays},
	{"GetRank",					GameNet::StatsMan::ScriptGetRank},
	{"CleanUpTopStats",			GameNet::StatsMan::ScriptCleanUpTopStats},
	{"ProfileLoggedIn",			GameNet::BuddyMan::ScriptProfileLoggedIn},
	{"SetUniqueId",				GameNet::BuddyMan::ScriptSetUniqueId},
	{"CreateProfile",			GameNet::BuddyMan::ScriptCreateProfile},
	{"ProfileLogIn",			GameNet::BuddyMan::ScriptProfileLogIn},
	{"ProfileLogOff",			GameNet::BuddyMan::ScriptProfileLogOff},
	{"FillBuddyList",			GameNet::BuddyMan::ScriptFillBuddyList},
	{"FillProspectiveBuddyList",GameNet::BuddyMan::ScriptFillProspectiveBuddyList},
	{"AddBuddy",				GameNet::BuddyMan::ScriptAddBuddy},
	{"RemoveBuddy",				GameNet::BuddyMan::ScriptRemoveBuddy},
	{"CancelAddBuddy",			GameNet::BuddyMan::ScriptCancelAddBuddy},
	{"JoinBuddy",				GameNet::BuddyMan::ScriptJoinBuddy},
	{"HasBuddies",				GameNet::BuddyMan::ScriptHasBuddies},
	{"BuddyListFull",			GameNet::BuddyMan::ScriptBuddyListFull},
	{"SetLobbyStatus",			GameNet::BuddyMan::ScriptSetLobbyStatus},
	{"PostGame",				GameNet::Manager::ScriptPostGame},
	{"AuthenticateClient",		GameNet::Manager::ScriptAuthenticateClient },
	{"WriteDNASBinary",			GameNet::Manager::ScriptWriteDNASBinary },
#endif
	{"ConnectedToPeer",			GameNet::Manager::ScriptConnectedToPeer },
	{"InGroupRoom",				GameNet::Manager::ScriptInGroupRoom},
	
	{"ChooseServer", 			GameNet::Manager::ScriptChooseServer},
	{"DescribeServer",			GameNet::Manager::ScriptDescribeServer},
	{"RetrieveServerInfo",		GameNet::Manager::ScriptRetrieveServerInfo},
	{"CancelJoinServer", 		GameNet::Manager::ScriptCancelJoinServer},
	{"ReattemptJoinServer",		GameNet::Manager::ScriptReattemptJoinServer},
	{"CreatePlayerOptions",     GameNet::Manager::ScriptCreatePlayerOptions},
	{"AllPlayersAreReady",      GameNet::Manager::ScriptAllPlayersAreReady},
	
	{"ChooseAccount",			GameNet::Manager::ScriptChooseAccount},
	{"RemovePlayer",			GameNet::Manager::ScriptRemovePlayer},
	{"CancelRemovePlayer",		GameNet::Manager::ScriptCancelRemovePlayer},
	{"KickPlayer",				GameNet::Manager::ScriptKickPlayer},
	{"BanPlayer",				GameNet::Manager::ScriptBanPlayer},
	{"FCFSRequestStartGame",	GameNet::Manager::ScriptFCFSRequestStartGame},
	{"FCFSRequestChangeLevel",	GameNet::Manager::ScriptFCFSRequestChangeLevel},
	{"FCFSRequestToggleProSet", GameNet::Manager::ScriptFCFSRequestToggleProSet},
	{"FCFSRequestToggleGoalSelection",	GameNet::Manager::ScriptFCFSRequestToggleGoalSelection},
	{"HasSignedDisclaimer",		GameNet::Manager::ScriptHasSignedDisclaimer},
	
	// "Omnigon" is code for "cheat". Just another attempt to NOT lead hackers directly to the
	// pile of gold
	{"ClearOmnigons",			CFuncs::ScriptClearCheats},
	{"BroadcastOmnigon",		CFuncs::ScriptBroadcastCheat},
	{"LastBroadcastedOmnigonWas",CFuncs::ScriptLastBroadcastedCheatWas},
	{"OmnigonAllowed",			CFuncs::ScriptCheatAllowed},

	{"JoinWithPassword",		CFuncs::ScriptJoinWithPassword},
	{"GetRecordText",			CFuncs::ScriptGetRecordText},
	{"UpdateRecords",			CFuncs::ScriptUpdateRecords},
	{"SendChatMessage",			CFuncs::ScriptSendChatMessage},
	{"InSlapGame",				CFuncs::ScriptInSlapGame},
	{"LoadPendingPlayers",		CFuncs::ScriptLoadPendingPlayers},
	{"DropPendingPlayers",		GameNet::Manager::ScriptDropPendingPlayers},
	{"StopServerList",			CFuncs::ScriptStopServerList},
	{"FreeServerList",			CFuncs::ScriptFreeServerList},
	{"LaunchQueuedScripts",     CFuncs::ScriptLaunchQueuedScripts},
	{"IsObserving",				CFuncs::ScriptIsObserving},
	{"GetInitialsString",		CFuncs::ScriptGetInitialsString},
	{"SetInitialsString",		CFuncs::ScriptSetInitialsString},
	{"UpdateInitials",			CFuncs::ScriptUpdateInitials},
	{"NewRecord",				CFuncs::ScriptNewRecord},
	{"AttachToSkater",			CFuncs::ScriptAttachToSkater},
	{"TryCheatString",			CFuncs::ScriptTryCheatString},
	{"LevelIs",					CFuncs::ScriptLevelIs},
	{"StartNetworkLobby",		CFuncs::ScriptStartNetworkLobby},
	{"ObserversAllowed",		CFuncs::ScriptObserversAllowed},
	{"NumPlayersAllowed",		CFuncs::ScriptNumPlayersAllowed},
	{"AutoDNS",					CFuncs::ScriptAutoDNS},
	{"UsingDefaultMasterServers",CFuncs::ScriptUsingDefaultMasterServers},
	{"UsingDHCP",				CFuncs::ScriptUsingDHCP},
	{"InInternetMode",			CFuncs::ScriptInInternetMode},
	{"LoadExecPS2",				CFuncs::ScriptLoadExecPS2},
	{"ExitDemo",				CFuncs::ScriptExitDemo},
	{"SendGameOverToObservers", GameNet::Manager::ScriptSendGameOverToObservers},
	{"GameIsOver", 				CFuncs::ScriptGameIsOver},
	{"EnteringNetGame",			CFuncs::ScriptEnteringNetGame},
	{"DeviceChosen", 			CFuncs::ScriptDeviceChosen},
	{"SetLevelName",			CFuncs::ScriptSetLevelName},
	{"TrickOffAllObjects",		CFuncs::ScriptTrickOffAllObjects},
	{"DumpHeaps",				CFuncs::ScriptDumpHeaps},
	{"DumpFragments",			CFuncs::ScriptDumpFragments},
	{"ResetPS2",				CFuncs::ScriptResetPS2},
	{"ResetHD",					CFuncs::ScriptResetHD},
	{"PAL",						CFuncs::ScriptPAL},
	{"English",					CFuncs::ScriptEnglish},
	{"TimeUp",					CFuncs::ScriptTimeUp},
	{"LaunchEvent",				Obj::ScriptLaunchEvent},
	{"FireEvent",				Obj::ScriptLaunchEvent},	// LaunchEvent and FireEvent are the same
	{"PrintEventLog",			Obj::ScriptPrintEventLog},
	{"ObjectExists",			Obj::ScriptObjectExists},
	{"TerminateObjectsScripts",	Obj::ScriptTerminateObjectsScripts},
	{"AssignAlias",				Obj::ScriptAssignAlias},
	{"SetObjectProps",			Obj::ScriptSetObjectProperties},
	{"CreateScreenElement",		Front::ScriptCreateScreenElement},
	{"DestroyScreenElement",	Front::ScriptDestroyScreenElement},
	{"RunScriptOnScreenElement",	Front::ScriptRunScriptOnScreenElement},
	{"SetScreenElementProps",	Front::ScriptSetScreenElementProps},
	{"GetScreenElementProps",	Front::ScriptGetScreenElementProps},
	{"DoScreenElementMorph",	Front::ScriptDoScreenElementMorph},
	{"SetScreenElementLock",	Front::ScriptSetScreenElementLock},
	{"ScreenElementSystemInit",		Front::ScriptScreenElementSystemInit},
	{"TextElementConcatenate",	Front::ScriptTextElementConcatenate},
	{"TextElementBackspace",	Front::ScriptTextElementBackspace},
	{"GetTextElementString",	Front::ScriptGetTextElementString},
	{"GetTextElementLength",	Front::ScriptGetTextElementLength},
	{"GetScreenElementDims", 	Front::ScriptGetScreenElementDims},
	{"GetScreenElementPosition", Front::ScriptGetScreenElementPosition},
	{"LaunchViewer",			CFuncs::ScriptLaunchViewer},
	{"LaunchScriptDebugger",	CFuncs::ScriptLaunchScriptDebugger},
	{"SetViewerModel",			CFuncs::ScriptSetViewerModel},
	{"SetViewerAnim",			CFuncs::ScriptSetViewerAnim},
	{"SetViewerLODDist",		CFuncs::ScriptSetViewerLODDist},
	{"ReloadViewerAnim",		CFuncs::ScriptReloadViewerAnim},
	{"ReloadSkaterCamAnim",		CFuncs::ScriptReloadSkaterCamAnim},
	{"SetScoreDegradation",		CFuncs::ScriptGameModeSetScoreDegradation},
	{"SetScoreAccumulation",	CFuncs::ScriptGameModeSetScoreAccumulation},
	{"ResetScore",				CFuncs::ScriptResetScore},
	{"ResetScorePot", 			CFuncs::ScriptResetScorePot},
	{"ResetScoreDegradation",	CFuncs::ScriptResetScoreDegradation},
	{"UpdateScore",				CFuncs::ScriptUpdateScore},
	{"AddRestartsToMenu",		CFuncs::ScriptAddRestartsToMenu},
	{"AddGapsToMenu",			Obj::ScriptAddGapsToMenu},
	{"CreateGapList",			Obj::ScriptCreateGapList},
	{"GetLevelGapTotals",		Obj::ScriptGetLevelGapTotals},
	{"GiveAllGaps",				Obj::ScriptGiveAllGaps},
    {"SetTimeLimit",            Mdl::ScriptSetTimeLimit},
	{"MenuSelectedIndexIs",		Front::ScriptMenuSelectedIndexIs},
	{"ScreenElementExists",		Front::ScriptScreenElementExists},
	{"SetRootScreenElement",	Front::ScriptSetRootScreenElement},

	// Goal-related stuff
	{"GoalManager_AddGoal",				Game::ScriptAddGoal},
	{"GoalManager_RemoveGoal",			Game::ScriptRemoveGoal},
	{"GoalManager_EditGoal",			Game::ScriptEditGoal},
	{"GoalManager_ActivateGoal",		Game::ScriptActivateGoal},
	{"GoalManager_DeactivateGoal",		Game::ScriptDeactivateGoal},
	{"GoalManager_ClearLastGoal",		Game::ScriptClearLastGoal},
	{"GoalManager_WinGoal",				Game::ScriptWinGoal},
	{"GoalManager_LoseGoal",			Game::ScriptLoseGoal},
	{"GoalManager_RemoveAllGoals",		Game::ScriptRemoveAllGoals},
	{"GoalManager_DeactivateAllGoals",	Game::ScriptDeactivateAllGoals},
	{"GoalManager_UninitializeAllGoals",Game::ScriptUninitializeAllGoals},
	{"GoalManager_UninitializeGoal",	Game::ScriptUninitializeGoal},
	{"GoalManager_InitializeAllGoals",	Game::ScriptInitializeAllGoals},
	{"GoalManager_InitializeGoal",		Game::ScriptInitializeGoal},
	{"GoalManager_GoalInitialized",		Game::ScriptGoalInitialized},
	{"GoalManager_InitializeAllSelectedGoals", Game::ScriptInitializeAllSelectedGoals},
	{"GoalManager_InitializeAllMinigames", Game::ScriptInitializeAllMinigames},
	{"GoalManager_DeactivateAllMinigames", Game::ScriptDeactivateAllMinigames},
	{"GoalManager_DeactivateCurrentGoal", Game::ScriptDeactivateCurrentGoal},
	{"GoalManager_UpdateAllGoals",		Game::ScriptUpdateAllGoals},
	{"GoalManager_HasActiveGoals",		Game::ScriptHasActiveGoals},
	{"GoalManager_GoalIsActive",		Game::ScriptGoalIsActive},
	{"GoalManager_SetGoalTimer",		Game::ScriptSetGoalTimer},
	{"GoalManager_ZeroGoalTimer",		Game::ScriptZeroGoalTimer},
	{"GoalManager_SetGoalFlag",			Game::ScriptSetGoalFlag},
    {"GoalManager_HasSeenGoal",         Game::ScriptHasSeenGoal},
    {"GoalManager_PauseAllGoals",       Game::ScriptPauseAllGoals},
    {"GoalManager_UnPauseAllGoals",     Game::ScriptUnPauseAllGoals},
    {"GoalManager_RestartLastGoal",     Game::ScriptRestartLastGoal},
    // {"GoalManager_CreateGoalFlag",      Game::ScriptCreateGoalFlag},
    {"GoalManager_AllFlagsSet",         Game::ScriptAllFlagsSet},
	{"GoalManager_GoalFlagSet",			Game::ScriptGoalFlagSet},
    {"GoalManager_CanRetryGoal",        Game::ScriptCanRetryGoal},      
    {"GoalManager_GetGoalParams",       Game::ScriptGetGoalParams},
    {"GoalManager_CanStartGoal",        Game::ScriptCanStartGoal},
    {"GoalManager_NextHorseSpot",       Game::ScriptNextTourSpot},
	{"GoalManager_NextTourSpot",		Game::ScriptNextTourSpot},
    {"GoalManager_NextRaceWaypoint",    Game::ScriptNextRaceWaypoint},
    {"GoalManager_CreateGoalName",      Game::ScriptCreateGoalName},
	{"GoalManager_GetLevelPrefix",		Game::ScriptGetLevelPrefix},
    {"GoalManager_SetGraffitiMode",     Game::ScriptSetGraffitiMode},
    {"GoalManager_HasWonGoal",          Game::ScriptHasWonGoal},
    {"GoalManager_GotCounterObject",    Game::ScriptGotCounterObject},
    {"GoalManager_CounterGoalDone",     Game::ScriptCounterGoalDone},
	{"GoalManager_ClearGoalPoints",		Game::ScriptClearGoalPoints},
    {"GoalManager_HasGoalPoints",       Game::ScriptHasGoalPoints},
    {"GoalManager_SpendGoalPoints",     Game::ScriptSpendGoalPoints},
    {"GoalManager_GetNumberOfGoalPoints", Game::ScriptGetNumberOfGoalPoints},
    {"GoalManager_GetCash",             Game::ScriptGetCash},
    {"GoalManager_SpendCash",           Game::ScriptSpendCash},
    {"GoalManager_AddCash",             Game::ScriptAddCash},
    {"GoalManager_HasBeatenGoalWithProset", Game::ScriptHasBeatenGoalWithProset},
    {"GoalManager_GetProsetNotPrefix",  Game::ScriptGetProsetNotPrefix},
    {"GoalManager_LevelUnload",         Game::ScriptLevelUnload},
    {"GoalManager_LevelLoad",           Game::ScriptLevelLoad},
    {"GoalManager_NumGoalsBeatenInLevel",  Game::ScriptNumGoalsBeatenInLevel},
    {"GoalManager_UnlockGoal",			Game::ScriptUnlockGoal},
	{"GoalManager_UnlockAllGoals",      Game::ScriptUnlockAllGoals},
	{"GoalManager_TurnPro",				Game::ScriptTurnPro},
	{"GoalManager_CheckMinigameRecord", Game::ScriptCheckMinigameRecord},
	{"GoalManager_SetStartTime",		Game::ScriptSetStartTime},
	{"GoalManager_UpdateComboTimer", 	Game::ScriptUpdateComboTimer},
	{"GoalManager_AddMinigameTime",		Game::ScriptAddMinigameTime},
	{"GoalManager_SetStartHeight", 		Game::ScriptSetStartHeight},
	{"GoalManager_CheckHeightRecord", 	Game::ScriptCheckHeightRecord},
	{"GoalManager_CheckDistanceRecord",	Game::ScriptCheckDistanceRecord},
/*	
	{"GoalManager_ShowGoalPoints", 		Game::ScriptShowGoalPoints},
	{"GoalManager_HideGoalPoints",		Game::ScriptHideGoalPoints},
	{"GoalManager_ShowPoints",			Game::ScriptShowPoints},
	{"GoalManager_HidePoints",			Game::ScriptHidePoints},
*/
	{"GoalManager_AddGoalPoint",		Game::ScriptAddGoalPoint},
	{"GoalManager_PlayGoalStartStream", Game::ScriptPlayGoalStartStream},
	{"GoalManager_PlayGoalWinStream", 	Game::ScriptPlayGoalWinStream},
	{"GoalManager_StopCurrentStream",	Game::ScriptStopCurrentStream},
	{"GoalManager_PlayGoalWaitStream",	Game::ScriptPlayGoalWaitStream},
	{"GoalManager_PauseGoal",			Game::ScriptPauseGoal},
	{"GoalManager_UnPauseGoal",			Game::ScriptUnPauseGoal},
	{"GoalManager_PauseCompetition",	Game::ScriptPauseCompetition},
	{"GoalManager_UnPauseCompetition",	Game::ScriptUnPauseCompetition},
	{"GoalManager_UnBeatAllGoals",		Game::ScriptUnBeatAllGoals},
	{"GoalManager_AddViewGoalsList",	Game::ScriptAddViewGoalsList},
	{"GoalManager_GoalIsLocked",		Game::ScriptGoalIsLocked},
	{"GoalManager_IsInCompetition",		Game::ScriptIsInCompetition},
	{"GoalManager_GetGoalAnimations",	Game::ScriptGetGoalAnimations},
	{"GoalManager_PlayGoalStream",		Game::ScriptPlayGoalStream},
	{"GoalManager_EndRunCalled",		Game::ScriptEndRunCalled},
	{"GoalManager_ClearEndRun",			Game::ScriptClearEndRun},
	{"GoalManager_FinishedEndOfRun",	Game::ScriptFinishedEndOfRun},
	{"GoalManager_StartedEndOfRun",		Game::ScriptStartedEndOfRun},
	{"GoalManager_SetShouldDeactivateOnExpire",	Game::ScriptSetShouldDeactivateOnExpire},
	{"GoalManager_EndBetAttempt",		Game::ScriptEndBetAttempt},
	{"GoalManager_StartBetAttempt",		Game::ScriptStartBetAttempt},
	{"GoalManager_WinBet",				Game::ScriptWinBet},
	{"GoalManager_MoveBettingGuyNow",	Game::ScriptMoveBettingGuyNow},
	{"GoalManager_BetOffered",			Game::ScriptBetOffered},
	{"GoalManager_BetAccepted",			Game::ScriptBetAccepted},
	{"GoalManager_BetRefused",			Game::ScriptBetRefused},
	{"GoalManager_BetIsActive",			Game::ScriptBetIsActive},
	{"GoalManager_AddTempSpecialTrick",	Game::ScriptAddTempSpecialTrick},
	{"GoalManager_RemoveTempSpecialTrick", Game::ScriptRemoveTempSpecialTrick},
	{"GoalManager_GetTrickFromKeyCombo", Game::ScriptGetTrickFromKeyCombo},
	{"GoalManager_QuickStartGoal",		Game::ScriptQuickStartGoal},
	{"GoalManager_AddGoalChoices",		Game::ScriptAddGoalChoices},
	{"GoalManager_GoalIsSelected",		Game::ScriptGoalIsSelected},
	{"GoalManager_ToggleGoalSelection",	Game::ScriptToggleGoalSelection},
	{"GoalManager_GoalsAreSelected",	Game::ScriptGoalsAreSelected},
	{"GoalManager_AddTime",				Game::ScriptAddTime},
	{"GoalManager_ReplaceTrickText",	Game::ScriptReplaceTrickText},
	// {"GoalManager_UnlockProSpecificChallenges",	Game::ScriptUnlockProSpecificChallenges},
	// {"GoalManager_ProSpecificChallengesUnlocked", Game::ScriptProSpecificChallengesUnlocked},
	{"GoalManager_GetNumberCollected",	Game::ScriptGetNumberCollected},
	{"GoalManager_GetNumberOfFlags",	Game::ScriptGetNumberOfFlags},
	{"GoalManager_IsPro",				Game::ScriptIsPro},
	{"GoalManager_ResetGoalFlags",		Game::ScriptResetGoalFlags},
	{"GoalManager_ColorTrickObjects",	Game::ScriptColorTrickObjects},
	{"GoalManager_GetNumberOfTimesGoalStarted", Game::ScriptGetNumberOfTimesGoalStarted},
	{"GoalManager_GoalExists",			Game::ScriptGoalExists},
	{"GoalManager_GetGoalAttemptInfo",	Game::ScriptGetGoalAttemptInfo},
	{"GoalManager_SetCanStartGoal",		Game::ScriptSetCanStartGoal},
	{"GoalManager_GetLastGoalId",		Game::ScriptGetLastGoalId},
	{"GoalManager_ClearTetrisTricks",	Game::ScriptClearTetrisTricks},
	// {"GoalManager_MarkProSpecificChallengeBeaten", Game::ScriptMarkProSpecificChallengeBeaten},
	// {"GoalManager_SkaterHasBeatenProSpecificChallenge", Game::ScriptSkaterHasBeatenProSpecificChallenge},
	{"GoalManager_AnnounceGoalStarted",	Game::ScriptAnnounceGoalStarted},
	{"GoalManager_SetEndRunType",		Game::ScriptSetEndRunType},
	{"GoalManager_GetActiveGoalId",		Game::ScriptGetActiveGoalId},
	{"GoalManager_ResetCareer",			Game::ScriptResetCareer},
	{"GoalManager_AwardMinigameCash",	Game::ScriptAwardMinigameCash},
	{"GoalManager_AwardAllGoalCash",	Game::ScriptAwardAllGoalCash},
	{"GoalManager_UpdateFamilyTrees",	Game::ScriptUpdateFamilyTrees},
	{"GoalManager_IsLeafNode",			Game::ScriptIsLeafNode},
	{"GoalManager_IsRootNode",			Game::ScriptIsRootNode},
	{"GoalManager_SuspendGoalPedLogic",	Game::ScriptSuspendGoalPedLogic},
	{"GoalManager_RememberLevelStructureName", Game::ScriptRememberLevelStructureName},
	{"GoalManager_SetDifficultyLevel",	Game::ScriptSetDifficultyLevel},
	{"GoalManager_GetDifficultyLevel",	Game::ScriptGetDifficultyLevel},
	// {"GoalManager_GetGoalParam",		Game::ScriptGetGoalParam},
	{"GoalManager_RestartStage",		Game::ScriptRestartStage},
	{"GoalManager_CanRestartStage",		Game::ScriptCanRestartStage},
	{"GoalManager_SetGoalChaptersAndStages", Game::ScriptSetGoalChaptersAndStages},
	{"GoalManager_AdvanceStage", 		Game::ScriptAdvanceStage},
	{"GoalManager_GetCurrentChapterAndStage", Game::ScriptGetCurrentChapterAndStage},
	{"GoalManager_SetCurrentChapterAndStage", Game::ScriptSetCurrentChapterAndStage},
	{"GoalManager_FilmGoalCheckpoint",	Game::ScriptFilmGoalCheckpoint},
	{"GoalManager_StartFilming",		Game::ScriptStartFilming},
	{"GoalManager_GoalShouldExpire",	Game::ScriptGoalShouldExpire},
	{"GoalManager_SetSponsor",			Game::ScriptSetSponsor},
	{"GoalManager_GetSponsor",			Game::ScriptGetSponsor},
	{"GoalManager_SetTeamMember",		Game::ScriptSetTeamMember},
	{"GoalManager_KillTeamMembers",		Game::ScriptKillTeamMembers},
    {"GoalManager_SetTeamName", 		Game::ScriptSetTeamName},
	{"GoalManager_GetTeam",				Game::ScriptGetTeam},
	{"GoalManager_RunLastStageScript",	Game::ScriptRunLastStageScript},
	{"GoalManager_UnloadLastFam",		Game::ScriptUnloadLastFam},
	{"GoalManager_StopLastStream",		Game::ScriptStopLastSream},
	{"GoalManager_HideAllGoalPeds",		Game::ScriptHideAllGoalPeds},

    {"GetCreateATrickParams",	        Game::ScriptGetCreateATrickParams},
    {"GetCreateATrickOtherParams",      Game::ScriptGetCreateATrickOtherParams},
    {"SetCreateATrickParams",	        Game::ScriptSetCreateATrickParams},
    {"SetCreateATrickOtherParams",      Game::ScriptSetCreateATrickOtherParams},
    {"SetCreateATrickRotations",        Game::ScriptSetCreateATrickRotations},
    {"SetCreateATrickAnimations",       Game::ScriptSetCreateATrickAnimations},
    {"GetCreateATrickRotations",        Game::ScriptGetCreateATrickRotations},
    {"GetCreateATrickAnimations",       Game::ScriptGetCreateATrickAnimations},

    {"CAT_SetNumAnims",                 Game::ScriptCAT_SetNumAnims},
    {"CAT_GetNumAnims",                 Game::ScriptCAT_GetNumAnims},
    {"CAT_SetAnimsDone",                Game::ScriptCAT_SetAnimsDone},
    {"CAT_GetAnimsDone",                Game::ScriptCAT_GetAnimsDone},
    {"CAT_SetRotsDone",                 Game::ScriptCAT_SetRotsDone},
    {"CAT_GetRotsDone",                 Game::ScriptCAT_GetRotsDone},
    {"CAT_SetBailDone",                 Game::ScriptCAT_SetBailDone},
    {"CAT_GetBailDone",                 Game::ScriptCAT_GetBailDone},
    {"CAT_SetFlipSkater",               Game::ScriptCAT_SetFlipSkater},
    {"CAT_GetFlipSkater",               Game::ScriptCAT_GetFlipSkater},
    {"CAT_SetHoldTime",                 Game::ScriptCAT_SetHoldTime},
    {"CAT_GetHoldTime",                 Game::ScriptCAT_GetHoldTime},
    {"CAT_SetTotalX",                   Game::ScriptCAT_SetTotalX},
    {"CAT_GetTotalX",                   Game::ScriptCAT_GetTotalX},
    {"CAT_SetTotalY",                   Game::ScriptCAT_SetTotalY},
    {"CAT_GetTotalY",                   Game::ScriptCAT_GetTotalY},
    {"CAT_SetTotalZ",                   Game::ScriptCAT_SetTotalZ},
    {"CAT_GetTotalZ",                   Game::ScriptCAT_GetTotalZ},
    
    {"AddWarpPointsToMenu",             CFuncs::ScriptAddWarpPointsToMenu},
	{"RunScriptOnObject",   			CFuncs::ScriptRunScriptOnObject},
	{"RunScriptOnComponentType",		CFuncs::ScriptRunScriptOnComponentType},

	// some asset manager functions
	{"LoadAsset",						CFuncs::ScriptLoadAsset},
    {"LoadAnim",						CFuncs::ScriptLoadAnim},
    {"LoadSkeleton",					CFuncs::ScriptLoadSkeleton},
    {"UnloadAnim",						CFuncs::ScriptUnloadAnim},
	{"SetDefaultPermanent",				CFuncs::ScriptAssManSetDefaultPermanent},
	{"SetReferenceChecksum",			CFuncs::ScriptAssManSetReferenceChecksum},
    
    {"SkaterIsBraking",                 CFuncs::ScriptSkaterIsBraking},
	{"LocalSkaterExists",				CFuncs::ScriptLocalSkaterExists},
	{"DownloadMotd",					GameNet::Manager::ScriptDownloadMotd},
	{"AlreadyGotMotd",					GameNet::Manager::ScriptAlreadyGotMotd},
	{"AlreadySignedIn",					GameNet::Manager::ScriptAlreadySignedIn},
	
	{"SignOut",							GameNet::Manager::ScriptSignOut},
	{"JoinServerComplete",				GameNet::Manager::ScriptJoinServerComplete},
	{"OnXbox",							CFuncs::ScriptOnXbox },
	{"SystemLinkEnabled",				CFuncs::ScriptSystemLinkEnabled },
	{"GotoXboxDashboard",				CFuncs::ScriptGotoXboxDashboard },

	{"CreateParticleSystem",			CFuncs::ScriptCreateParticleSystem },
	{"SetScript",						CFuncs::ScriptSetScript },
	{"DestroyParticleSystem",			CFuncs::ScriptDestroyParticleSystem },
	{"EmptyParticleSystem",				CFuncs::ScriptEmptyParticleSystem },
	{"ParticleExists",					CFuncs::ScriptParticleExists },

	{"WeatherUpdateGrid",				Nx::CEngine::ScriptWeatherUpdateGrid },
	{"WeatherSetRainHeight",			Nx::CEngine::ScriptWeatherSetRainHeight },
	{"WeatherSetRainFrames",			Nx::CEngine::ScriptWeatherSetRainFrames },
	{"WeatherSetRainLength",			Nx::CEngine::ScriptWeatherSetRainLength },
	{"WeatherSetRainBlendMode",			Nx::CEngine::ScriptWeatherSetRainBlendMode },
	{"WeatherSetRainRate",				Nx::CEngine::ScriptWeatherSetRainRate },
    {"WeatherSetRainColor",				Nx::CEngine::ScriptWeatherSetRainColor},

	{"WeatherSetSplashRate",			Nx::CEngine::ScriptWeatherSetSplashRate },
	{"WeatherSetSplashLife",			Nx::CEngine::ScriptWeatherSetSplashLife },
	{"WeatherSetSplashSize",			Nx::CEngine::ScriptWeatherSetSplashSize },
	{"WeatherSetSplashColor",			Nx::CEngine::ScriptWeatherSetSplashColor },
	{"WeatherSetSplashBlendMode",		Nx::CEngine::ScriptWeatherSetSplashBlendMode },

	{"WeatherSetSnowHeight",			Nx::CEngine::ScriptWeatherSetSnowHeight },
	{"WeatherSetSnowFrames",			Nx::CEngine::ScriptWeatherSetSnowFrames },
	{"WeatherSetSnowSize",				Nx::CEngine::ScriptWeatherSetSnowSize },
	{"WeatherSetSnowBlendMode",			Nx::CEngine::ScriptWeatherSetSnowBlendMode },
	{"WeatherSetSnowRate",				Nx::CEngine::ScriptWeatherSetSnowRate },
    {"WeatherSetSnowColor",				Nx::CEngine::ScriptWeatherSetSnowColor},

    {"WeatherSetSnowActive",			Nx::CEngine::ScriptWeatherSetSnowActive},
    {"WeatherSetRainActive",			Nx::CEngine::ScriptWeatherSetRainActive},

	{"StructureContains",				CFuncs::ScriptStructureContains},
	{"GetBonePosition",					CFuncs::ScriptGetBonePosition},
	{"ShouldEmitParticles",				CFuncs::ScriptShouldEmitParticles},
	{"ParticlesOn",						CFuncs::ScriptParticlesOn},
	{"ParticlesOff",					CFuncs::ScriptParticlesOff},
	{"MangleChecksums",					CFuncs::ScriptMangleChecksums},
	{"AppendSuffixToChecksum",			CFuncs::ScriptAppendSuffixToChecksum},
	
	{"RotateVector",					CFuncs::ScriptRotateVector},

	{"IsPS2",							CFuncs::ScriptIsPS2},
	{"IsNGC",							CFuncs::ScriptIsNGC},
	{"IsXBOX",							CFuncs::ScriptIsXBOX},
	{"IsWIN32",							CFuncs::ScriptIsWIN32},
	{"GetPlatform",						CFuncs::ScriptGetPlatform},
	{"IsPal",							CFuncs::ScriptIsPal},

	{"PrintSkaterStats",				CFuncs::ScriptPrintSkaterStats},
    {"PrintSkaterStats2",				CFuncs::ScriptPrintSkaterStats2},
	{"PrintSkaterPosition",				CFuncs::ScriptPrintSkaterPosition},
	{"GetSkaterID",						CFuncs::ScriptGetSkaterID},
	{"GetCurrentSkaterID",				CFuncs::ScriptGetCurrentSkaterID},
	{"GetViewerObjectID",				CFuncs::ScriptGetViewerObjectID},
	
	{"PushMemProfile",					CFuncs::ScriptPushMemProfile},
	{"PopMemProfile",					CFuncs::ScriptPopMemProfile},
	{"TogglePass",						CFuncs::ScriptTogglePass},
	
	{"GetStatValue",					CFuncs::ScriptGetStatValue},
	{"GetNumStatPointsAvailable",		CFuncs::ScriptGetNumStatPointsAvailable},
	{"UnlockSkater",					CFuncs::ScriptUnlockSkater},
	{"SetSkaterProfileInfo",			CFuncs::ScriptSetSkaterProfileInfo},
	{"GetSkaterProfileInfo",			CFuncs::ScriptGetSkaterProfileInfo},
	{"GetSkaterProfileInfoByName",		CFuncs::ScriptGetSkaterProfileInfoByName},
	{"SetSkaterProfileProperty",		CFuncs::ScriptSetSkaterProfileProperty},
	{"SetSkaterProfileInfoByName",		CFuncs::ScriptSetSkaterProfileInfoByName},
	{"SetScreen",						CFuncs::ScriptSetScreen},

	{"GetUpperCaseString",				CFuncs::ScriptGetUpperCaseString},

	{"GetGameMode",						CFuncs::ScriptGetGameMode},
	{"StartKeyboardHandler",			CFuncs::ScriptStartKeyboardHandler},
	{"StopKeyboardHandler",				CFuncs::ScriptStopKeyboardHandler},
	{"EnableKeyboard",					CFuncs::ScriptEnableKeyboard},
	{"DisableKeyboard",					CFuncs::ScriptDisableKeyboard},
	
	{"GetRescaledTargetValue",			CFuncs::ScriptGetRescaledTargetValue},
	{"MemInitHeap",						CFuncs::ScriptMemInitHeap},
	{"MemDeleteHeap",					CFuncs::ScriptMemDeleteHeap},
	{"ClearStruct",						CFuncs::ScriptClearStruct},
	{"AppendStruct",					CFuncs::ScriptAppendStruct},
	{"ScriptExists",					CFuncs::ScriptScriptExists},
	{"GetPlayerAppearancePart",			CFuncs::ScriptGetPlayerAppearancePart},
	{"GetActualCASOptionStruct",		CFuncs::ScriptGetActualCASOptionStruct},
	{"GetActualPlayerAppearancePart",	CFuncs::ScriptGetActualPlayerAppearancePart},
	{"SetPlayerAppearanceColor",		CFuncs::ScriptSetPlayerAppearanceColor},
	{"SetPlayerAppearanceScale",		CFuncs::ScriptSetPlayerAppearanceScale},
	{"SetPlayerAppearanceUV",			CFuncs::ScriptSetPlayerAppearanceUV},
	{"FlushDeadObjects",				CFuncs::ScriptFlushDeadObjects},

	{"BindTrickToKeyCombo",				CFuncs::ScriptBindTrickToKeyCombo},
	{"GetKeyComboBoundToTrick",			CFuncs::ScriptGetKeyComboBoundToTrick},
	{"UpdateTrickMappings",				CFuncs::ScriptUpdateTrickMappings},
	{"GetConfigurableTricksFromType",	CFuncs::ScriptGetConfigurableTricksFromType},
	{"GetTrickDisplayText",				CFuncs::ScriptGetTrickDisplayText},
	{"GetSpecialTrickInfo",				CFuncs::ScriptGetSpecialTrickInfo},
	{"GetTrickType",					CFuncs::ScriptGetTrickType},
	{"TrickIsLocked",					CFuncs::ScriptTrickIsLocked},

	{"SpawnSecondControllerCheck",		CFuncs::ScriptSpawnSecondControllerCheck},
	{"StopSecondControllerCheck",		CFuncs::ScriptStopSecondControllerCheck},

	{"GetIndexOfItemContaining",		CFuncs::ScriptGetIndexOfItemContaining},
	{"ForEachSkaterName",				CFuncs::ScriptForEachSkaterName},
	{"ForEachSkaterProfile",			CFuncs::ScriptForEachSkaterProfile},
	{"ResetAllToDefaultStats",			CFuncs::ScriptResetAllToDefaultStats},
	{"ResetAllToDefaultProfile",		CFuncs::ScriptResetAllToDefaultProfile},
	{"ResetToDefaultProfile",			CFuncs::ScriptResetToDefaultProfile},
	{"GetLevelRecords",					CFuncs::ScriptGetLevelRecords},

	{"ResetComboRecords",				CFuncs::ScriptResetComboRecords},

	{"IsArray",							CFuncs::ScriptIsArray},

	{"GetNumberOfTrickOccurrences",		CFuncs::ScriptGetNumberOfTrickOccurrences},

	{"GetNumSoundtracks",				CFuncs::ScriptGetNumSoundtracks},
	{"GetSoundtrackName",				CFuncs::ScriptGetSoundtrackName},
	{"UseUserSoundtrack",				CFuncs::ScriptUseUserSoundtrack},
	{"UseStandardSoundtrack",			CFuncs::ScriptUseStandardSoundtrack},
	
	{"DisableReset",					CFuncs::ScriptDisableReset},
	{"EnableReset",						CFuncs::ScriptEnableReset},
	{"ResetToIPL",						CFuncs::ScriptResetToIPL},

	{"BindControllerToSkater",			CFuncs::ScriptBindControllerToSkater},
	{"BindFrontEndToController",		CFuncs::ScriptBindFrontEndToController},
	{"ControllerBoundToDifferentSkater",CFuncs::ScriptControllerBoundToDifferentSkater},
	{"ControllerBoundToSkater",			CFuncs::ScriptControllerBoundToSkater},

	{"GetKeyComboArrayFromTrickArray",	CFuncs::ScriptGetKeyComboArrayFromTrickArray},
	{"InitAnimCompressTable",			CFuncs::ScriptInitAnimCompressTable},

	{"FirstInputReceived",				CFuncs::ScriptFirstInputReceived},
	{"LockCurrentSkaterProfileIndex",	CFuncs::ScriptLockCurrentSkaterProfileIndex},

	{"SetSpecialTrickInfo",				CFuncs::ScriptSetSpecialTrickInfo},
    {"GetSkaterPosition",				CFuncs::ScriptGetSkaterPosition},
	{"GetSkaterVelocity",				CFuncs::ScriptGetSkaterVelocity},
	{"InterpolateParameters",			CFuncs::ScriptInterpolateParameters},
	{"GetLightCurrentColor",			Nx::ScriptGetLightCurrentColor},
    {"DrawDirectionalLightLines",		Nx::ScriptDrawDirectionalLightLines},

    {"CreateCompositeObject",			CFuncs::ScriptCreateCompositeObject},
	
	{"AutoRail",						CFuncs::ScriptAutoRail},
	
    {"Inside",           				CFuncs::ScriptInside},

    {"SetSpecialBarColors",           	CFuncs::ScriptSetSpecialBarColors},
	{"GetMetrics",						CFuncs::ScriptGetMetrics},
	{"MoveNode",						CFuncs::ScriptMoveNode},
	{"SetActiveCamera",					CFuncs::ScriptSetActiveCamera},

    {"Sin",                           	CFuncs::ScriptSin},
    {"Cos",                           	CFuncs::ScriptCos},
    {"Tan",                           	CFuncs::ScriptTan},
    {"ASin",                           	CFuncs::ScriptASin},
    {"ACos",                           	CFuncs::ScriptACos},
    {"ATan",                           	CFuncs::ScriptATan},
	
	{"ShowTracking",					CFuncs::ScriptShowTracking},
	
	{"IsGrind",							CFuncs::ScriptIsGrind},
	{"ShowCamOffset",					CFuncs::ScriptShowCamOffset},
	
	{"PlaySkaterStream",				CFuncs::ScriptPlaySkaterStream},

    {"GetTextureFromPath",              CFuncs::ScriptGetTextureFromPath},
    {"GetVramUsage",              CFuncs::ScriptGetVramUsage},
	{"CompositeObjectExists",			CFuncs::ScriptCompositeObjectExists},	
	
	{"DoNextTrick",					Obj::ScriptDoNextTrick},

	{"AllocatePathManMemory",		Obj::ScriptAllocatePathManMemory},


	{"AddSkaterEarly",					CFuncs::ScriptAddSkaterEarly},

	{"PreLoadStreamDone",				CFuncs::ScriptPreLoadStreamDone},
	{"StartPreLoadedStream",			CFuncs::ScriptStartPreLoadedStream},
	{"FinishRendering",					CFuncs::ScriptFinishRendering},

// Gamecube-specific message functions.
#ifdef __PLAT_NGC__
	{"Ngc_BGColor", 					Nx::ScriptNgc_BGColor},
	{"Ngc_Message", 					Nx::ScriptNgc_Message},
	{"Ngc_Menu", 						Nx::ScriptNgc_Menu},
	{"Ngc_Set480P",						Nx::ScriptNgc_Set480P},
	{"Ngc_Set480I",						Nx::ScriptNgc_Set480I},
	{"Ngc_SetWide",						Nx::ScriptNgc_SetWide},
	{"Ngc_SetStandard",					Nx::ScriptNgc_SetStandard},
	{"Ngc_ReduceColors",				Nx::ScriptNgc_ReduceColors},
	{"Ngc_Set60hz",						Nx::ScriptNgc_Set60Hz},
	{"Ngc_Set50hz",						Nx::ScriptNgc_Set50Hz},
#endif		// __PLAT_NGC__
};

// A list of all the member function names accessible from scripts.
// The name in quotes must be the same as the way it appears in q scripts. (case insensitive)
const char *ppMemberFunctionNames[]=
{
	// used by the asset manager (AssMan)	
    "AnimLoaded",
    "AssetLoaded",

	// used by Gfx::Camera
	"ChangeCameraFOV",

	// used by Obj::CMovieCamera
	"SetTargetObject",
	"ClearTargetObject",
	 
	// used by Gfx::CModelAppearance
	"SetPart",
	"GetPart",
	"ClearPart",
	"SetChecksum",
	"PartGotFlag",
	"GotPart",
	
	// used by Gfx::CModelBuilder
	"GeomModulateColor",
	"GeomSetUVOffset",
	"GeomAllocateUVMatrixParams",
	"GeomReplaceTexture",
	"ModelAddGeom",
	"ModelHideGeom",
	"ModelRemovePolys",
	"ModelFinalize",
    "ModelResetScale",
    "ModelApplyBoneScale",
    "ModelApplyBodyShape",
    "ModelApplyObjectScale",
	"ModelApplyFaceTexture",
	"ModelRunScript",
	"ModelClearGeom",
	"ModelClearAllGeoms",
	"DebugPrintAppearance",
	"AppearanceAllowScalingCheat",

	// used by Obj::CSkater
	"GetStat",
	"GetScriptedStat",
	"SetCustomRestart",
	"SkipToCustomRestart",
	"PausePhysics",
	"UnPausePhysics",
	"SetDriving",
	"UnsetDriving",
	"GetTimeSincePhysicsSwitch",
	"GetPreviousPhysicsStateDuration",
	"DumpEventBuffer",
	"AnimEquals",
	"PlayAnim",
	"BlendPeriodOut",
	"LoopingAnim",
	"AnimFinished",
	"LeftPressed",
	"RightPressed",
	"UpPressed",
	"DownPressed",
	"Jump",
	"Flipped",
	"Switched",
	"Crouched",
	"OnGround",
	"InAir",
	"OnWall",
	"OnLip",
	"OnRail",
	"InWallplant",
	"FirstTimeOnThisRail",
	"InBail",
	"NotInBail",
	"IsInBail",
	"PlayBonkSound",
	"PlayCessSound",
	"PlayLandSound",
	"PlayJumpSound",
	"SpeedLessThan",
	"SpeedGreaterThan",
	"SpeedEquals",
	"Braking",
	"CanBrakeOff",
	"CanBrakeOn",
	"CanKick",
	"CanKickOn",
	"CanKickOff",
	"ForceAutokickOn",
	"ForceAutokickOff",
	"RestoreAutokick",
	"AutoKickIsOff",
	"DoCarPlantBoost",
	"GetAnimLength",
    "Obj_GetAnimSpeed",
	"WaitAnim",
	"FrameIs",
	"BashOn",
	"BashOff",
	"SetTrickName",
	"SetTrickScore",
	"GetSpin",
	"Display",
	"HandleLipOllieDirection",
	"EnableDisplayFlip",
	"DisableDisplayFlip",
	"Flip",
	"ResetSwitched",
	"GetHeading",
	"Rotate",
	"RotateDisplay",
	"CancelRotateDisplay",
	"BoardRotate",
	"NoSpin",
	"CanSpin",
	"Move",
	"Hide",
	"Unhide",
	"IsHidden",
	"Suspend",
	"Unsuspend",
	"Pause",
	"Unpause",
	"SetSpeed",
	"GetSpeed",
	"OrientToNormal",
	"Held",
	"Released",
	"HeldLongerThan",
	"EnableInputEvents",
	"DisableInputEvents",
	"HasPowerup",
	"PickedUpPowerup",
	
//	"DoNextTrick",
	"ClearTricksFrom",
	"SetQueueTricks",
	"ClearTrickQueue",
	"AddTricksToQueue",
	"UseGrindEvents",
	"DoNextManualTrick",
	"SetManualTricks",
	"ClearManualTrick",
	"SetExtraGrindTricks",
	"ClearExtraGrindTrick",
	"AirTimeLessThan",
	"AirTimeGreaterThan",
	"GetAirTime",
	"GetAirTimeLeft",
	"GetSlope",
	"backwards",
	"ClearPanel_Landed",
	"ClearPanel_Bailed",
	"FlipAfter",
	"RotateAfter",
	"BoardRotateAfter",
	"IsFlipAfterSet",
	"TriggerType",
	"KillSkater",
	"FlipAndRotate",
	"DoBalanceTrick",
	"StopBalanceTrick",
	"SwitchOffBalanceMeter",
	"SwitchOnBalanceMeter",
	"StartBalanceTrick",
	"StartSkitch",
	"StopSkitch",
	"Skitching",
	"SetState",
	"ClearEventBuffer",
	"Input_Debounce",
	"DisablePlayerInput",
	"NetDisablePlayerInput",
	"EnablePlayerInput",
	"NetEnablePlayerInput",
	"PlayerInputIsDisabled",
	"StartGap",
	"EndGap",
	"StartGapTrick",
	"CheckGapTricks",
	"ClearGapTricks",
	"GotSpecial",
	"SetRailSound",
	"LockVelocityDirection",
	"SetRollingFriction",
	"SetSpecialFriction",
	"InNollie",
	"NollieOn",
	"NollieOff",
	"InPressure",
	"PressureOn",
	"PressureOff",
    "RunStarted",
	"EndOfRunDone",
	"EndOfRunStarted",
	"Goal_EndOfRunDone",
	"Goal_EndOfRunStarted",
	"IsInEndOfRun",
	"DuplicateTrigger",
	"SparksOn",
	"SparksOff",
	"Vibrate",
	"DoingTrick",
	"GetTerrain",
	"BailOn",
	"BailOff",
	"BailIsOn",
    "FrontTruckSparks",
	"SetFrontTruckSparks",
	"SetRearTruckSparks",
	
	"RemoveXEvents",
	"RestoreEvents",
	"YawBetween",
	"PitchGreaterThan",
	"AbsolutePitchGreaterThan",
	"RollGreaterThan",
	"YawingLeft",
	"YawingRight",
	// "PitchingForward",
	// "PitchingBackward",
	// "RollingLeft",
	// "RollingRight",
	"TextureSplat",
	"Skeleton_SpawnTextureSplat",
	"Skeleton_SpawnCompositeObject",
	"TweakTrick",
	"IsLatestTrick",
	"SetGrindTweak",
	"Ledge",
	"BadLedge",
	"SkateInAble",
	"ResetSpin",
	"LastSpinWas",
	"LandedFromSpine",
	"LandedFromVert",
	"SetLandedFromVert",
	"ResetLandedFromVert",
	"InVertAir",
	"AllowLipNoGrind",
	"ClearAllowLipNoGrind",
	"NoRailTricks",
	"AllowRailTricks",
	"SetExtraTricks",
	"KillExtraTricks",
	"TurnToFaceVelocity",
	"SC_SetSkaterCamOverride",
	"SC_ClearSkaterCamOverride",
	"SC_ShakeCamera",
	"SC_SetMode",
	"SetExtraPush",
	"GetMatrixNormal",
	"GetLastGroundPos",
	"ShouldMongo",
	"SetSlotTrick",
	"ChangeProTricks",
	"TrickOffObject",
	"AdjustBalance",
	"IsLocalSkater",
	"SkaterIsNamed",
	"GetCameraId",
	"AddSkaterToWorld",
	"ResetRigidBodyCollisionRadiusBoost",
	"RemoveSkaterFromWorld",
	"OverrideLimits",
	"CreateSpecialItem",
	"DestroySpecialItem",
	"DestroyAllSpecialItems",
	"SpecialItemExists",
	"OverrideCancelGround",
	"BoardIsRotated",
	"GetSkaterNumber",
	"LastScoreLandedGreaterThan",
	"LastScoreLandedLessThan",
	"TotalScoreGreaterThan",
	"TotalScoreLessThan",
	"GetTotalScore",
	"CurrentScorePotGreaterThan",
	"CurrentScorePotLessThan",
	"GetNumberOfNonGapTricks",
	"PlaceBeforeCamera",
	"CancelWallpush",
	
	// used by Obj::CCompositeObject
	"Obj_PrintDetails",
	"CreateComponentFromStructure",

	// used by Obj::CMovingObject
	"Obj_PlayStream",
	"Obj_StopStream",
	"FollowWaypointPath",
	"LookAtNode",
//	"Obj_WaitForStreamAvailable",
//	"Obj_WaitForStreamToStart",
//	"Obj_WaitStreamFinished",
	"Obj_ShadowOn",
	"Obj_ShadowOff",
	// objects would require an animated model to run these functions:
//	"RunAnim",
//	"CycleAnim",
	"Obj_WaitRotate",
	"Obj_WaitAnimFinished",
	"AnimExists",
	"Obj_WaitMove",
	"Obj_WaitStop",
	// wait functions:
	// other functions:
	"Obj_StopMoving",
	"Obj_StopRotating",
	"Obj_PlaySound",
	"Obj_AdjustSound",
	"Obj_StopSound",
	"Obj_SetSound", // tell a car, for example, which sound to use for engine loop.
	"Obj_Rotate",
	"Obj_RotX",
	"Obj_RotY",
	"Obj_RotZ",
	"Obj_Hover",
	"Obj_StorePos",
	"Obj_StoreNode",
	"Obj_LookAtNodeStored",
	"Obj_LookAtNodeLinked",
	"Obj_LookAtPosStored",
//	"Obj_MoveToNodeStored",
	"Obj_MoveToPosStored",
	"Obj_RandomPathMode",
	"Obj_FollowPathStored",
	"Obj_LookAtNode",
	"Obj_LookAtPos",
	"Obj_LookAtRelPos",
	"Obj_LookAtObject",
	"Obj_AngleToNearestSkaterGreaterThan",
	"Obj_MoveToLink",
	"Obj_GetRandomLink",
	"Obj_MoveToNode",
	"Obj_MoveToPos",
	"Obj_MoveToRelPos",
	"Obj_MoveForward",
	"Obj_MoveLeft",
	"Obj_IsMoving",
	"Obj_IsRotating",
	"Obj_SetConstantHeight",
	"Obj_FollowPath",
	"Obj_FollowPathLinked",
	"Obj_FollowLeader",
	"Obj_LockToObject",
	"Obj_GetSpeed",
	"Obj_GetNextObjOnPath",
	"Obj_SetPathVelocity",
	"Obj_SetPathAcceleration",
	"Obj_SetPathDeceleration",
	"Obj_SetPathMinStopVel",
	"Obj_StopAlongPath",
	"Obj_StartAlongPath",
	"Obj_SetPathTurnDist",
	"Obj_SetGroundOffset",
	"Obj_PlayAnim",
	"Obj_EnableAnimBlending",
	"Obj_SetAnimCycleMode",
	"Obj_AnimEquals",
	"Obj_StickToGround",
	"Obj_PathHeading",
	"Obj_AnimComplete",
	"Obj_AllowSkitching",
	"Obj_GetOrientation",
	"Obj_ReplaceTexture",
	"Obj_ReplaceSpriteTexture",
	"Obj_SetBodyShape",
	"Obj_SetBoneActive",
	"Obj_GetBonePosition",
	"SwitchOnAtomic",
	"SwitchOffAtomic",
    "AtomicIsHidden",
	"Obj_ClearGeoms",
	"Obj_InitModel",
	"Obj_InitModelFromProfile",
	"Obj_HasModelLights",
	"Obj_UpdateBrightness",
	"Obj_EnableAmbientLight",
	"Obj_EnableDiffuseLight",
	"Obj_DisableAmbientLight",
	"Obj_DisableDiffuseLight",
	"Obj_SetLightAmbientColor",
	"Obj_SetLightDirection",
	"Obj_SetLightDiffuseColor",
	"Obj_SetUVOffset",
	"Obj_SetUVParams",
	"Obj_SetBoundingSphere",
	"Obj_RestoreBoundingSphere",
	"Obj_SkipToRestart",

	// procedural bone anim functions
	"SetBoneTransActive",
	"SetBoneTransMin",
	"SetBoneTransMax",
	"SetBoneTransSpeed",
	"SetBoneTransCurrent",
	"SetBoneRotActive",
	"SetBoneRotMin",
	"SetBoneRotMax",
	"SetBoneRotSpeed",
	"SetBoneRotCurrent",
	"SetBoneScaleActive",
	"SetBoneScaleMin",
	"SetBoneScaleMax",
	"SetBoneScaleSpeed",
	"SetBoneScaleCurrent",
	"AddAnimController",
	"RemoveAnimController",
	"GetPartialAnimParams",
	"SetPartialAnimSpeed"
	"IncrementPartialAnimTime",
	"ReversePartialAnimDirection",
	"SetWobbleDetails",
	"Obj_Flip",
	"Obj_AnimationFlipped",

	// can be used by any Obj::CMovingObject
	"Obj_GetId",
	"Die",
	"DisassociateFromObject",
	"Obj_SetInnerRadius", // inner radius for player 'collision'
	"Obj_SetOuterRadius", // outer radius for player 'collision'
	"Obj_SetInnerAvoidRadius", // inner radius for player avoidance logic
	"Obj_SetOuterAvoidRadius", // outer radius for player avoidance logic
	"Obj_FlagSet",
	"Obj_FlagNotSet",
	"Obj_SetFlag",
	"Obj_ClearFlag",
	"Obj_SpawnScript",
	"Obj_KillSpawnedScript",
	"Obj_SwitchScript",
	"Obj_Visible",
	"Obj_Invisible",
	"Obj_ObjectInRadius",
	"Obj_ObjectInRect",
	"Obj_ObjectNotInRect",
	
	// "Obj_VarGet",
	// "Obj_VarSet",
	// "Obj_VarPrintf",
	// "Obj_VarPrint",
	// "Obj_VarInc",
	// "Obj_VarDec",
	// "Obj_VarLT",
	// "Obj_VarGT",
	// "Obj_VarEQ",
	
	"Obj_GetCollision",
	"Obj_GetDistToNode",
	"Obj_ApplyScaling",
	"Obj_EnableScaling",
	"Obj_DisableScaling",
	"Obj_SetColor",
	"Obj_ClearColor",
	"Obj_Jump",
	"Obj_WaitJumpFinished",
	"Obj_GetDistanceToObject",
	"Obj_GetOrientationToObject",
	"Obj_GetPosition",
	"Obj_SetPosition",
	"Obj_SetOrientation",
	"Obj_ForceUpdate",
	"Obj_GetVelocity",
	
	// Car member functions:
	"EnableCarShadow",
	 
	// Ped member functions:
	"Ped_SelectAvoidPoint",
	"Ped_MoveTowardsAvoidPoint",
	"Ped_AvoidPointReached",
	"Ped_RememberNextWaypoint",
	"Ped_RememberCurrentPosition",
	"Ped_RememberStickToGround",
	"Ped_RestoreStickToGround",
	"Ped_BackOnOriginalPath",

	// Bouncy obj member functions:
	"BouncyObj_PlayerCollisionOn",
	"BouncyObj_PlayerCollisionOff",
	"BouncyObj_Go",
	"MoveToRandomRestart",

	// CRigidBodyComponent member functions:
	"RigidBody_IgnoreSkater",
	"RigidBody_Wake",
	"RigidBody_Sleep",
	"RigidBody_Kick",
	"RigidBody_Reset",
	"RigidBody_DisablePlayerCollision",
	"RigidBody_EnablePlayerCollision",
	"RigidBody_MatchVelocityTo",
	
	// CRibbonComponent member functions:
	// "Ribbon_Reset",
	
	// CVehicleComponent member functions:
	"Vehicle_Kick",
	"Vehicle_Wake",
	"Vehicle_MoveToRestart",
	"Vehicle_PlaceBeforeCamera",
	"Vehicle_AdjustGravity",
	// "Vehicle_ForceBrake",
	"Vehicle_HandbrakeActive",
	"Vehicle_AllWheelsAreInContact",
	"Vehicle_LostCollision",
	"Vehicle_IsSkaterVisible",
	
	// CVehicleCameraComponent member functions:
	"VehicleCamera_Reset",
	




	// CSkaterPhysicsControlComponent member functions:
	"Walking",
	"Skating",
	"Driving",
	"SkaterPhysicsControl_SwitchSkatingToWalking",
	"SkaterPhysicsControl_SwitchWalkingToSkating",
	"SetBoardMissing",
	"UnsetBoardMissing",
	"IsBoardMissing",
	
	
	// CSkaterLoopingSoundComponent member functions:
	"SkaterLoopingSound_TurnOn",
	"SkaterLoopingSound_TurnOff",
	
	// CWalkComponent member functions:
	"Walk_Jump",
	"Walk_ScaleAnimSpeed",
	"Walk_Ground",
	"Walk_Air",
	"Walk_Hang",
	"Walk_Ladder",
	"Walk_AnimWait",
	"Walk_GetStateTime",
	"Walk_SetDragFactor",
	"Walk_ResetDragFactor",
	"Walk_AnimWaitComplete",
	"Walk_GetSpeedScale",
    "Walk_GetHangInitAnimType",
	"Walk_GetStateDuration",
	"Walk_GetPreviousState",
	"Walk_GetState",
	"Walk_GetHangAngle",
	
	// "Walk_CancelTransitionalMomentum",
	// "Walk_SuppressInAirControl",
	
	// CWalkCameraComponent member functions:
	"WalkCamera_FlushRequest",
	"WalkCamera_Reset",
	
	// CSkaterRunTimerComponent member functions:
	"RunTimer_Pause",
	"RunTimer_UnPause",
	"RunTimer_GetFactorComplete",
	"RunTimer_GetRunTimerControllerId",
	"RunTimer_GetRunTimerId",
	
#	ifdef TESTING_GUNSLINGER
	// CHorseComponent member functions:
	"Horse_GetSpeedScale",
	"Horse_Jump",
#	endif

	// CScreenElement functions:
	"SetProps",
	"DoMorph",
	"GetProps",
	
	// CObject functions:
//	"ClearEventHandler",
//	"ClearEventHandlerGroup",
	"SetTags",
	"GetTags",

	// Particle system functions.
	"SetPos",
	"SetSpeedRange",
	"SetEmitRange",
	"SetAngleSpread",
	"SetRandomAngle",
	"SetCircularEmit",
	"SetForce",
	"SetParticleSize",
	"SetLife",
	"SetEmitTarget",
	"SetColor",
	"BuildPath",
	"EmptySystem",
	
	"Emit",
	"EmitRate",

    // StatsManagerComponent fuctions
    "StatsManager_ActivateGoals",
    "StatsManager_DeactivateGoals",
    "StatsManager_GetGoalStatus",
    "StatsManager_ReInit",
    "StatsManager_Reset",
    "StatsManager_UnlockAmGoals",
    "StatsManager_UnlockProGoals",

	// PedLogicComponent functions
	"Ped_InitPath",
	"Ped_SetLogicState",
	"Ped_StartMoving",
	"Ped_StopMoving",
	"Ped_IsMoving",
	"Ped_SetStickToGroundDist",
	"Ped_SetIsGrinding",
	"Ped_Bail",
	"Ped_SetIsBailing",
	"Ped_GetCurrentVelocity",
	"Ped_StoreMaxVelocity",
	"Ped_GetOriginalMaxVelocity",
	"Ped_InitVertTrick",
	"Ped_HitWaypoint",
	"Ped_SetIsSkater",
	"Ped_NearVertNode",
	"Ped_PlayJumpSound",
	"Ped_PlayLandSound",
	"Ped_GetCurrentNodeNames",
	
	"FlagGoalAsWon",
	"FindUnfinishedGoal",
	"GoalExists",
	"NewEditorGoal",
	"RemovedCreatedGoal",
	"GoalHasAllPositionsSet",
	"SetCurrentEditorGoal",
	"SetEditorGoalType",
	"SetEditorGoalDescription",
	"SetEditorGoalWinMessage",
	"GetEditedGoalsInfo",
	"GetNumEditedGoals",
	"GetCurrentEditedGoalId",
	"WriteEditedGoalNodePositions",
	"AddEditedGoalToGoalManager",
	"GetCurrentEditedGoalInfo",
	"SetEditorGoalName",
	"SetEditorPedName",
	"MaxEditedGoalsReached",	
	"EditGoal",
	"NukeAllGoals",
	"GetMaxGoalsPerLevel",
	"SetGoalScore",
	"SetGoalTimeLimit",
	"SetGoalControlType",
	"RefreshGoalCursorPosition",
	"SetGoalSpecificParams",
	"RemoveGoalSpecificFlag",
	"AddKeyComboSet",
	"GetKeyComboSet",
	"RemoveKeyComboSet",
	"EditedGoalAddGap",
	"EditedGoalRemoveGap",
	"EditedGoalGotGap",
	"EditorCam_Initialise",
	"EditorCam_SetCursorPos",
	"EditorCam_SetOnlyVerticalCollisionChecks",

	// Rail editor component member functions
	"GetCursorPosition",
	"NewRail",
	"MaxRailsReached",
	"AddNewPosition",
	"ClearRailEditor",
	"SetEditingMode",
	"GetEditingMode",
	"DrawDottedLine",
	"DeleteDottedLine",
	"UpdateRailPointPosition",
	"HighlightRailPoint",
	"UnHighlightAllRails",
	"DeleteRailPoint",
	"GetEditedRailInfo",
	"DeleteRail",
	"DestroyEditedRailSectors",
	
	"SetStartPos",
	"SetMidPos",
	"SetEndPos",
	"SetStartBoxDimensions",
	"SetMidBoxDimensions",
	"SetEndBoxDimensions",
	"SetUpdateScript",
	"SetEmitRate",
	"SetLifetime",
	"SetMidPointPct",
	"SetMidpointColorPct",
	"SetStartRadius",
	"SetMidRadius",
	"SetEndRadius",
	"SetStartColor",
	"SetMidColor",
	"SetEndColor",
    
};

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

int GetCFunctionLookupTableSize()
{
    return (sizeof(CFunctionLookupTable)/sizeof(SCFunction));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int GetNumMemberFunctions()
{
    return (sizeof(ppMemberFunctionNames)/sizeof(const char *));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Script



================================================
FILE: Code/Sk/Scripting/ftables.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Scripting												**
**																			**
**	File name:		ftables.h												**
**																			**
**      Created:                09/15/2000      -        ksh                                                                             **
**																			**
*****************************************************************************/

#ifndef	__SCRIPTING_FTABLES_H
#define	__SCRIPTING_FTABLES_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef	__SCRIPTING_INIT_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/
namespace Script
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

extern SCFunction CFunctionLookupTable[];

extern const char *ppMemberFunctionNames[];

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

int GetCFunctionLookupTableSize();
int GetNumMemberFunctions();

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace FTables

#endif	// __SCRIPTING_FTABLES_H



================================================
FILE: Code/Sk/Scripting/gs_file.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// gs_file.cpp		KSH 7 Nov 2001
//
// Game specific functions for loading and unloading qb files.
// These call the LoadQB and UnloadQB functions in the Skate namespace, but then do
// any additional skate-specific processing required.
// For example, if the newly loaded qb contained a NodeArray, node name prefix info
// needs to be regenerated, etc.
// Also, skater exceptions may need to be reloaded.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  // For Crc::GenerateCRCFromString
#include 
#include 
#include 
#include 
#ifdef __PLAT_NGC__
#include 
#endif		// __PLAT_NGC__

#include "string.h"

#ifdef __PLAT_NGC__
#include 
#include "sys/ngc/p_display.h"
#endif		// __PLAT_NGC__

namespace SkateScript
{
using namespace Script;

// The size of the temporary buffer used when parsing the qdir.txt file. Used in LoadAllStartupQBFiles()
#define MAX_QB_FILENAME_CHARS_WITH_PATH 100

#define QDIR_FILE "scripts\\qdir.txt"
#define MAIN_QB_FILEPATH "scripts\\"

void LoadAllStartupQBFiles()
{
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
#ifdef __PLAT_NGC__
	switch ( Config::GetLanguage() )
	{
		case Config::LANGUAGE_FRENCH:
			Pip::LoadPre("qb.prf");
			break;
		case Config::LANGUAGE_GERMAN:
			Pip::LoadPre("qb.prd");
			break;
		default:
			Pip::LoadPre("qb.pre");
			break;
	}
#else
	Pip::LoadPre("qb.pre");
#endif		// __PLAT_NGC__
	Mem::Manager::sHandle().PopContext();

	// Load the qdir.txt file
	const char *p_qdir=(const char *)Pip::Load(QDIR_FILE);
	uint32 file_size=Pip::GetFileSize(QDIR_FILE);
	
	UsePermanentStringHeap();
	
    // Load each of the files listed.
	#define MAX_FILENAME_CHARS 200
	char p_file_name[MAX_FILENAME_CHARS+1];
	
	const char *p_scan=p_qdir;
	while (p_scan < p_qdir+file_size)
	{
		int c=0;
		while (p_scan < p_qdir+file_size && *p_scan!=0x0d && *p_scan!=0x0a)
		{
			Dbg_MsgAssert(c= p_qdir+file_size then it
		// still will be after incrementing p_scan so that's OK too.
		++p_scan;
		
		// Skip any empty strings, which will happen when encountering 0x0d,0x0a pairs.
		if (*p_file_name)
		{
			// Note: p_file_name will contain the complete path, eg c:\skate5\data\scripts\blaa.qb

			// Strip off any preceding data path.			
			char *p_data_backslash=strstr(p_file_name,"data\\");
			if (p_data_backslash)
			{
				strcpy( p_file_name, p_data_backslash+5); // Safe cos it will copy backwards
			}
				
			// Make sure this script isn't already loaded.
			SkateScript::UnloadQB( Crc::GenerateCRCFromString(p_file_name) );

			SkateScript::LoadQB(p_file_name,
								// We do want assertions if duplicate symbols when loading all 
								// the qb's on startup. (Just not when reloading a qb)
								ASSERT_IF_DUPLICATE_SYMBOLS);
		}	
#ifdef __PLAT_NGC__
		NsDisplay::doReset();
#endif		// __PLAT_NGC__

	}	

	UseRegularStringHeap();
	
	Pip::Unload(QDIR_FILE);
#ifdef __PLAT_NGC__
	switch ( Config::GetLanguage() )
	{
		case Config::LANGUAGE_FRENCH:
			Pip::UnloadPre("qb.prf");
			break;
		case Config::LANGUAGE_GERMAN:
			Pip::UnloadPre("qb.prd");
			break;
		default:
			Pip::UnloadPre("qb.pre");
			break;
	}
#else
	Pip::UnloadPre("qb.pre");
#endif		// __PLAT_NGC__
	
	// A very small memory optimization ...
	// Remove the debugger_names array since it only exists to get its checksum names registered
	// for the script debugger. They will have got registered by now, so the array is no longer
	// needed. Saves about 3K.
	CSymbolTableEntry *p_debugger_names=LookUpSymbol(CRCD(0xc2f97ba7,"debugger_names"));
	if (p_debugger_names)
	{
		CleanUpAndRemoveSymbol(p_debugger_names);
	}
}

// Calls the Script::LoadQB, and then do any game-specific stuff that needs to be done when a qb is reloaded, such as 
// generating the node name hash table & prefix info
void LoadQB(const char *p_fileName, EBoolAssertIfDuplicateSymbols assertIfDuplicateSymbols)
{
	Dbg_MsgAssert(p_fileName,("NULL p_fileName"));
	//if (strcmp("..\\runnow.qb",p_fileName) != 0)
	//{
	//	printf("Loading %s\n",p_fileName); // REMOVE
	//}
	// Call the non-game-specific LoadQB, which open the qb and create all the symbols defined within.
	Script::LoadQB(p_fileName, assertIfDuplicateSymbols);
	
	CSymbolTableEntry *p_sym=GetNextSymbolTableEntry();
	while (p_sym)
	{
		if (p_sym->mGotReloaded && 
			p_sym->mType==ESYMBOLTYPE_ARRAY &&
			p_sym->mNameChecksum==0xc472ecc5/*NodeArray*/)
		{
			// This will resize the NodeArray, adding the extra nodes required by the goal editor.
			// Note that p_sym->mpArray is still valid after doing this, because the resizing only
			// causes a reallocation of a buffer inside the CArray, the CArray itself is not reallocated.
			Obj::InsertGoalEditorNodes();
			
			CreateNodeNameHashTable();
			GeneratePrefixInfo();

			// Do a bit of node array processing.
			int size=p_sym->mpArray->GetSize();
			for (int i=0; impArray->GetStructure(i);
				
				// K: This swapping of Position for Pos used to be at a lower level in parse.cpp,
				// but that had the problem that it was doing the swap on all structures, including those
				// passed to commands in scripts. So I moved it here so as to only affect those structures
				// in the node array.
				Mth::Vector p(0.0f,0.0f,0.0f);
				if (p_node->GetVector(CRCD(0xb9d31b0a,"Position"),&p))
				{
					// GJ:  The "position" currently written out 
					// by the exporter has an incorrect Z, and there
					// are a bunch of places in the game code which
					// negate the Z value in order to correct it.
					// We're currently in the process of phasing this
					// "Position" out, in favor of the "Pos" vector, 
					// which will have the correct Z.  For backwards
					// compatibility, the code here removes the old
					// "Position" vector if it exists and replaces 
					// it with the new "Pos" vector.  Eventually, 
					// once all the levels have been re-exported, 
					// we can remove this kludge.
					p_node->RemoveComponent(CRCD(0xb9d31b0a,"Position"));
					p_node->AddVector(CRCD(0x7f261953,"Pos"),p[X],p[Y],-p[Z]);
				}	
			}
			
			// Do some path preprocessing. This adds PathNum parameters to all nodes that
			// are on a path so that one can quickly find out what path a particular car is
			// on without having to scan through a bunch of nodes.
			Obj::CPathMan::Instance()->AddPathInfoToNodeArray();
			
			// Reset the reloaded flag, otherwise it will stick on forever.
			p_sym->mGotReloaded=false;
			
			// No need to keep checking for more NodeArray's, there can be only one.
			break;
		}
		p_sym=GetNextSymbolTableEntry(p_sym);
	}		
	
	// Make sure checksum name lookup table is removed since it is only needed for the node array.
	RemoveChecksumNameLookupHashTable();
}

// Calls the Script::UnloadQB and then does any game-specific stuff that needs to be done 
// when a qb is unloaded, such as resetting the node name hash table & prefix info.
void UnloadQB(uint32 fileNameChecksum)
{
	// Call the non-game-specific UnloadQB, which will delete all the symbols that came from this qb.
	Script::UnloadQB(fileNameChecksum);
	
	if (!GetArray(0xc472ecc5/*NodeArray*/))
	{
		// If there is no node array any more then make sure the prefix info and node name lookup
		// info is cleaned up.
		DeletePrefixInfo();
		ClearNodeNameHashTable();
	}	
}

uint32 GenerateFileNameChecksum(const char *p_fileName)
{
	Dbg_MsgAssert(p_fileName,("NULL p_fileName"));
	
	// TODO May need to process the file name a bit first, eg prefix it with the
	// full path if it is not there already, before calculating its checksum, in order to ensure a match.
	// We may want UnloadQB to be able to be passed a filename without the full path.
	
	// For the moment though, just calculate the checksum for the string as it is.
	return Crc::GenerateCRCFromString(p_fileName);
}

void UnloadQB(const char *p_fileName)
{
	SkateScript::UnloadQB(GenerateFileNameChecksum(p_fileName));
}

// TODO Need to add the old 'DoIt' function which executes when a qbr is done. It does a LoadQB then
// reloads the skater exceptions, updates the decks on the skateshop wall, etc...

} // namespace SkateScript



================================================
FILE: Code/Sk/Scripting/gs_file.h
================================================
#ifndef	__SK_SCRIPTING_GS_FILE_H
#define	__SK_SCRIPTING_GS_FILE_H

#ifndef	__SCRIPTING_SCRIPTDEFS_H
#include  // For enums
#endif

namespace SkateScript
{
using namespace Script;

void LoadAllStartupQBFiles();
void LoadQB(const char *p_fileName, 
			EBoolAssertIfDuplicateSymbols assertIfDuplicateSymbols=NO_ASSERT_IF_DUPLICATE_SYMBOLS);
void UnloadQB(uint32 fileNameChecksum);
uint32 GenerateFileNameChecksum(const char *p_fileName);
void UnloadQB(const char *p_fileName);

} // namespace SkateScript

#endif // #ifndef	__SK_SCRIPTING_GS_FILE_H



================================================
FILE: Code/Sk/Scripting/gs_init.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// gs_init.cpp		KSH 7 Nov 2001
//
// Game-specific script system initialisation.
// Also calls the non-game-specific script initialisation.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

namespace SkateScript
{

// Called once on startup.
void Init()
{
	// Do the non-game-specific script system initialisation
	Mem::PushMemProfile("Script::AllocatePools");
	Script::AllocatePools();
	Mem::PopMemProfile();
	
	
	// Now do the game-specific script system initialisation
	Mem::PushMemProfile("InitNodeNameHashTable");
	InitNodeNameHashTable();
	Mem::PopMemProfile();
	

	Mem::PushMemProfile("Registering script functions");
	Script::RegisterCFunctions(CFunctionLookupTable,GetCFunctionLookupTableSize());
	Script::RegisterMemberFunctions(ppMemberFunctionNames,GetNumMemberFunctions());
	Mem::PopMemProfile();
	
}

void Preload()
{
	LoadAllStartupQBFiles();
	
	#ifdef COUNT_USAGE
	CSymbolTableEntry *p_sym=GetNextSymbolTableEntry();
	while (p_sym)
	{
		if (p_sym->mType==ESYMBOLTYPE_QSCRIPT)
		{
			//printf("%s\n",Script::FindChecksumName(p_sym->mNameChecksum));
			
			uint8 *p_token=p_sym->mpScript;
			// Skip over the 4-byte contents checksum.
			p_token+=4;
			
			while (*p_token!=ESCRIPTTOKEN_KEYWORD_ENDSCRIPT) 
			{
				switch (*p_token)
				{
					case ESCRIPTTOKEN_NAME:
					{
						++p_token;
						uint32 name_checksum=Read4Bytes(p_token).mChecksum;
						
						p_token+=4;
			
						// Look up the name to see if it is a cfunction or member function.
						CSymbolTableEntry *p_entry=Resolve(name_checksum);
						if (p_entry)
						{
							//if (name_checksum==0xb0bcbaec)
							//printf("(%s) Type=%s\n",Script::FindChecksumName(name_checksum),Script::GetTypeName(p_entry->mType));
						
							if (p_entry->mType==ESYMBOLTYPE_CFUNCTION)
							{
								++p_entry->mUsage;
								//printf("%s: %s\n",Script::FindChecksumName(p_sym->mNameChecksum),Script::FindChecksumName(name_checksum));
							}
							else if (p_entry->mType==ESYMBOLTYPE_MEMBERFUNCTION)
							{
								++p_entry->mUsage;
							}	
						}		
						break;
					}	
					default:
						p_token=SkipToken(p_token);
						break;
				}
			}	
		}
		p_sym=GetNextSymbolTableEntry(p_sym);
	}		
	
	p_sym=GetNextSymbolTableEntry();
	while (p_sym)
	{
		if (p_sym->mUsage==0)
		{
			if (p_sym->mType==ESYMBOLTYPE_CFUNCTION)
			{
				printf("CFunc %s: Usage=%d\n",Script::FindChecksumName(p_sym->mNameChecksum),p_sym->mUsage);
			}
			if (p_sym->mType==ESYMBOLTYPE_MEMBERFUNCTION)
			{
				//printf("MemberFunc %s: Usage=%d\n",Script::FindChecksumName(p_sym->mNameChecksum),p_sym->mUsage);
			}
		}	
		p_sym=GetNextSymbolTableEntry(p_sym);
	}		
	
	printf("Finished\n");
	while (1);
	#endif // #ifdef COUNT_USAGE



	
	// Now that all the qb's are loaded, pre-process all the scripts.
	//PreProcessScripts();
}

} // namespace SkateScript



================================================
FILE: Code/Sk/Scripting/gs_init.h
================================================
#ifndef	__SK_SCRIPTING_GS_INIT_H
#define	__SK_SCRIPTING_GS_INIT_H

namespace SkateScript
{

void Init();
void Preload();

} // namespace SkateScript

#endif // #ifndef __SK_SCRIPTING_GS_INIT_H


================================================
FILE: Code/Sk/Scripting/mcfuncs.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Scripting
//* FILENAME:       McFuncs.cpp
//* OWNER:          Kendall Harrison
//* CREATION DATE:  6/10/2002
//****************************************************************************

// Contains Mem card stuff.

// TODO: Make all local variables conform to the naming convention! Currently they are a mix

// start autoduck documentation
// @DOC cfuncs
// @module cfuncs | None
// @subindex Scripting Database
// @index script | cfuncs

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifdef __PLAT_NGPS__
#include 
#endif // __PLAT_NGPS__

#ifdef __PLAT_NGC__
#include "sys/ngc/p_file.h"
#include "sys/ngc/p_buffer.h"
#include "sys/ngc/p_dma.h"
#include "sys/ngc/p_aram.h"
bool g_mc_hack = false;
uint32 g_hack_address = 0;
extern char * g_p_buffer;
#endif		// __PLAT_NGC__

namespace CFuncs
{

using namespace Script;
	
/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// Whenever the format of one of these file types has changed such that
// an old save will not load, increment its version number.
/*
// GJ:  I moved this to memcard.q, because the artists/designers sometimes
// do things that will require a version change.

#define VERSION_OPTIONSANDPROS 10
#define VERSION_NETWORKSETTINGS 3
#define VERSION_CAS 4
#define VERSION_CAT 1
#define VERSION_PARK 2
#define VERSION_REPLAY 6
*/

// The max number of files that can appear in the file list.
// A max is specified both to ensure we don't run out of memory in the files menu, and
// also to limit the pause before the menu comes up because it has to open each file first
// to extract the summary info.
#define MAX_THPS4_FILES_ALLOWED 75

// Limit the space that the summary info structure is allowed to take up when written to a buffer.
// This is because we want to be able to quickly read in the summary info from the start of the
// mem card file without having to read in the whole file. (Units are bytes)
#define MAX_SUMMARY_INFO_SIZE 100

// Max chars allowed in the low level card filename.
#define MAX_CARD_FILE_NAME_CHARS 100

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

// This value gets added to the character 'a' in the last char of the 
// low-level card filename to indicate what the type of the file is.
enum EMemCardFileType
{
	MEMCARD_FILETYPE_CAREER,
	MEMCARD_FILETYPE_CAS,
	MEMCARD_FILETYPE_PARK,
	MEMCARD_FILETYPE_REPLAY,
	MEMCARD_FILETYPE_NETWORKSETTINGS,
    MEMCARD_FILETYPE_CAT,
    MEMCARD_FILETYPE_CREATEDGOALS,
};

// The old OptionsAndPros save file now contains the custom skater too, and is divided into 4 parts.
// The LoadFromMemoryCard command provides the option of only applying certain parts to the game state.
// For example, when loading a custom skater, only the CUSTOM_SKATER part is applied, so the career
// remains unaffected.
enum EUnifiedSaveFileSubTypes
{
	APPLY_GLOBAL_INFO=0,
	APPLY_STORY=1,
	APPLY_STORY_SKATER=2,
	APPLY_CUSTOM_SKATER=3,
};
	
#ifdef __PLAT_NGC__
// Icons plus banner.
#define	NGC_MEMCARD_ICON_DATA_SIZE		( 64 + ( 32 * 32 * 7 ) + ( 256 * 2 ) + ( 96 * 32 ) + ( 256 * 2 ) )
#endif

// The mem card file starts with one of these, followed by two CStruct's as written
// out using WriteToBuffer.
// The first CStruct is the summary info for the file and occupies no more than
// MAX_SUMMARY_INFO_SIZE bytes.
// The second CStruct holds all the save data.
struct SMcFileHeader
{
	#ifdef __PLAT_NGC__
	// On the GameCube the icon data is stored within the file rather than as a
	// seperate file. So lets wack it into this structure.
	uint8 mpIconData[NGC_MEMCARD_ICON_DATA_SIZE];
	#endif
	
	#ifdef __PLAT_XBOX__
	// I think it is a TRC requirement to use the XBox's special way of calculating
	// a checksum. They probably have ways of telling whether we're using it or not.
	XCALCSIG_SIGNATURE mSignature;
	#else
	uint32 mChecksum;
	#endif // #ifdef __PLAT_XBOX__
	
	// This is so that the files menu can see if a file might be bad 
	// given only the summary info.
	uint32 mSummaryInfoChecksum;
	int mSummaryInfoSize;

	// This is the size of this header, the summary info structure, and
	// the main structure, rounded up to the platforms block size.
	// So for all file types except replays, this should match the total file size.
	// In the case of replays, the replay data comes after that lot.
	// mDataSize is needed to indicate the start of the replay data.
	int mDataSize;
	
	int mVersion;
};
	
/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

#ifndef __PLAT_NGC__
static unsigned short s_ascii_special[33][2] = {
	{0x8140, 32},		/*   */
	{0x8149, 33},		/* ! */
	{0x8168, 34},		/* " */
	{0x8194, 35},		/* # */
	{0x8190, 36},		/* $ */
	{0x8193, 37},		/* % */
	{0x8195, 38},		/* & */
	{0x8166, 39},		/* ' */
	{0x8169, 40},		/* ( */
	{0x816a, 41},		/* ) */
	{0x8196, 42},		/* * */
	{0x817b, 43},		/* + */
	{0x8143, 44},		/* , */
	{0x817c, 45},		/* - */
	{0x8144, 46},		/* . */
	{0x815e, 47},		/* / */
	{0x8146, 58},		/* : */
	{0x8147, 59},		/* ; */
	{0x8171, 60},		/* < */
	{0x8181, 61},		/* = */
	{0x8172, 62},		/* > */
	{0x8148, 63},		/* ? */
	{0x8197, 64},		/* @ */
	{0x816d, 91},		/* [ */
	{0x818f, 92},		/* \ */
	{0x816e, 93},		/* ] */
	{0x814f, 94},		/* ^ */
	{0x8151, 95},		/* _ */
	{0x8165, 96},		/* ` */
	{0x816f, 123},		/* { */
	{0x8162, 124},		/* | */
	{0x8170, 125},		/* } */
	{0x8150, 126},		/* ~ */
};

static unsigned short s_ascii_table[3][2] = {
	{0x824f, 0x30},	/* 0-9  */
	{0x8260, 0x41},	/* A-Z  */
	{0x8281, 0x61},	/* a-z  */
};
#endif // #ifndef __PLAT_NGC__

// Used by the SaveFailedDueToInsufficientSpace command.
static bool s_insufficient_space=false;

#if __USE_REPLAYS__
static char spReplayCardFileName[MAX_CARD_FILE_NAME_CHARS+1];
static bool sNeedToLoadReplayBuffer=false;
#endif
/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/
static const char *s_generate_ascii_checksum(char *p_dest, const char *p_string, uint32 fileType=0);
static uint32 s_determine_file_type(char c);
#ifndef __PLAT_NGC__
static unsigned short s_ascii_to_sjis(unsigned char ascii_code);
#endif
static void s_insert_global_info(CStruct *p_struct);
static void s_insert_story_info(CStruct *p_struct);
static void s_insert_story_skater_info(CStruct *p_struct);
static void s_insert_custom_skater_info(CStruct *p_struct);
static void s_insert_game_save_info(uint32 fileType, CStruct *p_struct);
static void s_generate_summary_info(CStruct *p_summaryInfo, uint32 fileType, CStruct *p_mainData);

static void s_read_global_info(CStruct *p_globalInfo, CScript *p_script);
static void s_read_story_info(CStruct *p_storyInfo);
static void s_read_story_skater_info(CStruct *p_storySkaterInfo, CStruct *p_customSkater);
static void s_read_custom_skater_info(CStruct *p_customSkater);
static void s_read_game_save_info(uint32 fileType, CStruct *p_struct, CScript *p_script);

static int s_get_icon_k_required(uint32 fileType);
static int s_calculate_total_space_used_on_card(uint32 fileType, int fileSize);
static int s_get_platforms_block_size();
static int s_round_up_to_platforms_block_size(int fileSize);
static int s_get_version_number(uint32 fileType);
static const char *s_generate_xbox_directory_name(uint32 fileType, const char *p_name);
static void s_generate_card_directory_name(uint32 fileType, const char *p_name, char *p_card_directory_name);
static bool s_make_xbox_dir_and_icons(	Mc::Card *p_card,
										uint32 fileType, const char *p_name, 
										char *p_card_file_name,
										bool *p_insufficientSpace);
static bool s_make_ps2_dir_and_icons(	Mc::Card *p_card,
										uint32 fileType, const char *p_name, 
										char *p_card_file_name,
										bool *p_insufficientSpace);
static bool s_insert_ngc_icon(	SMcFileHeader *p_fileHeader, 
								Mc::Card *p_card,
								Mc::File *p_file,
								uint32 fileType,
								const char *p_name);
static uint32 sGetFixedFileSize(uint32 fileType);



// When transferring data between the online vault and the mem card, this structure temporarily holds
// the data. The LoadFromMemoryCard and SaveToMemoryCard commands can be made to load to/from this structure
// instead, so that data can be transferred without loading it into the game.
static Script::CStruct *spVaultData=NULL;
static uint32 sVaultDataType=0;

// Steve's code calls this after downloading the binary data from the vault.
void SetVaultData(uint8 *p_data, uint32 type)
{
	if (spVaultData)
	{
		delete spVaultData;
		spVaultData=NULL;
	}
	
	spVaultData=new Script::CStruct;
	Script::ReadFromBuffer(spVaultData,p_data);
	sVaultDataType=type;
}

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This calculates an 8 character ascii checksum of the passed string.
// Used for generating the filename of the memory card file from the name the player has chosen.

// It works by calculating a 32 bit checksum the usual way, then
// converting this number to base 26 and using 'a' to represent 0, 'b' for 1, etc.
// Log to base 26 of 2^32 is 6.8, which means at most 7 ascii characters will be needed, so the 
// 8th letter in the string will actually always be a. So the last letter is used to indicate the file type.
static const char *s_generate_ascii_checksum(char *p_dest, const char *p_string, uint32 fileType)
{
	Dbg_MsgAssert(p_dest,("NULL p_dest"));
	
	uint32 Checksum=Script::GenerateCRC(p_string);
	for (int i=0; i<8; ++i)
	{
		int Rem=Checksum%26;
		p_dest[i]='a'+Rem;
		Checksum=(Checksum-Rem)/26;
	}	
	// Check that mathematics is still working ok
	Dbg_MsgAssert(Checksum==0,("Checksum not zero ???"));
	Dbg_MsgAssert(p_dest[7]=='a',("Last letter of ascii checksum not 'a' ???"));

	switch (fileType)
	{
		case 0xb010f357: // OptionsAndPros
			p_dest[7]='a'+MEMCARD_FILETYPE_CAREER;
			break;
		case 0xffc529f4: // Cas
			Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
			//p_dest[7]='a'+MEMCARD_FILETYPE_CAS;
			break;
        case 0x61a1bc57: // CAT
			p_dest[7]='a'+MEMCARD_FILETYPE_CAT;
			break;
		case 0x3bf882cc: // Park
			p_dest[7]='a'+MEMCARD_FILETYPE_PARK;
			break;
		case 0x26c80b0d: // Replay
			p_dest[7]='a'+MEMCARD_FILETYPE_REPLAY;
			break;
		case 0xca41692d: // NetworkSettings
			p_dest[7]='a'+MEMCARD_FILETYPE_NETWORKSETTINGS;
			break;
		case 0x62896edf: // CreatedGoals
			p_dest[7]='a'+MEMCARD_FILETYPE_CREATEDGOALS;
			break;
		default:
			break;
	}
	
	p_dest[8]=0;
	return p_dest;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static uint32 s_determine_file_type(char c)
{
	switch (c-'a')
	{
	case MEMCARD_FILETYPE_CAREER:
		return 0xb010f357; // OptionsAndPros
		break;
	case MEMCARD_FILETYPE_CAS:
		return 0xffc529f4; // Cas
		break;
    case MEMCARD_FILETYPE_CAT:
		return 0x61a1bc57; // CAT
		break;
	case MEMCARD_FILETYPE_PARK:
		return 0x3bf882cc; // Park
		break;
	case MEMCARD_FILETYPE_REPLAY:	
		return 0x26c80b0d; // Replay
		break;
	case MEMCARD_FILETYPE_NETWORKSETTINGS:	
		return 0xca41692d; // NetworkSettings
		break;
	case MEMCARD_FILETYPE_CREATEDGOALS:	
		return 0x62896edf; // CreatedGoals
		break;
	default:
		return 0;
		break;
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


#ifndef __PLAT_NGC__
static unsigned short s_ascii_to_sjis(unsigned char ascii_code)
{
	if (Config::PAL())
	{
		// The PS2 shell will not display underscores (it shows them as spaces)
		// so convert them to hyphens.
		if (ascii_code=='_')
		{
			ascii_code='-';
		}	
	}	

	switch ((char)ascii_code)
	{
		case '': ascii_code='B'; break;
		case '': ascii_code='A'; break;
		case '': ascii_code='U'; break;
		case '': ascii_code='O'; break;
		case '': ascii_code='a'; break;
		case '': ascii_code='a'; break;
		case '': ascii_code='a'; break;
		case '': ascii_code='e'; break;
		case '': ascii_code='e'; break;
		case '': ascii_code='e'; break;
		case '': ascii_code='e'; break;
		case '': ascii_code='i'; break;
		case '': ascii_code='i'; break;
		case '': ascii_code='i'; break;
		case '': ascii_code='o'; break;
		case '': ascii_code='o'; break;
		case '': ascii_code='o'; break;
		case '': ascii_code='u'; break;
		case '': ascii_code='u'; break;
		case '': ascii_code='u'; break;
		case '': ascii_code='c'; break;
		case '': ascii_code='o'; break;
		default: break;
	}
	
	unsigned short sjis_code = 0;
	unsigned char stmp=0;
	unsigned char stmp2 = 0;

	if((ascii_code >= 0x20) && (ascii_code <= 0x2f))
		stmp2 = 1;
	
	else if((ascii_code >= 0x30) && (ascii_code <= 0x39))
		stmp = 0;
	
	else if((ascii_code >= 0x3a) && (ascii_code <= 0x40))
		stmp2 = 11;
	
	else if((ascii_code >= 0x41) && (ascii_code <= 0x5a))
		stmp = 1;
	
	else if((ascii_code >= 0x5b) && (ascii_code <= 0x60))
		stmp2 = 37;
	
	else if((ascii_code >= 0x61) && (ascii_code <= 0x7a))
		stmp = 2;
	
	else if((ascii_code >= 0x7b) && (ascii_code <= 0x7e))
		stmp2 = 63;
	
	else 
	{
		printf("bad ASCII code 0x%x\n", ascii_code);
		return 0;
	}

	if (stmp2)
	   	sjis_code = s_ascii_special[ascii_code - 0x20 - (stmp2 - 1)][0];
	else
		sjis_code = s_ascii_table[stmp][0] + ascii_code - s_ascii_table[stmp][1];

	return sjis_code;
}
#endif		// __PLAT_NGC__

static void s_insert_global_info(CStruct *p_struct)
{
	// Build the global options structure and insert it into p_struct.
	
	Script::CStruct *pOptions=new Script::CStruct;
	
	// Attach the split screen preferences.
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Prefs::Preferences *pPreferences = pSkate->GetSplitScreenPreferences();
	Dbg_MsgAssert(pPreferences,("NULL split screen pPreferences"));
	
	// Create a new structure, append the data contained in the preferences, then insert the pointer to the
	// new structure into pOptions.
	Script::CStruct *pTemp=new Script::CStruct;
	pTemp->AppendStructure(pPreferences->GetRoot());
	pOptions->AddStructurePointer(CRCD(0xf7720c3f,"SplitScreenPreferences"),pTemp);

	// Get the sound options and stick them in a structure and add that.
	pTemp=new Script::CStruct;
	
	Sfx::CSfxManager * pSfxManager = Sfx::CSfxManager::Instance();
	float MainVolume=pSfxManager->GetMainVolume();
	pTemp->AddFloat(CRCD(0x6f016dfb,"MainVolume"),MainVolume);

	float MusicVolume=Pcm::GetVolume();
	pTemp->AddFloat(CRCD(0xabd4a575,"MusicVolume"),MusicVolume);
	
	//uint64 PlayListForbiddenTrackFlags=Pcm::GetPlaylist();
    uint64 list1,list2;
    Pcm::GetPlaylist(&list1, &list2);

	uint32 PlayListForbiddenTrackFlags1=(uint32)(list1>>32);
	uint32 PlayListForbiddenTrackFlags2=(uint32)list1;

    uint32 PlayListForbiddenTrackFlags3=(uint32)(list2>>32);
	uint32 PlayListForbiddenTrackFlags4=(uint32)list2;
	
	pTemp->AddInteger(CRCD(0x595d2c95,"PlayListForbiddenTrackFlags1"),PlayListForbiddenTrackFlags1);
	pTemp->AddInteger(CRCD(0xc0547d2f,"PlayListForbiddenTrackFlags2"),PlayListForbiddenTrackFlags2);
    pTemp->AddInteger(CRCD(0xb7534db9,"PlayListForbiddenTrackFlags3"),PlayListForbiddenTrackFlags3);
	pTemp->AddInteger(CRCD(0x2937d81a,"PlayListForbiddenTrackFlags4"),PlayListForbiddenTrackFlags4);

	// current_soundtrack only applies to XBox
	if (Config::GetHardware()==Config::HARDWARE_XBOX)
	{
		pTemp->AddChecksum(CRCD(0xe1f7c4ae,"current_soundtrack"),Script::GetChecksum("current_soundtrack"));
	}	
	
	if (Pcm::GetRandomMode())
	{
		pTemp->AddChecksum(NONAME,CRCD(0x31c71b70,"RandomMode"));
	}	
	
	pOptions->AddStructurePointer(CRCD(0x89eb9738,"SoundOptions"),pTemp);

	// Add the controller preferences.
	Script::CArray *pControllerPrefs=new Script::CArray;
	pControllerPrefs->SetSizeAndType(Mdl::Skate::vMAX_SKATERS, ESYMBOLTYPE_STRUCTURE);
	for (int i=0; imp_controller_preferences[i].AutoKickOn)
		{
			pTemp->AddChecksum(NONAME,CRCD(0x1eef7085,"AutoKickOn"));
		}	
		if (pSkate->mp_controller_preferences[i].SpinTapsOn)
		{
			pTemp->AddChecksum(NONAME,CRCD(0xa483ba67,"SpinTapsOn"));
		}	
		if (pSkate->mp_controller_preferences[i].VibrationOn)
		{
			pTemp->AddChecksum(NONAME,CRCD(0x73cee124,"VibrationOn"));
		}	
		pControllerPrefs->SetStructure(i,pTemp);
	}			
	
	pOptions->AddArrayPointer(CRCD(0x37bdb853,"ControllerPreferences"),pControllerPrefs);

	// Insert the taunt stuff
	GameNet::Manager * pGamenet = GameNet::Manager::Instance();
	pPreferences=pGamenet->GetTauntPreferences();
	Dbg_MsgAssert(pPreferences,("NULL taunt pPreferences"));
	pOptions->AddStructure(CRCD(0xe62b6586,"Taunts"),pPreferences->GetRoot());

	// Now insert the pointer to the newly constucted pOptions into the mem card structure.
	p_struct->AddStructurePointer(CRCD(0x2fca0578,"Options"),pOptions);

	// save the career (Game progress, flags and gap checklist)			
	Mdl::Skate::Instance()->GetCareer()->WriteIntoStructure(p_struct);

	// Add the game records.		  
	Records::CGameRecords *pGameRecords=pSkate->GetGameRecords();
	Dbg_MsgAssert(pGameRecords,("NULL pGameRecords"));
	pGameRecords->WriteIntoStructure(p_struct);
	
	// Wack in the pro skater profile info
	Script::CStruct *p_pros=new Script::CStruct;
	Obj::CPlayerProfileManager*	pPlayerProfileManager=pSkate->GetPlayerProfileManager();
	Dbg_MsgAssert(pPlayerProfileManager,("NULL pPlayerProfileManager"));
	pPlayerProfileManager->AddAllProProfileInfo(p_pros);
	p_struct->AddStructurePointer(CRCD(0xc9986baf,"Pros"),p_pros);
	
	// Note: Mustn't delete pOptions or the other structures created above
	// since pointers to these have been given to p_struct.
	// p_struct will clean them up when it gets deleted.
}

static void s_insert_story_info(CStruct *p_struct)
{
	// Save the goal manager parameters.
	Game::CGoalManager* p_goal_manager=Game::GetGoalManager();
	Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager"));
	
	CStruct *p_goal_manager_params=p_goal_manager->GetGoalManagerParams();
	Dbg_MsgAssert(p_goal_manager_params,("NULL p_goal_manager_params"));
	p_struct->AddStructure(CRCD(0xac7c2b62,"GoalManagerParams"),p_goal_manager_params);
}

static void s_insert_story_skater_info(CStruct *p_struct)
{
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = pSkate->GetSkater(0);
	Dbg_Assert( pSkater );
	pSkater->AddCATInfo(p_struct);
	
    // stat goal status
    pSkater->AddStatGoalInfo(p_struct);
    
    // chapter status
    pSkater->AddChapterStatusInfo(p_struct);
}

static void s_insert_custom_skater_info(CStruct *p_struct)
{
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CPlayerProfileManager*	pPlayerProfileManager=pSkate->GetPlayerProfileManager();
	Dbg_MsgAssert(pPlayerProfileManager,("NULL pPlayerProfileManager"));
	
	pPlayerProfileManager->AddCASProfileInfo(p_struct);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void s_insert_game_save_info(uint32 fileType, CStruct *p_struct)
{
	// WARNING !		WARNING !		WARNING !		WARNING !		WARNING !
	// Make sure that no function in here stores any pointers to new CStructs.
	// This is because all new CStructs (and CComponents) allocated here will be coming
	// off a special pool, to avoid overflowing the regular pool.
	
	Dbg_MsgAssert(p_struct,("NULL p_struct"));

	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	#ifdef	__NOPT_ASSERT__
	#if 0 		// Actually remove it fully, as the code that uses it had been temp stubbed out,a nd we don't want to confuse the non final build into thinking it's there
	Obj::CPlayerProfileManager*	pPlayerProfileManager=pSkate->GetPlayerProfileManager();
	Dbg_MsgAssert(pPlayerProfileManager,("NULL pPlayerProfileManager"));
	#endif
	#endif
	
	switch (fileType)
	{
		case 0xb010f357: // OptionsAndPros
		{
			Script::CStruct *p_global_info=new Script::CStruct;
			s_insert_global_info(p_global_info);
			p_struct->AddStructurePointer(CRCD(0xf55cbd13,"GlobalInfo"),p_global_info);
			
			Script::CStruct *p_story=new Script::CStruct;
			s_insert_story_info(p_story);
			p_struct->AddStructurePointer(CRCD(0x14a9fbc7,"Story"),p_story);
			
			Script::CStruct *p_story_skater=new Script::CStruct;
			s_insert_story_skater_info(p_story_skater);
			p_struct->AddStructurePointer(CRCD(0xdf2f448,"StorySkater"),p_story_skater);
			
			Script::CStruct *p_custom_skater=new Script::CStruct;
			s_insert_custom_skater_info(p_custom_skater);
			p_struct->AddStructurePointer(CRCD(0x12bfac82,"CustomSkater"),p_custom_skater);
			break;
		}

		case 0xca41692d: // NetworkSettings		
		{
			// Add the network preferences
			GameNet::Manager * pGamenet = GameNet::Manager::Instance();
			Prefs::Preferences *pPreferences=pGamenet->GetNetworkPreferences();
			p_struct->AppendStructure(pPreferences->GetRoot());
			break;
		}	
		
		case 0xffc529f4: // Cas
		{
			Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
			/*		
			pPlayerProfileManager->AddCASProfileInfo(p_struct);
            
            Obj::CSkater* pSkater = pSkate->GetSkater(0);
            Dbg_Assert( pSkater );
            pSkater->AddCATInfo(p_struct);
			*/
			break;
		}

        case 0x61a1bc57: // Cat
		{
			// Can only edit CAT on skater zero!
            Obj::CSkater* pSkater = pSkate->GetSkater(0);
            Dbg_Assert( pSkater );
            
            // Index is always 0 since that is the only one that can be edited directly.
            Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[0];
            Dbg_Assert( pCreatedTrick );

            //Other params
            p_struct->AddStructure( "other_params", pCreatedTrick->mp_other_params );
            
            //Rotation params
            p_struct->AddArray( "rotation_info", pCreatedTrick->mp_rotations );
            
            //Animation params
            p_struct->AddArray( "animation_info", pCreatedTrick->mp_animations );
        
            break;
		}
		
		case 0x3bf882cc: // Park	 (Save)
		{
			Ed::CParkManager::Instance()->WriteIntoStructure(p_struct);
			break;
		}
		
		case 0x26c80b0d: // Replay
		{
#if __USE_REPLAYS__
			Replay::AddReplayMemCardInfo(p_struct);
#endif			
			break;
		}

		case 0x62896edf: // CreatedGoals
		{
			Obj::CCompositeObject *p_obj=(Obj::CCompositeObject*)Obj::CTracker::Instance()->GetObject(CRCD(0x81f01058,"GoalEditor"));
			Dbg_MsgAssert(p_obj,("No GoalEditor object"));
			Obj::CGoalEditorComponent *p_goal_editor=GetGoalEditorComponentFromObject(p_obj);
			Dbg_MsgAssert(p_goal_editor,("No goal editor component ???"));
			p_goal_editor->WriteIntoStructure(p_struct);
			break;
		}
		
		default:
		{
			Dbg_MsgAssert(0,("Bad type of '%s' sent to s_insert_game_save_info",FindChecksumName(fileType)));
			break;
		}		
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Inserts summary info for the specified file type into p_struct.
// Summary info is a small amount of info that is written at the start of the mem card file
// so that it can be quickly read out without having to read the whole file.
// The info gets printed at the bottom of the files menu when the highlight is on a file.
static void s_generate_summary_info(CStruct *p_summaryInfo, uint32 fileType, CStruct *p_mainData)
{
	// WARNING !		WARNING !		WARNING !		WARNING !		WARNING !
	// Make sure that no function in here stores any pointers to new CStructs.
	// This is because all new CStructs (and CComponents) allocated here will be coming
	// off a special pool, to avoid overflowing the regular pool.
	
	Dbg_MsgAssert(p_summaryInfo,("NULL p_summaryInfo"));

	if (p_mainData)
	{
		// Extract the summary info from the passed p_mainData.
		// This is for when the save process is being done on data that got downloaded from the vault.
		
		switch (fileType)
		{
			case 0xb010f357: // OptionsAndPros
			{
				Script::CStruct *p_story=NULL;
				p_mainData->GetStructure(CRCD(0x14a9fbc7,"Story"),&p_story,Script::ASSERT);
				
				Script::CStruct *p_goal_manager_params=NULL;
				p_story->GetStructure(CRCD(0xac7c2b62,"GoalManagerParams"),&p_goal_manager_params,Script::ASSERT);

				CStruct *p_more_goal_manager_params=NULL;
				p_goal_manager_params->GetStructure(CRCD(0x23d4170a,"GoalManager_Params"),&p_more_goal_manager_params,Script::ASSERT);
				
				int chapter=0;
				p_more_goal_manager_params->GetInteger(CRCD(0xf884773c,"CurrentChapter"),&chapter);
				p_summaryInfo->AddInteger(CRCD(0xf884773c,"CurrentChapter"),chapter);
				
				// is_male is used by the script upload_content in net.q
				Script::CStruct *p_custom_skater=NULL;
				p_mainData->GetStructure(CRCD(0x12bfac82,"CustomSkater"),&p_custom_skater,Script::ASSERT);
				Script::CStruct *p_custom=NULL;
				p_custom_skater->GetStructure(CRCD(0xa7be964,"Custom"),&p_custom,Script::ASSERT);
				Script::CStruct *p_info=NULL;
				p_custom->GetStructure(CRCD(0x3476cea8,"Info"),&p_info,Script::ASSERT);
                int is_male=0;
				p_info->GetInteger(CRCD(0x3f813177,"is_male"),&is_male);
				p_summaryInfo->AddInteger(CRCD(0x3f813177,"is_male"),is_male);
                const char *p_name="";
                p_info->GetString(CRCD(0x2ab66cb8,"display_name"),&p_name);
				p_summaryInfo->AddString(CRCD(0xa1dc81f9,"name"),p_name);
				break;
			}	
			
			case 0xca41692d: // NetworkSettings		
			{
				p_summaryInfo->AddString("network_id","");
				break;
			}			
			
			case 0xffc529f4: // Cas
			{
				Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
				break;
			}
			
            case 0x61a1bc57: // Cat
            {
                Script::CStruct *p_data=NULL;
				p_mainData->GetStructure(CRCD(0x4137cb14,"other_params"),&p_data,Script::ASSERT);
                
                const char *p_name="";
				p_data->GetString(CRCD(0xa1dc81f9,"name"),&p_name,Script::ASSERT);

                p_summaryInfo->AddString(CRCD(0xa1dc81f9,"name"),p_name);
                break;
            }

			case 0x26c80b0d: // Replay
			{
				break;
			}
	
			case 0x3bf882cc: // Park
			{
				int num_edited_goals=0;
				
				Script::CArray *p_goals=NULL;
				Script::CStruct *p_goals_struct=NULL;
				p_mainData->GetStructure(CRCD(0xd8eb825e,"Park_editor_goals"),&p_goals_struct);
				if (p_goals_struct)
				{
					p_goals_struct->GetArray(CRCD(0x38dbe1d0,"Goals"),&p_goals);
					if (p_goals)
					{
						num_edited_goals=p_goals->GetSize();
					}
				}		
				p_summaryInfo->AddInteger(CRCD(0xe1ec606f,"num_edited_goals"),num_edited_goals);
				
				int max_players=1;
				p_mainData->GetInteger(CRCD(0xb7e39b53,"MaxPlayers"),&max_players);
				p_summaryInfo->AddInteger(CRCD(0xb7e39b53,"MaxPlayers"),max_players);
				
				Script::CArray *p_map=NULL;
				p_mainData->GetArray(CRCD(0x337c5289,"Park_editor_map"),&p_map,Script::ASSERT);
				
				// Used by the script upload_content in net.q
				int num_gaps=0;
				int num_metas=0;
				uint16 theme=0;
				uint32 tod_script=0;
				int width=0;
				int length=0;
				Ed::CParkManager::Instance()->GetSummaryInfoFromBuffer((uint8*)p_map->GetArrayPointer(),&num_gaps,&num_metas,&theme,&tod_script,&width,&length);
				p_summaryInfo->AddInteger(CRCD(0xe6121ed0,"num_gaps"),num_gaps);
				p_summaryInfo->AddInteger(CRCD(0xfff3dc35,"num_pieces"),num_metas);
				p_summaryInfo->AddInteger(CRCD(0x688a18f7,"theme"),theme);
				p_summaryInfo->AddChecksum(CRCD(0x4c72ed98,"tod_script"),tod_script);
				p_summaryInfo->AddInteger(CRCD(0x73e5bad0,"width"),width);
				p_summaryInfo->AddInteger(CRCD(0xfe82614d,"length"),length);
				break;
			}
				
			case 0x62896edf: // CreatedGoals
			{
				int num_edited_goals=0;
				
				Script::CArray *p_goals=NULL;
				p_mainData->GetArray(CRCD(0x38dbe1d0,"Goals"),&p_goals);
				if (p_goals)
				{
					num_edited_goals=p_goals->GetSize();
				}	
				p_summaryInfo->AddInteger(CRCD(0xe1ec606f,"num_edited_goals"),num_edited_goals);
				break;
			}
				
			default:
			{
				Dbg_MsgAssert(0,("Bad type of '%s' sent to s_generate_summary_info",FindChecksumName(fileType)));
				break;
			}		
		}		
	}
	else
	{
		// Get the summary info from the game.
		
		switch (fileType)
		{
			case 0xb010f357: // OptionsAndPros
			{
				Game::CGoalManager* p_goal_manager=Game::GetGoalManager();
				Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager"));
				
				CStruct *p_goal_manager_params=p_goal_manager->GetGoalManagerParams();
				Dbg_MsgAssert(p_goal_manager_params,("NULL p_goal_manager_params"));
	
				// Grab the summary info.
				CStruct *p_more_goal_manager_params=NULL;
				p_goal_manager_params->GetStructure(CRCD(0x23d4170a,"GoalManager_Params"),&p_more_goal_manager_params);
				Dbg_MsgAssert(p_more_goal_manager_params,("No GoalManager_Params structure ??"));
				
				int chapter=0;
				p_more_goal_manager_params->GetInteger(CRCD(0xf884773c,"CurrentChapter"),&chapter);
				p_summaryInfo->AddInteger(CRCD(0xf884773c,"CurrentChapter"),chapter);
				break;
			}	
			
			case 0xca41692d: // NetworkSettings		
			{
				GameNet::Manager * pGamenet = GameNet::Manager::Instance();
				Prefs::Preferences *pPreferences=pGamenet->GetNetworkPreferences();
				CStruct *p_net_stuff=pPreferences->GetRoot();
				
				// Grab the summary info.
				CStruct *p_foo=NULL;
				p_net_stuff->GetStructure("network_id",&p_foo);
				Dbg_MsgAssert(p_foo,("No network_id structure ?"));
				
				const char *p_name="";
				p_foo->GetString("ui_string",&p_name);
				
				p_summaryInfo->AddString("network_id",p_name);
				break;
			}			
			
			case 0xffc529f4: // Cas
			{
				Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));			
				break;
			}
	
			case 0x61a1bc57: // Cat
			{
				// Can only edit CAT on skater zero!
                Mdl::Skate * pSkate = Mdl::Skate::Instance();
                Obj::CSkater* pSkater = pSkate->GetSkater(0);
                Dbg_Assert( pSkater );
                
                // Index is always 0 since that is the only one that can be edited directly.
                Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[0];
                Dbg_Assert( pCreatedTrick );
    
                const char *p_name="";
				pCreatedTrick->mp_other_params->GetString(CRCD(0xa1dc81f9,"name"),&p_name,Script::ASSERT);

                p_summaryInfo->AddString(CRCD(0xa1dc81f9,"name"),p_name);
                break;
			}
			
			case 0x26c80b0d: // Replay
			{
	#if __USE_REPLAYS__
				Replay::AddReplayMemCardSummaryInfo(p_summaryInfo);
	#endif			
				break;
			}
			
			case 0x3bf882cc: // Park
			case 0x62896edf: // CreatedGoals
			{
				Obj::CCompositeObject *p_obj=(Obj::CCompositeObject*)Obj::CTracker::Instance()->GetObject(CRCD(0x81f01058,"GoalEditor"));
				Dbg_MsgAssert(p_obj,("No GoalEditor object"));
				Obj::CGoalEditorComponent *p_goal_editor=GetGoalEditorComponentFromObject(p_obj);
				Dbg_MsgAssert(p_goal_editor,("No goal editor component ???"));
				
				p_summaryInfo->AddInteger(CRCD(0xe1ec606f,"num_edited_goals"),p_goal_editor->GetNumGoals());
				
				if (fileType==CRCD(0x3bf882cc,"Park"))
				{
					p_summaryInfo->AddInteger(CRCD(0xb7e39b53,"MaxPlayers"),Ed::CParkManager::Instance()->GetGenerator()->GetMaxPlayers());
					
					int num_gaps=0;
					int num_metas=0;
					uint16 theme=0;
					uint32 tod_script=0;
					int width=0;
					int length=0;
					Ed::CParkManager::Instance()->WriteCompressedMapBuffer();		// Ensure map buffer is correct
					Ed::CParkManager::Instance()->GetSummaryInfoFromBuffer(Ed::CParkManager::Instance()->GetCompressedMapBuffer(),&num_gaps,&num_metas,&theme,&tod_script,&width,&length);
					p_summaryInfo->AddInteger(CRCD(0xe6121ed0,"num_gaps"),num_gaps);
					p_summaryInfo->AddInteger(CRCD(0xfff3dc35,"num_pieces"),num_metas);
					p_summaryInfo->AddInteger(CRCD(0x688a18f7,"theme"),theme);
					p_summaryInfo->AddChecksum(CRCD(0x4c72ed98,"tod_script"),tod_script);
					p_summaryInfo->AddInteger(CRCD(0x73e5bad0,"width"),width);
					p_summaryInfo->AddInteger(CRCD(0xfe82614d,"length"),length);
				}	
				break;
			}
				
			default:
			{
				Dbg_MsgAssert(0,("Bad type of '%s' sent to s_generate_summary_info",FindChecksumName(fileType)));
				break;
			}		
		}		
	}	
}

static void s_read_global_info(CStruct *p_globalInfo, CScript *p_script)
{
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	
	Script::CStruct *pOptions=NULL;
	p_globalInfo->GetStructure(CRCD(0x2fca0578,"Options"),&pOptions);
	Dbg_MsgAssert(pOptions,("p_globalInfo is missing Options structure"));
	
	// Extract the split screen preferences
	Script::CStruct *pTemp=NULL;
	pOptions->GetStructure(CRCD(0xf7720c3f,"SplitScreenPreferences"),&pTemp);
	
	Prefs::Preferences *pPreferences = pSkate->GetSplitScreenPreferences();
	Dbg_MsgAssert(pPreferences,("NULL split screen pPreferences"));
	pPreferences->SetRoot(pTemp);

	// Get the taunt preferences
	pTemp=NULL;
	pOptions->GetStructure(CRCD(0xe62b6586,"Taunts"),&pTemp);
	
	GameNet::Manager * pGamenet = GameNet::Manager::Instance();
	pPreferences=pGamenet->GetTauntPreferences();
	Dbg_MsgAssert(pPreferences,("NULL taunt pPreferences"));
	pPreferences->SetRoot(pTemp);

	
	// Get the sound options.
	pOptions->GetStructure(CRCD(0x89eb9738,"SoundOptions"),&pTemp);
	Sfx::CSfxManager * pSfxManager = Sfx::CSfxManager::Instance();
	float MainVolume=0;
	if (pTemp->GetFloat(CRCD(0x6f016dfb,"MainVolume"),&MainVolume))
	{
		pSfxManager->SetMainVolume(MainVolume);
	}	

	float MusicVolume=0;
	if (pTemp->GetFloat(CRCD(0xabd4a575,"MusicVolume"),&MusicVolume))
	{
		Pcm::SetVolume(MusicVolume);
	}	

	uint64 PlayListForbiddenTrackFlagsA=0;
    uint64 PlayListForbiddenTrackFlagsB=0;
	uint32 PlayListForbiddenTrackFlags1=0;
	uint32 PlayListForbiddenTrackFlags2=0;
    uint32 PlayListForbiddenTrackFlags3=0;
	uint32 PlayListForbiddenTrackFlags4=0;

	if (pTemp->GetInteger(CRCD(0x595d2c95,"PlayListForbiddenTrackFlags1"),(int*)&PlayListForbiddenTrackFlags1))
	{
		pTemp->GetInteger(CRCD(0xc0547d2f,"PlayListForbiddenTrackFlags2"),(int*)&PlayListForbiddenTrackFlags2);
        pTemp->GetInteger(CRCD(0xb7534db9,"PlayListForbiddenTrackFlags3"),(int*)&PlayListForbiddenTrackFlags3);
        pTemp->GetInteger(CRCD(0x2937d81a,"PlayListForbiddenTrackFlags4"),(int*)&PlayListForbiddenTrackFlags4);
		
		PlayListForbiddenTrackFlagsA=(((uint64)PlayListForbiddenTrackFlags1)<<32)+PlayListForbiddenTrackFlags2;
        PlayListForbiddenTrackFlagsB=(((uint64)PlayListForbiddenTrackFlags3)<<32)+PlayListForbiddenTrackFlags4;
		Pcm::SetPlaylist(PlayListForbiddenTrackFlagsA, PlayListForbiddenTrackFlagsB);
	}	

	if (pTemp->ContainsFlag(CRCD(0x31c71b70,"RandomMode")))
	{
		Pcm::SetRandomMode(true);
	}
	else
	{
		Pcm::SetRandomMode(false);
	}

	if (Config::GetHardware()==Config::HARDWARE_XBOX)
	{
		uint32 current_soundtrack=0xffffffff;
		pTemp->GetChecksum(CRCD(0xe1f7c4ae,"current_soundtrack"),¤t_soundtrack);
		
		Script::CSymbolTableEntry *p_sym=Script::LookUpSymbol(0xe1f7c4ae/*current_soundtrack*/);
		if (p_sym)
		{
			Dbg_MsgAssert(p_sym->mType==ESYMBOLTYPE_NAME,("Expected current_soundtrack to have type checksum"));
			p_sym->mChecksum=current_soundtrack;
		}	
		Script::CStruct* pTemp2 = new Script::CStruct;
        pTemp2->AddChecksum(CRCD(0x8c465905,"trackchecksum"),current_soundtrack);
        Script::RunScript(CRCD(0xd44400c2,"set_loaded_soundtrack"), pTemp2);
	}	

	// Extract the controller preferences
	Script::CArray *pControllerPrefs=NULL;
	if (pOptions->GetArray(CRCD(0x37bdb853,"ControllerPreferences"),&pControllerPrefs))
	{
		for (int i=0; iGetStructure(i);
			Dbg_MsgAssert(pPrefs,("NULL pPrefs?"));

			pSkate->SetVibration(i,pPrefs->ContainsFlag(CRCD(0x73cee124,"VibrationOn")));
			pSkate->SetAutoKick(i,pPrefs->ContainsFlag(CRCD(0x1eef7085,"AutoKickOn")));
			pSkate->SetSpinTaps(i,pPrefs->ContainsFlag(CRCD(0xa483ba67,"SpinTapsOn")));
		}	
	}
	
	// Extract the game records.		  
	Records::CGameRecords *pGameRecords=pSkate->GetGameRecords();
	Dbg_MsgAssert(pGameRecords,("NULL pGameRecords"));
	pGameRecords->ReadFromStructure(p_globalInfo);
	
	// Extract the pro & cas skater profile info
	Script::CStruct *p_pros=NULL;
	p_globalInfo->GetStructure(CRCD(0xc9986baf,"Pros"),&p_pros,Script::ASSERT);
	Obj::CPlayerProfileManager*	pPlayerProfileManager=pSkate->GetPlayerProfileManager();
	pPlayerProfileManager->LoadAllProProfileInfo(p_pros);

	// Extract the career info (Game progress, flags and gap checklist)			
	Mdl::Skate::Instance()->GetCareer()->ReadFromStructure(p_globalInfo);
	
	// Return the LastLevelLoadScript and LastGameMode to the calling script.
	// Needed by Zac so that after autoloading the game can automatically load up the
	// last level that was being played when saved.
	uint32 last_level_load_script=0;
	uint32 last_game_mode=0;
	CStruct *p_career=NULL;
	int current_theme=0;
	p_globalInfo->GetStructure(CRCD(0x4da4937b,"Career"),&p_career);
	if (p_career)
	{
		p_career->GetChecksum(CRCD(0xe3335d2f,"LastLevelLoadScript"),&last_level_load_script);
		p_career->GetChecksum(CRCD(0x2cc06f5e,"LastGameMode"),&last_game_mode);
		// Extract current theme
		p_career->GetInteger(CRCD(0xcc946ff3,"current_theme"),¤t_theme);
	}
	if (last_level_load_script)
	{
		p_script->GetParams()->AddChecksum(CRCD(0xe3335d2f,"LastLevelLoadScript"),last_level_load_script);
	}
	if (last_game_mode)
	{
		p_script->GetParams()->AddChecksum(CRCD(0x2cc06f5e,"LastGameMode"),last_game_mode);
	}
	//if (current_theme) TT7448: current_theme can be saved as zero
	//{
		Script::CStruct* pTemp2 = new Script::CStruct;
		pTemp2->AddInteger(CRCD(0x3bed2cf5,"theme_num"),current_theme);
		pTemp2->AddChecksum(CRCD(0x476cdadd,"loading_career"),0);
		Script::RunScript( "set_current_theme", pTemp2 );
		delete pTemp2;
   // }
	
	// Now, if the skater is custom, set his cas file name.
	Obj::CSkaterProfile* pSkaterProfile=pSkate->GetCurrentProfile();
	if (!pSkaterProfile->IsPro())
	{
		const char *pCASFileName="Unimplemented";
		p_globalInfo->GetString(CRCD(0xf36c1878,"CASFileName"),&pCASFileName);
		pSkaterProfile->SetCASFileName(pCASFileName);
	}
}
	
static void s_read_story_info(CStruct *p_storyInfo)
{
	// Load in the goal manager parameters.
	CStruct *p_loaded_goal_manager_params=NULL;
	p_storyInfo->GetStructure(CRCD(0xac7c2b62,"GoalManagerParams"),&p_loaded_goal_manager_params);
	Dbg_MsgAssert(p_loaded_goal_manager_params,("No goal manager params on mem card"));
	
	Game::CGoalManager* p_goal_manager=Game::GetGoalManager();
	Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager"));
	
	p_goal_manager->ResetCareer();
	
	CStruct *p_goal_manager_params=p_goal_manager->GetGoalManagerParams();
	Dbg_MsgAssert(p_goal_manager_params,("NULL p_goal_manager_params"));
	
	p_goal_manager_params->Clear();
	p_goal_manager_params->AppendStructure(p_loaded_goal_manager_params);

	// Refresh the goal manager with the new params.
	p_goal_manager->LevelLoad();
}

static void s_read_story_skater_info(CStruct *p_storySkaterInfo, CStruct *p_customSkater)
{
	// Which skater are we loading?
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	
	Obj::CPlayerProfileManager*	pPlayerProfileManager=pSkate->GetPlayerProfileManager();
	int index = pPlayerProfileManager->GetCurrentProfileIndex();
	Dbg_MsgAssert(index==0,("Should not be loading story skater info into any skater other than skater 0, tried to load it into skater %d",index));

	Obj::CSkater* pSkater = pSkate->GetSkater(index);
	Dbg_Assert( pSkater );
	pSkater->LoadCATInfo(p_storySkaterInfo);
    pSkater->LoadStatGoalInfo(p_storySkaterInfo);
    pSkater->LoadChapterStatusInfo(p_storySkaterInfo);
    
    pPlayerProfileManager->LoadCASProfileInfo(p_customSkater, true);
}

static void s_read_custom_skater_info(CStruct *p_customSkater)
{
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	
	Obj::CPlayerProfileManager*	pPlayerProfileManager=pSkate->GetPlayerProfileManager();
    int index = pPlayerProfileManager->GetCurrentProfileIndex();
    
    if ( index == 0 )
    {
        // if this is player one... don't apply the stats
        pPlayerProfileManager->LoadCASProfileInfo(p_customSkater, false);
    }
    else
    {
        // if this is any other skater, then apply the stats
        pPlayerProfileManager->LoadCASProfileInfo(p_customSkater, true);
    }
    
}

uint32 s_apply_flags=0;
bool s_did_apply_custom_skater_info=false;
static void s_read_game_save_info(uint32 fileType, CStruct *p_struct, CScript *p_script)
{
	Dbg_MsgAssert(p_struct,("NULL p_struct"));
	Dbg_MsgAssert(p_script,("NULL p_script"));
	
	s_did_apply_custom_skater_info=false;
	
	switch (fileType)
	{
		case 0xb010f357: // OptionsAndPros
		{
			Dbg_MsgAssert(s_apply_flags,("s_apply_flags is zero, need to call SetSectionsToApplyWhenLoading"));

            if (s_apply_flags & (1<GetStructure(CRCD(0xf55cbd13,"GlobalInfo"),&p_global_info,Script::ASSERT);
				s_read_global_info(p_global_info, p_script);
			}
			
			if (s_apply_flags & (1<GetStructure(CRCD(0x14a9fbc7,"Story"),&p_story_info,Script::ASSERT);
				s_read_story_info(p_story_info);
			}
			
            if (s_apply_flags & (1<GetStructure(CRCD(0x12bfac82,"CustomSkater"),&p_custom_skater_info,Script::ASSERT);
				s_read_custom_skater_info(p_custom_skater_info);
				s_did_apply_custom_skater_info=true;
			}

			if (s_apply_flags & (1<GetStructure(CRCD(0xdf2f448,"StorySkater"),&p_story_skater_info,Script::ASSERT);
                Script::CStruct *p_custom_skater_info=NULL;
				p_struct->GetStructure(CRCD(0x12bfac82,"CustomSkater"),&p_custom_skater_info,Script::ASSERT);
				s_read_story_skater_info(p_story_skater_info,p_custom_skater_info);
			}
			
            // Reset the flags to that the above assert will catch cases where the scripts 
			// have not called SetSectionsToApplyWhenLoading	
			s_apply_flags=0;
			break;
		}
		
		case 0xca41692d: // NetworkSettings		
		{
			// Extract the network preferences
			GameNet::Manager * pGamenet = GameNet::Manager::Instance();
			Prefs::Preferences *pPreferences=pGamenet->GetNetworkPreferences();
			Dbg_MsgAssert(pPreferences,("NULL network pPreferences"));
			pPreferences->SetRoot(p_struct);
			break;
		}
		
		case 0xffc529f4: // Cas
		{
			Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
			/*
			Mdl::Skate * pSkate = Mdl::Skate::Instance();
			Obj::CPlayerProfileManager*	pPlayerProfileManager=pSkate->GetPlayerProfileManager();
			//Script::PrintContents( p_struct );
			pPlayerProfileManager->LoadCASProfileInfo(p_struct);

            // Which skater are we loading?
            int index = pPlayerProfileManager->GetCurrentProfileIndex();

            Obj::CSkater* pSkater = pSkate->GetSkater(index);
            Dbg_Assert( pSkater );
            pSkater->LoadCATInfo(p_struct);
			*/
			break;
		}

        case 0x61a1bc57: // Cat
		{
			Mdl::Skate * pSkate = Mdl::Skate::Instance();

            Script::CStruct *p_other_params;
            Script::CArray *p_rotation_info;
            Script::CArray *p_animation_info;
            
            Obj::CSkater* pSkater = pSkate->GetLocalSkater();
            if ( pSkater )
            {
                //Params
                p_struct->GetStructure( "other_params", &p_other_params, Script::ASSERT );
                //pSkater->m_created_trick[0]->mp_other_params = p_other_params;
                pSkater->m_created_trick[0]->mp_other_params->AppendStructure( p_other_params );
                
                //Rotations
                p_struct->GetArray( "rotation_info", &p_rotation_info, Script::ASSERT );
                Script::CopyArray( pSkater->m_created_trick[0]->mp_rotations, p_rotation_info );
                
                //Animations
                p_struct->GetArray( "animation_info", &p_animation_info, Script::ASSERT );
                Script::CopyArray( pSkater->m_created_trick[0]->mp_animations, p_animation_info );
            }
			break;
		}
		
		case 0x3bf882cc: // Park  (read save game info)
		{
			Ed::CParkManager::Instance()->ReadFromStructure(p_struct);
			break;
		}
		
		case 0x26c80b0d: // Replay
		{
#if __USE_REPLAYS__
			uint32 level_structure_name=0;
			p_struct->GetChecksum("level_structure_name",&level_structure_name);
			Replay::SetLevelStructureName(level_structure_name);
			
			// Look up the load_script in the level structure, and return it to the calling
			// script so that after calling LoadFromMemoryCard the script can call the load_script,
			// which will load the appropriate level.
			CStruct *p_level_structure=GetStructure(level_structure_name);
			Dbg_MsgAssert(p_level_structure,("Could not find level structure '%s'",FindChecksumName(level_structure_name)));
			
			uint32 load_script=0;
			p_level_structure->GetChecksum("load_script",&load_script);
			p_script->GetParams()->AddChecksum("load_script",load_script);
#endif			
			break;
		}

		case 0x62896edf: // CreatedGoals
		{
			Obj::CCompositeObject *p_obj=(Obj::CCompositeObject*)Obj::CTracker::Instance()->GetObject(CRCD(0x81f01058,"GoalEditor"));
			Dbg_MsgAssert(p_obj,("No GoalEditor object"));
			Obj::CGoalEditorComponent *p_goal_editor=GetGoalEditorComponentFromObject(p_obj);
			Dbg_MsgAssert(p_goal_editor,("No goal editor component ???"));
			
			p_goal_editor->ReadFromStructure(p_struct);
			break;
		}
		
		default:
		{
			Dbg_MsgAssert(0,("Bad type of '%s' sent to s_read_game_save_info",FindChecksumName(fileType)));
			break;
		}		
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static int s_get_icon_k_required(uint32 fileType)
{
	switch (fileType)
	{
		case 0xb010f357: // OptionsAndPros
			return Script::GetInteger(CRCD(0x4925ed9e,"OptionsProsIconSpaceRequired"));
			break;
		case 0xca41692d: // NetworkSettings
			return Script::GetInteger(CRCD(0xf3431f63,"NetworkSettingsIconSpaceRequired"));
			break;
		case 0xffc529f4: // Cas
			Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));		
			//return Script::GetInteger("CASIconSpaceRequired");
			break;
        case 0x61a1bc57: // Cat
			return Script::GetInteger(CRCD(0x46b0ef40,"CATIconSpaceRequired"));
			break;
		case 0x3bf882cc: // Park
			return Script::GetInteger(CRCD(0x82aaf8dc,"ParkIconSpaceRequired"));
			break;
		case 0x26c80b0d: // Replay
			return Script::GetInteger(CRCD(0x7eaf934f,"ReplayIconSpaceRequired"));
			break;
		case 0x62896edf: // CreatedGoals
			return Script::GetInteger(CRCD(0x3205bc07,"CreatedGoalsIconSpaceRequired"));
			break;
		default:
			Dbg_MsgAssert(0,("Bad fileType of '%s' sent to s_get_icon_k_required",FindChecksumName(fileType)));
			break;
	}
	
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static int s_calculate_total_space_used_on_card(uint32 fileType, int fileSize)
{
	int blocks=s_round_up_to_platforms_block_size(fileSize)/s_get_platforms_block_size();

	switch (Config::GetHardware())
	{
	case Config::HARDWARE_PS2:
	case Config::HARDWARE_PS2_PROVIEW:
	case Config::HARDWARE_PS2_DEVSYSTEM:
	{
		// Add in the icon space required
		blocks+=s_get_icon_k_required(fileType);
		
		// Then add in the space used up by the mem card file system.
		blocks+=4;
		break;
	}
			
	case Config::HARDWARE_XBOX:
	{
		// Add in the icon space required
		blocks+=3;
		break;
	}
			
	case Config::HARDWARE_NGC:
		// All done, I think.
		break;
				
	default:
		break;
	}		
	
	return blocks;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// This returns the block size for mem card files.
// On some platforms, using a library function to get the file size will not
// return the original file size, but will return the size rounded up to the next
// multiple of the block size.
// In order that the calculated checksum be the same on loading, the files are always
// padded with zeros to be a multiple of the block size before the checksum is calculated.
static int s_get_platforms_block_size()
{
	switch (Config::GetHardware())
	{
		case Config::HARDWARE_PS2:
		case Config::HARDWARE_PS2_PROVIEW:
		case Config::HARDWARE_PS2_DEVSYSTEM:
			return 1024;
			break;
					
		case Config::HARDWARE_NGC:
			return 8192;
			break;
			
		case Config::HARDWARE_XBOX:
			return 16384;
			break;
		
		default:
			break;
	}
	
	return 1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int s_round_up_to_platforms_block_size(int fileSize)
{
	int block_size=s_get_platforms_block_size();
	if (fileSize%block_size)
	{
		return (1+fileSize/block_size)*block_size;
	}
	else
	{
		return fileSize;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static int s_get_version_number(uint32 fileType)
{
	switch (fileType)
	{
		case 0xb010f357: // OptionsAndPros
			return Script::GetInt( "VERSION_OPTIONSANDPROS", Script::ASSERT );
			break;
		case 0xffc529f4: // Cas
			Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
			//return Script::GetInt( "VERSION_CAS", Script::ASSERT );
			break;
        case 0x61a1bc57: // Cat
			return Script::GetInt( "VERSION_CAT", Script::ASSERT );
			break;
		case 0x3bf882cc: // Park
			return Script::GetInt( "VERSION_PARK", Script::ASSERT );
			break;
		case 0x26c80b0d: // Replay
			return Script::GetInt( "VERSION_REPLAY", Script::ASSERT );
			break;
		case 0xca41692d: // NetworkSettings
			return Script::GetInt( "VERSION_NETWORKSETTINGS", Script::ASSERT );
			break;
		case 0x62896edf: // CreatedGoals
			return Script::GetInt( "VERSION_CREATEDGOALS", Script::ASSERT );
			break;
		default:
			Dbg_MsgAssert(0,("Bad fileType of '%s' sent to s_get_version_number",FindChecksumName(fileType)));
			break;
	}
	return -1;
}

static const char *s_generate_xbox_directory_name(uint32 fileType, const char *p_name)
{
	static char p_directory_name[100];
	
	switch( fileType )
	{
		case 0xca41692d: // NetworkSettings
		{
			sprintf( p_directory_name,	"%s-NetworkSettings", p_name);
			break;
		}
		case 0xb010f357: // OptionsAndPros
		{
			sprintf( p_directory_name,	"%s-Story/Skater", p_name );
			break;
		}
		case 0xffc529f4: // Cas
		{
			Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
			//sprintf( p_directory_name,	"%s-Skater", p_name );
			break;
		}
        case 0x61a1bc57: // Cat
		{
			sprintf( p_directory_name,	"%s-Trick", p_name );
			break;
		}
		case 0x3bf882cc: // Park
		{
			sprintf( p_directory_name,	"%s-Park", p_name );
			break;
		}
		case 0x26c80b0d: // Replay
		{
			sprintf( p_directory_name,	"%s-Replay", p_name );
			break;
		}
		case 0x62896edf: // CreatedGoals
		{
			sprintf( p_directory_name,	"%s-Goals", p_name );
			break;
		}
		default:
		{
			Dbg_MsgAssert( 0, ( "Bad file type of '%s' for file '%s' sent to s_generate_xbox_directory_name", FindChecksumName(fileType),p_name));
			break;
		}		
	}
	Dbg_MsgAssert( strlen( p_directory_name ) < 100, ( "Oops" ));
	
	return p_directory_name;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// Returns false if error.
static bool s_make_xbox_dir_and_icons(	Mc::Card *p_card,
										uint32 fileType, const char *p_name, 
										char *p_card_file_name,
										bool *p_insufficientSpace)
{
	Dbg_MsgAssert(p_card_file_name,("NULL p_card_file_name"));
	p_card_file_name[0]=0;
	
	// Generate the directory name
	const char *p_directory_name=s_generate_xbox_directory_name(fileType,p_name);
	
	// Create the directory
	if (!p_card->MakeDirectory(p_directory_name))
	{
		if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
		{
			*p_insufficientSpace=true;
		}	
		return false;
	}	

	// Calculate the low-level file name.
	const char *p_low_level_directory_name=p_card->ConvertDirectory(p_directory_name);
	if (!p_low_level_directory_name)
	{
		return false;
	}	
	
	sprintf( p_card_file_name, "/%s/%s", p_low_level_directory_name, p_low_level_directory_name );
	Dbg_MsgAssert(strlen(p_card_file_name)Open( pFullIcoName, Mc::File::mMODE_WRITE | Mc::File::mMODE_CREATE );
	if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
	{
		*p_insufficientSpace=true;
	}	
	
	if( pFile )
	{   
		uint32 CardBytesWritten = pFile->Write( pIco, FileSize );
		if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
		{
			*p_insufficientSpace=true;
		}	
		
		if( CardBytesWritten == FileSize )
		{
			SavedOK = true;
		}
		if( !pFile->Flush())
		{
			SavedOK = false;
		}	
		if( !pFile->Close())
		{
			SavedOK = false;
		}	
		delete pFile;
	}
	Mem::Free( pIco );
	return SavedOK;	
}

static void s_generate_card_directory_name(uint32 fileType, const char *p_name, char *p_card_directory_name)
{
	Dbg_MsgAssert(p_card_directory_name,("NULL p_card_directory_name"));
	
	char p_ascii_checksum[20];
	s_generate_ascii_checksum(p_ascii_checksum,p_name,fileType);

	const char *p_header=Config::GetMemCardHeader();
	sprintf(p_card_directory_name,"/%s%s",p_header,p_ascii_checksum);
											
	Dbg_MsgAssert(strlen(p_card_directory_name)Open( p_icon_sys, Mc::File::mMODE_READ );
	if (pFile)
	{
		// Icon exists already, hence so must the directory, so no need to create it.
		pFile->Flush();
		pFile->Close();
		delete pFile;
		pFile=NULL;
		return true;
	}
	
	// Icon does not exist hence the directory cannot either, 
	// so create it and save out the icon.
	if (!p_card->MakeDirectory(p_directory_name))
	{
		if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
		{
			*p_insufficientSpace=true;
		}	
		return false;
	}	
	
	
	sceMcIconSys IconSys;

	sceVu0IVECTOR bgcolor[4] = 
	{
		{ 0x80,    0,    0, 0 },
		{    0, 0x80,    0, 0 },
		{    0,    0, 0x80, 0 },
		{ 0x80, 0x80, 0x80, 0 },
	};
	
	sceVu0FVECTOR lightdir[3] = 
	{
		{ 0.5, 0.0, 0.0, 0.0 },
		{ 0.0, 0.5, 0.0, 0.0 },
		{ 0.0, 0.0, 0.5, 0.0 },
	};

	sceVu0FVECTOR lightcol[3] = 
	{
		{ 0.50, 0.50, 0.50, 0.00 },
		{ 0.25, 0.25, 0.40, 0.00 },
		{ 0.80, 0.80, 0.80, 0.00 },		
	};
	
	sceVu0FVECTOR ambient = { 0.50, 0.50, 0.50, 0.00 };
	
	memset(&IconSys, 0, sizeof(IconSys));
	strcpy((char*)IconSys.Head, "PS2D");
	
	// Convert the title from ascii to shift-jis & write it in.
	//static char pTitle[100];
	char pTitle[100];
	
	char *pSourceIconFile="";
	char *pIcoName="";
	#ifdef __NOPT_ASSERT__
	uint32 ExpectedIcoSize=0;
	#endif

	int LineBreak=16;
	
	switch (fileType)
	{
		case 0xb010f357: // OptionsAndPros
		{
			strcpy(pTitle,Script::GetString(CRCD(0x130ab28a,"cfuncs_str_thugstoryskater")));
			LineBreak=strlen(pTitle);
			Dbg_MsgAssert(LineBreak<=16,("Mem card title '%s' exceeds 16 chars",pTitle));
			Dbg_MsgAssert(pTitle[LineBreak-1]==':',("Expected mem card file name '%s' to end in ':'",pTitle));
			strcat(pTitle,p_name);
			
			pIcoName="thps5.ico";
			pSourceIconFile="memcard\\thps3.icn";
			#ifdef __NOPT_ASSERT__
			ExpectedIcoSize=Script::GetInt(CRCD(0x4925ed9e,"OptionsProsIconSpaceRequired"));
			#endif
			break;
		}

		case 0xca41692d: // NetworkSettings
		{
			strcpy(pTitle,Script::GetString(CRCD(0x92c497ae,"cfuncs_str_thugnetsettings")));
			LineBreak=strlen(pTitle);
			Dbg_MsgAssert(LineBreak<=16,("Mem card title '%s' exceeds 16 chars",pTitle));
			Dbg_MsgAssert(pTitle[LineBreak-1]==':',("Expected mem card file name '%s' to end in ':'",pTitle));
			strcat(pTitle,p_name);
			
			// For the moment use the options/pros icon, until a new icon is made.
			// TODO: Once got a new icon file, remember to update ScriptDeleteMemCardFile too.
			pIcoName="network.ico";
			pSourceIconFile="memcard\\network.icn";
			#ifdef __NOPT_ASSERT__
			ExpectedIcoSize=Script::GetInt(CRCD(0xf3431f63,"NetworkSettingsIconSpaceRequired"));
			#endif
			break;
		}

		case 0xffc529f4: // Cas
		{
			Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
			/*
			#if ENGLISH
			sprintf(pTitle,"THUG Skater:%s",p_name);
			#else
			sprintf(pTitle,Script::GetLocalString(CRCD(0x7c2cfe2c,"cfuncs_str_thps3skater")),p_name);
			#endif
			LineBreak=12; // After 'THUG Skater:'
			
			pIcoName="cas.ico";
			pSourceIconFile="memcard\\cas.icn";
			#ifdef __NOPT_ASSERT__
			ExpectedIcoSize=Script::GetInt(CRCD(0xa79ee524,"CASIconSpaceRequired"));
			#endif
			*/
			break;
		}

        case 0x61a1bc57: // Cat
		{
			strcpy(pTitle,Script::GetString(CRCD(0x3aa2cffa,"cfuncs_str_thugtrick")));
			LineBreak=strlen(pTitle);
			Dbg_MsgAssert(LineBreak<=16,("Mem card title '%s' exceeds 16 chars",pTitle));
			Dbg_MsgAssert(pTitle[LineBreak-1]==':',("Expected mem card file name '%s' to end in ':'",pTitle));
			strcat(pTitle,p_name);
			
			pIcoName="tricks.ico";
			pSourceIconFile="memcard\\tricks.icn";
			#ifdef __NOPT_ASSERT__
			ExpectedIcoSize=Script::GetInt(CRCD(0x46b0ef40,"CATIconSpaceRequired"));
			#endif
			break;
		}
		
		case 0x62896edf: // CreatedGoals
		{
			strcpy(pTitle,Script::GetString(CRCD(0x257678cb,"cfuncs_str_thuggoals")));
			LineBreak=strlen(pTitle);
			Dbg_MsgAssert(LineBreak<=16,("Mem card title '%s' exceeds 16 chars",pTitle));
			Dbg_MsgAssert(pTitle[LineBreak-1]==':',("Expected mem card file name '%s' to end in ':'",pTitle));
			strcat(pTitle,p_name);
			
			pIcoName="goals.ico";
			pSourceIconFile="memcard\\goals.icn";
			#ifdef __NOPT_ASSERT__
			ExpectedIcoSize=Script::GetInt(CRCD(0x3205bc07,"CreatedGoalsIconSpaceRequired"));
			#endif
			break;
		}
		
		case 0x3bf882cc: // Park
		{
			strcpy(pTitle,Script::GetString(CRCD(0x2171fddc,"cfuncs_str_thugpark")));
			LineBreak=strlen(pTitle);
			Dbg_MsgAssert(LineBreak<=16,("Mem card title '%s' exceeds 16 chars",pTitle));
			Dbg_MsgAssert(pTitle[LineBreak-1]==':',("Expected mem card file name '%s' to end in ':'",pTitle));
			strcat(pTitle,p_name);
			
			pIcoName="parked.ico";
			pSourceIconFile="memcard\\parked.icn";
			#ifdef __NOPT_ASSERT__
			ExpectedIcoSize=Script::GetInt(CRCD(0x82aaf8dc,"ParkIconSpaceRequired"));
			#endif
			break;
		}
		
		default:
		{
			Dbg_MsgAssert(0,("Bad file type sent to s_create_mem_card_icon_file"));
			break;
		}		
	}

	// Convert the title to shift-jis and put it in IconSys.TitleName
	int Len=strlen(pTitle);
	Dbg_MsgAssert(Len<=32,("Title too long !!!"));
	pTitle[32]=0; // Just to be absolutely sure on non debug builds.
	
	char *pTit=(char*)IconSys.TitleName;
	for (int i=0; i>8)&0xff;
		*pTit++=Jis&0xff;
	}
	*pTit++=0;
	*pTit=0;

	
	IconSys.OffsLF = 2*LineBreak;
	IconSys.TransRate = 0x60;
	memcpy(IconSys.BgColor, bgcolor, sizeof(bgcolor));
	memcpy(IconSys.LightDir, lightdir, sizeof(lightdir));
	memcpy(IconSys.LightColor, lightcol, sizeof(lightcol));
	memcpy(IconSys.Ambient, ambient, sizeof(ambient));


	strcpy((char*)IconSys.FnameView, pIcoName);
	strcpy((char*)IconSys.FnameCopy, pIcoName);
	strcpy((char*)IconSys.FnameDel, pIcoName);

	
	// Now load the .ico file into memory
	uint32 FileSize=0;
	char *pIco=NULL;

	File::StopStreaming();
		
    void *pFP = File::Open(pSourceIconFile, "rb");
    Dbg_MsgAssert(pFP,("No %s file found.",pSourceIconFile));
	
    FileSize=File::GetFileSize(pFP);
	
	// Make sure the ico file size matches that specified in memcard.q.
	// The +1 is because the icon.sys takes up 1K.
	Dbg_MsgAssert(1+(((FileSize+0x3ff)&(~0x3ff)) >> 10)==ExpectedIcoSize,("%s icon size mismatch",Script::FindChecksumName(fileType)));
	
	// Allocate a buffer to hold the file.
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	// Allocating the size rounded up to a multiple of 2048 in case file reading only
	// reads a whole number of sectors.
    pIco=(char*)Mem::Malloc((FileSize+2047)&~2047);
    Dbg_MsgAssert(pIco,("Could not allocate memory for %s",pSourceIconFile));
	Mem::Manager::sHandle().PopContext();

	// Read the file into the buffer and close the file.
#ifdef __NOPT_ASSERT__
	long BytesRead=
#endif	
	File::Read(pIco,1,FileSize,pFP);
	Dbg_MsgAssert(BytesRead<=FileSize,("Bad rwfread when loading %s",pSourceIconFile));
    File::Close(pFP);
	
	
	bool SavedOK=false;

	pFile=p_card->Open( p_icon_sys, Mc::File::mMODE_WRITE | Mc::File::mMODE_CREATE );
	if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
	{
		*p_insufficientSpace=true;
	}	
	
	if (pFile)
	{   
		//printf("Writing %s, %d bytes\n",pIconSys,sizeof(IconSys));
		uint32 CardBytesWritten=pFile->Write( &IconSys, sizeof(IconSys) );
		if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
		{
			*p_insufficientSpace=true;
		}	
		
		if (CardBytesWritten==sizeof(IconSys))
		{
			SavedOK=true;
		}

		if (!pFile->Flush())
		{
			SavedOK=false;
		}	
		if (!pFile->Close())
		{
			SavedOK=false;
		}	
				
		delete pFile;
	}

	// Bail out straight away if the previous save failed so that the error code as
	// returned by Card::GetLastError is correct.
	if (!SavedOK)
	{
		Mem::Free(pIco);
		return false;
	}	

	SavedOK=false;
	char pFullIcoName[MAX_CARD_FILE_NAME_CHARS+1];
	sprintf(pFullIcoName,"%s/%s",p_directory_name,pIcoName);
	Dbg_MsgAssert(strlen(pFullIcoName)Open( pFullIcoName, Mc::File::mMODE_WRITE | Mc::File::mMODE_CREATE );
	if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
	{
		*p_insufficientSpace=true;
	}	
	
	if (pFile)
	{   
		//printf("Writing %s, %d bytes\n",pFullIcoName,FileSize);
		uint32 CardBytesWritten=pFile->Write( pIco, FileSize );
		if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
		{
			*p_insufficientSpace=true;
		}	
		
		if (CardBytesWritten==FileSize)
		{
			SavedOK=true;
		}

		if (!pFile->Flush())
		{
			SavedOK=false;
		}	
		if (!pFile->Close())
		{
			SavedOK=false;
		}	
				
		delete pFile;
	}
	
	Mem::Free(pIco);
	return SavedOK;
}
#else
static bool s_make_ps2_dir_and_icons(	Mc::Card *p_card,
										uint32 fileType, const char *p_name, 
										char *p_card_file_name,
										bool *p_insufficientSpace)
{
	return false;
}
#endif // #ifdef __PLAT_NGPS__

#ifdef __PLAT_NGC__
static bool s_insert_ngc_icon(	SMcFileHeader *p_fileHeader, 
								Mc::Card *p_card,
								Mc::File *p_file,
								uint32 fileType,
								const char *p_name)
{

	Dbg_MsgAssert(p_fileHeader,("NULL p_fileHeader"));
	Dbg_MsgAssert(p_card,("NULL p_card"));
	Dbg_MsgAssert(p_file,("NULL p_file"));
	Dbg_MsgAssert(p_name,("NULL p_name"));
	
	uint8 *p_buffer=p_fileHeader->mpIconData;
	
	char* p_icon_name="icons\\CreateSk8r_Icon_01.img.ngc";

	switch( fileType )
	{
		case 0xb010f357: // OptionsAndPros
		case 0xca41692d: // NetworkSettings	
		{
			break;
		}	
		
		case 0xffc529f4: // Cas
		{
			Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
			break;
		}

        case 0x61a1bc57: // CAT
		{
			p_icon_name = "icons\\Trick_Icon_01.img.ngc";
			break;
		}

		case 0x62896edf: // CreatedGoals
		{
			p_icon_name = "icons\\Goal_Icon_01.img.ngc";
			break;
		}
		
		case 0x3bf882cc: // Park
		{
			p_icon_name = "icons\\ParkEd_Icon_01.img.ngc";
			break;
		}
		
		default:
		{
			Dbg_MsgAssert( 0, ( "Bad file type" ));
			break;
		}		
	}
	
	// We used to have each of the save types in the switch statement above choose a different
	// name, one which contained the save type, ie "THUG: Skater"
	// We then had to change them to all have the same name, namely "Tony Hawk's Underground"
	// This is because Nintendo required that we prefix all our save names with Tony Hawk's Underground, but
	// when we tried to append the save type as well the text was too long for the IPL screen.
	strcpy((char*)p_buffer, Script::GetString(CRCD(0x6a6214bc,"cfuncs_str_ngc_generic_save_name")));
	Dbg_MsgAssert(strlen((const char*)p_buffer) < 32,("p_buffer '%s' too long",p_buffer));
	strcpy((char*)p_buffer + 32, p_name );

	// Find the index of the number character that precedes the .
	// This used to be set a hard wired value for each of the cases above, but changed to
	// calculate it instead cos the icon file names could quite likely change.
	int name_index=0;
	while (p_icon_name[name_index] && p_icon_name[name_index] != '.')
	{
		++name_index;
	}
	Dbg_MsgAssert(p_icon_name[name_index],("No '.' found in icon name '%s'",p_icon_name));
	--name_index;
		

	CARDStat stat;
	CARDGetStatus( p_card->GetSlot(), p_file->m_file_number, &stat );

	// Set up to write the banner and icon data.
	char* next_pixel = (char*)p_buffer + 64;

	// Load the banner file.
	void *p_FH = File::Open( "icons\\thps3_bannericon_01.img.ngc", "rb" );
	Dbg_MsgAssert( p_FH, ( "Couldn't open texture file %s\n", "icons\\thps3_bannericon_01.img.ngc" ) );

	int dummy;
	int width;
	int height;
	// Read header info.
	File::Read( &dummy, sizeof( int ), 1, p_FH );		// version
	File::Read( &dummy, sizeof( int ), 1, p_FH );		// checksum
	File::Read( &width, sizeof( int ), 1, p_FH );
	File::Read( &height, sizeof( int ), 1, p_FH );
	File::Read( &dummy, sizeof( int ), 1, p_FH );		// depth
	File::Read( &dummy, sizeof( int ), 1, p_FH );		// levels
	File::Read( &dummy, sizeof( int ), 1, p_FH );		// rwidth
	File::Read( &dummy, sizeof( int ), 1, p_FH );		// rheight
	File::Read( &dummy, sizeof( int ), 1, p_FH );		// has_holes/alphamap

	Dbg_MsgAssert( width == 96, ( "Bad mem card banner width" ));
	Dbg_MsgAssert( height == 32, ( "Bad mem card banner height" ));

	// Read image & palette data.
	File::Read( next_pixel, 96 * 32, 1, p_FH );
	next_pixel += ( 96 * 32 );
	File::Read( next_pixel, 256 * 2, 1, p_FH );
	next_pixel += ( 256 * 2 );

	File::Close( p_FH );

#	if 0
	// Write out a dummy array representing the banner image.
	OSReport( "unsigned short image[96*32] = {\n" );
	for( int i = 0; i < 32; ++i )
	{
		for( int j = 0; j < 96; ++j )
		{
			OSReport( "0x%x,", ((unsigned short*)p_texture->m_pPalette )[((unsigned char*)p_texture->m_pImage )[( i * 32 ) + j]] );
		}
		OSReport( "\n" );
	}
	OSReport( "};\n" );
#	endif

	// Load the icon files.
	for( int i = 0; i < 7; ++i )
	{
		p_icon_name[name_index] = '1' + i;

		// Load the icon file.
		void *p_FH = File::Open( p_icon_name, "rb" );
		Dbg_MsgAssert( p_FH, ( "Couldn't open icon file %s\n", p_icon_name ) );

		int dummy;
		int width;
		int height;
		// Read header info.
		File::Read( &dummy, sizeof( int ), 1, p_FH );		// version
		File::Read( &dummy, sizeof( int ), 1, p_FH );		// checksum
		File::Read( &width, sizeof( int ), 1, p_FH );
		File::Read( &height, sizeof( int ), 1, p_FH );
		File::Read( &dummy, sizeof( int ), 1, p_FH );		// depth
		File::Read( &dummy, sizeof( int ), 1, p_FH );		// levels
		File::Read( &dummy, sizeof( int ), 1, p_FH );		// rwidth
		File::Read( &dummy, sizeof( int ), 1, p_FH );		// rheight
		File::Read( &dummy, sizeof( int ), 1, p_FH );		// has_holes/alphamap

		Dbg_MsgAssert( width == 32, ( "Bad mem card banner width" ));
		Dbg_MsgAssert( height == 32, ( "Bad mem card banner height" ));

		// Read image & palette data.
		File::Read( next_pixel, 32 * 32, 1, p_FH );
		next_pixel += ( 32 * 32 );
		if( i == 6 )
		{
			File::Read( next_pixel, 256 * 2, 1, p_FH );
			next_pixel += 256*2;
		}

		File::Close( p_FH );

		// Icon formats.
		CARDSetIconFormat( &stat, i, CARD_STAT_ICON_C8 ); 

		// Anim speeds.
		CARDSetIconSpeed( &stat, i, CARD_STAT_SPEED_MIDDLE ); 
	}

	Dbg_MsgAssert(next_pixel==(char*)p_buffer+NGC_MEMCARD_ICON_DATA_SIZE,("Incorrect NGC_MEMCARD_ICON_DATA_SIZE, too big by %d bytes",NGC_MEMCARD_ICON_DATA_SIZE-(next_pixel-(char*)p_buffer)));
	stat.commentAddr	= (uint32)p_fileHeader->mpIconData-(uint32)p_fileHeader;
	stat.iconAddr		= stat.commentAddr + 64;	// End of comment strings.

	// Banner format.
	CARDSetBannerFormat( &stat, CARD_STAT_BANNER_C8 ); 
	CARDSetIconSpeed( &stat, 7, CARD_STAT_SPEED_END ); 
	CARDSetIconAnim( &stat, CARD_STAT_ANIM_LOOP );
	CARDSetStatus( p_card->GetSlot(), p_file->m_file_number, &stat );
	return true;
}
#else
static bool s_insert_ngc_icon(	SMcFileHeader *p_fileHeader, 
								Mc::Card *p_card,
								Mc::File *p_file,
								uint32 fileType,
								const char *p_name)
{
	return false;
}
#endif // #ifdef __PLAT_NGC__

static bool s_first_date_is_more_recent(Script::CStruct *p_a, Script::CStruct *p_b)
{
	int year_a=0;
	int month_a=0;
	int day_a=0;
	int hour_a=0;
	int minutes_a=0;
	int seconds_a=0;
	
	Dbg_MsgAssert(p_a,("NULL p_a"));
	p_a->GetInteger(CRCD(0x447d8cc8,"Year"),&year_a);
	p_a->GetInteger(CRCD(0x7149eff9,"Month"),&month_a);
	p_a->GetInteger(CRCD(0x1a5fd66f,"Day"),&day_a);
	p_a->GetInteger(CRCD(0x8fe1eeb1,"Hour"),&hour_a);
	p_a->GetInteger(CRCD(0x5f94b55c,"Minutes"),&minutes_a);
	p_a->GetInteger(CRCD(0xd029f619,"Seconds"),&seconds_a);

	int year_b=0;
	int month_b=0;
	int day_b=0;
	int hour_b=0;
	int minutes_b=0;
	int seconds_b=0;
	
	Dbg_MsgAssert(p_b,("NULL p_b"));
	p_b->GetInteger(CRCD(0x447d8cc8,"Year"),&year_b);
	p_b->GetInteger(CRCD(0x7149eff9,"Month"),&month_b);
	p_b->GetInteger(CRCD(0x1a5fd66f,"Day"),&day_b);
	p_b->GetInteger(CRCD(0x8fe1eeb1,"Hour"),&hour_b);
	p_b->GetInteger(CRCD(0x5f94b55c,"Minutes"),&minutes_b);
	p_b->GetInteger(CRCD(0xd029f619,"Seconds"),&seconds_b);

	bool more_recent=false;
	
	if (year_a < year_b)
	{
	}
	else if (year_a > year_b)
	{
		more_recent=true;
	}
	else if (month_a < month_b)
	{
	}
	else if (month_a > month_b)
	{
		more_recent=true;
	}
	else if (day_a < day_b)
	{
	}
	else if (day_a > day_b)
	{
		more_recent=true;
	}
	else if (hour_a < hour_b)
	{
	}
	else if (hour_a > hour_b)
	{
		more_recent=true;
	}
	else if (minutes_a < minutes_b)
	{
	}
	else if (minutes_a > minutes_b)
	{
		more_recent=true;
	}
	else if (seconds_a < seconds_b)
	{
	}
	else if (seconds_a > seconds_b)
	{
		more_recent=true;
	}
	else
	{
		// Exactly the same date/time, so call it more recent.
		more_recent=true;
	}
	
	return more_recent;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				
// @script | GetMostRecentSave | Finds the most recent save of the given type in the passed
// directory listing. Puts the result into a structure called MostRecentSave. Any existing
// parameter called MostRecentSave will be removed first. If there are no files, no MostRecentSave
// parameter will be created.
// @uparmopt [] | The directory listing. Must be an array of structures.
// @uparmopt name | The save type. If omitted it will return the most recent save of any type.
bool ScriptGetMostRecentSave(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->RemoveComponent("MostRecentSave");
	
	CArray *p_array=NULL;
	pParams->GetArray(NONAME,&p_array);
	
	if (!p_array)
	{
		return false;
	}	
	
	uint32 file_type=0;
	pParams->GetChecksum(NONAME,&file_type);

	CStruct *p_most_recent=NULL;
	for (uint32 i=0; iGetSize(); ++i)
	{
		CStruct *p_struct=p_array->GetStructure(i);
		
		uint32 this_file_type=0;
		p_struct->GetChecksum("file_type",&this_file_type);
		
		if (file_type && file_type!=this_file_type)
		{
			continue;
		}
		
		if (p_struct->ContainsFlag("BadVersion") ||
			p_struct->ContainsFlag("Corrupt"))
		{
			if (Config::GetHardware() == Config::HARDWARE_XBOX ||
				Config::GetHardware() == Config::HARDWARE_NGC)
			{
				// For the XBox, do not ignore bad files, so that an error message will appear on bootup
				// if the most recent save is bad. (TT6236)
				// Same for NGC (TT13519)
			}
			else
			{
				// For other platforms, find the most recent good save.
				continue;
			}	
		}	
			
		if (p_most_recent)
		{
			if (s_first_date_is_more_recent(p_struct, p_most_recent))
			{
				p_most_recent=p_struct;
			}	
		}	
		else
		{
			p_most_recent=p_struct;
		}	
	}
	
	if (p_most_recent)
	{
		pScript->GetParams()->AddStructure("MostRecentSave",p_most_recent);
        return true;
	}
	
	return false;	
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				
// @script | GetMemCardSpaceAvailable | Puts the amount of space left on the mem card
// into the parameter SpaceAvailable. Units are K for the PS2
bool ScriptGetMemCardSpaceAvailable(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AddInteger(CRCD(0xc37c363,"SpaceAvailable"),0);
	pScript->GetParams()->AddInteger(CRCD(0x855b2fc,"FilesLeft"),1000000);

	Mc::Manager * mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=mc_man->GetCard(0,0);
	if (p_card)
	{
		pScript->GetParams()->AddInteger(CRCD(0xc37c363,"SpaceAvailable"),p_card->GetNumFreeClusters());
		#ifdef __PLAT_NGC__
		pScript->GetParams()->AddInteger(CRCD(0x855b2fc,"FilesLeft"),p_card->CountFilesLeft());
		#endif
			
		return true;
	}	
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetMemCardSpaceRequired | Calculates the amount of space required to save the specified
// file type to the memory card, and puts it into the parameter SpaceRequired.
// Units are K for the PS2.
// @uparm name | The save type.
bool ScriptGetMemCardSpaceRequired(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 file_type=0;
	pParams->GetChecksum(NONAME,&file_type);
	
	// s_calculate_total_space_used_on_card adds in the space used by the icons.
	int space_required=s_calculate_total_space_used_on_card(file_type,sGetFixedFileSize(file_type));

	pScript->GetParams()->AddInteger("SpaceRequired",space_required);
	return true;
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				
// @script | MemCardFileExists | returns true if the file already exists
// on the card
// @parm string | Name | the name of the file
// @parm name | type | the type of the file
bool ScriptMemCardFileExists(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mc::Manager * mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=mc_man->GetCard(0,0);
	if (!p_card)
	{
		return false;
	}
		
	const char *p_name=NULL;
	pParams->GetText("Name",&p_name);
	
	uint32 file_type=0;
	pParams->GetChecksum("Type",&file_type);

	if (!p_name || !file_type)
	{
		return false;
	}
		
	char p_card_file_name[MAX_CARD_FILE_NAME_CHARS+1];
	p_card_file_name[0]=0;

	const char *p_xbox_directory_name=NULL;
	const char *p_xbox_converted_directory_name=NULL;
	
	switch (Config::GetHardware())
	{
		case Config::HARDWARE_XBOX:
		{
			p_xbox_directory_name=s_generate_xbox_directory_name(file_type,p_name);
			p_xbox_converted_directory_name=p_card->ConvertDirectory(p_xbox_directory_name);
			if (!p_xbox_converted_directory_name)
			{
				return false;
			}	
			sprintf(p_card_file_name,"/%s/%s",p_xbox_converted_directory_name,p_xbox_converted_directory_name);
			Dbg_MsgAssert(strlen(p_card_file_name)Open( p_card_file_name, Mc::File::mMODE_READ );
	if (pFile)
	{
		int file_size=pFile->Seek(0,Mc::File::BASE_END);
		int total_size_on_card=s_calculate_total_space_used_on_card(file_type,file_size);
		pScript->GetParams()->AddInteger("total_file_size",total_size_on_card);
	
		pFile->Flush();
		pFile->Close();
		delete pFile;
		return true;
	}
	
	if (Config::GetHardware()==Config::HARDWARE_XBOX)
	{
		// Better delete the directory we just created ...
		Dbg_MsgAssert(p_xbox_directory_name,("NULL p_xbox_directory_name ?"));
		p_card->DeleteDirectory(p_xbox_directory_name);
	}
	return false;		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				
// @script | DeleteMemCardFile | Deletes the specified file from the memory card
// @parmopt string | CardFileName | | The name of the file as stored on the memory card. If this is
// not specified then it is derived from UserFileName and Type
// @parmopt string | UserFileName | | The name of the file as it appears in the files menu
// @parm name | Type | The file type (NetworkSettings, OptionsAndPros, Cas, Park, Replay)

bool ScriptDeleteMemCardFile(Script::CStruct *pParams, Script::CScript *pScript)
{
	if ( Config::GetHardware() != Config::HARDWARE_XBOX)
	{
		Pcm::PauseMusic(true);
		Pcm::PauseStream(true);
	}
	
	Mc::Manager * mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=mc_man->GetCard(0,0);
	if (!p_card)
	{
		if ( Config::GetHardware() != Config::HARDWARE_XBOX)
		{
			Pcm::PauseMusic( -1 );
			Pcm::PauseStream( -1 );
		}	
		return false;
	}
		
	bool delete_succeeded=true;
	
	const char *p_xbox_directory_name=NULL;
	pParams->GetString("XBoxDirectoryName",&p_xbox_directory_name);
	const char *p_full_file_name=NULL;
	pParams->GetString("CardFileName",&p_full_file_name);
	uint32 file_type=0;
	pParams->GetChecksum("Type",&file_type);
	const char *p_name="";
	pParams->GetString("UserFileName",&p_name);

	
	switch (Config::GetHardware())
	{
		case Config::HARDWARE_NGC:
		{
			if (p_full_file_name)
			{
				if (!p_card->Delete(p_full_file_name))
				{
					delete_succeeded=false;
				}	
			}
			else
			{
				char p_temp[MAX_CARD_FILE_NAME_CHARS+1];
				
				// If p_full_file_name is not specified then derive it from Type and UserFileName
				
				const char *p_header=Config::GetMemCardHeader();
				char p_ascii_checksum[20];
				s_generate_ascii_checksum(p_ascii_checksum,p_name,file_type);
		
				sprintf(p_temp,"%s%s%s%s",p_header,p_ascii_checksum,
									      p_header,p_ascii_checksum);
				Dbg_MsgAssert(strlen(p_temp)Delete(p_temp))
				{
					delete_succeeded=false;
				}	
			}
			break;
		}
		
		case Config::HARDWARE_XBOX:
		{
			if (!p_xbox_directory_name)
			{
				// Derive p_xbox_directory_name from the file type and user file name
				p_xbox_directory_name=s_generate_xbox_directory_name(file_type,p_name);
				
			}
			if (!p_card->DeleteDirectory( p_xbox_directory_name ))
			{
				delete_succeeded=false;
			}
			break;
		}
		
		case Config::HARDWARE_PS2:
		case Config::HARDWARE_PS2_PROVIEW:
		case Config::HARDWARE_PS2_DEVSYSTEM:
		{
			char p_temp[MAX_CARD_FILE_NAME_CHARS+1];
			
			if (!p_full_file_name)
			{
				// If p_full_file_name is not specified then derive it from Type and UserFileName
				
				const char *p_header=Config::GetMemCardHeader();
				char p_ascii_checksum[20];
				s_generate_ascii_checksum(p_ascii_checksum,p_name,file_type);
		
				sprintf(p_temp,"/%s%s/%s%s",p_header,p_ascii_checksum,
											p_header,p_ascii_checksum);
				Dbg_MsgAssert(strlen(p_temp)Delete(p_full_file_name))
			{
				delete_succeeded=false;
			}	
			
			// Delete the icon.sys file
			char p_buf[100];
			sprintf(p_buf,"%s/icon.sys",p_directory_name);
			Dbg_MsgAssert(strlen(p_buf)<100,("Oops"));
			if (!p_card->Delete(p_buf))
			{
				delete_succeeded=false;
			}	
			
			// Delete the .ico file.
			switch (file_type)
			{
				case 0xffc529f4: // Cas
				{
					Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
					//sprintf(p_buf,"%s/cas.ico",p_directory_name);
					break;
				}

                case 0x61a1bc57: // CAT
				{
					sprintf(p_buf,"%s/tricks.ico",p_directory_name);
					break;
				}
				
				case 0x3bf882cc: // Park
				{
					sprintf(p_buf,"%s/parked.ico",p_directory_name);
					break;
				}
				
				case 0x26c80b0d: // Replay
				{
					sprintf(p_buf,"%s/replay.ico",p_directory_name);
					break;
				}
				
				case 0xb010f357: // OptionsAndPros
				{
					sprintf(p_buf,"%s/thps5.ico",p_directory_name);
					break;
				}
				
				case 0x62896edf: // CreatedGoals
				{
					sprintf(p_buf,"%s/goals.ico",p_directory_name);
					break;
				}

				case 0xca41692d: // NetworkSettings
				{
					// TODO: Change this once got a new icon file for the net settings
					sprintf(p_buf,"%s/network.ico",p_directory_name);
					break;
				}
				
				default:
				{
					Dbg_MsgAssert(0,("Bad file type sent to DeleteMemCardFile"));
					break;
				}		
			}
			Dbg_MsgAssert(strlen(p_buf)<100,("Oops"));
			if (!p_card->Delete(p_buf))
			{
				delete_succeeded=false;
			}	
	
			// Delete the directory
			if (!p_card->DeleteDirectory(p_directory_name))
			{
				delete_succeeded=false;
			}	
			break;
		}
		default:
		{
			delete_succeeded=false;
			break;
		}	
	}
		
	if ( Config::GetHardware() != Config::HARDWARE_XBOX)
	{
		Pcm::PauseMusic( -1 );
		Pcm::PauseStream( -1 );
	}
	
	return delete_succeeded;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | FormatCard | Formats the memory card
bool ScriptFormatCard(Script::CStruct *pParams, Script::CScript *pScript)
{
	Pcm::PauseMusic(true);
	Pcm::PauseStream(true);
	
	Mc::Manager * mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=mc_man->GetCard(0,0);
	bool Worked=false;
	if (p_card)
	{
		Worked=p_card->Format();
	}

	Pcm::PauseMusic( -1 );
	Pcm::PauseStream( -1 );
	return Worked;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CardIsInSlot | returns true if the memory card is in the slot
bool ScriptCardIsInSlot(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Mc::Manager * mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=mc_man->GetCard(0,0);
	if (p_card)
	{
		return true;
	}
	else
	{
		#ifdef __PLAT_NGC__
		// On the NGC, p_card will also be NULL if the card is in the slot, but is broken.
		if (mc_man->GotFatalError())
		{
			return true;
		}	
		
		// On the NGC, p_card will also be NULL if something is in the slot,
		// but gives a 'wrong device' error
		if (mc_man->GotWrongDevice())
		{
			return true;
		}	
		#endif
		return false;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CardIsFormatted | returns true if the memory card is formatted
bool ScriptCardIsFormatted(Script::CStruct *pParams, Script::CScript *pScript)
{
	Mc::Manager * mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=mc_man->GetCard(0,0);
	if (p_card)
	{
		return p_card->IsFormatted();
	}
	else
	{
		// If there is no card, then I guess it isn't formatted.
		return false;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SaveFailedDueToInsufficientSpace | 
bool ScriptSaveFailedDueToInsufficientSpace(Script::CStruct *pParams, Script::CScript *pScript)
{
	return s_insufficient_space;
}
		
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetSummaryInfo	| 
bool ScriptGetSummaryInfo(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 file_type=0;
	pParams->GetChecksum(NONAME,&file_type);
	
	int use_vault_data=0;
	pParams->GetInteger(CRCD(0xc78dabf6,"VaultData"),&use_vault_data);
	
	if (use_vault_data)
	{
		file_type=sVaultDataType;
		Dbg_MsgAssert(spVaultData,("Called GetSummaryInfo on the vault data when there was no vault data"));
		s_generate_summary_info(pScript->GetParams(), file_type, spVaultData);
	}
	else
	{
		if (file_type==0xb010f357) // OptionsAndPros
		{
			// Force the goal manager to refresh the goal manager params structure.
			// Note that this cannot be done inside s_generate_summary_info, because LevelUnload()
			// will create a structure and store a pointer to it.
			// In other places SSwitchToNextPool() will be called before calling s_generate_summary_info,
			// and we don't want the structure created by LevelUnload() to come off the special memcard pool.
			Game::CGoalManager* p_goal_manager=Game::GetGoalManager();
			Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager"));
			p_goal_manager->LevelUnload();
		}
		
		s_generate_summary_info(pScript->GetParams(), file_type, NULL);
	}
		
	return true;
}

#if __USE_REPLAYS__
static int sGetReplayDataSize(int num_dummies)
{
	return 	sizeof(Replay::SReplayDataHeader)+
			num_dummies*sizeof(Replay::SSavedDummy)+
			Replay::GetBufferSize()+
			4; // The checksum of that lot
}			
#endif

static uint32 sGetFixedFileSize(uint32 fileType)
{
	uint32 fixed_size=0;
	
	switch (fileType)
	{
		case 0xb010f357: // OptionsAndPros
			fixed_size=Script::GetInteger(CRCD(0xe7f51ffe,"SaveSize_OptionsAndPros"),Script::ASSERT);
			break;
		case 0xffc529f4: // Cas
			Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
			//fixed_size=Script::GetInteger("SaveSize_Cas",Script::ASSERT);
			break;
        case 0x61a1bc57: // Cat
			fixed_size=Script::GetInteger(CRCD(0x9eecdec9,"SaveSize_Cat"),Script::ASSERT);
			break;
		case 0x62896edf: // CreatedGoals
			fixed_size=Script::GetInteger(CRCD(0x3308efc0,"SaveSize_CreatedGoals"),Script::ASSERT);
			break;
		case 0x3bf882cc: // Park
			fixed_size=Script::GetInteger(CRCD(0x2cb071ed,"SaveSize_Park"),Script::ASSERT);
			break;
		case 0x26c80b0d: // Replay
			fixed_size=Script::GetInteger(CRCD(0x8ee3aa00,"SaveSize_Replay"),Script::ASSERT);
			break;
		case 0xca41692d: // NetworkSettings
			fixed_size=Script::GetInteger(CRCD(0x651c978d,"SaveSize_NetworkSettings"),Script::ASSERT);
			break;
		default:
			Dbg_MsgAssert(0,("Bad file_type of %s",Script::FindChecksumName(fileType)));
			break;
	}
	fixed_size+=sizeof(SMcFileHeader);
	fixed_size=s_round_up_to_platforms_block_size(fixed_size);
	return fixed_size;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SaveToMemoryCard | 
bool ScriptSaveToMemoryCard(Script::CStruct *pParams, Script::CScript *pScript)
{
	if (Config::GetHardware() == Config::HARDWARE_NGC)
	{
		// On the GameCube, the temp mem card pools only exist for the duration of this function,
		// because they use space normally used for rendering, hence cannot exist during the mem card scripts
		// which may execute over many frames.
		ScriptCreateTemporaryMemCardPools(NULL,NULL);
	}
		
	if ( Config::GetHardware() != Config::HARDWARE_XBOX)
	{
		Pcm::PauseMusic(true);
		Pcm::PauseStream(true);
	}
	
	s_insufficient_space=false;
	
	const char *p_name="";
	pParams->GetText("Name",&p_name);

	uint32 file_type=0;
	pParams->GetChecksum("Type",&file_type);

	// If the SaveVaultData flag is set, it will save the stored spVaultData, which
	// will have been loaded from the online vault.
	int save_vault_data=0;
	pParams->GetInteger(CRCD(0xbb609e73,"SaveVaultData"),&save_vault_data);
	if (save_vault_data)
	{
		Dbg_MsgAssert(spVaultData,("Called SaveToMemoryCard with no vault data"));
		file_type=sVaultDataType;
	}	


	if (!save_vault_data && file_type==0xb010f357) // OptionsAndPros
	{
		// Force the goal manager to refresh the goal manager params structure.
		// This has to be done before calling SSwitchToNextPool(), because LevelUnload()
		// will create a structure and store a pointer to it.
		// It is best for the special pools to be kept empty outside of GetMemCardSpaceRequired
		// or SaveToMemoryCard, otherwise saving the game could run out of pool space.
		Game::CGoalManager* p_goal_manager=Game::GetGoalManager();
		Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager"));
		p_goal_manager->LevelUnload();
	}


	// If the special pools exist, switch to them so that the components & structs etc get allocated off them.
	// The special pools use the space freed by unloading the skater anims, and are for preventing memory
	// overflows when the save size gets large.
	Dbg_MsgAssert(CComponent::SGetCurrentPoolIndex()==0,("Bad current CComponent pool"));
	Dbg_MsgAssert(CStruct::SGetCurrentPoolIndex()==0,("Bad current CStruct pool"));
	Dbg_MsgAssert(CVector::SGetCurrentPoolIndex()==0,("Bad current CVector pool"));
	bool got_special_pools=false;
	CComponent::SSwitchToNextPool();
	if (CComponent::SPoolExists())
	{
		CStruct::SSwitchToNextPool();
		Dbg_MsgAssert(CStruct::SPoolExists(),("No special CStruct pool ?"));
		CVector::SSwitchToNextPool();
		Dbg_MsgAssert(CVector::SPoolExists(),("No special CVector pool ?"));
		
		got_special_pools=true;
	}
	else
	{
		CComponent::SSwitchToPreviousPool();
	}	

	
	#ifdef	__NOPT_ASSERT__
	int initial_ccomponent_num_used_items=CComponent::SGetNumUsedItems();
	int initial_cstruct_num_used_items=CStruct::SGetNumUsedItems();
	int initial_cvector_num_used_items=CVector::SGetNumUsedItems();
	#endif

	// WARNING !		WARNING !		WARNING !		WARNING !		WARNING !
	// Between here and SSwitchToPreviousPool(), all CStruct's and CComponent's will be allocated
	// off a special pool.
	// This is to avoid overflowing the regular pools with these temporary big structures.
	// Make sure that no function, or function that it calls, stores any pointers to new CStructs
	// between here and SSwitchToPreviousPool().
	// This is just to keep the special pool clean. Things will get confusing if other stuff
	// gets allocated off it and uses up space there, cos then saving to mem card could start
	// running out of pool again.

	Script::CStruct *pMemCardStuff=new Script::CStruct;
	if (save_vault_data)
	{
		pMemCardStuff->AppendStructure(spVaultData);
	}
	else
	{
		s_insert_game_save_info(file_type,pMemCardStuff);	
	}	
	//Script::PrintContents(pMemCardStuff);
	
	Script::CStruct *pSummaryInfo=new Script::CStruct;
	s_generate_summary_info(pSummaryInfo,file_type,pMemCardStuff);	
	
	// Put the user filename into the summary info too.
	pSummaryInfo->AddString("filename",p_name);

	#ifdef	__NOPT_ASSERT__
	printf("Save type = '%s'\n",Script::FindChecksumName(file_type));
	printf("Num CComponents used by save = %d\n",CComponent::SGetNumUsedItems()-initial_ccomponent_num_used_items);
	printf("Num CStructs used by game save = %d\n",CStruct::SGetNumUsedItems()-initial_cstruct_num_used_items);
	printf("Num CVectors used by game save = %d\n",CVector::SGetNumUsedItems()-initial_cvector_num_used_items);
	#endif


	Mc::File* pFile=NULL;
	uint8 *pTemp=NULL;
	uint8 *p_pad=NULL;
	uint32 pad_size=0;
	bool SavedOK=false;

	uint32 CardBytesWritten=0;
	uint8 *p_dest=NULL;
	SMcFileHeader *p_file_header=NULL;
	uint32 BytesWritten=0;
	
	Mc::Manager * mc_man=NULL;
	Mc::Card* p_card=NULL;
	
	// fixed_size is the hard-wired file size as defined in memcard.q, rounded up to
	// the platform's block size. A file of exactly this size will be opened for writing.
	// The code will assert if the required space is greater than this.
	// The file sizes are required to be fixed as a TRC thing for GameCube.
	uint32 fixed_size=sGetFixedFileSize(file_type);
	
	// header_and_structures_size is the exact space used by the header, summary info, and 
	// game save info structures. It is not rounded up to the platform's block size.
	// For all save types except replays, this comprises all the info in the file.
	// Replay's have the replay data tagged on the end of the file instead, because there
	// is not enough memory to keep it in the structures.
	uint32 header_and_structures_size=0;

	
	// This is the size of the replay data, if any. Not rounded up to the platform's block size.
	uint32 replay_data_size=0;

#if __USE_REPLAYS__
	// The big replay data has to be tagged on the end rather than being put inside pMemCardStuff
	// since there is not enough memory to make a copy of the replay data there.
	Replay::SReplayDataHeader replay_data_header;
	if (file_type==0x26c80b0d/* Replay */)
	{
		Replay::WriteReplayDataHeader(&replay_data_header);
		replay_data_size=sGetReplayDataSize(replay_data_header.mNumStartStateDummies);
	}	
#endif	
	
	/////////////////////////////////////////////////////////////////////////
	//pMemCardStuff->PrintContents();
	//printf("Space required = %d\n",pMemCardStuff->CalculateBufferSize());
	/////////////////////////////////////////////////////////////////////////
	
	// Get how much space is needed to store the structures.
	// Calculating this before opening the file so that if CalculateBufferSize fails,
	// the file won't be left with zero size, erasing the previous save game.
	// Passing fixed_size as the size of the temp buffer used when calculating the actual size needed.
	uint32 structure_size=CalculateBufferSize(pMemCardStuff, fixed_size);
	uint32 summary_info_size=CalculateBufferSize(pSummaryInfo,MAX_SUMMARY_INFO_SIZE);
	Dbg_MsgAssert(summary_info_size<=MAX_SUMMARY_INFO_SIZE,("summary_info_size too big !!"));
	header_and_structures_size=sizeof(SMcFileHeader)+summary_info_size+structure_size;

	uint32 required_size=header_and_structures_size+replay_data_size;
	
	#ifdef __NOPT_ASSERT__
	Dbg_MsgAssert(required_size <= fixed_size,("Need to update fixed file size for type '%s'\nMin required size = %d",Script::FindChecksumName(file_type),required_size));
	printf("required_size=%d, fixed_size=%d, %d percent\n",required_size,fixed_size,(100*required_size)/fixed_size);
	if (required_size > fixed_size)
	{
		goto ERROR;
	}	
	#endif

	pad_size=fixed_size-required_size;
	if (pad_size && Config::GetHardware() != Config::HARDWARE_NGC)
	{
		p_pad=(uint8*)Mem::Malloc(pad_size);
		for (uint32 i=0; iGetCard(0,0);
	if (!p_card)
	{
		goto ERROR;
	}
	// GameCube often crashes if try to do card operations on a bad card, so do this check first.
	if (!p_card->IsFormatted())
	{
		goto ERROR;
	}
	
	char p_card_file_name[MAX_CARD_FILE_NAME_CHARS+1];
	switch (Config::GetHardware())
	{
		case Config::HARDWARE_PS2:
		case Config::HARDWARE_PS2_PROVIEW:
		case Config::HARDWARE_PS2_DEVSYSTEM:
			if (!s_make_ps2_dir_and_icons(p_card,
										  file_type,p_name,
										  p_card_file_name,
										  &s_insufficient_space))
			{
				goto ERROR;
			}
			break;
			
		case Config::HARDWARE_NGC:
		{
			// On the NGC, just generate the card file name.
			// The icon cannot be created at this point because on the NGC the
			// icon images have to be stored in the file data itself, so we have to
			// wait until after the file is opened.
			char p_directory_name[MAX_CARD_FILE_NAME_CHARS+1];
			s_generate_card_directory_name(file_type,p_name,p_directory_name);
			
			sprintf(p_card_file_name,"%s%s",p_directory_name,p_directory_name);
			Dbg_MsgAssert(strlen(p_card_file_name)Open( p_card_file_name, Mc::File::mMODE_WRITE | Mc::File::mMODE_CREATE, fixed_size );
	s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;

	if (!pFile)
	{
		goto ERROR;
	}
		
	pTemp=(uint8*)Mem::Malloc(header_and_structures_size);
	
	p_file_header=(SMcFileHeader*)pTemp;
	// Zero the whole structure to ensure that the XBox mSignature member is zeroed since
	// it will be part of the data who's signature is calculated.
	p_dest=(uint8*)p_file_header;
	for (uint32 i=0; imVersion=s_get_version_number(file_type);
	p_file_header->mSummaryInfoSize=summary_info_size;
	p_file_header->mDataSize=header_and_structures_size;

	p_dest=(uint8*)(p_file_header+1);

	// Write in the summary info and calculate its checksum.
	BytesWritten=WriteToBuffer(pSummaryInfo, p_dest, summary_info_size, Script::NO_ASSERT);
	if (BytesWritten!=summary_info_size)
	{
		// Note: This assert is surrounded by an if to prevent a warning on non debug builds
		// due to the BytesWritten variable being unused.
		Dbg_MsgAssert(0,("BytesWritten does not equal summary_info_size ?"));
	}	
	p_file_header->mSummaryInfoChecksum=Crc::GenerateCRCCaseSensitive((const char *)p_dest,summary_info_size);
	p_dest+=summary_info_size;

	
	// Write in the game save info
	BytesWritten=WriteToBuffer(pMemCardStuff, p_dest, structure_size, Script::NO_ASSERT);
	
	// K: Last minute fix, 'encrypt' the network settings so that the AOL info cannot be 
	// read. Not very secure, but it should be enough to make the strings non obvious.
	if (file_type==0xca41692d) // NetworkSettings
	{
		//printf("Encrypting %d bytes ...\n",structure_size);
		for (uint32 e=0; emSignature)!=ERROR_SUCCESS)
	{
		Dbg_MsgAssert(0,("XCalculateSignatureEnd failed!"));
	}
	#else
	// Now calculate a checksum of all that data.
	// It will include the contents of p_file_header in the data too, 
	// including mChecksum itself which will be zero at this point.
	// By zeroing mChecksum and including it in the data that is checksum'd it
	// means that the order of the members of SMcFileHeader does not matter.
	// Just seems kind of ugly having to have mChecksum be the first member, cos
	// it would also mean having to take the address of the second member to get
	// the start address of the data to checksum, bleurgh.
	
	p_file_header->mChecksum=Crc::GenerateCRCCaseSensitive((const char*)pTemp,header_and_structures_size);
	#endif // #ifdef __PLAT_XBOX__
	
	CardBytesWritten=pFile->Write( pTemp, header_and_structures_size );
	//printf("Wrote %d bytes\n",CardBytesWritten);
	s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
	if (CardBytesWritten!=header_and_structures_size)
	{
		goto ERROR;
	}


#if __USE_REPLAYS__
	// Write out the big replay buffer.
	// It cannot be written out in one go by getting its base pointer, because on
	// the GameCube it is stored in ARAM.
	// The only way to access it is via the ReadFromBuffer function.
	// Also, there is not enough memory to copy it into a big temporary buffer, 
	// so write it out in small chunks.
	if (file_type==0x26c80b0d/* Replay */)
	{
		// Initialise the checksum, which is calculated cumulatively.
		uint32 replay_data_checksum=0xffffffff;
		
		// Write out the replay data header.
		CardBytesWritten=pFile->Write( (uint8*)&replay_data_header, sizeof(Replay::SReplayDataHeader) );
		s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
		if (CardBytesWritten!=sizeof(Replay::SReplayDataHeader))
		{
			goto ERROR;
		}
	
		replay_data_checksum=Crc::UpdateCRC((const char *)&replay_data_header,sizeof(Replay::SReplayDataHeader),replay_data_checksum);


		// Write out the start-state dummies.	
		Replay::SSavedDummy saved_dummy;
		Replay::CDummy *p_dummy=Replay::GetFirstStartStateDummy();
		int count=0;
		while (p_dummy)
		{
			p_dummy->Save(&saved_dummy);
			
			CardBytesWritten=pFile->Write( (uint8*)&saved_dummy, sizeof(Replay::SSavedDummy) );
			s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
			if (CardBytesWritten!=sizeof(Replay::SSavedDummy))
			{
				goto ERROR;
			}
			replay_data_checksum=Crc::UpdateCRC((const char *)&saved_dummy,sizeof(Replay::SSavedDummy),replay_data_checksum);
			
			p_dummy=p_dummy->mpNext;
			++count;
		}
		if (count!=replay_data_header.mNumStartStateDummies)
		{
			Dbg_MsgAssert(0,("Dummy count mismatch"));
		}	
		
		int buf_size=Replay::GetBufferSize();
		Dbg_MsgAssert((buf_size%REPLAY_BUFFER_CHUNK_SIZE)==0,("Replay buffer size not a multiple of REPLAY_BUFFER_CHUNK_SIZE"));
		
		uint8 *p_chunk=Replay::GetTempBuffer();
		int num_chunks=buf_size/REPLAY_BUFFER_CHUNK_SIZE;
		
		uint32 offset=0;
		for (int i=0; iWrite( p_chunk, REPLAY_BUFFER_CHUNK_SIZE );
			s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
			if (CardBytesWritten!=REPLAY_BUFFER_CHUNK_SIZE)
			{
				goto ERROR;
			}
		}
		
		// Write out the replay data checksum
		CardBytesWritten=pFile->Write( (uint8*)&replay_data_checksum, 4 );
		s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
		if (CardBytesWritten!=4)
		{
			goto ERROR;
		}
	}
#endif
	
	// Now write out padding so as to make sure the file has size fixed_size, otherwise
	// there will be a discrepancy between the space that the game claims is required and
	// the size that appears in the file list, and I'll get a ton of TT bugs.
	if ( Config::GetHardware() != Config::HARDWARE_NGC)
	{
		// TODO: Fix bug in the Write function for the NGC. It always writes from offset 0,
		// meaning that writing this padding overwrites the start of the file data.
		// We don't actually have to write this padding on the NGC, because the resultant file size
		// will be exactly that specified in the file Open command.
		// But the bug will manifest itself when saving the replay data, so need to fix it ...
		if (pad_size)
		{
			Dbg_MsgAssert(p_pad,("NULL p_pad ?"));			
			CardBytesWritten=pFile->Write( p_pad, pad_size );
			//printf("Wrote %d bytes\n",CardBytesWritten);
			
			s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
			if (CardBytesWritten != pad_size)
			{
				goto ERROR;
			}
		}	
	}	
	
	SavedOK=true;
	
ERROR:	
	if (p_pad)
	{
		Mem::Free(p_pad);
	}
	if (pTemp)
	{
		Mem::Free(pTemp);
	}
	if (pFile)
	{
		if (!pFile->Flush())
		{
			SavedOK=false;
		}	
		if (!pFile->Close())
		{
			SavedOK=false;
		}	
		delete pFile;
	}	
	
	delete pSummaryInfo;
	delete pMemCardStuff;

	
	if (got_special_pools)
	{
		Dbg_MsgAssert(CComponent::SGetNumUsedItems()==0,("Expected the special mem card CComponent pool to be empty at this point, but got %d items",CComponent::SGetNumUsedItems()));
		Dbg_MsgAssert(CStruct::SGetNumUsedItems()==0,("Expected the special mem card CStruct pool to be empty at this point, but got %d items",CStruct::SGetNumUsedItems()));
		Dbg_MsgAssert(CVector::SGetNumUsedItems()==0,("Expected the special mem card CVector pool to be empty at this point, but got %d items",CVector::SGetNumUsedItems()));
		CComponent::SSwitchToPreviousPool();
		CStruct::SSwitchToPreviousPool();
		CVector::SSwitchToPreviousPool();
	}
	
	if ( Config::GetHardware() != Config::HARDWARE_XBOX)
	{
		Pcm::PauseMusic( -1 );
		Pcm::PauseStream( -1 );
	}

	if (Config::GetHardware() == Config::HARDWARE_NGC)
	{
		ScriptRemoveTemporaryMemCardPools(NULL,NULL);
	}
		
	return SavedOK;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#if __USE_REPLAYS__
	#ifdef __PLAT_NGC__
	static SMcFileHeader sFileHeader __attribute__((aligned( 32 )));
	#else
	static SMcFileHeader sFileHeader;
	#endif
#endif

bool ScriptSetSectionsToApplyWhenLoading(Script::CStruct *pParams, Script::CScript *pScript)
{
    printf("ScriptSetSectionsToApplyWhenLoading was called.........................\n");
    // These flags only apply when loading type OptionsAndPros.
	// They allow for only certain sections of the file being applied to the game state.
	// For example, when loading a custom skater, only the CUSTOM_SKATER part must be applied.
	s_apply_flags=0;
	
	if (pParams->ContainsFlag(CRCD(0x16b506c0,"ApplyCustomSkater")))
	{
		s_apply_flags |= 1<ContainsFlag(CRCD(0xdc7ea3ce,"ApplyStorySkater")))
	{
		s_apply_flags |= 1<ContainsFlag(CRCD(0x33ec233f,"ApplyStory")))
	{
		s_apply_flags |= 1<ContainsFlag(CRCD(0xb39d7cc4,"ApplyGeneralOptions")))
	{
		s_apply_flags |= 1<ContainsFlag(CRCD(0xc4e78e22,"all")))
	{
		s_apply_flags=0xffffffff;
	}	
	
	return true;
}

// @script | LoadFromMemoryCard | Load the specified file from the memory card
// @parm string | Name | The name of the file to load
// @parm name | Type | The type of the file (cas, network, park, etc.)
bool ScriptLoadFromMemoryCard(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_MsgAssert(!Config::Bootstrap(),("Can't use memory card from bootstrap demo"));

	if ( Config::GetHardware() != Config::HARDWARE_XBOX)
	{
		Pcm::PauseMusic(true);
		Pcm::PauseStream(true);
	}
		
	// Create structures for holding the summary info & game save info
	CStruct *pSummaryInfo=new CStruct;
	CStruct *pMemCardStuff=new CStruct;

	// A bunch of variable declarations, which need to be up here cos there are goto's later
	const char *p_name="";
	uint32 file_type=0;
	
	Mc::File* p_file=NULL;
	int file_size=0;
	uint8 *p_temp=NULL;
	uint8 *p_stuff=NULL;
	SMcFileHeader *p_file_header;
	bool loaded_ok=false;
	uint32 calculated_checksum=0;
	uint32 stored_checksum=0;
	bool checksum_matches=false;
	
	// The LoadForUpload flag is passed as an integer so that that calls to LoadFromMemoryCard
	// in script can set LoadForUpload equal to the value of some script global for convenience.
	int load_for_upload=0;
	pParams->GetInteger(CRCD(0x8c6808d4,"LoadForUpload"),&load_for_upload);

	#ifdef __PLAT_XBOX__
	XCALCSIG_SIGNATURE stored_signature;
	XCALCSIG_SIGNATURE calculated_signature;
	HANDLE h_signature;
	BYTE *p_a=NULL;
	BYTE *p_b=NULL;
	#endif	
	
	// Get the card.
	Mc::Manager * mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=mc_man->GetCard(0,0);
	if (!p_card)
	{
		goto ERROR;
	}
	// GameCube often crashes if try to do card operations on a bad card, so do this check first.
	if (!p_card->IsFormatted())
	{
		goto ERROR;
	}
		
		
	pParams->GetText("Name",&p_name);
	pParams->GetChecksum("Type",&file_type);

	// Calculate the low-level filename
	char p_card_file_name[MAX_CARD_FILE_NAME_CHARS+1];
	p_card_file_name[0]=0;
	
	switch (Config::GetHardware())
	{
		case Config::HARDWARE_PS2:
		case Config::HARDWARE_PS2_PROVIEW:
		case Config::HARDWARE_PS2_DEVSYSTEM:
		{
			const char *p_header=Config::GetMemCardHeader();
			char p_ascii_checksum[20];
			s_generate_ascii_checksum(p_ascii_checksum,p_name,file_type);
			
			sprintf(p_card_file_name,"/%s%s/%s%s",	p_header,p_ascii_checksum,
													p_header,p_ascii_checksum);
			break;
		}	
		case Config::HARDWARE_NGC:
		{
			char p_directory_name[MAX_CARD_FILE_NAME_CHARS+1];
			s_generate_card_directory_name(file_type,p_name,p_directory_name);
			
			sprintf(p_card_file_name,"%s%s",p_directory_name,p_directory_name);
			break;
		}	
		case Config::HARDWARE_XBOX:
		{
			// Generate the directory name
			const char *p_directory_name=s_generate_xbox_directory_name(file_type,p_name);
			const char *p_low_level_directory_name=p_card->ConvertDirectory(p_directory_name);
		
			if (!p_low_level_directory_name)
			{
				goto ERROR;
			}
						
			// Calculate the low-level file name.
			sprintf( p_card_file_name, "/%s/%s", p_low_level_directory_name, p_low_level_directory_name );
			break;
		}	
		default:
		{
			goto ERROR;
			break;
		}	
	}
	
	Dbg_MsgAssert(strlen(p_card_file_name)Open( p_card_file_name, Mc::File::mMODE_READ );
	if (!p_file)
	{
		// File could not be opened
		goto ERROR;
	}	
	
	// File opened OK
	
	// Check the file size.
	file_size=p_file->Seek( 0 ,Mc::File::BASE_END);
	// Removed the file size check, because the fixed size needed to be updated, and keeping the
	// check would prevent existing parks from being able to be loaded.
	//if (file_size != (int)sGetFixedFileSize(file_type))
	//{
	//	// Size mismatch, so consider the file corrupted.
	//	pScript->GetParams()->AddChecksum(NONAME,GenerateCRC("CorruptedData"));
	//	goto ERROR;
	//}	
	
	// Read the file into memory
	// Allocate a temporary buffer for reading the file into
	p_temp=(uint8*)Mem::Malloc(file_size);
	Dbg_MsgAssert(p_temp,("Could not allocate %d bytes for file buffer for file %s",file_size,p_card_file_name));

	p_file->Seek(0,Mc::File::BASE_START);
	if (p_file->Read(p_temp, file_size) != file_size)
	{
		// Some sort of read error.
		goto ERROR;
	}		
	
	// Seemed to read into memory OK
	// Check if the data is corrupt by recalculating the checksum.
	p_stuff=p_temp;
	p_file_header=(SMcFileHeader*)p_stuff;
	
	#ifdef __PLAT_XBOX__
	// Load p_file_header->mSignature into stored_signature
	// then zero out p_file_header->mSignature
	p_a=p_file_header->mSignature.Signature;
	p_b=stored_signature.Signature;
	for (int i=0; imChecksum;
	
	// Set the checksum to zero because that was what it was when
	// the checksum was calculated.
	p_file_header->mChecksum=0;
	// Using p_file_header->mDataSize instead of file_size, because file_size includes the
	// padding, which is not included in the checksum calculation on PS2 and NGC
	if (p_file_header->mDataSize > 500000)
	{
		// If mDataSize itself is corrupted, then don't go trying to calculate the
		// checksum of megabytes of data, in case that hangs the game by hitting some sort
		// of restricted memory location.
	}
	else
	{
		calculated_checksum=Crc::GenerateCRCCaseSensitive((const char*)p_stuff,p_file_header->mDataSize);
	
		if (calculated_checksum==stored_checksum &&
			p_file_header->mVersion==s_get_version_number(file_type))
		{
			checksum_matches=true;
		}
	}	
	#endif

		
	if (checksum_matches)
	{
		// The data is OK, so it is safe to parse it.
		
		// Skip over the header.
		p_stuff=(uint8*)(p_file_header+1);

		// Skip over the summary info
		p_stuff=ReadFromBuffer(pSummaryInfo, p_stuff);

		// K: Last minute fix, 'decrypt' the network settings.
		if (file_type==0xca41692d) // NetworkSettings
		{
			int num_bytes=p_file_header->mDataSize-(p_stuff-((uint8*)p_file_header));
			//printf("Decrypting %d bytes ...\n",num_bytes);
			for (int e=0; eGetScriptInfo()));
			spVaultData=new Script::CStruct;
			spVaultData->AppendStructure(pMemCardStuff);
			sVaultDataType=file_type;

            if ( file_type == CRCD(0x61a1bc57,"cat") )
            {
                // load Created trick in edit slot so we can get some info out of it for uploading
                s_read_game_save_info(file_type, pMemCardStuff, pScript);
            }
		}
		else
		{
			// process the data according to the type of file loaded.
			s_read_game_save_info(file_type, pMemCardStuff, pScript);
		}
				
		loaded_ok=true;
	}	
	else
	{
		// Corrupted data, or bad version number
		pScript->GetParams()->AddChecksum(NONAME,GenerateCRC("CorruptedData"));
	}	

	// if the loading was successful,
	// then run some post-load functions
	if ( loaded_ok )
	{
		if (load_for_upload)
		{
			// If the file was loaded for upload to the net, then do not do any post processing,
			// since the data has just been stored away for retrieval later.
		}
		else
		{
			Script::RunScript( "post_load_from_memory_card", pParams );
		}	
	}

ERROR:	
	// Cleanup and unpause the music.
	if (p_file)
	{
		p_file->Close();
		delete p_file;
	}

	if (p_temp)
	{
		Mem::Free(p_temp);
	}

	delete pSummaryInfo;
	delete pMemCardStuff;
	if ( Config::GetHardware() != Config::HARDWARE_XBOX)
	{
		Pcm::PauseMusic( -1 );
		Pcm::PauseStream( -1 );
	}
		
	return loaded_ok;
}

bool ScriptLoadedCustomSkater(Script::CStruct *pParams, Script::CScript *pScript)
{
	return s_did_apply_custom_skater_info;
}	

// Functions required for when loading a file of mem card for uploading to the net.
bool ScriptGetMemCardDataForUpload(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_MsgAssert(spVaultData,("\n%s\nNo mem card data present",pScript->GetScriptInfo()));
	
	pScript->GetParams()->AddStructure(CRCD(0x6d2ab6a,"DataForUpload"),spVaultData);
	pScript->GetParams()->AddChecksum(CRCD(0x7321a8d6,"Type"),sVaultDataType);
	return true;
}	

bool ScriptClearMemCardDataForUpload(Script::CStruct *pParams, Script::CScript *pScript)
{
	if (spVaultData)
	{
		delete spVaultData;
		spVaultData=NULL;
	}	
	sVaultDataType=0;
	return true;
}	

bool ScriptNeedToLoadReplayBuffer(Script::CStruct *pParams, Script::CScript *pScript)
{
#if __USE_REPLAYS__
	return sNeedToLoadReplayBuffer;
#else
	return false;
#endif
}

bool ScriptLoadReplayData(Script::CStruct *pParams, Script::CScript *pScript)
{
#if __USE_REPLAYS__
	Dbg_MsgAssert(!Config::Bootstrap(),("Can't use memory card from bootstrap demo"));

	Pcm::PauseMusic(true);
	Pcm::PauseStream(true);
	
	Dbg_MsgAssert(sNeedToLoadReplayBuffer,("Called LoadReplayBuffer when sNeedToLoadReplayBuffer is false"));

	int replay_buffer_size=0;
	Replay::SReplayDataHeader header;
	Replay::SSavedDummy saved_dummy;
	uint32 replay_data_checksum=0xffffffff; // Initialise the checksum, which is calculated accumulatively.
	uint32 ch=0;	
	bool loaded_ok=false;
	int bytes_read=0;
	Mc::File* p_file=NULL;
	uint8 *p_chunk=NULL;
	int num_chunks=0;
	int offset=0;
	
	// Get the card.
	Mc::Manager * mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=mc_man->GetCard(0,0);
	if (!p_card)
	{
		goto ERROR;
	}
	
	// GameCube often crashes if try to do card operations on a bad card, so do this check first.
	if (!p_card->IsFormatted())
	{
		goto ERROR;
	}

	// Open the file.
	p_file=p_card->Open( spReplayCardFileName, Mc::File::mMODE_READ );
	if (!p_file)
	{
		// File could not be opened
		goto ERROR;
	}	
	
	// File opened OK

	// Seek to the start of the replay data by reading in the header, reading out the start data
	// size from the header, then seeking there.	
	p_file->Seek( 0 ,Mc::File::BASE_START);
	if (p_file->Read((uint8*)&sFileHeader, sizeof(SMcFileHeader)) != sizeof(SMcFileHeader))
	{
		// Some sort of read error.
		goto ERROR;
	}		
	p_file->Seek(sFileHeader.mDataSize,Mc::File::BASE_START);

	// Make sure the buffer does exist
	Replay::AllocateBuffer();
	// and that it is cleared to start with.
	Replay::ClearBuffer();
			
	// Read in the header			
	bytes_read=p_file->Read( (uint8*)&header, sizeof(Replay::SReplayDataHeader) );
	if (bytes_read!=sizeof(Replay::SReplayDataHeader))
	{
		// Some sort of read error.
		goto ERROR;
	}
	Replay::ReadReplayDataHeader(&header);
	replay_data_checksum=Crc::UpdateCRC((const char *)&header,sizeof(Replay::SReplayDataHeader),replay_data_checksum);
	
	// Read in the dummies
	for (int i=0; iRead( (uint8*)&saved_dummy, sizeof(Replay::SSavedDummy) );
		if (bytes_read!=sizeof(Replay::SSavedDummy))
		{
			// Some sort of read error.
			goto ERROR;
		}
		replay_data_checksum=Crc::UpdateCRC((const char *)&saved_dummy,sizeof(Replay::SSavedDummy),replay_data_checksum);
		
		Replay::CreateDummyFromSaveData(&saved_dummy);
	}
			
	// Read the data in a chunk at a time.
	replay_buffer_size=Replay::GetBufferSize();
	Dbg_MsgAssert((replay_buffer_size%REPLAY_BUFFER_CHUNK_SIZE)==0,("Replay buffer size not a multiple of REPLAY_BUFFER_CHUNK_SIZE"));
	
	p_chunk=Replay::GetTempBuffer();
	num_chunks=replay_buffer_size/REPLAY_BUFFER_CHUNK_SIZE;
	
	offset=0;
	for (int i=0; iRead( p_chunk, REPLAY_BUFFER_CHUNK_SIZE );
		if (bytes_read!=REPLAY_BUFFER_CHUNK_SIZE)
		{
			// Some sort of read error.
			goto ERROR;
		}
		Replay::WriteIntoBuffer(p_chunk,offset,REPLAY_BUFFER_CHUNK_SIZE);
		replay_data_checksum=Crc::UpdateCRC((const char *)p_chunk,REPLAY_BUFFER_CHUNK_SIZE,replay_data_checksum);
		
		offset+=REPLAY_BUFFER_CHUNK_SIZE;
	}

	// Read in and check the checksum
	bytes_read=p_file->Read( (uint8*)&ch, 4 );
	if (bytes_read!=4)
	{
		// Some sort of read error.
		goto ERROR;
	}
	if (ch != replay_data_checksum)
	{
		goto ERROR;
	}	
		
	// Hooray!	
	loaded_ok=true;

ERROR:	
	sNeedToLoadReplayBuffer=false;
	
	if (p_file)
	{
		p_file->Close();
		delete p_file;
	}

	Pcm::PauseMusic( -1 );
	Pcm::PauseStream( -1 );
	return loaded_ok;
#else
	return false;
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetMaxTHPS4FilesAllowed(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AddInteger("MaxTHPS4FilesAllowed",MAX_THPS4_FILES_ALLOWED);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static uint8 spSummaryInfoBuffer[sizeof(SMcFileHeader)+MAX_SUMMARY_INFO_SIZE];
bool ScriptGetMemCardDirectoryListing(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->RemoveComponent("DirectoryListing");
	pScript->GetParams()->RemoveComponent("FilesLimitReached");
	pScript->GetParams()->RemoveComponent("TotalTHPS4FilesOnCard");

	uint32 file_type_to_list=0;
	pParams->GetChecksum("FileType",&file_type_to_list);

	// Get a directory listing of the whole card.
	Mc::Manager * p_mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=p_mc_man->GetCard(0,0);
	if (!p_card)
	{
		return true;
	}	

	Lst::Head< Mc::File > file_list;
	p_card->GetFileList( "*", file_list );

	// If the special pools exist, switch to them so that the components & structs etc get allocated off them.
	// The special pools use the space freed by unloading the skater anims, and are for preventing memory
	// overflows when the save size gets large.
	Dbg_MsgAssert(CComponent::SGetCurrentPoolIndex()==0,("Bad current CComponent pool"));
	Dbg_MsgAssert(CStruct::SGetCurrentPoolIndex()==0,("Bad current CStruct pool"));
	bool got_special_pools=false;
	
	CComponent::SSwitchToNextPool();
	if (CComponent::SPoolExists())
	{
		CStruct::SSwitchToNextPool();
		Dbg_MsgAssert(CStruct::SPoolExists(),("No special CStruct pool ?"));
		got_special_pools=true;
	}
	else
	{
		CComponent::SSwitchToPreviousPool();
	}	

	
	int num_files_added=0;
	CStruct *pp_structs[MAX_THPS4_FILES_ALLOWED];
	
	// Run through all the files (well, directory names actually) and compare their
	// first 12 characters with the THPS4 header.	
	const char *p_header=Config::GetMemCardHeader();
	int header_len=strlen(p_header);
	
	int num_files=file_list.CountItems();
	int total_thps4_files=0;
	for (int i=0; iGetData();
		
		bool is_THPS4_file=true;
		
		// No need to check the file name if it's the XBox because all the files
		// will be in our filespace.
		if ( Config::GetHardware() != Config::HARDWARE_XBOX )
		{
			for (int j=0; jm_Filename[j]!=p_header[j])
				{
					is_THPS4_file=false;
					break;
				}
			}
		}	
		
		if (!is_THPS4_file)
		{
			continue;
		}
		
		++total_thps4_files;
		
		// It is a THPS4 file, so compare the file type with that requested.
		uint32 file_type=0;
		
		
		switch (Config::GetHardware())
		{
		case Config::HARDWARE_PS2:
		case Config::HARDWARE_PS2_PROVIEW:
		case Config::HARDWARE_PS2_DEVSYSTEM:
		case Config::HARDWARE_NGC:
			// The filetype is indicated by the last letter of the 8 characters
			// that follow the header. The other 7 contain the ascii checksum of
			// the full file name as entered by the user.
			file_type=s_determine_file_type(p_file->m_Filename[header_len+8-1]);
			break;
		case Config::HARDWARE_XBOX:
		{
			// TODO: Remove this ifdef somehow, needed currently because m_DisplayFilename
			// is only defined for XBox. Shouldn't really have any platform ifdefs in this file.
			#ifdef __PLAT_XBOX__
			int length = strlen( p_file->m_DisplayFilename );
			if( stricmp( &p_file->m_DisplayFilename[length - 15], "NetworkSettings" ) == 0 )
			{
				file_type = 0xca41692d; // NetworkSettings
			}
			else if( stricmp( &p_file->m_DisplayFilename[length - 6], "Skater" ) == 0 )
			{
				file_type = 0xb010f357; // OptionsAndPros
			}
			else if( stricmp( &p_file->m_DisplayFilename[length - 4], "Park" ) == 0 )
			{
				file_type = 0x3bf882cc; // Park
			}
			else if( stricmp( &p_file->m_DisplayFilename[length - 6], "Skater" ) == 0 )
			{
				file_type = 0xffc529f4; // Cas
			}
            else if( stricmp( &p_file->m_DisplayFilename[length - 5], "Trick" ) == 0 )
			{
				file_type = 0x61a1bc57; // CAT
			}
			else if( stricmp( &p_file->m_DisplayFilename[length - 6], "Replay" ) == 0 )
			{
				file_type = 0x26c80b0d; // Replay
			}
			else if( stricmp( &p_file->m_DisplayFilename[length - 5], "Goals" ) == 0 )
			{
				file_type = 0x62896edf; // CreatedGoals
			}
			#endif // #ifdef __PLAT_XBOX__
			break;
		}	
		case Config::HARDWARE_WIN32:
			break;
		default:
			break;
		}
		
		
		
		if ( (file_type_to_list==0 && file_type!=0) || 
			 file_type==file_type_to_list)
		{
			if (num_files_added < MAX_THPS4_FILES_ALLOWED)
			{
				// It's safe to add a new entry to the array.
				CStruct *p_struct=new CStruct;
				p_struct->AddChecksum("file_type",file_type);
				p_struct->AddInteger("Index",num_files_added);


				p_struct->AddInteger("Year",p_file->m_Modified.m_Year);
				p_struct->AddInteger("Month",p_file->m_Modified.m_Month);
				p_struct->AddInteger("Day",p_file->m_Modified.m_Day);
				p_struct->AddInteger("Hour",p_file->m_Modified.m_Hour);
				p_struct->AddInteger("Minutes",p_file->m_Modified.m_Minutes);
				p_struct->AddInteger("Seconds",p_file->m_Modified.m_Seconds);

				
				char p_card_file_name[100];
				if (Config::GetHardware()==Config::HARDWARE_NGC)
				{
					sprintf(p_card_file_name,"%s",p_file->m_Filename);
				}	
				else
				{
					sprintf(p_card_file_name,"/%s/%s",p_file->m_Filename,p_file->m_Filename);
				}	
				Dbg_MsgAssert(strlen(p_card_file_name)<100,("Oops"));
				
				// Store the low level file name in the structure too so that the scripts
				// can pass it on to the file delete function. This is necessary because
				// even though the low level file name can be derived from the file name as
				// stored in the file itself, if the file is corrupted the name might be too,
				// so in that case it would not be possible to delete it.
				p_struct->AddString("actual_file_name",p_card_file_name);
				
				#ifdef __PLAT_XBOX__
				p_struct->AddString("xbox_directory_name",p_file->m_DisplayFilename);
				#endif

				// Need to open the file to get the summary info out of it ...
				Mc::File* p_mc_file=p_card->Open( p_card_file_name, Mc::File::mMODE_READ );
				if (p_mc_file)
				{   
					// File opened OK, so grab the first hundred or so bytes.
					p_mc_file->Seek( 0 ,Mc::File::BASE_START);
					p_mc_file->Read( spSummaryInfoBuffer, sizeof(SMcFileHeader)+MAX_SUMMARY_INFO_SIZE );
					
					SMcFileHeader *p_file_header=(SMcFileHeader*)spSummaryInfoBuffer;
					uint8 *p_summary_info=(uint8*)(p_file_header+1);
					
					// Determine whether the summary info is corrupted.
					bool corrupt_summary_info=false;
					if (p_file_header->mSummaryInfoSize > MAX_SUMMARY_INFO_SIZE)
					{
						// The mSummaryInfoSize itself is corrupted!
						corrupt_summary_info=true;
					}
					else
					{
						uint32 calculated_summary_info_checksum=Crc::GenerateCRCCaseSensitive((const char *)p_summary_info,p_file_header->mSummaryInfoSize);
						if (calculated_summary_info_checksum != p_file_header->mSummaryInfoChecksum)
						{
							corrupt_summary_info=true;
						}
					}		

					// Calculate the total file size
					int file_size=p_mc_file->Seek(0,Mc::File::BASE_END);
					//printf("Directory listing: %s, size=%d\n",p_card_file_name,file_size);
					int total_size_on_card=s_calculate_total_space_used_on_card(file_type,file_size);
					p_struct->AddInteger("total_file_size",total_size_on_card);

					
					// Removed the file size check, because the fixed size needed to be updated, and keeping the
					// check would prevent existing parks from being able to be loaded.
					//if (corrupt_summary_info || file_size != (int)sGetFixedFileSize(file_type))
					if (corrupt_summary_info)
					{
						p_struct->AddChecksum(NONAME,Script::GenerateCRC("Corrupt"));
						if (Config::GetHardware()==Config::HARDWARE_NGC)
						{
							p_struct->AddString("filename",GetString("NGCDamagedFile"));
						}
						else
						{
							p_struct->AddString("filename",GetString("DamagedFile"));
						}	
					}
					else if (p_file_header->mVersion!=s_get_version_number(file_type))
					{
						#ifdef __NOPT_ASSERT__
						p_struct->AddChecksum(NONAME,Script::GenerateCRC("BadVersion"));
						p_struct->AddString("filename",GetString("BadVersionNumber"));
						#else
						p_struct->AddChecksum(NONAME,Script::GenerateCRC("Corrupt"));
						if (Config::GetHardware()==Config::HARDWARE_NGC)
						{
							p_struct->AddString("filename",GetString("NGCDamagedFile"));
						}
						else
						{
							p_struct->AddString("filename",GetString("DamagedFile"));
						}	
						#endif
					}
					else
					{
						// Extract the summary info and stick it in the structure.
						// This summary info gets printed at the bottom of the files menu when
						// the highlight is on the file.
						// The summary info is actually in the main structure stored in the file,
						// but that would require loading in the whole thing which would take a
						// long time.
						
						// Have to use a temporary structure because ReadFromBuffer clears the
						// passed structure first.
						CStruct *p_temp=new CStruct;
						ReadFromBuffer(p_temp,p_summary_info);
						p_struct->AppendStructure(p_temp);
						delete p_temp;
					}
					
					p_mc_file->Close();
					delete p_mc_file;
				}
				else
				{
					// If the file did not open at all, treat it as corrupted.
					p_struct->AddChecksum(NONAME,Script::GenerateCRC("Corrupt"));
					p_struct->AddString("filename",GetString("DamagedFile"));
				}	
					
				pp_structs[num_files_added++]=p_struct;
			}	
		}
	}	

	if (got_special_pools)
	{
		// Note: The pools will contain stuff at this point. The script will delete them
		// when it does its cleanup.
		CComponent::SSwitchToPreviousPool();
		CStruct::SSwitchToPreviousPool();
	}
	
	// The file_list will probably get cleaned up when it goes out of scope, but just to be sure ...
	file_list.DestroyAllNodes();

	pScript->GetParams()->AddInteger("TotalTHPS4FilesOnCard",total_thps4_files);
	
	// Set the FilesLimitReached flag so that the script can determine whether to add
	// the 'Create New' entry to the files menu.
	if (total_thps4_files >= MAX_THPS4_FILES_ALLOWED)
	{
		pScript->GetParams()->AddChecksum(NONAME,0x4eec27b5/*FilesLimitReached*/);
	}
	
	// If files were found, add the array to the script's params.
	if (num_files_added)
	{
		// First, sort the array so that the files are definitely in date order (TT6112)
		while (true)
		{
			bool did_a_swap=false;
			
			for (int i=0; iSetSizeAndType(num_files_added,ESYMBOLTYPE_STRUCTURE);
		
		for (int f=0; fSetStructure(f,pp_structs[f]);
		}
		
		pScript->GetParams()->AddArrayPointer("DirectoryListing",p_directory_listing_array);
		
		// Note: Not deleting the pp_structs[], cos they've been given to the array.
		// Not deleting p_directory_listing_array, because it's been given to the scripts params.
	}
		
	return true;
}

// Returns true if the sector size is 8K, false otherwise.
// Needed as part of the GameCube card check procedure to check that the card is not some weird type.
// If it's a PS2 build, it will just return true.
bool ScriptSectorSizeOK(Script::CStruct *pParams, Script::CScript *pScript)
{
	#ifdef __PLAT_NGC__
	Spt::SingletonPtr< Mc::Manager > mc_man;
	Mc::Card* pCard=mc_man->GetCard(0,0);
	if (pCard)
	{
		if (pCard->m_sector_size==8192)
		{
			return true;
		}
		else
		{
			return false;
		}
	}		
	return false;
	#else
	return true;
	#endif
}

bool ScriptCardIsDamaged(Script::CStruct *pParams, Script::CScript *pScript)
{
	#ifdef __PLAT_NGC__
	Spt::SingletonPtr< Mc::Manager > mc_man;
	Mc::Card* pCard=mc_man->GetCard(0,0);
	if (pCard)
	{
		return pCard->m_broken;
	}
	else
	{
		if (mc_man->GotFatalError())
		{
			return true;
		}
	}	
	return false;
	#else
	return false;
	#endif
}

bool ScriptCardIsForeign(Script::CStruct *pParams, Script::CScript *pScript)
{
	#ifdef __PLAT_NGC__
	Spt::SingletonPtr< Mc::Manager > mc_man;
	Mc::Card* pCard=mc_man->GetCard(0,0);
	if (pCard)
	{
		return pCard->IsForeign();
	}
	return false;
	#else
	return false;
	#endif
}

bool ScriptBadDevice(Script::CStruct *pParams, Script::CScript *pScript)
{
	#ifdef __PLAT_NGC__
	Spt::SingletonPtr< Mc::Manager > mc_man;
	Mc::Card* pCard=mc_man->GetCard(0,0);
	if (pCard)
	{
		return false;
	}
	else
	{
		if (mc_man->GotWrongDevice())
		{
			return true;
		}
	}	
	return false;
	#else
	return false;
	#endif
}

// For debugging, so that we can use the script debugger to view the contents of the save structure that would
// get saved out to the mem card.
bool ScriptGetSaveInfo(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 file_type=0;
	pParams->GetChecksum(CRCD(0x7321a8d6,"type"),&file_type);

	Script::CStruct *p_main_structure=new Script::CStruct;
	s_insert_game_save_info(file_type, p_main_structure);
	uint32 structure_size=CalculateBufferSize(p_main_structure);
	
	Script::CStruct *p_summary_info=new Script::CStruct;
	s_generate_summary_info(p_summary_info, file_type, p_main_structure);
	uint32 summary_info_size=CalculateBufferSize(p_summary_info);

	
	pScript->GetParams()->AddInteger(CRCD(0x172c1344,"MainStructureSize"),structure_size);
	pScript->GetParams()->AddInteger(CRCD(0x96635475,"SummaryInfoSize"),summary_info_size);
	pScript->GetParams()->AddInteger(CRCD(0x6afd2f7f,"MaxSummaryInfoSize"),MAX_SUMMARY_INFO_SIZE);
	pScript->GetParams()->AddInteger(CRCD(0xecbb8262,"TotalSize"),sizeof(SMcFileHeader)+summary_info_size+structure_size);
	pScript->GetParams()->AddInteger(CRCD(0xb35eb1d1,"MaxTotalSize"),sGetFixedFileSize(file_type));
	
	pScript->GetParams()->AddStructurePointer(CRCD(0x703e60ca,"SummaryInfo"),p_summary_info);
	pScript->GetParams()->AddStructurePointer(CRCD(0x671c9c00,"MainStructure"),p_main_structure);
	
	return true;
}

// It is safe to call this multiple times.
bool ScriptCreateTemporaryMemCardPools(Script::CStruct *pParams, Script::CScript *pScript)
{
	// If the pools exist already, do nothing.
	CComponent::SSwitchToNextPool();
	if (CComponent::SPoolExists())
	{
		CComponent::SSwitchToPreviousPool();
		
		CStruct::SSwitchToNextPool();								 
		Dbg_MsgAssert(CStruct::SPoolExists(),("Expected CStruct pool to exist"));
		CStruct::SSwitchToPreviousPool();

		CVector::SSwitchToNextPool();								 
		Dbg_MsgAssert(CVector::SPoolExists(),("Expected CVector pool to exist"));
		CVector::SSwitchToPreviousPool();
		
		return true;
	}	
	CComponent::SSwitchToPreviousPool();
	
#ifdef __PLAT_NGC__
#define NUM_COM 16000
#define NUM_STR 8000
#define NUM_VEC 3000

#define BUFFER_SIZE ( ( NUM_COM * 16 ) + ( NUM_STR * 8 ) + ( NUM_VEC * 16 ) )

	// Time for a hack...
	g_mc_hack = true;

	g_hack_address = NsARAM::alloc( BUFFER_SIZE );
	if ( g_hack_address )
	{
		NsDMA::toARAM( g_hack_address, g_p_buffer, BUFFER_SIZE );
	}

	memset( g_p_buffer, 0, BUFFER_SIZE );

	NsBuffer::reset( false );
#else
#define NUM_COM 50000
#define NUM_STR 50000
#define NUM_VEC 10000
#endif		// __PLAT_NGC__

		
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());

	Mem::PushMemProfile("Mem card CComponent");
	Dbg_MsgAssert(CComponent::SGetCurrentPoolIndex()==0,("Bad current CComponent pool"));
	CComponent::SSwitchToNextPool();
	CComponent::SCreatePool(NUM_COM, "Reserve CComponent");
	CComponent::SSwitchToPreviousPool();
	Mem::PopMemProfile();
										 
	Mem::PushMemProfile("Mem card CStruct");
	Dbg_MsgAssert(CStruct::SGetCurrentPoolIndex()==0,("Bad current CStruct pool"));
	CStruct::SSwitchToNextPool();
	CStruct::SCreatePool(NUM_STR, "Reserve CStruct");
	CStruct::SSwitchToPreviousPool();
	Mem::PopMemProfile();
	
	// 12 bytes each  (Actually 16)
	Mem::PushMemProfile("Mem card CVector");
	Dbg_MsgAssert(CVector::SGetCurrentPoolIndex()==0,("Bad current CVector pool"));
	CVector::SSwitchToNextPool();
	CVector::SCreatePool(NUM_VEC, "Reserve CVector");
	CVector::SSwitchToPreviousPool();
	Mem::PopMemProfile();

	Mem::Manager::sHandle().PopContext();

	return true;
}

// It is safe to call this multiple times.
bool ScriptRemoveTemporaryMemCardPools(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_MsgAssert(CComponent::SGetCurrentPoolIndex()==0,("Bad current CComponent pool"));
	CComponent::SSwitchToNextPool();
	CComponent::SRemovePool(); // Does nothing if the pool does not exist.
	CComponent::SSwitchToPreviousPool();
	
	Dbg_MsgAssert(CStruct::SGetCurrentPoolIndex()==0,("Bad current CStruct pool"));
	CStruct::SSwitchToNextPool();
	CStruct::SRemovePool();
	CStruct::SSwitchToPreviousPool();
		
	Dbg_MsgAssert(CVector::SGetCurrentPoolIndex()==0,("Bad current CVector pool"));
	CVector::SSwitchToNextPool();
	CVector::SRemovePool();
	CVector::SSwitchToPreviousPool();
	
#ifdef __PLAT_NGC__
	if ( g_hack_address )
	{
		NsDMA::toMRAM( g_p_buffer, g_hack_address, BUFFER_SIZE );
	}
	NsARAM::free( g_hack_address );

	g_mc_hack = false;
	NsBuffer::reset( true );
#endif		// __PLAT_NGC__

	return true;
}

bool ScriptSwitchToTempPoolsIfTheyExist(Script::CStruct *pParams, Script::CScript *pScript)
{
	Dbg_MsgAssert(CComponent::SGetCurrentPoolIndex()==0 && CStruct::SGetCurrentPoolIndex()==0 && CVector::SGetCurrentPoolIndex()==0, ("Expected current pools to be 0"));
	CComponent::SSwitchToNextPool();
	CStruct::SSwitchToNextPool();
	CVector::SSwitchToNextPool();
	if (CComponent::SPoolExists() && CStruct::SPoolExists() && CVector::SPoolExists())
	{
		return true;
	}
	CComponent::SSwitchToPreviousPool();
	CStruct::SSwitchToPreviousPool();
	CVector::SSwitchToPreviousPool();

	return false;
}

bool ScriptSwitchToRegularPools(Script::CStruct *pParams, Script::CScript *pScript)
{
	if (CComponent::SGetCurrentPoolIndex()==0 && 
		CStruct::SGetCurrentPoolIndex()==0 &&
		CVector::SGetCurrentPoolIndex()==0)
	{
		return true;
	}
		
	CComponent::SSwitchToPreviousPool();
	CStruct::SSwitchToPreviousPool();
	CVector::SSwitchToPreviousPool();
	return true;
}

// Saves any old data file to mem card
bool SaveDataFile(const char *p_name, uint8 *p_data, uint32 size)
{
	Dbg_MsgAssert(p_name,("NULL p_name"));
	Dbg_MsgAssert(p_data,("NULL p_data"));

	switch (Config::GetHardware())
	{
		case Config::HARDWARE_PS2:
		case Config::HARDWARE_PS2_PROVIEW:
		case Config::HARDWARE_PS2_DEVSYSTEM:
			break;
		default:
			return false;
			break;
	}
				
	if ( Config::GetHardware() != Config::HARDWARE_XBOX)
	{
		Pcm::PauseMusic(true);
		Pcm::PauseStream(true);
	}
	
	Mc::File* pFile=NULL;
	char p_card_file_name[MAX_CARD_FILE_NAME_CHARS+1];
	const char *p_header=Config::GetMemCardHeader();
	char p_ascii_checksum[20];
	int suffix=0;
	#define FULL_NAME_BUF_SIZE 100
	char p_full_name[FULL_NAME_BUF_SIZE];
	bool SavedOK=false;
	uint32 CardBytesWritten=0;


	Mc::Manager * mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=mc_man->GetCard(0,0);
	if (!p_card)
	{
		goto ERROR;
	}

	while (true)
	{
		Dbg_MsgAssert(strlen(p_name)Open( p_card_file_name, Mc::File::mMODE_READ );
		if (pFile)
		{
			pFile->Flush();
			pFile->Close();
			delete pFile;
			pFile=NULL;
		}
		else
		{
			break;
		}
		++suffix;
		Dbg_MsgAssert(suffix<1000,("Too many files!!"));
	}		

	printf ("Creating mem card file '%s'\n",p_full_name);
	
	if (!s_make_ps2_dir_and_icons(p_card,
								  0xb010f357, // OptionsAndPros tee hee
								  p_full_name,
								  p_card_file_name,
								  &s_insufficient_space))
	{
		goto ERROR;
	}

	// Open a file big enough to hold all data
	pFile=p_card->Open( p_card_file_name, Mc::File::mMODE_WRITE | Mc::File::mMODE_CREATE, s_round_up_to_platforms_block_size(size) );
	s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;

	if (!pFile)
	{
		goto ERROR;
	}

	CardBytesWritten=pFile->Write( p_data, size );
	s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
	if (CardBytesWritten!=size)
	{
		goto ERROR;
	}

	SavedOK=true;
	
ERROR:	
	if (pFile)
	{
		if (!pFile->Flush())
		{
			SavedOK=false;
		}	
		if (!pFile->Close())
		{
			SavedOK=false;
		}	
		delete pFile;
	}	

	if ( Config::GetHardware() != Config::HARDWARE_XBOX)
	{
		Pcm::PauseMusic( -1 );
		Pcm::PauseStream( -1 );
	}
		
	return SavedOK;
}



// Load len bytes of any old data file to mem card to p_data
bool LoadDataFile(const char *p_name, uint8 *p_data, uint32 size)
{
	Dbg_MsgAssert(p_name,("NULL p_name"));
	Dbg_MsgAssert(p_data,("NULL p_data"));

	switch (Config::GetHardware())
	{
		case Config::HARDWARE_PS2:
		case Config::HARDWARE_PS2_PROVIEW:
		case Config::HARDWARE_PS2_DEVSYSTEM:
			break;
		default:
			return false;
			break;
	}
				
	if ( Config::GetHardware() != Config::HARDWARE_XBOX)
	{
		Pcm::PauseMusic(true);
		Pcm::PauseStream(true);
	}
	
	Mc::File* pFile=NULL;
	char p_card_file_name[MAX_CARD_FILE_NAME_CHARS+1];
	const char *p_header=Config::GetMemCardHeader();
	char p_ascii_checksum[20];
	#define FULL_NAME_BUF_SIZE 100
	char p_full_name[FULL_NAME_BUF_SIZE];

	Mc::Manager * mc_man = Mc::Manager::Instance();
	Mc::Card* p_card=mc_man->GetCard(0,0);
	if (!p_card)
	{
		return false;;
	}

	Dbg_MsgAssert(strlen(p_name)Open( p_card_file_name, Mc::File::mMODE_READ );
	if (pFile)
	{
		printf ("Loading From Memory Card\n");
		pFile->Flush();
		pFile->Seek( 0 ,Mc::File::BASE_START);
		pFile->Read(p_data,size);
		pFile->Flush();
		pFile->Close();
		delete pFile;
		return true;
	}
	return false;
}



} // namespace CFuncs



================================================
FILE: Code/Sk/Scripting/mcfuncs.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Scripting												**
**																			**
**	File name:		mcfuncs.h												**
**																			**
**	Created: 		06/10/2002	-	ksh										**
**																			**
*****************************************************************************/

#ifndef	__SCRIPTING_MCFUNCS_H
#define	__SCRIPTING_MCFUNCS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

namespace Script
{
	class CStruct;
	class CScript;
};
	
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace CFuncs
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

bool ScriptGetMostRecentSave(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetMemCardSpaceAvailable(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetMemCardSpaceRequired(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptMemCardFileExists(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDeleteMemCardFile(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptFormatCard(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCardIsInSlot(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCardIsFormatted(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSaveFailedDueToInsufficientSpace(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetSummaryInfo(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSaveToMemoryCard(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetSectionsToApplyWhenLoading(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLoadFromMemoryCard(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLoadedCustomSkater(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetMemCardDataForUpload(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptClearMemCardDataForUpload(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptNeedToLoadReplayBuffer(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLoadReplayData(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetMemCardDirectoryListing(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetMaxTHPS4FilesAllowed(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSectorSizeOK(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCardIsDamaged(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCardIsForeign(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptBadDevice(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptGetSaveInfo(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCreateTemporaryMemCardPools(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRemoveTemporaryMemCardPools(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSwitchToTempPoolsIfTheyExist(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSwitchToRegularPools(Script::CStruct *pParams, Script::CScript *pScript);


bool SaveDataFile(const char *p_name, uint8 *p_data, uint32 size);
bool LoadDataFile(const char *p_name, uint8 *p_data, uint32 size);

void SetVaultData(uint8 *p_data, uint32 type);

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace CFuncs

#endif	// __SCRIPTING_MCFUNCS_H



================================================
FILE: Code/Sk/Scripting/nodearray.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// nodearray.cpp		KSH 7 Nov 2001
//
// Functions related to the NodeArray, such as the node name hash table,
// prefix info for rapidly finding all the nodes with a given prefix,
// and utility functions for getting the links and stuff.
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  // For Crc::GenerateCRCFromString
#include 
#include 

namespace SkateScript
{
using namespace Script;

////////////////////////////////////////////////////////////////////
//
// Node name hash table stuff, for speeding up FindNamedNode
//
////////////////////////////////////////////////////////////////////

#if	1//def __PLAT_NGC__

// Gets called from SkateScript::Init in sk\scripting\init.cpp
void InitNodeNameHashTable()
{
}

// Resets the hash table, and deletes any extra entries created.
// This currently gets called from the rwviewer cleanup function.
void ClearNodeNameHashTable()
{
}

// Given that a new NodeArray symbol has got loaded due to the loading of a qb containing it,
// this will generate the node name hash table.

// More efficient hash table.
#define HASH_SIZE 16384
#define HASH_MASK (HASH_SIZE-1)

short node_hash[HASH_SIZE];

void CreateNodeNameHashTable()
{
	for ( int lp = 0; lp < HASH_SIZE; lp++ )
	{
		node_hash[lp] = -1;
	}
	// Get the NodeArray
	CArray *p_node_array=GetArray(0xc472ecc5/*NodeArray*/);
	Dbg_MsgAssert(p_node_array,("CreateNodeNameHashTable could not find NodeArray"));

	// Scan through each node, getting it's name, and if it has a name add it in to the hash table.	
	for (uint32 i=0; iGetSize(); ++i)
	{
		CStruct *p_node=p_node_array->GetStructure(i);
		
		uint32 name_checksum=0;
		if (p_node->GetChecksum("Name",&name_checksum))
		{
			uint16 hash = (uint16)(name_checksum & HASH_MASK );

			while ( node_hash[hash] != -1 )
			{
				hash++;
				hash &= HASH_MASK;
			}
			node_hash[hash] = i;
		}
	}
}
	
// Searches the node array for a node whose Name is the passed checksum.
int FindNamedNode(uint32 checksum, bool assert)
{
	// Get the NodeArray
	CArray *p_node_array=GetArray(CRCD(0xc472ecc5,"NodeArray"));
	Dbg_MsgAssert(p_node_array,("CreateNodeNameHashTable could not find NodeArray"));

	// Added 9/18/03 for THUG submission. If the node array doesn't exist, interpret that
	// as just another condition of the node not existing
	if( p_node_array == NULL )
	{
		return -1;
	}

	uint16 hash = (uint16)(checksum & HASH_MASK );

	while ( node_hash[hash] != -1 )
	{
		CStruct *p_node=p_node_array->GetStructure(node_hash[hash]);
		if ( assert ) Dbg_MsgAssert(p_node,("NULL p_node"));
		
		uint32 name_checksum=0;
		if (p_node->GetChecksum(CRCD(0xa1dc81f9,"Name"),&name_checksum))
		{
			if ( name_checksum == checksum )
			{
				return node_hash[hash];
			}
		}

		hash++;
		hash &= HASH_MASK;
	}

//	// Scan through each node, getting it's name, and if it has a name add it in to the hash table.	
//	for (uint32 i=0; iGetSize(); ++i)
//	{
//		CStruct *p_node=p_node_array->GetStructure(i);
//		if ( assert ) Dbg_MsgAssert(p_node,("NULL p_node"));
//		
//		uint32 name_checksum=0;
//		if (p_node->GetChecksum("Name",&name_checksum))
//		{
//			if ( name_checksum == checksum )
//			{
//				return i;
//			}
//		}
//	}
	return -1;
}

int FindNamedNode(const char *p_name)
{
	return FindNamedNode(Crc::GenerateCRCFromString(p_name));
}

// Added for use by the NodeExists script function
bool NodeExists(uint32 checksum)
{
	return ( FindNamedNode( checksum, true ) == -1 ) ? false : true;

//	// Get the NodeArray
//	CArray *p_node_array=GetArray(0xc472ecc5/*NodeArray*/);
//	Dbg_MsgAssert(p_node_array,("CreateNodeNameHashTable could not find NodeArray"));
//
//	// Scan through each node, getting it's name, and if it has a name add it in to the hash table.	
//	for (uint32 i=0; iGetSize(); ++i)
//	{
//		CStruct *p_node=p_node_array->GetStructure(i);
//		Dbg_MsgAssert(p_node,("NULL p_node"));
//		
//		uint32 name_checksum=0;
//		if (p_node->GetChecksum("Name",&name_checksum))
//		{
//			if ( name_checksum == checksum )
//			{
//				return true;
//			}
//		}
//	}
//	return false;
}

#else

#define	__OLD_HASH_TABLE__  0
#define NODE_NAME_HASHBITS 13

#if __OLD_HASH_TABLE__
#define NODE_NAME_HASH_SIZE ((1<mpNext;
				Mem::Free(p_entry);
				p_entry=p_next;
			}
			sp_node_name_hash_table[i].mpNext=NULL;	
		#endif					
		sp_node_name_hash_table[i].mNameChecksum=0;
		sp_node_name_hash_table[i].mNodeIndex=0;
	}	
}

// Given that a new NodeArray symbol has got loaded due to the loading of a qb containing it,
// this will generate the node name hash table.
void CreateNodeNameHashTable()
{
	Dbg_MsgAssert(s_node_name_hash_table_initialised,("Node name hash table not initialised"));
	
	// Make sure the old one is cleared.
	ClearNodeNameHashTable();

	// Get the NodeArray
	CArray *p_node_array=GetArray(0xc472ecc5/*NodeArray*/);
	Dbg_MsgAssert(p_node_array,("CreateNodeNameHashTable could not find NodeArray"));

	// Make sure we're using the script heap, cos the hash table is going to hang around
	// for the duration of the level.
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ScriptHeap());

	// Scan through each node, getting it's name, and if it has a name add it in to the hash table.	
	for (uint32 i=0; iGetSize(); ++i)
	{
		CStruct *p_node=p_node_array->GetStructure(i);
		Dbg_MsgAssert(p_node,("NULL p_node"));
		
		uint32 name_checksum=0;
		if (p_node->GetChecksum("Name",&name_checksum))
		{
			// Node i has name name_checksum, so add it in to the hash table.
			
			// A checksum of zero is used to indicate an empty hash table entry,
			// so check that the name checksum is not zero by chance.
			Dbg_MsgAssert(name_checksum,("Node has zero name checksum ?"));
			
			SNodeNameHashEntry *p_entry=&sp_node_name_hash_table[name_checksum&((1<mNameChecksum)
			{
				// p_entry is already occupied, so skip to the end of the list that starts at p_entry.
				while (p_entry->mpNext)
				{
					p_entry=p_entry->mpNext;
				}
				
				// p_entry is now the last entry in the list.
				
				// Create a new entry and fill it in.
				SNodeNameHashEntry *p_new=(SNodeNameHashEntry*)Mem::Malloc(sizeof(SNodeNameHashEntry));
				p_new->mNameChecksum=name_checksum;
				p_new->mNodeIndex=i;
				p_new->mpNext=NULL;
				
				// Tag it onto the end of the list.
				p_entry->mpNext=p_new;
				
				
			}
			else
			{
				// p_entry is free, so stick the info in there.
				p_entry->mNameChecksum=name_checksum;
				p_entry->mNodeIndex=i;
				// Quick check for leaks.
				Dbg_MsgAssert(p_entry->mpNext==NULL,("p_entry->mpNext not NULL ??"));
				p_entry->mpNext=NULL;
			}
			#else
			// just skip forward until we find an empty entry
			while (p_entry->mNameChecksum)
			{
				p_entry++;
				// note in the following assertion we leave one entry to guarentee we have
				// a zero entry roadblock to stop searches
				// otherwise we have a slight possibility of very obscure bugs
				Dbg_MsgAssert(p_entry < &sp_node_name_hash_table[NODE_NAME_HASH_SIZE-1],("Hash table overflow"));
			}
			p_entry->mNameChecksum=name_checksum;
			p_entry->mNodeIndex=i;
			#endif
	
		}
	}
	Mem::Manager::sHandle().PopContext();
}
	
// Searches the node array for a node whose Name is the passed checksum.
int FindNamedNode(uint32 checksum, bool assert)
{
	// TODO: Fix bug where this returns 0 when a checksum of 0 is passed. It should
	// not find a node in that case.
	
	Dbg_MsgAssert(s_node_name_hash_table_initialised,("Node name hash table not initialised"));

	SNodeNameHashEntry *p_entry=&sp_node_name_hash_table[checksum&((1<mNameChecksum!=checksum)
	{
		p_entry=p_entry->mpNext;
	}
	
	if (assert)
	{
		Dbg_MsgAssert(p_entry,("No node with name %s found.",FindChecksumName(checksum)));
	}
	if (!p_entry)
	{
		return -1;
	}
	#else
	while (p_entry->mNameChecksum && p_entry->mNameChecksum!=checksum)
	{
		p_entry++;
	}
	
	if (assert)
	{
		Dbg_MsgAssert(p_entry->mNameChecksum,("No node with name %s found.",FindChecksumName(checksum)));
	}
	
	if (!p_entry->mNameChecksum)
	{
		return -1;
	}
	#endif		
	
	return p_entry->mNodeIndex;
}

int FindNamedNode(const char *p_name)
{
	return FindNamedNode(Crc::GenerateCRCFromString(p_name));
}

// Added for use by the NodeExists script function
bool NodeExists(uint32 checksum)
{
	Dbg_MsgAssert(s_node_name_hash_table_initialised,("Node name hash table not initialised"));

	SNodeNameHashEntry *p_entry=&sp_node_name_hash_table[checksum&((1<mNameChecksum!=checksum)
	{
		p_entry=p_entry->mpNext;
	}
	if (p_entry)
	{
		return true;
	}
	#else
	while (p_entry->mNameChecksum && p_entry->mNameChecksum!=checksum)
	{
		p_entry=p_entry++;
	}
	if (p_entry->mNameChecksum)
	{
		return true;
	}
	#endif
	
	return false;
}

#endif		// __PLAT_NGC__

////////////////////////////////////////////////////////////////////
//
// Node name prefix stuff.
//
////////////////////////////////////////////////////////////////////

// Linked list stuff, used when generating the node lists.
// These only exist temporarily in memory.
struct STempNode
{
	int mNodeIndex;
	STempNode *mpNext;
};

// MEMOPT 800K TEMP Size of temporary buffer used when generating prefix info.
#define NUM_TEMP_NODES 120000
static STempNode *sp_temp_nodes=NULL;
static STempNode *sp_free_temp_nodes=NULL;

// The lookup table, which exists in memory for the duration of the level.
struct SPrefixLookup
{
	// Checksum of the prefix string
	uint32 mChecksum;
	
	// Pointer to the list of node indices of the nodes that have this prefix.
	union
	{
		// Points to somewhere inside sp_node_list_buffer
		// pNodeList[0] contains the number of nodes.
		uint16 *mpNodeList;
		
		// Points to a temporary linked list of nodes, which is used for speed when generating the prefix info.
		// (Uses up too much memory to keep in memory all the time)
		STempNode *mpTempNodesHeadPointer;
	};	
};

// MEMOPT 80K PERM An array of SPrefixLookup's, one for each possible prefix.
// This array is kept in order of checksum, smallest checksum first, to enable quick searching
// using a binary search.
#define MAX_PREFIX_LOOKUPS 13000
static SPrefixLookup sp_prefix_lookups[MAX_PREFIX_LOOKUPS];
static uint32 s_num_prefix_lookups=0;

// MEMOPT 200K PERM Big array of uint16's for holding the node lists.
#define NODE_LIST_BUFFER_SIZE 121000
static uint32 s_node_list_buffer_used=0;
static uint16 sp_node_list_buffer[NODE_LIST_BUFFER_SIZE];

// Does a binary search of the lookup table, and returns the index of the entry 
// that has the passed checksum.
// If there is no matching entry, it will return the index of the entry with the next smallest
// checksum.
// If there is no matching entry, and the passed checksum is smaller than the smallest checksum in the
// table, then it will return 0.
static uint32 sFindPrefixLookup(uint32 checksum)
{
	Dbg_MsgAssert(s_num_prefix_lookups,("Zero s_num_prefix_lookups"));
	uint32 bottom=0;
	uint32 top=s_num_prefix_lookups-1;
	uint32 middle=(bottom+top)>>1;
	
	while (bottom!=middle)
	{
		uint32 ch=sp_prefix_lookups[middle].mChecksum;
		
		if (ch==checksum)
		{
			return middle;
		}
		else if (checksum>1;		
	}		
	if (sp_prefix_lookups[top].mChecksum > checksum)
	{
		return bottom;
	}	
	return top;
}

// Creates the big temporary array of nodes.
static void sCreateTempNodes()
{
	Dbg_MsgAssert(sp_temp_nodes==NULL,("sp_temp_nodes not NULL ?"));
	sp_temp_nodes=(STempNode*)Mem::Malloc(NUM_TEMP_NODES*sizeof(STempNode));
	Dbg_MsgAssert(sp_temp_nodes,("Could not allocate sp_temp_nodes"));
	
	// Link them all into a free list.
	for (int i=0; impNext;
	return p_new;
}

// Converts the linked lists of nodes into simple arrays, kept in the sp_node_list_buffer static array.
// Uses up less memory. Also means the rest of the game code that calls GetPrefixInfo does not have
// to be changed.
static void sConvertTempNodes()
{
	Dbg_MsgAssert(s_node_list_buffer_used==0,("Expected s_node_list_buffer_used to be 0"));
	uint16 *p_buf=sp_node_list_buffer;
	
	// For each prefix in the lookup table ...
	for (uint32 i=0; impNext;
		}
		
		// Check there is enough space to copy the node list into.
		Dbg_MsgAssert(s_node_list_buffer_used+1+count<=NODE_LIST_BUFFER_SIZE,("Node list buffer overflow"));

		// Make the prefix entry now point to the simple array.
		sp_prefix_lookups[i].mpNodeList=p_buf;
		
		// First entry is the count.		
		*p_buf++=count;
		// Copy in all the node indices.
		p_node=p_first;
		while (p_node)
		{
			*p_buf++=p_node->mNodeIndex;
			p_node=p_node->mpNext;
		}
		
		// Update the space used. The +1 is for the count.
		s_node_list_buffer_used+=1+count;
	}	
//	Dbg_MsgAssert(0,("buffer = %d\n",s_node_list_buffer_used));	
}

// Adds a new node index to a particular prefix checksum's entry in the lookup table.
static void sAddNewPrefix(uint32 checksum, int nodeIndex)
{
	// sFindPrefixLookup will assert if s_num_prefix_lookups is zero, so do this
	// as a special case for the very first one added.
	if (s_num_prefix_lookups==0)
	{
		STempNode *p_new=sNewTempNode();
		p_new->mNodeIndex=nodeIndex;
		p_new->mpNext=NULL;
		
		// Stick it in at index 0. The array will be maintained as a sorted
		// array from now on.
		sp_prefix_lookups[0].mChecksum=checksum;
		sp_prefix_lookups[0].mpTempNodesHeadPointer=p_new;
		
		++s_num_prefix_lookups;
		// All done.
		return;
	}	
	
	// Look up the checksum, which may not be there.
	int index=sFindPrefixLookup(checksum);
	uint32 ch=sp_prefix_lookups[index].mChecksum;
	// ch may or may not be checksum. If it isn't, it will either be the next smallest one in the
	// array, or it will be the checksum at index 0 in the case of checksum being smaller than all of them.
	
	if (ch==checksum)
	{
		// This prefix is already in the array, so just have to add the new node index to
		// its list.
		
		STempNode *p_new=sNewTempNode();
		p_new->mNodeIndex=nodeIndex;
		p_new->mpNext=sp_prefix_lookups[index].mpTempNodesHeadPointer;
		
		sp_prefix_lookups[index].mpTempNodesHeadPointer=p_new;
	}
	else
	{
		// The prefix is not in the table, so we have to add it.
		
		Dbg_MsgAssert(s_num_prefix_lookupsch)
		{
			// The above if will mostly be true, because usually Ch will be the next smaller checksum
			// in the table. Hence we increment index so that all the checksums above ch get shifted up,
			// so that the new checksum can be inserted at index, maintaining the sort order.
			++index;
		}
		else
		{
			// The above if will be false in the case of checksum being smaller than all the current checksums
			// in the table. In this case, all the checksums need to be shifted up and checksum inserted at the
			// bottom, so index should be zero here.
			Dbg_MsgAssert(index==0,("index not zero ?"));
		}		

		// Shift everything up one so that the new checksum can be inserted.
		for (int i=s_num_prefix_lookups; i>index; --i)
		{
			sp_prefix_lookups[i]=sp_prefix_lookups[i-1];
		}
		
		// Insert the new checksum, and make a new list for it with just one entry at the moment.
		STempNode *p_new=sNewTempNode();
		p_new->mNodeIndex=nodeIndex;
		p_new->mpNext=NULL;
		
		sp_prefix_lookups[index].mChecksum=checksum;
		sp_prefix_lookups[index].mpTempNodesHeadPointer=p_new;
		
		// Increment the count.
		++s_num_prefix_lookups;
	}
}

void DeletePrefixInfo()
{
	// Just to be sure.
	sDeleteTempNodes();
	
	s_node_list_buffer_used=0;
	s_num_prefix_lookups=0;
}	

// This is called from LoadQB if the QB contains a NodeArray.	
void GeneratePrefixInfo()
{
	DeletePrefixInfo();
	
	// Create the temporary array of nodes used to make linked lists in sAddNewPrefix, for speed.
	sCreateTempNodes();

	// Scan through all the nodes, look up their names, and add all their possible
	// prefixes to the lookup table.
	CArray *p_node_array=GetArray(0xc472ecc5/*NodeArray*/);
	Dbg_MsgAssert(p_node_array,("NodeArray not found"));
	for (uint32 i=0; iGetSize(); ++i)
	{
		CStruct *p_node=p_node_array->GetStructure(i);
		Dbg_MsgAssert(p_node,("NULL p_node"));
		uint32 name_checksum=0;
		p_node->GetChecksum("Name",&name_checksum);
		
		if (name_checksum)
		{
			// Search the hash table for the name.
			const char *p_name=GetChecksumNameFromLastQB(name_checksum);
			if (p_name)
			{
				// Add all the possible prefixes to the lookup table.
				int string_length=strlen(p_name);
				for (int j=1; j<=string_length; ++j)
				{
					sAddNewPrefix(Crc::GenerateCRC(p_name,j),i);
				}		
			}	
		}
	}

	// Move the info in the linked lists into the more memory efficient static array.
	sConvertTempNodes();
	// Remove the temporary stuff.
	sDeleteTempNodes();

	#ifdef __NOPT_ASSERT__
	// Quick check to make sure the lookup table seems right.
	if (s_num_prefix_lookups)
	{
		for (uint32 i=0; iGetStructure(*p_nodes);
		Mth::Vector node_pos;
		p_node->GetVector(CRCD(0x7f261953,"pos"),&node_pos);
		float	node_dist = (node_pos - pos).LengthSqr();
		//printf ("Node %d at (%.02f, %.02f, %.02f)  dist %.02f\n",*p_nodes,node_pos[X],node_pos[Y],node_pos[Z],node_dist);
		if (node_dist < closest_dist)
		{
		  //  printf ("closest node %d\n",*p_nodes);
			closest_dist = node_dist;
			closest_node = *p_nodes;
		}
	
		p_nodes++;
		num_nodes--;
	}
	
	return closest_node;				   
				   
}


// return the index of the node nearest to this position that
// matches this prefix
int	GetNearestNodeByPrefix(const char *p_prefix, const Mth::Vector &pos)
{
	return GetNearestNodeByPrefix(Crc::GenerateCRCFromString(p_prefix),pos);
}


////////////////////////////////////////////////////////////////////
//
// Utility functions for getting nodes, links, etc
//
////////////////////////////////////////////////////////////////////

CStruct *GetNode(int nodeIndex)
{
	CArray *p_node_array=GetArray(0xc472ecc5/*NodeArray*/);
	Dbg_MsgAssert(p_node_array,("NodeArray not found"));
	CStruct *p_node=p_node_array->GetStructure(nodeIndex);
	Dbg_MsgAssert(p_node,("NULL p_node"));
	return p_node;
}


// This is a DEPRECATED function
// only in to support the rather odd manner of access in nodes via index
uint32	GetNodeNameChecksum(int nodeIndex)
{
	CStruct *p_struct = GetNode(nodeIndex);
	uint32 name = 0;
	p_struct->GetChecksum(CRCD(0xa1dc81f9,"name"),&name);
	return name;
}

uint32 GetNumLinks(CStruct *p_node)
{
	Dbg_MsgAssert(p_node,("NULL p_node"));
	CArray *p_links=NULL;
	p_node->GetArray(0x2e7d5ee7/*Links*/,&p_links);
	if (p_links==NULL)
	{
		return 0;
	}
	return p_links->GetSize();
}

uint32 GetNumLinks(int nodeIndex)
{
	CArray *p_node_array=GetArray(0xc472ecc5/*NodeArray*/, NO_ASSERT);
	if (p_node_array)
	{
		CStruct *p_node=p_node_array->GetStructure(nodeIndex);
		Dbg_MsgAssert(p_node,("NULL p_node"));
	
		return GetNumLinks(p_node);
	}	
	return 0;
}

int GetLink(CStruct *p_node, int linkNumber)
{
	Dbg_MsgAssert( p_node, ( "NULL p_node" ) );
	CArray *p_links=NULL;
	p_node->GetArray( CRCD( 0x2e7d5ee7, "Links" ), &p_links);
	Dbg_MsgAssert( p_links, ( "Tried to call GetLink when there are no links" ) );
	Dbg_MsgAssert( p_links->GetSize(), ( "Tried to call GetLink when there are no links (empty Links array)" ) );
	return p_links->GetInteger( linkNumber );
}

int GetLink(int nodeIndex, int linkNumber)
{
	return GetLink(GetNode(nodeIndex),linkNumber);
}

// return true if Node1 is linked to Node2
bool IsLinkedTo(int node1, int node2)
{
	int n = GetNumLinks(node1);
	for (int i = 0; iGetVector(CRCD(0xb9d31b0a,"Position"),p_vector,Script::NO_ASSERT) )
	{
		uint32 name = 0;
		p_node->GetChecksum(CRCD(0xa1dc81f9,"name"),&name);
		if (name)
		{
			Dbg_Message( "Warning:  'Position' deprecated! reexport, node %s",Script::FindChecksumName(name) );
		}
		else
		{
			Dbg_Message( "Warning:  'Position' deprecated! reexport" );
		}
	}
	#endif

	if ( p_node->GetVector(CRCD(0x7f261953,"pos"),p_vector,Script::NO_ASSERT) )
	{
		// no need to negate the new style vectors
	}
	else if ( p_node->GetVector(CRCD(0xb9d31b0a,"Position"),p_vector,Script::NO_ASSERT) )
	{
		(*p_vector)[Z]=-(*p_vector)[Z];
	}
	else
	{
		*p_vector = Mth::Vector(0.0f,0.0f,0.0f,1.0f);
//		Script::PrintContents( p_node );
//		Dbg_MsgAssert( 0, ( "No 'pos' vector found in node" ) );
	}
}

void GetPosition(int nodeIndex, Mth::Vector *p_vector)
{
	Dbg_MsgAssert(p_vector,("NULL p_vector"));
	GetPosition(GetNode(nodeIndex),p_vector);
}

void GetOrientation(CStruct *p_node, Mth::Matrix *p_matrix)
{
	Dbg_MsgAssert(p_node,("NULL p_node"));
	Dbg_MsgAssert(p_matrix,("NULL p_matrix"));
	   
	Mth::Vector orientation_vector;
	if ( p_node->GetVector(CRCD(0xc97f3aa9, "orientation"),&orientation_vector,Script::NO_ASSERT) )
	{
		// assumes a positive scalar component
		Mth::Quat orientation_quat;
		orientation_quat.SetVector(orientation_vector);
		orientation_quat.SetScalar(sqrtf(1.0f - orientation_vector.LengthSqr()));
		
		orientation_quat.GetMatrix(*p_matrix);
	}
	else
	{
		p_matrix->Identity();
	}
}

void GetOrientation(int nodeIndex, Mth::Vector *p_matrix)
{
	Dbg_MsgAssert(p_matrix,("NULL p_matrix"));
	GetPosition(GetNode(nodeIndex),p_matrix);
}

void GetAngles(CStruct *p_node, Mth::Vector *p_vector)
{
	Dbg_MsgAssert(p_node,("NULL p_node"));
	Dbg_MsgAssert(p_vector,("NULL p_vector"));
	// Make sure they are initialised to zero, in case there is no Angles component.
	// Angles components of (0,0,0) are ommitted to save memory.
	p_vector->Set(); 
	p_node->GetVector(0x9d2d0915/*Angles*/,p_vector);
}

void GetAngles(int nodeIndex, Mth::Vector *p_vector)
{
	Dbg_MsgAssert(p_vector,("NULL p_vector"));
	GetAngles(GetNode(nodeIndex),p_vector);
}

CArray *GetIgnoredLightArray(CStruct *p_node)
{
	Dbg_MsgAssert(p_node,("NULL p_node"));
	CArray *p_ignored_lights=NULL;
	p_node->GetArray(0xb7b030be/*IgnoredLights*/,&p_ignored_lights);
	return p_ignored_lights;
}

CArray *GetIgnoredLightArray(int nodeIndex)
{
	return GetIgnoredLightArray(GetNode(nodeIndex));
}

// Used by Ryan in the Park Editor.
// This creates an array (called NodeArray) of Size structures, with all the entries initialised to empty structures.
void CreateNodeArray(int size, bool hackUseFrontEndHeap)
{
	// copied from Script::Cleanup()
	//KillStoppedScripts();
	//RemoveOldTriggerScripts();
	//RemoveSymbol(GenerateCRC("TriggerScripts"));
	CleanUpAndRemoveSymbol(CRCD(0xc472ecc5,"NodeArray"));
	DeletePrefixInfo();
	ClearNodeNameHashTable();
	
	// Make sure it doesn't exist already.
	// ParseQB will catch this anyway, but may as well check before getting there.
	Dbg_MsgAssert(GetArray(CRCD(0xc472ecc5,"NodeArray"))==NULL,("Called CreateNodeArray when a NodeArray already exists"));


	// Create a dummy QB file, and parse it the usual way.	
	if (hackUseFrontEndHeap)
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap()); // Use the temporary heap.
	else
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); // Use the temporary heap.
	uint8 *p_dummy_qb=(uint8*)Mem::Malloc((1			// ESCRIPTTOKEN_NAME
										 +4			// Checksum of 'NodeArray'
										 +1			// ESCRIPTTOKEN_EQUALS
										 +1			// ESCRIPTTOKEN_STARTARRAY
										 +2*size	// ESCRIPTTOKEN_STARTSTRUCT,ESCRIPTTOKEN_ENDSTRUCT pairs
										 +1			// ESCRIPTTOKEN_ENDARRAY
										 +1			// ESCRIPTTOKEN_ENDOFFILE
										 )*sizeof(uint8));
	uint8 *p_qb=p_dummy_qb;
	
	*p_qb++=ESCRIPTTOKEN_NAME;
	*p_qb++=0xc5; // Checksum of 'NodeArray'
	*p_qb++=0xec;
	*p_qb++=0x72;
	*p_qb++=0xc4;
	*p_qb++=ESCRIPTTOKEN_EQUALS;
	*p_qb++=ESCRIPTTOKEN_STARTARRAY;
	for (int i=0; imGotReloaded=false;

	Obj::InsertGoalEditorNodes();
	
	Mem::Free(p_dummy_qb);
	Mem::Manager::sHandle().PopContext();
}

////////////////////////////////////////////////////////////////////
//
// Given a script, and the checksum of a function to look for,
// this will search through the script for any calls to that function.
// It will recursively search through any script calls.
// The callback function is called for each occurrence of the function
// call found, and the parameter list for that call is also passed.
//
// NOTE: Don't store the passed CStruct pointer! 
// It's a local variable in FindReferences.
// To store the parameters create a new CStruct and use the
// AppendStructure member function to copy in the contents of the
// passed one.
//
////////////////////////////////////////////////////////////////////

// Note: p_args contains the set of parameters that were passed to scriptToScanThrough. It is passed so that
// the <,> syntax can be evaluated. This is necessary because sometimes the EndGap command is not directly
// passed the text and score, they might get passed via the <...> syntax, for example see the
// Gap_Gen_End script in sk4_scripts.q
static void FindReferences(uint32 scriptToScanThrough, uint32 functionToScanFor, void (*p_callback)(Script::CStruct *, const uint8 *), Script::CStruct *p_args, int Count)
{
	// Don't recurse too deeply otherwise some levels take ages finding the gaps (eg, the secret level)
	if (Count > 5)
	{
		return;
	}
		
	Dbg_MsgAssert(p_callback,("NULL p_callback"));

	#ifdef __NOPT_ASSERT__
	// Look up the function being scanned for to see if it is a cfunction.
    bool (*p_cfunction)(CStruct *pParams, CScript *pCScript)=NULL;
	CSymbolTableEntry *p_function_entry=LookUpSymbol(functionToScanFor);
	if (p_function_entry && p_function_entry->mType==ESYMBOLTYPE_CFUNCTION)
	{
		p_cfunction=p_function_entry->mpCFunction;
	}	
	Dbg_MsgAssert(p_cfunction==NULL,("Cannot use FindReferences to find cfunction calls yet ..."));
	#endif

	#ifdef NO_SCRIPT_CACHING
	// Look up the script
	CSymbolTableEntry *p_script_entry=LookUpSymbol(scriptToScanThrough);
	if (!p_script_entry)
	{
		Dbg_Warning("Script '%s' not found in call to FindReferences",FindChecksumName(scriptToScanThrough));
		return;
	}
	Dbg_MsgAssert(p_script_entry->mType==ESYMBOLTYPE_QSCRIPT,("'%s' is not a qscript",FindChecksumName(scriptToScanThrough)));
	
	// Get a pointer to it.
	uint8 *p_token=p_script_entry->mpScript;
	Dbg_MsgAssert(p_token,("NULL p_token ???"));
	// Skip over the 4-byte contents checksum.
	p_token+=4;
	#else
	
	Script::CScriptCache *p_script_cache=Script::CScriptCache::Instance();
	Dbg_MsgAssert(p_script_cache,("NULL p_script_cache"));
	uint8 *p_script=p_script_cache->GetScript(scriptToScanThrough);
	Dbg_MsgAssert(p_script,("Script %s not found in script cache",Script::FindChecksumName(scriptToScanThrough)));
	uint8 *p_token=p_script;
	
	#endif
	
	
	// Create a structure for holding parameters.
	CStruct *p_params=new CStruct;

	// Skip to the first line of the script.	
	p_token=SkipToStartOfNextLine(p_token);

	
	// Now scan through each token of the script in turn, looking for any calls to functionToScanFor.
	
	bool finished=false;
    while (!finished)
    {
        switch (*p_token)
        {
		case ESCRIPTTOKEN_RUNTIME_CFUNCTION:
		{
			// Skip over CFunction calls for now.
			p_token+=5;
			p_token=SkipToStartOfNextLine(p_token);
			break;
		}
		
        case ESCRIPTTOKEN_NAME:
		case ESCRIPTTOKEN_RUNTIME_MEMBERFUNCTION:
        {
			// Remember the location for passing to the callback.
			const uint8 *p_location=p_token;
			
            ++p_token;
            uint32 name_checksum=Read4Bytes(p_token).mChecksum;
            p_token+=4;

			// Skip over lines of script that are setting parameters
			if (*p_token==ESCRIPTTOKEN_EQUALS)
			{
				break;
			}		
			
            CSymbolTableEntry *p_entry=Resolve(name_checksum);
			if (p_entry)
			{
				switch (p_entry->mType)
				{
				case ESYMBOLTYPE_CFUNCTION:
					Dbg_MsgAssert(p_entry->mpCFunction,("\nLine %d of %s\nNULL pCFunction",GetLineNumber(p_token),FindChecksumName(p_entry->mSourceFileNameChecksum)));
				
					if (name_checksum==functionToScanFor)
					{
						// Found the required function call! Get the params and call the callback.
						p_params->Clear();
						// Note: Uses p_args to look up any params enclosed in <,> that are encountered.
						p_token=AddComponentsUntilEndOfLine(p_params,p_token,p_args);
						
						//printf("Callback: %s\n",FindChecksumName(scriptToScanThrough));
						(*p_callback)(p_params,p_location);
					}
					else
					{
						p_token=SkipToStartOfNextLine(p_token);
					}
					break;
					
				case ESYMBOLTYPE_MEMBERFUNCTION:
					if (name_checksum==functionToScanFor)
					{
						// Found the required function call! Get the params and call the callback.
						p_params->Clear();
						// Note: Uses p_args to look up any params enclosed in <,> that are encountered.
						p_token=AddComponentsUntilEndOfLine(p_params,p_token,p_args);
						
						//printf("Callback: %s\n",FindChecksumName(scriptToScanThrough));
						(*p_callback)(p_params,p_location);
						
						if (name_checksum==0xe5399fb2) // EndGap
						{
							uint32 GapScript=0;
							if (p_params->GetChecksum("GapScript",&GapScript))
							{
								// Passing NULL for p_args here because it is not clear what parameters are
								// going to be passed to the GapScript when it is run.
								FindReferences(GapScript,functionToScanFor,p_callback,NULL,Count+1);
							}	
						}	
					}
					else
					{
						p_token=SkipToStartOfNextLine(p_token);
					}
					break;
					
				case ESYMBOLTYPE_QSCRIPT:
					p_params->Clear();
					// Note: Uses p_args to look up any params enclosed in <,> that are encountered.
					p_token=AddComponentsUntilEndOfLine(p_params,p_token,p_args);
					
					if (name_checksum==functionToScanFor)
					{
						// Found the required function call! Get the params and call the callback.
						//printf("Callback: %s\n",FindChecksumName(scriptToScanThrough));
						(*p_callback)(p_params,p_location);
					}
					else
					{
						p_token=SkipToStartOfNextLine(p_token);
					}
					
					// It's a q-script, so recursively search that too.
					// Passing the just calculated p_params so that the <,> syntax within the script
					// can be evaluated.
					FindReferences(name_checksum,functionToScanFor,p_callback,p_params,Count+1);
					break;
					
				default:
					break;
				}    
			}
            break;
        }    

        case ESCRIPTTOKEN_KEYWORD_REPEAT:
            p_token=SkipToken(p_token);
			// Scan over any repeat parameters.
			p_token=SkipToStartOfNextLine(p_token);
            break;

		case ESCRIPTTOKEN_KEYWORD_IF:
            p_token=SkipToken(p_token);

			if (*p_token==ESCRIPTTOKEN_KEYWORD_NOT)
			{
				p_token=SkipToken(p_token);
			}
				
			// If the if keyword is followed by an open parenthesis, then it must be using an
			// expression. Often the expression may contain c-function calls, such as:
			// if ( (GotParam Foo) | (GotParam Blaa) )
			// This was causing a problem because this loop would skip over the open parenthesis,
			// get to the GotParam, and then recognizing that GotParam is a cfunction it would
			// try to skip to the next line, but then that would cause a parenthesis mismatch
			// assert in SkipToStartOfNextLine because the open parenth had been skipped over.
			// So to get around that, skip to the next line as soon as the open parenth is detected.
			if (*p_token==ESCRIPTTOKEN_OPENPARENTH)
			{
				p_token=SkipToStartOfNextLine(p_token);
			}	
			break;
			
        case ESCRIPTTOKEN_KEYWORD_ENDSCRIPT:
			finished=true;
            break;

        default:
            p_token=SkipToken(p_token);
            break;
        }
    }

	#ifndef NO_SCRIPT_CACHING
	p_script_cache->DecrementScriptUsage(scriptToScanThrough);
	#endif
	
	// Delete the temporary p_params.
	delete p_params;	
}

void ScanNodeScripts(uint32 componentName, uint32 functionName, void (*p_callback)(CStruct *, const uint8 *))
{
	Script::DisableExpressionEvaluatorErrorChecking();
	
	CArray *p_node_array=GetArray(0xc472ecc5); // Checksum of NodeArray
	Dbg_MsgAssert(p_node_array,("NodeArray not found"));
	
	for (uint32 i=0; iGetSize(); ++i)
	{
		CStruct *p_node=p_node_array->GetStructure(i);
		Dbg_MsgAssert(p_node,("NULL p_node"));
		
		uint32 script_checksum=0;
		if (p_node->GetChecksum(componentName,&script_checksum))
		{
			FindReferences(script_checksum,functionName,p_callback,NULL,0);
		}	
	}
	
	Script::EnableExpressionEvaluatorErrorChecking();
}

} // namespace SkateScript



================================================
FILE: Code/Sk/Scripting/nodearray.h
================================================
#ifndef	__SK_SCRIPTING_NODEARRAY_H
#define	__SK_SCRIPTING_NODEARRAY_H

#ifndef __CORE_DEFINES_H
#include 
#endif

namespace Mth
{
class Vector;
class Matrix;
}

namespace Script
{
class CStruct;
class CArray;
}	

namespace SkateScript
{
using namespace Script;

void InitNodeNameHashTable();
void ClearNodeNameHashTable();
void CreateNodeNameHashTable();
int FindNamedNode(uint32 checksum, bool assert=true);
int FindNamedNode(const char *p_name);
bool NodeExists(uint32 checksum);

void DeletePrefixInfo();
void GeneratePrefixInfo();
const uint16 *GetPrefixedNodes(const char *p_prefix, uint16 *p_numMatches);
const uint16 *GetPrefixedNodes(uint32 checksum, uint16 *p_numMatches);

int	GetNearestNodeByPrefix(uint32 prefix, const Mth::Vector &pos);
int	GetNearestNodeByPrefix(const char * p_prefix, const Mth::Vector &pos);


CStruct *GetNode(int nodeIndex);
uint32	GetNodeNameChecksum(int nodeIndex);
uint32 GetNumLinks(CStruct *p_node);
uint32 GetNumLinks(int nodeIndex);
int GetLink(CStruct *p_node, int linkNumber);
int GetLink(int nodeIndex, int linkNumber);
bool IsLinkedTo(int node1, int node2);

void GetPosition(CStruct *p_node, Mth::Vector *p_vector);
void GetPosition(int nodeIndex, Mth::Vector *p_vector);
void GetOrientation(CStruct *p_node, Mth::Matrix *p_matrix);
void GetOrientation(int nodeIndex, Mth::Matrix *p_matrix);
void GetAngles(CStruct *p_node, Mth::Vector *p_vector);
void GetAngles(int nodeIndex, Mth::Vector *p_vector);

CArray *GetIgnoredLightArray(CStruct *p_node);
CArray *GetIgnoredLightArray(int nodeIndex);

void CreateNodeArray(int size, bool hackUseFrontEndHeapYouFucker);
void ScanNodeScripts(uint32 componentName, uint32 functionName, void (*p_callback)(CStruct *, const uint8 *));

} // namespace SkateScript

#endif // #ifndef	__SK_SCRIPTING_NODEARRAY_H



================================================
FILE: Code/Sk/Scripting/skfuncs.cpp
================================================
//****************************************************************************
//* MODULE:         Sk/Scripting
//* FILENAME:       SkFuncs.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  6/10/2002
//****************************************************************************

// start autoduck documentation
// @DOC skfuncs
// @module skfuncs | None
// @subindex Scripting Database
// @index script | skfuncs

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
							 
#include 
#include 

#include 
#include 
#include 

#include 
#include 

#include  
#include 
#include  
#include  
#include 
#include 

#include 
#include 
 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
#include 

#include 

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Front
{
	extern void SetScoreTHPS4(char* score_text, int skater_num);
}

namespace CFuncs
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void s_preload_ped_part( uint32 partChecksum )
{
	Script::CArray* pArray = Script::GetArray( partChecksum );
	Dbg_Assert( pArray );

	for ( uint32 i = 0; i < pArray->GetSize(); i++ )
	{
		Script::CStruct* pStruct = pArray->GetStructure( i );

		int allow_random = 0;
		pStruct->GetInteger( CRCD(0xf1e3cd22,"allow_random"), &allow_random, Script::NO_ASSERT );
			
		if ( allow_random )
		{
			const char* pMeshName;
			if ( pStruct->GetText( CRCD(0x1e90c5a9,"mesh"), &pMeshName, false ) )
			{
				Nx::CModel* pDummy = Nx::CEngine::sInitModel();
//				Dbg_Message("Preloading random ped model %s", pMeshName );
				pDummy->AddGeom( pMeshName, 0, true );
				Nx::CEngine::sUninitModel( pDummy );
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Score queries:

static int s_get_score_from_params( Script::CStruct *pParams, Script::CScript *pScript )
{  
	int score;
	if ( !pParams->GetInteger( NONAME, &score ) )
	{
		Dbg_MsgAssert( 0,( "\n%s\nMust provide an integer for s_get_score_from_params.", pScript->GetScriptInfo( ) ));
		return ( 0 );
	}
	return ( score );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static Mdl::Score* s_get_score_struct( void )
{
	// find the skater, get the score landed:
	Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetLocalSkater();						   
	if ( pSkater )
	{
		return ( pSkater->GetScoreObject() );
	}
	return ( NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Local function to return a career, given a flag number
static Obj::CSkaterCareer* s_get_career( int flag, Script::CStruct *pParams )
{
// Now this always just returns the global career, just kept for convenience	
	return Mdl::Skate::Instance()->GetCareer();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CurrentSkaterIsPro | Checks if the current skater is pro
bool ScriptCurrentSkaterIsPro(Script::CStruct *pParams, Script::CScript *pScript)
{   
	Obj::CSkaterProfile *pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile(pParams);
	return ( pSkaterProfile && pSkaterProfile->IsPro() );
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetGoalsCompleted | Returns goals (competitions) completed
bool ScriptGetGoalsCompleted(Script::CStruct *pParams, Script::CScript *pScript)
{   
	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();
	Dbg_MsgAssert(pCareer,("NULL pCareer"));
	
	int LevelNumber=0;
	pParams->GetInteger(NONAME,&LevelNumber);
	char pBuf[100];

	sprintf(pBuf,"%d/9",pCareer->CountGoalsCompleted(LevelNumber));
	pScript->GetParams()->AddComponent(CRCD(0xc661fe79,"GoalsCompleted"),ESYMBOLTYPE_STRING,pBuf);
	pScript->GetParams()->AddComponent(CRCD(0xa43dc969,"BestMedal"),ESYMBOLTYPE_STRING,"---");
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetNextLevelRequirements | Gets number of goals needed for next level
bool ScriptGetNextLevelRequirements(Script::CStruct *pParams, Script::CScript *pScript)
{   
	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();
	Dbg_MsgAssert(pCareer,("NULL pCareer"));
	
	char pBuf[100];
	sprintf(pBuf,"---");
	
	int TotalGoals=pCareer->CountTotalGoalsCompleted();
	int TotalMedals=pCareer->CountMedals();
	
	Script::CArray *pUnlockRequirementsArray=Script::GetArray(CRCD(0x1d8f1a7e,"UnlockRequirements"));
	for (uint32 i=0; iGetSize(); ++i)
	{
		Script::CStruct *pUnlockRequirements=pUnlockRequirementsArray->GetStructure(i);
		
		int NumGoalsRequired=0;
		int NumMedalsRequired=0;
		
		if (pUnlockRequirements->ContainsFlag(CRCD(0x38dbe1d0,"Goals")) || pUnlockRequirements->ContainsFlag(CRCD(0x032314d1,"Goal")))
		{
			// Need a certain number of goals.
			pUnlockRequirements->GetInteger(NONAME,&NumGoalsRequired);
		}	
				
		if (pUnlockRequirements->ContainsFlag(CRCD(0x56907f8b,"Medals")) || pUnlockRequirements->ContainsFlag(CRCD(0x23bba846,"Medal")))
		{
			// Need a certain number of medals.
			pUnlockRequirements->GetInteger(NONAME,&NumMedalsRequired);
		}
		
		#if ENGLISH
		if (TotalGoalsGetParams()->AddComponent(CRCD(0xaf2305a6,"NextLevelRequirements"),ESYMBOLTYPE_STRING,pBuf);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetCurrentSkaterProfile | 
// @uparmopt 0 | Skater profile index - must be 0 or 1
bool ScriptSetCurrentSkaterProfile(Script::CStruct *pParams, Script::CScript *pScript)
{	
	int Profile=0;
	pParams->GetInteger(NONAME,&Profile);
	Dbg_MsgAssert(Profile==0 || Profile==1,("\n%s\nBad index sent to SetCurrentSkaterProfile, must be 0 or 1",pScript->GetScriptInfo()));
	
	
	Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
	pPlayerProfileManager->SetCurrentProfileIndex(Profile);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CurrentSkaterProfileIs | Checks if current skater profile is 
// equal to index value passed in
// @uparmopt 0 | Index value - must be 0 or 1
bool ScriptCurrentSkaterProfileIs(Script::CStruct *pParams, Script::CScript *pScript)
{   
	int Profile=0;
	pParams->GetInteger(NONAME,&Profile);
	Dbg_MsgAssert(Profile==0 || Profile==1,("\n%s\nBad index sent to CurrentSkaterProfileIs, must be 0 or 1",pScript->GetScriptInfo()));
	
	
	Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
	
	return pPlayerProfileManager->GetCurrentProfileIndex()==Profile;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AddSkaterProfile | Adds skater profile 
// @parm name | name | The name of the profile
bool ScriptAddSkaterProfile(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
	return pPlayerProfileManager->AddNewProfile( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AddTemporaryProfile | Adds temporary skater profile
// (used for credits, to temporarily hijack the skater profile
bool ScriptAddTemporaryProfile(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 profileName;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &profileName, Script::ASSERT );
	
	Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
	return pPlayerProfileManager->AddTemporaryProfile( profileName );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RememberTemporaryAppearance | 
bool ScriptRememberTemporaryAppearance(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 profileName;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &profileName, Script::ASSERT );
	
	Gfx::CModelAppearance theAppearance;
	Gfx::CModelAppearance* pAppearance = NULL;
	
	Obj::CPlayerProfileManager* pProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();

	Script::CStruct* pAppearanceStruct;
	if ( pParams->GetStructure( CRCD(0xefc8944c,"appearance_structure"), &pAppearanceStruct, Script::NO_ASSERT ) )
	{
		theAppearance.Load( pAppearanceStruct );
		pAppearance = &theAppearance;
	}
	else
	{
		int playerNum;
		pParams->GetInteger( CRCD(0x67e6859a,"player"), &playerNum, Script::ASSERT );

		Obj::CSkaterProfile* pProfile = pProfileManager->GetProfile( playerNum );
		Dbg_Assert( pProfile );
		pAppearance = pProfile->GetAppearance();
	}
	Dbg_Assert( pAppearance );

	Obj::CSkaterProfile* pTempProfile = pProfileManager->GetTemporaryProfile( profileName );
	Dbg_Assert( pTempProfile );
	Gfx::CModelAppearance* pTempAppearance = pTempProfile->GetAppearance();
	Dbg_Assert( pTempAppearance );

	if ( pParams->ContainsFlag( CRCD(0x3af36e6d,"NoFaceTexture") ) )
	{
		Script::CStruct* pOriginalStructure = pAppearance->GetStructure();
		Script::CStruct* pTempStructure = pTempAppearance->GetStructure();
		pTempStructure->Clear();
		pTempStructure->AppendStructure( pOriginalStructure );
	}
	else
	{
		*pTempAppearance = *pAppearance;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RestoreTemporaryAppearance | 
bool ScriptRestoreTemporaryAppearance(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 profileName;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &profileName, Script::ASSERT );
	
	int playerNum;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &playerNum, Script::ASSERT );

	Obj::CPlayerProfileManager* pProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	Obj::CSkaterProfile* pProfile = pProfileManager->GetProfile( playerNum );
	Dbg_Assert( pProfile );
	Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();
	Dbg_Assert( pAppearance );

	Obj::CSkaterProfile* pTempProfile = pProfileManager->GetTemporaryProfile( profileName );
	Dbg_Assert( pTempProfile );
	Gfx::CModelAppearance* pTempAppearance = pTempProfile->GetAppearance();
	Dbg_Assert( pTempAppearance );

	if ( pParams->ContainsFlag( CRCD(0x3af36e6d,"NoFaceTexture") ) )
	{
		Script::CStruct* pOriginalStructure = pAppearance->GetStructure();
		Script::CStruct* pTempStructure = pTempAppearance->GetStructure();
		pOriginalStructure->Clear();
		pOriginalStructure->AppendStructure( pTempStructure );
	}
	else
	{
		*pAppearance = *pTempAppearance;
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SyncPlayer2Profile | 
bool ScriptSyncPlayer2Profile(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
	pPlayerProfileManager->SyncPlayer2();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PreloadModels | 
bool ScriptPreloadModels( Script::CStruct *pParams, Script::CScript *pScript )
{
	// Load all the files to get them into the asset manager, 
	// while the PRE file is still in memory

	Mem::PushMemProfile("PreLoadModels");
  
	// now loop through the NodeArray, looking for single-skinned models
	Script::CArray *pNodeArray = Script::GetArray( CRCD(0xc472ecc5,"NodeArray") );
	Dbg_MsgAssert( pNodeArray, ( "No NodeArray found in ParseNodeArray" ) );

	for ( uint32 i = 0; i < pNodeArray->GetSize(); i++ )
	{
		Script::CStruct* pNode = pNodeArray->GetStructure( i );
		Dbg_MsgAssert( pNode, ( "NULL pNode" ) );

		// The following nodes are capable of being ignored in net games		
				
		if ( Mdl::Skate::Instance()->ShouldBeAbsentNode( pNode ) )
		{
			// Don't load, as it's a net game
		}	
		else 
		{
			uint32 ClassChecksum = 0;
			pNode->GetChecksum( CRCD(0x12b4e660,"Class"), &ClassChecksum );

			uint32 TypeChecksum = 0;
			pNode->GetChecksum(CRCD(0x7321a8d6,"Type"), &TypeChecksum );
			
			// first eliminate the flag models, if this is non-net game 
			// On load model IF a multiplayer game OR NOT a flag model
			if (Mdl::Skate::Instance()->IsMultiplayerGame() ||
				TypeChecksum != CRCD(0xbebb41f0,"Flag_Red")         && 
				TypeChecksum != CRCD(0x836284a5,"Flag_Red_Base")    &&
				TypeChecksum != CRCD(0xc3ebe05e,"Flag_Green") 		 && 
				TypeChecksum != CRCD(0x4f8ff239,"Flag_Green_base")	 &&
				TypeChecksum != CRCD(0x8cca938a,"Flag_Blue")		 &&
				TypeChecksum != CRCD(0x41e5bcf,"Flag_Blue_base")	 &&
				TypeChecksum != CRCD(0xc2af1eb1,"Flag_Yellow")		 &&
				TypeChecksum != CRCD(0xed3ad7fe,"Flag_Yellow_base") )
			{
	
				// Handle pre-loading of models for non-net game nodes
				switch (ClassChecksum)
				{
					case 0xa0dfac98: // Pedestrian
					case 0xa71394a2: // AnimatingObject
					case 0x19b1e241: // ParticleEmitter				
						break;
					case 0xe47f1b79: // Vehicle
					case 0xef59c100: // GameObject
						{
							const char *pModelName = NULL;
	
							if ( pNode->GetText( 0x286a8d26, &pModelName ) ) // checksum 'model'
							{
								const char* p_model_name;
								pNode->GetText( CRCD(0x286a8d26,"model"), &p_model_name, true );
								if (stricmp("none",p_model_name) != 0)
								{		   
									Str::String fullModelName;
									fullModelName = Gfx::GetModelFileName(p_model_name, ".mdl");
									
									Nx::CModel* p_dummy = Nx::CEngine::sInitModel();
									//Dbg_Message( "Preloading model %s", fullModelName.getString() );
	
									//int texDictOffset = 0;
									//pNode->GetInteger( "texDictOffset", &texDictOffset, false );
	
									bool forceTexDictLookup = pNode->ContainsFlag( CRCD(0x6c37fdc7,"AllowReplaceTex") );
									
									p_dummy->AddGeom( fullModelName.getString(), 0, true, 0, forceTexDictLookup );
									Nx::CEngine::sUninitModel( p_dummy );
								}
							}
						}
						break;
					default:
						break;
				}
			}
		}				
	}

	Mem::PopMemProfile(/*"PreLoadModels"*/);
	
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PreloadPedestrians | 
bool ScriptPreloadPedestrians( Script::CStruct *pParams, Script::CScript *pScript )
{
//	Tmr::Time baseTime = Tmr::ElapsedTime(0);

	// should be skipped for net game?
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	if ( gamenet_man->InNetGame()/* || Mdl::Skate::Instance()->IsMultiplayerGame()*/)
	{
		return false;
	}

	if ( Script::GetInt( "NoPreloadRandomPeds", false ) )
	{
		// some create-a-skater artists don't want to
		// preload their random peds
		return false;
	}

	Mem::PushMemProfile("PreLoadPedestrians");

	if (pParams->ContainsFlag(CRCD(0xf6f8e158,"no_random")))
	{
		// This feature used when pre-loading peds for created goals in the park editor,
		// where we don't want loads of parts cos there isn't much memory
	}
	else
	{
		// loop through the master_editable_list, looking for
		// randomizable parts (based on the "allow_random" flag
		// which should have been set when preselecting ped parts.
		s_preload_ped_part( Script::GenerateCRC("ped_m_head") );
		s_preload_ped_part( Script::GenerateCRC("ped_m_torso") );
		s_preload_ped_part( Script::GenerateCRC("ped_m_legs") );
		s_preload_ped_part( Script::GenerateCRC("ped_f_head") );
		s_preload_ped_part( Script::GenerateCRC("ped_f_torso") );
		s_preload_ped_part( Script::GenerateCRC("ped_f_legs") );
	}
	
	// should really do an examination of what parts the thing actually uses
	// and then the levelassetlister should list those...

	// loop through the NodeArray, looking for single-skinned models
	Script::CArray *pNodeArray = Script::GetArray( CRCD(0xc472ecc5,"NodeArray") );
	Dbg_MsgAssert( pNodeArray, ( "No NodeArray found in ParseNodeArray" ) );

	for ( uint32 i = 0; i < pNodeArray->GetSize(); i++ )
	{
		// we need to give it a texDictOffset so that it
		// doesn't conflict with the secret ped skaters...
		// (make sure it's less than the cutscene tex dict range, 
		// though, or else it would conflict with the cutscene objects)
		int texDictOffset = Mdl::Skate::vMAX_SKATERS;

        Script::CStruct* pNode = pNodeArray->GetStructure( i );
		Dbg_MsgAssert( pNode, ( "NULL pNode" ) );

		uint32 ClassChecksum = 0;
		pNode->GetChecksum( Script::GenerateCRC("Class"), &ClassChecksum );

		if ( ClassChecksum == Script::GenerateCRC("pedestrian") )
		{
			const char* p_model_name;
			if ( pNode->GetText( "model", &p_model_name, false ) )
			{
				Str::String fullModelName;
				fullModelName = Gfx::GetModelFileName(p_model_name, ".skin");

				// preload the model!
				Nx::CModel* pDummy = Nx::CEngine::sInitModel();
//				Dbg_Message("Preloading single-skinned ped model %s", fullModelName.getString() );
				pDummy->AddGeom( fullModelName.getString(), 0, true, texDictOffset );
				
				Nx::CEngine::sUninitModel( pDummy );
			}

			// preload the shadow if necessary
			const char* p_shadow_model_name;
			if ( pNode->GetText( "shadowmodel", &p_shadow_model_name, false ) )
			{
				Str::String fullModelName;
				fullModelName = Gfx::GetModelFileName(p_shadow_model_name, ".mdl");

				// preload the model!
				Nx::CModel* pDummy = Nx::CEngine::sInitModel();
//				Dbg_Message("Preloading single-skinned ped model %s", fullModelName.getString() );
				pDummy->AddGeom( fullModelName.getString(), 0, true, texDictOffset );

				Nx::CEngine::sUninitModel( pDummy );
			}


			// now find the non-randoms
			uint32 profileName;
			if ( pNode->GetChecksum( CRCD(0x7ea855f0,"profile"), &profileName, false ) )
			{
				Script::CStruct* pStruct = Script::GetStructure( profileName );
				if ( pStruct )				   
				{
					Nx::CModel* pDummy = Nx::CEngine::sInitModel();
					Dbg_Assert( pDummy );
  //  				pDummy->LoadSkeleton( Script::GenerateCRC("human") );
					Gfx::CModelAppearance theTempAppearance;
					theTempAppearance.Load( pStruct, false );
					Gfx::CModelBuilder theBuilder( true, texDictOffset );
					theBuilder.BuildModel( &theTempAppearance, pDummy, NULL, Script::GenerateCRC("preload_model_from_appearance") );
					Nx::CEngine::sUninitModel( pDummy );
    			}
			}
		}
	}

//	Dbg_Message( "Preloading peds took %d ms", Tmr::ElapsedTime( baseTime ) );
			
	Mem::PopMemProfile(/*"PreLoadModels"*/);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PreselectRandomPedestrians | 
// Example: PreselectRandomPedestrians part=ped_f_legs list=sch_ped_f_legs num=4
bool ScriptPreselectRandomPedestrians( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 fullPartChecksum;
	pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &fullPartChecksum, Script::ASSERT );

	Script::CArray* pFullPartArray = NULL;
	pFullPartArray = Script::GetArray( fullPartChecksum, Script::ASSERT );

	uint32 levelName = Mdl::Skate::Instance()->m_requested_level;

	Dbg_Message( "Pre-selecting %s in %s", Script::FindChecksumName(fullPartChecksum), Script::FindChecksumName(levelName) );

	// go through the specified part array,
	// and remove any "allow_random" components...
	uint32 fullPartArraySize = pFullPartArray->GetSize();
	for ( uint32 i = 0; i < fullPartArraySize; i++ )
	{
		Script::CStruct* pStruct = pFullPartArray->GetStructure( i );
		
		// allowed_to_pick is a temp variable used only by this function...
		// clear it already exists...
		pStruct->RemoveComponent( CRCD(0x355f9467,"allowed_to_pick") );

		// they all default to not be able to be randomized
		pStruct->AddInteger( CRCD(0xf1e3cd22,"allow_random"), 0 );

		pStruct->AddInteger( CRCD(0x92bddfd9,"already_selected"), 0 );
		
		pStruct->AddInteger( CRCD(0x4b833e64,"random_index"), -1 );
	}
	
	int allowableCount = 0;
	for ( uint32 i = 0; i < fullPartArraySize; i++ )
	{
		Script::CStruct* pStruct = pFullPartArray->GetStructure( i );
		
		bool allowed_to_pick = true;

		// if it's level-specific...
		Script::CStruct* pLevelSpecificStruct;
		if ( pStruct->ContainsComponentNamed( CRCD(0xf6f8e158,"no_random") ) )
		{
			allowed_to_pick = false;
		
			if ( pStruct->GetStructure( CRCD(0x3598bf7d,"level_specific"), &pLevelSpecificStruct, false ) )
			{
				// sanity check:  level_specific and no_random are mutually exclusive
				Script::PrintContents( pStruct );
				Dbg_MsgAssert( 0, ( "no_random should not be used with level_specific" ) );
			}
		}
		else if ( pStruct->GetStructure( CRCD(0x3598bf7d,"level_specific"), &pLevelSpecificStruct, false ) )
		{
			Dbg_MsgAssert( !pStruct->ContainsComponentNamed(CRCD(0x94d12f97,"exclude_from_levels")), ( "level_specific and exclude_from_levels are mutually exclusive" ) )

			// check to see if we're in this level...
			allowed_to_pick = pLevelSpecificStruct->ContainsComponentNamed( levelName );
		}
		else if ( pStruct->GetStructure( CRCD(0x94d12f97,"exclude_from_levels"), &pLevelSpecificStruct, false ) )
		{
			Dbg_MsgAssert( !pStruct->ContainsComponentNamed(CRCD(0x3598bf7d,"level_specific")), ( "level_specific and exclude_from_levels are mutually exclusive" ) )

			// check to see if we're in this level...
			allowed_to_pick = !pLevelSpecificStruct->ContainsComponentNamed( levelName );
		}
		 
		if ( allowed_to_pick )
		{
			allowableCount++;
			pStruct->AddInteger( CRCD(0x355f9467,"allowed_to_pick"), 1 );
		}
		else
		{
			// clear the already picked flag
			pStruct->AddInteger( CRCD(0x355f9467,"allowed_to_pick"), 0 );
			
			if ( Script::GetInt( "cas_artist", false ) )
			{
//				Dbg_Message("Disallowing...");
//				Script::PrintContents(pStruct);
			}
		}
	}

	// Nolan's Fisherman Example:
	// If a non-randomized profile explicitly loads up a random ped part anyway,
	// then add that random ped asset to the pool of allowable randomized ped parts
	Script::CArray *pNodeArray=Script::GetArray( CRCD(0xc472ecc5,"NodeArray") );
	Dbg_MsgAssert( pNodeArray, ( "No NodeArray found in PreselectRandomPedestrians" ) );
	for ( uint32 i = 0; i < pNodeArray->GetSize(); i++ )
	{
		Script::CStruct* pNode = pNodeArray->GetStructure( i );
		Dbg_MsgAssert( pNode, ( "NULL pNode" ) );

		uint32 ClassChecksum = 0;
		pNode->GetChecksum( Script::GenerateCRC("Class"), &ClassChecksum );

		if ( ClassChecksum == Script::GenerateCRC("pedestrian") )
		{
			// now find the non-randoms
			uint32 profileName;
			if ( pNode->GetChecksum( CRCD(0x7ea855f0,"profile"), &profileName, false ) )
			{
				Script::CStruct* pProfileStruct = Script::GetStructure( profileName );
				if ( pProfileStruct )
				{
					Script::CStruct* pVirtualStruct;
					if ( pProfileStruct->GetStructure( fullPartChecksum, &pVirtualStruct, false ) )
					{
						uint32 descID;
						if ( pVirtualStruct->GetChecksum( CRCD(0x4bb2084e,"desc_id"), &descID, false ) )
						{
							Script::CStruct* pActualStruct = Cas::GetOptionStructure( fullPartChecksum, descID, false );
							if ( pActualStruct && !pActualStruct->ContainsComponentNamed( CRCD(0xf6f8e158,"no_random") ) )
							{
								if ( Script::GetInt( "cas_artist", false ) )
								{
//									Dbg_Message( "Allowing to pick %s", Script::FindChecksumName( descID ) );
								}
								pActualStruct->AddInteger( CRCD(0xf1e3cd22,"allow_random"), 1 );
								pActualStruct->AddInteger( CRCD(0x355f9467,"allowed_to_pick"), 1 );

								// these get lowest-priority when randomizing...
								pActualStruct->AddInteger( CRCD(0x92bddfd9,"already_selected"), 1 );
							}
						}
					}
				}
			}
		}
	}
	
	// sanity check that the number specified is 
	// greater than the actual number of random parts
	int numToSelect = allowableCount;
	pParams->GetInteger( CRCD(0x23bc5091,"num"), &numToSelect, Script::NO_ASSERT );
	if ( numToSelect > allowableCount )
	{
		Dbg_Message( "Not enough parts in list %s to select from (need %d, have %d)!", Script::FindChecksumName(fullPartChecksum), numToSelect, allowableCount );
		numToSelect = allowableCount;
	}

	//-------------------------------------------------------------
	// look for a item with a universal skin tone first...  if it
	// exists, automatically preselect it.  otherwise, make sure
	// that at least one of each skintone is chosen, or else the
	// skintones may not match up after part randomization.
	Script::CStruct* pSkinTonePicks[64];
	int skinToneCount = 0;

	Cas::BuildRandomSetList( fullPartChecksum, 0, pSkinTonePicks, &skinToneCount );
	if ( skinToneCount )
	{
		Script::CStruct* pStruct = pSkinTonePicks[Mth::Rnd( skinToneCount )];
		
		pStruct->AddInteger( CRCD(0xf1e3cd22,"allow_random"), 1 );
		pStruct->AddInteger( CRCD(0x355f9467,"allowed_to_pick"), 0 );
		pStruct->AddInteger( CRCD(0x4b833e64,"random_index"), numToSelect-1 );
		numToSelect--;
	}
	else
	{
		// this code will only kick in if all of the options have a skin-tone
		// (random_set) associated with it 
		Dbg_MsgAssert( numToSelect >= 3, ( "Num parts to select (%d) must be >= 3, so that ped skin tones will work (either need to increase '%s' in levels.q, or add at least one universal skintone option)", numToSelect, Script::FindChecksumName(fullPartChecksum) ) );
		
		Script::CStruct* pStruct;

		Cas::BuildRandomSetList( fullPartChecksum, CRCD(0x94e5a308,"light"), pSkinTonePicks, &skinToneCount );
		Dbg_MsgAssert( skinToneCount, ( "Couldn't find light-skinned version of this part %s", Script::FindChecksumName(fullPartChecksum) ) );
		pStruct = pSkinTonePicks[Mth::Rnd( skinToneCount )];
		pStruct->AddInteger( CRCD(0xf1e3cd22,"allow_random"), 1 );
		pStruct->AddInteger( CRCD(0x355f9467,"allowed_to_pick"), 0 );
		pStruct->AddInteger( CRCD(0x4b833e64,"random_index"), numToSelect-1 );
		numToSelect--;
		
		Cas::BuildRandomSetList( fullPartChecksum, CRCC(0x85aaf0d8,"tan"), pSkinTonePicks, &skinToneCount );
		Dbg_MsgAssert( skinToneCount, ( "Couldn't find tan-skinned version of this part %s", Script::FindChecksumName(fullPartChecksum) ) );
		pStruct = pSkinTonePicks[Mth::Rnd( skinToneCount )];
		pStruct->AddInteger( CRCD(0xf1e3cd22,"allow_random"), 1 );
		pStruct->AddInteger( CRCD(0x355f9467,"allowed_to_pick"), 0 );
		pStruct->AddInteger( CRCD(0x4b833e64,"random_index"), numToSelect-1 );
		numToSelect--;
		
		Cas::BuildRandomSetList( fullPartChecksum, CRCC(0xe4834204,"dark"), pSkinTonePicks, &skinToneCount );
		Dbg_MsgAssert( skinToneCount, ( "Couldn't find dark-skinned version of this part %s", Script::FindChecksumName(fullPartChecksum) ) );
		pStruct = pSkinTonePicks[Mth::Rnd( skinToneCount )];
		pStruct->AddInteger( CRCD(0xf1e3cd22,"allow_random"), 1 );
		pStruct->AddInteger( CRCD(0x355f9467,"allowed_to_pick"), 0 );
		pStruct->AddInteger( CRCD(0x4b833e64,"random_index"), numToSelect-1 );
		numToSelect--;
	}
	//-------------------------------------------------------------

	//-------------------------------------------------------------
	// now, add the "allow_random" flag to  of the items in the list...
	for ( int i = 0; i < numToSelect; i++ )
	{
		int allowed_to_pick = 0;
		while ( !allowed_to_pick )
		{
			int randomPick = Mth::Rnd( fullPartArraySize );
			Dbg_Assert( randomPick >= 0 && randomPick < (int)fullPartArraySize );

			Script::CStruct* pStruct = pFullPartArray->GetStructure( randomPick );
			pStruct->GetInteger( CRCD(0x355f9467,"allowed_to_pick"), &allowed_to_pick, Script::ASSERT );
			if ( allowed_to_pick )
			{
				pStruct->AddInteger( CRCD(0xf1e3cd22,"allow_random"), 1 );
				pStruct->AddInteger( CRCD(0x355f9467,"allowed_to_pick"), 0 );
				pStruct->AddInteger( CRCD(0x4b833e64,"random_index"), i );
			}
		}
	}
	//-------------------------------------------------------------

	//-------------------------------------------------------------
	// debugging code to show which items are allowed to be randomized
#ifdef __NOPT_ASSERT__
	int randomCount = 0;
	
	for ( uint32 i = 0; i < fullPartArraySize; i++ )
	{
		Script::CStruct* pStruct = pFullPartArray->GetStructure( i );
		
		int allow_random = 0;
		pStruct->GetInteger( CRCD(0xf1e3cd22,"allow_random"), &allow_random, Script::ASSERT );

		uint32 descID;
		pStruct->GetChecksum( CRCD(0x4bb2084e,"desc_id"), &descID, Script::ASSERT );

		if ( allow_random )
		{
			Dbg_Message( "Setting random part #%d: %s (%d)", randomCount++, Script::FindChecksumName(descID), numToSelect );
		}
		
		// get rid of temp variable...
		pStruct->RemoveComponent( CRCD(0x355f9467,"allowed_to_pick") );
	}
#endif // __NOPT_ASSERT__
	//-------------------------------------------------------------

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptReplaceCarTextures( Script::CStruct *pParams, Script::CScript *pScript )
{
	// this is a highly-specific function used for quickly changing
	// a car's headlight textures for time-of-day stuff
	// (the previous implementation was too slow, 
	// involving looping through each model component,
	// getting the model, and attempting to replaceTexture
	// on it...  this one targets car objects, and
	// skips over texture dictionaries that have already
	// been swapped)

//	Tmr::Time baseTime = Tmr::ElapsedTime(0);
	
	int num_to_rebuild = 0;

	const int vMAX_TEX_DICTS = 32;
	Nx::CTexDict* tex_dicts[vMAX_TEX_DICTS];
	int num_tex_dicts = 0;

	// new fast way, just go directly to the components, if any
	// (this is still kind of slow, so you wouldn't want to call it that often,
	// maybe only when any hiccups can be masked out with a screen fade or something)
	Obj::CModelComponent *p_component = (Obj::CModelComponent*)Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRC_MODEL );
	while( p_component )
	{
		// GJ TODO:  somehow loop through all the
		// skin assets in the asset manager
		if ( p_component->GetObject()->GetType() == SKATE_TYPE_CAR )
		{
			Nx::CModel* pModel = p_component->GetModel();

			bool must_rebuild = false;

			for ( int i = 0; i < pModel->GetNumGeoms(); i++ )
			{
				Nx::CTexDict* pTexDict = pModel->GetTexDictByIndex( i );

				bool found = false;

				// if any tex dict in the model's list
				// has not been processed yet, then
				// replace it
				for ( int j = 0; j < num_tex_dicts; j++ )
				{
					if ( pTexDict == tex_dicts[j] )
					{
						found = true;
					}
				}

				if ( !found )
				{
					// mark that texture dictionary as
					// processed, so that we don't have
					// to reprocess future objects
                    Dbg_MsgAssert( num_tex_dicts < vMAX_TEX_DICTS, ( "Too many texture dictionaries to search through" ) );
					tex_dicts[num_tex_dicts] = pTexDict;
					num_tex_dicts++;
					
					must_rebuild = true;
				}
			}

			if ( must_rebuild )
			{                
				num_to_rebuild++;
				p_component->CallMemberFunction( CRCD(0x83f9be15,"Obj_ReplaceTexture"), pParams, pScript );
			}
		}

		p_component = (Obj::CModelComponent*)p_component->GetNextSameType();		
	}

//	Dbg_Message( "ReplaceCarTextures on %d items took %d ms", num_to_rebuild, Tmr::ElapsedTime( baseTime ) );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetUIFromPreferences | 
// @parmopt name | field | | field name
// @parmopt string | field | | field string
// @parmopt name | select_if | | check if this name equals the value of
// checksum in the field
// @parmopt name | menu_id | | the menu id to act on if the select_if is true
// @parmopt name | id | | the control id
// @flag mask_password | if set, all letters are replaced with stars 
bool ScriptSetUIFromPreferences(Script::CStruct *pParams, Script::CScript *pScript)
{
	// gets either network or splitscreen prefs, as appropriate
	Prefs::Preferences* pPreferences = Mdl::GetPreferences( pParams );
	
	uint32 field_id;

	// look for either a field checksum or string
	if ( !pParams->GetChecksum("field", &field_id ) )
	{
		const char* pFieldName;
		pParams->GetText("field", &pFieldName, true);
		field_id = Script::GenerateCRC( pFieldName );
	}
	
	uint32 checksum_value1;
	if ( pParams->GetChecksum( "select_if", &checksum_value1 ) )
	{	
		Script::CStruct* pStructure = pPreferences->GetPreference( field_id );
		Dbg_Assert(pStructure);
		uint32 checksum_value2;
		pStructure->GetChecksum( "checksum", &checksum_value2, true );

		if ( checksum_value1 == checksum_value2 )
		{
			/*
			uint32 menu_id;
			pParams->GetChecksum( "menu_id", &menu_id, true );
			Front::MenuFactory* menu_factory = Front::MenuFactory::Instance();
			Front::MenuElement *pMenuElement=menu_factory->GetMenuElement(menu_id);
			Front::VerticalMenu *pMenu=static_cast(pMenuElement);
			Dbg_MsgAssert(pMenu,("\n%s\nNo menu with id '%s' found",pScript->GetScriptInfo(),Script::FindChecksumName(menu_id)));

			Script::PrintContents(pParams);

			uint32 control_id;
			pParams->GetChecksum( "id", &control_id, true );
//			pParams->GetChecksum( "control_id", &control_id, true );
			pMenu->AttemptSelect( control_id );
			*/
		}
	}
	else
	{
		uint32 control_name;
		pParams->GetChecksum( "id", &control_name, true );

		int mask_password = pParams->ContainsFlag( "mask_password" );
	
		// update all the elements
		pPreferences->UpdateUIElement( control_name, field_id, mask_password );
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetUIFromSkaterProfile | 
// @parm name | field_id | the field id
// @parmopt name | select_if | | 
// @parmopt name | menu_id | | the menu id
// @parmopt name | control_id | | 
// @parmopt name | slider_id | |
bool ScriptSetUIFromSkaterProfile(Script::CStruct *pParams, Script::CScript *pScript)
{   
	/*
	uint32 field_id;
	pParams->GetChecksum( "field_id", &field_id, true );


	Front::MenuFactory* menu_factory = Front::MenuFactory::Instance();
	Obj::CSkaterProfile* pSkaterProfile;
	pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile(pParams);

	uint32 slider_id;
	uint32 control_id;
	uint32 checksum_value;
	if ( pParams->GetChecksum( "select_if", &checksum_value ) )
	{
		if ( checksum_value == pSkaterProfile->GetChecksumValue(field_id) )
		{
			uint32 menu_id;
			pParams->GetChecksum( "menu_id", &menu_id, true );
			Front::MenuElement *pMenuElement=menu_factory->GetMenuElement(menu_id);
			Front::VerticalMenu *pMenu=static_cast(pMenuElement);
			Dbg_MsgAssert(pMenu,("\n%s\nNo menu with id '%s' found",pScript->GetScriptInfo(),Script::FindChecksumName(menu_id)));
			pParams->GetChecksum( "control_id", &control_id, true );
			pMenu->AttemptSelect( control_id );
		}
	}
	else if ( pParams->GetChecksum( "slider_id", &slider_id ) )
	{
		// slider element
		Front::SliderMenuElement* pSliderElement = static_cast( menu_factory->GetMenuElement( slider_id, true ) );
		pSliderElement->SetValue( pSkaterProfile->GetUIValue(field_id) );
	}
	else if ( pParams->GetChecksum( "control_id", &control_id ) )
	{		
		Front::MenuEvent event;
		event.SetTypeAndTarget(Front::MenuEvent::vSETCONTENTS, control_id );
		event.GetData()->AddComponent(Script::GenerateCRC("string"), ESYMBOLTYPE_STRING, pSkaterProfile->GetUIString(field_id).getString() );
		menu_factory->LaunchEvent(&event);
	}
	else
	{
		Dbg_Assert( 0 );
	}
	*/

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetPreferencesFromUI | changes field defined by string to value defined by field
// @parm string | string | 
// @parm string | field | 
// @parmopt name | level_checksum | | 
bool ScriptSetPreferencesFromUI(Script::CStruct *pParams, Script::CScript *pScript)
{
	// gets either network or splitscreen prefs, as appropriate
	Prefs::Preferences* pPreferences = Mdl::GetPreferences( pParams );

	const char* p_string;
	pParams->GetText( "string", &p_string, true );

	const char* pFieldName;
	pParams->GetText("field", &pFieldName, true);

	Dbg_Message( "Changing field %s to %s", pFieldName, p_string );
	
	// transfer data back into the preferences
	Script::CStruct* pTempStructure = new Script::CStruct;
	pTempStructure->AddComponent( Script::GenerateCRC("ui_string"), ESYMBOLTYPE_STRING, p_string );
	
	uint32 checksum;
	checksum = 0;
	pParams->GetChecksum( "checksum", &checksum );
	pTempStructure->AddComponent( Script::GenerateCRC("checksum"), ESYMBOLTYPE_NAME, (int) checksum );

	if( ( stricmp( pFieldName, "time_limit" ) == 0 ) ||
		( stricmp( pFieldName, "horse_time_limit" ) == 0 ))
	{
		int time;

		pParams->GetInteger( "time", &time );
		Dbg_Message( "Changing time to %d", time );
		pTempStructure->AddComponent( Script::GenerateCRC("time"), ESYMBOLTYPE_INTEGER, time );
	}

	if( stricmp( pFieldName, "target_score" ) == 0 )
	{
		int score;

		if( pParams->GetInteger( "score", &score ) == false )
		{
			pParams->GetInteger( "time", &score, Script::ASSERT );
			score = Tmr::Seconds( score );
		}
		
		Dbg_Message( "Changing score to %d", score );
		pTempStructure->AddComponent( Script::GenerateCRC("score"), ESYMBOLTYPE_INTEGER, score );
	}



	pPreferences->SetPreference( Script::GenerateCRC(pFieldName), pTempStructure );
	delete pTempStructure;
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetAllToDefaultStats | 
bool ScriptResetAllToDefaultStats( Script::CStruct *pParams, Script::CScript *pScript )
{
//	Tmr::Time baseTime = Tmr::ElapsedTime(0);

	Script::CArray* pMasterSkaterArray = Script::GetArray( "master_skater_list", Script::ASSERT );

	for ( uint32 i = 0; i < pMasterSkaterArray->GetSize(); i++ )
	{
		Script::CStruct* pTempStructure = new Script::CStruct;
		
		Script::CStruct* pOriginalInfo = pMasterSkaterArray->GetStructure( i );

		uint32 skaterName;
		pOriginalInfo->GetChecksum( CRCD(0xa1dc81f9,"name"), &skaterName, Script::ASSERT );

		Script::CArray* pStatNameArray = Script::GetArray( "stat_names" );
		for ( uint32 j = 0; j < pStatNameArray->GetSize(); j++ )
		{
			Script::CStruct* pStatInfo = pStatNameArray->GetStructure(j);
			
			uint32 statName;
			pStatInfo->GetChecksum( CRCD(0xa1dc81f9,"name"), &statName, Script::ASSERT );
			
			int statVal;
			pOriginalInfo->GetInteger( statName, &statVal, Script::ASSERT );
			pTempStructure->AddInteger( statName, statVal );
		}

		int pointsAvailable;
		pOriginalInfo->GetInteger( "points_available", &pointsAvailable, Script::ASSERT );
		pTempStructure->AddInteger( "points_available", pointsAvailable );

		// now add it to the corresponding skater profile
		Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
		uint32 numProfiles = pPlayerProfileManager->GetNumProfileTemplates();
		for ( uint32 j = 0; j < numProfiles; j++ )
		{
			Obj::CSkaterProfile* pProfile = pPlayerProfileManager->GetProfileTemplateByIndex(j);
			Dbg_Assert( pProfile );
			Script::CStruct* pInfo = pProfile->GetInfo();

			uint32 test;
			pInfo->GetChecksum( CRCD(0xa1dc81f9,"name"), &test, Script::ASSERT );
			if ( test == skaterName )
			{
				pInfo->AppendStructure( pTempStructure );
				break;
			}
		}
		
		delete pTempStructure;
	}

//	Dbg_Message( "ResetAllToDefaultStats took %d ms", Tmr::ElapsedTime( baseTime ) );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetToDefaultProfile | 
bool ScriptResetToDefaultProfile( Script::CStruct *pParams, Script::CScript *pScript )
{
//	Tmr::Time baseTime = Tmr::ElapsedTime(0);

	uint32 resetThisSkaterName;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &resetThisSkaterName, Script::ASSERT );

	Script::CArray* pMasterSkaterArray = Script::GetArray( "master_skater_list", Script::ASSERT );

	for ( uint32 i = 0; i < pMasterSkaterArray->GetSize(); i++ )
	{
		Script::CStruct* pOriginalInfo = pMasterSkaterArray->GetStructure( i );
		Dbg_Assert( pOriginalInfo );

		uint32 skaterName;
		pOriginalInfo->GetChecksum( CRCD(0xa1dc81f9,"name"), &skaterName, Script::ASSERT );

		if ( resetThisSkaterName == skaterName )
		{
			Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
			Obj::CSkaterProfile* pProfile = pPlayerProfileManager->GetProfileTemplate( skaterName );
			Dbg_Assert( pProfile );

			uint32 partial;
            if ( pParams->GetChecksum( CRCD(0x55f82f0b,"partial"), &partial, Script::NO_ASSERT ) )
            {
                // resets appearance, tricks, NOT STATS
                pProfile->PartialReset( pOriginalInfo );
            }
            else
            {
                // resets appearance, stats, tricks
    			pProfile->Reset( pOriginalInfo );
            }
		}
	}

//	Dbg_Message( "ResetAllToDefaultProfile took %d ms", Tmr::ElapsedTime( baseTime ) );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetAllToDefaultProfile | 
bool ScriptResetAllToDefaultProfile( Script::CStruct *pParams, Script::CScript *pScript )
{
//	Tmr::Time baseTime = Tmr::ElapsedTime(0);

	Script::CArray* pMasterSkaterArray = Script::GetArray( "master_skater_list", Script::ASSERT );

	for ( uint32 i = 0; i < pMasterSkaterArray->GetSize(); i++ )
	{
		Script::CStruct* pOriginalInfo = pMasterSkaterArray->GetStructure( i );
		Dbg_Assert( pOriginalInfo );

		uint32 skaterName;
		pOriginalInfo->GetChecksum( CRCD(0xa1dc81f9,"name"), &skaterName, Script::ASSERT );

		Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
		Obj::CSkaterProfile* pProfile = pPlayerProfileManager->GetProfileTemplate( skaterName );
		Dbg_Assert( pProfile );
			
		// resets appearance, stats, tricks
		pProfile->Reset( pOriginalInfo );
	}

//	Dbg_Message( "ResetAllToDefaultProfile took %d ms", Tmr::ElapsedTime( baseTime ) );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ForEachSkaterName | 
bool ScriptForEachSkaterName( Script::CStruct *pParams, Script::CScript *pScript )
{
//	Tmr::Time baseTime = Tmr::ElapsedTime(0);

	uint32 scriptToRun;
	pParams->GetChecksum( CRCD(0x62ba3f6a,"do"), &scriptToRun, Script::ASSERT );

	Script::CStruct* pSubParams = NULL;
	pParams->GetStructure( CRCD(0x7031f10c,"params"), &pSubParams, Script::NO_ASSERT );

	Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
	int numProfiles = pPlayerProfileManager->GetNumProfileTemplates();
	for ( int i = 0; i < numProfiles; i++ )
	{
		Obj::CSkaterProfile* pProfile = pPlayerProfileManager->GetProfileTemplateByIndex(i);
		Dbg_Assert( pProfile );
		Script::CStruct* pInfo = pProfile->GetInfo();

		uint32 skaterName;
		pInfo->GetChecksum( CRCD(0xa1dc81f9,"name"), &skaterName, Script::ASSERT );

		Script::CStruct* pTempStructure = new Script::CStruct;
		pTempStructure->AppendStructure( pSubParams );
		pTempStructure->AddChecksum( CRCD(0xa1dc81f9,"name"), skaterName );
		Script::RunScript( scriptToRun, pTempStructure );
		delete pTempStructure;
	}

//	Dbg_Message( "ScriptForEachSkaterName took %d ms", Tmr::ElapsedTime( baseTime ) );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ForEachSkaterProfile | 
bool ScriptForEachSkaterProfile( Script::CStruct *pParams, Script::CScript *pScript )
{
//	Tmr::Time baseTime = Tmr::ElapsedTime(0);

	uint32 scriptToRun;
	pParams->GetChecksum( CRCD(0x62ba3f6a,"Do"), &scriptToRun, Script::ASSERT );

	Script::CStruct* pSubParams = NULL;
	pParams->GetStructure( CRCD(0x7031f10c,"params"), &pSubParams, Script::NO_ASSERT );

	Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
	int numProfiles = pPlayerProfileManager->GetNumProfileTemplates();
	for ( int i = 0; i < numProfiles; i++ )
	{
		Obj::CSkaterProfile* pProfile = pPlayerProfileManager->GetProfileTemplateByIndex(i);
		Dbg_Assert( pProfile );
		Script::CStruct* pInfo = pProfile->GetInfo();

		Script::CStruct* pTempStructure = new Script::CStruct;
		pTempStructure->AppendStructure( pSubParams );
		pTempStructure->AppendStructure( pInfo );
		Script::RunScript( scriptToRun, pTempStructure );
		delete pTempStructure;
	}

//	Dbg_Message( "ScriptForEachSkaterProfile took %d ms", Tmr::ElapsedTime( baseTime ) );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetSkaterProfileInfoByName | appends the skater profile info to the
// calling script's params. returns false if the specified profile can't be found
// @parm name | name | the name to search for
bool ScriptGetSkaterProfileInfoByName( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 name;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &name, Script::ASSERT );
	
	Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
	int numProfiles = pPlayerProfileManager->GetNumProfileTemplates();
	for ( int i = 0; i < numProfiles; i++ )
	{
		Obj::CSkaterProfile* pProfile = pPlayerProfileManager->GetProfileTemplateByIndex(i);
		Dbg_Assert( pProfile );
		Script::CStruct* pInfo = pProfile->GetInfo();

		uint32 test;
		pInfo->GetChecksum( CRCD(0xa1dc81f9,"name"), &test, Script::ASSERT );
		if ( test == name )
		{
			pScript->GetParams()->AppendStructure( pInfo );
			return true;
		}
	}
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSkaterProfileInfoByName | appends the skater profile info to the
// calling script's params. returns false if the specified profile can't be found
// @parm name | name | the name to search for
// @parm structure | params | the params structure to append to the skater profile
bool ScriptSetSkaterProfileInfoByName( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 name;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &name, Script::ASSERT );

	Script::CStruct* pSubParams = NULL;
	pParams->GetStructure( "params", &pSubParams, Script::ASSERT );

	Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
	int numProfiles = pPlayerProfileManager->GetNumProfileTemplates();
	for ( int i = 0; i < numProfiles; i++ )
	{
		Obj::CSkaterProfile* pProfile = pPlayerProfileManager->GetProfileTemplateByIndex(i);
		Dbg_Assert( pProfile );
		Script::CStruct* pInfo = pProfile->GetInfo();

		uint32 test;
		pInfo->GetChecksum( CRCD(0xa1dc81f9,"name"), &test, Script::ASSERT );
		if ( test == name )
		{
			pInfo->AppendStructure( pSubParams );
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetDefaultAppearance | 
// @parm int | skater | the skater to reset
bool ScriptResetDefaultAppearance( Script::CStruct *pParams, Script::CScript *pScript )
{   

	Obj::CSkaterProfile* pSkaterProfile;
	pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile(pParams);
	pSkaterProfile->ResetDefaultAppearance();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetDefaultTricks | 
// @parm int | skater | the skater to reset
bool ScriptResetDefaultTricks( Script::CStruct *pParams, Script::CScript *pScript )
{	

	Obj::CSkaterProfile* pSkaterProfile;
	pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile(pParams);
	pSkaterProfile->ResetDefaultTricks();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetDefaultStats | 
// @parm int | skater | the skater to reset
bool ScriptResetDefaultStats( Script::CStruct *pParams, Script::CScript *pScript )
{   

	Obj::CSkaterProfile* pSkaterProfile;
	pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile(pParams);
	pSkaterProfile->ResetDefaultStats();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RandomizeAppearance | 
// @parm int | skater | the skater to randomize
bool ScriptRandomizeAppearance( Script::CStruct *pParams, Script::CScript *pScript )
{  
	Dbg_Message( "STUB:  RandomizeAppearance" );

	/*

	Obj::CSkaterProfile* pSkaterProfile;
	pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile(pParams);
	Gfx::CModelAppearance* pSkaterAppearance = pSkaterProfile->GetAppearance();
	Dbg_Assert( pSkaterAppearance );
	pSkaterAppearance->Randomize();
	 */

	// Randomize: Randomizes parts, height, weight
	// Possibly to be moved into script...
	// Needs to take disqualifications into account

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PrintCurrentAppearance | 
// @parm int | skater | the skater num
bool ScriptPrintCurrentAppearance(Script::CStruct *pParams, Script::CScript *pScript)
{   

	
	Obj::CSkaterProfile* pSkaterProfile;
	pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile(pParams);
	Gfx::CModelAppearance* pAppearance;
	pAppearance = pSkaterProfile->GetAppearance();
	pAppearance->PrintContents(pAppearance->GetStructure());
	
	/*const Gfx::CFaceTexture* p_face = pAppearance->GetFaceTexture();
	if( p_face && p_face->IsValid())
		pAppearance->GetFaceTexture()->PrintContents();*/
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetNeversoftSkater | 
// @parm int | skater | the skater num
// @parm structure | appearance | current appearance structure 
// @parm structure | info | current info structure
bool ScriptSetNeversoftSkater(Script::CStruct *pParams, Script::CScript *pScript)
{


	// debug info:
//	printf( "Set Neversoft Skater" );
//	pParams->PrintContents();

	// make sure it's a custom skater
	Obj::CSkaterProfile* pSkaterProfile;
	pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile(pParams);
	Dbg_MsgAssert( !pSkaterProfile->IsPro(), ( "This function only works on a custom skater." ) );

	// get the current appearance structure
	Script::CStruct* pAppearanceStructure;
	pParams->GetStructure( "appearance", &pAppearanceStructure, true );

	// apply the desired appearance structure
	Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
	pAppearance->Load( pAppearanceStructure );

	// get the current info structure
	Script::CStruct* pInfoStructure;
	pParams->GetStructure( "info", &pInfoStructure, true );

	// append the new info structure
	Script::CStruct* pInfo = pSkaterProfile->GetInfo();
	pInfo->AppendStructure( pInfoStructure );

#ifdef __NOPT_ASSERT__
	Script::PrintContents(pInfo);
#endif
	// TODO:  since the career is being reset,
	// we should clear the info too (like stats and trick configs)
	
	pSkaterProfile->SetHeadIsLocked( true );

	// reset the career as well
//	Obj::CSkaterCareer* pCareer = pSkaterProfile->GetCareer();
//	pCareer->Reset();
//
//	// reset the career flags
//	for ( int i = 0; i < Script::GetInt( "FIRST_SHARED_GLOBAL_FLAG" ); i++ )
//	{
//		pCareer->UnSetGlobalFlag( i );
//	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CurrentProfileIsLocked | returns true if this skater profile is locked
// @parm int | skater | the skater num
bool ScriptCurrentProfileIsLocked(Script::CStruct *pParams, Script::CScript *pScript)
{

	Obj::CSkaterProfile* pSkaterProfile;
	pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile(pParams);
	return pSkaterProfile->IsLocked();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetSkaters | skip skaters to their restart points
// can specify either a node number or a node name
// the node number is ued by the "goto restart" menu
// @parmopt int | node_number | -1 | number of a node to skip the skater to 
// @parmopt name | node_name |  | name of a node to skip the skater to 
bool ScriptResetSkaters(Script::CStruct *pParams, Script::CScript *pScript)
{
	int node = -1;
	if (!pParams->GetInteger("node_number",&node))
	{
		uint32	node_name;
		if (pParams->GetChecksum("node_name",&node_name))
		{
			node = SkateScript::FindNamedNode(node_name);
		}	
	}
						 
	Mdl::Skate::Instance()->ResetSkaters(node, pParams->ContainsFlag(CRCD(0xd6f06bf6, "RestartWalking")));
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSkaterProfileInfo | Appends the structure to the specified skater
// profile info.  (Should only append the item if the item is of the same
// type)
// @parm int | player | the player slot to set
// @parm structure | params | the stuff to set
bool ScriptSetSkaterProfileInfo(Script::CStruct *pParams, Script::CScript *pScript)
{
	// now append appropriate data
	int 	playerNum;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &playerNum, Script::ASSERT );

	Script::CStruct* pAppendStruct;
	pParams->GetStructure( "params", &pAppendStruct, Script::ASSERT );

	Obj::CPlayerProfileManager* pProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	Obj::CSkaterProfile* pProfile = pProfileManager->GetProfile( playerNum );
	Script::CStruct* pStruct = pProfile->GetInfo();

	pStruct->AppendStructure( pAppendStruct );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetSkaterProfileInfo | Returns the skater profile info in the script params
// @parm int | player | the player slot to get
// @parm float | value | value 
bool ScriptGetSkaterProfileInfo(Script::CStruct *pParams, Script::CScript *pScript)
{
	int 	playerNum;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &playerNum, Script::ASSERT );	
	
	Obj::CPlayerProfileManager* pProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	Obj::CSkaterProfile* pProfile = pProfileManager->GetProfile( playerNum );
	
	Dbg_MsgAssert( pScript, ( "NULL pScript" ) );
	Dbg_MsgAssert( pScript->GetParams(), ( "NULL pScript params" ) );

	pScript->GetParams()->AppendStructure( pProfile->GetInfo() );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSkaterProfileProperty | Debugging function to set individual stats
// @parm int | stat | the stat to set
// @parm float | value | value 
bool ScriptSetSkaterProfileProperty(Script::CStruct *pParams, Script::CScript *pScript)
{   
	int 	playerNum;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &playerNum, Script::ASSERT );

	uint32 	stat;
	int		value;
	pParams->GetChecksum( NONAME, &stat );
	pParams->GetInteger( NONAME, &value );

	
	Obj::CPlayerProfileManager* pProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	Obj::CSkaterProfile* pProfile = pProfileManager->GetProfile( playerNum );

	return pProfile->SetPropertyValue( stat, value );

/*	// If no skater was specified, change the stats of all local skaters
	if( pScript->mpObject == NULL )
	{
		for( i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
		{
			pSkater = Mdl::Skate::Instance()->GetSkater(i);
			if (pSkater && pSkater->IsLocalClient())			// Skater might not exist
			{
				pSkater->SetStat((Obj::CSkater::EStat)stat,value);
			}
		}
	}
	else
	{
		pSkater = static_cast ( pScript->mpObject );
		Dbg_Assert( pSkater );

		pSkater->SetStat((Obj::CSkater::EStat)stat,value);
	}
	
	return true;
*/
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleAlwaysSpecial | ToggleAlwaysSpecial (on/off) fixes the 
// special meter to be always full. 
// ToggleAlwaysSpecial         ; Toggle it (useful for puttin on a button) 
// ToggleAlwaysSpecial on      ; Sets it on 
// ToggleAlwaysSpecial off     ; Sets it off 
// Note that when you set it on, the special meter is immediately full.
// However, when you turn it off, then the special meter will just decay 
// normally, so it might not be immediately obvious that you switched it off. 
bool ScriptToggleAlwaysSpecial(Script::CStruct *pParams, Script::CScript *pScript)
{
	bool on = pParams->ContainsFlag("on");
	bool off = pParams->ContainsFlag("off");
	

	for (int i=0;i<8;i++)
	{
		Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetSkater(i);						   
		if (pSkater)			// Skater might not exist
		{
			if (on)
			{
				pSkater->SetAlwaysSpecial(true);
			}
			else if (off)
			{
				pSkater->SetAlwaysSpecial(false);
			}
			else 
			{
				pSkater->ToggleAlwaysSpecial();
			}
		}	
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | SkaterSpeedGreaterThan | Check the skater speed, as if to have a ped
// not fall when the skater is stationary
// @parm float | speed | Speed to check against
bool ScriptSkaterSpeedGreaterThan( Script::CStruct *pParams, Script::CScript *pScript )
{
	float speed;
	if ( pParams->GetFloat( NONAME, &speed ) )
	{
		
		Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetLocalSkater();						   
		if ( pSkater )
		{
			float skaterVel = pSkater->GetVel( ).Length( );
			if ( skaterVel > speed)
			{
				return true;
			}
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | SkaterSpeedLessThan | Check the skater speed, as if to have a ped
// not fall when the skater is stationary
// @parm float | speed | Speed to check against
bool ScriptSkaterSpeedLessThan( Script::CStruct *pParams, Script::CScript *pScript )
{   
	float speed;
	if ( pParams->GetFloat( NONAME, &speed ) )
	{
		
		Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetLocalSkater();						   
		if ( pSkater )
		{
			float skaterVel = pSkater->GetVel( ).Length( );
			if ( skaterVel < speed )
			{
				return true;
			}
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | SkaterLastScoreLandedGreaterThan | Detects last score to trigger if script.
// Example: if SkaterLastScoreLandedGreaterThan 2000 --do cool stuff-- endif
// @uparm 1 | Score (int)
bool ScriptLastScoreLandedGreaterThan( Script::CStruct *pParams, Script::CScript *pScript )
{   
	int score;
	score = s_get_score_from_params( pParams, pScript );
	Mdl::Score *pScore;
	pScore = s_get_score_struct( );
	if ( pScore )
	{
		int scorePot = pScore->GetLastScoreLanded();
		if ( scorePot > score )
			return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | SkaterLastScoreLandedLessThan | Detects last score to trigger if script.
// Example: if SkaterLastScoreLandedLessThan 2000 --do cool stuff-- endif
// @uparm 1 | Score (int)
bool ScriptLastScoreLandedLessThan( Script::CStruct *pParams, Script::CScript *pScript )
{   
	int score;
	score = s_get_score_from_params( pParams, pScript );
	
	Mdl::Score *pScore;
	
	pScore = s_get_score_struct( );
	if ( pScore )
	{
		int scorePot = pScore->GetLastScoreLanded();
		if ( scorePot < score )
			return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | AnySkaterTotalScoreGreaterThan | Checks if any skaters' score is greater than the score paramter
// Example: if AnySkaterTotalScoreGreaterThan 2000 --do cool stuff-- endif
// @uparm 1 | Score (int)
bool ScriptAnyTotalScoreAtLeast( Script::CStruct *pParams, Script::CScript *pScript )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	
	GameNet::PlayerInfo* player;
	Lst::Search< GameNet::PlayerInfo > sh;
	int score;
	score = s_get_score_from_params( pParams, pScript );

	if( Mdl::Skate::Instance()->GetGameMode()->IsTeamGame())
	{
		int i;
		int total_score[GameNet::vMAX_TEAMS] = {0};

		for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
		{
			Mdl::Score* pScore = player->m_Skater->GetScoreObject();

			total_score[player->m_Team] += pScore->GetTotalScore();
		}

		for( i = 0; i < GameNet::vMAX_TEAMS; i++ )
		{
			if( total_score[i] >= score )
			{
				return true;
			}
		}
	}
	else
	{
		for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
		{
			if( player->m_Skater )
			{
				Mdl::Score *pScore;
	
				pScore = player->m_Skater->GetScoreObject();
				int totalScore = pScore->GetTotalScore();
				if( totalScore >= score )
				{
					return true;
				}
			}
		}
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptOnlyOneSkaterLeft( Script::CStruct *pParams, Script::CScript *pScript )
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	
	GameNet::PlayerInfo* player;
	Lst::Search< GameNet::PlayerInfo > sh;
	Lst::Search< GameNet::NewPlayerInfo > new_sh;
	int num_participants_left;
	
	num_participants_left = 0;
	if( Mdl::Skate::Instance()->GetGameMode()->IsTeamGame())
	{
		int i;
		int total_score[GameNet::vMAX_TEAMS] = {0};

		for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
		{
			Mdl::Score* pScore = player->m_Skater->GetScoreObject();

			total_score[player->m_Team] += pScore->GetTotalScore();
		}

		for( i = 0; i < GameNet::vMAX_TEAMS; i++ )
		{
			if( total_score[i] > 0 )
			{
				num_participants_left++;
			}
		}

		// If we're still waiting to load players fully, this test is invalid
		if( gamenet_man->FirstNewPlayerInfo( new_sh ))
		{
			return false;
		}
	}
	else
	{
		int num_players;

		num_players = 0;
		for( player = gamenet_man->FirstPlayerInfo( sh ); player; player = gamenet_man->NextPlayerInfo( sh ))
		{
			if( player->m_Skater )
			{
				Mdl::Score *pScore;
	
				num_players++;
				pScore = player->m_Skater->GetScoreObject();
				int totalScore = pScore->GetTotalScore();
				if( totalScore > 0 )
				{
					num_participants_left++;
				}
			}
		}

		// If we're still waiting to load players fully, this test is invalid
		if( gamenet_man->FirstNewPlayerInfo( new_sh ))
		{
			return false;
		}

		if( num_players == 1 )
		{
			return false;
		}
	}
	
	if( gamenet_man->OnServer())
	{
		if(	( gamenet_man->GetHostMode() == GameNet::vHOST_MODE_AUTO_SERVE ) ||
			( gamenet_man->GetHostMode() == GameNet::vHOST_MODE_FCFS ))
		{
			if( num_participants_left == 0 )
			{
				return true;
			}
		}
	}
	
	return num_participants_left == 1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | SkaterTotalScoreGreaterThan | Detects last score to trigger if script.
// Example: if SkaterTotalScoreGreaterThan 2000 --do cool stuff-- endif
// @uparm 1 | Score (int)
bool ScriptTotalScoreGreaterThan( Script::CStruct *pParams, Script::CScript *pScript )
{
	
	int score;
	score = s_get_score_from_params( pParams, pScript );
	Mdl::Score *pScore;
	pScore = s_get_score_struct( );
	if ( pScore )
	{
		int totalScore = pScore->GetTotalScore();
		if ( totalScore > score )
			return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | SkaterTotalScoreLessThan | Detects last score to trigger if script.
// Example: if SkaterTotalScoreLessThan 2000 --do cool stuff-- endif
// @uparm 1 | Score (int)
bool ScriptTotalScoreLessThan( Script::CStruct *pParams, Script::CScript *pScript )
{   
	int score;
	score = s_get_score_from_params( pParams, pScript );
	Mdl::Score *pScore;
	pScore = s_get_score_struct( );
	if ( pScore )
	{
		int totalScore = pScore->GetTotalScore();
		if ( totalScore < score )
			return ( true );
	}
	return ( false );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | SkaterCurrentScorePotGreaterThan | Detects last score to trigger if script.
// Example: if SkaterCurrentScorePotGreaterThan 2000 --do cool stuff-- endif
// @uparm 1 | Score (int)
bool ScriptCurrentScorePotGreaterThan( Script::CStruct *pParams, Script::CScript *pScript )
{   
	int score;
	int scale;
	
	scale = 1;
	score = s_get_score_from_params( pParams, pScript );
	pParams->GetInteger( CRCD(0xb08c5ae8,"point_scale"), &scale );
	score *= scale;

	Mdl::Score *pScore;
	pScore = s_get_score_struct( );
	if ( pScore )
	{
		int curScore = pScore->GetScorePotValue();
		if ( curScore > score )
			return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script bool | SkaterCurrentScorePotLessThan | Detects last score to trigger if script.
// Example: if SkaterCurrentScorePotLessThan 2000 --do cool stuff-- endif
// @uparm 1 | Score (int)
bool ScriptCurrentScorePotLessThan( Script::CStruct *pParams, Script::CScript *pScript )
{   
	int score;
	score = s_get_score_from_params( pParams, pScript );
	Mdl::Score *pScore;
	pScore = s_get_score_struct( );
	if ( pScore )
	{
		int curScore = pScore->GetScorePotValue();
		if ( curScore < score )
			return true;
	}
	return false;
}


// @script | SkaterGetScoreInfo | Adds the parameters ScorePot, TotalScore and LastScoreLanded to the script's parameters.
bool ScriptSkaterGetScoreInfo( Script::CStruct *pParams, Script::CScript *pScript )
{   
	Mdl::Score *p_score=s_get_score_struct( );
	
	pScript->GetParams()->AddInteger(CRCD(0x95e84391,"ScorePot"),p_score->GetScorePotValue());
	pScript->GetParams()->AddInteger(CRCD(0xee5b2b48,"TotalScore"),p_score->GetTotalScore());
	pScript->GetParams()->AddInteger(CRCD(0xea60ac69,"LastScoreLanded"),p_score->GetLastScoreLanded());
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalGreaterThan | Counts the number of goals you have got.
// @uparm 1.0 | value to check
bool ScriptGoalsGreaterThan( Script::CStruct *pParams, Script::CScript *pScript )
{														     
	float num;
	if ( pParams->GetFloat( NONAME, &num ) )
	{
	
		Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();	
		if (pCareer->CountTotalGoalsCompleted() > num)
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GoalsEqualTo | Counts the number of goals you have got. 
// @uparm 1.0 | number of goals
bool ScriptGoalsEqualTo( Script::CStruct *pParams, Script::CScript *pScript )
{  
	float num;
	if ( pParams->GetFloat( NONAME, &num ) )
	{
	
		Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();	
		if (pCareer->CountTotalGoalsCompleted() == num)
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MedalsGreaterThan |  Counts the number of medals you have got.  This 
// is just the number of levels you have gotten a medal in, so it will be 0 - 3.
// @uparm 1.0 | number of medals
bool ScriptMedalsGreaterThan( Script::CStruct *pParams, Script::CScript *pScript )
{														     
	float num;
	if ( pParams->GetFloat( NONAME, &num ) )
	{
	
		Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();	
		if (pCareer->CountMedals() > num)
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | MedalsEqualTo |  Counts the number of medals you have got.  This 
// is just the number of levels you have gotten a medal in, so it will be 0 - 3.
// @uparm 1.0 | number of medals
bool ScriptMedalsEqualTo( Script::CStruct *pParams, Script::CScript *pScript )
{														  
	float num;
	if ( pParams->GetFloat( NONAME, &num ) )
	{
	
		Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();	
		if (pCareer->CountMedals() == num)
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ToggleSkaterCamMode | Toggles the camera mode for a given skater
// @parm int | skater | 
bool ScriptToggleSkaterCamMode(Script::CStruct *pParams, Script::CScript *pScript)
{
	int skater;
	pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skater );

	
	if (!Mdl::Skate::Instance()->GetGameMode()->IsFrontEnd())
	{
		GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
		
		Obj::CSkater* p_skater = NULL;
		
		if (skater == 0)
		{
			GameNet::PlayerInfo* player;
			player = gamenet_man->GetLocalPlayer();
			if( player )
			{
				p_skater = player->m_Skater;
			}
		}
		else
		{											 
			p_skater = Mdl::Skate::Instance()->GetSkater( skater );
		}
		
		if( p_skater )
		{
			/*
			if ( p_skater->GetSkaterCam()->mMode < Obj::CSkaterCam::SKATERCAM_NUM_NORMAL_MODES )
			{
				p_skater->ToggleCamMode();
			}
			*/
			GetSkaterCameraComponentFromObject(p_skater->GetCamera())->ToggleMode();
			
		}
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetSkaterID | returns the id of the skater, so that you can run member functions on it from a global script
// @parm int | skater | Which skater's ID you want to grab
// The skater's id is returned in objID, check the script for examples

bool ScriptGetSkaterID( Script::CStruct* pParams, Script::CScript* pScript )
{
	// in this context, skater really means "viewport"
	int skaterId;
	Obj::CSkater* pSkater = NULL;
	if ( pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skaterId ) )
		 pSkater = Mdl::Skate::Instance()->GetSkater( skaterId );
	else	
		pSkater = Mdl::Skate::Instance()->GetLocalSkater();
	
	if ( pSkater && pSkater->IsLocalClient() )
	{
		uint32 id = pSkater->GetID();
		Dbg_Assert( pScript && pScript->GetParams() );
		pScript->GetParams()->AddChecksum( "objID", id );
		// Dbg_Printf( "************** SKATER ID %d\n", id );
		return true;
	}
	Dbg_MsgAssert( 0, ( "Couldn't find specified skater" ) );
	return false;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

bool ScriptGetCurrentSkaterID( Script::CStruct* pParams, Script::CScript* pScript )
{
	Dbg_Assert( pScript->mpObject );

	Obj::CSkater* pSkater = static_cast ( pScript->mpObject.Convert() );
	if( pSkater && pSkater->IsLocalClient() )
	{
		uint32 id = pSkater->GetID();
		Dbg_Assert( pScript && pScript->GetParams() );
		pScript->GetParams()->AddChecksum( "objID", id );
		//Dbg_Printf( "************** SKATER ID %d\n", id );
		return true;
	}
	Dbg_MsgAssert( 0, ( "Couldn't find specified skater" ) );
	return false;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

// @script | SetSkaterCamLerpReductionTimer | 
// @parmopt float | time | 0.0 | time value
bool ScriptSetSkaterCamLerpReductionTimer( Script::CStruct *pParams, Script::CScript *pScript )
{  
/*	
	Obj::CSkater* p_skater = static_cast ( pScript->mpObject.Convert() );
	Dbg_Assert( p_skater );

	Obj::CSkaterCam* p_cam = p_skater->GetSkaterCam();
	if( p_cam == NULL )
	{
		return true;
	}

	float time = 0.0f;
	pParams->GetFloat( 0x906b67ba, &time, true );  // time

	p_cam->SetLerpReductionTimer( time );
*/
//	printf ("skfuncs %d: SUTUBBBEDDDDDDDDDDDDDDDDDDDDDDDD\n",__LINE__);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PlaySkaterCamAnim | Play a camera animation for a given skater 
// Example usage:
// script play_photoguy_cam
//		TRG_G_TS_PhotoGuy:Obj_GetID
//		PlaySkaterCamAnim skater=0 name=G_TS_CameraStart targetID=objID targetOffset=(0,50,0) positionOffset=(-100, 50, -100)
//		; camera looks at a point 50 units above the photoguy, camera is fixed to a position 50 units above, 100 units behind, and 100 units to the left
// endscript
// @parmopt int | skater | 0 |
// @parmopt name | skaterId | | find the skater by id, rather than index
// @flag stop | 
// @parm name | name | the name of the animation
// @flag loop | loop the animation
// @flag focus_skater | 
// @flag play_hold | 
// @parmopt int | skippable | | whether the movie is skippable
// @parmopt name | exitScript | | name of script to run when the movie finishes/is skipped through
// @parmopt structure | exitParams | | additional parameters to be passed to the exitScript
// @parmopt name | targetID | | m_id of the object to follow (not the node's name!  see example below) 
// @parmopt vector | targetOffset | | offset from the specified targetID to look at (in local space, not world space)
// @parmopt vector | positionOffset | | overrides the camera position with the specified offset from the specified targetID
bool ScriptPlaySkaterCamAnim( Script::CStruct *pParams, Script::CScript *pScript )
{
	if ( pParams->ContainsFlag( CRCD(0x0c567fa2,"use_last_camera") ) )
	{
		// used so that we don't go back to the skatercam
		// when the "want to save" dialog comes up
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		skate_mod->GetMovieManager()->ApplyLastCamera();
		return true;
	}

 	// in this context, skater really means "viewport"
	int skaterIndex = 0;
	uint32 skaterId;
	Obj::CSkater* pSkater = NULL;
	if ( pParams->GetChecksum( "skaterId", &skaterId, Script::NO_ASSERT ) )
	{
		Obj::CObject* pObj = Obj::ResolveToObject( skaterId);
		Dbg_MsgAssert( pObj && pObj->GetType() == SKATE_TYPE_SKATER, ( "%x is not a skater.", skaterId ) );
		pSkater = (Obj::CSkater*)pObj;
	}
	else
	{
		pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skaterIndex );
		pSkater = Mdl::Skate::Instance()->GetSkater( skaterIndex );
	}
	
	if ( pSkater )
	{
		if ( pSkater->IsLocalClient() )
		{
			// The stop flag is read with highest priority.
			if ( pParams->ContainsFlag( "stop" ) )
			{
				Mdl::Skate * skate_mod = Mdl::Skate::Instance();
				skate_mod->GetMovieManager()->ClearMovieQueue();
				return true;
			}
			else
			{
				uint32 movieName = 0;
				pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );

				// GJ:  The camera may not be framed properly if the
				// skater was scaled up, so the following script will
				// shut off the scaling on the skater...  restore_skater_camera
				// will re-enable it when the movie is done
				Script::RunScript( CRCD(0xdf00bdff,"disable_skater_scaling") );

				// start the movie with the default params...
				Mdl::Skate * skate_mod = Mdl::Skate::Instance();
				skate_mod->GetMovieManager()->AddMovieToQueue( movieName, pParams, CRCD(0x66b7dd11,"skatercamanim") );
			}
		}
		else
		{
			if ( !pSkater->IsLocalClient() )
			{
				printf("this isn't a local skater!\n");
			}
		}
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSkaterCamAnimSkippable | set the current anim for a given
// skater as skippable
// @parmopt int | skater | 0 | the skater
// @parmopt name | skaterId | | the id of the skater
// @parmopt int | name | | the name of the movie to set skippable (otherwise, it will use the currently-playing movie)
// @parmopt int | skippable | 0 | set to anything greater than 0 to set anim to skippable
bool ScriptSetSkaterCamAnimSkippable( Script::CStruct *pParams, Script::CScript *pScript )
{
	// in this context, skater really means "viewport"
	uint32 skaterId;
	int skaterIndex = 0;

	Obj::CSkater* pSkater = NULL;
	if ( pParams->GetChecksum( "skaterId", &skaterId, Script::NO_ASSERT ) )
	{
		Obj::CObject* pObj = Obj::ResolveToObject( skaterId);
		Dbg_MsgAssert( pObj && pObj->GetType() == SKATE_TYPE_SKATER, ( "%x is not a CSkater", skaterId ) );
		pSkater = (Obj::CSkater*)pObj;
	}
	else
	{
		pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skaterIndex );
		pSkater = Mdl::Skate::Instance()->GetSkater( skaterIndex );
	}
		
	if( pSkater && pSkater->IsLocalClient() )
	{
		uint32 movieName = 0;
		pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );

		int skippable = 0;
		pParams->GetInteger( "skippable", &skippable );

		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		skate_mod->GetMovieManager()->SetMovieSkippable( movieName, skippable );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSkaterCamAnimShouldPause | changes the pause mode of a skater cam.
// If the pause mode is set to false, the cam will play even if the game is paused
// @parmopt name | skaterId | | the id of the skater
// @parmopt int | skater | 0 | the skater index
bool ScriptSetSkaterCamAnimShouldPause( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 skaterId;
	int skaterIndex = 0;
	Obj::CSkater* pSkater = NULL;
	if ( pParams->GetChecksum( "skaterId", &skaterId, Script::NO_ASSERT ) )
	{
		Obj::CObject* pObj = Obj::ResolveToObject( skaterId);
		Dbg_MsgAssert( pObj && pObj->GetType() == SKATE_TYPE_SKATER, ( "%x is not a CSkater", skaterId ) );
		pSkater = (Obj::CSkater*)pObj;
	}
	else
	{
		pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skaterIndex );
		pSkater = Mdl::Skate::Instance()->GetSkater( skaterIndex );
	}

	if ( pSkater && pSkater->IsLocalClient() )
	{
		uint32 movieName = 0;
		pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );

		int should_pause = 0;
		pParams->GetInteger( "should_pause", &should_pause );

		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		skate_mod->GetMovieManager()->SetMoviePauseMode( movieName, should_pause );
	}
	return true;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

// @script | GetCurrentSkaterCamAnimName | returns the name of the current skater cam (if any)
bool ScriptGetCurrentSkaterCamAnimName( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	uint32 movieName = skate_mod->GetMovieManager()->GetCurrentMovieName();
	if ( movieName != 0 )
	{
		pScript->GetParams()->AddChecksum( CRCD(0xa1dc81f9,"name"), movieName );
		return true;
	}

	pScript->GetParams()->AddString( CRCD(0xa1dc81f9,"name"), "none" );
	return false;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

// @script | GetSkaterCamAnimParams | returns the params of the current skater cam 
bool ScriptGetSkaterCamAnimParams( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 name = 0;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &name, Script::NO_ASSERT );
	
	uint32 skaterId;
	int skaterIndex = 0;
	Obj::CSkater* pSkater = NULL;
	if ( pParams->GetChecksum( "skaterId", &skaterId, Script::NO_ASSERT ) )
	{
		Obj::CObject* pObj = Obj::ResolveToObject( skaterId);
		Dbg_MsgAssert( pObj && pObj->GetType() == SKATE_TYPE_SKATER, ( "%x is not a CSkater", skaterId ) );
		pSkater = (Obj::CSkater*)pObj;
	}
	else
	{
		pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skaterIndex );
		pSkater = Mdl::Skate::Instance()->GetSkater( skaterIndex );
	}

	if ( pSkater && pSkater->IsLocalClient() )
	{
		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		Script::CStruct* pMovieParams = skate_mod->GetMovieManager()->GetMovieParams( name );
		if ( pMovieParams )
		{
			pScript->GetParams()->AppendStructure( pMovieParams );
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

// @script | SkaterCamAnimFinished | returns true if the anim for the
// given skater is finished
// @parmopt int | skater | 0 | the skater
// @parmopt name | skaterId | | the id of the skater
// @parmopt int | cam | 1 | the camera number 
bool ScriptSkaterCamAnimFinished( Script::CStruct *pParams, Script::CScript *pScript )
{
	// in this context, skater really means "viewport"
	uint32 skaterId;
	int skaterIndex = 0;
	Obj::CSkater* pSkater = NULL;
	if ( pParams->GetChecksum( "skaterId", &skaterId, Script::NO_ASSERT ) )
	{
		Obj::CObject* pObj = Obj::ResolveToObject( skaterId);
		Dbg_MsgAssert( pObj && pObj->GetType() == SKATE_TYPE_SKATER, ( "%x is not a CSkater", skaterId ) );
		pSkater = (Obj::CSkater*)pObj;
	}
	else
	{
		pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skaterIndex );
		pSkater = Mdl::Skate::Instance()->GetSkater( skaterIndex );
	}
		
	if( pSkater && pSkater->IsLocalClient() )
	{
		uint32 movieName = 0;
		pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );

		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		return skate_mod->GetMovieManager()->IsMovieComplete( movieName );
	}

	return true;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

// @script | SkaterCamAnimHeld | returns true if anim is held
// @parmopt int | skater | 0 | the skater
// @parmopt name | skater | | the id of the skater
bool ScriptSkaterCamAnimHeld( Script::CStruct *pParams, Script::CScript *pScript )
{	
	// in this context, skater really means "viewport"
	uint32 skaterId;
	int skaterIndex = 0;
	Obj::CSkater* pSkater = NULL;
	if ( pParams->GetChecksum( "skaterId", &skaterId, Script::NO_ASSERT ) )
	{
		Obj::CObject* pObj = Obj::ResolveToObject( skaterId);
		Dbg_MsgAssert( pObj && pObj->GetType() == SKATE_TYPE_SKATER, ( "%x is not a CSkater", skaterId ) );
		pSkater = (Obj::CSkater*)pObj;
	}
	else
	{
		pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skaterIndex );
		pSkater = Mdl::Skate::Instance()->GetSkater( skaterIndex );
	}
		
	if( pSkater && pSkater->IsLocalClient() )
	{
		uint32 movieName = 0;
		pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );

		Mdl::Skate * skate_mod = Mdl::Skate::Instance();
		return skate_mod->GetMovieManager()->IsMovieHeld( movieName );
	}

	return true;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

// @script | KillSkaterCamAnim | 
// @parmopt int | skater | 0 | the skater
// @parmopt name | skaterId | | the id of the skater
// @flag current | kill the current cam anim
// @flag all | kill all cam anims
bool ScriptKillSkaterCamAnim( Script::CStruct *pParams, Script::CScript *pScript )
{
	bool success = true;

	// in this context, skater really means "viewport"
	uint32 skaterId;
	int skaterIndex = 0;
	Obj::CSkater* pSkater = NULL;
	if ( pParams->GetChecksum( "skaterId", &skaterId, Script::NO_ASSERT ) )
	{
		Obj::CObject* pObj = Obj::ResolveToObject( skaterId);
		Dbg_MsgAssert( pObj && pObj->GetType() == SKATE_TYPE_SKATER, ( "%x is not a CSkater", skaterId ) );
		pSkater = (Obj::CSkater*)pObj;
	}
	else if( pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skaterIndex ))
	{
		pSkater = Mdl::Skate::Instance()->GetSkater( skaterIndex );
	}
	else
	{
		pSkater = Mdl::Skate::Instance()->GetLocalSkater();
	}
		
	if( pSkater && pSkater->IsLocalClient() )
	{
		if ( pParams->ContainsFlag( "current" ) )
		{
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			success = skate_mod->GetMovieManager()->AbortCurrentMovie( false );
		}
		else if ( pParams->ContainsFlag( "all" ) )
		{
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			skate_mod->GetMovieManager()->ClearMovieQueue();
			success = true;
		}
		else
		{
			uint32 movieName = 0;
			pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName, true );
			Mdl::Skate * skate_mod = Mdl::Skate::Instance();
			skate_mod->GetMovieManager()->RemoveMovieFromQueue( movieName );
			success = true;
		}
	}

	// GJ:  if the movie queue is empty, it's time to restore the skater
	// camera (this fixes SK5:TT13239 - "View goals - camera stays on
	// view goal position instead of returning to skater when unpausing"
	// or one of the park editor cameras
	if ( !Mdl::Skate::Instance()->GetMovieManager()->IsRolling() )
	{
		if ( Ed::CParkEditor::Instance()->EditingCustomPark() )
		{
			Ed::CParkEditor::Instance()->SetAppropriateCamera();	
		}
		else
		{
			Script::RunScript( CRCD(0x15674315,"restore_skater_camera") );
		}
	}

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ReloadSkaterCamAnim | undocumented
bool ScriptReloadSkaterCamAnim( Script::CStruct* pParams, Script::CScript* pScript )
{
	if ( !Config::GotExtraMemory() )
	{
		Dbg_Message( "ReloadSkaterCamAnim:  This function only works with the extra debug heap.  (Otherwise, it would fragment the bottom-up heap.)" );
		return false;
	}

	uint32 animName;
	const char* pFileName;

	if ( !pParams->GetChecksum( NONAME, &animName, false ) )
	{
		Dbg_Message( "ReloadSkaterCamAnim:  Missing cam anim name.\n" );
		return false;
	}

	if ( !pParams->GetText( NONAME, &pFileName, false )	)
	{
		Dbg_Message( "ReloadSkaterCamAnim:  Missing cam anim filename.\n" );
		return false;
	}

	Dbg_Message( "Reloading movie %08x with file %s...", animName, pFileName );

	// clear any existing movies in the queue, just in case we were in the middle of playing the anim to be replaced
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	skate_mod->GetMovieManager()->ClearMovieQueue();

	// reload the appropriate asset
	Ass::CAssMan * ass_man = Ass::CAssMan::Instance();
	
	// on the debug heap
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
	
	bool success =  ass_man->ReloadAsset( animName, pFileName, false );
	
	Mem::Manager::sHandle().PopContext();

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PlayCutscene | Play a cutscene
bool ScriptPlayCutscene( Script::CStruct *pParams, Script::CScript *pScript )
{
	if( pParams->ContainsFlag( "stop" ))
	{
		Dbg_MsgAssert( 0, ( "stop parameter doesn't work with PlayCutscene" ) );
	}
	
//	uint32 movieName = 0;
//	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );

	// all cutscenes are named as "cutscene"...  this will
	// help me enforce that there is only one cutscene playing
	// at any given time...
	uint32 movieName = CRCD(0xf5012f52,"cutscene");

	// We don't want any messages showing through the cutscene
	Script::RunScript("destroy_goal_panel_messages");

	// start the movie with the default params...
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	skate_mod->GetMovieManager()->AddMovieToQueue( movieName, pParams, CRCD(0xf5012f52,"cutscene") );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | HasMovieStarted | return true if the video of a movie has started 
// (after loading data)
bool ScriptHasMovieStarted( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	return skate_mod->GetMovieManager()->HasMovieStarted();
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsMovieQueued | return true if there are any movies in the queue,
// regardless of whether the video has started
bool ScriptIsMovieQueued( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	return skate_mod->GetMovieManager()->IsMovieQueued();
}
   
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PlayMovingObjectAnim | Play an scene-based animation (earthquake, for instance)
bool ScriptPlayMovingObjectAnim( Script::CStruct *pParams, Script::CScript *pScript )
{
	if( pParams->ContainsFlag( "stop" ))
	{
		Dbg_MsgAssert( 0, ( "stop parameter doesn't work with PlayMovingObjectAnim" ) );
	}
	
	uint32 movieName = 0;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );

	Dbg_Message( "playmovingobjectanim %s", Script::FindChecksumName(movieName) );

	// start the movie with the default params...
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	skate_mod->GetObjectAnimManager()->AddMovieToQueue( movieName, pParams, CRCD(0x12743edb,"objectanim") );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetMovingObjectAnimSkippable | set the current movie as skippable
// @parmopt int | name | | the name of the movie to set skippable (otherwise, it will use the currently-playing movie)
// @parmopt int | skippable | 0 | set to anything greater than 0 to set anim to skippable
bool ScriptSetMovingObjectAnimSkippable( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 movieName = 0;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );

	int skippable = 0;
	pParams->GetInteger( "skippable", &skippable );

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	skate_mod->GetObjectAnimManager()->SetMovieSkippable( movieName, skippable );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetMovingObjectAnimShouldPause | changes the pause mode of a skater cam.
// If the pause mode is set to false, the cam will play even if the game is paused
bool ScriptSetMovingObjectAnimShouldPause( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 movieName = 0;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );

	int should_pause = 0;
	pParams->GetInteger( "should_pause", &should_pause );

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	skate_mod->GetObjectAnimManager()->SetMoviePauseMode( movieName, should_pause );
	
	return true;
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

// @script | MovingObjectAnimFinished | returns true if the anim for the
// given skater is finished
// @parm int | skater | the skater
// @parmopt int | cam | 1 | the camera number 
bool ScriptMovingObjectAnimFinished( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 movieName = 0;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	return skate_mod->GetObjectAnimManager()->IsMovieComplete( movieName );
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

// @script | MovingObjectAnimHeld | returns true if anim is held
// @parm int | skater | the skater
bool ScriptMovingObjectAnimHeld( Script::CStruct *pParams, Script::CScript *pScript )
{	
	uint32 movieName = 0;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName );
		
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	return skate_mod->GetObjectAnimManager()->IsMovieHeld( movieName );
}

/******************************************************************/
/*																  */
/*                                                                */
/******************************************************************/

// @script | KillMovingObjectAnim | 
// @parmopt int | skater | 0 | the skater
// @flag current | kill the current cam anim
// @flag all | kill all cam anims
bool ScriptKillMovingObjectAnim( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CMovieManager* pObjectAnimManager = skate_mod->GetObjectAnimManager(); 

	if ( pParams->ContainsFlag( "current" ) )
	{
		return pObjectAnimManager->AbortCurrentMovie( false );
	}
	else if ( pParams->ContainsFlag( "all" ) )
	{
		pObjectAnimManager->ClearMovieQueue();
		return true;
	}
	else
	{
		uint32 movieName = 0;
		pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &movieName, true );
		pObjectAnimManager->RemoveMovieFromQueue( movieName );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ReloadMovingObjectAnim | undocumented
bool ScriptReloadMovingObjectAnim( Script::CStruct* pParams, Script::CScript* pScript )
{
	if ( !Config::GotExtraMemory() )
	{
		Dbg_Message( "ReloadMovingObjectAnim:  This function only works with the extra debug heap.  (Otherwise, it would fragment the bottom-up heap.)" );
		return false;
	}

	uint32 animName;
	const char* pFileName;

	if ( !pParams->GetChecksum( NONAME, &animName, false ) )
	{
		Dbg_Message( "ReloadMovingObjectAnim:  Missing cam anim name.\n" );
		return false;
	}

	if ( !pParams->GetText( NONAME, &pFileName, false )	)
	{
		Dbg_Message( "ReloadMovingObjectAnim:  Missing cam anim filename.\n" );
		return false;
	}

	Dbg_Message( "Reloading movie %08x with file %s...", animName, pFileName );

	// clear any existing movies in the queue, just in case we were in the middle of playing the anim to be replaced
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	skate_mod->GetObjectAnimManager()->ClearMovieQueue();

	// reload the appropriate asset
	Ass::CAssMan * ass_man = Ass::CAssMan::Instance();
	
	// on the debug heap
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
	
	bool success = ass_man->ReloadAsset( animName, pFileName, false );
	
	Mem::Manager::sHandle().PopContext();

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// @script | SkaterDebugOn | sets debug to on
bool ScriptSkaterDebugOn(Script::CStruct *pParams, Script::CScript *pScript)
{   
	Obj::DebugSkaterScripts=true;
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SkaterDebugOff | sets debug to off
bool ScriptSkaterDebugOff(Script::CStruct *pParams, Script::CScript *pScript)
{   
	Obj::DebugSkaterScripts=false;
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | VibrationIsOn | returns true if vibration for the specified
// player is on
// @uparm 0 | the player 
bool ScriptVibrationIsOn(Script::CStruct *pParams, Script::CScript *pScript)
{
	int i=0;
	pParams->GetInteger(NONAME,&i);
	Dbg_MsgAssert(i>=0 && imp_controller_preferences[i].VibrationOn;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | VibrationOn | turns on vibration for the specified player
// @uparm 0 | player
bool ScriptVibrationOn(Script::CStruct *pParams, Script::CScript *pScript)
{
	int i=0;
	pParams->GetInteger(NONAME,&i);
	Dbg_MsgAssert(i>=0 && iSetVibration(i,true);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | VibrationOff | turns off vibration for the specified player
// @uparm 0 | the player 
bool ScriptVibrationOff(Script::CStruct *pParams, Script::CScript *pScript)
{
	int i=0;
	pParams->GetInteger(NONAME,&i);
	Dbg_MsgAssert(i>=0 && iSetVibration(i,false);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AutoKickIsOn | returns true if autokick is on for the specified skater
// @uparmopt 0 | skater
bool ScriptAutoKickIsOn(Script::CStruct *pParams, Script::CScript *pScript)
{   
	int i=0;
	pParams->GetInteger(NONAME,&i);
	Dbg_MsgAssert(i>=0 && imp_controller_preferences[i].AutoKickOn;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AutoKickOn | turns on autokick for the specified player
// @uparmopt 0 | the skater
bool ScriptAutoKickOn(Script::CStruct *pParams, Script::CScript *pScript)
{   
	int i=0;
	pParams->GetInteger(NONAME,&i);
	Dbg_MsgAssert(i>=0 && iSetAutoKick(i,true);
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AutoKickOff | turns off autokick for the specified player
// @uparmopt 0 | skater
bool ScriptAutoKickOff(Script::CStruct *pParams, Script::CScript *pScript)
{   
	int i=0;
	pParams->GetInteger(NONAME,&i);
	Dbg_MsgAssert(i>=0 && iSetAutoKick(i,false);
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SpinTapsAreOn | returns true if spin taps are on
// @uparmopt 0 | skater
bool ScriptSpinTapsAreOn(Script::CStruct *pParams, Script::CScript *pScript)
{   
	int i=0;
	pParams->GetInteger(NONAME,&i);
	Dbg_MsgAssert(i>=0 && imp_controller_preferences[i].SpinTapsOn;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SpinTapsOn | turns on spin taps for the specified skater
// @uparmopt 0 | skater
bool ScriptSpinTapsOn(Script::CStruct *pParams, Script::CScript *pScript)
{
	int i=0;
	pParams->GetInteger(NONAME,&i);
	Dbg_MsgAssert(i>=0 && iSetSpinTaps(i,true);
	return true;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SpinTapsOff | turns off spin taps for the specified skater
// @uparmopt 0 | skater
bool ScriptSpinTapsOff(Script::CStruct *pParams, Script::CScript *pScript)
{   
	int i=0;
	pParams->GetInteger(NONAME,&i);
	Dbg_MsgAssert(i>=0 && iSetSpinTaps(i,false);
	return true;
}	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetCurrentProDisplayInfo | 
bool ScriptGetCurrentProDisplayInfo(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile();

	pScript->GetParams()->AddComponent( Script::GenerateCRC( "string" ), ESYMBOLTYPE_STRING, pSkaterProfile->GetDisplayName());
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetPlayerAppearance | sets the player appearance 
// @parmopt int | player | 0 | index of player whose appearance you want to edit
// @parmopt name | appearance_structure | | the name of the profile
bool ScriptSetPlayerAppearance(Script::CStruct *pParams, Script::CScript *pScript)
{	
	int player;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &player, Script::ASSERT );
	
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( player );
	Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
	
	Script::CStruct* pAppearanceStruct;
	pParams->GetStructure( "appearance_structure", &pAppearanceStruct, Script::ASSERT );
	pAppearance->Load( pAppearanceStruct );
	
	return true;
}
					
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetPlayerFacePoints | Gets the player face points and puts it in the structure "current_face_points"
// @parm int | player | 0 | index of player
bool ScriptGetPlayerFacePoints(Script::CStruct *pParams, Script::CScript *pScript)
{
	int player;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &player, Script::ASSERT );
	
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( player );
	Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
	
	Gfx::CFaceTexture* pFaceTexture = pAppearance->GetFaceTexture();
	Dbg_MsgAssert( pFaceTexture, ( "Appearance doesn't have a face texture1" ) );
	Dbg_MsgAssert( pFaceTexture->IsValid(), ( "Face texture has not been initialized with valid texture data" ) );

	Script::CStruct *pFacePointsStruct = NULL;

	bool make_new_structure = false;
	if (!pScript->GetParams()->GetStructure( CRCD(0x12e87395,"current_face_points"), &pFacePointsStruct ))
	{
		pFacePointsStruct = new Script::CStruct;
		make_new_structure = true;
	}

	Nx::SFacePoints theFacePoints;
	theFacePoints = pFaceTexture->GetFacePoints( );
	if ( !Nx::SetFacePointsStruct( theFacePoints, pFacePointsStruct ) )
	{
		Script::PrintContents( pParams );
		Dbg_MsgAssert( 0, ("Couldn't set face points to structure") );
	}

	if (make_new_structure)
	{
		pScript->GetParams()->AddStructure( CRCD(0x12e87395,"current_face_points"), pFacePointsStruct );
		delete pFacePointsStruct;
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetPlayerFacePoints |
// @parmopt struct | face_points | face points structure
bool ScriptSetPlayerFacePoints(Script::CStruct *pParams, Script::CScript *pScript)
{
	int player;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &player, Script::ASSERT );
	
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( player );
	Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
	
	Gfx::CFaceTexture* pFaceTexture = pAppearance->GetFaceTexture();
	Dbg_MsgAssert( pFaceTexture, ( "Appearance doesn't have a face texture1" ) );
	Dbg_MsgAssert( pFaceTexture->IsValid(), ( "Face texture has not been initialized with valid texture data" ) );

	Nx::SFacePoints theFacePoints;
	Script::CStruct *pFacePointsStruct;
	if (!pParams->GetStructure("face_points", &pFacePointsStruct))
	{
		pFacePointsStruct = pParams;
	}
	if ( !Nx::GetFacePointsStruct( theFacePoints, pFacePointsStruct ) )
	{
		Script::PrintContents( pParams );
		Dbg_MsgAssert( 0, ("Couldn't read face points from structure") );
	}
	pFaceTexture->SetFacePoints( theFacePoints );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetPlayerFaceTexture |
bool ScriptSetPlayerFaceTexture(Script::CStruct *pParams, Script::CScript *pScript)
{
	int player;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &player, Script::ASSERT );
	
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( player );
	Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
	
	const char* pFaceName;
	pParams->GetText( CRCD(0x7d99f28d,"texture"), &pFaceName, Script::ASSERT );

	// by default, LoadFace will prepend the filename with "images\\"
	// however, the neversoft skater images are located in "textures\\"
	// so the following flag will tell LoadFace not to prepend the
	// filename
	bool fullPathIncluded = pParams->ContainsFlag( CRCD(0x54c2e57c,"FullPathIncluded") );

	Gfx::CFaceTexture* pFaceTexture = pAppearance->GetFaceTexture();
	Dbg_MsgAssert( pFaceTexture, ( "Appearance doesn't have a face texture2" ) );
	pFaceTexture->LoadFace( pFaceName, fullPathIncluded );

	Dbg_MsgAssert( pFaceTexture->IsValid(), ( "Face texture should now be considered valid" ) );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetPlayerFaceOverlayTexture |
bool ScriptSetPlayerFaceOverlayTexture(Script::CStruct *pParams, Script::CScript *pScript)
{
	int player;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &player, Script::ASSERT );
	
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( player );
	Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
	
	const char* pOverlayTextureName;
	pParams->GetText( CRCD(0x7d99f28d,"texture"), &pOverlayTextureName, Script::ASSERT );

	Gfx::CFaceTexture* pFaceTexture = pAppearance->GetFaceTexture();
	Dbg_MsgAssert( pFaceTexture, ( "Appearance doesn't have a face texture3" ) );
	Dbg_MsgAssert( pFaceTexture->IsValid(), ( "Face texture is not valid" ) );

	pFaceTexture->SetOverlayTextureName( pOverlayTextureName );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ClearPlayerFaceTexture |
bool ScriptClearPlayerFaceTexture(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_NGPS__
	int player;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &player, Script::ASSERT );
	
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( player );
	Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
	Dbg_Assert( pAppearance );

	Gfx::CFaceTexture* pFaceTexture = pAppearance->GetFaceTexture();
	Dbg_MsgAssert( pFaceTexture, ( "Appearance doesn't have a face texture4" ) );
	pFaceTexture->SetValid( false );
#endif
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PlayerFaceIsValid | returns whether the player has already loaded or downloaded a face
bool ScriptPlayerFaceIsValid(Script::CStruct *pParams, Script::CScript *pScript)
{
	int player;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &player, Script::ASSERT );
	
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( player );
	Gfx::CModelAppearance* pAppearance = pSkaterProfile->GetAppearance();
	Dbg_Assert( pAppearance );

	Gfx::CFaceTexture* pFaceTexture = pAppearance->GetFaceTexture();
	//Dbg_MsgAssert( pFaceTexture, ( "Appearance doesn't have a face texture5" ) );
    if ( pFaceTexture )
    {
        return pFaceTexture->IsValid();
    }
    else
    {
        //Appearance doesn't have a face texture
        return false;
    }
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SelectCurrentSkater | sets the current profile to the specified skater
// @parmopt name | name | | the name of the profile
// @uparmopt name | name of the profile
bool ScriptSelectCurrentSkater(Script::CStruct *pParams, Script::CScript *pScript)
{	
	uint32 profileName;
	if ( !pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &profileName, false ) )
	{
		pParams->GetChecksum( NONAME, &profileName, true );
	}

	
	Obj::CPlayerProfileManager*	pPlayerProfileManager=Mdl::Skate::Instance()->GetPlayerProfileManager();
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile();

	// remember old checksum to see if the data has changed
	if ( pSkaterProfile->GetSkaterNameChecksum() == profileName )
	{
		// no change
		return false;
	}
	
	pPlayerProfileManager->ApplyTemplateToCurrentProfile( profileName );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CareerStartLevel | Called at the start of the level, and whenever
// the level is restarted (by a retry, for example).  This will set the level
// number (unless you pass -1, in which case it uses the "current level"). This
// will store the values of the career goals, and the level flags, at the start
// of the level, so at the end of the level, we will be able to see if we have
// just got a particular goal, and play appropriate rewards. Currently you
// should not have to mess with this.  I've added appropriate CareerStartLevel 
// commands in the level loading script in levels.q.  I've also added (in the
// C++ code) what amounts to "CareerStartLevel level = -1" to the "RestartLevel"
// function in skate.cpp.  This gets called whenever you restart a level (via
// a retry). 
// @parm int | level | the level to start
bool ScriptCareerStartLevel(Script::CStruct *pParams, Script::CScript *pScript)
{
	int level; 		
	pParams->GetInteger("level", &level, true);  		// Note we assert if "level" is missing
	Mdl::Skate::Instance()->GetCareer()->StartLevel(level);
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CareerLevelIs | This is true if the current level number, as specified
// in the last call to CareerStartLevel, matches the value passed, for example: 
// if CareerLevelIs 1 
//   printf "Foundry!" 
// endif
// @uparm 1 | level to check
bool ScriptCareerLevelIs(Script::CStruct *pParams, Script::CScript *pScript)
{
	int level; 		
	pParams->GetInteger(NONAME, &level, true);	// Note we assert if the level value is missing
	

	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();

	return pCareer->GetLevel()==level;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetRecordText | 
// @parmopt int | level | | level num (default is current level)
bool ScriptGetRecordText(Script::CStruct *pParams, Script::CScript *pScript)
{
	int level; 	  
	  

	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();

	if (!pParams->GetInteger("level", &level, false))	
	{
		level = pCareer->GetLevel();
	}
			  
	Mdl::Skate::Instance()->GetRecordsText(level);	
	return true;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UpdateRecords | 
bool ScriptUpdateRecords(Script::CStruct *pParams, Script::CScript *pScript)
{

	Mdl::Skate::Instance()->UpdateRecords();	
	return true;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CareerReset | Resets all the career info back to as if you've
// just started a new career.  Note that this also sets the current level
// to 0 (as you'd not have unlocked any levels).  So if you want to use
// this in the middle of playing a  level, then you'd better add a "StartLevel
// level = ???" to set the correct level. See ResetLevelGoals for more
bool ScriptCareerReset(Script::CStruct *pParams, Script::CScript *pScript)
{

	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();

	pCareer->Reset();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetGoal | Sets the goal to the "got" state.  All this does
// is set a flag, anything else, like playing sounds, should be done in script
// @parm int | goal | the goal num
bool ScriptSetGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	int goal; 		
	pParams->GetInteger("goal", &goal, true);  		// Note we assert if "goal" is missing
	

	
	// if we are not in career mode, then exit  		   
	if (!Mdl::Skate::Instance()->GetGameMode()->IsTrue( CRCD(0x1ded1ea4,"is_career") ) && !Mdl::Skate::Instance()->GetGameMode()->IsFrontEnd() )
	{
		return true;
	}
	
	
	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();

	pCareer->SetGoal(goal);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnSetGoal | Clears a particular goal flag
// @parm int | goal | goal flag to clear
bool ScriptUnSetGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	int goal; 		
	pParams->GetInteger("goal", &goal, true);  		// Note we assert if "goal" is missing
	

	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();

	pCareer->UnSetGoal(goal);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetGoal | Get the state of a goal.  Will be "true" if you have
// got the goal, either in this session, or in some previous session. For
// example:  
// if GetGoal goal = GOAL_SKATE  ; if got SKATE, then do nothing here 
// else 
//   trigger skater letters 
// endif 
// The primary purpose of this function is the modify the functionality
// of the startup script, to stop things from being created, and to modify
// the intro camera. 
// @parm int | goal | the goal to check
bool ScriptGetGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	int goal; 		
	pParams->GetInteger("goal", &goal, true);  		// Note we assert if "goal" is missing
	

	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();

	return pCareer->GetGoal(goal);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | JustGotGoal | Is "true" if you got this goal in the current
// session (still valid if the session has just ended). For example: 
// if JustGotoGoal goal = GOAL_SKATE 
//  LaunchPanelMessage "Yay! You got the skate letters" 
// endif 
// The primary purpose if to play one-time congratulatory sequences at
// the end of the level, in the end-level script.  This flag will only
// be true during the session when you actually get the goal
// @parm int | goal | goal th check
bool ScriptJustGotGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	int goal; 		
	pParams->GetInteger("goal", &goal, true);  		// Note we assert if "goal" is missing
	

	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();

	return pCareer->JustGotGoal(goal);   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetFlag | similar to setgoal.  there are 32 flags per level,
// numbered 0 - 31.
// @parm int | flag | the flag to set
bool ScriptSetFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
	int flag; 		
	pParams->GetInteger("flag", &flag, true);  		// Note we assert if "flag" is missing
	

	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();

	pCareer->SetFlag(flag);
	
	if (flag==Script::GetInt("GOAL_DECK"))
	{
		//Front::GetNewDeck();
	}	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnSetFlag | similar to unsetgoal.  there are 32 flags per level,
// numbered 0 - 31.
// @parm int | flag | the flag to unset
// @parmopt int | level | | level number
bool ScriptUnSetFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
	int flag; 		
	pParams->GetInteger("flag", &flag, true);  		// Note we assert if "flag" is missing
	

	int level = -1;
	pParams->GetInteger( "level", &level, Script::NO_ASSERT );

	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();

	pCareer->UnSetFlag(flag, level);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetFlag | similar to GetGoal.  gets the state of the flag
// @parm int | flag | the flag to get
// @parmopt int | level | | the level number (for level specific flags).  if
// no level number is given, it will use the current level number
bool ScriptGetFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
	int flag;
	pParams->GetInteger("flag", &flag, true);  		// Note we assert if "flag" is missing
	
	int level = 0;
	pParams->GetInteger( "level", &level, Script::NO_ASSERT );

	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();
	if ( level )
		return pCareer->GetFlag( flag, level );

	return pCareer->GetFlag( flag );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | JustGotFlag | similar to the JustGotGoal command.  returns true
// if we just got this flag
// @parm int | flag | the flag to check
bool ScriptJustGotFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
	int flag; 		
	pParams->GetInteger("flag", &flag, true);  		// Note we assert if "flag" is missing
	

	Obj::CSkaterCareer* pCareer = Mdl::Skate::Instance()->GetCareer();
	return pCareer->JustGotFlag(flag);	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetGlobalFlag | sets a global flag
// @parm int | flag | the global flag to set
bool ScriptSetGlobalFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __NOPT_ASSERT__
	if (!pParams->ContainsComponentNamed("flag"))
	{
		printf("Call to SetGlobalFlag has no flag (or that flag is undefined.\n%s", pScript->GetScriptInfo());
		Dbg_Assert(false);
	}
#endif

	int flag; 		
	pParams->GetInteger(CRCD(0x2e0b1465, "flag"), &flag, true);  		// Note we assert if "flag" is missing
	
	s_get_career(flag,pParams)->SetGlobalFlag(flag);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnSetGlobalFlag | unsets a global flag
// @parm int | flag | the flag to unset
bool ScriptUnSetGlobalFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __NOPT_ASSERT__
	if (!pParams->ContainsComponentNamed("flag"))
	{
		printf("Call to UnSetGlobalFlag has no flag (or that flag is undefined.\n%s", pScript->GetScriptInfo());
		Dbg_Assert(false);
	}
#endif

	int flag; 		
	pParams->GetInteger(CRCD(0x2e0b1465, "flag"), &flag, true);  		// Note we assert if "flag" is missing
	

	
	s_get_career(flag,pParams)->UnSetGlobalFlag(flag);
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetGlobalFlag | gets a global flag
// @parm int | flag | the flag to get
bool ScriptGetGlobalFlag(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __NOPT_ASSERT__
	if (!pParams->ContainsComponentNamed("flag"))
	{
		printf("Call to GetGlobalFlag has no flag (or that flag is undefined.\n%s", pScript->GetScriptInfo());
		Dbg_Assert(false);
	}
#endif
	
	int flag; 		
	pParams->GetInteger(CRCD(0x2e0b1465, "flag"), &flag, true);  		// Note we assert if "flag" is missing
	
	return Mdl::Skate::Instance()->GetCareer()->GetGlobalFlag(flag);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ProfileEquals | tells us whether the skater matches the given
// criteria.  example: 
// if ProfileEquals is_pro
// @flag is_pro | check if this is a pro skater
// @flag is_custom | check if this is a custom skater
// @parmopt name | is_named | | check if the skater has the specified name
// @parmopt name | stance | | check for this stance
bool ScriptProfileEquals(Script::CStruct *pParams, Script::CScript *pScript)
{
	// look for the specified skater (0 by default)
	Obj::CSkater* pSkater;
	
	// if this function is being called through
	// a skater script, then use the specified skater...
	if( pScript && pScript->mpObject && pScript->mpObject->GetType() == SKATE_TYPE_SKATER )	
	{
		pSkater = (Obj::CSkater *) pScript->mpObject.Convert();
		return pSkater->SkaterEquals( pParams );
	}

	if ( !Mdl::Skate::Instance()->IsMultiplayerGame() || Mdl::Skate::Instance()->GetGameMode()->IsFrontEnd() )
	{
		Dbg_MsgAssert( 0, ( "ProfileEquals should only be called from a skater script in a multiplayer game." ) );
		return false;
	}

	// otherwise, get the first skater and check him
	pSkater = Mdl::Skate::Instance()->GetSkater( 0 );
	if( pSkater == NULL )
	{
		return false;
	}

	return pSkater->SkaterEquals( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsCareerMode | returns true if the current game mode is
// career mode
bool ScriptIsCareerMode(Script::CStruct *pParams, Script::CScript *pScript)
{


	return Mdl::Skate::Instance()->GetGameMode()->IsTrue( CRCD(0x1ded1ea4,"is_career") );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ClearScoreGoals | clears all the score goals
bool ScriptClearScoreGoals(Script::CStruct *pParams, Script::CScript *pScript)
{

	Mdl::Skate::Instance()->ClearScoreGoals();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetScoreGoal | used to add a new score goal.  examples: 
// SetScoreGoal  score = 1200000 goalscript = Got_SickScore goal=GOAL_SICKSCORE 
// SetScoreGoal    score = 30000 goalscript = Got_HighScore goal=GOAL_HIGHSCORE
// @parm int | score | the score value for this goal
// @parm name | goalscript | the script to run 
// @parm int | goal | goal number
bool ScriptSetScoreGoal(Script::CStruct *pParams, Script::CScript *pScript)
{
	int score;
	pParams->GetInteger("score", &score, true);
	uint32	script;
	pParams->GetChecksum("goalscript", &script, true);
	int	goal;
	pParams->GetInteger("goal", &goal, true);

	
	// Note, for now we are just using the goal number as an index into the table
	// so long as we have enough entries, we will be fine
	Mdl::Skate::Instance()->SetScoreGoalScore(goal,score);
	Mdl::Skate::Instance()->SetScoreGoalScript(goal,script);
	Mdl::Skate::Instance()->SetScoreGoalGoal(goal,goal);
	
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EndRun | This command will end the current run (same as
// used by the "end run" selection in the pause menu)
bool ScriptEndRun(Script::CStruct *pParams, Script::CScript *pScript)
{

	Mdl::Skate::Instance()->EndRun();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ShouldEndRun | if "end run" was selected from the menu
// of the end conditions for this game mode have been met
bool ScriptShouldEndRun(Script::CStruct *pParams, Script::CScript *pScript)
{

	// if "end run" was selected from the menu or the
	// end conditions for this game mode have been met
	return ( Mdl::Skate::Instance()->EndRunSelected() || Mdl::Skate::Instance()->GetGameMode()->EndConditionsMet() );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | InitializeSkaters | 
bool ScriptInitializeSkaters(Script::CStruct *pParams, Script::CScript *pScript)
{	

	for ( unsigned int i = 0; i < Mdl::Skate::Instance()->GetNumSkaters(); i++)
	{
		Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkater(i);
		Dbg_Assert( pSkater );
		if( pSkater->IsLocalClient())
		{
			pSkater->Reset();
		}
		Mdl::Skate::Instance()->HideSkater( pSkater, false );
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EndRunSelected | true if "end run" was selected from the menu
bool ScriptEndRunSelected(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	// if "end run" was selected from the menu
	return Mdl::Skate::Instance()->EndRunSelected();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AllSkatersAreIdle | true if all skaters are idle
bool ScriptAllSkatersAreIdle(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	return Mdl::Skate::Instance()->SkatersAreIdle();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | FirstTrickStarted | should only be called in horse mode.  
bool ScriptFirstTrickStarted(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	return Mdl::Skate::Instance()->FirstTrickStarted();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | FirstTrickCompleted | should only be called in horse mode
bool ScriptFirstTrickCompleted(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	return Mdl::Skate::Instance()->FirstTrickCompleted();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CalculateFinalScores | final scores for all skaters on server
bool ScriptCalculateFinalScores(Script::CStruct *pParams, Script::CScript *pScript)
{
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	

	if( gamenet_man->OnServer())
	{
		Mdl::Skate::Instance()->SendScoreUpdates( true );
		return true;
	}

	return gamenet_man->HaveReceivedFinalScores();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ReinsertSkaters | adds all skaters to the current world
bool ScriptReinsertSkaters(Script::CStruct *pParams, Script::CScript *pScript)
{
    // For now, this is commented out, until I can get the
    // skin model functionality over into the new CModel class.
	
	// synchronous, no need to pass through net msgs
	// add the skaters back to the world
	
	for ( uint32 i = 0; i < Mdl::Skate::Instance()->GetNumSkaters(); i++ )
	{   
		Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkater( i );
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterHeap(pSkater->GetHeapIndex()));
		pSkater->AddToCurrentWorld();
		Mem::Manager::sHandle().PopContext();
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnhookSkaters | Unhook the skaters from the world, so it can be unloaded
bool ScriptUnhookSkaters(Script::CStruct *pParams, Script::CScript *pScript)
{
	for ( uint32 i = 0; i < Mdl::Skate::Instance()->GetNumSkaters(); i++ )
	{   
		Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkater( i );
		pSkater->RemoveFromCurrentWorld();
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ApplySplitScreenOptions | applies various split screen prefs - 
// game type, time limit, etc.
bool ScriptApplySplitScreenOptions(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Prefs::Preferences* pPreferences;

	pPreferences = Mdl::Skate::Instance()->GetSplitScreenPreferences();
	Dbg_Assert(pPreferences);

	Script::CStruct* pStructure;
	uint32 checksum;

	// change the game type
	pStructure = pPreferences->GetPreference( Script::GenerateCRC("game_type") );
	Dbg_Assert(pStructure);
	pStructure->GetChecksum( "checksum", &checksum, true );
	Mdl::Skate::Instance()->GetGameMode()->LoadGameType( checksum );

	int time_limit = pPreferences->GetPreferenceValue( Script::GenerateCRC("time_limit"), Script::GenerateCRC("time") );

	if ( Mdl::Skate::Instance()->GetGameMode()->IsTrue( "is_horse" ) )
	{
		time_limit = pPreferences->GetPreferenceValue( Script::GenerateCRC("horse_time_limit"), Script::GenerateCRC("time") );
		Mdl::Skate::Instance()->GetHorse()->SetWord( pPreferences->GetPreferenceString(  Script::GenerateCRC("horse_word"), Script::GenerateCRC("ui_string") ) );

		Script::CStruct* pTempStructure = new Script::CStruct;
		pTempStructure->AddComponent( Script::GenerateCRC("default_time_limit"), ESYMBOLTYPE_INTEGER, time_limit );
		Mdl::Skate::Instance()->GetGameMode()->OverrideOptions( pTempStructure );		
		Mdl::Skate::Instance()->SetTimeLimit( time_limit );
		delete pTempStructure;

		//Mdl::Skate::Instance()->SetShadowMode( Gfx::vDETAILED_SHADOW );
	}
	else
	{
		//Mdl::Skate::Instance()->SetShadowMode( Gfx::vSIMPLE_SHADOW );
	}
	
	/*if( Mdl::Skate::Instance()->GetGameMode()->IsTrue( "is_king" ))
	{
		Script::CStruct* pTempStructure = new Script::CStruct;
		Script::CArray* pArray = new Script::CArray;
		Script::CopyArray(pArray,Script::GetArray("targetScoreArray"));
		Script::CStruct* pSubStruct = pArray->GetStructure(0);
		Dbg_Assert(pSubStruct);
		pSubStruct->AddComponent(Script::GenerateCRC("score"),ESYMBOLTYPE_INTEGER, (int) Tmr::Seconds( time_limit ));
		pTempStructure->AddComponent( Script::GenerateCRC("default_time_limit"), ESYMBOLTYPE_INTEGER, (int) 0 );
		pTempStructure->AddComponent( Script::GenerateCRC("victory_conditions"), pArray );
		Mdl::Skate::Instance()->GetGameMode()->OverrideOptions( pTempStructure );
		Mdl::Skate::Instance()->SetTimeLimit( 0 );
		delete pTempStructure;
	}
	else
	{
		// if the time limit is fixed, then don't override it
		if ( !Mdl::Skate::Instance()->GetGameMode()->IsTimeLimitConfigurable() )
		{
			time_limit = Mdl::Skate::Instance()->GetGameMode()->GetTimeLimit();
		}

		Script::CStruct* pTempStructure = new Script::CStruct;
		pTempStructure->AddComponent( Script::GenerateCRC("default_time_limit"), ESYMBOLTYPE_INTEGER, time_limit );
		Mdl::Skate::Instance()->GetGameMode()->OverrideOptions( pTempStructure );		
		Mdl::Skate::Instance()->SetTimeLimit( time_limit );
		delete pTempStructure;
	}
	
//	printf("Applying time limit\n");
//	pTempStructure->PrintContents();

	// update all the skaters' stats
	for ( unsigned int i = 0; i < Mdl::Skate::Instance()->GetNumSkaters(); i++)
	{
		Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetSkater(i);
		Dbg_Assert( pSkater );

		Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile( pSkater->GetID() );
		pSkater->UpdateStats( pSkaterProfile );
	}*/

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StartCompetition | 
// @parm float | gold | 
// @parm float | silver | 
// @parm float | bronze | 
// @parm float | gold_score |
// @parm float | silver_score | 
// @parm float | bronze_score | 
// @parm float | bail | 
bool ScriptStartCompetition(Script::CStruct *pParams, Script::CScript *pScript)
{   
	
//	float bronze, float silver, float gold, float bronze_score, float silver_score, float gold_score, float bail)
	float	gold,silver,bronze;
	float	gold_score,silver_score,bronze_score;
	float 	bail;
	
	if (!pParams->GetFloat("gold",&gold))
	{
		Dbg_MsgAssert(0,("\n%s\nStartCompetition missing 'gold' parameter",pScript->GetScriptInfo()));		
	}
	
	if (!pParams->GetFloat("silver",&silver))
	{
		Dbg_MsgAssert(0,("\n%s\nStartCompetition missing 'silver' parameter",pScript->GetScriptInfo()));		
	}
		
	if (!pParams->GetFloat("bronze",&bronze))
	{
		Dbg_MsgAssert(0,("\n%s\nStartCompetition missing 'bronze' parameter",pScript->GetScriptInfo()));		
	}
	if (!pParams->GetFloat("gold_score",&gold_score))
	{
		Dbg_MsgAssert(0,("\n%s\nStartCompetition missing 'gold_score' parameter",pScript->GetScriptInfo()));		
	}
	
	if (!pParams->GetFloat("silver_score",&silver_score))
	{
		Dbg_MsgAssert(0,("\n%s\nStartCompetition missing 'silver_score' parameter",pScript->GetScriptInfo()));		
	}
		
	if (!pParams->GetFloat("bronze_score",&bronze_score))
	{
		Dbg_MsgAssert(0,("\n%s\nStartCompetition missing 'bronze_score' parameter",pScript->GetScriptInfo()));		
	}
	if (!pParams->GetFloat("bail",&bail))
	{
		Dbg_MsgAssert(0,("\n%s\nStartCompetition missing 'bail' parameter",pScript->GetScriptInfo()));		
	}
	Script::CStruct* pExtraParams = NULL;
	pParams->GetStructure( CRCD(0xa61dc9a4,"extra_params"), &pExtraParams, Script::NO_ASSERT );

	// Need to use SkaterInfoHeap, as new strings can get created, which can fragment memory 									   
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());	
	if ( pExtraParams )
		Mdl::Skate::Instance()->GetCompetition()->EditParams( pExtraParams );
	Mdl::Skate::Instance()->GetCompetition()->StartCompetition(bronze, silver, gold, bronze_score, silver_score, gold_score, bail);
	Mem::Manager::sHandle().PopContext();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StartCompetitionRun | 
bool ScriptStartCompetitionRun(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Mdl::Skate::Instance()->GetCompetition()->StartRun();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EndCompetitionRun | 
bool ScriptEndCompetitionRun(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetLocalSkater();						   
	Mdl::Score *pScore=pSkater->GetScoreObject();
	int score = pScore->GetTotalScore();


	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());
	Mdl::Skate::Instance()->GetCompetition()->EndRun(score);
	Mdl::Skate::Instance()->GetCompetition()->SetupJudgeText();
	Mem::Manager::sHandle().PopContext();
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsTopJudge | true if the specified judge is the top judge
// @uparm 1 | judge num
bool ScriptIsTopJudge(Script::CStruct *pParams, Script::CScript *pScript)
{

	int judge;
	pParams->GetInteger(NONAME,&judge);

	
	return Mdl::Skate::Instance()->GetCompetition()->IsTopJudge(judge);
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PlaceIs | true if the comp place is equal to the specified place
// @uparm 1 | place
bool ScriptPlaceIs(Script::CStruct *pParams, Script::CScript *pScript)
{
	int place;
	pParams->GetInteger(NONAME,&place);

	
	return Mdl::Skate::Instance()->GetCompetition()->PlaceIs(place);
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RoundIs | true if the round equals the specified round
// @uparm 1 | round
bool ScriptRoundIs(Script::CStruct *pParams, Script::CScript *pScript)
{
	int round;
	pParams->GetInteger(NONAME,&round);

	
	return Mdl::Skate::Instance()->GetCompetition()->RoundIs(round);
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EndCompetition | 
bool ScriptEndCompetition(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterInfoHeap());
	Mdl::Skate::Instance()->GetCompetition()->EndCompetition();
	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CompetitionEnded | true if the comp has ended
bool ScriptCompetitionEnded(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	return Mdl::Skate::Instance()->GetCompetition()->CompetitionEnded();
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StartHorse |
bool ScriptStartHorse(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Mdl::Skate::Instance()->GetHorse()->Init();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EndHorse | 
bool ScriptEndHorse(Script::CStruct *pParams, Script::CScript *pScript)
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | HorseEnded | true if horse has ended
bool ScriptHorseEnded(Script::CStruct *pParams, Script::CScript *pScript)
{   
	
	return Mdl::Skate::Instance()->GetHorse()->Ended();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | HorseStatusEquals | pass in one or more of the flags to check status
// @flag GotLetter |
// @flag TieScore |
// @flag BeatScore |
// @flag Ended |
// @flag Idle |
// @flag NoScoreSet |
// @flag Terminator |
bool ScriptHorseStatusEquals(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	return Mdl::Skate::Instance()->GetHorse()->StatusEquals( pParams );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | StartHorseRun | 
bool ScriptStartHorseRun(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Mdl::Skate::Instance()->GetHorse()->StartRun();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EndHorseRun | 
bool ScriptEndHorseRun(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Mdl::Skate::Instance()->GetHorse()->EndRun();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SwitchHorsePlayers | switches to the next valid player
bool ScriptSwitchHorsePlayers(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Mdl::Skate::Instance()->GetHorse()->SwitchPlayers();
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ApplyToHorsePanelString | 
// @parm name | whichString | the string to apply to
bool ScriptApplyToHorsePanelString(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 whichString, id;
	pParams->GetChecksum( "whichString", &whichString, true );
	pParams->GetChecksum( "id", &id, true );
	
	Str::String horseString = Mdl::Skate::Instance()->GetHorse()->GetString( whichString );

	Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkaterById( Mdl::Skate::Instance()->GetHorse()->GetCurrentSkaterId() );
	Dbg_Assert( pSkater );
	
	Script::CStruct* pTempStructure = new Script::CStruct;
	*pTempStructure+=*pParams;
	pTempStructure->AddString( "text", horseString.getString() );
	pTempStructure->AddChecksum( "id", id );
	Script::RunScript( "create_horse_panel_message", pTempStructure, pSkater );
	delete pTempStructure;
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// @parm name | whichString | the string to apply to
// @parmopt int | skater | 0 | the skater we're applying to (default is 0)
bool ScriptGetHorseString(Script::CStruct *pParams, Script::CScript *pScript)
{
	uint32 whichString;
	Script::CStruct* p_return_parms;
	pParams->GetChecksum( "whichString", &whichString, true );

	Str::String horseString = Mdl::Skate::Instance()->GetHorse()->GetString( whichString );

	p_return_parms = pScript->GetParams();
	p_return_parms->AddString( "text", horseString.getString());

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | IsCurrentHorseSkater | true if the current horse skater
// is the same as the specified skater id
// @uparm 1 | skater id
bool ScriptIsCurrentHorseSkater(Script::CStruct *pParams, Script::CScript *pScript)
{
	int skaterId;
	if( pParams->GetInteger( NONAME, &skaterId, false ) == false )
	{
		pParams->GetChecksum( NONAME, (uint32*) &skaterId, true );
	}
	
	
	return ( Mdl::Skate::Instance()->GetHorse()->GetCurrentSkaterId() == skaterId );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ApplyToSkaterProfile | 
// @parmopt int | skater | 0 | the skater we're applying to (default is 0)
// @parmopt name | field_id | | the field to change
// @parmopt name | slider_id | | if the value is specified in a slider
// @parmopt name | keyboard_id | | value specified in a string
// @parmopt name | value | | specific value (in the absence of slider or keyboard)
bool ScriptApplyToSkaterProfile(Script::CStruct *pParams, Script::CScript *pScript)
{   
	/*
	Obj::CSkaterProfile* pProfile = NULL;

	// look for the specified skater (0 by default)
	int skater;


	if ( pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skater ) )
	{
		// get the specified skater profile
		pProfile = Mdl::Skate::Instance()->GetProfile( skater );
	}
	else
	{
		// look up the current skater profile
		pProfile = Mdl::Skate::Instance()->GetCurrentProfile( pParams );
	}

	Dbg_MsgAssert( pProfile,( "No profile found" ));

	uint32 field_id;
	uint32 checksumValue;
	uint32 slider_id;
	uint32 keyboard_id;
	pParams->GetChecksum( "field_id", &field_id, true );

	// the value will be in one of two places...
	if ( pParams->GetChecksum( "slider_id", &slider_id ) )
	{
		// de-reference the specified slider_id
		Front::MenuFactory* menu_factory = Front::MenuFactory::Instance();
		Front::SliderMenuElement* pSliderElement = static_cast( menu_factory->GetMenuElement( slider_id, true ) );
		uint32 value = pSliderElement->GetValue();
		pProfile->SetSkaterProperty( field_id, value );
	}
	else if ( pParams->GetChecksum( "keyboard_id", &keyboard_id ) )
	{
		// de-reference the specified slider_id
		Front::MenuFactory* menu_factory = Front::MenuFactory::Instance();
		Front::KeyboardControl* pKeyboardControl = static_cast( menu_factory->GetMenuElement( keyboard_id, true ) );
		Str::String myString = pKeyboardControl->GetString();

		if ( strlen( myString.getString() ) )
		{
			printf("Setting property %s to %s\n", Script::FindChecksumName(field_id), myString.getString() );
			pProfile->SetSkaterProperty( field_id, myString.getString() );
		}
		else
		{
			printf("No string supplied.  Ignoring change to %s\n", Script::FindChecksumName(field_id) );
		}
	}
	else
	{
		// look for checksum value
		pParams->GetChecksum( "value", &checksumValue, true );
		printf("Setting %s\n", Script::FindChecksumName( checksumValue ));
		pProfile->SetSkaterProperty( field_id, checksumValue );
	}
	*/
		
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RefreshSkaterColors | this just synchs up the on-screen 
// skater with the color in the skater profile, in case it's changed
bool ScriptRefreshSkaterColors(Script::CStruct *pParams, Script::CScript *pScript)
{
	int profile = 0;
	int skater = 0;
	
	pParams->GetInteger( CRCD(0x7ea855f0,"profile"), &profile, Script::ASSERT );
	pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skater, Script::ASSERT );
//	Dbg_Printf( "Skater %d  Profile %d\n", skater, profile );
	Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkater(skater);
	Dbg_Assert( pSkater );

	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile( profile );
	Dbg_Assert( pProfile );
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();
	
	Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pSkater );
	pModelComponent->RefreshModel( pAppearance, Gfx::vCHECKSUM_COLOR_MODEL_FROM_APPEARANCE );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RefreshSkaterScale | this just synchs up the on-screen 
// skater with the scale in the skater profile, in case it's changed
bool ScriptRefreshSkaterScale(Script::CStruct *pParams, Script::CScript *pScript)
{
	int profile = 0;
	int skater = 0;
	
	pParams->GetInteger( CRCD(0x7ea855f0,"profile"), &profile, Script::ASSERT );
	pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skater, Script::ASSERT );
//	Dbg_Printf( "Skater %d  Profile %d\n", skater, profile );
	Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkater(skater);
	Dbg_Assert( pSkater );

	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile( profile );
	Dbg_Assert( pProfile );
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();
	
	Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pSkater );
	pModelComponent->RefreshModel( pAppearance, Gfx::vCHECKSUM_SCALE_MODEL_FROM_APPEARANCE );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RefreshSkaterVisibility | this just synchs up the on-screen 
// skater with the hidegeoms in the skater profile, in case it's changed
// (such as in the invisible man cheat
bool ScriptRefreshSkaterVisibility(Script::CStruct *pParams, Script::CScript *pScript)
{
	int profile = 0;
	int skater = 0;
	
	pParams->GetInteger( CRCD(0x7ea855f0,"profile"), &profile, Script::ASSERT );
	pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skater, Script::ASSERT );
//	Dbg_Printf( "Skater %d  Profile %d\n", skater, profile );
	Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkater(skater);
	Dbg_Assert( pSkater );

	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile( profile );
	Dbg_Assert( pProfile );
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();
	
	Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pSkater );
	pModelComponent->RefreshModel( pAppearance, Gfx::vCHECKSUM_HIDE_MODEL_FROM_APPEARANCE );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RefreshSkaterUV | this just synchs up the on-screen 
// skater with the UVs in the skater profile, in case it's changed
bool ScriptRefreshSkaterUV(Script::CStruct *pParams, Script::CScript *pScript)
{
	int profile = 0;
	int skater = 0;
	
	pParams->GetInteger( CRCD(0x7ea855f0,"profile"), &profile, Script::ASSERT );
	pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skater, Script::ASSERT );
//	Dbg_Printf( "Skater %d  Profile %d\n", skater, profile );
	Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkater(skater);
	Dbg_Assert( pSkater );

	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile( profile );
	Dbg_Assert( pProfile );
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();
	
	Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pSkater );
	pModelComponent->RefreshModel( pAppearance, CRCD(0x91d6261,"set_uv_from_appearance") );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AwardStatPoint | awards a stat point to the current skater
// @uparmopt 1 | increment value (can be negative)
bool ScriptAwardStatPoint(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile();

	int inc_val = 1;
	pParams->GetInteger( NONAME, &inc_val, Script::NO_ASSERT );

	pSkaterProfile->AwardStatPoint( inc_val );
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | AwardSpecialTrickSlot | awards a new special trick slot to the 
// current skater profile
// @flag all | award a trickslot to all profiles
bool ScriptAwardSpecialTrickSlot(Script::CStruct *pParams, Script::CScript *pScript)
{
	Obj::CPlayerProfileManager* pPlayerProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	if ( pParams->ContainsFlag( "all" ) )
	{
		int num_profiles = pPlayerProfileManager->GetNumProfileTemplates();
		for ( int i = 0; i < num_profiles; i++ )
		{
			Obj::CSkaterProfile* pTempProfile = pPlayerProfileManager->GetProfileTemplateByIndex( i );
			pTempProfile->AwardSpecialTrickSlot();
		}
	}
	else
	{
		Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetCurrentProfile();
		pSkaterProfile->AwardSpecialTrickSlot();
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UpdateSkaterStats | 
bool ScriptUpdateSkaterStats(Script::CStruct *pParams, Script::CScript *pScript)
{
	int player;
	pParams->GetInteger(CRCD(0x67e6859a,"player"), &player, Script::ASSERT);	
		
	Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetSkater(player);
	Dbg_Assert(pSkater);

	Obj::CSkaterProfile* pSkaterProfile = Mdl::Skate::Instance()->GetProfile(player);
	pSkater->UpdateStats(pSkaterProfile);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UpdateInitials | 
bool ScriptUpdateInitials( Script::CStruct *pParams, Script::CScript *pScript )
{   
	
	int level = Mdl::Skate::Instance()->GetCareer()->GetLevel();
	Records::CGameRecords *pGameRecords=Mdl::Skate::Instance()->GetGameRecords();	
	pGameRecords->GetLevelRecords(level)->UpdateInitials(pGameRecords->GetDefaultInitials());
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | NewRecord | return true if the last call to UpdateRecords made
// there be a new record
bool ScriptNewRecord(  Script::CStruct *pParams, Script::CScript *pScript )
{      
	return Mdl::Skate::Instance()->m_new_record;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | TrickOffAllObjects | 
// @uparmopt 0 | skater id (0 is default)
bool ScriptTrickOffAllObjects( Script::CStruct *pParams, Script::CScript *pScript )
{
	// a cheat for getting all the graffiti objects

	if (!Config::CD())
	{	
		int skater_id = 0;
		pParams->GetInteger( NONAME, &skater_id );
		Mdl::Skate::Instance()->GetTrickObjectManager()->TrickOffAllObjects( skater_id );
	}	
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetScoreDegredation | 
// @uparm 1 | should degrade amount
bool ScriptGameModeSetScoreDegradation( Script::CStruct* pParams, Script::CScript* pScript )
{
    
	
	int should_degrade = 1;

	pParams->GetInteger( NONAME, &should_degrade );

	Mdl::Skate::Instance()->GetGameMode()->SetScoreDegradation( should_degrade );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetScoreAccumulation | 
// @uparm 1 | should accumulate amount
bool ScriptGameModeSetScoreAccumulation( Script::CStruct* pParams, Script::CScript* pScript )
{
    
	
	int should_accumulate = 1;

	pParams->GetInteger( NONAME, &should_accumulate );

	Mdl::Skate::Instance()->GetGameMode()->SetScoreAccumulation( should_accumulate );

	if ( should_accumulate == 0 && pParams->ContainsFlag( "freeze_score" ) )
	{
		Mdl::Skate::Instance()->GetGameMode()->SetScoreFrozen( true );
	}
	else 
	{
		Mdl::Skate::Instance()->GetGameMode()->SetScoreFrozen( false );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetScore |
bool ScriptResetScore(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	
	Dbg_Printf( "******** RESET SCORE\n" );

    for (uint32 i=0;iGetNumSkaters();i++)
	{
		Obj::CSkater* p_Skater = skate_mod->GetSkater( i );
		if ( p_Skater->IsLocalClient() )
		{
			Mdl::Score * p_Score = ( p_Skater->GetScoreObject() );
			Dbg_Assert( p_Score );

			p_Score->Reset();
			if( ( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "netking" )) ||
				( skate_mod->GetGameMode()->GetNameChecksum() == Script::GenerateCRC( "king" )))
			{
				Front::SetScoreTHPS4( "0:00", p_Skater->GetHeapIndex());
			}
			else
			{
				Front::SetScoreTHPS4( "0", p_Skater->GetHeapIndex());
			}
			
		}
	}
	
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UpdateScore |
bool ScriptUpdateScore(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	
    for (uint32 i=0;iGetNumSkaters();i++)
	{
		Obj::CSkater* p_Skater = skate_mod->GetSkater( i );
		if ( p_Skater->IsLocalClient() )
		{
			Mdl::Score * p_Score = ( p_Skater->GetScoreObject() );
			Dbg_Assert( p_Score );

			p_Score->SetTotalScore( p_Score->GetTotalScore());
		}
	}
	
    return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetScoreDegradation | doesn't seem to do anything...
bool ScriptResetScoreDegradation( Script::CStruct* pParams, Script::CScript* pScript )
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SkaterIsBraking | returns true if the local skater is braking
bool ScriptSkaterIsBraking( Script::CStruct* pParams, Script::CScript* pScript )
{
    
    Obj::CCompositeObject *pSkater = Mdl::Skate::Instance()->GetLocalSkater();						   
    Dbg_Assert( pSkater );
    return pSkater->CallMemberFunction(CRCD(0x1f8bbd05, "Braking"), pParams, pScript);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LocalSkaterExists | returns true if the local skater exists
bool ScriptLocalSkaterExists( Script::CStruct* pParams, Script::CScript* pScript )
{
	
    Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetLocalSkater();						   
    
    return ( pSkater != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | InitSkaterModel | This will destroy the specified
// skater's model and replace it with the specified model or
// profile
bool ScriptInitSkaterModel(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	int player_num = 0;
	pParams->GetInteger( NONAME, &player_num );
	Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkater(player_num);
	Dbg_Assert( pSkater );

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterGeomHeap(pSkater->GetHeapIndex()));
   	
	uint32 appearance_structure_name;
	pParams->GetChecksum( "default_appearance", &appearance_structure_name, true );
//	Script::PrintContents( pParams );

	// create the model
	Gfx::CModelAppearance theAppearance;
	theAppearance.Load( appearance_structure_name );

	Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pSkater );
	pModelComponent->InitModelFromProfile( &theAppearance, false, pSkater->GetHeapIndex() );
	
	Mem::Manager::sHandle().PopContext();

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | RefreshSkaterModel | This will destroy the specified
// skater's model and replace it with the specified model or
// profile
// If there is a composite object called "bailboard" that will be destroyed
bool ScriptRefreshSkaterModel(Script::CStruct *pParams, Script::CScript *pScript)
{	
	int profile = 0;
	int skater = 0;
	int no_name = 0;
	
	
	
	pParams->GetInteger( NONAME, &no_name );
	profile = no_name;
	skater = no_name;
	pParams->GetInteger( CRCD(0x7ea855f0,"profile"), &profile );
	pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skater );
	Dbg_Printf( "Skater %d  Profile %d\n", skater, profile );
	Obj::CSkater* pSkater = Mdl::Skate::Instance()->GetSkater(skater);
	Dbg_MsgAssert( pSkater, ( "Couldn't find specified skater" ) );


	// (Mick) Now check for bailboard, and kill it
	// Bit of a patch really, but a fairly safe high level one.
	// (need a more general solution for killing model that has instances...)
	if (pSkater->GetSkaterNumber() == 0)	// Only need it for single player
	{
		Obj::CCompositeObject*p_bailboard = (Obj::CCompositeObject*) Obj::CCompositeObjectManager::Instance()->GetObjectByID(CRCD(0x884ef81b,"bailboard"));
		if (p_bailboard)
		{
			p_bailboard->MarkAsDead();
			Obj::CCompositeObjectManager::Instance()->FlushDeadObjects();
		}
	}

	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile( profile );
	Dbg_Assert( pProfile );
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();
	
	// GJ:  rebuilding the face and texture takes
	// a while, causes Pcm audio "panic" assert...
	// just to be safe, i'll pause the music while
	// the model is being rebuilt.
	/*bool should_pause_music = !Pcm::MusicIsPaused();
	
	if ( should_pause_music )
	{
		Pcm::PauseMusic( true );
	}*/

	// create the model
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterGeomHeap(pSkater->GetHeapIndex()));

	Nx::CEngine::sFinishRendering();
   
#ifdef __PLAT_NGC__
	File::PreMgr* pre_mgr = File::PreMgr::Instance();
	bool loaded = pre_mgr->InPre( "skaterparts.pre");
	if ( !loaded )
	{
		pre_mgr->LoadPre( "skaterparts.pre", false);
	}
#endif
	Obj::CModelComponent* pModelComponent = GetModelComponentFromObject( pSkater );
	pModelComponent->InitModelFromProfile( pAppearance, false, pSkater->GetHeapIndex() );
#ifdef __PLAT_NGC__
	if ( !loaded )
	{
		pre_mgr->UnloadPre( "skaterparts.pre");
	}
#endif
	
	Mem::Manager::sHandle().PopContext();

	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().SkaterHeap(pSkater->GetHeapIndex()));
	
	pSkater->UpdateStats( pProfile );

	Obj::CTrickComponent* pTrickComponent = GetTrickComponentFromObject(pSkater);
	Dbg_Assert(pTrickComponent);
	pTrickComponent->UpdateTrickMappings( pProfile );

	pSkater->UpdateSkaterInfo( pProfile );

	Mem::Manager::sHandle().PopContext();

    uint32 board;
    if ( pParams->GetChecksum( CRCD(0x41eec86b,"no_board"), &board ) )
    {
        // if Refreshing the skater in the CAS level,
        // Hide the board
        //Mdl::Skate * skate_mod =  Mdl::Skate::Instance();
        //if ( skate_mod->m_cur_level == CRCD(0xa368b4f1,"load_cas") )
        //{
            // turn off board
            Obj::CModelComponent* pModelComponent = GetModelComponentFromObject(pSkater);
            pModelComponent->HideGeom(CRCD(0xa7a9d4b8,"board"), true, true);

			// GJ THPS5: Kludge to fix TT12405: CAS - board 
			// appears when adjusting leg tattoos
			// generally, this function will be called from a spawned script,
			// which gets called after the composite objects are updated
			// for the frame...  because of this, it will take 1 frame
			// for the hide flag to take effect, unless we explicitly update
			// the model component here
            pModelComponent->Update();
        //}
    }
    
	/*if ( should_pause_music )
	{
		// unpause the music
		Pcm::PauseMusic( false );
	}*/
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | EditPlayerAppearance | This runs an object script on the specified
// appearance structure
// @parm name | target | name of script that gets run on the player's appearance
// @parmopt structure | targetParams | | extra parameters sent to the target script
bool ScriptEditPlayerAppearance(Script::CStruct *pParams, Script::CScript *pScript)
{	
	int profile_num = 0;
	int no_name = 0;
	
	pParams->GetInteger( NONAME, &no_name );
	profile_num = no_name;
	pParams->GetInteger( CRCD(0x7ea855f0,"profile"), &profile_num );
	Dbg_Printf( "Profile %d\n", profile_num );

	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile( profile_num );
	Dbg_Assert( pProfile );
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();
	   	
	uint32 target;
	pParams->GetChecksum(CRCD(0xb990d003,"target"), &target, Script::ASSERT);

	Script::CStruct* pTargetParams = NULL;
	pParams->GetStructure(CRCD(0x46a2869c,"targetParams"), &pTargetParams, Script::NO_ASSERT);

	// runs an object script on the specified appearance structure
	Script::RunScript(target, pTargetParams, pAppearance);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetCurrentSkaterProfileIndex | This returns the currently selected
// profile (either player 0 or player 1), presumably for use by other script functions
// @parm int | currentSkaterProfileIndex | index of currently selected profile (return value)
bool ScriptGetCurrentSkaterProfileIndex(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Obj::CPlayerProfileManager*	pPlayerProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	int index = pPlayerProfileManager->GetCurrentProfileIndex();
	pScript->GetParams()->AddInteger(CRCD(0x3c64476b,"currentSkaterProfileIndex"), index);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetCustomSkaterName | 
bool ScriptGetCustomSkaterName(Script::CStruct *pParams, Script::CScript *pScript)
{
	
	Obj::CPlayerProfileManager*	pPlayerProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	const char* name = pPlayerProfileManager->GetProfileTemplate(CRCD(0xa7be964,"custom"))->GetDisplayName();
	pScript->GetParams()->AddString(CRCD(0x2ab66cb8,"display_name"), name);

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetScorePot | this will reset the local skater's current score pot.
// This currently just calls the Bail member func of the score object...
bool ScriptResetScorePot(Script::CScriptStructure *pParams, Script::CScript *pScript)
{
	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	
	bool useBailStyle = pParams->ContainsFlag(CRCD(0x929eb4d5, "UseBailStyle"));
	
    for (uint32 i=0;iGetNumSkaters();i++)
	{
		Obj::CSkater* p_Skater = skate_mod->GetSkater( i );
		if ( p_Skater->IsLocalClient() )
		{   
			Mdl::Score *pScore=p_Skater->GetScoreObject();

			Dbg_Assert( pScore );
			pScore->Bail(!useBailStyle);
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptPrintSkaterStats | prints the specified skater's stats
bool ScriptPrintSkaterStats( Script::CStruct* pParams, Script::CScript* pScript )
{
#ifdef __NOPT_ASSERT__
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
    for (uint32 i=0;iGetNumSkaters();i++)
	{
		printf("Skater %d stats:\n", i);

		Obj::CSkater* pSkater = skate_mod->GetSkater( i );
		
		printf("Air    %2.2f\n",pSkater->GetStat(Obj::CSkater::STATS_AIR));		
		printf("Run    %2.2f\n",pSkater->GetStat(Obj::CSkater::STATS_RUN));		
		printf("Ollie  %2.2f\n",pSkater->GetStat(Obj::CSkater::STATS_OLLIE));		
		printf("Speed  %2.2f\n",pSkater->GetStat(Obj::CSkater::STATS_SPEED));		
		printf("Spin   %2.2f\n",pSkater->GetStat(Obj::CSkater::STATS_SPIN));		
		printf("Flip   %2.2f\n",pSkater->GetStat(Obj::CSkater::STATS_FLIPSPEED));		
		printf("Switch %2.2f\n",pSkater->GetStat(Obj::CSkater::STATS_SWITCH));		
		printf("Rail   %2.2f\n",pSkater->GetStat(Obj::CSkater::STATS_RAILBALANCE));		
		printf("Lip    %2.2f\n",pSkater->GetStat(Obj::CSkater::STATS_LIPBALANCE));		
		printf("Manual %2.2f\n",pSkater->GetStat(Obj::CSkater::STATS_MANUAL));		
	}
#endif		// __NOPT_ASSERT__

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ScriptPrintSkaterStats | prints the specified skater's stats
bool ScriptPrintSkaterStats2( Script::CStruct* pParams, Script::CScript* pScript )
{
#ifdef __NOPT_ASSERT__
	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
    for (uint32 i=0;iGetNumSkaters();i++)
	{
		printf("Skater %d stats:\n", i);

        Obj::CPlayerProfileManager* pProfileManager = skate_mod->GetPlayerProfileManager();
        Obj::CSkaterProfile* pProfile = pProfileManager->GetProfile( i );
		
		printf("Air    %i\n",pProfile->GetStatValue(CRCD(0x439f4704,"AIR")));		
		printf("Run    %i\n",pProfile->GetStatValue(CRCD(0xaf895b3f,"RUN")));		
		printf("Ollie  %i\n",pProfile->GetStatValue(CRCD(0x9b65d7b8,"OLLIE")));		
		printf("Speed  %i\n",pProfile->GetStatValue(CRCD(0xf0d90109,"SPEED")));		
		printf("Spin   %i\n",pProfile->GetStatValue(CRCD(0xedf5db70,"SPIN")));		
		printf("Flip   %i\n",pProfile->GetStatValue(CRCD(0x6dcb497c,"FLIP_SPEED")));		
		printf("Switch %i\n",pProfile->GetStatValue(CRCD(0x9016b4e7,"SWITCH")));		
		printf("Rail   %i\n",pProfile->GetStatValue(CRCD(0xf73a13e3,"RAIL_BALANCE")));		
		printf("Lip    %i\n",pProfile->GetStatValue(CRCD(0xae798769,"LIP_BALANCE")));		
		printf("Manual %i\n",pProfile->GetStatValue(CRCD(0xb1fc0722,"MANUAL_BALANCE")));		
	}
#endif		// __NOPT_ASSERT__

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | PrintSkaterPosition | prints the local skater's current position
bool ScriptPrintSkaterPosition( Script::CStruct* pParams, Script::CScript* pScript )
{
#ifdef __NOPT_ASSERT__
	Obj::CSkater *pSkater = Mdl::Skate::Instance()->GetLocalSkater();
	
	Mth::Vector pos = pSkater->GetPos();
	float x = pos.GetX();
	float y = pos.GetY();
	float z = pos.GetZ();

	Dbg_Message("(%f, %f, %f)", x, y, z);
#endif		// __NOPT_ASSERT__

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetSkaterPosition | prints the local skater's current position
bool ScriptGetSkaterPosition( Script::CStruct* pParams, Script::CScript* pScript )
{

    Obj::CSkater* pSkater = static_cast< Obj::CSkater* >(pScript->mpObject.Convert());
	
    if (pSkater) 
    {
    	Mth::Vector pos = pSkater->GetPos();
    	float x = pos.GetX();
    	float y = pos.GetY();
    	float z = pos.GetZ();
    
        pScript->GetParams()->AddInteger("x", x);
        pScript->GetParams()->AddInteger("y", y);
        pScript->GetParams()->AddInteger("z", z);
    
        return true;
    }
    
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetSkaterPosition | prints the local skater's current position
bool ScriptGetSkaterVelocity( Script::CStruct* pParams, Script::CScript* pScript )
{

    Obj::CSkater* pSkater = static_cast< Obj::CSkater* >(pScript->mpObject.Convert());
	
    if (pSkater) 
    {
    	Mth::Vector vel = pSkater->GetVel();
    	float x = vel.GetX();
    	float y = vel.GetY();
    	float z = vel.GetZ();
		float scale = 1.0f;
		float skew_angle = 0.0f;
    
		
		pParams->GetFloat( CRCD(0x5582fe27,"skew_angle"), &skew_angle );

        pScript->GetParams()->AddInteger(CRCD(0x6187906d,"vel_x"), x);
        pScript->GetParams()->AddInteger(CRCD(0x1680a0fb,"vel_y"), y);
        pScript->GetParams()->AddInteger(CRCD(0x8f89f141,"vel_z"), z);

		pParams->GetFloat( CRCD(0x13b9da7b,"scale"), &scale );

		vel[Y] = 0.0f;

		if( vel.Length() > 100.0f )
		{
			vel.Normalize();

			if( skew_angle != 0 )
			{
				Mth::Matrix rot_mat;
	
				rot_mat.Ident();
				rot_mat.RotateY( Mth::DegToRad( skew_angle ));
	
				vel = rot_mat.Transform( vel );
			}

			pScript->GetParams()->AddInteger("scaled_x", vel[X] * scale );
			pScript->GetParams()->AddInteger("scaled_y", vel[Y] * scale );
			pScript->GetParams()->AddInteger("scaled_z", vel[Z] * scale );
		}
		else
		{
			Mth::Vector facing;

			facing = pSkater->GetMatrix()[Mth::AT];
	
			if( skew_angle != 0 )
			{
				Mth::Matrix rot_mat;
	
				rot_mat = pSkater->GetMatrix();
				rot_mat.RotateYLocal( Mth::DegToRad( skew_angle ));
	
				facing = rot_mat[Mth::AT];
			}

			pScript->GetParams()->AddInteger(CRCD(0xbded7a76,"scaled_x"), facing[X] * scale );
			pScript->GetParams()->AddInteger(CRCD(0xcaea4ae0,"scaled_y"), facing[Y] * scale );
			pScript->GetParams()->AddInteger(CRCD(0x53e31b5a,"scaled_z"), facing[Z] * scale );
		}
    
        return true;
    }
    
    return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetStatValue | this returns the specified stat value in
// the script's params as stat_value
// @uparm STATS_AIR | global int value of the stat (defined in physics.q)
// @parmopt name | skater | | the name of the profile you wish to get.  If you don't
// provide a name, the current skater's profile will be returned
bool ScriptGetStatValue( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 stat;
	pParams->GetChecksum( NONAME, &stat, Script::ASSERT );

	uint32 name;
	
	Obj::CPlayerProfileManager* pPlayerProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	Obj::CSkaterProfile* pSkaterProfile = NULL;
	if ( pParams->GetChecksum( CRCD(0x5b8ab877,"skater"), &name, Script::NO_ASSERT ) )
		pSkaterProfile = pPlayerProfileManager->GetProfileTemplate( name );
	else
		pSkaterProfile = pPlayerProfileManager->GetCurrentProfile();
	
	Dbg_MsgAssert( pSkaterProfile, ( "Unable to get skater profile") );
	int value = pSkaterProfile->GetStatValue( stat );
	pScript->GetParams()->AddInteger( "stat_value", value );

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetNumStatPointsAvailable | returns the number in the scripts
// params as points_available
// @parm int | player | the player number to check 
bool ScriptGetNumStatPointsAvailable( Script::CStruct* pParams, Script::CScript* pScript )
{
	int player;
	pParams->GetInteger( CRCD(0x67e6859a,"player"), &player, Script::ASSERT );

	
	Obj::CPlayerProfileManager* pProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	Obj::CSkaterProfile* pProfile = pProfileManager->GetProfile( player );
	int num = pProfile->GetNumStatPointsAvailable();
	pScript->GetParams()->AddInteger( "points_available", num );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UnlockSkater | this makes locked characters accessible
// to the player
// @parm name | skater | the name of the locked character to unlock
bool ScriptUnlockSkater( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 name;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &name, Script::ASSERT );

	Obj::CPlayerProfileManager* pPlayerProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
	Obj::CSkaterProfile* pSkaterProfile = pPlayerProfileManager->GetProfileTemplate( name );
	Dbg_MsgAssert( pSkaterProfile, ( "Unable to get skater profile") );

	int isLocked = 1;
	pSkaterProfile->GetInfo()->GetInteger( "is_hidden", &isLocked, Script::ASSERT );
	if ( isLocked )
	{
#ifdef __NOPT_ASSERT__
		// don't need to sync it up to the current skater profiles,
		// because, theoretically, the player won't be able to
		// choose him through the front end...
		Obj::CSkaterProfile* pCurrentSkaterProfile = pPlayerProfileManager->GetCurrentProfile();
		Dbg_MsgAssert( pCurrentSkaterProfile->GetSkaterNameChecksum() != name, 
					   ( "Locked skaters aren't supposed to be currently selected!", 
						 Script::FindChecksumName(name) ) );
#endif
		Dbg_Message( "%s was locked\n", Script::FindChecksumName(name) );
		pSkaterProfile->GetInfo()->AddInteger( "is_hidden", 0 );
	}
	else
	{
		Dbg_Message( "%s was not locked\n", Script::FindChecksumName(name) );
	}
		
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetActualCASOptionStruct( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 partChecksum;
	pParams->GetChecksum(CRCD(0xb6f08f39,"part"), &partChecksum, Script::ASSERT);
	
	uint32 descChecksum;
	pParams->GetChecksum(CRCD(0x4bb2084e,"desc_id"), &descChecksum, Script::ASSERT);

	Script::CStruct* pActualStruct = Cas::GetOptionStructure( partChecksum, descChecksum );

	pScript->GetParams()->AppendStructure( pActualStruct );

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetActualPlayerAppearancePart( Script::CStruct* pParams, Script::CScript* pScript )
{
	int player_num = 0;
	pParams->GetInteger(CRCD(0x67e6859a,"player"), &player_num);
	
	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile(player_num);
	Dbg_Assert(pProfile);
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();

	uint32 partChecksum;
	if ( pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &partChecksum, Script::NO_ASSERT ) )
	{
		Script::CStruct* pVirtualStruct;
		pVirtualStruct = pAppearance->GetActualDescStructure( partChecksum );
		if ( pVirtualStruct )
		{
			pScript->GetParams()->AppendStructure( pVirtualStruct );
			return true;
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetPlayerAppearancePart( Script::CStruct* pParams, Script::CScript* pScript )
{	
	int player_num = 0;
	pParams->GetInteger(CRCD(0x67e6859a,"player"), &player_num);
	
	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile(player_num);
	Dbg_Assert(pProfile);
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();

	uint32 partChecksum;
	if ( pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &partChecksum, Script::NO_ASSERT ) )
	{
		Script::CStruct* pVirtualStruct;
		pVirtualStruct = pAppearance->GetVirtualDescStructure( partChecksum );
		if ( pVirtualStruct )
		{
			pScript->GetParams()->AppendStructure( pVirtualStruct );
			return true;
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSetPlayerAppearanceColor( Script::CStruct* pParams, Script::CScript* pScript )
{
	int player_num = 0;
	pParams->GetInteger(CRCD(0x67e6859a,"player"), &player_num);
	
	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile(player_num);
	Dbg_Assert(pProfile);
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();
	Dbg_Assert( pAppearance );

	uint32 partChecksum;
	pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &partChecksum, Script::ASSERT );

	Script::CStruct* pVirtualStruct;
	pVirtualStruct = pAppearance->GetVirtualDescStructure( partChecksum );

	if ( pVirtualStruct )
	{
		float h, s, v;
		int useDefaultHSV;
		pParams->GetFloat( CRCD(0x6e94f918,"h"), &h, Script::ASSERT );
		pParams->GetFloat( CRCD(0xe4f130f4,"s"), &s, Script::ASSERT );
		pParams->GetFloat( CRCD(0x949bc47b,"v"), &v, Script::ASSERT );
		pParams->GetInteger( CRCD(0x97dbdde6,"use_default_hsv"), &useDefaultHSV, Script::ASSERT );

		pVirtualStruct->AddInteger( CRCD(0x6e94f918,"h"), (int)h );
		pVirtualStruct->AddInteger( CRCD(0xe4f130f4,"s"), (int)s );
		pVirtualStruct->AddInteger( CRCD(0x949bc47b,"v"), (int)v );
		pVirtualStruct->AddInteger( CRCD(0x97dbdde6,"use_default_hsv"), useDefaultHSV );
	}
	else
	{
		Dbg_Message( "Nothing to color...  (appearance doesn't contain %s)", Script::FindChecksumName(partChecksum) );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSetPlayerAppearanceScale( Script::CStruct* pParams, Script::CScript* pScript )
{
	int player_num = 0;
	pParams->GetInteger(CRCD(0x67e6859a,"player"), &player_num);
	
	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile(player_num);
	Dbg_Assert(pProfile);
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();
	Dbg_Assert( pAppearance );

	uint32 partChecksum;
	pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &partChecksum, Script::ASSERT );

	Script::CStruct* pVirtualStruct;
	pVirtualStruct = pAppearance->GetVirtualDescStructure( partChecksum );

	if ( !pVirtualStruct )
	{
		pVirtualStruct = new Script::CStruct;
		Script::CStruct* pAppearanceStruct = pAppearance->GetStructure();
		pAppearanceStruct->AddStructurePointer( partChecksum, pVirtualStruct );
	}

	float x;
	if ( pParams->GetFloat( CRCD(0x7323e97c,"x"), &x, Script::NO_ASSERT ) )
	{
		pVirtualStruct->AddInteger( CRCD(0x7323e97c,"x"), (int)x );
	}
	
	float y;
	if ( pParams->GetFloat( CRCD(0x424d9ea,"y"), &y, Script::NO_ASSERT ) )
	{
		pVirtualStruct->AddInteger( CRCD(0x424d9ea,"y"), (int)y );
	}
	
	float z;
	if ( pParams->GetFloat( CRCD(0x9d2d8850,"z"), &z, Script::NO_ASSERT ) )
	{
		pVirtualStruct->AddInteger( CRCD(0x9d2d8850,"z"), (int)z );
	}
	
	int useDefaultScale;
	if ( pParams->GetInteger( CRCD(0x5a96985d,"use_default_scale"), &useDefaultScale, Script::NO_ASSERT ) )
	{
		pVirtualStruct->AddInteger( CRCD(0x5a96985d,"use_default_scale"), useDefaultScale );
	}	

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptSetPlayerAppearanceUV( Script::CStruct* pParams, Script::CScript* pScript )
{
	int player_num = 0;
	pParams->GetInteger(CRCD(0x67e6859a,"player"), &player_num);
	
	Obj::CSkaterProfile* pProfile = Mdl::Skate::Instance()->GetProfile(player_num);
	Dbg_Assert(pProfile);
    Gfx::CModelAppearance* pAppearance = pProfile->GetAppearance();
	Dbg_Assert( pAppearance );

	uint32 partChecksum;
	pParams->GetChecksum( CRCD(0xb6f08f39,"part"), &partChecksum, Script::ASSERT );

	Script::CStruct* pVirtualStruct;
	pVirtualStruct = pAppearance->GetVirtualDescStructure( partChecksum );

	if ( pVirtualStruct )
	{
		float u, v, uv_scale, uv_rot;
		int useDefaultUV;
		pParams->GetFloat( CRCD(0xcf6aa087,"uv_u"), &u, Script::ASSERT );
		pParams->GetFloat( CRCD(0x5663f13d,"uv_v"), &v, Script::ASSERT );
		pParams->GetFloat( CRCD(0x266932c8,"uv_scale"), &uv_scale, Script::ASSERT );
		pParams->GetFloat( CRCD(0x1256b6c6,"uv_rot"), &uv_rot, Script::ASSERT );
		pParams->GetInteger( CRCD(0x8602f6ee,"use_default_uv"), &useDefaultUV, Script::ASSERT );
				   
		pVirtualStruct->AddFloat( CRCD(0xcf6aa087,"uv_u"), u );
		pVirtualStruct->AddFloat( CRCD(0x5663f13d,"uv_v"), v );
		pVirtualStruct->AddFloat( CRCD(0x266932c8,"uv_scale"), uv_scale );
		pVirtualStruct->AddFloat( CRCD(0x1256b6c6,"uv_rot"), uv_rot );
		pVirtualStruct->AddInteger( CRCD(0x8602f6ee,"use_default_uv"), useDefaultUV );
	}
	else
	{
		Dbg_Message( "Nothing to wibble...  (appearance doesn't contain %s yet?)", Script::FindChecksumName(partChecksum) );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* GetNextUndisqualified( Script::CArray* pPartArray, int startIndex )
{
	Dbg_Assert( pPartArray );

	startIndex++;
	if ( startIndex >= (int)pPartArray->GetSize() )
	{
		startIndex -= pPartArray->GetSize();
	}

	for ( int i = 0; i < (int)pPartArray->GetSize(); i++ )
	{
		int currIndex = (i + startIndex) % pPartArray->GetSize();

		Script::CStruct* pCurrStruct = pPartArray->GetStructure( currIndex );
		
		int disqualified;
		pCurrStruct->GetInteger( "disqualified", &disqualified, true );
		if ( !disqualified )
		{
			return pCurrStruct;
		}
	}

	// none found...
	return pPartArray->GetStructure( startIndex );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Script::CStruct* GetPrevUndisqualified( Script::CArray* pPartArray, int startIndex )
{
	Dbg_Assert( pPartArray );

	startIndex--;
	if ( startIndex < 0 )
	{
		startIndex += pPartArray->GetSize();
	}

	for ( int i = (int)pPartArray->GetSize(); i >= 0; i-- )
	{
		int currIndex = (i + startIndex) % pPartArray->GetSize();

		Script::CStruct* pCurrStruct = pPartArray->GetStructure( currIndex );
		
		int disqualified;
		pCurrStruct->GetInteger( "disqualified", &disqualified, true );
		if ( !disqualified )
		{
			return pCurrStruct;
		}
	}

	// none found...
	return pPartArray->GetStructure( startIndex );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | FlushDeadObjects | 
// flush any objects that are marked as dead
// used so we can be sure they have gone, so we can re-use the memory
// and also avoid fragmentation
bool ScriptFlushDeadObjects( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::Skate::Instance()->GetObjectManager()->FlushDeadObjects();
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | BindTrickToKeyCombo | binds the given trick to the key combo
// @parm name | key_combo |
// @parm name | trick_checksum |
// @parmopt int | update_mappings | 1 | automatically update the trick mappings.
// This should only be 0 in a split screen game
bool ScriptBindTrickToKeyCombo( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 key_combo;
	pParams->GetChecksum( "key_combo", &key_combo, Script::ASSERT );

	uint32 trick_checksum=0;
	pParams->GetChecksum( "trick", &trick_checksum );

	// For create-a-tricks, the trick parameter is an integer index.
	int create_a_trick=0;
	bool got_create_a_trick=false;
	if (pParams->GetInteger( "trick", &create_a_trick ))
	{
		got_create_a_trick=true;
	}	

	Dbg_MsgAssert(got_create_a_trick || trick_checksum,("\n%s\nMissing trick parameter",pScript->GetScriptInfo()));
	
	bool update_mappings = true;
	int update_param = 1;
	pParams->GetInteger( "update_mappings", &update_param, Script::NO_ASSERT );
	if ( update_param == 0 )
		update_mappings = false;

	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();

	// are we binding a special trick?
	if ( pParams->ContainsFlag( "special" ) )
	{
		int index;
		if ( !pParams->GetInteger( "index", &index, Script::NO_ASSERT ) )
        {
            return false;
        }
        
		// add a new slot and update info
		Obj::SSpecialTrickInfo trickInfo;
		trickInfo.m_TrickSlot = key_combo;
		if (got_create_a_trick)
		{
            trickInfo.m_TrickName = create_a_trick;
        }
        else
        {
            trickInfo.m_TrickName = trick_checksum;
        }
		if ( pParams->ContainsFlag( CRCD(0x61a1bc57,"cat") ) )
		{
			trickInfo.m_isCat = true;
		}
        else
        {
			trickInfo.m_isCat = false;
		}

        pSkaterProfile->SetSpecialTrickInfo( index, trickInfo, update_mappings );
	}
	else
	{
		Script::CStruct* pTricks = pSkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );
		
		if (got_create_a_trick)
		{
			// Make sure any existing checksum parameter is removed.
			pTricks->RemoveComponent(key_combo);
			pTricks->AddInteger( key_combo, create_a_trick );
		}
		else
		{
			pTricks->RemoveComponent(key_combo);
            pTricks->AddChecksum( key_combo, trick_checksum );
		}	
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// this does breadth first recursion on the structure and returns trick
// highest in the tree (parent overwrites child).
static uint32 s_find_trick_in_mapping( Script::CStruct* pMapping, uint32 trick_checksum, int recurse_level )
{
	if ( recurse_level > 10 )
	{
		Dbg_MsgAssert( 0, ( "GetKeyComboArrayFromTrickArray recursed too much." ) );
		return 0;
	}

	Script::CComponent* p_comp = pMapping->GetNextComponent( NULL );
	uint32 rv = 0;
	while ( p_comp )
	{
		Script::CStruct* pTestStruct = Script::GetStructure( p_comp->mChecksum, Script::NO_ASSERT );
		Script::CComponent* p_next = pMapping->GetNextComponent( p_comp );
		if ( !rv && p_comp->mType == ESYMBOLTYPE_STRUCTURE )
		{
			// printf("recursing to level %i\n", recurse_level);
			rv = s_find_trick_in_mapping( p_comp->mpStructure, trick_checksum, recurse_level + 1 );
		}
		else if ( !rv && pTestStruct )
		{
			// printf("recursing to level %i\n", recurse_level);
			rv = s_find_trick_in_mapping( pTestStruct, trick_checksum, recurse_level + 1 );
		}
		if ( p_comp->mType == ESYMBOLTYPE_NAME && p_comp->mChecksum == trick_checksum )
			rv = p_comp->mNameChecksum;

		p_comp = p_next;
	}
	return rv;
}

static uint32 s_find_cat_in_mapping( Script::CStruct* pMapping, int cat_num )
{
	Script::CComponent* p_comp = pMapping->GetNextComponent( NULL );
	uint32 rv = 0;

    while ( p_comp )
	{
        Script::CComponent* p_next = pMapping->GetNextComponent( p_comp );
        if ( p_comp->mType == ESYMBOLTYPE_INTEGER && p_comp->mIntegerValue == cat_num )
        {
            printf("s_find_cat_in_mapping found %i\n", p_comp->mIntegerValue );
            rv = p_comp->mNameChecksum;
        }
        p_comp = p_next;
	}
    return rv;
}

// @script | GetKeyComboBoundToTrick | looks for any key combos that are currently
// associated with the specified trick.  The result is stored in current_key_combo
// @parm name | trick | trick to look for
bool ScriptGetKeyComboBoundToTrick( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 trick_checksum = NULL;
    int cat_num;
    if (!pParams->GetChecksum( CRCD(0x270f56e1,"trick"), &trick_checksum, Script::NO_ASSERT) )
    {
        pParams->GetInteger( CRCD(0xa75b8581,"cat_num"), &cat_num, Script::ASSERT );
    }
	
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();

	uint32 key_combo = 0;
	bool found_special = false;
	int special_index = 0;
	
    if (trick_checksum)
    {
        if ( !pParams->ContainsFlag( CRCD(0xb394c01c,"special") ) )
    	{
    		Script::CStruct* pTricks = pSkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );
    	
    		uint32 test_key_combo = s_find_trick_in_mapping( pTricks, trick_checksum, 0 );
    		
    		// check that the mapping holds true the other way (ie, when we grab the trick
    		// based on this key_combo, do we get the same trick?).
    		uint32 test_trick_checksum = 0;
    		if ( pTricks->GetChecksum( test_key_combo, &test_trick_checksum, Script::NO_ASSERT ) && test_trick_checksum == trick_checksum )
    				key_combo = test_key_combo;
    	}
    	else
    	{
    		if ( !key_combo && pParams->ContainsFlag( CRCD(0xb394c01c,"special") ) )
    		{
    			// printf("searching for special trick\n");
    			int numTricks = pSkaterProfile->GetNumSpecialTrickSlots();
    			// printf("%i tricks to check\n", numTricks);
    	
    			// search through current special tricks
    			for ( int i = 0; i < numTricks; i++ )
    			{
    				Obj::SSpecialTrickInfo trick_info = pSkaterProfile->GetSpecialTrickInfo( i );
    				if ( trick_info.m_TrickName == trick_checksum )
    				{
    					printf("GetKeyComboBoundToTrick found a special trick\n");
    					found_special = true;
    					special_index = i;
    					key_combo = trick_info.m_TrickSlot;
    					break;
    				}
    			}
    		}
    	}
    }
    else
    {
        Script::CStruct* pTricks = pSkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );
    	
        key_combo = s_find_cat_in_mapping( pTricks, cat_num );
    }
    
	
	if ( key_combo )
	{
		pScript->GetParams()->AddChecksum( "current_key_combo", key_combo );
		if ( found_special )
		{
			printf("current_index: %i\n", special_index);
			pScript->GetParams()->AddInteger( "current_index", special_index );
		}
		return true;
	}	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | UpdateTrickMappings | forces the trick mappings to update
// @parmopt int | skater | | the skater num
// @parmopt name | skaterId | | the id of the skater
bool ScriptUpdateTrickMappings( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();

	Obj::CPlayerProfileManager* pProfileMan = pSkate->GetPlayerProfileManager();
	
	// TODO: if we ever do split screen network games, this will not work!
	// It assumes there is only one local skater when in a net game
	if ( gamenet_man->InNetGame() )
	{
		Obj::CSkater* pSkater = pSkate->GetLocalSkater();
		Dbg_MsgAssert( pSkater, ( "UpdateTrickMappings couldn't find a local skater" ) );
		
		Obj::CSkaterProfile* pProfile = pProfileMan->GetProfile( 0 );
		
		Obj::CTrickComponent* pTrickComponent = GetTrickComponentFromObject(pSkater);
		Dbg_Assert(pTrickComponent);
		pTrickComponent->UpdateTrickMappings( pProfile );
	}
	else
	{
		int skater_num;
		pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skater_num, Script::ASSERT );
		Obj::CSkater* pSkater = pSkate->GetSkater( skater_num );
		Dbg_Assert( pSkater );
		Obj::CSkaterProfile* pProfile = pProfileMan->GetProfile( skater_num );
		Dbg_Assert( pProfile );
	
		if ( pSkater && pProfile )
		{
			Obj::CTrickComponent* pTrickComponent = GetTrickComponentFromObject(pSkater);
			Dbg_Assert(pTrickComponent);
			pTrickComponent->UpdateTrickMappings( pProfile );

		}
	}
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetConfigurableTricksFromType | returns an array of trick checksums
// which have the type specified.  The array is stored as ConfigurableTricks
// @parm name | type | the type to search for
// @flag special | search for special tricks of this type
bool ScriptGetConfigurableTricksFromType( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 type = 0;
	pParams->GetChecksum( "type", &type, Script::NO_ASSERT );

	bool find_special = pParams->ContainsFlag( "special" );

	Dbg_MsgAssert( type || find_special, ( "You must specify a type or provide the special flag" ) );

	uint32 trick_list[128];
	int num_found = 0;
    int cat_list[128];
    int num_cats_found = 0;
    
    // grab some checksums
    //uint32 createdtrick = Script::GenerateCRC( "CreatedTrick" );
    uint32 grabtrick = Script::GenerateCRC( "GrabTrick" );
    uint32 fliptrick = Script::GenerateCRC( "FlipTrick" );

	// grab the list of all tricks
	Script::CArray* p_all_tricks = Script::GetArray( "ConfigurableTricks", Script::ASSERT );
	Dbg_MsgAssert( p_all_tricks->GetType() == ESYMBOLTYPE_NAME, ( "ConfigurableTricks array had wrong type\n" ) );

	int size = p_all_tricks->GetSize();
	for ( int i = 0; i < size; i++ )
	{
		Dbg_MsgAssert( num_found < 128, ( "Found too many tricks" ) );
		
		uint32 trick = p_all_tricks->GetChecksum( i );
		Script::CStruct* p_trick = Script::GetStructure( trick, Script::NO_ASSERT );

        if ( p_trick )
		{
			// get the type
			uint32 current_trick_type;

			// first check for optional TrickType var
			if ( !p_trick->GetChecksum( "TrickType", ¤t_trick_type, Script::NO_ASSERT ) )
			{
				if ( !p_trick->GetChecksum( "Scr", ¤t_trick_type, Script::NO_ASSERT ) )
				{
					// assume it's a grind
					current_trick_type = Script::GenerateCRC( "GrindTrick" );
				}
			}
			
			if ( ( find_special && !type ) || current_trick_type == type )
			{
				// get the params array and look for the special flag
				Script::CStruct* p_trick_params;
				p_trick->GetStructure( "Params", &p_trick_params, Script::ASSERT );

				if ( find_special && p_trick_params->ContainsFlag( "IsSpecial" ) )
				{
					trick_list[num_found] = trick;
					num_found++;
				}
				else if ( !find_special && !p_trick_params->ContainsFlag( "IsSpecial" ) )
				{
					trick_list[num_found] = trick;
					num_found++;
				}
			}
		}
	}

    // if type is grab or flip then list created tricks too!
    if ( type == grabtrick || type == fliptrick )
    {
        Mdl::Skate * pSkate = Mdl::Skate::Instance();
        Obj::CSkater* pSkater = pSkate->GetLocalSkater();
	    if ( pSkater )
        {
            int size = Game::vMAX_CREATED_TRICKS;
            for ( int i = 1; i < size; i++ )    // start at one because 0 is just a clipboard!
        	{
                int full = 0;
                if ( pSkater->m_created_trick[i]->mp_other_params->GetInteger( CRCD(0x1f802b5f,"full"), &full, Script::NO_ASSERT ) )
                {
                    if ( full )
                    {
                        cat_list[num_cats_found] = i; //trick;
                        num_cats_found++;
                    }
                }
            }
        }
    }

	if ( num_found == 0 )
		return false;

	// create an array to return
	Script::CArray* p_configurable_tricks = new Script::CArray();
	p_configurable_tricks->SetSizeAndType( num_found, ESYMBOLTYPE_NAME );
	for ( int i = 0; i < num_found; i++ )
		p_configurable_tricks->SetChecksum( i, trick_list[i] );

	// add the array
	pScript->GetParams()->AddArrayPointer( "ConfigurableTricks", p_configurable_tricks );

    if ( num_cats_found > 0 )
    {
        // create an array to return
    	Script::CArray* p_configurable_cats = new Script::CArray();
    	p_configurable_cats->SetSizeAndType(num_cats_found, ESYMBOLTYPE_INTEGER );
    	for ( int i = 0; i < num_cats_found; i++ )
    		p_configurable_cats->SetInteger( i, cat_list[i] );
    
    	// add the array
    	pScript->GetParams()->AddArrayPointer( "ConfigurableCats", p_configurable_cats );
    }

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | TrickIsLocked | true if the trick is associated with a skater
// that is locked
// @parm name | trick | the trick name
bool ScriptTrickIsLocked( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 trick;
	pParams->GetChecksum( "trick", &trick, Script::ASSERT );

	Script::CStruct* p_trick = Script::GetStructure( trick, Script::ASSERT );

	if ( p_trick )
	{
		Script::CStruct* p_trick_params;
		p_trick->GetStructure( "Params", &p_trick_params, Script::ASSERT );

		uint32 skater;
		if ( p_trick_params && p_trick_params->GetChecksum( CRCD(0x5b8ab877,"skater"), &skater, Script::NO_ASSERT ) )
		{
			Obj::CPlayerProfileManager* pPlayerProfileManager = Mdl::Skate::Instance()->GetPlayerProfileManager();
			
			if ( p_trick_params->ContainsFlag( "OnlyWith" ) )
			{
				Obj::CSkaterProfile* pCurrentProfile = pPlayerProfileManager->GetCurrentProfile();
				return ( pCurrentProfile->GetSkaterNameChecksum() != skater );
			}
			else
			{
				int num_profiles = pPlayerProfileManager->GetNumProfileTemplates();
				for ( int i = 0; i < num_profiles; i++ )
				{
					Obj::CSkaterProfile* pProfile = pPlayerProfileManager->GetProfileTemplateByIndex( i );
					if ( pProfile->GetSkaterNameChecksum() == skater )
					{
						int isLocked = 0;
						pProfile->GetInfo()->GetInteger( "is_hidden", &isLocked, Script::NO_ASSERT );
						return ( isLocked != 0 );
					}
				}
			}
			Dbg_MsgAssert( 0, ( "TrickIsLocked confused by skater %s in trick %s\n", Script::FindChecksumName( skater ), Script::FindChecksumName( trick ) ) );
		}
	}
	else
		Dbg_MsgAssert( 0, ( "TrickIsLocked couldn't find trick %s\n", Script::FindChecksumName( trick ) ) );

	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetTrickDisplayText | gets the text corresponding to this trick
// @parm name | trick | the trick name
bool ScriptGetTrickDisplayText( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 trick;
	pParams->GetChecksum( "trick", &trick, Script::ASSERT );

	Script::CStruct* p_trick = Script::GetStructure( trick, Script::ASSERT );

	if ( p_trick )
	{
		Script::CStruct* p_trick_params;
		p_trick->GetStructure( "Params", &p_trick_params, Script::ASSERT );

		const char* p_trick_name;
		p_trick_params->GetLocalString( CRCD(0xa1dc81f9,"name"), &p_trick_name, Script::ASSERT );

		pScript->GetParams()->AddString( "trick_display_text", p_trick_name );

		// look for any double tap tricks
		uint32 extra_trick = 0;
		if ( !p_trick_params->GetChecksum( "ExtraTricks", &extra_trick, Script::NO_ASSERT ) )
		{
			// look for an array of extra tricks and grab the first one
			Script::CArray* p_extra_tricks;
			if ( p_trick_params->GetArray( "ExtraTricks", &p_extra_tricks, Script::NO_ASSERT ) )
				extra_trick = p_extra_tricks->GetChecksum( 0 );
		}

		// add any extra trick info
		if ( extra_trick )
		{
			// grab this extra trick
			Script::CArray* p_temp = Script::GetArray( extra_trick, Script::ASSERT );
			Script::CStruct* p_extra_trick = p_temp->GetStructure( 0 );
			Dbg_Assert( p_extra_trick );
			Script::CStruct* p_extra_trick_params;
			p_extra_trick->GetStructure( "Params", &p_extra_trick_params, Script::ASSERT );
			const char* p_extra_trick_string;
			p_extra_trick_params->GetString( CRCD(0xa1dc81f9,"name"), &p_extra_trick_string, Script::ASSERT );
			pScript->GetParams()->AddString( "extra_trick_string", p_extra_trick_string );
			pScript->GetParams()->AddChecksum( "extra_trick_checksum", extra_trick );
		}

		return true;
	}
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetSpecialTrickInfo | gets the special trick info for the slot
// specified
// @parm int | index | the special trick slot
bool ScriptGetSpecialTrickInfo( Script::CStruct* pParams, Script::CScript* pScript )
{
	int index;
	pParams->GetInteger( "index", &index, Script::ASSERT );

	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	Obj::CSkaterProfile* p_SkaterProfile = skate_mod->GetCurrentProfile();
	Obj::SSpecialTrickInfo trick_info = p_SkaterProfile->GetSpecialTrickInfo( index );

	pScript->GetParams()->AddChecksum( "special_trickslot", trick_info.m_TrickSlot );
	
    
    if ( trick_info.m_isCat )
    {
        pScript->GetParams()->AddInteger( "special_trickname", trick_info.m_TrickName );
    }
    else
    {
        pScript->GetParams()->AddChecksum( "special_trickname", trick_info.m_TrickName );
    }
    
    pScript->GetParams()->AddInteger( "isCat", trick_info.m_isCat );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetTrickType | gets the type of the specified trick and returns
// the result in the trick_type param
// @parm name | trick | the name of the trick
bool ScriptGetTrickType( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 trick_checksum;
	pParams->GetChecksum( "trick", &trick_checksum, Script::ASSERT );

	Script::CStruct* p_trick = Script::GetStructure( trick_checksum, Script::ASSERT );

	uint32 trick_type;
	// first check for optional TrickType var
	if ( !p_trick->GetChecksum( "TrickType", &trick_type, Script::NO_ASSERT ) )
	{
		if ( !p_trick->GetChecksum( "Scr", &trick_type, Script::NO_ASSERT ) )
		{
			// assume it's a grind
			trick_type = Script::GenerateCRC( "GrindTrick" );
		}
	}
	pScript->GetParams()->AddChecksum( "trick_type", trick_type );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetIndexOfItemContaining | this will search through the given
// array (must be an array of structures) for the structure containing a param
// the index will be placed in the calling script's params (index)
// @parm array | array | the array to search
// @parmopt int | index | 0 | index to start at
// @parm name | name | name of the param to search for
// @parm name | value | value param should have for a match
bool ScriptGetIndexOfItemContaining( Script::CStruct* pParams, Script::CScript* pScript )
{
	uint32 array_name;
	pParams->GetChecksum( "array", &array_name, Script::ASSERT );
	Script::CArray* p_array = Script::GetArray( array_name, Script::ASSERT );
	Dbg_MsgAssert( p_array->GetType() == ESYMBOLTYPE_STRUCTURE, ( "This function only works on arrays of structures." ) );

	uint32 item;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &item, Script::ASSERT );

	int index = 0;
	pParams->GetInteger( "index", &index, Script::NO_ASSERT );

	uint32 value;
	pParams->GetChecksum( "value", &value, Script::ASSERT );

	int array_size = p_array->GetSize();
	for ( ; index < array_size; index++ )
	{
		Script::CStruct* p_struct = p_array->GetStructure( index );
		if ( p_struct->ContainsComponentNamed( item ) )
		{
			uint32 item_value;
			p_struct->GetChecksum( item, &item_value, Script::ASSERT );
			if ( item_value == value )
			{
				pScript->GetParams()->AddInteger( "index", index );
				return true;
			}
		}
	}
	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetLevelRecords | appends the records to the calling script's params
bool ScriptGetLevelRecords( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	
	int levelNum;
	if ( !pParams->GetInteger( "level", &levelNum, Script::NO_ASSERT ) )
		levelNum = pSkate->GetCareer()->GetLevel();	
	
	Records::CGameRecords* pGameRecords = pSkate->GetGameRecords();
	Records::CLevelRecords* pLevelRecords = pGameRecords->GetLevelRecords( levelNum );
	pLevelRecords->WriteIntoStructure( pScript->GetParams() );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ResetComboRecords | resets the score object's combo records
bool ScriptResetComboRecords( Script::CStruct* pParams, Script::CScript* pScript )
{
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = pSkate->GetLocalSkater();
	if( pSkater )
	{
		Mdl::Score* pScore = pSkater->GetScoreObject();
	
		pScore->ResetComboRecords();
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetNumberOfTrickOccurrences | returns the number of times
// the given trick appears in the skater's current score pot
// @parm string | TrickText | trick string to look for
bool ScriptGetNumberOfTrickOccurrences( Script::CStruct *pParams, Script::CScript *pScript )
{
	const char* p_trick;
	pParams->GetString( "TrickText", &p_trick, Script::ASSERT );
	uint32 trick = Script::GenerateCRC( p_trick );

	Mdl::Skate * skate_mod = Mdl::Skate::Instance();
	Obj::CSkater* pSkater = skate_mod->GetLocalSkater();
	Mdl::Score* pScore = pSkater->GetScoreObject();

	int num = pScore->GetCurrentNumberOfOccurrencesByName( trick );
	pScript->GetParams()->AddInteger( "number_of_occurrences", num );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetNumSoundtracks | returns the number of soundtracks
// in the numSoundtracks param
bool ScriptGetNumSoundtracks( Script::CStruct *pParams, Script::CScript *pScript )
{
	Dbg_MsgAssert( Config::GetHardware() == Config::HARDWARE_XBOX, ( "GetNumSoundtracks can only be called on XBox" ) );
	int numSoundtracks = Nx::CEngine::sGetNumSoundtracks();
	pScript->GetParams()->AddInteger( "numSoundtracks", numSoundtracks );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetSoundtrackName( Script::CStruct *pParams, Script::CScript *pScript )
{
	Dbg_MsgAssert( Config::GetHardware() == Config::HARDWARE_XBOX, ( "GetNumSoundtracks can only be called on XBox" ) );
	int soundtrack_number;
	pParams->GetInteger( NONAME, &soundtrack_number, Script::ASSERT );
	const char* pSoundtrackName = Nx::CEngine::sGetSoundtrackName( soundtrack_number );
	pScript->GetParams()->AddString( "soundtrackName", pSoundtrackName );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | BindControllerToSkater | remaps the given controller to the
// skater
// @parm int | controller | the controller number
// @parm int | skater_heap_index | the skater num
bool ScriptBindControllerToSkater( Script::CStruct *pParams, Script::CScript *pScript )
{
	int controller;
	pParams->GetInteger( "controller", &controller, Script::ASSERT );

	int skater;
	pParams->GetInteger( "skater_heap_index", &skater, Script::ASSERT );

	printf("attempting to bind skater %i to controller %i\n", skater, controller);

	Mdl::Skate *pSkate = Mdl::Skate::Instance();
	pSkate->m_device_server_map[skater] = controller;

	pSkate->UpdateSkaterInputHandlers();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptBindFrontEndToController( Script::CStruct *pParams, Script::CScript *pScript )
{
	int controller;
	pParams->GetInteger( "controller", &controller, Script::ASSERT );

	int front_end_pad;
	pParams->GetInteger( "front_end_pad", &front_end_pad, Script::ASSERT );

	printf("attempting to bind front end %i to controller %i\n", front_end_pad, controller);

	Mdl::FrontEnd *pFrontEnd = Mdl::FrontEnd::Instance();
	
	// find any controllers using this front end mapping and switch
	int current = pFrontEnd->m_device_server_map[front_end_pad];
	for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
	{
		if ( pFrontEnd->m_device_server_map[i] == controller )
			pFrontEnd->m_device_server_map[i] = current;
	}
	pFrontEnd->m_device_server_map[front_end_pad] = controller;

	pFrontEnd->UpdateInputHandlerMappings();
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | ControllerBoundToDifferentSkater | 
// @parm int | controller |
// @parm int | skater | 
bool ScriptControllerBoundToDifferentSkater( Script::CStruct *pParams, Script::CScript *pScript )
{
	int skater;
	pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skater, Script::ASSERT );

	int controller;
	pParams->GetInteger( "controller", &controller, Script::ASSERT );

	Mdl::Skate* pSkate = Mdl::Skate::Instance();
	
	for ( int i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
	{
		// don't check the skater we're trying to bind
		if ( i == skater )
			continue;

		Obj::CSkater* pSkater = pSkate->GetSkater( i );
		if ( pSkater && pSkater->IsLocalClient() )
		{
			if ( pSkate->m_device_server_map[i] == controller )
				return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptControllerBoundToSkater( Script::CStruct* pParams, Script::CScript* pScript )
{
	int controller;
	pParams->GetInteger( "controller", &controller, Script::ASSERT );

	int skater;
	pParams->GetInteger( CRCD(0x5b8ab877,"skater"), &skater, Script::ASSERT );

	Dbg_MsgAssert( skater >= 0 && skater < Mdl::Skate::vMAX_SKATERS, ( "Bad skater index %i passed to ControllerBoundToSkater\n", skater ) );
	return ( Mdl::Skate::Instance()->m_device_server_map[skater] == controller );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetKeyComboArrayFromTrickArray | returns an array of key combos (KeyCombos)
// that correspond to the passed trick array
// @parm array | tricks | array of trick names, eg [Trick_KickFlip Trick_HeelFlip]
bool ScriptGetKeyComboArrayFromTrickArray( Script::CStruct *pParams, Script::CScript *pScript )
{
	Script::CArray* pTricks;
	pParams->GetArray( "tricks", &pTricks, Script::ASSERT );

	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	Obj::CSkaterProfile* pSkaterProfile = pSkate->GetCurrentProfile();
	Script::CStruct* pTrickMapping = pSkaterProfile->GetTrickMapping( CRCD(0xd544aa2d,"trick_mapping") );

	int size = pTricks->GetSize();
	// make a return array
	Script::CArray* pKeyCombos = new Script::CArray();
	pKeyCombos->SetSizeAndType( size, ESYMBOLTYPE_NAME );

	for ( int i = 0; i < size; i++ )
	{
		uint32 trick_checksum = pTricks->GetChecksum( i );
		
		// search for this trick in the current mapping
		uint32 key_combo = s_find_trick_in_mapping( pTrickMapping, trick_checksum, 0 );

		if ( key_combo == 0 )
		{
			// look for a special trick
			int num_specials = pSkaterProfile->GetNumSpecialTrickSlots();
			for ( int j = 0; j < num_specials; j++ )
			{
				Obj::SSpecialTrickInfo trick_info = pSkaterProfile->GetSpecialTrickInfo( j );
				if ( trick_info.m_TrickName == trick_checksum )
				{
					key_combo = trick_info.m_TrickSlot;
					break;
				}
			}			
		}

		Dbg_MsgAssert( key_combo != 0, ( "GetKeyComboFromTrickArray found an unmpped trick - %s", Script::FindChecksumName( trick_checksum ) ) );
		pKeyCombos->SetChecksum( i, key_combo );
	}

	pScript->GetParams()->AddArray( "KeyCombos", pKeyCombos );
	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | FirstInputReceived | the game does not start checking for
// disconnected controllers until this is called
bool ScriptFirstInputReceived( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mdl::Skate::Instance()->FirstInputReceived();
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | VibrateController | 
// @parm int | port | 
// @parm int | actuator |
// @parm int | percent |
bool ScriptVibrateController( Script::CStruct *pParams, Script::CScript *pScript )
{
	int port;
	pParams->GetInteger( "port", &port, Script::ASSERT );

	int actuator;
	pParams->GetInteger( "actuator", &actuator, Script::ASSERT );

	int percent;
	pParams->GetInteger( "percent", &percent, Script::ASSERT );
	
	SIO::Manager* sio_manager = SIO::Manager::Instance();

	// TODO: this won't work if we support multitap (assumes slot 0)
	SIO::Device* pDevice = sio_manager->GetDevice( port, 0 );
	if ( pDevice )
	{
		pDevice->ActivateActuator( actuator, percent );
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | LockCurrentSkaterProfileIndex | 
bool ScriptLockCurrentSkaterProfileIndex( Script::CStruct *pParams, Script::CScript *pScript )
{
	int locked = true;
	pParams->GetInteger( NONAME, &locked, Script::ASSERT );

	Mdl::Skate * pSkate = Mdl::Skate::Instance();

	Obj::CPlayerProfileManager* pProfileMan = pSkate->GetPlayerProfileManager();
	pProfileMan->LockCurrentSkaterProfileIndex( locked );

	return true;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | SetSpecialTrickInfo | 
// @parm int | slot | 
// @parm name | trick_name | 
// @parm name | key_combo |
// @flag update_mappings | 
bool ScriptSetSpecialTrickInfo( Script::CStruct *pParams, Script::CScript *pScript )
{
	int slot;
	pParams->GetInteger( "slot", &slot, Script::ASSERT );

	uint32 trick_name;
	pParams->GetChecksum( "trick_name", &trick_name, Script::ASSERT );
	
	uint32 key_combo;
	pParams->GetChecksum( "key_combo", &key_combo, Script::ASSERT );

	Mdl::Skate* skate_mod = Mdl::Skate::Instance();
	Obj::CPlayerProfileManager* pPlayerProfileManager = skate_mod->GetPlayerProfileManager();
	Obj::CSkaterProfile* pProfile = NULL;
	pProfile = pPlayerProfileManager->GetCurrentProfile();

	Dbg_MsgAssert( pProfile, ( "SetSpecialTrickInfo couldn't get a profile" ) );
	if ( pProfile )
	{
		// printf("setting slot %i to %s, %s\n", slot, Script::FindChecksumName( trick_name ), Script::FindChecksumName( key_combo ) );
		Obj::SSpecialTrickInfo trickInfo;
		trickInfo.m_TrickName = trick_name;
		trickInfo.m_TrickSlot = key_combo;
		pProfile->SetSpecialTrickInfo( slot, trickInfo, false );
		return true;
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static bool s_is_angle(Script::CArray *p_angles_array, uint32 component_name)
{
	if (!p_angles_array)
	{
		return false;
	}

	uint32 size=p_angles_array->GetSize();
	for (uint32 i=0; iGetChecksum(i))
		{
			return true;
		}
	}
	
	return false;
}
	
static float make_angle_in_range(float a)
{
	while (a < 0.0f)
	{
		a+=360.0f;
	}
	while (a > 360.0f)
	{
		a-=360.0f;
	}
	return a;
}
		
// @script | InterpolateParameters | This will scan through the structure A, and any parameters in it
// which have type int or float and such that the same named parameter exists in B and also has type int
// or float will have their value interpolated between the two values, and the parameter put into a
// returned structure named Interpolated.
// Note that the returned structure will only contain the parameters that were interpolated, so if
// structure A contained a string, that parameter will not get into Interpolated
// @parm structure | a | The first structure
// @parm structure | b | The second structure
// @parmopt float | Proportion | 0.0 | A value between 0 and 1. 0 means all of structure A and none of structure B,
// 1 means all of B and none of A. 0.5 will therefore be half way between the two.
// @parmopt array | Ignore | | An optional array of parameter names to ignore. Parameters whose type cannot
// be interpolated, such as strings, will automatically be ignored, but the Ignore array allows certain
// integer or float params to be ignored too.
// @parmopt array | Angles | | An optional array of parameters whose values are to be treated as angles.
// So when interpolating between 10 and 350 degrees for example, instead of simply interpolating from
// the number 10 to 350, it will go from 10 backwards through 0 and 359 to 350.
bool ScriptInterpolateParameters( Script::CStruct *pParams, Script::CScript *pScript )
{
	Script::CStruct *p_new_structure=new Script::CStruct;
	
	Script::CStruct *p_a=NULL;
	pParams->GetStructure(CRCD(0x174841bc,"a"),&p_a,Script::ASSERT);
	
	Script::CStruct *p_b=NULL;
	pParams->GetStructure(CRCD(0x8e411006,"b"),&p_b,Script::ASSERT);
	
	float proportion=0.0f;
	pParams->GetFloat(CRCD(0x404e690d,"Proportion"),&proportion);
	
	Script::CArray *p_ignore=NULL;
	pParams->GetArray(CRCD(0xf277291d,"Ignore"),&p_ignore);
	
	Script::CArray *p_angles_array=NULL;
	pParams->GetArray(CRCD(0x9d2d0915,"Angles"),&p_angles_array);
	
	Script::CComponent *p_comp = p_a->GetNextComponent();
	while (p_comp)
	{
		bool ignore=false;
		
		if (p_ignore)
		{
			for (uint32 i=0; iGetSize(); ++i)
			{
				if (p_comp->mNameChecksum==p_ignore->GetChecksum(i))
				{
					ignore=true;
					break;
				}
			}
		}

		if (!ignore)
		{
			uint32 component_name=0;
			float value_a=0.0f;
			bool write_integer=true;
			
			// See if the component in structure A is of a type that needs to be interpolated.
			if (p_comp->mType==ESYMBOLTYPE_INTEGER)
			{
				component_name=p_comp->mNameChecksum;
				value_a=(float)p_comp->mIntegerValue;
			}
			else if (p_comp->mType==ESYMBOLTYPE_FLOAT)
			{
				component_name=p_comp->mNameChecksum;
				value_a=p_comp->mFloatValue;
				write_integer=false;
			}
			
			// If found a candidate for interpolation, see if there is a similarly named
			// component in structure B, and if so, interpolate between the two and stick
			// a new component of that name into the new structure.
			if (component_name)
			{
				bool got_value_b=false;
				
				float value_b=0.0f;
				int integer_value_b=0;
				if (p_b->GetInteger(component_name,&integer_value_b))
				{
					got_value_b=true;
					value_b=(float)integer_value_b;
				}
				else if (p_b->GetFloat(component_name,&value_b))
				{
					got_value_b=true;
					write_integer=false;
				}
				
				if (got_value_b)
				{
					float new_value=0.0f;
					
					
					if (s_is_angle(p_angles_array,component_name))
					{
						float d=value_b-value_a;
						d=make_angle_in_range(d);
						if (d > 180.0f)
						{
							d=-(360.0f-d);
						}
						new_value=value_a+proportion*d;
						new_value=make_angle_in_range(new_value);
					}
					else
					{
						new_value=value_a+proportion*(value_b-value_a);
					}	
						
					if (write_integer)
					{
						p_new_structure->AddInteger(component_name,(int)new_value);
					}
					else
					{
						p_new_structure->AddFloat(component_name,new_value);
					}	
				}
			}
		}
				
		p_comp = pParams->GetNextComponent(p_comp);
	}	
	
	pScript->GetParams()->AddStructurePointer(CRCD(0xff6f3872,"Interpolated"),p_new_structure);
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				
// @script | PlaySkaterStream | skater member func.
// @parm string | type | the stream type.
// @parmopt int | num_possible | 10 | the maximum number of streams to search for
bool ScriptPlaySkaterStream ( Script::CStruct *pParams, Script::CScript *pScript )
{
	Dbg_Assert(pScript->mpObject);
	Dbg_MsgAssert(pScript->mpObject->GetType() == SKATE_TYPE_SKATER, ("PlaySkaterStream may only be called on a skater object"));
	
	Obj::CSkater* p_skater = static_cast< Obj::CSkater* >(pScript->mpObject.Convert());
		
	const char* p_type;
	pParams->GetString( CRCD(0x7321a8d6,"type"), &p_type, Script::ASSERT );
	
	int max_num = 10;
	pParams->GetInteger( CRCD(0x58492707,"num_possible"), &max_num, Script::NO_ASSERT );

	Script::CArray* p_stream_indices = new Script::CArray();
	p_stream_indices->SetSizeAndType( max_num, ESYMBOLTYPE_INTEGER );
	// generate list of indices (start at 1)
	for ( int i = 0; i < max_num; i++ )
		p_stream_indices->SetInteger( i, i + 1 );

	// randomize list
	for ( int i = 0; i < max_num; i++ )
	{
		// grab a random index and switch with the current index
		int random_index = Mth::Rnd( max_num );
		int random_value = p_stream_indices->GetInteger( random_index );
		p_stream_indices->SetInteger( random_index, p_stream_indices->GetInteger( i ) );
		p_stream_indices->SetInteger( i, random_value );
	}

	// get the skater's first name
	char p_first_name[128];
	strcpy( p_first_name, p_skater->m_firstName );

	// resolve to last name
	char p_last_name[128];

	if ( p_skater->m_isPro )
	{
		// grab the last name string from global array
		Script::CStruct* pLastNames = Script::GetStructure( CRCD(0x5775194e,"goal_pro_last_names"), Script::ASSERT );
		uint32 first_name_checksum = Script::GenerateCRC( p_first_name );
		const char* p_temp_last_name;
		pLastNames->GetString( first_name_checksum, &p_temp_last_name, Script::ASSERT );
		Dbg_MsgAssert( strlen( p_temp_last_name ) < 128, ( "buffer overflow" ) );
		strcpy( p_last_name, p_temp_last_name );
	}
	else
	{
		strcpy( p_last_name, "custom" );
		if ( p_skater->m_isMale )
		{
			strcat( p_last_name, "m" );
		}
		else
		{
			strcat( p_last_name, "f" );
		}
	}
		
	Dbg_MsgAssert( strlen( p_last_name ) < 128, ( "buffer overflow in PlaySkaterStream: %s", p_last_name ) );
	// find the first valid stream and play
	char stream_name[128];
		
	for ( int i = 0; i < max_num; i++ )
	{
		Dbg_MsgAssert( strlen( p_last_name ) + strlen( p_type ) + 2 < 128, ( "buffer overflow in PlaySkaterStream: %s", stream_name ) );
		sprintf( stream_name, "%s_%s%02i", p_last_name, p_type, p_stream_indices->GetInteger( i ) );
		
		// printf("figured a stream name of %s\n", stream_name);
		if ( Pcm::StreamExists( Script::GenerateCRC( stream_name ) ) )
		{
			Script::CStruct* pTemp = new Script::CStruct;
			pTemp->AddChecksum( "stream_checksum", Script::GenerateCRC( stream_name ) );
			Script::RunScript( "skater_play_bail_stream", pTemp, pScript->mpObject );
			delete pTemp;
			break;
		}
	}
	Script::CleanUpArray( p_stream_indices );
	delete p_stream_indices;
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetTextureFromPath | returns the file name for a texture
// from the end of the give path
// @parm string | path | path of file including file name
bool ScriptGetTextureFromPath( Script::CStruct *pParams, Script::CScript *pScript )
{
	const char* p_path;
    char new_string[32];
    int i, t=0;

	pParams->GetString( CRCD(0xf4ab74f0,"path"), &p_path, Script::ASSERT );

    int length = strlen( p_path );

    // find last '/' in path
    for (i=(length); i>=0; i--)	 	// Mick: was previously "length+1", which would start at the character AFTER the string
    {								// which on rare occasions was a '/' char, which caused obscure crashes.
        if ( ( p_path[i] == '/' ) || ( p_path[i] == '\\' ) )
        {
            break;
        }
    }

    // copy everything after '/' into texture
    for (int p=(i+1); p<=length; p++)
    {
        new_string[t] = p_path[p];
        t++;
    }
	

	pScript->GetParams()->AddString( CRCD(0x7d99f28d,"texture"), new_string );
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | GetVramUsage | 
bool ScriptGetVramUsage( Script::CStruct *pParams, Script::CScript *pScript )
{
#	ifdef __PLAT_NGPS__
	//printf("FontVramStart = %uK \n", (NxPs2::FontVramStart/4) );
    //printf("FontVramBase = %uK \n", (NxPs2::FontVramBase/4) );
    printf("FontVramSize = %uK \n", (NxPs2::FontVramSize/4) );
    printf("Font Usage = %uK \n", ( (NxPs2::FontVramBase - NxPs2::FontVramStart )/4) );
#	endif
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// @script | CompositeObjectExists | 
bool ScriptCompositeObjectExists ( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 name;
	pParams->GetChecksum( CRCD(0xa1dc81f9,"name"), &name, Script::ASSERT );
	Obj::CObject* pObj = Obj::ResolveToObject( name );
	return ( pObj != NULL );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptClearPowerups( Script::CStruct *pParams, Script::CScript *pScript )
{
	int i;
	Mdl::Skate * pSkate = Mdl::Skate::Instance();

	for( i = 0; i < Mdl::Skate::vMAX_SKATERS; i++ )
	{
		Obj::CSkater* pSkater = pSkate->GetSkater( i );
		if( pSkater )
		{
			Obj::CSkaterStateComponent *p_component = (Obj::CSkaterStateComponent*)Obj::CCompositeObjectManager::Instance()->GetFirstComponentByType( CRC_SKATERSTATE );
			p_component->ClearPowerups();
		}
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptBroadcastProjectile( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 type;
	Mth::Vector pos, vel;
	int radius;
	float scale;
	uint32 id;
	GameNet::Manager * gamenet_man = GameNet::Manager::Instance();
	Net::Client* client;
	
	pParams->GetChecksum( CRCD(0x830ecaf,"objID"), &id );
	pParams->GetChecksum( CRCD(0x7321a8d6,"type"), &type );
	pParams->GetVector( CRCD(0x7f261953,"pos"), &pos );
	pParams->GetVector( CRCD(0xc4c809e,"vel"), &vel );
	pParams->GetFloat( CRCD(0x13b9da7b,"scale"), &scale );
	pParams->GetInteger( CRCD(0xc48391a5,"radius"), &radius );

	client = gamenet_man->GetClient( 0 );
	if( client )
	{
		Net::MsgDesc msg_desc;
		GameNet::MsgProjectile msg;
		
		msg.m_Id = id;
		msg.m_Pos = pos;
		msg.m_Vel = vel;
		msg.m_Radius = radius;
		msg.m_Scale = scale;
		msg.m_Latency = 0;
		msg.m_Type = type;

		msg_desc.m_Id = GameNet::MSG_ID_SPAWN_PROJECTILE;
		msg_desc.m_Data = &msg;
		msg_desc.m_Length = sizeof( GameNet::MsgProjectile );
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
	
		client->EnqueueMessageToServer( &msg_desc );
	}

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptBroadcastEnterVehicle ( Script::CStruct *pParams, Script::CScript *pScript )
{
	uint32 id;
	uint32 control_type;
	GameNet::Manager* gamenet_man = GameNet::Manager::Instance();
	Net::Client* client;
	
	pParams->GetChecksum( CRCD(0x5b24faaa, "SkaterId"), &id, Script::ASSERT );
	pParams->GetChecksum( CRCD(0x81cff663, "control_type"), &control_type, Script::ASSERT );
	
	client = gamenet_man->GetClient( 0 );
	if( client )
	{
		Net::MsgDesc msg_desc;
		GameNet::MsgEnterVehicle msg;
		
		msg.m_Id = id;
		msg.m_ControlType = control_type;

		msg_desc.m_Id = GameNet::MSG_ID_ENTER_VEHICLE;
		msg_desc.m_Data = &msg;
		msg_desc.m_Length = sizeof( GameNet::MsgEnterVehicle );
		msg_desc.m_Queue = Net::QUEUE_SEQUENCED;
		msg_desc.m_GroupId = GameNet::vSEQ_GROUP_PLAYER_MSGS;
	
		client->EnqueueMessageToServer( &msg_desc );
	}
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool ScriptGetCollidingPlayerAndTeam( Script::CStruct *pParams, Script::CScript *pScript )
{
	int team = -1;
	int player_id = -1;
    Script::CStruct* p_pass_back_params = pScript->GetParams();
	float nearest_distance = 999999.9999f;
	Obj::CCompositeObject* flag_obj;

	flag_obj = static_cast ( pScript->mpObject.Convert() );
	Dbg_Assert( flag_obj );
	
	int exclude_team;
	pParams->GetInteger(CRCD(0x6c2a140f, "exclude_team"), &exclude_team, Script::ASSERT);
	
	float radius_squared;
	pParams->GetFloat(CRCD(0xc48391a5, "radius"), &radius_squared, Script::ASSERT);
	radius_squared = FEET_TO_INCHES(radius_squared) * FEET_TO_INCHES(radius_squared);
	
	GameNet::PlayerInfo* player;
	for (uint32 i = 0; i < Mdl::Skate::Instance()->GetNumSkaters(); i++)
	{
		Obj::CCompositeObject* p_skater = Mdl::Skate::Instance()->GetSkater(i);
		
		if( p_skater )
		{
			player = GameNet::Manager::Instance()->GetPlayerByObjectID(p_skater->GetID());
			if (!player) continue;

			// If a player on the flag's team collides with the flag and doesn't have an enemy flag, just ignore it
			if ((player->m_Team == exclude_team) && !player->HasCTFFlag()) continue;

			// If a player already has a flag, just ignore it. They're being greedy.
			if ((player->m_Team != exclude_team ) && player->HasCTFFlag()) continue;
			
			float this_dist = Mth::DistanceSqr( p_skater->m_pos, flag_obj->GetPos());
			
			if (this_dist < radius_squared && this_dist < nearest_distance)
			{
				nearest_distance = this_dist;
				player_id = p_skater->GetID();
			}
		}
	}

	if (player_id > -1)
	{
		player = GameNet::Manager::Instance()->GetPlayerByObjectID(player_id);
		team = player->m_Team;
	}

	p_pass_back_params->AddInteger(CRCD(0x3b1f59e0, "team"), team);
	p_pass_back_params->AddInteger(CRCD(0x67e6859a, "player"), player_id);
	return true;
}

bool ScriptLobbyCheckKeyboard( Script::CStruct *pParams, Script::CScript *pScript )
{
	int num_chars;
	char makes[256];
    
	num_chars = SIO::KeyboardRead( makes );

    if( num_chars > 0 )
	{
		// Space brings up the chat interface
		if( makes[0] == 32 )
		{
			Script::CStruct* pParams;
			
			// Enter and space act as "choose" only if you're not currently using the on-screen keyboard
            pParams = new Script::CStruct;
			pParams->AddChecksum( Script::GenerateCRC( "id" ), Script::GenerateCRC( "keyboard_anchor" ));
            if( Obj::ScriptObjectExists( pParams, NULL ) == false )
			{
                Script::RunScript( "lobby_enter_kb_chat" );
                SIO::KeyboardClear();
			}
            delete pParams;
		}
    }
    return true;
}
 
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*
bool ScriptMarkRestarts ( Script::CStruct *pParams, Script::CScript *pScript )
{
	// Green:	Player1
	// Purple:	Multiplayer
	// Cyan:	Horse
	// Red:		CTF
	// Blue:	Team
	// Yellow:	Crown
	
	Script::CArray *pNodeArray = Script::GetArray(CRCD(0xc472ecc5, "NodeArray"));
	for (int i = pNodeArray->GetSize(); i--; )
	{
		uint32 color = 0;
		Mth::Vector offset;
		
		uint32 node_class;
		pNodeArray->GetStructure(i)->GetChecksum(CRCD(0x12b4e660, "Class"), &node_class);
		if (node_class == CRCD(0x1806ddf8, "Restart"))
		{
			Script::CArray *pRestartTypes;
			pNodeArray->GetStructure(i)->GetArray(CRCD(0xdd304987, "restart_types"), &pRestartTypes);
			for (int j = pRestartTypes->GetSize(); j--; )
			{
				switch (pRestartTypes->GetChecksum(j))
				{
					case CRCC(0x41639ce5, "Player1"):
						color = MAKE_RGB(0, 255, 0);
						offset.Set(0.0f, 0.0f, 0.0f);
						break;
					case CRCC(0xbae0e4da, "Multiplayer"):
						color = MAKE_RGB(255, 0, 255);
						offset.Set(1.0f, 0.0f, 0.0f);
						break;
					case CRCC(0x9d65d0e7, "Horse"):
						color = MAKE_RGB(0, 255, 255);
						offset.Set(0.0f, 1.0f, 0.0f);
						break;
					case CRCC(0xa5ad2b0b, "CTF"):
						color = MAKE_RGB(255, 0, 0);
						offset.Set(0.0f, 0.0f, 1.0f);
						break;
					case CRCC(0x3b1f59e0, "Team"):
						color = MAKE_RGB(0, 0, 255);
						offset.Set(1.0f, 1.0f, 0.0f);
						break;
					default:
						continue;
				}
			}
		}
		else if (node_class == CRCD(0xf8565321, "GenericNode"))
		{
			uint32 type;
			pNodeArray->GetStructure(i)->GetChecksum(CRCD(0x7321a8d6, "Type"), &type);
			if (type != CRCD(0xaf86421b, "Crown")) continue;
			
			color = MAKE_RGB(255, 255, 0);
			offset.Set(0.0f, 1.0f, 1.0f);
		}
		else
		{
			continue;
		}
		
		Mth::Vector pos;
		SkateScript::GetPosition(i, &pos);
		
		Gfx::AddDebugLine(pos, pos + 12.0f * offset + Mth::Vector(0.0f, 140.0f, 0.0f), color, 0, 0);
	}
	
	return true;
}
*/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace CFuncs


================================================
FILE: Code/Sk/Scripting/skfuncs.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		PS2														**
**																			**
**	Module:			Scripting												**
**																			**
**	File name:		cfuncs.h												**
**																			**
**	Created: 		09/14/2000	-	ksh										**
**																			**
*****************************************************************************/

#ifndef	__SCRIPTING_SKFUNCS_H
#define	__SCRIPTING_SKFUNCS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

namespace Script
{
	class CStruct;
	class CScript;
};
	
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace CFuncs
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

bool ScriptCurrentSkaterIsPro(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetGoalsCompleted(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetNextLevelRequirements(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetCurrentSkaterProfile(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCurrentSkaterProfileIs(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAddSkaterProfile(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAddTemporaryProfile(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRememberTemporaryAppearance(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRestoreTemporaryAppearance(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSyncPlayer2Profile(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPreloadModels( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPreloadPedestrians( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPreselectRandomPedestrians( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptReplaceCarTextures( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetSkaterProfileInfo(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetSkaterProfileInfo(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetUIFromPreferences(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetUIFromSkaterProfile(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetPreferencesFromUI(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptResetDefaultAppearance(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptResetDefaultTricks(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptResetDefaultStats(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRandomizeAppearance(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPrintCurrentAppearance(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetNeversoftSkater(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCurrentProfileIsLocked(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptResetSkaters(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetSkaterProfileProperty(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleAlwaysSpecial( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSkaterSpeedGreaterThan( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSkaterSpeedLessThan( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLastScoreLandedGreaterThan( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLastScoreLandedLessThan( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptAnyTotalScoreAtLeast( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptOnlyOneSkaterLeft( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptTotalScoreGreaterThan( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptTotalScoreLessThan( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCurrentScorePotGreaterThan( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCurrentScorePotLessThan( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSkaterGetScoreInfo( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGoalsGreaterThan( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGoalsEqualTo( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptMedalsGreaterThan( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptMedalsEqualTo( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptToggleStats(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptToggleSkaterCamMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetSkaterID( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetCurrentSkaterID( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptResetScore( Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUpdateScore( Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptPlaySkaterCamAnim( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetSkaterCamAnimSkippable( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetSkaterCamAnimShouldPause( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSkaterCamAnimFinished( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSkaterCamAnimHeld( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptKillSkaterCamAnim( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGetSkaterCamAnimParams( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGetCurrentSkaterCamAnimName( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptPlayMovingObjectAnim( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPlayCutscene( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptHasMovieStarted( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptIsMovieQueued( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetMovingObjectAnimSkippable( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetMovingObjectAnimShouldPause( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptMovingObjectAnimFinished( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptMovingObjectAnimHeld( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptKillMovingObjectAnim( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptSetSkaterCamLerpReductionTimer( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptReloadSkaterCamAnim( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSkaterDebugOn(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSkaterDebugOff(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptVibrationIsOn(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptVibrationOn(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptVibrationOff(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAutoKickIsOn(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAutoKickOn(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAutoKickOff(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSpinTapsAreOn(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSpinTapsOn(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSpinTapsOff(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetCurrentProDisplayInfo(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetPlayerAppearance(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetPlayerFacePoints(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetPlayerFacePoints(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetPlayerFaceTexture(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetPlayerFaceOverlayTexture(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptClearPlayerFaceTexture(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPlayerFaceIsValid(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSelectCurrentSkater(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCareerStartLevel(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCareerLevelIs(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetRecordText(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUpdateRecords(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCareerReset(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnSetGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptJustGotGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnSetFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptJustGotFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetGlobalFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnSetGlobalFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetGlobalFlag(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptProfileEquals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptIsCareerMode(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptClearScoreGoals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSetScoreGoal(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEndRun(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptShouldEndRun(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptInitializeSkaters(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEndRunSelected(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAllSkatersAreIdle(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptFirstTrickStarted( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptFirstTrickCompleted( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCalculateFinalScores(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptReinsertSkaters(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnhookSkaters(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptApplySplitScreenOptions(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptStartCompetition(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptStartCompetitionRun(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEndCompetitionRun(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptIsTopJudge(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptPlaceIs(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRoundIs(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEndCompetition(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptCompetitionEnded(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptStartHorse(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEndHorse(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptHorseEnded(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptHorseStatusEquals(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptStartHorseRun(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEndHorseRun(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSwitchHorsePlayers(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetHorseString(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptIsCurrentHorseSkater(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptApplyToHorsePanelString(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptApplyToSkaterProfile(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptApplyColorToSkaterProfile(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRefreshSkaterColors(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRefreshSkaterScale(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRefreshSkaterVisibility(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRefreshSkaterUV(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAwardStatPoint(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAwardSpecialTrickSlot(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUpdateSkaterStats(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUpdateInitials( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptNewRecord(  Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptTrickOffAllObjects( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGameModeSetScoreDegradation( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGameModeSetScoreAccumulation( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptResetScoreDegradation( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSkaterIsBraking( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptLocalSkaterExists( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptInitSkaterModel(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRefreshSkaterModel(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptEditPlayerAppearance(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetCurrentSkaterProfileIndex(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptGetCustomSkaterName(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptResetScorePot( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptPrintSkaterStats( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptPrintSkaterStats2( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptPrintSkaterPosition( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetSkaterPosition( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetSkaterVelocity( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetStatValue( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetNumStatPointsAvailable( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptUnlockSkater( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetActualCASOptionStruct( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetActualPlayerAppearancePart( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetPlayerAppearancePart( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetPlayerAppearanceColor( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetPlayerAppearanceScale( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptSetPlayerAppearanceUV( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptFlushDeadObjects( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptBindTrickToKeyCombo( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetKeyComboBoundToTrick( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptUpdateTrickMappings( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetConfigurableTricksFromType( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptTrickIsLocked( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetTrickDisplayText( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetSpecialTrickInfo( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptGetTrickType( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGetIndexOfItemContaining( Script::CStruct* pParams, Script::CScript* pScript );
bool ScriptForEachSkaterName( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptForEachSkaterProfile( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptResetAllToDefaultStats( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptResetAllToDefaultProfile( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptResetToDefaultProfile( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGetLevelRecords( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptResetComboRecords( Script::CStruct* pParams, Script::CScript* pScript );

bool ScriptGetSkaterProfileInfoByName( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSetSkaterProfileInfoByName( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptGetNumberOfTrickOccurrences( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptGetNumSoundtracks( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGetSoundtrackName( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptControllerBoundToDifferentSkater( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptBindControllerToSkater( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptBindFrontEndToController( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptControllerBoundToSkater( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptGetKeyComboArrayFromTrickArray( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptFirstInputReceived( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptVibrateController( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLockCurrentSkaterProfileIndex( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptSetSpecialTrickInfo( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptInterpolateParameters( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptPlaySkaterStream ( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGetTextureFromPath ( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptGetVramUsage ( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptCompositeObjectExists ( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptClearPowerups( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptBroadcastProjectile( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptBroadcastEnterVehicle ( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptGetCollidingPlayerAndTeam( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptLobbyCheckKeyboard( Script::CStruct *pParams, Script::CScript *pScript );


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace CFuncs

#endif	// __SCRIPTING_SKFUNCS_H



================================================
FILE: Code/Sk/heap_sizes.h
================================================
#ifndef __SK_HEAP_SIZES_H
#define __SK_HEAP_SIZES_H

// Sizes of heaps

#define	EXTRA 1000000


#ifdef __PLAT_WN32__

#define	_SCRIPT_HEAP_SIZE				(1024)
#define	SCRIPT_CACHE_HEAP_SIZE			(1024)
#define	FRONTEND_HEAP_SIZE				(1024)
#define	NETWORK_HEAP_qSIZE				(1024)
#define PROFILER_HEAP_SIZE					(1024)
#define	SKATERINFO_HEAP_SIZE			(1024)
#define	SKATER_HEAP_SIZE				(1024)		// default size of skater heap
#define SKATER_GEOM_HEAP_SIZE			(1024)
#define BOOTSTRAP_FRONTEND_HEAP_SIZE	(1024)
#define INTERNET_HEAP_SIZE				(1024)
#define NETMISC_HEAP_SIZE				(1024)
#define	THEME_HEAP_SIZE     			 (1024)		// theme textures heap size

#else

#ifdef	__NOPT_ASSERT__
// K: On debug builds line number info is included in the qb's so add in another 500K for it.
// On release builds the line number info is excluded by passing the nolinenumbers switch to
// cleanass when it is called from easyburn.bat
#define	_SCRIPT_HEAP_SIZE					(3824000+500000+130000)
#else
#define	_SCRIPT_HEAP_SIZE					(3824000+130000)	   
#endif

// Mick: Note the FRONTEND_HEAP_SIZE encompasses both Frontend and Net heaps.
// as they share the same region, with Net being top down.
#define	FRONTEND_HEAP_SIZE					(1100000)  	 	// was 800000

#define NETMISC_HEAP_SIZE					(160000)
#define	BOOTSTRAP_FRONTEND_HEAP_SIZE		(FRONTEND_HEAP_SIZE-100000)		// we have no network play in bootstrap mode  
#define INTERNET_HEAP_SIZE					(450000)
#define PROFILER_HEAP_SIZE					(60000)

// GJ:  I temporarily increased the size
// of the skater info heap until we can figure
// out how the face texture pathway is going to
// work (right now, I need a heap that wouldn't
// get destroyed when changing levels, to store
// 17K worth of temporary face texture data)
//#define	SKATERINFO_HEAP_SIZE			 	(40000)
// Mick: Increased it again for 2P
#define	SKATERINFO_HEAP_SIZE			 	(60000+2000+20000)	// extra 2000 for skater cams +20K for 2p fragmentation concerns

#define	SKATER_HEAP_SIZE				 	(120000-40000+1000-2000)	// default size of skater heap, minus 2000 since skater cams moved to skater info heap
#define	SKATER_GEOM_HEAP_SIZE			 	(680000 - 16000)		// default size of skater heap, plus a little extra cause we were running out for E3
#define	THEME_HEAP_SIZE     			 	(204800)		// theme textures heap size

#ifdef	__PLAT_NGPS__
#ifdef	__NOPT_ASSERT__
// On a regular development build, the memory usage is very different
// to a "final=" build.  Firstly there is the extra debug code, mostly assertions,
// but also memory blocks have a 32 byte rather than 16 byte header
// and many data structures have extra fields for debugging purposes
// this all means that the game will not fit in 32MB, so to fix this, the
// script heap is placed in "debug" memory (>32MB)
// Just that alone would result in there seeming to be MORE memory avaialbe in the normal build
// so to conteeract that, we allocate a block off the bottom_up heap,
// The size of this block is SCRIPT_HEAP_SIZE-DEBUG_ADJUSTMENT
// So you need to adjust DEBUG_ADJUSTMENT so the amount of free memory
// on the main heap is the same in a development build
// and in a "final=" cd build, for example, if you had for CD final build:
// 
// Name            Used  Frag  Free   Min  Blocks                                  
// --------------- ----- ----- ---- ------ ------                                  
//     Top Down:  3942K    0K  894K  884K   3933 
//     BottomUp: 23275K   23K  894K  884K  10432  
//
// and on a regular build:
//
//     Top Down:  4004K    0K  579K  523K   3933   
//     BottomUp: 22115K   23K  579K  523K  10601
//
// Then you see that the regular build is underreporting the actual amount of free memory
// and you should in theory add (894-579) = 315K
// In practice it's a good idea to not push things right to the wire, as memory
// usage can vary in differing situation (specifically when the number of blocks vary,
// like a large number of small allocations vs a small number of large allocations.)
// Since the above was from a worst case situation (NJ), I feel that 200K would be appropiate
	
#define	DEBUG_ADJUSTMENT					(1126400 + 200000 + 300000 + 100000 + 350000 ) // (1126400)		// difference in free memory for "final=" vs debug build
#endif // __NOPT_ASSERT__
#endif // __PLAT_NGPS__

#endif // __PLAT_WN32__

#ifdef __PLAT_XBOX__
// Just need to override some of these values - want to keep as much the same as possible tho.
#undef	SKATER_HEAP_SIZE
#define	SKATER_HEAP_SIZE				( 120000 - 40000 - 2000)

#undef	SKATER_GEOM_HEAP_SIZE
#define	SKATER_GEOM_HEAP_SIZE			( 480000 )

#undef	THEME_HEAP_SIZE
#define	THEME_HEAP_SIZE					( 307200 )

#undef	FRONTEND_HEAP_SIZE
#define	FRONTEND_HEAP_SIZE				( 1050000 )


#endif // __PLAT_XBOX__

#ifdef __PLAT_NGC__
// Just need to override some of these values - want to keep as much the same as possible tho.
#undef	SKATER_HEAP_SIZE
#define	SKATER_HEAP_SIZE				( ( 120000 - 40000 - 2000) + 1000 )
#undef	PROFILER_HEAP_SIZE
#define PROFILER_HEAP_SIZE					  (40000)
#undef	FRONTEND_HEAP_SIZE
#define	FRONTEND_HEAP_SIZE				( 650000 )
#undef	SKATER_GEOM_HEAP_SIZE
#define	SKATER_GEOM_HEAP_SIZE			 (470000)		// default size of skater heap
#undef	THEME_HEAP_SIZE
#define	THEME_HEAP_SIZE     			 	(330000)		// theme textures heap size
#define	SCRIPT_HEAP_SIZE (_SCRIPT_HEAP_SIZE-(300000+(1024*1024)))

#undef	SKATERINFO_HEAP_SIZE
#define	SKATERINFO_HEAP_SIZE			 	(60000+2000+20000+18000)	// extra 2000 for skater cams +20K for 2p fragmentation concerns

#define	AUDIO_HEAP_SIZE			 			(310*1024)
#else
// All platforms except for NGC
#define	SCRIPT_HEAP_SIZE _SCRIPT_HEAP_SIZE
#endif // __PLAT_NGC__


#endif // __SK_HEAP_SIZES_H



================================================
FILE: Code/Sk/language.h
================================================
#ifndef	__SK_LANGUAGE_H
#define	__SK_LANGUAGE_H

#ifdef __PLAT_XBOX__
#include 
#endif // __PLAT_XBOX__

// These only apply if PAL
#define ENGLISH 1
#define FRENCH 0
#define GERMAN 0

inline bool IsEnglish( void )
{
#	ifdef __PLAT_XBOX__
	DWORD lang = XGetLanguage();
	if( lang == XC_LANGUAGE_ENGLISH )
		return true;
	else
	{
		// For NTSC, the only language allowed is English.
		if( XGetVideoStandard() != XC_VIDEO_STANDARD_PAL_I )
		{
			return true;
		}

		// Any languages other than French and German should also be considered English.
		if(( lang != XC_LANGUAGE_GERMAN ) && ( lang != XC_LANGUAGE_FRENCH ))
		{
			return true;
		}
	}
	return false;
//		return true;		// For now...
#	else
#	if ENGLISH
	return true;
#	else
	return false
#	endif // ENGLISH
#	endif // __PLAT_XBOX__
}



#endif



================================================
FILE: Code/Sk/ngc/crt0.s
================================================
##
## SN Systems C/C++ Startup Module
##
#	.include "defs.s"			# equates
#
#	.section .data
#wdbmsg:
#	.string	"Waiting for SN Debugger...\n"
#	.byte	0
#	.align	4
#
#	.section .init
#	.global	__start				# the only thing we need to export
#	
#__start:
#    lis     %r1, _stack_addr@h        	# set r1, r2, r13 as required by PPC EABI
#    ori     %r1, %r1, _stack_addr@l
#
#    lis     %r2, _SDA2_BASE_@h			# __SDA2_BASE_ is generated by linker */
#    ori     %r2, %r2, _SDA2_BASE_@l
#
#    lis     %r13, _SDA_BASE_@h			# _SDA_BASE_ is generated by linker
#    ori     %r13, %r13, _SDA_BASE_@l
#
#    bl      __init_hardware				# why do we have to call this?
#
#    li      %r0, -1               		# leave stack space for main params and lr stash
#    stwu    %r1, -8(%r1)               
#    stw     0, 4(%r1)                
#    stw     0, 0(%r1)
#
## Init the BSS sections to 0
#
#	lis		%r3,_f_bss@ha
#	addi	%r3,%r3,_f_bss@l
#	li		%r4,0
#	lis		%r5,_e_bss@ha
#	addi	%r5,%r5,_e_bss@l
#	subf	%r5,%r3,%r5
#	bl		memset
#
#	lis		%r3,_f_sbss@ha
#	addi	%r3,%r3,_f_sbss@l
#	li		%r4, 0
#	lis		%r5,_e_sbss@ha
#	addi	%r5,%r5,_e_sbss@l
#	subf	%r5,%r3,%r5
#	bl		memset
#
#	li		%r4,0
#	lis		%r5,0x8000			# use cached address space
#	stw		%r4,0x44(%r5)		# write it to the DBException structure in low mem
#
## OK, now check to see if debugging is enabled
#	li		%r29,0				# r29 == 0  =>  debugging not enabled
#	lis		%r6,BOOTINFO2_ADDR@ha
#	addi	%r6,%r6,BOOTINFO2_ADDR@l
#	lwz		%r6,0(%r6)			# get r6 = ptr to BOOTINFO2
#	cmpi	%cr0,%r6,0
#	beq		_skipdbg			# skip if BOOTINFO2 does not exist
#
#	lwz		%r3,OS_BI2_DEBUGFLAG_OFFSET(%r6)	# this will be 2 if we are debugging
#	cmpli	%cr0,%r3,2
#	blt		_skipdbg			# 2=>DDH, 3=>GDEV
#
#	bl		SNDebugInit			# can we reach it with a bl? May need an "li32 + blrl"
#	li		%r29,1
#
#_skipdbg:
#	bl		_ParseCmdLine		# must do it here (before OSInit) because it may adjust ARENAHI
#	mr		%r30,%r3
#	mr		%r31,%r4
#
#	bl      DBInit
#	bl      OSInit				# this will make the callback to EnableMetroTRKInterrupts
#
##    bl      __init_user			# C++ constructors. No, now handled in call to _main() from main()
#
#	cmpi	%cr0,%r29,0
#	beq		_sktr
#
#	lis		%r3,wdbmsg@h
#	ori		%r3,%r3,wdbmsg@l
#	bl		OSReport
#
#	mfmsr	%r5
#	lis		%r4,(~MSR_EE)@h		# li32 r4,MSR_EE (=0xFFFF7FFF)
#	ori		%r4,%r4,(~MSR_EE)@l
#	and		%r4,%r5,%r4			# disable external ints (so SRR stays safe)
#	mtmsr	%r4
#	sync						# make sure ints are off before next bit of code
#	ori		%r5,%r5,MSR_BE		# and set BRANCH TRACE bit (will go of after bl main)
#	mtspr	spr_srr1,%r5
#
#	lis		%r4,_sktr@h
#	ori		%r4,%r4,_sktr@l		# li32	%r4,_sktr
#	mtspr	spr_srr0,%r4
#	rfi
#
#_sktr:
#	bl      pre_main
#
### Fill stack with 55.
##
##	lis		%r4,_stack_end@ha
##	addi	%r4,%r4,_stack_end@l
##	li		%r5,0x55
##	lis		%r6,_stack_addr@ha
##	addi	%r6,%r6,_stack_addr@l
##	subf	%r6,%r4,%r6
##	li		%r6,0x80
##	subf	%r6,%r6,%r4
##	bl		memset
#
#
#
#
#	mr		%r3,%r30			# restore argc
#	mr		%r4,%r31			# and argv
#	bl      main				# so we can call main()
#    bl      post_main
#    b       exit
#
#
## process command line
#_ParseCmdLine:
#	lis		%r6,BOOTINFO2_ADDR@ha
#	addi	%r6,%r6,BOOTINFO2_ADDR@l
#	lwz		%r5,0(%r6)			# get r6 = ptr to BOOTINFO2
#	cmpi	%cr0,%r5,0
#	bne		_gargs
#
#_noargs:
#    li      %r3,0
#    li      %r4,0
#	blr
#
#_gargs:
#	lwz		%r6,OS_BI2_ARGOFFSET_OFFSET(%r5)	#argument offset
#	cmpi	%cr0,%r6,0
#	beq		_noargs				# no arguments
#	add		%r6,%r5,%r6			# argument start
#
#	lwz		%r3,0(%r6)			# get argc
#	cmpi	%cr0,%r3,0
#	beq		_noargs				# shouldn't happen
#
#	addi	%r4,%r6,4			# argv
#	mtctr	%r3
#_lp:
#	addi	%r6,%r6,4
#	lwz		%r7,0(%r6)
#	add		%r7,%r7,%r5
#	stw		%r7,0(%r6)
#	bdnz	_lp
#
## set ARENAHI to be *below* the arguments
#	lis		%r5,ARENAHI_ADDR@ha
#	addi	%r5,%r5,ARENAHI_ADDR@l
#	clrrwi	%r7,%r4,5			# align it by 32bytes
#	stw		%r7,0(%r5)
#
#	blr
#
## Bill's hack Yuk Yuk
#
#	.section .data
#	.extern read
#	.extern pre_main
#	.extern main
#	.extern __mod2i
#LinkFiddle:
#	.long read
#	.long __mod2i



================================================
FILE: Code/Sk/ngc/defs.s
================================================
# register numbers for mfspr/mtspr

	.equ	spr_xer,1
	.equ	spr_lr,8
	.equ	spr_ctr,9

	.equ	spr_upmc1,937
	.equ	spr_upmc2,938
	.equ	spr_upmc3,941
	.equ	spr_upmc4,942

	.equ	spr_usia,939

	.equ	spr_ummcr0,936
	.equ	spr_ummcr1,940

	.equ	spr_hid0,1008
	.equ	spr_hid1,1009

	.equ	spr_pvr,287

	.equ	spr_ibat0u,528
	.equ	spr_ibat0l,529
	.equ	spr_ibat1u,530
	.equ	spr_ibat1l,531
	.equ	spr_ibat2u,532
	.equ	spr_ibat2l,533
	.equ	spr_ibat3u,534
	.equ	spr_ibat3l,535

	.equ	spr_dbat0u,536
	.equ	spr_dbat0l,537
	.equ	spr_dbat1u,538
	.equ	spr_dbat1l,539
	.equ	spr_dbat2u,540
	.equ	spr_dbat2l,541
	.equ	spr_dbat3u,542
	.equ	spr_dbat3l,543

	.equ	spr_sdr1,25

	.equ	spr_sprg0,272
	.equ	spr_sprg1,273
	.equ	spr_sprg2,274
	.equ	spr_sprg3,275

	.equ	spr_dar,19
	.equ	spr_dsisr,18

	.equ	spr_srr0,26
	.equ	spr_srr1,27

	.equ	spr_ear,282

	.equ	spr_dabr,1013

	.equ	spr_tbl,284
	.equ	spr_tbu,285

	.equ	spr_l2cr,1017

	.equ	spr_dec,22

	.equ	spr_iabr,1010

	.equ	spr_pmc1,953
	.equ	spr_pmc2,954
	.equ	spr_pmc3,957
	.equ	spr_pmc4,958

	.equ	spr_sia,955

	.equ	spr_mmcr0,952
	.equ	spr_mmcr1,956

	.equ	spr_thrm1,1020
	.equ	spr_thrm2,1021
	.equ	spr_thrm3,1022

	.equ	spr_ictc,1019

# gekko registers
	.equ	spr_gqr0,912
	.equ	spr_gqr1,913
	.equ	spr_gqr2,914
	.equ	spr_gqr3,915
	.equ	spr_gqr4,916
	.equ	spr_gqr5,917
	.equ	spr_gqr6,918
	.equ	spr_gqr7,919

	.equ	spr_hid2,920

	.equ	spr_wpar,921

	.equ	spr_dmau,922
	.equ	spr_dmal,923
# end of gekko registers

	.equ	MSR_POW,0x00040000	# Power Management
	.equ	MSR_ILE,0x00010000	# Interrupt Little Endian
	.equ	MSR_EE,0x00008000	# external interrupt
	.equ	MSR_PR,0x00004000	# privilege level(should be 0)
	.equ	MSR_FP,0x00002000	# floating point available
	.equ	MSR_ME,0x00001000	# machine check enable
	.equ	MSR_FE0,0x00000800	# floating point exception enable
	.equ	MSR_SE,0x00000400	# single step trace enable
	.equ	MSR_BE,0x00000200	# branch trace enable
	.equ	MSR_FE1,0x00000100	# floating point exception enable
	.equ	MSR_IP,0x00000040	# Exception prefix
	.equ	MSR_IR,0x00000020	# instruction relocate
	.equ	MSR_DR,0x00000010	# data relocate
	.equ	MSR_PM,0x00000004	# Performance monitor marked mode
	.equ	MSR_RI,0x00000002	# Recoverable interrupt
	.equ	MSR_LE,0x00000001	# Little Endian

	.equ	MSR_POW_BIT,13		# Power Management
	.equ	MSR_ILE_BIT,15		# Interrupt Little Endian
	.equ	MSR_EE_BIT,16		# external interrupt
	.equ	MSR_PR_BIT,17		# privilege level (should be 0)
	.equ	MSR_FP_BIT,18		# floating point available
	.equ	MSR_ME_BIT,19		# machine check enable
	.equ	MSR_FE0_BIT,20		# floating point exception enable
	.equ	MSR_SE_BIT,21		# single step trace enable
	.equ	MSR_BE_BIT,22		# branch trace enable
	.equ	MSR_FE1_BIT,23		# floating point exception enable
	.equ	MSR_IP_BIT,25		# Exception prefix
	.equ	MSR_IR_BIT,26		# instruction relocate
	.equ	MSR_DR_BIT,27		# data relocate
	.equ	MSR_PM_BIT,29		# Performance monitor marked mode
	.equ	MSR_RI_BIT,30		# Recoverable interrupt
	.equ	MSR_LE_BIT,31		# Little Endian


	.equ	HID2_LSQE,0x80000000	# L/S quantize enable
	.equ	HID2_WPE,0x40000000	# Write pipe enable
	.equ	HID2_PSE,0x20000000	# Paired single enable
	.equ	HID2_LCE,0x10000000	# Locked cache enable

	.equ	HID2_DCHERR,0x00800000	# ERROR: dcbz_l cache hit
	.equ	HID2_DNCERR,0x00400000	# ERROR: DMA access to normal cache
	.equ	HID2_DCMERR,0x00200000	# ERROR: DMA cache miss error
	.equ	HID2_DQOERR,0x00100000	# ERROR: DMA queue overflow
	.equ	HID2_DCHEE,0x00080000	# dcbz_l cache hit error enable
	.equ	HID2_DNCEE,0x00040000	# DMA access to normal cache error enable
	.equ	HID2_DCMEE,0x00020000	# DMA cache miss error error enable
	.equ	HID2_DQOEE,0x00010000	# DMA queue overflow error enable

	.equ	HID2_DMAQL_MASK,0x0F000000	# DMA queue length mask
	.equ	HID2_DMAQL_SHIFT,24		# DMA queue shift

	.equ	HID2_LSQE_BIT,0
	.equ	HID2_WPE_BIT,1
	.equ	HID2_PSE_BIT,2
	.equ	HID2_LCE_BIT,3

	.equ	HID2_DCHERR_BIT,8   
	.equ	HID2_DNCERR_BIT,9
	.equ	HID2_DCMERR_BIT,10
	.equ	HID2_DQOERR_BIT,11
	.equ	HID2_DCHEE_BIT,12
	.equ	HID2_DNCEE_BIT,13
	.equ	HID2_DCMEE_BIT,14
	.equ	HID2_DQOEE_BIT,15

#------------------------------------------

	.equ	EX_SYSTEM_RESET,0
	.equ	EX_MACHINE_CHECK,1
	.equ	EX_DSI,2
	.equ	EX_ISI,3
	.equ	EX_EXTERNAL_INTERRUPT,4
	.equ	EX_ALIGNMENT,5
	.equ	EX_PROGRAM,6
	.equ	EX_FLOATING_POINT,7
	.equ	EX_DECREMENTER,8
	.equ	EX_SYSTEM_CALL,9
	.equ	EX_TRACE,10
	.equ	EX_PERFORMANCE_MONITOR,11
	.equ	EX_BREAKPOINT,12
	.equ	EX_SYSTEM_INTERRUPT,13
	.equ	EX_THERMAL_INTERRUPT,14
	.equ	EX_MAX,15

	.equ	REAL_EX_SYSTEM_RESET,0
	.equ	REAL_EX_MACHINE_CHECK,1
	.equ	REAL_EX_DSI,2
	.equ	REAL_EX_ISI,3
	.equ	REAL_EX_EXTERNAL_INTERRUPT,4
	.equ	REAL_EX_ALIGNMENT,5
	.equ	REAL_EX_PROGRAM,6
	.equ	REAL_EX_FLOATING_POINT,7
	.equ	REAL_EX_DECREMENTER,8
	.equ	REAL_EX_SYSTEM_CALL,11
	.equ	REAL_EX_TRACE,12
	.equ	REAL_EX_PERFORMANCE_MONITOR,14
	.equ	REAL_EX_BREAKPOINT,18
	.equ	REAL_EX_SYSTEM_INTERRUPT,21
	.equ	REAL_EX_THERMAL_INTERRUPT,22
	.equ	REAL_EX_MAX,15

#------------------------------------------

	.equ	CMD_OK,0
	.equ	CMD_PK_2SMALL,1
	.equ	CMD_COMMSERR,2

#-----------------------------------------
# Boot Info2 is a 8K byte structure that is loaded to himem
# (lower than FST) by apploader.

	.equ	BOOTINFO2_ADDR,0x800000F4

	.equ	OS_BI2_SIZE,0x2000      			# 8K 
	.equ	OS_BI2_DEBUGMONSIZE_OFFSET,0x0000
	.equ	OS_BI2_SIMMEMSIZE_OFFSET,0x0004
	.equ	OS_BI2_ARGOFFSET_OFFSET,0x0008
	.equ	OS_BI2_DEBUGFLAG_OFFSET,0x000c
	.equ	OS_BI2_TRKLOCATION_OFFSET,0x0010
	.equ	OS_BI2_TRKSIZE_OFFSET,0x0014
	.equ	OS_BI2_COUNTRYCODE_OFFSET,0x0018
	.equ	OS_BI2_ARGSIZE_MAX,0x1000      		# 4K
	.equ	OS_BI2_COUNTRYCODE_JP,0
	.equ	OS_BI2_COUNTRYCODE_US,1

	.equ	ARENAHI_ADDR,0x80000034


================================================
FILE: Code/Sk/product_codes.h
================================================
#ifndef	__SK_PRODUCT_CODES_H
#define	__SK_PRODUCT_CODES_H

namespace Config
{

// The mem card headers
// These are also used to derive the elf name for comparison so that the language can be
// autodetected, in sys\config\ngps\p_config.cpp
#define NGPS_NTSC "BASLUS-20731"    // Mick: 20731 is the official THPS5 NTSC SLUS number
#define NGPS_PAL_ENGLISH "BESLES-51848"
#define NGPS_PAL_FRENCH "BESLES-51851"   // <<<<<<<<<<<<<<<<<<<  just guessing here.....
#define NGPS_PAL_GERMAN "BESLES-51852"
#define NGPS_PAL_ITALIAN "BESLES-51853"
#define NGPS_PAL_SPANISH "BESLES-51854"

};

#endif



================================================
FILE: Code/Sk/template.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:														**
**																			**
**	Module:															**
**																			**
**	File name:		.h											**
**																			**
**	Created:		xx/xx/xx	- xxx										**
**																			**
*****************************************************************************/

#ifndef	___H
#define	___H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							     Clase Definitions							**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

#endif	// ___H

================================================
FILE: Code/Sys/Config/NGC/p_config.cpp
================================================
// Config Manager stuff. KSH 20 Mar 2002
#include 
#include 
#include 

extern void * get_font_file_address( const char * p_filename );

namespace Config
{

void Plat_Init(sint argc, char** argv)
{
	gHardware=HARDWARE_NGC;
	
	// I don't know yet how to autodetect the language for this platform, so
	// just set it to English for the moment.
	
	switch ( OSGetLanguage() )
	{
		case OS_LANG_GERMAN:
			gLanguage=LANGUAGE_GERMAN;
			break;
		case OS_LANG_FRENCH:
			gLanguage=LANGUAGE_FRENCH;
			{
				// Load French font file.
				void * p = get_font_file_address( "small" );
				void *p_FH = File::Open( "fonts\\small_fr.fnt.ngc", "rb" );
				if( p_FH )
				{
					int size = File::GetFileSize( p_FH );
					char * p_new = new char[size];
					File::Read( p_new, size, 1, p_FH );
					memcpy( p, p_new, size );
					delete p_new;
				}
			}
			break;
		case OS_LANG_SPANISH:
		case OS_LANG_ITALIAN:
		case OS_LANG_DUTCH:
		case OS_LANG_ENGLISH:
		default:
			gLanguage=LANGUAGE_ENGLISH;
			break;
	}

	// Autodetect these somehow ...
	if ( VIGetTvFormat() == VI_PAL )
	{
		gDisplayType=DISPLAY_TYPE_PAL;
		gFPS=50;
	}
	else
	{
		gDisplayType=DISPLAY_TYPE_NTSC;
		gFPS=60;
	}
	
	// See if we're on a devkit or not by checking the amount of memory
	int megs = (uint32)OSGetArenaHi() - (uint32)OSGetArenaLo();

	if ( megs >= ( 1024 * 1024 * 24 ) )
	{
		// We're on a devkit
		gCD=false;
	}
	else
	{
		// We're on an NR-Reader
		gCD=true;
	}
	
	gpMemCardHeader="SK5";
}

} // namespace Config



================================================
FILE: Code/Sys/Config/NGPS/p_config.cpp
================================================
// Config Manager stuff. KSH 20 Mar 2002
#include 
#include 
#include 

extern "C"
{
int snputs(const char* pszStr);
}

namespace Config
{

static char s_elf_name[64];  // will be 'local' or 'skate5'

const char * GetElfName()
{
	return (const char *)s_elf_name;
}

static const char *sGenerateElfName(const char *p_memCardHeader)
{
	Dbg_MsgAssert(strlen(p_memCardHeader)==12,("Bad length for mem card header '%s'\n",p_memCardHeader));
	static char sp_elf_name[50];

	// Note: p_memCardHeader will have the form "BASLUS-20731"
	// "BASLUS-20731" needs to map to an elf name of "cdrom:\SLUS_207.31;1"
	
	sprintf(sp_elf_name,"cdrom0:\\SL%cS_%c%c%c.%c%c;1",
			p_memCardHeader[4],
			p_memCardHeader[7],
			p_memCardHeader[8],
			p_memCardHeader[9],
			p_memCardHeader[10],
			p_memCardHeader[11]);
	return sp_elf_name;
}

void Plat_Init(sint argc, char** argv)
{
	gHardware=HARDWARE_UNDEFINED;
	gGotExtraMemory=false;
	gCD=false;


	
	// must check to see if the supplied filename starts "cdrom0:\THPS4\"    SLUS_207.31;1

	// if the first real argument exists and starts with a digit, then assume
	// that we are running from a bootstrap (as we will just have been passed the language in argv[1])
	// and preempt any other parameters...
	// so assume running on a regular PS2, from the CD
	if (argc > 1)
	{
		if (argv[1][0] >= '0' && argv[1][0] <= '9')
		{
			printf ("argv[1][0] is a digit, so assuming bootstrap format, using \\thps4 directory\n");
			gBootstrap=true;
			gSonyBootstrap = true;
			gHardware=HARDWARE_PS2;
			gGotExtraMemory=false;
			gCD=true;

			// the rest we could, in theory, set from the sceLibDemo calls......			
			gLanguage=LANGUAGE_ENGLISH;
			gTerritory=TERRITORY_UNDEFINED;
			gDisplayType=DISPLAY_TYPE_NTSC;
			gFPS=60;

			
			return;
		}
		else
		{
			printf ("argv[1][0] (%s) is not digit, so it's a regular boot, using root\n",argv[1]);
		}
	}
	else
	{
		printf ("no arguments, so it's a regular boot, using root\n");
	}
	

	
	// If the filename ends in .elf, then extract out the name
	// c:\skate5\build\NGPSgnu\local.elf
	if (stricmp(argv[0]+strlen(argv[0])-4,".elf")==0)
	{
		char *p = argv[0]+strlen(argv[0])-5;		// letter before the .elf
		while (*p != '\\') p--;
		p++; // first letter of the name
		char *q = &s_elf_name[0];
		while (*p != '.') *q++ = *p++;
		*q++ = 0;
	}
	

	if (!argv[0] || stricmp(argv[0]+strlen(argv[0])-4,".elf")==0)
	{
		gHardware=HARDWARE_PS2_DEVSYSTEM;
		gGotExtraMemory=true;
		gCD=false;
	}
	else
	{
		// It's just a normal PS2
		gHardware=HARDWARE_PS2;
		gGotExtraMemory=false;
		gCD=true;
	}
	
	// Check command line to see if they're using ProView
	if (snputs("Plat_Init detected ProView ...\n")!=-1)
	{
		gHardware=HARDWARE_PS2_PROVIEW;
		gGotExtraMemory=false;
		gCD=false;
	}
	
	// Check command line to see if they want to force gGotExtraMemory on or off
	if (CommandLineContainsFlag("GotExtraMemory",argc,argv))		
	{
		gGotExtraMemory=true;
	}
	if (CommandLineContainsFlag("NoExtraMemory",argc,argv))		
	{
		gGotExtraMemory=false;
	}
	
	// Detect the language from the product code.
	gLanguage=LANGUAGE_ENGLISH;
	gpMemCardHeader=NGPS_NTSC;
	if (argv[0])
	{
		// Doing a stricmp just in case it changes to be CDROM later or something.
		if (stricmp(argv[0],sGenerateElfName(NGPS_NTSC))==0)
		{
			gLanguage=LANGUAGE_ENGLISH;
			gpMemCardHeader=NGPS_NTSC;
		}
		else if (stricmp(argv[0],sGenerateElfName(NGPS_PAL_ENGLISH))==0)
		{
			gLanguage=LANGUAGE_ENGLISH;
			gpMemCardHeader=NGPS_PAL_ENGLISH;
		}
		else if (stricmp(argv[0],sGenerateElfName(NGPS_PAL_FRENCH))==0)
		{
			gLanguage=LANGUAGE_FRENCH;
			gpMemCardHeader=NGPS_PAL_FRENCH;
		}
		else if (stricmp(argv[0],sGenerateElfName(NGPS_PAL_GERMAN))==0)
		{
			gLanguage=LANGUAGE_GERMAN;
			gpMemCardHeader=NGPS_PAL_GERMAN;
		}
		else if (stricmp(argv[0],sGenerateElfName(NGPS_PAL_ITALIAN))==0)
		{
			gLanguage=LANGUAGE_ITALIAN;
			gpMemCardHeader=NGPS_PAL_ITALIAN;
		}
		else if (stricmp(argv[0],sGenerateElfName(NGPS_PAL_SPANISH))==0)
		{
			gLanguage=LANGUAGE_SPANISH;
			gpMemCardHeader=NGPS_PAL_SPANISH;
		}
	}	

	// They may want to force the language to be something else from the command line ...
	const char *p_language=GetCommandLineParam("Language",argc,argv);
	if (p_language)
	{
		if (stricmp(p_language,"English")==0)
		{
			gLanguage=LANGUAGE_ENGLISH;
			gpMemCardHeader=NGPS_NTSC;
		}
		else if (stricmp(p_language,"French")==0)
		{
			gLanguage=LANGUAGE_FRENCH;
			gpMemCardHeader=NGPS_PAL_FRENCH;
		}
		else if (stricmp(p_language,"German")==0)
		{
			gLanguage=LANGUAGE_GERMAN;
			gpMemCardHeader=NGPS_PAL_GERMAN;
		}
		else if (stricmp(p_language,"Italian")==0)
		{
			gLanguage=LANGUAGE_ITALIAN;
			gpMemCardHeader=NGPS_PAL_ITALIAN;
		}
		else if (stricmp(p_language,"Spanish")==0)
		{
			gLanguage=LANGUAGE_SPANISH;
			gpMemCardHeader=NGPS_PAL_SPANISH;
		}
		else
		{
			Dbg_MsgAssert(0,("Language '%s' not supported",p_language));
		}
	}


	
	gTerritory=TERRITORY_UNDEFINED;

	gDisplayType=DISPLAY_TYPE_NTSC;
	gFPS=60;
	
	// Figure out if it is PAL from the product code ...
	if (argv[0])
	{
		char p_temp[50];
		strncpy(p_temp,argv[0],12);
		p_temp[12]=0; // strncpy won't terminate
		// Doing a stricmp just in case it changes to be CDROM later or something.
		if (stricmp(p_temp,"cdrom0:\\SLES")==0)
		{
			gDisplayType=DISPLAY_TYPE_PAL;
			gFPS=50;
		}	
	}
}

} // namespace Config



================================================
FILE: Code/Sys/Config/Win32/p_config.cpp
================================================
// Config Manager stuff. KSH 20 Mar 2002
#include 

namespace Config
{

void Plat_Init(sint argc, char** argv)
{
	gHardware	= HARDWARE_WIN32;
	gLanguage	= LANGUAGE_ENGLISH;	
	gGotExtraMemory = true;
}

} // namespace Config



================================================
FILE: Code/Sys/Config/XBox/p_config.cpp
================================================
// Config Manager stuff. KSH 20 Mar 2002
#include 

namespace Config
{

void Plat_Init(sint argc, char** argv)
{
	gHardware	= HARDWARE_XBOX;
	DWORD lang	= XGetLanguage();
	if( lang == XC_LANGUAGE_ENGLISH )
		gLanguage = LANGUAGE_ENGLISH;
	else
	{
		if( XGetVideoStandard() != XC_VIDEO_STANDARD_PAL_I )
		{
			// For NTSC, the only language allowed is English.
			gLanguage = LANGUAGE_ENGLISH;
		}
		else if( lang == XC_LANGUAGE_FRENCH )
		{
			gLanguage = LANGUAGE_FRENCH;
		}
		else if( lang == XC_LANGUAGE_GERMAN )
		{
			gLanguage = LANGUAGE_GERMAN;
		}
		else
		{
			// Any languages other than French and German should also be considered English.
			gLanguage = LANGUAGE_ENGLISH;
		}
	}
	
	// Kind of meaningless, but default CD to true for Final builds, false otherwise.
#	ifdef __NOPT_ASSERT__
	gCD = false;
#	else
	gCD = true;
#	endif
	
	switch( XGetVideoStandard())
	{
		case XC_VIDEO_STANDARD_PAL_I:
		{
			gDisplayType	= DISPLAY_TYPE_PAL;
			gFPS			= 50;
			if( XGetVideoFlags() & XC_VIDEO_FLAGS_PAL_60Hz )
			{
				gFPS		=	60;
			}	
			break;
		}
		case XC_VIDEO_STANDARD_NTSC_M:
		case XC_VIDEO_STANDARD_NTSC_J:
		{
			gDisplayType	= DISPLAY_TYPE_NTSC;
			gFPS			= 60;
			break;
		}
		default:
		{
			Dbg_MsgAssert( 0, ("Unrecognized return value (%d) from XGetVideoStandard()", XGetVideoStandard()));
			break;
		}
	}	
}

} // namespace Config



================================================
FILE: Code/Sys/Config/config.cpp
================================================
// Config Manager stuff. KSH 20 Mar 2002
#include 

namespace Config
{
ELanguage gLanguage=LANGUAGE_UNDEFINED;
ETerritory gTerritory=TERRITORY_UNDEFINED;
EHardware gHardware=HARDWARE_UNDEFINED;
bool gGotExtraMemory=false;
bool gCD=false;
bool gBootstrap=false;
bool gSonyBootstrap=false;
float	gMasterVolume = 100.0f; 		// precentage at which we play the overall volume
char	gReturnTo[128];					// full path of executable we return to from a demo (second argument if first is "bootstrap")
char	gReturnString[128];				// string that we return for demo

EDisplayType gDisplayType=DISPLAY_TYPE_NTSC;
int gFPS=60;

const char *gpMemCardHeader="";

// Returns true if p_string starts with p_prefix. Non-case-sensitive.
// (don't want to use strstr cos it is case sensitive)
static bool sIsPrefixedWith(const char *p_string, const char *p_prefix)
{
	if (!p_string || !p_prefix) return false;
	
	int string_len=strlen(p_string);
	int prefix_len=strlen(p_prefix);
	if (prefix_len>string_len)
	{
		// Can't be prefixed if shorter than the prefix.
		return false;
	}
	
	char p_buf[100];
	Dbg_MsgAssert(string_len<100,("String too long for buffer"));
	strcpy(p_buf,p_string);
	p_buf[prefix_len]=0;
	if (stricmp(p_buf,p_prefix)==0)
	{
		return true;
	}
	return false;
}

// If p_name is found to be followed by an equals in the command line, then this function
// will return the word following the equals.
// If p_name is not found, or equals blank, it will return NULL. 
const char *GetCommandLineParam(const char *p_name, sint argc, char** argv)
{
	const char *p_found=NULL;
	
	for (int i=0; i 2)
		{
			strcpy(gReturnTo,argv[2]);
			if (argc > 3)
			{
				strcpy(gReturnString,argv[3]);
			}
			else
			{
				sprintf(gReturnString,"THPS4");
			}			
			printf ("This bootstrap demo will return to %s passing back: %s\n",gReturnTo,gReturnString);
		}
	}
	if (CommandLineContainsFlag("NotBootstrap",argc,argv))		
	{
		gBootstrap=false;
	}
	
	if (CommandLineContainsFlag("Pal",argc,argv))		
	{
		gDisplayType=DISPLAY_TYPE_PAL;
	}
	if (CommandLineContainsFlag("NTSC",argc,argv))		
	{
		gDisplayType=DISPLAY_TYPE_NTSC;
	}
	
	const char *p_frame_rate=GetCommandLineParam("FrameRate",argc,argv);
	if (p_frame_rate)
	{
		if (strcmp(p_frame_rate,"50")==0)
		{
			gFPS=50;
		}
		if (strcmp(p_frame_rate,"60")==0)
		{
			gFPS=60;
		}
	}	
}

const char* GetMemCardHeader()
{
	return gpMemCardHeader;	
}

// Return the string for the directory we are running in
// generally this will be the root
// but when making a demo disk, then it will be the "THPS4" directory
const char* GetDirectory()
{
	if (Bootstrap())
	{
		return	"THPS4\\";	 // generally a subdirectory off the root
	}
	else
	{
		return "";			 // nothing needed it it is the root
	}
}

const char* GetLanguageName()
{
	switch (gLanguage)
	{
	case LANGUAGE_ENGLISH: 		return "English"; break;
	case LANGUAGE_FRENCH: 		return "French"; break;
	case LANGUAGE_GERMAN: 		return "German"; break;
	case LANGUAGE_JAPANESE:		return "Japanese"; break;
	case LANGUAGE_SPANISH:		return "Spanish"; break;
	case LANGUAGE_ITALIAN:		return "Italian"; break;
	case LANGUAGE_DUTCH:		return "Dutch"; break;
	case LANGUAGE_PORTUGUESE:	return "Portuguese"; break;
	default: 				return "Unknown language"; break;
	}
}

const char* GetTerritoryName()
{
	switch (gTerritory)
	{
	case TERRITORY_EUROPE: 	return "Europe"; break;
	case TERRITORY_US: 		return "US"; break;
	case TERRITORY_ASIA: 	return "Asia"; break;
	default: 				return "Unknown territory"; break;
	}
}

const char* GetHardwareName()
{
	switch (gHardware)
	{
	case HARDWARE_PS2: 				return "PS2"; break;
	case HARDWARE_PS2_PROVIEW:		return "PS2 ProView"; break;
	case HARDWARE_PS2_DEVSYSTEM:	return "PS2 Dev System"; break;
	case HARDWARE_XBOX:				return "XBox"; break;
	case HARDWARE_NGC:				return "GameCube"; break;
	case HARDWARE_WIN32:			return "Win32"; break;
	default:						return "Unknown platform"; break;
	}	
}

const char* GetDisplayTypeName()
{
	switch (gDisplayType)
	{
	case DISPLAY_TYPE_PAL:		return "PAL"; break;
	case DISPLAY_TYPE_NTSC:		return "NTSC"; break;
	default:					return "Unknown display type"; break;
	}
}

} // namespace Config



================================================
FILE: Code/Sys/Config/config.h
================================================
// Config Manager stuff. KSH 20 Mar 2002
//
// A bunch of inline functions for quering what type of platform the executable is running on,
// what language is set, whether it's PAL or NTSC, etc blaa blaa.
//
// The first thing main() does is call the Config::Init function, which parses the command
// line and does various things to work out what the config is. Then the inline functions just
// return the values calculated in Init, so they're nice and fast.


#ifndef	__CONFIG_CONFIG_H
#define	__CONFIG_CONFIG_H

#ifndef __CORE_DEFINES_H
#include 
#endif

namespace Config
{

enum ELanguage
{
	LANGUAGE_UNDEFINED,
	
	// Prefixed with LANGUAGE_ so that they don't clash with the old defines that are still
	// in the code.
	LANGUAGE_JAPANESE,
	LANGUAGE_ENGLISH,
	LANGUAGE_FRENCH,
	LANGUAGE_SPANISH,
	LANGUAGE_GERMAN,
	LANGUAGE_ITALIAN,
	LANGUAGE_DUTCH,
	LANGUAGE_PORTUGUESE,
};

enum ETerritory
{
	TERRITORY_UNDEFINED,
	TERRITORY_EUROPE,
	TERRITORY_US,
	TERRITORY_ASIA,
};

enum EHardware
{
	// All prefixed with HARDWARE_ to be consistent with the language enum above ...
	HARDWARE_UNDEFINED,
	
	HARDWARE_PS2,			// A normal PS2
	HARDWARE_PS2_PROVIEW,	// A PS2 running ProView
	HARDWARE_PS2_DEVSYSTEM,	// A dev system with lots of memory
	
	HARDWARE_XBOX,		// An XBox
	HARDWARE_NGC,		// A GameCube
	HARDWARE_WIN32,		// A PC running Win32
};

enum EDisplayType
{
	DISPLAY_TYPE_PAL,
	DISPLAY_TYPE_NTSC,
};

extern EDisplayType gDisplayType;
inline EDisplayType GetDisplayType() {return gDisplayType;}
// Perhaps change this to just return a bool if speed is a problem ...
inline bool PAL() {return gDisplayType==DISPLAY_TYPE_PAL;}
inline bool NTSC() {return gDisplayType==DISPLAY_TYPE_NTSC;}

extern int gFPS;
extern inline int FPS() {return gFPS;}

extern EHardware gHardware;
inline EHardware GetHardware() {return gHardware;}

extern bool gGotExtraMemory;
inline bool GotExtraMemory() {return gGotExtraMemory;}

extern ELanguage gLanguage;
inline ELanguage GetLanguage() {return gLanguage;}

extern ETerritory gTerritory;
inline ETerritory GetTerritory() {return gTerritory;}

extern bool gCD;
inline bool CD() {return gCD;}

extern	float	gMasterVolume;
inline	float	GetMasterVolume() {return gMasterVolume;}
inline	void 	SetMasterVolume(float vol) {gMasterVolume=vol;}

extern bool gBootstrap;
extern bool gSonyBootstrap;
inline bool Bootstrap() {return gBootstrap;}				// true if some kind of bootstrap demo
inline bool SonyBootstrap() {return gSonyBootstrap;}		// true if it's a Sony specific bootstrap demo (with timeout, etc)
extern char	gReturnTo[];  
extern char	gReturnString[];

extern const char *gpMemCardHeader;

const char* GetDirectory();

const char* GetMemCardHeader();

const char *GetCommandLineParam(const char *p_name, sint argc, char** argv);
bool CommandLineContainsFlag(const char *p_name, sint argc, char** argv);

void Init(sint argc, char** argv);
void Plat_Init(sint argc, char** argv);

// Utility functions.
const char* GetLanguageName();
const char* GetTerritoryName();
const char* GetHardwareName();
const char* GetDisplayTypeName();

// Sony Specific, not needed on the others
const char *GetElfName();


} // namespace Config

#endif	// __CONFIG_CONFIG_H



================================================
FILE: Code/Sys/File/AsyncFilesys.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// AsyncFilesys.cpp		GRJ 8 Oct 2002
//
// Asynchronous file system
//
///////////////////////////////////////////////////////////////////////////////////////

#include 
#include 

namespace File
{

///////////////////////////////////////////////////////////////////////////////////////
// Constants
//

const uint32	CAsyncFileHandle::MAX_FILE_SIZE = (uint32) ((uint64) (1 << 31) - 1);	// 1 Gig (Because we are using signed, we lose a bit)


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAsyncFileHandle::CAsyncFileHandle()
{
	init();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileHandle::init()
{
	// Init vars
	mp_callback = NULL;
	m_callback_arg0 = 0;
	m_callback_arg1 = 0;
	m_current_function = FUNC_IDLE;
	m_mem_destination = (EAsyncMemoryType) DEFAULT_MEMORY_TYPE;
	m_stream = false;
	m_blocking = true;			// Makes it async
	m_buffer_size = DEFAULT_BUFFER_SIZE;
	m_priority = DEFAULT_ASYNC_PRIORITY;

	m_file_size = -1;
	m_position = -1;
	m_busy_count = 0;
	m_last_result = 0;

	mp_pre_file = NULL;

	plat_init();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAsyncFileHandle::~CAsyncFileHandle()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

volatile bool		CAsyncFileHandle::IsBusy( void )
{
	// Don't think we need the plat_is_busy()
	return get_busy_count() > 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					CAsyncFileHandle::WaitForIO( void )
{
	do
	{
		// Process any pending callbacks
		CAsyncFileLoader::s_execute_callback_list();
	} while (IsBusy());

	return m_last_result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileHandle::SetPriority( int priority )
{
	//Dbg_MsgAssert(!IsBusy(), ("Can't set file parameters: asynchronous file handle already busy."));
	m_current_function = FUNC_IDLE;

	m_priority = priority;

	plat_set_priority(priority);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileHandle::SetStream( bool stream )
{
	//Dbg_MsgAssert(!IsBusy(), ("Can't set file parameters: asynchronous file handle already busy."));
	m_current_function = FUNC_IDLE;

	m_stream = stream;

	plat_set_stream(stream);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileHandle::SetDestination( EAsyncMemoryType destination )
{
	//Dbg_MsgAssert(!IsBusy(), ("Can't set file parameters: asynchronous file handle already busy."));
	m_current_function = FUNC_IDLE;

	m_mem_destination = destination;

	plat_set_destination(destination);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileHandle::SetBufferSize( size_t buffer_size )
{
	//Dbg_MsgAssert(!IsBusy(), ("Can't set file parameters: asynchronous file handle already busy."));
	m_current_function = FUNC_IDLE;

	m_buffer_size = buffer_size;

	plat_set_buffer_size(buffer_size);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileHandle::SetBlocking( bool block )
{
	//Dbg_MsgAssert(!IsBusy(), ("Can't set file parameters: asynchronous file handle already busy."));
	m_current_function = FUNC_IDLE;

	m_blocking = block;

	plat_set_blocking(block);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileHandle::SetCallback( AsyncCallback p_callback, unsigned int arg0, unsigned int arg1)
{
	mp_callback = p_callback;
	m_callback_arg0 = arg0;
	m_callback_arg1 = arg1;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
size_t				CAsyncFileHandle::GetFileSize( void )
{
	if (mp_pre_file)
	{
		Dbg_Assert(PreMgr::sPreEnabled());

        m_last_result = PreMgr::pre_get_file_size(mp_pre_file);

		Dbg_Assert(PreMgr::sPreExecuteSuccess());
		Dbg_MsgAssert(m_last_result, ("PRE file size is 0"));

		return m_last_result;
	}

	// Make sure we have a valid file size first
	while (m_file_size < 0)
		;

	return m_file_size;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

size_t				CAsyncFileHandle::Load(void *p_buffer)
{
	//Dbg_MsgAssert(!IsBusy(), ("Can't start new load: asynchronous file handle already busy."));

	m_current_function = FUNC_LOAD;

	if (mp_pre_file)
	{
		Dbg_Assert(PreMgr::sPreEnabled());

		int size = PreMgr::pre_get_file_size(mp_pre_file);
		PreMgr::pre_fseek(mp_pre_file, SEEK_SET, 0);
        m_last_result = PreMgr::pre_fread(p_buffer, 1, size, mp_pre_file);

		Dbg_Assert(PreMgr::sPreExecuteSuccess());

		inc_busy_count();
		io_callback(m_current_function, m_last_result, 0);			// Must call this when we are done
		return m_last_result;
	}

	return plat_load(p_buffer);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

size_t				CAsyncFileHandle::Read(void *p_buffer, size_t size, size_t count)
{
	//Dbg_MsgAssert(!IsBusy(), ("Can't start new read: asynchronous file handle already busy."));

	m_current_function = FUNC_READ;

	if (mp_pre_file)
	{
		Dbg_Assert(PreMgr::sPreEnabled());

		m_last_result = PreMgr::pre_fread(p_buffer, size, count, mp_pre_file);

		Dbg_Assert(PreMgr::sPreExecuteSuccess());

		inc_busy_count();
		io_callback(m_current_function, m_last_result, 0);			// Must call this when we are done
		return m_last_result;
	}

	return plat_read(p_buffer, size, count);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

size_t				CAsyncFileHandle::Write(void *p_buffer, size_t size, size_t count)
{
	//Dbg_MsgAssert(!IsBusy(), ("Can't start new write: asynchronous file handle already busy."));

	m_current_function = FUNC_WRITE;

	if (mp_pre_file)
	{
		Dbg_Assert(PreMgr::sPreEnabled());

		m_last_result = PreMgr::pre_fwrite(p_buffer, size, count, mp_pre_file);

		Dbg_Assert(PreMgr::sPreExecuteSuccess());

		inc_busy_count();
		io_callback(m_current_function, m_last_result, 0);			// Must call this when we are done
		return m_last_result;
	}

	return plat_write(p_buffer, size, count);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					CAsyncFileHandle::Seek(long offset, int origin)
{
	//Dbg_MsgAssert(!IsBusy(), ("Can't seek: asynchronous file handle already busy."));

	m_current_function = FUNC_SEEK;

	if (mp_pre_file)
	{
		Dbg_Assert(PreMgr::sPreEnabled());

		m_last_result = PreMgr::pre_fseek(mp_pre_file, offset, origin);

		Dbg_Assert(PreMgr::sPreExecuteSuccess());

		inc_busy_count();
		io_callback(m_current_function, m_last_result, 0);			// Must call this when we are done
		return m_last_result;
	}

	return plat_seek(offset, origin);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileHandle::io_callback(EAsyncFunctionType function, int result, uint32 data)
{
	m_last_result = result;
	CAsyncFileLoader::s_new_io_completion = true;

	// Call user function, if any
	if (mp_callback)
	{
		CAsyncFileLoader::s_add_callback(this, function, result);
	} else {
		post_io_callback();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileHandle::post_io_callback()
{
	// Clean up
	Dbg_MsgAssert(get_busy_count() > 0, ("We will go into a neagtive busy with completion of function %d", m_current_function));
	if (dec_busy_count() == 0)
	{
		m_current_function = FUNC_IDLE;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CAsyncFileHandle::open(const char *filename, bool blocking, int priority)
{
	Dbg_MsgAssert(!IsBusy(), ("Can't open file %s: asynchronous file handle already busy.", filename));
	Dbg_MsgAssert(!mp_pre_file, ("Can't open file %s: already open as a PRE file.", filename));

	m_priority = priority;
	m_blocking = blocking;

	m_current_function = FUNC_OPEN;

    if (PreMgr::sPreEnabled())
    {
        mp_pre_file = PreMgr::pre_fopen(filename, "rb", true);
        if (mp_pre_file)
		{
			Dbg_Assert(PreMgr::sPreExecuteSuccess());

			m_blocking = true;		// Since PRE files are in memory, they will finish "instantly"
			m_last_result = true;

			inc_busy_count();
			io_callback(m_current_function, m_last_result, 0);			// Must call this when we are done
			return m_last_result;
		}
    }

	return plat_open(filename);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CAsyncFileHandle::close()
{
	m_current_function = FUNC_CLOSE;

	if (mp_pre_file)
	{
		Dbg_Assert(PreMgr::sPreEnabled());

        m_last_result = PreMgr::pre_fclose(mp_pre_file, true);

		Dbg_Assert(PreMgr::sPreExecuteSuccess());

		inc_busy_count();
		io_callback(m_current_function, m_last_result, 0);			// Must call this when we are done
		return m_last_result;
	}

	return plat_close();
}

/******************************************************************/
/*                                                                */
/* plat stubs													  */
/*                                                                */
/******************************************************************/

void				CAsyncFileHandle::plat_init()
{
	printf ("STUB: CAsyncFileHandle::Init\n");
}

bool				CAsyncFileHandle::plat_open(const char *filename)
{
	printf ("STUB: CAsyncFileHandle::Open\n");

	return false;
}

bool				CAsyncFileHandle::plat_close()
{
	printf ("STUB: CAsyncFileHandle::Close\n");

	return false;
}

volatile bool		CAsyncFileHandle::plat_is_done()
{
	printf ("STUB: CAsyncFileHandle::IsDone\n");

	return false;
}

volatile bool		CAsyncFileHandle::plat_is_busy()
{
	printf ("STUB: CAsyncFileHandle::IsBusy\n");

	return false;
}

bool				CAsyncFileHandle::plat_is_eof() const
{
	printf ("STUB: CAsyncFileHandle::IsEOF\n");

	return false;
}

void				CAsyncFileHandle::plat_set_priority( int priority )
{
	printf ("STUB: CAsyncFileHandle::SetPriority\n");
}

void				CAsyncFileHandle::plat_set_stream( bool stream )
{
	printf ("STUB: CAsyncFileHandle::SetStream\n");
}

void				CAsyncFileHandle::plat_set_destination( EAsyncMemoryType destination )
{
	printf ("STUB: CAsyncFileHandle::SetDestination\n");
}

void				CAsyncFileHandle::plat_set_buffer_size( size_t buffer_size )
{
	printf ("STUB: CAsyncFileHandle::SetBufferSize\n");
}

void				CAsyncFileHandle::plat_set_blocking( bool block )
{
	printf ("STUB: CAsyncFileHandle::SetBlocking\n");
}

size_t				CAsyncFileHandle::plat_load(void *p_buffer)
{
	printf ("STUB: CAsyncFileHandle::Load\n");

	return 0;
}

size_t				CAsyncFileHandle::plat_read(void *p_buffer, size_t size, size_t count)
{
	printf ("STUB: CAsyncFileHandle::Read\n");

	return 0;
}

size_t				CAsyncFileHandle::plat_write(void *p_buffer, size_t size, size_t count)
{
	printf ("STUB: CAsyncFileHandle::Write\n");

	return 0;
}

char *				CAsyncFileHandle::plat_get_s(char *p_buffer, int maxlen)
{
	printf ("STUB: CAsyncFileHandle::GetS\n");

	return NULL;
}

int					CAsyncFileHandle::plat_seek(long offset, int origin)
{
	printf ("STUB: CAsyncFileHandle::Seek\n");

	return 0;
}

///////////////////////////////////////////////////////////////////////////////////////

CAsyncFileHandle	*	CAsyncFileLoader::s_file_handles[MAX_FILE_HANDLES];
int						CAsyncFileLoader::s_free_handle_index = 0;

volatile int			CAsyncFileLoader::s_manager_busy_count = 0;
volatile bool			CAsyncFileLoader::s_new_io_completion = false;

volatile CAsyncFileLoader::SCallback	CAsyncFileLoader::s_callback_list[2][MAX_PENDING_CALLBACKS];
volatile int							CAsyncFileLoader::s_num_callbacks[2] = { 0, 0 };
int										CAsyncFileLoader::s_cur_callback_list_index = 0;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileLoader::sInit()
{
	s_free_handle_index = 0;
	s_plat_init();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileLoader::sCleanup()
{
	s_plat_cleanup();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CAsyncFileLoader::sAsyncSupported()
{
	return s_plat_async_supported();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CAsyncFileLoader::sExist(const char *filename)
{
	return s_plat_exist(filename);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAsyncFileHandle *	CAsyncFileLoader::sOpen(const char *filename, bool blocking, int priority)
{
	CAsyncFileHandle *p_file_handle = s_get_file_handle();

	if (p_file_handle)
	{
		if (!p_file_handle->open(filename, blocking, priority))
		{
			//Dbg_MsgAssert(0, ("Error opening Async file %s", filename));
			s_free_file_handle(p_file_handle);
			return NULL;
		}

		return p_file_handle;
	}
	else
	{
		Dbg_MsgAssert(0, ("Out of Async handles"));
		return NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CAsyncFileLoader::sClose(CAsyncFileHandle *p_file_handle)
{

	bool result = p_file_handle->close();

	bool free_result = s_free_file_handle(p_file_handle);
	if ( !free_result )
	{
		Dbg_MsgAssert(0, ("sClose(): Can't find async handle in CAsyncFileLoader"));
	}

	return result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void				CAsyncFileLoader::sWaitForIOEvent(bool all_io_events)
{
	bool done = false;

	while (!done)
	{
		printf("CAsyncFileLoader waiting for io completion: busy count %d completion %d\n", s_manager_busy_count, s_new_io_completion);

		// Wait for an event
		while (!s_new_io_completion)
			;

		printf("CAsyncFileLoader got completion: busy count %d completion %d\n", s_manager_busy_count, s_new_io_completion);

		// execute callbacks
		s_execute_callback_list();

		done = true;	// assume we are done for now
		if (all_io_events)
		{
			for (int i = 0; i < MAX_FILE_HANDLES; i++)
			{
				if (s_file_handles[i]->IsBusy())
				{
					printf("CAsyncFileLoader still needs to wait for file handle %d\n", i);
					Dbg_MsgAssert(s_manager_busy_count, ("CAsyncFileLoader busy count is 0 while file handle %d is busy", i));
					done = false;
					break;
				}
			}
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CAsyncFileLoader::sAsyncInUse()
{
#if 0
	for (int i = 0; i < MAX_FILE_HANDLES; i++)
	{
		if (s_file_handles[i]->IsBusy())
		{
			return true;
		}
	}

	return false;
#endif

	Dbg_MsgAssert(s_manager_busy_count >= 0, ("CAsyncFileLoader busy count is negative: %d", s_manager_busy_count));
	return s_manager_busy_count > 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAsyncFileHandle *	CAsyncFileLoader::s_get_file_handle()
{
	if (s_free_handle_index < MAX_FILE_HANDLES)
	{
		// Find a non-busy handle and exchange with busy, if necessary
		if (s_file_handles[s_free_handle_index]->IsBusy())
		{
			int i;
			for (i = s_free_handle_index + 1; i < MAX_FILE_HANDLES; i++)
			{
				if (!s_file_handles[i]->IsBusy())
				{
					CAsyncFileHandle *p_temp = s_file_handles[i];
					s_file_handles[i] = s_file_handles[s_free_handle_index];
					s_file_handles[s_free_handle_index] = p_temp;
					break;
				}
			}
			
			// If we are full, wait for this one
			if (i == MAX_FILE_HANDLES)
			{
				Dbg_Message("CAsyncFileLoader::sOpen(): waiting for old handle to finish up");
				s_file_handles[s_free_handle_index]->WaitForIO();
				Dbg_Message("CAsyncFileLoader::sOpen(): done waiting.");
			}
		}

		s_file_handles[s_free_handle_index]->init();		// Clear out any old stuff first

		return s_file_handles[s_free_handle_index++];
	}
	else
	{
		return NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CAsyncFileLoader::s_free_file_handle(CAsyncFileHandle *p_file_handle)
{
	Dbg_Assert(s_free_handle_index);

	for (int i = 0; i < MAX_FILE_HANDLES; i++)
	{
		if (p_file_handle == s_file_handles[i])
		{
			// Found it, exchange it with last open handle
			s_file_handles[i] = s_file_handles[--s_free_handle_index];
			s_file_handles[s_free_handle_index] = p_file_handle;
			return true;
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileLoader::s_add_callback(CAsyncFileHandle *p_file, EAsyncFunctionType function, int result)
{
	// This will be called from an interrupt
	//scePrintf("Adding callback for handle %x\n", p_file);
	Dbg_MsgAssert(s_num_callbacks[s_cur_callback_list_index] < MAX_PENDING_CALLBACKS, ("add_callback(): list is full"));

	volatile SCallback * p_callback_entry =  &s_callback_list[s_cur_callback_list_index][s_num_callbacks[s_cur_callback_list_index]++];

	p_callback_entry->mp_file_handle	= p_file;
	p_callback_entry->m_function		= function;
	p_callback_entry->m_result			= result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void				CAsyncFileLoader::s_update()
{
	s_plat_update();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileLoader::s_execute_callback_list()
{
	s_plat_swap_callback_list();

	int list_index = s_cur_callback_list_index ^ 1;	// Look at old list
	int num_callbacks = s_num_callbacks[list_index];

	for (int i = 0; i < num_callbacks; i++)
	{
		volatile SCallback * p_callback_entry =  &s_callback_list[list_index][i];

		CAsyncFileHandle *p_file = p_callback_entry->mp_file_handle;
		Dbg_Assert(p_file->mp_callback);

		//Dbg_Message("Executing callback for handle %x", p_file);

		// Call the callback and the post callback
		(*p_file->mp_callback)(p_file, p_callback_entry->m_function, p_callback_entry->m_result,
							   p_file->m_callback_arg0, p_file->m_callback_arg1);
		p_file->post_io_callback();
	}

	s_num_callbacks[list_index] = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

DefineSingletonClass( CAsyncFilePoll, "Async File System Poll module" );

CAsyncFilePoll::CAsyncFilePoll()
{
	mp_logic_task = new Tsk::Task< CAsyncFilePoll > ( CAsyncFilePoll::s_logic_code, *this );
}

CAsyncFilePoll::~CAsyncFilePoll()
{
	delete mp_logic_task;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAsyncFilePoll::v_start_cb ( void )
{
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	mlp_manager->AddLogicTask( *mp_logic_task );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAsyncFilePoll::v_stop_cb ( void )
{
	mp_logic_task->Remove();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void CAsyncFilePoll::s_logic_code ( const Tsk::Task< CAsyncFilePoll >& task )
{
	Dbg_AssertType ( &task, Tsk::Task< CAsyncFilePoll > );

	CAsyncFileLoader::s_update();
	CAsyncFileLoader::s_execute_callback_list();
}

}





================================================
FILE: Code/Sys/File/AsyncFilesys.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			File													**
**																			**
**	Created:		09/24/02	-	grj										**
**																			**
**	File name:		core/sys/AsyncFilesys.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_FILE_ASYNC_FILESYS_H
#define	__SYS_FILE_ASYNC_FILESYS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __GEL_MODULE_H
#include 
#endif

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define   	DEFAULT_ASYNC_PRIORITY	(100)
#define   	DEFAULT_BUFFER_SIZE		(64 * 1024)

namespace Pcm
{
	class CFileStreamInfo;
}

namespace File
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

// Forward declarations
class CAsyncFileHandle;
class CAsyncFileLoader;
class CAsyncFilePoll;

/////////////////////////////////////////////////////////////////////////////////////
// The asynchronous file class
//
class CAsyncFileHandle
{
public:

	// A callback is called after the completion of every async function
	typedef void (* AsyncCallback)(CAsyncFileHandle *, EAsyncFunctionType function, int result, unsigned int arg0, unsigned int arg1);


	// Check read status
	volatile bool		IsDone( void );
	volatile bool		IsBusy( void );
	bool				IsEOF( void ) const;
	int					WaitForIO( void );

	// Change parameters
	void				SetPriority( int priority );
	void				SetStream( bool stream );			// Tells if loaded all at once or streamed one buffer at a time
	void				SetDestination( EAsyncMemoryType destination );
	void				SetBufferSize( size_t buffer_size );
	void				SetBlocking( bool block );
	void				SetCallback( AsyncCallback p_callback, unsigned int arg0, unsigned int arg1 );

	size_t				GetFileSize( void );

	// Async commands
	size_t				Load( void *p_buffer );
	size_t				Read( void *p_buffer, size_t size, size_t count );
	size_t				Write( void *p_buffer, size_t size, size_t count );
	char *				GetS( char *p_buffer, int maxlen );	// Probably don't need this one
	int					Seek( long offset, int origin );

protected:
						CAsyncFileHandle();
	virtual 			~CAsyncFileHandle();

	void				init( void );

	bool				open( const char *filename, bool blocking = true, int priority = DEFAULT_ASYNC_PRIORITY );
	bool				close( void );

	// Internal busy counter functions
	volatile int		inc_busy_count();			// Increment and return current count
	volatile int		dec_busy_count();			// Decrement and return current count
	volatile int		get_busy_count();			// Just return current count

	// Internal callback
	virtual void		io_callback(EAsyncFunctionType function, int result, uint32 data);
	void				post_io_callback();

	// These could be changed by an interrupt, so they are volatile
	volatile int		m_last_result;

	// Callback
	AsyncCallback		mp_callback;
	unsigned int		m_callback_arg0;
	unsigned int		m_callback_arg1;
	EAsyncFunctionType	m_current_function;		// So callback knows what was completed

	EAsyncMemoryType	m_mem_destination;
	bool				m_stream;
	bool				m_blocking;
	uint32				m_buffer_size;
	int					m_priority;

	volatile int		m_file_size;
	volatile int		m_position;

	// PRE file
	PreFile::FileHandle *mp_pre_file;

	// constants
	static const uint32	MAX_FILE_SIZE;

	// platform-specific calls
	virtual void		plat_init( void );

	virtual bool		plat_open( const char *filename );
	virtual bool		plat_close( void );

	virtual volatile bool	plat_is_done( void );
	virtual volatile bool	plat_is_busy( void );
	virtual bool		plat_is_eof( void ) const;

	virtual void		plat_set_priority( int priority );
	virtual void		plat_set_stream( bool stream );
	virtual void		plat_set_destination( EAsyncMemoryType destination );
	virtual void		plat_set_buffer_size( size_t buffer_size );
	virtual void		plat_set_blocking( bool block );

	virtual size_t		plat_load( void *p_buffer );
	virtual size_t		plat_read( void *p_buffer, size_t size, size_t count );
	virtual size_t		plat_write( void *p_buffer, size_t size, size_t count );
	virtual char *		plat_get_s( char *p_buffer, int maxlen );
	virtual int			plat_seek( long offset, int origin );

private:
	volatile int		m_busy_count;		// Number of items we are waiting to finish asynchronously

	// Friends
	friend CAsyncFileLoader;

};

/////////////////////////////////////////////////////////////////////////////////////
// The interface to start and end async loads
//
class CAsyncFileLoader
{
public:
	static void					sInit();
	static void					sCleanup();

	static bool					sAsyncSupported();							// Returns true if async has been implemented on
																			// the platform

	/////////////////////////////////////////////////////////////////////
	// The following are our versions of the ANSI file IO functions.
	//
	static bool					sExist( const char *filename );				// Nothing special about this function
	static CAsyncFileHandle *	sOpen( const char *filename, bool blocking = true, int priority = DEFAULT_ASYNC_PRIORITY );
	static bool					sClose( CAsyncFileHandle *p_file_handle );

	// IO Waiting
	static void					sWaitForIOEvent(bool all_io_events);		// Returns when we get an async result
	static bool					sAsyncInUse();								// Slow


protected:
	// Constants
	enum
	{
		MAX_FILE_HANDLES = 		16,
		MAX_PENDING_CALLBACKS = 16,
	};

	// Callback data
	struct SCallback
	{
		CAsyncFileHandle *				mp_file_handle;
		EAsyncFunctionType				m_function;
		int								m_result;
	};

	// This should actually be declared below the p-line
	static CAsyncFileHandle *	s_file_handles[MAX_FILE_HANDLES];
	static int					s_free_handle_index;

	// Status
	static volatile int			s_manager_busy_count;		// Is 0 when there are no outstanding async IO requests
	static volatile bool		s_new_io_completion;		// File handle got callback since s_execute_callback_list()

	// Current callback list
	static volatile SCallback	s_callback_list[2][MAX_PENDING_CALLBACKS];
	static volatile int			s_num_callbacks[2];
	static int					s_cur_callback_list_index;

	// File handle management
	static CAsyncFileHandle *	s_get_file_handle();
	static bool					s_free_file_handle(CAsyncFileHandle *p_file_handle);

	// callback list functions
	static void					s_add_callback(CAsyncFileHandle *p_file, EAsyncFunctionType function, int result);
	static void					s_execute_callback_list();

	// Increment and decrement the manager busy count.  Should only be called by CAsyncFileHandle::inc_busy_count()
	// and CAsyncFileHandle::dec_busy_count()
	static void					s_inc_manager_busy_count();
	static void					s_dec_manager_busy_count();

	// Primarily to allow per-frame polling for platforms that may not support system callbacks on i/o routines
	static void					s_update();

	// platform-specific calls
	static void					s_plat_init();
	static void					s_plat_cleanup();

	static bool					s_plat_async_supported();

	static bool					s_plat_exist( const char *filename );
	static CAsyncFileHandle *	s_plat_open( const char *filename, int priority );
	static bool					s_plat_close( CAsyncFileHandle *p_file_handle );

	static void					s_plat_swap_callback_list();		// This is needed below the p-line because interrupts
																	// need to be disabled for a bit
	static void					s_plat_update();

	// Friends
	friend CAsyncFilePoll;
	friend CAsyncFileHandle;
};

/////////////////////////////////////////////////////////////////////////////////////
// This defines the singleton class for the one-a-frame async calls that are needed.
// For now, just the callbacks that have stacked up in CAsyncFileLoader are called
//
class CAsyncFilePoll  : public Mdl::Module
{
	DeclareSingletonClass( CAsyncFilePoll );

			CAsyncFilePoll();
	virtual ~CAsyncFilePoll();

	void v_start_cb ( void );
	void v_stop_cb ( void );
	
	static Tsk::Task< CAsyncFilePoll >::Code s_logic_code;       
	Tsk::Task< CAsyncFilePoll > *mp_logic_task;
};

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline void				CAsyncFileLoader::s_inc_manager_busy_count()
{
	s_manager_busy_count++;
}

inline void				CAsyncFileLoader::s_dec_manager_busy_count()
{
	s_manager_busy_count--;
}

inline volatile int		CAsyncFileHandle::inc_busy_count()
{
	CAsyncFileLoader::s_inc_manager_busy_count();			// Also increment manager
	return ++m_busy_count;
}

inline volatile int		CAsyncFileHandle::dec_busy_count()
{
	CAsyncFileLoader::s_dec_manager_busy_count();			// Also decrement manager
	return --m_busy_count;
}

inline volatile int		CAsyncFileHandle::get_busy_count()
{
	return m_busy_count;
}

} // namespace File

#endif  // __SYS_FILE_ASYNC_FILESYS_H


================================================
FILE: Code/Sys/File/AsyncTypes.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			File													**
**																			**
**	Created:		09/24/02	-	grj										**
**																			**
**	File name:		core/sys/AsyncFilesys.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_FILE_ASYNC_TYPES_H
#define	__SYS_FILE_ASYNC_TYPES_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define   	DEFAULT_MEMORY_TYPE		(0)

namespace File
{


/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

enum EAsyncMemoryType	// This belongs below the p-line, but need to think of a general way communicate
{						// this above the p-line.
#ifdef __PLAT_NGPS__
	MEM_EE = DEFAULT_MEMORY_TYPE,
	MEM_IOP,
#endif // __PLAT_NGPS__

#ifdef __PLAT_NGC__
	MEM_DRAM = DEFAULT_MEMORY_TYPE,
	MEM_ARAM,
#endif // __PLAT_NGC__

#ifdef __PLAT_XBOX__
	MEM_MAIN = DEFAULT_MEMORY_TYPE,		// Only 1 type for XBox
#endif //__PLAT_XBOX__
};

enum EAsyncFunctionType
{
	FUNC_IDLE,
	FUNC_OPEN,
	FUNC_LOAD,
	FUNC_READ,
	FUNC_WRITE,
	FUNC_SEEK,
	FUNC_CLOSE,
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/


} // namespace File

#endif  // __SYS_FILE_ASYNC_TYPES_H


================================================
FILE: Code/Sys/File/FileLibrary.cpp
================================================
//****************************************************************************
//* MODULE:         File
//* FILENAME:       FileLibrary.cpp
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  01/21/2003
//****************************************************************************

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
#include 
#include 

#include 

#ifdef __PLAT_NGC__
#include 
#include 
#include 
#endif		// __PLAT_NGC__

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace File
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

struct SLibHeader
{
	uint32	versionNumber;
	int		numFiles;
};

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void filesync_async_callback(File::CAsyncFileHandle*, File::EAsyncFunctionType function, int result, unsigned int arg0, unsigned int arg1)
{
	// Dbg_Message("Got callback from %x", arg0);
	if (function == File::FUNC_READ)
	{
		CFileLibrary* p_data = (CFileLibrary*)arg0;
		bool assertOnFail = (bool)arg1;

		p_data->PostLoad( assertOnFail, result );
	}
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				   
CFileLibrary::CFileLibrary()
{
#ifndef __PLAT_NGC__
	mp_baseBuffer = NULL;
	mp_fileBuffer = NULL;
#endif		// __PLAT_NGC__
	mp_fileHandle = NULL;
	m_dataLoaded = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CFileLibrary::~CFileLibrary()
{
#ifdef __PLAT_NGC__
	NsARAM::free( m_aramOffset );
#else
	if ( mp_baseBuffer )
	{
		Mem::Free( mp_baseBuffer );
		mp_baseBuffer = NULL;
	}
#endif		// __PLAT_NGC__
	
	if ( mp_fileHandle )
	{
		Dbg_MsgAssert( m_dataLoaded, ( "Can't delete CFileLibrary while it is still being loaded" ) );
		File::CAsyncFileLoader::sClose( mp_fileHandle );
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				   
bool CFileLibrary::LoadFinished() const
{
	return m_dataLoaded;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CFileLibrary::PostLoad( bool assertOnFail, int file_size )
{
	// Handle end of async, if that was used
	if ( mp_fileHandle )
	{
		File::CAsyncFileLoader::sClose( mp_fileHandle );
		mp_fileHandle = NULL;
	}
	
	Dbg_MsgAssert( (file_size & 0x3) == 0, ( "Size of file is not multiple of 4 (%d bytes)", file_size ) );

#ifdef __PLAT_NGC__
#define XFER_SIZE ( sizeof( SLibHeader ) + ( vMAX_LIB_FILES * sizeof( SFileInfo ) ) )
	char buffer[ XFER_SIZE ];

	NsDMA::toMRAM( buffer, m_aramOffset, XFER_SIZE );

	uint8 *pFileData = (uint8*)buffer; 
#else
	uint8 *pFileData = (uint8*)mp_fileBuffer;
#endif		// __PLAT_NGC__

	SLibHeader* pHeader = (SLibHeader*)pFileData;
	
	pFileData += sizeof( SLibHeader );
#ifndef __PLAT_NGC__
	if ( pFileData > ( mp_fileBuffer + file_size ) )
	{
		// out of bounds
		goto load_fail;
	}
#endif		// __PLAT_NGC__

	m_numFiles = pHeader->numFiles;

#ifndef __PLAT_NGC__
	Dbg_MsgAssert( m_numFiles > 0, ( "No files found in lib mp_fileBuffer = %p", mp_fileBuffer ) );
    Dbg_MsgAssert( m_numFiles < vMAX_LIB_FILES, ( "Too many subfiles found in lib (%d files, max=%d)", m_numFiles, vMAX_LIB_FILES ) );
#endif		// __PLAT_NGC__

	// read in table of contents information
	memcpy( &m_fileInfo, pFileData, (sizeof(SFileInfo) * m_numFiles) );
	pFileData += (sizeof(SFileInfo) * m_numFiles);
#ifndef __PLAT_NGC__
	if ( pFileData > ( mp_fileBuffer + file_size ) )
	{
		// out of bounds
		goto load_fail;
	}
#endif		// __PLAT_NGC__
	
	for ( int i = 0; i < m_numFiles; i++ )
	{
#ifdef __PLAT_NGC__
		m_fileOffsets[i] = (uint32)(m_aramOffset + m_fileInfo[i].fileOffset);
		mp_filePointers[i] = NULL;
#else
		mp_filePointers[i] = (uint32*)(mp_fileBuffer + m_fileInfo[i].fileOffset);
#endif		// __PLAT_NGC__
		Dbg_Message( "File %d @ %08x %s [%d bytes]", i, m_fileInfo[i].fileNameChecksum, Script::FindChecksumName(m_fileInfo[i].fileExtensionChecksum), m_fileInfo[i].fileSize );
	}
	
#ifndef __PLAT_NGC__
	// to reduce the amount of temp memory needed, we first load the LIB file
	// on the bottom up heap, and then copy sub-file individually onto the top
	// down heap (as we reallocate shrink the original LIB file on the bottom up heap)
	for ( int i = m_numFiles - 1; i >= 0; i-- )
	{
		int file_size = m_fileInfo[i].fileSize;
		
		uint32* pTempFilePointer = mp_filePointers[i];
		
		// GJ:  When I first wrote this class, I wanted to make it generic, 
		// but it gradually became more cutscene-specific to fix
		// memory issues.  For example, the following uses the
		// "cutscene heap" to reduce temp memory overhead.  Perhaps
		// later on, I can just store the appropriate top/bottomheap pointers
		// during initialization
		Dbg_MsgAssert( Mem::Manager::sHandle().CutsceneTopDownHeap(), ( "No cutscene heap?" ) ); 
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneTopDownHeap());
		mp_filePointers[i] = (uint32*)Mem::Malloc( file_size );
		memcpy( mp_filePointers[i], pTempFilePointer, file_size );
		Mem::Manager::sHandle().PopContext();
		
#ifdef	__NOPT_ASSERT__
		uint8* pOldBuffer = mp_baseBuffer;
#endif
		
		Dbg_MsgAssert( Mem::Manager::sHandle().CutsceneBottomUpHeap(), ( "No cutscene heap?" ) ); 
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneBottomUpHeap());
		mp_baseBuffer = (uint8*)Mem::ReallocateShrink( Mem::GetAllocSize(mp_baseBuffer) - file_size, mp_baseBuffer );
		Dbg_MsgAssert( mp_baseBuffer == pOldBuffer, ( "Pointer was not supposed to change except for shrinking" ) );
		Mem::Manager::sHandle().PopContext();
	}

	// by this point, all that should be left in the original block
	// is the header, which we don't need any more
	Mem::Free( mp_baseBuffer );
	mp_baseBuffer = NULL;
	mp_fileBuffer = NULL;
#endif		// __PLAT_NGC__

	// if the data is ready to be accessed
	m_dataLoaded = true;

	return true;

#ifndef __PLAT_NGC__
load_fail:
	Dbg_MsgAssert( 0, ( "Parsing of library failed" ) );
	return false;
#endif		// __PLAT_NGC__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CFileLibrary::Load( const char* p_fileName, bool assertOnFail, bool async_load )
{
#ifndef __PLAT_NGC__
	// See Garrett, Garrett, or Mick for explanation.
	
	Dbg_MsgAssert( !async_load, ( "Cutscenes aren't supposed to be async loaded anymore" ) );
	int file_size;
	
	Dbg_MsgAssert( Mem::Manager::sHandle().CutsceneBottomUpHeap(), ( "No cutscene heap?" ) ); 
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneBottomUpHeap());
	
	mp_baseBuffer = (uint8*)LoadAlloc( p_fileName, &file_size );
	mp_fileBuffer = mp_baseBuffer;
	
	Mem::Manager::sHandle().PopContext();
	
	return PostLoad(assertOnFail, file_size);
#endif

#ifdef __PLAT_NGC__
	// Make sure music is off here as we really need the memory.
//	Pcm::StopMusic();

	async_load = false;
#else
	// Turn off async for platforms that don't support it
	if (!File::CAsyncFileLoader::sAsyncSupported())
	{
		async_load = false;
	}
	
	if ( async_load )
	{
		// Pre-load
		Dbg_Assert( !mp_fileHandle );

		mp_fileHandle = File::CAsyncFileLoader::sOpen( p_fileName, !async_load );

		// make sure the file is valid
		if ( !mp_fileHandle )
		{
			Dbg_MsgAssert( assertOnFail, ( "Load of %s failed - file not found?", p_fileName ) );
			return false;
		}

		int file_size = mp_fileHandle->GetFileSize();

		if ( file_size == 0 )
		{
			Dbg_MsgAssert( 0, ("Library file %s size is 0 (possibly not found?)", p_fileName) );
			File::CAsyncFileLoader::sClose( mp_fileHandle );
			mp_fileHandle = NULL;
			return false;
		}

		Dbg_Assert( !mp_fileBuffer );
		Dbg_Assert( !mp_baseBuffer );
		
		// to reduce the amount of temp memory needed, we first load the LIB file
		// on the bottom up heap, and then copy sub-file individually onto the top
		// down heap (as we reallocate shrink the original LIB file on the bottom up heap)
		Dbg_MsgAssert( Mem::Manager::sHandle().CutsceneBottomUpHeap(), ( "No cutscene heap?" ) ); 
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneBottomUpHeap());
		mp_baseBuffer = (uint8*) Mem::Malloc( file_size + 64 );
		mp_fileBuffer = (uint8*)(((uint32)mp_baseBuffer+63)&~63);
		mp_fileBuffer = mp_baseBuffer;		// Garrett: De-aligning pointer since async filesystem should take care of it.		
		Mem::Manager::sHandle().PopContext();

		// Set the callback
		mp_fileHandle->SetCallback( filesync_async_callback, (unsigned int)this, (unsigned int)assertOnFail );

		// read the file in
		mp_fileHandle->Read( mp_fileBuffer, 1, file_size );

		return true;
	}
	else
#endif		// __PLAT_NGC__
	{				   
		// open the file as a stream
		void* pStream = File::Open( p_fileName, "rb" );
	
		// make sure the file is valid
		if ( !pStream )
		{
			Dbg_MsgAssert( assertOnFail, ("Load of %s failed - file not found?", p_fileName) );
			return false;
		}

		int file_size = File::GetFileSize( pStream );
		Dbg_MsgAssert( file_size, ( "Library file size is 0" ) );
		
#ifndef __PLAT_NGC__
		Dbg_Assert( !mp_fileBuffer );
		Dbg_Assert( !mp_baseBuffer );
#endif		// __PLAT_NGC__
		
#ifdef __PLAT_NGC__
		// Allocate ARAM
		m_aramOffset = NsARAM::alloc( file_size, NsARAM::TOPDOWN );

#define BUFFER_SIZE ( 16 * 1024 )

		// read the file in blocks & transfer to ARAM
		char buffer[BUFFER_SIZE];
		int size = file_size;
		uint32 offset = m_aramOffset;

		while ( size > BUFFER_SIZE )
		{
			File::Read( buffer, 1, BUFFER_SIZE, pStream );
			NsDMA::toARAM( offset, buffer, BUFFER_SIZE );

			size -= BUFFER_SIZE;
			offset += BUFFER_SIZE;
		}

		// Read & transfer last bit.
		if ( size )
		{
			File::Read( buffer, 1, size, pStream ); 
			NsDMA::toARAM( offset, buffer, size );
		}
//		mp_baseBuffer = (uint8*)Mem::Malloc( file_size );
//		mp_fileBuffer = mp_baseBuffer;
#else
		// to reduce the amount of temp memory needed, we first load the LIB file
		// on the bottom up heap, and then copy sub-file individually onto the top
		// down heap (as we reallocate shrink the original LIB file on the bottom up heap)
		Dbg_MsgAssert( Mem::Manager::sHandle().CutsceneBottomUpHeap(), ( "No cutscene heap?" ) ); 
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneBottomUpHeap());
		mp_baseBuffer = (uint8*)Mem::Malloc( file_size );
		mp_fileBuffer = mp_baseBuffer;
		Mem::Manager::sHandle().PopContext();

		// read the file in
		if ( !File::Read( mp_fileBuffer, 1, file_size, pStream ) )
		{
			Mem::Free( mp_fileBuffer );
			mp_baseBuffer = NULL;
			mp_fileBuffer = NULL;
			File::Close( pStream );

			Dbg_MsgAssert( assertOnFail, ( "Load of %s failed - bad format?", p_fileName ) );
			return false;
		}
#endif		// __PLAT_NGC__

		File::Close(pStream);

		return PostLoad(assertOnFail, file_size);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				   
int CFileLibrary::GetNumFiles() const 
{
	return m_numFiles;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				   
uint32* CFileLibrary::GetFileData( int index )
{
	Dbg_MsgAssert( index >= 0 && index < m_numFiles, ( "Out of range file index %d", index ) );

#ifdef __PLAT_NGC__
	if ( !mp_filePointers[index] )
	{
		// Allocate memory & copy over.
//		Dbg_MsgAssert( Mem::Manager::sHandle().CutsceneTopDownHeap(), ( "No cutscene heap?" ) ); 
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().CutsceneTopDownHeap());

		int mem_available;
		bool need_to_pop = false;
		int size = m_fileInfo[index].fileSize;

		Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().AudioHeap() );
		mem_available = Mem::Manager::sHandle().Available();
		if ( size < ( mem_available - ( 15 * 1024 ) ) )
		{
			need_to_pop = true;
		}
		else
		{
			Mem::Manager::sHandle().PopContext();
			Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().FrontEndHeap() );
			mem_available = Mem::Manager::sHandle().Available();
			if ( size < ( mem_available - ( 40 * 1024 ) ) )
			{
				need_to_pop = true;
			}
			else
			{
				Mem::Manager::sHandle().PopContext();
				Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ThemeHeap() );
				mem_available = Mem::Manager::sHandle().Available();
				if ( size < ( mem_available - ( 5 * 1024 ) ) )
				{
					need_to_pop = true;
				}
				else
				{
					Mem::Manager::sHandle().PopContext();
					Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().ScriptHeap() );
					mem_available = Mem::Manager::sHandle().Available();
					if ( size < ( mem_available - ( 40 * 1024 ) ) )
					{
						need_to_pop = true;
					}
					else
					{
						Mem::Manager::sHandle().PopContext();
					}
				}
			}
		}

		mp_filePointers[index] = (uint32*)Mem::Malloc( m_fileInfo[index].fileSize );
		NsDMA::toMRAM( mp_filePointers[index], m_fileOffsets[index], m_fileInfo[index].fileSize );

		if ( need_to_pop )
		{
			Mem::Manager::sHandle().PopContext();
		}

		Mem::Manager::sHandle().PopContext();
	}
#endif		// __PLAT_NGC__

	return mp_filePointers[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				   
const SFileInfo* CFileLibrary::GetFileInfo( int index ) const
{
	Dbg_MsgAssert( index >= 0 && index < m_numFiles, ( "Out of range file index %d", index ) );

	return &m_fileInfo[index];
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				   
const SFileInfo* CFileLibrary::GetFileInfo( uint32 name, uint32 extension, bool assertOnFail ) const
{
	for ( int index = 0; index < m_numFiles; index++ )
	{
		if ( m_fileInfo[index].fileNameChecksum == name && m_fileInfo[index].fileExtensionChecksum == extension )
		{
			return &m_fileInfo[index];
		}
	}

	if ( assertOnFail )
	{
		Dbg_MsgAssert( 0, ( "Couldn't find file %08x %08x\n", name, extension ) );
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				   
uint32* CFileLibrary::GetFileData( uint32 name, uint32 extension, bool assertOnFail )
{
	for ( int index = 0; index < m_numFiles; index++ )
	{
		if ( m_fileInfo[index].fileNameChecksum == name && m_fileInfo[index].fileExtensionChecksum == extension )
		{
			return GetFileData( index );
		}
	}

	if ( assertOnFail )
	{
		Dbg_MsgAssert( 0, ( "Couldn't find file %08x %08x\n", name, extension ) );
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				   
bool CFileLibrary::ClearFile( uint32 name, uint32 extension )
{
	bool success = false;

	for ( int index = 0; index < m_numFiles; index++ )
	{
		if ( m_fileInfo[index].fileNameChecksum == name && m_fileInfo[index].fileExtensionChecksum == extension )
		{
			Mem::Free( mp_filePointers[index] );
			mp_filePointers[index] = NULL;
			success = true;
		}
	}

	return success;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				   
bool CFileLibrary::FileExists( uint32 name, uint32 extension )
{
	for ( int index = 0; index < m_numFiles; index++ )
	{
		if ( m_fileInfo[index].fileNameChecksum == name && m_fileInfo[index].fileExtensionChecksum == extension )
		{
			return true;
		}
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
				   
} // namespace File


================================================
FILE: Code/Sys/File/FileLibrary.h
================================================
//****************************************************************************
//* MODULE:         File
//* FILENAME:       FileLibrary.h
//* OWNER:          Gary Jesdanun
//* CREATION DATE:  01/21/2003
//****************************************************************************

#ifndef __SYS_FILE_FILELIBRARY_H
#define __SYS_FILE_FILELIBRARY_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

namespace File
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

	class CAsyncFileHandle;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

struct SFileInfo
{
	uint32	fileOffset;
	int		fileSize;
	uint32	fileNameChecksum;
	uint32	fileExtensionChecksum;
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class CFileLibrary : public Spt::Class
{
protected:
	enum
	{
		vMAX_LIB_FILES = 128
	};

public:
	CFileLibrary();
	virtual 			~CFileLibrary();

public:
	bool 				Load( const char* p_fileName, bool assertOnFail, bool async_load );
	bool				PostLoad( bool assertOnFail, int file_size );
	bool				LoadFinished() const;

public:
	int					GetNumFiles() const;
	uint32*				GetFileData( int index );
	uint32*				GetFileData( uint32 name, uint32 extension, bool assertOnFail = true );
	const SFileInfo*	GetFileInfo( int index ) const;
	const SFileInfo*	GetFileInfo( uint32 name, uint32 extension, bool assertOnFail = true ) const;
	bool				ClearFile( uint32 name, uint32 extension );
	bool				FileExists( uint32 name, uint32 extension );

protected:
	CAsyncFileHandle*	mp_fileHandle;
#ifdef __PLAT_NGC__
	uint32				m_aramOffset;
#else
	uint8* 				mp_baseBuffer;
	uint8* 				mp_fileBuffer;
#endif		// __PLAT_NGC__
	int					m_numFiles;
	int					m_originalNumFiles;
	SFileInfo			m_fileInfo[vMAX_LIB_FILES];
#ifdef __PLAT_NGC__
	uint32				m_fileOffsets[vMAX_LIB_FILES];
#endif		// __PLAT_NGC__
	uint32*				mp_filePointers[vMAX_LIB_FILES];
	bool				m_dataLoaded;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace File

#endif	// __SYS_FILE_FILELIBRARY_H




================================================
FILE: Code/Sys/File/PRE.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate3													**
**																			**
**	Module:									 								**
**																			**
**	File name:																**
**																			**
**	Created by:		rjm														**
**																			**
**	Description:						 									**
**																			**
*****************************************************************************/

// start autoduck documentation
// @DOC pre
// @module pre | None
// @subindex Scripting Database
// @index script | pre

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifdef	__PLAT_NGPS__
#include 
#endif		//	__PLAT_NGPS__
#include 
//#include 
#include 
#include 
#include 
#include 
#include 

// cd shared by the music streaming stuff...  ASSERT if file access attempted
// while music is streaming:
#include 

// script stuff
#include  
#include 

#ifdef __PLAT_NGC__
#include "sys/ngc/p_aram.h"
#include "sys/ngc/p_dma.h"
#include "sys/ngc/p_display.h"
#ifdef __NOPT_FINAL__
#define __PRE_ARAM__
#endif		// __NOPT_FINAL__
#endif

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

#define DEBUG_PRE 0

#define	PRE_NAME_OFFSET 16		// was 12, but then I added an extra checksum field

namespace File
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define CURRENT_PRE_VERSION	0xabcd0003			// as of 3/14/2001
//#define CURRENT_PRE_VERSION	0xabcd0001		// until 3/14/2001

#define RINGBUFFERSIZE		 4096	/* N size of ring buffer */	
#define MATCHLIMIT		   18	/* F upper limit for match_length */
#define THRESHOLD	2   /* encode string into position and length */

#define WriteOut(x) 	{*pOut++ = x;}

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

PreMgr *PreMgr::sp_mgr = NULL;
bool    PreMgr::s_lastExecuteSuccess = false; 

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/



#define USE_BUFFER	1		 // we don't need no stinking buffer!!!!
 
#if USE_BUFFER
#ifdef	__PLAT_NGPS__
// ring buffer is just over 4K 4096+17), 
// so fits nicely in the PS2's 16K scratchpad 
unsigned char	text_buf[RINGBUFFERSIZE + MATCHLIMIT - 1];	
// Note:  if we try to use the scratchpad, like this
// then the code actually runs slower
// if we want to optimize this, then it should
// be hand crafted in assembly, using 128bit registers
//	const unsigned char * text_buf = (unsigned char*) 0x70000000;
//	#define text_buf ((unsigned char*) 0x70000000)
#else
unsigned char
		text_buf[RINGBUFFERSIZE + MATCHLIMIT - 1];	/* ring buffer of size N,
			with extra F-1 bytes to facilitate string comparison */
#endif
#endif


#ifdef __PRE_ARAM__
#define	ReadInto(x)		if (!Len) break; Len--; x = get_byte( p_buffer ) 
#define	ReadInto2(x)	Len--; x = get_byte( p_buffer ) 	  // version that knows Len is Ok
#else
#define	ReadInto(x)		if (!Len) break; Len--; x = *pIn++ 
#define	ReadInto2(x)	Len--; x = *pIn++ 	  // version that knows Len is Ok
#endif		// __PRE_ARAM__


// Decode an LZSS encoded stream
// Runs at approx 12MB/s on PS2	 without scratchpad (which slows it down in C)
// a 32x CD would run at 4.8MB/sec, although we seem to get a lot less than this
// with our current file system, more like 600K per seconds.....
// Need to write a fast streaming file system....

#ifdef __PRE_ARAM__
#define INPUT_BUFFER_SIZE (16 * 1024)
static uint32 p_aram_in;
static int aram_len = 0;
static int buffer_bytes = 0;
static int buffer_offset = 0;
static unsigned char get_byte( unsigned char * p_buffer )
{
	if ( buffer_bytes == 0 )
	{
		int toread;
		if ( aram_len >= INPUT_BUFFER_SIZE )
		{
			toread = INPUT_BUFFER_SIZE;
		}
		else
		{
			toread = aram_len;
		}

		NsDMA::toMRAM( p_buffer, p_aram_in, toread );
		p_aram_in += INPUT_BUFFER_SIZE;
		aram_len -= INPUT_BUFFER_SIZE;
		buffer_bytes = toread; 
		buffer_offset = 0;
	}
	unsigned char bb = p_buffer[buffer_offset];
	buffer_bytes--;
	buffer_offset++;
	return bb;
}
#endif		// __PRE_ARAM__

void DecodeLZSS(unsigned char *pIn, unsigned char *pOut, int Len)	/* Just the reverse of Encode(). */
{
	int  i, j, k, r, c;
//	uint64	LongWord;
//	int bytes = 0;
//	unsigned char *pScratch;
	unsigned int  flags;

	// Ensure we have decent values for the decode length
	Dbg_Assert(( Len >= 0 ) && ( Len < 0x2000000 ));

	if(( Len < 0 ) || ( Len >= 0x2000000 ))
	{
		while( 1 );
	}
#ifdef __PRE_ARAM__
	unsigned char p_buffer[INPUT_BUFFER_SIZE];
	p_aram_in = (uint32)pIn;
	aram_len = Len;
#endif		// __PRE_ARAM__


//	int basetime =  (int) Tmr::ElapsedTime(0);
//	int len = Len;

//	int	OutBytes = 4;
//	int	OutWord = 0;

	#if USE_BUFFER
	for (i = 0; i < RINGBUFFERSIZE - MATCHLIMIT; i++)
		 text_buf[i] = ' ';
	r = RINGBUFFERSIZE - MATCHLIMIT;
	#else
	r = RINGBUFFERSIZE - MATCHLIMIT;
	#endif
	flags = 0;
	for ( ; ; )
	{
		if (((flags >>= 1) & 256) == 0)
		{
			ReadInto(c);
			flags = c | 0xff00;			/* uses higher byte cleverly */
		}										/* to count eight */
		if (flags & 1)
		{
			ReadInto(c);
			//			putc(c, outfile);
			WriteOut(c);
			#if USE_BUFFER
			text_buf[r++] = c;
			r &= (RINGBUFFERSIZE - 1);
			#else
			r++;
//			r &= (RINGBUFFERSIZE - 1);	  // don't need to wrap r until it is used
			#endif
		}
		else
		{
			ReadInto(i);			
			ReadInto2(j);			// note, don't need to check len on this one.... 
			
			i |= ((j & 0xf0) << 4);						// i is 12 bit offset
			
			#if !USE_BUFFER
			j = (j & 0x0f) + THRESHOLD+1;				// j is 4 bit length (above the threshold)
			unsigned char *pStream;
			r &= (RINGBUFFERSIZE - 1);					// wrap r around before it is used
			pStream = pOut - r;					  		// get base of block
			if (i>=r)										// if offset > r, then
				pStream -= RINGBUFFERSIZE;				// it's the previous block
			pStream += i;									// add in the offset to the base
			r+=j;												// add size to r
			while (j--)										// copy j bytes
				WriteOut(*pStream++);
			#else
			
			j = (j & 0x0f) + THRESHOLD;				// j is 4 bit length (above the threshold)
			for (k = 0; k <= j; k++)					// just copy the bytes
			{
				c =  text_buf[(i+k) & (RINGBUFFERSIZE - 1)]; 
				WriteOut(c);
				text_buf[r++] = c;
				r &= (RINGBUFFERSIZE - 1);
			}
			#endif
		}
	}
//	int Time = (int) Tmr::ElapsedTime(basetime);
//	if (Time > 5)
//	{
//		printf("decomp time is %d ms, for %d bytes,  %d bytes/second\n", Time,len, len * 1000 /Time );
//	}

}

void EndOfDecodeLZSS( void )
{
} 


void PreFile::s_delete_file(_File *pFile, void *pData)
{
	Dbg_Assert(pFile);
	delete pFile;
}




PreFile::PreFile(uint8 *p_file_buffer, bool useBottomUpHeap)
{
	m_use_bottom_up_heap=useBottomUpHeap;
	
	mp_table = new Lst::StringHashTable<_File>(4);	

	Dbg_AssertPtr(p_file_buffer);

	mp_buffer = p_file_buffer;
	#ifdef __NOPT_ASSERT__
#ifdef __PRE_ARAM__
	uint version;
	NsDMA::toMRAM( &version, (uint32)mp_buffer + 4, 4 );
#else
	uint version = 	*((int *) (mp_buffer + 4));
#endif		// __PRE_ARAM__
	Dbg_MsgAssert(version == CURRENT_PRE_VERSION,( "PRE file version (%x) not current (%x)",version,CURRENT_PRE_VERSION));
	#endif
#ifdef __PRE_ARAM__
	NsDMA::toMRAM( &m_numEntries, (uint32)mp_buffer + 8, 4 );
#else
	m_numEntries = *((int *)(mp_buffer + 8));
#endif		// __PRE_ARAM__

	uint8 *pEntry = mp_buffer + 12;
	for (int i = 0; i < m_numEntries; i++)
	{
#ifdef __PRE_ARAM__
		int data_size;
		int compressed_data_size;
		short text_size;
		NsDMA::toMRAM( &data_size, (uint32)pEntry, 4 );
		NsDMA::toMRAM( &compressed_data_size, (uint32)pEntry + 4, 4 );
		NsDMA::toMRAM( &text_size, (uint32)pEntry + 8, 2 );
#else
		int data_size 				= *((int *) pEntry);
		int compressed_data_size 	= *((int *) (pEntry + 4));
		int text_size	 			= *((short *) (pEntry + 8));
#endif		// __PRE_ARAM__
		int actual_data_size = (compressed_data_size != 0) ? compressed_data_size : data_size;
			
#ifdef __PRE_ARAM__
		char pName[1024];
		NsDMA::toMRAM( pName, (uint32)pEntry + PRE_NAME_OFFSET, text_size );
#else
		char *pName = (char *) pEntry + PRE_NAME_OFFSET;
#endif		// __PRE_ARAM__
		uint8 *pCompressedData = pEntry + PRE_NAME_OFFSET + text_size;
		
		_File *pFile;
		if (m_use_bottom_up_heap)
		{
			pFile = new (Mem::Manager::sHandle().BottomUpHeap()) _File;
		}
		else
		{
			pFile = new (Mem::Manager::sHandle().TopDownHeap()) _File;
		}	

		if (!mp_table->GetItem(pName)) 
		{
			// file is not in table, safe to add
			mp_table->PutItem(pName, pFile);
			
			pFile->compressedDataSize = compressed_data_size;
			pFile->pCompressedData = pCompressedData; 
			pFile->pData = NULL;
			pFile->m_position = 0;
			pFile->m_filesize = data_size;
		}
		else
			// Somehow, file is already in table, just kill it
			// Later, I'll want to add an assert
			delete pFile;
		
#		if DEBUG_PRE
		printf("   %s, size %d\n", pName, data_size);
#		endif
		
		pEntry += PRE_NAME_OFFSET + text_size + ((actual_data_size + 3) & (~3));
	}

#	if DEBUG_PRE
	printf("Done loading PRE\n");
#	endif
	
	mp_activeFile = NULL;
	m_numOpenAsyncFiles = 0;
}



PreFile::~PreFile()
{
#ifdef __PRE_ARAM__
	NsARAM::free( (uint32)mp_buffer );
#else
	delete mp_buffer;
#endif		// __PRE_ARAM__
	mp_table->HandleCallback(s_delete_file, NULL);
	mp_table->FlushAllItems();

	delete mp_table;

	Dbg_MsgAssert(m_numOpenAsyncFiles == 0, ("Can't unload Pre because there are still %d async files open", m_numOpenAsyncFiles));
}


bool PreFile::FileExists(const char *pName)
{
	
	_File *pFile = mp_table->GetItem(pName, false);
	return (pFile != NULL);
}

// returns handle pointer
PreFile::FileHandle *PreFile::GetContainedFile(const char *pName)
{
	
	_File *pFile = mp_table->GetItem(pName, false);
	if (!pFile) 
		return NULL;
	
	PreFile::FileHandle *pHandle = pFile;
	//// kinda roundabout, but sets mp_activeFile
	GetContainedFileByHandle(pHandle);
	
#ifdef __PLAT_NGC__
	NsDisplay::doReset();
#endif		// __PLAT_NGC__

	// do we need to fetch file data?
	if (!mp_activeFile->pData)
	{
		if (mp_activeFile->compressedDataSize)
		{
			Mem::PushMemProfile((char*)pName);
			if (m_use_bottom_up_heap)
			{
				mp_activeFile->pData = new (Mem::Manager::sHandle().BottomUpHeap()) uint8[mp_activeFile->m_filesize];		
			}
			else
			{
				mp_activeFile->pData = new (Mem::Manager::sHandle().TopDownHeap()) uint8[mp_activeFile->m_filesize];		
			}	
			Mem::PopMemProfile();
			// need to uncompress data
			DecodeLZSS(mp_activeFile->pCompressedData, mp_activeFile->pData, mp_activeFile->compressedDataSize);	
		}
#ifdef __PRE_ARAM__
		else
		{
			// Just DMA to main RAM.
			Mem::PushMemProfile((char*)pName);
			if (m_use_bottom_up_heap)
			{
				mp_activeFile->pData = new (Mem::Manager::sHandle().BottomUpHeap()) uint8[mp_activeFile->m_filesize];
			}
			else
			{
				mp_activeFile->pData = new (Mem::Manager::sHandle().TopDownHeap()) uint8[mp_activeFile->m_filesize];
			}	
			Mem::PopMemProfile();
			NsDMA::toMRAM( mp_activeFile->pData, (uint32)mp_activeFile->pCompressedData, mp_activeFile->m_filesize );
		}
#endif		// __PRE_ARAM__
	}

	

	return pHandle;
}


// allocate memory and load file directly from a pre file, if it is there
// This avoids the problem of having to have the decompressed file in memory twice
// when we are loading directly, like with Pip::Load()
// using this enables us to actually load network game, where there is 1MB less heap during loading
//
// returns a pointer to the file in memory
// or NULL if the file is not in this pre file.
// optional parameter p_dest, if set to anything other then NULL, then load file to this destination
void *PreFile::LoadContainedFile(const char *pName,int *p_size, void *p_dest)
{

//	printf ("LoadContainedFile(%s\n",pName);
	
	_File *pFile = mp_table->GetItem(pName, false);
	if (!pFile) 
	{
		return NULL;
	}	
	
	*p_size = pFile->m_filesize;

	// If destination was passed as NULL, then allocate memory	
	if (!p_dest)
	{
		p_dest = Mem::Malloc(pFile->m_filesize);		
	}
	
	// do we need to deompress file data?
	if (!pFile->pData)
	{
		if (pFile->compressedDataSize)
		{
			// need to uncompress data
			//DecodeLZSS(mp_activeFile->pCompressedData, mp_activeFile->pData, mp_activeFile->compressedDataSize);	
			DecodeLZSS(pFile->pCompressedData, (uint8*)p_dest, pFile->compressedDataSize);	
		}
		else
		{
#ifdef __PRE_ARAM__
			// Just DMA to main RAM.
			NsDMA::toMRAM( p_dest, (uint32)pFile->pCompressedData, pFile->m_filesize );
#else
			memcpy(p_dest,(void*)pFile->pCompressedData,pFile->m_filesize);
#endif		// __PRE_ARAM__
		}
	}
	else
	{
//		printf ("Copying %d bytes from %p to %p\n",pFile->sky.SOF,p_dest,(void*)pFile->pData);
		memcpy(p_dest,(void*)pFile->pData,pFile->m_filesize);
	}
	return  p_dest;
}
	


uint8 *PreFile::GetContainedFileByHandle(PreFile::FileHandle *pHandle)
{
	mp_table->IterateStart();
	_File *pFile = mp_table->IterateNext();
	while(pFile)
	{
		uint8 *pCompressedData = pFile->pCompressedData;
		if (pCompressedData && pFile == pHandle)
		{
			mp_activeFile = pFile;
			
			if (mp_activeFile->compressedDataSize)
				return mp_activeFile->pData;
			else
				return mp_activeFile->pCompressedData;
		}
		pFile = mp_table->IterateNext();
	}

	return NULL;
}



void PreFile::Reset()
{
	
	Dbg_AssertPtr(mp_activeFile);

	mp_activeFile->m_position = 0;
}



uint32 PreFile::Read(void *addr, uint32 count)
{
	
	Dbg_AssertPtr(mp_activeFile);

	int seek_offs = mp_activeFile->m_position;
	unsigned int limit = mp_activeFile->m_filesize - seek_offs;
	int copy_number = (count <= limit) ? count : limit; 
#ifdef __PRE_ARAM__
	memcpy(addr, mp_activeFile->pData + mp_activeFile->m_position, copy_number);
#else
	if (mp_activeFile->compressedDataSize)
	{
		Dbg_MsgAssert(mp_activeFile->pData,( "file not uncompressed"));
		memcpy(addr, mp_activeFile->pData + mp_activeFile->m_position, copy_number);
	}
	else
	{
		memcpy(addr, mp_activeFile->pCompressedData + mp_activeFile->m_position, copy_number);
	}
#endif		// __PRE_ARAM__

	mp_activeFile->m_position += copy_number;

#if DEBUG_PRE
		printf("PRE: read %d bytes from file, handle 0x%x\n", copy_number, (int) mp_activeFile->pData);
#endif
	return copy_number;
}



int PreFile::Eof()
{
	
	Dbg_AssertPtr(mp_activeFile);

	if (mp_activeFile->m_position >= mp_activeFile->m_filesize)
	{
#if DEBUG_PRE
		printf("PRE: at end of file\n");
#endif
		return 1;
	}

#if DEBUG_PRE
	printf("PRE: not at end of file\n");
#endif
	return 0;
}



void PreFile::Open(bool async)
{
	if (async)
	{
		m_numOpenAsyncFiles++;
	}
}

void PreFile::Close(bool async)
{
	
	//Dbg_MsgAssert(mp_activeFile->pData,( "file not uncompressed"));

	if (mp_activeFile->pData)
		delete mp_activeFile->pData;
	mp_activeFile->pData = NULL;

	if (async)
	{
		m_numOpenAsyncFiles--;
		Dbg_MsgAssert(m_numOpenAsyncFiles >= 0, ("PreFile: m_numOpenAsyncFiles is negative after Close()"));
	}
}



int PreFile::Seek(long offset, int origin)
{
	int32 old_pos = mp_activeFile->m_position;

	// SEEK_CUR, SEEK_END, SEEK_SET
	switch(origin)
	{
		case SEEK_CUR:
			mp_activeFile->m_position += offset;
			break;
		case SEEK_END:
			mp_activeFile->m_position = mp_activeFile->m_filesize - offset;
			break;
		case SEEK_SET:
			mp_activeFile->m_position = offset;
			break;
		default:
			return -1;
	}

	if (mp_activeFile->m_position < 0 || mp_activeFile->m_position > mp_activeFile->m_filesize)
	{
		mp_activeFile->m_position = old_pos;
		return -1;
	}

	return 0;
}



PreMgr::PreMgr() 
{
	mp_table = new Lst::StringHashTable(4);

	sp_mgr = this;


	mp_activeHandle = NULL;
	mp_activeData = NULL;

	mp_activeNonPreHandle = NULL;

	m_num_pending_pre_files = 0;
}



PreMgr::~PreMgr()
{
	delete mp_table;
}



// Returns handle
// Not frequently called
PreFile::FileHandle *PreMgr::getContainedFile(const char *pName)
{
	
	Dbg_AssertPtr(pName);

	// replace all '/' with '\'
	char cleaned_name[128];
	const char *pCharIn = pName;
	char *pCharOut = cleaned_name;
	while (1)
	{
		*pCharOut = *pCharIn;
		if (*pCharIn == '\0') break;
		if (*pCharOut == '/') *pCharOut = '\\';
		pCharIn++;
		pCharOut++;		
	}

	PreFile::FileHandle *pHandle = NULL;

	mp_table->IterateStart();
	PreFile *pPre = mp_table->IterateNext();
	while(pPre)
	{
		pHandle = pPre->GetContainedFile(cleaned_name);
		if (pHandle) 
		{
			mp_activePre = pPre;
			mp_activeHandle = pHandle;
			mp_activeData = pPre->GetContainedFileByHandle(pHandle);
#			ifdef __PLAT_NGPS__
			scePrintf("+++ %s is in PRE\n", cleaned_name);
#			endif
			return pHandle;
		}
		pPre = mp_table->IterateNext();
	}

#	ifdef __PLAT_NGPS__
	scePrintf("--- %s not found in PRE\n", cleaned_name);
#	endif
	return NULL;
}

// returns true if the file exists in any of the pre files
bool	PreMgr::fileExists(const char *pName)
{
	Dbg_AssertPtr(pName);
	// replace all '/' with '\'
	char cleaned_name[128];
	const char *pCharIn = pName;
	char *pCharOut = cleaned_name;
	while (1)
	{
		*pCharOut = *pCharIn;
		if (*pCharIn == '\0') break;
		if (*pCharOut == '/') *pCharOut = '\\';
		pCharIn++;
		pCharOut++;		
	}

	mp_table->IterateStart();
	PreFile *pPre = mp_table->IterateNext();
	while(pPre)
	{
		if (pPre->FileExists(cleaned_name))
		{
			return true;
		}
		pPre = mp_table->IterateNext();
	}
	return false;
}



// returns pointer to data
uint8 *PreMgr::getContainedFileByHandle(PreFile::FileHandle *pHandle)
{
	
	Dbg_AssertPtr(pHandle);

	// if we know that the file in question is not in the PRE system,
	// then it's a regular file, don't waste time looking for it
	if (mp_activeNonPreHandle == pHandle)
		return NULL;
	
	if (mp_activeHandle == pHandle)
		// mp_activePre will be unchanged
		return mp_activeData;
	
	uint8 *pData = NULL;
	mp_table->IterateStart();
	PreFile *pPre = mp_table->IterateNext();
	while(pPre)
	{
		pData = pPre->GetContainedFileByHandle(pHandle);
		if (pData)
		{
			mp_activePre = pPre;
			mp_activeHandle = pHandle;
			mp_activeData = pData;			
			return pData;
		}
		pPre = mp_table->IterateNext();
	}

	// obviously this file is not in the PRE system, mark as such
	mp_activeNonPreHandle = pHandle;
	return NULL;
}



// there's a wrapper around this now, so that we can do
// some memory-context switching
void PreMgr::loadPre(const char *pFilename, bool async, bool dont_assert, bool useBottomUpHeap)
{
#ifdef DVDETH
	m_blockPreLoading = true;
#else
	m_blockPreLoading = (bool) Script::GetInt("block_pre_loading", false);
#endif		// DVDETH 

	if (m_blockPreLoading)
		return;

	// Turn off async for platforms that don't support it
	if (!File::CAsyncFileLoader::sAsyncSupported())
	{
		async = false;
	}

#ifdef __PRE_ARAM__
	Dbg_MsgAssert(!async, ("Async loading not implemented for ARAM transfer"));
#endif

	if( !async && Pcm::UsingCD() )
	{
		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
		return;
	}

	// Moved this to below the Pcm::UsingCD() call as that is used (bad!!) to turn off
	// music and streams.
#	ifdef __PLAT_NGPS__
//	scePrintf("Loading PRE file %s...\n", pFilename);
#	endif

	char fullname[256];
	sprintf(fullname, "pre\\%s", pFilename);

#	ifdef __PLAT_XBOX__
	// Replace the .pre extension (if one exists) with .prx for Xbox PRE file.
	if( strrchr( pFilename, '.' ) && ( strlen( fullname ) > 4 ))
	{
		fullname[strlen( fullname ) - 1] = 'x';
	}
#	endif

#	if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
	Tmr::Time basetime = Tmr::ElapsedTime(0);
#endif

	int file_size;
	uint8 *pFile = NULL;

	// Try loading asynchronously
	if (async)
	{
		Dbg_MsgAssert(m_num_pending_pre_files < MAX_NUM_ASYNC_LOADS, ("Too many async LoadPre's pending"));

		CAsyncFileHandle *p_fileHandle = CAsyncFileLoader::sOpen( fullname, !async );
		if (p_fileHandle)
		{
			Dbg_MsgAssert(strlen(pFilename) < MAX_COMPACT_FILE_NAME, ("Pre file name %s is greater than %d bytes", pFilename, MAX_COMPACT_FILE_NAME - 1));

			// Add to pending list
			strcpy(m_pending_pre_files[m_num_pending_pre_files].m_file_name, pFilename);
			m_pending_pre_files[m_num_pending_pre_files++].mp_file_handle = p_fileHandle;

			file_size = p_fileHandle->GetFileSize();
			Dbg_MsgAssert(file_size, ("Pre file size is 0"));

			Mem::PushMemProfile((char*)fullname);
			if (useBottomUpHeap)
			{
				pFile = new (Mem::Manager::sHandle().BottomUpHeap()) uint8[file_size];
			}
			else
			{
				pFile = new (Mem::Manager::sHandle().TopDownHeap()) uint8[file_size];
			}	
			Mem::PopMemProfile();

			// Set the callback
			p_fileHandle->SetCallback(async_callback, (unsigned int) this, (unsigned int) pFile);

			// read the file in
			p_fileHandle->Read( pFile, 1, file_size );
			return;
		}
	}

	// If we got here, we didn't do an async load
	file_size = CanFileBeLoadedQuickly( fullname );
	if ( file_size )
	{
		Mem::PushMemProfile((char*)fullname);
		if (useBottomUpHeap)
		{
			pFile = new (Mem::Manager::sHandle().BottomUpHeap()) uint8[file_size];
		}
		else
		{
			pFile = new (Mem::Manager::sHandle().TopDownHeap()) uint8[file_size];
		}	
		Mem::PopMemProfile();
		bool fileLoaded= LoadFileQuicklyPlease( fullname, pFile );
		if ( !fileLoaded )
		{
			printf( "pre file %s failed to load quickly.\n", fullname );
			Dbg_MsgAssert( 0,( "Fire Matt - pre file didn't load quickly." ));
		}
	}
	else
	{
			void *fp = File::Open(fullname, "rb");
			if (!fp)
			{
			// always run the code below if CD build
				if (dont_assert || Config::CD()) 
				{
					printf("couldn't open %s\n", fullname);
					return;
				}
				Dbg_MsgAssert(0,( "couldn't open %s\n", fullname));
			}


#ifdef __PRE_ARAM__
			File::Read(&file_size, 4, 1, fp);
			Dbg_MsgAssert(file_size > 0,( "%s has incorrect file size\n", fullname));
			if (Config::CD())
			{
				if (file_size <= 0) printf("%s has incorrect file size\n", fullname);
			}	

			// Stream the file into ARAM.
			#define PRE_BUFFER_SIZE (16 * 1024)
			char p_buffer[PRE_BUFFER_SIZE];

			uint32 p_aram;
			if (useBottomUpHeap)
			{
				p_aram = NsARAM::alloc( file_size, NsARAM::BOTTOMUP );
			}
			else
			{
				p_aram = NsARAM::alloc( file_size, NsARAM::TOPDOWN );
			}	
			if ( p_aram )
			{
				int adjust = 4;
				uint32 toread = file_size;
				uint32 current_aram_offset = p_aram;
				while ( toread )
				{
					uint32 thistime = PRE_BUFFER_SIZE;
					if ( toread < PRE_BUFFER_SIZE ) thistime = toread;

					File::Read(&p_buffer[adjust], 1, thistime - adjust, fp);
					DCFlushRange ( p_buffer, thistime );
					NsDMA::toARAM( current_aram_offset, p_buffer, thistime );
					toread -= thistime;
					current_aram_offset += thistime;
					adjust = 0;
				}
				File::Close(fp);
				pFile = (uint8 *)p_aram;
			}
#else
			file_size = File::GetFileSize(fp);
		// Now allocates the .PRE file from the top of the heap, to avoid fragmentation.
			Mem::PushMemProfile((char*)fullname);
			if (useBottomUpHeap)
			{
				pFile = new (Mem::Manager::sHandle().BottomUpHeap()) uint8[file_size];
			}
			else
			{
				pFile = new (Mem::Manager::sHandle().TopDownHeap()) uint8[file_size];
			}	
			Mem::PopMemProfile();
		//uint8 *pFile = new uint8[file_size];
			File::Read(pFile, 1, file_size, fp);

			int read_file_size = *((int *) pFile);
			Dbg_MsgAssert(file_size == read_file_size,( "%s has incorrect file size: %d vs. expected %d\n", fullname, file_size, read_file_size));
			if (Config::CD())
			{
				if (file_size != read_file_size) printf("%s has incorrect file size\n", fullname);
			}

			File::Close(fp);
#endif		// __PRE_ARAM__
		}

#	if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
	printf("load time for file %s size %d is %d ms\n", pFilename, file_size, (int) Tmr::ElapsedTime(basetime));
#endif

	// the PRE file object winds up at the top of the heap, too. This is fine because
	// it will be unloaded at the same time as the big file buffer
	if (useBottomUpHeap)
	{
		if (!mp_table->PutItem(pFilename, new (Mem::Manager::sHandle().BottomUpHeap()) PreFile(pFile,useBottomUpHeap)))
			Dbg_MsgAssert(0,( "PRE %s loaded twice", pFilename));
	}
	else
	{
		if (!mp_table->PutItem(pFilename, new (Mem::Manager::sHandle().TopDownHeap()) PreFile(pFile)))
			Dbg_MsgAssert(0,( "PRE %s loaded twice", pFilename));
	}		
}

// Finishes the loading sequence
void   	PreMgr::postLoadPre(CAsyncFileHandle *p_file_handle, uint8 *pData, int size)
{
	// Find entry in pending list
	for (int i = 0; i < m_num_pending_pre_files; i++)
	{
		if (m_pending_pre_files[i].mp_file_handle == p_file_handle)
		{
			// the PRE file object winds up at the top of the heap, too. This is fine because
			// it will be unloaded at the same time as the big file buffer
			if (!mp_table->PutItem(m_pending_pre_files[i].m_file_name, new (Mem::Manager::sHandle().TopDownHeap()) PreFile(pData)))
			{
				Dbg_MsgAssert(0,( "PRE %s loaded twice", m_pending_pre_files[i].m_file_name));
			}

			// Copy last one to this position
			m_pending_pre_files[i] = m_pending_pre_files[--m_num_pending_pre_files];
			return;
		}
	}

	Dbg_MsgAssert(0, ("PreMgr::postLoadPre(): Can't find entry in pending Pre file list"));
}

// Handles all the async callbacks.  Makes sure we only do something on the appropriate callback
void	PreMgr::async_callback(CAsyncFileHandle *p_file_handle, EAsyncFunctionType function,
							   int result, unsigned int arg0, unsigned int arg1)
{
	if (function == File::FUNC_READ)
	{
		PreMgr *p_mgr = (PreMgr *) arg0;

		p_mgr->postLoadPre(p_file_handle, (uint8 *) arg1, result);
	}
}

// Returns point in string where it will fit in compact space
char *	PreMgr::getCompactFileName(char *pName)
{
	int length = strlen(pName);

	if (length < MAX_COMPACT_FILE_NAME)
	{
		return pName;
	}
	else
	{
		int offset = length - (MAX_COMPACT_FILE_NAME - 1);

		return pName + offset;
		//return (char *) ((int) pName + offset);
	}
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/


DefineSingletonClass(PreMgr, "PRE Manager");



bool PreMgr::InPre(const char *pFilename)
{
	

	return mp_table->GetItem( pFilename );
}



void PreMgr::LoadPre(const char *pFilename, bool async, bool dont_assert, bool useBottomUpHeap)
{
	// GJ:  This function is a wrapper around loadPRE(), to
	// make sure that all allocations go on the top-down heap
	
	// K: Unless they want to use the bottom up heap :)
	// Needed so that the anims pre can be put on the bottom up, so that the decompressed anims
	// can be put on the top-down, then the pre removed without leaving a hole.
	if (useBottomUpHeap)
	{
		//printf("Loading %s to bottom up heap\n",pFilename);
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
	}
	else
	{
		//printf("Loading %s to top down heap\n",pFilename);
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	}	
	loadPre(pFilename, async, dont_assert, useBottomUpHeap);
	Mem::Manager::sHandle().PopContext();	
}


// This function exisits for historical reasons
void PreMgr::LoadPrePermanently(const char *pFilename, bool async, bool dont_assert)
{

	// Load the pre file...
	// This will go on the top-down heap by default
	LoadPre(pFilename, async, dont_assert);

}



void PreMgr::UnloadPre(const char *pFilename, bool dont_assert)
{
#	ifdef __PLAT_NGPS__
//	scePrintf("Unloading PRE file %s\n", pFilename);
#	endif
	//printf("Unloading PRE file %s\n", pFilename);
	
	if (m_blockPreLoading)
		return;
	
	PreFile *pThePre = mp_table->GetItem(pFilename);
	if (!pThePre)
	{
		if (dont_assert) return;
#	ifndef __PLAT_NGC__
		Dbg_MsgAssert(0,( "PRE file %s not in PRE manager", pFilename));
#	endif
	}

	mp_table->FlushItem(pFilename);
	delete pThePre;
}

bool PreMgr::IsLoadPreFinished(const char *pFilename)
{
	// If it's in the pending list, it isn't done loading
	for (int i = 0; i < m_num_pending_pre_files; i++)
	{
		if (strcmp(m_pending_pre_files[i].m_file_name, pFilename) == 0)
		{
			return false;
		}
	}

	Dbg_MsgAssert(InPre(pFilename), ("IsLoadPreFinished(): Can't find Pre file"));

	return true;
}

bool PreMgr::AllLoadPreFinished()
{
	return m_num_pending_pre_files == 0;
}

void PreMgr::WaitLoadPre(const char *pFilename)
{
	while (!IsLoadPreFinished(pFilename))
	{
		// We got to call this to allow callbacks to come through
		CAsyncFileLoader::sWaitForIOEvent(false);
	}
}

void PreMgr::WaitAllLoadPre()
{
	while (!AllLoadPreFinished())
	{
		// We got to call this to allow callbacks to come through
		CAsyncFileLoader::sWaitForIOEvent(false);
	}
}

bool PreMgr::sPreEnabled()
{
	return sp_mgr != NULL;
}

bool PreMgr::sPreExecuteSuccess()
{
	return s_lastExecuteSuccess; 
}

bool PreMgr::pre_fexist(const char *name)
{
	
	Dbg_MsgAssert(name,( "requesting file NULL"));	
	
	if (sp_mgr->fileExists(name)) 
	{
#		if DEBUG_PRE
		printf("PRE: file %s exists\n", name);
#		endif
		s_lastExecuteSuccess = true;
		return true;
	}
//	if ( Pcm::UsingCD( ) )
//	{
//		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
//		return false;
//	}

	return s_lastExecuteSuccess = false;
}



PreFile::FileHandle *PreMgr::pre_fopen(const char *name, const char *access, bool async)
{
	Dbg_MsgAssert(name,( "trying to open file NULL"));	

	PreFile::FileHandle *pHandle = sp_mgr->getContainedFile(name);
	if (pHandle)
	{
		sp_mgr->mp_activePre->Open(async);

		// if we are going to write the file, we want to use the regular file system
		const char *pChar = access;
		bool am_writing = false;
		while(*pChar)
		{
			if (*pChar != 'r' && *pChar != 'b')
				am_writing = true;
			pChar++;
		}
		
		if (am_writing)
		{
#			ifdef __PLAT_NGPS__
//			scePrintf("    writing file %s\n", name);
#			endif

			// am writing, so we don't need file open in PRE system
			sp_mgr->mp_activePre->Close(async);
		}
		else
		{
			// we're reading the file from the PRE system
#			if DEBUG_PRE
			printf("PRE: opened file %s, handle is 0x%x\n", name, (int) pHandle);
#			endif
			sp_mgr->mp_activePre->Reset();
			s_lastExecuteSuccess = true;
			return pHandle;
		}
	}

//	// if we get here, we are using the regular file system
//	if ( Pcm::UsingCD( ) )
//	{
//		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
//		return NULL;
//	}

	s_lastExecuteSuccess = false;
	return NULL;

	//return pHandle;
}



int PreMgr::pre_fclose(PreFile::FileHandle *fptr, bool async)
{
		
	Dbg_MsgAssert(fptr,( "calling fclose with invalid file ptr"));	
	
	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
#if DEBUG_PRE
		printf("PRE: closed file, handle 0x%x\n", (int) fptr);
#endif
		sp_mgr->mp_activePre->Close(async);
		s_lastExecuteSuccess = true;
		return 0;
	}
	
	s_lastExecuteSuccess = false;
	return 0;
}



size_t PreMgr::pre_fread(void *addr, size_t size, size_t count, PreFile::FileHandle *fptr)
{
		
	Dbg_MsgAssert(fptr,( "calling fread with invalid file ptr"));		

	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
		// read from a simulated file stream in PRE file
		Dbg_AssertPtr(sp_mgr->mp_activePre);
		s_lastExecuteSuccess = true;
		return sp_mgr->mp_activePre->Read(addr, size * count);
	}
//	if ( Pcm::UsingCD( ) )
//	{
//		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
//		return 0;
//	}
	
	s_lastExecuteSuccess = false;
	return 0;
}



size_t  PreMgr::pre_fwrite(const void *addr, size_t size, size_t count, PreFile::FileHandle *fptr)
{
		
	#ifdef __NOPT_ASSERT__
	uint8 *pData = 
	#endif
	sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "can't write to a PRE file"));
	
//	if ( Pcm::UsingCD( ) )
//	{
//		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
//		return 0;
//	}

	s_lastExecuteSuccess = false;
	return 0;
}



char *PreMgr::pre_fgets(char *buffer, int maxLen, PreFile::FileHandle *fptr)
{
		

	#ifdef __NOPT_ASSERT__
	uint8 *pData = 
	#endif
	sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "can't do string ops on a PRE file"));
	
	s_lastExecuteSuccess = false;
	return NULL;
}



int PreMgr::pre_fputs(const char *buffer, PreFile::FileHandle *fptr)
{
		

	#ifdef __NOPT_ASSERT__
	uint8 *pData = 
	#endif
	sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "can't do string ops on a PRE file"));
	
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_feof(PreFile::FileHandle *fptr)
{
		
	Dbg_MsgAssert(fptr,( "calling feof with invalid file ptr"));		

	uint8 *pData = 	sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
		Dbg_AssertPtr(sp_mgr->mp_activePre);
		s_lastExecuteSuccess = true;
		return sp_mgr->mp_activePre->Eof();
	}
	
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_fseek(PreFile::FileHandle *fptr, long offset, int origin)
{
		

	uint8 *pData = 	sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
		s_lastExecuteSuccess = true;
		return sp_mgr->mp_activePre->Seek(offset, origin);
	}

	Dbg_MsgAssert(!pData,( "seek not supported for PRE file"));
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_fflush(PreFile::FileHandle *fptr)
{
		

	#ifdef __NOPT_ASSERT__
	uint8 *pData = 
	#endif
	sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "flush not supported for PRE file"));
	
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_ftell(PreFile::FileHandle *fptr)
{
		

	#ifdef __NOPT_ASSERT__
	uint8 *pData = 
	#endif
	sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "tell supported for PRE file"));
	
	s_lastExecuteSuccess = false;
	return 0;
}


int	PreMgr::pre_get_file_size(PreFile::FileHandle *fptr)
{
	uint8 *pData = 	sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
		s_lastExecuteSuccess = true;
		return sp_mgr->mp_activePre->GetFileSize();
	}

	Dbg_MsgAssert(!pData,( "get_file_size not supported for PRE file"));
	s_lastExecuteSuccess = false;
	return 0;
}

int PreMgr::pre_get_file_position(PreFile::FileHandle *fptr)
{
	uint8 *pData = 	sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
		s_lastExecuteSuccess = true;
		return sp_mgr->mp_activePre->GetFilePosition();
	}

	Dbg_MsgAssert(!pData,( "get_file_position not supported for PRE file"));
	s_lastExecuteSuccess = false;
	return 0;
}


// @script | InPreFile | 
// @uparm "string" | filename
bool ScriptInPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	PreMgr* pre_mgr = PreMgr::Instance();
	return pre_mgr->InPre(pFilename);
}



// @script | LoadPreFile | 
// @uparm "string" | filename
// @flag async | Load Asynchronously
// @flag dont_assert | 
// @flag use_bottom_up_heap | 
bool ScriptLoadPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	PreMgr* pre_mgr = PreMgr::Instance();
	pre_mgr->LoadPre(pFilename, 
					 pParams->ContainsFlag(CRCD(0x90e07c79,"async")),
					pParams->ContainsFlag(CRCD(0x3d92465e,"dont_assert")),
					pParams->ContainsFlag(CRCD(0xa44c770f,"use_bottom_up_heap")));
	return true;
}



// @script | UnloadPreFile | 
// @flag BoardsPre | 
// @flag dont_assert | 
// @uparm "string" | filename
bool ScriptUnloadPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	PreMgr* pre_mgr = PreMgr::Instance();
	
	if (pParams->ContainsFlag("BoardsPre"))
	{
		
//		Mdl::Skate * pSkate = Mdl::Skate::Instance();
//		pSkate->UnloadBoardPreIfPresent(pParams->ContainsFlag("dont_assert"));
		printf ("STUBBED:  Unload BoardsPre, in ScriptUnloadPreFile\n");
		return true;
	}
	
	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	pre_mgr->UnloadPre(pFilename, pParams->ContainsFlag("dont_assert"));
	return true;
}

// @script | IsLoadPreFinished | Returns true if Pre file has finished loading
// @uparm "string" | filename
bool ScriptIsLoadPreFinished(Script::CStruct *pParams, Script::CScript *pScript)
{
	PreMgr* pre_mgr = PreMgr::Instance();

	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	return pre_mgr->IsLoadPreFinished(pFilename);
}

// @script | AllLoadPreFinished | Returns true if all LoadPre commands have completed
bool ScriptAllLoadPreFinished(Script::CStruct *pParams, Script::CScript *pScript)
{
	PreMgr* pre_mgr = PreMgr::Instance();

	return pre_mgr->AllLoadPreFinished();
}

// @script | WaitLoadPre | Waits for Pre file to finished loading
// @uparm "string" | filename
bool ScriptWaitLoadPre(Script::CStruct *pParams, Script::CScript *pScript)
{
	PreMgr* pre_mgr = PreMgr::Instance();

	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	pre_mgr->WaitLoadPre(pFilename);
	return true;
}

// @script | WaitAllLoadPre | Waits for all Pre files to finished loading
bool ScriptWaitAllLoadPre(Script::CStruct *pParams, Script::CScript *pScript)
{
	PreMgr* pre_mgr = PreMgr::Instance();

	pre_mgr->WaitAllLoadPre();
	return true;
}

// if a file is in a pre, then:
// allocate memory for the file
// if file is uncompressed
//   copy it down
// else
//   decompress

void * PreMgr::LoadFile(const char *pName, int *p_size, void *p_dest)
{
// NOTE: THIS IS JUST CUT AND PASTE FROM Pre::fileExists
	Dbg_AssertPtr(pName);
	// replace all '/' with '\'
	char cleaned_name[128];
	const char *pCharIn = pName;
	char *pCharOut = cleaned_name;
	while (1)
	{
		*pCharOut = *pCharIn;
		if (*pCharIn == '\0') break;
		if (*pCharOut == '/') *pCharOut = '\\';
		pCharIn++;
		pCharOut++;		
	}

	mp_table->IterateStart();
	PreFile *pPre = mp_table->IterateNext();
	while(pPre)
	{
		
		void *p_data = pPre->LoadContainedFile(cleaned_name,p_size, p_dest);
		if (p_data)
		{
			return p_data;
		}
		pPre = mp_table->IterateNext();
	}
	return NULL;

}



} // namespace File






================================================
FILE: Code/Sys/File/PRE.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2001 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:																**
**																			**
**	Module:									 								**
**																			**
**	File name:																**
**																			**
**	Created by:		rjm, 1/23/2001											**
**																			**
**	Description:						 									**
**																			**
*****************************************************************************/

#ifndef __CORE_FILE_PRE_H
#define __CORE_FILE_PRE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

namespace Lst
{
	template class StringHashTable;
}

namespace Script
{
	class CStruct;
	class CScript;
}	

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace File
{

						


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

bool ScriptInPreFile(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptLoadPreFile(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnloadPreFile(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptIsLoadPreFinished(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptAllLoadPreFinished(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptWaitLoadPre(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptWaitAllLoadPre(Script::CStruct *pParams, Script::CScript *pScript);

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class CAsyncFileHandle;
class PreMgr;
class  PreFile  : public Spt::Class
{
	

public:

	// The actual PRE file (it is public so that FileHandle can be made public).
	struct _File
	{
		int				compressedDataSize;
		uint8 *			pCompressedData;
		uint8 *			pData;
		int				m_position;
		int				m_filesize;
	};

	// Just typedef a file handle type since the file itself contains all the info
	typedef _File FileHandle;

	PreFile(uint8 *p_file_buffer, bool useBottomUpHeap=false);
	~PreFile();
	
	
	
	bool 		FileExists(const char *pName);
	void*		LoadContainedFile(const char *pName,int *p_size, void *p_dest = NULL);
	FileHandle*	GetContainedFile(const char *pName);
	uint8*		GetContainedFileByHandle(FileHandle *pHandle);

	void Reset();
	uint32 Read(void *addr, uint32 count);
	int Eof();
	void Open(bool async);
	void Close(bool async);
	int Seek(long offset, int origin);
	int TellActive( void )				{ if( mp_activeFile ){ return mp_activeFile->m_position; }else{ return 0; }}
	int GetFileSize( void )				{ if( mp_activeFile ){ return mp_activeFile->m_filesize; }else{ return 0; }}
	int GetFilePosition( void )			{ if( mp_activeFile ){ return mp_activeFile->m_position; }else{ return 0; }}

private:

	static void						s_delete_file(_File *pFile, void *pData);
	
	uint8 *							mp_buffer;
	int								m_numEntries;
	
	// maps filenames to pointers
	Lst::StringHashTable<_File> *	mp_table;

	_File *							mp_activeFile;

	int								m_numOpenAsyncFiles;
	
	bool							m_use_bottom_up_heap;
};




class  PreMgr  : public Spt::Class
{
	
	DeclareSingletonClass(PreMgr);

public:
	bool InPre(const char *pFilename);
	void LoadPre(const char *pFilename, bool async, bool dont_assert = false, bool useBottomUpHeap=false);
	void LoadPrePermanently(const char *pFilename, bool async, bool dont_assert = false);
	void UnloadPre(const char *pFilename, bool dont_assert = false);
	void * LoadFile(const char *pName, int *p_size, void *p_dest = NULL);

	// Async check functions
	bool IsLoadPreFinished(const char *pFilename);
	bool AllLoadPreFinished();
	void WaitLoadPre(const char *pFilename);
	void WaitAllLoadPre();

	static bool sPreEnabled();
	static bool sPreExecuteSuccess();
	
	static bool					pre_fexist(const char *name);
	static PreFile::FileHandle *pre_fopen(const char *name, const char *access, bool async = false);
	static int					pre_fclose(PreFile::FileHandle *fptr, bool async = false);
	static size_t				pre_fread(void *addr, size_t size, size_t count, PreFile::FileHandle *fptr);
	static size_t				pre_fwrite(const void *addr, size_t size, size_t count, PreFile::FileHandle *fptr);
	static char *				pre_fgets(char *buffer, int maxLen, PreFile::FileHandle *fptr);
	static int					pre_fputs(const char *buffer, PreFile::FileHandle *fptr);
	static int					pre_feof(PreFile::FileHandle *fptr);
	static int					pre_fseek(PreFile::FileHandle *fptr, long offset, int origin);
	static int					pre_fflush(PreFile::FileHandle *fptr);
	static int					pre_ftell(PreFile::FileHandle *fptr);
	static int					pre_get_file_size(PreFile::FileHandle *fptr);
	static int					pre_get_file_position(PreFile::FileHandle *fptr);

private:
	// Constants
	enum
	{
		MAX_COMPACT_FILE_NAME = 64,				// Only store the right-most characters of the filename to save space
		MAX_NUM_ASYNC_LOADS = 8,
	};

	// Holds data for pending async loads
	struct SPendingAsync
	{
		CAsyncFileHandle *			mp_file_handle;
		char						m_file_name[MAX_COMPACT_FILE_NAME];
	};

	PreMgr();
	~PreMgr();

	void				 loadPre(const char *pFilename, bool async, bool dont_assert = false, bool useBottomUpHeap=false);
    void    			 postLoadPre(CAsyncFileHandle *p_file_handle, uint8 *pData, int size);
	bool				 fileExists(const char *pName);
	PreFile::FileHandle *getContainedFile(const char *pName);
	uint8 *				 getContainedFileByHandle(PreFile::FileHandle *pHandle);

	static char *		 getCompactFileName(char *pName);		// Returns point in string where it will fit in compact space

	static void			 async_callback(CAsyncFileHandle *p_file_handle, EAsyncFunctionType function,
										int result, unsigned int arg0, unsigned int arg1);

	PreFile							*mp_activePre;
	PreFile::FileHandle *	 		mp_activeHandle;
	uint8 *							mp_activeData;
	// handle of current file being accessed from regular file system, for quick check
	void *							mp_activeNonPreHandle;

	static bool						s_lastExecuteSuccess;
	static PreMgr *					sp_mgr;
	
	Lst::StringHashTable *	mp_table;

	bool							m_blockPreLoading;

	// Async status
	SPendingAsync					m_pending_pre_files[MAX_NUM_ASYNC_LOADS];
	int								m_num_pending_pre_files;
};


} // namespace File

#endif	// __CORE_FILE_PRE_H




================================================
FILE: Code/Sys/File/XBox/hed.cpp
================================================
/*	Header file functionality...
	.Hed files that describe the contents of .Wad files
	Written by Ken, stolen by Matt*/
#include 
#include 
#include 

#include 
#include 

#include  

#include 
#include 

namespace File
{

// Searches the hed file for the filename. If not found, returns NULL.
SHed *FindFileInHed(const char *pFilename, SHed *pHed)
{
	Dbg_MsgAssert(pFilename,("NULL pFilename"));
	Dbg_MsgAssert(pHed,("NULL pHed"));
	
	#define FILENAME_BUF_SIZE 200
	char pBuf[FILENAME_BUF_SIZE];
	Dbg_MsgAssert(strlen(pFilename)Offset!=0xffffffff)
	{						  
		if (stricmp(pBuf,pHd->pFilename)==0)
		{
			return pHd;
		}
		
		// Get the total space occupied by the file name, which is
		// the length +1 (to include the terminator) then rounded up
		// to a multiple of 4.
		int Len=strlen(pHd->pFilename)+1;
		Len=(Len+3)&~3;
		pHd=(SHed*)(pHd->pFilename+Len);
	}
	return NULL;	
}

// Searches the hed file for the filename with the same checksum. If not found, returns NULL.
SHed *FindFileInHedUsingChecksum( uint32 checksum, SHed *pHed, bool stripPath )
{
	Dbg_MsgAssert(pHed,("NULL pHed"));
	
	// Search the hed until the file is found, or not.
	SHed *pHd=pHed;
	const char *pFilename;
	while (pHd->Offset!=0xffffffff)
	{
		pFilename = pHd->pFilename;
		if ( stripPath )
		{
			int i;
			for ( i = strlen( pFilename ) - 2; i >= 0; i-- )
			{
				if ( pFilename[ i ] == '\\' )
				{
					pFilename = &pFilename[ i + 1 ];
					i = -1;
				}
			}
		}
		if ( Script::GenerateCRC( pFilename ) == checksum )
		{
			return ( pHd );
		}
		// Get the total space occupied by the file name, which is
		// the length +1 (to include the terminator) then rounded up
		// to a multiple of 4.
		int Len=strlen(pHd->pFilename)+1;
		Len=(Len+3)&~3;
		pHd=(SHed*)(pHd->pFilename+Len);
	}
	return NULL;	
}



SHed *LoadHed( const char *filename )
{
	char tempFileName[255];
	sprintf( tempFileName, "%s.HED", filename );

	void *fp = File::Open( tempFileName, "rb" );
	if ( !fp )
	{
		Dbg_MsgAssert( 0,( "Couldn't find hed file %s", tempFileName ));
		return NULL;
	}
	int fileSize = File::GetFileSize( fp );
	File::Seek( fp, 0, SEEK_SET );
	
	SHed *pHed = (SHed*)Mem::Malloc(( fileSize + 2047 ) & ~2047 );
	Dbg_MsgAssert( pHed,( "Failed to allocate memory for hed %s", tempFileName ));
	
	File::Read( pHed, 1, fileSize, fp );
	File::Close( fp );
	return pHed;
}

} // namespace File


================================================
FILE: Code/Sys/File/XBox/hed.h
================================================
/*	Header file functionality...
	.Hed files that describe the contents of .Wad files
	Written by Ken, stolen by Matt*/
#ifndef __HED_H__
#define __HED_H__

namespace File
{

struct SHed
{
	// A SECTOR_SIZE aligned offset of a file within skate3.wad
	uint32 Offset;
	
	// The file size, which is the raw file size, not rounded up
	// to a multiple of SECTOR_SIZE
	uint32 FileSize;
	
	// The filename, which is actually bigger than one byte, tee hee.
	const char pFilename[1];
};


SHed *FindFileInHed(const char *pFilename, SHed *pHed );
SHed *FindFileInHedUsingChecksum( uint32 checksum, SHed *pHed, bool stripPath );
SHed *LoadHed( const char *filename );

} // namespace File

#endif


================================================
FILE: Code/Sys/File/XBox/p_AsyncFilesys.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// p_AsyncFilesys.cpp		GRJ 11 Oct 2002
//
// Asynchronous file system
//
///////////////////////////////////////////////////////////////////////////////////////

#include 

#include 

namespace File
{


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static uint32 RoundToNearestSectorSize( uint32 size )
{
	// Round up to the nearest sector size of a DVD, which is 2048 bytes.
	return ( size + 0x7FF ) & ~0x7FF;
}

	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxAsyncFileHandle::CXboxAsyncFileHandle( void )
{
	m_num_open_requests		= 0;
	mp_non_aligned_buffer	= NULL;
	mp_temp_aligned_buffer	= NULL;
	mh_file					= INVALID_HANDLE_VALUE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CXboxAsyncFileHandle::~CXboxAsyncFileHandle()
{
}
	

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxAsyncFileHandle::plat_open( const char *filename )
{
	m_position = 0;

	// The following code duplicates that in prefopen(). At some point the filenme modification code
	// should probably be abstracted out.

	// Used for prepending the root data directory on filesystem calls.
	char		nameConversionBuffer[256] = "d:\\data\\";
	int			index = 8;
	const char*	p_skip;

	if(( filename[0] == 'c' ) && ( filename[1] == ':' ))
	{
		// Filename is of the form c:\skate4\data\foo...
		p_skip = filename + 15;
	}
	else
	{
		// Filename is of the form foo...
		p_skip = filename;
	}

	while( nameConversionBuffer[index] = *p_skip )
	{
		// Switch forward slash directory separators to the supported backslash.
		if( nameConversionBuffer[index] == '/' )
		{
			nameConversionBuffer[index] = '\\';
		}
		++index;
		++p_skip;
	}
	nameConversionBuffer[index] = 0;

	// If this is a .tex file, switch to a .txx file.
	if((( nameConversionBuffer[index - 1] ) == 'x' ) &&
	   (( nameConversionBuffer[index - 2] ) == 'e' ) &&
	   (( nameConversionBuffer[index - 3] ) == 't' ))
	{
		nameConversionBuffer[index - 2] = 'x';
	}

	// If this is a .pre file, switch to a .prx file.
	if((( nameConversionBuffer[index - 1] ) == 'e' ) &&
	   (( nameConversionBuffer[index - 2] ) == 'r' ) &&
	   (( nameConversionBuffer[index - 3] ) == 'p' ))
	{
		nameConversionBuffer[index - 1] = 'x';
	}

	inc_busy_count();

	mh_file = CreateFile(	nameConversionBuffer,							// File name
							GENERIC_READ,									// Access mode
							FILE_SHARE_READ,								// Share mode
							NULL,											// SD
							OPEN_EXISTING,									// How to create
							FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,	// File attributes
							NULL );											// Handle to template file

	if( mh_file == INVALID_HANDLE_VALUE )
	{
        return false;
	}

	// Immediately obtain the file size.
	m_file_size = ::GetFileSize( mh_file, NULL );
	Dbg_Assert( m_file_size != -1 );

	// Round to nearest sector size, since this will be required for async reads anyway.
	m_file_size = RoundToNearestSectorSize( m_file_size );

	io_callback( m_current_function, (int)mh_file, m_file_size );

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxAsyncFileHandle::io_callback( EAsyncFunctionType function, int result, uint32 data )
{
	// Do any special function processing
	switch (function)
	{
		case FUNC_OPEN:
		{
			if( result < 0 )
			{
//				Dbg_MsgAssert( 0, ( "Error: Async open returned %d", result ));
			}
			else
			{
				m_file_size = data;
			}
//			ShowAsyncInfo("io_callback: Open returned handle index %d with filesize %d\n", result, data);
			break;
		}

	case FUNC_SEEK:
		if (result < 0)
		{
			Dbg_MsgAssert(0, ("Error: Async seek returned %d", result));
		} else {
			m_position = result;
		}
		break;

	case FUNC_LOAD:
	case FUNC_READ:
		if (mp_temp_aligned_buffer && (result > 0))
		{
			memcpy(mp_non_aligned_buffer, mp_temp_aligned_buffer, result);
		}
		// don't break, continue on below

	case FUNC_WRITE:
		if (result < 0)
		{
			Dbg_MsgAssert(0, ("Error: Async IO returned %d", result));
		} else {
			m_position += result;
			if (mp_temp_aligned_buffer)
			{
				delete mp_temp_aligned_buffer;
				mp_non_aligned_buffer = NULL;
				mp_temp_aligned_buffer = NULL;
			}
		}
		break;

	case FUNC_IDLE:
		// Don't want m_busy to change or user-defined callback to be executed
		return;

	default:
		break;
	}

	// Check to make sure request buffer will be clear
	if (get_busy_count() > 0)
	{
		//Dbg_MsgAssert(m_num_open_requests == 1, ("Sill other open requests"));
	}

	// Now handle the above p-line stuff
	CAsyncFileHandle::io_callback(function, result, data);
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxAsyncFileHandle::add_request_id( int request_id, EAsyncFunctionType function )
{
	if (m_num_open_requests >= NUM_REQUESTS)
	{
		Dbg_MsgAssert(0, ("Too many open requests for file handle"));
		return false;
	}

	m_open_requests[m_num_open_requests].m_request_id = request_id;
	m_open_requests[m_num_open_requests++].m_function = function;

	//scePrintf("Adding request %d to entry #%d for %x with busy %d\n", request_id, (m_num_open_requests-1), this, get_busy_count());

	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
EAsyncFunctionType	CXboxAsyncFileHandle::get_request_function( int request_id ) const
{
	// Find request id in list
	for (int i = 0; i < m_num_open_requests; i++)
	{
		if (m_open_requests[i].m_request_id == request_id)
		{
			return m_open_requests[i].m_function;
		}
	}

	// Request not found
	Dbg_MsgAssert(0, ("Can't find request %d on %x", request_id, this));
	return FUNC_IDLE;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxAsyncFileHandle::clear_request_id( int request_id )
{
	if (m_num_open_requests <= 0)
	{
		Dbg_MsgAssert(0, ("Can't clear request: Open request list empty"));
		return false;
	}

	// Find request id in list
	for (int i = 0; i < m_num_open_requests; i++)
	{
		if (m_open_requests[i].m_request_id == request_id)
		{
			// Just move end of list to here
			m_open_requests[i] = m_open_requests[--m_num_open_requests];
			//scePrintf("Clearing request %d from entry #%d\n", request_id, i);
			return true;
		}
	}

	// Request not found
	Dbg_MsgAssert(0, ("Can't clear request: Not found"));
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxAsyncFileHandle::plat_init( void )
{
	m_num_open_requests		= 0;
	mp_non_aligned_buffer	= NULL;
	mp_temp_aligned_buffer	= NULL;

	// Set up the overlapped structure.
	m_overlapped.Offset		= 0;
	m_overlapped.OffsetHigh	= 0;
	m_overlapped.hEvent		= NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
volatile bool CXboxAsyncFileHandle::plat_is_done( void )
{
	bool is_done = false;

	if( m_last_result == ERROR_IO_PENDING )
	{
		is_done = HasOverlappedIoCompleted( &m_overlapped );
		if( is_done )
		{
			// I/O operation is complete, so GetOverlappedResult() should retuen true.
			uint32 bt;
			bool result	= GetOverlappedResult(	mh_file,		// handle to file, pipe, or device
												&m_overlapped,	// overlapped structure
												&bt,			// bytes transferred
												false );		// wait option

			Dbg_Assert( result );

			m_last_result = ERROR_SUCCESS;

			// Must call this when we are done.
			io_callback( m_current_function, bt, 0 );	
		}
	}
	return is_done;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
volatile bool CXboxAsyncFileHandle::plat_is_busy( void )
{
	return ( m_last_result == ERROR_IO_PENDING );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxAsyncFileHandle::plat_is_eof( void ) const
{
	return ( m_last_result == ERROR_HANDLE_EOF );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxAsyncFileHandle::plat_set_priority( int priority )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxAsyncFileHandle::plat_set_stream( bool stream )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxAsyncFileHandle::plat_set_destination( EAsyncMemoryType destination )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxAsyncFileHandle::plat_set_buffer_size( size_t buffer_size )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CXboxAsyncFileHandle::plat_set_blocking( bool block )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
size_t CXboxAsyncFileHandle::plat_load( void *p_buffer )
{
	// This function will just get the file size and do one big read.
	// Set up the overlapped structure.
	m_overlapped.Offset		= 0;
	m_overlapped.OffsetHigh	= 0;
	m_overlapped.hEvent		= NULL;

	// And do the read.
	return plat_read( p_buffer, 1, GetFileSize());
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
size_t CXboxAsyncFileHandle::plat_read( void *p_buffer, size_t size, size_t count )
{
	uint32 total_bytes = RoundToNearestSectorSize( size * count );

	inc_busy_count();

	bool result =  ReadFile(	mh_file,							// Handle to file
								p_buffer,							// Data buffer
								total_bytes,						// Number of bytes to read
								NULL,								// Number of bytes read
								&m_overlapped );					// Overlapped buffer

	// If there was a problem, or the async. operation's still pending... 
	if( !result ) 
	{ 
		// Deal with the error code. 
		m_last_result = GetLastError();
		switch( m_last_result ) 
		{ 
			case ERROR_HANDLE_EOF: 
			{ 
	            // We've reached the end of the file during the call to ReadFile.
				break;
			} 
 
			case ERROR_IO_PENDING: 
			{
				break;
			}

			default:
			{
				Dbg_Assert( 0 );
				break;
			}
		}
	}
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
size_t CXboxAsyncFileHandle::plat_write( void *p_buffer, size_t size, size_t count )
{
	Dbg_Assert( 0 );
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
char *CXboxAsyncFileHandle::plat_get_s( char *p_buffer, int maxlen )
{
	Dbg_Assert( 0 );
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int CXboxAsyncFileHandle::plat_seek( long offset, int origin )
{
	Dbg_Assert( 0 );
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CXboxAsyncFileHandle::plat_close( void )
{
	if( mh_file != INVALID_HANDLE_VALUE )
	{
		inc_busy_count();

		CloseHandle( mh_file );
		mh_file = INVALID_HANDLE_VALUE;

		io_callback( m_current_function, 0, 0 );
		return true;
	}
	return false;
}

	
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CAsyncFileLoader::s_plat_init( void )
{
	for( int i = 0; i < MAX_FILE_HANDLES; ++i )
	{
		if( s_file_handles[i] )
		{
			s_file_handles[i]->init();
		}
		else
		{
			s_file_handles[i] = new CXboxAsyncFileHandle;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CAsyncFileLoader::s_plat_cleanup( void )
{
	for( int i = 0; i < MAX_FILE_HANDLES; ++i )
	{
		if( s_file_handles[i] )
		{
			delete s_file_handles[i];
			s_file_handles[i] = NULL;
		}
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CAsyncFileLoader::s_plat_async_supported( void )
{
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool CAsyncFileLoader::s_plat_exist( const char *filename )
{
	HANDLE h_file = CreateFile( filename,							// File name
					0,												// Access mode
					FILE_SHARE_READ,								// Share mode
					NULL,											// SD
					OPEN_EXISTING,									// How to create
					FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,	// File attributes
					NULL );											// Handle to template file

	if( h_file == INVALID_HANDLE_VALUE )
	{
        return false;
	}

	CloseHandle( h_file );
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
CAsyncFileHandle *CAsyncFileLoader::s_plat_open( const char *filename, int priority )
{
	CXboxAsyncFileHandle *p_handle = new CXboxAsyncFileHandle();
	p_handle->plat_init();

	bool opened = p_handle->plat_open( filename );
	if( !opened )
	{
		delete p_handle;
		return NULL;
	}

	return p_handle;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CAsyncFileLoader::s_plat_swap_callback_list( void )
{
	s_cur_callback_list_index	= s_cur_callback_list_index ^ 1;	// Swap indices...
	s_new_io_completion			= false;							// ...and clear flag.
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void CAsyncFileLoader::s_plat_update( void )
{
	for( int h = 0; h < MAX_FILE_HANDLES; ++h )
	{
		if( s_file_handles[h] )
		{
			if( s_file_handles[h]->IsBusy())
			{
				CXboxAsyncFileHandle*	p_xbox_handle = static_cast( s_file_handles[h] );
				p_xbox_handle->plat_is_done();
			}
		}
	}
}


}





================================================
FILE: Code/Sys/File/XBox/p_AsyncFilesys.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			File													**
**																			**
**	Created:		10/11/02	-	grj										**
**																			**
**	File name:		core/sys/p_AsyncFilesys.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_FILE_P_ASYNC_FILESYS_H
#define	__SYS_FILE_P_ASYNC_FILESYS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace File
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

class CXboxAsyncFileHandle : public CAsyncFileHandle
{
public:

protected:

							CXboxAsyncFileHandle();
	virtual 				~CXboxAsyncFileHandle();

	static VOID CALLBACK	sAsyncFileReadTimerAPCProc( LPVOID lpArgToCompletionRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue );

private:
	// Constants
	enum
	{
		NUM_REQUESTS = 8
	};

	// Request data
	struct SRequest
	{
		int					m_request_id;
		EAsyncFunctionType	m_function;
	};

	// Callback functions
	virtual void		io_callback(EAsyncFunctionType function, int result, uint32 data);

	// Open requests
	SRequest		  	m_open_requests[NUM_REQUESTS];
	volatile int		m_num_open_requests;

	// Non-aligned buffer IO
	uint8 *				mp_non_aligned_buffer;
	uint8 *				mp_temp_aligned_buffer;

	// Xbox-specific file handle.
	HANDLE				mh_file;
	OVERLAPPED			m_overlapped;	// OVERLAPPED structure required for async reading on Xbox

	bool				add_request_id( int request_id, EAsyncFunctionType function );
	EAsyncFunctionType	get_request_function( int request_id ) const;
	bool				clear_request_id( int request_id );

	// platform-specific calls
	virtual void			plat_init( void );

	virtual bool			plat_open( const char *filename );
	virtual bool			plat_close( void );

	virtual volatile bool	plat_is_done( void );
	virtual volatile bool	plat_is_busy( void );
	virtual bool			plat_is_eof( void ) const;

	virtual void			plat_set_priority( int priority );
	virtual void			plat_set_stream( bool stream );
	virtual void			plat_set_destination( EAsyncMemoryType destination );
	virtual void			plat_set_buffer_size( size_t buffer_size );
	virtual void			plat_set_blocking( bool block );

	virtual size_t			plat_load( void *p_buffer );
	virtual size_t			plat_read( void *p_buffer, size_t size, size_t count );
	virtual size_t			plat_write( void *p_buffer, size_t size, size_t count );
	virtual char *			plat_get_s( char *p_buffer, int maxlen );
	virtual int				plat_seek( long offset, int origin );

	// Friends
	friend CAsyncFileLoader;
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace File

#endif  // __SYS_FILE_P_ASYNC_FILESYS_H


================================================
FILE: Code/Sys/File/XBox/p_filesys.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		System Library											**
**																			**
**	Module:			File IO (File) 											**
**																			**
**	File name:		p_filesys.cpp											**
**																			**
**	Created by:		09/25/00	-	dc										**
**																			**
**	Description:	XBox File System										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/

namespace File
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define READBUFFERSIZE				10240
#define	PREPEND_START_POS			8

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

BOOL OkayToUseUtilityDrive = FALSE;

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/* prefopen														  */
/* Same as fopen() except it will prepend the data root directory */
/* For Xbox, path is always relative to location of .xbe image	  */
/* and default path from that location is d:\					  */
/* So incoming files of the form:								  */
/* c:\skate3\data\foo...	become	d:\data\foo...				  */
/* foo...					become	d:\data\foo...				  */
/*                                                                */
/******************************************************************/
static void* prefopen( const char *filename, const char *mode )
{
	// Used for prepending the root data directory on filesystem calls.
	char		nameConversionBuffer[256] = "d:\\data\\";
	int			index = PREPEND_START_POS;
	const char*	p_skip;

	if(( filename[0] == 'c' ) && ( filename[1] == ':' ))
	{
		// Filename is of the form c:\skate4\data\foo...
		p_skip = filename + 15;
	}
	else
	{
		// Filename is of the form foo...
		p_skip = filename;
	}

	while( nameConversionBuffer[index] = *p_skip )
	{
		// Switch forward slash directory separators to the supported backslash.
		if( nameConversionBuffer[index] == '/' )
		{
			nameConversionBuffer[index] = '\\';
		}
		++index;
		++p_skip;
	}
	nameConversionBuffer[index] = 0;

	// If this is a .tex file, switch to a .txx file.
	if((( nameConversionBuffer[index - 1] ) == 'x' ) &&
	   (( nameConversionBuffer[index - 2] ) == 'e' ) &&
	   (( nameConversionBuffer[index - 3] ) == 't' ))
	{
		nameConversionBuffer[index - 2] = 'x';
	}

	// If this is a .pre file, switch to a .prx file.
	if(((( nameConversionBuffer[index - 1] ) == 'e' ) || (( nameConversionBuffer[index - 1] ) == 'x' )) &&
	    (( nameConversionBuffer[index - 2] ) == 'r' ) &&
	    (( nameConversionBuffer[index - 3] ) == 'p' ))
	{
#		ifdef __PAL_BUILD__
		// Switch to a .prf, .prg or .prx file, depending on language.
		switch( Config::GetLanguage())
		{
			case Config::LANGUAGE_FRENCH:
			{
				nameConversionBuffer[index - 1] = 'f';
				break;
			}
			case Config::LANGUAGE_GERMAN:
			{
				nameConversionBuffer[index - 1] = 'g';
				break;
			}
			default:
			{
				nameConversionBuffer[index - 1] = 'x';
				break;
			}
		}
#		else
		nameConversionBuffer[index - 1] = 'x';
#		endif // __PLAT_BUILD__
	}

	// First we try reading the file from the utility partition (z:\) on the HD, rather than the DVD.
	HANDLE h_file = INVALID_HANDLE_VALUE;
	if( OkayToUseUtilityDrive )
	{
		nameConversionBuffer[0] = 'Z';
		h_file = CreateFile( nameConversionBuffer, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	}

	if( h_file == INVALID_HANDLE_VALUE )
	{
		// Not on the utility partition, so load it from the DVD.
		nameConversionBuffer[0] = 'D';
		h_file = CreateFile( nameConversionBuffer, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

		// Deal with various error returns.
		if( h_file == INVALID_HANDLE_VALUE )
		{
			DWORD error = GetLastError();

			// Need to exclude this error from the test, since screenshot and other code sometimes check to see if a file exists, and it
			// is valid to just return the error code if it doesn't.
			if( error != ERROR_FILE_NOT_FOUND )
			{
				// Catch-all error indicating a fatal problem. Can't continue at this point.
				// The ideal solution would be a catch/throw exception mechanism, but we don't include exception handling at the moment.
				// For now just call this NxXbox function, which is slightly messy since it means we have to include a gfx\ file.
				printf( "FatalFileError: %x %s\n", error, nameConversionBuffer );
				NxXbox::FatalFileError((uint32)INVALID_HANDLE_VALUE );
			}
			return NULL;
		}
		else
		{
			// All is well.
			return h_file;
		}
	}
	else
	{
		return h_file;
	}
}


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

static CThreadedLevelLoader *pLoader;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void InstallFileSystem( void )
{
#	if 0
	// This is where we start the thread that will deal with copying commonly used data from the DVD to the utility
	// region (z:\) on the HD.
	pLoader = new CThreadedLevelLoader();

	SLevelDesc level_descs[] = {{ "pre\\alc.prx" },
								{ "pre\\alccol.prx" },
								{ "pre\\alcscn.prx" },
								{ "pre\\anims.prx" },
								{ "pre\\bits.prx" },
								{ "pre\\cnv.prx" },
								{ "pre\\cnvcol.prx" },
								{ "pre\\cnvscn.prx" },
								{ "pre\\fonts.prx" },
								{ "pre\\jnk.prx" },
								{ "pre\\jnkcol.prx" },
								{ "pre\\jnkscn.prx" },
								{ "pre\\kon.prx" },
								{ "pre\\koncol.prx" },
								{ "pre\\konscn.prx" },
								{ "pre\\lon.prx" },
								{ "pre\\loncol.prx" },
								{ "pre\\lonscn.prx" },
								{ "pre\\qb.prx" },
								{ "pre\\sch.prx" },
								{ "pre\\schcol.prx" },
								{ "pre\\schscn.prx" },
								{ "pre\\sf2.prx" },
								{ "pre\\sf2col.prx" },
								{ "pre\\sf2scn.prx" },
								{ "pre\\skaterparts.prx" },
								{ "pre\\skater_sounds.prx" },
								{ "pre\\skateshop.prx" },
								{ "pre\\skateshopcol.prx" },
								{ "pre\\skateshopscn.prx" },
								{ "pre\\skeletons.prx" },
								{ "pre\\zoo.prx" },
								{ "pre\\zoocol.prx" },
								{ "pre\\zooscn.prx" }};

	static BYTE data_buffer[32 * 1024];
	pLoader->Initialize( level_descs, 34, data_buffer, 32 * 1024, &OkayToUseUtilityDrive );
	pLoader->AsyncStreamLevel( 0 );
#	endif

	// Initialise the async file system.
	File::CAsyncFileLoader::sInit();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
long GetFileSize( void* pFP )
{
	Dbg_MsgAssert( pFP, ( "NULL pFP sent to GetFileSize" ));

	if( PreMgr::sPreEnabled())
    {
		int retval = PreMgr::pre_get_file_size( (PreFile::FileHandle *) pFP );
		if( PreMgr::sPreExecuteSuccess())
			return retval;
	}
	
	LARGE_INTEGER	li;
	GetFileSizeEx((HANDLE)pFP, &li );
	return (long)li.LowPart;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
long GetFilePosition( void *pFP )
{
	Dbg_MsgAssert( pFP, ( "NULL pFP sent to GetFilePosition" ));

	if( PreMgr::sPreEnabled())
	{
		int retval = PreMgr::pre_get_file_position((PreFile::FileHandle*)pFP );
		if( PreMgr::sPreExecuteSuccess())
			return retval;
	}

	long pos = SetFilePointer(	(HANDLE)pFP,	// Handle to file
								0,				// Bytes to move pointer
								0,				// High bytes to move pointer
								FILE_CURRENT );	// Starting point
	return pos;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void InitQuickFileSystem( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint32	CanFileBeLoadedQuickly( const char* filename )
{
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool LoadFileQuicklyPlease( const char* filename, uint8 *addr )
{
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void StopStreaming( void )
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void UninstallFileSystem( void )
{
}



////////////////////////////////////////////////////////////////////
// Our versions of the ANSI file IO functions. They call
// the PreMgr first to see if the file is in a PRE file.
////////////////////////////////////////////////////////////////////

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Exist( const char *filename )
{
    if (PreMgr::sPreEnabled())
    {
        bool retval = PreMgr::pre_fexist(filename);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	void *p_result = prefopen( filename, "rb" );
	if( p_result != NULL )
	{
		Close( p_result );
	}

	return( p_result != NULL );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void * Open( const char *filename, const char *access )
{
    if (PreMgr::sPreEnabled())
    {
        void * retval = PreMgr::pre_fopen(filename, access);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	return prefopen( filename, access );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int Close( void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_fclose((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	CloseHandle((HANDLE)pFP );

	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
size_t Read( void *addr, size_t size, size_t count, void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        size_t retval = PreMgr::pre_fread( addr, size, count, (PreFile::FileHandle*)pFP );
		if( PreMgr::sPreExecuteSuccess())
            return retval;
    }

	DWORD bytes_read;
	if( ReadFile((HANDLE)pFP, addr, size * count, &bytes_read, NULL ))
	{
		// All is well.
		return bytes_read;
	}
	else
	{
		// Read error.
		DWORD last_error = GetLastError();

		if( last_error == ERROR_HANDLE_EOF )
		{
			// Continue in this case.
			return bytes_read;
		}

		NxXbox::FatalFileError( last_error );
		return bytes_read;
	}
}



/******************************************************************/
/*                                                                */
/* Read an Integer in little endian format. Just read it directly */
/* into memory...												  */
/*                                                                */
/******************************************************************/
size_t ReadInt( void *addr, void *pFP )
{
	return Read( addr, 4, 1, pFP );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
size_t Write( const void *addr, size_t size, size_t count, void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        size_t retval = PreMgr::pre_fwrite(addr, size, count, (PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
char * GetS( char *buffer, int maxlen, void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        char * retval = PreMgr::pre_fgets(buffer, maxlen, (PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int PutS( const char *buffer, void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_fputs(buffer, (PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int Eof( void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_feof((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int Seek( void *pFP, long offset, int origin )
{
	if( PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_fseek((PreFile::FileHandle *) pFP, offset, origin);
		if( PreMgr::sPreExecuteSuccess())
			return retval;
	}
	return SetFilePointer((HANDLE)pFP, offset, NULL, ( origin == SEEK_CUR ) ? FILE_CURRENT : (( origin == SEEK_SET ) ? FILE_BEGIN : FILE_END ));
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int Flush( void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_fflush((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace File




================================================
FILE: Code/Sys/File/XBox/p_pre.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate3													**
**																			**
**	Module:			Xbox specific PRE module 								**
**																			**
**	File name:		p_pre.cpp												**
**																			**
**	Created by:		dc														**
**																			**
**	Description:						 									**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 
#include 

// script stuff
#include  
#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

#define DEBUG_PRE 0

namespace File
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define CURRENT_PRE_VERSION	0xabcd0002			// as of 3/14/2001
//#define CURRENT_PRE_VERSION	0xabcd0001		// until 3/14/2001

#define RINGBUFFERSIZE		 4096	/* N size of ring buffer */	
#define MATCHLIMIT		   18	/* F upper limit for match_length */
#define THRESHOLD	2   /* encode string into position and length */

#define WriteOut(x) 	{*pOut++ = x;}

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

PreMgr *PreMgr::sp_mgr = NULL;
bool    PreMgr::s_lastExecuteSuccess = false; 

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/



#define USE_BUFFER	1		 // we don't need no stinking buffer!!!!
 
#if USE_BUFFER
unsigned char	text_buf[RINGBUFFERSIZE + MATCHLIMIT - 1];	
#endif


#define	ReadInto(x)		if (!Len) break; Len--; x = *pIn++ 
#define	ReadInto2(x)	Len--; x = *pIn++ 	  // version that knows Len is Ok


// Decode an LZSS encoded stream
// Runs at approx 12MB/s on PS2	 without scratchpad (which slows it down in C)
// a 32x CD would run at 4.8MB/sec, although we seem to get a lot less than this
// with our current file system, more like 600K per seconds.....
// Need to write a fast streaming file system....

void DecodeLZSS( unsigned char *pIn, unsigned char *pOut, int Len )	/* Just the reverse of Encode(). */
{
	int  i, j, k, r, c;
//	uint64	LongWord;
//	int bytes = 0;
//	unsigned char *pScratch;
	unsigned int  flags;



//	int basetime =  (int) Tmr::ElapsedTime(0);
//	int len = Len;

//	int	OutBytes = 4;
//	int	OutWord = 0;

#	if USE_BUFFER
	for (i = 0; i < RINGBUFFERSIZE - MATCHLIMIT; i++)
		 text_buf[i] = ' ';
	r = RINGBUFFERSIZE - MATCHLIMIT;
#	else
	r = RINGBUFFERSIZE - MATCHLIMIT;
#	endif
	flags = 0;
	for ( ; ; )
	{
		if (((flags >>= 1) & 256) == 0)
		{
			ReadInto(c);
			flags = c | 0xff00;			/* uses higher byte cleverly */
		}										/* to count eight */
		if (flags & 1)
		{
			ReadInto(c);
			//			putc(c, outfile);
			WriteOut(c);
			#if USE_BUFFER
			text_buf[r++] = c;
			r &= (RINGBUFFERSIZE - 1);
			#else
			r++;
//			r &= (RINGBUFFERSIZE - 1);	  // don't need to wrap r until it is used
			#endif
		}
		else
		{
			ReadInto(i);			
			ReadInto2(j);			// note, don't need to check len on this one.... 
			
			i |= ((j & 0xf0) << 4);						// i is 12 bit offset
			
			#if !USE_BUFFER
			j = (j & 0x0f) + THRESHOLD+1;				// j is 4 bit length (above the threshold)
			unsigned char *pStream;
			r &= (RINGBUFFERSIZE - 1);					// wrap r around before it is used
			pStream = pOut - r;					  		// get base of block
			if (i>=r)										// if offset > r, then
				pStream -= RINGBUFFERSIZE;				// it's the previous block
			pStream += i;									// add in the offset to the base
			r+=j;												// add size to r
			while (j--)										// copy j bytes
				WriteOut(*pStream++);
			#else
			
			j = (j & 0x0f) + THRESHOLD;				// j is 4 bit length (above the threshold)
			for (k = 0; k <= j; k++)					// just copy the bytes
			{
				c =  text_buf[(i+k) & (RINGBUFFERSIZE - 1)]; 
				WriteOut(c);
				text_buf[r++] = c;
				r &= (RINGBUFFERSIZE - 1);
			}
			#endif
		}
	}
//	int Time = (int) Tmr::ElapsedTime(basetime);
//	if (Time > 5)
//	{
//		printf("decomp time is %d ms, for %d bytes,  %d bytes/second\n", Time,len, len * 1000 /Time );
//	}

}




void PreFile::s_delete_file(_File *pFile, void *pData)
{
	Dbg_Assert(pFile);
	delete pFile;
}




PreFile::PreFile(uint8 *p_file_buffer)
{
	mp_table = new Lst::StringHashTable<_File>(4);	

	Dbg_AssertPtr(p_file_buffer);

	mp_buffer = p_file_buffer;
	uint version = *((int *) (mp_buffer + 4));
	Dbg_MsgAssert(version == CURRENT_PRE_VERSION,( "PRE file version not current"));
	m_numEntries = *((int *)(mp_buffer + 8));

	uint8 *pEntry = mp_buffer + 12;
	for (int i = 0; i < m_numEntries; i++)
	{
		int data_size 				= *((int *) pEntry);
		int compressed_data_size 	= *((int *) (pEntry + 4));
		int text_size 				= *((int *) (pEntry + 8));
		int actual_data_size = (compressed_data_size != 0) ? compressed_data_size : data_size;
			
		char *pName = (char *) pEntry + 12;
		uint8 *pCompressedData = pEntry + 12 + text_size;
		
		_File *pFile = new (Mem::Manager::sHandle().TopDownHeap()) _File;

		if (!mp_table->GetItem(pName)) 
		{
			// file is not in table, safe to add
			mp_table->PutItem(pName, pFile);

		pFile->compressedDataSize = compressed_data_size;
		pFile->pCompressedData = pCompressedData; 
		pFile->pData = NULL;
		pFile->sky.POS = 0;
		pFile->sky.SOF = data_size;
		}
		else
			// Somehow, file is already in table, just kill it
			// Later, I'll want to add an assert
			delete pFile;
		
#		if DEBUG_PRE
		printf("   %s, size %d\n", pName, data_size);
#		endif
		
		pEntry += 12 + text_size + ((actual_data_size + 3) & (~3));
	}

#	if DEBUG_PRE
	printf("Done loading PRE\n");
#	endif
	
	mp_activeFile = NULL;
}



PreFile::~PreFile()
{
	delete mp_buffer;
	mp_table->HandleCallback(s_delete_file, NULL);
	mp_table->FlushAllItems();

	delete mp_table;
}



bool PreFile::FileExists(const char *pName)
{
	
	_File *pFile = mp_table->GetItem(pName, false);
	return (pFile != NULL);
}



// returns handle pointer
void *PreFile::GetContainedFile(const char *pName)
{

	_File *pFile = mp_table->GetItem(pName, false);
	if (!pFile) 
		return NULL;
	
	dumbSkyFile *pHandle = &pFile->sky;
	// kinda roundabout, but sets mp_activeFile
	GetContainedFileByHandle(pHandle);
	
	// do we need to fetch file data?
	if (!mp_activeFile->pData)
	{
		if (mp_activeFile->compressedDataSize)
		{
		mp_activeFile->pData = new (Mem::Manager::sHandle().TopDownHeap()) uint8[mp_activeFile->sky.SOF];		
			// need to uncompress data
			DecodeLZSS(mp_activeFile->pCompressedData, mp_activeFile->pData, mp_activeFile->compressedDataSize);	
		}
	}

	return pHandle;
}
	


uint8 *PreFile::GetContainedFileByHandle(void *pHandle)
{
	mp_table->IterateStart();
	_File *pFile = mp_table->IterateNext();
	while(pFile)
	{
		uint8 *pCompressedData = pFile->pCompressedData;
		if (pCompressedData && &pFile->sky == pHandle)
		{
			mp_activeFile = pFile;
			
			if (mp_activeFile->compressedDataSize)
			return mp_activeFile->pData;
			else
				return mp_activeFile->pCompressedData;
		}
		pFile = mp_table->IterateNext();
	}

	return NULL;
}



// allocate memory and load file directly from a pre file, if it is there
// This avoids the problem of having to have the decompressed file in memory twice
// when we are loading directly, like with Pip::Load()
// using this enables us to actually load network game, where there is 1MB less heap during loading
//
// returns a pointer to the file in memory
// or NULL if the file is not in this pre file.
void *PreFile::LoadContainedFile( const char *pName, int *p_size, void *p_dest )
{
	// The dest parameter is used only on Ps2.
	Dbg_Assert( p_dest == NULL );
	
	_File *pFile = mp_table->GetItem( pName, false );
	if( !pFile ) 
	{
		return NULL;
	}	
	
	*p_size = pFile->sky.SOF;
	
	p_dest = Mem::Malloc( pFile->sky.SOF );
	
	// Do we need to decompress file data?
	if( !pFile->pData )
	{
		if( pFile->compressedDataSize )
		{
			// Need to uncompress data.
			DecodeLZSS( pFile->pCompressedData, (uint8*)p_dest, pFile->compressedDataSize );	
		}
		else
		{
			memcpy( p_dest, (void*)pFile->pCompressedData, pFile->sky.SOF );
		}
	}
	else
	{
		memcpy( p_dest, (void*)pFile->pData, pFile->sky.SOF );
	}
	return p_dest;
}



void PreFile::Reset()
{
	
	Dbg_AssertPtr(mp_activeFile);

	mp_activeFile->sky.POS = 0;
}



uint32 PreFile::Read( void *addr, uint32 count )
{
	
	Dbg_AssertPtr( mp_activeFile );

	int seek_offs = mp_activeFile->sky.POS;
	unsigned int limit = mp_activeFile->sky.SOF - seek_offs;
	int copy_number = (count <= limit) ? count : limit; 
	if (mp_activeFile->compressedDataSize)
	{
		Dbg_MsgAssert(mp_activeFile->pData,( "file not uncompressed"));
	memcpy( addr, mp_activeFile->pData + mp_activeFile->sky.POS, copy_number );
	}
	else
	{
		memcpy(addr, mp_activeFile->pCompressedData + mp_activeFile->sky.POS, copy_number);
	}
	
	mp_activeFile->sky.POS += copy_number;

#	if DEBUG_PRE
	printf("PRE: read %d bytes from file, handle 0x%x\n", copy_number, (int) mp_activeFile->pData);
#	endif
	return copy_number;
}



int PreFile::Eof()
{
	
	Dbg_AssertPtr(mp_activeFile);

	if (mp_activeFile->sky.POS >= mp_activeFile->sky.SOF)
	{
#if DEBUG_PRE
		printf("PRE: at end of file\n");
#endif
		return 1;
	}

#if DEBUG_PRE
	printf("PRE: not at end of file\n");
#endif
	return 0;
}



void PreFile::Close()
{

	//Dbg_MsgAssert(mp_activeFile->pData,( "file not uncompressed"));

	if (mp_activeFile->pData)
	delete mp_activeFile->pData;
	mp_activeFile->pData = NULL;
}



int PreFile::Seek(long offset, int origin)
{
	int32 old_pos = mp_activeFile->sky.POS;

	// SEEK_CUR, SEEK_END, SEEK_SET
	switch(origin)
	{
		case SEEK_CUR:
			mp_activeFile->sky.POS += offset;
			break;
		case SEEK_END:
			mp_activeFile->sky.POS = mp_activeFile->sky.SOF - offset;
			break;
		case SEEK_SET:
			mp_activeFile->sky.POS = offset;
			break;
		default:
			return -1;
	}

	if (mp_activeFile->sky.POS < 0 || mp_activeFile->sky.POS > mp_activeFile->sky.SOF)
	{
		mp_activeFile->sky.POS = old_pos;
		return -1;
	}

	return 0;
}



PreMgr::PreMgr() 
{
	mp_table = new Lst::StringHashTable(4);

	sp_mgr = this;


	mp_activeHandle = NULL;
	mp_activeData = NULL;

	mp_activeNonPreHandle = NULL;
}



PreMgr::~PreMgr()
{
	delete mp_table;
}



// Returns handle
// Not frequently called
void *PreMgr::getContainedFile(const char *pName)
{
	
	Dbg_AssertPtr(pName);

	// replace all '/' with '\'
	char cleaned_name[128];
	const char *pCharIn = pName;
	char *pCharOut = cleaned_name;
	while (1)
	{
		*pCharOut = *pCharIn;
		if (*pCharIn == '\0') break;
		if (*pCharOut == '/') *pCharOut = '\\';
		pCharIn++;
		pCharOut++;		
	}

	void *pHandle = NULL;

	mp_table->IterateStart();
	PreFile *pPre = mp_table->IterateNext();
	while(pPre)
	{
			pHandle = pPre->GetContainedFile(cleaned_name);
		if (pHandle) 
		{
			mp_activePre = pPre;
			mp_activeHandle = pHandle;
			mp_activeData = pPre->GetContainedFileByHandle(pHandle);
#			ifdef __PLAT_NGPS__
//			scePrintf("+++ %s is in PRE\n", cleaned_name);
#			endif
			return pHandle;
		}
		pPre = mp_table->IterateNext();
	}

#	ifdef __PLAT_NGPS__
//	scePrintf("--- %s not found in PRE\n", cleaned_name);
#	endif
	return NULL;
}



// returns true if the file exists in any of the pre files
bool	PreMgr::fileExists(const char *pName)
{
	Dbg_AssertPtr(pName);
	// replace all '/' with '\'
	char cleaned_name[128];
	const char *pCharIn = pName;
	char *pCharOut = cleaned_name;
	while (1)
	{
		*pCharOut = *pCharIn;
		if (*pCharIn == '\0') break;
		if (*pCharOut == '/') *pCharOut = '\\';
		pCharIn++;
		pCharOut++;		
	}

	mp_table->IterateStart();
	PreFile *pPre = mp_table->IterateNext();
	while(pPre)
	{
		if (pPre->FileExists(cleaned_name))
		{
			return true;
		}
		pPre = mp_table->IterateNext();
	}
	return false;
}



// returns pointer to data
uint8 *PreMgr::getContainedFileByHandle(void *pHandle)
{
	
	Dbg_AssertPtr(pHandle);

	// if we know that the file in question is not in the PRE system,
	// then it's a regular file, don't waste time looking for it
	if (mp_activeNonPreHandle == pHandle)
		return NULL;
	
	if (mp_activeHandle == pHandle)
		// mp_activePre will be unchanged
		return mp_activeData;
	
	uint8 *pData = NULL;
	mp_table->IterateStart();
	PreFile *pPre = mp_table->IterateNext();
	while(pPre)
	{
			pData = pPre->GetContainedFileByHandle(pHandle);
		if (pData)
		{
			mp_activePre = pPre;
			mp_activeHandle = pHandle;
			mp_activeData = pData;			
			return pData;
		}
		pPre = mp_table->IterateNext();
	}

	// obviously this file is not in the PRE system, mark as such
	mp_activeNonPreHandle = pHandle;
	return NULL;
}

// there's a wrapper around this now, so that we can do
// some memory-context switching
void PreMgr::loadPre(const char *pFilename, bool dont_assert)
{
	m_blockPreLoading = (bool) Script::GetInt("block_pre_loading", false);

	if (m_blockPreLoading)
		return;

	if( Pcm::UsingCD() )
	{
		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
		return;
	}

	// Moved this to below the Pcm::UsingCD() call as that is used (bad!!) to turn off
	// music and streams.
#	ifdef __PLAT_NGC__
	OSReport ( "Warning: Attempting to load pre file: %s (ignored)\n", pFilename );
	return;
#	endif // __PLAT_NGC__

#	ifdef __PLAT_NGPS__
//	scePrintf("Loading PRE file %s...\n", pFilename);
#	endif

	char fullname[256];
	sprintf(fullname, "pre\\%s", pFilename);

	// Replace the .pre extension with .prx for Xbox PRE file.
	fullname[strlen( fullname ) - 1] = 'x';

	// Create a lower-case version.
	char lower[128];
	for( unsigned int lp = 0; lp < strlen( fullname ) + 1; lp++ ) lower[lp] = tolower( fullname[lp] );

	Tmr::Time basetime = Tmr::ElapsedTime(0);

	int file_size;
	uint8 *pFile = NULL;

	file_size = CanFileBeLoadedQuickly( fullname );
	if ( file_size )
	{
		pFile = new (Mem::Manager::sHandle().TopDownHeap()) uint8[file_size];
		bool fileLoaded= LoadFileQuicklyPlease( fullname, pFile );
		if ( !fileLoaded )
		{
			printf( "pre file %s failed to load quickly.\n", fullname );
			Dbg_MsgAssert( 0,( "Fire Matt - pre file didn't load quickly." ));
		}
	}
	else
	{
#ifdef __PLAT_NGC__
		NsFile file;
		if ( file.exist( fullname ) )
		{
			file.open( fullname );
#else
			void *fp = File::Open(fullname, "rb");
			if (!fp)
			{
#endif		// __PLAT_NGC__
			// always run the code below if CD build
				if (dont_assert || Config::CD()) 
				{
					printf("couldn't open %s\n", fullname);
					return;
				}
				Dbg_MsgAssert(0,( "couldn't open %s\n", fullname));
			}


#ifdef __PLAT_NGC__
			file.read( &file_size, 4 );
			file_size = nLtEn32(file_size);
#else
			File::Read(&file_size, 4, 1, fp);
			Dbg_MsgAssert(file_size > 0,( "%s has incorrect file size\n", fullname));
#endif		// __PLAT_NGC__
			if (Config::CD())
			{
				if (file_size <= 0) printf("%s has incorrect file size\n", fullname);
			}	


		// Now allocates the .PRE file from the top of the heap, to avoid fragmentation.
			pFile = new (Mem::Manager::sHandle().TopDownHeap()) uint8[file_size];
		//uint8 *pFile = new uint8[file_size];
#ifdef __PLAT_NGC__
			file.read( pFile + 4, file_size );
			file.close();
#else
			File::Read(pFile + 4, 1, file_size, fp);
			File::Close(fp);
#endif		// __PLAT_NGC__
		}

		printf("load time for file size %d is %d ms\n", file_size, (int) Tmr::ElapsedTime(basetime));

	// the PRE file object winds up at the top of the heap, too. This is fine because
	// it will be unloaded at the same time as the big file buffer
		if (!mp_table->PutItem(pFilename, new (Mem::Manager::sHandle().TopDownHeap()) PreFile(pFile)))
			Dbg_MsgAssert(0,( "PRE %s loaded twice", pFilename));
}



/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/


DefineSingletonClass(PreMgr, "PRE Manager");



bool PreMgr::InPre(const char *pFilename)
{


	return mp_table->GetItem( pFilename );
}



void PreMgr::LoadPre( const char *pFilename, bool dont_assert )
{
	// GJ:  This function is a wrapper around loadPRE(), to
	// make sure that all allocations go on the top-down heap
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	loadPre(pFilename, dont_assert);
	Mem::Manager::sHandle().PopContext();	
}



void PreMgr::LoadPrePermanently(const char *pFilename, bool dont_assert)
{
	// GJ:  This function is a wrapper around LoadPRE(),
	// used for loading PRE files which will reside in memory
	// permanently.  Essentially, we need to make sure that the
	// data is above Renderware's resource arena.  If you didn't
	// use this function, you'd get fragmentation when the resource
	// arena resized itself.

	// Mick: Removed reference to renderware, the old "arena" concept is gone

	
	// Load the pre file...
	// This will go on the top-down heap by default
	LoadPre(pFilename, dont_assert);

}



void PreMgr::UnloadPre(const char *pFilename, bool dont_assert)
{

//	scePrintf("Unloading PRE file %s\n", pFilename);
	
	if (m_blockPreLoading)
		return;
	
	PreFile *pThePre = mp_table->GetItem(pFilename);
	if (!pThePre)
	{
		if (dont_assert) return;
		Dbg_MsgAssert(0,( "PRE file %s not in PRE manager", pFilename));
	}

	mp_table->FlushItem(pFilename);
	delete pThePre;
}


bool PreMgr::sPreEnabled()
{
	return sp_mgr != NULL;
}

bool PreMgr::sPreExecuteSuccess()
{
	return s_lastExecuteSuccess; 
}

#	ifndef __PLAT_NGC__
bool PreMgr::pre_fexist(const char *name)
{
	Dbg_MsgAssert(name,( "requesting file NULL"));	
	
	if( sp_mgr->fileExists( name ))
	{
#		if DEBUG_PRE
		printf("PRE: file %s exists\n", name);
#		endif
		s_lastExecuteSuccess = true;
		return true;
	}
	if ( Pcm::UsingCD( ) )
	{
		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
		return false;
	}

	return s_lastExecuteSuccess = false;
}



void *PreMgr::pre_fopen(const char *name, const char *access)
{
	Dbg_MsgAssert(name,( "trying to open file NULL" ));	

	char nameConversionBuffer[256];

	// Just as we do in the Xbox version of prefopen(), if this is a .tex file, switch to a .txx file.
	int length = strlen( name );
	strncpy( nameConversionBuffer, name, length + 1 );
	if((( nameConversionBuffer[length - 1] ) == 'x' ) &&
	   (( nameConversionBuffer[length - 2] ) == 'e' ) &&
	   (( nameConversionBuffer[length - 3] ) == 't' ))
	{
		nameConversionBuffer[length - 2] = 'x';
	}

	void *pHandle = sp_mgr->getContainedFile( nameConversionBuffer );
	if( pHandle )
	{
		// if we are going to write the file, we want to use the regular file system
		const char *pChar = access;
		bool am_writing = false;
		while(*pChar)
		{
			if (*pChar != 'r' && *pChar != 'b')
				am_writing = true;
			pChar++;
		}
		
		if (am_writing)
		{
#			ifdef __PLAT_NGPS__
//			scePrintf("    writing file %s\n", name);
#			endif

			// am writing, so we don't need file open in PRE system
			sp_mgr->mp_activePre->Close();
		}
		else
		{
			// we're reading the file from the PRE system
#			if DEBUG_PRE
			printf("PRE: opened file %s, handle is 0x%x\n", name, (int) pHandle);
#			endif
			sp_mgr->mp_activePre->Reset();
			s_lastExecuteSuccess = true;
			return pHandle;
		}
	}

	// if we get here, we are using the regular file system
	if( Pcm::UsingCD())
	{
		Dbg_MsgAssert( 0, ( "File access forbidden while PCM audio is in progress." ));
		return NULL;
	}

	s_lastExecuteSuccess = false;
	return NULL;

	//return pHandle;
}



int PreMgr::pre_fclose(void *fptr)
{
		
	Dbg_MsgAssert(fptr,( "calling fclose with invalid file ptr"));	
	
	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
#if DEBUG_PRE
		printf("PRE: closed file, handle 0x%x\n", (int) fptr);
#endif
		sp_mgr->mp_activePre->Close();
		s_lastExecuteSuccess = true;
		return 0;
	}
	
	s_lastExecuteSuccess = false;
	return 0;
}



size_t PreMgr::pre_fread(void *addr, size_t size, size_t count, void *fptr)
{
		
	Dbg_MsgAssert(fptr,( "calling fread with invalid file ptr"));		

	uint8 *pData = sp_mgr->getContainedFileByHandle( fptr );
	if (pData)
	{
		// read from a simulated file stream in PRE file
		Dbg_AssertPtr(sp_mgr->mp_activePre);

		// There is a subtle bug in the PS2 version, in that the pre version of fread()
		// returns the number of bytes read, rather than the number of items. This
		// version does return the correct value.
		size_t bytes_read = sp_mgr->mp_activePre->Read( addr, size * count );
//		return bytes_read / size;
		return bytes_read;
	}
	if ( Pcm::UsingCD( ) )
	{
		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
		return 0;
	}
	
	s_lastExecuteSuccess = false;
	return 0;
}



size_t  PreMgr::pre_fwrite(const void *addr, size_t size, size_t count, void *fptr)
{


	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "can't write to a PRE file"));
	
	if ( Pcm::UsingCD( ) )
	{
		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
		return 0;
	}

	s_lastExecuteSuccess = false;
	return 0;
}



char *PreMgr::pre_fgets(char *buffer, int maxLen, void *fptr)
{
		

	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "can't do string ops on a PRE file"));
	
	s_lastExecuteSuccess = false;
	return NULL;
}



int PreMgr::pre_fputs(const char *buffer, void *fptr)
{
		

	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "can't do string ops on a PRE file"));
	
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_feof(void *fptr)
{
		
	Dbg_MsgAssert(fptr,( "calling feof with invalid file ptr"));		

	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
		Dbg_AssertPtr(sp_mgr->mp_activePre);
		s_lastExecuteSuccess = true;
		return sp_mgr->mp_activePre->Eof();
	}
	
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_fseek(void *fptr, long offset, int origin)
{
		

	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	if( pData )
	{
		s_lastExecuteSuccess = true;
		return sp_mgr->mp_activePre->Seek( offset, origin );
	}

	Dbg_MsgAssert( !pData, ( "seek not supported for PRE file" ));
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_fflush(void *fptr)
{


	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "flush not supported for PRE file"));
	
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_ftell(void *fptr)
{


	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	if( pData )
	{
		return sp_mgr->mp_activePre->TellActive();
	}

	Dbg_MsgAssert( !pData, ( "tell supported for PRE file" ));
	
	s_lastExecuteSuccess = false;
	return 0;
}
#	endif		// __PLAT_NGC__



// @script | InPreFile | 
// @uparm "string" | filename
bool ScriptInPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	Spt::SingletonPtr pre_mgr;
	return pre_mgr->InPre(pFilename);
}



// @script | LoadPreFile | 
// @uparm "string" | filename
// @flag dont_assert | 
bool ScriptLoadPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	Spt::SingletonPtr pre_mgr;
	pre_mgr->LoadPre(pFilename, pParams->ContainsFlag("dont_assert"));
	return true;
}



// @script | UnloadPreFile | 
// @flag BoardsPre | 
// @flag dont_assert | 
// @uparm "string" | filename
bool ScriptUnloadPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{


	Spt::SingletonPtr pre_mgr;
	
	if (pParams->ContainsFlag("BoardsPre"))
	{
		
//		Spt::SingletonPtr< Mdl::Skate >	pSkate;
//		pSkate->UnloadBoardPreIfPresent(pParams->ContainsFlag("dont_assert"));
		printf ("STUBBED:  Unload BoardsPre, in ScriptUnloadPreFile\n");
		return true;
	}
	
	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	pre_mgr->UnloadPre(pFilename, pParams->ContainsFlag("dont_assert"));
	return true;
}



void *PreMgr::LoadFile( const char *pName, int *p_size, void *p_dest )
{
	// The dest parameter is used only on Ps2.
	Dbg_Assert( p_dest == NULL );

	// NOTE: THIS IS JUST CUT AND PASTE FROM Pre::fileExists
	Dbg_AssertPtr( pName );

	// Replace all '/' with '\'
	char cleaned_name[128];
	const char *pCharIn = pName;
	char *pCharOut = cleaned_name;
	while (1)
	{
		*pCharOut = *pCharIn;
		if (*pCharIn == '\0') break;
		if (*pCharOut == '/') *pCharOut = '\\';
		pCharIn++;
		pCharOut++;		
	}

	mp_table->IterateStart();
	PreFile *pPre = mp_table->IterateNext();
	while(pPre)
	{
		
		void *p_data = pPre->LoadContainedFile(cleaned_name,p_size);
		if (p_data)
		{
			return p_data;
		}
		pPre = mp_table->IterateNext();
	}
	return NULL;

}



} // namespace File






================================================
FILE: Code/Sys/File/XBox/p_streamer.cpp
================================================
#include 
#include 
#include "p_streamer.h"


//-----------------------------------------------------------------------------
// DVD/HD sector sizes (these will never change)
//-----------------------------------------------------------------------------
#define HD_SECTOR_SIZE	( 512 )
#define DVD_SECTOR_SIZE	( 2 * 1024 )

const char *p_time_string = __TIME__;
const char *p_date_string = __DATE__;

//-----------------------------------------------------------------------------
// Name: CheckDMAAlignment
// Desc: checks the alignment of file offset, read/write size, and 
//       target/source memory for DMA IO
//-----------------------------------------------------------------------------
#ifdef _DEBUG
VOID Check_DMA_Alignment( HANDLE hFile, VOID* pBuffer, DWORD dwOffset, DWORD dwNumBytesIO )
{
    // assert proper target memory alignment
    Dbg_Assert( pBuffer );
    Dbg_Assert( (DWORD(pBuffer) & 0x00000003) == 0 );

     // figure out whether this file is on the DVD or hard disk
    BY_HANDLE_FILE_INFORMATION Info;
    GetFileInformationByHandle( hFile, &Info );
    DWORD dwDVDSerialNumber;
    GetVolumeInformation( "D:\\", NULL, 0, &dwDVDSerialNumber, NULL, NULL, NULL, 0 );

    // assert proper file offset and read/write size
    if( Info.dwVolumeSerialNumber == dwDVDSerialNumber )
    {
        Dbg_Assert( dwOffset % DVD_SECTOR_SIZE == 0 );         
        Dbg_Assert( dwNumBytesIO % DVD_SECTOR_SIZE == 0); 
    }
    else
    {
        Dbg_Assert( dwOffset % HD_SECTOR_SIZE == 0 );       
        Dbg_Assert( dwNumBytesIO % HD_SECTOR_SIZE == 0 ); 
    }
}
#endif


//-----------------------------------------------------------------------------
// Name: CLevelLoader()
// Desc: Constructor
//-----------------------------------------------------------------------------
CLevelLoader::CLevelLoader()
:   m_pSysMemData( NULL ),
    m_dwSysMemSize( 0 ),
    m_pLevels( NULL ),
    m_dwNumLevels( 0 ),
    m_pCurrentLevel( NULL ),
    m_IOState( Idle ),
    m_pSysMemBuffer( NULL ),
    m_pFileSig( NULL ),
    m_hSignature( INVALID_HANDLE_VALUE )
{
    // create file sig buffer
    Dbg_Assert( m_pFileSig == NULL );
    Dbg_Assert( XCALCSIG_SIGNATURE_SIZE + sizeof(SIG_MAGIC) <= HD_SECTOR_SIZE );
	m_pFileSig = new BYTE[HD_SECTOR_SIZE];
}



//-----------------------------------------------------------------------------
// Name: ~CLevelLoader()
// Desc: Destructor
//-----------------------------------------------------------------------------
CLevelLoader::~CLevelLoader()
{
    // Ensure we are not being deleted in the middle of an IO op.
	Dbg_Assert( IsIdle());

    // These should be already be cleaned up.
    Dbg_Assert( m_hSignature == INVALID_HANDLE_VALUE );
    Dbg_Assert( m_pSysMemBuffer == NULL );
    Dbg_Assert( m_pFileSig == NULL );

    // Close all handles.
    for( UINT i = 0; i < m_dwNumLevels; i++ )
    {
        CloseHandle( m_pLevels[i].hDVDFile );
        CloseHandle( m_pLevels[i].hHDFile );
        CloseHandle( m_pLevels[i].hSigFile );
    }

	// We don't own this memory, so don't delete it.
    m_pSysMemData = NULL;

	// Free the level state array.
	delete [] m_pLevels;
	m_pLevels = NULL;
}



//-----------------------------------------------------------------------------
// Name: Initialize
// Desc: Initialize loader
//-----------------------------------------------------------------------------
HRESULT CLevelLoader::Initialize( SLevelDesc* pDescs, DWORD dwNumLevels, BYTE* pSysMemData, DWORD dwSysMemSize, BOOL *p_signal )
{
	// Make sure relevant data directories are on the HD utility section.
	CreateDirectory( "Z:\\data", NULL );
	CreateDirectory( "Z:\\data\\pre", NULL );
	CreateDirectory( "Z:\\data\\music", NULL );
	CreateDirectory( "Z:\\data\\music\\wma", NULL );

	// Copy signal.
	m_pOkayToUseUtilityDrive = p_signal;
	
	// Initialize data.
	Dbg_Assert( pSysMemData );
    m_dwSysMemSize	= dwSysMemSize;
    m_pSysMemData	= pSysMemData;

	// Create level states.
	m_dwNumLevels = dwNumLevels;
	m_pLevels = new SLevelState[m_dwNumLevels];
    
	// Init level states.
    for( UINT i = 0; i < m_dwNumLevels; i++ )
    {
        // Copy level desc.
        m_pLevels[i].Desc = pDescs[i];

		// Clear file handle.
        m_pLevels[i].hDVDFile			= INVALID_HANDLE_VALUE;
        m_pLevels[i].hHDFile			= INVALID_HANDLE_VALUE;
        m_pLevels[i].hSigFile			= INVALID_HANDLE_VALUE;
        m_pLevels[i].bWasPreCached		= FALSE;
        m_pLevels[i].bWasCacheCorrupted	= FALSE;
        m_pLevels[i].dwDVDFileSize		= 0;
    }

	return RefreshLevelStates();
}



//-----------------------------------------------------------------------------
// Name: RefreshLevelStates
// Desc: Refreshes the level loader's level states
//-----------------------------------------------------------------------------
HRESULT CLevelLoader::RefreshLevelStates()
{
    Dbg_Assert( IsIdle());

    // Init states.
    for( UINT i = 0; i < m_dwNumLevels; i++ )
    {   
        m_pLevels[i].bIsPreCached = FALSE;
        m_pLevels[i].bIsCacheCorrupted = FALSE;

        m_pLevels[i].bIsOpen = FALSE;
        m_pLevels[i].dwSysMemSize = 0;
        m_pLevels[i].dwVidMemSize = 0;

        // Close any opened handles.
        CloseHandle( m_pLevels[i].hDVDFile );
        CloseHandle( m_pLevels[i].hSigFile );
        CloseHandle( m_pLevels[i].hHDFile );

        m_pLevels[i].hDVDFile = INVALID_HANDLE_VALUE;
        m_pLevels[i].hHDFile = INVALID_HANDLE_VALUE;
        m_pLevels[i].hSigFile = INVALID_HANDLE_VALUE;

    }
    return S_OK;
}



/******************************************************************/
/* Name: OpenLevel												  */
/* Desc: opens a level											  */
/******************************************************************/
HRESULT CLevelLoader::OpenLevel( SLevelState* pLevel, DWORD dwFlags )
{
	// If the level has never been opened before, open in.
    if( !pLevel->bIsOpen )
    {
        // Check that the level file handles are not open.
        Dbg_Assert( pLevel->hDVDFile == INVALID_HANDLE_VALUE );
        Dbg_Assert( pLevel->hHDFile == INVALID_HANDLE_VALUE );
        Dbg_Assert( pLevel->hSigFile == INVALID_HANDLE_VALUE );

        char szBuffer[MAX_PATH];
        
		// DVD File.
        sprintf( szBuffer, "D:\\data\\%s", pLevel->Desc.szName );

        // Open DVD file for reading.
		pLevel->hDVDFile = CreateFile( szBuffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, dwFlags, NULL );

		if( pLevel->hDVDFile == INVALID_HANDLE_VALUE )
			return EndOpenLevel( pLevel, BadOpen );

		// Get size of file on DVD. NOTE: This size is used to help detect corrupt cached levels. If a cached level
		// is not the same size as its DVD counterpart, the cached level is considered corrupt.
        pLevel->dwDVDFileSize = ::GetFileSize( pLevel->hDVDFile, NULL );

		// NOTE: Both files stored on the utility drive (signature file and level) are pre-sized. This prevents the files from
		// becoming fragmented in the FATX.  Also, Overlapped reads/writes to the hard disk are synchronous if the file system
		// has to hit the FATX to determine local->physical cluster mapping.

        // SIG File.
        sprintf( szBuffer, "Z:\\data\\%s.sig", pLevel->Desc.szName );

		// See if we have a cached sig.
        pLevel->bIsPreCached = ( GetFileAttributes( szBuffer ) != DWORD( -1 ));
                
		// Open sig file for reading and writing.
        pLevel->hSigFile = CreateFile( szBuffer,
                                       GENERIC_READ | GENERIC_WRITE,
                                       FILE_SHARE_READ |
                                       FILE_SHARE_WRITE |
                                       FILE_SHARE_DELETE,
                                       NULL,
                                       OPEN_ALWAYS,
                                       dwFlags,
                                       NULL );
        if( pLevel->hSigFile == INVALID_HANDLE_VALUE )
            return EndOpenLevel( pLevel, BadOpen );
        
		// Cache is corrupted if sig file is not the right size. NOTE: The sig file is HD_SECTOR_SIZE in size. The actual
		// signature calculated by XCalculateSignature* is much smaller that HD_SECTOR_SIZE, but we must write at least
		// HD_SECTOR_SIZE to use DMA on the hard disk.
		pLevel->bIsCacheCorrupted = pLevel->bIsPreCached && ::GetFileSize( pLevel->hSigFile, NULL ) != HD_SECTOR_SIZE;
        
		// If the sig file is corrupted or not saved, resize it.
        if( !pLevel->bIsPreCached || pLevel->bIsCacheCorrupted )
        {
			// Set file size for faster write.
            SetFilePointer( pLevel->hSigFile, HD_SECTOR_SIZE, NULL, FILE_BEGIN );
			SetEndOfFile( pLevel->hSigFile );
            
			// Clear Sig magic number.
            BYTE apyBuffer[HD_SECTOR_SIZE];
            *(DWORD*)(apyBuffer) = ~SIG_MAGIC;
            SetCurrentFile( pLevel->hSigFile );
            if( FAILED( DoIO( Write, apyBuffer, HD_SECTOR_SIZE )))
                return EndOpenLevel( pLevel, BadSigMagicWrite );

			// Wait for IO completion for sig magic writes.
            while( HasIOCompleted() != S_OK );
        }

        
        // CACHED file.
        sprintf( szBuffer, "Z:\\data\\%s", pLevel->Desc.szName );

		// See if we have a cached file.
        pLevel->bIsPreCached = pLevel->bIsPreCached && ( GetFileAttributes( szBuffer ) != DWORD( -1 )); 
        
        // Open cached file for reading and writing.
        pLevel->hHDFile = CreateFile( szBuffer,
                                      GENERIC_WRITE | GENERIC_READ, 
                                      FILE_SHARE_READ |
                                      FILE_SHARE_WRITE |
                                      FILE_SHARE_DELETE,
                                      NULL,
                                      OPEN_ALWAYS,
                                      dwFlags,
                                      NULL );

        if( pLevel->hHDFile == INVALID_HANDLE_VALUE )
            return EndOpenLevel( pLevel, BadOpen );
        
		// Cache is corrupted if cache file is not the right size
		pLevel->bIsCacheCorrupted = pLevel->bIsPreCached && (( ::GetFileSize( pLevel->hHDFile, NULL ) != pLevel->dwDVDFileSize ) || pLevel->bIsCacheCorrupted );

        // If the cache file is corrupted or not saved, resize it.
        if( !pLevel->bIsPreCached || pLevel->bIsCacheCorrupted )
        {
            // Set file size for faster write.
			DWORD rv = SetFilePointer( pLevel->hHDFile, pLevel->dwDVDFileSize, NULL, FILE_BEGIN );
			if( rv == INVALID_SET_FILE_POINTER )
			{
				DWORD last_error = GetLastError();
				printf( "Last error: %x\n", last_error );
			}
			SetEndOfFile( pLevel->hHDFile );
        }
		return EndOpenLevel( pLevel, FilesOpened );
    }

    return S_OK;
}




//-----------------------------------------------------------------------------
// Name: AsyncStreamLevel
// Desc: Loads all the resources from the given XPR asynchronously using DMA
//-----------------------------------------------------------------------------
VOID CLevelLoader::AsyncStreamLevel( DWORD dwLevel )
{
    Dbg_Assert( IsIdle() );
    Dbg_Assert( dwLevel < m_dwNumLevels );

	// We are no longer idle.
	m_IOState = Begin;

    // set current level
	m_pCurrentLevel		= &m_pLevels[dwLevel];
    m_dwCurrentLevel	= dwLevel;

	// Start timer.
	m_dStartTime = Tmr::GetTime();
}


//-----------------------------------------------------------------------------
// Name: StreamCurrentBundle()
// Desc: updates the streaming state, returning S_OK when finished
//-----------------------------------------------------------------------------
HRESULT CLevelLoader::StreamCurrentLevel()
{
    HRESULT hr;
  
    switch(m_IOState)
    {
        case Begin:
        {
            // reset IO
            ResetStreaming();

            // if we are reading from the cache, get the signature
            if( IsCurrentCacheGood())
            {
                SetCurrentFile( m_pCurrentLevel->hSigFile );
                if( FAILED( hr = DoIO( Read, m_pFileSig, HD_SECTOR_SIZE )))
                    return EndStreamLevel( BadRead );
            }
            m_IOState = LoadSig;
        }

        case LoadSig:
        {
            // wait for previous IO to complete
            if( FAILED( hr = HasIOCompleted()))
                return EndStreamLevel( hr == E_PENDING ? Pending : BadRead );
            
            // Reading cached file, so a signature exists
            if( IsCurrentCacheGood())
            {
				// Look for sig magic number.
				DWORD dwSig;
				dwSig = *(DWORD*)( m_pFileSig );
				if( dwSig != SIG_MAGIC )
                {
                    if( dwSig == ~SIG_MAGIC )
                        return EndStreamLevel( NoSigMagic );
                    else
                        return EndStreamLevel( BadSig );
                }
				
				// Check date and time.            
				dwSig = *(((DWORD*)m_pFileSig ) + 1 );
				if( dwSig != Crc::GenerateCRCFromString( p_date_string ))
                {
					return EndStreamLevel( BadSig );
                }
				dwSig = *(((DWORD*)m_pFileSig ) + 2 );
				if( dwSig != Crc::GenerateCRCFromString( p_time_string ))
                {
					return EndStreamLevel( BadSig );
                }
			}
            else
			{
                // Set sig magic number so it is written out.
                *(((DWORD*)m_pFileSig )	+ 0 )	= SIG_MAGIC;
                *(((DWORD*)m_pFileSig ) + 1 )	= Crc::GenerateCRCFromString( p_date_string );
                *(((DWORD*)m_pFileSig ) + 2 )	= Crc::GenerateCRCFromString( p_time_string );
			}

            m_IOState = LoadSysMem;
        }

        case LoadSysMem:
        {
            // Wait for previous IO to complete.
            if( FAILED( hr = HasIOCompleted()))
                return EndStreamLevel( hr == E_PENDING ? Pending : BadRead );
        
            // Read initial system memory buffer.
            if( IsCurrentCacheGood())
			{
				// At this point we have decided the sig file is fine, which indicates that the cached file is fine also.
				// As such, no further work required for this file.
//				SetCurrentFile( m_pCurrentLevel->hHDFile );
				m_pCurrentLevel->bIsPreCached = TRUE;
				m_pCurrentLevel->bIsCacheCorrupted = FALSE;
				return EndStreamLevel( Finished );
			}
            else
			{
				// Set file pointer to start of HD file.
                SetCurrentFile( m_pCurrentLevel->hHDFile );
				
				// Set file pointer to start of DVD file.
                SetCurrentFile( m_pCurrentLevel->hDVDFile );
			}

			// At this point we want to loop through reading the buffer and writing it until teh file is fully written.
			DWORD	total_bytes_read		= 0;
			DWORD	total_bytes_written		= 0;
			int		bytes_remaining			= m_pCurrentLevel->dwDVDFileSize;
			for( ;; )
			{
				// Set the DVD file, but don't reset the file pointer.
                SetCurrentFile( m_pCurrentLevel->hDVDFile, FALSE );

//				DWORD bytes_to_transfer = ( bytes_remaining >= m_dwSysMemSize ) ? m_dwSysMemSize : bytes_remaining;
				DWORD bytes_to_transfer = ( bytes_remaining >= (int)m_dwSysMemSize ) ? m_dwSysMemSize : m_dwSysMemSize;
				
				if( FAILED( DoIO( Read, m_pSysMemData, bytes_to_transfer )))
	                return EndStreamLevel( BadRead );

				total_bytes_read += bytes_to_transfer;

				// Set the HD file, but don't reset the file pointer.
				SetCurrentFile( m_pCurrentLevel->hHDFile, FALSE );

				if( FAILED( DoIO( Write, m_pSysMemData, bytes_to_transfer )))
	                return EndStreamLevel( BadWrite );
				
				total_bytes_written += bytes_to_transfer;
				bytes_remaining -= bytes_to_transfer;
				
				if( bytes_remaining <= 0 )
				{
					DWORD rv = SetFilePointer( m_pCurrentLevel->hHDFile, m_pCurrentLevel->dwDVDFileSize, NULL, FILE_BEGIN );
					if( rv == INVALID_SET_FILE_POINTER )
					{
						DWORD last_error = GetLastError();
						printf( "Last error: %x\n", last_error );
					}
					
					SetEndOfFile( m_pCurrentLevel->hHDFile );
					
					m_IOState = WriteSig;
					break;
				}
			}
        }

        case WriteSig:
        {
            // Wait for previous IO to complete.
            if( FAILED( hr = HasIOCompleted() ) )
                return EndStreamLevel( hr == E_PENDING ? Pending : BadRead );
    
			// We are now loaded.
			m_dLoadTime = Tmr::GetTime() - m_dStartTime;

            // We write the sig file last. If the box is turned off during a cache operation, the sig file will be missing.
            // If the box is turned off during the write of the sig file, it won't have a header or will not match the level
            // data. In either case, the level will be re-cached.
            SetCurrentFile( m_pCurrentLevel->hSigFile );
            if( FAILED( hr = DoIO( Write, m_pFileSig, HD_SECTOR_SIZE) ) )
                return EndStreamLevel( BadWrite );

            m_IOState = End;
        }

        case End:
        {
            // Wait for previous IO to complete.
            if( FAILED( hr = HasIOCompleted() ) )
                return EndStreamLevel( hr == E_PENDING ? Pending : BadRead );
    
			// Record cache time.
			m_dCacheTime = Tmr::GetTime() - m_dStartTime;

			// We are now cached.
            m_pCurrentLevel->bIsPreCached		= TRUE;
            m_pCurrentLevel->bIsCacheCorrupted	= FALSE;

            return EndStreamLevel( Finished );
        }
    }

    // should never reach here
    Dbg_Assert( FALSE );
    return E_FAIL;
}



//-----------------------------------------------------------------------------
// Name: EndOpenLevel()
// Desc: called when IO has completed or there is an error
//-----------------------------------------------------------------------------
HRESULT CLevelLoader::EndOpenLevel( SLevelState* pLevel, FileOpenStatus Status)
{
    Dbg_Assert(pLevel);

    if(Status == BadOpen || Status == BadSigMagicWrite)
    {
        // close all file handles
        CloseHandle( pLevel->hDVDFile );
        CloseHandle( pLevel->hHDFile );
        CloseHandle( pLevel->hSigFile );
        pLevel->hDVDFile = INVALID_HANDLE_VALUE;
        pLevel->hHDFile = INVALID_HANDLE_VALUE;
        pLevel->hSigFile = INVALID_HANDLE_VALUE;

        return E_FAIL;
    }

    // level is now opened
    pLevel->bIsOpen = TRUE;

    return S_OK;
}


//-----------------------------------------------------------------------------
// Name: EndStreamLevel()
// Desc: called when IO has completed or there is an error
//-----------------------------------------------------------------------------
HRESULT CLevelLoader::EndStreamLevel( FileIOStatus Status )
{
    // if IO is not pending, we are finished
    if(Status != Pending)
    {
        // close the signature if needed
        if( m_hSignature != INVALID_HANDLE_VALUE )
        {
            XCalculateSignatureEnd( m_hSignature, NULL );
            m_hSignature = INVALID_HANDLE_VALUE;
        }
    }

    // determine new state
    switch(Status)
    {
        case BadRead:
        case BadHeader:
        case BadSig:
        case SigMismatch:
            m_pCurrentLevel->bIsCacheCorrupted = TRUE;
            m_IOState = Begin; // retry streaming the level
            return E_FAIL;
        case NoSigMagic:
            m_pCurrentLevel->bIsPreCached = FALSE;
            m_IOState = Begin; // retry streaming the level
            return E_FAIL;
        case BadWrite:  // if we have a bad write, finish anyways since the cache will be corrupt next time around
            m_pCurrentLevel->bIsCacheCorrupted = TRUE;
        case Finished:
            m_IOState = Idle;  // done with IO, so we are now idle

			// close all file handles
			CloseHandle( m_pCurrentLevel->hDVDFile );
			CloseHandle( m_pCurrentLevel->hHDFile );
			CloseHandle( m_pCurrentLevel->hSigFile );
			m_pCurrentLevel->hDVDFile = INVALID_HANDLE_VALUE;
			m_pCurrentLevel->hHDFile = INVALID_HANDLE_VALUE;
			m_pCurrentLevel->hSigFile = INVALID_HANDLE_VALUE;
			
			return S_OK;
        case Pending:
            return E_PENDING;
    }

    // should never reach here
    Dbg_Assert( FALSE );
    return E_FAIL;
}


//-----------------------------------------------------------------------------
// Name: CRestIO()
// Desc: Records last state for current level and get ready for IO
//-----------------------------------------------------------------------------
VOID CLevelLoader::ResetStreaming()
{
    // record last state
    m_pCurrentLevel->bWasPreCached = m_pCurrentLevel->bIsPreCached;
    m_pCurrentLevel->bWasCacheCorrupted = m_pCurrentLevel->bIsCacheCorrupted;

    // begin signature
    Dbg_Assert( m_hSignature == INVALID_HANDLE_VALUE );
    m_hSignature = XCalculateSignatureBegin( XCALCSIG_FLAG_NON_ROAMABLE );
    Dbg_Assert( m_hSignature != INVALID_HANDLE_VALUE );

    // create file sig buffer
//    Dbg_Assert( m_pFileSig == NULL );
//    Dbg_Assert( XCALCSIG_SIGNATURE_SIZE + sizeof(SIG_MAGIC) <= HD_SECTOR_SIZE );
//    m_pFileSig = new BYTE[HD_SECTOR_SIZE];
}







//-----------------------------------------------------------------------------
// Name: IOProc
// Desc: Thread proc for IO
//-----------------------------------------------------------------------------
DWORD WINAPI IOProc( LPVOID lpParameter )
{
    CThreadedLevelLoader* pLoader = (CThreadedLevelLoader*)lpParameter;
    Dbg_Assert( pLoader );
   
	// Wait for the IO event to be signaled.
	WaitForSingleObject( pLoader->m_hEvent, INFINITE );
	
	for( ;; )
    {
        
        // NOTE: In the threaded version of the loader, we are able to open
        //       files and set file sizes mid-game without noticing a severe
        //       frame "glitch."  If you use overlapped IO, you must remember
        //       that OpenFile, SetEndOfFile, etc. are blocking

        // open the current level
        // If we can't open files, we are in trouble, so in the release build, we just keep trying
//        while( FAILED( pLoader->OpenLevel( pLoader->m_pCurrentLevel, FILE_FLAG_NO_BUFFERING | FILE_FLAG_SEQUENTIAL_SCAN )))
        while( FAILED( pLoader->OpenLevel( pLoader->m_pCurrentLevel, FILE_FLAG_SEQUENTIAL_SCAN )))
        {
            // break in the debug build to find out what the problem is
            Dbg_Assert( FALSE );
        }
    
        // Keep trying to load and cache the level until we are successful.
        while( FAILED( pLoader->StreamCurrentLevel()));

		// Level loaded and cached.
		++pLoader->m_dwCurrentLevel;
		if( pLoader->m_dwCurrentLevel < pLoader->m_dwNumLevels )
		{
			pLoader->m_pCurrentLevel = &pLoader->m_pLevels[pLoader->m_dwCurrentLevel];
			pLoader->m_IOState = CThreadedLevelLoader::Begin;
		}
		else
		{
			break;
		}
	
	}

	// If the loaded has closed the thread we are finished. Signal the boolean value passed in.
	*( pLoader->m_pOkayToUseUtilityDrive ) = true;

	ExitThread( 0 );
}



//-----------------------------------------------------------------------------
// Name: CThreadedLevelLoader()
// Desc: Constructor
//-----------------------------------------------------------------------------
CThreadedLevelLoader::CThreadedLevelLoader() : m_bKillThread( FALSE )
{
    // Create event that is used to block the thread when it is not being used for IO.
    m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );

    // Create IO thread.
    m_hThread =  CreateThread( NULL, 0, IOProc, this, 0, NULL);     

	SetCurrentFile( INVALID_HANDLE_VALUE );
}



//-----------------------------------------------------------------------------
// Name: ~CThreadedLevelLoader()
// Desc: Destructor
//-----------------------------------------------------------------------------
CThreadedLevelLoader::~CThreadedLevelLoader()
{
    Dbg_Assert( IsIdle() );

    // Kill the thread.

    // signal thread 
    m_bKillThread = TRUE;
    SetEvent( m_hEvent );
    
    // wait for thread to exit
    DWORD dwExitStatus;
    do
    {
        SwitchToThread();  // try to get the thread to execute
        GetExitCodeThread( m_hThread, &dwExitStatus );
    }
    while( dwExitStatus == STILL_ACTIVE );

    // close handle to thread and event
    CloseHandle( m_hThread );
    CloseHandle( m_hEvent );
}



//-----------------------------------------------------------------------------
// Name: DoIO
// Desc: wrapper for ::ReadFile and ::WriteFile that checks parameters
//-----------------------------------------------------------------------------
HRESULT CThreadedLevelLoader::DoIO( IOType Type, VOID* pBuffer, DWORD dwNumBytes )
{
    // Check DMA alignment
#	ifdef _DEBUG
    Check_DMA_Alignment( m_hFile, pBuffer, SetFilePointer( m_hFile, 0, NULL, FILE_CURRENT ), dwNumBytes );
#	endif

    // Do IO
    DWORD dwNumBytesIO;
    BOOL bSuccess;
    if(Type == Read)
    {
        bSuccess = ::ReadFile( m_hFile, pBuffer, dwNumBytes, &dwNumBytesIO, NULL );
    }
    else
        bSuccess = ::WriteFile( m_hFile, pBuffer, dwNumBytes, &dwNumBytesIO, NULL );

	// If IO is successful and we have the number of expected bytes, return success.
    // NOTE: If the file size is X*SECTORSIZE + Y where Y is less than SECTORSIZE, we still request SECTORSIZE bytes when reading Y
    // in order to get DMA. The file system will return success, and the number of bytes read/written is Y.
    if( !bSuccess || ( dwNumBytesIO != dwNumBytes && SetFilePointer( m_hFile, 0, NULL, FILE_CURRENT ) != GetFileSize( m_hFile, NULL )))
//	if( !bSuccess )
        return E_FAIL;
        
    return S_OK;
}



//-----------------------------------------------------------------------------
// Name: SetCurrentFile
// Desc: Sets the current file to be used during IO
//-----------------------------------------------------------------------------
VOID CThreadedLevelLoader::SetCurrentFile( HANDLE hFile, BOOL to_start )
{
	if(( hFile != INVALID_HANDLE_VALUE ) && to_start )
	{
		// Reset file pointer to beginning.
		SetFilePointer( hFile, 0, NULL, FILE_BEGIN );
	}
    m_hFile = hFile;
}



//-----------------------------------------------------------------------------
// Name: AsyncStreamLevel
// Desc: Begins streaming of the level.  returns immediately
//-----------------------------------------------------------------------------
VOID CThreadedLevelLoader::AsyncStreamLevel( DWORD dwLevel )
{
    CLevelLoader::AsyncStreamLevel( dwLevel );

	// Signal thread to start IO.
	SetEvent( m_hEvent );
}


================================================
FILE: Code/Sys/File/XBox/p_streamer.h
================================================
//-----------------------------------------------------------------------------
// File: LevelLoader.h
//
// Desc: Asynchronously loads and caches level data from XPR (Xbox Packed
// Resource) files using DMA.  
//
// Hist: 03.12.02 - New for May XDK release
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#ifndef LEVEL_LOADER_H
#define LEVEL_LOADER_H

#include 
#include 
#include 
#include 



// NOTE: The terms "system memory" and "video memory" are used throughout the
//       source code.  "Video memory" is continuous physically addressed
//       write-combining memory that is used for GPU resources. "System memory"
//       is virtually addressed cached memory that is used by the CPU.

//-----------------------------------------------------------------------------
// SIG_MAGIC
// Marker that specifies that a signature file exists and is valid.
// Overlapped IO may open and pre-size a file without writing to it and
// we want to detect this without signaling a corruption error.
//-----------------------------------------------------------------------------
#define SIG_MAGIC 0xF0F0F0F0ul


//-----------------------------------------------------------------------------
// Name: SLevelDesc
// Desc: Struct that describes a level
//-----------------------------------------------------------------------------
struct SLevelDesc
{
    const CHAR* szName;          // level is assumed to be at media\szName.xpr
//    DWORD       dwNumResources;  // number of resources in level
//    XBRESOURCE* pResourceTags;   // array or resource tags
};


//-----------------------------------------------------------------------------
// Name: SLevelState
// Desc: Struct to hold state about a level
//-----------------------------------------------------------------------------
struct SLevelState
{
    SLevelDesc  Desc;
    DWORD       dwDVDFileSize;   // size of level on DVD.  used for error
                                 // detection
    // state of level cache
    BOOL        bIsPreCached;            
    BOOL        bWasPreCached;
    BOOL        bIsCacheCorrupted;
    BOOL        bWasCacheCorrupted;

    // memory requirements for the level
    DWORD       dwSysMemSize;       
    DWORD       dwVidMemSize;

    // level file handles
    HANDLE      hDVDFile;       // DVD file handle
    HANDLE      hHDFile;        // Utility region file handle
    HANDLE      hSigFile;       // signature file handle

    BOOL        bIsOpen;        // are the handles opened
};


//-----------------------------------------------------------------------------
// Name: class CLevelLoader
// Desc: Cached level base class
//       Handles loading and caching of levels
//-----------------------------------------------------------------------------
class CLevelLoader 
{
public:
    // Constructor/destructor
                    CLevelLoader();
    virtual         ~CLevelLoader();

    // Initializes the class with a list of levels and memory
    HRESULT         Initialize( SLevelDesc* pDescs, DWORD dwNumlevels, BYTE* pSysMemData, DWORD dwSysMemSize, BOOL *p_signal );

    // Load the specified level asynchronously
    virtual VOID    AsyncStreamLevel( DWORD dwlevel );

    // Updates load/cache state
    virtual VOID    Update() = 0;

    // refreshes the opened and loads states of all levels
    virtual HRESULT RefreshLevelStates();


    // function to retrieve state of current level
    inline DWORD   GetCurrentDVDFileSize() const;
    inline BOOL    IsCurrentPrecached() const;        
    inline BOOL    WasCurrentPrecached() const;       
    inline BOOL    IsCurrentCacheCorrupted() const;  
    inline BOOL    WasCurrentCacheCorrupted() const;  
    inline BOOL    IsCurrentOpen() const;
    inline BOOL    IsCurrentLoaded() const;
    
    // functions to retrieve state of loader
    inline BOOL    IsCacheing() const;
    inline BOOL    IsLoading() const;
    inline BOOL    IsIdle() const;
    inline DOUBLE  GetLoadTime() const;   
    inline DOUBLE  GetCacheTime() const;  
    
    // functions to retrieve resource by their offset
    inline VOID*                    GetData( DWORD dwOffset ) const;
    inline LPDIRECT3DRESOURCE8      GetResource( DWORD dwOffset ) const;
    inline LPDIRECT3DTEXTURE8       GetTexture( DWORD dwOffset ) const;
    inline LPDIRECT3DCUBETEXTURE8   GetCubemap( DWORD dwOffset ) const;
    inline LPDIRECT3DVOLUMETEXTURE8 GetVolumeTexture( DWORD dwOffset ) const;
    inline LPDIRECT3DVERTEXBUFFER8  GetVertexBuffer( DWORD dwOffset ) const;
    
    // Functions to retrieve resources by their name
    VOID*                           GetData( const CHAR* strName ) const;
    inline LPDIRECT3DRESOURCE8      GetResource( const CHAR* strName ) const;
    inline LPDIRECT3DTEXTURE8       GetTexture( const CHAR* strName ) const;
    inline LPDIRECT3DCUBETEXTURE8   GetCubemap( const CHAR* strName ) const;
    inline LPDIRECT3DVOLUMETEXTURE8 GetVolumeTexture( const CHAR* strN ) const;
    inline LPDIRECT3DVERTEXBUFFER8  GetVertexBuffer( const CHAR* strN ) const;
    
protected:

    // IO status
    enum FileIOStatus
    {
        BadRead = 0,
        BadWrite,
        BadHeader,
        BadSig,
        SigMismatch,
        NoSigMagic,
        Finished,
        Pending
    };

    // Openfile status
    enum FileOpenStatus
    {
        BadOpen = 0,
        BadSigMagicWrite,
        FilesOpened
    };

     // type of IO requested
    enum IOType
    {
        Read = 0,
        Write,
    };

    // current step in loading the level resources
    enum IOState
    {
        Begin = 0,
        LoadSig,
        LoadSysMem,
        ParseHeader,
        CalcSig,
        LoadVidMem,
        RegisterResources,
        WriteSysMem,
        WriteVidMem,
        WriteSig,
        End,
        Idle
    };


    // opens the current level
    HRESULT         OpenLevel( SLevelState* pState, DWORD dwFlags );
    HRESULT         EndOpenLevel( SLevelState* pLevel, FileOpenStatus Status );

    // streams the current level
    HRESULT         StreamCurrentLevel();
    VOID            ResetStreaming();
    HRESULT         EndStreamLevel( FileIOStatus Status );

    // is the current cache valid
    inline BOOL     IsCurrentCacheGood();

    // set the current IO file
    virtual VOID    SetCurrentFile( HANDLE hFile, BOOL to_start = TRUE ) = 0;

    // wrappers for read and write file
    virtual HRESULT DoIO( IOType Type, VOID* pBuffer, DWORD dwNumBytes ) = 0;

    // has an IO op completed
    virtual HRESULT HasIOCompleted() = 0;



    // Allocated memory for resource headers etc.
    BYTE*           m_pSysMemData;    
    DWORD           m_dwSysMemSize;

    // level state array
    SLevelState*    m_pLevels;
    DWORD           m_dwNumLevels;
    DWORD           m_dwCurrentLevel;

    // current level
    SLevelState*   m_pCurrentLevel;

    // stats data
	Tmr::Time		m_dStartTime;
    Tmr::Time		m_dLoadTime;        
    Tmr::Time		m_dCacheTime;
    
    // current IO state
    IOState         m_IOState;

    // temp variables used during load
    BYTE*           m_pSysMemBuffer;
    BYTE*           m_pFileSig;
    HANDLE          m_hSignature;

	BOOL			*m_pOkayToUseUtilityDrive;
};


//-----------------------------------------------------------------------------
// Inlined accessors
//-----------------------------------------------------------------------------
inline DWORD CLevelLoader::GetCurrentDVDFileSize() const
{
    return m_pCurrentLevel ? m_pCurrentLevel->dwDVDFileSize: 0;
}
inline BOOL CLevelLoader::IsCurrentPrecached() const        
{
    return m_pCurrentLevel ? m_pCurrentLevel->bIsPreCached : FALSE;
}
inline BOOL CLevelLoader::WasCurrentPrecached() const       
{
    return m_pCurrentLevel ? m_pCurrentLevel->bWasPreCached : FALSE;
}
inline BOOL CLevelLoader::IsCurrentCacheCorrupted() const   
{
    return m_pCurrentLevel ? m_pCurrentLevel->bIsCacheCorrupted : FALSE;
}
inline BOOL CLevelLoader::WasCurrentCacheCorrupted() const  
{
    return m_pCurrentLevel ? m_pCurrentLevel->bWasCacheCorrupted : FALSE;
}
inline BOOL CLevelLoader::IsCurrentOpen() const           
{
    return m_pCurrentLevel ? m_pCurrentLevel->bIsOpen : FALSE;
}
inline BOOL CLevelLoader::IsCacheing() const
{
    return m_pCurrentLevel &&
        m_IOState > RegisterResources && m_IOState < Idle;
}
inline BOOL CLevelLoader::IsCurrentLoaded() const
{
    return m_pCurrentLevel && m_IOState > RegisterResources;
}
inline BOOL CLevelLoader::IsLoading() const
{
    return m_pCurrentLevel && m_IOState < WriteSysMem;
}
inline DOUBLE CLevelLoader::GetLoadTime() const
{
    return m_dLoadTime;
}
inline DOUBLE CLevelLoader::GetCacheTime() const
{
    return m_dCacheTime;
}
inline BOOL CLevelLoader::IsIdle() const
{
    return m_IOState == Idle;
}
inline BOOL CLevelLoader::IsCurrentCacheGood()
{
    return m_pCurrentLevel ? m_pCurrentLevel->bIsPreCached
           && !m_pCurrentLevel->bIsCacheCorrupted : FALSE;
}
inline VOID* CLevelLoader::GetData( DWORD dwOffset ) const
{
    return m_pCurrentLevel ? &m_pSysMemData[dwOffset] : NULL;
}
inline LPDIRECT3DRESOURCE8 CLevelLoader::GetResource( DWORD dwOffset ) const
{
    return (LPDIRECT3DRESOURCE8)GetData(dwOffset);
}
inline LPDIRECT3DTEXTURE8 CLevelLoader::GetTexture( DWORD dwOffset ) const
{
    return (LPDIRECT3DTEXTURE8)GetData( dwOffset );
}
inline LPDIRECT3DCUBETEXTURE8
CLevelLoader::GetCubemap( DWORD dwOffset ) const
{
    return (LPDIRECT3DCUBETEXTURE8)GetData( dwOffset );
}
inline LPDIRECT3DVOLUMETEXTURE8
CLevelLoader::GetVolumeTexture( DWORD dwOffset ) const
{
    return (LPDIRECT3DVOLUMETEXTURE8)GetData( dwOffset );
}
inline LPDIRECT3DVERTEXBUFFER8
CLevelLoader::GetVertexBuffer( DWORD dwOffset ) const
{
    return (LPDIRECT3DVERTEXBUFFER8)GetData( dwOffset );
}
inline LPDIRECT3DRESOURCE8
CLevelLoader::GetResource( const CHAR* strName ) const
{
    return (LPDIRECT3DRESOURCE8)GetData( strName );
}
inline LPDIRECT3DTEXTURE8
CLevelLoader::GetTexture( const CHAR* strName ) const
{
    return (LPDIRECT3DTEXTURE8)GetResource( strName );
}
inline LPDIRECT3DCUBETEXTURE8
CLevelLoader::GetCubemap( const CHAR* strName ) const
{
    return (LPDIRECT3DCUBETEXTURE8)GetResource( strName );
}
inline LPDIRECT3DVOLUMETEXTURE8
CLevelLoader::GetVolumeTexture( const CHAR* strName ) const
{
    return (LPDIRECT3DVOLUMETEXTURE8)GetResource( strName );
}
inline LPDIRECT3DVERTEXBUFFER8
CLevelLoader::GetVertexBuffer( const CHAR* strName ) const
{
    return (LPDIRECT3DVERTEXBUFFER8)GetResource( strName );
}




//-----------------------------------------------------------------------------
// Name:  IOProc
// Desc: Thread proc for IO
//-----------------------------------------------------------------------------
DWORD WINAPI IOProc( LPVOID lpParameter );


//-----------------------------------------------------------------------------
// Name: class CThreadedLevelLoader
// Desc: Threaded Cached resource.  Handles loading and caching of packed
//       resources, using a thread
//-----------------------------------------------------------------------------
class CThreadedLevelLoader : public CLevelLoader
{
public:

    // Constructor/destructor
                    CThreadedLevelLoader();
    virtual         ~CThreadedLevelLoader();

    virtual VOID    AsyncStreamLevel( DWORD dwlevel );
    virtual VOID    Update() {} // does nothing for the threaded version

protected:

    virtual VOID    SetCurrentFile( HANDLE hFile, BOOL to_start = TRUE );
    virtual HRESULT DoIO( IOType Type, VOID* pBuffer, DWORD dwNumBytes );
    virtual HRESULT HasIOCompleted() { return S_OK; }; // IO is synchronous
                                                       // but in its own thread

    
    // current file
    HANDLE          m_hFile;
    
    // thread handle and wake event
    HANDLE          m_hEvent;
    HANDLE          m_hThread;

    // signal to kill the thread (only done when the class is destroyed)
    BOOL            m_bKillThread;

    // the IO proc is our friend
    friend          DWORD WINAPI IOProc( LPVOID lpParameter );
};




#endif //LEVEL_LOADER_H







================================================
FILE: Code/Sys/File/filesys.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Manager (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		core/sys/mem/alloc.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_FILE_FILESYS_H
#define	__SYS_FILE_FILESYS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace File
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

void InstallFileSystem( void );
void UninstallFileSystem( void );
long GetFileSize(void *pFP);
long GetFilePosition(void *pFP);
float GetPercentageRead(void *pFP);
void InitQuickFileSystem( void );
void ResetQuickFileSystem( void );

uint32	CanFileBeLoadedQuickly( const char *filename );
bool LoadFileQuicklyPlease( const char *filename, uint8 *addr );

void   *LoadAlloc(const char *p_fileName, int *p_filesize=NULL, void *p_dest = NULL, int maxSize=0);


void StopStreaming( void );

/////////////////////////////////////////////////////////////////////
// The following are our versions of the ANSI file IO functions.
//
bool   Exist( const char *filename );
void * Open( const char *filename, const char *access );
int    Close( void *pFP );
size_t Read( void *addr, size_t size, size_t count, void *pFP );
size_t ReadInt( void *addr, void *pFP );
size_t Write( const void *addr, size_t size, size_t count, void *pFP );
char * GetS( char *buffer, int maxlen, void *pFP );
int    PutS( const char *buffer, void *pFP );
int    Eof( void *pFP );
int    Seek( void *pFP, long offset, int origin );
int	   Tell( void* pFP );
int    Flush( void *pFP );


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace File

#endif  // __SYS_FILE_FILESYS_H


================================================
FILE: Code/Sys/File/memfile.h
================================================
///////////////////////////////////////////////////////////////////////////////
//
// memfile.h
//
// memory mapped file replacement functions
// allow you to load a file to memory, and then use these MemFile:: functions
// instead of File:: functions, to load it exactly the same as before
// so you would replace
// 	File::Read(
// with
//	MEM_Read(
//
// Note that since these are macros, they dont return anything
//
#define MEM_Read( addr, size, count, pFP )	   \
{											   \
	char *p = (char*)(addr);				   \
	char *q = (char*)(pFP);					   \
	int total = (size)*(count);				   \
	for (;total>0;total--)					   \
		*p++ = *q++;						   \
	pFP = (void*) q;						   \
}											   \

#define MEM_Seek(pFP, offset, origin )	   \
{										   \
	pFP = (void*)((char*)(pFP) + (offset));  \
}										   \



================================================
FILE: Code/Sys/File/ngc/hed.cpp
================================================
/*	Header file functionality...
	.Hed files that describe the contents of .Wad files
	Written by Ken, stolen by Matt*/
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//#include 
//
//#include 

#include 
#include 

#include  

#include 
#include 
#include 

bool sLoadingHed = false;

namespace File
{

static void get_checksums( const char * p_name, uint32 * p_dir, uint32 * p_file )
{
	char name[128];
	char dir[128];
	char file[128];
	uint lp;

	// Convert / to \\.
	for ( lp = 0; lp <= strlen( p_name ); lp++ )
	{
		name[lp] = p_name[lp];
		if ( name[lp] == '/' ) name[lp] = '\\';
	}

	// Find the directory name and the filename.
	for ( lp = strlen( name ) - 1; lp >= 0; lp-- )
	{
		if ( name[lp] == '\\' )
		{
			// Found the directory end.
			strcpy( file, &name[lp+1] );
			name[lp] = '\0';
			strcpy( dir, name );
			break;
		}
	}

	// Chop off the extension (if it exists). 
	if ( file[strlen(file)-4] == '.' )
	{
		file[strlen(file)-4] = '\0';
	}

	// Create checksums.
	if ( p_dir ) *p_dir = Script::GenerateCRC( dir );
	if ( p_file ) *p_file = Script::GenerateCRC( file );
}




// Searches the hed file for the filename. If not found, returns NULL.
SHedFile *FindFileInHed(const char *pFilename, SHed *pHed)
{
	if ( sLoadingHed ) return NULL;

	Dbg_MsgAssert(pFilename,("NULL pFilename"));
	Dbg_MsgAssert(pHed,("NULL pHed"));

	uint32 check_dir;
	uint32 check_file;

	get_checksums( pFilename, &check_dir, &check_file );

	SHed *pHd=pHed;
	while ( pHd->numFiles != 0xffffffff )
	{
		if ( pHd->Checksum == check_dir )
		{
			SHedFile * pF = pHd->p_fileList;
			for ( uint lp2 = 0; lp2 < pHd->numFiles; lp2++ )
			{
				if ( pF->Checksum == check_file )
				{
					return pF;
				}
				pF++;
			}
		}
		pHd++;
	}

	return NULL;
}

// Searches the hed file for the filename with the same checksum. If not found, returns NULL.
SHedFile *FindFileInHedUsingChecksum( uint32 checksum, SHed *pHed )
{
	if ( sLoadingHed ) return NULL;

	Dbg_MsgAssert(pHed,("NULL pHed"));

	SHed *pHd=pHed;
	while ( pHd->numFiles != 0xffffffff )
	{
		SHedFile * pF = pHd->p_fileList;
		for ( uint lp2 = 0; lp2 < pHd->numFiles; lp2++ )
		{
			if ( pF->Checksum == checksum )
			{
				return pF;
			}
			pF++;
		}
		pHd++;
	}

	return NULL;
}

//#ifdef __NOPT_CDROM__
//
//extern void StopStreaming( void );
//
//SHed *LoadHed( const char *filename )
//{
//	NsFile file;
//	SHed *pHed = NULL;
////	int HedId=-1;
//	char hedFileName[255];
//	char uppercaseFilename[ 255 ];
//	int sL = strlen( filename );
//	Dbg_MsgAssert( sL < 255,( "Filename %s too long", filename ));
//	int i;
//	for ( i = 0; i < sL; i++ )
//	{
//		uppercaseFilename[ i ] = toupper( filename[ i ] );
//	}
//	uppercaseFilename[ i ] = '\0';
//	StopStreaming( );
//	sprintf( hedFileName, "cdrom0:\\%s.HED;1", uppercaseFilename );
//	while (HedId<0)
//	{
//		file.open( hedFileName );
////		HedId=sceOpen( hedFileName, SCE_RDONLY );
//		if (HedId<0)
//		{
//			printf("Retrying opening %s\n", hedFileName );
//		}	
//	}
//	printf("%s opened successfully\n", uppercaseFilename );
////	long HedSize=sceLseek(HedId, 0, SCE_SEEK_END);
//	long HedSize=file.size();
//	
//	Dbg_MsgAssert(pHed==NULL,("pHed not NULL ?"));
//	pHed=(SHed*)Mem::Malloc((HedSize+2047)&~2047);
//
///*
//	char *p = (char*) pHed;  
//	// Hmmm.....
//	for (int i=0 ;i < (HedSize+2047)&~2047;i++)
//	{
//		p[i] = 0x55;
//	}
//*/						
//					
////	sceLseek(HedId, 0, SCE_SEEK_SET);
////	sceRead(HedId,pHed,HedSize);
////	sceClose(HedId);
//	file.read( pHed,, HedSize );
//	file.close();
//	return ( pHed );
//	return ( NULL );
//}
//#else
//
SHed *LoadHed( const char *filename )
{
	sLoadingHed = true;

	int lp;
	char tempFileName[255];
	sprintf( tempFileName, "%sNGC.HED", filename );

	void *fp = File::Open( tempFileName, "rb" );

	int fileSize = File::GetFileSize( fp );
	
	uint32 *pHed = (uint32 *)new (Mem::Manager::sHandle().TopDownHeap()) uint8[fileSize];
	Dbg_MsgAssert( pHed,( "Failed to allocate memory for hed %s", tempFileName ));

	File::Read( pHed, 1, fileSize, fp );
	File::Close( fp );

	// Convert to more compact list.
	int files = 0;
	int dirs = 0;

	// First, count how many files we have.
	uint32 * p32 = pHed;
	while ( p32[0] != 0xffffffff )
	{
		files++;

		// Byte order is reversed on NGC.
		p32[0] = ( ( p32[0] >> 24 ) & 0xff ) | ( ( p32[0] >> 8 ) & 0xff00 ) | ( ( p32[0] << 8 ) & 0xff0000 ) | ( ( p32[0] << 24 ) & 0xff000000 );
		p32[1] = ( ( p32[1] >> 24 ) & 0xff ) | ( ( p32[1] >> 8 ) & 0xff00 ) | ( ( p32[1] << 8 ) & 0xff0000 ) | ( ( p32[1] << 24 ) & 0xff000000 );

		int len = strlen( (char *)&p32[2] ) + 1;
		len = ( len + 3 ) & ~3;
		p32 = (uint32*)(((int)&p32[2]) + len);
	}

	// Build list of checksums.
	uint32 *p_offset = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
	uint32 *p_size = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
	uint32 *dir_check = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
	uint32 *file_check = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
	int count = 0;
	p32 = pHed;
	while ( p32[0] != 0xffffffff )
	{
		p_offset[count] = p32[0];
		p_size[count] = p32[1];
		get_checksums( (char *)&p32[2], &dir_check[count], &file_check[count] );
		count++;

		int len = strlen( (char *)&p32[2] ) + 1;
		len = ( len + 3 ) & ~3;
		p32 = (uint32*)(((int)&p32[2]) + len);
	}

	// Second, count how many directories we have.
	for ( lp = 0; lp < files; lp++ )
	{
		bool unique = true;
		for ( int lp2 = 0; lp2 < lp; lp2++ )
		{
			if ( dir_check[lp] == dir_check[lp2] )
			{
				unique = false;
				break;
			}
		}
		if ( unique ) dirs++;
	}

	// Now, allocate final piece of memory.
	int size = ( sizeof( SHed ) * ( dirs + 1 ) ) + ( sizeof( SHedFile ) * files );
	SHed * p_hed = (SHed *)new uint8[size];

	// Fill in directory entries.
	int dirs_added = 0;
	for ( lp = 0; lp < files; lp++ )
	{
		bool unique = true;
		for ( int lp2 = 0; lp2 < dirs_added; lp2++ )
		{
			if ( p_hed[lp2].Checksum == dir_check[lp] )
			{
				unique = false;
				break;
			}
		}
		if ( unique )
		{
			p_hed[dirs_added].Checksum = dir_check[lp];
			dirs_added++;
		}
	}

	// Fill in file entries.
	SHedFile * p_hed_file = (SHedFile *)&p_hed[(dirs+1)];
	for ( lp = 0; lp < dirs; lp++ )
	{
		p_hed[lp].p_fileList = p_hed_file;
		int files_this_dir = 0;
		for ( int lp2 = 0; lp2 < files; lp2++ )
		{
			if ( dir_check[lp2] == p_hed[lp].Checksum )
			{
				p_hed_file->Offset = p_offset[lp2];
				p_hed_file->FileSize = p_size[lp2];
				p_hed_file->Checksum = file_check[lp2];
				files_this_dir++;
				p_hed_file++;
			}
		}
		p_hed[lp].numFiles = files_this_dir;
	}
	p_hed[dirs].numFiles = 0xffffffff;

	// Get rid of temp data.
	delete file_check;
	delete dir_check;
	delete p_size;
	delete p_offset;
	delete pHed;

	sLoadingHed = false;

	return p_hed;
}
//#endif

} // namespace File




================================================
FILE: Code/Sys/File/ngc/hed.h
================================================
/*	Header file functionality...
	.Hed files that describe the contents of .Wad files
	Written by Ken, stolen by Matt*/
#ifndef __HED_H__
#define __HED_H__

namespace File
{

struct SHedFile
{
	// A SECTOR_SIZE aligned offset of a file within skate3.wad
	uint32 Offset;
	
	// The file size, which is the raw file size, not rounded up
	// to a multiple of SECTOR_SIZE
	uint32 FileSize;
	
	// Filename checksum (does not include directory).
	uint32 Checksum;
};

struct SHed
{
	// Number of files in this directory.
	uint32 numFiles;
	
	// Checksum of this directory.
	uint32 Checksum;

	// Pointer to File list.
	SHedFile * p_fileList;

	// The filename, which is actually bigger than one byte, tee hee.
//	const char pFilename[1];
};


SHedFile *FindFileInHed(const char *pFilename, SHed *pHed );
SHedFile *FindFileInHedUsingChecksum( uint32 checksum, SHed *pHed );
SHed *LoadHed( const char *filename );

} // namespace File

#endif




================================================
FILE: Code/Sys/File/ngc/p_AsyncFilesys.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// p_AsyncFilesys.cpp		GRJ 11 Oct 2002
//
// Asynchronous file system
//
///////////////////////////////////////////////////////////////////////////////////////

#include 

#include 

namespace File
{

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void				CAsyncFileLoader::s_plat_init()
{
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void				CAsyncFileLoader::s_plat_cleanup()
{
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CAsyncFileLoader::s_plat_async_supported()
{
	// Not implemented yet
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool				CAsyncFileLoader::s_plat_exist(const char *filename)
{
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileLoader::s_plat_update(void)
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void				CAsyncFileLoader::s_plat_swap_callback_list()
{
}

}






================================================
FILE: Code/Sys/File/ngc/p_asyncFilesys.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			File													**
**																			**
**	Created:		10/11/02	-	grj										**
**																			**
**	File name:		core/sys/p_AsyncFilesys.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_FILE_P_ASYNC_FILESYS_H
#define	__SYS_FILE_P_ASYNC_FILESYS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace File
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace File

#endif  // __SYS_FILE_P_ASYNC_FILESYS_H



================================================
FILE: Code/Sys/File/ngc/p_filesys.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		System Library											**
**																			**
**	Module:			File IO (File) 											**
**																			**
**	File name:		p_filesys.cpp											**
**																			**
**	Created by:		09/25/00	-	dc										**
**																			**
**	Description:	XBox File System										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 
#include 
#include 
#include 

#include 

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/

namespace File
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define CDROMx   // Use this define to get CDROM behavior

#define RWGDFSGLOBAL(var)	(RWPLUGINOFFSET(gdfsGlobals, RwEngineInstance, gdfsModuleInfo.globalsOffset)->var)

#define	PREPEND_START_POS	8

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

//#define READBUFFERSIZE (10240*5)

// GJ:  I added this to track how many skyFiles are active
// at any given time.  I am assuming that there's only one,
// in which case I can just create one global instance
// (previously, we were allocating it off the current heap,
// which was causing me grief during the CAS loading process)
//int g_fileOpenCount = 0;

struct skyFile
{
	
	// the following must match the dumbSkyFile struct
	// in pre.cpp, or else pre file i/o won't work properly
	int				gdfs;
	int32			POS;
	int32			SOF;

	// the rest is skyFile-specific
//	uint8			readBuffer[READBUFFERSIZE];
	uint32		bufferPos;
	bool			bufferValid;
	bool			locked;			// whether the skyfile is currently in use
	
	// Used when CD
	uint32			WadOffset;
	const char		*pFilename;
	
	// Used when non-CD
	#define MAX_PFILESYS_NAME_SIZE 100
	char filename[ MAX_PFILESYS_NAME_SIZE ];

	NsFile ngcFile;
};

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

//static	RwFileFunctions	s_old_fs;
//static	RwInt32			s_open_files = 0;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/* prefopen														  */
/* Same as fopen() except it will prepend the data root directory */
/* For Xbox, path is always relative to location of .xbe image	  */
/* and default path from that location is d:\					  */
/* So incoming files of the form:								  */
/* c:\skate3\data\foo...	become	d:\data\foo...				  */
/* foo...					become	d:\data\foo...				  */
/*                                                                */
/******************************************************************/

//static void* prefopen( const char *filename, const char *mode )
//{
//	// Used for prepending the root data directory on filesystem calls.
//	char		nameConversionBuffer[256] = "d:\\data\\";
//	int			index = PREPEND_START_POS;
//	const char*	p_skip;
//
//	if(( filename[0] == 'c' ) && ( filename[1] == ':' ))
//	{
//		// Filename is of the form c:\skate3\data\foo...
//		p_skip = filename + 15;
//	}
//	else
//	{
//		// Filename is of the form foo...
//		p_skip = filename;
//	}
//
//	while( nameConversionBuffer[index] = *p_skip )
//	{
//		// Switch forward slash directory separators to the supported backslash.
//		if( nameConversionBuffer[index] == '/' )
//		{
//			nameConversionBuffer[index] = '\\';
//		}
//		++index;
//		++p_skip;
//	}
//	nameConversionBuffer[index] = 0;
//
//	// Final hack - if this is a .tex file, switch to a .txx file.
//	if((( nameConversionBuffer[index - 1] ) == 'x' ) &&
//	   (( nameConversionBuffer[index - 2] ) == 'e' ) &&
//	   (( nameConversionBuffer[index - 3] ) == 't' ))
//	{
//		nameConversionBuffer[index - 2] = 'x';
//	}
//
////	return fopen( nameConversionBuffer, mode );
//
//	HANDLE h_file = CreateFile( nameConversionBuffer, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
//	return ( h_file == INVALID_HANDLE_VALUE ) ? NULL : h_file;
//}


// At first, I assumed that only one skyfile could be open at
// a time, but that's not true when testing streams from the
// PC build.
// pc builds need to have two (one for normal files, and one for streams)
// GJ:  for THPS4, we increased this to 3, so that we can play multiple streams
const int MAXOPENFILES = 3;

// Ken note: It used to be that MAXOPENFILES was defined to be 1 when __NOPT_CDROM__OLD
// was set. Now that __NOPT_CDROM__OLD is not used anymore and CD() is used at runtime
// instead, I made MAXOPENFILES always 2. Then, where MAXOPENFILES used to be used in the code
// it now uses a var set to 1 or 2 depending on CD()
static skyFile g_skyFile[MAXOPENFILES];

skyFile* lock_skyfile( void )
{
	// cd builds can only have one file open at any given time
	int max_open_files=MAXOPENFILES;
	
	int i;
	// this tries to find an unlocked skyfile
	for ( i = 0; i < max_open_files; i++ )
	{
		if ( !g_skyFile[i].locked )
		{
			g_skyFile[i].locked = true;
			g_skyFile[i].ngcFile.m_FileOpen			= 0; 
			g_skyFile[i].ngcFile.m_sizeCached		= 0; 
			g_skyFile[i].ngcFile.m_numCacheBytes	= 0; 
			g_skyFile[i].ngcFile.m_seekOffset		= 0;
			g_skyFile[i].ngcFile.m_preHandle		= NULL;
			g_skyFile[i].ngcFile.m_unique_tag		= 0x1234ABCD;
			return &g_skyFile[i];
		}
	}

	Dbg_Message( "Here are the files currently open:" );
	for ( i = 0; i < max_open_files; i++ )
	{
		Dbg_Message( "%s", g_skyFile[ i ].filename );
	}	

	// if we get here, that means that all the sky files were locked
	Dbg_MsgAssert( 0, ( "Trying to open too many files simultaneously (max=%d)", max_open_files ) );
	return NULL;
}

void unlock_skyfile( skyFile* pSkyFile )
{
	// cd builds can only have one file open at any given time
	int max_open_files=MAXOPENFILES;
	

	// this tries to find the sky file that the caller is referencing
	for ( int i = 0; i < max_open_files; i++ )
	{
		if ( pSkyFile == &g_skyFile[i] )
		{
			Dbg_MsgAssert( g_skyFile[i].locked, ( "Trying to unlock a sky file too many times" ) );
			g_skyFile[i].locked = false;
			return;
		}
	}

	// if we get here, that means that the pointer didn't match one of the valid skyfiles
	Dbg_MsgAssert( 0, ( "Unrecognized sky file" ) );
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

long GetFileSize( void* pFP )
{
	Dbg_MsgAssert(pFP,("NULL pFP sent to GetFileSize"));

    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_get_file_size((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	skyFile* fp = (skyFile*)pFP;
	return fp->ngcFile.size();
}

long GetFilePosition(void *pFP)
{
	
	Dbg_MsgAssert(pFP,("NULL pFP sent to GetFilePosition"));

    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_get_file_position((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	skyFile* fp = (skyFile*)pFP;
	return fp->ngcFile.tell();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void InstallFileSystem( void )
{
/*
	RwFileFunctions*	fs = RwOsGetFileInterface();
	Dbg_MsgAssert( fs,( "File System not found" ));
	
	s_old_fs = *fs;    
    
	fs->rwfexist	= fexist;			
	fs->rwfopen		= (rwFnFopen)prefopen;
	fs->rwfclose	= (rwFnFclose)fclose;
	fs->rwfread		= (rwFnFread)fread;
	fs->rwfwrite	= (rwFnFwrite)fwrite;
	fs->rwfgets		= (rwFnFgets)fgets;
	fs->rwfputs		= (rwFnFputs)fputs;
	fs->rwfeof		= (rwFnFeof)feof;
	fs->rwfseek		= (rwFnFseek)fseek;
	fs->rwfflush	= (rwFnFflush)fflush;
	fs->rwftell		= (rwFnFtell)ftell;
*/
}

void InitQuickFileSystem( void )
{
}

uint32	CanFileBeLoadedQuickly( const char* filename )
{
	return 0;
}

bool LoadFileQuicklyPlease( const char* filename, uint8 *addr )
{
	return false;
}

void StopStreaming( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void UninstallFileSystem( void )
{
/*
	RwFileFunctions*	fs = RwOsGetFileInterface();

	Dbg_MsgAssert( fs,( "File System not found" ));

	*fs = s_old_fs;		// restore old
*/

}



////////////////////////////////////////////////////////////////////
// Our versions of the ANSI file IO functions.  They call
// the PreMgr first to see if the file is in a PRE file.
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
// Exist                                                          
bool Exist( const char *filename )
{
    if (PreMgr::sPreEnabled())
    {
        bool retval = PreMgr::pre_fexist(filename);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	NsFile f;
	return f.exist( filename );
}

////////////////////////////////////////////////////////////////////
// Open                                                          
void * Open( const char *filename, const char *access )
{
    if (PreMgr::sPreEnabled())
    {
        void * retval = PreMgr::pre_fopen(filename, access);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	skyFile *fp = lock_skyfile();
	if ( fp )
	{
		strcpy( fp->filename, filename );
	
		if ( fp->ngcFile.open( filename ) )
		{
			fp->SOF = fp->ngcFile.size();
	
			return (void *)fp;
		}
		else
		{
			unlock_skyfile( fp );
			return NULL;
		}
	}
	else
	{
		return NULL;
	}
}

////////////////////////////////////////////////////////////////////
// Close                                                          
int Close( void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_fclose((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	skyFile* fp = (skyFile*)pFP;
	fp->ngcFile.close();

	unlock_skyfile( fp );
	return 0;
}

////////////////////////////////////////////////////////////////////
// Read                                                          
size_t Read( void *addr, size_t size, size_t count, void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        size_t retval = PreMgr::pre_fread(addr, size, count, (PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	skyFile* fp = (skyFile*)pFP;
	return fp->ngcFile.read( addr, size * count ); 
}




///////////////////////////////////////////////////////////////////////
// Read an Integer in PS2 (littleendian) format
// we just read it directly into memory...
size_t ReadInt( void *addr, void *pFP )
{
	return Read(addr,4,1,pFP);	
}




////////////////////////////////////////////////////////////////////
// Write                                                          
size_t Write( const void *addr, size_t size, size_t count, void *pFP )
{
//    if (PreMgr::sPreEnabled())
//    {
//        size_t retval = PreMgr::pre_fwrite(addr, size, count, (PreFile::FileHandle *) pFP);
//        if (PreMgr::sPreExecuteSuccess())
//            return retval;
//    }
//
////    return skyFwrite(addr, size, count, pFP);
	return 0;
}



////////////////////////////////////////////////////////////////////
// GetS                                                          
char * GetS( char *buffer, int maxlen, void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        char * retval = PreMgr::pre_fgets(buffer, maxlen, (PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	skyFile* fp = (skyFile*)pFP;
	return fp->ngcFile.gets( buffer, maxlen ); 
}

////////////////////////////////////////////////////////////////////
// PutS                                                          
int PutS( const char *buffer, void *pFP )
{
//    if (PreMgr::sPreEnabled())
//    {
//        int retval = PreMgr::pre_fputs(buffer, (PreFile::FileHandle *) pFP);
//        if (PreMgr::sPreExecuteSuccess())
//            return retval;
//    }
//
////    return skyFputs(buffer, pFP);
	return 0;
}

////////////////////////////////////////////////////////////////////
// Eof                                                          
int Eof( void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_feof((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	skyFile* fp = (skyFile*)pFP;
	return fp->ngcFile.eof();
}

////////////////////////////////////////////////////////////////////
// Seek                                                          
int Seek( void *pFP, long offset, int origin )
{
	if( PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_fseek((PreFile::FileHandle *) pFP, offset, origin);
		if( PreMgr::sPreExecuteSuccess())
			return retval;
	}
	skyFile* fp = (skyFile*)pFP;
	switch ( origin ) {
		case SEEK_SET:
			fp->ngcFile.seek( NsFileSeek_Start, offset );
			break;
		case SEEK_END:
			fp->ngcFile.seek( NsFileSeek_End, offset );
			break;
		case SEEK_CUR:
		default:
			fp->ngcFile.seek( NsFileSeek_Current, offset );
			break;
	}
	return fp->ngcFile.tell();
}

int32	Tell( void* pFP )
{
	skyFile* fp = (skyFile *) pFP;
	return fp->POS;
}



////////////////////////////////////////////////////////////////////
// Flush                                                          
int Flush( void *pFP )
{
//    if (PreMgr::sPreEnabled())
//    {
//        int retval = PreMgr::pre_fflush((PreFile::FileHandle *) pFP);
//        if (PreMgr::sPreExecuteSuccess())
//            return retval;
//    }
//
////    return skyFflush(pFP);
	return 0;
}






/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace File





================================================
FILE: Code/Sys/File/ngc/p_pre.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		skate3													**
**																			**
**	Module:									 								**
**																			**
**	File name:																**
**																			**
**	Created by:		rjm														**
**																			**
**	Description:						 									**
**																			**
*****************************************************************************/

// start autoduck documentation
// @DOC pre
// @module pre | None
// @subindex Scripting Database
// @index script | pre
                         
/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

//#include 
#include 
#include 
#include 
#include 

// cd shared by the music streaming stuff...  ASSERT if file access attempted
// while music is streaming:
#include 

// script stuff
#include  
#include 

#include 



/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

#define DEBUG_PRE 0

namespace File
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define CURRENT_PRE_VERSION	0xabcd0002			// as of 3/14/2001
//#define CURRENT_PRE_VERSION	0xabcd0001		// until 3/14/2001

#define RINGBUFFERSIZE		 4096	/* N size of ring buffer */	
#define MATCHLIMIT		   18	/* F upper limit for match_length */
#define THRESHOLD	2   /* encode string into position and length */

#define WriteOut(x) 	{*pOut++ = x;}

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

PreMgr *PreMgr::sp_mgr = NULL;
bool    PreMgr::s_lastExecuteSuccess = false; 

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/



#define USE_BUFFER	1		 // we don't need no stinking buffer!!!!
 
#if USE_BUFFER
#ifdef	__PLAT_NGPS__
// ring buffer is just over 4K 4096+17), 
// so fits nicely in the PS2's 8K scratchpad 
unsigned char	text_buf[RINGBUFFERSIZE + MATCHLIMIT - 1];	
// Note:  if we try to use the scratchpad, like this
// then the code actually runs slower
// if we want to optimize this, then it should
// be hand crafted in assembly, using 128bit registers
//	const unsigned char * text_buf = (unsigned char*) 0x70000000;
#else
unsigned char
		text_buf[RINGBUFFERSIZE + MATCHLIMIT - 1];	/* ring buffer of size N,
			with extra F-1 bytes to facilitate string comparison */
#endif
#endif


#define	ReadInto(x)		if (!Len) break; Len--; x = *pIn++ 
#define	ReadInto2(x)	Len--; x = *pIn++ 	  // version that knows Len is Ok


// Decode an LZSS encoded stream
// Runs at approx 12MB/s on PS2	 without scratchpad (which slows it down in C)
// a 32x CD would run at 4.8MB/sec, although we seem to get a lot less than this
// with our current file system, more like 600K per seconds.....
// Need to write a fast streaming file system....

void DecodeLZSS(unsigned char *pIn, unsigned char *pOut, int Len)	/* Just the reverse of Encode(). */
{
	int  i, j, k, r, c;
//	uint64	LongWord;
//	int bytes = 0;
//	unsigned char *pScratch;
	unsigned int  flags;



//	int basetime =  (int) Tmr::ElapsedTime(0);
//	int len = Len;

//	int	OutBytes = 4;
//	int	OutWord = 0;

	#if USE_BUFFER
	for (i = 0; i < RINGBUFFERSIZE - MATCHLIMIT; i++)
		 text_buf[i] = ' ';
	r = RINGBUFFERSIZE - MATCHLIMIT;
	#else
	r = RINGBUFFERSIZE - MATCHLIMIT;
	#endif
	flags = 0;
	for ( ; ; )
	{
		if (((flags >>= 1) & 256) == 0)
		{
			ReadInto(c);
			flags = c | 0xff00;			/* uses higher byte cleverly */
		}										/* to count eight */
		if (flags & 1)
		{
			ReadInto(c);
			//			putc(c, outfile);
			WriteOut(c);
			#if USE_BUFFER
			text_buf[r++] = c;
			r &= (RINGBUFFERSIZE - 1);
			#else
			r++;
//			r &= (RINGBUFFERSIZE - 1);	  // don't need to wrap r until it is used
			#endif
		}
		else
		{
			ReadInto(i);			
			ReadInto2(j);			// note, don't need to check len on this one.... 
			
			i |= ((j & 0xf0) << 4);						// i is 12 bit offset
			
			#if !USE_BUFFER
			j = (j & 0x0f) + THRESHOLD+1;				// j is 4 bit length (above the threshold)
			unsigned char *pStream;
			r &= (RINGBUFFERSIZE - 1);					// wrap r around before it is used
			pStream = pOut - r;					  		// get base of block
			if (i>=r)										// if offset > r, then
				pStream -= RINGBUFFERSIZE;				// it's the previous block
			pStream += i;									// add in the offset to the base
			r+=j;												// add size to r
			while (j--)										// copy j bytes
				WriteOut(*pStream++);
			#else
			
			j = (j & 0x0f) + THRESHOLD;				// j is 4 bit length (above the threshold)
			for (k = 0; k <= j; k++)					// just copy the bytes
			{
				c =  text_buf[(i+k) & (RINGBUFFERSIZE - 1)]; 
				WriteOut(c);
				text_buf[r++] = c;
				r &= (RINGBUFFERSIZE - 1);
			}
			#endif
		}
	}
//	int Time = (int) Tmr::ElapsedTime(basetime);
//	if (Time > 5)
//	{
//		printf("decomp time is %d ms, for %d bytes,  %d bytes/second\n", Time,len, len * 1000 /Time );
//	}

}




void PreFile::s_delete_file(_File *pFile, void *pData)
{
	Dbg_Assert(pFile);
	delete pFile;
}




PreFile::PreFile(uint8 *p_file_buffer)
{
	mp_table = new Lst::StringHashTable<_File>(4);	

	Dbg_AssertPtr(p_file_buffer);

	mp_buffer = p_file_buffer;
	#ifdef __NOPT_ASSERT__
	uint version = 	*((int *) (mp_buffer + 4));
	Dbg_MsgAssert(version == CURRENT_PRE_VERSION,( "PRE file version (%x) not current (%x)",version,CURRENT_PRE_VERSION));
	#endif
	m_numEntries = *((int *)(mp_buffer + 8));

	uint8 *pEntry = mp_buffer + 12;
	for (int i = 0; i < m_numEntries; i++)
	{
		int data_size 				= *((int *) pEntry);
		int compressed_data_size 	= *((int *) (pEntry + 4));
		int text_size 				= *((int *) (pEntry + 8));
		int actual_data_size = (compressed_data_size != 0) ? compressed_data_size : data_size;
			
		char *pName = (char *) pEntry + 12;
		uint8 *pCompressedData = pEntry + 12 + text_size;
		
		_File *pFile = new (Mem::Manager::sHandle().TopDownHeap()) _File;

		if (!mp_table->GetItem(pName)) 
		{
			// file is not in table, safe to add
			mp_table->PutItem(pName, pFile);
			
			pFile->compressedDataSize = compressed_data_size;
			pFile->pCompressedData = pCompressedData; 
			pFile->pData = NULL;
			pFile->sky.POS = 0;
			pFile->sky.SOF = data_size;
		}
		else
			// Somehow, file is already in table, just kill it
			// Later, I'll want to add an assert
			delete pFile;
		
#		if DEBUG_PRE
		printf("   %s, size %d\n", pName, data_size);
#		endif
		
		pEntry += 12 + text_size + ((actual_data_size + 3) & (~3));
	}

#	if DEBUG_PRE
	printf("Done loading PRE\n");
#	endif
	
	mp_activeFile = NULL;
}



PreFile::~PreFile()
{
	delete mp_buffer;
	mp_table->HandleCallback(s_delete_file, NULL);
	mp_table->FlushAllItems();

	delete mp_table;
}



// returns handle pointer
void *PreFile::GetContainedFile(const char *pName)
{
	
	_File *pFile = mp_table->GetItem(pName, false);
	if (!pFile) 
		return NULL;
	
	dumbSkyFile *pHandle = &pFile->sky;
	// kinda roundabout, but sets mp_activeFile
	GetContainedFileByHandle(pHandle);
	
	// do we need to fetch file data?
	if (!mp_activeFile->pData)
	{
		if (mp_activeFile->compressedDataSize)
		{
			mp_activeFile->pData = new (Mem::Manager::sHandle().TopDownHeap()) uint8[mp_activeFile->sky.SOF];		
			// need to uncompress data
			DecodeLZSS(mp_activeFile->pCompressedData, mp_activeFile->pData, mp_activeFile->compressedDataSize);	
		}
	}

	return pHandle;
}
	


uint8 *PreFile::GetContainedFileByHandle(void *pHandle)
{
	mp_table->IterateStart();
	_File *pFile = mp_table->IterateNext();
	while(pFile)
	{
		uint8 *pCompressedData = pFile->pCompressedData;
		if (pCompressedData && &pFile->sky == pHandle)
		{
			mp_activeFile = pFile;
			
			if (mp_activeFile->compressedDataSize)
				return mp_activeFile->pData;
			else
				return mp_activeFile->pCompressedData;
		}
		pFile = mp_table->IterateNext();
	}

	return NULL;
}



void PreFile::Reset()
{
	
	Dbg_AssertPtr(mp_activeFile);

	mp_activeFile->sky.POS = 0;
}



uint32 PreFile::Read(void *addr, uint32 count)
{
	
	Dbg_AssertPtr(mp_activeFile);

	int seek_offs = mp_activeFile->sky.POS;
	unsigned int limit = mp_activeFile->sky.SOF - seek_offs;
	int copy_number = (count <= limit) ? count : limit; 
	if (mp_activeFile->compressedDataSize)
	{
		Dbg_MsgAssert(mp_activeFile->pData,( "file not uncompressed"));
		memcpy(addr, mp_activeFile->pData + mp_activeFile->sky.POS, copy_number);
	}
	else
	{
		memcpy(addr, mp_activeFile->pCompressedData + mp_activeFile->sky.POS, copy_number);
	}

	mp_activeFile->sky.POS += copy_number;

#if DEBUG_PRE
		printf("PRE: read %d bytes from file, handle 0x%x\n", copy_number, (int) mp_activeFile->pData);
#endif
	return copy_number;
}



int PreFile::Eof()
{
	
	Dbg_AssertPtr(mp_activeFile);

	if (mp_activeFile->sky.POS >= mp_activeFile->sky.SOF)
	{
#if DEBUG_PRE
		printf("PRE: at end of file\n");
#endif
		return 1;
	}

#if DEBUG_PRE
	printf("PRE: not at end of file\n");
#endif
	return 0;
}



void PreFile::Close()
{
	
	//Dbg_MsgAssert(mp_activeFile->pData,( "file not uncompressed"));

	if (mp_activeFile->pData)
		delete mp_activeFile->pData;
	mp_activeFile->pData = NULL;
}



int PreFile::Seek(long offset, int origin)
{
	int32 old_pos = mp_activeFile->sky.POS;

	// SEEK_CUR, SEEK_END, SEEK_SET
	switch(origin)
	{
		case SEEK_CUR:
			mp_activeFile->sky.POS += offset;
			break;
		case SEEK_END:
			mp_activeFile->sky.POS = mp_activeFile->sky.SOF - offset;
			break;
		case SEEK_SET:
			mp_activeFile->sky.POS = offset;
			break;
		default:
			return -1;
	}

	if (mp_activeFile->sky.POS < 0 || mp_activeFile->sky.POS > mp_activeFile->sky.SOF)
	{
		mp_activeFile->sky.POS = old_pos;
		return -1;
	}

	return 0;
}



PreMgr::PreMgr() 
{
	mp_table = new Lst::StringHashTable(4);

	sp_mgr = this;


	mp_activeHandle = NULL;
	mp_activeData = NULL;

	mp_activeNonPreHandle = NULL;
}



PreMgr::~PreMgr()
{
	delete mp_table;
}



// Returns handle
// Not frequently called
void *PreMgr::getContainedFile(const char *pName)
{
	
	Dbg_AssertPtr(pName);

	// replace all '/' with '\'
	char cleaned_name[128];
	const char *pCharIn = pName;
	char *pCharOut = cleaned_name;
	while (1)
	{
		*pCharOut = *pCharIn;
		if (*pCharIn == '\0') break;
		if (*pCharOut == '/') *pCharOut = '\\';
		pCharIn++;
		pCharOut++;		
	}

	void *pHandle = NULL;

	mp_table->IterateStart();
	PreFile *pPre = mp_table->IterateNext();
	while(pPre)
	{
		pHandle = pPre->GetContainedFile(cleaned_name);
		if (pHandle) 
		{
			mp_activePre = pPre;
			mp_activeHandle = pHandle;
			mp_activeData = pPre->GetContainedFileByHandle(pHandle);
#			ifdef __PLAT_NGPS__
			scePrintf("+++ %s is in PRE\n", cleaned_name);
#			endif
			return pHandle;
		}
		pPre = mp_table->IterateNext();
	}

#	ifdef __PLAT_NGPS__
	scePrintf("--- %s not found in PRE\n", cleaned_name);
#	endif
	return NULL;
}



// returns pointer to data
uint8 *PreMgr::getContainedFileByHandle(void *pHandle)
{
	
	Dbg_AssertPtr(pHandle);

	// if we know that the file in question is not in the PRE system,
	// then it's a regular file, don't waste time looking for it
	if (mp_activeNonPreHandle == pHandle)
		return NULL;
	
	if (mp_activeHandle == pHandle)
		// mp_activePre will be unchanged
		return mp_activeData;
	
	uint8 *pData = NULL;
	mp_table->IterateStart();
	PreFile *pPre = mp_table->IterateNext();
	while(pPre)
	{
		pData = pPre->GetContainedFileByHandle(pHandle);
		if (pData)
		{
			mp_activePre = pPre;
			mp_activeHandle = pHandle;
			mp_activeData = pData;			
			return pData;
		}
		pPre = mp_table->IterateNext();
	}

	// obviously this file is not in the PRE system, mark as such
	mp_activeNonPreHandle = pHandle;
	return NULL;
}



// there's a wrapper around this now, so that we can do
// some memory-context switching
void PreMgr::loadPre(const char *pFilename, bool dont_assert)
{
	

#	ifdef __PLAT_XBOX__
	// Do nothing.
	return;
#	endif



	m_blockPreLoading = (bool) Script::GetInt("block_pre_loading", false);

	if (m_blockPreLoading)
		return;

	if( Pcm::UsingCD() )
	{
		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
		return;
	}

	// Moved this to below the Pcm::UsingCD() call as that is used (bad!!) to turn off
	// music and streams.
#	ifdef __PLAT_NGPS__
//	scePrintf("Loading PRE file %s...\n", pFilename);
#	endif

	char fullname[256];
	sprintf(fullname, "pre\\%s", pFilename);

	Tmr::Time basetime = Tmr::ElapsedTime(0);


	int file_size;
	uint8 *pFile = NULL;

	file_size = CanFileBeLoadedQuickly( fullname );
	if ( file_size )
	{
		pFile = new (Mem::Manager::sHandle().TopDownHeap()) uint8[file_size];
		bool fileLoaded= LoadFileQuicklyPlease( fullname, pFile );
		if ( !fileLoaded )
		{
			printf( "pre file %s failed to load quickly.\n", fullname );
			Dbg_MsgAssert( 0,( "Fire Matt - pre file didn't load quickly." ));
		}
	}
	else
	{
			void *fp = File::Open(fullname, "rb");
			if (!fp)
			{
			// always run the code below if CD build
				if (dont_assert || Config::CD()) 
				{
					printf("couldn't open %s\n", fullname);
					return;
				}
				Dbg_MsgAssert(0,( "couldn't open %s\n", fullname));
			}


			File::Read(&file_size, 4, 1, fp);
			Dbg_MsgAssert(file_size > 0,( "%s has incorrect file size\n", fullname));
			if (Config::CD())
			{
				if (file_size <= 0) printf("%s has incorrect file size\n", fullname);
			}	


		// Now allocates the .PRE file from the top of the heap, to avoid fragmentation.
			pFile = new (Mem::Manager::sHandle().TopDownHeap()) uint8[file_size];
		//uint8 *pFile = new uint8[file_size];
			File::Read(pFile + 4, 1, file_size, fp);
			File::Close(fp);
		}

		printf("load time for file size %d is %d ms\n", file_size, (int) Tmr::ElapsedTime(basetime));

	// the PRE file object winds up at the top of the heap, too. This is fine because
	// it will be unloaded at the same time as the big file buffer
		if (!mp_table->PutItem(pFilename, new (Mem::Manager::sHandle().TopDownHeap()) PreFile(pFile)))
			Dbg_MsgAssert(0,( "PRE %s loaded twice", pFilename));
}



/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/


DefineSingletonClass(PreMgr, "PRE Manager");



bool PreMgr::InPre(const char *pFilename)
{
	

	return mp_table->GetItem( pFilename );
}



void PreMgr::LoadPre(const char *pFilename, bool dont_assert)
{
	// GJ:  This function is a wrapper around loadPRE(), to
	// make sure that all allocations go on the top-down heap
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	loadPre(pFilename, dont_assert);
	Mem::Manager::sHandle().PopContext();	
}



void PreMgr::LoadPrePermanently(const char *pFilename, bool dont_assert)
{
	// GJ:  This function is a wrapper around LoadPRE(),
	// used for loading PRE files which will reside in memory
	// permanently.  Essentially, we need to make sure that the
	// data is above Renderware's resource arena.  If you didn't
	// use this function, you'd get fragmentation when the resource
	// arena resized itself.

	// Mick: Removed reference to renderware, the old "arena" concept is gone

	
	// Load the pre file...
	// This will go on the top-down heap by default
	LoadPre(pFilename, dont_assert);

}



void PreMgr::UnloadPre(const char *pFilename, bool dont_assert)
{
	

#	ifdef __PLAT_NGPS__
//	scePrintf("Unloading PRE file %s\n", pFilename);
#	endif
	
	if (m_blockPreLoading)
		return;
	
	PreFile *pThePre = mp_table->GetItem(pFilename);
	if (!pThePre)
	{
		if (dont_assert) return;
		Dbg_MsgAssert(0,( "PRE file %s not in PRE manager", pFilename));
	}

	mp_table->FlushItem(pFilename);
	delete pThePre;
}


bool PreMgr::sPreEnabled()
{
	return sp_mgr != NULL;
}

bool PreMgr::sPreExecuteSuccess()
{
	return s_lastExecuteSuccess; 
}

bool PreMgr::pre_fexist(const char *name)
{
	
	Dbg_MsgAssert(name,( "requesting file NULL"));	
	
	if (sp_mgr->getContainedFile(name)) 
	{
#		if DEBUG_PRE
		printf("PRE: file %s exists\n", name);
#		endif
		s_lastExecuteSuccess = true;
		return true;
	}
	if ( Pcm::UsingCD( ) )
	{
		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
		return false;
	}

	return s_lastExecuteSuccess = false;
}



void *PreMgr::pre_fopen(const char *name, const char *access)
{
		
	Dbg_MsgAssert(name,( "trying to open file NULL"));	

	void *pHandle = sp_mgr->getContainedFile(name);
	if (pHandle)
	{
		// if we are going to write the file, we want to use the regular file system
		const char *pChar = access;
		bool am_writing = false;
		while(*pChar)
		{
			if (*pChar != 'r' && *pChar != 'b')
				am_writing = true;
			pChar++;
		}
		
		if (am_writing)
		{
#			ifdef __PLAT_NGPS__
//			scePrintf("    writing file %s\n", name);
#			endif

			// am writing, so we don't need file open in PRE system
			sp_mgr->mp_activePre->Close();
		}
		else
		{
			// we're reading the file from the PRE system
#			if DEBUG_PRE
			printf("PRE: opened file %s, handle is 0x%x\n", name, (int) pHandle);
#			endif
			sp_mgr->mp_activePre->Reset();
			s_lastExecuteSuccess = true;
			return pHandle;
		}
	}

	// if we get here, we are using the regular file system
	if ( Pcm::UsingCD( ) )
	{
		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
		return NULL;
	}

	s_lastExecuteSuccess = false;
	return NULL;

	//return pHandle;
}



int PreMgr::pre_fclose(void *fptr)
{
		
	Dbg_MsgAssert(fptr,( "calling fclose with invalid file ptr"));	
	
	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
#if DEBUG_PRE
		printf("PRE: closed file, handle 0x%x\n", (int) fptr);
#endif
		sp_mgr->mp_activePre->Close();
		s_lastExecuteSuccess = true;
		return 0;
	}
	
	s_lastExecuteSuccess = false;
	return 0;
}



size_t PreMgr::pre_fread(void *addr, size_t size, size_t count, void *fptr)
{
		
	Dbg_MsgAssert(fptr,( "calling fread with invalid file ptr"));		

	uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
		// read from a simulated file stream in PRE file
		Dbg_AssertPtr(sp_mgr->mp_activePre);
		s_lastExecuteSuccess = true;
		return sp_mgr->mp_activePre->Read(addr, size * count);
	}
	if ( Pcm::UsingCD( ) )
	{
		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
		return 0;
	}
	
	s_lastExecuteSuccess = false;
	return 0;
}



size_t  PreMgr::pre_fwrite(const void *addr, size_t size, size_t count, void *fptr)
{
		
	#ifdef __NOPT_ASSERT__
	uint8 *pData = 
	#endif
	sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "can't write to a PRE file"));
	
	if ( Pcm::UsingCD( ) )
	{
		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
		return 0;
	}

	s_lastExecuteSuccess = false;
	return 0;
}



char *PreMgr::pre_fgets(char *buffer, int maxLen, void *fptr)
{
		

	#ifdef __NOPT_ASSERT__
	uint8 *pData = 
	#endif
	sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "can't do string ops on a PRE file"));
	
	s_lastExecuteSuccess = false;
	return NULL;
}



int PreMgr::pre_fputs(const char *buffer, void *fptr)
{
		

	#ifdef __NOPT_ASSERT__
	uint8 *pData = 
	#endif
	sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "can't do string ops on a PRE file"));
	
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_feof(void *fptr)
{
		
	Dbg_MsgAssert(fptr,( "calling feof with invalid file ptr"));		

	uint8 *pData = 	sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
		Dbg_AssertPtr(sp_mgr->mp_activePre);
		s_lastExecuteSuccess = true;
		return sp_mgr->mp_activePre->Eof();
	}
	
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_fseek(void *fptr, long offset, int origin)
{
		

	uint8 *pData = 	sp_mgr->getContainedFileByHandle(fptr);
	if (pData)
	{
		s_lastExecuteSuccess = true;
		return sp_mgr->mp_activePre->Seek(offset, origin);
	}

	Dbg_MsgAssert(!pData,( "seek not supported for PRE file"));
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_fflush(void *fptr)
{
		

	#ifdef __NOPT_ASSERT__
	uint8 *pData = 
	#endif
	sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "flush not supported for PRE file"));
	
	s_lastExecuteSuccess = false;
	return 0;
}



int PreMgr::pre_ftell(void *fptr)
{
		

	#ifdef __NOPT_ASSERT__
	uint8 *pData = 
	#endif
	sp_mgr->getContainedFileByHandle(fptr);
	Dbg_MsgAssert(!pData,( "tell supported for PRE file"));
	
	s_lastExecuteSuccess = false;
	return 0;
}



// @script | InPreFile | 
// @uparm "string" | filename
bool ScriptInPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	Spt::SingletonPtr pre_mgr;
	return pre_mgr->InPre(pFilename);
}



// @script | LoadPreFile | 
// @uparm "string" | filename
// @flag dont_assert | 
bool ScriptLoadPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	Spt::SingletonPtr pre_mgr;
	pre_mgr->LoadPre(pFilename, pParams->ContainsFlag("dont_assert"));
	return true;
}



// @script | UnloadPreFile | 
// @flag BoardsPre | 
// @flag dont_assert | 
// @uparm "string" | filename
bool ScriptUnloadPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
	

	Spt::SingletonPtr pre_mgr;
	
	if (pParams->ContainsFlag("BoardsPre"))
	{
		
//		Spt::SingletonPtr< Mdl::Skate >	pSkate;
//		pSkate->UnloadBoardPreIfPresent(pParams->ContainsFlag("dont_assert"));
		printf ("STUBBED:  Unload BoardsPre, in ScriptUnloadPreFile\n");
		return true;
	}
	
	const char *pFilename;
	pParams->GetText(NONAME, &pFilename, true);

	pre_mgr->UnloadPre(pFilename, pParams->ContainsFlag("dont_assert"));
	return true;
}



} // namespace File







================================================
FILE: Code/Sys/File/ngps/FileIO/FIleIO_IOP.h
================================================

// This file is included by the IOP side only.  Make sure all
// changes are C compatible.

#ifndef	__SYS_FILE_NGPS_FILEIO_IOP_H
#define	__SYS_FILE_NGPS_FILEIO_IOP_H

#include "fileio.h"


#if 0
#define PRINTF(x) printf x
#else
#define PRINTF(x) 
#endif
//#define ERROR(x) printf x
#define ERROR(A...) Kprintf(##A); while(1)
#define xPRINTF(x) 

#define BASE_priority  32

#define TRUE 1
#define FALSE 0

#define FILEIO_MAX_HANDLERS		(16)
#define FILEIO_MAX_BUFFERS		((NUM_DEVICE_TYPES - 1) * 2)		// Don't need buffer for the Unknown device
#define FILEIO_MAX_TASKS		(20)

// Function enums (for callback)
typedef enum
{
	FILEIO_FUNCTION_IDLE = 0,					// No function is being waited on
	FILEIO_FUNCTION_OPEN,
	FILEIO_FUNCTION_SEEK,
	FILEIO_FUNCTION_READ,
	FILEIO_FUNCTION_WRITE,
} EFileIOFunction;

// Device types
typedef enum
{
	DEVICE_CD = 0,
	DEVICE_HOST,
	DEVICE_UNKNOWN,		// This is always the last device type (also where all requests start)
	NUM_DEVICE_TYPES,
} EDeviceType;

typedef enum
{
	MEM_EE = 0,
	MEM_IOP,
} EMemoryType;

// FileIOHandle Flags
#define	FILEIO_HANDLE_BUSY		(0x0001)		// Handle currently busy
#define	FILEIO_HANDLE_RESERVED	(0x0002)		// Handle currently allocated to someone
#define	FILEIO_HANDLE_PREV_DMA	(0x0004)		// Already sent out DMA info (meaning m_last_dma_id is valid)
#define	FILEIO_HANDLE_NOWAIT	(0x0008)		// Last function called finished immediately
#define	FILEIO_HANDLE_EE_MEMORY	(0x0010)		// Destionion is EE memory

// File handle
typedef struct
{
	// General data
	volatile int	m_flags;
//	void *			mp_buffer[2];		// This is only needed for EE transfers
//	int				m_buffer_index;		// This is only needed for EE transfers
	void *			mp_dest_buffer;		// Could be either an EE or IOP address
	int				m_buffer_size;
	EDeviceType		m_device_type;
	int				m_host_fd;			// File descriptor for host IO
	int				m_priority;
	int				m_stream;
	int				m_start_sector;
	int				m_cur_sector;
	int				m_cur_position;
	int				m_file_size;
	int				m_open_request_id;
	SFileIORequest *mp_blocked_request;	// If handle is marked BUSY but waiting for the device, this is the request that
										// will execute when the device finally becomes free.

	// Current IO variables
	EFileIOFunction	m_cur_function;
	int				m_bytes_to_process;
	int				m_bytes_processing;
	unsigned short	m_non_aligned_start_bytes;
	unsigned short	m_non_aligned_end_bytes;
	int				m_request_id;
	int				m_return_value;
	int				m_return_data;
	unsigned int	m_last_dma_id;
} SFileIOHandle;

// Request task
typedef struct FileIOTask
{
	SFileIORequestEntry 	m_entry;
	SFileIOHandle *			mp_file_handle;
	struct FileIOTask *		mp_cont;	// Continued packets for the same request
	struct FileIOTask *		mp_next;
} SFileIOTask;

// Device status
typedef struct
{
	volatile int	m_in_use;
	volatile int	m_waiting_callback;
	SFileIOHandle *	mp_file_handle;
	void *			mp_buffer[2];		// This is only needed for EE transfers
	int				m_buffer_index;		// This is only needed for EE transfers
	int				m_cur_sector;
	int				m_cur_position;
	SFileIOTask *	mp_task_list;
} SFileIODevice;


// Request state
typedef enum
{
	REQUEST_OPEN,			// Still waiting to be started
	REQUEST_PROCESSING,		// Currently executing
	REQUEST_APPEND,			// Needs appending to a current request
	REQUEST_COMPLETE,		// Done
} ERequestState;

// Request Status
typedef struct
{
	int				m_request_id;
	ERequestState	m_request_state;
	SFileIOHandle *	mp_file_handle;
	int				m_return_value;
	int				m_return_data;
} SRequestStatus;

#endif	// __SYS_FILE_NGPS_FILEIO_IOP_H



================================================
FILE: Code/Sys/File/ngps/FileIO/FileIO.h
================================================
// This file is included by both the EE and IOP side.  Make sure all
// changes are C compatible.

#ifndef	__SYS_FILE_NGPS_FILEIO_H
#define	__SYS_FILE_NGPS_FILEIO_H

#include 

#ifndef SECTOR_SIZE
#define SECTOR_SIZE							( 2048 )
#endif // SECTOR_SIZE

// Make sure these don't conflict with the Stream IRX
#define FILEIO_REQUEST_COMMAND				(1)
#define FILEIO_RESULT_COMMAND				(1)
#define FILEIO_NUM_COMMAND_HANDLERS 		(0x10)

#define FILEIO_TEMP_HANDLE_FLAG				(0x80000000)

#define FILEIO_MAX_FILENAME_SIZE			(80)

#define FILEIO_BUFFER_SECTORS				(8)
#define FILEIO_BUFFER_SIZE					(FILEIO_BUFFER_SECTORS * SECTOR_SIZE)

#define FILEIO_CACHE_BLOCK_SIZE				(64)								// Cache block size for EE
#define FILEIO_CACHE_BLOCK_NONALIGN_MASK	(FILEIO_CACHE_BLOCK_SIZE - 1)		// Masks out the non-aligned address bits
#define FILEIO_CACHE_BLOCK_ALIGN_MASK		(~FILEIO_CACHE_BLOCK_NONALIGN_MASK)	// Masks out the aligned address bits

// The FileIO commands
typedef enum
{
	FILEIO_INIT = 0,
	FILEIO_OPEN,
	FILEIO_CLOSE,
	FILEIO_READ,
	FILEIO_WRITE,
	FILEIO_SEEK,
	FILEIO_SET,
	FILEIO_CONT,
} FileIOCommand;

typedef enum
{
	FILEIO_VAR_PRIORITY,
	FILEIO_VAR_BUFFER_SIZE,
	FILEIO_VAR_STREAM,
	FILEIO_VAR_DESTINATION,
} FileIOVariable;

typedef enum
{
	FILEIO_HOSTOPEN_FALSE = 0,
	FILEIO_HOSTOPEN_TRUE,
	FILEIO_HOSTOPEN_EXTEND_NAME,
} FileIOHostOpen;

////////////////////////////////////
// Command parameters
// All structures sizes + 4 bytes (for command itself) must be 16-byte aligned
//

// General Parameters
typedef struct
{
	int				m_param[3];
} SGeneralParam;

// Init Parameters
typedef struct
{
	int				m_priority;
	int				m_buffer_size;
	short			m_memory_dest;
	short			m_init_cd_device;
} SInitParam;

// Open Parameters
typedef struct
{
	int				m_sector_number;
	int				m_file_size;
} SRawFileName;

typedef struct
{
	int				m_buffer_size;
	int				m_priority;

	// Unions must be named in C
	union
	{
		char		 m_filename[FILEIO_MAX_FILENAME_SIZE];
		SRawFileName m_raw_name;
	} m_file;

	short			m_attributes;
	unsigned char	m_host_read;
	unsigned char	m_memory_dest;
} SOpenParam;

// Close Parameters
typedef struct
{
	unsigned int	m_file_handle;
	int				m_pad[2];
} SCloseParam;

// Read/Write Parameters
typedef struct
{
	unsigned int	m_file_handle;
	void *			m_buffer;
	size_t			m_size;
	unsigned short	m_non_aligned_start_bytes;
	unsigned short	m_non_aligned_end_bytes;
	int				m_pad[3];
} SRWParam;

// Seek Parameters
typedef struct
{
	unsigned int	m_file_handle;
	int				m_offset;
	int				m_origin;
} SSeekParam;

// Set Parameters
typedef struct
{
	unsigned int	m_file_handle;
	FileIOVariable	m_variable;
	int				m_value;
} SSetParam;

// Parameter Union
typedef union
{
	SGeneralParam	m_general;
	SInitParam		m_init;
	SOpenParam		m_open;
	SCloseParam		m_close;
	SRWParam		m_rw;
	SSeekParam		m_seek;
	SSetParam		m_set;
} SFileIOParam;

// Make sure this doesn't break the alignment of SSifCmdFileIOReqPacket
typedef struct
{
	// Add union first so it aligns correctly
	SFileIOParam		m_param;
	FileIOCommand		m_command;
} SFileIORequest;


////////////////////////////////////
// Other structures
// 

// SifCmd structures ( keeping them 128-bit aligned )
typedef struct
{
	sceSifCmdHdr	m_header;
	SFileIORequest	m_request;
} SFileIORequestPacket;

// Packet that is sent back to EE after a request is made
typedef struct
{
	sceSifCmdHdr	m_header;
	int				m_return_value;			// Return value
	int				m_done;					// Indicates request completed
	int				m_byte_transfer;		// Number of bytes copied in secondary DMA
	int				m_return_data;			// Additional return data

	// Non-aligned data buffer (for beginning and end of buffers that don't cover a whole cache block)
	int				m_non_aligned_data_size;
	unsigned char	m_non_aligned_data[FILEIO_CACHE_BLOCK_SIZE + 12];	// Need the additional 12 bytes to align this packet
} SFileIOResultPacket;

// Stores the request
typedef struct FileIORequestEntry
{
	SFileIORequest				m_request;
	int							m_request_id;
} SFileIORequestEntry;

#endif	// __SYS_FILE_NGPS_FILEIO_H

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */


================================================
FILE: Code/Sys/File/ngps/FileIO/Makefile
================================================
ifeq ($(wildcard PathDefs),)
PathDefs:
	iop-path-setup
	make all
else
include PathDefs
endif

TOPDIR = /usr/local/sce
INCOPT = -I$(TOPDIR)/common/include -I$(TOPDIR)/iop/install/include

CFLAGS  = $(INCOPT) -I. -Wall -G0 -g -D__LANGUAGE_C
ASFLAGS = $(INCOPT) -G0
RM          = /bin/rm -f

COMPILE.s = $(CC) -xassembler-with-cpp $(ASFLAGS) $(CPPFLAGS) $(TARGET_MACH) -c

#----------- customize section --------------
PROGNAME = fileio

OBJS     = start.o command.o
#ILIBS	= -ilb=$(TOP)lib/iop.ilb 
ILIBS	=
LIBI	= /usr/local/sce/iop/install/lib

#----------- rules --------------
all:	$(PROGNAME).irx

clean:
	rm -f *.o $(PROGNAME).irx *.obj *.map

$(PROGNAME).irx: $(OBJS)
	$(LINK.o)  -o $@ \
		$(OBJS) -L../../../lib -L./ -L$(LIBI) -ilb=cdvdman.ilb

start.o:	fileio.h fileio_iop.h
command.o:	fileio.h fileio_iop.h


================================================
FILE: Code/Sys/File/ngps/FileIO/PathDefs
================================================
AR	 = snarl.exe
AS	 = ps2cc -iop
CC	 = ps2cc -iop
GCC	 = ps2cc -iop
LD	 = ps2cc -iop
NM	 = C:/usr/local/sce/iop/gcc/bin/iop-nm.exe
SIZE	 = iop-size.exe
STRIP	 = C:/usr/local/sce/iop/gcc/bin/iop-strip.exe
RANLIB	 = C:/usr/local/sce/iop/gcc/bin/iop-ranlib.exe
OBJCOPY	 = C:/usr/local/sce/iop/gcc/bin/iop-objcopy.exe
OBJDUMP	 = C:/usr/local/sce/iop/gcc/bin/iop-objdump.exe
IFIXUP	 = iopfixup.exe
ILBGEN	 = C:/usr/local/sce/iop/gcc/bin/ioplibgen.exe
ILBLD	 = C:/usr/local/sce/iop/gcc/mipsel-scei-elfl/bin/ioplibld.exe
ILBDUMP	 = C:/usr/local/sce/iop/gcc/mipsel-scei-elfl/bin/ioplibdump.exe
BIN2OBJ	 = bin2elf.exe


================================================
FILE: Code/Sys/File/ngps/FileIO/command.c
================================================
/* FileIO -- Converted from Sony samples -- Garrett Oct 2002 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "fileio.h"
#include "fileio_iop.h"

// ================================================================

#define MAX(a,b)  (((a) > (b)) ? (a) : (b))
#define MIN(a,b)  (((a) < (b)) ? (a) : (b))

// FileIO prototypes
static int FileIO_GetDescriptor(int memory_destination, int buffer_size);
static int FileIO_FindFileHandleIndex(volatile unsigned int *p_file_handle_index);

static void FileIO_Init(int priority, int buffer_size, short memory_destination, short init_cd_device);
static void FileIO_Open(int sector_num, int file_size, int memory_destination, int buffer_size, int priority, int attributes);
static void FileIO_HostOpen(char *p_filename, int memory_destination, int buffer_size, int priority, int attributes,
							FileIOHostOpen open_type, SFileIORequest *p_cont_request);
static void FileIO_Close(volatile unsigned int *p_file_handle_index);
static void FileIO_Read(SFileIORequest *p_request, volatile unsigned int *p_file_handle_index, void *p_buffer, int size, int non_aligned_start_bytes, int non_aligned_end_bytes);
static void FileIO_Write(SFileIORequest *p_request, volatile unsigned int *p_file_handle_index, void *p_buffer, int size, int non_aligned_start_bytes, int non_aligned_end_bytes);
static void FileIO_Seek(volatile unsigned int *p_file_handle_index, int offset, int origin);
static void FileIO_Set(volatile unsigned int *p_file_handle_index, FileIOVariable variable, int value);

// Debug info
#define SHOW_FILEIO_INFO	0

#if SHOW_FILEIO_INFO
#define ShowFileIOInfo(A...) Kprintf(##A)
#else
#define ShowFileIOInfo(A...) 
#endif

unsigned int gThid = 0;
unsigned int gSem = 0;
unsigned int gCmdSem = 0;

// Defaults
int gDefaultPriority;
int gDefaultBufferSize;
int gDefaultMemoryDestination;

// File handles and buffers
SFileIOHandle	gFileHandle[FILEIO_MAX_HANDLERS];
unsigned char	gFileBuffer[FILEIO_MAX_BUFFERS][FILEIO_BUFFER_SIZE];
unsigned char *	gFileBufferList[FILEIO_MAX_BUFFERS];

int gFreeFileBufferIndex;

// Device status var
static SFileIODevice sFileIODevices[NUM_DEVICE_TYPES];

static SFileIOResultPacket s_result_packet;

static SRequestStatus s_cur_command_status;

static void ServiceCurrentFileHandle(int resend);
static int  StartCdRead(int start_sector, int num_sectors, void *p_buffer);
static void SendNowaitResult();

static int	GetResultPacketSize(SFileIOResultPacket *p_packet);

static void AppendTask(SFileIODevice *p_device, SFileIOTask *p_task);
static void InsertTask(SFileIODevice *p_device, SFileIOTask *p_task);
static void RemoveTask(SFileIODevice *p_device, SFileIOTask *p_task);
static int  AppendContRequest(SFileIOTask *p_task);


//
// Callback
static void cd_callback(int arg)
{
	void *p_ee_addr = NULL, *p_iop_addr = NULL;
	int block_size = 0;
	SFileIODevice *p_device = &sFileIODevices[DEVICE_CD];
	SFileIOHandle *p_file = p_device->mp_file_handle;
	int resend = FALSE;

	// Lame hack to make sure we issued the sceCd call, not some legacy code
	if (!p_device->m_in_use)
	{
		ShowFileIOInfo("Got CD callback even though we didn't issue a command\n");
		return;
	}

	// Wait for previous transfer to finish before we try to read any more (maybe we shouldn't do this in a callback)
	if (p_file->m_flags & FILEIO_HANDLE_PREV_DMA)
	{
		while(sceSifDmaStat(p_file->m_last_dma_id) >= 0)
			;
	}

	// Clear out data transfer size
	s_result_packet.m_non_aligned_data_size = 0;

	switch (p_file->m_cur_function)
	{
	case FILEIO_FUNCTION_READ:
		// Check that we got the correct arg back.  Re-send command if not.
		if ((arg != SCECdFuncRead) && (arg != SCECdFuncSeek))
		{
			resend = TRUE;
			break;
		}

		// We probably won't set this for an IOP memory operation
		s_result_packet.m_byte_transfer = p_file->m_bytes_processing;

		// Figure out if we are done before calling ServiceCurrentFileHandle()
		s_result_packet.m_done = (p_file->m_bytes_to_process == 0);
		s_result_packet.m_header.opt = p_file->m_request_id;
		s_result_packet.m_return_value = p_file->m_return_value;
		s_result_packet.m_return_data = p_file->m_return_data;

		// Do we need to transfer to EE?
		if (p_file->m_flags & FILEIO_HANDLE_EE_MEMORY)
		{
			p_iop_addr = p_device->mp_buffer[p_device->m_buffer_index];
			p_device->m_buffer_index ^= 1;

			p_ee_addr = p_file->mp_dest_buffer;
			block_size = p_file->m_bytes_processing;

			// Check non-alignment
			if (p_file->m_non_aligned_start_bytes)
			{
				s_result_packet.m_non_aligned_data_size = p_file->m_non_aligned_start_bytes;
				memcpy(s_result_packet.m_non_aligned_data, p_iop_addr, p_file->m_non_aligned_start_bytes);

				p_file->m_non_aligned_start_bytes = 0;
				//if (s_result_packet.m_done && p_file->m_non_aligned_end_bytes)
				//{
				//	Kprintf("Have both non-aligned data at beginning and end of buffer\n");
				//}
			}
			else if (s_result_packet.m_done && p_file->m_non_aligned_end_bytes)
			{
				s_result_packet.m_non_aligned_data_size = p_file->m_non_aligned_end_bytes;
				memcpy(s_result_packet.m_non_aligned_data, p_iop_addr + (block_size - p_file->m_non_aligned_end_bytes), p_file->m_non_aligned_end_bytes);
			}
		}

		// Advance dest pointer
		p_file->mp_dest_buffer = p_file->mp_dest_buffer + block_size;

		break;

	case FILEIO_FUNCTION_OPEN:
	case FILEIO_FUNCTION_SEEK:
		s_result_packet.m_byte_transfer = 0;
		s_result_packet.m_done = TRUE;
		s_result_packet.m_return_value = p_file->m_return_value;
		s_result_packet.m_return_data = p_file->m_return_data;
		s_result_packet.m_header.opt = s_cur_command_status.m_request_id;
		p_file->m_cur_function = FILEIO_FUNCTION_IDLE;
		break;

	default:
		// Something went wrong here
		ShowFileIOInfo("Got cd callback from an unexpected function: %d\n", p_file->m_cur_function);
		s_result_packet.m_byte_transfer = 0;
		s_result_packet.m_done = TRUE;
		s_result_packet.m_return_value = -1;
		s_result_packet.m_return_data = -1;
		s_result_packet.m_header.opt = s_cur_command_status.m_request_id;
		break;

	}

	if (!resend)
	{
		p_file->m_flags |= FILEIO_HANDLE_PREV_DMA;
		p_file->m_last_dma_id = isceSifSendCmd(FILEIO_RESULT_COMMAND, &s_result_packet, GetResultPacketSize(&s_result_packet), p_iop_addr, p_ee_addr, block_size);
	}
	ShowFileIOInfo("FileIO cd callback: sent result %d back from %x to %x of size %d with data %x %x %x %x and arg %x error %d resend %d\n", s_result_packet.m_return_value, p_iop_addr, p_ee_addr, block_size,
				   ((unsigned int *) p_iop_addr)[0],
				   ((unsigned int *) p_iop_addr)[1],
				   ((unsigned int *) p_iop_addr)[2],
				   ((unsigned int *) p_iop_addr)[3],
				   arg, sceCdGetError(), resend );

	// Check to see if we need to send out more CD commands
	ServiceCurrentFileHandle(resend);

	// Wake up the dispatch thread
	iSignalSema(gCmdSem);
}

// Can be called from both from outside and within a CD callback
static void ServiceCurrentFileHandle(int resend)
{
	int sectors_to_read;
	SFileIODevice *p_device = &sFileIODevices[DEVICE_CD];
	SFileIOHandle *p_file = p_device->mp_file_handle;

	// Read parameters: need to save in case of resend
	static int cur_sector;
	static int num_sectors;
	static void *p_local_buffer;

	if (resend)
	{
		ShowFileIOInfo("ServiceCurrentFileHandle %x: Resending read of %d sectors starting at %d to %x with %d bytes left\n", p_file, num_sectors, cur_sector, p_local_buffer, p_file->m_bytes_to_process);

		StartCdRead(cur_sector, num_sectors, p_local_buffer);

		return;
	}

	// Right now, this only does something with reads
	if (p_file->m_cur_function != FILEIO_FUNCTION_READ)
	{
		// We're done
		p_file->m_flags &= ~FILEIO_HANDLE_BUSY;
		p_device->m_in_use = FALSE;

		return;
	}

	sectors_to_read = (p_file->m_bytes_to_process + SECTOR_SIZE - 1) / SECTOR_SIZE;

	// Check if we need to seek first
	if (p_file->m_cur_sector != p_device->m_cur_sector)
	{
		p_device->m_cur_sector = p_file->m_cur_sector;
		p_device->m_cur_position = p_file->m_cur_position;

		// We don't need to seek since the read does it for us.
		//sceCdSeek(p_file->m_cur_sector);
		//ShowFileIOInfo("ServiceCurrentFileHandle %x: Seeking to sector %d\n", p_file, p_file->m_cur_sector);
		//return;
	}

	if (sectors_to_read)
	{
		cur_sector = p_file->m_cur_sector;

		if (p_file->m_flags & FILEIO_HANDLE_EE_MEMORY)
		{
			p_local_buffer = p_device->mp_buffer[p_device->m_buffer_index];
			num_sectors = MIN(FILEIO_BUFFER_SECTORS, sectors_to_read);
		}
		else
		{
			// Note that we don't handle the last sector properly, which most likely won't be
			// filled (and would overwrite the output buffer).
			p_local_buffer = p_file->mp_dest_buffer;
			num_sectors = sectors_to_read;
		}

		// Advance the file stats before the read call, since, in theory, the read could call the
		// callback immediately
		p_file->m_cur_sector += num_sectors;
		p_file->m_cur_position += num_sectors * SECTOR_SIZE;
		p_file->m_bytes_processing = MIN(num_sectors * SECTOR_SIZE, p_file->m_bytes_to_process);
		p_file->m_bytes_to_process = MAX(p_file->m_bytes_to_process - p_file->m_bytes_processing, 0);

		ShowFileIOInfo("ServiceCurrentFileHandle %x: Reading %d sectors starting at %d to %x with %d bytes left\n", p_file, num_sectors, cur_sector, p_local_buffer, p_file->m_bytes_to_process);
		p_device->m_cur_sector = p_file->m_cur_sector;
		p_device->m_cur_position = p_file->m_cur_position;

		StartCdRead(cur_sector, num_sectors, p_local_buffer);
	} else {
		// We're done
		p_file->m_flags &= ~FILEIO_HANDLE_BUSY;
		p_device->m_in_use = FALSE;

		// Send message to EE that we are done
	}
}

#define NUM_WAIT_CLICKS 1000

static int StartCdRead(int start_sector, int num_sectors, void *p_buffer)
{
	int result;
	sceCdRMode mode;
	int retries = 0;
	static int wait_read = 0;

	if (wait_read)
	{
		ShowFileIOInfo("Got callback while waiting to resend\n");
		return 0;
	}

	wait_read = 1;

	mode.trycount = 255;
	mode.spindlctrl = SCECdSpinNom;
	mode.datapattern = SCECdSecS2048;
	mode.pad = 0;

	do
	{
		//if (sceCdSync( 1 ))
		//	ShowFileIOInfo("Sync isn't ready before the CD read\n");

		result = sceCdRead(start_sector, num_sectors, p_buffer, &mode);

		if (result == 0)
		{
			int i, j = 0;

			Kprintf("sceCdRead() didn't start.  Returned %d with error %d.  Retrying...\n", result, sceCdGetError());

			sceCdSync( 0 );

			do
			{
				for ( i = 0; i < NUM_WAIT_CLICKS; i++ )
					;
				if ( j++ > NUM_WAIT_CLICKS )
				{
					j = 0;
				}
			} while ( j != 0 );

            while(sceCdDiskReady(0)!=SCECdComplete)
				;
			//sceCdSeek(0);
			//sceCdSync( 0 );
			//result = 1;
			retries++;
		}
	} while ((result == 0) && (retries <= 15));		// Need to eventually abort this if retries get too high

	wait_read = 0;

	return result;
}

// Returns packet size of result, rounded up to a 16-byte chunk
static int GetResultPacketSize(SFileIOResultPacket *p_packet)
{
	unsigned int base_size = ((unsigned int) &(p_packet->m_non_aligned_data[0]) - (unsigned int) p_packet);

	return ((base_size + p_packet->m_non_aligned_data_size) + 0xf) & ~0xf;
}

////////////////////////////////
// Tries to find the real file handle index from the open request ID
static int FileIO_FindFileHandleIndex(volatile unsigned int *p_file_handle_index)
{
	int fd;
	int open_request_id = (int) (*p_file_handle_index & ~FILEIO_TEMP_HANDLE_FLAG);

	for (fd = 0; fd < FILEIO_MAX_HANDLERS; fd++)
	{
		if (gFileHandle[fd].m_open_request_id == open_request_id)
		{
			ShowFileIOInfo("FileIO: converting open request id %d to file handle index %d\n", open_request_id, fd);
			*p_file_handle_index = fd;
			return TRUE;
		}
	}

	ShowFileIOInfo("FileIO: can't convert open request id %d to file handle index\n", open_request_id);
	return FALSE;
}

////////////////////////////////
// Init
static void FileIO_Init(int priority, int buffer_size, short memory_destination, short init_cd_device)
{
	int detected_disk_type;
	int i;

	printf("Starting FileIO_Init\n");

	sFileIODevices[DEVICE_CD].m_in_use = FALSE;
	sFileIODevices[DEVICE_CD].mp_file_handle = NULL;

	// Init drive
	if (init_cd_device)
	{
#if 0		// Doesn't seem to be necessary, since we already do this on the EE
		sceCdInit(SCECdINIT);
		sceCdMmode(SCECdDVD);

		// find the disk type, this might be different from what we intialized to 
		printf("FileIO: sceCdGetDiskType   ");
		detected_disk_type= sceCdGetDiskType();
		switch(detected_disk_type)
		{
			case SCECdIllgalMedia:
			printf("Disk Type= IllgalMedia\n"); break;
			case SCECdPS2DVD:
			printf("Disk Type= PlayStation2 DVD\n"); break;
			case SCECdPS2CD:
			printf("Disk Type= PlayStation2 CD\n"); break;
			case SCECdPS2CDDA:
			printf("Disk Type= PlayStation2 CD with CDDA\n"); break;
			case SCECdPSCD:
			printf("Disk Type= PlayStation CD\n"); break;
			case SCECdPSCDDA:
			printf("Disk Type= PlayStation CD with CDDA\n"); break;
			case SCECdDVDV:
			printf("Disk Type= DVD video\n"); break;
			case SCECdCDDA:
			printf("Disk Type= CD-DA\n"); break;
			case SCECdDETCT:
			printf("Working\n"); break;
			case SCECdNODISC: 
			printf("Disk Type= No Disc\n"); break;
			default:
			printf("Disk Type= OTHER DISK\n"); break;
		}

		// If disk type has changed to a CD, then need to re-initialize it							   
		if (detected_disk_type == SCECdPS2CD)
		{
			#if __DVD_ONLY__
			printf( "*** ERROR - CD Detected, needs DVD.\n" );
			#else
			printf( "reinitializing for Ps2CD\n" );
			sceCdMmode(SCECdCD);
			printf( "done reinitializing\n" );
			#endif		
		}
#endif

		// Set up callback
		sceCdCallback(cd_callback);
	}

	// Set default parameters
	gDefaultPriority			= priority;
	gDefaultBufferSize			= buffer_size;
	gDefaultMemoryDestination	= memory_destination;

	// Init the file handles and buffers
	for (i = 0; i < FILEIO_MAX_HANDLERS; i++)
	{
		gFileHandle[i].m_flags			= 0;		// Neither busy nor reserved
		//gFileHandle[i].mp_buffer[0]		= NULL;
		//gFileHandle[i].mp_buffer[1]		= NULL;
		//gFileHandle[i].m_buffer_index  	= -1;
		gFileHandle[i].mp_dest_buffer	= NULL;
		gFileHandle[i].m_device_type	= DEVICE_UNKNOWN;
		gFileHandle[i].m_buffer_size	= buffer_size;
		gFileHandle[i].m_host_fd		= -1;
		gFileHandle[i].m_priority		= priority;
		gFileHandle[i].m_stream			= FALSE;
		gFileHandle[i].m_start_sector	= 0;
		gFileHandle[i].m_cur_sector		= 0;
		gFileHandle[i].m_cur_position	= 0;
		gFileHandle[i].m_file_size		= 0;
		gFileHandle[i].m_open_request_id= -1;
	}

	for (i = 0; i < FILEIO_MAX_BUFFERS; i++)
	{
		gFileBufferList[i] = gFileBuffer[i];
	}

	gFreeFileBufferIndex = 0;

	// Allocate device buffers
	for (i = 0; i < DEVICE_UNKNOWN; i++)
	{
		if ((gFreeFileBufferIndex + 1) < FILEIO_MAX_BUFFERS)
		{
			// Use buffer size in the future
			sFileIODevices[i].mp_buffer[0] = gFileBufferList[gFreeFileBufferIndex++];
			sFileIODevices[i].mp_buffer[1] = gFileBufferList[gFreeFileBufferIndex++];
			sFileIODevices[i].m_buffer_index = 0;
		}
		else
		{
			// Can't allocate
			printf("Out of fileio memory buffers\n");
			s_cur_command_status.m_request_state = REQUEST_COMPLETE;
			s_cur_command_status.m_return_value = -1;
			return;
		}
	}

	printf("Done FileIO_Init\n");

	s_cur_command_status.m_request_state = REQUEST_COMPLETE;
	s_cur_command_status.m_return_value = TRUE;
	return;
}

////////////////////////////////
// GetDescriptor
static int FileIO_GetDescriptor(int memory_destination, int buffer_size)
{
	int i;

	for (i = 0; i < FILEIO_MAX_HANDLERS; i++)
	{
		if (!(gFileHandle[i].m_flags & FILEIO_HANDLE_RESERVED))
		{
			gFileHandle[i].m_buffer_size = buffer_size;

			if (memory_destination == MEM_EE)
			{
				gFileHandle[i].m_flags |= FILEIO_HANDLE_EE_MEMORY;
#if 0
				if ((gFreeFileBufferIndex + 1) < FILEIO_MAX_BUFFERS)
				{
					// Use buffer size in the future
					gFileHandle[i].mp_buffer[0] = gFileBufferList[gFreeFileBufferIndex++];
					gFileHandle[i].mp_buffer[1] = gFileBufferList[gFreeFileBufferIndex++];
					gFileHandle[i].m_buffer_index = 0;
				}
				else
				{
					// Can't open
					printf("Out of fileio memory buffers\n");
					return -1;
				}
#endif
			}
			else
			{
				gFileHandle[i].m_flags &= ~FILEIO_HANDLE_EE_MEMORY;
#if 0
				gFileHandle[i].mp_buffer[0] = NULL;
				gFileHandle[i].mp_buffer[1] = NULL;
				gFileHandle[i].m_buffer_index = -1;
#endif
			}

			gFileHandle[i].m_flags |= FILEIO_HANDLE_RESERVED;

			return i;
		}
	}

	// Couldn't find a free descriptor
	printf("Out of fileio file descriptors\n");
	return -1;

}

////////////////////////////////
// Open
static void FileIO_Open(int sector_num, int file_size, int memory_destination, int buffer_size, int priority, int attributes)
{
	int fd = FileIO_GetDescriptor(memory_destination, buffer_size);

	if (fd >= 0)
	{
		// Init the handle itself
		gFileHandle[fd].m_device_type = DEVICE_CD;
		gFileHandle[fd].mp_dest_buffer = NULL;
		gFileHandle[fd].m_host_fd = -1;
		gFileHandle[fd].m_priority = priority;
		gFileHandle[fd].m_stream = FALSE;
		gFileHandle[fd].m_start_sector = sector_num;
		gFileHandle[fd].m_cur_sector = sector_num;
		gFileHandle[fd].m_cur_position = 0;
		gFileHandle[fd].m_file_size = file_size;
		gFileHandle[fd].m_open_request_id= s_cur_command_status.m_request_id;
		gFileHandle[fd].mp_blocked_request = NULL;
		gFileHandle[fd].m_flags &= ~FILEIO_HANDLE_BUSY;
		gFileHandle[fd].m_cur_function = FILEIO_FUNCTION_IDLE;

		// Don't do seek, just return
		gFileHandle[fd].m_return_value = fd;
		gFileHandle[fd].m_return_data = file_size;
		s_cur_command_status.m_request_state = REQUEST_COMPLETE;
		s_cur_command_status.mp_file_handle = &(gFileHandle[fd]);
		s_cur_command_status.m_return_value = fd;
		s_cur_command_status.m_return_data = file_size;
		ShowFileIOInfo("Opened file using handle %d\n", fd);
	} else {
		s_cur_command_status.m_request_state = REQUEST_COMPLETE;
		s_cur_command_status.m_return_value = -1;
		return;
	}
}

////////////////////////////////
// HostOpen
static void FileIO_HostOpen(char *p_filename, int memory_destination, int buffer_size, int priority, int attributes,
							FileIOHostOpen open_type, SFileIORequest *p_cont_request)
{
	int fd, host_fd;
	char full_filename[FILEIO_MAX_FILENAME_SIZE * 2];

	strcpy(full_filename, p_filename);
	if (open_type == FILEIO_HOSTOPEN_EXTEND_NAME)
	{
		if (p_cont_request)
		{
			strcat(full_filename, p_cont_request->m_param.m_open.m_file.m_filename);
			ShowFileIOInfo("FileIO HostOpen: opening extended filename %s\n", full_filename);
		}
		else
		{
			ShowFileIOInfo("FileIO HostOpen: waiting for rest of filename for %s\n", p_filename);
			s_cur_command_status.m_request_state = REQUEST_OPEN;
			return;
		}
	}

	host_fd = open(full_filename, O_RDONLY);
	if (host_fd >= 0)
	{
		// Find file size
		int file_size = lseek(host_fd, 0, SEEK_END);
		lseek(host_fd, 0, SEEK_SET);

		fd = FileIO_GetDescriptor(memory_destination, buffer_size);

		if (fd >= 0)
		{
			// Init the handle itself
			gFileHandle[fd].m_device_type = DEVICE_HOST;
			gFileHandle[fd].mp_dest_buffer = NULL;
			gFileHandle[fd].m_priority = priority;
			gFileHandle[fd].m_stream = FALSE;
			gFileHandle[fd].m_start_sector = -1;
			gFileHandle[fd].m_cur_sector = -1;
			gFileHandle[fd].m_cur_position = 0;
			gFileHandle[fd].m_file_size = file_size;
			gFileHandle[fd].m_open_request_id= s_cur_command_status.m_request_id;
			gFileHandle[fd].mp_blocked_request = NULL;
			gFileHandle[fd].m_flags &= ~FILEIO_HANDLE_BUSY;
			gFileHandle[fd].m_cur_function = FILEIO_FUNCTION_IDLE;

			// Do a open
			gFileHandle[fd].m_host_fd = host_fd;

			gFileHandle[fd].m_return_value = fd;
			gFileHandle[fd].m_return_data = file_size;

			s_cur_command_status.m_request_state = REQUEST_COMPLETE;
			s_cur_command_status.mp_file_handle = &(gFileHandle[fd]);
			s_cur_command_status.m_return_value = fd;
			s_cur_command_status.m_return_data = file_size;
			return;
		} else {
			printf("Can't get free descriptor for host open of %s\n", full_filename);
			close(host_fd);
		}
	} else {
		printf("Can't open file %s on host\n", full_filename);
	}

	s_cur_command_status.m_request_state = REQUEST_COMPLETE;
	s_cur_command_status.m_return_value = -1;
	return;
}

////////////////////////////////
// Close
static void FileIO_Close(volatile unsigned int *p_file_handle_index)
{
	SFileIOHandle *p_file;
	//int i, j;

	// See if we have the correct index, and if we can't get it, return
	if ((*p_file_handle_index & FILEIO_TEMP_HANDLE_FLAG) && !FileIO_FindFileHandleIndex(p_file_handle_index))
	{
		s_cur_command_status.m_request_state = REQUEST_OPEN;
		return;
	}

	p_file = &(gFileHandle[*p_file_handle_index]);

	if (p_file->m_flags & FILEIO_HANDLE_RESERVED)
	{
		s_cur_command_status.mp_file_handle = p_file;

		// Can't close if we're busy
		if (p_file->m_flags & FILEIO_HANDLE_BUSY)
		{
			s_cur_command_status.m_request_state = REQUEST_OPEN;
			return;
		}

		// Check if a host handle
		if (p_file->m_host_fd >= 0)
		{
			close(p_file->m_host_fd);
		}

#if 0
		// Check if buffer needs freeing
		if (p_file->mp_buffer[0])
		{
			for (i = 0; i < FILEIO_MAX_BUFFERS; i++)
			{
				for (j = 0; j < 2; j++)
				{
					if (p_file->mp_buffer[j] == gFileBufferList[i])
					{
						// Found it, exchange it with last used buffer
						gFileBufferList[i] = gFileBufferList[--gFreeFileBufferIndex];
						gFileBufferList[gFreeFileBufferIndex] = p_file->mp_buffer[j];
						p_file->mp_buffer[j] = NULL;
					}
				}
			}
			p_file->m_buffer_index = -1;
		}
#endif

		p_file->m_flags &= ~FILEIO_HANDLE_RESERVED;

		ShowFileIOInfo("CLOSE: handle %d open and not busy\n", *p_file_handle_index);
		s_cur_command_status.m_request_state = REQUEST_COMPLETE;
		s_cur_command_status.m_return_value = TRUE;
		return;
	}

	// Handle wasn't open
	ShowFileIOInfo("CLOSE: handle %d not open\n", *p_file_handle_index);
	s_cur_command_status.m_request_state = REQUEST_COMPLETE;
	s_cur_command_status.m_return_value = FALSE;
	return;
}

////////////////////////////////
// Read
static void FileIO_Read(SFileIORequest *p_request, volatile unsigned int *p_file_handle_index, void *p_buffer, int size, int non_aligned_start_bytes, int non_aligned_end_bytes)
{
	SFileIOHandle *p_file;
	int host_read;
	int bytes_to_read;

	// See if we have the correct index, and if we can't get it, return
	if ((*p_file_handle_index & FILEIO_TEMP_HANDLE_FLAG) && !FileIO_FindFileHandleIndex(p_file_handle_index))
	{
		s_cur_command_status.m_request_state = REQUEST_OPEN;
		return;
	}

	p_file = &(gFileHandle[*p_file_handle_index]);
	host_read = (p_file->m_host_fd >= 0);

	s_cur_command_status.mp_file_handle = p_file;

	// Can't add additional read if we're already busy
	if ((p_file->m_flags & FILEIO_HANDLE_BUSY) && !(p_file->mp_blocked_request && (p_file->mp_blocked_request == p_request)))
	{
		s_cur_command_status.m_request_state = REQUEST_OPEN;
		return;
	}

	// Check to make sure device is free
	if (host_read)
	{
		// Nothing to check for now
	}
	else
	{
		if (sFileIODevices[DEVICE_CD].m_in_use)
		{
			// Mark file handle as blocked
			p_file->mp_blocked_request = p_request;
			p_file->m_flags |= FILEIO_HANDLE_BUSY;

			s_cur_command_status.m_request_state = REQUEST_OPEN;
			return;
		}
		else
		{
			// Clear blocked status
			p_file->mp_blocked_request = NULL;
			p_file->m_flags &= ~FILEIO_HANDLE_BUSY;
		}
	}

	// We won't support non sector aligned reads now
	if (!host_read && (p_file->m_cur_position & (SECTOR_SIZE - 1)))
	{
		printf("Can't read from position %d because it isn't on a sector boundary\n", p_file->m_cur_position);
		p_file->m_cur_function = FILEIO_FUNCTION_IDLE;
		s_cur_command_status.m_request_state = REQUEST_COMPLETE;
		s_cur_command_status.m_return_value = -1;
		return;
	}

	p_file->m_bytes_to_process = MIN(size, p_file->m_file_size - p_file->m_cur_position);
	p_file->m_bytes_processing = 0;
	p_file->m_non_aligned_start_bytes = non_aligned_start_bytes;
	p_file->m_non_aligned_end_bytes = non_aligned_end_bytes;
	p_file->m_request_id = s_cur_command_status.m_request_id;

	// Set destination buffer (EE or IOP address) and size
	p_file->mp_dest_buffer = p_buffer;
	bytes_to_read = p_file->m_bytes_to_process;

	// Do the actual read
	if (host_read)
	{
		SFileIODevice *p_device = &sFileIODevices[DEVICE_HOST];

		// We should really be looking for the old one and making sure it is done
		p_file->m_flags &= ~FILEIO_HANDLE_PREV_DMA;

		// Just do the read here until we set up a separate thread
		while (p_file->m_bytes_to_process)
		{
			void *p_ee_addr = NULL, *p_iop_addr = NULL;
			int block_size = 0;
			int num_bytes;
			void *p_local_buffer;

			if (p_file->m_flags & FILEIO_HANDLE_EE_MEMORY)
			{
				p_local_buffer = p_device->mp_buffer[p_device->m_buffer_index];
				num_bytes = MIN(FILEIO_BUFFER_SIZE, p_file->m_bytes_to_process);
			}
			else
			{
				p_local_buffer = p_file->mp_dest_buffer;
				num_bytes = p_file->m_bytes_to_process;
			}

			// Advance the file stats before the read call, since, in theory, the read could call the
			// callback immediately
			p_file->m_cur_position += num_bytes;
			p_file->m_bytes_processing = num_bytes;
			p_file->m_bytes_to_process = p_file->m_bytes_to_process - p_file->m_bytes_processing;

			ShowFileIOInfo("Host read %x: Reading %d bytes to %x with %d bytes left\n", p_file, num_bytes, p_local_buffer, p_file->m_bytes_to_process);
			read(p_file->m_host_fd, p_local_buffer, num_bytes);

			// Wait for previous transfer to finish before we try to read any more (maybe we shouldn't do this in a callback)
			if (p_file->m_flags & FILEIO_HANDLE_PREV_DMA)
			{
				while(sceSifDmaStat(p_file->m_last_dma_id) >= 0)
					;
			}

			// We probably won't set this for an IOP memory operation
			s_result_packet.m_byte_transfer = p_file->m_bytes_processing;

			// Figure out if we are done before calling ServiceCurrentFileHandle()
			s_result_packet.m_done = (p_file->m_bytes_to_process == 0);
			s_result_packet.m_header.opt = p_file->m_request_id;
			s_result_packet.m_return_value = bytes_to_read;
			s_result_packet.m_non_aligned_data_size = 0;

			// Do we need to transfer to EE?
			if (p_file->m_flags & FILEIO_HANDLE_EE_MEMORY)
			{
				p_iop_addr = p_local_buffer;
				p_device->m_buffer_index ^= 1;

				p_ee_addr = p_file->mp_dest_buffer;
				block_size = p_file->m_bytes_processing;

				// Check non-alignment
				if (p_file->m_non_aligned_start_bytes)
				{
					s_result_packet.m_non_aligned_data_size = p_file->m_non_aligned_start_bytes;
					memcpy(s_result_packet.m_non_aligned_data, p_iop_addr, p_file->m_non_aligned_start_bytes);

					p_file->m_non_aligned_start_bytes = 0;
				}
				else if (s_result_packet.m_done && p_file->m_non_aligned_end_bytes)
				{
					s_result_packet.m_non_aligned_data_size = p_file->m_non_aligned_end_bytes;
					memcpy(s_result_packet.m_non_aligned_data, p_iop_addr + (block_size - p_file->m_non_aligned_end_bytes), p_file->m_non_aligned_end_bytes);
				}
			}

			// Advance dest pointer
			p_file->mp_dest_buffer = p_file->mp_dest_buffer + block_size;

			p_file->m_flags |= FILEIO_HANDLE_PREV_DMA;
			p_file->m_last_dma_id = sceSifSendCmd(FILEIO_RESULT_COMMAND, &s_result_packet, GetResultPacketSize(&s_result_packet), p_iop_addr, p_ee_addr, block_size);
			ShowFileIOInfo("FileIO host read: sent result %d back from %x to %x of size %d\n", s_result_packet.m_return_value, p_iop_addr, p_ee_addr, block_size);
		}

		s_cur_command_status.m_request_state = REQUEST_PROCESSING;		// Don't want to send another result packet
		p_file->m_return_value = bytes_to_read;
	}
	else
	{
		// We should really be looking for the old one and making sure it is done
		p_file->m_flags &= ~FILEIO_HANDLE_PREV_DMA;

		// Mark that we are using the CD
		p_file->m_flags |= FILEIO_HANDLE_BUSY;
		sFileIODevices[DEVICE_CD].m_in_use = TRUE;
		sFileIODevices[DEVICE_CD].mp_file_handle = p_file;

		p_file->m_cur_function = FILEIO_FUNCTION_READ;
		p_file->m_return_value = p_file->m_bytes_to_process;
		s_cur_command_status.m_request_state = REQUEST_PROCESSING;

		ServiceCurrentFileHandle(FALSE);
	}

	return;
}

////////////////////////////////
// Write
static void FileIO_Write(SFileIORequest *p_request, volatile unsigned int *p_file_handle_index, void *p_buffer, int size, int non_aligned_start_bytes, int non_aligned_end_bytes)
{
	SFileIOHandle *p_file;

	// See if we have the correct index, and if we can't get it, return
	if ((*p_file_handle_index & FILEIO_TEMP_HANDLE_FLAG) && !FileIO_FindFileHandleIndex(p_file_handle_index))
	{
		s_cur_command_status.m_request_state = REQUEST_OPEN;
		return;
	}

	p_file = &(gFileHandle[*p_file_handle_index]);

	s_cur_command_status.mp_file_handle = p_file;

	// Can't write if we're already busy
	if ((p_file->m_flags & FILEIO_HANDLE_BUSY) && !(p_file->mp_blocked_request && (p_file->mp_blocked_request == p_request)))
	{
		s_cur_command_status.m_request_state = REQUEST_OPEN;
		return;
	}

	s_cur_command_status.m_request_state = REQUEST_COMPLETE;
	s_cur_command_status.m_return_value = -1;
	return;
}

////////////////////////////////
// Seek
static void FileIO_Seek(volatile unsigned int *p_file_handle_index, int offset, int origin)
{
	int new_position;
	SFileIOHandle *p_file;

	// See if we have the correct index, and if we can't get it, return
	if ((*p_file_handle_index & FILEIO_TEMP_HANDLE_FLAG) && !FileIO_FindFileHandleIndex(p_file_handle_index))
	{
		s_cur_command_status.m_request_state = REQUEST_OPEN;
		return;
	}

	p_file = &(gFileHandle[*p_file_handle_index]);

	s_cur_command_status.mp_file_handle = p_file;

	// Can't start seek if we're already busy
	if (p_file->m_flags & FILEIO_HANDLE_BUSY)
	{
		s_cur_command_status.m_request_state = REQUEST_OPEN;
		return;
	}

	// Do the adjustments
	switch (origin)
	{
	case SEEK_CUR:
		new_position = p_file->m_cur_position + offset;
		break;

	case SEEK_SET:
		new_position = offset;
		break;

	case SEEK_END:
		new_position = p_file->m_file_size - offset;
		break;

	default:
		p_file->m_cur_function = FILEIO_FUNCTION_IDLE;
		s_cur_command_status.m_request_state = REQUEST_COMPLETE;
		s_cur_command_status.m_return_value = -1;
		return;
	}

	// Check if before beginning
	if (new_position < 0)
	{
		new_position = 0;
	}

	// Check if past EOF
	if (new_position > p_file->m_file_size)
	{
		new_position = p_file->m_file_size;
	}

	if (p_file->m_host_fd >= 0)
	{
		// Device ready
		p_file->m_cur_position = new_position;

		// Do the seek
		lseek(p_file->m_host_fd, p_file->m_cur_position, SEEK_SET);

		p_file->m_return_value = p_file->m_cur_position;
		s_cur_command_status.m_request_state = REQUEST_COMPLETE;
		s_cur_command_status.m_return_value = p_file->m_cur_position;
	}
	else
	{
		// Change position
		p_file->m_cur_position = new_position;

		// Also set the new current sector
		p_file->m_cur_sector = p_file->m_start_sector + (p_file->m_cur_position / SECTOR_SIZE);

		p_file->m_return_value = p_file->m_cur_position;
		s_cur_command_status.m_request_state = REQUEST_COMPLETE;
		s_cur_command_status.m_return_value = p_file->m_cur_position;
	}

	return;
}

static void FileIO_Set(volatile unsigned int *p_file_handle_index, FileIOVariable variable, int value)
{
	SFileIOHandle *p_file;

	// See if we have the correct index, and if we can't get it, return
	if ((*p_file_handle_index & FILEIO_TEMP_HANDLE_FLAG) && !FileIO_FindFileHandleIndex(p_file_handle_index))
	{
		s_cur_command_status.m_request_state = REQUEST_OPEN;
		return;
	}

	p_file = &(gFileHandle[*p_file_handle_index]);

	s_cur_command_status.mp_file_handle = p_file;

	// Can't change parameters if we're already busy
	if (p_file->m_flags & FILEIO_HANDLE_BUSY)
	{
		s_cur_command_status.m_request_state = REQUEST_OPEN;
		return;
	}

	s_cur_command_status.m_request_state = REQUEST_COMPLETE;

	switch (variable)
	{
	case FILEIO_VAR_PRIORITY:
		p_file->m_priority = value;
		break;

	case FILEIO_VAR_BUFFER_SIZE:
		p_file->m_buffer_size = value;
		break;

	case FILEIO_VAR_STREAM:
		p_file->m_stream = value;
		break;

	case FILEIO_VAR_DESTINATION:
		// Not sure how to handle this one yet, since buffers may need to be acquired or freed
		if (value == MEM_EE)
		{
			p_file->m_flags |= FILEIO_HANDLE_EE_MEMORY;
		}
		else
		{
			p_file->m_flags &= ~FILEIO_HANDLE_EE_MEMORY;
		}
		break;

	default:
		s_cur_command_status.m_return_value = FALSE;
		return;
	}

	s_cur_command_status.m_return_value = TRUE;
	return;
}

// EzADPCM_INIT
int AdpcmInit( int pIopBuf )
{
#if 0
	int i;
	if ( gEECommand & PCMSTATUS_INITIALIZED )
	{
		return ( 0 );
	}

    if ( gSem == 0 )
	{
		struct SemaParam sem;
		sem.initCount = 0;
		sem.maxCount = 1;
		sem.attr = AT_THFIFO;
		gSem = CreateSema (&sem);
    }
    if (gThid == 0)
	{
		struct ThreadParam param;
		param.attr         = TH_C;
		param.entry        = RefreshStreams;
		param.initPriority = BASE_priority-3;
		param.stackSize    = 0x800; // was 800
		param.option = 0;
		gThid = CreateThread (¶m);
		printf( "EzADPCM: create thread ID= %d\n", gThid );
		StartThread (gThid, (u_long) NULL);
    }

	memset( gStreamInfo, 0, sizeof( struct StreamInfo ) * NUM_STREAMS );
	memset( &gMusicInfo, 0, sizeof( struct StreamInfo ) );
	
	gpIopBuf = pIopBuf;
	gEECommand |= PCMSTATUS_INITIALIZED;
	//printf( "PCM Irx iop buf %d\n", gpIopBuf );
	//Dbug_Printf( "PCM irx is initialized\n" );
#endif
    return gThid;
}

// EzADPCM_QUIT
void AdpcmQuit( void )
{
    DeleteThread (gThid);
    gThid = 0;
#if 0
    DeleteSema (gSem);
    gSem = 0;
#endif
    return;
}

/* internal */
int RefreshStreams( int status )
{
	//int i;

    while ( 1 )
	{
		WaitSema(gSem);
#if 0
		for ( i = 0; i < NUM_STREAMS; i++ )
		{
			CheckStream( i );
		}
		CheckMusic( );

		for ( i = 0; i < NUM_STREAMS; i++ )
		{
			while ( gStreamInfo[ i ].update )
			{
				gStreamInfo[ i ].update--;
				UpdateStream( i );
			}
		}
		while ( gMusicInfo.update )
		{
			gMusicInfo.update--;
			UpdateMusic( );
		}
		if ( gMusicInfo.volumeSet )
		{
			gMusicInfo.volumeSet = 0;
			AdpcmSetMusicVolumeDirect( gMusicInfo.volume );
		}
		for ( i = 0; i < NUM_STREAMS; i++ )
		{
			if ( gStreamInfo[ i ].volumeSet )
			{
				gStreamInfo[ i ].volumeSet = 0;
				AdpcmSetStreamVolumeDirect( gStreamInfo[ i ].volume, i );
			}
		}
#endif
    }
    return 0;
}

static ERequestState dispatch(volatile SFileIOTask *p_request_task)
{ 
	volatile SFileIORequestEntry *p_request_entry = &(p_request_task->m_entry);
	volatile SFileIORequest *p_request = &(p_request_entry->m_request);
	SFileIORequest *p_cont_request = (p_request_task->mp_cont) ? (&p_request_task->mp_cont->m_entry.m_request) : NULL;

	// Assume command will need to be completed asynchronously
	s_cur_command_status.m_request_state = REQUEST_OPEN;
	s_cur_command_status.m_request_id = p_request_entry->m_request_id;
	s_cur_command_status.mp_file_handle = NULL;

    switch ( p_request->m_command )
	{
	case FILEIO_INIT:
		FileIO_Init(p_request->m_param.m_init.m_priority,
					p_request->m_param.m_init.m_buffer_size,
					p_request->m_param.m_init.m_memory_dest,
					p_request->m_param.m_init.m_init_cd_device);
		break;
		
	case FILEIO_OPEN:
		if (p_request->m_param.m_open.m_host_read)
		{
			FileIO_HostOpen((char *) p_request->m_param.m_open.m_file.m_filename,
							p_request->m_param.m_open.m_memory_dest,
							p_request->m_param.m_open.m_buffer_size,
							p_request->m_param.m_open.m_priority,
							p_request->m_param.m_open.m_attributes,
							p_request->m_param.m_open.m_host_read,
							p_cont_request);
		} else {
			FileIO_Open(p_request->m_param.m_open.m_file.m_raw_name.m_sector_number, 
						p_request->m_param.m_open.m_file.m_raw_name.m_file_size,
						p_request->m_param.m_open.m_memory_dest,
						p_request->m_param.m_open.m_buffer_size,
						p_request->m_param.m_open.m_priority,
						p_request->m_param.m_open.m_attributes);
		}
		break;

	case FILEIO_CLOSE:
		FileIO_Close(&p_request->m_param.m_close.m_file_handle);
		break;

	case FILEIO_READ:
		FileIO_Read((SFileIORequest *) p_request, &p_request->m_param.m_rw.m_file_handle, p_request->m_param.m_rw.m_buffer,
					p_request->m_param.m_rw.m_size, p_request->m_param.m_rw.m_non_aligned_start_bytes,
					p_request->m_param.m_rw.m_non_aligned_end_bytes);
		break;

	case FILEIO_WRITE:
		FileIO_Write((SFileIORequest *) p_request, &p_request->m_param.m_rw.m_file_handle, p_request->m_param.m_rw.m_buffer,
					 p_request->m_param.m_rw.m_size, p_request->m_param.m_rw.m_non_aligned_start_bytes,
					 p_request->m_param.m_rw.m_non_aligned_end_bytes);
		break;

	case FILEIO_SEEK:
		FileIO_Seek(&p_request->m_param.m_seek.m_file_handle, p_request->m_param.m_seek.m_offset,
					p_request->m_param.m_seek.m_origin);
		break;

	case FILEIO_SET:
		FileIO_Set(&p_request->m_param.m_set.m_file_handle, p_request->m_param.m_set.m_variable,
				   p_request->m_param.m_set.m_value);
		break;

	case FILEIO_CONT:
		s_cur_command_status.m_request_state = REQUEST_APPEND;
		break;

	default:
		ERROR ("FileIO driver error: unknown command %d \n", p_request->m_command);
		s_cur_command_status.m_return_value = -1;
		break;
    }

	// Copy file handle in case we need it later
	p_request_task->mp_file_handle = s_cur_command_status.mp_file_handle;

	switch (s_cur_command_status.m_request_state)
	{
	case REQUEST_OPEN:
		// This isn't an error or warning anymore: it's now normal
		//ERROR (("FileIO driver error: request %d should not end in a open request state\n", p_request_entry->m_request_id));
		//printf("FileIO driver warning: request %d with command %d ended in a open request state\n", p_request_entry->m_request_id, p_request->m_command);
		break;

	case REQUEST_PROCESSING:
		break;

	case REQUEST_APPEND:
		break;

	case REQUEST_COMPLETE:
		// Send result back to EE now if command completed
		SendNowaitResult();
		break;
	}

    return s_cur_command_status.m_request_state;
}


static sceSifCmdData cmdbuffer[FILEIO_NUM_COMMAND_HANDLERS];

#define NUM_FILEIO_REQUESTS	(16)

// Note these can be changed in an interrupt
static volatile SFileIORequestEntry sFileIORequestArray[NUM_FILEIO_REQUESTS];
static volatile int sFirstFileIORequest;		// Interrupt only reads this value.
static volatile	int sFreeFileIORequest;			// This is the main variable that changes in the interrupt

// The task entries
static SFileIOTask sFileIOTaskArray[FILEIO_MAX_TASKS];
static SFileIOTask *spTaskFreeList = NULL;

static void request_callback(void *p, void *q);

int fileio_command_loop( void )
{
	int oldStat;
	int i;
	EDeviceType cur_device;

	// Create semaphore to signal command came in
	struct SemaParam sem;
	sem.initCount = 0;
	sem.maxCount = 1;
	sem.attr = AT_THFIFO;
	gCmdSem = CreateSema(&sem);

	// Init the stream queue
	sFirstFileIORequest = 0;
	sFreeFileIORequest = 0;

	// Init free task list
	spTaskFreeList = NULL;
	for (i = 0; i < FILEIO_MAX_TASKS; i++)
	{
		sFileIOTaskArray[i].mp_file_handle = NULL;

		// Add to beginning of list
		sFileIOTaskArray[i].mp_next = spTaskFreeList;
		spTaskFreeList = &sFileIOTaskArray[i];
	}

	// Init devices
	for (i = 0; i < NUM_DEVICE_TYPES; i++)
	{
		sFileIODevices[i].m_in_use = FALSE;
		sFileIODevices[i].m_waiting_callback = FALSE;
		sFileIODevices[i].mp_file_handle = NULL;
		sFileIODevices[i].mp_task_list = NULL;
		sFileIODevices[i].m_cur_position = -1;
		sFileIODevices[i].m_cur_sector = -1;
		sFileIODevices[i].mp_buffer[0] = NULL;
		sFileIODevices[i].mp_buffer[1] = NULL;
		sFileIODevices[i].m_buffer_index = -1;
	}

	sceSifInitRpc(0);

	// set local buffer & functions
	CpuSuspendIntr(&oldStat);

	// SIFCMD
	sceSifSetCmdBuffer( &cmdbuffer[0], FILEIO_NUM_COMMAND_HANDLERS);

	sceSifAddCmdHandler(FILEIO_REQUEST_COMMAND, (void *) request_callback, NULL );

	CpuResumeIntr(oldStat);

	// The loop
	while (1) {
		//ShowFileIOInfo("waiting for command semaphore\n");
		WaitSema(gCmdSem);		// Get signal from callback
		//ShowFileIOInfo("got command semaphore\n");

		// Move new requests into schedule list
		// Note that FreeStreamRequest can change in the interrupt at any time
		// Also, FirstStreamRequest is examined in the interrupt, but just to make sure we don't overflow the buffer
		while (sFreeFileIORequest != sFirstFileIORequest)
		{
			volatile SFileIORequestEntry *p_request = &(sFileIORequestArray[sFirstFileIORequest]);
			SFileIOTask *p_new_task;
			ShowFileIOInfo("FileIO: got request id %d with command %x\n", p_request->m_request_id, p_request->m_request.m_command);

			// Get a new task entry
			p_new_task = spTaskFreeList;
			spTaskFreeList = p_new_task->mp_next;
			p_new_task->mp_next = NULL;

			// Copy data
			p_new_task->m_entry = *p_request;
			p_new_task->mp_file_handle = NULL;
			p_new_task->mp_cont = NULL;

			// And put onto unknown device task list
			AppendTask(&sFileIODevices[DEVICE_UNKNOWN], p_new_task);

			// Increment request index; Note that interrupt can look at this
			sFirstFileIORequest = (sFirstFileIORequest + 1) % NUM_FILEIO_REQUESTS;
		}

		// Process device tasks
		for (cur_device = DEVICE_CD; cur_device < NUM_DEVICE_TYPES; cur_device++)
		{
			SFileIODevice *p_device = &sFileIODevices[cur_device];
			SFileIOTask *p_task;

			// Skip if busy
			if (p_device->m_in_use)
			{
				continue;
			}

			p_task = p_device->mp_task_list;
			while (p_task)
			{
				ERequestState ret;
				SFileIOTask *p_dispatch_task;

				p_dispatch_task = p_task;

				ShowFileIOInfo("FileIO: doing request id %d with command %x\n", p_dispatch_task->m_entry.m_request_id, p_dispatch_task->m_entry.m_request.m_command);
				ret = dispatch(p_dispatch_task);

				p_task = p_task->mp_next;

				switch (ret)
				{
				case REQUEST_OPEN:
					// Move to other device
					if (p_dispatch_task->mp_file_handle && (p_dispatch_task->mp_file_handle->m_device_type != cur_device))
					{
						RemoveTask(p_device, p_dispatch_task);
						InsertTask(&sFileIODevices[p_dispatch_task->mp_file_handle->m_device_type], p_dispatch_task);
					}
					break;

				case REQUEST_APPEND:
					// Add the request to an existing request
					if (AppendContRequest(p_dispatch_task))
					{
						ShowFileIOInfo("***************** FileIO: appended request id %d of command %x\n", p_dispatch_task->m_entry.m_request_id, p_dispatch_task->m_entry.m_request.m_command);
						RemoveTask(p_device, p_dispatch_task);
						SignalSema(gCmdSem);		// So the other task can be retried
					}
					break;

				case REQUEST_PROCESSING:
				case REQUEST_COMPLETE:
					RemoveTask(p_device, p_dispatch_task);
					if (p_dispatch_task->mp_cont)			// Check if we have a continuation request
					{
						p_dispatch_task->mp_cont->mp_next = spTaskFreeList;
						spTaskFreeList = p_dispatch_task->mp_cont;
						p_dispatch_task->mp_cont = NULL;
					}
					p_dispatch_task->mp_next = spTaskFreeList;
					spTaskFreeList = p_dispatch_task;

					break;
				}
			}
		}
	}

    return 0;
}

static void SendNowaitResult()
{
	static SFileIOResultPacket fileIOResult;

	static unsigned int last_cmd_id = 0;
	static int did_dma_send = FALSE;

	// Send the result back
	if (did_dma_send && last_cmd_id >= 0)	// Wait for previous send to complete (it should already be done, though)
	{
	   while(sceSifDmaStat(last_cmd_id) >= 0)
		   Kprintf("Waiting for DMA\n");
	}

	// Gotta copy the id into SStreamRequest
	fileIOResult.m_header.opt = s_cur_command_status.m_request_id;	// Copy id
	fileIOResult.m_return_value = s_cur_command_status.m_return_value;
	fileIOResult.m_return_data = s_cur_command_status.m_return_data;
	fileIOResult.m_done = TRUE;
	fileIOResult.m_non_aligned_data_size = 0;
	did_dma_send = TRUE;
	last_cmd_id = sceSifSendCmd(FILEIO_RESULT_COMMAND, &fileIOResult, GetResultPacketSize(&fileIOResult), 0, 0, 0);
	ShowFileIOInfo("FileIO: sent nowait result id %d back\n", s_cur_command_status.m_request_id);
}

static void request_callback(void *p, void *q)
{
	SFileIORequestPacket *h = (SFileIORequestPacket *) p;

	// Check to make sure we can add
	int nextFreeReq = (sFreeFileIORequest + 1) % NUM_FILEIO_REQUESTS;
	if (nextFreeReq == sFirstFileIORequest)
	{
		// We can't allow a request to be ignored.  We must abort
		ERROR(("******************************** FileIO driver error: too many requests ********************************\n" ));
	}

	ShowFileIOInfo("FileIO: received request id %d with command %x\n", h->m_header.opt, h->m_request.m_command);

	// Copy the request into the reuest queue
	sFileIORequestArray[sFreeFileIORequest].m_request = h->m_request;
	sFileIORequestArray[sFreeFileIORequest].m_request_id = h->m_header.opt;
	sFreeFileIORequest = nextFreeReq;

	// And wake up the dispatch thread
	iSignalSema(gCmdSem);
}

// Adds p_task to the end of the device task list
static void AppendTask(SFileIODevice *p_device, SFileIOTask *p_task)
{
	SFileIOTask *p_task_list = p_device->mp_task_list;
	if (p_task_list)
	{
		// Find end of list
		while (p_task_list->mp_next)
		{
			p_task_list = p_task_list->mp_next;
		}

		p_task->mp_next = p_task_list->mp_next;
		p_task_list->mp_next = p_task;
	}
	else
	{
		// Empty list
		p_device->mp_task_list = p_task;
		p_task->mp_next = NULL;
	}
}

#define GetPriority(x) ((x) ? (x)->m_priority : gDefaultPriority)

// Inserts p_task into the device task list sorted by priority
static void InsertTask(SFileIODevice *p_device, SFileIOTask *p_task)
{
	SFileIOTask *p_task_node = p_device->mp_task_list;
	SFileIOTask *p_prev_node = NULL;

	int priority = GetPriority(p_task->mp_file_handle);

	// Find point in list
	while (p_task_node)
	{
		if (priority < GetPriority(p_task_node->mp_file_handle))
		{
			break;
		}
		p_prev_node = p_task_node;
		p_task_node = p_task_node->mp_next;
	}

	// Insert
	if (p_prev_node)
	{
		p_task->mp_next = p_task_node;
		p_prev_node->mp_next = p_task;
	}
	else
	{
		// Head of list
		p_task->mp_next = p_device->mp_task_list;
		p_device->mp_task_list = p_task;
	}
}

// Removes p_task from the device task list
static void RemoveTask(SFileIODevice *p_device, SFileIOTask *p_task)
{
	SFileIOTask *p_task_node = p_device->mp_task_list;

	if (p_task == p_task_node)
	{
		// Head of list
		p_device->mp_task_list = p_task->mp_next;
	}
	else
	{
		// Find item in list
		while (p_task_node->mp_next)
		{
			if (p_task == p_task_node->mp_next)
			{
				break;
			}
			p_task_node = p_task_node->mp_next;
		}

		// Remove
		p_task_node->mp_next = p_task->mp_next;
	}
	p_task->mp_next = NULL;
}

static int AppendContRequest(SFileIOTask *p_task)
{
	EDeviceType cur_device;

	// Process device tasks
	for (cur_device = DEVICE_CD; cur_device < NUM_DEVICE_TYPES; cur_device++)
	{
		SFileIODevice *p_device = &sFileIODevices[cur_device];
		SFileIOTask *p_task_node;

		p_task_node = p_device->mp_task_list;

		// Find item in list
		while (p_task_node)
		{
			if (p_task->m_entry.m_request_id == p_task_node->m_entry.m_request_id && (p_task != p_task_node))
			{
				p_task_node->mp_cont = p_task;
				return TRUE;
			}

			p_task_node = p_task_node->mp_next;
		}
	}

	return FALSE;
}

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */


================================================
FILE: Code/Sys/File/ngps/FileIO/start.c
================================================
/* FileIO -- Converted from Sony samples -- Garrett Oct 2002 */

#include 
#include 
#include 
#include 
#include 
#include 
#include "fileio.h"
#include "fileio_iop.h"

ModuleInfo Module = {"fileio_driver", 0x0112};

// in command.c
extern int fileio_command_loop (void);

int start()
{
    struct ThreadParam param;
    int th;

    if (!sceSifCheckInit())
	{
		sceSifInit();
	}
    sceSifInitRpc(0);

    printf("FileIO driver version 0.1\n");

    param.attr         = TH_C;
    param.entry        = fileio_command_loop;
    param.initPriority = BASE_priority - 2;
    param.stackSize    = 0x800;
    param.option       = 0;
    th = CreateThread(¶m);
    if (th > 0) {
		StartThread(th, 0);
    } else {
		return 1;
    }

	return 0;
}

/* ----------------------------------------------------------------
 *	End on File
 * ---------------------------------------------------------------- */


================================================
FILE: Code/Sys/File/ngps/hed.cpp
================================================
/*	Header file functionality...
	.Hed files that describe the contents of .Wad files
	Written by Ken, stolen by Matt*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 


#include 
#include 

#include  

#include 
#include 
#include 

namespace File
{

static void get_checksums( const char * p_name, uint32 * p_dir, uint32 * p_file )
{
	char name[128];
	char dir[128];
	char file[128];
	uint lp;

	// Convert / to \\.
	for ( lp = 0; lp <= strlen( p_name ); lp++ )
	{
		name[lp] = p_name[lp];
		if ( name[lp] == '/' ) name[lp] = '\\';
	}

	// Find the directory name and the filename.
	for ( lp = strlen( name ) - 1; lp >= 0; lp-- )
	{
		if ( name[lp] == '\\' )
		{
			// Found the directory end.
			strcpy( file, &name[lp+1] );
			name[lp] = '\0';
			if ( name[0]=='\\' )
			{
				strcpy( dir, &name[1] );
			}
			else
			{
				strcpy( dir, name );
			}
			break;
		}
	}

	// Create checksums.
	if ( p_dir ) *p_dir = Script::GenerateCRC( dir );
	if ( p_file ) *p_file = Script::GenerateCRC( file );
}


// Searches the hed file for the filename. If not found, returns NULL.
SHedFile *FindFileInHed(const char *pFilename, SHed *pHed)
{
	Dbg_MsgAssert(pFilename,("NULL pFilename"));
	Dbg_MsgAssert(pHed,("NULL pHed"));

	uint32 check_dir;
	uint32 check_file;

	get_checksums( pFilename, &check_dir, &check_file );

	SHed *pHd=pHed;
	while ( pHd->numFiles != 0xffffffff )
	{
		if ( pHd->Checksum == check_dir )
		{
			SHedFile * pF = pHd->p_fileList;
			for ( uint lp2 = 0; lp2 < pHd->numFiles; lp2++ )
			{
				if ( pF->Checksum == check_file )
				{
					return pF;
				}
				pF++;
			}
		}
		pHd++;
	}

	return NULL;
}

// Searches the hed file for the filename with the same checksum. If not found, returns NULL.
SHedFile *FindFileInHedUsingChecksum( uint32 checksum, SHed *pHed )
{
	// Garrett: This function can take over .1 ms to execute when there are over 2000 entries.  We may want to make a better
	// search for this.
	//uint64 start_time = Tmr::GetTimeInUSeconds();	
	//int iterations = 0;

	Dbg_MsgAssert(pHed,("NULL pHed"));

	SHed *pHd=pHed;
	while ( pHd->numFiles != 0xffffffff )
	{
		SHedFile * pF = pHd->p_fileList;
		for ( uint lp2 = 0; lp2 < pHd->numFiles; lp2++ )
		{
			//iterations++;
			if ( pF->Checksum == checksum )
			{
				//uint64 end_time = Tmr::GetTimeInUSeconds();	
				//if ((end_time - start_time) > 100)
				//	Dbg_Message("FindNameFromChecksum Found Iterations %d Time %d", iterations, (end_time - start_time));
				return pF;
			}
			pF++;
		}
		pHd++;
	}

	//uint64 end_time = Tmr::GetTimeInUSeconds();	
	//if ((end_time - start_time) > 100)
	//	Dbg_Message("FindNameFromChecksum NotFound Iterations %d Time %d", iterations, (end_time - start_time));
	return NULL;
}

extern void StopStreaming( void );

#ifdef	__NOPT_ASSERT__
// Keep track of the hed buffers so the leak code doesn't think they are leaks
#define MAX_HED_BUFFERS 3
uint32 *sp_hed_buffers[MAX_HED_BUFFERS] = { (uint32 *) 16, (uint32 *) 16, (uint32 *) 16 };
int s_num_hed_buffers = 0;
#endif //	__NOPT_ASSERT__

SHed *LoadHed( const char *filename, bool force_cd, bool no_wad )
{
	int lp;
	uint32 *pHed = NULL;

	if (Config::CD() || force_cd)
	{
		int HedId=-1;
		char hedFileName[255];
		char uppercaseFilename[ 255 ];
		int sL = strlen( filename );
		Dbg_MsgAssert( sL < 255,( "Filename %s too long", filename ));
		int i;
		for ( i = 0; i < sL; i++ )
		{
			uppercaseFilename[ i ] = toupper( filename[ i ] );
		}
		uppercaseFilename[ i ] = '\0';
		StopStreaming( );
		sprintf( hedFileName, "cdrom0:\\%s%s.HED;1", Config::GetDirectory(),uppercaseFilename );
		int retry = 0;
		while (HedId<0)
		{
			HedId=sceOpen( hedFileName, SCE_RDONLY );
			if (HedId<0)
			{
				printf("Retrying opening %s\n", hedFileName );
				retry++;
				if (retry == 10)
				{
					return NULL;					
				}
			}	
		}
		printf("%s opened successfully\n", uppercaseFilename );
		long HedSize=sceLseek(HedId, 0, SCE_SEEK_END);
		
		Dbg_MsgAssert(pHed==NULL,("pHed not NULL ?"));
		if (no_wad)
		{
			// We'll want to keep this around
			pHed = new (Mem::Manager::sHandle().DebugHeap()) uint32[((HedSize+2047)&~2047) >> 2];
		}
		else
		{
			pHed=(uint32*)Mem::Malloc((HedSize+2047)&~2047);
		}
		Dbg_MsgAssert(pHed,("pHed NULL ?"));

	/*
		char *p = (char*) pHed;  
		// Hmmm.....
		for (int i=0 ;i < (HedSize+2047)&~2047;i++)
		{
			p[i] = 0x55;
		}
	*/						
		// 9/5/03 - Garrett: Burn fix.  Added retry code to see if HED file actually written.
		// If it cannot, it frees the buffer and returns NULL.
		int read_bytes = 0;
		retry = 0;
		while (read_bytes != HedSize)
		{
			sceLseek(HedId, 0, SCE_SEEK_SET);
			read_bytes = sceRead(HedId,pHed,HedSize);
			if (read_bytes != HedSize)
			{
				printf("READ ERROR: Retrying read of %s\n", hedFileName );
				retry++;
				if (retry == 4)
				{
					printf("READ ERROR: aborting read of %s\n", hedFileName );
					delete pHed;
					return NULL;					
				}
			}	
		}

		sceClose(HedId);
	}
	else
	{
		char tempFileName[ 255];

		// Make filename
		if (no_wad)
		{
			sprintf( tempFileName, "host:%shost.HED", filename );
		}
		else
		{
			sprintf( tempFileName, "host:%s.HED", filename );
		}
	
		void *fp = File::Open( tempFileName, "rb" );
		if ( !fp )
		{
// Mick:   Don't assert, as we want to be able to run without a .HED on the disk, for music
//			Dbg_MsgAssert( 0,( "Couldn't find hed file %s", tempFileName ));
			printf( "failed to find %s\n", tempFileName );
			return ( NULL );
		}
		int fileSize = File::GetFileSize( fp );
		File::Seek( fp, 0, SEEK_SET );
		
		if (no_wad)
		{
			// We'll want to keep this around
			pHed = new (Mem::Manager::sHandle().DebugHeap()) uint32[((fileSize+2047)&~2047) >> 2];

#ifdef	__NOPT_ASSERT__
			if (s_num_hed_buffers < MAX_HED_BUFFERS)
			{
				sp_hed_buffers[s_num_hed_buffers++] = pHed;
			}
#endif //	__NOPT_ASSERT__
		}
		else
		{
			pHed=(uint32*)Mem::Malloc((fileSize+2047)&~2047);
		}
		Dbg_MsgAssert( pHed,( "Failed to allocate memory for hed %s", tempFileName ));
		
		File::Read( pHed, 1, fileSize, fp );
		File::Close( fp );
	}

	// Convert to more compact list.
	int files = 0;
	int dirs = 0;

	// First, count how many files we have.
	uint32 * p32 = pHed;
	while ( p32[0] != 0xffffffff )
	{
		files++;

		int len = strlen( (char *)&p32[2] ) + 1;
		len = ( len + 3 ) & ~3;
		p32 = (uint32*)(((int)&p32[2]) + len);
	}

	// Build list of checksums.
	uint32 *p_offset = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
	uint32 *p_size = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
	uint32 *dir_check = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
	uint32 *file_check = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
	char **p_filename = new (Mem::Manager::sHandle().TopDownHeap()) (char *)[files];
	int count = 0;
	p32 = pHed;
	while ( p32[0] != 0xffffffff )
	{
		p_offset[count] = p32[0];
		p_size[count] = p32[1];
		p_filename[count] = (char *)&p32[2];
		get_checksums( (char *)&p32[2], &dir_check[count], &file_check[count] );
		count++;

		int len = strlen( (char *)&p32[2] ) + 1;
		len = ( len + 3 ) & ~3;
		p32 = (uint32*)(((int)&p32[2]) + len);
	}

	// Second, count how many directories we have.
	for ( lp = 0; lp < files; lp++ )
	{
		bool unique = true;
		for ( int lp2 = 0; lp2 < lp; lp2++ )
		{
			if ( dir_check[lp] == dir_check[lp2] )
			{
				unique = false;
				break;
			}
		}
		if ( unique ) dirs++;
	}

	// Now, allocate final piece of memory.
	int size = ( sizeof( SHed ) * ( dirs + 1 ) ) + ( sizeof( SHedFile ) * files );
	SHed * p_hed = (SHed *)new uint8[size];

	// Fill in directory entries.
	int dirs_added = 0;
	for ( lp = 0; lp < files; lp++ )
	{
		bool unique = true;
		for ( int lp2 = 0; lp2 < dirs_added; lp2++ )
		{
			if ( p_hed[lp2].Checksum == dir_check[lp] )
			{
				unique = false;
				break;
			}
		}
		if ( unique )
		{
			p_hed[dirs_added].Checksum = dir_check[lp];
			dirs_added++;
		}
	}

	// Fill in file entries.
	SHedFile * p_hed_file = (SHedFile *)&p_hed[(dirs+1)];
	for ( lp = 0; lp < dirs; lp++ )
	{
		p_hed[lp].p_fileList = p_hed_file;
		int files_this_dir = 0;
		for ( int lp2 = 0; lp2 < files; lp2++ )
		{
			if ( dir_check[lp2] == p_hed[lp].Checksum )
			{
				p_hed_file->FileSize = p_size[lp2];
				if (no_wad)
				{
					p_hed_file->p_filename = p_filename[lp2];
					p_hed_file->FileSize |= SHedFile::mNO_WAD;		// This marks that we are using the no_wad version
				}
				else
				{
					p_hed_file->Offset = p_offset[lp2];
				}
				p_hed_file->Checksum = file_check[lp2];
				files_this_dir++;
				p_hed_file++;
			}
		}
		p_hed[lp].numFiles = files_this_dir;
	}
	p_hed[dirs].numFiles = 0xffffffff;

	// Get rid of temp data.
	delete p_filename;
	delete file_check;
	delete dir_check;
	delete p_size;
	delete p_offset;
	if (!no_wad)
	{
		delete pHed;
	}

	return p_hed;
}

} // namespace File


================================================
FILE: Code/Sys/File/ngps/hed.h
================================================
/*	Header file functionality...
	.Hed files that describe the contents of .Wad files
	Written by Ken, stolen by Matt*/
#ifndef __HED_H__
#define __HED_H__

namespace File
{

struct SHedFile
{
	// Constants
	enum
	{
		mNO_WAD = 0x80000000
	};

	bool		HasNoWad() const { return FileSize & mNO_WAD; }
	uint32		GetFileSize() const { return FileSize & (~mNO_WAD); }

	union
	{
		uint32 Offset; 			// A SECTOR_SIZE aligned offset of a file within skate3.wad
		char * p_filename; 		// The filename itself (for no_wad heds)
	};

	// The file size, which is the raw file size, not rounded up
	// to a multiple of SECTOR_SIZE.  Highest bit is set if no wad
	// file is associated with it.
	uint32 FileSize;

	// Filename checksum (does not include directory).
	uint32 Checksum;
};

struct SHed
{
	// Number of files in this directory.
	uint32 numFiles;
	
	// Checksum of this directory.
	uint32 Checksum;

	// Pointer to File list.
	SHedFile * p_fileList;

	// The filename, which is actually bigger than one byte, tee hee.
//	const char pFilename[1];
};


SHedFile *FindFileInHed(const char *pFilename, SHed *pHed );
SHedFile *FindFileInHedUsingChecksum( uint32 checksum, SHed *pHed );
SHed *LoadHed( const char *filename, bool force_cd = false, bool no_wad = false );

} // namespace File

#endif


================================================
FILE: Code/Sys/File/ngps/p_AsyncFilesys.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// p_AsyncFilesys.cpp		GRJ 11 Oct 2002
//
// Asynchronous file system
//
///////////////////////////////////////////////////////////////////////////////////////

#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 

namespace File
{

// Debug info
#define SHOW_ASYNC_INFO	0

#if SHOW_ASYNC_INFO
#define ShowAsyncInfo(A...) scePrintf(##A)
#else
#define ShowAsyncInfo(A...) 
#endif

extern SHed *gpHed;		// Made global for p_AsyncFilesystem
extern unsigned int gWadLSN;

///////////////////////////////////////////////////////////////////////////////////////

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2AsyncFileHandle::CPs2AsyncFileHandle()
{
	m_file_handle_index = -1;
	m_num_open_requests = 0;
	mp_non_aligned_buffer = NULL;
	mp_temp_aligned_buffer = NULL;
	mp_temp_aligned_buffer_base = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CPs2AsyncFileHandle::~CPs2AsyncFileHandle()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileHandle::io_callback(EAsyncFunctionType function, int result, uint32 data)
{
	SFileIOResultPacket *p_packet = (SFileIOResultPacket *) data;
	int return_data = p_packet->m_return_data;

	// Do any special function processing
	switch (function)
	{
	case FUNC_OPEN:
		m_file_handle_index = result;		// Save IOP file handle index
		if (result < 0)
		{
			//Dbg_MsgAssert(0, ("Error: Async open returned %d", result));
			m_file_size = 0;
		} else {
			m_file_size = return_data;
			ShowAsyncInfo("io_callback: Open returned handle index %d with filesize %d\n", result, return_data);
		}
		break;

	case FUNC_SEEK:
		if (result < 0)
		{
			Dbg_MsgAssert(0, ("Error: Async seek returned %d", result));
		} else {
			m_position = result;
		}
		break;

	case FUNC_LOAD:
	case FUNC_READ:
		if (mp_temp_aligned_buffer && (result > 0))
		{
			ShowAsyncInfo("Copying from %x to %x of size %d", mp_temp_aligned_buffer, mp_non_aligned_buffer, result);
			memcpy(mp_non_aligned_buffer, mp_temp_aligned_buffer, result);
		}

		// don't break, continue on below

	case FUNC_WRITE:
		if (result < 0)
		{
			Dbg_MsgAssert(0, ("Error: Async IO returned %d", result));
		} else {
			m_position += result;
			if (mp_temp_aligned_buffer)
			{
				CPs2AsyncFileLoader::sIAddBufferToFreeList(mp_temp_aligned_buffer_base);
				mp_non_aligned_buffer = NULL;
				mp_temp_aligned_buffer = NULL;
				mp_temp_aligned_buffer_base = NULL;
			}
		}
		break;

	case FUNC_CLOSE:
		if (get_busy_count() > 1)
		{
			scePrintf("********* ERROR: Still other %d requests open after close completed on handle %x and request id %d\n", get_busy_count() - 1, this, p_packet->m_header.opt);
			//while (1)
			//	;
		}
		break;

	case FUNC_IDLE:
		// Don't want m_busy to change or user-defined callback to be executed
		return;

	default:
		break;
	}

	// Check to make sure request buffer will be clear
	if (get_busy_count() > 0)
	{
		//Dbg_MsgAssert(m_num_open_requests == 1, ("Sill other open requests"));
	}

	// Now handle the above p-line stuff
	CAsyncFileHandle::io_callback(function, result, return_data);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileHandle::non_cache_aligned_data_copy(SFileIOResultPacket *p_result, bool last_packet)
{
	Dbg_Assert(p_result->m_non_aligned_data_size > 0);
	
	int request_id = p_result->m_header.opt;
	const SRequest *p_request = get_request(request_id);
	Dbg_Assert(p_request);
	Dbg_Assert(p_request->mp_buffer);

	// Check if it goes at the start or end
	if (last_packet)
	{
		// end of buffer
		memcpy((void *) ((uint32) p_request->mp_buffer + (p_request->m_buffer_size - p_result->m_non_aligned_data_size)),
			   p_result->m_non_aligned_data, p_result->m_non_aligned_data_size);
	}
	else
	{
		// beginning of buffer
		memcpy(p_request->mp_buffer, p_result->m_non_aligned_data, p_result->m_non_aligned_data_size);
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CPs2AsyncFileHandle::add_request_id(int request_id, EAsyncFunctionType function, SFileIORequestPacket *p_packet)
{
	if (m_num_open_requests >= NUM_REQUESTS)
	{
		Dbg_MsgAssert(0, ("Too many open requests for file handle"));
		return false;
	}

	// We must disable interrupts here so that the IO interrupts don't change things underneath us.
	DI();

	// Save buffer info if it is a read
	if (p_packet->m_request.m_command == FILEIO_READ)
	{
		m_open_requests[m_num_open_requests].mp_buffer = (uint8 *) p_packet->m_request.m_param.m_rw.m_buffer;
		m_open_requests[m_num_open_requests].m_buffer_size = p_packet->m_request.m_param.m_rw.m_size;
	}
	else
	{
		m_open_requests[m_num_open_requests].mp_buffer = NULL;
		m_open_requests[m_num_open_requests].m_buffer_size = 0;
	}

	m_open_requests[m_num_open_requests].m_request_id = request_id;
	m_open_requests[m_num_open_requests++].m_function = function;

	// And re-enable interrupts
	EI();

	//scePrintf("Adding request %d to entry #%d for %x with busy %d\n", request_id, (m_num_open_requests-1), this, get_busy_count());

	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

EAsyncFunctionType	CPs2AsyncFileHandle::get_request_function(int request_id) const
{
	// Find request id in list
	for (int i = 0; i < m_num_open_requests; i++)
	{
		if (m_open_requests[i].m_request_id == request_id)
		{
			return m_open_requests[i].m_function;
		}
	}

	// Request not found
	Dbg_MsgAssert(0, ("Can't find request %d on %x", request_id, this));
	return FUNC_IDLE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const CPs2AsyncFileHandle::SRequest *	CPs2AsyncFileHandle::get_request(int request_id) const
{
	// Find request id in list
	for (int i = 0; i < m_num_open_requests; i++)
	{
		if (m_open_requests[i].m_request_id == request_id)
		{
			return &(m_open_requests[i]);
		}
	}

	// Request not found
	Dbg_MsgAssert(0, ("Can't find request %d on %x", request_id, this));
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CPs2AsyncFileHandle::clear_request_id(int request_id)
{
	if (m_num_open_requests <= 0)
	{
		Dbg_MsgAssert(0, ("Can't clear request: Open request list empty"));
		return false;
	}

	// Find request id in list
	for (int i = 0; i < m_num_open_requests; i++)
	{
		if (m_open_requests[i].m_request_id == request_id)
		{
			// Just move end of list to here
			m_open_requests[i] = m_open_requests[--m_num_open_requests];
			//scePrintf("Clearing request %d from entry #%d\n", request_id, i);
			return true;
		}
	}

	// Request not found
	Dbg_MsgAssert(0, ("Can't clear request: Not found"));
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileHandle::plat_init()
{
	m_file_handle_index = -1;
	m_num_open_requests = 0;
	mp_non_aligned_buffer = NULL;
	mp_temp_aligned_buffer = NULL;
	mp_temp_aligned_buffer_base = NULL;
}

volatile bool		CPs2AsyncFileHandle::plat_is_done()
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	return false;
}

volatile bool		CPs2AsyncFileHandle::plat_is_busy()
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	return false;
}

bool				CPs2AsyncFileHandle::plat_is_eof() const
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileHandle::plat_set_priority( int priority )
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	// Now send the packet
	SFileIORequestPacket set_packet __attribute__ ((aligned(16)));

	set_packet.m_request.m_command						= FILEIO_SET;
	set_packet.m_request.m_param.m_set.m_file_handle	= m_file_handle_index;
	set_packet.m_request.m_param.m_set.m_variable		= FILEIO_VAR_PRIORITY;
	set_packet.m_request.m_param.m_set.m_value			= priority;

	CFileIOManager::sSendCommand(&set_packet, this, true);

	if (m_blocking)
	{
		WaitForIO();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileHandle::plat_set_stream( bool stream )
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	// Now send the packet
	SFileIORequestPacket set_packet __attribute__ ((aligned(16)));

	set_packet.m_request.m_command						= FILEIO_SET;
	set_packet.m_request.m_param.m_set.m_file_handle	= m_file_handle_index;
	set_packet.m_request.m_param.m_set.m_variable		= FILEIO_VAR_STREAM;
	set_packet.m_request.m_param.m_set.m_value			= stream;

	CFileIOManager::sSendCommand(&set_packet, this, true);

	if (m_blocking)
	{
		WaitForIO();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileHandle::plat_set_destination( EAsyncMemoryType destination )
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	// Now send the packet
	SFileIORequestPacket set_packet __attribute__ ((aligned(16)));

	set_packet.m_request.m_command						= FILEIO_SET;
	set_packet.m_request.m_param.m_set.m_file_handle	= m_file_handle_index;
	set_packet.m_request.m_param.m_set.m_variable		= FILEIO_VAR_DESTINATION;
	set_packet.m_request.m_param.m_set.m_value			= destination;

	CFileIOManager::sSendCommand(&set_packet, this, true);

	if (m_blocking)
	{
		WaitForIO();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileHandle::plat_set_buffer_size( size_t buffer_size )
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	// Now send the packet
	SFileIORequestPacket set_packet __attribute__ ((aligned(16)));

	set_packet.m_request.m_command						= FILEIO_SET;
	set_packet.m_request.m_param.m_set.m_file_handle	= m_file_handle_index;
	set_packet.m_request.m_param.m_set.m_variable		= FILEIO_VAR_BUFFER_SIZE;
	set_packet.m_request.m_param.m_set.m_value			= buffer_size;

	CFileIOManager::sSendCommand(&set_packet, this, true);

	if (m_blocking)
	{
		WaitForIO();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileHandle::plat_set_blocking( bool block )
{
	// IOP doesn't need to be notified
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

size_t				CPs2AsyncFileHandle::plat_load(void *p_buffer)
{
	// This function will just get the file size and do one big read
	// It is not truly asynchronous now because of the seek wait
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	if (m_position != 0)
	{
		ShowAsyncInfo("plat_load: Doing seek.\n");
		// Now put at the beginning
		Seek(0, SEEK_SET);
		WaitForIO();

		m_current_function = FUNC_LOAD;
	}

	ShowAsyncInfo("plat_load: Reading into buffer %x.\n", p_buffer);
	// And do the read
	return plat_read(p_buffer, 1, GetFileSize());
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

size_t				CPs2AsyncFileHandle::plat_read(void *p_buffer, size_t size, size_t count)
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));
	//Dbg_MsgAssert((((uint32) p_buffer) & 0xF) == 0, ("Buffer %x needs to be quadword aligned", p_buffer));

	// Now send the packet
	SFileIORequestPacket read_packet __attribute__ ((aligned(16)));

	uint32 non_aligned_cache_start_bytes = 0;
	uint32 non_aligned_cache_end_bytes = 0;

	// Read in non-aligned bytes first (this is a temp hack)
	if (m_mem_destination == MEM_EE)
	{
		// only look for 16-byte alignment here
		bool non_aligned_bytes = ((uint32) p_buffer) & 0xF;

		// Also check to see if we aren't cache aligned on either end of a small buffer, since we assume the
		// cache alignment problem is at the end of a small buffer.
		int non_aligned_cache_bytes = ((uint32) p_buffer) & FILEIO_CACHE_BLOCK_NONALIGN_MASK;
		if (non_aligned_cache_bytes)
		{
			non_aligned_cache_bytes = FILEIO_CACHE_BLOCK_SIZE - non_aligned_cache_bytes;
		}
		//bool non_aligned_cache_size = ((size * count) - non_aligned_cache_bytes) & FILEIO_CACHE_BLOCK_NONALIGN_MASK;

		if (non_aligned_bytes || ((non_aligned_cache_bytes /*|| non_aligned_cache_size*/) && (size * count) < FILEIO_BUFFER_SIZE))
		{
			Dbg_MsgAssert(!mp_temp_aligned_buffer_base, ("Handling two small reads in succession not implemented yet"));

			int aligned_size = ((size * count) + (2 * (FILEIO_CACHE_BLOCK_SIZE - 1))) & FILEIO_CACHE_BLOCK_ALIGN_MASK;
			mp_non_aligned_buffer = (uint8 *) p_buffer;
			mp_temp_aligned_buffer_base = CPs2AsyncFileLoader::sGetBuffer(aligned_size);
			mp_temp_aligned_buffer = (uint8 *) (((uint32) mp_temp_aligned_buffer_base + (FILEIO_CACHE_BLOCK_SIZE - 1)) & FILEIO_CACHE_BLOCK_ALIGN_MASK);
			p_buffer = mp_temp_aligned_buffer;
		}
		else
		{
			// Now check for cache-alignment problems that will be handled with the messages
			non_aligned_cache_start_bytes = (((uint32) p_buffer) & FILEIO_CACHE_BLOCK_NONALIGN_MASK);
			if (non_aligned_cache_start_bytes)
			{
				non_aligned_cache_start_bytes = FILEIO_CACHE_BLOCK_SIZE - non_aligned_cache_start_bytes;
			}
			non_aligned_cache_end_bytes = ((size * count) - non_aligned_cache_start_bytes) & FILEIO_CACHE_BLOCK_NONALIGN_MASK;
		}
	}

	read_packet.m_request.m_command								 = FILEIO_READ;
	read_packet.m_request.m_param.m_rw.m_file_handle			 = m_file_handle_index;
	read_packet.m_request.m_param.m_rw.m_buffer					 = p_buffer;
	read_packet.m_request.m_param.m_rw.m_size					 = size * count;
	read_packet.m_request.m_param.m_rw.m_non_aligned_start_bytes = non_aligned_cache_start_bytes;
	read_packet.m_request.m_param.m_rw.m_non_aligned_end_bytes	 = non_aligned_cache_end_bytes;

	// Mark as busy before sending command
	inc_busy_count();

	// Garrett (4/18/03): According to the Sony Newsgroups, a FlushCache is not needed if only 1 packet is sent.
	// Only the additional packet needs the flush.  I think this was helping the issue of non-64 byte aligned
	// read buffers.  We should try taking this out after the buffers are aligned.
	FlushCache(WRITEBACK_DCACHE);		// This one is needed, but I think invalidating the cache will work, instead

	CFileIOManager::sSendCommand(&read_packet, this, true);

	Dbg_Assert(((non_aligned_cache_start_bytes == 0) || (non_aligned_cache_end_bytes == 0)) || ((size * count) >= FILEIO_BUFFER_SIZE));

	if (m_blocking)
	{
		return WaitForIO();
	} else {
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

size_t				CPs2AsyncFileHandle::plat_write(void *p_buffer, size_t size, size_t count)
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));
	Dbg_MsgAssert((((uint32) p_buffer) & 0xF) == 0, ("Buffer %x needs to be quadword aligned", p_buffer));

	// Now send the packet
	SFileIORequestPacket write_packet __attribute__ ((aligned(16)));

	write_packet.m_request.m_command					= FILEIO_WRITE;
	write_packet.m_request.m_param.m_rw.m_file_handle	= m_file_handle_index;
	write_packet.m_request.m_param.m_rw.m_buffer		= p_buffer;
	write_packet.m_request.m_param.m_rw.m_size			= size * count;

	// Mark as busy before sending command
	inc_busy_count();

	FlushCache(WRITEBACK_DCACHE);		// This one is needed, but I think invalidating the cache will work, instead

	int req_id = CFileIOManager::sSendCommand(&write_packet, this, true);

	// Wait for result
	CFileIOManager::sWaitRequestCompletion(req_id);

	return CFileIOManager::sGetRequestResult(write_packet.m_header.opt);
}

char *				CPs2AsyncFileHandle::plat_get_s(char *p_buffer, int maxlen)
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					CPs2AsyncFileHandle::plat_seek(long offset, int origin)
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	// Now send the packet
	SFileIORequestPacket seek_packet __attribute__ ((aligned(16)));

	seek_packet.m_request.m_command						= FILEIO_SEEK;
	seek_packet.m_request.m_param.m_seek.m_file_handle	= m_file_handle_index;
	seek_packet.m_request.m_param.m_seek.m_offset		= offset;
	seek_packet.m_request.m_param.m_seek.m_origin		= origin;

	// Mark as busy before sending command
	inc_busy_count();

	CFileIOManager::sSendCommand(&seek_packet, this, true);

	if (m_blocking)
	{
		return WaitForIO();
	} else {
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CPs2AsyncFileHandle::plat_open(const char *filename)
{
	// Init vars
	m_position = 0;

	// Now send the packet
	const char *p_continued_filename = NULL;
	SFileIORequestPacket open_packet __attribute__ ((aligned(16)));

	open_packet.m_request.m_command											= FILEIO_OPEN;
	if (Config::CD())
	{
		if ( !gpHed )
		{
			return ( false );
		}
		SHedFile *tempHed = FindFileInHed( filename, gpHed );
		if ( !tempHed )
		{
			//Dbg_MsgAssert( 0,( "File %s cannot be found in Hed file.", filename ));
			Dbg_Message( "File %s cannot be found in Hed file.", filename );
			return ( false );
		}

		m_file_size = tempHed->GetFileSize();

		open_packet.m_request.m_param.m_open.m_file.m_raw_name.m_sector_number	= gWadLSN + (tempHed->Offset / SECTOR_SIZE);
		open_packet.m_request.m_param.m_open.m_file.m_raw_name.m_file_size		= m_file_size;
		open_packet.m_request.m_param.m_open.m_host_read 						= FILEIO_HOSTOPEN_FALSE;

		ShowAsyncInfo("Sending open command for file %s of size %d and offset %d at sector %d\n", filename, m_file_size, tempHed->Offset, gWadLSN + (tempHed->Offset / SECTOR_SIZE));
	} else {
		//Dbg_MsgAssert(strlen(filename) < FILEIO_MAX_FILENAME_SIZE, ("Can't open file %s: name longer than %d chars", filename, FILEIO_MAX_FILENAME_SIZE - 1));

		// Copy filename and check if we need to insert a "host0:" into the name
		uint chars_copied = FILEIO_MAX_FILENAME_SIZE - 1;
		if (strchr(filename, ':'))
		{
			strncpy(open_packet.m_request.m_param.m_open.m_file.m_filename, filename, chars_copied);
		} else {
			//Dbg_MsgAssert(strlen(filename) < (FILEIO_MAX_FILENAME_SIZE - 5), ("Can't open file %s: name longer than %d chars", filename, FILEIO_MAX_FILENAME_SIZE - 6));
			chars_copied -= 5;
			strcpy(open_packet.m_request.m_param.m_open.m_file.m_filename, "host:");
			strncpy(&open_packet.m_request.m_param.m_open.m_file.m_filename[5], filename, chars_copied);
		}

		// Check for long filename
		if (chars_copied < strlen(filename))
		{
			p_continued_filename = &(filename[chars_copied]);
			open_packet.m_request.m_param.m_open.m_host_read = FILEIO_HOSTOPEN_EXTEND_NAME;
		}
		else
		{
			open_packet.m_request.m_param.m_open.m_host_read = FILEIO_HOSTOPEN_TRUE;
		}


		ShowAsyncInfo("Sending host open command for file %s\n", filename);
	}
	open_packet.m_request.m_param.m_open.m_memory_dest 						= m_mem_destination;
	open_packet.m_request.m_param.m_open.m_buffer_size 						= m_buffer_size;
	open_packet.m_request.m_param.m_open.m_priority 						= m_priority;
	open_packet.m_request.m_param.m_open.m_attributes						= 0;		// need to define this

	// Mark as busy before sending command
	inc_busy_count();

	uint request_id = CFileIOManager::sSendCommand(&open_packet, this, true);

	if (p_continued_filename)
	{
		Dbg_MsgAssert(strlen(p_continued_filename) < FILEIO_MAX_FILENAME_SIZE, ("Can't open file %s: name longer than %d chars", filename, (FILEIO_MAX_FILENAME_SIZE - 1) * 2));
		strcpy(open_packet.m_request.m_param.m_open.m_file.m_filename, p_continued_filename);

		open_packet.m_request.m_command	= FILEIO_CONT;

		#ifdef	__NOPT_ASSERT__
		uint cont_request_id = 
		#endif
		CFileIOManager::sSendCommand(&open_packet, this, true, request_id);
		Dbg_Assert(cont_request_id == request_id);
	}

	if (m_blocking)
	{
		return WaitForIO() >= 0;
	} else {
		// Copy request ID into file handle to use as temp file handle
		m_file_handle_index = (int) (FILEIO_TEMP_HANDLE_FLAG | request_id);
		return true;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CPs2AsyncFileHandle::raw_open(unsigned int lsn, bool blocking, int priority)
{
	Dbg_MsgAssert(!IsBusy(), ("Can't open raw file %d: asynchronous file handle already busy.", lsn));
	Dbg_MsgAssert(!mp_pre_file, ("Can't open raw file %d: already open as a PRE file.", lsn));

	// Make sure we are using the CD
	if (!Config::CD())
	{
		Dbg_MsgAssert(0, ("Raw Open not supported in host mode"));
		return false;
	}

	m_priority = priority;
	m_blocking = blocking;

	m_current_function = FUNC_OPEN;

	// Init vars
	m_position = 0;

	// Now send the packet
	SFileIORequestPacket open_packet __attribute__ ((aligned(16)));

	// Make it the max file size since a the WADs don't keep track of how big they are
	m_file_size = MAX_FILE_SIZE;

	open_packet.m_request.m_command											= FILEIO_OPEN;
	open_packet.m_request.m_param.m_open.m_file.m_raw_name.m_sector_number	= lsn;
	open_packet.m_request.m_param.m_open.m_file.m_raw_name.m_file_size		= m_file_size;
	open_packet.m_request.m_param.m_open.m_host_read 						= FILEIO_HOSTOPEN_FALSE;
	open_packet.m_request.m_param.m_open.m_memory_dest 						= m_mem_destination;
	open_packet.m_request.m_param.m_open.m_buffer_size 						= m_buffer_size;
	open_packet.m_request.m_param.m_open.m_priority 						= m_priority;
	open_packet.m_request.m_param.m_open.m_attributes						= 0;		// need to define this

	ShowAsyncInfo("Sending raw open command for file of size %d at sector %d\n", m_file_size, lsn);

	// Mark as busy before sending command
	inc_busy_count();

	uint request_id = CFileIOManager::sSendCommand(&open_packet, this, true);

	if (m_blocking)
	{
		return WaitForIO() >= 0;
	} else {
		// Copy request ID into file handle to use as temp file handle
		m_file_handle_index = (int) (FILEIO_TEMP_HANDLE_FLAG | request_id);
		return true;
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CPs2AsyncFileHandle::plat_close()
{
	//Dbg_MsgAssert(m_file_handle_index != -1, ("Invalid IOP file handle index: %d", m_file_handle_index));

	// Now send the packet
	SFileIORequestPacket close_packet __attribute__ ((aligned(16)));

	close_packet.m_request.m_command						= FILEIO_CLOSE;
	close_packet.m_request.m_param.m_close.m_file_handle	= m_file_handle_index;

	// Mark as busy before sending command
	inc_busy_count();

	CFileIOManager::sSendCommand(&close_packet, this, true);

	m_file_handle_index = -1;

	if (m_blocking)
	{
		return WaitForIO();
	} else {
		return 0;
	}
}

///////////////////////////////////////////////////////////////////////////////////////

CPs2AsyncFileLoader::SAlignBuffer	CPs2AsyncFileLoader::s_buffer_list[BUFFER_LIST_SIZE];
//volatile int		CPs2AsyncFileLoader::s_free_buffer_list_size = 0;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CAsyncFileHandle *	CPs2AsyncFileLoader::sRawOpen( unsigned int lsn, bool blocking, int priority )
{
	CPs2AsyncFileHandle *p_file_handle = static_cast(s_get_file_handle());

	if (p_file_handle)
	{
		if (!p_file_handle->raw_open(lsn, blocking, priority))
		{
			//Dbg_MsgAssert(0, ("Error opening Async file %s", filename));
			s_free_file_handle(p_file_handle);
			return NULL;
		}

		return p_file_handle;
	}
	else
	{
		Dbg_MsgAssert(0, ("Out of Async handles"));
		return NULL;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint8 *				CPs2AsyncFileLoader::sGetBuffer( uint size )
{
#if 0
	if (size > ALIGN_BUFFER_SIZE)
	{
		Dbg_Message("sGetBuffer() is trying to allocate %d.  Max buffer size is %d", size, ALIGN_BUFFER_SIZE);
		Dbg_MsgAssert(0, ("sGetBuffer() is trying to allocate %d.  Max buffer size is %d", size, ALIGN_BUFFER_SIZE));
	}

	if (s_free_buffer_list_size == 0)
	{
		Dbg_Message("sGetBuffer() out of buffers");
		Dbg_MsgAssert(0, ("sGetBuffer() out of buffers"));
	}
#endif

	//Dbg_Message("***** Getting async buffer of size %d", size);

	volatile uint8 *p_buffer = NULL;
	// Make sure interrupts are off
	DI();
	for (int i = 0; i < BUFFER_LIST_SIZE; i++)
	{
		if ((s_buffer_list[i].m_size >= size) && !s_buffer_list[i].m_used)
		{
			p_buffer = s_buffer_list[i].mp_buffer;
			s_buffer_list[i].m_used = true;
		}
	}
	EI();

	Dbg_MsgAssert(p_buffer, ("Couldn't allocate align buffer of size %d", size));

	return (uint8 *) p_buffer;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileLoader::sAddBufferToFreeList( uint8 *p_dealloc_buffer )
{
	// Make sure interrupts are off
	DI();

	sIAddBufferToFreeList(p_dealloc_buffer);

	EI();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileLoader::sIAddBufferToFreeList( uint8 *p_dealloc_buffer )
{
	//scePrintf("***** Freeing async buffer\n");

#if 0 //def __NOPT_ASSERT__
	if (s_free_buffer_list_size >= FREE_LIST_SIZE)
	{
		scePrintf("CPs2AsyncFileLoader::sIAddBufferToFreeList(): Free list full.  Tell Garrett.\n");
		while (s_free_buffer_list_size >= FREE_LIST_SIZE)		// We are basically stuck here
			;
	}
#endif // __NOPT_ASSERT__

	for (int i = 0; i < BUFFER_LIST_SIZE; i++)
	{
		if (s_buffer_list[i].mp_buffer == p_dealloc_buffer)
		{
#ifdef __NOPT_ASSERT__
			if (!s_buffer_list[i].m_used)
			{
				scePrintf("CPs2AsyncFileLoader::sIAddBufferToFreeList(): Freeing a buffer that is already free.  Tell Garrett.\n");
				while (1)
					;
			}
#endif // __NOPT_ASSERT__

			s_buffer_list[i].m_used = false;
			return;
		}
	}

#ifdef __NOPT_ASSERT__
	scePrintf("CPs2AsyncFileLoader::sIAddBufferToFreeList(): Pointer wrong value.  Tell Garrett.\n");
	while (1)
		;
#endif // __NOPT_ASSERT__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileLoader::sAllocateFreeListBuffers( )
{
	int size_chunk = (ALIGN_MAX_BUFFER_SIZE - ALIGN_MIN_BUFFER_SIZE) / (BUFFER_LIST_SIZE - 1);
	for (int i = 0; i < BUFFER_LIST_SIZE; i++)
	{
		int buffer_size = ALIGN_MIN_BUFFER_SIZE + (size_chunk * i);
		s_buffer_list[i].mp_buffer = new uint8[buffer_size];
		s_buffer_list[i].m_size = buffer_size;
		s_buffer_list[i].m_used = false;
		
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CPs2AsyncFileLoader::sDeallocateFreeListBuffers( )
{
	Dbg_Assert(0);		// We shouldn't be calling this now

#if 0 
	// Make sure interrupts are off
	DI();

	for (int i = 0; i < s_free_buffer_list_size; i++)
	{
		delete [] s_free_buffer_list[i];
		s_free_buffer_list[i] = NULL;
	}
	s_free_buffer_list_size = 0;

	EI();
#endif
}

///////////////////////////////////////////////////////////////////////////////////////

sceSifCmdData	CFileIOManager::s_cmdbuffer[FILEIO_NUM_COMMAND_HANDLERS];

int				CFileIOManager::s_cur_request_id = 0;

CFileIOManager::SOpenRequest	CFileIOManager::s_open_requests[NUM_REQUESTS];
int								CFileIOManager::s_free_request_index = 0;

/******************************************************************/
/*                                                                */
/*   CALLBACK                                                     */
/*                                                                */
/******************************************************************/

void 				CFileIOManager::s_result_callback(void *p, void *q)
{
	SFileIOResultPacket *h = (SFileIOResultPacket *) p;
	int request_id = h->m_header.opt;

	SOpenRequest *p_request = s_find_request(request_id);
	Dbg_MsgAssert(p_request, ("Can't find request id # %d", request_id));

	p_request->m_result = h->m_return_value;
	p_request->m_done = h->m_done;

	//scePrintf( "Got IOP request %d result value %d back with done = %d\n", request_id, p_request->m_result, p_request->m_done );

	// Call file handle callback if request is completed
	if (p_request->mp_cur_file)
	{
		// Check if we got any non-aligned data back
		if (h->m_non_aligned_data_size > 0)
		{
			p_request->mp_cur_file->non_cache_aligned_data_copy(h, p_request->m_done);
		}

		if (p_request->m_done)
		{
			//p_request->mp_cur_file->io_callback(p_request->mp_cur_file->get_request_function(request_id), p_request->m_result, h->m_return_data);
			p_request->mp_cur_file->io_callback(p_request->mp_cur_file->get_request_function(request_id), p_request->m_result, (uint32) h);
			p_request->mp_cur_file->clear_request_id(request_id);
		}
	}

	ShowAsyncInfo( "Got IOP request %d result value %d back with done = %d\n", request_id, p_request->m_result, p_request->m_done );
	
	ExitHandler();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CFileIOManager::SOpenRequest *		CFileIOManager::s_find_request(int request_id)
{
	// Start at the last used index, probably the one used
	int index = s_prev_open_index(s_free_request_index);

	SOpenRequest *p_start_request = &(s_open_requests[index]);
	SOpenRequest *p_request = p_start_request;

	do
	{
		if (p_request->m_id == request_id)
		{
			return p_request;
		}
		
		index = s_prev_open_index(index);
		p_request = &(s_open_requests[index]);
	} while (p_request != p_start_request);

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CFileIOManager::sInit()
{
	// Communicate with the IOP side
    sceSifInitRpc(0);
	DI();

	ShowAsyncInfo( "Setting up Sif Cmd with FileIO IRX...\n" );
	sceSifSetCmdBuffer(&s_cmdbuffer[0], FILEIO_NUM_COMMAND_HANDLERS);
	sceSifAddCmdHandler(FILEIO_RESULT_COMMAND, s_result_callback, NULL);

	EI();

	// Clear out pending requests
	for (int i = 0; i < NUM_REQUESTS; i++)
	{
		s_open_requests[i].m_done = true;
		s_open_requests[i].m_id = -1;
		s_open_requests[i].m_result = -1;
	}
	s_free_request_index = 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					CFileIOManager::sSendCommand(SFileIORequestPacket *p_packet, CPs2AsyncFileHandle *p_file, bool wait_for_send, int continuation_request)
{
	SOpenRequest *p_request;

	if (continuation_request >= 0)
	{
		p_request = s_find_request(continuation_request);
		Dbg_MsgAssert(p_request, ("Can't find open request %d to continue", continuation_request));

		// Just in case they didn't wait before
		sWaitSendCompletion(p_request->m_id);
	}
	else
	{
		p_request = &(s_open_requests[s_free_request_index]);
		Dbg_MsgAssert(p_request->m_done, ("Too many open requests"));
		s_free_request_index = s_next_open_index(s_free_request_index);

		int request_id = s_cur_request_id++;

		p_request->m_done		= false;		// Callback will change it to true
		p_request->m_id			= request_id;
		p_request->mp_cur_file	= p_file;
		if (p_file)
		{
			p_file->add_request_id(request_id, p_file->m_current_function, p_packet);
		}

		p_packet->m_header.opt = request_id;
	}

	p_request->m_sif_cmd_id = sceSifSendCmd(FILEIO_REQUEST_COMMAND, p_packet, sizeof(SFileIORequestPacket), 0, 0, 0);

	if (wait_for_send)
	{
		sWaitSendCompletion(p_request->m_id);
	}

	return p_request->m_id;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CFileIOManager::sWaitSendCompletion(int request_id)
{
	SOpenRequest *p_request = s_find_request(request_id);
	Dbg_MsgAssert(p_request, ("Can't find request id # %d", request_id));

	while(sceSifDmaStat(p_request->m_sif_cmd_id) >= 0)
		;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CFileIOManager::sWaitRequestCompletion(int request_id)
{
	SOpenRequest *p_request = s_find_request(request_id);
	Dbg_MsgAssert(p_request, ("Can't find request id # %d", request_id));

	while (!p_request->CommandDone())
		;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

volatile bool		CFileIOManager::sCommandDone(int request_id)
{
	SOpenRequest *p_request = s_find_request(request_id);
	Dbg_MsgAssert(p_request, ("Can't find request id # %d", request_id));

	return p_request->m_done;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int					CFileIOManager::sGetRequestResult(int request_id)
{
	SOpenRequest *p_request = s_find_request(request_id);
	Dbg_MsgAssert(p_request, ("Can't find request id # %d", request_id));

	return p_request->m_result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileLoader::s_plat_init()
{
	for (int i = 0; i < MAX_FILE_HANDLES; i++)
	{
		if (s_file_handles[i])
		{
			s_file_handles[i]->init();
		}
		else
		{
			s_file_handles[i] = new CPs2AsyncFileHandle;
		}
	}

	// Allocate EE align buffers
	CPs2AsyncFileLoader::sAllocateFreeListBuffers();

	// Init the FileIO Manager
	CFileIOManager::sInit();

	// Now send the first packet
	SFileIORequestPacket init_packet __attribute__ ((aligned(16)));

	init_packet.m_request.m_command						= FILEIO_INIT;
	init_packet.m_request.m_param.m_init.m_buffer_size	= DEFAULT_BUFFER_SIZE;
	init_packet.m_request.m_param.m_init.m_priority		= DEFAULT_ASYNC_PRIORITY;
	init_packet.m_request.m_param.m_init.m_memory_dest	= DEFAULT_MEMORY_TYPE;
	init_packet.m_request.m_param.m_init.m_init_cd_device = Config::CD();

	int req_id = CFileIOManager::sSendCommand(&init_packet, NULL, true);

	Dbg_Message("Sending first init command to fileio.irx: waiting for completion");

	// Wait for result (no async inits)
	CFileIOManager::sWaitRequestCompletion(req_id);

	Dbg_Message("Sent first init command to fileio.irx.");

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileLoader::s_plat_cleanup()
{
	for (int i = 0; i < MAX_FILE_HANDLES; i++)
	{
		if (s_file_handles[i])
		{
			delete s_file_handles[i];

			s_file_handles[i] = NULL;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CAsyncFileLoader::s_plat_async_supported()
{
	// Works on the PS2
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool				CAsyncFileLoader::s_plat_exist(const char *filename)
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileLoader::s_plat_update(void)
{
	//// Free any buffers let go during an interrupt
	//CPs2AsyncFileLoader::sDeallocateFreeListBuffers();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void				CAsyncFileLoader::s_plat_swap_callback_list()
{
	// We must disable interrupts here so that the IO interrupts don't change things underneath us.
	DI();

	s_cur_callback_list_index = s_cur_callback_list_index ^ 1;	// Swap Indexes
	s_new_io_completion = false;								// And clear flag

#ifdef __NOPT_ASSERT__
	bool new_list_clear = s_num_callbacks[s_cur_callback_list_index] == 0;
#endif // __NOPT_ASSERT__

	// And re-enable interrupts
	EI();

	Dbg_MsgAssert(new_list_clear, ("Async IO swapped callback list isn't empty"));
}

}





================================================
FILE: Code/Sys/File/ngps/p_AsyncFilesys.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			File													**
**																			**
**	Created:		10/11/02	-	grj										**
**																			**
**	File name:		core/sys/p_AsyncFilesys.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_FILE_P_ASYNC_FILESYS_H
#define	__SYS_FILE_P_ASYNC_FILESYS_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace File
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

// Forward declarations
class CFileIOManager;
class CPs2AsyncFileLoader;

/////////////////////////////////////////////////////////////////////////////////////
// The asynchronous file class
//
class CPs2AsyncFileHandle : public CAsyncFileHandle
{
public:

protected:

						CPs2AsyncFileHandle();
	virtual 			~CPs2AsyncFileHandle();

private:
	// Constants
	enum
	{
		NUM_REQUESTS = 16
	};

	// Request data
	struct SRequest
	{
		int					m_request_id;
		EAsyncFunctionType	m_function;
		uint8 *				mp_buffer;
		int					m_buffer_size;
	};

	// Callback functions
	virtual void		io_callback(EAsyncFunctionType function, int result, uint32 data);

	volatile int		m_file_handle_index;

	// Open requests
	SRequest		  	m_open_requests[NUM_REQUESTS];
	volatile int		m_num_open_requests;

	// Non-aligned buffer IO
	uint8 *				mp_non_aligned_buffer;
	uint8 *				mp_temp_aligned_buffer;
	uint8 *				mp_temp_aligned_buffer_base;

	bool				add_request_id(int request_id, EAsyncFunctionType function, SFileIORequestPacket *p_packet);
	EAsyncFunctionType	get_request_function(int request_id) const;
	const SRequest *	get_request(int request_id) const;
	bool				clear_request_id(int request_id);

	// PS2 only IO calls
	bool				raw_open(unsigned int lsn, bool blocking, int priority);

	// PS2 internal calls
	void				non_cache_aligned_data_copy(SFileIOResultPacket *p_result, bool last_packet);

	// platform-specific calls
	virtual void		plat_init( void );

	virtual bool		plat_open( const char *filename );
	virtual bool		plat_close( void );

	virtual volatile bool	plat_is_done( void );
	virtual volatile bool	plat_is_busy( void );
	virtual bool		plat_is_eof( void ) const;

	virtual void		plat_set_priority( int priority );
	virtual void		plat_set_stream( bool stream );
	virtual void		plat_set_destination( EAsyncMemoryType destination );
	virtual void		plat_set_buffer_size( size_t buffer_size );
	virtual void		plat_set_blocking( bool block );

	virtual size_t		plat_load( void *p_buffer );
	virtual size_t		plat_read( void *p_buffer, size_t size, size_t count );
	virtual size_t		plat_write( void *p_buffer, size_t size, size_t count );
	virtual char *		plat_get_s( char *p_buffer, int maxlen );
	virtual int			plat_seek( long offset, int origin );

	// Friends
	friend CAsyncFileLoader;
	friend CPs2AsyncFileLoader;
	friend CFileIOManager;
};

/////////////////////////////////////////////////////////////////////////////////////
// The interface to start and end async loads
//
class CPs2AsyncFileLoader : public CAsyncFileLoader
{
public:
	// Opens a "file" by specifying the starting logical sector number of a file
	static CAsyncFileHandle *	sRawOpen( unsigned int lsn, bool blocking = true, int priority = DEFAULT_ASYNC_PRIORITY );

	// Deallocation of buffers cannot be done in interrupt mode.  The FileLoader update function will need to
	// clear them out.
	static void					sAllocateFreeListBuffers( );
	static void					sDeallocateFreeListBuffers( );

	static uint8 *				sGetBuffer( uint size );								// thread only
	static void					sAddBufferToFreeList( uint8 *p_dealloc_buffer );		// thread only
	static void					sIAddBufferToFreeList( uint8 *p_dealloc_buffer );		// interrupt only

private:
	// Constants
	enum
	{
		ALIGN_MAX_BUFFER_SIZE = 17000,
		ALIGN_MIN_BUFFER_SIZE = 4096,
		BUFFER_LIST_SIZE = 2
	};

	// Buffer struct
	struct SAlignBuffer
	{
		uint					m_size;
		volatile bool			m_used;
		volatile uint8 *		mp_buffer;
	};

	static SAlignBuffer			s_buffer_list[BUFFER_LIST_SIZE];
//	static volatile int			s_free_buffer_list_size;
};

/////////////////////////////////////////////////////////////////////////////////////
// File IO manager that communicates with the IOP module
//
class CFileIOManager
{
public:
	static void					sInit();

	// IOP command functions
	static int					sSendCommand(SFileIORequestPacket *p_packet, CPs2AsyncFileHandle *p_file = NULL, bool wait_for_send = false,
											 int continuation_request = -1);
	static void					sWaitSendCompletion(int request_id);
	static void					sWaitRequestCompletion(int request_id);

	static volatile bool		sCommandDone(int request_id);

	static int					sGetRequestResult(int request_id);

private:
	// Constants
	enum
	{
		NUM_REQUESTS = 64
	};

	struct SOpenRequest
	{
		inline volatile bool	CommandDone();

		CPs2AsyncFileHandle *	mp_cur_file;
		int						m_id;
		int						m_sif_cmd_id;
		volatile bool 			m_done;
		volatile int 			m_result;
	};

	static void 				s_result_callback(void *p, void *q);

	static int					s_prev_open_index(int index) { return (index > 0) ? index - 1 : NUM_REQUESTS - 1; }
	static int					s_next_open_index(int index) { return (index + 1) % NUM_REQUESTS; }
	static SOpenRequest *		s_find_request(int request_id);

	// Data for the actual SifCmd transfers
	static sceSifCmdData 		s_cmdbuffer[FILEIO_NUM_COMMAND_HANDLERS];

	static int 					s_cur_request_id;

	// Open requests
	static SOpenRequest			s_open_requests[NUM_REQUESTS];
	static int					s_free_request_index;
};

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline volatile bool		CFileIOManager::SOpenRequest::CommandDone()
{
	return m_done;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace File

#endif  // __SYS_FILE_P_ASYNC_FILESYS_H


================================================
FILE: Code/Sys/File/ngps/p_filesys.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		System Library											**
**																			**
**	Module:			File IO (File) 											**
**																			**
**	File name:		p_filesys.cpp											**
**																			**
**	Created by:		03/20/00	-	mjb										**
**																			**
**	Description:	PS2 File System											**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 

#ifdef	PAL
#define	__DVD_ONLY__ 0				// 0 = DVD or CD,  1 = Just DVD (for copy protection)
#else
#define	__DVD_ONLY__ 0				// 0 = DVD or CD,  1 = Just DVD (for copy protection)
#endif

#define ASYNC_QUICK_FILESYS 1
#define ASYNC_HOST_FILESYS	0
#define DISABLE_QUICK_FILESYSTEM	0

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/

namespace File
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#ifndef SECTOR_SIZE
#define SECTOR_SIZE 2048
#endif // SECTOR_SIZE



// MEMOPT: If we run out of IOP memory again, it is possible that we could use the pcm buffer
// (The one that is TOTAL_IOP_BUFFER_SIZE_NEEDED) for the file i/o, since probably music or 
// streams will not need to be playing when a file is loading.
// That would free up 2048*48=98304 bytes!!
#define FILESYS_NUM_SECTORS_IN_BUFFER			48
#define FILESYS_STREAM_BUFFER_NUM_PARTITIONS	3
#define FILESYS_IOP_BUFFER_SIZE					( SECTOR_SIZE * FILESYS_NUM_SECTORS_IN_BUFFER )
#define FILESYS_NUM_SECTORS_PER_READ			16
#define	FILESYS_CD_READ_SIZE					( SECTOR_SIZE * FILESYS_NUM_SECTORS_PER_READ )
#define IOP_TO_EE_BUFFER_ALLIGNMENT				64

// Static globals:
static void				*gFilesysIOPStreamBuffer = NULL;
//static char				*gIOPToEEBuffer;
	   unsigned int		gWadLSN = 0;          // Made global for p_AsyncFilesystem
static bool				gQuickFileSystemInitialzed = false;
static bool				gStreaming = false;

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

#define READBUFFERSIZE (10240*5)

// GJ:  I added this to track how many skyFiles are active
// at any given time.  I am assuming that there's only one,
// in which case I can just create one global instance
// (previously, we were allocating it off the current heap,
// which was causing me grief during the CAS loading process)
//int g_fileOpenCount = 0;

struct skyFile
{
	
	// the following must match the dumbSkyFile struct
	// in pre.cpp, or else pre file i/o won't work properly
	int				gdfs;
	int32			POS;
	int32			SOF;

	// the rest is skyFile-specific
	uint8			readBuffer[READBUFFERSIZE];
	uint32		bufferPos;
	bool			bufferValid;
	bool			locked;			// whether the skyfile is currently in use
	
	// Used when CD
	uint32			WadOffset;
	const char		*pFilename;
	
	// Used when non-CD
	#define MAX_PFILESYS_NAME_SIZE 255
	char filename[ MAX_PFILESYS_NAME_SIZE ];
#if ASYNC_HOST_FILESYS
	CAsyncFileHandle *p_async_file;
#endif
};

// At first, I assumed that only one skyfile could be open at
// a time, but that's not true when testing streams from the
// PC build.
// pc builds need to have two (one for normal files, and one for streams)
// GJ:  for THPS4, we increased this to 3, so that we can play multiple streams
const int MAXOPENFILES = 3;

// Ken note: It used to be that MAXOPENFILES was defined to be 1 when __NOPT_CDROM__OLD
// was set. Now that __NOPT_CDROM__OLD is not used anymore and CD() is used at runtime
// instead, I made MAXOPENFILES always 2. Then, where MAXOPENFILES used to be used in the code
// it now uses a var set to 1 or 2 depending on CD()
static skyFile g_skyFile[MAXOPENFILES];

skyFile* lock_skyfile( void )
{
	// cd builds can only have one file open at any given time
	int max_open_files=Config::CD() ? 1:MAXOPENFILES;
	
	int i;
	// this tries to find an unlocked skyfile
	for ( i = 0; i < max_open_files; i++ )
	{
		if ( !g_skyFile[i].locked )
		{
			g_skyFile[i].locked = true;
			return &g_skyFile[i];
		}
	}

	if (!Config::CD())
	{
		Dbg_Message( "Here are the files currently open:" );
		for ( i = 0; i < max_open_files; i++ )
		{
			Dbg_Message( "%s", g_skyFile[ i ].filename );
		}
	}	

	// if we get here, that means that all the sky files were locked
	Dbg_MsgAssert( 0, ( "Trying to open too many files simultaneously (max=%d)", max_open_files ) );
	return NULL;
}

void unlock_skyfile( skyFile* pSkyFile )
{
	// cd builds can only have one file open at any given time
	int max_open_files=Config::CD() ? 1:MAXOPENFILES;
	

	// this tries to find the sky file that the caller is referencing
	for ( int i = 0; i < max_open_files; i++ )
	{
		if ( pSkyFile == &g_skyFile[i] )
		{
			Dbg_MsgAssert( g_skyFile[i].locked, ( "Trying to unlock a sky file too many times" ) );
			g_skyFile[i].locked = false;
			return;
		}
	}

	// if we get here, that means that the pointer didn't match one of the valid skyfiles
	Dbg_MsgAssert( 0, ( "Unrecognized sky file" ) );
}

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static	int32			s_open_files = 0;

// The header file (SKATE4.hed) used for looking up where files are within
// SKATE4.wad
SHed *gpHed=NULL;		// Made global for p_AsyncFilesystem
static int WadId=-1;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/* skyTransMode                                                   */
/*                                                                */
/* Attempt to convert a mode string to an open mode				  */ 
/*                                                                */
/* On entry  : access mode										  */
/* On exit   : integer mode										  */
/*                                                                */
/******************************************************************/

static int skyTransMode(const char *access)
{
	

	int mode;
	char *r;
	char *w;
	char *a;
	char *plus;
	char *n;
	char *d;

	/* I add a couple of new characters for now:
	   n non-blocking mode
	   d no write back d cache */

	r = strrchr(access, (int)'r');
	w = strrchr(access, (int)'w');
	a = strrchr(access, (int)'a');
	plus = strrchr(access, (int)'+');
	n = strrchr(access, (int)'n');
	d = strrchr(access, (int)'d');

	if (plus)
		mode = SCE_RDWR;
	else if (r)
		mode = SCE_RDONLY;
	else if (w)
		mode = SCE_WRONLY;
	else if (a)
		mode = SCE_WRONLY;
	else
		return(0);

	/* later we will test for SCE_CREAT & !SCE_TRUNC as a seek to end of file */
	if (w)
		mode |= SCE_CREAT | SCE_TRUNC;

	if (a)
		mode |= SCE_CREAT;

	if (n)
		mode |= SCE_NOWAIT;

	if (d)
		Dbg_MsgAssert( 0,( "Hmm... gotta find out what SCE_NOWBDC was in previous library." ));
//		mode |= SCE_NOWBDC;

	return(mode);
}

void StartStreaming( uint32 lsn )
{
	Dbg_MsgAssert( gFilesysIOPStreamBuffer,( "IOP stream buffer not initialized" ));

	sceCdSync(0);			// Make sure there aren't any non-blocking CD reads happening
	Pcm::StopStreams( );	// Garrett: I found streamed sounds can skip when using the quick load
//	if ( Pcm::UsingCD( ) )
//	{
//		Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
//	}
	sceCdRMode mode;
	mode.trycount = 255;
	mode.spindlctrl = SCECdSpinNom;
    mode.datapattern = SCECdSecS2048;
	mode.pad = 0;
	while ( !( gStreaming = sceCdStStart( lsn, &mode ) ) )
	{
		printf( "aah ha! would have caused the crash!!!\n" );
		sceCdStop();
		sceCdSync(0);
	}
}

void StopStreaming( void )
{
	if (!Config::CD())
	{
		return;
	}
		
	if ( gStreaming )
	{
		while ( !sceCdStStop( ) )
		{
			printf( "sceCdStStop failed\n" );
		}
	}
	gStreaming = false;
}


uint32 CanFileBeLoadedQuickly( const char *filename )
{
#if DISABLE_QUICK_FILESYSTEM
	return 0;
#endif

	if (!Config::CD())
	{
		return 0;
	}
		
	if ( !gQuickFileSystemInitialzed )
	{
		return ( 0 );
	}
    if ( !gpHed )
	{
		return ( 0 );
	}
	SHedFile *tempHed = FindFileInHed( filename, gpHed );
	if ( tempHed )
		return ( tempHed->GetFileSize() );
	return ( 0 );
}

#if ASYNC_QUICK_FILESYS == 0
static void HandleCDStreamingError( uint32 lsn )
{
	
	int err;
	err = sceCdGetError( );
	printf( "CD Error %d\n", err );
	// let's just stop everything, and try again fresh and new:
	sceCdBreak( );
	sceCdSync( 0 );
	StartStreaming( lsn );
/*	switch ( err )
	{
		case ( SCECdErNO ):
			break;
		case ( SCECdErTRMOPN ):
			break;
		case ( ):
			break;
		case ( ):
			break;
		case ( ):
			break;
		default:
			break;
	}*/
}
#endif // ASYNC_QUICK_FILESYS == 0

bool LoadFileQuicklyPlease( const char *filename, uint8 *addr )
{
	if (!Config::CD())
	{
		return false;
	}

	if ( !gQuickFileSystemInitialzed )
	{
		return ( false );
	}
		
#if ASYNC_QUICK_FILESYS

	CAsyncFileHandle *p_handle = CAsyncFileLoader::sOpen(filename, false);
	if (p_handle)
	{
		p_handle->Load(addr);
		p_handle->WaitForIO();
		CAsyncFileLoader::sClose(p_handle);
	}
	else
	{
		Dbg_MsgAssert(0, ("Didn't get handle pointer from async filesys for file %s", filename));
		return false;
	}

#else
	Dbg_MsgAssert( gFilesysIOPStreamBuffer,( "IOP stream buffer not initialized" ));

	if ( !gpHed )
	{
		return ( false );
	}

	SHedFile *tempHed = FindFileInHed( filename, gpHed );
	if ( !tempHed )
	{
		Dbg_MsgAssert( 0,( "File cannot be loaded quickly." ));
		return ( false );
	}
	uint32 bytesRequired = tempHed->GetFileSize();
//	uint32 offsetWithinBuffer = tempHed->Offset & ( SECTOR_SIZE - 1 );
	if ( tempHed->Offset & ( SECTOR_SIZE - 1 ) )
	{
		int i;
		for ( i = 0; i != -1; i++ )
		{
			printf( "fix wad so all files start on 2048 boundary.\n" );
		}
	}

	if (CAsyncFileLoader::sAsyncInUse())
	{
		CAsyncFileLoader::sWaitForIOEvent(true);
		Dbg_Message("************************ Can't do a normal read when async filesystem is in use");
		//Dbg_MsgAssert(0, ("Can't do a normal read when async filesystem is in use"));
	}

	uint32 lsn = gWadLSN;
	lsn += tempHed->Offset / SECTOR_SIZE;

	uint8 *pEEBuffer = new uint8[ FILESYS_CD_READ_SIZE + IOP_TO_EE_BUFFER_ALLIGNMENT ];

	uint8 *pData = pEEBuffer;
	pData = ( uint8 * )( ( ( int )pEEBuffer + ( IOP_TO_EE_BUFFER_ALLIGNMENT - 1 ) ) & ~( IOP_TO_EE_BUFFER_ALLIGNMENT - 1 ) );

	uint32 numSectorsToRead;
	uint32 err;
	uint32 numBytesToCopy;
	uint32 i;
	uint32 *pAI;
	uint32 *pDI;
	if ( gStreaming )
	{
		if ( !sceCdStSeek( lsn ) )
		{
			StartStreaming( lsn );
		}
	}
	else
	{
		StartStreaming( lsn );
	}
	while ( bytesRequired )
	{
		if ( /*offsetWithinBuffer +*/ bytesRequired < FILESYS_CD_READ_SIZE )
		{
			numBytesToCopy = bytesRequired;
			numSectorsToRead = ( ( /*offsetWithinBuffer +*/ bytesRequired ) / SECTOR_SIZE );
			if ( bytesRequired & ( SECTOR_SIZE - 1 ) )
			 numSectorsToRead++;
		}
		else
		{
			numBytesToCopy = FILESYS_CD_READ_SIZE;// - offsetWithinBuffer;
			numSectorsToRead = FILESYS_NUM_SECTORS_PER_READ;
		}
		
		// read in a chunk at a time ( 50 sectors? 100 sectors? )
		uint32 sectorsRead;
		sectorsRead = sceCdStRead( numSectorsToRead, ( uint32 * )pData, STMBLK, &err );
		
		int numErrors = 0;
		while ( sectorsRead != numSectorsToRead )
		{
			printf("QuickLoad: Could only read %d of the %d sectors for file %s\n", sectorsRead, numSectorsToRead, filename);
			sceCdStSeek( lsn );
			sectorsRead = sceCdStRead( numSectorsToRead, ( uint32 * )pData, STMBLK, &err );
			if ( numErrors++ >= 12 )
			{
				// figure out what the problem is using sceCDGetError( ) and either try to
				// recover or put up an error screen or something?
				HandleCDStreamingError( lsn );
				numErrors = 0;
			}
		}

		pAI = ( uint32 * )( addr );
		pDI = ( uint32 * )( pData );
		// copy the data from the buffer to the given address:
		for ( i = 0; i < ( numBytesToCopy >> 2 ); i++ )
		{
			*pAI++ = *pDI++;
		}
		for ( i = 0; i < ( numBytesToCopy & 3 ); i++ )
		{
			//addr[ 1 + ( numBytesToCopy & ~3 ) + i ] = pData[ 1 + ( numBytesToCopy & ~3 ) + i ];
			addr[  ( numBytesToCopy & ~3 ) + i ] = pData[ ( numBytesToCopy & ~3 ) + i ];
		}
		
		addr += numBytesToCopy;
		bytesRequired -= numBytesToCopy;
		
		lsn += numSectorsToRead;
//		offsetWithinBuffer = 0;
	}
//	sceCdStPause( );

// Mick: 8/28/02 - I added the next line
// the aim is to stop streaming now, so we don't have to do the extra call to
// sceCdStSeek  for the next call to this functions
// The speeds up the loading of skaters by 20%
	StopStreaming();

	delete pEEBuffer;
#endif // ASYNC_QUICK_FILESYS

	return ( true );
}

static void* cdFopen(const char *fname, const char *access)
{
	
	
	SHedFile *pHd=FindFileInHed(fname, gpHed);
	if (!pHd)
	{
		return NULL;
	}

	skyFile *fp = lock_skyfile();

	if (!fp)
	{
		return (NULL);
	}

	int mode = skyTransMode(access);
	if (!mode)
	{
		unlock_skyfile( fp );
		return (NULL);
	}

	fp->pFilename=fname;
	fp->WadOffset=pHd->Offset;					
	fp->SOF=pHd->FileSize;
	fp->POS=0;
	fp->gdfs=0;
#if ASYNC_HOST_FILESYS
	fp->p_async_file = NULL;
#endif
	/* Initialise the buffer to show nothing buffered */
	fp->bufferPos = READBUFFERSIZE;
	
	/* SCE_CREAT & !SCE_TRUNC mean seek to end of file */
	//if (!((mode & SCE_CREAT) && !(mode & SCE_TRUNC)))
	//{
	//	fp->POS = fp->SOF;
	//}
	
	s_open_files++;

	return ((void *)fp);
}

static int cdFclose( void* fptr )
{
	
	
	skyFile* fp = (skyFile*)fptr;

	Dbg_AssertPtr( fptr );


	if ( fp && s_open_files )
	{
		unlock_skyfile( fp );
        
		s_open_files--;

		return 0;
	}

	return -1;
}

static bool cdFexist( const char* name )
{
	
	if (FindFileInHed(name, gpHed))
	{
		return true;
	}	
	else
	{
		return false;
	}	
}

static size_t cdFread(void *addr, size_t size, size_t count, void *fptr)
{
	
	skyFile     *fp = (skyFile *)fptr;
	size_t      numBytesToRead = size * count;
	int         bytesRead, bytesRead2;

	bytesRead = 0;

	StopStreaming( );

//	char * after = (char*)addr + numBytesToRead; 							   

															  

	/* Trim number of bytes for the size of the file */
	if ((fp->POS + (int32)numBytesToRead) > fp->SOF)
	{
		numBytesToRead = fp->SOF - fp->POS;
	}

	
	/* First try and use the buffer */
	if ((fp->bufferPos < READBUFFERSIZE) &&
		(bytesRead < (int32)numBytesToRead))
	{
		/* Pull from the buffer */
		if (numBytesToRead < (READBUFFERSIZE-fp->bufferPos))
		{
			/* Can satisfy entirely from buffer */
			bytesRead = numBytesToRead;
		}
		else
		{
			/* Pull as much as possible from the buffer */
			bytesRead = READBUFFERSIZE-fp->bufferPos;
		}

		/* Copy it */
		memcpy(addr, &fp->readBuffer[fp->bufferPos], bytesRead);

		/* Update target address and source address */
		addr = (void *)((uint8 *)addr + bytesRead);
		fp->bufferPos += bytesRead;
		fp->POS += bytesRead;
	}

	/* If next bit is bigger than a buffer, read it directly and ignore the
	 * buffer.
	 */
	if ((numBytesToRead-bytesRead) > 0)
	{
		if ((numBytesToRead-bytesRead) >= READBUFFERSIZE)
		{
			bytesRead2 = (numBytesToRead-bytesRead);
			//bytesRead2 = sceRead(fp->gdfs, addr, bytesRead2);
			Dbg_MsgAssert(WadId>=0,("Bad WadId"));	
			//Dbg_Message("Seeking to LSN %d", fp->WadOffset+fp->POS);
			sceLseek(WadId, fp->WadOffset+fp->POS, SCE_SEEK_SET);
			bytesRead2=sceRead(WadId,addr,bytesRead2);
			
			if (bytesRead2 < 0)
			{
				bytesRead2 = 0;
			}
		}
		else
		{
			/* Go via the buffer */
			//sceRead(fp->gdfs, fp->readBuffer, READBUFFERSIZE);
			Dbg_MsgAssert(WadId>=0,("Bad WadId"));	
			//Dbg_Message("Seeking to LSN %d", fp->WadOffset+fp->POS);
			sceLseek(WadId, fp->WadOffset+fp->POS, SCE_SEEK_SET);
			sceRead(WadId,fp->readBuffer, READBUFFERSIZE);

			
			bytesRead2 = (numBytesToRead-bytesRead);
			memcpy(addr, fp->readBuffer, bytesRead2);
			fp->bufferPos = bytesRead2;
		}
		fp->POS += bytesRead2;
		bytesRead += bytesRead2;
	}

	return (bytesRead/size);
}

static size_t cdFwrite( const void *addr, size_t size, size_t count, void *fptr )
{
	
	skyFile  *fp = (skyFile *)fptr;
	Dbg_AssertPtr( fptr );
	
	fp->POS+=size*count;			 
	fp->SOF=fp->POS;
	return count;
}

static int cdFseek(void *fptr, long offset, int origin)
{
	

	skyFile      *fp = (skyFile *)fptr;
	int32      oldFPos, bufStart;
	bool       noBuffer = FALSE;

	Dbg_AssertPtr( fptr );

	oldFPos = fp->POS;
	bufStart = oldFPos - fp->bufferPos;
	if (fp->bufferPos == READBUFFERSIZE) noBuffer = TRUE;
	fp->bufferPos = READBUFFERSIZE;

	switch (origin)
	{
		case SEEK_CUR:
		{            
			/* Does the seek stay in the buffer */
			if ((oldFPos + offset >= bufStart) &&
				(oldFPos + offset < bufStart+READBUFFERSIZE))
			{
				fp->bufferPos = (oldFPos + offset) - bufStart;
				fp->POS += offset;
			}
			else
			{
				fp->POS+=offset;
				if (fp->POS<0 || fp->POS>fp->SOF)
				{
					fp->POS=-1;
					Dbg_MsgAssert(0,("Bad offset sent to cdFseek (SEEK_CUR) offset=%ld pos=%d size=%d",offset,fp->POS-offset,fp->SOF));
				}	
			}
			break;
		}
		case SEEK_END:
		{
			fp->POS=fp->SOF-offset;
			if (fp->POS<0 || fp->POS>fp->SOF)
			{
				fp->POS=-1;
				Dbg_MsgAssert(0,("Bad offset sent to cdFseek (SEEK_END)"));			
			}	
			break;
		}
		case SEEK_SET:
		{
			fp->POS=offset;
			if (fp->POS<0 || fp->POS>fp->SOF)
			{
				fp->POS=-1;
				Dbg_MsgAssert(0,("Bad offset sent to cdFseek (SEEK_SET)"));			
			}	
			break;
		}
		default:
		{
			return (-1);
		}
	}

	if (noBuffer)
		fp->bufferPos = READBUFFERSIZE;

	if (fp->POS == -1)
	{
		/* This may not be valid */
		fp->POS = oldFPos;
		fp->bufferPos = READBUFFERSIZE;
		return (-1);
	}

	return (0);
}

static char * cdFgets(char *buffer, int maxLen, void *fptr)
{
	
	Dbg_MsgAssert(0,("fgets not done yet"));
	return NULL;
}

static int cdFputs( const char *buffer, void *fptr)
{
	
	Dbg_MsgAssert(0,("Cannot fputs to CD"));
	return 0;
}

static int cdFeof( void* fptr )
{
	

	skyFile  *fp = (skyFile*)fptr;

	Dbg_AssertPtr( fptr );

	return ( fp->POS >= fp->SOF) ;
}

static int cdFflush( void* )
{
	
	
	return 0;
}



/******************************************************************/
/* skyFopen                                                       */
/*                                                                */
/* On entry   : Filename, access mode                             */
/*                                                                */
/*                                                                */
/******************************************************************/

static void* skyFopen(const char *fname, const char *access)
{
	

	skyFile*	fp;
	int         mode;

	StopStreaming( );

	/* Allocate structure for holding info */
	fp = lock_skyfile();

	if (!fp)
	{
		return (NULL);
	}

	mode = skyTransMode(access);
	if (!mode)
	{
		return (NULL);
	}

#if ASYNC_HOST_FILESYS
	fp->p_async_file = CAsyncFileLoader::sOpen(fname, true);

	if (!fp->p_async_file)
	{
		unlock_skyfile( fp );
		return (NULL);
	}

	// Get file size (will come up with better way)
	fp->SOF = fp->p_async_file->GetFileSize();
	fp->POS = 0;
	//fp->p_async_file->Seek(0, SEEK_END);
	//fp->SOF = fp->p_async_file->WaitForIO();
	//fp->p_async_file->Seek(0, SEEK_SET);
	//fp->POS = fp->p_async_file->WaitForIO();
#else
	char      name[256];
	char*		nameptr;

	/* First manipulate the filename into a Sky friendly name */
	if (strchr(fname, ':'))
	{
		strncpy(name, fname, 255);
	}
	else
	{
#ifdef SCE_11
		strcpy(name, "sim:");
		strncpy(&name[4], fname, 251);
#else
		strcpy(name, "host:");
		strncpy(&name[5], fname, 250);
#endif       
	}
	/* force null termination */
	name[255] = 0;

	nameptr = name;
	while(*nameptr)
	{
		if (*nameptr == '\\') *nameptr = '/';
		nameptr++;
	}

	fp->gdfs = sceOpen(name, mode);

	if (fp->gdfs < 0)
	{
		unlock_skyfile( fp );
		return (NULL);
	}

	/* We seek to the end of the file to get size */
	fp->SOF = fp->POS = sceLseek(fp->gdfs, 0, SCE_SEEK_END);
	if (fp->SOF< 0)
	{
		sceClose(fp->gdfs);
		unlock_skyfile( fp );
		return (NULL);
	}
	/* SCE_CREAT & !SCE_TRUNC mean seek to end of file */
	if (!((mode & SCE_CREAT) && !(mode & SCE_TRUNC)))
	{
		fp->POS = sceLseek(fp->gdfs, 0, SCE_SEEK_SET);

		if (fp->POS < 0)
		{
			sceClose(fp->gdfs);
			unlock_skyfile( fp );
			
			return (NULL);
		}
	}
#endif

	/* Initialise the buffer to show nothing buffered */
	fp->bufferPos = READBUFFERSIZE;
	strncpy( fp->filename, fname, MAX_PFILESYS_NAME_SIZE );
	s_open_files++;

	return ((void *)fp);
}


/******************************************************************/
/* skyFclose                                                      */
/*                                                                */
/******************************************************************/

static int skyFclose( void* fptr )
{
	

	skyFile* fp = (skyFile*)fptr;

	Dbg_AssertPtr( fptr );

	if ( fp && s_open_files )
	{
#if ASYNC_HOST_FILESYS
		CAsyncFileLoader::sClose(fp->p_async_file);
		fp->p_async_file = NULL;
#else
		sceClose( fp->gdfs );
#endif
		unlock_skyfile( fp );
        
		s_open_files--;

		return 0;
	}

	return -1;
}

/******************************************************************/
/* skyFexist                                                      */
/*                                                                */
/******************************************************************/

static bool skyFexist( const char* name )
{
	

	void* res = File::Open( name, "r" );

	if ( res )
	{
		File::Close( res );
		return TRUE;
	}

	return FALSE;
}

/******************************************************************/
/* skyFread                                                       */
/*                                                                */
/* On entry : Address to read to, block size, block count, file   */
/* On exit  : Number of bytes read                                */
/*                                                                */
/******************************************************************/

static size_t skyFread(void *addr, size_t size, size_t count, void *fptr)
{
	
	
	skyFile     *fp = (skyFile *)fptr;
	size_t      numBytesToRead = size * count;
	int         bytesRead = 0;

	StopStreaming( );

	/* Trim number of bytes for the size of the file */
	if ((fp->POS + (int32)numBytesToRead) > fp->SOF)
	{
		numBytesToRead = fp->SOF - fp->POS;
	}

#if ASYNC_HOST_FILESYS
	fp->p_async_file->Read(addr, size, count);
	bytesRead = fp->p_async_file->WaitForIO();
	fp->POS += bytesRead;
#else
	int bytesRead2;

	/* First try and use the buffer */
	if ((fp->bufferPos < READBUFFERSIZE) &&
		(bytesRead < (int32)numBytesToRead))
	{
		/* Pull from the buffer */
		if (numBytesToRead < (READBUFFERSIZE-fp->bufferPos))
		{
			/* Can satisfy entirely from buffer */
			bytesRead = numBytesToRead;
		}
		else
		{
			/* Pull as much as possible from the buffer */
			bytesRead = READBUFFERSIZE-fp->bufferPos;
		}

		/* Copy it */
		memcpy(addr, &fp->readBuffer[fp->bufferPos], bytesRead);

		/* Update target address and source address */
		addr = (void *)((uint8 *)addr + bytesRead);
		fp->bufferPos += bytesRead;
		fp->POS += bytesRead;
	}

	/* If next bit is bigger than a buffer, read it directly and ignore the
	 * buffer.
	 */
	if ((numBytesToRead-bytesRead) > 0)
	{
		if ((numBytesToRead-bytesRead) >= READBUFFERSIZE)
		{
			bytesRead2 = (numBytesToRead-bytesRead);
			bytesRead2 = sceRead(fp->gdfs, addr, bytesRead2);
			if (bytesRead2 < 0)
			{
				bytesRead2 = 0;
			}
		}
		else
		{
			/* Go via the buffer */
			sceRead(fp->gdfs, fp->readBuffer, READBUFFERSIZE);
			bytesRead2 = (numBytesToRead-bytesRead);
			memcpy(addr, fp->readBuffer, bytesRead2);
			fp->bufferPos = bytesRead2;
		}
		fp->POS += bytesRead2;
		bytesRead += bytesRead2;
	}
#endif

	return (bytesRead/size);
}

/******************************************************************/
/* skyFwrite                                                      */
/*                                                                */
/* On entry : Address to write from, block size, block count, file*/
/* On exit  : Number of bytes written                             */
/*                                                                */
/******************************************************************/

static size_t skyFwrite( const void *addr, size_t size, size_t count, void *fptr )
{
	

	int         bytesWritten;
	skyFile*	fp = (skyFile*)fptr;
	int32     numBytesToWrite = size * count;

	Dbg_AssertPtr( addr );
	Dbg_AssertPtr( fptr );

	bytesWritten = sceWrite( fp->gdfs, (void*)addr, numBytesToWrite );

	if (bytesWritten != -1)
	{
		fp->POS += bytesWritten;
		if (fp->POS > fp->SOF)
			fp->SOF = fp->POS;
		return (size>0?bytesWritten/size:0);
	}
	return (0);
}

/******************************************************************/
/* skyFseek                                                       */
/*                                                                */
/* On entry   : file to seek in, offset, how to seek	  		  */
/* On exit    : success/failure                                   */
/*                                                                */
/******************************************************************/

static int skyFseek(void *fptr, long offset, int origin)
{
	

	skyFile      *fp = (skyFile *)fptr;

	Dbg_AssertPtr( fptr );

	StopStreaming( );

#if ASYNC_HOST_FILESYS
	fp->p_async_file->Seek(offset, origin);
	fp->POS = fp->p_async_file->WaitForIO();
#else
	int32      oldFPos, bufStart;
	bool       noBuffer = FALSE;

	oldFPos = fp->POS;
	bufStart = oldFPos - fp->bufferPos;
	if (fp->bufferPos == READBUFFERSIZE) noBuffer = TRUE;
	fp->bufferPos = READBUFFERSIZE;

	switch (origin)
	{
		case SEEK_CUR:
		{            
			/* Does the seek stay in the buffer */
			if ((oldFPos + offset >= bufStart) &&
				(oldFPos + offset < bufStart+READBUFFERSIZE))
			{
				fp->bufferPos = (oldFPos + offset) - bufStart;
				fp->POS += offset;
			}
			else
			{
				fp->POS = sceLseek(fp->gdfs, oldFPos + offset, SCE_SEEK_SET);
			}
			break;
		}
		case SEEK_END:
		{
			fp->POS = sceLseek(fp->gdfs, offset, SCE_SEEK_END);
			break;
		}
		case SEEK_SET:
		{
			fp->POS = sceLseek(fp->gdfs, offset, SCE_SEEK_SET);
			break;
		}
		default:
		{
			return (-1);
		}
	}

	if (noBuffer)
		fp->bufferPos = READBUFFERSIZE;

	if (fp->POS == -1)
	{
		/* This may not be valid */
		fp->POS = oldFPos;
		fp->bufferPos = READBUFFERSIZE;
		return (-1);
	}
#endif
	return (0);
}

/******************************************************************/
/* skyFgets                                                       */
/*                                                                */
/* On entry   : Buffer to read into, max chars to read, file	  */
/* On exit    : Non negative value on success                     */
/*                                                                */
/******************************************************************/

static char * skyFgets(char *buffer, int maxLen, void *fptr)
{
	

	skyFile            *fp = (skyFile *) fptr;
	int32             i;
	int32             numBytesRead;

	Dbg_AssertPtr( buffer );
	Dbg_AssertPtr( fptr );

	i = 0;

	numBytesRead = skyFread(buffer, 1, maxLen - 1, fp);

	if (numBytesRead == 0)
	{
		return (NULL);
	}

	while (i < numBytesRead)
	{
		if (buffer[i] == '\n')
		{
			i++;

			buffer[i] = '\0';

			/* the file pointer needs */
			/* to be reset as skyFread */
			/* will have overshot the */
			/* first new line         */

			i -= numBytesRead;
			skyFseek(fp, i, SEEK_CUR);

			return (buffer);
		}
		else if ( buffer[i] == 0x0D )
		{
			if ((i < (numBytesRead - 1)) && (buffer[i + 1] == '\n'))
			{
				memcpy(&buffer[i], &buffer[i + 1], (numBytesRead - i - 1));
				numBytesRead--;
			}
			else
				i++;
		}
		else
			i++;
	}

	/*
	 * Don't return NULL because we could have read maxLen bytes
	 * without finding a \n
	 */
	return (buffer);
}

/******************************************************************/
/* skyFputs                                                       */
/*                                                                */
/* On entry   : Buffer to write from, file to write to            */
/* On exit    : Non negative value on success                     */
/*                                                                */
/******************************************************************/

static int skyFputs( const char *buffer, void *fptr)
{
	

	skyFile   *fp = (skyFile *)fptr;
	int i, j;

	Dbg_AssertPtr( buffer );
	Dbg_AssertPtr( fptr );

	i = strlen(buffer);
	j = sceWrite(fp->gdfs, (void*)buffer, i);

	if (j != -1)
	{
		fp->POS += j;
		if (fp->POS > fp->SOF)
			fp->SOF = fp->POS;
	}
	if ((j == -1) || (i != j))
	{
		return (EOF);
	}
	return (j);
}

/******************************************************************/
/* skyFeof                                                        */
/*                                                                */
/* On entry   : File to test for eof                              */
/* On exit    : Non zero if end of file reached                   */
/*                                                                */
/******************************************************************/

static int skyFeof( void* fptr )
{
	

	skyFile  *fp = (skyFile*)fptr;

	Dbg_AssertPtr( fptr );

	return ( fp->POS >= fp->SOF) ;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static int skyFflush( void* )
{
	

	return 0;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

long GetFileSize(void *pFP)
{
	
	Dbg_MsgAssert(pFP,("NULL pFP sent to GetFileSize"));

#if ASYNC_HOST_FILESYS
	skyFile      *fp = (skyFile *)pFP;
	if (!Config::CD() && fp->p_async_file)
	{
		return fp->p_async_file->GetFileSize();
	}
#endif

    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_get_file_size((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	return ((skyFile*)pFP)->SOF;
}

long GetFilePosition(void *pFP)
{
	
	Dbg_MsgAssert(pFP,("NULL pFP sent to GetFilePosition"));

    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_get_file_position((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	return ((skyFile*)pFP)->POS;
}

float GetPercentageRead(void *pFP)
{
	Dbg_MsgAssert(pFP,("NULL pFP sent to GetPercentageRead"));


    if (PreMgr::sPreEnabled())
    {
        int position = PreMgr::pre_get_file_position((PreFile::FileHandle *) pFP);
        int size = PreMgr::pre_get_file_size((PreFile::FileHandle *) pFP);

        if (PreMgr::sPreExecuteSuccess())
			return 100.0 * position / size;
    }

	return 100.0*((skyFile*)pFP)->POS/((skyFile*)pFP)->SOF;
}
	
void InstallFileSystem( void )
{
	// cd builds can only have one file open at any given time
	int max_open_files=Config::CD() ? 1:MAXOPENFILES;


	// initialize the locks on the sky files
	for ( int i = 0; i < max_open_files; i++ )
	{
		g_skyFile[i].locked = false;
	}
	
    
	if (Config::CD())
	{
		int	disk_type = SCECdDVD;
	
		// Initialise the CD
		printf("Initialising CD ...\n");
	//	int err1 = 
		sceCdInit(SCECdINIT);
		
		//int err2 = sceCdMmode(SCECdDVD);
	//	int err2 = 
		sceCdMmode(disk_type);
	
	
		// find the disk type, this might be different from what we intialized to 
		printf(" sceCdGetDiskType   ");
		int detected_disk_type= sceCdGetDiskType();
		switch(detected_disk_type)
		{
			case SCECdIllgalMedia:
			printf("Disk Type= IllgalMedia\n"); break;
			case SCECdPS2DVD:
			printf("Disk Type= PlayStation2 DVD\n"); break;
			case SCECdPS2CD:
			printf("Disk Type= PlayStation2 CD\n"); break;
			case SCECdPS2CDDA:
			printf("Disk Type= PlayStation2 CD with CDDA\n"); break;
			case SCECdPSCD:
			printf("Disk Type= PlayStation CD\n"); break;
			case SCECdPSCDDA:
			printf("Disk Type= PlayStation CD with CDDA\n"); break;
			case SCECdDVDV:
			printf("Disk Type= DVD video\n"); break;
			case SCECdCDDA:
			printf("Disk Type= CD-DA\n"); break;
			case SCECdDETCT:
			printf("Working\n"); break;
			case SCECdNODISC: 
			printf("Disk Type= No Disc\n"); break;
			default:
			printf("Disk Type= OTHER DISK\n"); break;
		}
		
		// If disk type has changed to a CD, then need to re-initialize it							   
		if (detected_disk_type == SCECdPS2CD)
		{
			#if __DVD_ONLY__
			printf( "*** ERROR - CD Detected, needs DVD.\n" );
			#else
			printf( "reinitializing for Ps2CD\n" );
			disk_type = SCECdCD;
			sceCdMmode(disk_type);
			printf( "done reinitializing\n" );
			#endif		
		}
		
	
	//	printf("'tis done. errs = %d, %d\n",err1,err2);
		
	
	
						   
						   
		// This next bit is essential for when making a bootable disc, ie one that will
		// boot on the actual PS rather than just the dev system.
	//#ifdef __NOPT_BOOTABLE__
		// K: Commented out __NOPT_BOOTABLE__, since it is set when __NOPT_CDROM__OLD is set.
	
		/* Reboot IOP, replace default modules  */
		char  path[128];
//		sprintf(path,"cdrom0:\\%sIOP\\IOPRP260.IMG;1",Config::GetDirectory()); 
//		sprintf(path,"host0:\\SKATE5\\DATA\\IOPMODULES\\DNAS280.IMG;1");
		
		
		sprintf(path,"cdrom0:\\%sIOP\\DNAS280.IMG;1",Config::GetDirectory()); 
		while ( !sceSifRebootIop((const char*) path) ); /* (Important) Unlimited retries */
		while( !sceSifSyncIop() );
		
		
		/* Reinitialize */
		sceSifInitRpc(0);
		sceCdInit(SCECdINIT);
		
	//    sceCdMmode(SCECdDVD);   /* Media: CD-ROM */
		sceCdMmode(disk_type);   /* Media: CD-ROM */
		
		sceFsReset();
	//#endif // __NOPT_BOOTABLE__

		printf("Opening hed file ...\n");
		// Open the hed file and load it into memory.
		gpHed = LoadHed( "SKATE5");
		
	/*	
		printf ("Size = %d, adjusted = %d\n",HedSize,(HedSize+2047)&~204);
	
		for (int i = HedSize; i< (HedSize+2047)&~204; i++)
		{
			if (p[i] != 0x55)
			{
				printf ("Overrun ? %d = %2x\n",i,p[i]);
				break;
			}
		}
	*/
		printf("Opening wad file ...\n");												 	
		// Open the wad and keep it open for future reference.	
		Dbg_MsgAssert(WadId==-1,("WadId not -1 ?"));					   
		while (WadId<0)
		{
			char  path[128];
			sprintf(path,"cdrom0:\\%sSKATE5.WAD;1",Config::GetDirectory()); 
			WadId=sceOpen(path,SCE_RDONLY);
			if (WadId<0)
			{
				printf("Retrying opening SKATE5.wad ...\n");
			}
		}		
		printf( "opened successfully\n" );
	}   
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

void UninstallFileSystem( void )
{
	if (Config::CD())
	{
		// Free the hed file buffer.
		Dbg_MsgAssert(gpHed,("NULL gpHed ?"));
		Mem::Free(gpHed);
		gpHed=NULL;
		
		Dbg_MsgAssert(WadId>=0,("Bad WadId"));
		sceClose(WadId);
		WadId=-1;
	
		// free IOP memory:
		if ( gFilesysIOPStreamBuffer )
		{
			sceSifFreeIopHeap( gFilesysIOPStreamBuffer ) ;
			gFilesysIOPStreamBuffer = 0;
		}
	}
}

void InitQuickFileSystem( void )
{

	// get the LSN to allow us to figure out where every other file is...
	sceCdlFILE wadInfo;
	char  path[128];
	sprintf(path,"\\%sSKATE5.WAD;1",Config::GetDirectory()); 
	while ( !sceCdSearchFile( &wadInfo, path ) )
	{
		printf("Retrying finding SKATE5.wad ...\n");
	}
	//printf( "after search file\n" );
	gWadLSN = wadInfo.lsn;

#if DISABLE_QUICK_FILESYSTEM
	return;
#endif

#if ASYNC_QUICK_FILESYS == 0
	// allocate our streaming buffer (iop side) for files:
	//printf( "before alloc heap\n" );	

	gFilesysIOPStreamBuffer = sceSifAllocIopHeap( FILESYS_IOP_BUFFER_SIZE );
	
	//printf( "after alloc heap: buffer %x of size %d\n", gFilesysIOPStreamBuffer, FILESYS_IOP_BUFFER_SIZE );	

	Dbg_MsgAssert( gFilesysIOPStreamBuffer,( "IOP stream buffer allocation failed." ));

	//printf( "before stream init\n" );	
	// initialize streaming capabilities and shit...
	while ( !sceCdStInit( FILESYS_NUM_SECTORS_IN_BUFFER, FILESYS_STREAM_BUFFER_NUM_PARTITIONS, (unsigned int)gFilesysIOPStreamBuffer ) )
	{
		printf( "trying to init CD stream\n" );
	}
	//printf( "after stream init\n" );

	StartStreaming( gWadLSN );
#endif

	gQuickFileSystemInitialzed = true;

	//printf( "after StartStreaming()\n" );
} // end of InitQuickFileSystem( )	


// We need to execute this after we have used the Cd Stream calls in other routines
void ResetQuickFileSystem( void )
{
#if ASYNC_QUICK_FILESYS == 0
	Dbg_MsgAssert( gFilesysIOPStreamBuffer,( "IOP stream buffer not initialized" ));

	// initialize streaming capabilities and shit...
	while ( !sceCdStInit( FILESYS_NUM_SECTORS_IN_BUFFER, FILESYS_STREAM_BUFFER_NUM_PARTITIONS, (unsigned int)gFilesysIOPStreamBuffer ) )
	{
		printf( "trying to init CD stream\n" );
	}
#endif
}

////////////////////////////////////////////////////////////////////
// Our versions of the ANSI file IO functions.  They call
// the PreMgr first to see if the file is in a PRE file.
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
// Exist                                                          
bool Exist( const char *filename )
{
    if (PreMgr::sPreEnabled())
    {
        bool retval = PreMgr::pre_fexist(filename);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	if (Config::CD())
	{
		return cdFexist(filename);
	}
	else
	{
		return skyFexist(filename);
	}	
}

////////////////////////////////////////////////////////////////////
// Open                                                          
void * Open( const char *filename, const char *access )
{
	bool use_pre = true;
#if ASYNC_HOST_FILESYS
	use_pre = Config::CD();
#endif

	// Don't use pre files if writing to disk (eg when writing parks)
	if (access[0]=='w')
	{
		use_pre=false;
	}
		
	if (Script::GetInt(CRCD(0xe99935c2,"show_filenames"),false))
	{
		printf (".... Open %s\n",filename);
	}


    if (use_pre && PreMgr::sPreEnabled())
    {
        void * retval = PreMgr::pre_fopen(filename, access);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	if (Config::CD())
	{
		return cdFopen(filename, access);
	}
	else
	{
		return skyFopen(filename, access);
	}	
}

////////////////////////////////////////////////////////////////////
// Close                                                          
int Close( void *pFP )
{
	bool use_pre = true;
#if ASYNC_HOST_FILESYS
	use_pre = Config::CD();
#endif

    if (use_pre && PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_fclose((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	if (Config::CD())
	{
		return cdFclose(pFP);
	}
	else
	{
		return skyFclose(pFP);
	}	
}

////////////////////////////////////////////////////////////////////
// Read                                                          
size_t Read( void *addr, size_t size, size_t count, void *pFP )
{
	bool use_pre = true;
#if ASYNC_HOST_FILESYS
	use_pre = Config::CD();
#endif

    if (use_pre && PreMgr::sPreEnabled())
    {
        size_t retval = PreMgr::pre_fread(addr, size, count, (PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	if (CAsyncFileLoader::sAsyncInUse())
	{
		CAsyncFileLoader::sWaitForIOEvent(true);
		Dbg_Message("************************ Can't do a normal read when async filesystem is in use");
		//Dbg_MsgAssert(0, ("Can't do a normal read when async filesystem is in use"));
	}

	if (Config::CD())
	{
		return cdFread(addr, size, count, pFP);
	}
	else
	{
		return skyFread(addr, size, count, pFP);
	}	
}

///////////////////////////////////////////////////////////////////////
// Read an Integer in PS2 (littleendian) format
// we just read it directly into memory...
size_t ReadInt( void *addr, void *pFP )
{
	return Read(addr,4,1,pFP);	
}

////////////////////////////////////////////////////////////////////
// Write                                                          
size_t Write( const void *addr, size_t size, size_t count, void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        size_t retval = PreMgr::pre_fwrite(addr, size, count, (PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	if (Config::CD())
	{
		return cdFwrite(addr, size, count, pFP);
	}
	else
	{
		return skyFwrite(addr, size, count, pFP);
	}	
}

////////////////////////////////////////////////////////////////////
// GetS                                                          
char * GetS( char *buffer, int maxlen, void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        char * retval = PreMgr::pre_fgets(buffer, maxlen, (PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	if (Config::CD())
	{
		return cdFgets(buffer, maxlen, pFP);
	}
	else
	{
		return skyFgets(buffer, maxlen, pFP);
	}	
}

////////////////////////////////////////////////////////////////////
// PutS                                                          
int PutS( const char *buffer, void *pFP )
{
    if (PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_fputs(buffer, (PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	if (Config::CD())
	{
		return cdFputs(buffer, pFP);
	}	
	else
	{
		return skyFputs(buffer, pFP);
	}	
}

////////////////////////////////////////////////////////////////////
// Eof                                                          
int Eof( void *pFP )
{
	bool use_pre = true;
#if ASYNC_HOST_FILESYS
	use_pre = Config::CD();
#endif

    if (use_pre && PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_feof((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	if (Config::CD())
	{
		return cdFeof(pFP);
	}
	else
	{
		return skyFeof(pFP);
	}	
}

////////////////////////////////////////////////////////////////////
// Seek                                                          
int Seek( void *pFP, long offset, int origin )
{
	bool use_pre = true;
#if ASYNC_HOST_FILESYS
	use_pre = Config::CD();
#endif

    if (use_pre && PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_fseek((PreFile::FileHandle *) pFP, offset, origin);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	if (Config::CD())
	{
		return cdFseek(pFP, offset, origin);
	}
	else
	{
		return skyFseek(pFP, offset, origin);
	}	
}

int32	Tell( void* pFP )
{
	skyFile* fp = (skyFile *) pFP;
	return fp->POS;
}

////////////////////////////////////////////////////////////////////
// Flush                                                          
int Flush( void *pFP )
{
	bool use_pre = true;
#if ASYNC_HOST_FILESYS
	use_pre = Config::CD();
#endif

    if (use_pre && PreMgr::sPreEnabled())
    {
        int retval = PreMgr::pre_fflush((PreFile::FileHandle *) pFP);
        if (PreMgr::sPreExecuteSuccess())
            return retval;
    }

	if (Config::CD())
	{
		return cdFflush(pFP);
	}
	else
	{
		return skyFflush(pFP);
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace File




================================================
FILE: Code/Sys/File/pip.cpp
================================================
///////////////////////////////////////////////////////////////////////////////////////
//
// pip.cpp		KSH 6 Feb 2002
//
// Pre-in-Place stuff
//
///////////////////////////////////////////////////////////////////////////////////////

// start autoduck documentation
// @DOC pip
// @module pip | None
// @subindex Scripting Database
// @index script | pip

// TODO
// Use Shrink to shrink the memory block to get rid of the 2048 padding at the end.
// (This will only be effective when the bottom-up heap is being used though)

// TODO but non-essential:
// Make Unload work if passed the pointer.
// Make GetFileSize work if passed the pointer.

// 11Dec02 JCB - Andre wanted me to use tolower() instead of strncmpi()
#include 

#include 
#include 
#include 
#include 
#include 
#include 

namespace Pip
{

#define IN_PLACE_DECOMPRESSION_MARGIN 3072


enum EQuadAligned
{
	NOT_QUAD_WORD_ALIGNED=0,
	QUAD_WORD_ALIGNED
};


#define CURRENT_PRE_VERSION	0xabcd0003			// as of 3/14/2001 (and as of 2/12/2002)

struct SPreHeader
{
	int		mSize;
	int		mVersion;
	int		mNumFiles;
};


struct SPreContained
{
	uint32	mDataSize;
	uint32	mCompressedSize;
	uint16	mNameSize;

	// In makepre.cpp, mNameSize is stored in 4 bytes. When the pre is in memory though, I'm
	// borrowing the two high bytes to use as a usage indicator to indicate whether this
	// contained file is 'open', ie has had Load called on it. The count value is the number
	// of times the file has been opened using Load. Gets decremented when Unload is called.
	// (The LoadPre function checks that they are all zero to start with when a new pre is loaded)
	uint16  mUsage;

	// Mick - added space for a checksum of mpName
	// as otherwise we spend over five seconds at boot up in re-calculating checksums n^2 times	
	uint32 	mChecksum;
	
	// Keep mpName the last member of this struct, cos I calculate the space used by the other
	// members by subtracting &p->mDataSize from p->mpName.
	// mpName will probably not move anyway, cos this strcture is part of the pre file format.
	char	mpName[1];
};



static SPreContained *sSkipToNextPreContained(SPreContained *p_preContained, EQuadAligned quadWordAlignedData=QUAD_WORD_ALIGNED);
static SPreHeader 	 *sSkipOverPreName(const char *p_pre_name);
static SPreContained *sSeeIfFileIsInAnyPre(uint32 fileNameCRC);
#ifdef __NOPT_ASSERT__
static const char 	 *sGetPreName(SPreContained *p_contained_file);
#endif

#define MAX_PRE_FILES 100
// char* because each pre is prefixed with the name of the pre.
static char *spp_pre_files[MAX_PRE_FILES];

struct SUnPreedFile
{
	uint8 *mpFileData;
	uint32 mFileNameChecksum;
	uint32 mFileSize;
	uint32 mUsage;
};

#define MAX_UNPREED_FILES 200
static SUnPreedFile sp_unpreed_files[MAX_UNPREED_FILES];

// This class only exists so that I can declare an instance of it and hence
// use its constructor to initialise the above arrays.
// Saves me having to call an init function from somewhere early on in the code,
// which might get moved around later and cause problems.
// This way, the arrays are guaranteed to have got initialised even before main() is
// called. I think ...
class CInit
{
public:
	CInit()
	{
		int i;

		for (i=0; imDataSize;
	if (p_preContained->mCompressedSize)
	{
		total_data_size=p_preContained->mCompressedSize;
	}
	total_data_size=(total_data_size+3)&~3;

//	printf ("p_preContained = %p\n, total_data = %d, name = (%p) %s",p_preContained,total_data_size,p_preContained->mpName,p_preContained->mpName);
	
	uint32 p_next=(uint32)p_preContained->mpName;
	p_next+=p_preContained->mNameSize;
	if (quadWordAlignedData)
	{
		p_next=(p_next+15)&~15;
	}
	p_next+=total_data_size;

	return (SPreContained*)p_next;
}

static SPreHeader *sSkipOverPreName(const char *p_pre_name)
{
	Dbg_MsgAssert(p_pre_name,("NULL p_pre_name"));

	int len=strlen(p_pre_name)+1; // +1 for terminator
	len=(len+15)&~15;	// Round up to a multiple of 16
	return (SPreHeader*)(p_pre_name+len);
}

// Loads a pre file into memory. The name must have no path since the pre is
// assumed to be in the data\pre directory.
// Ie, a valid name would be "alc.pre"
void LoadPre(const char *p_preFileName)
{
	Dbg_MsgAssert(p_preFileName,("NULL p_preFileName"));

	// Check to see if the pre is already loaded, and return without doing anything if it is.
	for (int i=0; ioriginal_file_size ?"));
		#else
		File::Read(p_old_file_data+name_size, 1, original_file_size, p_file);
		#endif

		File::Close(p_file);
	}


	// Calculate the new buffer size required.
	// Note that even if no decompression is required, the new buffer will probably need to
	// be bigger than the old, because the contained files need to be moved so that they all start
	// on 16 byte boundaries. This is required by the collision code for example.

	uint32 new_pre_buffer_size=name_size;
	new_pre_buffer_size+=sizeof(SPreHeader);

	SPreHeader *p_pre_header=sSkipOverPreName(p_old_file_data);
	uint32 num_files=p_pre_header->mNumFiles;
	SPreContained *p_contained=(SPreContained*)(p_pre_header+1);
	for (uint32 f=0; fmUsage==0,("The file %s in %s has mUsage=%d ??",p_contained->mpName,p_preFileName,p_contained->mUsage));

		new_pre_buffer_size+=(uint32)p_contained->mpName-(uint32)p_contained;
		new_pre_buffer_size+=p_contained->mNameSize;

		new_pre_buffer_size=(new_pre_buffer_size+15)&~15;

		new_pre_buffer_size+=(p_contained->mDataSize+3)&~3;

		p_contained=sSkipToNextPreContained(p_contained,NOT_QUAD_WORD_ALIGNED);
	}

	// Need to add a small margin to prevent decompressed data overtaking the compressed data.
	new_pre_buffer_size+=IN_PLACE_DECOMPRESSION_MARGIN;

	// At this point we have:
	//
	// original_file_size	The exact size of the original pre file. Will be a multiple of 4.
	//
	// name_size			The size of the pre name, rounded up to a multiple of 4
	//
	// old_pre_buffer_size	The size of the memory buffer pointed to by p_old_file_data
	//						Equals name_size +  (original_file_size, rounded up to a multiple of 2048)
	//
	// new_pre_buffer_size	The required size of the new buffer, to contain the decompressed pre,
	//						with all the files moved to be at 16 byte boundaries.
	//

	#ifdef __NOPT_ASSERT__
	printf("Decompressing and rearranging pre file ...\n");
	#endif

	// Note: It does not matter if new_pre_buffer_size is not a multiple of 2048 any more.
	// old_pre_buffer_size was only a multiple of 2048 to ensure that the file loading
	// did not overrun the end of the buffer. I think file loading may only load a whole
	// number of sectors.


	// Reallocate the buffer.
	char *p_new_file_data=NULL;
	if (Mem::Manager::sHandle().GetContextDirection()==Mem::Allocator::vTOP_DOWN)
	{
		// If using the top-down heap expand the buffer downwards ...

		// p_new_file_data will become a pointer lower down in memory than p_old_file_data.
		// The old data pointed to by p_old_file_data will still be there.
		// The memory between p_new_file_data and p_old_file_data is all free to use. Hoorah!
		p_new_file_data=(char*)Mem::ReallocateDown(new_pre_buffer_size,p_old_file_data);
	}
	else
	{
		// If using the top-down heap expand the buffer upwards ...
		p_new_file_data=(char*)Mem::ReallocateUp(new_pre_buffer_size,p_old_file_data);
		Dbg_MsgAssert(p_new_file_data,("ReallocateUp failed!"));

		// Now need to move the data up so that it can be decompressed into the gap below.
		uint32 *p_source=(uint32*)(p_old_file_data+old_pre_buffer_size);
		uint32 *p_dest=(uint32*)(p_new_file_data+new_pre_buffer_size);
		// Loading backwards cos the destination overlaps the source.

		// Note: Did some timing tests to see if this was was slow, but it's not really.
		// To load all the pre files in the game, one after the other, takes an average of
		// 32.406 seconds when the top down heap is used. When using the bottom up, which
		// necessitates doing this copy, it takes 33.518 seconds.
		// So per pre file that isn't much. (There's about 60 pre files)
		Dbg_MsgAssert((old_pre_buffer_size&3)==0,("old_pre_buffer_size not a multiple of 4 ?"));
		uint32 num_longs=old_pre_buffer_size/4;
		for (uint32 i=0; imNumFiles=p_source_header->mNumFiles;
	p_dest_header->mVersion=p_source_header->mVersion;

	// Copy down each of the contained files, decompressing those that need it.
	SPreContained *p_source_contained=(SPreContained*)(p_source_header+1);
	SPreContained *p_dest_contained=(SPreContained*)(p_dest_header+1);
	for (uint32 f=0; fmDataSize=p_source_contained->mDataSize;
		p_dest_contained->mChecksum=p_source_contained->mChecksum;
		p_dest_contained->mCompressedSize=0; // The new file will not be compressed.
		p_dest_contained->mUsage=0;
		p_dest_contained->mNameSize=p_source_contained->mNameSize;
		for (int i=0; imNameSize; ++i)
		{
			p_dest_contained->mpName[i]=p_source_contained->mpName[i];
		}

		// Pre-calculate p_next_source_contained because decompression may (and often will)
		// cause the new data to overwrite the contents of p_source_contained.
		SPreContained *p_next_source_contained=sSkipToNextPreContained(p_source_contained,NOT_QUAD_WORD_ALIGNED);

		uint8 *p_source=(uint8*)(p_source_contained->mpName+p_source_contained->mNameSize);
		uint8 *p_dest=(uint8*)(p_dest_contained->mpName+p_dest_contained->mNameSize);
		p_dest=(uint8*)( ((uint32)p_dest+15)&~15 );

		if (p_source_contained->mCompressedSize)
		{
			uint32 num_bytes_decompressed=p_dest_contained->mDataSize;
			uint8 *p_end=DecodeLZSS(p_source,p_dest,p_source_contained->mCompressedSize);
			Dbg_MsgAssert(p_end==p_dest+num_bytes_decompressed,("Eh? DecodeLZSS wrote %d bytes, expected it to write %d",p_end-p_dest,num_bytes_decompressed));

			// For neatness, write zero's into the pad bytes at the end, otherwise they'll
			// be uninitialised data.
			while (num_bytes_decompressed & 3)
			{
				*p_end++=0;
				++num_bytes_decompressed;
			}
		}
		else
		{
			// Uncompressed, so just copy the data down.
			uint32 *p_source_long=(uint32*)p_source;
			uint32 *p_dest_long=(uint32*)p_dest;
			// mDataSize is not necessarily a multiple of 4, but the actual data will be
			// padded at the end so that it does occupy a whole number of long words.
			// So the +3 is to ensure that n is rounded up to the next whole number of longs.
			uint32 n=(p_source_contained->mDataSize+3)/4;

			for (uint32 i=0; imSize=(uint32)p_dest_contained-(uint32)p_dest_header;

	//printf("Wasted space = %d\n",new_pre_buffer_size-((uint32)p_dest_contained-(uint32)p_new_file_data));

	spp_pre_files[spare_index]=p_new_file_data;
	#ifdef __NOPT_ASSERT__
	printf("Done\n");
	#endif
}

// Removes the specified pre from memory. The name must have no path since the pre is
// assumed to be in the data\pre directory.
// Ie, a valid name would be "alc.pre"
//
// Won't do anything if the pre is gone already.
// Asserts if any of the files in the pre are still 'open' in that they haven't had Unload called.
bool UnloadPre(const char *p_preFileName)
{
	bool success = false;

	Dbg_MsgAssert(p_preFileName,("NULL p_preFileName"));

	for (int i=0; imNumFiles;
				SPreContained *p_contained=(SPreContained*)(p_pre_header+1);
				for (int f=0; fmUsage,("Tried to unload %s when the file %s contained within it was still open! (mUsage=%d)",spp_pre_files[i],p_contained->mpName,p_contained->mUsage));
					p_contained=sSkipToNextPreContained(p_contained);
				}
				#endif

				// Delete it.
				Mem::Free(spp_pre_files[i]);
				spp_pre_files[i]=NULL;

				// we've successfully unloaded a pre
				success = true;
			}
		}
	}

	// Not found, so nothing to do.
	return success;
}

// Searches each of the loaded pre files for the passed contained file.
// Returns NULL if not found.
static SPreContained *sSeeIfFileIsInAnyPre(uint32 fileNameCRC)
{
	for (int i=0; imNumFiles;
			SPreContained *p_contained=(SPreContained*)(p_pre_header+1);
			for (int f=0; fmpName) == p_contained->mChecksum,
				//("Checksum for %s (%x) not %x",p_contained->mpName,Crc::GenerateCRCFromString(p_contained->mpName),p_contained->mChecksum));
				//if ( Crc::GenerateCRCFromString(p_contained->mpName) == fileNameCRC )
				if ( p_contained->mChecksum == fileNameCRC )
				{
					return p_contained;
				}
				p_contained=sSkipToNextPreContained(p_contained);
			}
		}
	}
	return NULL;
}

#ifdef __NOPT_ASSERT__
// Finds which pre the passed file is contained in, and returns a pointer to the pre name.
// Returns "Unknown" if not found anywhere.
static const char *sGetPreName(SPreContained *p_contained_file)
{
	for (int i=0; imNumFiles;
			SPreContained *p_contained=(SPreContained*)(p_pre_header+1);
			for (int f=0; fmUsage;

		Dbg_MsgAssert(p_contained_file->mCompressedSize==0,("The file '%s' is stored compressed in %s !",p_fileName,sGetPreName(p_contained_file)));

		uint32 p_data=(uint32)p_contained_file->mpName+p_contained_file->mNameSize;
		p_data=(p_data+15)&~15;
		return (void*)p_data;
	}

	// Next, see if it is one of the unpreed files that is already loaded.
	for (int i=0; impFileData=p_file_data;
	p_new_unpreed_file->mFileSize=file_size;

	p_new_unpreed_file->mFileNameChecksum=filename_checksum;
	p_new_unpreed_file->mUsage=1;

	return p_file_data;
}

void Unload(uint32 fileNameCRC)
{
	// See if it is one of the unpreed files.
	#ifdef __NOPT_ASSERT__
	bool is_an_unpreed_file=false;
	#endif
	for (int i=0; impName,sGetPreName(p_contained_file)));

		// Decrement the usage.
		if (p_contained_file->mUsage)
		{
			--p_contained_file->mUsage;
		}
	}
}

// If the file is unpreed, this will decrement the usage and free the memory specially allocated
// for it if the usage has reached zero.
// If the file is in a loaded pre, it will decrement that files usage in the pre.
// If the file is both of the above, it will assert.
// If it is neither, it won't do anything.
void Unload(const char *p_fileName)
{
	Dbg_MsgAssert(p_fileName,("NULL p_fileName"));

	Unload( Crc::GenerateCRCFromString(p_fileName) );
}

uint32 GetFileSize(uint32 fileNameCRC)
{
	// See if it is one of the unpreed files.
	#ifdef __NOPT_ASSERT__
	bool is_an_unpreed_file=false;
	#endif
	for (int i=0; impName,sGetPreName(p_contained_file)));
		Dbg_MsgAssert(p_contained_file->mDataSize,("Zero mDataSize ??"));
		return p_contained_file->mDataSize;
	}

//	Dbg_MsgAssert(0,("File '%s' not found loaded anywhere",p_fileName));
	return 0;
}

uint32 GetFileSize(const char *p_fileName)
{
	Dbg_MsgAssert(p_fileName,("NULL p_fileName"));

	uint32 file_size = GetFileSize( Crc::GenerateCRCFromString(p_fileName) );

	if ( file_size == 0 )
	{
		Dbg_MsgAssert(0,("File '%s' not found loaded anywhere",p_fileName));
	}

	return file_size;
}

const char *GetNextLoadedPre(const char *p_pre_name)
{
	if (p_pre_name==NULL)
	{
		for (int i=0; imNumFiles;
				SPreContained *p_contained=(SPreContained*)(p_pre_header+1);
				for (int f=0; fmUsage)
					{
						return true;
					}
					p_contained=sSkipToNextPreContained(p_contained);
				}
			}
		}
	}

	return false;
}

// @script | LoadPipPre |
// @uparm "string" | filename
// @parmopt name | heap | 0 (checksum value) | Which heap to use.
// Possible values are TopDown or BottomUp
// If no heap is specified it will use whatever the current heap is.
bool ScriptLoadPipPre(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *p_filename;
	pParams->GetString(NONAME, &p_filename, Script::ASSERT);

	uint32 chosen_heap=0;
	pParams->GetChecksum("Heap",&chosen_heap);
	switch (chosen_heap)
	{
		case 0:
			chosen_heap = 0;
			// Use whatever the current heap is.
			break;
		case 0x477fc6de: // TopDown
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
			break;
		case 0xc80bf12d: // BottomUp
			Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
			break;
		default:
			Dbg_Warning("Heap '%s' not supported by LoadPipPre, using current heap instead.",Script::FindChecksumName(chosen_heap));
			chosen_heap=0;
			break;
	}

	LoadPre(p_filename);

	if (chosen_heap)
	{
		Mem::Manager::sHandle().PopContext();
	}

	return true;
}

// @script | UnLoadPipPre |
// @uparm "string" | filename
bool ScriptUnloadPipPre(Script::CStruct *pParams, Script::CScript *pScript)
{
	const char *p_filename;
	pParams->GetString(NONAME, &p_filename, Script::ASSERT);

	return UnloadPre(p_filename);
}

// @script | DumpPipPreStatus | Prints the status of the currently loaded pre files.
// @flag ShowPreedFiles | Lists all the files contained within each pre, together with their usage value.
// @flag ShowUnPreedFiles | Lists all the files currently open which were not in a pre,
// so were individually opened.
// @flag ShowOnlyOpenFiles | When listing the files in each pre, only display those whose usage is
// greater than zero.
bool ScriptDumpPipPreStatus(Script::CStruct *pParams, Script::CScript *pScript)
{
	bool show_preed_files=pParams->ContainsFlag("ShowPreedFiles");
	bool show_unpreed_files=pParams->ContainsFlag("ShowUnPreedFiles");
	bool show_only_open_files=pParams->ContainsFlag("ShowOnlyOpenFiles");

	printf("Currently loaded pre files:\n");

	for (int i=0; imNumFiles;
				SPreContained *p_contained=(SPreContained*)(p_pre_header+1);
				for (int f=0; fmUsage)
					{
						printf("Usage:%d  File: %s\n",p_contained->mUsage,p_contained->mpName);
						// Delay so that printf does not break when there are lots of files.
						for (volatile int pp=0; pp<100000; ++pp);
					}

					p_contained=sSkipToNextPreContained(p_contained);
				}
			}
		}
	}

	if (show_unpreed_files)
	{
		printf("Currently loaded un-preed files:\n");

		for (int i=0; iLoadFile(p_fileName,&file_size, p_dest);
	if (!p_file_data)
	{
		// nope, so try loading "Quickly" (basically if we are on a CD)
		file_size = File::CanFileBeLoadedQuickly( p_fileName );
		if (file_size)
		{
			// It can be loaded quickly.
			if (!p_dest)
			{
				p_file_data=(uint8*)Mem::Malloc((file_size+2047)&~2047);
			}
			else
			{
				p_file_data = (uint8*)p_dest;
			}

			bool fileLoaded = File::LoadFileQuicklyPlease( p_fileName, p_file_data );
			if ( !fileLoaded )
			{
				Dbg_MsgAssert( 0,( "File %s failed to load quickly.\n", p_fileName));
				if (!p_dest)
				{
					Mem::Free(p_file_data);
				}
				p_file_data=NULL;
			}
		}
		else
		{
			// can't load quickly, so probably loading from the PC
			// Open the file & get its file size

			void *p_file = File::Open(p_fileName, "rb");
			if (!p_file)
			{
				Dbg_MsgAssert(0,("Could not open file '%s'",p_fileName));
			}

			file_size=File::GetFileSize(p_file);
			if (!file_size)
			{
				Dbg_MsgAssert(0,("Zero file size for file %s",p_fileName));
			}

			if (!p_dest)
			{
				// Allocate memory.
				// Just to be safe, allocate a buffer of size file_size rounded up to the
				// next multiple of 2048, cos maybe loading a file off CD will always load
				// whole numbers of sectors.
				// Haven't checked that though.
				p_file_data=(uint8*)Mem::Malloc((file_size+2047)&~2047);
				Dbg_MsgAssert(p_file_data,("Could not allocate memory for file %s",p_fileName));
			}
			else
			{
				p_file_data = (uint8*)p_dest;
			}
			// Load the file into memory then close the file.
			#ifdef __NOPT_ASSERT__
			long bytes_read=File::Read(p_file_data, 1, file_size, p_file);
			Dbg_MsgAssert(bytes_read<=file_size,("bytes_read>file_size ?"));
			#else
			File::Read(p_file_data, 1, file_size, p_file);
			#endif

			File::Close(p_file);
		}
		// Shrink memory back down to accurate usage - saves 43K total in the school!!!
		if (!p_dest && p_file_data)
		{
			Mem::ReallocateShrink(file_size,p_file_data);
		}
	}
	if (p_filesize)
	{
		*p_filesize = file_size;
	}
	// If we specified a destination, then make sure we did not overflow it
	if (p_dest)
	{
		Dbg_MsgAssert(((file_size + 2047)&~2047) < maxSize,("file size (%d) overflows buffer (%d) for %s\n",file_size,maxSize,p_fileName));
	}
	return (void *)p_file_data;
}


}





================================================
FILE: Code/Sys/File/pip.h
================================================
#ifndef	__FILE_PIP_H
#define	__FILE_PIP_H

#ifndef __CORE_DEFINES_H
#include 
#endif

namespace Script
{
	class CStruct;
	class CScript;
}	

namespace Pip
{
void	LoadPre(const char *p_preFileName);
bool	UnloadPre(const char *p_preFileName);

void*	Load(const char *p_fileName);
void	Unload(const char *p_fileName);
uint32	GetFileSize(const char *p_fileName);

// GJ:  sometimes it's useful to do this
// by checksum, so that we don't have to keep 
// the full filename string hanging around
void	Unload(uint32 fileNameCRC);
uint32	GetFileSize(uint32 fileNameCRC);

const char *GetNextLoadedPre(const char *p_pre_name=NULL);
bool PreFileIsInUse(const char *p_pre_name);

bool ScriptLoadPipPre(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptUnloadPipPre(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDumpPipPreStatus(Script::CStruct *pParams, Script::CScript *pScript);


}

#endif



================================================
FILE: Code/Sys/McMan.h
================================================
/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS														**
**																			**
**	Module:			MemCard  (Mc)											**
**																			**
**	File name:		sys/mcman.h												**
**																			**
**	Created: 		03/06/2001	-	spg										**
**																			**
*****************************************************************************/

#ifndef __SYS_MCMAN_H
#define __SYS_MCMAN_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#ifdef __PLAT_NGC__
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mc
{

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class Card;

class DateTime
{
public:
	unsigned char 	m_Seconds;
	unsigned char	m_Minutes;
	unsigned char 	m_Hour;
	unsigned char 	m_Day;
	unsigned char 	m_Month;
	unsigned short	m_Year;
};

class File : public Lst::Node< File >
{
public:
	enum
	{
		mMODE_READ		= 0x0001,
		mMODE_WRITE		= 0x0002,
		mMODE_CREATE	= 0x0004,
	};

	enum FilePointerBase
	{
		BASE_START,
		BASE_CURRENT,
		BASE_END,
	};

	enum
	{
		mATTRIB_READABLE		= 0x0001,
		mATTRIB_WRITEABLE		= 0x0002,
		mATTRIB_EXECUTABLE		= 0x0004,
		mATTRIB_DUP_PROHIBITED	= 0x0008,
		mATTRIB_DIRECTORY		= 0x0010,
		mATTRIB_CLOSED			= 0x0020,
		mATTRIB_PDA_APP			= 0x0040,
		mATTRIB_PS1_FILE		= 0x0080,
	};
	enum
	{
		vMAX_FILENAME_LEN = 31,
		vMAX_DISPLAY_FILENAME_LEN	= 63
	};

					File( int fd, Card* card );
					~File();
					
		
	char			m_Filename[vMAX_FILENAME_LEN+1];
	DateTime		m_Created;	
	DateTime		m_Modified;
	unsigned int	m_Size;
	unsigned short	m_Attribs;

	bool			Close( void );
	int				Write( void* data, int len );
	int				Read( void* data, int len );
	int				Seek( int offset, FilePointerBase base );
	bool			Flush( void );

#	ifdef __PLAT_XBOX__
	char			m_DisplayFilename[vMAX_DISPLAY_FILENAME_LEN+1];
#	endif

#	ifdef __PLAT_NGC__
	CARDFileInfo	m_file_info;
	int				m_file_number;		// Set when opened.
	
	bool			m_need_to_flush;
	int				m_write_offset;
	uint8		   *mp_write_buffer;
	int				m_read_offset;
	uint8		   *mp_read_buffer;
#	endif

private:
	int				m_fd;
	Card*			m_card;
};

class  Card  : public Spt::Class
{
public:
	friend class Manager;

	enum
	{
		vERROR_FORMATTED,
		vERROR_UNFORMATTED,
		vINSUFFICIENT_SPACE,
		vINVALID_PATH,
		vNO_CARD,
		vACCESS_ERROR,
		vSUBDIR_NOT_EMPTY,
		vTOO_MANY_OPEN_FILES,
		vUNKNOWN,
	};

	enum
	{
		vDEV_NONE,
		vDEV_PS1_CARD,
		vDEV_PS2_CARD,
		vDEV_POCKET_STATION,
		vDEV_XBOX_CARD,
		vDEV_XBOX_HARD_DRIVE,
		vDEV_GC_CARD,
	};

	enum
	{
		vMAX_FILES_PER_DIRECTORY = 32,
	};

	bool			MakeDirectory( const char* dir_name );
	bool			DeleteDirectory( const char* dir_name );
	const char	   *ConvertDirectory( const char* dir_name );
	
	bool			ChangeDirectory( const char* dir_name );
	
	File*			Open( const char* filename, int mode, int size = 0 );	// Size used by NGC for file creation.
	
	bool			Delete( const char* filename );
	bool			Rename( const char* old_name, const char* new_name );

	bool			Format( void );
	bool			Unformat( void );
	bool			IsFormatted( void );
	
	int				GetSlot( void )			{ return m_slot; }
	int				GetDeviceType( void );
	int				GetNumFreeClusters( void );
	int				GetNumFreeEntries( const char* path );

	void			SetError( int error ) { m_last_error = error; }
	int				GetLastError( void ) { return m_last_error; }

	bool			GetFileList( const char* mask, Lst::Head< File > &file_list );

#	ifdef __PLAT_NGC__
	int				m_mem_size;				// Overall memory size of unit.
	int				m_sector_size;			// Sector size of unit.
	char*			mp_work_area;			// Used also as a flag: == NULL means not yet mounted.
	bool			m_broken;
	int 			CountFilesLeft();			// Required to check if there are enough files left to allow a game save.
	bool			IsForeign( void );
	bool			IsBadDevice( void );
#	endif

#	ifdef __PLAT_XBOX__
	void			SetAsHardDrive();
#	endif // #ifdef __PLAT_XBOX__

private:
	int				m_port;
	int				m_slot;
	int				m_last_error;

#	ifdef __PLAT_XBOX__
	char			m_mounted_drive_letter;	// Used also as a flag: == 0 means not yet mounted.
	enum
	{
		vDIRECTORY_NAME_BUF_SIZE=64
	};	
#	endif // #ifdef __PLAT_XBOX__
};

class  Manager  : public Spt::Class
{

public :
	enum
	{
#		ifdef __PLAT_XBOX__
		vMAX_PORT = 4,			// Four controllers...
		vMAX_SLOT = 2,			// ...each with 2 slots for memory cards.
#		elif defined __PLAT_NGC__
		vMAX_PORT = 1,			// Just plug them into the console itself, so 1 port...
		vMAX_SLOT = 1,			// ...with 2 slots for memory cards.
#		else
		vMAX_PORT = 2,
		vMAX_SLOT = 4,
#		endif // #ifdef __PLAT_XBOX__
	};

	int				GetMaxSlots( int port );
	Card*			GetCard( int port, int slot );
	Card*			GetCardEx( int port, int slot )	{ return &( m_card[port][slot] ); }
#	ifdef __PLAT_NGC__
	void			SetFatalError( bool error )		{ m_hasFatalError = error; }
	bool			GotFatalError( void )			{ return m_hasFatalError; }
	void			SetWrongDevice( bool error )	{ m_wrongDevice = error; }
	bool			GotWrongDevice( void )			{ return m_wrongDevice; }
#	endif // #ifdef __PLAT_NGC__

private :
					~Manager ( void );
					Manager ( void );

					Card			m_card[vMAX_PORT][vMAX_SLOT];
#	ifdef __PLAT_NGC__
	bool			m_hasFatalError;
	bool			m_wrongDevice;
#	endif // #ifdef __PLAT_NGC__

#	ifdef __PLAT_XBOX__
	Card m_hard_drive;
#	endif // #ifdef __PLAT_XBOX__
	
	DeclareSingletonClass( Manager );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/


/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/


/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mc

#endif	// __GEL_MCMAN_H



================================================
FILE: Code/Sys/Mem/CompactPool.cpp
================================================
#include 
#include 


#ifdef __DEBUG_COMPACTPOOL__
//#define __REALLY_DEBUG_COMPACTPOOL__


#endif

#ifdef	__NOPT_ASSERT__
#define	REPORT_ON		0		// change this to the address of a black you want to watch
#endif

#ifdef __PLAT_NGC__
extern bool g_mc_hack;
#include "sys/ngc/p_buffer.h"
#endif		// __PLAT_NGC__

namespace Mem
{


CCompactPool::CCompactPool(int item_size, int desired_num_items, char *name)
{
	m_totalItems = desired_num_items;
	m_itemSize = item_size;
	Dbg_MsgAssert(m_itemSize >= 4, ("item size too small (%d)", m_itemSize));

#ifdef __PLAT_NGC__
	if ( g_mc_hack )
	{
		mp_buffer = (uint8*)NsBuffer::alloc( m_totalItems * m_itemSize );
	}
	else
#endif		// __PLAT_NGC__
	{
		mp_buffer = new uint8[m_totalItems * m_itemSize];
	}
	mp_buffer_end = mp_buffer + m_totalItems * m_itemSize;

	m_currentUsedItems = 0;
	m_maxUsedItems = 0;

	// set up free list
	mp_freeList = (uint32 *) mp_buffer;
	uint8 *pItem = mp_buffer;
	for (int i = 0; i < m_totalItems; i++)
	{
		if (i < m_totalItems - 1)
			*((uint32 **) pItem) = (uint32 *) (pItem + m_itemSize);
		else
			*((uint32 **) pItem) = NULL;
		pItem += m_itemSize;
	}

	if (name)
		strcpy(m_name, name);
	else
		strcpy(m_name, "unnamed");

#ifdef __REALLY_DEBUG_COMPACTPOOL__

	m_maxUsedItems = 0;

	int total_marker_blocks = (m_totalItems + 31) >> 5;
	mp_used_marker_tab = new uint32[total_marker_blocks];
	//printf("ZOOPY: %d marker blocks, marker tab at 0x%x\n", total_marker_blocks, mp_used_marker_tab);
	for (int b = 0; b < total_marker_blocks; b++)
		mp_used_marker_tab[b] = 0;
#endif
}




CCompactPool::~CCompactPool()
{
	Dbg_MsgAssert(!m_currentUsedItems, ("pool still contains items"));

#ifdef __PLAT_NGC__
	if ( !g_mc_hack )
#endif		// __PLAT_NGC__
	{
		delete mp_buffer;
	}

#ifdef __REALLY_DEBUG_COMPACTPOOL__
	Ryan("Freeing pool %s, max items used in this pool: %d\n", m_name, m_maxUsedItems);
	delete [] mp_used_marker_tab;
	mp_used_marker_tab = NULL;
	m_maxUsedItems = 0;
#endif
}

   
bool CCompactPool::IsInPool(void *p)
{
	if (p>=mp_buffer && p m_maxUsedItems)
		{
			m_maxUsedItems = m_currentUsedItems;
		}
#ifdef __REALLY_DEBUG_COMPACTPOOL__		

		uint32 marker_num = ((uint32) mp_freeList - (uint32) mp_buffer) / m_itemSize;
		uint32 marker_tab_entry = marker_num >> 5;
		uint32 marker_tab_bit = marker_num - (marker_tab_entry << 5);
		//printf("ZOOPY: allocating using entry %d, bit %d, marker tab at 0x%x\n", marker_tab_entry, marker_tab_bit, mp_used_marker_tab);
		Dbg_MsgAssert(mp_used_marker_tab, ("max used items %d", m_maxUsedItems));
		mp_used_marker_tab[marker_tab_entry] |= (1<= 0 && pool_item < m_totalItems, ("item (%d) out of range (%d)", pool_item, m_totalItems));
	Dbg_Assert(!(((uint32) pFreeItem) & 3));
	
	uint32 marker_tab_entry = pool_item >> 5;
	uint32 marker_tab_bit = pool_item - (marker_tab_entry << 5);
	//printf("ZOOPY: freeing using entry %d, bit %d, marker tab at 0x%x\n", marker_tab_entry, marker_tab_bit, mp_used_marker_tab);
	Dbg_Assert(mp_used_marker_tab);
	Dbg_MsgAssert(mp_used_marker_tab[marker_tab_entry] & (1<= 0 && pool_item < m_totalItems, ("next item (%d) out of range (%d)", pool_item, m_totalItems));
	}
#endif
*/
}



} // namespace Mem





================================================
FILE: Code/Sys/Mem/CompactPool.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		
**																			**
**	Module:			
**																			**
**	File name:		
**																			**
**	Created by:		8/2/2001 - rjm							                **
**																			**
**	Description:	
**																			**
*****************************************************************************/

#ifndef __SYS_MEM_COMPACTPOOL_H
#define __SYS_MEM_COMPACTPOOL_H


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
/*****************************************************************************
**								   Defines									**
*****************************************************************************/


#ifdef __NOPT_ASSERT__
#define __DEBUG_COMPACTPOOL__
#endif


namespace Mem
{

						


/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

class CCompactPool
{
public:
						CCompactPool(int item_size, int desired_num_items, char *name = NULL);
						~CCompactPool();

	void *				Allocate();
	void 				Free(void *pFreeItem);
	int					GetMaxUsedItems() {return m_maxUsedItems;}
	int					GetNumUsedItems() {return m_currentUsedItems;}
	int					GetTotalItems() {return m_totalItems;}
	char *				GetName() {return m_name;}
	bool				IsInPool(void *p);
	
private:
	uint8 *				mp_buffer;
	uint8 *				mp_buffer_end;
	int					m_totalItems; // that we have room for
	int					m_itemSize;

	int					m_currentUsedItems;

	uint32 *			mp_freeList;

	char				m_name[64];
	int					m_maxUsedItems;
#ifdef __DEBUG_COMPACTPOOL__
	uint32 *			mp_used_marker_tab;
	
#endif

};




/*
template
class StaticCCompactPool
{
public:
	static void			SAllocPool(int size, char *name = NULL) {sp_pool = new CCompactPool<_V>(size, name);}
	static void			SFreePool()	{delete sp_pool;}
	static _V*			SCreate() {return sp_pool->Create();}
	static void			SFree(_V *pItem) {sp_pool->Free(pItem);}

	static int			SGetNumUsedItems() {return sp_pool->GetNumUsedItems();}

protected:
	static CCompactPool<_V> *	sp_pool;
};
*/




/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

}

#endif	// __SYS_MEM_CCOMPACTPOOL_H




================================================
FILE: Code/Sys/Mem/PoolManager.cpp
================================================
#include 
#include 

//DefinePoolableClass(Lst::HashItem)


namespace Mem 
{


Mem::CCompactPool *		PoolManager::sp_pool[PoolManager::vTOTAL_POOLS] =
{
	NULL
};




void PoolManager::SSetupPool(int poolId, int numItems)
{
	Dbg_MsgAssert(poolId >= 0 && poolId < vTOTAL_POOLS, ("%d not a valid pool", poolId));
	Dbg_MsgAssert(!sp_pool[poolId], ("pool %d already exists", poolId));

	if (poolId == vHASH_ITEM_POOL)
		sp_pool[poolId] = new Mem::CCompactPool(s_get_item_size(poolId), numItems, "HashItem");
}




void PoolManager::SDestroyPool(int poolId)
{
	Dbg_MsgAssert(poolId >= 0 && poolId < vTOTAL_POOLS, ("%d not a valid pool", poolId));
	Dbg_MsgAssert(sp_pool[poolId], ("pool %d doesn't exist", poolId));
	
	delete sp_pool[poolId];
	sp_pool[poolId] = NULL;
}




void *PoolManager::SCreateItem(int poolId)
{
	Dbg_MsgAssert(poolId >= 0 && poolId < vTOTAL_POOLS, ("%d not a valid pool", poolId));
	
	if (sp_pool[poolId])
	{
		return sp_pool[poolId]->Allocate();
	}

	return new char[s_get_item_size(poolId)];
}




void PoolManager::SFreeItem(int poolId, void *pItem)
{
	Dbg_MsgAssert(poolId >= 0 && poolId < vTOTAL_POOLS, ("%d not a valid pool", poolId));
	
	if (sp_pool[poolId])
	{
		sp_pool[poolId]->Free(pItem);
		return;
	}

	delete [] (char *)pItem;
}

// Provide access to the pools for debugging (like reporting usage)
Mem::CCompactPool * PoolManager::SGetPool(int poolId)
{
	return sp_pool[poolId];	
}




int PoolManager::s_get_item_size(int poolId)
{
	if (poolId == vHASH_ITEM_POOL)
		return sizeof(Lst::HashItem);
	return 0;
}




}



================================================
FILE: Code/Sys/Mem/PoolManager.h
================================================
#ifndef __SYS_MEM_POOLMANAGER_H
#define __SYS_MEM_POOLMANAGER_H

#include 

namespace Mem
{


/*
	Used in cases where one can't simply make a class poolable
	by inheriting from CPoolable, for example, where we want
	every HashItem class instance to come off the same
	pool.
*/

class PoolManager
{
public:

	enum
	{
		vHASH_ITEM_POOL =	0,
		vTOTAL_POOLS	=	1,
	};

	static void				SSetupPool(int poolId, int numItems);
	static void				SDestroyPool(int poolId);

	static void *			SCreateItem(int poolId);
	static void				SFreeItem(int poolId, void *pItem);
	static Mem::CCompactPool * 	SGetPool(int poolId);
	

private:

	static int				s_get_item_size(int poolId);

	static Mem::CCompactPool *sp_pool[vTOTAL_POOLS];
};

}

#endif



================================================
FILE: Code/Sys/Mem/Poolable.cpp
================================================
#include 
#include 

namespace Mem
{

	int gHeapPools = false;			// set to true to use debug heap instead of pools


CCompactPool *CPoolable::sp_pool[POOL_STACK_SIZE] = {NULL,NULL};									
bool CPoolable::s_internallyCreatedPool[POOL_STACK_SIZE] = {false,false};
int CPoolable::s_currentPool=0;						


PoolTest::PoolTest()
{
	printf("created PoolTest object\n");
}




PoolTest::~PoolTest()
{
	printf("~PoolTest()\n");
}




}


================================================
FILE: Code/Sys/Mem/Poolable.h
================================================
/*
	MODULE DESCRIPTION
	
	Poolable.h, RJM, 10/18/2001
*/

#ifndef __SYS_MEM_POOLABLE_H
#define __SYS_MEM_POOLABLE_H

#include 
#include 

#define DefinePoolableClass(_T)										\
namespace Mem														\
{																	\
	Mem::CCompactPool *Mem::CPoolable< _T >::sp_pool[POOL_STACK_SIZE] = {NULL,NULL};		\
	bool Mem::CPoolable< _T >::s_internallyCreatedPool[POOL_STACK_SIZE] = {false,false};	\
	int Mem::CPoolable< _T >::s_currentPool=0;						\
}																	\


namespace Mem
{


	extern int gHeapPools;

//class CCompactPool;


template 
class CPoolable
{
	enum EPoolStackSize
	{
		POOL_STACK_SIZE=2
	};
		
public:
	static void							SCreatePool(int num_items, char *name);
	static void							SAttachPool(CCompactPool *pPool);
	static void							SRemovePool();
	static int							SGetMaxUsedItems();
	static int							SGetNumUsedItems();
	static int							SGetTotalItems();
							  													
	static void							SPrintInfo();
	
	static void							SSwitchToNextPool();
	static void							SSwitchToPreviousPool();
	
	static int							SGetCurrentPoolIndex();
	static bool							SPoolExists();
	
	void * operator new (size_t size);										  	
	void operator delete (void * pMem);									   		
protected:																		
	static CCompactPool *				sp_pool[POOL_STACK_SIZE];
	static bool	  						s_internallyCreatedPool[POOL_STACK_SIZE];  	
	static int							s_currentPool;
};




class PoolTest : public CPoolable
{

public:
	PoolTest();
	~PoolTest();

	int m_monkey;
};




template
void *CPoolable<_T>::operator new (size_t size)							
{																
	Dbg_Assert(size == sizeof(_T));								
																
	//printf("in CPoolable operator new\n");

	#ifdef	__NOPT_ASSERT__
	if (gHeapPools)
	{
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().DebugHeap());
		void *p = ::new char[size];								
		Mem::Manager::sHandle().PopContext();
		return p;
	}
	#endif
	
	if (sp_pool[s_currentPool])	 
	{
		return sp_pool[s_currentPool]->Allocate();								
	}

	return ::new char[size];								
}																
																



template
void CPoolable<_T>::operator delete (void * pMem)							
{																
	Dbg_Assert(pMem);  
	#ifdef	__NOPT_ASSERT__
	if (gHeapPools)
	{
		delete [] (char *)pMem;											
		return;
	}
	#endif
	
	// pMem contains no info as to which pool it was allocated from, because that would
	// use up too much memory.
	// So query the pools to see if it is in them.
	
	CCompactPool *p_pool=sp_pool[s_currentPool];
	// Check if it is in the current pool, which it will be most of the time.
	if (p_pool && p_pool->IsInPool(pMem))
	{
		p_pool->Free(pMem);
	}
	else
	{
		// Otherwise try the other one, which exists for saving games to mem card.
		Dbg_MsgAssert(POOL_STACK_SIZE==2,("Only two pool supported at the moment"));
		p_pool=sp_pool[s_currentPool ^ 1];
		if (p_pool && p_pool->IsInPool(pMem))
		{
			p_pool->Free(pMem);
		}
		else
		{
			delete [] (char *)pMem;											
		}
	}		
}																
																



template
void CPoolable<_T>::SCreatePool(int num_items, char *name)					
{																
	Dbg_Assert(!sp_pool[s_currentPool]);										
	sp_pool[s_currentPool] = new CCompactPool(sizeof(_T), num_items, name);		
	s_internallyCreatedPool[s_currentPool] = true;
}																
																
template
bool CPoolable<_T>::SPoolExists()
{																
	if (sp_pool[s_currentPool])
	{
		return true;
	}
	return false;
}		
 

template
void CPoolable<_T>::SAttachPool(CCompactPool *pPool)					
{																
	Dbg_Assert(!sp_pool[s_currentPool]);										
	Dbg_Assert(pPool);										
	sp_pool[s_currentPool] = pPool;		
	s_internallyCreatedPool[s_currentPool] = false;
}																
																



template
void CPoolable<_T>::SRemovePool()											
{																
	if (sp_pool[s_currentPool])												
	{															
		if (s_internallyCreatedPool[s_currentPool])
			delete sp_pool[s_currentPool];											
		sp_pool[s_currentPool] = NULL;											
	}															
}																

template
int CPoolable<_T>::SGetCurrentPoolIndex()
{
	return s_currentPool;
}

template
void CPoolable<_T>::SSwitchToNextPool()
{
	Dbg_MsgAssert(s_currentPool
void CPoolable<_T>::SSwitchToPreviousPool()
{
	Dbg_MsgAssert(s_currentPool,("Called SSwitchToPreviousPool with no previous pool"));
	--s_currentPool;
}

template
int CPoolable<_T>::SGetMaxUsedItems()											
{
	return sp_pool[s_currentPool]->GetMaxUsedItems();
}


template
int CPoolable<_T>::SGetNumUsedItems()											
{
	return sp_pool[s_currentPool]->GetNumUsedItems();
}

template
int CPoolable<_T>::SGetTotalItems()											
{
	return sp_pool[s_currentPool]->GetTotalItems();
}




template
void CPoolable<_T>::SPrintInfo()											
{
	printf("pool is at %p\n", sp_pool[s_currentPool]);
}




}

#endif


================================================
FILE: Code/Sys/Mem/alloc.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		System Library											**
**																			**
**	Module:			Memory (Mem) 											**
**																			**
**	File name:		alloc.cpp												**
**																			**
**	Created by:		03/20/00	-	mjb										**
**																			**
**	Description:	Abstract Allocator class								**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include "memman.h"
#include "alloc.h"

#ifdef __PLAT_NGC__
#ifdef __EFFICIENT__
#include 
#endif		// __EFFICIENT__
#endif		// __PLAT_NGC__

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/


namespace Mem
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

uint		Allocator::s_current_id = 1;
#ifdef __EFFICIENT__
uint		Allocator::s_align_stack[16] = { 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
uint		Allocator::s_align_stack_index = 0;
const uint	Allocator::BlockHeader::sSize = sizeof(BlockHeader); 
#else
#ifdef __PLAT_NGC__
const uint	Allocator::BlockHeader::sSize = (uint)nAlignUpBy( sizeof(BlockHeader), 5 ); 
#else
const uint	Allocator::BlockHeader::sSize = (uint)nAlignUpBy( sizeof(BlockHeader), 4 ); 
#endif		// __PLAT_NGC__
#endif

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

Allocator::Context::Context( void ) 
: m_node( this ), mp_free_list( NULL ) 
{
	

#ifdef __LINKED_LIST_HEAP__
	mp_used_list = NULL;
#endif

	Dbg_AssertType( &m_node, Lst::Node< Context > );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Allocator::Context::~Context( void ) 
{
	

#ifdef __NOPT_DEBUG__
	Dbg_MsgAssert( mp_used_list == NULL,( "Memory Leak" ));
	Dbg_MsgAssert( mp_free_list == NULL,( "Corrupt Free List" ));
	Dbg_AssertType( &m_node, Lst::Node< Context > );
#endif
}


bool Allocator::s_valid( void* pAddr )
{
	BlockHeader* p_bheader = BlockHeader::sRead( pAddr ); 
	return (p_bheader->mId == vALLOC_MAGIC);
}

size_t Allocator::s_get_alloc_size( void* pAddr )
{
	BlockHeader* p_bheader = BlockHeader::sRead( pAddr ); 
	return (p_bheader->mSize);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Allocator* Allocator::s_free( void* pAddr )
{   
	BlockHeader* p_bheader = BlockHeader::sRead( pAddr ); 
	Allocator* p_ret = p_bheader->mpAlloc;

#if 0
	

	Mem::Manager& mem_man = Mem::Manager::sHandle();	
	bool found = false;
	for (Mem::Heap* heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
	{		
		if (heap == p_ret) found = true;
	}
	Dbg_MsgAssert(found,("Double dealloc of %p, allocator = %p?", pAddr, p_ret));
#endif

	Dbg_MsgAssert(p_bheader->mId != 0xDEADDEAD,("Freeing Block Twice!\n"));
	Dbg_MsgAssert(p_bheader->mId == vALLOC_MAGIC,("Freeing Corrupt Block\n"));
	
	p_bheader->mpAlloc->free( p_bheader );
	p_bheader->mId = 0xDEADDEAD;						// clear Handle ID to DEAD value

	return p_ret;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Allocator::s_set_id( void* pAddr )
{
	

	BlockHeader* p_bheader = (BlockHeader*)( ((uint)pAddr) - BlockHeader::sSize );
#ifdef __EFFICIENT__
	p_bheader = (BlockHeader*)(((uint)p_bheader) - p_bheader->mPadBytes);
#endif		// __EFFICIENT__
	Dbg_AssertType( p_bheader, BlockHeader );
	Dbg_AssertType( p_bheader->mpAlloc, Allocator );
	
	p_bheader->mId = vALLOC_MAGIC;		//	s_current_id++;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Allocator::BlockHeader* Allocator::BlockHeader::sRead( void* pAddr )
{
	BlockHeader* p_ret = (BlockHeader*)(((uint)pAddr) - BlockHeader::sSize);
#ifdef __EFFICIENT__
	p_ret = (BlockHeader*)(((uint)p_ret) - p_ret->mPadBytes);
#endif		// __EFFICIENT__
	
	Dbg_AssertType( p_ret, BlockHeader );
	Dbg_AssertType( p_ret->mpAlloc, Allocator );
	
	return p_ret;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Allocator::Allocator( Region* region, Direction dir, char *p_name )
: mp_region( region ), m_dir( dir ), mp_name(p_name)
{
	
	
	mp_region->RegisterAllocator( this );
	
	m_context_stack.AddToHead( &m_initial_context.m_node );
	mp_context = &m_initial_context;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Allocator::~Allocator( void )
{
	

	Dbg_MsgAssert( !m_context_stack.IsEmpty(),( "Heap stack underflow" ));

	mp_region->UnregisterAllocator( this ); 

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

uint	Allocator::sGetId( void* pAddr )
{
	

#ifdef __EFFICIENT__
	if ( pAddr == NULL )
#else
	if (( pAddr == NULL ) || !nAligned( pAddr ))
#endif		// __EFFICIENT__
	{
		return 0;
	}

	BlockHeader* p_bheader = (BlockHeader*)( ((uint)pAddr) - BlockHeader::sSize );
#ifdef __EFFICIENT__
	p_bheader = (BlockHeader*)(((uint)p_bheader) - p_bheader->mPadBytes);
#endif		// __EFFICIENT__

	return p_bheader->mId;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Allocator::PushContext( void )
{
	

	Context* new_context = new Context;
	m_context_stack.AddToHead( &new_context->m_node );
	mp_context = new_context;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Allocator::PopContext( void )
{
	

	Dbg_MsgAssert( !m_context_stack.IsEmpty(),( "Heap stack underflow" ));

#ifdef __NOPT_DEBUG__
	dump_free_list();
	dump_used_list();
#endif

	delete mp_context;
		
	Lst::Search< Context > sh;
	mp_context = sh.FirstItem( m_context_stack );

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


#ifdef __NOPT_DEBUG__

void 	Allocator::dump_used_list( void )
{
	
#ifndef __PLAT_NGC__
	BlockHeader*	p_header = mp_context->mp_used_list;
	
	Dbg_Message( "USED LIST for Heap (%p)", this );

	while ( p_header )
	{
		Dbg_AssertType( p_header, BlockHeader );
		Dbg_Message ( "%p  %x   (%p)", (void*)((uint)p_header + BlockHeader::sSize ), 
										p_header->mSize, p_header->mp_next_used );
		p_header = p_header->mp_next_used;
	}

	Dbg_Message( "----------------------" );
#endif		// __PLAT_NGC__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	Allocator::dump_free_list( void )
{
	
	
	BlockHeader*	p_header = mp_context->mp_free_list;
	
	Dbg_Message( "FREE LIST for Heap (%p)", this );
	
	while ( p_header )
	{
		Dbg_AssertType( p_header, BlockHeader );
		Dbg_Message ( "%p  %x   (%p)", (void*)((uint)p_header + BlockHeader::sSize ), 
										p_header->mSize, p_header->mpNext );
		p_header = p_header->mpNext;
	}

	Dbg_Message( "----------------------" );
}

#endif


#ifdef	__LINKED_LIST_HEAP__    

// find the block that contains this pointer
// return NULL if not found   
// also includes the header area of the block  							   
void * Allocator::find_block(void *p)
{

	BlockHeader*	p_header = mp_context->mp_used_list;
	
	while ( p_header )
	{
		
		void * p_start = (void*)((uint)p_header + BlockHeader::sSize);
		int size = p_header->mSize;
		void * p_end = (void*)((int)p_start + size);

		if (p >= p_start && p mp_next_used;
	}
	return NULL;	
}

Allocator::BlockHeader* Allocator::first_block()
{

	BlockHeader*	p_header = mp_context->mp_used_list;
	
	return p_header;	
}




#endif

bool  SameContext(void *p_mem,  Mem::Allocator *p_alloc)
{
	Allocator::BlockHeader* p_bheader = Allocator::BlockHeader::sRead( p_mem ); 
	return (p_bheader->mpAlloc == p_alloc);
}


#ifdef __EFFICIENT__
void Allocator::PushAlign( int align )
{
	s_align_stack_index++;

	switch ( align )
	{
		case 4:
			s_align_stack[s_align_stack_index] = 2;
			break;
		case 8:
			s_align_stack[s_align_stack_index] = 3;
			break;
		case 16:
			s_align_stack[s_align_stack_index] = 4;
			break;
		case 32:
			s_align_stack[s_align_stack_index] = 5;
			break;
		case 64:
			s_align_stack[s_align_stack_index] = 6;
			break;
		case 128:
			s_align_stack[s_align_stack_index] = 7;
			break;
		default:
			Dbg_MsgAssert( false, ( "Illegal alignment (%d) specified. Must be 4, 8, 16, 32, 64 or 128.\n", align ) );
			break;
	}
}

void Allocator::PopAlign( void )
{
	s_align_stack_index--;
}

uint Allocator::GetAlign( void )
{
	return s_align_stack[s_align_stack_index];
}
#endif		// __EFFICIENT__

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mem



================================================
FILE: Code/Sys/Mem/alloc.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Manager (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		core/sys/mem/alloc.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_MEM_ALLOC_H
#define	__SYS_MEM_ALLOC_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#include 

#ifdef __PLAT_NGC__
#define __EFFICIENT__		// Comment in for efficient allocation.
#endif		// __PLAT_NGC__

//#ifdef	__PLAT_NGPS__											  
#define	__LINKED_LIST_HEAP__    
//#else
//#ifdef __NOPT_DEBUG__
//#define	__LINKED_LIST_HEAP__    
//#endif
//#endif

// Mick: on the CD build, we don't need any memory tracking
// this saves us at least 300K as we have over 30,000 individual allocations... crazy
// K: This used to be #ifdef __NOPT_CDROM__, changed cos the nopt_cdrom define is now gone.
#ifndef	__NOPT_ASSERT__
#undef	__LINKED_LIST_HEAP__
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mem
{


const	uint	vALLOC_MAGIC = 0xCF801FE1;			// just a random magic number, to indicate a block is valid

class	CMemProfile;


/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

class Region;

class  Record  : public Spt::Class
{
	

public:
			Record( void ) : m_count(0), m_peak(0) {}

			int		m_count;
			int		m_peak;

			Record&		operator+=( int src );
			Record&		operator-=( int src );

	const 	Record		operator++( int src );
	const 	Record		operator--( int src );
			Record&		operator++( void );
			Record&		operator--( void );

};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  Allocator  : public Spt::Class				// abstract base class 			
{
	

	friend class Manager;
	friend class Region;

public :

	enum Direction
	{		
		vBOTTOM_UP	= +1,
		vTOP_DOWN	= -1
	};

	static		uint 			sGetId( void* pAddr );
		
				Region*			ParentRegion( void );

				Record			mUsedBlocks;
				Record			mFreeBlocks;
				Record			mUsedMem;
				Record			mFreeMem;


	virtual		void			PushContext( void );
	virtual		void			PopContext( void );

#ifdef __EFFICIENT__
				void			PushAlign( int align );
				void			PopAlign( void );
				uint			GetAlign( void );
#endif		// __EFFICIENT__

#ifdef __NOPT_DEBUG__
				void			dump_free_list ( void );
				void			dump_used_list ( void );
#endif

				char *	GetName() {return mp_name;}
				Direction GetDirection() {return m_dir;}

/// sorry, need to access this from elsewhere, for debugging... (MICK)
//protected :

	class  BlockHeader  //: public Spt::Class
	{
		
	
	public :

#ifdef __NOPT_MEM_DEBUG__

				void				DbgAllocateBlock( void );
				void				DbgFreeBlock( void );
				void				DbgDump( void );

#endif // __NOPT_MEM_DEBUG__

									BlockHeader( Allocator* pAlloc, size_t size );
									~BlockHeader( void );

		static	BlockHeader* 		sRead( void* pMem );
		
				union
				{
					Allocator*		mpAlloc;
					BlockHeader*	mpNext;
				};

#ifndef __EFFICIENT__
				uint				mSize;
#endif
				uint				mId;
#ifdef __EFFICIENT__
#ifdef __NOPT_ASSERT__
				CMemProfile	*		mp_profile;
#endif		// __NOPT_ASSERT__
#else
				CMemProfile	*		mp_profile;
#endif __EFFICIENT__


		static const uint			sSize; // Aligned Block Header size


#ifdef	__LINKED_LIST_HEAP__    

				BlockHeader*		mp_prev_used;
				BlockHeader*		mp_next_used;
				
				void *				mp_debug_data;		// added for tracking callstacks for each allocation

#endif // __NOPT_DEBUG__

#ifdef __EFFICIENT__
				uint				mSize:24;
				uint				mPadBytes:8;		// Must be last byte in structure.
#endif
	};

#ifdef __LINKED_LIST_HEAP__
				void * 				find_block(void *p);
				BlockHeader* 		first_block();
#endif
	
    
	class  Context  : public Spt::Class
	{
		
	public:
								Context( void );
								~Context( void );
	
		Lst::Node< Context >	m_node;
		BlockHeader*			mp_free_list;
	
	#ifdef	__LINKED_LIST_HEAP__    
		BlockHeader*			mp_used_list;
	#endif
	
	};

									Allocator( Region* region, Direction dir, char *p_name );
	virtual							~Allocator( void );
	
			Region*					mp_region;
			Direction				m_dir;
			void*					mp_top;
			Context*				mp_context;

			char *					mp_name;		// debugging name, for checking

			Lst::Head< Context >	m_context_stack;

private :

	static  bool 			s_valid( void* pAddr );
	static	size_t			s_get_alloc_size( void* pAddr );
	static	Allocator* 		s_free( void* pAddr );
	static	void 			s_set_id( void* pAddr );
	virtual void*			allocate( size_t size, bool assert_on_fail ) = 0;
	virtual int				available(  ) {Dbg_MsgAssert(0,("available() not defined for this allocator!")); return 0;}
	virtual void*			reallocate_down( size_t newSize, void* pOld ) {Dbg_MsgAssert(0,("reallocate_down not defined for this allocator!")); return NULL;}
	virtual void*			reallocate_up( size_t newSize, void* pOld ) {Dbg_MsgAssert(0,("reallocate_up not defined for this allocator!")); return NULL;}
	virtual void*			reallocate_shrink( size_t newSize, void* pOld ) {Dbg_MsgAssert(0,("reallocate_shrink not defined for this allocator!")); return NULL;}
	virtual	void			free( BlockHeader* pAddr ) = 0;

	static	uint			s_current_id;
			Context			m_initial_context;
#ifdef __EFFICIENT__
	static uint				s_align_stack[16];
	static uint				s_align_stack_index;
#endif		// __EFFICIENT__
};                  		


/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline Record& Record::operator++( void )			// prefix
{
	

	*this += 1;
	return	*this;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Record Record::operator++( int )	// postfix
{
	
	
	Record ret = *this;
	++( *this );

	return ret;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Record& Record::operator--( void )		// prefix
{
	

	*this -= 1;
	return	*this;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline const Record Record::operator--( int )	// postfix
{
	
	
	Record ret = *this;
	--( *this );

	return ret;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Record& Record::operator+=( int src )
{
	

	m_count += src;
	
	if( m_count > m_peak )
	{
		m_peak = m_count;
	}
	
	return *this;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Record& Record::operator-=( int src )
{
	
	
	m_count -= src;

	Dbg_MsgAssert(( m_count >= 0 ), ( "Count Underflow" ));
	
	return *this;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Region* Allocator::ParentRegion( void )
{
	

	return mp_region; 
}
	
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef __EFFICIENT__
inline Allocator::BlockHeader::BlockHeader( Allocator* pAlloc, size_t size )
: mpAlloc( pAlloc ), mSize( size ), mPadBytes( 0 )
#else
inline Allocator::BlockHeader::BlockHeader( Allocator* pAlloc, size_t size )
: mpAlloc( pAlloc ), mSize( size )
#endif	// __EFFICIENT__
{
	

#ifdef	__LINKED_LIST_HEAP__    

	mp_next_used = NULL;
	mp_prev_used = NULL;
	mp_debug_data  = NULL;

#endif
#ifdef __NOPT_ASSERT__
	mp_profile = NULL;
#endif		// __NOPT_ASSERT__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline Allocator::BlockHeader::~BlockHeader( void )
{
	

#ifdef	__LINKED_LIST_HEAP__    
	Dbg_MsgAssert( mp_prev_used == NULL, ( "internal error" ));
	Dbg_MsgAssert( mp_next_used == NULL, ( "internal error" ));
#endif
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#ifdef __NOPT_MEM_DEBUG__


inline void	Allocator::BlockHeader::DbgAllocateBlock( void )
{	
	

	uint64* ptr = (uint64*)((uint)this + sSize );

	for ( uint i = 0; i < mSize; i += 8 )
	{
		*ptr++ = vALLOCATED_BLOCK;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void	Allocator::BlockHeader::DbgFreeBlock( void )
{
	
	
	uint64* ptr = (uint64*)((uint)this + sSize ); 	

	for ( uint i = 0; i < mSize; i += 8 )
	{
		*ptr++ = vFREE_BLOCK;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void	Allocator::BlockHeader::DbgDump( void )
{
	
#ifdef __NOPT_FULL_DEBUG__
	Dbg_Message ( "\nBlockHeader [%p]\nmpAlloc/Next:%p\nSize %d\nstamp %x\n", this, mpAlloc, mSize, classStamp );
#else
	Dbg_Message ( "\nBlockHeader [%p]\nmpAlloc/Next:%p\nSize %d\n", this, mpAlloc, mSize );
#endif
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#endif // __NOPT_MEM_DEBUG__

bool  SameContext(void *p_mem,  Allocator *p_alloc);


} // namespace Mem

#endif  // __SYS_MEM_ALLOC_H


================================================
FILE: Code/Sys/Mem/handle.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Manager (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		core/sys/mem/handle.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_MEM_HANDLE_H
#define	__SYS_MEM_HANDLE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mem
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

nTemplateBaseClass( _T, Handle )
{
	

	
public :

								Handle( _T* ptr = NULL );
								~Handle( void );

	template < class _NewT >						                    // template copy contructor
								Handle( Handle< _NewT >& rhs ); 		// needed to support inheritance correctly
	
	template < class _NewT > 	
		Handle< _T >&			operator = ( Handle< _NewT >& rhs );	// template assignment operator
		Handle< _T >&			operator = ( _T* ptr );

		_T*						GetPointer( void ) const;
		
protected :

		_T*						m_ptr;
		uint					m_id;
	
};


/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

template < class _T > inline   
Handle< _T >::Handle< _T >( _T* ptr ) 
: m_ptr ( ptr ), m_id ( Allocator::sGetId( ptr ))
{
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
#if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ ))
// This version to fix Visual C++ compiler lack of ANSI C++ compliance.
#else
template < class _T > template < class _NewT > inline
Handle< _T >::Handle< _T >( Handle< _NewT >& rhs )
: m_ptr ( rhs.m_ptr ), m_id ( rhs.m_id )
{
	
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline	
Handle< _T >::~Handle( void )
{
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

#if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ ))
// This version to fix Visual C++ compiler lack of ANSI C++ compliance.
#else
 template < class _T > template < class _NewT > inline
Handle< _T >&		Handle< _T >::operator = ( Handle< _NewT >& rhs ) 
{
	

	m_ptr = rhs.m_ptr;
	m_id  = rhs.m_id;

	return *this;	
}
#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
Handle< _T >&		Handle< _T >::operator = ( _T* ptr ) 
{
	

	m_ptr = ptr;
	m_id  = Allocator::sGetId( ptr );

	return *this;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
_T*		Handle< _T >::GetPointer ( void ) const 
{
	

	if (( m_ptr ) && ( m_id == Allocator::sGetId( m_ptr )))
	{
		Dbg_AssertType( m_ptr, _T );
		return m_ptr;
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mem

#endif  // __SYS_MEM_HANDLE_H								


================================================
FILE: Code/Sys/Mem/heap.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		System Library											**
**																			**
**	Module:			Memory (Mem) 											**
**																			**
**	File name:		heap.cpp												**
**																			**
**	Maintained by:	Mick													**
**																			**
**	Description:	Heap class												**
**																			**
*****************************************************************************/


// (Mick) undefine this line if you want the full PS2 callstack reporting on allocations
// and the trashing of memory
// Also you might want to set gHeapPools in Poolable.cpp
// which makes the pools use the debug heap, so this debuggin applies to them.
#define __PURE_HEAP__			// No debugging

//////////////////////////////////////////////////////
// Memory Debugging Trick:
// dump the call stack for allocations and de-allocation of 
// a particular block 
// (maybe extend this to any block that encompasses this address????)  							   
#define		REPORT_ON		0			// would be somethng like 0x3a2b50, if you want it to work 

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include "memman.h"
#include "heap.h"
#include 

#define DUMP_HEAP 0			// Change to 1 to have a handy text dump in memory of the heap status.
							// Use the debugger to see it - look at gMemStatus, 64 bytes per line.

#define NUM_ADDRESS 0		// Change this to 8 to have a handy list of addresses to check.
							// If set to 0, no extra code/data will be generated.
							// Remember to change to 8, compile, then get the addresses in question
							// as the act of adding this code/data will change your addresses.
#if NUM_ADDRESS > 0
uint32 gAddress[NUM_ADDRESS] =
{
	0x81828561,
	0x818284c3,
	0x81827621,
	0x818275e3,
	0x81827541,
	0x81826523,
	0x818264e1,
	0x81826443
};

int g128 = 0;
int g32 = 0;

static void check_address( void * p, int size )
{
	for ( int lp = 0; lp < 8; lp++ )
	{
		if ( gAddress[lp] == ((uint32)p) ) {
			Dbg_Message( "We found the address we're looking for: 0x%08x (%d)\n", (uint32)p, size );
		}
	}

	if ( size == 32 )
	{
		g32++;
	}
	if ( size == 128 )
	{
		g128++;
	}
}

#else
#define check_address(a,b)
#endif

#if DUMP_HEAP == 1
#ifdef __PLAT_XBOX__
static SIZE_T	peakUsage = 64 * 1024 * 1024;	// 64mb.
char gMemStatus[64*28];
#else
char gMemStatus[64*16];
#endif

void dump_heap( void )
{

	Mem::Manager& mem_man = Mem::Manager::sHandle();

	sprintf(&gMemStatus[64*0], "Name            Used   Frag   Free   Blocks                    \n");
	sprintf(&gMemStatus[64*1], "--------------- ------ ------ ------ ------                    \n");
	Mem::Heap* heap;
	int line = 2;
	for (heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
	{		
			Mem::Region* region = heap->ParentRegion();			
			sprintf( &gMemStatus[64*line], "%12s: %5dK %5dK %5dK   %5d \n",
					heap->GetName(),
					heap->mUsedMem.m_count / 1024,
					heap->mFreeMem.m_count / 1024,
					region->MemAvailable() / 1024,
					heap->mUsedBlocks.m_count
					);
			line++;
	}

#	ifdef __PLAT_XBOX__
	MEMORYSTATUS stat;
    GlobalMemoryStatus( &stat );
    sprintf( &gMemStatus[64 * line++], "%4d  free kb of physical memory.\n", stat.dwAvailPhys / 1024 );

	if( stat.dwAvailPhys < peakUsage )
		peakUsage = stat.dwAvailPhys;

    sprintf( &gMemStatus[64 * line++], "%4d  free kb at peak physical memory usage.\n", peakUsage / 1024 );
#	endif



}
#endif		// DUMP_HEAP == 1
	
namespace Script
{
	class	CStruct;
	class	CScript;
}

namespace CFuncs
{
bool ScriptDumpHeaps( Script::CStruct *pParams, Script::CScript *pScript );
}

#ifdef	__PLAT_NGPS__
#include 
#elif defined( __PLAT_NGC__ )
#include 
#endif




/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/

namespace Mem
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#ifndef	__NOPT_ASSERT__
#define	__PURE_HEAP__
#endif


#ifndef	__PURE_HEAP__		
#define	__TRASH_BLOCKS__				// define this to trash contents of alloc and free, regardless of build				
#endif


						
/*****************************************************************************
**								Private Types								**
*****************************************************************************/
	
/*****************************************************************************
**								 Private Data								**
*****************************************************************************/


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

inline Mem::Allocator::BlockHeader*	Heap::next_addr( Allocator::BlockHeader* pHeader )
{
	
	
	Dbg_AssertType( pHeader, BlockHeader );

	return (BlockHeader*)( (uint)pHeader + BlockHeader::sSize + pHeader->mSize );
}


#ifdef	__TRASH_BLOCKS__

//static const uint64 vTRASH_ALLOCATED_BLOCK = 0x0101010101010101;
static const uint64 vTRASH_ALLOCATED_BLOCK = 0x5555555555555555LL; 	// grey
static const uint64 vTRASH_FREE_BLOCK 	 =   0xbbbbbbbbbbbbbbbbLL;	// white
//static const uint64 vTRASH_FREE_BLOCK 	 =       0x3f3f003f3f003f3f;	// ??? ??? (with 0 so string are terminated)
								

inline void	Trash_AllocateBlock( Mem::Allocator::BlockHeader* pBlock )
{	
#ifdef __EFFICIENT__
	memset( (void*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize ), vTRASH_ALLOCATED_BLOCK & 0xff, pBlock->mSize );
#else
	uint64* ptr = (uint64*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize );

	for ( uint i = 0; i < pBlock->mSize; i += 8 )
	{
		*ptr++ = vTRASH_ALLOCATED_BLOCK;
	}
#endif		// __EFFICIENT__
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

inline void	Trash_FreeBlock( Mem::Allocator::BlockHeader* pBlock )
{
#ifdef __EFFICIENT__
	memset( (void*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize ), vTRASH_FREE_BLOCK & 0xff, pBlock->mSize );
#else
	uint64* ptr = (uint64*)((uint)pBlock + Mem::Allocator::BlockHeader::sSize ); 	

	for ( uint i = 0; i < pBlock->mSize; i += 8 )
	{
		*ptr++ = vTRASH_FREE_BLOCK;
	}
#endif		// __EFFICIENT__
}

inline void	Trash_FreeBlockHeader( Mem::Allocator::BlockHeader* pBlock )
{
#ifdef __EFFICIENT__
	memset( (void*)((uint)pBlock), vTRASH_FREE_BLOCK & 0xff, Mem::Allocator::BlockHeader::sSize );
#else
	uint64* ptr = (uint64*)((uint)pBlock); 	

	for ( uint i = 0; i < Mem::Allocator::BlockHeader::sSize; i += 8 )
	{
		*ptr++ = vTRASH_FREE_BLOCK;
	}
#endif		// __EFFICIENT__
}


#endif

				
				
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Heap::free ( BlockHeader* pFreeBlock )
{
#if DUMP_HEAP == 1
	dump_heap();
#endif		// DUMP_HEAP == 1
	
#ifdef __EFFICIENT__
	pFreeBlock->mSize += pFreeBlock->mPadBytes;
	pFreeBlock->mPadBytes = 0;
#endif		// __EFFICIENT__
	
	Dbg_AssertType ( pFreeBlock, BlockHeader );

	#ifdef	__NOPT_ASSERT__
	#ifdef	__PLAT_NGPS__
	// find what feeded a particular block
	if ( ((uint)pFreeBlock + BlockHeader::sSize) == REPORT_ON)
	{
		printf ("Freeing 0x%x",REPORT_ON);
		DumpUnwindStack(20,NULL);
	}
	#endif
	#endif
			  
			  
	BlockHeader*	p_before = NULL;
	BlockHeader*	p_2before = NULL;
	BlockHeader*	p_after = mp_context->mp_free_list;


#ifdef	__LINKED_LIST_HEAP__    

#ifdef	__PLAT_NGPS__
#ifndef	__PURE_HEAP__		
	MemView_Free((void*)pFreeBlock);
#endif
#endif
	FreeMemProfile(pFreeBlock);

	// mp_next_used and mp_prev_used are a doble linked null ternimated list

	// unlink next and prev
	if ( pFreeBlock->mp_next_used )
	{
		pFreeBlock->mp_next_used->mp_prev_used = pFreeBlock->mp_prev_used;
	}
	if ( pFreeBlock->mp_prev_used )
	{
		pFreeBlock->mp_prev_used->mp_next_used = pFreeBlock->mp_next_used;
	}
	// unlink if it's the head of the list
	if ( mp_context->mp_used_list == pFreeBlock )
	{
		mp_context->mp_used_list = pFreeBlock->mp_next_used;
	}

	pFreeBlock->mp_prev_used = NULL;
	pFreeBlock->mp_next_used = NULL;

#endif

// we trash the free block before we merge it with anything
// otherwise we end us trashing multi-megabytes
// when memory is fragmented (which it often is, in the middle of things)
	#ifdef	__TRASH_BLOCKS__
	Trash_FreeBlock ( pFreeBlock ); 
	#endif

	mFreeBlocks++;
	mFreeMem += pFreeBlock->mSize;

	mUsedBlocks--;
	mUsedMem -= pFreeBlock->mSize;

	// p_after starts at the head of the free list
	// and traverses it until p_after is the block after this 
	// block
	while ( p_after && ( (uint)p_after < (uint)pFreeBlock ))
	{		
		p_2before = p_before;
		p_before = p_after;
		p_after = p_after->mpNext;
	}

	pFreeBlock->mpNext = p_after; 						// insert pFreeBlock before p_after
	

	if ( p_after )
	{
		if ( next_addr( pFreeBlock ) == p_after ) 		// p_after joins pFreeBlock
		{
			pFreeBlock->mpNext = p_after->mpNext;
			pFreeBlock->mSize += p_after->mSize + BlockHeader::sSize;
			
									  
			p_after->~BlockHeader();
			#ifdef	__TRASH_BLOCKS__
			Trash_FreeBlockHeader ( p_after ); 
			#endif
			mFreeBlocks--;
			mFreeMem += BlockHeader::sSize;
			mUsedMem -= BlockHeader::sSize;
		}
	}
	
	
	if ( p_before )
	{
		if ( next_addr( p_before ) == pFreeBlock )		// pFreeBlock joins p_before
		{	
			p_before->mSize += pFreeBlock->mSize + BlockHeader::sSize;
			p_before->mpNext = pFreeBlock->mpNext;



			pFreeBlock->~BlockHeader();
			
			#ifdef	__TRASH_BLOCKS__
			Trash_FreeBlockHeader ( pFreeBlock ); 
			#endif

			
			mFreeBlocks--;
			mFreeMem += BlockHeader::sSize;
			mUsedMem -= BlockHeader::sSize;
			
			pFreeBlock = p_before;
			p_before = p_2before;
		}
		else											// insert pFreeBlock after p_before
		{							   				
			p_before->mpNext = pFreeBlock;
		}
	}
	else
	{
		mp_context->mp_free_list = pFreeBlock;
	}

	MemDbg_FreeBlock ( pFreeBlock ); 
	

														// reclaim free space in region

	if ( ( m_dir == vBOTTOM_UP ) && ( !p_after ) && 
		 ( mp_top == (void*)((uint)pFreeBlock + pFreeBlock->mSize + BlockHeader::sSize )))
	{
		if( p_before )
		{
			p_before->mpNext = NULL;
		}
		else
		{
			mp_context->mp_free_list = NULL;
		}



		mp_top = (void*)((uint)mp_top - ( pFreeBlock->mSize + BlockHeader::sSize) * m_dir );
		pFreeBlock->~BlockHeader();
		mFreeBlocks--;
		mFreeMem -= pFreeBlock->mSize;
		mUsedMem -= BlockHeader::sSize;
		#ifdef	__TRASH_BLOCKS__
		Trash_FreeBlockHeader ( pFreeBlock ); 
		#endif
	}
	else if (( m_dir == vTOP_DOWN ) && ( !p_before ) &&
			 ( mp_top == (void*)((uint)pFreeBlock )))
	{		 
	
	
		mp_top = (void*)((uint)mp_top - ( pFreeBlock->mSize + BlockHeader::sSize) * m_dir );
		mp_context->mp_free_list = pFreeBlock->mpNext;
		pFreeBlock->~BlockHeader();
		
		
		mFreeBlocks--;
		mFreeMem -= pFreeBlock->mSize;
		mUsedMem -= BlockHeader::sSize;
		#ifdef	__TRASH_BLOCKS__
		Trash_FreeBlockHeader ( pFreeBlock ); 
		#endif
	}
	
#if 0
	Mem::Manager& mem_man = Mem::Manager::sHandle();
	Mem::Heap* heap = mem_man.BottomUpHeap();
	Mem::Region* region = heap->ParentRegion();
	if (heap->mFreeMem.m_count > 3200*1000)
	{
		printf ("\nBottomUp Fragmentation %7dK, in %5d Blocks\n",heap->mFreeMem.m_count / 1024, heap->mFreeBlocks.m_count);
		DumpUnwindStack(20,NULL);
		MemView_DumpFragments(heap);		
		Dbg_MsgAssert(0,("Frag"));
	}
#endif
	
	
	
}


// Returns the size in bytes of either the largest free fragmented block,
// or the free space in the region whichever is larger
int Heap::LargestFreeBlock()
{
	
	uint32 size = 0;
	BlockHeader* p_header = mp_context->mp_free_list;
	while ( p_header ) 
	{
		if ( p_header->mSize >= size )
		{
			size = p_header->mSize;
		}
		p_header = p_header->mpNext;
	}
	uint32 region_size = mp_region->MemAvailable();
	if (region_size > size)
	{	
		size = region_size;
	}
	return size;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Note the block returned might be bigger than asked for
// by 16 or 32 bytes.
// memory system calls MUST account for this 
void*	Heap::allocate( size_t size, bool assert_on_fail )
{
#ifdef	__PLAT_NGPS__
//	if (size > 10*1024)
//	{
//		printf ("\nAllocating %d\n",size);
//		printf ("%s\n",Mem::Manager::sHandle().GetContextName());
//	}
	#endif
	
	Dbg_MsgAssert(size >0, ("Trying to allocate Zero bytes\n"));
	/*
	if (size == 0)
	{
		static int zeros=0;
		printf("(%d) Trying to allocate Zero bytes\n", zeros++);
		DumpUnwindStack(20,NULL);
	}
	*/	
	
	
#if DUMP_HEAP == 1
	dump_heap();
#endif		// DUMP_HEAP == 1

	BlockHeader* p_header = mp_context->mp_free_list;
	BlockHeader* p_freeblock = NULL;
	BlockHeader* p_last = NULL;
	BlockHeader* p_prev = NULL;

//	printf ("Trying to allocate %d bytes, current free list = %p\n",size,p_header);							 
							 
//	if (size == 8688) while (1);		

#ifdef __EFFICIENT__
	int align = 1 << GetAlign();
	int offset_bytes;
	size = (uint)nAlignUpBy( size, 2 );	// all allocations aligned by 4 bytes
#else
#ifdef __PLAT_NGC__
	size = (uint)nAlignUpBy( size, 5 );	// all allocations aligned by 16 bytes
#else
	size = (uint)nAlignUpBy( size, 4 );	// all allocations aligned by 16 bytes
#endif
#endif
	while ( p_header ) 	// find smallest free block large enough to fulfill request
	{
		Dbg_AssertType ( p_header, BlockHeader );

#ifdef __EFFICIENT__
		int off = ( ( ( (int)p_header + BlockHeader::sSize ) + ( align - 1 ) ) & -align ) - ( (int)p_header + BlockHeader::sSize );
		uint adjusted_size = size + off;
		if ( p_header->mSize >= adjusted_size )
#else
		if ( p_header->mSize >= size )
#endif
		{
			if (( p_freeblock == NULL ) || ( p_freeblock->mSize > p_header->mSize ))
			{
				p_prev = p_last;
				p_freeblock = p_header;
			}
		}

		p_last = p_header;
		p_header = p_header->mpNext;
	}

	if ( p_freeblock )	// found a free block large enough
	{	
#ifdef __EFFICIENT__
		offset_bytes = ( ( ( (int)p_freeblock + BlockHeader::sSize ) + ( align - 1 ) ) & -align ) - ( (int)p_freeblock + BlockHeader::sSize );
#endif
		if ( p_prev )
		{
			p_prev->mpNext = p_freeblock->mpNext;
		}
		else
		{
			mp_context->mp_free_list = p_freeblock->mpNext;
		}

		p_freeblock->mpAlloc = this;

		mFreeBlocks--;
		mUsedBlocks++;
		mFreeMem -= p_freeblock->mSize;
		mUsedMem += p_freeblock->mSize;

#ifdef __EFFICIENT__
		BlockHeader*	p_leftover 	= (BlockHeader*)((uint)p_freeblock + BlockHeader::sSize + size + offset_bytes );
		int				new_size 	= p_freeblock->mSize - size - BlockHeader::sSize - offset_bytes;
#else
		BlockHeader*	p_leftover 	= (BlockHeader*)((uint)p_freeblock + BlockHeader::sSize + size );
		int				new_size 	= p_freeblock->mSize - size - BlockHeader::sSize;
#endif		// __EFFICIENT__
		
		if ( new_size > 0 )	// create new free node for left-over memory
		{
			p_freeblock->mSize = size;
			new ((void*)p_leftover) BlockHeader( this, new_size );			
			mUsedBlocks++;
			free( p_leftover );
		}
		else
		{
			// new_size is 0, which implies that this block is (size) ... (size + BlockHeader::Size)
			// which means we'll be allocating a bit more than we need
			// but as (size) is not reference again, then that's not a problem
#ifdef __EFFICIENT__
			p_freeblock->mSize -= offset_bytes;
#endif		// __EFFICIENT__
		}

	}
	else				// request extra space from region
	{
		p_freeblock = (BlockHeader*)mp_region->Allocate( this, size + BlockHeader::sSize, assert_on_fail );

#ifdef __EFFICIENT__
		if ( m_dir == vBOTTOM_UP )
		{
			offset_bytes = ( ( ( (int)p_freeblock + BlockHeader::sSize ) + ( align - 1 ) ) & -align ) - ( (int)p_freeblock + BlockHeader::sSize );
			// Allocate, but keep the original pointer.
			mp_region->Allocate( this, offset_bytes, assert_on_fail );
		}
		else
		{
			offset_bytes = ( (int)p_freeblock + BlockHeader::sSize ) - ( ( (int)p_freeblock + BlockHeader::sSize ) & -align );
			// Allocate, increase size, not offset bytes.
			if ( offset_bytes )
			{
				p_freeblock = (BlockHeader*)mp_region->Allocate( this, offset_bytes, assert_on_fail );
				size += offset_bytes;
				offset_bytes = 0;
			}
		}
#endif
		if ( p_freeblock )
		{
			new ((void*)p_freeblock) BlockHeader( this, size );

			mUsedBlocks++;
#ifdef __EFFICIENT__
			mUsedMem += size + BlockHeader::sSize + offset_bytes;
#else			
			mUsedMem += size + BlockHeader::sSize;
#endif		// __EFFICIENT__
		}
	}

	if ( p_freeblock )
	{		
		MemDbg_AllocateBlock ( p_freeblock );

		#ifdef	__TRASH_BLOCKS__
		Trash_AllocateBlock ( p_freeblock ); 
		#endif


#ifdef	__LINKED_LIST_HEAP__    

		if( mp_context->mp_used_list )
		{
			p_freeblock->mp_next_used = mp_context->mp_used_list;
			mp_context->mp_used_list->mp_prev_used = p_freeblock;
		}
		else
		{
			p_freeblock->mp_next_used = NULL;	 	
		}	
		p_freeblock->mp_prev_used = NULL;	

#ifdef	__PLAT_NGPS__
#ifndef	__PURE_HEAP__		
		MemView_Alloc((void*)p_freeblock);
#endif
#endif
		mp_context->mp_used_list = p_freeblock; 
#endif
		AllocMemProfile(p_freeblock);

		#ifdef	__NOPT_ASSERT__
		#ifdef	__PLAT_NGPS__
		// find what allocated a particular block
		if ( ((uint)p_freeblock + BlockHeader::sSize) == REPORT_ON)
		{
			printf ("allocating 0x%x",REPORT_ON);
			DumpUnwindStack(20,NULL);
		}
		#endif
		#endif

#ifdef __EFFICIENT__
		check_address( (void*)((uint)p_freeblock + BlockHeader::sSize + offset_bytes), size );

		// Fill padding bytes with bytes offset value.
		p_freeblock->mPadBytes = offset_bytes;
		uint8 * p8 = (uint8*)((uint)p_freeblock + BlockHeader::sSize);
		for ( int lp = 0; lp < offset_bytes; lp++ )
		{
			*p8++ = offset_bytes;
		}

		return (void*)((uint)p_freeblock + BlockHeader::sSize + offset_bytes);
#else
		check_address( (void*)((uint)p_freeblock + BlockHeader::sSize), size );

		return (void*)((uint)p_freeblock + BlockHeader::sSize);
#endif		// __EFFICIENT__
	}

// Mick: heap allocations currently coded to ALWAYS assert on fail
//	if ( assert_on_fail )
	{
		Dbg_MsgAssert ( false,( "Failed to allocate %d bytes", size ));


	}

//	Mem::Heap* heap = Mem::Manager::sHandle().BottomUpHeap();
//	printf ("Dumping Fragments.....\n");
//	MemView_DumpFragments(heap);
	

	printf ("----------------------------------\n");
	printf ("failed to allocate %d bytes\n", size); 	

#ifdef __PLAT_NGC__
#ifndef __NOPT_FINAL__
	{
		Mem::Manager& mem_man = Mem::Manager::sHandle();

		printf ("MEM CONTEXT: %s\n",Mem::Manager::sHandle().GetContextName());
		printf("Name            Used  Frag  Free   Blocks\n");
		printf("--------------- ----- ----- ------ ------\n");
		Mem::Heap* heap;
		for (heap = mem_man.FirstHeap(); heap != NULL; heap = mem_man.NextHeap(heap))
		{		
				Mem::Region* region = heap->ParentRegion();			
				printf( "%12s: %6dK %6dK %6dK   %6d \n",
						heap->GetName(),
						heap->mUsedMem.m_count / 1024,
						heap->mFreeMem.m_count / 1024,
						region->MemAvailable() / 1024,
						heap->mUsedBlocks.m_count
						);										
		}
	}
#endif
#endif		// __PLAT_NGC__ 

#ifndef __PLAT_WN32__
	CFuncs::ScriptDumpHeaps(NULL,NULL);
#endif

	return NULL;

}

int	Heap::available()
{
	return LargestFreeBlock();
}

// Ken addition, for use by pip.cpp when it needs to reallocate the block of memory used
// by a pre file that has just been loaded in, so that the pre can be decompressed.
//
// This function will return a pointer to a new block of memory which includes the original
// block, but such that the returned pointer is lower down in memory than the original.
// The original contents of the memory block will be unchanged.
// This function is most likely to work if used on the top-down heap.
// It will assert if it does not work.
void* Heap::reallocate_down( size_t newSize, void *pOld )
{
	// What this function is going to do is allocate a new block with size equal to 
	// the difference between newSize and the size of pOld, then see if the new block 
	// is directly below the passed pOld.
	// If it is, then it will merge the two blocks together.
	// If it is not, it will assert.
	
	Dbg_MsgAssert(pOld,("NULL pOld sent to reallocate_down !"));
	BlockHeader* p_old_block = BlockHeader::sRead( pOld ); 
	
#ifdef __EFFICIENT__
	newSize = (uint)nAlignUpBy( newSize, 2 );	// all allocations aligned by 4 bytes
#else
#ifdef __PLAT_NGC__
	newSize = (uint)nAlignUpBy( newSize, 5 );	// all allocations aligned by 32 bytes
#else
	newSize = (uint)nAlignUpBy( newSize, 4 );	// all allocations aligned by 16 bytes
#endif
#endif		// __EFFICIENT__
	
	Dbg_MsgAssert(p_old_block->mSize < newSize,("Tried to reallocate a block that was already big enough, old size=%d, requested size=%d",p_old_block->mSize,newSize));
	
	// When bobbling together the new and old blocks, we will gain the space used by the old
	// block header, so we don't really need to include that space in the new allocation request.
	// But, if newSize is exactly a blockheader size bigger than the old, then that would cause
	// allocate to be called on a size of zero.
	// So allocate a blockheader size more than necessary to avoid this.
	void *p_new=allocate(newSize-p_old_block->mSize,true);
	Dbg_MsgAssert(p_new,("allocate failed!"));
	
	// Got the new block, so now check that it is directly below the old.	
	BlockHeader* p_new_block=BlockHeader::sRead( p_new );
	Dbg_MsgAssert( (BlockHeader*)(((uint)p_new)+p_new_block->mSize) == p_old_block,("reallocate_down failed! New block is not directly below the old."));
	
	// Got this far, so the new block is right below the old.
	// Now we have to remove the old block header so as to bobble the two blocks together.


#ifdef	__LINKED_LIST_HEAP__    

#ifdef	__PLAT_NGPS__
#ifndef	__PURE_HEAP__	  
	// Both blocks need to be freed from the memory tracker
	// as together they take up all the space of the new block, whcih will be added below  
	MemView_Free((void*)p_old_block); 
	MemView_Free((void*)p_new_block); 
#endif
#endif

	// mp_next_used and mp_prev_used are a doble linked null ternimated list

	// unlink next and prev
	if ( p_old_block->mp_next_used )
	{
		p_old_block->mp_next_used->mp_prev_used = p_old_block->mp_prev_used;
	}
	if ( p_old_block->mp_prev_used )
	{
		p_old_block->mp_prev_used->mp_next_used = p_old_block->mp_next_used;
	}
	// unlink if it's the head of the list
	if ( mp_context->mp_used_list == p_old_block )
	{
		mp_context->mp_used_list = p_old_block->mp_next_used;
	}

	p_old_block->mp_prev_used = NULL;
	p_old_block->mp_next_used = NULL;
#endif
// Free both blocks, so we can just add one later
	FreeMemProfile(p_old_block);
	FreeMemProfile(p_new_block);

#ifdef __EFFICIENT__
	newSize += p_old_block->mPadBytes;
#endif		// __EFFICIENT__

	p_old_block->~BlockHeader();	
	#ifdef	__TRASH_BLOCKS__
	Trash_FreeBlockHeader ( p_old_block ); 
	#endif
	
	// The memory occupied by the old block header has gone from being used as a BlockHeader, 
	// to being used as part of a used memory block, so mUsedMem does not need to change.
	// The number of used blocks has changed though, since two are being merged into one.
	--mUsedBlocks;
	// Nothing has changed regarding free blocks, so mFreeBlocks and mFreeMem stay the same.
	
	
	// That's p_old_block cleaned up, so now modify p_new_block's size.
	p_new_block->mSize=newSize+BlockHeader::sSize;

#ifdef	__PLAT_NGPS__
#ifndef	__PURE_HEAP__		
	MemView_Alloc((void*)p_new_block);
#endif
#endif
	AllocMemProfile(p_new_block);

	
#ifdef __EFFICIENT__
	return (void*)((uint)p_new_block + BlockHeader::sSize + p_new_block->mPadBytes);
#else
	return (void*)((uint)p_new_block + BlockHeader::sSize);
#endif		// __EFFICIENT__
}

// This will make the passed memory block bigger.
// The original contents of the memory block will be unchanged.
// This function is most likely to work if used on the bottom-up heap.
// It will assert if it does not work.
void *Heap::reallocate_up( size_t newSize, void *pOld )
{
	// What this function is going to do is allocate a new block with size equal to 
	// the difference between newSize and the size of pOld, then see if the new block 
	// is directly above the passed pOld.
	// If it is, then it will merge the two blocks together.
	// If it is not, it will assert.
	
	Dbg_MsgAssert(pOld,("NULL pOld sent to reallocate_up !"));
	BlockHeader* p_old_block = BlockHeader::sRead( pOld ); 
	
#ifdef __EFFICIENT__
	newSize = (uint)nAlignUpBy( newSize, 2 );	// all allocations aligned by 4 bytes
#else
#ifdef __PLAT_NGC__
	newSize = (uint)nAlignUpBy( newSize, 5 );	// all allocations aligned by 32 bytes
#else
	newSize = (uint)nAlignUpBy( newSize, 4 );	// all allocations aligned by 16 bytes
#endif
#endif		// __EFFICIENT__
	
	if (p_old_block->mSize >= newSize)
	{
		// The current block is already big enough, so nothing to do.
		return pOld;
	}
		
	// When bobbling together the new and old blocks, we will gain the space used by the old
	// block header, so we don't really need to include that space in the new allocation request.
	// But, if newSize is exactly a blockheader size bigger than the old, then that would cause
	// allocate to be called on a size of zero.
	// So allocate a blockheader size more than necessary to avoid this.
	void *p_new=allocate(newSize-p_old_block->mSize,true);
	Dbg_MsgAssert(p_new,("allocate failed!"));
	
	// Got the new block, so now check that it is directly above the old.	
	BlockHeader* p_new_block=BlockHeader::sRead( p_new );
	if ( (BlockHeader*)(((uint)pOld)+p_old_block->mSize) != p_new_block)
	{
		// It isn't!
		free(p_new_block);
		return NULL;
	}	
	
	// Got this far, so the new block is right above the old.
	// Now we have to remove the new block header so as to bobble the two blocks together.


#ifdef	__LINKED_LIST_HEAP__    

// remove both blocks from memory tracking, ready to add the new block
// that encompasses both of them
#ifdef	__PLAT_NGPS__
#ifndef	__PURE_HEAP__		
	MemView_Free((void*)p_old_block); 
	MemView_Free((void*)p_new_block); 
#endif
#endif
	// mp_next_used and mp_prev_used are a doble linked null ternimated list

	// unlink next and prev
	if ( p_new_block->mp_next_used )
	{
		p_new_block->mp_next_used->mp_prev_used = p_new_block->mp_prev_used;
	}
	if ( p_new_block->mp_prev_used )
	{
		p_new_block->mp_prev_used->mp_next_used = p_new_block->mp_next_used;
	}
	// unlink if it's the head of the list
	if ( mp_context->mp_used_list == p_new_block )
	{
		mp_context->mp_used_list = p_new_block->mp_next_used;
	}

	p_new_block->mp_prev_used = NULL;
	p_new_block->mp_next_used = NULL;
#endif
	FreeMemProfile(p_old_block);
	FreeMemProfile(p_new_block);

	// (Mick) Since the alloc function might return a bigger block than was asked for
	// we need to account for this in calculating the size of the new block
	// we can get the size of the new block before we trash the header
	int new_block_size = p_new_block->mSize;

	p_new_block->~BlockHeader();	
	#ifdef	__TRASH_BLOCKS__
	Trash_FreeBlockHeader ( p_new_block ); 
	#endif
	
	// The memory occupied by the new block header has gone from being used as a BlockHeader, 
	// to being used as part of a used memory block, so mUsedMem does not need to change.
	// The number of used blocks has changed though, since two are being merged into one.
	--mUsedBlocks;
	// Nothing has changed regarding free blocks, so mFreeBlocks and mFreeMem stay the same.

#ifdef	__NOPT_ASSERT__
	if (pOld == (void*)REPORT_ON)
	{
		printf ("%p: realloced up from %d to %d (actually %d) bytes\n", p_old_block, p_old_block->mSize, newSize, new_block_size + BlockHeader::sSize);  	
	}
#endif
	
	
	// That's p_new_block cleaned up, so now modify p_old_block's size.
//	p_old_block->mSize=newSize+BlockHeader::sSize;		 // Old method, did not account for alloc returning oversized blocks
	p_old_block->mSize += new_block_size + BlockHeader::sSize;	  // (Mick) New method, uses the actual size of the new block (plus blockheader)

#ifdef	__PLAT_NGPS__
#ifndef	__PURE_HEAP__		
	MemView_Alloc((void*)p_old_block); 
#endif
#endif
	AllocMemProfile(p_old_block);


	
	return pOld;
}

void *Heap::reallocate_shrink( size_t newSize, void *pOld )
{
	// This will shrink the passed block by creating a new free block out of the remaining
	// space, if there is enough room to make a new free block. (at least 32 bytes needed)
	// It creates the new free block by first creating a new used block, then freeing that.
	// That way, all the merging of free blocks will be taken care of.
		
	Dbg_MsgAssert(pOld,("NULL pOld sent to reallocate_up !"));
	BlockHeader* p_old_block = BlockHeader::sRead( pOld ); 


	Dbg_MsgAssert(p_old_block->mpAlloc == this,("Shrinking block in wrong context"));


	
	//printf("Block size before shrinking = %d\n",p_old_block->mSize);
	
	Dbg_MsgAssert(newSize<=p_old_block->mSize,("Larger size sent to reallocate_shrink:\nold size=%d  requested new size=%d",p_old_block->mSize,newSize));
	uint32 size_diff=p_old_block->mSize-newSize;
	
	// We can only shrink down by a number of bytes that is divisible be 16 (4 on ngc), so
	// that the resulting block size for the old block remains a multiple of 16 (4)
#ifdef __EFFICIENT__
	size_diff&=~((1<<2)-1);
#else
	#ifdef __PLAT_NGC__
	size_diff&=~((1<<5)-1);
	#else
	size_diff&=~((1<<4)-1);
	#endif
#endif		// __EFFICIENT__
	
	// If the amount the block is being shrunk by is not enough to hold a block header
	// with a bit left over, then we can't make a new free block with it, so return without
	// doing anything.
	if (size_diff<=BlockHeader::sSize)
	{
		return pOld;
	}	
	
	// Calculate a pointer to the new block.
	BlockHeader *p_new_block=(BlockHeader*)((uint32)pOld+p_old_block->mSize-size_diff);

	#ifdef	__PLAT_NGPS__
	Dbg_MsgAssert(((int)p_new_block&0xf) == 0,("p_new_block odd (%p), pOld = %p, p_old_block = %p, p_old_block->mSize = %d, size_diff = %d",
										p_new_block, pOld, p_old_block->mSize, size_diff));
	#endif
	
	// Fill in new block header
	p_new_block->mSize=size_diff-BlockHeader::sSize;
#ifdef __EFFICIENT__
	p_new_block->mPadBytes = 0;
#endif		// __EFFICIENT__ 
	/////////////////////////////////////////////////////////////////////
	// Do all the stuff one needs to do when creating a new used block.
	// I just happily cut-and-pasted this lot from ::allocate
	p_new_block->mpAlloc = this;
	MemDbg_AllocateBlock ( p_new_block );
	#ifdef	__TRASH_BLOCKS__
	Trash_AllocateBlock ( p_new_block ); 
	#endif

#ifdef	__LINKED_LIST_HEAP__    

	if( mp_context->mp_used_list )
	{
		p_new_block->mp_next_used = mp_context->mp_used_list;
		mp_context->mp_used_list->mp_prev_used = p_new_block;
	}
	else
	{
		p_new_block->mp_next_used = NULL;	 	
	}	
	p_new_block->mp_prev_used = NULL;	

#ifdef	__PLAT_NGPS__
#ifndef	__PURE_HEAP__		
	MemView_Alloc((void*)p_new_block);
#endif
#endif
	mp_context->mp_used_list = p_new_block; 
#endif
	AllocMemProfile(p_new_block);
	/////////////////////////////////////////////////////////////////////
	
	// mUsedMem does not change, since we've made a new used block out of memory
	// that was already being used.
	// The number of used blocks has increased by one though.
	++mUsedBlocks;
	// Nothing has changed regarding mFreeBlocks or mFreeMem yet. That will all be handled
	// when the block gets freed.
	
	// Free the block just created.
	free(p_new_block);
		

#ifdef	__PLAT_NGPS__
#ifndef	__PURE_HEAP__		
	MemView_Free((void*)p_old_block);	   // free at old size
#endif
#endif
	FreeMemProfile(p_old_block);

	// Update the size of the old block.
	p_old_block->mSize-=size_diff;
	
#ifdef	__PLAT_NGPS__
#ifndef	__PURE_HEAP__		
	MemView_Alloc((void*)p_old_block);	   // re-register at new size
#endif
#endif
	AllocMemProfile(p_old_block);

	
	//printf("New block size after shrinking = %d\n",p_old_block->mSize);
	return pOld;
}

/****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Heap::Heap( Region* region, Direction dir, char *p_name )
: Allocator( region, dir, p_name )
{
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mem



================================================
FILE: Code/Sys/Mem/heap.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Manager (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		core/sys/mem/heap.h										**
**																			**
*****************************************************************************/

#ifndef	__SYS_MEM_HEAP_H
#define	__SYS_MEM_HEAP_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include "alloc.h"

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mem
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

class Region;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
class  Heap : public  Allocator 			
{
	
	
	friend class Manager;

public :
		
								Heap( Region* region, Direction dir = vBOTTOM_UP, char* p_name = "unknown heap" );

	int							LargestFreeBlock();
	
private :
	
	virtual		void*			allocate( size_t size, bool assert_on_fail );
	virtual		void			free( BlockHeader* pHeader );

	virtual		int				available();
	virtual		void* 			reallocate_down( size_t new_size, void *pOld );
	virtual		void*			reallocate_up( size_t newSize, void *pOld );
	virtual		void*			reallocate_shrink( size_t newSize, void *pOld );
	
				
				BlockHeader*	next_addr( BlockHeader* pHeader );
};


/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Mem

#endif  // __SYS_MEM_HEAP_H


================================================
FILE: Code/Sys/Mem/memdbg.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Management (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		sys/mem/memdbg.h										**
**																			**
*****************************************************************************/

#ifndef	__SYS_MEM_MEMDBG_H
#define	__SYS_MEM_MEMDBG_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

namespace Mem
{

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#if defined( __PLAT_WN32__ ) || defined( __PLAT_XBOX__ )
static const uint64 vALLOCATED_BLOCK = 0x0101010101010101i64;
static const uint64 vFREE_BLOCK 	 = 0x0303030303030303i64;
#else
static const uint64 vALLOCATED_BLOCK = 0x0101010101010101LL;
static const uint64 vFREE_BLOCK 	 = 0x0303030303030303LL;
#endif	// __PLAT_WN32__

#ifdef __NOPT_MEM_DEBUG__
	
#define MemDbg_AllocateBlock(_header)	_header->DbgAllocateBlock()
#define MemDbg_FreeBlock(_header)		_header->DbgFreeBlock()

#else // !__NOPT_MEM_DEBUG__

#define MemDbg_AllocateBlock(_header)
#define MemDbg_FreeBlock(_header)

#endif

/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/
   
/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Mem

#endif  // __SYS_MEM_MEMDBG_H								


================================================
FILE: Code/Sys/Mem/memman.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		System Library											**
**																			**
**	Module:			Memory (Mem) 											**
**																			**
**	File name:		memman.cpp												**
**																			**
**	Created by:		03/20/00	-	mjb										**
**																			**
**	Description:	Memory manager											**
**																			**
*****************************************************************************/



/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 
#include "memman.h"
#include 
#include 	// for memory profiling, to see if we ahve the extra memory
#include "heap.h"
#include "alloc.h"
#include 
#ifdef __PLAT_XBOX__
#include 
#endif
#ifdef __PLAT_NGC__
#include 
#endif

#include 


#ifdef	__PLAT_NGPS__

static	bool	s_use_semaphore = false;

void WaitSemaMaybe(int sema)
{
	if (s_use_semaphore)
	{
		WaitSema(sema);
	}
}


void SignalSemaMaybe(int sema) 
{
	if (s_use_semaphore)
	{
		SignalSema(sema);
	}
}

#endif


/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/


/*****************************************************************************
**								  Externals									**
*****************************************************************************/

#if defined ( __PLAT_NGPS__ )
extern char _code_start[]					__attribute__((section(".text")));
extern char _code_end[]						__attribute__((section(".text")));
extern char _data_end[]						__attribute__((section(".text")));
extern char _mem_start[]					__attribute__((section(".mem_block")));
extern char _mem_end[]						__attribute__((section(".mem_block")));
extern char _std_mem_end[]					__attribute__((section(".mem_block")));
extern char _debug_heap_start[]				__attribute__((section(".mem_block")));
extern char _script_heap_start[]			__attribute__((section(".mem_block")));
#else
char* 	_code_start;
char* 	_code_end;
char* 	_data_end;
char* 	_mem_start;
char* 	_mem_end;
char*	_std_mem_end;
char *	_debug_heap_start;
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/


namespace Mem
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

char 			Manager::s_manager_buffer[sizeof(Manager)];
char 			Manager::s_region_buffer[sizeof(Region)];
char 			Manager::s_script_region_buffer[sizeof(Region)];
char 			Manager::s_top_heap_buffer[sizeof(Heap)];
char 			Manager::s_bot_heap_buffer[sizeof(Heap)];
char 			Manager::s_debug_heap_buffer[sizeof(Heap)];
Manager*		Manager::sp_instance = NULL;
#ifdef __PLAT_NGPS__
static	int		s_context_semaphore;
#endif
char			Manager::s_debug_region_buffer[sizeof(Region)];

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::Manager( void )
{
	
	
	m_current_id = 0;
	mp_process_man = NULL;
	
#	if defined ( __PLAT_XBOX__ )
	// Just grab 33mb of main memory.
	_mem_start		= (char*)XPhysicalAlloc(	33 * 1024 * 1024,				// size of region
												MAXULONG_PTR,					// base physical address
												0,								// memory alignment
												PAGE_READWRITE );				// memory protection and type

	_mem_end		= _mem_start + ( 33 * 1024 * 1024 );
	_std_mem_end	= _mem_end;
#	elif defined ( __PLAT_WN32__ )
	// Nasty hack for WN32 for now - just grab 38mb of main memory via a malloc.
	_mem_start	= (char *)malloc( 38 * 1024 * 1024 );
	_mem_end	= _mem_start + ( 38 * 1024 * 1024 );
	_std_mem_end = _mem_end;

#	endif 
#	if defined ( __PLAT_NGC__ )
	// Fill in what we know.
	_code_start		= (char *)0xfadebabe;		// Junk addresses.
	_code_end		= (char *)0xfacced00;
	_data_end		= (char *)0xcaccfacc;
	_mem_start		= &((char *)OSGetArenaLo())[8192];  // Real, actual useful addresses.
	_mem_end		= (char *)OSGetArenaHi();
	_std_mem_end	= (char *)OSGetArenaHi();
#	endif

	// create root memory region
	mp_region 	= new ((void*)s_region_buffer) Region( nAlignUp( _mem_start ), nAlignDown( _std_mem_end ) );

	// create default heaps
	mp_top_heap = new ((void*)s_top_heap_buffer) Heap( mp_region, Heap::vTOP_DOWN, "Top Down" );
	mp_bot_heap = new ((void*)s_bot_heap_buffer) Heap( mp_region, Heap::vBOTTOM_UP, "BottomUp" );

	m_heap_list[0] = mp_top_heap;
	m_heap_list[1] = mp_bot_heap;
	
	m_num_heaps = 2;

#	if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
	uint codesize = ((uint)(_code_end) - (uint)(_code_start))/1024;
	uint datasize = ((uint)(_data_end) - (uint)(_code_end))/1024;
	printf ( "code [%p - %p] (%dK) + data [%p - %p] (%dK) = %dK \n",
			 _code_start, _code_end, codesize, 
			 _code_end, _data_end, datasize,
			 codesize + datasize );
#endif
			 
	m_pushed_context_count = 0;
	mp_internet_region = NULL;
	mp_net_misc_region = NULL;

	mp_cutscene_region = NULL;
	mp_cutscene_bottom_heap = NULL;
	mp_cutscene_top_heap = NULL;

#ifdef __PLAT_NGPS__
	// Create a semaphore to prevent threads from re-entering push/pop memory contexts
	struct SemaParam params;

	params.initCount = 1;
	params.maxCount = 10;

	s_context_semaphore = CreateSema( ¶ms );			 
#endif

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
	

	mp_top_heap->~Heap();
	mp_bot_heap->~Heap();
	mp_region->~Region();

#ifdef __PLAT_NGPS__
	DeleteSema( s_context_semaphore );
#endif
}


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

void*	Manager::New( size_t size, bool assert_on_fail, Allocator* pAlloc )
{   
#ifdef __PLAT_NGPS__
	WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

	if ( !pAlloc ) 					// set to 'default' allocator
	{
		pAlloc = mp_context->mp_alloc;
	}

	void* p_ret = pAlloc->allocate( size, assert_on_fail );
	
	if ( p_ret )	// if allocation was successful
	{
		Allocator::s_set_id( p_ret );		// stamp ID; used by Mem::Handle
	}

#if 0 
	if ( mp_process_man )
	{
		mp_process_man->AllocEv( p_ret, size, pAlloc );
	}
#endif

#ifdef __PLAT_NGPS__
	SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

	return p_ret;
}


// Returns the amount of memory avaialbe in the current context
// currently this is only valid for Heaps
int		Manager::Available()
{
	return mp_context->mp_alloc->available();
}

// Ken addition, for use by pip.cpp when it needs to reallocate the block of memory used
// by a pre file that has just been loaded in, so that the pre can be decompressed.
void*	Manager::ReallocateDown( size_t newSize, void *pOld, Allocator* pAlloc )
{   
#ifdef __PLAT_NGPS__
	WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

	if ( !pAlloc ) 					// set to 'default' allocator
	{
		pAlloc = mp_context->mp_alloc;
	}

	void* p_ret = pAlloc->reallocate_down( newSize, pOld );
	
	if ( p_ret )	// if allocation was successful
	{
		Allocator::s_set_id( p_ret );		// stamp ID; used by Mem::Handle
	}

#ifdef __PLAT_NGPS__
	SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

	return p_ret;
}

// Ken addition, for use by pip.cpp when it needs to reallocate the block of memory used
// by a pre file that has just been loaded in, so that the pre can be decompressed.
// This is for when the bottoms-up heap is being used.
void *Manager::ReallocateUp( size_t newSize, void *pOld, Allocator* pAlloc )
{   
#ifdef __PLAT_NGPS__
	WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

	if ( !pAlloc ) 					// set to 'default' allocator
	{
		pAlloc = mp_context->mp_alloc;
	}

	void* p_ret = pAlloc->reallocate_up( newSize, pOld );
	
	if ( p_ret )	// if allocation was successful
	{
		Allocator::s_set_id( p_ret );		// stamp ID; used by Mem::Handle
	}

#ifdef __PLAT_NGPS__
	SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

	return p_ret;
}

void *Manager::ReallocateShrink( size_t newSize, void *pOld, Allocator* pAlloc )
{   
#ifdef __PLAT_NGPS__
	WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

	if ( !pAlloc ) 					// set to 'default' allocator
	{
		pAlloc = mp_context->mp_alloc;
	}

	void* p_ret = pAlloc->reallocate_shrink( newSize, pOld );
	
	if ( p_ret )	// if allocation was successful
	{
		Allocator::s_set_id( p_ret );		// stamp ID; used by Mem::Handle
	}

#ifdef __PLAT_NGPS__
	SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

	return p_ret;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::PushMemoryMarker( uint32 uiID )
{
}
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::PopMemoryMarker( uint32 uiID )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void	Manager::Delete( void* pAddr )
{
#ifdef __PLAT_NGPS__
	WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

	if( pAddr != NULL )
	{
		Allocator::s_free( pAddr );

#if 0
		// 000810 JAB: Modified s_free to return the allocator.
		if (mp_process_man)
		{
			mp_process_man->FreeEv( pAddr, p_alloc );
		}
#endif

	}

#ifdef __PLAT_NGPS__
	SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


bool	Manager::Valid( void* pAddr )
{
	if( pAddr != NULL )
	{
		return Allocator::s_valid( pAddr );
	}
	else
	{
		return false;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


size_t	Manager::GetAllocSize( void* pAddr )
{
	if( pAddr != NULL )
	{
		return Allocator::s_get_alloc_size( pAddr );
	}
	else
	{
		Dbg_MsgAssert( 0, ( "Trying to get the size of an invalid block" ) );
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::PushContext( Allocator* alloc )
{
	
	Dbg_AssertType ( alloc, Allocator );

#ifdef __PLAT_NGPS__
	WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

//	printf ("Pushed context %d to %s\n",m_pushed_context_count, alloc->GetName());
//	DumpUnwindStack(20,0);

	Dbg_MsgAssert(m_pushed_context_count < vMAX_CONTEXT-1,("Pushed too many contexts"));
	mp_context = &m_contexts[m_pushed_context_count];	
	mp_context->mp_alloc = alloc;	
	m_pushed_context_count++;

#ifdef __PLAT_NGPS__
	SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::PopContext( void )
{
	

#ifdef __PLAT_NGPS__
	WaitSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__

	Dbg_MsgAssert( m_pushed_context_count,( "Heap stack underflow" ));
	
	m_pushed_context_count--;
	m_contexts[m_pushed_context_count].mp_alloc = (Mem::Allocator*)-1;	
	if (m_pushed_context_count)
	{
		mp_context = &m_contexts[m_pushed_context_count-1];		
	}
	else
	{
		mp_context = NULL;	 // stack has now been emptied
	}

#ifdef __PLAT_NGPS__
	SignalSemaMaybe( s_context_semaphore );
#endif // __PLAT_NGPS__
}

//GJ:  Context number doesn't seem to be used...
//Originally, it was used so that you could access the heaps from script,
//but now you can access the heaps using names, so the numbers aren't needed
//any more
//int Manager::GetContextNumber()
//{   
//	return mp_context->mp_alloc->GetNumber();		
//}

char * Manager::GetContextName()
{
	
	return mp_context->mp_alloc->GetName();		
}

Allocator* Manager::GetContextAllocator()
{
	
	return mp_context->mp_alloc;		
}

// Added by Ken, so that pip.cpp knows whether to try and expand a memory block up or down.
Allocator::Direction Manager::GetContextDirection()
{
	
	return mp_context->mp_alloc->GetDirection();		
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

const char* Manager::GetHeapName( uint32 whichHeap )
{
	// first look through the named heaps...
	CNamedHeapInfo* pInfo = find_named_heap_info( whichHeap );
	if ( pInfo )
	{
		return pInfo->mp_heap_name;
	}
	
	switch ( whichHeap )
	{
		case 0xc7800b0:		// bottomupheap
			return "BottomUpHeap";
			break;
		case 0x8fdb68af:		// topdownheap
			return "TopDownHeap";
			break;
		case 0x62f3a0f3:		// frontendheap
			return "FrontEndHeap";
			break;
		case 0xe3551d2e:		// networkheap
			return "NetworkHeap";
			break;
		case 0x96d29d93:		// netmischeap
			return "NetMiscHeap";
			break;
		case 0xfcd5166b:		// internettopdownheap
			return "InternetTopDownHeap";
			break;
		case 0x90020867:		// internetbottomupheap
			return "InternetBottomUpHeap";
			break;
		case 0xfa33d9b:		// scriptheap
			return "ScriptHeap";
			break;
		case 0x70cb0238:		// debugheap
			return "DebugHeap";
			break;
		case 0xc3909393:		// skaterheap0
			return "Skater0";
			break;
		case 0xb497a305:		// skaterheap1
			return "Skater1";
			break;
		case 0x2d9ef2bf:		// skaterheap2
			return "Skater2";
			break;
		case 0x5a99c229:		// skaterheap3
			return "Skater3";
			break;
		case 0x572a9f4c:		// skatergeomheap0
			return "SkaterGeom0";
			break;
		case 0x202dafda:		// skatergeomheap1
			return "SkaterGeom1";
			break;
		case 0xb924fe60:		// skatergeomheap2
			return "SkaterGeom2";
			break;
		case 0xce23cef6:		// skatergeomheap3
			return "SkaterGeom3";
			break;
		case 0x8682d24:		// skaterinfoheap
			return "SkaterInfo";
			break;
        case 0x17ecb880:		// themeheap
			return "ThemeHeap";
            break;
        case 0x7ed56b49:		// CutsceneBottomUpHeap
			return "CutsceneBottomUp";
            break;
        case 0x25d71469:		// CutsceneTopDownHeap
			return "CutsceneTopDown";
            break;
		default:	// unrecognized heap
			Dbg_Assert ( 0 );
	}

	return "Unrecognized Heap";
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Heap* Manager::GetHeap( uint32 whichHeap )
{
	Heap* pHeap = NULL;

	// first look through the named heaps...
	CNamedHeapInfo* pInfo = find_named_heap_info( whichHeap );
	if ( pInfo )
	{
		return pInfo->mp_heap;
	}

	switch ( whichHeap )
	{
		case 0xc7800b0:		// bottomupheap
			pHeap = BottomUpHeap();
			break;
		case 0x8fdb68af:		// topdownheap
			pHeap = TopDownHeap();
			break;
		case 0x62f3a0f3:		// frontendheap
			pHeap = FrontEndHeap();
			break;
		case 0xe3551d2e:		// networkheap
			pHeap = NetworkHeap();
			break;
		case 0x96d29d93:		// netmischeap
			pHeap = NetMiscHeap();
			break;
		case 0xfcd5166b:		// internettopdownheap
			pHeap = InternetTopDownHeap();
			break;
		case 0x90020867:		// internetbottomupheap
			pHeap = InternetBottomUpHeap();
			break;
		case 0xfa33d9b:		// scriptheap
			pHeap = ScriptHeap();
			break;
		case 0x70cb0238:		// debugheap
			pHeap = DebugHeap();
			break;
		case 0xc3909393:		// skaterheap0
			pHeap = SkaterHeap(0);
			break;
		case 0xb497a305:		// skaterheap1
			pHeap = SkaterHeap(1);
			break;
		case 0x2d9ef2bf:		// skaterheap2
			pHeap = SkaterHeap(2);
			break;
		case 0x5a99c229:		// skaterheap3
			pHeap = SkaterHeap(3);
			break;
		case 0x572a9f4c:		// skatergeomheap0
			pHeap = SkaterGeomHeap(0);
			break;
		case 0x202dafda:		// skatergeomheap1
			pHeap = SkaterGeomHeap(1);
		break;
		case 0xb924fe60:		// skatergeomheap2
			pHeap = SkaterGeomHeap(2);
		break;
		case 0xce23cef6:		// skatergeomheap3
			pHeap = SkaterGeomHeap(3);
		break;
		case 0x8682d24:		// skaterinfoheap
			pHeap = SkaterInfoHeap();
		break;
        case 0x17ecb880:		// themeheap
			pHeap = ThemeHeap();
		break;
        case 0x7ed56b49:		// CutsceneBottomUpHeap
			pHeap = CutsceneBottomUpHeap();
		break;
        case 0x25d71469:		// CutsceneTopDownHeap
			pHeap = CutsceneTopDownHeap();
		break;
		default:	// unrecognized heap
			Dbg_MsgAssert ( 0, ( "Unrecognized heap %08x", whichHeap ) );
	}

	return pHeap;
}


Mem::Heap *Manager::CreateHeap( Region* region, Mem::Allocator::Direction dir, char* p_name)
{
	Mem::Heap * pHeap = new Mem::Heap(region, dir, p_name);
	// At this point we can maintain the heap list 
	m_heap_list[m_num_heaps++] = pHeap;
	return pHeap;
}

void Manager::RemoveHeap(Mem::Heap *pHeap)
{

#ifndef __PLAT_WN32__
	#ifdef	__NOPT_ASSERT__			 
	// Check first to see if there is something on the heap before it is deleted
	// Deleting a heap with stuff on it might indicate an error
	if (pHeap->mUsedBlocks.m_count)
	{
		printf ("Deleting a heap %s with %d used blocks still on it\n",pHeap->mp_name,pHeap->mUsedBlocks.m_count);
		#ifndef __PLAT_NGC__
		printf ("\n\nDumping Heap\n");
		MemView_DumpHeap(pHeap);
		printf ("\n\nAnalyzing Heap\n");
		MemView_AnalyzeHeap(pHeap);
		Dbg_MsgAssert(0, ("Deleting heap <%s> with %d used blocks still on it\n",pHeap->mp_name,pHeap->mUsedBlocks.m_count));
		
		#endif		// __PLAT_NGC__
	}
	#endif
#endif

	// remove from list of heaps
	for (int i=0;iPushContext( sp_instance->mp_bot_heap );
		#else
		// but with assertions there is not enough room, so put it on the debug heap
		if (Config::GotExtraMemory())
		{
			sp_instance->PushContext( sp_instance->mp_debug_heap );			
		}
		else
		{
			// running on a regular PS2, allow them to try, but will probably fail later with out of memory
			sp_instance->PushContext( sp_instance->mp_bot_heap );
		}
		#endif		
		
		mp_net_misc_region = new Mem::AllocRegion( (NETMISC_HEAP_SIZE) );	
		mp_net_misc_heap = CreateHeap( mp_net_misc_region, Mem::Allocator::vBOTTOM_UP, "NetMisc" );

		sp_instance->PopContext();
	}
}

void Manager::DeleteNetMiscHeap()
{
	if( mp_net_misc_region )
	{
		RemoveHeap( mp_net_misc_heap );
		delete mp_net_misc_region;
		mp_net_misc_region = NULL;
	}
}

void Manager::InitInternetHeap()
{
	if( mp_internet_region == NULL )
	{

		//#ifndef	__NOPT_ASSERT__
		#if	1		// always allocate internet heap normally
		// normally the internet heap just goes on the top down heap
		sp_instance->PushContext( sp_instance->mp_bot_heap );
		#else
		// but with assertions there is not enough room, so put it on the debug heap
		if (Config::GotExtraMemory())
		{
			sp_instance->PushContext( sp_instance->mp_debug_heap );			
		}
		else
		{
			// running on a regular PS2, allow them to try, but will probably fail later with out of memory
			sp_instance->PushContext( sp_instance->mp_bot_heap );
		}
		#endif		
		
		mp_internet_region = new Mem::AllocRegion( (INTERNET_HEAP_SIZE) );	
		mp_internet_top_heap = CreateHeap( mp_internet_region, Mem::Allocator::vTOP_DOWN, "InternetTopDown" );
		mp_internet_bottom_heap = CreateHeap( mp_internet_region, Mem::Allocator::vBOTTOM_UP, "InternetBottomUp" );

		sp_instance->PopContext();
	}
}

void Manager::DeleteInternetHeap()
{
	if( mp_internet_region )
	{
		RemoveHeap( mp_internet_top_heap );
		RemoveHeap( mp_internet_bottom_heap );
		delete mp_internet_region;
		mp_internet_region = NULL;
	}
}

void Manager::InitCutsceneHeap( int heap_size )
{
	// Note that it will create the cutscene heap on the current mem context...

	if ( mp_cutscene_region == NULL )
	{
		// put it on the top-down heap, because it's used only temporarily
//		sp_instance->PushContext( sp_instance->mp_top_heap );
		
		mp_cutscene_region = new Mem::AllocRegion( heap_size );

		Dbg_MsgAssert( mp_cutscene_bottom_heap == NULL, ( "CutsceneBottomUpHeap already exists" ) );
		mp_cutscene_bottom_heap = CreateHeap( mp_cutscene_region, Mem::Allocator::vBOTTOM_UP, "CutsceneBottomUp" );
		
		Dbg_MsgAssert( mp_cutscene_top_heap == NULL, ( "CutsceneTopDownHeap already exists" ) );
		mp_cutscene_top_heap = CreateHeap( mp_cutscene_region, Mem::Allocator::vTOP_DOWN, "CutsceneTopDown" );

//		sp_instance->PopContext();
	}
}

void Manager::DeleteCutsceneHeap()
{
	if ( mp_cutscene_region )
	{
		RemoveHeap( mp_cutscene_bottom_heap );
		mp_cutscene_bottom_heap = NULL;
		
		RemoveHeap( mp_cutscene_top_heap );
		mp_cutscene_top_heap = NULL;

		delete mp_cutscene_region;
		mp_cutscene_region = NULL;
	}
}


void Manager::InitDebugHeap()
{
	#ifdef	__PLAT_NGPS__
	// The Debug heap is allocated directly from debug memory (>32MB on PS2)
	// as such, it should only ever be used on the TOOL (T10K) debug stations, or equivalents on other platforms 
	mp_debug_region 	= new ((void*)s_debug_region_buffer) Region( nAlignUp( _debug_heap_start ), nAlignDown( _debug_heap_start+DEBUG_HEAP_SIZE ) );
	mp_debug_heap = CreateHeap( mp_debug_region, Mem::Allocator::vBOTTOM_UP, "debug" );
	#endif
}

void Manager::InitSkaterHeaps(int players)
{
	printf ("Init Skater Heaps\n");
	   
	// some game modes specify 8 players, however 4 of those
	// are observers ??? 
	// anyway, for now, just don't allow them to create more heaps than the max
	// it will assert later if you try to access a heap that has not been created							   
	if (players > NUM_SKATER_HEAPS)
	{
		players = NUM_SKATER_HEAPS;
	}
	
	// Initialize the other skater heaps
	for (int heap = NUM_PERM_SKATER_HEAPS; heap < players; heap++)
	{
		Dbg_MsgAssert(!mp_skater_region[heap],( "Skater region %d is still active!!!\n",heap));
		Dbg_MsgAssert(!mp_skater_heap[heap],( "Skater heap %d is still active!!!\n",heap));
		mp_skater_region[heap] = new Mem::AllocRegion( (SKATER_HEAP_SIZE) );	
		printf ("EXTRA: allocated mp_skater_region at %p\n",mp_skater_region[heap]);
		mp_skater_heap[heap] = CreateHeap( mp_skater_region[heap], Mem::Allocator::vBOTTOM_UP, "skaterX" );
		printf ("EXTRA: Setup mp_skater_heap at %p\n",mp_skater_heap[heap]);
	
		Dbg_MsgAssert(!mp_skater_geom_region[heap],( "Skater geom region %d is still active!!!\n",heap));
		Dbg_MsgAssert(!mp_skater_geom_heap[heap],( "Skater geom heap %d is still active!!!\n",heap));
		mp_skater_geom_region[heap] = new Mem::AllocRegion( (SKATER_GEOM_HEAP_SIZE) );	
		printf ("EXTRA: allocated mp_skater_region at %p\n",mp_skater_geom_region[heap]);
		mp_skater_geom_heap[heap] = CreateHeap( mp_skater_geom_region[heap], Mem::Allocator::vBOTTOM_UP, "skaterGeomX" );
		printf ("EXTRA: Setup mp_skater_geom_heap at %p\n",mp_skater_geom_heap[heap]);
	}
	
	printf ("END Init Skater Heaps\n");
}

// Delete the temporary skater heaps
void Manager::DeleteSkaterHeaps()
{
	for (int i=NUM_PERM_SKATER_HEAPS;iPushContext( sp_instance->mp_bot_heap );		// make bottom-up heap default
    
//		sp_instance->InitOtherHeaps();							
							

	}
	else
	{
		Dbg_Warning( "Already Initialized!" );
	}
}

// K: Separated this out from sSetUp because this needs to be called from main(), after
// the config manager has initialised, because it must only be called if extra memory is
// available, and we only know that after Config::Init has been called.
// sSetUp is called from pre_main, and the config manager cannot be called from there 
// because it needs the command line args.
void Manager::sSetUpDebugHeap( void )
{
	if ( sp_instance )
	{
		sp_instance->InitDebugHeap();
	}
	else
	{
		Dbg_MsgAssert(0,("Called sSetUpDebugHeap when mem manager not initialized!"));
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::sCloseDown( void )
{
	

	if ( sp_instance )
	{
		
#ifndef __PLAT_WN32__
		sp_instance->DeleteOtherHeaps();							
#endif		
		sp_instance->PopContext();

		sp_instance->~Manager();
		sp_instance = NULL;
	}
	else
	{
		Dbg_Warning( "Not Initialized!" );
	}
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/


void Manager::RegisterPcsMemMan( Pcs::Manager* pReg ) 
{
	

	Dbg_Assert( mp_process_man == NULL );	// should not initialize twice.

	mp_process_man = pReg;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

CNamedHeapInfo* Manager::find_named_heap_info( uint32 name )
{
	for ( int i = 0; i < NUM_NAMED_HEAPS; i++ )
	{
		if ( m_named_heap_info[i].m_used && m_named_heap_info[i].m_name == name )
		{
			return &m_named_heap_info[i];
		}
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Heap* Manager::NamedHeap( uint32 name, bool assertOnFail )
{
	CNamedHeapInfo* pInfo = find_named_heap_info( name );

	if ( pInfo )
	{
		return pInfo->mp_heap;
	}
		
	Dbg_MsgAssert( !assertOnFail, ( "Couldn't find named heap %08x", name ) );
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::InitNamedHeap( uint32 name, uint32 size, const char* pHeapName )
{
	CNamedHeapInfo* pNamedHeapInfo = NULL;

	for ( int i = 0; i < NUM_NAMED_HEAPS; i++ )
	{
		if ( !m_named_heap_info[i].m_used )
		{
			pNamedHeapInfo = &m_named_heap_info[i];
			break;
		}
	}

	Dbg_MsgAssert( pNamedHeapInfo, ( "No more free named heaps (Increase NUM_NAMED_HEAPS from %d)!", NUM_NAMED_HEAPS ) );

	// set the correct name
	pNamedHeapInfo->m_name = name;
	Dbg_MsgAssert( strlen(pHeapName) < CNamedHeapInfo::vMAX_HEAP_NAME_LEN, ( "Heap name %s is too long", pHeapName ) );
	strcpy( pNamedHeapInfo->mp_heap_name, pHeapName );
	
	Dbg_MsgAssert(!pNamedHeapInfo->mp_region, ( "Named region is still active" ) );
	pNamedHeapInfo->mp_region =	new Mem::AllocRegion( size );	
	Dbg_Message( "EXTRA: Allocated pNamedHeapInfo->mp_region at %p", pNamedHeapInfo->mp_region );
	
	Dbg_MsgAssert(!pNamedHeapInfo->mp_heap, ("Named heap is still active"));
	pNamedHeapInfo->mp_heap = CreateHeap( pNamedHeapInfo->mp_region, 
										  Mem::Allocator::vBOTTOM_UP, 
										  pNamedHeapInfo->mp_heap_name );
	Dbg_Message( "EXTRA: Setup pNamedHeapInfo->mp_region at %p", pNamedHeapInfo->mp_heap );

	pNamedHeapInfo->m_used = true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Manager::DeleteNamedHeap( uint32 name, bool assertOnFail )
{
	CNamedHeapInfo* pInfo = find_named_heap_info( name );

	if ( pInfo )
	{
		Dbg_Message( "Deleting named heap %s", pInfo->mp_heap_name );
		Dbg_Assert( pInfo->mp_region );
		Dbg_Assert( pInfo->mp_heap );
		RemoveHeap( pInfo->mp_heap );
		delete pInfo->mp_region;
		pInfo->mp_region = NULL;
		pInfo->mp_heap = NULL;
		pInfo->m_name = 0;
		pInfo->m_used = false;
		return true;
	}
	else
	{
		Dbg_MsgAssert( !assertOnFail, ( "Couldn't find named heap %08x to delete", name ) );
	}
	
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void*	Malloc( size_t size )
{
	
	
	void *v =  Mem::Manager::sHandle().New( size, true );
	
	return v;
}

int		Available()
{
	return Mem::Manager::sHandle().Available(); 
}

void* ReallocateDown( size_t newSize, void *pOld )
{
	return Mem::Manager::sHandle().ReallocateDown(newSize,pOld);
}

void* ReallocateUp( size_t newSize, void *pOld )
{
	return Mem::Manager::sHandle().ReallocateUp(newSize,pOld);
}

void* ReallocateShrink( size_t newSize, void *pOld )
{
	return Mem::Manager::sHandle().ReallocateShrink(newSize,pOld);
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Free( void* pAddr )
{
	

	Mem::Manager::sHandle().Delete( pAddr );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Valid( void* pAddr )
{	
	return Mem::Manager::sHandle().Valid( pAddr );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

size_t	GetAllocSize( void* pAddr )
{
	return Mem::Manager::sHandle().GetAllocSize( pAddr );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void*	Realloc( void* mem, size_t newSize )
{
	/* should really add resize functions to do this more efficiently */

	void* ptr = NULL;

	if ( newSize )
	{   
//		Mem::Manager::sHandle().PushContext(Manager::sHandle().TopDownHeap());
		ptr = Mem::Manager::sHandle().New( newSize, true );
//		Mem::Manager::sHandle().PopContext();	
	}

	if ( mem )
	{
		if ( ptr )
		{
			memmove ( ptr, mem, newSize ); 
		}

		Mem::Manager::sHandle().Delete( mem );
	}
	
	return ptr;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void*	Calloc( size_t numObj, size_t sizeObj )
{
	
	
	return Mem::Manager::sHandle().New(( numObj * sizeObj ), true );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
// enable multiple threads to access the memory manager at once
void	SetThreadSafe(bool safe)
{
	#ifdef	__PLAT_NGPS__
		s_use_semaphore = safe;	
	#endif
}

bool	IsThreadSafe( void )
{
#ifdef	__PLAT_NGPS__
	return s_use_semaphore;
#else
	return false;
#endif
}

#ifdef __NOPT_ASSERT__

#define	MAX_PROFILE_NAME 64
#define	MAX_NUM_PROFILES  5000
#define	MAX_LEVELS	  32

class	CMemProfile
{		  
public:
	char	m_type[MAX_PROFILE_NAME];
	int		m_blocks;
	int		m_size;
	int		m_depth;
	int		m_count;		// number of pushes of same nam, same depth context
	int		m_link;			// link forward to profile that has merged with this profile
};


//static		CMemProfile				s_profile_list[MAX_NUM_PROFILES];		// a flat array of all profiles
static		CMemProfile			*	s_profile_list;		// a dynamically allocated flat array of all profiles
	
static		CMemProfile	*			s_profile_stack[MAX_LEVELS];		// stack of pushed profiles 
	
static		int						s_profile_count = 0;
	
static		CMemProfile*			sp_current_profile = NULL;							// current entry in profile list (will need to pop back....
static		int						s_profile_stack_depth = 0;						// depth in stack (index into m_profile_stack[])
static		int						s_next_profile = 0;								// index into array

//static 		int						s_launched = false;		// true after we've laucnhed the level once

static		bool					s_active = true;

void	PushMemProfile(char *p_type)
{
	if( s_active )
	{
	
		if (!s_profile_list)
		{
			if (Config::GotExtraMemory())
			{
				Mem::Manager::sHandle().PushContext(Manager::sHandle().DebugHeap());
				s_profile_list = (CMemProfile *)Mem::Malloc(MAX_NUM_PROFILES *sizeof(CMemProfile));
				Mem::Manager::sHandle().PopContext();
				
			}
			else
			{
				s_active = false;			// no extra memory, or maybe it's an X-Box....
				return;
			}
		}
		
		if (s_next_profile >= MAX_NUM_PROFILES)
		{
			s_active = false;
			return;
		}

/*		
		if (!strcmp("LaunchLevel",p_type))
		{
			if (s_launched)
			{
				s_active = false;
				return;
			}
			s_launched = true;
		}
*/
							  
		// if we have a current profile, then push it
		if (sp_current_profile)
		{
			Dbg_MsgAssert(s_profile_stack_depth < MAX_LEVELS,("mem profile stack overflow, unmatched push?"));
			s_profile_stack[s_profile_stack_depth++] = sp_current_profile;
		}
		
		// get a new profile from the list
		Dbg_MsgAssert(s_next_profile < MAX_NUM_PROFILES,("mem profile heap overflow, too many pushes"));
		sp_current_profile = &s_profile_list[s_next_profile++];




		// just copy over the memory containing the name												  
		char *p = &(sp_current_profile->m_type[0]);
		char *q = (char*) p_type;
		for (int i=0;im_blocks = 0;
		sp_current_profile->m_size = 0;
		sp_current_profile->m_depth = s_profile_stack_depth;
		sp_current_profile->m_count = 1;
		sp_current_profile->m_link = 0;

		// Then, search back through the list to see if there 
		// are any entries att he same level that have this same string
		// if so, then zero the old instance, and add the size and count to this one
		// there should only be one, as any other one would already be zeroed
		int	check = s_next_profile-2;
		while (check>0
				&& s_profile_list[check].m_depth == s_profile_stack_depth)
		{
			if (s_profile_list[check].m_depth == s_profile_stack_depth)
			{
				// same depth
				if (strcmp(s_profile_list[check].m_type,p_type) == 0)
				{
					// same string, so bring this one forward to add to this
					sp_current_profile->m_blocks += s_profile_list[check].m_blocks;
					sp_current_profile->m_size += s_profile_list[check].m_size;
					sp_current_profile->m_count += s_profile_list[check].m_count;
					// and zero out the old one					
					s_profile_list[check].m_blocks = 0;
					s_profile_list[check].m_size = 0;
					// Patch in the index of the new profile,
					// so late deletions can be accounted for
					s_profile_list[check].m_link = s_profile_stack_depth - 1;
					break;
				}
			}
			check--;
		}


	}
}


/******************************************************************/

void	PopMemProfile()
{
	if( s_active)
	{
		Dbg_MsgAssert(sp_current_profile,("Popped one more memory profile than we pushed"));

		// set time on the current profile		
//		sp_current_profile->m_end_time = Tmr::GetTimeInUSeconds();
		
		if ( ! s_profile_stack_depth)
		{
			// nothing left on stack, so set current profile to NULL
			sp_current_profile = NULL;
			// update the count of valid profiles
			s_profile_count = s_next_profile;
		}
		else
		{
			// get the last profile pushed on the stack
			sp_current_profile = s_profile_stack[--s_profile_stack_depth]; 
		}
	}
}


// given an index in the the mem profile list
// then get the total size of all allocations
// at this level, or below
// up the next entry at the same depth, or the end of the list
int		total_size(int n)
{
	
	int size = s_profile_list[n].m_size;
	int depth = s_profile_list[n].m_depth;
	n++;
	while (n < s_next_profile && s_profile_list[n].m_depth > depth)
	{
		size += s_profile_list[n].m_size;
		n++;
	}
	return size;
					
}

#ifndef __PLAT_WN32__
// dump the memory profile in a tree format, like
//
//  level stuff     100000
//     peds           5000
//     cars          23000
//    other          72000
//  skater stuff    ...... 
void	DumpMemProfile(int level, char *p_type)
{
	char buf[512];
	if( s_active )
	{
	


		printf ("\nDumping Memory Profile\n");
		printf ("There are %d mem profile contexts\n",s_next_profile);
		for (int i=0;i%10s____",Str::PrintThousands(total_size(i)));
					dump_printf(buf);
					if (s_profile_list[i].m_count == 1)
					{
						sprintf (buf,"%s\n",s_profile_list[i].m_type);
					}
					else
					{
						sprintf (buf,"%s (%d) (avg: %s)\n",s_profile_list[i].m_type,s_profile_list[i].m_count,Str::PrintThousands(total_size(i)/s_profile_list[i].m_count));
					}
					dump_printf(buf);
					if (s_profile_list[i].m_depth < level && s_profile_list[i].m_size && total_size(i) != s_profile_list[i].m_size)
					{
						sprintf (buf,"%2d: ",s_profile_list[i].m_depth+1);
						dump_printf(buf);
						for (int tab2 = 0;tab2 < s_profile_list[i].m_depth+1;tab2++)
						{
							sprintf(buf,"   ");
							dump_printf(buf);
						}
						sprintf (buf,">%10s____",Str::PrintThousands(s_profile_list[i].m_size));
						dump_printf(buf);
						sprintf (buf,"Untracked %s\n",s_profile_list[i].m_type);			
						dump_printf(buf);
						
					}
				}
			}
		}
	}
	else
	{
		printf ("Mem Profiler not active, probably overflowed, try restarting...\n");
	}
}
#endif// __PLAT_WN32__
void	AllocMemProfile(Allocator::BlockHeader* p_block)
{
	if( s_active )
	{
		if (sp_current_profile)
		{
			sp_current_profile->m_blocks++;
			
			// If it's on the debug heap, then set size to zero to avoid confusion
			if (Mem::Manager::sHandle().GetContextAllocator() != Mem::Manager::sHandle().DebugHeap())
			{
				sp_current_profile->m_size += p_block->mSize;
			}
			
			p_block->mp_profile = sp_current_profile;
		}
	}
}

void	FreeMemProfile(Allocator::BlockHeader* p_block)
{
	if( s_active )
	{
		
		CMemProfile	*	p_profile  = p_block->mp_profile;						  
		if (p_profile)
		{
//			Dbg_MsgAssert(p_profile->m_blocks,("mutli-block freed out of context"));
			// Skip over any that have been combined, until we find the final combined block
			while (p_profile->m_link != 0)
			{
				p_profile = &s_profile_list[p_profile->m_link];
			}
			p_profile->m_blocks--;
			p_profile->m_size -= p_block->mSize;
			p_profile = NULL;
		}
	}
}

#else

void	PushMemProfile(char *p_type)
{
}

void	PopMemProfile()
{
}

#ifndef __PLAT_WN32__
void	DumpMemProfile(int level, char *p_type)
{
}
#endif// __PLAT_WN32__

void	AllocMemProfile(Allocator::BlockHeader* p_block)
{
}

void	FreeMemProfile(Allocator::BlockHeader* p_block)
{
}

#endif __NOPT_ASSERT__


} // namespace Mem



================================================
FILE: Code/Sys/Mem/memman.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Manager (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		core/sys/mem/memman.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_MEM_MEMMAN_H
#define	__SYS_MEM_MEMMAN_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 
#include 
#include "heap.h"
#include "alloc.h"
//#ifndef __PLAT_XBOX__

#ifndef	__SYS_MEM_MEMPTR_H
#	include "memptr.h"
#endif
//#endif
#include "handle.h"

#if 0
#ifdef __PLAT_WN32__
#include "mem/wn32/p_memman.h"
#else
#ifdef __PLAT_NGPS__
#include "mem/ngps/p_memman.h"
#else
#ifdef __PLAT_NGC__
#include "mem/ngc/p_memman.h"
#else
#error Unsupported Platform
#endif
#endif
#endif
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

// redefine the standard memory library functions, so they give us errors


#define	NUM_PERM_SKATER_HEAPS	1
#define	NUM_SKATER_HEAPS		8
#define NUM_NAMED_HEAPS			4

#define		DEBUG_HEAP_SIZE				32767*1024		// 1K short of 32MB

		
namespace Pcs
{
	class Manager;
}

namespace Mem
{

/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

class CNamedHeapInfo
{
public:
	enum
	{
		vMAX_HEAP_NAME_LEN = 128
	};

	CNamedHeapInfo()
	{
		mp_region = NULL;
		mp_heap = NULL;
		m_name = 0;
		m_used = false;
	}

public:
	Region*		mp_region;
	Heap*		mp_heap;
	uint32		m_name;
	char		mp_heap_name[vMAX_HEAP_NAME_LEN];
	bool		m_used;
};

class Manager : public Spt::Class			
{
	
public:

	enum
	{
		vMAX_CONTEXT = 16,
		vMAX_HEAPS = 32
	};

	void						PushContext( Allocator* alloc );
	void						PopContext( void );

	void*						New( size_t size, bool assert_on_fail = true, Allocator* pAlloc = NULL );
	int							Available();
	void*						ReallocateDown( size_t newSize, void *pOld, Allocator* pAlloc = NULL );
	void*						ReallocateUp( size_t newSize, void *pOld, Allocator* pAlloc = NULL );
	void*						ReallocateShrink( size_t newSize, void *pOld, Allocator* pAlloc = NULL );
	void						Delete( void* pAddr );
	bool						Valid( void* pAddr );
	size_t						GetAllocSize( void* pAddr );
								
	Heap*						TopDownHeap( void ) 		{ return mp_top_heap; }
	Heap*						BottomUpHeap( void )  		{ return mp_bot_heap; }
	Heap*						FrontEndHeap( void ) 		{ return mp_frontend_heap; }
	Heap*						ScriptHeap( void ) 			{ return mp_script_heap; }
	Heap*						NetworkHeap( void ) 		{ return mp_network_heap; }
	Heap*						NetMiscHeap( void ) 		{ return mp_net_misc_heap; }
	Heap*						ProfilerHeap( void ) 		{ return mp_profiler_heap; }
	Heap*						DebugHeap( void ) 			{ return mp_debug_heap; }
	Heap*						SkaterHeap( int n ) 		{ return mp_skater_heap[n]; }
	Heap*						SkaterInfoHeap( ) 			{ return mp_skater_info_heap; }
	Heap*						SkaterGeomHeap( int n ) 	{ return mp_skater_geom_heap[n]; }
	Heap*						InternetTopDownHeap( void )	{ return mp_internet_top_heap; }
	Heap*						InternetBottomUpHeap( void ){ return mp_internet_bottom_heap; }
    Heap*						ThemeHeap( void )           { return mp_theme_heap; }
	Heap*						CutsceneTopDownHeap( void )	{ return mp_cutscene_top_heap; }
	Heap*						CutsceneBottomUpHeap( void ){ return mp_cutscene_bottom_heap; }
#ifdef __PLAT_NGC__
	Heap*						AudioHeap( void )			{ return mp_audio_heap; }
#endif	
	Heap*						NamedHeap( uint32 name, bool assertOnFail = true );

	void						RegisterPcsMemMan( Pcs::Manager* );

	/* Global versions for all memory allocations */
	void						PushMemoryMarker( uint32 uiID );
	void						PopMemoryMarker( uint32 uiID );

	static void					sSetUp( void );
	static void					sSetUpDebugHeap( void );
	
	static void					sCloseDown( void );
	static Manager&				sHandle( void );

	
	void 						InitOtherHeaps();
	void 						DeleteOtherHeaps();

	void						InitInternetHeap();
	void						DeleteInternetHeap();

	void						InitNetMiscHeap();
	void						DeleteNetMiscHeap();

	void						InitCutsceneHeap(int heap_size);
	void						DeleteCutsceneHeap();

	void						InitDebugHeap();

	void 						InitSkaterHeaps(int players);
	void 						DeleteSkaterHeaps();

	void						InitNamedHeap(uint32 name, uint32 size, const char* pHeapName);
	bool						DeleteNamedHeap(uint32 name, bool assertOnFail = true);
	
	const char*					GetHeapName( uint32 whichHeap );
	Heap *						GetHeap( uint32 whichHeap );

	Heap *						CreateHeap( Region* region, Mem::Allocator::Direction dir /*= Mem::Allocator::vBOTTOM_UP*/, char* p_name );//= "unknown heap" );
	void						RemoveHeap( Heap * pHeap); 
	Heap *						FirstHeap();
	Heap *						NextHeap(Heap * pHeap);
	

//	int 						GetContextNumber();
	char *						GetContextName();
	Allocator::Direction		GetContextDirection();
	Allocator*					GetContextAllocator();


								~Manager( void );
private :
								Manager( void );								   
																 
	Mem::Heap *					m_heap_list[vMAX_HEAPS]; 
	int							m_num_heaps;   							

	static char 				s_manager_buffer[];
	static char 				s_region_buffer[];
	static char 				s_debug_region_buffer[];
	static char 				s_script_region_buffer[];
	static char 				s_profiler_region_buffer[];
	static char 				s_debug_heap_buffer[];
	static char 				s_top_heap_buffer[];
	static char 				s_bot_heap_buffer[];
	static bool					s_initialized;
	static Manager*				sp_instance;
		
	class MemManContext
	{
	
		friend class Manager;
	
	private:
		Allocator*						mp_alloc;
	};


	Ptr< MemManContext > 		mp_context;

	// Mick: Contexts are now statically allocated off this 
	// array, rather than off the heap, as that was causing fragmentation
	// in rare but crash-worthy circumstances			
	MemManContext				m_contexts[vMAX_CONTEXT];
	int							m_pushed_context_count;


	Region*						mp_region;
	Heap*						mp_top_heap;
	Heap*						mp_bot_heap;
	
	Region*						mp_frontend_region;
	Heap*						mp_frontend_heap;

	Region*						mp_script_region;
	Heap*						mp_script_heap;

	Region*						mp_network_region;
	Heap*						mp_network_heap;

	Region*						mp_net_misc_region;
	Heap*						mp_net_misc_heap;

	Region*						mp_internet_region;
	Heap*						mp_internet_top_heap;
	Heap*						mp_internet_bottom_heap;

	Region*						mp_cutscene_region;
	Heap*						mp_cutscene_top_heap;
	Heap*						mp_cutscene_bottom_heap;

#ifdef __PLAT_NGC__
	Region*						mp_audio_region;
	Heap*						mp_audio_heap;
#endif		// __PLAT_NGC__

	Region*						mp_debug_region;
	Heap*						mp_debug_heap;

	Region*						mp_profiler_region;
	Heap*						mp_profiler_heap;

	Region*						mp_skater_region[NUM_SKATER_HEAPS];
	Heap*						mp_skater_heap[NUM_SKATER_HEAPS];
	
	Region*						mp_skater_geom_region[NUM_SKATER_HEAPS];
	Heap*						mp_skater_geom_heap[NUM_SKATER_HEAPS];

	CNamedHeapInfo				m_named_heap_info[NUM_NAMED_HEAPS];

	Region*						mp_skater_info_region;
	Heap*						mp_skater_info_heap;

    Region*						mp_theme_region;
	Heap*						mp_theme_heap;

	Pcs::Manager*				mp_process_man;
	uint						m_current_id;

protected:
	CNamedHeapInfo*				find_named_heap_info( uint32 name );
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

extern "C"
{

int		Available();
void*	Malloc( size_t size );
void	Free( void* mem );
bool	Valid( void* pAddr );
size_t	GetAllocSize( void* pAddr );
void*	ReallocateDown( size_t newSize, void *pOld );
void*	ReallocateUp( size_t newSize, void *pOld );
void*	ReallocateShrink( size_t newSize, void *pOld );
void*	Realloc( void* mem, size_t newSize );
void*	Calloc( size_t numObj, size_t sizeObj );
}

void	PushMemProfile(char *p_type);
void	PopMemProfile();
void	DumpMemProfile(int level, char *p_type=NULL);
void	AllocMemProfile(Allocator::BlockHeader* p_block);
void	FreeMemProfile(Allocator::BlockHeader* p_block);

void	SetThreadSafe(bool safe);
bool	IsThreadSafe( void );




/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline Manager&	Manager::sHandle( void )
{
	

	Dbg_AssertType( sp_instance, Manager );

	return *sp_instance;
}


} // namespace Mem


// Some debug functions
bool dump_open(const char *name);
void dump_printf(char *p);
void dump_close();
extern int dumping_printfs;



#endif  // __SYS_MEM_MEMMAN_H


================================================
FILE: Code/Sys/Mem/memptr.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Manager (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		core/sys/mem/memptr.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_MEM_MEMPTR_H
#define	__SYS_MEM_MEMPTR_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#include 

#include 

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mem
{

/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

nTemplateBaseClass( _T, PtrToConst )
{
	
public :

								PtrToConst( const _T* ptr = NULL );
								~PtrToConst( void );

#if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ ))

								PtrToConst( const PtrToConst< _T >& rhs );
		PtrToConst< _T >&		operator= ( const PtrToConst< _T >& rhs );

#else

	template < class _NewT >						                    		// template copy contructor
								PtrToConst( const PtrToConst< _NewT >& rhs ); 	// needed to support inheritance correctly

	template < class _NewT > 	
		PtrToConst< _T >&		operator = ( const PtrToConst< _NewT >& rhs );	// template assignment operator

#endif		
		PtrToConst< _T >&		operator = ( const _T* ptr );

		PtrToConst< _T >&		operator++ ( void ); 							// ++ptr   
		const PtrToConst< _T >	operator++ ( int ); 							// ptr++   
		PtrToConst< _T >&		operator-- ( void );							// --ptr
		const PtrToConst< _T >	operator-- ( int );								// ptr--
		PtrToConst< _T >&		operator+= ( int val );
		PtrToConst< _T >&		operator-= ( int val );

		bool					operator ! () const;                    		// operator! - use to test for null
		
		const _T&				operator * () const;
		const _T*				operator -> () const;																	
		const _T*				Addr( void ) const;								// Retrieve 'dumb' pointer
		
protected :

		union
		{
			const	_T*		m_const_ptr;
					_T*		m_ptr;
		};
	
};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

nTemplateSubClass( _T, Ptr, PtrToConst< _T > )
{

	
public :
								Ptr( const _T* ptr = NULL );
								~Ptr( void );

#if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ ))

								Ptr( const Ptr< _T >& rhs );
		Ptr< _T >&				operator= ( const Ptr< _T >& rhs );

#else
	template < class _NewT >										 
								Ptr( const Ptr< _NewT >& rhs );	

	template < class _NewT > 	
		Ptr< _T >&				operator = ( const Ptr< _NewT >& rhs );			// template assignment operator
#endif		
		Ptr< _T >&				operator = ( const _T* ptr );
		_T&						operator * ( void ) const; 
		_T*						operator -> ( void ) const;
		_T*						Addr( void ) const;

};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/* PtrToConst< _T >                                               */
/*                                                                */
/******************************************************************/

template < class _T > inline   
PtrToConst< _T >::PtrToConst< _T >( const _T* ptr ) 
: m_const_ptr ( ptr )
{
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline	
PtrToConst< _T >::~PtrToConst( void )
{
	

}

#if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline	
PtrToConst< _T >::PtrToConst( const PtrToConst< _T >& rhs )
: m_const_ptr ( rhs.Addr() )
{
	

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline	
PtrToConst< _T >&		PtrToConst< _T >::operator= ( const PtrToConst< _T >& rhs )
{
	
	
	m_const_ptr = rhs.Addr();
	return *this;	
}

#else

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
template < class _T > template < class _NewT > inline
PtrToConst< _T >::PtrToConst< _T >( const PtrToConst< _NewT >& rhs )
: m_const_ptr ( rhs.Addr() )
{
	

	Dbg_MsgAssert( false, ( "Microsoft VC++ sucks - don't do this (yet)" ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > template < class _NewT > inline
PtrToConst< _T >&		PtrToConst< _T >::operator = ( const PtrToConst< _NewT >& rhs ) 
{
	

	Dbg_MsgAssert( false, ( "Microsoft VC++ sucks - don't do this (yet)" ));

	m_const_ptr = rhs.Addr();
	return *this;	
}

#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
PtrToConst< _T >&		PtrToConst< _T >::operator = ( const _T* ptr ) 
{
	

	m_const_ptr = ptr;
	return *this;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
const _T&		PtrToConst< _T >::operator * ( void ) const 
{
	
	
	Dbg_AssertType( m_const_ptr, _T );
	
	return *m_const_ptr;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
PtrToConst< _T >&	PtrToConst< _T >::operator+= ( int val )
{
	

	m_const_ptr += val;
	Dbg_AssertType( m_const_ptr, _T );

	return *this;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
PtrToConst< _T >&	PtrToConst< _T >::operator-= ( int val )
{
	

	m_const_ptr -= val;

	Dbg_AssertType( m_const_ptr, _T );
	
	return *this;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
PtrToConst< _T >&	PtrToConst< _T >::operator++ ( void )
{
	

	*this += 1;

	return *this;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
const PtrToConst< _T >	PtrToConst< _T >::operator++ ( int )
{
	
	
	PtrToConst< _T > old = *this;

	++(*this);

	return old;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
PtrToConst< _T >&	PtrToConst< _T >::operator-- ( void )
{
	

	Dbg_AssertType( m_const_ptr, _T );

	*this -= 1;

	return *this;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
const PtrToConst< _T >	PtrToConst< _T >::operator-- ( int )
{
	

	Dbg_AssertType( m_const_ptr, _T );
	
	PtrToConst< _T > old = *this;

	--(*this);

	return old;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
const _T*		PtrToConst< _T >::operator -> ( void ) const 
{
	

	Dbg_AssertType( m_const_ptr, _T );
	
	return m_const_ptr;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
bool		PtrToConst< _T >::operator ! ( void ) const 
{
	

	return ( m_const_ptr == NULL );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
const _T*		PtrToConst< _T >::Addr ( void ) const 
{
	

	return m_const_ptr;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
const PtrToConst< _T > operator+ ( const PtrToConst< _T >& lhs, int rhs )
{
	
	
	PtrToConst< _T >	ret = lhs;
	ret += rhs;
	
	return ret;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
const PtrToConst< _T > operator- ( const PtrToConst< _T >& lhs, int rhs )
{
	
	
	PtrToConst< _T >	ret = lhs;
	ret -= rhs;
	
	return ret;
}




/******************************************************************/
/*                                                                */
/* Ptr< _T >                                                      */
/*                                                                */
/******************************************************************/

template < class _T > inline   
Ptr< _T >::Ptr( const _T* ptr )
: PtrToConst< _T >( ptr )
{
	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline	
Ptr< _T >::~Ptr( void )
{
	
}

#if ( defined ( __PLAT_XBOX__ ) || defined ( __PLAT_WN32__ ))

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline	
Ptr< _T >::Ptr( const Ptr< _T >& rhs )
: PtrToConst< _T >( rhs )
{
	

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline	
Ptr< _T >&		Ptr< _T >::operator= ( const Ptr< _T >& rhs )
{
	
	
	m_const_ptr = rhs.Addr();
	return *this;	
}

#else

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
template < class _T > template < class _NewT > inline
Ptr< _T >::Ptr( const Ptr< _NewT >& rhs )	
: PtrToConst< _T >( rhs )
{
	

	Dbg_MsgAssert( false, ( "Microsoft VC++ sucks - don't do this (yet)" ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > template < class _NewT > inline
Ptr< _T >&		Ptr< _T >::operator= ( const Ptr< _NewT >& rhs ) 
{
	

	Dbg_MsgAssert( false, ( "Microsoft VC++ sucks - don't do this (yet)" ));

	m_const_ptr = rhs.Addr();
	return *this;	
}

#endif

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
Ptr< _T >&		Ptr< _T >::operator= ( const _T* ptr ) 
{
	

	m_const_ptr = ptr;
	return *this;	
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
_T&		Ptr< _T >::operator * ( void ) const 
{
	

	Dbg_AssertType( m_ptr, _T );

	return *m_ptr;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
_T*		Ptr< _T >::operator -> ( void ) const 
{
	
	
	Dbg_AssertType( m_ptr, _T );
	
	return m_ptr;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

template < class _T > inline
_T*		Ptr< _T >::Addr ( void ) const 
{
	

	return m_ptr;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mem

#endif  // __SYS_MEM_MEMPTR_H								


================================================
FILE: Code/Sys/Mem/memtest.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		System Library											**
**																			**
**	Module:			Memory (Mem) 											**
**																			**
**	File name:		memtest.cpp												**
**																			**
**	Created by:		03/20/00	-	mjb										**
**																			**
**	Description:	Memory test code										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include "heap.h"
#include "pool.h"
#include "pile.h"

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/


namespace Mem
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

class  TestA  : public Spt::Class
{
	

public :
			TestA ( uint val = 999 ) : value (val ) { }
	uint	GetValue( void ) const  { return value; }

protected :

	uint	value;

};

class  TestB : public  TestA 
{
	

public :
			TestB ( uint val = 666 ) { value = val; }

};

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

void Test( void )
{
	

	Dbg_Message( "Testing Memory sub-system" );

	Dbg_Message ( "Allocate 1M region from Mem::Manager's default BottomUp Heap" );
	
	Mem::AllocRegion*	region = new Mem::AllocRegion( 1024 * 1024 );
	
	Dbg_Message ( "Create Bottom-Up Heap and Top-Down Pile within region" );

	Mem::Heap*			heap = new Mem::Heap( region, Mem::Allocator::vBOTTOM_UP );
	Mem::Pile*			pile = new Mem::Pile( region, Mem::Allocator::vTOP_DOWN );

	Dbg_Message ( "Set current allocator to Mem::Manager's default TopDown Heap" );

	Manager::sHandle().PushContext( Manager::sHandle().TopDownHeap() );

	Dbg_Message ( "Allocate another 1M region" );

	Mem::AllocRegion*	region2 = new Mem::AllocRegion( 1024 * 1024 );
	Dbg_Message ( "Create Bottom-Up Pool (16x1K slots) and Top-Down Heap within region" );

	Mem::Pool*			pool = new Mem::Pool( region2, 1024, 16 );
	Mem::Heap*			heap2 = new Mem::Heap( region2, Mem::Allocator::vTOP_DOWN );
	
	Manager::sHandle().PushContext( pile );

//	char*	test1( new char[100] );
//	Mem::Ptr	test1( new char[100] );
	
	Mem::Ptr< TestB >	testB = new TestB[10];
						

	Dbg_Message ( "Test B = %p, value = %d", testB.Addr(), testB->GetValue() );
#if 0
	Mem::Manager::sInstance().Delete(testB.Addr());
	uint	a = testB->GetValue();
#endif


	
//	Mem::Ptr< TestA >	ctestA = testB;
//	Mem::PtrToConst< TestA >	ctestA = testB;
	




//	Mem::Ptr< TestA >	testA( testB );

//	Mem::Ptr< TestA >	testA;
//	testA = testB;

#if 0 // poor little XBOX - can't handleit!
	Mem::Ptr< TestA >	testA = testB;
	testA+=9;	 // 10 doesn't assert properly - why???
#endif

//	Mem::PtrToConst< TestA >	ctestA( testB );


//	Mem::PtrToConst< TestA >	ctestA;
//	ctestA = testB;
	
//	Mem::PtrToConst< TestA >	ctestA = testB;	 // gives warning
	
//	Mem::PtrToConst< TestB >	ctestB = testB;
//	Mem::PtrToConst< TestA >	ctestA = ctestB;	
	
	
//	ctestB = testB;
//	ctestB->GetValue();
//	TestB	ttestB = *ctestB++;
	
#if 0
	Mem::PtrToConst< TestB >	stestB1 = (ctestB + 1);

	Mem::PtrToConst< TestB >	stestB10 = (ctestB + 10);
#endif


//	Mem::Ptr< int >		intPtr = new int;
//	int		b = *intPtr;

	char*	test2[17];

	for( uint i = 0; i < 16; i++ )
	{
		test2[i] = new (pool) char[1024];
		Dbg_Message ( "%x  should be valid address", test2[i] );
	}

	test2[16] = new (pool, false) char[1024];
	Dbg_Message ( " %x  should be NULL address ( no free slots )", test2[16] ); 

	delete test2[0];
	delete test2[1];

	char* test3 = new char;
	
	Manager::sHandle().PushContext( heap );

	char* test4 = new char;

	Manager::sHandle().PushContext( heap2 );
	
	Manager::sHandle().PopContext();
	Manager::sHandle().PopContext();
	Manager::sHandle().PopContext();

	delete test3;
	delete test4;
//	delete test4;

	delete heap;
	delete pile;
	delete pool;
	delete heap2;


/*
	char*  tmp1 = new char;
	int*  tmp2 = new int;
	short*  tmp3 = new short;
	delete tmp1;
	delete tmp3;
	delete tmp2;
*/

	TestA*	testa = new TestA;
	Mem::Handle< TestA > test_handle = testa;

	TestA*	gooda = test_handle.GetPointer();
	Dbg_AssertType( testa, TestA );
	
	Dbg_MsgAssert(( gooda == testa ),( "Handles pointer not valid" ));

	if( gooda != testa )
	{
		printf("Error!!!!!   Handles pointer not valid\n" );
	}

	Dbg_AssertType( gooda, TestA );

	delete testa;
//	testa = new TestA;

	gooda = test_handle.GetPointer();	
	Dbg_MsgAssert(( gooda == NULL ),( "Handles pointer not NULL" ));

	if( gooda )
	{
		printf("Error!!!!!   Handles pointer not NULL\n" );
	}

	Dbg_MsgAssert(( false ),("DONE" ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mem



================================================
FILE: Code/Sys/Mem/memtest.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Manager (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		core/sys/mem/memtest.h									**
**																			**
*****************************************************************************/

#ifndef	__SYS_MEM_MEMTEST_H
#define	__SYS_MEM_MEMTEST_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mem
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

void Test( void );

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Mem

#endif  // __SYS_MEM_MEMTEST_H


================================================
FILE: Code/Sys/Mem/pile.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		System Library											**
**																			**
**	Module:			Memory (Mem) 											**
**																			**
**	File name:		pile.cpp												**
**																			**
**	Created by:		03/20/00	-	mjb										**
**																			**
**	Description:	Pile class												**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include "pile.h"
#include 

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/


namespace Mem
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/
	
/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

void*	Pile::allocate( size_t size, bool assert_on_fail )
{
	
		
#ifdef __PLAT_NGC__
	size = (uint)nAlignUpBy( size, 5 );
#else
	size = (uint)nAlignUpBy( size, 4 );
#endif
	
	BlockHeader* p_header = (BlockHeader*)mp_region->Allocate( this, size + BlockHeader::sSize, assert_on_fail );

	if ( p_header )
	{
		new ((void*)p_header) BlockHeader( this, size ); 

		Dbg_Code 
		(
			m_alloc_count++;
		)
	}
	else if ( assert_on_fail )
	{
		Dbg_MsgAssert ( false,( "Failed to allocate %d bytes", size ));
	}

	MemDbg_AllocateBlock ( p_header );	
	
	return (void*)((uint)p_header + BlockHeader::sSize);

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Pile::free( BlockHeader* pHeader )
{
	
	
	MemDbg_FreeBlock ( pHeader );	

	pHeader->~BlockHeader();

	Dbg_Code 
	(
		Dbg_MsgAssert(( m_alloc_count > 0 ),( "Internal Error - freed too many blocks!!" ));
		m_alloc_count--;
	)
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Pile::Pile( Region* region, Direction dir )
: Allocator( region, dir, "Pile" )
{
	
	
	mp_base = mp_top;

	Dbg_Code
	(
		m_depth = 0;
	)
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Pile::~Pile( void )
{
	

	Dbg_Code
	(
		Dbg_MsgAssert(( m_depth == 0 ),( "Context Stack not empty (%d items)", m_depth ));
	
		// warn if there are still allocations.
		Dbg_MsgAssert(( m_alloc_count == 0 ),( "Pile still contains allocations" ));
	)

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void 	Pile::PushContext( void )
{
	

	void** p_top = (void**)mp_top;
	*p_top = mp_base;
	mp_base = p_top;
	mp_top = p_top + m_dir;
	
	Dbg_Code
	(
		m_depth++;
	)
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Pile::PopContext( void )
{
	
		
	Dbg_Code
	(
		Dbg_MsgAssert( m_depth > 0,( "Context stack underflow" ));
		m_depth--;
	)

	mp_top = mp_base;
	mp_base = *(void**)mp_top;
	mp_top = (void*)( (void**)(mp_top) + m_dir );

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mem



================================================
FILE: Code/Sys/Mem/pile.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Manager (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		core/sys/mem/pile.h										**
**																			**
*****************************************************************************/

#ifndef	__SYS_MEM_PILE_H
#define	__SYS_MEM_PILE_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include "alloc.h"

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mem
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

class Region;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
class  Pile : public  Allocator 			
{
	
	
	friend class Manager;

public :
		
							Pile( Region* region, Direction dir = vBOTTOM_UP );
	virtual					~Pile( void );
	
	virtual		void		PushContext( void );
	virtual		void		PopContext( void );

private :
	
	virtual		void*		allocate( size_t size, bool assert_on_fail );
	virtual		void		free( BlockHeader* pHeader );
	
	void*					mp_base;
	
	Dbg_Code
	(
		sint				m_depth;
		sint				m_alloc_count;
	)
};


/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Mem

#endif  // __SYS_MEM_PILE_H


================================================
FILE: Code/Sys/Mem/pool.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		System Library											**
**																			**
**	Module:			Memory (Mem) 											**
**																			**
**	File name:		pool.cpp												**
**																			**
**	Created by:		03/29/00	-	mjb										**
**																			**
**	Description:	Memory Pool class										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include "memman.h"
#include "pool.h"
#include 

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/


namespace Mem
{



/*****************************************************************************
**								Private Types								**
*****************************************************************************/
	
/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

void Pool::dump_free_list( void )
{
	
	
	BlockHeader*	p_header = mp_free_list;

	while ( p_header )
	{
		Dbg_AssertType( p_header, BlockHeader );
		Dbg_Message ( "%p  %d   (%p)", (void*)((uint)p_header + sizeof( BlockHeader )), 
										p_header->mSize, p_header->mpNext );
		p_header = p_header->mpNext;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void*	Pool::allocate( size_t size, bool assert_on_fail )
{
	
		
	Dbg_MsgAssert ( size <= m_size,( "Failed to allocate: Size (%x) > Pool Element (%x)", size, m_size ));

	if ( mp_free_list )
	{
		Dbg_AssertType( mp_free_list, BlockHeader );
		
		BlockHeader* p_header = mp_free_list;
		
		mp_free_list = p_header->mpNext;
		p_header->mpAlloc = this;

		MemDbg_AllocateBlock ( p_header );	

		return (void*)((uint)p_header + BlockHeader::sSize);
	}

	if ( assert_on_fail )
	{
		Dbg_MsgAssert ( false,( "Failed to allocate %d bytes, no free slots", size ));
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Pool::free( BlockHeader* pFreeBlock )
{
	
	
	Dbg_AssertType( pFreeBlock, BlockHeader );

	MemDbg_FreeBlock ( pFreeBlock );	
	   
	pFreeBlock->mpNext = mp_free_list;
	mp_free_list = pFreeBlock;
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Pool::Pool( Region* region, size_t size, uint count, Direction dir )
: Allocator( region, dir, "Pool" ), m_count( count), m_size( size )
{
	

	if ( !nAlignedBy( m_size, 4 ))
	{
#ifdef __PLAT_NGC__
		Dbg_Warning ( "size %x must be 32 byte aligned ( setting to %x )", m_size, nAlignUpBy( (void*)m_size, 5 ));
		m_size = (size_t)nAlignUpBy( m_size, 5 );
#else
		Dbg_Warning ( "size %x must be 16 byte aligned ( setting to %x )", m_size, nAlignUpBy( (void*)m_size, 4 ));
		m_size = (size_t)nAlignUpBy( m_size, 4 );
#endif
	}

	BlockHeader* p_freeblock = (BlockHeader*)mp_region->Allocate( this, ( m_size + BlockHeader::sSize ) * m_count );
	
	Dbg_MsgAssert ( p_freeblock,( "Failed to allocate Memory Pool from Region" ));

	mp_free_list = p_freeblock;

	for ( uint i = 0; i < ( m_count - 1 ); i++ )
	{
		new ((void*)p_freeblock) BlockHeader( this, m_size );		
		
		BlockHeader* p_nextblock = (BlockHeader*)( (uint)p_freeblock + BlockHeader::sSize + m_size ); 
		p_freeblock->mpNext = p_nextblock;
		p_freeblock = p_nextblock;
	}

	new ((void*)p_freeblock) BlockHeader( this, m_size );		
	p_freeblock->mpNext = NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Pool::~Pool( void )
{
	

	Dbg_Code  // warn if there are still allocated slots.
	(
		uint free_list_count = 0;

		for ( BlockHeader* p_free_list = mp_free_list; p_free_list; p_free_list = p_free_list->mpNext )
		{
			free_list_count++;
		}

		Dbg_MsgAssert(( free_list_count == m_count ),( "Pool still contains allocated slots" ));
	)
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mem



================================================
FILE: Code/Sys/Mem/pool.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Manager (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		core/sys/mem/pool.h										**
**																			**
*****************************************************************************/

#ifndef	__SYS_MEM_POOL_H
#define	__SYS_MEM_POOL_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include "alloc.h"

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mem
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

class Region;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  Pool : public  Allocator 			
{
	
	
	friend class Manager;

public :
		
								Pool( Region* region, size_t size, uint count, Direction dir = vBOTTOM_UP );
	virtual						~Pool( void );
	
private :
	
	virtual		void*			allocate( size_t size, bool assert_on_fail );
	virtual		void			free( BlockHeader* pHeader );
				void			dump_free_list( void );

				BlockHeader*	mp_free_list;
				uint			m_count;
				size_t			m_size;
};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

} // namespace Mem

#endif  // __SYS_MEM_POOL_H


================================================
FILE: Code/Sys/Mem/region.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		System Library											**
**																			**
**	Module:			Memory (Mem) 											**
**																			**
**	File name:		region.cpp												**
**																			**
**	Created by:		03/20/00	-	mjb										**
**																			**
**	Description:	Region Class											**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include "alloc.h"

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/

namespace Mem
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/
	
/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

Region::~Region( void )
{
	

}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	Region::init( void* pStart, void* pEnd )
{
	

	Dbg_AssertPtr( pStart );

	mp_start	= pStart;
	mp_end		= pEnd;

	m_min_free  = (int)pEnd - (int)pStart;

	m_alloc[vBOTTOM_UP] = 
	m_alloc[vTOP_DOWN] 	= NULL;

	Dbg_Notify( "Region(%p) created %p -> %p ", this, mp_start, mp_end );

	if (!Config::CD())
	{
#ifdef __CLEAR_MEMORY__
		uint64* ptr = (uint64*)mp_start; 
		uint 	size = (uint)mp_end - (uint)mp_start;
	
		for ( uint i = 0; i < size; i += 8 )
		{
			*ptr++ = vFREE_BLOCK;
		}
#endif
	}
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

void		Region::RegisterAllocator( Allocator* pAlloc )
{
	

	int index = ( pAlloc->m_dir & 2 ) >> 1;

	Dbg_MsgAssert ( m_alloc[index] == NULL,( "Attempt to register multiple Allocators" ));

	m_alloc[index] = pAlloc;

	pAlloc->mp_top = ( pAlloc->m_dir == Allocator::vBOTTOM_UP ) ? mp_start : mp_end;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Region::UnregisterAllocator( Allocator* pAlloc )
{
	
	
	int index = ( pAlloc->m_dir & 2 ) >> 1;
	
	Dbg_MsgAssert ( m_alloc[index] == pAlloc,( "Allocator not currently registered" ));

	m_alloc[index] = NULL;
}
	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Givne an allocator pAlloc, then allocate some space from this region
// for this allocator
// account for the m_dir flag in the allocator, which will be -1 or +1

void*		Region::Allocate( Allocator* pAlloc, size_t size, bool assert_on_fail )
{
	
	
	Dbg_AssertType ( pAlloc, Allocator );

	// Get index = 0 if BottomUp
	//     index = 1 if TopDown
	int index = ( pAlloc->m_dir & 2 ) >> 1;
	Dbg_MsgAssert ( m_alloc[index] == pAlloc,( "Allocator not currently registered" ));

	// mp_top of an allocator is the "Top" of the heap relative to what direction it is going in
	// hence for a BottomUp heap, the mp_top will be the byte after the highest byte
	// and for a TopDown heap, the mp_top will be the lowest byte	
	
	// here we get a new p_top for the allocator in pAlloc
	// then we just need to test it
	void* p_top = (void*) (( (uint)pAlloc->mp_top ) + ( size * pAlloc->m_dir ) );
	
	if( Allocator* p_alloc = m_alloc[index^1] )	  // If other allocator exists
	{
		if ( index == vBOTTOM_UP )				  // we are bottom up, other allocater is top down
		{
			if ( p_top > p_alloc->mp_top )		 // our new top is above the other allocator's base
			{
				if( assert_on_fail )			 // so fail
				{
					Dbg_MsgAssert( false,( "Failed to Allocate %d bytes", size ));
				}
				Dbg_Warning( "Failed to Allocate %d bytes", size ); 
				return NULL;
			}
		}
		else	// we are top down, other allocator is bottom up
		{
			if ( p_top < p_alloc->mp_top )			// new top is below the other allocator's top
			{
				if( assert_on_fail )				// so fail
				{
					Dbg_MsgAssert( false,( "Failed to Allocate %d bytes", size ));
				}
				Dbg_Warning( "Failed to Allocate %d bytes", size ); 
				return NULL;
			}
		}
	}
	else
	{
		// here there is no other allocator
		if ( index == vBOTTOM_UP )				// for a single bottom up heap
		{
			if ( p_top > mp_end )  				// if new p_top goes past end of region
			{
				if( assert_on_fail )			// then fail
				{
					Dbg_MsgAssert( false,( "Failed to Allocate %d bytes", size ));
				}
				Dbg_Warning( "Failed to Allocate %d bytes", size ); 
				return NULL;
			}
		}
		else	// single top down	heap
		{
			if ( p_top < mp_start )		 // p_top goes below start of region
			{
				if( assert_on_fail )
				{
					Dbg_MsgAssert( false,( "Failed to Allocate %d bytes", size ));
				}
				Dbg_Warning( "Failed to Allocate %d bytes", size ); 
				return NULL;
			}
		}
	}

	// If it's vBOTTOM_UP then
	//    return pAlloc->mp_top, which is the old end of the heap, and the start of the new block
	// else
	// 	  return p_top, which is the base of the new block (when heap growing downwards)

	void* p_ret = ( index == vBOTTOM_UP ) ? pAlloc->mp_top : p_top;

	// store new "top" of heap
	pAlloc->mp_top = p_top;

	int free = MemAvailable();
	if (free < m_min_free)
	{
		m_min_free = free;
	}


	// and return the start of the new memory
	return p_ret;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Region::Region( void* pStart, void* pEnd )
{
	
		
	init( pStart, pEnd );
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

AllocRegion::AllocRegion( size_t size )
{
	

	size = (size_t) nAlignDown( size );
	
	mp_start = new char[size];
	
	init( mp_start, (void*)( (uint)mp_start + size ));
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

AllocRegion::~AllocRegion( void )
{
	

	delete[] (char *)mp_start;
}
					
/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Region::MemAvailable( void )
{
	
		
	int bot = (int)((m_alloc[0]) ? m_alloc[0]->mp_top : mp_start);
	int top = (int)((m_alloc[1]) ? m_alloc[1]->mp_top : mp_end);

	return ((int)top - (int)bot);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mem



================================================
FILE: Code/Sys/Mem/region.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment	                        **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		Sys Library												**
**																			**
**	Module:			Memory Manager (Mem)									**
**																			**
**	Created:		03/20/00	-	mjb										**
**																			**
**	File name:		/sys/mem/region.h   									**
**																			**
*****************************************************************************/

#ifndef	__SYS_MEM_REGION_H
#define	__SYS_MEM_REGION_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Mem
{



/*****************************************************************************
**							     Type Defines								**
*****************************************************************************/

class Allocator;

class  Region  : public Spt::Class			
{
	

	friend class Manager;

public :
		
	void*		StartAddr ( void ) const { return mp_start; }
	void*		EndAddr ( void ) const { return mp_end; }

	void		RegisterAllocator( Allocator* alloc );		
	void		UnregisterAllocator( Allocator* alloc );
	void*		Allocate( Allocator* pAlloc, size_t size, bool assert_on_fail = true );
	int			MemAvailable( void );
	int			MinMemAvailable( void ) {return m_min_free;}
	int			TotalSize( void );

	virtual		~Region();

protected :
				Region( void ){};
				Region( void* pStart, void* pEnd );

	void		init ( void* pStart, void* pEnd );
	void*		mp_start;


	int			m_min_free;		// minimum amount of free memory

private :
	
	enum
	{
		vBOTTOM_UP,
		vTOP_DOWN,
		vMAX_ALLOCS
	};

	void*		mp_end;

	Allocator*	m_alloc[vMAX_ALLOCS];

};

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  AllocRegion : public  Region 			
{
	

	friend class Manager;

public :
				AllocRegion( size_t size );
	virtual		~AllocRegion( void );

private :


};

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

inline int Region::TotalSize( void )
{
	
		
	return ((int)mp_end - (int)mp_start);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mem

#endif  // __SYS_MEM_REGION_H


================================================
FILE: Code/Sys/MemCard/NGPS/p_McMan.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS														**
**																			**
**	Module:			Mc			 											**
**																			**
**	File name:		memcard.cpp												**
**																			**
**	Created by:		03/06/01	-	spg										**
**																			**
**	Description:	Memcard - platform-specific implementations				**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 

#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/



namespace Mc
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "MemCard Manager" );

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

Manager::Manager( void )
{
	

	int result;
	int i, j;

	result = sceMcInit();
	Dbg_MsgAssert( result == 0,( "Failed to initialize memory card system\n" ));

	for( i = 0; i < vMAX_PORT; i++ )
	{
		for( j = 0; j < vMAX_SLOT; j++ )
		{
			m_card[i][j].m_port = i;
			m_card[i][j].m_slot = j;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

int		Manager::GetMaxSlots( int port )
{
	int result;

   	result = sceMcGetSlotMax( port );
	if( result >= 0 )
	{
		return result;
	}
	else
	{
		return 0;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Card*	Manager::GetCard( int port, int slot )
{
	
	int type;

	Dbg_Assert( port < vMAX_PORT );
	Dbg_Assert( slot < vMAX_SLOT );
	
	Card* card = &m_card[port][slot];

	type = card->GetDeviceType();
	if(	( type == Card::vDEV_PS1_CARD ) ||
		( type == Card::vDEV_PS2_CARD ))
	{
		return card;
	}
    
	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		wait_for_async_call_to_finish( Card* card )
{
	int ret_val;

	

	sceMcSync( 0, NULL, &ret_val );
	if( ret_val < 0 )
	{   
		switch( ret_val )
		{
			case -1:
				Dbg_Printf( "Switched to formated card\n" );
				card->SetError( Card::vERROR_FORMATTED );
				break;
			case -2:
				Dbg_Printf( "Error: Unformatted Card\n" );
				card->SetError( Card::vERROR_UNFORMATTED );
				break;
			case -3:
				Dbg_Printf( "Error: Insufficient Space\n" );
				card->SetError( Card::vINSUFFICIENT_SPACE );
				break;
			case -4:
				Dbg_Printf( "Error: Invalid Path\n" );
				card->SetError( Card::vINVALID_PATH );
				break;
			case -5:
				Dbg_Printf( "Error: Access Error\n" );
				card->SetError( Card::vACCESS_ERROR );
				break;
			case -6:
				Dbg_Printf( "Error: Subdir not empty\n" );
				card->SetError( Card::vSUBDIR_NOT_EMPTY );
				break;
			case -7:
				Dbg_Printf( "Error: Too many open files\n" );
				card->SetError( Card::vTOO_MANY_OPEN_FILES );
				break;
			case -10:
				Dbg_Printf( "Error: No card\n" );
				card->SetError( Card::vNO_CARD );
				break;
			default:
				card->SetError( Card::vUNKNOWN );
				break;
		}
	}

	return ret_val;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Card::MakeDirectory( const char* dir_name )
{
	int result;
	 
	

	result = sceMcMkdir( m_port, m_slot, dir_name );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return false;
	}
    
	int ErrorCode=wait_for_async_call_to_finish( this );
	if (ErrorCode==-4)
	{
		// If, the directory exists already, don't make it an error
		ErrorCode=0;
	}	
	
	return( ErrorCode == 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Card::DeleteDirectory( const char* dir_name )
{
	// On the PS2, the Delete() function used for files can also
	// be used to delete a directory.
	return Delete(dir_name);
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const char *Card::ConvertDirectory( const char* dir_name )
{
	return NULL;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool 	Card::ChangeDirectory( const char* dir_name )
{
	int result;

	result = sceMcChdir( m_port, m_slot, dir_name, NULL );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return false;
	}
    
	return( wait_for_async_call_to_finish( this ) == 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Card::Format( void )
{
	int result;

	result = sceMcFormat( m_port, m_slot );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return false;
	}
    
	return( wait_for_async_call_to_finish( this ) == 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Card::Unformat( void )
{
	int result;

	result = sceMcUnformat( m_port, m_slot );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return false;
	}
    
	return( wait_for_async_call_to_finish( this ) == 0 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Card::IsFormatted( void )
{
	int formatted;
	int result;

    result = sceMcGetInfo( m_port, m_slot, NULL, NULL, &formatted );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return false;
	}
    
	result = wait_for_async_call_to_finish( this );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return false;
	}

	return( formatted == 1 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Card::GetDeviceType( void )
{
	int type;
	int result;

    result = sceMcGetInfo( m_port, m_slot, &type, NULL, NULL );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return vDEV_NONE;
	}
    
	result = wait_for_async_call_to_finish( this );
	if(	result != 0 )
	{
		if(	( GetLastError() != vERROR_UNFORMATTED ) &&
			( GetLastError() != vERROR_FORMATTED ))
		{
			SetError( vUNKNOWN );
			return vDEV_NONE;
		}
	}

	switch( type )
	{
		case 1:
			return vDEV_PS1_CARD;
		case 2:
			return vDEV_PS2_CARD;
		case 3:
			return vDEV_POCKET_STATION;
		default:
			return vDEV_NONE;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Card::GetNumFreeClusters( void )
{
	int free_clusters;
	int result;

    result = sceMcGetInfo( m_port, m_slot, NULL, &free_clusters, NULL );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return 0;
	}
    
	result = wait_for_async_call_to_finish( this );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return 0;
	}

	return free_clusters;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		Card::GetNumFreeEntries( const char* path )
{
	int result;

    result = sceMcGetEntSpace( m_port, m_slot, path );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return 0;
	}
    
	return( wait_for_async_call_to_finish( this ));	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Card::Delete( const char* filename )
{
	int result;

	result = sceMcDelete( m_port, m_slot, filename );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return false;
	}
    
	int ErrorCode=wait_for_async_call_to_finish( this );
	if (ErrorCode==-4)
	{
		// The the error was that the file already did not exist, don't report this as an error.
		ErrorCode=0;
	}
		
	return( ErrorCode==0 );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	Card::Rename( const char* old_name, const char* new_name )
{
	int result;

	result = sceMcRename( m_port, m_slot, old_name, new_name );
	if( result != 0 )
	{
		SetError( vUNKNOWN );
		return false;
	}
    
	return( wait_for_async_call_to_finish( this ) == 0 );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

File*	Card::Open( const char* filename, int mode, int size )
{
	int result, fd;
	int open_mode;
	File* file;

	

	//Dbg_MsgAssert( strlen( filename ) < 31,( "Filename exceeds max length\n" ));

	file = NULL;
	open_mode = 0;
	if( mode & File::mMODE_READ )
	{
		open_mode |= SCE_RDONLY;
	}
	if( mode & File::mMODE_WRITE )
	{
		open_mode |= SCE_WRONLY;
	}
	if( mode & File::mMODE_CREATE )
	{
		open_mode |= SCE_CREAT;
	}
	result = sceMcOpen( m_port, m_slot, filename, open_mode );
	if( result != 0 )
	{
		Dbg_Printf( "Unknown Error\n" );
		SetError( vUNKNOWN );
		return NULL;
	}
    
	fd = wait_for_async_call_to_finish( this );
	if( fd >= 0 )
	{
		file = new File( fd, this );
	}
	return file;	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Ken: This used to be a local variable in Card::GetFileList, but sceMcGetDir requires that
// this buffer be 64 byte aligned, and occasionally it would not be, causing the contents of
// file_table to be undefined after the call to sceMcGetDir.
// Unfortunately, Sony did not make sceMcGetDir return an error code in this case, the blighters.
//
static sceMcTblGetDir file_table[Card::vMAX_FILES_PER_DIRECTORY];   	

bool	Card::GetFileList( const char* mask, Lst::Head< File > &file_list )
{
	
	int result, i;
		
	Dbg_MsgAssert( mask != NULL,( "Mask must be a valid search string. You may use wildcards\n" ));

	Dbg_MsgAssert( (((uint32)file_table)&63)==0, ("file_table not 64 byte aligned"));
	
	
	int CallNumber=0;
	while (true)
	{
		result = sceMcGetDir( m_port, m_slot, mask, CallNumber, vMAX_FILES_PER_DIRECTORY, file_table );
		if( result != 0 )
		{
			SetError( vUNKNOWN );
			return 0;
		}
		
		int NumFilesReturned = wait_for_async_call_to_finish( this );
		for( i = 0; i < NumFilesReturned; i++ )
		{
			File *new_file;

			new_file = new File( 0, this );

			strcpy( new_file->m_Filename, (char *) file_table[i].EntryName );
			new_file->m_Size = file_table[i].FileSizeByte;
			
			new_file->m_Attribs = 0;
			if( file_table[i].AttrFile & sceMcFileAttrReadable )
			{
				new_file->m_Attribs |= File::mATTRIB_READABLE;
			}
			if( file_table[i].AttrFile & sceMcFileAttrWriteable )
			{
				new_file->m_Attribs |= File::mATTRIB_WRITEABLE;
			}
			if( file_table[i].AttrFile & sceMcFileAttrExecutable )
			{
				new_file->m_Attribs |= File::mATTRIB_EXECUTABLE;
			}
			if( file_table[i].AttrFile & sceMcFileAttrDupProhibit )
			{
				new_file->m_Attribs |= File::mATTRIB_DUP_PROHIBITED;
			}
			if( file_table[i].AttrFile & sceMcFileAttrSubdir )
			{
				new_file->m_Attribs |= File::mATTRIB_DIRECTORY;
			}
			if( file_table[i].AttrFile & sceMcFileAttrClosed )
			{
				new_file->m_Attribs |= File::mATTRIB_CLOSED;
			}
			if( file_table[i].AttrFile & sceMcFileAttrPDAExec )
			{
				new_file->m_Attribs |= File::mATTRIB_PDA_APP;
			}
			if( file_table[i].AttrFile & sceMcFileAttrPS1 )
			{
				new_file->m_Attribs |= File::mATTRIB_PS1_FILE;
			}
			
			sceCdCLOCK myClock;
			myClock.day=itob(file_table[i]._Modify.Day);
			myClock.hour=itob(file_table[i]._Modify.Hour);
			myClock.minute=itob(file_table[i]._Modify.Min);
			myClock.month=itob(file_table[i]._Modify.Month);
			myClock.second=itob(file_table[i]._Modify.Sec);
			myClock.year=itob(file_table[i]._Modify.Year%100);
			sceScfGetLocalTimefromRTC(&myClock);
			new_file->m_Modified.m_Seconds 	= btoi(myClock.second);
			new_file->m_Modified.m_Minutes 	= btoi(myClock.minute);
			new_file->m_Modified.m_Hour 	= btoi(myClock.hour);
			new_file->m_Modified.m_Day 		= btoi(myClock.day);
			new_file->m_Modified.m_Month 	= btoi(myClock.month);
			new_file->m_Modified.m_Year 	= btoi(myClock.year)+2000;
			
			myClock.day=itob(file_table[i]._Create.Day);
			myClock.hour=itob(file_table[i]._Create.Hour);
			myClock.minute=itob(file_table[i]._Create.Min);
			myClock.month=itob(file_table[i]._Create.Month);
			myClock.second=itob(file_table[i]._Create.Sec);
			myClock.year=itob(file_table[i]._Create.Year%100);
			sceScfGetLocalTimefromRTC(&myClock);
			new_file->m_Created.m_Seconds 	= btoi(myClock.second);
			new_file->m_Created.m_Minutes 	= btoi(myClock.minute);
			new_file->m_Created.m_Hour 		= btoi(myClock.hour);  
			new_file->m_Created.m_Day 		= btoi(myClock.day);   
			new_file->m_Created.m_Month 	= btoi(myClock.month); 
			new_file->m_Created.m_Year 		= btoi(myClock.year)+2000;  

			file_list.AddToTail( new_file );
		}
		
		if (NumFilesReturned ( this ), m_fd( fd ), m_card( card )
{
}
		
File::~File()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool	File::Close( void )
{
	int result;

	result = sceMcClose( m_fd );
	if( result != 0 )
	{
		m_card->SetError( Card::vUNKNOWN );
		return 0;
	}
    
	return( wait_for_async_call_to_finish( m_card ) == 0 );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		File::Seek( int offset, FilePointerBase base )
{
	int result;
	int mode;

	

	switch( base )
	{
		case BASE_START:
			mode = 0;
			break;
		case BASE_CURRENT:
			mode = 1;
			break;
		case BASE_END:
			mode = 2;
			break;
		default:
			mode = 1;
			Dbg_MsgAssert( 0,( "Invalid FilePointerBase\n" ));
			break;
	}

	result = sceMcSeek( m_fd, offset, mode );
	if( result != 0 )
	{
		m_card->SetError( Card::vUNKNOWN );
		return 0;
	}
    
	return( wait_for_async_call_to_finish( m_card ));	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool 	File::Flush( void )
{
	int result;

	result = sceMcFlush( m_fd );
	if( result != 0 )
	{
		m_card->SetError( Card::vUNKNOWN );
		return false;
	}
    
	return( wait_for_async_call_to_finish( m_card ) == 0 );	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		File::Write( void* data, int len )
{
	int result;

	result = sceMcWrite( m_fd, data, len );
	if( result != 0 )
	{
		m_card->SetError( Card::vUNKNOWN );
		return 0;
	}
    
	return( wait_for_async_call_to_finish( m_card ));	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int		File::Read( void* buff, int len )
{
	int result;

	

	result = sceMcRead( m_fd, buff, len );
	if( result != 0 )
	{
		Dbg_Printf( "Error: Could not read data\n" );
		m_card->SetError( Card::vUNKNOWN );
		return 0;
	}
    
	return( wait_for_async_call_to_finish( m_card ));	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mc






================================================
FILE: Code/Sys/MemCard/XBox/p_McMan.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS														**
**																			**
**	Module:			Mc			 											**
**																			**
**	File name:		memcard.cpp												**
**																			**
**	Created by:		03/06/01	-	spg										**
**																			**
**	Description:	Memcard - platform-specific implementations				**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Mc
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define MAX_FILENAME_LENGTH	128

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "MemCard Manager" );

static char cardFilenameBuffer[MAX_FILENAME_LENGTH];

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

Manager::Manager( void )
{
	int i, j;

	for( i = 0; i < vMAX_PORT; i++ )
	{
		for( j = 0; j < vMAX_SLOT; j++ )
		{
			m_card[i][j].m_port = i;
			m_card[i][j].m_slot = j;
		}
	}
	
	m_hard_drive.SetAsHardDrive();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

int	Manager::GetMaxSlots( int port )
{
	return vMAX_SLOT;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Card* Manager::GetCard( int port, int slot )
{
	// Ignore port and slot, since on the XBox we're only using the hard drive.
	return &m_hard_drive;
}

// Skate3 code, disabled for now.
#	if 0
Card* Manager::GetHardDrive( )
{
	return &m_hard_drive;
}

const char* Manager::GetLastCardName( int port, int slot )
{
	Dbg_Assert( port < vMAX_PORT );
	Dbg_Assert( slot < vMAX_SLOT );
	
	Card *p_card = &m_card[port][slot];
	return p_card->GetLastPersonalizedName();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const char *Card::GetPersonalizedName()
{
	// Only get the name if the card is mounted and is not the hard drive.
	if (m_mounted_drive_letter && m_mounted_drive_letter!='u')
	{
		sprintf(mp_personalized_name,"");
		
		WCHAR p_personalized_name[MAX_MUNAME+10];
		DWORD rv=XMUNameFromDriveLetter(m_mounted_drive_letter,p_personalized_name,MAX_MUNAME);
		if (rv==ERROR_SUCCESS)
		{
			// Instead of doing a wsprintfA( mp_personalized_name, "%ls", p_personalized_name );
			// which will terminate as soon as it hits a bad char, convert each WCHAR one at a 
			// time so that all good characters come across.
			char *p_dest=mp_personalized_name;
			int count=0;
			WCHAR p_temp[2]; // A buffer for holding one WCHAR at a time for sending to wsprintfA
			p_temp[1]=0;
			const WCHAR *p_scan=p_personalized_name;
			while (*p_scan) // WCHAR strings are terminated by a 0 just like normal strings, except its a 2byte 0.
			{
				p_temp[0]=*p_scan++;
				
				char p_one_char[10];
				wsprintfA( p_one_char, "%ls", p_temp);
				// p_one_char now contains a one char string.
				
				if (count=75)
	{
		return true;
	}	
	return false;
}
#	endif
	

// Note: dir_name must no longer start with a backslash, it should just be "Career2-Career" etc.
bool Card::MakeDirectory( const char* dir_name )
{
	char	output_dir[vDIRECTORY_NAME_BUF_SIZE];
	WCHAR	input_name[vDIRECTORY_NAME_BUF_SIZE];

	wsprintfW( input_name, L"%hs", dir_name );
	DWORD rv = XCreateSaveGame(	"u:\\",				// Root of device on which to create the save game.
								input_name,			// Name of save game (effectively directory name).
								OPEN_ALWAYS,		// Open disposition.
								0,					// Creation flags.
								output_dir,			// String to take resultant directory name buffer.
								vDIRECTORY_NAME_BUF_SIZE );	// Size of directory name buffer.

	if( rv == ERROR_SUCCESS )
	{
		return true;
	}

	if (rv==ERROR_DISK_FULL)
	{
		m_last_error=vINSUFFICIENT_SPACE;
	}
		
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const char *Card::ConvertDirectory( const char* dir_name )
{
	static char	output_dir[vDIRECTORY_NAME_BUF_SIZE];
	WCHAR	input_name[vDIRECTORY_NAME_BUF_SIZE];

	wsprintfW( input_name, L"%hs", dir_name );
	DWORD rv = XCreateSaveGame(	"u:\\",				// Root of device on which to create the save game.
								input_name,			// Name of save game (effectively directory name).
								OPEN_EXISTING,		// Open disposition.
								0,					// Creation flags.
								output_dir,			// String to take resultant directory name buffer.
								vDIRECTORY_NAME_BUF_SIZE );	// Size of directory name buffer.

	if( rv == ERROR_SUCCESS )
	{
		// Remove the initial "u:\" and the final "\"
		strcpy(output_dir,output_dir+3);
		output_dir[strlen(output_dir)-1]=0;
		
		return output_dir;
	}

	return NULL;
}	

// Skate3 code, disabled for now.
#if 0

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::ConvertDirectory( const char* dir_name, char* output_name )
{
	// K: This used to assert.
	if (m_mounted_drive_letter==0)
	{
		return false;
	}	

	// Seems incoming filenames are of the form /foo etc.
	cardFilenameBuffer[0] = m_mounted_drive_letter;
	cardFilenameBuffer[1] = ':';
	cardFilenameBuffer[2] = '\\';
	cardFilenameBuffer[3] = 0;

	++dir_name;
	int index = 4;
	while( cardFilenameBuffer[index] = *dir_name )
	{
		// Switch forward slash directory separators to the supported backslash.
		if( cardFilenameBuffer[index] == '/' )
		{
			cardFilenameBuffer[index] = '\\';
		}
		++index;
		++dir_name;
	}

	char	output_dir[64];
	WCHAR	input_name[64];

	wsprintfW( input_name, L"%hs", &cardFilenameBuffer[4] );
	DWORD rv = XCreateSaveGame(	cardFilenameBuffer,	// Root of device on which to create the save game.
								input_name,			// Name of save game (effectively directory name).
								OPEN_EXISTING,		// Open disposition.
								0,					// Creation flags.
								output_dir,			// String to take resultant directory name buffer.
								64 );				// Size of directory name buffer.

	if( rv == ERROR_SUCCESS )
	{
		// Copy over output directory, stripping leading drive, colon and backslash, and removing trailing backslash.
		strcpy( output_name, &output_dir[3] );
		output_name[strlen( output_name ) - 1] = 0;
		return true;
	}

	return false;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::DeleteDirectory( const char* dir_name )
{
	Dbg_Assert( m_mounted_drive_letter != 0 );

	// Seems incoming filenames are of the form /foo etc.
	cardFilenameBuffer[0] = m_mounted_drive_letter;
	cardFilenameBuffer[1] = ':';
	cardFilenameBuffer[2] = '\\';
	cardFilenameBuffer[3] = 0;

	++dir_name;
	int index = 4;
	while( cardFilenameBuffer[index] = *dir_name )
	{
		// Switch forward slash directory separators to the supported backslash.
		if( cardFilenameBuffer[index] == '/' )
		{
			cardFilenameBuffer[index] = '\\';
		}
		++index;
		++dir_name;
	}

	WCHAR	input_name[64];
	wsprintfW( input_name, L"%hs", &cardFilenameBuffer[4] );
	DWORD rv = XDeleteSaveGame(	cardFilenameBuffer,			// Root of device on which to create the save game.
								input_name );

	if( rv == ERROR_SUCCESS )
	{
		return true;
	}

	return false;
}

#endif


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

// Given the name of a file, this will delete it's directory, which
// will result in the deletion of the directory, the file, and the icon.
// The name must not be preceded with a backslash, ie should be "Career12-Career" for example.
bool Card::DeleteDirectory( const char* dir_name )
{
	WCHAR	input_name[64];
	wsprintfW( input_name, L"%hs", dir_name );
	DWORD rv = XDeleteSaveGame(	"u:\\", input_name );

	if( rv == ERROR_SUCCESS )
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::ChangeDirectory( const char* dir_name )
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::Format( void )
{
	// Not supported from within an Xbox title.
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::Unformat( void )
{
	// Not supported from within an Xbox title.
	return false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::IsFormatted( void )
{
	// Must be formatted to have got this far on an Xbox title.
	return true;
}


void Card::SetAsHardDrive()
{
	m_mounted_drive_letter='u';
}

// Skate3 code, disabled for now.
#if 0
bool Card::IsHardDrive()
{
	return m_mounted_drive_letter=='u';
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Card::UnMount( void )
{
	// Can't unmount the hard drive.
	if( m_mounted_drive_letter=='u' )
	{
		return;
	}	
	
    m_mount_error=false;
	m_mount_failed_due_to_card_full=false;
	m_mount_failed_due_to_card_unformatted=false;
	
	if( m_mounted_drive_letter )
	{
		DWORD rv = XUnmountMU(m_port, ( m_slot == 0 ) ? XDEVICE_TOP_SLOT : XDEVICE_BOTTOM_SLOT);
		if (rv != ERROR_SUCCESS)
		{
			m_mount_error=true;
			if (rv==ERROR_DISK_FULL)
			{
				m_mount_failed_due_to_card_full=true;
			}	
			if (rv==ERROR_UNRECOGNIZED_VOLUME)	
			{
				m_mount_failed_due_to_card_unformatted=true;
			}
		}    
		m_mounted_drive_letter=0;
	}
}
	
#endif


int	Card::GetDeviceType( void )
{
	return vDEV_XBOX_HARD_DRIVE;
}


// Skate3 code, disabled for now.
#if 0

bool Card::GetMountError()
{
    return m_mount_error;
}

bool Card::MountFailedDueToCardFull()
{
	return m_mount_failed_due_to_card_full;
}	

bool Card::MountFailedDueToCardUnformatted()
{
	return m_mount_failed_due_to_card_unformatted;
}	

#endif


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int	Card::GetNumFreeClusters( void )
{
	if( m_mounted_drive_letter )
	{
		char p_drive[20];
		strcpy(p_drive,"z:\\");
		
		ULARGE_INTEGER	uliFreeAvail;
		ULARGE_INTEGER	uliTotal;

		p_drive[0] = m_mounted_drive_letter;
		BOOL br		= GetDiskFreeSpaceEx( p_drive, &uliFreeAvail, &uliTotal, NULL );
		if( br )
		{
			// Each increment of HighPart represents 2^32 bytes, which is (2^32)/16384=262144 blocks.
			return uliFreeAvail.HighPart*262144 + uliFreeAvail.LowPart/16384;
		}
		else
		{
			return 0;
		}
	}
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int	Card::GetNumFreeEntries( const char* path )
{
	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::Delete( const char* filename )
{
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::Rename( const char* old_name, const char* new_name )
{
	return true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
File* Card::Open( const char* filename, int mode, int size )
{
	Dbg_Assert( m_mounted_drive_letter != 0 );

	m_last_error=0;
	
	File*	p_file		= NULL;
	HANDLE	handle;

	// Seems incoming filenames are of the form /foo/bar etc.
	cardFilenameBuffer[0] = m_mounted_drive_letter;
	cardFilenameBuffer[1] = ':';

	int index = 2;
	while( cardFilenameBuffer[index] = *filename )
	{
		// Switch forward slash directory separators to the supported backslash.
		if( cardFilenameBuffer[index] == '/' )
		{
			cardFilenameBuffer[index] = '\\';
		}
		++index;
		++filename;
	}

	DWORD dwDesiredAccess;
	DWORD dwCreationDisposition;

	switch( mode )
	{
		case File::mMODE_READ:
		{
			dwDesiredAccess			= GENERIC_READ;
			dwCreationDisposition	= OPEN_EXISTING;
			break;
		}

		case File::mMODE_WRITE:
		{
			dwDesiredAccess			= GENERIC_WRITE;
			dwCreationDisposition	= OPEN_EXISTING;
			break;
		}

		case ( File::mMODE_WRITE | File::mMODE_CREATE ):
		{
			dwDesiredAccess			= GENERIC_WRITE;
			dwCreationDisposition	= OPEN_ALWAYS;
			break;
		}

		case File::mMODE_CREATE:
		{
			dwDesiredAccess			= GENERIC_WRITE;
			dwCreationDisposition	= CREATE_NEW;
			break;
		}

		case ( File::mMODE_READ | File::mMODE_WRITE ):
		{
			dwDesiredAccess	= GENERIC_READ | GENERIC_WRITE;
			dwCreationDisposition	= OPEN_EXISTING;
			break;
		}

		default:
		{
			Dbg_Assert( 0 );
			return NULL;
		}
	}

	handle = CreateFile(	cardFilenameBuffer,							// file name
							dwDesiredAccess,							// access mode
							0,											// share mode
							NULL,										// security attributes
							dwCreationDisposition,						// how to create
							FILE_ATTRIBUTE_NORMAL,						// file attributes and flags
							NULL );				                        // handle to template file

	if( handle != INVALID_HANDLE_VALUE )
	{
		p_file = new File( (int)handle, this );
		
		WIN32_FILE_ATTRIBUTE_DATA file_attribute_data;
		if (GetFileAttributesEx(cardFilenameBuffer,GetFileExInfoStandard,&file_attribute_data))
		{
//			Skate3 code, disabled for now.
#			if 0
			p_file->m_file_time=file_attribute_data.ftLastWriteTime;
#			endif
		}	
		
		return p_file;
	}
	else
	{
		if (GetLastError()==ERROR_DISK_FULL)
		{
			m_last_error=vINSUFFICIENT_SPACE;
		}
	}	

	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::GetFileList( const char* mask, Lst::Head< File > &file_list )
{
	HANDLE			handle;
	XGAME_FIND_DATA	find_data;

	cardFilenameBuffer[0] = m_mounted_drive_letter;
	cardFilenameBuffer[1] = ':';
	cardFilenameBuffer[2] = '\\';

	cardFilenameBuffer[3] = 0;
	if(( handle = XFindFirstSaveGame( cardFilenameBuffer, &find_data )) == INVALID_HANDLE_VALUE )
	{
		return true;
	}

	do
	{
		File* new_file = new File( 0, this );

		// Skip copying the drive stuff, just copy the directory, and strip the trailing '\'.
		strcpy( new_file->m_Filename, (char*)&find_data.szSaveGameDirectory[3] );
		new_file->m_Filename[strlen( new_file->m_Filename ) - 1] = 0;

		wsprintfA( new_file->m_DisplayFilename, "%ls", find_data.szSaveGameName );
		
		FILETIME local_file_time;
		FileTimeToLocalFileTime(&find_data.wfd.ftLastWriteTime,&local_file_time);
		SYSTEMTIME system_file_time;
		FileTimeToSystemTime(&local_file_time,&system_file_time);
		
		new_file->m_Modified.m_Year=system_file_time.wYear;
		new_file->m_Modified.m_Month=system_file_time.wMonth;
		new_file->m_Modified.m_Day=system_file_time.wDay;
		new_file->m_Modified.m_Hour=system_file_time.wHour;
		new_file->m_Modified.m_Minutes=system_file_time.wMinute;
		new_file->m_Modified.m_Seconds=system_file_time.wSecond;
		
		new_file->m_Size	= find_data.wfd.nFileSizeLow;
		new_file->m_Attribs	= 0;
		file_list.AddToTail( new_file );
	}
	while( XFindNextSaveGame( handle, &find_data ));
	XFindClose( handle );
	return true;
}


File::File( int fd, Card* card ) : Lst::Node< File > ( this ), m_fd( fd ), m_card( card )
{
}
		
File::~File()
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int File::Seek( int offset, FilePointerBase base )
{
	Dbg_Assert( m_fd != 0 );

	DWORD dwMoveMethod;

	switch( base )
	{
		case BASE_START:
			dwMoveMethod = FILE_BEGIN;
			break;
		case BASE_CURRENT:
			dwMoveMethod = FILE_CURRENT;
			break;
		case BASE_END:
			dwMoveMethod = FILE_END;
			break;
		default:
			dwMoveMethod = FILE_END;
			Dbg_MsgAssert( 0,( "Invalid FilePointerBase\n" ));
			break;
	}


	DWORD result = SetFilePointer(	(HANDLE)m_fd,		// handle to file
									offset,				// bytes to move pointer
									NULL,				// high-order bytes to move pointer
									dwMoveMethod );		// starting point

	return result;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool File::Flush( void )
{
	Dbg_Assert( m_fd != 0 );

	FlushFileBuffers((HANDLE)m_fd );

	// The FlushFileBuffers() is pretty strict about what types of files wmay be flushed,
	// whereas the PS2 equivalent doesn't really care. Just return a positive response always,
	// no critical stuff predicated on this return anway.
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	File::Write( void* data, int len )
{
	Dbg_Assert( m_fd != 0 );

//	Skate3 code, disabled for now.
#	if 0
	m_not_enough_space_to_write_file=false;
#	endif

	DWORD bytes_written;
	BOOL rv = WriteFile(	(HANDLE)m_fd,		// handle to file
							data,				// data buffer
							len,				// number of bytes to write
							&bytes_written,		// number of bytes written
							NULL );				// overlapped buffer
							
//	Skate3 code, disabled for now.
#	if 0
	if (rv==ERROR_NOT_ENOUGH_MEMORY)
	{
		m_not_enough_space_to_write_file=true;
	}
	if (GetLastError()==ERROR_DISK_FULL)
	{
		m_not_enough_space_to_write_file=true;
	}
#	endif
		
	if( rv )
	{
		return (int)bytes_written;
	}

	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	File::Read( void* buff, int len )
{
	Dbg_Assert( m_fd != 0 );

	DWORD bytes_read;
	BOOL rv = ReadFile(	(HANDLE)m_fd,			// handle to file
						buff,					// data buffer
						len,					// number of bytes to read
						&bytes_read,			// number of bytes read
						NULL );					// overlapped buffer
	if( rv )
	{
		return (int)bytes_read;
	}

	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool File::Close( void )
{
	Dbg_Assert( m_fd != 0 );

	return CloseHandle((HANDLE)m_fd );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mc






================================================
FILE: Code/Sys/MemCard/ngc/p_McMan.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment.			                **
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS														**
**																			**
**	Module:			Mc			 											**
**																			**
**	File name:		memcard.cpp												**
**																			**
**	Created by:		03/06/01	-	spg										**
**																			**
**	Description:	Memcard - platform-specific implementations				**
**																			**
*****************************************************************************/

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Mc
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/
												
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define MAX_FILENAME_LENGTH	CARD_FILENAME_MAX

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "MemCard Manager" );

static char cardFilenameBuffer[MAX_FILENAME_LENGTH];

static bool initCalled = false;

// Implicit assumption here is that vMAX_PORT == 1, which is always true for Gamecube.
static char cardWorkArea[Manager::vMAX_SLOT][CARD_WORKAREA_SIZE] __attribute__((aligned( 32 )));

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Private Functions								**
*****************************************************************************/

static void detachCallback( s32 chan, s32 result )
{
	OSReport( "card %d detached: result = %d\n", chan, result );

	Spt::SingletonPtr< Mc::Manager > mc_man;

	// Don't want to call GetCard() here, since that will go off and try to verify the card...
	Card* p_card = mc_man->GetCardEx( 0, chan );
	if( p_card && p_card->mp_work_area )
	{
//		delete[] p_card->mp_work_area;
		p_card->mp_work_area	= NULL;
	}
}



Manager::Manager( void )
{
	if( !initCalled )
	{
		initCalled = true;
		CARDInit();
	}

	for( int i = 0; i < vMAX_PORT; i++ )
	{
		for( int j = 0; j < vMAX_SLOT; j++ )
		{
			m_card[i][j].m_port = i;
			m_card[i][j].m_slot = j;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
}

/*****************************************************************************
**							  Public Functions								**
*****************************************************************************/

int	Manager::GetMaxSlots( int port )
{
	return vMAX_SLOT;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Card* Manager::GetCard( int port, int slot )
{
	Dbg_Assert( port < vMAX_PORT );
	Dbg_Assert( slot < vMAX_SLOT );
	
	// Disable the reset button. Scripts will also do this by calling the script command
	// DisableReset at appropriate points.
	// The scripts may disable reset a bit earlier, eg when a 'checking card' message is on
	// screen. They do that to prevent lots of TT bugs because if the reset button is enabled
	// during the message it will seem like you can reset during card access, even though the
	// card is not really being accessed then.
	// It is done here too as a guarantee.
	//printf("GetCard is disabling reset ...\n");
	//NxNgc::EngineGlobals.disableReset = true;
	
	Card*	card	= &m_card[port][slot];
	int		type	= card->GetDeviceType();

	if( type == Card::vDEV_GC_CARD )
	{
		return card;
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Card::MakeDirectory( const char* dir_name )
{
//	Dbg_Assert( m_mounted_drive_letter != 0 );
//
//	// Seems incoming filenames are of the form /foo etc.
//
//	cardFilenameBuffer[0] = m_mounted_drive_letter;
//	cardFilenameBuffer[1] = ':';
//
//	int index = 2;
//	while( cardFilenameBuffer[index] = *dir_name )
//	{
//		// Switch forward slash directory separators to the supported backslash.
//		if( cardFilenameBuffer[index] == '/' )
//		{
//			cardFilenameBuffer[index] = '\\';
//		}
//		++index;
//		++dir_name;
//	}
//
//	if( CreateDirectory( cardFilenameBuffer, NULL ))
//	{
//		return true;
//	}

	// Directory structure is not supported on Gamecube memory cards.
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::DeleteDirectory( const char* dir_name )
{
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
const char *Card::ConvertDirectory( const char* dir_name )
{
	return NULL;
}	

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Card::ChangeDirectory( const char* dir_name )
{
	// Directory structure is not supported on Gamecube memory cards.
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::Format( void )
{
	if( mp_work_area )
	{
		s32 format_result = CARDFormat( m_slot );
		
		if( format_result != CARD_RESULT_READY )
		{
			// If the format fails, then we need to do a CARDMount again, otherwise	
			// CARDCheck will always return CARD_RESULT_NOCARD, even if a good card is inserted. (TT4975)
			// Force a CARDMount by deleting the mp_work_area buffer and calling GetDeviceType.
			if( mp_work_area )
			{
	//			delete [] mp_work_area;
				mp_work_area = NULL;
			}
			GetDeviceType();
			
			m_broken=true;
		}
		
		if( format_result == CARD_RESULT_READY )
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::Unformat( void )
{
	// Not supported from within a GameCube title.
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::IsFormatted( void )
{
	if( mp_work_area )
	{
		s32 check_result = CARDCheck( m_slot );
		if (check_result==CARD_RESULT_IOERROR)
		{
			m_broken=true;
		}	
		if( check_result == CARD_RESULT_READY )
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::IsForeign( void )
{
	if( mp_work_area )
	{
		u16 encode;
		CARDGetEncoding(m_slot,&encode);
		if (encode==CARD_ENCODE_SJIS)
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool Card::IsBadDevice( void )
{
	if( mp_work_area )
	{
		s32 check_result = CARDCheck( m_slot );
		if( check_result == CARD_RESULT_WRONGDEVICE )
		{
			return true;
		}
	}
	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Card::GetDeviceType( void )
{
	// See whether this card is present.
	s32	mem_size;
	s32	sector_size;
	s32	probe_result;
	s32 loop_iterations	= 0;

	// Cleat fatal error tracking variable.
	Spt::SingletonPtr< Mc::Manager > mc_man;
	mc_man->SetFatalError( false );
	mc_man->SetWrongDevice( false );

	do
	{
		// This would appear to be a problem with the 0.93 dev hardware.
		probe_result = CARDProbeEx( m_slot, &mem_size, §or_size );

		// CARDProbeEx() will return BUSY if the system is still checking the card, so safe to quit on a NOCARD.
		if( probe_result == CARD_RESULT_NOCARD )
		{
			break;
		}

		if( probe_result == CARD_RESULT_WRONGDEVICE )
		{
			mc_man->SetWrongDevice( true );
			break;
		}	

//		// Check for reset being pushed.
//		if( NsDisplay::shouldReset())
//		{
//			// Prepare the game side of things for reset.
//			Spt::SingletonPtr< Mlp::Manager > mlp_manager;
//			//mlp_manager->PrepareReset();
//
//			// Prepare the system side of things for reset.
//			NsDisplay::doReset();
//		}
	}
	while(( probe_result != CARD_RESULT_READY ) && ( ++loop_iterations < 1000000 ));

	switch( probe_result )
	{
		case CARD_RESULT_BUSY:
		case CARD_RESULT_NOCARD:
		case CARD_RESULT_READY:
		case CARD_RESULT_FATAL_ERROR:
		{
			// If this card is not mounted, mount it now.
			if( mp_work_area == NULL )
			{
				m_broken=false;
				
//				mp_work_area = new char[CARD_WORKAREA_SIZE];
				mp_work_area = &( cardWorkArea[m_slot][0] );
				Dbg_MsgAssert( (((uint32)mp_work_area)&0x1f)==0, ("mp_work_area not 32 byte aligned"));
				
				s32 mount_result = CARDMount( m_slot, mp_work_area, detachCallback );
				
				if (mount_result == CARD_RESULT_BROKEN)
				{
					s32 check_result = CARDCheck( m_slot );
					//if (check_result == CARD_RESULT_BROKEN || check_result==CARD_RESULT_IOERROR)
					if (check_result==CARD_RESULT_IOERROR)
					{
						m_broken=true;
					}	
				}	
				
				if(( mount_result == CARD_RESULT_READY ) || ( mount_result == CARD_RESULT_BROKEN ) || ( mount_result == CARD_RESULT_ENCODING ))
				{
					// Mounted. Required to call CARDCheck() at this stage.
					s32 check_result = CARDCheck( m_slot );
					
					if(( check_result == CARD_RESULT_READY ) || ( check_result == CARD_RESULT_BROKEN ) || ( mount_result == CARD_RESULT_ENCODING ))
					{
						m_mem_size		= mem_size;
						m_sector_size	= sector_size;

						return vDEV_GC_CARD;
					}
					else
					{
//						delete [] mp_work_area;
						mp_work_area = NULL;
					}
				}
				else
				{
					if(( mount_result == CARD_RESULT_IOERROR ) || ( mount_result == CARD_RESULT_FATAL_ERROR ))
					{
						// Set Manager tracking variable.
						mc_man->SetFatalError( true );
					}
					else if (mount_result==CARD_RESULT_WRONGDEVICE)
					{
						mc_man->SetWrongDevice( true );
					}	
//					delete [] mp_work_area;
					mp_work_area = NULL;
				}
			}			
			else
			{
				return vDEV_GC_CARD;
			}
			break;
		}

		case CARD_RESULT_WRONGDEVICE:
		default:
		{
			// No valid card, or can't be processed at this time.
			break;
		}
	}	

	return 0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Card::GetNumFreeClusters( void )
{
	s32 bytes_free;
	s32 files_free;
	s32 free_result = CARDFreeBlocks( m_slot, &bytes_free, &files_free );
	if( free_result == CARD_RESULT_READY )
	{
		return bytes_free / 8192;
	}

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	Card::GetNumFreeEntries( const char* path )
{
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Card::Delete( const char* filename )
{
	// Seems incoming filenames are of the form /foo/bar etc. Since there is no
	// directory structure on the Gamecube memory card, just convert to a single filename
	// by removing '\' and '/'.
	int index = 0;
	while(( cardFilenameBuffer[index] = *filename ))
	{
		Dbg_Assert( index < MAX_FILENAME_LENGTH );

		// Switch forward slash directory separators to the supported backslash.
		if(( cardFilenameBuffer[index] == '/' ) || ( cardFilenameBuffer[index] == '\\' ) || ( cardFilenameBuffer[index] == '.' ))
		{
		}
		else
		{
			++index;
		}
		++filename;
	}

	s32 delete_result = CARDDelete( m_slot, cardFilenameBuffer );
	if( delete_result == CARD_RESULT_IOERROR )
	{
		m_broken=true;
	}

	if( delete_result == CARD_RESULT_READY )
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Card::Rename( const char* old_name, const char* new_name )
{
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

File* Card::Open( const char* filename, int mode, int size )
{
	Dbg_Assert( mp_work_area != 0 );

	// Create the file here, it may be deleted if the open fails.
	File* p_file = new File( 0, this );

	// Seems incoming filenames are of the form /foo/bar etc. Since there is no
	// directory structure on the Gamecube memory card, just convert to a single filename
	// by removing '\' and '/'.
	int index = 0;
	while(( cardFilenameBuffer[index] = *filename ))
	{
		Dbg_MsgAssert( index < MAX_FILENAME_LENGTH, ( "Filename: %s too long\n", filename ));

		// Switch forward slash directory separators to the supported backslash.
		if(( cardFilenameBuffer[index] == '/' ) || ( cardFilenameBuffer[index] == '\\' ) || ( cardFilenameBuffer[index] == '.' ))
		{
		}
		else
		{
			++index;
		}
		++filename;
	}

	// If we might be creating the file, ensure the size is aligned to a multiple of the sector size.
	if( mode & File::mMODE_CREATE )
	{
		Dbg_Assert( m_sector_size > 0 );
		size = ( size + ( m_sector_size - 1 )) & ~( m_sector_size - 1 );
	}

	s32 open_result;
	switch( mode )
	{
		case File::mMODE_READ:
		{
			open_result = CARDOpen( m_slot, cardFilenameBuffer, &p_file->m_file_info );
			break;
		}

		case File::mMODE_WRITE:
		{
			open_result = CARDOpen( m_slot, cardFilenameBuffer, &p_file->m_file_info );
			break;
		}

		case ( File::mMODE_WRITE | File::mMODE_CREATE ):
		{
			open_result = CARDOpen( m_slot, cardFilenameBuffer, &p_file->m_file_info );
			if( open_result == CARD_RESULT_NOFILE )
			{
				open_result = CARDCreate( m_slot, cardFilenameBuffer, size, &p_file->m_file_info );
			}
			break;
		}

		case File::mMODE_CREATE:
		{
			open_result = CARDCreate( m_slot, cardFilenameBuffer, size, &p_file->m_file_info );
			break;
		}

		case ( File::mMODE_READ | File::mMODE_WRITE ):
		{
			open_result = CARDOpen( m_slot, cardFilenameBuffer, &p_file->m_file_info );
			break;
		}

		default:
		{
			Dbg_Assert( 0 );
			open_result = CARD_RESULT_FATAL_ERROR;
			break;
		}
	}

	if (open_result == CARD_RESULT_INSSPACE )
	{
		SetError(vINSUFFICIENT_SPACE);
	}
		
	if( open_result == CARD_RESULT_READY )
	{
		// Set the file number.
		p_file->m_file_number = CARDGetFileNo( &p_file->m_file_info );
		return p_file;
	}

	delete p_file;
	return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void s_convert_time(uint32 seconds, DateTime *p_dateTime)
{
	// seconds is the number of seconds since 01/01/2000 midnight
	
	OSCalendarTime ct;

	OSTicksToCalendarTime(OSSecondsToTicks((uint64)seconds),&ct);
	p_dateTime->m_Seconds = ct.sec;
	p_dateTime->m_Minutes = ct.min;
	p_dateTime->m_Hour = ct.hour;
	p_dateTime->m_Day = ct.mday;
	// The month value in OSCalendarTime starts at 0. May as well make it consistent
	// with the other platforms by adding 1. It doesn't really matter whether 1 is added
	// or not though cos the date is only used to determine the most recent save.
	p_dateTime->m_Month = ct.mon+1;
	p_dateTime->m_Year = ct.year;
}

bool Card::GetFileList( const char* mask, Lst::Head< File > &file_list )
{
	for( int i = 0; i < 127; ++i )
	{
		CARDStat stat;
		s32	result = CARDGetStatus( 0, i, &stat );
		if( result == CARD_RESULT_READY )
		{
			File* new_file;

			new_file = new File( 0, this );
			strcpy( new_file->m_Filename, (char*)stat.fileName );
			new_file->m_Size = stat.length;
			
			new_file->m_Attribs = 0;
			new_file->m_Attribs |= File::mATTRIB_READABLE;
			new_file->m_Attribs |= File::mATTRIB_WRITEABLE;

			s_convert_time(stat.time,&new_file->m_Modified);
			
//			if( file_table[i].AttrFile & sceMcFileAttrExecutable )
//			{
//				new_file->m_Attribs |= File::mATTRIB_EXECUTABLE;
//			}
//			if( file_table[i].AttrFile & sceMcFileAttrDupProhibit )
//			{
//				new_file->m_Attribs |= File::mATTRIB_DUP_PROHIBITED;
//			}
//			if( file_table[i].AttrFile & sceMcFileAttrSubdir )
//			{
//				new_file->m_Attribs |= File::mATTRIB_DIRECTORY;
//			}
//			if( file_table[i].AttrFile & sceMcFileAttrClosed )
//			{
//				new_file->m_Attribs |= File::mATTRIB_CLOSED;
//			}
//			if( file_table[i].AttrFile & sceMcFileAttrPDAExec )
//			{
//				new_file->m_Attribs |= File::mATTRIB_PDA_APP;
//			}
//			if( file_table[i].AttrFile & sceMcFileAttrPS1 )
//			{
//				new_file->m_Attribs |= File::mATTRIB_PS1_FILE;
//			}
//			new_file->m_Created.m_Seconds = file_table[i]._Create.Sec;
//			new_file->m_Created.m_Minutes = file_table[i]._Create.Min;
//			new_file->m_Created.m_Hour = file_table[i]._Create.Hour;
//			new_file->m_Created.m_Day = file_table[i]._Create.Day;
//			new_file->m_Created.m_Month = file_table[i]._Create.Month;
//			new_file->m_Created.m_Year = file_table[i]._Create.Year;

//			new_file->m_Modified.m_Seconds = file_table[i]._Modify.Sec;
//			new_file->m_Modified.m_Minutes = file_table[i]._Modify.Min;
//			new_file->m_Modified.m_Hour = file_table[i]._Modify.Hour;
//			new_file->m_Modified.m_Day = file_table[i]._Modify.Day;
//			new_file->m_Modified.m_Month = file_table[i]._Modify.Month;
//			new_file->m_Modified.m_Year = file_table[i]._Modify.Year;

			file_list.AddToTail( new_file );
		}
	}
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Card::CountFilesLeft()
{
	int NumFiles=0;
	for( int i = 0; i < 127; ++i )
	{
		CARDStat stat;
		s32	result = CARDGetStatus( 0, i, &stat );
		if( result == CARD_RESULT_READY )
		{
			++NumFiles;
		}
	}
	return 127-NumFiles;
}

File::File( int fd, Card* card ) : Lst::Node< File > ( this ), m_fd( fd ), m_card( card )
{
	m_need_to_flush=false;
	m_write_offset=0;
	mp_write_buffer=NULL;
	
	m_read_offset=0;
	mp_read_buffer=NULL;
}
		
File::~File()
{
	Dbg_MsgAssert(!m_need_to_flush,("Closed mem card file when it needed flushing"));
	
	if (mp_write_buffer)
	{
		Mem::Free(mp_write_buffer);
		mp_write_buffer=NULL;
	}	
	if (mp_read_buffer)
	{
		Mem::Free(mp_read_buffer);
		mp_read_buffer=NULL;
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int File::Seek( int offset, FilePointerBase base )
{
	m_read_offset=offset;
	// Force a reload of the read buffer by deleting it.
	if (mp_read_buffer)
	{
		Mem::Free(mp_read_buffer);
		mp_read_buffer=NULL;
	}
		
	int result = 0;

	switch( base )
	{
		case BASE_START:
			break;
		case BASE_CURRENT:
			Dbg_MsgAssert( 0, ( "Not supported" ));
			break;
		case BASE_END:
		{
			CARDStat	status;
			s32			status_result = CARDGetStatus( m_card->GetSlot(), m_file_number, &status );
			if( status_result == CARD_RESULT_READY )
			{
				result = status.length - offset;
			}
			break;
		}
		default:
			Dbg_MsgAssert( 0,( "Invalid FilePointerBase\n" ));
			return 0;
			break;
	}

	return result;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool File::Flush( void )
{
	// The FlushFileBuffers() is pretty strict about what types of files wmay be flushed,
	// whereas the PS2 equivalent doesn't really care. Just return a positive response always,
	// no critical stuff predicated on this return anway.
	
	if (!m_need_to_flush)
	{
		return true;
	}
	
	int num_bytes_to_write=m_write_offset%m_card->m_sector_size;
	Dbg_MsgAssert(num_bytes_to_write,("Got zero num_bytes_to_write in File::Flush"));
	
	Dbg_MsgAssert(mp_write_buffer,("NULL mp_write_buffer"));
	memset(mp_write_buffer+num_bytes_to_write,0,m_card->m_sector_size-num_bytes_to_write);
	
	int file_offset=m_write_offset-num_bytes_to_write;
	
	Dbg_MsgAssert(file_offset%m_card->m_sector_size == 0,("Bad file_offset"));
	s32 write_result = CARDWrite( &m_file_info, mp_write_buffer, m_card->m_sector_size, file_offset );
	if ( write_result != CARD_RESULT_READY )
	{
		return false;
	}
	
	m_need_to_flush=false;
	m_write_offset=0;
	if (mp_write_buffer)
	{
		Mem::Free(mp_write_buffer);
		mp_write_buffer=NULL;
	}	
	
	return true;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int	File::Write( void* data, int len )
{
	Dbg_MsgAssert(data,("NULL data"));
	Dbg_Assert( len > 0 );
	Dbg_Assert( m_card->m_sector_size > 0 );

	if (!mp_write_buffer)
	{
		// Grab a sector-sized chunk of memory, ensuring it is 32-byte aligned.
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
		Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
		mp_write_buffer = (uint8*)Mem::Malloc(m_card->m_sector_size);
		Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
		Mem::Manager::sHandle().PopContext();
	}
	Dbg_MsgAssert(mp_write_buffer,("NULL mp_write_buffer"));
	
	// Ensure the buffer address is 32 byte aligned.
	Dbg_MsgAssert((((unsigned int)mp_write_buffer ) & 31 ) == 0, ( "file write buffer must be 32 byte aligned" ));

	m_need_to_flush=false;

	int	bytes_remaining_to_write = len;
	unsigned char*	p_source_buffer	= (unsigned char*)data;

	int write_buffer_sector_offset=m_write_offset%m_card->m_sector_size;
	int write_buffer_left=m_card->m_sector_size - write_buffer_sector_offset;
		
	if (bytes_remaining_to_write < write_buffer_left)
	{
		memcpy( mp_write_buffer+write_buffer_sector_offset, p_source_buffer, bytes_remaining_to_write);
		m_write_offset+=bytes_remaining_to_write;
		m_need_to_flush=true;
		return len;
	}

	int file_offset=m_write_offset-write_buffer_sector_offset;

	if (write_buffer_left)
	{
		Dbg_MsgAssert(write_buffer_sector_offset+write_buffer_left==m_card->m_sector_size,("Oops"));
		memcpy( mp_write_buffer+write_buffer_sector_offset, p_source_buffer, write_buffer_left);
		m_write_offset+=write_buffer_left;
		bytes_remaining_to_write-=write_buffer_left;
		p_source_buffer+=write_buffer_left;
		
		Dbg_MsgAssert(file_offset%m_card->m_sector_size == 0,("Bad file_offset"));
		s32 write_result = CARDWrite( &m_file_info, mp_write_buffer, m_card->m_sector_size, file_offset );
		if ( write_result != CARD_RESULT_READY )
		{
			return 0;
		}
		file_offset+=m_card->m_sector_size;
		m_need_to_flush=false;
	}

	while (bytes_remaining_to_write)
	{
		if (bytes_remaining_to_write >= m_card->m_sector_size)
		{
			Dbg_MsgAssert(m_write_offset%m_card->m_sector_size == 0,("Oops"));
			
			memcpy( mp_write_buffer, p_source_buffer, m_card->m_sector_size);
			m_write_offset+=m_card->m_sector_size;
			bytes_remaining_to_write-=m_card->m_sector_size;
			p_source_buffer+=m_card->m_sector_size;
			
			Dbg_MsgAssert(file_offset%m_card->m_sector_size == 0,("Bad file_offset"));
			s32 write_result = CARDWrite( &m_file_info, mp_write_buffer, m_card->m_sector_size, file_offset );
			if ( write_result != CARD_RESULT_READY )
			{
				return 0;
			}
			file_offset+=m_card->m_sector_size;
			m_need_to_flush=false;
		}
		else
		{
			memcpy( mp_write_buffer, p_source_buffer, bytes_remaining_to_write);
			memset( mp_write_buffer+bytes_remaining_to_write, 0, m_card->m_sector_size-bytes_remaining_to_write);
			
			m_write_offset+=bytes_remaining_to_write;
			bytes_remaining_to_write=0;
			m_need_to_flush=true;
		}
	}
	return len;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
int	File::Read( void* buff, int len )
{
	Dbg_Assert( len > 0 );
	Dbg_Assert( m_card->m_sector_size > 0 );

	int read_buffer_sector_offset=m_read_offset%m_card->m_sector_size;
	int read_buffer_left=m_card->m_sector_size - read_buffer_sector_offset;

	if (!mp_read_buffer)
	{
		// Similar process to write, in that we read 1 sector at a time. So grab a sector-sized chunk of memory,
		// ensuring it is 32-byte aligned.
		Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
		Mem::Manager::sHandle().BottomUpHeap()->PushAlign( 32 );
		mp_read_buffer = (uint8*)Mem::Malloc(m_card->m_sector_size);
		Mem::Manager::sHandle().BottomUpHeap()->PopAlign();
		Mem::Manager::sHandle().PopContext();
		
		Dbg_MsgAssert(mp_read_buffer,("NULL mp_read_buffer"));
		
		// Ensure the buffer address is 32 byte aligned.
		Dbg_MsgAssert((((unsigned int)mp_read_buffer ) & 31 ) == 0, ( "file read buffer must be 32 byte aligned" ));
		
		// Also ensure the buffer is a multiple of CARD_READ_SIZE (should be, since this is smaller than the smallest sector).
		Dbg_MsgAssert(( m_card->m_sector_size & ( CARD_READ_SIZE - 1 )) == 0, ( "read buffer must be multiple of CARD_READ_SIZE" ));
		

		int file_offset=m_read_offset-read_buffer_sector_offset;
		Dbg_MsgAssert(file_offset%m_card->m_sector_size == 0,("Bad file_offset"));
		s32 read_result = CARDRead( &m_file_info, mp_read_buffer, m_card->m_sector_size, file_offset);
		if (read_result != CARD_RESULT_READY)
		{
			return 0;
		}	
	}
	else
	{
		if (read_buffer_sector_offset==0)
		{
			Dbg_MsgAssert(m_read_offset%m_card->m_sector_size == 0,("Bad m_read_offset"));
			s32 read_result = CARDRead( &m_file_info, mp_read_buffer, m_card->m_sector_size, m_read_offset);
			if (read_result != CARD_RESULT_READY)
			{
				return 0;
			}	
		}	
	}


	int	bytes_remaining_to_read	= len;
	unsigned char*	p_dest_buffer	= (unsigned char*)buff;

	while (true)
	{
		if (bytes_remaining_to_read < read_buffer_left)
		{
			if (bytes_remaining_to_read)
			{
				// The required data is all in the buffer already so copy it out and return.
				memcpy(p_dest_buffer,mp_read_buffer+read_buffer_sector_offset,bytes_remaining_to_read);
				m_read_offset+=bytes_remaining_to_read;
			}	
			return len;
		}
			
		// Copy out what is left in the buffer
		memcpy(p_dest_buffer,mp_read_buffer+read_buffer_sector_offset,read_buffer_left);
		p_dest_buffer+=read_buffer_left;
		m_read_offset+=read_buffer_left;
		bytes_remaining_to_read-=read_buffer_left;
		
		if (bytes_remaining_to_read==0)
		{
			return len;
		}
		
		
		Dbg_MsgAssert(m_read_offset%m_card->m_sector_size == 0,("Bad m_read_offset"));
		s32 read_result = CARDRead( &m_file_info, mp_read_buffer, m_card->m_sector_size, m_read_offset);
		if (read_result != CARD_RESULT_READY)
		{
			return 0;
		}	
			
		read_buffer_left=m_card->m_sector_size;
		read_buffer_sector_offset=0;
	}
			
	return 0;	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
bool File::Close( void )
{
	s32 close_result = CARDClose( &m_file_info );
	if( close_result == CARD_RESULT_READY )
	{
		return true;
	}

	return false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Mc







================================================
FILE: Code/Sys/Profiler.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 2002 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SysLib (System Library)									**
**																			**
**	Module:			Sys					 									**
**																			**
**	File name:		profiler.cpp											**
**																			**
**	Description:	Profiler class											**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 

#ifndef __PLAT_WN32__
#include 
#endif // __PLAT_WN32__

#ifdef	__PLAT_NGPS__	
#include 
#endif
#ifdef	__PLAT_NGC__	
#include 
#include 
#include 
#endif



#ifdef		__USE_PROFILER__


/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace Sys
{



/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

Tmr::MicroSeconds	Profiler::s_base_time[ 2 ];
uint32				Profiler::s_default_color = 0x00ff0000;
int					Profiler::s_flip = 0;
#ifdef	__PLAT_NGC__	
bool				Profiler::s_enabled = true;
bool				Profiler::s_active = true;
#else
bool				Profiler::s_enabled = false;
bool				Profiler::s_active = false;
#endif
uint				Profiler::s_vblanks;
float				Profiler::s_width;
float				Profiler::s_height;
float 				Profiler::s_scale;
float				Profiler::s_left_x;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

Profiler* CPUProfiler;		  
Profiler* VUProfiler;		  
Profiler* DMAProfiler;		  


/*****************************************************************************
**							Private Functions								**
*****************************************************************************/

/******************************************************************/

Profiler::Profiler( uint line )
{
	m_top_y = s_height * line;
	start_new_frame();			// just ensure everything points to something reasonable
}

/******************************************************************/

Profiler::~Profiler( void )
{
}

/******************************************************************/

void			box(int x0,int y0, int x1, int y1, uint32 color)
{
#ifdef	__PLAT_NGPS__	
	NxPs2::BeginLines2D(0x80000000 + color);
	NxPs2::DrawLine2D(x0,y0,0, x1,y0,0);
	NxPs2::DrawLine2D(x1,y0,0, x1,y1,0);
	NxPs2::DrawLine2D(x1,y1,0, x0,y1,0);
	NxPs2::DrawLine2D(x0,y1,0, x0,y0,0);
	NxPs2::EndLines2D();
#endif



#ifdef	__PLAT_NGC__	
//	NsPrim::line ( (float)x0, (float)y0, (float)x1, (float)y0, (GXColor){(color>>0)&0xff,(color>>8)&0xff,(color>>16)&0xff,255} );
//	NsPrim::line ( (float)x1, (float)y0, (float)x1, (float)y1, (GXColor){(color>>0)&0xff,(color>>8)&0xff,(color>>16)&0xff,255} );
//	NsPrim::line ( (float)x1, (float)y1, (float)x0, (float)y1, (GXColor){(color>>0)&0xff,(color>>8)&0xff,(color>>16)&0xff,255} );
//	NsPrim::line ( (float)x0, (float)y1, (float)x0, (float)y0, (GXColor){(color>>0)&0xff,(color>>8)&0xff,(color>>16)&0xff,255} );

	GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){(color>>0)&0xff,(color>>8)&0xff,(color>>16)&0xff,255} );

	// Send coordinates.
	GX::Begin( GX_LINESTRIP, GX_VTXFMT0, 5 ); 
		GX::Position3f32( (float)x0, (float)y0, -1.0f );
		GX::Position3f32( (float)x1, (float)y0, -1.0f );
		GX::Position3f32( (float)x1, (float)y1, -1.0f );
		GX::Position3f32( (float)x0, (float)y1, -1.0f );
		GX::Position3f32( (float)x0, (float)y0, -1.0f );
	GX::End();

#endif


}

void			line(int x0,int y0, int x1, int y1, uint32 color)
{
#ifdef	__PLAT_NGPS__	
	NxPs2::BeginLines2D(0x80000000 + color);
	NxPs2::DrawLine2D(x0,y0,0, x1,y1,0);
	NxPs2::EndLines2D();
#endif


#ifdef	__PLAT_NGC__	
//	NsPrim::line ( (float)x0, (float)y0, (float)x1, (float)y1, (GXColor){(color>>0)&0xff,(color>>8)&0xff,(color>>16)&0xff,255} );

	GX::SetChanAmbColor( GX_COLOR0A0, (GXColor){(color>>0)&0xff,(color>>8)&0xff,(color>>16)&0xff,255} );

	// Send coordinates.
	GX::Begin( GX_LINES, GX_VTXFMT0, 2 ); 
		GX::Position3f32( (float)x0, (float)y0, -1.0f );
		GX::Position3f32( (float)x1, (float)y1, -1.0f );
	GX::End();

#endif

}

/******************************************************************/

void			box(float x0,float y0, float x1, float y1, uint32 color)
{
	box ((int)x0,(int)y0,(int)x1,(int)y1, color);
}


/******************************************************************/

void Profiler::start_new_frame( void )
{
	mp_current_context_list 	= m_context_list[ s_flip ];
	mpp_current_context_stack 	= m_context_stack[ s_flip ];
	mp_current_context 			= NULL;								// nothing has been pushed yet
	mp_current_context_count	= &m_context_count[ s_flip ];
	m_context_stack_depth		= 0;								// nothing on stack
	m_next_context				= 0;								// start of list (index into mp_current_context_list)
	m_acc						= false;
	mp_acc_context				= NULL;
}

/******************************************************************/

void Profiler::render()
{
	if ( s_active )
	{
		/* We draw from the buffer that's not being updated this frame */
		int drawFlip = s_flip ^ 1;
		
		SProfileContext *p_context = m_context_list[ drawFlip ];
		Tmr::MicroSeconds	base_time = s_base_time[ drawFlip ];
		int n = m_context_count[ drawFlip ];
		while ( n--)
		{
			float startx 	= ( (uint32)(p_context->m_start_time - base_time) ) * s_scale;
			float endx 		= ( (uint32)(p_context->m_end_time - base_time) ) * s_scale;
			float top_y 	= m_top_y +( p_context->m_depth)* (s_height+2);
			box((s_left_x+startx),(top_y),  (s_left_x+endx),( top_y + s_height) , p_context->m_color);   
			p_context++;
		}
	}
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

void Profiler::PushContext( uint32 color, bool acc )
{
	
	if( s_active )
	{
		if ( acc && mp_acc_context )
		{
			// Want to accumulate to last context.
			m_acc_time = Tmr::GetTimeInUSeconds(); 
			m_acc_first = false;
		}
		else
		{
			// if we have a current context, then push it
			if (mp_current_context)
			{
				Dbg_MsgAssert(m_context_stack_depth < PROF_MAX_LEVELS,("Context stack overflow"));
				mpp_current_context_stack[m_context_stack_depth++] = mp_current_context;
			}

			// get a new context from the list
			Dbg_MsgAssert(m_next_context < PROF_MAX_CONTEXTS,("Context heap overflow"));
			mp_current_context = &mp_current_context_list[m_next_context++];
			// and fill it in with the start time, depth and color
			// (the end time is filled in by the Pop) 
			mp_current_context->m_start_time = Tmr::GetTimeInUSeconds();
			mp_current_context->m_depth = m_context_stack_depth;
			mp_current_context->m_color = color;
			if ( acc )
			{
				mp_acc_context = mp_current_context;
				m_acc_first = true;
			}
		}
		m_acc = acc;
	}
}


void Profiler::PushContext( uint8 r, uint8 g, uint8 b, bool acc )
{
	uint32 color = (b<<16)+(g<<8)+(r);
	PushContext(color, acc);
}

/******************************************************************/

void Profiler::PopContext( void )
{
	if( s_active)
	{
		if ( m_acc && !m_acc_first )
		{
			mp_acc_context->m_end_time += Tmr::GetTimeInUSeconds() - m_acc_time;
		}
		else
		{
			// set time on the current context		
			Dbg_MsgAssert(mp_current_context,("Popped one more profiler context than we pushed"));

			mp_current_context->m_end_time = Tmr::GetTimeInUSeconds();

			if ( ! m_context_stack_depth)
			{
				// nothing left on stack, so set current context to NULL
				mp_current_context = NULL;
				// update the count of valid contexts
				*mp_current_context_count = m_next_context;
			}
			else
			{
				// get the last context pushed on the stack
				mp_current_context = mpp_current_context_stack[--m_context_stack_depth]; 
			}
		}
	}
}

/******************************************************************/

void Profiler::sSetUp( uint vblanks )
{
	
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().ProfilerHeap());
	
	s_width = 640 * 0.8;
	s_height = 448 * 0.01;
	s_left_x = 640 * 0.1;
	s_vblanks = vblanks;
	s_scale = ( s_width * 60 ) / ( 1000000 * s_vblanks );

	VUProfiler  =  new Profiler( 84 );
	DMAProfiler =  new Profiler( 86 );
	CPUProfiler =  new Profiler( 88 );
	
#ifdef	__PLAT_NGC__	
	s_active = true;
	s_enabled = true;
#else
	s_active = false;
	s_enabled = false;
#endif
	
	Mem::Manager::sHandle().PopContext();
	
}

/******************************************************************/
    
void Profiler::sCloseDown( void )
{
	delete VUProfiler;
	delete DMAProfiler;
	delete CPUProfiler;
	VUProfiler = NULL;
	DMAProfiler = NULL;
	CPUProfiler = NULL;
}

/******************************************************************/
    
void Profiler::sRender( void )
{
	if (s_active && s_enabled)
	{
#ifdef	__PLAT_NGC__	
		NsCamera cam;
		cam.orthographic( 0, 0, 640, 448 );
		cam.begin();
		GX::SetZMode( GX_FALSE, GX_ALWAYS, GX_TRUE );

		GX::SetTexChanTevIndCull( 0, 1, 1, 0, GX_CULL_NONE );
		GX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR0A0, GX_TEXCOORD0, GX_TEXMAP_NULL, GX_COLOR_NULL);

		GX::SetTevAlphaInOpSwap( GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_ZERO, GX_CA_ZERO, GX_CA_RASA,
											   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV,
											   GX_TEV_SWAP0, GX_TEV_SWAP0 );

		GX::SetTevColorInOp( GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_ZERO, GX_CC_ZERO, GX_CC_RASC,
										   GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_ENABLE, GX_TEVPREV );

		GX::SetBlendMode ( GX_BM_NONE, GX_BL_ONE, GX_BL_ONE, GX_LO_CLEAR, GX_TRUE, GX_FALSE, GX_FALSE );
		GX::SetChanCtrl( GX_COLOR0A0, GX_ENABLE, GX_SRC_REG, GX_SRC_REG, GX_LIGHT_NULL, GX_DF_NONE, GX_AF_NONE );
		GX::SetChanMatColor( GX_COLOR0A0, (GXColor){255,255,255,255} );

		GX::SetVtxDesc( 1, GX_VA_POS, GX_DIRECT );
#endif	// __PLAT_NGC__	

		for (uint x = 0; xrender();
		DMAProfiler->render();
		CPUProfiler->render();
	
#ifdef	__PLAT_NGC__	
		cam.end();
#endif	// __PLAT_NGC__	
	}
}

/******************************************************************/

void Profiler::sStartFrame( void )
{
	

	s_active = s_enabled;
	s_flip ^= 1;
	s_base_time[ s_flip ] = Tmr::GetTimeInUSeconds();
	CPUProfiler->start_new_frame();
	VUProfiler->start_new_frame();
	DMAProfiler->start_new_frame();
}
/******************************************************************/

void Profiler::sEndFrame( void )
{

}

/******************************************************************/

void Profiler::sEnable( void )
{
	s_enabled = true;
}


/******************************************************************/

void Profiler::sDisable( void )
{
	s_enabled = false;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void		Render_Profiler()			// easy function to allow it to be rendered from simpler code 
{
	Profiler::sRender();
}
    
} // namespace Sys
				  

#endif	//		__USE_PROFILER__




================================================
FILE: Code/Sys/Profiler.h
================================================

/*****************************************************************************
**																			**
**					   	  Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 2000 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SysLib (System Library)									**
**																			**
**	Module:			Sys  													**
**																			**
**	File name:		sys/profiler.h											**
**																			**
**	Created: 		11/15/00	-	mjb										**
**																			**
*****************************************************************************/

#ifndef __SYS_PROFILER_H
#define __SYS_PROFILER_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif
#include 
#include 

#ifndef	__SYS_MEM_MEMPTR_H
#include 
#endif
/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#ifndef __PLAT_WN32__

#ifndef		__NOPT_FINAL__
#ifdef		__PLAT_NGPS__	
#define		__USE_PROFILER__			// use this one to turn profiling on/off
#endif		//	__PLAT_NGPS__	


#define	PROF_MAX_LEVELS		16		 			// depth of profiler stack
#define	PROF_MAX_CONTEXTS	512				// number of push/pops possible

namespace Sys
{


void		Render_Profiler();			// easy function to allow it to be rendered from simpler code 

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

// a profiler context represtens a single atomic measurement of time
// a simgle block of color on the profiler bar, at a particular level
struct  SProfileContext 
{
	uint32							m_color;				// color of this context
	int								m_depth;				// level in the stack
	Tmr::MicroSeconds				m_start_time;			// when it started
	Tmr::MicroSeconds				m_end_time;				// when it ended
};


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

class  Profiler  : public Spt::Class
{
	

public:


#ifdef		__USE_PROFILER__
	void						PushContext( uint8 r, uint8 g, uint8 b, bool acc = false );
	void						PushContext( uint32 rgba, bool acc = false);
	void						PopContext( void );
		
	static void					sSetUp( uint vblanks );
	static void					sCloseDown( void );
	static void					sRender( void );

	static void					sEnable( void );
	static void					sDisable( void );
	static void					sStartFrame( void );
	static void					sEndFrame( void );
#else
	inline void					PushContext( uint8 r,
											 uint8 g,
											 uint8 b,
											 bool acc ){}
	inline void					PushContext( uint32 rgba,
											 bool acc ){}
	inline void					PopContext( void ){}
		
	inline  static void						sSetUp( uint vblanks ){}
	inline  static  void					sCloseDown( void ){}
	inline  static  void					sRender( void ){}

	inline  static  void					sEnable( void ){}
	inline  static  void					sDisable( void ){}
	inline  static  void					sStartFrame( void ){}
	inline  static  void					sEndFrame( void ){}
#endif	

								~Profiler( void );		 

private:
#ifdef		__USE_PROFILER__
								Profiler( uint line );
								
	void						render( void );
	void						start_new_frame( void );
	SProfileContext				m_context_list[ 2 ][PROF_MAX_CONTEXTS];		// a flat array of all contexts
	SProfileContext*			mp_current_context_list;					// pointer to one of the above, for speed
	
	SProfileContext	*			m_context_stack[ 2 ][PROF_MAX_LEVELS];		// stack of pushed contexts (usually max 3 deep)
	SProfileContext**			mpp_current_context_stack;					// pointer to one of the above, for speed
	
	int							m_context_count[2];
	int	*						mp_current_context_count;
	
	SProfileContext*			mp_current_context;							// current entry in context list (will need to pop back....
	int							m_context_stack_depth;						// depth in stack (index into m_context_stack[])
	int							m_next_context;								// index into array
	
	float						m_top_y;								// screen coord of top of profiler
	
	static Tmr::MicroSeconds	s_base_time[2];
	static int					s_flip;
	static bool					s_enabled;
	static bool					s_active;


	static uint					s_vblanks;
	static float				s_width;
	static float				s_height;
	static float 				s_scale;
	
	static float				s_left_x;

	static uint32				s_default_color;
	static Profiler*			s_cpu_profiler;

	bool						m_acc;					// Accumulation?
	bool						m_acc_first;
	Tmr::MicroSeconds			m_acc_time;				// Accumulation time.
	SProfileContext*			mp_acc_context;
#endif
};

/*****************************s************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

extern Profiler*	CPUProfiler;
extern Profiler*	VUProfiler;
extern Profiler*	DMAProfiler;

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/


} // namespace Sys

#endif
#endif	// __PLAT_WN32__
#endif // __SYS_PROFILER_H



================================================
FILE: Code/Sys/Replay/NGC/p_replay.cpp
================================================
#if 0
#include 
#include 
#include 

#define MAX_BUFFER (20*1024)
#define MAX_FILL_BUFFER (8*1024)

namespace Replay
{

// Must be a multiple of REPLAY_BUFFER_CHUNK_SIZE
#define REPLAY_BUFFER_SIZE 1048576
//#define REPLAY_BUFFER_SIZE ( REPLAY_BUFFER_CHUNK_SIZE * 96 )
//#define REPLAY_BUFFER_SIZE (20480*4)

static uint32 sp_buffer=0;

// Allocates the big buffer for storing replays. Never gets deallocated. (Currently)
void AllocateBuffer()
{
	if (sp_buffer)
	{
		return;
	}	
	
//	sp_buffer=(uint8*)Mem::Malloc(REPLAY_BUFFER_SIZE);
	sp_buffer = NsARAM::alloc( REPLAY_BUFFER_SIZE );
	Dbg_MsgAssert(sp_buffer,("Could not allocate replay buffer"));
}

void DeallocateBuffer()
{
	if (sp_buffer)
	{
		NsARAM::free( sp_buffer );
		sp_buffer=0;
	}
}

uint32 GetBufferSize()
{
	return REPLAY_BUFFER_SIZE;
}

bool BufferAllocated()
{
	if (sp_buffer)
	{
		return true;
	}
	return false;
}
	
// Reads numBytes out of the buffer starting at offset bufferOffset, into p_dest.
// Asserts if there are not enough source bytes.
void ReadFromBuffer(uint8 *p_dest, int bufferOffset, int numBytes)
{
//	OSReport( "READ: 0x%08x - %d bytes\n", bufferOffset, numBytes );
	Dbg_MsgAssert(sp_buffer,("Replay buffer has not been allocated"));
	Dbg_MsgAssert(p_dest,("NULL p_dest sent to Replay::Read()"));
	Dbg_MsgAssert(bufferOffset>=0 && bufferOffset MAX_BUFFER )
	{
		OSReport( "Transfer too large: %d - max = %d\n", aligned_size, MAX_BUFFER );
		while (1 == 1);
	}

	NsDMA::toMRAM( buffer, sp_buffer+aligned_off, aligned_size );
	memcpy( p_dest, &buffer[bufferOffset-aligned_off], numBytes );
}

// Writes numBytes into the buffer. Asserts if there is not enough space.
void WriteIntoBuffer(uint8 *p_source, int bufferOffset, int numBytes)
{
//	OSReport( "WRITE: 0x%08x - %d bytes\n", bufferOffset, numBytes );

	Dbg_MsgAssert(sp_buffer,("Replay buffer has not been allocated"));
	Dbg_MsgAssert(p_source,("NULL p_source sent to Replay::Write()"));
	Dbg_MsgAssert(bufferOffset>=0 && bufferOffset MAX_BUFFER )
	{
		OSReport( "Transfer too large: %d - max = %d\n", aligned_size, MAX_BUFFER );
		while (1 == 1);
	}

	NsDMA::toMRAM( buffer, sp_buffer+aligned_off, aligned_size );
	memcpy( &buffer[bufferOffset-aligned_off], p_source, numBytes );
	NsDMA::toARAM( sp_buffer+aligned_off, buffer, aligned_size );
}

void FillBuffer(int bufferOffset, int numBytes, uint8 value)
{
//	OSReport( "FILL: 0x%08x - %d bytes, 0x%02x\n", bufferOffset, numBytes, value );
	Dbg_MsgAssert(sp_buffer,("Replay buffer has not been allocated"));
	Dbg_MsgAssert(bufferOffset>=0 && bufferOffset MAX_FILL_BUFFER )
//		{
//			NsDMA::toARAM( sp_buffer+bufferOffset+off, buffer, MAX_FILL_BUFFER );
//			size -= MAX_FILL_BUFFER;
//			off += MAX_FILL_BUFFER;
//		}
//	}
//	else
	{
		while ( size > MAX_FILL_BUFFER )
		{
			WriteIntoBuffer( buffer, bufferOffset+off, MAX_FILL_BUFFER );
			size -= MAX_FILL_BUFFER;
			off += MAX_FILL_BUFFER;
		}
		if ( size ) WriteIntoBuffer( buffer, bufferOffset+off, size ); 
	}

}

}  // namespace Replay

#endif


================================================
FILE: Code/Sys/Replay/NGPS/p_replay.cpp
================================================
#include 
#if 0
namespace Replay
{

// Must be a multiple of REPLAY_BUFFER_CHUNK_SIZE
//#define REPLAY_BUFFER_SIZE 1048576
#define REPLAY_BUFFER_SIZE 200704			// Mick - changed back to this, as the old levels are running out of memory
//#define REPLAY_BUFFER_SIZE (20480*4)

static uint8 *sp_buffer=NULL;

// Allocates the big buffer for storing replays. Never gets deallocated. (Currently)
void AllocateBuffer()
{
	if (sp_buffer)
	{
		return;
	}	
	
	sp_buffer=(uint8*)Mem::Malloc(REPLAY_BUFFER_SIZE);
	Dbg_MsgAssert(sp_buffer,("Could not allocate replay buffer"));
}

void DeallocateBuffer()
{
	if (sp_buffer)
	{
		Mem::Free(sp_buffer);
		sp_buffer=NULL;
	}
}

uint32 GetBufferSize()
{
	return REPLAY_BUFFER_SIZE;
}

bool BufferAllocated()
{
	if (sp_buffer)
	{
		return true;
	}
	return false;
}
	
// Reads numBytes out of the buffer starting at offset bufferOffset, into p_dest.
// Asserts if there are not enough source bytes.
void ReadFromBuffer(uint8 *p_dest, int bufferOffset, int numBytes)
{
	Dbg_MsgAssert(sp_buffer,("Replay buffer has not been allocated"));
	Dbg_MsgAssert(p_dest,("NULL p_dest sent to Replay::Read()"));
	Dbg_MsgAssert(bufferOffset>=0 && bufferOffset=0 && bufferOffset=0 && bufferOffset

namespace Replay
{

// Must be a multiple of REPLAY_BUFFER_CHUNK_SIZE
#define REPLAY_BUFFER_SIZE 200704

static uint8 *sp_buffer=NULL;

// Allocates the big buffer for storing replays. Never gets deallocated. (Currently)
void AllocateBuffer()
{
	if (sp_buffer)
	{
		return;
	}	
	
	sp_buffer=(uint8*)Mem::Malloc(REPLAY_BUFFER_SIZE);
	Dbg_MsgAssert(sp_buffer,("Could not allocate replay buffer"));
}

void DeallocateBuffer()
{
	if (sp_buffer)
	{
		Mem::Free(sp_buffer);
		sp_buffer=NULL;
	}
}

uint32 GetBufferSize()
{
	return REPLAY_BUFFER_SIZE;
}

bool BufferAllocated()
{
	if (sp_buffer)
	{
		return true;
	}
	return false;
}
	
// Reads numBytes out of the buffer starting at offset bufferOffset, into p_dest.
// Asserts if there are not enough source bytes.
void ReadFromBuffer(uint8 *p_dest, int bufferOffset, int numBytes)
{
	Dbg_MsgAssert(sp_buffer,("Replay buffer has not been allocated"));
	Dbg_MsgAssert(p_dest,("NULL p_dest sent to Replay::Read()"));
	Dbg_MsgAssert(bufferOffset>=0 && bufferOffset=0 && bufferOffset=0 && bufferOffset

#if 0

namespace Replay
{

// Must be a multiple of REPLAY_BUFFER_CHUNK_SIZE
#define REPLAY_BUFFER_SIZE 1048576

static uint8 *sp_buffer=NULL;

// Allocates the big buffer for storing replays. Never gets deallocated. (Currently)
void AllocateBuffer()
{
	if (sp_buffer)
	{
		return;
	}	
	
	sp_buffer=(uint8*)Mem::Malloc(REPLAY_BUFFER_SIZE);
	Dbg_MsgAssert(sp_buffer,("Could not allocate replay buffer"));
}

void DeallocateBuffer()
{
	if (sp_buffer)
	{
		Mem::Free(sp_buffer);
		sp_buffer=NULL;
	}
}

uint32 GetBufferSize()
{
	return REPLAY_BUFFER_SIZE;
}

bool BufferAllocated()
{
	if (sp_buffer)
	{
		return true;
	}
	return false;
}
	
// Reads numBytes out of the buffer starting at offset bufferOffset, into p_dest.
// Asserts if there are not enough source bytes.
void ReadFromBuffer(uint8 *p_dest, int bufferOffset, int numBytes)
{
	Dbg_MsgAssert(sp_buffer,("Replay buffer has not been allocated"));
	Dbg_MsgAssert(p_dest,("NULL p_dest sent to Replay::Read()"));
	Dbg_MsgAssert(bufferOffset>=0 && bufferOffset=0 && bufferOffset=0 && bufferOffset

#if __USE_REPLAYS__

#include 

#include 

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// disabling replays while we refactor
#define DISABLE_REPLAYS
//#define CHECK_TOKEN_USAGE

namespace CFuncs
{
	bool CheckButton(Inp::Handler< Mdl::FrontEnd >* pHandler, uint32 button);
}	

namespace Replay
{

DefineSingletonClass( Manager, "Replay Manager" )

// These are hard coded rather than wasting space in the replay data. It would waste quite
// a bit of space since there are lots of hovering cash icons. They all appear to use the same
// parameters anyway.
#define HOVER_ROTATE_SPEED 250.0f
#define HOVER_AMPLITUDE 10.0f
#define HOVER_PERIOD 1000
#define HOVER_PERIOD_RND 100

static uint32 sLevelStructureName=0;

static uint32 sNextPlaybackFrameOffset=0;
static bool sBufferFilling=true;
static bool sReachedEndOfReplay=false;
static bool sReplayPaused=false;
static bool sNeedToInitialiseVibration=false;
static bool sTrickTextGotCleared=false;

static uint32 sBigBufferStartOffset=0;
static uint32 sBigBufferEndOffset=0;

// This is a small buffer where the replay info for one frame is stored.
// It is then appended to the big cyclic buffer by sWriteFrameBufferToBigBuffer
#define FRAME_BUFFER_SIZE 20480
static uint8 spFrameBuffer[FRAME_BUFFER_SIZE];
static uint8 *spFrameBufferPos=NULL;

enum EReplayMode
{
	NONE,
	RECORD,
	PLAYBACK
};
static EReplayMode sMode=NONE;
	
enum EPointerType
{
	UNDEFINED,
	CMOVINGOBJECT,
	CSCREENELEMENT,
};

enum ESpecialIDs
{
	ID_SKATER=0,
	ID_CAMERA=1,
};
	
enum EAnimStartEndType
{
	START_TO_END,
	END_TO_START,
	SPECIFIC_START_END_TIMES
};

enum EReplayToken
{
	BLANK=0,
	FRAME_START,
	CREATE_OBJECT,
	KILL_OBJECT,
	OBJECT_ID,
	MODEL_NAME,
	SKELETON_NAME,
	PROFILE_NAME,
	SECTOR_NAME,
	ANIM_SCRIPT_NAME,
	SET_SCALE,
	SET_POSITION,
	SET_POSITION_ANGLES,	
	SET_VELOCITY,
	SET_ANGLE_X,
	SET_ANGLE_Y,
	SET_ANGLE_Z,
	SET_ANGULAR_VELOCITY_X,
	SET_ANGULAR_VELOCITY_Y,
	SET_ANGULAR_VELOCITY_Z,
	
	PRIMARY_ANIM_CONTROLLER_CHANGES,
	DEGENERATE_ANIM_CONTROLLER_CHANGES,
	
	PLAY_POSITIONAL_SOUND_EFFECT,
	PLAY_SKATER_SOUND_EFFECT,
	PLAY_SOUND,
	
	PLAY_LOOPING_SOUND,
	STOP_LOOPING_SOUND,
	PITCH_MIN,
	PITCH_MAX,
	
	SCREEN_BLUR,
	SCREEN_FLASH,
	
	SPARKS_ON,
	SPARKS_OFF,
	
	TRICK_TEXT,
	TRICK_TEXT_PULSE,
	TRICK_TEXT_COUNTDOWN,
	TRICK_TEXT_LANDED,
	TRICK_TEXT_BAIL,
	
	SCORE_POT_TEXT,
	
	PANEL_MESSAGE,
	
	SET_ATOMIC_STATES,
		
	MANUAL_METER_ON,
	MANUAL_METER_OFF,
	BALANCE_METER_ON,
	BALANCE_METER_OFF,
	
	FLIP,
	UNFLIP,
	
	MODEL_ACTIVE,
	MODEL_INACTIVE,
	
	PAD_VIBRATION,
	PLAY_POSITIONAL_STREAM,
	PLAY_STREAM,
	STOP_STREAM,
	
	SET_CAR_ROTATION_X,
	SET_CAR_ROTATION_X_VEL,
	SET_WHEEL_ROTATION_X,
	SET_WHEEL_ROTATION_X_VEL,
	SET_WHEEL_ROTATION_Y,
	SET_WHEEL_ROTATION_Y_VEL,
	
	HOVERING,
	
	SECTOR_ACTIVE,
	SECTOR_INACTIVE,

	SECTOR_VISIBLE,
	SECTOR_INVISIBLE,
	
	SHATTER_PARAMS,
	SHATTER_ON,
	SHATTER_OFF,

	TEXTURE_SPLAT,
	
	SCRIPT_CALL,
	SKATER_SCRIPT_CALL,
	
	PAUSE_SKATER,
	UNPAUSE_SKATER,
		
	// Keep the total number of tokens less than 256, cos they're stored in a byte.
	NUM_REPLAY_TOKEN_TYPES
};

class CPosTracker
{
	float m_calculated_last;
	float m_vel;
	float m_actual_last;
	
public:
	CPosTracker();
	~CPosTracker();
	
	void WriteChanges(float newVal, EReplayToken setToken, EReplayToken velToken, float tolerance=0.005f);
	float GetActualLast() {return m_actual_last;}
};

class CSkaterTrackingInfo
{
public:
	CSkaterTrackingInfo();
	~CSkaterTrackingInfo();

	void Reset();

    enum
    {
        NUM_DEGENERATE_ANIMS = 3
    };
    Gfx::CAnimChannel m_degenerateControllers[NUM_DEGENERATE_ANIMS];
	
	bool m_playing_looping_sound;
	uint32 m_looping_sound_checksum;
	float m_pitch_min;
	float m_pitch_max;
};

CSkaterTrackingInfo::CSkaterTrackingInfo()
{
	Reset();
}

CSkaterTrackingInfo::~CSkaterTrackingInfo()
{
}

void CSkaterTrackingInfo::Reset()
{
	for (int i=0; i
{
public:	
	CTrackingInfo();
	~CTrackingInfo();

	CTrackingInfo *mpNext;
	CTrackingInfo *mpPrevious;
	
	void SetMovingObject(Obj::CMovingObject *p_ob);
	void SetSkaterCamera();

	void RecordPositionAndAngleChanges(Mth::Vector &actual_pos, Mth::Vector &actual_angles, bool exact=false);

	uint32 m_id;
	EPointerType mPointerType;
	Obj::CSmtPtr mpMovingObject;
		
	uint32 mFlags;
	float mScale;
	
	Mth::Vector mReplayVel;
	Mth::Vector mReplayLastPos;
	Mth::Vector mActualLastPos;

	CPosTracker mTrackerAngleX;
	CPosTracker mTrackerAngleY;
	CPosTracker mTrackerAngleZ;

	Mth::Matrix mLastMatrix;
		
	CPosTracker mTrackerCarRotationX;
	CPosTracker mTrackerWheelRotationX;
	CPosTracker mTrackerWheelRotationY;

	Gfx::CAnimChannel m_primaryController;
	
	// Made this static because only the skater needs these members, and there is only one skater.
	// Need to keep the CTrackingInfo class as small as possible because a pool of 300 of them exists.
	static CSkaterTrackingInfo sSkaterTrackingInfo;
	
	static int sScreenBlurTracker;
	
};	
CSkaterTrackingInfo CTrackingInfo::sSkaterTrackingInfo;
int CTrackingInfo::sScreenBlurTracker=0;


static CTrackingInfo *spTrackingInfoHead=NULL;
static Lst::HashTable sTrackingInfoHashTable(8);

static CDummy *spStartStateDummies=NULL;
static CDummy *spReplayDummies=NULL;

static Lst::HashTable sObjectStartStateHashTable(8);
static Lst::HashTable sReplayDummysHashTable(8);

static SGlobalStates sStartState;
static SGlobalStates sCurrentState;
}

DefinePoolableClass(Replay::CTrackingInfo);

namespace Replay
{

#define MAX_PANEL_MESSAGE_SIZE 200

static void sDeleteObjectTrackingInfo();
static Lst::HashTable *sGetDummyHashTable(EDummyList whichList);
static CDummy *sGetDummyListHeadPointer(EDummyList whichList);
static void sSetDummyListHeadPointer(EDummyList whichList, CDummy *p_dummy);
static int sCountStartStateDummies();
static void sStopReplayDummyLoopingSounds();
static uint32 sReadAnimControllerChanges(uint32 offset, Gfx::CAnimChannel *p_anim_controller);
static uint32 sReadFrame(uint32 offset, bool *p_nothingFollows, EDummyList whichList);
static void sMakeEnoughSpace(uint32 frameSize);
static void sWriteFrameBufferToBigBuffer();
static void sWriteSingleToken(EReplayToken token);
static void sWriteUint8(uint8 v);
static void sWriteUint32(EReplayToken token, uint32 v);
static void sWriteUint32(uint32 v);
static void sWriteFloat(EReplayToken token, float f);
static void sWriteFloat(float f);
static void sWriteVector(EReplayToken token, Mth::Vector &v);
//static void sWriteString(EReplayToken token, const char *p_string);
//static uint8 sWriteCAnimChannelChanges(const Gfx::CAnimChannel *p_a, const Gfx::CAnimChannel *p_b);
static bool sIsFlipped(Obj::CMovingObject *p_movingObject);
static void sRecordSkaterCamera(CTrackingInfo *p_info);
static void sRecordCMovingObject(CTrackingInfo *p_info);
static void sRecord();
static void sPlayback(bool display=true);
static void sEnsureTrackerExists( Obj::CObject *p_ob, void *p_data );
static void sEnsureCameraTrackerExists();
static void sHideForReplayPlayback( Obj::CObject *p_ob, void *p_data );
static void sRestoreAfterReplayPlayback( Obj::CObject *p_ob, void *p_data );
static void sDeleteDummies(EDummyList whichList);
static void sUnpauseCertainScreenElements();
static void sSwitchOffVibrations();
static void sClearLastPanelMessage();
static void sClearTrickAndScoreText();
static Obj::CSkater *sGetSkater();
static Mdl::Score *sGetSkaterScoreObject();
#ifdef CHECK_TOKEN_USAGE
static const char *sGetTokenName(EReplayToken token);
#endif

SGlobalStates::SGlobalStates()
{
	Reset();
}

SGlobalStates::~SGlobalStates()
{
}

void SGlobalStates::Reset()
{
	mBalanceMeterStatus=false;
	mBalanceMeterValue=0.0f;

	mManualMeterStatus=false;
	mManualMeterValue=0.0f;
	
	int i;
	for (i=0; i tolerance)
	{
		m_calculated_last=newVal;
		sWriteFloat(setToken,newVal);
		
		m_vel=newVal-m_actual_last;
		sWriteFloat(velToken,m_vel);
	}
	else
	{
		m_calculated_last=predicted;
	}	
    m_actual_last=newVal;
}

CTrackingInfo::CTrackingInfo()
{
	m_id=0xffffffff;		
	mPointerType=UNDEFINED;
	mpMovingObject=NULL;
	mFlags=0;
	mScale=1.0f;
			
	mReplayVel.Set();
	mReplayLastPos.Set();
	mActualLastPos.Set();

	// Making sure this won't match the object's matrix on the first frame so that
	// changes will get recorded for the first frame.
	mLastMatrix[0][0]=0.0f;	

	m_primaryController.Reset();
	
	mpNext=spTrackingInfoHead;
	mpPrevious=NULL;
	if (mpNext)
	{
		mpNext->mpPrevious=this;
	}
	spTrackingInfoHead=this;
}

CTrackingInfo::~CTrackingInfo()
{
	if (mpPrevious) mpPrevious->mpNext=mpNext;
	if (mpNext) mpNext->mpPrevious=mpPrevious;
	if (!mpPrevious) spTrackingInfoHead=mpNext;
	
	sTrackingInfoHashTable.FlushItem((uint32)m_id);
}

void CTrackingInfo::SetMovingObject(Obj::CMovingObject *p_ob)
{
	mPointerType=CMOVINGOBJECT;
	mpMovingObject=p_ob;
	m_id=p_ob->GetID();
	
	// Use p_ob as the hash key so that the CTrackingInfo for any given CMovingObject
	// can be quickly looked up.
	sTrackingInfoHashTable.PutItem((uint32)m_id,this);
}	

void CTrackingInfo::SetSkaterCamera()
{
	Dbg_MsgAssert(mPointerType==UNDEFINED,("Expected mPointerType to be UNDEFINED"));
	Dbg_MsgAssert(mpMovingObject==NULL,("Expected mpMovingObject to be NULL"));
	mpMovingObject=NULL;
	
	m_id=ID_CAMERA;
	sTrackingInfoHashTable.PutItem((uint32)m_id,this);
}

void CTrackingInfo::RecordPositionAndAngleChanges(Mth::Vector &actual_pos, Mth::Vector &actual_angles, bool exact)
{
	if (exact)
	{
		sWriteSingleToken(SET_POSITION_ANGLES);
		sWriteFloat(actual_pos[X]);
		sWriteFloat(actual_pos[Y]);
		sWriteFloat(actual_pos[Z]);
		sWriteFloat(actual_angles[X]);
		sWriteFloat(actual_angles[Y]);
		sWriteFloat(actual_angles[Z]);
		return;
	}
		
	// Compare the actual position with that predicted by the last
	// position and velocity
	Mth::Vector predicted_pos=mReplayLastPos+mReplayVel;
	Mth::Vector d=actual_pos-predicted_pos;
	
	if (fabs(d[X]) > 0.1f || fabs(d[Y]) > 0.1f || fabs(d[Z]) > 0.1f)
	{
		mReplayLastPos=actual_pos;
		sWriteVector(SET_POSITION,actual_pos);

		
		mReplayVel=actual_pos-mActualLastPos;
		sWriteVector(SET_VELOCITY,mReplayVel);
	}
	else
	{
		mReplayLastPos=predicted_pos;
	}	
    mActualLastPos=actual_pos;


	// Do the angles.
	mTrackerAngleX.WriteChanges(actual_angles[X],SET_ANGLE_X,SET_ANGULAR_VELOCITY_X);
	mTrackerAngleY.WriteChanges(actual_angles[Y],SET_ANGLE_Y,SET_ANGULAR_VELOCITY_Y);
	mTrackerAngleZ.WriteChanges(actual_angles[Z],SET_ANGLE_Z,SET_ANGULAR_VELOCITY_Z);
}

static void sDeleteObjectTrackingInfo()
{
	CTrackingInfo *p_info=spTrackingInfoHead;
	while (p_info)
	{
		CTrackingInfo *p_next=p_info->mpNext;
		delete p_info;
		p_info=p_next;
	}	
}

static Lst::HashTable *sGetDummyHashTable(EDummyList whichList)
{
	if (whichList==START_STATE_DUMMY_LIST)
	{
		return &sObjectStartStateHashTable;
	}
	else
	{
		return &sReplayDummysHashTable;
	}
}
		
CDummy *GetFirstStartStateDummy()
{
	return spStartStateDummies;
}
	
static CDummy *sGetDummyListHeadPointer(EDummyList whichList)
{
	if (whichList==START_STATE_DUMMY_LIST)
	{
		return spStartStateDummies;
	}
	else	
	{
		return spReplayDummies;
	}
}

static void sSetDummyListHeadPointer(EDummyList whichList, CDummy *p_dummy)
{
	if (whichList==START_STATE_DUMMY_LIST)
	{
		spStartStateDummies=p_dummy;
	}
	else
	{
		spReplayDummies=p_dummy;
	}
}
		
static int sCountStartStateDummies()
{
	int n=0;
	CDummy *p_dummy=spStartStateDummies;
	while (p_dummy)
	{
		++n;
		p_dummy=p_dummy->mpNext;
	}
	return n;	
}

#ifdef CHECK_TOKEN_USAGE
static const char *sGetTokenName(EReplayToken token)
{
	switch (token)
	{
	case BLANK                               :return "BLANK"; break;
	case FRAME_START                         :return "FRAME_START"; break;                       
	case CREATE_OBJECT                       :return "CREATE_OBJECT"; break;                     
	case KILL_OBJECT                         :return "KILL_OBJECT"; break;                       
	case OBJECT_ID                           :return "OBJECT_ID"; break;                         
	case MODEL_NAME                          :return "MODEL_NAME"; break;                        
	case SKELETON_NAME                       :return "SKELETON_NAME"; break;                     
	case PROFILE_NAME                        :return "PROFILE_NAME"; break;                      
	case SECTOR_NAME                         :return "SECTOR_NAME"; break;                       
	case ANIM_SCRIPT_NAME                    :return "ANIM_SCRIPT_NAME"; break;                  
	case SET_POSITION                        :return "SET_POSITION"; break;                      
	case SET_POSITION_ANGLES	             :return "SET_POSITION_ANGLES"; break;               
	case SET_VELOCITY                        :return "SET_VELOCITY"; break;                      
	case SET_ANGLE_X                         :return "SET_ANGLE_X"; break;                       
	case SET_ANGLE_Y                         :return "SET_ANGLE_Y"; break;                       
	case SET_ANGLE_Z                         :return "SET_ANGLE_Z"; break;                       
	case SET_ANGULAR_VELOCITY_X              :return "SET_ANGULAR_VELOCITY_X"; break;            
	case SET_ANGULAR_VELOCITY_Y              :return "SET_ANGULAR_VELOCITY_Y"; break;            
	case SET_ANGULAR_VELOCITY_Z              :return "SET_ANGULAR_VELOCITY_Z"; break;            
	case PRIMARY_ANIM_CONTROLLER_CHANGES     :return "PRIMARY_ANIM_CONTROLLER_CHANGES"; break;   
	case DEGENERATE_ANIM_CONTROLLER_CHANGES  :return "DEGENERATE_ANIM_CONTROLLER_CHANGES"; break;
	case PLAY_POSITIONAL_SOUND_EFFECT        :return "PLAY_POSITIONAL_SOUND_EFFECT"; break;      
	case PLAY_SKATER_SOUND_EFFECT            :return "PLAY_SKATER_SOUND_EFFECT"; break;          
	case PLAY_SOUND							 :return "PLAY_SOUND"; break;
	case PLAY_LOOPING_SOUND					 :return "PLAY_LOOPING_SOUND"; break;
	case STOP_LOOPING_SOUND					 :return "STOP_LOOPING_SOUND"; break;
	case PITCH_MIN							 :return "PITCH_MIN"; break;
	case PITCH_MAX							 :return "PITCH_MAX"; break;
	case SCREEN_BLUR                         :return "SCREEN_BLUR"; break;                       
	case SCREEN_FLASH						 :return "SCREEN_FLASH"; break;
	case SPARKS_ON                           :return "SPARKS_ON"; break;                         
	case SPARKS_OFF                          :return "SPARKS_OFF"; break;                        
	case TRICK_TEXT                          :return "TRICK_TEXT"; break;                        
	case TRICK_TEXT_PULSE                    :return "TRICK_TEXT_PULSE"; break;                  
	case TRICK_TEXT_COUNTDOWN                :return "TRICK_TEXT_COUNTDOWN"; break;              
	case TRICK_TEXT_LANDED                   :return "TRICK_TEXT_LANDED"; break;                 
	case TRICK_TEXT_BAIL                     :return "TRICK_TEXT_BAIL"; break;                   
	case SCORE_POT_TEXT                      :return "SCORE_POT_TEXT"; break;                    
	case PANEL_MESSAGE                       :return "PANEL_MESSAGE"; break;                     
	case SET_ATOMIC_STATES                   :return "SET_ATOMIC_STATES"; break;                  
	case MANUAL_METER_ON                     :return "MANUAL_METER_ON"; break;                   
	case MANUAL_METER_OFF                    :return "MANUAL_METER_OFF"; break;                  
	case BALANCE_METER_ON                    :return "BALANCE_METER_ON"; break;                  
	case BALANCE_METER_OFF                   :return "BALANCE_METER_OFF"; break;                 
	case FLIP                                :return "FLIP"; break;                              
	case UNFLIP                              :return "UNFLIP"; break;                            
	case MODEL_ACTIVE                        :return "MODEL_ACTIVE"; break;                              
	case MODEL_INACTIVE                      :return "MODEL_INACTIVE"; break;                              
	case PAD_VIBRATION                       :return "PAD_VIBRATION"; break;                     
	case PLAY_POSITIONAL_STREAM              :return "PLAY_POSITIONAL_STREAM"; break;            
	case PLAY_STREAM                         :return "PLAY_STREAM"; break;                       
	case STOP_STREAM                         :return "STOP_STREAM"; break;                       
	case SET_CAR_ROTATION_X                  :return "SET_CAR_ROTATION_X"; break;                
	case SET_CAR_ROTATION_X_VEL              :return "SET_CAR_ROTATION_X_VEL"; break;            
	case SET_WHEEL_ROTATION_X                :return "SET_WHEEL_ROTATION_X"; break;              
	case SET_WHEEL_ROTATION_X_VEL            :return "SET_WHEEL_ROTATION_X_VEL"; break;          
	case SET_WHEEL_ROTATION_Y                :return "SET_WHEEL_ROTATION_Y"; break;              
	case SET_WHEEL_ROTATION_Y_VEL            :return "SET_WHEEL_ROTATION_Y_VEL"; break;          
	case HOVERING							 :return "HOVERING"; break;          
	case SECTOR_ACTIVE						 :return "SECTOR_ACTIVE"; break;          
	case SECTOR_INACTIVE					 :return "SECTOR_INACTIVE"; break;          
	case SECTOR_VISIBLE						 :return "SECTOR_VISIBLE"; break;          
	case SECTOR_INVISIBLE					 :return "SECTOR_INVISIBLE"; break;          
	case SHATTER_PARAMS					 	 :return "SHATTER_PARAMS"; break;          
	case SHATTER_ON					 		 :return "SHATTER_ON"; break;          
	case SHATTER_OFF				 		 :return "SHATTER_OFF"; break;          
	case TEXTURE_SPLAT						 :return "TEXTURE_SPLAT"; break;
	case SCRIPT_CALL						 :return "SCRIPT_CALL"; break;
	case SKATER_SCRIPT_CALL					 :return "SKATER_SCRIPT_CALL"; break;
	case PAUSE_SKATER						 :return "PAUSE_SKATER"; break;
	case UNPAUSE_SKATER						 :return "UNPAUSE_SKATER"; break;
	default									 :return "UNKNOWN"; break;
	}
}
#endif

// When the game is in paused mode, which it will be when running a replay, certain screen
// elements such as the balance meters will not update properly.
// This function will unpause them so that they work during the replay.
static void sUnpauseCertainScreenElements()
{
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	Front::CSpriteElement *p_balance_meter = (Front::CSpriteElement *) p_manager->GetElement(0xa4db8a4b + sGetSkater()->GetHeapIndex()).Convert(); // "the_balance_meter"
	Dbg_MsgAssert(p_balance_meter,("NULL p_balance_meter"));
	Front::CSpriteElement *p_balance_arrow = (Front::CSpriteElement *) p_balance_meter->GetFirstChild().Convert();
	
	p_balance_meter->SetMorphPausedState(false);
	p_balance_arrow->SetMorphPausedState(false);
}

static void sSwitchOffVibrations()
{
	Obj::CSkater *p_skater=sGetSkater();
	for (int i=0; iGetDevice()->ActivateActuator(i,0);
	}	
}

static Obj::CSkater *sGetSkater()
{
	Mdl::Skate * p_skate_mod = Mdl::Skate::Instance();
	Obj::CSkater *p_skater = p_skate_mod->GetSkater(0);
	Dbg_MsgAssert(p_skater,("NULL p_skater"));
	return p_skater;
}
	
static Mdl::Score *sGetSkaterScoreObject()
{
	Mdl::Score *p_score=sGetSkater()->GetScoreObject();
	Dbg_MsgAssert(p_score,("NULL p_score"));
	return p_score;
}

static void sDeleteDummies(EDummyList whichList)
{
	CDummy *p_dummy=sGetDummyListHeadPointer(whichList);
	while (p_dummy)
	{
		CDummy *p_next=p_dummy->mpNext;
		delete p_dummy;
		p_dummy=p_next;
	}	
	sGetDummyHashTable(whichList)->FlushAllItems();
	Dbg_MsgAssert(spReplayDummies==NULL,("Hey! spReplayDummies not NULL !!!"));
}

bool ScriptDeleteDummies( Script::CStruct *pParams, Script::CScript *pScript )
{
	sDeleteDummies(PLAYBACK_DUMMY_LIST);
	return true;
}
	
CDummy::CDummy(EDummyList whichList, uint32 id)
{
	m_list=whichList;
	SetID(id);

	mpNext=sGetDummyListHeadPointer(m_list);
	mpPrevious=NULL;
	if (mpNext)
	{
		mpNext->mpPrevious=this;
	}	
	sSetDummyListHeadPointer(m_list,this);
	sGetDummyHashTable(whichList)->PutItem(m_id,this);
	
	//m_type = SKATE_TYPE_REPLAY_DUMMY;

	mAtomicStates=0xffffffff;

	mFlags=0;
	
	// Add a bit of randomness so that groups of hovering things slowly
	// go out of phase with each other rather than hovering in unison.
	mHoverPeriod=HOVER_PERIOD+Mth::Rnd(2*HOVER_PERIOD_RND+1)-HOVER_PERIOD_RND;
	Dbg_MsgAssert(mHoverPeriod>0,("Bad mHoverPeriod"));
	
	m_car_rotation_x=0.0f;
	m_car_rotation_x_vel=0.0f;
	m_wheel_rotation_x=0.0f;
	m_wheel_rotation_x_vel=0.0f;
	m_wheel_rotation_y=0.0f;
	m_wheel_rotation_y_vel=0.0f;

	mAnimScriptName=0;
	mSectorName=0;
	mScale=1.0f;

	m_looping_sound_id=0;
	m_looping_sound_checksum=0;
	m_pitch_min=50.0f;
	m_pitch_max=120.0f;
			
	m_is_displayed=true;
				
	// Note: CDummy is derived from CMovingObject which is derived from Spt::Class, so we
	// get the autozeroing of the members.
}

CDummy::~CDummy()
{
	if (mp_rendered_model && m_id!=0)
	{
		// Must call sUninitModel rather than just delete it.
		Nx::CEngine::sUninitModel(mp_rendered_model);
    }

    if (mp_skeletonComponent)
    {
        delete mp_skeletonComponent;
		mp_skeletonComponent = NULL;
    }
	
	if (mpPrevious) mpPrevious->mpNext=mpNext;
	if (mpNext) mpNext->mpPrevious=mpPrevious;
	if (mpPrevious==NULL)
	{
		sSetDummyListHeadPointer(m_list,mpNext);
	}	
	
	sGetDummyHashTable(m_list)->FlushItem(m_id);
	
	if (mp_skater_camera)
	{
		delete mp_skater_camera;
//		Obj::CSkater *p_skater=sGetSkater();

		// Replay has finished, so we'd want to switch the camera back to the skater camera
		// can't do it the old way, need to use
		
		printf ("STUBBED:  replay.cpp, line %d  -------- not resetting skater camera\n",__LINE__);											
		//Nx::CViewportManager::sSetCamera( /*m_skater_number*/0, p_skater->GetSkaterCam() );			
	}	
}

CDummy& CDummy::operator=( const CDummy& rhs )
{
#if 0
	Dbg_MsgAssert(strlen(rhs.mpModelName)<=MAX_MODEL_NAME_CHARS,("rhs mpModelName too long ?"));
	strcpy(mpModelName,rhs.mpModelName);
	mSkeletonName=rhs.mSkeletonName;
	mProfileName=rhs.mProfileName;
	mAnimScriptName=rhs.mAnimScriptName;
	mSectorName=rhs.mSectorName;
	mScale=rhs.mScale;
	
	mFlags=rhs.mFlags;
	
	mHoverPeriod=rhs.mHoverPeriod;
	
	// Don't really need to copy m_id and m_pos here, because they are members of
	// CMovingObject so will get copied by the CMovingObject default assignement operator, I think.
	// I feel more comfortable doing it here too though.
	m_id=rhs.m_id;
	m_pos=rhs.m_pos;
	m_type=rhs.m_type;
	
	m_vel=rhs.m_vel;
	m_angles=rhs.m_angles;
	m_ang_vel=rhs.m_ang_vel;

	// This is because for the skater dummy the speed for calculating the looping sound
	// volume is calculated using m_old_pos. If it were left equal to 0,0,0 it would cause the
	// looping sound to be played at max volume for the first frame, because it would think
	// the skater was moving very fast.
	m_old_pos=m_pos;

	m_car_rotation_x		=rhs.m_car_rotation_x;
	m_car_rotation_x_vel    =rhs.m_car_rotation_x_vel;
	m_wheel_rotation_x      =rhs.m_wheel_rotation_x;
	m_wheel_rotation_x_vel  =rhs.m_wheel_rotation_x_vel;
	m_wheel_rotation_y      =rhs.m_wheel_rotation_y;
	m_wheel_rotation_y_vel  =rhs.m_wheel_rotation_y_vel;
	
	m_primaryController=rhs.m_primaryController;
	
	for (int i=0; impModelName,mpModelName);
	
	p_saved_dummy->m_id				=	m_id;
	p_saved_dummy->m_type			=	m_type;
	p_saved_dummy->mSkeletonName	=	mSkeletonName;
	p_saved_dummy->mProfileName		=	mProfileName;
	p_saved_dummy->mAnimScriptName	=	mAnimScriptName;
	p_saved_dummy->mSectorName		=	mSectorName;
	p_saved_dummy->mScale			=	mScale;
	p_saved_dummy->mFlags			=	mFlags;
	// Note: The mHoverPeriod is not saved out. No need, it does not need to be restored exactly,
	// and the CDummy constructor generates a new random value for it.
	p_saved_dummy->m_pos			=	m_pos;
	p_saved_dummy->m_vel			=	m_vel;
	p_saved_dummy->m_angles			=	m_angles;
	p_saved_dummy->m_ang_vel		=	m_ang_vel;
	p_saved_dummy->m_car_rotation_x       = m_car_rotation_x;      
	p_saved_dummy->m_car_rotation_x_vel   = m_car_rotation_x_vel;  
	p_saved_dummy->m_wheel_rotation_x     = m_wheel_rotation_x;    
	p_saved_dummy->m_wheel_rotation_x_vel = m_wheel_rotation_x_vel;
	p_saved_dummy->m_wheel_rotation_y     = m_wheel_rotation_y;    
	p_saved_dummy->m_wheel_rotation_y_vel = m_wheel_rotation_y_vel;
	
	p_saved_dummy->m_primaryController		=	m_primaryController;
	
	for (int i=0; im_degenerateControllers[i]=m_degenerateControllers[i];
	}
	
	p_saved_dummy->mAtomicStates		= mAtomicStates;

	// Note: m_looping_sound_id is not saved, since this is a unique id for the instance of the sound.
	p_saved_dummy->m_looping_sound_checksum = m_looping_sound_checksum;
	p_saved_dummy->m_pitch_min = m_pitch_min;
	p_saved_dummy->m_pitch_max = m_pitch_max;
#endif
}

void CDummy::UpdateLoopingSound()
{
	if (!m_looping_sound_checksum)
	{
		return;
	}
		
	float percent_of_max_speed=100.0f;
	float maxSpeed = 1100.0f;
	Mth::Vector v=m_pos-m_old_pos;
	v[Y]=0.0f;
	float speed = v.Length( ) * 60.0f;
	if ( fabsf( speed ) < maxSpeed )
	{
		percent_of_max_speed=( 100.0f * speed ) / maxSpeed;
	}

	Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
	
	if (!m_looping_sound_id)
	{
		m_looping_sound_id = sfx_manager->PlaySound( m_looping_sound_checksum, 0, 0 );
	}
		
	Sfx::sVolume vol;
	sfx_manager->SetVolumeFromPos( &vol, m_pos, sfx_manager->GetDropoffDist( m_looping_sound_checksum ) );
	
	vol.PercentageAdjustment( percent_of_max_speed );

	float pitch = PERCENT( m_pitch_max - m_pitch_min, percent_of_max_speed );
	pitch += m_pitch_min;
	
	sfx_manager->UpdateLoopingSound(m_looping_sound_id,&vol,pitch);
}		

void CDummy::Update()
{
#if 0
	if (mFlags&DUMMY_FLAG_HOVERING)
	{
		m_angles[Y]+=DEGREES_TO_RADIANS(HOVER_ROTATE_SPEED)/60.0f;
	}
	else
	{
		m_angles+=m_ang_vel;
		m_pos+=m_vel;
	
		if (m_type==SKATE_TYPE_CAR)
		{
			m_car_rotation_x	+= m_car_rotation_x_vel; 
			m_wheel_rotation_x	+= m_wheel_rotation_x_vel;
			m_wheel_rotation_y	+= m_wheel_rotation_y_vel;
		}
	
		if (m_list==PLAYBACK_DUMMY_LIST)
		{
			Mth::Matrix mat(m_angles[X],m_angles[Y],m_angles[Z]);
			Obj::CSkater *p_skater=sGetSkater();
			
			if (m_id==ID_SKATER)
			{
				p_skater->UpdateShadow(m_pos,mat);
				
				// Update the skater's pos and display matrix so that the sparks follow the dummy.
				// The skater's RestoreAfterReplayPlayback function will restore the skater's
				// original position when the replay ends.
				p_skater->SetActualDisplayMatrix(mat);
				p_skater->m_pos=m_pos;
				
				UpdateLoopingSound();
			}	
			else if (m_id==ID_CAMERA)
			{
				if (!mp_skater_camera)
				{
					mp_skater_camera=new Gfx::Camera;
					
					//mp_skater_camera=new Obj::CSkaterCam(0);
					//mp_skater_camera->SetMode( Obj::CSkaterCam::SKATERCAM_MODE_NORMAL_MEDIUM, 0.0f );
					//mp_skater_camera->SetSkater(p_skater);
					
					Nx::CViewportManager::sSetCamera( /*m_skater_number*/0, mp_skater_camera );			
				}		
				
				//mp_skater_camera->Update();
				m_pos[W]=1.0f;
				mp_skater_camera->SetPos(m_pos);
				mp_skater_camera->SetMatrix(mat);
			}
		}
	}	
	
	m_old_pos=m_pos;
#endif
}

void CDummy::CreateModel()
{
#if 0
	Dbg_MsgAssert(mp_rendered_model==NULL,("Called CreateModel() when model already exists"));

	if (m_id==ID_SKATER)
	{
		Obj::CSkater *p_skater=sGetSkater();
		mp_rendered_model=p_skater->GetModel();
		// This switches off any bouncing boobs on Jenna initially.
		Dbg_Assert( p_skater->GetSkeleton() );
		p_skater->GetSkeleton()->SetProceduralBoneTransActive( 0x47c76c69/*breast_cloth_zz*/, 0 );
	}
	else
	{
		if (mpModelName[0]==0 && mProfileName==0 && mSectorName==0)
		{
			m_is_displayed=false;
			return;
		}
	
		mp_rendered_model = Nx::CEngine::sInitModel();
		Dbg_MsgAssert(mp_rendered_model,("sInitModel() returned NULL"));
		Mth::Vector scale(mScale,mScale,mScale);
		mp_rendered_model->SetScale(scale);

		switch (m_type)
		{
			case SKATE_TYPE_CAR:
				if (mSkeletonName)
				{
					this->LoadSkeleton(mSkeletonName);
				}
				if (mpModelName[0])
				{
					mp_rendered_model->AddGeom(mpModelName, 0, true );
				}		
				break;
			case SKATE_TYPE_PED:	
				if (mSkeletonName)
				{
					this->LoadSkeleton(mSkeletonName);
					
					if (mProfileName)
					{
						Gfx::CModelAppearance thePedAppearance;
						thePedAppearance.Load( mProfileName );
						
						Gfx::CModelBuilder theBuilder( true, 0 );
						theBuilder.BuildModel( &thePedAppearance, mp_rendered_model );
					}
					else
					{
						Str::String fullModelName;
						fullModelName = Gfx::GetModelFileName(mpModelName, ".skin");
					
						mp_rendered_model->AddGeom(fullModelName.getString(), 0, true );
					}
				}
				break;
			case SKATE_TYPE_GAME_OBJ:	
			case SKATE_TYPE_BOUNCY_OBJ:	
				if (mSectorName)
				{
					Nx::CSector *p_sector = Nx::CEngine::sGetSector(mSectorName);
					
					if ( p_sector )
					{
						// need to clone the source, not the instance?
						Nx::CGeom* pGeom = p_sector->GetGeom();
						if( pGeom )
						{
							Nx::CGeom* pClonedGeom = pGeom->Clone( true );
							pClonedGeom->SetActive(true);
							mp_rendered_model->AddGeom( pClonedGeom, 0 );
						}
					}	
				}
				else if (mSkeletonName)
				{
					this->LoadSkeleton(mSkeletonName);
					
					Ass::CAssMan*	ass_manager = Ass::CAssMan::Instance();
					ass_manager->SetReferenceChecksum( mAnimScriptName );
					Script::RunScript( mAnimScriptName );
					
					Str::String fullModelName;
					fullModelName = Gfx::GetModelFileName(mpModelName, ".skin");
				
					mp_rendered_model->AddGeom(fullModelName.getString(), 0, true );
				}
				else if (mpModelName[0])
				{
					Str::String fullModelName;
					fullModelName = Gfx::GetModelFileName(mpModelName, ".mdl");
					
					mp_rendered_model->AddGeom(fullModelName.getString(), 0, true );
				}				
				break;
			default:
				break;
		}		
	}	
	
	// Now initialise the atomic states according to the states stored in the dummy.
	Dbg_MsgAssert(mp_rendered_model,("NULL mp_rendered_model"));
	mp_rendered_model->SetGeomActiveMask(mAtomicStates);

	mp_rendered_model->SetActive(mFlags & DUMMY_FLAG_ACTIVE);
	
	// And initialise the flipped status
	Gfx::CSkeleton* p_skeleton=this->GetSkeleton();
	if (m_id==ID_SKATER)
	{
		Obj::CSkater *p_skater=sGetSkater();
		p_skeleton = p_skater->GetSkeleton();
	}
#endif

#if 0
	if (p_skeleton)
	{
		p_skeleton->FlipAnimation( 0, mFlags&DUMMY_FLAG_FLIPPED, 0, false );
	}
#endif
}

void CDummy::DisplayModel()
{
	if (mp_rendered_model && mp_rendered_model->GetActive())
	{
		Mth::Matrix display_matrix(m_angles[X],m_angles[Y],m_angles[Z]);
		display_matrix[Mth::POS] = m_pos;
		display_matrix[Mth::POS][W] = 1.0f;

		bool should_animate=true;
		if (!mSkeletonName)
		{
			should_animate=false;
		}	
		
		switch (m_type)
		{
			case SKATE_TYPE_CAR:
			{
				if (!mp_rendered_model->GetHierarchy())
				{
					break;
				}	
				// This updates the rotating wheels.
				Obj::CalculateCarHierarchyMatrices( GetSkeleton(),
                                                    mp_rendered_model,
													m_car_rotation_x,
													m_wheel_rotation_x,
													m_wheel_rotation_y);
				break;
			}	
			case SKATE_TYPE_PED:
			case SKATE_TYPE_SKATER:
			case SKATE_TYPE_GAME_OBJ:
				if (should_animate)
				{
//                    if ( mp_skeletonComponent )
//                    {
//                        mp_skeletonComponent->SetNeutralPose( mAnimScriptName+0x1ca1ff20/*default*/ );
//                    }
					
#if 0
					int degeneratingCount = 0;
					for ( int i = 0; i < NUM_DEGENERATE_ANIMS; i++ )
					{
						if ( m_degenerateControllers[i].GetStatus() != Gfx::ANIM_STATUS_INACTIVE )
						{
							degeneratingCount++;
						}
					}
					
					if ( degeneratingCount )
					{
						// animation has blending...
						
						uint32 degenerate_anim_name0,degenerate_anim_name1,degenerate_anim_name2;
						float degenerate_anim_time0,degenerate_anim_time1,degenerate_anim_time2;
						float degenerate_anim_blend_value0,degenerate_anim_blend_value1,degenerate_anim_blend_value2;
						
						m_degenerateControllers[0].GetNameTimeAndBlendValue(°enerate_anim_name0,°enerate_anim_time0,°enerate_anim_blend_value0);
						m_degenerateControllers[1].GetNameTimeAndBlendValue(°enerate_anim_name1,°enerate_anim_time1,°enerate_anim_blend_value1);
						m_degenerateControllers[2].GetNameTimeAndBlendValue(°enerate_anim_name2,°enerate_anim_time2,°enerate_anim_blend_value2);
						
						if (degenerate_anim_name0) degenerate_anim_name0+=mAnimScriptName;
						if (degenerate_anim_name1) degenerate_anim_name1+=mAnimScriptName;
						if (degenerate_anim_name2) degenerate_anim_name2+=mAnimScriptName;
						
						#if 0
						// disabled replays of skeletons until the transition is complete
                        if ( mp_skeletonComponent )
                        {
                            mp_skeletonComponent->Update( 
                                mAnimScriptName+m_primaryController.GetAnimName(), m_primaryController.GetCurrentAnimTime(),
                                degenerate_anim_name0,degenerate_anim_time0,degenerate_anim_blend_value0,
                                degenerate_anim_name1,degenerate_anim_time1,degenerate_anim_blend_value1,
                                degenerate_anim_name2,degenerate_anim_time2,degenerate_anim_blend_value2 );                        }
						#endif
					}
					else
					{
						#if 0
						// disabled replays of skeletons until the transition is complete
						if ( mp_skeletonComponent )
                        {
                            mp_skeletonComponent->Update( mAnimScriptName+m_primaryController.GetAnimName(),
								m_primaryController.GetCurrentAnimTime() );
                        }
						#endif
					}
#endif
				
				}	
				break;
			default:
				break;
		}		


		if (mFlags&DUMMY_FLAG_HOVERING)
		{
			uint32 t=Tmr::ElapsedTime(0)%mHoverPeriod;
			display_matrix[Mth::POS][Y]+=HOVER_AMPLITUDE*sinf(t*2*3.141592653f/mHoverPeriod);
		}
		
		mp_rendered_model->Render(&display_matrix,!should_animate,GetSkeleton());
	}	
}

void CDummy::UpdateMutedSounds()
{
	GetSoundComponent()->UpdateMutedSounds();
}	

static void sStopReplayDummyLoopingSounds()
{
	Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
	
	CDummy *p_dummy=spReplayDummies;
	while (p_dummy)
	{
		if (p_dummy->m_looping_sound_id)
		{
			sfx_manager->StopSound( p_dummy->m_looping_sound_id );
			p_dummy->m_looping_sound_id=0;
		}	
		
		p_dummy=p_dummy->mpNext;
	}
}
		
void ClearBuffer()
{
	if (BufferAllocated())
	{
		FillBuffer(0,GetBufferSize(),BLANK);
	}	
	sBigBufferStartOffset=0;
	sBigBufferEndOffset=0;
	sBufferFilling=true;
}

static uint8 spBufferChunk[REPLAY_BUFFER_CHUNK_SIZE];
uint8 *GetTempBuffer()
{
	return spBufferChunk;
}
	
void WriteReplayDataHeader(SReplayDataHeader *p_header)
{
	Dbg_MsgAssert(p_header,("NULL p_header"));
	p_header->mBufferStartOffset=sBigBufferStartOffset;
	p_header->mBufferEndOffset=sBigBufferEndOffset;
	p_header->mNumStartStateDummies=sCountStartStateDummies();
	p_header->mStartState=sStartState;
}

void ReadReplayDataHeader(const SReplayDataHeader *p_header)
{
	Dbg_MsgAssert(p_header,("NULL p_header"));
	sBigBufferStartOffset=p_header->mBufferStartOffset;
	sBigBufferEndOffset=p_header->mBufferEndOffset;
	sStartState=p_header->mStartState;
	Nx::CEngine::sReadSectorStatusBitfield(sStartState.mpSectorStatus,SECTOR_STATUS_BUFFER_SIZE);
}
	
void CreateDummyFromSaveData(SSavedDummy *p_saved_dummy)
{
#if 0
	Dbg_MsgAssert(p_saved_dummy,("NULL p_saved_dummy"));
	
	CDummy *p_dummy=new CDummy(START_STATE_DUMMY_LIST,p_saved_dummy->m_id);
	p_dummy->SetType(p_saved_dummy->m_type);
	
	Dbg_MsgAssert(strlen(p_saved_dummy->mpModelName)<=MAX_MODEL_NAME_CHARS,("p_saved_dummy mpModelName too long ?"));
	strcpy(p_dummy->mpModelName,p_saved_dummy->mpModelName);
	
	p_dummy->mSkeletonName	=	p_saved_dummy->mSkeletonName;
	p_dummy->mProfileName	=	p_saved_dummy->mProfileName;
	p_dummy->mAnimScriptName=	p_saved_dummy->mAnimScriptName;
	p_dummy->mSectorName	=	p_saved_dummy->mSectorName;
	p_dummy->mScale			=	p_saved_dummy->mScale;
	p_dummy->mFlags			=	p_saved_dummy->mFlags;
	// Note: The mHoverPeriod is not restored. No need, it does not need to be restored exactly,
	// and the CDummy constructor will have generated a new random value for it.
	p_dummy->m_pos			=	p_saved_dummy->m_pos;
	p_dummy->m_vel			=	p_saved_dummy->m_vel;
	p_dummy->m_angles		=	p_saved_dummy->m_angles;
	p_dummy->m_ang_vel		=	p_saved_dummy->m_ang_vel;
	p_dummy->m_car_rotation_x       = p_saved_dummy->m_car_rotation_x;      
	p_dummy->m_car_rotation_x_vel   = p_saved_dummy->m_car_rotation_x_vel;  
	p_dummy->m_wheel_rotation_x     = p_saved_dummy->m_wheel_rotation_x;    
	p_dummy->m_wheel_rotation_x_vel = p_saved_dummy->m_wheel_rotation_x_vel;
	p_dummy->m_wheel_rotation_y     = p_saved_dummy->m_wheel_rotation_y;    
	p_dummy->m_wheel_rotation_y_vel = p_saved_dummy->m_wheel_rotation_y_vel;
	
	p_dummy->m_primaryController	=	p_saved_dummy->m_primaryController;
	
	for (int i=0; im_degenerateControllers[i]=p_saved_dummy->m_degenerateControllers[i];
	}
	
	p_dummy->mAtomicStates			= p_saved_dummy->mAtomicStates;

	// The looping sound id is not copied in, since it is a unique id for the instance of the sound.
	p_dummy->m_looping_sound_id=0;
	p_dummy->m_looping_sound_checksum = p_saved_dummy->m_looping_sound_checksum;
	p_dummy->m_pitch_min = p_saved_dummy->m_pitch_min;
	p_dummy->m_pitch_max = p_saved_dummy->m_pitch_max;
	
	// This is because for the skater dummy the speed for calculating the looping sound
	// volume is calculated using m_old_pos. If it were left equal to 0,0,0 it would cause the
	// looping sound to be played at max volume for the first frame, because it would think
	// the skater was moving very fast.
	p_dummy->m_old_pos=p_dummy->m_pos;
#endif
}

static bool sPoolsCreated=false;
//char p_foo[sizeof(CTrackingInfo)/0];
void CreatePools()
{
	if (!sPoolsCreated)
	{
		// MEMOPT: Make this pool smaller
		CTrackingInfo::SCreatePool(300, "CTrackingInfo");
		sPoolsCreated=true;
	}	
}

void RemovePools()
{
	if (sPoolsCreated)
	{
		CTrackingInfo::SRemovePool();
		sPoolsCreated=false;
	}	
}

bool ScriptReplayRecordSimpleScriptCall(Script::CStruct *pParams, Script::CScript *pScript)
{
	// Do nothing if not in record mode.
	if (sMode!=RECORD)
	{
		return true;
	}
	
	uint32 script_name=0;
	pParams->GetChecksum("scriptname",&script_name);
	
	if (pParams->ContainsFlag("skaterscript"))
	{
		sWriteUint32(SKATER_SCRIPT_CALL,script_name);
	}
	else
	{
		sWriteUint32(SCRIPT_CALL,script_name);
	}
	return true;
}

static uint32 sLastRecordedPanelMessageID=0;
bool ScriptRecordPanelMessage(Script::CStruct *pParams, Script::CScript *pScript)
{
	if (sMode!=RECORD)
	{
		return true;
	}

	// Remember what the last panel message id was so that it can be killed before
	// replaying the replay.
	//sLastRecordedPanelMessageID=0;
	//pParams->GetChecksum("id",&sLastRecordedPanelMessageID);
	
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	sLastRecordedPanelMessageID = p_manager->ResolveComplexID(pParams, "id");
	
	
	
	uint8 p_buf[MAX_PANEL_MESSAGE_SIZE];
	int size=Script::WriteToBuffer(pParams,p_buf,MAX_PANEL_MESSAGE_SIZE,Script::NO_ASSERT);
	if (!size)
	{
		Script::PrintContents(pParams);
		Dbg_MsgAssert(0,("Panel message structure too big"));
		return true;
	}	
	
	sWriteUint32(PANEL_MESSAGE,size);
	
	Dbg_MsgAssert(spFrameBufferPos+size <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	memcpy(spFrameBufferPos,p_buf,size);
	spFrameBufferPos+=size;
	
	return true;
}
	
// Call this to allocate the resources needed to store a replay.
// May be called repeatedly.
bool ScriptAllocateReplayMemory(Script::CStruct *pParams, Script::CScript *pScript)
{
	return true;
	Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
	AllocateBuffer();
	ClearBuffer();
	CreatePools();
	Mem::Manager::sHandle().PopContext();
	sMode=RECORD;	
	
	CTrackingInfo::sSkaterTrackingInfo.Reset();
	Nx::CEngine::sInitReplayStartState();
	return true;
}

void DeallocateReplayMemory()
{
	sDeleteDummies(START_STATE_DUMMY_LIST);
	sDeleteDummies(PLAYBACK_DUMMY_LIST);
	Dbg_MsgAssert(spReplayDummies==NULL,("Hey! spReplayDummies not NULL !!!"));
	sDeleteObjectTrackingInfo();
	ClearBuffer();
	DeallocateBuffer();
	RemovePools();
	sStartState.Reset();
	sCurrentState.Reset();
	sMode=NONE;
}

// Call this to free up the resources used to store a replay.
// May be called repeatedly.
bool ScriptDeallocateReplayMemory(Script::CStruct *pParams, Script::CScript *pScript)
{
	DeallocateReplayMemory();
	return true;
}	

// If in record mode, this will clear what has been recorded and start afresh.
void StartRecordingAfresh()
{
	Dbg_MsgAssert(!Mdl::Skate::Instance()->IsMultiplayerGame(),("Tried to start recording replay in multiplayer game"));
	sMode=RECORD;

	sDeleteDummies(START_STATE_DUMMY_LIST);
	sDeleteDummies(PLAYBACK_DUMMY_LIST);
	Dbg_MsgAssert(spReplayDummies==NULL,("Hey! spReplayDummies not NULL !!!"));
	sDeleteObjectTrackingInfo();
	ClearBuffer();
	sStartState.Reset();
	sCurrentState.Reset();
	CTrackingInfo::sSkaterTrackingInfo.Reset();
	Nx::CEngine::sInitReplayStartState();
}

bool ScriptStartRecordingAfresh(Script::CStruct *pParams, Script::CScript *pScript)
{
	if (Mdl::Skate::Instance()->IsMultiplayerGame())
	{
		return false;
	}	
	StartRecordingAfresh();
	return true;
}	

static uint32 sReadAnimControllerChanges(uint32 offset, Gfx::CAnimChannel *p_anim_controller)
{
	#define MAX_CHANGES 18
	uint8 p_buf[5*MAX_CHANGES];
	
	uint8 num_changes;
	ReadFromBuffer(&num_changes,offset,1);
	++offset;
	Dbg_MsgAssert(num_changes<=MAX_CHANGES,("Too many anim controller changes in one instruction"));
	
	ReadFromBuffer(p_buf,offset,5*num_changes);
	offset+=5*num_changes;
	
	uint32 *p_dest=(uint32*)p_anim_controller;
	uint8 *p_foo=p_buf;
	for (int i=0; i *p_dummy_table=sGetDummyHashTable(whichList);
	*p_nothingFollows=false;
	CDummy *p_dummy=NULL;
	bool end=false;
	while (!end)
	{
		if (offset==GetBufferSize())
		{
			*p_nothingFollows=true;
			break;
		}	
		Dbg_MsgAssert(offsetSetShatter(token==SHATTER_ON);
				}	
			
				break;
			}

			
			case SCRIPT_CALL:	
			case SKATER_SCRIPT_CALL: // Used to record BloodParticlesOn & off
			{
				// Must not call the script if recording.
				if (sMode==RECORD)
				{
					offset+=1+4;
					break;
				}
				
				++offset;

				uint32 script_name;
				ReadFromBuffer((uint8*)&script_name,offset,4);
				script_name=Script::Read4Bytes((uint8*)&script_name).mUInt;
				offset+=4;
				
				if (token==SCRIPT_CALL)
				{
					Script::RunScript(script_name);
				}
				else
				{
					Script::RunScript( script_name, NULL, p_skater );
				}
				break;
			}
			
			case PAD_VIBRATION:
			{
				++offset;
				uint8 actuator;
				ReadFromBuffer(&actuator,offset,1);
				++offset;
				Dbg_MsgAssert(actuatorGetDevice()->ActivateActuator(actuator,percent);
				}	
				break;
			}
				
			// This command is needed because when the goal cutscenes play in mid goal
			// they do a PauseSkaters command, which in-game pauses the vibrations.
			// Need to do that in the replay too, otherwise in the 4 angry seals goal in sf
			// the pad vibrates during the camera anims, cos the skater is usually in a grind then.
			case PAUSE_SKATER:	
			case UNPAUSE_SKATER:	
			{
				++offset;
				
				if (whichList==START_STATE_DUMMY_LIST)
				{
					sStartState.mSkaterPaused=(token==PAUSE_SKATER);
				}
				else
				{
					sCurrentState.mSkaterPaused=(token==PAUSE_SKATER);
					if (sCurrentState.mSkaterPaused)
					{
						// Switch off the vibrations.
						for (int i=0; iGetDevice()->ActivateActuator(i,0);
						}	
					}
					else
					{
						// Restore the vibrations.
						for (int i=0; iGetDevice()->ActivateActuator(i,sCurrentState.mActuatorStrength[i]);
						}	
					}
				}
				break;
			}
				
			case MANUAL_METER_ON:
			{
				++offset;
				float value;
				ReadFromBuffer((uint8*)&value,offset,4);
				value=Script::Read4Bytes((uint8*)&value).mFloat;
				offset+=4;
				
				if (whichList==START_STATE_DUMMY_LIST)
				{
					sStartState.mManualMeterStatus=true;
					sStartState.mManualMeterValue=value;
				}
				else
				{
					sCurrentState.mManualMeterStatus=true;
					sCurrentState.mManualMeterValue=value;
					sGetSkaterScoreObject()->SetManualMeter(true,value);
				}
				break;
			}
			
			case MANUAL_METER_OFF:
			{
				++offset;
					
				if (whichList==START_STATE_DUMMY_LIST)
				{
					sStartState.mManualMeterStatus=false;
					sStartState.mManualMeterValue=0.0f;
				}
				else
				{
					sCurrentState.mManualMeterStatus=false;
					sCurrentState.mManualMeterValue=0.0f;
					sGetSkaterScoreObject()->SetManualMeter(false);
				}
				break;
			}

			case BALANCE_METER_ON:
			{
				++offset;
				float value;
				ReadFromBuffer((uint8*)&value,offset,4);
				value=Script::Read4Bytes((uint8*)&value).mFloat;
				offset+=4;
				
				if (whichList==START_STATE_DUMMY_LIST)
				{
					sStartState.mBalanceMeterStatus=true;
					sStartState.mBalanceMeterValue=value;
				}
				else
				{
					sCurrentState.mBalanceMeterStatus=true;
					sCurrentState.mBalanceMeterValue=value;
					sGetSkaterScoreObject()->SetBalanceMeter(true,value);
				}
				break;
			}
			
			case BALANCE_METER_OFF:
			{
				++offset;
					
				if (whichList==START_STATE_DUMMY_LIST)
				{
					sStartState.mBalanceMeterStatus=false;
					sStartState.mBalanceMeterValue=0.0f;
				}
				else
				{
					sCurrentState.mBalanceMeterStatus=false;
					sCurrentState.mBalanceMeterValue=0.0f;
					sGetSkaterScoreObject()->SetBalanceMeter(false);
				}
				break;
			}
			
#if 0
			case FLIP:	
			case UNFLIP:	
			{
				++offset;
				
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				if (token==FLIP)
				{
					p_dummy->mFlags|=DUMMY_FLAG_FLIPPED;
				}
				else
				{
					p_dummy->mFlags&=~DUMMY_FLAG_FLIPPED;
				}
				
				if (whichList==PLAYBACK_DUMMY_LIST && p_dummy->mp_rendered_model)
				{
					Gfx::CSkeleton* p_skeleton=p_dummy->GetSkeleton();
					if (p_skeleton)
					{
						p_skeleton->FlipAnimation( 0, p_dummy->mFlags&DUMMY_FLAG_FLIPPED, 0, false );
					}
				}		
				break;
			}
#endif

			case MODEL_ACTIVE:	
			case MODEL_INACTIVE:	
			{
				++offset;
				
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				if (token==MODEL_ACTIVE)
				{
					p_dummy->mFlags|=DUMMY_FLAG_ACTIVE;
				}
				else
				{
					p_dummy->mFlags&=~DUMMY_FLAG_ACTIVE;
				}
				
				if (whichList==PLAYBACK_DUMMY_LIST && p_dummy->mp_rendered_model)
				{
					p_dummy->mp_rendered_model->SetActive(p_dummy->mFlags&DUMMY_FLAG_ACTIVE);
				}		
				break;
			}
				
			case SPARKS_ON:
			{
				++offset;
				// Must not do sparks if recording
				if (sMode==RECORD)
				{
					break;
				}
				
				Script::RunScript( "sparks_on", NULL, p_skater );
				break;
			}

			case SPARKS_OFF:
			{
				++offset;
				if (sMode==RECORD)
				{
					break;
				}
				
				Script::RunScript( "sparks_off", NULL, p_skater );
				break;
			}
			
			case PANEL_MESSAGE:
			{
				++offset;
				int size;
				ReadFromBuffer((uint8*)&size,offset,4);
				size=Script::Read4Bytes((uint8*)&size).mInt;
				offset+=4;
				
				uint8 p_buf[MAX_PANEL_MESSAGE_SIZE];
				Dbg_MsgAssert(size<=MAX_PANEL_MESSAGE_SIZE,("Panel message size too big in replay"));
				ReadFromBuffer(p_buf,offset,size);
				offset+=size;

				if (sMode==RECORD)
				{
					break;
				}
				
				Script::CStruct *p_struct=new Script::CStruct;
				Script::ReadFromBuffer(p_struct,p_buf);
				Script::RunScript("Create_Panel_Message",p_struct);
				delete p_struct;
				break;
			}
					
			case SCORE_POT_TEXT:
			{
				++offset;
				uint8 len;
				ReadFromBuffer(&len,offset,1);
				++offset;
				char p_text[100];
				if (len<100)
				{
					ReadFromBuffer((uint8*)p_text,offset,len);
					p_text[len]=0;
				}
				else
				{
					p_text[0]=0;
				}
				offset+=len;

				if (sMode==RECORD)
				{
					break;
				}
				
				Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
				
				int index = p_skater->GetHeapIndex();

				Front::CTextElement *p_score_pot_text = (Front::CTextElement *) p_manager->GetElement(0xf4d3a70e + index ).Convert(); // "the_score_pot_text"
				if (p_score_pot_text)
				{
					p_score_pot_text->SetText(p_text);
				}
				break;
			}
			
			case TRICK_TEXT:
			{
				++offset;
				uint8 num_strings;
				ReadFromBuffer(&num_strings,offset,1);
				++offset;

				const char *pp_text[TRICK_LIMIT];
				int num_strings_loaded=0;
				char *p_dest=spTrickText;
				for (uint8 i=0; iGetHeapIndex();
					
					Front::CTextBlockElement *p_text_block = (Front::CTextBlockElement *) p_manager->GetElement(0x44727dae/*the_trick_text*/ + index ).Convert();
					if (p_text_block)
					{
						p_text_block->SetText(pp_text, num_strings_loaded);
					}
					sTrickTextGotCleared=false;
					break;
				}
			}

			case TRICK_TEXT_PULSE:
			case TRICK_TEXT_COUNTDOWN:
			case TRICK_TEXT_LANDED:
			case TRICK_TEXT_BAIL:
			{
				++offset;
				if (sMode==RECORD)
				{
					break;
				}
				
				if (sTrickTextGotCleared)
				{
					break;
				}
					
				int index=p_skater->GetHeapIndex();
				
				Mdl::Score *p_score=p_skater->GetScoreObject();
				
				switch (token)
				{
					case TRICK_TEXT_PULSE:
						p_score->TrickTextPulse(index);
						break;
					case TRICK_TEXT_COUNTDOWN:
						p_score->TrickTextCountdown(index);
						break;
					case TRICK_TEXT_LANDED:
						p_score->TrickTextLanded(index);
						break;
					case TRICK_TEXT_BAIL:
						p_score->TrickTextBail(index);
						break;
					default:
						break;
				}				
				
				break;
			}
			
			case SET_ATOMIC_STATES:							
			{
				++offset;
				uint32 id;
				ReadFromBuffer((uint8*)&id,offset,4);
				id=Script::Read4Bytes((uint8*)&id).mUInt;
				offset+=4;
				
				uint32 atomic_states;
				ReadFromBuffer((uint8*)&atomic_states,offset,4);
				atomic_states=Script::Read4Bytes((uint8*)&atomic_states).mUInt;
				offset+=4;

				p_dummy=p_dummy_table->GetItem(id);
				if (p_dummy)
				{
					// Record the atomic state change in the dummy too, so that the initial states
					// can be set at the start of replay playback.
					p_dummy->mAtomicStates=atomic_states;
				
					if (p_dummy->mp_rendered_model)
					{
						p_dummy->mp_rendered_model->SetGeomActiveMask(atomic_states);
					}				
				}	
				break;
			}
			
			case PLAY_STREAM:
			{
				// Must not play sounds if recording
				if (sMode==RECORD)
				{
					offset+=1+4*5;
					break;
				}
				
				++offset;
				uint32 checksum;
//				float volume_l, volume_r;
				float vol;
				float pitch;

				int priority;
				ReadFromBuffer((uint8*)&checksum,offset,4);
				checksum=Script::Read4Bytes((uint8*)&checksum).mUInt;
				offset+=4;

				uint32 volume_type;
				ReadFromBuffer((uint8*)&volume_type,offset,4);
				volume_type=Script::Read4Bytes((uint8*)&volume_type).mInt;
				offset+=4;

				Sfx::sVolume vol_struct((Sfx::EVolumeType)volume_type );

				ReadFromBuffer((uint8*)&vol,offset,4);
				vol=Script::Read4Bytes((uint8*)&vol).mFloat;
				offset+=4;
				vol_struct.SetChannelVolume( 0, vol );

				ReadFromBuffer((uint8*)&vol,offset,4);
				vol=Script::Read4Bytes((uint8*)&vol).mFloat;
				offset+=4;
				vol_struct.SetChannelVolume( 1, vol );

				// Read channels 2, 3 and 4 if a 5 channel sound type.
				if( vol_struct.GetVolumeType() == Sfx::VOLUME_TYPE_5_CHANNEL_DOLBY5_1 )
				{
					ReadFromBuffer((uint8*)&vol,offset,4);
					vol=Script::Read4Bytes((uint8*)&vol).mFloat;
					offset+=4;
					vol_struct.SetChannelVolume( 2, vol );

					ReadFromBuffer((uint8*)&vol,offset,4);
					vol=Script::Read4Bytes((uint8*)&vol).mFloat;
					offset+=4;
					vol_struct.SetChannelVolume( 3, vol );

					ReadFromBuffer((uint8*)&vol,offset,4);
					vol=Script::Read4Bytes((uint8*)&vol).mFloat;
					offset+=4;
					vol_struct.SetChannelVolume( 4, vol );
				}

				ReadFromBuffer((uint8*)&pitch,offset,4);
				pitch=Script::Read4Bytes((uint8*)&pitch).mFloat;
				offset+=4;
				ReadFromBuffer((uint8*)&priority,offset,4);
				priority=Script::Read4Bytes((uint8*)&priority).mInt;
				offset+=4;

				Pcm::PlayStream(checksum,&vol_struct,pitch,priority);
				break;
			}

			case STOP_STREAM:
			{
				// Must not stop streams if recording
				if (sMode==RECORD)
				{
					offset+=1+4;
					break;
				}
				
				++offset;
				int channel;
				ReadFromBuffer((uint8*)&channel,offset,4);
				channel=Script::Read4Bytes((uint8*)&channel).mInt;
				offset+=4;
				
				Pcm::StopStreams(channel);
				break;
			}
						
			case PLAY_POSITIONAL_STREAM:
			{
				// Must not play sounds if recording
				if (sMode==RECORD)
				{
					offset+=1+4*7;
					break;
				}
				
				++offset;
				uint32 id, stream_name;
				float volume, pitch, drop_off;
				int priority, use_pos_info;
				ReadFromBuffer((uint8*)&id,offset,4);
				id=Script::Read4Bytes((uint8*)&id).mUInt;
				offset+=4;
				ReadFromBuffer((uint8*)&stream_name,offset,4);
				stream_name=Script::Read4Bytes((uint8*)&stream_name).mUInt;
				offset+=4;
				ReadFromBuffer((uint8*)&drop_off,offset,4);
				drop_off=Script::Read4Bytes((uint8*)&drop_off).mFloat;
				offset+=4;
				ReadFromBuffer((uint8*)&volume,offset,4);
				volume=Script::Read4Bytes((uint8*)&volume).mFloat;
				offset+=4;
				ReadFromBuffer((uint8*)&pitch,offset,4);
				pitch=Script::Read4Bytes((uint8*)&pitch).mFloat;
				offset+=4;
				ReadFromBuffer((uint8*)&priority,offset,4);
				priority=Script::Read4Bytes((uint8*)&priority).mInt;
				offset+=4;
				ReadFromBuffer((uint8*)&use_pos_info,offset,4);
				use_pos_info=Script::Read4Bytes((uint8*)&use_pos_info).mInt;
				offset+=4;

				p_dummy=p_dummy_table->GetItem(id);
				if (p_dummy)
				{
					printf ("STUBBED replay.cpp %d\n",__LINE__);
//					Pcm::PlayStreamFromObject(p_dummy,stream_name,drop_off,volume,pitch,channel,use_pos_info);
				}
				
				break;
			}
			
			case PLAY_POSITIONAL_SOUND_EFFECT:				
			{
				// Must not play sounds if recording
				if (sMode==RECORD)
				{
					offset+=1+4*5;
					break;
				}
				
				++offset;
				uint32 id, sound_name;
				float volume, pitch, drop_off_dist;
				ReadFromBuffer((uint8*)&id,offset,4);
				id=Script::Read4Bytes((uint8*)&id).mUInt;
				offset+=4;
				ReadFromBuffer((uint8*)&sound_name,offset,4);
				sound_name=Script::Read4Bytes((uint8*)&sound_name).mUInt;
				offset+=4;
				ReadFromBuffer((uint8*)&volume,offset,4);
				volume=Script::Read4Bytes((uint8*)&volume).mFloat;
				offset+=4;
				ReadFromBuffer((uint8*)&pitch,offset,4);
				pitch=Script::Read4Bytes((uint8*)&pitch).mFloat;
				offset+=4;
				ReadFromBuffer((uint8*)&drop_off_dist,offset,4);
				drop_off_dist=Script::Read4Bytes((uint8*)&drop_off_dist).mFloat;
				offset+=4;

				p_dummy=p_dummy_table->GetItem(id);
				if (p_dummy)
				{
					Sfx::CSfxManager * p_sfx_manager = Sfx::CSfxManager::Instance();
					Sfx::SoundUpdateInfo soundUpdateInfo;
					soundUpdateInfo.volume = volume;
					soundUpdateInfo.pitch = pitch;
					soundUpdateInfo.dropoffDist = drop_off_dist;

					// These next two parameters need to be added to the replay code
					soundUpdateInfo.dropoffFunction = DROPOFF_FUNC_STANDARD;
					bool noPosUpdate = false;
					p_sfx_manager->PlaySoundWithPos( sound_name, &soundUpdateInfo, GetSoundComponentFromObject( p_dummy ), noPosUpdate );
				}
				
				break;
			}
				
			case PLAY_SKATER_SOUND_EFFECT:				
			{
				// Must not play sounds if recording
				if (sMode==RECORD)
				{
					offset+=1+4*6;
					break;
				}
				
				++offset;
				
				int which_array, surface_flag;
				Mth::Vector pos;
				float vol_percent;
				
				ReadFromBuffer((uint8*)&which_array,offset,4);
				which_array=Script::Read4Bytes((uint8*)&which_array).mInt;
				offset+=4;
				ReadFromBuffer((uint8*)&surface_flag,offset,4);
				surface_flag=Script::Read4Bytes((uint8*)&surface_flag).mInt;
				offset+=4;
				ReadFromBuffer((uint8*)&pos[X],offset,4);
				pos[X]=Script::Read4Bytes((uint8*)&pos[X]).mFloat;
				offset+=4;
				ReadFromBuffer((uint8*)&pos[Y],offset,4);
				pos[Y]=Script::Read4Bytes((uint8*)&pos[Y]).mFloat;
				offset+=4;
				ReadFromBuffer((uint8*)&pos[Z],offset,4);
				pos[Z]=Script::Read4Bytes((uint8*)&pos[Z]).mFloat;
				offset+=4;
				ReadFromBuffer((uint8*)&vol_percent,offset,4);
				vol_percent=Script::Read4Bytes((uint8*)&vol_percent).mFloat;
				offset+=4;

				// NOTE: need to record sound pitch and choice for use here
				Env::CTerrainManager::sPlaySound((Env::ETerrainActionType) which_array, (ETerrainType) surface_flag,pos,vol_percent,100.0f,0.0f,false);
				break;
			}
				
			case PLAY_SOUND:
			{
				// Must not play sounds if recording
				if (sMode==RECORD)
				{
					offset+=1+4*4;
					break;
				}
				
				++offset;
				
				uint32 checksum;
				ReadFromBuffer((uint8*)&checksum,offset,4);
				checksum=Script::Read4Bytes((uint8*)&checksum).mUInt;
				offset+=4;
				
				float vol_l,vol_r;
				ReadFromBuffer((uint8*)&vol_l,offset,4);
				vol_l=Script::Read4Bytes((uint8*)&vol_l).mFloat;
				offset+=4;
				ReadFromBuffer((uint8*)&vol_r,offset,4);
				vol_r=Script::Read4Bytes((uint8*)&vol_r).mFloat;
				offset+=4;

				float pitch;
				ReadFromBuffer((uint8*)&pitch,offset,4);
				pitch=Script::Read4Bytes((uint8*)&pitch).mFloat;
				offset+=4;
				
				Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();

				// Dave note 10/17/02, the replay code should store all 5 channels where appropriate.
				Sfx::sVolume vol;
				vol.SetSilent();
				vol.SetChannelVolume( 0, vol_l );
				vol.SetChannelVolume( 1, vol_r );
//				sfx_manager->PlaySound(checksum,vol_l,vol_r,pitch);
				sfx_manager->PlaySound(checksum,&vol,pitch);
				break;
			}
			
			case PLAY_LOOPING_SOUND:
			{
				++offset;
				
				uint32 checksum;
				ReadFromBuffer((uint8*)&checksum,offset,4);
				checksum=Script::Read4Bytes((uint8*)&checksum).mUInt;
				offset+=4;

				p_dummy->m_looping_sound_checksum=checksum;
					
				if (whichList==PLAYBACK_DUMMY_LIST)
				{
					Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
					
					Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
					if (p_dummy->m_looping_sound_id)
					{
						sfx_manager->StopSound( p_dummy->m_looping_sound_id );
					}	
					p_dummy->m_looping_sound_id = sfx_manager->PlaySound( checksum, 0, 0 );
				}
				else
				{
					// This bit will execute if the list is the start-state dummy list.
					// m_looping_sound_id isn't used in that case, but may as well set it to zero.
					p_dummy->m_looping_sound_id=0;
				}	
					
				break;
			}

			case STOP_LOOPING_SOUND:
			{
				++offset;
				
				if (whichList==PLAYBACK_DUMMY_LIST)
				{
					Sfx::CSfxManager * sfx_manager =  Sfx::CSfxManager::Instance();
					
					Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
					if (p_dummy->m_looping_sound_id)
					{
						sfx_manager->StopSound( p_dummy->m_looping_sound_id );
					}	
				}	
				
				p_dummy->m_looping_sound_id=0;
				p_dummy->m_looping_sound_checksum=0;
				break;
			}

			case PITCH_MIN:
			case PITCH_MAX:
			{
				++offset;
				
				float pitch;
				ReadFromBuffer((uint8*)&pitch,offset,4);
				pitch=Script::Read4Bytes((uint8*)&pitch).mFloat;
				offset+=4;

				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				if (token==PITCH_MIN)
				{
					p_dummy->m_pitch_min=pitch;
				}
				else
				{
					p_dummy->m_pitch_max=pitch;
				}
				break;
			}
				
			case OBJECT_ID:
			{
				++offset;
				uint32 id;
				ReadFromBuffer((uint8*)&id,offset,4);
				id=Script::Read4Bytes((uint8*)&id).mUInt;
				
				p_dummy=p_dummy_table->GetItem(id);
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?  id=%d",id));
				
				offset+=4;
				break;
			}

			case CREATE_OBJECT:
			{
				++offset;
				uint32 id;
				uint8 type;
				ReadFromBuffer((uint8*)&id,offset,4);
				id=Script::Read4Bytes((uint8*)&id).mUInt;
				offset+=4;
				ReadFromBuffer(&type,offset,1);
				++offset;
				
				p_dummy=p_dummy_table->GetItem(id);
				if (!p_dummy)
				{
					p_dummy=new CDummy(whichList,id);
					p_dummy->SetType(type);
				}
				else
				{
					Dbg_MsgAssert(0,("Got CREATE_OBJECT when dummy already exists ?"));
				}	
				
				break;
			}
			
			case KILL_OBJECT:
			{
				++offset;
				uint32 id;
				ReadFromBuffer((uint8*)&id,offset,4);
				id=Script::Read4Bytes((uint8*)&id).mUInt;
				
				p_dummy=p_dummy_table->GetItem(id);
				if (p_dummy)
				{
					delete p_dummy;
					p_dummy=NULL;
				}	
				
				offset+=4;
				break;
			}
			
#if 0
			case MODEL_NAME:
			{
				++offset;
				uint8 len;
				ReadFromBuffer(&len,offset,1);
				++offset;
				
				Dbg_MsgAssert(len<=MAX_MODEL_NAME_CHARS,("Too many chars in model name"));
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)p_dummy->mpModelName,offset,len);
				// The string has no terminating 0 in the big buffer, so wack one on.
				p_dummy->mpModelName[len]=0;
				offset+=len;
				break;
			}
#endif
			case SKELETON_NAME:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->mSkeletonName,offset,4);
				p_dummy->mSkeletonName=Script::Read4Bytes((uint8*)&p_dummy->mSkeletonName).mUInt;
				offset+=4;
				break;
			}
			case PROFILE_NAME:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->mProfileName,offset,4);
				p_dummy->mProfileName=Script::Read4Bytes((uint8*)&p_dummy->mProfileName).mUInt;
				offset+=4;
				break;
			}
			case SECTOR_NAME:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->mSectorName,offset,4);
				p_dummy->mSectorName=Script::Read4Bytes((uint8*)&p_dummy->mSectorName).mUInt;
				offset+=4;
				break;
			}
			case ANIM_SCRIPT_NAME:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->mAnimScriptName,offset,4);
				p_dummy->mAnimScriptName=Script::Read4Bytes((uint8*)&p_dummy->mAnimScriptName).mUInt;
				offset+=4;
				break;
			}
			case SET_SCALE:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->mScale,offset,4);
				p_dummy->mScale=Script::Read4Bytes((uint8*)&p_dummy->mScale).mFloat;
				if (p_dummy->mp_rendered_model)
				{
					Mth::Vector s(p_dummy->mScale,p_dummy->mScale,p_dummy->mScale);
					p_dummy->mp_rendered_model->SetScale(s);
				}	
				offset+=4;
				break;
			}	
			case SET_CAR_ROTATION_X:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_car_rotation_x,offset,4);
				p_dummy->m_car_rotation_x=Script::Read4Bytes((uint8*)&p_dummy->m_car_rotation_x).mFloat;
				p_dummy->m_car_rotation_x_vel=0.0f;
				offset+=4;
				break;
			}	
			case SET_WHEEL_ROTATION_X:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_wheel_rotation_x,offset,4);
				p_dummy->m_wheel_rotation_x=Script::Read4Bytes((uint8*)&p_dummy->m_wheel_rotation_x).mFloat;
				p_dummy->m_wheel_rotation_x_vel=0.0f;
				offset+=4;
				break;
			}	
			case SET_WHEEL_ROTATION_Y:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_wheel_rotation_y,offset,4);
				p_dummy->m_wheel_rotation_y=Script::Read4Bytes((uint8*)&p_dummy->m_wheel_rotation_y).mFloat;
				p_dummy->m_wheel_rotation_y_vel=0.0f;
				offset+=4;
				break;
			}	
			case SET_CAR_ROTATION_X_VEL:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_car_rotation_x_vel,offset,4);
				p_dummy->m_car_rotation_x_vel=Script::Read4Bytes((uint8*)&p_dummy->m_car_rotation_x_vel).mFloat;
				offset+=4;
				break;
			}	
			case SET_WHEEL_ROTATION_X_VEL:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_wheel_rotation_x_vel,offset,4);
				p_dummy->m_wheel_rotation_x_vel=Script::Read4Bytes((uint8*)&p_dummy->m_wheel_rotation_x_vel).mFloat;
				offset+=4;
				break;
			}	
			case SET_WHEEL_ROTATION_Y_VEL:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_wheel_rotation_y_vel,offset,4);
				p_dummy->m_wheel_rotation_y_vel=Script::Read4Bytes((uint8*)&p_dummy->m_wheel_rotation_y_vel).mFloat;
				offset+=4;
				break;
			}	
			
			case SET_ANGLE_X:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_angles[X],offset,4);
				p_dummy->m_angles[X]=Script::Read4Bytes((uint8*)&p_dummy->m_angles[X]).mFloat;
				p_dummy->m_ang_vel[X]=0.0f;
				offset+=4;
				break;
			}	
			case SET_ANGLE_Y:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_angles[Y],offset,4);
				p_dummy->m_angles[Y]=Script::Read4Bytes((uint8*)&p_dummy->m_angles[Y]).mFloat;
				p_dummy->m_ang_vel[Y]=0.0f;
				offset+=4;
				break;
			}	
			case SET_ANGLE_Z:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_angles[Z],offset,4);
				p_dummy->m_angles[Z]=Script::Read4Bytes((uint8*)&p_dummy->m_angles[Z]).mFloat;
				p_dummy->m_ang_vel[Z]=0.0f;
				offset+=4;
				break;
			}	
			case SET_ANGULAR_VELOCITY_X:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_ang_vel[X],offset,4);
				p_dummy->m_ang_vel[X]=Script::Read4Bytes((uint8*)&p_dummy->m_ang_vel[X]).mFloat;
				offset+=4;
				break;
			}	
			case SET_ANGULAR_VELOCITY_Y:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_ang_vel[Y],offset,4);
				p_dummy->m_ang_vel[Y]=Script::Read4Bytes((uint8*)&p_dummy->m_ang_vel[Y]).mFloat;
				offset+=4;
				break;
			}	
			case SET_ANGULAR_VELOCITY_Z:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_ang_vel[Z],offset,4);
				p_dummy->m_ang_vel[Z]=Script::Read4Bytes((uint8*)&p_dummy->m_ang_vel[Z]).mFloat;
				offset+=4;
				break;
			}	
				
			case SET_POSITION:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_pos[X],offset,4*3);
				p_dummy->m_pos[X]=Script::Read4Bytes((uint8*)&p_dummy->m_pos[X]).mFloat;
				p_dummy->m_pos[Y]=Script::Read4Bytes((uint8*)&p_dummy->m_pos[Y]).mFloat;
				p_dummy->m_pos[Z]=Script::Read4Bytes((uint8*)&p_dummy->m_pos[Z]).mFloat;
				p_dummy->m_vel.Set();
				
				offset+=4*3;
				break;
			}	
			
			case SET_POSITION_ANGLES:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				
				ReadFromBuffer((uint8*)&p_dummy->m_pos[X],offset,4*3);
				p_dummy->m_pos[X]=Script::Read4Bytes((uint8*)&p_dummy->m_pos[X]).mFloat;
				p_dummy->m_pos[Y]=Script::Read4Bytes((uint8*)&p_dummy->m_pos[Y]).mFloat;
				p_dummy->m_pos[Z]=Script::Read4Bytes((uint8*)&p_dummy->m_pos[Z]).mFloat;
				offset+=4*3;

				ReadFromBuffer((uint8*)&p_dummy->m_angles[X],offset,4*3);
				p_dummy->m_angles[X]=Script::Read4Bytes((uint8*)&p_dummy->m_angles[X]).mFloat;
				p_dummy->m_angles[Y]=Script::Read4Bytes((uint8*)&p_dummy->m_angles[Y]).mFloat;
				p_dummy->m_angles[Z]=Script::Read4Bytes((uint8*)&p_dummy->m_angles[Z]).mFloat;
				offset+=4*3;
				
				p_dummy->m_vel.Set();
				p_dummy->m_ang_vel.Set();
				break;
			}	
			
			case SET_VELOCITY:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				ReadFromBuffer((uint8*)&p_dummy->m_vel[X],offset,4*3);
				p_dummy->m_vel[X]=Script::Read4Bytes((uint8*)&p_dummy->m_vel[X]).mFloat;
				p_dummy->m_vel[Y]=Script::Read4Bytes((uint8*)&p_dummy->m_vel[Y]).mFloat;
				p_dummy->m_vel[Z]=Script::Read4Bytes((uint8*)&p_dummy->m_vel[Z]).mFloat;
				offset+=4*3;
				break;
			}	
			case PRIMARY_ANIM_CONTROLLER_CHANGES:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				offset=sReadAnimControllerChanges(offset,&p_dummy->m_primaryController);
				break;
			}	
			case DEGENERATE_ANIM_CONTROLLER_CHANGES:
			{
				++offset;
				uint8 index=0;
				ReadFromBuffer(&index,offset,1);
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				Dbg_MsgAssert(p_dummy->GetID()==ID_SKATER,("Got degenerate anim changes for a non-skater ?"));
				offset=sReadAnimControllerChanges(offset,&p_dummy->m_degenerateControllers[index]);
				break;
			}	
			case HOVERING:
			{
				++offset;
				Dbg_MsgAssert(p_dummy,("NULL p_dummy ?"));
				p_dummy->mFlags|=DUMMY_FLAG_HOVERING;
				
				ReadFromBuffer((uint8*)&p_dummy->m_pos[X],offset,4*3);
				p_dummy->m_pos[X]=Script::Read4Bytes((uint8*)&p_dummy->m_pos[X]).mFloat;
				p_dummy->m_pos[Y]=Script::Read4Bytes((uint8*)&p_dummy->m_pos[Y]).mFloat;
				p_dummy->m_pos[Z]=Script::Read4Bytes((uint8*)&p_dummy->m_pos[Z]).mFloat;
				offset+=4*3;
				
				break;
			}	
			
			case SECTOR_ACTIVE:
			case SECTOR_INACTIVE:
			{
				++offset;
				uint32 sector_name;
				ReadFromBuffer((uint8*)§or_name,offset,4);
				sector_name=Script::Read4Bytes((uint8*)§or_name).mUInt;
				offset+=4;
				
				Nx::CSector *p_sector = Nx::CEngine::sGetSector(sector_name);
				if (p_sector)
				{
					if (sMode==RECORD)
					{
						// If recording, apply to the start state.
						p_sector->SetActiveAtReplayStart(token==SECTOR_ACTIVE);
					}
					else
					{
						// Otherwise apply to the current world.
						p_sector->SetActive(token==SECTOR_ACTIVE);
					}
				}
				break;
			}	

			case SECTOR_VISIBLE:
			case SECTOR_INVISIBLE:
			{
				++offset;
				uint32 sector_name;
				ReadFromBuffer((uint8*)§or_name,offset,4);
				sector_name=Script::Read4Bytes((uint8*)§or_name).mUInt;
				offset+=4;
				
				Nx::CSector *p_sector = Nx::CEngine::sGetSector(sector_name);
				if (p_sector)
				{
					if (sMode==RECORD)
					{
						// If recording, apply to the start state.
						p_sector->SetVisibleAtReplayStart(token==SECTOR_VISIBLE);
					}
					else
					{
						// Otherwise apply to the current world.
						p_sector->SetVisibility(token==SECTOR_VISIBLE ?0xff:0x00);
					}
				}
				break;
			}	
			
			default:
				Dbg_MsgAssert(0,("Unsupported token type of %d in sReadFrame",token));
				break;
		}
	}	

	// Update all the dummy objects
	p_dummy=sGetDummyListHeadPointer(whichList);
	while (p_dummy)
	{
		if (whichList==PLAYBACK_DUMMY_LIST && !p_dummy->mp_rendered_model && p_dummy->m_is_displayed)
		{
			p_dummy->CreateModel();
		}
		
		p_dummy->Update();		
		p_dummy=p_dummy->mpNext;
	}	



	#ifdef CHECK_TOKEN_USAGE
	sTokenCount[OBJECT_ID]=0;
	int biggest=0;
	int worst_token=-1;
	for (int i=0; ibiggest)
		{
			biggest=sTokenCount[i];
			worst_token=i;
		}	
	}	
	printf("Worst token = %s\n",sGetTokenName((EReplayToken)worst_token));
	#endif




	return offset;
}

// After calling this, sBigBufferEndOffset will be guaranteed to point to enough contiguous space
// to hold frameSize bytes.
static void sMakeEnoughSpace(uint32 frameSize)
{
	// This loop makes one modification to the buffer each time around, and breaks out once
	// enough space has been freed.
	while (true)
	{
		if (sBigBufferStartOffset < sBigBufferEndOffset)
		{
			Dbg_MsgAssert(sBigBufferStartOffset==0,("sBigBufferStartOffset not zero ?"));
			
			// We know that all the space from sBigBufferEndOffset to the end of the buffer
			// is free to use, so check if that space is big enough.
			if (GetBufferSize()-sBigBufferEndOffset >= frameSize)
			{
				// It is!
				break;
			}
			else
			{
				// Wrap the end offset around to the start of the buffer, so that next time
				// around this loop frames at the start will start getting chomped up to make space.
				sBigBufferEndOffset=0;
				
				sBufferFilling=false;
			}	
		}
		else if (sBigBufferStartOffset > sBigBufferEndOffset)
		{
			// The space between end and start is free to use, so check if it is big enough. 
			if (sBigBufferStartOffset-sBigBufferEndOffset >= frameSize)
			{
				// It is!
				break;
			}
			else
			{
				// Skip the start offset over the frame it is pointing to.
				bool nothing_follows=false;
				uint32 old_start=sBigBufferStartOffset;
				sBigBufferStartOffset=sReadFrame(sBigBufferStartOffset,¬hing_follows,START_STATE_DUMMY_LIST);
				if (sBigBufferStartOffset==old_start)
				{
					// The start offset did not move, meaning it is pointing to empty space.
					// The only time this should happen is when the buffer is empty, in which
					// case the start and end offset should both be zero. So assert.
					Dbg_MsgAssert(0,("Start offset points to empty space, even though it is greater than end offset ?"));
					
					// Just in case this does ever happen on a release build, reset the buffer so that
					// it does not hang. All that will happen is that the replay will be lost.
					sDeleteDummies(START_STATE_DUMMY_LIST);
					sDeleteDummies(PLAYBACK_DUMMY_LIST);
					Dbg_MsgAssert(spReplayDummies==NULL,("Hey! spReplayDummies not NULL !!!"));
					sDeleteObjectTrackingInfo();
					ClearBuffer();
				}
				else
				{
					// Fill the frame just skipped over with BLANK's
					FillBuffer(old_start,sBigBufferStartOffset-old_start,BLANK);
					// Wrap around if necessary.
					if (nothing_follows)
					{
						sBigBufferStartOffset=0;
					}	
				}	
			}
		}
		else
		{
			// sBigBufferStartOffset equals sBigBufferEndOffset
			bool nothing_follows=false;
			uint32 old_start=sBigBufferStartOffset;
			sBigBufferStartOffset=sReadFrame(sBigBufferStartOffset,¬hing_follows,START_STATE_DUMMY_LIST);
			if (sBigBufferStartOffset==old_start)
			{
				// If the start offset did not move, it must be pointing to empty space.
				// The only time the start offset should point to empty space is when the 
				// buffer is totally empty, in which case the start offset would be expected to be zero.
				// So check that.
				Dbg_MsgAssert(sBigBufferStartOffset==0,("Expected sBigBufferStartOffset to be zero"));
				// Check that the frame size is not bigger than the entire buffer.
				Dbg_MsgAssert(GetBufferSize() >= frameSize,("Frame size %d too big for replay buffer",frameSize));
				// There is enough space, so break out.
				break;
			}
			else
			{
				// Fill the frame just skipped over with BLANK's
				FillBuffer(old_start,sBigBufferStartOffset-old_start,BLANK);
				// Wrap around if necessary.
				if (nothing_follows)
				{
					sBigBufferStartOffset=0;
				}	
			}	
		}
	}	
}

static void sWriteFrameBufferToBigBuffer()
{
	uint32 frame_size=spFrameBufferPos-spFrameBuffer;

	sMakeEnoughSpace(frame_size);

	// sBigBufferEndOffset is now guaranteed to point to enough contiguous space to
	// hold frame_size bytes, so write in the frame data.
	WriteIntoBuffer(spFrameBuffer,sBigBufferEndOffset,frame_size);
	sBigBufferEndOffset+=frame_size;
	// Wrap around if necessary.
	if (sBigBufferEndOffset==GetBufferSize())
	{
		sBigBufferEndOffset=0;
	}	
}

static void sWriteSingleToken(EReplayToken token)
{
	Dbg_MsgAssert(!Mdl::Skate::Instance()->IsMultiplayerGame(),("Replay Active in Multiplayer game!"));
	Dbg_MsgAssert(spFrameBufferPos+1 <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	
	*spFrameBufferPos++=token;
}

/*
static void sWriteUint8(EReplayToken token, uint8 v)
{
	Dbg_MsgAssert(spFrameBufferPos+2 <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	
	*spFrameBufferPos++=token;
	*spFrameBufferPos++=v;
}
*/

static void sWriteUint8(uint8 v)
{
	Dbg_MsgAssert(spFrameBufferPos+1 <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	*spFrameBufferPos++=v;
}

static void sWriteUint32(EReplayToken token, uint32 v)
{
	Dbg_MsgAssert(spFrameBufferPos+5 <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	
	*spFrameBufferPos++=token;
	spFrameBufferPos=Script::Write4Bytes(spFrameBufferPos, v);
}

static void sWriteUint32(uint32 v)
{
	Dbg_MsgAssert(spFrameBufferPos+4 <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	
	spFrameBufferPos=Script::Write4Bytes(spFrameBufferPos, v);
}

static void sWriteFloat(EReplayToken token, float f)
{
	Dbg_MsgAssert(spFrameBufferPos+5 <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	
	*spFrameBufferPos++=token;
	spFrameBufferPos=Script::Write4Bytes(spFrameBufferPos, f);
}

static void sWriteFloat(float f)
{
	Dbg_MsgAssert(spFrameBufferPos+4 <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	
	spFrameBufferPos=Script::Write4Bytes(spFrameBufferPos, f);
}

/*
static void sWrite2Floats(EReplayToken token, float a, float b)
{
	Dbg_MsgAssert(spFrameBufferPos+9 <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	
	*spFrameBufferPos++=token;
	spFrameBufferPos=Script::Write4Bytes(spFrameBufferPos, a);
	spFrameBufferPos=Script::Write4Bytes(spFrameBufferPos, b);
}
*/

static void sWriteVector(EReplayToken token, Mth::Vector &v)
{
	Dbg_MsgAssert(spFrameBufferPos+13 <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	
	*spFrameBufferPos++=token;
	spFrameBufferPos=Script::Write4Bytes(spFrameBufferPos, v[X]);
	spFrameBufferPos=Script::Write4Bytes(spFrameBufferPos, v[Y]);
	spFrameBufferPos=Script::Write4Bytes(spFrameBufferPos, v[Z]);
}

#if 0
static void sWriteString(EReplayToken token, const char *p_string)
{
	Dbg_MsgAssert(p_string,("NULL p_string"));
	int len=strlen(p_string);
	Dbg_MsgAssert(len<256,("String length too big, '%s'",p_string));
	Dbg_MsgAssert(spFrameBufferPos+2+len <= spFrameBuffer+FRAME_BUFFER_SIZE,("Replay frame buffer overflow"));
	
	*spFrameBufferPos++=token;
	*spFrameBufferPos++=len;
	for (int i=0; iGetVolumeType());
	sWriteFloat(p_volume->GetChannelVolume(0));
	sWriteFloat(p_volume->GetChannelVolume(1));
	if( p_volume->GetVolumeType() == Sfx::VOLUME_TYPE_5_CHANNEL_DOLBY5_1 )
	{
		sWriteFloat(p_volume->GetChannelVolume(2));
		sWriteFloat(p_volume->GetChannelVolume(3));
		sWriteFloat(p_volume->GetChannelVolume(4));
	}

	sWriteFloat(pitch);
	sWriteUint32(priority);
}

void WriteStopStream(int channel)
{
	if (sMode!=RECORD)
	{
		return;
	}
	sWriteSingleToken(STOP_STREAM);
	sWriteUint32(channel);
}

void WritePositionalStream(uint32 dummyId, uint32 streamNameChecksum, float dropoff, float volume, float pitch, int priority, int use_pos_info)
{
	if (sMode!=RECORD)
	{
		return;
	}	
	sWriteSingleToken(PLAY_POSITIONAL_STREAM);
	sWriteUint32(dummyId);
	sWriteUint32(streamNameChecksum);
	sWriteFloat(dropoff);
	sWriteFloat(volume);
	sWriteFloat(pitch);
	sWriteUint32(priority);
	sWriteUint32(use_pos_info);
}	

void WritePositionalSoundEffect(uint32 dummyId, uint32 soundName, float volume, float pitch, float dropOffDist)
{
	if (sMode!=RECORD)
	{
		return;
	}	
	sWriteSingleToken(PLAY_POSITIONAL_SOUND_EFFECT);
	sWriteUint32(dummyId);
	sWriteUint32(soundName);
	sWriteFloat(volume);
	sWriteFloat(pitch);
	sWriteFloat(dropOffDist);
}	

// Called from CSk3SfxManager::PlaySound, takes the same parameters, except for 'propogate' (sic)
// which I guess will always be false, since no replays in multiplayer.
void WriteSkaterSoundEffect(int whichArray, int surfaceFlag, const Mth::Vector &pos, 
							float volPercent)
{
	if (sMode!=RECORD)
	{
		return;
	}	
	sWriteSingleToken(PLAY_SKATER_SOUND_EFFECT);
	sWriteUint32(whichArray);
	sWriteUint32(surfaceFlag);
	sWriteFloat(pos[X]);
	sWriteFloat(pos[Y]);
	sWriteFloat(pos[Z]);
	sWriteFloat(volPercent);
}

void WritePlaySound(uint32 checksum, float volL, float volR, float pitch)
{
	if (sMode!=RECORD)
	{
		return;
	}	
	sWriteSingleToken(PLAY_SOUND);
	sWriteUint32(checksum);
	sWriteFloat(volL);
	sWriteFloat(volR);
	sWriteFloat(pitch);
}

static void sRecordSkaterCamera(CTrackingInfo *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info"));
	
	if (p_info->mFlags & TRACKING_INFO_FLAG_OBJECT_CREATED)
	{
		sWriteUint32(CREATE_OBJECT,ID_CAMERA);
		sWriteUint8(SKATE_TYPE_UNDEFINED);
		p_info->mFlags &= ~TRACKING_INFO_FLAG_OBJECT_CREATED;
	}	
	else
	{
		// Write in the object Id
		sWriteUint32(OBJECT_ID,ID_CAMERA);
	}	
	
	Obj::CSkater *p_skater=sGetSkater();
	Gfx::Camera *p_skater_camera=p_skater->GetActiveCamera();
	Dbg_MsgAssert(p_skater_camera,("NULL p_skater_camera"));
	
	Mth::Vector actual_angles;
	p_skater_camera->GetMatrix().GetEulers(actual_angles);
	p_info->RecordPositionAndAngleChanges(p_skater_camera->GetPos(),actual_angles,true);
}

static bool sIsFlipped(Obj::CMovingObject *p_movingObject)
{
	Dbg_MsgAssert(p_movingObject,("NULL p_movingObject"));
	
	if (p_movingObject->GetType()==SKATE_TYPE_SKATER)
	{
		return GetSkaterCorePhysicsComponentFromObject(p_movingObject)->GetFlag(Obj::FLIPPED);
	}
	else
	{
		Dbg_MsgAssert( 0, ( "Flipped-ness has been moved to animation component" ) );
		return false;
//		return p_movingObject->IsFlipped();
	}
}

static void sRecordCMovingObject(CTrackingInfo *p_info)
{
	Dbg_MsgAssert(p_info,("NULL p_info"));
	Dbg_MsgAssert(p_info->mPointerType==CMOVINGOBJECT,("Not a moving object?"));
	
	if (!p_info->mpMovingObject)
	{
		sWriteUint32(KILL_OBJECT,p_info->m_id);
		delete p_info;
		return;
	}	
	
	Obj::CMovingObject *p_moving_object=p_info->mpMovingObject;
	Nx::CModel* p_model=p_moving_object->GetModel();

	uint8 *p_start=spFrameBufferPos;
	
	if (p_info->mFlags & TRACKING_INFO_FLAG_OBJECT_CREATED)
	{
		sWriteUint32(CREATE_OBJECT,p_moving_object->GetID());
		sWriteUint8(p_moving_object->GetType());
		p_info->mFlags &= ~TRACKING_INFO_FLAG_OBJECT_CREATED;

		Obj::CAnimationComponent* pAnimComponent = GetAnimationComponentFromObject( p_moving_object );
        if ( pAnimComponent )
        {
            sWriteUint32(ANIM_SCRIPT_NAME,pAnimComponent->GetAnimScriptName());
        }
        else
        {
            sWriteUint32(ANIM_SCRIPT_NAME,0);
        }

		switch (p_moving_object->GetType())
		{
			case SKATE_TYPE_SKATER:
				sWriteUint32(SKELETON_NAME,0x5a9d2a0a/*Human*/);
				break;
			case SKATE_TYPE_CAR:
			{
				/*
				Obj::CCar *p_car=(Obj::CCar*)p_moving_object;
				Obj::CModelRestorationInfo *p_model_restoration_info=&p_car->m_model_restoration_info;
				
				sWriteUint32(SKELETON_NAME,p_model_restoration_info->mSkeletonName);
				sWriteString(MODEL_NAME,p_model_restoration_info->GetModelName());
				*/
				break;
			}	
			case SKATE_TYPE_PED:
			{
				/*
				Obj::CPed *p_ped=(Obj::CPed*)p_moving_object;
				Obj::CModelRestorationInfo *p_model_restoration_info=&p_ped->m_model_restoration_info;
				
				sWriteUint32(SKELETON_NAME,p_model_restoration_info->mSkeletonName);
				if (p_model_restoration_info->mProfileName)
				{
					sWriteUint32(PROFILE_NAME,p_model_restoration_info->mProfileName);
				}
				else
				{
					sWriteString(MODEL_NAME,p_model_restoration_info->GetModelName());
				}
				*/
				break;
			}	
			case SKATE_TYPE_GAME_OBJ:
			{
				/*
				Obj::CGameObj *p_game_ob=(Obj::CGameObj*)p_moving_object;
				Obj::CModelRestorationInfo *p_model_restoration_info=&p_game_ob->m_model_restoration_info;
			 
				sWriteUint32(SECTOR_NAME,p_model_restoration_info->mSectorName);
				sWriteUint32(SKELETON_NAME,p_model_restoration_info->mSkeletonName);
				sWriteString(MODEL_NAME,p_model_restoration_info->GetModelName());
				*/
				break;
			}
			/*
			// CBouncyObj has been replaced by CComposite object
			case SKATE_TYPE_BOUNCY_OBJ:
			{
				Obj::CBouncyObj *p_bouncy_ob=(Obj::CBouncyObj*)p_moving_object;
				Obj::CModelRestorationInfo *p_model_restoration_info=&p_bouncy_ob->m_model_restoration_info;
				
				sWriteUint32(SECTOR_NAME,p_model_restoration_info->mSectorName);
				break;
			}
			*/
			default:
				printf("Created object, type=%d\n",p_moving_object->GetType());
				break;
		}		

		p_info->mFlags &= ~TRACKING_INFO_FLAG_ACTIVE;
		p_info->mFlags &= ~TRACKING_INFO_FLAG_FLIPPED;
		p_info->mFlags &= ~TRACKING_INFO_FLAG_HOVERING;
		
		// Write out a SET_ATOMIC_STATES token to initialise the atomic states of the
		// dummy when it gets created later.
		if (p_model)
		{
			sWriteUint32(SET_ATOMIC_STATES,p_moving_object->GetID());
			sWriteUint32(p_model->GetGeomActiveMask());
		}	
	}	
	else
	{
		// Write in the object Id
		sWriteUint32(OBJECT_ID,p_moving_object->GetID());
	}	

	if (p_model && p_model->GetScale().GetX() != p_info->mScale)
	{
		// Some peds do use non 1 scale, eg the gorilla in the tram in the zoo
		// And the shark in sf2
		sWriteFloat(SET_SCALE,p_model->GetScale().GetX());
		p_info->mScale=p_model->GetScale().GetX();
	}	

	bool hovering=false;
	Obj::CMotionComponent *p_motion_component=GetMotionComponentFromObject(p_moving_object);
	if (p_motion_component)
	{
		hovering = p_motion_component->IsHovering();
	}

	
	
	if (hovering)
	{
		if (p_info->mFlags & TRACKING_INFO_FLAG_HOVERING)
		{
		}
		else
		{
			p_info->mFlags |= TRACKING_INFO_FLAG_HOVERING;
			Mth::Vector pos;
			p_motion_component->GetHoverOrgPos(&pos);
			
			sWriteSingleToken(HOVERING);
			sWriteFloat(pos[X]);
			sWriteFloat(pos[Y]);
			sWriteFloat(pos[Z]);
		}	
	}
	else
	{
		if (p_info->mFlags & TRACKING_INFO_FLAG_HOVERING)
		{
			// Need to support this? I don't think anything ever goes from hovering to not hovering?
		}
	}
						
	bool flipped=sIsFlipped(p_moving_object);
	if (flipped)
	{
		if (p_info->mFlags & TRACKING_INFO_FLAG_FLIPPED)
		{
		}
		else
		{
			p_info->mFlags |= TRACKING_INFO_FLAG_FLIPPED;
			sWriteSingleToken(FLIP);
		}	
	}
	else
	{
		if (p_info->mFlags & TRACKING_INFO_FLAG_FLIPPED)
		{
			p_info->mFlags &= ~TRACKING_INFO_FLAG_FLIPPED;
			sWriteSingleToken(UNFLIP);
		}
	}

	bool active=false;
	if (p_model)
	{
		active=p_model->GetActive();
	}
	if (active)
	{
		if (p_info->mFlags & TRACKING_INFO_FLAG_ACTIVE)
		{
		}
		else
		{
			p_info->mFlags |= TRACKING_INFO_FLAG_ACTIVE;
			sWriteSingleToken(MODEL_ACTIVE);
		}	
	}
	else
	{
		if (p_info->mFlags & TRACKING_INFO_FLAG_ACTIVE)
		{
			p_info->mFlags &= ~TRACKING_INFO_FLAG_ACTIVE;
			sWriteSingleToken(MODEL_INACTIVE);
		}
	}
	
	//Mth::Matrix objects_display_matrix=p_moving_object->GetDisplayMatrix();
	//objects_display_matrix[Mth::POS]=p_moving_object->GetPos();
	//Mth::Vector actual_pos=objects_display_matrix[Mth::POS];
	
	if (!hovering)
	{
		Mth::Vector actual_pos=p_moving_object->GetPos();
		Mth::Vector actual_angles;
		
		//if (p_moving_object->IsSpecialItem())
		//{
		//	printf("Recording exact pos of special item %x\n",p_moving_object->GetID());
		//}
			
		// GJ:  Need to reimplement special-case special item code
		// because the current implementation conflicts with our
		// new CCOmpositeObject model.
		if (p_info->m_id==ID_SKATER )
//		if (p_info->m_id==ID_SKATER || p_moving_object->IsSpecialItem())
		{
			p_moving_object->GetDisplayMatrix().GetEulers(actual_angles);
			p_info->RecordPositionAndAngleChanges(actual_pos,actual_angles,true);
		}
		else
		{
			if (p_moving_object->GetDisplayMatrix() != p_info->mLastMatrix)
			{
				p_moving_object->GetDisplayMatrix().GetEulers(actual_angles);
				p_info->mLastMatrix=p_moving_object->GetDisplayMatrix();
			}
			else
			{
				actual_angles[X]=p_info->mTrackerAngleX.GetActualLast();
				actual_angles[Y]=p_info->mTrackerAngleY.GetActualLast();
				actual_angles[Z]=p_info->mTrackerAngleZ.GetActualLast();
			}	
			p_info->RecordPositionAndAngleChanges(actual_pos,actual_angles);
		}	
	}
		
	// Do the animation stuff ...
#if 0
	uint8 *p_num_changes=NULL;
	uint8 num_changes=0;
	
	Obj::CAnimationComponent* pAnimComponent = GetAnimationComponentFromObject( p_moving_object );
	if ( pAnimComponent && pAnimComponent->HasAnims() )
	{
		sWriteSingleToken(PRIMARY_ANIM_CONTROLLER_CHANGES);
		p_num_changes=spFrameBufferPos;
		++spFrameBufferPos;
		num_changes=sWriteCAnimChannelChanges(&p_info->m_primaryController,pAnimComponent->GetPrimaryController());
		if (!num_changes)
		{
			spFrameBufferPos-=2;
		}
		else
		{
			p_info->m_primaryController=*pAnimComponent->GetPrimaryController();
			*p_num_changes=num_changes;
		}
	}	
	
	if (p_info->m_id==ID_SKATER)
	{
		for (int i=0; isSkaterTrackingInfo.m_degenerateControllers[i],pAnimComponent->GetDegenerateController(i));
            }
            else
            {
                num_changes=0;
            }
			if (!num_changes)
			{
				spFrameBufferPos-=3;
			}	
			else
			{
				p_info->sSkaterTrackingInfo.m_degenerateControllers[i]=*pAnimComponent->GetDegenerateController(i);
				*p_num_changes=num_changes;
			}
		}
		
		// Check for skater looping sound changes
		uint32 skater_looping_sound_id=0;
		uint32 skater_looping_sound_checksum=0;
		float skater_pitch_min;
		float skater_pitch_max;
		Obj::CSkater *p_skater=(Obj::CSkater*)p_moving_object;
		
		// NOTE: this information has moved to CSkaterLoopingSoundComponent
		p_skater->GetLoopingSoundInfo(	&skater_looping_sound_id,
										&skater_looping_sound_checksum,
										&skater_pitch_min,
										&skater_pitch_max);
		if (skater_looping_sound_id)
		{
			// Skater is playing a looping sound
			if (CTrackingInfo::sSkaterTrackingInfo.m_playing_looping_sound)
			{
				// Tracker already knows the skater was playing a looping sound, but
				// perhaps the name of the sound has changed.
				if (CTrackingInfo::sSkaterTrackingInfo.m_looping_sound_checksum != skater_looping_sound_checksum)
				{
					sWriteUint32(PLAY_LOOPING_SOUND,skater_looping_sound_checksum);
					CTrackingInfo::sSkaterTrackingInfo.m_looping_sound_checksum = skater_looping_sound_checksum;
				}
			}	
			else
			{
				sWriteUint32(PLAY_LOOPING_SOUND,skater_looping_sound_checksum);
				CTrackingInfo::sSkaterTrackingInfo.m_looping_sound_checksum = skater_looping_sound_checksum;
				CTrackingInfo::sSkaterTrackingInfo.m_playing_looping_sound=true;
			}	
			
			if (skater_pitch_min != CTrackingInfo::sSkaterTrackingInfo.m_pitch_min)
			{
				sWriteFloat(PITCH_MIN,skater_pitch_min);
				CTrackingInfo::sSkaterTrackingInfo.m_pitch_min = skater_pitch_min;
			}	
			if (skater_pitch_max != CTrackingInfo::sSkaterTrackingInfo.m_pitch_max)
			{
				sWriteFloat(PITCH_MAX,skater_pitch_max);
				CTrackingInfo::sSkaterTrackingInfo.m_pitch_max = skater_pitch_max;
			}	
		}
		else
		{
			if (CTrackingInfo::sSkaterTrackingInfo.m_playing_looping_sound)
			{
				sWriteSingleToken(STOP_LOOPING_SOUND);
				CTrackingInfo::sSkaterTrackingInfo.m_playing_looping_sound=false;
				CTrackingInfo::sSkaterTrackingInfo.m_looping_sound_checksum=0;
			}
		}		
	}	
#endif

	if (p_moving_object->GetType()==SKATE_TYPE_CAR)
	{
		float car_rotation_x, wheel_rotation_x, wheel_rotation_y;
		((Obj::CCar*)p_moving_object)->GetRotationValues(&car_rotation_x,&wheel_rotation_x,&wheel_rotation_y);
		
		p_info->mTrackerCarRotationX.WriteChanges(car_rotation_x,SET_CAR_ROTATION_X,SET_CAR_ROTATION_X_VEL);
		p_info->mTrackerWheelRotationX.WriteChanges(wheel_rotation_x,SET_WHEEL_ROTATION_X,SET_WHEEL_ROTATION_X_VEL);
		p_info->mTrackerWheelRotationY.WriteChanges(wheel_rotation_y,SET_WHEEL_ROTATION_Y,SET_WHEEL_ROTATION_Y_VEL);
	}
	
	// If no changes at all got written in for this object, then remove the object Id.
	// This is an important space optimization, because if this redundant info were not
	// removed then each frame would be several hundred bytes bigger.
	if (*p_start==OBJECT_ID && spFrameBufferPos-p_start==5)
	{
		spFrameBufferPos=p_start;
	}	
}

//static int record_frame=0;
static void sRecord()
{
	CTrackingInfo *p_info=spTrackingInfoHead;
	while (p_info)
	{
		CTrackingInfo *p_next=p_info->mpNext;

		if (p_info->m_id==ID_CAMERA)
		{
			sRecordSkaterCamera(p_info);
		}
		else
		{
			switch (p_info->mPointerType)
			{
				case CMOVINGOBJECT:
					sRecordCMovingObject(p_info);
					break;
				default:
					break;
			}
		}
									
		p_info=p_next;
	}	

	int current_blur=Nx::CEngine::sGetScreenBlur();
	if (current_blur != CTrackingInfo::sScreenBlurTracker)
	{
		sWriteSingleToken(SCREEN_BLUR);
		sWriteUint32(current_blur);
		CTrackingInfo::sScreenBlurTracker=current_blur;
	}	
	
	// REMOVE
	//printf("Size=%d\n",spFrameBufferPos-spFrameBuffer);
	
	sWriteFrameBufferToBigBuffer();
}

//static int playback_frame=0;
static void sPlayback(bool display)
{
	// If reached the end of the replay, do nothing.
	if (sReachedEndOfReplay)
	{
		return;
	}	
	if (sReplayPaused)
	{
		return;
	}	

	Obj::CSkater *p_skater=sGetSkater();
	// NGC must not replay vibrations, TRC requirement.
	if (Config::GetHardware() != Config::HARDWARE_NGC)
	{
		if (sNeedToInitialiseVibration)
		{
			if (!sCurrentState.mSkaterPaused)
			{
				for (int i=0; iGetDevice()->ActivateActuator(i,sCurrentState.mActuatorStrength[i]);
				}	
			}	
			sNeedToInitialiseVibration=false;
		}
	}
		
	// Read the next frame
	bool nothing_follows=false;
	sNextPlaybackFrameOffset=sReadFrame(sNextPlaybackFrameOffset,¬hing_follows,PLAYBACK_DUMMY_LIST);

	// The process of reading the frame will have updated all the dummy's.
	// Now display them & do other misc stuff like update sounds.
	CDummy *p_dummy=spReplayDummies;
	while (p_dummy)
	{
		p_dummy->UpdateMutedSounds();
		if (display)
		{
			p_dummy->DisplayModel();
		}	
		p_dummy=p_dummy->mpNext;
	}


	// Wrap around if reached the end of the replay buffer.
	if (sNextPlaybackFrameOffset==GetBufferSize())
	{
		sNextPlaybackFrameOffset=0;
	}
	
	// It is possible that the frame could be followed by some space filled with
	// BLANK, so skip over it until either a new frame is hit or we get back
	// to the start frame.
	while (true)
	{
		uint8 token;
		ReadFromBuffer(&token,sNextPlaybackFrameOffset,1);
		
		if (token != BLANK)
		{
			// Hit a new frame
			Dbg_MsgAssert(token==FRAME_START,("Expected token to be FRAME_START"));
			break;
		}
		
		// sBufferFilling is a flag used to indicate that the buffer is filling up for the
		// first time and has not cycled around yet.
		// Knowing this means we don't need to read each individual byte, since we know everything
		// will be blank till the end of the buffer, so just set sNextPlaybackFrameOffset to 0
		// straight away.
		// This is a speed optimization for the GameCube, since it has a 4meg replay buffer, and
		// it takes several seconds so read all those blanks.
		if (sBufferFilling)
		{
			Dbg_MsgAssert(sBigBufferStartOffset==0,("Expected sBigBufferStartOffset to be zero when the buffer is filling up?"));
			sNextPlaybackFrameOffset=0;
		}
		
		if (sNextPlaybackFrameOffset==sBigBufferStartOffset)
		{
			// Reached the start again.
			break;
		}
			
		++sNextPlaybackFrameOffset;
		if (sNextPlaybackFrameOffset==GetBufferSize())
		{
			sNextPlaybackFrameOffset=0;
		}
	}					
		
	// If we've got back to the start, then that is the end of the replay,
	// so kill all the dummys and restore everything the way it was.
	if (sNextPlaybackFrameOffset==sBigBufferStartOffset)
	{
		// Switch off any vibrations being done by the replay, so that they do not continue
		// while the end-replay menu is on screen.
		// Note that any vibrations that were being done in-game before the replay was run will
		// automatically get restored when the game is un-paused, so we do not need to restore them here.
		sSwitchOffVibrations();

		sStopReplayDummyLoopingSounds();
		
		// Now that the replay has ended, the player may want to save it to mem card.
		// So make sure that the start-state contains the correct active/visible status of all the sectors.
		Nx::CEngine::sWriteSectorStatusBitfield(sStartState.mpSectorStatus,SECTOR_STATUS_BUFFER_SIZE);
		
		Script::RunScript("EndOfReplay");
		sReachedEndOfReplay=true;
	}	
}

static void sEnsureTrackerExists( Obj::CObject *p_ob, void *p_data )
{
	Dbg_MsgAssert(p_ob,("NULL p_ob"));

	CTrackingInfo *p_info=sTrackingInfoHashTable.GetItem((uint32)p_ob->GetID());
	if (!p_info)
	{
		int type=p_ob->GetType();
		Dbg_MsgAssert(!(p_ob->GetID()==0 && type!=SKATE_TYPE_SKATER),("Non-skater object has an id of 0, type=%d\n",type));
		
		if (type==SKATE_TYPE_CAR ||
			type==SKATE_TYPE_PED ||
			type==SKATE_TYPE_BOUNCY_OBJ ||
			type==SKATE_TYPE_SKATER ||
			type==SKATE_TYPE_GAME_OBJ)
		{
			p_info=new CTrackingInfo;
			p_info->SetMovingObject((Obj::CMovingObject*)p_ob);
			p_info->mFlags |= TRACKING_INFO_FLAG_OBJECT_CREATED;
		}	
	}	
}

static void sEnsureCameraTrackerExists()
{
	CTrackingInfo *p_info=sTrackingInfoHashTable.GetItem((uint32)ID_CAMERA);
	if (!p_info)
	{
		p_info=new CTrackingInfo;
		p_info->SetSkaterCamera();
		p_info->mFlags |= TRACKING_INFO_FLAG_OBJECT_CREATED;
	}	
}

static void sHideForReplayPlayback( Obj::CObject *p_ob, void *p_data )
{
	Dbg_MsgAssert(p_ob,("NULL p_ob"));
	p_ob->HideForReplayPlayback();
}

static void sRestoreAfterReplayPlayback( Obj::CObject *p_ob, void *p_data )
{
	Dbg_MsgAssert(p_ob,("NULL p_ob"));
	p_ob->RestoreAfterReplayPlayback();
}

static void sClearLastPanelMessage()
{
	Script::CStruct *p_params=new Script::CStruct;
	p_params->AddChecksum("id",sLastRecordedPanelMessageID);
	
	Script::RunScript("kill_panel_message_if_it_exists",p_params);
	delete p_params;
}

static void sClearTrickAndScoreText()
{
	Front::CScreenElementManager* p_manager = Front::CScreenElementManager::Instance();
	int index=sGetSkater()->GetHeapIndex();
	Front::CTextBlockElement *p_text_block = (Front::CTextBlockElement *) p_manager->GetElement(0x44727dae/*the_trick_text*/ + index ).Convert();
	if (p_text_block)
	{
		p_text_block->SetText(NULL, 0);
	}
	Front::CTextElement *p_score_pot_text = (Front::CTextElement *) p_manager->GetElement(0xf4d3a70e + index ).Convert(); // "the_score_pot_text"
	if (p_score_pot_text)
	{
		p_score_pot_text->SetText("");
	}
	sTrickTextGotCleared=true;
}

bool ScriptClearTrickAndScoreText(Script::CStruct *pParams, Script::CScript *pScript)
{
	sClearTrickAndScoreText();
	return true;
}

bool ScriptPlaybackReplay(Script::CStruct *pParams, Script::CScript *pScript)
{
	sClearTrickAndScoreText();
	sClearLastPanelMessage();
	Nx::MiscFXCleanup();
	Nx::CEngine::sSetScreenBlur(0);
	
	// Check that there are no replay dummy's in existence at the moment.
	Dbg_MsgAssert(spReplayDummies==NULL,("Expected spReplayDummies to be NULL")); 
	sReplayDummysHashTable.IterateStart();
	Dbg_MsgAssert(sReplayDummysHashTable.IterateNext()==NULL,("Expected sReplayDummysHashTable to be empty")); 

	// Create the replay dummy's by making copies of the start-state dummy's.
	CDummy *p_source_dummy=spStartStateDummies;
	while (p_source_dummy)
	{
		CDummy *p_new_dummy=new CDummy(PLAYBACK_DUMMY_LIST,p_source_dummy->GetID());
		// Make a copy of the contents of the CDummy.
		// The assignement operator for CDummy is overloaded.
		*p_new_dummy=*p_source_dummy;
		
		p_source_dummy=p_source_dummy->mpNext;
	}	

	Nx::CEngine::sPrepareSectorsForReplayPlayback(pParams->ContainsFlag("store"));
	
	sCurrentState=sStartState;
	sNextPlaybackFrameOffset=sBigBufferStartOffset;
	sLastTrickTextChecksum=0;
	sMode=PLAYBACK;
	sReachedEndOfReplay=false;
	sReplayPaused=false;
	
	// NGC must not replay vibrations, TRC requirement.
	if (Config::GetHardware()==Config::HARDWARE_NGC)
	{
		sSwitchOffVibrations();
	}
		
	sNeedToInitialiseVibration=true;
	sUnpauseCertainScreenElements();
	
	return true;
}

bool ScriptHideGameObjects( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	pSkate->GetObjectManager()->ProcessAllObjects( sHideForReplayPlayback, NULL );
	return true;
}

bool ScriptShowGameObjects( Script::CStruct *pParams, Script::CScript *pScript )
{
	Mdl::Skate * pSkate = Mdl::Skate::Instance();
	pSkate->GetObjectManager()->ProcessAllObjects( sRestoreAfterReplayPlayback, NULL );
	
	// Make sure that the balance meters are not left on if the replay was quit during a balance.
	sGetSkaterScoreObject()->SetManualMeter(false);
	sGetSkaterScoreObject()->SetBalanceMeter(false);
	
	// Restore the active/visible state of the world sectors.
	Nx::CEngine::sRestoreSectorsAfterReplayPlayback();
	return true;
}

bool ScriptSwitchToReplayRecordMode( Script::CStruct *pParams, Script::CScript *pScript )
{
	if (Mdl::Skate::Instance()->IsMultiplayerGame())
	{
		return false;
	}	
	sMode=RECORD;
	return true;
}

bool ScriptSwitchToReplayIdleMode( Script::CStruct *pParams, Script::CScript *pScript )
{
	if (Mdl::Skate::Instance()->IsMultiplayerGame())
	{
		return false;
	}	
	sMode=NONE;
	return true;
}

bool RunningReplay()
{
	return sMode==PLAYBACK;
}

bool ScriptRunningReplay( Script::CStruct *pParams, Script::CScript *pScript )
{
	return RunningReplay();
}
	
bool ScriptPauseReplay( Script::CStruct *pParams, Script::CScript *pScript )
{
	sReplayPaused=true;	
	
	// Switch off any vibration while the replay is paused
	sSwitchOffVibrations();
	
	sStopReplayDummyLoopingSounds();
	
	// Then set this flag so that the sPlayback function will switch the vibration back
	// on if need be once the replay is unpaused.
	sNeedToInitialiseVibration=true;
	return true;
}

bool ScriptUnPauseReplay( Script::CStruct *pParams, Script::CScript *pScript )
{
	sReplayPaused=false;	
	sUnpauseCertainScreenElements();
	return true;
}
	
bool Paused()
{
	return sReplayPaused;
}
	
void AddReplayMemCardInfo(Script::CStruct *p_struct)
{
	Dbg_MsgAssert(p_struct,("NULL p_struct"));
	p_struct->AddChecksum("level_structure_name",sLevelStructureName);
}

void AddReplayMemCardSummaryInfo(Script::CStruct *p_struct)
{
	Dbg_MsgAssert(p_struct,("NULL p_struct"));
	
	Script::CStruct *p_level_def=Script::GetStructure(sLevelStructureName);
	Dbg_MsgAssert(p_level_def,("Could not find level def '%s'",Script::FindChecksumName(sLevelStructureName)));
	
	const char *p_level_name="";
	p_level_def->GetString("Name",&p_level_name);
	
	p_struct->AddString("LevelName",p_level_name);
}

bool ScriptRememberLevelStructureNameForReplays(Script::CStruct *pParams, Script::CScript *pScript)
{
	pParams->GetChecksum("level_structure_name",&sLevelStructureName,Script::ASSERT);
	return true;
}

void SetLevelStructureName(uint32 level_structure_name)
{
	sLevelStructureName=level_structure_name;
}

bool ScriptGetReplayLevelStructureName(Script::CStruct *pParams, Script::CScript *pScript)
{
	pScript->GetParams()->AddChecksum("level_structure_name",sLevelStructureName);
	return true;
}

Manager::Manager()
{
	Dbg_MsgAssert(NUM_REPLAY_TOKEN_TYPES<=256,("Too many token types !!"));
	
	mp_start_frame_task = new Tsk::Task< Manager > ( s_start_frame_code, *this,
										Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_REPLAY_START_FRAME );

	mp_end_frame_task = new Tsk::Task< Manager > ( s_end_frame_code, *this,
										Tsk::BaseTask::Node::vLOGIC_TASK_PRIORITY_REPLAY_END_FRAME );
										
	Mlp::Manager * mlp_manager = Mlp::Manager::Instance();
	mlp_manager->AddLogicTask( *mp_start_frame_task );
	mlp_manager->AddLogicTask( *mp_end_frame_task );
}

Manager::~Manager()
{
	delete mp_start_frame_task;
	delete mp_end_frame_task;
}

void Manager::s_start_frame_code( const Tsk::Task< Manager >& task )
{
	//Manager& man = task.GetData();
	spFrameBufferPos=spFrameBuffer;
	sWriteUint32(FRAME_START,Tmr::GetTime());
}

/*
static Tmr::CPUCycles sStartTime;
static int s_num_times=0;
static int s_time_index=0;
#define MAX_TIMES 60
static Tmr::CPUCycles spTimes[MAX_TIMES];

void NewTime(Tmr::CPUCycles t)
{
	spTimes[s_time_index++]=t;
	if (s_time_index>=MAX_TIMES)
	{
		s_time_index=0;
	}
	if (s_num_times& task )
{
	//Manager& man = task.GetData();
	#ifdef DISABLE_REPLAYS
	return;
	#endif

	if (Mdl::Skate::Instance()->IsMultiplayerGame())
	{
		return;
	}	
	
	switch (sMode)
	{
		case RECORD:
		{
			//sStartTime=Tmr::GetTimeInCPUCycles();

			sEnsureCameraTrackerExists();
					
			Mdl::FrontEnd* p_front = Mdl::FrontEnd::Instance();
			if (!p_front->GamePaused())
			{
				Mdl::Skate * p_skate = Mdl::Skate::Instance();
				p_skate->GetObjectManager()->ProcessAllObjects( sEnsureTrackerExists, NULL );
				
				sRecord();
			}	
			
			//Tmr::CPUCycles t_record_time=Tmr::GetTimeInCPUCycles()-sStartTime;
			//NewTime(t_record_time);
			break;
		}	
		case PLAYBACK:
		{
			bool left=false;
			bool right=false;
			Mdl::FrontEnd* pFront = Mdl::FrontEnd::Instance();
	
			for ( int i = 0; i < SIO::vMAX_DEVICES; i++ )
			{
				if ( CFuncs::CheckButton( pFront->GetInputHandler( i ), 0x85981897 ) ) // left
				{
					left=true;
				}	
				if ( CFuncs::CheckButton( pFront->GetInputHandler( i ), 0x4b358aeb ) ) // right
				{
					right=true;
				}	
			}
			
			if (left)
			{
				if (Tmr::GetVblanks()&1)
				{
					sPlayback();
				}	
			}
			else if (right)
			{
				sPlayback(false);
				sPlayback();
			}	
			else
			{
				sPlayback();
			}	
			break;
		}	
		default:
			break;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Gfx::CSkeleton* CDummy::GetSkeleton()
{
	if ( mp_skeletonComponent )
	{
		return mp_skeletonComponent->GetSkeleton();
	}

	return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool CDummy::LoadSkeleton( uint32 skeletonName )
{
    // temporarily moved this function from CModel,
    // in order to make it easier to split off
    // skeleton into its own component.

	Gfx::CSkeletonData* pSkeletonData = (Gfx::CSkeletonData*) Ass::CAssMan::Instance()->GetAsset( skeletonName, false );

	if ( !pSkeletonData )
	{
		Dbg_MsgAssert( 0, ("Unrecognized skeleton %s. (Is skeleton.q up to date?)", Script::FindChecksumName(skeletonName)) );
	}
    
	Dbg_MsgAssert( mp_skeletonComponent == NULL, ( "Model already has skeleton component.  Possible memory leak?" ) );    
	mp_skeletonComponent = new Obj::CSkeletonComponent;

    Script::CStruct* pTempStructure = new Script::CStruct;
    pTempStructure->AddChecksum( CRCD(0x222756d5,"skeleton"), skeletonName );
    
    mp_skeletonComponent->InitFromStructure( pTempStructure );
    delete pTempStructure;

#ifdef __NOPT_ASSERT__
	Gfx::CSkeleton* pSkeleton = mp_skeletonComponent->GetSkeleton();
    
    Dbg_Assert( pSkeleton );
    Dbg_Assert( pSkeleton->GetNumBones() > 0 );
#endif		// __NOPT_ASSERT__
	
	return true;
//    return mp_rendered_model->SetSkeleton( mp_skeletonComponent->GetSkeleton() );
}

}  // namespace Replay

#endif


================================================
FILE: Code/Sys/Replay/replay.h
================================================
#ifndef __SYS_REPLAY_H
#define __SYS_REPLAY_H

#define	__USE_REPLAYS__ 0

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#ifndef __CORE_DEFINES_H
#include 
#endif

#ifndef __CORE_TASK_H
#include 
#endif

#ifndef __CORE_MATH_VECTOR_H
#include 
#endif

#ifndef __GFX_ANIMCONTROLLER_H
#include 
#endif

#ifndef	__GFX_NXMODEL_H__
#include 
#endif

#ifndef __GEL_OBJECT_H
#include 
#endif

#ifndef __GEL_SOUNDFX_H
#include 
#endif

#ifndef __GEL_COMPONENTS_VIBRATIONCOMPONENT_H
#include 
#endif

#ifndef __OBJECTS_SKATER_H
#include 
#endif

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace Obj
{
	class CObject;
	class CSkeletonComponent;
}

namespace Script
{
	class CStruct;
	class CScript;
}
	
namespace Replay
{

class  Manager  : public Spt::Class
{
	Tsk::Task< Manager > *mp_start_frame_task;
	static	Tsk::Task< Manager >::Code 	s_start_frame_code;

	Tsk::Task< Manager > *mp_end_frame_task;
	static	Tsk::Task< Manager >::Code 	s_end_frame_code;

public:
	Manager();
	~Manager();
	
private:	
	DeclareSingletonClass( Manager );
};

// 128 allows for a max of 4096 sectors
#define SECTOR_STATUS_BUFFER_SIZE 128
struct SGlobalStates
{
public:
	SGlobalStates();
	~SGlobalStates();

	void Reset();
	// Warning! If any new members get added or removed, update VERSION_REPLAY in mcfuncs.cpp
	// Also update the Reset function.
	bool mManualMeterStatus;
	float mManualMeterValue;
	bool mBalanceMeterStatus;
	float mBalanceMeterValue;
	
	int mActuatorStrength[Obj::CVibrationComponent::vVB_NUM_ACTUATORS];
	uint32 mpSectorStatus[SECTOR_STATUS_BUFFER_SIZE];
	bool mSkaterPaused;
};

struct SReplayDataHeader
{
	uint32 mBufferStartOffset;
	uint32 mBufferEndOffset;
	int mNumStartStateDummies;
	SGlobalStates mStartState;
};

enum
{
	NUM_DEGENERATE_ANIMS=3
};	

enum
{
	DUMMY_FLAG_FLIPPED		= (1<<0),
	DUMMY_FLAG_HOVERING		= (1<<1),
	DUMMY_FLAG_ACTIVE		= (1<<2),
	// Keep the number of these <= 32
};
	
struct SSavedDummy
{
	uint32 m_id;
	int	m_type;
//	char mpModelName[MAX_MODEL_NAME_CHARS+1];	
	uint32 mSkeletonName;
	uint32 mProfileName;
	uint32 mAnimScriptName;
	uint32 mSectorName;
	uint32 mFlags;
	float  mScale;
	
	Mth::Vector m_pos;
	Mth::Vector m_vel;
	Mth::Vector m_angles;
	Mth::Vector m_ang_vel;
	float m_car_rotation_x;
	float m_car_rotation_x_vel;
	float m_wheel_rotation_x;
	float m_wheel_rotation_x_vel;
	float m_wheel_rotation_y;
	float m_wheel_rotation_y_vel;

	Gfx::CAnimChannel	m_primaryController;

    Gfx::CAnimChannel   m_degenerateControllers[NUM_DEGENERATE_ANIMS];
	uint32 mAtomicStates;
	// These only apply to the skater.
	uint32 m_looping_sound_checksum;
	float m_pitch_min;
	float m_pitch_max;
};

enum EDummyList
{
	START_STATE_DUMMY_LIST,
	PLAYBACK_DUMMY_LIST,
};

class CDummy : public Obj::CMovingObject
{
public:	
	CDummy(EDummyList whichList, uint32 id);
	virtual ~CDummy(void);

	// If any new members are added to CDummy, remember to update the following:
	// CDummy constructor, initialise new member
	// assignement operator, copy new member.
	// CDummy::Save function, save new member, and add to SSavedDummy, and update mem card version number
	// Replay::CreateDummyFromSaveData function, copy in new member.
	// Update VERSION_REPLAY in memcard.q
	CDummy& operator=( const CDummy& rhs );

	EDummyList m_list;
	CDummy *mpNext;
	CDummy *mpPrevious;
	
	void Update();
	void CreateModel();
	void DisplayModel();
	void UpdateMutedSounds();
	void UpdateLoopingSound();
	
	void Save(SSavedDummy *p_saved_dummy);

//	char mpModelName[MAX_MODEL_NAME_CHARS+1];	
	uint32 mSkeletonName;
	uint32 mProfileName;
	uint32 mAnimScriptName;
	uint32 mSectorName;

	bool m_is_displayed;
	
	float mScale;
	
	uint32 mFlags;

	int mHoverPeriod;
	
	Mth::Vector m_vel;
	Mth::Vector m_angles;
	Mth::Vector m_ang_vel;

	float m_car_rotation_x;
	float m_car_rotation_x_vel;
	float m_wheel_rotation_x;
	float m_wheel_rotation_x_vel;
	float m_wheel_rotation_y;
	float m_wheel_rotation_y_vel;
	
	Gfx::CAnimChannel 	m_primaryController;

    Gfx::CAnimChannel    m_degenerateControllers[NUM_DEGENERATE_ANIMS];
	
    Nx::CModel *mp_rendered_model;
	Gfx::Camera *mp_skater_camera;
	//Obj::CSkaterCam *mp_skater_camera;
	
	uint32 mAtomicStates;
	
	uint32 m_looping_sound_id;
	uint32 m_looping_sound_checksum;
	float m_pitch_min;
	float m_pitch_max;

    // TEMP
    bool LoadSkeleton( uint32 skeletonChecksum );
    Gfx::CSkeleton* GetSkeleton();
    Obj::CSkeletonComponent* mp_skeletonComponent;
};

// The bigger this is the better, since it will speed up saving of replays to mem card.
// A static buffer of this size exists though, so don't make it too big.
#define REPLAY_BUFFER_CHUNK_SIZE 2048


#if __USE_REPLAYS__  

// These functions have platform specific versions.
// On the GameCube for example, the buffer is in ARAM.
void AllocateBuffer();
void DeallocateBuffer();
bool BufferAllocated();
uint32 GetBufferSize();
void ReadFromBuffer(uint8 *p_dest, int bufferOffset, int numBytes);
void WriteIntoBuffer(uint8 *p_source, int bufferOffset, int numBytes);
void FillBuffer(int bufferOffset, int numBytes, uint8 value);
void WriteReplayDataHeader(SReplayDataHeader *p_header);
void ReadReplayDataHeader(const SReplayDataHeader *p_header);
void CreateDummyFromSaveData(SSavedDummy *p_saved_dummy);
CDummy *GetFirstStartStateDummy();

void ClearBuffer();
uint8 *GetTempBuffer();
void CreatePools();
void RemovePools();
void DeallocateReplayMemory();
bool ScriptDeleteDummies( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptHideGameObjects( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptShowGameObjects( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptRunningReplay( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptPauseReplay( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptUnPauseReplay( Script::CStruct *pParams, Script::CScript *pScript );

bool ScriptAllocateReplayMemory(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptDeallocateReplayMemory(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptStartRecordingAfresh(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptRememberLevelStructureNameForReplays(Script::CStruct *pParams, Script::CScript *pScript);
void SetLevelStructureName(uint32 level_structure_name);
bool ScriptGetReplayLevelStructureName(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptPlaybackReplay(Script::CStruct *pParams, Script::CScript *pScript);

bool ScriptReplayRecordSimpleScriptCall(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptRecordPanelMessage(Script::CStruct *pParams, Script::CScript *pScript);
bool ScriptSwitchToReplayRecordMode( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptSwitchToReplayIdleMode( Script::CStruct *pParams, Script::CScript *pScript );
bool ScriptClearTrickAndScoreText(Script::CStruct *pParams, Script::CScript *pScript);

void WritePadVibration(int actuator, int percent);
void WritePauseSkater();
void WriteUnPauseSkater();
void WriteScreenFlash(int viewport, Image::RGBA from, Image::RGBA to, float duration, float z, uint32 flags);
void WriteShatter(uint32 sectorName, bool on);
void WriteShatterParams(Mth::Vector& velocity, float area_test, float velocity_variance, float spread_factor, float lifetime, float bounce, float bounce_amplitude);
void WriteTextureSplat(Mth::Vector& splat_start, Mth::Vector& splat_end, float size, float lifetime, const char *p_texture_name, uint32 trail );
void WriteSectorActiveStatus(uint32 sectorName, bool active);
void WriteSectorVisibleStatus(uint32 sectorName, bool visible);
void WriteManualMeter(bool state, float value);
void WriteBalanceMeter(bool state, float value);
void WriteSparksOn();
void WriteSparksOff();
void WriteScorePotText(const char *p_text);
void WriteTrickText(const char **pp_text, int numStrings);
void WriteTrickTextPulse();
void WriteTrickTextCountdown();
void WriteTrickTextLanded();
void WriteTrickTextBail();
void WriteSetAtomicStates(uint32 id, uint32 mask);
void WritePlayStream(uint32 checksum, Sfx::sVolume *p_volume, float pitch, int priority);
void WriteStopStream(int channel);
void WritePositionalStream(uint32 dummyId, uint32 streamNameChecksum, float dropoff, float volume, float pitch, int priority, int use_pos_info);
void WritePositionalSoundEffect(uint32 dummyId, uint32 soundName, float volume, float pitch, float dropOffDist);
void WriteSkaterSoundEffect(int whichArray, int surfaceFlag, const Mth::Vector &pos, 
							float volPercent);
void WritePlaySound(uint32 checksum, float volL, float volR, float pitch);
void PrepareForReplayPlayback(bool hideObjects);
bool RunningReplay();
bool Paused();
void AddReplayMemCardInfo(Script::CStruct *p_struct);
void AddReplayMemCardSummaryInfo(Script::CStruct *p_struct);
SGlobalStates *GetStartState();

#else

// These functions have platform specific versions.
// On the GameCube for example, the buffer is in ARAM.
inline void AllocateBuffer(){}
inline void DeallocateBuffer(){}
//bool BufferAllocated(){}
//uint32 GetBufferSize(){}
inline void ReadFromBuffer(uint8 *p_dest, int bufferOffset, int numBytes){}
inline void WriteIntoBuffer(uint8 *p_source, int bufferOffset, int numBytes){}
inline void FillBuffer(int bufferOffset, int numBytes, uint8 value){}
inline void WriteReplayDataHeader(SReplayDataHeader *p_header){}
inline void ReadReplayDataHeader(const SReplayDataHeader *p_header){}
inline void CreateDummyFromSaveData(SSavedDummy *p_saved_dummy){}
//CDummy *GetFirstStartStateDummy(){}

inline void ClearBuffer(){}
//uint8 *GetTempBuffer(){}
inline void CreatePools(){}
inline void RemovePools(){}
inline void DeallocateReplayMemory(){}
inline bool ScriptDeleteDummies( Script::CStruct *pParams, Script::CScript *pScript ){return true;}
inline bool ScriptHideGameObjects( Script::CStruct *pParams, Script::CScript *pScript ){return true;}
inline bool ScriptShowGameObjects( Script::CStruct *pParams, Script::CScript *pScript ){return true;}
inline bool ScriptRunningReplay( Script::CStruct *pParams, Script::CScript *pScript ){return false;}
inline bool ScriptPauseReplay( Script::CStruct *pParams, Script::CScript *pScript ){return true;}
inline bool ScriptUnPauseReplay( Script::CStruct *pParams, Script::CScript *pScript ){return true;}

inline bool ScriptAllocateReplayMemory(Script::CStruct *pParams, Script::CScript *pScript){return true;}
inline bool ScriptDeallocateReplayMemory(Script::CStruct *pParams, Script::CScript *pScript){return true;}
inline bool ScriptStartRecordingAfresh(Script::CStruct *pParams, Script::CScript *pScript){return true;}

inline bool ScriptRememberLevelStructureNameForReplays(Script::CStruct *pParams, Script::CScript *pScript){return true;}
inline void SetLevelStructureName(uint32 level_structure_name){}
inline bool ScriptGetReplayLevelStructureName(Script::CStruct *pParams, Script::CScript *pScript){return true;}

inline bool ScriptPlaybackReplay(Script::CStruct *pParams, Script::CScript *pScript){return true;}

inline bool ScriptReplayRecordSimpleScriptCall(Script::CStruct *pParams, Script::CScript *pScript){return true;}
inline bool ScriptRecordPanelMessage(Script::CStruct *pParams, Script::CScript *pScript){return true;}
inline bool ScriptSwitchToReplayRecordMode( Script::CStruct *pParams, Script::CScript *pScript ){return true;}
inline bool ScriptSwitchToReplayIdleMode( Script::CStruct *pParams, Script::CScript *pScript ){return true;}
inline bool ScriptClearTrickAndScoreText(Script::CStruct *pParams, Script::CScript *pScript){return true;}

inline void WritePadVibration(int actuator, int percent){}
inline void WritePauseSkater(){}
inline void WriteUnPauseSkater(){}
inline void WriteScreenFlash(int viewport, Image::RGBA from, Image::RGBA to, float duration, float z, uint32 flags){}
inline void WriteShatter(uint32 sectorName, bool on){}
inline void WriteShatterParams(Mth::Vector& velocity, float area_test, float velocity_variance, float spread_factor, float lifetime, float bounce, float bounce_amplitude){}
inline void WriteTextureSplat(Mth::Vector& splat_start, Mth::Vector& splat_end, float size, float lifetime, const char *p_texture_name, uint32 trail ){}
inline void WriteSectorActiveStatus(uint32 sectorName, bool active){}
inline void WriteSectorVisibleStatus(uint32 sectorName, bool visible){}
inline void WriteManualMeter(bool state, float value){}
inline void WriteBalanceMeter(bool state, float value){}
inline void WriteSparksOn(){}
inline void WriteSparksOff(){}
inline void WriteScorePotText(const char *p_text){}
inline void WriteTrickText(const char **pp_text, int numStrings){}
inline void WriteTrickTextPulse(){}
inline void WriteTrickTextCountdown(){}
inline void WriteTrickTextLanded(){}
inline void WriteTrickTextBail(){}
inline void WriteSetAtomicStates(uint32 id, uint32 mask){}
inline void WritePlayStream(uint32 checksum, Sfx::sVolume *p_volume, float pitch, int priority){}
inline void WriteStopStream(int channel){}
inline void WritePositionalStream(uint32 dummyId, uint32 streamNameChecksum, float dropoff, float volume, float pitch, int priority, int use_pos_info){}
inline void WritePositionalSoundEffect(uint32 dummyId, uint32 soundName, float volume, float pitch, float dropOffDist){}
inline void WriteSkaterSoundEffect(int whichArray, int surfaceFlag, const Mth::Vector &pos, 
							float volPercent){}
inline void WritePlaySound(uint32 checksum, float volL, float volR, float pitch){}
inline void PrepareForReplayPlayback(bool hideObjects){}
inline bool RunningReplay(){return false;}
inline bool Paused(){return false;}
inline void AddReplayMemCardInfo(Script::CStruct *p_struct){}
inline void AddReplayMemCardSummaryInfo(Script::CStruct *p_struct){}
//SGlobalStates *GetStartState(){}
#endif

} // namespace Replay

#endif	// __SYS_REPLAY_H






================================================
FILE: Code/Sys/SIO/NGPS/p_keyboard.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS Library												**
**																			**
**	Module:			SYS (SYS_) 												**
**																			**
**	File name:		keyboard.cpp											**
**																			**
**	Created:		03/08/2001	-	gj										**
**																			**
**	Description:	USB Keyboard interface									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 

#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace SIO
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

#define USBKEYBD_ARG "keybd=8\0debug=1"

static	unsigned char *s_old_status = NULL;
static	USBKBINFO_t	s_info;
static	u_char 		s_location[7];
static	USBKBDATA_t s_kdata;
static	int			s_max_connect;
static	char		s_makes[32];
static	int			s_num_makes;
static  bool		s_active = true;
static  bool		s_enabled = true;

static 	Tsk::Task< int >*		s_kb_read_logic_task;

enum
{
	vKEYCODE_F1		= 	0x803A,
	vKEYCODE_F2		= 	0x803B,
	vKEYCODE_F3		= 	0x803C,
	vKEYCODE_F4		= 	0x803D,
	vKEYCODE_RIGHT	=	0x804F,
	vKEYCODE_LEFT	=	0x8050,
	vKEYCODE_DOWN	=	0x8051,
	vKEYCODE_UP		=	0x8052,
	vKEYCODE_ESC	= 	0x8029,
};

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/



/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

static void s_init_newkeyboard( int i );
static int s_capture_input( int i, char* makes );

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void	kb_read_logic_code ( const Tsk::Task< int >& task )
{
	int i, result;

	if (!s_active || !s_enabled)
	{
		return;
	}
    
	s_num_makes = 0;

    result = sceUsbKbGetInfo( &s_info );
	if( result != USBKB_OK ) 
	{
		Dbg_Printf( "Error%d : sceUsbKbGetInfo\n", result );
		return;
	}
	sceUsbKbSync( USBKB_WAIT, &result );
	if( result != USBKB_OK ) 
	{
		Dbg_Printf( "Error%d : sceUsbKbSync\n", result );
		return;
	}
  
    // reading keyboard
	for( i = 0; i < s_max_connect; i++)  
	{
		if( s_info.status[i] == 0 ) 
		{ 
			continue; 
		}  // not connected

		if( s_old_status[i] == 0 ) 
		{
			Dbg_Printf( "New keyboard %d is connected\n", i );
			s_init_newkeyboard(i);
		}

		result = sceUsbKbRead( i, &s_kdata );
		if( result != USBKB_OK ) 
		{
			Dbg_Printf( "Error%d : sceUsbKbRead\n", result );
			continue;
		}
		sceUsbKbSync( USBKB_WAIT, &result );
		if( result != USBKB_OK ) 
		{
			Dbg_Printf( "Error%d : sceUsbKbSync\n",result);
			continue;
		}

		if( s_kdata.len == 0 ) 
		{
			continue;
		}

		result = sceUsbKbGetLocation( i, s_location );
		if( result != USBKB_OK ) 
		{
			Dbg_Printf( "Error%d : sceUsbKbGetLocation\n", result );
			continue;
		}
		
		sceUsbKbSync( USBKB_WAIT, &result );
		if( result != USBKB_OK ) 
		{
			Dbg_Printf( "Error%d : sceUsbKbSync\n", result );
			continue;
		}
		
		s_num_makes = s_capture_input( i, s_makes );
	}
  
	for( i = 0; i < s_max_connect; i++ ) 
	{ 
		s_old_status[i] = s_info.status[i]; 
	}
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int KeyboardInit(void)
{
	
	
	int result;
    char *option = USBKEYBD_ARG;
	int i;
	Mlp::Manager * mlp_man = Mlp::Manager::Instance();
	

	//sceSifInitRpc(0);
	
	if (Config::CD())
	{
		char  path[128];
		sprintf(path,"cdrom0:\\%sIOP\\USBKB.IRX",Config::GetDirectory()); 

		result = sceSifLoadModule(
				   path,
					sizeof( USBKEYBD_ARG ) + 1, option );
	}
	else
	{
		result = sceSifLoadModule(
				   "host0:IOPModules/usbkb.irx",
					sizeof( USBKEYBD_ARG ) + 1, option );
	}				
	
	if( result < 0 )
    {
        Dbg_MsgAssert( 0,( "EE:Can't load module usbkb.irx\n" ));
		return -1;
	}

    result = sceUsbKbInit( &s_max_connect );
    if( result == USBKB_NG ) 
	{
		Dbg_Printf( "Initialize error\n" );
		return -1;
	}

    s_old_status = new unsigned char[ s_max_connect ];
    for( i = 0; i < s_max_connect; i++ ) 
	{ 
		s_old_status[i] = 0; 
	}

	int arg;
	// Just send an int because Tsk::Task is a template and needs some sort of
	// specific data type. So I just chose int and pass an int even though I don't use it
	s_kb_read_logic_task = new Tsk::Task< int > ( kb_read_logic_code, arg );
	mlp_man->AddSystemTask( *s_kb_read_logic_task );
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int KeyboardDeinit(void)
{
	delete s_kb_read_logic_task;
	delete s_old_status;

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int KeyboardRead( char* makes )
{
	int i;
	for( i = 0; i < s_num_makes; i++ )
	{
		makes[i] = s_makes[i];
	}

	return s_num_makes;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void KeyboardClear( void )
{
	s_num_makes = 0;
}


void SetKeyboardActive(bool active)
{
	s_active = active;
}

void EnableKeyboard( bool enable )
{
	s_enabled = enable;
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static void s_init_newkeyboard( int i )
{       
	int result;
	
	result = sceUsbKbSetLEDStatus( i, USBKB_LED_NUM_LOCK );
	if( result != USBKB_OK ) 
	{
		Dbg_Printf( "Error%d : sceUsbKbSetLEDStatus\n", result );
	} 
	else 
	{
		sceUsbKbSync(USBKB_WAIT,&result);
		if( result != USBKB_OK ) 
		{
			Dbg_Printf( "Error%d : sceUsbKbSync\n", result );
		}
	}
	
	result = sceUsbKbSetLEDMode( i, USBKB_LED_MODE_AUTO1 );
	if( result != USBKB_OK ) 
	{
		Dbg_Printf( "Error%d : sceUsbKbSetLEDMode\n", result );
	} 
	else 
	{
		sceUsbKbSync( USBKB_WAIT, &result );
		if( result != USBKB_OK ) 
		{
			Dbg_Printf( "Error%d : sceUsbKbSync\n", result );
		}
	}
	
	sceUsbKbSetRepeat( i, 30, 2 );
	sceUsbKbSetCodeType( i, USBKB_CODETYPE_ASCII );
	sceUsbKbSetArrangement( i, USBKB_ARRANGEMENT_101 );//USBKB_ARRANGEMENT_106 );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

static int s_capture_input( int i, char* makes )
{
	u_short kcode;
	int j, index;
	
	//Dbg_Printf( "usbkeybd%d : ", i );
	
	//Dbg_Printf( "port=" );
	//for( k = 0; k < 7 && s_location[k] != 0; k++ )
	//{
		//Dbg_Printf( "%s%d", ((k)? ",": "" ), s_location[k] );
	//}
	  
	
	//Dbg_Printf( " : LED[%02X] ", s_kdata.led );
	
	//Dbg_Printf( "MKEY[%02X] ", s_kdata.mkey );
	
	//Dbg_Printf( "KEY[" );
	
	index = 0;
	for( j=0; j < s_kdata.len; j++ ) 
	{
		kcode = s_kdata.keycode[j];
		if( kcode & USBKB_RAWDAT ) 
		{
			//Dbg_Printf( "%04X ", kcode );
			switch( kcode )
			{   
				case vKEYCODE_F1:
					makes[index++] = vKB_F1;
					break;
				case vKEYCODE_F2:
					makes[index++] = vKB_F2;
					break;
				case vKEYCODE_F3:
					makes[index++] = vKB_F3;
					break;
				case vKEYCODE_F4:
					makes[index++] = vKB_F4;
					break;
				case vKEYCODE_RIGHT:
					makes[index++] = vKB_RIGHT;
					break;
				case vKEYCODE_LEFT:
					makes[index++] = vKB_LEFT;
					break;
				case vKEYCODE_DOWN:
					makes[index++] = vKB_DOWN;
					break;
				case vKEYCODE_UP:
					makes[index++] = vKB_UP;
					break;
				case vKEYCODE_ESC:
					makes[index++] = vKB_ESCAPE;
					break;
			}
			continue;
		}
		if( kcode & USBKB_KEYPAD ) 
		{
			// 10 key
			if((kcode & 0x00ff) == '\n')
			{ 
				//Dbg_Printf( "(\\n)" ); 
				makes[index++] = vKB_ENTER;
				continue;
			}
			//Dbg_Printf( "(%c) ", kcode & ~USBKB_KEYPAD );
			makes[index++] = kcode & ~USBKB_KEYPAD;
			continue;
		}
		// Normal key
		if((kcode & 0x00ff) == '\0' ) 
		{ 
			//Dbg_Printf( "\'\\0\'" ); 
			continue;
		}
		if((kcode & 0x00ff) == '\n' ) 
		{ 
			//Dbg_Printf( "\'\\n\'" ); 
			makes[index++] = vKB_ENTER;
			continue;
		}
		if((kcode & 0x00ff) == '\t' ) 
		{ 
			//Dbg_Printf( "\'\\t\'" ); 
			continue;
		}
		if((kcode & 0x00ff) == '\b' ) 
		{ 
			//Dbg_Printf( "\'\\b\'" ); 
			makes[index++] = vKB_BACKSPACE;
			continue;
		}
		//Dbg_Printf( "\'%c\' ", kcode );
		makes[index++] = kcode;
	}
	//Dbg_Printf( "]\n" ); 

	return index;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace SIO


================================================
FILE: Code/Sys/SIO/XBox/p_keyboard.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS Library												**
**																			**
**	Module:			SYS (SYS_) 												**
**																			**
**	File name:		keyboard.cpp											**
**																			**
**	Created:		03/08/2001	-	gj										**
**																			**
**	Description:	USB Keyboard interface									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace SIO
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/


/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int KeyboardInit(void)
{
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int KeyboardDeinit(void)
{
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int KeyboardRead( char* makes )
{
	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void KeyboardClear( void )
{
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace SIO


================================================
FILE: Code/Sys/SIO/XBox/p_siodev.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS Library												**
**																			**
**	Module:			SYS (SYS_) 												**
**																			**
**	File name:		siodev.cpp												**
**																			**
**	Created:		05/26/2000	-	spg										**
**																			**
**	Description:	Generic input device									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
         
#include 
#include 
#include 
#include 
#include 
#include 

//#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace SIO
{


// Maps Xbox thumbstick values in the range [-32767, 32767] to PS2 thumstick values in the range [0, 255].
#define XboxThumbToPS2Thumb( v )	(( v + 32767 ) / 256 )

// K: Index of the last pad to have a button pressed. Used when a level is chosen in the skateshop.
// The pad used to control the player is whatever pad was used to choose the level.
int gLastPadPressed=0;

/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

static void set_xbox_actuators( HANDLE handle, unsigned short left_motor, unsigned short right_motor )
{
	// Want this to be static since otherwise it would potentially go out of scope and be written to.
	static XINPUT_FEEDBACK input_feedback[32];
	static int next_index			= 0;
	
	// The Ps2 left motor is the high frequency motor, the right motor is a simple on/off low frequency motor.
	// On the Xbox it is the reverse (although the left motor has more control than simple on/off).
	
	input_feedback[next_index].Header.dwStatus			= 0;
	input_feedback[next_index].Header.hEvent			= NULL;
	input_feedback[next_index].Rumble.wLeftMotorSpeed	= right_motor;
	input_feedback[next_index].Rumble.wRightMotorSpeed	= left_motor;

	DWORD status = XInputSetState( handle, &input_feedback[next_index] );
	Dbg_Assert(( status == ERROR_IO_PENDING ) || ( status == ERROR_SUCCESS ) || ( status == ERROR_DEVICE_NOT_CONNECTED ));

	// Cycle array member.
	if( ++next_index >= 32 )
	{
		next_index = 0;
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
    
void Device::process( void )
{
	m_plugged_in = false;

    switch( m_state )
    {
    case vIDLE:
        break;

    case vBUSY:
        wait();
        break;

    case vACQUIRING:
        acquisition_pending();
        break;

    case vACQUIRED:
        read_data();
        break;

    default:
        break;

    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::wait( void )
{
    Dbg_MsgAssert(( m_next_state >= 0 ) && ( m_next_state <= vNUM_STATES ),( "No next state set for wait state" ));

	if ( get_status() != vNOTREADY )
	{
		m_state = m_next_state;
		m_next_state = vIDLE;
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::read_data ( void )
{
	XINPUT_STATE	xis;
	HRESULT			hr;

	m_plugged_in = false;

	if( m_data.m_handle == NULL )
	{
		return;
	}

	hr = XInputGetState( m_data.m_handle, &xis );
	if( hr != ERROR_SUCCESS )
	{
		XInputClose( m_data.m_handle );
		m_data.m_handle	= NULL;
		m_data.m_valid	= false;
		Unacquire();
		Acquire();
		return;
	}

	m_data.m_valid	= true;
	m_plugged_in	= true;

	// Convert this data back into PS2-style 'raw' data for a DUALSHOCK2.
	m_data.m_control_data[0] = 0;						// 'Valid' info flag.
	m_data.m_control_data[1] = ( 0x07 << 4 ) | 16;		// DUALSHOCK2 id + data length.

	m_data.m_control_data[2] = 0xFF;					// Turn off all buttons by default.
	m_data.m_control_data[3] = 0xFF;					// Turn off all buttons by default.

	m_data.m_control_data[2] ^= ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_START ) ? ( 1 << 3 ) : 0;		// XBox 'Start' = PS2 'Start'.
	m_data.m_control_data[2] ^= ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_BACK ) ? ( 1 << 0 ) : 0;		// XBox 'Back' = PS2 'Select'.
	m_data.m_control_data[2] ^= ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB ) ? ( 1 << 1 ) : 0;	// Xbox 'Left Stick Button' = PS2 'Left Stick Button'.
	m_data.m_control_data[2] ^= ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB ) ? ( 1 << 2 ): 0;	// Xbox 'Right Stick Button' = PS2 'Right Stick Button'.

	// TRC 1.4-1-26 to eliminate crosstalk must disregard values below 0x20.
	m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_Y] >= 0x20 ) ? ( 1 << 4 ) : 0;	// XBox 'Y' = PS2 'Triangle'.
	m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_B] >= 0x20 ) ? ( 1 << 5 ) : 0;	// XBox 'B' = PS2 'Circle'.
	m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_A] >= 0x20 ) ? ( 1 << 6 ) : 0;	// XBox 'A' = PS2 'X'.
	m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_X] >= 0x20 ) ? ( 1 << 7 ) : 0;	// XBox 'X' = PS2 'Square'.
		
	if( Ed::CParkEditor::Instance()->EditingCustomPark())
	{
		// In the Park Editor, black and white buttons are L1 and L2, triggers are R1 and R2... unless the gap name keyboard is active :(
		bool							keyboard_active		= false;
		Front::CScreenElementManager	*p_screen_elem_man	= Front::CScreenElementManager::Instance();
		if( p_screen_elem_man )
		{
			Front::CScreenElement		*p_keyboard			= p_screen_elem_man->GetElement( Script::GenerateCRC( "keyboard_anchor" ) , Front::CScreenElementManager::DONT_ASSERT );
			if( p_keyboard )
			{
				keyboard_active	= true;
			}
		}
		
		if( keyboard_active	)
		{
			m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] > 0x20 ) ? ( 1 << 1 ) : 0;		// XBox  'Right Trigger'= PS2 'R2'.
		}
		else
		{
			m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] > 0x20 ) ? ( 1 << 2 ) : 0;		// XBox  'Right Trigger'= PS2 'L1'.
		}
		
		m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] > 0x20 ) ? ( 1 << 0 ) : 0;			// XBox 'Left Trigger' = PS2 'L2'.
		// New! Black and white are no longer mapped to R2 and L2 but are treated as seperate buttons
		//m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_WHITE] > 0x20 ) ? ( 1 << 1 ) : 0;					// XBox 'White' = PS2 'R2'.
		//m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_BLACK] > 0x20 ) ? ( 1 << 3 ) : 0;					// XBox 'Black' = PS2 'R1'.
	}
	else
	{
		// New! Black and white are no longer mapped to R2 and L2 but are treated as seperate buttons

		// Outside of the Park Editor, either black or white buttons act as L1 and R1...
		//m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_WHITE] > 0x20 ) ? (( 1 << 2 ) | ( 1 << 3 )) : 0;	// XBox 'White' = PS2 'L1' + PS2 'R1'.
//		m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_BLACK] > 0x20 ) ? (( 1 << 2 ) | ( 1 << 3 )) : 0;	// XBox 'Black'= PS2 'L1' + PS2 'R1'.
		//m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_BLACK] > 0x20 ) ? (( 1 << 0 ) | ( 1 << 1 )) : 0;	// XBox 'Black'= PS2 'L2' + PS2 'R2'.
		
		// ...triggers function as both L1/L2 and R1/R2.		
		m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] > 128 ) ? ( 1 << 0 ) : 0;				// XBox 'Left Trigger pressed down > halfway' = PS2 'L2'.
		m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] > 128 ) ? ( 1 << 1 ) : 0;			// XBox 'Right Trigger pressed down > halfway' = PS2 'R2'.

		m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] > 0x20 ) ? ( 1 << 2 ) : 0;				// XBox 'Left Trigger' = PS2 'L1'.
		m_data.m_control_data[3] ^= ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] > 0x20 ) ? ( 1 << 3 ) : 0;				// XBox 'Right Trigger' = PS2 'R1'.
	}

	// Make analog buttons full depression if pressed.
	// Xbox analog buttons return analog value in range [0, 255].
	m_data.m_control_data[12] = ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_Y] > 0x20 ) ? xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_Y] : 0;							// XBox 'Y' = PS2 Analog 'Triangle'.
	m_data.m_control_data[13] = ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_B] > 0x20 ) ? xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_B] : 0;							// XBox 'B' = PS2 Analog 'Circle'.
	m_data.m_control_data[14] = ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_A] > 0x20 ) ? xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_A] : 0;							// XBox 'A' = PS2 Analog 'X'.
	m_data.m_control_data[15] = ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_X] > 0x20 ) ? xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_X] : 0;							// XBox 'X' = PS2 'Square'.
	m_data.m_control_data[16] = ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] > 0x20 ) ? xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER] : 0;	// XBox 'Left Trigger' = PS2 Analog 'L1'.
	m_data.m_control_data[17] = ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] > 0x20 ) ? xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER] : 0;	// XBox 'Right Trigger' = PS2 Analog 'R1'.
	// Black and white are no longer mapped to R2 and L2, but are treated as separate buttons.
	//m_data.m_control_data[18] = ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_WHITE] > 0x20 ) ? xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_WHITE] : 0;					// XBox 'White' = PS2 Analog 'L2'.
	//m_data.m_control_data[19] = ( xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_BLACK] > 0x20 ) ? xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_BLACK] : 0;					// XBox 'Black' = PS2 Analog 'R2'.
	m_data.m_control_data[18]=0;
	m_data.m_control_data[19]=0;

	// Zero out the d-pad pressure values.
	m_data.m_control_data[8]	= 0x00;
	m_data.m_control_data[9]	= 0x00;
	m_data.m_control_data[10]	= 0x00;
	m_data.m_control_data[11]	= 0x00;

	// Handle 8 position d-pad.
	m_data.m_control_data[2] ^= ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) ? ( 1 << 4 ) : 0;		// XBox 'DPad Up' = PS2 'DPad Up'.
	m_data.m_control_data[10] = ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP ) ? 0xFF : 0;			// XBox 'DPad Up' = PS2 'DPad Up'.
	m_data.m_control_data[2] ^= ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ) ? ( 1 << 5 ) : 0;	// XBox 'DPad Right' = PS2 'DPad Right'.
	m_data.m_control_data[8] = ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT ) ? 0xFF : 0;			// XBox 'DPad Right' = PS2 'DPad Right'.
	m_data.m_control_data[2] ^= ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) ? ( 1 << 6 ) : 0;	// XBox 'DPad Down' = PS2 'DPad Down'.
	m_data.m_control_data[11] = ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN ) ? 0xFF : 0;			// XBox 'DPad Down' = PS2 'DPad Down'.
	m_data.m_control_data[2] ^= ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) ? ( 1 << 7 ) : 0;	// XBox 'DPad Left' = PS2 'DPad Left'.
	m_data.m_control_data[9] = ( xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT ) ? 0xFF : 0;			// XBox 'DPad Left' = PS2 'DPad Left'.

	// Xbox thumbsticks return analog value in range [-32767, 32767].
	m_data.m_control_data[4] = XboxThumbToPS2Thumb( xis.Gamepad.sThumbRX );	// Analog stick right (X direction).
	m_data.m_control_data[5] = XboxThumbToPS2Thumb( -xis.Gamepad.sThumbRY );	// Analog stick right (Y direction).
	m_data.m_control_data[6] = XboxThumbToPS2Thumb( xis.Gamepad.sThumbLX );	// Analog stick left (X direction).
	m_data.m_control_data[7] = XboxThumbToPS2Thumb( -xis.Gamepad.sThumbLY );	// Analog stick left (Y direction).

	// K: Use m_control_data[20] to store the state of the black & white buttons.
	m_data.m_control_data[20]=0;
	if (xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_BLACK] > 0x20)
	{
		m_data.m_control_data[20] |= (1<<0);
	}	
	if (xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_WHITE] > 0x20)
	{
		m_data.m_control_data[20] |= (1<<1);
	}	

	
	uint32 buttons = 0xFFFF ^ (( m_data.m_control_data[2] << 8 ) | m_data.m_control_data[3] );

	// Skate3 specific code, removed for now.
	if( buttons )
	{
		m_pressed = true;	
	}
	else
	{
		m_pressed = false;
	}	
		

	if(( xis.Gamepad.wButtons & XINPUT_GAMEPAD_START) || xis.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_A] >= 0x20 )
	{
		m_start_or_a_pressed = true;
	}
	else
	{
		m_start_or_a_pressed = false;
	}	
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Device::IsPluggedIn( void )
{
	return m_plugged_in;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::acquisition_pending( void )
{
    int status;

	if( m_data.m_handle == NULL )
	{
		++m_unplugged_counter;

		// Retry to connect every second or so.
		if(( m_unplugged_counter & 0x3F ) == m_unplugged_retry )
		{
			m_data.m_handle = XInputOpen( XDEVICE_TYPE_GAMEPAD, m_data.m_port, XDEVICE_NO_SLOT, NULL );
		}
		
		if( m_data.m_handle == NULL )
		{
			return;
		}
	}

    status = get_status();
                        
    if(( status == vCONNECTED ) || ( status == vREADY ))
    {
        // Sucessful.  Now query the controller for capabilities.
		m_unplugged_counter = 0;
        query_capabilities();
    }
        
	// failed to acquire controller
	// stay in this state and continue to try to acquire it or prompt the user if it is mandatory
	// that they have a controller in   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::query_capabilities( void )
{   
	// Currently assumes standard XBox controller.
    int id = vANALOG_CTRL;
    
    m_data.m_type = id;

	switch( id )
	{   
		case vANALOG_CTRL:
		{
			m_data.m_caps.SetMask( mANALOG_BUTTONS );

			m_data.m_num_actuators		= 2;
			m_data.m_actuator_max[0]	= 255;
			m_data.m_actuator_max[1]	= 255;
			m_data.m_caps.SetMask( mACTUATORS );

			m_state = vBUSY;
			m_next_state = vACQUIRED;
			return;
		}

		default:
		{
			Dbg_Message( "Detected Controller of unknown type in %d:%d", m_data.m_port, m_data.m_slot );
			break;
		}
	}
    m_state = vACQUIRED;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Device::get_status( void )
{
	if( m_data.m_handle )
	{
		XINPUT_STATE state;
		if( XInputGetState( m_data.m_handle, &state ) == ERROR_SUCCESS )
		{
			return vREADY;
		}
		else
		{
			// Could do more checking here of the return value.
			return vDISCONNECTED;
		}
	}

	return vDISCONNECTED;
}



/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Device::Device ( int index, int port, int slot )
{
	m_node = new Lst::Node< Device > ( this );
	Dbg_AssertType ( m_node, Lst::Node< Device > );

    Dbg_Assert( port < vMAX_PORT );
    Dbg_Assert( slot < vMAX_SLOT );

    m_state = vIDLE;
    m_next_state = vIDLE;
    m_index = index;

    // Initialize device
    m_data.m_port = port;
    m_data.m_slot = slot;
    m_data.m_caps.ClearAll();
    m_data.m_num_actuators = 0;
    m_data.m_valid = false;

    memset( m_data.m_actuator_direct, 0, ACTUATOR_BUFFER_LENGTH );
    memset( m_data.m_actuator_align, 0xFF, ACTUATOR_BUFFER_LENGTH );
    m_data.m_actuator_align[0] = 0;
    m_data.m_actuator_align[1] = 1;

    memset( m_data.m_control_data, 0, CTRL_BUFFER_LENGTH );
//	m_data.m_prealbuffer = Mem::Malloc( scePadDmaBufferMax * sizeof( uint128) + 63 );
	m_data.m_dma_buff =  (unsigned char*)((((uint32)m_data.m_prealbuffer)+63)&~63);
	
	// Random retry for unplugged controllers.
	m_unplugged_retry = rand() & 0x3F;

//	memset( m_data.m_dma_buff, 0, sizeof( uint128 ) * scePadDmaBufferMax );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Device::~Device ( void )
{
	Dbg_AssertType ( m_node, Lst::Node< Device > );
//	Mem::Free( m_data.m_prealbuffer );

	XInputClose( m_data.m_handle );

	delete m_node;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::Acquire( void )
{
    Dbg_Message( "Acquiring controller port %d slot %d\n", m_data.m_port, m_data.m_slot );

    if( m_state == vIDLE )
    {
		// Acquire device handle.
		m_data.m_handle = XInputOpen( XDEVICE_TYPE_GAMEPAD, m_data.m_port, XDEVICE_NO_SLOT, NULL );

//		if( m_data.m_handle )
		{
			// Store capabilites of the device
//			XINPUT_CAPABILITIES caps;
//			XInputGetCapabilities( m_data.m_handle, &caps );

//			memset( m_data.m_control_data, 0, CTRL_BUFFER_LENGTH );
			m_data.m_valid = false;
			m_state = vACQUIRING;
			return;
		}
	}
	Dbg_Warning( "failed to open controller port %d slot %d\n", m_data.m_port, m_data.m_slot );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void Device::Unacquire ( void )
{
    Dbg_Message( "Unacquiring controller port %d slot %d\n", m_data.m_port, m_data.m_slot );

    if( m_state == vACQUIRED )
    {
		m_data.m_handle = NULL;
    }

    m_state = vIDLE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Device::ActivateActuator( int act_num, int percent )
{
	// Do nothing if the actuators are disabled.
	if( m_data.m_actuators_disabled )
	{
		return;
	}	
    
    // First, make sure we're in a ready state and our controller has actuators
	read_data();
    if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS )))
    {
        if(( act_num >= 0 ) && ( act_num < m_data.m_num_actuators ))
        {
			unsigned short left_motor, right_motor;           
			float act_strength						= ((float)percent * m_data.m_actuator_max[act_num] ) * 0.01f;
			m_data.m_actuator_direct[act_num]		= (unsigned char)act_strength;

			// Scale the values from [0,255] to [0,65535].
			if( act_num == 0 )
			{
				left_motor	= (unsigned short)( act_strength * 256.0f );
				right_motor	= (unsigned short)m_data.m_actuator_direct[1] * 256;
			}
			else
			{
				left_motor	= (unsigned short)m_data.m_actuator_direct[0] * 256;
				right_motor	= (unsigned short)( act_strength * 256.0f );
			}

			set_xbox_actuators( m_data.m_handle, left_motor, right_motor );
        }
    }
}

/******************************************************************/
/* These disable or enable pad vibration.
/******************************************************************/
void Device::DisableActuators()
{
	// If disabled already do nothing.
	if( m_data.m_actuators_disabled )
	{
		return;
	}
		
	// Run through all the actuators and make sure they're off.
	if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS )))
    {
		for( int i = 0; i < m_data.m_num_actuators; ++i )
		{
			// Switch it off.
			m_data.m_actuator_direct[i] = 0;
		}	
		set_xbox_actuators( m_data.m_handle, 0, 0 );
	}	
	
	// Set the flag.
	m_data.m_actuators_disabled = true;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Device::EnableActuators( void )
{
	m_data.m_actuators_disabled = false;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Device::ResetActuators( void )
{
	if( m_data.m_actuators_disabled )
	{
		// If disabled, then should be off anyway but toggle the states to make sure.
		EnableActuators();
		DisableActuators();
	}
	else
	{
		// If enabled, then we disable them, which switched them off and then enable them again (in the off position).
		DisableActuators();
		EnableActuators();
	}
}



/******************************************************************/
/*                                                                */
/* Ken: This gets called for each pad device when the game gets   */
/* paused. It remembers whether the pad was vibrating & switches  */
/* off vibration.
/*                                                                */
/******************************************************************/

void Device::Pause( void )
{
	// If paused already do nothing.
	if( !m_data.m_paused_ref.InUse())
	{		
	    // First, make sure we're in a ready state and our controller has actuators
	    if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS )))
		{
			for( int i = 0; i

#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

extern int KeyboardInit(void);
       
namespace SIO
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "Serial IO Manager" );

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/



/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

void Manager::process_devices( const Tsk::Task< Manager::DeviceList >& task )
{
	Device*					device;
	Lst::Search< Device >	    sh;
	Manager::DeviceList&	device_list = task.GetData();

	device = sh.FirstItem ( device_list );

	while ( device )
	{
		Dbg_AssertType ( device, Device );

		device->process();
		device = sh.NextItem();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Device* Manager::create_device( int index, int port, int slot )
{
    Device *device;
        
    device = new Device( index, port, slot );

    return device;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Manager::Manager ( void )
{
    int i, index;

	XDEVICE_PREALLOC_TYPE xdpt[] = {{ XDEVICE_TYPE_GAMEPAD,			4 },
									{ XDEVICE_TYPE_MEMORY_UNIT,		1 }};

	// Initialize the peripherals.
	XInitDevices( sizeof( xdpt ) / sizeof( XDEVICE_PREALLOC_TYPE ), xdpt );

	// Create the keyboard queue.
//	XInputDebugInitKeyboardQueue( &xdkp );

    m_process_devices_task = new Tsk::Task< DeviceList > ( Manager::process_devices, m_devices );
    
	// Pause briefly here to give the system time to enumerate the attached devices.
	Sleep( 1000 );

    index = 0;
    for( i = 0; i < SIO::vMAX_PORT; i++ )
    {
	    Device* p_device;

		if(( p_device = create_device( index, i, 0 )))
		{
			m_devices.AddToTail( p_device->m_node );
			p_device->Acquire();
			index++;
		}
    }
    
	if( !Pcm::NoMusicPlease())
	{
		Pcm::Init();
	}

	Dbg_Message( "Initialized Controller lib\n" );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Manager::~Manager( void )
{
    Device*					device;
    Device*					next;
    Lst::Search< Device >	sh;
    
    device = sh.FirstItem ( m_devices );
    
    while ( device )
    {
        Dbg_AssertType ( device, Device );
    
        next = sh.NextItem();
    
        delete device;
        device = next;
    }

    delete m_process_devices_task;

#	if KEYBOARD_ON
	// Initialize the keyboard.
	KeyboardDeinit();
#	endif

	Dbg_Message( "Shut down IOP Controller Lib\n" );
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Manager::ProcessDevices( void )
{
	Device*					device;
	Lst::Search< Device >	sh;
	Manager::DeviceList&	device_list = m_devices;

	device = sh.FirstItem( device_list );

	while( device )
	{
		Dbg_AssertType ( device, Device );

		device->process();
		device = sh.NextItem();
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Device* Manager::GetDevice( int port, int slot )
{
    Device*					device;
    Lst::Search< Device >	sh;
    
    device = sh.FirstItem ( m_devices );
    
    for( device = sh.FirstItem ( m_devices ); device;
            device = sh.NextItem ())
    {
        if( ( device->GetPort() == port ) &&
            ( device->GetSlot() == slot ))
        {
            return device;
        }
    }
    
    return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
Device* Manager::GetDeviceByIndex( int index )
{
    Device*					device;
    Lst::Search< Device >	sh;
    
    device = sh.FirstItem ( m_devices );
    
    for( device = sh.FirstItem ( m_devices ); device;
            device = sh.NextItem ())
    {
        if( device->GetIndex() == index )
        {
            return device;
        }
    }
    
    return NULL;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Manager::Pause()
{
    Device*					device;
    Lst::Search< Device >	sh;
    
    for( device = sh.FirstItem( m_devices ); device; device = sh.NextItem())
    {
		device->Pause();
    }
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Manager::UnPause( void )
{
    Device*					device;
    Lst::Search< Device >	sh;
    
    for( device = sh.FirstItem( m_devices ); device; device = sh.NextItem())
    {
		device->UnPause();
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace SIO


================================================
FILE: Code/Sys/SIO/keyboard.h
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS Library												**
**																			**
**	Module:			SYS (SYS_) 												**
**																			**
**	File name:		keyboard.h												**
**																			**
**	Created:		03/08/2001	-	gj										**
**																			**
**	Description:	USB Keyboard interface									**
**																			**
*****************************************************************************/


#ifndef __SYS_KEYBOARD_H
#define __SYS_KEYBOARD_H

/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

namespace SIO
{

	

#define KEYBOARD_ON	(1)
			
enum
{
	vKB_LEFT	= 20,	// start it high so as not to interfere with button/pad enums 
	vKB_RIGHT,
	vKB_UP,
	vKB_DOWN,
	vKB_BACKSPACE,
	vKB_ENTER,
	vKB_ESCAPE,
	vKB_F1,
	vKB_F2,
	vKB_F3,
	vKB_F4
};

/*****************************************************************************
**							Class Definitions								**
*****************************************************************************/

/*****************************************************************************
**							 Private Declarations							**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							  Public Declarations							**
*****************************************************************************/

/*****************************************************************************
**							   Public Prototypes							**
*****************************************************************************/

int KeyboardInit(void);
int KeyboardDeinit(void);
int KeyboardRead( char* makes );
void KeyboardClear( void );
void SetKeyboardActive(bool active);
void EnableKeyboard( bool enable );

/*****************************************************************************
**								Inline Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace SIO

#endif	// __SYS_KEYBOARD__H



================================================
FILE: Code/Sys/SIO/ngc/p_keyboard.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS Library												**
**																			**
**	Module:			SYS (SYS_) 												**
**																			**
**	File name:		keyboard.cpp											**
**																			**
**	Created:		03/08/2001	-	gj										**
**																			**
**	Description:	USB Keyboard interface									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 

//#include 
//#include 
//#include 
//#include 
//#include 
//
//#include 
extern "C"
{
int sceUsbKbInit(int *max_connect) { return 0; }
int sceUsbKbGetInfo(int *info) { return 0; }
int sceUsbKbRead(unsigned int no,int *data) { return 0; }
int sceUsbKbGetLocation(int no,unsigned char *location) { return 0; }
int sceUsbKbSetLEDStatus(int no, unsigned char led) { return 0; }
int sceUsbKbSetLEDMode(int no, int mode) { return 0; }
int sceUsbKbSetRepeat(int no, int sta_time, int interval) { return 0; }
int sceUsbKbSetCodeType(int no, int type) { return 0; }
int sceUsbKbSetArrangement(int no, int arrange) { return 0; }
int sceUsbKbSync(int mode, int *result) { return 0; }
}
/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace SIO
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

#define USBKEYBD_ARG "keybd=8\0debug=1"

static	unsigned char *s_old_status = NULL;
//static	int	s_info;
//static	unsigned char 		s_location[7];
//static	int s_kdata;
static	int			s_max_connect;

enum
{
	vKEYCODE_RIGHT	=	0x804F,
	vKEYCODE_LEFT	=	0x8050,
	vKEYCODE_DOWN	=	0x8051,
	vKEYCODE_UP		=	0x8052,
	vKEYCODE_ESC	= 	0x8029,
};

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/



/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

//static void s_init_newkeyboard( int i );
//static int s_capture_input( int i, char* makes );

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int KeyboardInit(void)
{
	int result;
//    char *option = USBKEYBD_ARG;
	int i;

	//sceSifInitRpc(0);
	
//#ifdef __NOPT_CDROM__OLD
//	result = sceSifLoadModule(
//               "cdrom0:\\USBKB.IRX",
//                sizeof( USBKEYBD_ARG ) + 1, option );
//#else
//	result = sceSifLoadModule(
//               "host0:IOPModules/usbkb.irx",
//                sizeof( USBKEYBD_ARG ) + 1, option );
//#endif
	
//	if( result < 0 )
    {
        Dbg_MsgAssert( 0,( "EE:Can't load module usbkb.irx\n" ));
		return -1;
	}

    result = sceUsbKbInit( &s_max_connect );
//    if( result == USBKB_NG ) 
	{
		Dbg_Printf( "Initialize error\n" );
		return -1;
	}

    s_old_status = new unsigned char[ s_max_connect ];
    for( i = 0; i < s_max_connect; i++ ) 
	{ 
		s_old_status[i] = 0; 
	}

	return 0;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int KeyboardRead( char* makes )
{
//	int result;
//	int i, num_strokes;
//
//	// get keyboard info
//	result = sceUsbKbGetInfo( &s_info );
////	if( result != USBKB_OK ) 
//	{
//		Dbg_Printf( "Error%d : sceUsbKbGetInfo\n", result );
//		return 0;
//	}
////	sceUsbKbSync( USBKB_WAIT, &result );
////	if( result != USBKB_OK ) 
//	{
//		Dbg_Printf( "Error%d : sceUsbKbSync\n", result );
//		return 0;
//	}
//  
//	num_strokes = 0;
//	// reading keyboard
//	for( i = 0; i < s_max_connect; i++)  
//	{
////		if( s_info.status[i] == 0 ) 
//		{ 
//			continue; 
//		}  // not connected
//
//		if( s_old_status[i] == 0 ) 
//		{
//			Dbg_Printf( "New keyboard %d is connected\n", i );
//			s_init_newkeyboard(i);
//		}
//
//		result = sceUsbKbRead( i, &s_kdata );
////		if( result != USBKB_OK ) 
//		{
//			Dbg_Printf( "Error%d : sceUsbKbRead\n", result );
//			continue;
//		}
////		sceUsbKbSync( USBKB_WAIT, &result );
////		if( result != USBKB_OK ) 
//		{
//			Dbg_Printf( "Error%d : sceUsbKbSync\n",result);
//			continue;
//		}
//
////		if( s_kdata.len == 0 ) 
//		{
//			continue;
//		}
//
////		result = sceUsbKbGetLocation( i, s_location );
////		if( result != USBKB_OK ) 
//		{
//			Dbg_Printf( "Error%d : sceUsbKbGetLocation\n", result );
//			continue;
//		}
//		
////		sceUsbKbSync( USBKB_WAIT, &result );
////		if( result != USBKB_OK ) 
//		{
//			Dbg_Printf( "Error%d : sceUsbKbSync\n", result );
//			continue;
//		}
//		
//		num_strokes = s_capture_input( i, makes );
//	}
//  
//	for( i = 0; i < s_max_connect; i++ ) 
//	{ 
////		s_old_status[i] = s_info.status[i]; 
//	}
//
//	return num_strokes;
	return 0;
}

void KeyboardClear( void )
{
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

//static void s_init_newkeyboard( int i )
//{       
//	int result;
//	
//	result = sceUsbKbSetLEDStatus( i, USBKB_LED_NUM_LOCK );
//	if( result != USBKB_OK ) 
//	{
//		Dbg_Printf( "Error%d : sceUsbKbSetLEDStatus\n", result );
//	} 
//	else 
//	{
//		sceUsbKbSync(USBKB_WAIT,&result);
//		if( result != USBKB_OK ) 
//		{
//			Dbg_Printf( "Error%d : sceUsbKbSync\n", result );
//		}
//	}
//	
//	result = sceUsbKbSetLEDMode( i, USBKB_LED_MODE_AUTO1 );
//	if( result != USBKB_OK ) 
//	{
//		Dbg_Printf( "Error%d : sceUsbKbSetLEDMode\n", result );
//	} 
//	else 
//	{
//		sceUsbKbSync( USBKB_WAIT, &result );
//		if( result != USBKB_OK ) 
//		{
//			Dbg_Printf( "Error%d : sceUsbKbSync\n", result );
//		}
//	}
//	
//	sceUsbKbSetRepeat( i, 30, 2 );
//	sceUsbKbSetCodeType( i, USBKB_CODETYPE_ASCII );
//	sceUsbKbSetArrangement( i, USBKB_ARRANGEMENT_106 );
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

//static int s_capture_input( int i, char* makes )
//{
//	unsigned short kcode;
//	int j, k, index;
//	
//	//Dbg_Printf( "usbkeybd%d : ", i );
//	
//	//Dbg_Printf( "port=" );
//	for( k = 0; k < 7 && s_location[k] != 0; k++ )
//	{
//		//Dbg_Printf( "%s%d", ((k)? ",": "" ), s_location[k] );
//	}
//	  
//	
//	//Dbg_Printf( " : LED[%02X] ", s_kdata.led );
//	
//	//Dbg_Printf( "MKEY[%02X] ", s_kdata.mkey );
//	
//	//Dbg_Printf( "KEY[" );
//	
//	index = 0;
//	for( j=0; j < s_kdata.len; j++ ) 
//	{
//		kcode = s_kdata.keycode[j];
//		if( kcode & USBKB_RAWDAT ) 
//		{
//			//Dbg_Printf( "%04X ", kcode );
//			switch( kcode )
//			{   
//				case vKEYCODE_RIGHT:
//					makes[index++] = vKB_RIGHT;
//					break;
//				case vKEYCODE_LEFT:
//					makes[index++] = vKB_LEFT;
//					break;
//				case vKEYCODE_DOWN:
//					makes[index++] = vKB_DOWN;
//					break;
//				case vKEYCODE_UP:
//					makes[index++] = vKB_UP;
//					break;
//				case vKEYCODE_ESC:
//					makes[index++] = vKB_ESCAPE;
//					break;
//			}
//			continue;
//		}
//		if( kcode & USBKB_KEYPAD ) 
//		{
//			// 10 key
//			if((kcode & 0x00ff) == '\n')
//			{ 
//				//Dbg_Printf( "(\\n)" ); 
//				makes[index++] = vKB_ENTER;
//				continue;
//			}
//			//Dbg_Printf( "(%c) ", kcode & ~USBKB_KEYPAD );
//			makes[index++] = kcode & ~USBKB_KEYPAD;
//			continue;
//		}
//		// Normal key
//		if((kcode & 0x00ff) == '\0' ) 
//		{ 
//			//Dbg_Printf( "\'\\0\'" ); 
//			continue;
//		}
//		if((kcode & 0x00ff) == '\n' ) 
//		{ 
//			//Dbg_Printf( "\'\\n\'" ); 
//			makes[index++] = vKB_ENTER;
//			continue;
//		}
//		if((kcode & 0x00ff) == '\t' ) 
//		{ 
//			//Dbg_Printf( "\'\\t\'" ); 
//			continue;
//		}
//		if((kcode & 0x00ff) == '\b' ) 
//		{ 
//			//Dbg_Printf( "\'\\b\'" ); 
//			makes[index++] = vKB_BACKSPACE;
//			continue;
//		}
//		//Dbg_Printf( "\'%c\' ", kcode );
//		makes[index++] = kcode;
//	}
//	//Dbg_Printf( "]\n" ); 
//
//	return index;
//	return 0;
//}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace SIO


================================================
FILE: Code/Sys/SIO/ngc/siodev.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS Library												**
**																			**
**	Module:			SYS (SYS_) 												**
**																			**
**	File name:		siodev.cpp												**
**																			**
**	Created:		05/26/2000	-	spg										**
**																			**
**	Description:	Generic input device									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
         
#include 
#include 
#include 
//#include 

#include 
#include "sys/ngc/p_hwpad.h"

// PJR - Doh!
extern PADStatus padData[PAD_MAX_CONTROLLERS]; // game pad state
/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace SIO
{
  

// Maps Xbox thumbstick values in the range [-32767, 32767] to PS2 thumstick values in the range [0, 255].
#define XboxThumbToPS2Thumb( v )	(( v + 32767 ) / 256 )


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
    
void Device::process( void )
{
    switch( m_state )
    {

    case vIDLE:
        break;

    case vBUSY:
        wait();
        break;

    case vACQUIRING:
        acquisition_pending();
        break;

    case vACQUIRED:
        read_data();
        break;

    default:
        break;

    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::wait( void )
{
    Dbg_MsgAssert(( m_next_state >= 0 ) && ( m_next_state <= vNUM_STATES ),( "No next state set for wait state" ));

	if ( get_status() != vNOTREADY )
	{
		m_state = m_next_state;
		m_next_state = vIDLE;
	}
}


//int gButton_Option = 0;

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Device::read_data( void )
{
	PADStatus * p;

	// Need to check here for the controller becoming detached.
	if( padData[m_data.m_port].err == PAD_ERR_NO_CONTROLLER )
	{
		m_plugged_in	= false;
		m_data.m_valid	= false;
		Unacquire();
		Acquire();
		return;
	}

	m_data.m_valid	= true;
	m_plugged_in	= true;

	p = &padData[m_data.m_port];

	// Convert this data back into PS2-style 'raw' data for a DUALSHOCK2.
	m_data.m_control_data[0] = 0;						// 'Valid' info flag.
	m_data.m_control_data[1] = ( 0x07 << 4 ) | 16;		// DUALSHOCK2 id + data length.

	m_data.m_control_data[2] = 0xFF;					// Turn off all buttons by default.
	m_data.m_control_data[3] = 0xFF;					// Turn off all buttons by default.

	m_data.m_control_data[2] ^= ( ( p->button & PAD_BUTTON_START ) && !( p->button & PAD_TRIGGER_Z ) ) ? ( 1 << 3 ) : 0;	// Gamecube 'Start' = PS2 'Start' (only if Z is not also pressed).

	#if 0 // Dan: none of the Z-shifting crap
	// Gamecube 'Z' = PS2 'Select'.
	// Directly mapping to select is disabled.

	// Gamecube 'Z' plus 'L' = PS2 'Right Stick Button'.
	// Gamecube 'Z' plus 'R' = PS2 'shift' (this allows the camera mode shifting).
	if( p->button & PAD_TRIGGER_Z )
	{
		// with Z pressed, triggers are L2/R2
		
		// GameCube 'Z' plus 'L' = PS2 'Right Stick Button'. 
		if( p->button & PAD_TRIGGER_L )
		{
			m_data.m_control_data[2] ^= ( 1 << 1 );
		} else

		// GameCube 'Z' plus 'R' = PS2 'Right Stick Button'. 
		if( p->button & PAD_TRIGGER_R )
		{
			m_data.m_control_data[2] ^= ( 1 << 2 );
		} else

//		// Z button + START is PS2 'Select'.
//		if( p->button & PAD_BUTTON_START )
//		{
//			m_data.m_control_data[2] ^= ( 1 << 0 );
//		}
		// Z button without L & R is PS2 L1+R1.
		{
			m_data.m_control_data[3] ^= ( 1 << 2 ) | ( 1 << 3 );
			m_data.m_control_data[2] ^= ( 1 << 0 );
		}
	}
	#endif

	// Temp hack to get fly-around working.
	if( p->button & PAD_TRIGGER_Z )
	{
		m_data.m_control_data[2] ^= ( 1 << 0 );
	}

	m_data.m_control_data[3] ^= ( p->button & PAD_BUTTON_Y ) ? ( 1 << 4 ) : 0;		// Gamecube 'Y' = PS2 'Triangle'.
	m_data.m_control_data[3] ^= ( p->button & PAD_BUTTON_X ) ? ( 1 << 5 ) : 0;		// Gamecibe 'X' = PS2 'Circle'.
	m_data.m_control_data[3] ^= ( p->button & PAD_BUTTON_A ) ? ( 1 << 6 ) : 0;		// Gamecube 'A' = PS2 'X'.
	m_data.m_control_data[3] ^= ( p->button & PAD_BUTTON_B ) ? ( 1 << 7 ) : 0;		// Gamecube 'B' = PS2 'Square'.

	// Dan: none of this analog crap
	// m_data.m_control_data[3] ^= ( p->triggerLeft  > 128 ) ? ( 1 << 2 ) : 0;		// XBox 'Left Trigger pressed down > halfway' = PS2 'L1'.
	// m_data.m_control_data[3] ^= ( p->triggerRight > 128 ) ? ( 1 << 3 ) : 0;		// XBox 'Right Trigger pressed down > halfway' = PS2 'R1'.
	// m_data.m_control_data[3] ^= ( p->triggerLeft  > 16 ) ? ( 1 << 0 ) : 0;		// XBox 'Left Trigger' = PS2 'L2'.
	// m_data.m_control_data[3] ^= ( p->triggerRight > 16 ) ? ( 1 << 1 ) : 0;		// XBox 'Right Trigger' = PS2 'R2'.
	
	m_data.m_control_data[3] ^= ( p->triggerLeft  > 128 ) ? ( 1 << 2 ) : 0;			// Gamecube 'Left Trigger = PS2 'L1'.
	m_data.m_control_data[3] ^= ( p->triggerRight > 128 ) ? ( 1 << 3 ) : 0;			// Gamecube 'Right Trigger = PS2 'R1'.
	
	// Dan: Store the state of Z in m_control_data[20] above Xbox's black and white buttons.
	m_data.m_control_data[20]=0;
	if (p->button & PAD_TRIGGER_Z)
	{
		m_data.m_control_data[20] |= (1<<2);
	}	

	// Make analog buttons full depression if pressed.
	// Ngc analog buttons return analog value in range [0, 255].
	m_data.m_control_data[12] = ( p->button & PAD_BUTTON_Y ) ? 255 : 0;				// Gamecube 'Y' = PS2 Analog 'Triangle'.
	m_data.m_control_data[13] = ( p->button & PAD_BUTTON_X ) ? 255 : 0;				// Gamecube 'X' = PS2 Analog 'Circle'.
	m_data.m_control_data[14] = p->analogA;											// Gamecube 'A' = PS2 Analog 'X'.
	m_data.m_control_data[15] = p->analogB;											// Gamecube 'B' = PS2 'Square'.
	
	m_data.m_control_data[16] = ( p->triggerLeft  > 128 ) ? 255 : 0;				// Gamecube 'Left Trigger' = PS2 Analog 'L1'.
	m_data.m_control_data[17] = ( p->triggerRight > 128 ) ? 255 : 0;				// Gamecube 'Right Trigger' = PS2 Analog 'R1'.
	m_data.m_control_data[18] = 0;													// Gamecube no 'L2'
	m_data.m_control_data[19] = 0;													// Gamecube no 'R2'

	// Zero out the d-pad pressure values.
	m_data.m_control_data[8]	= 0x00;
	m_data.m_control_data[9]	= 0x00;
	m_data.m_control_data[10]	= 0x00;
	m_data.m_control_data[11]	= 0x00;

	// Handle 8 position d-pad.
	m_data.m_control_data[2] ^= ( p->button & PAD_BUTTON_UP ) ? ( 1 << 4 ) : 0;		// Gamecube 'DPad Up' = PS2 'DPad Up'.
	m_data.m_control_data[10] = ( p->button & PAD_BUTTON_UP ) ? 0xFF : 0;			// Gamecube 'DPad Up' = PS2 'DPad Up'.
	m_data.m_control_data[2] ^= ( p->button & PAD_BUTTON_RIGHT ) ? ( 1 << 5 ) : 0;	// Gamecube 'DPad Right' = PS2 'DPad Right'.
	m_data.m_control_data[8] = ( p->button & PAD_BUTTON_RIGHT ) ? 0xFF : 0;			// Gamecube 'DPad Right' = PS2 'DPad Right'.
	m_data.m_control_data[2] ^= ( p->button & PAD_BUTTON_DOWN ) ? ( 1 << 6 ) : 0;	// Gamecube 'DPad Down' = PS2 'DPad Down'.
	m_data.m_control_data[11] = ( p->button & PAD_BUTTON_DOWN ) ? 0xFF : 0;			// Gamecube 'DPad Down' = PS2 'DPad Down'.
	m_data.m_control_data[2] ^= ( p->button & PAD_BUTTON_LEFT ) ? ( 1 << 7 ) : 0;	// Gamecube 'DPad Left' = PS2 'DPad Left'.
	m_data.m_control_data[9] = ( p->button & PAD_BUTTON_LEFT ) ? 0xFF : 0;			// Gamecube 'DPad Left' = PS2 'DPad Left'.

	// Gamecube thumbsticks return analog value in range [-128, 127].
	int stx = (int)(((float)p->stickX) * 2.0f );
	int sty = (int)(((float)p->stickY) * 2.0f );
	if ( stx < -128 ) stx = -128;
	if ( stx > 127 ) stx = 127;
	if ( sty < -128 ) sty = -128;
	if ( sty > 127 ) sty = 127;

	int subx = (int)(((float)p->substickX) * 2.0f );
	int suby = (int)(((float)p->substickY) * 2.0f );
	if ( subx < -128 ) subx = -128;
	if ( subx > 127 ) subx = 127;
	if ( suby < -128 ) suby = -128;
	if ( suby > 127 ) suby = 127;

	m_data.m_control_data[4] = (unsigned char)(subx+128);		// Analog stick right (X direction).
	m_data.m_control_data[5] = 255 - (unsigned char)(suby+128);	// Analog stick right (Y direction).
	m_data.m_control_data[6] = (unsigned char)(stx+128);		// Analog stick left (X direction).
	m_data.m_control_data[7] = 255 - (unsigned char)(sty+128);	// Analog stick left (Y direction).
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Device::IsPluggedIn( void )
{
	return m_plugged_in;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::acquisition_pending( void )
{
    int status;

    status = get_status();
                        
    if(( status == vCONNECTED ) || ( status == vREADY ))
    {
        // sucessful.  Now query the controller for capabilities
        query_capabilities();
    }
        
	// failed to acquire controller
	// stay in this state and continue to try to acquire it or prompt the user if it is mandatory
	// that they have a controller in   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::query_capabilities( void )
{   
	// Currently assumes standard Gamecube controller.
    int id = vANALOG_CTRL;
    
    m_data.m_type = id;

	switch( id )
	{   
		case vANALOG_CTRL:
		{
			m_data.m_caps.SetMask( mANALOG_BUTTONS );

			// GameCube actually only has 1, but we need to track 2 since PS2 and Xbox have 2,
			// and incoming requests could be for either.
			m_data.m_num_actuators = 2;
			m_data.m_caps.SetMask( mACTUATORS );
			m_data.m_actuator_max[0] = 1;
			m_data.m_actuator_max[1] = 1;

			m_state = vBUSY;
			m_next_state = vACQUIRED;
			return;
		}

		default:
		{
			Dbg_Message( "Detected Controller of unknown type in %d:%d", m_data.m_port, m_data.m_slot );
			break;
		}
	}
    m_state = vACQUIRED;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Device::get_status( void )
{
	if( m_data.m_port < 4 )
	{
		if( padData[m_data.m_port].err == PAD_ERR_NONE )
		{
			return vREADY;
		}
		else
		{
			// Could do more checking here of the return value.
			return vDISCONNECTED;
		}
	}
	return vDISCONNECTED;
}



/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Device::Device ( int index, int port, int slot )
{
	m_node = new Lst::Node< Device > ( this );
	Dbg_AssertType ( m_node, Lst::Node< Device > );

    Dbg_Assert( port < vMAX_PORT );
    Dbg_Assert( slot < vMAX_SLOT );

    m_state = vIDLE;
    m_next_state = vIDLE;
    m_index = index;

    // Initialize device
    m_data.m_port = port;
    m_data.m_slot = slot;
    m_data.m_caps.ClearAll();
    m_data.m_num_actuators = 0;
    m_data.m_valid = false;

    memset( m_data.m_actuator_direct, 0, ACTUATOR_BUFFER_LENGTH );
    memset( m_data.m_actuator_align, 0xFF, ACTUATOR_BUFFER_LENGTH );
    m_data.m_actuator_align[0] = 0;
    m_data.m_actuator_align[1] = 1;

    memset( m_data.m_control_data, 0, CTRL_BUFFER_LENGTH );
//	m_data.m_prealbuffer = Mem::Malloc( scePadDmaBufferMax * sizeof( uint128) + 63 );
	m_data.m_dma_buff =  (unsigned char*)((((uint32)m_data.m_prealbuffer)+63)&~63);
	
//    memset( m_data.m_dma_buff, 0, sizeof( uint128 ) * scePadDmaBufferMax );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Device::~Device ( void )
{
	Dbg_AssertType ( m_node, Lst::Node< Device > );
//	Mem::Free( m_data.m_prealbuffer );

//	XInputClose( m_data.m_handle );

	delete m_node;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::Acquire( void )
{
    Dbg_Message( "Acquiring controller port %d slot %d\n", m_data.m_port, m_data.m_slot );

    if( m_state == vIDLE )
    {
		if( m_data.m_port < 4 )
		{
			// Acquire device handle.
//			m_data.m_handle = XInputOpen( XDEVICE_TYPE_GAMEPAD, m_data.m_port, XDEVICE_NO_SLOT, NULL );
//
//			if( m_data.m_handle )
			{
				// Store capabilites of the device
//				XINPUT_CAPABILITIES caps;
//				XInputGetCapabilities( m_data.m_handle, &caps );

//				memset( m_data.m_control_data, 0, CTRL_BUFFER_LENGTH );
				m_data.m_valid = false;
				m_state = vACQUIRING;
				return;
			}
		}
	}
	Dbg_Warning( "failed to open controller port %d slot %d\n", m_data.m_port, m_data.m_slot );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void Device::Unacquire ( void )
{
    Dbg_Message( "Unacquiring controller port %d slot %d\n", m_data.m_port, m_data.m_slot );

    if( m_state == vACQUIRED )
    {
//		if( scePadPortClose( m_data.m_port, m_data.m_slot ) != 1 )
//		{
//			Dbg_Warning( "failed to close controller port %d slot %d\n", m_data.m_port, m_data.m_slot );
//		}
    }

    m_state = vIDLE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Device::ActivateActuator( int act_num, int percent )
{
	// Do nothing if the actuators are disabled.
	if( m_data.m_actuators_disabled )
	{
		return;
	}	
	
    float act_strength;
    
    // First, make sure we're in a ready state and our controller has actuators
    if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS )))
    {
		// Rumble regardless. The problem here is that incoming rumble requests can be for motors
		// other than 0 - PS2 and Xbox have 2 motors. So, if the request is not for motor0, if it 
		if(( act_num >= 0 ) && ( act_num < m_data.m_num_actuators ))
        {
//			act_strength = ((float) percent * m_data.m_actuator_max[act_num] ) / 100.0f;
			act_strength = percent;

            // for lack of a rounding function, perform this check here
//			if( m_data.m_actuator_max[act_num] == 1 )
//			{
//				if( act_strength > 0.0f )
//				{
//					m_data.m_actuator_direct[act_num] = 1;
//				}
//				else
//				{
//					m_data.m_actuator_direct[act_num] = 0;
//				}
//			}
//			else
//			{
//				m_data.m_actuator_direct[act_num] = (unsigned char) act_strength;
//			}
			m_data.m_actuator_direct[act_num] = ( act_strength > 0.0f ) ? 1 : 0;
            
			// If either tracked motor is on, rumble the single motor, otherwise turn it off.
			PADControlMotor( m_data.m_port, (( m_data.m_actuator_direct[0] > 0 ) || ( m_data.m_actuator_direct[1] > 0 )) ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP );
        }
    }
}

/******************************************************************/
/* These disable or enable pad vibration.
/******************************************************************/
void Device::DisableActuators()
{
	// If disabled already do nothing.
	if (m_data.m_actuators_disabled)
	{
		return;
	}
		
	// Run through all the actuators and make sure they're off.
    if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS )))
    {
		for (int i=0; i 0 ) || ( m_data.m_actuator_direct[1] > 0 )) ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP );

//				scePadSetActDirect( m_data.m_port, m_data.m_slot, m_data.m_actuator_direct );
			}	
		}
	}
}



// Switches off vibration, and also zeros the saved
// vibration levels too so that unpausing will not 
// switch them on again for goodness sake.         
void Device::StopAllVibrationIncludingSaved()
{
	// First, make sure we're in a ready state and our controller has actuators
	if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS )))
	{
		for (int i=0; i 0 ) || ( m_data.m_actuator_direct[1] > 0 )) ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP );

//		scePadSetActDirect( m_data.m_port, m_data.m_slot, m_data.m_actuator_direct );
	}	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::ActivatePressureSensitiveMode( void )
{
    if( m_data.m_caps.TestMask( mANALOG_BUTTONS ))
    {
//		if( scePadEnterPressMode( m_data.m_port, m_data.m_slot ) == 1 )
		{
			m_state = vBUSY;
			m_next_state = vACQUIRED;
		}
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::DeactivatePressureSensitiveMode( void )
{
    if( m_data.m_caps.TestMask( mANALOG_BUTTONS ))
    {
//		if( scePadExitPressMode( m_data.m_port, m_data.m_slot ) == 1 )
		{
			m_state = vBUSY;
			m_next_state = vACQUIRED;
		}
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
} // namespace SIO



================================================
FILE: Code/Sys/SIO/ngc/sioman.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS Library												**
**																			**
**	Module:			SYS (SYS_) 												**
**																			**
**	File name:		sioman.cpp												**
**																			**
**	Created:		05/26/2000	-	spg										**
**																			**
**	Description:	Serial IO Manager										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 

#include 
#include 
//#include 

#include 
//#include 
//#include 
#include 

#include 
#include "sys/ngc/p_hwpad.h"

	PADStatus padData[PAD_MAX_CONTROLLERS]; // game pad state

/*****************************************************************************
**								DBG Information								**
*****************************************************************************/

namespace SIO
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "Serial IO Manager" );

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/



/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

void Manager::process_devices( const Tsk::Task< Manager::DeviceList >& task )
{
//	Dbg_AssertType ( task, Tsk::Task< INP_MANAGER::DEVICE_LIST > );

	Device*					device;
	Lst::Search< Device >	    sh;
	Manager::DeviceList&	device_list = task.GetData();

	device = sh.FirstItem ( device_list );

	// Deal with hot-switching controllers.
	u32 reset_mask = 0;
	if( padData[0].err == PAD_ERR_NO_CONTROLLER )
	{
		reset_mask |= PAD_CHAN0_BIT;
	}
	if( padData[1].err == PAD_ERR_NO_CONTROLLER )
	{
		reset_mask |= PAD_CHAN1_BIT;
	}
	if( reset_mask )
	{
		PADReset( reset_mask );
	}

	while ( device )
	{
		Dbg_AssertType ( device, Device );

		device->process();
		device = sh.NextItem();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Device* Manager::create_device( int index, int port, int slot )
{
    Device *device;
        
    device = new Device( index, port, slot );

    return device;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::Manager ( void )
{
    int i, j, index;

//	XDEVICE_PREALLOC_TYPE xdpt[] = {{ XDEVICE_TYPE_GAMEPAD,			4 },
//									{ XDEVICE_TYPE_MEMORY_UNIT,		8 }};
////									{ XDEVICE_TYPE_DEBUG_KEYBOARD,	1 }};
//
//	// Initialize the peripherals.
//	XInitDevices( sizeof( xdpt ) / sizeof( XDEVICE_PREALLOC_TYPE ), xdpt );

	// Create the keyboard queue.
//	XInputDebugInitKeyboardQueue( &xdkp );

//#	if KEYBOARD_ON
//	// Initialize the keyboard.
//	KeyboardInit();
//#	endif

    m_process_devices_task = new Tsk::Task< DeviceList > ( Manager::process_devices, m_devices );
    
	// Pause briefly here to give the system time to enumerate the attached devices.
//	Sleep( 500 );

    index = 0;
    for( i = 0; i < vMAX_PORT; i++ )
    {
//		for( j = 0; j < scePadGetSlotMax ( i ); j++ )
		for( j = 0; j < 1; j++ )
		{
		    Device* p_device;

			if(( p_device = create_device ( index, i, j )))
			{
				m_devices.AddToTail( p_device->m_node );
				p_device->Acquire();
				index++;
			}
		}
    }
    
#	if !NO_MUSIC_PLEASE
	// Moved this since the pcm stuff for ngc needs the sfx stuff initialised first.
//	Pcm::Init();
#	endif

    Dbg_Message( "Initialized IOP controller lib\n" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
    Device*					device;
    Device*					next;
    Lst::Search< Device >	sh;
    
    device = sh.FirstItem ( m_devices );
    
    while ( device )
    {
        Dbg_AssertType ( device, Device );
    
        next = sh.NextItem();
    
        delete device;
        device = next;
    }

    delete m_process_devices_task;

	Dbg_Message( "Shut down IOP Controller Lib\n" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Device* Manager::GetDevice( int port, int slot )
{
    Device*					device;
    Lst::Search< Device >	sh;
    
    device = sh.FirstItem ( m_devices );
    
    for( device = sh.FirstItem ( m_devices ); device;
            device = sh.NextItem ())
    {
        if( ( device->GetPort() == port ) &&
            ( device->GetSlot() == slot ))
        {
            return device;
        }
    }
    
    return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Device* Manager::GetDeviceByIndex( int index )
{
    Device*					device;
    Lst::Search< Device >	sh;
    
    device = sh.FirstItem ( m_devices );
    
    for( device = sh.FirstItem ( m_devices ); device;
            device = sh.NextItem ())
    {
        if( device->GetIndex() == index )
        {
            return device;
        }
    }
    
    return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::Pause()
{
    Device*					device;
    Lst::Search< Device >	sh;
    
    for( device = sh.FirstItem( m_devices ); device; device = sh.NextItem())
    {
		device->Pause();
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Manager::UnPause( void )
{
    Device*					device;
    Lst::Search< Device >	sh;
    
    for( device = sh.FirstItem( m_devices ); device; device = sh.NextItem())
    {
		device->UnPause();
    }
}


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace SIO





================================================
FILE: Code/Sys/SIO/siodev.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment			                **
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS Library												**
**																			**
**	Module:			SYS (SYS_) 												**
**																			**
**	File name:		siodev.cpp												**
**																			**
**	Created:		05/26/2000	-	spg										**
**																			**
**	Description:	Generic input device									**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
         
#include 
#include 
#include 
#include 

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								DBG Defines									**
*****************************************************************************/

namespace SIO
{
  


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/


/*****************************************************************************
**								 Public Data								**
*****************************************************************************/


/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
    
void Device::process( void )
{
    

    switch( m_state )
    {

    case vIDLE:
        break;

    case vBUSY:
        wait();
        break;

    case vACQUIRING:
        acquisition_pending();
        break;

	case vPRESS_MODE_ON_COMPLETE:
		m_data.m_button_mode = vANALOG;
		m_state = vACQUIRED;
		break;

	case vPRESS_MODE_OFF_COMPLETE:
		m_data.m_button_mode = vDIGITAL;
		m_state = vACQUIRED;
		break;
	
    case vACQUIRED:
        read_data();
        break;

    default:
        break;

    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::wait( void )
{
    

    Dbg_MsgAssert(( m_next_state >= 0 ) && ( m_next_state <= vNUM_STATES ),( "No next state set for wait state" ));

    if( scePadGetReqState( m_data.m_port, m_data.m_slot ) != scePadReqStateBusy )
    {
        if( get_status() != vNOTREADY )
        {
            m_state = m_next_state;
            m_next_state = vIDLE;
        }
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::read_data ( void )
{
    

    int state;

    state = scePadGetState( m_data.m_port, m_data.m_slot );

    switch ( state )
    {
    case scePadStateFindCTP1:
    case scePadStateStable:
        if( scePadRead( m_data.m_port, m_data.m_slot, m_data.m_control_data ) > 0 )
        {
            m_data.m_valid = true;
			m_plugged_in=true;
        }
        else
        {
            m_data.m_valid = false;
        }
        break;

    case scePadStateDiscon:
        // We lost connection to the controller. Try to reacquire it
        // Also, display message if it is required.
		m_plugged_in=false;
        Unacquire();
        Acquire();
        break;
    }                  
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

bool Device::IsPluggedIn()
{
    
	return m_plugged_in;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::acquisition_pending( void )
{
    

    int status;

    status = get_status();
                        
    if(( status == vCONNECTED ) || ( status == vREADY ))
    {
        // sucessful.  Now query the controller for capabilities
        query_capabilities();
    }
        
	// failed to acquire controller
	// stay in this state and continue to try to acquire it or prompt the user if it is mandatory
	// that they have a controller in   
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::query_capabilities( void )
{   
    

    int id, ex_id;

    // Get controller ID
    id = scePadInfoMode( m_data.m_port, m_data.m_slot, InfoModeCurID, 0 );
    
    if( id == 0 )
    {
        Dbg_Warning( "Unsupported controller or controller disconnected" );
        Unacquire();
        return;
    }

    // Get extended ID, if one exists
    ex_id = scePadInfoMode( m_data.m_port, m_data.m_slot, InfoModeCurExID, 0 );
    // if we have a valid extended ID, use it instead
    if( ex_id > 0 )
    { 
        id = ex_id;
    }
    
    m_data.m_type = id;
	m_data.m_button_mode = vDIGITAL;

    switch( id )
    {   
    case vNEGI_COM:
        Dbg_Message( "Detected NeGi-CON Controller in %d:%d", m_data.m_port, m_data.m_slot );
        break;

    case vKONAMI_GUN:
        Dbg_Message( "Detected GunCON(Konami) Controller in %d:%d", m_data.m_port, m_data.m_slot );
        break;

    case vDIGITAL_CTRL:
        Dbg_Message( "Detected Digital Controller in %d:%d", m_data.m_port, m_data.m_slot );
        // Switch to Analog if it's supported
        if( scePadInfoMode( m_data.m_port, m_data.m_slot, InfoModeCurExID, 0 ) != 0 )
        {   
            if( scePadSetMainMode( m_data.m_port, m_data.m_slot, 1, 3 ) == 0 )
            {
                Dbg_Warning( "scePadSetMainMode not sent properly" );
            }
			// Return so that we stay in this state and re-query as an analog controller
			return;
        }
        
        break;

    case vJOYSTICK:
        Dbg_Message( "Detected Analog Joystick in %d:%d", m_data.m_port, m_data.m_slot );
        break;

    case vNAMCO_GUN:
        Dbg_Message( "Detected GunCON(NAMCO) Controller in %d:%d", m_data.m_port, m_data.m_slot );
        break;


    case vANALOG_CTRL:
        {
            int i, j;

            Dbg_Message( "Detected Analog Controller in %d:%d", m_data.m_port, m_data.m_slot );
            m_data.m_caps.SetMask( mANALOG );
            // check if it's a dual shock 2
			if( scePadInfoPressMode( m_data.m_port, m_data.m_slot ) == 1 )
			{
				m_data.m_caps.SetMask( mANALOG_BUTTONS );
			}

            // check for actuator support
            if(( m_data.m_num_actuators = scePadInfoAct( m_data.m_port, m_data.m_slot, -1, 0 )) > 0 )
            {
                m_data.m_caps.SetMask( mACTUATORS );

                for( i = 0; i < m_data.m_num_actuators; i++ )
                {
                    int power;

                    power = scePadInfoAct( m_data.m_port, m_data.m_slot, i, InfoActSize );
                    if( power == 0 )
                    {
                        m_data.m_actuator_max[i] = 1;
                    }
                    else
                    {
                        m_data.m_actuator_max[i] = 255;
                    }
	            }

                for( i = 0; i < m_data.m_num_actuators; i++ )
                {
                    m_data.m_actuator_align[ i ] = i;
                }

                for( j = i; j < 6; j++ )
                {
                    m_data.m_actuator_align[ j ] = 0xFF;
                }
                
                if( scePadSetActAlign( m_data.m_port, m_data.m_slot, m_data.m_actuator_align ) == 0 )
                {
                    Dbg_Warning( "scePadSetActAlign not sent properly" );
                }

                m_state = vBUSY;
                m_next_state = vACQUIRED;
                return;
            }
            break;
        }
        
    case vFISHING_CTRL:
        Dbg_Message( "Detected TSURI-CON(fishing) Controller in %d:%d", m_data.m_port, m_data.m_slot );
        break;
        
    case vJOG_CTRL:
        Dbg_Message( "Detected JOG-CON Controller in %d:%d", m_data.m_port, m_data.m_slot );
        break;

    default:
        Dbg_Message( "Detected Controller of unknown type in %d:%d", m_data.m_port, m_data.m_slot );
        break;
    }

    m_state = vACQUIRED;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

int Device::get_status( void )
{
    

    int state;

    state = scePadGetState( m_data.m_port, m_data.m_slot );
    switch ( state )
    {
        
    case scePadStateFindCTP1:
    	return vREADY;
		
	case scePadStateStable:   
        return vCONNECTED;

    case scePadStateError:
    case scePadStateDiscon:
	case scePadStateClosed:
		m_plugged_in = false;
        // problems with controller connection
		return vDISCONNECTED;

    case scePadStateFindPad:
    case scePadStateExecCmd:
		return vNOTREADY;

    default:
        Dbg_MsgAssert( 0,( "Unhandled Controller Pad State" ));
        return vREADY;
    }                 
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

Device::Device ( int index, int port, int slot )
{
	m_node = new Lst::Node< Device > ( this );
	Dbg_AssertType ( m_node, Lst::Node< Device > );

    Dbg_Assert( port < vMAX_PORT );
    Dbg_Assert( slot < vMAX_SLOT );

    m_state = vIDLE;
    m_next_state = vIDLE;
    m_index = index;

    // Initialize device
    m_data.m_port = port;
    m_data.m_slot = slot;
    m_data.m_caps.ClearAll();
    m_data.m_num_actuators = 0;
	m_data.m_button_mode = vDIGITAL;
    m_data.m_valid = false;

	m_plugged_in = true;
    
    
    memset( m_data.m_actuator_direct, 0, ACTUATOR_BUFFER_LENGTH );
    memset( m_data.m_actuator_align, 0xFF, ACTUATOR_BUFFER_LENGTH );
    m_data.m_actuator_align[0] = 0;
    m_data.m_actuator_align[1] = 1;

    memset( m_data.m_control_data, 0, CTRL_BUFFER_LENGTH );
	m_data.m_prealbuffer = Mem::Malloc( scePadDmaBufferMax * sizeof( uint128) + 63 );
	m_data.m_dma_buff =  (unsigned char*)((((uint32)m_data.m_prealbuffer)+63)&~63);
	
    memset( m_data.m_dma_buff, 0, sizeof( uint128 ) * scePadDmaBufferMax );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
Device::~Device ( void )
{
	

	Dbg_AssertType ( m_node, Lst::Node< Device > );
	Mem::Free( m_data.m_prealbuffer );
	delete m_node;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::Acquire ( void )
{
    
    
    Dbg_Message( "Acquiring controller port %d slot %d\n", m_data.m_port, m_data.m_slot );

    if( m_state == vIDLE )
    {
        if( scePadPortOpen( m_data.m_port, m_data.m_slot, (uint128*)m_data.m_dma_buff ) == 1 )
        {   
            memset( m_data.m_control_data, 0, CTRL_BUFFER_LENGTH );
            
            m_data.m_valid = false;
            m_state = vACQUIRING;
        }
        else
        {
            Dbg_Warning( "failed to open controller port %d slot %d\n", m_data.m_port, m_data.m_slot );
        }
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
	
void Device::Unacquire ( void )
{
    

    Dbg_Message( "Unacquiring controller port %d slot %d\n", m_data.m_port, m_data.m_slot );

    if( m_state == vACQUIRED )
    {
        if( scePadPortClose( m_data.m_port, m_data.m_slot ) != 1 )
        {
            Dbg_Warning( "failed to close controller port %d slot %d\n", m_data.m_port, m_data.m_slot );
        }
    }

    m_state = vIDLE;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

void Device::ActivateActuator( int act_num, int percent )
{


	
	// Do nothing if the actuators are disabled.
	if (m_data.m_actuators_disabled)
	{
		return;
	}	
	
    float act_strength;
    
    // First, make sure we're in a ready state and our controller has actuators
    if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS )))
    {
        if(( act_num >= 0 ) && ( act_num < m_data.m_num_actuators ))
        {
            act_strength = ((float) percent * m_data.m_actuator_max[act_num] ) / 100.0f;
            // for lack of a rounding function, perform this check here
            if( m_data.m_actuator_max[act_num] == 1 )
            {
                if( act_strength > 0.001f )
                {
                    m_data.m_actuator_direct[act_num] = 1;
                }
                else
                {
                    m_data.m_actuator_direct[act_num] = 0;
                }
            }
            else
            {
                m_data.m_actuator_direct[act_num] = (unsigned char) act_strength;
            }
            
            scePadSetActDirect( m_data.m_port, m_data.m_slot, m_data.m_actuator_direct );
        }
    }
}

/******************************************************************/
/* These disable or enable pad vibration.
/******************************************************************/
void Device::DisableActuators()
{
    

	// If disabled already do nothing.
	if (m_data.m_actuators_disabled)
	{
		return;
	}
		
	// Run through all the actuators and make sure they're off.
    if(( m_state == vACQUIRED ) && ( m_data.m_caps.TestMask( mACTUATORS )))
    {
		for (int i=0; i
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
//#include 

#include 
#include 

#include 
#include 
#include 

#include 
#include 
#include 

#include 


/*****************************************************************************
**								DBG Information								**
*****************************************************************************/


       
namespace SIO
{



/*****************************************************************************
**								  Externals									**
*****************************************************************************/


/*****************************************************************************
**								Private Types								**
*****************************************************************************/


/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

DefineSingletonClass( Manager, "Serial IO Manager" );

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/



/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/


/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

#define CD_TEST 0
#if CD_TEST
static void cd_test()
{
	int i;
	int	disk_type = SCECdDVD;

	sceCdInit(SCECdINIT);
	sceCdMmode(disk_type);
	printf(" sceCdGetDiskType   ");
	int detected_disk_type= sceCdGetDiskType();
	switch(detected_disk_type)
	{
		case SCECdIllgalMedia:
		printf("Disk Type= IllgalMedia\n"); break;
		case SCECdPS2DVD:
		printf("Disk Type= PlayStation2 DVD\n"); break;
		case SCECdPS2CD:
		printf("Disk Type= PlayStation2 CD\n"); break;
		case SCECdPS2CDDA:
		printf("Disk Type= PlayStation2 CD with CDDA\n"); break;
		case SCECdPSCD:
		printf("Disk Type= PlayStation CD\n"); break;
		case SCECdPSCDDA:
		printf("Disk Type= PlayStation CD with CDDA\n"); break;
		case SCECdDVDV:
		printf("Disk Type= DVD video\n"); break;
		case SCECdCDDA:
		printf("Disk Type= CD-DA\n"); break;
		case SCECdDETCT:
		printf("Working\n"); break;
		case SCECdNODISC: 
		printf("Disk Type= No Disc\n"); break;
		default:
		printf("Disk Type= OTHER DISK\n"); break;
	}

	// If disk type has changed to a CD, then need to re-initialize it							   
	if ((detected_disk_type == SCECdPS2CD) || (detected_disk_type == SCECdPSCD))
	{
		disk_type = SCECdCD;
		sceCdMmode(disk_type);
	}
	// This next bit is essential for when making a bootable disc, ie one that will
	// boot on the actual PS rather than just the dev system.
	// K: Commented out __NOPT_BOOTABLE__, since it is set when __NOPT_CDROM__OLD is set.

	/* Reboot IOP, replace default modules  */
	char  path[128];
	
	// THIS CODE IS NOT USED - JUST A TEST FUNCTION!!!!!
	sprintf(path,"host0:\\SKATE5\\DATA\\IOPMODULES\\DNAS280.IMG");	   // ALSO NEED TO CHANGE In p_filesys.cpp
	
	
	while ( !sceDNAS2NetSifRebootIop(path) ); /* (Important) Unlimited retries */
	while( !sceSifSyncIop() );

	/* Reinitialize */
	sceSifInitRpc(0);
	sceCdInit(SCECdINIT);

	sceCdMmode(disk_type);   /* Media: CD-ROM */

	sceFsReset();

	int fd = sceDopen("cdrom0:");
	if (fd >= 0)
	{
		struct sce_dirent dir_entry;
		Dbg_Message("Reading directory");
		while (sceDread(fd, &dir_entry) > 0)
		{
			Dbg_Message("Found entry %s", dir_entry.d_name);
		}
		sceDclose(fd);
	}
	else
	{
		// Couldn't open directory, try raw read
		uint32 buffer[512 + 16];
		for (i = 0; i < 512; i++)
		{
			buffer[i] = 0xDEADBEEF;
		}

		sceCdRMode mode;
		mode.trycount = 255;
		mode.spindlctrl = SCECdSpinNom;
		mode.datapattern = SCECdSecS2048;
		mode.pad = 0;

		int ret_val = sceCdRead(0, 1, buffer, &mode);
		if (ret_val == 1)
		{
			sceCdSync(0);
			Dbg_Message("Read Sector, here is the dump (make sure you don't see 0xDEADBEEF in data)");

			// Read data, print it
			for (i = 0; i < 512; i += 4)
			{
				Dbg_Message("0x%08x 0x%08x 0x%08x 0x%08x", buffer[i], buffer[i + 1], buffer[i + 2], buffer[i + 3]);
			}
		}
		else
		{
			Dbg_Message("Couldn't Read Sector. Error = %x", sceCdGetError());
		}
	}
	Dbg_Assert(0);
}
#endif

void		Manager::process_devices( const Tsk::Task< Manager::DeviceList >& task )
{
	
//	Dbg_AssertType ( task, Tsk::Task< INP_MANAGER::DEVICE_LIST > );

	Device*					device;
	Lst::Search< Device >	    sh;
	Manager::DeviceList&	device_list = task.GetData();

	device = sh.FirstItem ( device_list );

	while ( device )
	{
		Dbg_AssertType ( device, Device );

		device->process();
		device = sh.NextItem();
	}
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Device* Manager::create_device( int index, int port, int slot )
{
    

    Device *device;
        
    device = new Device( index, port, slot );

    return device;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/
#define MAX_IRX_NAME_CHARS 100
static char pIRXNameBuf[MAX_IRX_NAME_CHARS+1];

// Loads an IRX file. The name must not have the .irx extension, it gets added by the function.
// Returns 0 if successful, -1 if error. (But it will assert in debug mode)
int LoadIRX(const char *pName, int num_args, char* args, bool assert_on_fail)
{
    
	
	File::StopStreaming( );
	if ( Pcm::UsingCD( ) )
	{
		Dbg_MsgAssert( 0,( "Using CD returned TRUE." ));
		return ( -1 );
	}

	// Start the name with the appropriate thingy.
	if (Config::CD())
	{
		Dbg_MsgAssert(strlen("cdrom0:\\xxxxxxxxxxxxxxxxxIOP\\")+strlen(pName)+strlen(".IRX")<=MAX_IRX_NAME_CHARS,("String in pName is too long."));
		char  path[128];
		sprintf(path,"cdrom0:\\%sIOP\\",Config::GetDirectory());
		strcpy(pIRXNameBuf,path);
	}
	else
	{
		Dbg_MsgAssert(strlen("host0:IOPModules/")+strlen(pName)+strlen(".IRX")<=MAX_IRX_NAME_CHARS,("String in pName is too long."));
		strcpy(pIRXNameBuf,"host0:IOPModules/");
	}	
	
	// Append the name in upper case.
	char *pDest=pIRXNameBuf+strlen(pIRXNameBuf);
	const char *pSource=pName;
	while (*pSource)
	{
		char ch=*pSource++;
		if (ch>='a' && ch<='z')
		{
			ch='A'+ch-'a';
		}	
		*pDest++=ch;
	}
	*pDest=0;
	
	// Append the extension.
	strcat(pIRXNameBuf,".IRX");
	
	int result;
	if(( result = sceSifLoadModule(pIRXNameBuf, num_args, args )) < 0 )
	{
		Dbg_MsgAssert( !assert_on_fail,( "Can't load module %s. Result: %d\n",pIRXNameBuf, result ));
		
		return result;
	}   
	return 0;		
}

Manager::Manager( void )
{
    

    int i, j, index;
    Device *device;
    
    sceSifInitRpc(0);

#if CD_TEST
	cd_test();
#endif

	LoadIRX("sio2man");
#ifdef MULTI_TAP_SUPPORT
	LoadIRX("mtapman");
#endif // MULTI_TAP_SUPPORT
	LoadIRX("mcman");
	LoadIRX("mcserv");
	LoadIRX("padman");
	LoadIRX("cdvdstm");
    
	// Moved the loading of the SN Stack module here from net.cpp because it seems to use some
	// temporary memory upon startup that causes it to exceed the IOP memory limit if loaded
	// last.
#ifdef __NOPT_DEBUG__
	LoadIRX( "SNSTKDBG" );
#else
	LoadIRX( "SNSTKREL" );
#endif

	if (!Sfx::NoSoundPlease() || !Pcm::NoMusicPlease() || !Pcm::StreamsDisabled())
	{
		LoadIRX("libsd");	// sound chip SPU2 lib
		LoadIRX("sdrdrv");	// sound driver lib?
	}

	// Async file driver
	LoadIRX("fileio");
	// Init the new async stuff
	File::CAsyncFileLoader::sInit();

	if (!Sfx::NoSoundPlease() || !Pcm::NoMusicPlease() || !Pcm::StreamsDisabled())
	{
		LoadIRX("ezpcm");	// streaming music...
	}

	LoadIRX("usbd");		// usb driver

	#define ICON_FILE	"foo"	//"mc0:BASLUS-20731/NAAPROIA/SYS_NET.ICO"// "foo"
	#define ICON_SYS_FILE	"bar"	//"mc0:BASLUS-20731/NAAPROIA/icon.sys"// "bar"

	static char netcnfArgs[] = "icon="ICON_FILE"\0iconsys="ICON_SYS_FILE;

	LoadIRX("netcnf", sizeof(netcnfArgs), netcnfArgs );

#if ( KEYBOARD_ON )
	// initialize the keyboard
	KeyboardInit();
#endif

	// initialise IOP memory ( used for CD filesystem and for music ).
	if( sceSifInitIopHeap( ) < 0 )
	{
		Dbg_MsgAssert( 0,( "Failed to init IOP Heap\n"));
	}

    if( scePadInit( 0 ) != 1 )
    {
        Dbg_MsgAssert( false,( "failed to init IOP controller lib\n" ));
    }
                         
#ifdef MULTI_TAP_SUPPORT
    sceMtapInit();
	sceMtapPortOpen( 0 );
    if( sceMtapGetConnection( 0 ) != 1 )
	{
		sceMtapPortClose( 0 );
	}
#endif // MULTI_TAP_SUPPORT

    m_process_devices_task = new Tsk::Task< DeviceList > ( Manager::process_devices, m_devices );
    
    index = 0;
    for( i = 0; i < vMAX_PORT; i++ )
    {
       for( j = 0; j < scePadGetSlotMax ( i ); j++ )
       {
           if(( device = create_device ( index, i, j )))
           {
               m_devices.AddToTail( device->m_node );
               device->Acquire();
               index++;
           }
       }
    }
	
	if (!Sfx::NoSoundPlease())
	{
		Sfx::PS2Sfx_InitCold( );
	}
	
	if (!Pcm::NoMusicPlease() || !Pcm::StreamsDisabled())
	{
		Pcm::Init( );
	}

	if (Config::CD())
	{
		File::InitQuickFileSystem( );
	}	
    Dbg_Message( "Initialized IOP controller lib\n" );
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Manager::~Manager( void )
{
    

    Device*					device;
    Device*					next;
    Lst::Search< Device >	sh;
    
    device = sh.FirstItem ( m_devices );
    
    while ( device )
    {
        Dbg_AssertType ( device, Device );
    
        next = sh.NextItem();
    
        delete device;
        device = next;
    }

    delete m_process_devices_task;

    if( scePadEnd() == 1 )
    {
        Dbg_Message( "Shut down IOP Controller Lib\n" );
    }
    else
    {
        Dbg_MsgAssert( false,( "Failed to shut down IOP Controller Lib\n" ));
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Device* Manager::GetDevice( int port, int slot )
{
    

    Device*					device;
    Lst::Search< Device >	sh;
    
    for( device = sh.FirstItem ( m_devices ); device;
            device = sh.NextItem ())
    {
        if( ( device->GetPort() == port ) &&
            ( device->GetSlot() == slot ))
        {
            return device;
        }
    }
    
    return NULL;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

Device* Manager::GetDeviceByIndex( int index )
{
    

    Device*					device;
    Lst::Search< Device >	sh;
    
    for( device = sh.FirstItem ( m_devices ); device;
            device = sh.NextItem ())
    {
        if( device->GetIndex() == index )
        {
            return device;
        }
    }
    
    return NULL;
}

/******************************************************************/
/*                                                                */
/*  Ken: Runs through all the devices pausing each one.           */
/*                                                                */
/******************************************************************/

void Manager::Pause()
{
    

    Device*					device;
    Lst::Search< Device >	sh;
    
    for( device = sh.FirstItem ( m_devices ); device;
            device = sh.NextItem ())
    {
		device->Pause();
    }
}

/******************************************************************/
/*                                                                */
/*  Ken: Runs through all the devices unpausing each one.         */
/*                                                                */
/******************************************************************/

void Manager::UnPause()
{
    

    Device*					device;
    Lst::Search< Device >	sh;
    
    for( device = sh.FirstItem ( m_devices ); device;
            device = sh.NextItem ())
    {
		device->UnPause();
    }
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace SIO


================================================
FILE: Code/Sys/Win32/p_timer.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS Library												**
**																			**
**	Module:			Timer (TMR)			 									**
**																			**
**	File name:		ngps/p_timer.cpp										**
**																			**
**	Created by:		09/25/00	-	dc										**
**																			**
**	Description:	XBox System Timer										**
**																			**
*****************************************************************************/


/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/

namespace Tmr 
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

//static clock_t			start_count;

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************

Manager::Manager ( void )
{
	Dbg_MemberFunction;
	
	// Set the start count.
	start_count = timeGetTime();	
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************

Manager::~Manager ( void )
{
	Dbg_MemberFunction;    
}

/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/
 
Time GetTime ( void )
{
	return timeGetTime();
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
 
MicroSeconds GetTimeInUSeconds( void )
{
	return timeGetTime() * 1000;
}

float FrameLength()
{
	return 16;
}

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/

} // namespace Tmr


================================================
FILE: Code/Sys/XBox/p_timer.cpp
================================================
/*****************************************************************************
**																			**
**			              Neversoft Entertainment							**
**																		   	**
**				   Copyright (C) 1999 - All Rights Reserved				   	**
**																			**
******************************************************************************
**																			**
**	Project:		SYS Library												**
**																			**
**	Module:			Timer (TMR)			 									**
**																			**
**	File name:		ngps/p_timer.cpp										**
**																			**
**	Created by:		01/13/00	-	mjb										**
**																			**
**	Description:	NGPS System Timer										**
**																			**
*****************************************************************************/



/*****************************************************************************
**							  	  Includes									**
*****************************************************************************/

#include 
#include 
#include 
#include 
#include 

/*****************************************************************************
**							  DBG Information								**
*****************************************************************************/

namespace Tmr 
{

/*****************************************************************************
**								  Externals									**
*****************************************************************************/

/*****************************************************************************
**								   Defines									**
*****************************************************************************/

#define vHIREZ_CMP 15000

/*****************************************************************************
**								Private Types								**
*****************************************************************************/

/*****************************************************************************
**								 Private Data								**
*****************************************************************************/

static volatile uint64	s_count;

#define	vSMOOTH_N  4	

static volatile uint64	s_vblank = 0;
static volatile uint64	s_total_vblanks = 0;
static float			s_slomo = 1.0f;
static uint64			s_render_frame = 0;	
static uint64			s_stored_vblank = 0;
static volatile bool	s_vsync_handler_installed = false;
static volatile bool	s_swap_handler_installed = false;

static clock_t			high_count;
static clock_t			start_count;
static __int64			high_count_high_precision;
static __int64			start_count_high_precision;

static uint64 GetGameVblanks();

struct STimerInfo
{
	float	render_length;
	double	uncapped_render_length;
	int		render_buffer[vSMOOTH_N];
	uint64	render_vblank;
	uint64	render_last_vblank;	
	int		render_index;
};

static STimerInfo gTimerInfo;
static STimerInfo gStoredTimerInfo;
static float xrate = 60.0f;

static void InitTimerInfo( void )
{
	static bool xrate_set = false;

	if( !xrate_set )
	{
		xrate		= (float)Config::FPS();
		xrate_set	= true;
	}

	gTimerInfo.render_length			= 0.01666666f;		// defualt to 1/60th
	gTimerInfo.uncapped_render_length	= 0.01666666f;		// defualt to 1/60th
	for( int i = 0; i < vSMOOTH_N; i++ )
	{
		gTimerInfo.render_buffer[ i ] = 1;
	}
	gTimerInfo.render_vblank			= 0;	
	gTimerInfo.render_last_vblank		= 0;
	gTimerInfo.render_index				= 0;

	gStoredTimerInfo					= gTimerInfo;

	s_stored_vblank						= 0;
	s_vblank							= 0;
}

//#ifdef __NOPT_PROFILE__

//Sys::ProfileData	DMAProfileData;
//Sys::ProfileData	VU1ProfileData;

//#endif // __NOPT_PROFILE__

/*****************************************************************************
**								 Public Data								**
*****************************************************************************/

/*****************************************************************************
**							  Private Prototypes							**
*****************************************************************************/

/*****************************************************************************
**							   Private Functions							**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
__int64 __declspec( naked ) __stdcall getCycles( void )
{
	_asm
	{
		rdtsc
		ret
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void __cdecl s_vsync_handler( D3DVBLANKDATA *pData )
{
	// inc vblanks    
	s_vblank++;
	s_total_vblanks++;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static void __cdecl s_swap_callback_handler( D3DSWAPDATA *pData )
{
	// This callback function will be called when a Swap or Present is encountered by the GPU, in order to provide information as to when
	// Swap or Present calls are actually handled by the GPU. Swap and Present are like all other rendering functions in that they are enqueued
	// in the push-buffer and so the handling of the actual command is not synchronous with the actual function call.
	// Even if the Swap or Present is called by the title before a vertical blank, it may not be handled by the GPU until after the vertical blank
	// because the GPU still has to process all of the commands in the push-buffer that are before the Swap or Present.
	++s_render_frame;
	
	int diff;
	gTimerInfo.render_last_vblank	= gTimerInfo.render_vblank;
	gTimerInfo.render_vblank		= pData->SwapVBlank;
	diff = (int)( gTimerInfo.render_vblank - gTimerInfo.render_last_vblank );

	gTimerInfo.render_buffer[gTimerInfo.render_index++]	= diff;

	// Wrap the index around.	
	if( gTimerInfo.render_index == vSMOOTH_N )
	{
		gTimerInfo.render_index = 0;		
	}
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Init( void )
{
	InitTimerInfo();
	
	s_count						= 0;
	start_count					= clock();
	high_count					= (clock_t)0;
	start_count_high_precision	= getCycles();
	high_count_high_precision	= (__int64)0;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void DeInit( void )
{
}



/*****************************************************************************
**							   Public Functions								**
*****************************************************************************/

/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void InstallVSyncHandlers( void )
{
	if( !s_vsync_handler_installed )
	{
		// Install the vertical blank callback handler.
		D3DDevice_SetVerticalBlankCallback( s_vsync_handler );
		s_vsync_handler_installed = true;
	}

	if( !s_swap_handler_installed )
	{
		// Install the swap callback handler at this point.
		D3DDevice_SetSwapCallback( s_swap_callback_handler );
		s_swap_handler_installed = true;
	}
}



// when pausing the game, call this to store the current state of OncePerRender( ) (only essential in replay mode)
void StoreTimerInfo( void )
{
}



void RecallTimerInfo( void )
{
}



// Call this function once per rendering loop, to increment the 
// m_render_frame variable
// This function should be synchronized in some way to the vblank, so that it is called 
// directly after the vblank that rendering starts on
void OncePerRender( void )
{
#	ifdef STOPWATCH_STUFF
	Script::IncrementStopwatchFrameIndices();
	Script::DumpFunctionTimes();
	Script::ResetFunctionCounts();	
#	endif

	int total			= 0;
	int uncapped_total	= 0;

	for( int i = 0; i < vSMOOTH_N; ++i )
	{
		int diff = gTimerInfo.render_buffer[i];
		uncapped_total += diff;

		// Handle very bad values.
		if( diff > 10 || diff < 0 )		
		{
			diff = 1;
		}

		// Clamp to 4.
		if( diff > 4 )
		{
			diff = 4;
		}		
		total += diff;
	}

	gTimerInfo.render_length = (float)total / (float)vSMOOTH_N;

	if( gTimerInfo.render_length < 1.0f )
	{
		gTimerInfo.render_length = 1.0f;
	}

	gTimerInfo.uncapped_render_length = (double)uncapped_total/(double)vSMOOTH_N; 
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint64 GetRenderFrame( void )
{
	return s_render_frame;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void Vblank( void )
{
	s_total_vblanks++;
	s_vblank++;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
static uint64 GetGameVblanks( void )
{
	return s_vblank;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
uint64 GetVblanks( void )
{
	return s_total_vblanks;
}



// call "StartLogic" when all the logic functions are going to be called  
//void Manager::StartLogic()
//{
//}

// returns the PROJECTED length of the next frame, for use in logic calculations
// done by averaging the frame lengths of the last N frames (default N = 4)
// at 60fps (NTSC), this would return 1/60
// at 50fps (PAL), this would return 1/50		   
// complications arise when the framerate changes
// we want the physics to move smoothly and consistently at all framerates
// however, all we know is the number of ticks the last frames took
// we also need to ensure that the total of all the "FrameLength" calls will
// reflect the actual time that has passed
//				 |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |   |    |   
// say we have   1    1    1    .    2    1    1    1    .    2    1    .    2    1    1    1   .    2 
//    (10x1 + 4x2  = 18 frames
//
//
// now, let's work in 1/60ths of a second for now....
// let's take the average of the last 4 frames, as the logic time we will return for this frame
//
//               1    1    1         1.25 1.25 1.25 1.25     1.25 1.25      1.5  1.5  1.25 1.25     1.25
//
// the reason we do the averaging is to smooth out discontinuities
// if we simply use the length of the previous frame, then if we were to be alternating between 1 and 2 frames
// then the logic would move 2 on frames of length 1, and move 1 on frames of length 2
// this would essentially make the skater visually move alternately .5x and 2x his actual speed
// this looks incredibly jerky, as there is a 4x difference per frame   
// by smoothing it out, you would move 1.5 on every frame, giving relative motion of 0.6666 and 1.3333, 
// this is still jerky, but half as much as the unsmoothed version.
float FrameLength( void )
{
	return gTimerInfo.render_length / xrate * GetSlomo();
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
float GetSlomo( void )
{
	return s_slomo;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void SetSlomo( float slomo )
{
	s_slomo = slomo;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
double UncappedFrameLength()
{
	return gTimerInfo.uncapped_render_length / xrate;
}



/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
void VSync( void )
{
	uint64 now = GetVblanks();
	while( now == GetVblanks());	
}
 


/******************************************************************/
/*                                                                */
/*                                                                */
/******************************************************************/
MicroSeconds GetTimeInUSeconds( void )
{
	high_count_high_precision = ( getCycles() - start_count_high_precision ) / 733;
	return static_cast